aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/3rdparty
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/3rdparty')
-rw-r--r--src/libs/3rdparty/CMakeLists.txt10
-rw-r--r--src/libs/3rdparty/cplusplus/AST.cpp1690
-rw-r--r--src/libs/3rdparty/cplusplus/AST.h4732
-rw-r--r--src/libs/3rdparty/cplusplus/ASTClone.cpp289
-rw-r--r--src/libs/3rdparty/cplusplus/ASTMatch0.cpp75
-rw-r--r--src/libs/3rdparty/cplusplus/ASTMatcher.cpp182
-rw-r--r--src/libs/3rdparty/cplusplus/ASTMatcher.h10
-rw-r--r--src/libs/3rdparty/cplusplus/ASTPatternBuilder.h329
-rw-r--r--src/libs/3rdparty/cplusplus/ASTVisit.cpp86
-rw-r--r--src/libs/3rdparty/cplusplus/ASTVisitor.cpp30
-rw-r--r--src/libs/3rdparty/cplusplus/ASTVisitor.h56
-rw-r--r--src/libs/3rdparty/cplusplus/ASTfwd.h10
-rw-r--r--src/libs/3rdparty/cplusplus/Bind.cpp839
-rw-r--r--src/libs/3rdparty/cplusplus/Bind.h333
-rw-r--r--src/libs/3rdparty/cplusplus/CMakeLists.txt24
-rw-r--r--src/libs/3rdparty/cplusplus/Control.cpp139
-rw-r--r--src/libs/3rdparty/cplusplus/Control.h71
-rw-r--r--src/libs/3rdparty/cplusplus/CoreTypes.cpp67
-rw-r--r--src/libs/3rdparty/cplusplus/CoreTypes.h165
-rw-r--r--src/libs/3rdparty/cplusplus/DiagnosticClient.h2
-rw-r--r--src/libs/3rdparty/cplusplus/FullySpecifiedType.cpp154
-rw-r--r--src/libs/3rdparty/cplusplus/FullySpecifiedType.h101
-rw-r--r--src/libs/3rdparty/cplusplus/Keywords.cpp570
-rw-r--r--src/libs/3rdparty/cplusplus/Keywords.kwgen157
-rw-r--r--src/libs/3rdparty/cplusplus/LICENSE.txt17
-rw-r--r--src/libs/3rdparty/cplusplus/Lexer.cpp91
-rw-r--r--src/libs/3rdparty/cplusplus/Lexer.h8
-rw-r--r--src/libs/3rdparty/cplusplus/LiteralTable.h18
-rw-r--r--src/libs/3rdparty/cplusplus/Literals.cpp6
-rw-r--r--src/libs/3rdparty/cplusplus/Literals.h21
-rw-r--r--src/libs/3rdparty/cplusplus/Matcher.cpp24
-rw-r--r--src/libs/3rdparty/cplusplus/Matcher.h4
-rw-r--r--src/libs/3rdparty/cplusplus/MemoryPool.cpp10
-rw-r--r--src/libs/3rdparty/cplusplus/Name.cpp54
-rw-r--r--src/libs/3rdparty/cplusplus/Name.h32
-rw-r--r--src/libs/3rdparty/cplusplus/Names.cpp82
-rw-r--r--src/libs/3rdparty/cplusplus/Names.h150
-rw-r--r--src/libs/3rdparty/cplusplus/Parser.cpp1212
-rw-r--r--src/libs/3rdparty/cplusplus/Parser.h74
-rw-r--r--src/libs/3rdparty/cplusplus/QtContextKeywords.cpp17
-rw-r--r--src/libs/3rdparty/cplusplus/QtContextKeywords.h3
-rw-r--r--src/libs/3rdparty/cplusplus/SafeMatcher.h22
-rw-r--r--src/libs/3rdparty/cplusplus/Scope.cpp94
-rw-r--r--src/libs/3rdparty/cplusplus/Scope.h30
-rw-r--r--src/libs/3rdparty/cplusplus/Symbol.cpp217
-rw-r--r--src/libs/3rdparty/cplusplus/Symbol.h251
-rw-r--r--src/libs/3rdparty/cplusplus/Symbols.cpp548
-rw-r--r--src/libs/3rdparty/cplusplus/Symbols.h751
-rw-r--r--src/libs/3rdparty/cplusplus/Templates.cpp57
-rw-r--r--src/libs/3rdparty/cplusplus/Templates.h113
-rw-r--r--src/libs/3rdparty/cplusplus/Token.cpp14
-rw-r--r--src/libs/3rdparty/cplusplus/Token.h36
-rw-r--r--src/libs/3rdparty/cplusplus/TranslationUnit.cpp226
-rw-r--r--src/libs/3rdparty/cplusplus/TranslationUnit.h112
-rw-r--r--src/libs/3rdparty/cplusplus/Type.cpp66
-rw-r--r--src/libs/3rdparty/cplusplus/Type.h103
-rw-r--r--src/libs/3rdparty/cplusplus/cplusplus.pri69
m---------src/libs/3rdparty/googletest0
-rw-r--r--src/libs/3rdparty/json/LICENSE.MIT21
-rw-r--r--src/libs/3rdparty/json/json.hpp24640
-rw-r--r--src/libs/3rdparty/libptyqt/.clang-format1
-rw-r--r--src/libs/3rdparty/libptyqt/CMakeLists.txt28
-rw-r--r--src/libs/3rdparty/libptyqt/LICENSE21
-rw-r--r--src/libs/3rdparty/libptyqt/LICENSE-CONPTY21
-rw-r--r--src/libs/3rdparty/libptyqt/conptyprocess.cpp1148
-rw-r--r--src/libs/3rdparty/libptyqt/conptyprocess.h90
-rw-r--r--src/libs/3rdparty/libptyqt/iptyprocess.h62
-rw-r--r--src/libs/3rdparty/libptyqt/ptyqt.cpp56
-rw-r--r--src/libs/3rdparty/libptyqt/ptyqt.h14
-rw-r--r--src/libs/3rdparty/libptyqt/ptyqt.qbs40
-rw-r--r--src/libs/3rdparty/libptyqt/unixptyprocess.cpp361
-rw-r--r--src/libs/3rdparty/libptyqt/unixptyprocess.h66
-rw-r--r--src/libs/3rdparty/libptyqt/winptyprocess.cpp278
-rw-r--r--src/libs/3rdparty/libptyqt/winptyprocess.h43
-rw-r--r--src/libs/3rdparty/libvterm/CMakeLists.txt18
-rw-r--r--src/libs/3rdparty/libvterm/CONTRIBUTING22
-rw-r--r--src/libs/3rdparty/libvterm/LICENSE23
-rw-r--r--src/libs/3rdparty/libvterm/include/vterm.h639
-rw-r--r--src/libs/3rdparty/libvterm/include/vterm_keycodes.h61
-rw-r--r--src/libs/3rdparty/libvterm/src/encoding.c230
-rw-r--r--src/libs/3rdparty/libvterm/src/encoding/DECdrawing.inc36
-rw-r--r--src/libs/3rdparty/libvterm/src/encoding/uk.inc6
-rw-r--r--src/libs/3rdparty/libvterm/src/fullwidth.inc111
-rw-r--r--src/libs/3rdparty/libvterm/src/keyboard.c226
-rw-r--r--src/libs/3rdparty/libvterm/src/mouse.c99
-rw-r--r--src/libs/3rdparty/libvterm/src/parser.c402
-rw-r--r--src/libs/3rdparty/libvterm/src/pen.c613
-rw-r--r--src/libs/3rdparty/libvterm/src/rect.h56
-rw-r--r--src/libs/3rdparty/libvterm/src/screen.c1191
-rw-r--r--src/libs/3rdparty/libvterm/src/state.c2347
-rw-r--r--src/libs/3rdparty/libvterm/src/unicode.c313
-rw-r--r--src/libs/3rdparty/libvterm/src/utf8.h39
-rw-r--r--src/libs/3rdparty/libvterm/src/vterm.c430
-rw-r--r--src/libs/3rdparty/libvterm/src/vterm_internal.h297
-rw-r--r--src/libs/3rdparty/libvterm/vterm.pc.in8
-rw-r--r--src/libs/3rdparty/libvterm/vterm.qbs35
-rw-r--r--src/libs/3rdparty/lua/CMakeLists.txt40
-rw-r--r--src/libs/3rdparty/lua/LICENSE16
-rw-r--r--src/libs/3rdparty/lua/README6
-rw-r--r--src/libs/3rdparty/lua/doc/contents.html678
-rw-r--r--src/libs/3rdparty/lua/doc/index.css21
-rw-r--r--src/libs/3rdparty/lua/doc/logo.gifbin0 -> 9893 bytes
-rw-r--r--src/libs/3rdparty/lua/doc/lua.1155
-rw-r--r--src/libs/3rdparty/lua/doc/lua.css161
-rw-r--r--src/libs/3rdparty/lua/doc/luac.1118
-rw-r--r--src/libs/3rdparty/lua/doc/manual.css21
-rw-r--r--src/libs/3rdparty/lua/doc/manual.html12046
-rw-r--r--src/libs/3rdparty/lua/doc/osi-certified-72x60.pngbin0 -> 3774 bytes
-rw-r--r--src/libs/3rdparty/lua/doc/readme.html337
-rw-r--r--src/libs/3rdparty/lua/src/lapi.c1463
-rw-r--r--src/libs/3rdparty/lua/src/lapi.h52
-rw-r--r--src/libs/3rdparty/lua/src/lauxlib.c1112
-rw-r--r--src/libs/3rdparty/lua/src/lauxlib.h301
-rw-r--r--src/libs/3rdparty/lua/src/lbaselib.c549
-rw-r--r--src/libs/3rdparty/lua/src/lcode.c1871
-rw-r--r--src/libs/3rdparty/lua/src/lcode.h104
-rw-r--r--src/libs/3rdparty/lua/src/lcorolib.c210
-rw-r--r--src/libs/3rdparty/lua/src/lctype.c64
-rw-r--r--src/libs/3rdparty/lua/src/lctype.h101
-rw-r--r--src/libs/3rdparty/lua/src/ldblib.c483
-rw-r--r--src/libs/3rdparty/lua/src/ldebug.c924
-rw-r--r--src/libs/3rdparty/lua/src/ldebug.h63
-rw-r--r--src/libs/3rdparty/lua/src/ldo.c1024
-rw-r--r--src/libs/3rdparty/lua/src/ldo.h88
-rw-r--r--src/libs/3rdparty/lua/src/ldump.c230
-rw-r--r--src/libs/3rdparty/lua/src/lfunc.c294
-rw-r--r--src/libs/3rdparty/lua/src/lfunc.h64
-rw-r--r--src/libs/3rdparty/lua/src/lgc.c1739
-rw-r--r--src/libs/3rdparty/lua/src/lgc.h202
-rw-r--r--src/libs/3rdparty/lua/src/linit.c65
-rw-r--r--src/libs/3rdparty/lua/src/liolib.c828
-rw-r--r--src/libs/3rdparty/lua/src/ljumptab.h112
-rw-r--r--src/libs/3rdparty/lua/src/llex.c581
-rw-r--r--src/libs/3rdparty/lua/src/llex.h91
-rw-r--r--src/libs/3rdparty/lua/src/llimits.h380
-rw-r--r--src/libs/3rdparty/lua/src/lmathlib.c764
-rw-r--r--src/libs/3rdparty/lua/src/lmem.c215
-rw-r--r--src/libs/3rdparty/lua/src/lmem.h93
-rw-r--r--src/libs/3rdparty/lua/src/loadlib.c767
-rw-r--r--src/libs/3rdparty/lua/src/lobject.c602
-rw-r--r--src/libs/3rdparty/lua/src/lobject.h815
-rw-r--r--src/libs/3rdparty/lua/src/lopcodes.c104
-rw-r--r--src/libs/3rdparty/lua/src/lopcodes.h405
-rw-r--r--src/libs/3rdparty/lua/src/lopnames.h103
-rw-r--r--src/libs/3rdparty/lua/src/loslib.c428
-rw-r--r--src/libs/3rdparty/lua/src/lparser.c1967
-rw-r--r--src/libs/3rdparty/lua/src/lparser.h171
-rw-r--r--src/libs/3rdparty/lua/src/lprefix.h45
-rw-r--r--src/libs/3rdparty/lua/src/lstate.c445
-rw-r--r--src/libs/3rdparty/lua/src/lstate.h409
-rw-r--r--src/libs/3rdparty/lua/src/lstring.c273
-rw-r--r--src/libs/3rdparty/lua/src/lstring.h57
-rw-r--r--src/libs/3rdparty/lua/src/lstrlib.c1874
-rw-r--r--src/libs/3rdparty/lua/src/ltable.c980
-rw-r--r--src/libs/3rdparty/lua/src/ltable.h65
-rw-r--r--src/libs/3rdparty/lua/src/ltablib.c430
-rw-r--r--src/libs/3rdparty/lua/src/ltm.c271
-rw-r--r--src/libs/3rdparty/lua/src/ltm.h104
-rw-r--r--src/libs/3rdparty/lua/src/lua.c679
-rw-r--r--src/libs/3rdparty/lua/src/lua.h523
-rw-r--r--src/libs/3rdparty/lua/src/lua.hpp9
-rw-r--r--src/libs/3rdparty/lua/src/luac.c723
-rw-r--r--src/libs/3rdparty/lua/src/luaconf.h793
-rw-r--r--src/libs/3rdparty/lua/src/lualib.h52
-rw-r--r--src/libs/3rdparty/lua/src/lundump.c335
-rw-r--r--src/libs/3rdparty/lua/src/lundump.h36
-rw-r--r--src/libs/3rdparty/lua/src/lutf8lib.c291
-rw-r--r--src/libs/3rdparty/lua/src/lvm.c1901
-rw-r--r--src/libs/3rdparty/lua/src/lvm.h141
-rw-r--r--src/libs/3rdparty/lua/src/lzio.c68
-rw-r--r--src/libs/3rdparty/lua/src/lzio.h66
-rw-r--r--src/libs/3rdparty/optional/CMakeLists.txt11
-rw-r--r--src/libs/3rdparty/optional/README.md39
-rw-r--r--src/libs/3rdparty/optional/copyright.txt10
-rw-r--r--src/libs/3rdparty/optional/optional.hpp1046
-rw-r--r--src/libs/3rdparty/optional/test_optional.cpp1459
-rw-r--r--src/libs/3rdparty/optional/test_type_traits.cpp66
-rw-r--r--src/libs/3rdparty/qrcodegen/CMakeLists.txt11
-rw-r--r--src/libs/3rdparty/qrcodegen/LICENSE21
-rw-r--r--src/libs/3rdparty/qrcodegen/README.md25
-rw-r--r--src/libs/3rdparty/qrcodegen/src/qrcodegen.cpp863
-rw-r--r--src/libs/3rdparty/qrcodegen/src/qrcodegen.h566
-rw-r--r--src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp89
-rw-r--r--src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h36
-rw-r--r--src/libs/3rdparty/qtkeychain/CMakeLists.txt67
-rw-r--r--src/libs/3rdparty/qtkeychain/CMakeLists.txt.upstream198
-rw-r--r--src/libs/3rdparty/qtkeychain/COPYING23
-rw-r--r--src/libs/3rdparty/qtkeychain/ChangeLog109
-rw-r--r--src/libs/3rdparty/qtkeychain/QtKeychainConfig.cmake.in28
-rw-r--r--src/libs/3rdparty/qtkeychain/ReadMe.md29
-rw-r--r--src/libs/3rdparty/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake202
-rw-r--r--src/libs/3rdparty/qtkeychain/cmake/Modules/ECMQueryQt.cmake100
-rw-r--r--src/libs/3rdparty/qtkeychain/cmake/Modules/ECMSetupVersion.cmake202
-rw-r--r--src/libs/3rdparty/qtkeychain/cmake/Modules/QtVersionOption.cmake36
-rw-r--r--src/libs/3rdparty/qtkeychain/gnomekeyring.cpp86
-rw-r--r--src/libs/3rdparty/qtkeychain/gnomekeyring_p.h94
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain.cpp235
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain.h282
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain_apple.mm263
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain_p.h167
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain_unix.cpp633
-rw-r--r--src/libs/3rdparty/qtkeychain/keychain_win.cpp193
-rw-r--r--src/libs/3rdparty/qtkeychain/libsecret.cpp349
-rw-r--r--src/libs/3rdparty/qtkeychain/libsecret_p.h33
-rw-r--r--src/libs/3rdparty/qtkeychain/org.kde.KWallet.xml276
-rw-r--r--src/libs/3rdparty/qtkeychain/plaintextstore.cpp110
-rw-r--r--src/libs/3rdparty/qtkeychain/plaintextstore_p.h47
-rw-r--r--src/libs/3rdparty/qtkeychain/qkeychain_export.h11
-rw-r--r--src/libs/3rdparty/qtkeychain/qtkeychain.qbs92
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_de.ts234
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_fr.ts226
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_nl.ts320
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_ro.ts235
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_ru.ts234
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/qtkeychain_zh.ts234
-rw-r--r--src/libs/3rdparty/qtkeychain/translations/translations.qrc.in5
-rw-r--r--src/libs/3rdparty/sol2/CMakeLists.txt12
-rw-r--r--src/libs/3rdparty/sol2/LICENSE.txt20
-rw-r--r--src/libs/3rdparty/sol2/include/sol/config.hpp57
-rw-r--r--src/libs/3rdparty/sol2/include/sol/forward.hpp1340
-rw-r--r--src/libs/3rdparty/sol2/include/sol/sol.hpp29202
-rw-r--r--src/libs/3rdparty/span/LICENSE_1_0.txt (renamed from src/libs/3rdparty/optional/LICENSE_1_0.txt)0
-rw-r--r--src/libs/3rdparty/span/README.md557
-rw-r--r--src/libs/3rdparty/span/span.hpp1947
-rw-r--r--src/libs/3rdparty/sqlite/carray.c561
-rw-r--r--src/libs/3rdparty/sqlite/config.h87
-rw-r--r--src/libs/3rdparty/sqlite/okapi_bm25.h231
-rw-r--r--src/libs/3rdparty/sqlite/sqlite.h (renamed from src/libs/3rdparty/syntax-highlighting/src/lib/ksyntaxhighlighting_export.h)11
-rw-r--r--src/libs/3rdparty/sqlite/sqlite.pri11
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.c100225
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.h3902
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3ext.h91
-rw-r--r--src/libs/3rdparty/sqlite/sqlite_static_config.h305
-rw-r--r--src/libs/3rdparty/syntax-highlighting/.git-blame-ignore-revs5
-rw-r--r--src/libs/3rdparty/syntax-highlighting/.gitignore9
-rw-r--r--src/libs/3rdparty/syntax-highlighting/.gitlab-ci.yml10
-rw-r--r--src/libs/3rdparty/syntax-highlighting/.kde-ci.yml12
-rw-r--r--src/libs/3rdparty/syntax-highlighting/CMakeLists.txt56
-rw-r--r--src/libs/3rdparty/syntax-highlighting/CMakeLists.txt.kde23
-rw-r--r--src/libs/3rdparty/syntax-highlighting/KF5SyntaxHighlightingConfig.cmake.in4
-rw-r--r--src/libs/3rdparty/syntax-highlighting/QC_README.txt0
-rw-r--r--src/libs/3rdparty/syntax-highlighting/README.md236
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/autogenerated.pri9
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/AbstractHighlighter (renamed from src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/AbstractHighlighter)0
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Definition (renamed from src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Definition)0
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/DefinitionDownloader (renamed from src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/DefinitionDownloader)0
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/FoldingRegion (renamed from src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/FoldingRegion)0
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Format (renamed from src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Format)0
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Repository (renamed from src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Repository)0
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/State (renamed from src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/State)0
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/SyntaxHighlighter (renamed from src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/SyntaxHighlighter)0
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Theme (renamed from src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Theme)0
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/WildcardMatcher1
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/ksyntaxhighlighting_version.h14
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/ksyntaxhighlighting_export.h111
-rw-r--r--src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/ksyntaxhighlighting_logging.cpp6
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/CMakeLists.txt92
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/data.pro11
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/.gitignore1
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/Pipfile12
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/cmake.xml.tpl509
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/cmake.yaml3305
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/generate-cmake-syntax.py426
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/generate-doxygenlua.pl42
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/generate-html.pl143
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/generate-nginx-lists.rb55
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/generate-php.pl58
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/generate-spdx-syntax.py67
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/get-Qt-classes.sh2
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/get-Qt-macros.sh2
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/qmake-gen.py15
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/spdx-comments.xml.tpl94
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/update_css_and_scss.py498
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/generators/update_less.py45
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/schema/language.xsd204
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/schema/validatehl.sh3
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/alert.xml10
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/autoconf.xml396
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/bash.xml1832
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/cmake.xml5435
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/comments.xml27
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/css.xml2042
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/doxygen.xml406
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/dtd.xml13
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/gnuassembler.xml2352
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/html.xml219
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/ini.xml44
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/java.xml221
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/javadoc.xml104
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/json.xml41
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.GPLv2282
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.GPLv3674
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.LGPLv21504
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.LGPLv3163
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/makefile.xml498
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/markdown.xml767
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/modelines.xml24
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/perl.xml146
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/powershell.xml299
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/python.xml688
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/qdocconf.xml10
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/ruby.xml125
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/spdx-comments.xml591
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/toml.xml183
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/valgrind-suppression.xml9
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/xml.xml126
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/yacc.xml46
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/syntax/yaml.xml642
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/atom-one-dark.theme370
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/atom-one-light.theme377
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/ayu-dark.theme205
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/ayu-light.theme180
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/ayu-mirage.theme205
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/breeze-dark.theme67
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/breeze-light.theme (renamed from src/libs/3rdparty/syntax-highlighting/data/themes/default.theme)71
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/dracula.theme705
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/falcon.theme184
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/github-dark.theme216
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/github-light.theme216
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/gruvbox-dark.theme185
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/gruvbox-light.theme185
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/homunculus.theme177
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/monokai.theme394
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/nord.theme181
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/oblivion.theme179
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/printing.theme65
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/radical.theme182
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/solarized-dark.theme95
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/solarized-light.theme83
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/theme-data.qrc34
-rw-r--r--src/libs/3rdparty/syntax-highlighting/data/themes/vim-dark.theme180
-rw-r--r--src/libs/3rdparty/syntax-highlighting/examples/CMakeLists.txt4
-rw-r--r--src/libs/3rdparty/syntax-highlighting/examples/codeeditor.cpp358
-rw-r--r--src/libs/3rdparty/syntax-highlighting/examples/codeeditor.h69
-rw-r--r--src/libs/3rdparty/syntax-highlighting/examples/main.cpp46
-rw-r--r--src/libs/3rdparty/syntax-highlighting/metainfo.yaml2
-rw-r--r--src/libs/3rdparty/syntax-highlighting/patches/0001-Remove-highlight-definitions-with-incompatible-licen.patch1381
-rw-r--r--src/libs/3rdparty/syntax-highlighting/patches/0002-Remove-autotests.patch2901
-rw-r--r--src/libs/3rdparty/syntax-highlighting/patches/0003-Add-qmake-Qbs-files-and-files-generated-by-CMake.patch389
-rw-r--r--src/libs/3rdparty/syntax-highlighting/patches/0004-Compile-with-namespaced-Qt.patch281
-rw-r--r--src/libs/3rdparty/syntax-highlighting/patches/0005-Prevent-assertion-in-RegExpr-doLoad.patch29
-rw-r--r--src/libs/3rdparty/syntax-highlighting/patches/0006-Syntax-Highlighter-return-all-definitions-for-a-file.patch101
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/CMakeLists.txt11
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/Messages.sh2
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/cli/CMakeLists.txt8
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/cli/kate-syntax-highlighter.cpp164
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/cli/ksyntaxhighlighter.cpp236
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/indexer/CMakeLists.txt22
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/indexer/katehighlightingindexer.cpp3115
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt106
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.cpp221
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h46
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h34
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.cpp1422
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.h66
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp217
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h102
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch.cpp95
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch_p.h51
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp729
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/definition.h108
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/definition_p.h83
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp91
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.h27
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h49
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/dynamicregexpcache_p.h39
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp54
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h46
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp196
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/format.h106
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/format_p.h46
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata.cpp406
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata_p.hpp216
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp198
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h35
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp106
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist_p.h53
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h32
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp322
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/repository.h96
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h60
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp787
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/rule_p.h281
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp103
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/state.h40
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h90
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp223
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h28
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/textstyledata_p.h50
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp77
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/theme.h39
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/themedata.cpp209
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/themedata_p.h51
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.cpp43
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.h33
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher_p.h51
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters.cpp54
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters_p.h63
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/xml_p.h32
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/quick/CMakeLists.txt16
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.cpp114
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.h64
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.cpp46
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.h22
-rw-r--r--src/libs/3rdparty/syntax-highlighting/syntax-highlighting.pro55
-rw-r--r--src/libs/3rdparty/syntax-highlighting/syntax-highlighting.qbs101
-rw-r--r--src/libs/3rdparty/syntax-highlighting/syntax-highlighting_dependencies.pri3
-rw-r--r--src/libs/3rdparty/tl_expected/COPYING121
-rw-r--r--src/libs/3rdparty/tl_expected/README.md74
-rw-r--r--src/libs/3rdparty/tl_expected/include/tl/expected.hpp2444
-rw-r--r--src/libs/3rdparty/variant/LICENSE.md23
-rw-r--r--src/libs/3rdparty/variant/README.md37
-rw-r--r--src/libs/3rdparty/variant/variant.hpp2465
-rw-r--r--src/libs/3rdparty/winpty/.gitattributes19
-rw-r--r--src/libs/3rdparty/winpty/.gitignore16
-rw-r--r--src/libs/3rdparty/winpty/CMakeLists.txt1
-rw-r--r--src/libs/3rdparty/winpty/LICENSE21
-rw-r--r--src/libs/3rdparty/winpty/README.md151
-rw-r--r--src/libs/3rdparty/winpty/RELEASES.md280
-rw-r--r--src/libs/3rdparty/winpty/VERSION.txt1
-rw-r--r--src/libs/3rdparty/winpty/appveyor.yml16
-rw-r--r--src/libs/3rdparty/winpty/configure167
-rw-r--r--src/libs/3rdparty/winpty/misc/.gitignore2
-rw-r--r--src/libs/3rdparty/winpty/misc/BufferResizeTests.cc90
-rw-r--r--src/libs/3rdparty/winpty/misc/ChangeScreenBuffer.cc53
-rw-r--r--src/libs/3rdparty/winpty/misc/ClearConsole.cc72
-rw-r--r--src/libs/3rdparty/winpty/misc/ConinMode.cc117
-rw-r--r--src/libs/3rdparty/winpty/misc/ConinMode.ps1116
-rw-r--r--src/libs/3rdparty/winpty/misc/ConoutMode.cc113
-rw-r--r--src/libs/3rdparty/winpty/misc/DebugClient.py42
-rw-r--r--src/libs/3rdparty/winpty/misc/DebugServer.py63
-rw-r--r--src/libs/3rdparty/winpty/misc/DumpLines.py5
-rw-r--r--src/libs/3rdparty/winpty/misc/EnableExtendedFlags.txt46
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Consolas.txt528
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Lucida.txt633
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP932.txt630
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP936.txt630
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP949.txt630
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP950.txt630
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt16
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/Results.txt4
-rw-r--r--src/libs/3rdparty/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt144
-rw-r--r--src/libs/3rdparty/winpty/misc/FontSurvey.cc100
-rw-r--r--src/libs/3rdparty/winpty/misc/FormatChar.h21
-rw-r--r--src/libs/3rdparty/winpty/misc/FreezePerfTest.cc62
-rw-r--r--src/libs/3rdparty/winpty/misc/GetCh.cc20
-rw-r--r--src/libs/3rdparty/winpty/misc/GetConsolePos.cc41
-rw-r--r--src/libs/3rdparty/winpty/misc/GetFont.cc261
-rw-r--r--src/libs/3rdparty/winpty/misc/IdentifyConsoleWindow.ps151
-rw-r--r--src/libs/3rdparty/winpty/misc/IsNewConsole.cc87
-rw-r--r--src/libs/3rdparty/winpty/misc/MouseInputNotes.txt90
-rw-r--r--src/libs/3rdparty/winpty/misc/MoveConsoleWindow.cc34
-rw-r--r--src/libs/3rdparty/winpty/misc/Notes.txt219
-rw-r--r--src/libs/3rdparty/winpty/misc/OSVersion.cc27
-rw-r--r--src/libs/3rdparty/winpty/misc/ScreenBufferFreezeInactive.cc101
-rw-r--r--src/libs/3rdparty/winpty/misc/ScreenBufferTest.cc671
-rw-r--r--src/libs/3rdparty/winpty/misc/ScreenBufferTest2.cc151
-rw-r--r--src/libs/3rdparty/winpty/misc/SelectAllTest.cc45
-rw-r--r--src/libs/3rdparty/winpty/misc/SetBufInfo.cc90
-rw-r--r--src/libs/3rdparty/winpty/misc/SetBufferSize.cc32
-rw-r--r--src/libs/3rdparty/winpty/misc/SetCursorPos.cc10
-rw-r--r--src/libs/3rdparty/winpty/misc/SetFont.cc145
-rw-r--r--src/libs/3rdparty/winpty/misc/SetWindowRect.cc36
-rw-r--r--src/libs/3rdparty/winpty/misc/ShowArgv.cc12
-rw-r--r--src/libs/3rdparty/winpty/misc/ShowConsoleInput.cc40
-rw-r--r--src/libs/3rdparty/winpty/misc/Spew.py5
-rw-r--r--src/libs/3rdparty/winpty/misc/TestUtil.cc172
-rw-r--r--src/libs/3rdparty/winpty/misc/UnicodeDoubleWidthTest.cc102
-rw-r--r--src/libs/3rdparty/winpty/misc/UnicodeWideTest1.cc246
-rw-r--r--src/libs/3rdparty/winpty/misc/UnicodeWideTest2.cc130
-rw-r--r--src/libs/3rdparty/winpty/misc/UnixEcho.cc89
-rw-r--r--src/libs/3rdparty/winpty/misc/Utf16Echo.cc46
-rw-r--r--src/libs/3rdparty/winpty/misc/VeryLargeRead.cc122
-rw-r--r--src/libs/3rdparty/winpty/misc/VkEscapeTest.cc56
-rw-r--r--src/libs/3rdparty/winpty/misc/Win10ResizeWhileFrozen.cc52
-rw-r--r--src/libs/3rdparty/winpty/misc/Win10WrapTest1.cc57
-rw-r--r--src/libs/3rdparty/winpty/misc/Win10WrapTest2.cc30
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Echo1.cc26
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Echo2.cc19
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Test1.cc46
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Test2.cc70
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Test3.cc78
-rw-r--r--src/libs/3rdparty/winpty/misc/Win32Write1.cc44
-rw-r--r--src/libs/3rdparty/winpty/misc/WindowsBugCrashReader.cc27
-rw-r--r--src/libs/3rdparty/winpty/misc/WriteConsole.cc106
-rw-r--r--src/libs/3rdparty/winpty/misc/build32.sh9
-rw-r--r--src/libs/3rdparty/winpty/misc/build64.sh9
-rw-r--r--src/libs/3rdparty/winpty/misc/color-test.sh212
-rw-r--r--src/libs/3rdparty/winpty/misc/font-notes.txt300
-rw-r--r--src/libs/3rdparty/winpty/misc/winbug-15048.cc201
-rw-r--r--src/libs/3rdparty/winpty/ship/build-pty4j-libpty.bat36
-rw-r--r--src/libs/3rdparty/winpty/ship/common_ship.py89
-rw-r--r--src/libs/3rdparty/winpty/ship/make_msvc_package.py163
-rw-r--r--src/libs/3rdparty/winpty/ship/ship.py104
-rw-r--r--src/libs/3rdparty/winpty/src/CMakeLists.txt111
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Agent.cc612
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Agent.h103
-rw-r--r--src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.cc84
-rw-r--r--src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.h28
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleFont.cc698
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleFont.h28
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleInput.cc852
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleInput.h109
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.cc121
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.h36
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleLine.cc152
-rw-r--r--src/libs/3rdparty/winpty/src/agent/ConsoleLine.h41
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Coord.h87
-rw-r--r--src/libs/3rdparty/winpty/src/agent/DebugShowInput.cc239
-rw-r--r--src/libs/3rdparty/winpty/src/agent/DebugShowInput.h32
-rw-r--r--src/libs/3rdparty/winpty/src/agent/DefaultInputMap.cc422
-rw-r--r--src/libs/3rdparty/winpty/src/agent/DefaultInputMap.h28
-rw-r--r--src/libs/3rdparty/winpty/src/agent/DsrSender.h30
-rw-r--r--src/libs/3rdparty/winpty/src/agent/EventLoop.cc99
-rw-r--r--src/libs/3rdparty/winpty/src/agent/EventLoop.h47
-rw-r--r--src/libs/3rdparty/winpty/src/agent/InputMap.cc246
-rw-r--r--src/libs/3rdparty/winpty/src/agent/InputMap.h114
-rw-r--r--src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.cc71
-rw-r--r--src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.h68
-rw-r--r--src/libs/3rdparty/winpty/src/agent/NamedPipe.cc378
-rw-r--r--src/libs/3rdparty/winpty/src/agent/NamedPipe.h125
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Scraper.cc699
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Scraper.h103
-rw-r--r--src/libs/3rdparty/winpty/src/agent/SimplePool.h75
-rw-r--r--src/libs/3rdparty/winpty/src/agent/SmallRect.h143
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Terminal.cc535
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Terminal.h69
-rw-r--r--src/libs/3rdparty/winpty/src/agent/UnicodeEncoding.h157
-rw-r--r--src/libs/3rdparty/winpty/src/agent/UnicodeEncodingTest.cc189
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Win32Console.cc107
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Win32Console.h67
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.cc193
-rw-r--r--src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.h99
-rw-r--r--src/libs/3rdparty/winpty/src/agent/main.cc120
-rw-r--r--src/libs/3rdparty/winpty/src/agent/subdir.mk61
-rw-r--r--src/libs/3rdparty/winpty/src/configurations.gypi60
-rw-r--r--src/libs/3rdparty/winpty/src/debugserver/DebugServer.cc117
-rw-r--r--src/libs/3rdparty/winpty/src/debugserver/subdir.mk41
-rw-r--r--src/libs/3rdparty/winpty/src/include/winpty.h242
-rw-r--r--src/libs/3rdparty/winpty/src/include/winpty_constants.h131
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.cc75
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.h28
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/LibWinptyException.h54
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/WinptyInternal.h72
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/subdir.mk46
-rw-r--r--src/libs/3rdparty/winpty/src/libwinpty/winpty.cc970
-rw-r--r--src/libs/3rdparty/winpty/src/shared/AgentMsg.h38
-rw-r--r--src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.cc122
-rw-r--r--src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.h73
-rw-r--r--src/libs/3rdparty/winpty/src/shared/Buffer.cc103
-rw-r--r--src/libs/3rdparty/winpty/src/shared/Buffer.h102
-rw-r--r--src/libs/3rdparty/winpty/src/shared/DebugClient.cc187
-rw-r--r--src/libs/3rdparty/winpty/src/shared/DebugClient.h38
-rw-r--r--src/libs/3rdparty/winpty/src/shared/GenRandom.cc138
-rw-r--r--src/libs/3rdparty/winpty/src/shared/GenRandom.h55
-rw-r--r--src/libs/3rdparty/winpty/src/shared/GetCommitHash.bat13
-rw-r--r--src/libs/3rdparty/winpty/src/shared/Mutex.h54
-rw-r--r--src/libs/3rdparty/winpty/src/shared/OsModule.h63
-rw-r--r--src/libs/3rdparty/winpty/src/shared/OwnedHandle.cc36
-rw-r--r--src/libs/3rdparty/winpty/src/shared/OwnedHandle.h45
-rw-r--r--src/libs/3rdparty/winpty/src/shared/PrecompiledHeader.h43
-rw-r--r--src/libs/3rdparty/winpty/src/shared/StringBuilder.h227
-rw-r--r--src/libs/3rdparty/winpty/src/shared/StringBuilderTest.cc114
-rw-r--r--src/libs/3rdparty/winpty/src/shared/StringUtil.cc55
-rw-r--r--src/libs/3rdparty/winpty/src/shared/StringUtil.h80
-rw-r--r--src/libs/3rdparty/winpty/src/shared/TimeMeasurement.h63
-rw-r--r--src/libs/3rdparty/winpty/src/shared/UnixCtrlChars.h45
-rw-r--r--src/libs/3rdparty/winpty/src/shared/UpdateGenVersion.bat20
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WindowsSecurity.cc460
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WindowsSecurity.h104
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WindowsVersion.cc252
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WindowsVersion.h29
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyAssert.cc55
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyAssert.h64
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyException.cc57
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyException.h43
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyVersion.cc42
-rw-r--r--src/libs/3rdparty/winpty/src/shared/WinptyVersion.h27
-rw-r--r--src/libs/3rdparty/winpty/src/shared/winpty_snprintf.h99
-rw-r--r--src/libs/3rdparty/winpty/src/subdir.mk5
-rw-r--r--src/libs/3rdparty/winpty/src/tests/subdir.mk28
-rw-r--r--src/libs/3rdparty/winpty/src/tests/trivial_test.cc158
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.cc114
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.h56
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.cc80
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.h53
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/Util.cc86
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/Util.h31
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.cc70
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.h42
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/main.cc729
-rw-r--r--src/libs/3rdparty/winpty/src/unix-adapter/subdir.mk41
-rw-r--r--src/libs/3rdparty/winpty/src/winpty.gyp206
-rw-r--r--src/libs/3rdparty/winpty/vcbuild.bat83
-rw-r--r--src/libs/3rdparty/winpty/winpty.qbs205
-rw-r--r--src/libs/3rdparty/xdg/README7
-rw-r--r--src/libs/3rdparty/xdg/freedesktop.org.xml41233
-rw-r--r--src/libs/3rdparty/yaml-cpp/.clang-format47
-rw-r--r--src/libs/3rdparty/yaml-cpp/LICENSE19
-rw-r--r--src/libs/3rdparty/yaml-cpp/README.md58
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/anchor.h17
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/binary.h71
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/depthguard.h77
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h61
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitfromevents.h57
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitter.h281
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitterdef.h16
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emittermanip.h144
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitterstyle.h16
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/eventhandler.h45
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h303
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/mark.h29
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/convert.h469
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/impl.h235
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator.h96
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator_fwd.h27
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/memory.h47
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node.h177
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_data.h127
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_iterator.h181
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_ref.h98
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/emit.h32
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/impl.h385
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/iterator.h34
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/node.h148
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/parse.h78
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/ptr.h28
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/type.h16
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/noexcept.h18
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/null.h26
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/ostream_wrapper.h76
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/parser.h90
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/stlemitter.h50
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/traits.h135
-rw-r--r--src/libs/3rdparty/yaml-cpp/include/yaml-cpp/yaml.h24
-rw-r--r--src/libs/3rdparty/yaml-cpp/patches/0001-yaml-cpp-Strip-unneeded-sources.patch1556
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/binary.cpp100
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/collectionstack.h41
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/convert.cpp74
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/depthguard.cpp9
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/directives.cpp17
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/directives.h29
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/emit.cpp25
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/emitfromevents.cpp124
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/emitter.cpp972
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/emitterstate.cpp400
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/emitterstate.h216
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/emitterutils.cpp497
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/emitterutils.h55
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/exceptions.cpp20
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/exp.cpp137
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/exp.h226
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/indentation.h41
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/memory.cpp26
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/node.cpp12
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/node_data.cpp324
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/nodebuilder.cpp134
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/nodebuilder.h74
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/nodeevents.cpp98
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/nodeevents.h68
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/null.cpp10
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/ostream_wrapper.cpp62
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/parse.cpp72
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/parser.cpp119
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/ptr_vector.h45
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/regex_yaml.cpp43
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/regex_yaml.h88
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/regeximpl.h185
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/scanner.cpp391
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/scanner.h188
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/scanscalar.cpp251
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/scanscalar.h63
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/scantag.cpp81
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/scantag.h19
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/scantoken.cpp437
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/setting.h100
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/simplekey.cpp132
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/singledocparser.cpp435
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/singledocparser.h71
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/stream.cpp446
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/stream.h82
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/streamcharsource.h50
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/stringsource.h48
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/tag.cpp50
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/tag.h33
-rw-r--r--src/libs/3rdparty/yaml-cpp/src/token.h70
-rw-r--r--src/libs/3rdparty/yaml-cpp/yaml-cpp.qbs102
687 files changed, 310602 insertions, 60647 deletions
diff --git a/src/libs/3rdparty/CMakeLists.txt b/src/libs/3rdparty/CMakeLists.txt
index 7cf97ab87f..364c9a425e 100644
--- a/src/libs/3rdparty/CMakeLists.txt
+++ b/src/libs/3rdparty/CMakeLists.txt
@@ -1,2 +1,12 @@
add_subdirectory(cplusplus)
add_subdirectory(syntax-highlighting)
+add_subdirectory(libvterm)
+add_subdirectory(libptyqt)
+add_subdirectory(qrcodegen)
+add_subdirectory(qtkeychain)
+add_subdirectory(lua)
+add_subdirectory(sol2)
+
+if(WIN32)
+ add_subdirectory(winpty)
+endif()
diff --git a/src/libs/3rdparty/cplusplus/AST.cpp b/src/libs/3rdparty/cplusplus/AST.cpp
index 035c9baf73..5c59bb683c 100644
--- a/src/libs/3rdparty/cplusplus/AST.cpp
+++ b/src/libs/3rdparty/cplusplus/AST.cpp
@@ -68,12 +68,22 @@ bool AST::match(AST *pattern, ASTMatcher *matcher)
return match0(pattern, matcher);
}
-unsigned GnuAttributeSpecifierAST::firstToken() const
+int GnuAttributeSpecifierAST::firstToken() const
{
return attribute_token;
}
-unsigned BaseSpecifierAST::firstToken() const
+int MsvcDeclspecSpecifierAST::firstToken() const
+{
+ return attribute_token;
+}
+
+int StdAttributeSpecifierAST::firstToken() const
+{
+ return first_lbracket_token;
+}
+
+int BaseSpecifierAST::firstToken() const
{
if (virtual_token && access_specifier_token)
return std::min(virtual_token, access_specifier_token);
@@ -87,7 +97,7 @@ unsigned BaseSpecifierAST::firstToken() const
return 0;
}
-unsigned BaseSpecifierAST::lastToken() const
+int BaseSpecifierAST::lastToken() const
{
if (ellipsis_token)
return ellipsis_token;
@@ -104,7 +114,7 @@ unsigned BaseSpecifierAST::lastToken() const
}
/** \generated */
-unsigned AccessDeclarationAST::firstToken() const
+int AccessDeclarationAST::firstToken() const
{
if (access_specifier_token)
return access_specifier_token;
@@ -116,7 +126,7 @@ unsigned AccessDeclarationAST::firstToken() const
}
/** \generated */
-unsigned AccessDeclarationAST::lastToken() const
+int AccessDeclarationAST::lastToken() const
{
if (colon_token)
return colon_token + 1;
@@ -128,15 +138,15 @@ unsigned AccessDeclarationAST::lastToken() const
}
/** \generated */
-unsigned ArrayAccessAST::firstToken() const
+int ArrayAccessAST::firstToken() const
{
if (base_expression)
- if (unsigned candidate = base_expression->firstToken())
+ if (int candidate = base_expression->firstToken())
return candidate;
if (lbracket_token)
return lbracket_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rbracket_token)
return rbracket_token;
@@ -144,28 +154,28 @@ unsigned ArrayAccessAST::firstToken() const
}
/** \generated */
-unsigned ArrayAccessAST::lastToken() const
+int ArrayAccessAST::lastToken() const
{
if (rbracket_token)
return rbracket_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lbracket_token)
return lbracket_token + 1;
if (base_expression)
- if (unsigned candidate = base_expression->lastToken())
+ if (int candidate = base_expression->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ArrayDeclaratorAST::firstToken() const
+int ArrayDeclaratorAST::firstToken() const
{
if (lbracket_token)
return lbracket_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rbracket_token)
return rbracket_token;
@@ -173,12 +183,12 @@ unsigned ArrayDeclaratorAST::firstToken() const
}
/** \generated */
-unsigned ArrayDeclaratorAST::lastToken() const
+int ArrayDeclaratorAST::lastToken() const
{
if (rbracket_token)
return rbracket_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lbracket_token)
return lbracket_token + 1;
@@ -186,12 +196,12 @@ unsigned ArrayDeclaratorAST::lastToken() const
}
/** \generated */
-unsigned ArrayInitializerAST::firstToken() const
+int ArrayInitializerAST::firstToken() const
{
if (lbrace_token)
return lbrace_token;
if (expression_list)
- if (unsigned candidate = expression_list->firstToken())
+ if (int candidate = expression_list->firstToken())
return candidate;
if (rbrace_token)
return rbrace_token;
@@ -199,12 +209,12 @@ unsigned ArrayInitializerAST::firstToken() const
}
/** \generated */
-unsigned ArrayInitializerAST::lastToken() const
+int ArrayInitializerAST::lastToken() const
{
if (rbrace_token)
return rbrace_token + 1;
if (expression_list)
- if (unsigned candidate = expression_list->lastToken())
+ if (int candidate = expression_list->lastToken())
return candidate;
if (lbrace_token)
return lbrace_token + 1;
@@ -212,7 +222,7 @@ unsigned ArrayInitializerAST::lastToken() const
}
/** \generated */
-unsigned AsmDefinitionAST::firstToken() const
+int AsmDefinitionAST::firstToken() const
{
if (asm_token)
return asm_token;
@@ -228,7 +238,7 @@ unsigned AsmDefinitionAST::firstToken() const
}
/** \generated */
-unsigned AsmDefinitionAST::lastToken() const
+int AsmDefinitionAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
@@ -244,7 +254,7 @@ unsigned AsmDefinitionAST::lastToken() const
}
/** \generated */
-unsigned GnuAttributeAST::firstToken() const
+int GnuAttributeAST::firstToken() const
{
if (identifier_token)
return identifier_token;
@@ -253,7 +263,7 @@ unsigned GnuAttributeAST::firstToken() const
if (tag_token)
return tag_token;
if (expression_list)
- if (unsigned candidate = expression_list->firstToken())
+ if (int candidate = expression_list->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -261,12 +271,12 @@ unsigned GnuAttributeAST::firstToken() const
}
/** \generated */
-unsigned GnuAttributeAST::lastToken() const
+int GnuAttributeAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (expression_list)
- if (unsigned candidate = expression_list->lastToken())
+ if (int candidate = expression_list->lastToken())
return candidate;
if (tag_token)
return tag_token + 1;
@@ -278,35 +288,35 @@ unsigned GnuAttributeAST::lastToken() const
}
/** \generated */
-unsigned BinaryExpressionAST::firstToken() const
+int BinaryExpressionAST::firstToken() const
{
if (left_expression)
- if (unsigned candidate = left_expression->firstToken())
+ if (int candidate = left_expression->firstToken())
return candidate;
if (binary_op_token)
return binary_op_token;
if (right_expression)
- if (unsigned candidate = right_expression->firstToken())
+ if (int candidate = right_expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned BinaryExpressionAST::lastToken() const
+int BinaryExpressionAST::lastToken() const
{
if (right_expression)
- if (unsigned candidate = right_expression->lastToken())
+ if (int candidate = right_expression->lastToken())
return candidate;
if (binary_op_token)
return binary_op_token + 1;
if (left_expression)
- if (unsigned candidate = left_expression->lastToken())
+ if (int candidate = left_expression->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned BoolLiteralAST::firstToken() const
+int BoolLiteralAST::firstToken() const
{
if (literal_token)
return literal_token;
@@ -314,7 +324,7 @@ unsigned BoolLiteralAST::firstToken() const
}
/** \generated */
-unsigned BoolLiteralAST::lastToken() const
+int BoolLiteralAST::lastToken() const
{
if (literal_token)
return literal_token + 1;
@@ -322,12 +332,12 @@ unsigned BoolLiteralAST::lastToken() const
}
/** \generated */
-unsigned BracedInitializerAST::firstToken() const
+int BracedInitializerAST::firstToken() const
{
if (lbrace_token)
return lbrace_token;
if (expression_list)
- if (unsigned candidate = expression_list->firstToken())
+ if (int candidate = expression_list->firstToken())
return candidate;
if (comma_token)
return comma_token;
@@ -337,14 +347,14 @@ unsigned BracedInitializerAST::firstToken() const
}
/** \generated */
-unsigned BracedInitializerAST::lastToken() const
+int BracedInitializerAST::lastToken() const
{
if (rbrace_token)
return rbrace_token + 1;
if (comma_token)
return comma_token + 1;
if (expression_list)
- if (unsigned candidate = expression_list->lastToken())
+ if (int candidate = expression_list->lastToken())
return candidate;
if (lbrace_token)
return lbrace_token + 1;
@@ -352,7 +362,7 @@ unsigned BracedInitializerAST::lastToken() const
}
/** \generated */
-unsigned BreakStatementAST::firstToken() const
+int BreakStatementAST::firstToken() const
{
if (break_token)
return break_token;
@@ -362,7 +372,7 @@ unsigned BreakStatementAST::firstToken() const
}
/** \generated */
-unsigned BreakStatementAST::lastToken() const
+int BreakStatementAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
@@ -372,15 +382,15 @@ unsigned BreakStatementAST::lastToken() const
}
/** \generated */
-unsigned CallAST::firstToken() const
+int CallAST::firstToken() const
{
if (base_expression)
- if (unsigned candidate = base_expression->firstToken())
+ if (int candidate = base_expression->firstToken())
return candidate;
if (lparen_token)
return lparen_token;
if (expression_list)
- if (unsigned candidate = expression_list->firstToken())
+ if (int candidate = expression_list->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -388,37 +398,37 @@ unsigned CallAST::firstToken() const
}
/** \generated */
-unsigned CallAST::lastToken() const
+int CallAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (expression_list)
- if (unsigned candidate = expression_list->lastToken())
+ if (int candidate = expression_list->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
if (base_expression)
- if (unsigned candidate = base_expression->lastToken())
+ if (int candidate = base_expression->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned CaptureAST::firstToken() const
+int CaptureAST::firstToken() const
{
if (amper_token)
return amper_token;
if (identifier)
- if (unsigned candidate = identifier->firstToken())
+ if (int candidate = identifier->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned CaptureAST::lastToken() const
+int CaptureAST::lastToken() const
{
if (identifier)
- if (unsigned candidate = identifier->lastToken())
+ if (int candidate = identifier->lastToken())
return candidate;
if (amper_token)
return amper_token + 1;
@@ -426,31 +436,31 @@ unsigned CaptureAST::lastToken() const
}
/** \generated */
-unsigned CaseStatementAST::firstToken() const
+int CaseStatementAST::firstToken() const
{
if (case_token)
return case_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (colon_token)
return colon_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned CaseStatementAST::lastToken() const
+int CaseStatementAST::lastToken() const
{
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (colon_token)
return colon_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (case_token)
return case_token + 1;
@@ -458,31 +468,31 @@ unsigned CaseStatementAST::lastToken() const
}
/** \generated */
-unsigned CastExpressionAST::firstToken() const
+int CastExpressionAST::firstToken() const
{
if (lparen_token)
return lparen_token;
if (type_id)
- if (unsigned candidate = type_id->firstToken())
+ if (int candidate = type_id->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned CastExpressionAST::lastToken() const
+int CastExpressionAST::lastToken() const
{
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (type_id)
- if (unsigned candidate = type_id->lastToken())
+ if (int candidate = type_id->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -490,33 +500,33 @@ unsigned CastExpressionAST::lastToken() const
}
/** \generated */
-unsigned CatchClauseAST::firstToken() const
+int CatchClauseAST::firstToken() const
{
if (catch_token)
return catch_token;
if (lparen_token)
return lparen_token;
if (exception_declaration)
- if (unsigned candidate = exception_declaration->firstToken())
+ if (int candidate = exception_declaration->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned CatchClauseAST::lastToken() const
+int CatchClauseAST::lastToken() const
{
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (exception_declaration)
- if (unsigned candidate = exception_declaration->lastToken())
+ if (int candidate = exception_declaration->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -526,29 +536,29 @@ unsigned CatchClauseAST::lastToken() const
}
/** \generated */
-unsigned ClassSpecifierAST::firstToken() const
+int ClassSpecifierAST::firstToken() const
{
if (classkey_token)
return classkey_token;
if (attribute_list)
- if (unsigned candidate = attribute_list->firstToken())
+ if (int candidate = attribute_list->firstToken())
return candidate;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
if (final_token)
return final_token;
if (colon_token)
return colon_token;
if (base_clause_list)
- if (unsigned candidate = base_clause_list->firstToken())
+ if (int candidate = base_clause_list->firstToken())
return candidate;
if (dot_dot_dot_token)
return dot_dot_dot_token;
if (lbrace_token)
return lbrace_token;
if (member_specifier_list)
- if (unsigned candidate = member_specifier_list->firstToken())
+ if (int candidate = member_specifier_list->firstToken())
return candidate;
if (rbrace_token)
return rbrace_token;
@@ -556,29 +566,29 @@ unsigned ClassSpecifierAST::firstToken() const
}
/** \generated */
-unsigned ClassSpecifierAST::lastToken() const
+int ClassSpecifierAST::lastToken() const
{
if (rbrace_token)
return rbrace_token + 1;
if (member_specifier_list)
- if (unsigned candidate = member_specifier_list->lastToken())
+ if (int candidate = member_specifier_list->lastToken())
return candidate;
if (lbrace_token)
return lbrace_token + 1;
if (dot_dot_dot_token)
return dot_dot_dot_token + 1;
if (base_clause_list)
- if (unsigned candidate = base_clause_list->lastToken())
+ if (int candidate = base_clause_list->lastToken())
return candidate;
if (colon_token)
return colon_token + 1;
if (final_token)
return final_token + 1;
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
if (classkey_token)
return classkey_token + 1;
@@ -586,12 +596,12 @@ unsigned ClassSpecifierAST::lastToken() const
}
/** \generated */
-unsigned CompoundExpressionAST::firstToken() const
+int CompoundExpressionAST::firstToken() const
{
if (lparen_token)
return lparen_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -599,12 +609,12 @@ unsigned CompoundExpressionAST::firstToken() const
}
/** \generated */
-unsigned CompoundExpressionAST::lastToken() const
+int CompoundExpressionAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -612,31 +622,31 @@ unsigned CompoundExpressionAST::lastToken() const
}
/** \generated */
-unsigned CompoundLiteralAST::firstToken() const
+int CompoundLiteralAST::firstToken() const
{
if (lparen_token)
return lparen_token;
if (type_id)
- if (unsigned candidate = type_id->firstToken())
+ if (int candidate = type_id->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (initializer)
- if (unsigned candidate = initializer->firstToken())
+ if (int candidate = initializer->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned CompoundLiteralAST::lastToken() const
+int CompoundLiteralAST::lastToken() const
{
if (initializer)
- if (unsigned candidate = initializer->lastToken())
+ if (int candidate = initializer->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (type_id)
- if (unsigned candidate = type_id->lastToken())
+ if (int candidate = type_id->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -644,12 +654,12 @@ unsigned CompoundLiteralAST::lastToken() const
}
/** \generated */
-unsigned CompoundStatementAST::firstToken() const
+int CompoundStatementAST::firstToken() const
{
if (lbrace_token)
return lbrace_token;
if (statement_list)
- if (unsigned candidate = statement_list->firstToken())
+ if (int candidate = statement_list->firstToken())
return candidate;
if (rbrace_token)
return rbrace_token;
@@ -657,12 +667,12 @@ unsigned CompoundStatementAST::firstToken() const
}
/** \generated */
-unsigned CompoundStatementAST::lastToken() const
+int CompoundStatementAST::lastToken() const
{
if (rbrace_token)
return rbrace_token + 1;
if (statement_list)
- if (unsigned candidate = statement_list->lastToken())
+ if (int candidate = statement_list->lastToken())
return candidate;
if (lbrace_token)
return lbrace_token + 1;
@@ -670,69 +680,69 @@ unsigned CompoundStatementAST::lastToken() const
}
/** \generated */
-unsigned ConditionAST::firstToken() const
+int ConditionAST::firstToken() const
{
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ConditionAST::lastToken() const
+int ConditionAST::lastToken() const
{
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ConditionalExpressionAST::firstToken() const
+int ConditionalExpressionAST::firstToken() const
{
if (condition)
- if (unsigned candidate = condition->firstToken())
+ if (int candidate = condition->firstToken())
return candidate;
if (question_token)
return question_token;
if (left_expression)
- if (unsigned candidate = left_expression->firstToken())
+ if (int candidate = left_expression->firstToken())
return candidate;
if (colon_token)
return colon_token;
if (right_expression)
- if (unsigned candidate = right_expression->firstToken())
+ if (int candidate = right_expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ConditionalExpressionAST::lastToken() const
+int ConditionalExpressionAST::lastToken() const
{
if (right_expression)
- if (unsigned candidate = right_expression->lastToken())
+ if (int candidate = right_expression->lastToken())
return candidate;
if (colon_token)
return colon_token + 1;
if (left_expression)
- if (unsigned candidate = left_expression->lastToken())
+ if (int candidate = left_expression->lastToken())
return candidate;
if (question_token)
return question_token + 1;
if (condition)
- if (unsigned candidate = condition->lastToken())
+ if (int candidate = condition->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ContinueStatementAST::firstToken() const
+int ContinueStatementAST::firstToken() const
{
if (continue_token)
return continue_token;
@@ -742,7 +752,7 @@ unsigned ContinueStatementAST::firstToken() const
}
/** \generated */
-unsigned ContinueStatementAST::lastToken() const
+int ContinueStatementAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
@@ -752,27 +762,27 @@ unsigned ContinueStatementAST::lastToken() const
}
/** \generated */
-unsigned ConversionFunctionIdAST::firstToken() const
+int ConversionFunctionIdAST::firstToken() const
{
if (operator_token)
return operator_token;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (ptr_operator_list)
- if (unsigned candidate = ptr_operator_list->firstToken())
+ if (int candidate = ptr_operator_list->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ConversionFunctionIdAST::lastToken() const
+int ConversionFunctionIdAST::lastToken() const
{
if (ptr_operator_list)
- if (unsigned candidate = ptr_operator_list->lastToken())
+ if (int candidate = ptr_operator_list->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
if (operator_token)
return operator_token + 1;
@@ -780,21 +790,21 @@ unsigned ConversionFunctionIdAST::lastToken() const
}
/** \generated */
-unsigned CppCastExpressionAST::firstToken() const
+int CppCastExpressionAST::firstToken() const
{
if (cast_token)
return cast_token;
if (less_token)
return less_token;
if (type_id)
- if (unsigned candidate = type_id->firstToken())
+ if (int candidate = type_id->firstToken())
return candidate;
if (greater_token)
return greater_token;
if (lparen_token)
return lparen_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -802,19 +812,19 @@ unsigned CppCastExpressionAST::firstToken() const
}
/** \generated */
-unsigned CppCastExpressionAST::lastToken() const
+int CppCastExpressionAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
if (greater_token)
return greater_token + 1;
if (type_id)
- if (unsigned candidate = type_id->lastToken())
+ if (int candidate = type_id->lastToken())
return candidate;
if (less_token)
return less_token + 1;
@@ -824,12 +834,12 @@ unsigned CppCastExpressionAST::lastToken() const
}
/** \generated */
-unsigned CtorInitializerAST::firstToken() const
+int CtorInitializerAST::firstToken() const
{
if (colon_token)
return colon_token;
if (member_initializer_list)
- if (unsigned candidate = member_initializer_list->firstToken())
+ if (int candidate = member_initializer_list->firstToken())
return candidate;
if (dot_dot_dot_token)
return dot_dot_dot_token;
@@ -837,12 +847,12 @@ unsigned CtorInitializerAST::firstToken() const
}
/** \generated */
-unsigned CtorInitializerAST::lastToken() const
+int CtorInitializerAST::lastToken() const
{
if (dot_dot_dot_token)
return dot_dot_dot_token + 1;
if (member_initializer_list)
- if (unsigned candidate = member_initializer_list->lastToken())
+ if (int candidate = member_initializer_list->lastToken())
return candidate;
if (colon_token)
return colon_token + 1;
@@ -850,91 +860,93 @@ unsigned CtorInitializerAST::lastToken() const
}
/** \generated */
-unsigned DeclarationStatementAST::firstToken() const
+int DeclarationStatementAST::firstToken() const
{
if (declaration)
- if (unsigned candidate = declaration->firstToken())
+ if (int candidate = declaration->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned DeclarationStatementAST::lastToken() const
+int DeclarationStatementAST::lastToken() const
{
if (declaration)
- if (unsigned candidate = declaration->lastToken())
+ if (int candidate = declaration->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned DeclaratorAST::firstToken() const
+int DeclaratorAST::firstToken() const
{
if (attribute_list)
- if (unsigned candidate = attribute_list->firstToken())
+ if (int candidate = attribute_list->firstToken())
return candidate;
if (ptr_operator_list)
- if (unsigned candidate = ptr_operator_list->firstToken())
+ if (int candidate = ptr_operator_list->firstToken())
return candidate;
if (core_declarator)
- if (unsigned candidate = core_declarator->firstToken())
+ if (int candidate = core_declarator->firstToken())
return candidate;
if (postfix_declarator_list)
- if (unsigned candidate = postfix_declarator_list->firstToken())
+ if (int candidate = postfix_declarator_list->firstToken())
return candidate;
if (post_attribute_list)
- if (unsigned candidate = post_attribute_list->firstToken())
+ if (int candidate = post_attribute_list->firstToken())
return candidate;
if (equal_token)
return equal_token;
if (initializer)
- if (unsigned candidate = initializer->firstToken())
+ if (int candidate = initializer->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned DeclaratorAST::lastToken() const
+int DeclaratorAST::lastToken() const
{
if (initializer)
- if (unsigned candidate = initializer->lastToken())
+ if (int candidate = initializer->lastToken())
return candidate;
if (equal_token)
return equal_token + 1;
+ if (requiresClause)
+ return requiresClause->lastToken();
if (post_attribute_list)
- if (unsigned candidate = post_attribute_list->lastToken())
+ if (int candidate = post_attribute_list->lastToken())
return candidate;
if (postfix_declarator_list)
- if (unsigned candidate = postfix_declarator_list->lastToken())
+ if (int candidate = postfix_declarator_list->lastToken())
return candidate;
if (core_declarator)
- if (unsigned candidate = core_declarator->lastToken())
+ if (int candidate = core_declarator->lastToken())
return candidate;
if (ptr_operator_list)
- if (unsigned candidate = ptr_operator_list->lastToken())
+ if (int candidate = ptr_operator_list->lastToken())
return candidate;
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned DeclaratorIdAST::firstToken() const
+int DeclaratorIdAST::firstToken() const
{
if (dot_dot_dot_token)
return dot_dot_dot_token;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned DeclaratorIdAST::lastToken() const
+int DeclaratorIdAST::lastToken() const
{
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (dot_dot_dot_token)
return dot_dot_dot_token + 1;
@@ -942,7 +954,7 @@ unsigned DeclaratorIdAST::lastToken() const
}
/** \generated */
-unsigned DeleteExpressionAST::firstToken() const
+int DeleteExpressionAST::firstToken() const
{
if (scope_token)
return scope_token;
@@ -953,16 +965,16 @@ unsigned DeleteExpressionAST::firstToken() const
if (rbracket_token)
return rbracket_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned DeleteExpressionAST::lastToken() const
+int DeleteExpressionAST::lastToken() const
{
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (rbracket_token)
return rbracket_token + 1;
@@ -976,21 +988,21 @@ unsigned DeleteExpressionAST::lastToken() const
}
/** \generated */
-unsigned DestructorNameAST::firstToken() const
+int DestructorNameAST::firstToken() const
{
if (tilde_token)
return tilde_token;
if (unqualified_name)
- if (unsigned candidate = unqualified_name->firstToken())
+ if (int candidate = unqualified_name->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned DestructorNameAST::lastToken() const
+int DestructorNameAST::lastToken() const
{
if (unqualified_name)
- if (unsigned candidate = unqualified_name->lastToken())
+ if (int candidate = unqualified_name->lastToken())
return candidate;
if (tilde_token)
return tilde_token + 1;
@@ -998,19 +1010,19 @@ unsigned DestructorNameAST::lastToken() const
}
/** \generated */
-unsigned DoStatementAST::firstToken() const
+int DoStatementAST::firstToken() const
{
if (do_token)
return do_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
if (while_token)
return while_token;
if (lparen_token)
return lparen_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -1020,21 +1032,21 @@ unsigned DoStatementAST::firstToken() const
}
/** \generated */
-unsigned DoStatementAST::lastToken() const
+int DoStatementAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (rparen_token)
return rparen_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
if (while_token)
return while_token + 1;
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (do_token)
return do_token + 1;
@@ -1042,27 +1054,27 @@ unsigned DoStatementAST::lastToken() const
}
/** \generated */
-unsigned ElaboratedTypeSpecifierAST::firstToken() const
+int ElaboratedTypeSpecifierAST::firstToken() const
{
if (classkey_token)
return classkey_token;
if (attribute_list)
- if (unsigned candidate = attribute_list->firstToken())
+ if (int candidate = attribute_list->firstToken())
return candidate;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ElaboratedTypeSpecifierAST::lastToken() const
+int ElaboratedTypeSpecifierAST::lastToken() const
{
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
if (classkey_token)
return classkey_token + 1;
@@ -1070,7 +1082,7 @@ unsigned ElaboratedTypeSpecifierAST::lastToken() const
}
/** \generated */
-unsigned EmptyDeclarationAST::firstToken() const
+int EmptyDeclarationAST::firstToken() const
{
if (semicolon_token)
return semicolon_token;
@@ -1078,7 +1090,7 @@ unsigned EmptyDeclarationAST::firstToken() const
}
/** \generated */
-unsigned EmptyDeclarationAST::lastToken() const
+int EmptyDeclarationAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
@@ -1086,24 +1098,24 @@ unsigned EmptyDeclarationAST::lastToken() const
}
/** \generated */
-unsigned EnumSpecifierAST::firstToken() const
+int EnumSpecifierAST::firstToken() const
{
if (enum_token)
return enum_token;
if (key_token)
return key_token;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
if (colon_token)
return colon_token;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (lbrace_token)
return lbrace_token;
if (enumerator_list)
- if (unsigned candidate = enumerator_list->firstToken())
+ if (int candidate = enumerator_list->firstToken())
return candidate;
if (stray_comma_token)
return stray_comma_token;
@@ -1113,24 +1125,24 @@ unsigned EnumSpecifierAST::firstToken() const
}
/** \generated */
-unsigned EnumSpecifierAST::lastToken() const
+int EnumSpecifierAST::lastToken() const
{
if (rbrace_token)
return rbrace_token + 1;
if (stray_comma_token)
return stray_comma_token + 1;
if (enumerator_list)
- if (unsigned candidate = enumerator_list->lastToken())
+ if (int candidate = enumerator_list->lastToken())
return candidate;
if (lbrace_token)
return lbrace_token + 1;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
if (colon_token)
return colon_token + 1;
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (key_token)
return key_token + 1;
@@ -1140,23 +1152,23 @@ unsigned EnumSpecifierAST::lastToken() const
}
/** \generated */
-unsigned EnumeratorAST::firstToken() const
+int EnumeratorAST::firstToken() const
{
if (identifier_token)
return identifier_token;
if (equal_token)
return equal_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned EnumeratorAST::lastToken() const
+int EnumeratorAST::lastToken() const
{
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (equal_token)
return equal_token + 1;
@@ -1166,13 +1178,13 @@ unsigned EnumeratorAST::lastToken() const
}
/** \generated */
-unsigned ExceptionDeclarationAST::firstToken() const
+int ExceptionDeclarationAST::firstToken() const
{
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
if (dot_dot_dot_token)
return dot_dot_dot_token;
@@ -1180,21 +1192,21 @@ unsigned ExceptionDeclarationAST::firstToken() const
}
/** \generated */
-unsigned ExceptionDeclarationAST::lastToken() const
+int ExceptionDeclarationAST::lastToken() const
{
if (dot_dot_dot_token)
return dot_dot_dot_token + 1;
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned DynamicExceptionSpecificationAST::firstToken() const
+int DynamicExceptionSpecificationAST::firstToken() const
{
if (throw_token)
return throw_token;
@@ -1203,7 +1215,7 @@ unsigned DynamicExceptionSpecificationAST::firstToken() const
if (dot_dot_dot_token)
return dot_dot_dot_token;
if (type_id_list)
- if (unsigned candidate = type_id_list->firstToken())
+ if (int candidate = type_id_list->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -1211,12 +1223,12 @@ unsigned DynamicExceptionSpecificationAST::firstToken() const
}
/** \generated */
-unsigned DynamicExceptionSpecificationAST::lastToken() const
+int DynamicExceptionSpecificationAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (type_id_list)
- if (unsigned candidate = type_id_list->lastToken())
+ if (int candidate = type_id_list->lastToken())
return candidate;
if (dot_dot_dot_token)
return dot_dot_dot_token + 1;
@@ -1228,34 +1240,34 @@ unsigned DynamicExceptionSpecificationAST::lastToken() const
}
/** \generated */
-unsigned ExpressionOrDeclarationStatementAST::firstToken() const
+int ExpressionOrDeclarationStatementAST::firstToken() const
{
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (declaration)
- if (unsigned candidate = declaration->firstToken())
+ if (int candidate = declaration->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ExpressionOrDeclarationStatementAST::lastToken() const
+int ExpressionOrDeclarationStatementAST::lastToken() const
{
if (declaration)
- if (unsigned candidate = declaration->lastToken())
+ if (int candidate = declaration->lastToken())
return candidate;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ExpressionStatementAST::firstToken() const
+int ExpressionStatementAST::firstToken() const
{
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -1263,60 +1275,60 @@ unsigned ExpressionStatementAST::firstToken() const
}
/** \generated */
-unsigned ExpressionStatementAST::lastToken() const
+int ExpressionStatementAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ForStatementAST::firstToken() const
+int ForStatementAST::firstToken() const
{
if (for_token)
return for_token;
if (lparen_token)
return lparen_token;
if (initializer)
- if (unsigned candidate = initializer->firstToken())
+ if (int candidate = initializer->firstToken())
return candidate;
if (condition)
- if (unsigned candidate = condition->firstToken())
+ if (int candidate = condition->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ForStatementAST::lastToken() const
+int ForStatementAST::lastToken() const
{
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (semicolon_token)
return semicolon_token + 1;
if (condition)
- if (unsigned candidate = condition->lastToken())
+ if (int candidate = condition->lastToken())
return candidate;
if (initializer)
- if (unsigned candidate = initializer->lastToken())
+ if (int candidate = initializer->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -1326,55 +1338,55 @@ unsigned ForStatementAST::lastToken() const
}
/** \generated */
-unsigned ForeachStatementAST::firstToken() const
+int ForeachStatementAST::firstToken() const
{
if (foreach_token)
return foreach_token;
if (lparen_token)
return lparen_token;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
if (initializer)
- if (unsigned candidate = initializer->firstToken())
+ if (int candidate = initializer->firstToken())
return candidate;
if (comma_token)
return comma_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ForeachStatementAST::lastToken() const
+int ForeachStatementAST::lastToken() const
{
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (comma_token)
return comma_token + 1;
if (initializer)
- if (unsigned candidate = initializer->lastToken())
+ if (int candidate = initializer->lastToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -1383,54 +1395,63 @@ unsigned ForeachStatementAST::lastToken() const
return 1;
}
-/** \generated */
-unsigned FunctionDeclaratorAST::firstToken() const
+int FunctionDeclaratorAST::firstToken() const
{
if (lparen_token)
return lparen_token;
if (parameter_declaration_clause)
- if (unsigned candidate = parameter_declaration_clause->firstToken())
+ if (int candidate = parameter_declaration_clause->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
- if (cv_qualifier_list)
- if (unsigned candidate = cv_qualifier_list->firstToken())
- return candidate;
- if (ref_qualifier_token)
+ const int firstQualListToken = cv_qualifier_list ? cv_qualifier_list->firstToken() : 0;
+ const auto isBeforeFirstQualListToken = [firstQualListToken](int token) {
+ return token && (!firstQualListToken || token < firstQualListToken);
+ };
+ if (isBeforeFirstQualListToken(ref_qualifier_token))
return ref_qualifier_token;
- if (exception_specification)
- if (unsigned candidate = exception_specification->firstToken())
- return candidate;
- if (trailing_return_type)
- if (unsigned candidate = trailing_return_type->firstToken())
- return candidate;
+ if (exception_specification) {
+ const int candidate = exception_specification->firstToken();
+ if (isBeforeFirstQualListToken(candidate))
+ return candidate;
+ }
+ if (trailing_return_type) {
+ const int candidate = trailing_return_type->firstToken();
+ if (isBeforeFirstQualListToken(candidate))
+ return candidate;
+ }
+ if (firstQualListToken)
+ return firstQualListToken;
if (as_cpp_initializer)
- if (unsigned candidate = as_cpp_initializer->firstToken())
+ if (int candidate = as_cpp_initializer->firstToken())
return candidate;
return 0;
}
-/** \generated */
-unsigned FunctionDeclaratorAST::lastToken() const
+int FunctionDeclaratorAST::lastToken() const
{
if (as_cpp_initializer)
- if (unsigned candidate = as_cpp_initializer->lastToken())
- return candidate;
- if (trailing_return_type)
- if (unsigned candidate = trailing_return_type->lastToken())
- return candidate;
- if (exception_specification)
- if (unsigned candidate = exception_specification->lastToken())
- return candidate;
- if (ref_qualifier_token)
- return ref_qualifier_token + 1;
- if (cv_qualifier_list)
- if (unsigned candidate = cv_qualifier_list->lastToken())
- return candidate;
+ if (int candidate = as_cpp_initializer->lastToken())
+ return candidate;
+ const int lastQualListToken = cv_qualifier_list ? cv_qualifier_list->lastToken() : 0;
+ const auto tokenOrLastQualListToken = [lastQualListToken](int token) {
+ return std::max(token ? token + 1 : 0, lastQualListToken);
+ };
+ const auto tokenFromAstOrLastQualListToken = [lastQualListToken](const AST *ast) {
+ return std::max(ast ? ast->lastToken() : 0, lastQualListToken);
+ };
+ if (int candidate = tokenFromAstOrLastQualListToken(trailing_return_type))
+ return candidate;
+ if (int candidate = tokenFromAstOrLastQualListToken(exception_specification))
+ return candidate;
+ if (int candidate = tokenOrLastQualListToken(ref_qualifier_token))
+ return candidate;
+ if (lastQualListToken)
+ return lastQualListToken;
if (rparen_token)
return rparen_token + 1;
if (parameter_declaration_clause)
- if (unsigned candidate = parameter_declaration_clause->lastToken())
+ if (int candidate = parameter_declaration_clause->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -1438,39 +1459,39 @@ unsigned FunctionDeclaratorAST::lastToken() const
}
/** \generated */
-unsigned FunctionDefinitionAST::firstToken() const
+int FunctionDefinitionAST::firstToken() const
{
if (qt_invokable_token)
return qt_invokable_token;
if (decl_specifier_list)
- if (unsigned candidate = decl_specifier_list->firstToken())
+ if (int candidate = decl_specifier_list->firstToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
if (ctor_initializer)
- if (unsigned candidate = ctor_initializer->firstToken())
+ if (int candidate = ctor_initializer->firstToken())
return candidate;
if (function_body)
- if (unsigned candidate = function_body->firstToken())
+ if (int candidate = function_body->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned FunctionDefinitionAST::lastToken() const
+int FunctionDefinitionAST::lastToken() const
{
if (function_body)
- if (unsigned candidate = function_body->lastToken())
+ if (int candidate = function_body->lastToken())
return candidate;
if (ctor_initializer)
- if (unsigned candidate = ctor_initializer->lastToken())
+ if (int candidate = ctor_initializer->lastToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (decl_specifier_list)
- if (unsigned candidate = decl_specifier_list->lastToken())
+ if (int candidate = decl_specifier_list->lastToken())
return candidate;
if (qt_invokable_token)
return qt_invokable_token + 1;
@@ -1478,7 +1499,7 @@ unsigned FunctionDefinitionAST::lastToken() const
}
/** \generated */
-unsigned GotoStatementAST::firstToken() const
+int GotoStatementAST::firstToken() const
{
if (goto_token)
return goto_token;
@@ -1490,7 +1511,7 @@ unsigned GotoStatementAST::firstToken() const
}
/** \generated */
-unsigned GotoStatementAST::lastToken() const
+int GotoStatementAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
@@ -1502,87 +1523,91 @@ unsigned GotoStatementAST::lastToken() const
}
/** \generated */
-unsigned IdExpressionAST::firstToken() const
+int IdExpressionAST::firstToken() const
{
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned IdExpressionAST::lastToken() const
+int IdExpressionAST::lastToken() const
{
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned IfStatementAST::firstToken() const
+int IfStatementAST::firstToken() const
{
if (if_token)
return if_token;
+ if (constexpr_token)
+ return constexpr_token;
if (lparen_token)
return lparen_token;
if (condition)
- if (unsigned candidate = condition->firstToken())
+ if (int candidate = condition->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
if (else_token)
return else_token;
if (else_statement)
- if (unsigned candidate = else_statement->firstToken())
+ if (int candidate = else_statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned IfStatementAST::lastToken() const
+int IfStatementAST::lastToken() const
{
if (else_statement)
- if (unsigned candidate = else_statement->lastToken())
+ if (int candidate = else_statement->lastToken())
return candidate;
if (else_token)
return else_token + 1;
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (condition)
- if (unsigned candidate = condition->lastToken())
+ if (int candidate = condition->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
+ if (constexpr_token)
+ return constexpr_token + 1;
if (if_token)
return if_token + 1;
return 1;
}
/** \generated */
-unsigned LabeledStatementAST::firstToken() const
+int LabeledStatementAST::firstToken() const
{
if (label_token)
return label_token;
if (colon_token)
return colon_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned LabeledStatementAST::lastToken() const
+int LabeledStatementAST::lastToken() const
{
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (colon_token)
return colon_token + 1;
@@ -1592,21 +1617,21 @@ unsigned LabeledStatementAST::lastToken() const
}
/** \generated */
-unsigned LambdaCaptureAST::firstToken() const
+int LambdaCaptureAST::firstToken() const
{
if (default_capture_token)
return default_capture_token;
if (capture_list)
- if (unsigned candidate = capture_list->firstToken())
+ if (int candidate = capture_list->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned LambdaCaptureAST::lastToken() const
+int LambdaCaptureAST::lastToken() const
{
if (capture_list)
- if (unsigned candidate = capture_list->lastToken())
+ if (int candidate = capture_list->lastToken())
return candidate;
if (default_capture_token)
return default_capture_token + 1;
@@ -1614,47 +1639,53 @@ unsigned LambdaCaptureAST::lastToken() const
}
/** \generated */
-unsigned LambdaDeclaratorAST::firstToken() const
+int LambdaDeclaratorAST::firstToken() const
{
if (lparen_token)
return lparen_token;
if (parameter_declaration_clause)
- if (unsigned candidate = parameter_declaration_clause->firstToken())
+ if (int candidate = parameter_declaration_clause->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (attributes)
- if (unsigned candidate = attributes->firstToken())
+ if (int candidate = attributes->firstToken())
return candidate;
if (mutable_token)
return mutable_token;
if (exception_specification)
- if (unsigned candidate = exception_specification->firstToken())
+ if (int candidate = exception_specification->firstToken())
return candidate;
if (trailing_return_type)
- if (unsigned candidate = trailing_return_type->firstToken())
+ if (int candidate = trailing_return_type->firstToken())
+ return candidate;
+ if (requiresClause)
+ if (int candidate = requiresClause->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned LambdaDeclaratorAST::lastToken() const
+int LambdaDeclaratorAST::lastToken() const
{
+ if (requiresClause)
+ if (int candidate = requiresClause->firstToken())
+ return candidate;
if (trailing_return_type)
- if (unsigned candidate = trailing_return_type->lastToken())
+ if (int candidate = trailing_return_type->lastToken())
return candidate;
if (exception_specification)
- if (unsigned candidate = exception_specification->lastToken())
+ if (int candidate = exception_specification->lastToken())
return candidate;
if (mutable_token)
return mutable_token + 1;
if (attributes)
- if (unsigned candidate = attributes->lastToken())
+ if (int candidate = attributes->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (parameter_declaration_clause)
- if (unsigned candidate = parameter_declaration_clause->lastToken())
+ if (int candidate = parameter_declaration_clause->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -1662,42 +1693,60 @@ unsigned LambdaDeclaratorAST::lastToken() const
}
/** \generated */
-unsigned LambdaExpressionAST::firstToken() const
+int LambdaExpressionAST::firstToken() const
{
if (lambda_introducer)
- if (unsigned candidate = lambda_introducer->firstToken())
+ if (int candidate = lambda_introducer->firstToken())
+ return candidate;
+ if (templateParameters)
+ if (int candidate = templateParameters->firstToken())
+ return candidate;
+ if (requiresClause)
+ if (int candidate = requiresClause->firstToken())
+ return candidate;
+ if (attributes)
+ if (int candidate = attributes->firstToken())
return candidate;
if (lambda_declarator)
- if (unsigned candidate = lambda_declarator->firstToken())
+ if (int candidate = lambda_declarator->firstToken())
return candidate;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned LambdaExpressionAST::lastToken() const
+int LambdaExpressionAST::lastToken() const
{
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (lambda_declarator)
- if (unsigned candidate = lambda_declarator->lastToken())
+ if (int candidate = lambda_declarator->lastToken())
+ return candidate;
+ if (attributes)
+ if (int candidate = attributes->firstToken())
+ return candidate;
+ if (requiresClause)
+ if (int candidate = requiresClause->firstToken())
+ return candidate;
+ if (templateParameters)
+ if (int candidate = templateParameters->firstToken())
return candidate;
if (lambda_introducer)
- if (unsigned candidate = lambda_introducer->lastToken())
+ if (int candidate = lambda_introducer->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned LambdaIntroducerAST::firstToken() const
+int LambdaIntroducerAST::firstToken() const
{
if (lbracket_token)
return lbracket_token;
if (lambda_capture)
- if (unsigned candidate = lambda_capture->firstToken())
+ if (int candidate = lambda_capture->firstToken())
return candidate;
if (rbracket_token)
return rbracket_token;
@@ -1705,12 +1754,12 @@ unsigned LambdaIntroducerAST::firstToken() const
}
/** \generated */
-unsigned LambdaIntroducerAST::lastToken() const
+int LambdaIntroducerAST::lastToken() const
{
if (rbracket_token)
return rbracket_token + 1;
if (lambda_capture)
- if (unsigned candidate = lambda_capture->lastToken())
+ if (int candidate = lambda_capture->lastToken())
return candidate;
if (lbracket_token)
return lbracket_token + 1;
@@ -1718,12 +1767,12 @@ unsigned LambdaIntroducerAST::lastToken() const
}
/** \generated */
-unsigned LinkageBodyAST::firstToken() const
+int LinkageBodyAST::firstToken() const
{
if (lbrace_token)
return lbrace_token;
if (declaration_list)
- if (unsigned candidate = declaration_list->firstToken())
+ if (int candidate = declaration_list->firstToken())
return candidate;
if (rbrace_token)
return rbrace_token;
@@ -1731,12 +1780,12 @@ unsigned LinkageBodyAST::firstToken() const
}
/** \generated */
-unsigned LinkageBodyAST::lastToken() const
+int LinkageBodyAST::lastToken() const
{
if (rbrace_token)
return rbrace_token + 1;
if (declaration_list)
- if (unsigned candidate = declaration_list->lastToken())
+ if (int candidate = declaration_list->lastToken())
return candidate;
if (lbrace_token)
return lbrace_token + 1;
@@ -1744,23 +1793,23 @@ unsigned LinkageBodyAST::lastToken() const
}
/** \generated */
-unsigned LinkageSpecificationAST::firstToken() const
+int LinkageSpecificationAST::firstToken() const
{
if (extern_token)
return extern_token;
if (extern_type_token)
return extern_type_token;
if (declaration)
- if (unsigned candidate = declaration->firstToken())
+ if (int candidate = declaration->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned LinkageSpecificationAST::lastToken() const
+int LinkageSpecificationAST::lastToken() const
{
if (declaration)
- if (unsigned candidate = declaration->lastToken())
+ if (int candidate = declaration->lastToken())
return candidate;
if (extern_type_token)
return extern_type_token + 1;
@@ -1770,81 +1819,81 @@ unsigned LinkageSpecificationAST::lastToken() const
}
/** \generated */
-unsigned MemInitializerAST::firstToken() const
+int MemInitializerAST::firstToken() const
{
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned MemInitializerAST::lastToken() const
+int MemInitializerAST::lastToken() const
{
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned MemberAccessAST::firstToken() const
+int MemberAccessAST::firstToken() const
{
if (base_expression)
- if (unsigned candidate = base_expression->firstToken())
+ if (int candidate = base_expression->firstToken())
return candidate;
if (access_token)
return access_token;
if (template_token)
return template_token;
if (member_name)
- if (unsigned candidate = member_name->firstToken())
+ if (int candidate = member_name->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned MemberAccessAST::lastToken() const
+int MemberAccessAST::lastToken() const
{
if (member_name)
- if (unsigned candidate = member_name->lastToken())
+ if (int candidate = member_name->lastToken())
return candidate;
if (template_token)
return template_token + 1;
if (access_token)
return access_token + 1;
if (base_expression)
- if (unsigned candidate = base_expression->lastToken())
+ if (int candidate = base_expression->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned NamedTypeSpecifierAST::firstToken() const
+int NamedTypeSpecifierAST::firstToken() const
{
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned NamedTypeSpecifierAST::lastToken() const
+int NamedTypeSpecifierAST::lastToken() const
{
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned NamespaceAST::firstToken() const
+int NamespaceAST::firstToken() const
{
if (inline_token)
return inline_token;
@@ -1853,22 +1902,22 @@ unsigned NamespaceAST::firstToken() const
if (identifier_token)
return identifier_token;
if (attribute_list)
- if (unsigned candidate = attribute_list->firstToken())
+ if (int candidate = attribute_list->firstToken())
return candidate;
if (linkage_body)
- if (unsigned candidate = linkage_body->firstToken())
+ if (int candidate = linkage_body->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned NamespaceAST::lastToken() const
+int NamespaceAST::lastToken() const
{
if (linkage_body)
- if (unsigned candidate = linkage_body->lastToken())
+ if (int candidate = linkage_body->lastToken())
return candidate;
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
if (identifier_token)
return identifier_token + 1;
@@ -1880,7 +1929,7 @@ unsigned NamespaceAST::lastToken() const
}
/** \generated */
-unsigned NamespaceAliasDefinitionAST::firstToken() const
+int NamespaceAliasDefinitionAST::firstToken() const
{
if (namespace_token)
return namespace_token;
@@ -1889,7 +1938,7 @@ unsigned NamespaceAliasDefinitionAST::firstToken() const
if (equal_token)
return equal_token;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -1897,12 +1946,12 @@ unsigned NamespaceAliasDefinitionAST::firstToken() const
}
/** \generated */
-unsigned NamespaceAliasDefinitionAST::lastToken() const
+int NamespaceAliasDefinitionAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (equal_token)
return equal_token + 1;
@@ -1914,12 +1963,12 @@ unsigned NamespaceAliasDefinitionAST::lastToken() const
}
/** \generated */
-unsigned NestedDeclaratorAST::firstToken() const
+int NestedDeclaratorAST::firstToken() const
{
if (lparen_token)
return lparen_token;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -1927,12 +1976,12 @@ unsigned NestedDeclaratorAST::firstToken() const
}
/** \generated */
-unsigned NestedDeclaratorAST::lastToken() const
+int NestedDeclaratorAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -1940,12 +1989,12 @@ unsigned NestedDeclaratorAST::lastToken() const
}
/** \generated */
-unsigned NestedExpressionAST::firstToken() const
+int NestedExpressionAST::firstToken() const
{
if (lparen_token)
return lparen_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -1953,12 +2002,12 @@ unsigned NestedExpressionAST::firstToken() const
}
/** \generated */
-unsigned NestedExpressionAST::lastToken() const
+int NestedExpressionAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -1966,10 +2015,10 @@ unsigned NestedExpressionAST::lastToken() const
}
/** \generated */
-unsigned NestedNameSpecifierAST::firstToken() const
+int NestedNameSpecifierAST::firstToken() const
{
if (class_or_namespace_name)
- if (unsigned candidate = class_or_namespace_name->firstToken())
+ if (int candidate = class_or_namespace_name->firstToken())
return candidate;
if (scope_token)
return scope_token;
@@ -1977,23 +2026,23 @@ unsigned NestedNameSpecifierAST::firstToken() const
}
/** \generated */
-unsigned NestedNameSpecifierAST::lastToken() const
+int NestedNameSpecifierAST::lastToken() const
{
if (scope_token)
return scope_token + 1;
if (class_or_namespace_name)
- if (unsigned candidate = class_or_namespace_name->lastToken())
+ if (int candidate = class_or_namespace_name->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned NewArrayDeclaratorAST::firstToken() const
+int NewArrayDeclaratorAST::firstToken() const
{
if (lbracket_token)
return lbracket_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rbracket_token)
return rbracket_token;
@@ -2001,12 +2050,12 @@ unsigned NewArrayDeclaratorAST::firstToken() const
}
/** \generated */
-unsigned NewArrayDeclaratorAST::lastToken() const
+int NewArrayDeclaratorAST::lastToken() const
{
if (rbracket_token)
return rbracket_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lbracket_token)
return lbracket_token + 1;
@@ -2014,49 +2063,49 @@ unsigned NewArrayDeclaratorAST::lastToken() const
}
/** \generated */
-unsigned NewExpressionAST::firstToken() const
+int NewExpressionAST::firstToken() const
{
if (scope_token)
return scope_token;
if (new_token)
return new_token;
if (new_placement)
- if (unsigned candidate = new_placement->firstToken())
+ if (int candidate = new_placement->firstToken())
return candidate;
if (lparen_token)
return lparen_token;
if (type_id)
- if (unsigned candidate = type_id->firstToken())
+ if (int candidate = type_id->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (new_type_id)
- if (unsigned candidate = new_type_id->firstToken())
+ if (int candidate = new_type_id->firstToken())
return candidate;
if (new_initializer)
- if (unsigned candidate = new_initializer->firstToken())
+ if (int candidate = new_initializer->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned NewExpressionAST::lastToken() const
+int NewExpressionAST::lastToken() const
{
if (new_initializer)
- if (unsigned candidate = new_initializer->lastToken())
+ if (int candidate = new_initializer->lastToken())
return candidate;
if (new_type_id)
- if (unsigned candidate = new_type_id->lastToken())
+ if (int candidate = new_type_id->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (type_id)
- if (unsigned candidate = type_id->lastToken())
+ if (int candidate = type_id->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
if (new_placement)
- if (unsigned candidate = new_placement->lastToken())
+ if (int candidate = new_placement->lastToken())
return candidate;
if (new_token)
return new_token + 1;
@@ -2066,12 +2115,12 @@ unsigned NewExpressionAST::lastToken() const
}
/** \generated */
-unsigned ExpressionListParenAST::firstToken() const
+int ExpressionListParenAST::firstToken() const
{
if (lparen_token)
return lparen_token;
if (expression_list)
- if (unsigned candidate = expression_list->firstToken())
+ if (int candidate = expression_list->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -2079,12 +2128,12 @@ unsigned ExpressionListParenAST::firstToken() const
}
/** \generated */
-unsigned ExpressionListParenAST::lastToken() const
+int ExpressionListParenAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (expression_list)
- if (unsigned candidate = expression_list->lastToken())
+ if (int candidate = expression_list->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -2092,37 +2141,37 @@ unsigned ExpressionListParenAST::lastToken() const
}
/** \generated */
-unsigned NewTypeIdAST::firstToken() const
+int NewTypeIdAST::firstToken() const
{
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (ptr_operator_list)
- if (unsigned candidate = ptr_operator_list->firstToken())
+ if (int candidate = ptr_operator_list->firstToken())
return candidate;
if (new_array_declarator_list)
- if (unsigned candidate = new_array_declarator_list->firstToken())
+ if (int candidate = new_array_declarator_list->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned NewTypeIdAST::lastToken() const
+int NewTypeIdAST::lastToken() const
{
if (new_array_declarator_list)
- if (unsigned candidate = new_array_declarator_list->lastToken())
+ if (int candidate = new_array_declarator_list->lastToken())
return candidate;
if (ptr_operator_list)
- if (unsigned candidate = ptr_operator_list->lastToken())
+ if (int candidate = ptr_operator_list->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned NumericLiteralAST::firstToken() const
+int NumericLiteralAST::firstToken() const
{
if (literal_token)
return literal_token;
@@ -2130,7 +2179,7 @@ unsigned NumericLiteralAST::firstToken() const
}
/** \generated */
-unsigned NumericLiteralAST::lastToken() const
+int NumericLiteralAST::lastToken() const
{
if (literal_token)
return literal_token + 1;
@@ -2138,38 +2187,38 @@ unsigned NumericLiteralAST::lastToken() const
}
/** \generated */
-unsigned ObjCClassDeclarationAST::firstToken() const
+int ObjCClassDeclarationAST::firstToken() const
{
if (attribute_list)
- if (unsigned candidate = attribute_list->firstToken())
+ if (int candidate = attribute_list->firstToken())
return candidate;
if (interface_token)
return interface_token;
if (implementation_token)
return implementation_token;
if (class_name)
- if (unsigned candidate = class_name->firstToken())
+ if (int candidate = class_name->firstToken())
return candidate;
if (lparen_token)
return lparen_token;
if (category_name)
- if (unsigned candidate = category_name->firstToken())
+ if (int candidate = category_name->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (colon_token)
return colon_token;
if (superclass)
- if (unsigned candidate = superclass->firstToken())
+ if (int candidate = superclass->firstToken())
return candidate;
if (protocol_refs)
- if (unsigned candidate = protocol_refs->firstToken())
+ if (int candidate = protocol_refs->firstToken())
return candidate;
if (inst_vars_decl)
- if (unsigned candidate = inst_vars_decl->firstToken())
+ if (int candidate = inst_vars_decl->firstToken())
return candidate;
if (member_declaration_list)
- if (unsigned candidate = member_declaration_list->firstToken())
+ if (int candidate = member_declaration_list->firstToken())
return candidate;
if (end_token)
return end_token;
@@ -2177,54 +2226,54 @@ unsigned ObjCClassDeclarationAST::firstToken() const
}
/** \generated */
-unsigned ObjCClassDeclarationAST::lastToken() const
+int ObjCClassDeclarationAST::lastToken() const
{
if (end_token)
return end_token + 1;
if (member_declaration_list)
- if (unsigned candidate = member_declaration_list->lastToken())
+ if (int candidate = member_declaration_list->lastToken())
return candidate;
if (inst_vars_decl)
- if (unsigned candidate = inst_vars_decl->lastToken())
+ if (int candidate = inst_vars_decl->lastToken())
return candidate;
if (protocol_refs)
- if (unsigned candidate = protocol_refs->lastToken())
+ if (int candidate = protocol_refs->lastToken())
return candidate;
if (superclass)
- if (unsigned candidate = superclass->lastToken())
+ if (int candidate = superclass->lastToken())
return candidate;
if (colon_token)
return colon_token + 1;
if (rparen_token)
return rparen_token + 1;
if (category_name)
- if (unsigned candidate = category_name->lastToken())
+ if (int candidate = category_name->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
if (class_name)
- if (unsigned candidate = class_name->lastToken())
+ if (int candidate = class_name->lastToken())
return candidate;
if (implementation_token)
return implementation_token + 1;
if (interface_token)
return interface_token + 1;
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ObjCClassForwardDeclarationAST::firstToken() const
+int ObjCClassForwardDeclarationAST::firstToken() const
{
if (attribute_list)
- if (unsigned candidate = attribute_list->firstToken())
+ if (int candidate = attribute_list->firstToken())
return candidate;
if (class_token)
return class_token;
if (identifier_list)
- if (unsigned candidate = identifier_list->firstToken())
+ if (int candidate = identifier_list->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -2232,28 +2281,28 @@ unsigned ObjCClassForwardDeclarationAST::firstToken() const
}
/** \generated */
-unsigned ObjCClassForwardDeclarationAST::lastToken() const
+int ObjCClassForwardDeclarationAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (identifier_list)
- if (unsigned candidate = identifier_list->lastToken())
+ if (int candidate = identifier_list->lastToken())
return candidate;
if (class_token)
return class_token + 1;
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ObjCDynamicPropertiesDeclarationAST::firstToken() const
+int ObjCDynamicPropertiesDeclarationAST::firstToken() const
{
if (dynamic_token)
return dynamic_token;
if (property_identifier_list)
- if (unsigned candidate = property_identifier_list->firstToken())
+ if (int candidate = property_identifier_list->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -2261,12 +2310,12 @@ unsigned ObjCDynamicPropertiesDeclarationAST::firstToken() const
}
/** \generated */
-unsigned ObjCDynamicPropertiesDeclarationAST::lastToken() const
+int ObjCDynamicPropertiesDeclarationAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (property_identifier_list)
- if (unsigned candidate = property_identifier_list->lastToken())
+ if (int candidate = property_identifier_list->lastToken())
return candidate;
if (dynamic_token)
return dynamic_token + 1;
@@ -2274,21 +2323,21 @@ unsigned ObjCDynamicPropertiesDeclarationAST::lastToken() const
}
/** \generated */
-unsigned ObjCEncodeExpressionAST::firstToken() const
+int ObjCEncodeExpressionAST::firstToken() const
{
if (encode_token)
return encode_token;
if (type_name)
- if (unsigned candidate = type_name->firstToken())
+ if (int candidate = type_name->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ObjCEncodeExpressionAST::lastToken() const
+int ObjCEncodeExpressionAST::lastToken() const
{
if (type_name)
- if (unsigned candidate = type_name->lastToken())
+ if (int candidate = type_name->lastToken())
return candidate;
if (encode_token)
return encode_token + 1;
@@ -2296,55 +2345,55 @@ unsigned ObjCEncodeExpressionAST::lastToken() const
}
/** \generated */
-unsigned ObjCFastEnumerationAST::firstToken() const
+int ObjCFastEnumerationAST::firstToken() const
{
if (for_token)
return for_token;
if (lparen_token)
return lparen_token;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
if (initializer)
- if (unsigned candidate = initializer->firstToken())
+ if (int candidate = initializer->firstToken())
return candidate;
if (in_token)
return in_token;
if (fast_enumeratable_expression)
- if (unsigned candidate = fast_enumeratable_expression->firstToken())
+ if (int candidate = fast_enumeratable_expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ObjCFastEnumerationAST::lastToken() const
+int ObjCFastEnumerationAST::lastToken() const
{
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (fast_enumeratable_expression)
- if (unsigned candidate = fast_enumeratable_expression->lastToken())
+ if (int candidate = fast_enumeratable_expression->lastToken())
return candidate;
if (in_token)
return in_token + 1;
if (initializer)
- if (unsigned candidate = initializer->lastToken())
+ if (int candidate = initializer->lastToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -2354,12 +2403,12 @@ unsigned ObjCFastEnumerationAST::lastToken() const
}
/** \generated */
-unsigned ObjCInstanceVariablesDeclarationAST::firstToken() const
+int ObjCInstanceVariablesDeclarationAST::firstToken() const
{
if (lbrace_token)
return lbrace_token;
if (instance_variable_list)
- if (unsigned candidate = instance_variable_list->firstToken())
+ if (int candidate = instance_variable_list->firstToken())
return candidate;
if (rbrace_token)
return rbrace_token;
@@ -2367,12 +2416,12 @@ unsigned ObjCInstanceVariablesDeclarationAST::firstToken() const
}
/** \generated */
-unsigned ObjCInstanceVariablesDeclarationAST::lastToken() const
+int ObjCInstanceVariablesDeclarationAST::lastToken() const
{
if (rbrace_token)
return rbrace_token + 1;
if (instance_variable_list)
- if (unsigned candidate = instance_variable_list->lastToken())
+ if (int candidate = instance_variable_list->lastToken())
return candidate;
if (lbrace_token)
return lbrace_token + 1;
@@ -2380,66 +2429,66 @@ unsigned ObjCInstanceVariablesDeclarationAST::lastToken() const
}
/** \generated */
-unsigned ObjCMessageArgumentAST::firstToken() const
+int ObjCMessageArgumentAST::firstToken() const
{
if (parameter_value_expression)
- if (unsigned candidate = parameter_value_expression->firstToken())
+ if (int candidate = parameter_value_expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ObjCMessageArgumentAST::lastToken() const
+int ObjCMessageArgumentAST::lastToken() const
{
if (parameter_value_expression)
- if (unsigned candidate = parameter_value_expression->lastToken())
+ if (int candidate = parameter_value_expression->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ObjCMessageArgumentDeclarationAST::firstToken() const
+int ObjCMessageArgumentDeclarationAST::firstToken() const
{
if (type_name)
- if (unsigned candidate = type_name->firstToken())
+ if (int candidate = type_name->firstToken())
return candidate;
if (attribute_list)
- if (unsigned candidate = attribute_list->firstToken())
+ if (int candidate = attribute_list->firstToken())
return candidate;
if (param_name)
- if (unsigned candidate = param_name->firstToken())
+ if (int candidate = param_name->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ObjCMessageArgumentDeclarationAST::lastToken() const
+int ObjCMessageArgumentDeclarationAST::lastToken() const
{
if (param_name)
- if (unsigned candidate = param_name->lastToken())
+ if (int candidate = param_name->lastToken())
return candidate;
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
if (type_name)
- if (unsigned candidate = type_name->lastToken())
+ if (int candidate = type_name->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ObjCMessageExpressionAST::firstToken() const
+int ObjCMessageExpressionAST::firstToken() const
{
if (lbracket_token)
return lbracket_token;
if (receiver_expression)
- if (unsigned candidate = receiver_expression->firstToken())
+ if (int candidate = receiver_expression->firstToken())
return candidate;
if (selector)
- if (unsigned candidate = selector->firstToken())
+ if (int candidate = selector->firstToken())
return candidate;
if (argument_list)
- if (unsigned candidate = argument_list->firstToken())
+ if (int candidate = argument_list->firstToken())
return candidate;
if (rbracket_token)
return rbracket_token;
@@ -2447,18 +2496,18 @@ unsigned ObjCMessageExpressionAST::firstToken() const
}
/** \generated */
-unsigned ObjCMessageExpressionAST::lastToken() const
+int ObjCMessageExpressionAST::lastToken() const
{
if (rbracket_token)
return rbracket_token + 1;
if (argument_list)
- if (unsigned candidate = argument_list->lastToken())
+ if (int candidate = argument_list->lastToken())
return candidate;
if (selector)
- if (unsigned candidate = selector->lastToken())
+ if (int candidate = selector->lastToken())
return candidate;
if (receiver_expression)
- if (unsigned candidate = receiver_expression->lastToken())
+ if (int candidate = receiver_expression->lastToken())
return candidate;
if (lbracket_token)
return lbracket_token + 1;
@@ -2466,13 +2515,13 @@ unsigned ObjCMessageExpressionAST::lastToken() const
}
/** \generated */
-unsigned ObjCMethodDeclarationAST::firstToken() const
+int ObjCMethodDeclarationAST::firstToken() const
{
if (method_prototype)
- if (unsigned candidate = method_prototype->firstToken())
+ if (int candidate = method_prototype->firstToken())
return candidate;
if (function_body)
- if (unsigned candidate = function_body->firstToken())
+ if (int candidate = function_body->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -2480,57 +2529,57 @@ unsigned ObjCMethodDeclarationAST::firstToken() const
}
/** \generated */
-unsigned ObjCMethodDeclarationAST::lastToken() const
+int ObjCMethodDeclarationAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (function_body)
- if (unsigned candidate = function_body->lastToken())
+ if (int candidate = function_body->lastToken())
return candidate;
if (method_prototype)
- if (unsigned candidate = method_prototype->lastToken())
+ if (int candidate = method_prototype->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ObjCMethodPrototypeAST::firstToken() const
+int ObjCMethodPrototypeAST::firstToken() const
{
if (method_type_token)
return method_type_token;
if (type_name)
- if (unsigned candidate = type_name->firstToken())
+ if (int candidate = type_name->firstToken())
return candidate;
if (selector)
- if (unsigned candidate = selector->firstToken())
+ if (int candidate = selector->firstToken())
return candidate;
if (argument_list)
- if (unsigned candidate = argument_list->firstToken())
+ if (int candidate = argument_list->firstToken())
return candidate;
if (dot_dot_dot_token)
return dot_dot_dot_token;
if (attribute_list)
- if (unsigned candidate = attribute_list->firstToken())
+ if (int candidate = attribute_list->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ObjCMethodPrototypeAST::lastToken() const
+int ObjCMethodPrototypeAST::lastToken() const
{
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
if (dot_dot_dot_token)
return dot_dot_dot_token + 1;
if (argument_list)
- if (unsigned candidate = argument_list->lastToken())
+ if (int candidate = argument_list->lastToken())
return candidate;
if (selector)
- if (unsigned candidate = selector->lastToken())
+ if (int candidate = selector->lastToken())
return candidate;
if (type_name)
- if (unsigned candidate = type_name->lastToken())
+ if (int candidate = type_name->lastToken())
return candidate;
if (method_type_token)
return method_type_token + 1;
@@ -2538,23 +2587,23 @@ unsigned ObjCMethodPrototypeAST::lastToken() const
}
/** \generated */
-unsigned ObjCPropertyAttributeAST::firstToken() const
+int ObjCPropertyAttributeAST::firstToken() const
{
if (attribute_identifier_token)
return attribute_identifier_token;
if (equals_token)
return equals_token;
if (method_selector)
- if (unsigned candidate = method_selector->firstToken())
+ if (int candidate = method_selector->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ObjCPropertyAttributeAST::lastToken() const
+int ObjCPropertyAttributeAST::lastToken() const
{
if (method_selector)
- if (unsigned candidate = method_selector->lastToken())
+ if (int candidate = method_selector->lastToken())
return candidate;
if (equals_token)
return equals_token + 1;
@@ -2564,63 +2613,63 @@ unsigned ObjCPropertyAttributeAST::lastToken() const
}
/** \generated */
-unsigned ObjCPropertyDeclarationAST::firstToken() const
+int ObjCPropertyDeclarationAST::firstToken() const
{
if (attribute_list)
- if (unsigned candidate = attribute_list->firstToken())
+ if (int candidate = attribute_list->firstToken())
return candidate;
if (property_token)
return property_token;
if (lparen_token)
return lparen_token;
if (property_attribute_list)
- if (unsigned candidate = property_attribute_list->firstToken())
+ if (int candidate = property_attribute_list->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (simple_declaration)
- if (unsigned candidate = simple_declaration->firstToken())
+ if (int candidate = simple_declaration->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ObjCPropertyDeclarationAST::lastToken() const
+int ObjCPropertyDeclarationAST::lastToken() const
{
if (simple_declaration)
- if (unsigned candidate = simple_declaration->lastToken())
+ if (int candidate = simple_declaration->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (property_attribute_list)
- if (unsigned candidate = property_attribute_list->lastToken())
+ if (int candidate = property_attribute_list->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
if (property_token)
return property_token + 1;
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ObjCProtocolDeclarationAST::firstToken() const
+int ObjCProtocolDeclarationAST::firstToken() const
{
if (attribute_list)
- if (unsigned candidate = attribute_list->firstToken())
+ if (int candidate = attribute_list->firstToken())
return candidate;
if (protocol_token)
return protocol_token;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
if (protocol_refs)
- if (unsigned candidate = protocol_refs->firstToken())
+ if (int candidate = protocol_refs->firstToken())
return candidate;
if (member_declaration_list)
- if (unsigned candidate = member_declaration_list->firstToken())
+ if (int candidate = member_declaration_list->firstToken())
return candidate;
if (end_token)
return end_token;
@@ -2628,29 +2677,29 @@ unsigned ObjCProtocolDeclarationAST::firstToken() const
}
/** \generated */
-unsigned ObjCProtocolDeclarationAST::lastToken() const
+int ObjCProtocolDeclarationAST::lastToken() const
{
if (end_token)
return end_token + 1;
if (member_declaration_list)
- if (unsigned candidate = member_declaration_list->lastToken())
+ if (int candidate = member_declaration_list->lastToken())
return candidate;
if (protocol_refs)
- if (unsigned candidate = protocol_refs->lastToken())
+ if (int candidate = protocol_refs->lastToken())
return candidate;
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (protocol_token)
return protocol_token + 1;
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ObjCProtocolExpressionAST::firstToken() const
+int ObjCProtocolExpressionAST::firstToken() const
{
if (protocol_token)
return protocol_token;
@@ -2664,7 +2713,7 @@ unsigned ObjCProtocolExpressionAST::firstToken() const
}
/** \generated */
-unsigned ObjCProtocolExpressionAST::lastToken() const
+int ObjCProtocolExpressionAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
@@ -2678,15 +2727,15 @@ unsigned ObjCProtocolExpressionAST::lastToken() const
}
/** \generated */
-unsigned ObjCProtocolForwardDeclarationAST::firstToken() const
+int ObjCProtocolForwardDeclarationAST::firstToken() const
{
if (attribute_list)
- if (unsigned candidate = attribute_list->firstToken())
+ if (int candidate = attribute_list->firstToken())
return candidate;
if (protocol_token)
return protocol_token;
if (identifier_list)
- if (unsigned candidate = identifier_list->firstToken())
+ if (int candidate = identifier_list->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -2694,28 +2743,28 @@ unsigned ObjCProtocolForwardDeclarationAST::firstToken() const
}
/** \generated */
-unsigned ObjCProtocolForwardDeclarationAST::lastToken() const
+int ObjCProtocolForwardDeclarationAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (identifier_list)
- if (unsigned candidate = identifier_list->lastToken())
+ if (int candidate = identifier_list->lastToken())
return candidate;
if (protocol_token)
return protocol_token + 1;
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ObjCProtocolRefsAST::firstToken() const
+int ObjCProtocolRefsAST::firstToken() const
{
if (less_token)
return less_token;
if (identifier_list)
- if (unsigned candidate = identifier_list->firstToken())
+ if (int candidate = identifier_list->firstToken())
return candidate;
if (greater_token)
return greater_token;
@@ -2723,12 +2772,12 @@ unsigned ObjCProtocolRefsAST::firstToken() const
}
/** \generated */
-unsigned ObjCProtocolRefsAST::lastToken() const
+int ObjCProtocolRefsAST::lastToken() const
{
if (greater_token)
return greater_token + 1;
if (identifier_list)
- if (unsigned candidate = identifier_list->lastToken())
+ if (int candidate = identifier_list->lastToken())
return candidate;
if (less_token)
return less_token + 1;
@@ -2736,25 +2785,25 @@ unsigned ObjCProtocolRefsAST::lastToken() const
}
/** \generated */
-unsigned ObjCSelectorAST::firstToken() const
+int ObjCSelectorAST::firstToken() const
{
if (selector_argument_list)
- if (unsigned candidate = selector_argument_list->firstToken())
+ if (int candidate = selector_argument_list->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ObjCSelectorAST::lastToken() const
+int ObjCSelectorAST::lastToken() const
{
if (selector_argument_list)
- if (unsigned candidate = selector_argument_list->lastToken())
+ if (int candidate = selector_argument_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ObjCSelectorArgumentAST::firstToken() const
+int ObjCSelectorArgumentAST::firstToken() const
{
if (name_token)
return name_token;
@@ -2764,7 +2813,7 @@ unsigned ObjCSelectorArgumentAST::firstToken() const
}
/** \generated */
-unsigned ObjCSelectorArgumentAST::lastToken() const
+int ObjCSelectorArgumentAST::lastToken() const
{
if (colon_token)
return colon_token + 1;
@@ -2774,14 +2823,14 @@ unsigned ObjCSelectorArgumentAST::lastToken() const
}
/** \generated */
-unsigned ObjCSelectorExpressionAST::firstToken() const
+int ObjCSelectorExpressionAST::firstToken() const
{
if (selector_token)
return selector_token;
if (lparen_token)
return lparen_token;
if (selector)
- if (unsigned candidate = selector->firstToken())
+ if (int candidate = selector->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -2789,12 +2838,12 @@ unsigned ObjCSelectorExpressionAST::firstToken() const
}
/** \generated */
-unsigned ObjCSelectorExpressionAST::lastToken() const
+int ObjCSelectorExpressionAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (selector)
- if (unsigned candidate = selector->lastToken())
+ if (int candidate = selector->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -2804,33 +2853,33 @@ unsigned ObjCSelectorExpressionAST::lastToken() const
}
/** \generated */
-unsigned ObjCSynchronizedStatementAST::firstToken() const
+int ObjCSynchronizedStatementAST::firstToken() const
{
if (synchronized_token)
return synchronized_token;
if (lparen_token)
return lparen_token;
if (synchronized_object)
- if (unsigned candidate = synchronized_object->firstToken())
+ if (int candidate = synchronized_object->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ObjCSynchronizedStatementAST::lastToken() const
+int ObjCSynchronizedStatementAST::lastToken() const
{
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (synchronized_object)
- if (unsigned candidate = synchronized_object->lastToken())
+ if (int candidate = synchronized_object->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -2840,12 +2889,12 @@ unsigned ObjCSynchronizedStatementAST::lastToken() const
}
/** \generated */
-unsigned ObjCSynthesizedPropertiesDeclarationAST::firstToken() const
+int ObjCSynthesizedPropertiesDeclarationAST::firstToken() const
{
if (synthesized_token)
return synthesized_token;
if (property_identifier_list)
- if (unsigned candidate = property_identifier_list->firstToken())
+ if (int candidate = property_identifier_list->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -2853,12 +2902,12 @@ unsigned ObjCSynthesizedPropertiesDeclarationAST::firstToken() const
}
/** \generated */
-unsigned ObjCSynthesizedPropertiesDeclarationAST::lastToken() const
+int ObjCSynthesizedPropertiesDeclarationAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (property_identifier_list)
- if (unsigned candidate = property_identifier_list->lastToken())
+ if (int candidate = property_identifier_list->lastToken())
return candidate;
if (synthesized_token)
return synthesized_token + 1;
@@ -2866,7 +2915,7 @@ unsigned ObjCSynthesizedPropertiesDeclarationAST::lastToken() const
}
/** \generated */
-unsigned ObjCSynthesizedPropertyAST::firstToken() const
+int ObjCSynthesizedPropertyAST::firstToken() const
{
if (property_identifier_token)
return property_identifier_token;
@@ -2878,7 +2927,7 @@ unsigned ObjCSynthesizedPropertyAST::firstToken() const
}
/** \generated */
-unsigned ObjCSynthesizedPropertyAST::lastToken() const
+int ObjCSynthesizedPropertyAST::lastToken() const
{
if (alias_identifier_token)
return alias_identifier_token + 1;
@@ -2890,14 +2939,14 @@ unsigned ObjCSynthesizedPropertyAST::lastToken() const
}
/** \generated */
-unsigned ObjCTypeNameAST::firstToken() const
+int ObjCTypeNameAST::firstToken() const
{
if (lparen_token)
return lparen_token;
if (type_qualifier_token)
return type_qualifier_token;
if (type_id)
- if (unsigned candidate = type_id->firstToken())
+ if (int candidate = type_id->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -2905,12 +2954,12 @@ unsigned ObjCTypeNameAST::firstToken() const
}
/** \generated */
-unsigned ObjCTypeNameAST::lastToken() const
+int ObjCTypeNameAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (type_id)
- if (unsigned candidate = type_id->lastToken())
+ if (int candidate = type_id->lastToken())
return candidate;
if (type_qualifier_token)
return type_qualifier_token + 1;
@@ -2920,7 +2969,7 @@ unsigned ObjCTypeNameAST::lastToken() const
}
/** \generated */
-unsigned ObjCVisibilityDeclarationAST::firstToken() const
+int ObjCVisibilityDeclarationAST::firstToken() const
{
if (visibility_token)
return visibility_token;
@@ -2928,7 +2977,7 @@ unsigned ObjCVisibilityDeclarationAST::firstToken() const
}
/** \generated */
-unsigned ObjCVisibilityDeclarationAST::lastToken() const
+int ObjCVisibilityDeclarationAST::lastToken() const
{
if (visibility_token)
return visibility_token + 1;
@@ -2936,7 +2985,7 @@ unsigned ObjCVisibilityDeclarationAST::lastToken() const
}
/** \generated */
-unsigned OperatorAST::firstToken() const
+int OperatorAST::firstToken() const
{
if (op_token)
return op_token;
@@ -2948,7 +2997,7 @@ unsigned OperatorAST::firstToken() const
}
/** \generated */
-unsigned OperatorAST::lastToken() const
+int OperatorAST::lastToken() const
{
if (close_token)
return close_token + 1;
@@ -2960,21 +3009,21 @@ unsigned OperatorAST::lastToken() const
}
/** \generated */
-unsigned OperatorFunctionIdAST::firstToken() const
+int OperatorFunctionIdAST::firstToken() const
{
if (operator_token)
return operator_token;
if (op)
- if (unsigned candidate = op->firstToken())
+ if (int candidate = op->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned OperatorFunctionIdAST::lastToken() const
+int OperatorFunctionIdAST::lastToken() const
{
if (op)
- if (unsigned candidate = op->lastToken())
+ if (int candidate = op->lastToken())
return candidate;
if (operator_token)
return operator_token + 1;
@@ -2982,44 +3031,44 @@ unsigned OperatorFunctionIdAST::lastToken() const
}
/** \generated */
-unsigned ParameterDeclarationAST::firstToken() const
+int ParameterDeclarationAST::firstToken() const
{
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
if (equal_token)
return equal_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ParameterDeclarationAST::lastToken() const
+int ParameterDeclarationAST::lastToken() const
{
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (equal_token)
return equal_token + 1;
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned ParameterDeclarationClauseAST::firstToken() const
+int ParameterDeclarationClauseAST::firstToken() const
{
if (parameter_declaration_list)
- if (unsigned candidate = parameter_declaration_list->firstToken())
+ if (int candidate = parameter_declaration_list->firstToken())
return candidate;
if (dot_dot_dot_token)
return dot_dot_dot_token;
@@ -3027,32 +3076,32 @@ unsigned ParameterDeclarationClauseAST::firstToken() const
}
/** \generated */
-unsigned ParameterDeclarationClauseAST::lastToken() const
+int ParameterDeclarationClauseAST::lastToken() const
{
if (dot_dot_dot_token)
return dot_dot_dot_token + 1;
if (parameter_declaration_list)
- if (unsigned candidate = parameter_declaration_list->lastToken())
+ if (int candidate = parameter_declaration_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned PointerAST::firstToken() const
+int PointerAST::firstToken() const
{
if (star_token)
return star_token;
if (cv_qualifier_list)
- if (unsigned candidate = cv_qualifier_list->firstToken())
+ if (int candidate = cv_qualifier_list->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned PointerAST::lastToken() const
+int PointerAST::lastToken() const
{
if (cv_qualifier_list)
- if (unsigned candidate = cv_qualifier_list->lastToken())
+ if (int candidate = cv_qualifier_list->lastToken())
return candidate;
if (star_token)
return star_token + 1;
@@ -3060,17 +3109,17 @@ unsigned PointerAST::lastToken() const
}
/** \generated */
-unsigned PointerToMemberAST::firstToken() const
+int PointerToMemberAST::firstToken() const
{
if (global_scope_token)
return global_scope_token;
if (nested_name_specifier_list)
- if (unsigned candidate = nested_name_specifier_list->firstToken())
+ if (int candidate = nested_name_specifier_list->firstToken())
return candidate;
if (star_token)
return star_token;
if (cv_qualifier_list)
- if (unsigned candidate = cv_qualifier_list->firstToken())
+ if (int candidate = cv_qualifier_list->firstToken())
return candidate;
if (ref_qualifier_token)
return ref_qualifier_token;
@@ -3078,17 +3127,17 @@ unsigned PointerToMemberAST::firstToken() const
}
/** \generated */
-unsigned PointerToMemberAST::lastToken() const
+int PointerToMemberAST::lastToken() const
{
if (ref_qualifier_token)
return ref_qualifier_token + 1;
if (cv_qualifier_list)
- if (unsigned candidate = cv_qualifier_list->lastToken())
+ if (int candidate = cv_qualifier_list->lastToken())
return candidate;
if (star_token)
return star_token + 1;
if (nested_name_specifier_list)
- if (unsigned candidate = nested_name_specifier_list->lastToken())
+ if (int candidate = nested_name_specifier_list->lastToken())
return candidate;
if (global_scope_token)
return global_scope_token + 1;
@@ -3096,10 +3145,10 @@ unsigned PointerToMemberAST::lastToken() const
}
/** \generated */
-unsigned PostIncrDecrAST::firstToken() const
+int PostIncrDecrAST::firstToken() const
{
if (base_expression)
- if (unsigned candidate = base_expression->firstToken())
+ if (int candidate = base_expression->firstToken())
return candidate;
if (incr_decr_token)
return incr_decr_token;
@@ -3107,25 +3156,25 @@ unsigned PostIncrDecrAST::firstToken() const
}
/** \generated */
-unsigned PostIncrDecrAST::lastToken() const
+int PostIncrDecrAST::lastToken() const
{
if (incr_decr_token)
return incr_decr_token + 1;
if (base_expression)
- if (unsigned candidate = base_expression->lastToken())
+ if (int candidate = base_expression->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned QtEnumDeclarationAST::firstToken() const
+int QtEnumDeclarationAST::firstToken() const
{
if (enum_specifier_token)
return enum_specifier_token;
if (lparen_token)
return lparen_token;
if (enumerator_list)
- if (unsigned candidate = enumerator_list->firstToken())
+ if (int candidate = enumerator_list->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -3133,12 +3182,12 @@ unsigned QtEnumDeclarationAST::firstToken() const
}
/** \generated */
-unsigned QtEnumDeclarationAST::lastToken() const
+int QtEnumDeclarationAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (enumerator_list)
- if (unsigned candidate = enumerator_list->lastToken())
+ if (int candidate = enumerator_list->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -3148,14 +3197,14 @@ unsigned QtEnumDeclarationAST::lastToken() const
}
/** \generated */
-unsigned QtFlagsDeclarationAST::firstToken() const
+int QtFlagsDeclarationAST::firstToken() const
{
if (flags_specifier_token)
return flags_specifier_token;
if (lparen_token)
return lparen_token;
if (flag_enums_list)
- if (unsigned candidate = flag_enums_list->firstToken())
+ if (int candidate = flag_enums_list->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -3163,12 +3212,12 @@ unsigned QtFlagsDeclarationAST::firstToken() const
}
/** \generated */
-unsigned QtFlagsDeclarationAST::lastToken() const
+int QtFlagsDeclarationAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (flag_enums_list)
- if (unsigned candidate = flag_enums_list->lastToken())
+ if (int candidate = flag_enums_list->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -3178,38 +3227,38 @@ unsigned QtFlagsDeclarationAST::lastToken() const
}
/** \generated */
-unsigned QtInterfaceNameAST::firstToken() const
+int QtInterfaceNameAST::firstToken() const
{
if (interface_name)
- if (unsigned candidate = interface_name->firstToken())
+ if (int candidate = interface_name->firstToken())
return candidate;
if (constraint_list)
- if (unsigned candidate = constraint_list->firstToken())
+ if (int candidate = constraint_list->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned QtInterfaceNameAST::lastToken() const
+int QtInterfaceNameAST::lastToken() const
{
if (constraint_list)
- if (unsigned candidate = constraint_list->lastToken())
+ if (int candidate = constraint_list->lastToken())
return candidate;
if (interface_name)
- if (unsigned candidate = interface_name->lastToken())
+ if (int candidate = interface_name->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned QtInterfacesDeclarationAST::firstToken() const
+int QtInterfacesDeclarationAST::firstToken() const
{
if (interfaces_token)
return interfaces_token;
if (lparen_token)
return lparen_token;
if (interface_name_list)
- if (unsigned candidate = interface_name_list->firstToken())
+ if (int candidate = interface_name_list->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -3217,12 +3266,12 @@ unsigned QtInterfacesDeclarationAST::firstToken() const
}
/** \generated */
-unsigned QtInterfacesDeclarationAST::lastToken() const
+int QtInterfacesDeclarationAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (interface_name_list)
- if (unsigned candidate = interface_name_list->lastToken())
+ if (int candidate = interface_name_list->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -3232,14 +3281,14 @@ unsigned QtInterfacesDeclarationAST::lastToken() const
}
/** \generated */
-unsigned QtMemberDeclarationAST::firstToken() const
+int QtMemberDeclarationAST::firstToken() const
{
if (q_token)
return q_token;
if (lparen_token)
return lparen_token;
if (type_id)
- if (unsigned candidate = type_id->firstToken())
+ if (int candidate = type_id->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -3247,12 +3296,12 @@ unsigned QtMemberDeclarationAST::firstToken() const
}
/** \generated */
-unsigned QtMemberDeclarationAST::lastToken() const
+int QtMemberDeclarationAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (type_id)
- if (unsigned candidate = type_id->lastToken())
+ if (int candidate = type_id->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -3262,14 +3311,14 @@ unsigned QtMemberDeclarationAST::lastToken() const
}
/** \generated */
-unsigned QtMethodAST::firstToken() const
+int QtMethodAST::firstToken() const
{
if (method_token)
return method_token;
if (lparen_token)
return lparen_token;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -3277,12 +3326,12 @@ unsigned QtMethodAST::firstToken() const
}
/** \generated */
-unsigned QtMethodAST::lastToken() const
+int QtMethodAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -3292,7 +3341,7 @@ unsigned QtMethodAST::lastToken() const
}
/** \generated */
-unsigned QtObjectTagAST::firstToken() const
+int QtObjectTagAST::firstToken() const
{
if (q_object_token)
return q_object_token;
@@ -3300,7 +3349,7 @@ unsigned QtObjectTagAST::firstToken() const
}
/** \generated */
-unsigned QtObjectTagAST::lastToken() const
+int QtObjectTagAST::lastToken() const
{
if (q_object_token)
return q_object_token + 1;
@@ -3308,7 +3357,7 @@ unsigned QtObjectTagAST::lastToken() const
}
/** \generated */
-unsigned QtPrivateSlotAST::firstToken() const
+int QtPrivateSlotAST::firstToken() const
{
if (q_private_slot_token)
return q_private_slot_token;
@@ -3323,10 +3372,10 @@ unsigned QtPrivateSlotAST::firstToken() const
if (comma_token)
return comma_token;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -3334,15 +3383,15 @@ unsigned QtPrivateSlotAST::firstToken() const
}
/** \generated */
-unsigned QtPrivateSlotAST::lastToken() const
+int QtPrivateSlotAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
if (comma_token)
return comma_token + 1;
@@ -3360,25 +3409,25 @@ unsigned QtPrivateSlotAST::lastToken() const
}
/** \generated */
-unsigned QtPropertyDeclarationAST::firstToken() const
+int QtPropertyDeclarationAST::firstToken() const
{
if (property_specifier_token)
return property_specifier_token;
if (lparen_token)
return lparen_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (comma_token)
return comma_token;
if (type_id)
- if (unsigned candidate = type_id->firstToken())
+ if (int candidate = type_id->firstToken())
return candidate;
if (property_name)
- if (unsigned candidate = property_name->firstToken())
+ if (int candidate = property_name->firstToken())
return candidate;
if (property_declaration_item_list)
- if (unsigned candidate = property_declaration_item_list->firstToken())
+ if (int candidate = property_declaration_item_list->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -3386,23 +3435,23 @@ unsigned QtPropertyDeclarationAST::firstToken() const
}
/** \generated */
-unsigned QtPropertyDeclarationAST::lastToken() const
+int QtPropertyDeclarationAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (property_declaration_item_list)
- if (unsigned candidate = property_declaration_item_list->lastToken())
+ if (int candidate = property_declaration_item_list->lastToken())
return candidate;
if (property_name)
- if (unsigned candidate = property_name->lastToken())
+ if (int candidate = property_name->lastToken())
return candidate;
if (type_id)
- if (unsigned candidate = type_id->lastToken())
+ if (int candidate = type_id->lastToken())
return candidate;
if (comma_token)
return comma_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -3412,21 +3461,21 @@ unsigned QtPropertyDeclarationAST::lastToken() const
}
/** \generated */
-unsigned QtPropertyDeclarationItemAST::firstToken() const
+int QtPropertyDeclarationItemAST::firstToken() const
{
if (item_name_token)
return item_name_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned QtPropertyDeclarationItemAST::lastToken() const
+int QtPropertyDeclarationItemAST::lastToken() const
{
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (item_name_token)
return item_name_token + 1;
@@ -3434,27 +3483,27 @@ unsigned QtPropertyDeclarationItemAST::lastToken() const
}
/** \generated */
-unsigned QualifiedNameAST::firstToken() const
+int QualifiedNameAST::firstToken() const
{
if (global_scope_token)
return global_scope_token;
if (nested_name_specifier_list)
- if (unsigned candidate = nested_name_specifier_list->firstToken())
+ if (int candidate = nested_name_specifier_list->firstToken())
return candidate;
if (unqualified_name)
- if (unsigned candidate = unqualified_name->firstToken())
+ if (int candidate = unqualified_name->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned QualifiedNameAST::lastToken() const
+int QualifiedNameAST::lastToken() const
{
if (unqualified_name)
- if (unsigned candidate = unqualified_name->lastToken())
+ if (int candidate = unqualified_name->lastToken())
return candidate;
if (nested_name_specifier_list)
- if (unsigned candidate = nested_name_specifier_list->lastToken())
+ if (int candidate = nested_name_specifier_list->lastToken())
return candidate;
if (global_scope_token)
return global_scope_token + 1;
@@ -3462,7 +3511,7 @@ unsigned QualifiedNameAST::lastToken() const
}
/** \generated */
-unsigned ReferenceAST::firstToken() const
+int ReferenceAST::firstToken() const
{
if (reference_token)
return reference_token;
@@ -3470,7 +3519,7 @@ unsigned ReferenceAST::firstToken() const
}
/** \generated */
-unsigned ReferenceAST::lastToken() const
+int ReferenceAST::lastToken() const
{
if (reference_token)
return reference_token + 1;
@@ -3478,12 +3527,12 @@ unsigned ReferenceAST::lastToken() const
}
/** \generated */
-unsigned ReturnStatementAST::firstToken() const
+int ReturnStatementAST::firstToken() const
{
if (return_token)
return return_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -3491,12 +3540,12 @@ unsigned ReturnStatementAST::firstToken() const
}
/** \generated */
-unsigned ReturnStatementAST::lastToken() const
+int ReturnStatementAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (return_token)
return return_token + 1;
@@ -3504,15 +3553,15 @@ unsigned ReturnStatementAST::lastToken() const
}
/** \generated */
-unsigned SimpleDeclarationAST::firstToken() const
+int SimpleDeclarationAST::firstToken() const
{
if (qt_invokable_token)
return qt_invokable_token;
if (decl_specifier_list)
- if (unsigned candidate = decl_specifier_list->firstToken())
+ if (int candidate = decl_specifier_list->firstToken())
return candidate;
if (declarator_list)
- if (unsigned candidate = declarator_list->firstToken())
+ if (int candidate = declarator_list->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -3520,15 +3569,15 @@ unsigned SimpleDeclarationAST::firstToken() const
}
/** \generated */
-unsigned SimpleDeclarationAST::lastToken() const
+int SimpleDeclarationAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (declarator_list)
- if (unsigned candidate = declarator_list->lastToken())
+ if (int candidate = declarator_list->lastToken())
return candidate;
if (decl_specifier_list)
- if (unsigned candidate = decl_specifier_list->lastToken())
+ if (int candidate = decl_specifier_list->lastToken())
return candidate;
if (qt_invokable_token)
return qt_invokable_token + 1;
@@ -3536,7 +3585,7 @@ unsigned SimpleDeclarationAST::lastToken() const
}
/** \generated */
-unsigned SimpleNameAST::firstToken() const
+int SimpleNameAST::firstToken() const
{
if (identifier_token)
return identifier_token;
@@ -3544,7 +3593,7 @@ unsigned SimpleNameAST::firstToken() const
}
/** \generated */
-unsigned SimpleNameAST::lastToken() const
+int SimpleNameAST::lastToken() const
{
if (identifier_token)
return identifier_token + 1;
@@ -3552,7 +3601,7 @@ unsigned SimpleNameAST::lastToken() const
}
/** \generated */
-unsigned SimpleSpecifierAST::firstToken() const
+int SimpleSpecifierAST::firstToken() const
{
if (specifier_token)
return specifier_token;
@@ -3560,7 +3609,7 @@ unsigned SimpleSpecifierAST::firstToken() const
}
/** \generated */
-unsigned SimpleSpecifierAST::lastToken() const
+int SimpleSpecifierAST::lastToken() const
{
if (specifier_token)
return specifier_token + 1;
@@ -3568,7 +3617,7 @@ unsigned SimpleSpecifierAST::lastToken() const
}
/** \generated */
-unsigned SizeofExpressionAST::firstToken() const
+int SizeofExpressionAST::firstToken() const
{
if (sizeof_token)
return sizeof_token;
@@ -3577,7 +3626,7 @@ unsigned SizeofExpressionAST::firstToken() const
if (lparen_token)
return lparen_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -3585,12 +3634,12 @@ unsigned SizeofExpressionAST::firstToken() const
}
/** \generated */
-unsigned SizeofExpressionAST::lastToken() const
+int SizeofExpressionAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -3602,21 +3651,21 @@ unsigned SizeofExpressionAST::lastToken() const
}
/** \generated */
-unsigned StringLiteralAST::firstToken() const
+int StringLiteralAST::firstToken() const
{
if (literal_token)
return literal_token;
if (next)
- if (unsigned candidate = next->firstToken())
+ if (int candidate = next->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned StringLiteralAST::lastToken() const
+int StringLiteralAST::lastToken() const
{
if (next)
- if (unsigned candidate = next->lastToken())
+ if (int candidate = next->lastToken())
return candidate;
if (literal_token)
return literal_token + 1;
@@ -3624,33 +3673,33 @@ unsigned StringLiteralAST::lastToken() const
}
/** \generated */
-unsigned SwitchStatementAST::firstToken() const
+int SwitchStatementAST::firstToken() const
{
if (switch_token)
return switch_token;
if (lparen_token)
return lparen_token;
if (condition)
- if (unsigned candidate = condition->firstToken())
+ if (int candidate = condition->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned SwitchStatementAST::lastToken() const
+int SwitchStatementAST::lastToken() const
{
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (condition)
- if (unsigned candidate = condition->lastToken())
+ if (int candidate = condition->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -3660,7 +3709,7 @@ unsigned SwitchStatementAST::lastToken() const
}
/** \generated */
-unsigned TemplateDeclarationAST::firstToken() const
+int TemplateDeclarationAST::firstToken() const
{
if (export_token)
return export_token;
@@ -3669,26 +3718,26 @@ unsigned TemplateDeclarationAST::firstToken() const
if (less_token)
return less_token;
if (template_parameter_list)
- if (unsigned candidate = template_parameter_list->firstToken())
+ if (int candidate = template_parameter_list->firstToken())
return candidate;
if (greater_token)
return greater_token;
if (declaration)
- if (unsigned candidate = declaration->firstToken())
+ if (int candidate = declaration->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned TemplateDeclarationAST::lastToken() const
+int TemplateDeclarationAST::lastToken() const
{
if (declaration)
- if (unsigned candidate = declaration->lastToken())
+ if (int candidate = declaration->lastToken())
return candidate;
if (greater_token)
return greater_token + 1;
if (template_parameter_list)
- if (unsigned candidate = template_parameter_list->lastToken())
+ if (int candidate = template_parameter_list->lastToken())
return candidate;
if (less_token)
return less_token + 1;
@@ -3700,7 +3749,7 @@ unsigned TemplateDeclarationAST::lastToken() const
}
/** \generated */
-unsigned TemplateIdAST::firstToken() const
+int TemplateIdAST::firstToken() const
{
if (template_token)
return template_token;
@@ -3709,7 +3758,7 @@ unsigned TemplateIdAST::firstToken() const
if (less_token)
return less_token;
if (template_argument_list)
- if (unsigned candidate = template_argument_list->firstToken())
+ if (int candidate = template_argument_list->firstToken())
return candidate;
if (greater_token)
return greater_token;
@@ -3717,12 +3766,12 @@ unsigned TemplateIdAST::firstToken() const
}
/** \generated */
-unsigned TemplateIdAST::lastToken() const
+int TemplateIdAST::lastToken() const
{
if (greater_token)
return greater_token + 1;
if (template_argument_list)
- if (unsigned candidate = template_argument_list->lastToken())
+ if (int candidate = template_argument_list->lastToken())
return candidate;
if (less_token)
return less_token + 1;
@@ -3734,14 +3783,16 @@ unsigned TemplateIdAST::lastToken() const
}
/** \generated */
-unsigned TemplateTypeParameterAST::firstToken() const
+int TemplateTypeParameterAST::firstToken() const
{
if (template_token)
return template_token;
+ if (typeConstraint)
+ return typeConstraint->firstToken();
if (less_token)
return less_token;
if (template_parameter_list)
- if (unsigned candidate = template_parameter_list->firstToken())
+ if (int candidate = template_parameter_list->firstToken())
return candidate;
if (greater_token)
return greater_token;
@@ -3750,26 +3801,26 @@ unsigned TemplateTypeParameterAST::firstToken() const
if (dot_dot_dot_token)
return dot_dot_dot_token;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
if (equal_token)
return equal_token;
if (type_id)
- if (unsigned candidate = type_id->firstToken())
+ if (int candidate = type_id->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned TemplateTypeParameterAST::lastToken() const
+int TemplateTypeParameterAST::lastToken() const
{
if (type_id)
- if (unsigned candidate = type_id->lastToken())
+ if (int candidate = type_id->lastToken())
return candidate;
if (equal_token)
return equal_token + 1;
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (dot_dot_dot_token)
return dot_dot_dot_token + 1;
@@ -3778,17 +3829,19 @@ unsigned TemplateTypeParameterAST::lastToken() const
if (greater_token)
return greater_token + 1;
if (template_parameter_list)
- if (unsigned candidate = template_parameter_list->lastToken())
+ if (int candidate = template_parameter_list->lastToken())
return candidate;
if (less_token)
return less_token + 1;
+ if (typeConstraint)
+ return typeConstraint->lastToken();
if (template_token)
return template_token + 1;
return 1;
}
/** \generated */
-unsigned ThisExpressionAST::firstToken() const
+int ThisExpressionAST::firstToken() const
{
if (this_token)
return this_token;
@@ -3796,7 +3849,7 @@ unsigned ThisExpressionAST::firstToken() const
}
/** \generated */
-unsigned ThisExpressionAST::lastToken() const
+int ThisExpressionAST::lastToken() const
{
if (this_token)
return this_token + 1;
@@ -3804,21 +3857,21 @@ unsigned ThisExpressionAST::lastToken() const
}
/** \generated */
-unsigned ThrowExpressionAST::firstToken() const
+int ThrowExpressionAST::firstToken() const
{
if (throw_token)
return throw_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned ThrowExpressionAST::lastToken() const
+int ThrowExpressionAST::lastToken() const
{
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (throw_token)
return throw_token + 1;
@@ -3826,33 +3879,33 @@ unsigned ThrowExpressionAST::lastToken() const
}
/** \generated */
-unsigned TrailingReturnTypeAST::firstToken() const
+int TrailingReturnTypeAST::firstToken() const
{
if (arrow_token)
return arrow_token;
if (attributes)
- if (unsigned candidate = attributes->firstToken())
+ if (int candidate = attributes->firstToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned TrailingReturnTypeAST::lastToken() const
+int TrailingReturnTypeAST::lastToken() const
{
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
if (attributes)
- if (unsigned candidate = attributes->lastToken())
+ if (int candidate = attributes->lastToken())
return candidate;
if (arrow_token)
return arrow_token + 1;
@@ -3860,45 +3913,45 @@ unsigned TrailingReturnTypeAST::lastToken() const
}
/** \generated */
-unsigned TranslationUnitAST::firstToken() const
+int TranslationUnitAST::firstToken() const
{
if (declaration_list)
- if (unsigned candidate = declaration_list->firstToken())
+ if (int candidate = declaration_list->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned TranslationUnitAST::lastToken() const
+int TranslationUnitAST::lastToken() const
{
if (declaration_list)
- if (unsigned candidate = declaration_list->lastToken())
+ if (int candidate = declaration_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned TryBlockStatementAST::firstToken() const
+int TryBlockStatementAST::firstToken() const
{
if (try_token)
return try_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
if (catch_clause_list)
- if (unsigned candidate = catch_clause_list->firstToken())
+ if (int candidate = catch_clause_list->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned TryBlockStatementAST::lastToken() const
+int TryBlockStatementAST::lastToken() const
{
if (catch_clause_list)
- if (unsigned candidate = catch_clause_list->lastToken())
+ if (int candidate = catch_clause_list->lastToken())
return candidate;
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (try_token)
return try_token + 1;
@@ -3906,62 +3959,62 @@ unsigned TryBlockStatementAST::lastToken() const
}
/** \generated */
-unsigned TypeConstructorCallAST::firstToken() const
+int TypeConstructorCallAST::firstToken() const
{
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned TypeConstructorCallAST::lastToken() const
+int TypeConstructorCallAST::lastToken() const
{
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned TypeIdAST::firstToken() const
+int TypeIdAST::firstToken() const
{
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned TypeIdAST::lastToken() const
+int TypeIdAST::lastToken() const
{
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned TypeidExpressionAST::firstToken() const
+int TypeidExpressionAST::firstToken() const
{
if (typeid_token)
return typeid_token;
if (lparen_token)
return lparen_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -3969,12 +4022,12 @@ unsigned TypeidExpressionAST::firstToken() const
}
/** \generated */
-unsigned TypeidExpressionAST::lastToken() const
+int TypeidExpressionAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -3984,27 +4037,27 @@ unsigned TypeidExpressionAST::lastToken() const
}
/** \generated */
-unsigned TypenameCallExpressionAST::firstToken() const
+int TypenameCallExpressionAST::firstToken() const
{
if (typename_token)
return typename_token;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned TypenameCallExpressionAST::lastToken() const
+int TypenameCallExpressionAST::lastToken() const
{
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (typename_token)
return typename_token + 1;
@@ -4012,33 +4065,33 @@ unsigned TypenameCallExpressionAST::lastToken() const
}
/** \generated */
-unsigned TypenameTypeParameterAST::firstToken() const
+int TypenameTypeParameterAST::firstToken() const
{
if (classkey_token)
return classkey_token;
if (dot_dot_dot_token)
return dot_dot_dot_token;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
if (equal_token)
return equal_token;
if (type_id)
- if (unsigned candidate = type_id->firstToken())
+ if (int candidate = type_id->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned TypenameTypeParameterAST::lastToken() const
+int TypenameTypeParameterAST::lastToken() const
{
if (type_id)
- if (unsigned candidate = type_id->lastToken())
+ if (int candidate = type_id->lastToken())
return candidate;
if (equal_token)
return equal_token + 1;
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (dot_dot_dot_token)
return dot_dot_dot_token + 1;
@@ -4048,14 +4101,14 @@ unsigned TypenameTypeParameterAST::lastToken() const
}
/** \generated */
-unsigned TypeofSpecifierAST::firstToken() const
+int TypeofSpecifierAST::firstToken() const
{
if (typeof_token)
return typeof_token;
if (lparen_token)
return lparen_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -4063,12 +4116,12 @@ unsigned TypeofSpecifierAST::firstToken() const
}
/** \generated */
-unsigned TypeofSpecifierAST::lastToken() const
+int TypeofSpecifierAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -4078,21 +4131,21 @@ unsigned TypeofSpecifierAST::lastToken() const
}
/** \generated */
-unsigned UnaryExpressionAST::firstToken() const
+int UnaryExpressionAST::firstToken() const
{
if (unary_op_token)
return unary_op_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned UnaryExpressionAST::lastToken() const
+int UnaryExpressionAST::lastToken() const
{
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (unary_op_token)
return unary_op_token + 1;
@@ -4100,14 +4153,14 @@ unsigned UnaryExpressionAST::lastToken() const
}
/** \generated */
-unsigned UsingAST::firstToken() const
+int UsingAST::firstToken() const
{
if (using_token)
return using_token;
if (typename_token)
return typename_token;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -4115,12 +4168,12 @@ unsigned UsingAST::firstToken() const
}
/** \generated */
-unsigned UsingAST::lastToken() const
+int UsingAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (typename_token)
return typename_token + 1;
@@ -4130,14 +4183,14 @@ unsigned UsingAST::lastToken() const
}
/** \generated */
-unsigned UsingDirectiveAST::firstToken() const
+int UsingDirectiveAST::firstToken() const
{
if (using_token)
return using_token;
if (namespace_token)
return namespace_token;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -4145,12 +4198,12 @@ unsigned UsingDirectiveAST::firstToken() const
}
/** \generated */
-unsigned UsingDirectiveAST::lastToken() const
+int UsingDirectiveAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (namespace_token)
return namespace_token + 1;
@@ -4160,33 +4213,33 @@ unsigned UsingDirectiveAST::lastToken() const
}
/** \generated */
-unsigned WhileStatementAST::firstToken() const
+int WhileStatementAST::firstToken() const
{
if (while_token)
return while_token;
if (lparen_token)
return lparen_token;
if (condition)
- if (unsigned candidate = condition->firstToken())
+ if (int candidate = condition->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned WhileStatementAST::lastToken() const
+int WhileStatementAST::lastToken() const
{
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (condition)
- if (unsigned candidate = condition->lastToken())
+ if (int candidate = condition->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -4196,14 +4249,14 @@ unsigned WhileStatementAST::lastToken() const
}
/** \generated */
-unsigned GnuAttributeSpecifierAST::lastToken() const
+int GnuAttributeSpecifierAST::lastToken() const
{
if (second_rparen_token)
return second_rparen_token + 1;
if (first_rparen_token)
return first_rparen_token + 1;
if (attribute_list)
- if (unsigned candidate = attribute_list->lastToken())
+ if (int candidate = attribute_list->lastToken())
return candidate;
if (second_lparen_token)
return second_lparen_token + 1;
@@ -4214,8 +4267,38 @@ unsigned GnuAttributeSpecifierAST::lastToken() const
return 1;
}
+int MsvcDeclspecSpecifierAST::lastToken() const
+{
+ if (rparen_token)
+ return rparen_token + 1;
+ if (attribute_list)
+ if (int candidate = attribute_list->lastToken())
+ return candidate;
+ if (lparen_token)
+ return lparen_token + 1;
+ if (attribute_token)
+ return attribute_token + 1;
+ return 1;
+}
+
+int StdAttributeSpecifierAST::lastToken() const
+{
+ if (second_rbracket_token)
+ return second_rbracket_token + 1;
+ if (first_rbracket_token)
+ return first_rbracket_token + 1;
+ if (attribute_list)
+ if (int candidate = attribute_list->lastToken())
+ return candidate;
+ if (second_lbracket_token)
+ return second_lbracket_token + 1;
+ if (first_lbracket_token)
+ return first_lbracket_token + 1;
+ return 1;
+}
+
/** \generated */
-unsigned PointerLiteralAST::firstToken() const
+int PointerLiteralAST::firstToken() const
{
if (literal_token)
return literal_token;
@@ -4223,7 +4306,7 @@ unsigned PointerLiteralAST::firstToken() const
}
/** \generated */
-unsigned PointerLiteralAST::lastToken() const
+int PointerLiteralAST::lastToken() const
{
if (literal_token)
return literal_token + 1;
@@ -4231,14 +4314,14 @@ unsigned PointerLiteralAST::lastToken() const
}
/** \generated */
-unsigned NoExceptSpecificationAST::firstToken() const
+int NoExceptSpecificationAST::firstToken() const
{
if (noexcept_token)
return noexcept_token;
if (lparen_token)
return lparen_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -4246,12 +4329,12 @@ unsigned NoExceptSpecificationAST::firstToken() const
}
/** \generated */
-unsigned NoExceptSpecificationAST::lastToken() const
+int NoExceptSpecificationAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -4261,19 +4344,19 @@ unsigned NoExceptSpecificationAST::lastToken() const
}
/** \generated */
-unsigned StaticAssertDeclarationAST::firstToken() const
+int StaticAssertDeclarationAST::firstToken() const
{
if (static_assert_token)
return static_assert_token;
if (lparen_token)
return lparen_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (comma_token)
return comma_token;
if (string_literal)
- if (unsigned candidate = string_literal->firstToken())
+ if (int candidate = string_literal->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -4283,19 +4366,19 @@ unsigned StaticAssertDeclarationAST::firstToken() const
}
/** \generated */
-unsigned StaticAssertDeclarationAST::lastToken() const
+int StaticAssertDeclarationAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (rparen_token)
return rparen_token + 1;
if (string_literal)
- if (unsigned candidate = string_literal->lastToken())
+ if (int candidate = string_literal->lastToken())
return candidate;
if (comma_token)
return comma_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -4305,14 +4388,14 @@ unsigned StaticAssertDeclarationAST::lastToken() const
}
/** \generated */
-unsigned DecltypeSpecifierAST::firstToken() const
+int DecltypeSpecifierAST::firstToken() const
{
if (decltype_token)
return decltype_token;
if (lparen_token)
return lparen_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -4320,12 +4403,12 @@ unsigned DecltypeSpecifierAST::firstToken() const
}
/** \generated */
-unsigned DecltypeSpecifierAST::lastToken() const
+int DecltypeSpecifierAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -4335,49 +4418,49 @@ unsigned DecltypeSpecifierAST::lastToken() const
}
/** \generated */
-unsigned RangeBasedForStatementAST::firstToken() const
+int RangeBasedForStatementAST::firstToken() const
{
if (for_token)
return for_token;
if (lparen_token)
return lparen_token;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->firstToken())
+ if (int candidate = type_specifier_list->firstToken())
return candidate;
if (declarator)
- if (unsigned candidate = declarator->firstToken())
+ if (int candidate = declarator->firstToken())
return candidate;
if (colon_token)
return colon_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
if (statement)
- if (unsigned candidate = statement->firstToken())
+ if (int candidate = statement->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned RangeBasedForStatementAST::lastToken() const
+int RangeBasedForStatementAST::lastToken() const
{
if (statement)
- if (unsigned candidate = statement->lastToken())
+ if (int candidate = statement->lastToken())
return candidate;
if (rparen_token)
return rparen_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (colon_token)
return colon_token + 1;
if (declarator)
- if (unsigned candidate = declarator->lastToken())
+ if (int candidate = declarator->lastToken())
return candidate;
if (type_specifier_list)
- if (unsigned candidate = type_specifier_list->lastToken())
+ if (int candidate = type_specifier_list->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -4387,14 +4470,14 @@ unsigned RangeBasedForStatementAST::lastToken() const
}
/** \generated */
-unsigned AlignofExpressionAST::firstToken() const
+int AlignofExpressionAST::firstToken() const
{
if (alignof_token)
return alignof_token;
if (lparen_token)
return lparen_token;
if (typeId)
- if (unsigned candidate = typeId->firstToken())
+ if (int candidate = typeId->firstToken())
return candidate;
if (rparen_token)
return rparen_token;
@@ -4402,12 +4485,12 @@ unsigned AlignofExpressionAST::firstToken() const
}
/** \generated */
-unsigned AlignofExpressionAST::lastToken() const
+int AlignofExpressionAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (typeId)
- if (unsigned candidate = typeId->lastToken())
+ if (int candidate = typeId->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -4417,17 +4500,17 @@ unsigned AlignofExpressionAST::lastToken() const
}
/** \generated */
-unsigned AliasDeclarationAST::firstToken() const
+int AliasDeclarationAST::firstToken() const
{
if (using_token)
return using_token;
if (name)
- if (unsigned candidate = name->firstToken())
+ if (int candidate = name->firstToken())
return candidate;
if (equal_token)
return equal_token;
if (typeId)
- if (unsigned candidate = typeId->firstToken())
+ if (int candidate = typeId->firstToken())
return candidate;
if (semicolon_token)
return semicolon_token;
@@ -4435,17 +4518,17 @@ unsigned AliasDeclarationAST::firstToken() const
}
/** \generated */
-unsigned AliasDeclarationAST::lastToken() const
+int AliasDeclarationAST::lastToken() const
{
if (semicolon_token)
return semicolon_token + 1;
if (typeId)
- if (unsigned candidate = typeId->lastToken())
+ if (int candidate = typeId->lastToken())
return candidate;
if (equal_token)
return equal_token + 1;
if (name)
- if (unsigned candidate = name->lastToken())
+ if (int candidate = name->lastToken())
return candidate;
if (using_token)
return using_token + 1;
@@ -4453,40 +4536,40 @@ unsigned AliasDeclarationAST::lastToken() const
}
/** \generated */
-unsigned DesignatedInitializerAST::firstToken() const
+int DesignatedInitializerAST::firstToken() const
{
if (designator_list)
- if (unsigned candidate = designator_list->firstToken())
+ if (int candidate = designator_list->firstToken())
return candidate;
if (equal_token)
return equal_token;
if (initializer)
- if (unsigned candidate = initializer->firstToken())
+ if (int candidate = initializer->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned DesignatedInitializerAST::lastToken() const
+int DesignatedInitializerAST::lastToken() const
{
if (initializer)
- if (unsigned candidate = initializer->lastToken())
+ if (int candidate = initializer->lastToken())
return candidate;
if (equal_token)
return equal_token + 1;
if (designator_list)
- if (unsigned candidate = designator_list->lastToken())
+ if (int candidate = designator_list->lastToken())
return candidate;
return 1;
}
/** \generated */
-unsigned BracketDesignatorAST::firstToken() const
+int BracketDesignatorAST::firstToken() const
{
if (lbracket_token)
return lbracket_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
if (rbracket_token)
return rbracket_token;
@@ -4494,12 +4577,12 @@ unsigned BracketDesignatorAST::firstToken() const
}
/** \generated */
-unsigned BracketDesignatorAST::lastToken() const
+int BracketDesignatorAST::lastToken() const
{
if (rbracket_token)
return rbracket_token + 1;
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (lbracket_token)
return lbracket_token + 1;
@@ -4507,7 +4590,7 @@ unsigned BracketDesignatorAST::lastToken() const
}
/** \generated */
-unsigned DotDesignatorAST::firstToken() const
+int DotDesignatorAST::firstToken() const
{
if (dot_token)
return dot_token;
@@ -4517,7 +4600,7 @@ unsigned DotDesignatorAST::firstToken() const
}
/** \generated */
-unsigned DotDesignatorAST::lastToken() const
+int DotDesignatorAST::lastToken() const
{
if (identifier_token)
return identifier_token + 1;
@@ -4527,14 +4610,14 @@ unsigned DotDesignatorAST::lastToken() const
}
/** \generated */
-unsigned AlignmentSpecifierAST::firstToken() const
+int AlignmentSpecifierAST::firstToken() const
{
if (align_token)
return align_token;
if (lparen_token)
return lparen_token;
if (typeIdExprOrAlignmentExpr)
- if (unsigned candidate = typeIdExprOrAlignmentExpr->firstToken())
+ if (int candidate = typeIdExprOrAlignmentExpr->firstToken())
return candidate;
if (ellipses_token)
return ellipses_token;
@@ -4544,14 +4627,14 @@ unsigned AlignmentSpecifierAST::firstToken() const
}
/** \generated */
-unsigned AlignmentSpecifierAST::lastToken() const
+int AlignmentSpecifierAST::lastToken() const
{
if (rparen_token)
return rparen_token + 1;
if (ellipses_token)
return ellipses_token + 1;
if (typeIdExprOrAlignmentExpr)
- if (unsigned candidate = typeIdExprOrAlignmentExpr->lastToken())
+ if (int candidate = typeIdExprOrAlignmentExpr->lastToken())
return candidate;
if (lparen_token)
return lparen_token + 1;
@@ -4561,24 +4644,53 @@ unsigned AlignmentSpecifierAST::lastToken() const
}
/** \generated */
-unsigned NoExceptOperatorExpressionAST::firstToken() const
+int NoExceptOperatorExpressionAST::firstToken() const
{
if (noexcept_token)
return noexcept_token;
if (expression)
- if (unsigned candidate = expression->firstToken())
+ if (int candidate = expression->firstToken())
return candidate;
return 0;
}
/** \generated */
-unsigned NoExceptOperatorExpressionAST::lastToken() const
+int NoExceptOperatorExpressionAST::lastToken() const
{
if (expression)
- if (unsigned candidate = expression->lastToken())
+ if (int candidate = expression->lastToken())
return candidate;
if (noexcept_token)
return noexcept_token + 1;
return 1;
}
+int TypeConstraintAST::firstToken() const
+{
+ if (nestedName)
+ return nestedName->firstToken();
+ return conceptName->firstToken();
+}
+
+int TypeConstraintAST::lastToken() const
+{
+ if (greaterToken)
+ return greaterToken + 1;
+ return conceptName->lastToken();
+}
+
+int PlaceholderTypeSpecifierAST::firstToken() const
+{
+ if (typeConstraint)
+ return typeConstraint->firstToken();
+ if (declTypetoken)
+ return declTypetoken;
+ return autoToken;
+}
+
+int PlaceholderTypeSpecifierAST::lastToken() const
+{
+ if (rparenToken)
+ return rparenToken + 1;
+ return autoToken + 1;
+}
diff --git a/src/libs/3rdparty/cplusplus/AST.h b/src/libs/3rdparty/cplusplus/AST.h
index fc6750320b..3f48e192b9 100644
--- a/src/libs/3rdparty/cplusplus/AST.h
+++ b/src/libs/3rdparty/cplusplus/AST.h
@@ -26,6 +26,14 @@
namespace CPlusPlus {
+// clang-cl needs an export for the subclass, while msvc fails to build in debug mode if
+// the export is present.
+#if defined(Q_CC_CLANG) && defined(Q_CC_MSVC)
+#define CPLUSPLUS_EXPORT_SUBCLASS CPLUSPLUS_EXPORT
+#else
+#define CPLUSPLUS_EXPORT_SUBCLASS
+#endif
+
template <typename Tptr>
class CPLUSPLUS_EXPORT List: public Managed
{
@@ -41,7 +49,7 @@ public:
: value(value), next(0)
{ }
- unsigned firstToken() const
+ int firstToken() const
{
if (value)
return value->firstToken();
@@ -50,7 +58,7 @@ public:
return 0;
}
- unsigned lastToken() const
+ int lastToken() const
{
Tptr lv = lastValue();
@@ -75,8 +83,51 @@ public:
Tptr value;
List *next;
+
+ class CPLUSPLUS_EXPORT_SUBCLASS ListIterator
+ {
+ List<Tptr> *iter;
+
+ public:
+ ListIterator(List<Tptr> *iter)
+ : iter(iter)
+ {}
+ Tptr operator*() { return iter->value; }
+ ListIterator &operator++()
+ {
+ if (iter)
+ iter = iter->next;
+ return *this;
+ }
+ bool operator==(const ListIterator &other) { return iter == other.iter; }
+ bool operator!=(const ListIterator &other) { return iter != other.iter; }
+ };
+ ListIterator begin() { return {this}; }
+ ListIterator end() { return {nullptr}; }
+
+ int size() { return next ? next->size() + 1 : 1; }
};
+template<typename Tptr>
+typename List<Tptr>::ListIterator begin(List<Tptr> *list)
+{
+ return list ? list->begin() : typename List<Tptr>::ListIterator(nullptr);
+}
+
+template<typename Tptr>
+typename List<Tptr>::ListIterator end(List<Tptr> *list)
+{
+ return list ? list->end() : typename List<Tptr>::ListIterator(nullptr);
+}
+
+template<typename Tptr>
+int size(List<Tptr> *list)
+{
+ if (list)
+ return list->size();
+ return 0;
+}
+
class CPLUSPLUS_EXPORT AST: public Managed
{
AST(const AST &other);
@@ -118,173 +169,183 @@ public:
return false;
}
- virtual unsigned firstToken() const = 0;
- virtual unsigned lastToken() const = 0;
+ virtual int firstToken() const = 0;
+ virtual int lastToken() const = 0;
virtual AST *clone(MemoryPool *pool) const = 0;
- virtual AccessDeclarationAST *asAccessDeclaration() { return 0; }
- virtual AliasDeclarationAST *asAliasDeclaration() { return 0; }
- virtual AlignmentSpecifierAST *asAlignmentSpecifier() { return 0; }
- virtual AlignofExpressionAST *asAlignofExpression() { return 0; }
- virtual AnonymousNameAST *asAnonymousName() { return 0; }
- virtual ArrayAccessAST *asArrayAccess() { return 0; }
- virtual ArrayDeclaratorAST *asArrayDeclarator() { return 0; }
- virtual ArrayInitializerAST *asArrayInitializer() { return 0; }
- virtual AsmDefinitionAST *asAsmDefinition() { return 0; }
- virtual AttributeSpecifierAST *asAttributeSpecifier() { return 0; }
- virtual BaseSpecifierAST *asBaseSpecifier() { return 0; }
- virtual BinaryExpressionAST *asBinaryExpression() { return 0; }
- virtual BoolLiteralAST *asBoolLiteral() { return 0; }
- virtual BracedInitializerAST *asBracedInitializer() { return 0; }
- virtual BracketDesignatorAST *asBracketDesignator() { return 0; }
- virtual BreakStatementAST *asBreakStatement() { return 0; }
- virtual CallAST *asCall() { return 0; }
- virtual CaptureAST *asCapture() { return 0; }
- virtual CaseStatementAST *asCaseStatement() { return 0; }
- virtual CastExpressionAST *asCastExpression() { return 0; }
- virtual CatchClauseAST *asCatchClause() { return 0; }
- virtual ClassSpecifierAST *asClassSpecifier() { return 0; }
- virtual CompoundExpressionAST *asCompoundExpression() { return 0; }
- virtual CompoundLiteralAST *asCompoundLiteral() { return 0; }
- virtual CompoundStatementAST *asCompoundStatement() { return 0; }
- virtual ConditionAST *asCondition() { return 0; }
- virtual ConditionalExpressionAST *asConditionalExpression() { return 0; }
- virtual ContinueStatementAST *asContinueStatement() { return 0; }
- virtual ConversionFunctionIdAST *asConversionFunctionId() { return 0; }
- virtual CoreDeclaratorAST *asCoreDeclarator() { return 0; }
- virtual CppCastExpressionAST *asCppCastExpression() { return 0; }
- virtual CtorInitializerAST *asCtorInitializer() { return 0; }
- virtual DeclarationAST *asDeclaration() { return 0; }
- virtual DeclarationStatementAST *asDeclarationStatement() { return 0; }
- virtual DeclaratorAST *asDeclarator() { return 0; }
- virtual DeclaratorIdAST *asDeclaratorId() { return 0; }
- virtual DecltypeSpecifierAST *asDecltypeSpecifier() { return 0; }
- virtual DeleteExpressionAST *asDeleteExpression() { return 0; }
- virtual DesignatedInitializerAST *asDesignatedInitializer() { return 0; }
- virtual DesignatorAST *asDesignator() { return 0; }
- virtual DestructorNameAST *asDestructorName() { return 0; }
- virtual DoStatementAST *asDoStatement() { return 0; }
- virtual DotDesignatorAST *asDotDesignator() { return 0; }
- virtual DynamicExceptionSpecificationAST *asDynamicExceptionSpecification() { return 0; }
- virtual ElaboratedTypeSpecifierAST *asElaboratedTypeSpecifier() { return 0; }
- virtual EmptyDeclarationAST *asEmptyDeclaration() { return 0; }
- virtual EnumSpecifierAST *asEnumSpecifier() { return 0; }
- virtual EnumeratorAST *asEnumerator() { return 0; }
- virtual ExceptionDeclarationAST *asExceptionDeclaration() { return 0; }
- virtual ExceptionSpecificationAST *asExceptionSpecification() { return 0; }
- virtual ExpressionAST *asExpression() { return 0; }
- virtual ExpressionListParenAST *asExpressionListParen() { return 0; }
- virtual ExpressionOrDeclarationStatementAST *asExpressionOrDeclarationStatement() { return 0; }
- virtual ExpressionStatementAST *asExpressionStatement() { return 0; }
- virtual ForStatementAST *asForStatement() { return 0; }
- virtual ForeachStatementAST *asForeachStatement() { return 0; }
- virtual FunctionDeclaratorAST *asFunctionDeclarator() { return 0; }
- virtual FunctionDefinitionAST *asFunctionDefinition() { return 0; }
- virtual GnuAttributeAST *asGnuAttribute() { return 0; }
- virtual GnuAttributeSpecifierAST *asGnuAttributeSpecifier() { return 0; }
- virtual GotoStatementAST *asGotoStatement() { return 0; }
- virtual IdExpressionAST *asIdExpression() { return 0; }
- virtual IfStatementAST *asIfStatement() { return 0; }
- virtual LabeledStatementAST *asLabeledStatement() { return 0; }
- virtual LambdaCaptureAST *asLambdaCapture() { return 0; }
- virtual LambdaDeclaratorAST *asLambdaDeclarator() { return 0; }
- virtual LambdaExpressionAST *asLambdaExpression() { return 0; }
- virtual LambdaIntroducerAST *asLambdaIntroducer() { return 0; }
- virtual LinkageBodyAST *asLinkageBody() { return 0; }
- virtual LinkageSpecificationAST *asLinkageSpecification() { return 0; }
- virtual MemInitializerAST *asMemInitializer() { return 0; }
- virtual MemberAccessAST *asMemberAccess() { return 0; }
- virtual NameAST *asName() { return 0; }
- virtual NamedTypeSpecifierAST *asNamedTypeSpecifier() { return 0; }
- virtual NamespaceAST *asNamespace() { return 0; }
- virtual NamespaceAliasDefinitionAST *asNamespaceAliasDefinition() { return 0; }
- virtual NestedDeclaratorAST *asNestedDeclarator() { return 0; }
- virtual NestedExpressionAST *asNestedExpression() { return 0; }
- virtual NestedNameSpecifierAST *asNestedNameSpecifier() { return 0; }
- virtual NewArrayDeclaratorAST *asNewArrayDeclarator() { return 0; }
- virtual NewExpressionAST *asNewExpression() { return 0; }
- virtual NewTypeIdAST *asNewTypeId() { return 0; }
- virtual NoExceptOperatorExpressionAST *asNoExceptOperatorExpression() { return 0; }
- virtual NoExceptSpecificationAST *asNoExceptSpecification() { return 0; }
- virtual NumericLiteralAST *asNumericLiteral() { return 0; }
- virtual ObjCClassDeclarationAST *asObjCClassDeclaration() { return 0; }
- virtual ObjCClassForwardDeclarationAST *asObjCClassForwardDeclaration() { return 0; }
- virtual ObjCDynamicPropertiesDeclarationAST *asObjCDynamicPropertiesDeclaration() { return 0; }
- virtual ObjCEncodeExpressionAST *asObjCEncodeExpression() { return 0; }
- virtual ObjCFastEnumerationAST *asObjCFastEnumeration() { return 0; }
- virtual ObjCInstanceVariablesDeclarationAST *asObjCInstanceVariablesDeclaration() { return 0; }
- virtual ObjCMessageArgumentAST *asObjCMessageArgument() { return 0; }
- virtual ObjCMessageArgumentDeclarationAST *asObjCMessageArgumentDeclaration() { return 0; }
- virtual ObjCMessageExpressionAST *asObjCMessageExpression() { return 0; }
- virtual ObjCMethodDeclarationAST *asObjCMethodDeclaration() { return 0; }
- virtual ObjCMethodPrototypeAST *asObjCMethodPrototype() { return 0; }
- virtual ObjCPropertyAttributeAST *asObjCPropertyAttribute() { return 0; }
- virtual ObjCPropertyDeclarationAST *asObjCPropertyDeclaration() { return 0; }
- virtual ObjCProtocolDeclarationAST *asObjCProtocolDeclaration() { return 0; }
- virtual ObjCProtocolExpressionAST *asObjCProtocolExpression() { return 0; }
- virtual ObjCProtocolForwardDeclarationAST *asObjCProtocolForwardDeclaration() { return 0; }
- virtual ObjCProtocolRefsAST *asObjCProtocolRefs() { return 0; }
- virtual ObjCSelectorAST *asObjCSelector() { return 0; }
- virtual ObjCSelectorArgumentAST *asObjCSelectorArgument() { return 0; }
- virtual ObjCSelectorExpressionAST *asObjCSelectorExpression() { return 0; }
- virtual ObjCSynchronizedStatementAST *asObjCSynchronizedStatement() { return 0; }
- virtual ObjCSynthesizedPropertiesDeclarationAST *asObjCSynthesizedPropertiesDeclaration() { return 0; }
- virtual ObjCSynthesizedPropertyAST *asObjCSynthesizedProperty() { return 0; }
- virtual ObjCTypeNameAST *asObjCTypeName() { return 0; }
- virtual ObjCVisibilityDeclarationAST *asObjCVisibilityDeclaration() { return 0; }
- virtual OperatorAST *asOperator() { return 0; }
- virtual OperatorFunctionIdAST *asOperatorFunctionId() { return 0; }
- virtual ParameterDeclarationAST *asParameterDeclaration() { return 0; }
- virtual ParameterDeclarationClauseAST *asParameterDeclarationClause() { return 0; }
- virtual PointerAST *asPointer() { return 0; }
- virtual PointerLiteralAST *asPointerLiteral() { return 0; }
- virtual PointerToMemberAST *asPointerToMember() { return 0; }
- virtual PostIncrDecrAST *asPostIncrDecr() { return 0; }
- virtual PostfixAST *asPostfix() { return 0; }
- virtual PostfixDeclaratorAST *asPostfixDeclarator() { return 0; }
- virtual PtrOperatorAST *asPtrOperator() { return 0; }
- virtual QtEnumDeclarationAST *asQtEnumDeclaration() { return 0; }
- virtual QtFlagsDeclarationAST *asQtFlagsDeclaration() { return 0; }
- virtual QtInterfaceNameAST *asQtInterfaceName() { return 0; }
- virtual QtInterfacesDeclarationAST *asQtInterfacesDeclaration() { return 0; }
- virtual QtMemberDeclarationAST *asQtMemberDeclaration() { return 0; }
- virtual QtMethodAST *asQtMethod() { return 0; }
- virtual QtObjectTagAST *asQtObjectTag() { return 0; }
- virtual QtPrivateSlotAST *asQtPrivateSlot() { return 0; }
- virtual QtPropertyDeclarationAST *asQtPropertyDeclaration() { return 0; }
- virtual QtPropertyDeclarationItemAST *asQtPropertyDeclarationItem() { return 0; }
- virtual QualifiedNameAST *asQualifiedName() { return 0; }
- virtual RangeBasedForStatementAST *asRangeBasedForStatement() { return 0; }
- virtual ReferenceAST *asReference() { return 0; }
- virtual ReturnStatementAST *asReturnStatement() { return 0; }
- virtual SimpleDeclarationAST *asSimpleDeclaration() { return 0; }
- virtual SimpleNameAST *asSimpleName() { return 0; }
- virtual SimpleSpecifierAST *asSimpleSpecifier() { return 0; }
- virtual SizeofExpressionAST *asSizeofExpression() { return 0; }
- virtual SpecifierAST *asSpecifier() { return 0; }
- virtual StatementAST *asStatement() { return 0; }
- virtual StaticAssertDeclarationAST *asStaticAssertDeclaration() { return 0; }
- virtual StringLiteralAST *asStringLiteral() { return 0; }
- virtual SwitchStatementAST *asSwitchStatement() { return 0; }
- virtual TemplateDeclarationAST *asTemplateDeclaration() { return 0; }
- virtual TemplateIdAST *asTemplateId() { return 0; }
- virtual TemplateTypeParameterAST *asTemplateTypeParameter() { return 0; }
- virtual ThisExpressionAST *asThisExpression() { return 0; }
- virtual ThrowExpressionAST *asThrowExpression() { return 0; }
- virtual TrailingReturnTypeAST *asTrailingReturnType() { return 0; }
- virtual TranslationUnitAST *asTranslationUnit() { return 0; }
- virtual TryBlockStatementAST *asTryBlockStatement() { return 0; }
- virtual TypeConstructorCallAST *asTypeConstructorCall() { return 0; }
- virtual TypeIdAST *asTypeId() { return 0; }
- virtual TypeidExpressionAST *asTypeidExpression() { return 0; }
- virtual TypenameCallExpressionAST *asTypenameCallExpression() { return 0; }
- virtual TypenameTypeParameterAST *asTypenameTypeParameter() { return 0; }
- virtual TypeofSpecifierAST *asTypeofSpecifier() { return 0; }
- virtual UnaryExpressionAST *asUnaryExpression() { return 0; }
- virtual UsingAST *asUsing() { return 0; }
- virtual UsingDirectiveAST *asUsingDirective() { return 0; }
- virtual WhileStatementAST *asWhileStatement() { return 0; }
+ virtual AccessDeclarationAST *asAccessDeclaration() { return nullptr; }
+ virtual AliasDeclarationAST *asAliasDeclaration() { return nullptr; }
+ virtual AlignmentSpecifierAST *asAlignmentSpecifier() { return nullptr; }
+ virtual AlignofExpressionAST *asAlignofExpression() { return nullptr; }
+ virtual AnonymousNameAST *asAnonymousName() { return nullptr; }
+ virtual ArrayAccessAST *asArrayAccess() { return nullptr; }
+ virtual ArrayDeclaratorAST *asArrayDeclarator() { return nullptr; }
+ virtual ArrayInitializerAST *asArrayInitializer() { return nullptr; }
+ virtual AsmDefinitionAST *asAsmDefinition() { return nullptr; }
+ virtual AttributeSpecifierAST *asAttributeSpecifier() { return nullptr; }
+ virtual AwaitExpressionAST *asAwaitExpression() { return nullptr; }
+ virtual BaseSpecifierAST *asBaseSpecifier() { return nullptr; }
+ virtual BinaryExpressionAST *asBinaryExpression() { return nullptr; }
+ virtual BoolLiteralAST *asBoolLiteral() { return nullptr; }
+ virtual BracedInitializerAST *asBracedInitializer() { return nullptr; }
+ virtual BracketDesignatorAST *asBracketDesignator() { return nullptr; }
+ virtual BreakStatementAST *asBreakStatement() { return nullptr; }
+ virtual CallAST *asCall() { return nullptr; }
+ virtual CaptureAST *asCapture() { return nullptr; }
+ virtual CaseStatementAST *asCaseStatement() { return nullptr; }
+ virtual CastExpressionAST *asCastExpression() { return nullptr; }
+ virtual CatchClauseAST *asCatchClause() { return nullptr; }
+ virtual ClassSpecifierAST *asClassSpecifier() { return nullptr; }
+ virtual CompoundExpressionAST *asCompoundExpression() { return nullptr; }
+ virtual CompoundLiteralAST *asCompoundLiteral() { return nullptr; }
+ virtual CompoundStatementAST *asCompoundStatement() { return nullptr; }
+ virtual ConditionAST *asCondition() { return nullptr; }
+ virtual ConditionalExpressionAST *asConditionalExpression() { return nullptr; }
+ virtual ContinueStatementAST *asContinueStatement() { return nullptr; }
+ virtual ConversionFunctionIdAST *asConversionFunctionId() { return nullptr; }
+ virtual CoreDeclaratorAST *asCoreDeclarator() { return nullptr; }
+ virtual CppCastExpressionAST *asCppCastExpression() { return nullptr; }
+ virtual CtorInitializerAST *asCtorInitializer() { return nullptr; }
+ virtual DeclarationAST *asDeclaration() { return nullptr; }
+ virtual DeclarationStatementAST *asDeclarationStatement() { return nullptr; }
+ virtual DeclaratorAST *asDeclarator() { return nullptr; }
+ virtual DeclaratorIdAST *asDeclaratorId() { return nullptr; }
+ virtual DecompositionDeclaratorAST *asDecompositionDeclarator() { return nullptr; }
+ virtual DecltypeSpecifierAST *asDecltypeSpecifier() { return nullptr; }
+ virtual DeleteExpressionAST *asDeleteExpression() { return nullptr; }
+ virtual DesignatedInitializerAST *asDesignatedInitializer() { return nullptr; }
+ virtual DesignatorAST *asDesignator() { return nullptr; }
+ virtual DestructorNameAST *asDestructorName() { return nullptr; }
+ virtual DoStatementAST *asDoStatement() { return nullptr; }
+ virtual DotDesignatorAST *asDotDesignator() { return nullptr; }
+ virtual DynamicExceptionSpecificationAST *asDynamicExceptionSpecification() { return nullptr; }
+ virtual ElaboratedTypeSpecifierAST *asElaboratedTypeSpecifier() { return nullptr; }
+ virtual EmptyDeclarationAST *asEmptyDeclaration() { return nullptr; }
+ virtual EnumSpecifierAST *asEnumSpecifier() { return nullptr; }
+ virtual EnumeratorAST *asEnumerator() { return nullptr; }
+ virtual ExceptionDeclarationAST *asExceptionDeclaration() { return nullptr; }
+ virtual ExceptionSpecificationAST *asExceptionSpecification() { return nullptr; }
+ virtual ExpressionAST *asExpression() { return nullptr; }
+ virtual ExpressionListParenAST *asExpressionListParen() { return nullptr; }
+ virtual ExpressionOrDeclarationStatementAST *asExpressionOrDeclarationStatement() { return nullptr; }
+ virtual ExpressionStatementAST *asExpressionStatement() { return nullptr; }
+ virtual ForStatementAST *asForStatement() { return nullptr; }
+ virtual ForeachStatementAST *asForeachStatement() { return nullptr; }
+ virtual FunctionDeclaratorAST *asFunctionDeclarator() { return nullptr; }
+ virtual FunctionDefinitionAST *asFunctionDefinition() { return nullptr; }
+ virtual GnuAttributeAST *asGnuAttribute() { return nullptr; }
+ virtual GnuAttributeSpecifierAST *asGnuAttributeSpecifier() { return nullptr; }
+ virtual GotoStatementAST *asGotoStatement() { return nullptr; }
+ virtual IdExpressionAST *asIdExpression() { return nullptr; }
+ virtual IfStatementAST *asIfStatement() { return nullptr; }
+ virtual LabeledStatementAST *asLabeledStatement() { return nullptr; }
+ virtual LambdaCaptureAST *asLambdaCapture() { return nullptr; }
+ virtual LambdaDeclaratorAST *asLambdaDeclarator() { return nullptr; }
+ virtual LambdaExpressionAST *asLambdaExpression() { return nullptr; }
+ virtual LambdaIntroducerAST *asLambdaIntroducer() { return nullptr; }
+ virtual LinkageBodyAST *asLinkageBody() { return nullptr; }
+ virtual LinkageSpecificationAST *asLinkageSpecification() { return nullptr; }
+ virtual MemInitializerAST *asMemInitializer() { return nullptr; }
+ virtual MemberAccessAST *asMemberAccess() { return nullptr; }
+ virtual MsvcDeclspecSpecifierAST *asMsvcDeclspecSpecifier() { return nullptr; }
+ virtual NameAST *asName() { return nullptr; }
+ virtual NamedTypeSpecifierAST *asNamedTypeSpecifier() { return nullptr; }
+ virtual NamespaceAST *asNamespace() { return nullptr; }
+ virtual NamespaceAliasDefinitionAST *asNamespaceAliasDefinition() { return nullptr; }
+ virtual NestedDeclaratorAST *asNestedDeclarator() { return nullptr; }
+ virtual NestedExpressionAST *asNestedExpression() { return nullptr; }
+ virtual NestedNameSpecifierAST *asNestedNameSpecifier() { return nullptr; }
+ virtual NewArrayDeclaratorAST *asNewArrayDeclarator() { return nullptr; }
+ virtual NewExpressionAST *asNewExpression() { return nullptr; }
+ virtual NewTypeIdAST *asNewTypeId() { return nullptr; }
+ virtual NoExceptOperatorExpressionAST *asNoExceptOperatorExpression() { return nullptr; }
+ virtual NoExceptSpecificationAST *asNoExceptSpecification() { return nullptr; }
+ virtual NumericLiteralAST *asNumericLiteral() { return nullptr; }
+ virtual ObjCClassDeclarationAST *asObjCClassDeclaration() { return nullptr; }
+ virtual ObjCClassForwardDeclarationAST *asObjCClassForwardDeclaration() { return nullptr; }
+ virtual ObjCDynamicPropertiesDeclarationAST *asObjCDynamicPropertiesDeclaration() { return nullptr; }
+ virtual ObjCEncodeExpressionAST *asObjCEncodeExpression() { return nullptr; }
+ virtual ObjCFastEnumerationAST *asObjCFastEnumeration() { return nullptr; }
+ virtual ObjCInstanceVariablesDeclarationAST *asObjCInstanceVariablesDeclaration() { return nullptr; }
+ virtual ObjCMessageArgumentAST *asObjCMessageArgument() { return nullptr; }
+ virtual ObjCMessageArgumentDeclarationAST *asObjCMessageArgumentDeclaration() { return nullptr; }
+ virtual ObjCMessageExpressionAST *asObjCMessageExpression() { return nullptr; }
+ virtual ObjCMethodDeclarationAST *asObjCMethodDeclaration() { return nullptr; }
+ virtual ObjCMethodPrototypeAST *asObjCMethodPrototype() { return nullptr; }
+ virtual ObjCPropertyAttributeAST *asObjCPropertyAttribute() { return nullptr; }
+ virtual ObjCPropertyDeclarationAST *asObjCPropertyDeclaration() { return nullptr; }
+ virtual ObjCProtocolDeclarationAST *asObjCProtocolDeclaration() { return nullptr; }
+ virtual ObjCProtocolExpressionAST *asObjCProtocolExpression() { return nullptr; }
+ virtual ObjCProtocolForwardDeclarationAST *asObjCProtocolForwardDeclaration() { return nullptr; }
+ virtual ObjCProtocolRefsAST *asObjCProtocolRefs() { return nullptr; }
+ virtual ObjCSelectorAST *asObjCSelector() { return nullptr; }
+ virtual ObjCSelectorArgumentAST *asObjCSelectorArgument() { return nullptr; }
+ virtual ObjCSelectorExpressionAST *asObjCSelectorExpression() { return nullptr; }
+ virtual ObjCSynchronizedStatementAST *asObjCSynchronizedStatement() { return nullptr; }
+ virtual ObjCSynthesizedPropertiesDeclarationAST *asObjCSynthesizedPropertiesDeclaration() { return nullptr; }
+ virtual ObjCSynthesizedPropertyAST *asObjCSynthesizedProperty() { return nullptr; }
+ virtual ObjCTypeNameAST *asObjCTypeName() { return nullptr; }
+ virtual ObjCVisibilityDeclarationAST *asObjCVisibilityDeclaration() { return nullptr; }
+ virtual OperatorAST *asOperator() { return nullptr; }
+ virtual OperatorFunctionIdAST *asOperatorFunctionId() { return nullptr; }
+ virtual ParameterDeclarationAST *asParameterDeclaration() { return nullptr; }
+ virtual ParameterDeclarationClauseAST *asParameterDeclarationClause() { return nullptr; }
+ virtual PlaceholderTypeSpecifierAST *asPlaceholderTypeSpecifier() { return nullptr; }
+ virtual PointerAST *asPointer() { return nullptr; }
+ virtual PointerLiteralAST *asPointerLiteral() { return nullptr; }
+ virtual PointerToMemberAST *asPointerToMember() { return nullptr; }
+ virtual PostIncrDecrAST *asPostIncrDecr() { return nullptr; }
+ virtual PostfixAST *asPostfix() { return nullptr; }
+ virtual PostfixDeclaratorAST *asPostfixDeclarator() { return nullptr; }
+ virtual PtrOperatorAST *asPtrOperator() { return nullptr; }
+ virtual QtEnumDeclarationAST *asQtEnumDeclaration() { return nullptr; }
+ virtual QtFlagsDeclarationAST *asQtFlagsDeclaration() { return nullptr; }
+ virtual QtInterfaceNameAST *asQtInterfaceName() { return nullptr; }
+ virtual QtInterfacesDeclarationAST *asQtInterfacesDeclaration() { return nullptr; }
+ virtual QtMemberDeclarationAST *asQtMemberDeclaration() { return nullptr; }
+ virtual QtMethodAST *asQtMethod() { return nullptr; }
+ virtual QtObjectTagAST *asQtObjectTag() { return nullptr; }
+ virtual QtPrivateSlotAST *asQtPrivateSlot() { return nullptr; }
+ virtual QtPropertyDeclarationAST *asQtPropertyDeclaration() { return nullptr; }
+ virtual QtPropertyDeclarationItemAST *asQtPropertyDeclarationItem() { return nullptr; }
+ virtual QualifiedNameAST *asQualifiedName() { return nullptr; }
+ virtual RangeBasedForStatementAST *asRangeBasedForStatement() { return nullptr; }
+ virtual ReferenceAST *asReference() { return nullptr; }
+ virtual ReturnStatementAST *asReturnStatement() { return nullptr; }
+ virtual SimpleDeclarationAST *asSimpleDeclaration() { return nullptr; }
+ virtual SimpleNameAST *asSimpleName() { return nullptr; }
+ virtual SimpleSpecifierAST *asSimpleSpecifier() { return nullptr; }
+ virtual SizeofExpressionAST *asSizeofExpression() { return nullptr; }
+ virtual SpecifierAST *asSpecifier() { return nullptr; }
+ virtual StatementAST *asStatement() { return nullptr; }
+ virtual StaticAssertDeclarationAST *asStaticAssertDeclaration() { return nullptr; }
+ virtual StdAttributeSpecifierAST *asStdAttributeSpecifier() { return nullptr; }
+ virtual StringLiteralAST *asStringLiteral() { return nullptr; }
+ virtual SwitchStatementAST *asSwitchStatement() { return nullptr; }
+ virtual TemplateDeclarationAST *asTemplateDeclaration() { return nullptr; }
+ virtual ConceptDeclarationAST *asConceptDeclaration() { return nullptr; }
+ virtual TemplateIdAST *asTemplateId() { return nullptr; }
+ virtual TemplateTypeParameterAST *asTemplateTypeParameter() { return nullptr; }
+ virtual ThisExpressionAST *asThisExpression() { return nullptr; }
+ virtual ThrowExpressionAST *asThrowExpression() { return nullptr; }
+ virtual TrailingReturnTypeAST *asTrailingReturnType() { return nullptr; }
+ virtual TranslationUnitAST *asTranslationUnit() { return nullptr; }
+ virtual TryBlockStatementAST *asTryBlockStatement() { return nullptr; }
+ virtual TypeConstraintAST *asTypeConstraint() { return nullptr; }
+ virtual TypeConstructorCallAST *asTypeConstructorCall() { return nullptr; }
+ virtual TypeIdAST *asTypeId() { return nullptr; }
+ virtual TypeidExpressionAST *asTypeidExpression() { return nullptr; }
+ virtual TypenameCallExpressionAST *asTypenameCallExpression() { return nullptr; }
+ virtual TypenameTypeParameterAST *asTypenameTypeParameter() { return nullptr; }
+ virtual TypeofSpecifierAST *asTypeofSpecifier() { return nullptr; }
+ virtual UnaryExpressionAST *asUnaryExpression() { return nullptr; }
+ virtual RequiresExpressionAST *asRequiresExpression() { return nullptr; }
+ virtual RequiresClauseAST *asRequiresClause() { return nullptr; }
+ virtual UsingAST *asUsing() { return nullptr; }
+ virtual UsingDirectiveAST *asUsingDirective() { return nullptr; }
+ virtual WhileStatementAST *asWhileStatement() { return nullptr; }
+ virtual YieldExpressionAST *asYieldExpression() { return nullptr; }
protected:
virtual void accept0(ASTVisitor *visitor) = 0;
@@ -294,4396 +355,3531 @@ protected:
class CPLUSPLUS_EXPORT StatementAST: public AST
{
public:
- StatementAST()
- {}
+ StatementAST *asStatement() override { return this; }
- virtual StatementAST *asStatement() { return this; }
-
- virtual StatementAST *clone(MemoryPool *pool) const = 0;
+ StatementAST *clone(MemoryPool *pool) const override = 0;
};
class CPLUSPLUS_EXPORT ExpressionAST: public AST
{
public:
- ExpressionAST()
- {}
-
- virtual ExpressionAST *asExpression() { return this; }
+ ExpressionAST *asExpression() override { return this; }
- virtual ExpressionAST *clone(MemoryPool *pool) const = 0;
+ ExpressionAST *clone(MemoryPool *pool) const override = 0;
};
class CPLUSPLUS_EXPORT DeclarationAST: public AST
{
public:
- DeclarationAST()
- {}
+ DeclarationAST *asDeclaration() override { return this; }
- virtual DeclarationAST *asDeclaration() { return this; }
-
- virtual DeclarationAST *clone(MemoryPool *pool) const = 0;
+ DeclarationAST *clone(MemoryPool *pool) const override = 0;
};
class CPLUSPLUS_EXPORT NameAST: public AST
{
public: // annotations
- const Name *name;
+ const Name *name = nullptr;
public:
- NameAST()
- : name(0)
- {}
-
- virtual NameAST *asName() { return this; }
+ NameAST *asName() override { return this; }
- virtual NameAST *clone(MemoryPool *pool) const = 0;
+ NameAST *clone(MemoryPool *pool) const override = 0;
};
class CPLUSPLUS_EXPORT SpecifierAST: public AST
{
public:
- SpecifierAST()
- {}
+ SpecifierAST *asSpecifier() override { return this; }
- virtual SpecifierAST *asSpecifier() { return this; }
-
- virtual SpecifierAST *clone(MemoryPool *pool) const = 0;
+ SpecifierAST *clone(MemoryPool *pool) const override = 0;
};
class CPLUSPLUS_EXPORT PtrOperatorAST: public AST
{
public:
- PtrOperatorAST()
- {}
-
- virtual PtrOperatorAST *asPtrOperator() { return this; }
+ PtrOperatorAST *asPtrOperator() override { return this; }
- virtual PtrOperatorAST *clone(MemoryPool *pool) const = 0;
+ PtrOperatorAST *clone(MemoryPool *pool) const override = 0;
};
class CPLUSPLUS_EXPORT PostfixAST: public ExpressionAST
{
public:
- PostfixAST()
- {}
+ PostfixAST *asPostfix() override { return this; }
- virtual PostfixAST *asPostfix() { return this; }
-
- virtual PostfixAST *clone(MemoryPool *pool) const = 0;
+ PostfixAST *clone(MemoryPool *pool) const override = 0;
};
class CPLUSPLUS_EXPORT CoreDeclaratorAST: public AST
{
public:
- CoreDeclaratorAST()
- {}
-
- virtual CoreDeclaratorAST *asCoreDeclarator() { return this; }
+ CoreDeclaratorAST *asCoreDeclarator() override { return this; }
- virtual CoreDeclaratorAST *clone(MemoryPool *pool) const = 0;
+ CoreDeclaratorAST *clone(MemoryPool *pool) const override = 0;
};
class CPLUSPLUS_EXPORT PostfixDeclaratorAST: public AST
{
public:
- PostfixDeclaratorAST()
- {}
+ PostfixDeclaratorAST *asPostfixDeclarator() override { return this; }
- virtual PostfixDeclaratorAST *asPostfixDeclarator() { return this; }
-
- virtual PostfixDeclaratorAST *clone(MemoryPool *pool) const = 0;
+ PostfixDeclaratorAST *clone(MemoryPool *pool) const override = 0;
};
class CPLUSPLUS_EXPORT ObjCSelectorArgumentAST: public AST
{
public:
- unsigned name_token;
- unsigned colon_token;
+ int name_token = 0;
+ int colon_token = 0;
public:
- ObjCSelectorArgumentAST()
- : name_token(0)
- , colon_token(0)
- {}
-
- virtual ObjCSelectorArgumentAST *asObjCSelectorArgument() { return this; }
+ ObjCSelectorArgumentAST *asObjCSelectorArgument() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCSelectorArgumentAST *clone(MemoryPool *pool) const;
+ ObjCSelectorArgumentAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCSelectorAST: public NameAST
{
public:
- ObjCSelectorArgumentListAST *selector_argument_list;
+ ObjCSelectorArgumentListAST *selector_argument_list = nullptr;
public:
- ObjCSelectorAST()
- : selector_argument_list(0)
- {}
+ ObjCSelectorAST *asObjCSelector() override { return this; }
- virtual ObjCSelectorAST *asObjCSelector() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCSelectorAST *clone(MemoryPool *pool) const;
+ ObjCSelectorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT SimpleSpecifierAST: public SpecifierAST
{
public:
- unsigned specifier_token;
+ int specifier_token = 0;
public:
- SimpleSpecifierAST()
- : specifier_token(0)
- {}
-
- virtual SimpleSpecifierAST *asSimpleSpecifier() { return this; }
+ SimpleSpecifierAST *asSimpleSpecifier() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual SimpleSpecifierAST *clone(MemoryPool *pool) const;
+ SimpleSpecifierAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT AttributeSpecifierAST: public SpecifierAST
{
public:
- AttributeSpecifierAST()
- {}
+ AttributeSpecifierAST *asAttributeSpecifier() override { return this; }
- virtual AttributeSpecifierAST *asAttributeSpecifier() { return this; }
-
- virtual AttributeSpecifierAST *clone(MemoryPool *pool) const = 0;
+ AttributeSpecifierAST *clone(MemoryPool *pool) const override = 0;
};
class CPLUSPLUS_EXPORT AlignmentSpecifierAST: public AttributeSpecifierAST
{
public:
- unsigned align_token;
- unsigned lparen_token;
- ExpressionAST *typeIdExprOrAlignmentExpr;
- unsigned ellipses_token;
- unsigned rparen_token;
+ int align_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *typeIdExprOrAlignmentExpr = nullptr;
+ int ellipses_token = 0;
+ int rparen_token = 0;
public:
- AlignmentSpecifierAST()
- : align_token(0)
- , lparen_token(0)
- , typeIdExprOrAlignmentExpr(0)
- , ellipses_token(0)
- , rparen_token(0)
- {}
-
- virtual AlignmentSpecifierAST *asAlignmentSpecifier() { return this; }
+ AlignmentSpecifierAST *asAlignmentSpecifier() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual AlignmentSpecifierAST *clone(MemoryPool *pool) const;
+ AlignmentSpecifierAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT GnuAttributeSpecifierAST: public AttributeSpecifierAST
{
public:
- unsigned attribute_token;
- unsigned first_lparen_token;
- unsigned second_lparen_token;
- GnuAttributeListAST *attribute_list;
- unsigned first_rparen_token;
- unsigned second_rparen_token;
+ int attribute_token = 0;
+ int first_lparen_token = 0;
+ int second_lparen_token = 0;
+ GnuAttributeListAST *attribute_list = nullptr;
+ int first_rparen_token = 0;
+ int second_rparen_token = 0;
+
+public:
+ GnuAttributeSpecifierAST *asGnuAttributeSpecifier() override { return this; }
+
+ int firstToken() const override;
+ int lastToken() const override;
+
+ GnuAttributeSpecifierAST *clone(MemoryPool *pool) const override;
+
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
+class CPLUSPLUS_EXPORT MsvcDeclspecSpecifierAST: public AttributeSpecifierAST
+{
+public:
+ int attribute_token = 0;
+ int lparen_token = 0;
+ GnuAttributeListAST *attribute_list = nullptr;
+ int rparen_token = 0;
public:
- GnuAttributeSpecifierAST()
- : attribute_token(0)
- , first_lparen_token(0)
- , second_lparen_token(0)
- , attribute_list(0)
- , first_rparen_token(0)
- , second_rparen_token(0)
- {}
+ MsvcDeclspecSpecifierAST *asMsvcDeclspecSpecifier() override { return this; }
+
+ int firstToken() const override;
+ int lastToken() const override;
- virtual GnuAttributeSpecifierAST *asGnuAttributeSpecifier() { return this; }
+ MsvcDeclspecSpecifierAST *clone(MemoryPool *pool) const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
- virtual GnuAttributeSpecifierAST *clone(MemoryPool *pool) const;
+class CPLUSPLUS_EXPORT StdAttributeSpecifierAST: public AttributeSpecifierAST
+{
+public:
+ int first_lbracket_token = 0;
+ int second_lbracket_token = 0;
+ GnuAttributeListAST *attribute_list = nullptr;
+ int first_rbracket_token = 0;
+ int second_rbracket_token = 0;
+
+public:
+ StdAttributeSpecifierAST *asStdAttributeSpecifier() override { return this; }
+
+ int firstToken() const override;
+ int lastToken() const override;
+
+ StdAttributeSpecifierAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT GnuAttributeAST: public AST
{
public:
- unsigned identifier_token;
- unsigned lparen_token;
- unsigned tag_token;
- ExpressionListAST *expression_list;
- unsigned rparen_token;
+ int identifier_token = 0;
+ int lparen_token = 0;
+ int tag_token = 0;
+ ExpressionListAST *expression_list = nullptr;
+ int rparen_token = 0;
public:
- GnuAttributeAST()
- : identifier_token(0)
- , lparen_token(0)
- , tag_token(0)
- , expression_list(0)
- , rparen_token(0)
- {}
+ GnuAttributeAST *asGnuAttribute() override { return this; }
- virtual GnuAttributeAST *asGnuAttribute() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual GnuAttributeAST *clone(MemoryPool *pool) const;
+ GnuAttributeAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT TypeofSpecifierAST: public SpecifierAST
{
public:
- unsigned typeof_token;
- unsigned lparen_token;
- ExpressionAST *expression;
- unsigned rparen_token;
+ int typeof_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rparen_token = 0;
public:
- TypeofSpecifierAST()
- : typeof_token(0)
- , lparen_token(0)
- , expression(0)
- , rparen_token(0)
- {}
-
- virtual TypeofSpecifierAST *asTypeofSpecifier() { return this; }
+ TypeofSpecifierAST *asTypeofSpecifier() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual TypeofSpecifierAST *clone(MemoryPool *pool) const;
+ TypeofSpecifierAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT DecltypeSpecifierAST: public SpecifierAST
{
public:
- unsigned decltype_token;
- unsigned lparen_token;
- ExpressionAST *expression;
- unsigned rparen_token;
+ int decltype_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rparen_token = 0;
public:
- DecltypeSpecifierAST()
- : decltype_token(0)
- , lparen_token(0)
- , expression(0)
- , rparen_token(0)
- {}
+ DecltypeSpecifierAST *asDecltypeSpecifier() override { return this; }
- virtual DecltypeSpecifierAST *asDecltypeSpecifier() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ DecltypeSpecifierAST *clone(MemoryPool *pool) const override;
- virtual DecltypeSpecifierAST *clone(MemoryPool *pool) const;
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
+class CPLUSPLUS_EXPORT TypeConstraintAST: public AST
+{
+public:
+ NestedNameSpecifierListAST *nestedName = nullptr;
+ NameAST *conceptName = nullptr;
+ int lessToken = 0;
+ ExpressionListAST *templateArgs = nullptr;
+ int greaterToken = 0;
+
+ TypeConstraintAST *asTypeConstraint() override { return this; }
+
+ int firstToken() const override;
+ int lastToken() const override;
+
+ TypeConstraintAST *clone(MemoryPool *pool) const override;
+
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
+class CPLUSPLUS_EXPORT PlaceholderTypeSpecifierAST: public SpecifierAST
+{
+public:
+ TypeConstraintAST *typeConstraint = nullptr;
+ int declTypetoken = 0;
+ int lparenToken = 0;
+ int decltypeToken = 0;
+ int autoToken = 0;
+ int rparenToken = 0;
+
+public:
+ PlaceholderTypeSpecifierAST *asPlaceholderTypeSpecifier() override { return this; }
+
+ int firstToken() const override;
+ int lastToken() const override;
+
+ PlaceholderTypeSpecifierAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT DeclaratorAST: public AST
{
public:
- SpecifierListAST *attribute_list;
- PtrOperatorListAST *ptr_operator_list;
- CoreDeclaratorAST *core_declarator;
- PostfixDeclaratorListAST *postfix_declarator_list;
- SpecifierListAST *post_attribute_list;
- unsigned equal_token;
- ExpressionAST *initializer;
+ SpecifierListAST *attribute_list = nullptr;
+ PtrOperatorListAST *ptr_operator_list = nullptr;
+ CoreDeclaratorAST *core_declarator = nullptr;
+ PostfixDeclaratorListAST *postfix_declarator_list = nullptr;
+ SpecifierListAST *post_attribute_list = nullptr;
+ int equal_token = 0;
+ ExpressionAST *initializer = nullptr;
+ RequiresClauseAST *requiresClause = nullptr;
public:
- DeclaratorAST()
- : attribute_list(0)
- , ptr_operator_list(0)
- , core_declarator(0)
- , postfix_declarator_list(0)
- , post_attribute_list(0)
- , equal_token(0)
- , initializer(0)
- {}
-
- virtual DeclaratorAST *asDeclarator() { return this; }
+ DeclaratorAST *asDeclarator() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual DeclaratorAST *clone(MemoryPool *pool) const;
+ DeclaratorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT SimpleDeclarationAST: public DeclarationAST
{
public:
- unsigned qt_invokable_token;
- SpecifierListAST *decl_specifier_list;
- DeclaratorListAST *declarator_list;
- unsigned semicolon_token;
+ int qt_invokable_token = 0;
+ SpecifierListAST *decl_specifier_list = nullptr;
+ DeclaratorListAST *declarator_list = nullptr;
+ int semicolon_token = 0;
public:
- List<Symbol *> *symbols;
+ List<Symbol *> *symbols = nullptr;
public:
- SimpleDeclarationAST()
- : qt_invokable_token(0)
- , decl_specifier_list(0)
- , declarator_list(0)
- , semicolon_token(0)
- , symbols(0)
- {}
+ SimpleDeclarationAST *asSimpleDeclaration() override { return this; }
- virtual SimpleDeclarationAST *asSimpleDeclaration() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual SimpleDeclarationAST *clone(MemoryPool *pool) const;
+ SimpleDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT EmptyDeclarationAST: public DeclarationAST
{
public:
- unsigned semicolon_token;
+ int semicolon_token = 0;
public:
- EmptyDeclarationAST()
- : semicolon_token(0)
- {}
-
- virtual EmptyDeclarationAST *asEmptyDeclaration() { return this; }
+ EmptyDeclarationAST *asEmptyDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual EmptyDeclarationAST *clone(MemoryPool *pool) const;
+ EmptyDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT AccessDeclarationAST: public DeclarationAST
{
public:
- unsigned access_specifier_token;
- unsigned slots_token;
- unsigned colon_token;
+ int access_specifier_token = 0;
+ int slots_token = 0;
+ int colon_token = 0;
public:
- AccessDeclarationAST()
- : access_specifier_token(0)
- , slots_token(0)
- , colon_token(0)
- {}
+ AccessDeclarationAST *asAccessDeclaration() override { return this; }
- virtual AccessDeclarationAST *asAccessDeclaration() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual AccessDeclarationAST *clone(MemoryPool *pool) const;
+ AccessDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT QtObjectTagAST: public DeclarationAST
{
public:
- unsigned q_object_token;
+ int q_object_token = 0;
public:
- QtObjectTagAST()
- : q_object_token(0)
- {}
-
- virtual QtObjectTagAST *asQtObjectTag() { return this; }
+ QtObjectTagAST *asQtObjectTag() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual QtObjectTagAST *clone(MemoryPool *pool) const;
+ QtObjectTagAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT QtPrivateSlotAST: public DeclarationAST
{
public:
- unsigned q_private_slot_token;
- unsigned lparen_token;
- unsigned dptr_token;
- unsigned dptr_lparen_token;
- unsigned dptr_rparen_token;
- unsigned comma_token;
- SpecifierListAST *type_specifier_list;
- DeclaratorAST *declarator;
- unsigned rparen_token;
+ int q_private_slot_token = 0;
+ int lparen_token = 0;
+ int dptr_token = 0;
+ int dptr_lparen_token = 0;
+ int dptr_rparen_token = 0;
+ int comma_token = 0;
+ SpecifierListAST *type_specifier_list = nullptr;
+ DeclaratorAST *declarator = nullptr;
+ int rparen_token = 0;
public:
- QtPrivateSlotAST()
- : q_private_slot_token(0)
- , lparen_token(0)
- , dptr_token(0)
- , dptr_lparen_token(0)
- , dptr_rparen_token(0)
- , comma_token(0)
- , type_specifier_list(0)
- , declarator(0)
- , rparen_token(0)
- {}
-
- virtual QtPrivateSlotAST *asQtPrivateSlot() { return this; }
+ QtPrivateSlotAST *asQtPrivateSlot() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual QtPrivateSlotAST *clone(MemoryPool *pool) const;
+ QtPrivateSlotAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class QtPropertyDeclarationItemAST: public AST
{
public:
- unsigned item_name_token;
- ExpressionAST *expression;
+ int item_name_token = 0;
+ ExpressionAST *expression = nullptr;
public:
- QtPropertyDeclarationItemAST()
- : item_name_token(0)
- , expression(0)
- {}
+ QtPropertyDeclarationItemAST *asQtPropertyDeclarationItem() override { return this; }
- virtual QtPropertyDeclarationItemAST *asQtPropertyDeclarationItem() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual QtPropertyDeclarationItemAST *clone(MemoryPool *pool) const;
+ QtPropertyDeclarationItemAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT QtPropertyDeclarationAST: public DeclarationAST
{
public:
- unsigned property_specifier_token;
- unsigned lparen_token;
- ExpressionAST *expression; // for Q_PRIVATE_PROPERTY(expression, ...)
- unsigned comma_token;
- ExpressionAST *type_id;
- NameAST *property_name;
- QtPropertyDeclarationItemListAST *property_declaration_item_list;
- unsigned rparen_token;
+ int property_specifier_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *expression = nullptr; // for Q_PRIVATE_PROPERTY(expression, ...)
+ int comma_token = 0;
+ ExpressionAST *type_id = nullptr;
+ NameAST *property_name = nullptr;
+ QtPropertyDeclarationItemListAST *property_declaration_item_list = nullptr;
+ int rparen_token = 0;
public:
- QtPropertyDeclarationAST()
- : property_specifier_token(0)
- , lparen_token(0)
- , expression(0)
- , comma_token(0)
- , type_id(0)
- , property_name(0)
- , property_declaration_item_list(0)
- , rparen_token(0)
- {}
-
- virtual QtPropertyDeclarationAST *asQtPropertyDeclaration() { return this; }
+ QtPropertyDeclarationAST *asQtPropertyDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual QtPropertyDeclarationAST *clone(MemoryPool *pool) const;
+ QtPropertyDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT QtEnumDeclarationAST: public DeclarationAST
{
public:
- unsigned enum_specifier_token;
- unsigned lparen_token;
- NameListAST *enumerator_list;
- unsigned rparen_token;
+ int enum_specifier_token = 0;
+ int lparen_token = 0;
+ NameListAST *enumerator_list = nullptr;
+ int rparen_token = 0;
public:
- QtEnumDeclarationAST()
- : enum_specifier_token(0)
- , lparen_token(0)
- , enumerator_list(0)
- , rparen_token(0)
- {}
+ QtEnumDeclarationAST *asQtEnumDeclaration() override { return this; }
- virtual QtEnumDeclarationAST *asQtEnumDeclaration() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual QtEnumDeclarationAST *clone(MemoryPool *pool) const;
+ QtEnumDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT QtFlagsDeclarationAST: public DeclarationAST
{
public:
- unsigned flags_specifier_token;
- unsigned lparen_token;
- NameListAST *flag_enums_list;
- unsigned rparen_token;
+ int flags_specifier_token = 0;
+ int lparen_token = 0;
+ NameListAST *flag_enums_list = nullptr;
+ int rparen_token = 0;
public:
- QtFlagsDeclarationAST()
- : flags_specifier_token(0)
- , lparen_token(0)
- , flag_enums_list(0)
- , rparen_token(0)
- {}
-
- virtual QtFlagsDeclarationAST *asQtFlagsDeclaration() { return this; }
+ QtFlagsDeclarationAST *asQtFlagsDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual QtFlagsDeclarationAST *clone(MemoryPool *pool) const;
+ QtFlagsDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT QtInterfaceNameAST: public AST
{
public:
- NameAST *interface_name;
- NameListAST *constraint_list;
+ NameAST *interface_name = nullptr;
+ NameListAST *constraint_list = nullptr;
public:
- QtInterfaceNameAST()
- : interface_name(0)
- , constraint_list(0)
- {}
+ QtInterfaceNameAST *asQtInterfaceName() override { return this; }
- virtual QtInterfaceNameAST *asQtInterfaceName() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual QtInterfaceNameAST *clone(MemoryPool *pool) const;
+ QtInterfaceNameAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT QtInterfacesDeclarationAST: public DeclarationAST
{
public:
- unsigned interfaces_token;
- unsigned lparen_token;
- QtInterfaceNameListAST *interface_name_list;
- unsigned rparen_token;
+ int interfaces_token = 0;
+ int lparen_token = 0;
+ QtInterfaceNameListAST *interface_name_list = nullptr;
+ int rparen_token = 0;
public:
- QtInterfacesDeclarationAST()
- : interfaces_token(0)
- , lparen_token(0)
- , interface_name_list(0)
- , rparen_token(0)
- {}
-
- virtual QtInterfacesDeclarationAST *asQtInterfacesDeclaration() { return this; }
+ QtInterfacesDeclarationAST *asQtInterfacesDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual QtInterfacesDeclarationAST *clone(MemoryPool *pool) const;
+ QtInterfacesDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT AsmDefinitionAST: public DeclarationAST
{
public:
- unsigned asm_token;
- unsigned volatile_token;
- unsigned lparen_token;
+ int asm_token = 0;
+ int volatile_token = 0;
+ int lparen_token = 0;
// ### string literals
// ### asm operand list
- unsigned rparen_token;
- unsigned semicolon_token;
+ int rparen_token = 0;
+ int semicolon_token = 0;
public:
- AsmDefinitionAST()
- : asm_token(0)
- , volatile_token(0)
- , lparen_token(0)
- , rparen_token(0)
- , semicolon_token(0)
- {}
+ AsmDefinitionAST *asAsmDefinition() override { return this; }
- virtual AsmDefinitionAST *asAsmDefinition() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual AsmDefinitionAST *clone(MemoryPool *pool) const;
+ AsmDefinitionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT BaseSpecifierAST: public AST
{
public:
- unsigned virtual_token;
- unsigned access_specifier_token;
- NameAST *name;
- unsigned ellipsis_token;
+ int virtual_token = 0;
+ int access_specifier_token = 0;
+ NameAST *name = nullptr;
+ int ellipsis_token = 0;
public: // annotations
- BaseClass *symbol;
+ BaseClass *symbol = nullptr;
public:
- BaseSpecifierAST()
- : virtual_token(0)
- , access_specifier_token(0)
- , name(0)
- , ellipsis_token(0)
- , symbol(0)
- {}
-
- virtual BaseSpecifierAST *asBaseSpecifier() { return this; }
+ BaseSpecifierAST *asBaseSpecifier() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual BaseSpecifierAST *clone(MemoryPool *pool) const;
+ BaseSpecifierAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT IdExpressionAST: public ExpressionAST
{
public:
- NameAST *name;
+ NameAST *name = nullptr;
public:
- IdExpressionAST()
- : name(0)
- {}
+ IdExpressionAST *asIdExpression() override { return this; }
- virtual IdExpressionAST *asIdExpression() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual IdExpressionAST *clone(MemoryPool *pool) const;
+ IdExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT CompoundExpressionAST: public ExpressionAST
{
public:
- unsigned lparen_token;
- CompoundStatementAST *statement;
- unsigned rparen_token;
+ int lparen_token = 0;
+ CompoundStatementAST *statement = nullptr;
+ int rparen_token = 0;
public:
- CompoundExpressionAST()
- : lparen_token(0)
- , statement(0)
- , rparen_token(0)
- {}
-
- virtual CompoundExpressionAST *asCompoundExpression() { return this; }
+ CompoundExpressionAST *asCompoundExpression() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual CompoundExpressionAST *clone(MemoryPool *pool) const;
+ CompoundExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT CompoundLiteralAST: public ExpressionAST
{
public:
- unsigned lparen_token;
- ExpressionAST *type_id;
- unsigned rparen_token;
- ExpressionAST *initializer;
+ int lparen_token = 0;
+ ExpressionAST *type_id = nullptr;
+ int rparen_token = 0;
+ ExpressionAST *initializer = nullptr;
public:
- CompoundLiteralAST()
- : lparen_token(0)
- , type_id(0)
- , rparen_token(0)
- , initializer(0)
- {}
+ CompoundLiteralAST *asCompoundLiteral() override { return this; }
- virtual CompoundLiteralAST *asCompoundLiteral() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual CompoundLiteralAST *clone(MemoryPool *pool) const;
+ CompoundLiteralAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT QtMethodAST: public ExpressionAST
{
public:
- unsigned method_token;
- unsigned lparen_token;
- DeclaratorAST *declarator;
- unsigned rparen_token;
+ int method_token = 0;
+ int lparen_token = 0;
+ DeclaratorAST *declarator = nullptr;
+ int rparen_token = 0;
public:
- QtMethodAST()
- : method_token(0)
- , lparen_token(0)
- , declarator(0)
- , rparen_token(0)
- {}
-
- virtual QtMethodAST *asQtMethod() { return this; }
+ QtMethodAST *asQtMethod() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual QtMethodAST *clone(MemoryPool *pool) const;
+ QtMethodAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT QtMemberDeclarationAST: public StatementAST
{
public:
- unsigned q_token;
- unsigned lparen_token;
- ExpressionAST *type_id;
- unsigned rparen_token;
+ int q_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *type_id = nullptr;
+ int rparen_token = 0;
public:
- QtMemberDeclarationAST()
- : q_token(0)
- , lparen_token(0)
- , type_id(0)
- , rparen_token(0)
- {}
+ QtMemberDeclarationAST *asQtMemberDeclaration() override { return this; }
- virtual QtMemberDeclarationAST *asQtMemberDeclaration() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual QtMemberDeclarationAST *clone(MemoryPool *pool) const;
+ QtMemberDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT BinaryExpressionAST: public ExpressionAST
{
public:
- ExpressionAST *left_expression;
- unsigned binary_op_token;
- ExpressionAST *right_expression;
+ ExpressionAST *left_expression = nullptr;
+ int binary_op_token = 0;
+ ExpressionAST *right_expression = nullptr;
public:
- BinaryExpressionAST()
- : left_expression(0)
- , binary_op_token(0)
- , right_expression(0)
- {}
-
- virtual BinaryExpressionAST *asBinaryExpression() { return this; }
+ BinaryExpressionAST *asBinaryExpression() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual BinaryExpressionAST *clone(MemoryPool *pool) const;
+ BinaryExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT CastExpressionAST: public ExpressionAST
{
public:
- unsigned lparen_token;
- ExpressionAST *type_id;
- unsigned rparen_token;
- ExpressionAST *expression;
+ int lparen_token = 0;
+ ExpressionAST *type_id = nullptr;
+ int rparen_token = 0;
+ ExpressionAST *expression = nullptr;
public:
- CastExpressionAST()
- : lparen_token(0)
- , type_id(0)
- , rparen_token(0)
- , expression(0)
- {}
+ CastExpressionAST *asCastExpression() override { return this; }
- virtual CastExpressionAST *asCastExpression() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual CastExpressionAST *clone(MemoryPool *pool) const;
+ CastExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ClassSpecifierAST: public SpecifierAST
{
public:
- unsigned classkey_token;
- SpecifierListAST *attribute_list;
- NameAST *name;
- unsigned final_token;
- unsigned colon_token;
- BaseSpecifierListAST *base_clause_list;
- unsigned dot_dot_dot_token;
- unsigned lbrace_token;
- DeclarationListAST *member_specifier_list;
- unsigned rbrace_token;
+ int classkey_token = 0;
+ SpecifierListAST *attribute_list = nullptr;
+ NameAST *name = nullptr;
+ int final_token = 0;
+ int colon_token = 0;
+ BaseSpecifierListAST *base_clause_list = nullptr;
+ int dot_dot_dot_token = 0;
+ int lbrace_token = 0;
+ DeclarationListAST *member_specifier_list = nullptr;
+ int rbrace_token = 0;
public: // annotations
- Class *symbol;
+ Class *symbol = nullptr;
public:
- ClassSpecifierAST()
- : classkey_token(0)
- , attribute_list(0)
- , name(0)
- , final_token(0)
- , colon_token(0)
- , base_clause_list(0)
- , dot_dot_dot_token(0)
- , lbrace_token(0)
- , member_specifier_list(0)
- , rbrace_token(0)
- , symbol(0)
- {}
-
- virtual ClassSpecifierAST *asClassSpecifier() { return this; }
+ ClassSpecifierAST *asClassSpecifier() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ClassSpecifierAST *clone(MemoryPool *pool) const;
+ ClassSpecifierAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT CaseStatementAST: public StatementAST
{
public:
- unsigned case_token;
- ExpressionAST *expression;
- unsigned colon_token;
- StatementAST *statement;
+ int case_token = 0;
+ ExpressionAST *expression = nullptr;
+ int colon_token = 0;
+ StatementAST *statement = nullptr;
public:
- CaseStatementAST()
- : case_token(0)
- , expression(0)
- , colon_token(0)
- , statement(0)
- {}
+ CaseStatementAST *asCaseStatement() override { return this; }
- virtual CaseStatementAST *asCaseStatement() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual CaseStatementAST *clone(MemoryPool *pool) const;
+ CaseStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT CompoundStatementAST: public StatementAST
{
public:
- unsigned lbrace_token;
- StatementListAST *statement_list;
- unsigned rbrace_token;
+ int lbrace_token = 0;
+ StatementListAST *statement_list = nullptr;
+ int rbrace_token = 0;
public: // annotations
- Block *symbol;
+ Block *symbol = nullptr;
public:
- CompoundStatementAST()
- : lbrace_token(0)
- , statement_list(0)
- , rbrace_token(0)
- , symbol(0)
- {}
-
- virtual CompoundStatementAST *asCompoundStatement() { return this; }
+ CompoundStatementAST *asCompoundStatement() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual CompoundStatementAST *clone(MemoryPool *pool) const;
+ CompoundStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ConditionAST: public ExpressionAST
{
public:
- SpecifierListAST *type_specifier_list;
- DeclaratorAST *declarator;
+ SpecifierListAST *type_specifier_list = nullptr;
+ DeclaratorAST *declarator = nullptr;
public:
- ConditionAST()
- : type_specifier_list(0)
- , declarator(0)
- {}
+ ConditionAST *asCondition() override { return this; }
- virtual ConditionAST *asCondition() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ConditionAST *clone(MemoryPool *pool) const;
+ ConditionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ConditionalExpressionAST: public ExpressionAST
{
public:
- ExpressionAST *condition;
- unsigned question_token;
- ExpressionAST *left_expression;
- unsigned colon_token;
- ExpressionAST *right_expression;
+ ExpressionAST *condition = nullptr;
+ int question_token = 0;
+ ExpressionAST *left_expression = nullptr;
+ int colon_token = 0;
+ ExpressionAST *right_expression = nullptr;
public:
- ConditionalExpressionAST()
- : condition(0)
- , question_token(0)
- , left_expression(0)
- , colon_token(0)
- , right_expression(0)
- {}
-
- virtual ConditionalExpressionAST *asConditionalExpression() { return this; }
+ ConditionalExpressionAST *asConditionalExpression() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ConditionalExpressionAST *clone(MemoryPool *pool) const;
+ ConditionalExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT CppCastExpressionAST: public ExpressionAST
{
public:
- unsigned cast_token;
- unsigned less_token;
- ExpressionAST *type_id;
- unsigned greater_token;
- unsigned lparen_token;
- ExpressionAST *expression;
- unsigned rparen_token;
+ int cast_token = 0;
+ int less_token = 0;
+ ExpressionAST *type_id = nullptr;
+ int greater_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rparen_token = 0;
public:
- CppCastExpressionAST()
- : cast_token(0)
- , less_token(0)
- , type_id(0)
- , greater_token(0)
- , lparen_token(0)
- , expression(0)
- , rparen_token(0)
- {}
+ CppCastExpressionAST *asCppCastExpression() override { return this; }
- virtual CppCastExpressionAST *asCppCastExpression() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual CppCastExpressionAST *clone(MemoryPool *pool) const;
+ CppCastExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT CtorInitializerAST: public AST
{
public:
- unsigned colon_token;
- MemInitializerListAST *member_initializer_list;
- unsigned dot_dot_dot_token;
+ int colon_token = 0;
+ MemInitializerListAST *member_initializer_list = nullptr;
+ int dot_dot_dot_token = 0;
public:
- CtorInitializerAST()
- : colon_token(0)
- , member_initializer_list(0)
- , dot_dot_dot_token(0)
- {}
-
- virtual CtorInitializerAST *asCtorInitializer() { return this; }
+ CtorInitializerAST *asCtorInitializer() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual CtorInitializerAST *clone(MemoryPool *pool) const;
+ CtorInitializerAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT DeclarationStatementAST: public StatementAST
{
public:
- DeclarationAST *declaration;
+ DeclarationAST *declaration = nullptr;
public:
- DeclarationStatementAST()
- : declaration(0)
- {}
+ DeclarationStatementAST *asDeclarationStatement() override { return this; }
- virtual DeclarationStatementAST *asDeclarationStatement() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual DeclarationStatementAST *clone(MemoryPool *pool) const;
+ DeclarationStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT DeclaratorIdAST: public CoreDeclaratorAST
{
public:
- unsigned dot_dot_dot_token;
- NameAST *name;
+ int dot_dot_dot_token = 0;
+ NameAST *name = nullptr;
public:
- DeclaratorIdAST()
- : dot_dot_dot_token(0)
- , name(0)
- {}
+ DeclaratorIdAST *asDeclaratorId() override { return this; }
+
+ int firstToken() const override;
+ int lastToken() const override;
- virtual DeclaratorIdAST *asDeclaratorId() { return this; }
+ DeclaratorIdAST *clone(MemoryPool *pool) const override;
+
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+class CPLUSPLUS_EXPORT DecompositionDeclaratorAST: public CoreDeclaratorAST
+{
+public:
+ NameListAST *identifiers = nullptr;
- virtual DeclaratorIdAST *clone(MemoryPool *pool) const;
+public:
+ DecompositionDeclaratorAST *asDecompositionDeclarator() override { return this; }
+
+ int firstToken() const override { return identifiers->firstToken(); }
+ int lastToken() const override { return identifiers->lastToken(); }
+
+ DecompositionDeclaratorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NestedDeclaratorAST: public CoreDeclaratorAST
{
public:
- unsigned lparen_token;
- DeclaratorAST *declarator;
- unsigned rparen_token;
+ int lparen_token = 0;
+ DeclaratorAST *declarator = nullptr;
+ int rparen_token = 0;
public:
- NestedDeclaratorAST()
- : lparen_token(0)
- , declarator(0)
- , rparen_token(0)
- {}
+ NestedDeclaratorAST *asNestedDeclarator() override { return this; }
- virtual NestedDeclaratorAST *asNestedDeclarator() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual NestedDeclaratorAST *clone(MemoryPool *pool) const;
+ NestedDeclaratorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT FunctionDeclaratorAST: public PostfixDeclaratorAST
{
public:
- unsigned lparen_token;
- ParameterDeclarationClauseAST *parameter_declaration_clause;
- unsigned rparen_token;
- SpecifierListAST *cv_qualifier_list;
- unsigned ref_qualifier_token;
- ExceptionSpecificationAST *exception_specification;
- TrailingReturnTypeAST *trailing_return_type;
+ SpecifierListAST *decl_specifier_list = nullptr;
+ int lparen_token = 0;
+ ParameterDeclarationClauseAST *parameter_declaration_clause = nullptr;
+ int rparen_token = 0;
+ SpecifierListAST *cv_qualifier_list = nullptr;
+ int ref_qualifier_token = 0;
+ ExceptionSpecificationAST *exception_specification = nullptr;
+ TrailingReturnTypeAST *trailing_return_type = nullptr;
// Some FunctionDeclarators can also be interpreted as an initializer, like for 'A b(c);'
- ExpressionAST *as_cpp_initializer;
+ ExpressionAST *as_cpp_initializer = nullptr;
public: // annotations
- Function *symbol;
+ Function *symbol = nullptr;
public:
- FunctionDeclaratorAST()
- : lparen_token(0)
- , parameter_declaration_clause(0)
- , rparen_token(0)
- , cv_qualifier_list(0)
- , ref_qualifier_token(0)
- , exception_specification(0)
- , trailing_return_type(0)
- , as_cpp_initializer(0)
- , symbol(0)
- {}
-
- virtual FunctionDeclaratorAST *asFunctionDeclarator() { return this; }
+ FunctionDeclaratorAST *asFunctionDeclarator() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual FunctionDeclaratorAST *clone(MemoryPool *pool) const;
+ FunctionDeclaratorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ArrayDeclaratorAST: public PostfixDeclaratorAST
{
public:
- unsigned lbracket_token;
- ExpressionAST *expression;
- unsigned rbracket_token;
+ int lbracket_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rbracket_token = 0;
public:
- ArrayDeclaratorAST()
- : lbracket_token(0)
- , expression(0)
- , rbracket_token(0)
- {}
+ ArrayDeclaratorAST *asArrayDeclarator() override { return this; }
- virtual ArrayDeclaratorAST *asArrayDeclarator() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ArrayDeclaratorAST *clone(MemoryPool *pool) const;
+ ArrayDeclaratorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT DeleteExpressionAST: public ExpressionAST
{
public:
- unsigned scope_token;
- unsigned delete_token;
- unsigned lbracket_token;
- unsigned rbracket_token;
- ExpressionAST *expression;
+ int scope_token = 0;
+ int delete_token = 0;
+ int lbracket_token = 0;
+ int rbracket_token = 0;
+ ExpressionAST *expression = nullptr;
public:
- DeleteExpressionAST()
- : scope_token(0)
- , delete_token(0)
- , lbracket_token(0)
- , rbracket_token(0)
- , expression(0)
- {}
-
- virtual DeleteExpressionAST *asDeleteExpression() { return this; }
+ DeleteExpressionAST *asDeleteExpression() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual DeleteExpressionAST *clone(MemoryPool *pool) const;
+ DeleteExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT DoStatementAST: public StatementAST
{
public:
- unsigned do_token;
- StatementAST *statement;
- unsigned while_token;
- unsigned lparen_token;
- ExpressionAST *expression;
- unsigned rparen_token;
- unsigned semicolon_token;
+ int do_token = 0;
+ StatementAST *statement = nullptr;
+ int while_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rparen_token = 0;
+ int semicolon_token = 0;
public:
- DoStatementAST()
- : do_token(0)
- , statement(0)
- , while_token(0)
- , lparen_token(0)
- , expression(0)
- , rparen_token(0)
- , semicolon_token(0)
- {}
+ DoStatementAST *asDoStatement() override { return this; }
- virtual DoStatementAST *asDoStatement() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual DoStatementAST *clone(MemoryPool *pool) const;
+ DoStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NamedTypeSpecifierAST: public SpecifierAST
{
public:
- NameAST *name;
+ NameAST *name = nullptr;
public:
- NamedTypeSpecifierAST()
- : name(0)
- {}
-
- virtual NamedTypeSpecifierAST *asNamedTypeSpecifier() { return this; }
+ NamedTypeSpecifierAST *asNamedTypeSpecifier() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual NamedTypeSpecifierAST *clone(MemoryPool *pool) const;
+ NamedTypeSpecifierAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ElaboratedTypeSpecifierAST: public SpecifierAST
{
public:
- unsigned classkey_token;
- SpecifierListAST *attribute_list;
- NameAST *name;
+ int classkey_token = 0;
+ SpecifierListAST *attribute_list = nullptr;
+ NameAST *name = nullptr;
public:
- ElaboratedTypeSpecifierAST()
- : classkey_token(0)
- , attribute_list(0)
- , name(0)
- {}
+ ElaboratedTypeSpecifierAST *asElaboratedTypeSpecifier() override { return this; }
- virtual ElaboratedTypeSpecifierAST *asElaboratedTypeSpecifier() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ElaboratedTypeSpecifierAST *clone(MemoryPool *pool) const;
+ ElaboratedTypeSpecifierAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT EnumSpecifierAST: public SpecifierAST
{
public:
- unsigned enum_token;
- unsigned key_token; // struct, class or 0
- NameAST *name;
- unsigned colon_token; // can be 0 if there is no enum-base
- SpecifierListAST *type_specifier_list; // ditto
- unsigned lbrace_token;
- EnumeratorListAST *enumerator_list;
- unsigned stray_comma_token;
- unsigned rbrace_token;
+ int enum_token = 0;
+ int key_token = 0; // struct, class or 0
+ NameAST *name = nullptr;
+ int colon_token = 0; // can be 0 if there is no enum-base
+ SpecifierListAST *type_specifier_list = nullptr; // ditto
+ int lbrace_token = 0;
+ EnumeratorListAST *enumerator_list = nullptr;
+ int stray_comma_token = 0;
+ int rbrace_token = 0;
public: // annotations
- Enum *symbol;
+ Enum *symbol = nullptr;
public:
- EnumSpecifierAST()
- : enum_token(0)
- , key_token(0)
- , name(0)
- , colon_token(0)
- , type_specifier_list(0)
- , lbrace_token(0)
- , enumerator_list(0)
- , stray_comma_token(0)
- , rbrace_token(0)
- , symbol(0)
- {}
-
- virtual EnumSpecifierAST *asEnumSpecifier() { return this; }
+ EnumSpecifierAST *asEnumSpecifier() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual EnumSpecifierAST *clone(MemoryPool *pool) const;
+ EnumSpecifierAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT EnumeratorAST: public AST
{
public:
- unsigned identifier_token;
- unsigned equal_token;
- ExpressionAST *expression;
+ int identifier_token = 0;
+ int equal_token = 0;
+ ExpressionAST *expression = nullptr;
public:
- EnumeratorAST()
- : identifier_token(0)
- , equal_token(0)
- , expression(0)
- {}
+ EnumeratorAST *asEnumerator() override { return this; }
- virtual EnumeratorAST *asEnumerator() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual EnumeratorAST *clone(MemoryPool *pool) const;
+ EnumeratorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ExceptionDeclarationAST: public DeclarationAST
{
public:
- SpecifierListAST *type_specifier_list;
- DeclaratorAST *declarator;
- unsigned dot_dot_dot_token;
+ SpecifierListAST *type_specifier_list = nullptr;
+ DeclaratorAST *declarator = nullptr;
+ int dot_dot_dot_token = 0;
public:
- ExceptionDeclarationAST()
- : type_specifier_list(0)
- , declarator(0)
- , dot_dot_dot_token(0)
- {}
-
- virtual ExceptionDeclarationAST *asExceptionDeclaration() { return this; }
+ ExceptionDeclarationAST *asExceptionDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ExceptionDeclarationAST *clone(MemoryPool *pool) const;
+ ExceptionDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ExceptionSpecificationAST: public AST
{
public:
- ExceptionSpecificationAST()
- {}
+ ExceptionSpecificationAST *asExceptionSpecification() override { return this; }
- virtual ExceptionSpecificationAST *asExceptionSpecification() { return this; }
-
- virtual ExceptionSpecificationAST *clone(MemoryPool *pool) const = 0;
+ ExceptionSpecificationAST *clone(MemoryPool *pool) const override = 0;
};
class CPLUSPLUS_EXPORT DynamicExceptionSpecificationAST: public ExceptionSpecificationAST
{
public:
- unsigned throw_token;
- unsigned lparen_token;
- unsigned dot_dot_dot_token;
- ExpressionListAST *type_id_list;
- unsigned rparen_token;
+ int throw_token = 0;
+ int lparen_token = 0;
+ int dot_dot_dot_token = 0;
+ ExpressionListAST *type_id_list = nullptr;
+ int rparen_token = 0;
public:
- DynamicExceptionSpecificationAST()
- : throw_token(0)
- , lparen_token(0)
- , dot_dot_dot_token(0)
- , type_id_list(0)
- , rparen_token(0)
- {}
-
- virtual DynamicExceptionSpecificationAST *asDynamicExceptionSpecification() { return this; }
+ DynamicExceptionSpecificationAST *asDynamicExceptionSpecification() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual DynamicExceptionSpecificationAST *clone(MemoryPool *pool) const;
+ DynamicExceptionSpecificationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NoExceptSpecificationAST: public ExceptionSpecificationAST
{
public:
- unsigned noexcept_token;
- unsigned lparen_token;
- ExpressionAST *expression;
- unsigned rparen_token;
+ int noexcept_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rparen_token = 0;
public:
- NoExceptSpecificationAST()
- : noexcept_token(0)
- , lparen_token(0)
- , expression(0)
- , rparen_token(0)
- {}
+ NoExceptSpecificationAST *asNoExceptSpecification() override { return this; }
- virtual NoExceptSpecificationAST *asNoExceptSpecification() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual NoExceptSpecificationAST *clone(MemoryPool *pool) const;
+ NoExceptSpecificationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ExpressionOrDeclarationStatementAST: public StatementAST
{
public:
- ExpressionStatementAST *expression;
- DeclarationStatementAST *declaration;
+ ExpressionStatementAST *expression = nullptr;
+ DeclarationStatementAST *declaration = nullptr;
public:
- ExpressionOrDeclarationStatementAST()
- : expression(0)
- , declaration(0)
- {}
-
- virtual ExpressionOrDeclarationStatementAST *asExpressionOrDeclarationStatement() { return this; }
+ ExpressionOrDeclarationStatementAST *asExpressionOrDeclarationStatement() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ExpressionOrDeclarationStatementAST *clone(MemoryPool *pool) const;
+ ExpressionOrDeclarationStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ExpressionStatementAST: public StatementAST
{
public:
- ExpressionAST *expression;
- unsigned semicolon_token;
+ ExpressionAST *expression = nullptr;
+ int semicolon_token = 0;
public:
- ExpressionStatementAST()
- : expression(0)
- , semicolon_token(0)
- {}
+ ExpressionStatementAST *asExpressionStatement() override { return this; }
- virtual ExpressionStatementAST *asExpressionStatement() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ExpressionStatementAST *clone(MemoryPool *pool) const;
+ ExpressionStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT FunctionDefinitionAST: public DeclarationAST
{
public:
- unsigned qt_invokable_token;
- SpecifierListAST *decl_specifier_list;
- DeclaratorAST *declarator;
- CtorInitializerAST *ctor_initializer;
- StatementAST *function_body;
+ int qt_invokable_token = 0;
+ SpecifierListAST *decl_specifier_list = nullptr;
+ DeclaratorAST *declarator = nullptr;
+ CtorInitializerAST *ctor_initializer = nullptr;
+ StatementAST *function_body = nullptr;
public: // annotations
- Function *symbol;
+ Function *symbol = nullptr;
public:
- FunctionDefinitionAST()
- : qt_invokable_token(0)
- , decl_specifier_list(0)
- , declarator(0)
- , ctor_initializer(0)
- , function_body(0)
- , symbol(0)
- {}
-
- virtual FunctionDefinitionAST *asFunctionDefinition() { return this; }
+ FunctionDefinitionAST *asFunctionDefinition() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual FunctionDefinitionAST *clone(MemoryPool *pool) const;
+ FunctionDefinitionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ForeachStatementAST: public StatementAST
{
public:
- unsigned foreach_token;
- unsigned lparen_token;
+ int foreach_token = 0;
+ int lparen_token = 0;
// declaration
- SpecifierListAST *type_specifier_list;
- DeclaratorAST *declarator;
+ SpecifierListAST *type_specifier_list = nullptr;
+ DeclaratorAST *declarator = nullptr;
// or an expression
- ExpressionAST *initializer;
- unsigned comma_token;
- ExpressionAST *expression;
- unsigned rparen_token;
- StatementAST *statement;
+ ExpressionAST *initializer = nullptr;
+ int comma_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rparen_token = 0;
+ StatementAST *statement = nullptr;
public: // annotations
- Block *symbol;
+ Block *symbol = nullptr;
public:
- ForeachStatementAST()
- : foreach_token(0)
- , lparen_token(0)
- , type_specifier_list(0)
- , declarator(0)
- , initializer(0)
- , comma_token(0)
- , expression(0)
- , rparen_token(0)
- , statement(0)
- , symbol(0)
- {}
+ ForeachStatementAST *asForeachStatement() override { return this; }
- virtual ForeachStatementAST *asForeachStatement() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ForeachStatementAST *clone(MemoryPool *pool) const;
+ ForeachStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT RangeBasedForStatementAST : public StatementAST
{
public:
- unsigned for_token;
- unsigned lparen_token;
+ int for_token = 0;
+ int lparen_token = 0;
// declaration
- SpecifierListAST *type_specifier_list;
- DeclaratorAST *declarator;
+ SpecifierListAST *type_specifier_list = nullptr;
+ DeclaratorAST *declarator = nullptr;
// or an expression
- unsigned colon_token;
- ExpressionAST *expression;
- unsigned rparen_token;
- StatementAST *statement;
+ int colon_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rparen_token = 0;
+ StatementAST *statement = nullptr;
public: // annotations
- Block *symbol;
+ Block *symbol = nullptr;
public:
- RangeBasedForStatementAST()
- : for_token(0)
- , lparen_token(0)
- , type_specifier_list(0)
- , declarator(0)
- , colon_token(0)
- , expression(0)
- , rparen_token(0)
- , statement(0)
- , symbol(0)
- {}
-
- virtual RangeBasedForStatementAST *asRangeBasedForStatement() { return this; }
+ RangeBasedForStatementAST *asRangeBasedForStatement() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual RangeBasedForStatementAST *clone(MemoryPool *pool) const;
+ RangeBasedForStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ForStatementAST: public StatementAST
{
public:
- unsigned for_token;
- unsigned lparen_token;
- StatementAST *initializer;
- ExpressionAST *condition;
- unsigned semicolon_token;
- ExpressionAST *expression;
- unsigned rparen_token;
- StatementAST *statement;
+ int for_token = 0;
+ int lparen_token = 0;
+ StatementAST *initializer = nullptr;
+ ExpressionAST *condition = nullptr;
+ int semicolon_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rparen_token = 0;
+ StatementAST *statement = nullptr;
public: // annotations
- Block *symbol;
+ Block *symbol = nullptr;
public:
- ForStatementAST()
- : for_token(0)
- , lparen_token(0)
- , initializer(0)
- , condition(0)
- , semicolon_token(0)
- , expression(0)
- , rparen_token(0)
- , statement(0)
- , symbol(0)
- {}
+ ForStatementAST *asForStatement() override { return this; }
- virtual ForStatementAST *asForStatement() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ForStatementAST *clone(MemoryPool *pool) const;
+ ForStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT IfStatementAST: public StatementAST
{
public:
- unsigned if_token;
- unsigned lparen_token;
- ExpressionAST *condition;
- unsigned rparen_token;
- StatementAST *statement;
- unsigned else_token;
- StatementAST *else_statement;
+ int if_token = 0;
+ int constexpr_token = 0;
+ int lparen_token = 0;
+ StatementAST *initStmt = nullptr;
+ ExpressionAST *condition = nullptr;
+ int rparen_token = 0;
+ StatementAST *statement = nullptr;
+ int else_token = 0;
+ StatementAST *else_statement = nullptr;
public: // annotations
- Block *symbol;
+ Block *symbol = nullptr;
public:
- IfStatementAST()
- : if_token(0)
- , lparen_token(0)
- , condition(0)
- , rparen_token(0)
- , statement(0)
- , else_token(0)
- , else_statement(0)
- , symbol(0)
- {}
-
- virtual IfStatementAST *asIfStatement() { return this; }
+ IfStatementAST *asIfStatement() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual IfStatementAST *clone(MemoryPool *pool) const;
+ IfStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ArrayInitializerAST: public ExpressionAST
{
public:
- unsigned lbrace_token;
- ExpressionListAST *expression_list;
- unsigned rbrace_token;
+ int lbrace_token = 0;
+ ExpressionListAST *expression_list = nullptr;
+ int rbrace_token = 0;
public:
- ArrayInitializerAST()
- : lbrace_token(0)
- , expression_list(0)
- , rbrace_token(0)
- {}
+ ArrayInitializerAST *asArrayInitializer() override { return this; }
- virtual ArrayInitializerAST *asArrayInitializer() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ArrayInitializerAST *clone(MemoryPool *pool) const;
+ ArrayInitializerAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT LabeledStatementAST: public StatementAST
{
public:
- unsigned label_token;
- unsigned colon_token;
- StatementAST *statement;
+ int label_token = 0;
+ int colon_token = 0;
+ StatementAST *statement = nullptr;
public:
- LabeledStatementAST()
- : label_token(0)
- , colon_token(0)
- , statement(0)
- {}
-
- virtual LabeledStatementAST *asLabeledStatement() { return this; }
+ LabeledStatementAST *asLabeledStatement() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual LabeledStatementAST *clone(MemoryPool *pool) const;
+ LabeledStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT LinkageBodyAST: public DeclarationAST
{
public:
- unsigned lbrace_token;
- DeclarationListAST *declaration_list;
- unsigned rbrace_token;
+ int lbrace_token = 0;
+ DeclarationListAST *declaration_list = nullptr;
+ int rbrace_token = 0;
public:
- LinkageBodyAST()
- : lbrace_token(0)
- , declaration_list(0)
- , rbrace_token(0)
- {}
+ LinkageBodyAST *asLinkageBody() override { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual LinkageBodyAST *asLinkageBody() { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual LinkageBodyAST *clone(MemoryPool *pool) const;
+ LinkageBodyAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT LinkageSpecificationAST: public DeclarationAST
{
public:
- unsigned extern_token;
- unsigned extern_type_token;
- DeclarationAST *declaration;
+ int extern_token = 0;
+ int extern_type_token = 0;
+ DeclarationAST *declaration = nullptr;
public:
- LinkageSpecificationAST()
- : extern_token(0)
- , extern_type_token(0)
- , declaration(0)
- {}
-
- virtual LinkageSpecificationAST *asLinkageSpecification() { return this; }
+ LinkageSpecificationAST *asLinkageSpecification() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual LinkageSpecificationAST *clone(MemoryPool *pool) const;
+ LinkageSpecificationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT MemInitializerAST: public AST
{
public:
- NameAST *name;
+ NameAST *name = nullptr;
// either a BracedInitializerAST or a ExpressionListParenAST
- ExpressionAST *expression;
+ ExpressionAST *expression = nullptr;
public:
- MemInitializerAST()
- : name(0)
- , expression(0)
- {}
+ MemInitializerAST *asMemInitializer() override { return this; }
- virtual MemInitializerAST *asMemInitializer() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual MemInitializerAST *clone(MemoryPool *pool) const;
+ MemInitializerAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NestedNameSpecifierAST: public AST
{
public:
- NameAST *class_or_namespace_name;
- unsigned scope_token;
+ NameAST *class_or_namespace_name = nullptr;
+ int scope_token = 0;
public:
- NestedNameSpecifierAST()
- : class_or_namespace_name(0)
- , scope_token(0)
- {}
-
- virtual NestedNameSpecifierAST *asNestedNameSpecifier() { return this; }
+ NestedNameSpecifierAST *asNestedNameSpecifier() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual NestedNameSpecifierAST *clone(MemoryPool *pool) const;
+ NestedNameSpecifierAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT QualifiedNameAST: public NameAST
{
public:
- unsigned global_scope_token;
- NestedNameSpecifierListAST *nested_name_specifier_list;
- NameAST *unqualified_name;
+ int global_scope_token = 0;
+ NestedNameSpecifierListAST *nested_name_specifier_list = nullptr;
+ NameAST *unqualified_name = nullptr;
public:
- QualifiedNameAST()
- : global_scope_token(0)
- , nested_name_specifier_list(0)
- , unqualified_name(0)
- {}
+ QualifiedNameAST *asQualifiedName() override { return this; }
- virtual QualifiedNameAST *asQualifiedName() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual QualifiedNameAST *clone(MemoryPool *pool) const;
+ QualifiedNameAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT OperatorFunctionIdAST: public NameAST
{
public:
- unsigned operator_token;
- OperatorAST *op;
+ int operator_token = 0;
+ OperatorAST *op = nullptr;
public:
- OperatorFunctionIdAST()
- : operator_token(0)
- , op(0)
- {}
-
- virtual OperatorFunctionIdAST *asOperatorFunctionId() { return this; }
+ OperatorFunctionIdAST *asOperatorFunctionId() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual OperatorFunctionIdAST *clone(MemoryPool *pool) const;
+ OperatorFunctionIdAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ConversionFunctionIdAST: public NameAST
{
public:
- unsigned operator_token;
- SpecifierListAST *type_specifier_list;
- PtrOperatorListAST *ptr_operator_list;
+ int operator_token = 0;
+ SpecifierListAST *type_specifier_list = nullptr;
+ PtrOperatorListAST *ptr_operator_list = nullptr;
public:
- ConversionFunctionIdAST()
- : operator_token(0)
- , type_specifier_list(0)
- , ptr_operator_list(0)
- {}
+ ConversionFunctionIdAST *asConversionFunctionId() override { return this; }
- virtual ConversionFunctionIdAST *asConversionFunctionId() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ConversionFunctionIdAST *clone(MemoryPool *pool) const;
+ ConversionFunctionIdAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT AnonymousNameAST: public NameAST
{
public:
- unsigned class_token;
-public:
- AnonymousNameAST()
- : class_token(0)
- {}
+ int class_token = 0;
- virtual AnonymousNameAST *asAnonymousName() { return this; }
- virtual unsigned firstToken() const { return 0; }
- virtual unsigned lastToken() const { return 0; }
+public:
+ AnonymousNameAST *asAnonymousName() override { return this; }
+ int firstToken() const override { return 0; }
+ int lastToken() const override { return 0; }
- virtual AnonymousNameAST *clone(MemoryPool *pool) const;
+ AnonymousNameAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT SimpleNameAST: public NameAST
{
public:
- unsigned identifier_token;
+ int identifier_token = 0;
public:
- SimpleNameAST()
- : identifier_token(0)
- {}
-
- virtual SimpleNameAST *asSimpleName() { return this; }
+ SimpleNameAST *asSimpleName() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual SimpleNameAST *clone(MemoryPool *pool) const;
+ SimpleNameAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT DestructorNameAST: public NameAST
{
public:
- unsigned tilde_token;
- NameAST *unqualified_name;
+ int tilde_token = 0;
+ NameAST *unqualified_name = nullptr;
public:
- DestructorNameAST()
- : tilde_token(0)
- , unqualified_name(0)
- {}
-
- virtual DestructorNameAST *asDestructorName() { return this; }
+ DestructorNameAST *asDestructorName() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual DestructorNameAST *clone(MemoryPool *pool) const;
+ DestructorNameAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT TemplateIdAST: public NameAST
{
public:
- unsigned template_token;
- unsigned identifier_token;
- unsigned less_token;
- ExpressionListAST *template_argument_list;
- unsigned greater_token;
+ int template_token = 0;
+ int identifier_token = 0;
+ int less_token = 0;
+ ExpressionListAST *template_argument_list = nullptr;
+ int greater_token = 0;
public:
- TemplateIdAST()
- : template_token(0)
- , identifier_token(0)
- , less_token(0)
- , template_argument_list(0)
- , greater_token(0)
- {}
+ TemplateIdAST *asTemplateId() override { return this; }
- virtual TemplateIdAST *asTemplateId() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual TemplateIdAST *clone(MemoryPool *pool) const;
+ TemplateIdAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NamespaceAST: public DeclarationAST
{
public:
- unsigned inline_token;
- unsigned namespace_token;
- unsigned identifier_token;
- SpecifierListAST *attribute_list;
- DeclarationAST *linkage_body;
+ int inline_token = 0;
+ int namespace_token = 0;
+ int identifier_token = 0;
+ SpecifierListAST *attribute_list = nullptr;
+ DeclarationAST *linkage_body = nullptr;
public: // annotations
- Namespace *symbol;
+ Namespace *symbol = nullptr;
public:
- NamespaceAST()
- : inline_token(0)
- , namespace_token(0)
- , identifier_token(0)
- , attribute_list(0)
- , linkage_body(0)
- , symbol(0)
- {}
-
- virtual NamespaceAST *asNamespace() { return this; }
+ NamespaceAST *asNamespace() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual NamespaceAST *clone(MemoryPool *pool) const;
+ NamespaceAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NamespaceAliasDefinitionAST: public DeclarationAST
{
public:
- unsigned namespace_token;
- unsigned namespace_name_token;
- unsigned equal_token;
- NameAST *name;
- unsigned semicolon_token;
+ int namespace_token = 0;
+ int namespace_name_token = 0;
+ int equal_token = 0;
+ NameAST *name = nullptr;
+ int semicolon_token = 0;
public:
- NamespaceAliasDefinitionAST()
- : namespace_token(0)
- , namespace_name_token(0)
- , equal_token(0)
- , name(0)
- , semicolon_token(0)
- {}
+ NamespaceAliasDefinitionAST *asNamespaceAliasDefinition() override { return this; }
- virtual NamespaceAliasDefinitionAST *asNamespaceAliasDefinition() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual NamespaceAliasDefinitionAST *clone(MemoryPool *pool) const;
+ NamespaceAliasDefinitionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT AliasDeclarationAST: public DeclarationAST
{
public:
- unsigned using_token;
- NameAST *name;
- unsigned equal_token;
- TypeIdAST *typeId;
- unsigned semicolon_token;
+ int using_token = 0;
+ NameAST *name = nullptr;
+ int equal_token = 0;
+ TypeIdAST *typeId = nullptr;
+ int semicolon_token = 0;
public: // annotations
- Declaration *symbol;
+ Declaration *symbol = nullptr;
public:
- AliasDeclarationAST()
- : using_token(0)
- , name(0)
- , equal_token(0)
- , typeId(0)
- , semicolon_token(0)
- , symbol(0)
- {}
-
- virtual AliasDeclarationAST *asAliasDeclaration() { return this; }
+ AliasDeclarationAST *asAliasDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual AliasDeclarationAST *clone(MemoryPool *pool) const;
+ AliasDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ExpressionListParenAST: public ExpressionAST
{
public:
- unsigned lparen_token;
- ExpressionListAST *expression_list;
- unsigned rparen_token;
+ int lparen_token = 0;
+ ExpressionListAST *expression_list = nullptr;
+ int rparen_token = 0;
public:
- ExpressionListParenAST()
- : lparen_token(0)
- , expression_list(0)
- , rparen_token(0)
- {}
+ ExpressionListParenAST *asExpressionListParen() override { return this; }
- virtual ExpressionListParenAST *asExpressionListParen() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ExpressionListParenAST *clone(MemoryPool *pool) const;
+ ExpressionListParenAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NewArrayDeclaratorAST: public AST
{
public:
- unsigned lbracket_token;
- ExpressionAST *expression;
- unsigned rbracket_token;
+ int lbracket_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rbracket_token = 0;
public:
- NewArrayDeclaratorAST()
- : lbracket_token(0)
- , expression(0)
- , rbracket_token(0)
- {}
-
- virtual NewArrayDeclaratorAST *asNewArrayDeclarator() { return this; }
+ NewArrayDeclaratorAST *asNewArrayDeclarator() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual NewArrayDeclaratorAST *clone(MemoryPool *pool) const;
+ NewArrayDeclaratorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NewExpressionAST: public ExpressionAST
{
public:
- unsigned scope_token;
- unsigned new_token;
- ExpressionListParenAST *new_placement;
+ int scope_token = 0;
+ int new_token = 0;
+ ExpressionListParenAST *new_placement = nullptr;
- unsigned lparen_token;
- ExpressionAST *type_id;
- unsigned rparen_token;
+ int lparen_token = 0;
+ ExpressionAST *type_id = nullptr;
+ int rparen_token = 0;
- NewTypeIdAST *new_type_id;
+ NewTypeIdAST *new_type_id = nullptr;
- ExpressionAST *new_initializer; // either ExpressionListParenAST or BracedInitializerAST
+ ExpressionAST *new_initializer = nullptr; // either ExpressionListParenAST or BracedInitializerAST
public:
- NewExpressionAST()
- : scope_token(0)
- , new_token(0)
- , new_placement(0)
- , lparen_token(0)
- , type_id(0)
- , rparen_token(0)
- , new_type_id(0)
- , new_initializer(0)
- {}
+ NewExpressionAST *asNewExpression() override { return this; }
- virtual NewExpressionAST *asNewExpression() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual NewExpressionAST *clone(MemoryPool *pool) const;
+ NewExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NewTypeIdAST: public AST
{
public:
- SpecifierListAST *type_specifier_list;
- PtrOperatorListAST *ptr_operator_list;
- NewArrayDeclaratorListAST *new_array_declarator_list;
+ SpecifierListAST *type_specifier_list = nullptr;
+ PtrOperatorListAST *ptr_operator_list = nullptr;
+ NewArrayDeclaratorListAST *new_array_declarator_list = nullptr;
public:
- NewTypeIdAST()
- : type_specifier_list(0)
- , ptr_operator_list(0)
- , new_array_declarator_list(0)
- {}
-
- virtual NewTypeIdAST *asNewTypeId() { return this; }
+ NewTypeIdAST *asNewTypeId() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual NewTypeIdAST *clone(MemoryPool *pool) const;
+ NewTypeIdAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT OperatorAST: public AST
{
public:
- unsigned op_token;
- unsigned open_token;
- unsigned close_token;
+ int op_token = 0;
+ int open_token = 0;
+ int close_token = 0;
public:
- OperatorAST()
- : op_token(0)
- , open_token(0)
- , close_token(0)
- {}
+ OperatorAST *asOperator() override{ return this; }
- virtual OperatorAST *asOperator() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual OperatorAST *clone(MemoryPool *pool) const;
+ OperatorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ParameterDeclarationAST: public DeclarationAST
{
public:
- SpecifierListAST *type_specifier_list;
- DeclaratorAST *declarator;
- unsigned equal_token;
- ExpressionAST *expression;
+ SpecifierListAST *type_specifier_list = nullptr;
+ DeclaratorAST *declarator = nullptr;
+ int equal_token = 0;
+ ExpressionAST *expression = nullptr;
public: // annotations
- Argument *symbol;
+ Argument *symbol = nullptr;
public:
- ParameterDeclarationAST()
- : type_specifier_list(0)
- , declarator(0)
- , equal_token(0)
- , expression(0)
- , symbol(0)
- {}
-
- virtual ParameterDeclarationAST *asParameterDeclaration() { return this; }
+ ParameterDeclarationAST *asParameterDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ParameterDeclarationAST *clone(MemoryPool *pool) const;
+ ParameterDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ParameterDeclarationClauseAST: public AST
{
public:
- ParameterDeclarationListAST *parameter_declaration_list;
- unsigned dot_dot_dot_token;
+ ParameterDeclarationListAST *parameter_declaration_list = nullptr;
+ int dot_dot_dot_token = 0;
public:
- ParameterDeclarationClauseAST()
- : parameter_declaration_list(0)
- , dot_dot_dot_token(0)
- {}
+ ParameterDeclarationClauseAST *asParameterDeclarationClause() override { return this; }
- virtual ParameterDeclarationClauseAST *asParameterDeclarationClause() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ParameterDeclarationClauseAST *clone(MemoryPool *pool) const;
+ ParameterDeclarationClauseAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT CallAST: public PostfixAST
{
public:
- ExpressionAST *base_expression;
- unsigned lparen_token;
- ExpressionListAST *expression_list;
- unsigned rparen_token;
+ ExpressionAST *base_expression = nullptr;
+ int lparen_token = 0;
+ ExpressionListAST *expression_list = nullptr;
+ int rparen_token = 0;
public:
- CallAST()
- : base_expression(0)
- , lparen_token(0)
- , expression_list(0)
- , rparen_token(0)
- {}
-
- virtual CallAST *asCall() { return this; }
+ CallAST *asCall() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual CallAST *clone(MemoryPool *pool) const;
+ CallAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ArrayAccessAST: public PostfixAST
{
public:
- ExpressionAST *base_expression;
- unsigned lbracket_token;
- ExpressionAST *expression;
- unsigned rbracket_token;
+ ExpressionAST *base_expression = nullptr;
+ int lbracket_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rbracket_token = 0;
public:
- ArrayAccessAST()
- : base_expression(0)
- , lbracket_token(0)
- , expression(0)
- , rbracket_token(0)
- {}
+ ArrayAccessAST *asArrayAccess() override { return this; }
- virtual ArrayAccessAST *asArrayAccess() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ArrayAccessAST *clone(MemoryPool *pool) const;
+ ArrayAccessAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT PostIncrDecrAST: public PostfixAST
{
public:
- ExpressionAST *base_expression;
- unsigned incr_decr_token;
+ ExpressionAST *base_expression = nullptr;
+ int incr_decr_token = 0;
public:
- PostIncrDecrAST()
- : base_expression(0)
- , incr_decr_token(0)
- {}
-
- virtual PostIncrDecrAST *asPostIncrDecr() { return this; }
+ PostIncrDecrAST *asPostIncrDecr() override{ return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual PostIncrDecrAST *clone(MemoryPool *pool) const;
+ PostIncrDecrAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT MemberAccessAST: public PostfixAST
{
public:
- ExpressionAST *base_expression;
- unsigned access_token;
- unsigned template_token;
- NameAST *member_name;
+ ExpressionAST *base_expression = nullptr;
+ int access_token = 0;
+ int template_token = 0;
+ NameAST *member_name = nullptr;
public:
- MemberAccessAST()
- : base_expression(0)
- , access_token(0)
- , template_token(0)
- , member_name(0)
- {}
+ MemberAccessAST *asMemberAccess() override { return this; }
- virtual MemberAccessAST *asMemberAccess() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual MemberAccessAST *clone(MemoryPool *pool) const;
+ MemberAccessAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT TypeidExpressionAST: public ExpressionAST
{
public:
- unsigned typeid_token;
- unsigned lparen_token;
- ExpressionAST *expression;
- unsigned rparen_token;
+ int typeid_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rparen_token = 0;
public:
- TypeidExpressionAST()
- : typeid_token(0)
- , lparen_token(0)
- , expression(0)
- , rparen_token(0)
- {}
-
- virtual TypeidExpressionAST *asTypeidExpression() { return this; }
+ TypeidExpressionAST *asTypeidExpression() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual TypeidExpressionAST *clone(MemoryPool *pool) const;
+ TypeidExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT TypenameCallExpressionAST: public ExpressionAST
{
public:
- unsigned typename_token;
- NameAST *name;
- ExpressionAST *expression; // either ExpressionListParenAST or BracedInitializerAST
+ int typename_token = 0;
+ NameAST *name = nullptr;
+ ExpressionAST *expression = nullptr; // either ExpressionListParenAST or BracedInitializerAST
public:
- TypenameCallExpressionAST()
- : typename_token(0)
- , name(0)
- , expression(0)
- {}
+ TypenameCallExpressionAST *asTypenameCallExpression() override { return this; }
- virtual TypenameCallExpressionAST *asTypenameCallExpression() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual TypenameCallExpressionAST *clone(MemoryPool *pool) const;
+ TypenameCallExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT TypeConstructorCallAST: public ExpressionAST
{
public:
- SpecifierListAST *type_specifier_list;
- ExpressionAST *expression; // either ExpressionListParenAST or BracedInitializerAST
+ SpecifierListAST *type_specifier_list = nullptr;
+ ExpressionAST *expression = nullptr; // either ExpressionListParenAST or BracedInitializerAST
public:
- TypeConstructorCallAST()
- : type_specifier_list(0)
- , expression(0)
- {}
-
- virtual TypeConstructorCallAST *asTypeConstructorCall() { return this; }
+ TypeConstructorCallAST *asTypeConstructorCall() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual TypeConstructorCallAST *clone(MemoryPool *pool) const;
+ TypeConstructorCallAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT PointerToMemberAST: public PtrOperatorAST
{
public:
- unsigned global_scope_token;
- NestedNameSpecifierListAST *nested_name_specifier_list;
- unsigned star_token;
- SpecifierListAST *cv_qualifier_list;
- unsigned ref_qualifier_token;
+ int global_scope_token = 0;
+ NestedNameSpecifierListAST *nested_name_specifier_list = nullptr;
+ int star_token = 0;
+ SpecifierListAST *cv_qualifier_list = nullptr;
+ int ref_qualifier_token = 0;
public:
- PointerToMemberAST()
- : global_scope_token(0)
- , nested_name_specifier_list(0)
- , star_token(0)
- , cv_qualifier_list(0)
- , ref_qualifier_token(0)
- {}
+ PointerToMemberAST *asPointerToMember() override { return this; }
- virtual PointerToMemberAST *asPointerToMember() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual PointerToMemberAST *clone(MemoryPool *pool) const;
+ PointerToMemberAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT PointerAST: public PtrOperatorAST
{
public:
- unsigned star_token;
- SpecifierListAST *cv_qualifier_list;
+ int star_token = 0;
+ SpecifierListAST *cv_qualifier_list = nullptr;
public:
- PointerAST()
- : star_token(0)
- , cv_qualifier_list(0)
- {}
-
- virtual PointerAST *asPointer() { return this; }
+ PointerAST *asPointer() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual PointerAST *clone(MemoryPool *pool) const;
+ PointerAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ReferenceAST: public PtrOperatorAST
{
public:
- unsigned reference_token;
+ int reference_token = 0;
public:
- ReferenceAST()
- : reference_token(0)
- {}
+ ReferenceAST *asReference() override { return this; }
- virtual ReferenceAST *asReference() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ReferenceAST *clone(MemoryPool *pool) const;
+ ReferenceAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT BreakStatementAST: public StatementAST
{
public:
- unsigned break_token;
- unsigned semicolon_token;
+ int break_token = 0;
+ int semicolon_token = 0;
public:
- BreakStatementAST()
- : break_token(0)
- , semicolon_token(0)
- {}
-
- virtual BreakStatementAST *asBreakStatement() { return this; }
+ BreakStatementAST *asBreakStatement() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual BreakStatementAST *clone(MemoryPool *pool) const;
+ BreakStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ContinueStatementAST: public StatementAST
{
public:
- unsigned continue_token;
- unsigned semicolon_token;
+ int continue_token = 0;
+ int semicolon_token = 0;
public:
- ContinueStatementAST()
- : continue_token(0)
- , semicolon_token(0)
- {}
+ ContinueStatementAST *asContinueStatement() override { return this; }
- virtual ContinueStatementAST *asContinueStatement() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ContinueStatementAST *clone(MemoryPool *pool) const;
+ ContinueStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT GotoStatementAST: public StatementAST
{
public:
- unsigned goto_token;
- unsigned identifier_token;
- unsigned semicolon_token;
+ int goto_token = 0;
+ int identifier_token = 0;
+ int semicolon_token = 0;
public:
- GotoStatementAST()
- : goto_token(0)
- , identifier_token(0)
- , semicolon_token(0)
- {}
-
- virtual GotoStatementAST *asGotoStatement() { return this; }
+ GotoStatementAST *asGotoStatement() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual GotoStatementAST *clone(MemoryPool *pool) const;
+ GotoStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ReturnStatementAST: public StatementAST
{
public:
- unsigned return_token;
- ExpressionAST *expression;
- unsigned semicolon_token;
+ int return_token = 0;
+ ExpressionAST *expression = nullptr;
+ int semicolon_token = 0;
public:
- ReturnStatementAST()
- : return_token(0)
- , expression(0)
- , semicolon_token(0)
- {}
+ ReturnStatementAST *asReturnStatement() override { return this; }
- virtual ReturnStatementAST *asReturnStatement() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ReturnStatementAST *clone(MemoryPool *pool) const;
+ ReturnStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT SizeofExpressionAST: public ExpressionAST
{
public:
- unsigned sizeof_token;
- unsigned dot_dot_dot_token;
- unsigned lparen_token;
- ExpressionAST *expression;
- unsigned rparen_token;
+ int sizeof_token = 0;
+ int dot_dot_dot_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rparen_token = 0;
public:
- SizeofExpressionAST()
- : sizeof_token(0)
- , dot_dot_dot_token(0)
- , lparen_token(0)
- , expression(0)
- , rparen_token(0)
- {}
-
- virtual SizeofExpressionAST *asSizeofExpression() { return this; }
+ SizeofExpressionAST *asSizeofExpression() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual SizeofExpressionAST *clone(MemoryPool *pool) const;
+ SizeofExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT AlignofExpressionAST: public ExpressionAST
{
public:
- unsigned alignof_token;
- unsigned lparen_token;
- TypeIdAST *typeId;
- unsigned rparen_token;
+ int alignof_token = 0;
+ int lparen_token = 0;
+ TypeIdAST *typeId = nullptr;
+ int rparen_token = 0;
public:
- AlignofExpressionAST()
- : alignof_token(0)
- , lparen_token(0)
- , typeId(0)
- , rparen_token(0)
- {}
+ AlignofExpressionAST *asAlignofExpression() override { return this; }
- virtual AlignofExpressionAST *asAlignofExpression() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual AlignofExpressionAST *clone(MemoryPool *pool) const;
+ AlignofExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT PointerLiteralAST: public ExpressionAST
{
public:
- unsigned literal_token;
+ int literal_token = 0;
public:
- PointerLiteralAST()
- : literal_token(0)
- {}
-
- virtual PointerLiteralAST *asPointerLiteral() { return this; }
+ PointerLiteralAST *asPointerLiteral() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual PointerLiteralAST *clone(MemoryPool *pool) const;
+ PointerLiteralAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NumericLiteralAST: public ExpressionAST
{
public:
- unsigned literal_token;
+ int literal_token = 0;
public:
- NumericLiteralAST()
- : literal_token(0)
- {}
+ NumericLiteralAST *asNumericLiteral() override { return this; }
- virtual NumericLiteralAST *asNumericLiteral() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual NumericLiteralAST *clone(MemoryPool *pool) const;
+ NumericLiteralAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT BoolLiteralAST: public ExpressionAST
{
public:
- unsigned literal_token;
+ int literal_token = 0;
public:
- BoolLiteralAST()
- : literal_token(0)
- {}
-
- virtual BoolLiteralAST *asBoolLiteral() { return this; }
+ BoolLiteralAST *asBoolLiteral() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual BoolLiteralAST *clone(MemoryPool *pool) const;
+ BoolLiteralAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ThisExpressionAST: public ExpressionAST
{
public:
- unsigned this_token;
+ int this_token = 0;
public:
- ThisExpressionAST()
- : this_token(0)
- {}
+ ThisExpressionAST *asThisExpression() override { return this; }
- virtual ThisExpressionAST *asThisExpression() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ThisExpressionAST *clone(MemoryPool *pool) const;
+ ThisExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT NestedExpressionAST: public ExpressionAST
{
public:
- unsigned lparen_token;
- ExpressionAST *expression;
- unsigned rparen_token;
+ int lparen_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rparen_token = 0;
public:
- NestedExpressionAST()
- : lparen_token(0)
- , expression(0)
- , rparen_token(0)
- {}
-
- virtual NestedExpressionAST *asNestedExpression() { return this; }
+ NestedExpressionAST *asNestedExpression() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual NestedExpressionAST *clone(MemoryPool *pool) const;
+ NestedExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT StaticAssertDeclarationAST: public DeclarationAST
{
public:
- unsigned static_assert_token;
- unsigned lparen_token;
- ExpressionAST *expression;
- unsigned comma_token;
- ExpressionAST *string_literal;
- unsigned rparen_token;
- unsigned semicolon_token;
+ int static_assert_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *expression = nullptr;
+ int comma_token = 0;
+ ExpressionAST *string_literal = nullptr;
+ int rparen_token = 0;
+ int semicolon_token = 0;
public:
- StaticAssertDeclarationAST()
- : static_assert_token(0)
- , lparen_token(0)
- , expression(0)
- , comma_token(0)
- , string_literal(0)
- , rparen_token(0)
- , semicolon_token(0)
- {}
-
- virtual StaticAssertDeclarationAST *asStaticAssertDeclaration() { return this; }
+ StaticAssertDeclarationAST *asStaticAssertDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual StaticAssertDeclarationAST *clone(MemoryPool *pool) const;
+ StaticAssertDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT StringLiteralAST: public ExpressionAST
{
public:
- unsigned literal_token;
- StringLiteralAST *next;
+ int literal_token = 0;
+ StringLiteralAST *next = nullptr;
public:
- StringLiteralAST()
- : literal_token(0)
- , next(0)
- {}
+ StringLiteralAST *asStringLiteral() override { return this; }
- virtual StringLiteralAST *asStringLiteral() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual StringLiteralAST *clone(MemoryPool *pool) const;
+ StringLiteralAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT SwitchStatementAST: public StatementAST
{
public:
- unsigned switch_token;
- unsigned lparen_token;
- ExpressionAST *condition;
- unsigned rparen_token;
- StatementAST *statement;
+ int switch_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *condition = nullptr;
+ int rparen_token = 0;
+ StatementAST *statement = nullptr;
public: // annotations
- Block *symbol;
+ Block *symbol = nullptr;
public:
- SwitchStatementAST()
- : switch_token(0)
- , lparen_token(0)
- , condition(0)
- , rparen_token(0)
- , statement(0)
- , symbol(0)
- {}
-
- virtual SwitchStatementAST *asSwitchStatement() { return this; }
+ SwitchStatementAST *asSwitchStatement() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual SwitchStatementAST *clone(MemoryPool *pool) const;
+ SwitchStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT TemplateDeclarationAST: public DeclarationAST
{
public:
- unsigned export_token;
- unsigned template_token;
- unsigned less_token;
- DeclarationListAST *template_parameter_list;
- unsigned greater_token;
- DeclarationAST *declaration;
+ int export_token = 0;
+ int template_token = 0;
+ int less_token = 0;
+ DeclarationListAST *template_parameter_list = nullptr;
+ int greater_token = 0;
+ RequiresClauseAST *requiresClause = nullptr;
+ DeclarationAST *declaration = nullptr;
public: // annotations
- Template *symbol;
+ Template *symbol = nullptr;
public:
- TemplateDeclarationAST()
- : export_token(0)
- , template_token(0)
- , less_token(0)
- , template_parameter_list(0)
- , greater_token(0)
- , declaration(0)
- , symbol(0)
- {}
+ TemplateDeclarationAST *asTemplateDeclaration() override { return this; }
- virtual TemplateDeclarationAST *asTemplateDeclaration() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ TemplateDeclarationAST *clone(MemoryPool *pool) const override;
- virtual TemplateDeclarationAST *clone(MemoryPool *pool) const;
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
+class CPLUSPLUS_EXPORT ConceptDeclarationAST: public DeclarationAST
+{
+public:
+ int concept_token = 0;
+ NameAST *name = nullptr;
+ SpecifierListAST *attributes = nullptr;
+ int equals_token = 0;
+ ExpressionAST *constraint = nullptr;
+ int semicolon_token = 0;
+
+public:
+ ConceptDeclarationAST *asConceptDeclaration() override { return this; }
+
+ int firstToken() const override { return concept_token; }
+ int lastToken() const override { return semicolon_token + 1; }
+
+ ConceptDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ThrowExpressionAST: public ExpressionAST
{
public:
- unsigned throw_token;
- ExpressionAST *expression;
+ int throw_token = 0;
+ ExpressionAST *expression = nullptr;
public:
- ThrowExpressionAST()
- : throw_token(0)
- , expression(0)
- {}
+ ThrowExpressionAST *asThrowExpression() override { return this; }
- virtual ThrowExpressionAST *asThrowExpression() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ ThrowExpressionAST *clone(MemoryPool *pool) const override;
- virtual ThrowExpressionAST *clone(MemoryPool *pool) const;
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
+class CPLUSPLUS_EXPORT YieldExpressionAST: public ExpressionAST
+{
+public:
+ int yield_token = 0;
+ ExpressionAST *expression = nullptr;
+
+public:
+ YieldExpressionAST *asYieldExpression() override { return this; }
+
+ int firstToken() const override { return yield_token; }
+ int lastToken() const override { return expression->lastToken(); }
+
+ YieldExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
-class CPLUSPLUS_EXPORT NoExceptOperatorExpressionAST: public ExpressionAST
+class CPLUSPLUS_EXPORT AwaitExpressionAST: public ExpressionAST
{
public:
- unsigned noexcept_token;
- ExpressionAST *expression;
+ int await_token = 0;
+ ExpressionAST *castExpression = nullptr;
public:
- NoExceptOperatorExpressionAST()
- : noexcept_token(0)
- , expression(0)
- {}
+ AwaitExpressionAST *asAwaitExpression() override { return this; }
+
+ int firstToken() const override { return await_token; }
+ int lastToken() const override { return castExpression->lastToken(); }
- virtual NoExceptOperatorExpressionAST *asNoExceptOperatorExpression() { return this; }
+ AwaitExpressionAST *clone(MemoryPool *pool) const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
+class CPLUSPLUS_EXPORT NoExceptOperatorExpressionAST: public ExpressionAST
+{
+public:
+ int noexcept_token = 0;
+ ExpressionAST *expression = nullptr;
+
+public:
+ NoExceptOperatorExpressionAST *asNoExceptOperatorExpression() override { return this; }
- virtual NoExceptOperatorExpressionAST *clone(MemoryPool *pool) const;
+ int firstToken() const override;
+ int lastToken() const override;
+
+ NoExceptOperatorExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT TranslationUnitAST: public AST
{
public:
- DeclarationListAST *declaration_list;
+ DeclarationListAST *declaration_list = nullptr;
public:
- TranslationUnitAST()
- : declaration_list(0)
- {}
-
- virtual TranslationUnitAST *asTranslationUnit() { return this; }
+ TranslationUnitAST *asTranslationUnit() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual TranslationUnitAST *clone(MemoryPool *pool) const;
+ TranslationUnitAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT TryBlockStatementAST: public StatementAST
{
public:
- unsigned try_token;
- StatementAST *statement;
- CatchClauseListAST *catch_clause_list;
+ int try_token = 0;
+ StatementAST *statement = nullptr;
+ CatchClauseListAST *catch_clause_list = nullptr;
public:
- TryBlockStatementAST()
- : try_token(0)
- , statement(0)
- , catch_clause_list(0)
- {}
-
- virtual TryBlockStatementAST *asTryBlockStatement() { return this; }
+ TryBlockStatementAST *asTryBlockStatement() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual TryBlockStatementAST *clone(MemoryPool *pool) const;
+ TryBlockStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT CatchClauseAST: public StatementAST
{
public:
- unsigned catch_token;
- unsigned lparen_token;
- ExceptionDeclarationAST *exception_declaration;
- unsigned rparen_token;
- StatementAST *statement;
+ int catch_token = 0;
+ int lparen_token = 0;
+ ExceptionDeclarationAST *exception_declaration = nullptr;
+ int rparen_token = 0;
+ StatementAST *statement = nullptr;
public: // annotations
- Block *symbol;
+ Block *symbol = nullptr;
public:
- CatchClauseAST()
- : catch_token(0)
- , lparen_token(0)
- , exception_declaration(0)
- , rparen_token(0)
- , statement(0)
- , symbol(0)
- {}
+ CatchClauseAST *asCatchClause() override { return this; }
- virtual CatchClauseAST *asCatchClause() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual CatchClauseAST *clone(MemoryPool *pool) const;
+ CatchClauseAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT TypeIdAST: public ExpressionAST
{
public:
- SpecifierListAST *type_specifier_list;
- DeclaratorAST *declarator;
+ SpecifierListAST *type_specifier_list = nullptr;
+ DeclaratorAST *declarator = nullptr;
public:
- TypeIdAST()
- : type_specifier_list(0)
- , declarator(0)
- {}
-
- virtual TypeIdAST *asTypeId() { return this; }
+ TypeIdAST *asTypeId() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual TypeIdAST *clone(MemoryPool *pool) const;
+ TypeIdAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT TypenameTypeParameterAST: public DeclarationAST
{
public:
- unsigned classkey_token;
- unsigned dot_dot_dot_token;
- NameAST *name;
- unsigned equal_token;
- ExpressionAST *type_id;
+ int classkey_token = 0;
+ int dot_dot_dot_token = 0;
+ NameAST *name = nullptr;
+ int equal_token = 0;
+ ExpressionAST *type_id = nullptr;
public: // annotations
- TypenameArgument *symbol;
+ TypenameArgument *symbol = nullptr;
public:
- TypenameTypeParameterAST()
- : classkey_token(0)
- , dot_dot_dot_token(0)
- , name(0)
- , equal_token(0)
- , type_id(0)
- , symbol(0)
- {}
+ TypenameTypeParameterAST *asTypenameTypeParameter() override { return this; }
- virtual TypenameTypeParameterAST *asTypenameTypeParameter() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual TypenameTypeParameterAST *clone(MemoryPool *pool) const;
+ TypenameTypeParameterAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT TemplateTypeParameterAST: public DeclarationAST
{
public:
- unsigned template_token;
- unsigned less_token;
- DeclarationListAST *template_parameter_list;
- unsigned greater_token;
- unsigned class_token;
- unsigned dot_dot_dot_token;
- NameAST *name;
- unsigned equal_token;
- ExpressionAST *type_id;
+ int template_token = 0;
+ TypeConstraintAST *typeConstraint = nullptr;
+ int less_token = 0;
+ DeclarationListAST *template_parameter_list = nullptr;
+ int greater_token = 0;
+ int class_token = 0;
+ int dot_dot_dot_token = 0;
+ NameAST *name = nullptr;
+ int equal_token = 0;
+ ExpressionAST *type_id = nullptr;
public:
- TypenameArgument *symbol;
+ TypenameArgument *symbol = nullptr;
public:
- TemplateTypeParameterAST()
- : template_token(0)
- , less_token(0)
- , template_parameter_list(0)
- , greater_token(0)
- , class_token(0)
- , dot_dot_dot_token(0)
- , name(0)
- , equal_token(0)
- , type_id(0)
- , symbol(0)
- {}
-
- virtual TemplateTypeParameterAST *asTemplateTypeParameter() { return this; }
+ TemplateTypeParameterAST *asTemplateTypeParameter() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual TemplateTypeParameterAST *clone(MemoryPool *pool) const;
+ TemplateTypeParameterAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT UnaryExpressionAST: public ExpressionAST
{
public:
- unsigned unary_op_token;
- ExpressionAST *expression;
+ int unary_op_token = 0;
+ ExpressionAST *expression = nullptr;
public:
- UnaryExpressionAST()
- : unary_op_token(0)
- , expression(0)
- {}
+ UnaryExpressionAST *asUnaryExpression() override { return this; }
- virtual UnaryExpressionAST *asUnaryExpression() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ UnaryExpressionAST *clone(MemoryPool *pool) const override;
- virtual UnaryExpressionAST *clone(MemoryPool *pool) const;
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
+class CPLUSPLUS_EXPORT RequiresExpressionAST: public ExpressionAST
+{
+public:
+ int requires_token = 0;
+ int lparen_token = 0;
+ ParameterDeclarationClauseAST *parameters = nullptr;
+ int rparen_token = 0;
+ int lbrace_token = 0;
+ int rbrace_token = 0;
+
+public:
+ RequiresExpressionAST *asRequiresExpression() override { return this; }
+
+ int firstToken() const override { return requires_token; }
+ int lastToken() const override { return rbrace_token + 1; }
+
+ RequiresExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
+};
+
+class CPLUSPLUS_EXPORT RequiresClauseAST: public AST
+{
+public:
+ int requires_token = 0;
+ ExpressionAST *constraint = nullptr;
+
+public:
+ RequiresClauseAST *asRequiresClause() override { return this; }
+
+ int firstToken() const override { return requires_token; }
+ int lastToken() const override { return constraint->lastToken(); }
+
+ RequiresClauseAST *clone(MemoryPool *pool) const override;
+
+protected:
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT UsingAST: public DeclarationAST
{
public:
- unsigned using_token;
- unsigned typename_token;
- NameAST *name;
- unsigned semicolon_token;
+ int using_token = 0;
+ int typename_token = 0;
+ NameAST *name = nullptr;
+ int semicolon_token = 0;
public: // annotations
- UsingDeclaration *symbol;
+ UsingDeclaration *symbol = nullptr;
public:
- UsingAST()
- : using_token(0)
- , typename_token(0)
- , name(0)
- , semicolon_token(0)
- , symbol(0)
- {}
-
- virtual UsingAST *asUsing() { return this; }
+ UsingAST *asUsing() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual UsingAST *clone(MemoryPool *pool) const;
+ UsingAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT UsingDirectiveAST: public DeclarationAST
{
public:
- unsigned using_token;
- unsigned namespace_token;
- NameAST *name;
- unsigned semicolon_token;
+ int using_token = 0;
+ int namespace_token = 0;
+ NameAST *name = nullptr;
+ int semicolon_token = 0;
public:
- UsingNamespaceDirective *symbol;
+ UsingNamespaceDirective *symbol = nullptr;
public:
- UsingDirectiveAST()
- : using_token(0)
- , namespace_token(0)
- , name(0)
- , semicolon_token(0)
- , symbol(0)
- {}
+ UsingDirectiveAST *asUsingDirective() override { return this; }
- virtual UsingDirectiveAST *asUsingDirective() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual UsingDirectiveAST *clone(MemoryPool *pool) const;
+ UsingDirectiveAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT WhileStatementAST: public StatementAST
{
public:
- unsigned while_token;
- unsigned lparen_token;
- ExpressionAST *condition;
- unsigned rparen_token;
- StatementAST *statement;
+ int while_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *condition = nullptr;
+ int rparen_token = 0;
+ StatementAST *statement = nullptr;
public: // annotations
- Block *symbol;
+ Block *symbol = nullptr;
public:
- WhileStatementAST()
- : while_token(0)
- , lparen_token(0)
- , condition(0)
- , rparen_token(0)
- , statement(0)
- , symbol(0)
- {}
-
- virtual WhileStatementAST *asWhileStatement() { return this; }
+ WhileStatementAST *asWhileStatement() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual WhileStatementAST *clone(MemoryPool *pool) const;
+ WhileStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCClassForwardDeclarationAST: public DeclarationAST
{
public:
- SpecifierListAST *attribute_list;
- unsigned class_token;
- NameListAST *identifier_list;
- unsigned semicolon_token;
+ SpecifierListAST *attribute_list = nullptr;
+ int class_token = 0;
+ NameListAST *identifier_list = nullptr;
+ int semicolon_token = 0;
public: // annotations
- List<ObjCForwardClassDeclaration *> *symbols;
+ List<ObjCForwardClassDeclaration *> *symbols = nullptr;
public:
- ObjCClassForwardDeclarationAST()
- : attribute_list(0)
- , class_token(0)
- , identifier_list(0)
- , semicolon_token(0)
- , symbols(0)
- {}
+ ObjCClassForwardDeclarationAST *asObjCClassForwardDeclaration() override { return this; }
- virtual ObjCClassForwardDeclarationAST *asObjCClassForwardDeclaration() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCClassForwardDeclarationAST *clone(MemoryPool *pool) const;
+ ObjCClassForwardDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCClassDeclarationAST: public DeclarationAST
{
public:
- SpecifierListAST *attribute_list;
- unsigned interface_token;
- unsigned implementation_token;
- NameAST *class_name;
- unsigned lparen_token;
- NameAST *category_name;
- unsigned rparen_token;
- unsigned colon_token;
- NameAST *superclass;
- ObjCProtocolRefsAST *protocol_refs;
- ObjCInstanceVariablesDeclarationAST *inst_vars_decl;
- DeclarationListAST *member_declaration_list;
- unsigned end_token;
+ SpecifierListAST *attribute_list = nullptr;
+ int interface_token = 0;
+ int implementation_token = 0;
+ NameAST *class_name = nullptr;
+ int lparen_token = 0;
+ NameAST *category_name = nullptr;
+ int rparen_token = 0;
+ int colon_token = 0;
+ NameAST *superclass = nullptr;
+ ObjCProtocolRefsAST *protocol_refs = nullptr;
+ ObjCInstanceVariablesDeclarationAST *inst_vars_decl = nullptr;
+ DeclarationListAST *member_declaration_list = nullptr;
+ int end_token = 0;
public: // annotations
- ObjCClass *symbol;
+ ObjCClass *symbol = nullptr;
public:
- ObjCClassDeclarationAST()
- : attribute_list(0)
- , interface_token(0)
- , implementation_token(0)
- , class_name(0)
- , lparen_token(0)
- , category_name(0)
- , rparen_token(0)
- , colon_token(0)
- , superclass(0)
- , protocol_refs(0)
- , inst_vars_decl(0)
- , member_declaration_list(0)
- , end_token(0)
- , symbol(0)
- {}
-
- virtual ObjCClassDeclarationAST *asObjCClassDeclaration() { return this; }
+ ObjCClassDeclarationAST *asObjCClassDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCClassDeclarationAST *clone(MemoryPool *pool) const;
+ ObjCClassDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCProtocolForwardDeclarationAST: public DeclarationAST
{
public:
- SpecifierListAST *attribute_list;
- unsigned protocol_token;
- NameListAST *identifier_list;
- unsigned semicolon_token;
+ SpecifierListAST *attribute_list = nullptr;
+ int protocol_token = 0;
+ NameListAST *identifier_list = nullptr;
+ int semicolon_token = 0;
public: // annotations
- List<ObjCForwardProtocolDeclaration *> *symbols;
+ List<ObjCForwardProtocolDeclaration *> *symbols = nullptr;
public:
- ObjCProtocolForwardDeclarationAST()
- : attribute_list(0)
- , protocol_token(0)
- , identifier_list(0)
- , semicolon_token(0)
- , symbols(0)
- {}
+ ObjCProtocolForwardDeclarationAST *asObjCProtocolForwardDeclaration() override { return this; }
- virtual ObjCProtocolForwardDeclarationAST *asObjCProtocolForwardDeclaration() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCProtocolForwardDeclarationAST *clone(MemoryPool *pool) const;
+ ObjCProtocolForwardDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCProtocolDeclarationAST: public DeclarationAST
{
public:
- SpecifierListAST *attribute_list;
- unsigned protocol_token;
- NameAST *name;
- ObjCProtocolRefsAST *protocol_refs;
- DeclarationListAST *member_declaration_list;
- unsigned end_token;
+ SpecifierListAST *attribute_list = nullptr;
+ int protocol_token = 0;
+ NameAST *name = nullptr;
+ ObjCProtocolRefsAST *protocol_refs = nullptr;
+ DeclarationListAST *member_declaration_list = nullptr;
+ int end_token = 0;
public: // annotations
- ObjCProtocol *symbol;
+ ObjCProtocol *symbol = nullptr;
public:
- ObjCProtocolDeclarationAST()
- : attribute_list(0)
- , protocol_token(0)
- , name(0)
- , protocol_refs(0)
- , member_declaration_list(0)
- , end_token(0)
- , symbol(0)
- {}
-
- virtual ObjCProtocolDeclarationAST *asObjCProtocolDeclaration() { return this; }
+ ObjCProtocolDeclarationAST *asObjCProtocolDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCProtocolDeclarationAST *clone(MemoryPool *pool) const;
+ ObjCProtocolDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCProtocolRefsAST: public AST
{
public:
- unsigned less_token;
- NameListAST *identifier_list;
- unsigned greater_token;
+ int less_token = 0;
+ NameListAST *identifier_list = nullptr;
+ int greater_token = 0;
public:
- ObjCProtocolRefsAST()
- : less_token(0)
- , identifier_list(0)
- , greater_token(0)
- {}
+ ObjCProtocolRefsAST *asObjCProtocolRefs() override { return this; }
- virtual ObjCProtocolRefsAST *asObjCProtocolRefs() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCProtocolRefsAST *clone(MemoryPool *pool) const;
+ ObjCProtocolRefsAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCMessageArgumentAST: public AST
{
public:
- ExpressionAST *parameter_value_expression;
+ ExpressionAST *parameter_value_expression = nullptr;
public:
- ObjCMessageArgumentAST()
- : parameter_value_expression(0)
- {}
-
- virtual ObjCMessageArgumentAST *asObjCMessageArgument() { return this; }
+ ObjCMessageArgumentAST *asObjCMessageArgument() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCMessageArgumentAST *clone(MemoryPool *pool) const;
+ ObjCMessageArgumentAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCMessageExpressionAST: public ExpressionAST
{
public:
- unsigned lbracket_token;
- ExpressionAST *receiver_expression;
- ObjCSelectorAST *selector;
- ObjCMessageArgumentListAST *argument_list;
- unsigned rbracket_token;
+ int lbracket_token = 0;
+ ExpressionAST *receiver_expression = nullptr;
+ ObjCSelectorAST *selector = nullptr;
+ ObjCMessageArgumentListAST *argument_list = nullptr;
+ int rbracket_token = 0;
public:
- ObjCMessageExpressionAST()
- : lbracket_token(0)
- , receiver_expression(0)
- , selector(0)
- , argument_list(0)
- , rbracket_token(0)
- {}
+ ObjCMessageExpressionAST *asObjCMessageExpression() override { return this; }
- virtual ObjCMessageExpressionAST *asObjCMessageExpression() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCMessageExpressionAST *clone(MemoryPool *pool) const;
+ ObjCMessageExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCProtocolExpressionAST: public ExpressionAST
{
public:
- unsigned protocol_token;
- unsigned lparen_token;
- unsigned identifier_token;
- unsigned rparen_token;
+ int protocol_token = 0;
+ int lparen_token = 0;
+ int identifier_token = 0;
+ int rparen_token = 0;
public:
- ObjCProtocolExpressionAST()
- : protocol_token(0)
- , lparen_token(0)
- , identifier_token(0)
- , rparen_token(0)
- {}
-
- virtual ObjCProtocolExpressionAST *asObjCProtocolExpression() { return this; }
+ ObjCProtocolExpressionAST *asObjCProtocolExpression() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCProtocolExpressionAST *clone(MemoryPool *pool) const;
+ ObjCProtocolExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCTypeNameAST: public AST
{
public:
- unsigned lparen_token;
- unsigned type_qualifier_token;
- ExpressionAST *type_id;
- unsigned rparen_token;
+ int lparen_token = 0;
+ int type_qualifier_token = 0;
+ ExpressionAST *type_id = nullptr;
+ int rparen_token = 0;
public:
- ObjCTypeNameAST()
- : lparen_token(0)
- , type_qualifier_token(0)
- , type_id(0)
- , rparen_token(0)
- {}
+ ObjCTypeNameAST *asObjCTypeName() override { return this; }
- virtual ObjCTypeNameAST *asObjCTypeName() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCTypeNameAST *clone(MemoryPool *pool) const;
+ ObjCTypeNameAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCEncodeExpressionAST: public ExpressionAST
{
public:
- unsigned encode_token;
- ObjCTypeNameAST *type_name;
+ int encode_token = 0;
+ ObjCTypeNameAST *type_name = nullptr;
public:
- ObjCEncodeExpressionAST()
- : encode_token(0)
- , type_name(0)
- {}
-
- virtual ObjCEncodeExpressionAST *asObjCEncodeExpression() { return this; }
+ ObjCEncodeExpressionAST *asObjCEncodeExpression() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCEncodeExpressionAST *clone(MemoryPool *pool) const;
+ ObjCEncodeExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCSelectorExpressionAST: public ExpressionAST
{
public:
- unsigned selector_token;
- unsigned lparen_token;
- ObjCSelectorAST *selector;
- unsigned rparen_token;
+ int selector_token = 0;
+ int lparen_token = 0;
+ ObjCSelectorAST *selector = nullptr;
+ int rparen_token = 0;
public:
- ObjCSelectorExpressionAST()
- : selector_token(0)
- , lparen_token(0)
- , selector(0)
- , rparen_token(0)
- {}
+ ObjCSelectorExpressionAST *asObjCSelectorExpression() override { return this; }
- virtual ObjCSelectorExpressionAST *asObjCSelectorExpression() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCSelectorExpressionAST *clone(MemoryPool *pool) const;
+ ObjCSelectorExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCInstanceVariablesDeclarationAST: public AST
{
public:
- unsigned lbrace_token;
- DeclarationListAST *instance_variable_list;
- unsigned rbrace_token;
+ int lbrace_token = 0;
+ DeclarationListAST *instance_variable_list = nullptr;
+ int rbrace_token = 0;
public:
- ObjCInstanceVariablesDeclarationAST()
- : lbrace_token(0)
- , instance_variable_list(0)
- , rbrace_token(0)
- {}
-
- virtual ObjCInstanceVariablesDeclarationAST *asObjCInstanceVariablesDeclaration() { return this; }
+ ObjCInstanceVariablesDeclarationAST *asObjCInstanceVariablesDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCInstanceVariablesDeclarationAST *clone(MemoryPool *pool) const;
+ ObjCInstanceVariablesDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCVisibilityDeclarationAST: public DeclarationAST
{
public:
- unsigned visibility_token;
+ int visibility_token = 0;
public:
- ObjCVisibilityDeclarationAST()
- : visibility_token(0)
- {}
+ ObjCVisibilityDeclarationAST *asObjCVisibilityDeclaration() override { return this; }
- virtual ObjCVisibilityDeclarationAST *asObjCVisibilityDeclaration() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCVisibilityDeclarationAST *clone(MemoryPool *pool) const;
+ ObjCVisibilityDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCPropertyAttributeAST: public AST
{
public:
- unsigned attribute_identifier_token;
- unsigned equals_token;
- ObjCSelectorAST *method_selector;
+ int attribute_identifier_token = 0;
+ int equals_token = 0;
+ ObjCSelectorAST *method_selector = nullptr;
public:
- ObjCPropertyAttributeAST()
- : attribute_identifier_token(0)
- , equals_token(0)
- , method_selector(0)
- {}
-
- virtual ObjCPropertyAttributeAST *asObjCPropertyAttribute() { return this; }
+ ObjCPropertyAttributeAST *asObjCPropertyAttribute() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCPropertyAttributeAST *clone(MemoryPool *pool) const;
+ ObjCPropertyAttributeAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCPropertyDeclarationAST: public DeclarationAST
{
public:
- SpecifierListAST *attribute_list;
- unsigned property_token;
- unsigned lparen_token;
- ObjCPropertyAttributeListAST *property_attribute_list;
- unsigned rparen_token;
- DeclarationAST *simple_declaration;
+ SpecifierListAST *attribute_list = nullptr;
+ int property_token = 0;
+ int lparen_token = 0;
+ ObjCPropertyAttributeListAST *property_attribute_list = nullptr;
+ int rparen_token = 0;
+ DeclarationAST *simple_declaration = nullptr;
public: // annotations
- List<ObjCPropertyDeclaration *> *symbols;
+ List<ObjCPropertyDeclaration *> *symbols = nullptr;
public:
- ObjCPropertyDeclarationAST()
- : attribute_list(0)
- , property_token(0)
- , lparen_token(0)
- , property_attribute_list(0)
- , rparen_token(0)
- , simple_declaration(0)
- , symbols(0)
- {}
+ ObjCPropertyDeclarationAST *asObjCPropertyDeclaration() override { return this; }
- virtual ObjCPropertyDeclarationAST *asObjCPropertyDeclaration() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCPropertyDeclarationAST *clone(MemoryPool *pool) const;
+ ObjCPropertyDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCMessageArgumentDeclarationAST: public AST
{
public:
- ObjCTypeNameAST* type_name;
- SpecifierListAST *attribute_list;
- NameAST *param_name;
+ ObjCTypeNameAST *type_name = nullptr;
+ SpecifierListAST *attribute_list = nullptr;
+ NameAST *param_name = nullptr;
public: // annotations
- Argument *argument;
+ Argument *argument = nullptr;
public:
- ObjCMessageArgumentDeclarationAST()
- : type_name(0)
- , attribute_list(0)
- , param_name(0)
- , argument(0)
- {}
-
- virtual ObjCMessageArgumentDeclarationAST *asObjCMessageArgumentDeclaration() { return this; }
+ ObjCMessageArgumentDeclarationAST *asObjCMessageArgumentDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCMessageArgumentDeclarationAST *clone(MemoryPool *pool) const;
+ ObjCMessageArgumentDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCMethodPrototypeAST: public AST
{
public:
- unsigned method_type_token;
- ObjCTypeNameAST *type_name;
- ObjCSelectorAST *selector;
- ObjCMessageArgumentDeclarationListAST *argument_list;
- unsigned dot_dot_dot_token;
- SpecifierListAST *attribute_list;
+ int method_type_token = 0;
+ ObjCTypeNameAST *type_name = nullptr;
+ ObjCSelectorAST *selector = nullptr;
+ ObjCMessageArgumentDeclarationListAST *argument_list = nullptr;
+ int dot_dot_dot_token = 0;
+ SpecifierListAST *attribute_list = nullptr;
public: // annotations
- ObjCMethod *symbol;
+ ObjCMethod *symbol = nullptr;
public:
- ObjCMethodPrototypeAST()
- : method_type_token(0)
- , type_name(0)
- , selector(0)
- , argument_list(0)
- , dot_dot_dot_token(0)
- , attribute_list(0)
- , symbol(0)
- {}
+ ObjCMethodPrototypeAST *asObjCMethodPrototype() override { return this; }
- virtual ObjCMethodPrototypeAST *asObjCMethodPrototype() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCMethodPrototypeAST *clone(MemoryPool *pool) const;
+ ObjCMethodPrototypeAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCMethodDeclarationAST: public DeclarationAST
{
public:
- ObjCMethodPrototypeAST *method_prototype;
- StatementAST *function_body;
- unsigned semicolon_token;
+ ObjCMethodPrototypeAST *method_prototype = nullptr;
+ StatementAST *function_body = nullptr;
+ int semicolon_token = 0;
public:
- ObjCMethodDeclarationAST()
- : method_prototype(0)
- , function_body(0)
- , semicolon_token(0)
- {}
-
- virtual ObjCMethodDeclarationAST *asObjCMethodDeclaration() { return this; }
+ ObjCMethodDeclarationAST *asObjCMethodDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCMethodDeclarationAST *clone(MemoryPool *pool) const;
+ ObjCMethodDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCSynthesizedPropertyAST: public AST
{
public:
- unsigned property_identifier_token;
- unsigned equals_token;
- unsigned alias_identifier_token;
+ int property_identifier_token = 0;
+ int equals_token = 0;
+ int alias_identifier_token = 0;
public:
- ObjCSynthesizedPropertyAST()
- : property_identifier_token(0)
- , equals_token(0)
- , alias_identifier_token(0)
- {}
+ ObjCSynthesizedPropertyAST *asObjCSynthesizedProperty() override { return this; }
- virtual ObjCSynthesizedPropertyAST *asObjCSynthesizedProperty() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCSynthesizedPropertyAST *clone(MemoryPool *pool) const;
+ ObjCSynthesizedPropertyAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCSynthesizedPropertiesDeclarationAST: public DeclarationAST
{
public:
- unsigned synthesized_token;
- ObjCSynthesizedPropertyListAST *property_identifier_list;
- unsigned semicolon_token;
+ int synthesized_token = 0;
+ ObjCSynthesizedPropertyListAST *property_identifier_list = nullptr;
+ int semicolon_token = 0;
public:
- ObjCSynthesizedPropertiesDeclarationAST()
- : synthesized_token(0)
- , property_identifier_list(0)
- , semicolon_token(0)
- {}
-
- virtual ObjCSynthesizedPropertiesDeclarationAST *asObjCSynthesizedPropertiesDeclaration() { return this; }
+ ObjCSynthesizedPropertiesDeclarationAST *asObjCSynthesizedPropertiesDeclaration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCSynthesizedPropertiesDeclarationAST *clone(MemoryPool *pool) const;
+ ObjCSynthesizedPropertiesDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCDynamicPropertiesDeclarationAST: public DeclarationAST
{
public:
- unsigned dynamic_token;
- NameListAST *property_identifier_list;
- unsigned semicolon_token;
+ int dynamic_token = 0;
+ NameListAST *property_identifier_list = nullptr;
+ int semicolon_token = 0;
public:
- ObjCDynamicPropertiesDeclarationAST()
- : dynamic_token(0)
- , property_identifier_list(0)
- , semicolon_token(0)
- {}
+ ObjCDynamicPropertiesDeclarationAST *asObjCDynamicPropertiesDeclaration() override { return this; }
- virtual ObjCDynamicPropertiesDeclarationAST *asObjCDynamicPropertiesDeclaration() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCDynamicPropertiesDeclarationAST *clone(MemoryPool *pool) const;
+ ObjCDynamicPropertiesDeclarationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCFastEnumerationAST: public StatementAST
{
public:
- unsigned for_token;
- unsigned lparen_token;
+ int for_token = 0;
+ int lparen_token = 0;
// declaration
- SpecifierListAST *type_specifier_list;
- DeclaratorAST *declarator;
+ SpecifierListAST *type_specifier_list = nullptr;
+ DeclaratorAST *declarator = nullptr;
// or an expression
- ExpressionAST *initializer;
+ ExpressionAST *initializer = nullptr;
- unsigned in_token;
- ExpressionAST *fast_enumeratable_expression;
- unsigned rparen_token;
- StatementAST *statement;
+ int in_token = 0;
+ ExpressionAST *fast_enumeratable_expression = nullptr;
+ int rparen_token = 0;
+ StatementAST *statement = nullptr;
public: // annotations
- Block *symbol;
+ Block *symbol = nullptr;
public:
- ObjCFastEnumerationAST()
- : for_token(0)
- , lparen_token(0)
- , type_specifier_list(0)
- , declarator(0)
- , initializer(0)
- , in_token(0)
- , fast_enumeratable_expression(0)
- , rparen_token(0)
- , statement(0)
- , symbol(0)
- {}
-
- virtual ObjCFastEnumerationAST *asObjCFastEnumeration() { return this; }
+ ObjCFastEnumerationAST *asObjCFastEnumeration() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ int firstToken() const override;
+ int lastToken() const override;
- virtual ObjCFastEnumerationAST *clone(MemoryPool *pool) const;
+ ObjCFastEnumerationAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CPLUSPLUS_EXPORT ObjCSynchronizedStatementAST: public StatementAST
{
public:
- unsigned synchronized_token;
- unsigned lparen_token;
- ExpressionAST *synchronized_object;
- unsigned rparen_token;
- StatementAST *statement;
+ int synchronized_token = 0;
+ int lparen_token = 0;
+ ExpressionAST *synchronized_object = nullptr;
+ int rparen_token = 0;
+ StatementAST *statement = nullptr;
public:
- ObjCSynchronizedStatementAST()
- : synchronized_token(0)
- , lparen_token(0)
- , synchronized_object(0)
- , rparen_token(0)
- , statement(0)
- {}
+ ObjCSynchronizedStatementAST *asObjCSynchronizedStatement() override { return this; }
- virtual ObjCSynchronizedStatementAST *asObjCSynchronizedStatement() { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual ObjCSynchronizedStatementAST *clone(MemoryPool *pool) const;
+ ObjCSynchronizedStatementAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class LambdaExpressionAST: public ExpressionAST
{
public:
- LambdaIntroducerAST *lambda_introducer;
- LambdaDeclaratorAST *lambda_declarator;
- StatementAST *statement;
+ LambdaIntroducerAST *lambda_introducer = nullptr;
+ DeclarationListAST *templateParameters = nullptr;
+ RequiresClauseAST *requiresClause = nullptr;
+ SpecifierListAST *attributes = nullptr;
+ LambdaDeclaratorAST *lambda_declarator = nullptr;
+ StatementAST *statement = nullptr;
public:
- LambdaExpressionAST()
- : lambda_introducer(0)
- , lambda_declarator(0)
- , statement(0)
- {}
-
- virtual LambdaExpressionAST *asLambdaExpression() { return this; }
+ LambdaExpressionAST *asLambdaExpression() override { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
- virtual LambdaExpressionAST *clone(MemoryPool *pool) const;
+ int firstToken() const override;
+ int lastToken() const override;
+ LambdaExpressionAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class LambdaIntroducerAST: public AST
{
public:
- unsigned lbracket_token;
- LambdaCaptureAST *lambda_capture;
- unsigned rbracket_token;
+ int lbracket_token = 0;
+ LambdaCaptureAST *lambda_capture = nullptr;
+ int rbracket_token = 0;
public:
- LambdaIntroducerAST()
- : lbracket_token(0)
- , lambda_capture(0)
- , rbracket_token(0)
- {}
+ LambdaIntroducerAST *asLambdaIntroducer() override { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual LambdaIntroducerAST *asLambdaIntroducer() { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual LambdaIntroducerAST *clone(MemoryPool *pool) const;
+ LambdaIntroducerAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class LambdaCaptureAST: public AST
{
public:
- unsigned default_capture_token;
- CaptureListAST *capture_list;
+ int default_capture_token = 0;
+ CaptureListAST *capture_list = nullptr;
public:
- LambdaCaptureAST()
- : default_capture_token(0)
- , capture_list(0)
- {}
-
- virtual LambdaCaptureAST *asLambdaCapture() { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ LambdaCaptureAST *asLambdaCapture() override { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual LambdaCaptureAST *clone(MemoryPool *pool) const;
+ LambdaCaptureAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class CaptureAST: public AST
{
public:
- unsigned amper_token;
- NameAST *identifier;
+ int amper_token = 0;
+ NameAST *identifier = nullptr;
public:
- CaptureAST()
- : amper_token(0)
- , identifier(0)
- {}
+ CaptureAST *asCapture() override { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual CaptureAST *asCapture() { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual CaptureAST *clone(MemoryPool *pool) const;
+ CaptureAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class LambdaDeclaratorAST: public AST
{
public:
- unsigned lparen_token;
- ParameterDeclarationClauseAST *parameter_declaration_clause;
- unsigned rparen_token;
- SpecifierListAST *attributes;
- unsigned mutable_token;
- ExceptionSpecificationAST *exception_specification;
- TrailingReturnTypeAST *trailing_return_type;
+ int lparen_token = 0;
+ ParameterDeclarationClauseAST *parameter_declaration_clause = nullptr;
+ int rparen_token = 0;
+ SpecifierListAST *attributes = nullptr;
+ int mutable_token = 0;
+ ExceptionSpecificationAST *exception_specification = nullptr;
+ TrailingReturnTypeAST *trailing_return_type = nullptr;
+ RequiresClauseAST *requiresClause = nullptr;
public: // annotations
- Function *symbol;
+ Function *symbol = nullptr;
public:
- LambdaDeclaratorAST()
- : lparen_token(0)
- , parameter_declaration_clause(0)
- , rparen_token(0)
- , attributes(0)
- , mutable_token(0)
- , exception_specification(0)
- , trailing_return_type(0)
- , symbol(0)
- {}
-
- virtual LambdaDeclaratorAST *asLambdaDeclarator() { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ LambdaDeclaratorAST *asLambdaDeclarator() override { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual LambdaDeclaratorAST *clone(MemoryPool *pool) const;
+ LambdaDeclaratorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class TrailingReturnTypeAST: public AST
{
public:
- unsigned arrow_token;
- SpecifierListAST *attributes;
- SpecifierListAST *type_specifier_list;
- DeclaratorAST *declarator;
+ int arrow_token = 0;
+ SpecifierListAST *attributes = nullptr;
+ SpecifierListAST *type_specifier_list = nullptr;
+ DeclaratorAST *declarator = nullptr;
public:
- TrailingReturnTypeAST()
- : arrow_token(0)
- , attributes(0)
- , type_specifier_list(0)
- , declarator(0)
- {}
+ TrailingReturnTypeAST *asTrailingReturnType() override { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual TrailingReturnTypeAST *asTrailingReturnType() { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
-
- virtual TrailingReturnTypeAST *clone(MemoryPool *pool) const;
+ TrailingReturnTypeAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class BracedInitializerAST: public ExpressionAST
{
public:
- unsigned lbrace_token;
- ExpressionListAST *expression_list;
- unsigned comma_token;
- unsigned rbrace_token;
+ int lbrace_token = 0;
+ ExpressionListAST *expression_list = nullptr;
+ int comma_token = 0;
+ int rbrace_token = 0;
public:
- BracedInitializerAST()
- : lbrace_token(0)
- , expression_list(0)
- , comma_token(0)
- , rbrace_token(0)
- {}
-
- virtual BracedInitializerAST *asBracedInitializer() { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ BracedInitializerAST *asBracedInitializer() override { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual BracedInitializerAST *clone(MemoryPool *pool) const;
+ BracedInitializerAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class DesignatorAST: public AST
{
public:
- DesignatorAST()
- {}
-
- virtual DesignatorAST *asDesignator() { return this; }
- virtual DesignatorAST *clone(MemoryPool *pool) const = 0;
+ DesignatorAST *asDesignator() override { return this; }
+ DesignatorAST *clone(MemoryPool *pool) const override = 0;
};
class DotDesignatorAST: public DesignatorAST
{
public:
- unsigned dot_token;
- unsigned identifier_token;
-public:
- DotDesignatorAST()
- : dot_token(0)
- , identifier_token(0)
- {}
+ int dot_token = 0;
+ int identifier_token = 0;
- virtual DotDesignatorAST *asDotDesignator() { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+public:
+ DotDesignatorAST *asDotDesignator() override { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual DotDesignatorAST *clone(MemoryPool *pool) const;
+ DotDesignatorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class BracketDesignatorAST: public DesignatorAST
{
public:
- unsigned lbracket_token;
- ExpressionAST *expression;
- unsigned rbracket_token;
-public:
- BracketDesignatorAST()
- : lbracket_token(0)
- , expression(0)
- , rbracket_token(0)
- {}
+ int lbracket_token = 0;
+ ExpressionAST *expression = nullptr;
+ int rbracket_token = 0;
- virtual BracketDesignatorAST *asBracketDesignator() { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+public:
+ BracketDesignatorAST *asBracketDesignator() override { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual BracketDesignatorAST *clone(MemoryPool *pool) const;
+ BracketDesignatorAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
class DesignatedInitializerAST: public ExpressionAST
{
public:
- DesignatorListAST *designator_list;
- unsigned equal_token;
- ExpressionAST *initializer;
+ DesignatorListAST *designator_list = nullptr;
+ int equal_token = 0;
+ ExpressionAST *initializer = nullptr;
public:
- DesignatedInitializerAST()
- : designator_list(0)
- , equal_token(0)
- , initializer(0)
- {}
-
- virtual DesignatedInitializerAST *asDesignatedInitializer() { return this; }
- virtual unsigned firstToken() const;
- virtual unsigned lastToken() const;
+ DesignatedInitializerAST *asDesignatedInitializer() override { return this; }
+ int firstToken() const override;
+ int lastToken() const override;
- virtual DesignatedInitializerAST *clone(MemoryPool *pool) const;
+ DesignatedInitializerAST *clone(MemoryPool *pool) const override;
protected:
- virtual void accept0(ASTVisitor *visitor);
- virtual bool match0(AST *, ASTMatcher *);
+ void accept0(ASTVisitor *visitor) override;
+ bool match0(AST *, ASTMatcher *) override;
};
} // namespace CPlusPlus
diff --git a/src/libs/3rdparty/cplusplus/ASTClone.cpp b/src/libs/3rdparty/cplusplus/ASTClone.cpp
index 17a1af18d9..c9c2fc8294 100644
--- a/src/libs/3rdparty/cplusplus/ASTClone.cpp
+++ b/src/libs/3rdparty/cplusplus/ASTClone.cpp
@@ -44,7 +44,7 @@ ObjCSelectorAST *ObjCSelectorAST::clone(MemoryPool *pool) const
ObjCSelectorAST *ast = new (pool) ObjCSelectorAST;
for (ObjCSelectorArgumentListAST *iter = selector_argument_list, **ast_iter = &ast->selector_argument_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ObjCSelectorArgumentListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ObjCSelectorArgumentListAST((iter->value) ? iter->value->clone(pool) : nullptr);
return ast;
}
@@ -75,12 +75,37 @@ GnuAttributeSpecifierAST *GnuAttributeSpecifierAST::clone(MemoryPool *pool) cons
ast->second_lparen_token = second_lparen_token;
for (GnuAttributeListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) GnuAttributeListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) GnuAttributeListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->first_rparen_token = first_rparen_token;
ast->second_rparen_token = second_rparen_token;
return ast;
}
+MsvcDeclspecSpecifierAST *MsvcDeclspecSpecifierAST::clone(MemoryPool *pool) const
+{
+ MsvcDeclspecSpecifierAST *ast = new (pool) MsvcDeclspecSpecifierAST;
+ ast->attribute_token = attribute_token;
+ ast->lparen_token = lparen_token;
+ for (GnuAttributeListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
+ iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
+ *ast_iter = new (pool) GnuAttributeListAST((iter->value) ? iter->value->clone(pool) : nullptr);
+ ast->rparen_token = rparen_token;
+ return ast;
+}
+
+StdAttributeSpecifierAST *StdAttributeSpecifierAST::clone(MemoryPool *pool) const
+{
+ StdAttributeSpecifierAST *ast = new (pool) StdAttributeSpecifierAST;
+ ast->first_lbracket_token = first_lbracket_token;
+ ast->second_lbracket_token = second_lbracket_token;
+ for (GnuAttributeListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
+ iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
+ *ast_iter = new (pool) GnuAttributeListAST((iter->value) ? iter->value->clone(pool) : nullptr);
+ ast->first_rbracket_token = first_rbracket_token;
+ ast->second_rbracket_token = second_rbracket_token;
+ return ast;
+}
+
GnuAttributeAST *GnuAttributeAST::clone(MemoryPool *pool) const
{
GnuAttributeAST *ast = new (pool) GnuAttributeAST;
@@ -89,7 +114,7 @@ GnuAttributeAST *GnuAttributeAST::clone(MemoryPool *pool) const
ast->tag_token = tag_token;
for (ExpressionListAST *iter = expression_list, **ast_iter = &ast->expression_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rparen_token = rparen_token;
return ast;
}
@@ -116,23 +141,51 @@ DecltypeSpecifierAST *DecltypeSpecifierAST::clone(MemoryPool *pool) const
return ast;
}
+TypeConstraintAST *TypeConstraintAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) TypeConstraintAST;
+ for (NestedNameSpecifierListAST *iter = nestedName, **ast_iter = &ast->nestedName; iter;
+ iter = iter->next, ast_iter = &(*ast_iter)->next)
+ *ast_iter = new (pool) NestedNameSpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
+ if (conceptName)
+ ast->conceptName = conceptName->clone(pool);
+ ast->lessToken = lessToken;
+ for (ExpressionListAST *iter = templateArgs, **ast_iter = &ast->templateArgs;
+ iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
+ *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : nullptr);
+ ast->greaterToken = greaterToken;
+ return ast;
+}
+
+PlaceholderTypeSpecifierAST *PlaceholderTypeSpecifierAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) PlaceholderTypeSpecifierAST;
+ if (typeConstraint)
+ ast->typeConstraint = typeConstraint->clone(pool);
+ ast->lparenToken = lparenToken;
+ ast->declTypetoken = declTypetoken;
+ ast->autoToken = autoToken;
+ ast->rparenToken = rparenToken;
+ return ast;
+}
+
DeclaratorAST *DeclaratorAST::clone(MemoryPool *pool) const
{
DeclaratorAST *ast = new (pool) DeclaratorAST;
for (SpecifierListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
for (PtrOperatorListAST *iter = ptr_operator_list, **ast_iter = &ast->ptr_operator_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) PtrOperatorListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) PtrOperatorListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (core_declarator)
ast->core_declarator = core_declarator->clone(pool);
for (PostfixDeclaratorListAST *iter = postfix_declarator_list, **ast_iter = &ast->postfix_declarator_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) PostfixDeclaratorListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) PostfixDeclaratorListAST((iter->value) ? iter->value->clone(pool) : nullptr);
for (SpecifierListAST *iter = post_attribute_list, **ast_iter = &ast->post_attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->equal_token = equal_token;
if (initializer)
ast->initializer = initializer->clone(pool);
@@ -145,10 +198,10 @@ SimpleDeclarationAST *SimpleDeclarationAST::clone(MemoryPool *pool) const
ast->qt_invokable_token = qt_invokable_token;
for (SpecifierListAST *iter = decl_specifier_list, **ast_iter = &ast->decl_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
for (DeclaratorListAST *iter = declarator_list, **ast_iter = &ast->declarator_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) DeclaratorListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) DeclaratorListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->semicolon_token = semicolon_token;
return ast;
}
@@ -187,7 +240,7 @@ QtPrivateSlotAST *QtPrivateSlotAST::clone(MemoryPool *pool) const
ast->comma_token = comma_token;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (declarator)
ast->declarator = declarator->clone(pool);
ast->rparen_token = rparen_token;
@@ -217,7 +270,7 @@ QtPropertyDeclarationAST *QtPropertyDeclarationAST::clone(MemoryPool *pool) cons
ast->property_name = property_name->clone(pool);
for (QtPropertyDeclarationItemListAST *iter = property_declaration_item_list, **ast_iter = &ast->property_declaration_item_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) QtPropertyDeclarationItemListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) QtPropertyDeclarationItemListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rparen_token = rparen_token;
return ast;
}
@@ -229,7 +282,7 @@ QtEnumDeclarationAST *QtEnumDeclarationAST::clone(MemoryPool *pool) const
ast->lparen_token = lparen_token;
for (NameListAST *iter = enumerator_list, **ast_iter = &ast->enumerator_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rparen_token = rparen_token;
return ast;
}
@@ -241,7 +294,7 @@ QtFlagsDeclarationAST *QtFlagsDeclarationAST::clone(MemoryPool *pool) const
ast->lparen_token = lparen_token;
for (NameListAST *iter = flag_enums_list, **ast_iter = &ast->flag_enums_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rparen_token = rparen_token;
return ast;
}
@@ -253,7 +306,7 @@ QtInterfaceNameAST *QtInterfaceNameAST::clone(MemoryPool *pool) const
ast->interface_name = interface_name->clone(pool);
for (NameListAST *iter = constraint_list, **ast_iter = &ast->constraint_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : nullptr);
return ast;
}
@@ -264,7 +317,7 @@ QtInterfacesDeclarationAST *QtInterfacesDeclarationAST::clone(MemoryPool *pool)
ast->lparen_token = lparen_token;
for (QtInterfaceNameListAST *iter = interface_name_list, **ast_iter = &ast->interface_name_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) QtInterfaceNameListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) QtInterfaceNameListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rparen_token = rparen_token;
return ast;
}
@@ -372,19 +425,19 @@ ClassSpecifierAST *ClassSpecifierAST::clone(MemoryPool *pool) const
ast->classkey_token = classkey_token;
for (SpecifierListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (name)
ast->name = name->clone(pool);
ast->final_token = final_token;
ast->colon_token = colon_token;
for (BaseSpecifierListAST *iter = base_clause_list, **ast_iter = &ast->base_clause_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) BaseSpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) BaseSpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->dot_dot_dot_token = dot_dot_dot_token;
ast->lbrace_token = lbrace_token;
for (DeclarationListAST *iter = member_specifier_list, **ast_iter = &ast->member_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rbrace_token = rbrace_token;
return ast;
}
@@ -407,7 +460,7 @@ CompoundStatementAST *CompoundStatementAST::clone(MemoryPool *pool) const
ast->lbrace_token = lbrace_token;
for (StatementListAST *iter = statement_list, **ast_iter = &ast->statement_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) StatementListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) StatementListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rbrace_token = rbrace_token;
return ast;
}
@@ -417,7 +470,7 @@ ConditionAST *ConditionAST::clone(MemoryPool *pool) const
ConditionAST *ast = new (pool) ConditionAST;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (declarator)
ast->declarator = declarator->clone(pool);
return ast;
@@ -458,7 +511,7 @@ CtorInitializerAST *CtorInitializerAST::clone(MemoryPool *pool) const
ast->colon_token = colon_token;
for (MemInitializerListAST *iter = member_initializer_list, **ast_iter = &ast->member_initializer_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) MemInitializerListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) MemInitializerListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->dot_dot_dot_token = dot_dot_dot_token;
return ast;
}
@@ -499,7 +552,7 @@ FunctionDeclaratorAST *FunctionDeclaratorAST::clone(MemoryPool *pool) const
ast->rparen_token = rparen_token;
for (SpecifierListAST *iter = cv_qualifier_list, **ast_iter = &ast->cv_qualifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->ref_qualifier_token = ref_qualifier_token;
if (exception_specification)
ast->exception_specification = exception_specification->clone(pool);
@@ -561,7 +614,7 @@ ElaboratedTypeSpecifierAST *ElaboratedTypeSpecifierAST::clone(MemoryPool *pool)
ast->classkey_token = classkey_token;
for (SpecifierListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (name)
ast->name = name->clone(pool);
return ast;
@@ -577,11 +630,11 @@ EnumSpecifierAST *EnumSpecifierAST::clone(MemoryPool *pool) const
ast->colon_token = colon_token;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->lbrace_token = lbrace_token;
for (EnumeratorListAST *iter = enumerator_list, **ast_iter = &ast->enumerator_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) EnumeratorListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) EnumeratorListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->stray_comma_token = stray_comma_token;
ast->rbrace_token = rbrace_token;
return ast;
@@ -602,7 +655,7 @@ ExceptionDeclarationAST *ExceptionDeclarationAST::clone(MemoryPool *pool) const
ExceptionDeclarationAST *ast = new (pool) ExceptionDeclarationAST;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (declarator)
ast->declarator = declarator->clone(pool);
ast->dot_dot_dot_token = dot_dot_dot_token;
@@ -617,7 +670,7 @@ DynamicExceptionSpecificationAST *DynamicExceptionSpecificationAST::clone(Memory
ast->dot_dot_dot_token = dot_dot_dot_token;
for (ExpressionListAST *iter = type_id_list, **ast_iter = &ast->type_id_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rparen_token = rparen_token;
return ast;
}
@@ -658,7 +711,7 @@ FunctionDefinitionAST *FunctionDefinitionAST::clone(MemoryPool *pool) const
ast->qt_invokable_token = qt_invokable_token;
for (SpecifierListAST *iter = decl_specifier_list, **ast_iter = &ast->decl_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (declarator)
ast->declarator = declarator->clone(pool);
if (ctor_initializer)
@@ -675,7 +728,7 @@ ForeachStatementAST *ForeachStatementAST::clone(MemoryPool *pool) const
ast->lparen_token = lparen_token;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (declarator)
ast->declarator = declarator->clone(pool);
if (initializer)
@@ -696,7 +749,7 @@ RangeBasedForStatementAST *RangeBasedForStatementAST::clone(MemoryPool *pool) co
ast->lparen_token = lparen_token;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (declarator)
ast->declarator = declarator->clone(pool);
ast->colon_token = colon_token;
@@ -730,7 +783,10 @@ IfStatementAST *IfStatementAST::clone(MemoryPool *pool) const
{
IfStatementAST *ast = new (pool) IfStatementAST;
ast->if_token = if_token;
+ ast->constexpr_token = constexpr_token;
ast->lparen_token = lparen_token;
+ if (initStmt)
+ ast->initStmt = initStmt->clone(pool);
if (condition)
ast->condition = condition->clone(pool);
ast->rparen_token = rparen_token;
@@ -748,7 +804,7 @@ ArrayInitializerAST *ArrayInitializerAST::clone(MemoryPool *pool) const
ast->lbrace_token = lbrace_token;
for (ExpressionListAST *iter = expression_list, **ast_iter = &ast->expression_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rbrace_token = rbrace_token;
return ast;
}
@@ -769,7 +825,7 @@ LinkageBodyAST *LinkageBodyAST::clone(MemoryPool *pool) const
ast->lbrace_token = lbrace_token;
for (DeclarationListAST *iter = declaration_list, **ast_iter = &ast->declaration_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rbrace_token = rbrace_token;
return ast;
}
@@ -809,7 +865,7 @@ QualifiedNameAST *QualifiedNameAST::clone(MemoryPool *pool) const
ast->global_scope_token = global_scope_token;
for (NestedNameSpecifierListAST *iter = nested_name_specifier_list, **ast_iter = &ast->nested_name_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) NestedNameSpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) NestedNameSpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (unqualified_name)
ast->unqualified_name = unqualified_name->clone(pool);
return ast;
@@ -830,10 +886,10 @@ ConversionFunctionIdAST *ConversionFunctionIdAST::clone(MemoryPool *pool) const
ast->operator_token = operator_token;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
for (PtrOperatorListAST *iter = ptr_operator_list, **ast_iter = &ast->ptr_operator_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) PtrOperatorListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) PtrOperatorListAST((iter->value) ? iter->value->clone(pool) : nullptr);
return ast;
}
@@ -868,7 +924,7 @@ TemplateIdAST *TemplateIdAST::clone(MemoryPool *pool) const
ast->less_token = less_token;
for (ExpressionListAST *iter = template_argument_list, **ast_iter = &ast->template_argument_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->greater_token = greater_token;
return ast;
}
@@ -881,7 +937,7 @@ NamespaceAST *NamespaceAST::clone(MemoryPool *pool) const
ast->identifier_token = identifier_token;
for (SpecifierListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (linkage_body)
ast->linkage_body = linkage_body->clone(pool);
return ast;
@@ -918,7 +974,7 @@ ExpressionListParenAST *ExpressionListParenAST::clone(MemoryPool *pool) const
ast->lparen_token = lparen_token;
for (ExpressionListAST *iter = expression_list, **ast_iter = &ast->expression_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rparen_token = rparen_token;
return ast;
}
@@ -956,13 +1012,13 @@ NewTypeIdAST *NewTypeIdAST::clone(MemoryPool *pool) const
NewTypeIdAST *ast = new (pool) NewTypeIdAST;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
for (PtrOperatorListAST *iter = ptr_operator_list, **ast_iter = &ast->ptr_operator_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) PtrOperatorListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) PtrOperatorListAST((iter->value) ? iter->value->clone(pool) : nullptr);
for (NewArrayDeclaratorListAST *iter = new_array_declarator_list, **ast_iter = &ast->new_array_declarator_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) NewArrayDeclaratorListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) NewArrayDeclaratorListAST((iter->value) ? iter->value->clone(pool) : nullptr);
return ast;
}
@@ -980,7 +1036,7 @@ ParameterDeclarationAST *ParameterDeclarationAST::clone(MemoryPool *pool) const
ParameterDeclarationAST *ast = new (pool) ParameterDeclarationAST;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (declarator)
ast->declarator = declarator->clone(pool);
ast->equal_token = equal_token;
@@ -994,7 +1050,7 @@ ParameterDeclarationClauseAST *ParameterDeclarationClauseAST::clone(MemoryPool *
ParameterDeclarationClauseAST *ast = new (pool) ParameterDeclarationClauseAST;
for (ParameterDeclarationListAST *iter = parameter_declaration_list, **ast_iter = &ast->parameter_declaration_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ParameterDeclarationListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ParameterDeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->dot_dot_dot_token = dot_dot_dot_token;
return ast;
}
@@ -1007,7 +1063,7 @@ CallAST *CallAST::clone(MemoryPool *pool) const
ast->lparen_token = lparen_token;
for (ExpressionListAST *iter = expression_list, **ast_iter = &ast->expression_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rparen_token = rparen_token;
return ast;
}
@@ -1072,7 +1128,7 @@ TypeConstructorCallAST *TypeConstructorCallAST::clone(MemoryPool *pool) const
TypeConstructorCallAST *ast = new (pool) TypeConstructorCallAST;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (expression)
ast->expression = expression->clone(pool);
return ast;
@@ -1084,11 +1140,11 @@ PointerToMemberAST *PointerToMemberAST::clone(MemoryPool *pool) const
ast->global_scope_token = global_scope_token;
for (NestedNameSpecifierListAST *iter = nested_name_specifier_list, **ast_iter = &ast->nested_name_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) NestedNameSpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) NestedNameSpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->star_token = star_token;
for (SpecifierListAST *iter = cv_qualifier_list, **ast_iter = &ast->cv_qualifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->ref_qualifier_token = ref_qualifier_token;
return ast;
}
@@ -1099,7 +1155,7 @@ PointerAST *PointerAST::clone(MemoryPool *pool) const
ast->star_token = star_token;
for (SpecifierListAST *iter = cv_qualifier_list, **ast_iter = &ast->cv_qualifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
return ast;
}
@@ -1251,13 +1307,52 @@ TemplateDeclarationAST *TemplateDeclarationAST::clone(MemoryPool *pool) const
ast->less_token = less_token;
for (DeclarationListAST *iter = template_parameter_list, **ast_iter = &ast->template_parameter_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->greater_token = greater_token;
+ if (requiresClause)
+ ast->requiresClause = requiresClause->clone(pool);
if (declaration)
ast->declaration = declaration->clone(pool);
return ast;
}
+ConceptDeclarationAST *ConceptDeclarationAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) ConceptDeclarationAST;
+ ast->concept_token = concept_token;
+ ast->name = name->clone(pool);
+ ast->equals_token = equals_token;
+ ast->semicolon_token = semicolon_token;
+ for (SpecifierListAST *iter = attributes, **ast_iter = &ast->attributes;
+ iter; iter = iter->next, ast_iter = &(*ast_iter)->next) {
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
+ }
+ ast->constraint = constraint->clone(pool);
+ return ast;
+}
+
+RequiresExpressionAST *RequiresExpressionAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) RequiresExpressionAST;
+ ast->requires_token = requires_token;
+ ast->lparen_token = lparen_token;
+ if (parameters)
+ ast->parameters = parameters->clone(pool);
+ ast->rparen_token = rparen_token;
+ ast->lbrace_token = lbrace_token;
+ ast->rbrace_token = rbrace_token;
+ return ast;
+}
+
+RequiresClauseAST *RequiresClauseAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) RequiresClauseAST;
+ ast->requires_token = requires_token;
+ if (constraint)
+ ast->constraint = constraint->clone(pool);
+ return ast;
+}
+
ThrowExpressionAST *ThrowExpressionAST::clone(MemoryPool *pool) const
{
ThrowExpressionAST *ast = new (pool) ThrowExpressionAST;
@@ -1267,6 +1362,24 @@ ThrowExpressionAST *ThrowExpressionAST::clone(MemoryPool *pool) const
return ast;
}
+YieldExpressionAST *YieldExpressionAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) YieldExpressionAST;
+ ast->yield_token = yield_token;
+ if (expression)
+ ast->expression = expression->clone(pool);
+ return ast;
+}
+
+AwaitExpressionAST *AwaitExpressionAST::clone(MemoryPool *pool) const
+{
+ const auto ast = new (pool) AwaitExpressionAST;
+ ast->await_token = await_token;
+ if (castExpression)
+ ast->castExpression = castExpression->clone(pool);
+ return ast;
+}
+
NoExceptOperatorExpressionAST *NoExceptOperatorExpressionAST::clone(MemoryPool *pool) const
{
NoExceptOperatorExpressionAST *ast = new (pool) NoExceptOperatorExpressionAST;
@@ -1281,7 +1394,7 @@ TranslationUnitAST *TranslationUnitAST::clone(MemoryPool *pool) const
TranslationUnitAST *ast = new (pool) TranslationUnitAST;
for (DeclarationListAST *iter = declaration_list, **ast_iter = &ast->declaration_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
return ast;
}
@@ -1293,7 +1406,7 @@ TryBlockStatementAST *TryBlockStatementAST::clone(MemoryPool *pool) const
ast->statement = statement->clone(pool);
for (CatchClauseListAST *iter = catch_clause_list, **ast_iter = &ast->catch_clause_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) CatchClauseListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) CatchClauseListAST((iter->value) ? iter->value->clone(pool) : nullptr);
return ast;
}
@@ -1315,7 +1428,7 @@ TypeIdAST *TypeIdAST::clone(MemoryPool *pool) const
TypeIdAST *ast = new (pool) TypeIdAST;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (declarator)
ast->declarator = declarator->clone(pool);
return ast;
@@ -1338,10 +1451,12 @@ TemplateTypeParameterAST *TemplateTypeParameterAST::clone(MemoryPool *pool) cons
{
TemplateTypeParameterAST *ast = new (pool) TemplateTypeParameterAST;
ast->template_token = template_token;
+ if (typeConstraint)
+ ast->typeConstraint = typeConstraint->clone(pool);
ast->less_token = less_token;
for (DeclarationListAST *iter = template_parameter_list, **ast_iter = &ast->template_parameter_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->greater_token = greater_token;
ast->class_token = class_token;
ast->dot_dot_dot_token = dot_dot_dot_token;
@@ -1402,11 +1517,11 @@ ObjCClassForwardDeclarationAST *ObjCClassForwardDeclarationAST::clone(MemoryPool
ObjCClassForwardDeclarationAST *ast = new (pool) ObjCClassForwardDeclarationAST;
for (SpecifierListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->class_token = class_token;
for (NameListAST *iter = identifier_list, **ast_iter = &ast->identifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->semicolon_token = semicolon_token;
return ast;
}
@@ -1416,7 +1531,7 @@ ObjCClassDeclarationAST *ObjCClassDeclarationAST::clone(MemoryPool *pool) const
ObjCClassDeclarationAST *ast = new (pool) ObjCClassDeclarationAST;
for (SpecifierListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->interface_token = interface_token;
ast->implementation_token = implementation_token;
if (class_name)
@@ -1434,7 +1549,7 @@ ObjCClassDeclarationAST *ObjCClassDeclarationAST::clone(MemoryPool *pool) const
ast->inst_vars_decl = inst_vars_decl->clone(pool);
for (DeclarationListAST *iter = member_declaration_list, **ast_iter = &ast->member_declaration_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->end_token = end_token;
return ast;
}
@@ -1444,11 +1559,11 @@ ObjCProtocolForwardDeclarationAST *ObjCProtocolForwardDeclarationAST::clone(Memo
ObjCProtocolForwardDeclarationAST *ast = new (pool) ObjCProtocolForwardDeclarationAST;
for (SpecifierListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->protocol_token = protocol_token;
for (NameListAST *iter = identifier_list, **ast_iter = &ast->identifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->semicolon_token = semicolon_token;
return ast;
}
@@ -1458,7 +1573,7 @@ ObjCProtocolDeclarationAST *ObjCProtocolDeclarationAST::clone(MemoryPool *pool)
ObjCProtocolDeclarationAST *ast = new (pool) ObjCProtocolDeclarationAST;
for (SpecifierListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->protocol_token = protocol_token;
if (name)
ast->name = name->clone(pool);
@@ -1466,7 +1581,7 @@ ObjCProtocolDeclarationAST *ObjCProtocolDeclarationAST::clone(MemoryPool *pool)
ast->protocol_refs = protocol_refs->clone(pool);
for (DeclarationListAST *iter = member_declaration_list, **ast_iter = &ast->member_declaration_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->end_token = end_token;
return ast;
}
@@ -1477,7 +1592,7 @@ ObjCProtocolRefsAST *ObjCProtocolRefsAST::clone(MemoryPool *pool) const
ast->less_token = less_token;
for (NameListAST *iter = identifier_list, **ast_iter = &ast->identifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->greater_token = greater_token;
return ast;
}
@@ -1500,7 +1615,7 @@ ObjCMessageExpressionAST *ObjCMessageExpressionAST::clone(MemoryPool *pool) cons
ast->selector = selector->clone(pool);
for (ObjCMessageArgumentListAST *iter = argument_list, **ast_iter = &ast->argument_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ObjCMessageArgumentListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ObjCMessageArgumentListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rbracket_token = rbracket_token;
return ast;
}
@@ -1552,7 +1667,7 @@ ObjCInstanceVariablesDeclarationAST *ObjCInstanceVariablesDeclarationAST::clone(
ast->lbrace_token = lbrace_token;
for (DeclarationListAST *iter = instance_variable_list, **ast_iter = &ast->instance_variable_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rbrace_token = rbrace_token;
return ast;
}
@@ -1579,12 +1694,12 @@ ObjCPropertyDeclarationAST *ObjCPropertyDeclarationAST::clone(MemoryPool *pool)
ObjCPropertyDeclarationAST *ast = new (pool) ObjCPropertyDeclarationAST;
for (SpecifierListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->property_token = property_token;
ast->lparen_token = lparen_token;
for (ObjCPropertyAttributeListAST *iter = property_attribute_list, **ast_iter = &ast->property_attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ObjCPropertyAttributeListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ObjCPropertyAttributeListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->rparen_token = rparen_token;
if (simple_declaration)
ast->simple_declaration = simple_declaration->clone(pool);
@@ -1598,7 +1713,7 @@ ObjCMessageArgumentDeclarationAST *ObjCMessageArgumentDeclarationAST::clone(Memo
ast->type_name = type_name->clone(pool);
for (SpecifierListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (param_name)
ast->param_name = param_name->clone(pool);
return ast;
@@ -1614,11 +1729,11 @@ ObjCMethodPrototypeAST *ObjCMethodPrototypeAST::clone(MemoryPool *pool) const
ast->selector = selector->clone(pool);
for (ObjCMessageArgumentDeclarationListAST *iter = argument_list, **ast_iter = &ast->argument_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ObjCMessageArgumentDeclarationListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ObjCMessageArgumentDeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->dot_dot_dot_token = dot_dot_dot_token;
for (SpecifierListAST *iter = attribute_list, **ast_iter = &ast->attribute_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
return ast;
}
@@ -1648,7 +1763,7 @@ ObjCSynthesizedPropertiesDeclarationAST *ObjCSynthesizedPropertiesDeclarationAST
ast->synthesized_token = synthesized_token;
for (ObjCSynthesizedPropertyListAST *iter = property_identifier_list, **ast_iter = &ast->property_identifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ObjCSynthesizedPropertyListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ObjCSynthesizedPropertyListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->semicolon_token = semicolon_token;
return ast;
}
@@ -1659,7 +1774,7 @@ ObjCDynamicPropertiesDeclarationAST *ObjCDynamicPropertiesDeclarationAST::clone(
ast->dynamic_token = dynamic_token;
for (NameListAST *iter = property_identifier_list, **ast_iter = &ast->property_identifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->semicolon_token = semicolon_token;
return ast;
}
@@ -1671,7 +1786,7 @@ ObjCFastEnumerationAST *ObjCFastEnumerationAST::clone(MemoryPool *pool) const
ast->lparen_token = lparen_token;
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (declarator)
ast->declarator = declarator->clone(pool);
if (initializer)
@@ -1703,6 +1818,14 @@ LambdaExpressionAST *LambdaExpressionAST::clone(MemoryPool *pool) const
LambdaExpressionAST *ast = new (pool) LambdaExpressionAST;
if (lambda_introducer)
ast->lambda_introducer = lambda_introducer->clone(pool);
+ for (DeclarationListAST *iter = templateParameters, **ast_iter = &ast->templateParameters;
+ iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
+ *ast_iter = new (pool) DeclarationListAST((iter->value) ? iter->value->clone(pool) : nullptr);
+ if (requiresClause)
+ ast->requiresClause = requiresClause->clone(pool);
+ for (SpecifierListAST *iter = attributes, **ast_iter = &ast->attributes;
+ iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (lambda_declarator)
ast->lambda_declarator = lambda_declarator->clone(pool);
if (statement)
@@ -1726,7 +1849,7 @@ LambdaCaptureAST *LambdaCaptureAST::clone(MemoryPool *pool) const
ast->default_capture_token = default_capture_token;
for (CaptureListAST *iter = capture_list, **ast_iter = &ast->capture_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) CaptureListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) CaptureListAST((iter->value) ? iter->value->clone(pool) : nullptr);
return ast;
}
@@ -1748,12 +1871,14 @@ LambdaDeclaratorAST *LambdaDeclaratorAST::clone(MemoryPool *pool) const
ast->rparen_token = rparen_token;
for (SpecifierListAST *iter = attributes, **ast_iter = &ast->attributes;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->mutable_token = mutable_token;
if (exception_specification)
ast->exception_specification = exception_specification->clone(pool);
if (trailing_return_type)
ast->trailing_return_type = trailing_return_type->clone(pool);
+ if (requiresClause)
+ ast->requiresClause = requiresClause->clone(pool);
return ast;
}
@@ -1763,10 +1888,10 @@ TrailingReturnTypeAST *TrailingReturnTypeAST::clone(MemoryPool *pool) const
ast->arrow_token = arrow_token;
for (SpecifierListAST *iter = attributes, **ast_iter = &ast->attributes;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
for (SpecifierListAST *iter = type_specifier_list, **ast_iter = &ast->type_specifier_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) SpecifierListAST((iter->value) ? iter->value->clone(pool) : nullptr);
if (declarator)
ast->declarator = declarator->clone(pool);
return ast;
@@ -1778,7 +1903,7 @@ BracedInitializerAST *BracedInitializerAST::clone(MemoryPool *pool) const
ast->lbrace_token = lbrace_token;
for (ExpressionListAST *iter = expression_list, **ast_iter = &ast->expression_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->comma_token = comma_token;
ast->rbrace_token = rbrace_token;
return ast;
@@ -1807,10 +1932,18 @@ DesignatedInitializerAST *DesignatedInitializerAST::clone(MemoryPool *pool) cons
DesignatedInitializerAST *ast = new (pool) DesignatedInitializerAST;
for (DesignatorListAST *iter = designator_list, **ast_iter = &ast->designator_list;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) DesignatorListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) DesignatorListAST((iter->value) ? iter->value->clone(pool) : nullptr);
ast->equal_token = equal_token;
if (initializer)
ast->initializer = initializer->clone(pool);
return ast;
}
+DecompositionDeclaratorAST *DecompositionDeclaratorAST::clone(MemoryPool *pool) const
+{
+ const auto theClone = new (pool) DecompositionDeclaratorAST;
+ for (NameListAST *iter = identifiers, **ast_iter = &theClone->identifiers;
+ iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
+ *ast_iter = new (pool) NameListAST((iter->value) ? iter->value->clone(pool) : nullptr);
+ return theClone;
+}
diff --git a/src/libs/3rdparty/cplusplus/ASTMatch0.cpp b/src/libs/3rdparty/cplusplus/ASTMatch0.cpp
index 66c6a1be4d..b58bd59efe 100644
--- a/src/libs/3rdparty/cplusplus/ASTMatch0.cpp
+++ b/src/libs/3rdparty/cplusplus/ASTMatch0.cpp
@@ -72,6 +72,22 @@ bool GnuAttributeSpecifierAST::match0(AST *pattern, ASTMatcher *matcher)
return false;
}
+bool MsvcDeclspecSpecifierAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (MsvcDeclspecSpecifierAST *_other = pattern->asMsvcDeclspecSpecifier())
+ return matcher->match(this, _other);
+
+ return false;
+}
+
+bool StdAttributeSpecifierAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (StdAttributeSpecifierAST *_other = pattern->asStdAttributeSpecifier())
+ return matcher->match(this, _other);
+
+ return false;
+}
+
bool GnuAttributeAST::match0(AST *pattern, ASTMatcher *matcher)
{
if (GnuAttributeAST *_other = pattern->asGnuAttribute())
@@ -96,6 +112,21 @@ bool DecltypeSpecifierAST::match0(AST *pattern, ASTMatcher *matcher)
return false;
}
+bool TypeConstraintAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto _other = pattern->asTypeConstraint())
+ return matcher->match(this, _other);
+
+ return false;
+}
+
+bool PlaceholderTypeSpecifierAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto _other = pattern->asPlaceholderTypeSpecifier())
+ return matcher->match(this, _other);
+ return false;
+}
+
bool DeclaratorAST::match0(AST *pattern, ASTMatcher *matcher)
{
if (DeclaratorAST *_other = pattern->asDeclarator())
@@ -336,6 +367,14 @@ bool DeclaratorIdAST::match0(AST *pattern, ASTMatcher *matcher)
return false;
}
+bool DecompositionDeclaratorAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (DecompositionDeclaratorAST *_other = pattern->asDecompositionDeclarator())
+ return matcher->match(this, _other);
+
+ return false;
+}
+
bool NestedDeclaratorAST::match0(AST *pattern, ASTMatcher *matcher)
{
if (NestedDeclaratorAST *_other = pattern->asNestedDeclarator())
@@ -872,6 +911,28 @@ bool TemplateDeclarationAST::match0(AST *pattern, ASTMatcher *matcher)
return false;
}
+bool ConceptDeclarationAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (ConceptDeclarationAST *_other = pattern->asConceptDeclaration())
+ return matcher->match(this, _other);
+
+ return false;
+}
+
+bool RequiresExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto other = pattern->asRequiresExpression())
+ return matcher->match(this, other);
+ return false;
+}
+
+bool RequiresClauseAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto other = pattern->asRequiresClause())
+ return matcher->match(this, other);
+ return false;
+}
+
bool ThrowExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
{
if (ThrowExpressionAST *_other = pattern->asThrowExpression())
@@ -880,6 +941,20 @@ bool ThrowExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
return false;
}
+bool YieldExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto other = pattern->asYieldExpression())
+ return matcher->match(this, other);
+ return false;
+}
+
+bool AwaitExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
+{
+ if (const auto other = pattern->asAwaitExpression())
+ return matcher->match(this, other);
+ return false;
+}
+
bool NoExceptOperatorExpressionAST::match0(AST *pattern, ASTMatcher *matcher)
{
if (NoExceptOperatorExpressionAST *_other = pattern->asNoExceptOperatorExpression())
diff --git a/src/libs/3rdparty/cplusplus/ASTMatcher.cpp b/src/libs/3rdparty/cplusplus/ASTMatcher.cpp
index d76180a8ca..b264098a96 100644
--- a/src/libs/3rdparty/cplusplus/ASTMatcher.cpp
+++ b/src/libs/3rdparty/cplusplus/ASTMatcher.cpp
@@ -117,6 +117,46 @@ bool ASTMatcher::match(GnuAttributeSpecifierAST *node, GnuAttributeSpecifierAST
return true;
}
+bool ASTMatcher::match(MsvcDeclspecSpecifierAST *node, MsvcDeclspecSpecifierAST *pattern)
+{
+ (void) node;
+ (void) pattern;
+
+ pattern->attribute_token = node->attribute_token;
+
+ pattern->lparen_token = node->lparen_token;
+
+ if (! pattern->attribute_list)
+ pattern->attribute_list = node->attribute_list;
+ else if (! AST::match(node->attribute_list, pattern->attribute_list, this))
+ return false;
+
+ pattern->rparen_token = node->rparen_token;
+
+ return true;
+}
+
+bool ASTMatcher::match(StdAttributeSpecifierAST *node, StdAttributeSpecifierAST *pattern)
+{
+ (void) node;
+ (void) pattern;
+
+ pattern->first_lbracket_token = node->first_lbracket_token;
+
+ pattern->second_lbracket_token = node->second_lbracket_token;
+
+ if (! pattern->attribute_list)
+ pattern->attribute_list = node->attribute_list;
+ else if (! AST::match(node->attribute_list, pattern->attribute_list, this))
+ return false;
+
+ pattern->first_rbracket_token = node->first_rbracket_token;
+
+ pattern->second_rbracket_token = node->second_rbracket_token;
+
+ return true;
+}
+
bool ASTMatcher::match(GnuAttributeAST *node, GnuAttributeAST *pattern)
{
(void) node;
@@ -176,6 +216,25 @@ bool ASTMatcher::match(DecltypeSpecifierAST *node, DecltypeSpecifierAST *pattern
return true;
}
+bool ASTMatcher::match(TypeConstraintAST *node, TypeConstraintAST *pattern)
+{
+ if (!pattern->nestedName)
+ pattern->nestedName = node->nestedName;
+ else if (!AST::match(node->nestedName, pattern->nestedName, this))
+ return false;
+ if (!pattern->conceptName)
+ pattern->conceptName = node->conceptName;
+ else if (!AST::match(node->conceptName, pattern->conceptName, this))
+ return false;
+ pattern->lessToken = node->lessToken;
+ if (!pattern->templateArgs)
+ pattern->templateArgs = node->templateArgs;
+ else if (!AST::match(node->templateArgs, pattern->templateArgs, this))
+ return false;
+ pattern->greaterToken = node->greaterToken;
+ return true;
+}
+
bool ASTMatcher::match(DeclaratorAST *node, DeclaratorAST *pattern)
{
(void) node;
@@ -796,6 +855,15 @@ bool ASTMatcher::match(DeclaratorIdAST *node, DeclaratorIdAST *pattern)
return true;
}
+bool ASTMatcher::match(DecompositionDeclaratorAST *node, DecompositionDeclaratorAST *pattern)
+{
+ if (!pattern->identifiers)
+ pattern->identifiers = node->identifiers;
+ else if (! AST::match(node->identifiers, pattern->identifiers, this))
+ return false;
+ return true;
+}
+
bool ASTMatcher::match(NestedDeclaratorAST *node, NestedDeclaratorAST *pattern)
{
(void) node;
@@ -1246,8 +1314,15 @@ bool ASTMatcher::match(IfStatementAST *node, IfStatementAST *pattern)
pattern->if_token = node->if_token;
+ pattern->constexpr_token = node->constexpr_token;
+
pattern->lparen_token = node->lparen_token;
+ if (!pattern->initStmt)
+ pattern->initStmt = node->initStmt;
+ else if (!AST::match(node->initStmt, pattern->initStmt, this))
+ return false;
+
if (! pattern->condition)
pattern->condition = node->condition;
else if (! AST::match(node->condition, pattern->condition, this))
@@ -1698,6 +1773,19 @@ bool ASTMatcher::match(ParameterDeclarationClauseAST *node, ParameterDeclaration
return true;
}
+bool ASTMatcher::match(PlaceholderTypeSpecifierAST *node, PlaceholderTypeSpecifierAST *pattern)
+{
+ if (!pattern->typeConstraint)
+ pattern->typeConstraint = node->typeConstraint;
+ else if (!AST::match(node->typeConstraint, pattern->typeConstraint, this))
+ return false;
+ pattern->declTypetoken = node->declTypetoken;
+ pattern->lparenToken = node->lparenToken;
+ pattern->autoToken = node->autoToken;
+ pattern->rparenToken = node->rparenToken;
+ return true;
+}
+
bool ASTMatcher::match(CallAST *node, CallAST *pattern)
{
(void) node;
@@ -2122,6 +2210,11 @@ bool ASTMatcher::match(TemplateDeclarationAST *node, TemplateDeclarationAST *pat
pattern->greater_token = node->greater_token;
+ if (! pattern->requiresClause)
+ pattern->requiresClause = node->requiresClause;
+ else if (! AST::match(node->requiresClause, pattern->requiresClause, this))
+ return false;
+
if (! pattern->declaration)
pattern->declaration = node->declaration;
else if (! AST::match(node->declaration, pattern->declaration, this))
@@ -2130,6 +2223,50 @@ bool ASTMatcher::match(TemplateDeclarationAST *node, TemplateDeclarationAST *pat
return true;
}
+bool ASTMatcher::match(ConceptDeclarationAST *node, ConceptDeclarationAST *pattern)
+{
+ pattern->concept_token = node->concept_token;
+ pattern->equals_token = node->equals_token;
+ pattern->semicolon_token = node->semicolon_token;
+
+ if (!pattern->attributes)
+ pattern->attributes = node->attributes;
+ else if (!AST::match(node->attributes, pattern->attributes, this))
+ return false;
+
+ if (!pattern->constraint)
+ pattern->constraint = node->constraint;
+ else if (! AST::match(node->constraint, pattern->constraint, this))
+ return false;
+
+ return true;
+}
+
+bool ASTMatcher::match(RequiresExpressionAST *node, RequiresExpressionAST *pattern)
+{
+ pattern->requires_token = node->requires_token;
+ pattern->lparen_token = node->lparen_token;
+ pattern->lbrace_token = node->lbrace_token;
+ pattern->rbrace_token = node->rbrace_token;
+
+ if (!pattern->parameters)
+ pattern->parameters = node->parameters;
+ else if (!AST::match(node->parameters, pattern->parameters, this))
+ return false;
+
+ return true;
+}
+
+bool ASTMatcher::match(RequiresClauseAST *node, RequiresClauseAST *pattern)
+{
+ pattern->requires_token = node->requires_token;
+ if (!pattern->constraint)
+ pattern->constraint = node->constraint;
+ else if (!AST::match(node->constraint, pattern->constraint, this))
+ return false;
+ return true;
+}
+
bool ASTMatcher::match(ThrowExpressionAST *node, ThrowExpressionAST *pattern)
{
(void) node;
@@ -2145,6 +2282,26 @@ bool ASTMatcher::match(ThrowExpressionAST *node, ThrowExpressionAST *pattern)
return true;
}
+bool ASTMatcher::match(YieldExpressionAST *node, YieldExpressionAST *pattern)
+{
+ pattern->yield_token = node->yield_token;
+ if (!pattern->expression)
+ pattern->expression = node->expression;
+ else if (!AST::match(node->expression, pattern->expression, this))
+ return false;
+ return true;
+}
+
+bool ASTMatcher::match(AwaitExpressionAST *node, AwaitExpressionAST *pattern)
+{
+ pattern->await_token = node->await_token;
+ if (!pattern->castExpression)
+ pattern->castExpression = node->castExpression;
+ else if (!AST::match(node->castExpression, pattern->castExpression, this))
+ return false;
+ return true;
+}
+
bool ASTMatcher::match(NoExceptOperatorExpressionAST *node, NoExceptOperatorExpressionAST *pattern)
{
(void) node;
@@ -2266,6 +2423,11 @@ bool ASTMatcher::match(TemplateTypeParameterAST *node, TemplateTypeParameterAST
pattern->template_token = node->template_token;
+ if (!pattern->typeConstraint)
+ pattern->typeConstraint = node->typeConstraint;
+ else if (!AST::match(node->typeConstraint, pattern->typeConstraint, this))
+ return false;
+
pattern->less_token = node->less_token;
if (! pattern->template_parameter_list)
@@ -2899,6 +3061,21 @@ bool ASTMatcher::match(LambdaExpressionAST *node, LambdaExpressionAST *pattern)
else if (! AST::match(node->lambda_introducer, pattern->lambda_introducer, this))
return false;
+ if (! pattern->templateParameters)
+ pattern->templateParameters = node->templateParameters;
+ else if (! AST::match(node->templateParameters, pattern->templateParameters, this))
+ return false;
+
+ if (! pattern->requiresClause)
+ pattern->requiresClause = node->requiresClause;
+ else if (! AST::match(node->requiresClause, pattern->requiresClause, this))
+ return false;
+
+ if (! pattern->attributes)
+ pattern->attributes = node->attributes;
+ else if (! AST::match(node->attributes, pattern->attributes, this))
+ return false;
+
if (! pattern->lambda_declarator)
pattern->lambda_declarator = node->lambda_declarator;
else if (! AST::match(node->lambda_declarator, pattern->lambda_declarator, this))
@@ -2990,6 +3167,11 @@ bool ASTMatcher::match(LambdaDeclaratorAST *node, LambdaDeclaratorAST *pattern)
else if (! AST::match(node->trailing_return_type, pattern->trailing_return_type, this))
return false;
+ if (! pattern->requiresClause)
+ pattern->requiresClause = node->requiresClause;
+ else if (! AST::match(node->requiresClause, pattern->requiresClause, this))
+ return false;
+
return true;
}
diff --git a/src/libs/3rdparty/cplusplus/ASTMatcher.h b/src/libs/3rdparty/cplusplus/ASTMatcher.h
index f6f3eae8d9..fb6109a07d 100644
--- a/src/libs/3rdparty/cplusplus/ASTMatcher.h
+++ b/src/libs/3rdparty/cplusplus/ASTMatcher.h
@@ -38,6 +38,7 @@ public:
virtual bool match(ArrayAccessAST *node, ArrayAccessAST *pattern);
virtual bool match(ArrayDeclaratorAST *node, ArrayDeclaratorAST *pattern);
virtual bool match(ArrayInitializerAST *node, ArrayInitializerAST *pattern);
+ virtual bool match(AwaitExpressionAST *node, AwaitExpressionAST *pattern);
virtual bool match(AsmDefinitionAST *node, AsmDefinitionAST *pattern);
virtual bool match(BaseSpecifierAST *node, BaseSpecifierAST *pattern);
virtual bool match(BinaryExpressionAST *node, BinaryExpressionAST *pattern);
@@ -54,6 +55,7 @@ public:
virtual bool match(CompoundExpressionAST *node, CompoundExpressionAST *pattern);
virtual bool match(CompoundLiteralAST *node, CompoundLiteralAST *pattern);
virtual bool match(CompoundStatementAST *node, CompoundStatementAST *pattern);
+ virtual bool match(ConceptDeclarationAST *node, ConceptDeclarationAST *pattern);
virtual bool match(ConditionAST *node, ConditionAST *pattern);
virtual bool match(ConditionalExpressionAST *node, ConditionalExpressionAST *pattern);
virtual bool match(ContinueStatementAST *node, ContinueStatementAST *pattern);
@@ -63,6 +65,7 @@ public:
virtual bool match(DeclarationStatementAST *node, DeclarationStatementAST *pattern);
virtual bool match(DeclaratorAST *node, DeclaratorAST *pattern);
virtual bool match(DeclaratorIdAST *node, DeclaratorIdAST *pattern);
+ virtual bool match(DecompositionDeclaratorAST *node, DecompositionDeclaratorAST *pattern);
virtual bool match(DecltypeSpecifierAST *node, DecltypeSpecifierAST *pattern);
virtual bool match(DeleteExpressionAST *node, DeleteExpressionAST *pattern);
virtual bool match(DesignatedInitializerAST *node, DesignatedInitializerAST *pattern);
@@ -96,6 +99,7 @@ public:
virtual bool match(LinkageSpecificationAST *node, LinkageSpecificationAST *pattern);
virtual bool match(MemInitializerAST *node, MemInitializerAST *pattern);
virtual bool match(MemberAccessAST *node, MemberAccessAST *pattern);
+ virtual bool match(MsvcDeclspecSpecifierAST *node, MsvcDeclspecSpecifierAST *pattern);
virtual bool match(NamedTypeSpecifierAST *node, NamedTypeSpecifierAST *pattern);
virtual bool match(NamespaceAST *node, NamespaceAST *pattern);
virtual bool match(NamespaceAliasDefinitionAST *node, NamespaceAliasDefinitionAST *pattern);
@@ -137,6 +141,7 @@ public:
virtual bool match(OperatorFunctionIdAST *node, OperatorFunctionIdAST *pattern);
virtual bool match(ParameterDeclarationAST *node, ParameterDeclarationAST *pattern);
virtual bool match(ParameterDeclarationClauseAST *node, ParameterDeclarationClauseAST *pattern);
+ virtual bool match(PlaceholderTypeSpecifierAST *node, PlaceholderTypeSpecifierAST *pattern);
virtual bool match(PointerAST *node, PointerAST *pattern);
virtual bool match(PointerLiteralAST *node, PointerLiteralAST *pattern);
virtual bool match(PointerToMemberAST *node, PointerToMemberAST *pattern);
@@ -154,12 +159,15 @@ public:
virtual bool match(QualifiedNameAST *node, QualifiedNameAST *pattern);
virtual bool match(RangeBasedForStatementAST *node, RangeBasedForStatementAST *pattern);
virtual bool match(ReferenceAST *node, ReferenceAST *pattern);
+ virtual bool match(RequiresClauseAST *node, RequiresClauseAST *pattern);
+ virtual bool match(RequiresExpressionAST *node, RequiresExpressionAST *pattern);
virtual bool match(ReturnStatementAST *node, ReturnStatementAST *pattern);
virtual bool match(SimpleDeclarationAST *node, SimpleDeclarationAST *pattern);
virtual bool match(SimpleNameAST *node, SimpleNameAST *pattern);
virtual bool match(SimpleSpecifierAST *node, SimpleSpecifierAST *pattern);
virtual bool match(SizeofExpressionAST *node, SizeofExpressionAST *pattern);
virtual bool match(StaticAssertDeclarationAST *node, StaticAssertDeclarationAST *pattern);
+ virtual bool match(StdAttributeSpecifierAST *node, StdAttributeSpecifierAST *pattern);
virtual bool match(StringLiteralAST *node, StringLiteralAST *pattern);
virtual bool match(SwitchStatementAST *node, SwitchStatementAST *pattern);
virtual bool match(TemplateDeclarationAST *node, TemplateDeclarationAST *pattern);
@@ -170,6 +178,7 @@ public:
virtual bool match(TrailingReturnTypeAST *node, TrailingReturnTypeAST *pattern);
virtual bool match(TranslationUnitAST *node, TranslationUnitAST *pattern);
virtual bool match(TryBlockStatementAST *node, TryBlockStatementAST *pattern);
+ virtual bool match(TypeConstraintAST *node, TypeConstraintAST *pattern);
virtual bool match(TypeConstructorCallAST *node, TypeConstructorCallAST *pattern);
virtual bool match(TypeIdAST *node, TypeIdAST *pattern);
virtual bool match(TypeidExpressionAST *node, TypeidExpressionAST *pattern);
@@ -180,6 +189,7 @@ public:
virtual bool match(UsingAST *node, UsingAST *pattern);
virtual bool match(UsingDirectiveAST *node, UsingDirectiveAST *pattern);
virtual bool match(WhileStatementAST *node, WhileStatementAST *pattern);
+ virtual bool match(YieldExpressionAST *node, YieldExpressionAST *pattern);
};
} // namespace CPlusPlus
diff --git a/src/libs/3rdparty/cplusplus/ASTPatternBuilder.h b/src/libs/3rdparty/cplusplus/ASTPatternBuilder.h
index 9270a3429c..1e23ed6b54 100644
--- a/src/libs/3rdparty/cplusplus/ASTPatternBuilder.h
+++ b/src/libs/3rdparty/cplusplus/ASTPatternBuilder.h
@@ -49,7 +49,7 @@ public:
return ast;
}
- ObjCSelectorAST *ObjCSelector(ObjCSelectorArgumentListAST *selector_argument_list = 0)
+ ObjCSelectorAST *ObjCSelector(ObjCSelectorArgumentListAST *selector_argument_list = nullptr)
{
ObjCSelectorAST *ast = new (&pool) ObjCSelectorAST;
ast->selector_argument_list = selector_argument_list;
@@ -62,42 +62,56 @@ public:
return ast;
}
- AlignmentSpecifierAST *AlignmentSpecifier(ExpressionAST *typeIdExprOrAlignmentExpr = 0)
+ AlignmentSpecifierAST *AlignmentSpecifier(ExpressionAST *typeIdExprOrAlignmentExpr = nullptr)
{
AlignmentSpecifierAST *ast = new (&pool) AlignmentSpecifierAST;
ast->typeIdExprOrAlignmentExpr = typeIdExprOrAlignmentExpr;
return ast;
}
- GnuAttributeSpecifierAST *GnuAttributeSpecifier(GnuAttributeListAST *attribute_list = 0)
+ GnuAttributeSpecifierAST *GnuAttributeSpecifier(GnuAttributeListAST *attribute_list = nullptr)
{
GnuAttributeSpecifierAST *ast = new (&pool) GnuAttributeSpecifierAST;
ast->attribute_list = attribute_list;
return ast;
}
- GnuAttributeAST *GnuAttribute(ExpressionListAST *expression_list = 0)
+ MsvcDeclspecSpecifierAST *MsvcDeclspecSpecifier(GnuAttributeListAST *attribute_list = nullptr)
+ {
+ MsvcDeclspecSpecifierAST *ast = new (&pool) MsvcDeclspecSpecifierAST;
+ ast->attribute_list = attribute_list;
+ return ast;
+ }
+
+ StdAttributeSpecifierAST *StdAttributeSpecifier(GnuAttributeListAST *attribute_list = nullptr)
+ {
+ StdAttributeSpecifierAST *ast = new (&pool) StdAttributeSpecifierAST;
+ ast->attribute_list = attribute_list;
+ return ast;
+ }
+
+ GnuAttributeAST *GnuAttribute(ExpressionListAST *expression_list = nullptr)
{
GnuAttributeAST *ast = new (&pool) GnuAttributeAST;
ast->expression_list = expression_list;
return ast;
}
- TypeofSpecifierAST *TypeofSpecifier(ExpressionAST *expression = 0)
+ TypeofSpecifierAST *TypeofSpecifier(ExpressionAST *expression = nullptr)
{
TypeofSpecifierAST *ast = new (&pool) TypeofSpecifierAST;
ast->expression = expression;
return ast;
}
- DecltypeSpecifierAST *DecltypeSpecifier(ExpressionAST *expression = 0)
+ DecltypeSpecifierAST *DecltypeSpecifier(ExpressionAST *expression = nullptr)
{
DecltypeSpecifierAST *ast = new (&pool) DecltypeSpecifierAST;
ast->expression = expression;
return ast;
}
- DeclaratorAST *Declarator(SpecifierListAST *attribute_list = 0, PtrOperatorListAST *ptr_operator_list = 0, CoreDeclaratorAST *core_declarator = 0, PostfixDeclaratorListAST *postfix_declarator_list = 0, SpecifierListAST *post_attribute_list = 0, ExpressionAST *initializer = 0)
+ DeclaratorAST *Declarator(SpecifierListAST *attribute_list = nullptr, PtrOperatorListAST *ptr_operator_list = nullptr, CoreDeclaratorAST *core_declarator = nullptr, PostfixDeclaratorListAST *postfix_declarator_list = nullptr, SpecifierListAST *post_attribute_list = nullptr, ExpressionAST *initializer = nullptr)
{
DeclaratorAST *ast = new (&pool) DeclaratorAST;
ast->attribute_list = attribute_list;
@@ -109,7 +123,7 @@ public:
return ast;
}
- SimpleDeclarationAST *SimpleDeclaration(SpecifierListAST *decl_specifier_list = 0, DeclaratorListAST *declarator_list = 0)
+ SimpleDeclarationAST *SimpleDeclaration(SpecifierListAST *decl_specifier_list = nullptr, DeclaratorListAST *declarator_list = nullptr)
{
SimpleDeclarationAST *ast = new (&pool) SimpleDeclarationAST;
ast->decl_specifier_list = decl_specifier_list;
@@ -135,7 +149,7 @@ public:
return ast;
}
- QtPrivateSlotAST *QtPrivateSlot(SpecifierListAST *type_specifier_list = 0, DeclaratorAST *declarator = 0)
+ QtPrivateSlotAST *QtPrivateSlot(SpecifierListAST *type_specifier_list = nullptr, DeclaratorAST *declarator = nullptr)
{
QtPrivateSlotAST *ast = new (&pool) QtPrivateSlotAST;
ast->type_specifier_list = type_specifier_list;
@@ -143,14 +157,14 @@ public:
return ast;
}
- QtPropertyDeclarationItemAST *QtPropertyDeclarationItem(ExpressionAST *expression = 0)
+ QtPropertyDeclarationItemAST *QtPropertyDeclarationItem(ExpressionAST *expression = nullptr)
{
QtPropertyDeclarationItemAST *ast = new (&pool) QtPropertyDeclarationItemAST;
ast->expression = expression;
return ast;
}
- QtPropertyDeclarationAST *QtPropertyDeclaration(ExpressionAST *expression = 0, ExpressionAST *type_id = 0, NameAST *property_name = 0, QtPropertyDeclarationItemListAST *property_declaration_item_list = 0)
+ QtPropertyDeclarationAST *QtPropertyDeclaration(ExpressionAST *expression = nullptr, ExpressionAST *type_id = nullptr, NameAST *property_name = nullptr, QtPropertyDeclarationItemListAST *property_declaration_item_list = nullptr)
{
QtPropertyDeclarationAST *ast = new (&pool) QtPropertyDeclarationAST;
ast->expression = expression;
@@ -160,21 +174,21 @@ public:
return ast;
}
- QtEnumDeclarationAST *QtEnumDeclaration(NameListAST *enumerator_list = 0)
+ QtEnumDeclarationAST *QtEnumDeclaration(NameListAST *enumerator_list = nullptr)
{
QtEnumDeclarationAST *ast = new (&pool) QtEnumDeclarationAST;
ast->enumerator_list = enumerator_list;
return ast;
}
- QtFlagsDeclarationAST *QtFlagsDeclaration(NameListAST *flag_enums_list = 0)
+ QtFlagsDeclarationAST *QtFlagsDeclaration(NameListAST *flag_enums_list = nullptr)
{
QtFlagsDeclarationAST *ast = new (&pool) QtFlagsDeclarationAST;
ast->flag_enums_list = flag_enums_list;
return ast;
}
- QtInterfaceNameAST *QtInterfaceName(NameAST *interface_name = 0, NameListAST *constraint_list = 0)
+ QtInterfaceNameAST *QtInterfaceName(NameAST *interface_name = nullptr, NameListAST *constraint_list = nullptr)
{
QtInterfaceNameAST *ast = new (&pool) QtInterfaceNameAST;
ast->interface_name = interface_name;
@@ -182,7 +196,7 @@ public:
return ast;
}
- QtInterfacesDeclarationAST *QtInterfacesDeclaration(QtInterfaceNameListAST *interface_name_list = 0)
+ QtInterfacesDeclarationAST *QtInterfacesDeclaration(QtInterfaceNameListAST *interface_name_list = nullptr)
{
QtInterfacesDeclarationAST *ast = new (&pool) QtInterfacesDeclarationAST;
ast->interface_name_list = interface_name_list;
@@ -195,28 +209,28 @@ public:
return ast;
}
- BaseSpecifierAST *BaseSpecifier(NameAST *name = 0)
+ BaseSpecifierAST *BaseSpecifier(NameAST *name = nullptr)
{
BaseSpecifierAST *ast = new (&pool) BaseSpecifierAST;
ast->name = name;
return ast;
}
- IdExpressionAST *IdExpression(NameAST *name = 0)
+ IdExpressionAST *IdExpression(NameAST *name = nullptr)
{
IdExpressionAST *ast = new (&pool) IdExpressionAST;
ast->name = name;
return ast;
}
- CompoundExpressionAST *CompoundExpression(CompoundStatementAST *statement = 0)
+ CompoundExpressionAST *CompoundExpression(CompoundStatementAST *statement = nullptr)
{
CompoundExpressionAST *ast = new (&pool) CompoundExpressionAST;
ast->statement = statement;
return ast;
}
- CompoundLiteralAST *CompoundLiteral(ExpressionAST *type_id = 0, ExpressionAST *initializer = 0)
+ CompoundLiteralAST *CompoundLiteral(ExpressionAST *type_id = nullptr, ExpressionAST *initializer = nullptr)
{
CompoundLiteralAST *ast = new (&pool) CompoundLiteralAST;
ast->type_id = type_id;
@@ -224,21 +238,21 @@ public:
return ast;
}
- QtMethodAST *QtMethod(DeclaratorAST *declarator = 0)
+ QtMethodAST *QtMethod(DeclaratorAST *declarator = nullptr)
{
QtMethodAST *ast = new (&pool) QtMethodAST;
ast->declarator = declarator;
return ast;
}
- QtMemberDeclarationAST *QtMemberDeclaration(ExpressionAST *type_id = 0)
+ QtMemberDeclarationAST *QtMemberDeclaration(ExpressionAST *type_id = nullptr)
{
QtMemberDeclarationAST *ast = new (&pool) QtMemberDeclarationAST;
ast->type_id = type_id;
return ast;
}
- BinaryExpressionAST *BinaryExpression(ExpressionAST *left_expression = 0, ExpressionAST *right_expression = 0)
+ BinaryExpressionAST *BinaryExpression(ExpressionAST *left_expression = nullptr, ExpressionAST *right_expression = nullptr)
{
BinaryExpressionAST *ast = new (&pool) BinaryExpressionAST;
ast->left_expression = left_expression;
@@ -246,7 +260,7 @@ public:
return ast;
}
- CastExpressionAST *CastExpression(ExpressionAST *type_id = 0, ExpressionAST *expression = 0)
+ CastExpressionAST *CastExpression(ExpressionAST *type_id = nullptr, ExpressionAST *expression = nullptr)
{
CastExpressionAST *ast = new (&pool) CastExpressionAST;
ast->type_id = type_id;
@@ -254,7 +268,7 @@ public:
return ast;
}
- ClassSpecifierAST *ClassSpecifier(SpecifierListAST *attribute_list = 0, NameAST *name = 0, BaseSpecifierListAST *base_clause_list = 0, DeclarationListAST *member_specifier_list = 0)
+ ClassSpecifierAST *ClassSpecifier(SpecifierListAST *attribute_list = nullptr, NameAST *name = nullptr, BaseSpecifierListAST *base_clause_list = nullptr, DeclarationListAST *member_specifier_list = nullptr)
{
ClassSpecifierAST *ast = new (&pool) ClassSpecifierAST;
ast->attribute_list = attribute_list;
@@ -264,7 +278,7 @@ public:
return ast;
}
- CaseStatementAST *CaseStatement(ExpressionAST *expression = 0, StatementAST *statement = 0)
+ CaseStatementAST *CaseStatement(ExpressionAST *expression = nullptr, StatementAST *statement = nullptr)
{
CaseStatementAST *ast = new (&pool) CaseStatementAST;
ast->expression = expression;
@@ -272,14 +286,14 @@ public:
return ast;
}
- CompoundStatementAST *CompoundStatement(StatementListAST *statement_list = 0)
+ CompoundStatementAST *CompoundStatement(StatementListAST *statement_list = nullptr)
{
CompoundStatementAST *ast = new (&pool) CompoundStatementAST;
ast->statement_list = statement_list;
return ast;
}
- ConditionAST *Condition(SpecifierListAST *type_specifier_list = 0, DeclaratorAST *declarator = 0)
+ ConditionAST *Condition(SpecifierListAST *type_specifier_list = nullptr, DeclaratorAST *declarator = nullptr)
{
ConditionAST *ast = new (&pool) ConditionAST;
ast->type_specifier_list = type_specifier_list;
@@ -287,7 +301,7 @@ public:
return ast;
}
- ConditionalExpressionAST *ConditionalExpression(ExpressionAST *condition = 0, ExpressionAST *left_expression = 0, ExpressionAST *right_expression = 0)
+ ConditionalExpressionAST *ConditionalExpression(ExpressionAST *condition = nullptr, ExpressionAST *left_expression = nullptr, ExpressionAST *right_expression = nullptr)
{
ConditionalExpressionAST *ast = new (&pool) ConditionalExpressionAST;
ast->condition = condition;
@@ -296,7 +310,7 @@ public:
return ast;
}
- CppCastExpressionAST *CppCastExpression(ExpressionAST *type_id = 0, ExpressionAST *expression = 0)
+ CppCastExpressionAST *CppCastExpression(ExpressionAST *type_id = nullptr, ExpressionAST *expression = nullptr)
{
CppCastExpressionAST *ast = new (&pool) CppCastExpressionAST;
ast->type_id = type_id;
@@ -304,35 +318,42 @@ public:
return ast;
}
- CtorInitializerAST *CtorInitializer(MemInitializerListAST *member_initializer_list = 0)
+ CtorInitializerAST *CtorInitializer(MemInitializerListAST *member_initializer_list = nullptr)
{
CtorInitializerAST *ast = new (&pool) CtorInitializerAST;
ast->member_initializer_list = member_initializer_list;
return ast;
}
- DeclarationStatementAST *DeclarationStatement(DeclarationAST *declaration = 0)
+ DeclarationStatementAST *DeclarationStatement(DeclarationAST *declaration = nullptr)
{
DeclarationStatementAST *ast = new (&pool) DeclarationStatementAST;
ast->declaration = declaration;
return ast;
}
- DeclaratorIdAST *DeclaratorId(NameAST *name = 0)
+ DeclaratorIdAST *DeclaratorId(NameAST *name = nullptr)
{
DeclaratorIdAST *ast = new (&pool) DeclaratorIdAST;
ast->name = name;
return ast;
}
- NestedDeclaratorAST *NestedDeclarator(DeclaratorAST *declarator = 0)
+ DecompositionDeclaratorAST *DecompositionDeclarator(NameListAST *names = nullptr)
+ {
+ const auto ast = new (&pool) DecompositionDeclaratorAST;
+ ast->identifiers = names;
+ return ast;
+ }
+
+ NestedDeclaratorAST *NestedDeclarator(DeclaratorAST *declarator = nullptr)
{
NestedDeclaratorAST *ast = new (&pool) NestedDeclaratorAST;
ast->declarator = declarator;
return ast;
}
- FunctionDeclaratorAST *FunctionDeclarator(ParameterDeclarationClauseAST *parameter_declaration_clause = 0, SpecifierListAST *cv_qualifier_list = 0, ExceptionSpecificationAST *exception_specification = 0, TrailingReturnTypeAST *trailing_return_type = 0, ExpressionAST *as_cpp_initializer = 0)
+ FunctionDeclaratorAST *FunctionDeclarator(ParameterDeclarationClauseAST *parameter_declaration_clause = nullptr, SpecifierListAST *cv_qualifier_list = nullptr, ExceptionSpecificationAST *exception_specification = nullptr, TrailingReturnTypeAST *trailing_return_type = nullptr, ExpressionAST *as_cpp_initializer = nullptr)
{
FunctionDeclaratorAST *ast = new (&pool) FunctionDeclaratorAST;
ast->parameter_declaration_clause = parameter_declaration_clause;
@@ -343,21 +364,21 @@ public:
return ast;
}
- ArrayDeclaratorAST *ArrayDeclarator(ExpressionAST *expression = 0)
+ ArrayDeclaratorAST *ArrayDeclarator(ExpressionAST *expression = nullptr)
{
ArrayDeclaratorAST *ast = new (&pool) ArrayDeclaratorAST;
ast->expression = expression;
return ast;
}
- DeleteExpressionAST *DeleteExpression(ExpressionAST *expression = 0)
+ DeleteExpressionAST *DeleteExpression(ExpressionAST *expression = nullptr)
{
DeleteExpressionAST *ast = new (&pool) DeleteExpressionAST;
ast->expression = expression;
return ast;
}
- DoStatementAST *DoStatement(StatementAST *statement = 0, ExpressionAST *expression = 0)
+ DoStatementAST *DoStatement(StatementAST *statement = nullptr, ExpressionAST *expression = nullptr)
{
DoStatementAST *ast = new (&pool) DoStatementAST;
ast->statement = statement;
@@ -365,14 +386,14 @@ public:
return ast;
}
- NamedTypeSpecifierAST *NamedTypeSpecifier(NameAST *name = 0)
+ NamedTypeSpecifierAST *NamedTypeSpecifier(NameAST *name = nullptr)
{
NamedTypeSpecifierAST *ast = new (&pool) NamedTypeSpecifierAST;
ast->name = name;
return ast;
}
- ElaboratedTypeSpecifierAST *ElaboratedTypeSpecifier(SpecifierListAST *attribute_list = 0, NameAST *name = 0)
+ ElaboratedTypeSpecifierAST *ElaboratedTypeSpecifier(SpecifierListAST *attribute_list = nullptr, NameAST *name = nullptr)
{
ElaboratedTypeSpecifierAST *ast = new (&pool) ElaboratedTypeSpecifierAST;
ast->attribute_list = attribute_list;
@@ -380,7 +401,7 @@ public:
return ast;
}
- EnumSpecifierAST *EnumSpecifier(NameAST *name = 0, SpecifierListAST *type_specifier_list = 0, EnumeratorListAST *enumerator_list = 0)
+ EnumSpecifierAST *EnumSpecifier(NameAST *name = nullptr, SpecifierListAST *type_specifier_list = nullptr, EnumeratorListAST *enumerator_list = nullptr)
{
EnumSpecifierAST *ast = new (&pool) EnumSpecifierAST;
ast->name = name;
@@ -389,14 +410,14 @@ public:
return ast;
}
- EnumeratorAST *Enumerator(ExpressionAST *expression = 0)
+ EnumeratorAST *Enumerator(ExpressionAST *expression = nullptr)
{
EnumeratorAST *ast = new (&pool) EnumeratorAST;
ast->expression = expression;
return ast;
}
- ExceptionDeclarationAST *ExceptionDeclaration(SpecifierListAST *type_specifier_list = 0, DeclaratorAST *declarator = 0)
+ ExceptionDeclarationAST *ExceptionDeclaration(SpecifierListAST *type_specifier_list = nullptr, DeclaratorAST *declarator = nullptr)
{
ExceptionDeclarationAST *ast = new (&pool) ExceptionDeclarationAST;
ast->type_specifier_list = type_specifier_list;
@@ -404,21 +425,21 @@ public:
return ast;
}
- DynamicExceptionSpecificationAST *DynamicExceptionSpecification(ExpressionListAST *type_id_list = 0)
+ DynamicExceptionSpecificationAST *DynamicExceptionSpecification(ExpressionListAST *type_id_list = nullptr)
{
DynamicExceptionSpecificationAST *ast = new (&pool) DynamicExceptionSpecificationAST;
ast->type_id_list = type_id_list;
return ast;
}
- NoExceptSpecificationAST *NoExceptSpecification(ExpressionAST *expression = 0)
+ NoExceptSpecificationAST *NoExceptSpecification(ExpressionAST *expression = nullptr)
{
NoExceptSpecificationAST *ast = new (&pool) NoExceptSpecificationAST;
ast->expression = expression;
return ast;
}
- ExpressionOrDeclarationStatementAST *ExpressionOrDeclarationStatement(ExpressionStatementAST *expression = 0, DeclarationStatementAST *declaration = 0)
+ ExpressionOrDeclarationStatementAST *ExpressionOrDeclarationStatement(ExpressionStatementAST *expression = nullptr, DeclarationStatementAST *declaration = nullptr)
{
ExpressionOrDeclarationStatementAST *ast = new (&pool) ExpressionOrDeclarationStatementAST;
ast->expression = expression;
@@ -426,14 +447,14 @@ public:
return ast;
}
- ExpressionStatementAST *ExpressionStatement(ExpressionAST *expression = 0)
+ ExpressionStatementAST *ExpressionStatement(ExpressionAST *expression = nullptr)
{
ExpressionStatementAST *ast = new (&pool) ExpressionStatementAST;
ast->expression = expression;
return ast;
}
- FunctionDefinitionAST *FunctionDefinition(SpecifierListAST *decl_specifier_list = 0, DeclaratorAST *declarator = 0, CtorInitializerAST *ctor_initializer = 0, StatementAST *function_body = 0)
+ FunctionDefinitionAST *FunctionDefinition(SpecifierListAST *decl_specifier_list = nullptr, DeclaratorAST *declarator = nullptr, CtorInitializerAST *ctor_initializer = nullptr, StatementAST *function_body = nullptr)
{
FunctionDefinitionAST *ast = new (&pool) FunctionDefinitionAST;
ast->decl_specifier_list = decl_specifier_list;
@@ -443,7 +464,7 @@ public:
return ast;
}
- ForeachStatementAST *ForeachStatement(SpecifierListAST *type_specifier_list = 0, DeclaratorAST *declarator = 0, ExpressionAST *initializer = 0, ExpressionAST *expression = 0, StatementAST *statement = 0)
+ ForeachStatementAST *ForeachStatement(SpecifierListAST *type_specifier_list = nullptr, DeclaratorAST *declarator = nullptr, ExpressionAST *initializer = nullptr, ExpressionAST *expression = nullptr, StatementAST *statement = nullptr)
{
ForeachStatementAST *ast = new (&pool) ForeachStatementAST;
ast->type_specifier_list = type_specifier_list;
@@ -454,7 +475,7 @@ public:
return ast;
}
- RangeBasedForStatementAST *RangeBasedForStatement(SpecifierListAST *type_specifier_list = 0, DeclaratorAST *declarator = 0, ExpressionAST *expression = 0, StatementAST *statement = 0)
+ RangeBasedForStatementAST *RangeBasedForStatement(SpecifierListAST *type_specifier_list = nullptr, DeclaratorAST *declarator = nullptr, ExpressionAST *expression = nullptr, StatementAST *statement = nullptr)
{
RangeBasedForStatementAST *ast = new (&pool) RangeBasedForStatementAST;
ast->type_specifier_list = type_specifier_list;
@@ -464,7 +485,7 @@ public:
return ast;
}
- ForStatementAST *ForStatement(StatementAST *initializer = 0, ExpressionAST *condition = 0, ExpressionAST *expression = 0, StatementAST *statement = 0)
+ ForStatementAST *ForStatement(StatementAST *initializer = nullptr, ExpressionAST *condition = nullptr, ExpressionAST *expression = nullptr, StatementAST *statement = nullptr)
{
ForStatementAST *ast = new (&pool) ForStatementAST;
ast->initializer = initializer;
@@ -474,7 +495,7 @@ public:
return ast;
}
- IfStatementAST *IfStatement(ExpressionAST *condition = 0, StatementAST *statement = 0, StatementAST *else_statement = 0)
+ IfStatementAST *IfStatement(ExpressionAST *condition = nullptr, StatementAST *statement = nullptr, StatementAST *else_statement = nullptr)
{
IfStatementAST *ast = new (&pool) IfStatementAST;
ast->condition = condition;
@@ -483,35 +504,35 @@ public:
return ast;
}
- ArrayInitializerAST *ArrayInitializer(ExpressionListAST *expression_list = 0)
+ ArrayInitializerAST *ArrayInitializer(ExpressionListAST *expression_list = nullptr)
{
ArrayInitializerAST *ast = new (&pool) ArrayInitializerAST;
ast->expression_list = expression_list;
return ast;
}
- LabeledStatementAST *LabeledStatement(StatementAST *statement = 0)
+ LabeledStatementAST *LabeledStatement(StatementAST *statement = nullptr)
{
LabeledStatementAST *ast = new (&pool) LabeledStatementAST;
ast->statement = statement;
return ast;
}
- LinkageBodyAST *LinkageBody(DeclarationListAST *declaration_list = 0)
+ LinkageBodyAST *LinkageBody(DeclarationListAST *declaration_list = nullptr)
{
LinkageBodyAST *ast = new (&pool) LinkageBodyAST;
ast->declaration_list = declaration_list;
return ast;
}
- LinkageSpecificationAST *LinkageSpecification(DeclarationAST *declaration = 0)
+ LinkageSpecificationAST *LinkageSpecification(DeclarationAST *declaration = nullptr)
{
LinkageSpecificationAST *ast = new (&pool) LinkageSpecificationAST;
ast->declaration = declaration;
return ast;
}
- MemInitializerAST *MemInitializer(NameAST *name = 0, ExpressionAST *expression = 0)
+ MemInitializerAST *MemInitializer(NameAST *name = nullptr, ExpressionAST *expression = nullptr)
{
MemInitializerAST *ast = new (&pool) MemInitializerAST;
ast->name = name;
@@ -519,14 +540,14 @@ public:
return ast;
}
- NestedNameSpecifierAST *NestedNameSpecifier(NameAST *class_or_namespace_name = 0)
+ NestedNameSpecifierAST *NestedNameSpecifier(NameAST *class_or_namespace_name = nullptr)
{
NestedNameSpecifierAST *ast = new (&pool) NestedNameSpecifierAST;
ast->class_or_namespace_name = class_or_namespace_name;
return ast;
}
- QualifiedNameAST *QualifiedName(NestedNameSpecifierListAST *nested_name_specifier_list = 0, NameAST *unqualified_name = 0)
+ QualifiedNameAST *QualifiedName(NestedNameSpecifierListAST *nested_name_specifier_list = nullptr, NameAST *unqualified_name = nullptr)
{
QualifiedNameAST *ast = new (&pool) QualifiedNameAST;
ast->nested_name_specifier_list = nested_name_specifier_list;
@@ -534,14 +555,14 @@ public:
return ast;
}
- OperatorFunctionIdAST *OperatorFunctionId(OperatorAST *op = 0)
+ OperatorFunctionIdAST *OperatorFunctionId(OperatorAST *op = nullptr)
{
OperatorFunctionIdAST *ast = new (&pool) OperatorFunctionIdAST;
ast->op = op;
return ast;
}
- ConversionFunctionIdAST *ConversionFunctionId(SpecifierListAST *type_specifier_list = 0, PtrOperatorListAST *ptr_operator_list = 0)
+ ConversionFunctionIdAST *ConversionFunctionId(SpecifierListAST *type_specifier_list = nullptr, PtrOperatorListAST *ptr_operator_list = nullptr)
{
ConversionFunctionIdAST *ast = new (&pool) ConversionFunctionIdAST;
ast->type_specifier_list = type_specifier_list;
@@ -561,21 +582,21 @@ public:
return ast;
}
- DestructorNameAST *DestructorName(NameAST *unqualified_name = 0)
+ DestructorNameAST *DestructorName(NameAST *unqualified_name = nullptr)
{
DestructorNameAST *ast = new (&pool) DestructorNameAST;
ast->unqualified_name = unqualified_name;
return ast;
}
- TemplateIdAST *TemplateId(ExpressionListAST *template_argument_list = 0)
+ TemplateIdAST *TemplateId(ExpressionListAST *template_argument_list = nullptr)
{
TemplateIdAST *ast = new (&pool) TemplateIdAST;
ast->template_argument_list = template_argument_list;
return ast;
}
- NamespaceAST *Namespace(SpecifierListAST *attribute_list = 0, DeclarationAST *linkage_body = 0)
+ NamespaceAST *Namespace(SpecifierListAST *attribute_list = nullptr, DeclarationAST *linkage_body = nullptr)
{
NamespaceAST *ast = new (&pool) NamespaceAST;
ast->attribute_list = attribute_list;
@@ -583,14 +604,14 @@ public:
return ast;
}
- NamespaceAliasDefinitionAST *NamespaceAliasDefinition(NameAST *name = 0)
+ NamespaceAliasDefinitionAST *NamespaceAliasDefinition(NameAST *name = nullptr)
{
NamespaceAliasDefinitionAST *ast = new (&pool) NamespaceAliasDefinitionAST;
ast->name = name;
return ast;
}
- AliasDeclarationAST *AliasDeclaration(NameAST *name = 0, TypeIdAST *typeId = 0)
+ AliasDeclarationAST *AliasDeclaration(NameAST *name = nullptr, TypeIdAST *typeId = nullptr)
{
AliasDeclarationAST *ast = new (&pool) AliasDeclarationAST;
ast->name = name;
@@ -598,21 +619,21 @@ public:
return ast;
}
- ExpressionListParenAST *ExpressionListParen(ExpressionListAST *expression_list = 0)
+ ExpressionListParenAST *ExpressionListParen(ExpressionListAST *expression_list = nullptr)
{
ExpressionListParenAST *ast = new (&pool) ExpressionListParenAST;
ast->expression_list = expression_list;
return ast;
}
- NewArrayDeclaratorAST *NewArrayDeclarator(ExpressionAST *expression = 0)
+ NewArrayDeclaratorAST *NewArrayDeclarator(ExpressionAST *expression = nullptr)
{
NewArrayDeclaratorAST *ast = new (&pool) NewArrayDeclaratorAST;
ast->expression = expression;
return ast;
}
- NewExpressionAST *NewExpression(ExpressionListParenAST *new_placement = 0, ExpressionAST *type_id = 0, NewTypeIdAST *new_type_id = 0, ExpressionAST *new_initializer = 0)
+ NewExpressionAST *NewExpression(ExpressionListParenAST *new_placement = nullptr, ExpressionAST *type_id = nullptr, NewTypeIdAST *new_type_id = nullptr, ExpressionAST *new_initializer = nullptr)
{
NewExpressionAST *ast = new (&pool) NewExpressionAST;
ast->new_placement = new_placement;
@@ -622,7 +643,7 @@ public:
return ast;
}
- NewTypeIdAST *NewTypeId(SpecifierListAST *type_specifier_list = 0, PtrOperatorListAST *ptr_operator_list = 0, NewArrayDeclaratorListAST *new_array_declarator_list = 0)
+ NewTypeIdAST *NewTypeId(SpecifierListAST *type_specifier_list = nullptr, PtrOperatorListAST *ptr_operator_list = nullptr, NewArrayDeclaratorListAST *new_array_declarator_list = nullptr)
{
NewTypeIdAST *ast = new (&pool) NewTypeIdAST;
ast->type_specifier_list = type_specifier_list;
@@ -637,7 +658,7 @@ public:
return ast;
}
- ParameterDeclarationAST *ParameterDeclaration(SpecifierListAST *type_specifier_list = 0, DeclaratorAST *declarator = 0, ExpressionAST *expression = 0)
+ ParameterDeclarationAST *ParameterDeclaration(SpecifierListAST *type_specifier_list = nullptr, DeclaratorAST *declarator = nullptr, ExpressionAST *expression = nullptr)
{
ParameterDeclarationAST *ast = new (&pool) ParameterDeclarationAST;
ast->type_specifier_list = type_specifier_list;
@@ -646,14 +667,14 @@ public:
return ast;
}
- ParameterDeclarationClauseAST *ParameterDeclarationClause(ParameterDeclarationListAST *parameter_declaration_list = 0)
+ ParameterDeclarationClauseAST *ParameterDeclarationClause(ParameterDeclarationListAST *parameter_declaration_list = nullptr)
{
ParameterDeclarationClauseAST *ast = new (&pool) ParameterDeclarationClauseAST;
ast->parameter_declaration_list = parameter_declaration_list;
return ast;
}
- CallAST *Call(ExpressionAST *base_expression = 0, ExpressionListAST *expression_list = 0)
+ CallAST *Call(ExpressionAST *base_expression = nullptr, ExpressionListAST *expression_list = nullptr)
{
CallAST *ast = new (&pool) CallAST;
ast->base_expression = base_expression;
@@ -661,7 +682,7 @@ public:
return ast;
}
- ArrayAccessAST *ArrayAccess(ExpressionAST *base_expression = 0, ExpressionAST *expression = 0)
+ ArrayAccessAST *ArrayAccess(ExpressionAST *base_expression = nullptr, ExpressionAST *expression = nullptr)
{
ArrayAccessAST *ast = new (&pool) ArrayAccessAST;
ast->base_expression = base_expression;
@@ -669,14 +690,14 @@ public:
return ast;
}
- PostIncrDecrAST *PostIncrDecr(ExpressionAST *base_expression = 0)
+ PostIncrDecrAST *PostIncrDecr(ExpressionAST *base_expression = nullptr)
{
PostIncrDecrAST *ast = new (&pool) PostIncrDecrAST;
ast->base_expression = base_expression;
return ast;
}
- MemberAccessAST *MemberAccess(ExpressionAST *base_expression = 0, NameAST *member_name = 0)
+ MemberAccessAST *MemberAccess(ExpressionAST *base_expression = nullptr, NameAST *member_name = nullptr)
{
MemberAccessAST *ast = new (&pool) MemberAccessAST;
ast->base_expression = base_expression;
@@ -684,14 +705,14 @@ public:
return ast;
}
- TypeidExpressionAST *TypeidExpression(ExpressionAST *expression = 0)
+ TypeidExpressionAST *TypeidExpression(ExpressionAST *expression = nullptr)
{
TypeidExpressionAST *ast = new (&pool) TypeidExpressionAST;
ast->expression = expression;
return ast;
}
- TypenameCallExpressionAST *TypenameCallExpression(NameAST *name = 0, ExpressionAST *expression = 0)
+ TypenameCallExpressionAST *TypenameCallExpression(NameAST *name = nullptr, ExpressionAST *expression = nullptr)
{
TypenameCallExpressionAST *ast = new (&pool) TypenameCallExpressionAST;
ast->name = name;
@@ -699,7 +720,7 @@ public:
return ast;
}
- TypeConstructorCallAST *TypeConstructorCall(SpecifierListAST *type_specifier_list = 0, ExpressionAST *expression = 0)
+ TypeConstructorCallAST *TypeConstructorCall(SpecifierListAST *type_specifier_list = nullptr, ExpressionAST *expression = nullptr)
{
TypeConstructorCallAST *ast = new (&pool) TypeConstructorCallAST;
ast->type_specifier_list = type_specifier_list;
@@ -707,7 +728,7 @@ public:
return ast;
}
- PointerToMemberAST *PointerToMember(NestedNameSpecifierListAST *nested_name_specifier_list = 0, SpecifierListAST *cv_qualifier_list = 0)
+ PointerToMemberAST *PointerToMember(NestedNameSpecifierListAST *nested_name_specifier_list = nullptr, SpecifierListAST *cv_qualifier_list = nullptr)
{
PointerToMemberAST *ast = new (&pool) PointerToMemberAST;
ast->nested_name_specifier_list = nested_name_specifier_list;
@@ -715,7 +736,7 @@ public:
return ast;
}
- PointerAST *Pointer(SpecifierListAST *cv_qualifier_list = 0)
+ PointerAST *Pointer(SpecifierListAST *cv_qualifier_list = nullptr)
{
PointerAST *ast = new (&pool) PointerAST;
ast->cv_qualifier_list = cv_qualifier_list;
@@ -746,21 +767,21 @@ public:
return ast;
}
- ReturnStatementAST *ReturnStatement(ExpressionAST *expression = 0)
+ ReturnStatementAST *ReturnStatement(ExpressionAST *expression = nullptr)
{
ReturnStatementAST *ast = new (&pool) ReturnStatementAST;
ast->expression = expression;
return ast;
}
- SizeofExpressionAST *SizeofExpression(ExpressionAST *expression = 0)
+ SizeofExpressionAST *SizeofExpression(ExpressionAST *expression = nullptr)
{
SizeofExpressionAST *ast = new (&pool) SizeofExpressionAST;
ast->expression = expression;
return ast;
}
- AlignofExpressionAST *AlignofExpression(TypeIdAST *typeId = 0)
+ AlignofExpressionAST *AlignofExpression(TypeIdAST *typeId = nullptr)
{
AlignofExpressionAST *ast = new (&pool) AlignofExpressionAST;
ast->typeId = typeId;
@@ -791,14 +812,14 @@ public:
return ast;
}
- NestedExpressionAST *NestedExpression(ExpressionAST *expression = 0)
+ NestedExpressionAST *NestedExpression(ExpressionAST *expression = nullptr)
{
NestedExpressionAST *ast = new (&pool) NestedExpressionAST;
ast->expression = expression;
return ast;
}
- StaticAssertDeclarationAST *StaticAssertDeclaration(ExpressionAST *expression = 0, ExpressionAST *string_literal = 0)
+ StaticAssertDeclarationAST *StaticAssertDeclaration(ExpressionAST *expression = nullptr, ExpressionAST *string_literal = nullptr)
{
StaticAssertDeclarationAST *ast = new (&pool) StaticAssertDeclarationAST;
ast->expression = expression;
@@ -806,14 +827,14 @@ public:
return ast;
}
- StringLiteralAST *StringLiteral(StringLiteralAST *next = 0)
+ StringLiteralAST *StringLiteral(StringLiteralAST *next = nullptr)
{
StringLiteralAST *ast = new (&pool) StringLiteralAST;
ast->next = next;
return ast;
}
- SwitchStatementAST *SwitchStatement(ExpressionAST *condition = 0, StatementAST *statement = 0)
+ SwitchStatementAST *SwitchStatement(ExpressionAST *condition = nullptr, StatementAST *statement = nullptr)
{
SwitchStatementAST *ast = new (&pool) SwitchStatementAST;
ast->condition = condition;
@@ -821,7 +842,7 @@ public:
return ast;
}
- TemplateDeclarationAST *TemplateDeclaration(DeclarationListAST *template_parameter_list = 0, DeclarationAST *declaration = 0)
+ TemplateDeclarationAST *TemplateDeclaration(DeclarationListAST *template_parameter_list = nullptr, DeclarationAST *declaration = nullptr)
{
TemplateDeclarationAST *ast = new (&pool) TemplateDeclarationAST;
ast->template_parameter_list = template_parameter_list;
@@ -829,28 +850,28 @@ public:
return ast;
}
- ThrowExpressionAST *ThrowExpression(ExpressionAST *expression = 0)
+ ThrowExpressionAST *ThrowExpression(ExpressionAST *expression = nullptr)
{
ThrowExpressionAST *ast = new (&pool) ThrowExpressionAST;
ast->expression = expression;
return ast;
}
- NoExceptOperatorExpressionAST *NoExceptOperatorExpression(ExpressionAST *expression = 0)
+ NoExceptOperatorExpressionAST *NoExceptOperatorExpression(ExpressionAST *expression = nullptr)
{
NoExceptOperatorExpressionAST *ast = new (&pool) NoExceptOperatorExpressionAST;
ast->expression = expression;
return ast;
}
- TranslationUnitAST *TranslationUnit(DeclarationListAST *declaration_list = 0)
+ TranslationUnitAST *TranslationUnit(DeclarationListAST *declaration_list = nullptr)
{
TranslationUnitAST *ast = new (&pool) TranslationUnitAST;
ast->declaration_list = declaration_list;
return ast;
}
- TryBlockStatementAST *TryBlockStatement(StatementAST *statement = 0, CatchClauseListAST *catch_clause_list = 0)
+ TryBlockStatementAST *TryBlockStatement(StatementAST *statement = nullptr, CatchClauseListAST *catch_clause_list = nullptr)
{
TryBlockStatementAST *ast = new (&pool) TryBlockStatementAST;
ast->statement = statement;
@@ -858,7 +879,7 @@ public:
return ast;
}
- CatchClauseAST *CatchClause(ExceptionDeclarationAST *exception_declaration = 0, StatementAST *statement = 0)
+ CatchClauseAST *CatchClause(ExceptionDeclarationAST *exception_declaration = nullptr, StatementAST *statement = nullptr)
{
CatchClauseAST *ast = new (&pool) CatchClauseAST;
ast->exception_declaration = exception_declaration;
@@ -866,7 +887,7 @@ public:
return ast;
}
- TypeIdAST *TypeId(SpecifierListAST *type_specifier_list = 0, DeclaratorAST *declarator = 0)
+ TypeIdAST *TypeId(SpecifierListAST *type_specifier_list = nullptr, DeclaratorAST *declarator = nullptr)
{
TypeIdAST *ast = new (&pool) TypeIdAST;
ast->type_specifier_list = type_specifier_list;
@@ -874,7 +895,7 @@ public:
return ast;
}
- TypenameTypeParameterAST *TypenameTypeParameter(NameAST *name = 0, ExpressionAST *type_id = 0)
+ TypenameTypeParameterAST *TypenameTypeParameter(NameAST *name = nullptr, ExpressionAST *type_id = nullptr)
{
TypenameTypeParameterAST *ast = new (&pool) TypenameTypeParameterAST;
ast->name = name;
@@ -882,7 +903,7 @@ public:
return ast;
}
- TemplateTypeParameterAST *TemplateTypeParameter(DeclarationListAST *template_parameter_list = 0, NameAST *name = 0, ExpressionAST *type_id = 0)
+ TemplateTypeParameterAST *TemplateTypeParameter(DeclarationListAST *template_parameter_list = nullptr, NameAST *name = nullptr, ExpressionAST *type_id = nullptr)
{
TemplateTypeParameterAST *ast = new (&pool) TemplateTypeParameterAST;
ast->template_parameter_list = template_parameter_list;
@@ -891,28 +912,28 @@ public:
return ast;
}
- UnaryExpressionAST *UnaryExpression(ExpressionAST *expression = 0)
+ UnaryExpressionAST *UnaryExpression(ExpressionAST *expression = nullptr)
{
UnaryExpressionAST *ast = new (&pool) UnaryExpressionAST;
ast->expression = expression;
return ast;
}
- UsingAST *Using(NameAST *name = 0)
+ UsingAST *Using(NameAST *name = nullptr)
{
UsingAST *ast = new (&pool) UsingAST;
ast->name = name;
return ast;
}
- UsingDirectiveAST *UsingDirective(NameAST *name = 0)
+ UsingDirectiveAST *UsingDirective(NameAST *name = nullptr)
{
UsingDirectiveAST *ast = new (&pool) UsingDirectiveAST;
ast->name = name;
return ast;
}
- WhileStatementAST *WhileStatement(ExpressionAST *condition = 0, StatementAST *statement = 0)
+ WhileStatementAST *WhileStatement(ExpressionAST *condition = nullptr, StatementAST *statement = nullptr)
{
WhileStatementAST *ast = new (&pool) WhileStatementAST;
ast->condition = condition;
@@ -920,7 +941,7 @@ public:
return ast;
}
- ObjCClassForwardDeclarationAST *ObjCClassForwardDeclaration(SpecifierListAST *attribute_list = 0, NameListAST *identifier_list = 0)
+ ObjCClassForwardDeclarationAST *ObjCClassForwardDeclaration(SpecifierListAST *attribute_list = nullptr, NameListAST *identifier_list = nullptr)
{
ObjCClassForwardDeclarationAST *ast = new (&pool) ObjCClassForwardDeclarationAST;
ast->attribute_list = attribute_list;
@@ -928,7 +949,7 @@ public:
return ast;
}
- ObjCClassDeclarationAST *ObjCClassDeclaration(SpecifierListAST *attribute_list = 0, NameAST *class_name = 0, NameAST *category_name = 0, NameAST *superclass = 0, ObjCProtocolRefsAST *protocol_refs = 0, ObjCInstanceVariablesDeclarationAST *inst_vars_decl = 0, DeclarationListAST *member_declaration_list = 0)
+ ObjCClassDeclarationAST *ObjCClassDeclaration(SpecifierListAST *attribute_list = nullptr, NameAST *class_name = nullptr, NameAST *category_name = nullptr, NameAST *superclass = nullptr, ObjCProtocolRefsAST *protocol_refs = nullptr, ObjCInstanceVariablesDeclarationAST *inst_vars_decl = nullptr, DeclarationListAST *member_declaration_list = nullptr)
{
ObjCClassDeclarationAST *ast = new (&pool) ObjCClassDeclarationAST;
ast->attribute_list = attribute_list;
@@ -941,7 +962,7 @@ public:
return ast;
}
- ObjCProtocolForwardDeclarationAST *ObjCProtocolForwardDeclaration(SpecifierListAST *attribute_list = 0, NameListAST *identifier_list = 0)
+ ObjCProtocolForwardDeclarationAST *ObjCProtocolForwardDeclaration(SpecifierListAST *attribute_list = nullptr, NameListAST *identifier_list = nullptr)
{
ObjCProtocolForwardDeclarationAST *ast = new (&pool) ObjCProtocolForwardDeclarationAST;
ast->attribute_list = attribute_list;
@@ -949,7 +970,7 @@ public:
return ast;
}
- ObjCProtocolDeclarationAST *ObjCProtocolDeclaration(SpecifierListAST *attribute_list = 0, NameAST *name = 0, ObjCProtocolRefsAST *protocol_refs = 0, DeclarationListAST *member_declaration_list = 0)
+ ObjCProtocolDeclarationAST *ObjCProtocolDeclaration(SpecifierListAST *attribute_list = nullptr, NameAST *name = nullptr, ObjCProtocolRefsAST *protocol_refs = nullptr, DeclarationListAST *member_declaration_list = nullptr)
{
ObjCProtocolDeclarationAST *ast = new (&pool) ObjCProtocolDeclarationAST;
ast->attribute_list = attribute_list;
@@ -959,21 +980,21 @@ public:
return ast;
}
- ObjCProtocolRefsAST *ObjCProtocolRefs(NameListAST *identifier_list = 0)
+ ObjCProtocolRefsAST *ObjCProtocolRefs(NameListAST *identifier_list = nullptr)
{
ObjCProtocolRefsAST *ast = new (&pool) ObjCProtocolRefsAST;
ast->identifier_list = identifier_list;
return ast;
}
- ObjCMessageArgumentAST *ObjCMessageArgument(ExpressionAST *parameter_value_expression = 0)
+ ObjCMessageArgumentAST *ObjCMessageArgument(ExpressionAST *parameter_value_expression = nullptr)
{
ObjCMessageArgumentAST *ast = new (&pool) ObjCMessageArgumentAST;
ast->parameter_value_expression = parameter_value_expression;
return ast;
}
- ObjCMessageExpressionAST *ObjCMessageExpression(ExpressionAST *receiver_expression = 0, ObjCSelectorAST *selector = 0, ObjCMessageArgumentListAST *argument_list = 0)
+ ObjCMessageExpressionAST *ObjCMessageExpression(ExpressionAST *receiver_expression = nullptr, ObjCSelectorAST *selector = nullptr, ObjCMessageArgumentListAST *argument_list = nullptr)
{
ObjCMessageExpressionAST *ast = new (&pool) ObjCMessageExpressionAST;
ast->receiver_expression = receiver_expression;
@@ -988,28 +1009,28 @@ public:
return ast;
}
- ObjCTypeNameAST *ObjCTypeName(ExpressionAST *type_id = 0)
+ ObjCTypeNameAST *ObjCTypeName(ExpressionAST *type_id = nullptr)
{
ObjCTypeNameAST *ast = new (&pool) ObjCTypeNameAST;
ast->type_id = type_id;
return ast;
}
- ObjCEncodeExpressionAST *ObjCEncodeExpression(ObjCTypeNameAST *type_name = 0)
+ ObjCEncodeExpressionAST *ObjCEncodeExpression(ObjCTypeNameAST *type_name = nullptr)
{
ObjCEncodeExpressionAST *ast = new (&pool) ObjCEncodeExpressionAST;
ast->type_name = type_name;
return ast;
}
- ObjCSelectorExpressionAST *ObjCSelectorExpression(ObjCSelectorAST *selector = 0)
+ ObjCSelectorExpressionAST *ObjCSelectorExpression(ObjCSelectorAST *selector = nullptr)
{
ObjCSelectorExpressionAST *ast = new (&pool) ObjCSelectorExpressionAST;
ast->selector = selector;
return ast;
}
- ObjCInstanceVariablesDeclarationAST *ObjCInstanceVariablesDeclaration(DeclarationListAST *instance_variable_list = 0)
+ ObjCInstanceVariablesDeclarationAST *ObjCInstanceVariablesDeclaration(DeclarationListAST *instance_variable_list = nullptr)
{
ObjCInstanceVariablesDeclarationAST *ast = new (&pool) ObjCInstanceVariablesDeclarationAST;
ast->instance_variable_list = instance_variable_list;
@@ -1022,14 +1043,14 @@ public:
return ast;
}
- ObjCPropertyAttributeAST *ObjCPropertyAttribute(ObjCSelectorAST *method_selector = 0)
+ ObjCPropertyAttributeAST *ObjCPropertyAttribute(ObjCSelectorAST *method_selector = nullptr)
{
ObjCPropertyAttributeAST *ast = new (&pool) ObjCPropertyAttributeAST;
ast->method_selector = method_selector;
return ast;
}
- ObjCPropertyDeclarationAST *ObjCPropertyDeclaration(SpecifierListAST *attribute_list = 0, ObjCPropertyAttributeListAST *property_attribute_list = 0, DeclarationAST *simple_declaration = 0)
+ ObjCPropertyDeclarationAST *ObjCPropertyDeclaration(SpecifierListAST *attribute_list = nullptr, ObjCPropertyAttributeListAST *property_attribute_list = nullptr, DeclarationAST *simple_declaration = nullptr)
{
ObjCPropertyDeclarationAST *ast = new (&pool) ObjCPropertyDeclarationAST;
ast->attribute_list = attribute_list;
@@ -1038,7 +1059,7 @@ public:
return ast;
}
- ObjCMessageArgumentDeclarationAST *ObjCMessageArgumentDeclaration(ObjCTypeNameAST *type_name = 0, SpecifierListAST *attribute_list = 0, NameAST *param_name = 0)
+ ObjCMessageArgumentDeclarationAST *ObjCMessageArgumentDeclaration(ObjCTypeNameAST *type_name = nullptr, SpecifierListAST *attribute_list = nullptr, NameAST *param_name = nullptr)
{
ObjCMessageArgumentDeclarationAST *ast = new (&pool) ObjCMessageArgumentDeclarationAST;
ast->type_name = type_name;
@@ -1047,7 +1068,7 @@ public:
return ast;
}
- ObjCMethodPrototypeAST *ObjCMethodPrototype(ObjCTypeNameAST *type_name = 0, ObjCSelectorAST *selector = 0, ObjCMessageArgumentDeclarationListAST *argument_list = 0, SpecifierListAST *attribute_list = 0)
+ ObjCMethodPrototypeAST *ObjCMethodPrototype(ObjCTypeNameAST *type_name = nullptr, ObjCSelectorAST *selector = nullptr, ObjCMessageArgumentDeclarationListAST *argument_list = nullptr, SpecifierListAST *attribute_list = nullptr)
{
ObjCMethodPrototypeAST *ast = new (&pool) ObjCMethodPrototypeAST;
ast->type_name = type_name;
@@ -1057,7 +1078,7 @@ public:
return ast;
}
- ObjCMethodDeclarationAST *ObjCMethodDeclaration(ObjCMethodPrototypeAST *method_prototype = 0, StatementAST *function_body = 0)
+ ObjCMethodDeclarationAST *ObjCMethodDeclaration(ObjCMethodPrototypeAST *method_prototype = nullptr, StatementAST *function_body = nullptr)
{
ObjCMethodDeclarationAST *ast = new (&pool) ObjCMethodDeclarationAST;
ast->method_prototype = method_prototype;
@@ -1071,21 +1092,21 @@ public:
return ast;
}
- ObjCSynthesizedPropertiesDeclarationAST *ObjCSynthesizedPropertiesDeclaration(ObjCSynthesizedPropertyListAST *property_identifier_list = 0)
+ ObjCSynthesizedPropertiesDeclarationAST *ObjCSynthesizedPropertiesDeclaration(ObjCSynthesizedPropertyListAST *property_identifier_list = nullptr)
{
ObjCSynthesizedPropertiesDeclarationAST *ast = new (&pool) ObjCSynthesizedPropertiesDeclarationAST;
ast->property_identifier_list = property_identifier_list;
return ast;
}
- ObjCDynamicPropertiesDeclarationAST *ObjCDynamicPropertiesDeclaration(NameListAST *property_identifier_list = 0)
+ ObjCDynamicPropertiesDeclarationAST *ObjCDynamicPropertiesDeclaration(NameListAST *property_identifier_list = nullptr)
{
ObjCDynamicPropertiesDeclarationAST *ast = new (&pool) ObjCDynamicPropertiesDeclarationAST;
ast->property_identifier_list = property_identifier_list;
return ast;
}
- ObjCFastEnumerationAST *ObjCFastEnumeration(SpecifierListAST *type_specifier_list = 0, DeclaratorAST *declarator = 0, ExpressionAST *initializer = 0, ExpressionAST *fast_enumeratable_expression = 0, StatementAST *statement = 0)
+ ObjCFastEnumerationAST *ObjCFastEnumeration(SpecifierListAST *type_specifier_list = nullptr, DeclaratorAST *declarator = nullptr, ExpressionAST *initializer = nullptr, ExpressionAST *fast_enumeratable_expression = nullptr, StatementAST *statement = nullptr)
{
ObjCFastEnumerationAST *ast = new (&pool) ObjCFastEnumerationAST;
ast->type_specifier_list = type_specifier_list;
@@ -1096,7 +1117,7 @@ public:
return ast;
}
- ObjCSynchronizedStatementAST *ObjCSynchronizedStatement(ExpressionAST *synchronized_object = 0, StatementAST *statement = 0)
+ ObjCSynchronizedStatementAST *ObjCSynchronizedStatement(ExpressionAST *synchronized_object = nullptr, StatementAST *statement = nullptr)
{
ObjCSynchronizedStatementAST *ast = new (&pool) ObjCSynchronizedStatementAST;
ast->synchronized_object = synchronized_object;
@@ -1104,7 +1125,7 @@ public:
return ast;
}
- LambdaExpressionAST *LambdaExpression(LambdaIntroducerAST *lambda_introducer = 0, LambdaDeclaratorAST *lambda_declarator = 0, StatementAST *statement = 0)
+ LambdaExpressionAST *LambdaExpression(LambdaIntroducerAST *lambda_introducer = nullptr, LambdaDeclaratorAST *lambda_declarator = nullptr, StatementAST *statement = nullptr)
{
LambdaExpressionAST *ast = new (&pool) LambdaExpressionAST;
ast->lambda_introducer = lambda_introducer;
@@ -1113,28 +1134,28 @@ public:
return ast;
}
- LambdaIntroducerAST *LambdaIntroducer(LambdaCaptureAST *lambda_capture = 0)
+ LambdaIntroducerAST *LambdaIntroducer(LambdaCaptureAST *lambda_capture = nullptr)
{
LambdaIntroducerAST *ast = new (&pool) LambdaIntroducerAST;
ast->lambda_capture = lambda_capture;
return ast;
}
- LambdaCaptureAST *LambdaCapture(CaptureListAST *capture_list = 0)
+ LambdaCaptureAST *LambdaCapture(CaptureListAST *capture_list = nullptr)
{
LambdaCaptureAST *ast = new (&pool) LambdaCaptureAST;
ast->capture_list = capture_list;
return ast;
}
- CaptureAST *Capture(NameAST *identifier = 0)
+ CaptureAST *Capture(NameAST *identifier = nullptr)
{
CaptureAST *ast = new (&pool) CaptureAST;
ast->identifier = identifier;
return ast;
}
- LambdaDeclaratorAST *LambdaDeclarator(ParameterDeclarationClauseAST *parameter_declaration_clause = 0, SpecifierListAST *attributes = 0, ExceptionSpecificationAST *exception_specification = 0, TrailingReturnTypeAST *trailing_return_type = 0)
+ LambdaDeclaratorAST *LambdaDeclarator(ParameterDeclarationClauseAST *parameter_declaration_clause = nullptr, SpecifierListAST *attributes = nullptr, ExceptionSpecificationAST *exception_specification = nullptr, TrailingReturnTypeAST *trailing_return_type = nullptr)
{
LambdaDeclaratorAST *ast = new (&pool) LambdaDeclaratorAST;
ast->parameter_declaration_clause = parameter_declaration_clause;
@@ -1144,7 +1165,7 @@ public:
return ast;
}
- TrailingReturnTypeAST *TrailingReturnType(SpecifierListAST *attributes = 0, SpecifierListAST *type_specifier_list = 0, DeclaratorAST *declarator = 0)
+ TrailingReturnTypeAST *TrailingReturnType(SpecifierListAST *attributes = nullptr, SpecifierListAST *type_specifier_list = nullptr, DeclaratorAST *declarator = nullptr)
{
TrailingReturnTypeAST *ast = new (&pool) TrailingReturnTypeAST;
ast->attributes = attributes;
@@ -1153,7 +1174,7 @@ public:
return ast;
}
- BracedInitializerAST *BracedInitializer(ExpressionListAST *expression_list = 0)
+ BracedInitializerAST *BracedInitializer(ExpressionListAST *expression_list = nullptr)
{
BracedInitializerAST *ast = new (&pool) BracedInitializerAST;
ast->expression_list = expression_list;
@@ -1166,14 +1187,14 @@ public:
return ast;
}
- BracketDesignatorAST *BracketDesignator(ExpressionAST *expression = 0)
+ BracketDesignatorAST *BracketDesignator(ExpressionAST *expression = nullptr)
{
BracketDesignatorAST *ast = new (&pool) BracketDesignatorAST;
ast->expression = expression;
return ast;
}
- DesignatedInitializerAST *DesignatedInitializer(DesignatorListAST *designator_list = 0, ExpressionAST *initializer = 0)
+ DesignatedInitializerAST *DesignatedInitializer(DesignatorListAST *designator_list = nullptr, ExpressionAST *initializer = nullptr)
{
DesignatedInitializerAST *ast = new (&pool) DesignatedInitializerAST;
ast->designator_list = designator_list;
@@ -1181,7 +1202,7 @@ public:
return ast;
}
- BaseSpecifierListAST *BaseSpecifierList(BaseSpecifierAST *value, BaseSpecifierListAST *next = 0)
+ BaseSpecifierListAST *BaseSpecifierList(BaseSpecifierAST *value, BaseSpecifierListAST *next = nullptr)
{
BaseSpecifierListAST *list = new (&pool) BaseSpecifierListAST;
list->next = next;
@@ -1189,7 +1210,7 @@ public:
return list;
}
- CaptureListAST *CaptureList(CaptureAST *value, CaptureListAST *next = 0)
+ CaptureListAST *CaptureList(CaptureAST *value, CaptureListAST *next = nullptr)
{
CaptureListAST *list = new (&pool) CaptureListAST;
list->next = next;
@@ -1197,7 +1218,7 @@ public:
return list;
}
- CatchClauseListAST *CatchClauseList(CatchClauseAST *value, CatchClauseListAST *next = 0)
+ CatchClauseListAST *CatchClauseList(CatchClauseAST *value, CatchClauseListAST *next = nullptr)
{
CatchClauseListAST *list = new (&pool) CatchClauseListAST;
list->next = next;
@@ -1205,7 +1226,7 @@ public:
return list;
}
- DeclarationListAST *DeclarationList(DeclarationAST *value, DeclarationListAST *next = 0)
+ DeclarationListAST *DeclarationList(DeclarationAST *value, DeclarationListAST *next = nullptr)
{
DeclarationListAST *list = new (&pool) DeclarationListAST;
list->next = next;
@@ -1213,7 +1234,7 @@ public:
return list;
}
- DeclaratorListAST *DeclaratorList(DeclaratorAST *value, DeclaratorListAST *next = 0)
+ DeclaratorListAST *DeclaratorList(DeclaratorAST *value, DeclaratorListAST *next = nullptr)
{
DeclaratorListAST *list = new (&pool) DeclaratorListAST;
list->next = next;
@@ -1221,7 +1242,7 @@ public:
return list;
}
- DesignatorListAST *DesignatorList(DesignatorAST *value, DesignatorListAST *next = 0)
+ DesignatorListAST *DesignatorList(DesignatorAST *value, DesignatorListAST *next = nullptr)
{
DesignatorListAST *list = new (&pool) DesignatorListAST;
list->next = next;
@@ -1229,7 +1250,7 @@ public:
return list;
}
- EnumeratorListAST *EnumeratorList(EnumeratorAST *value, EnumeratorListAST *next = 0)
+ EnumeratorListAST *EnumeratorList(EnumeratorAST *value, EnumeratorListAST *next = nullptr)
{
EnumeratorListAST *list = new (&pool) EnumeratorListAST;
list->next = next;
@@ -1237,7 +1258,7 @@ public:
return list;
}
- ExpressionListAST *ExpressionList(ExpressionAST *value, ExpressionListAST *next = 0)
+ ExpressionListAST *ExpressionList(ExpressionAST *value, ExpressionListAST *next = nullptr)
{
ExpressionListAST *list = new (&pool) ExpressionListAST;
list->next = next;
@@ -1245,7 +1266,7 @@ public:
return list;
}
- GnuAttributeListAST *GnuAttributeList(GnuAttributeAST *value, GnuAttributeListAST *next = 0)
+ GnuAttributeListAST *GnuAttributeList(GnuAttributeAST *value, GnuAttributeListAST *next = nullptr)
{
GnuAttributeListAST *list = new (&pool) GnuAttributeListAST;
list->next = next;
@@ -1253,7 +1274,7 @@ public:
return list;
}
- MemInitializerListAST *MemInitializerList(MemInitializerAST *value, MemInitializerListAST *next = 0)
+ MemInitializerListAST *MemInitializerList(MemInitializerAST *value, MemInitializerListAST *next = nullptr)
{
MemInitializerListAST *list = new (&pool) MemInitializerListAST;
list->next = next;
@@ -1261,7 +1282,7 @@ public:
return list;
}
- NameListAST *NameList(NameAST *value, NameListAST *next = 0)
+ NameListAST *NameList(NameAST *value, NameListAST *next = nullptr)
{
NameListAST *list = new (&pool) NameListAST;
list->next = next;
@@ -1269,7 +1290,7 @@ public:
return list;
}
- NestedNameSpecifierListAST *NestedNameSpecifierList(NestedNameSpecifierAST *value, NestedNameSpecifierListAST *next = 0)
+ NestedNameSpecifierListAST *NestedNameSpecifierList(NestedNameSpecifierAST *value, NestedNameSpecifierListAST *next = nullptr)
{
NestedNameSpecifierListAST *list = new (&pool) NestedNameSpecifierListAST;
list->next = next;
@@ -1277,7 +1298,7 @@ public:
return list;
}
- NewArrayDeclaratorListAST *NewArrayDeclaratorList(NewArrayDeclaratorAST *value, NewArrayDeclaratorListAST *next = 0)
+ NewArrayDeclaratorListAST *NewArrayDeclaratorList(NewArrayDeclaratorAST *value, NewArrayDeclaratorListAST *next = nullptr)
{
NewArrayDeclaratorListAST *list = new (&pool) NewArrayDeclaratorListAST;
list->next = next;
@@ -1285,7 +1306,7 @@ public:
return list;
}
- ObjCMessageArgumentDeclarationListAST *ObjCMessageArgumentDeclarationList(ObjCMessageArgumentDeclarationAST *value, ObjCMessageArgumentDeclarationListAST *next = 0)
+ ObjCMessageArgumentDeclarationListAST *ObjCMessageArgumentDeclarationList(ObjCMessageArgumentDeclarationAST *value, ObjCMessageArgumentDeclarationListAST *next = nullptr)
{
ObjCMessageArgumentDeclarationListAST *list = new (&pool) ObjCMessageArgumentDeclarationListAST;
list->next = next;
@@ -1293,7 +1314,7 @@ public:
return list;
}
- ObjCMessageArgumentListAST *ObjCMessageArgumentList(ObjCMessageArgumentAST *value, ObjCMessageArgumentListAST *next = 0)
+ ObjCMessageArgumentListAST *ObjCMessageArgumentList(ObjCMessageArgumentAST *value, ObjCMessageArgumentListAST *next = nullptr)
{
ObjCMessageArgumentListAST *list = new (&pool) ObjCMessageArgumentListAST;
list->next = next;
@@ -1301,7 +1322,7 @@ public:
return list;
}
- ObjCPropertyAttributeListAST *ObjCPropertyAttributeList(ObjCPropertyAttributeAST *value, ObjCPropertyAttributeListAST *next = 0)
+ ObjCPropertyAttributeListAST *ObjCPropertyAttributeList(ObjCPropertyAttributeAST *value, ObjCPropertyAttributeListAST *next = nullptr)
{
ObjCPropertyAttributeListAST *list = new (&pool) ObjCPropertyAttributeListAST;
list->next = next;
@@ -1309,7 +1330,7 @@ public:
return list;
}
- ObjCSelectorArgumentListAST *ObjCSelectorArgumentList(ObjCSelectorArgumentAST *value, ObjCSelectorArgumentListAST *next = 0)
+ ObjCSelectorArgumentListAST *ObjCSelectorArgumentList(ObjCSelectorArgumentAST *value, ObjCSelectorArgumentListAST *next = nullptr)
{
ObjCSelectorArgumentListAST *list = new (&pool) ObjCSelectorArgumentListAST;
list->next = next;
@@ -1317,7 +1338,7 @@ public:
return list;
}
- ObjCSynthesizedPropertyListAST *ObjCSynthesizedPropertyList(ObjCSynthesizedPropertyAST *value, ObjCSynthesizedPropertyListAST *next = 0)
+ ObjCSynthesizedPropertyListAST *ObjCSynthesizedPropertyList(ObjCSynthesizedPropertyAST *value, ObjCSynthesizedPropertyListAST *next = nullptr)
{
ObjCSynthesizedPropertyListAST *list = new (&pool) ObjCSynthesizedPropertyListAST;
list->next = next;
@@ -1325,7 +1346,7 @@ public:
return list;
}
- ParameterDeclarationListAST *ParameterDeclarationList(ParameterDeclarationAST *value, ParameterDeclarationListAST *next = 0)
+ ParameterDeclarationListAST *ParameterDeclarationList(ParameterDeclarationAST *value, ParameterDeclarationListAST *next = nullptr)
{
ParameterDeclarationListAST *list = new (&pool) ParameterDeclarationListAST;
list->next = next;
@@ -1333,7 +1354,7 @@ public:
return list;
}
- PostfixDeclaratorListAST *PostfixDeclaratorList(PostfixDeclaratorAST *value, PostfixDeclaratorListAST *next = 0)
+ PostfixDeclaratorListAST *PostfixDeclaratorList(PostfixDeclaratorAST *value, PostfixDeclaratorListAST *next = nullptr)
{
PostfixDeclaratorListAST *list = new (&pool) PostfixDeclaratorListAST;
list->next = next;
@@ -1341,7 +1362,7 @@ public:
return list;
}
- PtrOperatorListAST *PtrOperatorList(PtrOperatorAST *value, PtrOperatorListAST *next = 0)
+ PtrOperatorListAST *PtrOperatorList(PtrOperatorAST *value, PtrOperatorListAST *next = nullptr)
{
PtrOperatorListAST *list = new (&pool) PtrOperatorListAST;
list->next = next;
@@ -1349,7 +1370,7 @@ public:
return list;
}
- QtInterfaceNameListAST *QtInterfaceNameList(QtInterfaceNameAST *value, QtInterfaceNameListAST *next = 0)
+ QtInterfaceNameListAST *QtInterfaceNameList(QtInterfaceNameAST *value, QtInterfaceNameListAST *next = nullptr)
{
QtInterfaceNameListAST *list = new (&pool) QtInterfaceNameListAST;
list->next = next;
@@ -1357,7 +1378,7 @@ public:
return list;
}
- QtPropertyDeclarationItemListAST *QtPropertyDeclarationItemList(QtPropertyDeclarationItemAST *value, QtPropertyDeclarationItemListAST *next = 0)
+ QtPropertyDeclarationItemListAST *QtPropertyDeclarationItemList(QtPropertyDeclarationItemAST *value, QtPropertyDeclarationItemListAST *next = nullptr)
{
QtPropertyDeclarationItemListAST *list = new (&pool) QtPropertyDeclarationItemListAST;
list->next = next;
@@ -1365,7 +1386,7 @@ public:
return list;
}
- SpecifierListAST *SpecifierList(SpecifierAST *value, SpecifierListAST *next = 0)
+ SpecifierListAST *SpecifierList(SpecifierAST *value, SpecifierListAST *next = nullptr)
{
SpecifierListAST *list = new (&pool) SpecifierListAST;
list->next = next;
@@ -1373,7 +1394,7 @@ public:
return list;
}
- StatementListAST *StatementList(StatementAST *value, StatementListAST *next = 0)
+ StatementListAST *StatementList(StatementAST *value, StatementListAST *next = nullptr)
{
StatementListAST *list = new (&pool) StatementListAST;
list->next = next;
diff --git a/src/libs/3rdparty/cplusplus/ASTVisit.cpp b/src/libs/3rdparty/cplusplus/ASTVisit.cpp
index 5ebe6ba3a5..5b0ef3ce33 100644
--- a/src/libs/3rdparty/cplusplus/ASTVisit.cpp
+++ b/src/libs/3rdparty/cplusplus/ASTVisit.cpp
@@ -70,6 +70,22 @@ void GnuAttributeSpecifierAST::accept0(ASTVisitor *visitor)
visitor->endVisit(this);
}
+void MsvcDeclspecSpecifierAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(attribute_list, visitor);
+ }
+ visitor->endVisit(this);
+}
+
+void StdAttributeSpecifierAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(attribute_list, visitor);
+ }
+ visitor->endVisit(this);
+}
+
void GnuAttributeAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
@@ -94,6 +110,23 @@ void DecltypeSpecifierAST::accept0(ASTVisitor *visitor)
visitor->endVisit(this);
}
+void TypeConstraintAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(nestedName, visitor);
+ accept(conceptName, visitor);
+ accept(templateArgs, visitor);
+ }
+ visitor->endVisit(this);
+}
+
+void PlaceholderTypeSpecifierAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this))
+ accept(typeConstraint, visitor);
+ visitor->endVisit(this);
+}
+
void DeclaratorAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
@@ -352,6 +385,14 @@ void DeclaratorIdAST::accept0(ASTVisitor *visitor)
visitor->endVisit(this);
}
+void DecompositionDeclaratorAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(identifiers, visitor);
+ }
+ visitor->endVisit(this);
+}
+
void NestedDeclaratorAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
@@ -522,6 +563,7 @@ void ForStatementAST::accept0(ASTVisitor *visitor)
void IfStatementAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
+ accept(initStmt, visitor);
accept(condition, visitor);
accept(statement, visitor);
accept(else_statement, visitor);
@@ -917,11 +959,36 @@ void TemplateDeclarationAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
accept(template_parameter_list, visitor);
+ accept(requiresClause, visitor);
accept(declaration, visitor);
}
visitor->endVisit(this);
}
+void ConceptDeclarationAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(name, visitor);
+ accept(attributes, visitor);
+ accept(constraint, visitor);
+ }
+ visitor->endVisit(this);
+}
+
+void RequiresExpressionAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this))
+ accept(parameters, visitor);
+ visitor->endVisit(this);
+}
+
+void RequiresClauseAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this))
+ accept(constraint, visitor);
+ visitor->endVisit(this);
+}
+
void ThrowExpressionAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
@@ -930,6 +997,20 @@ void ThrowExpressionAST::accept0(ASTVisitor *visitor)
visitor->endVisit(this);
}
+void YieldExpressionAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this))
+ accept(expression, visitor);
+ visitor->endVisit(this);
+}
+
+void AwaitExpressionAST::accept0(ASTVisitor *visitor)
+{
+ if (visitor->visit(this))
+ accept(castExpression, visitor);
+ visitor->endVisit(this);
+}
+
void NoExceptOperatorExpressionAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
@@ -985,6 +1066,7 @@ void TypenameTypeParameterAST::accept0(ASTVisitor *visitor)
void TemplateTypeParameterAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
+ accept(typeConstraint, visitor);
accept(template_parameter_list, visitor);
accept(name, visitor);
accept(type_id, visitor);
@@ -1236,6 +1318,9 @@ void LambdaExpressionAST::accept0(ASTVisitor *visitor)
{
if (visitor->visit(this)) {
accept(lambda_introducer, visitor);
+ accept(templateParameters, visitor);
+ accept(requiresClause, visitor);
+ accept(attributes, visitor);
accept(lambda_declarator, visitor);
accept(statement, visitor);
}
@@ -1273,6 +1358,7 @@ void LambdaDeclaratorAST::accept0(ASTVisitor *visitor)
accept(attributes, visitor);
accept(exception_specification, visitor);
accept(trailing_return_type, visitor);
+ accept(requiresClause, visitor);
}
visitor->endVisit(this);
}
diff --git a/src/libs/3rdparty/cplusplus/ASTVisitor.cpp b/src/libs/3rdparty/cplusplus/ASTVisitor.cpp
index f4119e9ce1..efd5ff1d53 100644
--- a/src/libs/3rdparty/cplusplus/ASTVisitor.cpp
+++ b/src/libs/3rdparty/cplusplus/ASTVisitor.cpp
@@ -40,7 +40,7 @@ Control *ASTVisitor::control() const
if (_translationUnit)
return _translationUnit->control();
- return 0;
+ return nullptr;
}
TranslationUnit *ASTVisitor::translationUnit() const
@@ -49,44 +49,40 @@ TranslationUnit *ASTVisitor::translationUnit() const
void ASTVisitor::setTranslationUnit(TranslationUnit *translationUnit)
{ _translationUnit = translationUnit; }
-unsigned ASTVisitor::tokenCount() const
+int ASTVisitor::tokenCount() const
{ return translationUnit()->tokenCount(); }
-const Token &ASTVisitor::tokenAt(unsigned index) const
+const Token &ASTVisitor::tokenAt(int index) const
{ return translationUnit()->tokenAt(index); }
-int ASTVisitor::tokenKind(unsigned index) const
+int ASTVisitor::tokenKind(int index) const
{ return translationUnit()->tokenKind(index); }
-const char *ASTVisitor::spell(unsigned index) const
+const char *ASTVisitor::spell(int index) const
{ return translationUnit()->spell(index); }
-const Identifier *ASTVisitor::identifier(unsigned index) const
+const Identifier *ASTVisitor::identifier(int index) const
{ return translationUnit()->identifier(index); }
-const Literal *ASTVisitor::literal(unsigned index) const
+const Literal *ASTVisitor::literal(int index) const
{ return translationUnit()->literal(index); }
-const NumericLiteral *ASTVisitor::numericLiteral(unsigned index) const
+const NumericLiteral *ASTVisitor::numericLiteral(int index) const
{ return translationUnit()->numericLiteral(index); }
-const StringLiteral *ASTVisitor::stringLiteral(unsigned index) const
+const StringLiteral *ASTVisitor::stringLiteral(int index) const
{ return translationUnit()->stringLiteral(index); }
-void ASTVisitor::getPosition(unsigned offset,
- unsigned *line,
- unsigned *column,
+void ASTVisitor::getPosition(int offset, int *line, int *column,
const StringLiteral **fileName) const
{ translationUnit()->getPosition(offset, line, column, fileName); }
-void ASTVisitor::getTokenPosition(unsigned index,
- unsigned *line,
- unsigned *column,
+void ASTVisitor::getTokenPosition(int index, int *line, int *column,
const StringLiteral **fileName) const
{ translationUnit()->getTokenPosition(index, line, column, fileName); }
-void ASTVisitor::getTokenStartPosition(unsigned index, unsigned *line, unsigned *column) const
+void ASTVisitor::getTokenStartPosition(int index, int *line, int *column) const
{ getPosition(tokenAt(index).utf16charsBegin(), line, column); }
-void ASTVisitor::getTokenEndPosition(unsigned index, unsigned *line, unsigned *column) const
+void ASTVisitor::getTokenEndPosition(int index, int *line, int *column) const
{ getPosition(tokenAt(index).utf16charsEnd(), line, column); }
diff --git a/src/libs/3rdparty/cplusplus/ASTVisitor.h b/src/libs/3rdparty/cplusplus/ASTVisitor.h
index 46bfde340b..9d9fec75b1 100644
--- a/src/libs/3rdparty/cplusplus/ASTVisitor.h
+++ b/src/libs/3rdparty/cplusplus/ASTVisitor.h
@@ -38,27 +38,27 @@ public:
void setTranslationUnit(TranslationUnit *translationUnit);
Control *control() const;
- unsigned tokenCount() const;
- const Token &tokenAt(unsigned index) const;
- int tokenKind(unsigned index) const;
- const char *spell(unsigned index) const;
- const Identifier *identifier(unsigned index) const;
- const Literal *literal(unsigned index) const;
- const NumericLiteral *numericLiteral(unsigned index) const;
- const StringLiteral *stringLiteral(unsigned index) const;
+ int tokenCount() const;
+ const Token &tokenAt(int index) const;
+ int tokenKind(int index) const;
+ const char *spell(int index) const;
+ const Identifier *identifier(int index) const;
+ const Literal *literal(int index) const;
+ const NumericLiteral *numericLiteral(int index) const;
+ const StringLiteral *stringLiteral(int index) const;
- void getPosition(unsigned offset,
- unsigned *line,
- unsigned *column = 0,
- const StringLiteral **fileName = 0) const;
+ void getPosition(int offset,
+ int *line,
+ int *column = nullptr,
+ const StringLiteral **fileName = nullptr) const;
- void getTokenPosition(unsigned index,
- unsigned *line,
- unsigned *column = 0,
- const StringLiteral **fileName = 0) const;
+ void getTokenPosition(int index,
+ int *line,
+ int *column = nullptr,
+ const StringLiteral **fileName = nullptr) const;
- void getTokenStartPosition(unsigned index, unsigned *line, unsigned *column) const;
- void getTokenEndPosition(unsigned index, unsigned *line, unsigned *column) const;
+ void getTokenStartPosition(int index, int *line, int *column) const;
+ void getTokenEndPosition(int index, int *line, int *column) const;
void accept(AST *ast);
@@ -81,6 +81,7 @@ public:
virtual bool visit(ArrayDeclaratorAST *) { return true; }
virtual bool visit(ArrayInitializerAST *) { return true; }
virtual bool visit(AsmDefinitionAST *) { return true; }
+ virtual bool visit(AwaitExpressionAST *) { return true; }
virtual bool visit(BaseSpecifierAST *) { return true; }
virtual bool visit(BinaryExpressionAST *) { return true; }
virtual bool visit(BoolLiteralAST *) { return true; }
@@ -96,6 +97,7 @@ public:
virtual bool visit(CompoundExpressionAST *) { return true; }
virtual bool visit(CompoundLiteralAST *) { return true; }
virtual bool visit(CompoundStatementAST *) { return true; }
+ virtual bool visit(ConceptDeclarationAST *) { return true; }
virtual bool visit(ConditionAST *) { return true; }
virtual bool visit(ConditionalExpressionAST *) { return true; }
virtual bool visit(ContinueStatementAST *) { return true; }
@@ -105,6 +107,7 @@ public:
virtual bool visit(DeclarationStatementAST *) { return true; }
virtual bool visit(DeclaratorAST *) { return true; }
virtual bool visit(DeclaratorIdAST *) { return true; }
+ virtual bool visit(DecompositionDeclaratorAST *) { return true; }
virtual bool visit(DecltypeSpecifierAST *) { return true; }
virtual bool visit(DeleteExpressionAST *) { return true; }
virtual bool visit(DesignatedInitializerAST *) { return true; }
@@ -138,6 +141,7 @@ public:
virtual bool visit(LinkageSpecificationAST *) { return true; }
virtual bool visit(MemInitializerAST *) { return true; }
virtual bool visit(MemberAccessAST *) { return true; }
+ virtual bool visit(MsvcDeclspecSpecifierAST *) { return true; }
virtual bool visit(NamedTypeSpecifierAST *) { return true; }
virtual bool visit(NamespaceAST *) { return true; }
virtual bool visit(NamespaceAliasDefinitionAST *) { return true; }
@@ -179,6 +183,7 @@ public:
virtual bool visit(OperatorFunctionIdAST *) { return true; }
virtual bool visit(ParameterDeclarationAST *) { return true; }
virtual bool visit(ParameterDeclarationClauseAST *) { return true; }
+ virtual bool visit(PlaceholderTypeSpecifierAST *) { return true; }
virtual bool visit(PointerAST *) { return true; }
virtual bool visit(PointerLiteralAST *) { return true; }
virtual bool visit(PointerToMemberAST *) { return true; }
@@ -196,12 +201,15 @@ public:
virtual bool visit(QualifiedNameAST *) { return true; }
virtual bool visit(RangeBasedForStatementAST *) { return true; }
virtual bool visit(ReferenceAST *) { return true; }
+ virtual bool visit(RequiresExpressionAST *) { return true; }
+ virtual bool visit(RequiresClauseAST *) { return true; }
virtual bool visit(ReturnStatementAST *) { return true; }
virtual bool visit(SimpleDeclarationAST *) { return true; }
virtual bool visit(SimpleNameAST *) { return true; }
virtual bool visit(SimpleSpecifierAST *) { return true; }
virtual bool visit(SizeofExpressionAST *) { return true; }
virtual bool visit(StaticAssertDeclarationAST *) { return true; }
+ virtual bool visit(StdAttributeSpecifierAST *) { return true; }
virtual bool visit(StringLiteralAST *) { return true; }
virtual bool visit(SwitchStatementAST *) { return true; }
virtual bool visit(TemplateDeclarationAST *) { return true; }
@@ -212,6 +220,7 @@ public:
virtual bool visit(TrailingReturnTypeAST *) { return true; }
virtual bool visit(TranslationUnitAST *) { return true; }
virtual bool visit(TryBlockStatementAST *) { return true; }
+ virtual bool visit(TypeConstraintAST *) { return true; }
virtual bool visit(TypeConstructorCallAST *) { return true; }
virtual bool visit(TypeIdAST *) { return true; }
virtual bool visit(TypeidExpressionAST *) { return true; }
@@ -222,6 +231,7 @@ public:
virtual bool visit(UsingAST *) { return true; }
virtual bool visit(UsingDirectiveAST *) { return true; }
virtual bool visit(WhileStatementAST *) { return true; }
+ virtual bool visit(YieldExpressionAST *) { return true; }
virtual void endVisit(AccessDeclarationAST *) {}
virtual void endVisit(AliasDeclarationAST *) {}
@@ -232,6 +242,7 @@ public:
virtual void endVisit(ArrayDeclaratorAST *) {}
virtual void endVisit(ArrayInitializerAST *) {}
virtual void endVisit(AsmDefinitionAST *) {}
+ virtual void endVisit(AwaitExpressionAST *) {}
virtual void endVisit(BaseSpecifierAST *) {}
virtual void endVisit(BinaryExpressionAST *) {}
virtual void endVisit(BoolLiteralAST *) {}
@@ -247,6 +258,7 @@ public:
virtual void endVisit(CompoundExpressionAST *) {}
virtual void endVisit(CompoundLiteralAST *) {}
virtual void endVisit(CompoundStatementAST *) {}
+ virtual void endVisit(ConceptDeclarationAST *) {}
virtual void endVisit(ConditionAST *) {}
virtual void endVisit(ConditionalExpressionAST *) {}
virtual void endVisit(ContinueStatementAST *) {}
@@ -256,6 +268,7 @@ public:
virtual void endVisit(DeclarationStatementAST *) {}
virtual void endVisit(DeclaratorAST *) {}
virtual void endVisit(DeclaratorIdAST *) {}
+ virtual void endVisit(DecompositionDeclaratorAST *) {}
virtual void endVisit(DecltypeSpecifierAST *) {}
virtual void endVisit(DeleteExpressionAST *) {}
virtual void endVisit(DesignatedInitializerAST *) {}
@@ -289,6 +302,7 @@ public:
virtual void endVisit(LinkageSpecificationAST *) {}
virtual void endVisit(MemInitializerAST *) {}
virtual void endVisit(MemberAccessAST *) {}
+ virtual void endVisit(MsvcDeclspecSpecifierAST *) {}
virtual void endVisit(NamedTypeSpecifierAST *) {}
virtual void endVisit(NamespaceAST *) {}
virtual void endVisit(NamespaceAliasDefinitionAST *) {}
@@ -330,6 +344,7 @@ public:
virtual void endVisit(OperatorFunctionIdAST *) {}
virtual void endVisit(ParameterDeclarationAST *) {}
virtual void endVisit(ParameterDeclarationClauseAST *) {}
+ virtual void endVisit(PlaceholderTypeSpecifierAST *) {}
virtual void endVisit(PointerAST *) {}
virtual void endVisit(PointerLiteralAST *) {}
virtual void endVisit(PointerToMemberAST *) {}
@@ -347,12 +362,15 @@ public:
virtual void endVisit(QualifiedNameAST *) {}
virtual void endVisit(RangeBasedForStatementAST *) {}
virtual void endVisit(ReferenceAST *) {}
+ virtual void endVisit(RequiresExpressionAST *) {}
+ virtual void endVisit(RequiresClauseAST *) {}
virtual void endVisit(ReturnStatementAST *) {}
virtual void endVisit(SimpleDeclarationAST *) {}
virtual void endVisit(SimpleNameAST *) {}
virtual void endVisit(SimpleSpecifierAST *) {}
virtual void endVisit(SizeofExpressionAST *) {}
virtual void endVisit(StaticAssertDeclarationAST *) {}
+ virtual void endVisit(StdAttributeSpecifierAST *) {}
virtual void endVisit(StringLiteralAST *) {}
virtual void endVisit(SwitchStatementAST *) {}
virtual void endVisit(TemplateDeclarationAST *) {}
@@ -363,6 +381,7 @@ public:
virtual void endVisit(TrailingReturnTypeAST *) {}
virtual void endVisit(TranslationUnitAST *) {}
virtual void endVisit(TryBlockStatementAST *) {}
+ virtual void endVisit(TypeConstraintAST *) {}
virtual void endVisit(TypeConstructorCallAST *) {}
virtual void endVisit(TypeIdAST *) {}
virtual void endVisit(TypeidExpressionAST *) {}
@@ -373,6 +392,7 @@ public:
virtual void endVisit(UsingAST *) {}
virtual void endVisit(UsingDirectiveAST *) {}
virtual void endVisit(WhileStatementAST *) {}
+ virtual void endVisit(YieldExpressionAST *) {}
private:
TranslationUnit *_translationUnit;
diff --git a/src/libs/3rdparty/cplusplus/ASTfwd.h b/src/libs/3rdparty/cplusplus/ASTfwd.h
index 2c55fd27d6..102fda75fb 100644
--- a/src/libs/3rdparty/cplusplus/ASTfwd.h
+++ b/src/libs/3rdparty/cplusplus/ASTfwd.h
@@ -40,6 +40,7 @@ class ArrayDeclaratorAST;
class ArrayInitializerAST;
class AsmDefinitionAST;
class AttributeSpecifierAST;
+class AwaitExpressionAST;
class BaseSpecifierAST;
class BinaryExpressionAST;
class BoolLiteralAST;
@@ -66,6 +67,7 @@ class DeclarationAST;
class DeclarationStatementAST;
class DeclaratorAST;
class DeclaratorIdAST;
+class DecompositionDeclaratorAST;
class DecltypeSpecifierAST;
class DeleteExpressionAST;
class DesignatedInitializerAST;
@@ -102,6 +104,7 @@ class LinkageBodyAST;
class LinkageSpecificationAST;
class MemInitializerAST;
class MemberAccessAST;
+class MsvcDeclspecSpecifierAST;
class NameAST;
class NamedTypeSpecifierAST;
class NamespaceAST;
@@ -144,6 +147,7 @@ class OperatorAST;
class OperatorFunctionIdAST;
class ParameterDeclarationAST;
class ParameterDeclarationClauseAST;
+class PlaceholderTypeSpecifierAST;
class PointerAST;
class PointerLiteralAST;
class PointerToMemberAST;
@@ -164,6 +168,8 @@ class QtPropertyDeclarationItemAST;
class QualifiedNameAST;
class RangeBasedForStatementAST;
class ReferenceAST;
+class RequiresClauseAST;
+class RequiresExpressionAST;
class ReturnStatementAST;
class SimpleDeclarationAST;
class SimpleNameAST;
@@ -172,9 +178,11 @@ class SizeofExpressionAST;
class SpecifierAST;
class StatementAST;
class StaticAssertDeclarationAST;
+class StdAttributeSpecifierAST;
class StringLiteralAST;
class SwitchStatementAST;
class TemplateDeclarationAST;
+class ConceptDeclarationAST;
class TemplateIdAST;
class TemplateTypeParameterAST;
class ThisExpressionAST;
@@ -182,6 +190,7 @@ class ThrowExpressionAST;
class TrailingReturnTypeAST;
class TranslationUnitAST;
class TryBlockStatementAST;
+class TypeConstraintAST;
class TypeConstructorCallAST;
class TypeIdAST;
class TypeidExpressionAST;
@@ -192,6 +201,7 @@ class UnaryExpressionAST;
class UsingAST;
class UsingDirectiveAST;
class WhileStatementAST;
+class YieldExpressionAST;
typedef List<ExpressionAST *> ExpressionListAST;
typedef List<DeclarationAST *> DeclarationListAST;
diff --git a/src/libs/3rdparty/cplusplus/Bind.cpp b/src/libs/3rdparty/cplusplus/Bind.cpp
index 1f082a6ec8..c85d401c49 100644
--- a/src/libs/3rdparty/cplusplus/Bind.cpp
+++ b/src/libs/3rdparty/cplusplus/Bind.cpp
@@ -35,6 +35,7 @@
#include <string>
#include <memory>
#include <sstream>
+#include <utility>
using namespace CPlusPlus;
@@ -42,10 +43,11 @@ const int Bind::kMaxDepth(100);
Bind::Bind(TranslationUnit *unit)
: ASTVisitor(unit),
- _scope(0),
- _expression(0),
- _name(0),
- _declaratorId(0),
+ _scope(nullptr),
+ _expression(nullptr),
+ _name(nullptr),
+ _declaratorId(nullptr),
+ _decompositionDeclarator(nullptr),
_visibility(Symbol::Public),
_objcVisibility(Symbol::Public),
_methodKey(Function::NormalMethod),
@@ -64,7 +66,7 @@ void Bind::setSkipFunctionBodies(bool skipFunctionBodies)
_skipFunctionBodies = skipFunctionBodies;
}
-unsigned Bind::location(DeclaratorAST *ast, unsigned defaultLocation) const
+int Bind::location(DeclaratorAST *ast, int defaultLocation) const
{
if (! ast)
return defaultLocation;
@@ -75,7 +77,7 @@ unsigned Bind::location(DeclaratorAST *ast, unsigned defaultLocation) const
return ast->firstToken();
}
-unsigned Bind::location(CoreDeclaratorAST *ast, unsigned defaultLocation) const
+int Bind::location(CoreDeclaratorAST *ast, int defaultLocation) const
{
if (! ast)
return defaultLocation;
@@ -89,7 +91,7 @@ unsigned Bind::location(CoreDeclaratorAST *ast, unsigned defaultLocation) const
return ast->firstToken();
}
-unsigned Bind::location(NameAST *name, unsigned defaultLocation) const
+int Bind::location(NameAST *name, int defaultLocation) const
{
if (! name)
return defaultLocation;
@@ -135,6 +137,8 @@ void Bind::setDeclSpecifiers(Symbol *symbol, const FullySpecifiedType &declSpeci
if (Function *funTy = symbol->asFunction()) {
if (declSpecifiers.isVirtual())
funTy->setVirtual(true);
+ if (declSpecifiers.isStatic())
+ funTy->setStatic(true);
}
if (declSpecifiers.isDeprecated())
@@ -229,7 +233,7 @@ void Bind::declaration(DeclarationAST *ast)
const Name *Bind::name(NameAST *ast)
{
- const Name *value = 0;
+ const Name *value = nullptr;
std::swap(_name, value);
accept(ast);
std::swap(_name, value);
@@ -272,8 +276,25 @@ FullySpecifiedType Bind::postfixDeclarator(PostfixDeclaratorAST *ast, const Full
return value;
}
-bool Bind::preVisit(AST *)
+bool Bind::preVisit(AST *ast)
{
+ if (_typeWasUnsignedOrSigned) {
+ if (SimpleSpecifierAST *simpleAst = ast->asSimpleSpecifier()) {
+ switch (tokenKind(simpleAst->specifier_token)) {
+ case T_CHAR:
+ case T_CHAR16_T:
+ case T_CHAR32_T:
+ case T_WCHAR_T:
+ case T_INT:
+ case T_SHORT:
+ case T_LONG:
+ _type.setType(&UndefinedType::instance);
+ break;
+ }
+ }
+ _typeWasUnsignedOrSigned = false;
+ }
+
++_depth;
if (_depth > kMaxDepth)
return false;
@@ -296,7 +317,7 @@ bool Bind::visit(ObjCSelectorArgumentAST *ast)
const Name *Bind::objCSelectorArgument(ObjCSelectorArgumentAST *ast, bool *hasArg)
{
if (! (ast && ast->name_token))
- return 0;
+ return nullptr;
if (ast->colon_token)
*hasArg = true;
@@ -316,7 +337,7 @@ void Bind::attribute(GnuAttributeAST *ast)
if (! ast)
return;
- // unsigned identifier_token = ast->identifier_token;
+ // int identifier_token = ast->identifier_token;
if (const Identifier *id = identifier(ast->identifier_token)) {
if (id == control()->deprecatedId())
_type.setDeprecated(true);
@@ -324,12 +345,12 @@ void Bind::attribute(GnuAttributeAST *ast)
_type.setUnavailable(true);
}
- // unsigned lparen_token = ast->lparen_token;
- // unsigned tag_token = ast->tag_token;
+ // int lparen_token = ast->lparen_token;
+ // int tag_token = ast->tag_token;
for (ExpressionListAST *it = ast->expression_list; it; it = it->next) {
ExpressionTy value = this->expression(it->value);
}
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
}
bool Bind::visit(DeclaratorAST *ast)
@@ -339,7 +360,9 @@ bool Bind::visit(DeclaratorAST *ast)
return false;
}
-FullySpecifiedType Bind::declarator(DeclaratorAST *ast, const FullySpecifiedType &init, DeclaratorIdAST **declaratorId)
+FullySpecifiedType Bind::declarator(DeclaratorAST *ast, const FullySpecifiedType &init,
+ DeclaratorIdAST **declaratorId,
+ DecompositionDeclaratorAST **decompDeclarator)
{
FullySpecifiedType type = init;
@@ -347,6 +370,7 @@ FullySpecifiedType Bind::declarator(DeclaratorAST *ast, const FullySpecifiedType
return type;
std::swap(_declaratorId, declaratorId);
+ std::swap(_decompositionDeclarator, decompDeclarator);
bool isAuto = false;
const bool cxx11Enabled = translationUnit()->languageFeatures().cxx11Enabled;
if (cxx11Enabled)
@@ -369,7 +393,7 @@ FullySpecifiedType Bind::declarator(DeclaratorAST *ast, const FullySpecifiedType
if (type.isAuto())
isAuto = true;
}
- if (!type->isFunctionType()) {
+ if (!type->asFunctionType()) {
ExpressionTy initializer = this->expression(ast->initializer);
if (cxx11Enabled && isAuto) {
type = initializer;
@@ -378,6 +402,7 @@ FullySpecifiedType Bind::declarator(DeclaratorAST *ast, const FullySpecifiedType
}
std::swap(_declaratorId, declaratorId);
+ std::swap(_decompositionDeclarator, decompDeclarator);
return type;
}
@@ -413,12 +438,12 @@ bool Bind::visit(BaseSpecifierAST *ast)
return false;
}
-void Bind::baseSpecifier(BaseSpecifierAST *ast, unsigned colon_token, Class *klass)
+void Bind::baseSpecifier(BaseSpecifierAST *ast, int colon_token, Class *klass)
{
if (! ast)
return;
- unsigned sourceLocation = location(ast->name, ast->firstToken());
+ int sourceLocation = location(ast->name, ast->firstToken());
if (! sourceLocation)
sourceLocation = std::max(colon_token, klass->sourceLocation());
@@ -448,11 +473,11 @@ void Bind::ctorInitializer(CtorInitializerAST *ast, Function *fun)
if (! ast)
return;
- // unsigned colon_token = ast->colon_token;
+ // int colon_token = ast->colon_token;
for (MemInitializerListAST *it = ast->member_initializer_list; it; it = it->next) {
this->memInitializer(it->value, fun);
}
- // unsigned dot_dot_dot_token = ast->dot_dot_dot_token;
+ // int dot_dot_dot_token = ast->dot_dot_dot_token;
}
bool Bind::visit(EnumeratorAST *ast)
@@ -508,7 +533,7 @@ void calculateConstantValue(const Symbol *symbol, EnumeratorDeclaration *e, Cont
const std::string buffer
= std::to_string(static_cast<long long>(constantValueAsInt));
e->setConstantValue(control->stringLiteral(buffer.c_str(),
- unsigned(buffer.size())));
+ int(buffer.size())));
}
}
}
@@ -531,7 +556,7 @@ const StringLiteral *valueOfEnumerator(const Enum *e, const Identifier *value) {
}
}
}
- return 0;
+ return nullptr;
}
} // anonymous namespace
@@ -543,8 +568,8 @@ void Bind::enumerator(EnumeratorAST *ast, Enum *symbol)
if (! ast)
return;
- // unsigned identifier_token = ast->identifier_token;
- // unsigned equal_token = ast->equal_token;
+ // int identifier_token = ast->identifier_token;
+ // int equal_token = ast->equal_token;
/*ExpressionTy expression =*/ this->expression(ast->expression);
if (ast->identifier_token) {
@@ -556,7 +581,7 @@ void Bind::enumerator(EnumeratorAST *ast, Enum *symbol)
const int firstToken = expr->firstToken();
const int lastToken = expr->lastToken();
const StringLiteral *constantValue = asStringLiteral(expr);
- const StringLiteral *resolvedValue = 0;
+ const StringLiteral *resolvedValue = nullptr;
if (lastToken - firstToken == 1) {
if (const Identifier *constantId = identifier(firstToken))
resolvedValue = valueOfEnumerator(symbol, constantId);
@@ -587,16 +612,16 @@ FullySpecifiedType Bind::exceptionSpecification(ExceptionSpecificationAST *ast,
return type;
if (DynamicExceptionSpecificationAST *dyn = ast->asDynamicExceptionSpecification()) {
- // unsigned throw_token = ast->throw_token;
- // unsigned lparen_token = ast->lparen_token;
- // unsigned dot_dot_dot_token = ast->dot_dot_dot_token;
+ // int throw_token = ast->throw_token;
+ // int lparen_token = ast->lparen_token;
+ // int dot_dot_dot_token = ast->dot_dot_dot_token;
for (ExpressionListAST *it = dyn->type_id_list; it; it = it->next) {
/*ExpressionTy value =*/ this->expression(it->value);
}
} else if (NoExceptSpecificationAST *no = ast->asNoExceptSpecification()) {
/*ExpressionTy value =*/ this->expression(no->expression);
}
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return type;
}
@@ -629,7 +654,7 @@ bool Bind::visit(NestedNameSpecifierAST *ast)
const Name *Bind::nestedNameSpecifier(NestedNameSpecifierAST *ast)
{
if (! ast)
- return 0;
+ return nullptr;
const Name *class_or_namespace_name = this->name(ast->class_or_namespace_name);
return class_or_namespace_name;
@@ -637,11 +662,11 @@ const Name *Bind::nestedNameSpecifier(NestedNameSpecifierAST *ast)
bool Bind::visit(ExpressionListParenAST *ast)
{
- // unsigned lparen_token = ast->lparen_token;
+ // int lparen_token = ast->lparen_token;
for (ExpressionListAST *it = ast->expression_list; it; it = it->next) {
/*ExpressionTy value =*/ this->expression(it->value);
}
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
@@ -651,11 +676,11 @@ void Bind::newPlacement(ExpressionListParenAST *ast)
if (! ast)
return;
- // unsigned lparen_token = ast->lparen_token;
+ // int lparen_token = ast->lparen_token;
for (ExpressionListAST *it = ast->expression_list; it; it = it->next) {
ExpressionTy value = this->expression(it->value);
}
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
}
bool Bind::visit(NewArrayDeclaratorAST *ast)
@@ -672,9 +697,9 @@ FullySpecifiedType Bind::newArrayDeclarator(NewArrayDeclaratorAST *ast, const Fu
if (! ast)
return type;
- // unsigned lbracket_token = ast->lbracket_token;
+ // int lbracket_token = ast->lbracket_token;
ExpressionTy expression = this->expression(ast->expression);
- // unsigned rbracket_token = ast->rbracket_token;
+ // int rbracket_token = ast->rbracket_token;
return type;
}
@@ -719,9 +744,9 @@ OperatorNameId::Kind Bind::cppOperator(OperatorAST *ast)
if (! ast)
return kind;
- // unsigned op_token = ast->op_token;
- // unsigned open_token = ast->open_token;
- // unsigned close_token = ast->close_token;
+ // int op_token = ast->op_token;
+ // int open_token = ast->open_token;
+ // int close_token = ast->close_token;
switch (tokenKind(ast->op_token)) {
case T_NEW:
@@ -890,6 +915,10 @@ OperatorNameId::Kind Bind::cppOperator(OperatorAST *ast)
kind = OperatorNameId::ArrayAccessOp;
break;
+ case T_LESS_EQUAL_GREATER:
+ kind = OperatorNameId::SpaceShipOp;
+ break;
+
default:
kind = OperatorNameId::InvalidOp;
} // switch
@@ -904,7 +933,12 @@ bool Bind::visit(ParameterDeclarationClauseAST *ast)
return false;
}
-void Bind::parameterDeclarationClause(ParameterDeclarationClauseAST *ast, unsigned lparen_token, Function *fun)
+bool Bind::visit(RequiresExpressionAST *)
+{
+ return false;
+}
+
+void Bind::parameterDeclarationClause(ParameterDeclarationClauseAST *ast, int lparen_token, Function *fun)
{
if (! ast)
return;
@@ -918,6 +952,19 @@ void Bind::parameterDeclarationClause(ParameterDeclarationClauseAST *ast, unsign
for (ParameterDeclarationListAST *it = ast->parameter_declaration_list; it; it = it->next) {
this->declaration(it->value);
+
+ // Check for '...' in last parameter declarator for variadic template
+ // (i.e. template<class ... T> void foo(T ... args);)
+ // those last dots are part of parameter declarator, not the parameter declaration clause
+ if (! it->next
+ && it->value->declarator != nullptr
+ && it->value->declarator->core_declarator != nullptr){
+ DeclaratorIdAST* declId = it->value->declarator->core_declarator->asDeclaratorId();
+ if (declId && declId->dot_dot_dot_token != 0){
+ fun->setVariadic(true);
+ fun->setVariadicTemplate(true);
+ }
+ }
}
if (ast->dot_dot_dot_token)
@@ -992,7 +1039,7 @@ FullySpecifiedType Bind::objCTypeName(ObjCTypeNameAST *ast)
if (! ast)
return FullySpecifiedType();
- // unsigned type_qualifier_token = ast->type_qualifier_token;
+ // int type_qualifier_token = ast->type_qualifier_token;
ExpressionTy type_id = this->expression(ast->type_id);
return type_id;
}
@@ -1011,11 +1058,11 @@ void Bind::objCInstanceVariablesDeclaration(ObjCInstanceVariablesDeclarationAST
if (! ast)
return;
- // unsigned lbrace_token = ast->lbrace_token;
+ // int lbrace_token = ast->lbrace_token;
for (DeclarationListAST *it = ast->instance_variable_list; it; it = it->next) {
this->declaration(it->value);
}
- // unsigned rbrace_token = ast->rbrace_token;
+ // int rbrace_token = ast->rbrace_token;
}
bool Bind::visit(ObjCPropertyAttributeAST *ast)
@@ -1030,8 +1077,8 @@ void Bind::objCPropertyAttribute(ObjCPropertyAttributeAST *ast)
if (! ast)
return;
- // unsigned attribute_identifier_token = ast->attribute_identifier_token;
- // unsigned equals_token = ast->equals_token;
+ // int attribute_identifier_token = ast->attribute_identifier_token;
+ // int equals_token = ast->equals_token;
/*const Name *method_selector =*/ this->name(ast->method_selector);
}
@@ -1070,13 +1117,13 @@ bool Bind::visit(ObjCMethodPrototypeAST *ast)
ObjCMethod *Bind::objCMethodPrototype(ObjCMethodPrototypeAST *ast)
{
if (! ast)
- return 0;
+ return nullptr;
- // unsigned method_type_token = ast->method_type_token;
+ // int method_type_token = ast->method_type_token;
FullySpecifiedType returnType = this->objCTypeName(ast->type_name);
const Name *selector = this->name(ast->selector);
- const unsigned sourceLocation = location(ast->selector, ast->firstToken());
+ const int sourceLocation = location(ast->selector, ast->firstToken());
ObjCMethod *method = control()->newObjCMethod(sourceLocation, selector);
// ### set the offsets
method->setReturnType(returnType);
@@ -1115,9 +1162,9 @@ void Bind::objCSynthesizedProperty(ObjCSynthesizedPropertyAST *ast)
if (! ast)
return;
- // unsigned property_identifier_token = ast->property_identifier_token;
- // unsigned equals_token = ast->equals_token;
- // unsigned alias_identifier_token = ast->alias_identifier_token;
+ // int property_identifier_token = ast->property_identifier_token;
+ // int equals_token = ast->equals_token;
+ // int alias_identifier_token = ast->alias_identifier_token;
}
bool Bind::visit(LambdaIntroducerAST *ast)
@@ -1132,9 +1179,9 @@ void Bind::lambdaIntroducer(LambdaIntroducerAST *ast)
if (! ast)
return;
- // unsigned lbracket_token = ast->lbracket_token;
+ // int lbracket_token = ast->lbracket_token;
this->lambdaCapture(ast->lambda_capture);
- // unsigned rbracket_token = ast->rbracket_token;
+ // int rbracket_token = ast->rbracket_token;
}
bool Bind::visit(LambdaCaptureAST *ast)
@@ -1149,7 +1196,7 @@ void Bind::lambdaCapture(LambdaCaptureAST *ast)
if (! ast)
return;
- // unsigned default_capture_token = ast->default_capture_token;
+ // int default_capture_token = ast->default_capture_token;
for (CaptureListAST *it = ast->capture_list; it; it = it->next) {
this->capture(it->value);
}
@@ -1180,9 +1227,9 @@ bool Bind::visit(LambdaDeclaratorAST *ast)
Function *Bind::lambdaDeclarator(LambdaDeclaratorAST *ast)
{
if (! ast)
- return 0;
+ return nullptr;
- Function *fun = control()->newFunction(0, 0);
+ Function *fun = control()->newFunction(0, nullptr);
fun->setStartOffset(tokenAt(ast->firstToken()).utf16charsBegin());
fun->setEndOffset(tokenAt(ast->lastToken() - 1).utf16charsEnd());
@@ -1191,13 +1238,13 @@ Function *Bind::lambdaDeclarator(LambdaDeclaratorAST *ast)
type = this->trailingReturnType(ast->trailing_return_type, type);
ast->symbol = fun;
- // unsigned lparen_token = ast->lparen_token;
+ // int lparen_token = ast->lparen_token;
this->parameterDeclarationClause(ast->parameter_declaration_clause, ast->lparen_token, fun);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
for (SpecifierListAST *it = ast->attributes; it; it = it->next) {
type = this->specifier(it->value, type);
}
- // unsigned mutable_token = ast->mutable_token;
+ // int mutable_token = ast->mutable_token;
type = this->exceptionSpecification(ast->exception_specification, type);
if (!type.isValid())
@@ -1220,37 +1267,64 @@ FullySpecifiedType Bind::trailingReturnType(TrailingReturnTypeAST *ast, const Fu
if (! ast)
return type;
- // unsigned arrow_token = ast->arrow_token;
+ // int arrow_token = ast->arrow_token;
for (SpecifierListAST *it = ast->attributes; it; it = it->next) {
type = this->specifier(it->value, type);
}
for (SpecifierListAST *it = ast->type_specifier_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- DeclaratorIdAST *declaratorId = 0;
+ DeclaratorIdAST *declaratorId = nullptr;
type = this->declarator(ast->declarator, type, &declaratorId);
return type;
}
-const StringLiteral *Bind::asStringLiteral(const ExpressionAST *ast)
+const StringLiteral *Bind::asStringLiteral(const AST *ast)
{
- CPP_ASSERT(ast, return 0);
- const unsigned firstToken = ast->firstToken();
- const unsigned lastToken = ast->lastToken();
+ CPP_ASSERT(ast, return nullptr);
+ const int firstToken = ast->firstToken();
+ const int lastToken = ast->lastToken();
std::string buffer;
- for (unsigned index = firstToken; index != lastToken; ++index) {
+
+ const auto token = tokenAt(ast->firstToken());
+
+ if (token.isCharLiteral()) {
+ if (token.kind() == T_WIDE_CHAR_LITERAL)
+ buffer += 'L';
+ else if (token.kind() == T_UTF16_CHAR_LITERAL)
+ buffer += 'u';
+ else if (token.kind() == T_UTF32_CHAR_LITERAL)
+ buffer += 'U';
+ buffer += '\'';
+ } else if (token.isStringLiteral()) {
+ if (token.kind() == T_WIDE_STRING_LITERAL)
+ buffer += 'L';
+ else if (token.kind() == T_UTF16_STRING_LITERAL)
+ buffer += 'u';
+ else if (token.kind() == T_UTF32_STRING_LITERAL)
+ buffer += 'U';
+ else if (token.kind() == T_UTF8_STRING_LITERAL)
+ buffer += "u8";
+ buffer += '"';
+ }
+ for (int index = firstToken; index != lastToken; ++index) {
const Token &tk = tokenAt(index);
if (index != firstToken && (tk.whitespace() || tk.newline()))
buffer += ' ';
buffer += tk.spell();
}
- return control()->stringLiteral(buffer.c_str(), unsigned(buffer.size()));
+ if (token.isCharLiteral())
+ buffer += '\'';
+ else if (token.isStringLiteral())
+ buffer += '"';
+
+ return control()->stringLiteral(buffer.c_str(), int(buffer.size()));
}
// StatementAST
bool Bind::visit(QtMemberDeclarationAST *ast)
{
- const Name *name = 0;
+ const Name *name = nullptr;
if (tokenKind(ast->q_token) == T_Q_D)
name = control()->identifier("d");
@@ -1266,7 +1340,7 @@ bool Bind::visit(QtMemberDeclarationAST *ast)
privateClass += nameId->identifier()->chars();
privateClass += "Private";
- const Name *privName = control()->identifier(privateClass.c_str(), unsigned(privateClass.size()));
+ const Name *privName = control()->identifier(privateClass.c_str(), int(privateClass.size()));
declTy.setType(control()->namedType(privName));
}
}
@@ -1289,7 +1363,7 @@ bool Bind::visit(CaseStatementAST *ast)
bool Bind::visit(CompoundStatementAST *ast)
{
Block *block = control()->newBlock(ast->firstToken());
- unsigned startScopeToken = ast->lbrace_token ? ast->lbrace_token : ast->firstToken();
+ int startScopeToken = ast->lbrace_token ? ast->lbrace_token : ast->firstToken();
block->setStartOffset(tokenAt(startScopeToken).utf16charsEnd());
block->setEndOffset(tokenAt(ast->lastToken() - 1).utf16charsEnd());
ast->symbol = block;
@@ -1325,14 +1399,14 @@ bool Bind::visit(ExpressionOrDeclarationStatementAST *ast)
bool Bind::visit(ExpressionStatementAST *ast)
{
ExpressionTy expression = this->expression(ast->expression);
- // unsigned semicolon_token = ast->semicolon_token;
+ // int semicolon_token = ast->semicolon_token;
return false;
}
bool Bind::visit(ForeachStatementAST *ast)
{
Block *block = control()->newBlock(ast->firstToken());
- const unsigned startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
+ const int startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
block->setStartOffset(tokenAt(startScopeToken).utf16charsEnd());
block->setEndOffset(tokenAt(ast->lastToken()).utf16charsBegin());
_scope->addMember(block);
@@ -1344,26 +1418,26 @@ bool Bind::visit(ForeachStatementAST *ast)
for (SpecifierListAST *it = ast->type_specifier_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- DeclaratorIdAST *declaratorId = 0;
+ DeclaratorIdAST *declaratorId = nullptr;
type = this->declarator(ast->declarator, type, &declaratorId);
- const StringLiteral *initializer = 0;
+ const StringLiteral *initializer = nullptr;
if (type.isAuto() && translationUnit()->languageFeatures().cxx11Enabled) {
ExpressionTy exprType = this->expression(ast->expression);
- ArrayType* arrayType = 0;
+ ArrayType* arrayType = nullptr;
arrayType = exprType->asArrayType();
- if (arrayType != 0)
+ if (arrayType != nullptr)
type = arrayType->elementType();
- else if (ast->expression != 0) {
+ else if (ast->expression != nullptr) {
const StringLiteral *sl = asStringLiteral(ast->expression);
const std::string buff = std::string("*") + sl->chars() + ".begin()";
- initializer = control()->stringLiteral(buff.c_str(), unsigned(buff.size()));
+ initializer = control()->stringLiteral(buff.c_str(), int(buff.size()));
}
}
if (declaratorId && declaratorId->name) {
- unsigned sourceLocation = location(declaratorId->name, ast->firstToken());
+ int sourceLocation = location(declaratorId->name, ast->firstToken());
Declaration *decl = control()->newDeclaration(sourceLocation, declaratorId->name->name);
decl->setType(type);
decl->setInitializer(initializer);
@@ -1380,7 +1454,7 @@ bool Bind::visit(ForeachStatementAST *ast)
bool Bind::visit(RangeBasedForStatementAST *ast)
{
Block *block = control()->newBlock(ast->firstToken());
- const unsigned startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
+ const int startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
block->setStartOffset(tokenAt(startScopeToken).utf16charsEnd());
block->setEndOffset(tokenAt(ast->lastToken()).utf16charsBegin());
_scope->addMember(block);
@@ -1392,26 +1466,26 @@ bool Bind::visit(RangeBasedForStatementAST *ast)
for (SpecifierListAST *it = ast->type_specifier_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- DeclaratorIdAST *declaratorId = 0;
+ DeclaratorIdAST *declaratorId = nullptr;
type = this->declarator(ast->declarator, type, &declaratorId);
- const StringLiteral *initializer = 0;
+ const StringLiteral *initializer = nullptr;
if (type.isAuto() && translationUnit()->languageFeatures().cxx11Enabled) {
ExpressionTy exprType = this->expression(ast->expression);
- ArrayType* arrayType = 0;
+ ArrayType* arrayType = nullptr;
arrayType = exprType->asArrayType();
- if (arrayType != 0)
+ if (arrayType != nullptr)
type = arrayType->elementType();
- else if (ast->expression != 0) {
+ else if (ast->expression != nullptr) {
const StringLiteral *sl = asStringLiteral(ast->expression);
const std::string buff = std::string("*") + sl->chars() + ".begin()";
- initializer = control()->stringLiteral(buff.c_str(), unsigned(buff.size()));
+ initializer = control()->stringLiteral(buff.c_str(), int(buff.size()));
}
}
if (declaratorId && declaratorId->name) {
- unsigned sourceLocation = location(declaratorId->name, ast->firstToken());
+ int sourceLocation = location(declaratorId->name, ast->firstToken());
Declaration *decl = control()->newDeclaration(sourceLocation, declaratorId->name->name);
decl->setType(type);
decl->setInitializer(initializer);
@@ -1427,7 +1501,7 @@ bool Bind::visit(RangeBasedForStatementAST *ast)
bool Bind::visit(ForStatementAST *ast)
{
Block *block = control()->newBlock(ast->firstToken());
- const unsigned startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
+ const int startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
block->setStartOffset(tokenAt(startScopeToken).utf16charsEnd());
block->setEndOffset(tokenAt(ast->lastToken()).utf16charsBegin());
_scope->addMember(block);
@@ -1436,9 +1510,9 @@ bool Bind::visit(ForStatementAST *ast)
Scope *previousScope = switchScope(block);
this->statement(ast->initializer);
/*ExpressionTy condition =*/ this->expression(ast->condition);
- // unsigned semicolon_token = ast->semicolon_token;
+ // int semicolon_token = ast->semicolon_token;
/*ExpressionTy expression =*/ this->expression(ast->expression);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
this->statement(ast->statement);
(void) switchScope(previousScope);
return false;
@@ -1447,13 +1521,15 @@ bool Bind::visit(ForStatementAST *ast)
bool Bind::visit(IfStatementAST *ast)
{
Block *block = control()->newBlock(ast->firstToken());
- const unsigned startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
+ const int startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
block->setStartOffset(tokenAt(startScopeToken).utf16charsEnd());
block->setEndOffset(tokenAt(ast->lastToken()).utf16charsBegin());
_scope->addMember(block);
ast->symbol = block;
Scope *previousScope = switchScope(block);
+ if (ast->initStmt)
+ this->statement(ast->initStmt);
/*ExpressionTy condition =*/ this->expression(ast->condition);
this->statement(ast->statement);
this->statement(ast->else_statement);
@@ -1463,8 +1539,8 @@ bool Bind::visit(IfStatementAST *ast)
bool Bind::visit(LabeledStatementAST *ast)
{
- // unsigned label_token = ast->label_token;
- // unsigned colon_token = ast->colon_token;
+ // int label_token = ast->label_token;
+ // int colon_token = ast->colon_token;
this->statement(ast->statement);
return false;
}
@@ -1472,25 +1548,25 @@ bool Bind::visit(LabeledStatementAST *ast)
bool Bind::visit(BreakStatementAST *ast)
{
(void) ast;
- // unsigned break_token = ast->break_token;
- // unsigned semicolon_token = ast->semicolon_token;
+ // int break_token = ast->break_token;
+ // int semicolon_token = ast->semicolon_token;
return false;
}
bool Bind::visit(ContinueStatementAST *ast)
{
(void) ast;
- // unsigned continue_token = ast->continue_token;
- // unsigned semicolon_token = ast->semicolon_token;
+ // int continue_token = ast->continue_token;
+ // int semicolon_token = ast->semicolon_token;
return false;
}
bool Bind::visit(GotoStatementAST *ast)
{
(void) ast;
- // unsigned goto_token = ast->goto_token;
- // unsigned identifier_token = ast->identifier_token;
- // unsigned semicolon_token = ast->semicolon_token;
+ // int goto_token = ast->goto_token;
+ // int identifier_token = ast->identifier_token;
+ // int semicolon_token = ast->semicolon_token;
return false;
}
@@ -1503,7 +1579,7 @@ bool Bind::visit(ReturnStatementAST *ast)
bool Bind::visit(SwitchStatementAST *ast)
{
Block *block = control()->newBlock(ast->firstToken());
- const unsigned startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
+ const int startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
block->setStartOffset(tokenAt(startScopeToken).utf16charsEnd());
block->setEndOffset(tokenAt(ast->lastToken()).utf16charsBegin());
_scope->addMember(block);
@@ -1518,7 +1594,7 @@ bool Bind::visit(SwitchStatementAST *ast)
bool Bind::visit(TryBlockStatementAST *ast)
{
- // unsigned try_token = ast->try_token;
+ // int try_token = ast->try_token;
this->statement(ast->statement);
for (CatchClauseListAST *it = ast->catch_clause_list; it; it = it->next) {
this->statement(it->value);
@@ -1529,7 +1605,7 @@ bool Bind::visit(TryBlockStatementAST *ast)
bool Bind::visit(CatchClauseAST *ast)
{
Block *block = control()->newBlock(ast->firstToken());
- const unsigned startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
+ const int startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
block->setStartOffset(tokenAt(startScopeToken).utf16charsEnd());
block->setEndOffset(tokenAt(ast->lastToken()).utf16charsBegin());
_scope->addMember(block);
@@ -1537,7 +1613,7 @@ bool Bind::visit(CatchClauseAST *ast)
Scope *previousScope = switchScope(block);
this->declaration(ast->exception_declaration);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
this->statement(ast->statement);
(void) switchScope(previousScope);
return false;
@@ -1546,7 +1622,7 @@ bool Bind::visit(CatchClauseAST *ast)
bool Bind::visit(WhileStatementAST *ast)
{
Block *block = control()->newBlock(ast->firstToken());
- const unsigned startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
+ const int startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
block->setStartOffset(tokenAt(startScopeToken).utf16charsEnd());
block->setEndOffset(tokenAt(ast->lastToken()).utf16charsBegin());
_scope->addMember(block);
@@ -1562,7 +1638,7 @@ bool Bind::visit(WhileStatementAST *ast)
bool Bind::visit(ObjCFastEnumerationAST *ast)
{
Block *block = control()->newBlock(ast->firstToken());
- const unsigned startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
+ const int startScopeToken = ast->lparen_token ? ast->lparen_token : ast->firstToken();
block->setStartOffset(tokenAt(startScopeToken).utf16charsEnd());
block->setEndOffset(tokenAt(ast->lastToken()).utf16charsBegin());
_scope->addMember(block);
@@ -1573,11 +1649,11 @@ bool Bind::visit(ObjCFastEnumerationAST *ast)
for (SpecifierListAST *it = ast->type_specifier_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- DeclaratorIdAST *declaratorId = 0;
+ DeclaratorIdAST *declaratorId = nullptr;
type = this->declarator(ast->declarator, type, &declaratorId);
if (declaratorId && declaratorId->name) {
- unsigned sourceLocation = location(declaratorId->name, ast->firstToken());
+ int sourceLocation = location(declaratorId->name, ast->firstToken());
Declaration *decl = control()->newDeclaration(sourceLocation, declaratorId->name->name);
decl->setType(type);
block->addMember(decl);
@@ -1592,10 +1668,10 @@ bool Bind::visit(ObjCFastEnumerationAST *ast)
bool Bind::visit(ObjCSynchronizedStatementAST *ast)
{
- // unsigned synchronized_token = ast->synchronized_token;
- // unsigned lparen_token = ast->lparen_token;
+ // int synchronized_token = ast->synchronized_token;
+ // int lparen_token = ast->lparen_token;
ExpressionTy synchronized_object = this->expression(ast->synchronized_object);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
this->statement(ast->statement);
return false;
}
@@ -1610,45 +1686,45 @@ bool Bind::visit(IdExpressionAST *ast)
bool Bind::visit(CompoundExpressionAST *ast)
{
- // unsigned lparen_token = ast->lparen_token;
+ // int lparen_token = ast->lparen_token;
this->statement(ast->statement);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
bool Bind::visit(CompoundLiteralAST *ast)
{
- // unsigned lparen_token = ast->lparen_token;
+ // int lparen_token = ast->lparen_token;
ExpressionTy type_id = this->expression(ast->type_id);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
ExpressionTy initializer = this->expression(ast->initializer);
return false;
}
bool Bind::visit(QtMethodAST *ast)
{
- // unsigned method_token = ast->method_token;
- // unsigned lparen_token = ast->lparen_token;
+ // int method_token = ast->method_token;
+ // int lparen_token = ast->lparen_token;
FullySpecifiedType type;
- DeclaratorIdAST *declaratorId = 0;
+ DeclaratorIdAST *declaratorId = nullptr;
type = this->declarator(ast->declarator, type, &declaratorId);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
bool Bind::visit(BinaryExpressionAST *ast)
{
ExpressionTy left_expression = this->expression(ast->left_expression);
- // unsigned binary_op_token = ast->binary_op_token;
+ // int binary_op_token = ast->binary_op_token;
ExpressionTy right_expression = this->expression(ast->right_expression);
return false;
}
bool Bind::visit(CastExpressionAST *ast)
{
- // unsigned lparen_token = ast->lparen_token;
+ // int lparen_token = ast->lparen_token;
ExpressionTy type_id = this->expression(ast->type_id);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
ExpressionTy expression = this->expression(ast->expression);
return false;
}
@@ -1659,11 +1735,11 @@ bool Bind::visit(ConditionAST *ast)
for (SpecifierListAST *it = ast->type_specifier_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- DeclaratorIdAST *declaratorId = 0;
+ DeclaratorIdAST *declaratorId = nullptr;
type = this->declarator(ast->declarator, type, &declaratorId);
if (declaratorId && declaratorId->name) {
- unsigned sourceLocation = location(declaratorId->name, ast->firstToken());
+ int sourceLocation = location(declaratorId->name, ast->firstToken());
Declaration *decl = control()->newDeclaration(sourceLocation, declaratorId->name->name);
decl->setType(type);
@@ -1679,53 +1755,53 @@ bool Bind::visit(ConditionAST *ast)
bool Bind::visit(ConditionalExpressionAST *ast)
{
ExpressionTy condition = this->expression(ast->condition);
- // unsigned question_token = ast->question_token;
+ // int question_token = ast->question_token;
ExpressionTy left_expression = this->expression(ast->left_expression);
- // unsigned colon_token = ast->colon_token;
+ // int colon_token = ast->colon_token;
ExpressionTy right_expression = this->expression(ast->right_expression);
return false;
}
bool Bind::visit(CppCastExpressionAST *ast)
{
- // unsigned cast_token = ast->cast_token;
- // unsigned less_token = ast->less_token;
+ // int cast_token = ast->cast_token;
+ // int less_token = ast->less_token;
ExpressionTy type_id = this->expression(ast->type_id);
- // unsigned greater_token = ast->greater_token;
- // unsigned lparen_token = ast->lparen_token;
+ // int greater_token = ast->greater_token;
+ // int lparen_token = ast->lparen_token;
ExpressionTy expression = this->expression(ast->expression);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
bool Bind::visit(DeleteExpressionAST *ast)
{
- // unsigned scope_token = ast->scope_token;
- // unsigned delete_token = ast->delete_token;
- // unsigned lbracket_token = ast->lbracket_token;
- // unsigned rbracket_token = ast->rbracket_token;
+ // int scope_token = ast->scope_token;
+ // int delete_token = ast->delete_token;
+ // int lbracket_token = ast->lbracket_token;
+ // int rbracket_token = ast->rbracket_token;
ExpressionTy expression = this->expression(ast->expression);
return false;
}
bool Bind::visit(ArrayInitializerAST *ast)
{
- // unsigned lbrace_token = ast->lbrace_token;
+ // int lbrace_token = ast->lbrace_token;
for (ExpressionListAST *it = ast->expression_list; it; it = it->next) {
ExpressionTy value = this->expression(it->value);
}
- // unsigned rbrace_token = ast->rbrace_token;
+ // int rbrace_token = ast->rbrace_token;
return false;
}
bool Bind::visit(NewExpressionAST *ast)
{
- // unsigned scope_token = ast->scope_token;
- // unsigned new_token = ast->new_token;
+ // int scope_token = ast->scope_token;
+ // int new_token = ast->new_token;
this->newPlacement(ast->new_placement);
- // unsigned lparen_token = ast->lparen_token;
+ // int lparen_token = ast->lparen_token;
ExpressionTy type_id = this->expression(ast->type_id);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
this->newTypeId(ast->new_type_id);
this->expression(ast->new_initializer);
return false;
@@ -1733,16 +1809,16 @@ bool Bind::visit(NewExpressionAST *ast)
bool Bind::visit(TypeidExpressionAST *ast)
{
- // unsigned typeid_token = ast->typeid_token;
- // unsigned lparen_token = ast->lparen_token;
+ // int typeid_token = ast->typeid_token;
+ // int lparen_token = ast->lparen_token;
ExpressionTy expression = this->expression(ast->expression);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
bool Bind::visit(TypenameCallExpressionAST *ast)
{
- // unsigned typename_token = ast->typename_token;
+ // int typename_token = ast->typename_token;
/*const Name *name =*/ this->name(ast->name);
this->expression(ast->expression);
return false;
@@ -1760,60 +1836,60 @@ bool Bind::visit(TypeConstructorCallAST *ast)
bool Bind::visit(SizeofExpressionAST *ast)
{
- // unsigned sizeof_token = ast->sizeof_token;
- // unsigned dot_dot_dot_token = ast->dot_dot_dot_token;
- // unsigned lparen_token = ast->lparen_token;
+ // int sizeof_token = ast->sizeof_token;
+ // int dot_dot_dot_token = ast->dot_dot_dot_token;
+ // int lparen_token = ast->lparen_token;
ExpressionTy expression = this->expression(ast->expression);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
bool Bind::visit(PointerLiteralAST *ast)
{
(void) ast;
- // unsigned literal_token = ast->literal_token;
+ // int literal_token = ast->literal_token;
return false;
}
bool Bind::visit(NumericLiteralAST *ast)
{
(void) ast;
- // unsigned literal_token = ast->literal_token;
+ // int literal_token = ast->literal_token;
return false;
}
bool Bind::visit(BoolLiteralAST *ast)
{
(void) ast;
- // unsigned literal_token = ast->literal_token;
+ // int literal_token = ast->literal_token;
return false;
}
bool Bind::visit(ThisExpressionAST *ast)
{
(void) ast;
- // unsigned this_token = ast->this_token;
+ // int this_token = ast->this_token;
return false;
}
bool Bind::visit(NestedExpressionAST *ast)
{
- // unsigned lparen_token = ast->lparen_token;
+ // int lparen_token = ast->lparen_token;
ExpressionTy expression = this->expression(ast->expression);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
bool Bind::visit(StringLiteralAST *ast)
{
- // unsigned literal_token = ast->literal_token;
+ // int literal_token = ast->literal_token;
ExpressionTy next = this->expression(ast->next);
return false;
}
bool Bind::visit(ThrowExpressionAST *ast)
{
- // unsigned throw_token = ast->throw_token;
+ // int throw_token = ast->throw_token;
ExpressionTy expression = this->expression(ast->expression);
return false;
}
@@ -1824,7 +1900,7 @@ bool Bind::visit(TypeIdAST *ast)
for (SpecifierListAST *it = ast->type_specifier_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- DeclaratorIdAST *declaratorId = 0;
+ DeclaratorIdAST *declaratorId = nullptr;
type = this->declarator(ast->declarator, type, &declaratorId);
_expression = type;
return false;
@@ -1832,46 +1908,46 @@ bool Bind::visit(TypeIdAST *ast)
bool Bind::visit(UnaryExpressionAST *ast)
{
- // unsigned unary_op_token = ast->unary_op_token;
+ // int unary_op_token = ast->unary_op_token;
ExpressionTy expression = this->expression(ast->expression);
return false;
}
bool Bind::visit(ObjCMessageExpressionAST *ast)
{
- // unsigned lbracket_token = ast->lbracket_token;
+ // int lbracket_token = ast->lbracket_token;
/*ExpressionTy receiver_expression =*/ this->expression(ast->receiver_expression);
/*const Name *selector =*/ this->name(ast->selector);
for (ObjCMessageArgumentListAST *it = ast->argument_list; it; it = it->next) {
this->objCMessageArgument(it->value);
}
- // unsigned rbracket_token = ast->rbracket_token;
+ // int rbracket_token = ast->rbracket_token;
return false;
}
bool Bind::visit(ObjCProtocolExpressionAST *ast)
{
(void) ast;
- // unsigned protocol_token = ast->protocol_token;
- // unsigned lparen_token = ast->lparen_token;
- // unsigned identifier_token = ast->identifier_token;
- // unsigned rparen_token = ast->rparen_token;
+ // int protocol_token = ast->protocol_token;
+ // int lparen_token = ast->lparen_token;
+ // int identifier_token = ast->identifier_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
bool Bind::visit(ObjCEncodeExpressionAST *ast)
{
- // unsigned encode_token = ast->encode_token;
+ // int encode_token = ast->encode_token;
FullySpecifiedType type = this->objCTypeName(ast->type_name);
return false;
}
bool Bind::visit(ObjCSelectorExpressionAST *ast)
{
- // unsigned selector_token = ast->selector_token;
- // unsigned lparen_token = ast->lparen_token;
+ // int selector_token = ast->selector_token;
+ // int lparen_token = ast->lparen_token;
/*const Name *selector =*/ this->name(ast->selector);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
@@ -1879,6 +1955,7 @@ bool Bind::visit(LambdaExpressionAST *ast)
{
this->lambdaIntroducer(ast->lambda_introducer);
if (Function *function = this->lambdaDeclarator(ast->lambda_declarator)) {
+ function->setSourceLocation(ast->lambda_introducer->firstToken(), translationUnit());
_scope->addMember(function);
Scope *previousScope = switchScope(function);
this->statement(ast->statement);
@@ -1892,12 +1969,12 @@ bool Bind::visit(LambdaExpressionAST *ast)
bool Bind::visit(BracedInitializerAST *ast)
{
- // unsigned lbrace_token = ast->lbrace_token;
+ // int lbrace_token = ast->lbrace_token;
for (ExpressionListAST *it = ast->expression_list; it; it = it->next) {
/*ExpressionTy value =*/ this->expression(it->value);
}
- // unsigned comma_token = ast->comma_token;
- // unsigned rbrace_token = ast->rbrace_token;
+ // int comma_token = ast->comma_token;
+ // int rbrace_token = ast->rbrace_token;
return false;
}
@@ -1920,7 +1997,7 @@ bool Bind::visit(SimpleDeclarationAST *ast)
if (ast->qt_invokable_token)
methodKey = methodKeyForInvokableToken(tokenKind(ast->qt_invokable_token));
- // unsigned qt_invokable_token = ast->qt_invokable_token;
+ // int qt_invokable_token = ast->qt_invokable_token;
FullySpecifiedType type;
for (SpecifierListAST *it = ast->decl_specifier_list; it; it = it->next) {
type = this->specifier(it->value, type);
@@ -1929,13 +2006,13 @@ bool Bind::visit(SimpleDeclarationAST *ast)
List<Symbol *> **symbolTail = &ast->symbols;
if (! ast->declarator_list) {
- ElaboratedTypeSpecifierAST *elabTypeSpec = 0;
+ ElaboratedTypeSpecifierAST *elabTypeSpec = nullptr;
for (SpecifierListAST *it = ast->decl_specifier_list; ! elabTypeSpec && it; it = it->next)
elabTypeSpec = it->value->asElaboratedTypeSpecifier();
if (elabTypeSpec && tokenKind(elabTypeSpec->classkey_token) != T_TYPENAME) {
- unsigned sourceLocation = elabTypeSpec->firstToken();
- const Name *name = 0;
+ int sourceLocation = elabTypeSpec->firstToken();
+ const Name *name = nullptr;
if (elabTypeSpec->name) {
sourceLocation = location(elabTypeSpec->name, sourceLocation);
name = elabTypeSpec->name->name;
@@ -1953,52 +2030,70 @@ bool Bind::visit(SimpleDeclarationAST *ast)
}
for (DeclaratorListAST *it = ast->declarator_list; it; it = it->next) {
- DeclaratorIdAST *declaratorId = 0;
- FullySpecifiedType declTy = this->declarator(it->value, type, &declaratorId);
+ DeclaratorIdAST *declaratorId = nullptr;
+ DecompositionDeclaratorAST *decompDeclarator = nullptr;
+ FullySpecifiedType declTy = this->declarator(it->value, type, &declaratorId,
+ &decompDeclarator);
- const Name *declName = 0;
- unsigned sourceLocation = location(it->value, ast->firstToken());
- if (declaratorId && declaratorId->name)
- declName = declaratorId->name->name;
+ std::vector<std::pair<const Name *, int>> namesAndLocations;
+ if (declaratorId && declaratorId->name) {
+ namesAndLocations.push_back({declaratorId->name->name,
+ location(it->value, ast->firstToken())});
+ } else if (decompDeclarator) {
+ for (auto it = decompDeclarator->identifiers->begin();
+ it != decompDeclarator->identifiers->end(); ++it) {
+ if ((*it)->name)
+ namesAndLocations.push_back({(*it)->name, (*it)->firstToken()});
+ }
+ }
- Declaration *decl = control()->newDeclaration(sourceLocation, declName);
- decl->setType(declTy);
- setDeclSpecifiers(decl, type);
+ for (const auto &nameAndLoc : std::as_const(namesAndLocations)) {
+ const int sourceLocation = nameAndLoc.second;
+ Declaration *decl = control()->newDeclaration(sourceLocation, nameAndLoc.first);
+ if (const Type * const t = declTy.type(); t && declTy.isTypedef() && t->asClassType()
+ && t->asClassType()->name() && t->asClassType()->name()->asAnonymousNameId()) {
+ declTy.type()->asClassType()->setName(decl->name());
+ }
+ decl->setType(declTy);
+ setDeclSpecifiers(decl, type);
- if (Function *fun = decl->type()->asFunctionType()) {
- fun->setEnclosingScope(_scope);
- fun->setSourceLocation(sourceLocation, translationUnit());
+ if (Function *fun = decl->type()->asFunctionType()) {
+ fun->setEnclosingScope(_scope);
+ fun->setSourceLocation(sourceLocation, translationUnit());
- setDeclSpecifiers(fun, type);
- if (declaratorId && declaratorId->name)
- fun->setName(declaratorId->name->name); // update the function name
- }
- else if (declTy.isAuto()) {
- const ExpressionAST *initializer = it->value->initializer;
- if (!initializer && declaratorId)
- translationUnit()->error(location(declaratorId->name, ast->firstToken()), "auto-initialized variable must have an initializer");
- else if (initializer)
- decl->setInitializer(asStringLiteral(initializer));
- }
+ setDeclSpecifiers(fun, type);
+ if (declaratorId && declaratorId->name)
+ fun->setName(declaratorId->name->name); // update the function name
+ }
+ else if (declTy.isAuto()) {
+ const ExpressionAST *initializer = it->value->initializer;
+ if (!initializer && declaratorId) {
+ translationUnit()->error(location(declaratorId->name, ast->firstToken()),
+ "auto-initialized variable must have an initializer");
+ } else if (initializer) {
+ decl->setInitializer(asStringLiteral(initializer));
+ }
+ }
- if (_scope->isClass()) {
- decl->setVisibility(_visibility);
+ if (_scope->asClass()) {
+ decl->setVisibility(_visibility);
- if (Function *funTy = decl->type()->asFunctionType()) {
- funTy->setMethodKey(methodKey);
+ if (Function *funTy = decl->type()->asFunctionType()) {
+ funTy->setMethodKey(methodKey);
- bool pureVirtualInit = it->value->equal_token
- && it->value->initializer
- && it->value->initializer->asNumericLiteral();
- if (funTy->isVirtual() && pureVirtualInit)
- funTy->setPureVirtual(true);
+ bool pureVirtualInit = it->value->equal_token
+ && it->value->initializer
+ && it->value->initializer->asNumericLiteral();
+ if (funTy->isVirtual() && pureVirtualInit)
+ funTy->setPureVirtual(true);
+ }
}
- }
- _scope->addMember(decl);
+ _scope->addMember(decl);
- *symbolTail = new (translationUnit()->memoryPool()) List<Symbol *>(decl);
- symbolTail = &(*symbolTail)->next;
+ *symbolTail = new (translationUnit()->memoryPool()) List<Symbol *>(decl);
+ symbolTail = &(*symbolTail)->next;
+ }
}
return false;
}
@@ -2006,8 +2101,8 @@ bool Bind::visit(SimpleDeclarationAST *ast)
bool Bind::visit(EmptyDeclarationAST *ast)
{
(void) ast;
- unsigned semicolon_token = ast->semicolon_token;
- if (_scope && (_scope->isClass() || _scope->isNamespace())) {
+ int semicolon_token = ast->semicolon_token;
+ if (_scope && (_scope->asClass() || _scope->asNamespace())) {
const Token &tk = tokenAt(semicolon_token);
if (! tk.generated())
@@ -2034,25 +2129,25 @@ bool Bind::visit(AccessDeclarationAST *ast)
bool Bind::visit(QtObjectTagAST *ast)
{
(void) ast;
- // unsigned q_object_token = ast->q_object_token;
+ // int q_object_token = ast->q_object_token;
return false;
}
bool Bind::visit(QtPrivateSlotAST *ast)
{
- // unsigned q_private_slot_token = ast->q_private_slot_token;
- // unsigned lparen_token = ast->lparen_token;
- // unsigned dptr_token = ast->dptr_token;
- // unsigned dptr_lparen_token = ast->dptr_lparen_token;
- // unsigned dptr_rparen_token = ast->dptr_rparen_token;
- // unsigned comma_token = ast->comma_token;
+ // int q_private_slot_token = ast->q_private_slot_token;
+ // int lparen_token = ast->lparen_token;
+ // int dptr_token = ast->dptr_token;
+ // int dptr_lparen_token = ast->dptr_lparen_token;
+ // int dptr_rparen_token = ast->dptr_rparen_token;
+ // int comma_token = ast->comma_token;
FullySpecifiedType type;
for (SpecifierListAST *it = ast->type_specifier_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- DeclaratorIdAST *declaratorId = 0;
+ DeclaratorIdAST *declaratorId = nullptr;
type = this->declarator(ast->declarator, type, &declaratorId);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
@@ -2075,12 +2170,12 @@ static void qtPropertyAttribute(TranslationUnit *unit, ExpressionAST *expression
bool Bind::visit(QtPropertyDeclarationAST *ast)
{
- // unsigned property_specifier_token = ast->property_specifier_token;
- // unsigned lparen_token = ast->lparen_token;
+ // int property_specifier_token = ast->property_specifier_token;
+ // int lparen_token = ast->lparen_token;
ExpressionTy type_id = this->expression(ast->type_id);
const Name *property_name = this->name(ast->property_name);
- unsigned sourceLocation = ast->firstToken();
+ int sourceLocation = ast->firstToken();
if (ast->property_name)
sourceLocation = ast->property_name->firstToken();
QtPropertyDeclaration *propertyDeclaration = control()->newQtPropertyDeclaration(sourceLocation, property_name);
@@ -2128,14 +2223,14 @@ bool Bind::visit(QtPropertyDeclarationAST *ast)
}
propertyDeclaration->setFlags(flags);
_scope->addMember(propertyDeclaration);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
bool Bind::visit(QtEnumDeclarationAST *ast)
{
- // unsigned enum_specifier_token = ast->enum_specifier_token;
- // unsigned lparen_token = ast->lparen_token;
+ // int enum_specifier_token = ast->enum_specifier_token;
+ // int lparen_token = ast->lparen_token;
for (NameListAST *it = ast->enumerator_list; it; it = it->next) {
const Name *value = this->name(it->value);
if (!value)
@@ -2144,29 +2239,29 @@ bool Bind::visit(QtEnumDeclarationAST *ast)
_scope->addMember(qtEnum);
}
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
bool Bind::visit(QtFlagsDeclarationAST *ast)
{
- // unsigned flags_specifier_token = ast->flags_specifier_token;
- // unsigned lparen_token = ast->lparen_token;
+ // int flags_specifier_token = ast->flags_specifier_token;
+ // int lparen_token = ast->lparen_token;
for (NameListAST *it = ast->flag_enums_list; it; it = it->next) {
/*const Name *value =*/ this->name(it->value);
}
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
bool Bind::visit(QtInterfacesDeclarationAST *ast)
{
- // unsigned interfaces_token = ast->interfaces_token;
- // unsigned lparen_token = ast->lparen_token;
+ // int interfaces_token = ast->interfaces_token;
+ // int lparen_token = ast->lparen_token;
for (QtInterfaceNameListAST *it = ast->interface_name_list; it; it = it->next) {
this->qtInterfaceName(it->value);
}
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
@@ -2184,7 +2279,7 @@ bool Bind::visit(AliasDeclarationAST *ast)
decl->setType(ty);
decl->setStorage(Symbol::Typedef);
ast->symbol = decl;
- if (_scope->isClass())
+ if (_scope->asClass())
decl->setVisibility(_visibility);
_scope->addMember(decl);
@@ -2194,11 +2289,11 @@ bool Bind::visit(AliasDeclarationAST *ast)
bool Bind::visit(AsmDefinitionAST *ast)
{
(void) ast;
- // unsigned asm_token = ast->asm_token;
- // unsigned volatile_token = ast->volatile_token;
- // unsigned lparen_token = ast->lparen_token;
- // unsigned rparen_token = ast->rparen_token;
- // unsigned semicolon_token = ast->semicolon_token;
+ // int asm_token = ast->asm_token;
+ // int volatile_token = ast->volatile_token;
+ // int lparen_token = ast->lparen_token;
+ // int rparen_token = ast->rparen_token;
+ // int semicolon_token = ast->semicolon_token;
return false;
}
@@ -2208,17 +2303,17 @@ bool Bind::visit(ExceptionDeclarationAST *ast)
for (SpecifierListAST *it = ast->type_specifier_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- DeclaratorIdAST *declaratorId = 0;
+ DeclaratorIdAST *declaratorId = nullptr;
type = this->declarator(ast->declarator, type, &declaratorId);
- const Name *argName = 0;
+ const Name *argName = nullptr;
if (declaratorId && declaratorId->name)
argName = declaratorId->name->name;
Argument *arg = control()->newArgument(location(declaratorId, ast->firstToken()), argName);
arg->setType(type);
_scope->addMember(arg);
- // unsigned dot_dot_dot_token = ast->dot_dot_dot_token;
+ // int dot_dot_dot_token = ast->dot_dot_dot_token;
return false;
}
@@ -2233,17 +2328,30 @@ bool Bind::visit(FunctionDefinitionAST *ast)
for (SpecifierListAST *it = ast->decl_specifier_list; it; it = it->next) {
declSpecifiers = this->specifier(it->value, declSpecifiers);
}
- DeclaratorIdAST *declaratorId = 0;
+ DeclaratorIdAST *declaratorId = nullptr;
FullySpecifiedType type = this->declarator(ast->declarator, declSpecifiers.qualifiedType(), &declaratorId);
Function *fun = type->asFunctionType();
ast->symbol = fun;
+ if (!fun && ast->declarator && ast->declarator->initializer)
+ if (ExpressionListParenAST *exprAst = ast->declarator->initializer->asExpressionListParen()) {
+ // this could be non-expanded function like macro, because
+ // for find usages we parse without expanding them
+ // So we create dummy function type here for findUsages to see function body
+ fun = control()->newFunction(0, nullptr);
+ fun->setStartOffset(tokenAt(exprAst->firstToken()).utf16charsBegin());
+ fun->setEndOffset(tokenAt(exprAst->lastToken() - 1).utf16charsEnd());
+
+ type = fun;
+ ast->symbol = fun;
+ }
+
if (fun) {
setDeclSpecifiers(fun, declSpecifiers);
fun->setEndOffset(tokenAt(ast->lastToken() - 1).utf16charsEnd());
- if (_scope->isClass()) {
+ if (_scope->asClass()) {
fun->setVisibility(_visibility);
fun->setMethodKey(methodKey);
}
@@ -2270,33 +2378,33 @@ bool Bind::visit(FunctionDefinitionAST *ast)
bool Bind::visit(LinkageBodyAST *ast)
{
- // unsigned lbrace_token = ast->lbrace_token;
+ // int lbrace_token = ast->lbrace_token;
for (DeclarationListAST *it = ast->declaration_list; it; it = it->next) {
this->declaration(it->value);
}
- // unsigned rbrace_token = ast->rbrace_token;
+ // int rbrace_token = ast->rbrace_token;
return false;
}
bool Bind::visit(LinkageSpecificationAST *ast)
{
- // unsigned extern_token = ast->extern_token;
- // unsigned extern_type_token = ast->extern_type_token;
+ // int extern_token = ast->extern_token;
+ // int extern_type_token = ast->extern_type_token;
this->declaration(ast->declaration);
return false;
}
bool Bind::visit(NamespaceAST *ast)
{
- // unsigned namespace_token = ast->namespace_token;
- // unsigned identifier_token = ast->identifier_token;
+ // int namespace_token = ast->namespace_token;
+ // int identifier_token = ast->identifier_token;
FullySpecifiedType type;
for (SpecifierListAST *it = ast->attribute_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- unsigned sourceLocation = ast->firstToken();
- const Name *namespaceName = 0;
+ int sourceLocation = ast->firstToken();
+ const Name *namespaceName = nullptr;
if (ast->identifier_token) {
sourceLocation = ast->identifier_token;
namespaceName = identifier(ast->identifier_token);
@@ -2317,8 +2425,8 @@ bool Bind::visit(NamespaceAST *ast)
bool Bind::visit(NamespaceAliasDefinitionAST *ast)
{
- unsigned sourceLocation = ast->firstToken();
- const Name *name = 0;
+ int sourceLocation = ast->firstToken();
+ const Name *name = nullptr;
if (ast->namespace_name_token) {
sourceLocation = ast->namespace_name_token;
name = identifier(ast->namespace_name_token);
@@ -2336,12 +2444,12 @@ bool Bind::visit(ParameterDeclarationAST *ast)
for (SpecifierListAST *it = ast->type_specifier_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- DeclaratorIdAST *declaratorId = 0;
+ DeclaratorIdAST *declaratorId = nullptr;
type = this->declarator(ast->declarator, type, &declaratorId);
- // unsigned equal_token = ast->equal_token;
+ // int equal_token = ast->equal_token;
ExpressionTy expression = this->expression(ast->expression);
- const Name *argName = 0;
+ const Name *argName = nullptr;
if (declaratorId && declaratorId->name)
argName = declaratorId->name->name;
@@ -2359,7 +2467,7 @@ bool Bind::visit(ParameterDeclarationAST *ast)
bool Bind::visit(TemplateDeclarationAST *ast)
{
- Template *templ = control()->newTemplate(ast->firstToken(), 0);
+ Template *templ = control()->newTemplate(ast->firstToken(), nullptr);
templ->setStartOffset(tokenAt(ast->firstToken()).utf16charsBegin());
templ->setEndOffset(tokenAt(ast->lastToken() - 1).utf16charsEnd());
ast->symbol = templ;
@@ -2368,7 +2476,7 @@ bool Bind::visit(TemplateDeclarationAST *ast)
for (DeclarationListAST *it = ast->template_parameter_list; it; it = it->next) {
this->declaration(it->value);
}
- // unsigned greater_token = ast->greater_token;
+ // int greater_token = ast->greater_token;
this->declaration(ast->declaration);
(void) switchScope(previousScope);
@@ -2383,9 +2491,9 @@ bool Bind::visit(TemplateDeclarationAST *ast)
bool Bind::visit(TypenameTypeParameterAST *ast)
{
- unsigned sourceLocation = location(ast->name, ast->firstToken());
- // unsigned classkey_token = ast->classkey_token;
- // unsigned dot_dot_dot_token = ast->dot_dot_dot_token;
+ int sourceLocation = location(ast->name, ast->firstToken());
+ // int classkey_token = ast->classkey_token;
+ // int dot_dot_dot_token = ast->dot_dot_dot_token;
const Name *name = this->name(ast->name);
ExpressionTy type_id = this->expression(ast->type_id);
CPlusPlus::Kind classKey = translationUnit()->tokenKind(ast->classkey_token);
@@ -2400,19 +2508,19 @@ bool Bind::visit(TypenameTypeParameterAST *ast)
bool Bind::visit(TemplateTypeParameterAST *ast)
{
- unsigned sourceLocation = location(ast->name, ast->firstToken());
+ int sourceLocation = location(ast->name, ast->firstToken());
- // unsigned template_token = ast->template_token;
- // unsigned less_token = ast->less_token;
+ // int template_token = ast->template_token;
+ // int less_token = ast->less_token;
// ### process the template prototype
#if 0
for (DeclarationListAST *it = ast->template_parameter_list; it; it = it->next) {
this->declaration(it->value);
}
#endif
- // unsigned greater_token = ast->greater_token;
- // unsigned class_token = ast->class_token;
- // unsigned dot_dot_dot_token = ast->dot_dot_dot_token;
+ // int greater_token = ast->greater_token;
+ // int class_token = ast->class_token;
+ // int dot_dot_dot_token = ast->dot_dot_dot_token;
const Name *name = this->name(ast->name);
ExpressionTy type_id = this->expression(ast->type_id);
@@ -2426,9 +2534,14 @@ bool Bind::visit(TemplateTypeParameterAST *ast)
return false;
}
+bool Bind::visit(TypeConstraintAST *)
+{
+ return false;
+}
+
bool Bind::visit(UsingAST *ast)
{
- unsigned sourceLocation = location(ast->name, ast->firstToken());
+ int sourceLocation = location(ast->name, ast->firstToken());
const Name *name = this->name(ast->name);
UsingDeclaration *symbol = control()->newUsingDeclaration(sourceLocation, name);
@@ -2439,7 +2552,7 @@ bool Bind::visit(UsingAST *ast)
bool Bind::visit(UsingDirectiveAST *ast)
{
- unsigned sourceLocation = location(ast->name, ast->firstToken());
+ int sourceLocation = location(ast->name, ast->firstToken());
const Name *name = this->name(ast->name);
UsingNamespaceDirective *symbol = control()->newUsingNamespaceDirective(sourceLocation, name);
ast->symbol = symbol;
@@ -2456,11 +2569,11 @@ bool Bind::visit(ObjCClassForwardDeclarationAST *ast)
List<ObjCForwardClassDeclaration *> **symbolTail = &ast->symbols;
- // unsigned class_token = ast->class_token;
+ // int class_token = ast->class_token;
for (NameListAST *it = ast->identifier_list; it; it = it->next) {
const Name *name = this->name(it->value);
- const unsigned sourceLocation = location(it->value, ast->firstToken());
+ const int sourceLocation = location(it->value, ast->firstToken());
ObjCForwardClassDeclaration *fwd = control()->newObjCForwardClassDeclaration(sourceLocation, name);
setDeclSpecifiers(fwd, declSpecifiers);
_scope->addMember(fwd);
@@ -2472,18 +2585,18 @@ bool Bind::visit(ObjCClassForwardDeclarationAST *ast)
return false;
}
-unsigned Bind::calculateScopeStart(ObjCClassDeclarationAST *ast) const
+int Bind::calculateScopeStart(ObjCClassDeclarationAST *ast) const
{
if (ast->inst_vars_decl)
- if (unsigned pos = ast->inst_vars_decl->lbrace_token)
+ if (int pos = ast->inst_vars_decl->lbrace_token)
return tokenAt(pos).utf16charsEnd();
if (ast->protocol_refs)
- if (unsigned pos = ast->protocol_refs->lastToken())
+ if (int pos = ast->protocol_refs->lastToken())
return tokenAt(pos - 1).utf16charsEnd();
if (ast->superclass)
- if (unsigned pos = ast->superclass->lastToken())
+ if (int pos = ast->superclass->lastToken())
return tokenAt(pos - 1).utf16charsEnd();
if (ast->colon_token)
@@ -2493,14 +2606,14 @@ unsigned Bind::calculateScopeStart(ObjCClassDeclarationAST *ast) const
return tokenAt(ast->rparen_token).utf16charsEnd();
if (ast->category_name)
- if (unsigned pos = ast->category_name->lastToken())
+ if (int pos = ast->category_name->lastToken())
return tokenAt(pos - 1).utf16charsEnd();
if (ast->lparen_token)
return tokenAt(ast->lparen_token).utf16charsEnd();
if (ast->class_name)
- if (unsigned pos = ast->class_name->lastToken())
+ if (int pos = ast->class_name->lastToken())
return tokenAt(pos - 1).utf16charsEnd();
return tokenAt(ast->firstToken()).utf16charsBegin();
@@ -2515,7 +2628,7 @@ bool Bind::visit(ObjCClassDeclarationAST *ast)
const Name *class_name = this->name(ast->class_name);
const Name *category_name = this->name(ast->category_name);
- const unsigned sourceLocation = location(ast->class_name, ast->firstToken());
+ const int sourceLocation = location(ast->class_name, ast->firstToken());
ObjCClass *klass = control()->newObjCClass(sourceLocation, class_name);
ast->symbol = klass;
_scope->addMember(klass);
@@ -2560,11 +2673,11 @@ bool Bind::visit(ObjCProtocolForwardDeclarationAST *ast)
List<ObjCForwardProtocolDeclaration *> **symbolTail = &ast->symbols;
- // unsigned class_token = ast->class_token;
+ // int class_token = ast->class_token;
for (NameListAST *it = ast->identifier_list; it; it = it->next) {
const Name *name = this->name(it->value);
- const unsigned sourceLocation = location(it->value, ast->firstToken());
+ const int sourceLocation = location(it->value, ast->firstToken());
ObjCForwardProtocolDeclaration *fwd = control()->newObjCForwardProtocolDeclaration(sourceLocation, name);
setDeclSpecifiers(fwd, declSpecifiers);
_scope->addMember(fwd);
@@ -2576,13 +2689,13 @@ bool Bind::visit(ObjCProtocolForwardDeclarationAST *ast)
return false;
}
-unsigned Bind::calculateScopeStart(ObjCProtocolDeclarationAST *ast) const
+int Bind::calculateScopeStart(ObjCProtocolDeclarationAST *ast) const
{
if (ast->protocol_refs)
- if (unsigned pos = ast->protocol_refs->lastToken())
+ if (int pos = ast->protocol_refs->lastToken())
return tokenAt(pos - 1).utf16charsEnd();
if (ast->name)
- if (unsigned pos = ast->name->lastToken())
+ if (int pos = ast->name->lastToken())
return tokenAt(pos - 1).utf16charsEnd();
return tokenAt(ast->firstToken()).utf16charsBegin();
@@ -2594,10 +2707,10 @@ bool Bind::visit(ObjCProtocolDeclarationAST *ast)
for (SpecifierListAST *it = ast->attribute_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- // unsigned protocol_token = ast->protocol_token;
+ // int protocol_token = ast->protocol_token;
const Name *name = this->name(ast->name);
- const unsigned sourceLocation = location(ast->name, ast->firstToken());
+ const int sourceLocation = location(ast->name, ast->firstToken());
ObjCProtocol *protocol = control()->newObjCProtocol(sourceLocation, name);
protocol->setStartOffset(calculateScopeStart(ast));
protocol->setEndOffset(tokenAt(ast->lastToken() - 1).utf16charsEnd());
@@ -2630,12 +2743,12 @@ bool Bind::visit(ObjCPropertyDeclarationAST *ast)
for (SpecifierListAST *it = ast->attribute_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
- // unsigned property_token = ast->property_token;
- // unsigned lparen_token = ast->lparen_token;
+ // int property_token = ast->property_token;
+ // int lparen_token = ast->lparen_token;
for (ObjCPropertyAttributeListAST *it = ast->property_attribute_list; it; it = it->next) {
this->objCPropertyAttribute(it->value);
}
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
this->declaration(ast->simple_declaration);
// List<ObjCPropertyDeclaration *> *symbols = ast->symbols;
return false;
@@ -2647,7 +2760,7 @@ bool Bind::visit(ObjCMethodDeclarationAST *ast)
if (! ast->function_body) {
const Name *name = method->name();
- unsigned sourceLocation = ast->firstToken();
+ int sourceLocation = ast->firstToken();
Declaration *decl = control()->newDeclaration(sourceLocation, name);
decl->setType(method);
_scope->addMember(decl);
@@ -2665,21 +2778,21 @@ bool Bind::visit(ObjCMethodDeclarationAST *ast)
bool Bind::visit(ObjCSynthesizedPropertiesDeclarationAST *ast)
{
- // unsigned synthesized_token = ast->synthesized_token;
+ // int synthesized_token = ast->synthesized_token;
for (ObjCSynthesizedPropertyListAST *it = ast->property_identifier_list; it; it = it->next) {
this->objCSynthesizedProperty(it->value);
}
- // unsigned semicolon_token = ast->semicolon_token;
+ // int semicolon_token = ast->semicolon_token;
return false;
}
bool Bind::visit(ObjCDynamicPropertiesDeclarationAST *ast)
{
- // unsigned dynamic_token = ast->dynamic_token;
+ // int dynamic_token = ast->dynamic_token;
for (NameListAST *it = ast->property_identifier_list; it; it = it->next) {
/*const Name *value =*/ this->name(it->value);
}
- // unsigned semicolon_token = ast->semicolon_token;
+ // int semicolon_token = ast->semicolon_token;
return false;
}
@@ -2696,7 +2809,7 @@ bool Bind::visit(ObjCSelectorAST *ast) // ### review
}
if (! arguments.empty()) {
- _name = control()->selectorNameId(&arguments[0], unsigned(arguments.size()), hasArgs);
+ _name = control()->selectorNameId(&arguments[0], int(arguments.size()), hasArgs);
ast->name = _name;
}
@@ -2767,10 +2880,27 @@ bool Bind::visit(DestructorNameAST *ast)
bool Bind::visit(TemplateIdAST *ast)
{
// collect the template parameters
- std::vector<FullySpecifiedType> templateArguments;
+ std::vector<TemplateArgument> templateArguments;
for (ExpressionListAST *it = ast->template_argument_list; it; it = it->next) {
ExpressionTy value = this->expression(it->value);
- templateArguments.push_back(value);
+ if (value.isValid()) {
+ templateArguments.emplace_back(value);
+ } else {
+ // special case for numeric values
+ if (it->value->asNumericLiteral()) {
+ templateArguments
+ .emplace_back(value,
+ tokenAt(it->value->asNumericLiteral()->literal_token).number);
+ } else if (it->value->asBoolLiteral()) {
+ templateArguments
+ .emplace_back(value, tokenAt(it->value->asBoolLiteral()->literal_token).number);
+ } else {
+ // fall back to non-valid type in templateArguments
+ // for ast->template_argument_list and templateArguments sizes match
+ // TODO support other literals/expressions as default arguments
+ templateArguments.emplace_back(value);
+ }
+ }
}
const Identifier *id = identifier(ast->identifier_token);
@@ -2781,7 +2911,7 @@ bool Bind::visit(TemplateIdAST *ast)
_name = control()->templateNameId(id, isSpecialization);
else
_name = control()->templateNameId(id, isSpecialization, &templateArguments[0],
- unsigned(templateArguments.size()));
+ int(templateArguments.size()));
ast->name = _name;
return false;
@@ -2883,13 +3013,17 @@ bool Bind::visit(SimpleSpecifierAST *ast)
case T_SIGNED:
if (_type.isSigned())
translationUnit()->error(ast->specifier_token, "duplicate `%s'", spell(ast->specifier_token));
+ _type.setType(control()->integerType(IntegerType::Int));
_type.setSigned(true);
+ _typeWasUnsignedOrSigned = true;
break;
case T_UNSIGNED:
if (_type.isUnsigned())
translationUnit()->error(ast->specifier_token, "duplicate `%s'", spell(ast->specifier_token));
+ _type.setType(control()->integerType(IntegerType::Int));
_type.setUnsigned(true);
+ _typeWasUnsignedOrSigned = true;
break;
case T_CHAR:
@@ -3003,14 +3137,30 @@ bool Bind::visit(AlignmentSpecifierAST *ast)
bool Bind::visit(GnuAttributeSpecifierAST *ast)
{
- // unsigned attribute_token = ast->attribute_token;
- // unsigned first_lparen_token = ast->first_lparen_token;
- // unsigned second_lparen_token = ast->second_lparen_token;
+ // int attribute_token = ast->attribute_token;
+ // int first_lparen_token = ast->first_lparen_token;
+ // int second_lparen_token = ast->second_lparen_token;
+ for (GnuAttributeListAST *it = ast->attribute_list; it; it = it->next) {
+ this->attribute(it->value);
+ }
+ // int first_rparen_token = ast->first_rparen_token;
+ // int second_rparen_token = ast->second_rparen_token;
+ return false;
+}
+
+bool Bind::visit(MsvcDeclspecSpecifierAST *ast)
+{
+ for (GnuAttributeListAST *it = ast->attribute_list; it; it = it->next) {
+ this->attribute(it->value);
+ }
+ return false;
+}
+
+bool Bind::visit(StdAttributeSpecifierAST *ast)
+{
for (GnuAttributeListAST *it = ast->attribute_list; it; it = it->next) {
this->attribute(it->value);
}
- // unsigned first_rparen_token = ast->first_rparen_token;
- // unsigned second_rparen_token = ast->second_rparen_token;
return false;
}
@@ -3029,9 +3179,9 @@ bool Bind::visit(DecltypeSpecifierAST *ast)
bool Bind::visit(ClassSpecifierAST *ast)
{
- // unsigned classkey_token = ast->classkey_token;
- unsigned sourceLocation = ast->firstToken();
- unsigned startScopeOffset = tokenAt(sourceLocation).utf16charsEnd(); // at the end of the class key
+ // int classkey_token = ast->classkey_token;
+ int sourceLocation = ast->firstToken();
+ int startScopeOffset = tokenAt(sourceLocation).utf16charsEnd(); // at the end of the class key
for (SpecifierListAST *it = ast->attribute_list; it; it = it->next) {
_type = this->specifier(it->value, _type);
@@ -3058,11 +3208,11 @@ bool Bind::visit(ClassSpecifierAST *ast)
klass->setEndOffset(tokenAt(ast->lastToken() - 1).utf16charsEnd());
_scope->addMember(klass);
- if (_scope->isClass())
+ if (_scope->asClass())
klass->setVisibility(_visibility);
// set the class key
- unsigned classKey = tokenKind(ast->classkey_token);
+ int classKey = tokenKind(ast->classkey_token);
if (classKey == T_CLASS)
klass->setClassKey(Class::ClassKey);
else if (classKey == T_STRUCT)
@@ -3079,7 +3229,7 @@ bool Bind::visit(ClassSpecifierAST *ast)
for (BaseSpecifierListAST *it = ast->base_clause_list; it; it = it->next) {
this->baseSpecifier(it->value, ast->colon_token, klass);
}
- // unsigned dot_dot_dot_token = ast->dot_dot_dot_token;
+ // int dot_dot_dot_token = ast->dot_dot_dot_token;
for (DeclarationListAST *it = ast->member_specifier_list; it; it = it->next) {
this->declaration(it->value);
}
@@ -3100,7 +3250,7 @@ bool Bind::visit(NamedTypeSpecifierAST *ast)
bool Bind::visit(ElaboratedTypeSpecifierAST *ast)
{
- // unsigned classkey_token = ast->classkey_token;
+ // int classkey_token = ast->classkey_token;
for (SpecifierListAST *it = ast->attribute_list; it; it = it->next) {
_type = this->specifier(it->value, _type);
}
@@ -3110,7 +3260,7 @@ bool Bind::visit(ElaboratedTypeSpecifierAST *ast)
bool Bind::visit(EnumSpecifierAST *ast)
{
- unsigned sourceLocation = location(ast->name, ast->firstToken());
+ int sourceLocation = location(ast->name, ast->firstToken());
const Name *enumName = this->name(ast->name);
Enum *e = control()->newEnum(sourceLocation, enumName);
@@ -3121,7 +3271,7 @@ bool Bind::visit(EnumSpecifierAST *ast)
ast->symbol = e;
_scope->addMember(e);
- if (_scope->isClass())
+ if (_scope->asClass())
e->setVisibility(_visibility);
Scope *previousScope = switchScope(e);
@@ -3130,13 +3280,17 @@ bool Bind::visit(EnumSpecifierAST *ast)
}
(void) switchScope(previousScope);
+ if (ast->name)
+ _type.setType(control()->namedType(this->name(ast->name)));
+ else
+ _type.setType(ast->symbol);
return false;
}
// PtrOperatorAST
bool Bind::visit(PointerToMemberAST *ast)
{
- const Name *memberName = 0;
+ const Name *memberName = nullptr;
for (NestedNameSpecifierListAST *it = ast->nested_name_specifier_list; it; it = it->next) {
const Name *class_or_namespace_name = this->nestedNameSpecifier(it->value);
@@ -3156,7 +3310,7 @@ bool Bind::visit(PointerToMemberAST *ast)
bool Bind::visit(PointerAST *ast)
{
- if (_type->isReferenceType())
+ if (_type->asReferenceType())
translationUnit()->error(ast->firstToken(), "cannot declare pointer to a reference");
FullySpecifiedType type(control()->pointerType(_type));
@@ -3171,7 +3325,7 @@ bool Bind::visit(ReferenceAST *ast)
{
const bool rvalueRef = (tokenKind(ast->reference_token) == T_AMPER_AMPER);
- if (_type->isReferenceType())
+ if (_type->asReferenceType())
translationUnit()->error(ast->firstToken(), "cannot declare reference to a reference");
FullySpecifiedType type(control()->referenceType(_type, rvalueRef));
@@ -3184,35 +3338,35 @@ bool Bind::visit(ReferenceAST *ast)
bool Bind::visit(CallAST *ast)
{
/*ExpressionTy base_expression =*/ this->expression(ast->base_expression);
- // unsigned lparen_token = ast->lparen_token;
+ // int lparen_token = ast->lparen_token;
for (ExpressionListAST *it = ast->expression_list; it; it = it->next) {
/*ExpressionTy value =*/ this->expression(it->value);
}
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
return false;
}
bool Bind::visit(ArrayAccessAST *ast)
{
/*ExpressionTy base_expression =*/ this->expression(ast->base_expression);
- // unsigned lbracket_token = ast->lbracket_token;
+ // int lbracket_token = ast->lbracket_token;
/*ExpressionTy expression =*/ this->expression(ast->expression);
- // unsigned rbracket_token = ast->rbracket_token;
+ // int rbracket_token = ast->rbracket_token;
return false;
}
bool Bind::visit(PostIncrDecrAST *ast)
{
ExpressionTy base_expression = this->expression(ast->base_expression);
- // unsigned incr_decr_token = ast->incr_decr_token;
+ // int incr_decr_token = ast->incr_decr_token;
return false;
}
bool Bind::visit(MemberAccessAST *ast)
{
ExpressionTy base_expression = this->expression(ast->base_expression);
- // unsigned access_token = ast->access_token;
- // unsigned template_token = ast->template_token;
+ // int access_token = ast->access_token;
+ // int template_token = ast->template_token;
/*const Name *member_name =*/ this->name(ast->member_name);
return false;
}
@@ -3226,6 +3380,15 @@ bool Bind::visit(DeclaratorIdAST *ast)
return false;
}
+bool Bind::visit(DecompositionDeclaratorAST *ast)
+{
+ for (auto it = ast->identifiers->begin(); it != ast->identifiers->end(); ++it)
+ name(*it);
+ if (_decompositionDeclarator)
+ *_decompositionDeclarator = ast;
+ return false;
+}
+
bool Bind::visit(NestedDeclaratorAST *ast)
{
_type = this->declarator(ast->declarator, _type, _declaratorId);
@@ -3236,17 +3399,27 @@ bool Bind::visit(NestedDeclaratorAST *ast)
// PostfixDeclaratorAST
bool Bind::visit(FunctionDeclaratorAST *ast)
{
- Function *fun = control()->newFunction(0, 0);
+ Function *fun = control()->newFunction(0, nullptr);
fun->setStartOffset(tokenAt(ast->firstToken()).utf16charsBegin());
fun->setEndOffset(tokenAt(ast->lastToken() - 1).utf16charsEnd());
if (ast->trailing_return_type)
_type = this->trailingReturnType(ast->trailing_return_type, _type);
fun->setReturnType(_type);
- // unsigned lparen_token = ast->lparen_token;
+ // "static", "virtual" etc.
+ FullySpecifiedType declSpecifiers;
+ for (SpecifierListAST *it = ast->decl_specifier_list; it; it = it->next)
+ declSpecifiers = this->specifier(it->value, declSpecifiers);
+ setDeclSpecifiers(fun, declSpecifiers);
+
+ // int lparen_token = ast->lparen_token;
this->parameterDeclarationClause(ast->parameter_declaration_clause, ast->lparen_token, fun);
- // unsigned rparen_token = ast->rparen_token;
+ // int rparen_token = ast->rparen_token;
FullySpecifiedType type(fun);
+ type.setStatic(declSpecifiers.isStatic());
+ type.setVirtual(declSpecifiers.isVirtual());
+ type.setDeprecated(declSpecifiers.isDeprecated());
+ type.setUnavailable(declSpecifiers.isUnavailable());
for (SpecifierListAST *it = ast->cv_qualifier_list; it; it = it->next) {
type = this->specifier(it->value, type);
}
@@ -3265,8 +3438,12 @@ bool Bind::visit(FunctionDeclaratorAST *ast)
Function::RvalueRefQualifier);
}
+ // propagate exception spec
this->exceptionSpecification(ast->exception_specification, type);
- if (ast->as_cpp_initializer != 0) {
+ if (ExceptionSpecificationAST *spec = ast->exception_specification)
+ fun->setExceptionSpecification(asStringLiteral(spec));
+
+ if (ast->as_cpp_initializer != nullptr) {
fun->setAmbiguous(true);
/*ExpressionTy as_cpp_initializer =*/ this->expression(ast->as_cpp_initializer);
}
@@ -3283,7 +3460,7 @@ bool Bind::visit(ArrayDeclaratorAST *ast)
return false;
}
-void Bind::ensureValidClassName(const Name **name, unsigned sourceLocation)
+void Bind::ensureValidClassName(const Name **name, int sourceLocation)
{
if (!*name)
return;
@@ -3291,7 +3468,7 @@ void Bind::ensureValidClassName(const Name **name, unsigned sourceLocation)
const QualifiedNameId *qName = (*name)->asQualifiedNameId();
const Name *uqName = qName ? qName->name() : *name;
- if (!uqName->isNameId() && !uqName->isTemplateNameId()) {
+ if (!uqName->asNameId() && !uqName->asTemplateNameId()) {
translationUnit()->error(sourceLocation, "expected a class-name");
*name = uqName->identifier();
diff --git a/src/libs/3rdparty/cplusplus/Bind.h b/src/libs/3rdparty/cplusplus/Bind.h
index 741043fdea..867ed7baaa 100644
--- a/src/libs/3rdparty/cplusplus/Bind.h
+++ b/src/libs/3rdparty/cplusplus/Bind.h
@@ -43,9 +43,9 @@ public:
protected:
using ASTVisitor::translationUnit;
- unsigned location(DeclaratorAST *ast, unsigned defaultLocation) const;
- unsigned location(CoreDeclaratorAST *ast, unsigned defaultLocation) const;
- unsigned location(NameAST *name, unsigned defaultLocation) const;
+ int location(DeclaratorAST *ast, int defaultLocation) const;
+ int location(CoreDeclaratorAST *ast, int defaultLocation) const;
+ int location(NameAST *name, int defaultLocation) const;
static int visibilityForAccessSpecifier(int tokenKind);
static int visibilityForClassKey(int tokenKind);
@@ -72,14 +72,16 @@ protected:
int switchMethodKey(int methodKey);
int switchObjCVisibility(int visibility);
- unsigned calculateScopeStart(ObjCClassDeclarationAST *ast) const;
- unsigned calculateScopeStart(ObjCProtocolDeclarationAST *ast) const;
+ int calculateScopeStart(ObjCClassDeclarationAST *ast) const;
+ int calculateScopeStart(ObjCProtocolDeclarationAST *ast) const;
const Name *objCSelectorArgument(ObjCSelectorArgumentAST *ast, bool *hasArg);
void attribute(GnuAttributeAST *ast);
- FullySpecifiedType declarator(DeclaratorAST *ast, const FullySpecifiedType &init, DeclaratorIdAST **declaratorId);
+ FullySpecifiedType declarator(DeclaratorAST *ast, const FullySpecifiedType &init,
+ DeclaratorIdAST **declaratorId,
+ DecompositionDeclaratorAST **decompDeclarator = nullptr);
void qtInterfaceName(QtInterfaceNameAST *ast);
- void baseSpecifier(BaseSpecifierAST *ast, unsigned colon_token, Class *klass);
+ void baseSpecifier(BaseSpecifierAST *ast, int colon_token, Class *klass);
void ctorInitializer(CtorInitializerAST *ast, Function *fun);
void enumerator(EnumeratorAST *ast, Enum *symbol);
FullySpecifiedType exceptionSpecification(ExceptionSpecificationAST *ast, const FullySpecifiedType &init);
@@ -89,7 +91,7 @@ protected:
FullySpecifiedType newArrayDeclarator(NewArrayDeclaratorAST *ast, const FullySpecifiedType &init);
FullySpecifiedType newTypeId(NewTypeIdAST *ast);
OperatorNameId::Kind cppOperator(OperatorAST *ast);
- void parameterDeclarationClause(ParameterDeclarationClauseAST *ast, unsigned lparen_token, Function *fun);
+ void parameterDeclarationClause(ParameterDeclarationClauseAST *ast, int lparen_token, Function *fun);
void translationUnit(TranslationUnitAST *ast);
void objCProtocolRefs(ObjCProtocolRefsAST *ast, Symbol *objcClassOrProtocol);
void objCMessageArgument(ObjCMessageArgumentAST *ast);
@@ -104,196 +106,203 @@ protected:
void capture(CaptureAST *ast);
Function *lambdaDeclarator(LambdaDeclaratorAST *ast);
FullySpecifiedType trailingReturnType(TrailingReturnTypeAST *ast, const FullySpecifiedType &init);
- const StringLiteral *asStringLiteral(const ExpressionAST *ast);
+ const StringLiteral *asStringLiteral(const AST *ast);
- virtual bool preVisit(AST *);
- virtual void postVisit(AST *);
+ bool preVisit(AST *) override;
+ void postVisit(AST *) override;
// AST
- virtual bool visit(ObjCSelectorArgumentAST *ast);
- virtual bool visit(GnuAttributeAST *ast);
- virtual bool visit(DeclaratorAST *ast);
- virtual bool visit(QtPropertyDeclarationItemAST *ast);
- virtual bool visit(QtInterfaceNameAST *ast);
- virtual bool visit(BaseSpecifierAST *ast);
- virtual bool visit(CtorInitializerAST *ast);
- virtual bool visit(EnumeratorAST *ast);
- virtual bool visit(DynamicExceptionSpecificationAST *ast);
- virtual bool visit(MemInitializerAST *ast);
- virtual bool visit(NestedNameSpecifierAST *ast);
- virtual bool visit(NoExceptSpecificationAST *) { return true; }
- virtual bool visit(NewArrayDeclaratorAST *ast);
- virtual bool visit(NewTypeIdAST *ast);
- virtual bool visit(OperatorAST *ast);
- virtual bool visit(ParameterDeclarationClauseAST *ast);
- virtual bool visit(TranslationUnitAST *ast);
- virtual bool visit(ObjCProtocolRefsAST *ast);
- virtual bool visit(ObjCMessageArgumentAST *ast);
- virtual bool visit(ObjCTypeNameAST *ast);
- virtual bool visit(ObjCInstanceVariablesDeclarationAST *ast);
- virtual bool visit(ObjCPropertyAttributeAST *ast);
- virtual bool visit(ObjCMessageArgumentDeclarationAST *ast);
- virtual bool visit(ObjCMethodPrototypeAST *ast);
- virtual bool visit(ObjCSynthesizedPropertyAST *ast);
- virtual bool visit(LambdaIntroducerAST *ast);
- virtual bool visit(LambdaCaptureAST *ast);
- virtual bool visit(CaptureAST *ast);
- virtual bool visit(LambdaDeclaratorAST *ast);
- virtual bool visit(TrailingReturnTypeAST *ast);
- virtual bool visit(DotDesignatorAST *) { return true; }
- virtual bool visit(BracketDesignatorAST *) { return true; }
+ bool visit(ObjCSelectorArgumentAST *ast) override;
+ bool visit(GnuAttributeAST *ast) override;
+ bool visit(DeclaratorAST *ast) override;
+ bool visit(QtPropertyDeclarationItemAST *ast) override;
+ bool visit(QtInterfaceNameAST *ast) override;
+ bool visit(BaseSpecifierAST *ast) override;
+ bool visit(CtorInitializerAST *ast) override;
+ bool visit(EnumeratorAST *ast) override;
+ bool visit(DynamicExceptionSpecificationAST *ast) override;
+ bool visit(MemInitializerAST *ast) override;
+ bool visit(NestedNameSpecifierAST *ast) override;
+ bool visit(NoExceptSpecificationAST *) override { return true; }
+ bool visit(NewArrayDeclaratorAST *ast) override;
+ bool visit(NewTypeIdAST *ast) override;
+ bool visit(OperatorAST *ast) override;
+ bool visit(ParameterDeclarationClauseAST *ast) override;
+ bool visit(RequiresExpressionAST *ast) override;
+ bool visit(TranslationUnitAST *ast) override;
+ bool visit(ObjCProtocolRefsAST *ast) override;
+ bool visit(ObjCMessageArgumentAST *ast) override;
+ bool visit(ObjCTypeNameAST *ast) override;
+ bool visit(ObjCInstanceVariablesDeclarationAST *ast) override;
+ bool visit(ObjCPropertyAttributeAST *ast) override;
+ bool visit(ObjCMessageArgumentDeclarationAST *ast) override;
+ bool visit(ObjCMethodPrototypeAST *ast) override;
+ bool visit(ObjCSynthesizedPropertyAST *ast) override;
+ bool visit(LambdaIntroducerAST *ast) override;
+ bool visit(LambdaCaptureAST *ast) override;
+ bool visit(CaptureAST *ast) override;
+ bool visit(LambdaDeclaratorAST *ast) override;
+ bool visit(TrailingReturnTypeAST *ast) override;
+ bool visit(DotDesignatorAST *) override { return true; }
+ bool visit(BracketDesignatorAST *) override { return true; }
// StatementAST
- virtual bool visit(QtMemberDeclarationAST *ast);
- virtual bool visit(CaseStatementAST *ast);
- virtual bool visit(CompoundStatementAST *ast);
- virtual bool visit(DeclarationStatementAST *ast);
- virtual bool visit(DoStatementAST *ast);
- virtual bool visit(ExpressionOrDeclarationStatementAST *ast);
- virtual bool visit(ExpressionStatementAST *ast);
- virtual bool visit(ForeachStatementAST *ast);
- virtual bool visit(RangeBasedForStatementAST *ast);
- virtual bool visit(ForStatementAST *ast);
- virtual bool visit(IfStatementAST *ast);
- virtual bool visit(LabeledStatementAST *ast);
- virtual bool visit(BreakStatementAST *ast);
- virtual bool visit(ContinueStatementAST *ast);
- virtual bool visit(GotoStatementAST *ast);
- virtual bool visit(ReturnStatementAST *ast);
- virtual bool visit(SwitchStatementAST *ast);
- virtual bool visit(TryBlockStatementAST *ast);
- virtual bool visit(CatchClauseAST *ast);
- virtual bool visit(WhileStatementAST *ast);
- virtual bool visit(ObjCFastEnumerationAST *ast);
- virtual bool visit(ObjCSynchronizedStatementAST *ast);
+ bool visit(QtMemberDeclarationAST *ast) override;
+ bool visit(CaseStatementAST *ast) override;
+ bool visit(CompoundStatementAST *ast) override;
+ bool visit(DeclarationStatementAST *ast) override;
+ bool visit(DoStatementAST *ast) override;
+ bool visit(ExpressionOrDeclarationStatementAST *ast) override;
+ bool visit(ExpressionStatementAST *ast) override;
+ bool visit(ForeachStatementAST *ast) override;
+ bool visit(RangeBasedForStatementAST *ast) override;
+ bool visit(ForStatementAST *ast) override;
+ bool visit(IfStatementAST *ast) override;
+ bool visit(LabeledStatementAST *ast) override;
+ bool visit(BreakStatementAST *ast) override;
+ bool visit(ContinueStatementAST *ast) override;
+ bool visit(GotoStatementAST *ast) override;
+ bool visit(ReturnStatementAST *ast) override;
+ bool visit(SwitchStatementAST *ast) override;
+ bool visit(TryBlockStatementAST *ast) override;
+ bool visit(CatchClauseAST *ast) override;
+ bool visit(WhileStatementAST *ast) override;
+ bool visit(ObjCFastEnumerationAST *ast) override;
+ bool visit(ObjCSynchronizedStatementAST *ast) override;
// ExpressionAST
- virtual bool visit(AlignofExpressionAST *) { return true; }
- virtual bool visit(IdExpressionAST *ast);
- virtual bool visit(CompoundExpressionAST *ast);
- virtual bool visit(CompoundLiteralAST *ast);
- virtual bool visit(QtMethodAST *ast);
- virtual bool visit(BinaryExpressionAST *ast);
- virtual bool visit(CastExpressionAST *ast);
- virtual bool visit(ConditionAST *ast);
- virtual bool visit(ConditionalExpressionAST *ast);
- virtual bool visit(CppCastExpressionAST *ast);
- virtual bool visit(DeleteExpressionAST *ast);
- virtual bool visit(DesignatedInitializerAST *) { return true; }
- virtual bool visit(ArrayInitializerAST *ast);
- virtual bool visit(NewExpressionAST *ast);
- virtual bool visit(NoExceptOperatorExpressionAST *) { return true; }
- virtual bool visit(TypeidExpressionAST *ast);
- virtual bool visit(TypenameCallExpressionAST *ast);
- virtual bool visit(TypeConstructorCallAST *ast);
- virtual bool visit(SizeofExpressionAST *ast);
- virtual bool visit(StaticAssertDeclarationAST *) { return true; }
- virtual bool visit(PointerLiteralAST *ast);
- virtual bool visit(NumericLiteralAST *ast);
- virtual bool visit(BoolLiteralAST *ast);
- virtual bool visit(ThisExpressionAST *ast);
- virtual bool visit(NestedExpressionAST *ast);
- virtual bool visit(StringLiteralAST *ast);
- virtual bool visit(ThrowExpressionAST *ast);
- virtual bool visit(TypeIdAST *ast);
- virtual bool visit(UnaryExpressionAST *ast);
- virtual bool visit(ObjCMessageExpressionAST *ast);
- virtual bool visit(ObjCProtocolExpressionAST *ast);
- virtual bool visit(ObjCEncodeExpressionAST *ast);
- virtual bool visit(ObjCSelectorExpressionAST *ast);
- virtual bool visit(LambdaExpressionAST *ast);
- virtual bool visit(BracedInitializerAST *ast);
- virtual bool visit(ExpressionListParenAST *ast);
+ bool visit(AlignofExpressionAST *) override { return true; }
+ bool visit(IdExpressionAST *ast) override;
+ bool visit(CompoundExpressionAST *ast) override;
+ bool visit(CompoundLiteralAST *ast) override;
+ bool visit(QtMethodAST *ast) override;
+ bool visit(BinaryExpressionAST *ast) override;
+ bool visit(CastExpressionAST *ast) override;
+ bool visit(ConditionAST *ast) override;
+ bool visit(ConditionalExpressionAST *ast) override;
+ bool visit(CppCastExpressionAST *ast) override;
+ bool visit(DeleteExpressionAST *ast) override;
+ bool visit(DesignatedInitializerAST *) override { return true; }
+ bool visit(ArrayInitializerAST *ast) override;
+ bool visit(NewExpressionAST *ast) override;
+ bool visit(NoExceptOperatorExpressionAST *) override { return true; }
+ bool visit(TypeidExpressionAST *ast) override;
+ bool visit(TypenameCallExpressionAST *ast) override;
+ bool visit(TypeConstructorCallAST *ast) override;
+ bool visit(SizeofExpressionAST *ast) override;
+ bool visit(StaticAssertDeclarationAST *) override { return true; }
+ bool visit(PointerLiteralAST *ast) override;
+ bool visit(NumericLiteralAST *ast) override;
+ bool visit(BoolLiteralAST *ast) override;
+ bool visit(ThisExpressionAST *ast) override;
+ bool visit(NestedExpressionAST *ast) override;
+ bool visit(StringLiteralAST *ast) override;
+ bool visit(ThrowExpressionAST *ast) override;
+ bool visit(TypeIdAST *ast) override;
+ bool visit(UnaryExpressionAST *ast) override;
+ bool visit(ObjCMessageExpressionAST *ast) override;
+ bool visit(ObjCProtocolExpressionAST *ast) override;
+ bool visit(ObjCEncodeExpressionAST *ast) override;
+ bool visit(ObjCSelectorExpressionAST *ast) override;
+ bool visit(LambdaExpressionAST *ast) override;
+ bool visit(BracedInitializerAST *ast) override;
+ bool visit(ExpressionListParenAST *ast) override;
// DeclarationAST
- virtual bool visit(SimpleDeclarationAST *ast);
- virtual bool visit(EmptyDeclarationAST *ast);
- virtual bool visit(AccessDeclarationAST *ast);
- virtual bool visit(QtObjectTagAST *ast);
- virtual bool visit(QtPrivateSlotAST *ast);
- virtual bool visit(QtPropertyDeclarationAST *ast);
- virtual bool visit(QtEnumDeclarationAST *ast);
- virtual bool visit(QtFlagsDeclarationAST *ast);
- virtual bool visit(QtInterfacesDeclarationAST *ast);
- virtual bool visit(AliasDeclarationAST *ast);
- virtual bool visit(AsmDefinitionAST *ast);
- virtual bool visit(ExceptionDeclarationAST *ast);
- virtual bool visit(FunctionDefinitionAST *ast);
- virtual bool visit(LinkageBodyAST *ast);
- virtual bool visit(LinkageSpecificationAST *ast);
- virtual bool visit(NamespaceAST *ast);
- virtual bool visit(NamespaceAliasDefinitionAST *ast);
- virtual bool visit(ParameterDeclarationAST *ast);
- virtual bool visit(TemplateDeclarationAST *ast);
- virtual bool visit(TypenameTypeParameterAST *ast);
- virtual bool visit(TemplateTypeParameterAST *ast);
- virtual bool visit(UsingAST *ast);
- virtual bool visit(UsingDirectiveAST *ast);
- virtual bool visit(ObjCClassForwardDeclarationAST *ast);
- virtual bool visit(ObjCClassDeclarationAST *ast);
- virtual bool visit(ObjCProtocolForwardDeclarationAST *ast);
- virtual bool visit(ObjCProtocolDeclarationAST *ast);
- virtual bool visit(ObjCVisibilityDeclarationAST *ast);
- virtual bool visit(ObjCPropertyDeclarationAST *ast);
- virtual bool visit(ObjCMethodDeclarationAST *ast);
- virtual bool visit(ObjCSynthesizedPropertiesDeclarationAST *ast);
- virtual bool visit(ObjCDynamicPropertiesDeclarationAST *ast);
+ bool visit(SimpleDeclarationAST *ast) override;
+ bool visit(EmptyDeclarationAST *ast) override;
+ bool visit(AccessDeclarationAST *ast) override;
+ bool visit(QtObjectTagAST *ast) override;
+ bool visit(QtPrivateSlotAST *ast) override;
+ bool visit(QtPropertyDeclarationAST *ast) override;
+ bool visit(QtEnumDeclarationAST *ast) override;
+ bool visit(QtFlagsDeclarationAST *ast) override;
+ bool visit(QtInterfacesDeclarationAST *ast) override;
+ bool visit(AliasDeclarationAST *ast) override;
+ bool visit(AsmDefinitionAST *ast) override;
+ bool visit(ExceptionDeclarationAST *ast) override;
+ bool visit(FunctionDefinitionAST *ast) override;
+ bool visit(LinkageBodyAST *ast) override;
+ bool visit(LinkageSpecificationAST *ast) override;
+ bool visit(NamespaceAST *ast) override;
+ bool visit(NamespaceAliasDefinitionAST *ast) override;
+ bool visit(ParameterDeclarationAST *ast) override;
+ bool visit(TemplateDeclarationAST *ast) override;
+ bool visit(TypenameTypeParameterAST *ast) override;
+ bool visit(TemplateTypeParameterAST *ast) override;
+ bool visit(TypeConstraintAST *ast) override;
+ bool visit(UsingAST *ast) override;
+ bool visit(UsingDirectiveAST *ast) override;
+ bool visit(ObjCClassForwardDeclarationAST *ast) override;
+ bool visit(ObjCClassDeclarationAST *ast) override;
+ bool visit(ObjCProtocolForwardDeclarationAST *ast) override;
+ bool visit(ObjCProtocolDeclarationAST *ast) override;
+ bool visit(ObjCVisibilityDeclarationAST *ast) override;
+ bool visit(ObjCPropertyDeclarationAST *ast) override;
+ bool visit(ObjCMethodDeclarationAST *ast) override;
+ bool visit(ObjCSynthesizedPropertiesDeclarationAST *ast) override;
+ bool visit(ObjCDynamicPropertiesDeclarationAST *ast) override;
// NameAST
- virtual bool visit(ObjCSelectorAST *ast);
- virtual bool visit(QualifiedNameAST *ast);
- virtual bool visit(OperatorFunctionIdAST *ast);
- virtual bool visit(ConversionFunctionIdAST *ast);
- virtual bool visit(AnonymousNameAST *ast);
- virtual bool visit(SimpleNameAST *ast);
- virtual bool visit(DestructorNameAST *ast);
- virtual bool visit(TemplateIdAST *ast);
+ bool visit(ObjCSelectorAST *ast) override;
+ bool visit(QualifiedNameAST *ast) override;
+ bool visit(OperatorFunctionIdAST *ast) override;
+ bool visit(ConversionFunctionIdAST *ast) override;
+ bool visit(AnonymousNameAST *ast) override;
+ bool visit(SimpleNameAST *ast) override;
+ bool visit(DestructorNameAST *ast) override;
+ bool visit(TemplateIdAST *ast) override;
// SpecifierAST
- virtual bool visit(SimpleSpecifierAST *ast);
- virtual bool visit(AlignmentSpecifierAST *ast);
- virtual bool visit(GnuAttributeSpecifierAST *ast);
- virtual bool visit(TypeofSpecifierAST *ast);
- virtual bool visit(DecltypeSpecifierAST *ast);
- virtual bool visit(ClassSpecifierAST *ast);
- virtual bool visit(NamedTypeSpecifierAST *ast);
- virtual bool visit(ElaboratedTypeSpecifierAST *ast);
- virtual bool visit(EnumSpecifierAST *ast);
+ bool visit(SimpleSpecifierAST *ast) override;
+ bool visit(AlignmentSpecifierAST *ast) override;
+ bool visit(GnuAttributeSpecifierAST *ast) override;
+ bool visit(MsvcDeclspecSpecifierAST *ast) override;
+ bool visit(StdAttributeSpecifierAST *ast) override;
+ bool visit(TypeofSpecifierAST *ast) override;
+ bool visit(DecltypeSpecifierAST *ast) override;
+ bool visit(ClassSpecifierAST *ast) override;
+ bool visit(NamedTypeSpecifierAST *ast) override;
+ bool visit(ElaboratedTypeSpecifierAST *ast) override;
+ bool visit(EnumSpecifierAST *ast) override;
// PtrOperatorAST
- virtual bool visit(PointerToMemberAST *ast);
- virtual bool visit(PointerAST *ast);
- virtual bool visit(ReferenceAST *ast);
+ bool visit(PointerToMemberAST *ast) override;
+ bool visit(PointerAST *ast) override;
+ bool visit(ReferenceAST *ast) override;
// PostfixAST
- virtual bool visit(CallAST *ast);
- virtual bool visit(ArrayAccessAST *ast);
- virtual bool visit(PostIncrDecrAST *ast);
- virtual bool visit(MemberAccessAST *ast);
+ bool visit(CallAST *ast) override;
+ bool visit(ArrayAccessAST *ast) override;
+ bool visit(PostIncrDecrAST *ast) override;
+ bool visit(MemberAccessAST *ast) override;
// CoreDeclaratorAST
- virtual bool visit(DeclaratorIdAST *ast);
- virtual bool visit(NestedDeclaratorAST *ast);
+ bool visit(DeclaratorIdAST *ast) override;
+ bool visit(DecompositionDeclaratorAST *ast) override;
+ bool visit(NestedDeclaratorAST *ast) override;
// PostfixDeclaratorAST
- virtual bool visit(FunctionDeclaratorAST *ast);
- virtual bool visit(ArrayDeclaratorAST *ast);
+ bool visit(FunctionDeclaratorAST *ast) override;
+ bool visit(ArrayDeclaratorAST *ast) override;
private:
static const int kMaxDepth;
- void ensureValidClassName(const Name **name, unsigned sourceLocation);
+ void ensureValidClassName(const Name **name, int sourceLocation);
Scope *_scope;
ExpressionTy _expression;
const Name *_name;
FullySpecifiedType _type;
DeclaratorIdAST **_declaratorId;
+ DecompositionDeclaratorAST **_decompositionDeclarator;
int _visibility;
int _objcVisibility;
int _methodKey;
bool _skipFunctionBodies;
int _depth;
+ bool _typeWasUnsignedOrSigned = false;
};
} // namespace CPlusPlus
diff --git a/src/libs/3rdparty/cplusplus/CMakeLists.txt b/src/libs/3rdparty/cplusplus/CMakeLists.txt
index a33e96a197..d9f130b470 100644
--- a/src/libs/3rdparty/cplusplus/CMakeLists.txt
+++ b/src/libs/3rdparty/cplusplus/CMakeLists.txt
@@ -1,7 +1,5 @@
add_qtc_library(3rd_cplusplus OBJECT
- PUBLIC_DEPENDS Qt5::Core Utils
- DEFINES CPLUSPLUS_BUILD_LIB
- INCLUDES "${CMAKE_SOURCE_DIR}/src/libs"
+ PUBLIC_DEPENDS Qt::Core Utils
SOURCES
AST.cpp AST.h
ASTClone.cpp
@@ -42,5 +40,23 @@ add_qtc_library(3rd_cplusplus OBJECT
Type.cpp Type.h
TypeVisitor.cpp TypeVisitor.h
cppassert.h
- PROPERTIES POSITION_INDEPENDENT_CODE ON
+ SKIP_PCH
)
+
+set(export_symbol_declaration DEFINES CPLUSPLUS_BUILD_LIB)
+if (QTC_STATIC_BUILD)
+ set(export_symbol_declaration PUBLIC_DEFINES CPLUSPLUS_BUILD_STATIC_LIB)
+endif()
+extend_qtc_target(3rd_cplusplus ${export_symbol_declaration})
+
+if(TARGET 3rd_cplusplus)
+ qtc_enable_release_for_debug_configuration()
+ if (BUILD_WITH_PCH)
+ target_precompile_headers(3rd_cplusplus PRIVATE
+ "${QtCreator_SOURCE_DIR}/src/shared/qtcreator_gui_pch.h")
+ endif()
+
+ if(UNIX)
+ target_compile_options(3rd_cplusplus PRIVATE "-Wno-unused-variable")
+ endif()
+endif()
diff --git a/src/libs/3rdparty/cplusplus/Control.cpp b/src/libs/3rdparty/cplusplus/Control.cpp
index a5601bf620..7a40cbba31 100644
--- a/src/libs/3rdparty/cplusplus/Control.cpp
+++ b/src/libs/3rdparty/cplusplus/Control.cpp
@@ -25,6 +25,9 @@
#include "CoreTypes.h"
#include "Symbols.h"
#include "Names.h"
+
+#include <QList>
+
#include <map>
#include <set>
#include <algorithm>
@@ -209,21 +212,21 @@ class Control::Data
public:
Data(Control *control)
: control(control)
- , translationUnit(0)
- , diagnosticClient(0)
- , deprecatedId(0)
- , unavailableId(0)
- , objcGetterId(0)
- , objcSetterId(0)
- , objcReadwriteId(0)
- , objcReadonlyId(0)
- , objcAssignId(0)
- , objcRetainId(0)
- , objcCopyId(0)
- , objcNonatomicId(0)
- , cpp11Override(0)
- , cpp11Final(0)
- , processor(0)
+ , translationUnit(nullptr)
+ , diagnosticClient(nullptr)
+ , deprecatedId(nullptr)
+ , unavailableId(nullptr)
+ , objcGetterId(nullptr)
+ , objcSetterId(nullptr)
+ , objcReadwriteId(nullptr)
+ , objcReadonlyId(nullptr)
+ , objcAssignId(nullptr)
+ , objcRetainId(nullptr)
+ , objcCopyId(nullptr)
+ , objcNonatomicId(nullptr)
+ , cpp11Override(nullptr)
+ , cpp11Final(nullptr)
+ , processor(nullptr)
{}
~Data()
@@ -574,20 +577,29 @@ const OperatorNameId *Control::findOperatorNameId(OperatorNameId::Kind operatorI
{
Table<OperatorNameId>::const_iterator i = d->operatorNameIds.find(operatorId);
if (i == d->operatorNameIds.end())
- return 0;
+ return nullptr;
else
return &*i;
}
-const Identifier *Control::findIdentifier(const char *chars, unsigned size) const
+const ConversionNameId *Control::findConversionNameId(const FullySpecifiedType &type) const
+{
+ for (const ConversionNameId &id : d->conversionNameIds) {
+ if (type.match(id.type()))
+ return &id;
+ }
+ return nullptr;
+}
+
+const Identifier *Control::findIdentifier(const char *chars, int size) const
{ return d->identifiers.findLiteral(chars, size); }
-const Identifier *Control::identifier(const char *chars, unsigned size)
+const Identifier *Control::identifier(const char *chars, int size)
{ return d->identifiers.findOrInsertLiteral(chars, size); }
const Identifier *Control::identifier(const char *chars)
{
- const unsigned length = unsigned(std::strlen(chars));
+ const int length = int(std::strlen(chars));
return identifier(chars, length);
}
@@ -609,28 +621,28 @@ Control::NumericLiteralIterator Control::firstNumericLiteral() const
Control::NumericLiteralIterator Control::lastNumericLiteral() const
{ return d->numericLiterals.end(); }
-const StringLiteral *Control::stringLiteral(const char *chars, unsigned size)
+const StringLiteral *Control::stringLiteral(const char *chars, int size)
{ return d->stringLiterals.findOrInsertLiteral(chars, size); }
const StringLiteral *Control::stringLiteral(const char *chars)
{
- const unsigned length = unsigned(std::strlen(chars));
+ const int length = int(std::strlen(chars));
return stringLiteral(chars, length);
}
-const NumericLiteral *Control::numericLiteral(const char *chars, unsigned size)
+const NumericLiteral *Control::numericLiteral(const char *chars, int size)
{ return d->numericLiterals.findOrInsertLiteral(chars, size); }
const NumericLiteral *Control::numericLiteral(const char *chars)
{
- const unsigned length = unsigned(std::strlen(chars));
+ const int length = int(std::strlen(chars));
return numericLiteral(chars, length);
}
const TemplateNameId *Control::templateNameId(const Identifier *id,
bool isSpecialization,
- const FullySpecifiedType *const args,
- unsigned argv)
+ const TemplateArgument *const args,
+ int argv)
{
return d->findOrInsertTemplateNameId(id, isSpecialization, args, args + argv);
}
@@ -650,7 +662,7 @@ const QualifiedNameId *Control::qualifiedNameId(const Name *base, const Name *na
}
const SelectorNameId *Control::selectorNameId(const Name *const *names,
- unsigned nameCount,
+ int nameCount,
bool hasArguments)
{
return d->findOrInsertSelectorNameId(names, names + nameCount, hasArguments);
@@ -675,88 +687,88 @@ PointerType *Control::pointerType(const FullySpecifiedType &elementType)
ReferenceType *Control::referenceType(const FullySpecifiedType &elementType, bool rvalueRef)
{ return d->findOrInsertReferenceType(elementType, rvalueRef); }
-ArrayType *Control::arrayType(const FullySpecifiedType &elementType, unsigned size)
+ArrayType *Control::arrayType(const FullySpecifiedType &elementType, int size)
{ return d->findOrInsertArrayType(elementType, size); }
NamedType *Control::namedType(const Name *name)
{ return d->findOrInsertNamedType(name); }
-Argument *Control::newArgument(unsigned sourceLocation, const Name *name)
+Argument *Control::newArgument(int sourceLocation, const Name *name)
{ return d->newArgument(sourceLocation, name); }
-TypenameArgument *Control::newTypenameArgument(unsigned sourceLocation, const Name *name)
+TypenameArgument *Control::newTypenameArgument(int sourceLocation, const Name *name)
{ return d->newTypenameArgument(sourceLocation, name); }
-Function *Control::newFunction(unsigned sourceLocation, const Name *name)
+Function *Control::newFunction(int sourceLocation, const Name *name)
{ return d->newFunction(sourceLocation, name); }
-Namespace *Control::newNamespace(unsigned sourceLocation, const Name *name)
+Namespace *Control::newNamespace(int sourceLocation, const Name *name)
{ return d->newNamespace(sourceLocation, name); }
-Template *Control::newTemplate(unsigned sourceLocation, const Name *name)
+Template *Control::newTemplate(int sourceLocation, const Name *name)
{ return d->newTemplate(sourceLocation, name); }
-NamespaceAlias *Control::newNamespaceAlias(unsigned sourceLocation, const Name *name)
+NamespaceAlias *Control::newNamespaceAlias(int sourceLocation, const Name *name)
{ return d->newNamespaceAlias(sourceLocation, name); }
-BaseClass *Control::newBaseClass(unsigned sourceLocation, const Name *name)
+BaseClass *Control::newBaseClass(int sourceLocation, const Name *name)
{ return d->newBaseClass(sourceLocation, name); }
-Class *Control::newClass(unsigned sourceLocation, const Name *name)
+Class *Control::newClass(int sourceLocation, const Name *name)
{ return d->newClass(sourceLocation, name); }
-Enum *Control::newEnum(unsigned sourceLocation, const Name *name)
+Enum *Control::newEnum(int sourceLocation, const Name *name)
{ return d->newEnum(sourceLocation, name); }
-Block *Control::newBlock(unsigned sourceLocation)
+Block *Control::newBlock(int sourceLocation)
{ return d->newBlock(sourceLocation); }
-Declaration *Control::newDeclaration(unsigned sourceLocation, const Name *name)
+Declaration *Control::newDeclaration(int sourceLocation, const Name *name)
{ return d->newDeclaration(sourceLocation, name); }
-EnumeratorDeclaration *Control::newEnumeratorDeclaration(unsigned sourceLocation, const Name *name)
+EnumeratorDeclaration *Control::newEnumeratorDeclaration(int sourceLocation, const Name *name)
{ return d->newEnumeratorDeclaration(sourceLocation, name); }
-UsingNamespaceDirective *Control::newUsingNamespaceDirective(unsigned sourceLocation,
+UsingNamespaceDirective *Control::newUsingNamespaceDirective(int sourceLocation,
const Name *name)
{ return d->newUsingNamespaceDirective(sourceLocation, name); }
-UsingDeclaration *Control::newUsingDeclaration(unsigned sourceLocation, const Name *name)
+UsingDeclaration *Control::newUsingDeclaration(int sourceLocation, const Name *name)
{ return d->newUsingDeclaration(sourceLocation, name); }
-ForwardClassDeclaration *Control::newForwardClassDeclaration(unsigned sourceLocation,
+ForwardClassDeclaration *Control::newForwardClassDeclaration(int sourceLocation,
const Name *name)
{ return d->newForwardClassDeclaration(sourceLocation, name); }
-QtPropertyDeclaration *Control::newQtPropertyDeclaration(unsigned sourceLocation,
+QtPropertyDeclaration *Control::newQtPropertyDeclaration(int sourceLocation,
const Name *name)
{ return d->newQtPropertyDeclaration(sourceLocation, name); }
-QtEnum *Control::newQtEnum(unsigned sourceLocation, const Name *name)
+QtEnum *Control::newQtEnum(int sourceLocation, const Name *name)
{ return d->newQtEnum(sourceLocation, name); }
-ObjCBaseClass *Control::newObjCBaseClass(unsigned sourceLocation, const Name *name)
+ObjCBaseClass *Control::newObjCBaseClass(int sourceLocation, const Name *name)
{ return d->newObjCBaseClass(sourceLocation, name); }
-ObjCBaseProtocol *Control::newObjCBaseProtocol(unsigned sourceLocation, const Name *name)
+ObjCBaseProtocol *Control::newObjCBaseProtocol(int sourceLocation, const Name *name)
{ return d->newObjCBaseProtocol(sourceLocation, name); }
-ObjCClass *Control::newObjCClass(unsigned sourceLocation, const Name *name)
+ObjCClass *Control::newObjCClass(int sourceLocation, const Name *name)
{ return d->newObjCClass(sourceLocation, name); }
-ObjCForwardClassDeclaration *Control::newObjCForwardClassDeclaration(unsigned sourceLocation, const Name *name)
+ObjCForwardClassDeclaration *Control::newObjCForwardClassDeclaration(int sourceLocation, const Name *name)
{ return d->newObjCForwardClassDeclaration(sourceLocation, name); }
-ObjCProtocol *Control::newObjCProtocol(unsigned sourceLocation, const Name *name)
+ObjCProtocol *Control::newObjCProtocol(int sourceLocation, const Name *name)
{ return d->newObjCProtocol(sourceLocation, name); }
-ObjCForwardProtocolDeclaration *Control::newObjCForwardProtocolDeclaration(unsigned sourceLocation, const Name *name)
+ObjCForwardProtocolDeclaration *Control::newObjCForwardProtocolDeclaration(int sourceLocation, const Name *name)
{ return d->newObjCForwardProtocolDeclaration(sourceLocation, name); }
-ObjCMethod *Control::newObjCMethod(unsigned sourceLocation, const Name *name)
+ObjCMethod *Control::newObjCMethod(int sourceLocation, const Name *name)
{ return d->newObjCMethod(sourceLocation, name); }
-ObjCPropertyDeclaration *Control::newObjCPropertyDeclaration(unsigned sourceLocation, const Name *name)
+ObjCPropertyDeclaration *Control::newObjCPropertyDeclaration(int sourceLocation, const Name *name)
{ return d->newObjCPropertyDeclaration(sourceLocation, name); }
const Identifier *Control::deprecatedId() const
@@ -798,7 +810,7 @@ const Identifier *Control::cpp11Final() const
Symbol **Control::firstSymbol() const
{
if (d->symbols.empty())
- return 0;
+ return nullptr;
return &*d->symbols.begin();
}
@@ -806,12 +818,12 @@ Symbol **Control::firstSymbol() const
Symbol **Control::lastSymbol() const
{
if (d->symbols.empty())
- return 0;
+ return nullptr;
return &*d->symbols.begin() + d->symbols.size();
}
-unsigned Control::symbolCount() const
+int Control::symbolCount() const
{
return unsigned(d->symbols.size());
}
@@ -821,11 +833,6 @@ bool Control::hasSymbol(Symbol *symbol) const
return std::find(d->symbols.begin(), d->symbols.end(), symbol) != d->symbols.end();
}
-void Control::squeeze()
-{
- d->numericLiterals.reset();
-}
-
TopLevelDeclarationProcessor *Control::topLevelDeclarationProcessor() const
{
return d->processor;
@@ -840,3 +847,15 @@ void Control::addSymbol(Symbol *symbol)
{
d->symbols.push_back(symbol);
}
+
+const Name *Control::toName(const QList<const Name *> &names)
+{
+ const Name *n = nullptr;
+ for (int i = names.size() - 1; i >= 0; --i) {
+ if (! n)
+ n = names.at(i);
+ else
+ n = qualifiedNameId(names.at(i), n);
+ }
+ return n;
+}
diff --git a/src/libs/3rdparty/cplusplus/Control.h b/src/libs/3rdparty/cplusplus/Control.h
index 97c1503833..62e51c435c 100644
--- a/src/libs/3rdparty/cplusplus/Control.h
+++ b/src/libs/3rdparty/cplusplus/Control.h
@@ -54,8 +54,8 @@ public:
/// Returns the canonical template name id.
const TemplateNameId *templateNameId(const Identifier *id,
bool isSpecialization,
- const FullySpecifiedType *const args = 0,
- unsigned argc = 0);
+ const TemplateArgument *const args = nullptr,
+ int argc = 0);
/// Returns the canonical destructor name id.
const DestructorNameId *destructorNameId(const Name *name);
@@ -70,7 +70,7 @@ public:
const QualifiedNameId *qualifiedNameId(const Name *base, const Name *name);
const SelectorNameId *selectorNameId(const Name *const *names,
- unsigned nameCount,
+ int nameCount,
bool hasArguments);
/// Returns a Type object of type VoidType.
@@ -93,82 +93,82 @@ public:
ReferenceType *referenceType(const FullySpecifiedType &elementType, bool rvalueRef);
/// Retruns a Type object of type ArrayType.
- ArrayType *arrayType(const FullySpecifiedType &elementType, unsigned size = 0);
+ ArrayType *arrayType(const FullySpecifiedType &elementType, int size = 0);
/// Returns a Type object of type NamedType.
NamedType *namedType(const Name *name);
/// Creates a new Declaration symbol.
- Declaration *newDeclaration(unsigned sourceLocation, const Name *name);
+ Declaration *newDeclaration(int sourceLocation, const Name *name);
/// Creates a new EnumeratorDeclaration symbol.
- EnumeratorDeclaration *newEnumeratorDeclaration(unsigned sourceLocation, const Name *name);
+ EnumeratorDeclaration *newEnumeratorDeclaration(int sourceLocation, const Name *name);
/// Creates a new Argument symbol.
- Argument *newArgument(unsigned sourceLocation, const Name *name = 0);
+ Argument *newArgument(int sourceLocation, const Name *name = nullptr);
/// Creates a new Argument symbol.
- TypenameArgument *newTypenameArgument(unsigned sourceLocation, const Name *name = 0);
+ TypenameArgument *newTypenameArgument(int sourceLocation, const Name *name = nullptr);
/// Creates a new Function symbol.
- Function *newFunction(unsigned sourceLocation, const Name *name = 0);
+ Function *newFunction(int sourceLocation, const Name *name = nullptr);
/// Creates a new Namespace symbol.
- Namespace *newNamespace(unsigned sourceLocation, const Name *name = 0);
+ Namespace *newNamespace(int sourceLocation, const Name *name = nullptr);
/// Creates a new Template symbol.
- Template *newTemplate(unsigned sourceLocation, const Name *name = 0);
+ Template *newTemplate(int sourceLocation, const Name *name = nullptr);
/// Creates a new Namespace symbol.
- NamespaceAlias *newNamespaceAlias(unsigned sourceLocation, const Name *name = 0);
+ NamespaceAlias *newNamespaceAlias(int sourceLocation, const Name *name = nullptr);
/// Creates a new BaseClass symbol.
- BaseClass *newBaseClass(unsigned sourceLocation, const Name *name = 0);
+ BaseClass *newBaseClass(int sourceLocation, const Name *name = nullptr);
/// Creates a new Class symbol.
- Class *newClass(unsigned sourceLocation, const Name *name = 0);
+ Class *newClass(int sourceLocation, const Name *name = nullptr);
/// Creates a new Enum symbol.
- Enum *newEnum(unsigned sourceLocation, const Name *name = 0);
+ Enum *newEnum(int sourceLocation, const Name *name = nullptr);
/// Creates a new Block symbol.
- Block *newBlock(unsigned sourceLocation);
+ Block *newBlock(int sourceLocation);
/// Creates a new UsingNamespaceDirective symbol.
- UsingNamespaceDirective *newUsingNamespaceDirective(unsigned sourceLocation, const Name *name = 0);
+ UsingNamespaceDirective *newUsingNamespaceDirective(int sourceLocation, const Name *name = nullptr);
/// Creates a new UsingDeclaration symbol.
- UsingDeclaration *newUsingDeclaration(unsigned sourceLocation, const Name *name = 0);
+ UsingDeclaration *newUsingDeclaration(int sourceLocation, const Name *name = nullptr);
/// Creates a new ForwardClassDeclaration symbol.
- ForwardClassDeclaration *newForwardClassDeclaration(unsigned sourceLocation, const Name *name = 0);
+ ForwardClassDeclaration *newForwardClassDeclaration(int sourceLocation, const Name *name = nullptr);
/// Creates a new QtPropertyDeclaration symbol.
- QtPropertyDeclaration *newQtPropertyDeclaration(unsigned sourceLocation, const Name *name = 0);
+ QtPropertyDeclaration *newQtPropertyDeclaration(int sourceLocation, const Name *name = nullptr);
/// Creates a new QtEnum symbol.
- QtEnum *newQtEnum(unsigned sourceLocation, const Name *name = 0);
+ QtEnum *newQtEnum(int sourceLocation, const Name *name = nullptr);
- ObjCBaseClass *newObjCBaseClass(unsigned sourceLocation, const Name *name);
- ObjCBaseProtocol *newObjCBaseProtocol(unsigned sourceLocation, const Name *name);
+ ObjCBaseClass *newObjCBaseClass(int sourceLocation, const Name *name);
+ ObjCBaseProtocol *newObjCBaseProtocol(int sourceLocation, const Name *name);
/// Creates a new Objective-C class symbol.
- ObjCClass *newObjCClass(unsigned sourceLocation, const Name *name = 0);
+ ObjCClass *newObjCClass(int sourceLocation, const Name *name = nullptr);
/// Creates a new Objective-C class forward declaration symbol.
- ObjCForwardClassDeclaration *newObjCForwardClassDeclaration(unsigned sourceLocation, const Name *name = 0);
+ ObjCForwardClassDeclaration *newObjCForwardClassDeclaration(int sourceLocation, const Name *name = nullptr);
/// Creates a new Objective-C protocol symbol.
- ObjCProtocol *newObjCProtocol(unsigned sourceLocation, const Name *name = 0);
+ ObjCProtocol *newObjCProtocol(int sourceLocation, const Name *name = nullptr);
/// Creates a new Objective-C protocol forward declaration symbol.
- ObjCForwardProtocolDeclaration *newObjCForwardProtocolDeclaration(unsigned sourceLocation, const Name *name = 0);
+ ObjCForwardProtocolDeclaration *newObjCForwardProtocolDeclaration(int sourceLocation, const Name *name = nullptr);
/// Creates a new Objective-C method symbol.
- ObjCMethod *newObjCMethod(unsigned sourceLocation, const Name *name = 0);
+ ObjCMethod *newObjCMethod(int sourceLocation, const Name *name = nullptr);
/// Creates a new Objective-C @property declaration symbol.
- ObjCPropertyDeclaration *newObjCPropertyDeclaration(unsigned sourceLocation, const Name *name);
+ ObjCPropertyDeclaration *newObjCPropertyDeclaration(int sourceLocation, const Name *name);
const Identifier *deprecatedId() const;
const Identifier *unavailableId() const;
@@ -186,9 +186,10 @@ public:
const Identifier *cpp11Final() const;
const OperatorNameId *findOperatorNameId(OperatorNameId::Kind operatorId) const;
+ const ConversionNameId *findConversionNameId(const FullySpecifiedType &type) const;
- const Identifier *findIdentifier(const char *chars, unsigned size) const;
- const Identifier *identifier(const char *chars, unsigned size);
+ const Identifier *findIdentifier(const char *chars, int size) const;
+ const Identifier *identifier(const char *chars, int size);
const Identifier *identifier(const char *chars);
typedef const Identifier *const *IdentifierIterator;
@@ -204,20 +205,20 @@ public:
NumericLiteralIterator firstNumericLiteral() const;
NumericLiteralIterator lastNumericLiteral() const;
- const StringLiteral *stringLiteral(const char *chars, unsigned size);
+ const StringLiteral *stringLiteral(const char *chars, int size);
const StringLiteral *stringLiteral(const char *chars);
- const NumericLiteral *numericLiteral(const char *chars, unsigned size);
+ const NumericLiteral *numericLiteral(const char *chars, int size);
const NumericLiteral *numericLiteral(const char *chars);
Symbol **firstSymbol() const;
Symbol **lastSymbol() const;
- unsigned symbolCount() const;
+ int symbolCount() const;
bool hasSymbol(Symbol *symbol) const;
void addSymbol(Symbol *symbol);
- void squeeze();
+ const Name *toName(const QList<const Name *> &names);
private:
class Data;
diff --git a/src/libs/3rdparty/cplusplus/CoreTypes.cpp b/src/libs/3rdparty/cplusplus/CoreTypes.cpp
index 0561f66058..a9b1d88ad8 100644
--- a/src/libs/3rdparty/cplusplus/CoreTypes.cpp
+++ b/src/libs/3rdparty/cplusplus/CoreTypes.cpp
@@ -21,10 +21,12 @@
#include "CoreTypes.h"
#include "TypeVisitor.h"
#include "Matcher.h"
-#include "Names.h"
+
#include <algorithm>
-using namespace CPlusPlus;
+namespace CPlusPlus {
+
+UndefinedType UndefinedType::instance;
void UndefinedType::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -53,14 +55,6 @@ PointerToMemberType::PointerToMemberType(const Name *memberName, const FullySpec
_elementType(elementType)
{ }
-PointerToMemberType::~PointerToMemberType()
-{ }
-
-const Name *PointerToMemberType::memberName() const
-{ return _memberName; }
-
-FullySpecifiedType PointerToMemberType::elementType() const
-{ return _elementType; }
void PointerToMemberType::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -73,12 +67,6 @@ bool PointerToMemberType::match0(const Type *otherType, Matcher *matcher) const
return false;
}
-PointerType::PointerType(const FullySpecifiedType &elementType)
- : _elementType(elementType)
-{ }
-
-PointerType::~PointerType()
-{ }
void PointerType::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -91,16 +79,11 @@ bool PointerType::match0(const Type *otherType, Matcher *matcher) const
return false;
}
-FullySpecifiedType PointerType::elementType() const
-{ return _elementType; }
ReferenceType::ReferenceType(const FullySpecifiedType &elementType, bool rvalueRef)
: _elementType(elementType), _rvalueReference(rvalueRef)
{ }
-ReferenceType::~ReferenceType()
-{ }
-
void ReferenceType::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -112,19 +95,6 @@ bool ReferenceType::match0(const Type *otherType, Matcher *matcher) const
return false;
}
-FullySpecifiedType ReferenceType::elementType() const
-{ return _elementType; }
-
-bool ReferenceType::isRvalueReference() const
-{ return _rvalueReference; }
-
-IntegerType::IntegerType(int kind)
- : _kind(kind)
-{ }
-
-IntegerType::~IntegerType()
-{ }
-
void IntegerType::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -136,15 +106,6 @@ bool IntegerType::match0(const Type *otherType, Matcher *matcher) const
return false;
}
-int IntegerType::kind() const
-{ return _kind; }
-
-FloatType::FloatType(int kind)
- : _kind(kind)
-{ }
-
-FloatType::~FloatType()
-{ }
void FloatType::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -157,16 +118,11 @@ bool FloatType::match0(const Type *otherType, Matcher *matcher) const
return false;
}
-int FloatType::kind() const
-{ return _kind; }
ArrayType::ArrayType(const FullySpecifiedType &elementType, unsigned size)
: _elementType(elementType), _size(size)
{ }
-ArrayType::~ArrayType()
-{ }
-
void ArrayType::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -178,21 +134,8 @@ bool ArrayType::match0(const Type *otherType, Matcher *matcher) const
return false;
}
-FullySpecifiedType ArrayType::elementType() const
-{ return _elementType; }
-
-unsigned ArrayType::size() const
-{ return _size; }
-
-NamedType::NamedType(const Name *name)
- : _name(name)
-{ }
-NamedType::~NamedType()
-{ }
-const Name *NamedType::name() const
-{ return _name; }
void NamedType::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -204,3 +147,5 @@ bool NamedType::match0(const Type *otherType, Matcher *matcher) const
return false;
}
+
+} // CPlusPlus
diff --git a/src/libs/3rdparty/cplusplus/CoreTypes.h b/src/libs/3rdparty/cplusplus/CoreTypes.h
index c0d799c4f2..7ccd861bb9 100644
--- a/src/libs/3rdparty/cplusplus/CoreTypes.h
+++ b/src/libs/3rdparty/cplusplus/CoreTypes.h
@@ -26,41 +26,31 @@
namespace CPlusPlus {
-class CPLUSPLUS_EXPORT UndefinedType : public Type
+class CPLUSPLUS_EXPORT UndefinedType final : public Type
{
public:
- static UndefinedType *instance()
- {
- static UndefinedType t;
- return &t;
- }
+ static UndefinedType instance;
- virtual const UndefinedType *asUndefinedType() const
- { return this; }
-
- virtual UndefinedType *asUndefinedType()
- { return this; }
+ const UndefinedType *asUndefinedType() const override { return this; }
+ UndefinedType *asUndefinedType() override { return this; }
protected:
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
};
-class CPLUSPLUS_EXPORT VoidType: public Type
+class CPLUSPLUS_EXPORT VoidType final : public Type
{
public:
- virtual const VoidType *asVoidType() const
- { return this; }
-
- virtual VoidType *asVoidType()
- { return this; }
+ const VoidType *asVoidType() const override { return this; }
+ VoidType *asVoidType() override { return this; }
protected:
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
};
-class CPLUSPLUS_EXPORT IntegerType: public Type
+class CPLUSPLUS_EXPORT IntegerType final : public Type
{
public:
enum Kind {
@@ -76,26 +66,23 @@ public:
};
public:
- IntegerType(int kind);
- virtual ~IntegerType();
-
- int kind() const;
+ IntegerType(int kind) : _kind(kind) {}
+ ~IntegerType() override = default;
- virtual IntegerType *asIntegerType()
- { return this; }
+ int kind() const { return _kind; }
- virtual const IntegerType *asIntegerType() const
- { return this; }
+ IntegerType *asIntegerType() override { return this; }
+ const IntegerType *asIntegerType() const override { return this; }
protected:
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
int _kind;
};
-class CPLUSPLUS_EXPORT FloatType: public Type
+class CPLUSPLUS_EXPORT FloatType final : public Type
{
public:
enum Kind {
@@ -105,136 +92,118 @@ public:
};
public:
- FloatType(int kind);
- virtual ~FloatType();
+ FloatType(int kind) : _kind(kind) {}
+ ~FloatType() override = default;
- int kind() const;
+ int kind() const { return _kind; }
- virtual const FloatType *asFloatType() const
- { return this; }
-
- virtual FloatType *asFloatType()
- { return this; }
+ const FloatType *asFloatType() const override { return this; }
+ FloatType *asFloatType() override { return this; }
protected:
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
int _kind;
};
-class CPLUSPLUS_EXPORT PointerType: public Type
+class CPLUSPLUS_EXPORT PointerType final : public Type
{
public:
- PointerType(const FullySpecifiedType &elementType);
- virtual ~PointerType();
-
- FullySpecifiedType elementType() const;
+ PointerType(const FullySpecifiedType &elementType) : _elementType(elementType) {}
+ ~PointerType() override = default;
- virtual const PointerType *asPointerType() const
- { return this; }
+ FullySpecifiedType elementType() const { return _elementType; }
- virtual PointerType *asPointerType()
- { return this; }
+ const PointerType *asPointerType() const override { return this; }
+ PointerType *asPointerType() override { return this; }
protected:
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
FullySpecifiedType _elementType;
};
-class CPLUSPLUS_EXPORT PointerToMemberType: public Type
+class CPLUSPLUS_EXPORT PointerToMemberType final : public Type
{
public:
PointerToMemberType(const Name *memberName, const FullySpecifiedType &elementType);
- virtual ~PointerToMemberType();
+ ~PointerToMemberType() override = default;
- const Name *memberName() const;
- FullySpecifiedType elementType() const;
+ const Name *memberName() const { return _memberName; }
+ FullySpecifiedType elementType() const { return _elementType; }
- virtual const PointerToMemberType *asPointerToMemberType() const
- { return this; }
-
- virtual PointerToMemberType *asPointerToMemberType()
- { return this; }
+ const PointerToMemberType *asPointerToMemberType() const override { return this; }
+ PointerToMemberType *asPointerToMemberType() override { return this; }
protected:
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
const Name *_memberName;
FullySpecifiedType _elementType;
};
-class CPLUSPLUS_EXPORT ReferenceType: public Type
+class CPLUSPLUS_EXPORT ReferenceType final : public Type
{
public:
ReferenceType(const FullySpecifiedType &elementType, bool rvalueRef);
- virtual ~ReferenceType();
-
- FullySpecifiedType elementType() const;
- bool isRvalueReference() const;
+ ~ReferenceType() override = default;
- virtual const ReferenceType *asReferenceType() const
- { return this; }
+ FullySpecifiedType elementType() const { return _elementType; }
+ bool isRvalueReference() const { return _rvalueReference; }
- virtual ReferenceType *asReferenceType()
- { return this; }
+ const ReferenceType *asReferenceType() const override { return this; }
+ ReferenceType *asReferenceType() override { return this; }
protected:
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
FullySpecifiedType _elementType;
bool _rvalueReference;
};
-class CPLUSPLUS_EXPORT ArrayType: public Type
+class CPLUSPLUS_EXPORT ArrayType final : public Type
{
public:
ArrayType(const FullySpecifiedType &elementType, unsigned size);
- virtual ~ArrayType();
+ ~ArrayType() override = default;
- FullySpecifiedType elementType() const;
- unsigned size() const;
+ FullySpecifiedType elementType() const { return _elementType; }
+ unsigned size() const { return _size; }
- virtual const ArrayType *asArrayType() const
- { return this; }
-
- virtual ArrayType *asArrayType()
- { return this; }
+ const ArrayType *asArrayType() const override { return this; }
+ ArrayType *asArrayType() override { return this; }
protected:
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
FullySpecifiedType _elementType;
unsigned _size;
};
-class CPLUSPLUS_EXPORT NamedType: public Type
+class CPLUSPLUS_EXPORT NamedType final : public Type
{
public:
- NamedType(const Name *name);
- virtual ~NamedType();
-
- const Name *name() const;
+ NamedType(const Name *name) : _name(name) {}
+ ~NamedType() override = default;
- virtual const NamedType *asNamedType() const
- { return this; }
+ const Name *name() const { return _name; }
- virtual NamedType *asNamedType()
- { return this; }
+ const NamedType *asNamedType() const override { return this; }
+ NamedType *asNamedType() override { return this; }
protected:
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
const Name *_name;
diff --git a/src/libs/3rdparty/cplusplus/DiagnosticClient.h b/src/libs/3rdparty/cplusplus/DiagnosticClient.h
index 108133b97e..b009074ebd 100644
--- a/src/libs/3rdparty/cplusplus/DiagnosticClient.h
+++ b/src/libs/3rdparty/cplusplus/DiagnosticClient.h
@@ -42,7 +42,7 @@ public:
virtual void report(int level,
const StringLiteral *fileName,
- unsigned line, unsigned column,
+ int line, int column,
const char *format, va_list ap) = 0;
};
diff --git a/src/libs/3rdparty/cplusplus/FullySpecifiedType.cpp b/src/libs/3rdparty/cplusplus/FullySpecifiedType.cpp
index 82f1e63eea..2bc96c1778 100644
--- a/src/libs/3rdparty/cplusplus/FullySpecifiedType.cpp
+++ b/src/libs/3rdparty/cplusplus/FullySpecifiedType.cpp
@@ -22,26 +22,24 @@
#include "Type.h"
#include "CoreTypes.h"
+#include <functional>
+
using namespace CPlusPlus;
+FullySpecifiedType::FullySpecifiedType() :
+ _type(&UndefinedType::instance), _flags(0)
+{}
+
FullySpecifiedType::FullySpecifiedType(Type *type) :
_type(type), _flags(0)
{
if (! type)
- _type = UndefinedType::instance();
+ _type = &UndefinedType::instance;
}
-FullySpecifiedType::~FullySpecifiedType()
-{ }
-
bool FullySpecifiedType::isValid() const
-{ return _type != UndefinedType::instance(); }
-
-Type *FullySpecifiedType::type() const
-{ return _type; }
+{ return _type != &UndefinedType::instance; }
-void FullySpecifiedType::setType(Type *type)
-{ _type = type; }
FullySpecifiedType FullySpecifiedType::qualifiedType() const
{
@@ -64,135 +62,12 @@ FullySpecifiedType FullySpecifiedType::qualifiedType() const
return ty;
}
-bool FullySpecifiedType::isConst() const
-{ return f._isConst; }
-
-void FullySpecifiedType::setConst(bool isConst)
-{ f._isConst = isConst; }
-
-bool FullySpecifiedType::isVolatile() const
-{ return f._isVolatile; }
-
-void FullySpecifiedType::setVolatile(bool isVolatile)
-{ f._isVolatile = isVolatile; }
-
-bool FullySpecifiedType::isSigned() const
-{ return f._isSigned; }
-
-void FullySpecifiedType::setSigned(bool isSigned)
-{ f._isSigned = isSigned; }
-
-bool FullySpecifiedType::isUnsigned() const
-{ return f._isUnsigned; }
-
-void FullySpecifiedType::setUnsigned(bool isUnsigned)
-{ f._isUnsigned = isUnsigned; }
-
-bool FullySpecifiedType::isFriend() const
-{ return f._isFriend; }
-
-void FullySpecifiedType::setFriend(bool isFriend)
-{ f._isFriend = isFriend; }
-
-bool FullySpecifiedType::isAuto() const
-{ return f._isAuto; }
-
-void FullySpecifiedType::setAuto(bool isAuto)
-{ f._isAuto = isAuto; }
-
-bool FullySpecifiedType::isRegister() const
-{ return f._isRegister; }
-
-void FullySpecifiedType::setRegister(bool isRegister)
-{ f._isRegister = isRegister; }
-
-bool FullySpecifiedType::isStatic() const
-{ return f._isStatic; }
-
-void FullySpecifiedType::setStatic(bool isStatic)
-{ f._isStatic = isStatic; }
-
-bool FullySpecifiedType::isExtern() const
-{ return f._isExtern; }
-
-void FullySpecifiedType::setExtern(bool isExtern)
-{ f._isExtern = isExtern; }
-
-bool FullySpecifiedType::isMutable() const
-{ return f._isMutable; }
-
-void FullySpecifiedType::setMutable(bool isMutable)
-{ f._isMutable = isMutable; }
-
-bool FullySpecifiedType::isTypedef() const
-{ return f._isTypedef; }
-
-void FullySpecifiedType::setTypedef(bool isTypedef)
-{ f._isTypedef = isTypedef; }
-
-bool FullySpecifiedType::isInline() const
-{ return f._isInline; }
-
-void FullySpecifiedType::setInline(bool isInline)
-{ f._isInline = isInline; }
-
-bool FullySpecifiedType::isVirtual() const
-{ return f._isVirtual; }
-
-void FullySpecifiedType::setVirtual(bool isVirtual)
-{ f._isVirtual = isVirtual; }
-
-bool FullySpecifiedType::isOverride() const
-{ return f._isOverride; }
-
-void FullySpecifiedType::setOverride(bool isOverride)
-{ f._isOverride = isOverride; }
-
-bool FullySpecifiedType::isFinal() const
-{ return f._isFinal; }
-
-void FullySpecifiedType::setFinal(bool isFinal)
-{ f._isFinal = isFinal; }
-
-bool FullySpecifiedType::isExplicit() const
-{ return f._isExplicit; }
-
-void FullySpecifiedType::setExplicit(bool isExplicit)
-{ f._isExplicit = isExplicit; }
-
-bool FullySpecifiedType::isDeprecated() const
-{ return f._isDeprecated; }
-
-void FullySpecifiedType::setDeprecated(bool isDeprecated)
-{ f._isDeprecated = isDeprecated; }
-
-bool FullySpecifiedType::isUnavailable() const
-{ return f._isUnavailable; }
-
-void FullySpecifiedType::setUnavailable(bool isUnavailable)
-{ f._isUnavailable = isUnavailable; }
-
-Type &FullySpecifiedType::operator*()
-{ return *_type; }
-
FullySpecifiedType::operator bool() const
-{ return _type != UndefinedType::instance(); }
-
-const Type &FullySpecifiedType::operator*() const
-{ return *_type; }
-
-Type *FullySpecifiedType::operator->()
-{ return _type; }
-
-const Type *FullySpecifiedType::operator->() const
-{ return _type; }
+{ return _type != &UndefinedType::instance; }
bool FullySpecifiedType::operator == (const FullySpecifiedType &other) const
{ return _type == other._type && _flags == other._flags; }
-bool FullySpecifiedType::operator != (const FullySpecifiedType &other) const
-{ return ! operator ==(other); }
-
bool FullySpecifiedType::operator < (const FullySpecifiedType &other) const
{
if (_type == other._type)
@@ -200,6 +75,11 @@ bool FullySpecifiedType::operator < (const FullySpecifiedType &other) const
return _type < other._type;
}
+size_t FullySpecifiedType::hash() const
+{
+ return std::hash<Type *>()(_type) ^ std::hash<unsigned>()(_flags);
+}
+
FullySpecifiedType FullySpecifiedType::simplified() const
{
if (const ReferenceType *refTy = type()->asReferenceType())
@@ -208,12 +88,6 @@ FullySpecifiedType FullySpecifiedType::simplified() const
return *this;
}
-unsigned FullySpecifiedType::flags() const
-{ return _flags; }
-
-void FullySpecifiedType::setFlags(unsigned flags)
-{ _flags = flags; }
-
bool FullySpecifiedType::match(const FullySpecifiedType &otherTy, Matcher *matcher) const
{
static const unsigned flagsMask = [](){
diff --git a/src/libs/3rdparty/cplusplus/FullySpecifiedType.h b/src/libs/3rdparty/cplusplus/FullySpecifiedType.h
index 5a7e33a2db..1e8146ef34 100644
--- a/src/libs/3rdparty/cplusplus/FullySpecifiedType.h
+++ b/src/libs/3rdparty/cplusplus/FullySpecifiedType.h
@@ -25,90 +25,93 @@
namespace CPlusPlus {
-class CPLUSPLUS_EXPORT FullySpecifiedType
+class CPLUSPLUS_EXPORT FullySpecifiedType final
{
public:
- FullySpecifiedType(Type *type = 0);
- ~FullySpecifiedType();
+ FullySpecifiedType();
+ FullySpecifiedType(Type *type);
+ ~FullySpecifiedType() = default;
bool isValid() const;
explicit operator bool() const;
- Type *type() const;
- void setType(Type *type);
+ Type *type() const { return _type; }
+ void setType(Type *type) { _type = type; }
FullySpecifiedType qualifiedType() const;
- bool isConst() const;
- void setConst(bool isConst);
+ bool isConst() const { return f._isConst; }
+ void setConst(bool isConst) { f._isConst = isConst; }
- bool isVolatile() const;
- void setVolatile(bool isVolatile);
+ bool isVolatile() const { return f._isVolatile; }
+ void setVolatile(bool isVolatile) { f._isVolatile = isVolatile; }
- bool isSigned() const;
- void setSigned(bool isSigned);
+ bool isSigned() const { return f._isSigned; }
+ void setSigned(bool isSigned) { f._isSigned = isSigned; }
- bool isUnsigned() const;
- void setUnsigned(bool isUnsigned);
+ bool isUnsigned() const { return f._isUnsigned; }
+ void setUnsigned(bool isUnsigned) { f._isUnsigned = isUnsigned; }
- bool isFriend() const;
- void setFriend(bool isFriend);
+ bool isFriend() const { return f._isFriend; }
+ void setFriend(bool isFriend) { f._isFriend = isFriend; }
- bool isAuto() const;
- void setAuto(bool isAuto);
+ bool isAuto() const { return f._isAuto; }
+ void setAuto(bool isAuto) { f._isAuto = isAuto; }
- bool isRegister() const;
- void setRegister(bool isRegister);
+ bool isRegister() const { return f._isRegister; }
+ void setRegister(bool isRegister) { f._isRegister = isRegister; }
- bool isStatic() const;
- void setStatic(bool isStatic);
+ bool isStatic() const { return f._isStatic; }
+ void setStatic(bool isStatic) { f._isStatic = isStatic; }
- bool isExtern() const;
- void setExtern(bool isExtern);
+ bool isExtern() const { return f._isExtern; }
+ void setExtern(bool isExtern) { f._isExtern = isExtern; }
- bool isMutable() const;
- void setMutable(bool isMutable);
+ bool isMutable() const { return f._isMutable; }
+ void setMutable(bool isMutable) { f._isMutable = isMutable; }
- bool isTypedef() const;
- void setTypedef(bool isTypedef);
+ bool isTypedef() const { return f._isTypedef; }
+ void setTypedef(bool isTypedef) { f._isTypedef = isTypedef; }
- bool isInline() const;
- void setInline(bool isInline);
+ bool isInline() const { return f._isInline; }
+ void setInline(bool isInline) { f._isInline = isInline; }
- bool isVirtual() const;
- void setVirtual(bool isVirtual);
+ bool isVirtual() const { return f._isVirtual; }
+ void setVirtual(bool isVirtual) { f._isVirtual = isVirtual; }
- bool isOverride() const;
- void setOverride(bool isOverride);
+ bool isOverride() const { return f._isOverride; }
+ void setOverride(bool isOverride) { f._isOverride = isOverride; }
- bool isFinal() const;
- void setFinal(bool isFinal);
+ bool isFinal() const { return f._isFinal; }
+ void setFinal(bool isFinal) { f._isFinal = isFinal; }
- bool isExplicit() const;
- void setExplicit(bool isExplicit);
+ bool isExplicit() const { return f._isExplicit; }
+ void setExplicit(bool isExplicit) { f._isExplicit = isExplicit; }
- bool isDeprecated() const;
- void setDeprecated(bool isDeprecated);
+ bool isDeprecated() const { return f._isDeprecated; }
+ void setDeprecated(bool isDeprecated) { f._isDeprecated = isDeprecated; }
- bool isUnavailable() const;
- void setUnavailable(bool isUnavailable);
+ bool isUnavailable() const { return f._isUnavailable; }
+ void setUnavailable(bool isUnavailable) { f._isUnavailable = isUnavailable; }
- Type &operator*();
- const Type &operator*() const;
+ Type &operator*() { return *_type; }
+ const Type &operator*() const { return *_type; }
- Type *operator->();
- const Type *operator->() const;
+ Type *operator->() { return _type; }
+ const Type *operator->() const { return _type; }
bool operator == (const FullySpecifiedType &other) const;
- bool operator != (const FullySpecifiedType &other) const;
+ bool operator != (const FullySpecifiedType &other) const { return ! operator ==(other); }
bool operator < (const FullySpecifiedType &other) const;
- bool match(const FullySpecifiedType &otherTy, Matcher *matcher = 0) const;
+ size_t hash() const;
+
+ bool match(const FullySpecifiedType &otherTy, Matcher *matcher = nullptr) const;
FullySpecifiedType simplified() const;
- unsigned flags() const;
- void setFlags(unsigned flags);
+ unsigned flags() const { return _flags; }
+ void setFlags(unsigned flags) { _flags = flags; }
private:
Type *_type;
diff --git a/src/libs/3rdparty/cplusplus/Keywords.cpp b/src/libs/3rdparty/cplusplus/Keywords.cpp
index 9188e6e318..a60ec24aec 100644
--- a/src/libs/3rdparty/cplusplus/Keywords.cpp
+++ b/src/libs/3rdparty/cplusplus/Keywords.cpp
@@ -23,6 +23,12 @@
namespace CPlusPlus {
+
+// === following code is generated with cplusplus-keywordgen tool
+// === from source file: Keywords.kwgen
+
+// === keywords begin
+
static inline int classify2(const char *s, LanguageFeatures)
{
if (s[0] == 'd') {
@@ -132,13 +138,6 @@ static inline int classify4(const char *s, LanguageFeatures features)
}
}
}
- else if (s[1] == 'n') {
- if (s[2] == 'u') {
- if (s[3] == 'm') {
- return T_ENUM;
- }
- }
- }
else if (features.qtKeywordsEnabled && s[1] == 'm') {
if (s[2] == 'i') {
if (s[3] == 't') {
@@ -146,6 +145,13 @@ static inline int classify4(const char *s, LanguageFeatures features)
}
}
}
+ else if (s[1] == 'n') {
+ if (s[2] == 'u') {
+ if (s[3] == 'm') {
+ return T_ENUM;
+ }
+ }
+ }
}
else if (s[0] == 'g') {
if (s[1] == 'o') {
@@ -165,15 +171,15 @@ static inline int classify4(const char *s, LanguageFeatures features)
}
}
}
- else if (s[0] == 't') {
- if (features.cxxEnabled && s[1] == 'h') {
+ else if (features.cxxEnabled && s[0] == 't') {
+ if (s[1] == 'h') {
if (s[2] == 'i') {
if (s[3] == 's') {
return T_THIS;
}
}
}
- else if (features.cxxEnabled && s[1] == 'r') {
+ else if (s[1] == 'r') {
if (s[2] == 'u') {
if (s[3] == 'e') {
return T_TRUE;
@@ -285,13 +291,11 @@ static inline int classify5(const char *s, LanguageFeatures features)
}
}
}
- else if (features.qtKeywordsEnabled) {
- if (s[1] == 'l') {
- if (s[2] == 'o') {
- if (s[3] == 't') {
- if (s[4] == 's') {
- return T_Q_SLOTS;
- }
+ else if (features.qtKeywordsEnabled && s[1] == 'l') {
+ if (s[2] == 'o') {
+ if (s[3] == 't') {
+ if (s[4] == 's') {
+ return T_SLOTS;
}
}
}
@@ -513,22 +517,18 @@ static inline int classify6(const char *s, LanguageFeatures features)
}
}
}
- else if (features.qtKeywordsEnabled && s[0] == 'S') {
- if (s[1] == 'I') {
- if (s[2] == 'G') {
- if (s[3] == 'N') {
- if (s[4] == 'A') {
- if (s[5] == 'L') {
- return T_SIGNAL;
+ else if (features.qtKeywordsEnabled && s[0] == 'Q') {
+ if (s[1] == '_') {
+ if (s[2] == 'E') {
+ if (s[3] == 'M') {
+ if (s[4] == 'I') {
+ if (s[5] == 'T') {
+ return T_Q_EMIT;
}
}
}
}
- }
- }
- else if (features.qtKeywordsEnabled && s[0] == 'Q') {
- if (s[1] == '_') {
- if (s[2] == 'S') {
+ else if (s[2] == 'S') {
if (s[3] == 'L') {
if (s[4] == 'O') {
if (s[5] == 'T') {
@@ -537,11 +537,15 @@ static inline int classify6(const char *s, LanguageFeatures features)
}
}
}
- else if (s[2] == 'E') {
- if (s[3] == 'M') {
- if (s[4] == 'I') {
- if (s[5] == 'T') {
- return T_Q_EMIT;
+ }
+ }
+ else if (features.qtKeywordsEnabled && s[0] == 'S') {
+ if (s[1] == 'I') {
+ if (s[2] == 'G') {
+ if (s[3] == 'N') {
+ if (s[4] == 'A') {
+ if (s[5] == 'L') {
+ return T_SIGNAL;
}
}
}
@@ -599,6 +603,34 @@ static inline int classify7(const char *s, LanguageFeatures features)
}
}
}
+ else if (features.cxx20Enabled && s[0] == 'c') {
+ if (s[1] == 'h') {
+ if (s[2] == 'a') {
+ if (s[3] == 'r') {
+ if (s[4] == '8') {
+ if (s[5] == '_') {
+ if (s[6] == 't') {
+ return T_CHAR8_T;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[1] == 'o') {
+ if (s[2] == 'n') {
+ if (s[3] == 'c') {
+ if (s[4] == 'e') {
+ if (s[5] == 'p') {
+ if (s[6] == 't') {
+ return T_CONCEPT;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
else if (s[0] == 'd') {
if (s[1] == 'e') {
if (s[2] == 'f') {
@@ -614,6 +646,21 @@ static inline int classify7(const char *s, LanguageFeatures features)
}
}
}
+ else if (features.qtKeywordsEnabled && s[0] == 'f') {
+ if (s[1] == 'o') {
+ if (s[2] == 'r') {
+ if (s[3] == 'e') {
+ if (s[4] == 'a') {
+ if (s[5] == 'c') {
+ if (s[6] == 'h') {
+ return T_FOREACH;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
else if (features.cxxEnabled && s[0] == 'm') {
if (s[1] == 'u') {
if (s[2] == 't') {
@@ -659,21 +706,6 @@ static inline int classify7(const char *s, LanguageFeatures features)
}
}
}
- else if (features.qtKeywordsEnabled && s[0] == 'f') {
- if (s[1] == 'o') {
- if (s[2] == 'r') {
- if (s[3] == 'e') {
- if (s[4] == 'a') {
- if (s[5] == 'c') {
- if (s[6] == 'h') {
- return T_Q_FOREACH;
- }
- }
- }
- }
- }
- }
- }
else if (features.qtKeywordsEnabled && s[0] == 's') {
if (s[1] == 'i') {
if (s[2] == 'g') {
@@ -681,7 +713,7 @@ static inline int classify7(const char *s, LanguageFeatures features)
if (s[4] == 'a') {
if (s[5] == 'l') {
if (s[6] == 's') {
- return T_Q_SIGNALS;
+ return T_SIGNALS;
}
}
}
@@ -736,18 +768,7 @@ static inline int classify7(const char *s, LanguageFeatures features)
}
else if (features.qtEnabled && s[0] == 'Q') {
if (s[1] == '_') {
- if (s[2] == 'S') {
- if (s[3] == 'L') {
- if (s[4] == 'O') {
- if (s[5] == 'T') {
- if (s[6] == 'S') {
- return T_Q_SLOTS;
- }
- }
- }
- }
- }
- else if (s[2] == 'E') {
+ if (s[2] == 'E') {
if (s[3] == 'N') {
if (s[4] == 'U') {
if (s[5] == 'M') {
@@ -769,6 +790,17 @@ static inline int classify7(const char *s, LanguageFeatures features)
}
}
}
+ else if (s[2] == 'S') {
+ if (s[3] == 'L') {
+ if (s[4] == 'O') {
+ if (s[5] == 'T') {
+ if (s[6] == 'S') {
+ return T_Q_SLOTS;
+ }
+ }
+ }
+ }
+ }
}
}
return T_IDENTIFIER;
@@ -792,7 +824,18 @@ static inline int classify8(const char *s, LanguageFeatures features)
}
}
else if (s[2] == 't') {
- if (s[3] == 'y') {
+ if (s[3] == 'h') {
+ if (s[4] == 'r') {
+ if (s[5] == 'e') {
+ if (s[6] == 'a') {
+ if (s[7] == 'd') {
+ return T___THREAD;
+ }
+ }
+ }
+ }
+ }
+ else if (s[3] == 'y') {
if (s[4] == 'p') {
if (s[5] == 'e') {
if (s[6] == 'o') {
@@ -803,23 +846,60 @@ static inline int classify8(const char *s, LanguageFeatures features)
}
}
}
- else if (s[3] == 'h') {
- if (s[4] == 'r') {
+ }
+ }
+ }
+ else if (s[0] == 'c') {
+ if (features.cxx11Enabled && s[1] == 'h') {
+ if (s[2] == 'a') {
+ if (s[3] == 'r') {
+ if (s[4] == '1') {
+ if (s[5] == '6') {
+ if (s[6] == '_') {
+ if (s[7] == 't') {
+ return T_CHAR16_T;
+ }
+ }
+ }
+ }
+ else if (s[4] == '3') {
+ if (s[5] == '2') {
+ if (s[6] == '_') {
+ if (s[7] == 't') {
+ return T_CHAR32_T;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[1] == 'o') {
+ if (features.cxx20Enabled && s[2] == '_') {
+ if (s[3] == 'a') {
+ if (s[4] == 'w') {
+ if (s[5] == 'a') {
+ if (s[6] == 'i') {
+ if (s[7] == 't') {
+ return T_CO_AWAIT;
+ }
+ }
+ }
+ }
+ }
+ else if (s[3] == 'y') {
+ if (s[4] == 'i') {
if (s[5] == 'e') {
- if (s[6] == 'a') {
+ if (s[6] == 'l') {
if (s[7] == 'd') {
- return T___THREAD;
+ return T_CO_YIELD;
}
}
}
}
}
}
- }
- }
- else if (s[0] == 'c') {
- if (s[1] == 'o') {
- if (s[2] == 'n') {
+ else if (s[2] == 'n') {
if (s[3] == 't') {
if (s[4] == 'i') {
if (s[5] == 'n') {
@@ -832,28 +912,6 @@ static inline int classify8(const char *s, LanguageFeatures features)
}
}
}
- } else if (features.cxx11Enabled && s[1] == 'h') {
- if (s[2] == 'a') {
- if (s[3] == 'r') {
- if (s[4] == '1') {
- if (s[5] == '6') {
- if (s[6] == '_') {
- if (s[7] == 't') {
- return T_CHAR16_T;
- }
- }
- }
- } else if (s[4] == '3') {
- if (s[5] == '2') {
- if (s[6] == '_') {
- if (s[7] == 't') {
- return T_CHAR32_T;
- }
- }
- }
- }
- }
- }
}
}
else if (features.cxx11Enabled && s[0] == 'd') {
@@ -939,6 +997,19 @@ static inline int classify8(const char *s, LanguageFeatures features)
}
}
}
+ else if (features.cxx20Enabled && s[2] == 'q') {
+ if (s[3] == 'u') {
+ if (s[4] == 'i') {
+ if (s[5] == 'r') {
+ if (s[6] == 'e') {
+ if (s[7] == 's') {
+ return T_REQUIRES;
+ }
+ }
+ }
+ }
+ }
+ }
}
}
else if (features.cxxEnabled && s[0] == 't') {
@@ -1073,14 +1144,53 @@ static inline int classify9(const char *s, LanguageFeatures features)
}
}
}
+ else if (s[1] == 'd') {
+ if (s[2] == 'e') {
+ if (s[3] == 'c') {
+ if (s[4] == 'l') {
+ if (s[5] == 's') {
+ if (s[6] == 'p') {
+ if (s[7] == 'e') {
+ if (s[8] == 'c') {
+ return T__DECLSPEC;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
- else if (features.cxx11Enabled && s[0] == 'c') {
+ else if (s[0] == 'c') {
if (s[1] == 'o') {
- if (s[2] == 'n') {
+ if (features.cxx20Enabled && s[2] == '_') {
+ if (s[3] == 'r') {
+ if (s[4] == 'e') {
+ if (s[5] == 't') {
+ if (s[6] == 'u') {
+ if (s[7] == 'r') {
+ if (s[8] == 'n') {
+ return T_CO_RETURN;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[2] == 'n') {
if (s[3] == 's') {
if (s[4] == 't') {
if (s[5] == 'e') {
- if (s[6] == 'x') {
+ if (features.cxx20Enabled && s[6] == 'v') {
+ if (s[7] == 'a') {
+ if (s[8] == 'l') {
+ return T_CONSTEVAL;
+ }
+ }
+ }
+ else if (features.cxx11Enabled && s[6] == 'x') {
if (s[7] == 'p') {
if (s[8] == 'r') {
return T_CONSTEXPR;
@@ -1088,6 +1198,15 @@ static inline int classify9(const char *s, LanguageFeatures features)
}
}
}
+ else if (features.cxx20Enabled && s[5] == 'i') {
+ if (s[6] == 'n') {
+ if (s[7] == 'i') {
+ if (s[8] == 't') {
+ return T_CONSTINIT;
+ }
+ }
+ }
+ }
}
}
}
@@ -1133,28 +1252,29 @@ static inline int classify9(const char *s, LanguageFeatures features)
}
else if (features.qtEnabled && s[0] == 'Q') {
if (s[1] == '_') {
- if (s[2] == 'S') {
- if (s[3] == 'I') {
- if (s[4] == 'G') {
- if (s[5] == 'N') {
+ if (s[2] == 'F') {
+ if (s[3] == 'O') {
+ if (s[4] == 'R') {
+ if (s[5] == 'E') {
if (s[6] == 'A') {
- if (s[7] == 'L') {
- if (s[8] == 'S') {
- return T_Q_SIGNALS;
+ if (s[7] == 'C') {
+ if (s[8] == 'H') {
+ return T_Q_FOREACH;
}
}
}
}
}
}
- } else if (s[2] == 'F') {
- if (s[3] == 'O') {
- if (s[4] == 'R') {
- if (s[5] == 'E') {
+ }
+ else if (s[2] == 'S') {
+ if (s[3] == 'I') {
+ if (s[4] == 'G') {
+ if (s[5] == 'N') {
if (s[6] == 'A') {
- if (s[7] == 'C') {
- if (s[8] == 'H') {
- return T_Q_FOREACH;
+ if (s[7] == 'L') {
+ if (s[8] == 'S') {
+ return T_Q_SIGNALS;
}
}
}
@@ -1171,28 +1291,20 @@ static inline int classify10(const char *s, LanguageFeatures features)
{
if (s[0] == '_') {
if (s[1] == '_') {
- if (s[2] == 'i') {
- if (s[3] == 'n') {
- if (s[4] == 'l') {
- if (s[5] == 'i') {
- if (s[6] == 'n') {
- if (s[7] == 'e') {
- if (s[8] == '_') {
- if (s[9] == '_') {
- return T___INLINE__;
+ if (s[2] == 'd') {
+ if (s[3] == 'e') {
+ if (s[4] == 'c') {
+ if (s[5] == 'l') {
+ if (s[6] == 's') {
+ if (s[7] == 'p') {
+ if (s[8] == 'e') {
+ if (s[9] == 'c') {
+ return T___DECLSPEC;
}
}
}
}
- }
- }
- }
- }
- else if (features.cxxEnabled && s[2] == 'd') {
- if (s[3] == 'e') {
- if (s[4] == 'c') {
- if (s[5] == 'l') {
- if (s[6] == 't') {
+ else if (features.cxxEnabled && s[6] == 't') {
if (s[7] == 'y') {
if (s[8] == 'p') {
if (s[9] == 'e') {
@@ -1205,6 +1317,23 @@ static inline int classify10(const char *s, LanguageFeatures features)
}
}
}
+ else if (s[2] == 'i') {
+ if (s[3] == 'n') {
+ if (s[4] == 'l') {
+ if (s[5] == 'i') {
+ if (s[6] == 'n') {
+ if (s[7] == 'e') {
+ if (s[8] == '_') {
+ if (s[9] == '_') {
+ return T___INLINE__;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
else if (s[2] == 't') {
if (s[3] == 'y') {
if (s[4] == 'p') {
@@ -1272,7 +1401,7 @@ static inline int classify10(const char *s, LanguageFeatures features)
if (s[7] == 'I') {
if (s[8] == 'D') {
if (s[9] == 'E') {
- return T_Q_PROPERTY; // Q_OVERRIDE is just an alias for Q_PROPERTY
+ return T_Q_OVERRIDE;
}
}
}
@@ -1308,23 +1437,7 @@ static inline int classify11(const char *s, LanguageFeatures features)
if (s[0] == '_') {
if (s[1] == '_') {
if (s[2] == 'a') {
- if (s[3] == 't') {
- if (s[4] == 't') {
- if (s[5] == 'r') {
- if (s[6] == 'i') {
- if (s[7] == 'b') {
- if (s[8] == 'u') {
- if (s[9] == 't') {
- if (s[10] == 'e') {
- return T___ATTRIBUTE;
- }
- }
- }
- }
- }
- }
- }
- } else if (s[3] == 'l') {
+ if (s[3] == 'l') {
if (s[4] == 'i') {
if (s[5] == 'g') {
if (s[6] == 'n') {
@@ -1341,6 +1454,23 @@ static inline int classify11(const char *s, LanguageFeatures features)
}
}
}
+ else if (s[3] == 't') {
+ if (s[4] == 't') {
+ if (s[5] == 'r') {
+ if (s[6] == 'i') {
+ if (s[7] == 'b') {
+ if (s[8] == 'u') {
+ if (s[9] == 't') {
+ if (s[10] == 'e') {
+ return T___ATTRIBUTE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
}
}
@@ -1420,31 +1550,6 @@ static inline int classify12(const char *s, LanguageFeatures features)
}
}
}
- else if (features.qtEnabled && s[0] == 'Q') {
- if (s[1] == '_') {
- if (s[2] == 'I') {
- if (s[3] == 'N') {
- if (s[4] == 'T') {
- if (s[5] == 'E') {
- if (s[6] == 'R') {
- if (s[7] == 'F') {
- if (s[8] == 'A') {
- if (s[9] == 'C') {
- if (s[10] == 'E') {
- if (s[11] == 'S') {
- return T_Q_INTERFACES;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
else if (features.cxxEnabled && s[0] == 'd') {
if (s[1] == 'y') {
if (s[2] == 'n') {
@@ -1495,6 +1600,31 @@ static inline int classify12(const char *s, LanguageFeatures features)
}
}
}
+ else if (features.qtEnabled && s[0] == 'Q') {
+ if (s[1] == '_') {
+ if (s[2] == 'I') {
+ if (s[3] == 'N') {
+ if (s[4] == 'T') {
+ if (s[5] == 'E') {
+ if (s[6] == 'R') {
+ if (s[7] == 'F') {
+ if (s[8] == 'A') {
+ if (s[9] == 'C') {
+ if (s[10] == 'E') {
+ if (s[11] == 'S') {
+ return T_Q_INTERFACES;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
return T_IDENTIFIER;
}
@@ -1526,7 +1656,8 @@ static inline int classify13(const char *s, LanguageFeatures features)
}
}
}
- } else if (features.cxx11Enabled && s[0] == 's') {
+ }
+ else if (features.cxx11Enabled && s[0] == 's') {
if (s[1] == 't') {
if (s[2] == 'a') {
if (s[3] == 't') {
@@ -1556,6 +1687,40 @@ static inline int classify13(const char *s, LanguageFeatures features)
return T_IDENTIFIER;
}
+static inline int classify14(const char *s, LanguageFeatures features)
+{
+ if (features.qtEnabled && s[0] == 'Q') {
+ if (s[1] == '_') {
+ if (s[2] == 'P') {
+ if (s[3] == 'R') {
+ if (s[4] == 'I') {
+ if (s[5] == 'V') {
+ if (s[6] == 'A') {
+ if (s[7] == 'T') {
+ if (s[8] == 'E') {
+ if (s[9] == '_') {
+ if (s[10] == 'S') {
+ if (s[11] == 'L') {
+ if (s[12] == 'O') {
+ if (s[13] == 'T') {
+ return T_Q_PRIVATE_SLOT;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return T_IDENTIFIER;
+}
+
static inline int classify16(const char *s, LanguageFeatures features)
{
if (features.cxxEnabled && s[0] == 'r') {
@@ -1594,40 +1759,6 @@ static inline int classify16(const char *s, LanguageFeatures features)
return T_IDENTIFIER;
}
-static inline int classify14(const char *s, LanguageFeatures features)
-{
- if (features.qtEnabled && s[0] == 'Q') {
- if (s[1] == '_') {
- if (s[2] == 'P') {
- if (s[3] == 'R') {
- if (s[4] == 'I') {
- if (s[5] == 'V') {
- if (s[6] == 'A') {
- if (s[7] == 'T') {
- if (s[8] == 'E') {
- if (s[9] == '_') {
- if (s[10] == 'S') {
- if (s[11] == 'L') {
- if (s[12] == 'O') {
- if (s[13] == 'T') {
- return T_Q_PRIVATE_SLOT;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- return T_IDENTIFIER;
-}
-
static inline int classify18(const char *s, LanguageFeatures features)
{
if (features.qtEnabled && s[0] == 'Q') {
@@ -1714,8 +1845,8 @@ static inline int classify19(const char *s, LanguageFeatures features)
return T_IDENTIFIER;
}
-
-int Lexer::classify(const char *s, int n, LanguageFeatures features) {
+int Lexer::classify(const char *s, int n, LanguageFeatures features)
+{
switch (n) {
case 2: return classify2(s, features);
case 3: return classify3(s, features);
@@ -1737,7 +1868,13 @@ int Lexer::classify(const char *s, int n, LanguageFeatures features) {
} // switch
}
-static inline int classifyOperator2(const char *s) {
+// === keywords end
+
+
+// === keywords begin
+
+static inline int classifyOperator2(const char *s)
+{
if (s[0] == 'o') {
if (s[1] == 'r') {
return T_OR;
@@ -1746,7 +1883,8 @@ static inline int classifyOperator2(const char *s) {
return T_IDENTIFIER;
}
-static inline int classifyOperator3(const char *s) {
+static inline int classifyOperator3(const char *s)
+{
if (s[0] == 'a') {
if (s[1] == 'n') {
if (s[2] == 'd') {
@@ -1771,7 +1909,8 @@ static inline int classifyOperator3(const char *s) {
return T_IDENTIFIER;
}
-static inline int classifyOperator5(const char *s) {
+static inline int classifyOperator5(const char *s)
+{
if (s[0] == 'b') {
if (s[1] == 'i') {
if (s[2] == 't') {
@@ -1808,7 +1947,8 @@ static inline int classifyOperator5(const char *s) {
return T_IDENTIFIER;
}
-static inline int classifyOperator6(const char *s) {
+static inline int classifyOperator6(const char *s)
+{
if (s[0] == 'a') {
if (s[1] == 'n') {
if (s[2] == 'd') {
@@ -1864,7 +2004,8 @@ static inline int classifyOperator6(const char *s) {
return T_IDENTIFIER;
}
-int Lexer::classifyOperator(const char *s, int n) {
+int Lexer::classifyOperator(const char *s, int n)
+{
switch (n) {
case 2: return classifyOperator2(s);
case 3: return classifyOperator3(s);
@@ -1874,5 +2015,6 @@ int Lexer::classifyOperator(const char *s, int n) {
} // switch
}
+// === keywords end
} // namespace CPlusPlus
diff --git a/src/libs/3rdparty/cplusplus/Keywords.kwgen b/src/libs/3rdparty/cplusplus/Keywords.kwgen
index f23b3b7cea..70deeb648d 100644
--- a/src/libs/3rdparty/cplusplus/Keywords.kwgen
+++ b/src/libs/3rdparty/cplusplus/Keywords.kwgen
@@ -1,55 +1,101 @@
+// Copyright (c) 2008 Roberto Raggi <roberto.raggi@gmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
#include "Lexer.h"
#include "Token.h"
+namespace CPlusPlus {
+
%token-prefix=T_
%toupper
%no-enums
%namespace=Lexer
+%no-namespace-for-tokens
+%pre-check-argument=LanguageFeatures features
+%function-name=classify
%%
__asm
__asm__
__attribute
__attribute__
+__alignof__
+_declspec
+__declspec
__const
__const__
__inline
__inline__
+__thread
__typeof
__typeof__
__volatile
__volatile__
asm
-auto
-bool
break
case
-catch
char
-class
const
-const_cast
continue
default
-delete
do
double
-dynamic_cast
else
enum
-explicit
-export
extern
-false
float
for
-friend
goto
if
inline
int
long
+register
+return
+short
+signed
+sizeof
+static
+struct
+switch
+typedef
+typeof
+union
+unsigned
+void
+volatile
+while
+
+%pre-check=features.cxxEnabled
+__decltype
+auto
+bool
+catch
+class
+const_cast
+delete
+dynamic_cast
+explicit
+export
+false
+friend
mutable
namespace
new
@@ -57,30 +103,89 @@ operator
private
protected
public
-register
reinterpret_cast
-return
-short
-signed
-sizeof
-static
static_cast
-struct
-switch
template
this
throw
true
try
-typedef
typeid
typename
-typeof
-union
-unsigned
using
virtual
-void
-volatile
wchar_t
-while
+
+%pre-check=features.cxx11Enabled
+alignas
+alignof
+char16_t
+char32_t
+constexpr
+decltype
+noexcept
+nullptr
+static_assert
+thread_local
+
+%pre-check=features.cxx20Enabled
+char8_t
+concept
+consteval
+constinit
+co_await
+co_return
+co_yield
+requires
+
+%pre-check=features.qtKeywordsEnabled
+emit
+foreach
+Q_EMIT
+Q_SLOT
+SIGNAL
+signals
+slots
+
+%pre-check=features.qtMocRunEnabled
+Q_Q
+Q_D
+
+%pre-check=features.qtEnabled
+Q_DECLARE_INTERFACE
+Q_ENUMS
+Q_FLAGS
+Q_FOREACH
+Q_GADGET
+Q_INVOKABLE
+Q_INTERFACES
+Q_OBJECT
+Q_OVERRIDE
+Q_PRIVATE_PROPERTY
+Q_PRIVATE_SLOT
+Q_PROPERTY
+Q_SIGNAL
+Q_SIGNALS
+Q_SLOTS
+SLOT
+
+%%
+
+%pre-check-argument=
+%function-name=classifyOperator
+
+%%
+or
+and
+not
+xor
+bitor
+compl
+or_eq
+and_eq
+bitand
+not_eq
+xor_eq
+%%
+
+} // namespace CPlusPlus
diff --git a/src/libs/3rdparty/cplusplus/LICENSE.txt b/src/libs/3rdparty/cplusplus/LICENSE.txt
new file mode 100644
index 0000000000..552bf603d0
--- /dev/null
+++ b/src/libs/3rdparty/cplusplus/LICENSE.txt
@@ -0,0 +1,17 @@
+Copyright 2005 Roberto Raggi <roberto@kdevelop.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+KDEVELOP TEAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/libs/3rdparty/cplusplus/Lexer.cpp b/src/libs/3rdparty/cplusplus/Lexer.cpp
index 079ae0ca6e..92cfc5d6ec 100644
--- a/src/libs/3rdparty/cplusplus/Lexer.cpp
+++ b/src/libs/3rdparty/cplusplus/Lexer.cpp
@@ -25,6 +25,8 @@
#include "cppassert.h"
+#include <QScopeGuard>
+
#include <cctype>
using namespace CPlusPlus;
@@ -64,8 +66,8 @@ Lexer::Lexer(TranslationUnit *unit)
}
Lexer::Lexer(const char *firstChar, const char *lastChar)
- : _translationUnit(0),
- _control(0),
+ : _translationUnit(nullptr),
+ _control(nullptr),
_state(0),
_flags(0),
_currentLine(1)
@@ -213,14 +215,19 @@ void Lexer::scan_helper(Token *tok)
return;
} else if (!control() && isRawStringLiteral(s._tokenKind)) {
tok->f.kind = s._tokenKind;
- if (scanUntilRawStringLiteralEndSimple())
+ const bool found = _expectedRawStringSuffix.isEmpty()
+ ? scanUntilRawStringLiteralEndSimple() : scanUntilRawStringLiteralEndPrecise();
+ if (found) {
+ scanOptionalUserDefinedLiteral(tok);
_state = 0;
+ }
return;
} else { // non-raw strings
tok->f.joined = true;
tok->f.kind = s._tokenKind;
_state = 0;
scanUntilQuote(tok, '"');
+ scanOptionalUserDefinedLiteral(tok);
return;
}
@@ -608,7 +615,12 @@ void Lexer::scan_helper(Token *tok)
tok->f.kind = T_LESS_LESS;
} else if (_yychar == '=') {
yyinp();
- tok->f.kind = T_LESS_EQUAL;
+ if (_languageFeatures.cxx20Enabled && _yychar == '>') {
+ yyinp();
+ tok->f.kind = T_LESS_EQUAL_GREATER;
+ } else {
+ tok->f.kind = T_LESS_EQUAL;
+ }
} else if (_yychar == ':') {
if (*(_currentChar+1) != ':' || *(_currentChar+2) == ':' || *(_currentChar+2) == '>') {
yyinp();
@@ -744,10 +756,14 @@ void Lexer::scanStringLiteral(Token *tok, unsigned char hint)
void Lexer::scanRawStringLiteral(Token *tok, unsigned char hint)
{
+ QScopeGuard cleanup([this] { _expectedRawStringSuffix.clear(); });
+ if (control())
+ cleanup.dismiss();
+
const char *yytext = _currentChar;
int delimLength = -1;
- const char *closingDelimCandidate = 0;
+ const char *closingDelimCandidate = nullptr;
bool closed = false;
while (_yychar) {
if (_yychar == '(' && delimLength == -1) {
@@ -766,6 +782,8 @@ void Lexer::scanRawStringLiteral(Token *tok, unsigned char hint)
tok->f.kind = T_ERROR;
return;
}
+ if (!control())
+ _expectedRawStringSuffix.append(_yychar);
yyinp();
} else {
if (!closingDelimCandidate) {
@@ -781,7 +799,7 @@ void Lexer::scanRawStringLiteral(Token *tok, unsigned char hint)
// Make sure this continues to be a valid candidate.
if (_yychar != *(yytext + (_currentChar - closingDelimCandidate)))
- closingDelimCandidate = 0;
+ closingDelimCandidate = nullptr;
yyinp();
}
@@ -808,12 +826,36 @@ void Lexer::scanRawStringLiteral(Token *tok, unsigned char hint)
else
tok->f.kind = T_RAW_STRING_LITERAL;
- if (!control() && !closed)
+ if (!control() && !closed) {
+ cleanup.dismiss();
s._tokenKind = tok->f.kind;
+ _expectedRawStringSuffix.prepend(')');
+ _expectedRawStringSuffix.append('"');
+ }
+ if (closed)
+ scanOptionalUserDefinedLiteral(tok);
}
-// In the highlighting case we don't have any further information
-// like the delimiter or its length, so just match for: ...)..."
+bool Lexer::scanUntilRawStringLiteralEndPrecise()
+{
+ QByteArray slidingWindow;
+ slidingWindow.reserve(_expectedRawStringSuffix.size());
+ while (_yychar) {
+ slidingWindow.append(_yychar);
+ if (slidingWindow.size() > _expectedRawStringSuffix.size())
+ slidingWindow.remove(0, 1);
+ if (slidingWindow == _expectedRawStringSuffix) {
+ _expectedRawStringSuffix.clear();
+ yyinp();
+ return true;
+ }
+ yyinp();
+ }
+ return false;
+}
+
+// In case we don't have any further information
+// like the delimiter or its length, just match for: ...)..."
bool Lexer::scanUntilRawStringLiteralEndSimple()
{
bool closingParenthesisPassed = false;
@@ -926,11 +968,15 @@ bool Lexer::scanOptionalIntegerSuffix(bool allowU)
yyinp();
if (_yychar == 'l')
yyinp();
+ if (_yychar == 'u' || _yychar == 'U')
+ yyinp();
return true;
case 'L':
yyinp();
if (_yychar == 'L')
yyinp();
+ if (_yychar == 'u' || _yychar == 'U')
+ yyinp();
return true;
default:
return false;
@@ -939,7 +985,7 @@ bool Lexer::scanOptionalIntegerSuffix(bool allowU)
void Lexer::scanOptionalUserDefinedLiteral(Token *tok)
{
- if (_languageFeatures.cxx11Enabled && _yychar == '_') {
+ if (_languageFeatures.cxx11Enabled && (_yychar == '_' || std::isalpha(_yychar))) {
tok->f.userDefinedLiteral = true;
while (std::isalnum(_yychar) || _yychar == '_' || isByteOfMultiByteCodePoint(_yychar))
yyinp();
@@ -954,7 +1000,8 @@ void Lexer::scanNumericLiteral(Token *tok)
yyinp();
while (std::isdigit(_yychar) ||
(_yychar >= 'a' && _yychar <= 'f') ||
- (_yychar >= 'A' && _yychar <= 'F')) {
+ (_yychar >= 'A' && _yychar <= 'F') ||
+ ((_yychar == '\'') && _languageFeatures.cxx14Enabled)) {
yyinp();
}
if (!scanOptionalIntegerSuffix())
@@ -962,7 +1009,8 @@ void Lexer::scanNumericLiteral(Token *tok)
goto theEnd;
} else if (_yychar == 'b' || _yychar == 'B') { // see n3472
yyinp();
- while (_yychar == '0' || _yychar == '1')
+ while (_yychar == '0' || _yychar == '1' ||
+ ((_yychar == '\'') && _languageFeatures.cxx14Enabled))
yyinp();
if (!scanOptionalIntegerSuffix())
scanOptionalUserDefinedLiteral(tok);
@@ -970,7 +1018,8 @@ void Lexer::scanNumericLiteral(Token *tok)
} else if (_yychar >= '0' && _yychar <= '7') {
do {
yyinp();
- } while (_yychar >= '0' && _yychar <= '7');
+ } while ((_yychar >= '0' && _yychar <= '7') ||
+ ((_yychar == '\'') && _languageFeatures.cxx14Enabled));
if (!scanOptionalIntegerSuffix())
scanOptionalUserDefinedLiteral(tok);
goto theEnd;
@@ -989,7 +1038,8 @@ void Lexer::scanNumericLiteral(Token *tok)
if (scanExponentPart() && !scanOptionalFloatingSuffix())
scanOptionalUserDefinedLiteral(tok);
break;
- } else if (std::isdigit(_yychar)) {
+ } else if (std::isdigit(_yychar) ||
+ ((_yychar == '\'') && _languageFeatures.cxx14Enabled)) {
yyinp();
} else {
if (!scanOptionalIntegerSuffix())
@@ -1026,7 +1076,7 @@ void Lexer::scanPreprocessorNumber(Token *tok, bool dotAlreadySkipped)
yyinp();
if (_yychar == '+' || _yychar == '-')
yyinp();
- } else if (std::isalnum(_yychar) || _yychar == '_' || _yychar == '.') {
+ } else if (std::isalnum(_yychar) || (_yychar == '\'') || _yychar == '_' || _yychar == '.') {
yyinp();
} else {
scanOptionalUserDefinedLiteral(tok);
@@ -1048,10 +1098,17 @@ void Lexer::scanIdentifier(Token *tok, unsigned extraProcessedChars)
yyinp();
}
int yylen = _currentChar - yytext;
- if (f._scanKeywords)
+ if (f._scanKeywords) {
tok->f.kind = classify(yytext, yylen, _languageFeatures);
- else
+
+ if (tok->f.kind == T_FALSE || tok->f.kind == T_TRUE) {
+ if (control()) {
+ tok->number = control()->numericLiteral(yytext, yylen);
+ }
+ }
+ } else {
tok->f.kind = T_IDENTIFIER;
+ }
if (tok->f.kind == T_IDENTIFIER) {
tok->f.kind = classifyOperator(yytext, yylen);
diff --git a/src/libs/3rdparty/cplusplus/Lexer.h b/src/libs/3rdparty/cplusplus/Lexer.h
index 1acfcf31b0..db68a676c5 100644
--- a/src/libs/3rdparty/cplusplus/Lexer.h
+++ b/src/libs/3rdparty/cplusplus/Lexer.h
@@ -23,6 +23,8 @@
#include "CPlusPlusForwardDeclarations.h"
#include "Token.h"
+#include <QByteArray>
+
namespace CPlusPlus {
class CPLUSPLUS_EXPORT Lexer
@@ -62,6 +64,10 @@ public:
void setPreprocessorMode(bool onoff)
{ f._ppMode = onoff; }
+ QByteArray expectedRawStringSuffix() const { return _expectedRawStringSuffix; }
+ void setExpectedRawStringSuffix(const QByteArray &suffix)
+ { _expectedRawStringSuffix = suffix; }
+
public:
static void yyinp_utf8(const char *&currentSourceChar, unsigned char &yychar,
unsigned &utf16charCounter)
@@ -94,6 +100,7 @@ private:
void scanStringLiteral(Token *tok, unsigned char hint = 0);
void scanRawStringLiteral(Token *tok, unsigned char hint = 0);
+ bool scanUntilRawStringLiteralEndPrecise();
bool scanUntilRawStringLiteralEndSimple();
void scanCharLiteral(Token *tok, unsigned char hint = 0);
void scanUntilQuote(Token *tok, unsigned char quote);
@@ -134,6 +141,7 @@ private:
TranslationUnit *_translationUnit;
Control *_control;
+ QByteArray _expectedRawStringSuffix;
const char *_firstChar;
const char *_currentChar;
const char *_lastChar;
diff --git a/src/libs/3rdparty/cplusplus/LiteralTable.h b/src/libs/3rdparty/cplusplus/LiteralTable.h
index 5a3f1e9042..494bc928bd 100644
--- a/src/libs/3rdparty/cplusplus/LiteralTable.h
+++ b/src/libs/3rdparty/cplusplus/LiteralTable.h
@@ -36,8 +36,8 @@ public:
public:
LiteralTable()
- : _literals(0),
- _buckets(0),
+ : _literals(nullptr),
+ _buckets(nullptr),
_allocatedLiterals(0),
_literalCount(-1),
_allocatedBuckets(0)
@@ -59,8 +59,8 @@ public:
if (_buckets)
std::free(_buckets);
- _literals = 0;
- _buckets = 0;
+ _literals = nullptr;
+ _buckets = nullptr;
_allocatedLiterals = 0;
_literalCount = -1;
_allocatedBuckets = 0;
@@ -69,10 +69,10 @@ public:
bool empty() const
{ return _literalCount == -1; }
- unsigned size() const
+ int size() const
{ return _literalCount + 1; }
- const Literal *at(unsigned index) const
+ const Literal *at(int index) const
{ return _literals[index]; }
iterator begin() const
@@ -81,7 +81,7 @@ public:
iterator end() const
{ return _literals + _literalCount + 1; }
- const Literal *findLiteral(const char *chars, unsigned size) const
+ const Literal *findLiteral(const char *chars, int size) const
{
if (_buckets) {
unsigned h = Literal::hashCode(chars, size);
@@ -92,10 +92,10 @@ public:
}
}
- return 0;
+ return nullptr;
}
- const Literal *findOrInsertLiteral(const char *chars, unsigned size)
+ const Literal *findOrInsertLiteral(const char *chars, int size)
{
if (_buckets) {
unsigned h = Literal::hashCode(chars, size);
diff --git a/src/libs/3rdparty/cplusplus/Literals.cpp b/src/libs/3rdparty/cplusplus/Literals.cpp
index 19c92e49bb..d778c2f0cc 100644
--- a/src/libs/3rdparty/cplusplus/Literals.cpp
+++ b/src/libs/3rdparty/cplusplus/Literals.cpp
@@ -28,8 +28,8 @@
using namespace CPlusPlus;
////////////////////////////////////////////////////////////////////////////////
-Literal::Literal(const char *chars, unsigned size)
- : _next(0), _index(0)
+Literal::Literal(const char *chars, int size)
+ : _next(nullptr), _index(0)
{
_chars = new char[size + 1];
@@ -79,7 +79,7 @@ unsigned Literal::hashCode(const char *chars, unsigned size)
}
////////////////////////////////////////////////////////////////////////////////
-NumericLiteral::NumericLiteral(const char *chars, unsigned size)
+NumericLiteral::NumericLiteral(const char *chars, int size)
: Literal(chars, size), _flags(0)
{
f._type = NumericLiteralIsInt;
diff --git a/src/libs/3rdparty/cplusplus/Literals.h b/src/libs/3rdparty/cplusplus/Literals.h
index 19255b619b..d4f59d9987 100644
--- a/src/libs/3rdparty/cplusplus/Literals.h
+++ b/src/libs/3rdparty/cplusplus/Literals.h
@@ -36,15 +36,15 @@ public:
typedef iterator const_iterator;
public:
- Literal(const char *chars, unsigned size);
+ Literal(const char *chars, int size);
virtual ~Literal();
iterator begin() const { return _chars; }
iterator end() const { return _chars + _size; }
- char at(unsigned index) const { return _chars[index]; }
+ char at(int index) const { return _chars[index]; }
const char *chars() const { return _chars; }
- unsigned size() const { return _size; }
+ int size() const { return _size; }
unsigned hashCode() const { return _hashCode; }
static unsigned hashCode(const char *chars, unsigned size);
@@ -73,7 +73,7 @@ public:
class CPLUSPLUS_EXPORT NumericLiteral: public Literal
{
public:
- NumericLiteral(const char *chars, unsigned size);
+ NumericLiteral(const char *chars, int size);
enum {
NumericLiteralIsInt,
@@ -105,21 +105,20 @@ private:
};
};
-class CPLUSPLUS_EXPORT Identifier: public Literal, public Name
+class CPLUSPLUS_EXPORT Identifier final : public Literal, public Name
{
public:
- Identifier(const char *chars, unsigned size)
+ Identifier(const char *chars, int size)
: Literal(chars, size)
{ }
- virtual const Identifier *identifier() const { return this; }
+ const Identifier *identifier() const override { return this; }
- virtual const Identifier *asNameId() const
- { return this; }
+ const Identifier *asNameId() const override { return this; }
protected:
- virtual void accept0(NameVisitor *visitor) const;
- virtual bool match0(const Name *otherName, Matcher *matcher) const;
+ void accept0(NameVisitor *visitor) const override;
+ bool match0(const Name *otherName, Matcher *matcher) const override;
};
} // namespace CPlusPlus
diff --git a/src/libs/3rdparty/cplusplus/Matcher.cpp b/src/libs/3rdparty/cplusplus/Matcher.cpp
index ffb852a67c..1ed201a8a4 100644
--- a/src/libs/3rdparty/cplusplus/Matcher.cpp
+++ b/src/libs/3rdparty/cplusplus/Matcher.cpp
@@ -154,19 +154,7 @@ bool Matcher::match(const NamedType *type, const NamedType *otherType)
{
if (type == otherType)
return true;
-
- const Name *name = type->name();
- if (const QualifiedNameId *q = name->asQualifiedNameId())
- name = q->name();
-
- const Name *otherName = otherType->name();
- if (const QualifiedNameId *q = otherName->asQualifiedNameId())
- otherName = q->name();
-
- if (! Matcher::match(name, otherName, this))
- return false;
-
- return true;
+ return Matcher::match(type->name(), otherType->name(), this);
}
bool Matcher::match(const Function *type, const Function *otherType)
@@ -293,7 +281,7 @@ bool Matcher::match(const ObjCMethod *type, const ObjCMethod *otherType)
else if (! type->returnType().match(otherType->returnType(), this))
return false;
- for (unsigned i = 0; i < type->argumentCount(); ++i) {
+ for (int i = 0; i < type->argumentCount(); ++i) {
Symbol *l = type->argumentAt(i);
Symbol *r = otherType->argumentAt(i);
if (! l->type().match(r->type(), this))
@@ -324,8 +312,8 @@ bool Matcher::match(const TemplateNameId *name, const TemplateNameId *otherName)
if (name->templateArgumentCount() != otherName->templateArgumentCount())
return false;
for (unsigned i = 0, ei = name->templateArgumentCount(); i != ei; ++i) {
- const FullySpecifiedType &l = name->templateArgumentAt(i);
- const FullySpecifiedType &r = otherName->templateArgumentAt(i);
+ const TemplateArgument &l = name->templateArgumentAt(i);
+ const TemplateArgument &r = otherName->templateArgumentAt(i);
if (! l.match(r, this))
return false;
}
@@ -356,10 +344,10 @@ bool Matcher::match(const QualifiedNameId *name, const QualifiedNameId *otherNam
bool Matcher::match(const SelectorNameId *name, const SelectorNameId *otherName)
{
- const unsigned nc = name->nameCount();
+ const int nc = name->nameCount();
if (name->hasArguments() != otherName->hasArguments() || nc != otherName->nameCount())
return false;
- for (unsigned i = 0; i < nc; ++i)
+ for (int i = 0; i < nc; ++i)
if (! Matcher::match(name->nameAt(i), otherName->nameAt(i), this))
return false;
return true;
diff --git a/src/libs/3rdparty/cplusplus/Matcher.h b/src/libs/3rdparty/cplusplus/Matcher.h
index 6a8da9a7b7..4fc3fb1054 100644
--- a/src/libs/3rdparty/cplusplus/Matcher.h
+++ b/src/libs/3rdparty/cplusplus/Matcher.h
@@ -38,8 +38,8 @@ public:
Matcher();
virtual ~Matcher();
- static bool match(const Type *type, const Type *otherType, Matcher *matcher = 0);
- static bool match(const Name *name, const Name *otherName, Matcher *matcher = 0);
+ static bool match(const Type *type, const Type *otherType, Matcher *matcher = nullptr);
+ static bool match(const Name *name, const Name *otherName, Matcher *matcher = nullptr);
virtual bool match(const UndefinedType *type, const UndefinedType *otherType);
virtual bool match(const VoidType *type, const VoidType *otherType);
diff --git a/src/libs/3rdparty/cplusplus/MemoryPool.cpp b/src/libs/3rdparty/cplusplus/MemoryPool.cpp
index bcd5154b72..af6f7ebddb 100644
--- a/src/libs/3rdparty/cplusplus/MemoryPool.cpp
+++ b/src/libs/3rdparty/cplusplus/MemoryPool.cpp
@@ -27,11 +27,11 @@
using namespace CPlusPlus;
MemoryPool::MemoryPool()
- : _blocks(0),
+ : _blocks(nullptr),
_allocatedBlocks(0),
_blockCount(-1),
- _ptr(0),
- _end(0)
+ _ptr(nullptr),
+ _end(nullptr)
{ }
MemoryPool::~MemoryPool()
@@ -49,7 +49,7 @@ MemoryPool::~MemoryPool()
void MemoryPool::reset()
{
_blockCount = -1;
- _ptr = _end = 0;
+ _ptr = _end = nullptr;
}
void *MemoryPool::allocate_helper(size_t size)
@@ -65,7 +65,7 @@ void *MemoryPool::allocate_helper(size_t size)
_blocks = (char **) realloc(_blocks, sizeof(char *) * _allocatedBlocks);
for (int index = _blockCount; index < _allocatedBlocks; ++index)
- _blocks[index] = 0;
+ _blocks[index] = nullptr;
}
char *&block = _blocks[_blockCount];
diff --git a/src/libs/3rdparty/cplusplus/Name.cpp b/src/libs/3rdparty/cplusplus/Name.cpp
index 1016586c89..7236a21954 100644
--- a/src/libs/3rdparty/cplusplus/Name.cpp
+++ b/src/libs/3rdparty/cplusplus/Name.cpp
@@ -25,6 +25,7 @@
#include "NameVisitor.h"
#include <cstring>
+#include <string_view>
using namespace CPlusPlus;
@@ -34,30 +35,6 @@ Name::Name()
Name::~Name()
{ }
-bool Name::isNameId() const
-{ return asNameId() != 0; }
-
-bool Name::isAnonymousNameId() const
-{ return asAnonymousNameId() != 0; }
-
-bool Name::isTemplateNameId() const
-{ return asTemplateNameId() != 0; }
-
-bool Name::isDestructorNameId() const
-{ return asDestructorNameId() != 0; }
-
-bool Name::isOperatorNameId() const
-{ return asOperatorNameId() != 0; }
-
-bool Name::isConversionNameId() const
-{ return asConversionNameId() != 0; }
-
-bool Name::isQualifiedNameId() const
-{ return asQualifiedNameId() != 0; }
-
-bool Name::isSelectorNameId() const
-{ return asSelectorNameId() != 0; }
-
void Name::accept(NameVisitor *visitor) const
{
if (visitor->preVisit(this))
@@ -77,22 +54,33 @@ bool Name::match(const Name *other, Matcher *matcher) const
return Matcher::match(this, other, matcher);
}
-bool Name::Compare::operator()(const Name *name, const Name *other) const
+bool Name::Equals::operator()(const Name *name, const Name *other) const
{
- if (name == 0)
- return other != 0;
- if (other == 0)
- return false;
if (name == other)
+ return true;
+ if (name == nullptr || other == nullptr)
return false;
const Identifier *id = name->identifier();
const Identifier *otherId = other->identifier();
- if (id == 0)
- return otherId != 0;
- if (otherId == 0)
+ if (id == otherId)
+ return true;
+ if (id == nullptr || otherId == nullptr)
return false;
- return std::strcmp(id->chars(), otherId->chars()) < 0;
+ return std::strcmp(id->chars(), otherId->chars()) == 0;
+}
+
+size_t Name::Hash::operator()(const Name *name) const
+{
+ if (name == nullptr)
+ return 0;
+
+ const Identifier *id = name->identifier();
+
+ if (id == nullptr)
+ return 0;
+
+ return std::hash<std::string_view>()(std::string_view(id->chars()));
}
diff --git a/src/libs/3rdparty/cplusplus/Name.h b/src/libs/3rdparty/cplusplus/Name.h
index 460f0bca11..d1ba99ae36 100644
--- a/src/libs/3rdparty/cplusplus/Name.h
+++ b/src/libs/3rdparty/cplusplus/Name.h
@@ -35,33 +35,27 @@ public:
virtual const Identifier *identifier() const = 0;
- bool isNameId() const;
- bool isAnonymousNameId() const;
- bool isTemplateNameId() const;
- bool isDestructorNameId() const;
- bool isOperatorNameId() const;
- bool isConversionNameId() const;
- bool isQualifiedNameId() const;
- bool isSelectorNameId() const;
-
- virtual const Identifier *asNameId() const { return 0; }
- virtual const AnonymousNameId *asAnonymousNameId() const { return 0; }
- virtual const TemplateNameId *asTemplateNameId() const { return 0; }
- virtual const DestructorNameId *asDestructorNameId() const { return 0; }
- virtual const OperatorNameId *asOperatorNameId() const { return 0; }
- virtual const ConversionNameId *asConversionNameId() const { return 0; }
- virtual const QualifiedNameId *asQualifiedNameId() const { return 0; }
- virtual const SelectorNameId *asSelectorNameId() const { return 0; }
+ virtual const Identifier *asNameId() const { return nullptr; }
+ virtual const AnonymousNameId *asAnonymousNameId() const { return nullptr; }
+ virtual const TemplateNameId *asTemplateNameId() const { return nullptr; }
+ virtual const DestructorNameId *asDestructorNameId() const { return nullptr; }
+ virtual const OperatorNameId *asOperatorNameId() const { return nullptr; }
+ virtual const ConversionNameId *asConversionNameId() const { return nullptr; }
+ virtual const QualifiedNameId *asQualifiedNameId() const { return nullptr; }
+ virtual const SelectorNameId *asSelectorNameId() const { return nullptr; }
void accept(NameVisitor *visitor) const;
static void accept(const Name *name, NameVisitor *visitor);
- bool match(const Name *other, Matcher *matcher = 0) const;
+ bool match(const Name *other, Matcher *matcher = nullptr) const;
public:
- struct Compare {
+ struct Equals {
bool operator()(const Name *name, const Name *other) const;
};
+ struct Hash {
+ size_t operator()(const Name *name) const;
+ };
protected:
virtual void accept0(NameVisitor *visitor) const = 0;
diff --git a/src/libs/3rdparty/cplusplus/Names.cpp b/src/libs/3rdparty/cplusplus/Names.cpp
index 10bbf389d2..d240ac536b 100644
--- a/src/libs/3rdparty/cplusplus/Names.cpp
+++ b/src/libs/3rdparty/cplusplus/Names.cpp
@@ -24,6 +24,7 @@
#include "Literals.h"
#include <algorithm>
#include <cstring>
+#include <string_view>
using namespace CPlusPlus;
@@ -45,7 +46,7 @@ const Identifier *QualifiedNameId::identifier() const
if (const Name *u = name())
return u->identifier();
- return 0;
+ return nullptr;
}
const Name *QualifiedNameId::base() const
@@ -93,44 +94,61 @@ bool TemplateNameId::match0(const Name *otherName, Matcher *matcher) const
const Identifier *TemplateNameId::identifier() const
{ return _identifier; }
-unsigned TemplateNameId::templateArgumentCount() const
-{ return unsigned(_templateArguments.size()); }
+bool TemplateArgument::match(const TemplateArgument &otherTy, Matcher *matcher) const
+{
+ if (_numericLiteral != otherTy._numericLiteral)
+ return false;
+ return type().match(otherTy.type(), matcher);
+}
+
+int TemplateNameId::templateArgumentCount() const
+{ return int(_templateArguments.size()); }
-const FullySpecifiedType &TemplateNameId::templateArgumentAt(unsigned index) const
+const TemplateArgument &TemplateNameId::templateArgumentAt(int index) const
{ return _templateArguments[index]; }
-bool TemplateNameId::Compare::operator()(const TemplateNameId *name,
- const TemplateNameId *other) const
+bool TemplateNameId::Equals::operator()(const TemplateNameId *name,
+ const TemplateNameId *other) const
{
- if (name == 0)
- return other != 0;
- if (other == 0)
- return false;
if (name == other)
+ return true;
+ if (name == nullptr || other == nullptr)
return false;
const Identifier *id = name->identifier();
const Identifier *otherId = other->identifier();
- if (id == 0)
- return otherId != 0;
- if (otherId == 0)
+ if (!id != !otherId) // mimic logical xor (id == nullptr ^^ otherId == nullptr)
return false;
- const int c = std::strcmp(id->chars(), otherId->chars());
+ const int c = id ? std::strcmp(id->chars(), otherId->chars()) : 0; // 0 if both are nullptr
if (c == 0) {
// we have to differentiate TemplateNameId with respect to specialization or instantiation
if (name->isSpecialization() == other->isSpecialization()) {
- return std::lexicographical_compare(name->firstTemplateArgument(),
- name->lastTemplateArgument(),
- other->firstTemplateArgument(),
- other->lastTemplateArgument());
- } else {
- return name->isSpecialization();
+ return std::equal(name->firstTemplateArgument(),
+ name->lastTemplateArgument(),
+ other->firstTemplateArgument(),
+ other->lastTemplateArgument());
}
}
+ return false;
+}
- return c < 0;
+size_t TemplateNameId::Hash::operator()(const TemplateNameId *name) const
+{
+ if (name == nullptr)
+ return 0;
+
+ const Identifier *id = name->identifier();
+
+ size_t hash = id ? std::hash<std::string_view>()(std::string_view(id->chars())) : 0;
+ hash ^= name->isSpecialization() ? 0x1 : 0x0;
+ std::for_each(name->firstTemplateArgument(),
+ name->lastTemplateArgument(),
+ [&hash](const TemplateArgument &ta) {
+ hash ^= ta.hash();
+ });
+ return hash;
}
OperatorNameId::OperatorNameId(Kind kind)
@@ -153,9 +171,6 @@ bool OperatorNameId::match0(const Name *otherName, Matcher *matcher) const
OperatorNameId::Kind OperatorNameId::kind() const
{ return _kind; }
-const Identifier *OperatorNameId::identifier() const
-{ return 0; }
-
ConversionNameId::ConversionNameId(const FullySpecifiedType &type)
: _type(type)
{ }
@@ -173,11 +188,7 @@ bool ConversionNameId::match0(const Name *otherName, Matcher *matcher) const
return false;
}
-FullySpecifiedType ConversionNameId::type() const
-{ return _type; }
-const Identifier *ConversionNameId::identifier() const
-{ return 0; }
SelectorNameId::~SelectorNameId()
{ }
@@ -195,28 +206,28 @@ bool SelectorNameId::match0(const Name *otherName, Matcher *matcher) const
const Identifier *SelectorNameId::identifier() const
{
if (_names.empty())
- return 0;
+ return nullptr;
return nameAt(0)->identifier();
}
-unsigned SelectorNameId::nameCount() const
-{ return unsigned(_names.size()); }
+int SelectorNameId::nameCount() const
+{ return int(_names.size()); }
-const Name *SelectorNameId::nameAt(unsigned index) const
+const Name *SelectorNameId::nameAt(int index) const
{ return _names[index]; }
bool SelectorNameId::hasArguments() const
{ return _hasArguments; }
-AnonymousNameId::AnonymousNameId(unsigned classTokenIndex)
+AnonymousNameId::AnonymousNameId(int classTokenIndex)
: _classTokenIndex(classTokenIndex)
{ }
AnonymousNameId::~AnonymousNameId()
{ }
-unsigned AnonymousNameId::classTokenIndex() const
+int AnonymousNameId::classTokenIndex() const
{
return _classTokenIndex;
}
@@ -231,5 +242,4 @@ bool AnonymousNameId::match0(const Name *otherName, Matcher *matcher) const
return false;
}
-const Identifier *AnonymousNameId::identifier() const
-{ return 0; }
+
diff --git a/src/libs/3rdparty/cplusplus/Names.h b/src/libs/3rdparty/cplusplus/Names.h
index b8d090da2f..ede94eb781 100644
--- a/src/libs/3rdparty/cplusplus/Names.h
+++ b/src/libs/3rdparty/cplusplus/Names.h
@@ -23,6 +23,7 @@
#include "CPlusPlusForwardDeclarations.h"
#include "Name.h"
#include "FullySpecifiedType.h"
+#include <functional>
#include <vector>
namespace CPlusPlus {
@@ -35,17 +36,17 @@ public:
virtual ~QualifiedNameId();
- virtual const Identifier *identifier() const;
+ const Identifier *identifier() const override;
const Name *base() const;
const Name *name() const;
- virtual const QualifiedNameId *asQualifiedNameId() const
+ const QualifiedNameId *asQualifiedNameId() const override
{ return this; }
protected:
- virtual void accept0(NameVisitor *visitor) const;
- virtual bool match0(const Name *otherName, Matcher *matcher) const;
+ void accept0(NameVisitor *visitor) const override;
+ bool match0(const Name *otherName, Matcher *matcher) const override;
private:
const Name *_base;
@@ -58,21 +59,72 @@ public:
DestructorNameId(const Name *name);
virtual ~DestructorNameId();
- virtual const Name *name() const;
+ const Name *name() const;
- virtual const Identifier *identifier() const;
+ const Identifier *identifier() const override;
- virtual const DestructorNameId *asDestructorNameId() const
+ const DestructorNameId *asDestructorNameId() const override
{ return this; }
protected:
- virtual void accept0(NameVisitor *visitor) const;
- virtual bool match0(const Name *otherName, Matcher *matcher) const;
+ void accept0(NameVisitor *visitor) const override;
+ bool match0(const Name *otherName, Matcher *matcher) const override;
private:
const Name *_name;
};
+class CPLUSPLUS_EXPORT TemplateArgument
+{
+public:
+ TemplateArgument()
+ : _expressionTy(nullptr)
+ , _numericLiteral(nullptr)
+ {}
+
+ TemplateArgument(const FullySpecifiedType &type, const NumericLiteral *numericLiteral = nullptr)
+ : _expressionTy(type)
+ , _numericLiteral(numericLiteral)
+ {}
+
+ bool hasType() const { return _expressionTy.isValid(); }
+
+ bool hasNumericLiteral() const { return _numericLiteral != nullptr; }
+
+ const FullySpecifiedType &type() const { return _expressionTy; }
+ FullySpecifiedType &type() { return _expressionTy; }
+
+ const NumericLiteral *numericLiteral() const { return _numericLiteral; }
+ void setNumericLiteral(const NumericLiteral *l) { _numericLiteral = l; }
+
+ bool operator==(const TemplateArgument &other) const
+ {
+ return _expressionTy == other._expressionTy && _numericLiteral == other._numericLiteral;
+ }
+ bool operator!=(const TemplateArgument &other) const
+ {
+ return _expressionTy != other._expressionTy || _numericLiteral != other._numericLiteral;
+ }
+ bool operator<(const TemplateArgument &other) const
+ {
+ if (_expressionTy == other._expressionTy) {
+ return _numericLiteral < other._numericLiteral;
+ }
+ return _expressionTy < other._expressionTy;
+ }
+
+ bool match(const TemplateArgument &otherTy, Matcher *matcher = nullptr) const;
+
+ size_t hash() const
+ {
+ return _expressionTy.hash() ^ std::hash<const NumericLiteral *>()(_numericLiteral);
+ }
+
+private:
+ FullySpecifiedType _expressionTy;
+ const NumericLiteral *_numericLiteral = nullptr;
+};
+
class CPLUSPLUS_EXPORT TemplateNameId: public Name
{
public:
@@ -85,33 +137,36 @@ public:
virtual ~TemplateNameId();
- virtual const Identifier *identifier() const;
+ const Identifier *identifier() const override;
// ### find a better name
- unsigned templateArgumentCount() const;
- const FullySpecifiedType &templateArgumentAt(unsigned index) const;
+ int templateArgumentCount() const;
+ const TemplateArgument &templateArgumentAt(int index) const;
- virtual const TemplateNameId *asTemplateNameId() const
+ const TemplateNameId *asTemplateNameId() const override
{ return this; }
- typedef std::vector<FullySpecifiedType>::const_iterator TemplateArgumentIterator;
+ typedef std::vector<TemplateArgument>::const_iterator TemplateArgumentIterator;
TemplateArgumentIterator firstTemplateArgument() const { return _templateArguments.begin(); }
TemplateArgumentIterator lastTemplateArgument() const { return _templateArguments.end(); }
bool isSpecialization() const { return _isSpecialization; }
- // Comparator needed to distinguish between two different TemplateNameId(e.g.:used in std::map)
- struct Compare {
+ // Comparator needed to distinguish between two different TemplateNameId(e.g.:used in std::unordered_map)
+ struct Equals {
bool operator()(const TemplateNameId *name, const TemplateNameId *other) const;
};
+ struct Hash {
+ size_t operator()(const TemplateNameId *name) const;
+ };
protected:
- virtual void accept0(NameVisitor *visitor) const;
- virtual bool match0(const Name *otherName, Matcher *matcher) const;
+ void accept0(NameVisitor *visitor) const override;
+ bool match0(const Name *otherName, Matcher *matcher) const override;
private:
const Identifier *_identifier;
- std::vector<FullySpecifiedType> _templateArguments;
+ std::vector<TemplateArgument> _templateArguments;
// now TemplateNameId can be a specialization or an instantiation
bool _isSpecialization;
};
@@ -125,7 +180,7 @@ public:
! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- , ->* ->
- () []
+ () [] <=>
*/
enum Kind {
InvalidOp,
@@ -170,7 +225,8 @@ public:
ArrowStarOp,
ArrowOp,
FunctionCallOp,
- ArrayAccessOp
+ ArrayAccessOp,
+ SpaceShipOp
};
public:
@@ -179,14 +235,12 @@ public:
Kind kind() const;
- virtual const Identifier *identifier() const;
-
- virtual const OperatorNameId *asOperatorNameId() const
- { return this; }
+ const Identifier *identifier() const override { return nullptr; }
+ const OperatorNameId *asOperatorNameId() const override { return this; }
protected:
- virtual void accept0(NameVisitor *visitor) const;
- virtual bool match0(const Name *otherName, Matcher *matcher) const;
+ void accept0(NameVisitor *visitor) const override;
+ bool match0(const Name *otherName, Matcher *matcher) const override;
private:
Kind _kind;
@@ -198,16 +252,13 @@ public:
ConversionNameId(const FullySpecifiedType &type);
virtual ~ConversionNameId();
- FullySpecifiedType type() const;
-
- virtual const Identifier *identifier() const;
-
- virtual const ConversionNameId *asConversionNameId() const
- { return this; }
+ FullySpecifiedType type() const { return _type; }
+ const Identifier *identifier() const override { return nullptr; }
+ const ConversionNameId *asConversionNameId() const override { return this; }
protected:
- virtual void accept0(NameVisitor *visitor) const;
- virtual bool match0(const Name *otherName, Matcher *matcher) const;
+ void accept0(NameVisitor *visitor) const override;
+ bool match0(const Name *otherName, Matcher *matcher) const override;
private:
FullySpecifiedType _type;
@@ -222,13 +273,13 @@ public:
virtual ~SelectorNameId();
- virtual const Identifier *identifier() const;
+ const Identifier *identifier() const override;
- unsigned nameCount() const;
- const Name *nameAt(unsigned index) const;
+ int nameCount() const;
+ const Name *nameAt(int index) const;
bool hasArguments() const;
- virtual const SelectorNameId *asSelectorNameId() const
+ const SelectorNameId *asSelectorNameId() const override
{ return this; }
typedef std::vector<const Name *>::const_iterator NameIterator;
@@ -237,33 +288,32 @@ public:
NameIterator lastName() const { return _names.end(); }
protected:
- virtual void accept0(NameVisitor *visitor) const;
- virtual bool match0(const Name *otherName, Matcher *matcher) const;
+ void accept0(NameVisitor *visitor) const override;
+ bool match0(const Name *otherName, Matcher *matcher) const override;
private:
std::vector<const Name *> _names;
bool _hasArguments;
};
-class CPLUSPLUS_EXPORT AnonymousNameId: public Name
+class CPLUSPLUS_EXPORT AnonymousNameId final : public Name
{
public:
- AnonymousNameId(unsigned classTokenIndex);
+ AnonymousNameId(int classTokenIndex);
virtual ~AnonymousNameId();
- unsigned classTokenIndex() const;
+ int classTokenIndex() const;
- virtual const Identifier *identifier() const;
+ const Identifier *identifier() const override { return nullptr; }
- virtual const AnonymousNameId *asAnonymousNameId() const
- { return this; }
+ const AnonymousNameId *asAnonymousNameId() const override { return this; }
protected:
- virtual void accept0(NameVisitor *visitor) const;
- virtual bool match0(const Name *otherName, Matcher *matcher) const;
+ void accept0(NameVisitor *visitor) const override;
+ bool match0(const Name *otherName, Matcher *matcher) const override;
private:
- unsigned _classTokenIndex;
+ int _classTokenIndex;
};
diff --git a/src/libs/3rdparty/cplusplus/Parser.cpp b/src/libs/3rdparty/cplusplus/Parser.cpp
index 6bee0fa972..30e3fe0be1 100644
--- a/src/libs/3rdparty/cplusplus/Parser.cpp
+++ b/src/libs/3rdparty/cplusplus/Parser.cpp
@@ -39,7 +39,7 @@
#define CPLUSPLUS_NO_DEBUG_RULE
#define MAX_EXPRESSION_DEPTH 1000
-#define MAX_STATEMENT_DEPTH 100
+#define MAX_STATEMENT_DEPTH 300
using namespace CPlusPlus;
@@ -49,7 +49,7 @@ class DebugRule {
public:
static int depth;
- DebugRule(const char *name, const char *spell, unsigned idx, bool blocked)
+ DebugRule(const char *name, const char *spell, int idx, bool blocked)
{
for (int i = 0; i <= depth; ++i)
fputc('-', stderr);
@@ -98,10 +98,11 @@ enum {
And = 8,
Equality = 9,
Relational = 10,
- Shift = 11,
- Additive = 12,
- Multiplicative = 13,
- PointerToMember = 14
+ ThreeWayComp = 11,
+ Shift = 12,
+ Additive = 13,
+ Multiplicative = 14,
+ PointerToMember = 15
};
} // namespace Precedece
@@ -116,29 +117,30 @@ inline int precedence(int tokenKind, bool templateArguments)
return Prec::Assignment;
switch (tokenKind) {
- case T_COMMA: return Prec::Comma;
- case T_QUESTION: return Prec::Conditional;
- case T_PIPE_PIPE: return Prec::LogicalOr;
- case T_AMPER_AMPER: return Prec::LogicalAnd;
- case T_PIPE: return Prec::InclusiveOr;
- case T_CARET: return Prec::ExclusiveOr;
- case T_AMPER: return Prec::And;
+ case T_COMMA: return Prec::Comma;
+ case T_QUESTION: return Prec::Conditional;
+ case T_PIPE_PIPE: return Prec::LogicalOr;
+ case T_AMPER_AMPER: return Prec::LogicalAnd;
+ case T_PIPE: return Prec::InclusiveOr;
+ case T_CARET: return Prec::ExclusiveOr;
+ case T_AMPER: return Prec::And;
case T_EQUAL_EQUAL:
- case T_EXCLAIM_EQUAL: return Prec::Equality;
+ case T_EXCLAIM_EQUAL: return Prec::Equality;
case T_GREATER:
case T_LESS:
case T_LESS_EQUAL:
- case T_GREATER_EQUAL: return Prec::Relational;
+ case T_GREATER_EQUAL: return Prec::Relational;
+ case T_LESS_EQUAL_GREATER: return Prec::ThreeWayComp;
case T_LESS_LESS:
- case T_GREATER_GREATER: return Prec::ExclusiveOr;
+ case T_GREATER_GREATER: return Prec::ExclusiveOr;
case T_PLUS:
- case T_MINUS: return Prec::Additive;
+ case T_MINUS: return Prec::Additive;
case T_STAR:
case T_SLASH:
- case T_PERCENT: return Prec::Multiplicative;
+ case T_PERCENT: return Prec::Multiplicative;
case T_ARROW_STAR:
- case T_DOT_STAR: return Prec::PointerToMember;
- default: return Prec::Unknown;
+ case T_DOT_STAR: return Prec::PointerToMember;
+ default: return Prec::Unknown;
}
}
@@ -170,20 +172,20 @@ public:
};
struct CacheKey {
- CacheKey(unsigned initialCursor, ASTKind astKind)
+ CacheKey(int initialCursor, ASTKind astKind)
: initialCursor(initialCursor)
, astKind(astKind)
{}
- const unsigned initialCursor;
+ const int initialCursor;
const ASTKind astKind;
};
public:
ASTCache() {}
- void insert(ASTKind astKind, unsigned tokenIndexBeforeParsing,
- AST *resultingAST, unsigned resultingTokenIndex, bool resultingReturnValue)
+ void insert(ASTKind astKind, int tokenIndexBeforeParsing,
+ AST *resultingAST, int resultingTokenIndex, bool resultingReturnValue)
{
const auto key = std::make_pair(astKind, tokenIndexBeforeParsing);
@@ -195,14 +197,14 @@ public:
_cache.insert(keyValue);
}
- AST *find(ASTKind astKind, unsigned tokenIndex,
- unsigned *resultingTokenIndex, bool *foundInCache, bool *returnValue) const
+ AST *find(ASTKind astKind, int tokenIndex,
+ int *resultingTokenIndex, bool *foundInCache, bool *returnValue) const
{
const auto key = std::make_pair(astKind, tokenIndex);
const auto it = _cache.find(key);
if (it == _cache.end()) {
*foundInCache = false;
- return 0;
+ return nullptr;
} else {
*foundInCache = true;
*resultingTokenIndex = it->second.resultingTokenIndex;
@@ -224,11 +226,11 @@ private:
struct ParseFunctionResult {
AST *resultingAST;
- unsigned resultingTokenIndex;
+ int resultingTokenIndex;
bool returnValue;
};
- typedef std::pair<int, unsigned> ASTKindAndTokenIndex;
+ typedef std::pair<int, int> ASTKindAndTokenIndex;
std::unordered_map<ASTKindAndTokenIndex, ParseFunctionResult, KeyHasher> _cache;
};
@@ -251,7 +253,7 @@ inline void debugPrintCheckCache(bool) {}
#define CHECK_CACHE(ASTKind, ASTType) \
do { \
bool foundInCache; \
- unsigned newTokenIndex; \
+ int newTokenIndex; \
bool returnValue; \
if (AST *ast = _astCache->find(ASTKind, cursor(), \
&newTokenIndex, &foundInCache, &returnValue)) { \
@@ -411,6 +413,7 @@ bool Parser::skipUntilStatement()
case T_BREAK:
case T_CONTINUE:
case T_RETURN:
+ case T_CO_RETURN:
case T_GOTO:
case T_TRY:
case T_CATCH:
@@ -486,7 +489,7 @@ int Parser::find(int token, int stopAt)
return 0;
}
-void Parser::match(int kind, unsigned *token)
+void Parser::match(int kind, int *token)
{
if (LA() == kind)
*token = consumeToken();
@@ -501,7 +504,7 @@ bool Parser::parseClassOrNamespaceName(NameAST *&node)
{
DEBUG_THIS_RULE();
if (LA() == T_IDENTIFIER && (LA(2) == T_COLON_COLON || LA(2) == T_LESS)) {
- unsigned identifier_token = cursor();
+ int identifier_token = cursor();
if (LA(2) == T_LESS) {
bool blocked = blockErrors(true);
@@ -521,7 +524,7 @@ bool Parser::parseClassOrNamespaceName(NameAST *&node)
return true;
}
} else if (LA() == T_TEMPLATE) {
- unsigned template_token = consumeToken();
+ int template_token = consumeToken();
if (parseTemplateId(node, template_token) && LA() == T_COLON_COLON)
return true;
rewind(template_token);
@@ -529,12 +532,12 @@ bool Parser::parseClassOrNamespaceName(NameAST *&node)
return false;
}
-bool Parser::parseTemplateId(NameAST *&node, unsigned template_token)
+bool Parser::parseTemplateId(NameAST *&node, int template_token)
{
DEBUG_THIS_RULE();
CHECK_CACHE(ASTCache::TemplateId, NameAST);
- const unsigned start = cursor();
+ const int start = cursor();
if (LA() == T_IDENTIFIER && LA(2) == T_LESS) {
TemplateIdAST *ast = new (_pool) TemplateIdAST;
@@ -554,7 +557,7 @@ bool Parser::parseTemplateId(NameAST *&node, unsigned template_token)
}
const bool result = false;
- _astCache->insert(ASTCache::TemplateId, start, 0, cursor(), result);
+ _astCache->insert(ASTCache::TemplateId, start, nullptr, cursor(), result);
rewind(start);
return result;
}
@@ -564,9 +567,9 @@ bool Parser::parseNestedNameSpecifier(NestedNameSpecifierListAST *&node,
{
DEBUG_THIS_RULE();
NestedNameSpecifierListAST **nested_name_specifier = &node;
- NameAST *class_or_namespace_name = 0;
+ NameAST *class_or_namespace_name = nullptr;
if (parseClassOrNamespaceName(class_or_namespace_name) && LA() == T_COLON_COLON) {
- unsigned scope_token = consumeToken();
+ int scope_token = consumeToken();
NestedNameSpecifierAST *name = new (_pool) NestedNameSpecifierAST;
name->class_or_namespace_name = class_or_namespace_name;
@@ -598,7 +601,7 @@ bool Parser::parseNestedNameSpecifier(NestedNameSpecifierListAST *&node,
bool Parser::parseNestedNameSpecifierOpt(NestedNameSpecifierListAST *&name, bool acceptTemplateId)
{
DEBUG_THIS_RULE();
- unsigned start = cursor();
+ int start = cursor();
if (! parseNestedNameSpecifier(name, acceptTemplateId))
rewind(start);
return true;
@@ -607,7 +610,7 @@ bool Parser::parseNestedNameSpecifierOpt(NestedNameSpecifierListAST *&name, bool
bool Parser::parseName(NameAST *&node, bool acceptTemplateId)
{
DEBUG_THIS_RULE();
- unsigned global_scope_token = 0;
+ int global_scope_token = 0;
switch (LA()) {
case T_COLON_COLON:
@@ -623,13 +626,13 @@ bool Parser::parseName(NameAST *&node, bool acceptTemplateId)
if (LA() == T_COLON_COLON)
global_scope_token = consumeToken();
- NestedNameSpecifierListAST *nested_name_specifier = 0;
+ NestedNameSpecifierListAST *nested_name_specifier = nullptr;
parseNestedNameSpecifierOpt(nested_name_specifier,
/*acceptTemplateId=*/ true);
- NameAST *unqualified_name = 0;
+ NameAST *unqualified_name = nullptr;
if (parseUnqualifiedName(unqualified_name,
- /*acceptTemplateId=*/ acceptTemplateId || nested_name_specifier != 0)) {
+ /*acceptTemplateId=*/ acceptTemplateId || nested_name_specifier != nullptr)) {
if (! global_scope_token && ! nested_name_specifier) {
node = unqualified_name;
return true;
@@ -653,9 +656,9 @@ bool Parser::parseTranslationUnit(TranslationUnitAST *&node)
DeclarationListAST **decl = &ast->declaration_list;
while (LA()) {
- unsigned start_declaration = cursor();
+ int start_declaration = cursor();
- DeclarationAST *declaration = 0;
+ DeclarationAST *declaration = nullptr;
if (parseDeclaration(declaration)) {
*decl = new (_pool) DeclarationListAST;
@@ -729,15 +732,15 @@ bool Parser::parseDeclaration(DeclarationAST *&node)
case T_Q_DECLARE_INTERFACE:
{
consumeToken();
- unsigned lparen_token = 0;
+ int lparen_token = 0;
match(T_LPAREN, &lparen_token);
- NameAST *name = 0;
+ NameAST *name = nullptr;
parseName(name);
- unsigned comma_token = 0;
+ int comma_token = 0;
match(T_COMMA, &comma_token);
- unsigned string_literal = 0;
+ int string_literal = 0;
match(T_STRING_LITERAL, &string_literal);
- unsigned rparen_token = 0;
+ int rparen_token = 0;
match(T_RPAREN, &rparen_token);
} return true;
@@ -759,8 +762,8 @@ bool Parser::parseDeclaration(DeclarationAST *&node)
default: {
if (_languageFeatures.objCEnabled && LA() == T___ATTRIBUTE__) {
- const unsigned start = cursor();
- SpecifierListAST *attributes = 0, **attr = &attributes;
+ const int start = cursor();
+ SpecifierListAST *attributes = nullptr, **attr = &attributes;
while (parseGnuAttributeSpecifier(*attr))
attr = &(*attr)->next;
if (LA() == T_AT_INTERFACE)
@@ -770,6 +773,18 @@ bool Parser::parseDeclaration(DeclarationAST *&node)
else if (LA() == T_AT_PROPERTY)
return parseObjCPropertyDeclaration(node, attributes);
rewind(start);
+ } else if (LA() == T___DECLSPEC) {
+ const int start = cursor();
+ SpecifierListAST *attributes = nullptr, **attr = &attributes;
+ while (parseMsvcDeclspecSpecifier(*attr))
+ attr = &(*attr)->next;
+ rewind(start);
+ } else if (lookAtStdAttribute()) {
+ const int start = cursor();
+ SpecifierListAST *attributes = nullptr, **attr = &attributes;
+ while (parseStdAttributeSpecifier(*attr))
+ attr = &(*attr)->next;
+ rewind(start);
}
if (LA() == T_EXTERN && LA(2) == T_TEMPLATE)
@@ -817,8 +832,8 @@ bool Parser::parseLinkageBody(DeclarationAST *&node)
if (tk == T_RBRACE)
break;
- unsigned start_declaration = cursor();
- DeclarationAST *declaration = 0;
+ int start_declaration = cursor();
+ DeclarationAST *declaration = nullptr;
if (parseDeclaration(declaration)) {
*declaration_ptr = new (_pool) DeclarationListAST;
(*declaration_ptr)->value = declaration;
@@ -865,11 +880,11 @@ bool Parser::parseNamespace(DeclarationAST *&node)
&& !isNestedNamespace())
return false;
- unsigned inline_token = 0;
+ int inline_token = 0;
if (_languageFeatures.cxx11Enabled && LA() == T_INLINE)
inline_token = consumeToken();
- unsigned namespace_token = consumeToken();
+ int namespace_token = consumeToken();
if (LA() == T_IDENTIFIER && LA(2) == T_EQUAL) {
if (inline_token)
@@ -896,7 +911,7 @@ bool Parser::parseNamespace(DeclarationAST *&node)
} else if (LA() == T_LBRACE) {
parseLinkageBody(ast->linkage_body);
} else { // attempt to do error recovery
- unsigned pos = cursor();
+ int pos = cursor();
for (;LA() != T_EOF_SYMBOL; consumeToken()) {
switch (LA()) {
case T_IDENTIFIER:
@@ -932,7 +947,7 @@ bool Parser::isNestedNamespace() const
bool Parser::parseNestedNamespace(DeclarationAST *&node)
{
DEBUG_THIS_RULE();
- DeclarationAST *ast = 0;
+ DeclarationAST *ast = nullptr;
if (isNestedNamespace() && parseNamespace(ast)) {
node = ast;
return true;
@@ -1003,7 +1018,7 @@ bool Parser::parseAliasDeclaration(DeclarationAST *&node)
alias->equal_token = consumeToken();
- ExpressionAST *expr = 0;
+ ExpressionAST *expr = nullptr;
parseTypeId(expr);
if (expr)
alias->typeId = expr->asTypeId();
@@ -1019,11 +1034,11 @@ bool Parser::parseConversionFunctionId(NameAST *&node)
DEBUG_THIS_RULE();
if (LA() != T_OPERATOR)
return false;
- unsigned operator_token = consumeToken();
- SpecifierListAST *type_specifier = 0;
+ int operator_token = consumeToken();
+ SpecifierListAST *type_specifier = nullptr;
if (! parseTypeSpecifier(type_specifier))
return false;
- PtrOperatorListAST *ptr_operators = 0, **ptr_operators_tail = &ptr_operators;
+ PtrOperatorListAST *ptr_operators = nullptr, **ptr_operators_tail = &ptr_operators;
while (parsePtrOperator(*ptr_operators_tail))
ptr_operators_tail = &(*ptr_operators_tail)->next;
@@ -1040,9 +1055,9 @@ bool Parser::parseOperatorFunctionId(NameAST *&node)
DEBUG_THIS_RULE();
if (LA() != T_OPERATOR)
return false;
- unsigned operator_token = consumeToken();
+ int operator_token = consumeToken();
- OperatorAST *op = 0;
+ OperatorAST *op = nullptr;
if (! parseOperator(op))
return false;
@@ -1053,13 +1068,13 @@ bool Parser::parseOperatorFunctionId(NameAST *&node)
return true;
}
-Parser::TemplateArgumentListEntry *Parser::templateArgumentListEntry(unsigned tokenIndex)
+Parser::TemplateArgumentListEntry *Parser::templateArgumentListEntry(int tokenIndex)
{
- std::map<unsigned, TemplateArgumentListEntry>::iterator it =_templateArgumentList.find(tokenIndex);
+ std::map<int, TemplateArgumentListEntry>::iterator it =_templateArgumentList.find(tokenIndex);
if (it != _templateArgumentList.end())
return &it->second;
- return 0;
+ return nullptr;
}
bool Parser::parseTemplateArgumentList(ExpressionListAST *&node)
@@ -1069,13 +1084,13 @@ bool Parser::parseTemplateArgumentList(ExpressionListAST *&node)
if (TemplateArgumentListEntry *entry = templateArgumentListEntry(cursor())) {
rewind(entry->cursor);
node = entry->ast;
- return entry->ast != 0;
+ return entry->ast != nullptr;
}
- unsigned start = cursor();
+ int start = cursor();
ExpressionListAST **template_argument_ptr = &node;
- ExpressionAST *template_argument = 0;
+ ExpressionAST *template_argument = nullptr;
const bool cxx11Enabled = _languageFeatures.cxx11Enabled;
if (parseTemplateArgument(template_argument)) {
*template_argument_ptr = new (_pool) ExpressionListAST;
@@ -1103,14 +1118,14 @@ bool Parser::parseTemplateArgumentList(ExpressionListAST *&node)
ExpressionListAST *template_argument_list = node;
for (ExpressionListAST *iter = template_argument_list, **ast_iter = &node;
iter; iter = iter->next, ast_iter = &(*ast_iter)->next)
- *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : 0);
+ *ast_iter = new (pool) ExpressionListAST((iter->value) ? iter->value->clone(pool) : nullptr);
}
_templateArgumentList.insert(std::make_pair(start, TemplateArgumentListEntry(start, cursor(), node)));
return true;
}
- _templateArgumentList.insert(std::make_pair(start, TemplateArgumentListEntry(start, cursor(), 0)));
+ _templateArgumentList.insert(std::make_pair(start, TemplateArgumentListEntry(start, cursor(), nullptr)));
return false;
}
@@ -1127,7 +1142,7 @@ bool Parser::parseAsmDefinition(DeclarationAST *&node)
ast->volatile_token = consumeToken();
match(T_LPAREN, &ast->lparen_token);
- unsigned string_literal_token = 0;
+ int string_literal_token = 0;
match(T_STRING_LITERAL, &string_literal_token);
while (LA() == T_STRING_LITERAL) {
consumeToken();
@@ -1181,19 +1196,19 @@ bool Parser::parseAsmOperandList()
bool Parser::parseAsmOperand()
{
DEBUG_THIS_RULE();
- unsigned string_literal_token = 0;
+ int string_literal_token = 0;
match(T_STRING_LITERAL, &string_literal_token);
if (LA() == T_LBRACKET) {
- /*unsigned lbracket_token = */ consumeToken();
+ /*int lbracket_token = */ consumeToken();
match(T_STRING_LITERAL, &string_literal_token);
- unsigned rbracket_token = 0;
+ int rbracket_token = 0;
match(T_RBRACKET, &rbracket_token);
}
- unsigned lparen_token = 0, rparen_token = 0;
+ int lparen_token = 0, rparen_token = 0;
match(T_LPAREN, &lparen_token);
- ExpressionAST *expression = 0;
+ ExpressionAST *expression = nullptr;
parseExpression(expression);
match(T_RPAREN, &rparen_token);
return true;
@@ -1205,7 +1220,7 @@ bool Parser::parseAsmClobberList()
if (LA() != T_STRING_LITERAL)
return false;
- unsigned string_literal_token = consumeToken();
+ int string_literal_token = consumeToken();
while (LA() == T_COMMA) {
consumeToken();
@@ -1233,14 +1248,18 @@ bool Parser::parseTemplateDeclaration(DeclarationAST *&node)
ast->less_token = consumeToken();
if (maybeSplitGreaterGreaterToken() || LA() == T_GREATER || parseTemplateParameterList(ast->template_parameter_list))
match(T_GREATER, &ast->greater_token);
+ if (!parseRequiresClauseOpt(ast->requiresClause))
+ return false;
}
while (LA()) {
- unsigned start_declaration = cursor();
+ int start_declaration = cursor();
- ast->declaration = 0;
+ ast->declaration = nullptr;
if (parseDeclaration(ast->declaration))
break;
+ if (parseConceptDeclaration(ast->declaration))
+ break;
error(start_declaration, "expected a declaration");
rewind(start_declaration + 1);
@@ -1251,6 +1270,205 @@ bool Parser::parseTemplateDeclaration(DeclarationAST *&node)
return true;
}
+bool Parser::parseConceptDeclaration(DeclarationAST *&node)
+{
+ if (!_languageFeatures.cxx20Enabled)
+ return false;
+ if (LA() != T_CONCEPT)
+ return false;
+
+ const auto ast = new (_pool) ConceptDeclarationAST;
+ ast->concept_token = consumeToken();
+ if (!parseName(ast->name))
+ return false;
+ parseAttributeSpecifier(ast->attributes);
+ if (LA() != T_EQUAL)
+ return false;
+ ast->equals_token = consumeToken();
+ if (!parseLogicalOrExpression(ast->constraint))
+ return false;
+ if (LA() != T_SEMICOLON)
+ return false;
+ ast->semicolon_token = consumeToken();
+ node = ast;
+ return true;
+}
+
+bool Parser::parsePlaceholderTypeSpecifier(PlaceholderTypeSpecifierAST *&node)
+{
+ if ((lookAtBuiltinTypeSpecifier() || _translationUnit->tokenAt(_tokenIndex).isKeyword())
+ && (LA() != T_AUTO && LA() != T_DECLTYPE)) {
+ return false;
+ }
+
+ TypeConstraintAST *typeConstraint = nullptr;
+ const int savedCursor = cursor();
+ parseTypeConstraint(typeConstraint);
+ if (LA() != T_AUTO && (LA() != T_DECLTYPE || LA(1) != T_LPAREN || LA(2) != T_AUTO)) {
+ rewind(savedCursor);
+ return false;
+ }
+ const auto spec = new (_pool) PlaceholderTypeSpecifierAST;
+ spec->typeConstraint = typeConstraint;
+ if (LA() == T_DECLTYPE) {
+ spec->declTypetoken = consumeToken();
+ if (LA() != T_LPAREN)
+ return false;
+ spec->lparenToken = consumeToken();
+ if (LA() != T_AUTO)
+ return false;
+ spec->autoToken = consumeToken();
+ if (LA() != T_RPAREN)
+ return false;
+ spec->rparenToken = consumeToken();
+ } else {
+ spec->autoToken = consumeToken();
+ }
+ node = spec;
+ return true;
+}
+
+bool Parser::parseTypeConstraint(TypeConstraintAST *&node)
+{
+ if (!_languageFeatures.cxx20Enabled)
+ return false;
+ NestedNameSpecifierListAST *nestedName = nullptr;
+ parseNestedNameSpecifierOpt(nestedName, true);
+ NameAST *conceptName = nullptr;
+ if (!parseUnqualifiedName(conceptName, false))
+ return false;
+ const auto typeConstraint = new (_pool) TypeConstraintAST;
+ typeConstraint->nestedName = nestedName;
+ typeConstraint->conceptName = conceptName;
+ if (LA() != T_LESS) {
+ node = typeConstraint;
+ return true;
+ }
+ typeConstraint->lessToken = consumeToken();
+ if (LA() != T_GREATER) {
+ if (!parseTemplateArgumentList(typeConstraint->templateArgs))
+ return false;
+ }
+ if (LA() != T_GREATER)
+ return false;
+ typeConstraint->greaterToken = consumeToken();
+ node = typeConstraint;
+ return true;
+}
+
+bool Parser::parseRequirement()
+{
+ if (LA() == T_TYPENAME) { // type-requirement
+ consumeToken();
+ NameAST *name = nullptr;
+ if (!parseName(name, true))
+ return false;
+ if (LA() != T_SEMICOLON)
+ return false;
+ consumeToken();
+ return true;
+ }
+ if (LA() == T_LBRACE) { // compound-requirement
+ consumeToken();
+ ExpressionAST *expr = nullptr;
+ if (!parseExpression(expr))
+ return false;
+ if (LA() != T_RBRACE)
+ return false;
+ consumeToken();
+ if (LA() == T_NOEXCEPT)
+ consumeToken();
+ if (LA() == T_SEMICOLON) {
+ consumeToken();
+ return true;
+ }
+ TypeConstraintAST *typeConstraint = nullptr;
+ if (!parseTypeConstraint(typeConstraint))
+ return false;
+ if (LA() != T_SEMICOLON)
+ return false;
+ consumeToken();
+ return true;
+ }
+ if (LA() == T_REQUIRES) { // nested-requirement
+ consumeToken();
+ ExpressionAST *constraintExpr = nullptr;
+ if (!parseLogicalOrExpression(constraintExpr))
+ return false;
+ if (LA() != T_SEMICOLON)
+ return false;
+ consumeToken();
+ return true;
+ }
+ ExpressionAST *simpleExpr;
+ if (!parseExpression(simpleExpr)) // simple-requirement
+ return false;
+ if (LA() != T_SEMICOLON)
+ return false;
+ consumeToken();
+ return true;
+}
+
+bool Parser::parseRequiresClauseOpt(RequiresClauseAST *&node)
+{
+ if (!_languageFeatures.cxx20Enabled)
+ return true;
+ if (LA() != T_REQUIRES)
+ return true;
+ const auto ast = new (_pool) RequiresClauseAST;
+ ast->requires_token = consumeToken();
+ if (!parsePrimaryExpression(ast->constraint))
+ return false;
+ while (true) {
+ if (LA() != T_PIPE_PIPE && LA() != T_AMPER_AMPER)
+ break;
+ ExpressionAST *next = nullptr;
+ if (!parsePrimaryExpression(next))
+ return false;
+
+ // This won't yield the right precedence, but I don't care.
+ BinaryExpressionAST *expr = new (_pool) BinaryExpressionAST;
+ expr->left_expression = ast->constraint;
+ expr->binary_op_token = consumeToken();
+ expr->right_expression = next;
+ ast->constraint = expr;
+ }
+ node = ast;
+ return true;
+}
+
+bool Parser::parseRequiresExpression(ExpressionAST *&node)
+{
+ if (!_languageFeatures.cxx20Enabled)
+ return false;
+ if (LA() != T_REQUIRES)
+ return false;
+
+ const auto ast = new (_pool) RequiresExpressionAST;
+ ast->requires_token = consumeToken();
+ if (LA() == T_LPAREN) {
+ ast->lparen_token = consumeToken();
+ if (!parseParameterDeclarationClause(ast->parameters))
+ return false;
+ if (LA() != T_RPAREN)
+ return false;
+ ast->rparen_token = consumeToken();
+ }
+ if (LA() != T_LBRACE)
+ return false;
+ ast->lbrace_token = consumeToken();
+ if (!parseRequirement())
+ return false;
+ while (LA() != T_RBRACE) {
+ if (!parseRequirement())
+ return false;
+ }
+ ast->rbrace_token = consumeToken();
+
+ node = ast;
+ return true;
+}
+
bool Parser::parseOperator(OperatorAST *&node) // ### FIXME
{
DEBUG_THIS_RULE();
@@ -1288,6 +1506,7 @@ bool Parser::parseOperator(OperatorAST *&node) // ### FIXME
case T_GREATER_EQUAL:
case T_GREATER_GREATER_EQUAL:
case T_LESS_EQUAL:
+ case T_LESS_EQUAL_GREATER:
case T_LESS_LESS_EQUAL:
case T_MINUS_EQUAL:
case T_PERCENT_EQUAL:
@@ -1335,7 +1554,7 @@ bool Parser::parseCvQualifiers(SpecifierListAST *&node)
{
DEBUG_THIS_RULE();
- unsigned start = cursor();
+ int start = cursor();
SpecifierListAST **ast = &node;
while (*ast)
@@ -1357,7 +1576,7 @@ bool Parser::parseCvQualifiers(SpecifierListAST *&node)
return start != cursor();
}
-bool Parser::parseRefQualifier(unsigned &ref_qualifier)
+bool Parser::parseRefQualifier(int &ref_qualifier)
{
DEBUG_THIS_RULE();
@@ -1382,7 +1601,7 @@ bool Parser::parseOverrideFinalQualifiers(SpecifierListAST *&node)
if (!_languageFeatures.cxx11Enabled)
return false;
- unsigned start = cursor();
+ int start = cursor();
SpecifierListAST **ast = &node;
while (*ast)
@@ -1421,13 +1640,13 @@ bool Parser::parsePtrOperator(PtrOperatorListAST *&node)
node = new (_pool) PtrOperatorListAST(ast);
return true;
} else if (LA() == T_COLON_COLON || LA() == T_IDENTIFIER) {
- unsigned scope_or_identifier_token = cursor();
+ int scope_or_identifier_token = cursor();
- unsigned global_scope_token = 0;
+ int global_scope_token = 0;
if (LA() == T_COLON_COLON)
global_scope_token = consumeToken();
- NestedNameSpecifierListAST *nested_name_specifiers = 0;
+ NestedNameSpecifierListAST *nested_name_specifiers = nullptr;
bool has_nested_name_specifier = parseNestedNameSpecifier(nested_name_specifiers, true);
if (has_nested_name_specifier && LA() == T_STAR) {
PointerToMemberAST *ast = new (_pool) PointerToMemberAST;
@@ -1446,7 +1665,7 @@ bool Parser::parsePtrOperator(PtrOperatorListAST *&node)
bool Parser::parseTemplateArgument(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
- unsigned start = cursor();
+ int start = cursor();
if (parseTypeId(node)) {
int index = 1;
@@ -1482,9 +1701,17 @@ bool Parser::parseDeclSpecifierSeq(SpecifierListAST *&decl_specifier_seq,
{
DEBUG_THIS_RULE();
bool has_type_specifier = false;
- NameAST *named_type_specifier = 0;
+ NameAST *named_type_specifier = nullptr;
SpecifierListAST **decl_specifier_seq_ptr = &decl_specifier_seq;
for (;;) {
+ PlaceholderTypeSpecifierAST *placeholderSpec = nullptr;
+ // A simple auto is also technically a placeholder-type-specifier, but for historical
+ // reasons, it is handled further below.
+ if (LA() != T_AUTO && parsePlaceholderTypeSpecifier(placeholderSpec)) {
+ *decl_specifier_seq_ptr = new (_pool) SpecifierListAST(placeholderSpec);
+ decl_specifier_seq_ptr = &(*decl_specifier_seq_ptr)->next;
+ continue;
+ }
if (! noStorageSpecifiers && ! onlySimpleTypeSpecifiers && lookAtStorageClassSpecifier()) {
// storage-class-specifier
SimpleSpecifierAST *spec = new (_pool) SimpleSpecifierAST;
@@ -1515,24 +1742,38 @@ bool Parser::parseDeclSpecifierSeq(SpecifierListAST *&decl_specifier_seq,
} else if (! onlySimpleTypeSpecifiers && ! has_type_specifier &&
(LA() == T_TYPENAME || LA() == T_ENUM || lookAtClassKey())) {
// typename-specifier, elaborated-type-specifier
- unsigned startOfElaboratedTypeSpecifier = cursor();
+ int startOfTypeSpecifier = cursor();
if (! parseElaboratedTypeSpecifier(*decl_specifier_seq_ptr)) {
- error(startOfElaboratedTypeSpecifier, "expected an elaborated type specifier");
- break;
+ rewind(startOfTypeSpecifier);
+ if (LA() == T_ENUM) {
+ if (!parseEnumSpecifier(*decl_specifier_seq_ptr)) {
+ error(startOfTypeSpecifier, "expected an enum specifier");
+ break;
+ }
+ } else if (lookAtClassKey()) {
+ if (!parseClassSpecifier(*decl_specifier_seq_ptr)) {
+ error(startOfTypeSpecifier, "expected a class specifier");
+ break;
+ }
+ } else {
+ error(startOfTypeSpecifier, "expected an elaborated type specifier");
+ break;
+ }
}
decl_specifier_seq_ptr = &(*decl_specifier_seq_ptr)->next;
has_type_specifier = true;
- } else
+ } else {
break;
+ }
}
- return decl_specifier_seq != 0;
+ return decl_specifier_seq != nullptr;
}
bool Parser::parseDeclaratorOrAbstractDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list)
{
DEBUG_THIS_RULE();
- unsigned start = cursor();
+ int start = cursor();
bool blocked = blockErrors(true);
if (parseDeclarator(node, decl_specifier_list)) {
blockErrors(blocked);
@@ -1543,26 +1784,28 @@ bool Parser::parseDeclaratorOrAbstractDeclarator(DeclaratorAST *&node, Specifier
return parseAbstractDeclarator(node, decl_specifier_list);
}
+
+
bool Parser::parseCoreDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *)
{
DEBUG_THIS_RULE();
- unsigned start = cursor();
- SpecifierListAST *attributes = 0;
+ int start = cursor();
+ SpecifierListAST *attributes = nullptr;
parseOptionalAttributeSpecifierSequence(attributes);
- PtrOperatorListAST *ptr_operators = 0, **ptr_operators_tail = &ptr_operators;
+ PtrOperatorListAST *ptr_operators = nullptr, **ptr_operators_tail = &ptr_operators;
while (parsePtrOperator(*ptr_operators_tail))
ptr_operators_tail = &(*ptr_operators_tail)->next;
if (LA() == T_COLON_COLON || LA() == T_IDENTIFIER || LA() == T_TILDE || LA() == T_OPERATOR
|| (_languageFeatures.cxx11Enabled && LA() == T_DOT_DOT_DOT && (LA(2) == T_COLON_COLON || LA(2) == T_IDENTIFIER))) {
- unsigned dot_dot_dot_token = 0;
+ int dot_dot_dot_token = 0;
if (LA() == T_DOT_DOT_DOT)
dot_dot_dot_token = consumeToken();
- NameAST *name = 0;
+ NameAST *name = nullptr;
if (parseName(name)) {
DeclaratorIdAST *declarator_id = new (_pool) DeclaratorIdAST;
declarator_id->dot_dot_dot_token = dot_dot_dot_token;
@@ -1578,8 +1821,8 @@ bool Parser::parseCoreDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_sp
if (attributes)
warning(attributes->firstToken(), "unexpected attribtues");
- unsigned lparen_token = consumeToken();
- DeclaratorAST *declarator = 0;
+ int lparen_token = consumeToken();
+ DeclaratorAST *declarator = nullptr;
if (parseDeclarator(declarator, decl_specifier_list) && LA() == T_RPAREN) {
NestedDeclaratorAST *nested_declarator = new (_pool) NestedDeclaratorAST;
nested_declarator->lparen_token = lparen_token;
@@ -1591,11 +1834,53 @@ bool Parser::parseCoreDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_sp
node = ast;
return true;
}
+ } else if (const auto decl = parseDecompositionDeclarator(decl_specifier_list)) {
+ DeclaratorAST *ast = new (_pool) DeclaratorAST;
+ ast->attribute_list = attributes;
+ ast->ptr_operator_list = ptr_operators;
+ ast->core_declarator = decl;
+ node = ast;
+ return true;
}
rewind(start);
return false;
}
+DecompositionDeclaratorAST *Parser::parseDecompositionDeclarator(
+ SpecifierListAST *decl_specifier_list)
+{
+ if (!_languageFeatures.cxx11Enabled || LA() != T_LBRACKET || !hasAuto(decl_specifier_list))
+ return nullptr;
+ consumeToken();
+
+ const auto decl = new (_pool) DecompositionDeclaratorAST;
+ for (NameListAST **iter = &decl->identifiers; ; iter = &(*iter)->next) {
+ if (LA() != T_IDENTIFIER) {
+ error(cursor(), "expected an identifier");
+ return nullptr;
+ }
+ SimpleNameAST * const name_ast = new (_pool) SimpleNameAST;
+ name_ast->identifier_token = consumeToken();
+ *iter = new (_pool) NameListAST;
+ (*iter)->value = name_ast;
+
+ if (LA() == T_RBRACKET) {
+ consumeToken();
+ return decl;
+ }
+
+ if (LA() == T_COMMA) {
+ consumeToken();
+ continue;
+ }
+
+ error(cursor(), "expected ',' or ']'");
+ return nullptr;
+ }
+
+ return nullptr;
+}
+
static bool maybeCppInitializer(DeclaratorAST *declarator)
{
if (declarator->ptr_operator_list)
@@ -1614,6 +1899,20 @@ static bool maybeCppInitializer(DeclaratorAST *declarator)
return true;
}
+bool Parser::hasAuto(SpecifierListAST *decl_specifier_list) const
+{
+ for (SpecifierListAST *iter = decl_specifier_list; iter; iter = iter->next) {
+ SpecifierAST *spec = iter->value;
+ if (SimpleSpecifierAST *simpleSpec = spec->asSimpleSpecifier()) {
+ if (_translationUnit->tokenKind(simpleSpec->specifier_token) == T_AUTO)
+ return true;
+ }
+ if (spec->asPlaceholderTypeSpecifier())
+ return true;
+ }
+ return false;
+}
+
bool Parser::parseDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *declaringClass)
{
DEBUG_THIS_RULE();
@@ -1623,17 +1922,17 @@ bool Parser::parseDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specif
PostfixDeclaratorListAST **postfix_ptr = &node->postfix_declarator_list;
for (;;) {
- unsigned startOfPostDeclarator = cursor();
+ int startOfPostDeclarator = cursor();
if (LA() == T_LPAREN) {
if (! declaringClass && LA(2) != T_RPAREN && maybeCppInitializer(node)) {
- unsigned lparen_token = cursor();
- ExpressionAST *initializer = 0;
+ int lparen_token = cursor();
+ ExpressionAST *initializer = nullptr;
bool blocked = blockErrors(true);
if (parseInitializer(initializer, &node->equal_token)) {
// maybe the initializer also parses as a FunctionDeclarator?
- ExpressionListParenAST *expr = 0;
+ ExpressionListParenAST *expr = nullptr;
if (initializer)
expr = initializer->asExpressionListParen();
if (expr) {
@@ -1643,11 +1942,12 @@ bool Parser::parseDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specif
// check for ambiguous declarators.
consumeToken();
- ParameterDeclarationClauseAST *parameter_declaration_clause = 0;
+ ParameterDeclarationClauseAST *parameter_declaration_clause = nullptr;
if (parseParameterDeclarationClause(parameter_declaration_clause) && LA() == T_RPAREN) {
- unsigned rparen_token = consumeToken();
+ int rparen_token = consumeToken();
FunctionDeclaratorAST *ast = new (_pool) FunctionDeclaratorAST;
+ ast->decl_specifier_list = decl_specifier_list;
ast->lparen_token = lparen_token;
ast->parameter_declaration_clause = parameter_declaration_clause;
ast->as_cpp_initializer = initializer;
@@ -1671,6 +1971,7 @@ bool Parser::parseDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specif
}
FunctionDeclaratorAST *ast = new (_pool) FunctionDeclaratorAST;
+ ast->decl_specifier_list = decl_specifier_list;
ast->lparen_token = consumeToken();
parseParameterDeclarationClause(ast->parameter_declaration_clause);
if (LA() != T_RPAREN) {
@@ -1684,19 +1985,9 @@ bool Parser::parseDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specif
parseRefQualifier(ast->ref_qualifier_token);
parseExceptionSpecification(ast->exception_specification);
- if (_languageFeatures.cxx11Enabled && ! node->ptr_operator_list && LA() == T_ARROW) {
- // only allow if there is 1 type spec, which has to be 'auto'
- bool hasAuto = false;
- for (SpecifierListAST *iter = decl_specifier_list; !hasAuto && iter; iter = iter->next) {
- SpecifierAST *spec = iter->value;
- if (SimpleSpecifierAST *simpleSpec = spec->asSimpleSpecifier()) {
- if (_translationUnit->tokenKind(simpleSpec->specifier_token) == T_AUTO)
- hasAuto = true;
- }
- }
-
- if (hasAuto)
- parseTrailingReturnType(ast->trailing_return_type);
+ if (_languageFeatures.cxx11Enabled && ! node->ptr_operator_list && LA() == T_ARROW
+ && hasAuto(decl_specifier_list)) {
+ parseTrailingReturnType(ast->trailing_return_type);
}
parseOverrideFinalQualifiers(ast->cv_qualifier_list);
@@ -1730,15 +2021,15 @@ bool Parser::parseAbstractCoreDeclarator(DeclaratorAST *&node, SpecifierListAST
{
DEBUG_THIS_RULE();
- PtrOperatorListAST *ptr_operators = 0, **ptr_operators_tail = &ptr_operators;
+ PtrOperatorListAST *ptr_operators = nullptr, **ptr_operators_tail = &ptr_operators;
while (parsePtrOperator(*ptr_operators_tail))
ptr_operators_tail = &(*ptr_operators_tail)->next;
- unsigned after_ptr_operators = cursor();
+ int after_ptr_operators = cursor();
if (LA() == T_LPAREN && LA(2) != T_RPAREN) {
- unsigned lparen_token = consumeToken();
- DeclaratorAST *declarator = 0;
+ int lparen_token = consumeToken();
+ DeclaratorAST *declarator = nullptr;
if (parseAbstractDeclarator(declarator, decl_specifier_list) && LA() == T_RPAREN) {
NestedDeclaratorAST *nested_declarator = new (_pool) NestedDeclaratorAST;
nested_declarator->lparen_token = lparen_token;
@@ -1768,12 +2059,13 @@ bool Parser::parseAbstractDeclarator(DeclaratorAST *&node, SpecifierListAST *dec
if (! parseAbstractCoreDeclarator(node, decl_specifier_list))
return false;
- PostfixDeclaratorListAST *postfix_declarators = 0,
+ PostfixDeclaratorListAST *postfix_declarators = nullptr,
**postfix_ptr = &postfix_declarators;
for (;;) {
if (LA() == T_LPAREN) {
FunctionDeclaratorAST *ast = new (_pool) FunctionDeclaratorAST;
+ ast->decl_specifier_list = decl_specifier_list;
ast->lparen_token = consumeToken();
if (LA() == T_RPAREN || parseParameterDeclarationClause(ast->parameter_declaration_clause)) {
if (LA() == T_RPAREN)
@@ -1844,7 +2136,7 @@ bool Parser::parseEnumSpecifier(SpecifierListAST *&node)
}
if (LA() == T_LBRACE) {
ast->lbrace_token = consumeToken();
- unsigned comma_token = 0;
+ int comma_token = 0;
EnumeratorListAST **enumerator_ptr = &ast->enumerator_list;
while (int tk = LA()) {
if (tk == T_RBRACE)
@@ -1879,7 +2171,7 @@ bool Parser::parseTemplateParameterList(DeclarationListAST *&node)
{
DEBUG_THIS_RULE();
DeclarationListAST **template_parameter_ptr = &node;
- DeclarationAST *declaration = 0;
+ DeclarationAST *declaration = nullptr;
if (parseTemplateParameter(declaration)) {
*template_parameter_ptr = new (_pool) DeclarationListAST;
(*template_parameter_ptr)->value = declaration;
@@ -1888,7 +2180,7 @@ bool Parser::parseTemplateParameterList(DeclarationListAST *&node)
while (LA() == T_COMMA) {
consumeToken(); // XXX Store this token somewhere
- declaration = 0;
+ declaration = nullptr;
if (parseTemplateParameter(declaration)) {
*template_parameter_ptr = new (_pool) DeclarationListAST;
(*template_parameter_ptr)->value = declaration;
@@ -1906,7 +2198,7 @@ bool Parser::parseTemplateParameter(DeclarationAST *&node)
if (parseTypeParameter(node))
return true;
bool previousTemplateArguments = switchTemplateArguments(true);
- ParameterDeclarationAST *ast = 0;
+ ParameterDeclarationAST *ast = nullptr;
bool parsed = parseParameterDeclaration(ast);
node = ast;
(void) switchTemplateArguments(previousTemplateArguments);
@@ -1935,8 +2227,8 @@ bool Parser::parseTypenameTypeParameter(DeclarationAST *&node)
bool Parser::parseTemplateTypeParameter(DeclarationAST *&node)
{
DEBUG_THIS_RULE();
+ TemplateTypeParameterAST *ast = new (_pool) TemplateTypeParameterAST;
if (LA() == T_TEMPLATE) {
- TemplateTypeParameterAST *ast = new (_pool) TemplateTypeParameterAST;
ast->template_token = consumeToken();
if (LA() == T_LESS)
ast->less_token = consumeToken();
@@ -1945,20 +2237,21 @@ bool Parser::parseTemplateTypeParameter(DeclarationAST *&node)
ast->greater_token = consumeToken();
if (LA() == T_CLASS)
ast->class_token = consumeToken();
- if (_languageFeatures.cxx11Enabled && LA() == T_DOT_DOT_DOT)
- ast->dot_dot_dot_token = consumeToken();
+ } else if (!parseTypeConstraint(ast->typeConstraint)) {
+ return false;
+ }
+ if (_languageFeatures.cxx11Enabled && LA() == T_DOT_DOT_DOT)
+ ast->dot_dot_dot_token = consumeToken();
- // parse optional name
- parseName(ast->name);
+ // parse optional name
+ parseName(ast->name);
- if (LA() == T_EQUAL) {
- ast->equal_token = consumeToken();
- parseTypeId(ast->type_id);
- }
- node = ast;
- return true;
+ if (LA() == T_EQUAL) {
+ ast->equal_token = consumeToken();
+ parseTypeId(ast->type_id);
}
- return false;
+ node = ast;
+ return true;
}
bool Parser::lookAtTypeParameter()
@@ -1994,10 +2287,9 @@ bool Parser::parseTypeParameter(DeclarationAST *&node)
if (lookAtTypeParameter())
return parseTypenameTypeParameter(node);
- else if (LA() == T_TEMPLATE)
+ if (LA() == T_TEMPLATE)
return parseTemplateTypeParameter(node);
- else
- return false;
+ return parseTemplateTypeParameter(node);
}
bool Parser::parseTypeId(ExpressionAST *&node)
@@ -2005,7 +2297,7 @@ bool Parser::parseTypeId(ExpressionAST *&node)
DEBUG_THIS_RULE();
CHECK_CACHE(ASTCache::TypeId, ExpressionAST);
- SpecifierListAST *type_specifier = 0;
+ SpecifierListAST *type_specifier = nullptr;
if (parseTypeSpecifier(type_specifier)) {
TypeIdAST *ast = new (_pool) TypeIdAST;
ast->type_specifier_list = type_specifier;
@@ -2022,11 +2314,11 @@ bool Parser::parseParameterDeclarationClause(ParameterDeclarationClauseAST *&nod
if (LA() == T_RPAREN)
return true; // nothing to do
CHECK_CACHE(ASTCache::ParameterDeclarationClause, ParameterDeclarationClauseAST);
- const unsigned initialCursor = cursor();
+ const int initialCursor = cursor();
- ParameterDeclarationListAST *parameter_declarations = 0;
+ ParameterDeclarationListAST *parameter_declarations = nullptr;
- unsigned dot_dot_dot_token = 0;
+ int dot_dot_dot_token = 0;
if (LA() == T_DOT_DOT_DOT)
dot_dot_dot_token = consumeToken();
else {
@@ -2059,7 +2351,7 @@ bool Parser::parseParameterDeclarationList(ParameterDeclarationListAST *&node)
return false; // nothing to do.
ParameterDeclarationListAST **parameter_declaration_ptr = &node;
- ParameterDeclarationAST *declaration = 0;
+ ParameterDeclarationAST *declaration = nullptr;
if (parseParameterDeclaration(declaration)) {
*parameter_declaration_ptr = new (_pool) ParameterDeclarationListAST;
(*parameter_declaration_ptr)->value = declaration;
@@ -2070,7 +2362,7 @@ bool Parser::parseParameterDeclarationList(ParameterDeclarationListAST *&node)
if (LA() == T_DOT_DOT_DOT)
break;
- declaration = 0;
+ declaration = nullptr;
if (parseParameterDeclaration(declaration)) {
*parameter_declaration_ptr = new (_pool) ParameterDeclarationListAST;
(*parameter_declaration_ptr)->value = declaration;
@@ -2085,7 +2377,12 @@ bool Parser::parseParameterDeclarationList(ParameterDeclarationListAST *&node)
bool Parser::parseParameterDeclaration(ParameterDeclarationAST *&node)
{
DEBUG_THIS_RULE();
- SpecifierListAST *decl_specifier_seq = 0;
+ if (_languageFeatures.cxx11Enabled) {
+ SpecifierListAST *attr_specifier_seq = nullptr;
+ while (parseStdAttributeSpecifier(attr_specifier_seq))
+ ;
+ }
+ SpecifierListAST *decl_specifier_seq = nullptr;
if (parseDeclSpecifierSeq(decl_specifier_seq)) {
ParameterDeclarationAST *ast = new (_pool) ParameterDeclarationAST;
ast->type_specifier_list = decl_specifier_seq;
@@ -2107,7 +2404,7 @@ bool Parser::parseParameterDeclaration(ParameterDeclarationAST *&node)
const Identifier *Parser::className(ClassSpecifierAST *ast) const
{
if (! ast)
- return 0;
+ return nullptr;
return identifier(ast->name);
}
@@ -2115,7 +2412,7 @@ const Identifier *Parser::className(ClassSpecifierAST *ast) const
const Identifier *Parser::identifier(NameAST *name) const
{
if (! name)
- return 0;
+ return nullptr;
if (QualifiedNameAST *q = name->asQualifiedName())
name = q->unqualified_name;
@@ -2127,7 +2424,7 @@ const Identifier *Parser::identifier(NameAST *name) const
return _translationUnit->identifier(template_id->identifier_token);
}
- return 0;
+ return nullptr;
}
bool Parser::parseClassSpecifier(SpecifierListAST *&node)
@@ -2136,9 +2433,9 @@ bool Parser::parseClassSpecifier(SpecifierListAST *&node)
if (! lookAtClassKey())
return false;
- unsigned classkey_token = consumeToken();
+ int classkey_token = consumeToken();
- SpecifierListAST *attributes = 0;
+ SpecifierListAST *attributes = nullptr;
parseOptionalAttributeSpecifierSequence(attributes);
if (LA(1) == T_IDENTIFIER && LA(2) == T_IDENTIFIER) {
@@ -2150,7 +2447,7 @@ bool Parser::parseClassSpecifier(SpecifierListAST *&node)
}
}
- NameAST *name = 0;
+ NameAST *name = nullptr;
parseName(name);
bool parsed = false;
@@ -2158,9 +2455,9 @@ bool Parser::parseClassSpecifier(SpecifierListAST *&node)
const bool previousInFunctionBody = _inFunctionBody;
_inFunctionBody = false;
- unsigned colon_token = 0;
- unsigned dot_dot_dot_token = 0;
- unsigned final_token = 0;
+ int colon_token = 0;
+ int dot_dot_dot_token = 0;
+ int final_token = 0;
if (LA() == T_IDENTIFIER) {
const Identifier *id = tok().identifier;
@@ -2175,7 +2472,7 @@ bool Parser::parseClassSpecifier(SpecifierListAST *&node)
name = ast;
}
- BaseSpecifierListAST *base_clause_list = 0;
+ BaseSpecifierListAST *base_clause_list = nullptr;
if (LA() == T_COLON) {
colon_token = cursor();
@@ -2188,7 +2485,7 @@ bool Parser::parseClassSpecifier(SpecifierListAST *&node)
if (LA() != T_LBRACE) {
error(cursor(), "expected `{' before `%s'", tok().spell());
- const unsigned saved = cursor();
+ const int saved = cursor();
for (int n = 0; n < 3 && LA() != T_EOF_SYMBOL; ++n, consumeToken()) {
if (LA() == T_LBRACE)
@@ -2219,8 +2516,8 @@ bool Parser::parseClassSpecifier(SpecifierListAST *&node)
break;
}
- unsigned start_declaration = cursor();
- DeclarationAST *declaration = 0;
+ int start_declaration = cursor();
+ DeclarationAST *declaration = nullptr;
if (parseMemberSpecification(declaration, ast)) {
if (declaration) { // paranoia check
*declaration_ptr = new (_pool) DeclarationListAST;
@@ -2275,6 +2572,7 @@ bool Parser::parseAccessDeclaration(DeclarationAST *&node)
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
+ [BINDABLE bindableFunction]
[CONSTANT]
[FINAL])
@@ -2325,11 +2623,12 @@ bool Parser::parseQtPropertyDeclaration(DeclarationAST *&node)
node = ast;
break;
} else if (LA() == T_IDENTIFIER) {
- QtPropertyDeclarationItemAST *item = 0;
+ QtPropertyDeclarationItemAST *item = nullptr;
switch (peekAtQtContextKeyword()) {
case Token_READ:
case Token_WRITE:
case Token_MEMBER:
+ case Token_BINDABLE:
case Token_RESET:
case Token_NOTIFY:
case Token_REVISION:
@@ -2337,8 +2636,8 @@ bool Parser::parseQtPropertyDeclaration(DeclarationAST *&node)
case Token_SCRIPTABLE:
case Token_STORED:
case Token_USER: {
- unsigned item_name_token = consumeToken();
- ExpressionAST *expr = 0;
+ int item_name_token = consumeToken();
+ ExpressionAST *expr = nullptr;
if (parsePostfixExpression(expr)) {
QtPropertyDeclarationItemAST *bItem =
new (_pool) QtPropertyDeclarationItemAST;
@@ -2403,7 +2702,7 @@ bool Parser::parseQtEnumDeclaration(DeclarationAST *&node)
ast->enum_specifier_token = consumeToken();
match(T_LPAREN, &ast->lparen_token);
for (NameListAST **iter = &ast->enumerator_list; LA() && LA() != T_RPAREN; iter = &(*iter)->next) {
- NameAST *name_ast = 0;
+ NameAST *name_ast = nullptr;
if (!parseName(name_ast))
break;
*iter = new (_pool) NameListAST;
@@ -2436,7 +2735,7 @@ bool Parser::parseQtFlags(DeclarationAST *&node)
ast->flags_specifier_token = consumeToken();
match(T_LPAREN, &ast->lparen_token);
for (NameListAST **iter = &ast->flag_enums_list; LA() && LA() != T_RPAREN; iter = &(*iter)->next) {
- NameAST *name_ast = 0;
+ NameAST *name_ast = nullptr;
if (!parseName(name_ast))
break;
*iter = new (_pool) NameListAST;
@@ -2486,15 +2785,15 @@ bool Parser::parseQtInterfaces(DeclarationAST *&node)
ast->interfaces_token = consumeToken();
match(T_LPAREN, &ast->lparen_token);
for (QtInterfaceNameListAST **iter = &ast->interface_name_list; LA() && LA() != T_RPAREN; iter = &(*iter)->next) {
- NameAST *name_ast = 0;
+ NameAST *name_ast = nullptr;
if (!parseName(name_ast))
break;
*iter = new (_pool) QtInterfaceNameListAST;
(*iter)->value = new (_pool) QtInterfaceNameAST;
(*iter)->value->interface_name = name_ast;
for (NameListAST **iter2 = &(*iter)->value->constraint_list; LA() && LA() == T_COLON; iter2 = &(*iter2)->next) {
- /*unsigned colon_token =*/ consumeToken();
- NameAST *name_ast2 = 0;
+ /*int colon_token =*/ consumeToken();
+ NameAST *name_ast2 = nullptr;
if (!parseName(name_ast2))
break;
*iter2 = new (_pool) NameListAST;
@@ -2595,7 +2894,7 @@ bool Parser::parseCtorInitializer(CtorInitializerAST *&node)
{
DEBUG_THIS_RULE();
if (LA() == T_COLON) {
- unsigned colon_token = consumeToken();
+ int colon_token = consumeToken();
CtorInitializerAST *ast = new (_pool) CtorInitializerAST;
ast->colon_token = colon_token;
@@ -2615,11 +2914,11 @@ bool Parser::parseElaboratedTypeSpecifier(SpecifierListAST *&node)
{
DEBUG_THIS_RULE();
if (lookAtClassKey() || LA() == T_ENUM || LA() == T_TYPENAME) {
- unsigned classkey_token = consumeToken();
+ int classkey_token = consumeToken();
- SpecifierListAST *attributes = 0;
+ SpecifierListAST *attributes = nullptr;
parseOptionalAttributeSpecifierSequence(attributes);
- NameAST *name = 0;
+ NameAST *name = nullptr;
if (parseName(name)) {
ElaboratedTypeSpecifierAST *ast = new (_pool) ElaboratedTypeSpecifierAST;
ast->classkey_token = classkey_token;
@@ -2706,8 +3005,8 @@ bool Parser::parseInitDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_sp
&& node->postfix_declarator_list->lastValue()->asFunctionDeclarator();
if (declaringClass && LA() == T_COLON
&& (! node || ! node->postfix_declarator_list)) {
- unsigned colon_token = consumeToken();
- ExpressionAST *expression = 0;
+ int colon_token = consumeToken();
+ ExpressionAST *expression = nullptr;
if (parseConstantExpression(expression) && (LA() == T_COMMA ||
LA() == T_SEMICOLON)) {
// recognized a bitfielddeclarator.
@@ -2717,10 +3016,15 @@ bool Parser::parseInitDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_sp
return true;
}
rewind(colon_token);
- } else if (isFunctionDeclarator && declaringClass && node->core_declarator && LA() == T_EQUAL && LA(3) == T_SEMICOLON) { // = 0, = delete, = default
+ } else if (isFunctionDeclarator && node->core_declarator && LA() == T_EQUAL && LA(3) == T_SEMICOLON) { // = 0, = delete, = default
if (!_languageFeatures.cxx11Enabled || LA(2) == T_NUMERIC_LITERAL) {
parseInitializer(node->initializer, &node->equal_token);
} else {
+ if (LA(2) != T_NUMERIC_LITERAL && LA(2) != T_DEFAULT && LA(2) != T_DELETE) {
+ error(cursor(), "expected 'default', 'delete' or '0', got '%s'", tok(2).spell());
+ return false;
+ }
+
node->equal_token = consumeToken();
IdExpressionAST *id_expr = new (_pool) IdExpressionAST;
@@ -2732,6 +3036,11 @@ bool Parser::parseInitDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_sp
}
} else if (node->core_declarator && (LA() == T_EQUAL || (_languageFeatures.cxx11Enabled && !isFunctionDeclarator && LA() == T_LBRACE) || (! declaringClass && LA() == T_LPAREN))) {
parseInitializer(node->initializer, &node->equal_token);
+ } else if (node->core_declarator && node->core_declarator->asDecompositionDeclarator()) {
+ error(cursor(), "structured binding needs initializer");
+ return false;
+ } else if (!parseRequiresClauseOpt(node->requiresClause)) {
+ return false;
}
return true;
}
@@ -2760,14 +3069,14 @@ bool Parser::parseBaseClause(BaseSpecifierListAST *&node)
return false;
}
-bool Parser::parseInitializer(ExpressionAST *&node, unsigned *equals_token)
+bool Parser::parseInitializer(ExpressionAST *&node, int *equals_token)
{
DEBUG_THIS_RULE();
return parseInitializer0x(node, equals_token);
}
-bool Parser::parseInitializer0x(ExpressionAST *&node, unsigned *equals_token)
+bool Parser::parseInitializer0x(ExpressionAST *&node, int *equals_token)
{
DEBUG_THIS_RULE();
@@ -2835,7 +3144,7 @@ bool Parser::parseInitializerList0x(ExpressionListAST *&node)
{
DEBUG_THIS_RULE();
ExpressionListAST **expression_list_ptr = &node;
- ExpressionAST *expression = 0;
+ ExpressionAST *expression = nullptr;
_initializerClauseDepth.push(1);
@@ -2941,7 +3250,7 @@ bool Parser::parseMemInitializerList(MemInitializerListAST *&node)
bool Parser::parseMemInitializer(MemInitializerListAST *&node)
{
DEBUG_THIS_RULE();
- NameAST *name = 0;
+ NameAST *name = nullptr;
if (! parseName(name))
return false;
@@ -2969,7 +3278,7 @@ bool Parser::parseTypeIdList(ExpressionListAST *&node)
{
DEBUG_THIS_RULE();
ExpressionListAST **expression_list_ptr = &node;
- ExpressionAST *typeId = 0;
+ ExpressionAST *typeId = nullptr;
if (parseTypeId(typeId)) {
*expression_list_ptr = new (_pool) ExpressionListAST;
(*expression_list_ptr)->value = typeId;
@@ -3000,7 +3309,7 @@ bool Parser::parseExpressionList(ExpressionListAST *&node)
{
DEBUG_THIS_RULE();
CHECK_CACHE(ASTCache::ExpressionList, ExpressionListAST);
- unsigned initialCursor = cursor();
+ int initialCursor = cursor();
if (_languageFeatures.cxx11Enabled) {
const bool result = parseInitializerList0x(node);
@@ -3009,7 +3318,7 @@ bool Parser::parseExpressionList(ExpressionListAST *&node)
}
ExpressionListAST **expression_list_ptr = &node;
- ExpressionAST *expression = 0;
+ ExpressionAST *expression = nullptr;
if (parseAssignmentExpression(expression)) {
*expression_list_ptr = new (_pool) ExpressionListAST;
(*expression_list_ptr)->value = expression;
@@ -3029,7 +3338,7 @@ bool Parser::parseExpressionList(ExpressionListAST *&node)
}
const bool result = false;
- _astCache->insert(ASTCache::ExpressionList, initialCursor, 0, cursor(), result);
+ _astCache->insert(ASTCache::ExpressionList, initialCursor, nullptr, cursor(), result);
return result;
}
@@ -3071,14 +3380,14 @@ bool Parser::parseInitializerList(ExpressionListAST *&node)
{
DEBUG_THIS_RULE();
ExpressionListAST **initializer_ptr = &node;
- ExpressionAST *initializer = 0;
+ ExpressionAST *initializer = nullptr;
if (parseInitializerClause(initializer)) {
*initializer_ptr = new (_pool) ExpressionListAST;
(*initializer_ptr)->value = initializer;
initializer_ptr = &(*initializer_ptr)->next;
while (LA() == T_COMMA) {
consumeToken(); // consume T_COMMA
- initializer = 0;
+ initializer = nullptr;
parseInitializerClause(initializer);
*initializer_ptr = new (_pool) ExpressionListAST;
(*initializer_ptr)->value = initializer;
@@ -3116,13 +3425,13 @@ bool Parser::parseUnqualifiedName(NameAST *&node, bool acceptTemplateId)
node = ast;
return true;
} else if (LA() == T_OPERATOR) {
- unsigned operator_token = cursor();
+ int operator_token = cursor();
if (parseOperatorFunctionId(node))
return true;
rewind(operator_token);
return parseConversionFunctionId(node);
} else if (LA() == T_IDENTIFIER) {
- unsigned identifier_token = cursor();
+ int identifier_token = cursor();
if (acceptTemplateId && LA(2) == T_LESS) {
bool blocked = blockErrors(true);
if (parseTemplateId(node)
@@ -3141,7 +3450,7 @@ bool Parser::parseUnqualifiedName(NameAST *&node, bool acceptTemplateId)
node = ast;
return true;
} else if (LA() == T_TEMPLATE) {
- unsigned template_token = consumeToken();
+ int template_token = consumeToken();
if (parseTemplateId(node, template_token))
return true;
rewind(template_token);
@@ -3205,7 +3514,7 @@ bool Parser::parseExpressionStatement(StatementAST *&node)
bool parsed = false;
- ExpressionAST *expression = 0;
+ ExpressionAST *expression = nullptr;
if (parseExpression(expression)) {
ExpressionStatementAST *ast = new (previousPool) ExpressionStatementAST;
if (expression)
@@ -3253,7 +3562,7 @@ bool Parser::parseStatement(StatementAST *&node, bool blockLabeledStatement)
return parseSwitchStatement(node);
case T_TRY:
- return parseTryBlockStatement(node, 0);
+ return parseTryBlockStatement(node, nullptr);
case T_CASE:
case T_DEFAULT:
@@ -3271,6 +3580,7 @@ bool Parser::parseStatement(StatementAST *&node, bool blockLabeledStatement)
return parseGotoStatement(node);
case T_RETURN:
+ case T_CO_RETURN:
return parseReturnStatement(node);
case T_LBRACE:
@@ -3314,7 +3624,7 @@ bool Parser::parseStatement(StatementAST *&node, bool blockLabeledStatement)
// Simply skip the emit token and parse as an expression statement - no strong
// reason to have an specific ast type.
consumeToken();
- ExpressionAST *expression = 0;
+ ExpressionAST *expression = nullptr;
if (parsePostfixExpression(expression)) {
ExpressionStatementAST *ast = new (_pool) ExpressionStatementAST;
ast->expression = expression;
@@ -3381,7 +3691,7 @@ bool Parser::parseGotoStatement(StatementAST *&node)
bool Parser::parseReturnStatement(StatementAST *&node)
{
DEBUG_THIS_RULE();
- if (LA() == T_RETURN) {
+ if (LA() == T_RETURN || LA() == T_CO_RETURN) {
ReturnStatementAST *ast = new (_pool) ReturnStatementAST;
ast->return_token = consumeToken();
if (_languageFeatures.cxx11Enabled && LA() == T_LBRACE)
@@ -3397,13 +3707,13 @@ bool Parser::parseReturnStatement(StatementAST *&node)
bool Parser::maybeAmbiguousStatement(DeclarationStatementAST *ast, StatementAST *&node)
{
- const unsigned start = ast->firstToken();
- const unsigned end = ast->lastToken();
+ const int start = ast->firstToken();
+ const int end = ast->lastToken();
const bool blocked = blockErrors(true);
bool maybeAmbiguous = false;
- StatementAST *stmt = 0;
+ StatementAST *stmt = nullptr;
if (parseExpressionStatement(stmt)) {
if (stmt->firstToken() == start && stmt->lastToken() == end) {
maybeAmbiguous = true;
@@ -3423,7 +3733,7 @@ bool Parser::parseExpressionOrDeclarationStatement(StatementAST *&node)
if (LA() == T_SEMICOLON)
return parseExpressionStatement(node);
- const unsigned start = cursor();
+ const int start = cursor();
if (lookAtCVQualifier()
|| lookAtStorageClassSpecifier()
@@ -3438,13 +3748,13 @@ bool Parser::parseExpressionOrDeclarationStatement(StatementAST *&node)
if (LA() == T_IDENTIFIER || (LA() == T_COLON_COLON && LA(2) == T_IDENTIFIER)) {
const bool blocked = blockErrors(true);
- ExpressionAST *expression = 0;
+ ExpressionAST *expression = nullptr;
const bool hasExpression = parseExpression(expression);
- const unsigned afterExpression = cursor();
+ const int afterExpression = cursor();
if (hasExpression/* && LA() == T_SEMICOLON*/) {
- //const unsigned semicolon_token = consumeToken();
- unsigned semicolon_token = 0;
+ //const int semicolon_token = consumeToken();
+ int semicolon_token = 0;
if (LA() == T_SEMICOLON)
semicolon_token = cursor();
@@ -3467,7 +3777,7 @@ bool Parser::parseExpressionOrDeclarationStatement(StatementAST *&node)
}
}
} else if (CallAST *call = expression->asCall()) {
- if (call->base_expression->asIdExpression() != 0) {
+ if (call->base_expression->asIdExpression() != nullptr) {
(void) blockErrors(blocked);
node = as_expression;
match(T_SEMICOLON, &as_expression->semicolon_token);
@@ -3477,14 +3787,14 @@ bool Parser::parseExpressionOrDeclarationStatement(StatementAST *&node)
rewind(start);
- DeclarationAST *declaration = 0;
+ DeclarationAST *declaration = nullptr;
if (parseSimpleDeclaration(declaration)) {
DeclarationStatementAST *as_declaration = new (_pool) DeclarationStatementAST;
as_declaration->declaration = declaration;
SimpleDeclarationAST *simple = declaration->asSimpleDeclaration();
if (! semicolon_token || invalidAssignment || semicolon_token != simple->semicolon_token ||
- (simple->decl_specifier_list != 0 && simple->declarator_list != 0)) {
+ (simple->decl_specifier_list != nullptr && simple->declarator_list != nullptr)) {
node = as_declaration;
(void) blockErrors(blocked);
return true;
@@ -3518,13 +3828,13 @@ bool Parser::parseExpressionOrDeclarationStatement(StatementAST *&node)
bool Parser::parseCondition(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
- unsigned start = cursor();
+ int start = cursor();
bool blocked = blockErrors(true);
- SpecifierListAST *type_specifier = 0;
+ SpecifierListAST *type_specifier = nullptr;
if (parseTypeSpecifier(type_specifier)) {
- DeclaratorAST *declarator = 0;
- if (parseInitDeclarator(declarator, type_specifier, /*declaringClass=*/ 0)) {
+ DeclaratorAST *declarator = nullptr;
+ if (parseInitDeclarator(declarator, type_specifier, /*declaringClass=*/ nullptr)) {
if (declarator->initializer && declarator->equal_token) {
ConditionAST *ast = new (_pool) ConditionAST;
ast->type_specifier_list = type_specifier;
@@ -3583,15 +3893,15 @@ bool Parser::parseForeachStatement(StatementAST *&node)
ast->foreach_token = consumeToken();
match(T_LPAREN, &ast->lparen_token);
- unsigned startOfTypeSpecifier = cursor();
+ int startOfTypeSpecifier = cursor();
bool blocked = blockErrors(true);
if (parseTypeSpecifier(ast->type_specifier_list))
parseDeclarator(ast->declarator, ast->type_specifier_list);
if (! ast->type_specifier_list || ! ast->declarator) {
- ast->type_specifier_list = 0;
- ast->declarator = 0;
+ ast->type_specifier_list = nullptr;
+ ast->declarator = nullptr;
blockErrors(blocked);
rewind(startOfTypeSpecifier);
@@ -3617,11 +3927,11 @@ bool Parser::parseForStatement(StatementAST *&node)
if (LA() != T_FOR)
return false;
- unsigned for_token = consumeToken();
- unsigned lparen_token = 0;
+ int for_token = consumeToken();
+ int lparen_token = 0;
match(T_LPAREN, &lparen_token);
- unsigned startOfTypeSpecifier = cursor();
+ int startOfTypeSpecifier = cursor();
bool blocked = blockErrors(true);
if (_languageFeatures.objCEnabled) {
@@ -3634,16 +3944,16 @@ bool Parser::parseForStatement(StatementAST *&node)
if ((ast->type_specifier_list || ast->declarator) && !peekAtObjCContextKeyword(Token_in)) {
// woops, probably parsed too much: "in" got parsed as a declarator. Let's redo it:
- ast->type_specifier_list = 0;
- ast->declarator = 0;
+ ast->type_specifier_list = nullptr;
+ ast->declarator = nullptr;
rewind(startOfTypeSpecifier);
parseDeclarator(ast->declarator, ast->type_specifier_list);
}
if (! ast->type_specifier_list || ! ast->declarator) {
- ast->type_specifier_list = 0;
- ast->declarator = 0;
+ ast->type_specifier_list = nullptr;
+ ast->declarator = nullptr;
rewind(startOfTypeSpecifier);
parseAssignmentExpression(ast->initializer);
@@ -3736,8 +4046,8 @@ bool Parser::parseCompoundStatement(StatementAST *&node)
if (tk == T_RBRACE)
break;
- unsigned start_statement = cursor();
- StatementAST *statement = 0;
+ int start_statement = cursor();
+ StatementAST *statement = nullptr;
if (! parseStatement(statement)) {
rewind(start_statement + 1);
skipUntilStatement();
@@ -3761,7 +4071,37 @@ bool Parser::parseIfStatement(StatementAST *&node)
if (LA() == T_IF) {
IfStatementAST *ast = new (_pool) IfStatementAST;
ast->if_token = consumeToken();
+ if (LA() == T_CONSTEXPR) {
+ // "if constexpr" added in cxx17, but we don't check cxx version here
+ // because msvc 2019 compiler uses "if constexpr" in headers despite cxx version set for the project
+ ast->constexpr_token = consumeToken();
+ }
match(T_LPAREN, &ast->lparen_token);
+
+ // C++17: init-statement
+ if (_languageFeatures.cxx17Enabled) {
+ const int savedCursor = cursor();
+ const bool savedBlockErrors = _translationUnit->blockErrors(true);
+ bool foundInitStmt = parseExpressionOrDeclarationStatement(ast->initStmt);
+ if (foundInitStmt)
+ foundInitStmt = ast->initStmt;
+ if (foundInitStmt) {
+ if (const auto exprStmt = ast->initStmt->asExpressionStatement()) {
+ foundInitStmt = exprStmt->semicolon_token;
+ } else if (const auto declStmt = ast->initStmt->asDeclarationStatement()) {
+ foundInitStmt = declStmt->declaration
+ && declStmt->declaration->asSimpleDeclaration()
+ && declStmt->declaration->asSimpleDeclaration()->semicolon_token;
+ } else {
+ foundInitStmt = false;
+ }
+ }
+ if (!foundInitStmt) {
+ ast->initStmt = nullptr;
+ rewind(savedCursor);
+ }
+ _translationUnit->blockErrors(savedBlockErrors);
+ }
parseCondition(ast->condition);
match(T_RPAREN, &ast->rparen_token);
if (! parseStatement(ast->statement))
@@ -3876,8 +4216,8 @@ bool Parser::parseNamespaceAliasDefinition(DeclarationAST *&node)
bool Parser::parseDeclarationStatement(StatementAST *&node)
{
DEBUG_THIS_RULE();
- unsigned start = cursor();
- DeclarationAST *declaration = 0;
+ int start = cursor();
+ DeclarationAST *declaration = nullptr;
if (! parseBlockDeclaration(declaration))
return false;
@@ -3961,6 +4301,8 @@ bool Parser::lookAtBuiltinTypeSpecifier() const
// [gcc] extensions
case T___TYPEOF__:
case T___ATTRIBUTE__:
+ // [msvc] extensions
+ case T___DECLSPEC:
return true;
default:
return false;
@@ -3998,7 +4340,7 @@ bool Parser::parseAttributeSpecifier(SpecifierListAST *&attribute_list)
ast->align_token = consumeToken();
match(T_LPAREN, &ast->lparen_token);
- const unsigned saved = cursor();
+ const int saved = cursor();
if (!parseTypeId(ast->typeIdExprOrAlignmentExpr) ||
(LA() != T_RPAREN &&
(LA(1) != T_DOT_DOT_DOT || LA(2) != T_RPAREN))) {
@@ -4020,8 +4362,22 @@ bool Parser::parseAttributeSpecifier(SpecifierListAST *&attribute_list)
attr_ptr = &(*attr_ptr)->next;
}
return true;
+ case T___DECLSPEC:
+ while (LA() == T___DECLSPEC) {
+ parseMsvcDeclspecSpecifier(*attr_ptr);
+ attr_ptr = &(*attr_ptr)->next;
+ }
+ return true;
default:
- return false;
+ {
+ bool foundAttributes = false;
+ while (lookAtStdAttribute()) {
+ parseStdAttributeSpecifier(*attr_ptr);
+ attr_ptr = &(*attr_ptr)->next;
+ foundAttributes = true;
+ }
+ return foundAttributes;
+ }
}
}
@@ -4078,16 +4434,51 @@ bool Parser::parseGnuAttributeList(GnuAttributeListAST *&node)
return true;
}
+bool Parser::parseMsvcDeclspecSpecifier(SpecifierListAST *&node)
+{
+ DEBUG_THIS_RULE();
+ if (LA() != T___DECLSPEC)
+ return false;
+
+ MsvcDeclspecSpecifierAST *ast = new (_pool) MsvcDeclspecSpecifierAST;
+ ast->attribute_token = consumeToken();
+ match(T_LPAREN, &ast->lparen_token);
+ parseGnuAttributeList(ast->attribute_list);
+ match(T_RPAREN, &ast->rparen_token);
+ node = new (_pool) SpecifierListAST(ast);
+ return true;
+}
+
+bool Parser::parseStdAttributeSpecifier(SpecifierListAST *&node)
+{
+ DEBUG_THIS_RULE();
+ if (!lookAtStdAttribute())
+ return false;
+
+ StdAttributeSpecifierAST *ast = new (_pool) StdAttributeSpecifierAST;
+ match(T_LBRACKET, &ast->first_lbracket_token);
+ match(T_LBRACKET, &ast->second_lbracket_token);
+ parseGnuAttributeList(ast->attribute_list);
+ match(T_RBRACKET, &ast->first_rbracket_token);
+ match(T_RBRACKET, &ast->second_rbracket_token);
+ node = new (_pool) SpecifierListAST(ast);
+ return true;
+}
+
bool Parser::parseBuiltinTypeSpecifier(SpecifierListAST *&node)
{
DEBUG_THIS_RULE();
if (LA() == T___ATTRIBUTE__) {
return parseGnuAttributeSpecifier(node);
+ } else if (LA() == T___DECLSPEC) {
+ return parseMsvcDeclspecSpecifier(node);
+ } else if (lookAtStdAttribute()) {
+ return parseStdAttributeSpecifier(node);
} else if (LA() == T___TYPEOF__) {
TypeofSpecifierAST *ast = new (_pool) TypeofSpecifierAST;
ast->typeof_token = consumeToken();
if (LA() == T_LPAREN) {
- unsigned lparen_token = consumeToken();
+ int lparen_token = consumeToken();
if (parseTypeId(ast->expression) && LA() == T_RPAREN) {
ast->lparen_token = lparen_token;
ast->rparen_token = consumeToken();
@@ -4122,7 +4513,7 @@ bool Parser::parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *de
const ASTCache::CacheKey cacheKey(cursor(), ASTCache::Declaration);
CHECK_CACHE(cacheKey.astKind, DeclarationAST);
- unsigned qt_invokable_token = 0;
+ int qt_invokable_token = 0;
if (declaringClass && (LA() == T_Q_SIGNAL || LA() == T_Q_SLOT || LA() == T_Q_INVOKABLE))
qt_invokable_token = consumeToken();
@@ -4130,9 +4521,9 @@ bool Parser::parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *de
// or a contructor declaration.
bool has_type_specifier = false;
bool has_complex_type_specifier = false;
- unsigned startOfNamedTypeSpecifier = 0;
- NameAST *named_type_specifier = 0;
- SpecifierListAST *decl_specifier_seq = 0,
+ int startOfNamedTypeSpecifier = 0;
+ NameAST *named_type_specifier = nullptr;
+ SpecifierListAST *decl_specifier_seq = nullptr,
**decl_specifier_seq_ptr = &decl_specifier_seq;
for (;;) {
if (lookAtCVQualifier() || lookAtFunctionSpecifier()
@@ -4174,7 +4565,7 @@ bool Parser::parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *de
break;
}
} else if (! has_type_specifier && LA() == T_ENUM) {
- unsigned startOfTypeSpecifier = cursor();
+ int startOfTypeSpecifier = cursor();
if (! parseElaboratedTypeSpecifier(*decl_specifier_seq_ptr)
|| LA() == T_LBRACE
|| (_languageFeatures.cxx11Enabled && LA() == T_COLON)) {
@@ -4188,7 +4579,7 @@ bool Parser::parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *de
decl_specifier_seq_ptr = &(*decl_specifier_seq_ptr)->next;
has_type_specifier = true;
} else if (! has_type_specifier && LA() == T_TYPENAME) {
- unsigned startOfElaboratedTypeSpecifier = cursor();
+ int startOfElaboratedTypeSpecifier = cursor();
if (! parseElaboratedTypeSpecifier(*decl_specifier_seq_ptr)) {
error(startOfElaboratedTypeSpecifier, "expected an elaborated type specifier");
break;
@@ -4196,7 +4587,7 @@ bool Parser::parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *de
decl_specifier_seq_ptr = &(*decl_specifier_seq_ptr)->next;
has_type_specifier = true;
} else if (! has_type_specifier && lookAtClassKey()) {
- unsigned startOfTypeSpecifier = cursor();
+ int startOfTypeSpecifier = cursor();
if (! parseElaboratedTypeSpecifier(*decl_specifier_seq_ptr) ||
(LA() == T_COLON || LA() == T_LBRACE
|| (LA(0) == T_IDENTIFIER && LA(1) == T_IDENTIFIER // MACRO Name followed by : or {
@@ -4216,22 +4607,22 @@ bool Parser::parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *de
break;
}
- DeclaratorListAST *declarator_list = 0,
+ DeclaratorListAST *declarator_list = nullptr,
**declarator_ptr = &declarator_list;
- DeclaratorAST *declarator = 0;
+ DeclaratorAST *declarator = nullptr;
if (LA() != T_SEMICOLON) {
const bool maybeCtor = (LA() == T_LPAREN && named_type_specifier);
bool didParseInitDeclarator = parseInitDeclarator(declarator, decl_specifier_seq, declaringClass);
if ((! didParseInitDeclarator && maybeCtor) || (didParseInitDeclarator && maybeCtor && LA() == T_COLON)){
rewind(startOfNamedTypeSpecifier);
- named_type_specifier = 0;
+ named_type_specifier = nullptr;
// pop the named type specifier from the decl-specifier-seq
SpecifierListAST **spec_ptr = &decl_specifier_seq;
for (; *spec_ptr; spec_ptr = &(*spec_ptr)->next) {
if (! (*spec_ptr)->next) {
- *spec_ptr = 0;
+ *spec_ptr = nullptr;
break;
}
}
@@ -4258,7 +4649,7 @@ bool Parser::parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *de
while (LA() == T_COMMA) {
consumeToken(); // consume T_COMMA
- declarator = 0;
+ declarator = nullptr;
if (parseInitDeclarator(declarator, decl_specifier_seq, declaringClass)) {
*declarator_ptr = new (_pool) DeclaratorListAST;
(*declarator_ptr)->value = declarator;
@@ -4282,7 +4673,7 @@ bool Parser::parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *de
node = ast;
CACHE_AND_RETURN(cacheKey, true); // recognized a function definition.
} else {
- CtorInitializerAST *ctor_initializer = 0;
+ CtorInitializerAST *ctor_initializer = nullptr;
bool hasCtorInitializer = false;
if (LA() == T_COLON) {
@@ -4290,7 +4681,7 @@ bool Parser::parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *de
parseCtorInitializer(ctor_initializer);
if (LA() != T_LBRACE) {
- const unsigned pos = cursor();
+ const int pos = cursor();
for (int n = 0; n < 3 && LA(); consumeToken(), ++n)
if (LA() == T_LBRACE)
@@ -4339,7 +4730,7 @@ bool Parser::maybeForwardOrClassDeclaration(SpecifierListAST *decl_specifier_seq
spec->asEnumSpecifier() ||
spec->asClassSpecifier()) {
for (it = it->next; it; it = it->next)
- if (it->value->asAttributeSpecifier() == 0)
+ if (it->value->asAttributeSpecifier() == nullptr)
return false;
return true;
}
@@ -4353,7 +4744,7 @@ bool Parser::parseFunctionBody(StatementAST *&node)
{
DEBUG_THIS_RULE();
if (_translationUnit->skipFunctionBody()) {
- unsigned token_lbrace = 0;
+ int token_lbrace = 0;
match(T_LBRACE, &token_lbrace);
if (! token_lbrace)
return false;
@@ -4361,7 +4752,7 @@ bool Parser::parseFunctionBody(StatementAST *&node)
const Token &tk = _translationUnit->tokenAt(token_lbrace);
if (tk.close_brace)
rewind(tk.close_brace);
- unsigned token_rbrace = 0;
+ int token_rbrace = 0;
match(T_RBRACE, &token_rbrace);
return true;
}
@@ -4391,12 +4782,12 @@ bool Parser::parseTryBlockStatement(StatementAST *&node, CtorInitializerAST **pl
ast->try_token = consumeToken();
// [ctor-initializer]
if (LA() == T_COLON) {
- const unsigned colonPos = cursor();
- CtorInitializerAST *ctor_initializer = 0;
+ const int colonPos = cursor();
+ CtorInitializerAST *ctor_initializer = nullptr;
parseCtorInitializer(ctor_initializer);
if (LA() != T_LBRACE) {
- const unsigned pos = cursor();
+ const int pos = cursor();
for (int n = 0; n < 3 && LA(); consumeToken(), ++n)
if (LA() == T_LBRACE)
@@ -4451,7 +4842,7 @@ bool Parser::parseExceptionDeclaration(ExceptionDeclarationAST *&node)
return true;
}
- SpecifierListAST *type_specifier = 0;
+ SpecifierListAST *type_specifier = nullptr;
if (parseTypeSpecifier(type_specifier)) {
ExceptionDeclarationAST *ast = new (_pool) ExceptionDeclarationAST;
ast->type_specifier_list = type_specifier;
@@ -4554,9 +4945,9 @@ bool Parser::parsePrimaryExpression(ExpressionAST *&node)
// GNU extension: '(' '{' statement-list '}' ')'
CompoundExpressionAST *ast = new (_pool) CompoundExpressionAST;
ast->lparen_token = consumeToken();
- StatementAST *statement = 0;
- parseCompoundStatement(statement);
- ast->statement = statement->asCompoundStatement();
+ StatementAST *statement = nullptr;
+ if (parseCompoundStatement(statement))
+ ast->statement = statement->asCompoundStatement();
match(T_RPAREN, &ast->rparen_token);
node = ast;
return true;
@@ -4569,7 +4960,7 @@ bool Parser::parsePrimaryExpression(ExpressionAST *&node)
return parseQtMethod(node);
case T_LBRACKET: {
- const unsigned lbracket_token = cursor();
+ const int lbracket_token = cursor();
if (_languageFeatures.cxx11Enabled) {
if (parseLambdaExpression(node))
@@ -4588,8 +4979,11 @@ bool Parser::parsePrimaryExpression(ExpressionAST *&node)
case T_AT_SELECTOR:
return parseObjCExpression(node);
+ case T_REQUIRES:
+ return parseRequiresExpression(node);
+
default: {
- NameAST *name = 0;
+ NameAST *name = nullptr;
if (parseNameId(name)) {
IdExpressionAST *ast = new (_pool) IdExpressionAST;
ast->name = name;
@@ -4667,15 +5061,15 @@ bool Parser::parseObjCTryStatement(StatementAST *& /*node*/)
parseCompoundStatement(body_statment);
while (LA() == T_AT_CATCH) {
/*catch_token =*/ consumeToken();
- unsigned lparen_token;
+ int lparen_token;
match(T_LPAREN, &lparen_token);
if (LA() == T_DOT_DOT_DOT) {
- /*unsigned ellipsis_token =*/ consumeToken();
+ /*int ellipsis_token =*/ consumeToken();
} else {
ParameterDeclarationAST *exception_decl;
parseParameterDeclaration(exception_decl);
}
- unsigned rparen_token;
+ int rparen_token;
match(T_RPAREN, &rparen_token);
StatementAST *catch_statement;
parseCompoundStatement(catch_statement);
@@ -4720,7 +5114,7 @@ bool Parser::parseObjCThrowStatement(StatementAST *&/*node*/)
/*throw_token =*/ consumeToken();
ExpressionAST *thrown_expression;
parseExpression(thrown_expression);
- unsigned semicolon_token;
+ int semicolon_token;
match(T_SEMICOLON, &semicolon_token);
return true;
@@ -4764,7 +5158,7 @@ bool Parser::parseObjCSelectorExpression(ExpressionAST *&node)
ast->selector_token = consumeToken();
match(T_LPAREN, &ast->lparen_token);
- unsigned identifier_token = 0;
+ int identifier_token = 0;
match(T_IDENTIFIER, &identifier_token);
if (LA() == T_COLON) {
ObjCSelectorAST *args = new (_pool) ObjCSelectorAST;
@@ -4807,12 +5201,12 @@ bool Parser::parseObjCMessageExpression(ExpressionAST *&node)
if (LA() != T_LBRACKET)
return false;
- unsigned start = cursor();
+ int start = cursor();
- unsigned lbracket_token = consumeToken();
- ExpressionAST *receiver_expression = 0;
- ObjCSelectorAST *selector = 0;
- ObjCMessageArgumentListAST *argument_list = 0;
+ int lbracket_token = consumeToken();
+ ExpressionAST *receiver_expression = nullptr;
+ ObjCSelectorAST *selector = nullptr;
+ ObjCMessageArgumentListAST *argument_list = nullptr;
if (parseObjCMessageReceiver(receiver_expression) &&
parseObjCMessageArguments(selector, argument_list)) {
@@ -4845,10 +5239,10 @@ bool Parser::parseObjCMessageArguments(ObjCSelectorAST *&selNode, ObjCMessageArg
if (LA() == T_RBRACKET)
return false; // nothing to do.
- unsigned start = cursor();
+ int start = cursor();
- ObjCSelectorArgumentAST *selectorArgument = 0;
- ObjCMessageArgumentAST *messageArgument = 0;
+ ObjCSelectorArgumentAST *selectorArgument = nullptr;
+ ObjCMessageArgumentAST *messageArgument = nullptr;
if (parseObjCSelectorArg(selectorArgument, messageArgument)) {
ObjCSelectorArgumentListAST *selAst = new (_pool) ObjCSelectorArgumentListAST;
@@ -4890,7 +5284,7 @@ bool Parser::parseObjCMessageArguments(ObjCSelectorAST *&selNode, ObjCMessageArg
return true;
} else {
rewind(start);
- unsigned name_token = 0;
+ int name_token = 0;
if (!parseObjCSelector(name_token))
return false;
ObjCSelectorAST *sel = new (_pool) ObjCSelectorAST;
@@ -4898,7 +5292,7 @@ bool Parser::parseObjCMessageArguments(ObjCSelectorAST *&selNode, ObjCMessageArg
sel->selector_argument_list->value = new (_pool) ObjCSelectorArgumentAST;
sel->selector_argument_list->value->name_token = name_token;
selNode = sel;
- argNode = 0;
+ argNode = nullptr;
return true;
}
@@ -4908,7 +5302,7 @@ bool Parser::parseObjCMessageArguments(ObjCSelectorAST *&selNode, ObjCMessageArg
bool Parser::parseObjCSelectorArg(ObjCSelectorArgumentAST *&selNode, ObjCMessageArgumentAST *&argNode)
{
DEBUG_THIS_RULE();
- unsigned selector_token = 0;
+ int selector_token = 0;
if (!parseObjCSelector(selector_token))
return false;
@@ -4921,7 +5315,7 @@ bool Parser::parseObjCSelectorArg(ObjCSelectorArgumentAST *&selNode, ObjCMessage
argNode = new (_pool) ObjCMessageArgumentAST;
ExpressionAST **expr = &argNode->parameter_value_expression;
- unsigned expressionStart = cursor();
+ int expressionStart = cursor();
if (parseAssignmentExpression(*expr) && LA() == T_COLON && (*expr)->asCastExpression()) {
rewind(expressionStart);
parseUnaryExpression(*expr);
@@ -4933,7 +5327,7 @@ bool Parser::parseObjCSelectorArg(ObjCSelectorArgumentAST *&selNode, ObjCMessage
bool Parser::parseNameId(NameAST *&name)
{
DEBUG_THIS_RULE();
- unsigned start = cursor();
+ int start = cursor();
if (! parseName(name))
return false;
@@ -4942,7 +5336,7 @@ bool Parser::parseNameId(NameAST *&name)
QualifiedNameAST *qualified_name_id = name->asQualifiedName();
- TemplateIdAST *template_id = 0;
+ TemplateIdAST *template_id = nullptr;
if (qualified_name_id) {
if (NameAST *unqualified_name = qualified_name_id->unqualified_name)
template_id = unqualified_name->asTemplateId();
@@ -4959,8 +5353,8 @@ bool Parser::parseNameId(NameAST *&name)
if (! template_arguments->next && template_arguments->value &&
template_arguments->value->asBinaryExpression()) {
- unsigned saved = cursor();
- ExpressionAST *expr = 0;
+ int saved = cursor();
+ ExpressionAST *expr = nullptr;
bool blocked = blockErrors(true);
bool lookAtCastExpression = parseCastExpression(expr);
@@ -4972,7 +5366,7 @@ bool Parser::parseNameId(NameAST *&name)
&& cast_expression->type_id && cast_expression->expression) {
rewind(start);
- name = 0;
+ name = nullptr;
return parseName(name, false);
}
}
@@ -4998,12 +5392,6 @@ bool Parser::parseNameId(NameAST *&name)
case T_CONST_CAST:
rewind(start);
return parseName(name, false);
-
- default:
- if (tok().isLiteral() || tok().isPunctuationOrOperator()) {
- rewind(start);
- return parseName(name, false);
- }
} // switch
return true;
@@ -5013,10 +5401,10 @@ bool Parser::parseNestedExpression(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
if (LA() == T_LPAREN) {
- unsigned lparen_token = consumeToken();
+ int lparen_token = consumeToken();
bool previousTemplateArguments = switchTemplateArguments(false);
- ExpressionAST *expression = 0;
+ ExpressionAST *expression = nullptr;
if (parseExpression(expression) && LA() == T_RPAREN) {
NestedExpressionAST *ast = new (_pool) NestedExpressionAST;
ast->lparen_token = lparen_token;
@@ -5056,8 +5444,8 @@ bool Parser::parseTypenameCallExpression(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
if (LA() == T_TYPENAME) {
- unsigned typename_token = consumeToken();
- NameAST *name = 0;
+ int typename_token = consumeToken();
+ NameAST *name = nullptr;
if (parseName(name)
&& (LA() == T_LPAREN || (_languageFeatures.cxx11Enabled && LA() == T_LBRACE))) {
TypenameCallExpressionAST *ast = new (_pool) TypenameCallExpressionAST;
@@ -5085,7 +5473,7 @@ bool Parser::parseTypeidExpression(ExpressionAST *&node)
ast->typeid_token = consumeToken();
if (LA() == T_LPAREN)
ast->lparen_token = consumeToken();
- unsigned saved = cursor();
+ int saved = cursor();
if (! (parseTypeId(ast->expression) && LA() == T_RPAREN)) {
rewind(saved);
parseExpression(ast->expression);
@@ -5115,13 +5503,13 @@ bool Parser::parseCorePostfixExpression(ExpressionAST *&node)
return parseTypeidExpression(node);
default: {
- unsigned start = cursor();
- SpecifierListAST *type_specifier = 0;
+ int start = cursor();
+ SpecifierListAST *type_specifier = nullptr;
bool blocked = blockErrors(true);
if (lookAtBuiltinTypeSpecifier() &&
parseSimpleTypeSpecifier(type_specifier) &&
(LA() == T_LPAREN || (_languageFeatures.cxx11Enabled && LA() == T_LBRACE))) {
- ExpressionAST *expr = 0;
+ ExpressionAST *expr = nullptr;
if (LA() == T_LPAREN) {
parseExpressionListParen(expr);
} else { // T_LBRACE
@@ -5138,10 +5526,10 @@ bool Parser::parseCorePostfixExpression(ExpressionAST *&node)
// look for compound literals
if (LA() == T_LPAREN) {
- unsigned lparen_token = consumeToken();
- ExpressionAST *type_id = 0;
+ int lparen_token = consumeToken();
+ ExpressionAST *type_id = nullptr;
if (parseTypeId(type_id) && LA() == T_RPAREN) {
- unsigned rparen_token = consumeToken();
+ int rparen_token = consumeToken();
if (LA() == T_LBRACE) {
blockErrors(blocked);
@@ -5223,7 +5611,7 @@ bool Parser::parseUnaryExpression(ExpressionAST *&node)
case T_PLUS:
case T_MINUS:
case T_EXCLAIM: {
- unsigned op = cursor();
+ int op = cursor();
UnaryExpressionAST *ast = new (_pool) UnaryExpressionAST;
ast->unary_op_token = consumeToken();
if (! parseCastExpression(ast->expression))
@@ -5253,7 +5641,7 @@ bool Parser::parseUnaryExpression(ExpressionAST *&node)
ast->dot_dot_dot_token = consumeToken();
if (LA() == T_LPAREN) {
- unsigned lparen_token = consumeToken();
+ int lparen_token = consumeToken();
const bool blocked = blockErrors(true);
const bool hasTypeId = parseTypeId(ast->expression);
(void) blockErrors(blocked);
@@ -5281,7 +5669,7 @@ bool Parser::parseUnaryExpression(ExpressionAST *&node)
ast->alignof_token = consumeToken();
match(T_LPAREN, &ast->lparen_token);
- ExpressionAST *temp = 0;
+ ExpressionAST *temp = nullptr;
parseTypeId(temp);
if (temp)
ast->typeId = temp->asTypeId();
@@ -5299,6 +5687,11 @@ bool Parser::parseUnaryExpression(ExpressionAST *&node)
return parseNoExceptOperatorExpression(node);
}
+ case T_CO_AWAIT:
+ if (!_languageFeatures.cxx20Enabled)
+ break;
+ return parseAwaitExpression(node);
+
default:
break;
} // switch
@@ -5318,10 +5711,10 @@ bool Parser::parseExpressionListParen(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
if (LA() == T_LPAREN) {
- unsigned lparen_token = consumeToken();
- ExpressionListAST *expression_list = 0;
+ int lparen_token = consumeToken();
+ ExpressionListAST *expression_list = nullptr;
if (parseExpressionList(expression_list) && LA() == T_RPAREN) {
- unsigned rparen_token = consumeToken();
+ int rparen_token = consumeToken();
ExpressionListParenAST *ast = new (_pool) ExpressionListParenAST;
ast->lparen_token = lparen_token;
ast->expression_list = expression_list;
@@ -5351,12 +5744,12 @@ bool Parser::parseNewExpression(ExpressionAST *&node)
ast->new_token = consumeToken();
- ExpressionAST *parenExpressionList = 0;
+ ExpressionAST *parenExpressionList = nullptr;
if (parseExpressionListParen(parenExpressionList)) {
- unsigned after_new_placement = cursor();
+ int after_new_placement = cursor();
- NewTypeIdAST *new_type_id = 0;
+ NewTypeIdAST *new_type_id = nullptr;
if (parseNewTypeId(new_type_id)) {
ast->new_placement = parenExpressionList->asExpressionListParen();
ast->new_type_id = new_type_id;
@@ -5368,8 +5761,8 @@ bool Parser::parseNewExpression(ExpressionAST *&node)
rewind(after_new_placement);
if (LA() == T_LPAREN) {
- unsigned lparen_token = consumeToken();
- ExpressionAST *type_id = 0;
+ int lparen_token = consumeToken();
+ ExpressionAST *type_id = nullptr;
if (parseTypeId(type_id) && LA() == T_RPAREN) {
ast->new_placement = parenExpressionList->asExpressionListParen();
ast->lparen_token = lparen_token;
@@ -5385,8 +5778,8 @@ bool Parser::parseNewExpression(ExpressionAST *&node)
rewind(ast->new_token + 1);
if (LA() == T_LPAREN) {
- unsigned lparen_token = consumeToken();
- ExpressionAST *type_id = 0;
+ int lparen_token = consumeToken();
+ ExpressionAST *type_id = nullptr;
if (parseTypeId(type_id) && LA() == T_RPAREN) {
ast->lparen_token = lparen_token;
ast->type_id = type_id;
@@ -5406,7 +5799,7 @@ bool Parser::parseNewExpression(ExpressionAST *&node)
bool Parser::parseNewTypeId(NewTypeIdAST *&node)
{
DEBUG_THIS_RULE();
- SpecifierListAST *typeSpec = 0;
+ SpecifierListAST *typeSpec = nullptr;
if (! parseTypeSpecifier(typeSpec))
return false;
@@ -5479,9 +5872,9 @@ bool Parser::parseCastExpression(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
if (LA() == T_LPAREN) {
- unsigned lparen_token = consumeToken();
- unsigned initialCursor = cursor();
- ExpressionAST *type_id = 0;
+ int lparen_token = consumeToken();
+ int initialCursor = cursor();
+ ExpressionAST *type_id = nullptr;
if (parseTypeId(type_id) && LA() == T_RPAREN) {
if (TypeIdAST *tid = type_id->asTypeId()) {
@@ -5493,10 +5886,10 @@ bool Parser::parseCastExpression(ExpressionAST *&node)
case T_PLUS_PLUS:
case T_MINUS_MINUS: {
- const unsigned rparen_token = consumeToken();
+ const int rparen_token = consumeToken();
const bool blocked = blockErrors(true);
- ExpressionAST *unary = 0;
+ ExpressionAST *unary = nullptr;
bool followedByUnaryExpression = parseUnaryExpression(unary);
blockErrors(blocked);
rewind(rparen_token);
@@ -5505,7 +5898,7 @@ bool Parser::parseCastExpression(ExpressionAST *&node)
if (! unary)
followedByUnaryExpression = false;
else if (UnaryExpressionAST *u = unary->asUnaryExpression())
- followedByUnaryExpression = u->expression != 0;
+ followedByUnaryExpression = u->expression != nullptr;
}
if (! followedByUnaryExpression)
@@ -5521,8 +5914,8 @@ bool Parser::parseCastExpression(ExpressionAST *&node)
}
}
- unsigned rparen_token = consumeToken();
- ExpressionAST *expression = 0;
+ int rparen_token = consumeToken();
+ ExpressionAST *expression = nullptr;
if (parseCastExpression(expression)) {
CastExpressionAST *ast = new (_pool) CastExpressionAST;
ast->lparen_token = lparen_token;
@@ -5535,63 +5928,13 @@ bool Parser::parseCastExpression(ExpressionAST *&node)
}
parse_as_unary_expression:
- _astCache->insert(ASTCache::TypeId, initialCursor, 0, cursor(), false);
+ _astCache->insert(ASTCache::TypeId, initialCursor, nullptr, cursor(), false);
rewind(lparen_token);
}
return parseUnaryExpression(node);
}
-bool Parser::parsePmExpression(ExpressionAST *&node)
-{
- PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::PointerToMember)
-}
-
-bool Parser::parseMultiplicativeExpression(ExpressionAST *&node)
-{
- PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::Multiplicative)
-}
-
-bool Parser::parseAdditiveExpression(ExpressionAST *&node)
-{
- PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::Additive)
-}
-
-bool Parser::parseShiftExpression(ExpressionAST *&node)
-{
- PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::Shift)
-}
-
-bool Parser::parseRelationalExpression(ExpressionAST *&node)
-{
- PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::Relational)
-}
-
-bool Parser::parseEqualityExpression(ExpressionAST *&node)
-{
- PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::Equality)
-}
-
-bool Parser::parseAndExpression(ExpressionAST *&node)
-{
- PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::And)
-}
-
-bool Parser::parseExclusiveOrExpression(ExpressionAST *&node)
-{
- PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::ExclusiveOr)
-}
-
-bool Parser::parseInclusiveOrExpression(ExpressionAST *&node)
-{
- PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::InclusiveOr)
-}
-
-bool Parser::parseLogicalAndExpression(ExpressionAST *&node)
-{
- PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::LogicalAnd)
-}
-
bool Parser::parseLogicalOrExpression(ExpressionAST *&node)
{
PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::LogicalOr)
@@ -5607,6 +5950,8 @@ bool Parser::parseAssignmentExpression(ExpressionAST *&node)
DEBUG_THIS_RULE();
if (LA() == T_THROW)
return parseThrowExpression(node);
+ else if (LA() == T_CO_YIELD)
+ return parseYieldExpression(node);
else
PARSE_EXPRESSION_WITH_OPERATOR_PRECEDENCE(node, Prec::Assignment)
}
@@ -5618,7 +5963,7 @@ bool Parser::parseQtMethod(ExpressionAST *&node)
QtMethodAST *ast = new (_pool) QtMethodAST;
ast->method_token = consumeToken();
match(T_LPAREN, &ast->lparen_token);
- if (! parseDeclarator(ast->declarator, /*decl_specifier_seq =*/ 0))
+ if (! parseDeclarator(ast->declarator, /*decl_specifier_seq =*/ nullptr))
error(cursor(), "expected a function declarator before token `%s'", tok().spell());
match(T_RPAREN, &ast->rparen_token);
node = ast;
@@ -5637,7 +5982,7 @@ bool Parser::parseExpression(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
CHECK_CACHE(ASTCache::Expression, ExpressionAST);
- unsigned initialCursor = cursor();
+ int initialCursor = cursor();
if (_expressionDepth > MAX_EXPRESSION_DEPTH)
return false;
@@ -5654,7 +5999,7 @@ void Parser::parseExpressionWithOperatorPrecedence(ExpressionAST *&lhs, int minP
{
DEBUG_THIS_RULE();
- unsigned iterations = 0;
+ int iterations = 0;
while (precedence(tok().kind(), _templateArguments) >= minPrecedence) {
if (++iterations > MAX_EXPRESSION_DEPTH) {
warning(cursor(), "Reached parse limit for expression");
@@ -5664,21 +6009,21 @@ void Parser::parseExpressionWithOperatorPrecedence(ExpressionAST *&lhs, int minP
const int operPrecedence = precedence(tok().kind(), _templateArguments);
const int oper = consumeToken();
- ConditionalExpressionAST *condExpr = 0;
+ ConditionalExpressionAST *condExpr = nullptr;
if (operPrecedence == Prec::Conditional) {
condExpr = new (_pool) ConditionalExpressionAST;
condExpr->question_token = oper;
if (tok().kind() == T_COLON) {
// GNU extension:
// logical-or-expression '?' ':' conditional-expression
- condExpr->left_expression = 0;
+ condExpr->left_expression = nullptr;
} else {
parseExpression(condExpr->left_expression);
}
match(T_COLON, &condExpr->colon_token);
}
- ExpressionAST *rhs = 0;
+ ExpressionAST *rhs = nullptr;
const bool isCPlusPlus = true;
if (operPrecedence <= Prec::Conditional && isCPlusPlus) {
// in C++ you can put a throw in the right-most expression of a conditional expression,
@@ -5735,6 +6080,34 @@ bool Parser::parseThrowExpression(ExpressionAST *&node)
return false;
}
+bool Parser::parseYieldExpression(ExpressionAST *&node)
+{
+ DEBUG_THIS_RULE();
+ if (LA() != T_CO_YIELD)
+ return false;
+ const auto ast = new (_pool) YieldExpressionAST;
+ ast->yield_token = consumeToken();
+ if (parseBracedInitList0x(ast->expression) || parseAssignmentExpression(ast->expression)) {
+ node = ast;
+ return true;
+ }
+ return false;
+}
+
+bool Parser::parseAwaitExpression(ExpressionAST *&node)
+{
+ DEBUG_THIS_RULE();
+ if (LA() != T_CO_AWAIT)
+ return false;
+ const auto ast = new (_pool) AwaitExpressionAST;
+ ast->await_token = consumeToken();
+ if (parseCastExpression(ast->castExpression)) {
+ node = ast;
+ return true;
+ }
+ return false;
+}
+
bool Parser::parseNoExceptOperatorExpression(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
@@ -5748,6 +6121,11 @@ bool Parser::parseNoExceptOperatorExpression(ExpressionAST *&node)
return false;
}
+bool Parser::lookAtStdAttribute() const
+{
+ return _languageFeatures.cxx11Enabled && LA() == T_LBRACKET && LA(2) == T_LBRACKET;
+}
+
bool Parser::lookAtObjCSelector() const
{
switch (LA()) {
@@ -5783,8 +6161,8 @@ bool Parser::parseDesignatedInitializer(ExpressionAST *&node)
DesignatedInitializerAST *ast = new (_pool) DesignatedInitializerAST;
DesignatorListAST **designator_list_ptr = &ast->designator_list;
- DesignatorAST *designator = 0;
- const unsigned start = cursor();
+ DesignatorAST *designator = nullptr;
+ const int start = cursor();
while (parseDesignator(designator)) {
*designator_list_ptr = new (_pool) DesignatorListAST;
(*designator_list_ptr)->value = designator;
@@ -5817,7 +6195,7 @@ bool Parser::parseDesignatedInitializer(ExpressionAST *&node)
bool Parser::parseDesignator(DesignatorAST *&node)
{
DEBUG_THIS_RULE();
- const unsigned start = cursor();
+ const int start = cursor();
if (LA() == T_DOT) {
DotDesignatorAST *ast = new (_pool) DotDesignatorAST;
ast->dot_token = consumeToken();
@@ -5849,7 +6227,7 @@ bool Parser::parseObjCClassForwardDeclaration(DeclarationAST *&node)
ObjCClassForwardDeclarationAST *ast = new (_pool) ObjCClassForwardDeclarationAST;
ast->class_token = consumeToken();
- unsigned identifier_token = 0;
+ int identifier_token = 0;
match(T_IDENTIFIER, &identifier_token);
ast->identifier_list = new (_pool) NameListAST;
@@ -5902,8 +6280,8 @@ bool Parser::parseObjCInterface(DeclarationAST *&node,
if (LA() != T_AT_INTERFACE)
return false;
- unsigned objc_interface_token = consumeToken();
- unsigned identifier_token = 0;
+ int objc_interface_token = consumeToken();
+ int identifier_token = 0;
match(T_IDENTIFIER, &identifier_token);
if (LA() == T_LPAREN) {
@@ -5931,7 +6309,7 @@ bool Parser::parseObjCInterface(DeclarationAST *&node,
parseObjCProtocolRefs(ast->protocol_refs);
DeclarationListAST **nextMembers = &ast->member_declaration_list;
- DeclarationAST *declaration = 0;
+ DeclarationAST *declaration = nullptr;
while (parseObjCInterfaceMemberDeclaration(declaration)) {
*nextMembers = new (_pool) DeclarationListAST;
(*nextMembers)->value = declaration;
@@ -5962,7 +6340,7 @@ bool Parser::parseObjCInterface(DeclarationAST *&node,
parseObjClassInstanceVariables(ast->inst_vars_decl);
DeclarationListAST **nextMembers = &ast->member_declaration_list;
- DeclarationAST *declaration = 0;
+ DeclarationAST *declaration = nullptr;
while (parseObjCInterfaceMemberDeclaration(declaration)) {
*nextMembers = new (_pool) DeclarationListAST;
(*nextMembers)->value = declaration;
@@ -5991,8 +6369,8 @@ bool Parser::parseObjCProtocol(DeclarationAST *&node,
if (LA() != T_AT_PROTOCOL)
return false;
- unsigned protocol_token = consumeToken();
- unsigned identifier_token = 0;
+ int protocol_token = consumeToken();
+ int identifier_token = 0;
match(T_IDENTIFIER, &identifier_token);
if (LA() == T_COMMA || LA() == T_SEMICOLON) {
@@ -6033,7 +6411,7 @@ bool Parser::parseObjCProtocol(DeclarationAST *&node,
parseObjCProtocolRefs(ast->protocol_refs);
DeclarationListAST **nextMembers = &ast->member_declaration_list;
- DeclarationAST *declaration = 0;
+ DeclarationAST *declaration = nullptr;
while (parseObjCInterfaceMemberDeclaration(declaration)) {
*nextMembers = new (_pool) DeclarationListAST;
(*nextMembers)->value = declaration;
@@ -6057,8 +6435,8 @@ bool Parser::parseObjCImplementation(DeclarationAST *&node)
if (LA() != T_AT_IMPLEMENTATION)
return false;
- unsigned implementation_token = consumeToken();
- unsigned identifier_token = 0;
+ int implementation_token = consumeToken();
+ int identifier_token = 0;
match(T_IDENTIFIER, &identifier_token);
if (LA() == T_LPAREN) {
@@ -6110,8 +6488,8 @@ bool Parser::parseObjCMethodDefinitionList(DeclarationListAST *&node)
DeclarationListAST **next = &node;
while (LA() && LA() != T_AT_END) {
- unsigned start = cursor();
- DeclarationAST *declaration = 0;
+ int start = cursor();
+ DeclarationAST *declaration = nullptr;
switch (LA()) {
case T_PLUS:
@@ -6213,7 +6591,7 @@ bool Parser::parseObjCMethodDefinitionList(DeclarationListAST *&node)
bool Parser::parseObjCMethodDefinition(DeclarationAST *&node)
{
DEBUG_THIS_RULE();
- ObjCMethodPrototypeAST *method_prototype = 0;
+ ObjCMethodPrototypeAST *method_prototype = nullptr;
if (! parseObjCMethodPrototype(method_prototype))
return false;
@@ -6245,7 +6623,7 @@ bool Parser::parseObjCProtocolRefs(ObjCProtocolRefsAST *&node)
match(T_LESS, &ast->less_token);
- unsigned identifier_token = 0;
+ int identifier_token = 0;
match(T_IDENTIFIER, &identifier_token);
ast->identifier_list = new (_pool) NameListAST;
SimpleNameAST *name = new (_pool) SimpleNameAST;
@@ -6286,7 +6664,7 @@ bool Parser::parseObjClassInstanceVariables(ObjCInstanceVariablesDeclarationAST
if (LA() == T_RBRACE)
break;
- const unsigned start = cursor();
+ const int start = cursor();
*next = new (_pool) DeclarationListAST;
parseObjCInstanceVariableDeclaration((*next)->value);
@@ -6393,7 +6771,7 @@ bool Parser::parseObjCPropertyDeclaration(DeclarationAST *&node, SpecifierListAS
if (LA() == T_LPAREN) {
match(T_LPAREN, &ast->lparen_token);
- ObjCPropertyAttributeAST *property_attribute = 0;
+ ObjCPropertyAttributeAST *property_attribute = nullptr;
if (parseObjCPropertyAttribute(property_attribute)) {
ast->property_attribute_list = new (_pool) ObjCPropertyAttributeListAST;
ast->property_attribute_list->value = property_attribute;
@@ -6439,8 +6817,8 @@ bool Parser::parseObjCMethodPrototype(ObjCMethodPrototypeAST *&node)
parseObjCTypeName(ast->type_name);
if ((lookAtObjCSelector() && LA(2) == T_COLON) || LA() == T_COLON) {
- ObjCSelectorArgumentAST *argument = 0;
- ObjCMessageArgumentDeclarationAST *declaration = 0;
+ ObjCSelectorArgumentAST *argument = nullptr;
+ ObjCMessageArgumentDeclarationAST *declaration = nullptr;
parseObjCKeywordDeclaration(argument, declaration);
ObjCSelectorAST *sel = new (_pool) ObjCSelectorAST;
@@ -6472,7 +6850,7 @@ bool Parser::parseObjCMethodPrototype(ObjCMethodPrototypeAST *&node)
}
// TODO: Is this still valid, and if so, should it be stored in the AST? (EV)
- ParameterDeclarationAST *parameter_declaration = 0;
+ ParameterDeclarationAST *parameter_declaration = nullptr;
parseParameterDeclaration(parameter_declaration);
}
} else if (lookAtObjCSelector()) {
@@ -6568,7 +6946,7 @@ bool Parser::parseObjCTypeName(ObjCTypeNameAST *&node)
// objc-selector ::= T_IDENTIFIER | keyword
//
-bool Parser::parseObjCSelector(unsigned &selector_token)
+bool Parser::parseObjCSelector(int &selector_token)
{
DEBUG_THIS_RULE();
if (! lookAtObjCSelector())
@@ -6605,7 +6983,7 @@ bool Parser::parseObjCKeywordDeclaration(ObjCSelectorArgumentAST *&argument, Obj
return true;
}
-bool Parser::parseObjCTypeQualifiers(unsigned &type_qualifier)
+bool Parser::parseObjCTypeQualifiers(int &type_qualifier)
{
DEBUG_THIS_RULE();
if (LA() != T_IDENTIFIER)
@@ -6636,7 +7014,7 @@ bool Parser::peekAtObjCContextKeyword(int kind)
return k == kind;
}
-bool Parser::parseObjCContextKeyword(int kind, unsigned &in_token)
+bool Parser::parseObjCContextKeyword(int kind, int &in_token)
{
DEBUG_THIS_RULE();
@@ -6661,10 +7039,19 @@ bool Parser::parseLambdaExpression(ExpressionAST *&node)
{
DEBUG_THIS_RULE();
- LambdaIntroducerAST *lambda_introducer = 0;
+ LambdaIntroducerAST *lambda_introducer = nullptr;
if (parseLambdaIntroducer(lambda_introducer)) {
LambdaExpressionAST *ast = new (_pool) LambdaExpressionAST;
ast->lambda_introducer = lambda_introducer;
+ if (_languageFeatures.cxx20Enabled && LA() == T_LESS) {
+ consumeToken();
+ parseTemplateParameterList(ast->templateParameters);
+ if (LA() != T_GREATER)
+ return false;
+ consumeToken();
+ parseRequiresClauseOpt(ast->requiresClause);
+ }
+ parseOptionalAttributeSpecifierSequence(ast->attributes);
parseLambdaDeclarator(ast->lambda_declarator);
parseCompoundStatement(ast->statement);
node = ast;
@@ -6689,7 +7076,9 @@ bool Parser::parseLambdaIntroducer(LambdaIntroducerAST *&node)
if (LA() == T_RBRACKET) {
ast->rbracket_token = consumeToken();
- if (LA() == T_LPAREN || LA() == T_LBRACE) {
+ // FIXME: Attributes are also allowed ...
+ if (LA() == T_LPAREN || LA() == T_LBRACE
+ || (_languageFeatures.cxx20Enabled && LA() == T_LESS)) {
node = ast;
return true;
}
@@ -6703,8 +7092,8 @@ bool Parser::parseLambdaCapture(LambdaCaptureAST *&node)
DEBUG_THIS_RULE();
bool startsWithDefaultCapture = false;
- unsigned default_capture = 0;
- CaptureListAST *capture_list = 0;
+ int default_capture = 0;
+ CaptureListAST *capture_list = nullptr;
if (LA() == T_AMPER || LA() == T_EQUAL) {
if (LA(2) == T_COMMA || LA(2) == T_RBRACKET) {
@@ -6758,7 +7147,7 @@ bool Parser::parseCaptureList(CaptureListAST *&node)
{
DEBUG_THIS_RULE();
- CaptureAST *capture = 0;
+ CaptureAST *capture = nullptr;
if (parseCapture(capture)) {
node = new (_pool) CaptureListAST;
@@ -6767,7 +7156,7 @@ bool Parser::parseCaptureList(CaptureListAST *&node)
CaptureListAST **l = &node->next;
while (LA() == T_COMMA) {
consumeToken(); // consume `,'
- CaptureAST *capture = 0;
+ CaptureAST *capture = nullptr;
parseCapture(capture);
if (capture) {
*l = new (_pool) CaptureListAST;
@@ -6797,12 +7186,15 @@ bool Parser::parseLambdaDeclarator(LambdaDeclaratorAST *&node)
SpecifierListAST **attr = &ast->attributes;
while (parseGnuAttributeSpecifier(*attr))
attr = &(*attr)->next;
+ while (parseStdAttributeSpecifier(*attr))
+ attr = &(*attr)->next;
if (LA() == T_MUTABLE)
ast->mutable_token = consumeToken();
parseExceptionSpecification(ast->exception_specification);
parseTrailingReturnType(ast->trailing_return_type);
+ parseRequiresClauseOpt(ast->requiresClause);
node = ast;
return true;
@@ -6821,6 +7213,8 @@ bool Parser::parseTrailingReturnType(TrailingReturnTypeAST *&node)
SpecifierListAST **attr = &ast->attributes;
while (parseGnuAttributeSpecifier(*attr))
attr = &(*attr)->next;
+ while (parseStdAttributeSpecifier(*attr))
+ attr = &(*attr)->next;
parseTrailingTypeSpecifierSeq(ast->type_specifier_list);
parseAbstractDeclarator(ast->declarator, ast->type_specifier_list);
@@ -6828,21 +7222,21 @@ bool Parser::parseTrailingReturnType(TrailingReturnTypeAST *&node)
return true;
}
-void Parser::rewind(unsigned cursor)
+void Parser::rewind(int cursor)
{
#ifndef CPLUSPLUS_NO_DEBUG_RULE
if (cursor != _tokenIndex)
fprintf(stderr, "! rewinding from token %d to token %d\n", _tokenIndex, cursor);
#endif
- const unsigned n = _translationUnit->tokenCount();
+ const int n = _translationUnit->tokenCount();
if (cursor < n)
_tokenIndex = cursor;
else
_tokenIndex = n - 1;
}
-void Parser::warning(unsigned index, const char *format, ...)
+void Parser::warning(int index, const char *format, ...)
{
va_list args, ap;
va_start(args, format);
@@ -6852,7 +7246,7 @@ void Parser::warning(unsigned index, const char *format, ...)
va_end(args);
}
-void Parser::error(unsigned index, const char *format, ...)
+void Parser::error(int index, const char *format, ...)
{
va_list args, ap;
va_start(args, format);
@@ -6862,7 +7256,7 @@ void Parser::error(unsigned index, const char *format, ...)
va_end(args);
}
-void Parser::fatal(unsigned index, const char *format, ...)
+void Parser::fatal(int index, const char *format, ...)
{
va_list args, ap;
va_start(args, format);
diff --git a/src/libs/3rdparty/cplusplus/Parser.h b/src/libs/3rdparty/cplusplus/Parser.h
index 110bb29d2b..e34a4ff522 100644
--- a/src/libs/3rdparty/cplusplus/Parser.h
+++ b/src/libs/3rdparty/cplusplus/Parser.h
@@ -48,8 +48,6 @@ public:
bool parseQtEnumDeclaration(DeclarationAST *&node);
bool parseQtFlags(DeclarationAST *&node);
bool parseQtInterfaces(DeclarationAST *&node);
- bool parseAdditiveExpression(ExpressionAST *&node);
- bool parseAndExpression(ExpressionAST *&node);
bool parseAsmDefinition(DeclarationAST *&node);
bool parseAsmOperandList();
bool parseAsmOperand();
@@ -72,23 +70,22 @@ public:
bool parseConstantExpression(ExpressionAST *&node);
bool parseCtorInitializer(CtorInitializerAST *&node);
bool parseCvQualifiers(SpecifierListAST *&node);
- bool parseRefQualifier(unsigned &ref_qualifier);
+ bool parseRefQualifier(int &ref_qualifier);
bool parseOverrideFinalQualifiers(SpecifierListAST *&node);
bool parseDeclaratorOrAbstractDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list);
bool parseDeclaration(DeclarationAST *&node);
- bool parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *declaringClass = 0);
+ bool parseSimpleDeclaration(DeclarationAST *&node, ClassSpecifierAST *declaringClass = nullptr);
bool parseDeclarationStatement(StatementAST *&node);
bool parseCoreDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *declaringClass);
- bool parseDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *declaringClass = 0);
+ DecompositionDeclaratorAST *parseDecompositionDeclarator(SpecifierListAST *decl_specifier_list);
+ bool parseDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *declaringClass = nullptr);
bool parseDeleteExpression(ExpressionAST *&node);
bool parseDoStatement(StatementAST *&node);
bool parseElaboratedTypeSpecifier(SpecifierListAST *&node);
bool parseEnumSpecifier(SpecifierListAST *&node);
bool parseEnumerator(EnumeratorListAST *&node);
- bool parseEqualityExpression(ExpressionAST *&node);
bool parseExceptionDeclaration(ExceptionDeclarationAST *&node);
bool parseExceptionSpecification(ExceptionSpecificationAST *&node);
- bool parseExclusiveOrExpression(ExpressionAST *&node);
bool parseExpression(ExpressionAST *&node);
bool parseExpressionOrDeclarationStatement(StatementAST *&node);
bool parseExpressionStatement(StatementAST *&node);
@@ -97,21 +94,18 @@ public:
bool parseForStatement(StatementAST *&node);
bool parseFunctionBody(StatementAST *&node);
bool parseIfStatement(StatementAST *&node);
- bool parseInclusiveOrExpression(ExpressionAST *&node);
bool parseInitDeclarator(DeclaratorAST *&node, SpecifierListAST *decl_specifier_list, ClassSpecifierAST *declaringClass);
bool parseInitializerList(ExpressionListAST *&node);
- bool parseInitializer(ExpressionAST *&node, unsigned *equals_token);
+ bool parseInitializer(ExpressionAST *&node, int *equals_token);
bool parseInitializerClause(ExpressionAST *&node);
bool parseLabeledStatement(StatementAST *&node);
bool parseLinkageBody(DeclarationAST *&node);
bool parseLinkageSpecification(DeclarationAST *&node);
- bool parseLogicalAndExpression(ExpressionAST *&node);
bool parseLogicalOrExpression(ExpressionAST *&node);
bool parseMemInitializer(MemInitializerListAST *&node);
bool parseMemInitializerList(MemInitializerListAST *&node);
bool parseMemberSpecification(DeclarationAST *&node, ClassSpecifierAST *declaringClass);
- bool parseMultiplicativeExpression(ExpressionAST *&node);
- bool parseTemplateId(NameAST *&node, unsigned template_token = 0);
+ bool parseTemplateId(NameAST *&node, int template_token = 0);
bool parseClassOrNamespaceName(NameAST *&node);
bool parseNameId(NameAST *&node);
bool parseName(NameAST *&node, bool acceptTemplateId = true);
@@ -132,7 +126,6 @@ public:
bool parseParameterDeclaration(ParameterDeclarationAST *&node);
bool parseParameterDeclarationClause(ParameterDeclarationClauseAST *&node);
bool parseParameterDeclarationList(ParameterDeclarationListAST *&node);
- bool parsePmExpression(ExpressionAST *&node);
bool parseTypeidExpression(ExpressionAST *&node);
bool parseTypenameCallExpression(ExpressionAST *&node);
bool parseCorePostfixExpression(ExpressionAST *&node);
@@ -140,8 +133,6 @@ public:
bool parsePrimaryExpression(ExpressionAST *&node);
bool parseNestedExpression(ExpressionAST *&node);
bool parsePtrOperator(PtrOperatorListAST *&node);
- bool parseRelationalExpression(ExpressionAST *&node);
- bool parseShiftExpression(ExpressionAST *&node);
bool parseStatement(StatementAST *&node, bool blockLabeledStatement = false);
bool parseThisExpression(ExpressionAST *&node);
bool parseBoolLiteral(ExpressionAST *&node);
@@ -152,9 +143,17 @@ public:
bool parseTemplateArgument(ExpressionAST *&node);
bool parseTemplateArgumentList(ExpressionListAST *&node);
bool parseTemplateDeclaration(DeclarationAST *&node);
+ bool parseConceptDeclaration(DeclarationAST *&node);
+ bool parsePlaceholderTypeSpecifier(PlaceholderTypeSpecifierAST *&node);
+ bool parseTypeConstraint(TypeConstraintAST *&node);
+ bool parseRequirement();
+ bool parseRequiresClauseOpt(RequiresClauseAST *&node);
+ bool parseRequiresExpression(ExpressionAST *&node);
bool parseTemplateParameter(DeclarationAST *&node);
bool parseTemplateParameterList(DeclarationListAST *&node);
bool parseThrowExpression(ExpressionAST *&node);
+ bool parseYieldExpression(ExpressionAST *&node);
+ bool parseAwaitExpression(ExpressionAST *&node);
bool parseNoExceptOperatorExpression(ExpressionAST *&node);
bool parseTryBlockStatement(StatementAST *&node, CtorInitializerAST **placeholder);
bool parseCatchClause(CatchClauseListAST *&node);
@@ -170,6 +169,9 @@ public:
bool parseGnuAttributeSpecifier(SpecifierListAST *&node);
bool parseGnuAttributeList(GnuAttributeListAST *&node);
+ bool parseMsvcDeclspecSpecifier(SpecifierListAST *&node);
+ bool parseStdAttributeSpecifier(SpecifierListAST *&node);
+
bool parseDeclSpecifierSeq(SpecifierListAST *&node,
bool noStorageSpecifiers = false,
bool onlySimpleTypeSpecifiers = false);
@@ -195,7 +197,7 @@ public:
bool parseQtMethod(ExpressionAST *&node);
// C++0x
- bool parseInitializer0x(ExpressionAST *&node, unsigned *equals_token);
+ bool parseInitializer0x(ExpressionAST *&node, int *equals_token);
bool parseBraceOrEqualInitializer0x(ExpressionAST *&node);
bool parseInitializerClause0x(ExpressionAST *&node);
bool parseInitializerList0x(ExpressionListAST *&node);
@@ -213,9 +215,9 @@ public:
bool parseObjCExpression(ExpressionAST *&node);
bool parseObjCClassForwardDeclaration(DeclarationAST *&node);
bool parseObjCInterface(DeclarationAST *&node,
- SpecifierListAST *attributes = 0);
+ SpecifierListAST *attributes = nullptr);
bool parseObjCProtocol(DeclarationAST *&node,
- SpecifierListAST *attributes = 0);
+ SpecifierListAST *attributes = nullptr);
bool parseObjCTryStatement(StatementAST *&node);
bool parseObjCSynchronizedStatement(StatementAST *&node);
@@ -236,16 +238,18 @@ public:
bool parseObjCInterfaceMemberDeclaration(DeclarationAST *&node);
bool parseObjCInstanceVariableDeclaration(DeclarationAST *&node);
bool parseObjCPropertyDeclaration(DeclarationAST *&node,
- SpecifierListAST *attributes = 0);
+ SpecifierListAST *attributes = nullptr);
bool parseObjCImplementation(DeclarationAST *&node);
bool parseObjCMethodPrototype(ObjCMethodPrototypeAST *&node);
bool parseObjCPropertyAttribute(ObjCPropertyAttributeAST *&node);
bool parseObjCTypeName(ObjCTypeNameAST *&node);
- bool parseObjCSelector(unsigned &selector_token);
+ bool parseObjCSelector(int &selector_token);
bool parseObjCKeywordDeclaration(ObjCSelectorArgumentAST *&argument, ObjCMessageArgumentDeclarationAST *&node);
- bool parseObjCTypeQualifiers(unsigned &type_qualifier);
+ bool parseObjCTypeQualifiers(int &type_qualifier);
bool peekAtObjCContextKeyword(int kind);
- bool parseObjCContextKeyword(int kind, unsigned &in_token);
+ bool parseObjCContextKeyword(int kind, int &in_token);
+
+ bool lookAtStdAttribute() const;
bool lookAtObjCSelector() const;
@@ -269,7 +273,7 @@ public:
const Identifier *className(ClassSpecifierAST *ast) const;
const Identifier *identifier(NameAST *name) const;
- void match(int kind, unsigned *token);
+ void match(int kind, int *token);
bool maybeAmbiguousStatement(DeclarationStatementAST *ast, StatementAST *&node);
bool maybeForwardOrClassDeclaration(SpecifierListAST *decl_specifier_seq) const;
@@ -280,9 +284,9 @@ public:
bool maybeSplitGreaterGreaterToken(int n = 1);
bool blockErrors(bool block) { return _translationUnit->blockErrors(block); }
- void warning(unsigned index, const char *format, ...);
- void error(unsigned index, const char *format, ...);
- void fatal(unsigned index, const char *format, ...);
+ void warning(int index, const char *format, ...);
+ void error(int index, const char *format, ...);
+ void fatal(int index, const char *format, ...);
inline const Token &tok(int i = 1) const
{ return _translationUnit->tokenAt(_tokenIndex + i - 1); }
@@ -293,29 +297,31 @@ public:
inline int consumeToken()
{ return _tokenIndex++; }
- inline unsigned cursor() const
+ inline int cursor() const
{ return _tokenIndex; }
- void rewind(unsigned cursor);
+ void rewind(int cursor);
struct TemplateArgumentListEntry {
- unsigned index;
- unsigned cursor;
+ int index;
+ int cursor;
ExpressionListAST *ast;
- TemplateArgumentListEntry(unsigned index = 0, unsigned cursor = 0, ExpressionListAST *ast = 0)
+ TemplateArgumentListEntry(int index = 0, int cursor = 0, ExpressionListAST *ast = nullptr)
: index(index), cursor(cursor), ast(ast) {}
};
- TemplateArgumentListEntry *templateArgumentListEntry(unsigned tokenIndex);
+ TemplateArgumentListEntry *templateArgumentListEntry(int tokenIndex);
void clearTemplateArgumentList() { _templateArgumentList.clear(); }
private:
+ bool hasAuto(SpecifierListAST *decl_specifier_list) const;
+
TranslationUnit *_translationUnit;
Control *_control;
MemoryPool *_pool;
LanguageFeatures _languageFeatures;
- unsigned _tokenIndex;
+ int _tokenIndex;
bool _templateArguments: 1;
bool _inFunctionBody: 1;
bool _inExpressionStatement: 1;
@@ -324,7 +330,7 @@ private:
std::stack<int> _initializerClauseDepth;
MemoryPool _expressionStatementTempPool;
- std::map<unsigned, TemplateArgumentListEntry> _templateArgumentList;
+ std::map<int, TemplateArgumentListEntry> _templateArgumentList;
class ASTCache;
ASTCache *_astCache;
diff --git a/src/libs/3rdparty/cplusplus/QtContextKeywords.cpp b/src/libs/3rdparty/cplusplus/QtContextKeywords.cpp
index ce7bd0a1dd..f903b2b9b2 100644
--- a/src/libs/3rdparty/cplusplus/QtContextKeywords.cpp
+++ b/src/libs/3rdparty/cplusplus/QtContextKeywords.cpp
@@ -159,6 +159,23 @@ static inline int classify8(const char *s) {
}
}
}
+ if (s[0] == 'B') {
+ if (s[1] == 'I') {
+ if (s[2] == 'N') {
+ if (s[3] == 'D') {
+ if (s[4] == 'A') {
+ if (s[5] == 'B') {
+ if (s[6] == 'L') {
+ if (s[7] == 'E') {
+ return Token_BINDABLE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
return Token_not_Qt_context_keyword;
}
diff --git a/src/libs/3rdparty/cplusplus/QtContextKeywords.h b/src/libs/3rdparty/cplusplus/QtContextKeywords.h
index 42dce9b917..f5d39a49cf 100644
--- a/src/libs/3rdparty/cplusplus/QtContextKeywords.h
+++ b/src/libs/3rdparty/cplusplus/QtContextKeywords.h
@@ -37,7 +37,8 @@ enum {
Token_DESIGNABLE,
Token_SCRIPTABLE,
Token_REVISION,
- Token_MEMBER
+ Token_MEMBER,
+ Token_BINDABLE
};
CPLUSPLUS_EXPORT int classifyQtContextKeyword(const char *s, int n);
diff --git a/src/libs/3rdparty/cplusplus/SafeMatcher.h b/src/libs/3rdparty/cplusplus/SafeMatcher.h
index 98fbbae2aa..9c6a64a3f3 100644
--- a/src/libs/3rdparty/cplusplus/SafeMatcher.h
+++ b/src/libs/3rdparty/cplusplus/SafeMatcher.h
@@ -37,17 +37,17 @@ public:
SafeMatcher();
~SafeMatcher();
- bool match(const PointerToMemberType *type, const PointerToMemberType *otherType);
- bool match(const PointerType *type, const PointerType *otherType);
- bool match(const ReferenceType *type, const ReferenceType *otherType);
- bool match(const ArrayType *type, const ArrayType *otherType);
- bool match(const NamedType *type, const NamedType *otherType);
-
- bool match(const TemplateNameId *name, const TemplateNameId *otherName);
- bool match(const DestructorNameId *name, const DestructorNameId *otherName);
- bool match(const ConversionNameId *name, const ConversionNameId *otherName);
- bool match(const QualifiedNameId *name, const QualifiedNameId *otherName);
- bool match(const SelectorNameId *name, const SelectorNameId *otherName);
+ bool match(const PointerToMemberType *type, const PointerToMemberType *otherType) override;
+ bool match(const PointerType *type, const PointerType *otherType) override;
+ bool match(const ReferenceType *type, const ReferenceType *otherType) override;
+ bool match(const ArrayType *type, const ArrayType *otherType) override;
+ bool match(const NamedType *type, const NamedType *otherType) override;
+
+ bool match(const TemplateNameId *name, const TemplateNameId *otherName) override;
+ bool match(const DestructorNameId *name, const DestructorNameId *otherName) override;
+ bool match(const ConversionNameId *name, const ConversionNameId *otherName) override;
+ bool match(const QualifiedNameId *name, const QualifiedNameId *otherName) override;
+ bool match(const SelectorNameId *name, const SelectorNameId *otherName) override;
private:
std::vector<const Type *> _blockedTypes;
diff --git a/src/libs/3rdparty/cplusplus/Scope.cpp b/src/libs/3rdparty/cplusplus/Scope.cpp
index 406a794c7e..644b690c75 100644
--- a/src/libs/3rdparty/cplusplus/Scope.cpp
+++ b/src/libs/3rdparty/cplusplus/Scope.cpp
@@ -19,7 +19,6 @@
// THE SOFTWARE.
#include "Scope.h"
-#include "Symbols.h"
#include "Names.h"
#include "Literals.h"
#include "Templates.h"
@@ -40,7 +39,7 @@ public:
public:
/// Constructs an empty Scope.
- SymbolTable(Scope *owner = 0);
+ SymbolTable(Scope *owner = nullptr);
/// Destroy this scope.
~SymbolTable();
@@ -55,22 +54,23 @@ public:
void enterSymbol(Symbol *symbol);
/// Returns true if this Scope is empty; otherwise returns false.
- bool isEmpty() const;
+ bool isEmpty() const { return _symbolCount == -1; }
/// Returns the number of symbols is in the scope.
- unsigned symbolCount() const;
+ int symbolCount() const { return _symbolCount + 1; }
/// Returns the Symbol at the given position.
- Symbol *symbolAt(unsigned index) const;
+ Symbol *symbolAt(int index) const;
/// Returns the first Symbol in the scope.
- iterator firstSymbol() const;
+ iterator firstSymbol() const { return _symbols; }
/// Returns the last Symbol in the scope.
- iterator lastSymbol() const;
+ iterator lastSymbol() const { return _symbols + _symbolCount + 1; }
Symbol *lookat(const Identifier *id) const;
Symbol *lookat(OperatorNameId::Kind operatorId) const;
+ Symbol *lookat(const ConversionNameId *convId) const;
private:
/// Returns the hash value for the given Symbol.
@@ -92,8 +92,8 @@ private:
SymbolTable::SymbolTable(Scope *owner)
: _owner(owner),
- _symbols(0),
- _hash(0),
+ _symbols(nullptr),
+ _hash(nullptr),
_allocatedSymbols(0),
_symbolCount(-1),
_hashSize(0)
@@ -136,7 +136,7 @@ void SymbolTable::enterSymbol(Symbol *symbol)
Symbol *SymbolTable::lookat(const Identifier *id) const
{
if (! _hash || ! id)
- return 0;
+ return nullptr;
const unsigned h = id->hashCode() % _hashSize;
Symbol *symbol = _hash[h];
@@ -153,8 +153,8 @@ Symbol *SymbolTable::lookat(const Identifier *id) const
} else if (const DestructorNameId *d = identity->asDestructorNameId()) {
if (d->identifier()->match(id))
break;
- } else if (identity->isQualifiedNameId()) {
- return 0;
+ } else if (identity->asQualifiedNameId()) {
+ return nullptr;
} else if (const SelectorNameId *selectorNameId = identity->asSelectorNameId()) {
if (selectorNameId->identifier()->match(id))
break;
@@ -166,7 +166,7 @@ Symbol *SymbolTable::lookat(const Identifier *id) const
Symbol *SymbolTable::lookat(OperatorNameId::Kind operatorId) const
{
if (! _hash)
- return 0;
+ return nullptr;
const unsigned h = operatorId % _hashSize;
Symbol *symbol = _hash[h];
@@ -181,6 +181,23 @@ Symbol *SymbolTable::lookat(OperatorNameId::Kind operatorId) const
return symbol;
}
+Symbol *SymbolTable::lookat(const ConversionNameId *convId) const
+{
+ if (!_hash)
+ return nullptr;
+
+ Symbol *symbol = _hash[0]; // See Symbol::HashCode
+ for (; symbol; symbol = symbol->_next) {
+ if (const Name *identity = symbol->unqualifiedName()) {
+ if (const ConversionNameId * const id = identity->asConversionNameId()) {
+ if (id->type().match(convId->type()))
+ break;
+ }
+ }
+ }
+ return symbol;
+}
+
void SymbolTable::rehash()
{
_hashSize <<= 1;
@@ -207,35 +224,23 @@ unsigned SymbolTable::hashValue(Symbol *symbol) const
return symbol->hashCode() % _hashSize;
}
-bool SymbolTable::isEmpty() const
-{ return _symbolCount == -1; }
-
-unsigned SymbolTable::symbolCount() const
-{ return _symbolCount + 1; }
-
-Symbol *SymbolTable::symbolAt(unsigned index) const
+Symbol *SymbolTable::symbolAt(int index) const
{
if (! _symbols)
- return 0;
+ return nullptr;
return _symbols[index];
}
-SymbolTable::iterator SymbolTable::firstSymbol() const
-{ return _symbols; }
-
-SymbolTable::iterator SymbolTable::lastSymbol() const
-{ return _symbols + _symbolCount + 1; }
-
-Scope::Scope(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+Scope::Scope(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name),
- _members(0),
+ _members(nullptr),
_startOffset(0),
_endOffset(0)
{ }
Scope::Scope(Clone *clone, Subst *subst, Scope *original)
: Symbol(clone, subst, original)
- , _members(0)
+ , _members(nullptr)
, _startOffset(original->_startOffset)
, _endOffset(original->_endOffset)
{
@@ -260,39 +265,28 @@ bool Scope::isEmpty() const
{ return _members ? _members->isEmpty() : true; }
/// Returns the number of symbols is in the scope.
-unsigned Scope::memberCount() const
+int Scope::memberCount() const
{ return _members ? _members->symbolCount() : 0; }
/// Returns the Symbol at the given position.
-Symbol *Scope::memberAt(unsigned index) const
-{ return _members ? _members->symbolAt(index) : 0; }
+Symbol *Scope::memberAt(int index) const
+{ return _members ? _members->symbolAt(index) : nullptr; }
/// Returns the first Symbol in the scope.
Scope::iterator Scope::memberBegin() const
-{ return _members ? _members->firstSymbol() : 0; }
+{ return _members ? _members->firstSymbol() : nullptr; }
/// Returns the last Symbol in the scope.
Scope::iterator Scope::memberEnd() const
-{ return _members ? _members->lastSymbol() : 0; }
+{ return _members ? _members->lastSymbol() : nullptr; }
Symbol *Scope::find(const Identifier *id) const
-{ return _members ? _members->lookat(id) : 0; }
+{ return _members ? _members->lookat(id) : nullptr; }
Symbol *Scope::find(OperatorNameId::Kind operatorId) const
-{ return _members ? _members->lookat(operatorId) : 0; }
-
-/// Set the start offset of the scope
-unsigned Scope::startOffset() const
-{ return _startOffset; }
-
-void Scope::setStartOffset(unsigned offset)
-{ _startOffset = offset; }
-
-/// Set the end offset of the scope
-unsigned Scope::endOffset() const
-{ return _endOffset; }
+{ return _members ? _members->lookat(operatorId) : nullptr; }
-void Scope::setEndOffset(unsigned offset)
-{ _endOffset = offset; }
+Symbol *Scope::find(const ConversionNameId *conv) const
+{ return _members ? _members->lookat(conv) : nullptr; }
} // namespace CPlusPlus
diff --git a/src/libs/3rdparty/cplusplus/Scope.h b/src/libs/3rdparty/cplusplus/Scope.h
index 01a5b7450d..8266e4c6fe 100644
--- a/src/libs/3rdparty/cplusplus/Scope.h
+++ b/src/libs/3rdparty/cplusplus/Scope.h
@@ -26,10 +26,10 @@
namespace CPlusPlus {
-class CPLUSPLUS_EXPORT Scope: public Symbol
+class CPLUSPLUS_EXPORT Scope : public Symbol
{
public:
- Scope(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ Scope(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
Scope(Clone *clone, Subst *subst, Scope *original);
virtual ~Scope();
@@ -40,10 +40,10 @@ public:
bool isEmpty() const;
/// Returns the number of symbols is in the scope.
- unsigned memberCount() const;
+ int memberCount() const;
/// Returns the Symbol at the given position.
- Symbol *memberAt(unsigned index) const;
+ Symbol *memberAt(int index) const;
typedef Symbol **iterator;
@@ -55,25 +55,25 @@ public:
Symbol *find(const Identifier *id) const;
Symbol *find(OperatorNameId::Kind operatorId) const;
+ Symbol *find(const ConversionNameId *conv) const;
/// Set the start offset of the scope
- unsigned startOffset() const;
- void setStartOffset(unsigned offset);
+ int startOffset() const { return _startOffset; }
+ /// Set the start offset of the scope
+ void setStartOffset(int offset) { _startOffset = offset; }
/// Set the end offset of the scope
- unsigned endOffset() const;
- void setEndOffset(unsigned offset);
-
- virtual const Scope *asScope() const
- { return this; }
+ int endOffset() const { return _endOffset; }
+ /// Set the end offset of the scope
+ void setEndOffset(int offset) { _endOffset = offset; }
- virtual Scope *asScope()
- { return this; }
+ const Scope *asScope() const override { return this; }
+ Scope *asScope() override { return this; }
private:
SymbolTable *_members;
- unsigned _startOffset;
- unsigned _endOffset;
+ int _startOffset;
+ int _endOffset;
};
} // namespace CPlusPlus
diff --git a/src/libs/3rdparty/cplusplus/Symbol.cpp b/src/libs/3rdparty/cplusplus/Symbol.cpp
index e9c7edb0dc..4ed2ab0c72 100644
--- a/src/libs/3rdparty/cplusplus/Symbol.cpp
+++ b/src/libs/3rdparty/cplusplus/Symbol.cpp
@@ -34,7 +34,9 @@
#include <utils/link.h>
-using namespace CPlusPlus;
+using namespace Utils;
+
+namespace CPlusPlus {
class Symbol::HashCode: protected NameVisitor
{
@@ -86,11 +88,11 @@ private:
unsigned _value;
};
-Symbol::Symbol(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
- : _name(0),
- _enclosingScope(0),
- _next(0),
- _fileId(0),
+Symbol::Symbol(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
+ : _name(nullptr),
+ _enclosingScope(nullptr),
+ _next(nullptr),
+ _fileId(nullptr),
_sourceLocation(0),
_hashCode(0),
_storage(Symbol::NoStorage),
@@ -108,8 +110,8 @@ Symbol::Symbol(TranslationUnit *translationUnit, unsigned sourceLocation, const
Symbol::Symbol(Clone *clone, Subst *subst, Symbol *original)
: _name(clone->name(original->_name, subst)),
- _enclosingScope(0),
- _next(0),
+ _enclosingScope(nullptr),
+ _next(nullptr),
_fileId(clone->control()->stringLiteral(original->fileName(), original->fileNameLength())),
_sourceLocation(original->_sourceLocation),
_hashCode(original->_hashCode),
@@ -142,25 +144,9 @@ void Symbol::visitSymbol(Symbol *symbol, SymbolVisitor *visitor)
symbol->visitSymbol(visitor);
}
-unsigned Symbol::sourceLocation() const
-{ return _sourceLocation; }
-
-bool Symbol::isGenerated() const
-{ return _isGenerated; }
-
-bool Symbol::isDeprecated() const
-{ return _isDeprecated; }
-
-void Symbol::setDeprecated(bool isDeprecated)
-{ _isDeprecated = isDeprecated; }
-bool Symbol::isUnavailable() const
-{ return _isUnavailable; }
-void Symbol::setUnavailable(bool isUnavailable)
-{ _isUnavailable = isUnavailable; }
-
-void Symbol::setSourceLocation(unsigned sourceLocation, TranslationUnit *translationUnit)
+void Symbol::setSourceLocation(int sourceLocation, TranslationUnit *translationUnit)
{
_sourceLocation = sourceLocation;
@@ -172,35 +158,26 @@ void Symbol::setSourceLocation(unsigned sourceLocation, TranslationUnit *transla
_isGenerated = false;
_line = 0;
_column = 0;
- _fileId = 0;
+ _fileId = nullptr;
}
}
-unsigned Symbol::line() const
-{
- return _line;
-}
-
-unsigned Symbol::column() const
-{
- return _column;
-}
-
-const StringLiteral *Symbol::fileId() const
-{
- return _fileId;
-}
-
const char *Symbol::fileName() const
{ return _fileId ? _fileId->chars() : ""; }
-unsigned Symbol::fileNameLength() const
+int Symbol::fileNameLength() const
{ return _fileId ? _fileId->size() : 0; }
+Utils::FilePath Symbol::filePath() const
+{
+ return _fileId ? Utils::FilePath::fromUtf8(_fileId->chars(), _fileId->size())
+ : Utils::FilePath();
+}
+
const Name *Symbol::unqualifiedName() const
{
if (! _name)
- return 0;
+ return nullptr;
else if (const QualifiedNameId *q = _name->asQualifiedNameId())
return q->name();
@@ -208,9 +185,6 @@ const Name *Symbol::unqualifiedName() const
return _name;
}
-const Name *Symbol::name() const
-{ return _name; }
-
void Symbol::setName(const Name *name)
{
_name = name;
@@ -228,12 +202,9 @@ const Identifier *Symbol::identifier() const
if (_name)
return _name->identifier();
- return 0;
+ return nullptr;
}
-Scope *Symbol::enclosingScope() const
-{ return _enclosingScope; }
-
void Symbol::setEnclosingScope(Scope *scope)
{
CPP_CHECK(! _enclosingScope);
@@ -242,7 +213,7 @@ void Symbol::setEnclosingScope(Scope *scope)
void Symbol::resetEnclosingScope()
{
- _enclosingScope = 0;
+ _enclosingScope = nullptr;
}
Namespace *Symbol::enclosingNamespace() const
@@ -251,7 +222,7 @@ Namespace *Symbol::enclosingNamespace() const
if (Namespace *ns = s->asNamespace())
return ns;
}
- return 0;
+ return nullptr;
}
Template *Symbol::enclosingTemplate() const
@@ -260,7 +231,7 @@ Template *Symbol::enclosingTemplate() const
if (Template *templ = s->asTemplate())
return templ;
}
- return 0;
+ return nullptr;
}
Class *Symbol::enclosingClass() const
@@ -269,7 +240,7 @@ Class *Symbol::enclosingClass() const
if (Class *klass = s->asClass())
return klass;
}
- return 0;
+ return nullptr;
}
Enum *Symbol::enclosingEnum() const
@@ -278,7 +249,7 @@ Enum *Symbol::enclosingEnum() const
if (Enum *e = s->asEnum())
return e;
}
- return 0;
+ return nullptr;
}
Function *Symbol::enclosingFunction() const
@@ -287,7 +258,7 @@ Function *Symbol::enclosingFunction() const
if (Function *fun = s->asFunction())
return fun;
}
- return 0;
+ return nullptr;
}
Block *Symbol::enclosingBlock() const
@@ -296,129 +267,9 @@ Block *Symbol::enclosingBlock() const
if (Block *block = s->asBlock())
return block;
}
- return 0;
+ return nullptr;
}
-unsigned Symbol::index() const
-{ return _index; }
-
-Symbol *Symbol::next() const
-{ return _next; }
-
-unsigned Symbol::hashCode() const
-{ return _hashCode; }
-
-int Symbol::storage() const
-{ return _storage; }
-
-void Symbol::setStorage(int storage)
-{ _storage = storage; }
-
-int Symbol::visibility() const
-{ return _visibility; }
-
-void Symbol::setVisibility(int visibility)
-{ _visibility = visibility; }
-
-bool Symbol::isFriend() const
-{ return _storage == Friend; }
-
-bool Symbol::isRegister() const
-{ return _storage == Register; }
-
-bool Symbol::isStatic() const
-{ return _storage == Static; }
-
-bool Symbol::isExtern() const
-{ return _storage == Extern; }
-
-bool Symbol::isMutable() const
-{ return _storage == Mutable; }
-
-bool Symbol::isTypedef() const
-{ return _storage == Typedef; }
-
-bool Symbol::isPublic() const
-{ return _visibility == Public; }
-
-bool Symbol::isProtected() const
-{ return _visibility == Protected; }
-
-bool Symbol::isPrivate() const
-{ return _visibility == Private; }
-
-bool Symbol::isScope() const
-{ return asScope() != 0; }
-
-bool Symbol::isEnum() const
-{ return asEnum() != 0; }
-
-bool Symbol::isFunction() const
-{ return asFunction() != 0; }
-
-bool Symbol::isNamespace() const
-{ return asNamespace() != 0; }
-
-bool Symbol::isTemplate() const
-{ return asTemplate() != 0; }
-
-bool Symbol::isClass() const
-{ return asClass() != 0; }
-
-bool Symbol::isForwardClassDeclaration() const
-{ return asForwardClassDeclaration() != 0; }
-
-bool Symbol::isQtPropertyDeclaration() const
-{ return asQtPropertyDeclaration() != 0; }
-
-bool Symbol::isQtEnum() const
-{ return asQtEnum() != 0; }
-
-bool Symbol::isBlock() const
-{ return asBlock() != 0; }
-
-bool Symbol::isUsingNamespaceDirective() const
-{ return asUsingNamespaceDirective() != 0; }
-
-bool Symbol::isUsingDeclaration() const
-{ return asUsingDeclaration() != 0; }
-
-bool Symbol::isDeclaration() const
-{ return asDeclaration() != 0; }
-
-bool Symbol::isArgument() const
-{ return asArgument() != 0; }
-
-bool Symbol::isTypenameArgument() const
-{ return asTypenameArgument() != 0; }
-
-bool Symbol::isBaseClass() const
-{ return asBaseClass() != 0; }
-
-bool Symbol::isObjCBaseClass() const
-{ return asObjCBaseClass() != 0; }
-
-bool Symbol::isObjCBaseProtocol() const
-{ return asObjCBaseProtocol() != 0; }
-
-bool Symbol::isObjCClass() const
-{ return asObjCClass() != 0; }
-
-bool Symbol::isObjCForwardClassDeclaration() const
-{ return asObjCForwardClassDeclaration() != 0; }
-
-bool Symbol::isObjCProtocol() const
-{ return asObjCProtocol() != 0; }
-
-bool Symbol::isObjCForwardProtocolDeclaration() const
-{ return asObjCForwardProtocolDeclaration() != 0; }
-
-bool Symbol::isObjCMethod() const
-{ return asObjCMethod() != 0; }
-
-bool Symbol::isObjCPropertyDeclaration() const
-{ return asObjCPropertyDeclaration() != 0; }
-
void Symbol::copy(Symbol *other)
{
_sourceLocation = other->_sourceLocation;
@@ -437,12 +288,10 @@ void Symbol::copy(Symbol *other)
_isDeprecated = other->_isDeprecated;
}
-Utils::Link Symbol::toLink() const
+Link Symbol::toLink() const
{
- const QString filename = QString::fromUtf8(fileName(), static_cast<int>(fileNameLength()));
-
- int line = static_cast<int>(this->line());
- int column = static_cast<int>(this->column());
+ int line = this->line();
+ int column = this->column();
if (column)
--column;
@@ -450,5 +299,7 @@ Utils::Link Symbol::toLink() const
if (isGenerated())
column = 0;
- return Utils::Link(filename, line, column);
+ return Link(filePath(), line, column);
}
+
+} // CPlusPlus
diff --git a/src/libs/3rdparty/cplusplus/Symbol.h b/src/libs/3rdparty/cplusplus/Symbol.h
index e4c8eea152..dc07ced907 100644
--- a/src/libs/3rdparty/cplusplus/Symbol.h
+++ b/src/libs/3rdparty/cplusplus/Symbol.h
@@ -22,7 +22,10 @@
#include "CPlusPlusForwardDeclarations.h"
-namespace Utils { struct Link; }
+namespace Utils {
+class FilePath;
+class Link;
+} // Utils
namespace CPlusPlus {
@@ -54,32 +57,34 @@ public:
public:
/// Constructs a Symbol with the given source location, name and translation unit.
- Symbol(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ Symbol(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
Symbol(Clone *clone, Subst *subst, Symbol *original);
/// Destroy this Symbol.
virtual ~Symbol();
/// Returns this Symbol's source location.
- unsigned sourceLocation() const;
+ int sourceLocation() const { return _sourceLocation; }
- /// \returns this Symbol's line number. The line number is 1-based.
- unsigned line() const;
+ /// Returns this Symbol's line number. The line number is 1-based.
+ int line() const { return _line; }
- /// \returns this Symbol's column number. The column number is 1-based.
- unsigned column() const;
+ /// Returns this Symbol's column number. The column number is 1-based.
+ int column() const { return _column; }
/// Returns this Symbol's file name.
- const StringLiteral *fileId() const;
+ const StringLiteral *fileId() const { return _fileId; }
/// Returns this Symbol's file name.
const char *fileName() const;
/// Returns this Symbol's file name length.
- unsigned fileNameLength() const;
+ int fileNameLength() const;
+
+ Utils::FilePath filePath() const;
/// Returns this Symbol's name.
- const Name *name() const;
+ const Name *name() const { return _name; }
/// Sets this Symbol's name.
void setName(const Name *name); // ### dangerous
@@ -88,191 +93,167 @@ public:
const Identifier *identifier() const;
/// Returns this Symbol's storage class specifier.
- int storage() const;
+ int storage() const { return _storage; }
/// Sets this Symbol's storage class specifier.
- void setStorage(int storage);
+ void setStorage(int storage) { _storage = storage; }
/// Returns this Symbol's visibility.
- int visibility() const;
+ int visibility() const { return _visibility; }
/// Sets this Symbol's visibility.
- void setVisibility(int visibility);
+ void setVisibility(int visibility) { _visibility = visibility; }
/// Returns the next chained Symbol.
- Symbol *next() const;
+ Symbol *next() const { return _next; }
/// Returns true if this Symbol has friend storage specifier.
- bool isFriend() const;
+ bool isFriend() const { return _storage == Friend; }
/// Returns true if this Symbol has register storage specifier.
- bool isRegister() const;
+ bool isRegister() const { return _storage == Register; }
/// Returns true if this Symbol has static storage specifier.
- bool isStatic() const;
+ bool isStatic() const { return _storage == Static; }
/// Returns true if this Symbol has extern storage specifier.
- bool isExtern() const;
+ bool isExtern() const { return _storage == Extern; }
/// Returns true if this Symbol has mutable storage specifier.
- bool isMutable() const;
+ bool isMutable() const { return _storage == Mutable; }
/// Returns true if this Symbol has typedef storage specifier.
- bool isTypedef() const;
+ bool isTypedef() const { return _storage == Typedef; }
/// Returns true if this Symbol's visibility is public.
- bool isPublic() const;
+ bool isPublic() const { return _visibility == Public; }
/// Returns true if this Symbol's visibility is protected.
- bool isProtected() const;
+ bool isProtected() const { return _visibility == Protected; }
/// Returns true if this Symbol's visibility is private.
- bool isPrivate() const;
+ bool isPrivate() const { return _visibility == Private; }
+
+ Utils::Link toLink() const;
- /// Returns true if this Symbol is a Scope.
- bool isScope() const;
+ virtual const Scope *asScope() const { return nullptr; }
+ virtual const Enum *asEnum() const { return nullptr; }
+ virtual const Function *asFunction() const { return nullptr; }
+ virtual const Namespace *asNamespace() const { return nullptr; }
+ virtual const Template *asTemplate() const { return nullptr; }
+ virtual const NamespaceAlias *asNamespaceAlias() const { return nullptr; }
+ virtual const Class *asClass() const { return nullptr; }
+ virtual const Block *asBlock() const { return nullptr; }
+ virtual const UsingNamespaceDirective *asUsingNamespaceDirective() const { return nullptr; }
+ virtual const UsingDeclaration *asUsingDeclaration() const { return nullptr; }
+ virtual const Declaration *asDeclaration() const { return nullptr; }
+ virtual const Argument *asArgument() const { return nullptr; }
+ virtual const TypenameArgument *asTypenameArgument() const { return nullptr; }
+ virtual const BaseClass *asBaseClass() const { return nullptr; }
+ virtual const ForwardClassDeclaration *asForwardClassDeclaration() const { return nullptr; }
+ virtual const QtPropertyDeclaration *asQtPropertyDeclaration() const { return nullptr; }
+ virtual const QtEnum *asQtEnum() const { return nullptr; }
+ virtual const ObjCBaseClass *asObjCBaseClass() const { return nullptr; }
+ virtual const ObjCBaseProtocol *asObjCBaseProtocol() const { return nullptr; }
+ virtual const ObjCClass *asObjCClass() const { return nullptr; }
+ virtual const ObjCForwardClassDeclaration *asObjCForwardClassDeclaration() const { return nullptr; }
+ virtual const ObjCProtocol *asObjCProtocol() const { return nullptr; }
+ virtual const ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclaration() const { return nullptr; }
+ virtual const ObjCMethod *asObjCMethod() const { return nullptr; }
+ virtual const ObjCPropertyDeclaration *asObjCPropertyDeclaration() const { return nullptr; }
- /// Returns true if this Symbol is an Enum.
- bool isEnum() const;
+ /// Returns this Symbol as a Scope.
+ virtual Scope *asScope() { return nullptr; }
- /// Returns true if this Symbol is an Function.
- bool isFunction() const;
+ /// Returns this Symbol as an Enum.
+ virtual Enum *asEnum() { return nullptr; }
- /// Returns true if this Symbol is a Namespace.
- bool isNamespace() const;
+ /// Returns this Symbol as an Function.
+ virtual Function *asFunction() { return nullptr; }
- /// Returns true if this Symbol is a Template.
- bool isTemplate() const;
+ /// Returns this Symbol as a Namespace.
+ virtual Namespace *asNamespace() { return nullptr; }
- /// Returns true if this Symbol is a Class.
- bool isClass() const;
+ /// Returns this Symbol as a Template.
+ virtual Template *asTemplate() { return nullptr; }
- /// Returns true if this Symbol is a Block.
- bool isBlock() const;
+ virtual NamespaceAlias *asNamespaceAlias() { return nullptr; }
- /// Returns true if this Symbol is a UsingNamespaceDirective.
- bool isUsingNamespaceDirective() const;
+ /// Returns this Symbol as a Class.
+ virtual Class *asClass() { return nullptr; }
- /// Returns true if this Symbol is a UsingDeclaration.
- bool isUsingDeclaration() const;
+ /// Returns this Symbol as a Block.
+ virtual Block *asBlock() { return nullptr; }
- /// Returns true if this Symbol is a Declaration.
- bool isDeclaration() const;
+ /// Returns this Symbol as a UsingNamespaceDirective.
+ virtual UsingNamespaceDirective *asUsingNamespaceDirective() { return nullptr; }
- /// Returns true if this Symbol is an Argument.
- bool isArgument() const;
+ /// Returns this Symbol as a UsingDeclaration.
+ virtual UsingDeclaration *asUsingDeclaration() { return nullptr; }
- /// Returns true if this Symbol is a Typename argument.
- bool isTypenameArgument() const;
+ /// Returns this Symbol as a Declaration.
+ virtual Declaration *asDeclaration() { return nullptr; }
- /// Returns true if this Symbol is a BaseClass.
- bool isBaseClass() const;
+ /// Returns this Symbol as an Argument.
+ virtual Argument *asArgument() { return nullptr; }
- /// Returns true if this Symbol is a ForwardClassDeclaration.
- bool isForwardClassDeclaration() const;
+ /// Returns this Symbol as a Typename argument.
+ virtual TypenameArgument *asTypenameArgument() { return nullptr; }
- /// Returns true if this Symbol is a QtPropertyDeclaration.
- bool isQtPropertyDeclaration() const;
+ /// Returns this Symbol as a BaseClass.
+ virtual BaseClass *asBaseClass() { return nullptr; }
- /// Returns true if this Symbol is a QtEnum.
- bool isQtEnum() const;
+ /// Returns this Symbol as a ForwardClassDeclaration.
+ virtual ForwardClassDeclaration *asForwardClassDeclaration() { return nullptr; }
- bool isObjCBaseClass() const;
- bool isObjCBaseProtocol() const;
+ /// Returns this Symbol as a QtPropertyDeclaration.
+ virtual QtPropertyDeclaration *asQtPropertyDeclaration() { return nullptr; }
- /// Returns true if this Symbol is an Objective-C Class declaration.
- bool isObjCClass() const;
+ /// Returns this Symbol as a QtEnum.
+ virtual QtEnum *asQtEnum() { return nullptr; }
- /// Returns true if this Symbol is an Objective-C Class forward declaration.
- bool isObjCForwardClassDeclaration() const;
+ virtual ObjCBaseClass *asObjCBaseClass() { return nullptr; }
+ virtual ObjCBaseProtocol *asObjCBaseProtocol() { return nullptr; }
- /// Returns true if this Symbol is an Objective-C Protocol declaration.
- bool isObjCProtocol() const;
+ /// Returns this Symbol as an Objective-C Class declaration.
+ virtual ObjCClass *asObjCClass() { return nullptr; }
- /// Returns true if this Symbol is an Objective-C Protocol forward declaration.
- bool isObjCForwardProtocolDeclaration() const;
+ /// Returns this Symbol as an Objective-C Class forward declaration.
+ virtual ObjCForwardClassDeclaration *asObjCForwardClassDeclaration() { return nullptr; }
- /// Returns true if this Symbol is an Objective-C method declaration.
- bool isObjCMethod() const;
+ /// Returns this Symbol as an Objective-C Protocol declaration.
+ virtual ObjCProtocol *asObjCProtocol() { return nullptr; }
- /// Returns true if this Symbol is an Objective-C @property declaration.
- bool isObjCPropertyDeclaration() const;
+ /// Returns this Symbol as an Objective-C Protocol forward declaration.
+ virtual ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclaration() { return nullptr; }
- Utils::Link toLink() const;
+ /// Returns this Symbol as an Objective-C method declaration.
+ virtual ObjCMethod *asObjCMethod() { return nullptr; }
- virtual const Scope *asScope() const { return 0; }
- virtual const Enum *asEnum() const { return 0; }
- virtual const Function *asFunction() const { return 0; }
- virtual const Namespace *asNamespace() const { return 0; }
- virtual const Template *asTemplate() const { return 0; }
- virtual const NamespaceAlias *asNamespaceAlias() const { return 0; }
- virtual const Class *asClass() const { return 0; }
- virtual const Block *asBlock() const { return 0; }
- virtual const UsingNamespaceDirective *asUsingNamespaceDirective() const { return 0; }
- virtual const UsingDeclaration *asUsingDeclaration() const { return 0; }
- virtual const Declaration *asDeclaration() const { return 0; }
- virtual const Argument *asArgument() const { return 0; }
- virtual const TypenameArgument *asTypenameArgument() const { return 0; }
- virtual const BaseClass *asBaseClass() const { return 0; }
- virtual const ForwardClassDeclaration *asForwardClassDeclaration() const { return 0; }
- virtual const QtPropertyDeclaration *asQtPropertyDeclaration() const { return 0; }
- virtual const QtEnum *asQtEnum() const { return 0; }
- virtual const ObjCBaseClass *asObjCBaseClass() const { return 0; }
- virtual const ObjCBaseProtocol *asObjCBaseProtocol() const { return 0; }
- virtual const ObjCClass *asObjCClass() const { return 0; }
- virtual const ObjCForwardClassDeclaration *asObjCForwardClassDeclaration() const { return 0; }
- virtual const ObjCProtocol *asObjCProtocol() const { return 0; }
- virtual const ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclaration() const { return 0; }
- virtual const ObjCMethod *asObjCMethod() const { return 0; }
- virtual const ObjCPropertyDeclaration *asObjCPropertyDeclaration() const { return 0; }
-
- virtual Scope *asScope() { return 0; }
- virtual Enum *asEnum() { return 0; }
- virtual Function *asFunction() { return 0; }
- virtual Namespace *asNamespace() { return 0; }
- virtual Template *asTemplate() { return 0; }
- virtual NamespaceAlias *asNamespaceAlias() { return 0; }
- virtual Class *asClass() { return 0; }
- virtual Block *asBlock() { return 0; }
- virtual UsingNamespaceDirective *asUsingNamespaceDirective() { return 0; }
- virtual UsingDeclaration *asUsingDeclaration() { return 0; }
- virtual Declaration *asDeclaration() { return 0; }
- virtual Argument *asArgument() { return 0; }
- virtual TypenameArgument *asTypenameArgument() { return 0; }
- virtual BaseClass *asBaseClass() { return 0; }
- virtual ForwardClassDeclaration *asForwardClassDeclaration() { return 0; }
- virtual QtPropertyDeclaration *asQtPropertyDeclaration() { return 0; }
- virtual QtEnum *asQtEnum() { return 0; }
- virtual ObjCBaseClass *asObjCBaseClass() { return 0; }
- virtual ObjCBaseProtocol *asObjCBaseProtocol() { return 0; }
- virtual ObjCClass *asObjCClass() { return 0; }
- virtual ObjCForwardClassDeclaration *asObjCForwardClassDeclaration() { return 0; }
- virtual ObjCProtocol *asObjCProtocol() { return 0; }
- virtual ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclaration() { return 0; }
- virtual ObjCMethod *asObjCMethod() { return 0; }
- virtual ObjCPropertyDeclaration *asObjCPropertyDeclaration() { return 0; }
+ /// Returns this Symbol as an Objective-C @property declaration.
+ virtual ObjCPropertyDeclaration *asObjCPropertyDeclaration() { return nullptr; }
/// Returns this Symbol's type.
virtual FullySpecifiedType type() const = 0;
/// Returns this Symbol's hash value.
- unsigned hashCode() const;
+ unsigned hashCode() const { return _hashCode; }
/// Returns this Symbol's index.
- unsigned index() const;
+ unsigned index() const { return _index; }
const Name *unqualifiedName() const;
- bool isGenerated() const;
+ bool isGenerated() const { return _isGenerated; }
- bool isDeprecated() const;
- void setDeprecated(bool isDeprecated);
+ bool isDeprecated() const { return _isDeprecated; }
+ void setDeprecated(bool isDeprecated) { _isDeprecated = isDeprecated; }
- bool isUnavailable() const;
- void setUnavailable(bool isUnavailable);
+ bool isUnavailable() const { return _isUnavailable; }
+ void setUnavailable(bool isUnavailable) { _isUnavailable = isUnavailable; }
/// Returns this Symbol's eclosing scope.
- Scope *enclosingScope() const;
+ Scope *enclosingScope() const { return _enclosingScope; }
/// Returns the eclosing namespace scope.
Namespace *enclosingNamespace() const;
@@ -294,7 +275,7 @@ public:
void setEnclosingScope(Scope *enclosingScope); // ### make me private
void resetEnclosingScope(); // ### make me private
- void setSourceLocation(unsigned sourceLocation, TranslationUnit *translationUnit); // ### make me private
+ void setSourceLocation(int sourceLocation, TranslationUnit *translationUnit); // ### make me private
void visitSymbol(SymbolVisitor *visitor);
static void visitSymbol(Symbol *symbol, SymbolVisitor *visitor);
@@ -309,13 +290,13 @@ private:
Scope *_enclosingScope;
Symbol *_next;
const StringLiteral *_fileId;
- unsigned _sourceLocation;
+ int _sourceLocation;
unsigned _hashCode;
int _storage;
int _visibility;
- unsigned _index;
- unsigned _line;
- unsigned _column;
+ int _index;
+ int _line;
+ int _column;
bool _isGenerated: 1;
bool _isDeprecated: 1;
diff --git a/src/libs/3rdparty/cplusplus/Symbols.cpp b/src/libs/3rdparty/cplusplus/Symbols.cpp
index 8ce7cb8a73..05b5f59793 100644
--- a/src/libs/3rdparty/cplusplus/Symbols.cpp
+++ b/src/libs/3rdparty/cplusplus/Symbols.cpp
@@ -34,7 +34,7 @@
using namespace CPlusPlus;
UsingNamespaceDirective::UsingNamespaceDirective(TranslationUnit *translationUnit,
- unsigned sourceLocation, const Name *name)
+ int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name)
{ }
@@ -42,18 +42,16 @@ UsingNamespaceDirective::UsingNamespaceDirective(Clone *clone, Subst *subst, Usi
: Symbol(clone, subst, original)
{ }
-UsingNamespaceDirective::~UsingNamespaceDirective()
-{ }
-
FullySpecifiedType UsingNamespaceDirective::type() const
{ return FullySpecifiedType(); }
void UsingNamespaceDirective::visitSymbol0(SymbolVisitor *visitor)
{ visitor->visit(this); }
+
NamespaceAlias::NamespaceAlias(TranslationUnit *translationUnit,
- unsigned sourceLocation, const Name *name)
- : Symbol(translationUnit, sourceLocation, name), _namespaceName(0)
+ int sourceLocation, const Name *name)
+ : Symbol(translationUnit, sourceLocation, name), _namespaceName(nullptr)
{ }
NamespaceAlias::NamespaceAlias(Clone *clone, Subst *subst, NamespaceAlias *original)
@@ -61,15 +59,6 @@ NamespaceAlias::NamespaceAlias(Clone *clone, Subst *subst, NamespaceAlias *origi
, _namespaceName(clone->name(original->_namespaceName, subst))
{ }
-NamespaceAlias::~NamespaceAlias()
-{ }
-
-const Name *NamespaceAlias::namespaceName() const
-{ return _namespaceName; }
-
-void NamespaceAlias::setNamespaceName(const Name *namespaceName)
-{ _namespaceName = namespaceName; }
-
FullySpecifiedType NamespaceAlias::type() const
{ return FullySpecifiedType(); }
@@ -78,7 +67,7 @@ void NamespaceAlias::visitSymbol0(SymbolVisitor *visitor)
UsingDeclaration::UsingDeclaration(TranslationUnit *translationUnit,
- unsigned sourceLocation, const Name *name)
+ int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name)
{ }
@@ -86,18 +75,16 @@ UsingDeclaration::UsingDeclaration(Clone *clone, Subst *subst, UsingDeclaration
: Symbol(clone, subst, original)
{ }
-UsingDeclaration::~UsingDeclaration()
-{ }
-
FullySpecifiedType UsingDeclaration::type() const
{ return FullySpecifiedType(); }
-void UsingDeclaration::visitSymbol0(SymbolVisitor *visitor)
+void CPlusPlus::UsingDeclaration::visitSymbol0(SymbolVisitor *visitor)
{ visitor->visit(this); }
-Declaration::Declaration(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+
+Declaration::Declaration(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name)
- , _initializer(0)
+ , _initializer(nullptr)
{ }
Declaration::Declaration(Clone *clone, Subst *subst, Declaration *original)
@@ -158,7 +145,7 @@ Declaration::Declaration(Clone *clone, Subst *subst, Declaration *original)
std::strcmp(enNamespaceNameId, "__cxx11") == 0) {
if (std::strcmp(enClassNameId, "unique_ptr") == 0) {
if (std::strcmp(nameId, "pointer") == 0) {
- newType = clone->type(subst->apply(firstTemplParamName), 0);
+ newType = clone->type(subst->apply(firstTemplParamName), nullptr);
newType = FullySpecifiedType(clone->control()->pointerType(newType));
}
} else if (std::strcmp(enClassNameId, "list") == 0 ||
@@ -172,12 +159,12 @@ Declaration::Declaration(Clone *clone, Subst *subst, Declaration *original)
std::strcmp(enClassNameId, "array") == 0) {
if (std::strcmp(nameId, "reference") == 0 ||
std::strcmp(nameId, "const_reference") == 0) {
- newType = clone->type(subst->apply(firstTemplParamName), 0);
+ newType = clone->type(subst->apply(firstTemplParamName), nullptr);
} else if (std::strcmp(nameId, "iterator") == 0 ||
std::strcmp(nameId, "reverse_iterator") == 0 ||
std::strcmp(nameId, "const_reverse_iterator") == 0 ||
std::strcmp(nameId, "const_iterator") == 0) {
- newType = clone->type(subst->apply(firstTemplParamName), 0);
+ newType = clone->type(subst->apply(firstTemplParamName), nullptr);
newType = FullySpecifiedType(clone->control()->pointerType(newType));
}
} else if (std::strcmp(enClassNameId, "_Hash") == 0 ||
@@ -186,12 +173,12 @@ Declaration::Declaration(Clone *clone, Subst *subst, Declaration *original)
std::strcmp(nameId, "reverse_iterator") == 0 ||
std::strcmp(nameId, "const_reverse_iterator") == 0 ||
std::strcmp(nameId, "const_iterator") == 0) {
- FullySpecifiedType clonedType = clone->type(subst->apply(firstTemplParamName), 0);
+ FullySpecifiedType clonedType = clone->type(subst->apply(firstTemplParamName), nullptr);
if (NamedType *namedType = clonedType.type()->asNamedType()) {
if (const TemplateNameId * templateNameId =
namedType->name()->asTemplateNameId()) {
if (templateNameId->templateArgumentCount()) {
- newType = clone->type(templateNameId->templateArgumentAt(0), 0);
+ newType = clone->type(templateNameId->templateArgumentAt(0).type(), nullptr);
newType = FullySpecifiedType(clone->control()->pointerType(newType));
}
}
@@ -204,45 +191,19 @@ Declaration::Declaration(Clone *clone, Subst *subst, Declaration *original)
_type = newType;
}
-Declaration::~Declaration()
-{ }
-
-void Declaration::setType(const FullySpecifiedType &type)
-{ _type = type; }
-
-void Declaration::setInitializer(const StringLiteral *initializer)
-{
- _initializer = initializer;
-}
-
-FullySpecifiedType Declaration::type() const
-{ return _type; }
-
-const StringLiteral *Declaration::getInitializer() const
-{
- return _initializer;
-}
-
void Declaration::visitSymbol0(SymbolVisitor *visitor)
{ visitor->visit(this); }
-EnumeratorDeclaration::EnumeratorDeclaration(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
- : Declaration(translationUnit, sourceLocation, name)
- , _constantValue(0)
-{}
-EnumeratorDeclaration::~EnumeratorDeclaration()
+EnumeratorDeclaration::EnumeratorDeclaration(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
+ : Declaration(translationUnit, sourceLocation, name)
+ , _constantValue(nullptr)
{}
-const StringLiteral *EnumeratorDeclaration::constantValue() const
-{ return _constantValue; }
-
-void EnumeratorDeclaration::setConstantValue(const StringLiteral *constantValue)
-{ _constantValue = constantValue; }
-Argument::Argument(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+Argument::Argument(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name),
- _initializer(0)
+ _initializer(nullptr)
{ }
Argument::Argument(Clone *clone, Subst *subst, Argument *original)
@@ -251,28 +212,11 @@ Argument::Argument(Clone *clone, Subst *subst, Argument *original)
, _type(clone->type(original->_type, subst))
{ }
-Argument::~Argument()
-{ }
-
-bool Argument::hasInitializer() const
-{ return _initializer != 0; }
-
-const StringLiteral *Argument::initializer() const
-{ return _initializer; }
-
-void Argument::setInitializer(const StringLiteral *initializer)
-{ _initializer = initializer; }
-
-void Argument::setType(const FullySpecifiedType &type)
-{ _type = type; }
-
-FullySpecifiedType Argument::type() const
-{ return _type; }
-
void Argument::visitSymbol0(SymbolVisitor *visitor)
{ visitor->visit(this); }
-TypenameArgument::TypenameArgument(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+
+TypenameArgument::TypenameArgument(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name)
, _isClassDeclarator(false)
{ }
@@ -283,19 +227,11 @@ TypenameArgument::TypenameArgument(Clone *clone, Subst *subst, TypenameArgument
, _isClassDeclarator(original->_isClassDeclarator)
{ }
-TypenameArgument::~TypenameArgument()
-{ }
-
-void TypenameArgument::setType(const FullySpecifiedType &type)
-{ _type = type; }
-
-FullySpecifiedType TypenameArgument::type() const
-{ return _type; }
-
void TypenameArgument::visitSymbol0(SymbolVisitor *visitor)
{ visitor->visit(this); }
-Function::Function(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+
+Function::Function(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Scope(translationUnit, sourceLocation, name),
_flags(0)
{ }
@@ -303,30 +239,10 @@ Function::Function(TranslationUnit *translationUnit, unsigned sourceLocation, co
Function::Function(Clone *clone, Subst *subst, Function *original)
: Scope(clone, subst, original)
, _returnType(clone->type(original->_returnType, subst))
+ , _exceptionSpecification(original->_exceptionSpecification)
, _flags(original->_flags)
{ }
-Function::~Function()
-{ }
-
-bool Function::isNormal() const
-{ return f._methodKey == NormalMethod; }
-
-bool Function::isSignal() const
-{ return f._methodKey == SignalMethod; }
-
-bool Function::isSlot() const
-{ return f._methodKey == SlotMethod; }
-
-bool Function::isInvokable() const
-{ return f._methodKey == InvokableMethod; }
-
-int Function::methodKey() const
-{ return f._methodKey; }
-
-void Function::setMethodKey(int key)
-{ f._methodKey = key; }
-
bool Function::isSignatureEqualTo(const Function *other, Matcher *matcher) const
{
if (! other)
@@ -338,17 +254,39 @@ bool Function::isSignatureEqualTo(const Function *other, Matcher *matcher) const
else if (! Matcher::match(unqualifiedName(), other->unqualifiedName(), matcher))
return false;
- const unsigned argc = argumentCount();
+ class FallbackMatcher : public Matcher
+ {
+ public:
+ explicit FallbackMatcher(Matcher *baseMatcher) : m_baseMatcher(baseMatcher) {}
+
+ private:
+ bool match(const NamedType *type, const NamedType *otherType) override
+ {
+ if (type == otherType)
+ return true;
+ const Name *name = type->name();
+ if (const QualifiedNameId *q = name->asQualifiedNameId())
+ name = q->name();
+ const Name *otherName = otherType->name();
+ if (const QualifiedNameId *q = otherName->asQualifiedNameId())
+ otherName = q->name();
+ return Matcher::match(name, otherName, m_baseMatcher);
+ }
+
+ Matcher * const m_baseMatcher;
+ } fallbackMatcher(matcher);
+
+ const int argc = argumentCount();
if (argc != other->argumentCount())
return false;
- for (unsigned i = 0; i < argc; ++i) {
+ for (int i = 0; i < argc; ++i) {
Symbol *l = argumentAt(i);
Symbol *r = other->argumentAt(i);
if (! l->type().match(r->type(), matcher)) {
- if (!l->type()->isReferenceType() && !l->type()->isPointerType()
- && !l->type()->isPointerToMemberType()
- && !r->type()->isReferenceType() && !r->type()->isPointerType()
- && !r->type()->isPointerToMemberType()) {
+ if (!l->type()->asReferenceType() && !l->type()->asPointerType()
+ && !l->type()->asPointerToMemberType()
+ && !r->type()->asReferenceType() && !r->type()->asPointerType()
+ && !r->type()->asPointerToMemberType()) {
FullySpecifiedType lType = l->type();
FullySpecifiedType rType = r->type();
lType.setConst(false);
@@ -358,6 +296,8 @@ bool Function::isSignatureEqualTo(const Function *other, Matcher *matcher) const
if (lType.match(rType))
continue;
}
+ if (l->type().match(r->type(), &fallbackMatcher))
+ continue;
return false;
}
}
@@ -380,39 +320,34 @@ FullySpecifiedType Function::type() const
FullySpecifiedType ty(const_cast<Function *>(this));
ty.setConst(isConst());
ty.setVolatile(isVolatile());
+ ty.setStatic(isStatic());
return ty;
}
-FullySpecifiedType Function::returnType() const
-{ return _returnType; }
-
-void Function::setReturnType(const FullySpecifiedType &returnType)
-{ _returnType = returnType; }
-
bool Function::hasReturnType() const
{
const FullySpecifiedType ty = returnType();
return ty.isValid() || ty.isSigned() || ty.isUnsigned();
}
-unsigned Function::argumentCount() const
+int Function::argumentCount() const
{
- const unsigned memCnt = memberCount();
- if (memCnt > 0 && memberAt(0)->type()->isVoidType())
+ const int memCnt = memberCount();
+ if (memCnt > 0 && memberAt(0)->type()->asVoidType())
return 0;
// Definitions with function-try-blocks will have more than a block, and
// arguments with a lambda as default argument will also have more blocks.
- unsigned argc = 0;
- for (unsigned it = 0; it < memCnt; ++it)
- if (memberAt(it)->isArgument())
+ int argc = 0;
+ for (int it = 0; it < memCnt; ++it)
+ if (memberAt(it)->asArgument())
++argc;
return argc;
}
-Symbol *Function::argumentAt(unsigned index) const
+Symbol *Function::argumentAt(int index) const
{
- for (unsigned it = 0, eit = memberCount(); it < eit; ++it) {
+ for (int it = 0, eit = memberCount(); it < eit; ++it) {
if (Argument *arg = memberAt(it)->asArgument()) {
if (index == 0)
return arg;
@@ -421,20 +356,20 @@ Symbol *Function::argumentAt(unsigned index) const
}
}
- return 0;
+ return nullptr;
}
bool Function::hasArguments() const
{
- unsigned argc = argumentCount();
- return ! (argc == 0 || (argc == 1 && argumentAt(0)->type()->isVoidType()));
+ int argc = argumentCount();
+ return ! (argc == 0 || (argc == 1 && argumentAt(0)->type()->asVoidType()));
}
-unsigned Function::minimumArgumentCount() const
+int Function::minimumArgumentCount() const
{
- unsigned index = 0;
+ int index = 0;
- for (unsigned ei = argumentCount(); index < ei; ++index) {
+ for (int ei = argumentCount(); index < ei; ++index) {
if (Argument *arg = argumentAt(index)->asArgument()) {
if (arg->hasInitializer())
break;
@@ -444,73 +379,19 @@ unsigned Function::minimumArgumentCount() const
return index;
}
-bool Function::isVirtual() const
-{ return f._isVirtual; }
-
-void Function::setVirtual(bool isVirtual)
-{ f._isVirtual = isVirtual; }
-
-bool Function::isOverride() const
-{ return f._isOverride; }
-
-void Function::setOverride(bool isOverride)
-{ f._isOverride = isOverride; }
-
-bool Function::isFinal() const
-{ return f._isFinal; }
-
-void Function::setFinal(bool isFinal)
-{ f._isFinal = isFinal; }
-
-bool Function::isVariadic() const
-{ return f._isVariadic; }
-
-void Function::setVariadic(bool isVariadic)
-{ f._isVariadic = isVariadic; }
-
-bool Function::isConst() const
-{ return f._isConst; }
-
-void Function::setConst(bool isConst)
-{ f._isConst = isConst; }
-
-bool Function::isVolatile() const
-{ return f._isVolatile; }
-
-void Function::setVolatile(bool isVolatile)
-{ f._isVolatile = isVolatile; }
-
-bool Function::isPureVirtual() const
-{ return f._isPureVirtual; }
-
-void Function::setPureVirtual(bool isPureVirtual)
-{ f._isPureVirtual = isPureVirtual; }
-
-Function::RefQualifier Function::refQualifier() const
-{ return static_cast<RefQualifier>(f._refQualifier); }
-
-void Function::setRefQualifier(Function::RefQualifier refQualifier)
-{ f._refQualifier = refQualifier; }
-
-bool Function::isAmbiguous() const
-{ return f._isAmbiguous; }
-
-void Function::setAmbiguous(bool isAmbiguous)
-{ f._isAmbiguous = isAmbiguous; }
-
void Function::visitSymbol0(SymbolVisitor *visitor)
{
if (visitor->visit(this)) {
- for (unsigned i = 0; i < memberCount(); ++i) {
+ for (int i = 0; i < memberCount(); ++i) {
visitSymbol(memberAt(i), visitor);
}
}
}
-bool Function::maybeValidPrototype(unsigned actualArgumentCount) const
+bool Function::maybeValidPrototype(int actualArgumentCount) const
{
- const unsigned argc = argumentCount();
- unsigned minNumberArguments = 0;
+ const int argc = argumentCount();
+ int minNumberArguments = 0;
for (; minNumberArguments < argc; ++minNumberArguments) {
Argument *arg = argumentAt(minNumberArguments)->asArgument();
@@ -522,6 +403,9 @@ bool Function::maybeValidPrototype(unsigned actualArgumentCount) const
break;
}
+ if (isVariadicTemplate())
+ --minNumberArguments;
+
if (actualArgumentCount < minNumberArguments) {
// not enough arguments.
return false;
@@ -535,30 +419,27 @@ bool Function::maybeValidPrototype(unsigned actualArgumentCount) const
}
-Block::Block(TranslationUnit *translationUnit, unsigned sourceLocation)
- : Scope(translationUnit, sourceLocation, /*name = */ 0)
+Block::Block(TranslationUnit *translationUnit, int sourceLocation)
+ : Scope(translationUnit, sourceLocation, /*name = */ nullptr)
{ }
Block::Block(Clone *clone, Subst *subst, Block *original)
: Scope(clone, subst, original)
{ }
-Block::~Block()
-{ }
-
FullySpecifiedType Block::type() const
{ return FullySpecifiedType(); }
void Block::visitSymbol0(SymbolVisitor *visitor)
{
if (visitor->visit(this)) {
- for (unsigned i = 0; i < memberCount(); ++i) {
+ for (int i = 0; i < memberCount(); ++i) {
visitSymbol(memberAt(i), visitor);
}
}
}
-Enum::Enum(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+Enum::Enum(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Scope(translationUnit, sourceLocation, name)
, _isScoped(false)
{ }
@@ -568,21 +449,9 @@ Enum::Enum(Clone *clone, Subst *subst, Enum *original)
, _isScoped(original->isScoped())
{ }
-Enum::~Enum()
-{ }
-
FullySpecifiedType Enum::type() const
{ return FullySpecifiedType(const_cast<Enum *>(this)); }
-bool Enum::isScoped() const
-{
- return _isScoped;
-}
-
-void Enum::setScoped(bool scoped)
-{
- _isScoped = scoped;
-}
void Enum::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -598,13 +467,13 @@ bool Enum::match0(const Type *otherType, Matcher *matcher) const
void Enum::visitSymbol0(SymbolVisitor *visitor)
{
if (visitor->visit(this)) {
- for (unsigned i = 0; i < memberCount(); ++i) {
+ for (int i = 0; i < memberCount(); ++i) {
visitSymbol(memberAt(i), visitor);
}
}
}
-Template::Template(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+Template::Template(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Scope(translationUnit, sourceLocation, name)
{ }
@@ -612,32 +481,26 @@ Template::Template(Clone *clone, Subst *subst, Template *original)
: Scope(clone, subst, original)
{ }
-Template::~Template()
-{ }
-
-unsigned Template::templateParameterCount() const
+int Template::templateParameterCount() const
{
- if (declaration() != 0)
+ if (declaration() != nullptr)
return memberCount() - 1;
return 0;
}
-Symbol *Template::templateParameterAt(unsigned index) const
-{ return memberAt(index); }
-
Symbol *Template::declaration() const
{
if (isEmpty())
- return 0;
+ return nullptr;
if (Symbol *s = memberAt(memberCount() - 1)) {
- if (s->isClass() || s->isForwardClassDeclaration() ||
- s->isTemplate() || s->isFunction() || s->isDeclaration())
+ if (s->asClass() || s->asForwardClassDeclaration() ||
+ s->asTemplate() || s->asFunction() || s->asDeclaration())
return s;
}
- return 0;
+ return nullptr;
}
FullySpecifiedType Template::type() const
@@ -646,7 +509,7 @@ FullySpecifiedType Template::type() const
void Template::visitSymbol0(SymbolVisitor *visitor)
{
if (visitor->visit(this)) {
- for (unsigned i = 0; i < memberCount(); ++i) {
+ for (int i = 0; i < memberCount(); ++i) {
visitSymbol(memberAt(i), visitor);
}
}
@@ -662,7 +525,7 @@ bool Template::match0(const Type *otherType, Matcher *matcher) const
return false;
}
-Namespace::Namespace(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+Namespace::Namespace(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Scope(translationUnit, sourceLocation, name)
, _isInline(false)
{ }
@@ -672,9 +535,6 @@ Namespace::Namespace(Clone *clone, Subst *subst, Namespace *original)
, _isInline(original->_isInline)
{ }
-Namespace::~Namespace()
-{ }
-
void Namespace::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -689,7 +549,7 @@ bool Namespace::match0(const Type *otherType, Matcher *matcher) const
void Namespace::visitSymbol0(SymbolVisitor *visitor)
{
if (visitor->visit(this)) {
- for (unsigned i = 0; i < memberCount(); ++i) {
+ for (int i = 0; i < memberCount(); ++i) {
visitSymbol(memberAt(i), visitor);
}
}
@@ -698,7 +558,7 @@ void Namespace::visitSymbol0(SymbolVisitor *visitor)
FullySpecifiedType Namespace::type() const
{ return FullySpecifiedType(const_cast<Namespace *>(this)); }
-BaseClass::BaseClass(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+BaseClass::BaseClass(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name),
_isVirtual(false)
{ }
@@ -709,32 +569,12 @@ BaseClass::BaseClass(Clone *clone, Subst *subst, BaseClass *original)
, _type(clone->type(original->_type, subst))
{ }
-BaseClass::~BaseClass()
-{ }
-
-FullySpecifiedType BaseClass::type() const
-{ return _type; }
-
-void BaseClass::setType(const FullySpecifiedType &type)
-{ _type = type; }
-
-bool BaseClass::isVirtual() const
-{ return _isVirtual; }
-
-void BaseClass::setVirtual(bool isVirtual)
-{ _isVirtual = isVirtual; }
-
-bool BaseClass::isVariadic() const
-{ return _isVariadic; }
-
-void BaseClass::setVariadic(bool isVariadic)
-{ _isVariadic = isVariadic; }
-
void BaseClass::visitSymbol0(SymbolVisitor *visitor)
{ visitor->visit(this); }
+
ForwardClassDeclaration::ForwardClassDeclaration(TranslationUnit *translationUnit,
- unsigned sourceLocation, const Name *name)
+ int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name)
{ }
@@ -742,9 +582,6 @@ ForwardClassDeclaration::ForwardClassDeclaration(Clone *clone, Subst *subst, For
: Symbol(clone, subst, original)
{ }
-ForwardClassDeclaration::~ForwardClassDeclaration()
-{ }
-
FullySpecifiedType ForwardClassDeclaration::type() const
{ return FullySpecifiedType(const_cast<ForwardClassDeclaration *>(this)); }
@@ -762,7 +599,7 @@ bool ForwardClassDeclaration::match0(const Type *otherType, Matcher *matcher) co
return false;
}
-Class::Class(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+Class::Class(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Scope(translationUnit, sourceLocation, name),
_key(ClassKey)
{ }
@@ -775,24 +612,6 @@ Class::Class(Clone *clone, Subst *subst, Class *original)
addBaseClass(clone->symbol(original->_baseClasses.at(i), subst)->asBaseClass());
}
-Class::~Class()
-{ }
-
-bool Class::isClass() const
-{ return _key == ClassKey; }
-
-bool Class::isStruct() const
-{ return _key == StructKey; }
-
-bool Class::isUnion() const
-{ return _key == UnionKey; }
-
-Class::Key Class::classKey() const
-{ return _key; }
-
-void Class::setClassKey(Key key)
-{ _key = key; }
-
void Class::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -804,10 +623,10 @@ bool Class::match0(const Type *otherType, Matcher *matcher) const
return false;
}
-unsigned Class::baseClassCount() const
-{ return unsigned(_baseClasses.size()); }
+int Class::baseClassCount() const
+{ return int(_baseClasses.size()); }
-BaseClass *Class::baseClassAt(unsigned index) const
+BaseClass *Class::baseClassAt(int index) const
{ return _baseClasses.at(index); }
void Class::addBaseClass(BaseClass *baseClass)
@@ -819,17 +638,17 @@ FullySpecifiedType Class::type() const
void Class::visitSymbol0(SymbolVisitor *visitor)
{
if (visitor->visit(this)) {
- for (unsigned i = 0; i < _baseClasses.size(); ++i) {
+ for (int i = 0; i < int(_baseClasses.size()); ++i) {
visitSymbol(_baseClasses.at(i), visitor);
}
- for (unsigned i = 0; i < memberCount(); ++i) {
+ for (int i = 0; i < memberCount(); ++i) {
visitSymbol(memberAt(i), visitor);
}
}
}
-QtPropertyDeclaration::QtPropertyDeclaration(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+QtPropertyDeclaration::QtPropertyDeclaration(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name)
, _flags(NoFlags)
{ }
@@ -840,26 +659,11 @@ QtPropertyDeclaration::QtPropertyDeclaration(Clone *clone, Subst *subst, QtPrope
, _flags(original->_flags)
{ }
-QtPropertyDeclaration::~QtPropertyDeclaration()
-{ }
-
-void QtPropertyDeclaration::setType(const FullySpecifiedType &type)
-{ _type = type; }
-
-void QtPropertyDeclaration::setFlags(int flags)
-{ _flags = flags; }
-
-int QtPropertyDeclaration::flags() const
-{ return _flags; }
-
-FullySpecifiedType QtPropertyDeclaration::type() const
-{ return _type; }
-
void QtPropertyDeclaration::visitSymbol0(SymbolVisitor *visitor)
{ visitor->visit(this); }
-QtEnum::QtEnum(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+QtEnum::QtEnum(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name)
{ }
@@ -867,17 +671,11 @@ QtEnum::QtEnum(Clone *clone, Subst *subst, QtEnum *original)
: Symbol(clone, subst, original)
{ }
-QtEnum::~QtEnum()
-{ }
-
-FullySpecifiedType QtEnum::type() const
-{ return FullySpecifiedType(); }
-
void QtEnum::visitSymbol0(SymbolVisitor *visitor)
{ visitor->visit(this); }
-ObjCBaseClass::ObjCBaseClass(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+ObjCBaseClass::ObjCBaseClass(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name)
{ }
@@ -885,8 +683,6 @@ ObjCBaseClass::ObjCBaseClass(Clone *clone, Subst *subst, ObjCBaseClass *original
: Symbol(clone, subst, original)
{ }
-ObjCBaseClass::~ObjCBaseClass()
-{ }
FullySpecifiedType ObjCBaseClass::type() const
{ return FullySpecifiedType(); }
@@ -894,7 +690,7 @@ FullySpecifiedType ObjCBaseClass::type() const
void ObjCBaseClass::visitSymbol0(SymbolVisitor *visitor)
{ visitor->visit(this); }
-ObjCBaseProtocol::ObjCBaseProtocol(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+ObjCBaseProtocol::ObjCBaseProtocol(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name)
{ }
@@ -902,26 +698,23 @@ ObjCBaseProtocol::ObjCBaseProtocol(Clone *clone, Subst *subst, ObjCBaseProtocol
: Symbol(clone, subst, original)
{ }
-ObjCBaseProtocol::~ObjCBaseProtocol()
-{ }
-
FullySpecifiedType ObjCBaseProtocol::type() const
{ return FullySpecifiedType(); }
void ObjCBaseProtocol::visitSymbol0(SymbolVisitor *visitor)
{ visitor->visit(this); }
-ObjCClass::ObjCClass(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name):
+ObjCClass::ObjCClass(TranslationUnit *translationUnit, int sourceLocation, const Name *name):
Scope(translationUnit, sourceLocation, name),
- _categoryName(0),
- _baseClass(0),
+ _categoryName(nullptr),
+ _baseClass(nullptr),
_isInterface(false)
{ }
ObjCClass::ObjCClass(Clone *clone, Subst *subst, ObjCClass *original)
: Scope(clone, subst, original)
, _categoryName(clone->name(original->_categoryName, subst))
- , _baseClass(0)
+ , _baseClass(nullptr)
, _isInterface(original->_isInterface)
{
if (original->_baseClass)
@@ -930,34 +723,10 @@ ObjCClass::ObjCClass(Clone *clone, Subst *subst, ObjCClass *original)
addProtocol(clone->symbol(original->_protocols.at(i), subst)->asObjCBaseProtocol());
}
-ObjCClass::~ObjCClass()
-{}
-
-bool ObjCClass::isInterface() const
-{ return _isInterface; }
-
-void ObjCClass::setInterface(bool isInterface)
-{ _isInterface = isInterface; }
-
-bool ObjCClass::isCategory() const
-{ return _categoryName != 0; }
-
-const Name *ObjCClass::categoryName() const
-{ return _categoryName; }
-
-void ObjCClass::setCategoryName(const Name *categoryName)
-{ _categoryName = categoryName; }
+int ObjCClass::protocolCount() const
+{ return int(_protocols.size()); }
-ObjCBaseClass *ObjCClass::baseClass() const
-{ return _baseClass; }
-
-void ObjCClass::setBaseClass(ObjCBaseClass *baseClass)
-{ _baseClass = baseClass; }
-
-unsigned ObjCClass::protocolCount() const
-{ return unsigned(_protocols.size()); }
-
-ObjCBaseProtocol *ObjCClass::protocolAt(unsigned index) const
+ObjCBaseProtocol *ObjCClass::protocolAt(int index) const
{ return _protocols.at(index); }
void ObjCClass::addProtocol(ObjCBaseProtocol *protocol)
@@ -972,10 +741,10 @@ void ObjCClass::visitSymbol0(SymbolVisitor *visitor)
if (_baseClass)
visitSymbol(_baseClass, visitor);
- for (unsigned i = 0; i < _protocols.size(); ++i)
+ for (int i = 0; i < int(_protocols.size()); ++i)
visitSymbol(_protocols.at(i), visitor);
- for (unsigned i = 0; i < memberCount(); ++i)
+ for (int i = 0; i < memberCount(); ++i)
visitSymbol(memberAt(i), visitor);
}
}
@@ -991,7 +760,7 @@ bool ObjCClass::match0(const Type *otherType, Matcher *matcher) const
return false;
}
-ObjCProtocol::ObjCProtocol(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name):
+ObjCProtocol::ObjCProtocol(TranslationUnit *translationUnit, int sourceLocation, const Name *name):
Scope(translationUnit, sourceLocation, name)
{
}
@@ -1003,13 +772,10 @@ ObjCProtocol::ObjCProtocol(Clone *clone, Subst *subst, ObjCProtocol *original)
addProtocol(clone->symbol(original->_protocols.at(i), subst)->asObjCBaseProtocol());
}
-ObjCProtocol::~ObjCProtocol()
-{}
-
-unsigned ObjCProtocol::protocolCount() const
-{ return unsigned(_protocols.size()); }
+int ObjCProtocol::protocolCount() const
+{ return int(_protocols.size()); }
-ObjCBaseProtocol *ObjCProtocol::protocolAt(unsigned index) const
+ObjCBaseProtocol *ObjCProtocol::protocolAt(int index) const
{ return _protocols.at(index); }
void ObjCProtocol::addProtocol(ObjCBaseProtocol *protocol)
@@ -1021,7 +787,7 @@ FullySpecifiedType ObjCProtocol::type() const
void ObjCProtocol::visitSymbol0(SymbolVisitor *visitor)
{
if (visitor->visit(this)) {
- for (unsigned i = 0; i < _protocols.size(); ++i)
+ for (int i = 0; i < int(_protocols.size()); ++i)
visitSymbol(_protocols.at(i), visitor);
}
}
@@ -1037,7 +803,7 @@ bool ObjCProtocol::match0(const Type *otherType, Matcher *matcher) const
return false;
}
-ObjCForwardClassDeclaration::ObjCForwardClassDeclaration(TranslationUnit *translationUnit, unsigned sourceLocation,
+ObjCForwardClassDeclaration::ObjCForwardClassDeclaration(TranslationUnit *translationUnit, int sourceLocation,
const Name *name):
Symbol(translationUnit, sourceLocation, name)
{
@@ -1047,9 +813,6 @@ ObjCForwardClassDeclaration::ObjCForwardClassDeclaration(Clone *clone, Subst *su
: Symbol(clone, subst, original)
{ }
-ObjCForwardClassDeclaration::~ObjCForwardClassDeclaration()
-{}
-
FullySpecifiedType ObjCForwardClassDeclaration::type() const
{ return FullySpecifiedType(); }
@@ -1067,7 +830,7 @@ bool ObjCForwardClassDeclaration::match0(const Type *otherType, Matcher *matcher
return false;
}
-ObjCForwardProtocolDeclaration::ObjCForwardProtocolDeclaration(TranslationUnit *translationUnit, unsigned sourceLocation,
+ObjCForwardProtocolDeclaration::ObjCForwardProtocolDeclaration(TranslationUnit *translationUnit, int sourceLocation,
const Name *name):
Symbol(translationUnit, sourceLocation, name)
{
@@ -1077,9 +840,6 @@ ObjCForwardProtocolDeclaration::ObjCForwardProtocolDeclaration(Clone *clone, Sub
: Symbol(clone, subst, original)
{ }
-ObjCForwardProtocolDeclaration::~ObjCForwardProtocolDeclaration()
-{}
-
FullySpecifiedType ObjCForwardProtocolDeclaration::type() const
{ return FullySpecifiedType(); }
@@ -1097,7 +857,7 @@ bool ObjCForwardProtocolDeclaration::match0(const Type *otherType, Matcher *matc
return false;
}
-ObjCMethod::ObjCMethod(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
+ObjCMethod::ObjCMethod(TranslationUnit *translationUnit, int sourceLocation, const Name *name)
: Scope(translationUnit, sourceLocation, name),
_flags(0)
{ }
@@ -1108,9 +868,6 @@ ObjCMethod::ObjCMethod(Clone *clone, Subst *subst, ObjCMethod *original)
, _flags(original->_flags)
{ }
-ObjCMethod::~ObjCMethod()
-{ }
-
void ObjCMethod::accept0(TypeVisitor *visitor)
{ visitor->visit(this); }
@@ -1125,27 +882,21 @@ bool ObjCMethod::match0(const Type *otherType, Matcher *matcher) const
FullySpecifiedType ObjCMethod::type() const
{ return FullySpecifiedType(const_cast<ObjCMethod *>(this)); }
-FullySpecifiedType ObjCMethod::returnType() const
-{ return _returnType; }
-
-void ObjCMethod::setReturnType(const FullySpecifiedType &returnType)
-{ _returnType = returnType; }
-
bool ObjCMethod::hasReturnType() const
{
const FullySpecifiedType ty = returnType();
return ty.isValid() || ty.isSigned() || ty.isUnsigned();
}
-unsigned ObjCMethod::argumentCount() const
+int ObjCMethod::argumentCount() const
{
- const unsigned c = memberCount();
- if (c > 0 && memberAt(c - 1)->isBlock())
+ const int c = memberCount();
+ if (c > 0 && memberAt(c - 1)->asBlock())
return c - 1;
return c;
}
-Symbol *ObjCMethod::argumentAt(unsigned index) const
+Symbol *ObjCMethod::argumentAt(int index) const
{
return memberAt(index);
}
@@ -1153,30 +904,24 @@ Symbol *ObjCMethod::argumentAt(unsigned index) const
bool ObjCMethod::hasArguments() const
{
return ! (argumentCount() == 0 ||
- (argumentCount() == 1 && argumentAt(0)->type()->isVoidType()));
+ (argumentCount() == 1 && argumentAt(0)->type()->asVoidType()));
}
-bool ObjCMethod::isVariadic() const
-{ return f._isVariadic; }
-
-void ObjCMethod::setVariadic(bool isVariadic)
-{ f._isVariadic = isVariadic; }
-
void ObjCMethod::visitSymbol0(SymbolVisitor *visitor)
{
if (visitor->visit(this)) {
- for (unsigned i = 0; i < memberCount(); ++i) {
+ for (int i = 0; i < memberCount(); ++i) {
visitSymbol(memberAt(i), visitor);
}
}
}
ObjCPropertyDeclaration::ObjCPropertyDeclaration(TranslationUnit *translationUnit,
- unsigned sourceLocation,
+ int sourceLocation,
const Name *name):
Symbol(translationUnit, sourceLocation, name),
- _getterName(0),
- _setterName(0),
+ _getterName(nullptr),
+ _setterName(nullptr),
_propertyAttributes(None)
{}
@@ -1188,39 +933,6 @@ ObjCPropertyDeclaration::ObjCPropertyDeclaration(Clone *clone, Subst *subst, Obj
, _propertyAttributes(original->_propertyAttributes)
{ }
-ObjCPropertyDeclaration::~ObjCPropertyDeclaration()
-{}
-
-bool ObjCPropertyDeclaration::hasAttribute(int attribute) const
-{ return _propertyAttributes & attribute; }
-
-void ObjCPropertyDeclaration::setAttributes(int attributes)
-{ _propertyAttributes = attributes; }
-
-bool ObjCPropertyDeclaration::hasGetter() const
-{ return hasAttribute(Getter); }
-
-bool ObjCPropertyDeclaration::hasSetter() const
-{ return hasAttribute(Setter); }
-
-const Name *ObjCPropertyDeclaration::getterName() const
-{ return _getterName; }
-
-void ObjCPropertyDeclaration::setGetterName(const Name *getterName)
-{ _getterName = getterName; }
-
-const Name *ObjCPropertyDeclaration::setterName() const
-{ return _setterName; }
-
-void ObjCPropertyDeclaration::setSetterName(const Name *setterName)
-{ _setterName = setterName; }
-
-void ObjCPropertyDeclaration::setType(const FullySpecifiedType &type)
-{ _type = type; }
-
-FullySpecifiedType ObjCPropertyDeclaration::type() const
-{ return _type; }
-
void ObjCPropertyDeclaration::visitSymbol0(SymbolVisitor *visitor)
{
if (visitor->visit(this)) {
diff --git a/src/libs/3rdparty/cplusplus/Symbols.h b/src/libs/3rdparty/cplusplus/Symbols.h
index e29f78bf01..c9267e4fdd 100644
--- a/src/libs/3rdparty/cplusplus/Symbols.h
+++ b/src/libs/3rdparty/cplusplus/Symbols.h
@@ -29,269 +29,232 @@
namespace CPlusPlus {
-class CPLUSPLUS_EXPORT UsingNamespaceDirective: public Symbol
+class StringLiteral;
+
+class CPLUSPLUS_EXPORT UsingNamespaceDirective final : public Symbol
{
public:
- UsingNamespaceDirective(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ UsingNamespaceDirective(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
UsingNamespaceDirective(Clone *clone, Subst *subst, UsingNamespaceDirective *original);
- virtual ~UsingNamespaceDirective();
+ ~UsingNamespaceDirective() override = default;
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const UsingNamespaceDirective *asUsingNamespaceDirective() const
- { return this; }
+ FullySpecifiedType type() const override;
- virtual UsingNamespaceDirective *asUsingNamespaceDirective()
- { return this; }
+ const UsingNamespaceDirective *asUsingNamespaceDirective() const override { return this; }
+ UsingNamespaceDirective *asUsingNamespaceDirective() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
};
-class CPLUSPLUS_EXPORT UsingDeclaration: public Symbol
+class CPLUSPLUS_EXPORT UsingDeclaration final : public Symbol
{
public:
- UsingDeclaration(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ UsingDeclaration(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
UsingDeclaration(Clone *clone, Subst *subst, UsingDeclaration *original);
- virtual ~UsingDeclaration();
+ ~UsingDeclaration() override = default;
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const UsingDeclaration *asUsingDeclaration() const
- { return this; }
+ FullySpecifiedType type() const override;
- virtual UsingDeclaration *asUsingDeclaration()
- { return this; }
+ const UsingDeclaration *asUsingDeclaration() const override { return this; }
+ UsingDeclaration *asUsingDeclaration() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
};
-class CPLUSPLUS_EXPORT NamespaceAlias: public Symbol
+class CPLUSPLUS_EXPORT NamespaceAlias final : public Symbol
{
public:
- NamespaceAlias(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ NamespaceAlias(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
NamespaceAlias(Clone *clone, Subst *subst, NamespaceAlias *original);
- virtual ~NamespaceAlias();
+ ~NamespaceAlias() override = default;
- const Name *namespaceName() const;
- void setNamespaceName(const Name *namespaceName);
+ const Name *namespaceName() const { return _namespaceName; }
+ void setNamespaceName(const Name *namespaceName) { _namespaceName = namespaceName; }
// Symbol's interface
- virtual FullySpecifiedType type() const;
+ FullySpecifiedType type() const override;
- virtual const NamespaceAlias *asNamespaceAlias() const
- { return this; }
-
- virtual NamespaceAlias *asNamespaceAlias()
- { return this; }
+ const NamespaceAlias *asNamespaceAlias() const override { return this; }
+ NamespaceAlias *asNamespaceAlias() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
private:
const Name *_namespaceName;
};
-class CPLUSPLUS_EXPORT Declaration: public Symbol
+class CPLUSPLUS_EXPORT Declaration : public Symbol
{
public:
- Declaration(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ Declaration(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
Declaration(Clone *clone, Subst *subst, Declaration *original);
- virtual ~Declaration();
+ ~Declaration() override = default;
- void setType(const FullySpecifiedType &type);
- void setInitializer(StringLiteral const* initializer);
+ void setType(const FullySpecifiedType &type) { _type = type; }
+ void setInitializer(StringLiteral const* initializer) { _initializer = initializer; }
// Symbol's interface
- virtual FullySpecifiedType type() const;
- const StringLiteral *getInitializer() const;
-
- virtual const Declaration *asDeclaration() const
- { return this; }
-
- virtual Declaration *asDeclaration()
- { return this; }
+ FullySpecifiedType type() const override { return _type; }
+ const StringLiteral *getInitializer() const { return _initializer; }
- virtual EnumeratorDeclaration *asEnumeratorDeclarator()
- { return 0; }
+ const Declaration *asDeclaration() const override { return this; }
+ Declaration *asDeclaration() override { return this; }
- virtual const EnumeratorDeclaration *asEnumeratorDeclarator() const
- { return 0; }
+ virtual EnumeratorDeclaration *asEnumeratorDeclarator() { return nullptr; }
+ virtual const EnumeratorDeclaration *asEnumeratorDeclarator() const { return nullptr; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
private:
FullySpecifiedType _type;
const StringLiteral *_initializer;
};
-class CPLUSPLUS_EXPORT EnumeratorDeclaration: public Declaration
+class CPLUSPLUS_EXPORT EnumeratorDeclaration final : public Declaration
{
public:
- EnumeratorDeclaration(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
- virtual ~EnumeratorDeclaration();
+ EnumeratorDeclaration(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
+ ~EnumeratorDeclaration() override = default;
- const StringLiteral *constantValue() const;
- void setConstantValue(const StringLiteral *constantValue);
+ const StringLiteral *constantValue() const { return _constantValue; }
+ void setConstantValue(const StringLiteral *constantValue) { _constantValue = constantValue; }
- virtual EnumeratorDeclaration *asEnumeratorDeclarator()
- { return this; }
-
- virtual const EnumeratorDeclaration *asEnumeratorDeclarator() const
- { return this; }
+ EnumeratorDeclaration *asEnumeratorDeclarator() override { return this; }
+ const EnumeratorDeclaration *asEnumeratorDeclarator() const override { return this; }
private:
const StringLiteral *_constantValue;
};
-class CPLUSPLUS_EXPORT Argument: public Symbol
+class CPLUSPLUS_EXPORT Argument final : public Symbol
{
public:
- Argument(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ Argument(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
Argument(Clone *clone, Subst *subst, Argument *original);
- virtual ~Argument();
+ ~Argument() override = default;
- void setType(const FullySpecifiedType &type);
+ void setType(const FullySpecifiedType &type) { _type = type; }
- bool hasInitializer() const;
+ bool hasInitializer() const { return _initializer != nullptr; }
- const StringLiteral *initializer() const;
- void setInitializer(const StringLiteral *initializer);
+ const StringLiteral *initializer() const { return _initializer; }
+ void setInitializer(const StringLiteral *initializer) { _initializer = initializer; }
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const Argument *asArgument() const
- { return this; }
+ FullySpecifiedType type() const override { return _type; }
- virtual Argument *asArgument()
- { return this; }
+ const Argument *asArgument() const override { return this; }
+ Argument *asArgument() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
private:
const StringLiteral *_initializer;
FullySpecifiedType _type;
};
-class CPLUSPLUS_EXPORT TypenameArgument: public Symbol
+class CPLUSPLUS_EXPORT TypenameArgument final : public Symbol
{
public:
- TypenameArgument(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ TypenameArgument(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
TypenameArgument(Clone *clone, Subst *subst, TypenameArgument *original);
- virtual ~TypenameArgument();
+ ~TypenameArgument() = default;
- void setType(const FullySpecifiedType &type);
+ void setType(const FullySpecifiedType &type) { _type = type; }
void setClassDeclarator(bool isClassDecl) { _isClassDeclarator = isClassDecl; }
bool isClassDeclarator() const { return _isClassDeclarator; }
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const TypenameArgument *asTypenameArgument() const
- { return this; }
+ FullySpecifiedType type() const override { return _type; }
- virtual TypenameArgument *asTypenameArgument()
- { return this; }
+ const TypenameArgument *asTypenameArgument() const override { return this; }
+ TypenameArgument *asTypenameArgument() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
private:
FullySpecifiedType _type;
bool _isClassDeclarator;
};
-class CPLUSPLUS_EXPORT Block: public Scope
+class CPLUSPLUS_EXPORT Block final : public Scope
{
public:
- Block(TranslationUnit *translationUnit, unsigned sourceLocation);
+ Block(TranslationUnit *translationUnit, int sourceLocation);
Block(Clone *clone, Subst *subst, Block *original);
- virtual ~Block();
+ ~Block() override = default;
// Symbol's interface
- virtual FullySpecifiedType type() const;
+ FullySpecifiedType type() const override;
- virtual const Block *asBlock() const
- { return this; }
-
- virtual Block *asBlock()
- { return this; }
+ const Block *asBlock() const override { return this; }
+ Block *asBlock() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
};
-class CPLUSPLUS_EXPORT ForwardClassDeclaration: public Symbol, public Type
+class CPLUSPLUS_EXPORT ForwardClassDeclaration final : public Symbol, public Type
{
public:
- ForwardClassDeclaration(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ ForwardClassDeclaration(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
ForwardClassDeclaration(Clone *clone, Subst *subst, ForwardClassDeclaration *original);
- virtual ~ForwardClassDeclaration();
+ ~ForwardClassDeclaration() override = default;
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const ForwardClassDeclaration *asForwardClassDeclaration() const
- { return this; }
+ FullySpecifiedType type() const override;
- virtual ForwardClassDeclaration *asForwardClassDeclaration()
- { return this; }
+ const ForwardClassDeclaration *asForwardClassDeclaration() const override { return this; }
+ ForwardClassDeclaration *asForwardClassDeclaration() override { return this; }
// Type's interface
- virtual const ForwardClassDeclaration *asForwardClassDeclarationType() const
- { return this; }
-
- virtual ForwardClassDeclaration *asForwardClassDeclarationType()
- { return this; }
+ const ForwardClassDeclaration *asForwardClassDeclarationType() const override { return this; }
+ ForwardClassDeclaration *asForwardClassDeclarationType() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void visitSymbol0(SymbolVisitor *visitor) override;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
};
-class CPLUSPLUS_EXPORT Enum: public Scope, public Type
+class CPLUSPLUS_EXPORT Enum final : public Scope, public Type
{
public:
- Enum(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ Enum(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
Enum(Clone *clone, Subst *subst, Enum *original);
- virtual ~Enum();
+ ~Enum() override = default;
- bool isScoped() const;
- void setScoped(bool scoped);
+ bool isScoped() const { return _isScoped; }
+ void setScoped(bool scoped) { _isScoped = scoped; }
// Symbol's interface
- virtual FullySpecifiedType type() const;
+ FullySpecifiedType type() const override;
- virtual const Enum *asEnum() const
- { return this; }
-
- virtual Enum *asEnum()
- { return this; }
+ const Enum *asEnum() const override { return this; }
+ Enum *asEnum() override { return this; }
// Type's interface
- virtual const Enum *asEnumType() const
- { return this; }
-
- virtual Enum *asEnumType()
- { return this; }
+ const Enum *asEnumType() const override { return this; }
+ Enum *asEnumType() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void visitSymbol0(SymbolVisitor *visitor) override;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
bool _isScoped;
};
-class CPLUSPLUS_EXPORT Function: public Scope, public Type
+class CPLUSPLUS_EXPORT Function final : public Scope, public Type
{
public:
enum MethodKey {
@@ -308,89 +271,96 @@ public:
};
public:
- Function(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ Function(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
Function(Clone *clone, Subst *subst, Function *original);
- virtual ~Function();
+ ~Function() override = default;
+
+ bool isNormal() const { return f._methodKey == NormalMethod; }
+ bool isSignal() const { return f._methodKey == SignalMethod; }
+ bool isSlot() const { return f._methodKey == SlotMethod; }
+ bool isInvokable() const { return f._methodKey == InvokableMethod; }
- bool isNormal() const;
- bool isSignal() const;
- bool isSlot() const;
- bool isInvokable() const;
- int methodKey() const;
- void setMethodKey(int key);
+ int methodKey() const { return f._methodKey; }
+ void setMethodKey(int key) { f._methodKey = key; }
- FullySpecifiedType returnType() const;
- void setReturnType(const FullySpecifiedType &returnType);
+ FullySpecifiedType returnType() const { return _returnType; }
+ void setReturnType(const FullySpecifiedType &returnType) { _returnType = returnType; }
/** Convenience function that returns whether the function returns something (including void). */
bool hasReturnType() const;
- unsigned argumentCount() const;
- Symbol *argumentAt(unsigned index) const;
+ int argumentCount() const;
+ Symbol *argumentAt(int index) const;
/** Convenience function that returns whether the function receives any arguments. */
bool hasArguments() const;
- unsigned minimumArgumentCount() const;
+ int minimumArgumentCount() const;
- bool isVirtual() const;
- void setVirtual(bool isVirtual);
+ bool isVirtual() const { return f._isVirtual; }
+ void setVirtual(bool isVirtual) { f._isVirtual = isVirtual; }
- bool isOverride() const;
- void setOverride(bool isOverride);
+ bool isOverride() const { return f._isOverride; }
+ void setOverride(bool isOverride) { f._isOverride = isOverride; }
- bool isFinal() const;
- void setFinal(bool isFinal);
+ bool isFinal() const { return f._isFinal; }
+ void setFinal(bool isFinal) { f._isFinal = isFinal; }
- bool isVariadic() const;
- void setVariadic(bool isVariadic);
+ bool isVariadic() const { return f._isVariadic; }
+ void setVariadic(bool isVariadic) { f._isVariadic = isVariadic; }
- bool isConst() const;
- void setConst(bool isConst);
+ bool isVariadicTemplate() const { return f._isVariadicTemplate; }
+ void setVariadicTemplate(bool isVariadicTemplate) { f._isVariadicTemplate = isVariadicTemplate; }
- bool isVolatile() const;
- void setVolatile(bool isVolatile);
+ bool isConst() const { return f._isConst; }
+ void setConst(bool isConst) { f._isConst = isConst; }
- bool isPureVirtual() const;
- void setPureVirtual(bool isPureVirtual);
+ bool isStatic() const { return f._isStatic; }
+ void setStatic(bool isStatic) { f._isStatic = isStatic; }
- RefQualifier refQualifier() const;
- void setRefQualifier(RefQualifier refQualifier);
+ bool isVolatile() const { return f._isVolatile; }
+ void setVolatile(bool isVolatile) { f._isVolatile = isVolatile; }
- bool isSignatureEqualTo(const Function *other, Matcher *matcher = 0) const;
+ bool isPureVirtual() const { return f._isPureVirtual; }
+ void setPureVirtual(bool isPureVirtual) { f._isPureVirtual = isPureVirtual; }
- bool isAmbiguous() const; // internal
- void setAmbiguous(bool isAmbiguous); // internal
+ RefQualifier refQualifier() const { return static_cast<RefQualifier>(f._refQualifier); }
+ void setRefQualifier(RefQualifier refQualifier) { f._refQualifier = refQualifier; }
- bool maybeValidPrototype(unsigned actualArgumentCount) const;
+ bool isSignatureEqualTo(const Function *other, Matcher *matcher = nullptr) const;
- // Symbol's interface
- virtual FullySpecifiedType type() const;
+ bool isAmbiguous() const { return f._isAmbiguous; } // internal
+ void setAmbiguous(bool isAmbiguous) { f._isAmbiguous = isAmbiguous; } // internal
- virtual const Function *asFunction() const
- { return this; }
+ bool maybeValidPrototype(int actualArgumentCount) const;
- virtual Function *asFunction()
- { return this; }
+ const StringLiteral *exceptionSpecification() { return _exceptionSpecification; }
+ void setExceptionSpecification(const StringLiteral *spec) { _exceptionSpecification = spec; }
- // Type's interface
- virtual const Function *asFunctionType() const
- { return this; }
+ // Symbol's interface
+ FullySpecifiedType type() const override;
- virtual Function *asFunctionType()
- { return this; }
+ const Function *asFunction() const override { return this; }
+ Function *asFunction() override { return this; }
+
+ // Type's interface
+ const Function *asFunctionType() const override { return this; }
+ Function *asFunctionType() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void visitSymbol0(SymbolVisitor *visitor) override;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
FullySpecifiedType _returnType;
+ const StringLiteral *_exceptionSpecification = nullptr;
struct Flags {
unsigned _isVirtual: 1;
unsigned _isOverride: 1;
unsigned _isFinal: 1;
+ unsigned _isStatic: 1;
unsigned _isVariadic: 1;
+ unsigned _isVariadicTemplate: 1;
unsigned _isPureVirtual: 1;
unsigned _isConst: 1;
unsigned _isVolatile: 1;
@@ -404,103 +374,85 @@ private:
};
};
-class CPLUSPLUS_EXPORT Template: public Scope, public Type
+class CPLUSPLUS_EXPORT Template final : public Scope, public Type
{
public:
- Template(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ Template(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
Template(Clone *clone, Subst *subst, Template *original);
- virtual ~Template();
+ ~Template() override = default;
- unsigned templateParameterCount() const;
- Symbol *templateParameterAt(unsigned index) const;
+ int templateParameterCount() const;
+ Symbol *templateParameterAt(int index) const { return memberAt(index); }
Symbol *declaration() const;
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const Template *asTemplate() const
- { return this; }
+ FullySpecifiedType type() const override;
- virtual Template *asTemplate()
- { return this; }
+ const Template *asTemplate() const override { return this; }
+ Template *asTemplate() override { return this; }
// Type's interface
- virtual const Template *asTemplateType() const
- { return this; }
-
- virtual Template *asTemplateType()
- { return this; }
+ const Template *asTemplateType() const override { return this; }
+ Template *asTemplateType() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void visitSymbol0(SymbolVisitor *visitor) override;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
};
-class CPLUSPLUS_EXPORT Namespace: public Scope, public Type
+class CPLUSPLUS_EXPORT Namespace final : public Scope, public Type
{
public:
- Namespace(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ Namespace(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
Namespace(Clone *clone, Subst *subst, Namespace *original);
- virtual ~Namespace();
+ ~Namespace() override = default;
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const Namespace *asNamespace() const
- { return this; }
+ FullySpecifiedType type() const override;
- virtual Namespace *asNamespace()
- { return this; }
+ const Namespace *asNamespace() const override { return this; }
+ Namespace *asNamespace() override { return this; }
// Type's interface
- virtual const Namespace *asNamespaceType() const
- { return this; }
+ const Namespace *asNamespaceType() const override { return this; }
+ Namespace *asNamespaceType() override { return this; }
- virtual Namespace *asNamespaceType()
- { return this; }
-
- bool isInline() const
- { return _isInline; }
-
- void setInline(bool onoff)
- { _isInline = onoff; }
+ bool isInline() const { return _isInline; }
+ void setInline(bool onoff) { _isInline = onoff; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void visitSymbol0(SymbolVisitor *visitor) override;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
bool _isInline;
};
-class CPLUSPLUS_EXPORT BaseClass: public Symbol
+class CPLUSPLUS_EXPORT BaseClass final : public Symbol
{
public:
- BaseClass(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ BaseClass(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
BaseClass(Clone *clone, Subst *subst, BaseClass *original);
- virtual ~BaseClass();
+ ~BaseClass() override = default;
- bool isVirtual() const;
- void setVirtual(bool isVirtual);
+ bool isVirtual() const { return _isVirtual; }
+ void setVirtual(bool isVirtual) { _isVirtual = isVirtual; }
- bool isVariadic() const;
- void setVariadic(bool isVariadic);
+ bool isVariadic() const { return _isVariadic; }
+ void setVariadic(bool isVariadic) { _isVariadic = isVariadic; }
// Symbol's interface
- virtual FullySpecifiedType type() const;
- void setType(const FullySpecifiedType &type);
-
- virtual const BaseClass *asBaseClass() const
- { return this; }
+ FullySpecifiedType type() const override { return _type; }
+ void setType(const FullySpecifiedType &type) { _type = type; }
- virtual BaseClass *asBaseClass()
- { return this; }
+ const BaseClass *asBaseClass() const override { return this; }
+ BaseClass *asBaseClass() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
private:
bool _isVariadic = false;
@@ -508,12 +460,12 @@ private:
FullySpecifiedType _type;
};
-class CPLUSPLUS_EXPORT Class: public Scope, public Type
+class CPLUSPLUS_EXPORT Class final : public Scope, public Type
{
public:
- Class(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ Class(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
Class(Clone *clone, Subst *subst, Class *original);
- virtual ~Class();
+ ~Class() override = default;
enum Key {
ClassKey,
@@ -521,43 +473,39 @@ public:
UnionKey
};
- bool isClass() const;
- bool isStruct() const;
- bool isUnion() const;
- Key classKey() const;
- void setClassKey(Key key);
+ bool isClass() const { return _key == ClassKey; }
+ bool isStruct() const { return _key == StructKey; }
+ bool isUnion() const { return _key == UnionKey; }
- unsigned baseClassCount() const;
- BaseClass *baseClassAt(unsigned index) const;
+ Key classKey() const { return _key; }
+ void setClassKey(Key key) { _key = key; }
+
+ int baseClassCount() const;
+ BaseClass *baseClassAt(int index) const;
void addBaseClass(BaseClass *baseClass);
+ const std::vector<BaseClass *> &baseClasses() const { return _baseClasses; }
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const Class *asClass() const
- { return this; }
+ FullySpecifiedType type() const override;
- virtual Class *asClass()
- { return this; }
+ const Class *asClass() const override { return this; }
+ Class *asClass() override { return this; }
// Type's interface
- virtual const Class *asClassType() const
- { return this; }
-
- virtual Class *asClassType()
- { return this; }
+ const Class *asClassType() const override { return this; }
+ Class *asClassType() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void visitSymbol0(SymbolVisitor *visitor) override;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
Key _key;
std::vector<BaseClass *> _baseClasses;
};
-class CPLUSPLUS_EXPORT QtPropertyDeclaration: public Symbol
+class CPLUSPLUS_EXPORT QtPropertyDeclaration final : public Symbol
{
public:
enum Flag {
@@ -580,227 +528,190 @@ public:
};
public:
- QtPropertyDeclaration(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ QtPropertyDeclaration(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
QtPropertyDeclaration(Clone *clone, Subst *subst, QtPropertyDeclaration *original);
- virtual ~QtPropertyDeclaration();
+ ~QtPropertyDeclaration() = default;
- void setType(const FullySpecifiedType &type);
-
- void setFlags(int flags);
- int flags() const;
+ void setType(const FullySpecifiedType &type) { _type = type; }
+ void setFlags(int flags) { _flags = flags; }
+ int flags() const { return _flags; }
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const QtPropertyDeclaration *asQtPropertyDeclaration() const
- { return this; }
+ FullySpecifiedType type() const override { return _type; }
- virtual QtPropertyDeclaration *asQtPropertyDeclaration()
- { return this; }
+ const QtPropertyDeclaration *asQtPropertyDeclaration() const override { return this; }
+ QtPropertyDeclaration *asQtPropertyDeclaration() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
private:
FullySpecifiedType _type;
int _flags;
};
-class CPLUSPLUS_EXPORT QtEnum: public Symbol
+class CPLUSPLUS_EXPORT QtEnum final : public Symbol
{
public:
- QtEnum(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ QtEnum(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
QtEnum(Clone *clone, Subst *subst, QtEnum *original);
- virtual ~QtEnum();
+ ~QtEnum() override = default;
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const QtEnum *asQtEnum() const
- { return this; }
+ FullySpecifiedType type() const override { return FullySpecifiedType(); }
- virtual QtEnum *asQtEnum()
- { return this; }
+ const QtEnum *asQtEnum() const override { return this; }
+ QtEnum *asQtEnum() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
};
-class CPLUSPLUS_EXPORT ObjCBaseClass: public Symbol
+class CPLUSPLUS_EXPORT ObjCBaseClass final : public Symbol
{
public:
- ObjCBaseClass(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ ObjCBaseClass(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
ObjCBaseClass(Clone *clone, Subst *subst, ObjCBaseClass *original);
- virtual ~ObjCBaseClass();
+ ~ObjCBaseClass() override = default;
// Symbol's interface
- virtual FullySpecifiedType type() const;
+ FullySpecifiedType type() const override;
- virtual const ObjCBaseClass *asObjCBaseClass() const
- { return this; }
-
- virtual ObjCBaseClass *asObjCBaseClass()
- { return this; }
+ const ObjCBaseClass *asObjCBaseClass() const override { return this; }
+ ObjCBaseClass *asObjCBaseClass() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
};
-class CPLUSPLUS_EXPORT ObjCBaseProtocol: public Symbol
+class CPLUSPLUS_EXPORT ObjCBaseProtocol final : public Symbol
{
public:
- ObjCBaseProtocol(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ ObjCBaseProtocol(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
ObjCBaseProtocol(Clone *clone, Subst *subst, ObjCBaseProtocol *original);
- virtual ~ObjCBaseProtocol();
+ ~ObjCBaseProtocol() override = default;
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const ObjCBaseProtocol *asObjCBaseProtocol() const
- { return this; }
+ FullySpecifiedType type() const override;
- virtual ObjCBaseProtocol *asObjCBaseProtocol()
- { return this; }
+ const ObjCBaseProtocol *asObjCBaseProtocol() const override { return this; }
+ ObjCBaseProtocol *asObjCBaseProtocol() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
};
-class CPLUSPLUS_EXPORT ObjCForwardProtocolDeclaration: public Symbol, public Type
+class CPLUSPLUS_EXPORT ObjCForwardProtocolDeclaration final : public Symbol, public Type
{
public:
- ObjCForwardProtocolDeclaration(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ ObjCForwardProtocolDeclaration(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
ObjCForwardProtocolDeclaration(Clone *clone, Subst *subst, ObjCForwardProtocolDeclaration *original);
- virtual ~ObjCForwardProtocolDeclaration();
+ ~ObjCForwardProtocolDeclaration() override = default;
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclaration() const
- { return this; }
+ FullySpecifiedType type() const override;
- virtual ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclaration()
- { return this; }
+ const ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclaration() const override { return this; }
+ ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclaration() override { return this; }
// Type's interface
- virtual const ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclarationType() const
- { return this; }
-
- virtual ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclarationType()
- { return this; }
+ const ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclarationType() const override { return this; }
+ ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclarationType() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void visitSymbol0(SymbolVisitor *visitor) override;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
};
-class CPLUSPLUS_EXPORT ObjCProtocol: public Scope, public Type
+class CPLUSPLUS_EXPORT ObjCProtocol final : public Scope, public Type
{
public:
- ObjCProtocol(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ ObjCProtocol(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
ObjCProtocol(Clone *clone, Subst *subst, ObjCProtocol *original);
- virtual ~ObjCProtocol();
+ ~ObjCProtocol() override = default;
- unsigned protocolCount() const;
- ObjCBaseProtocol *protocolAt(unsigned index) const;
+ int protocolCount() const;
+ ObjCBaseProtocol *protocolAt(int index) const;
void addProtocol(ObjCBaseProtocol *protocol);
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const ObjCProtocol *asObjCProtocol() const
- { return this; }
+ FullySpecifiedType type() const override;
- virtual ObjCProtocol *asObjCProtocol()
- { return this; }
+ const ObjCProtocol *asObjCProtocol() const override { return this; }
+ ObjCProtocol *asObjCProtocol() override { return this; }
// Type's interface
- virtual const ObjCProtocol *asObjCProtocolType() const
- { return this; }
-
- virtual ObjCProtocol *asObjCProtocolType()
- { return this; }
+ const ObjCProtocol *asObjCProtocolType() const override { return this; }
+ ObjCProtocol *asObjCProtocolType() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void visitSymbol0(SymbolVisitor *visitor) override;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
std::vector<ObjCBaseProtocol *> _protocols;
};
-class CPLUSPLUS_EXPORT ObjCForwardClassDeclaration: public Symbol, public Type
+class CPLUSPLUS_EXPORT ObjCForwardClassDeclaration final : public Symbol, public Type
{
public:
- ObjCForwardClassDeclaration(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ ObjCForwardClassDeclaration(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
ObjCForwardClassDeclaration(Clone *clone, Subst *subst, ObjCForwardClassDeclaration *original);
- virtual ~ObjCForwardClassDeclaration();
+ ~ObjCForwardClassDeclaration() override = default;
// Symbol's interface
- virtual FullySpecifiedType type() const;
+ FullySpecifiedType type() const override;
- virtual const ObjCForwardClassDeclaration *asObjCForwardClassDeclaration() const
- { return this; }
-
- virtual ObjCForwardClassDeclaration *asObjCForwardClassDeclaration()
- { return this; }
+ const ObjCForwardClassDeclaration *asObjCForwardClassDeclaration() const override { return this; }
+ ObjCForwardClassDeclaration *asObjCForwardClassDeclaration() override { return this; }
// Type's interface
- virtual const ObjCForwardClassDeclaration *asObjCForwardClassDeclarationType() const
- { return this; }
-
- virtual ObjCForwardClassDeclaration *asObjCForwardClassDeclarationType()
- { return this; }
+ const ObjCForwardClassDeclaration *asObjCForwardClassDeclarationType() const override { return this; }
+ ObjCForwardClassDeclaration *asObjCForwardClassDeclarationType() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void visitSymbol0(SymbolVisitor *visitor) override;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
};
-class CPLUSPLUS_EXPORT ObjCClass: public Scope, public Type
+class CPLUSPLUS_EXPORT ObjCClass final : public Scope, public Type
{
public:
- ObjCClass(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ ObjCClass(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
ObjCClass(Clone *clone, Subst *subst, ObjCClass *original);
- virtual ~ObjCClass();
+ ~ObjCClass() override = default;
- bool isInterface() const;
- void setInterface(bool isInterface);
+ bool isInterface() const { return _isInterface; }
+ void setInterface(bool isInterface) { _isInterface = isInterface; }
- bool isCategory() const;
- const Name *categoryName() const;
- void setCategoryName(const Name *categoryName);
+ bool isCategory() const { return _categoryName != nullptr; }
+ const Name *categoryName() const { return _categoryName; }
+ void setCategoryName(const Name *categoryName) { _categoryName = categoryName; }
- ObjCBaseClass *baseClass() const;
- void setBaseClass(ObjCBaseClass *baseClass);
+ ObjCBaseClass *baseClass() const { return _baseClass; }
+ void setBaseClass(ObjCBaseClass *baseClass) { _baseClass = baseClass; }
- unsigned protocolCount() const;
- ObjCBaseProtocol *protocolAt(unsigned index) const;
+ int protocolCount() const;
+ ObjCBaseProtocol *protocolAt(int index) const;
void addProtocol(ObjCBaseProtocol *protocol);
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const ObjCClass *asObjCClass() const
- { return this; }
+ FullySpecifiedType type() const override;
- virtual ObjCClass *asObjCClass()
- { return this; }
+ const ObjCClass *asObjCClass() const override { return this; }
+ ObjCClass *asObjCClass() override { return this; }
// Type's interface
- virtual const ObjCClass *asObjCClassType() const
- { return this; }
-
- virtual ObjCClass *asObjCClassType()
- { return this; }
+ const ObjCClass *asObjCClassType() const override { return this; }
+ ObjCClass *asObjCClassType() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void visitSymbol0(SymbolVisitor *visitor) override;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
const Name *_categoryName;
@@ -809,48 +720,42 @@ private:
bool _isInterface;
};
-class CPLUSPLUS_EXPORT ObjCMethod: public Scope, public Type
+class CPLUSPLUS_EXPORT ObjCMethod final : public Scope, public Type
{
public:
- ObjCMethod(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ ObjCMethod(TranslationUnit *translationUnit, int sourceLocation, const Name *name);
ObjCMethod(Clone *clone, Subst *subst, ObjCMethod *original);
- virtual ~ObjCMethod();
+ ~ObjCMethod() override = default;
- FullySpecifiedType returnType() const;
- void setReturnType(const FullySpecifiedType &returnType);
+ FullySpecifiedType returnType() const { return _returnType; }
+ void setReturnType(const FullySpecifiedType &returnType) { _returnType = returnType; }
/** Convenience function that returns whether the function returns something (including void). */
bool hasReturnType() const;
- unsigned argumentCount() const;
- Symbol *argumentAt(unsigned index) const;
+ int argumentCount() const;
+ Symbol *argumentAt(int index) const;
/** Convenience function that returns whether the function receives any arguments. */
bool hasArguments() const;
- bool isVariadic() const;
- void setVariadic(bool isVariadic);
+ bool isVariadic() const { return f._isVariadic; }
+ void setVariadic(bool isVariadic) { f._isVariadic = isVariadic; }
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const ObjCMethod *asObjCMethod() const
- { return this; }
+ FullySpecifiedType type() const override;
- virtual ObjCMethod *asObjCMethod()
- { return this; }
+ const ObjCMethod *asObjCMethod() const override { return this; }
+ ObjCMethod *asObjCMethod() override { return this; }
// Type's interface
- virtual const ObjCMethod *asObjCMethodType() const
- { return this; }
-
- virtual ObjCMethod *asObjCMethodType()
- { return this; }
+ const ObjCMethod *asObjCMethodType() const override { return this; }
+ ObjCMethod *asObjCMethodType() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
- virtual void accept0(TypeVisitor *visitor);
- virtual bool match0(const Type *otherType, Matcher *matcher) const;
+ void visitSymbol0(SymbolVisitor *visitor) override;
+ void accept0(TypeVisitor *visitor) override;
+ bool match0(const Type *otherType, Matcher *matcher) const override;
private:
FullySpecifiedType _returnType;
@@ -863,7 +768,7 @@ private:
};
};
-class CPLUSPLUS_EXPORT ObjCPropertyDeclaration: public Symbol
+class CPLUSPLUS_EXPORT ObjCPropertyDeclaration final : public Symbol
{
public:
enum PropertyAttributes {
@@ -883,37 +788,33 @@ public:
public:
ObjCPropertyDeclaration(TranslationUnit *translationUnit,
- unsigned sourceLocation,
+ int sourceLocation,
const Name *name);
ObjCPropertyDeclaration(Clone *clone, Subst *subst, ObjCPropertyDeclaration *original);
- virtual ~ObjCPropertyDeclaration();
+ ~ObjCPropertyDeclaration() override = default;
- bool hasAttribute(int attribute) const;
- void setAttributes(int attributes);
+ bool hasAttribute(int attribute) const { return _propertyAttributes & attribute; }
+ void setAttributes(int attributes) { _propertyAttributes = attributes; }
- bool hasGetter() const;
- bool hasSetter() const;
+ bool hasGetter() const { return hasAttribute(Getter); }
+ bool hasSetter() const { return hasAttribute(Setter); }
- const Name *getterName() const;
+ const Name *getterName() const { return _getterName; }
+ void setGetterName(const Name *getterName) { _getterName = getterName; }
- void setGetterName(const Name *getterName);
+ const Name *setterName() const { return _setterName; }
+ void setSetterName(const Name *setterName) { _setterName = setterName; }
- const Name *setterName() const;
- void setSetterName(const Name *setterName);
-
- void setType(const FullySpecifiedType &type);
+ void setType(const FullySpecifiedType &type) { _type = type; }
// Symbol's interface
- virtual FullySpecifiedType type() const;
-
- virtual const ObjCPropertyDeclaration *asObjCPropertyDeclaration() const
- { return this; }
+ FullySpecifiedType type() const override { return _type; }
- virtual ObjCPropertyDeclaration *asObjCPropertyDeclaration()
- { return this; }
+ const ObjCPropertyDeclaration *asObjCPropertyDeclaration() const override { return this; }
+ ObjCPropertyDeclaration *asObjCPropertyDeclaration() override { return this; }
protected:
- virtual void visitSymbol0(SymbolVisitor *visitor);
+ void visitSymbol0(SymbolVisitor *visitor) override;
private:
const Name *_getterName;
diff --git a/src/libs/3rdparty/cplusplus/Templates.cpp b/src/libs/3rdparty/cplusplus/Templates.cpp
index 61c6e612ae..4a222e0363 100644
--- a/src/libs/3rdparty/cplusplus/Templates.cpp
+++ b/src/libs/3rdparty/cplusplus/Templates.cpp
@@ -34,7 +34,7 @@ using namespace CPlusPlus;
CloneType::CloneType(Clone *clone)
: _clone(clone)
, _control(clone->control())
- , _subst(0)
+ , _subst(nullptr)
{
}
@@ -102,7 +102,7 @@ void CloneType::visit(NamedType *type)
const Name *name = _clone->name(type->name(), _subst);
FullySpecifiedType ty;
if (_subst)
- ty = _clone->type(_subst->apply(name), 0);
+ ty = _clone->type(_subst->apply(name), nullptr);
if (! ty.isValid())
ty = _control->namedType(name);
_type.setType(ty.type());
@@ -177,15 +177,15 @@ void CloneType::visit(ObjCForwardProtocolDeclaration *type)
CloneSymbol::CloneSymbol(Clone *clone)
: _clone(clone)
, _control(clone->control())
- , _subst(0)
- , _symbol(0)
+ , _subst(nullptr)
+ , _symbol(nullptr)
{
}
Symbol *CloneSymbol::cloneSymbol(Symbol *symbol, Subst *subst)
{
if (! symbol)
- return 0;
+ return nullptr;
SymbolSubstPair symbolSubstPair = std::make_pair(symbol, subst);
auto it = _cache.find(symbolSubstPair);
@@ -194,14 +194,14 @@ Symbol *CloneSymbol::cloneSymbol(Symbol *symbol, Subst *subst)
return it->second;
}
- Symbol *r = 0;
+ Symbol *r = nullptr;
std::swap(_subst, subst);
std::swap(_symbol, r);
accept(symbol);
std::swap(_symbol, r);
std::swap(_subst, subst);
- CPP_CHECK(r != 0);
+ CPP_CHECK(r != nullptr);
_cache[symbolSubstPair] = r;
return r;
}
@@ -401,28 +401,28 @@ bool CloneSymbol::visit(ObjCPropertyDeclaration *symbol)
CloneName::CloneName(Clone *clone)
: _clone(clone)
, _control(clone->control())
- , _subst(0)
- , _name(0)
+ , _subst(nullptr)
+ , _name(nullptr)
{
}
const Name *CloneName::cloneName(const Name *name, Subst *subst)
{
if (! name)
- return 0;
+ return nullptr;
NameSubstPair nameSubstPair = std::make_pair(name, subst);
auto it = _cache.find(nameSubstPair);
if (it != _cache.end())
return it->second;
- const Name *r = 0;
+ const Name *r = nullptr;
std::swap(_subst, subst);
std::swap(_name, r);
accept(name);
std::swap(_name, r);
std::swap(_subst, subst);
- CPP_CHECK(r != 0);
+ CPP_CHECK(r != nullptr);
_cache[nameSubstPair] = r;
return r;
}
@@ -439,14 +439,17 @@ void CloneName::visit(const AnonymousNameId *name)
void CloneName::visit(const TemplateNameId *name)
{
- std::vector<FullySpecifiedType> args(name->templateArgumentCount());
- for (unsigned i = 0; i < args.size(); ++i)
- args[i] = _clone->type(name->templateArgumentAt(i), _subst);
+ std::vector<TemplateArgument> args(name->templateArgumentCount());
+ for (int i = 0; i < int(args.size()); ++i) {
+ args[i].type() = _clone->type(name->templateArgumentAt(i).type(), _subst);
+ args[i].setNumericLiteral(_clone->numericLiteral(
+ name->templateArgumentAt(i).numericLiteral()));
+ }
if (args.empty())
_name = _control->templateNameId(_clone->identifier(name->identifier()), name->isSpecialization());
else
_name = _control->templateNameId(_clone->identifier(name->identifier()), name->isSpecialization(),
- &args[0], unsigned(args.size()));
+ &args[0], int(args.size()));
}
void CloneName::visit(const DestructorNameId *name)
@@ -474,9 +477,9 @@ void CloneName::visit(const SelectorNameId *name)
{
CPP_CHECK(name->nameCount() > 0);
std::vector<const Name *> names(name->nameCount());
- for (unsigned i = 0; i < names.size(); ++i)
+ for (int i = 0; i < int(names.size()); ++i)
names[i] = _clone->name(name->nameAt(i), _subst);
- _name = _control->selectorNameId(&names[0], unsigned(names.size()), name->hasArguments());
+ _name = _control->selectorNameId(&names[0], int(names.size()), name->hasArguments());
}
@@ -490,17 +493,17 @@ Clone::Clone(Control *control)
const StringLiteral *Clone::stringLiteral(const StringLiteral *literal)
{
- return literal ? _control->stringLiteral(literal->chars(), literal->size()) : 0;
+ return literal ? _control->stringLiteral(literal->chars(), literal->size()) : nullptr;
}
const NumericLiteral *Clone::numericLiteral(const NumericLiteral *literal)
{
- return literal ? _control->numericLiteral(literal->chars(), literal->size()) : 0;
+ return literal ? _control->numericLiteral(literal->chars(), literal->size()) : nullptr;
}
const Identifier *Clone::identifier(const Identifier *id)
{
- return id ? _control->identifier(id->chars(), id->size()) : 0;
+ return id ? _control->identifier(id->chars(), id->size()) : nullptr;
}
FullySpecifiedType Clone::type(const FullySpecifiedType &type, Subst *subst)
@@ -518,16 +521,16 @@ Symbol *Clone::symbol(Symbol *symbol, Subst *subst)
return _symbol(symbol, subst);
}
-Symbol *Clone::instantiate(Template *templ, const FullySpecifiedType *const args, unsigned argc, Subst *s)
+Symbol *Clone::instantiate(Template *templ, const FullySpecifiedType *const args, int argc, Subst *s)
{
Subst subst(_control, s);
- for (unsigned i = 0, e = std::min(templ->templateParameterCount(), argc); i < e; ++i) {
+ for (int i = 0, e = std::min(templ->templateParameterCount(), argc); i < e; ++i) {
Symbol *formal = templ->templateParameterAt(i);
FullySpecifiedType actual = args[i];
- subst.bind(name(formal->name(), 0), actual);
+ subst.bind(name(formal->name(), nullptr), actual);
}
if (argc < templ->templateParameterCount()) {
- for (unsigned i = argc; i < templ->templateParameterCount(); ++i) {
+ for (int i = argc; i < templ->templateParameterCount(); ++i) {
Symbol *formal = templ->templateParameterAt(i);
if (TypenameArgument *tn = formal->asTypenameArgument())
subst.bind(name(formal->name(), &subst), type(tn->type(), &subst));
@@ -537,7 +540,7 @@ Symbol *Clone::instantiate(Template *templ, const FullySpecifiedType *const args
inst->setEnclosingScope(templ->enclosingScope());
return inst;
}
- return 0;
+ return nullptr;
}
//
@@ -564,7 +567,7 @@ FullySpecifiedType Subst::apply(const Name *name) const
return control()->namedType(control()->qualifiedNameId(qualifiedBase,
qualifiedName));
}
- else if(baseNamedType->name()->identifier() != 0) {
+ else if(baseNamedType->name()->identifier() != nullptr) {
const QualifiedNameId *clonedQualifiedNameId
= control()->qualifiedNameId(baseNamedType->name()->identifier(),
unqualified->name());
diff --git a/src/libs/3rdparty/cplusplus/Templates.h b/src/libs/3rdparty/cplusplus/Templates.h
index 67b57a6296..8196f4e0c3 100644
--- a/src/libs/3rdparty/cplusplus/Templates.h
+++ b/src/libs/3rdparty/cplusplus/Templates.h
@@ -28,6 +28,7 @@
#include "SymbolVisitor.h"
#include <map>
+#include <unordered_map>
#include <utility>
namespace CPlusPlus {
@@ -40,7 +41,7 @@ class CPLUSPLUS_EXPORT Subst
Subst &operator = (const Subst &other);
public:
- Subst(Control *control, Subst *previous = 0)
+ Subst(Control *control, Subst *previous = nullptr)
: _control(control)
, _previous(previous)
{ }
@@ -60,7 +61,7 @@ public:
private:
Control *_control;
Subst *_previous;
- std::map<const Name *, FullySpecifiedType, Name::Compare> _map;
+ std::unordered_map<const Name *, FullySpecifiedType, Name::Hash, Name::Equals> _map;
};
class CPLUSPLUS_EXPORT CloneType: protected TypeVisitor
@@ -72,26 +73,26 @@ public:
FullySpecifiedType cloneType(const FullySpecifiedType &type, Subst *subst);
protected:
- virtual void visit(UndefinedType *type);
- virtual void visit(VoidType *type);
- virtual void visit(IntegerType *type);
- virtual void visit(FloatType *type);
- virtual void visit(PointerToMemberType *type);
- virtual void visit(PointerType *type);
- virtual void visit(ReferenceType *type);
- virtual void visit(ArrayType *type);
- virtual void visit(NamedType *type);
- virtual void visit(Function *type);
- virtual void visit(Namespace *type);
- virtual void visit(Template *type);
- virtual void visit(Class *type);
- virtual void visit(Enum *type);
- virtual void visit(ForwardClassDeclaration *type);
- virtual void visit(ObjCClass *type);
- virtual void visit(ObjCProtocol *type);
- virtual void visit(ObjCMethod *type);
- virtual void visit(ObjCForwardClassDeclaration *type);
- virtual void visit(ObjCForwardProtocolDeclaration *type);
+ void visit(UndefinedType *type) override;
+ void visit(VoidType *type) override;
+ void visit(IntegerType *type) override;
+ void visit(FloatType *type) override;
+ void visit(PointerToMemberType *type) override;
+ void visit(PointerType *type) override;
+ void visit(ReferenceType *type) override;
+ void visit(ArrayType *type) override;
+ void visit(NamedType *type) override;
+ void visit(Function *type) override;
+ void visit(Namespace *type) override;
+ void visit(Template *type) override;
+ void visit(Class *type) override;
+ void visit(Enum *type) override;
+ void visit(ForwardClassDeclaration *type) override;
+ void visit(ObjCClass *type) override;
+ void visit(ObjCProtocol *type) override;
+ void visit(ObjCMethod *type) override;
+ void visit(ObjCForwardClassDeclaration *type) override;
+ void visit(ObjCForwardProtocolDeclaration *type) override;
protected:
typedef std::pair <const FullySpecifiedType, Subst *> TypeSubstPair;
@@ -112,14 +113,14 @@ public:
const Name *cloneName(const Name *name, Subst *subst);
protected:
- virtual void visit(const Identifier *name);
- virtual void visit(const AnonymousNameId *name);
- virtual void visit(const TemplateNameId *name);
- virtual void visit(const DestructorNameId *name);
- virtual void visit(const OperatorNameId *name);
- virtual void visit(const ConversionNameId *name);
- virtual void visit(const QualifiedNameId *name);
- virtual void visit(const SelectorNameId *name);
+ void visit(const Identifier *name) override;
+ void visit(const AnonymousNameId *name) override;
+ void visit(const TemplateNameId *name) override;
+ void visit(const DestructorNameId *name) override;
+ void visit(const OperatorNameId *name) override;
+ void visit(const ConversionNameId *name) override;
+ void visit(const QualifiedNameId *name) override;
+ void visit(const SelectorNameId *name) override;
protected:
typedef std::pair <const Name *, Subst *> NameSubstPair;
@@ -140,34 +141,34 @@ public:
Symbol *cloneSymbol(Symbol *symbol, Subst *subst);
protected:
- virtual bool visit(UsingNamespaceDirective *symbol);
- virtual bool visit(UsingDeclaration *symbol);
- virtual bool visit(NamespaceAlias *symbol);
- virtual bool visit(Declaration *symbol);
- virtual bool visit(Argument *symbol);
- virtual bool visit(TypenameArgument *symbol);
- virtual bool visit(BaseClass *symbol);
- virtual bool visit(Enum *symbol);
- virtual bool visit(Function *symbol);
- virtual bool visit(Namespace *symbol);
- virtual bool visit(Template *symbol);
- virtual bool visit(Class *symbol);
- virtual bool visit(Block *symbol);
- virtual bool visit(ForwardClassDeclaration *symbol);
+ bool visit(UsingNamespaceDirective *symbol) override;
+ bool visit(UsingDeclaration *symbol) override;
+ bool visit(NamespaceAlias *symbol) override;
+ bool visit(Declaration *symbol) override;
+ bool visit(Argument *symbol) override;
+ bool visit(TypenameArgument *symbol) override;
+ bool visit(BaseClass *symbol) override;
+ bool visit(Enum *symbol) override;
+ bool visit(Function *symbol) override;
+ bool visit(Namespace *symbol) override;
+ bool visit(Template *symbol) override;
+ bool visit(Class *symbol) override;
+ bool visit(Block *symbol) override;
+ bool visit(ForwardClassDeclaration *symbol) override;
// Qt
- virtual bool visit(QtPropertyDeclaration *symbol);
- virtual bool visit(QtEnum *symbol);
+ bool visit(QtPropertyDeclaration *symbol) override;
+ bool visit(QtEnum *symbol) override;
// Objective-C
- virtual bool visit(ObjCBaseClass *symbol);
- virtual bool visit(ObjCBaseProtocol *symbol);
- virtual bool visit(ObjCClass *symbol);
- virtual bool visit(ObjCForwardClassDeclaration *symbol);
- virtual bool visit(ObjCProtocol *symbol);
- virtual bool visit(ObjCForwardProtocolDeclaration *symbol);
- virtual bool visit(ObjCMethod *symbol);
- virtual bool visit(ObjCPropertyDeclaration *symbol);
+ bool visit(ObjCBaseClass *symbol) override;
+ bool visit(ObjCBaseProtocol *symbol) override;
+ bool visit(ObjCClass *symbol) override;
+ bool visit(ObjCForwardClassDeclaration *symbol) override;
+ bool visit(ObjCProtocol *symbol) override;
+ bool visit(ObjCForwardProtocolDeclaration *symbol) override;
+ bool visit(ObjCMethod *symbol) override;
+ bool visit(ObjCPropertyDeclaration *symbol) override;
protected:
typedef std::pair <Symbol *, Subst *> SymbolSubstPair;
@@ -196,8 +197,8 @@ public:
Symbol *symbol(Symbol *symbol, Subst *subst);
Symbol *instantiate(Template *templ,
- const FullySpecifiedType *const args, unsigned argc,
- Subst *subst = 0);
+ const FullySpecifiedType *const args, int argc,
+ Subst *subst = nullptr);
private:
CloneType _type;
diff --git a/src/libs/3rdparty/cplusplus/Token.cpp b/src/libs/3rdparty/cplusplus/Token.cpp
index ecc7a8487b..2e8a0ce7f2 100644
--- a/src/libs/3rdparty/cplusplus/Token.cpp
+++ b/src/libs/3rdparty/cplusplus/Token.cpp
@@ -91,6 +91,7 @@ const char *token_names[] = {
("<="),
("<<"),
("<<="),
+ ("<=>"),
("-"),
("-="),
("--"),
@@ -119,9 +120,15 @@ const char *token_names[] = {
("case"),
("catch"),
("class"),
+ ("co_await"),
+ ("co_return"),
+ ("co_yield"),
+ ("concept"),
("const"),
("const_cast"),
+ ("consteval"),
("constexpr"),
+ ("constinit"),
("continue"),
("decltype"),
("default"),
@@ -150,6 +157,7 @@ const char *token_names[] = {
("public"),
("register"),
("reinterpret_cast"),
+ ("requires"),
("return"),
("sizeof"),
("static"),
@@ -177,6 +185,9 @@ const char *token_names[] = {
("__thread"),
("__typeof__"),
+ // msvc
+ ("__declspec"),
+
// objc @keywords
("@catch"),
("@class"),
@@ -206,6 +217,7 @@ const char *token_names[] = {
// Primitive types
("bool"),
("char"),
+ ("char8_t"),
("char16_t"),
("char32_t"),
("double"),
@@ -247,7 +259,7 @@ void Token::reset()
flags = 0;
byteOffset = 0;
utf16charOffset = 0;
- ptr = 0;
+ ptr = nullptr;
}
const char *Token::name(int kind)
diff --git a/src/libs/3rdparty/cplusplus/Token.h b/src/libs/3rdparty/cplusplus/Token.h
index 67378b28a3..f853d29c77 100644
--- a/src/libs/3rdparty/cplusplus/Token.h
+++ b/src/libs/3rdparty/cplusplus/Token.h
@@ -99,6 +99,7 @@ enum Kind {
T_LESS_EQUAL,
T_LESS_LESS,
T_LESS_LESS_EQUAL,
+ T_LESS_EQUAL_GREATER,
T_MINUS,
T_MINUS_EQUAL,
T_MINUS_MINUS,
@@ -129,9 +130,15 @@ enum Kind {
T_CASE,
T_CATCH,
T_CLASS,
+ T_CO_AWAIT,
+ T_CO_RETURN,
+ T_CO_YIELD,
+ T_CONCEPT,
T_CONST,
T_CONST_CAST,
+ T_CONSTEVAL,
T_CONSTEXPR,
+ T_CONSTINIT,
T_CONTINUE,
T_DECLTYPE,
T_DEFAULT,
@@ -160,6 +167,7 @@ enum Kind {
T_PUBLIC,
T_REGISTER,
T_REINTERPRET_CAST,
+ T_REQUIRES,
T_RETURN,
T_SIZEOF,
T_STATIC,
@@ -186,6 +194,8 @@ enum Kind {
T___THREAD,
T___TYPEOF__,
+ T___DECLSPEC,
+
// obj c++ @ keywords
T_FIRST_OBJC_AT_KEYWORD,
@@ -220,6 +230,7 @@ enum Kind {
T_FIRST_PRIMITIVE,
T_BOOL = T_FIRST_PRIMITIVE,
T_CHAR,
+ T_CHAR8_T,
T_CHAR16_T,
T_CHAR32_T,
T_DOUBLE,
@@ -293,12 +304,18 @@ enum Kind {
T___ATTRIBUTE = T___ATTRIBUTE__,
T___ALIGNOF__ = T_ALIGNOF,
+
+ T_SLOTS = T_Q_SLOTS,
+ T_FOREACH = T_Q_FOREACH,
+ T_SIGNALS = T_Q_SIGNALS,
+ T_Q_OVERRIDE = T_Q_PROPERTY,
+ T__DECLSPEC = T___DECLSPEC,
};
class CPLUSPLUS_EXPORT Token
{
public:
- Token() : flags(0), byteOffset(0), utf16charOffset(0), ptr(0) {}
+ Token() : flags(0), byteOffset(0), utf16charOffset(0), ptr(nullptr) {}
inline bool is(unsigned k) const { return f.kind == k; }
inline bool isNot(unsigned k) const { return f.kind != k; }
@@ -313,13 +330,13 @@ public:
inline bool generated() const { return f.generated; }
inline bool userDefinedLiteral() const { return f.userDefinedLiteral; }
- inline unsigned bytes() const { return f.bytes; }
- inline unsigned bytesBegin() const { return byteOffset; }
- inline unsigned bytesEnd() const { return byteOffset + f.bytes; }
+ inline int bytes() const { return f.bytes; }
+ inline int bytesBegin() const { return byteOffset; }
+ inline int bytesEnd() const { return byteOffset + f.bytes; }
- inline unsigned utf16chars() const { return f.utf16chars; }
- inline unsigned utf16charsBegin() const { return utf16charOffset; }
- inline unsigned utf16charsEnd() const { return utf16charOffset + f.utf16chars; }
+ inline int utf16chars() const { return f.utf16chars; }
+ inline int utf16charsBegin() const { return utf16charOffset; }
+ inline int utf16charsEnd() const { return utf16charOffset + f.utf16chars; }
inline bool isLiteral() const
{ return f.kind >= T_FIRST_LITERAL && f.kind <= T_LAST_LITERAL; }
@@ -405,7 +422,7 @@ public:
const StringLiteral *string;
const Identifier *identifier;
unsigned close_brace;
- unsigned lineno;
+ int lineno;
};
};
@@ -437,6 +454,9 @@ struct LanguageFeatures
unsigned int qtKeywordsEnabled : 1; // If Qt is used but QT_NO_KEYWORDS defined
unsigned int cxxEnabled : 1;
unsigned int cxx11Enabled : 1;
+ unsigned int cxx14Enabled : 1;
+ unsigned int cxx17Enabled : 1;
+ unsigned int cxx20Enabled : 1;
unsigned int objCEnabled : 1;
unsigned int c99Enabled : 1;
};
diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp
index 2da8ad563e..e680ee2660 100644
--- a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp
+++ b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp
@@ -26,6 +26,10 @@
#include "AST.h"
#include "Literals.h"
#include "DiagnosticClient.h"
+
+#include "cppassert.h"
+#include <utils/textutils.h>
+
#include <stack>
#include <vector>
#include <cstdarg>
@@ -43,10 +47,10 @@ const Token TranslationUnit::nullToken;
TranslationUnit::TranslationUnit(Control *control, const StringLiteral *fileId)
: _control(control),
_fileId(fileId),
- _firstSourceChar(0),
- _lastSourceChar(0),
- _pool(0),
- _ast(0),
+ _firstSourceChar(nullptr),
+ _lastSourceChar(nullptr),
+ _pool(nullptr),
+ _ast(nullptr),
_flags(0)
{
_tokens = new std::vector<Token>();
@@ -70,7 +74,7 @@ const StringLiteral *TranslationUnit::fileId() const
const char *TranslationUnit::fileName() const
{ return _fileId->chars(); }
-unsigned TranslationUnit::fileNameLength() const
+int TranslationUnit::fileNameLength() const
{ return _fileId->size(); }
const char *TranslationUnit::firstSourceChar() const
@@ -79,42 +83,72 @@ const char *TranslationUnit::firstSourceChar() const
const char *TranslationUnit::lastSourceChar() const
{ return _lastSourceChar; }
-unsigned TranslationUnit::sourceLength() const
+int TranslationUnit::sourceLength() const
{ return _lastSourceChar - _firstSourceChar; }
-void TranslationUnit::setSource(const char *source, unsigned size)
+void TranslationUnit::setSource(const char *source, int size)
{
+ CPP_CHECK(source);
_firstSourceChar = source;
_lastSourceChar = source + size;
}
-const char *TranslationUnit::spell(unsigned index) const
+const char *TranslationUnit::spell(int index) const
{
if (! index)
- return 0;
+ return nullptr;
return tokenAt(index).spell();
}
-unsigned TranslationUnit::commentCount() const
-{ return unsigned(_comments->size()); }
+int TranslationUnit::commentCount() const
+{ return int(_comments->size()); }
-const Token &TranslationUnit::commentAt(unsigned index) const
+const Token &TranslationUnit::commentAt(int index) const
{ return _comments->at(index); }
-const Identifier *TranslationUnit::identifier(unsigned index) const
+std::vector<Token> TranslationUnit::allTokens() const
+{
+ std::vector<Token> all;
+ int tokIndex = 0;
+ int commentIndex = 0;
+ while (true) {
+ if (tokIndex == tokenCount()) {
+ for (int i = commentIndex; i < commentCount(); ++i)
+ all.push_back(commentAt(i));
+ break;
+ }
+ if (commentIndex == commentCount()) {
+ for (int i = tokIndex; i < tokenCount(); ++i)
+ all.push_back(tokenAt(i));
+ break;
+ }
+ const Token &tok = tokenAt(tokIndex);
+ const Token &comment = commentAt(commentIndex);
+ if (tok.utf16charsBegin() < comment.utf16charsBegin()) {
+ all.push_back(tok);
+ ++tokIndex;
+ } else {
+ all.push_back(comment);
+ ++commentIndex;
+ }
+ }
+ return all;
+}
+
+const Identifier *TranslationUnit::identifier(int index) const
{ return tokenAt(index).identifier; }
-const Literal *TranslationUnit::literal(unsigned index) const
+const Literal *TranslationUnit::literal(int index) const
{ return tokenAt(index).literal; }
-const StringLiteral *TranslationUnit::stringLiteral(unsigned index) const
+const StringLiteral *TranslationUnit::stringLiteral(int index) const
{ return tokenAt(index).string; }
-const NumericLiteral *TranslationUnit::numericLiteral(unsigned index) const
+const NumericLiteral *TranslationUnit::numericLiteral(int index) const
{ return tokenAt(index).number; }
-unsigned TranslationUnit::matchingBrace(unsigned index) const
+int TranslationUnit::matchingBrace(int index) const
{ return tokenAt(index).close_brace; }
MemoryPool *TranslationUnit::memoryPool() const
@@ -140,7 +174,7 @@ void TranslationUnit::tokenize()
lex.setLanguageFeatures(_languageFeatures);
lex.setScanCommentTokens(true);
- std::stack<unsigned> braces;
+ std::stack<int> braces;
_tokens->push_back(nullToken); // the first token needs to be invalid!
pushLineOffset(0);
@@ -153,8 +187,8 @@ void TranslationUnit::tokenize()
// We need to track information about the expanded tokens. A vector with an addition
// explicit index control is used instead of queue mainly for performance reasons.
- std::vector<std::pair<unsigned, unsigned> > lineColumn;
- unsigned lineColumnIdx = 0;
+ std::vector<std::pair<int, int> > lineColumn;
+ int lineColumnIdx = 0;
Token tk;
do {
@@ -162,7 +196,7 @@ void TranslationUnit::tokenize()
recognize:
if (tk.is(T_POUND) && tk.newline()) {
- const unsigned utf16CharOffset = tk.utf16charOffset;
+ const int utf16CharOffset = tk.utf16charOffset;
lex(&tk);
if (! tk.newline() && tk.is(T_IDENTIFIER) && tk.identifier == expansionId) {
@@ -175,10 +209,10 @@ recognize:
lex(&tk);
// Gather where the expansion happens and its length.
- //unsigned macroOffset = static_cast<unsigned>(strtoul(tk.spell(), 0, 0));
+ //int macroOffset = static_cast<int>(strtoul(tk.spell(), 0, 0));
lex(&tk);
lex(&tk); // Skip the separating comma
- //unsigned macroLength = static_cast<unsigned>(strtoul(tk.spell(), 0, 0));
+ //int macroLength = static_cast<int>(strtoul(tk.spell(), 0, 0));
lex(&tk);
// NOTE: We are currently not using the macro offset and length. They
@@ -198,7 +232,7 @@ recognize:
// Get the total number of generated tokens and specify "null"
// information for them.
unsigned totalGenerated =
- static_cast<unsigned>(strtoul(tk.spell(), 0, 0));
+ static_cast<int>(strtoul(tk.spell(), nullptr, 0));
const std::size_t previousSize = lineColumn.size();
lineColumn.resize(previousSize + totalGenerated);
std::fill(lineColumn.begin() + previousSize,
@@ -207,10 +241,10 @@ recognize:
lex(&tk);
} else if (tk.is(T_NUMERIC_LITERAL)) {
- unsigned line = static_cast<unsigned>(strtoul(tk.spell(), 0, 0));
+ int line = static_cast<int>(strtoul(tk.spell(), nullptr, 0));
lex(&tk);
lex(&tk); // Skip the separating colon
- unsigned column = static_cast<unsigned>(strtoul(tk.spell(), 0, 0));
+ int column = static_cast<int>(strtoul(tk.spell(), nullptr, 0));
// Store line and column for this non-generated token.
lineColumn.push_back(std::make_pair(line, column));
@@ -230,7 +264,7 @@ recognize:
if (! tk.newline() && tk.is(T_IDENTIFIER) && tk.identifier == lineId)
lex(&tk);
if (! tk.newline() && tk.is(T_NUMERIC_LITERAL)) {
- unsigned line = (unsigned) strtoul(tk.spell(), 0, 0);
+ int line = static_cast<int>(strtol(tk.spell(), nullptr, 0));
lex(&tk);
if (! tk.newline() && tk.is(T_STRING_LITERAL)) {
const StringLiteral *fileName =
@@ -244,12 +278,12 @@ recognize:
}
goto recognize;
} else if (tk.kind() == T_LBRACE) {
- braces.push(unsigned(_tokens->size()));
+ braces.push(int(_tokens->size()));
} else if (tk.kind() == T_RBRACE && ! braces.empty()) {
- const unsigned open_brace_index = braces.top();
+ const int open_brace_index = braces.top();
braces.pop();
if (open_brace_index < tokenCount())
- (*_tokens)[open_brace_index].close_brace = unsigned(_tokens->size());
+ (*_tokens)[open_brace_index].close_brace = int(_tokens->size());
} else if (tk.isComment()) {
_comments->push_back(tk);
continue; // comments are not in the regular token stream
@@ -258,9 +292,9 @@ recognize:
bool currentExpanded = false;
bool currentGenerated = false;
- if (!lineColumn.empty() && lineColumnIdx < lineColumn.size()) {
+ if (!lineColumn.empty() && lineColumnIdx < static_cast<int>(lineColumn.size())) {
currentExpanded = true;
- const std::pair<unsigned, unsigned> &p = lineColumn[lineColumnIdx];
+ const std::pair<int, int> &p = lineColumn[lineColumnIdx];
if (p.first)
_expandedLineColumn.insert(std::make_pair(tk.utf16charsBegin(), p));
else
@@ -276,8 +310,8 @@ recognize:
} while (tk.kind());
for (; ! braces.empty(); braces.pop()) {
- unsigned open_brace_index = braces.top();
- (*_tokens)[open_brace_index].close_brace = unsigned(_tokens->size());
+ int open_brace_index = braces.top();
+ (*_tokens)[open_brace_index].close_brace = int(_tokens->size());
}
}
@@ -302,31 +336,31 @@ bool TranslationUnit::parse(ParseMode mode)
switch (mode) {
case ParseTranlationUnit: {
- TranslationUnitAST *node = 0;
+ TranslationUnitAST *node = nullptr;
parsed = parser.parseTranslationUnit(node);
_ast = node;
} break;
case ParseDeclaration: {
- DeclarationAST *node = 0;
+ DeclarationAST *node = nullptr;
parsed = parser.parseDeclaration(node);
_ast = node;
} break;
case ParseExpression: {
- ExpressionAST *node = 0;
+ ExpressionAST *node = nullptr;
parsed = parser.parseExpression(node);
_ast = node;
} break;
case ParseDeclarator: {
- DeclaratorAST *node = 0;
- parsed = parser.parseDeclarator(node, /*decl_specifier_list =*/ 0);
+ DeclaratorAST *node = nullptr;
+ parsed = parser.parseDeclarator(node, /*decl_specifier_list =*/ nullptr);
_ast = node;
} break;
case ParseStatement: {
- StatementAST *node = 0;
+ StatementAST *node = nullptr;
parsed = parser.parseStatement(node);
_ast = node;
} break;
@@ -338,17 +372,17 @@ bool TranslationUnit::parse(ParseMode mode)
return parsed;
}
-void TranslationUnit::pushLineOffset(unsigned offset)
+void TranslationUnit::pushLineOffset(int offset)
{ _lineOffsets.push_back(offset); }
-void TranslationUnit::pushPreprocessorLine(unsigned utf16charOffset,
- unsigned line,
+void TranslationUnit::pushPreprocessorLine(int utf16charOffset,
+ int line,
const StringLiteral *fileName)
{ _ppLines.push_back(PPLine(utf16charOffset, line, fileName)); }
-unsigned TranslationUnit::findLineNumber(unsigned utf16charOffset) const
+int TranslationUnit::findLineNumber(int utf16charOffset) const
{
- std::vector<unsigned>::const_iterator it =
+ std::vector<int>::const_iterator it =
std::lower_bound(_lineOffsets.begin(), _lineOffsets.end(), utf16charOffset);
if (it != _lineOffsets.begin())
@@ -357,7 +391,7 @@ unsigned TranslationUnit::findLineNumber(unsigned utf16charOffset) const
return it - _lineOffsets.begin();
}
-TranslationUnit::PPLine TranslationUnit::findPreprocessorLine(unsigned utf16charOffset) const
+TranslationUnit::PPLine TranslationUnit::findPreprocessorLine(int utf16charOffset) const
{
std::vector<PPLine>::const_iterator it =
std::lower_bound(_ppLines.begin(), _ppLines.end(), PPLine(utf16charOffset));
@@ -368,7 +402,7 @@ TranslationUnit::PPLine TranslationUnit::findPreprocessorLine(unsigned utf16char
return *it;
}
-unsigned TranslationUnit::findColumnNumber(unsigned utf16CharOffset, unsigned lineNumber) const
+int TranslationUnit::findColumnNumber(int utf16CharOffset, int lineNumber) const
{
if (! utf16CharOffset)
return 0;
@@ -376,30 +410,66 @@ unsigned TranslationUnit::findColumnNumber(unsigned utf16CharOffset, unsigned li
return utf16CharOffset - _lineOffsets[lineNumber];
}
-void TranslationUnit::getTokenPosition(unsigned index,
- unsigned *line,
- unsigned *column,
- const StringLiteral **fileName) const
-{ return getPosition(tokenAt(index).utf16charsBegin(), line, column, fileName); }
+int TranslationUnit::getTokenPositionInDocument(int index, const QTextDocument *doc) const
+{
+ return getTokenPositionInDocument(_tokens->at(index), doc);
+}
-void TranslationUnit::getTokenStartPosition(unsigned index, unsigned *line,
- unsigned *column,
- const StringLiteral **fileName) const
-{ return getPosition(tokenAt(index).utf16charsBegin(), line, column, fileName); }
+int TranslationUnit::getTokenEndPositionInDocument(int index, const QTextDocument *doc) const
+{
+ return getTokenEndPositionInDocument(_tokens->at(index), doc);
+}
+
+void TranslationUnit::getTokenPosition(int index, int *line,
+ int *column,
+ const StringLiteral **fileName) const
+{
+ return getTokenPosition(_tokens->at(index), line, column, fileName);
+}
-void TranslationUnit::getTokenEndPosition(unsigned index, unsigned *line,
- unsigned *column,
+void TranslationUnit::getTokenEndPosition(int index, int *line,
+ int *column,
const StringLiteral **fileName) const
-{ return getPosition(tokenAt(index).utf16charsEnd(), line, column, fileName); }
+{
+ return getTokenEndPosition(_tokens->at(index), line, column, fileName);
+}
+
+void TranslationUnit::getTokenPosition(const Token &token, int *line, int *column,
+ const StringLiteral **fileName) const
+{
+ return getPosition(token.utf16charsBegin(), line, column, fileName);
+}
+
+void TranslationUnit::getTokenEndPosition(const Token &token, int *line,
+ int *column, const StringLiteral **fileName) const
+{
+ return getPosition(token.utf16charsEnd(), line, column, fileName);
+}
+
+int TranslationUnit::getTokenPositionInDocument(const Token token,
+ const QTextDocument *doc) const
+{
+ int line, column;
+ getTokenPosition(token, &line, &column);
+ return Utils::Text::positionInText(doc, line, column);
+}
+
+int TranslationUnit::getTokenEndPositionInDocument(const Token &token,
+ const QTextDocument *doc) const
+{
+ int line, column;
+ getTokenEndPosition(token, &line, &column);
+ return Utils::Text::positionInText(doc, line, column);
+}
-void TranslationUnit::getPosition(unsigned utf16charOffset,
- unsigned *line,
- unsigned *column,
+void TranslationUnit::getPosition(int utf16charOffset,
+ int *line,
+ int *column,
const StringLiteral **fileName) const
{
- unsigned lineNumber = 0;
- unsigned columnNumber = 0;
- const StringLiteral *file = 0;
+ int lineNumber = 0;
+ int columnNumber = 0;
+ const StringLiteral *file = nullptr;
// If this token is expanded we already have the information directly from the expansion
// section header. Otherwise, we need to calculate it.
@@ -433,22 +503,22 @@ void TranslationUnit::getPosition(unsigned utf16charOffset,
*fileName = file;
}
-void TranslationUnit::message(DiagnosticClient::Level level, unsigned index, const char *format, va_list args)
+void TranslationUnit::message(DiagnosticClient::Level level, int index, const char *format, va_list args)
{
if (f._blockErrors)
return;
index = std::min(index, tokenCount() - 1);
- unsigned line = 0, column = 0;
- const StringLiteral *fileName = 0;
+ int line = 0, column = 0;
+ const StringLiteral *fileName = nullptr;
getTokenPosition(index, &line, &column, &fileName);
if (DiagnosticClient *client = control()->diagnosticClient())
client->report(level, fileName, line, column, format, args);
}
-void TranslationUnit::warning(unsigned index, const char *format, ...)
+void TranslationUnit::warning(int index, const char *format, ...)
{
if (f._blockErrors)
return;
@@ -461,7 +531,7 @@ void TranslationUnit::warning(unsigned index, const char *format, ...)
va_end(args);
}
-void TranslationUnit::error(unsigned index, const char *format, ...)
+void TranslationUnit::error(int index, const char *format, ...)
{
if (f._blockErrors)
return;
@@ -474,7 +544,7 @@ void TranslationUnit::error(unsigned index, const char *format, ...)
va_end(args);
}
-void TranslationUnit::fatal(unsigned index, const char *format, ...)
+void TranslationUnit::fatal(int index, const char *format, ...)
{
if (f._blockErrors)
return;
@@ -487,13 +557,13 @@ void TranslationUnit::fatal(unsigned index, const char *format, ...)
va_end(args);
}
-unsigned TranslationUnit::findPreviousLineOffset(unsigned tokenIndex) const
+int TranslationUnit::findPreviousLineOffset(int tokenIndex) const
{
- unsigned lineOffset = _lineOffsets[findLineNumber(tokenAt(tokenIndex).utf16charsBegin())];
+ int lineOffset = _lineOffsets[findLineNumber(tokenAt(tokenIndex).utf16charsBegin())];
return lineOffset;
}
-bool TranslationUnit::maybeSplitGreaterGreaterToken(unsigned tokenIndex)
+bool TranslationUnit::maybeSplitGreaterGreaterToken(int tokenIndex)
{
if (tokenIndex >= tokenCount())
return false;
@@ -529,16 +599,16 @@ bool TranslationUnit::maybeSplitGreaterGreaterToken(unsigned tokenIndex)
void TranslationUnit::releaseTokensAndComments()
{
delete _tokens;
- _tokens = 0;
+ _tokens = nullptr;
delete _comments;
- _comments = 0;
+ _comments = nullptr;
}
void TranslationUnit::resetAST()
{
delete _pool;
- _pool = 0;
- _ast = 0;
+ _pool = nullptr;
+ _ast = nullptr;
}
void TranslationUnit::release()
diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.h b/src/libs/3rdparty/cplusplus/TranslationUnit.h
index 7def1a65a3..40f79d0091 100644
--- a/src/libs/3rdparty/cplusplus/TranslationUnit.h
+++ b/src/libs/3rdparty/cplusplus/TranslationUnit.h
@@ -28,6 +28,10 @@
#include <unordered_map>
#include <vector>
+QT_BEGIN_NAMESPACE
+class QTextDocument;
+QT_END_NAMESPACE
+
namespace CPlusPlus {
class CPLUSPLUS_EXPORT TranslationUnit
@@ -43,29 +47,32 @@ public:
const StringLiteral *fileId() const;
const char *fileName() const;
- unsigned fileNameLength() const;
+ int fileNameLength() const;
const char *firstSourceChar() const;
const char *lastSourceChar() const;
- unsigned sourceLength() const;
+ int sourceLength() const;
- void setSource(const char *source, unsigned size);
+ void setSource(const char *source, int size);
- unsigned tokenCount() const { return _tokens ? unsigned(_tokens->size()) : unsigned(0); }
- const Token &tokenAt(unsigned index) const
+ int tokenCount() const { return _tokens ? int(_tokens->size()) : 0; }
+ const Token &tokenAt(int index) const
{ return _tokens && index < tokenCount() ? (*_tokens)[index] : nullToken; }
- Kind tokenKind(unsigned index) const { return tokenAt(index).kind(); }
- const char *spell(unsigned index) const;
+ Kind tokenKind(int index) const { return tokenAt(index).kind(); }
+ const char *spell(int index) const;
+
+ int commentCount() const;
+ const Token &commentAt(int index) const;
- unsigned commentCount() const;
- const Token &commentAt(unsigned index) const;
+ // Including comments.
+ std::vector<Token> allTokens() const;
- unsigned matchingBrace(unsigned index) const;
- const Identifier *identifier(unsigned index) const;
- const Literal *literal(unsigned index) const;
- const StringLiteral *stringLiteral(unsigned index) const;
- const NumericLiteral *numericLiteral(unsigned index) const;
+ int matchingBrace(int index) const;
+ const Identifier *identifier(int index) const;
+ const Literal *literal(int index) const;
+ const StringLiteral *stringLiteral(int index) const;
+ const NumericLiteral *numericLiteral(int index) const;
MemoryPool *memoryPool() const;
AST *ast() const;
@@ -78,11 +85,11 @@ public:
return previous;
}
- void warning(unsigned index, const char *fmt, ...);
- void error(unsigned index, const char *fmt, ...);
- void fatal(unsigned index, const char *fmt, ...);
+ void warning(int index, const char *fmt, ...);
+ void error(int index, const char *fmt, ...);
+ void fatal(int index, const char *fmt, ...);
- void message(DiagnosticClient::Level level, unsigned index,
+ void message(DiagnosticClient::Level level, int index,
const char *format, va_list ap);
bool isTokenized() const;
@@ -106,45 +113,48 @@ public:
void resetAST();
void release();
- void getTokenStartPosition(unsigned index, unsigned *line,
- unsigned *column = 0,
- const StringLiteral **fileName = 0) const;
-
- void getTokenEndPosition(unsigned index, unsigned *line,
- unsigned *column = 0,
- const StringLiteral **fileName = 0) const;
-
- void getPosition(unsigned utf16charOffset,
- unsigned *line,
- unsigned *column = 0,
- const StringLiteral **fileName = 0) const;
-
- void getTokenPosition(unsigned index,
- unsigned *line,
- unsigned *column = 0,
- const StringLiteral **fileName = 0) const;
-
- void pushLineOffset(unsigned offset);
- void pushPreprocessorLine(unsigned utf16charOffset,
- unsigned line,
+ void getTokenPosition(int index, int *line,
+ int *column = nullptr,
+ const StringLiteral **fileName = nullptr) const;
+ void getTokenEndPosition(int index, int *line,
+ int *column = nullptr,
+ const StringLiteral **fileName = nullptr) const;
+ void getPosition(int utf16charOffset,
+ int *line,
+ int *column = nullptr,
+ const StringLiteral **fileName = nullptr) const;
+
+ int getTokenPositionInDocument(int index, const QTextDocument *doc) const;
+ int getTokenEndPositionInDocument(int index, const QTextDocument *doc) const;
+
+ void getTokenPosition(const Token &token, int *line, int *column = nullptr,
+ const StringLiteral **fileName = nullptr) const;
+ void getTokenEndPosition(const Token &token, int *line, int *column = nullptr,
+ const StringLiteral **fileName = nullptr) const;
+ int getTokenPositionInDocument(const Token token, const QTextDocument *doc) const;
+ int getTokenEndPositionInDocument(const Token &token, const QTextDocument *doc) const;
+
+ void pushLineOffset(int offset);
+ void pushPreprocessorLine(int utf16charOffset,
+ int line,
const StringLiteral *fileName);
- unsigned findPreviousLineOffset(unsigned tokenIndex) const;
+ int findPreviousLineOffset(int tokenIndex) const;
- bool maybeSplitGreaterGreaterToken(unsigned tokenIndex);
+ bool maybeSplitGreaterGreaterToken(int tokenIndex);
LanguageFeatures languageFeatures() const { return _languageFeatures; }
void setLanguageFeatures(LanguageFeatures features) { _languageFeatures = features; }
private:
struct PPLine {
- unsigned utf16charOffset;
- unsigned line;
+ int utf16charOffset;
+ int line;
const StringLiteral *fileName;
- PPLine(unsigned utf16charOffset = 0,
- unsigned line = 0,
- const StringLiteral *fileName = 0)
+ PPLine(int utf16charOffset = 0,
+ int line = 0,
+ const StringLiteral *fileName = nullptr)
: utf16charOffset(utf16charOffset), line(line), fileName(fileName)
{ }
@@ -159,9 +169,9 @@ private:
};
void releaseTokensAndComments();
- unsigned findLineNumber(unsigned utf16charOffset) const;
- unsigned findColumnNumber(unsigned utf16CharOffset, unsigned lineNumber) const;
- PPLine findPreprocessorLine(unsigned utf16charOffset) const;
+ int findLineNumber(int utf16charOffset) const;
+ int findColumnNumber(int utf16CharOffset, int lineNumber) const;
+ PPLine findPreprocessorLine(int utf16charOffset) const;
static const Token nullToken;
@@ -171,9 +181,9 @@ private:
const char *_lastSourceChar;
std::vector<Token> *_tokens;
std::vector<Token> *_comments;
- std::vector<unsigned> _lineOffsets;
+ std::vector<int> _lineOffsets;
std::vector<PPLine> _ppLines;
- typedef std::unordered_map<unsigned, std::pair<unsigned, unsigned> > TokenLineColumn;
+ typedef std::unordered_map<unsigned, std::pair<int, int> > TokenLineColumn;
TokenLineColumn _expandedLineColumn;
MemoryPool *_pool;
AST *_ast;
diff --git a/src/libs/3rdparty/cplusplus/Type.cpp b/src/libs/3rdparty/cplusplus/Type.cpp
index bad4d42eeb..05d8d56d2d 100644
--- a/src/libs/3rdparty/cplusplus/Type.cpp
+++ b/src/libs/3rdparty/cplusplus/Type.cpp
@@ -22,75 +22,13 @@
#include "Type.h"
#include "TypeVisitor.h"
#include "CoreTypes.h"
-#include "Symbols.h"
using namespace CPlusPlus;
-Type::Type()
-{ }
-
-Type::~Type()
-{ }
+Type::~Type() = default;
bool Type::isUndefinedType() const
-{ return this == UndefinedType::instance(); }
-
-bool Type::isVoidType() const
-{ return asVoidType() != 0; }
-
-bool Type::isIntegerType() const
-{ return asIntegerType() != 0; }
-
-bool Type::isFloatType() const
-{ return asFloatType() != 0; }
-
-bool Type::isPointerType() const
-{ return asPointerType() != 0; }
-
-bool Type::isPointerToMemberType() const
-{ return asPointerToMemberType() != 0; }
-
-bool Type::isReferenceType() const
-{ return asReferenceType() != 0; }
-
-bool Type::isArrayType() const
-{ return asArrayType() != 0; }
-
-bool Type::isNamedType() const
-{ return asNamedType() != 0; }
-
-bool Type::isFunctionType() const
-{ return asFunctionType() != 0; }
-
-bool Type::isNamespaceType() const
-{ return asNamespaceType() != 0; }
-
-bool Type::isTemplateType() const
-{ return asTemplateType() != 0; }
-
-bool Type::isClassType() const
-{ return asClassType() != 0; }
-
-bool Type::isEnumType() const
-{ return asEnumType() != 0; }
-
-bool Type::isForwardClassDeclarationType() const
-{ return asForwardClassDeclarationType() != 0; }
-
-bool Type::isObjCClassType() const
-{ return asObjCClassType() != 0; }
-
-bool Type::isObjCProtocolType() const
-{ return asObjCProtocolType() != 0; }
-
-bool Type::isObjCMethodType() const
-{ return asObjCMethodType() != 0; }
-
-bool Type::isObjCForwardClassDeclarationType() const
-{ return asObjCForwardClassDeclarationType() != 0; }
-
-bool Type::isObjCForwardProtocolDeclarationType() const
-{ return asObjCForwardProtocolDeclarationType() != 0; }
+{ return this == &UndefinedType::instance; }
void Type::accept(TypeVisitor *visitor)
{
diff --git a/src/libs/3rdparty/cplusplus/Type.h b/src/libs/3rdparty/cplusplus/Type.h
index 25f260c2cd..44438ecef9 100644
--- a/src/libs/3rdparty/cplusplus/Type.h
+++ b/src/libs/3rdparty/cplusplus/Type.h
@@ -27,76 +27,57 @@ namespace CPlusPlus {
class CPLUSPLUS_EXPORT Type
{
public:
- Type();
+ Type() = default;
virtual ~Type();
bool isUndefinedType() const;
- bool isVoidType() const;
- bool isIntegerType() const;
- bool isFloatType() const;
- bool isPointerType() const;
- bool isPointerToMemberType() const;
- bool isReferenceType() const;
- bool isArrayType() const;
- bool isNamedType() const;
- bool isFunctionType() const;
- bool isNamespaceType() const;
- bool isTemplateType() const;
- bool isClassType() const;
- bool isEnumType() const;
- bool isForwardClassDeclarationType() const;
- bool isObjCClassType() const;
- bool isObjCProtocolType() const;
- bool isObjCMethodType() const;
- bool isObjCForwardClassDeclarationType() const;
- bool isObjCForwardProtocolDeclarationType() const;
- virtual const UndefinedType *asUndefinedType() const { return 0; }
- virtual const VoidType *asVoidType() const { return 0; }
- virtual const IntegerType *asIntegerType() const { return 0; }
- virtual const FloatType *asFloatType() const { return 0; }
- virtual const PointerType *asPointerType() const { return 0; }
- virtual const PointerToMemberType *asPointerToMemberType() const { return 0; }
- virtual const ReferenceType *asReferenceType() const { return 0; }
- virtual const ArrayType *asArrayType() const { return 0; }
- virtual const NamedType *asNamedType() const { return 0; }
- virtual const Function *asFunctionType() const { return 0; }
- virtual const Namespace *asNamespaceType() const { return 0; }
- virtual const Template *asTemplateType() const { return 0; }
- virtual const Class *asClassType() const { return 0; }
- virtual const Enum *asEnumType() const { return 0; }
- virtual const ForwardClassDeclaration *asForwardClassDeclarationType() const { return 0; }
- virtual const ObjCClass *asObjCClassType() const { return 0; }
- virtual const ObjCProtocol *asObjCProtocolType() const { return 0; }
- virtual const ObjCMethod *asObjCMethodType() const { return 0; }
- virtual const ObjCForwardClassDeclaration *asObjCForwardClassDeclarationType() const { return 0; }
- virtual const ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclarationType() const { return 0; }
+ virtual const UndefinedType *asUndefinedType() const { return nullptr; }
+ virtual const VoidType *asVoidType() const { return nullptr; }
+ virtual const IntegerType *asIntegerType() const { return nullptr; }
+ virtual const FloatType *asFloatType() const { return nullptr; }
+ virtual const PointerType *asPointerType() const { return nullptr; }
+ virtual const PointerToMemberType *asPointerToMemberType() const { return nullptr; }
+ virtual const ReferenceType *asReferenceType() const { return nullptr; }
+ virtual const ArrayType *asArrayType() const { return nullptr; }
+ virtual const NamedType *asNamedType() const { return nullptr; }
+ virtual const Function *asFunctionType() const { return nullptr; }
+ virtual const Namespace *asNamespaceType() const { return nullptr; }
+ virtual const Template *asTemplateType() const { return nullptr; }
+ virtual const Class *asClassType() const { return nullptr; }
+ virtual const Enum *asEnumType() const { return nullptr; }
+ virtual const ForwardClassDeclaration *asForwardClassDeclarationType() const { return nullptr; }
+ virtual const ObjCClass *asObjCClassType() const { return nullptr; }
+ virtual const ObjCProtocol *asObjCProtocolType() const { return nullptr; }
+ virtual const ObjCMethod *asObjCMethodType() const { return nullptr; }
+ virtual const ObjCForwardClassDeclaration *asObjCForwardClassDeclarationType() const { return nullptr; }
+ virtual const ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclarationType() const { return nullptr; }
- virtual UndefinedType *asUndefinedType() { return 0; }
- virtual VoidType *asVoidType() { return 0; }
- virtual IntegerType *asIntegerType() { return 0; }
- virtual FloatType *asFloatType() { return 0; }
- virtual PointerType *asPointerType() { return 0; }
- virtual PointerToMemberType *asPointerToMemberType() { return 0; }
- virtual ReferenceType *asReferenceType() { return 0; }
- virtual ArrayType *asArrayType() { return 0; }
- virtual NamedType *asNamedType() { return 0; }
- virtual Function *asFunctionType() { return 0; }
- virtual Namespace *asNamespaceType() { return 0; }
- virtual Template *asTemplateType() { return 0; }
- virtual Class *asClassType() { return 0; }
- virtual Enum *asEnumType() { return 0; }
- virtual ForwardClassDeclaration *asForwardClassDeclarationType() { return 0; }
- virtual ObjCClass *asObjCClassType() { return 0; }
- virtual ObjCProtocol *asObjCProtocolType() { return 0; }
- virtual ObjCMethod *asObjCMethodType() { return 0; }
- virtual ObjCForwardClassDeclaration *asObjCForwardClassDeclarationType() { return 0; }
- virtual ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclarationType() { return 0; }
+ virtual UndefinedType *asUndefinedType() { return nullptr; }
+ virtual VoidType *asVoidType() { return nullptr; }
+ virtual IntegerType *asIntegerType() { return nullptr; }
+ virtual FloatType *asFloatType() { return nullptr; }
+ virtual PointerType *asPointerType() { return nullptr; }
+ virtual PointerToMemberType *asPointerToMemberType() { return nullptr; }
+ virtual ReferenceType *asReferenceType() { return nullptr; }
+ virtual ArrayType *asArrayType() { return nullptr; }
+ virtual NamedType *asNamedType() { return nullptr; }
+ virtual Function *asFunctionType() { return nullptr; }
+ virtual Namespace *asNamespaceType() { return nullptr; }
+ virtual Template *asTemplateType() { return nullptr; }
+ virtual Class *asClassType() { return nullptr; }
+ virtual Enum *asEnumType() { return nullptr; }
+ virtual ForwardClassDeclaration *asForwardClassDeclarationType() { return nullptr; }
+ virtual ObjCClass *asObjCClassType() { return nullptr; }
+ virtual ObjCProtocol *asObjCProtocolType() { return nullptr; }
+ virtual ObjCMethod *asObjCMethodType() { return nullptr; }
+ virtual ObjCForwardClassDeclaration *asObjCForwardClassDeclarationType() { return nullptr; }
+ virtual ObjCForwardProtocolDeclaration *asObjCForwardProtocolDeclarationType() { return nullptr; }
void accept(TypeVisitor *visitor);
static void accept(Type *type, TypeVisitor *visitor);
- bool match(const Type *other, Matcher *matcher = 0) const;
+ bool match(const Type *other, Matcher *matcher = nullptr) const;
protected:
virtual void accept0(TypeVisitor *visitor) = 0;
diff --git a/src/libs/3rdparty/cplusplus/cplusplus.pri b/src/libs/3rdparty/cplusplus/cplusplus.pri
deleted file mode 100644
index 2a4f71a712..0000000000
--- a/src/libs/3rdparty/cplusplus/cplusplus.pri
+++ /dev/null
@@ -1,69 +0,0 @@
-HEADERS += \
- $$PWD/cppassert.h \
- $$PWD/CPlusPlus.h \
- $$PWD/AST.h \
- $$PWD/ASTVisitor.h \
- $$PWD/ASTMatcher.h \
- $$PWD/ASTPatternBuilder.h \
- $$PWD/ASTfwd.h \
- $$PWD/Matcher.h \
- $$PWD/CPlusPlusForwardDeclarations.h \
- $$PWD/Control.h \
- $$PWD/CoreTypes.h \
- $$PWD/DiagnosticClient.h \
- $$PWD/FullySpecifiedType.h \
- $$PWD/Lexer.h \
- $$PWD/LiteralTable.h \
- $$PWD/Literals.h \
- $$PWD/MemoryPool.h \
- $$PWD/Name.h \
- $$PWD/NameVisitor.h \
- $$PWD/Names.h \
- $$PWD/Parser.h \
- $$PWD/Scope.h \
- $$PWD/Bind.h \
- $$PWD/Symbol.h \
- $$PWD/Symbols.h \
- $$PWD/SymbolVisitor.h \
- $$PWD/Token.h \
- $$PWD/TranslationUnit.h \
- $$PWD/Type.h \
- $$PWD/TypeVisitor.h \
- $$PWD/ObjectiveCTypeQualifiers.h \
- $$PWD/QtContextKeywords.h \
- $$PWD/Templates.h \
- $$PWD/SafeMatcher.h
-
-SOURCES += \
- $$PWD/AST.cpp \
- $$PWD/ASTVisit.cpp \
- $$PWD/ASTMatch0.cpp \
- $$PWD/ASTVisitor.cpp \
- $$PWD/ASTClone.cpp \
- $$PWD/ASTMatcher.cpp \
- $$PWD/Matcher.cpp \
- $$PWD/Control.cpp \
- $$PWD/CoreTypes.cpp \
- $$PWD/DiagnosticClient.cpp \
- $$PWD/FullySpecifiedType.cpp \
- $$PWD/Keywords.cpp \
- $$PWD/ObjectiveCAtKeywords.cpp \
- $$PWD/ObjectiveCTypeQualifiers.cpp \
- $$PWD/Lexer.cpp \
- $$PWD/Literals.cpp \
- $$PWD/MemoryPool.cpp \
- $$PWD/Name.cpp \
- $$PWD/NameVisitor.cpp \
- $$PWD/Names.cpp \
- $$PWD/Parser.cpp \
- $$PWD/Scope.cpp \
- $$PWD/Bind.cpp \
- $$PWD/Symbol.cpp \
- $$PWD/Symbols.cpp \
- $$PWD/Token.cpp \
- $$PWD/TranslationUnit.cpp \
- $$PWD/Type.cpp \
- $$PWD/TypeVisitor.cpp \
- $$PWD/QtContextKeywords.cpp \
- $$PWD/Templates.cpp \
- $$PWD/SafeMatcher.cpp
diff --git a/src/libs/3rdparty/googletest b/src/libs/3rdparty/googletest
new file mode 160000
+Subproject b796f7d44681514f58a683a3a71ff17c94edb0c
diff --git a/src/libs/3rdparty/json/LICENSE.MIT b/src/libs/3rdparty/json/LICENSE.MIT
new file mode 100644
index 0000000000..1c1f7a690d
--- /dev/null
+++ b/src/libs/3rdparty/json/LICENSE.MIT
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2013-2022 Niels Lohmann
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/libs/3rdparty/json/json.hpp b/src/libs/3rdparty/json/json.hpp
new file mode 100644
index 0000000000..e11f529688
--- /dev/null
+++ b/src/libs/3rdparty/json/json.hpp
@@ -0,0 +1,24640 @@
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+/****************************************************************************\
+ * Note on documentation: The source files contain links to the online *
+ * documentation of the public API at https://json.nlohmann.me. This URL *
+ * contains the most recent documentation and should also be applicable to *
+ * previous versions; documentation for deprecated functions is not *
+ * removed, but marked deprecated. See "Generate documentation" section in *
+ * file docs/README.md. *
+\****************************************************************************/
+
+#ifndef INCLUDE_NLOHMANN_JSON_HPP_
+#define INCLUDE_NLOHMANN_JSON_HPP_
+
+#include <algorithm> // all_of, find, for_each
+#include <cstddef> // nullptr_t, ptrdiff_t, size_t
+#include <functional> // hash, less
+#include <initializer_list> // initializer_list
+#ifndef JSON_NO_IO
+ #include <iosfwd> // istream, ostream
+#endif // JSON_NO_IO
+#include <iterator> // random_access_iterator_tag
+#include <memory> // unique_ptr
+#include <string> // string, stoi, to_string
+#include <utility> // declval, forward, move, pair, swap
+#include <vector> // vector
+
+// #include <nlohmann/adl_serializer.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <utility>
+
+// #include <nlohmann/detail/abi_macros.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// This file contains all macro definitions affecting or depending on the ABI
+
+#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
+ #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
+ #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2
+ #warning "Already included a different version of the library!"
+ #endif
+ #endif
+#endif
+
+#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
+#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum)
+#define NLOHMANN_JSON_VERSION_PATCH 2 // NOLINT(modernize-macro-to-enum)
+
+#ifndef JSON_DIAGNOSTICS
+ #define JSON_DIAGNOSTICS 0
+#endif
+
+#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+ #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
+#endif
+
+#if JSON_DIAGNOSTICS
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
+#else
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
+#endif
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+ #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
+#else
+ #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
+ #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
+#endif
+
+// Construct the namespace ABI tags component
+#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b
+#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \
+ NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)
+
+#define NLOHMANN_JSON_ABI_TAGS \
+ NLOHMANN_JSON_ABI_TAGS_CONCAT( \
+ NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
+ NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)
+
+// Construct the namespace version component
+#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
+ _v ## major ## _ ## minor ## _ ## patch
+#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
+ NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
+
+#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
+#define NLOHMANN_JSON_NAMESPACE_VERSION
+#else
+#define NLOHMANN_JSON_NAMESPACE_VERSION \
+ NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
+ NLOHMANN_JSON_VERSION_MINOR, \
+ NLOHMANN_JSON_VERSION_PATCH)
+#endif
+
+// Combine namespace components
+#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
+#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
+ NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
+
+#ifndef NLOHMANN_JSON_NAMESPACE
+#define NLOHMANN_JSON_NAMESPACE \
+ nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
+ NLOHMANN_JSON_ABI_TAGS, \
+ NLOHMANN_JSON_NAMESPACE_VERSION)
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
+#define NLOHMANN_JSON_NAMESPACE_BEGIN \
+ namespace nlohmann \
+ { \
+ inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
+ NLOHMANN_JSON_ABI_TAGS, \
+ NLOHMANN_JSON_NAMESPACE_VERSION) \
+ {
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_END
+#define NLOHMANN_JSON_NAMESPACE_END \
+ } /* namespace (inline namespace) NOLINT(readability/namespace) */ \
+ } // namespace nlohmann
+#endif
+
+// #include <nlohmann/detail/conversions/from_json.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // transform
+#include <array> // array
+#include <forward_list> // forward_list
+#include <iterator> // inserter, front_inserter, end
+#include <map> // map
+#include <string> // string
+#include <tuple> // tuple, make_tuple
+#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
+#include <unordered_map> // unordered_map
+#include <utility> // pair, declval
+#include <valarray> // valarray
+
+// #include <nlohmann/detail/exceptions.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // nullptr_t
+#include <exception> // exception
+#if JSON_DIAGNOSTICS
+ #include <numeric> // accumulate
+#endif
+#include <stdexcept> // runtime_error
+#include <string> // to_string
+#include <vector> // vector
+
+// #include <nlohmann/detail/value_t.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t
+#include <string> // string
+
+// #include <nlohmann/detail/macro_scope.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <utility> // declval, pair
+// #include <nlohmann/detail/meta/detected.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <type_traits>
+
+// #include <nlohmann/detail/meta/void_t.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename ...Ts> struct make_void
+{
+ using type = void;
+};
+template<typename ...Ts> using void_t = typename make_void<Ts...>::type;
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// https://en.cppreference.com/w/cpp/experimental/is_detected
+struct nonesuch
+{
+ nonesuch() = delete;
+ ~nonesuch() = delete;
+ nonesuch(nonesuch const&) = delete;
+ nonesuch(nonesuch const&&) = delete;
+ void operator=(nonesuch const&) = delete;
+ void operator=(nonesuch&&) = delete;
+};
+
+template<class Default,
+ class AlwaysVoid,
+ template<class...> class Op,
+ class... Args>
+struct detector
+{
+ using value_t = std::false_type;
+ using type = Default;
+};
+
+template<class Default, template<class...> class Op, class... Args>
+struct detector<Default, void_t<Op<Args...>>, Op, Args...>
+{
+ using value_t = std::true_type;
+ using type = Op<Args...>;
+};
+
+template<template<class...> class Op, class... Args>
+using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
+
+template<template<class...> class Op, class... Args>
+struct is_detected_lazy : is_detected<Op, Args...> { };
+
+template<template<class...> class Op, class... Args>
+using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
+
+template<class Default, template<class...> class Op, class... Args>
+using detected_or = detector<Default, void, Op, Args...>;
+
+template<class Default, template<class...> class Op, class... Args>
+using detected_or_t = typename detected_or<Default, Op, Args...>::type;
+
+template<class Expected, template<class...> class Op, class... Args>
+using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
+
+template<class To, template<class...> class Op, class... Args>
+using is_detected_convertible =
+ std::is_convertible<detected_t<Op, Args...>, To>;
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/thirdparty/hedley/hedley.hpp>
+
+
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson <evan@nemerson.com>
+// SPDX-License-Identifier: MIT
+
+/* Hedley - https://nemequ.github.io/hedley
+ * Created by Evan Nemerson <evan@nemerson.com>
+ */
+
+#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15)
+#if defined(JSON_HEDLEY_VERSION)
+ #undef JSON_HEDLEY_VERSION
+#endif
+#define JSON_HEDLEY_VERSION 15
+
+#if defined(JSON_HEDLEY_STRINGIFY_EX)
+ #undef JSON_HEDLEY_STRINGIFY_EX
+#endif
+#define JSON_HEDLEY_STRINGIFY_EX(x) #x
+
+#if defined(JSON_HEDLEY_STRINGIFY)
+ #undef JSON_HEDLEY_STRINGIFY
+#endif
+#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)
+
+#if defined(JSON_HEDLEY_CONCAT_EX)
+ #undef JSON_HEDLEY_CONCAT_EX
+#endif
+#define JSON_HEDLEY_CONCAT_EX(a,b) a##b
+
+#if defined(JSON_HEDLEY_CONCAT)
+ #undef JSON_HEDLEY_CONCAT
+#endif
+#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)
+
+#if defined(JSON_HEDLEY_CONCAT3_EX)
+ #undef JSON_HEDLEY_CONCAT3_EX
+#endif
+#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c
+
+#if defined(JSON_HEDLEY_CONCAT3)
+ #undef JSON_HEDLEY_CONCAT3
+#endif
+#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c)
+
+#if defined(JSON_HEDLEY_VERSION_ENCODE)
+ #undef JSON_HEDLEY_VERSION_ENCODE
+#endif
+#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)
+ #undef JSON_HEDLEY_VERSION_DECODE_MAJOR
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)
+ #undef JSON_HEDLEY_VERSION_DECODE_MINOR
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)
+ #undef JSON_HEDLEY_VERSION_DECODE_REVISION
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)
+
+#if defined(JSON_HEDLEY_GNUC_VERSION)
+ #undef JSON_HEDLEY_GNUC_VERSION
+#endif
+#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)
+ #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#elif defined(__GNUC__)
+ #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)
+ #undef JSON_HEDLEY_GNUC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_GNUC_VERSION)
+ #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_MSVC_VERSION)
+ #undef JSON_HEDLEY_MSVC_VERSION
+#endif
+#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL)
+ #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)
+#elif defined(_MSC_FULL_VER) && !defined(__ICL)
+ #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)
+#elif defined(_MSC_VER) && !defined(__ICL)
+ #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)
+ #undef JSON_HEDLEY_MSVC_VERSION_CHECK
+#endif
+#if !defined(JSON_HEDLEY_MSVC_VERSION)
+ #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)
+#elif defined(_MSC_VER) && (_MSC_VER >= 1400)
+ #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+ #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))
+#else
+ #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_VERSION)
+ #undef JSON_HEDLEY_INTEL_VERSION
+#endif
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL)
+ #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)
+#elif defined(__INTEL_COMPILER) && !defined(__ICL)
+ #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)
+ #undef JSON_HEDLEY_INTEL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_INTEL_VERSION)
+ #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+ #undef JSON_HEDLEY_INTEL_CL_VERSION
+#endif
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL)
+ #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK)
+ #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+ #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_PGI_VERSION)
+ #undef JSON_HEDLEY_PGI_VERSION
+#endif
+#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)
+ #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)
+#endif
+
+#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)
+ #undef JSON_HEDLEY_PGI_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_PGI_VERSION)
+ #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_SUNPRO_VERSION)
+ #undef JSON_HEDLEY_SUNPRO_VERSION
+#endif
+#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
+ #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)
+#elif defined(__SUNPRO_C)
+ #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)
+#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
+ #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)
+#elif defined(__SUNPRO_CC)
+ #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)
+#endif
+
+#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)
+ #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_SUNPRO_VERSION)
+ #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)
+ #undef JSON_HEDLEY_EMSCRIPTEN_VERSION
+#endif
+#if defined(__EMSCRIPTEN__)
+ #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)
+#endif
+
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)
+ #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)
+ #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_ARM_VERSION)
+ #undef JSON_HEDLEY_ARM_VERSION
+#endif
+#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)
+ #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)
+#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)
+ #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)
+#endif
+
+#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)
+ #undef JSON_HEDLEY_ARM_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_ARM_VERSION)
+ #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_IBM_VERSION)
+ #undef JSON_HEDLEY_IBM_VERSION
+#endif
+#if defined(__ibmxl__)
+ #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)
+#elif defined(__xlC__) && defined(__xlC_ver__)
+ #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)
+#elif defined(__xlC__)
+ #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)
+#endif
+
+#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)
+ #undef JSON_HEDLEY_IBM_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_IBM_VERSION)
+ #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_VERSION)
+ #undef JSON_HEDLEY_TI_VERSION
+#endif
+#if \
+ defined(__TI_COMPILER_VERSION__) && \
+ ( \
+ defined(__TMS470__) || defined(__TI_ARM__) || \
+ defined(__MSP430__) || \
+ defined(__TMS320C2000__) \
+ )
+#if (__TI_COMPILER_VERSION__ >= 16000000)
+ #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+#endif
+
+#if defined(JSON_HEDLEY_TI_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_VERSION)
+ #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION)
+ #undef JSON_HEDLEY_TI_CL2000_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__)
+ #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION)
+ #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL430_VERSION)
+ #undef JSON_HEDLEY_TI_CL430_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__)
+ #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL430_VERSION)
+ #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)
+ #undef JSON_HEDLEY_TI_ARMCL_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__))
+ #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)
+ #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION)
+ #undef JSON_HEDLEY_TI_CL6X_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__)
+ #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION)
+ #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION)
+ #undef JSON_HEDLEY_TI_CL7X_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__)
+ #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION)
+ #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)
+ #undef JSON_HEDLEY_TI_CLPRU_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__)
+ #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)
+ #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_CRAY_VERSION)
+ #undef JSON_HEDLEY_CRAY_VERSION
+#endif
+#if defined(_CRAYC)
+ #if defined(_RELEASE_PATCHLEVEL)
+ #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)
+ #else
+ #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)
+ #endif
+#endif
+
+#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)
+ #undef JSON_HEDLEY_CRAY_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_CRAY_VERSION)
+ #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_IAR_VERSION)
+ #undef JSON_HEDLEY_IAR_VERSION
+#endif
+#if defined(__IAR_SYSTEMS_ICC__)
+ #if __VER__ > 1000
+ #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))
+ #else
+ #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0)
+ #endif
+#endif
+
+#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)
+ #undef JSON_HEDLEY_IAR_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_IAR_VERSION)
+ #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TINYC_VERSION)
+ #undef JSON_HEDLEY_TINYC_VERSION
+#endif
+#if defined(__TINYC__)
+ #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)
+#endif
+
+#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)
+ #undef JSON_HEDLEY_TINYC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TINYC_VERSION)
+ #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_DMC_VERSION)
+ #undef JSON_HEDLEY_DMC_VERSION
+#endif
+#if defined(__DMC__)
+ #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)
+#endif
+
+#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)
+ #undef JSON_HEDLEY_DMC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_DMC_VERSION)
+ #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_COMPCERT_VERSION)
+ #undef JSON_HEDLEY_COMPCERT_VERSION
+#endif
+#if defined(__COMPCERT_VERSION__)
+ #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)
+#endif
+
+#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)
+ #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_COMPCERT_VERSION)
+ #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_PELLES_VERSION)
+ #undef JSON_HEDLEY_PELLES_VERSION
+#endif
+#if defined(__POCC__)
+ #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)
+ #undef JSON_HEDLEY_PELLES_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_PELLES_VERSION)
+ #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+ #undef JSON_HEDLEY_MCST_LCC_VERSION
+#endif
+#if defined(__LCC__) && defined(__LCC_MINOR__)
+ #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__)
+#endif
+
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK)
+ #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+ #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_VERSION)
+ #undef JSON_HEDLEY_GCC_VERSION
+#endif
+#if \
+ defined(JSON_HEDLEY_GNUC_VERSION) && \
+ !defined(__clang__) && \
+ !defined(JSON_HEDLEY_INTEL_VERSION) && \
+ !defined(JSON_HEDLEY_PGI_VERSION) && \
+ !defined(JSON_HEDLEY_ARM_VERSION) && \
+ !defined(JSON_HEDLEY_CRAY_VERSION) && \
+ !defined(JSON_HEDLEY_TI_VERSION) && \
+ !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \
+ !defined(JSON_HEDLEY_TI_CL430_VERSION) && \
+ !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \
+ !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \
+ !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \
+ !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \
+ !defined(__COMPCERT__) && \
+ !defined(JSON_HEDLEY_MCST_LCC_VERSION)
+ #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION
+#endif
+
+#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)
+ #undef JSON_HEDLEY_GCC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_GCC_VERSION)
+ #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)
+ #undef JSON_HEDLEY_HAS_ATTRIBUTE
+#endif
+#if \
+ defined(__has_attribute) && \
+ ( \
+ (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \
+ )
+# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)
+#else
+# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)
+ #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
+#endif
+#if defined(__has_attribute)
+ #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)
+ #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
+#endif
+#if defined(__has_attribute)
+ #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+#else
+ #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)
+ #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
+#endif
+#if \
+ defined(__has_cpp_attribute) && \
+ defined(__cplusplus) && \
+ (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))
+ #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)
+#else
+ #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)
+ #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
+#endif
+#if !defined(__cplusplus) || !defined(__has_cpp_attribute)
+ #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)
+#elif \
+ !defined(JSON_HEDLEY_PGI_VERSION) && \
+ !defined(JSON_HEDLEY_IAR_VERSION) && \
+ (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \
+ (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))
+ #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)
+#else
+ #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)
+ #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
+#endif
+#if defined(__has_cpp_attribute) && defined(__cplusplus)
+ #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)
+ #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
+#endif
+#if defined(__has_cpp_attribute) && defined(__cplusplus)
+ #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
+#else
+ #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_BUILTIN)
+ #undef JSON_HEDLEY_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+ #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)
+#else
+ #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)
+ #undef JSON_HEDLEY_GNUC_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+ #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)
+ #undef JSON_HEDLEY_GCC_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+ #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
+#else
+ #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_FEATURE)
+ #undef JSON_HEDLEY_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+ #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)
+#else
+ #define JSON_HEDLEY_HAS_FEATURE(feature) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)
+ #undef JSON_HEDLEY_GNUC_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+ #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)
+ #undef JSON_HEDLEY_GCC_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+ #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
+#else
+ #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_EXTENSION)
+ #undef JSON_HEDLEY_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+ #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)
+#else
+ #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)
+ #undef JSON_HEDLEY_GNUC_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+ #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)
+ #undef JSON_HEDLEY_GCC_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+ #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
+#else
+ #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)
+ #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+ #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)
+#else
+ #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)
+ #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+ #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)
+ #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+ #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
+#else
+ #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_WARNING)
+ #undef JSON_HEDLEY_HAS_WARNING
+#endif
+#if defined(__has_warning)
+ #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)
+#else
+ #define JSON_HEDLEY_HAS_WARNING(warning) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)
+ #undef JSON_HEDLEY_GNUC_HAS_WARNING
+#endif
+#if defined(__has_warning)
+ #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_WARNING)
+ #undef JSON_HEDLEY_GCC_HAS_WARNING
+#endif
+#if defined(__has_warning)
+ #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
+#else
+ #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if \
+ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+ defined(__clang__) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \
+ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \
+ (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))
+ #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+ #define JSON_HEDLEY_PRAGMA(value) __pragma(value)
+#else
+ #define JSON_HEDLEY_PRAGMA(value)
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)
+ #undef JSON_HEDLEY_DIAGNOSTIC_PUSH
+#endif
+#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)
+ #undef JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+#if defined(__clang__)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))
+ #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))
+#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop")
+#elif \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop")
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH
+ #define JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+
+/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for
+ HEDLEY INTERNAL USE ONLY. API subject to change without notice. */
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
+#endif
+#if defined(__cplusplus)
+# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat")
+# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions")
+# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions")
+# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+ _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+ _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \
+ xpr \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# else
+# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+ _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+ xpr \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# endif
+# else
+# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+ xpr \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# endif
+# endif
+#endif
+#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x
+#endif
+
+#if defined(JSON_HEDLEY_CONST_CAST)
+ #undef JSON_HEDLEY_CONST_CAST
+#endif
+#if defined(__cplusplus)
+# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))
+#elif \
+ JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \
+ ((T) (expr)); \
+ JSON_HEDLEY_DIAGNOSTIC_POP \
+ }))
+#else
+# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_REINTERPRET_CAST)
+ #undef JSON_HEDLEY_REINTERPRET_CAST
+#endif
+#if defined(__cplusplus)
+ #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))
+#else
+ #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_STATIC_CAST)
+ #undef JSON_HEDLEY_STATIC_CAST
+#endif
+#if defined(__cplusplus)
+ #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))
+#else
+ #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_CPP_CAST)
+ #undef JSON_HEDLEY_CPP_CAST
+#endif
+#if defined(__cplusplus)
+# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast")
+# define JSON_HEDLEY_CPP_CAST(T, expr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \
+ ((T) (expr)) \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0)
+# define JSON_HEDLEY_CPP_CAST(T, expr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("diag_suppress=Pe137") \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# else
+# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))
+# endif
+#else
+# define JSON_HEDLEY_CPP_CAST(T, expr) (expr)
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations")
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445")
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
+#elif \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215")
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))
+#elif \
+ JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes")
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292))
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098")
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)")
+#elif \
+ JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual")
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunused-function")
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+
+#if defined(JSON_HEDLEY_DEPRECATED)
+ #undef JSON_HEDLEY_DEPRECATED
+#endif
+#if defined(JSON_HEDLEY_DEPRECATED_FOR)
+ #undef JSON_HEDLEY_DEPRECATED_FOR
+#endif
+#if \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since))
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement))
+#elif \
+ (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since)))
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement)))
+#elif defined(__cplusplus) && (__cplusplus >= 201402L)
+ #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]])
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]])
+#elif \
+ JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+ #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+ JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated")
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated")
+#else
+ #define JSON_HEDLEY_DEPRECATED(since)
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)
+#endif
+
+#if defined(JSON_HEDLEY_UNAVAILABLE)
+ #undef JSON_HEDLEY_UNAVAILABLE
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since)))
+#else
+ #define JSON_HEDLEY_UNAVAILABLE(available_since)
+#endif
+
+#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)
+ #undef JSON_HEDLEY_WARN_UNUSED_RESULT
+#endif
+#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)
+ #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))
+#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+#elif defined(_Check_return_) /* SAL */
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_
+#else
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg)
+#endif
+
+#if defined(JSON_HEDLEY_SENTINEL)
+ #undef JSON_HEDLEY_SENTINEL
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))
+#else
+ #define JSON_HEDLEY_SENTINEL(position)
+#endif
+
+#if defined(JSON_HEDLEY_NO_RETURN)
+ #undef JSON_HEDLEY_NO_RETURN
+#endif
+#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_NO_RETURN __noreturn
+#elif \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+ #define JSON_HEDLEY_NO_RETURN _Noreturn
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+ #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])
+#elif \
+ JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+ #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+ #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return")
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)
+ #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;")
+#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
+ #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)
+ #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)
+#else
+ #define JSON_HEDLEY_NO_RETURN
+#endif
+
+#if defined(JSON_HEDLEY_NO_ESCAPE)
+ #undef JSON_HEDLEY_NO_ESCAPE
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)
+ #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))
+#else
+ #define JSON_HEDLEY_NO_ESCAPE
+#endif
+
+#if defined(JSON_HEDLEY_UNREACHABLE)
+ #undef JSON_HEDLEY_UNREACHABLE
+#endif
+#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)
+ #undef JSON_HEDLEY_UNREACHABLE_RETURN
+#endif
+#if defined(JSON_HEDLEY_ASSUME)
+ #undef JSON_HEDLEY_ASSUME
+#endif
+#if \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_ASSUME(expr) __assume(expr)
+#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)
+ #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)
+#elif \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)
+ #if defined(__cplusplus)
+ #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)
+ #else
+ #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)
+ #endif
+#endif
+#if \
+ (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()
+#elif defined(JSON_HEDLEY_ASSUME)
+ #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)
+#endif
+#if !defined(JSON_HEDLEY_ASSUME)
+ #if defined(JSON_HEDLEY_UNREACHABLE)
+ #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1)))
+ #else
+ #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr)
+ #endif
+#endif
+#if defined(JSON_HEDLEY_UNREACHABLE)
+ #if \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)
+ #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value))
+ #else
+ #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()
+ #endif
+#else
+ #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value)
+#endif
+#if !defined(JSON_HEDLEY_UNREACHABLE)
+ #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)
+#endif
+
+JSON_HEDLEY_DIAGNOSTIC_PUSH
+#if JSON_HEDLEY_HAS_WARNING("-Wpedantic")
+ #pragma clang diagnostic ignored "-Wpedantic"
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus)
+ #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#endif
+#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0)
+ #if defined(__clang__)
+ #pragma clang diagnostic ignored "-Wvariadic-macros"
+ #elif defined(JSON_HEDLEY_GCC_VERSION)
+ #pragma GCC diagnostic ignored "-Wvariadic-macros"
+ #endif
+#endif
+#if defined(JSON_HEDLEY_NON_NULL)
+ #undef JSON_HEDLEY_NON_NULL
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)
+ #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))
+#else
+ #define JSON_HEDLEY_NON_NULL(...)
+#endif
+JSON_HEDLEY_DIAGNOSTIC_POP
+
+#if defined(JSON_HEDLEY_PRINTF_FORMAT)
+ #undef JSON_HEDLEY_PRINTF_FORMAT
+#endif
+#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)
+ #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))
+#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)
+ #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))
+#elif \
+ JSON_HEDLEY_HAS_ATTRIBUTE(format) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)
+ #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))
+#else
+ #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)
+#endif
+
+#if defined(JSON_HEDLEY_CONSTEXPR)
+ #undef JSON_HEDLEY_CONSTEXPR
+#endif
+#if defined(__cplusplus)
+ #if __cplusplus >= 201103L
+ #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)
+ #endif
+#endif
+#if !defined(JSON_HEDLEY_CONSTEXPR)
+ #define JSON_HEDLEY_CONSTEXPR
+#endif
+
+#if defined(JSON_HEDLEY_PREDICT)
+ #undef JSON_HEDLEY_PREDICT
+#endif
+#if defined(JSON_HEDLEY_LIKELY)
+ #undef JSON_HEDLEY_LIKELY
+#endif
+#if defined(JSON_HEDLEY_UNLIKELY)
+ #undef JSON_HEDLEY_UNLIKELY
+#endif
+#if defined(JSON_HEDLEY_UNPREDICTABLE)
+ #undef JSON_HEDLEY_UNPREDICTABLE
+#endif
+#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)
+ #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))
+#endif
+#if \
+ (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability))
+# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability))
+# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability))
+# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 )
+# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 )
+#elif \
+ (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+# define JSON_HEDLEY_PREDICT(expr, expected, probability) \
+ (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))
+# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \
+ (__extension__ ({ \
+ double hedley_probability_ = (probability); \
+ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \
+ }))
+# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \
+ (__extension__ ({ \
+ double hedley_probability_ = (probability); \
+ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \
+ }))
+# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1)
+# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
+#else
+# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))
+# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))
+# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))
+# define JSON_HEDLEY_LIKELY(expr) (!!(expr))
+# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))
+#endif
+#if !defined(JSON_HEDLEY_UNPREDICTABLE)
+ #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)
+#endif
+
+#if defined(JSON_HEDLEY_MALLOC)
+ #undef JSON_HEDLEY_MALLOC
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+ #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory")
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_MALLOC __declspec(restrict)
+#else
+ #define JSON_HEDLEY_MALLOC
+#endif
+
+#if defined(JSON_HEDLEY_PURE)
+ #undef JSON_HEDLEY_PURE
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+# define JSON_HEDLEY_PURE __attribute__((__pure__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data")
+#elif defined(__cplusplus) && \
+ ( \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \
+ )
+# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;")
+#else
+# define JSON_HEDLEY_PURE
+#endif
+
+#if defined(JSON_HEDLEY_CONST)
+ #undef JSON_HEDLEY_CONST
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(const) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_CONST __attribute__((__const__))
+#elif \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+ #define JSON_HEDLEY_CONST _Pragma("no_side_effect")
+#else
+ #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE
+#endif
+
+#if defined(JSON_HEDLEY_RESTRICT)
+ #undef JSON_HEDLEY_RESTRICT
+#endif
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)
+ #define JSON_HEDLEY_RESTRICT restrict
+#elif \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
+ defined(__clang__) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_RESTRICT __restrict
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)
+ #define JSON_HEDLEY_RESTRICT _Restrict
+#else
+ #define JSON_HEDLEY_RESTRICT
+#endif
+
+#if defined(JSON_HEDLEY_INLINE)
+ #undef JSON_HEDLEY_INLINE
+#endif
+#if \
+ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+ (defined(__cplusplus) && (__cplusplus >= 199711L))
+ #define JSON_HEDLEY_INLINE inline
+#elif \
+ defined(JSON_HEDLEY_GCC_VERSION) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)
+ #define JSON_HEDLEY_INLINE __inline__
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_INLINE __inline
+#else
+ #define JSON_HEDLEY_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_ALWAYS_INLINE)
+ #undef JSON_HEDLEY_ALWAYS_INLINE
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+# define JSON_HEDLEY_ALWAYS_INLINE __forceinline
+#elif defined(__cplusplus) && \
+ ( \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \
+ )
+# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced")
+#else
+# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_NEVER_INLINE)
+ #undef JSON_HEDLEY_NEVER_INLINE
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+ #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)
+ #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline")
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)
+ #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never")
+#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
+ #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)
+ #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)
+#else
+ #define JSON_HEDLEY_NEVER_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_PRIVATE)
+ #undef JSON_HEDLEY_PRIVATE
+#endif
+#if defined(JSON_HEDLEY_PUBLIC)
+ #undef JSON_HEDLEY_PUBLIC
+#endif
+#if defined(JSON_HEDLEY_IMPORT)
+ #undef JSON_HEDLEY_IMPORT
+#endif
+#if defined(_WIN32) || defined(__CYGWIN__)
+# define JSON_HEDLEY_PRIVATE
+# define JSON_HEDLEY_PUBLIC __declspec(dllexport)
+# define JSON_HEDLEY_IMPORT __declspec(dllimport)
+#else
+# if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+ ( \
+ defined(__TI_EABI__) && \
+ ( \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \
+ ) \
+ ) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden")))
+# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default")))
+# else
+# define JSON_HEDLEY_PRIVATE
+# define JSON_HEDLEY_PUBLIC
+# endif
+# define JSON_HEDLEY_IMPORT extern
+#endif
+
+#if defined(JSON_HEDLEY_NO_THROW)
+ #undef JSON_HEDLEY_NO_THROW
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)
+ #define JSON_HEDLEY_NO_THROW __declspec(nothrow)
+#else
+ #define JSON_HEDLEY_NO_THROW
+#endif
+
+#if defined(JSON_HEDLEY_FALL_THROUGH)
+ #undef JSON_HEDLEY_FALL_THROUGH
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)
+ #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)
+ #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])
+#elif defined(__fallthrough) /* SAL */
+ #define JSON_HEDLEY_FALL_THROUGH __fallthrough
+#else
+ #define JSON_HEDLEY_FALL_THROUGH
+#endif
+
+#if defined(JSON_HEDLEY_RETURNS_NON_NULL)
+ #undef JSON_HEDLEY_RETURNS_NON_NULL
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))
+#elif defined(_Ret_notnull_) /* SAL */
+ #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_
+#else
+ #define JSON_HEDLEY_RETURNS_NON_NULL
+#endif
+
+#if defined(JSON_HEDLEY_ARRAY_PARAM)
+ #undef JSON_HEDLEY_ARRAY_PARAM
+#endif
+#if \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
+ !defined(__STDC_NO_VLA__) && \
+ !defined(__cplusplus) && \
+ !defined(JSON_HEDLEY_PGI_VERSION) && \
+ !defined(JSON_HEDLEY_TINYC_VERSION)
+ #define JSON_HEDLEY_ARRAY_PARAM(name) (name)
+#else
+ #define JSON_HEDLEY_ARRAY_PARAM(name)
+#endif
+
+#if defined(JSON_HEDLEY_IS_CONSTANT)
+ #undef JSON_HEDLEY_IS_CONSTANT
+#endif
+#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)
+ #undef JSON_HEDLEY_REQUIRE_CONSTEXPR
+#endif
+/* JSON_HEDLEY_IS_CONSTEXPR_ is for
+ HEDLEY INTERNAL USE ONLY. API subject to change without notice. */
+#if defined(JSON_HEDLEY_IS_CONSTEXPR_)
+ #undef JSON_HEDLEY_IS_CONSTEXPR_
+#endif
+#if \
+ JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)
+#endif
+#if !defined(__cplusplus)
+# if \
+ JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
+ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)
+#if defined(__INTPTR_TYPE__)
+ #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)
+#else
+ #include <stdint.h>
+ #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)
+#endif
+# elif \
+ ( \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \
+ !defined(JSON_HEDLEY_SUNPRO_VERSION) && \
+ !defined(JSON_HEDLEY_PGI_VERSION) && \
+ !defined(JSON_HEDLEY_IAR_VERSION)) || \
+ (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)
+#if defined(__INTPTR_TYPE__)
+ #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)
+#else
+ #include <stdint.h>
+ #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)
+#endif
+# elif \
+ defined(JSON_HEDLEY_GCC_VERSION) || \
+ defined(JSON_HEDLEY_INTEL_VERSION) || \
+ defined(JSON_HEDLEY_TINYC_VERSION) || \
+ defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \
+ defined(JSON_HEDLEY_TI_CL2000_VERSION) || \
+ defined(JSON_HEDLEY_TI_CL6X_VERSION) || \
+ defined(JSON_HEDLEY_TI_CL7X_VERSION) || \
+ defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \
+ defined(__clang__)
+# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \
+ sizeof(void) != \
+ sizeof(*( \
+ 1 ? \
+ ((void*) ((expr) * 0L) ) : \
+((struct { char v[sizeof(void) * 2]; } *) 1) \
+ ) \
+ ) \
+ )
+# endif
+#endif
+#if defined(JSON_HEDLEY_IS_CONSTEXPR_)
+ #if !defined(JSON_HEDLEY_IS_CONSTANT)
+ #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)
+ #endif
+ #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))
+#else
+ #if !defined(JSON_HEDLEY_IS_CONSTANT)
+ #define JSON_HEDLEY_IS_CONSTANT(expr) (0)
+ #endif
+ #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)
+#endif
+
+#if defined(JSON_HEDLEY_BEGIN_C_DECLS)
+ #undef JSON_HEDLEY_BEGIN_C_DECLS
+#endif
+#if defined(JSON_HEDLEY_END_C_DECLS)
+ #undef JSON_HEDLEY_END_C_DECLS
+#endif
+#if defined(JSON_HEDLEY_C_DECL)
+ #undef JSON_HEDLEY_C_DECL
+#endif
+#if defined(__cplusplus)
+ #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" {
+ #define JSON_HEDLEY_END_C_DECLS }
+ #define JSON_HEDLEY_C_DECL extern "C"
+#else
+ #define JSON_HEDLEY_BEGIN_C_DECLS
+ #define JSON_HEDLEY_END_C_DECLS
+ #define JSON_HEDLEY_C_DECL
+#endif
+
+#if defined(JSON_HEDLEY_STATIC_ASSERT)
+ #undef JSON_HEDLEY_STATIC_ASSERT
+#endif
+#if \
+ !defined(__cplusplus) && ( \
+ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \
+ (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ defined(_Static_assert) \
+ )
+# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)
+#elif \
+ (defined(__cplusplus) && (__cplusplus >= 201103L)) || \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))
+#else
+# define JSON_HEDLEY_STATIC_ASSERT(expr, message)
+#endif
+
+#if defined(JSON_HEDLEY_NULL)
+ #undef JSON_HEDLEY_NULL
+#endif
+#if defined(__cplusplus)
+ #if __cplusplus >= 201103L
+ #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)
+ #elif defined(NULL)
+ #define JSON_HEDLEY_NULL NULL
+ #else
+ #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)
+ #endif
+#elif defined(NULL)
+ #define JSON_HEDLEY_NULL NULL
+#else
+ #define JSON_HEDLEY_NULL ((void*) 0)
+#endif
+
+#if defined(JSON_HEDLEY_MESSAGE)
+ #undef JSON_HEDLEY_MESSAGE
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+# define JSON_HEDLEY_MESSAGE(msg) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
+ JSON_HEDLEY_PRAGMA(message msg) \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+#elif \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)
+#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)
+# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)
+# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#else
+# define JSON_HEDLEY_MESSAGE(msg)
+#endif
+
+#if defined(JSON_HEDLEY_WARNING)
+ #undef JSON_HEDLEY_WARNING
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+# define JSON_HEDLEY_WARNING(msg) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
+ JSON_HEDLEY_PRAGMA(clang warning msg) \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+#elif \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#else
+# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)
+#endif
+
+#if defined(JSON_HEDLEY_REQUIRE)
+ #undef JSON_HEDLEY_REQUIRE
+#endif
+#if defined(JSON_HEDLEY_REQUIRE_MSG)
+ #undef JSON_HEDLEY_REQUIRE_MSG
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)
+# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat")
+# define JSON_HEDLEY_REQUIRE(expr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+ __attribute__((diagnose_if(!(expr), #expr, "error"))) \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+ __attribute__((diagnose_if(!(expr), msg, "error"))) \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# else
+# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error")))
+# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error")))
+# endif
+#else
+# define JSON_HEDLEY_REQUIRE(expr)
+# define JSON_HEDLEY_REQUIRE_MSG(expr,msg)
+#endif
+
+#if defined(JSON_HEDLEY_FLAGS)
+ #undef JSON_HEDLEY_FLAGS
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion"))
+ #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))
+#else
+ #define JSON_HEDLEY_FLAGS
+#endif
+
+#if defined(JSON_HEDLEY_FLAGS_CAST)
+ #undef JSON_HEDLEY_FLAGS_CAST
+#endif
+#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)
+# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("warning(disable:188)") \
+ ((T) (expr)); \
+ JSON_HEDLEY_DIAGNOSTIC_POP \
+ }))
+#else
+# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)
+#endif
+
+#if defined(JSON_HEDLEY_EMPTY_BASES)
+ #undef JSON_HEDLEY_EMPTY_BASES
+#endif
+#if \
+ (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)
+#else
+ #define JSON_HEDLEY_EMPTY_BASES
+#endif
+
+/* Remaining macros are deprecated. */
+
+#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)
+ #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
+#endif
+#if defined(__clang__)
+ #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)
+#else
+ #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)
+ #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)
+ #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)
+ #undef JSON_HEDLEY_CLANG_HAS_BUILTIN
+#endif
+#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)
+ #undef JSON_HEDLEY_CLANG_HAS_FEATURE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)
+ #undef JSON_HEDLEY_CLANG_HAS_EXTENSION
+#endif
+#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)
+ #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)
+ #undef JSON_HEDLEY_CLANG_HAS_WARNING
+#endif
+#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)
+
+#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */
+
+
+// This file contains all internal macro definitions (except those affecting ABI)
+// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+// exclude unsupported compilers
+#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)
+ #if defined(__clang__)
+ #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
+ #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
+ #endif
+ #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
+ #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800
+ #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
+ #endif
+ #endif
+#endif
+
+// C++ language standard detection
+// if the user manually specified the used c++ version this is skipped
+#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
+ #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
+ #define JSON_HAS_CPP_20
+ #define JSON_HAS_CPP_17
+ #define JSON_HAS_CPP_14
+ #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
+ #define JSON_HAS_CPP_17
+ #define JSON_HAS_CPP_14
+ #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
+ #define JSON_HAS_CPP_14
+ #endif
+ // the cpp 11 flag is always specified because it is the minimal required version
+ #define JSON_HAS_CPP_11
+#endif
+
+#ifdef __has_include
+ #if __has_include(<version>)
+ #include <version>
+ #endif
+#endif
+
+#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)
+ #ifdef JSON_HAS_CPP_17
+ #if defined(__cpp_lib_filesystem)
+ #define JSON_HAS_FILESYSTEM 1
+ #elif defined(__cpp_lib_experimental_filesystem)
+ #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+ #elif !defined(__has_include)
+ #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+ #elif __has_include(<filesystem>)
+ #define JSON_HAS_FILESYSTEM 1
+ #elif __has_include(<experimental/filesystem>)
+ #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+ #endif
+
+ // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/
+ #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+
+ // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support
+ #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+
+ // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support
+ #if defined(__clang_major__) && __clang_major__ < 7
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+
+ // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support
+ #if defined(_MSC_VER) && _MSC_VER < 1914
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+
+ // no filesystem support before iOS 13
+ #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+
+ // no filesystem support before macOS Catalina
+ #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+ #endif
+#endif
+
+#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0
+#endif
+
+#ifndef JSON_HAS_FILESYSTEM
+ #define JSON_HAS_FILESYSTEM 0
+#endif
+
+#ifndef JSON_HAS_THREE_WAY_COMPARISON
+ #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
+ && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
+ #define JSON_HAS_THREE_WAY_COMPARISON 1
+ #else
+ #define JSON_HAS_THREE_WAY_COMPARISON 0
+ #endif
+#endif
+
+#ifndef JSON_HAS_RANGES
+ // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error
+ #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427
+ #define JSON_HAS_RANGES 0
+ #elif defined(__cpp_lib_ranges)
+ #define JSON_HAS_RANGES 1
+ #else
+ #define JSON_HAS_RANGES 0
+ #endif
+#endif
+
+#ifdef JSON_HAS_CPP_17
+ #define JSON_INLINE_VARIABLE inline
+#else
+ #define JSON_INLINE_VARIABLE
+#endif
+
+#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
+ #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
+#else
+ #define JSON_NO_UNIQUE_ADDRESS
+#endif
+
+// disable documentation warnings on clang
+#if defined(__clang__)
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wdocumentation"
+ #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
+#endif
+
+// allow disabling exceptions
+#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
+ #define JSON_THROW(exception) throw exception
+ #define JSON_TRY try
+ #define JSON_CATCH(exception) catch(exception)
+ #define JSON_INTERNAL_CATCH(exception) catch(exception)
+#else
+ #include <cstdlib>
+ #define JSON_THROW(exception) std::abort()
+ #define JSON_TRY if(true)
+ #define JSON_CATCH(exception) if(false)
+ #define JSON_INTERNAL_CATCH(exception) if(false)
+#endif
+
+// override exception macros
+#if defined(JSON_THROW_USER)
+ #undef JSON_THROW
+ #define JSON_THROW JSON_THROW_USER
+#endif
+#if defined(JSON_TRY_USER)
+ #undef JSON_TRY
+ #define JSON_TRY JSON_TRY_USER
+#endif
+#if defined(JSON_CATCH_USER)
+ #undef JSON_CATCH
+ #define JSON_CATCH JSON_CATCH_USER
+ #undef JSON_INTERNAL_CATCH
+ #define JSON_INTERNAL_CATCH JSON_CATCH_USER
+#endif
+#if defined(JSON_INTERNAL_CATCH_USER)
+ #undef JSON_INTERNAL_CATCH
+ #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
+#endif
+
+// allow overriding assert
+#if !defined(JSON_ASSERT)
+ #include <cassert> // assert
+ #define JSON_ASSERT(x) assert(x)
+#endif
+
+// allow to access some private functions (needed by the test suite)
+#if defined(JSON_TESTS_PRIVATE)
+ #define JSON_PRIVATE_UNLESS_TESTED public
+#else
+ #define JSON_PRIVATE_UNLESS_TESTED private
+#endif
+
+/*!
+@brief macro to briefly define a mapping between an enum and JSON
+@def NLOHMANN_JSON_SERIALIZE_ENUM
+@since version 3.4.0
+*/
+#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \
+ template<typename BasicJsonType> \
+ inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \
+ { \
+ static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
+ static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
+ auto it = std::find_if(std::begin(m), std::end(m), \
+ [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
+ { \
+ return ej_pair.first == e; \
+ }); \
+ j = ((it != std::end(m)) ? it : std::begin(m))->second; \
+ } \
+ template<typename BasicJsonType> \
+ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
+ { \
+ static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
+ static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
+ auto it = std::find_if(std::begin(m), std::end(m), \
+ [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
+ { \
+ return ej_pair.second == j; \
+ }); \
+ e = ((it != std::end(m)) ? it : std::begin(m))->first; \
+ }
+
+// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
+// may be removed in the future once the class is split.
+
+#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \
+ template<template<typename, typename, typename...> class ObjectType, \
+ template<typename, typename...> class ArrayType, \
+ class StringType, class BooleanType, class NumberIntegerType, \
+ class NumberUnsignedType, class NumberFloatType, \
+ template<typename> class AllocatorType, \
+ template<typename, typename = void> class JSONSerializer, \
+ class BinaryType, \
+ class CustomBaseClass>
+
+#define NLOHMANN_BASIC_JSON_TPL \
+ basic_json<ObjectType, ArrayType, StringType, BooleanType, \
+ NumberIntegerType, NumberUnsignedType, NumberFloatType, \
+ AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>
+
+// Macros to simplify conversion from/to types
+
+#define NLOHMANN_JSON_EXPAND( x ) x
+#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME
+#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
+ NLOHMANN_JSON_PASTE64, \
+ NLOHMANN_JSON_PASTE63, \
+ NLOHMANN_JSON_PASTE62, \
+ NLOHMANN_JSON_PASTE61, \
+ NLOHMANN_JSON_PASTE60, \
+ NLOHMANN_JSON_PASTE59, \
+ NLOHMANN_JSON_PASTE58, \
+ NLOHMANN_JSON_PASTE57, \
+ NLOHMANN_JSON_PASTE56, \
+ NLOHMANN_JSON_PASTE55, \
+ NLOHMANN_JSON_PASTE54, \
+ NLOHMANN_JSON_PASTE53, \
+ NLOHMANN_JSON_PASTE52, \
+ NLOHMANN_JSON_PASTE51, \
+ NLOHMANN_JSON_PASTE50, \
+ NLOHMANN_JSON_PASTE49, \
+ NLOHMANN_JSON_PASTE48, \
+ NLOHMANN_JSON_PASTE47, \
+ NLOHMANN_JSON_PASTE46, \
+ NLOHMANN_JSON_PASTE45, \
+ NLOHMANN_JSON_PASTE44, \
+ NLOHMANN_JSON_PASTE43, \
+ NLOHMANN_JSON_PASTE42, \
+ NLOHMANN_JSON_PASTE41, \
+ NLOHMANN_JSON_PASTE40, \
+ NLOHMANN_JSON_PASTE39, \
+ NLOHMANN_JSON_PASTE38, \
+ NLOHMANN_JSON_PASTE37, \
+ NLOHMANN_JSON_PASTE36, \
+ NLOHMANN_JSON_PASTE35, \
+ NLOHMANN_JSON_PASTE34, \
+ NLOHMANN_JSON_PASTE33, \
+ NLOHMANN_JSON_PASTE32, \
+ NLOHMANN_JSON_PASTE31, \
+ NLOHMANN_JSON_PASTE30, \
+ NLOHMANN_JSON_PASTE29, \
+ NLOHMANN_JSON_PASTE28, \
+ NLOHMANN_JSON_PASTE27, \
+ NLOHMANN_JSON_PASTE26, \
+ NLOHMANN_JSON_PASTE25, \
+ NLOHMANN_JSON_PASTE24, \
+ NLOHMANN_JSON_PASTE23, \
+ NLOHMANN_JSON_PASTE22, \
+ NLOHMANN_JSON_PASTE21, \
+ NLOHMANN_JSON_PASTE20, \
+ NLOHMANN_JSON_PASTE19, \
+ NLOHMANN_JSON_PASTE18, \
+ NLOHMANN_JSON_PASTE17, \
+ NLOHMANN_JSON_PASTE16, \
+ NLOHMANN_JSON_PASTE15, \
+ NLOHMANN_JSON_PASTE14, \
+ NLOHMANN_JSON_PASTE13, \
+ NLOHMANN_JSON_PASTE12, \
+ NLOHMANN_JSON_PASTE11, \
+ NLOHMANN_JSON_PASTE10, \
+ NLOHMANN_JSON_PASTE9, \
+ NLOHMANN_JSON_PASTE8, \
+ NLOHMANN_JSON_PASTE7, \
+ NLOHMANN_JSON_PASTE6, \
+ NLOHMANN_JSON_PASTE5, \
+ NLOHMANN_JSON_PASTE4, \
+ NLOHMANN_JSON_PASTE3, \
+ NLOHMANN_JSON_PASTE2, \
+ NLOHMANN_JSON_PASTE1)(__VA_ARGS__))
+#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)
+#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)
+#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)
+#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)
+#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)
+#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)
+#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)
+#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)
+#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)
+#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)
+#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
+#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)
+#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)
+#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)
+#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)
+#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)
+#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)
+#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)
+#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)
+#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
+#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)
+#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)
+#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)
+#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)
+#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)
+#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)
+#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)
+#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)
+#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)
+#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)
+#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)
+#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)
+#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)
+#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)
+#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)
+#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)
+#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)
+#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)
+#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)
+#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)
+#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)
+#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)
+#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)
+#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)
+#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)
+#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)
+#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)
+#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)
+#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)
+#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)
+#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)
+#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)
+#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)
+#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)
+#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)
+#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)
+#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)
+#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)
+#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)
+#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)
+#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)
+#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)
+#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)
+
+#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
+#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
+#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
+@since version 3.9.0
+*/
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
+ friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \
+ friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
+@since version 3.9.0
+*/
+#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
+ inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
+ inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+
+// inspired from https://stackoverflow.com/a/26745591
+// allows to call any std function as if (e.g. with begin):
+// using std::begin; begin(x);
+//
+// it allows using the detected idiom to retrieve the return type
+// of such an expression
+#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \
+ namespace detail { \
+ using std::std_name; \
+ \
+ template<typename... T> \
+ using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \
+ } \
+ \
+ namespace detail2 { \
+ struct std_name##_tag \
+ { \
+ }; \
+ \
+ template<typename... T> \
+ std_name##_tag std_name(T&&...); \
+ \
+ template<typename... T> \
+ using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \
+ \
+ template<typename... T> \
+ struct would_call_std_##std_name \
+ { \
+ static constexpr auto const value = ::nlohmann::detail:: \
+ is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \
+ }; \
+ } /* namespace detail2 */ \
+ \
+ template<typename... T> \
+ struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...> \
+ { \
+ }
+
+#ifndef JSON_USE_IMPLICIT_CONVERSIONS
+ #define JSON_USE_IMPLICIT_CONVERSIONS 1
+#endif
+
+#if JSON_USE_IMPLICIT_CONVERSIONS
+ #define JSON_EXPLICIT
+#else
+ #define JSON_EXPLICIT explicit
+#endif
+
+#ifndef JSON_DISABLE_ENUM_SERIALIZATION
+ #define JSON_DISABLE_ENUM_SERIALIZATION 0
+#endif
+
+#ifndef JSON_USE_GLOBAL_UDLS
+ #define JSON_USE_GLOBAL_UDLS 1
+#endif
+
+#if JSON_HAS_THREE_WAY_COMPARISON
+ #include <compare> // partial_ordering
+#endif
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////////////
+// JSON type enumeration //
+///////////////////////////
+
+/*!
+@brief the JSON type enumeration
+
+This enumeration collects the different JSON types. It is internally used to
+distinguish the stored values, and the functions @ref basic_json::is_null(),
+@ref basic_json::is_object(), @ref basic_json::is_array(),
+@ref basic_json::is_string(), @ref basic_json::is_boolean(),
+@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
+@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
+@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
+@ref basic_json::is_structured() rely on it.
+
+@note There are three enumeration entries (number_integer, number_unsigned, and
+number_float), because the library distinguishes these three types for numbers:
+@ref basic_json::number_unsigned_t is used for unsigned integers,
+@ref basic_json::number_integer_t is used for signed integers, and
+@ref basic_json::number_float_t is used for floating-point numbers or to
+approximate integers which do not fit in the limits of their respective type.
+
+@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON
+value with the default value for a given type
+
+@since version 1.0.0
+*/
+enum class value_t : std::uint8_t
+{
+ null, ///< null value
+ object, ///< object (unordered set of name/value pairs)
+ array, ///< array (ordered collection of values)
+ string, ///< string value
+ boolean, ///< boolean value
+ number_integer, ///< number value (signed integer)
+ number_unsigned, ///< number value (unsigned integer)
+ number_float, ///< number value (floating-point)
+ binary, ///< binary array (ordered collection of bytes)
+ discarded ///< discarded by the parser callback function
+};
+
+/*!
+@brief comparison operator for JSON types
+
+Returns an ordering that is similar to Python:
+- order: null < boolean < number < object < array < string < binary
+- furthermore, each type is not smaller than itself
+- discarded values are not comparable
+- binary is represented as a b"" string in python and directly comparable to a
+ string; however, making a binary array directly comparable with a string would
+ be surprising behavior in a JSON file.
+
+@since version 1.0.0
+*/
+#if JSON_HAS_THREE_WAY_COMPARISON
+ inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD*
+#else
+ inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+#endif
+{
+ static constexpr std::array<std::uint8_t, 9> order = {{
+ 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
+ 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,
+ 6 /* binary */
+ }
+ };
+
+ const auto l_index = static_cast<std::size_t>(lhs);
+ const auto r_index = static_cast<std::size_t>(rhs);
+#if JSON_HAS_THREE_WAY_COMPARISON
+ if (l_index < order.size() && r_index < order.size())
+ {
+ return order[l_index] <=> order[r_index]; // *NOPAD*
+ }
+ return std::partial_ordering::unordered;
+#else
+ return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
+#endif
+}
+
+// GCC selects the built-in operator< over an operator rewritten from
+// a user-defined spaceship operator
+// Clang, MSVC, and ICC select the rewritten candidate
+// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)
+#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__)
+inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+{
+ return std::is_lt(lhs <=> rhs); // *NOPAD*
+}
+#endif
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_escape.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief replace all occurrences of a substring by another string
+
+@param[in,out] s the string to manipulate; changed so that all
+ occurrences of @a f are replaced with @a t
+@param[in] f the substring to replace with @a t
+@param[in] t the string to replace @a f
+
+@pre The search string @a f must not be empty. **This precondition is
+enforced with an assertion.**
+
+@since version 2.0.0
+*/
+template<typename StringType>
+inline void replace_substring(StringType& s, const StringType& f,
+ const StringType& t)
+{
+ JSON_ASSERT(!f.empty());
+ for (auto pos = s.find(f); // find first occurrence of f
+ pos != StringType::npos; // make sure f was found
+ s.replace(pos, f.size(), t), // replace with t, and
+ pos = s.find(f, pos + t.size())) // find next occurrence of f
+ {}
+}
+
+/*!
+ * @brief string escaping as described in RFC 6901 (Sect. 4)
+ * @param[in] s string to escape
+ * @return escaped string
+ *
+ * Note the order of escaping "~" to "~0" and "/" to "~1" is important.
+ */
+template<typename StringType>
+inline StringType escape(StringType s)
+{
+ replace_substring(s, StringType{"~"}, StringType{"~0"});
+ replace_substring(s, StringType{"/"}, StringType{"~1"});
+ return s;
+}
+
+/*!
+ * @brief string unescaping as described in RFC 6901 (Sect. 4)
+ * @param[in] s string to unescape
+ * @return unescaped string
+ *
+ * Note the order of escaping "~1" to "/" and "~0" to "~" is important.
+ */
+template<typename StringType>
+static void unescape(StringType& s)
+{
+ replace_substring(s, StringType{"~1"}, StringType{"/"});
+ replace_substring(s, StringType{"~0"}, StringType{"~"});
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/position_t.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // size_t
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// struct to capture the start position of the current token
+struct position_t
+{
+ /// the total number of characters read
+ std::size_t chars_read_total = 0;
+ /// the number of characters read in the current line
+ std::size_t chars_read_current_line = 0;
+ /// the number of lines read
+ std::size_t lines_read = 0;
+
+ /// conversion to size_t to preserve SAX interface
+ constexpr operator size_t() const
+ {
+ return chars_read_total;
+ }
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-FileCopyrightText: 2018 The Abseil Authors
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
+#include <utility> // index_sequence, make_index_sequence, index_sequence_for
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename T>
+using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+#ifdef JSON_HAS_CPP_14
+
+// the following utilities are natively available in C++14
+using std::enable_if_t;
+using std::index_sequence;
+using std::make_index_sequence;
+using std::index_sequence_for;
+
+#else
+
+// alias templates to reduce boilerplate
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h
+// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0.
+
+//// START OF CODE FROM GOOGLE ABSEIL
+
+// integer_sequence
+//
+// Class template representing a compile-time integer sequence. An instantiation
+// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
+// type through its template arguments (which is a common need when
+// working with C++11 variadic templates). `absl::integer_sequence` is designed
+// to be a drop-in replacement for C++14's `std::integer_sequence`.
+//
+// Example:
+//
+// template< class T, T... Ints >
+// void user_function(integer_sequence<T, Ints...>);
+//
+// int main()
+// {
+// // user_function's `T` will be deduced to `int` and `Ints...`
+// // will be deduced to `0, 1, 2, 3, 4`.
+// user_function(make_integer_sequence<int, 5>());
+// }
+template <typename T, T... Ints>
+struct integer_sequence
+{
+ using value_type = T;
+ static constexpr std::size_t size() noexcept
+ {
+ return sizeof...(Ints);
+ }
+};
+
+// index_sequence
+//
+// A helper template for an `integer_sequence` of `size_t`,
+// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
+// `std::index_sequence`.
+template <size_t... Ints>
+using index_sequence = integer_sequence<size_t, Ints...>;
+
+namespace utility_internal
+{
+
+template <typename Seq, size_t SeqSize, size_t Rem>
+struct Extend;
+
+// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 0>
+{
+ using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >;
+};
+
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 1>
+{
+ using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >;
+};
+
+// Recursion helper for 'make_integer_sequence<T, N>'.
+// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
+template <typename T, size_t N>
+struct Gen
+{
+ using type =
+ typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type;
+};
+
+template <typename T>
+struct Gen<T, 0>
+{
+ using type = integer_sequence<T>;
+};
+
+} // namespace utility_internal
+
+// Compile-time sequences of integers
+
+// make_integer_sequence
+//
+// This template alias is equivalent to
+// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
+// replacement for C++14's `std::make_integer_sequence`.
+template <typename T, T N>
+using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
+
+// make_index_sequence
+//
+// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
+// and is designed to be a drop-in replacement for C++14's
+// `std::make_index_sequence`.
+template <size_t N>
+using make_index_sequence = make_integer_sequence<size_t, N>;
+
+// index_sequence_for
+//
+// Converts a typename pack into an index sequence of the same length, and
+// is designed to be a drop-in replacement for C++14's
+// `std::index_sequence_for()`
+template <typename... Ts>
+using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+
+//// END OF CODE FROM GOOGLE ABSEIL
+
+#endif
+
+// dispatch utility (taken from ranges-v3)
+template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
+template<> struct priority_tag<0> {};
+
+// taken from ranges-v3
+template<typename T>
+struct static_const
+{
+ static JSON_INLINE_VARIABLE constexpr T value{};
+};
+
+#ifndef JSON_HAS_CPP_17
+ template<typename T>
+ constexpr T static_const<T>::value;
+#endif
+
+template<typename T, typename... Args>
+inline constexpr std::array<T, sizeof...(Args)> make_array(Args&& ... args)
+{
+ return std::array<T, sizeof...(Args)> {{static_cast<T>(std::forward<Args>(args))...}};
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <limits> // numeric_limits
+#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
+#include <utility> // declval
+#include <tuple> // tuple
+
+// #include <nlohmann/detail/iterators/iterator_traits.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <iterator> // random_access_iterator_tag
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/void_t.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename It, typename = void>
+struct iterator_types {};
+
+template<typename It>
+struct iterator_types <
+ It,
+ void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
+ typename It::reference, typename It::iterator_category >>
+{
+ using difference_type = typename It::difference_type;
+ using value_type = typename It::value_type;
+ using pointer = typename It::pointer;
+ using reference = typename It::reference;
+ using iterator_category = typename It::iterator_category;
+};
+
+// This is required as some compilers implement std::iterator_traits in a way that
+// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
+template<typename T, typename = void>
+struct iterator_traits
+{
+};
+
+template<typename T>
+struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
+ : iterator_types<T>
+{
+};
+
+template<typename T>
+struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
+{
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = T;
+ using difference_type = ptrdiff_t;
+ using pointer = T*;
+ using reference = T&;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/call_std/begin.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin);
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/call_std/end.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end);
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+// #include <nlohmann/json_fwd.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
+ #define INCLUDE_NLOHMANN_JSON_FWD_HPP_
+
+ #include <cstdint> // int64_t, uint64_t
+ #include <map> // map
+ #include <memory> // allocator
+ #include <string> // string
+ #include <vector> // vector
+
+ // #include <nlohmann/detail/abi_macros.hpp>
+
+
+ /*!
+ @brief namespace for Niels Lohmann
+ @see https://github.com/nlohmann
+ @since version 1.0.0
+ */
+ NLOHMANN_JSON_NAMESPACE_BEGIN
+
+ /*!
+ @brief default JSONSerializer template argument
+
+ This serializer ignores the template arguments and uses ADL
+ ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
+ for serialization.
+ */
+ template<typename T = void, typename SFINAE = void>
+ struct adl_serializer;
+
+ /// a class to store JSON values
+ /// @sa https://json.nlohmann.me/api/basic_json/
+ template<template<typename U, typename V, typename... Args> class ObjectType =
+ std::map,
+ template<typename U, typename... Args> class ArrayType = std::vector,
+ class StringType = std::string, class BooleanType = bool,
+ class NumberIntegerType = std::int64_t,
+ class NumberUnsignedType = std::uint64_t,
+ class NumberFloatType = double,
+ template<typename U> class AllocatorType = std::allocator,
+ template<typename T, typename SFINAE = void> class JSONSerializer =
+ adl_serializer,
+ class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
+ class CustomBaseClass = void>
+ class basic_json;
+
+ /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
+ /// @sa https://json.nlohmann.me/api/json_pointer/
+ template<typename RefStringType>
+ class json_pointer;
+
+ /*!
+ @brief default specialization
+ @sa https://json.nlohmann.me/api/json/
+ */
+ using json = basic_json<>;
+
+ /// @brief a minimal map-like container that preserves insertion order
+ /// @sa https://json.nlohmann.me/api/ordered_map/
+ template<class Key, class T, class IgnoredLess, class Allocator>
+ struct ordered_map;
+
+ /// @brief specialization that maintains the insertion order of object keys
+ /// @sa https://json.nlohmann.me/api/ordered_json/
+ using ordered_json = basic_json<nlohmann::ordered_map>;
+
+ NLOHMANN_JSON_NAMESPACE_END
+
+#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+/*!
+@brief detail namespace with internal helper functions
+
+This namespace collects functions that should not be exposed,
+implementations of some @ref basic_json methods, and meta-programming helpers.
+
+@since version 2.1.0
+*/
+namespace detail
+{
+
+/////////////
+// helpers //
+/////////////
+
+// Note to maintainers:
+//
+// Every trait in this file expects a non CV-qualified type.
+// The only exceptions are in the 'aliases for detected' section
+// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))
+//
+// In this case, T has to be properly CV-qualified to constraint the function arguments
+// (e.g. to_json(BasicJsonType&, const T&))
+
+template<typename> struct is_basic_json : std::false_type {};
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
+
+// used by exceptions create() member functions
+// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t
+// false_type otherwise
+template<typename BasicJsonContext>
+struct is_basic_json_context :
+ std::integral_constant < bool,
+ is_basic_json<typename std::remove_cv<typename std::remove_pointer<BasicJsonContext>::type>::type>::value
+ || std::is_same<BasicJsonContext, std::nullptr_t>::value >
+{};
+
+//////////////////////
+// json_ref helpers //
+//////////////////////
+
+template<typename>
+class json_ref;
+
+template<typename>
+struct is_json_ref : std::false_type {};
+
+template<typename T>
+struct is_json_ref<json_ref<T>> : std::true_type {};
+
+//////////////////////////
+// aliases for detected //
+//////////////////////////
+
+template<typename T>
+using mapped_type_t = typename T::mapped_type;
+
+template<typename T>
+using key_type_t = typename T::key_type;
+
+template<typename T>
+using value_type_t = typename T::value_type;
+
+template<typename T>
+using difference_type_t = typename T::difference_type;
+
+template<typename T>
+using pointer_t = typename T::pointer;
+
+template<typename T>
+using reference_t = typename T::reference;
+
+template<typename T>
+using iterator_category_t = typename T::iterator_category;
+
+template<typename T, typename... Args>
+using to_json_function = decltype(T::to_json(std::declval<Args>()...));
+
+template<typename T, typename... Args>
+using from_json_function = decltype(T::from_json(std::declval<Args>()...));
+
+template<typename T, typename U>
+using get_template_function = decltype(std::declval<T>().template get<U>());
+
+// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
+template<typename BasicJsonType, typename T, typename = void>
+struct has_from_json : std::false_type {};
+
+// trait checking if j.get<T> is valid
+// use this trait instead of std::is_constructible or std::is_convertible,
+// both rely on, or make use of implicit conversions, and thus fail when T
+// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
+template <typename BasicJsonType, typename T>
+struct is_getable
+{
+ static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
+};
+
+template<typename BasicJsonType, typename T>
+struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+ using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+ static constexpr bool value =
+ is_detected_exact<void, from_json_function, serializer,
+ const BasicJsonType&, T&>::value;
+};
+
+// This trait checks if JSONSerializer<T>::from_json(json const&) exists
+// this overload is used for non-default-constructible user-defined-types
+template<typename BasicJsonType, typename T, typename = void>
+struct has_non_default_from_json : std::false_type {};
+
+template<typename BasicJsonType, typename T>
+struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+ using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+ static constexpr bool value =
+ is_detected_exact<T, from_json_function, serializer,
+ const BasicJsonType&>::value;
+};
+
+// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
+// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
+template<typename BasicJsonType, typename T, typename = void>
+struct has_to_json : std::false_type {};
+
+template<typename BasicJsonType, typename T>
+struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+ using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+ static constexpr bool value =
+ is_detected_exact<void, to_json_function, serializer, BasicJsonType&,
+ T>::value;
+};
+
+template<typename T>
+using detect_key_compare = typename T::key_compare;
+
+template<typename T>
+struct has_key_compare : std::integral_constant<bool, is_detected<detect_key_compare, T>::value> {};
+
+// obtains the actual object key comparator
+template<typename BasicJsonType>
+struct actual_object_comparator
+{
+ using object_t = typename BasicJsonType::object_t;
+ using object_comparator_t = typename BasicJsonType::default_object_comparator_t;
+ using type = typename std::conditional < has_key_compare<object_t>::value,
+ typename object_t::key_compare, object_comparator_t>::type;
+};
+
+template<typename BasicJsonType>
+using actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;
+
+///////////////////
+// is_ functions //
+///////////////////
+
+// https://en.cppreference.com/w/cpp/types/conjunction
+template<class...> struct conjunction : std::true_type { };
+template<class B> struct conjunction<B> : B { };
+template<class B, class... Bn>
+struct conjunction<B, Bn...>
+: std::conditional<static_cast<bool>(B::value), conjunction<Bn...>, B>::type {};
+
+// https://en.cppreference.com/w/cpp/types/negation
+template<class B> struct negation : std::integral_constant < bool, !B::value > { };
+
+// Reimplementation of is_constructible and is_default_constructible, due to them being broken for
+// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
+// This causes compile errors in e.g. clang 3.5 or gcc 4.9.
+template <typename T>
+struct is_default_constructible : std::is_default_constructible<T> {};
+
+template <typename T1, typename T2>
+struct is_default_constructible<std::pair<T1, T2>>
+ : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
+
+template <typename T1, typename T2>
+struct is_default_constructible<const std::pair<T1, T2>>
+ : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
+
+template <typename... Ts>
+struct is_default_constructible<std::tuple<Ts...>>
+ : conjunction<is_default_constructible<Ts>...> {};
+
+template <typename... Ts>
+struct is_default_constructible<const std::tuple<Ts...>>
+ : conjunction<is_default_constructible<Ts>...> {};
+
+
+template <typename T, typename... Args>
+struct is_constructible : std::is_constructible<T, Args...> {};
+
+template <typename T1, typename T2>
+struct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};
+
+template <typename T1, typename T2>
+struct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};
+
+template <typename... Ts>
+struct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};
+
+template <typename... Ts>
+struct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};
+
+
+template<typename T, typename = void>
+struct is_iterator_traits : std::false_type {};
+
+template<typename T>
+struct is_iterator_traits<iterator_traits<T>>
+{
+ private:
+ using traits = iterator_traits<T>;
+
+ public:
+ static constexpr auto value =
+ is_detected<value_type_t, traits>::value &&
+ is_detected<difference_type_t, traits>::value &&
+ is_detected<pointer_t, traits>::value &&
+ is_detected<iterator_category_t, traits>::value &&
+ is_detected<reference_t, traits>::value;
+};
+
+template<typename T>
+struct is_range
+{
+ private:
+ using t_ref = typename std::add_lvalue_reference<T>::type;
+
+ using iterator = detected_t<result_of_begin, t_ref>;
+ using sentinel = detected_t<result_of_end, t_ref>;
+
+ // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator
+ // and https://en.cppreference.com/w/cpp/iterator/sentinel_for
+ // but reimplementing these would be too much work, as a lot of other concepts are used underneath
+ static constexpr auto is_iterator_begin =
+ is_iterator_traits<iterator_traits<iterator>>::value;
+
+ public:
+ static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin;
+};
+
+template<typename R>
+using iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R&>())>>;
+
+template<typename T>
+using range_value_t = value_type_t<iterator_traits<iterator_t<T>>>;
+
+// The following implementation of is_complete_type is taken from
+// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/
+// and is written by Xiang Fan who agreed to using it in this library.
+
+template<typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+template<typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+ typename = void>
+struct is_compatible_object_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleObjectType>
+struct is_compatible_object_type_impl <
+ BasicJsonType, CompatibleObjectType,
+ enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&
+ is_detected<key_type_t, CompatibleObjectType>::value >>
+{
+ using object_t = typename BasicJsonType::object_t;
+
+ // macOS's is_constructible does not play well with nonesuch...
+ static constexpr bool value =
+ is_constructible<typename object_t::key_type,
+ typename CompatibleObjectType::key_type>::value &&
+ is_constructible<typename object_t::mapped_type,
+ typename CompatibleObjectType::mapped_type>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleObjectType>
+struct is_compatible_object_type
+ : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
+
+template<typename BasicJsonType, typename ConstructibleObjectType,
+ typename = void>
+struct is_constructible_object_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename ConstructibleObjectType>
+struct is_constructible_object_type_impl <
+ BasicJsonType, ConstructibleObjectType,
+ enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&
+ is_detected<key_type_t, ConstructibleObjectType>::value >>
+{
+ using object_t = typename BasicJsonType::object_t;
+
+ static constexpr bool value =
+ (is_default_constructible<ConstructibleObjectType>::value &&
+ (std::is_move_assignable<ConstructibleObjectType>::value ||
+ std::is_copy_assignable<ConstructibleObjectType>::value) &&
+ (is_constructible<typename ConstructibleObjectType::key_type,
+ typename object_t::key_type>::value &&
+ std::is_same <
+ typename object_t::mapped_type,
+ typename ConstructibleObjectType::mapped_type >::value)) ||
+ (has_from_json<BasicJsonType,
+ typename ConstructibleObjectType::mapped_type>::value ||
+ has_non_default_from_json <
+ BasicJsonType,
+ typename ConstructibleObjectType::mapped_type >::value);
+};
+
+template<typename BasicJsonType, typename ConstructibleObjectType>
+struct is_constructible_object_type
+ : is_constructible_object_type_impl<BasicJsonType,
+ ConstructibleObjectType> {};
+
+template<typename BasicJsonType, typename CompatibleStringType>
+struct is_compatible_string_type
+{
+ static constexpr auto value =
+ is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
+};
+
+template<typename BasicJsonType, typename ConstructibleStringType>
+struct is_constructible_string_type
+{
+ // launder type through decltype() to fix compilation failure on ICPC
+#ifdef __INTEL_COMPILER
+ using laundered_type = decltype(std::declval<ConstructibleStringType>());
+#else
+ using laundered_type = ConstructibleStringType;
+#endif
+
+ static constexpr auto value =
+ conjunction <
+ is_constructible<laundered_type, typename BasicJsonType::string_t>,
+ is_detected_exact<typename BasicJsonType::string_t::value_type,
+ value_type_t, laundered_type >>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
+struct is_compatible_array_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+struct is_compatible_array_type_impl <
+ BasicJsonType, CompatibleArrayType,
+ enable_if_t <
+ is_detected<iterator_t, CompatibleArrayType>::value&&
+ is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&
+// special case for types like std::filesystem::path whose iterator's value_type are themselves
+// c.f. https://github.com/nlohmann/json/pull/3073
+ !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>
+{
+ static constexpr bool value =
+ is_constructible<BasicJsonType,
+ range_value_t<CompatibleArrayType>>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+struct is_compatible_array_type
+ : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType, typename = void>
+struct is_constructible_array_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type_impl <
+ BasicJsonType, ConstructibleArrayType,
+ enable_if_t<std::is_same<ConstructibleArrayType,
+ typename BasicJsonType::value_type>::value >>
+ : std::true_type {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type_impl <
+ BasicJsonType, ConstructibleArrayType,
+ enable_if_t < !std::is_same<ConstructibleArrayType,
+ typename BasicJsonType::value_type>::value&&
+ !is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
+ is_default_constructible<ConstructibleArrayType>::value&&
+(std::is_move_assignable<ConstructibleArrayType>::value ||
+ std::is_copy_assignable<ConstructibleArrayType>::value)&&
+is_detected<iterator_t, ConstructibleArrayType>::value&&
+is_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value&&
+is_detected<range_value_t, ConstructibleArrayType>::value&&
+// special case for types like std::filesystem::path whose iterator's value_type are themselves
+// c.f. https://github.com/nlohmann/json/pull/3073
+!std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value&&
+ is_complete_type <
+ detected_t<range_value_t, ConstructibleArrayType >>::value >>
+{
+ using value_type = range_value_t<ConstructibleArrayType>;
+
+ static constexpr bool value =
+ std::is_same<value_type,
+ typename BasicJsonType::array_t::value_type>::value ||
+ has_from_json<BasicJsonType,
+ value_type>::value ||
+ has_non_default_from_json <
+ BasicJsonType,
+ value_type >::value;
+};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type
+ : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType,
+ typename = void>
+struct is_compatible_integer_type_impl : std::false_type {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type_impl <
+ RealIntegerType, CompatibleNumberIntegerType,
+ enable_if_t < std::is_integral<RealIntegerType>::value&&
+ std::is_integral<CompatibleNumberIntegerType>::value&&
+ !std::is_same<bool, CompatibleNumberIntegerType>::value >>
+{
+ // is there an assert somewhere on overflows?
+ using RealLimits = std::numeric_limits<RealIntegerType>;
+ using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
+
+ static constexpr auto value =
+ is_constructible<RealIntegerType,
+ CompatibleNumberIntegerType>::value &&
+ CompatibleLimits::is_integer &&
+ RealLimits::is_signed == CompatibleLimits::is_signed;
+};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type
+ : is_compatible_integer_type_impl<RealIntegerType,
+ CompatibleNumberIntegerType> {};
+
+template<typename BasicJsonType, typename CompatibleType, typename = void>
+struct is_compatible_type_impl: std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleType>
+struct is_compatible_type_impl <
+ BasicJsonType, CompatibleType,
+ enable_if_t<is_complete_type<CompatibleType>::value >>
+{
+ static constexpr bool value =
+ has_to_json<BasicJsonType, CompatibleType>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleType>
+struct is_compatible_type
+ : is_compatible_type_impl<BasicJsonType, CompatibleType> {};
+
+template<typename T1, typename T2>
+struct is_constructible_tuple : std::false_type {};
+
+template<typename T1, typename... Args>
+struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};
+
+template<typename BasicJsonType, typename T>
+struct is_json_iterator_of : std::false_type {};
+
+template<typename BasicJsonType>
+struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::iterator> : std::true_type {};
+
+template<typename BasicJsonType>
+struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::const_iterator> : std::true_type
+{};
+
+// checks if a given type T is a template specialization of Primary
+template<template <typename...> class Primary, typename T>
+struct is_specialization_of : std::false_type {};
+
+template<template <typename...> class Primary, typename... Args>
+struct is_specialization_of<Primary, Primary<Args...>> : std::true_type {};
+
+template<typename T>
+using is_json_pointer = is_specialization_of<::nlohmann::json_pointer, uncvref_t<T>>;
+
+// checks if A and B are comparable using Compare functor
+template<typename Compare, typename A, typename B, typename = void>
+struct is_comparable : std::false_type {};
+
+template<typename Compare, typename A, typename B>
+struct is_comparable<Compare, A, B, void_t<
+decltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>())),
+decltype(std::declval<Compare>()(std::declval<B>(), std::declval<A>()))
+>> : std::true_type {};
+
+template<typename T>
+using detect_is_transparent = typename T::is_transparent;
+
+// type trait to check if KeyType can be used as object key (without a BasicJsonType)
+// see is_usable_as_basic_json_key_type below
+template<typename Comparator, typename ObjectKeyType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
+ bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
+using is_usable_as_key_type = typename std::conditional <
+ is_comparable<Comparator, ObjectKeyType, KeyTypeCVRef>::value
+ && !(ExcludeObjectKeyType && std::is_same<KeyType,
+ ObjectKeyType>::value)
+ && (!RequireTransparentComparator
+ || is_detected <detect_is_transparent, Comparator>::value)
+ && !is_json_pointer<KeyType>::value,
+ std::true_type,
+ std::false_type >::type;
+
+// type trait to check if KeyType can be used as object key
+// true if:
+// - KeyType is comparable with BasicJsonType::object_t::key_type
+// - if ExcludeObjectKeyType is true, KeyType is not BasicJsonType::object_t::key_type
+// - the comparator is transparent or RequireTransparentComparator is false
+// - KeyType is not a JSON iterator or json_pointer
+template<typename BasicJsonType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
+ bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
+using is_usable_as_basic_json_key_type = typename std::conditional <
+ is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
+ typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
+ RequireTransparentComparator, ExcludeObjectKeyType>::value
+ && !is_json_iterator_of<BasicJsonType, KeyType>::value,
+ std::true_type,
+ std::false_type >::type;
+
+template<typename ObjectType, typename KeyType>
+using detect_erase_with_key_type = decltype(std::declval<ObjectType&>().erase(std::declval<KeyType>()));
+
+// type trait to check if object_t has an erase() member functions accepting KeyType
+template<typename BasicJsonType, typename KeyType>
+using has_erase_with_key_type = typename std::conditional <
+ is_detected <
+ detect_erase_with_key_type,
+ typename BasicJsonType::object_t, KeyType >::value,
+ std::true_type,
+ std::false_type >::type;
+
+// a naive helper to check if a type is an ordered_map (exploits the fact that
+// ordered_map inherits capacity() from std::vector)
+template <typename T>
+struct is_ordered_map
+{
+ using one = char;
+
+ struct two
+ {
+ char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+ };
+
+ template <typename C> static one test( decltype(&C::capacity) ) ;
+ template <typename C> static two test(...);
+
+ enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+};
+
+// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
+template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
+T conditional_static_cast(U value)
+{
+ return static_cast<T>(value);
+}
+
+template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
+T conditional_static_cast(U value)
+{
+ return value;
+}
+
+template<typename... Types>
+using all_integral = conjunction<std::is_integral<Types>...>;
+
+template<typename... Types>
+using all_signed = conjunction<std::is_signed<Types>...>;
+
+template<typename... Types>
+using all_unsigned = conjunction<std::is_unsigned<Types>...>;
+
+// there's a disjunction trait in another PR; replace when merged
+template<typename... Types>
+using same_sign = std::integral_constant < bool,
+ all_signed<Types...>::value || all_unsigned<Types...>::value >;
+
+template<typename OfType, typename T>
+using never_out_of_range = std::integral_constant < bool,
+ (std::is_signed<OfType>::value && (sizeof(T) < sizeof(OfType)))
+ || (same_sign<OfType, T>::value && sizeof(OfType) == sizeof(T)) >;
+
+template<typename OfType, typename T,
+ bool OfTypeSigned = std::is_signed<OfType>::value,
+ bool TSigned = std::is_signed<T>::value>
+struct value_in_range_of_impl2;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, false>
+{
+ static constexpr bool test(T val)
+ {
+ using CommonType = typename std::common_type<OfType, T>::type;
+ return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+ }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, false>
+{
+ static constexpr bool test(T val)
+ {
+ using CommonType = typename std::common_type<OfType, T>::type;
+ return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+ }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, true>
+{
+ static constexpr bool test(T val)
+ {
+ using CommonType = typename std::common_type<OfType, T>::type;
+ return val >= 0 && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+ }
+};
+
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, true>
+{
+ static constexpr bool test(T val)
+ {
+ using CommonType = typename std::common_type<OfType, T>::type;
+ return static_cast<CommonType>(val) >= static_cast<CommonType>((std::numeric_limits<OfType>::min)())
+ && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+ }
+};
+
+template<typename OfType, typename T,
+ bool NeverOutOfRange = never_out_of_range<OfType, T>::value,
+ typename = detail::enable_if_t<all_integral<OfType, T>::value>>
+struct value_in_range_of_impl1;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, false>
+{
+ static constexpr bool test(T val)
+ {
+ return value_in_range_of_impl2<OfType, T>::test(val);
+ }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, true>
+{
+ static constexpr bool test(T /*val*/)
+ {
+ return true;
+ }
+};
+
+template<typename OfType, typename T>
+inline constexpr bool value_in_range_of(T val)
+{
+ return value_in_range_of_impl1<OfType, T>::test(val);
+}
+
+template<bool Value>
+using bool_constant = std::integral_constant<bool, Value>;
+
+///////////////////////////////////////////////////////////////////////////////
+// is_c_string
+///////////////////////////////////////////////////////////////////////////////
+
+namespace impl
+{
+
+template<typename T>
+inline constexpr bool is_c_string()
+{
+ using TUnExt = typename std::remove_extent<T>::type;
+ using TUnCVExt = typename std::remove_cv<TUnExt>::type;
+ using TUnPtr = typename std::remove_pointer<T>::type;
+ using TUnCVPtr = typename std::remove_cv<TUnPtr>::type;
+ return
+ (std::is_array<T>::value && std::is_same<TUnCVExt, char>::value)
+ || (std::is_pointer<T>::value && std::is_same<TUnCVPtr, char>::value);
+}
+
+} // namespace impl
+
+// checks whether T is a [cv] char */[cv] char[] C string
+template<typename T>
+struct is_c_string : bool_constant<impl::is_c_string<T>()> {};
+
+template<typename T>
+using is_c_string_uncvref = is_c_string<uncvref_t<T>>;
+
+///////////////////////////////////////////////////////////////////////////////
+// is_transparent
+///////////////////////////////////////////////////////////////////////////////
+
+namespace impl
+{
+
+template<typename T>
+inline constexpr bool is_transparent()
+{
+ return is_detected<detect_is_transparent, T>::value;
+}
+
+} // namespace impl
+
+// checks whether T has a member named is_transparent
+template<typename T>
+struct is_transparent : bool_constant<impl::is_transparent<T>()> {};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_concat.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstring> // strlen
+#include <string> // string
+#include <utility> // forward
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+inline std::size_t concat_length()
+{
+ return 0;
+}
+
+template<typename... Args>
+inline std::size_t concat_length(const char* cstr, Args&& ... rest);
+
+template<typename StringType, typename... Args>
+inline std::size_t concat_length(const StringType& str, Args&& ... rest);
+
+template<typename... Args>
+inline std::size_t concat_length(const char /*c*/, Args&& ... rest)
+{
+ return 1 + concat_length(std::forward<Args>(rest)...);
+}
+
+template<typename... Args>
+inline std::size_t concat_length(const char* cstr, Args&& ... rest)
+{
+ // cppcheck-suppress ignoredReturnValue
+ return ::strlen(cstr) + concat_length(std::forward<Args>(rest)...);
+}
+
+template<typename StringType, typename... Args>
+inline std::size_t concat_length(const StringType& str, Args&& ... rest)
+{
+ return str.size() + concat_length(std::forward<Args>(rest)...);
+}
+
+template<typename OutStringType>
+inline void concat_into(OutStringType& /*out*/)
+{}
+
+template<typename StringType, typename Arg>
+using string_can_append = decltype(std::declval<StringType&>().append(std::declval < Arg && > ()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append = is_detected<string_can_append, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_op = decltype(std::declval<StringType&>() += std::declval < Arg && > ());
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_op = is_detected<string_can_append_op, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_iter = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().begin(), std::declval<const Arg&>().end()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_iter = is_detected<string_can_append_iter, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_data = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().data(), std::declval<const Arg&>().size()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_data = is_detected<string_can_append_data, StringType, Arg>;
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && detect_string_can_append_op<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest);
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && !detect_string_can_append_op<OutStringType, Arg>::value
+ && detect_string_can_append_iter<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && !detect_string_can_append_op<OutStringType, Arg>::value
+ && !detect_string_can_append_iter<OutStringType, Arg>::value
+ && detect_string_can_append_data<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
+
+template<typename OutStringType, typename Arg, typename... Args,
+ enable_if_t<detect_string_can_append<OutStringType, Arg>::value, int> = 0>
+inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest)
+{
+ out.append(std::forward<Arg>(arg));
+ concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && detect_string_can_append_op<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest)
+{
+ out += std::forward<Arg>(arg);
+ concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && !detect_string_can_append_op<OutStringType, Arg>::value
+ && detect_string_can_append_iter<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
+{
+ out.append(arg.begin(), arg.end());
+ concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && !detect_string_can_append_op<OutStringType, Arg>::value
+ && !detect_string_can_append_iter<OutStringType, Arg>::value
+ && detect_string_can_append_data<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
+{
+ out.append(arg.data(), arg.size());
+ concat_into(out, std::forward<Args>(rest)...);
+}
+
+template<typename OutStringType = std::string, typename... Args>
+inline OutStringType concat(Args && ... args)
+{
+ OutStringType str;
+ str.reserve(concat_length(std::forward<Args>(args)...));
+ concat_into(str, std::forward<Args>(args)...);
+ return str;
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+////////////////
+// exceptions //
+////////////////
+
+/// @brief general exception of the @ref basic_json class
+/// @sa https://json.nlohmann.me/api/basic_json/exception/
+class exception : public std::exception
+{
+ public:
+ /// returns the explanatory string
+ const char* what() const noexcept override
+ {
+ return m.what();
+ }
+
+ /// the id of the exception
+ const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
+
+ protected:
+ JSON_HEDLEY_NON_NULL(3)
+ exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)
+
+ static std::string name(const std::string& ename, int id_)
+ {
+ return concat("[json.exception.", ename, '.', std::to_string(id_), "] ");
+ }
+
+ static std::string diagnostics(std::nullptr_t /*leaf_element*/)
+ {
+ return "";
+ }
+
+ template<typename BasicJsonType>
+ static std::string diagnostics(const BasicJsonType* leaf_element)
+ {
+#if JSON_DIAGNOSTICS
+ std::vector<std::string> tokens;
+ for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent)
+ {
+ switch (current->m_parent->type())
+ {
+ case value_t::array:
+ {
+ for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i)
+ {
+ if (&current->m_parent->m_value.array->operator[](i) == current)
+ {
+ tokens.emplace_back(std::to_string(i));
+ break;
+ }
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ for (const auto& element : *current->m_parent->m_value.object)
+ {
+ if (&element.second == current)
+ {
+ tokens.emplace_back(element.first.c_str());
+ break;
+ }
+ }
+ break;
+ }
+
+ case value_t::null: // LCOV_EXCL_LINE
+ case value_t::string: // LCOV_EXCL_LINE
+ case value_t::boolean: // LCOV_EXCL_LINE
+ case value_t::number_integer: // LCOV_EXCL_LINE
+ case value_t::number_unsigned: // LCOV_EXCL_LINE
+ case value_t::number_float: // LCOV_EXCL_LINE
+ case value_t::binary: // LCOV_EXCL_LINE
+ case value_t::discarded: // LCOV_EXCL_LINE
+ default: // LCOV_EXCL_LINE
+ break; // LCOV_EXCL_LINE
+ }
+ }
+
+ if (tokens.empty())
+ {
+ return "";
+ }
+
+ auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
+ [](const std::string & a, const std::string & b)
+ {
+ return concat(a, '/', detail::escape(b));
+ });
+ return concat('(', str, ") ");
+#else
+ static_cast<void>(leaf_element);
+ return "";
+#endif
+ }
+
+ private:
+ /// an exception object as storage for error messages
+ std::runtime_error m;
+};
+
+/// @brief exception indicating a parse error
+/// @sa https://json.nlohmann.me/api/basic_json/parse_error/
+class parse_error : public exception
+{
+ public:
+ /*!
+ @brief create a parse error exception
+ @param[in] id_ the id of the exception
+ @param[in] pos the position where the error occurred (or with
+ chars_read_total=0 if the position cannot be
+ determined)
+ @param[in] what_arg the explanatory string
+ @return parse_error object
+ */
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("parse_error", id_), "parse error",
+ position_string(pos), ": ", exception::diagnostics(context), what_arg);
+ return {id_, pos.chars_read_total, w.c_str()};
+ }
+
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("parse_error", id_), "parse error",
+ (byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""),
+ ": ", exception::diagnostics(context), what_arg);
+ return {id_, byte_, w.c_str()};
+ }
+
+ /*!
+ @brief byte index of the parse error
+
+ The byte index of the last read character in the input file.
+
+ @note For an input with n bytes, 1 is the index of the first character and
+ n+1 is the index of the terminating null byte or the end of file.
+ This also holds true when reading a byte vector (CBOR or MessagePack).
+ */
+ const std::size_t byte;
+
+ private:
+ parse_error(int id_, std::size_t byte_, const char* what_arg)
+ : exception(id_, what_arg), byte(byte_) {}
+
+ static std::string position_string(const position_t& pos)
+ {
+ return concat(" at line ", std::to_string(pos.lines_read + 1),
+ ", column ", std::to_string(pos.chars_read_current_line));
+ }
+};
+
+/// @brief exception indicating errors with iterators
+/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/
+class invalid_iterator : public exception
+{
+ public:
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg);
+ return {id_, w.c_str()};
+ }
+
+ private:
+ JSON_HEDLEY_NON_NULL(3)
+ invalid_iterator(int id_, const char* what_arg)
+ : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating executing a member function with a wrong type
+/// @sa https://json.nlohmann.me/api/basic_json/type_error/
+class type_error : public exception
+{
+ public:
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg);
+ return {id_, w.c_str()};
+ }
+
+ private:
+ JSON_HEDLEY_NON_NULL(3)
+ type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating access out of the defined range
+/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/
+class out_of_range : public exception
+{
+ public:
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg);
+ return {id_, w.c_str()};
+ }
+
+ private:
+ JSON_HEDLEY_NON_NULL(3)
+ out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating other library errors
+/// @sa https://json.nlohmann.me/api/basic_json/other_error/
+class other_error : public exception
+{
+ public:
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg);
+ return {id_, w.c_str()};
+ }
+
+ private:
+ JSON_HEDLEY_NON_NULL(3)
+ other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/identity_tag.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// dispatching helper struct
+template <class T> struct identity_tag {};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/std_fs.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+#if JSON_HAS_EXPERIMENTAL_FILESYSTEM
+#include <experimental/filesystem>
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+namespace std_fs = std::experimental::filesystem;
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+#elif JSON_HAS_FILESYSTEM
+#include <filesystem>
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+namespace std_fs = std::filesystem;
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+#endif
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j));
+ }
+ n = nullptr;
+}
+
+// overloads for basic_json template parameters
+template < typename BasicJsonType, typename ArithmeticType,
+ enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
+ !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+ int > = 0 >
+void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
+{
+ switch (static_cast<value_t>(j))
+ {
+ case value_t::number_unsigned:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+ break;
+ }
+ case value_t::number_integer:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+ break;
+ }
+ case value_t::number_float:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+ break;
+ }
+
+ case value_t::null:
+ case value_t::object:
+ case value_t::array:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
+ }
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j));
+ }
+ b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+ }
+ s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template <
+ typename BasicJsonType, typename StringType,
+ enable_if_t <
+ std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
+ && is_detected_exact<typename BasicJsonType::string_t::value_type, value_type_t, StringType>::value
+ && !std::is_same<typename BasicJsonType::string_t, StringType>::value
+ && !is_json_ref<StringType>::value, int > = 0 >
+inline void from_json(const BasicJsonType& j, StringType& s)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+ }
+
+ s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+#if !JSON_DISABLE_ENUM_SERIALIZATION
+template<typename BasicJsonType, typename EnumType,
+ enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, EnumType& e)
+{
+ typename std::underlying_type<EnumType>::type val;
+ get_arithmetic_value(j, val);
+ e = static_cast<EnumType>(val);
+}
+#endif // JSON_DISABLE_ENUM_SERIALIZATION
+
+// forward_list doesn't have an insert method
+template<typename BasicJsonType, typename T, typename Allocator,
+ enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+ l.clear();
+ std::transform(j.rbegin(), j.rend(),
+ std::front_inserter(l), [](const BasicJsonType & i)
+ {
+ return i.template get<T>();
+ });
+}
+
+// valarray doesn't have an insert method
+template<typename BasicJsonType, typename T,
+ enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+ l.resize(j.size());
+ std::transform(j.begin(), j.end(), std::begin(l),
+ [](const BasicJsonType & elem)
+ {
+ return elem.template get<T>();
+ });
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+-> decltype(j.template get<T>(), void())
+{
+ for (std::size_t i = 0; i < N; ++i)
+ {
+ arr[i] = j.at(i).template get<T>();
+ }
+}
+
+template<typename BasicJsonType>
+inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
+{
+ arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
+ priority_tag<2> /*unused*/)
+-> decltype(j.template get<T>(), void())
+{
+ for (std::size_t i = 0; i < N; ++i)
+ {
+ arr[i] = j.at(i).template get<T>();
+ }
+}
+
+template<typename BasicJsonType, typename ConstructibleArrayType,
+ enable_if_t<
+ std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
+ int> = 0>
+auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
+-> decltype(
+ arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
+ j.template get<typename ConstructibleArrayType::value_type>(),
+ void())
+{
+ using std::end;
+
+ ConstructibleArrayType ret;
+ ret.reserve(j.size());
+ std::transform(j.begin(), j.end(),
+ std::inserter(ret, end(ret)), [](const BasicJsonType & i)
+ {
+ // get<BasicJsonType>() returns *this, this won't call a from_json
+ // method when value_type is BasicJsonType
+ return i.template get<typename ConstructibleArrayType::value_type>();
+ });
+ arr = std::move(ret);
+}
+
+template<typename BasicJsonType, typename ConstructibleArrayType,
+ enable_if_t<
+ std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
+ int> = 0>
+inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
+ priority_tag<0> /*unused*/)
+{
+ using std::end;
+
+ ConstructibleArrayType ret;
+ std::transform(
+ j.begin(), j.end(), std::inserter(ret, end(ret)),
+ [](const BasicJsonType & i)
+ {
+ // get<BasicJsonType>() returns *this, this won't call a from_json
+ // method when value_type is BasicJsonType
+ return i.template get<typename ConstructibleArrayType::value_type>();
+ });
+ arr = std::move(ret);
+}
+
+template < typename BasicJsonType, typename ConstructibleArrayType,
+ enable_if_t <
+ is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
+ !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
+ !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
+ !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
+ !is_basic_json<ConstructibleArrayType>::value,
+ int > = 0 >
+auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
+-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),
+j.template get<typename ConstructibleArrayType::value_type>(),
+void())
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+
+ from_json_array_impl(j, arr, priority_tag<3> {});
+}
+
+template < typename BasicJsonType, typename T, std::size_t... Idx >
+std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,
+ identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
+{
+ return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
+}
+
+template < typename BasicJsonType, typename T, std::size_t N >
+auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
+-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+
+ return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j));
+ }
+
+ bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
+}
+
+template<typename BasicJsonType, typename ConstructibleObjectType,
+ enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
+ }
+
+ ConstructibleObjectType ret;
+ const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
+ using value_type = typename ConstructibleObjectType::value_type;
+ std::transform(
+ inner_object->begin(), inner_object->end(),
+ std::inserter(ret, ret.begin()),
+ [](typename BasicJsonType::object_t::value_type const & p)
+ {
+ return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
+ });
+ obj = std::move(ret);
+}
+
+// overload for arithmetic types, not chosen for basic_json template arguments
+// (BooleanType, etc..); note: Is it really necessary to provide explicit
+// overloads for boolean_t etc. in case of a custom BooleanType which is not
+// an arithmetic type?
+template < typename BasicJsonType, typename ArithmeticType,
+ enable_if_t <
+ std::is_arithmetic<ArithmeticType>::value&&
+ !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
+ !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
+ !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
+ !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+ int > = 0 >
+inline void from_json(const BasicJsonType& j, ArithmeticType& val)
+{
+ switch (static_cast<value_t>(j))
+ {
+ case value_t::number_unsigned:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+ break;
+ }
+ case value_t::number_integer:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+ break;
+ }
+ case value_t::number_float:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+ break;
+ }
+ case value_t::boolean:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
+ break;
+ }
+
+ case value_t::null:
+ case value_t::object:
+ case value_t::array:
+ case value_t::string:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
+ }
+}
+
+template<typename BasicJsonType, typename... Args, std::size_t... Idx>
+std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
+{
+ return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
+}
+
+template < typename BasicJsonType, class A1, class A2 >
+std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
+{
+ return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),
+ std::forward<BasicJsonType>(j).at(1).template get<A2>()};
+}
+
+template<typename BasicJsonType, typename A1, typename A2>
+inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
+{
+ p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
+}
+
+template<typename BasicJsonType, typename... Args>
+std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
+{
+ return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
+}
+
+template<typename BasicJsonType, typename... Args>
+inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
+{
+ t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
+}
+
+template<typename BasicJsonType, typename TupleRelated>
+auto from_json(BasicJsonType&& j, TupleRelated&& t)
+-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+
+ return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
+}
+
+template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
+ typename = enable_if_t < !std::is_constructible <
+ typename BasicJsonType::string_t, Key >::value >>
+inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+ m.clear();
+ for (const auto& p : j)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
+ }
+ m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
+ }
+}
+
+template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
+ typename = enable_if_t < !std::is_constructible <
+ typename BasicJsonType::string_t, Key >::value >>
+inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+ m.clear();
+ for (const auto& p : j)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
+ }
+ m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
+ }
+}
+
+#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, std_fs::path& p)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+ }
+ p = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+#endif
+
+struct from_json_fn
+{
+ template<typename BasicJsonType, typename T>
+ auto operator()(const BasicJsonType& j, T&& val) const
+ noexcept(noexcept(from_json(j, std::forward<T>(val))))
+ -> decltype(from_json(j, std::forward<T>(val)))
+ {
+ return from_json(j, std::forward<T>(val));
+ }
+};
+
+} // namespace detail
+
+#ifndef JSON_HAS_CPP_17
+/// namespace to hold default `from_json` function
+/// to see why this is required:
+/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
+namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
+{
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)
+ detail::static_const<detail::from_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
+} // namespace
+#endif
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/conversions/to_json.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // copy
+#include <iterator> // begin, end
+#include <string> // string
+#include <tuple> // tuple, get
+#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
+#include <utility> // move, forward, declval, pair
+#include <valarray> // valarray
+#include <vector> // vector
+
+// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // size_t
+#include <iterator> // input_iterator_tag
+#include <string> // string, to_string
+#include <tuple> // tuple_size, get, tuple_element
+#include <utility> // move
+
+#if JSON_HAS_RANGES
+ #include <ranges> // enable_borrowed_range
+#endif
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename string_type>
+void int_to_string( string_type& target, std::size_t value )
+{
+ // For ADL
+ using std::to_string;
+ target = to_string(value);
+}
+template<typename IteratorType> class iteration_proxy_value
+{
+ public:
+ using difference_type = std::ptrdiff_t;
+ using value_type = iteration_proxy_value;
+ using pointer = value_type *;
+ using reference = value_type &;
+ using iterator_category = std::input_iterator_tag;
+ using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
+
+ private:
+ /// the iterator
+ IteratorType anchor{};
+ /// an index for arrays (used to create key names)
+ std::size_t array_index = 0;
+ /// last stringified array index
+ mutable std::size_t array_index_last = 0;
+ /// a string representation of the array index
+ mutable string_type array_index_str = "0";
+ /// an empty string (to return a reference for primitive values)
+ string_type empty_str{};
+
+ public:
+ explicit iteration_proxy_value() = default;
+ explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)
+ noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+ && std::is_nothrow_default_constructible<string_type>::value)
+ : anchor(std::move(it))
+ , array_index(array_index_)
+ {}
+
+ iteration_proxy_value(iteration_proxy_value const&) = default;
+ iteration_proxy_value& operator=(iteration_proxy_value const&) = default;
+ // older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions
+ iteration_proxy_value(iteration_proxy_value&&)
+ noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+ && std::is_nothrow_move_constructible<string_type>::value) = default;
+ iteration_proxy_value& operator=(iteration_proxy_value&&)
+ noexcept(std::is_nothrow_move_assignable<IteratorType>::value
+ && std::is_nothrow_move_assignable<string_type>::value) = default;
+ ~iteration_proxy_value() = default;
+
+ /// dereference operator (needed for range-based for)
+ const iteration_proxy_value& operator*() const
+ {
+ return *this;
+ }
+
+ /// increment operator (needed for range-based for)
+ iteration_proxy_value& operator++()
+ {
+ ++anchor;
+ ++array_index;
+
+ return *this;
+ }
+
+ iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)
+ {
+ auto tmp = iteration_proxy_value(anchor, array_index);
+ ++anchor;
+ ++array_index;
+ return tmp;
+ }
+
+ /// equality operator (needed for InputIterator)
+ bool operator==(const iteration_proxy_value& o) const
+ {
+ return anchor == o.anchor;
+ }
+
+ /// inequality operator (needed for range-based for)
+ bool operator!=(const iteration_proxy_value& o) const
+ {
+ return anchor != o.anchor;
+ }
+
+ /// return key of the iterator
+ const string_type& key() const
+ {
+ JSON_ASSERT(anchor.m_object != nullptr);
+
+ switch (anchor.m_object->type())
+ {
+ // use integer array index as key
+ case value_t::array:
+ {
+ if (array_index != array_index_last)
+ {
+ int_to_string( array_index_str, array_index );
+ array_index_last = array_index;
+ }
+ return array_index_str;
+ }
+
+ // use key from the object
+ case value_t::object:
+ return anchor.key();
+
+ // use an empty key for all primitive types
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ return empty_str;
+ }
+ }
+
+ /// return value of the iterator
+ typename IteratorType::reference value() const
+ {
+ return anchor.value();
+ }
+};
+
+/// proxy class for the items() function
+template<typename IteratorType> class iteration_proxy
+{
+ private:
+ /// the container to iterate
+ typename IteratorType::pointer container = nullptr;
+
+ public:
+ explicit iteration_proxy() = default;
+
+ /// construct iteration proxy from a container
+ explicit iteration_proxy(typename IteratorType::reference cont) noexcept
+ : container(&cont) {}
+
+ iteration_proxy(iteration_proxy const&) = default;
+ iteration_proxy& operator=(iteration_proxy const&) = default;
+ iteration_proxy(iteration_proxy&&) noexcept = default;
+ iteration_proxy& operator=(iteration_proxy&&) noexcept = default;
+ ~iteration_proxy() = default;
+
+ /// return iterator begin (needed for range-based for)
+ iteration_proxy_value<IteratorType> begin() const noexcept
+ {
+ return iteration_proxy_value<IteratorType>(container->begin());
+ }
+
+ /// return iterator end (needed for range-based for)
+ iteration_proxy_value<IteratorType> end() const noexcept
+ {
+ return iteration_proxy_value<IteratorType>(container->end());
+ }
+};
+
+// Structured Bindings Support
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>
+auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())
+{
+ return i.key();
+}
+// Structured Bindings Support
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>
+auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())
+{
+ return i.value();
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// The Addition to the STD Namespace is required to add
+// Structured Bindings Support to the iteration_proxy_value class
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+namespace std
+{
+
+#if defined(__clang__)
+ // Fix: https://github.com/nlohmann/json/issues/1401
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wmismatched-tags"
+#endif
+template<typename IteratorType>
+class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>> // NOLINT(cert-dcl58-cpp)
+ : public std::integral_constant<std::size_t, 2> {};
+
+template<std::size_t N, typename IteratorType>
+class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >> // NOLINT(cert-dcl58-cpp)
+{
+ public:
+ using type = decltype(
+ get<N>(std::declval <
+ ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));
+};
+#if defined(__clang__)
+ #pragma clang diagnostic pop
+#endif
+
+} // namespace std
+
+#if JSON_HAS_RANGES
+ template <typename IteratorType>
+ inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;
+#endif
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/std_fs.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+//////////////////
+// constructors //
+//////////////////
+
+/*
+ * Note all external_constructor<>::construct functions need to call
+ * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an
+ * allocated value (e.g., a string). See bug issue
+ * https://github.com/nlohmann/json/issues/2865 for more information.
+ */
+
+template<value_t> struct external_constructor;
+
+template<>
+struct external_constructor<value_t::boolean>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::boolean;
+ j.m_value = b;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::string>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::string;
+ j.m_value = s;
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::string;
+ j.m_value = std::move(s);
+ j.assert_invariant();
+ }
+
+ template < typename BasicJsonType, typename CompatibleStringType,
+ enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
+ int > = 0 >
+ static void construct(BasicJsonType& j, const CompatibleStringType& str)
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::string;
+ j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::binary>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::binary;
+ j.m_value = typename BasicJsonType::binary_t(b);
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::binary;
+ j.m_value = typename BasicJsonType::binary_t(std::move(b));
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_float>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::number_float;
+ j.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_unsigned>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::number_unsigned;
+ j.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_integer>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::number_integer;
+ j.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::array>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::array;
+ j.m_value = arr;
+ j.set_parents();
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::array;
+ j.m_value = std::move(arr);
+ j.set_parents();
+ j.assert_invariant();
+ }
+
+ template < typename BasicJsonType, typename CompatibleArrayType,
+ enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
+ int > = 0 >
+ static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
+ {
+ using std::begin;
+ using std::end;
+
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::array;
+ j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
+ j.set_parents();
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const std::vector<bool>& arr)
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::array;
+ j.m_value = value_t::array;
+ j.m_value.array->reserve(arr.size());
+ for (const bool x : arr)
+ {
+ j.m_value.array->push_back(x);
+ j.set_parent(j.m_value.array->back());
+ }
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+ static void construct(BasicJsonType& j, const std::valarray<T>& arr)
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::array;
+ j.m_value = value_t::array;
+ j.m_value.array->resize(arr.size());
+ if (arr.size() > 0)
+ {
+ std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
+ }
+ j.set_parents();
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::object>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::object;
+ j.m_value = obj;
+ j.set_parents();
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+ {
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::object;
+ j.m_value = std::move(obj);
+ j.set_parents();
+ j.assert_invariant();
+ }
+
+ template < typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
+ static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
+ {
+ using std::begin;
+ using std::end;
+
+ j.m_value.destroy(j.m_type);
+ j.m_type = value_t::object;
+ j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
+ j.set_parents();
+ j.assert_invariant();
+ }
+};
+
+/////////////
+// to_json //
+/////////////
+
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
+inline void to_json(BasicJsonType& j, T b) noexcept
+{
+ external_constructor<value_t::boolean>::construct(j, b);
+}
+
+template < typename BasicJsonType, typename BoolRef,
+ enable_if_t <
+ ((std::is_same<std::vector<bool>::reference, BoolRef>::value
+ && !std::is_same <std::vector<bool>::reference, typename BasicJsonType::boolean_t&>::value)
+ || (std::is_same<std::vector<bool>::const_reference, BoolRef>::value
+ && !std::is_same <detail::uncvref_t<std::vector<bool>::const_reference>,
+ typename BasicJsonType::boolean_t >::value))
+ && std::is_convertible<const BoolRef&, typename BasicJsonType::boolean_t>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const BoolRef& b) noexcept
+{
+ external_constructor<value_t::boolean>::construct(j, static_cast<typename BasicJsonType::boolean_t>(b));
+}
+
+template<typename BasicJsonType, typename CompatibleString,
+ enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const CompatibleString& s)
+{
+ external_constructor<value_t::string>::construct(j, s);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+{
+ external_constructor<value_t::string>::construct(j, std::move(s));
+}
+
+template<typename BasicJsonType, typename FloatType,
+ enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, FloatType val) noexcept
+{
+ external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
+ enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
+{
+ external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberIntegerType,
+ enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
+{
+ external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
+}
+
+#if !JSON_DISABLE_ENUM_SERIALIZATION
+template<typename BasicJsonType, typename EnumType,
+ enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, EnumType e) noexcept
+{
+ using underlying_type = typename std::underlying_type<EnumType>::type;
+ external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
+}
+#endif // JSON_DISABLE_ENUM_SERIALIZATION
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const std::vector<bool>& e)
+{
+ external_constructor<value_t::array>::construct(j, e);
+}
+
+template < typename BasicJsonType, typename CompatibleArrayType,
+ enable_if_t < is_compatible_array_type<BasicJsonType,
+ CompatibleArrayType>::value&&
+ !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
+ !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
+ !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
+ !is_basic_json<CompatibleArrayType>::value,
+ int > = 0 >
+inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
+{
+ external_constructor<value_t::array>::construct(j, arr);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
+{
+ external_constructor<value_t::binary>::construct(j, bin);
+}
+
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
+{
+ external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+{
+ external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template < typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
+{
+ external_constructor<value_t::object>::construct(j, obj);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+{
+ external_constructor<value_t::object>::construct(j, std::move(obj));
+}
+
+template <
+ typename BasicJsonType, typename T, std::size_t N,
+ enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
+ const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+ int > = 0 >
+inline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+{
+ external_constructor<value_t::array>::construct(j, arr);
+}
+
+template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
+{
+ j = { p.first, p.second };
+}
+
+// for https://github.com/nlohmann/json/pull/1134
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const T& b)
+{
+ j = { {b.key(), b.value()} };
+}
+
+template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
+inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
+{
+ j = { std::get<Idx>(t)... };
+}
+
+template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
+inline void to_json(BasicJsonType& j, const T& t)
+{
+ to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
+}
+
+#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const std_fs::path& p)
+{
+ j = p.string();
+}
+#endif
+
+struct to_json_fn
+{
+ template<typename BasicJsonType, typename T>
+ auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
+ -> decltype(to_json(j, std::forward<T>(val)), void())
+ {
+ return to_json(j, std::forward<T>(val));
+ }
+};
+} // namespace detail
+
+#ifndef JSON_HAS_CPP_17
+/// namespace to hold default `to_json` function
+/// to see why this is required:
+/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
+namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
+{
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)
+ detail::static_const<detail::to_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
+} // namespace
+#endif
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/identity_tag.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @sa https://json.nlohmann.me/api/adl_serializer/
+template<typename ValueType, typename>
+struct adl_serializer
+{
+ /// @brief convert a JSON value to any value type
+ /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
+ template<typename BasicJsonType, typename TargetType = ValueType>
+ static auto from_json(BasicJsonType && j, TargetType& val) noexcept(
+ noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
+ -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
+ {
+ ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
+ }
+
+ /// @brief convert a JSON value to any value type
+ /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
+ template<typename BasicJsonType, typename TargetType = ValueType>
+ static auto from_json(BasicJsonType && j) noexcept(
+ noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))
+ -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))
+ {
+ return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});
+ }
+
+ /// @brief convert any value type to a JSON value
+ /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/
+ template<typename BasicJsonType, typename TargetType = ValueType>
+ static auto to_json(BasicJsonType& j, TargetType && val) noexcept(
+ noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))
+ -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())
+ {
+ ::nlohmann::to_json(j, std::forward<TargetType>(val));
+ }
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/byte_container_with_subtype.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // uint8_t, uint64_t
+#include <tuple> // tie
+#include <utility> // move
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @brief an internal type for a backed binary type
+/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/
+template<typename BinaryType>
+class byte_container_with_subtype : public BinaryType
+{
+ public:
+ using container_type = BinaryType;
+ using subtype_type = std::uint64_t;
+
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+ byte_container_with_subtype() noexcept(noexcept(container_type()))
+ : container_type()
+ {}
+
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+ byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))
+ : container_type(b)
+ {}
+
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+ byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))
+ : container_type(std::move(b))
+ {}
+
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+ byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b)))
+ : container_type(b)
+ , m_subtype(subtype_)
+ , m_has_subtype(true)
+ {}
+
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+ byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b))))
+ : container_type(std::move(b))
+ , m_subtype(subtype_)
+ , m_has_subtype(true)
+ {}
+
+ bool operator==(const byte_container_with_subtype& rhs) const
+ {
+ return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
+ std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
+ }
+
+ bool operator!=(const byte_container_with_subtype& rhs) const
+ {
+ return !(rhs == *this);
+ }
+
+ /// @brief sets the binary subtype
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/
+ void set_subtype(subtype_type subtype_) noexcept
+ {
+ m_subtype = subtype_;
+ m_has_subtype = true;
+ }
+
+ /// @brief return the binary subtype
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/
+ constexpr subtype_type subtype() const noexcept
+ {
+ return m_has_subtype ? m_subtype : static_cast<subtype_type>(-1);
+ }
+
+ /// @brief return whether the value has a subtype
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/
+ constexpr bool has_subtype() const noexcept
+ {
+ return m_has_subtype;
+ }
+
+ /// @brief clears the binary subtype
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/
+ void clear_subtype() noexcept
+ {
+ m_subtype = 0;
+ m_has_subtype = false;
+ }
+
+ private:
+ subtype_type m_subtype = 0;
+ bool m_has_subtype = false;
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/conversions/from_json.hpp>
+
+// #include <nlohmann/detail/conversions/to_json.hpp>
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/hash.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // uint8_t
+#include <cstddef> // size_t
+#include <functional> // hash
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// boost::hash_combine
+inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
+{
+ seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);
+ return seed;
+}
+
+/*!
+@brief hash a JSON value
+
+The hash function tries to rely on std::hash where possible. Furthermore, the
+type of the JSON value is taken into account to have different hash values for
+null, 0, 0U, and false, etc.
+
+@tparam BasicJsonType basic_json specialization
+@param j JSON value to hash
+@return hash value of j
+*/
+template<typename BasicJsonType>
+std::size_t hash(const BasicJsonType& j)
+{
+ using string_t = typename BasicJsonType::string_t;
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+
+ const auto type = static_cast<std::size_t>(j.type());
+ switch (j.type())
+ {
+ case BasicJsonType::value_t::null:
+ case BasicJsonType::value_t::discarded:
+ {
+ return combine(type, 0);
+ }
+
+ case BasicJsonType::value_t::object:
+ {
+ auto seed = combine(type, j.size());
+ for (const auto& element : j.items())
+ {
+ const auto h = std::hash<string_t> {}(element.key());
+ seed = combine(seed, h);
+ seed = combine(seed, hash(element.value()));
+ }
+ return seed;
+ }
+
+ case BasicJsonType::value_t::array:
+ {
+ auto seed = combine(type, j.size());
+ for (const auto& element : j)
+ {
+ seed = combine(seed, hash(element));
+ }
+ return seed;
+ }
+
+ case BasicJsonType::value_t::string:
+ {
+ const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());
+ return combine(type, h);
+ }
+
+ case BasicJsonType::value_t::boolean:
+ {
+ const auto h = std::hash<bool> {}(j.template get<bool>());
+ return combine(type, h);
+ }
+
+ case BasicJsonType::value_t::number_integer:
+ {
+ const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
+ return combine(type, h);
+ }
+
+ case BasicJsonType::value_t::number_unsigned:
+ {
+ const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
+ return combine(type, h);
+ }
+
+ case BasicJsonType::value_t::number_float:
+ {
+ const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
+ return combine(type, h);
+ }
+
+ case BasicJsonType::value_t::binary:
+ {
+ auto seed = combine(type, j.get_binary().size());
+ const auto h = std::hash<bool> {}(j.get_binary().has_subtype());
+ seed = combine(seed, h);
+ seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype()));
+ for (const auto byte : j.get_binary())
+ {
+ seed = combine(seed, std::hash<std::uint8_t> {}(byte));
+ }
+ return seed;
+ }
+
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ return 0; // LCOV_EXCL_LINE
+ }
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/binary_reader.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // generate_n
+#include <array> // array
+#include <cmath> // ldexp
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstdio> // snprintf
+#include <cstring> // memcpy
+#include <iterator> // back_inserter
+#include <limits> // numeric_limits
+#include <string> // char_traits, string
+#include <utility> // make_pair, move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <cstring> // strlen
+#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
+#include <memory> // shared_ptr, make_shared, addressof
+#include <numeric> // accumulate
+#include <string> // string, char_traits
+#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
+#include <utility> // pair, declval
+
+#ifndef JSON_NO_IO
+ #include <cstdio> // FILE *
+ #include <istream> // istream
+#endif // JSON_NO_IO
+
+// #include <nlohmann/detail/iterators/iterator_traits.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// the supported input formats
+enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };
+
+////////////////////
+// input adapters //
+////////////////////
+
+#ifndef JSON_NO_IO
+/*!
+Input adapter for stdio file access. This adapter read only 1 byte and do not use any
+ buffer. This adapter is a very low level adapter.
+*/
+class file_input_adapter
+{
+ public:
+ using char_type = char;
+
+ JSON_HEDLEY_NON_NULL(2)
+ explicit file_input_adapter(std::FILE* f) noexcept
+ : m_file(f)
+ {
+ JSON_ASSERT(m_file != nullptr);
+ }
+
+ // make class move-only
+ file_input_adapter(const file_input_adapter&) = delete;
+ file_input_adapter(file_input_adapter&&) noexcept = default;
+ file_input_adapter& operator=(const file_input_adapter&) = delete;
+ file_input_adapter& operator=(file_input_adapter&&) = delete;
+ ~file_input_adapter() = default;
+
+ std::char_traits<char>::int_type get_character() noexcept
+ {
+ return std::fgetc(m_file);
+ }
+
+ private:
+ /// the file pointer to read from
+ std::FILE* m_file;
+};
+
+
+/*!
+Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
+beginning of input. Does not support changing the underlying std::streambuf
+in mid-input. Maintains underlying std::istream and std::streambuf to support
+subsequent use of standard std::istream operations to process any input
+characters following those used in parsing the JSON input. Clears the
+std::istream flags; any input errors (e.g., EOF) will be detected by the first
+subsequent call for input from the std::istream.
+*/
+class input_stream_adapter
+{
+ public:
+ using char_type = char;
+
+ ~input_stream_adapter()
+ {
+ // clear stream flags; we use underlying streambuf I/O, do not
+ // maintain ifstream flags, except eof
+ if (is != nullptr)
+ {
+ is->clear(is->rdstate() & std::ios::eofbit);
+ }
+ }
+
+ explicit input_stream_adapter(std::istream& i)
+ : is(&i), sb(i.rdbuf())
+ {}
+
+ // delete because of pointer members
+ input_stream_adapter(const input_stream_adapter&) = delete;
+ input_stream_adapter& operator=(input_stream_adapter&) = delete;
+ input_stream_adapter& operator=(input_stream_adapter&&) = delete;
+
+ input_stream_adapter(input_stream_adapter&& rhs) noexcept
+ : is(rhs.is), sb(rhs.sb)
+ {
+ rhs.is = nullptr;
+ rhs.sb = nullptr;
+ }
+
+ // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
+ // ensure that std::char_traits<char>::eof() and the character 0xFF do not
+ // end up as the same value, e.g. 0xFFFFFFFF.
+ std::char_traits<char>::int_type get_character()
+ {
+ auto res = sb->sbumpc();
+ // set eof manually, as we don't use the istream interface.
+ if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
+ {
+ is->clear(is->rdstate() | std::ios::eofbit);
+ }
+ return res;
+ }
+
+ private:
+ /// the associated input stream
+ std::istream* is = nullptr;
+ std::streambuf* sb = nullptr;
+};
+#endif // JSON_NO_IO
+
+// General-purpose iterator-based adapter. It might not be as fast as
+// theoretically possible for some containers, but it is extremely versatile.
+template<typename IteratorType>
+class iterator_input_adapter
+{
+ public:
+ using char_type = typename std::iterator_traits<IteratorType>::value_type;
+
+ iterator_input_adapter(IteratorType first, IteratorType last)
+ : current(std::move(first)), end(std::move(last))
+ {}
+
+ typename std::char_traits<char_type>::int_type get_character()
+ {
+ if (JSON_HEDLEY_LIKELY(current != end))
+ {
+ auto result = std::char_traits<char_type>::to_int_type(*current);
+ std::advance(current, 1);
+ return result;
+ }
+
+ return std::char_traits<char_type>::eof();
+ }
+
+ private:
+ IteratorType current;
+ IteratorType end;
+
+ template<typename BaseInputAdapter, size_t T>
+ friend struct wide_string_input_helper;
+
+ bool empty() const
+ {
+ return current == end;
+ }
+};
+
+
+template<typename BaseInputAdapter, size_t T>
+struct wide_string_input_helper;
+
+template<typename BaseInputAdapter>
+struct wide_string_input_helper<BaseInputAdapter, 4>
+{
+ // UTF-32
+ static void fill_buffer(BaseInputAdapter& input,
+ std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
+ size_t& utf8_bytes_index,
+ size_t& utf8_bytes_filled)
+ {
+ utf8_bytes_index = 0;
+
+ if (JSON_HEDLEY_UNLIKELY(input.empty()))
+ {
+ utf8_bytes[0] = std::char_traits<char>::eof();
+ utf8_bytes_filled = 1;
+ }
+ else
+ {
+ // get the current character
+ const auto wc = input.get_character();
+
+ // UTF-32 to UTF-8 encoding
+ if (wc < 0x80)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+ utf8_bytes_filled = 1;
+ }
+ else if (wc <= 0x7FF)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+ utf8_bytes_filled = 2;
+ }
+ else if (wc <= 0xFFFF)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+ utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+ utf8_bytes_filled = 3;
+ }
+ else if (wc <= 0x10FFFF)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));
+ utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+ utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+ utf8_bytes_filled = 4;
+ }
+ else
+ {
+ // unknown character
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+ utf8_bytes_filled = 1;
+ }
+ }
+ }
+};
+
+template<typename BaseInputAdapter>
+struct wide_string_input_helper<BaseInputAdapter, 2>
+{
+ // UTF-16
+ static void fill_buffer(BaseInputAdapter& input,
+ std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
+ size_t& utf8_bytes_index,
+ size_t& utf8_bytes_filled)
+ {
+ utf8_bytes_index = 0;
+
+ if (JSON_HEDLEY_UNLIKELY(input.empty()))
+ {
+ utf8_bytes[0] = std::char_traits<char>::eof();
+ utf8_bytes_filled = 1;
+ }
+ else
+ {
+ // get the current character
+ const auto wc = input.get_character();
+
+ // UTF-16 to UTF-8 encoding
+ if (wc < 0x80)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+ utf8_bytes_filled = 1;
+ }
+ else if (wc <= 0x7FF)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+ utf8_bytes_filled = 2;
+ }
+ else if (0xD800 > wc || wc >= 0xE000)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+ utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+ utf8_bytes_filled = 3;
+ }
+ else
+ {
+ if (JSON_HEDLEY_UNLIKELY(!input.empty()))
+ {
+ const auto wc2 = static_cast<unsigned int>(input.get_character());
+ const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
+ utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
+ utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
+ utf8_bytes_filled = 4;
+ }
+ else
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+ utf8_bytes_filled = 1;
+ }
+ }
+ }
+ }
+};
+
+// Wraps another input apdater to convert wide character types into individual bytes.
+template<typename BaseInputAdapter, typename WideCharType>
+class wide_string_input_adapter
+{
+ public:
+ using char_type = char;
+
+ wide_string_input_adapter(BaseInputAdapter base)
+ : base_adapter(base) {}
+
+ typename std::char_traits<char>::int_type get_character() noexcept
+ {
+ // check if buffer needs to be filled
+ if (utf8_bytes_index == utf8_bytes_filled)
+ {
+ fill_buffer<sizeof(WideCharType)>();
+
+ JSON_ASSERT(utf8_bytes_filled > 0);
+ JSON_ASSERT(utf8_bytes_index == 0);
+ }
+
+ // use buffer
+ JSON_ASSERT(utf8_bytes_filled > 0);
+ JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
+ return utf8_bytes[utf8_bytes_index++];
+ }
+
+ private:
+ BaseInputAdapter base_adapter;
+
+ template<size_t T>
+ void fill_buffer()
+ {
+ wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
+ }
+
+ /// a buffer for UTF-8 bytes
+ std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
+
+ /// index to the utf8_codes array for the next valid byte
+ std::size_t utf8_bytes_index = 0;
+ /// number of valid bytes in the utf8_codes array
+ std::size_t utf8_bytes_filled = 0;
+};
+
+
+template<typename IteratorType, typename Enable = void>
+struct iterator_input_adapter_factory
+{
+ using iterator_type = IteratorType;
+ using char_type = typename std::iterator_traits<iterator_type>::value_type;
+ using adapter_type = iterator_input_adapter<iterator_type>;
+
+ static adapter_type create(IteratorType first, IteratorType last)
+ {
+ return adapter_type(std::move(first), std::move(last));
+ }
+};
+
+template<typename T>
+struct is_iterator_of_multibyte
+{
+ using value_type = typename std::iterator_traits<T>::value_type;
+ enum
+ {
+ value = sizeof(value_type) > 1
+ };
+};
+
+template<typename IteratorType>
+struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
+{
+ using iterator_type = IteratorType;
+ using char_type = typename std::iterator_traits<iterator_type>::value_type;
+ using base_adapter_type = iterator_input_adapter<iterator_type>;
+ using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
+
+ static adapter_type create(IteratorType first, IteratorType last)
+ {
+ return adapter_type(base_adapter_type(std::move(first), std::move(last)));
+ }
+};
+
+// General purpose iterator-based input
+template<typename IteratorType>
+typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
+{
+ using factory_type = iterator_input_adapter_factory<IteratorType>;
+ return factory_type::create(first, last);
+}
+
+// Convenience shorthand from container to iterator
+// Enables ADL on begin(container) and end(container)
+// Encloses the using declarations in namespace for not to leak them to outside scope
+
+namespace container_input_adapter_factory_impl
+{
+
+using std::begin;
+using std::end;
+
+template<typename ContainerType, typename Enable = void>
+struct container_input_adapter_factory {};
+
+template<typename ContainerType>
+struct container_input_adapter_factory< ContainerType,
+ void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>
+ {
+ using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
+
+ static adapter_type create(const ContainerType& container)
+{
+ return input_adapter(begin(container), end(container));
+}
+ };
+
+} // namespace container_input_adapter_factory_impl
+
+template<typename ContainerType>
+typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)
+{
+ return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
+}
+
+#ifndef JSON_NO_IO
+// Special cases with fast paths
+inline file_input_adapter input_adapter(std::FILE* file)
+{
+ return file_input_adapter(file);
+}
+
+inline input_stream_adapter input_adapter(std::istream& stream)
+{
+ return input_stream_adapter(stream);
+}
+
+inline input_stream_adapter input_adapter(std::istream&& stream)
+{
+ return input_stream_adapter(stream);
+}
+#endif // JSON_NO_IO
+
+using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));
+
+// Null-delimited strings, and the like.
+template < typename CharT,
+ typename std::enable_if <
+ std::is_pointer<CharT>::value&&
+ !std::is_array<CharT>::value&&
+ std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
+ sizeof(typename std::remove_pointer<CharT>::type) == 1,
+ int >::type = 0 >
+contiguous_bytes_input_adapter input_adapter(CharT b)
+{
+ auto length = std::strlen(reinterpret_cast<const char*>(b));
+ const auto* ptr = reinterpret_cast<const char*>(b);
+ return input_adapter(ptr, ptr + length);
+}
+
+template<typename T, std::size_t N>
+auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+{
+ return input_adapter(array, array + N);
+}
+
+// This class only handles inputs of input_buffer_adapter type.
+// It's required so that expressions like {ptr, len} can be implicitly cast
+// to the correct adapter.
+class span_input_adapter
+{
+ public:
+ template < typename CharT,
+ typename std::enable_if <
+ std::is_pointer<CharT>::value&&
+ std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
+ sizeof(typename std::remove_pointer<CharT>::type) == 1,
+ int >::type = 0 >
+ span_input_adapter(CharT b, std::size_t l)
+ : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}
+
+ template<class IteratorType,
+ typename std::enable_if<
+ std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
+ int>::type = 0>
+ span_input_adapter(IteratorType first, IteratorType last)
+ : ia(input_adapter(first, last)) {}
+
+ contiguous_bytes_input_adapter&& get()
+ {
+ return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
+ }
+
+ private:
+ contiguous_bytes_input_adapter ia;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/json_sax.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef>
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/*!
+@brief SAX interface
+
+This class describes the SAX interface used by @ref nlohmann::json::sax_parse.
+Each function is called in different situations while the input is parsed. The
+boolean return value informs the parser whether to continue processing the
+input.
+*/
+template<typename BasicJsonType>
+struct json_sax
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+
+ /*!
+ @brief a null value was read
+ @return whether parsing should proceed
+ */
+ virtual bool null() = 0;
+
+ /*!
+ @brief a boolean value was read
+ @param[in] val boolean value
+ @return whether parsing should proceed
+ */
+ virtual bool boolean(bool val) = 0;
+
+ /*!
+ @brief an integer number was read
+ @param[in] val integer value
+ @return whether parsing should proceed
+ */
+ virtual bool number_integer(number_integer_t val) = 0;
+
+ /*!
+ @brief an unsigned integer number was read
+ @param[in] val unsigned integer value
+ @return whether parsing should proceed
+ */
+ virtual bool number_unsigned(number_unsigned_t val) = 0;
+
+ /*!
+ @brief a floating-point number was read
+ @param[in] val floating-point value
+ @param[in] s raw token value
+ @return whether parsing should proceed
+ */
+ virtual bool number_float(number_float_t val, const string_t& s) = 0;
+
+ /*!
+ @brief a string value was read
+ @param[in] val string value
+ @return whether parsing should proceed
+ @note It is safe to move the passed string value.
+ */
+ virtual bool string(string_t& val) = 0;
+
+ /*!
+ @brief a binary value was read
+ @param[in] val binary value
+ @return whether parsing should proceed
+ @note It is safe to move the passed binary value.
+ */
+ virtual bool binary(binary_t& val) = 0;
+
+ /*!
+ @brief the beginning of an object was read
+ @param[in] elements number of object elements or -1 if unknown
+ @return whether parsing should proceed
+ @note binary formats may report the number of elements
+ */
+ virtual bool start_object(std::size_t elements) = 0;
+
+ /*!
+ @brief an object key was read
+ @param[in] val object key
+ @return whether parsing should proceed
+ @note It is safe to move the passed string.
+ */
+ virtual bool key(string_t& val) = 0;
+
+ /*!
+ @brief the end of an object was read
+ @return whether parsing should proceed
+ */
+ virtual bool end_object() = 0;
+
+ /*!
+ @brief the beginning of an array was read
+ @param[in] elements number of array elements or -1 if unknown
+ @return whether parsing should proceed
+ @note binary formats may report the number of elements
+ */
+ virtual bool start_array(std::size_t elements) = 0;
+
+ /*!
+ @brief the end of an array was read
+ @return whether parsing should proceed
+ */
+ virtual bool end_array() = 0;
+
+ /*!
+ @brief a parse error occurred
+ @param[in] position the position in the input where the error occurs
+ @param[in] last_token the last read token
+ @param[in] ex an exception object describing the error
+ @return whether parsing should proceed (must return false)
+ */
+ virtual bool parse_error(std::size_t position,
+ const std::string& last_token,
+ const detail::exception& ex) = 0;
+
+ json_sax() = default;
+ json_sax(const json_sax&) = default;
+ json_sax(json_sax&&) noexcept = default;
+ json_sax& operator=(const json_sax&) = default;
+ json_sax& operator=(json_sax&&) noexcept = default;
+ virtual ~json_sax() = default;
+};
+
+
+namespace detail
+{
+/*!
+@brief SAX implementation to create a JSON value from SAX events
+
+This class implements the @ref json_sax interface and processes the SAX events
+to create a JSON value which makes it basically a DOM parser. The structure or
+hierarchy of the JSON value is managed by the stack `ref_stack` which contains
+a pointer to the respective array or object for each recursion depth.
+
+After successful parsing, the value that is passed by reference to the
+constructor contains the parsed value.
+
+@tparam BasicJsonType the JSON type
+*/
+template<typename BasicJsonType>
+class json_sax_dom_parser
+{
+ public:
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+
+ /*!
+ @param[in,out] r reference to a JSON value that is manipulated while
+ parsing
+ @param[in] allow_exceptions_ whether parse errors yield exceptions
+ */
+ explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
+ : root(r), allow_exceptions(allow_exceptions_)
+ {}
+
+ // make class move-only
+ json_sax_dom_parser(const json_sax_dom_parser&) = delete;
+ json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;
+ json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ ~json_sax_dom_parser() = default;
+
+ bool null()
+ {
+ handle_value(nullptr);
+ return true;
+ }
+
+ bool boolean(bool val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_integer(number_integer_t val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_unsigned(number_unsigned_t val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_float(number_float_t val, const string_t& /*unused*/)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool string(string_t& val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool binary(binary_t& val)
+ {
+ handle_value(std::move(val));
+ return true;
+ }
+
+ bool start_object(std::size_t len)
+ {
+ ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
+
+ if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+ {
+ JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
+ }
+
+ return true;
+ }
+
+ bool key(string_t& val)
+ {
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(ref_stack.back()->is_object());
+
+ // add null at given key and store the reference for later
+ object_element = &(ref_stack.back()->m_value.object->operator[](val));
+ return true;
+ }
+
+ bool end_object()
+ {
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(ref_stack.back()->is_object());
+
+ ref_stack.back()->set_parents();
+ ref_stack.pop_back();
+ return true;
+ }
+
+ bool start_array(std::size_t len)
+ {
+ ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
+
+ if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+ {
+ JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
+ }
+
+ return true;
+ }
+
+ bool end_array()
+ {
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(ref_stack.back()->is_array());
+
+ ref_stack.back()->set_parents();
+ ref_stack.pop_back();
+ return true;
+ }
+
+ template<class Exception>
+ bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
+ const Exception& ex)
+ {
+ errored = true;
+ static_cast<void>(ex);
+ if (allow_exceptions)
+ {
+ JSON_THROW(ex);
+ }
+ return false;
+ }
+
+ constexpr bool is_errored() const
+ {
+ return errored;
+ }
+
+ private:
+ /*!
+ @invariant If the ref stack is empty, then the passed value will be the new
+ root.
+ @invariant If the ref stack contains a value, then it is an array or an
+ object to which we can add elements
+ */
+ template<typename Value>
+ JSON_HEDLEY_RETURNS_NON_NULL
+ BasicJsonType* handle_value(Value&& v)
+ {
+ if (ref_stack.empty())
+ {
+ root = BasicJsonType(std::forward<Value>(v));
+ return &root;
+ }
+
+ JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
+
+ if (ref_stack.back()->is_array())
+ {
+ ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));
+ return &(ref_stack.back()->m_value.array->back());
+ }
+
+ JSON_ASSERT(ref_stack.back()->is_object());
+ JSON_ASSERT(object_element);
+ *object_element = BasicJsonType(std::forward<Value>(v));
+ return object_element;
+ }
+
+ /// the parsed JSON value
+ BasicJsonType& root;
+ /// stack to model hierarchy of values
+ std::vector<BasicJsonType*> ref_stack {};
+ /// helper to hold the reference for the next object element
+ BasicJsonType* object_element = nullptr;
+ /// whether a syntax error occurred
+ bool errored = false;
+ /// whether to throw exceptions in case of errors
+ const bool allow_exceptions = true;
+};
+
+template<typename BasicJsonType>
+class json_sax_dom_callback_parser
+{
+ public:
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+ using parser_callback_t = typename BasicJsonType::parser_callback_t;
+ using parse_event_t = typename BasicJsonType::parse_event_t;
+
+ json_sax_dom_callback_parser(BasicJsonType& r,
+ const parser_callback_t cb,
+ const bool allow_exceptions_ = true)
+ : root(r), callback(cb), allow_exceptions(allow_exceptions_)
+ {
+ keep_stack.push_back(true);
+ }
+
+ // make class move-only
+ json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;
+ json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;
+ json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ ~json_sax_dom_callback_parser() = default;
+
+ bool null()
+ {
+ handle_value(nullptr);
+ return true;
+ }
+
+ bool boolean(bool val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_integer(number_integer_t val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_unsigned(number_unsigned_t val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_float(number_float_t val, const string_t& /*unused*/)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool string(string_t& val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool binary(binary_t& val)
+ {
+ handle_value(std::move(val));
+ return true;
+ }
+
+ bool start_object(std::size_t len)
+ {
+ // check callback for object start
+ const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
+ keep_stack.push_back(keep);
+
+ auto val = handle_value(BasicJsonType::value_t::object, true);
+ ref_stack.push_back(val.second);
+
+ // check object limit
+ if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+ {
+ JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
+ }
+
+ return true;
+ }
+
+ bool key(string_t& val)
+ {
+ BasicJsonType k = BasicJsonType(val);
+
+ // check callback for key
+ const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
+ key_keep_stack.push_back(keep);
+
+ // add discarded value at given key and store the reference for later
+ if (keep && ref_stack.back())
+ {
+ object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);
+ }
+
+ return true;
+ }
+
+ bool end_object()
+ {
+ if (ref_stack.back())
+ {
+ if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
+ {
+ // discard object
+ *ref_stack.back() = discarded;
+ }
+ else
+ {
+ ref_stack.back()->set_parents();
+ }
+ }
+
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(!keep_stack.empty());
+ ref_stack.pop_back();
+ keep_stack.pop_back();
+
+ if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
+ {
+ // remove discarded value
+ for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
+ {
+ if (it->is_discarded())
+ {
+ ref_stack.back()->erase(it);
+ break;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool start_array(std::size_t len)
+ {
+ const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
+ keep_stack.push_back(keep);
+
+ auto val = handle_value(BasicJsonType::value_t::array, true);
+ ref_stack.push_back(val.second);
+
+ // check array limit
+ if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
+ {
+ JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
+ }
+
+ return true;
+ }
+
+ bool end_array()
+ {
+ bool keep = true;
+
+ if (ref_stack.back())
+ {
+ keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
+ if (keep)
+ {
+ ref_stack.back()->set_parents();
+ }
+ else
+ {
+ // discard array
+ *ref_stack.back() = discarded;
+ }
+ }
+
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(!keep_stack.empty());
+ ref_stack.pop_back();
+ keep_stack.pop_back();
+
+ // remove discarded value
+ if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())
+ {
+ ref_stack.back()->m_value.array->pop_back();
+ }
+
+ return true;
+ }
+
+ template<class Exception>
+ bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
+ const Exception& ex)
+ {
+ errored = true;
+ static_cast<void>(ex);
+ if (allow_exceptions)
+ {
+ JSON_THROW(ex);
+ }
+ return false;
+ }
+
+ constexpr bool is_errored() const
+ {
+ return errored;
+ }
+
+ private:
+ /*!
+ @param[in] v value to add to the JSON value we build during parsing
+ @param[in] skip_callback whether we should skip calling the callback
+ function; this is required after start_array() and
+ start_object() SAX events, because otherwise we would call the
+ callback function with an empty array or object, respectively.
+
+ @invariant If the ref stack is empty, then the passed value will be the new
+ root.
+ @invariant If the ref stack contains a value, then it is an array or an
+ object to which we can add elements
+
+ @return pair of boolean (whether value should be kept) and pointer (to the
+ passed value in the ref_stack hierarchy; nullptr if not kept)
+ */
+ template<typename Value>
+ std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
+ {
+ JSON_ASSERT(!keep_stack.empty());
+
+ // do not handle this value if we know it would be added to a discarded
+ // container
+ if (!keep_stack.back())
+ {
+ return {false, nullptr};
+ }
+
+ // create value
+ auto value = BasicJsonType(std::forward<Value>(v));
+
+ // check callback
+ const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
+
+ // do not handle this value if we just learnt it shall be discarded
+ if (!keep)
+ {
+ return {false, nullptr};
+ }
+
+ if (ref_stack.empty())
+ {
+ root = std::move(value);
+ return {true, &root};
+ }
+
+ // skip this value if we already decided to skip the parent
+ // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
+ if (!ref_stack.back())
+ {
+ return {false, nullptr};
+ }
+
+ // we now only expect arrays and objects
+ JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
+
+ // array
+ if (ref_stack.back()->is_array())
+ {
+ ref_stack.back()->m_value.array->emplace_back(std::move(value));
+ return {true, &(ref_stack.back()->m_value.array->back())};
+ }
+
+ // object
+ JSON_ASSERT(ref_stack.back()->is_object());
+ // check if we should store an element for the current key
+ JSON_ASSERT(!key_keep_stack.empty());
+ const bool store_element = key_keep_stack.back();
+ key_keep_stack.pop_back();
+
+ if (!store_element)
+ {
+ return {false, nullptr};
+ }
+
+ JSON_ASSERT(object_element);
+ *object_element = std::move(value);
+ return {true, object_element};
+ }
+
+ /// the parsed JSON value
+ BasicJsonType& root;
+ /// stack to model hierarchy of values
+ std::vector<BasicJsonType*> ref_stack {};
+ /// stack to manage which values to keep
+ std::vector<bool> keep_stack {};
+ /// stack to manage which object keys to keep
+ std::vector<bool> key_keep_stack {};
+ /// helper to hold the reference for the next object element
+ BasicJsonType* object_element = nullptr;
+ /// whether a syntax error occurred
+ bool errored = false;
+ /// callback function
+ const parser_callback_t callback = nullptr;
+ /// whether to throw exceptions in case of errors
+ const bool allow_exceptions = true;
+ /// a discarded value for the callback
+ BasicJsonType discarded = BasicJsonType::value_t::discarded;
+};
+
+template<typename BasicJsonType>
+class json_sax_acceptor
+{
+ public:
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+
+ bool null()
+ {
+ return true;
+ }
+
+ bool boolean(bool /*unused*/)
+ {
+ return true;
+ }
+
+ bool number_integer(number_integer_t /*unused*/)
+ {
+ return true;
+ }
+
+ bool number_unsigned(number_unsigned_t /*unused*/)
+ {
+ return true;
+ }
+
+ bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
+ {
+ return true;
+ }
+
+ bool string(string_t& /*unused*/)
+ {
+ return true;
+ }
+
+ bool binary(binary_t& /*unused*/)
+ {
+ return true;
+ }
+
+ bool start_object(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
+ {
+ return true;
+ }
+
+ bool key(string_t& /*unused*/)
+ {
+ return true;
+ }
+
+ bool end_object()
+ {
+ return true;
+ }
+
+ bool start_array(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
+ {
+ return true;
+ }
+
+ bool end_array()
+ {
+ return true;
+ }
+
+ bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)
+ {
+ return false;
+ }
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/lexer.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <clocale> // localeconv
+#include <cstddef> // size_t
+#include <cstdio> // snprintf
+#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
+#include <initializer_list> // initializer_list
+#include <string> // char_traits, string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/position_t.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////
+// lexer //
+///////////
+
+template<typename BasicJsonType>
+class lexer_base
+{
+ public:
+ /// token types for the parser
+ enum class token_type
+ {
+ uninitialized, ///< indicating the scanner is uninitialized
+ literal_true, ///< the `true` literal
+ literal_false, ///< the `false` literal
+ literal_null, ///< the `null` literal
+ value_string, ///< a string -- use get_string() for actual value
+ value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value
+ value_integer, ///< a signed integer -- use get_number_integer() for actual value
+ value_float, ///< an floating point number -- use get_number_float() for actual value
+ begin_array, ///< the character for array begin `[`
+ begin_object, ///< the character for object begin `{`
+ end_array, ///< the character for array end `]`
+ end_object, ///< the character for object end `}`
+ name_separator, ///< the name separator `:`
+ value_separator, ///< the value separator `,`
+ parse_error, ///< indicating a parse error
+ end_of_input, ///< indicating the end of the input buffer
+ literal_or_value ///< a literal or the begin of a value (only for diagnostics)
+ };
+
+ /// return name of values of type token_type (only used for errors)
+ JSON_HEDLEY_RETURNS_NON_NULL
+ JSON_HEDLEY_CONST
+ static const char* token_type_name(const token_type t) noexcept
+ {
+ switch (t)
+ {
+ case token_type::uninitialized:
+ return "<uninitialized>";
+ case token_type::literal_true:
+ return "true literal";
+ case token_type::literal_false:
+ return "false literal";
+ case token_type::literal_null:
+ return "null literal";
+ case token_type::value_string:
+ return "string literal";
+ case token_type::value_unsigned:
+ case token_type::value_integer:
+ case token_type::value_float:
+ return "number literal";
+ case token_type::begin_array:
+ return "'['";
+ case token_type::begin_object:
+ return "'{'";
+ case token_type::end_array:
+ return "']'";
+ case token_type::end_object:
+ return "'}'";
+ case token_type::name_separator:
+ return "':'";
+ case token_type::value_separator:
+ return "','";
+ case token_type::parse_error:
+ return "<parse error>";
+ case token_type::end_of_input:
+ return "end of input";
+ case token_type::literal_or_value:
+ return "'[', '{', or a literal";
+ // LCOV_EXCL_START
+ default: // catch non-enum values
+ return "unknown token";
+ // LCOV_EXCL_STOP
+ }
+ }
+};
+/*!
+@brief lexical analysis
+
+This class organizes the lexical analysis during JSON deserialization.
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class lexer : public lexer_base<BasicJsonType>
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using char_type = typename InputAdapterType::char_type;
+ using char_int_type = typename std::char_traits<char_type>::int_type;
+
+ public:
+ using token_type = typename lexer_base<BasicJsonType>::token_type;
+
+ explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept
+ : ia(std::move(adapter))
+ , ignore_comments(ignore_comments_)
+ , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))
+ {}
+
+ // delete because of pointer members
+ lexer(const lexer&) = delete;
+ lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ lexer& operator=(lexer&) = delete;
+ lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ ~lexer() = default;
+
+ private:
+ /////////////////////
+ // locales
+ /////////////////////
+
+ /// return the locale-dependent decimal point
+ JSON_HEDLEY_PURE
+ static char get_decimal_point() noexcept
+ {
+ const auto* loc = localeconv();
+ JSON_ASSERT(loc != nullptr);
+ return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
+ }
+
+ /////////////////////
+ // scan functions
+ /////////////////////
+
+ /*!
+ @brief get codepoint from 4 hex characters following `\u`
+
+ For input "\u c1 c2 c3 c4" the codepoint is:
+ (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
+ = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
+
+ Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'
+ must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The
+ conversion is done by subtracting the offset (0x30, 0x37, and 0x57)
+ between the ASCII value of the character and the desired integer value.
+
+ @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or
+ non-hex character)
+ */
+ int get_codepoint()
+ {
+ // this function only makes sense after reading `\u`
+ JSON_ASSERT(current == 'u');
+ int codepoint = 0;
+
+ const auto factors = { 12u, 8u, 4u, 0u };
+ for (const auto factor : factors)
+ {
+ get();
+
+ if (current >= '0' && current <= '9')
+ {
+ codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);
+ }
+ else if (current >= 'A' && current <= 'F')
+ {
+ codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);
+ }
+ else if (current >= 'a' && current <= 'f')
+ {
+ codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);
+ return codepoint;
+ }
+
+ /*!
+ @brief check if the next byte(s) are inside a given range
+
+ Adds the current byte and, for each passed range, reads a new byte and
+ checks if it is inside the range. If a violation was detected, set up an
+ error message and return false. Otherwise, return true.
+
+ @param[in] ranges list of integers; interpreted as list of pairs of
+ inclusive lower and upper bound, respectively
+
+ @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,
+ 1, 2, or 3 pairs. This precondition is enforced by an assertion.
+
+ @return true if and only if no range violation was detected
+ */
+ bool next_byte_in_range(std::initializer_list<char_int_type> ranges)
+ {
+ JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);
+ add(current);
+
+ for (auto range = ranges.begin(); range != ranges.end(); ++range)
+ {
+ get();
+ if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range)))
+ {
+ add(current);
+ }
+ else
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /*!
+ @brief scan a string literal
+
+ This function scans a string according to Sect. 7 of RFC 8259. While
+ scanning, bytes are escaped and copied into buffer token_buffer. Then the
+ function returns successfully, token_buffer is *not* null-terminated (as it
+ may contain \0 bytes), and token_buffer.size() is the number of bytes in the
+ string.
+
+ @return token_type::value_string if string could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note In case of errors, variable error_message contains a textual
+ description.
+ */
+ token_type scan_string()
+ {
+ // reset token_buffer (ignore opening quote)
+ reset();
+
+ // we entered the function by reading an open quote
+ JSON_ASSERT(current == '\"');
+
+ while (true)
+ {
+ // get next character
+ switch (get())
+ {
+ // end of file while parsing string
+ case std::char_traits<char_type>::eof():
+ {
+ error_message = "invalid string: missing closing quote";
+ return token_type::parse_error;
+ }
+
+ // closing quote
+ case '\"':
+ {
+ return token_type::value_string;
+ }
+
+ // escapes
+ case '\\':
+ {
+ switch (get())
+ {
+ // quotation mark
+ case '\"':
+ add('\"');
+ break;
+ // reverse solidus
+ case '\\':
+ add('\\');
+ break;
+ // solidus
+ case '/':
+ add('/');
+ break;
+ // backspace
+ case 'b':
+ add('\b');
+ break;
+ // form feed
+ case 'f':
+ add('\f');
+ break;
+ // line feed
+ case 'n':
+ add('\n');
+ break;
+ // carriage return
+ case 'r':
+ add('\r');
+ break;
+ // tab
+ case 't':
+ add('\t');
+ break;
+
+ // unicode escapes
+ case 'u':
+ {
+ const int codepoint1 = get_codepoint();
+ int codepoint = codepoint1; // start with codepoint1
+
+ if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if code point is a high surrogate
+ if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)
+ {
+ // expect next \uxxxx entry
+ if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u'))
+ {
+ const int codepoint2 = get_codepoint();
+
+ if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if codepoint2 is a low surrogate
+ if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))
+ {
+ // overwrite codepoint
+ codepoint = static_cast<int>(
+ // high surrogate occupies the most significant 22 bits
+ (static_cast<unsigned int>(codepoint1) << 10u)
+ // low surrogate occupies the least significant 15 bits
+ + static_cast<unsigned int>(codepoint2)
+ // there is still the 0xD800, 0xDC00 and 0x10000 noise
+ // in the result, so we have to subtract with:
+ // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
+ - 0x35FDC00u);
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
+ return token_type::parse_error;
+ }
+ }
+
+ // result of the above calculation yields a proper codepoint
+ JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
+
+ // translate codepoint into bytes
+ if (codepoint < 0x80)
+ {
+ // 1-byte characters: 0xxxxxxx (ASCII)
+ add(static_cast<char_int_type>(codepoint));
+ }
+ else if (codepoint <= 0x7FF)
+ {
+ // 2-byte characters: 110xxxxx 10xxxxxx
+ add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));
+ add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+ }
+ else if (codepoint <= 0xFFFF)
+ {
+ // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
+ add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));
+ add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+ add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+ }
+ else
+ {
+ // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));
+ add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));
+ add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+ add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+ }
+
+ break;
+ }
+
+ // other characters after escape
+ default:
+ error_message = "invalid string: forbidden character after backslash";
+ return token_type::parse_error;
+ }
+
+ break;
+ }
+
+ // invalid control characters
+ case 0x00:
+ {
+ error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000";
+ return token_type::parse_error;
+ }
+
+ case 0x01:
+ {
+ error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001";
+ return token_type::parse_error;
+ }
+
+ case 0x02:
+ {
+ error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002";
+ return token_type::parse_error;
+ }
+
+ case 0x03:
+ {
+ error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003";
+ return token_type::parse_error;
+ }
+
+ case 0x04:
+ {
+ error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004";
+ return token_type::parse_error;
+ }
+
+ case 0x05:
+ {
+ error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005";
+ return token_type::parse_error;
+ }
+
+ case 0x06:
+ {
+ error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006";
+ return token_type::parse_error;
+ }
+
+ case 0x07:
+ {
+ error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007";
+ return token_type::parse_error;
+ }
+
+ case 0x08:
+ {
+ error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b";
+ return token_type::parse_error;
+ }
+
+ case 0x09:
+ {
+ error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t";
+ return token_type::parse_error;
+ }
+
+ case 0x0A:
+ {
+ error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n";
+ return token_type::parse_error;
+ }
+
+ case 0x0B:
+ {
+ error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B";
+ return token_type::parse_error;
+ }
+
+ case 0x0C:
+ {
+ error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f";
+ return token_type::parse_error;
+ }
+
+ case 0x0D:
+ {
+ error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r";
+ return token_type::parse_error;
+ }
+
+ case 0x0E:
+ {
+ error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E";
+ return token_type::parse_error;
+ }
+
+ case 0x0F:
+ {
+ error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F";
+ return token_type::parse_error;
+ }
+
+ case 0x10:
+ {
+ error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010";
+ return token_type::parse_error;
+ }
+
+ case 0x11:
+ {
+ error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011";
+ return token_type::parse_error;
+ }
+
+ case 0x12:
+ {
+ error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012";
+ return token_type::parse_error;
+ }
+
+ case 0x13:
+ {
+ error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013";
+ return token_type::parse_error;
+ }
+
+ case 0x14:
+ {
+ error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014";
+ return token_type::parse_error;
+ }
+
+ case 0x15:
+ {
+ error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015";
+ return token_type::parse_error;
+ }
+
+ case 0x16:
+ {
+ error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016";
+ return token_type::parse_error;
+ }
+
+ case 0x17:
+ {
+ error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017";
+ return token_type::parse_error;
+ }
+
+ case 0x18:
+ {
+ error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018";
+ return token_type::parse_error;
+ }
+
+ case 0x19:
+ {
+ error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019";
+ return token_type::parse_error;
+ }
+
+ case 0x1A:
+ {
+ error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A";
+ return token_type::parse_error;
+ }
+
+ case 0x1B:
+ {
+ error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B";
+ return token_type::parse_error;
+ }
+
+ case 0x1C:
+ {
+ error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C";
+ return token_type::parse_error;
+ }
+
+ case 0x1D:
+ {
+ error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D";
+ return token_type::parse_error;
+ }
+
+ case 0x1E:
+ {
+ error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E";
+ return token_type::parse_error;
+ }
+
+ case 0x1F:
+ {
+ error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F";
+ return token_type::parse_error;
+ }
+
+ // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))
+ case 0x20:
+ case 0x21:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ {
+ add(current);
+ break;
+ }
+
+ // U+0080..U+07FF: bytes C2..DF 80..BF
+ case 0xC2:
+ case 0xC3:
+ case 0xC4:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ case 0xD0:
+ case 0xD1:
+ case 0xD2:
+ case 0xD3:
+ case 0xD4:
+ case 0xD5:
+ case 0xD6:
+ case 0xD7:
+ case 0xD8:
+ case 0xD9:
+ case 0xDA:
+ case 0xDB:
+ case 0xDC:
+ case 0xDD:
+ case 0xDE:
+ case 0xDF:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+0800..U+0FFF: bytes E0 A0..BF 80..BF
+ case 0xE0:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF
+ // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xEE:
+ case 0xEF:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+D000..U+D7FF: bytes ED 80..9F 80..BF
+ case 0xED:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
+ case 0xF0:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
+ case 0xF4:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // remaining bytes (80..C1 and F5..FF) are ill-formed
+ default:
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return token_type::parse_error;
+ }
+ }
+ }
+ }
+
+ /*!
+ * @brief scan a comment
+ * @return whether comment could be scanned successfully
+ */
+ bool scan_comment()
+ {
+ switch (get())
+ {
+ // single-line comments skip input until a newline or EOF is read
+ case '/':
+ {
+ while (true)
+ {
+ switch (get())
+ {
+ case '\n':
+ case '\r':
+ case std::char_traits<char_type>::eof():
+ case '\0':
+ return true;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ // multi-line comments skip input until */ is read
+ case '*':
+ {
+ while (true)
+ {
+ switch (get())
+ {
+ case std::char_traits<char_type>::eof():
+ case '\0':
+ {
+ error_message = "invalid comment; missing closing '*/'";
+ return false;
+ }
+
+ case '*':
+ {
+ switch (get())
+ {
+ case '/':
+ return true;
+
+ default:
+ {
+ unget();
+ continue;
+ }
+ }
+ }
+
+ default:
+ continue;
+ }
+ }
+ }
+
+ // unexpected character after reading '/'
+ default:
+ {
+ error_message = "invalid comment; expecting '/' or '*' after '/'";
+ return false;
+ }
+ }
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ static void strtof(float& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtof(str, endptr);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ static void strtof(double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtod(str, endptr);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ static void strtof(long double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtold(str, endptr);
+ }
+
+ /*!
+ @brief scan a number literal
+
+ This function scans a string according to Sect. 6 of RFC 8259.
+
+ The function is realized with a deterministic finite state machine derived
+ from the grammar described in RFC 8259. Starting in state "init", the
+ input is read and used to determined the next state. Only state "done"
+ accepts the number. State "error" is a trap state to model errors. In the
+ table below, "anything" means any character but the ones listed before.
+
+ state | 0 | 1-9 | e E | + | - | . | anything
+ ---------|----------|----------|----------|---------|---------|----------|-----------
+ init | zero | any1 | [error] | [error] | minus | [error] | [error]
+ minus | zero | any1 | [error] | [error] | [error] | [error] | [error]
+ zero | done | done | exponent | done | done | decimal1 | done
+ any1 | any1 | any1 | exponent | done | done | decimal1 | done
+ decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error]
+ decimal2 | decimal2 | decimal2 | exponent | done | done | done | done
+ exponent | any2 | any2 | [error] | sign | sign | [error] | [error]
+ sign | any2 | any2 | [error] | [error] | [error] | [error] | [error]
+ any2 | any2 | any2 | done | done | done | done | done
+
+ The state machine is realized with one label per state (prefixed with
+ "scan_number_") and `goto` statements between them. The state machine
+ contains cycles, but any cycle can be left when EOF is read. Therefore,
+ the function is guaranteed to terminate.
+
+ During scanning, the read bytes are stored in token_buffer. This string is
+ then converted to a signed integer, an unsigned integer, or a
+ floating-point number.
+
+ @return token_type::value_unsigned, token_type::value_integer, or
+ token_type::value_float if number could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note The scanner is independent of the current locale. Internally, the
+ locale's decimal point is used instead of `.` to work with the
+ locale-dependent converters.
+ */
+ token_type scan_number() // lgtm [cpp/use-of-goto]
+ {
+ // reset token_buffer to store the number's bytes
+ reset();
+
+ // the type of the parsed number; initially set to unsigned; will be
+ // changed if minus sign, decimal point or exponent is read
+ token_type number_type = token_type::value_unsigned;
+
+ // state (init): we just found out we need to scan a number
+ switch (current)
+ {
+ case '-':
+ {
+ add(current);
+ goto scan_number_minus;
+ }
+
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ // all other characters are rejected outside scan_number()
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+
+scan_number_minus:
+ // state: we just parsed a leading minus sign
+ number_type = token_type::value_integer;
+ switch (get())
+ {
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '-'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_zero:
+ // state: we just parse a zero (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '.':
+ {
+ add(decimal_point_char);
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_any1:
+ // state: we just parsed a number 0-9 (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ case '.':
+ {
+ add(decimal_point_char);
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_decimal1:
+ // state: we just parsed a decimal point
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '.'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_decimal2:
+ // we just parsed at least one number after a decimal point
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_exponent:
+ // we just parsed an exponent
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '+':
+ case '-':
+ {
+ add(current);
+ goto scan_number_sign;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message =
+ "invalid number; expected '+', '-', or digit after exponent";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_sign:
+ // we just parsed an exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after exponent sign";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_any2:
+ // we just parsed a number after the exponent or exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_done:
+ // unget the character after the number (we only read it to know that
+ // we are done scanning a number)
+ unget();
+
+ char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ errno = 0;
+
+ // try to parse integers first and fall back to floats
+ if (number_type == token_type::value_unsigned)
+ {
+ const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
+
+ // we checked the number format before
+ JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+ if (errno == 0)
+ {
+ value_unsigned = static_cast<number_unsigned_t>(x);
+ if (value_unsigned == x)
+ {
+ return token_type::value_unsigned;
+ }
+ }
+ }
+ else if (number_type == token_type::value_integer)
+ {
+ const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
+
+ // we checked the number format before
+ JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+ if (errno == 0)
+ {
+ value_integer = static_cast<number_integer_t>(x);
+ if (value_integer == x)
+ {
+ return token_type::value_integer;
+ }
+ }
+ }
+
+ // this code is reached if we parse a floating-point number or if an
+ // integer conversion above failed
+ strtof(value_float, token_buffer.data(), &endptr);
+
+ // we checked the number format before
+ JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+ return token_type::value_float;
+ }
+
+ /*!
+ @param[in] literal_text the literal text to expect
+ @param[in] length the length of the passed literal text
+ @param[in] return_type the token type to return on success
+ */
+ JSON_HEDLEY_NON_NULL(2)
+ token_type scan_literal(const char_type* literal_text, const std::size_t length,
+ token_type return_type)
+ {
+ JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);
+ for (std::size_t i = 1; i < length; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))
+ {
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+ }
+ return return_type;
+ }
+
+ /////////////////////
+ // input management
+ /////////////////////
+
+ /// reset token_buffer; current character is beginning of token
+ void reset() noexcept
+ {
+ token_buffer.clear();
+ token_string.clear();
+ token_string.push_back(std::char_traits<char_type>::to_char_type(current));
+ }
+
+ /*
+ @brief get next character from the input
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns a
+ `std::char_traits<char>::eof()` in that case. Stores the scanned characters
+ for use in error messages.
+
+ @return character read from the input
+ */
+ char_int_type get()
+ {
+ ++position.chars_read_total;
+ ++position.chars_read_current_line;
+
+ if (next_unget)
+ {
+ // just reset the next_unget variable and work with current
+ next_unget = false;
+ }
+ else
+ {
+ current = ia.get_character();
+ }
+
+ if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
+ {
+ token_string.push_back(std::char_traits<char_type>::to_char_type(current));
+ }
+
+ if (current == '\n')
+ {
+ ++position.lines_read;
+ position.chars_read_current_line = 0;
+ }
+
+ return current;
+ }
+
+ /*!
+ @brief unget current character (read it again on next get)
+
+ We implement unget by setting variable next_unget to true. The input is not
+ changed - we just simulate ungetting by modifying chars_read_total,
+ chars_read_current_line, and token_string. The next call to get() will
+ behave as if the unget character is read again.
+ */
+ void unget()
+ {
+ next_unget = true;
+
+ --position.chars_read_total;
+
+ // in case we "unget" a newline, we have to also decrement the lines_read
+ if (position.chars_read_current_line == 0)
+ {
+ if (position.lines_read > 0)
+ {
+ --position.lines_read;
+ }
+ }
+ else
+ {
+ --position.chars_read_current_line;
+ }
+
+ if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))
+ {
+ JSON_ASSERT(!token_string.empty());
+ token_string.pop_back();
+ }
+ }
+
+ /// add a character to token_buffer
+ void add(char_int_type c)
+ {
+ token_buffer.push_back(static_cast<typename string_t::value_type>(c));
+ }
+
+ public:
+ /////////////////////
+ // value getters
+ /////////////////////
+
+ /// return integer value
+ constexpr number_integer_t get_number_integer() const noexcept
+ {
+ return value_integer;
+ }
+
+ /// return unsigned integer value
+ constexpr number_unsigned_t get_number_unsigned() const noexcept
+ {
+ return value_unsigned;
+ }
+
+ /// return floating-point value
+ constexpr number_float_t get_number_float() const noexcept
+ {
+ return value_float;
+ }
+
+ /// return current string value (implicitly resets the token; useful only once)
+ string_t& get_string()
+ {
+ return token_buffer;
+ }
+
+ /////////////////////
+ // diagnostics
+ /////////////////////
+
+ /// return position of last read token
+ constexpr position_t get_position() const noexcept
+ {
+ return position;
+ }
+
+ /// return the last read token (for errors only). Will never contain EOF
+ /// (an arbitrary value that is not a valid char value, often -1), because
+ /// 255 may legitimately occur. May contain NUL, which should be escaped.
+ std::string get_token_string() const
+ {
+ // escape control characters
+ std::string result;
+ for (const auto c : token_string)
+ {
+ if (static_cast<unsigned char>(c) <= '\x1F')
+ {
+ // escape control characters
+ std::array<char, 9> cs{{}};
+ static_cast<void>((std::snprintf)(cs.data(), cs.size(), "<U+%.4X>", static_cast<unsigned char>(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ result += cs.data();
+ }
+ else
+ {
+ // add character as is
+ result.push_back(static_cast<std::string::value_type>(c));
+ }
+ }
+
+ return result;
+ }
+
+ /// return syntax error message
+ JSON_HEDLEY_RETURNS_NON_NULL
+ constexpr const char* get_error_message() const noexcept
+ {
+ return error_message;
+ }
+
+ /////////////////////
+ // actual scanner
+ /////////////////////
+
+ /*!
+ @brief skip the UTF-8 byte order mark
+ @return true iff there is no BOM or the correct BOM has been skipped
+ */
+ bool skip_bom()
+ {
+ if (get() == 0xEF)
+ {
+ // check if we completely parse the BOM
+ return get() == 0xBB && get() == 0xBF;
+ }
+
+ // the first character is not the beginning of the BOM; unget it to
+ // process is later
+ unget();
+ return true;
+ }
+
+ void skip_whitespace()
+ {
+ do
+ {
+ get();
+ }
+ while (current == ' ' || current == '\t' || current == '\n' || current == '\r');
+ }
+
+ token_type scan()
+ {
+ // initially, skip the BOM
+ if (position.chars_read_total == 0 && !skip_bom())
+ {
+ error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given";
+ return token_type::parse_error;
+ }
+
+ // read next character and ignore whitespace
+ skip_whitespace();
+
+ // ignore comments
+ while (ignore_comments && current == '/')
+ {
+ if (!scan_comment())
+ {
+ return token_type::parse_error;
+ }
+
+ // skip following whitespace
+ skip_whitespace();
+ }
+
+ switch (current)
+ {
+ // structural characters
+ case '[':
+ return token_type::begin_array;
+ case ']':
+ return token_type::end_array;
+ case '{':
+ return token_type::begin_object;
+ case '}':
+ return token_type::end_object;
+ case ':':
+ return token_type::name_separator;
+ case ',':
+ return token_type::value_separator;
+
+ // literals
+ case 't':
+ {
+ std::array<char_type, 4> true_literal = {{static_cast<char_type>('t'), static_cast<char_type>('r'), static_cast<char_type>('u'), static_cast<char_type>('e')}};
+ return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);
+ }
+ case 'f':
+ {
+ std::array<char_type, 5> false_literal = {{static_cast<char_type>('f'), static_cast<char_type>('a'), static_cast<char_type>('l'), static_cast<char_type>('s'), static_cast<char_type>('e')}};
+ return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);
+ }
+ case 'n':
+ {
+ std::array<char_type, 4> null_literal = {{static_cast<char_type>('n'), static_cast<char_type>('u'), static_cast<char_type>('l'), static_cast<char_type>('l')}};
+ return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);
+ }
+
+ // string
+ case '\"':
+ return scan_string();
+
+ // number
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return scan_number();
+
+ // end of input (the null byte is needed when parsing from
+ // string literals)
+ case '\0':
+ case std::char_traits<char_type>::eof():
+ return token_type::end_of_input;
+
+ // error
+ default:
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+ }
+
+ private:
+ /// input adapter
+ InputAdapterType ia;
+
+ /// whether comments should be ignored (true) or signaled as errors (false)
+ const bool ignore_comments = false;
+
+ /// the current character
+ char_int_type current = std::char_traits<char_type>::eof();
+
+ /// whether the next get() call should just return current
+ bool next_unget = false;
+
+ /// the start position of the current token
+ position_t position {};
+
+ /// raw input token string (for error messages)
+ std::vector<char_type> token_string {};
+
+ /// buffer for variable-length tokens (numbers, strings)
+ string_t token_buffer {};
+
+ /// a description of occurred lexer errors
+ const char* error_message = "";
+
+ // number values
+ number_integer_t value_integer = 0;
+ number_unsigned_t value_unsigned = 0;
+ number_float_t value_float = 0;
+
+ /// the decimal point
+ const char_int_type decimal_point_char = '.';
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/is_sax.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // size_t
+#include <utility> // declval
+#include <string> // string
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename T>
+using null_function_t = decltype(std::declval<T&>().null());
+
+template<typename T>
+using boolean_function_t =
+ decltype(std::declval<T&>().boolean(std::declval<bool>()));
+
+template<typename T, typename Integer>
+using number_integer_function_t =
+ decltype(std::declval<T&>().number_integer(std::declval<Integer>()));
+
+template<typename T, typename Unsigned>
+using number_unsigned_function_t =
+ decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));
+
+template<typename T, typename Float, typename String>
+using number_float_function_t = decltype(std::declval<T&>().number_float(
+ std::declval<Float>(), std::declval<const String&>()));
+
+template<typename T, typename String>
+using string_function_t =
+ decltype(std::declval<T&>().string(std::declval<String&>()));
+
+template<typename T, typename Binary>
+using binary_function_t =
+ decltype(std::declval<T&>().binary(std::declval<Binary&>()));
+
+template<typename T>
+using start_object_function_t =
+ decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));
+
+template<typename T, typename String>
+using key_function_t =
+ decltype(std::declval<T&>().key(std::declval<String&>()));
+
+template<typename T>
+using end_object_function_t = decltype(std::declval<T&>().end_object());
+
+template<typename T>
+using start_array_function_t =
+ decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));
+
+template<typename T>
+using end_array_function_t = decltype(std::declval<T&>().end_array());
+
+template<typename T, typename Exception>
+using parse_error_function_t = decltype(std::declval<T&>().parse_error(
+ std::declval<std::size_t>(), std::declval<const std::string&>(),
+ std::declval<const Exception&>()));
+
+template<typename SAX, typename BasicJsonType>
+struct is_sax
+{
+ private:
+ static_assert(is_basic_json<BasicJsonType>::value,
+ "BasicJsonType must be of type basic_json<...>");
+
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+ using exception_t = typename BasicJsonType::exception;
+
+ public:
+ static constexpr bool value =
+ is_detected_exact<bool, null_function_t, SAX>::value &&
+ is_detected_exact<bool, boolean_function_t, SAX>::value &&
+ is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&
+ is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&
+ is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&
+ is_detected_exact<bool, string_function_t, SAX, string_t>::value &&
+ is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&
+ is_detected_exact<bool, start_object_function_t, SAX>::value &&
+ is_detected_exact<bool, key_function_t, SAX, string_t>::value &&
+ is_detected_exact<bool, end_object_function_t, SAX>::value &&
+ is_detected_exact<bool, start_array_function_t, SAX>::value &&
+ is_detected_exact<bool, end_array_function_t, SAX>::value &&
+ is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;
+};
+
+template<typename SAX, typename BasicJsonType>
+struct is_sax_static_asserts
+{
+ private:
+ static_assert(is_basic_json<BasicJsonType>::value,
+ "BasicJsonType must be of type basic_json<...>");
+
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+ using exception_t = typename BasicJsonType::exception;
+
+ public:
+ static_assert(is_detected_exact<bool, null_function_t, SAX>::value,
+ "Missing/invalid function: bool null()");
+ static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
+ "Missing/invalid function: bool boolean(bool)");
+ static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
+ "Missing/invalid function: bool boolean(bool)");
+ static_assert(
+ is_detected_exact<bool, number_integer_function_t, SAX,
+ number_integer_t>::value,
+ "Missing/invalid function: bool number_integer(number_integer_t)");
+ static_assert(
+ is_detected_exact<bool, number_unsigned_function_t, SAX,
+ number_unsigned_t>::value,
+ "Missing/invalid function: bool number_unsigned(number_unsigned_t)");
+ static_assert(is_detected_exact<bool, number_float_function_t, SAX,
+ number_float_t, string_t>::value,
+ "Missing/invalid function: bool number_float(number_float_t, const string_t&)");
+ static_assert(
+ is_detected_exact<bool, string_function_t, SAX, string_t>::value,
+ "Missing/invalid function: bool string(string_t&)");
+ static_assert(
+ is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,
+ "Missing/invalid function: bool binary(binary_t&)");
+ static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,
+ "Missing/invalid function: bool start_object(std::size_t)");
+ static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,
+ "Missing/invalid function: bool key(string_t&)");
+ static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,
+ "Missing/invalid function: bool end_object()");
+ static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,
+ "Missing/invalid function: bool start_array(std::size_t)");
+ static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,
+ "Missing/invalid function: bool end_array()");
+ static_assert(
+ is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,
+ "Missing/invalid function: bool parse_error(std::size_t, const "
+ "std::string&, const exception&)");
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// how to treat CBOR tags
+enum class cbor_tag_handler_t
+{
+ error, ///< throw a parse_error exception in case of a tag
+ ignore, ///< ignore tags
+ store ///< store tags as binary type
+};
+
+/*!
+@brief determine system byte order
+
+@return true if and only if system's byte order is little endian
+
+@note from https://stackoverflow.com/a/1001328/266378
+*/
+static inline bool little_endianness(int num = 1) noexcept
+{
+ return *reinterpret_cast<char*>(&num) == 1;
+}
+
+
+///////////////////
+// binary reader //
+///////////////////
+
+/*!
+@brief deserialization of CBOR, MessagePack, and UBJSON values
+*/
+template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>>
+class binary_reader
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+ using json_sax_t = SAX;
+ using char_type = typename InputAdapterType::char_type;
+ using char_int_type = typename std::char_traits<char_type>::int_type;
+
+ public:
+ /*!
+ @brief create a binary reader
+
+ @param[in] adapter input adapter to read from
+ */
+ explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json) noexcept : ia(std::move(adapter)), input_format(format)
+ {
+ (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
+ }
+
+ // make class move-only
+ binary_reader(const binary_reader&) = delete;
+ binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ binary_reader& operator=(const binary_reader&) = delete;
+ binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ ~binary_reader() = default;
+
+ /*!
+ @param[in] format the binary format to parse
+ @param[in] sax_ a SAX event processor
+ @param[in] strict whether to expect the input to be consumed completed
+ @param[in] tag_handler how to treat CBOR tags
+
+ @return whether parsing was successful
+ */
+ JSON_HEDLEY_NON_NULL(3)
+ bool sax_parse(const input_format_t format,
+ json_sax_t* sax_,
+ const bool strict = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+ {
+ sax = sax_;
+ bool result = false;
+
+ switch (format)
+ {
+ case input_format_t::bson:
+ result = parse_bson_internal();
+ break;
+
+ case input_format_t::cbor:
+ result = parse_cbor_internal(true, tag_handler);
+ break;
+
+ case input_format_t::msgpack:
+ result = parse_msgpack_internal();
+ break;
+
+ case input_format_t::ubjson:
+ case input_format_t::bjdata:
+ result = parse_ubjson_internal();
+ break;
+
+ case input_format_t::json: // LCOV_EXCL_LINE
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+
+ // strict mode: next byte must be EOF
+ if (result && strict)
+ {
+ if (input_format == input_format_t::ubjson || input_format == input_format_t::bjdata)
+ {
+ get_ignore_noop();
+ }
+ else
+ {
+ get();
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read,
+ exception_message(input_format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr));
+ }
+ }
+
+ return result;
+ }
+
+ private:
+ //////////
+ // BSON //
+ //////////
+
+ /*!
+ @brief Reads in a BSON-object and passes it to the SAX-parser.
+ @return whether a valid BSON-value was passed to the SAX parser
+ */
+ bool parse_bson_internal()
+ {
+ std::int32_t document_size{};
+ get_number<std::int32_t, true>(input_format_t::bson, document_size);
+
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))
+ {
+ return false;
+ }
+
+ return sax->end_object();
+ }
+
+ /*!
+ @brief Parses a C-style string from the BSON input.
+ @param[in,out] result A reference to the string variable where the read
+ string is to be stored.
+ @return `true` if the \x00-byte indicating the end of the string was
+ encountered before the EOF; false` indicates an unexpected EOF.
+ */
+ bool get_bson_cstr(string_t& result)
+ {
+ auto out = std::back_inserter(result);
+ while (true)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring")))
+ {
+ return false;
+ }
+ if (current == 0x00)
+ {
+ return true;
+ }
+ *out++ = static_cast<typename string_t::value_type>(current);
+ }
+ }
+
+ /*!
+ @brief Parses a zero-terminated string of length @a len from the BSON
+ input.
+ @param[in] len The length (including the zero-byte at the end) of the
+ string to be read.
+ @param[in,out] result A reference to the string variable where the read
+ string is to be stored.
+ @tparam NumberType The type of the length @a len
+ @pre len >= 1
+ @return `true` if the string was successfully parsed
+ */
+ template<typename NumberType>
+ bool get_bson_string(const NumberType len, string_t& result)
+ {
+ if (JSON_HEDLEY_UNLIKELY(len < 1))
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
+ }
+
+ return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();
+ }
+
+ /*!
+ @brief Parses a byte array input of length @a len from the BSON input.
+ @param[in] len The length of the byte array to be read.
+ @param[in,out] result A reference to the binary variable where the read
+ array is to be stored.
+ @tparam NumberType The type of the length @a len
+ @pre len >= 0
+ @return `true` if the byte array was successfully parsed
+ */
+ template<typename NumberType>
+ bool get_bson_binary(const NumberType len, binary_t& result)
+ {
+ if (JSON_HEDLEY_UNLIKELY(len < 0))
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format_t::bson, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr));
+ }
+
+ // All BSON binary values have a subtype
+ std::uint8_t subtype{};
+ get_number<std::uint8_t>(input_format_t::bson, subtype);
+ result.set_subtype(subtype);
+
+ return get_binary(input_format_t::bson, len, result);
+ }
+
+ /*!
+ @brief Read a BSON document element of the given @a element_type.
+ @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html
+ @param[in] element_type_parse_position The position in the input stream,
+ where the `element_type` was read.
+ @warning Not all BSON element types are supported yet. An unsupported
+ @a element_type will give rise to a parse_error.114:
+ Unsupported BSON record type 0x...
+ @return whether a valid BSON-object/array was passed to the SAX parser
+ */
+ bool parse_bson_element_internal(const char_int_type element_type,
+ const std::size_t element_type_parse_position)
+ {
+ switch (element_type)
+ {
+ case 0x01: // double
+ {
+ double number{};
+ return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 0x02: // string
+ {
+ std::int32_t len{};
+ string_t value;
+ return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);
+ }
+
+ case 0x03: // object
+ {
+ return parse_bson_internal();
+ }
+
+ case 0x04: // array
+ {
+ return parse_bson_array();
+ }
+
+ case 0x05: // binary
+ {
+ std::int32_t len{};
+ binary_t value;
+ return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);
+ }
+
+ case 0x08: // boolean
+ {
+ return sax->boolean(get() != 0);
+ }
+
+ case 0x0A: // null
+ {
+ return sax->null();
+ }
+
+ case 0x10: // int32
+ {
+ std::int32_t value{};
+ return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);
+ }
+
+ case 0x12: // int64
+ {
+ std::int64_t value{};
+ return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
+ }
+
+ default: // anything else not supported (yet)
+ {
+ std::array<char, 3> cr{{}};
+ static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ const std::string cr_str{cr.data()};
+ return sax->parse_error(element_type_parse_position, cr_str,
+ parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @brief Read a BSON element list (as specified in the BSON-spec)
+
+ The same binary layout is used for objects and arrays, hence it must be
+ indicated with the argument @a is_array which one is expected
+ (true --> array, false --> object).
+
+ @param[in] is_array Determines if the element list being read is to be
+ treated as an object (@a is_array == false), or as an
+ array (@a is_array == true).
+ @return whether a valid BSON-object/array was passed to the SAX parser
+ */
+ bool parse_bson_element_list(const bool is_array)
+ {
+ string_t key;
+
+ while (auto element_type = get())
+ {
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list")))
+ {
+ return false;
+ }
+
+ const std::size_t element_type_parse_position = chars_read;
+ if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))
+ {
+ return false;
+ }
+
+ if (!is_array && !sax->key(key))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))
+ {
+ return false;
+ }
+
+ // get_bson_cstr only appends
+ key.clear();
+ }
+
+ return true;
+ }
+
+ /*!
+ @brief Reads an array from the BSON input and passes it to the SAX-parser.
+ @return whether a valid BSON-array was passed to the SAX parser
+ */
+ bool parse_bson_array()
+ {
+ std::int32_t document_size{};
+ get_number<std::int32_t, true>(input_format_t::bson, document_size);
+
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))
+ {
+ return false;
+ }
+
+ return sax->end_array();
+ }
+
+ //////////
+ // CBOR //
+ //////////
+
+ /*!
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true) or whether the last read character should
+ be considered instead (false)
+ @param[in] tag_handler how CBOR tags should be treated
+
+ @return whether a valid CBOR value was passed to the SAX parser
+ */
+ bool parse_cbor_internal(const bool get_char,
+ const cbor_tag_handler_t tag_handler)
+ {
+ switch (get_char ? get() : current)
+ {
+ // EOF
+ case std::char_traits<char_type>::eof():
+ return unexpect_eof(input_format_t::cbor, "value");
+
+ // Integer 0x00..0x17 (0..23)
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ return sax->number_unsigned(static_cast<number_unsigned_t>(current));
+
+ case 0x18: // Unsigned integer (one-byte uint8_t follows)
+ {
+ std::uint8_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+ }
+
+ case 0x19: // Unsigned integer (two-byte uint16_t follows)
+ {
+ std::uint16_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+ }
+
+ case 0x1A: // Unsigned integer (four-byte uint32_t follows)
+ {
+ std::uint32_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+ }
+
+ case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
+ {
+ std::uint64_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+ }
+
+ // Negative integer -1-0x00..-1-0x17 (-1..-24)
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));
+
+ case 0x38: // Negative integer (one-byte uint8_t follows)
+ {
+ std::uint8_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+ }
+
+ case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
+ {
+ std::uint16_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+ }
+
+ case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
+ {
+ std::uint32_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+ }
+
+ case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
+ {
+ std::uint64_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)
+ - static_cast<number_integer_t>(number));
+ }
+
+ // Binary data (0x00..0x17 bytes follow)
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58: // Binary data (one-byte uint8_t for n follows)
+ case 0x59: // Binary data (two-byte uint16_t for n follow)
+ case 0x5A: // Binary data (four-byte uint32_t for n follow)
+ case 0x5B: // Binary data (eight-byte uint64_t for n follow)
+ case 0x5F: // Binary data (indefinite length)
+ {
+ binary_t b;
+ return get_cbor_binary(b) && sax->binary(b);
+ }
+
+ // UTF-8 string (0x00..0x17 bytes follow)
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+ case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+ case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+ case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+ case 0x7F: // UTF-8 string (indefinite length)
+ {
+ string_t s;
+ return get_cbor_string(s) && sax->string(s);
+ }
+
+ // array (0x00..0x17 data items follow)
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ case 0x8D:
+ case 0x8E:
+ case 0x8F:
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ return get_cbor_array(
+ conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+
+ case 0x98: // array (one-byte uint8_t for n follows)
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0x99: // array (two-byte uint16_t for n follow)
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0x9A: // array (four-byte uint32_t for n follow)
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0x9B: // array (eight-byte uint64_t for n follow)
+ {
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0x9F: // array (indefinite length)
+ return get_cbor_array(static_cast<std::size_t>(-1), tag_handler);
+
+ // map (0x00..0x17 pairs of data items follow)
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ return get_cbor_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+
+ case 0xB8: // map (one-byte uint8_t for n follows)
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0xB9: // map (two-byte uint16_t for n follow)
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0xBA: // map (four-byte uint32_t for n follow)
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0xBB: // map (eight-byte uint64_t for n follow)
+ {
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0xBF: // map (indefinite length)
+ return get_cbor_object(static_cast<std::size_t>(-1), tag_handler);
+
+ case 0xC6: // tagged item
+ case 0xC7:
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ case 0xD0:
+ case 0xD1:
+ case 0xD2:
+ case 0xD3:
+ case 0xD4:
+ case 0xD8: // tagged item (1 bytes follow)
+ case 0xD9: // tagged item (2 bytes follow)
+ case 0xDA: // tagged item (4 bytes follow)
+ case 0xDB: // tagged item (8 bytes follow)
+ {
+ switch (tag_handler)
+ {
+ case cbor_tag_handler_t::error:
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
+ }
+
+ case cbor_tag_handler_t::ignore:
+ {
+ // ignore binary subtype
+ switch (current)
+ {
+ case 0xD8:
+ {
+ std::uint8_t subtype_to_ignore{};
+ get_number(input_format_t::cbor, subtype_to_ignore);
+ break;
+ }
+ case 0xD9:
+ {
+ std::uint16_t subtype_to_ignore{};
+ get_number(input_format_t::cbor, subtype_to_ignore);
+ break;
+ }
+ case 0xDA:
+ {
+ std::uint32_t subtype_to_ignore{};
+ get_number(input_format_t::cbor, subtype_to_ignore);
+ break;
+ }
+ case 0xDB:
+ {
+ std::uint64_t subtype_to_ignore{};
+ get_number(input_format_t::cbor, subtype_to_ignore);
+ break;
+ }
+ default:
+ break;
+ }
+ return parse_cbor_internal(true, tag_handler);
+ }
+
+ case cbor_tag_handler_t::store:
+ {
+ binary_t b;
+ // use binary subtype and store in binary container
+ switch (current)
+ {
+ case 0xD8:
+ {
+ std::uint8_t subtype{};
+ get_number(input_format_t::cbor, subtype);
+ b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+ break;
+ }
+ case 0xD9:
+ {
+ std::uint16_t subtype{};
+ get_number(input_format_t::cbor, subtype);
+ b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+ break;
+ }
+ case 0xDA:
+ {
+ std::uint32_t subtype{};
+ get_number(input_format_t::cbor, subtype);
+ b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+ break;
+ }
+ case 0xDB:
+ {
+ std::uint64_t subtype{};
+ get_number(input_format_t::cbor, subtype);
+ b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+ break;
+ }
+ default:
+ return parse_cbor_internal(true, tag_handler);
+ }
+ get();
+ return get_cbor_binary(b) && sax->binary(b);
+ }
+
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ return false; // LCOV_EXCL_LINE
+ }
+ }
+
+ case 0xF4: // false
+ return sax->boolean(false);
+
+ case 0xF5: // true
+ return sax->boolean(true);
+
+ case 0xF6: // null
+ return sax->null();
+
+ case 0xF9: // Half-Precision Float (two-byte IEEE 754)
+ {
+ const auto byte1_raw = get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
+ {
+ return false;
+ }
+ const auto byte2_raw = get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
+ {
+ return false;
+ }
+
+ const auto byte1 = static_cast<unsigned char>(byte1_raw);
+ const auto byte2 = static_cast<unsigned char>(byte2_raw);
+
+ // code from RFC 7049, Appendix D, Figure 3:
+ // As half-precision floating-point numbers were only added
+ // to IEEE 754 in 2008, today's programming platforms often
+ // still only have limited support for them. It is very
+ // easy to include at least decoding support for them even
+ // without such support. An example of a small decoder for
+ // half-precision floating-point numbers in the C language
+ // is shown in Fig. 3.
+ const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);
+ const double val = [&half]
+ {
+ const int exp = (half >> 10u) & 0x1Fu;
+ const unsigned int mant = half & 0x3FFu;
+ JSON_ASSERT(0 <= exp&& exp <= 32);
+ JSON_ASSERT(mant <= 1024);
+ switch (exp)
+ {
+ case 0:
+ return std::ldexp(mant, -24);
+ case 31:
+ return (mant == 0)
+ ? std::numeric_limits<double>::infinity()
+ : std::numeric_limits<double>::quiet_NaN();
+ default:
+ return std::ldexp(mant + 1024, exp - 25);
+ }
+ }();
+ return sax->number_float((half & 0x8000u) != 0
+ ? static_cast<number_float_t>(-val)
+ : static_cast<number_float_t>(val), "");
+ }
+
+ case 0xFA: // Single-Precision Float (four-byte IEEE 754)
+ {
+ float number{};
+ return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
+ {
+ double number{};
+ return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ default: // anything else (0xFF is handled inside the other types)
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @brief reads a CBOR string
+
+ This function first reads starting bytes to determine the expected
+ string length and then copies this number of bytes into a string.
+ Additionally, CBOR's strings with indefinite lengths are supported.
+
+ @param[out] result created string
+
+ @return whether string creation completed
+ */
+ bool get_cbor_string(string_t& result)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
+ {
+ return false;
+ }
+
+ switch (current)
+ {
+ // UTF-8 string (0x00..0x17 bytes follow)
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ {
+ return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);
+ }
+
+ case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+ }
+
+ case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+ }
+
+ case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+ }
+
+ case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+ {
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+ }
+
+ case 0x7F: // UTF-8 string (indefinite length)
+ {
+ while (get() != 0xFF)
+ {
+ string_t chunk;
+ if (!get_cbor_string(chunk))
+ {
+ return false;
+ }
+ result.append(chunk);
+ }
+ return true;
+ }
+
+ default:
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+ exception_message(input_format_t::cbor, concat("expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x", last_token), "string"), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @brief reads a CBOR byte array
+
+ This function first reads starting bytes to determine the expected
+ byte array length and then copies this number of bytes into the byte array.
+ Additionally, CBOR's byte arrays with indefinite lengths are supported.
+
+ @param[out] result created byte array
+
+ @return whether byte array creation completed
+ */
+ bool get_cbor_binary(binary_t& result)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
+ {
+ return false;
+ }
+
+ switch (current)
+ {
+ // Binary data (0x00..0x17 bytes follow)
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ {
+ return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);
+ }
+
+ case 0x58: // Binary data (one-byte uint8_t for n follows)
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) &&
+ get_binary(input_format_t::cbor, len, result);
+ }
+
+ case 0x59: // Binary data (two-byte uint16_t for n follow)
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) &&
+ get_binary(input_format_t::cbor, len, result);
+ }
+
+ case 0x5A: // Binary data (four-byte uint32_t for n follow)
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) &&
+ get_binary(input_format_t::cbor, len, result);
+ }
+
+ case 0x5B: // Binary data (eight-byte uint64_t for n follow)
+ {
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) &&
+ get_binary(input_format_t::cbor, len, result);
+ }
+
+ case 0x5F: // Binary data (indefinite length)
+ {
+ while (get() != 0xFF)
+ {
+ binary_t chunk;
+ if (!get_cbor_binary(chunk))
+ {
+ return false;
+ }
+ result.insert(result.end(), chunk.begin(), chunk.end());
+ }
+ return true;
+ }
+
+ default:
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+ exception_message(input_format_t::cbor, concat("expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x", last_token), "binary"), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @param[in] len the length of the array or static_cast<std::size_t>(-1) for an
+ array of indefinite size
+ @param[in] tag_handler how CBOR tags should be treated
+ @return whether array creation completed
+ */
+ bool get_cbor_array(const std::size_t len,
+ const cbor_tag_handler_t tag_handler)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
+ {
+ return false;
+ }
+
+ if (len != static_cast<std::size_t>(-1))
+ {
+ for (std::size_t i = 0; i < len; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ while (get() != 0xFF)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))
+ {
+ return false;
+ }
+ }
+ }
+
+ return sax->end_array();
+ }
+
+ /*!
+ @param[in] len the length of the object or static_cast<std::size_t>(-1) for an
+ object of indefinite size
+ @param[in] tag_handler how CBOR tags should be treated
+ @return whether object creation completed
+ */
+ bool get_cbor_object(const std::size_t len,
+ const cbor_tag_handler_t tag_handler)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
+ {
+ return false;
+ }
+
+ if (len != 0)
+ {
+ string_t key;
+ if (len != static_cast<std::size_t>(-1))
+ {
+ for (std::size_t i = 0; i < len; ++i)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+ {
+ return false;
+ }
+ key.clear();
+ }
+ }
+ else
+ {
+ while (get() != 0xFF)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+ {
+ return false;
+ }
+ key.clear();
+ }
+ }
+ }
+
+ return sax->end_object();
+ }
+
+ /////////////
+ // MsgPack //
+ /////////////
+
+ /*!
+ @return whether a valid MessagePack value was passed to the SAX parser
+ */
+ bool parse_msgpack_internal()
+ {
+ switch (get())
+ {
+ // EOF
+ case std::char_traits<char_type>::eof():
+ return unexpect_eof(input_format_t::msgpack, "value");
+
+ // positive fixint
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5C:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ return sax->number_unsigned(static_cast<number_unsigned_t>(current));
+
+ // fixmap
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ case 0x8D:
+ case 0x8E:
+ case 0x8F:
+ return get_msgpack_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+
+ // fixarray
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ case 0x98:
+ case 0x99:
+ case 0x9A:
+ case 0x9B:
+ case 0x9C:
+ case 0x9D:
+ case 0x9E:
+ case 0x9F:
+ return get_msgpack_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+
+ // fixstr
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF:
+ case 0xD9: // str 8
+ case 0xDA: // str 16
+ case 0xDB: // str 32
+ {
+ string_t s;
+ return get_msgpack_string(s) && sax->string(s);
+ }
+
+ case 0xC0: // nil
+ return sax->null();
+
+ case 0xC2: // false
+ return sax->boolean(false);
+
+ case 0xC3: // true
+ return sax->boolean(true);
+
+ case 0xC4: // bin 8
+ case 0xC5: // bin 16
+ case 0xC6: // bin 32
+ case 0xC7: // ext 8
+ case 0xC8: // ext 16
+ case 0xC9: // ext 32
+ case 0xD4: // fixext 1
+ case 0xD5: // fixext 2
+ case 0xD6: // fixext 4
+ case 0xD7: // fixext 8
+ case 0xD8: // fixext 16
+ {
+ binary_t b;
+ return get_msgpack_binary(b) && sax->binary(b);
+ }
+
+ case 0xCA: // float 32
+ {
+ float number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 0xCB: // float 64
+ {
+ double number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 0xCC: // uint 8
+ {
+ std::uint8_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+ }
+
+ case 0xCD: // uint 16
+ {
+ std::uint16_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+ }
+
+ case 0xCE: // uint 32
+ {
+ std::uint32_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+ }
+
+ case 0xCF: // uint 64
+ {
+ std::uint64_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+ }
+
+ case 0xD0: // int 8
+ {
+ std::int8_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+ }
+
+ case 0xD1: // int 16
+ {
+ std::int16_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+ }
+
+ case 0xD2: // int 32
+ {
+ std::int32_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+ }
+
+ case 0xD3: // int 64
+ {
+ std::int64_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+ }
+
+ case 0xDC: // array 16
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));
+ }
+
+ case 0xDD: // array 32
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_array(conditional_static_cast<std::size_t>(len));
+ }
+
+ case 0xDE: // map 16
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));
+ }
+
+ case 0xDF: // map 32
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_object(conditional_static_cast<std::size_t>(len));
+ }
+
+ // negative fixint
+ case 0xE0:
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xED:
+ case 0xEE:
+ case 0xEF:
+ case 0xF0:
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ case 0xF4:
+ case 0xF5:
+ case 0xF6:
+ case 0xF7:
+ case 0xF8:
+ case 0xF9:
+ case 0xFA:
+ case 0xFB:
+ case 0xFC:
+ case 0xFD:
+ case 0xFE:
+ case 0xFF:
+ return sax->number_integer(static_cast<std::int8_t>(current));
+
+ default: // anything else
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format_t::msgpack, concat("invalid byte: 0x", last_token), "value"), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @brief reads a MessagePack string
+
+ This function first reads starting bytes to determine the expected
+ string length and then copies this number of bytes into a string.
+
+ @param[out] result created string
+
+ @return whether string creation completed
+ */
+ bool get_msgpack_string(string_t& result)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string")))
+ {
+ return false;
+ }
+
+ switch (current)
+ {
+ // fixstr
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF:
+ {
+ return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);
+ }
+
+ case 0xD9: // str 8
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+ }
+
+ case 0xDA: // str 16
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+ }
+
+ case 0xDB: // str 32
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+ }
+
+ default:
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+ exception_message(input_format_t::msgpack, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @brief reads a MessagePack byte array
+
+ This function first reads starting bytes to determine the expected
+ byte array length and then copies this number of bytes into a byte array.
+
+ @param[out] result created byte array
+
+ @return whether byte array creation completed
+ */
+ bool get_msgpack_binary(binary_t& result)
+ {
+ // helper function to set the subtype
+ auto assign_and_return_true = [&result](std::int8_t subtype)
+ {
+ result.set_subtype(static_cast<std::uint8_t>(subtype));
+ return true;
+ };
+
+ switch (current)
+ {
+ case 0xC4: // bin 8
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_binary(input_format_t::msgpack, len, result);
+ }
+
+ case 0xC5: // bin 16
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_binary(input_format_t::msgpack, len, result);
+ }
+
+ case 0xC6: // bin 32
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_binary(input_format_t::msgpack, len, result);
+ }
+
+ case 0xC7: // ext 8
+ {
+ std::uint8_t len{};
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, len, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xC8: // ext 16
+ {
+ std::uint16_t len{};
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, len, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xC9: // ext 32
+ {
+ std::uint32_t len{};
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, len, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xD4: // fixext 1
+ {
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 1, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xD5: // fixext 2
+ {
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 2, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xD6: // fixext 4
+ {
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 4, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xD7: // fixext 8
+ {
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 8, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xD8: // fixext 16
+ {
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 16, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ default: // LCOV_EXCL_LINE
+ return false; // LCOV_EXCL_LINE
+ }
+ }
+
+ /*!
+ @param[in] len the length of the array
+ @return whether array creation completed
+ */
+ bool get_msgpack_array(const std::size_t len)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
+ {
+ return false;
+ }
+
+ for (std::size_t i = 0; i < len; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
+ {
+ return false;
+ }
+ }
+
+ return sax->end_array();
+ }
+
+ /*!
+ @param[in] len the length of the object
+ @return whether object creation completed
+ */
+ bool get_msgpack_object(const std::size_t len)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
+ {
+ return false;
+ }
+
+ string_t key;
+ for (std::size_t i = 0; i < len; ++i)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
+ {
+ return false;
+ }
+ key.clear();
+ }
+
+ return sax->end_object();
+ }
+
+ ////////////
+ // UBJSON //
+ ////////////
+
+ /*!
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true, default) or whether the last read
+ character should be considered instead
+
+ @return whether a valid UBJSON value was passed to the SAX parser
+ */
+ bool parse_ubjson_internal(const bool get_char = true)
+ {
+ return get_ubjson_value(get_char ? get_ignore_noop() : current);
+ }
+
+ /*!
+ @brief reads a UBJSON string
+
+ This function is either called after reading the 'S' byte explicitly
+ indicating a string, or in case of an object key where the 'S' byte can be
+ left out.
+
+ @param[out] result created string
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true, default) or whether the last read
+ character should be considered instead
+
+ @return whether string creation completed
+ */
+ bool get_ubjson_string(string_t& result, const bool get_char = true)
+ {
+ if (get_char)
+ {
+ get(); // TODO(niels): may we ignore N here?
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value")))
+ {
+ return false;
+ }
+
+ switch (current)
+ {
+ case 'U':
+ {
+ std::uint8_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'i':
+ {
+ std::int8_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'I':
+ {
+ std::int16_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'l':
+ {
+ std::int32_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'L':
+ {
+ std::int64_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'u':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint16_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'm':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint32_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'M':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint64_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ default:
+ break;
+ }
+ auto last_token = get_token_string();
+ std::string message;
+
+ if (input_format != input_format_t::bjdata)
+ {
+ message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
+ }
+ else
+ {
+ message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
+ }
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr));
+ }
+
+ /*!
+ @param[out] dim an integer vector storing the ND array dimensions
+ @return whether reading ND array size vector is successful
+ */
+ bool get_ubjson_ndarray_size(std::vector<size_t>& dim)
+ {
+ std::pair<std::size_t, char_int_type> size_and_type;
+ size_t dimlen = 0;
+ bool no_ndarray = true;
+
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type, no_ndarray)))
+ {
+ return false;
+ }
+
+ if (size_and_type.first != npos)
+ {
+ if (size_and_type.second != 0)
+ {
+ if (size_and_type.second != 'N')
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second)))
+ {
+ return false;
+ }
+ dim.push_back(dimlen);
+ }
+ }
+ }
+ else
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray)))
+ {
+ return false;
+ }
+ dim.push_back(dimlen);
+ }
+ }
+ }
+ else
+ {
+ while (current != ']')
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, current)))
+ {
+ return false;
+ }
+ dim.push_back(dimlen);
+ get_ignore_noop();
+ }
+ }
+ return true;
+ }
+
+ /*!
+ @param[out] result determined size
+ @param[in,out] is_ndarray for input, `true` means already inside an ndarray vector
+ or ndarray dimension is not allowed; `false` means ndarray
+ is allowed; for output, `true` means an ndarray is found;
+ is_ndarray can only return `true` when its initial value
+ is `false`
+ @param[in] prefix type marker if already read, otherwise set to 0
+
+ @return whether size determination completed
+ */
+ bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
+ {
+ if (prefix == 0)
+ {
+ prefix = get_ignore_noop();
+ }
+
+ switch (prefix)
+ {
+ case 'U':
+ {
+ std::uint8_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ result = static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'i':
+ {
+ std::int8_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ if (number < 0)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+ exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+ }
+ result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
+ return true;
+ }
+
+ case 'I':
+ {
+ std::int16_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ if (number < 0)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+ exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+ }
+ result = static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'l':
+ {
+ std::int32_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ if (number < 0)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+ exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+ }
+ result = static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'L':
+ {
+ std::int64_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ if (number < 0)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+ exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+ }
+ if (!value_in_range_of<std::size_t>(number))
+ {
+ return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+ exception_message(input_format, "integer value overflow", "size"), nullptr));
+ }
+ result = static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'u':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint16_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ result = static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'm':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint32_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ result = conditional_static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'M':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint64_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ if (!value_in_range_of<std::size_t>(number))
+ {
+ return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+ exception_message(input_format, "integer value overflow", "size"), nullptr));
+ }
+ result = detail::conditional_static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case '[':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ if (is_ndarray) // ndarray dimensional vector can only contain integers, and can not embed another array
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimensional vector is not allowed", "size"), nullptr));
+ }
+ std::vector<size_t> dim;
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))
+ {
+ return false;
+ }
+ if (dim.size() == 1 || (dim.size() == 2 && dim.at(0) == 1)) // return normal array size if 1D row vector
+ {
+ result = dim.at(dim.size() - 1);
+ return true;
+ }
+ if (!dim.empty()) // if ndarray, convert to an object in JData annotated array format
+ {
+ for (auto i : dim) // test if any dimension in an ndarray is 0, if so, return a 1D empty container
+ {
+ if ( i == 0 )
+ {
+ result = 0;
+ return true;
+ }
+ }
+
+ string_t key = "_ArraySize_";
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
+ {
+ return false;
+ }
+ result = 1;
+ for (auto i : dim)
+ {
+ result *= i;
+ if (result == 0 || result == npos) // because dim elements shall not have zeros, result = 0 means overflow happened; it also can't be npos as it is used to initialize size in get_ubjson_size_type()
+ {
+ return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
+ }
+ if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))
+ {
+ return false;
+ }
+ }
+ is_ndarray = true;
+ return sax->end_array();
+ }
+ result = 0;
+ return true;
+ }
+
+ default:
+ break;
+ }
+ auto last_token = get_token_string();
+ std::string message;
+
+ if (input_format != input_format_t::bjdata)
+ {
+ message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
+ }
+ else
+ {
+ message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
+ }
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr));
+ }
+
+ /*!
+ @brief determine the type and size for a container
+
+ In the optimized UBJSON format, a type and a size can be provided to allow
+ for a more compact representation.
+
+ @param[out] result pair of the size and the type
+ @param[in] inside_ndarray whether the parser is parsing an ND array dimensional vector
+
+ @return whether pair creation completed
+ */
+ bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result, bool inside_ndarray = false)
+ {
+ result.first = npos; // size
+ result.second = 0; // type
+ bool is_ndarray = false;
+
+ get_ignore_noop();
+
+ if (current == '$')
+ {
+ result.second = get(); // must not ignore 'N', because 'N' maybe the type
+ if (input_format == input_format_t::bjdata
+ && JSON_HEDLEY_UNLIKELY(std::binary_search(bjd_optimized_type_markers.begin(), bjd_optimized_type_markers.end(), result.second)))
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format, concat("marker 0x", last_token, " is not a permitted optimized array type"), "type"), nullptr));
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type")))
+ {
+ return false;
+ }
+
+ get_ignore_noop();
+ if (JSON_HEDLEY_UNLIKELY(current != '#'))
+ {
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value")))
+ {
+ return false;
+ }
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
+ }
+
+ const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+ if (input_format == input_format_t::bjdata && is_ndarray)
+ {
+ if (inside_ndarray)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+ exception_message(input_format, "ndarray can not be recursive", "size"), nullptr));
+ }
+ result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
+ }
+ return is_error;
+ }
+
+ if (current == '#')
+ {
+ const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+ if (input_format == input_format_t::bjdata && is_ndarray)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+ exception_message(input_format, "ndarray requires both type and size", "size"), nullptr));
+ }
+ return is_error;
+ }
+
+ return true;
+ }
+
+ /*!
+ @param prefix the previously read or set type prefix
+ @return whether value creation completed
+ */
+ bool get_ubjson_value(const char_int_type prefix)
+ {
+ switch (prefix)
+ {
+ case std::char_traits<char_type>::eof(): // EOF
+ return unexpect_eof(input_format, "value");
+
+ case 'T': // true
+ return sax->boolean(true);
+ case 'F': // false
+ return sax->boolean(false);
+
+ case 'Z': // null
+ return sax->null();
+
+ case 'U':
+ {
+ std::uint8_t number{};
+ return get_number(input_format, number) && sax->number_unsigned(number);
+ }
+
+ case 'i':
+ {
+ std::int8_t number{};
+ return get_number(input_format, number) && sax->number_integer(number);
+ }
+
+ case 'I':
+ {
+ std::int16_t number{};
+ return get_number(input_format, number) && sax->number_integer(number);
+ }
+
+ case 'l':
+ {
+ std::int32_t number{};
+ return get_number(input_format, number) && sax->number_integer(number);
+ }
+
+ case 'L':
+ {
+ std::int64_t number{};
+ return get_number(input_format, number) && sax->number_integer(number);
+ }
+
+ case 'u':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint16_t number{};
+ return get_number(input_format, number) && sax->number_unsigned(number);
+ }
+
+ case 'm':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint32_t number{};
+ return get_number(input_format, number) && sax->number_unsigned(number);
+ }
+
+ case 'M':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint64_t number{};
+ return get_number(input_format, number) && sax->number_unsigned(number);
+ }
+
+ case 'h':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ const auto byte1_raw = get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+ {
+ return false;
+ }
+ const auto byte2_raw = get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+ {
+ return false;
+ }
+
+ const auto byte1 = static_cast<unsigned char>(byte1_raw);
+ const auto byte2 = static_cast<unsigned char>(byte2_raw);
+
+ // code from RFC 7049, Appendix D, Figure 3:
+ // As half-precision floating-point numbers were only added
+ // to IEEE 754 in 2008, today's programming platforms often
+ // still only have limited support for them. It is very
+ // easy to include at least decoding support for them even
+ // without such support. An example of a small decoder for
+ // half-precision floating-point numbers in the C language
+ // is shown in Fig. 3.
+ const auto half = static_cast<unsigned int>((byte2 << 8u) + byte1);
+ const double val = [&half]
+ {
+ const int exp = (half >> 10u) & 0x1Fu;
+ const unsigned int mant = half & 0x3FFu;
+ JSON_ASSERT(0 <= exp&& exp <= 32);
+ JSON_ASSERT(mant <= 1024);
+ switch (exp)
+ {
+ case 0:
+ return std::ldexp(mant, -24);
+ case 31:
+ return (mant == 0)
+ ? std::numeric_limits<double>::infinity()
+ : std::numeric_limits<double>::quiet_NaN();
+ default:
+ return std::ldexp(mant + 1024, exp - 25);
+ }
+ }();
+ return sax->number_float((half & 0x8000u) != 0
+ ? static_cast<number_float_t>(-val)
+ : static_cast<number_float_t>(val), "");
+ }
+
+ case 'd':
+ {
+ float number{};
+ return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 'D':
+ {
+ double number{};
+ return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 'H':
+ {
+ return get_ubjson_high_precision_number();
+ }
+
+ case 'C': // char
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "char")))
+ {
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(current > 127))
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+ exception_message(input_format, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
+ }
+ string_t s(1, static_cast<typename string_t::value_type>(current));
+ return sax->string(s);
+ }
+
+ case 'S': // string
+ {
+ string_t s;
+ return get_ubjson_string(s) && sax->string(s);
+ }
+
+ case '[': // array
+ return get_ubjson_array();
+
+ case '{': // object
+ return get_ubjson_object();
+
+ default: // anything else
+ break;
+ }
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format, "invalid byte: 0x" + last_token, "value"), nullptr));
+ }
+
+ /*!
+ @return whether array creation completed
+ */
+ bool get_ubjson_array()
+ {
+ std::pair<std::size_t, char_int_type> size_and_type;
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+ {
+ return false;
+ }
+
+ // if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
+ // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
+
+ if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
+ {
+ size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
+ auto it = std::lower_bound(bjd_types_map.begin(), bjd_types_map.end(), size_and_type.second, [](const bjd_type & p, char_int_type t)
+ {
+ return p.first < t;
+ });
+ string_t key = "_ArrayType_";
+ if (JSON_HEDLEY_UNLIKELY(it == bjd_types_map.end() || it->first != size_and_type.second))
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
+ }
+
+ string_t type = it->second; // sax->string() takes a reference
+ if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
+ {
+ return false;
+ }
+
+ if (size_and_type.second == 'C')
+ {
+ size_and_type.second = 'U';
+ }
+
+ key = "_ArrayData_";
+ if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
+ {
+ return false;
+ }
+
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+ {
+ return false;
+ }
+ }
+
+ return (sax->end_array() && sax->end_object());
+ }
+
+ if (size_and_type.first != npos)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))
+ {
+ return false;
+ }
+
+ if (size_and_type.second != 0)
+ {
+ if (size_and_type.second != 'N')
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+ {
+ return false;
+ }
+
+ while (current != ']')
+ {
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))
+ {
+ return false;
+ }
+ get_ignore_noop();
+ }
+ }
+
+ return sax->end_array();
+ }
+
+ /*!
+ @return whether object creation completed
+ */
+ bool get_ubjson_object()
+ {
+ std::pair<std::size_t, char_int_type> size_and_type;
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+ {
+ return false;
+ }
+
+ // do not accept ND-array size in objects in BJData
+ if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format, "BJData object does not support ND-array size in optimized format", "object"), nullptr));
+ }
+
+ string_t key;
+ if (size_and_type.first != npos)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))
+ {
+ return false;
+ }
+
+ if (size_and_type.second != 0)
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
+ {
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+ {
+ return false;
+ }
+ key.clear();
+ }
+ }
+ else
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
+ {
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+ {
+ return false;
+ }
+ key.clear();
+ }
+ }
+ }
+ else
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+ {
+ return false;
+ }
+
+ while (current != '}')
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))
+ {
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+ {
+ return false;
+ }
+ get_ignore_noop();
+ key.clear();
+ }
+ }
+
+ return sax->end_object();
+ }
+
+ // Note, no reader for UBJSON binary types is implemented because they do
+ // not exist
+
+ bool get_ubjson_high_precision_number()
+ {
+ // get size of following number string
+ std::size_t size{};
+ bool no_ndarray = true;
+ auto res = get_ubjson_size_value(size, no_ndarray);
+ if (JSON_HEDLEY_UNLIKELY(!res))
+ {
+ return res;
+ }
+
+ // get number string
+ std::vector<char> number_vector;
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+ {
+ return false;
+ }
+ number_vector.push_back(static_cast<char>(current));
+ }
+
+ // parse number string
+ using ia_type = decltype(detail::input_adapter(number_vector));
+ auto number_lexer = detail::lexer<BasicJsonType, ia_type>(detail::input_adapter(number_vector), false);
+ const auto result_number = number_lexer.scan();
+ const auto number_string = number_lexer.get_token_string();
+ const auto result_remainder = number_lexer.scan();
+
+ using token_type = typename detail::lexer_base<BasicJsonType>::token_type;
+
+ if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
+ {
+ return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
+ exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
+ }
+
+ switch (result_number)
+ {
+ case token_type::value_integer:
+ return sax->number_integer(number_lexer.get_number_integer());
+ case token_type::value_unsigned:
+ return sax->number_unsigned(number_lexer.get_number_unsigned());
+ case token_type::value_float:
+ return sax->number_float(number_lexer.get_number_float(), std::move(number_string));
+ case token_type::uninitialized:
+ case token_type::literal_true:
+ case token_type::literal_false:
+ case token_type::literal_null:
+ case token_type::value_string:
+ case token_type::begin_array:
+ case token_type::begin_object:
+ case token_type::end_array:
+ case token_type::end_object:
+ case token_type::name_separator:
+ case token_type::value_separator:
+ case token_type::parse_error:
+ case token_type::end_of_input:
+ case token_type::literal_or_value:
+ default:
+ return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
+ exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
+ }
+ }
+
+ ///////////////////////
+ // Utility functions //
+ ///////////////////////
+
+ /*!
+ @brief get next character from the input
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns a -'ve valued
+ `std::char_traits<char_type>::eof()` in that case.
+
+ @return character read from the input
+ */
+ char_int_type get()
+ {
+ ++chars_read;
+ return current = ia.get_character();
+ }
+
+ /*!
+ @return character read from the input after ignoring all 'N' entries
+ */
+ char_int_type get_ignore_noop()
+ {
+ do
+ {
+ get();
+ }
+ while (current == 'N');
+
+ return current;
+ }
+
+ /*
+ @brief read a number from the input
+
+ @tparam NumberType the type of the number
+ @param[in] format the current format (for diagnostics)
+ @param[out] result number of type @a NumberType
+
+ @return whether conversion completed
+
+ @note This function needs to respect the system's endianness, because
+ bytes in CBOR, MessagePack, and UBJSON are stored in network order
+ (big endian) and therefore need reordering on little endian systems.
+ On the other hand, BSON and BJData use little endian and should reorder
+ on big endian systems.
+ */
+ template<typename NumberType, bool InputIsLittleEndian = false>
+ bool get_number(const input_format_t format, NumberType& result)
+ {
+ // step 1: read input into array with system's byte order
+ std::array<std::uint8_t, sizeof(NumberType)> vec{};
+ for (std::size_t i = 0; i < sizeof(NumberType); ++i)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
+ {
+ return false;
+ }
+
+ // reverse byte order prior to conversion if necessary
+ if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata))
+ {
+ vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);
+ }
+ else
+ {
+ vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE
+ }
+ }
+
+ // step 2: convert array into number of type T and return
+ std::memcpy(&result, vec.data(), sizeof(NumberType));
+ return true;
+ }
+
+ /*!
+ @brief create a string by reading characters from the input
+
+ @tparam NumberType the type of the number
+ @param[in] format the current format (for diagnostics)
+ @param[in] len number of characters to read
+ @param[out] result string created by reading @a len bytes
+
+ @return whether string creation completed
+
+ @note We can not reserve @a len bytes for the result, because @a len
+ may be too large. Usually, @ref unexpect_eof() detects the end of
+ the input before we run out of string memory.
+ */
+ template<typename NumberType>
+ bool get_string(const input_format_t format,
+ const NumberType len,
+ string_t& result)
+ {
+ bool success = true;
+ for (NumberType i = 0; i < len; i++)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string")))
+ {
+ success = false;
+ break;
+ }
+ result.push_back(static_cast<typename string_t::value_type>(current));
+ }
+ return success;
+ }
+
+ /*!
+ @brief create a byte array by reading bytes from the input
+
+ @tparam NumberType the type of the number
+ @param[in] format the current format (for diagnostics)
+ @param[in] len number of bytes to read
+ @param[out] result byte array created by reading @a len bytes
+
+ @return whether byte array creation completed
+
+ @note We can not reserve @a len bytes for the result, because @a len
+ may be too large. Usually, @ref unexpect_eof() detects the end of
+ the input before we run out of memory.
+ */
+ template<typename NumberType>
+ bool get_binary(const input_format_t format,
+ const NumberType len,
+ binary_t& result)
+ {
+ bool success = true;
+ for (NumberType i = 0; i < len; i++)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary")))
+ {
+ success = false;
+ break;
+ }
+ result.push_back(static_cast<std::uint8_t>(current));
+ }
+ return success;
+ }
+
+ /*!
+ @param[in] format the current format (for diagnostics)
+ @param[in] context further context information (for diagnostics)
+ @return whether the last read character is not EOF
+ */
+ JSON_HEDLEY_NON_NULL(3)
+ bool unexpect_eof(const input_format_t format, const char* context) const
+ {
+ if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))
+ {
+ return sax->parse_error(chars_read, "<end of file>",
+ parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr));
+ }
+ return true;
+ }
+
+ /*!
+ @return a string representation of the last read byte
+ */
+ std::string get_token_string() const
+ {
+ std::array<char, 3> cr{{}};
+ static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ return std::string{cr.data()};
+ }
+
+ /*!
+ @param[in] format the current format
+ @param[in] detail a detailed error message
+ @param[in] context further context information
+ @return a message string to use in the parse_error exceptions
+ */
+ std::string exception_message(const input_format_t format,
+ const std::string& detail,
+ const std::string& context) const
+ {
+ std::string error_msg = "syntax error while parsing ";
+
+ switch (format)
+ {
+ case input_format_t::cbor:
+ error_msg += "CBOR";
+ break;
+
+ case input_format_t::msgpack:
+ error_msg += "MessagePack";
+ break;
+
+ case input_format_t::ubjson:
+ error_msg += "UBJSON";
+ break;
+
+ case input_format_t::bson:
+ error_msg += "BSON";
+ break;
+
+ case input_format_t::bjdata:
+ error_msg += "BJData";
+ break;
+
+ case input_format_t::json: // LCOV_EXCL_LINE
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+
+ return concat(error_msg, ' ', context, ": ", detail);
+ }
+
+ private:
+ static JSON_INLINE_VARIABLE constexpr std::size_t npos = static_cast<std::size_t>(-1);
+
+ /// input adapter
+ InputAdapterType ia;
+
+ /// the current character
+ char_int_type current = std::char_traits<char_type>::eof();
+
+ /// the number of characters read
+ std::size_t chars_read = 0;
+
+ /// whether we can assume little endianness
+ const bool is_little_endian = little_endianness();
+
+ /// input format
+ const input_format_t input_format = input_format_t::json;
+
+ /// the SAX parser
+ json_sax_t* sax = nullptr;
+
+ // excluded markers in bjdata optimized type
+#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \
+ make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')
+
+#define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \
+ make_array<bjd_type>( \
+ bjd_type{'C', "char"}, \
+ bjd_type{'D', "double"}, \
+ bjd_type{'I', "int16"}, \
+ bjd_type{'L', "int64"}, \
+ bjd_type{'M', "uint64"}, \
+ bjd_type{'U', "uint8"}, \
+ bjd_type{'d', "single"}, \
+ bjd_type{'i', "int8"}, \
+ bjd_type{'l', "int32"}, \
+ bjd_type{'m', "uint32"}, \
+ bjd_type{'u', "uint16"})
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ // lookup tables
+ // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
+ const decltype(JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_) bjd_optimized_type_markers =
+ JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_;
+
+ using bjd_type = std::pair<char_int_type, string_t>;
+ // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
+ const decltype(JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_) bjd_types_map =
+ JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_;
+
+#undef JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_
+#undef JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_
+};
+
+#ifndef JSON_HAS_CPP_17
+ template<typename BasicJsonType, typename InputAdapterType, typename SAX>
+ constexpr std::size_t binary_reader<BasicJsonType, InputAdapterType, SAX>::npos;
+#endif
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/lexer.hpp>
+
+// #include <nlohmann/detail/input/parser.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cmath> // isfinite
+#include <cstdint> // uint8_t
+#include <functional> // function
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/json_sax.hpp>
+
+// #include <nlohmann/detail/input/lexer.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/is_sax.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+////////////
+// parser //
+////////////
+
+enum class parse_event_t : std::uint8_t
+{
+ /// the parser read `{` and started to process a JSON object
+ object_start,
+ /// the parser read `}` and finished processing a JSON object
+ object_end,
+ /// the parser read `[` and started to process a JSON array
+ array_start,
+ /// the parser read `]` and finished processing a JSON array
+ array_end,
+ /// the parser read a key of a value in an object
+ key,
+ /// the parser finished reading a JSON value
+ value
+};
+
+template<typename BasicJsonType>
+using parser_callback_t =
+ std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
+
+/*!
+@brief syntax analysis
+
+This class implements a recursive descent parser.
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class parser
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using lexer_t = lexer<BasicJsonType, InputAdapterType>;
+ using token_type = typename lexer_t::token_type;
+
+ public:
+ /// a parser reading from an input adapter
+ explicit parser(InputAdapterType&& adapter,
+ const parser_callback_t<BasicJsonType> cb = nullptr,
+ const bool allow_exceptions_ = true,
+ const bool skip_comments = false)
+ : callback(cb)
+ , m_lexer(std::move(adapter), skip_comments)
+ , allow_exceptions(allow_exceptions_)
+ {
+ // read first token
+ get_token();
+ }
+
+ /*!
+ @brief public parser interface
+
+ @param[in] strict whether to expect the last token to be EOF
+ @param[in,out] result parsed JSON value
+
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+ */
+ void parse(const bool strict, BasicJsonType& result)
+ {
+ if (callback)
+ {
+ json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
+ sax_parse_internal(&sdp);
+
+ // in strict mode, input must be completely read
+ if (strict && (get_token() != token_type::end_of_input))
+ {
+ sdp.parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(),
+ exception_message(token_type::end_of_input, "value"), nullptr));
+ }
+
+ // in case of an error, return discarded value
+ if (sdp.is_errored())
+ {
+ result = value_t::discarded;
+ return;
+ }
+
+ // set top-level value to null if it was discarded by the callback
+ // function
+ if (result.is_discarded())
+ {
+ result = nullptr;
+ }
+ }
+ else
+ {
+ json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
+ sax_parse_internal(&sdp);
+
+ // in strict mode, input must be completely read
+ if (strict && (get_token() != token_type::end_of_input))
+ {
+ sdp.parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
+ }
+
+ // in case of an error, return discarded value
+ if (sdp.is_errored())
+ {
+ result = value_t::discarded;
+ return;
+ }
+ }
+
+ result.assert_invariant();
+ }
+
+ /*!
+ @brief public accept interface
+
+ @param[in] strict whether to expect the last token to be EOF
+ @return whether the input is a proper JSON text
+ */
+ bool accept(const bool strict = true)
+ {
+ json_sax_acceptor<BasicJsonType> sax_acceptor;
+ return sax_parse(&sax_acceptor, strict);
+ }
+
+ template<typename SAX>
+ JSON_HEDLEY_NON_NULL(2)
+ bool sax_parse(SAX* sax, const bool strict = true)
+ {
+ (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
+ const bool result = sax_parse_internal(sax);
+
+ // strict mode: next byte must be EOF
+ if (result && strict && (get_token() != token_type::end_of_input))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
+ }
+
+ return result;
+ }
+
+ private:
+ template<typename SAX>
+ JSON_HEDLEY_NON_NULL(2)
+ bool sax_parse_internal(SAX* sax)
+ {
+ // stack to remember the hierarchy of structured values we are parsing
+ // true = array; false = object
+ std::vector<bool> states;
+ // value to avoid a goto (see comment where set to true)
+ bool skip_to_state_evaluation = false;
+
+ while (true)
+ {
+ if (!skip_to_state_evaluation)
+ {
+ // invariant: get_token() was called before each iteration
+ switch (last_token)
+ {
+ case token_type::begin_object:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
+ {
+ return false;
+ }
+
+ // closing } -> we are done
+ if (get_token() == token_type::end_object)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
+ {
+ return false;
+ }
+ break;
+ }
+
+ // parse key
+ if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
+ }
+ if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
+ {
+ return false;
+ }
+
+ // parse separator (:)
+ if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
+ }
+
+ // remember we are now inside an object
+ states.push_back(false);
+
+ // parse values
+ get_token();
+ continue;
+ }
+
+ case token_type::begin_array:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
+ {
+ return false;
+ }
+
+ // closing ] -> we are done
+ if (get_token() == token_type::end_array)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
+ {
+ return false;
+ }
+ break;
+ }
+
+ // remember we are now inside an array
+ states.push_back(true);
+
+ // parse values (no need to call get_token)
+ continue;
+ }
+
+ case token_type::value_float:
+ {
+ const auto res = m_lexer.get_number_float();
+
+ if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
+ {
+ return false;
+ }
+
+ break;
+ }
+
+ case token_type::literal_false:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::literal_null:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->null()))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::literal_true:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::value_integer:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::value_string:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::value_unsigned:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::parse_error:
+ {
+ // using "uninitialized" to avoid "expected" message
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
+ }
+
+ case token_type::uninitialized:
+ case token_type::end_array:
+ case token_type::end_object:
+ case token_type::name_separator:
+ case token_type::value_separator:
+ case token_type::end_of_input:
+ case token_type::literal_or_value:
+ default: // the last token was unexpected
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
+ }
+ }
+ }
+ else
+ {
+ skip_to_state_evaluation = false;
+ }
+
+ // we reached this line after we successfully parsed a value
+ if (states.empty())
+ {
+ // empty stack: we reached the end of the hierarchy: done
+ return true;
+ }
+
+ if (states.back()) // array
+ {
+ // comma -> next value
+ if (get_token() == token_type::value_separator)
+ {
+ // parse a new value
+ get_token();
+ continue;
+ }
+
+ // closing ]
+ if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
+ {
+ return false;
+ }
+
+ // We are done with this array. Before we can parse a
+ // new value, we need to evaluate the new state first.
+ // By setting skip_to_state_evaluation to false, we
+ // are effectively jumping to the beginning of this if.
+ JSON_ASSERT(!states.empty());
+ states.pop_back();
+ skip_to_state_evaluation = true;
+ continue;
+ }
+
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
+ }
+
+ // states.back() is false -> object
+
+ // comma -> next value
+ if (get_token() == token_type::value_separator)
+ {
+ // parse key
+ if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
+ {
+ return false;
+ }
+
+ // parse separator (:)
+ if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
+ }
+
+ // parse values
+ get_token();
+ continue;
+ }
+
+ // closing }
+ if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
+ {
+ return false;
+ }
+
+ // We are done with this object. Before we can parse a
+ // new value, we need to evaluate the new state first.
+ // By setting skip_to_state_evaluation to false, we
+ // are effectively jumping to the beginning of this if.
+ JSON_ASSERT(!states.empty());
+ states.pop_back();
+ skip_to_state_evaluation = true;
+ continue;
+ }
+
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
+ }
+ }
+
+ /// get next token from lexer
+ token_type get_token()
+ {
+ return last_token = m_lexer.scan();
+ }
+
+ std::string exception_message(const token_type expected, const std::string& context)
+ {
+ std::string error_msg = "syntax error ";
+
+ if (!context.empty())
+ {
+ error_msg += concat("while parsing ", context, ' ');
+ }
+
+ error_msg += "- ";
+
+ if (last_token == token_type::parse_error)
+ {
+ error_msg += concat(m_lexer.get_error_message(), "; last read: '",
+ m_lexer.get_token_string(), '\'');
+ }
+ else
+ {
+ error_msg += concat("unexpected ", lexer_t::token_type_name(last_token));
+ }
+
+ if (expected != token_type::uninitialized)
+ {
+ error_msg += concat("; expected ", lexer_t::token_type_name(expected));
+ }
+
+ return error_msg;
+ }
+
+ private:
+ /// callback function
+ const parser_callback_t<BasicJsonType> callback = nullptr;
+ /// the type of the last read token
+ token_type last_token = token_type::uninitialized;
+ /// the lexer
+ lexer_t m_lexer;
+ /// whether to throw exceptions in case of errors
+ const bool allow_exceptions = true;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/internal_iterator.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // ptrdiff_t
+#include <limits> // numeric_limits
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*
+@brief an iterator for primitive JSON types
+
+This class models an iterator for primitive JSON types (boolean, number,
+string). It's only purpose is to allow the iterator/const_iterator classes
+to "iterate" over primitive values. Internally, the iterator is modeled by
+a `difference_type` variable. Value begin_value (`0`) models the begin,
+end_value (`1`) models past the end.
+*/
+class primitive_iterator_t
+{
+ private:
+ using difference_type = std::ptrdiff_t;
+ static constexpr difference_type begin_value = 0;
+ static constexpr difference_type end_value = begin_value + 1;
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ /// iterator as signed integer type
+ difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
+
+ public:
+ constexpr difference_type get_value() const noexcept
+ {
+ return m_it;
+ }
+
+ /// set iterator to a defined beginning
+ void set_begin() noexcept
+ {
+ m_it = begin_value;
+ }
+
+ /// set iterator to a defined past the end
+ void set_end() noexcept
+ {
+ m_it = end_value;
+ }
+
+ /// return whether the iterator can be dereferenced
+ constexpr bool is_begin() const noexcept
+ {
+ return m_it == begin_value;
+ }
+
+ /// return whether the iterator is at end
+ constexpr bool is_end() const noexcept
+ {
+ return m_it == end_value;
+ }
+
+ friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it == rhs.m_it;
+ }
+
+ friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it < rhs.m_it;
+ }
+
+ primitive_iterator_t operator+(difference_type n) noexcept
+ {
+ auto result = *this;
+ result += n;
+ return result;
+ }
+
+ friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it - rhs.m_it;
+ }
+
+ primitive_iterator_t& operator++() noexcept
+ {
+ ++m_it;
+ return *this;
+ }
+
+ primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp)
+ {
+ auto result = *this;
+ ++m_it;
+ return result;
+ }
+
+ primitive_iterator_t& operator--() noexcept
+ {
+ --m_it;
+ return *this;
+ }
+
+ primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp)
+ {
+ auto result = *this;
+ --m_it;
+ return result;
+ }
+
+ primitive_iterator_t& operator+=(difference_type n) noexcept
+ {
+ m_it += n;
+ return *this;
+ }
+
+ primitive_iterator_t& operator-=(difference_type n) noexcept
+ {
+ m_it -= n;
+ return *this;
+ }
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief an iterator value
+
+@note This structure could easily be a union, but MSVC currently does not allow
+unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
+*/
+template<typename BasicJsonType> struct internal_iterator
+{
+ /// iterator for JSON objects
+ typename BasicJsonType::object_t::iterator object_iterator {};
+ /// iterator for JSON arrays
+ typename BasicJsonType::array_t::iterator array_iterator {};
+ /// generic iterator for all other types
+ primitive_iterator_t primitive_iterator {};
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/iter_impl.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
+#include <type_traits> // conditional, is_const, remove_const
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/iterators/internal_iterator.hpp>
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// forward declare, to be able to friend it later on
+template<typename IteratorType> class iteration_proxy;
+template<typename IteratorType> class iteration_proxy_value;
+
+/*!
+@brief a template for a bidirectional iterator for the @ref basic_json class
+This class implements a both iterators (iterator and const_iterator) for the
+@ref basic_json class.
+@note An iterator is called *initialized* when a pointer to a JSON value has
+ been set (e.g., by a constructor or a copy assignment). If the iterator is
+ default-constructed, it is *uninitialized* and most methods are undefined.
+ **The library uses assertions to detect calls on uninitialized iterators.**
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
+ The iterator that can be moved can be moved in both directions (i.e.
+ incremented and decremented).
+@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
+ iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
+*/
+template<typename BasicJsonType>
+class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
+{
+ /// the iterator with BasicJsonType of different const-ness
+ using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
+ /// allow basic_json to access private members
+ friend other_iter_impl;
+ friend BasicJsonType;
+ friend iteration_proxy<iter_impl>;
+ friend iteration_proxy_value<iter_impl>;
+
+ using object_t = typename BasicJsonType::object_t;
+ using array_t = typename BasicJsonType::array_t;
+ // make sure BasicJsonType is basic_json or const basic_json
+ static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
+ "iter_impl only accepts (const) basic_json");
+ // superficial check for the LegacyBidirectionalIterator named requirement
+ static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value
+ && std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<typename array_t::iterator>::iterator_category>::value,
+ "basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.");
+
+ public:
+ /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
+ /// The C++ Standard has never required user-defined iterators to derive from std::iterator.
+ /// A user-defined iterator should provide publicly accessible typedefs named
+ /// iterator_category, value_type, difference_type, pointer, and reference.
+ /// Note that value_type is required to be non-const, even for constant iterators.
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ /// the type of the values when the iterator is dereferenced
+ using value_type = typename BasicJsonType::value_type;
+ /// a type to represent differences between iterators
+ using difference_type = typename BasicJsonType::difference_type;
+ /// defines a pointer to the type iterated over (value_type)
+ using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
+ typename BasicJsonType::const_pointer,
+ typename BasicJsonType::pointer>::type;
+ /// defines a reference to the type iterated over (value_type)
+ using reference =
+ typename std::conditional<std::is_const<BasicJsonType>::value,
+ typename BasicJsonType::const_reference,
+ typename BasicJsonType::reference>::type;
+
+ iter_impl() = default;
+ ~iter_impl() = default;
+ iter_impl(iter_impl&&) noexcept = default;
+ iter_impl& operator=(iter_impl&&) noexcept = default;
+
+ /*!
+ @brief constructor for a given JSON instance
+ @param[in] object pointer to a JSON object for this iterator
+ @pre object != nullptr
+ @post The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ explicit iter_impl(pointer object) noexcept : m_object(object)
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = typename object_t::iterator();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = typename array_t::iterator();
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ m_it.primitive_iterator = primitive_iterator_t();
+ break;
+ }
+ }
+ }
+
+ /*!
+ @note The conventional copy constructor and copy assignment are implicitly
+ defined. Combined with the following converting constructor and
+ assignment, they support: (1) copy from iterator to iterator, (2)
+ copy from const iterator to const iterator, and (3) conversion from
+ iterator to const iterator. However conversion from const iterator
+ to iterator is not defined.
+ */
+
+ /*!
+ @brief const copy constructor
+ @param[in] other const iterator to copy from
+ @note This copy constructor had to be defined explicitly to circumvent a bug
+ occurring on msvc v19.0 compiler (VS 2015) debug build. For more
+ information refer to: https://github.com/nlohmann/json/issues/1608
+ */
+ iter_impl(const iter_impl<const BasicJsonType>& other) noexcept
+ : m_object(other.m_object), m_it(other.m_it)
+ {}
+
+ /*!
+ @brief converting assignment
+ @param[in] other const iterator to copy from
+ @return const/non-const iterator
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept
+ {
+ if (&other != this)
+ {
+ m_object = other.m_object;
+ m_it = other.m_it;
+ }
+ return *this;
+ }
+
+ /*!
+ @brief converting constructor
+ @param[in] other non-const iterator to copy from
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
+ : m_object(other.m_object), m_it(other.m_it)
+ {}
+
+ /*!
+ @brief converting assignment
+ @param[in] other non-const iterator to copy from
+ @return const/non-const iterator
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)
+ {
+ m_object = other.m_object;
+ m_it = other.m_it;
+ return *this;
+ }
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ /*!
+ @brief set the iterator to the first value
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ void set_begin() noexcept
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = m_object->m_value.object->begin();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = m_object->m_value.array->begin();
+ break;
+ }
+
+ case value_t::null:
+ {
+ // set to end so begin()==end() is true: null is empty
+ m_it.primitive_iterator.set_end();
+ break;
+ }
+
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ m_it.primitive_iterator.set_begin();
+ break;
+ }
+ }
+ }
+
+ /*!
+ @brief set the iterator past the last value
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ void set_end() noexcept
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = m_object->m_value.object->end();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = m_object->m_value.array->end();
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ m_it.primitive_iterator.set_end();
+ break;
+ }
+ }
+ }
+
+ public:
+ /*!
+ @brief return a reference to the value pointed to by the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference operator*() const
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
+ return m_it.object_iterator->second;
+ }
+
+ case value_t::array:
+ {
+ JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
+ return *m_it.array_iterator;
+ }
+
+ case value_t::null:
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
+ {
+ return *m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+ }
+ }
+ }
+
+ /*!
+ @brief dereference the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ pointer operator->() const
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
+ return &(m_it.object_iterator->second);
+ }
+
+ case value_t::array:
+ {
+ JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
+ return &*m_it.array_iterator;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
+ {
+ return m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+ }
+ }
+ }
+
+ /*!
+ @brief post-increment (it++)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp)
+ {
+ auto result = *this;
+ ++(*this);
+ return result;
+ }
+
+ /*!
+ @brief pre-increment (++it)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator++()
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ std::advance(m_it.object_iterator, 1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, 1);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ ++m_it.primitive_iterator;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief post-decrement (it--)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp)
+ {
+ auto result = *this;
+ --(*this);
+ return result;
+ }
+
+ /*!
+ @brief pre-decrement (--it)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator--()
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ std::advance(m_it.object_iterator, -1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, -1);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ --m_it.primitive_iterator;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief comparison: equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
+ bool operator==(const IterImpl& other) const
+ {
+ // if objects are not the same, the comparison is undefined
+ if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
+ }
+
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ return (m_it.object_iterator == other.m_it.object_iterator);
+
+ case value_t::array:
+ return (m_it.array_iterator == other.m_it.array_iterator);
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ return (m_it.primitive_iterator == other.m_it.primitive_iterator);
+ }
+ }
+
+ /*!
+ @brief comparison: not equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
+ bool operator!=(const IterImpl& other) const
+ {
+ return !operator==(other);
+ }
+
+ /*!
+ @brief comparison: smaller
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator<(const iter_impl& other) const
+ {
+ // if objects are not the same, the comparison is undefined
+ if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
+ }
+
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object));
+
+ case value_t::array:
+ return (m_it.array_iterator < other.m_it.array_iterator);
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ return (m_it.primitive_iterator < other.m_it.primitive_iterator);
+ }
+ }
+
+ /*!
+ @brief comparison: less than or equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator<=(const iter_impl& other) const
+ {
+ return !other.operator < (*this);
+ }
+
+ /*!
+ @brief comparison: greater than
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator>(const iter_impl& other) const
+ {
+ return !operator<=(other);
+ }
+
+ /*!
+ @brief comparison: greater than or equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator>=(const iter_impl& other) const
+ {
+ return !operator<(other);
+ }
+
+ /*!
+ @brief add to iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator+=(difference_type i)
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, i);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ m_it.primitive_iterator += i;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief subtract from iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator-=(difference_type i)
+ {
+ return operator+=(-i);
+ }
+
+ /*!
+ @brief add to iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator+(difference_type i) const
+ {
+ auto result = *this;
+ result += i;
+ return result;
+ }
+
+ /*!
+ @brief addition of distance and iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ friend iter_impl operator+(difference_type i, const iter_impl& it)
+ {
+ auto result = it;
+ result += i;
+ return result;
+ }
+
+ /*!
+ @brief subtract from iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator-(difference_type i) const
+ {
+ auto result = *this;
+ result -= i;
+ return result;
+ }
+
+ /*!
+ @brief return difference
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ difference_type operator-(const iter_impl& other) const
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
+
+ case value_t::array:
+ return m_it.array_iterator - other.m_it.array_iterator;
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ return m_it.primitive_iterator - other.m_it.primitive_iterator;
+ }
+ }
+
+ /*!
+ @brief access to successor
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference operator[](difference_type n) const
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object));
+
+ case value_t::array:
+ return *std::next(m_it.array_iterator, n);
+
+ case value_t::null:
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))
+ {
+ return *m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+ }
+ }
+ }
+
+ /*!
+ @brief return the key of an object iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ const typename object_t::key_type& key() const
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ if (JSON_HEDLEY_LIKELY(m_object->is_object()))
+ {
+ return m_it.object_iterator->first;
+ }
+
+ JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object));
+ }
+
+ /*!
+ @brief return the value of an iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference value() const
+ {
+ return operator*();
+ }
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ /// associated JSON instance
+ pointer m_object = nullptr;
+ /// the actual iterator of the associated instance
+ internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
+
+// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // ptrdiff_t
+#include <iterator> // reverse_iterator
+#include <utility> // declval
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+//////////////////////
+// reverse_iterator //
+//////////////////////
+
+/*!
+@brief a template for a reverse iterator class
+
+@tparam Base the base iterator type to reverse. Valid types are @ref
+iterator (to create @ref reverse_iterator) and @ref const_iterator (to
+create @ref const_reverse_iterator).
+
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
+ The iterator that can be moved can be moved in both directions (i.e.
+ incremented and decremented).
+- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
+ It is possible to write to the pointed-to element (only if @a Base is
+ @ref iterator).
+
+@since version 1.0.0
+*/
+template<typename Base>
+class json_reverse_iterator : public std::reverse_iterator<Base>
+{
+ public:
+ using difference_type = std::ptrdiff_t;
+ /// shortcut to the reverse iterator adapter
+ using base_iterator = std::reverse_iterator<Base>;
+ /// the reference type for the pointed-to element
+ using reference = typename Base::reference;
+
+ /// create reverse iterator from iterator
+ explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
+ : base_iterator(it) {}
+
+ /// create reverse iterator from base class
+ explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
+
+ /// post-increment (it++)
+ json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp)
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
+ }
+
+ /// pre-increment (++it)
+ json_reverse_iterator& operator++()
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator++());
+ }
+
+ /// post-decrement (it--)
+ json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp)
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
+ }
+
+ /// pre-decrement (--it)
+ json_reverse_iterator& operator--()
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator--());
+ }
+
+ /// add to iterator
+ json_reverse_iterator& operator+=(difference_type i)
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
+ }
+
+ /// add to iterator
+ json_reverse_iterator operator+(difference_type i) const
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
+ }
+
+ /// subtract from iterator
+ json_reverse_iterator operator-(difference_type i) const
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
+ }
+
+ /// return difference
+ difference_type operator-(const json_reverse_iterator& other) const
+ {
+ return base_iterator(*this) - base_iterator(other);
+ }
+
+ /// access to successor
+ reference operator[](difference_type n) const
+ {
+ return *(this->operator+(n));
+ }
+
+ /// return the key of an object iterator
+ auto key() const -> decltype(std::declval<Base>().key())
+ {
+ auto it = --this->base();
+ return it.key();
+ }
+
+ /// return the value of an iterator
+ reference value() const
+ {
+ auto it = --this->base();
+ return it.operator * ();
+ }
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+
+// #include <nlohmann/detail/json_custom_base_class.hpp>
+
+
+#include <type_traits> // conditional, is_same
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief Default base class of the @ref basic_json class.
+
+So that the correct implementations of the copy / move ctors / assign operators
+of @ref basic_json do not require complex case distinctions
+(no base class / custom base class used as customization point),
+@ref basic_json always has a base class.
+By default, this class is used because it is empty and thus has no effect
+on the behavior of @ref basic_json.
+*/
+struct json_default_base {};
+
+template<class T>
+using json_base_class = typename std::conditional <
+ std::is_same<T, void>::value,
+ json_default_base,
+ T
+ >::type;
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/json_pointer.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // all_of
+#include <cctype> // isdigit
+#include <cerrno> // errno, ERANGE
+#include <cstdlib> // strtoull
+#ifndef JSON_NO_IO
+ #include <iosfwd> // ostream
+#endif // JSON_NO_IO
+#include <limits> // max
+#include <numeric> // accumulate
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/string_escape.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
+/// @sa https://json.nlohmann.me/api/json_pointer/
+template<typename RefStringType>
+class json_pointer
+{
+ // allow basic_json to access private members
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ friend class basic_json;
+
+ template<typename>
+ friend class json_pointer;
+
+ template<typename T>
+ struct string_t_helper
+ {
+ using type = T;
+ };
+
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ struct string_t_helper<NLOHMANN_BASIC_JSON_TPL>
+ {
+ using type = StringType;
+ };
+
+ public:
+ // for backwards compatibility accept BasicJsonType
+ using string_t = typename string_t_helper<RefStringType>::type;
+
+ /// @brief create JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/
+ explicit json_pointer(const string_t& s = "")
+ : reference_tokens(split(s))
+ {}
+
+ /// @brief return a string representation of the JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/to_string/
+ string_t to_string() const
+ {
+ return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
+ string_t{},
+ [](const string_t& a, const string_t& b)
+ {
+ return detail::concat(a, '/', detail::escape(b));
+ });
+ }
+
+ /// @brief return a string representation of the JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())
+ operator string_t() const
+ {
+ return to_string();
+ }
+
+#ifndef JSON_NO_IO
+ /// @brief write string representation of the JSON pointer to stream
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+ friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr)
+ {
+ o << ptr.to_string();
+ return o;
+ }
+#endif
+
+ /// @brief append another JSON pointer at the end of this JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+ json_pointer& operator/=(const json_pointer& ptr)
+ {
+ reference_tokens.insert(reference_tokens.end(),
+ ptr.reference_tokens.begin(),
+ ptr.reference_tokens.end());
+ return *this;
+ }
+
+ /// @brief append an unescaped reference token at the end of this JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+ json_pointer& operator/=(string_t token)
+ {
+ push_back(std::move(token));
+ return *this;
+ }
+
+ /// @brief append an array index at the end of this JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+ json_pointer& operator/=(std::size_t array_idx)
+ {
+ return *this /= std::to_string(array_idx);
+ }
+
+ /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+ friend json_pointer operator/(const json_pointer& lhs,
+ const json_pointer& rhs)
+ {
+ return json_pointer(lhs) /= rhs;
+ }
+
+ /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+ friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)
+ {
+ return json_pointer(lhs) /= std::move(token);
+ }
+
+ /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+ friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx)
+ {
+ return json_pointer(lhs) /= array_idx;
+ }
+
+ /// @brief returns the parent of this JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/
+ json_pointer parent_pointer() const
+ {
+ if (empty())
+ {
+ return *this;
+ }
+
+ json_pointer res = *this;
+ res.pop_back();
+ return res;
+ }
+
+ /// @brief remove last reference token
+ /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/
+ void pop_back()
+ {
+ if (JSON_HEDLEY_UNLIKELY(empty()))
+ {
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+ }
+
+ reference_tokens.pop_back();
+ }
+
+ /// @brief return last reference token
+ /// @sa https://json.nlohmann.me/api/json_pointer/back/
+ const string_t& back() const
+ {
+ if (JSON_HEDLEY_UNLIKELY(empty()))
+ {
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+ }
+
+ return reference_tokens.back();
+ }
+
+ /// @brief append an unescaped token at the end of the reference pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
+ void push_back(const string_t& token)
+ {
+ reference_tokens.push_back(token);
+ }
+
+ /// @brief append an unescaped token at the end of the reference pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
+ void push_back(string_t&& token)
+ {
+ reference_tokens.push_back(std::move(token));
+ }
+
+ /// @brief return whether pointer points to the root document
+ /// @sa https://json.nlohmann.me/api/json_pointer/empty/
+ bool empty() const noexcept
+ {
+ return reference_tokens.empty();
+ }
+
+ private:
+ /*!
+ @param[in] s reference token to be converted into an array index
+
+ @return integer representation of @a s
+
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index begins not with a digit
+ @throw out_of_range.404 if string @a s could not be converted to an integer
+ @throw out_of_range.410 if an array index exceeds size_type
+ */
+ template<typename BasicJsonType>
+ static typename BasicJsonType::size_type array_index(const string_t& s)
+ {
+ using size_type = typename BasicJsonType::size_type;
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
+ }
+
+ const char* p = s.c_str();
+ char* p_end = nullptr;
+ errno = 0; // strtoull doesn't reset errno
+ const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
+ if (p == p_end // invalid input or empty string
+ || errno == ERANGE // out of range
+ || JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read
+ {
+ JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
+ }
+
+ // only triggered on special platforms (like 32bit), see also
+ // https://github.com/nlohmann/json/pull/2203
+ if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int)
+ {
+ JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE
+ }
+
+ return static_cast<size_type>(res);
+ }
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ json_pointer top() const
+ {
+ if (JSON_HEDLEY_UNLIKELY(empty()))
+ {
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+ }
+
+ json_pointer result = *this;
+ result.reference_tokens = {reference_tokens[0]};
+ return result;
+ }
+
+ private:
+ /*!
+ @brief create and return a reference to the pointed to value
+
+ @complexity Linear in the number of reference tokens.
+
+ @throw parse_error.109 if array index is not a number
+ @throw type_error.313 if value cannot be unflattened
+ */
+ template<typename BasicJsonType>
+ BasicJsonType& get_and_create(BasicJsonType& j) const
+ {
+ auto* result = &j;
+
+ // in case no reference tokens exist, return a reference to the JSON value
+ // j which will be overwritten by a primitive value
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (result->type())
+ {
+ case detail::value_t::null:
+ {
+ if (reference_token == "0")
+ {
+ // start a new array if reference token is 0
+ result = &result->operator[](0);
+ }
+ else
+ {
+ // start a new object otherwise
+ result = &result->operator[](reference_token);
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ // create an entry in the object
+ result = &result->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ // create an entry in the array
+ result = &result->operator[](array_index<BasicJsonType>(reference_token));
+ break;
+ }
+
+ /*
+ The following code is only reached if there exists a reference
+ token _and_ the current value is primitive. In this case, we have
+ an error situation, because primitive values may only occur as
+ single value; that is, with an empty list of reference tokens.
+ */
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
+ }
+ }
+
+ return *result;
+ }
+
+ /*!
+ @brief return a reference to the pointed to value
+
+ @note This version does not throw if a value is not present, but tries to
+ create nested values instead. For instance, calling this function
+ with pointer `"/this/that"` on a null value is equivalent to calling
+ `operator[]("this").operator[]("that")` on that value, effectively
+ changing the null value to an object.
+
+ @param[in] ptr a JSON value
+
+ @return reference to the JSON value pointed to by the JSON pointer
+
+ @complexity Linear in the length of the JSON pointer.
+
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ template<typename BasicJsonType>
+ BasicJsonType& get_unchecked(BasicJsonType* ptr) const
+ {
+ for (const auto& reference_token : reference_tokens)
+ {
+ // convert null values to arrays or objects before continuing
+ if (ptr->is_null())
+ {
+ // check if reference token is a number
+ const bool nums =
+ std::all_of(reference_token.begin(), reference_token.end(),
+ [](const unsigned char x)
+ {
+ return std::isdigit(x);
+ });
+
+ // change value to array for numbers or "-" or to object otherwise
+ *ptr = (nums || reference_token == "-")
+ ? detail::value_t::array
+ : detail::value_t::object;
+ }
+
+ switch (ptr->type())
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (reference_token == "-")
+ {
+ // explicitly treat "-" as index beyond the end
+ ptr = &ptr->operator[](ptr->m_value.array->size());
+ }
+ else
+ {
+ // convert array index to number; unchecked access
+ ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
+ }
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+ }
+ }
+
+ return *ptr;
+ }
+
+ /*!
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ template<typename BasicJsonType>
+ BasicJsonType& get_checked(BasicJsonType* ptr) const
+ {
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->type())
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402, detail::concat(
+ "array index '-' (", std::to_string(ptr->m_value.array->size()),
+ ") is out of range"), ptr));
+ }
+
+ // note: at performs range check
+ ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+ }
+ }
+
+ return *ptr;
+ }
+
+ /*!
+ @brief return a const reference to the pointed to value
+
+ @param[in] ptr a JSON value
+
+ @return const reference to the JSON value pointed to by the JSON
+ pointer
+
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ template<typename BasicJsonType>
+ const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
+ {
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->type())
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+ {
+ // "-" cannot be used for const access
+ JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_value.array->size()), ") is out of range"), ptr));
+ }
+
+ // use unchecked array access
+ ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+ }
+ }
+
+ return *ptr;
+ }
+
+ /*!
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ template<typename BasicJsonType>
+ const BasicJsonType& get_checked(const BasicJsonType* ptr) const
+ {
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->type())
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402, detail::concat(
+ "array index '-' (", std::to_string(ptr->m_value.array->size()),
+ ") is out of range"), ptr));
+ }
+
+ // note: at performs range check
+ ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+ }
+ }
+
+ return *ptr;
+ }
+
+ /*!
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ */
+ template<typename BasicJsonType>
+ bool contains(const BasicJsonType* ptr) const
+ {
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->type())
+ {
+ case detail::value_t::object:
+ {
+ if (!ptr->contains(reference_token))
+ {
+ // we did not find the key in the object
+ return false;
+ }
+
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
+ {
+ // invalid char
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))
+ {
+ if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))
+ {
+ // first char should be between '1' and '9'
+ return false;
+ }
+ for (std::size_t i = 1; i < reference_token.size(); i++)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))
+ {
+ // other char should be between '0' and '9'
+ return false;
+ }
+ }
+ }
+
+ const auto idx = array_index<BasicJsonType>(reference_token);
+ if (idx >= ptr->size())
+ {
+ // index out of range
+ return false;
+ }
+
+ ptr = &ptr->operator[](idx);
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ {
+ // we do not expect primitive values if there is still a
+ // reference token to process
+ return false;
+ }
+ }
+ }
+
+ // no reference token left means we found a primitive value
+ return true;
+ }
+
+ /*!
+ @brief split the string input to reference tokens
+
+ @note This function is only called by the json_pointer constructor.
+ All exceptions below are documented there.
+
+ @throw parse_error.107 if the pointer is not empty or begins with '/'
+ @throw parse_error.108 if character '~' is not followed by '0' or '1'
+ */
+ static std::vector<string_t> split(const string_t& reference_string)
+ {
+ std::vector<string_t> result;
+
+ // special case: empty reference string -> no reference tokens
+ if (reference_string.empty())
+ {
+ return result;
+ }
+
+ // check if nonempty reference string begins with slash
+ if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
+ {
+ JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
+ }
+
+ // extract the reference tokens:
+ // - slash: position of the last read slash (or end of string)
+ // - start: position after the previous slash
+ for (
+ // search for the first slash after the first character
+ std::size_t slash = reference_string.find_first_of('/', 1),
+ // set the beginning of the first reference token
+ start = 1;
+ // we can stop if start == 0 (if slash == string_t::npos)
+ start != 0;
+ // set the beginning of the next reference token
+ // (will eventually be 0 if slash == string_t::npos)
+ start = (slash == string_t::npos) ? 0 : slash + 1,
+ // find next slash
+ slash = reference_string.find_first_of('/', start))
+ {
+ // use the text between the beginning of the reference token
+ // (start) and the last slash (slash).
+ auto reference_token = reference_string.substr(start, slash - start);
+
+ // check reference tokens are properly escaped
+ for (std::size_t pos = reference_token.find_first_of('~');
+ pos != string_t::npos;
+ pos = reference_token.find_first_of('~', pos + 1))
+ {
+ JSON_ASSERT(reference_token[pos] == '~');
+
+ // ~ must be followed by 0 or 1
+ if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
+ (reference_token[pos + 1] != '0' &&
+ reference_token[pos + 1] != '1')))
+ {
+ JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
+ }
+ }
+
+ // finally, store the reference token
+ detail::unescape(reference_token);
+ result.push_back(reference_token);
+ }
+
+ return result;
+ }
+
+ private:
+ /*!
+ @param[in] reference_string the reference string to the current value
+ @param[in] value the value to consider
+ @param[in,out] result the result object to insert values to
+
+ @note Empty objects or arrays are flattened to `null`.
+ */
+ template<typename BasicJsonType>
+ static void flatten(const string_t& reference_string,
+ const BasicJsonType& value,
+ BasicJsonType& result)
+ {
+ switch (value.type())
+ {
+ case detail::value_t::array:
+ {
+ if (value.m_value.array->empty())
+ {
+ // flatten empty array as null
+ result[reference_string] = nullptr;
+ }
+ else
+ {
+ // iterate array and use index as reference string
+ for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
+ {
+ flatten(detail::concat(reference_string, '/', std::to_string(i)),
+ value.m_value.array->operator[](i), result);
+ }
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ if (value.m_value.object->empty())
+ {
+ // flatten empty object as null
+ result[reference_string] = nullptr;
+ }
+ else
+ {
+ // iterate object and use keys as reference string
+ for (const auto& element : *value.m_value.object)
+ {
+ flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result);
+ }
+ }
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ {
+ // add primitive value with its reference string
+ result[reference_string] = value;
+ break;
+ }
+ }
+ }
+
+ /*!
+ @param[in] value flattened JSON
+
+ @return unflattened JSON
+
+ @throw parse_error.109 if array index is not a number
+ @throw type_error.314 if value is not an object
+ @throw type_error.315 if object values are not primitive
+ @throw type_error.313 if value cannot be unflattened
+ */
+ template<typename BasicJsonType>
+ static BasicJsonType
+ unflatten(const BasicJsonType& value)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
+ {
+ JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value));
+ }
+
+ BasicJsonType result;
+
+ // iterate the JSON object values
+ for (const auto& element : *value.m_value.object)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
+ {
+ JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second));
+ }
+
+ // assign value to reference pointed to by JSON pointer; Note that if
+ // the JSON pointer is "" (i.e., points to the whole value), function
+ // get_and_create returns a reference to result itself. An assignment
+ // will then create a primitive value.
+ json_pointer(element.first).get_and_create(result) = element.second;
+ }
+
+ return result;
+ }
+
+ // can't use conversion operator because of ambiguity
+ json_pointer<string_t> convert() const&
+ {
+ json_pointer<string_t> result;
+ result.reference_tokens = reference_tokens;
+ return result;
+ }
+
+ json_pointer<string_t> convert()&&
+ {
+ json_pointer<string_t> result;
+ result.reference_tokens = std::move(reference_tokens);
+ return result;
+ }
+
+ public:
+#if JSON_HAS_THREE_WAY_COMPARISON
+ /// @brief compares two JSON pointers for equality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+ template<typename RefStringTypeRhs>
+ bool operator==(const json_pointer<RefStringTypeRhs>& rhs) const noexcept
+ {
+ return reference_tokens == rhs.reference_tokens;
+ }
+
+ /// @brief compares JSON pointer and string for equality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer))
+ bool operator==(const string_t& rhs) const
+ {
+ return *this == json_pointer(rhs);
+ }
+
+ /// @brief 3-way compares two JSON pointers
+ template<typename RefStringTypeRhs>
+ std::strong_ordering operator<=>(const json_pointer<RefStringTypeRhs>& rhs) const noexcept // *NOPAD*
+ {
+ return reference_tokens <=> rhs.reference_tokens; // *NOPAD*
+ }
+#else
+ /// @brief compares two JSON pointers for equality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+ template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+
+ /// @brief compares JSON pointer and string for equality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+ template<typename RefStringTypeLhs, typename StringType>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+ const StringType& rhs);
+
+ /// @brief compares string and JSON pointer for equality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+ template<typename RefStringTypeRhs, typename StringType>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator==(const StringType& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs);
+
+ /// @brief compares two JSON pointers for inequality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+ template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+
+ /// @brief compares JSON pointer and string for inequality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+ template<typename RefStringTypeLhs, typename StringType>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+ const StringType& rhs);
+
+ /// @brief compares string and JSON pointer for inequality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+ template<typename RefStringTypeRhs, typename StringType>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator!=(const StringType& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs);
+
+ /// @brief compares two JSON pointer for less-than
+ template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+#endif
+
+ private:
+ /// the reference tokens
+ std::vector<string_t> reference_tokens;
+};
+
+#if !JSON_HAS_THREE_WAY_COMPARISON
+// functions cannot be defined inside class due to ODR violations
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+ return lhs.reference_tokens == rhs.reference_tokens;
+}
+
+template<typename RefStringTypeLhs,
+ typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
+inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+ const StringType& rhs)
+{
+ return lhs == json_pointer<RefStringTypeLhs>(rhs);
+}
+
+template<typename RefStringTypeRhs,
+ typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
+inline bool operator==(const StringType& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs)
+{
+ return json_pointer<RefStringTypeRhs>(lhs) == rhs;
+}
+
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+template<typename RefStringTypeLhs,
+ typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
+inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+ const StringType& rhs)
+{
+ return !(lhs == rhs);
+}
+
+template<typename RefStringTypeRhs,
+ typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
+inline bool operator!=(const StringType& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+ return lhs.reference_tokens < rhs.reference_tokens;
+}
+#endif
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/json_ref.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <initializer_list>
+#include <utility>
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename BasicJsonType>
+class json_ref
+{
+ public:
+ using value_type = BasicJsonType;
+
+ json_ref(value_type&& value)
+ : owned_value(std::move(value))
+ {}
+
+ json_ref(const value_type& value)
+ : value_ref(&value)
+ {}
+
+ json_ref(std::initializer_list<json_ref> init)
+ : owned_value(init)
+ {}
+
+ template <
+ class... Args,
+ enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >
+ json_ref(Args && ... args)
+ : owned_value(std::forward<Args>(args)...)
+ {}
+
+ // class should be movable only
+ json_ref(json_ref&&) noexcept = default;
+ json_ref(const json_ref&) = delete;
+ json_ref& operator=(const json_ref&) = delete;
+ json_ref& operator=(json_ref&&) = delete;
+ ~json_ref() = default;
+
+ value_type moved_or_copied() const
+ {
+ if (value_ref == nullptr)
+ {
+ return std::move(owned_value);
+ }
+ return *value_ref;
+ }
+
+ value_type const& operator*() const
+ {
+ return value_ref ? *value_ref : owned_value;
+ }
+
+ value_type const* operator->() const
+ {
+ return &** this;
+ }
+
+ private:
+ mutable value_type owned_value = nullptr;
+ value_type const* value_ref = nullptr;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/string_escape.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/output/binary_writer.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // reverse
+#include <array> // array
+#include <map> // map
+#include <cmath> // isnan, isinf
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstring> // memcpy
+#include <limits> // numeric_limits
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/input/binary_reader.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // copy
+#include <cstddef> // size_t
+#include <iterator> // back_inserter
+#include <memory> // shared_ptr, make_shared
+#include <string> // basic_string
+#include <vector> // vector
+
+#ifndef JSON_NO_IO
+ #include <ios> // streamsize
+ #include <ostream> // basic_ostream
+#endif // JSON_NO_IO
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// abstract output adapter interface
+template<typename CharType> struct output_adapter_protocol
+{
+ virtual void write_character(CharType c) = 0;
+ virtual void write_characters(const CharType* s, std::size_t length) = 0;
+ virtual ~output_adapter_protocol() = default;
+
+ output_adapter_protocol() = default;
+ output_adapter_protocol(const output_adapter_protocol&) = default;
+ output_adapter_protocol(output_adapter_protocol&&) noexcept = default;
+ output_adapter_protocol& operator=(const output_adapter_protocol&) = default;
+ output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default;
+};
+
+/// a type to simplify interfaces
+template<typename CharType>
+using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
+
+/// output adapter for byte vectors
+template<typename CharType, typename AllocatorType = std::allocator<CharType>>
+class output_vector_adapter : public output_adapter_protocol<CharType>
+{
+ public:
+ explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept
+ : v(vec)
+ {}
+
+ void write_character(CharType c) override
+ {
+ v.push_back(c);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ v.insert(v.end(), s, s + length);
+ }
+
+ private:
+ std::vector<CharType, AllocatorType>& v;
+};
+
+#ifndef JSON_NO_IO
+/// output adapter for output streams
+template<typename CharType>
+class output_stream_adapter : public output_adapter_protocol<CharType>
+{
+ public:
+ explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept
+ : stream(s)
+ {}
+
+ void write_character(CharType c) override
+ {
+ stream.put(c);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ stream.write(s, static_cast<std::streamsize>(length));
+ }
+
+ private:
+ std::basic_ostream<CharType>& stream;
+};
+#endif // JSON_NO_IO
+
+/// output adapter for basic_string
+template<typename CharType, typename StringType = std::basic_string<CharType>>
+class output_string_adapter : public output_adapter_protocol<CharType>
+{
+ public:
+ explicit output_string_adapter(StringType& s) noexcept
+ : str(s)
+ {}
+
+ void write_character(CharType c) override
+ {
+ str.push_back(c);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ str.append(s, length);
+ }
+
+ private:
+ StringType& str;
+};
+
+template<typename CharType, typename StringType = std::basic_string<CharType>>
+class output_adapter
+{
+ public:
+ template<typename AllocatorType = std::allocator<CharType>>
+ output_adapter(std::vector<CharType, AllocatorType>& vec)
+ : oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}
+
+#ifndef JSON_NO_IO
+ output_adapter(std::basic_ostream<CharType>& s)
+ : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
+#endif // JSON_NO_IO
+
+ output_adapter(StringType& s)
+ : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
+
+ operator output_adapter_t<CharType>()
+ {
+ return oa;
+ }
+
+ private:
+ output_adapter_t<CharType> oa = nullptr;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////
+// binary writer //
+///////////////////
+
+/*!
+@brief serialization to CBOR and MessagePack values
+*/
+template<typename BasicJsonType, typename CharType>
+class binary_writer
+{
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+
+ public:
+ /*!
+ @brief create a binary writer
+
+ @param[in] adapter output adapter to write to
+ */
+ explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))
+ {
+ JSON_ASSERT(oa);
+ }
+
+ /*!
+ @param[in] j JSON value to serialize
+ @pre j.type() == value_t::object
+ */
+ void write_bson(const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::object:
+ {
+ write_bson_object(*j.m_value.object);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::array:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j));
+ }
+ }
+ }
+
+ /*!
+ @param[in] j JSON value to serialize
+ */
+ void write_cbor(const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::null:
+ {
+ oa->write_character(to_char_type(0xF6));
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ oa->write_character(j.m_value.boolean
+ ? to_char_type(0xF5)
+ : to_char_type(0xF4));
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_value.number_integer >= 0)
+ {
+ // CBOR does not differentiate between positive signed
+ // integers and unsigned integers. Therefore, we used the
+ // code from the value_t::number_unsigned case here.
+ if (j.m_value.number_integer <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x18));
+ write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x19));
+ write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x1A));
+ write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
+ }
+ else
+ {
+ oa->write_character(to_char_type(0x1B));
+ write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
+ }
+ }
+ else
+ {
+ // The conversions below encode the sign in the first
+ // byte, and the value is converted to a positive number.
+ const auto positive_number = -1 - j.m_value.number_integer;
+ if (j.m_value.number_integer >= -24)
+ {
+ write_number(static_cast<std::uint8_t>(0x20 + positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x38));
+ write_number(static_cast<std::uint8_t>(positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x39));
+ write_number(static_cast<std::uint16_t>(positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x3A));
+ write_number(static_cast<std::uint32_t>(positive_number));
+ }
+ else
+ {
+ oa->write_character(to_char_type(0x3B));
+ write_number(static_cast<std::uint64_t>(positive_number));
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x18));
+ write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x19));
+ write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x1A));
+ write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned));
+ }
+ else
+ {
+ oa->write_character(to_char_type(0x1B));
+ write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned));
+ }
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ if (std::isnan(j.m_value.number_float))
+ {
+ // NaN is 0xf97e00 in CBOR
+ oa->write_character(to_char_type(0xF9));
+ oa->write_character(to_char_type(0x7E));
+ oa->write_character(to_char_type(0x00));
+ }
+ else if (std::isinf(j.m_value.number_float))
+ {
+ // Infinity is 0xf97c00, -Infinity is 0xf9fc00
+ oa->write_character(to_char_type(0xf9));
+ oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));
+ oa->write_character(to_char_type(0x00));
+ }
+ else
+ {
+ write_compact_float(j.m_value.number_float, detail::input_format_t::cbor);
+ }
+ break;
+ }
+
+ case value_t::string:
+ {
+ // step 1: write control byte and the string length
+ const auto N = j.m_value.string->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(0x60 + N));
+ }
+ else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x78));
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x79));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x7A));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ oa->write_character(to_char_type(0x7B));
+ write_number(static_cast<std::uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write the string
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+ j.m_value.string->size());
+ break;
+ }
+
+ case value_t::array:
+ {
+ // step 1: write control byte and the array size
+ const auto N = j.m_value.array->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(0x80 + N));
+ }
+ else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x98));
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x99));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x9A));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ oa->write_character(to_char_type(0x9B));
+ write_number(static_cast<std::uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.array)
+ {
+ write_cbor(el);
+ }
+ break;
+ }
+
+ case value_t::binary:
+ {
+ if (j.m_value.binary->has_subtype())
+ {
+ if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ write_number(static_cast<std::uint8_t>(0xd8));
+ write_number(static_cast<std::uint8_t>(j.m_value.binary->subtype()));
+ }
+ else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ write_number(static_cast<std::uint8_t>(0xd9));
+ write_number(static_cast<std::uint16_t>(j.m_value.binary->subtype()));
+ }
+ else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ write_number(static_cast<std::uint8_t>(0xda));
+ write_number(static_cast<std::uint32_t>(j.m_value.binary->subtype()));
+ }
+ else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ write_number(static_cast<std::uint8_t>(0xdb));
+ write_number(static_cast<std::uint64_t>(j.m_value.binary->subtype()));
+ }
+ }
+
+ // step 1: write control byte and the binary array size
+ const auto N = j.m_value.binary->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(0x40 + N));
+ }
+ else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x58));
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x59));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x5A));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ oa->write_character(to_char_type(0x5B));
+ write_number(static_cast<std::uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_value.binary->data()),
+ N);
+
+ break;
+ }
+
+ case value_t::object:
+ {
+ // step 1: write control byte and the object size
+ const auto N = j.m_value.object->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(0xA0 + N));
+ }
+ else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0xB8));
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0xB9));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0xBA));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ oa->write_character(to_char_type(0xBB));
+ write_number(static_cast<std::uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.object)
+ {
+ write_cbor(el.first);
+ write_cbor(el.second);
+ }
+ break;
+ }
+
+ case value_t::discarded:
+ default:
+ break;
+ }
+ }
+
+ /*!
+ @param[in] j JSON value to serialize
+ */
+ void write_msgpack(const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::null: // nil
+ {
+ oa->write_character(to_char_type(0xC0));
+ break;
+ }
+
+ case value_t::boolean: // true and false
+ {
+ oa->write_character(j.m_value.boolean
+ ? to_char_type(0xC3)
+ : to_char_type(0xC2));
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_value.number_integer >= 0)
+ {
+ // MessagePack does not differentiate between positive
+ // signed integers and unsigned integers. Therefore, we used
+ // the code from the value_t::number_unsigned case here.
+ if (j.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ // uint 8
+ oa->write_character(to_char_type(0xCC));
+ write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ // uint 16
+ oa->write_character(to_char_type(0xCD));
+ write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ // uint 32
+ oa->write_character(to_char_type(0xCE));
+ write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ // uint 64
+ oa->write_character(to_char_type(0xCF));
+ write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
+ }
+ }
+ else
+ {
+ if (j.m_value.number_integer >= -32)
+ {
+ // negative fixnum
+ write_number(static_cast<std::int8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
+ j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
+ {
+ // int 8
+ oa->write_character(to_char_type(0xD0));
+ write_number(static_cast<std::int8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
+ j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
+ {
+ // int 16
+ oa->write_character(to_char_type(0xD1));
+ write_number(static_cast<std::int16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
+ j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
+ {
+ // int 32
+ oa->write_character(to_char_type(0xD2));
+ write_number(static_cast<std::int32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&
+ j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
+ {
+ // int 64
+ oa->write_character(to_char_type(0xD3));
+ write_number(static_cast<std::int64_t>(j.m_value.number_integer));
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ // uint 8
+ oa->write_character(to_char_type(0xCC));
+ write_number(static_cast<std::uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ // uint 16
+ oa->write_character(to_char_type(0xCD));
+ write_number(static_cast<std::uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ // uint 32
+ oa->write_character(to_char_type(0xCE));
+ write_number(static_cast<std::uint32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ // uint 64
+ oa->write_character(to_char_type(0xCF));
+ write_number(static_cast<std::uint64_t>(j.m_value.number_integer));
+ }
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack);
+ break;
+ }
+
+ case value_t::string:
+ {
+ // step 1: write control byte and the string length
+ const auto N = j.m_value.string->size();
+ if (N <= 31)
+ {
+ // fixstr
+ write_number(static_cast<std::uint8_t>(0xA0 | N));
+ }
+ else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ // str 8
+ oa->write_character(to_char_type(0xD9));
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ // str 16
+ oa->write_character(to_char_type(0xDA));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ // str 32
+ oa->write_character(to_char_type(0xDB));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+
+ // step 2: write the string
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+ j.m_value.string->size());
+ break;
+ }
+
+ case value_t::array:
+ {
+ // step 1: write control byte and the array size
+ const auto N = j.m_value.array->size();
+ if (N <= 15)
+ {
+ // fixarray
+ write_number(static_cast<std::uint8_t>(0x90 | N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ // array 16
+ oa->write_character(to_char_type(0xDC));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ // array 32
+ oa->write_character(to_char_type(0xDD));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.array)
+ {
+ write_msgpack(el);
+ }
+ break;
+ }
+
+ case value_t::binary:
+ {
+ // step 0: determine if the binary type has a set subtype to
+ // determine whether or not to use the ext or fixext types
+ const bool use_ext = j.m_value.binary->has_subtype();
+
+ // step 1: write control byte and the byte string length
+ const auto N = j.m_value.binary->size();
+ if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ std::uint8_t output_type{};
+ bool fixed = true;
+ if (use_ext)
+ {
+ switch (N)
+ {
+ case 1:
+ output_type = 0xD4; // fixext 1
+ break;
+ case 2:
+ output_type = 0xD5; // fixext 2
+ break;
+ case 4:
+ output_type = 0xD6; // fixext 4
+ break;
+ case 8:
+ output_type = 0xD7; // fixext 8
+ break;
+ case 16:
+ output_type = 0xD8; // fixext 16
+ break;
+ default:
+ output_type = 0xC7; // ext 8
+ fixed = false;
+ break;
+ }
+
+ }
+ else
+ {
+ output_type = 0xC4; // bin 8
+ fixed = false;
+ }
+
+ oa->write_character(to_char_type(output_type));
+ if (!fixed)
+ {
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ const std::uint8_t output_type = use_ext
+ ? 0xC8 // ext 16
+ : 0xC5; // bin 16
+
+ oa->write_character(to_char_type(output_type));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ const std::uint8_t output_type = use_ext
+ ? 0xC9 // ext 32
+ : 0xC6; // bin 32
+
+ oa->write_character(to_char_type(output_type));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+
+ // step 1.5: if this is an ext type, write the subtype
+ if (use_ext)
+ {
+ write_number(static_cast<std::int8_t>(j.m_value.binary->subtype()));
+ }
+
+ // step 2: write the byte string
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_value.binary->data()),
+ N);
+
+ break;
+ }
+
+ case value_t::object:
+ {
+ // step 1: write control byte and the object size
+ const auto N = j.m_value.object->size();
+ if (N <= 15)
+ {
+ // fixmap
+ write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ // map 16
+ oa->write_character(to_char_type(0xDE));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ // map 32
+ oa->write_character(to_char_type(0xDF));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.object)
+ {
+ write_msgpack(el.first);
+ write_msgpack(el.second);
+ }
+ break;
+ }
+
+ case value_t::discarded:
+ default:
+ break;
+ }
+ }
+
+ /*!
+ @param[in] j JSON value to serialize
+ @param[in] use_count whether to use '#' prefixes (optimized format)
+ @param[in] use_type whether to use '$' prefixes (optimized format)
+ @param[in] add_prefix whether prefixes need to be used for this value
+ @param[in] use_bjdata whether write in BJData format, default is false
+ */
+ void write_ubjson(const BasicJsonType& j, const bool use_count,
+ const bool use_type, const bool add_prefix = true,
+ const bool use_bjdata = false)
+ {
+ switch (j.type())
+ {
+ case value_t::null:
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('Z'));
+ }
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ if (add_prefix)
+ {
+ oa->write_character(j.m_value.boolean
+ ? to_char_type('T')
+ : to_char_type('F'));
+ }
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix, use_bjdata);
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix, use_bjdata);
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix, use_bjdata);
+ break;
+ }
+
+ case value_t::string:
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('S'));
+ }
+ write_number_with_ubjson_prefix(j.m_value.string->size(), true, use_bjdata);
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+ j.m_value.string->size());
+ break;
+ }
+
+ case value_t::array:
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('['));
+ }
+
+ bool prefix_required = true;
+ if (use_type && !j.m_value.array->empty())
+ {
+ JSON_ASSERT(use_count);
+ const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
+ const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
+ [this, first_prefix, use_bjdata](const BasicJsonType & v)
+ {
+ return ubjson_prefix(v, use_bjdata) == first_prefix;
+ });
+
+ std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
+
+ if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
+ {
+ prefix_required = false;
+ oa->write_character(to_char_type('$'));
+ oa->write_character(first_prefix);
+ }
+ }
+
+ if (use_count)
+ {
+ oa->write_character(to_char_type('#'));
+ write_number_with_ubjson_prefix(j.m_value.array->size(), true, use_bjdata);
+ }
+
+ for (const auto& el : *j.m_value.array)
+ {
+ write_ubjson(el, use_count, use_type, prefix_required, use_bjdata);
+ }
+
+ if (!use_count)
+ {
+ oa->write_character(to_char_type(']'));
+ }
+
+ break;
+ }
+
+ case value_t::binary:
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('['));
+ }
+
+ if (use_type && !j.m_value.binary->empty())
+ {
+ JSON_ASSERT(use_count);
+ oa->write_character(to_char_type('$'));
+ oa->write_character('U');
+ }
+
+ if (use_count)
+ {
+ oa->write_character(to_char_type('#'));
+ write_number_with_ubjson_prefix(j.m_value.binary->size(), true, use_bjdata);
+ }
+
+ if (use_type)
+ {
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_value.binary->data()),
+ j.m_value.binary->size());
+ }
+ else
+ {
+ for (size_t i = 0; i < j.m_value.binary->size(); ++i)
+ {
+ oa->write_character(to_char_type('U'));
+ oa->write_character(j.m_value.binary->data()[i]);
+ }
+ }
+
+ if (!use_count)
+ {
+ oa->write_character(to_char_type(']'));
+ }
+
+ break;
+ }
+
+ case value_t::object:
+ {
+ if (use_bjdata && j.m_value.object->size() == 3 && j.m_value.object->find("_ArrayType_") != j.m_value.object->end() && j.m_value.object->find("_ArraySize_") != j.m_value.object->end() && j.m_value.object->find("_ArrayData_") != j.m_value.object->end())
+ {
+ if (!write_bjdata_ndarray(*j.m_value.object, use_count, use_type)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata)
+ {
+ break;
+ }
+ }
+
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('{'));
+ }
+
+ bool prefix_required = true;
+ if (use_type && !j.m_value.object->empty())
+ {
+ JSON_ASSERT(use_count);
+ const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
+ const bool same_prefix = std::all_of(j.begin(), j.end(),
+ [this, first_prefix, use_bjdata](const BasicJsonType & v)
+ {
+ return ubjson_prefix(v, use_bjdata) == first_prefix;
+ });
+
+ std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
+
+ if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
+ {
+ prefix_required = false;
+ oa->write_character(to_char_type('$'));
+ oa->write_character(first_prefix);
+ }
+ }
+
+ if (use_count)
+ {
+ oa->write_character(to_char_type('#'));
+ write_number_with_ubjson_prefix(j.m_value.object->size(), true, use_bjdata);
+ }
+
+ for (const auto& el : *j.m_value.object)
+ {
+ write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(el.first.c_str()),
+ el.first.size());
+ write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata);
+ }
+
+ if (!use_count)
+ {
+ oa->write_character(to_char_type('}'));
+ }
+
+ break;
+ }
+
+ case value_t::discarded:
+ default:
+ break;
+ }
+ }
+
+ private:
+ //////////
+ // BSON //
+ //////////
+
+ /*!
+ @return The size of a BSON document entry header, including the id marker
+ and the entry name size (and its null-terminator).
+ */
+ static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
+ {
+ const auto it = name.find(static_cast<typename string_t::value_type>(0));
+ if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
+ {
+ JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
+ static_cast<void>(j);
+ }
+
+ return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
+ }
+
+ /*!
+ @brief Writes the given @a element_type and @a name to the output adapter
+ */
+ void write_bson_entry_header(const string_t& name,
+ const std::uint8_t element_type)
+ {
+ oa->write_character(to_char_type(element_type)); // boolean
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(name.c_str()),
+ name.size() + 1u);
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and boolean value @a value
+ */
+ void write_bson_boolean(const string_t& name,
+ const bool value)
+ {
+ write_bson_entry_header(name, 0x08);
+ oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and double value @a value
+ */
+ void write_bson_double(const string_t& name,
+ const double value)
+ {
+ write_bson_entry_header(name, 0x01);
+ write_number<double>(value, true);
+ }
+
+ /*!
+ @return The size of the BSON-encoded string in @a value
+ */
+ static std::size_t calc_bson_string_size(const string_t& value)
+ {
+ return sizeof(std::int32_t) + value.size() + 1ul;
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and string value @a value
+ */
+ void write_bson_string(const string_t& name,
+ const string_t& value)
+ {
+ write_bson_entry_header(name, 0x02);
+
+ write_number<std::int32_t>(static_cast<std::int32_t>(value.size() + 1ul), true);
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(value.c_str()),
+ value.size() + 1);
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and null value
+ */
+ void write_bson_null(const string_t& name)
+ {
+ write_bson_entry_header(name, 0x0A);
+ }
+
+ /*!
+ @return The size of the BSON-encoded integer @a value
+ */
+ static std::size_t calc_bson_integer_size(const std::int64_t value)
+ {
+ return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()
+ ? sizeof(std::int32_t)
+ : sizeof(std::int64_t);
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and integer @a value
+ */
+ void write_bson_integer(const string_t& name,
+ const std::int64_t value)
+ {
+ if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())
+ {
+ write_bson_entry_header(name, 0x10); // int32
+ write_number<std::int32_t>(static_cast<std::int32_t>(value), true);
+ }
+ else
+ {
+ write_bson_entry_header(name, 0x12); // int64
+ write_number<std::int64_t>(static_cast<std::int64_t>(value), true);
+ }
+ }
+
+ /*!
+ @return The size of the BSON-encoded unsigned integer in @a j
+ */
+ static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept
+ {
+ return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+ ? sizeof(std::int32_t)
+ : sizeof(std::int64_t);
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and unsigned @a value
+ */
+ void write_bson_unsigned(const string_t& name,
+ const BasicJsonType& j)
+ {
+ if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+ {
+ write_bson_entry_header(name, 0x10 /* int32 */);
+ write_number<std::int32_t>(static_cast<std::int32_t>(j.m_value.number_unsigned), true);
+ }
+ else if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+ {
+ write_bson_entry_header(name, 0x12 /* int64 */);
+ write_number<std::int64_t>(static_cast<std::int64_t>(j.m_value.number_unsigned), true);
+ }
+ else
+ {
+ JSON_THROW(out_of_range::create(407, concat("integer number ", std::to_string(j.m_value.number_unsigned), " cannot be represented by BSON as it does not fit int64"), &j));
+ }
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and object @a value
+ */
+ void write_bson_object_entry(const string_t& name,
+ const typename BasicJsonType::object_t& value)
+ {
+ write_bson_entry_header(name, 0x03); // object
+ write_bson_object(value);
+ }
+
+ /*!
+ @return The size of the BSON-encoded array @a value
+ */
+ static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)
+ {
+ std::size_t array_index = 0ul;
+
+ const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast<std::size_t>(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)
+ {
+ return result + calc_bson_element_size(std::to_string(array_index++), el);
+ });
+
+ return sizeof(std::int32_t) + embedded_document_size + 1ul;
+ }
+
+ /*!
+ @return The size of the BSON-encoded binary array @a value
+ */
+ static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value)
+ {
+ return sizeof(std::int32_t) + value.size() + 1ul;
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and array @a value
+ */
+ void write_bson_array(const string_t& name,
+ const typename BasicJsonType::array_t& value)
+ {
+ write_bson_entry_header(name, 0x04); // array
+ write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_array_size(value)), true);
+
+ std::size_t array_index = 0ul;
+
+ for (const auto& el : value)
+ {
+ write_bson_element(std::to_string(array_index++), el);
+ }
+
+ oa->write_character(to_char_type(0x00));
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and binary value @a value
+ */
+ void write_bson_binary(const string_t& name,
+ const binary_t& value)
+ {
+ write_bson_entry_header(name, 0x05);
+
+ write_number<std::int32_t>(static_cast<std::int32_t>(value.size()), true);
+ write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00));
+
+ oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
+ }
+
+ /*!
+ @brief Calculates the size necessary to serialize the JSON value @a j with its @a name
+ @return The calculated size for the BSON document entry for @a j with the given @a name.
+ */
+ static std::size_t calc_bson_element_size(const string_t& name,
+ const BasicJsonType& j)
+ {
+ const auto header_size = calc_bson_entry_header_size(name, j);
+ switch (j.type())
+ {
+ case value_t::object:
+ return header_size + calc_bson_object_size(*j.m_value.object);
+
+ case value_t::array:
+ return header_size + calc_bson_array_size(*j.m_value.array);
+
+ case value_t::binary:
+ return header_size + calc_bson_binary_size(*j.m_value.binary);
+
+ case value_t::boolean:
+ return header_size + 1ul;
+
+ case value_t::number_float:
+ return header_size + 8ul;
+
+ case value_t::number_integer:
+ return header_size + calc_bson_integer_size(j.m_value.number_integer);
+
+ case value_t::number_unsigned:
+ return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);
+
+ case value_t::string:
+ return header_size + calc_bson_string_size(*j.m_value.string);
+
+ case value_t::null:
+ return header_size + 0ul;
+
+ // LCOV_EXCL_START
+ case value_t::discarded:
+ default:
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
+ return 0ul;
+ // LCOV_EXCL_STOP
+ }
+ }
+
+ /*!
+ @brief Serializes the JSON value @a j to BSON and associates it with the
+ key @a name.
+ @param name The name to associate with the JSON entity @a j within the
+ current BSON document
+ */
+ void write_bson_element(const string_t& name,
+ const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::object:
+ return write_bson_object_entry(name, *j.m_value.object);
+
+ case value_t::array:
+ return write_bson_array(name, *j.m_value.array);
+
+ case value_t::binary:
+ return write_bson_binary(name, *j.m_value.binary);
+
+ case value_t::boolean:
+ return write_bson_boolean(name, j.m_value.boolean);
+
+ case value_t::number_float:
+ return write_bson_double(name, j.m_value.number_float);
+
+ case value_t::number_integer:
+ return write_bson_integer(name, j.m_value.number_integer);
+
+ case value_t::number_unsigned:
+ return write_bson_unsigned(name, j);
+
+ case value_t::string:
+ return write_bson_string(name, *j.m_value.string);
+
+ case value_t::null:
+ return write_bson_null(name);
+
+ // LCOV_EXCL_START
+ case value_t::discarded:
+ default:
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
+ return;
+ // LCOV_EXCL_STOP
+ }
+ }
+
+ /*!
+ @brief Calculates the size of the BSON serialization of the given
+ JSON-object @a j.
+ @param[in] value JSON value to serialize
+ @pre value.type() == value_t::object
+ */
+ static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
+ {
+ const std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast<std::size_t>(0),
+ [](size_t result, const typename BasicJsonType::object_t::value_type & el)
+ {
+ return result += calc_bson_element_size(el.first, el.second);
+ });
+
+ return sizeof(std::int32_t) + document_size + 1ul;
+ }
+
+ /*!
+ @param[in] value JSON value to serialize
+ @pre value.type() == value_t::object
+ */
+ void write_bson_object(const typename BasicJsonType::object_t& value)
+ {
+ write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_object_size(value)), true);
+
+ for (const auto& el : value)
+ {
+ write_bson_element(el.first, el.second);
+ }
+
+ oa->write_character(to_char_type(0x00));
+ }
+
+ //////////
+ // CBOR //
+ //////////
+
+ static constexpr CharType get_cbor_float_prefix(float /*unused*/)
+ {
+ return to_char_type(0xFA); // Single-Precision Float
+ }
+
+ static constexpr CharType get_cbor_float_prefix(double /*unused*/)
+ {
+ return to_char_type(0xFB); // Double-Precision Float
+ }
+
+ /////////////
+ // MsgPack //
+ /////////////
+
+ static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
+ {
+ return to_char_type(0xCA); // float 32
+ }
+
+ static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
+ {
+ return to_char_type(0xCB); // float 64
+ }
+
+ ////////////
+ // UBJSON //
+ ////////////
+
+ // UBJSON: write number (floating point)
+ template<typename NumberType, typename std::enable_if<
+ std::is_floating_point<NumberType>::value, int>::type = 0>
+ void write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix,
+ const bool use_bjdata)
+ {
+ if (add_prefix)
+ {
+ oa->write_character(get_ubjson_float_prefix(n));
+ }
+ write_number(n, use_bjdata);
+ }
+
+ // UBJSON: write number (unsigned integer)
+ template<typename NumberType, typename std::enable_if<
+ std::is_unsigned<NumberType>::value, int>::type = 0>
+ void write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix,
+ const bool use_bjdata)
+ {
+ if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('i')); // int8
+ }
+ write_number(static_cast<std::uint8_t>(n), use_bjdata);
+ }
+ else if (n <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('U')); // uint8
+ }
+ write_number(static_cast<std::uint8_t>(n), use_bjdata);
+ }
+ else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('I')); // int16
+ }
+ write_number(static_cast<std::int16_t>(n), use_bjdata);
+ }
+ else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint16_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('u')); // uint16 - bjdata only
+ }
+ write_number(static_cast<std::uint16_t>(n), use_bjdata);
+ }
+ else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('l')); // int32
+ }
+ write_number(static_cast<std::int32_t>(n), use_bjdata);
+ }
+ else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint32_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('m')); // uint32 - bjdata only
+ }
+ write_number(static_cast<std::uint32_t>(n), use_bjdata);
+ }
+ else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('L')); // int64
+ }
+ write_number(static_cast<std::int64_t>(n), use_bjdata);
+ }
+ else if (use_bjdata && n <= (std::numeric_limits<uint64_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('M')); // uint64 - bjdata only
+ }
+ write_number(static_cast<std::uint64_t>(n), use_bjdata);
+ }
+ else
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('H')); // high-precision number
+ }
+
+ const auto number = BasicJsonType(n).dump();
+ write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
+ for (std::size_t i = 0; i < number.size(); ++i)
+ {
+ oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+ }
+ }
+ }
+
+ // UBJSON: write number (signed integer)
+ template < typename NumberType, typename std::enable_if <
+ std::is_signed<NumberType>::value&&
+ !std::is_floating_point<NumberType>::value, int >::type = 0 >
+ void write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix,
+ const bool use_bjdata)
+ {
+ if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('i')); // int8
+ }
+ write_number(static_cast<std::int8_t>(n), use_bjdata);
+ }
+ else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('U')); // uint8
+ }
+ write_number(static_cast<std::uint8_t>(n), use_bjdata);
+ }
+ else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('I')); // int16
+ }
+ write_number(static_cast<std::int16_t>(n), use_bjdata);
+ }
+ else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::max)())))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('u')); // uint16 - bjdata only
+ }
+ write_number(static_cast<uint16_t>(n), use_bjdata);
+ }
+ else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('l')); // int32
+ }
+ write_number(static_cast<std::int32_t>(n), use_bjdata);
+ }
+ else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::max)())))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('m')); // uint32 - bjdata only
+ }
+ write_number(static_cast<uint32_t>(n), use_bjdata);
+ }
+ else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('L')); // int64
+ }
+ write_number(static_cast<std::int64_t>(n), use_bjdata);
+ }
+ // LCOV_EXCL_START
+ else
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('H')); // high-precision number
+ }
+
+ const auto number = BasicJsonType(n).dump();
+ write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
+ for (std::size_t i = 0; i < number.size(); ++i)
+ {
+ oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+ }
+ }
+ // LCOV_EXCL_STOP
+ }
+
+ /*!
+ @brief determine the type prefix of container values
+ */
+ CharType ubjson_prefix(const BasicJsonType& j, const bool use_bjdata) const noexcept
+ {
+ switch (j.type())
+ {
+ case value_t::null:
+ return 'Z';
+
+ case value_t::boolean:
+ return j.m_value.boolean ? 'T' : 'F';
+
+ case value_t::number_integer:
+ {
+ if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
+ {
+ return 'i';
+ }
+ if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ return 'U';
+ }
+ if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
+ {
+ return 'I';
+ }
+ if (use_bjdata && ((std::numeric_limits<std::uint16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)()))
+ {
+ return 'u';
+ }
+ if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
+ {
+ return 'l';
+ }
+ if (use_bjdata && ((std::numeric_limits<std::uint32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)()))
+ {
+ return 'm';
+ }
+ if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
+ {
+ return 'L';
+ }
+ // anything else is treated as high-precision number
+ return 'H'; // LCOV_EXCL_LINE
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
+ {
+ return 'i';
+ }
+ if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))
+ {
+ return 'U';
+ }
+ if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
+ {
+ return 'I';
+ }
+ if (use_bjdata && j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint16_t>::max)()))
+ {
+ return 'u';
+ }
+ if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+ {
+ return 'l';
+ }
+ if (use_bjdata && j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint32_t>::max)()))
+ {
+ return 'm';
+ }
+ if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+ {
+ return 'L';
+ }
+ if (use_bjdata && j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ return 'M';
+ }
+ // anything else is treated as high-precision number
+ return 'H'; // LCOV_EXCL_LINE
+ }
+
+ case value_t::number_float:
+ return get_ubjson_float_prefix(j.m_value.number_float);
+
+ case value_t::string:
+ return 'S';
+
+ case value_t::array: // fallthrough
+ case value_t::binary:
+ return '[';
+
+ case value_t::object:
+ return '{';
+
+ case value_t::discarded:
+ default: // discarded values
+ return 'N';
+ }
+ }
+
+ static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
+ {
+ return 'd'; // float 32
+ }
+
+ static constexpr CharType get_ubjson_float_prefix(double /*unused*/)
+ {
+ return 'D'; // float 64
+ }
+
+ /*!
+ @return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid
+ */
+ bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type)
+ {
+ std::map<string_t, CharType> bjdtype = {{"uint8", 'U'}, {"int8", 'i'}, {"uint16", 'u'}, {"int16", 'I'},
+ {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'}, {"char", 'C'}
+ };
+
+ string_t key = "_ArrayType_";
+ auto it = bjdtype.find(static_cast<string_t>(value.at(key)));
+ if (it == bjdtype.end())
+ {
+ return true;
+ }
+ CharType dtype = it->second;
+
+ key = "_ArraySize_";
+ std::size_t len = (value.at(key).empty() ? 0 : 1);
+ for (const auto& el : value.at(key))
+ {
+ len *= static_cast<std::size_t>(el.m_value.number_unsigned);
+ }
+
+ key = "_ArrayData_";
+ if (value.at(key).size() != len)
+ {
+ return true;
+ }
+
+ oa->write_character('[');
+ oa->write_character('$');
+ oa->write_character(dtype);
+ oa->write_character('#');
+
+ key = "_ArraySize_";
+ write_ubjson(value.at(key), use_count, use_type, true, true);
+
+ key = "_ArrayData_";
+ if (dtype == 'U' || dtype == 'C')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::uint8_t>(el.m_value.number_unsigned), true);
+ }
+ }
+ else if (dtype == 'i')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::int8_t>(el.m_value.number_integer), true);
+ }
+ }
+ else if (dtype == 'u')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::uint16_t>(el.m_value.number_unsigned), true);
+ }
+ }
+ else if (dtype == 'I')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::int16_t>(el.m_value.number_integer), true);
+ }
+ }
+ else if (dtype == 'm')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::uint32_t>(el.m_value.number_unsigned), true);
+ }
+ }
+ else if (dtype == 'l')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::int32_t>(el.m_value.number_integer), true);
+ }
+ }
+ else if (dtype == 'M')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::uint64_t>(el.m_value.number_unsigned), true);
+ }
+ }
+ else if (dtype == 'L')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::int64_t>(el.m_value.number_integer), true);
+ }
+ }
+ else if (dtype == 'd')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<float>(el.m_value.number_float), true);
+ }
+ }
+ else if (dtype == 'D')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<double>(el.m_value.number_float), true);
+ }
+ }
+ return false;
+ }
+
+ ///////////////////////
+ // Utility functions //
+ ///////////////////////
+
+ /*
+ @brief write a number to output input
+ @param[in] n number of type @a NumberType
+ @param[in] OutputIsLittleEndian Set to true if output data is
+ required to be little endian
+ @tparam NumberType the type of the number
+
+ @note This function needs to respect the system's endianness, because bytes
+ in CBOR, MessagePack, and UBJSON are stored in network order (big
+ endian) and therefore need reordering on little endian systems.
+ On the other hand, BSON and BJData use little endian and should reorder
+ on big endian systems.
+ */
+ template<typename NumberType>
+ void write_number(const NumberType n, const bool OutputIsLittleEndian = false)
+ {
+ // step 1: write number to array of length NumberType
+ std::array<CharType, sizeof(NumberType)> vec{};
+ std::memcpy(vec.data(), &n, sizeof(NumberType));
+
+ // step 2: write array to output (with possible reordering)
+ if (is_little_endian != OutputIsLittleEndian)
+ {
+ // reverse byte order prior to conversion if necessary
+ std::reverse(vec.begin(), vec.end());
+ }
+
+ oa->write_characters(vec.data(), sizeof(NumberType));
+ }
+
+ void write_compact_float(const number_float_t n, detail::input_format_t format)
+ {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
+ static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
+ static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))
+ {
+ oa->write_character(format == detail::input_format_t::cbor
+ ? get_cbor_float_prefix(static_cast<float>(n))
+ : get_msgpack_float_prefix(static_cast<float>(n)));
+ write_number(static_cast<float>(n));
+ }
+ else
+ {
+ oa->write_character(format == detail::input_format_t::cbor
+ ? get_cbor_float_prefix(n)
+ : get_msgpack_float_prefix(n));
+ write_number(n);
+ }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ }
+
+ public:
+ // The following to_char_type functions are implement the conversion
+ // between uint8_t and CharType. In case CharType is not unsigned,
+ // such a conversion is required to allow values greater than 128.
+ // See <https://github.com/nlohmann/json/issues/1286> for a discussion.
+ template < typename C = CharType,
+ enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >
+ static constexpr CharType to_char_type(std::uint8_t x) noexcept
+ {
+ return *reinterpret_cast<char*>(&x);
+ }
+
+ template < typename C = CharType,
+ enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >
+ static CharType to_char_type(std::uint8_t x) noexcept
+ {
+ static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t");
+ static_assert(std::is_trivial<CharType>::value, "CharType must be trivial");
+ CharType result;
+ std::memcpy(&result, &x, sizeof(x));
+ return result;
+ }
+
+ template<typename C = CharType,
+ enable_if_t<std::is_unsigned<C>::value>* = nullptr>
+ static constexpr CharType to_char_type(std::uint8_t x) noexcept
+ {
+ return x;
+ }
+
+ template < typename InputCharType, typename C = CharType,
+ enable_if_t <
+ std::is_signed<C>::value &&
+ std::is_signed<char>::value &&
+ std::is_same<char, typename std::remove_cv<InputCharType>::type>::value
+ > * = nullptr >
+ static constexpr CharType to_char_type(InputCharType x) noexcept
+ {
+ return x;
+ }
+
+ private:
+ /// whether we can assume little endianness
+ const bool is_little_endian = little_endianness();
+
+ /// the output
+ output_adapter_t<CharType> oa = nullptr;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+
+// #include <nlohmann/detail/output/serializer.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2008-2009 Björn Hoehrmann <bjoern@hoehrmann.de>
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // reverse, remove, fill, find, none_of
+#include <array> // array
+#include <clocale> // localeconv, lconv
+#include <cmath> // labs, isfinite, isnan, signbit
+#include <cstddef> // size_t, ptrdiff_t
+#include <cstdint> // uint8_t
+#include <cstdio> // snprintf
+#include <limits> // numeric_limits
+#include <string> // string, char_traits
+#include <iomanip> // setfill, setw
+#include <type_traits> // is_same
+#include <utility> // move
+
+// #include <nlohmann/detail/conversions/to_chars.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2009 Florian Loitsch <https://florian.loitsch.com/>
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cmath> // signbit, isfinite
+#include <cstdint> // intN_t, uintN_t
+#include <cstring> // memcpy, memmove
+#include <limits> // numeric_limits
+#include <type_traits> // conditional
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief implements the Grisu2 algorithm for binary to decimal floating-point
+conversion.
+
+This implementation is a slightly modified version of the reference
+implementation which may be obtained from
+http://florian.loitsch.com/publications (bench.tar.gz).
+
+The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.
+
+For a detailed description of the algorithm see:
+
+[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with
+ Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming
+ Language Design and Implementation, PLDI 2010
+[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
+ Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language
+ Design and Implementation, PLDI 1996
+*/
+namespace dtoa_impl
+{
+
+template<typename Target, typename Source>
+Target reinterpret_bits(const Source source)
+{
+ static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
+
+ Target target;
+ std::memcpy(&target, &source, sizeof(Source));
+ return target;
+}
+
+struct diyfp // f * 2^e
+{
+ static constexpr int kPrecision = 64; // = q
+
+ std::uint64_t f = 0;
+ int e = 0;
+
+ constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}
+
+ /*!
+ @brief returns x - y
+ @pre x.e == y.e and x.f >= y.f
+ */
+ static diyfp sub(const diyfp& x, const diyfp& y) noexcept
+ {
+ JSON_ASSERT(x.e == y.e);
+ JSON_ASSERT(x.f >= y.f);
+
+ return {x.f - y.f, x.e};
+ }
+
+ /*!
+ @brief returns x * y
+ @note The result is rounded. (Only the upper q bits are returned.)
+ */
+ static diyfp mul(const diyfp& x, const diyfp& y) noexcept
+ {
+ static_assert(kPrecision == 64, "internal error");
+
+ // Computes:
+ // f = round((x.f * y.f) / 2^q)
+ // e = x.e + y.e + q
+
+ // Emulate the 64-bit * 64-bit multiplication:
+ //
+ // p = u * v
+ // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)
+ // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi )
+ // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 )
+ // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 )
+ // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3)
+ // = (p0_lo ) + 2^32 (Q ) + 2^64 (H )
+ // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H )
+ //
+ // (Since Q might be larger than 2^32 - 1)
+ //
+ // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)
+ //
+ // (Q_hi + H does not overflow a 64-bit int)
+ //
+ // = p_lo + 2^64 p_hi
+
+ const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;
+ const std::uint64_t u_hi = x.f >> 32u;
+ const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;
+ const std::uint64_t v_hi = y.f >> 32u;
+
+ const std::uint64_t p0 = u_lo * v_lo;
+ const std::uint64_t p1 = u_lo * v_hi;
+ const std::uint64_t p2 = u_hi * v_lo;
+ const std::uint64_t p3 = u_hi * v_hi;
+
+ const std::uint64_t p0_hi = p0 >> 32u;
+ const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;
+ const std::uint64_t p1_hi = p1 >> 32u;
+ const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;
+ const std::uint64_t p2_hi = p2 >> 32u;
+
+ std::uint64_t Q = p0_hi + p1_lo + p2_lo;
+
+ // The full product might now be computed as
+ //
+ // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)
+ // p_lo = p0_lo + (Q << 32)
+ //
+ // But in this particular case here, the full p_lo is not required.
+ // Effectively we only need to add the highest bit in p_lo to p_hi (and
+ // Q_hi + 1 does not overflow).
+
+ Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up
+
+ const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);
+
+ return {h, x.e + y.e + 64};
+ }
+
+ /*!
+ @brief normalize x such that the significand is >= 2^(q-1)
+ @pre x.f != 0
+ */
+ static diyfp normalize(diyfp x) noexcept
+ {
+ JSON_ASSERT(x.f != 0);
+
+ while ((x.f >> 63u) == 0)
+ {
+ x.f <<= 1u;
+ x.e--;
+ }
+
+ return x;
+ }
+
+ /*!
+ @brief normalize x such that the result has the exponent E
+ @pre e >= x.e and the upper e - x.e bits of x.f must be zero.
+ */
+ static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept
+ {
+ const int delta = x.e - target_exponent;
+
+ JSON_ASSERT(delta >= 0);
+ JSON_ASSERT(((x.f << delta) >> delta) == x.f);
+
+ return {x.f << delta, target_exponent};
+ }
+};
+
+struct boundaries
+{
+ diyfp w;
+ diyfp minus;
+ diyfp plus;
+};
+
+/*!
+Compute the (normalized) diyfp representing the input number 'value' and its
+boundaries.
+
+@pre value must be finite and positive
+*/
+template<typename FloatType>
+boundaries compute_boundaries(FloatType value)
+{
+ JSON_ASSERT(std::isfinite(value));
+ JSON_ASSERT(value > 0);
+
+ // Convert the IEEE representation into a diyfp.
+ //
+ // If v is denormal:
+ // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1))
+ // If v is normalized:
+ // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))
+
+ static_assert(std::numeric_limits<FloatType>::is_iec559,
+ "internal error: dtoa_short requires an IEEE-754 floating-point implementation");
+
+ constexpr int kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)
+ constexpr int kBias = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);
+ constexpr int kMinExp = 1 - kBias;
+ constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)
+
+ using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;
+
+ const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value));
+ const std::uint64_t E = bits >> (kPrecision - 1);
+ const std::uint64_t F = bits & (kHiddenBit - 1);
+
+ const bool is_denormal = E == 0;
+ const diyfp v = is_denormal
+ ? diyfp(F, kMinExp)
+ : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);
+
+ // Compute the boundaries m- and m+ of the floating-point value
+ // v = f * 2^e.
+ //
+ // Determine v- and v+, the floating-point predecessor and successor if v,
+ // respectively.
+ //
+ // v- = v - 2^e if f != 2^(p-1) or e == e_min (A)
+ // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B)
+ //
+ // v+ = v + 2^e
+ //
+ // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_
+ // between m- and m+ round to v, regardless of how the input rounding
+ // algorithm breaks ties.
+ //
+ // ---+-------------+-------------+-------------+-------------+--- (A)
+ // v- m- v m+ v+
+ //
+ // -----------------+------+------+-------------+-------------+--- (B)
+ // v- m- v m+ v+
+
+ const bool lower_boundary_is_closer = F == 0 && E > 1;
+ const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);
+ const diyfp m_minus = lower_boundary_is_closer
+ ? diyfp(4 * v.f - 1, v.e - 2) // (B)
+ : diyfp(2 * v.f - 1, v.e - 1); // (A)
+
+ // Determine the normalized w+ = m+.
+ const diyfp w_plus = diyfp::normalize(m_plus);
+
+ // Determine w- = m- such that e_(w-) = e_(w+).
+ const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);
+
+ return {diyfp::normalize(v), w_minus, w_plus};
+}
+
+// Given normalized diyfp w, Grisu needs to find a (normalized) cached
+// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies
+// within a certain range [alpha, gamma] (Definition 3.2 from [1])
+//
+// alpha <= e = e_c + e_w + q <= gamma
+//
+// or
+//
+// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q
+// <= f_c * f_w * 2^gamma
+//
+// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies
+//
+// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma
+//
+// or
+//
+// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma)
+//
+// The choice of (alpha,gamma) determines the size of the table and the form of
+// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well
+// in practice:
+//
+// The idea is to cut the number c * w = f * 2^e into two parts, which can be
+// processed independently: An integral part p1, and a fractional part p2:
+//
+// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e
+// = (f div 2^-e) + (f mod 2^-e) * 2^e
+// = p1 + p2 * 2^e
+//
+// The conversion of p1 into decimal form requires a series of divisions and
+// modulos by (a power of) 10. These operations are faster for 32-bit than for
+// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be
+// achieved by choosing
+//
+// -e >= 32 or e <= -32 := gamma
+//
+// In order to convert the fractional part
+//
+// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...
+//
+// into decimal form, the fraction is repeatedly multiplied by 10 and the digits
+// d[-i] are extracted in order:
+//
+// (10 * p2) div 2^-e = d[-1]
+// (10 * p2) mod 2^-e = d[-2] / 10^1 + ...
+//
+// The multiplication by 10 must not overflow. It is sufficient to choose
+//
+// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.
+//
+// Since p2 = f mod 2^-e < 2^-e,
+//
+// -e <= 60 or e >= -60 := alpha
+
+constexpr int kAlpha = -60;
+constexpr int kGamma = -32;
+
+struct cached_power // c = f * 2^e ~= 10^k
+{
+ std::uint64_t f;
+ int e;
+ int k;
+};
+
+/*!
+For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached
+power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c
+satisfies (Definition 3.2 from [1])
+
+ alpha <= e_c + e + q <= gamma.
+*/
+inline cached_power get_cached_power_for_binary_exponent(int e)
+{
+ // Now
+ //
+ // alpha <= e_c + e + q <= gamma (1)
+ // ==> f_c * 2^alpha <= c * 2^e * 2^q
+ //
+ // and since the c's are normalized, 2^(q-1) <= f_c,
+ //
+ // ==> 2^(q - 1 + alpha) <= c * 2^(e + q)
+ // ==> 2^(alpha - e - 1) <= c
+ //
+ // If c were an exact power of ten, i.e. c = 10^k, one may determine k as
+ //
+ // k = ceil( log_10( 2^(alpha - e - 1) ) )
+ // = ceil( (alpha - e - 1) * log_10(2) )
+ //
+ // From the paper:
+ // "In theory the result of the procedure could be wrong since c is rounded,
+ // and the computation itself is approximated [...]. In practice, however,
+ // this simple function is sufficient."
+ //
+ // For IEEE double precision floating-point numbers converted into
+ // normalized diyfp's w = f * 2^e, with q = 64,
+ //
+ // e >= -1022 (min IEEE exponent)
+ // -52 (p - 1)
+ // -52 (p - 1, possibly normalize denormal IEEE numbers)
+ // -11 (normalize the diyfp)
+ // = -1137
+ //
+ // and
+ //
+ // e <= +1023 (max IEEE exponent)
+ // -52 (p - 1)
+ // -11 (normalize the diyfp)
+ // = 960
+ //
+ // This binary exponent range [-1137,960] results in a decimal exponent
+ // range [-307,324]. One does not need to store a cached power for each
+ // k in this range. For each such k it suffices to find a cached power
+ // such that the exponent of the product lies in [alpha,gamma].
+ // This implies that the difference of the decimal exponents of adjacent
+ // table entries must be less than or equal to
+ //
+ // floor( (gamma - alpha) * log_10(2) ) = 8.
+ //
+ // (A smaller distance gamma-alpha would require a larger table.)
+
+ // NB:
+ // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.
+
+ constexpr int kCachedPowersMinDecExp = -300;
+ constexpr int kCachedPowersDecStep = 8;
+
+ static constexpr std::array<cached_power, 79> kCachedPowers =
+ {
+ {
+ { 0xAB70FE17C79AC6CA, -1060, -300 },
+ { 0xFF77B1FCBEBCDC4F, -1034, -292 },
+ { 0xBE5691EF416BD60C, -1007, -284 },
+ { 0x8DD01FAD907FFC3C, -980, -276 },
+ { 0xD3515C2831559A83, -954, -268 },
+ { 0x9D71AC8FADA6C9B5, -927, -260 },
+ { 0xEA9C227723EE8BCB, -901, -252 },
+ { 0xAECC49914078536D, -874, -244 },
+ { 0x823C12795DB6CE57, -847, -236 },
+ { 0xC21094364DFB5637, -821, -228 },
+ { 0x9096EA6F3848984F, -794, -220 },
+ { 0xD77485CB25823AC7, -768, -212 },
+ { 0xA086CFCD97BF97F4, -741, -204 },
+ { 0xEF340A98172AACE5, -715, -196 },
+ { 0xB23867FB2A35B28E, -688, -188 },
+ { 0x84C8D4DFD2C63F3B, -661, -180 },
+ { 0xC5DD44271AD3CDBA, -635, -172 },
+ { 0x936B9FCEBB25C996, -608, -164 },
+ { 0xDBAC6C247D62A584, -582, -156 },
+ { 0xA3AB66580D5FDAF6, -555, -148 },
+ { 0xF3E2F893DEC3F126, -529, -140 },
+ { 0xB5B5ADA8AAFF80B8, -502, -132 },
+ { 0x87625F056C7C4A8B, -475, -124 },
+ { 0xC9BCFF6034C13053, -449, -116 },
+ { 0x964E858C91BA2655, -422, -108 },
+ { 0xDFF9772470297EBD, -396, -100 },
+ { 0xA6DFBD9FB8E5B88F, -369, -92 },
+ { 0xF8A95FCF88747D94, -343, -84 },
+ { 0xB94470938FA89BCF, -316, -76 },
+ { 0x8A08F0F8BF0F156B, -289, -68 },
+ { 0xCDB02555653131B6, -263, -60 },
+ { 0x993FE2C6D07B7FAC, -236, -52 },
+ { 0xE45C10C42A2B3B06, -210, -44 },
+ { 0xAA242499697392D3, -183, -36 },
+ { 0xFD87B5F28300CA0E, -157, -28 },
+ { 0xBCE5086492111AEB, -130, -20 },
+ { 0x8CBCCC096F5088CC, -103, -12 },
+ { 0xD1B71758E219652C, -77, -4 },
+ { 0x9C40000000000000, -50, 4 },
+ { 0xE8D4A51000000000, -24, 12 },
+ { 0xAD78EBC5AC620000, 3, 20 },
+ { 0x813F3978F8940984, 30, 28 },
+ { 0xC097CE7BC90715B3, 56, 36 },
+ { 0x8F7E32CE7BEA5C70, 83, 44 },
+ { 0xD5D238A4ABE98068, 109, 52 },
+ { 0x9F4F2726179A2245, 136, 60 },
+ { 0xED63A231D4C4FB27, 162, 68 },
+ { 0xB0DE65388CC8ADA8, 189, 76 },
+ { 0x83C7088E1AAB65DB, 216, 84 },
+ { 0xC45D1DF942711D9A, 242, 92 },
+ { 0x924D692CA61BE758, 269, 100 },
+ { 0xDA01EE641A708DEA, 295, 108 },
+ { 0xA26DA3999AEF774A, 322, 116 },
+ { 0xF209787BB47D6B85, 348, 124 },
+ { 0xB454E4A179DD1877, 375, 132 },
+ { 0x865B86925B9BC5C2, 402, 140 },
+ { 0xC83553C5C8965D3D, 428, 148 },
+ { 0x952AB45CFA97A0B3, 455, 156 },
+ { 0xDE469FBD99A05FE3, 481, 164 },
+ { 0xA59BC234DB398C25, 508, 172 },
+ { 0xF6C69A72A3989F5C, 534, 180 },
+ { 0xB7DCBF5354E9BECE, 561, 188 },
+ { 0x88FCF317F22241E2, 588, 196 },
+ { 0xCC20CE9BD35C78A5, 614, 204 },
+ { 0x98165AF37B2153DF, 641, 212 },
+ { 0xE2A0B5DC971F303A, 667, 220 },
+ { 0xA8D9D1535CE3B396, 694, 228 },
+ { 0xFB9B7CD9A4A7443C, 720, 236 },
+ { 0xBB764C4CA7A44410, 747, 244 },
+ { 0x8BAB8EEFB6409C1A, 774, 252 },
+ { 0xD01FEF10A657842C, 800, 260 },
+ { 0x9B10A4E5E9913129, 827, 268 },
+ { 0xE7109BFBA19C0C9D, 853, 276 },
+ { 0xAC2820D9623BF429, 880, 284 },
+ { 0x80444B5E7AA7CF85, 907, 292 },
+ { 0xBF21E44003ACDD2D, 933, 300 },
+ { 0x8E679C2F5E44FF8F, 960, 308 },
+ { 0xD433179D9C8CB841, 986, 316 },
+ { 0x9E19DB92B4E31BA9, 1013, 324 },
+ }
+ };
+
+ // This computation gives exactly the same results for k as
+ // k = ceil((kAlpha - e - 1) * 0.30102999566398114)
+ // for |e| <= 1500, but doesn't require floating-point operations.
+ // NB: log_10(2) ~= 78913 / 2^18
+ JSON_ASSERT(e >= -1500);
+ JSON_ASSERT(e <= 1500);
+ const int f = kAlpha - e - 1;
+ const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);
+
+ const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
+ JSON_ASSERT(index >= 0);
+ JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());
+
+ const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];
+ JSON_ASSERT(kAlpha <= cached.e + e + 64);
+ JSON_ASSERT(kGamma >= cached.e + e + 64);
+
+ return cached;
+}
+
+/*!
+For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.
+For n == 0, returns 1 and sets pow10 := 1.
+*/
+inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)
+{
+ // LCOV_EXCL_START
+ if (n >= 1000000000)
+ {
+ pow10 = 1000000000;
+ return 10;
+ }
+ // LCOV_EXCL_STOP
+ if (n >= 100000000)
+ {
+ pow10 = 100000000;
+ return 9;
+ }
+ if (n >= 10000000)
+ {
+ pow10 = 10000000;
+ return 8;
+ }
+ if (n >= 1000000)
+ {
+ pow10 = 1000000;
+ return 7;
+ }
+ if (n >= 100000)
+ {
+ pow10 = 100000;
+ return 6;
+ }
+ if (n >= 10000)
+ {
+ pow10 = 10000;
+ return 5;
+ }
+ if (n >= 1000)
+ {
+ pow10 = 1000;
+ return 4;
+ }
+ if (n >= 100)
+ {
+ pow10 = 100;
+ return 3;
+ }
+ if (n >= 10)
+ {
+ pow10 = 10;
+ return 2;
+ }
+
+ pow10 = 1;
+ return 1;
+}
+
+inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,
+ std::uint64_t rest, std::uint64_t ten_k)
+{
+ JSON_ASSERT(len >= 1);
+ JSON_ASSERT(dist <= delta);
+ JSON_ASSERT(rest <= delta);
+ JSON_ASSERT(ten_k > 0);
+
+ // <--------------------------- delta ---->
+ // <---- dist --------->
+ // --------------[------------------+-------------------]--------------
+ // M- w M+
+ //
+ // ten_k
+ // <------>
+ // <---- rest ---->
+ // --------------[------------------+----+--------------]--------------
+ // w V
+ // = buf * 10^k
+ //
+ // ten_k represents a unit-in-the-last-place in the decimal representation
+ // stored in buf.
+ // Decrement buf by ten_k while this takes buf closer to w.
+
+ // The tests are written in this order to avoid overflow in unsigned
+ // integer arithmetic.
+
+ while (rest < dist
+ && delta - rest >= ten_k
+ && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))
+ {
+ JSON_ASSERT(buf[len - 1] != '0');
+ buf[len - 1]--;
+ rest += ten_k;
+ }
+}
+
+/*!
+Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.
+M- and M+ must be normalized and share the same exponent -60 <= e <= -32.
+*/
+inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
+ diyfp M_minus, diyfp w, diyfp M_plus)
+{
+ static_assert(kAlpha >= -60, "internal error");
+ static_assert(kGamma <= -32, "internal error");
+
+ // Generates the digits (and the exponent) of a decimal floating-point
+ // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's
+ // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.
+ //
+ // <--------------------------- delta ---->
+ // <---- dist --------->
+ // --------------[------------------+-------------------]--------------
+ // M- w M+
+ //
+ // Grisu2 generates the digits of M+ from left to right and stops as soon as
+ // V is in [M-,M+].
+
+ JSON_ASSERT(M_plus.e >= kAlpha);
+ JSON_ASSERT(M_plus.e <= kGamma);
+
+ std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
+ std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e)
+
+ // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):
+ //
+ // M+ = f * 2^e
+ // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e
+ // = ((p1 ) * 2^-e + (p2 )) * 2^e
+ // = p1 + p2 * 2^e
+
+ const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);
+
+ auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)
+ std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e
+
+ // 1)
+ //
+ // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
+
+ JSON_ASSERT(p1 > 0);
+
+ std::uint32_t pow10{};
+ const int k = find_largest_pow10(p1, pow10);
+
+ // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)
+ //
+ // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))
+ // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1))
+ //
+ // M+ = p1 + p2 * 2^e
+ // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e
+ // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e
+ // = d[k-1] * 10^(k-1) + ( rest) * 2^e
+ //
+ // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)
+ //
+ // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]
+ //
+ // but stop as soon as
+ //
+ // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e
+
+ int n = k;
+ while (n > 0)
+ {
+ // Invariants:
+ // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k)
+ // pow10 = 10^(n-1) <= p1 < 10^n
+ //
+ const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1)
+ const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1)
+ //
+ // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
+ // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
+ //
+ JSON_ASSERT(d <= 9);
+ buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+ //
+ // M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
+ //
+ p1 = r;
+ n--;
+ //
+ // M+ = buffer * 10^n + (p1 + p2 * 2^e)
+ // pow10 = 10^n
+ //
+
+ // Now check if enough digits have been generated.
+ // Compute
+ //
+ // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e
+ //
+ // Note:
+ // Since rest and delta share the same exponent e, it suffices to
+ // compare the significands.
+ const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;
+ if (rest <= delta)
+ {
+ // V = buffer * 10^n, with M- <= V <= M+.
+
+ decimal_exponent += n;
+
+ // We may now just stop. But instead look if the buffer could be
+ // decremented to bring V closer to w.
+ //
+ // pow10 = 10^n is now 1 ulp in the decimal representation V.
+ // The rounding procedure works with diyfp's with an implicit
+ // exponent of e.
+ //
+ // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e
+ //
+ const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;
+ grisu2_round(buffer, length, dist, delta, rest, ten_n);
+
+ return;
+ }
+
+ pow10 /= 10;
+ //
+ // pow10 = 10^(n-1) <= p1 < 10^n
+ // Invariants restored.
+ }
+
+ // 2)
+ //
+ // The digits of the integral part have been generated:
+ //
+ // M+ = d[k-1]...d[1]d[0] + p2 * 2^e
+ // = buffer + p2 * 2^e
+ //
+ // Now generate the digits of the fractional part p2 * 2^e.
+ //
+ // Note:
+ // No decimal point is generated: the exponent is adjusted instead.
+ //
+ // p2 actually represents the fraction
+ //
+ // p2 * 2^e
+ // = p2 / 2^-e
+ // = d[-1] / 10^1 + d[-2] / 10^2 + ...
+ //
+ // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)
+ //
+ // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m
+ // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)
+ //
+ // using
+ //
+ // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)
+ // = ( d) * 2^-e + ( r)
+ //
+ // or
+ // 10^m * p2 * 2^e = d + r * 2^e
+ //
+ // i.e.
+ //
+ // M+ = buffer + p2 * 2^e
+ // = buffer + 10^-m * (d + r * 2^e)
+ // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e
+ //
+ // and stop as soon as 10^-m * r * 2^e <= delta * 2^e
+
+ JSON_ASSERT(p2 > delta);
+
+ int m = 0;
+ for (;;)
+ {
+ // Invariant:
+ // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e
+ // = buffer * 10^-m + 10^-m * (p2 ) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e
+ //
+ JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);
+ p2 *= 10;
+ const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e
+ const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
+ //
+ // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
+ // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
+ //
+ JSON_ASSERT(d <= 9);
+ buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+ //
+ // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
+ //
+ p2 = r;
+ m++;
+ //
+ // M+ = buffer * 10^-m + 10^-m * p2 * 2^e
+ // Invariant restored.
+
+ // Check if enough digits have been generated.
+ //
+ // 10^-m * p2 * 2^e <= delta * 2^e
+ // p2 * 2^e <= 10^m * delta * 2^e
+ // p2 <= 10^m * delta
+ delta *= 10;
+ dist *= 10;
+ if (p2 <= delta)
+ {
+ break;
+ }
+ }
+
+ // V = buffer * 10^-m, with M- <= V <= M+.
+
+ decimal_exponent -= m;
+
+ // 1 ulp in the decimal representation is now 10^-m.
+ // Since delta and dist are now scaled by 10^m, we need to do the
+ // same with ulp in order to keep the units in sync.
+ //
+ // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e
+ //
+ const std::uint64_t ten_m = one.f;
+ grisu2_round(buffer, length, dist, delta, p2, ten_m);
+
+ // By construction this algorithm generates the shortest possible decimal
+ // number (Loitsch, Theorem 6.2) which rounds back to w.
+ // For an input number of precision p, at least
+ //
+ // N = 1 + ceil(p * log_10(2))
+ //
+ // decimal digits are sufficient to identify all binary floating-point
+ // numbers (Matula, "In-and-Out conversions").
+ // This implies that the algorithm does not produce more than N decimal
+ // digits.
+ //
+ // N = 17 for p = 53 (IEEE double precision)
+ // N = 9 for p = 24 (IEEE single precision)
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+JSON_HEDLEY_NON_NULL(1)
+inline void grisu2(char* buf, int& len, int& decimal_exponent,
+ diyfp m_minus, diyfp v, diyfp m_plus)
+{
+ JSON_ASSERT(m_plus.e == m_minus.e);
+ JSON_ASSERT(m_plus.e == v.e);
+
+ // --------(-----------------------+-----------------------)-------- (A)
+ // m- v m+
+ //
+ // --------------------(-----------+-----------------------)-------- (B)
+ // m- v m+
+ //
+ // First scale v (and m- and m+) such that the exponent is in the range
+ // [alpha, gamma].
+
+ const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);
+
+ const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k
+
+ // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]
+ const diyfp w = diyfp::mul(v, c_minus_k);
+ const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);
+ const diyfp w_plus = diyfp::mul(m_plus, c_minus_k);
+
+ // ----(---+---)---------------(---+---)---------------(---+---)----
+ // w- w w+
+ // = c*m- = c*v = c*m+
+ //
+ // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and
+ // w+ are now off by a small amount.
+ // In fact:
+ //
+ // w - v * 10^k < 1 ulp
+ //
+ // To account for this inaccuracy, add resp. subtract 1 ulp.
+ //
+ // --------+---[---------------(---+---)---------------]---+--------
+ // w- M- w M+ w+
+ //
+ // Now any number in [M-, M+] (bounds included) will round to w when input,
+ // regardless of how the input rounding algorithm breaks ties.
+ //
+ // And digit_gen generates the shortest possible such number in [M-, M+].
+ // Note that this does not mean that Grisu2 always generates the shortest
+ // possible number in the interval (m-, m+).
+ const diyfp M_minus(w_minus.f + 1, w_minus.e);
+ const diyfp M_plus (w_plus.f - 1, w_plus.e );
+
+ decimal_exponent = -cached.k; // = -(-k) = k
+
+ grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+template<typename FloatType>
+JSON_HEDLEY_NON_NULL(1)
+void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
+{
+ static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
+ "internal error: not enough precision");
+
+ JSON_ASSERT(std::isfinite(value));
+ JSON_ASSERT(value > 0);
+
+ // If the neighbors (and boundaries) of 'value' are always computed for double-precision
+ // numbers, all float's can be recovered using strtod (and strtof). However, the resulting
+ // decimal representations are not exactly "short".
+ //
+ // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)
+ // says "value is converted to a string as if by std::sprintf in the default ("C") locale"
+ // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars'
+ // does.
+ // On the other hand, the documentation for 'std::to_chars' requires that "parsing the
+ // representation using the corresponding std::from_chars function recovers value exactly". That
+ // indicates that single precision floating-point numbers should be recovered using
+ // 'std::strtof'.
+ //
+ // NB: If the neighbors are computed for single-precision numbers, there is a single float
+ // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision
+ // value is off by 1 ulp.
+#if 0
+ const boundaries w = compute_boundaries(static_cast<double>(value));
+#else
+ const boundaries w = compute_boundaries(value);
+#endif
+
+ grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
+}
+
+/*!
+@brief appends a decimal representation of e to buf
+@return a pointer to the element following the exponent.
+@pre -1000 < e < 1000
+*/
+JSON_HEDLEY_NON_NULL(1)
+JSON_HEDLEY_RETURNS_NON_NULL
+inline char* append_exponent(char* buf, int e)
+{
+ JSON_ASSERT(e > -1000);
+ JSON_ASSERT(e < 1000);
+
+ if (e < 0)
+ {
+ e = -e;
+ *buf++ = '-';
+ }
+ else
+ {
+ *buf++ = '+';
+ }
+
+ auto k = static_cast<std::uint32_t>(e);
+ if (k < 10)
+ {
+ // Always print at least two digits in the exponent.
+ // This is for compatibility with printf("%g").
+ *buf++ = '0';
+ *buf++ = static_cast<char>('0' + k);
+ }
+ else if (k < 100)
+ {
+ *buf++ = static_cast<char>('0' + k / 10);
+ k %= 10;
+ *buf++ = static_cast<char>('0' + k);
+ }
+ else
+ {
+ *buf++ = static_cast<char>('0' + k / 100);
+ k %= 100;
+ *buf++ = static_cast<char>('0' + k / 10);
+ k %= 10;
+ *buf++ = static_cast<char>('0' + k);
+ }
+
+ return buf;
+}
+
+/*!
+@brief prettify v = buf * 10^decimal_exponent
+
+If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point
+notation. Otherwise it will be printed in exponential notation.
+
+@pre min_exp < 0
+@pre max_exp > 0
+*/
+JSON_HEDLEY_NON_NULL(1)
+JSON_HEDLEY_RETURNS_NON_NULL
+inline char* format_buffer(char* buf, int len, int decimal_exponent,
+ int min_exp, int max_exp)
+{
+ JSON_ASSERT(min_exp < 0);
+ JSON_ASSERT(max_exp > 0);
+
+ const int k = len;
+ const int n = len + decimal_exponent;
+
+ // v = buf * 10^(n-k)
+ // k is the length of the buffer (number of decimal digits)
+ // n is the position of the decimal point relative to the start of the buffer.
+
+ if (k <= n && n <= max_exp)
+ {
+ // digits[000]
+ // len <= max_exp + 2
+
+ std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));
+ // Make it look like a floating-point number (#362, #378)
+ buf[n + 0] = '.';
+ buf[n + 1] = '0';
+ return buf + (static_cast<size_t>(n) + 2);
+ }
+
+ if (0 < n && n <= max_exp)
+ {
+ // dig.its
+ // len <= max_digits10 + 1
+
+ JSON_ASSERT(k > n);
+
+ std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));
+ buf[n] = '.';
+ return buf + (static_cast<size_t>(k) + 1U);
+ }
+
+ if (min_exp < n && n <= 0)
+ {
+ // 0.[000]digits
+ // len <= 2 + (-min_exp - 1) + max_digits10
+
+ std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k));
+ buf[0] = '0';
+ buf[1] = '.';
+ std::memset(buf + 2, '0', static_cast<size_t>(-n));
+ return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));
+ }
+
+ if (k == 1)
+ {
+ // dE+123
+ // len <= 1 + 5
+
+ buf += 1;
+ }
+ else
+ {
+ // d.igitsE+123
+ // len <= max_digits10 + 1 + 5
+
+ std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);
+ buf[1] = '.';
+ buf += 1 + static_cast<size_t>(k);
+ }
+
+ *buf++ = 'e';
+ return append_exponent(buf, n - 1);
+}
+
+} // namespace dtoa_impl
+
+/*!
+@brief generates a decimal representation of the floating-point number value in [first, last).
+
+The format of the resulting decimal representation is similar to printf's %g
+format. Returns an iterator pointing past-the-end of the decimal representation.
+
+@note The input number must be finite, i.e. NaN's and Inf's are not supported.
+@note The buffer must be large enough.
+@note The result is NOT null-terminated.
+*/
+template<typename FloatType>
+JSON_HEDLEY_NON_NULL(1, 2)
+JSON_HEDLEY_RETURNS_NON_NULL
+char* to_chars(char* first, const char* last, FloatType value)
+{
+ static_cast<void>(last); // maybe unused - fix warning
+ JSON_ASSERT(std::isfinite(value));
+
+ // Use signbit(value) instead of (value < 0) since signbit works for -0.
+ if (std::signbit(value))
+ {
+ value = -value;
+ *first++ = '-';
+ }
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ if (value == 0) // +-0
+ {
+ *first++ = '0';
+ // Make it look like a floating-point number (#362, #378)
+ *first++ = '.';
+ *first++ = '0';
+ return first;
+ }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+ JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);
+
+ // Compute v = buffer * 10^decimal_exponent.
+ // The decimal digits are stored in the buffer, which needs to be interpreted
+ // as an unsigned decimal integer.
+ // len is the length of the buffer, i.e. the number of decimal digits.
+ int len = 0;
+ int decimal_exponent = 0;
+ dtoa_impl::grisu2(first, len, decimal_exponent, value);
+
+ JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);
+
+ // Format the buffer like printf("%.*g", prec, value)
+ constexpr int kMinExp = -4;
+ // Use digits10 here to increase compatibility with version 2.
+ constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
+
+ JSON_ASSERT(last - first >= kMaxExp + 2);
+ JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
+ JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
+
+ return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/output/binary_writer.hpp>
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////
+// serialization //
+///////////////////
+
+/// how to treat decoding errors
+enum class error_handler_t
+{
+ strict, ///< throw a type_error exception in case of invalid UTF-8
+ replace, ///< replace invalid UTF-8 sequences with U+FFFD
+ ignore ///< ignore invalid UTF-8 sequences
+};
+
+template<typename BasicJsonType>
+class serializer
+{
+ using string_t = typename BasicJsonType::string_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using binary_char_t = typename BasicJsonType::binary_t::value_type;
+ static constexpr std::uint8_t UTF8_ACCEPT = 0;
+ static constexpr std::uint8_t UTF8_REJECT = 1;
+
+ public:
+ /*!
+ @param[in] s output stream to serialize to
+ @param[in] ichar indentation character to use
+ @param[in] error_handler_ how to react on decoding errors
+ */
+ serializer(output_adapter_t<char> s, const char ichar,
+ error_handler_t error_handler_ = error_handler_t::strict)
+ : o(std::move(s))
+ , loc(std::localeconv())
+ , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
+ , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
+ , indent_char(ichar)
+ , indent_string(512, indent_char)
+ , error_handler(error_handler_)
+ {}
+
+ // delete because of pointer members
+ serializer(const serializer&) = delete;
+ serializer& operator=(const serializer&) = delete;
+ serializer(serializer&&) = delete;
+ serializer& operator=(serializer&&) = delete;
+ ~serializer() = default;
+
+ /*!
+ @brief internal implementation of the serialization function
+
+ This function is called by the public member function dump and organizes
+ the serialization internally. The indentation level is propagated as
+ additional parameter. In case of arrays and objects, the function is
+ called recursively.
+
+ - strings and object keys are escaped using `escape_string()`
+ - integer numbers are converted implicitly via `operator<<`
+ - floating-point numbers are converted to a string using `"%g"` format
+ - binary values are serialized as objects containing the subtype and the
+ byte array
+
+ @param[in] val value to serialize
+ @param[in] pretty_print whether the output shall be pretty-printed
+ @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
+ in the output are escaped with `\uXXXX` sequences, and the result consists
+ of ASCII characters only.
+ @param[in] indent_step the indent level
+ @param[in] current_indent the current indent level (only used internally)
+ */
+ void dump(const BasicJsonType& val,
+ const bool pretty_print,
+ const bool ensure_ascii,
+ const unsigned int indent_step,
+ const unsigned int current_indent = 0)
+ {
+ switch (val.m_type)
+ {
+ case value_t::object:
+ {
+ if (val.m_value.object->empty())
+ {
+ o->write_characters("{}", 2);
+ return;
+ }
+
+ if (pretty_print)
+ {
+ o->write_characters("{\n", 2);
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, ' ');
+ }
+
+ // first n-1 elements
+ auto i = val.m_value.object->cbegin();
+ for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+ {
+ o->write_characters(indent_string.c_str(), new_indent);
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\": ", 3);
+ dump(i->second, true, ensure_ascii, indent_step, new_indent);
+ o->write_characters(",\n", 2);
+ }
+
+ // last element
+ JSON_ASSERT(i != val.m_value.object->cend());
+ JSON_ASSERT(std::next(i) == val.m_value.object->cend());
+ o->write_characters(indent_string.c_str(), new_indent);
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\": ", 3);
+ dump(i->second, true, ensure_ascii, indent_step, new_indent);
+
+ o->write_character('\n');
+ o->write_characters(indent_string.c_str(), current_indent);
+ o->write_character('}');
+ }
+ else
+ {
+ o->write_character('{');
+
+ // first n-1 elements
+ auto i = val.m_value.object->cbegin();
+ for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+ {
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\":", 2);
+ dump(i->second, false, ensure_ascii, indent_step, current_indent);
+ o->write_character(',');
+ }
+
+ // last element
+ JSON_ASSERT(i != val.m_value.object->cend());
+ JSON_ASSERT(std::next(i) == val.m_value.object->cend());
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\":", 2);
+ dump(i->second, false, ensure_ascii, indent_step, current_indent);
+
+ o->write_character('}');
+ }
+
+ return;
+ }
+
+ case value_t::array:
+ {
+ if (val.m_value.array->empty())
+ {
+ o->write_characters("[]", 2);
+ return;
+ }
+
+ if (pretty_print)
+ {
+ o->write_characters("[\n", 2);
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, ' ');
+ }
+
+ // first n-1 elements
+ for (auto i = val.m_value.array->cbegin();
+ i != val.m_value.array->cend() - 1; ++i)
+ {
+ o->write_characters(indent_string.c_str(), new_indent);
+ dump(*i, true, ensure_ascii, indent_step, new_indent);
+ o->write_characters(",\n", 2);
+ }
+
+ // last element
+ JSON_ASSERT(!val.m_value.array->empty());
+ o->write_characters(indent_string.c_str(), new_indent);
+ dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
+
+ o->write_character('\n');
+ o->write_characters(indent_string.c_str(), current_indent);
+ o->write_character(']');
+ }
+ else
+ {
+ o->write_character('[');
+
+ // first n-1 elements
+ for (auto i = val.m_value.array->cbegin();
+ i != val.m_value.array->cend() - 1; ++i)
+ {
+ dump(*i, false, ensure_ascii, indent_step, current_indent);
+ o->write_character(',');
+ }
+
+ // last element
+ JSON_ASSERT(!val.m_value.array->empty());
+ dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
+
+ o->write_character(']');
+ }
+
+ return;
+ }
+
+ case value_t::string:
+ {
+ o->write_character('\"');
+ dump_escaped(*val.m_value.string, ensure_ascii);
+ o->write_character('\"');
+ return;
+ }
+
+ case value_t::binary:
+ {
+ if (pretty_print)
+ {
+ o->write_characters("{\n", 2);
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, ' ');
+ }
+
+ o->write_characters(indent_string.c_str(), new_indent);
+
+ o->write_characters("\"bytes\": [", 10);
+
+ if (!val.m_value.binary->empty())
+ {
+ for (auto i = val.m_value.binary->cbegin();
+ i != val.m_value.binary->cend() - 1; ++i)
+ {
+ dump_integer(*i);
+ o->write_characters(", ", 2);
+ }
+ dump_integer(val.m_value.binary->back());
+ }
+
+ o->write_characters("],\n", 3);
+ o->write_characters(indent_string.c_str(), new_indent);
+
+ o->write_characters("\"subtype\": ", 11);
+ if (val.m_value.binary->has_subtype())
+ {
+ dump_integer(val.m_value.binary->subtype());
+ }
+ else
+ {
+ o->write_characters("null", 4);
+ }
+ o->write_character('\n');
+ o->write_characters(indent_string.c_str(), current_indent);
+ o->write_character('}');
+ }
+ else
+ {
+ o->write_characters("{\"bytes\":[", 10);
+
+ if (!val.m_value.binary->empty())
+ {
+ for (auto i = val.m_value.binary->cbegin();
+ i != val.m_value.binary->cend() - 1; ++i)
+ {
+ dump_integer(*i);
+ o->write_character(',');
+ }
+ dump_integer(val.m_value.binary->back());
+ }
+
+ o->write_characters("],\"subtype\":", 12);
+ if (val.m_value.binary->has_subtype())
+ {
+ dump_integer(val.m_value.binary->subtype());
+ o->write_character('}');
+ }
+ else
+ {
+ o->write_characters("null}", 5);
+ }
+ }
+ return;
+ }
+
+ case value_t::boolean:
+ {
+ if (val.m_value.boolean)
+ {
+ o->write_characters("true", 4);
+ }
+ else
+ {
+ o->write_characters("false", 5);
+ }
+ return;
+ }
+
+ case value_t::number_integer:
+ {
+ dump_integer(val.m_value.number_integer);
+ return;
+ }
+
+ case value_t::number_unsigned:
+ {
+ dump_integer(val.m_value.number_unsigned);
+ return;
+ }
+
+ case value_t::number_float:
+ {
+ dump_float(val.m_value.number_float);
+ return;
+ }
+
+ case value_t::discarded:
+ {
+ o->write_characters("<discarded>", 11);
+ return;
+ }
+
+ case value_t::null:
+ {
+ o->write_characters("null", 4);
+ return;
+ }
+
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+ }
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ /*!
+ @brief dump escaped string
+
+ Escape a string by replacing certain special characters by a sequence of an
+ escape character (backslash) and another character and other control
+ characters by a sequence of "\u" followed by a four-digit hex
+ representation. The escaped string is written to output stream @a o.
+
+ @param[in] s the string to escape
+ @param[in] ensure_ascii whether to escape non-ASCII characters with
+ \uXXXX sequences
+
+ @complexity Linear in the length of string @a s.
+ */
+ void dump_escaped(const string_t& s, const bool ensure_ascii)
+ {
+ std::uint32_t codepoint{};
+ std::uint8_t state = UTF8_ACCEPT;
+ std::size_t bytes = 0; // number of bytes written to string_buffer
+
+ // number of bytes written at the point of the last valid byte
+ std::size_t bytes_after_last_accept = 0;
+ std::size_t undumped_chars = 0;
+
+ for (std::size_t i = 0; i < s.size(); ++i)
+ {
+ const auto byte = static_cast<std::uint8_t>(s[i]);
+
+ switch (decode(state, codepoint, byte))
+ {
+ case UTF8_ACCEPT: // decode found a new code point
+ {
+ switch (codepoint)
+ {
+ case 0x08: // backspace
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'b';
+ break;
+ }
+
+ case 0x09: // horizontal tab
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 't';
+ break;
+ }
+
+ case 0x0A: // newline
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'n';
+ break;
+ }
+
+ case 0x0C: // formfeed
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'f';
+ break;
+ }
+
+ case 0x0D: // carriage return
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'r';
+ break;
+ }
+
+ case 0x22: // quotation mark
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = '\"';
+ break;
+ }
+
+ case 0x5C: // reverse solidus
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = '\\';
+ break;
+ }
+
+ default:
+ {
+ // escape control characters (0x00..0x1F) or, if
+ // ensure_ascii parameter is used, non-ASCII characters
+ if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))
+ {
+ if (codepoint <= 0xFFFF)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
+ static_cast<std::uint16_t>(codepoint)));
+ bytes += 6;
+ }
+ else
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
+ static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
+ static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
+ bytes += 12;
+ }
+ }
+ else
+ {
+ // copy byte to buffer (all previous bytes
+ // been copied have in default case above)
+ string_buffer[bytes++] = s[i];
+ }
+ break;
+ }
+ }
+
+ // write buffer and reset index; there must be 13 bytes
+ // left, as this is the maximal number of bytes to be
+ // written ("\uxxxx\uxxxx\0") for one code point
+ if (string_buffer.size() - bytes < 13)
+ {
+ o->write_characters(string_buffer.data(), bytes);
+ bytes = 0;
+ }
+
+ // remember the byte position of this accept
+ bytes_after_last_accept = bytes;
+ undumped_chars = 0;
+ break;
+ }
+
+ case UTF8_REJECT: // decode found invalid UTF-8 byte
+ {
+ switch (error_handler)
+ {
+ case error_handler_t::strict:
+ {
+ JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr));
+ }
+
+ case error_handler_t::ignore:
+ case error_handler_t::replace:
+ {
+ // in case we saw this character the first time, we
+ // would like to read it again, because the byte
+ // may be OK for itself, but just not OK for the
+ // previous sequence
+ if (undumped_chars > 0)
+ {
+ --i;
+ }
+
+ // reset length buffer to the last accepted index;
+ // thus removing/ignoring the invalid characters
+ bytes = bytes_after_last_accept;
+
+ if (error_handler == error_handler_t::replace)
+ {
+ // add a replacement character
+ if (ensure_ascii)
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'u';
+ string_buffer[bytes++] = 'f';
+ string_buffer[bytes++] = 'f';
+ string_buffer[bytes++] = 'f';
+ string_buffer[bytes++] = 'd';
+ }
+ else
+ {
+ string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
+ string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
+ string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
+ }
+
+ // write buffer and reset index; there must be 13 bytes
+ // left, as this is the maximal number of bytes to be
+ // written ("\uxxxx\uxxxx\0") for one code point
+ if (string_buffer.size() - bytes < 13)
+ {
+ o->write_characters(string_buffer.data(), bytes);
+ bytes = 0;
+ }
+
+ bytes_after_last_accept = bytes;
+ }
+
+ undumped_chars = 0;
+
+ // continue processing the string
+ state = UTF8_ACCEPT;
+ break;
+ }
+
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+ break;
+ }
+
+ default: // decode found yet incomplete multi-byte code point
+ {
+ if (!ensure_ascii)
+ {
+ // code point will not be escaped - copy byte to buffer
+ string_buffer[bytes++] = s[i];
+ }
+ ++undumped_chars;
+ break;
+ }
+ }
+ }
+
+ // we finished processing the string
+ if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))
+ {
+ // write buffer
+ if (bytes > 0)
+ {
+ o->write_characters(string_buffer.data(), bytes);
+ }
+ }
+ else
+ {
+ // we finish reading, but do not accept: string was incomplete
+ switch (error_handler)
+ {
+ case error_handler_t::strict:
+ {
+ JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast<std::uint8_t>(s.back() | 0))), nullptr));
+ }
+
+ case error_handler_t::ignore:
+ {
+ // write all accepted bytes
+ o->write_characters(string_buffer.data(), bytes_after_last_accept);
+ break;
+ }
+
+ case error_handler_t::replace:
+ {
+ // write all accepted bytes
+ o->write_characters(string_buffer.data(), bytes_after_last_accept);
+ // add a replacement character
+ if (ensure_ascii)
+ {
+ o->write_characters("\\ufffd", 6);
+ }
+ else
+ {
+ o->write_characters("\xEF\xBF\xBD", 3);
+ }
+ break;
+ }
+
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+ }
+ }
+
+ private:
+ /*!
+ @brief count digits
+
+ Count the number of decimal (base 10) digits for an input unsigned integer.
+
+ @param[in] x unsigned integer number to count its digits
+ @return number of decimal digits
+ */
+ inline unsigned int count_digits(number_unsigned_t x) noexcept
+ {
+ unsigned int n_digits = 1;
+ for (;;)
+ {
+ if (x < 10)
+ {
+ return n_digits;
+ }
+ if (x < 100)
+ {
+ return n_digits + 1;
+ }
+ if (x < 1000)
+ {
+ return n_digits + 2;
+ }
+ if (x < 10000)
+ {
+ return n_digits + 3;
+ }
+ x = x / 10000u;
+ n_digits += 4;
+ }
+ }
+
+ /*!
+ * @brief convert a byte to a uppercase hex representation
+ * @param[in] byte byte to represent
+ * @return representation ("00".."FF")
+ */
+ static std::string hex_bytes(std::uint8_t byte)
+ {
+ std::string result = "FF";
+ constexpr const char* nibble_to_hex = "0123456789ABCDEF";
+ result[0] = nibble_to_hex[byte / 16];
+ result[1] = nibble_to_hex[byte % 16];
+ return result;
+ }
+
+ // templates to avoid warnings about useless casts
+ template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
+ bool is_negative_number(NumberType x)
+ {
+ return x < 0;
+ }
+
+ template < typename NumberType, enable_if_t <std::is_unsigned<NumberType>::value, int > = 0 >
+ bool is_negative_number(NumberType /*unused*/)
+ {
+ return false;
+ }
+
+ /*!
+ @brief dump an integer
+
+ Dump a given integer to output stream @a o. Works internally with
+ @a number_buffer.
+
+ @param[in] x integer number (signed or unsigned) to dump
+ @tparam NumberType either @a number_integer_t or @a number_unsigned_t
+ */
+ template < typename NumberType, detail::enable_if_t <
+ std::is_integral<NumberType>::value ||
+ std::is_same<NumberType, number_unsigned_t>::value ||
+ std::is_same<NumberType, number_integer_t>::value ||
+ std::is_same<NumberType, binary_char_t>::value,
+ int > = 0 >
+ void dump_integer(NumberType x)
+ {
+ static constexpr std::array<std::array<char, 2>, 100> digits_to_99
+ {
+ {
+ {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},
+ {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},
+ {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},
+ {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},
+ {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},
+ {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},
+ {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},
+ {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},
+ {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},
+ {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},
+ }
+ };
+
+ // special case for "0"
+ if (x == 0)
+ {
+ o->write_character('0');
+ return;
+ }
+
+ // use a pointer to fill the buffer
+ auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+
+ number_unsigned_t abs_value;
+
+ unsigned int n_chars{};
+
+ if (is_negative_number(x))
+ {
+ *buffer_ptr = '-';
+ abs_value = remove_sign(static_cast<number_integer_t>(x));
+
+ // account one more byte for the minus sign
+ n_chars = 1 + count_digits(abs_value);
+ }
+ else
+ {
+ abs_value = static_cast<number_unsigned_t>(x);
+ n_chars = count_digits(abs_value);
+ }
+
+ // spare 1 byte for '\0'
+ JSON_ASSERT(n_chars < number_buffer.size() - 1);
+
+ // jump to the end to generate the string from backward,
+ // so we later avoid reversing the result
+ buffer_ptr += n_chars;
+
+ // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
+ // See: https://www.youtube.com/watch?v=o4-CwDo2zpg
+ while (abs_value >= 100)
+ {
+ const auto digits_index = static_cast<unsigned>((abs_value % 100));
+ abs_value /= 100;
+ *(--buffer_ptr) = digits_to_99[digits_index][1];
+ *(--buffer_ptr) = digits_to_99[digits_index][0];
+ }
+
+ if (abs_value >= 10)
+ {
+ const auto digits_index = static_cast<unsigned>(abs_value);
+ *(--buffer_ptr) = digits_to_99[digits_index][1];
+ *(--buffer_ptr) = digits_to_99[digits_index][0];
+ }
+ else
+ {
+ *(--buffer_ptr) = static_cast<char>('0' + abs_value);
+ }
+
+ o->write_characters(number_buffer.data(), n_chars);
+ }
+
+ /*!
+ @brief dump a floating-point number
+
+ Dump a given floating-point number to output stream @a o. Works internally
+ with @a number_buffer.
+
+ @param[in] x floating-point number to dump
+ */
+ void dump_float(number_float_t x)
+ {
+ // NaN / inf
+ if (!std::isfinite(x))
+ {
+ o->write_characters("null", 4);
+ return;
+ }
+
+ // If number_float_t is an IEEE-754 single or double precision number,
+ // use the Grisu2 algorithm to produce short numbers which are
+ // guaranteed to round-trip, using strtof and strtod, resp.
+ //
+ // NB: The test below works if <long double> == <double>.
+ static constexpr bool is_ieee_single_or_double
+ = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||
+ (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);
+
+ dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
+ }
+
+ void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
+ {
+ auto* begin = number_buffer.data();
+ auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
+
+ o->write_characters(begin, static_cast<size_t>(end - begin));
+ }
+
+ void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
+ {
+ // get number of digits for a float -> text -> float round-trip
+ static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
+
+ // the actual conversion
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
+
+ // negative value indicates an error
+ JSON_ASSERT(len > 0);
+ // check if buffer was large enough
+ JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
+
+ // erase thousands separator
+ if (thousands_sep != '\0')
+ {
+ // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
+ const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
+ std::fill(end, number_buffer.end(), '\0');
+ JSON_ASSERT((end - number_buffer.begin()) <= len);
+ len = (end - number_buffer.begin());
+ }
+
+ // convert decimal point to '.'
+ if (decimal_point != '\0' && decimal_point != '.')
+ {
+ // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
+ const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
+ if (dec_pos != number_buffer.end())
+ {
+ *dec_pos = '.';
+ }
+ }
+
+ o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
+
+ // determine if we need to append ".0"
+ const bool value_is_int_like =
+ std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
+ [](char c)
+ {
+ return c == '.' || c == 'e';
+ });
+
+ if (value_is_int_like)
+ {
+ o->write_characters(".0", 2);
+ }
+ }
+
+ /*!
+ @brief check whether a string is UTF-8 encoded
+
+ The function checks each byte of a string whether it is UTF-8 encoded. The
+ result of the check is stored in the @a state parameter. The function must
+ be called initially with state 0 (accept). State 1 means the string must
+ be rejected, because the current byte is not allowed. If the string is
+ completely processed, but the state is non-zero, the string ended
+ prematurely; that is, the last byte indicated more bytes should have
+ followed.
+
+ @param[in,out] state the state of the decoding
+ @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
+ @param[in] byte next byte to decode
+ @return new state
+
+ @note The function has been edited: a std::array is used.
+
+ @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+ */
+ static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
+ {
+ static const std::array<std::uint8_t, 400> utf8d =
+ {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
+ 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
+ 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
+ 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
+ 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
+ 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
+ 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
+ 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
+ }
+ };
+
+ JSON_ASSERT(byte < utf8d.size());
+ const std::uint8_t type = utf8d[byte];
+
+ codep = (state != UTF8_ACCEPT)
+ ? (byte & 0x3fu) | (codep << 6u)
+ : (0xFFu >> type) & (byte);
+
+ const std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);
+ JSON_ASSERT(index < 400);
+ state = utf8d[index];
+ return state;
+ }
+
+ /*
+ * Overload to make the compiler happy while it is instantiating
+ * dump_integer for number_unsigned_t.
+ * Must never be called.
+ */
+ number_unsigned_t remove_sign(number_unsigned_t x)
+ {
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ return x; // LCOV_EXCL_LINE
+ }
+
+ /*
+ * Helper function for dump_integer
+ *
+ * This function takes a negative signed integer and returns its absolute
+ * value as unsigned integer. The plus/minus shuffling is necessary as we can
+ * not directly remove the sign of an arbitrary signed integer as the
+ * absolute values of INT_MIN and INT_MAX are usually not the same. See
+ * #1708 for details.
+ */
+ inline number_unsigned_t remove_sign(number_integer_t x) noexcept
+ {
+ JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
+ return static_cast<number_unsigned_t>(-(x + 1)) + 1;
+ }
+
+ private:
+ /// the output of the serializer
+ output_adapter_t<char> o = nullptr;
+
+ /// a (hopefully) large enough character buffer
+ std::array<char, 64> number_buffer{{}};
+
+ /// the locale
+ const std::lconv* loc = nullptr;
+ /// the locale's thousand separator character
+ const char thousands_sep = '\0';
+ /// the locale's decimal point character
+ const char decimal_point = '\0';
+
+ /// string buffer
+ std::array<char, 512> string_buffer{{}};
+
+ /// the indentation character
+ const char indent_char;
+ /// the indentation string
+ string_t indent_string;
+
+ /// error_handler how to react on decoding errors
+ const error_handler_t error_handler;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/value_t.hpp>
+
+// #include <nlohmann/json_fwd.hpp>
+
+// #include <nlohmann/ordered_map.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <functional> // equal_to, less
+#include <initializer_list> // initializer_list
+#include <iterator> // input_iterator_tag, iterator_traits
+#include <memory> // allocator
+#include <stdexcept> // for out_of_range
+#include <type_traits> // enable_if, is_convertible
+#include <utility> // pair
+#include <vector> // vector
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// ordered_map: a minimal map-like container that preserves insertion order
+/// for use within nlohmann::basic_json<ordered_map>
+template <class Key, class T, class IgnoredLess = std::less<Key>,
+ class Allocator = std::allocator<std::pair<const Key, T>>>
+ struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>
+{
+ using key_type = Key;
+ using mapped_type = T;
+ using Container = std::vector<std::pair<const Key, T>, Allocator>;
+ using iterator = typename Container::iterator;
+ using const_iterator = typename Container::const_iterator;
+ using size_type = typename Container::size_type;
+ using value_type = typename Container::value_type;
+#ifdef JSON_HAS_CPP_14
+ using key_compare = std::equal_to<>;
+#else
+ using key_compare = std::equal_to<Key>;
+#endif
+
+ // Explicit constructors instead of `using Container::Container`
+ // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
+ ordered_map() noexcept(noexcept(Container())) : Container{} {}
+ explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {}
+ template <class It>
+ ordered_map(It first, It last, const Allocator& alloc = Allocator())
+ : Container{first, last, alloc} {}
+ ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )
+ : Container{init, alloc} {}
+
+ std::pair<iterator, bool> emplace(const key_type& key, T&& t)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return {it, false};
+ }
+ }
+ Container::emplace_back(key, std::forward<T>(t));
+ return {std::prev(this->end()), true};
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ std::pair<iterator, bool> emplace(KeyType && key, T && t)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return {it, false};
+ }
+ }
+ Container::emplace_back(std::forward<KeyType>(key), std::forward<T>(t));
+ return {std::prev(this->end()), true};
+ }
+
+ T& operator[](const key_type& key)
+ {
+ return emplace(key, T{}).first->second;
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ T & operator[](KeyType && key)
+ {
+ return emplace(std::forward<KeyType>(key), T{}).first->second;
+ }
+
+ const T& operator[](const key_type& key) const
+ {
+ return at(key);
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ const T & operator[](KeyType && key) const
+ {
+ return at(std::forward<KeyType>(key));
+ }
+
+ T& at(const key_type& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it->second;
+ }
+ }
+
+ JSON_THROW(std::out_of_range("key not found"));
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ T & at(KeyType && key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it->second;
+ }
+ }
+
+ JSON_THROW(std::out_of_range("key not found"));
+ }
+
+ const T& at(const key_type& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it->second;
+ }
+ }
+
+ JSON_THROW(std::out_of_range("key not found"));
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ const T & at(KeyType && key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it->second;
+ }
+ }
+
+ JSON_THROW(std::out_of_range("key not found"));
+ }
+
+ size_type erase(const key_type& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ // Since we cannot move const Keys, re-construct them in place
+ for (auto next = it; ++next != this->end(); ++it)
+ {
+ it->~value_type(); // Destroy but keep allocation
+ new (&*it) value_type{std::move(*next)};
+ }
+ Container::pop_back();
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ size_type erase(KeyType && key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ // Since we cannot move const Keys, re-construct them in place
+ for (auto next = it; ++next != this->end(); ++it)
+ {
+ it->~value_type(); // Destroy but keep allocation
+ new (&*it) value_type{std::move(*next)};
+ }
+ Container::pop_back();
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ iterator erase(iterator pos)
+ {
+ return erase(pos, std::next(pos));
+ }
+
+ iterator erase(iterator first, iterator last)
+ {
+ if (first == last)
+ {
+ return first;
+ }
+
+ const auto elements_affected = std::distance(first, last);
+ const auto offset = std::distance(Container::begin(), first);
+
+ // This is the start situation. We need to delete elements_affected
+ // elements (3 in this example: e, f, g), and need to return an
+ // iterator past the last deleted element (h in this example).
+ // Note that offset is the distance from the start of the vector
+ // to first. We will need this later.
+
+ // [ a, b, c, d, e, f, g, h, i, j ]
+ // ^ ^
+ // first last
+
+ // Since we cannot move const Keys, we re-construct them in place.
+ // We start at first and re-construct (viz. copy) the elements from
+ // the back of the vector. Example for first iteration:
+
+ // ,--------.
+ // v | destroy e and re-construct with h
+ // [ a, b, c, d, e, f, g, h, i, j ]
+ // ^ ^
+ // it it + elements_affected
+
+ for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it)
+ {
+ it->~value_type(); // destroy but keep allocation
+ new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it
+ }
+
+ // [ a, b, c, d, h, i, j, h, i, j ]
+ // ^ ^
+ // first last
+
+ // remove the unneeded elements at the end of the vector
+ Container::resize(this->size() - static_cast<size_type>(elements_affected));
+
+ // [ a, b, c, d, h, i, j ]
+ // ^ ^
+ // first last
+
+ // first is now pointing past the last deleted element, but we cannot
+ // use this iterator, because it may have been invalidated by the
+ // resize call. Instead, we can return begin() + offset.
+ return Container::begin() + offset;
+ }
+
+ size_type count(const key_type& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ size_type count(KeyType && key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ iterator find(const key_type& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it;
+ }
+ }
+ return Container::end();
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ iterator find(KeyType && key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it;
+ }
+ }
+ return Container::end();
+ }
+
+ const_iterator find(const key_type& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it;
+ }
+ }
+ return Container::end();
+ }
+
+ std::pair<iterator, bool> insert( value_type&& value )
+ {
+ return emplace(value.first, std::move(value.second));
+ }
+
+ std::pair<iterator, bool> insert( const value_type& value )
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, value.first))
+ {
+ return {it, false};
+ }
+ }
+ Container::push_back(value);
+ return {--this->end(), true};
+ }
+
+ template<typename InputIt>
+ using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
+ std::input_iterator_tag>::value>::type;
+
+ template<typename InputIt, typename = require_input_iter<InputIt>>
+ void insert(InputIt first, InputIt last)
+ {
+ for (auto it = first; it != last; ++it)
+ {
+ insert(*it);
+ }
+ }
+
+private:
+ JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare();
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+
+#if defined(JSON_HAS_CPP_17)
+ #include <any>
+ #include <string_view>
+#endif
+
+/*!
+@brief namespace for Niels Lohmann
+@see https://github.com/nlohmann
+@since version 1.0.0
+*/
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/*!
+@brief a class to store JSON values
+
+@internal
+@invariant The member variables @a m_value and @a m_type have the following
+relationship:
+- If `m_type == value_t::object`, then `m_value.object != nullptr`.
+- If `m_type == value_t::array`, then `m_value.array != nullptr`.
+- If `m_type == value_t::string`, then `m_value.string != nullptr`.
+The invariants are checked by member function assert_invariant().
+
+@note ObjectType trick from https://stackoverflow.com/a/9860911
+@endinternal
+
+@since version 1.0.0
+
+@nosubgrouping
+*/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
+ : public ::nlohmann::detail::json_base_class<CustomBaseClass>
+{
+ private:
+ template<detail::value_t> friend struct detail::external_constructor;
+
+ template<typename>
+ friend class ::nlohmann::json_pointer;
+ // can be restored when json_pointer backwards compatibility is removed
+ // friend ::nlohmann::json_pointer<StringType>;
+
+ template<typename BasicJsonType, typename InputType>
+ friend class ::nlohmann::detail::parser;
+ friend ::nlohmann::detail::serializer<basic_json>;
+ template<typename BasicJsonType>
+ friend class ::nlohmann::detail::iter_impl;
+ template<typename BasicJsonType, typename CharType>
+ friend class ::nlohmann::detail::binary_writer;
+ template<typename BasicJsonType, typename InputType, typename SAX>
+ friend class ::nlohmann::detail::binary_reader;
+ template<typename BasicJsonType>
+ friend class ::nlohmann::detail::json_sax_dom_parser;
+ template<typename BasicJsonType>
+ friend class ::nlohmann::detail::json_sax_dom_callback_parser;
+ friend class ::nlohmann::detail::exception;
+
+ /// workaround type for MSVC
+ using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
+ using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ // convenience aliases for types residing in namespace detail;
+ using lexer = ::nlohmann::detail::lexer_base<basic_json>;
+
+ template<typename InputAdapterType>
+ static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser(
+ InputAdapterType adapter,
+ detail::parser_callback_t<basic_json>cb = nullptr,
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false
+ )
+ {
+ return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
+ std::move(cb), allow_exceptions, ignore_comments);
+ }
+
+ private:
+ using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;
+ template<typename BasicJsonType>
+ using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;
+ template<typename BasicJsonType>
+ using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;
+ template<typename Iterator>
+ using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;
+ template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;
+
+ template<typename CharType>
+ using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;
+
+ template<typename InputType>
+ using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;
+ template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ using serializer = ::nlohmann::detail::serializer<basic_json>;
+
+ public:
+ using value_t = detail::value_t;
+ /// JSON Pointer, see @ref nlohmann::json_pointer
+ using json_pointer = ::nlohmann::json_pointer<StringType>;
+ template<typename T, typename SFINAE>
+ using json_serializer = JSONSerializer<T, SFINAE>;
+ /// how to treat decoding errors
+ using error_handler_t = detail::error_handler_t;
+ /// how to treat CBOR tags
+ using cbor_tag_handler_t = detail::cbor_tag_handler_t;
+ /// helper type for initializer lists of basic_json values
+ using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
+
+ using input_format_t = detail::input_format_t;
+ /// SAX interface type, see @ref nlohmann::json_sax
+ using json_sax_t = json_sax<basic_json>;
+
+ ////////////////
+ // exceptions //
+ ////////////////
+
+ /// @name exceptions
+ /// Classes to implement user-defined exceptions.
+ /// @{
+
+ using exception = detail::exception;
+ using parse_error = detail::parse_error;
+ using invalid_iterator = detail::invalid_iterator;
+ using type_error = detail::type_error;
+ using out_of_range = detail::out_of_range;
+ using other_error = detail::other_error;
+
+ /// @}
+
+
+ /////////////////////
+ // container types //
+ /////////////////////
+
+ /// @name container types
+ /// The canonic container types to use @ref basic_json like any other STL
+ /// container.
+ /// @{
+
+ /// the type of elements in a basic_json container
+ using value_type = basic_json;
+
+ /// the type of an element reference
+ using reference = value_type&;
+ /// the type of an element const reference
+ using const_reference = const value_type&;
+
+ /// a type to represent differences between iterators
+ using difference_type = std::ptrdiff_t;
+ /// a type to represent container sizes
+ using size_type = std::size_t;
+
+ /// the allocator type
+ using allocator_type = AllocatorType<basic_json>;
+
+ /// the type of an element pointer
+ using pointer = typename std::allocator_traits<allocator_type>::pointer;
+ /// the type of an element const pointer
+ using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
+
+ /// an iterator for a basic_json container
+ using iterator = iter_impl<basic_json>;
+ /// a const iterator for a basic_json container
+ using const_iterator = iter_impl<const basic_json>;
+ /// a reverse iterator for a basic_json container
+ using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;
+ /// a const reverse iterator for a basic_json container
+ using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;
+
+ /// @}
+
+
+ /// @brief returns the allocator associated with the container
+ /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/
+ static allocator_type get_allocator()
+ {
+ return allocator_type();
+ }
+
+ /// @brief returns version information on the library
+ /// @sa https://json.nlohmann.me/api/basic_json/meta/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json meta()
+ {
+ basic_json result;
+
+ result["copyright"] = "(C) 2013-2022 Niels Lohmann";
+ result["name"] = "JSON for Modern C++";
+ result["url"] = "https://github.com/nlohmann/json";
+ result["version"]["string"] =
+ detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.',
+ std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.',
+ std::to_string(NLOHMANN_JSON_VERSION_PATCH));
+ result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
+ result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
+ result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
+
+#ifdef _WIN32
+ result["platform"] = "win32";
+#elif defined __linux__
+ result["platform"] = "linux";
+#elif defined __APPLE__
+ result["platform"] = "apple";
+#elif defined __unix__
+ result["platform"] = "unix";
+#else
+ result["platform"] = "unknown";
+#endif
+
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+ result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
+#elif defined(__clang__)
+ result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
+#elif defined(__GNUC__) || defined(__GNUG__)
+ result["compiler"] = {{"family", "gcc"}, {"version", detail::concat(
+ std::to_string(__GNUC__), '.',
+ std::to_string(__GNUC_MINOR__), '.',
+ std::to_string(__GNUC_PATCHLEVEL__))
+ }
+ };
+#elif defined(__HP_cc) || defined(__HP_aCC)
+ result["compiler"] = "hp"
+#elif defined(__IBMCPP__)
+ result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
+#elif defined(_MSC_VER)
+ result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
+#elif defined(__PGI)
+ result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
+#elif defined(__SUNPRO_CC)
+ result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
+#else
+ result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
+#endif
+
+
+#if defined(_MSVC_LANG)
+ result["compiler"]["c++"] = std::to_string(_MSVC_LANG);
+#elif defined(__cplusplus)
+ result["compiler"]["c++"] = std::to_string(__cplusplus);
+#else
+ result["compiler"]["c++"] = "unknown";
+#endif
+ return result;
+ }
+
+
+ ///////////////////////////
+ // JSON value data types //
+ ///////////////////////////
+
+ /// @name JSON value data types
+ /// The data types to store a JSON value. These types are derived from
+ /// the template arguments passed to class @ref basic_json.
+ /// @{
+
+ /// @brief default object key comparator type
+ /// The actual object key comparator type (@ref object_comparator_t) may be
+ /// different.
+ /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
+#if defined(JSON_HAS_CPP_14)
+ // use of transparent comparator avoids unnecessary repeated construction of temporaries
+ // in functions involving lookup by key with types other than object_t::key_type (aka. StringType)
+ using default_object_comparator_t = std::less<>;
+#else
+ using default_object_comparator_t = std::less<StringType>;
+#endif
+
+ /// @brief a type for an object
+ /// @sa https://json.nlohmann.me/api/basic_json/object_t/
+ using object_t = ObjectType<StringType,
+ basic_json,
+ default_object_comparator_t,
+ AllocatorType<std::pair<const StringType,
+ basic_json>>>;
+
+ /// @brief a type for an array
+ /// @sa https://json.nlohmann.me/api/basic_json/array_t/
+ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
+
+ /// @brief a type for a string
+ /// @sa https://json.nlohmann.me/api/basic_json/string_t/
+ using string_t = StringType;
+
+ /// @brief a type for a boolean
+ /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/
+ using boolean_t = BooleanType;
+
+ /// @brief a type for a number (integer)
+ /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/
+ using number_integer_t = NumberIntegerType;
+
+ /// @brief a type for a number (unsigned)
+ /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/
+ using number_unsigned_t = NumberUnsignedType;
+
+ /// @brief a type for a number (floating-point)
+ /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/
+ using number_float_t = NumberFloatType;
+
+ /// @brief a type for a packed binary type
+ /// @sa https://json.nlohmann.me/api/basic_json/binary_t/
+ using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
+
+ /// @brief object key comparator type
+ /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/
+ using object_comparator_t = detail::actual_object_comparator_t<basic_json>;
+
+ /// @}
+
+ private:
+
+ /// helper for exception-safe object creation
+ template<typename T, typename... Args>
+ JSON_HEDLEY_RETURNS_NON_NULL
+ static T* create(Args&& ... args)
+ {
+ AllocatorType<T> alloc;
+ using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;
+
+ auto deleter = [&](T * obj)
+ {
+ AllocatorTraits::deallocate(alloc, obj, 1);
+ };
+ std::unique_ptr<T, decltype(deleter)> obj(AllocatorTraits::allocate(alloc, 1), deleter);
+ AllocatorTraits::construct(alloc, obj.get(), std::forward<Args>(args)...);
+ JSON_ASSERT(obj != nullptr);
+ return obj.release();
+ }
+
+ ////////////////////////
+ // JSON value storage //
+ ////////////////////////
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ /*!
+ @brief a JSON value
+
+ The actual storage for a JSON value of the @ref basic_json class. This
+ union combines the different storage types for the JSON value types
+ defined in @ref value_t.
+
+ JSON type | value_t type | used type
+ --------- | --------------- | ------------------------
+ object | object | pointer to @ref object_t
+ array | array | pointer to @ref array_t
+ string | string | pointer to @ref string_t
+ boolean | boolean | @ref boolean_t
+ number | number_integer | @ref number_integer_t
+ number | number_unsigned | @ref number_unsigned_t
+ number | number_float | @ref number_float_t
+ binary | binary | pointer to @ref binary_t
+ null | null | *no value is stored*
+
+ @note Variable-length types (objects, arrays, and strings) are stored as
+ pointers. The size of the union should not exceed 64 bits if the default
+ value types are used.
+
+ @since version 1.0.0
+ */
+ union json_value
+ {
+ /// object (stored with pointer to save storage)
+ object_t* object;
+ /// array (stored with pointer to save storage)
+ array_t* array;
+ /// string (stored with pointer to save storage)
+ string_t* string;
+ /// binary (stored with pointer to save storage)
+ binary_t* binary;
+ /// boolean
+ boolean_t boolean;
+ /// number (integer)
+ number_integer_t number_integer;
+ /// number (unsigned integer)
+ number_unsigned_t number_unsigned;
+ /// number (floating-point)
+ number_float_t number_float;
+
+ /// default constructor (for null values)
+ json_value() = default;
+ /// constructor for booleans
+ json_value(boolean_t v) noexcept : boolean(v) {}
+ /// constructor for numbers (integer)
+ json_value(number_integer_t v) noexcept : number_integer(v) {}
+ /// constructor for numbers (unsigned)
+ json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}
+ /// constructor for numbers (floating-point)
+ json_value(number_float_t v) noexcept : number_float(v) {}
+ /// constructor for empty values of a given type
+ json_value(value_t t)
+ {
+ switch (t)
+ {
+ case value_t::object:
+ {
+ object = create<object_t>();
+ break;
+ }
+
+ case value_t::array:
+ {
+ array = create<array_t>();
+ break;
+ }
+
+ case value_t::string:
+ {
+ string = create<string_t>("");
+ break;
+ }
+
+ case value_t::binary:
+ {
+ binary = create<binary_t>();
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ boolean = static_cast<boolean_t>(false);
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ number_integer = static_cast<number_integer_t>(0);
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ number_unsigned = static_cast<number_unsigned_t>(0);
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ number_float = static_cast<number_float_t>(0.0);
+ break;
+ }
+
+ case value_t::null:
+ {
+ object = nullptr; // silence warning, see #821
+ break;
+ }
+
+ case value_t::discarded:
+ default:
+ {
+ object = nullptr; // silence warning, see #821
+ if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
+ {
+ JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.11.2", nullptr)); // LCOV_EXCL_LINE
+ }
+ break;
+ }
+ }
+ }
+
+ /// constructor for strings
+ json_value(const string_t& value) : string(create<string_t>(value)) {}
+
+ /// constructor for rvalue strings
+ json_value(string_t&& value) : string(create<string_t>(std::move(value))) {}
+
+ /// constructor for objects
+ json_value(const object_t& value) : object(create<object_t>(value)) {}
+
+ /// constructor for rvalue objects
+ json_value(object_t&& value) : object(create<object_t>(std::move(value))) {}
+
+ /// constructor for arrays
+ json_value(const array_t& value) : array(create<array_t>(value)) {}
+
+ /// constructor for rvalue arrays
+ json_value(array_t&& value) : array(create<array_t>(std::move(value))) {}
+
+ /// constructor for binary arrays
+ json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {}
+
+ /// constructor for rvalue binary arrays
+ json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {}
+
+ /// constructor for binary arrays (internal type)
+ json_value(const binary_t& value) : binary(create<binary_t>(value)) {}
+
+ /// constructor for rvalue binary arrays (internal type)
+ json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}
+
+ void destroy(value_t t)
+ {
+ if (t == value_t::array || t == value_t::object)
+ {
+ // flatten the current json_value to a heap-allocated stack
+ std::vector<basic_json> stack;
+
+ // move the top-level items to stack
+ if (t == value_t::array)
+ {
+ stack.reserve(array->size());
+ std::move(array->begin(), array->end(), std::back_inserter(stack));
+ }
+ else
+ {
+ stack.reserve(object->size());
+ for (auto&& it : *object)
+ {
+ stack.push_back(std::move(it.second));
+ }
+ }
+
+ while (!stack.empty())
+ {
+ // move the last item to local variable to be processed
+ basic_json current_item(std::move(stack.back()));
+ stack.pop_back();
+
+ // if current_item is array/object, move
+ // its children to the stack to be processed later
+ if (current_item.is_array())
+ {
+ std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack));
+
+ current_item.m_value.array->clear();
+ }
+ else if (current_item.is_object())
+ {
+ for (auto&& it : *current_item.m_value.object)
+ {
+ stack.push_back(std::move(it.second));
+ }
+
+ current_item.m_value.object->clear();
+ }
+
+ // it's now safe that current_item get destructed
+ // since it doesn't have any children
+ }
+ }
+
+ switch (t)
+ {
+ case value_t::object:
+ {
+ AllocatorType<object_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, object);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ AllocatorType<array_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, array);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);
+ break;
+ }
+
+ case value_t::string:
+ {
+ AllocatorType<string_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
+ break;
+ }
+
+ case value_t::binary:
+ {
+ AllocatorType<binary_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::discarded:
+ default:
+ {
+ break;
+ }
+ }
+ }
+ };
+
+ private:
+ /*!
+ @brief checks the class invariants
+
+ This function asserts the class invariants. It needs to be called at the
+ end of every constructor to make sure that created objects respect the
+ invariant. Furthermore, it has to be called each time the type of a JSON
+ value is changed, because the invariant expresses a relationship between
+ @a m_type and @a m_value.
+
+ Furthermore, the parent relation is checked for arrays and objects: If
+ @a check_parents true and the value is an array or object, then the
+ container's elements must have the current value as parent.
+
+ @param[in] check_parents whether the parent relation should be checked.
+ The value is true by default and should only be set to false
+ during destruction of objects when the invariant does not
+ need to hold.
+ */
+ void assert_invariant(bool check_parents = true) const noexcept
+ {
+ JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr);
+ JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr);
+ JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr);
+ JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr);
+
+#if JSON_DIAGNOSTICS
+ JSON_TRY
+ {
+ // cppcheck-suppress assertWithSideEffect
+ JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)
+ {
+ return j.m_parent == this;
+ }));
+ }
+ JSON_CATCH(...) {} // LCOV_EXCL_LINE
+#endif
+ static_cast<void>(check_parents);
+ }
+
+ void set_parents()
+ {
+#if JSON_DIAGNOSTICS
+ switch (m_type)
+ {
+ case value_t::array:
+ {
+ for (auto& element : *m_value.array)
+ {
+ element.m_parent = this;
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ for (auto& element : *m_value.object)
+ {
+ element.second.m_parent = this;
+ }
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ break;
+ }
+#endif
+ }
+
+ iterator set_parents(iterator it, typename iterator::difference_type count_set_parents)
+ {
+#if JSON_DIAGNOSTICS
+ for (typename iterator::difference_type i = 0; i < count_set_parents; ++i)
+ {
+ (it + i)->m_parent = this;
+ }
+#else
+ static_cast<void>(count_set_parents);
+#endif
+ return it;
+ }
+
+ reference set_parent(reference j, std::size_t old_capacity = static_cast<std::size_t>(-1))
+ {
+#if JSON_DIAGNOSTICS
+ if (old_capacity != static_cast<std::size_t>(-1))
+ {
+ // see https://github.com/nlohmann/json/issues/2838
+ JSON_ASSERT(type() == value_t::array);
+ if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))
+ {
+ // capacity has changed: update all parents
+ set_parents();
+ return j;
+ }
+ }
+
+ // ordered_json uses a vector internally, so pointers could have
+ // been invalidated; see https://github.com/nlohmann/json/issues/2962
+#ifdef JSON_HEDLEY_MSVC_VERSION
+#pragma warning(push )
+#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr
+#endif
+ if (detail::is_ordered_map<object_t>::value)
+ {
+ set_parents();
+ return j;
+ }
+#ifdef JSON_HEDLEY_MSVC_VERSION
+#pragma warning( pop )
+#endif
+
+ j.m_parent = this;
+#else
+ static_cast<void>(j);
+ static_cast<void>(old_capacity);
+#endif
+ return j;
+ }
+
+ public:
+ //////////////////////////
+ // JSON parser callback //
+ //////////////////////////
+
+ /// @brief parser event types
+ /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/
+ using parse_event_t = detail::parse_event_t;
+
+ /// @brief per-element parser callback type
+ /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/
+ using parser_callback_t = detail::parser_callback_t<basic_json>;
+
+ //////////////////
+ // constructors //
+ //////////////////
+
+ /// @name constructors and destructors
+ /// Constructors of class @ref basic_json, copy/move constructor, copy
+ /// assignment, static functions creating objects, and the destructor.
+ /// @{
+
+ /// @brief create an empty value with a given type
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(const value_t v)
+ : m_type(v), m_value(v)
+ {
+ assert_invariant();
+ }
+
+ /// @brief create a null object
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape)
+ : basic_json(value_t::null)
+ {
+ assert_invariant();
+ }
+
+ /// @brief create a JSON value from compatible types
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ template < typename CompatibleType,
+ typename U = detail::uncvref_t<CompatibleType>,
+ detail::enable_if_t <
+ !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >
+ basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)
+ JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
+ std::forward<CompatibleType>(val))))
+ {
+ JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief create a JSON value from an existing one
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ template < typename BasicJsonType,
+ detail::enable_if_t <
+ detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >
+ basic_json(const BasicJsonType& val)
+ {
+ using other_boolean_t = typename BasicJsonType::boolean_t;
+ using other_number_float_t = typename BasicJsonType::number_float_t;
+ using other_number_integer_t = typename BasicJsonType::number_integer_t;
+ using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using other_string_t = typename BasicJsonType::string_t;
+ using other_object_t = typename BasicJsonType::object_t;
+ using other_array_t = typename BasicJsonType::array_t;
+ using other_binary_t = typename BasicJsonType::binary_t;
+
+ switch (val.type())
+ {
+ case value_t::boolean:
+ JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
+ break;
+ case value_t::number_float:
+ JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
+ break;
+ case value_t::number_integer:
+ JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
+ break;
+ case value_t::number_unsigned:
+ JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
+ break;
+ case value_t::string:
+ JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
+ break;
+ case value_t::object:
+ JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
+ break;
+ case value_t::array:
+ JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
+ break;
+ case value_t::binary:
+ JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
+ break;
+ case value_t::null:
+ *this = nullptr;
+ break;
+ case value_t::discarded:
+ m_type = value_t::discarded;
+ break;
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+ JSON_ASSERT(m_type == val.type());
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief create a container (array or object) from an initializer list
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(initializer_list_t init,
+ bool type_deduction = true,
+ value_t manual_type = value_t::array)
+ {
+ // check if each element is an array with two elements whose first
+ // element is a string
+ bool is_an_object = std::all_of(init.begin(), init.end(),
+ [](const detail::json_ref<basic_json>& element_ref)
+ {
+ return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string();
+ });
+
+ // adjust type if type deduction is not wanted
+ if (!type_deduction)
+ {
+ // if array is wanted, do not create an object though possible
+ if (manual_type == value_t::array)
+ {
+ is_an_object = false;
+ }
+
+ // if object is wanted but impossible, throw an exception
+ if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
+ {
+ JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr));
+ }
+ }
+
+ if (is_an_object)
+ {
+ // the initializer list is a list of pairs -> create object
+ m_type = value_t::object;
+ m_value = value_t::object;
+
+ for (auto& element_ref : init)
+ {
+ auto element = element_ref.moved_or_copied();
+ m_value.object->emplace(
+ std::move(*((*element.m_value.array)[0].m_value.string)),
+ std::move((*element.m_value.array)[1]));
+ }
+ }
+ else
+ {
+ // the initializer list describes an array -> create array
+ m_type = value_t::array;
+ m_value.array = create<array_t>(init.begin(), init.end());
+ }
+
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief explicitly create a binary array (without subtype)
+ /// @sa https://json.nlohmann.me/api/basic_json/binary/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json binary(const typename binary_t::container_type& init)
+ {
+ auto res = basic_json();
+ res.m_type = value_t::binary;
+ res.m_value = init;
+ return res;
+ }
+
+ /// @brief explicitly create a binary array (with subtype)
+ /// @sa https://json.nlohmann.me/api/basic_json/binary/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
+ {
+ auto res = basic_json();
+ res.m_type = value_t::binary;
+ res.m_value = binary_t(init, subtype);
+ return res;
+ }
+
+ /// @brief explicitly create a binary array
+ /// @sa https://json.nlohmann.me/api/basic_json/binary/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json binary(typename binary_t::container_type&& init)
+ {
+ auto res = basic_json();
+ res.m_type = value_t::binary;
+ res.m_value = std::move(init);
+ return res;
+ }
+
+ /// @brief explicitly create a binary array (with subtype)
+ /// @sa https://json.nlohmann.me/api/basic_json/binary/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
+ {
+ auto res = basic_json();
+ res.m_type = value_t::binary;
+ res.m_value = binary_t(std::move(init), subtype);
+ return res;
+ }
+
+ /// @brief explicitly create an array from an initializer list
+ /// @sa https://json.nlohmann.me/api/basic_json/array/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json array(initializer_list_t init = {})
+ {
+ return basic_json(init, false, value_t::array);
+ }
+
+ /// @brief explicitly create an object from an initializer list
+ /// @sa https://json.nlohmann.me/api/basic_json/object/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json object(initializer_list_t init = {})
+ {
+ return basic_json(init, false, value_t::object);
+ }
+
+ /// @brief construct an array with count copies of given value
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(size_type cnt, const basic_json& val)
+ : m_type(value_t::array)
+ {
+ m_value.array = create<array_t>(cnt, val);
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief construct a JSON container given an iterator range
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ template < class InputIT, typename std::enable_if <
+ std::is_same<InputIT, typename basic_json_t::iterator>::value ||
+ std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >
+ basic_json(InputIT first, InputIT last)
+ {
+ JSON_ASSERT(first.m_object != nullptr);
+ JSON_ASSERT(last.m_object != nullptr);
+
+ // make sure iterator fits the current value
+ if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr));
+ }
+
+ // copy type from first iterator
+ m_type = first.m_object->m_type;
+
+ // check if iterator range is complete for primitive values
+ switch (m_type)
+ {
+ case value_t::boolean:
+ case value_t::number_float:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::string:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
+ || !last.m_it.primitive_iterator.is_end()))
+ {
+ JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object));
+ }
+ break;
+ }
+
+ case value_t::null:
+ case value_t::object:
+ case value_t::array:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ break;
+ }
+
+ switch (m_type)
+ {
+ case value_t::number_integer:
+ {
+ m_value.number_integer = first.m_object->m_value.number_integer;
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ m_value.number_unsigned = first.m_object->m_value.number_unsigned;
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ m_value.number_float = first.m_object->m_value.number_float;
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ m_value.boolean = first.m_object->m_value.boolean;
+ break;
+ }
+
+ case value_t::string:
+ {
+ m_value = *first.m_object->m_value.string;
+ break;
+ }
+
+ case value_t::object:
+ {
+ m_value.object = create<object_t>(first.m_it.object_iterator,
+ last.m_it.object_iterator);
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_value.array = create<array_t>(first.m_it.array_iterator,
+ last.m_it.array_iterator);
+ break;
+ }
+
+ case value_t::binary:
+ {
+ m_value = *first.m_object->m_value.binary;
+ break;
+ }
+
+ case value_t::null:
+ case value_t::discarded:
+ default:
+ JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
+ }
+
+ set_parents();
+ assert_invariant();
+ }
+
+
+ ///////////////////////////////////////
+ // other constructors and destructor //
+ ///////////////////////////////////////
+
+ template<typename JsonRef,
+ detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
+ std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
+ basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}
+
+ /// @brief copy constructor
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(const basic_json& other)
+ : json_base_class_t(other),
+ m_type(other.m_type)
+ {
+ // check of passed value is valid
+ other.assert_invariant();
+
+ switch (m_type)
+ {
+ case value_t::object:
+ {
+ m_value = *other.m_value.object;
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_value = *other.m_value.array;
+ break;
+ }
+
+ case value_t::string:
+ {
+ m_value = *other.m_value.string;
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ m_value = other.m_value.boolean;
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ m_value = other.m_value.number_integer;
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ m_value = other.m_value.number_unsigned;
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ m_value = other.m_value.number_float;
+ break;
+ }
+
+ case value_t::binary:
+ {
+ m_value = *other.m_value.binary;
+ break;
+ }
+
+ case value_t::null:
+ case value_t::discarded:
+ default:
+ break;
+ }
+
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief move constructor
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(basic_json&& other) noexcept
+ : json_base_class_t(std::move(other)),
+ m_type(std::move(other.m_type)),
+ m_value(std::move(other.m_value))
+ {
+ // check that passed value is valid
+ other.assert_invariant(false); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
+
+ // invalidate payload
+ other.m_type = value_t::null;
+ other.m_value = {};
+
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief copy assignment
+ /// @sa https://json.nlohmann.me/api/basic_json/operator=/
+ basic_json& operator=(basic_json other) noexcept (
+ std::is_nothrow_move_constructible<value_t>::value&&
+ std::is_nothrow_move_assignable<value_t>::value&&
+ std::is_nothrow_move_constructible<json_value>::value&&
+ std::is_nothrow_move_assignable<json_value>::value&&
+ std::is_nothrow_move_assignable<json_base_class_t>::value
+ )
+ {
+ // check that passed value is valid
+ other.assert_invariant();
+
+ using std::swap;
+ swap(m_type, other.m_type);
+ swap(m_value, other.m_value);
+ json_base_class_t::operator=(std::move(other));
+
+ set_parents();
+ assert_invariant();
+ return *this;
+ }
+
+ /// @brief destructor
+ /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/
+ ~basic_json() noexcept
+ {
+ assert_invariant(false);
+ m_value.destroy(m_type);
+ }
+
+ /// @}
+
+ public:
+ ///////////////////////
+ // object inspection //
+ ///////////////////////
+
+ /// @name object inspection
+ /// Functions to inspect the type of a JSON value.
+ /// @{
+
+ /// @brief serialization
+ /// @sa https://json.nlohmann.me/api/basic_json/dump/
+ string_t dump(const int indent = -1,
+ const char indent_char = ' ',
+ const bool ensure_ascii = false,
+ const error_handler_t error_handler = error_handler_t::strict) const
+ {
+ string_t result;
+ serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);
+
+ if (indent >= 0)
+ {
+ s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
+ }
+ else
+ {
+ s.dump(*this, false, ensure_ascii, 0);
+ }
+
+ return result;
+ }
+
+ /// @brief return the type of the JSON value (explicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/type/
+ constexpr value_t type() const noexcept
+ {
+ return m_type;
+ }
+
+ /// @brief return whether type is primitive
+ /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/
+ constexpr bool is_primitive() const noexcept
+ {
+ return is_null() || is_string() || is_boolean() || is_number() || is_binary();
+ }
+
+ /// @brief return whether type is structured
+ /// @sa https://json.nlohmann.me/api/basic_json/is_structured/
+ constexpr bool is_structured() const noexcept
+ {
+ return is_array() || is_object();
+ }
+
+ /// @brief return whether value is null
+ /// @sa https://json.nlohmann.me/api/basic_json/is_null/
+ constexpr bool is_null() const noexcept
+ {
+ return m_type == value_t::null;
+ }
+
+ /// @brief return whether value is a boolean
+ /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/
+ constexpr bool is_boolean() const noexcept
+ {
+ return m_type == value_t::boolean;
+ }
+
+ /// @brief return whether value is a number
+ /// @sa https://json.nlohmann.me/api/basic_json/is_number/
+ constexpr bool is_number() const noexcept
+ {
+ return is_number_integer() || is_number_float();
+ }
+
+ /// @brief return whether value is an integer number
+ /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/
+ constexpr bool is_number_integer() const noexcept
+ {
+ return m_type == value_t::number_integer || m_type == value_t::number_unsigned;
+ }
+
+ /// @brief return whether value is an unsigned integer number
+ /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/
+ constexpr bool is_number_unsigned() const noexcept
+ {
+ return m_type == value_t::number_unsigned;
+ }
+
+ /// @brief return whether value is a floating-point number
+ /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/
+ constexpr bool is_number_float() const noexcept
+ {
+ return m_type == value_t::number_float;
+ }
+
+ /// @brief return whether value is an object
+ /// @sa https://json.nlohmann.me/api/basic_json/is_object/
+ constexpr bool is_object() const noexcept
+ {
+ return m_type == value_t::object;
+ }
+
+ /// @brief return whether value is an array
+ /// @sa https://json.nlohmann.me/api/basic_json/is_array/
+ constexpr bool is_array() const noexcept
+ {
+ return m_type == value_t::array;
+ }
+
+ /// @brief return whether value is a string
+ /// @sa https://json.nlohmann.me/api/basic_json/is_string/
+ constexpr bool is_string() const noexcept
+ {
+ return m_type == value_t::string;
+ }
+
+ /// @brief return whether value is a binary array
+ /// @sa https://json.nlohmann.me/api/basic_json/is_binary/
+ constexpr bool is_binary() const noexcept
+ {
+ return m_type == value_t::binary;
+ }
+
+ /// @brief return whether value is discarded
+ /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/
+ constexpr bool is_discarded() const noexcept
+ {
+ return m_type == value_t::discarded;
+ }
+
+ /// @brief return the type of the JSON value (implicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/
+ constexpr operator value_t() const noexcept
+ {
+ return m_type;
+ }
+
+ /// @}
+
+ private:
+ //////////////////
+ // value access //
+ //////////////////
+
+ /// get a boolean (explicit)
+ boolean_t get_impl(boolean_t* /*unused*/) const
+ {
+ if (JSON_HEDLEY_LIKELY(is_boolean()))
+ {
+ return m_value.boolean;
+ }
+
+ JSON_THROW(type_error::create(302, detail::concat("type must be boolean, but is ", type_name()), this));
+ }
+
+ /// get a pointer to the value (object)
+ object_t* get_impl_ptr(object_t* /*unused*/) noexcept
+ {
+ return is_object() ? m_value.object : nullptr;
+ }
+
+ /// get a pointer to the value (object)
+ constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
+ {
+ return is_object() ? m_value.object : nullptr;
+ }
+
+ /// get a pointer to the value (array)
+ array_t* get_impl_ptr(array_t* /*unused*/) noexcept
+ {
+ return is_array() ? m_value.array : nullptr;
+ }
+
+ /// get a pointer to the value (array)
+ constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
+ {
+ return is_array() ? m_value.array : nullptr;
+ }
+
+ /// get a pointer to the value (string)
+ string_t* get_impl_ptr(string_t* /*unused*/) noexcept
+ {
+ return is_string() ? m_value.string : nullptr;
+ }
+
+ /// get a pointer to the value (string)
+ constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept
+ {
+ return is_string() ? m_value.string : nullptr;
+ }
+
+ /// get a pointer to the value (boolean)
+ boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept
+ {
+ return is_boolean() ? &m_value.boolean : nullptr;
+ }
+
+ /// get a pointer to the value (boolean)
+ constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept
+ {
+ return is_boolean() ? &m_value.boolean : nullptr;
+ }
+
+ /// get a pointer to the value (integer number)
+ number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
+ {
+ return is_number_integer() ? &m_value.number_integer : nullptr;
+ }
+
+ /// get a pointer to the value (integer number)
+ constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept
+ {
+ return is_number_integer() ? &m_value.number_integer : nullptr;
+ }
+
+ /// get a pointer to the value (unsigned number)
+ number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
+ {
+ return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
+ }
+
+ /// get a pointer to the value (unsigned number)
+ constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
+ {
+ return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
+ }
+
+ /// get a pointer to the value (floating-point number)
+ number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept
+ {
+ return is_number_float() ? &m_value.number_float : nullptr;
+ }
+
+ /// get a pointer to the value (floating-point number)
+ constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept
+ {
+ return is_number_float() ? &m_value.number_float : nullptr;
+ }
+
+ /// get a pointer to the value (binary)
+ binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept
+ {
+ return is_binary() ? m_value.binary : nullptr;
+ }
+
+ /// get a pointer to the value (binary)
+ constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept
+ {
+ return is_binary() ? m_value.binary : nullptr;
+ }
+
+ /*!
+ @brief helper function to implement get_ref()
+
+ This function helps to implement get_ref() without code duplication for
+ const and non-const overloads
+
+ @tparam ThisType will be deduced as `basic_json` or `const basic_json`
+
+ @throw type_error.303 if ReferenceType does not match underlying value
+ type of the current JSON
+ */
+ template<typename ReferenceType, typename ThisType>
+ static ReferenceType get_ref_impl(ThisType& obj)
+ {
+ // delegate the call to get_ptr<>()
+ auto* ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();
+
+ if (JSON_HEDLEY_LIKELY(ptr != nullptr))
+ {
+ return *ptr;
+ }
+
+ JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
+ }
+
+ public:
+ /// @name value access
+ /// Direct access to the stored value of a JSON value.
+ /// @{
+
+ /// @brief get a pointer value (implicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value, int>::type = 0>
+ auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
+ {
+ // delegate the call to get_impl_ptr<>()
+ return get_impl_ptr(static_cast<PointerType>(nullptr));
+ }
+
+ /// @brief get a pointer value (implicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/
+ template < typename PointerType, typename std::enable_if <
+ std::is_pointer<PointerType>::value&&
+ std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >
+ constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
+ {
+ // delegate the call to get_impl_ptr<>() const
+ return get_impl_ptr(static_cast<PointerType>(nullptr));
+ }
+
+ private:
+ /*!
+ @brief get a value (explicit)
+
+ Explicit type conversion between the JSON value and a compatible value
+ which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
+ and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
+ The value is converted by calling the @ref json_serializer<ValueType>
+ `from_json()` method.
+
+ The function is equivalent to executing
+ @code {.cpp}
+ ValueType ret;
+ JSONSerializer<ValueType>::from_json(*this, ret);
+ return ret;
+ @endcode
+
+ This overloads is chosen if:
+ - @a ValueType is not @ref basic_json,
+ - @ref json_serializer<ValueType> has a `from_json()` method of the form
+ `void from_json(const basic_json&, ValueType&)`, and
+ - @ref json_serializer<ValueType> does not have a `from_json()` method of
+ the form `ValueType from_json(const basic_json&)`
+
+ @tparam ValueType the returned value type
+
+ @return copy of the JSON value, converted to @a ValueType
+
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+ @liveexample{The example below shows several conversions from JSON values
+ to other types. There a few things to note: (1) Floating-point numbers can
+ be converted to integers\, (2) A JSON array can be converted to a standard
+ `std::vector<short>`\, (3) A JSON object can be converted to C++
+ associative containers such as `std::unordered_map<std::string\,
+ json>`.,get__ValueType_const}
+
+ @since version 2.1.0
+ */
+ template < typename ValueType,
+ detail::enable_if_t <
+ detail::is_default_constructible<ValueType>::value&&
+ detail::has_from_json<basic_json_t, ValueType>::value,
+ int > = 0 >
+ ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(
+ JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
+ {
+ auto ret = ValueType();
+ JSONSerializer<ValueType>::from_json(*this, ret);
+ return ret;
+ }
+
+ /*!
+ @brief get a value (explicit); special case
+
+ Explicit type conversion between the JSON value and a compatible value
+ which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
+ and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
+ The value is converted by calling the @ref json_serializer<ValueType>
+ `from_json()` method.
+
+ The function is equivalent to executing
+ @code {.cpp}
+ return JSONSerializer<ValueType>::from_json(*this);
+ @endcode
+
+ This overloads is chosen if:
+ - @a ValueType is not @ref basic_json and
+ - @ref json_serializer<ValueType> has a `from_json()` method of the form
+ `ValueType from_json(const basic_json&)`
+
+ @note If @ref json_serializer<ValueType> has both overloads of
+ `from_json()`, this one is chosen.
+
+ @tparam ValueType the returned value type
+
+ @return copy of the JSON value, converted to @a ValueType
+
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+ @since version 2.1.0
+ */
+ template < typename ValueType,
+ detail::enable_if_t <
+ detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+ int > = 0 >
+ ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(
+ JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))
+ {
+ return JSONSerializer<ValueType>::from_json(*this);
+ }
+
+ /*!
+ @brief get special-case overload
+
+ This overloads converts the current @ref basic_json in a different
+ @ref basic_json type
+
+ @tparam BasicJsonType == @ref basic_json
+
+ @return a copy of *this, converted into @a BasicJsonType
+
+ @complexity Depending on the implementation of the called `from_json()`
+ method.
+
+ @since version 3.2.0
+ */
+ template < typename BasicJsonType,
+ detail::enable_if_t <
+ detail::is_basic_json<BasicJsonType>::value,
+ int > = 0 >
+ BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const
+ {
+ return *this;
+ }
+
+ /*!
+ @brief get special-case overload
+
+ This overloads avoids a lot of template boilerplate, it can be seen as the
+ identity method
+
+ @tparam BasicJsonType == @ref basic_json
+
+ @return a copy of *this
+
+ @complexity Constant.
+
+ @since version 2.1.0
+ */
+ template<typename BasicJsonType,
+ detail::enable_if_t<
+ std::is_same<BasicJsonType, basic_json_t>::value,
+ int> = 0>
+ basic_json get_impl(detail::priority_tag<3> /*unused*/) const
+ {
+ return *this;
+ }
+
+ /*!
+ @brief get a pointer value (explicit)
+ @copydoc get()
+ */
+ template<typename PointerType,
+ detail::enable_if_t<
+ std::is_pointer<PointerType>::value,
+ int> = 0>
+ constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept
+ -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
+ {
+ // delegate the call to get_ptr
+ return get_ptr<PointerType>();
+ }
+
+ public:
+ /*!
+ @brief get a (pointer) value (explicit)
+
+ Performs explicit type conversion between the JSON value and a compatible value if required.
+
+ - If the requested type is a pointer to the internally stored JSON value that pointer is returned.
+ No copies are made.
+
+ - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible
+ from the current @ref basic_json.
+
+ - Otherwise the value is converted by calling the @ref json_serializer<ValueType> `from_json()`
+ method.
+
+ @tparam ValueTypeCV the provided value type
+ @tparam ValueType the returned value type
+
+ @return copy of the JSON value, converted to @tparam ValueType if necessary
+
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
+
+ @since version 2.1.0
+ */
+ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
+#if defined(JSON_HAS_CPP_14)
+ constexpr
+#endif
+ auto get() const noexcept(
+ noexcept(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {})))
+ -> decltype(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {}))
+ {
+ // we cannot static_assert on ValueTypeCV being non-const, because
+ // there is support for get<const basic_json_t>(), which is why we
+ // still need the uncvref
+ static_assert(!std::is_reference<ValueTypeCV>::value,
+ "get() cannot be used with reference types, you might want to use get_ref()");
+ return get_impl<ValueType>(detail::priority_tag<4> {});
+ }
+
+ /*!
+ @brief get a pointer value (explicit)
+
+ Explicit pointer access to the internally stored JSON value. No copies are
+ made.
+
+ @warning The pointer becomes invalid if the underlying JSON object
+ changes.
+
+ @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
+ object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
+ @ref number_unsigned_t, or @ref number_float_t.
+
+ @return pointer to the internally stored JSON value if the requested
+ pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
+
+ @complexity Constant.
+
+ @liveexample{The example below shows how pointers to internal values of a
+ JSON value can be requested. Note that no type conversions are made and a
+ `nullptr` is returned if the value and the requested pointer type does not
+ match.,get__PointerType}
+
+ @sa see @ref get_ptr() for explicit pointer-member access
+
+ @since version 1.0.0
+ */
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value, int>::type = 0>
+ auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())
+ {
+ // delegate the call to get_ptr
+ return get_ptr<PointerType>();
+ }
+
+ /// @brief get a value (explicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/get_to/
+ template < typename ValueType,
+ detail::enable_if_t <
+ !detail::is_basic_json<ValueType>::value&&
+ detail::has_from_json<basic_json_t, ValueType>::value,
+ int > = 0 >
+ ValueType & get_to(ValueType& v) const noexcept(noexcept(
+ JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
+ {
+ JSONSerializer<ValueType>::from_json(*this, v);
+ return v;
+ }
+
+ // specialization to allow calling get_to with a basic_json value
+ // see https://github.com/nlohmann/json/issues/2175
+ template<typename ValueType,
+ detail::enable_if_t <
+ detail::is_basic_json<ValueType>::value,
+ int> = 0>
+ ValueType & get_to(ValueType& v) const
+ {
+ v = *this;
+ return v;
+ }
+
+ template <
+ typename T, std::size_t N,
+ typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+ detail::enable_if_t <
+ detail::has_from_json<basic_json_t, Array>::value, int > = 0 >
+ Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+ noexcept(noexcept(JSONSerializer<Array>::from_json(
+ std::declval<const basic_json_t&>(), v)))
+ {
+ JSONSerializer<Array>::from_json(*this, v);
+ return v;
+ }
+
+ /// @brief get a reference value (implicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/get_ref/
+ template<typename ReferenceType, typename std::enable_if<
+ std::is_reference<ReferenceType>::value, int>::type = 0>
+ ReferenceType get_ref()
+ {
+ // delegate call to get_ref_impl
+ return get_ref_impl<ReferenceType>(*this);
+ }
+
+ /// @brief get a reference value (implicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/get_ref/
+ template < typename ReferenceType, typename std::enable_if <
+ std::is_reference<ReferenceType>::value&&
+ std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >
+ ReferenceType get_ref() const
+ {
+ // delegate call to get_ref_impl
+ return get_ref_impl<ReferenceType>(*this);
+ }
+
+ /*!
+ @brief get a value (implicit)
+
+ Implicit type conversion between the JSON value and a compatible value.
+ The call is realized by calling @ref get() const.
+
+ @tparam ValueType non-pointer type compatible to the JSON value, for
+ instance `int` for JSON integer numbers, `bool` for JSON booleans, or
+ `std::vector` types for JSON arrays. The character type of @ref string_t
+ as well as an initializer list of this type is excluded to avoid
+ ambiguities as these types implicitly convert to `std::string`.
+
+ @return copy of the JSON value, converted to type @a ValueType
+
+ @throw type_error.302 in case passed type @a ValueType is incompatible
+ to the JSON value type (e.g., the JSON value is of type boolean, but a
+ string is requested); see example below
+
+ @complexity Linear in the size of the JSON value.
+
+ @liveexample{The example below shows several conversions from JSON values
+ to other types. There a few things to note: (1) Floating-point numbers can
+ be converted to integers\, (2) A JSON array can be converted to a standard
+ `std::vector<short>`\, (3) A JSON object can be converted to C++
+ associative containers such as `std::unordered_map<std::string\,
+ json>`.,operator__ValueType}
+
+ @since version 1.0.0
+ */
+ template < typename ValueType, typename std::enable_if <
+ detail::conjunction <
+ detail::negation<std::is_pointer<ValueType>>,
+ detail::negation<std::is_same<ValueType, std::nullptr_t>>,
+ detail::negation<std::is_same<ValueType, detail::json_ref<basic_json>>>,
+ detail::negation<std::is_same<ValueType, typename string_t::value_type>>,
+ detail::negation<detail::is_basic_json<ValueType>>,
+ detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,
+#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))
+ detail::negation<std::is_same<ValueType, std::string_view>>,
+#endif
+#if defined(JSON_HAS_CPP_17)
+ detail::negation<std::is_same<ValueType, std::any>>,
+#endif
+ detail::is_detected_lazy<detail::get_template_function, const basic_json_t&, ValueType>
+ >::value, int >::type = 0 >
+ JSON_EXPLICIT operator ValueType() const
+ {
+ // delegate the call to get<>() const
+ return get<ValueType>();
+ }
+
+ /// @brief get a binary value
+ /// @sa https://json.nlohmann.me/api/basic_json/get_binary/
+ binary_t& get_binary()
+ {
+ if (!is_binary())
+ {
+ JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
+ }
+
+ return *get_ptr<binary_t*>();
+ }
+
+ /// @brief get a binary value
+ /// @sa https://json.nlohmann.me/api/basic_json/get_binary/
+ const binary_t& get_binary() const
+ {
+ if (!is_binary())
+ {
+ JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
+ }
+
+ return *get_ptr<const binary_t*>();
+ }
+
+ /// @}
+
+
+ ////////////////////
+ // element access //
+ ////////////////////
+
+ /// @name element access
+ /// Access to the JSON value.
+ /// @{
+
+ /// @brief access specified array element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ reference at(size_type idx)
+ {
+ // at only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ JSON_TRY
+ {
+ return set_parent(m_value.array->at(idx));
+ }
+ JSON_CATCH (std::out_of_range&)
+ {
+ // create better exception explanation
+ JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+ }
+ }
+ else
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+ }
+
+ /// @brief access specified array element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ const_reference at(size_type idx) const
+ {
+ // at only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ JSON_TRY
+ {
+ return m_value.array->at(idx);
+ }
+ JSON_CATCH (std::out_of_range&)
+ {
+ // create better exception explanation
+ JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+ }
+ }
+ else
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+ }
+
+ /// @brief access specified object element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ reference at(const typename object_t::key_type& key)
+ {
+ // at only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+
+ auto it = m_value.object->find(key);
+ if (it == m_value.object->end())
+ {
+ JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
+ }
+ return set_parent(it->second);
+ }
+
+ /// @brief access specified object element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ reference at(KeyType && key)
+ {
+ // at only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+
+ auto it = m_value.object->find(std::forward<KeyType>(key));
+ if (it == m_value.object->end())
+ {
+ JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
+ }
+ return set_parent(it->second);
+ }
+
+ /// @brief access specified object element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ const_reference at(const typename object_t::key_type& key) const
+ {
+ // at only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+
+ auto it = m_value.object->find(key);
+ if (it == m_value.object->end())
+ {
+ JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
+ }
+ return it->second;
+ }
+
+ /// @brief access specified object element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ const_reference at(KeyType && key) const
+ {
+ // at only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+
+ auto it = m_value.object->find(std::forward<KeyType>(key));
+ if (it == m_value.object->end())
+ {
+ JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
+ }
+ return it->second;
+ }
+
+ /// @brief access specified array element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ reference operator[](size_type idx)
+ {
+ // implicitly convert null value to an empty array
+ if (is_null())
+ {
+ m_type = value_t::array;
+ m_value.array = create<array_t>();
+ assert_invariant();
+ }
+
+ // operator[] only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ // fill up array with null values if given idx is outside range
+ if (idx >= m_value.array->size())
+ {
+#if JSON_DIAGNOSTICS
+ // remember array size & capacity before resizing
+ const auto old_size = m_value.array->size();
+ const auto old_capacity = m_value.array->capacity();
+#endif
+ m_value.array->resize(idx + 1);
+
+#if JSON_DIAGNOSTICS
+ if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))
+ {
+ // capacity has changed: update all parents
+ set_parents();
+ }
+ else
+ {
+ // set parent for values added above
+ set_parents(begin() + static_cast<typename iterator::difference_type>(old_size), static_cast<typename iterator::difference_type>(idx + 1 - old_size));
+ }
+#endif
+ assert_invariant();
+ }
+
+ return m_value.array->operator[](idx);
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
+ }
+
+ /// @brief access specified array element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ const_reference operator[](size_type idx) const
+ {
+ // const operator[] only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ return m_value.array->operator[](idx);
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
+ }
+
+ /// @brief access specified object element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ reference operator[](typename object_t::key_type key)
+ {
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ // operator[] only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ auto result = m_value.object->emplace(std::move(key), nullptr);
+ return set_parent(result.first->second);
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+ }
+
+ /// @brief access specified object element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ const_reference operator[](const typename object_t::key_type& key) const
+ {
+ // const operator[] only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ auto it = m_value.object->find(key);
+ JSON_ASSERT(it != m_value.object->end());
+ return it->second;
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+ }
+
+ // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
+ // (they seemingly cannot be constrained to resolve the ambiguity)
+ template<typename T>
+ reference operator[](T* key)
+ {
+ return operator[](typename object_t::key_type(key));
+ }
+
+ template<typename T>
+ const_reference operator[](T* key) const
+ {
+ return operator[](typename object_t::key_type(key));
+ }
+
+ /// @brief access specified object element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
+ reference operator[](KeyType && key)
+ {
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ // operator[] only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ auto result = m_value.object->emplace(std::forward<KeyType>(key), nullptr);
+ return set_parent(result.first->second);
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+ }
+
+ /// @brief access specified object element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
+ const_reference operator[](KeyType && key) const
+ {
+ // const operator[] only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ auto it = m_value.object->find(std::forward<KeyType>(key));
+ JSON_ASSERT(it != m_value.object->end());
+ return it->second;
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+ }
+
+ private:
+ template<typename KeyType>
+ using is_comparable_with_object_key = detail::is_comparable <
+ object_comparator_t, const typename object_t::key_type&, KeyType >;
+
+ template<typename ValueType>
+ using value_return_type = std::conditional <
+ detail::is_c_string_uncvref<ValueType>::value,
+ string_t, typename std::decay<ValueType>::type >;
+
+ public:
+ /// @brief access specified object element with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, detail::enable_if_t <
+ !detail::is_transparent<object_comparator_t>::value
+ && detail::is_getable<basic_json_t, ValueType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if key is found, return value and given default value otherwise
+ const auto it = find(key);
+ if (it != end())
+ {
+ return it->template get<ValueType>();
+ }
+
+ return default_value;
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ /// @brief access specified object element with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,
+ detail::enable_if_t <
+ !detail::is_transparent<object_comparator_t>::value
+ && detail::is_getable<basic_json_t, ReturnType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ReturnType value(const typename object_t::key_type& key, ValueType && default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if key is found, return value and given default value otherwise
+ const auto it = find(key);
+ if (it != end())
+ {
+ return it->template get<ReturnType>();
+ }
+
+ return std::forward<ValueType>(default_value);
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ /// @brief access specified object element with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, class KeyType, detail::enable_if_t <
+ detail::is_transparent<object_comparator_t>::value
+ && !detail::is_json_pointer<KeyType>::value
+ && is_comparable_with_object_key<KeyType>::value
+ && detail::is_getable<basic_json_t, ValueType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ValueType value(KeyType && key, const ValueType& default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if key is found, return value and given default value otherwise
+ const auto it = find(std::forward<KeyType>(key));
+ if (it != end())
+ {
+ return it->template get<ValueType>();
+ }
+
+ return default_value;
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ /// @brief access specified object element via JSON Pointer with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, class KeyType, class ReturnType = typename value_return_type<ValueType>::type,
+ detail::enable_if_t <
+ detail::is_transparent<object_comparator_t>::value
+ && !detail::is_json_pointer<KeyType>::value
+ && is_comparable_with_object_key<KeyType>::value
+ && detail::is_getable<basic_json_t, ReturnType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ReturnType value(KeyType && key, ValueType && default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if key is found, return value and given default value otherwise
+ const auto it = find(std::forward<KeyType>(key));
+ if (it != end())
+ {
+ return it->template get<ReturnType>();
+ }
+
+ return std::forward<ValueType>(default_value);
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ /// @brief access specified object element via JSON Pointer with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, detail::enable_if_t <
+ detail::is_getable<basic_json_t, ValueType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ValueType value(const json_pointer& ptr, const ValueType& default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if pointer resolves a value, return it or use default value
+ JSON_TRY
+ {
+ return ptr.get_checked(this).template get<ValueType>();
+ }
+ JSON_INTERNAL_CATCH (out_of_range&)
+ {
+ return default_value;
+ }
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ /// @brief access specified object element via JSON Pointer with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,
+ detail::enable_if_t <
+ detail::is_getable<basic_json_t, ReturnType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ReturnType value(const json_pointer& ptr, ValueType && default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if pointer resolves a value, return it or use default value
+ JSON_TRY
+ {
+ return ptr.get_checked(this).template get<ReturnType>();
+ }
+ JSON_INTERNAL_CATCH (out_of_range&)
+ {
+ return std::forward<ValueType>(default_value);
+ }
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ template < class ValueType, class BasicJsonType, detail::enable_if_t <
+ detail::is_basic_json<BasicJsonType>::value
+ && detail::is_getable<basic_json_t, ValueType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const
+ {
+ return value(ptr.convert(), default_value);
+ }
+
+ template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
+ detail::enable_if_t <
+ detail::is_basic_json<BasicJsonType>::value
+ && detail::is_getable<basic_json_t, ReturnType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ ReturnType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, ValueType && default_value) const
+ {
+ return value(ptr.convert(), std::forward<ValueType>(default_value));
+ }
+
+ /// @brief access the first element
+ /// @sa https://json.nlohmann.me/api/basic_json/front/
+ reference front()
+ {
+ return *begin();
+ }
+
+ /// @brief access the first element
+ /// @sa https://json.nlohmann.me/api/basic_json/front/
+ const_reference front() const
+ {
+ return *cbegin();
+ }
+
+ /// @brief access the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/back/
+ reference back()
+ {
+ auto tmp = end();
+ --tmp;
+ return *tmp;
+ }
+
+ /// @brief access the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/back/
+ const_reference back() const
+ {
+ auto tmp = cend();
+ --tmp;
+ return *tmp;
+ }
+
+ /// @brief remove element given an iterator
+ /// @sa https://json.nlohmann.me/api/basic_json/erase/
+ template < class IteratorType, detail::enable_if_t <
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >
+ IteratorType erase(IteratorType pos)
+ {
+ // make sure iterator fits the current value
+ if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+ }
+
+ IteratorType result = end();
+
+ switch (m_type)
+ {
+ case value_t::boolean:
+ case value_t::number_float:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::string:
+ case value_t::binary:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
+ {
+ JSON_THROW(invalid_iterator::create(205, "iterator out of range", this));
+ }
+
+ if (is_string())
+ {
+ AllocatorType<string_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
+ m_value.string = nullptr;
+ }
+ else if (is_binary())
+ {
+ AllocatorType<binary_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);
+ m_value.binary = nullptr;
+ }
+
+ m_type = value_t::null;
+ assert_invariant();
+ break;
+ }
+
+ case value_t::object:
+ {
+ result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);
+ break;
+ }
+
+ case value_t::array:
+ {
+ result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::discarded:
+ default:
+ JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+ }
+
+ return result;
+ }
+
+ /// @brief remove elements given an iterator range
+ /// @sa https://json.nlohmann.me/api/basic_json/erase/
+ template < class IteratorType, detail::enable_if_t <
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >
+ IteratorType erase(IteratorType first, IteratorType last)
+ {
+ // make sure iterator fits the current value
+ if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this));
+ }
+
+ IteratorType result = end();
+
+ switch (m_type)
+ {
+ case value_t::boolean:
+ case value_t::number_float:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::string:
+ case value_t::binary:
+ {
+ if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()
+ || !last.m_it.primitive_iterator.is_end()))
+ {
+ JSON_THROW(invalid_iterator::create(204, "iterators out of range", this));
+ }
+
+ if (is_string())
+ {
+ AllocatorType<string_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
+ m_value.string = nullptr;
+ }
+ else if (is_binary())
+ {
+ AllocatorType<binary_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);
+ m_value.binary = nullptr;
+ }
+
+ m_type = value_t::null;
+ assert_invariant();
+ break;
+ }
+
+ case value_t::object:
+ {
+ result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,
+ last.m_it.object_iterator);
+ break;
+ }
+
+ case value_t::array:
+ {
+ result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,
+ last.m_it.array_iterator);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::discarded:
+ default:
+ JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+ }
+
+ return result;
+ }
+
+ private:
+ template < typename KeyType, detail::enable_if_t <
+ detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >
+ size_type erase_internal(KeyType && key)
+ {
+ // this erase only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+ }
+
+ return m_value.object->erase(std::forward<KeyType>(key));
+ }
+
+ template < typename KeyType, detail::enable_if_t <
+ !detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >
+ size_type erase_internal(KeyType && key)
+ {
+ // this erase only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+ }
+
+ const auto it = m_value.object->find(std::forward<KeyType>(key));
+ if (it != m_value.object->end())
+ {
+ m_value.object->erase(it);
+ return 1;
+ }
+ return 0;
+ }
+
+ public:
+
+ /// @brief remove element from a JSON object given a key
+ /// @sa https://json.nlohmann.me/api/basic_json/erase/
+ size_type erase(const typename object_t::key_type& key)
+ {
+ // the indirection via erase_internal() is added to avoid making this
+ // function a template and thus de-rank it during overload resolution
+ return erase_internal(key);
+ }
+
+ /// @brief remove element from a JSON object given a key
+ /// @sa https://json.nlohmann.me/api/basic_json/erase/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ size_type erase(KeyType && key)
+ {
+ return erase_internal(std::forward<KeyType>(key));
+ }
+
+ /// @brief remove element from a JSON array given an index
+ /// @sa https://json.nlohmann.me/api/basic_json/erase/
+ void erase(const size_type idx)
+ {
+ // this erase only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ if (JSON_HEDLEY_UNLIKELY(idx >= size()))
+ {
+ JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+ }
+
+ m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
+ }
+ else
+ {
+ JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+ }
+ }
+
+ /// @}
+
+
+ ////////////
+ // lookup //
+ ////////////
+
+ /// @name lookup
+ /// @{
+
+ /// @brief find an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/find/
+ iterator find(const typename object_t::key_type& key)
+ {
+ auto result = end();
+
+ if (is_object())
+ {
+ result.m_it.object_iterator = m_value.object->find(key);
+ }
+
+ return result;
+ }
+
+ /// @brief find an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/find/
+ const_iterator find(const typename object_t::key_type& key) const
+ {
+ auto result = cend();
+
+ if (is_object())
+ {
+ result.m_it.object_iterator = m_value.object->find(key);
+ }
+
+ return result;
+ }
+
+ /// @brief find an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/find/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ iterator find(KeyType && key)
+ {
+ auto result = end();
+
+ if (is_object())
+ {
+ result.m_it.object_iterator = m_value.object->find(std::forward<KeyType>(key));
+ }
+
+ return result;
+ }
+
+ /// @brief find an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/find/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ const_iterator find(KeyType && key) const
+ {
+ auto result = cend();
+
+ if (is_object())
+ {
+ result.m_it.object_iterator = m_value.object->find(std::forward<KeyType>(key));
+ }
+
+ return result;
+ }
+
+ /// @brief returns the number of occurrences of a key in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/count/
+ size_type count(const typename object_t::key_type& key) const
+ {
+ // return 0 for all nonobject types
+ return is_object() ? m_value.object->count(key) : 0;
+ }
+
+ /// @brief returns the number of occurrences of a key in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/count/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ size_type count(KeyType && key) const
+ {
+ // return 0 for all nonobject types
+ return is_object() ? m_value.object->count(std::forward<KeyType>(key)) : 0;
+ }
+
+ /// @brief check the existence of an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/contains/
+ bool contains(const typename object_t::key_type& key) const
+ {
+ return is_object() && m_value.object->find(key) != m_value.object->end();
+ }
+
+ /// @brief check the existence of an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/contains/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ bool contains(KeyType && key) const
+ {
+ return is_object() && m_value.object->find(std::forward<KeyType>(key)) != m_value.object->end();
+ }
+
+ /// @brief check the existence of an element in a JSON object given a JSON pointer
+ /// @sa https://json.nlohmann.me/api/basic_json/contains/
+ bool contains(const json_pointer& ptr) const
+ {
+ return ptr.contains(this);
+ }
+
+ template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ bool contains(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+ {
+ return ptr.contains(this);
+ }
+
+ /// @}
+
+
+ ///////////////
+ // iterators //
+ ///////////////
+
+ /// @name iterators
+ /// @{
+
+ /// @brief returns an iterator to the first element
+ /// @sa https://json.nlohmann.me/api/basic_json/begin/
+ iterator begin() noexcept
+ {
+ iterator result(this);
+ result.set_begin();
+ return result;
+ }
+
+ /// @brief returns an iterator to the first element
+ /// @sa https://json.nlohmann.me/api/basic_json/begin/
+ const_iterator begin() const noexcept
+ {
+ return cbegin();
+ }
+
+ /// @brief returns a const iterator to the first element
+ /// @sa https://json.nlohmann.me/api/basic_json/cbegin/
+ const_iterator cbegin() const noexcept
+ {
+ const_iterator result(this);
+ result.set_begin();
+ return result;
+ }
+
+ /// @brief returns an iterator to one past the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/end/
+ iterator end() noexcept
+ {
+ iterator result(this);
+ result.set_end();
+ return result;
+ }
+
+ /// @brief returns an iterator to one past the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/end/
+ const_iterator end() const noexcept
+ {
+ return cend();
+ }
+
+ /// @brief returns an iterator to one past the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/cend/
+ const_iterator cend() const noexcept
+ {
+ const_iterator result(this);
+ result.set_end();
+ return result;
+ }
+
+ /// @brief returns an iterator to the reverse-beginning
+ /// @sa https://json.nlohmann.me/api/basic_json/rbegin/
+ reverse_iterator rbegin() noexcept
+ {
+ return reverse_iterator(end());
+ }
+
+ /// @brief returns an iterator to the reverse-beginning
+ /// @sa https://json.nlohmann.me/api/basic_json/rbegin/
+ const_reverse_iterator rbegin() const noexcept
+ {
+ return crbegin();
+ }
+
+ /// @brief returns an iterator to the reverse-end
+ /// @sa https://json.nlohmann.me/api/basic_json/rend/
+ reverse_iterator rend() noexcept
+ {
+ return reverse_iterator(begin());
+ }
+
+ /// @brief returns an iterator to the reverse-end
+ /// @sa https://json.nlohmann.me/api/basic_json/rend/
+ const_reverse_iterator rend() const noexcept
+ {
+ return crend();
+ }
+
+ /// @brief returns a const reverse iterator to the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/crbegin/
+ const_reverse_iterator crbegin() const noexcept
+ {
+ return const_reverse_iterator(cend());
+ }
+
+ /// @brief returns a const reverse iterator to one before the first
+ /// @sa https://json.nlohmann.me/api/basic_json/crend/
+ const_reverse_iterator crend() const noexcept
+ {
+ return const_reverse_iterator(cbegin());
+ }
+
+ public:
+ /// @brief wrapper to access iterator member functions in range-based for
+ /// @sa https://json.nlohmann.me/api/basic_json/items/
+ /// @deprecated This function is deprecated since 3.1.0 and will be removed in
+ /// version 4.0.0 of the library. Please use @ref items() instead;
+ /// that is, replace `json::iterator_wrapper(j)` with `j.items()`.
+ JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
+ static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept
+ {
+ return ref.items();
+ }
+
+ /// @brief wrapper to access iterator member functions in range-based for
+ /// @sa https://json.nlohmann.me/api/basic_json/items/
+ /// @deprecated This function is deprecated since 3.1.0 and will be removed in
+ /// version 4.0.0 of the library. Please use @ref items() instead;
+ /// that is, replace `json::iterator_wrapper(j)` with `j.items()`.
+ JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
+ static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept
+ {
+ return ref.items();
+ }
+
+ /// @brief helper to access iterator member functions in range-based for
+ /// @sa https://json.nlohmann.me/api/basic_json/items/
+ iteration_proxy<iterator> items() noexcept
+ {
+ return iteration_proxy<iterator>(*this);
+ }
+
+ /// @brief helper to access iterator member functions in range-based for
+ /// @sa https://json.nlohmann.me/api/basic_json/items/
+ iteration_proxy<const_iterator> items() const noexcept
+ {
+ return iteration_proxy<const_iterator>(*this);
+ }
+
+ /// @}
+
+
+ //////////////
+ // capacity //
+ //////////////
+
+ /// @name capacity
+ /// @{
+
+ /// @brief checks whether the container is empty.
+ /// @sa https://json.nlohmann.me/api/basic_json/empty/
+ bool empty() const noexcept
+ {
+ switch (m_type)
+ {
+ case value_t::null:
+ {
+ // null values are empty
+ return true;
+ }
+
+ case value_t::array:
+ {
+ // delegate call to array_t::empty()
+ return m_value.array->empty();
+ }
+
+ case value_t::object:
+ {
+ // delegate call to object_t::empty()
+ return m_value.object->empty();
+ }
+
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ // all other types are nonempty
+ return false;
+ }
+ }
+ }
+
+ /// @brief returns the number of elements
+ /// @sa https://json.nlohmann.me/api/basic_json/size/
+ size_type size() const noexcept
+ {
+ switch (m_type)
+ {
+ case value_t::null:
+ {
+ // null values are empty
+ return 0;
+ }
+
+ case value_t::array:
+ {
+ // delegate call to array_t::size()
+ return m_value.array->size();
+ }
+
+ case value_t::object:
+ {
+ // delegate call to object_t::size()
+ return m_value.object->size();
+ }
+
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ // all other types have size 1
+ return 1;
+ }
+ }
+ }
+
+ /// @brief returns the maximum possible number of elements
+ /// @sa https://json.nlohmann.me/api/basic_json/max_size/
+ size_type max_size() const noexcept
+ {
+ switch (m_type)
+ {
+ case value_t::array:
+ {
+ // delegate call to array_t::max_size()
+ return m_value.array->max_size();
+ }
+
+ case value_t::object:
+ {
+ // delegate call to object_t::max_size()
+ return m_value.object->max_size();
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ // all other types have max_size() == size()
+ return size();
+ }
+ }
+ }
+
+ /// @}
+
+
+ ///////////////
+ // modifiers //
+ ///////////////
+
+ /// @name modifiers
+ /// @{
+
+ /// @brief clears the contents
+ /// @sa https://json.nlohmann.me/api/basic_json/clear/
+ void clear() noexcept
+ {
+ switch (m_type)
+ {
+ case value_t::number_integer:
+ {
+ m_value.number_integer = 0;
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ m_value.number_unsigned = 0;
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ m_value.number_float = 0.0;
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ m_value.boolean = false;
+ break;
+ }
+
+ case value_t::string:
+ {
+ m_value.string->clear();
+ break;
+ }
+
+ case value_t::binary:
+ {
+ m_value.binary->clear();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_value.array->clear();
+ break;
+ }
+
+ case value_t::object:
+ {
+ m_value.object->clear();
+ break;
+ }
+
+ case value_t::null:
+ case value_t::discarded:
+ default:
+ break;
+ }
+ }
+
+ /// @brief add an object to an array
+ /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+ void push_back(basic_json&& val)
+ {
+ // push_back only works for null objects or arrays
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+ {
+ JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+ }
+
+ // transform null object into an array
+ if (is_null())
+ {
+ m_type = value_t::array;
+ m_value = value_t::array;
+ assert_invariant();
+ }
+
+ // add element to array (move semantics)
+ const auto old_capacity = m_value.array->capacity();
+ m_value.array->push_back(std::move(val));
+ set_parent(m_value.array->back(), old_capacity);
+ // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor
+ }
+
+ /// @brief add an object to an array
+ /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+ reference operator+=(basic_json&& val)
+ {
+ push_back(std::move(val));
+ return *this;
+ }
+
+ /// @brief add an object to an array
+ /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+ void push_back(const basic_json& val)
+ {
+ // push_back only works for null objects or arrays
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+ {
+ JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+ }
+
+ // transform null object into an array
+ if (is_null())
+ {
+ m_type = value_t::array;
+ m_value = value_t::array;
+ assert_invariant();
+ }
+
+ // add element to array
+ const auto old_capacity = m_value.array->capacity();
+ m_value.array->push_back(val);
+ set_parent(m_value.array->back(), old_capacity);
+ }
+
+ /// @brief add an object to an array
+ /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+ reference operator+=(const basic_json& val)
+ {
+ push_back(val);
+ return *this;
+ }
+
+ /// @brief add an object to an object
+ /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+ void push_back(const typename object_t::value_type& val)
+ {
+ // push_back only works for null objects or objects
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
+ {
+ JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+ }
+
+ // transform null object into an object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value = value_t::object;
+ assert_invariant();
+ }
+
+ // add element to object
+ auto res = m_value.object->insert(val);
+ set_parent(res.first->second);
+ }
+
+ /// @brief add an object to an object
+ /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+ reference operator+=(const typename object_t::value_type& val)
+ {
+ push_back(val);
+ return *this;
+ }
+
+ /// @brief add an object to an object
+ /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+ void push_back(initializer_list_t init)
+ {
+ if (is_object() && init.size() == 2 && (*init.begin())->is_string())
+ {
+ basic_json&& key = init.begin()->moved_or_copied();
+ push_back(typename object_t::value_type(
+ std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));
+ }
+ else
+ {
+ push_back(basic_json(init));
+ }
+ }
+
+ /// @brief add an object to an object
+ /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+ reference operator+=(initializer_list_t init)
+ {
+ push_back(init);
+ return *this;
+ }
+
+ /// @brief add an object to an array
+ /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/
+ template<class... Args>
+ reference emplace_back(Args&& ... args)
+ {
+ // emplace_back only works for null objects or arrays
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+ {
+ JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this));
+ }
+
+ // transform null object into an array
+ if (is_null())
+ {
+ m_type = value_t::array;
+ m_value = value_t::array;
+ assert_invariant();
+ }
+
+ // add element to array (perfect forwarding)
+ const auto old_capacity = m_value.array->capacity();
+ m_value.array->emplace_back(std::forward<Args>(args)...);
+ return set_parent(m_value.array->back(), old_capacity);
+ }
+
+ /// @brief add an object to an object if key does not exist
+ /// @sa https://json.nlohmann.me/api/basic_json/emplace/
+ template<class... Args>
+ std::pair<iterator, bool> emplace(Args&& ... args)
+ {
+ // emplace only works for null objects or arrays
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
+ {
+ JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this));
+ }
+
+ // transform null object into an object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value = value_t::object;
+ assert_invariant();
+ }
+
+ // add element to array (perfect forwarding)
+ auto res = m_value.object->emplace(std::forward<Args>(args)...);
+ set_parent(res.first->second);
+
+ // create result iterator and set iterator to the result of emplace
+ auto it = begin();
+ it.m_it.object_iterator = res.first;
+
+ // return pair of iterator and boolean
+ return {it, res.second};
+ }
+
+ /// Helper for insertion of an iterator
+ /// @note: This uses std::distance to support GCC 4.8,
+ /// see https://github.com/nlohmann/json/pull/1257
+ template<typename... Args>
+ iterator insert_iterator(const_iterator pos, Args&& ... args)
+ {
+ iterator result(this);
+ JSON_ASSERT(m_value.array != nullptr);
+
+ auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);
+ m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);
+ result.m_it.array_iterator = m_value.array->begin() + insert_pos;
+
+ // This could have been written as:
+ // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
+ // but the return value of insert is missing in GCC 4.8, so it is written this way instead.
+
+ set_parents();
+ return result;
+ }
+
+ /// @brief inserts element into array
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ iterator insert(const_iterator pos, const basic_json& val)
+ {
+ // insert only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ // check if iterator pos fits to this JSON value
+ if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+ }
+
+ // insert to array and return iterator
+ return insert_iterator(pos, val);
+ }
+
+ JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+ }
+
+ /// @brief inserts element into array
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ iterator insert(const_iterator pos, basic_json&& val)
+ {
+ return insert(pos, val);
+ }
+
+ /// @brief inserts copies of element into array
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
+ {
+ // insert only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ // check if iterator pos fits to this JSON value
+ if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+ }
+
+ // insert to array and return iterator
+ return insert_iterator(pos, cnt, val);
+ }
+
+ JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+ }
+
+ /// @brief inserts range of elements into array
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ iterator insert(const_iterator pos, const_iterator first, const_iterator last)
+ {
+ // insert only works for arrays
+ if (JSON_HEDLEY_UNLIKELY(!is_array()))
+ {
+ JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+ }
+
+ // check if iterator pos fits to this JSON value
+ if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
+ {
+ JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this));
+ }
+
+ // insert to array and return iterator
+ return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);
+ }
+
+ /// @brief inserts elements from initializer list into array
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ iterator insert(const_iterator pos, initializer_list_t ilist)
+ {
+ // insert only works for arrays
+ if (JSON_HEDLEY_UNLIKELY(!is_array()))
+ {
+ JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+ }
+
+ // check if iterator pos fits to this JSON value
+ if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+ }
+
+ // insert to array and return iterator
+ return insert_iterator(pos, ilist.begin(), ilist.end());
+ }
+
+ /// @brief inserts range of elements into object
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ void insert(const_iterator first, const_iterator last)
+ {
+ // insert only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+ }
+
+ // passed iterators must belong to objects
+ if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this));
+ }
+
+ m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
+ }
+
+ /// @brief updates a JSON object from another object, overwriting existing keys
+ /// @sa https://json.nlohmann.me/api/basic_json/update/
+ void update(const_reference j, bool merge_objects = false)
+ {
+ update(j.begin(), j.end(), merge_objects);
+ }
+
+ /// @brief updates a JSON object from another object, overwriting existing keys
+ /// @sa https://json.nlohmann.me/api/basic_json/update/
+ void update(const_iterator first, const_iterator last, bool merge_objects = false)
+ {
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+ }
+
+ // passed iterators must belong to objects
+ if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
+ {
+ JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
+ }
+
+ for (auto it = first; it != last; ++it)
+ {
+ if (merge_objects && it.value().is_object())
+ {
+ auto it2 = m_value.object->find(it.key());
+ if (it2 != m_value.object->end())
+ {
+ it2->second.update(it.value(), true);
+ continue;
+ }
+ }
+ m_value.object->operator[](it.key()) = it.value();
+#if JSON_DIAGNOSTICS
+ m_value.object->operator[](it.key()).m_parent = this;
+#endif
+ }
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(reference other) noexcept (
+ std::is_nothrow_move_constructible<value_t>::value&&
+ std::is_nothrow_move_assignable<value_t>::value&&
+ std::is_nothrow_move_constructible<json_value>::value&&
+ std::is_nothrow_move_assignable<json_value>::value
+ )
+ {
+ std::swap(m_type, other.m_type);
+ std::swap(m_value, other.m_value);
+
+ set_parents();
+ other.set_parents();
+ assert_invariant();
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ friend void swap(reference left, reference right) noexcept (
+ std::is_nothrow_move_constructible<value_t>::value&&
+ std::is_nothrow_move_assignable<value_t>::value&&
+ std::is_nothrow_move_constructible<json_value>::value&&
+ std::is_nothrow_move_assignable<json_value>::value
+ )
+ {
+ left.swap(right);
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(array_t& other) // NOLINT(bugprone-exception-escape)
+ {
+ // swap only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ using std::swap;
+ swap(*(m_value.array), other);
+ }
+ else
+ {
+ JSON_THROW(type_error::create(310, detail::concat("cannot use swap(array_t&) with ", type_name()), this));
+ }
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(object_t& other) // NOLINT(bugprone-exception-escape)
+ {
+ // swap only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ using std::swap;
+ swap(*(m_value.object), other);
+ }
+ else
+ {
+ JSON_THROW(type_error::create(310, detail::concat("cannot use swap(object_t&) with ", type_name()), this));
+ }
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(string_t& other) // NOLINT(bugprone-exception-escape)
+ {
+ // swap only works for strings
+ if (JSON_HEDLEY_LIKELY(is_string()))
+ {
+ using std::swap;
+ swap(*(m_value.string), other);
+ }
+ else
+ {
+ JSON_THROW(type_error::create(310, detail::concat("cannot use swap(string_t&) with ", type_name()), this));
+ }
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(binary_t& other) // NOLINT(bugprone-exception-escape)
+ {
+ // swap only works for strings
+ if (JSON_HEDLEY_LIKELY(is_binary()))
+ {
+ using std::swap;
+ swap(*(m_value.binary), other);
+ }
+ else
+ {
+ JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t&) with ", type_name()), this));
+ }
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)
+ {
+ // swap only works for strings
+ if (JSON_HEDLEY_LIKELY(is_binary()))
+ {
+ using std::swap;
+ swap(*(m_value.binary), other);
+ }
+ else
+ {
+ JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t::container_type&) with ", type_name()), this));
+ }
+ }
+
+ /// @}
+
+ //////////////////////////////////////////
+ // lexicographical comparison operators //
+ //////////////////////////////////////////
+
+ /// @name lexicographical comparison operators
+ /// @{
+
+ // note parentheses around operands are necessary; see
+ // https://github.com/nlohmann/json/issues/1530
+#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result) \
+ const auto lhs_type = lhs.type(); \
+ const auto rhs_type = rhs.type(); \
+ \
+ if (lhs_type == rhs_type) /* NOLINT(readability/braces) */ \
+ { \
+ switch (lhs_type) \
+ { \
+ case value_t::array: \
+ return (*lhs.m_value.array) op (*rhs.m_value.array); \
+ \
+ case value_t::object: \
+ return (*lhs.m_value.object) op (*rhs.m_value.object); \
+ \
+ case value_t::null: \
+ return (null_result); \
+ \
+ case value_t::string: \
+ return (*lhs.m_value.string) op (*rhs.m_value.string); \
+ \
+ case value_t::boolean: \
+ return (lhs.m_value.boolean) op (rhs.m_value.boolean); \
+ \
+ case value_t::number_integer: \
+ return (lhs.m_value.number_integer) op (rhs.m_value.number_integer); \
+ \
+ case value_t::number_unsigned: \
+ return (lhs.m_value.number_unsigned) op (rhs.m_value.number_unsigned); \
+ \
+ case value_t::number_float: \
+ return (lhs.m_value.number_float) op (rhs.m_value.number_float); \
+ \
+ case value_t::binary: \
+ return (*lhs.m_value.binary) op (*rhs.m_value.binary); \
+ \
+ case value_t::discarded: \
+ default: \
+ return (unordered_result); \
+ } \
+ } \
+ else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) \
+ { \
+ return static_cast<number_float_t>(lhs.m_value.number_integer) op rhs.m_value.number_float; \
+ } \
+ else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) \
+ { \
+ return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_integer); \
+ } \
+ else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) \
+ { \
+ return static_cast<number_float_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_float; \
+ } \
+ else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) \
+ { \
+ return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_unsigned); \
+ } \
+ else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) \
+ { \
+ return static_cast<number_integer_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_integer; \
+ } \
+ else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) \
+ { \
+ return lhs.m_value.number_integer op static_cast<number_integer_t>(rhs.m_value.number_unsigned); \
+ } \
+ else if(compares_unordered(lhs, rhs))\
+ {\
+ return (unordered_result);\
+ }\
+ \
+ return (default_result);
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ // returns true if:
+ // - any operand is NaN and the other operand is of number type
+ // - any operand is discarded
+ // in legacy mode, discarded values are considered ordered if
+ // an operation is computed as an odd number of inverses of others
+ static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept
+ {
+ if ((lhs.is_number_float() && std::isnan(lhs.m_value.number_float) && rhs.is_number())
+ || (rhs.is_number_float() && std::isnan(rhs.m_value.number_float) && lhs.is_number()))
+ {
+ return true;
+ }
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+ return (lhs.is_discarded() || rhs.is_discarded()) && !inverse;
+#else
+ static_cast<void>(inverse);
+ return lhs.is_discarded() || rhs.is_discarded();
+#endif
+ }
+
+ private:
+ bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept
+ {
+ return compares_unordered(*this, rhs, inverse);
+ }
+
+ public:
+#if JSON_HAS_THREE_WAY_COMPARISON
+ /// @brief comparison: equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+ bool operator==(const_reference rhs) const noexcept
+ {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ const_reference lhs = *this;
+ JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ }
+
+ /// @brief comparison: equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+ template<typename ScalarType>
+ requires std::is_scalar_v<ScalarType>
+ bool operator==(ScalarType rhs) const noexcept
+ {
+ return *this == basic_json(rhs);
+ }
+
+ /// @brief comparison: not equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+ bool operator!=(const_reference rhs) const noexcept
+ {
+ if (compares_unordered(rhs, true))
+ {
+ return false;
+ }
+ return !operator==(rhs);
+ }
+
+ /// @brief comparison: 3-way
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+ std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
+ {
+ const_reference lhs = *this;
+ // default_result is used if we cannot compare values. In that case,
+ // we compare types.
+ JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
+ std::partial_ordering::equivalent,
+ std::partial_ordering::unordered,
+ lhs_type <=> rhs_type) // *NOPAD*
+ }
+
+ /// @brief comparison: 3-way
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+ template<typename ScalarType>
+ requires std::is_scalar_v<ScalarType>
+ std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD*
+ {
+ return *this <=> basic_json(rhs); // *NOPAD*
+ }
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+ // all operators that are computed as an odd number of inverses of others
+ // need to be overloaded to emulate the legacy comparison behavior
+
+ /// @brief comparison: less than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+ bool operator<=(const_reference rhs) const noexcept
+ {
+ if (compares_unordered(rhs, true))
+ {
+ return false;
+ }
+ return !(rhs < *this);
+ }
+
+ /// @brief comparison: less than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+ template<typename ScalarType>
+ requires std::is_scalar_v<ScalarType>
+ bool operator<=(ScalarType rhs) const noexcept
+ {
+ return *this <= basic_json(rhs);
+ }
+
+ /// @brief comparison: greater than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+ bool operator>=(const_reference rhs) const noexcept
+ {
+ if (compares_unordered(rhs, true))
+ {
+ return false;
+ }
+ return !(*this < rhs);
+ }
+
+ /// @brief comparison: greater than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+ template<typename ScalarType>
+ requires std::is_scalar_v<ScalarType>
+ bool operator>=(ScalarType rhs) const noexcept
+ {
+ return *this >= basic_json(rhs);
+ }
+#endif
+#else
+ /// @brief comparison: equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+ friend bool operator==(const_reference lhs, const_reference rhs) noexcept
+ {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ }
+
+ /// @brief comparison: equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs == basic_json(rhs);
+ }
+
+ /// @brief comparison: equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) == rhs;
+ }
+
+ /// @brief comparison: not equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+ friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
+ {
+ if (compares_unordered(lhs, rhs, true))
+ {
+ return false;
+ }
+ return !(lhs == rhs);
+ }
+
+ /// @brief comparison: not equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs != basic_json(rhs);
+ }
+
+ /// @brief comparison: not equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) != rhs;
+ }
+
+ /// @brief comparison: less than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+ friend bool operator<(const_reference lhs, const_reference rhs) noexcept
+ {
+ // default_result is used if we cannot compare values. In that case,
+ // we compare types. Note we have to call the operator explicitly,
+ // because MSVC has problems otherwise.
+ JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))
+ }
+
+ /// @brief comparison: less than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs < basic_json(rhs);
+ }
+
+ /// @brief comparison: less than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) < rhs;
+ }
+
+ /// @brief comparison: less than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+ friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
+ {
+ if (compares_unordered(lhs, rhs, true))
+ {
+ return false;
+ }
+ return !(rhs < lhs);
+ }
+
+ /// @brief comparison: less than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs <= basic_json(rhs);
+ }
+
+ /// @brief comparison: less than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) <= rhs;
+ }
+
+ /// @brief comparison: greater than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
+ friend bool operator>(const_reference lhs, const_reference rhs) noexcept
+ {
+ // double inverse
+ if (compares_unordered(lhs, rhs))
+ {
+ return false;
+ }
+ return !(lhs <= rhs);
+ }
+
+ /// @brief comparison: greater than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs > basic_json(rhs);
+ }
+
+ /// @brief comparison: greater than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) > rhs;
+ }
+
+ /// @brief comparison: greater than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+ friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
+ {
+ if (compares_unordered(lhs, rhs, true))
+ {
+ return false;
+ }
+ return !(lhs < rhs);
+ }
+
+ /// @brief comparison: greater than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs >= basic_json(rhs);
+ }
+
+ /// @brief comparison: greater than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) >= rhs;
+ }
+#endif
+
+#undef JSON_IMPLEMENT_OPERATOR
+
+ /// @}
+
+ ///////////////////
+ // serialization //
+ ///////////////////
+
+ /// @name serialization
+ /// @{
+#ifndef JSON_NO_IO
+ /// @brief serialize to stream
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+ friend std::ostream& operator<<(std::ostream& o, const basic_json& j)
+ {
+ // read width member and use it as indentation parameter if nonzero
+ const bool pretty_print = o.width() > 0;
+ const auto indentation = pretty_print ? o.width() : 0;
+
+ // reset width to 0 for subsequent calls to this stream
+ o.width(0);
+
+ // do the actual serialization
+ serializer s(detail::output_adapter<char>(o), o.fill());
+ s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));
+ return o;
+ }
+
+ /// @brief serialize to stream
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+ /// @deprecated This function is deprecated since 3.0.0 and will be removed in
+ /// version 4.0.0 of the library. Please use
+ /// operator<<(std::ostream&, const basic_json&) instead; that is,
+ /// replace calls like `j >> o;` with `o << j;`.
+ JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&))
+ friend std::ostream& operator>>(const basic_json& j, std::ostream& o)
+ {
+ return o << j;
+ }
+#endif // JSON_NO_IO
+ /// @}
+
+
+ /////////////////////
+ // deserialization //
+ /////////////////////
+
+ /// @name deserialization
+ /// @{
+
+ /// @brief deserialize from a compatible input
+ /// @sa https://json.nlohmann.me/api/basic_json/parse/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json parse(InputType&& i,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false)
+ {
+ basic_json result;
+ parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result);
+ return result;
+ }
+
+ /// @brief deserialize from a pair of character iterators
+ /// @sa https://json.nlohmann.me/api/basic_json/parse/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json parse(IteratorType first,
+ IteratorType last,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false)
+ {
+ basic_json result;
+ parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result);
+ return result;
+ }
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
+ static basic_json parse(detail::span_input_adapter&& i,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false)
+ {
+ basic_json result;
+ parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result);
+ return result;
+ }
+
+ /// @brief check if the input is valid JSON
+ /// @sa https://json.nlohmann.me/api/basic_json/accept/
+ template<typename InputType>
+ static bool accept(InputType&& i,
+ const bool ignore_comments = false)
+ {
+ return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);
+ }
+
+ /// @brief check if the input is valid JSON
+ /// @sa https://json.nlohmann.me/api/basic_json/accept/
+ template<typename IteratorType>
+ static bool accept(IteratorType first, IteratorType last,
+ const bool ignore_comments = false)
+ {
+ return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);
+ }
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
+ static bool accept(detail::span_input_adapter&& i,
+ const bool ignore_comments = false)
+ {
+ return parser(i.get(), nullptr, false, ignore_comments).accept(true);
+ }
+
+ /// @brief generate SAX events
+ /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+ template <typename InputType, typename SAX>
+ JSON_HEDLEY_NON_NULL(2)
+ static bool sax_parse(InputType&& i, SAX* sax,
+ input_format_t format = input_format_t::json,
+ const bool strict = true,
+ const bool ignore_comments = false)
+ {
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ return format == input_format_t::json
+ ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+ : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+ }
+
+ /// @brief generate SAX events
+ /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+ template<class IteratorType, class SAX>
+ JSON_HEDLEY_NON_NULL(3)
+ static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
+ input_format_t format = input_format_t::json,
+ const bool strict = true,
+ const bool ignore_comments = false)
+ {
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ return format == input_format_t::json
+ ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+ : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+ }
+
+ /// @brief generate SAX events
+ /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+ /// @deprecated This function is deprecated since 3.8.0 and will be removed in
+ /// version 4.0.0 of the library. Please use
+ /// sax_parse(ptr, ptr + len) instead.
+ template <typename SAX>
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))
+ JSON_HEDLEY_NON_NULL(2)
+ static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,
+ input_format_t format = input_format_t::json,
+ const bool strict = true,
+ const bool ignore_comments = false)
+ {
+ auto ia = i.get();
+ return format == input_format_t::json
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+ }
+#ifndef JSON_NO_IO
+ /// @brief deserialize from stream
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
+ /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in
+ /// version 4.0.0 of the library. Please use
+ /// operator>>(std::istream&, basic_json&) instead; that is,
+ /// replace calls like `j << i;` with `i >> j;`.
+ JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&))
+ friend std::istream& operator<<(basic_json& j, std::istream& i)
+ {
+ return operator>>(i, j);
+ }
+
+ /// @brief deserialize from stream
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
+ friend std::istream& operator>>(std::istream& i, basic_json& j)
+ {
+ parser(detail::input_adapter(i)).parse(false, j);
+ return i;
+ }
+#endif // JSON_NO_IO
+ /// @}
+
+ ///////////////////////////
+ // convenience functions //
+ ///////////////////////////
+
+ /// @brief return the type as string
+ /// @sa https://json.nlohmann.me/api/basic_json/type_name/
+ JSON_HEDLEY_RETURNS_NON_NULL
+ const char* type_name() const noexcept
+ {
+ switch (m_type)
+ {
+ case value_t::null:
+ return "null";
+ case value_t::object:
+ return "object";
+ case value_t::array:
+ return "array";
+ case value_t::string:
+ return "string";
+ case value_t::boolean:
+ return "boolean";
+ case value_t::binary:
+ return "binary";
+ case value_t::discarded:
+ return "discarded";
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ default:
+ return "number";
+ }
+ }
+
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ //////////////////////
+ // member variables //
+ //////////////////////
+
+ /// the type of the current element
+ value_t m_type = value_t::null;
+
+ /// the value of the current element
+ json_value m_value = {};
+
+#if JSON_DIAGNOSTICS
+ /// a pointer to a parent value (for debugging purposes)
+ basic_json* m_parent = nullptr;
+#endif
+
+ //////////////////////////////////////////
+ // binary serialization/deserialization //
+ //////////////////////////////////////////
+
+ /// @name binary serialization/deserialization support
+ /// @{
+
+ public:
+ /// @brief create a CBOR serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+ static std::vector<std::uint8_t> to_cbor(const basic_json& j)
+ {
+ std::vector<std::uint8_t> result;
+ to_cbor(j, result);
+ return result;
+ }
+
+ /// @brief create a CBOR serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+ static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+ {
+ binary_writer<std::uint8_t>(o).write_cbor(j);
+ }
+
+ /// @brief create a CBOR serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+ static void to_cbor(const basic_json& j, detail::output_adapter<char> o)
+ {
+ binary_writer<char>(o).write_cbor(j);
+ }
+
+ /// @brief create a MessagePack serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+ static std::vector<std::uint8_t> to_msgpack(const basic_json& j)
+ {
+ std::vector<std::uint8_t> result;
+ to_msgpack(j, result);
+ return result;
+ }
+
+ /// @brief create a MessagePack serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+ static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+ {
+ binary_writer<std::uint8_t>(o).write_msgpack(j);
+ }
+
+ /// @brief create a MessagePack serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+ static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)
+ {
+ binary_writer<char>(o).write_msgpack(j);
+ }
+
+ /// @brief create a UBJSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+ static std::vector<std::uint8_t> to_ubjson(const basic_json& j,
+ const bool use_size = false,
+ const bool use_type = false)
+ {
+ std::vector<std::uint8_t> result;
+ to_ubjson(j, result, use_size, use_type);
+ return result;
+ }
+
+ /// @brief create a UBJSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+ static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,
+ const bool use_size = false, const bool use_type = false)
+ {
+ binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type);
+ }
+
+ /// @brief create a UBJSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+ static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
+ const bool use_size = false, const bool use_type = false)
+ {
+ binary_writer<char>(o).write_ubjson(j, use_size, use_type);
+ }
+
+ /// @brief create a BJData serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+ static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
+ const bool use_size = false,
+ const bool use_type = false)
+ {
+ std::vector<std::uint8_t> result;
+ to_bjdata(j, result, use_size, use_type);
+ return result;
+ }
+
+ /// @brief create a BJData serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+ static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
+ const bool use_size = false, const bool use_type = false)
+ {
+ binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true);
+ }
+
+ /// @brief create a BJData serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+ static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
+ const bool use_size = false, const bool use_type = false)
+ {
+ binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true);
+ }
+
+ /// @brief create a BSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+ static std::vector<std::uint8_t> to_bson(const basic_json& j)
+ {
+ std::vector<std::uint8_t> result;
+ to_bson(j, result);
+ return result;
+ }
+
+ /// @brief create a BSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+ static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+ {
+ binary_writer<std::uint8_t>(o).write_bson(j);
+ }
+
+ /// @brief create a BSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+ static void to_bson(const basic_json& j, detail::output_adapter<char> o)
+ {
+ binary_writer<char>(o).write_bson(j);
+ }
+
+ /// @brief create a JSON value from an input in CBOR format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_cbor(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in CBOR format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_cbor(IteratorType first, IteratorType last,
+ const bool strict = true,
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ template<typename T>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
+ static basic_json from_cbor(const T* ptr, std::size_t len,
+ const bool strict = true,
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+ {
+ return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);
+ }
+
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
+ static basic_json from_cbor(detail::span_input_adapter&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = i.get();
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in MessagePack format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_msgpack(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in MessagePack format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_msgpack(IteratorType first, IteratorType last,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ template<typename T>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
+ static basic_json from_msgpack(const T* ptr, std::size_t len,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ return from_msgpack(ptr, ptr + len, strict, allow_exceptions);
+ }
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
+ static basic_json from_msgpack(detail::span_input_adapter&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = i.get();
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in UBJSON format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_ubjson(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in UBJSON format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_ubjson(IteratorType first, IteratorType last,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ template<typename T>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
+ static basic_json from_ubjson(const T* ptr, std::size_t len,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ return from_ubjson(ptr, ptr + len, strict, allow_exceptions);
+ }
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
+ static basic_json from_ubjson(detail::span_input_adapter&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = i.get();
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+
+ /// @brief create a JSON value from an input in BJData format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_bjdata(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in BJData format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_bjdata(IteratorType first, IteratorType last,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in BSON format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_bson/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_bson(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in BSON format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_bson/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_bson(IteratorType first, IteratorType last,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ template<typename T>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
+ static basic_json from_bson(const T* ptr, std::size_t len,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ return from_bson(ptr, ptr + len, strict, allow_exceptions);
+ }
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
+ static basic_json from_bson(detail::span_input_adapter&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
+ auto ia = i.get();
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);
+ return res ? result : basic_json(value_t::discarded);
+ }
+ /// @}
+
+ //////////////////////////
+ // JSON Pointer support //
+ //////////////////////////
+
+ /// @name JSON Pointer functions
+ /// @{
+
+ /// @brief access specified element via JSON Pointer
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ reference operator[](const json_pointer& ptr)
+ {
+ return ptr.get_unchecked(this);
+ }
+
+ template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)
+ {
+ return ptr.get_unchecked(this);
+ }
+
+ /// @brief access specified element via JSON Pointer
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ const_reference operator[](const json_pointer& ptr) const
+ {
+ return ptr.get_unchecked(this);
+ }
+
+ template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+ {
+ return ptr.get_unchecked(this);
+ }
+
+ /// @brief access specified element via JSON Pointer
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ reference at(const json_pointer& ptr)
+ {
+ return ptr.get_checked(this);
+ }
+
+ template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)
+ {
+ return ptr.get_checked(this);
+ }
+
+ /// @brief access specified element via JSON Pointer
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ const_reference at(const json_pointer& ptr) const
+ {
+ return ptr.get_checked(this);
+ }
+
+ template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+ {
+ return ptr.get_checked(this);
+ }
+
+ /// @brief return flattened JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/flatten/
+ basic_json flatten() const
+ {
+ basic_json result(value_t::object);
+ json_pointer::flatten("", *this, result);
+ return result;
+ }
+
+ /// @brief unflatten a previously flattened JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/unflatten/
+ basic_json unflatten() const
+ {
+ return json_pointer::unflatten(*this);
+ }
+
+ /// @}
+
+ //////////////////////////
+ // JSON Patch functions //
+ //////////////////////////
+
+ /// @name JSON Patch functions
+ /// @{
+
+ /// @brief applies a JSON patch in-place without copying the object
+ /// @sa https://json.nlohmann.me/api/basic_json/patch/
+ void patch_inplace(const basic_json& json_patch)
+ {
+ basic_json& result = *this;
+ // the valid JSON Patch operations
+ enum class patch_operations {add, remove, replace, move, copy, test, invalid};
+
+ const auto get_op = [](const std::string & op)
+ {
+ if (op == "add")
+ {
+ return patch_operations::add;
+ }
+ if (op == "remove")
+ {
+ return patch_operations::remove;
+ }
+ if (op == "replace")
+ {
+ return patch_operations::replace;
+ }
+ if (op == "move")
+ {
+ return patch_operations::move;
+ }
+ if (op == "copy")
+ {
+ return patch_operations::copy;
+ }
+ if (op == "test")
+ {
+ return patch_operations::test;
+ }
+
+ return patch_operations::invalid;
+ };
+
+ // wrapper for "add" operation; add value at ptr
+ const auto operation_add = [&result](json_pointer & ptr, basic_json val)
+ {
+ // adding to the root of the target document means replacing it
+ if (ptr.empty())
+ {
+ result = val;
+ return;
+ }
+
+ // make sure the top element of the pointer exists
+ json_pointer const top_pointer = ptr.top();
+ if (top_pointer != ptr)
+ {
+ result.at(top_pointer);
+ }
+
+ // get reference to parent of JSON pointer ptr
+ const auto last_path = ptr.back();
+ ptr.pop_back();
+ // parent must exist when performing patch add per RFC6902 specs
+ basic_json& parent = result.at(ptr);
+
+ switch (parent.m_type)
+ {
+ case value_t::null:
+ case value_t::object:
+ {
+ // use operator[] to add value
+ parent[last_path] = val;
+ break;
+ }
+
+ case value_t::array:
+ {
+ if (last_path == "-")
+ {
+ // special case: append to back
+ parent.push_back(val);
+ }
+ else
+ {
+ const auto idx = json_pointer::template array_index<basic_json_t>(last_path);
+ if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
+ {
+ // avoid undefined behavior
+ JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent));
+ }
+
+ // default case: insert add offset
+ parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
+ }
+ break;
+ }
+
+ // if there exists a parent it cannot be primitive
+ case value_t::string: // LCOV_EXCL_LINE
+ case value_t::boolean: // LCOV_EXCL_LINE
+ case value_t::number_integer: // LCOV_EXCL_LINE
+ case value_t::number_unsigned: // LCOV_EXCL_LINE
+ case value_t::number_float: // LCOV_EXCL_LINE
+ case value_t::binary: // LCOV_EXCL_LINE
+ case value_t::discarded: // LCOV_EXCL_LINE
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+ };
+
+ // wrapper for "remove" operation; remove value at ptr
+ const auto operation_remove = [this, &result](json_pointer & ptr)
+ {
+ // get reference to parent of JSON pointer ptr
+ const auto last_path = ptr.back();
+ ptr.pop_back();
+ basic_json& parent = result.at(ptr);
+
+ // remove child
+ if (parent.is_object())
+ {
+ // perform range check
+ auto it = parent.find(last_path);
+ if (JSON_HEDLEY_LIKELY(it != parent.end()))
+ {
+ parent.erase(it);
+ }
+ else
+ {
+ JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this));
+ }
+ }
+ else if (parent.is_array())
+ {
+ // note erase performs range check
+ parent.erase(json_pointer::template array_index<basic_json_t>(last_path));
+ }
+ };
+
+ // type check: top level value must be an array
+ if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
+ {
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch));
+ }
+
+ // iterate and apply the operations
+ for (const auto& val : json_patch)
+ {
+ // wrapper to get a value for an operation
+ const auto get_value = [&val](const std::string & op,
+ const std::string & member,
+ bool string_type) -> basic_json &
+ {
+ // find value
+ auto it = val.m_value.object->find(member);
+
+ // context-sensitive error message
+ const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\'');
+
+ // check if desired value is present
+ if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))
+ {
+ // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+ JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val));
+ }
+
+ // check if result is of type string
+ if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
+ {
+ // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+ JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val));
+ }
+
+ // no error: return value
+ return it->second;
+ };
+
+ // type check: every element of the array must be an object
+ if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
+ {
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val));
+ }
+
+ // collect mandatory members
+ const auto op = get_value("op", "op", true).template get<std::string>();
+ const auto path = get_value(op, "path", true).template get<std::string>();
+ json_pointer ptr(path);
+
+ switch (get_op(op))
+ {
+ case patch_operations::add:
+ {
+ operation_add(ptr, get_value("add", "value", false));
+ break;
+ }
+
+ case patch_operations::remove:
+ {
+ operation_remove(ptr);
+ break;
+ }
+
+ case patch_operations::replace:
+ {
+ // the "path" location must exist - use at()
+ result.at(ptr) = get_value("replace", "value", false);
+ break;
+ }
+
+ case patch_operations::move:
+ {
+ const auto from_path = get_value("move", "from", true).template get<std::string>();
+ json_pointer from_ptr(from_path);
+
+ // the "from" location must exist - use at()
+ basic_json const v = result.at(from_ptr);
+
+ // The move operation is functionally identical to a
+ // "remove" operation on the "from" location, followed
+ // immediately by an "add" operation at the target
+ // location with the value that was just removed.
+ operation_remove(from_ptr);
+ operation_add(ptr, v);
+ break;
+ }
+
+ case patch_operations::copy:
+ {
+ const auto from_path = get_value("copy", "from", true).template get<std::string>();
+ const json_pointer from_ptr(from_path);
+
+ // the "from" location must exist - use at()
+ basic_json const v = result.at(from_ptr);
+
+ // The copy is functionally identical to an "add"
+ // operation at the target location using the value
+ // specified in the "from" member.
+ operation_add(ptr, v);
+ break;
+ }
+
+ case patch_operations::test:
+ {
+ bool success = false;
+ JSON_TRY
+ {
+ // check if "value" matches the one at "path"
+ // the "path" location must exist - use at()
+ success = (result.at(ptr) == get_value("test", "value", false));
+ }
+ JSON_INTERNAL_CATCH (out_of_range&)
+ {
+ // ignore out of range errors: success remains false
+ }
+
+ // throw an exception if test fails
+ if (JSON_HEDLEY_UNLIKELY(!success))
+ {
+ JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val));
+ }
+
+ break;
+ }
+
+ case patch_operations::invalid:
+ default:
+ {
+ // op must be "add", "remove", "replace", "move", "copy", or
+ // "test"
+ JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val));
+ }
+ }
+ }
+ }
+
+ /// @brief applies a JSON patch to a copy of the current object
+ /// @sa https://json.nlohmann.me/api/basic_json/patch/
+ basic_json patch(const basic_json& json_patch) const
+ {
+ basic_json result = *this;
+ result.patch_inplace(json_patch);
+ return result;
+ }
+
+ /// @brief creates a diff as a JSON patch
+ /// @sa https://json.nlohmann.me/api/basic_json/diff/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json diff(const basic_json& source, const basic_json& target,
+ const std::string& path = "")
+ {
+ // the patch
+ basic_json result(value_t::array);
+
+ // if the values are the same, return empty patch
+ if (source == target)
+ {
+ return result;
+ }
+
+ if (source.type() != target.type())
+ {
+ // different types: replace value
+ result.push_back(
+ {
+ {"op", "replace"}, {"path", path}, {"value", target}
+ });
+ return result;
+ }
+
+ switch (source.type())
+ {
+ case value_t::array:
+ {
+ // first pass: traverse common elements
+ std::size_t i = 0;
+ while (i < source.size() && i < target.size())
+ {
+ // recursive call to compare array values at index i
+ auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i)));
+ result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+ ++i;
+ }
+
+ // We now reached the end of at least one array
+ // in a second pass, traverse the remaining elements
+
+ // remove my remaining elements
+ const auto end_index = static_cast<difference_type>(result.size());
+ while (i < source.size())
+ {
+ // add operations in reverse order to avoid invalid
+ // indices
+ result.insert(result.begin() + end_index, object(
+ {
+ {"op", "remove"},
+ {"path", detail::concat(path, '/', std::to_string(i))}
+ }));
+ ++i;
+ }
+
+ // add other remaining elements
+ while (i < target.size())
+ {
+ result.push_back(
+ {
+ {"op", "add"},
+ {"path", detail::concat(path, "/-")},
+ {"value", target[i]}
+ });
+ ++i;
+ }
+
+ break;
+ }
+
+ case value_t::object:
+ {
+ // first pass: traverse this object's elements
+ for (auto it = source.cbegin(); it != source.cend(); ++it)
+ {
+ // escape the key name to be used in a JSON patch
+ const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
+
+ if (target.find(it.key()) != target.end())
+ {
+ // recursive call to compare object values at key it
+ auto temp_diff = diff(it.value(), target[it.key()], path_key);
+ result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+ }
+ else
+ {
+ // found a key that is not in o -> remove it
+ result.push_back(object(
+ {
+ {"op", "remove"}, {"path", path_key}
+ }));
+ }
+ }
+
+ // second pass: traverse other object's elements
+ for (auto it = target.cbegin(); it != target.cend(); ++it)
+ {
+ if (source.find(it.key()) == source.end())
+ {
+ // found a key that is not in this -> add it
+ const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
+ result.push_back(
+ {
+ {"op", "add"}, {"path", path_key},
+ {"value", it.value()}
+ });
+ }
+ }
+
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ // both primitive type: replace value
+ result.push_back(
+ {
+ {"op", "replace"}, {"path", path}, {"value", target}
+ });
+ break;
+ }
+ }
+
+ return result;
+ }
+ /// @}
+
+ ////////////////////////////////
+ // JSON Merge Patch functions //
+ ////////////////////////////////
+
+ /// @name JSON Merge Patch functions
+ /// @{
+
+ /// @brief applies a JSON Merge Patch
+ /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/
+ void merge_patch(const basic_json& apply_patch)
+ {
+ if (apply_patch.is_object())
+ {
+ if (!is_object())
+ {
+ *this = object();
+ }
+ for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)
+ {
+ if (it.value().is_null())
+ {
+ erase(it.key());
+ }
+ else
+ {
+ operator[](it.key()).merge_patch(it.value());
+ }
+ }
+ }
+ else
+ {
+ *this = apply_patch;
+ }
+ }
+
+ /// @}
+};
+
+/// @brief user-defined to_string function for JSON values
+/// @sa https://json.nlohmann.me/api/basic_json/to_string/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)
+{
+ return j.dump();
+}
+
+inline namespace literals
+{
+inline namespace json_literals
+{
+
+/// @brief user-defined string literal for JSON values
+/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/
+JSON_HEDLEY_NON_NULL(1)
+inline nlohmann::json operator "" _json(const char* s, std::size_t n)
+{
+ return nlohmann::json::parse(s, s + n);
+}
+
+/// @brief user-defined string literal for JSON pointer
+/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/
+JSON_HEDLEY_NON_NULL(1)
+inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n)
+{
+ return nlohmann::json::json_pointer(std::string(s, n));
+}
+
+} // namespace json_literals
+} // namespace literals
+NLOHMANN_JSON_NAMESPACE_END
+
+///////////////////////
+// nonmember support //
+///////////////////////
+
+namespace std // NOLINT(cert-dcl58-cpp)
+{
+
+/// @brief hash value for JSON objects
+/// @sa https://json.nlohmann.me/api/basic_json/std_hash/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL> // NOLINT(cert-dcl58-cpp)
+{
+ std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const
+ {
+ return nlohmann::detail::hash(j);
+ }
+};
+
+// specialization for std::less<value_t>
+template<>
+struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679
+{
+ /*!
+ @brief compare two value_t enum values
+ @since version 3.0.0
+ */
+ bool operator()(::nlohmann::detail::value_t lhs,
+ ::nlohmann::detail::value_t rhs) const noexcept
+ {
+#if JSON_HAS_THREE_WAY_COMPARISON
+ return std::is_lt(lhs <=> rhs); // *NOPAD*
+#else
+ return ::nlohmann::detail::operator<(lhs, rhs);
+#endif
+ }
+};
+
+// C++20 prohibit function specialization in the std namespace.
+#ifndef JSON_HAS_CPP_20
+
+/// @brief exchanges the values of two JSON objects
+/// @sa https://json.nlohmann.me/api/basic_json/std_swap/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp)
+ is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&& // NOLINT(misc-redundant-expression)
+ is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value)
+{
+ j1.swap(j2);
+}
+
+#endif
+
+} // namespace std
+
+#if JSON_USE_GLOBAL_UDLS
+ using nlohmann::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+ using nlohmann::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+#endif
+
+// #include <nlohmann/detail/macro_unscope.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// restore clang diagnostic settings
+#if defined(__clang__)
+ #pragma clang diagnostic pop
+#endif
+
+// clean up
+#undef JSON_ASSERT
+#undef JSON_INTERNAL_CATCH
+#undef JSON_THROW
+#undef JSON_PRIVATE_UNLESS_TESTED
+#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
+#undef NLOHMANN_BASIC_JSON_TPL
+#undef JSON_EXPLICIT
+#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL
+#undef JSON_INLINE_VARIABLE
+#undef JSON_NO_UNIQUE_ADDRESS
+#undef JSON_DISABLE_ENUM_SERIALIZATION
+#undef JSON_USE_GLOBAL_UDLS
+
+#ifndef JSON_TEST_KEEP_MACROS
+ #undef JSON_CATCH
+ #undef JSON_TRY
+ #undef JSON_HAS_CPP_11
+ #undef JSON_HAS_CPP_14
+ #undef JSON_HAS_CPP_17
+ #undef JSON_HAS_CPP_20
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #undef JSON_HAS_THREE_WAY_COMPARISON
+ #undef JSON_HAS_RANGES
+ #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+#endif
+
+// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.2
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#undef JSON_HEDLEY_ALWAYS_INLINE
+#undef JSON_HEDLEY_ARM_VERSION
+#undef JSON_HEDLEY_ARM_VERSION_CHECK
+#undef JSON_HEDLEY_ARRAY_PARAM
+#undef JSON_HEDLEY_ASSUME
+#undef JSON_HEDLEY_BEGIN_C_DECLS
+#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_BUILTIN
+#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_EXTENSION
+#undef JSON_HEDLEY_CLANG_HAS_FEATURE
+#undef JSON_HEDLEY_CLANG_HAS_WARNING
+#undef JSON_HEDLEY_COMPCERT_VERSION
+#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
+#undef JSON_HEDLEY_CONCAT
+#undef JSON_HEDLEY_CONCAT3
+#undef JSON_HEDLEY_CONCAT3_EX
+#undef JSON_HEDLEY_CONCAT_EX
+#undef JSON_HEDLEY_CONST
+#undef JSON_HEDLEY_CONSTEXPR
+#undef JSON_HEDLEY_CONST_CAST
+#undef JSON_HEDLEY_CPP_CAST
+#undef JSON_HEDLEY_CRAY_VERSION
+#undef JSON_HEDLEY_CRAY_VERSION_CHECK
+#undef JSON_HEDLEY_C_DECL
+#undef JSON_HEDLEY_DEPRECATED
+#undef JSON_HEDLEY_DEPRECATED_FOR
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#undef JSON_HEDLEY_DIAGNOSTIC_POP
+#undef JSON_HEDLEY_DIAGNOSTIC_PUSH
+#undef JSON_HEDLEY_DMC_VERSION
+#undef JSON_HEDLEY_DMC_VERSION_CHECK
+#undef JSON_HEDLEY_EMPTY_BASES
+#undef JSON_HEDLEY_EMSCRIPTEN_VERSION
+#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
+#undef JSON_HEDLEY_END_C_DECLS
+#undef JSON_HEDLEY_FLAGS
+#undef JSON_HEDLEY_FLAGS_CAST
+#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_BUILTIN
+#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_EXTENSION
+#undef JSON_HEDLEY_GCC_HAS_FEATURE
+#undef JSON_HEDLEY_GCC_HAS_WARNING
+#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
+#undef JSON_HEDLEY_GCC_VERSION
+#undef JSON_HEDLEY_GCC_VERSION_CHECK
+#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_BUILTIN
+#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_EXTENSION
+#undef JSON_HEDLEY_GNUC_HAS_FEATURE
+#undef JSON_HEDLEY_GNUC_HAS_WARNING
+#undef JSON_HEDLEY_GNUC_VERSION
+#undef JSON_HEDLEY_GNUC_VERSION_CHECK
+#undef JSON_HEDLEY_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_BUILTIN
+#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
+#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_EXTENSION
+#undef JSON_HEDLEY_HAS_FEATURE
+#undef JSON_HEDLEY_HAS_WARNING
+#undef JSON_HEDLEY_IAR_VERSION
+#undef JSON_HEDLEY_IAR_VERSION_CHECK
+#undef JSON_HEDLEY_IBM_VERSION
+#undef JSON_HEDLEY_IBM_VERSION_CHECK
+#undef JSON_HEDLEY_IMPORT
+#undef JSON_HEDLEY_INLINE
+#undef JSON_HEDLEY_INTEL_CL_VERSION
+#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
+#undef JSON_HEDLEY_INTEL_VERSION
+#undef JSON_HEDLEY_INTEL_VERSION_CHECK
+#undef JSON_HEDLEY_IS_CONSTANT
+#undef JSON_HEDLEY_IS_CONSTEXPR_
+#undef JSON_HEDLEY_LIKELY
+#undef JSON_HEDLEY_MALLOC
+#undef JSON_HEDLEY_MCST_LCC_VERSION
+#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
+#undef JSON_HEDLEY_MESSAGE
+#undef JSON_HEDLEY_MSVC_VERSION
+#undef JSON_HEDLEY_MSVC_VERSION_CHECK
+#undef JSON_HEDLEY_NEVER_INLINE
+#undef JSON_HEDLEY_NON_NULL
+#undef JSON_HEDLEY_NO_ESCAPE
+#undef JSON_HEDLEY_NO_RETURN
+#undef JSON_HEDLEY_NO_THROW
+#undef JSON_HEDLEY_NULL
+#undef JSON_HEDLEY_PELLES_VERSION
+#undef JSON_HEDLEY_PELLES_VERSION_CHECK
+#undef JSON_HEDLEY_PGI_VERSION
+#undef JSON_HEDLEY_PGI_VERSION_CHECK
+#undef JSON_HEDLEY_PREDICT
+#undef JSON_HEDLEY_PRINTF_FORMAT
+#undef JSON_HEDLEY_PRIVATE
+#undef JSON_HEDLEY_PUBLIC
+#undef JSON_HEDLEY_PURE
+#undef JSON_HEDLEY_REINTERPRET_CAST
+#undef JSON_HEDLEY_REQUIRE
+#undef JSON_HEDLEY_REQUIRE_CONSTEXPR
+#undef JSON_HEDLEY_REQUIRE_MSG
+#undef JSON_HEDLEY_RESTRICT
+#undef JSON_HEDLEY_RETURNS_NON_NULL
+#undef JSON_HEDLEY_SENTINEL
+#undef JSON_HEDLEY_STATIC_ASSERT
+#undef JSON_HEDLEY_STATIC_CAST
+#undef JSON_HEDLEY_STRINGIFY
+#undef JSON_HEDLEY_STRINGIFY_EX
+#undef JSON_HEDLEY_SUNPRO_VERSION
+#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
+#undef JSON_HEDLEY_TINYC_VERSION
+#undef JSON_HEDLEY_TINYC_VERSION_CHECK
+#undef JSON_HEDLEY_TI_ARMCL_VERSION
+#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL2000_VERSION
+#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL430_VERSION
+#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL6X_VERSION
+#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL7X_VERSION
+#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CLPRU_VERSION
+#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
+#undef JSON_HEDLEY_TI_VERSION
+#undef JSON_HEDLEY_TI_VERSION_CHECK
+#undef JSON_HEDLEY_UNAVAILABLE
+#undef JSON_HEDLEY_UNLIKELY
+#undef JSON_HEDLEY_UNPREDICTABLE
+#undef JSON_HEDLEY_UNREACHABLE
+#undef JSON_HEDLEY_UNREACHABLE_RETURN
+#undef JSON_HEDLEY_VERSION
+#undef JSON_HEDLEY_VERSION_DECODE_MAJOR
+#undef JSON_HEDLEY_VERSION_DECODE_MINOR
+#undef JSON_HEDLEY_VERSION_DECODE_REVISION
+#undef JSON_HEDLEY_VERSION_ENCODE
+#undef JSON_HEDLEY_WARNING
+#undef JSON_HEDLEY_WARN_UNUSED_RESULT
+#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
+#undef JSON_HEDLEY_FALL_THROUGH
+
+
+
+#endif // INCLUDE_NLOHMANN_JSON_HPP_
diff --git a/src/libs/3rdparty/libptyqt/.clang-format b/src/libs/3rdparty/libptyqt/.clang-format
new file mode 100644
index 0000000000..b861ff7a95
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/.clang-format
@@ -0,0 +1 @@
+{ "DisableFormat" : true } \ No newline at end of file
diff --git a/src/libs/3rdparty/libptyqt/CMakeLists.txt b/src/libs/3rdparty/libptyqt/CMakeLists.txt
new file mode 100644
index 0000000000..c6e8b74573
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/CMakeLists.txt
@@ -0,0 +1,28 @@
+set(SOURCES
+ iptyprocess.h
+ ptyqt.cpp ptyqt.h
+)
+
+if (WIN32)
+ list(APPEND SOURCES
+ winptyprocess.cpp winptyprocess.h
+ conptyprocess.cpp conptyprocess.h
+ )
+else()
+ list(APPEND SOURCES unixptyprocess.cpp unixptyprocess.h)
+endif()
+
+add_library(ptyqt STATIC ${SOURCES})
+target_link_libraries(ptyqt PUBLIC Qt::Core)
+
+if (WIN32)
+ target_link_libraries(ptyqt PRIVATE winpty Qt::Network)
+ #target_compile_definitions(ptyqt PRIVATE PTYQT_DEBUG)
+endif()
+
+set_target_properties(ptyqt
+ PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}
+ QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
+ POSITION_INDEPENDENT_CODE ON
+)
diff --git a/src/libs/3rdparty/libptyqt/LICENSE b/src/libs/3rdparty/libptyqt/LICENSE
new file mode 100644
index 0000000000..73996c7c90
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Vitaly Petrov, v31337@gmail.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/src/libs/3rdparty/libptyqt/LICENSE-CONPTY b/src/libs/3rdparty/libptyqt/LICENSE-CONPTY
new file mode 100644
index 0000000000..8cb179cdb6
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/LICENSE-CONPTY
@@ -0,0 +1,21 @@
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/libs/3rdparty/libptyqt/conptyprocess.cpp b/src/libs/3rdparty/libptyqt/conptyprocess.cpp
new file mode 100644
index 0000000000..94bb53b583
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/conptyprocess.cpp
@@ -0,0 +1,1148 @@
+#include "conptyprocess.h"
+#include <QFile>
+#include <QFileInfo>
+#include <QThread>
+#include <sstream>
+#include <QTimer>
+#include <QMutexLocker>
+#include <QCoreApplication>
+#include <QWinEventNotifier>
+
+#include <qt_windows.h>
+
+#ifdef QTCREATOR_PCH_H
+#ifndef IN
+#define IN
+#endif
+#ifndef OUT
+#define OUT
+#endif
+#endif
+
+#include <winternl.h>
+
+#define READ_INTERVAL_MSEC 500
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
+
+////////////////////////////////////////////////////////////////////////////////////////
+/// Ported from wil/result_macros.h
+
+#define FAILED_NTSTATUS(status) (((NTSTATUS)(status)) < 0)
+#define SUCCEEDED_NTSTATUS(status) (((NTSTATUS)(status)) >= 0)
+
+#define RETURN_IF_NTSTATUS_FAILED(call) if(FAILED_NTSTATUS(call)) return E_FAIL;
+#define RETURN_IF_WIN32_BOOL_FALSE(call) if((call) == FALSE) return E_FAIL;
+#define RETURN_IF_NULL_ALLOC(call) if((call) == nullptr) return E_OUTOFMEMORY;
+#define RETURN_IF_FAILED(call) {HRESULT hr = (call); if(hr != S_OK)return hr; }
+
+//! Set zero or more bitflags specified by `flags` in the variable `var`.
+#define WI_SetAllFlags(var, flags) ((var) |= (flags))
+//! Set a single compile-time constant `flag` in the variable `var`.
+#define WI_SetFlag(var, flag) WI_SetAllFlags(var,flag)
+
+////////////////////////////////////////////////////////////////////////////////////////
+/// Ported from wil
+class unique_hmodule : public std::unique_ptr<std::remove_pointer_t<HMODULE>, decltype(&FreeLibrary)>
+{
+public:
+ unique_hmodule() : std::unique_ptr<std::remove_pointer_t<HMODULE>, decltype(&FreeLibrary)>(nullptr, FreeLibrary) {}
+ unique_hmodule(HMODULE module) : std::unique_ptr<std::remove_pointer_t<HMODULE>, decltype(&FreeLibrary)>(module, FreeLibrary) {}
+};
+
+class unique_handle : public std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&CloseHandle)>
+{
+public:
+ unique_handle() : std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&CloseHandle)>(nullptr, CloseHandle) {}
+ unique_handle(HANDLE module) : std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&CloseHandle)>(module, CloseHandle) {}
+
+
+ class AddressOf {
+ public:
+ AddressOf(unique_handle &h) : m_h(h) {}
+ ~AddressOf() {
+ m_h.reset(m_dest);
+ }
+
+ operator PHANDLE() {return &m_dest;}
+
+ HANDLE m_dest{INVALID_HANDLE_VALUE};
+ unique_handle &m_h;
+ };
+
+ AddressOf addressof() {
+ return AddressOf(*this);
+ }
+};
+
+class unique_process_information : public PROCESS_INFORMATION
+{
+public:
+ unique_process_information() {
+ hProcess = 0;
+ hThread = 0;
+ dwProcessId = 0;
+ dwThreadId = 0;
+ }
+ ~unique_process_information() {
+ if (hProcess)
+ {
+ CloseHandle(hProcess);
+ }
+
+ if (hThread)
+ {
+ CloseHandle(hThread);
+ }
+ }
+
+ PROCESS_INFORMATION* addressof() {
+ return this;
+ }
+};
+
+template <typename TLambda>
+class on_scope_exit {
+public:
+ TLambda m_func;
+ bool m_call{true};
+ on_scope_exit(TLambda &&func) : m_func(std::move(func)) {}
+ ~on_scope_exit() {if(m_call)m_func();}
+
+ void release() {m_call = false;}
+};
+
+template <typename TLambda>
+[[nodiscard]] inline auto scope_exit(TLambda&& lambda) noexcept
+{
+ return on_scope_exit<TLambda>(std::forward<TLambda>(lambda));
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+class WinNTControl
+{
+public:
+ [[nodiscard]] static NTSTATUS NtOpenFile(_Out_ PHANDLE FileHandle,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_ POBJECT_ATTRIBUTES ObjectAttributes,
+ _Out_ PIO_STATUS_BLOCK IoStatusBlock,
+ _In_ ULONG ShareAccess,
+ _In_ ULONG OpenOptions);
+
+private:
+ WinNTControl();
+
+ WinNTControl(WinNTControl const&) = delete;
+ void operator=(WinNTControl const&) = delete;
+
+ static WinNTControl& GetInstance();
+
+ unique_hmodule const _NtDllDll;
+
+ typedef NTSTATUS(NTAPI* PfnNtOpenFile)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG);
+ PfnNtOpenFile const _NtOpenFile;
+};
+
+WinNTControl::WinNTControl() :
+ // NOTE: Use LoadLibraryExW with LOAD_LIBRARY_SEARCH_SYSTEM32 flag below to avoid unneeded directory traversal.
+ // This has triggered CPG boot IO warnings in the past.
+ _NtDllDll(/*THROW_LAST_ERROR_IF_NULL*/(LoadLibraryExW(L"ntdll.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))),
+ _NtOpenFile(reinterpret_cast<PfnNtOpenFile>(/*THROW_LAST_ERROR_IF_NULL*/(GetProcAddress(_NtDllDll.get(), "NtOpenFile"))))
+{
+}
+
+// Routine Description:
+// - Provides the singleton pattern for WinNT control. Stores the single instance and returns it.
+// Arguments:
+// - <none>
+// Return Value:
+// - Reference to the single instance of NTDLL.dll wrapped methods.
+WinNTControl& WinNTControl::GetInstance()
+{
+ static WinNTControl Instance;
+ return Instance;
+}
+
+// Routine Description:
+// - Provides access to the NtOpenFile method documented at:
+// https://msdn.microsoft.com/en-us/library/bb432381(v=vs.85).aspx
+// Arguments:
+// - See definitions at MSDN
+// Return Value:
+// - See definitions at MSDN
+[[nodiscard]] NTSTATUS WinNTControl::NtOpenFile(_Out_ PHANDLE FileHandle,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_ POBJECT_ATTRIBUTES ObjectAttributes,
+ _Out_ PIO_STATUS_BLOCK IoStatusBlock,
+ _In_ ULONG ShareAccess,
+ _In_ ULONG OpenOptions)
+{
+ return GetInstance()._NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions);
+}
+
+namespace DeviceHandle {
+
+/*++
+Routine Description:
+- This routine opens a handle to the console driver.
+
+Arguments:
+- Handle - Receives the handle.
+- DeviceName - Supplies the name to be used to open the console driver.
+- DesiredAccess - Supplies the desired access mask.
+- Parent - Optionally supplies the parent object.
+- Inheritable - Supplies a boolean indicating if the new handle is to be made inheritable.
+- OpenOptions - Supplies the open options to be passed to NtOpenFile. A common
+ option for clients is FILE_SYNCHRONOUS_IO_NONALERT, to make the handle
+ synchronous.
+
+Return Value:
+- NTSTATUS indicating if the handle was successfully created.
+--*/
+[[nodiscard]] NTSTATUS
+_CreateHandle(
+ _Out_ PHANDLE Handle,
+ _In_ PCWSTR DeviceName,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_opt_ HANDLE Parent,
+ _In_ BOOLEAN Inheritable,
+ _In_ ULONG OpenOptions)
+
+{
+ ULONG Flags = OBJ_CASE_INSENSITIVE;
+
+ if (Inheritable)
+ {
+ WI_SetFlag(Flags, OBJ_INHERIT);
+ }
+
+ UNICODE_STRING Name;
+#pragma warning(suppress : 26492) // const_cast is prohibited, but we can't avoid it for filling UNICODE_STRING.
+ Name.Buffer = const_cast<wchar_t*>(DeviceName);
+ //Name.Length = gsl::narrow_cast<USHORT>((wcslen(DeviceName) * sizeof(wchar_t)));
+ Name.Length = static_cast<unsigned short>((wcslen(DeviceName) * sizeof(wchar_t)));
+ Name.MaximumLength = Name.Length + sizeof(wchar_t);
+
+ OBJECT_ATTRIBUTES ObjectAttributes;
+#pragma warning(suppress : 26477) // The QOS part of this macro in the define is 0. Can't fix that.
+ InitializeObjectAttributes(&ObjectAttributes,
+ &Name,
+ Flags,
+ Parent,
+ nullptr);
+
+ IO_STATUS_BLOCK IoStatus;
+ return WinNTControl::NtOpenFile(Handle,
+ DesiredAccess,
+ &ObjectAttributes,
+ &IoStatus,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OpenOptions);
+}
+
+/*++
+Routine Description:
+- This routine creates a handle to an input or output client of the given
+ server. No control io is sent to the server as this request must be coming
+ from the server itself.
+
+Arguments:
+- Handle - Receives a handle to the new client.
+- ServerHandle - Supplies a handle to the server to which to attach the
+ newly created client.
+- Name - Supplies the name of the client object.
+- Inheritable - Supplies a flag indicating if the handle must be inheritable.
+
+Return Value:
+- NTSTATUS indicating if the client was successfully created.
+--*/
+[[nodiscard]] NTSTATUS
+CreateClientHandle(
+ _Out_ PHANDLE Handle,
+ _In_ HANDLE ServerHandle,
+ _In_ PCWSTR Name,
+ _In_ BOOLEAN Inheritable)
+{
+ return _CreateHandle(Handle,
+ Name,
+ GENERIC_WRITE | GENERIC_READ | SYNCHRONIZE,
+ ServerHandle,
+ Inheritable,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+}
+
+/*++
+Routine Description:
+- This routine creates a new server on the driver and returns a handle to it.
+
+Arguments:
+- Handle - Receives a handle to the new server.
+- Inheritable - Supplies a flag indicating if the handle must be inheritable.
+
+Return Value:
+- NTSTATUS indicating if the console was successfully created.
+--*/
+[[nodiscard]] NTSTATUS
+CreateServerHandle(
+ _Out_ PHANDLE Handle,
+ _In_ BOOLEAN Inheritable)
+{
+ return _CreateHandle(Handle,
+ L"\\Device\\ConDrv\\Server",
+ GENERIC_ALL,
+ nullptr,
+ Inheritable,
+ 0);
+}
+
+
+} // namespace DeviceHandle
+
+[[nodiscard]] static inline NTSTATUS CreateClientHandle(PHANDLE Handle, HANDLE ServerHandle, PCWSTR Name, BOOLEAN Inheritable)
+{
+ return DeviceHandle::CreateClientHandle(Handle, ServerHandle, Name, Inheritable);
+}
+
+[[nodiscard]] static inline NTSTATUS CreateServerHandle(PHANDLE Handle, BOOLEAN Inheritable)
+{
+ return DeviceHandle::CreateServerHandle(Handle, Inheritable);
+}
+
+typedef struct _PseudoConsole
+{
+ HANDLE hSignal;
+ HANDLE hPtyReference;
+ HANDLE hConPtyProcess;
+} PseudoConsole;
+
+// Signals
+// These are not defined publicly, but are used for controlling the conpty via
+// the signal pipe.
+#define PTY_SIGNAL_CLEAR_WINDOW (2u)
+#define PTY_SIGNAL_RESIZE_WINDOW (8u)
+
+// CreatePseudoConsole Flags
+// The other flag (PSEUDOCONSOLE_INHERIT_CURSOR) is actually defined in consoleapi.h in the OS repo
+#ifndef PSEUDOCONSOLE_INHERIT_CURSOR
+#define PSEUDOCONSOLE_INHERIT_CURSOR (0x1)
+#endif
+#define PSEUDOCONSOLE_RESIZE_QUIRK (0x2)
+#define PSEUDOCONSOLE_WIN32_INPUT_MODE (0x4)
+
+static QString qSystemDirectory()
+{
+ static const QString result = []() -> QString {
+ QVarLengthArray<wchar_t, MAX_PATH> fullPath = {};
+ UINT retLen = ::GetSystemDirectoryW(fullPath.data(), MAX_PATH);
+ if (retLen > MAX_PATH) {
+ fullPath.resize(retLen);
+ retLen = ::GetSystemDirectoryW(fullPath.data(), retLen);
+ }
+ // in some rare cases retLen might be 0
+ return QString::fromWCharArray(fullPath.constData(), int(retLen));
+ }();
+ return result;
+}
+
+// Function Description:
+// - Returns the path to conhost.exe as a process heap string.
+static QString _InboxConsoleHostPath()
+{
+ return QString("\\\\?\\%1\\conhost.exe").arg(qSystemDirectory());
+}
+
+// Function Description:
+// - Returns the path to either conhost.exe or the side-by-side OpenConsole, depending on whether this
+// module is building with Windows and OpenConsole could be found.
+// Return Value:
+// - A pointer to permanent storage containing the path to the console host.
+static const wchar_t* _ConsoleHostPath()
+{
+ // Use the magic of magic statics to only calculate this once.
+ static QString consoleHostPath = _InboxConsoleHostPath();
+ return reinterpret_cast<const wchar_t*>(consoleHostPath.utf16());
+}
+
+static bool _HandleIsValid(HANDLE h) noexcept
+{
+ return (h != INVALID_HANDLE_VALUE) && (h != nullptr);
+}
+
+
+
+HRESULT _CreatePseudoConsole(const HANDLE hToken,
+ const COORD size,
+ const HANDLE hInput,
+ const HANDLE hOutput,
+ const DWORD dwFlags,
+ _Inout_ PseudoConsole* pPty)
+{
+ if (pPty == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+ if (size.X == 0 || size.Y == 0)
+ {
+ return E_INVALIDARG;
+ }
+
+ unique_handle serverHandle;
+ RETURN_IF_NTSTATUS_FAILED(CreateServerHandle(serverHandle.addressof(), TRUE));
+
+ unique_handle signalPipeConhostSide;
+ unique_handle signalPipeOurSide;
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ // Mark inheritable for signal handle when creating. It'll have the same value on the other side.
+ sa.bInheritHandle = FALSE;
+ sa.lpSecurityDescriptor = nullptr;
+
+ RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(signalPipeConhostSide.addressof(), signalPipeOurSide.addressof(), &sa, 0));
+ RETURN_IF_WIN32_BOOL_FALSE(SetHandleInformation(signalPipeConhostSide.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));
+
+ // GH4061: Ensure that the path to executable in the format is escaped so C:\Program.exe cannot collide with C:\Program Files
+ const wchar_t* pwszFormat = L"\"%s\" --headless %s%s%s--width %hu --height %hu --signal 0x%x --server 0x%x";
+ // This is plenty of space to hold the formatted string
+ wchar_t cmd[MAX_PATH]{};
+ const BOOL bInheritCursor = (dwFlags & PSEUDOCONSOLE_INHERIT_CURSOR) == PSEUDOCONSOLE_INHERIT_CURSOR;
+ const BOOL bResizeQuirk = (dwFlags & PSEUDOCONSOLE_RESIZE_QUIRK) == PSEUDOCONSOLE_RESIZE_QUIRK;
+ const BOOL bWin32InputMode = (dwFlags & PSEUDOCONSOLE_WIN32_INPUT_MODE) == PSEUDOCONSOLE_WIN32_INPUT_MODE;
+ swprintf_s(cmd,
+ MAX_PATH,
+ pwszFormat,
+ _ConsoleHostPath(),
+ bInheritCursor ? L"--inheritcursor " : L"",
+ bWin32InputMode ? L"--win32input " : L"",
+ bResizeQuirk ? L"--resizeQuirk " : L"",
+ size.X,
+ size.Y,
+ signalPipeConhostSide.get(),
+ serverHandle.get());
+
+ STARTUPINFOEXW siEx{ 0 };
+ siEx.StartupInfo.cb = sizeof(STARTUPINFOEXW);
+ siEx.StartupInfo.hStdInput = hInput;
+ siEx.StartupInfo.hStdOutput = hOutput;
+ siEx.StartupInfo.hStdError = hOutput;
+ siEx.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Only pass the handles we actually want the conhost to know about to it:
+ const size_t INHERITED_HANDLES_COUNT = 4;
+ HANDLE inheritedHandles[INHERITED_HANDLES_COUNT];
+ inheritedHandles[0] = serverHandle.get();
+ inheritedHandles[1] = hInput;
+ inheritedHandles[2] = hOutput;
+ inheritedHandles[3] = signalPipeConhostSide.get();
+
+ // Get the size of the attribute list. We need one attribute, the handle list.
+ SIZE_T listSize = 0;
+ InitializeProcThreadAttributeList(nullptr, 1, 0, &listSize);
+
+ // I have to use a HeapAlloc here because kernelbase can't link new[] or delete[]
+ PPROC_THREAD_ATTRIBUTE_LIST attrList = static_cast<PPROC_THREAD_ATTRIBUTE_LIST>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, listSize));
+ RETURN_IF_NULL_ALLOC(attrList);
+ auto attrListDelete = scope_exit([&]() noexcept {
+ HeapFree(GetProcessHeap(), 0, attrList);
+ });
+
+ siEx.lpAttributeList = attrList;
+ RETURN_IF_WIN32_BOOL_FALSE(InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &listSize));
+ // Set cleanup data for ProcThreadAttributeList when successful.
+ auto cleanupProcThreadAttribute = scope_exit([&]() noexcept {
+ DeleteProcThreadAttributeList(siEx.lpAttributeList);
+ });
+ RETURN_IF_WIN32_BOOL_FALSE(UpdateProcThreadAttribute(siEx.lpAttributeList,
+ 0,
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ inheritedHandles,
+ (INHERITED_HANDLES_COUNT * sizeof(HANDLE)),
+ nullptr,
+ nullptr));
+ unique_process_information pi;
+ { // wow64 disabled filesystem redirection scope
+ if (hToken == INVALID_HANDLE_VALUE || hToken == nullptr)
+ {
+ // Call create process
+ RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(_ConsoleHostPath(),
+ cmd,
+ nullptr,
+ nullptr,
+ TRUE,
+ EXTENDED_STARTUPINFO_PRESENT,
+ nullptr,
+ nullptr,
+ &siEx.StartupInfo,
+ pi.addressof()));
+ }
+ else
+ {
+ // Call create process
+ RETURN_IF_WIN32_BOOL_FALSE(CreateProcessAsUserW(hToken,
+ _ConsoleHostPath(),
+ cmd,
+ nullptr,
+ nullptr,
+ TRUE,
+ EXTENDED_STARTUPINFO_PRESENT,
+ nullptr,
+ nullptr,
+ &siEx.StartupInfo,
+ pi.addressof()));
+ }
+ }
+
+ // Move the process handle out of the PROCESS_INFORMATION into out Pseudoconsole
+ pPty->hConPtyProcess = pi.hProcess;
+ pi.hProcess = nullptr;
+
+ RETURN_IF_NTSTATUS_FAILED(CreateClientHandle(&pPty->hPtyReference,
+ serverHandle.get(),
+ L"\\Reference",
+ FALSE));
+
+ pPty->hSignal = signalPipeOurSide.release();
+
+ return S_OK;
+}
+
+// Function Description:
+// - Resizes the conpty
+// Arguments:
+// - hSignal: A signal pipe as returned by CreateConPty.
+// - size: The new dimensions of the conpty, in characters.
+// Return Value:
+// - S_OK if the call succeeded, else an appropriate HRESULT for failing to
+// write the resize message to the pty.
+HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const COORD size)
+{
+ if (pPty == nullptr || size.X < 0 || size.Y < 0)
+ {
+ return E_INVALIDARG;
+ }
+
+ unsigned short signalPacket[3];
+ signalPacket[0] = PTY_SIGNAL_RESIZE_WINDOW;
+ signalPacket[1] = size.X;
+ signalPacket[2] = size.Y;
+
+ const BOOL fSuccess = WriteFile(pPty->hSignal, signalPacket, sizeof(signalPacket), nullptr, nullptr);
+ return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
+}
+
+// Function Description:
+// - Clears the conpty
+// Arguments:
+// - hSignal: A signal pipe as returned by CreateConPty.
+// Return Value:
+// - S_OK if the call succeeded, else an appropriate HRESULT for failing to
+// write the clear message to the pty.
+HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty)
+{
+ if (pPty == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ unsigned short signalPacket[1];
+ signalPacket[0] = PTY_SIGNAL_CLEAR_WINDOW;
+
+ const BOOL fSuccess = WriteFile(pPty->hSignal, signalPacket, sizeof(signalPacket), nullptr, nullptr);
+ return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
+}
+
+// Function Description:
+// - This closes each of the members of a PseudoConsole. It does not free the
+// data associated with the PseudoConsole. This is helpful for testing,
+// where we might stack allocate a PseudoConsole (instead of getting a
+// HPCON via the API).
+// Arguments:
+// - pPty: A pointer to a PseudoConsole struct.
+// Return Value:
+// - <none>
+void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty)
+{
+ if (pPty != nullptr)
+ {
+ // See MSFT:19918626
+ // First break the signal pipe - this will trigger conhost to tear itself down
+ if (_HandleIsValid(pPty->hSignal))
+ {
+ CloseHandle(pPty->hSignal);
+ pPty->hSignal = nullptr;
+ }
+ // Then, wait on the conhost process before killing it.
+ // We do this to make sure the conhost finishes flushing any output it
+ // has yet to send before we hard kill it.
+ if (_HandleIsValid(pPty->hConPtyProcess))
+ {
+ // If the conhost is already dead, then that's fine. Presumably
+ // it's finished flushing it's output already.
+ DWORD dwExit = 0;
+ // If GetExitCodeProcess failed, it's likely conhost is already dead
+ // If so, skip waiting regardless of whatever error
+ // GetExitCodeProcess returned.
+ // We'll just go straight to killing conhost.
+ if (GetExitCodeProcess(pPty->hConPtyProcess, &dwExit) && dwExit == STILL_ACTIVE)
+ {
+ WaitForSingleObject(pPty->hConPtyProcess, INFINITE);
+ }
+
+ TerminateProcess(pPty->hConPtyProcess, 0);
+ CloseHandle(pPty->hConPtyProcess);
+ pPty->hConPtyProcess = nullptr;
+ }
+ // Then take care of the reference handle.
+ // TODO GH#1810: Closing the reference handle late leaves conhost thinking
+ // that we have an outstanding connected client.
+ if (_HandleIsValid(pPty->hPtyReference))
+ {
+ CloseHandle(pPty->hPtyReference);
+ pPty->hPtyReference = nullptr;
+ }
+ }
+}
+
+// Function Description:
+// - This closes each of the members of a PseudoConsole, and HeapFree's the
+// memory allocated to it. This should be used to cleanup any
+// PseudoConsoles that were created with CreatePseudoConsole.
+// Arguments:
+// - pPty: A pointer to a PseudoConsole struct.
+// Return Value:
+// - <none>
+VOID _ClosePseudoConsole(_In_ PseudoConsole* pPty)
+{
+ if (pPty != nullptr)
+ {
+ _ClosePseudoConsoleMembers(pPty);
+ HeapFree(GetProcessHeap(), 0, pPty);
+ }
+}
+
+extern "C" HRESULT ConptyCreatePseudoConsoleAsUser(_In_ HANDLE hToken,
+ _In_ COORD size,
+ _In_ HANDLE hInput,
+ _In_ HANDLE hOutput,
+ _In_ DWORD dwFlags,
+ _Out_ HPCON* phPC)
+{
+ if (phPC == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+ *phPC = nullptr;
+ if ((!_HandleIsValid(hInput)) && (!_HandleIsValid(hOutput)))
+ {
+ return E_INVALIDARG;
+ }
+
+ PseudoConsole* pPty = (PseudoConsole*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PseudoConsole));
+ RETURN_IF_NULL_ALLOC(pPty);
+ auto cleanupPty = scope_exit([&]() noexcept {
+ _ClosePseudoConsole(pPty);
+ });
+
+ unique_handle duplicatedInput;
+ unique_handle duplicatedOutput;
+ RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), hInput, GetCurrentProcess(), duplicatedInput.addressof(), 0, TRUE, DUPLICATE_SAME_ACCESS));
+ RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), hOutput, GetCurrentProcess(), duplicatedOutput.addressof(), 0, TRUE, DUPLICATE_SAME_ACCESS));
+
+ RETURN_IF_FAILED(_CreatePseudoConsole(hToken, size, duplicatedInput.get(), duplicatedOutput.get(), dwFlags, pPty));
+
+ *phPC = (HPCON)pPty;
+ cleanupPty.release();
+
+ return S_OK;
+}
+
+// These functions are defined in the console l1 apiset, which is generated from
+// the consoleapi.apx file in minkernel\apiset\libs\Console.
+
+// Function Description:
+// Creates a "Pseudo-console" (conpty) with dimensions (in characters)
+// provided by the `size` parameter. The caller should provide two handles:
+// - `hInput` is used for writing input to the pty, encoded as UTF-8 and VT sequences.
+// - `hOutput` is used for reading the output of the pty, encoded as UTF-8 and VT sequences.
+// Once the call completes, `phPty` will receive a token value to identify this
+// conpty object. This value should be used in conjunction with the other
+// Pseudoconsole API's.
+// `dwFlags` is used to specify optional behavior to the created pseudoconsole.
+// The flags can be combinations of the following values:
+// INHERIT_CURSOR: This will cause the created conpty to attempt to inherit the
+// cursor position of the parent terminal application. This can be useful
+// for applications like `ssh`, where ssh (currently running in a terminal)
+// might want to create a pseudoterminal session for an child application
+// and the child inherit the cursor position of ssh.
+// The created conpty will immediately emit a "Device Status Request" VT
+// sequence to hOutput, that should be replied to on hInput in the format
+// "\x1b[<r>;<c>R", where `<r>` is the row and `<c>` is the column of the
+// cursor position.
+// This requires a cooperating terminal application - if a caller does not
+// reply to this message, the conpty will not process any input until it
+// does. Most *nix terminals and the Windows Console (after Windows 10
+// Anniversary Update) will be able to handle such a message.
+
+extern "C" HRESULT WINAPI ConptyCreatePseudoConsole(_In_ COORD size,
+ _In_ HANDLE hInput,
+ _In_ HANDLE hOutput,
+ _In_ DWORD dwFlags,
+ _Out_ HPCON* phPC)
+{
+ return ConptyCreatePseudoConsoleAsUser(INVALID_HANDLE_VALUE, size, hInput, hOutput, dwFlags, phPC);
+}
+
+// Function Description:
+// Resizes the given conpty to the specified size, in characters.
+extern "C" HRESULT WINAPI ConptyResizePseudoConsole(_In_ HPCON hPC, _In_ COORD size)
+{
+ const PseudoConsole* const pPty = (PseudoConsole*)hPC;
+ HRESULT hr = pPty == nullptr ? E_INVALIDARG : S_OK;
+ if (SUCCEEDED(hr))
+ {
+ hr = _ResizePseudoConsole(pPty, size);
+ }
+ return hr;
+}
+
+// Function Description:
+// - Clear the contents of the conpty buffer, leaving the cursor row at the top
+// of the viewport.
+// - This is used exclusively by ConPTY to support GH#1193, GH#1882. This allows
+// a terminal to clear the contents of the ConPTY buffer, which is important
+// if the user would like to be able to clear the terminal-side buffer.
+extern "C" HRESULT WINAPI ConptyClearPseudoConsole(_In_ HPCON hPC)
+{
+ const PseudoConsole* const pPty = (PseudoConsole*)hPC;
+ HRESULT hr = pPty == nullptr ? E_INVALIDARG : S_OK;
+ if (SUCCEEDED(hr))
+ {
+ hr = _ClearPseudoConsole(pPty);
+ }
+ return hr;
+}
+
+// Function Description:
+// Closes the conpty and all associated state.
+// Client applications attached to the conpty will also behave as though the
+// console window they were running in was closed.
+// This can fail if the conhost hosting the pseudoconsole failed to be
+// terminated, or if the pseudoconsole was already terminated.
+extern "C" VOID WINAPI ConptyClosePseudoConsole(_In_ HPCON hPC)
+{
+ PseudoConsole* const pPty = (PseudoConsole*)hPC;
+ if (pPty != nullptr)
+ {
+ _ClosePseudoConsole(pPty);
+ }
+}
+
+
+//ConPTY is available only on Windows 10 released after 1903 (19H1) Windows release
+class WindowsContext
+{
+private:
+ WindowsContext() {}
+
+public:
+ typedef HRESULT (*CreatePseudoConsolePtr)(
+ COORD size, // ConPty Dimensions
+ HANDLE hInput, // ConPty Input
+ HANDLE hOutput, // ConPty Output
+ DWORD dwFlags, // ConPty Flags
+ HPCON* phPC); // ConPty Reference
+
+ typedef HRESULT (*ResizePseudoConsolePtr)(HPCON hPC, COORD size);
+
+ typedef VOID (*ClosePseudoConsolePtr)(HPCON hPC);
+
+ static WindowsContext &instance()
+ {
+ static WindowsContext ctx;
+ return ctx;
+ }
+
+ bool init()
+ {
+ createPseudoConsole = (CreatePseudoConsolePtr)ConptyCreatePseudoConsole;
+ resizePseudoConsole = (ResizePseudoConsolePtr)ConptyResizePseudoConsole;
+ closePseudoConsole = (ClosePseudoConsolePtr)ConptyClosePseudoConsole;
+
+ return true;
+ }
+
+ QString lastError()
+ {
+ return m_lastError;
+ }
+
+public:
+ //vars
+ CreatePseudoConsolePtr createPseudoConsole{nullptr};
+ ResizePseudoConsolePtr resizePseudoConsole{nullptr};
+ ClosePseudoConsolePtr closePseudoConsole{nullptr};
+
+private:
+ QString m_lastError;
+};
+
+static bool checkConHostHasResizeQuirkOption()
+{
+ static bool hasResizeQuirk = std::invoke([](){
+ QFile f(_InboxConsoleHostPath());
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning() << "couldn't open conhost.exe, assuming no resizeQuirk.";
+ return false;
+ }
+ // Conhost.exe should be around 1 MB
+ if (f.size() > 5 * 1024 * 1024) {
+ qWarning() << "conhost.exe is > 5MB, assuming no resizeQuirk.";
+ return false;
+ }
+ QByteArray content = f.readAll();
+ QString searchString("--resizeQuirk");
+ QByteArrayView v((const char*)searchString.data(), searchString.length()*2);
+ bool result = content.contains(v);
+ if (!result)
+ qDebug() << "No resizeQuirk option found in conhost.";
+ return result;
+ });
+
+ return hasResizeQuirk;
+}
+
+HRESULT ConPtyProcess::createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut, qint16 cols, qint16 rows)
+{
+ HRESULT hr{ E_UNEXPECTED };
+ HANDLE hPipePTYIn{ INVALID_HANDLE_VALUE };
+ HANDLE hPipePTYOut{ INVALID_HANDLE_VALUE };
+
+ // Create the pipes to which the ConPTY will connect
+ if (CreatePipe(&hPipePTYIn, phPipeOut, NULL, 0) &&
+ CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0))
+ {
+ // Create the Pseudo Console of the required size, attached to the PTY-end of the pipes
+ hr = WindowsContext::instance().createPseudoConsole({cols, rows}, hPipePTYIn, hPipePTYOut, checkConHostHasResizeQuirkOption() ? PSEUDOCONSOLE_RESIZE_QUIRK : 0, phPC);
+
+ // Note: We can close the handles to the PTY-end of the pipes here
+ // because the handles are dup'ed into the ConHost and will be released
+ // when the ConPTY is destroyed.
+ if (INVALID_HANDLE_VALUE != hPipePTYOut) CloseHandle(hPipePTYOut);
+ if (INVALID_HANDLE_VALUE != hPipePTYIn) CloseHandle(hPipePTYIn);
+ }
+
+ return hr;
+}
+
+// Initializes the specified startup info struct with the required properties and
+// updates its thread attribute list with the specified ConPTY handle
+HRESULT ConPtyProcess::initializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC)
+{
+ HRESULT hr{ E_UNEXPECTED };
+
+ if (pStartupInfo)
+ {
+ SIZE_T attrListSize{};
+
+ pStartupInfo->StartupInfo.hStdInput = m_hPipeIn;
+ pStartupInfo->StartupInfo.hStdError = m_hPipeOut;
+ pStartupInfo->StartupInfo.hStdOutput = m_hPipeOut;
+ pStartupInfo->StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ pStartupInfo->StartupInfo.cb = sizeof(STARTUPINFOEX);
+
+ // Get the size of the thread attribute list.
+ InitializeProcThreadAttributeList(NULL, 1, 0, &attrListSize);
+
+ // Allocate a thread attribute list of the correct size
+ pStartupInfo->lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(
+ HeapAlloc(GetProcessHeap(), 0, attrListSize));
+
+ // Initialize thread attribute list
+ if (pStartupInfo->lpAttributeList
+ && InitializeProcThreadAttributeList(pStartupInfo->lpAttributeList, 1, 0, &attrListSize))
+ {
+ // Set Pseudo Console attribute
+ hr = UpdateProcThreadAttribute(
+ pStartupInfo->lpAttributeList,
+ 0,
+ PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
+ hPC,
+ sizeof(HPCON),
+ NULL,
+ NULL)
+ ? S_OK
+ : HRESULT_FROM_WIN32(GetLastError());
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+ return hr;
+}
+
+Q_DECLARE_METATYPE(HANDLE)
+
+ConPtyProcess::ConPtyProcess()
+ : IPtyProcess()
+ , m_ptyHandler { INVALID_HANDLE_VALUE }
+ , m_hPipeIn { INVALID_HANDLE_VALUE }
+ , m_hPipeOut { INVALID_HANDLE_VALUE }
+ , m_readThread(nullptr)
+{
+ qRegisterMetaType<HANDLE>("HANDLE");
+}
+
+ConPtyProcess::~ConPtyProcess()
+{
+ kill();
+}
+
+bool ConPtyProcess::startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows)
+{
+ if (!isAvailable()) {
+ m_lastError = WindowsContext::instance().lastError();
+ return false;
+ }
+
+ //already running
+ if (m_ptyHandler != INVALID_HANDLE_VALUE)
+ return false;
+
+ QFileInfo fi(executable);
+ if (fi.isRelative() || !QFile::exists(executable)) {
+ //todo add auto-find executable in PATH env var
+ m_lastError = QString("ConPty Error: shell file path '%1' must be absolute").arg(executable);
+ return false;
+ }
+
+ m_shellPath = executable;
+ m_shellPath.replace('/', '\\');
+ m_size = QPair<qint16, qint16>(cols, rows);
+
+ //env
+ const QString env = environment.join(QChar(QChar::Null)) + QChar(QChar::Null);
+ LPVOID envPtr = env.isEmpty() ? nullptr : (LPVOID) env.utf16();
+
+ LPCWSTR workingDirPtr = workingDir.isEmpty() ? nullptr : (LPCWSTR) workingDir.utf16();
+
+ QStringList exeAndArgs = arguments;
+ exeAndArgs.prepend(m_shellPath);
+ std::wstring cmdArg{(LPCWSTR) (exeAndArgs.join(QLatin1String(" ")).utf16())};
+
+ HRESULT hr{E_UNEXPECTED};
+
+ // Create the Pseudo Console and pipes to it
+ hr = createPseudoConsoleAndPipes(&m_ptyHandler, &m_hPipeIn, &m_hPipeOut, cols, rows);
+
+ if (S_OK != hr) {
+ m_lastError = QString("ConPty Error: CreatePseudoConsoleAndPipes fail");
+ return false;
+ }
+
+ // Initialize the necessary startup info struct
+ if (S_OK != initializeStartupInfoAttachedToPseudoConsole(&m_shellStartupInfo, m_ptyHandler)) {
+ m_lastError = QString("ConPty Error: InitializeStartupInfoAttachedToPseudoConsole fail");
+ return false;
+ }
+
+ hr = CreateProcessW(nullptr, // No module name - use Command Line
+ cmdArg.data(), // Command Line
+ nullptr, // Process handle not inheritable
+ nullptr, // Thread handle not inheritable
+ FALSE, // Inherit handles
+ EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, // Creation flags
+ envPtr, // Environment block
+ workingDirPtr, // Use parent's starting directory
+ &m_shellStartupInfo.StartupInfo, // Pointer to STARTUPINFO
+ &m_shellProcessInformation) // Pointer to PROCESS_INFORMATION
+ ? S_OK
+ : GetLastError();
+
+ if (S_OK != hr) {
+ m_lastError = QString("ConPty Error: Cannot create process -> %1").arg(hr);
+ return false;
+ }
+
+ m_pid = m_shellProcessInformation.dwProcessId;
+
+ // Notify when the shell process has been terminated
+ m_shellCloseWaitNotifier = new QWinEventNotifier(m_shellProcessInformation.hProcess, notifier());
+ QObject::connect(m_shellCloseWaitNotifier,
+ &QWinEventNotifier::activated,
+ notifier(),
+ [this](HANDLE hEvent) {
+ DWORD exitCode = 0;
+ GetExitCodeProcess(hEvent, &exitCode);
+ m_exitCode = exitCode;
+ // Do not respawn if the object is about to be destructed
+ if (!m_aboutToDestruct)
+ emit notifier()->aboutToClose();
+ m_shellCloseWaitNotifier->setEnabled(false);
+ }, Qt::QueuedConnection);
+
+ //this code runned in separate thread
+ m_readThread = QThread::create([this]() {
+ //buffers
+ const DWORD BUFF_SIZE{1024};
+ char szBuffer[BUFF_SIZE]{};
+
+ forever {
+ DWORD dwBytesRead{};
+
+ // Read from the pipe
+ BOOL result = ReadFile(m_hPipeIn, szBuffer, BUFF_SIZE, &dwBytesRead, NULL);
+
+ const bool needMoreData = !result && GetLastError() == ERROR_MORE_DATA;
+ if (result || needMoreData) {
+ QMutexLocker locker(&m_bufferMutex);
+ m_buffer.m_readBuffer.append(szBuffer, dwBytesRead);
+ m_buffer.emitReadyRead();
+ }
+
+ const bool brokenPipe = !result && GetLastError() == ERROR_BROKEN_PIPE;
+ if (QThread::currentThread()->isInterruptionRequested() || brokenPipe)
+ break;
+ }
+
+ CancelIoEx(m_hPipeIn, nullptr);
+ });
+
+ //start read thread
+ m_readThread->start();
+
+ return true;
+}
+
+bool ConPtyProcess::resize(qint16 cols, qint16 rows)
+{
+ if (m_ptyHandler == nullptr)
+ {
+ return false;
+ }
+
+ bool res = SUCCEEDED(WindowsContext::instance().resizePseudoConsole(m_ptyHandler, {cols, rows}));
+
+ if (res)
+ {
+ m_size = QPair<qint16, qint16>(cols, rows);
+ }
+
+ return res;
+
+ return true;
+}
+
+bool ConPtyProcess::kill()
+{
+ bool exitCode = false;
+
+ if (m_ptyHandler != INVALID_HANDLE_VALUE) {
+ m_aboutToDestruct = true;
+
+ // Close ConPTY - this will terminate client process if running
+ WindowsContext::instance().closePseudoConsole(m_ptyHandler);
+
+ // Clean-up the pipes
+ if (INVALID_HANDLE_VALUE != m_hPipeOut)
+ CloseHandle(m_hPipeOut);
+ if (INVALID_HANDLE_VALUE != m_hPipeIn)
+ CloseHandle(m_hPipeIn);
+
+ if (m_readThread) {
+ m_readThread->requestInterruption();
+ if (!m_readThread->wait(1000))
+ m_readThread->terminate();
+ m_readThread->deleteLater();
+ m_readThread = nullptr;
+ }
+
+ delete m_shellCloseWaitNotifier;
+ m_shellCloseWaitNotifier = nullptr;
+
+ m_pid = 0;
+ m_ptyHandler = INVALID_HANDLE_VALUE;
+ m_hPipeIn = INVALID_HANDLE_VALUE;
+ m_hPipeOut = INVALID_HANDLE_VALUE;
+
+ CloseHandle(m_shellProcessInformation.hThread);
+ CloseHandle(m_shellProcessInformation.hProcess);
+
+ // Cleanup attribute list
+ if (m_shellStartupInfo.lpAttributeList) {
+ DeleteProcThreadAttributeList(m_shellStartupInfo.lpAttributeList);
+ HeapFree(GetProcessHeap(), 0, m_shellStartupInfo.lpAttributeList);
+ }
+
+ exitCode = true;
+ }
+
+ return exitCode;
+}
+
+IPtyProcess::PtyType ConPtyProcess::type()
+{
+ return PtyType::ConPty;
+}
+
+QString ConPtyProcess::dumpDebugInfo()
+{
+#ifdef PTYQT_DEBUG
+ return QString("PID: %1, Type: %2, Cols: %3, Rows: %4")
+ .arg(m_pid).arg(type())
+ .arg(m_size.first).arg(m_size.second);
+#else
+ return QString("Nothing...");
+#endif
+}
+
+QIODevice *ConPtyProcess::notifier()
+{
+ return &m_buffer;
+}
+
+QByteArray ConPtyProcess::readAll()
+{
+ QByteArray result;
+ {
+ QMutexLocker locker(&m_bufferMutex);
+ result.swap(m_buffer.m_readBuffer);
+ }
+ return result;
+}
+
+qint64 ConPtyProcess::write(const QByteArray &byteArray)
+{
+ DWORD dwBytesWritten{};
+ WriteFile(m_hPipeOut, byteArray.data(), byteArray.size(), &dwBytesWritten, NULL);
+ return dwBytesWritten;
+}
+
+bool ConPtyProcess::isAvailable()
+{
+#ifdef TOO_OLD_WINSDK
+ return false; //very importnant! ConPty can be built, but it doesn't work if built with old sdk and Win10 < 1903
+#endif
+
+ qint32 buildNumber = QSysInfo::kernelVersion().split(".").last().toInt();
+ if (buildNumber < CONPTY_MINIMAL_WINDOWS_VERSION)
+ return false;
+ return WindowsContext::instance().init();
+}
+
+void ConPtyProcess::moveToThread(QThread *targetThread)
+{
+ //nothing for now...
+}
diff --git a/src/libs/3rdparty/libptyqt/conptyprocess.h b/src/libs/3rdparty/libptyqt/conptyprocess.h
new file mode 100644
index 0000000000..aaf56fe76f
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/conptyprocess.h
@@ -0,0 +1,90 @@
+#ifndef CONPTYPROCESS_H
+#define CONPTYPROCESS_H
+
+#include "iptyprocess.h"
+#include <windows.h>
+#include <process.h>
+#include <stdio.h>
+
+#include <QIODevice>
+#include <QLibrary>
+#include <QMutex>
+#include <QTimer>
+#include <QThread>
+
+//Taken from the RS5 Windows SDK, but redefined here in case we're targeting <= 17733
+//Just for compile, ConPty doesn't work with Windows SDK < 17733
+#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
+#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE \
+ ProcThreadAttributeValue(22, FALSE, TRUE, FALSE)
+
+typedef VOID* HPCON;
+
+#define TOO_OLD_WINSDK
+#endif
+
+class QWinEventNotifier;
+
+class PtyBuffer : public QIODevice
+{
+ friend class ConPtyProcess;
+ Q_OBJECT
+public:
+
+ //just empty realization, we need only 'readyRead' signal of this class
+ qint64 readData(char *data, qint64 maxlen) { return 0; }
+ qint64 writeData(const char *data, qint64 len) { return 0; }
+
+ bool isSequential() const { return true; }
+ qint64 bytesAvailable() const { return m_readBuffer.size(); }
+ qint64 size() const { return m_readBuffer.size(); }
+
+ void emitReadyRead()
+ {
+ emit readyRead();
+ }
+
+private:
+ QByteArray m_readBuffer;
+};
+
+class ConPtyProcess : public IPtyProcess
+{
+public:
+ ConPtyProcess();
+ ~ConPtyProcess();
+
+ bool startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows);
+ bool resize(qint16 cols, qint16 rows);
+ bool kill();
+ PtyType type();
+ QString dumpDebugInfo();
+ virtual QIODevice *notifier();
+ virtual QByteArray readAll();
+ virtual qint64 write(const QByteArray &byteArray);
+ static bool isAvailable();
+ void moveToThread(QThread *targetThread);
+
+private:
+ HRESULT createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut, qint16 cols, qint16 rows);
+ HRESULT initializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC);
+
+private:
+ HPCON m_ptyHandler{INVALID_HANDLE_VALUE};
+ HANDLE m_hPipeIn{INVALID_HANDLE_VALUE}, m_hPipeOut{INVALID_HANDLE_VALUE};
+
+ QThread *m_readThread{nullptr};
+ QMutex m_bufferMutex;
+ PtyBuffer m_buffer;
+ bool m_aboutToDestruct{false};
+ PROCESS_INFORMATION m_shellProcessInformation{};
+ QWinEventNotifier *m_shellCloseWaitNotifier{nullptr};
+ STARTUPINFOEX m_shellStartupInfo{};
+};
+
+#endif // CONPTYPROCESS_H
diff --git a/src/libs/3rdparty/libptyqt/iptyprocess.h b/src/libs/3rdparty/libptyqt/iptyprocess.h
new file mode 100644
index 0000000000..6005b0efde
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/iptyprocess.h
@@ -0,0 +1,62 @@
+#ifndef IPTYPROCESS_H
+#define IPTYPROCESS_H
+
+#include <QDebug>
+#include <QString>
+#include <QThread>
+
+#define CONPTY_MINIMAL_WINDOWS_VERSION 18309
+
+class IPtyProcess
+{
+public:
+ enum PtyType { UnixPty = 0, WinPty = 1, ConPty = 2, AutoPty = 3 };
+ enum PtyInputFlag { None = 0x0, InputModeHidden = 0x1, };
+ Q_DECLARE_FLAGS(PtyInputFlags, PtyInputFlag)
+
+ IPtyProcess() = default;
+ IPtyProcess(const IPtyProcess &) = delete;
+ IPtyProcess &operator=(const IPtyProcess &) = delete;
+
+ virtual ~IPtyProcess() {}
+
+ virtual bool startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows)
+ = 0;
+ virtual bool resize(qint16 cols, qint16 rows) = 0;
+ virtual bool kill() = 0;
+ virtual PtyType type() = 0;
+ virtual QString dumpDebugInfo() = 0;
+ virtual QIODevice *notifier() = 0;
+ virtual QByteArray readAll() = 0;
+ virtual qint64 write(const QByteArray &byteArray) = 0;
+ virtual void moveToThread(QThread *targetThread) = 0;
+ qint64 pid() { return m_pid; }
+ QPair<qint16, qint16> size() { return m_size; }
+ const QString lastError() { return m_lastError; }
+ int exitCode() { return m_exitCode; }
+ bool toggleTrace()
+ {
+ m_trace = !m_trace;
+ return m_trace;
+ }
+
+ PtyInputFlags inputFlags() { return m_inputFlags; }
+
+protected:
+ QString m_shellPath;
+ QString m_lastError;
+ qint64 m_pid{0};
+ int m_exitCode{0};
+ QPair<qint16, qint16> m_size; //cols / rows
+ bool m_trace{false};
+ PtyInputFlags m_inputFlags;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(IPtyProcess::PtyInputFlags)
+
+#endif // IPTYPROCESS_H
diff --git a/src/libs/3rdparty/libptyqt/ptyqt.cpp b/src/libs/3rdparty/libptyqt/ptyqt.cpp
new file mode 100644
index 0000000000..06fe449819
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/ptyqt.cpp
@@ -0,0 +1,56 @@
+#include "ptyqt.h"
+#include <utility>
+
+#ifdef Q_OS_WIN
+#include "winptyprocess.h"
+#include "conptyprocess.h"
+#endif
+
+#ifdef Q_OS_UNIX
+#include "unixptyprocess.h"
+#endif
+
+bool PtyQt::isUsingConPTY()
+{
+#ifdef Q_OS_WIN
+ if (ConPtyProcess::isAvailable() && qgetenv("QTC_USE_WINPTY").isEmpty())
+ return true;
+#endif
+
+ return false;
+}
+
+IPtyProcess *PtyQt::createPtyProcess(IPtyProcess::PtyType ptyType)
+{
+ switch (ptyType)
+ {
+#ifdef Q_OS_WIN
+ case IPtyProcess::PtyType::WinPty:
+ return new WinPtyProcess();
+ break;
+ case IPtyProcess::PtyType::ConPty:
+ return new ConPtyProcess();
+ break;
+#endif
+#ifdef Q_OS_UNIX
+ case IPtyProcess::PtyType::UnixPty:
+ return new UnixPtyProcess();
+ break;
+#endif
+ case IPtyProcess::PtyType::AutoPty:
+ default:
+ break;
+ }
+
+#ifdef Q_OS_WIN
+ if (isUsingConPTY())
+ return new ConPtyProcess();
+ else
+ return new WinPtyProcess();
+#endif
+#ifdef Q_OS_UNIX
+ return new UnixPtyProcess();
+#endif
+}
+
+
diff --git a/src/libs/3rdparty/libptyqt/ptyqt.h b/src/libs/3rdparty/libptyqt/ptyqt.h
new file mode 100644
index 0000000000..85f27b33a9
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/ptyqt.h
@@ -0,0 +1,14 @@
+#ifndef PTYQT_H
+#define PTYQT_H
+
+#include "iptyprocess.h"
+
+class PtyQt
+{
+public:
+ static IPtyProcess *createPtyProcess(IPtyProcess::PtyType ptyType);
+
+ static bool isUsingConPTY();
+};
+
+#endif // PTYQT_H
diff --git a/src/libs/3rdparty/libptyqt/ptyqt.qbs b/src/libs/3rdparty/libptyqt/ptyqt.qbs
new file mode 100644
index 0000000000..3b56dd8e46
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/ptyqt.qbs
@@ -0,0 +1,40 @@
+QtcLibrary {
+ name: "ptyqt"
+ type: "staticlibrary"
+
+ Depends { name: "Qt.core" }
+ Depends { name: "Qt.network"; condition: qbs.targetOS.contains("windows") }
+ Depends { name: "winpty"; condition: qbs.targetOS.contains("windows") }
+
+ files: [
+ "iptyprocess.h",
+ "ptyqt.cpp",
+ "ptyqt.h",
+ ]
+
+ Group {
+ name: "ptyqt UNIX files"
+ condition: qbs.targetOS.contains("unix")
+ files: [
+ "unixptyprocess.cpp",
+ "unixptyprocess.h",
+ ]
+ }
+
+ Group {
+ name: "ptyqt Windows files"
+ condition: qbs.targetOS.contains("windows")
+ files: [
+ "conptyprocess.cpp",
+ "conptyprocess.h",
+ "winptyprocess.cpp",
+ "winptyprocess.h",
+ ]
+ }
+
+ Export {
+ Depends { name: "cpp" }
+ Depends { name: "winpty"; condition: qbs.targetOS.contains("windows") }
+ cpp.includePaths: exportingProduct.sourceDirectory
+ }
+}
diff --git a/src/libs/3rdparty/libptyqt/unixptyprocess.cpp b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp
new file mode 100644
index 0000000000..e9ec1d590f
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp
@@ -0,0 +1,361 @@
+#include "unixptyprocess.h"
+#include <QStandardPaths>
+
+#include <errno.h>
+#include <termios.h>
+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_FREEBSD)
+#include <utmpx.h>
+#endif
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <QCoreApplication>
+#include <QFileInfo>
+
+UnixPtyProcess::UnixPtyProcess()
+ : IPtyProcess()
+ , m_readMasterNotify(0)
+{
+ m_shellProcess.setWorkingDirectory(
+ QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
+}
+
+UnixPtyProcess::~UnixPtyProcess()
+{
+ kill();
+}
+
+bool UnixPtyProcess::startProcess(const QString &shellPath,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows)
+{
+ if (!isAvailable()) {
+ m_lastError = QString("UnixPty Error: unavailable");
+ return false;
+ }
+
+ if (m_shellProcess.state() == QProcess::Running)
+ return false;
+
+ QFileInfo fi(shellPath);
+ if (fi.isRelative() || !QFile::exists(shellPath)) {
+ //todo add auto-find executable in PATH env var
+ m_lastError = QString("UnixPty Error: shell file path must be absolute");
+ return false;
+ }
+
+ m_shellPath = shellPath;
+ m_size = QPair<qint16, qint16>(cols, rows);
+
+ int rc = 0;
+
+ m_shellProcess.m_handleMaster = ::posix_openpt(O_RDWR | O_NOCTTY);
+
+ int flags = fcntl(m_shellProcess.m_handleMaster, F_GETFL, 0);
+ fcntl(m_shellProcess.m_handleMaster, F_SETFL, flags | O_NONBLOCK);
+
+ if (m_shellProcess.m_handleMaster <= 0) {
+ m_lastError = QString("UnixPty Error: unable to open master -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ m_shellProcess.m_handleSlaveName = QLatin1String(ptsname(m_shellProcess.m_handleMaster));
+ if (m_shellProcess.m_handleSlaveName.isEmpty()) {
+ m_lastError = QString("UnixPty Error: unable to get slave name -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ rc = grantpt(m_shellProcess.m_handleMaster);
+ if (rc != 0) {
+ m_lastError
+ = QString("UnixPty Error: unable to change perms for slave -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ rc = unlockpt(m_shellProcess.m_handleMaster);
+ if (rc != 0) {
+ m_lastError = QString("UnixPty Error: unable to unlock slave -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ m_shellProcess.m_handleSlave = ::open(m_shellProcess.m_handleSlaveName.toLatin1().data(),
+ O_RDWR | O_NOCTTY);
+ if (m_shellProcess.m_handleSlave < 0) {
+ m_lastError = QString("UnixPty Error: unable to open slave -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ rc = fcntl(m_shellProcess.m_handleMaster, F_SETFD, FD_CLOEXEC);
+ if (rc == -1) {
+ m_lastError
+ = QString("UnixPty Error: unable to set flags for master -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ rc = fcntl(m_shellProcess.m_handleSlave, F_SETFD, FD_CLOEXEC);
+ if (rc == -1) {
+ m_lastError
+ = QString("UnixPty Error: unable to set flags for slave -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ struct ::termios ttmode;
+ rc = tcgetattr(m_shellProcess.m_handleMaster, &ttmode);
+ if (rc != 0) {
+ m_lastError = QString("UnixPty Error: termios fail -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ ttmode.c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT;
+#if defined(IUTF8)
+ ttmode.c_iflag |= IUTF8;
+#endif
+
+ ttmode.c_oflag = OPOST | ONLCR;
+ ttmode.c_cflag = CREAD | CS8 | HUPCL;
+ ttmode.c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL;
+
+ ttmode.c_cc[VEOF] = 4;
+ ttmode.c_cc[VEOL] = -1;
+ ttmode.c_cc[VEOL2] = -1;
+ ttmode.c_cc[VERASE] = 0x7f;
+ ttmode.c_cc[VWERASE] = 23;
+ ttmode.c_cc[VKILL] = 21;
+ ttmode.c_cc[VREPRINT] = 18;
+ ttmode.c_cc[VINTR] = 3;
+ ttmode.c_cc[VQUIT] = 0x1c;
+ ttmode.c_cc[VSUSP] = 26;
+ ttmode.c_cc[VSTART] = 17;
+ ttmode.c_cc[VSTOP] = 19;
+ ttmode.c_cc[VLNEXT] = 22;
+ ttmode.c_cc[VDISCARD] = 15;
+ ttmode.c_cc[VMIN] = 1;
+ ttmode.c_cc[VTIME] = 0;
+
+#if (__APPLE__)
+ ttmode.c_cc[VDSUSP] = 25;
+ ttmode.c_cc[VSTATUS] = 20;
+#endif
+
+ cfsetispeed(&ttmode, B38400);
+ cfsetospeed(&ttmode, B38400);
+
+ rc = tcsetattr(m_shellProcess.m_handleMaster, TCSANOW, &ttmode);
+ if (rc != 0) {
+ m_lastError
+ = QString("UnixPty Error: unabble to set associated params -> %1").arg(QLatin1String(strerror(errno)));
+ kill();
+ return false;
+ }
+
+ m_readMasterNotify = new QSocketNotifier(m_shellProcess.m_handleMaster,
+ QSocketNotifier::Read,
+ &m_shellProcess);
+ m_readMasterNotify->setEnabled(true);
+ m_readMasterNotify->moveToThread(m_shellProcess.thread());
+ QObject::connect(m_readMasterNotify, &QSocketNotifier::activated, [this](int socket) {
+ Q_UNUSED(socket)
+
+ const size_t maxRead = 16 * 1024;
+ static std::array<char, maxRead> buffer;
+
+ int len = ::read(m_shellProcess.m_handleMaster, buffer.data(), buffer.size());
+
+ struct termios termAttributes;
+ tcgetattr(m_shellProcess.m_handleMaster, &termAttributes);
+ const bool isPasswordEntry = !(termAttributes.c_lflag & ECHO) && (termAttributes.c_lflag & ICANON);
+ m_inputFlags.setFlag(PtyInputFlag::InputModeHidden, isPasswordEntry);
+
+ if (len > 0) {
+ m_shellReadBuffer.append(buffer.data(), len);
+ m_shellProcess.emitReadyRead();
+ }
+ });
+
+ QObject::connect(&m_shellProcess, &QProcess::finished, &m_shellProcess, [this](int exitCode) {
+ m_exitCode = exitCode;
+ emit m_shellProcess.aboutToClose();
+ m_readMasterNotify->disconnect();
+ });
+
+ QStringList varNames;
+ for (const QString &line : std::as_const(environment))
+ varNames.append(line.split("=").first());
+
+ QProcessEnvironment envFormat;
+ for (const QString &line : std::as_const(environment))
+ envFormat.insert(line.split("=").first(), line.split("=").last());
+
+ m_shellProcess.setWorkingDirectory(workingDir);
+ m_shellProcess.setProcessEnvironment(envFormat);
+ m_shellProcess.setReadChannel(QProcess::StandardOutput);
+ m_shellProcess.start(m_shellPath, arguments);
+ if (!m_shellProcess.waitForStarted())
+ return false;
+
+ m_pid = m_shellProcess.processId();
+
+ resize(cols, rows);
+
+ return true;
+}
+
+bool UnixPtyProcess::resize(qint16 cols, qint16 rows)
+{
+ struct winsize winp;
+ winp.ws_col = cols;
+ winp.ws_row = rows;
+ winp.ws_xpixel = 0;
+ winp.ws_ypixel = 0;
+
+ bool res = ((ioctl(m_shellProcess.m_handleMaster, TIOCSWINSZ, &winp) != -1)
+ && (ioctl(m_shellProcess.m_handleSlave, TIOCSWINSZ, &winp) != -1));
+
+ if (res) {
+ m_size = QPair<qint16, qint16>(cols, rows);
+ }
+
+ return res;
+}
+
+bool UnixPtyProcess::kill()
+{
+ m_shellProcess.m_handleSlaveName = QString();
+ if (m_shellProcess.m_handleSlave >= 0) {
+ ::close(m_shellProcess.m_handleSlave);
+ m_shellProcess.m_handleSlave = -1;
+ }
+ if (m_shellProcess.m_handleMaster >= 0) {
+ ::close(m_shellProcess.m_handleMaster);
+ m_shellProcess.m_handleMaster = -1;
+ }
+
+ if (m_shellProcess.state() == QProcess::Running) {
+ m_readMasterNotify->disconnect();
+ m_readMasterNotify->deleteLater();
+
+ m_shellProcess.terminate();
+ m_shellProcess.waitForFinished(1000);
+
+ if (m_shellProcess.state() == QProcess::Running) {
+ QProcess::startDetached(QString("kill -9 %1").arg(pid()));
+ m_shellProcess.kill();
+ m_shellProcess.waitForFinished(1000);
+ }
+
+ return (m_shellProcess.state() == QProcess::NotRunning);
+ }
+ return false;
+}
+
+IPtyProcess::PtyType UnixPtyProcess::type()
+{
+ return IPtyProcess::UnixPty;
+}
+
+QString UnixPtyProcess::dumpDebugInfo()
+{
+#ifdef PTYQT_DEBUG
+ return QString("PID: %1, In: %2, Out: %3, Type: %4, Cols: %5, Rows: %6, IsRunning: %7, Shell: "
+ "%8, SlaveName: %9")
+ .arg(m_pid)
+ .arg(m_shellProcess.m_handleMaster)
+ .arg(m_shellProcess.m_handleSlave)
+ .arg(type())
+ .arg(m_size.first)
+ .arg(m_size.second)
+ .arg(m_shellProcess.state() == QProcess::Running)
+ .arg(m_shellPath)
+ .arg(m_shellProcess.m_handleSlaveName);
+#else
+ return QString("Nothing...");
+#endif
+}
+
+QIODevice *UnixPtyProcess::notifier()
+{
+ return &m_shellProcess;
+}
+
+QByteArray UnixPtyProcess::readAll()
+{
+ QByteArray tmpBuffer = m_shellReadBuffer;
+ m_shellReadBuffer.clear();
+ return tmpBuffer;
+}
+
+qint64 UnixPtyProcess::write(const QByteArray &byteArray)
+{
+ return ::write(m_shellProcess.m_handleMaster, byteArray.constData(), byteArray.size());
+}
+
+bool UnixPtyProcess::isAvailable()
+{
+ //todo check something more if required
+ return true;
+}
+
+void UnixPtyProcess::moveToThread(QThread *targetThread)
+{
+ m_shellProcess.moveToThread(targetThread);
+}
+
+void ShellProcess::configChildProcess()
+{
+ dup2(m_handleSlave, STDIN_FILENO);
+ dup2(m_handleSlave, STDOUT_FILENO);
+ dup2(m_handleSlave, STDERR_FILENO);
+
+ pid_t sid = setsid();
+ ioctl(m_handleSlave, TIOCSCTTY, 0);
+ tcsetpgrp(m_handleSlave, sid);
+
+#if !defined(Q_OS_ANDROID) && !defined(Q_OS_FREEBSD)
+ // on Android imposible to put record to the 'utmp' file
+ struct utmpx utmpxInfo;
+ memset(&utmpxInfo, 0, sizeof(utmpxInfo));
+
+ strncpy(utmpxInfo.ut_user, qgetenv("USER"), sizeof(utmpxInfo.ut_user) - 1);
+
+ QString device(m_handleSlaveName);
+ if (device.startsWith("/dev/"))
+ device = device.mid(5);
+
+ const auto deviceAsLatin1 = device.toLatin1();
+ const char *d = deviceAsLatin1.constData();
+
+ strncpy(utmpxInfo.ut_line, d, sizeof(utmpxInfo.ut_line) - 1);
+
+ strncpy(utmpxInfo.ut_id, d + strlen(d) - sizeof(utmpxInfo.ut_id), sizeof(utmpxInfo.ut_id));
+
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ utmpxInfo.ut_tv.tv_sec = tv.tv_sec;
+ utmpxInfo.ut_tv.tv_usec = tv.tv_usec;
+
+ utmpxInfo.ut_type = USER_PROCESS;
+ utmpxInfo.ut_pid = getpid();
+
+ utmpxname(_PATH_UTMPX);
+ setutxent();
+ pututxline(&utmpxInfo);
+ endutxent();
+
+#if !defined(Q_OS_UNIX)
+ updwtmpx(_PATH_UTMPX, &loginInfo);
+#endif
+
+#endif
+}
diff --git a/src/libs/3rdparty/libptyqt/unixptyprocess.h b/src/libs/3rdparty/libptyqt/unixptyprocess.h
new file mode 100644
index 0000000000..f7e3a2e528
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/unixptyprocess.h
@@ -0,0 +1,66 @@
+#ifndef UNIXPTYPROCESS_H
+#define UNIXPTYPROCESS_H
+
+#include "iptyprocess.h"
+#include <QProcess>
+#include <QSocketNotifier>
+
+// support for build with MUSL on Alpine Linux
+#ifndef _PATH_UTMPX
+#include <sys/time.h>
+#define _PATH_UTMPX "/var/log/utmp"
+#endif
+
+class ShellProcess : public QProcess
+{
+ friend class UnixPtyProcess;
+ Q_OBJECT
+public:
+ ShellProcess()
+ : QProcess()
+ , m_handleMaster(-1)
+ , m_handleSlave(-1)
+ {
+ setProcessChannelMode(QProcess::SeparateChannels);
+ setChildProcessModifier([this]() { configChildProcess(); });
+ }
+
+ void emitReadyRead() { emit readyRead(); }
+
+protected:
+ void configChildProcess();
+
+private:
+ int m_handleMaster, m_handleSlave;
+ QString m_handleSlaveName;
+};
+
+class UnixPtyProcess : public IPtyProcess
+{
+public:
+ UnixPtyProcess();
+ virtual ~UnixPtyProcess();
+
+ virtual bool startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows);
+ virtual bool resize(qint16 cols, qint16 rows);
+ virtual bool kill();
+ virtual PtyType type();
+ virtual QString dumpDebugInfo();
+ virtual QIODevice *notifier();
+ virtual QByteArray readAll();
+ virtual qint64 write(const QByteArray &byteArray);
+ static bool isAvailable();
+ void moveToThread(QThread *targetThread);
+
+private:
+ ShellProcess m_shellProcess;
+ QSocketNotifier *m_readMasterNotify;
+ QByteArray m_shellReadBuffer;
+};
+
+#endif // UNIXPTYPROCESS_H
diff --git a/src/libs/3rdparty/libptyqt/winptyprocess.cpp b/src/libs/3rdparty/libptyqt/winptyprocess.cpp
new file mode 100644
index 0000000000..7a781bc195
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/winptyprocess.cpp
@@ -0,0 +1,278 @@
+#include "winptyprocess.h"
+#include <QFile>
+#include <QFileInfo>
+#include <sstream>
+#include <QCoreApplication>
+#include <QLocalSocket>
+#include <QWinEventNotifier>
+
+#define DEBUG_VAR_LEGACY "WINPTYDBG"
+#define DEBUG_VAR_ACTUAL "WINPTY_DEBUG"
+#define SHOW_CONSOLE_VAR "WINPTY_SHOW_CONSOLE"
+#define WINPTY_AGENT_NAME "winpty-agent.exe"
+#define WINPTY_DLL_NAME "winpty.dll"
+
+QString castErrorToString(winpty_error_ptr_t error_ptr)
+{
+ return QString::fromStdWString(winpty_error_msg(error_ptr));
+}
+
+WinPtyProcess::WinPtyProcess()
+ : IPtyProcess()
+ , m_ptyHandler(nullptr)
+ , m_innerHandle(nullptr)
+ , m_inSocket(nullptr)
+ , m_outSocket(nullptr)
+{
+#ifdef PTYQT_DEBUG
+ m_trace = true;
+#endif
+}
+
+WinPtyProcess::~WinPtyProcess()
+{
+ kill();
+}
+
+bool WinPtyProcess::startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows)
+{
+ if (!isAvailable())
+ {
+ m_lastError = QString("WinPty Error: winpty-agent.exe or winpty.dll not found!");
+ return false;
+ }
+
+ //already running
+ if (m_ptyHandler != nullptr)
+ return false;
+
+ QFileInfo fi(executable);
+ if (fi.isRelative() || !QFile::exists(executable))
+ {
+ //todo add auto-find executable in PATH env var
+ m_lastError = QString("WinPty Error: shell file path must be absolute");
+ return false;
+ }
+
+ m_shellPath = executable;
+ m_shellPath.replace('/', '\\');
+ m_size = QPair<qint16, qint16>(cols, rows);
+
+#ifdef PTYQT_DEBUG
+ if (m_trace)
+ {
+ environment.append(QString("%1=1").arg(DEBUG_VAR_LEGACY));
+ environment.append(QString("%1=trace").arg(DEBUG_VAR_ACTUAL));
+ environment.append(QString("%1=1").arg(SHOW_CONSOLE_VAR));
+ SetEnvironmentVariableA(DEBUG_VAR_LEGACY, "1");
+ SetEnvironmentVariableA(DEBUG_VAR_ACTUAL, "trace");
+ SetEnvironmentVariableA(SHOW_CONSOLE_VAR, "1");
+ }
+#endif
+
+ //env
+ std::wstringstream envBlock;
+ for (const QString &line : environment)
+ {
+ envBlock << line.toStdWString() << L'\0';
+ }
+ std::wstring env = envBlock.str();
+
+ //create start config
+ winpty_error_ptr_t errorPtr = nullptr;
+ winpty_config_t* startConfig = winpty_config_new(0, &errorPtr);
+ if (startConfig == nullptr)
+ {
+ m_lastError = QString("WinPty Error: create start config -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ //set params
+ winpty_config_set_initial_size(startConfig, cols, rows);
+ winpty_config_set_mouse_mode(startConfig, WINPTY_MOUSE_MODE_AUTO);
+ //winpty_config_set_agent_timeout();
+
+ //start agent
+ m_ptyHandler = winpty_open(startConfig, &errorPtr);
+ winpty_config_free(startConfig); //start config is local var, free it after use
+
+ if (m_ptyHandler == nullptr)
+ {
+ m_lastError = QString("WinPty Error: start agent -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ //create spawn config
+ winpty_spawn_config_t* spawnConfig = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, m_shellPath.toStdWString().c_str(),
+ //commandLine.toStdWString().c_str(), cwd.toStdWString().c_str(),
+ arguments.join(" ").toStdWString().c_str(), workingDir.toStdWString().c_str(),
+ env.c_str(),
+ &errorPtr);
+
+ if (spawnConfig == nullptr)
+ {
+ m_lastError = QString("WinPty Error: create spawn config -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ //spawn the new process
+ BOOL spawnSuccess = winpty_spawn(m_ptyHandler, spawnConfig, &m_innerHandle, nullptr, nullptr, &errorPtr);
+ winpty_spawn_config_free(spawnConfig); //spawn config is local var, free it after use
+ if (!spawnSuccess)
+ {
+ m_lastError = QString("WinPty Error: start terminal process -> %1").arg(castErrorToString(errorPtr));
+ return false;
+ }
+ winpty_error_free(errorPtr);
+
+ m_pid = (int)GetProcessId(m_innerHandle);
+
+ m_outSocket = new QLocalSocket();
+
+ // Notify when the shell process has been terminated
+ m_shellCloseWaitNotifier = new QWinEventNotifier(m_innerHandle, notifier());
+ QObject::connect(m_shellCloseWaitNotifier,
+ &QWinEventNotifier::activated,
+ notifier(),
+ [this](HANDLE hEvent) {
+ DWORD exitCode = 0;
+ GetExitCodeProcess(hEvent, &exitCode);
+ m_exitCode = exitCode;
+ // Do not respawn if the object is about to be destructed
+ if (!m_aboutToDestruct)
+ emit notifier()->aboutToClose();
+ m_shellCloseWaitNotifier->setEnabled(false);
+ });
+
+ //get pipe names
+ LPCWSTR conInPipeName = winpty_conin_name(m_ptyHandler);
+ m_conInName = QString::fromStdWString(std::wstring(conInPipeName));
+ m_inSocket = new QLocalSocket();
+ m_inSocket->connectToServer(m_conInName, QIODevice::WriteOnly);
+ m_inSocket->waitForConnected();
+
+ LPCWSTR conOutPipeName = winpty_conout_name(m_ptyHandler);
+ m_conOutName = QString::fromStdWString(std::wstring(conOutPipeName));
+ m_outSocket->connectToServer(m_conOutName, QIODevice::ReadOnly);
+ m_outSocket->waitForConnected();
+
+ if (m_inSocket->state() != QLocalSocket::ConnectedState && m_outSocket->state() != QLocalSocket::ConnectedState)
+ {
+ m_lastError = QString("WinPty Error: Unable to connect local sockets -> %1 / %2").arg(m_inSocket->errorString()).arg(m_outSocket->errorString());
+ m_inSocket->deleteLater();
+ m_outSocket->deleteLater();
+ m_inSocket = nullptr;
+ m_outSocket = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+bool WinPtyProcess::resize(qint16 cols, qint16 rows)
+{
+ if (m_ptyHandler == nullptr)
+ {
+ return false;
+ }
+
+ bool res = winpty_set_size(m_ptyHandler, cols, rows, nullptr);
+
+ if (res)
+ {
+ m_size = QPair<qint16, qint16>(cols, rows);
+ }
+
+ return res;
+}
+
+bool WinPtyProcess::kill()
+{
+ bool exitCode = false;
+ if (m_innerHandle != nullptr && m_ptyHandler != nullptr) {
+ m_aboutToDestruct = true;
+ //disconnect all signals (readyRead, ...)
+ m_inSocket->disconnect();
+ m_outSocket->disconnect();
+
+ //disconnect for server
+ m_inSocket->disconnectFromServer();
+ m_outSocket->disconnectFromServer();
+
+ m_inSocket->deleteLater();
+ m_outSocket->deleteLater();
+
+ m_inSocket = nullptr;
+ m_outSocket = nullptr;
+
+ winpty_free(m_ptyHandler);
+ exitCode = CloseHandle(m_innerHandle);
+
+ delete m_shellCloseWaitNotifier;
+ m_shellCloseWaitNotifier = nullptr;
+
+ m_ptyHandler = nullptr;
+ m_innerHandle = nullptr;
+ m_conInName = QString();
+ m_conOutName = QString();
+ m_pid = 0;
+ }
+ return exitCode;
+}
+
+IPtyProcess::PtyType WinPtyProcess::type()
+{
+ return PtyType::WinPty;
+}
+
+QString WinPtyProcess::dumpDebugInfo()
+{
+#ifdef PTYQT_DEBUG
+ return QString("PID: %1, ConIn: %2, ConOut: %3, Type: %4, Cols: %5, Rows: %6, IsRunning: %7, Shell: %8")
+ .arg(m_pid).arg(m_conInName).arg(m_conOutName).arg(type())
+ .arg(m_size.first).arg(m_size.second).arg(m_ptyHandler != nullptr)
+ .arg(m_shellPath);
+#else
+ return QString("Nothing...");
+#endif
+}
+
+QIODevice *WinPtyProcess::notifier()
+{
+ return m_outSocket;
+}
+
+QByteArray WinPtyProcess::readAll()
+{
+ return m_outSocket->readAll();
+}
+
+qint64 WinPtyProcess::write(const QByteArray &byteArray)
+{
+ return m_inSocket->write(byteArray);
+}
+
+bool WinPtyProcess::isAvailable()
+{
+#ifdef PTYQT_BUILD_STATIC
+ return QFile::exists(QCoreApplication::applicationDirPath() + "/" + WINPTY_AGENT_NAME);
+#elif PTYQT_BUILD_DYNAMIC
+ return QFile::exists(QCoreApplication::applicationDirPath() + "/" + WINPTY_AGENT_NAME)
+ && QFile::exists(QCoreApplication::applicationDirPath() + "/" + WINPTY_DLL_NAME);
+#endif
+ return true;
+}
+
+void WinPtyProcess::moveToThread(QThread *targetThread)
+{
+ m_inSocket->moveToThread(targetThread);
+ m_outSocket->moveToThread(targetThread);
+}
diff --git a/src/libs/3rdparty/libptyqt/winptyprocess.h b/src/libs/3rdparty/libptyqt/winptyprocess.h
new file mode 100644
index 0000000000..ee91fbdf8d
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/winptyprocess.h
@@ -0,0 +1,43 @@
+#ifndef WINPTYPROCESS_H
+#define WINPTYPROCESS_H
+
+#include "iptyprocess.h"
+#include "winpty.h"
+
+class QLocalSocket;
+class QWinEventNotifier;
+
+class WinPtyProcess : public IPtyProcess
+{
+public:
+ WinPtyProcess();
+ ~WinPtyProcess();
+
+ bool startProcess(const QString &executable,
+ const QStringList &arguments,
+ const QString &workingDir,
+ QStringList environment,
+ qint16 cols,
+ qint16 rows);
+ bool resize(qint16 cols, qint16 rows);
+ bool kill();
+ PtyType type();
+ QString dumpDebugInfo();
+ QIODevice *notifier();
+ QByteArray readAll();
+ qint64 write(const QByteArray &byteArray);
+ static bool isAvailable();
+ void moveToThread(QThread *targetThread);
+
+private:
+ winpty_t *m_ptyHandler;
+ HANDLE m_innerHandle;
+ QString m_conInName;
+ QString m_conOutName;
+ QLocalSocket *m_inSocket;
+ QLocalSocket *m_outSocket;
+ bool m_aboutToDestruct{false};
+ QWinEventNotifier* m_shellCloseWaitNotifier;
+};
+
+#endif // WINPTYPROCESS_H
diff --git a/src/libs/3rdparty/libvterm/CMakeLists.txt b/src/libs/3rdparty/libvterm/CMakeLists.txt
new file mode 100644
index 0000000000..232217d9f5
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_qtc_library(libvterm STATIC
+ PUBLIC_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include
+ PROPERTIES QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
+ SOURCES
+ src/encoding.c
+ src/fullwidth.inc
+ src/keyboard.c
+ src/mouse.c
+ src/parser.c
+ src/pen.c
+ src/rect.h
+ src/screen.c
+ src/state.c
+ src/unicode.c
+ src/utf8.h
+ src/vterm.c
+ src/vterm_internal.h
+)
diff --git a/src/libs/3rdparty/libvterm/CONTRIBUTING b/src/libs/3rdparty/libvterm/CONTRIBUTING
new file mode 100644
index 0000000000..e9a8f0c331
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/CONTRIBUTING
@@ -0,0 +1,22 @@
+How to Contribute
+-----------------
+
+The main resources for this library are:
+
+ Launchpad
+ https://launchpad.net/libvterm
+
+ IRC:
+ ##tty or #tickit on irc.libera.chat
+
+ Email:
+ Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
+
+
+Bug reports and feature requests can be sent to any of the above resources.
+
+New features, bug patches, etc.. should in the first instance be discussed via
+any of the resources listed above, before starting work on the actual code.
+There may be future plans or development already in-progress that could be
+affected so it is better to discuss the ideas first before starting work
+actually writing any code.
diff --git a/src/libs/3rdparty/libvterm/LICENSE b/src/libs/3rdparty/libvterm/LICENSE
new file mode 100644
index 0000000000..0d051634b2
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/LICENSE
@@ -0,0 +1,23 @@
+
+
+The MIT License
+
+Copyright (c) 2008 Paul Evans <leonerd@leonerd.org.uk>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/libs/3rdparty/libvterm/include/vterm.h b/src/libs/3rdparty/libvterm/include/vterm.h
new file mode 100644
index 0000000000..d08dd382db
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/include/vterm.h
@@ -0,0 +1,639 @@
+#ifndef __VTERM_H__
+#define __VTERM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "vterm_keycodes.h"
+
+#define VTERM_VERSION_MAJOR 0
+#define VTERM_VERSION_MINOR 3
+#define VTERM_VERSION_PATCH 3
+
+#define VTERM_CHECK_VERSION \
+ vterm_check_version(VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR)
+
+/* Any cell can contain at most one basic printing character and 5 combining
+ * characters. This number could be changed but will be ABI-incompatible if
+ * you do */
+#define VTERM_MAX_CHARS_PER_CELL 6
+
+typedef struct VTerm VTerm;
+typedef struct VTermState VTermState;
+typedef struct VTermScreen VTermScreen;
+
+typedef struct {
+ int row;
+ int col;
+} VTermPos;
+
+/* some small utility functions; we can just keep these static here */
+
+/* order points by on-screen flow order */
+static inline int vterm_pos_cmp(VTermPos a, VTermPos b)
+{
+ return (a.row == b.row) ? a.col - b.col : a.row - b.row;
+}
+
+typedef struct {
+ int start_row;
+ int end_row;
+ int start_col;
+ int end_col;
+} VTermRect;
+
+/* true if the rect contains the point */
+static inline int vterm_rect_contains(VTermRect r, VTermPos p)
+{
+ return p.row >= r.start_row && p.row < r.end_row &&
+ p.col >= r.start_col && p.col < r.end_col;
+}
+
+/* move a rect */
+static inline void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta)
+{
+ rect->start_row += row_delta; rect->end_row += row_delta;
+ rect->start_col += col_delta; rect->end_col += col_delta;
+}
+
+/**
+ * Bit-field describing the content of the tagged union `VTermColor`.
+ */
+typedef enum {
+ /**
+ * If the lower bit of `type` is not set, the colour is 24-bit RGB.
+ */
+ VTERM_COLOR_RGB = 0x00,
+
+ /**
+ * The colour is an index into a palette of 256 colours.
+ */
+ VTERM_COLOR_INDEXED = 0x01,
+
+ /**
+ * Mask that can be used to extract the RGB/Indexed bit.
+ */
+ VTERM_COLOR_TYPE_MASK = 0x01,
+
+ /**
+ * If set, indicates that this colour should be the default foreground
+ * color, i.e. there was no SGR request for another colour. When
+ * rendering this colour it is possible to ignore "idx" and just use a
+ * colour that is not in the palette.
+ */
+ VTERM_COLOR_DEFAULT_FG = 0x02,
+
+ /**
+ * If set, indicates that this colour should be the default background
+ * color, i.e. there was no SGR request for another colour. A common
+ * option when rendering this colour is to not render a background at
+ * all, for example by rendering the window transparently at this spot.
+ */
+ VTERM_COLOR_DEFAULT_BG = 0x04,
+
+ /**
+ * Mask that can be used to extract the default foreground/background bit.
+ */
+ VTERM_COLOR_DEFAULT_MASK = 0x06
+} VTermColorType;
+
+/**
+ * Returns true if the VTERM_COLOR_RGB `type` flag is set, indicating that the
+ * given VTermColor instance is an indexed colour.
+ */
+#define VTERM_COLOR_IS_INDEXED(col) \
+ (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_INDEXED)
+
+/**
+ * Returns true if the VTERM_COLOR_INDEXED `type` flag is set, indicating that
+ * the given VTermColor instance is an rgb colour.
+ */
+#define VTERM_COLOR_IS_RGB(col) \
+ (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB)
+
+/**
+ * Returns true if the VTERM_COLOR_DEFAULT_FG `type` flag is set, indicating
+ * that the given VTermColor instance corresponds to the default foreground
+ * color.
+ */
+#define VTERM_COLOR_IS_DEFAULT_FG(col) \
+ (!!((col)->type & VTERM_COLOR_DEFAULT_FG))
+
+/**
+ * Returns true if the VTERM_COLOR_DEFAULT_BG `type` flag is set, indicating
+ * that the given VTermColor instance corresponds to the default background
+ * color.
+ */
+#define VTERM_COLOR_IS_DEFAULT_BG(col) \
+ (!!((col)->type & VTERM_COLOR_DEFAULT_BG))
+
+/**
+ * Tagged union storing either an RGB color or an index into a colour palette.
+ * In order to convert indexed colours to RGB, you may use the
+ * vterm_state_convert_color_to_rgb() or vterm_screen_convert_color_to_rgb()
+ * functions which lookup the RGB colour from the palette maintained by a
+ * VTermState or VTermScreen instance.
+ */
+typedef union {
+ /**
+ * Tag indicating which union member is actually valid. This variable
+ * coincides with the `type` member of the `rgb` and the `indexed` struct
+ * in memory. Please use the `VTERM_COLOR_IS_*` test macros to check whether
+ * a particular type flag is set.
+ */
+ uint8_t type;
+
+ /**
+ * Valid if `VTERM_COLOR_IS_RGB(type)` is true. Holds the RGB colour values.
+ */
+ struct {
+ /**
+ * Same as the top-level `type` member stored in VTermColor.
+ */
+ uint8_t type;
+
+ /**
+ * The actual 8-bit red, green, blue colour values.
+ */
+ uint8_t red, green, blue;
+ } rgb;
+
+ /**
+ * If `VTERM_COLOR_IS_INDEXED(type)` is true, this member holds the index into
+ * the colour palette.
+ */
+ struct {
+ /**
+ * Same as the top-level `type` member stored in VTermColor.
+ */
+ uint8_t type;
+
+ /**
+ * Index into the colour map.
+ */
+ uint8_t idx;
+ } indexed;
+} VTermColor;
+
+/**
+ * Constructs a new VTermColor instance representing the given RGB values.
+ */
+static inline void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green,
+ uint8_t blue)
+{
+ col->type = VTERM_COLOR_RGB;
+ col->rgb.red = red;
+ col->rgb.green = green;
+ col->rgb.blue = blue;
+}
+
+/**
+ * Construct a new VTermColor instance representing an indexed color with the
+ * given index.
+ */
+static inline void vterm_color_indexed(VTermColor *col, uint8_t idx)
+{
+ col->type = VTERM_COLOR_INDEXED;
+ col->indexed.idx = idx;
+}
+
+/**
+ * Compares two colours. Returns true if the colors are equal, false otherwise.
+ */
+int vterm_color_is_equal(const VTermColor *a, const VTermColor *b);
+
+typedef enum {
+ /* VTERM_VALUETYPE_NONE = 0 */
+ VTERM_VALUETYPE_BOOL = 1,
+ VTERM_VALUETYPE_INT,
+ VTERM_VALUETYPE_STRING,
+ VTERM_VALUETYPE_COLOR,
+
+ VTERM_N_VALUETYPES
+} VTermValueType;
+
+typedef struct {
+ const char *str;
+ size_t len : 30;
+ bool initial : 1;
+ bool final : 1;
+} VTermStringFragment;
+
+typedef union {
+ int boolean;
+ int number;
+ VTermStringFragment string;
+ VTermColor color;
+} VTermValue;
+
+typedef enum {
+ /* VTERM_ATTR_NONE = 0 */
+ VTERM_ATTR_BOLD = 1, // bool: 1, 22
+ VTERM_ATTR_UNDERLINE, // number: 4, 21, 24
+ VTERM_ATTR_ITALIC, // bool: 3, 23
+ VTERM_ATTR_BLINK, // bool: 5, 25
+ VTERM_ATTR_REVERSE, // bool: 7, 27
+ VTERM_ATTR_CONCEAL, // bool: 8, 28
+ VTERM_ATTR_STRIKE, // bool: 9, 29
+ VTERM_ATTR_FONT, // number: 10-19
+ VTERM_ATTR_FOREGROUND, // color: 30-39 90-97
+ VTERM_ATTR_BACKGROUND, // color: 40-49 100-107
+ VTERM_ATTR_SMALL, // bool: 73, 74, 75
+ VTERM_ATTR_BASELINE, // number: 73, 74, 75
+
+ VTERM_N_ATTRS
+} VTermAttr;
+
+typedef enum {
+ /* VTERM_PROP_NONE = 0 */
+ VTERM_PROP_CURSORVISIBLE = 1, // bool
+ VTERM_PROP_CURSORBLINK, // bool
+ VTERM_PROP_ALTSCREEN, // bool
+ VTERM_PROP_TITLE, // string
+ VTERM_PROP_ICONNAME, // string
+ VTERM_PROP_REVERSE, // bool
+ VTERM_PROP_CURSORSHAPE, // number
+ VTERM_PROP_MOUSE, // number
+ VTERM_PROP_FOCUSREPORT, // bool
+
+ VTERM_N_PROPS
+} VTermProp;
+
+enum {
+ VTERM_PROP_CURSORSHAPE_BLOCK = 1,
+ VTERM_PROP_CURSORSHAPE_UNDERLINE,
+ VTERM_PROP_CURSORSHAPE_BAR_LEFT,
+
+ VTERM_N_PROP_CURSORSHAPES
+};
+
+enum {
+ VTERM_PROP_MOUSE_NONE = 0,
+ VTERM_PROP_MOUSE_CLICK,
+ VTERM_PROP_MOUSE_DRAG,
+ VTERM_PROP_MOUSE_MOVE,
+
+ VTERM_N_PROP_MOUSES
+};
+
+typedef enum {
+ VTERM_SELECTION_CLIPBOARD = (1<<0),
+ VTERM_SELECTION_PRIMARY = (1<<1),
+ VTERM_SELECTION_SECONDARY = (1<<2),
+ VTERM_SELECTION_SELECT = (1<<3),
+ VTERM_SELECTION_CUT0 = (1<<4), /* also CUT1 .. CUT7 by bitshifting */
+} VTermSelectionMask;
+
+typedef struct {
+ const uint32_t *chars;
+ int width;
+ unsigned int protected_cell:1; /* DECSCA-protected against DECSEL/DECSED */
+ unsigned int dwl:1; /* DECDWL or DECDHL double-width line */
+ unsigned int dhl:2; /* DECDHL double-height line (1=top 2=bottom) */
+} VTermGlyphInfo;
+
+typedef struct {
+ unsigned int doublewidth:1; /* DECDWL or DECDHL line */
+ unsigned int doubleheight:2; /* DECDHL line (1=top 2=bottom) */
+ unsigned int continuation:1; /* Line is a flow continuation of the previous */
+} VTermLineInfo;
+
+/* Copies of VTermState fields that the 'resize' callback might have reason to
+ * edit. 'resize' callback gets total control of these fields and may
+ * free-and-reallocate them if required. They will be copied back from the
+ * struct after the callback has returned.
+ */
+typedef struct {
+ VTermPos pos; /* current cursor position */
+ VTermLineInfo *lineinfos[2]; /* [1] may be NULL */
+} VTermStateFields;
+
+typedef struct {
+ /* libvterm relies on this memory to be zeroed out before it is returned
+ * by the allocator. */
+ void *(*malloc)(size_t size, void *allocdata);
+ void (*free)(void *ptr, void *allocdata);
+} VTermAllocatorFunctions;
+
+void vterm_check_version(int major, int minor);
+
+struct VTermBuilder {
+ int ver; /* currently unused but reserved for some sort of ABI version flag */
+
+ int rows, cols;
+
+ const VTermAllocatorFunctions *allocator;
+ void *allocdata;
+
+ /* Override default sizes for various structures */
+ size_t outbuffer_len; /* default: 4096 */
+ size_t tmpbuffer_len; /* default: 4096 */
+};
+
+VTerm *vterm_build(const struct VTermBuilder *builder);
+
+/* A convenient shortcut for default cases */
+VTerm *vterm_new(int rows, int cols);
+/* This shortcuts are generally discouraged in favour of just using vterm_build() */
+VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata);
+
+void vterm_free(VTerm* vt);
+
+void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp);
+void vterm_set_size(VTerm *vt, int rows, int cols);
+
+int vterm_get_utf8(const VTerm *vt);
+void vterm_set_utf8(VTerm *vt, int is_utf8);
+
+size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len);
+
+/* Setting output callback will override the buffer logic */
+typedef void VTermOutputCallback(const char *s, size_t len, void *user);
+void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user);
+
+/* These buffer functions only work if output callback is NOT set
+ * These are deprecated and will be removed in a later version */
+size_t vterm_output_get_buffer_size(const VTerm *vt);
+size_t vterm_output_get_buffer_current(const VTerm *vt);
+size_t vterm_output_get_buffer_remaining(const VTerm *vt);
+
+/* This too */
+size_t vterm_output_read(VTerm *vt, char *buffer, size_t len);
+
+void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod);
+void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod);
+
+void vterm_keyboard_start_paste(VTerm *vt);
+void vterm_keyboard_end_paste(VTerm *vt);
+
+void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod);
+void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod);
+
+// ------------
+// Parser layer
+// ------------
+
+/* Flag to indicate non-final subparameters in a single CSI parameter.
+ * Consider
+ * CSI 1;2:3:4;5a
+ * 1 4 and 5 are final.
+ * 2 and 3 are non-final and will have this bit set
+ *
+ * Don't confuse this with the final byte of the CSI escape; 'a' in this case.
+ */
+#define CSI_ARG_FLAG_MORE (1U<<31)
+#define CSI_ARG_MASK (~(1U<<31))
+
+#define CSI_ARG_HAS_MORE(a) ((a) & CSI_ARG_FLAG_MORE)
+#define CSI_ARG(a) ((a) & CSI_ARG_MASK)
+
+/* Can't use -1 to indicate a missing argument; use this instead */
+#define CSI_ARG_MISSING ((1UL<<31)-1)
+
+#define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
+#define CSI_ARG_OR(a,def) (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : CSI_ARG(a))
+#define CSI_ARG_COUNT(a) (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 0 ? 1 : CSI_ARG(a))
+
+typedef struct {
+ int (*text)(const char *bytes, size_t len, void *user);
+ int (*control)(unsigned char control, void *user);
+ int (*escape)(const char *bytes, size_t len, void *user);
+ int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
+ int (*osc)(int command, VTermStringFragment frag, void *user);
+ int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
+ int (*apc)(VTermStringFragment frag, void *user);
+ int (*pm)(VTermStringFragment frag, void *user);
+ int (*sos)(VTermStringFragment frag, void *user);
+ int (*resize)(int rows, int cols, void *user);
+} VTermParserCallbacks;
+
+void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user);
+void *vterm_parser_get_cbdata(VTerm *vt);
+
+/* Normally NUL, CAN, SUB and DEL are ignored. Setting this true causes them
+ * to be emitted by the 'control' callback
+ */
+void vterm_parser_set_emit_nul(VTerm *vt, bool emit);
+
+// -----------
+// State layer
+// -----------
+
+typedef struct {
+ int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
+ int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+ int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
+ int (*moverect)(VTermRect dest, VTermRect src, void *user);
+ int (*erase)(VTermRect rect, int selective, void *user);
+ int (*initpen)(void *user);
+ int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
+ int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+ int (*bell)(void *user);
+ int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
+ int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
+ int (*sb_clear)(void *user);
+} VTermStateCallbacks;
+
+typedef struct {
+ int (*control)(unsigned char control, void *user);
+ int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
+ int (*osc)(int command, VTermStringFragment frag, void *user);
+ int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
+ int (*apc)(VTermStringFragment frag, void *user);
+ int (*pm)(VTermStringFragment frag, void *user);
+ int (*sos)(VTermStringFragment frag, void *user);
+} VTermStateFallbacks;
+
+typedef struct {
+ int (*set)(VTermSelectionMask mask, VTermStringFragment frag, void *user);
+ int (*query)(VTermSelectionMask mask, void *user);
+} VTermSelectionCallbacks;
+
+VTermState *vterm_obtain_state(VTerm *vt);
+
+void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user);
+void *vterm_state_get_cbdata(VTermState *state);
+
+void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user);
+void *vterm_state_get_unrecognised_fbdata(VTermState *state);
+
+void vterm_state_reset(VTermState *state, int hard);
+void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
+void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg);
+void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col);
+void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg);
+void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col);
+void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright);
+int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
+int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val);
+void vterm_state_focus_in(VTermState *state);
+void vterm_state_focus_out(VTermState *state);
+const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row);
+
+/**
+ * Makes sure that the given color `col` is indeed an RGB colour. After this
+ * function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other
+ * flags stored in `col->type` will have been reset.
+ *
+ * @param state is the VTermState instance from which the colour palette should
+ * be extracted.
+ * @param col is a pointer at the VTermColor instance that should be converted
+ * to an RGB colour.
+ */
+void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col);
+
+void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user,
+ char *buffer, size_t buflen);
+
+void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag);
+
+// ------------
+// Screen layer
+// ------------
+
+typedef struct {
+ unsigned int bold : 1;
+ unsigned int underline : 3;
+ unsigned int italic : 1;
+ unsigned int blink : 1;
+ unsigned int reverse : 1;
+ unsigned int conceal : 1;
+ unsigned int strike : 1;
+ unsigned int font : 4; /* 0 to 9 */
+ unsigned int dwl : 1; /* On a DECDWL or DECDHL line */
+ unsigned int dhl : 2; /* On a DECDHL line (1=top 2=bottom) */
+ unsigned int small : 1;
+ unsigned int baseline : 2;
+} VTermScreenCellAttrs;
+
+enum {
+ VTERM_UNDERLINE_OFF,
+ VTERM_UNDERLINE_SINGLE,
+ VTERM_UNDERLINE_DOUBLE,
+ VTERM_UNDERLINE_CURLY,
+ VTERM_UNDERLINE_DOTTED,
+ VTERM_UNDERLINE_DASHED
+};
+
+enum {
+ VTERM_BASELINE_NORMAL,
+ VTERM_BASELINE_RAISE,
+ VTERM_BASELINE_LOWER,
+};
+
+typedef struct {
+ uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
+ char width;
+ VTermScreenCellAttrs attrs;
+ VTermColor fg, bg;
+} VTermScreenCell;
+
+typedef struct {
+ int (*damage)(VTermRect rect, void *user);
+ int (*moverect)(VTermRect dest, VTermRect src, void *user);
+ int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+ int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+ int (*bell)(void *user);
+ int (*resize)(int rows, int cols, void *user);
+ int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
+ int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
+ int (*sb_clear)(void* user);
+} VTermScreenCallbacks;
+
+VTermScreen *vterm_obtain_screen(VTerm *vt);
+
+void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user);
+void *vterm_screen_get_cbdata(VTermScreen *screen);
+
+void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user);
+void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen);
+
+void vterm_screen_enable_reflow(VTermScreen *screen, bool reflow);
+
+// Back-compat alias for the brief time it was in 0.3-RC1
+#define vterm_screen_set_reflow vterm_screen_enable_reflow
+
+void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen);
+
+typedef enum {
+ VTERM_DAMAGE_CELL, /* every cell */
+ VTERM_DAMAGE_ROW, /* entire rows */
+ VTERM_DAMAGE_SCREEN, /* entire screen */
+ VTERM_DAMAGE_SCROLL, /* entire screen + scrollrect */
+
+ VTERM_N_DAMAGES
+} VTermDamageSize;
+
+void vterm_screen_flush_damage(VTermScreen *screen);
+void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size);
+
+void vterm_screen_reset(VTermScreen *screen, int hard);
+
+/* Neither of these functions NUL-terminate the buffer */
+size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect);
+size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect);
+
+typedef enum {
+ VTERM_ATTR_BOLD_MASK = 1 << 0,
+ VTERM_ATTR_UNDERLINE_MASK = 1 << 1,
+ VTERM_ATTR_ITALIC_MASK = 1 << 2,
+ VTERM_ATTR_BLINK_MASK = 1 << 3,
+ VTERM_ATTR_REVERSE_MASK = 1 << 4,
+ VTERM_ATTR_STRIKE_MASK = 1 << 5,
+ VTERM_ATTR_FONT_MASK = 1 << 6,
+ VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
+ VTERM_ATTR_BACKGROUND_MASK = 1 << 8,
+ VTERM_ATTR_CONCEAL_MASK = 1 << 9,
+ VTERM_ATTR_SMALL_MASK = 1 << 10,
+ VTERM_ATTR_BASELINE_MASK = 1 << 11,
+
+ VTERM_ALL_ATTRS_MASK = (1 << 12) - 1
+} VTermAttrMask;
+
+int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
+
+int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell);
+
+int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
+
+/**
+ * Same as vterm_state_convert_color_to_rgb(), but takes a `screen` instead of a `state`
+ * instance.
+ */
+void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col);
+
+/**
+ * Similar to vterm_state_set_default_colors(), but also resets colours in the
+ * screen buffer(s)
+ */
+void vterm_screen_set_default_colors(VTermScreen *screen, const VTermColor *default_fg, const VTermColor *default_bg);
+
+// ---------
+// Utilities
+// ---------
+
+VTermValueType vterm_get_attr_type(VTermAttr attr);
+VTermValueType vterm_get_prop_type(VTermProp prop);
+
+void vterm_scroll_rect(VTermRect rect,
+ int downward,
+ int rightward,
+ int (*moverect)(VTermRect src, VTermRect dest, void *user),
+ int (*eraserect)(VTermRect rect, int selective, void *user),
+ void *user);
+
+void vterm_copy_cells(VTermRect dest,
+ VTermRect src,
+ void (*copycell)(VTermPos dest, VTermPos src, void *user),
+ void *user);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libs/3rdparty/libvterm/include/vterm_keycodes.h b/src/libs/3rdparty/libvterm/include/vterm_keycodes.h
new file mode 100644
index 0000000000..661759febd
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/include/vterm_keycodes.h
@@ -0,0 +1,61 @@
+#ifndef __VTERM_INPUT_H__
+#define __VTERM_INPUT_H__
+
+typedef enum {
+ VTERM_MOD_NONE = 0x00,
+ VTERM_MOD_SHIFT = 0x01,
+ VTERM_MOD_ALT = 0x02,
+ VTERM_MOD_CTRL = 0x04,
+
+ VTERM_ALL_MODS_MASK = 0x07
+} VTermModifier;
+
+typedef enum {
+ VTERM_KEY_NONE,
+
+ VTERM_KEY_ENTER,
+ VTERM_KEY_TAB,
+ VTERM_KEY_BACKSPACE,
+ VTERM_KEY_ESCAPE,
+
+ VTERM_KEY_UP,
+ VTERM_KEY_DOWN,
+ VTERM_KEY_LEFT,
+ VTERM_KEY_RIGHT,
+
+ VTERM_KEY_INS,
+ VTERM_KEY_DEL,
+ VTERM_KEY_HOME,
+ VTERM_KEY_END,
+ VTERM_KEY_PAGEUP,
+ VTERM_KEY_PAGEDOWN,
+
+ VTERM_KEY_FUNCTION_0 = 256,
+ VTERM_KEY_FUNCTION_MAX = VTERM_KEY_FUNCTION_0 + 255,
+
+ VTERM_KEY_KP_0,
+ VTERM_KEY_KP_1,
+ VTERM_KEY_KP_2,
+ VTERM_KEY_KP_3,
+ VTERM_KEY_KP_4,
+ VTERM_KEY_KP_5,
+ VTERM_KEY_KP_6,
+ VTERM_KEY_KP_7,
+ VTERM_KEY_KP_8,
+ VTERM_KEY_KP_9,
+ VTERM_KEY_KP_MULT,
+ VTERM_KEY_KP_PLUS,
+ VTERM_KEY_KP_COMMA,
+ VTERM_KEY_KP_MINUS,
+ VTERM_KEY_KP_PERIOD,
+ VTERM_KEY_KP_DIVIDE,
+ VTERM_KEY_KP_ENTER,
+ VTERM_KEY_KP_EQUAL,
+
+ VTERM_KEY_MAX, // Must be last
+ VTERM_N_KEYS = VTERM_KEY_MAX
+} VTermKey;
+
+#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0+(n))
+
+#endif
diff --git a/src/libs/3rdparty/libvterm/src/encoding.c b/src/libs/3rdparty/libvterm/src/encoding.c
new file mode 100644
index 0000000000..434ac3f99b
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/encoding.c
@@ -0,0 +1,230 @@
+#include "vterm_internal.h"
+
+#define UNICODE_INVALID 0xFFFD
+
+#if defined(DEBUG) && DEBUG > 1
+# define DEBUG_PRINT_UTF8
+#endif
+
+struct UTF8DecoderData {
+ // number of bytes remaining in this codepoint
+ int bytes_remaining;
+
+ // number of bytes total in this codepoint once it's finished
+ // (for detecting overlongs)
+ int bytes_total;
+
+ int this_cp;
+};
+
+static void init_utf8(VTermEncoding *enc, void *data_)
+{
+ struct UTF8DecoderData *data = data_;
+
+ data->bytes_remaining = 0;
+ data->bytes_total = 0;
+}
+
+static void decode_utf8(VTermEncoding *enc, void *data_,
+ uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ struct UTF8DecoderData *data = data_;
+
+#ifdef DEBUG_PRINT_UTF8
+ printf("BEGIN UTF-8\n");
+#endif
+
+ for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ unsigned char c = bytes[*pos];
+
+#ifdef DEBUG_PRINT_UTF8
+ printf(" pos=%zd c=%02x rem=%d\n", *pos, c, data->bytes_remaining);
+#endif
+
+ if(c < 0x20) // C0
+ return;
+
+ else if(c >= 0x20 && c < 0x7f) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ cp[(*cpi)++] = c;
+#ifdef DEBUG_PRINT_UTF8
+ printf(" UTF-8 char: U+%04x\n", c);
+#endif
+ data->bytes_remaining = 0;
+ }
+
+ else if(c == 0x7f) // DEL
+ return;
+
+ else if(c >= 0x80 && c < 0xc0) {
+ if(!data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ continue;
+ }
+
+ data->this_cp <<= 6;
+ data->this_cp |= c & 0x3f;
+ data->bytes_remaining--;
+
+ if(!data->bytes_remaining) {
+#ifdef DEBUG_PRINT_UTF8
+ printf(" UTF-8 raw char U+%04x bytelen=%d ", data->this_cp, data->bytes_total);
+#endif
+ // Check for overlong sequences
+ switch(data->bytes_total) {
+ case 2:
+ if(data->this_cp < 0x0080) data->this_cp = UNICODE_INVALID;
+ break;
+ case 3:
+ if(data->this_cp < 0x0800) data->this_cp = UNICODE_INVALID;
+ break;
+ case 4:
+ if(data->this_cp < 0x10000) data->this_cp = UNICODE_INVALID;
+ break;
+ case 5:
+ if(data->this_cp < 0x200000) data->this_cp = UNICODE_INVALID;
+ break;
+ case 6:
+ if(data->this_cp < 0x4000000) data->this_cp = UNICODE_INVALID;
+ break;
+ }
+ // Now look for plain invalid ones
+ if((data->this_cp >= 0xD800 && data->this_cp <= 0xDFFF) ||
+ data->this_cp == 0xFFFE ||
+ data->this_cp == 0xFFFF)
+ data->this_cp = UNICODE_INVALID;
+#ifdef DEBUG_PRINT_UTF8
+ printf(" char: U+%04x\n", data->this_cp);
+#endif
+ cp[(*cpi)++] = data->this_cp;
+ }
+ }
+
+ else if(c >= 0xc0 && c < 0xe0) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x1f;
+ data->bytes_total = 2;
+ data->bytes_remaining = 1;
+ }
+
+ else if(c >= 0xe0 && c < 0xf0) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x0f;
+ data->bytes_total = 3;
+ data->bytes_remaining = 2;
+ }
+
+ else if(c >= 0xf0 && c < 0xf8) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x07;
+ data->bytes_total = 4;
+ data->bytes_remaining = 3;
+ }
+
+ else if(c >= 0xf8 && c < 0xfc) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x03;
+ data->bytes_total = 5;
+ data->bytes_remaining = 4;
+ }
+
+ else if(c >= 0xfc && c < 0xfe) {
+ if(data->bytes_remaining)
+ cp[(*cpi)++] = UNICODE_INVALID;
+
+ data->this_cp = c & 0x01;
+ data->bytes_total = 6;
+ data->bytes_remaining = 5;
+ }
+
+ else {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+ }
+}
+
+static VTermEncoding encoding_utf8 = {
+ .init = &init_utf8,
+ .decode = &decode_utf8,
+};
+
+static void decode_usascii(VTermEncoding *enc, void *data,
+ uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ int is_gr = bytes[*pos] & 0x80;
+
+ for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ unsigned char c = bytes[*pos] ^ is_gr;
+
+ if(c < 0x20 || c == 0x7f || c >= 0x80)
+ return;
+
+ cp[(*cpi)++] = c;
+ }
+}
+
+static VTermEncoding encoding_usascii = {
+ .decode = &decode_usascii,
+};
+
+struct StaticTableEncoding {
+ const VTermEncoding enc;
+ const uint32_t chars[128];
+};
+
+static void decode_table(VTermEncoding *enc, void *data,
+ uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
+ int is_gr = bytes[*pos] & 0x80;
+
+ for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ unsigned char c = bytes[*pos] ^ is_gr;
+
+ if(c < 0x20 || c == 0x7f || c >= 0x80)
+ return;
+
+ if(table->chars[c])
+ cp[(*cpi)++] = table->chars[c];
+ else
+ cp[(*cpi)++] = c;
+ }
+}
+
+#include "encoding/DECdrawing.inc"
+#include "encoding/uk.inc"
+
+static struct {
+ VTermEncodingType type;
+ char designation;
+ VTermEncoding *enc;
+}
+encodings[] = {
+ { ENC_UTF8, 'u', &encoding_utf8 },
+ { ENC_SINGLE_94, '0', (VTermEncoding*)&encoding_DECdrawing },
+ { ENC_SINGLE_94, 'A', (VTermEncoding*)&encoding_uk },
+ { ENC_SINGLE_94, 'B', &encoding_usascii },
+ { 0 },
+};
+
+/* This ought to be INTERNAL but isn't because it's used by unit testing */
+VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
+{
+ for(int i = 0; encodings[i].designation; i++)
+ if(encodings[i].type == type && encodings[i].designation == designation)
+ return encodings[i].enc;
+ return NULL;
+}
diff --git a/src/libs/3rdparty/libvterm/src/encoding/DECdrawing.inc b/src/libs/3rdparty/libvterm/src/encoding/DECdrawing.inc
new file mode 100644
index 0000000000..47093ed0a8
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/encoding/DECdrawing.inc
@@ -0,0 +1,36 @@
+static const struct StaticTableEncoding encoding_DECdrawing = {
+ { .decode = &decode_table },
+ {
+ [0x60] = 0x25C6,
+ [0x61] = 0x2592,
+ [0x62] = 0x2409,
+ [0x63] = 0x240C,
+ [0x64] = 0x240D,
+ [0x65] = 0x240A,
+ [0x66] = 0x00B0,
+ [0x67] = 0x00B1,
+ [0x68] = 0x2424,
+ [0x69] = 0x240B,
+ [0x6a] = 0x2518,
+ [0x6b] = 0x2510,
+ [0x6c] = 0x250C,
+ [0x6d] = 0x2514,
+ [0x6e] = 0x253C,
+ [0x6f] = 0x23BA,
+ [0x70] = 0x23BB,
+ [0x71] = 0x2500,
+ [0x72] = 0x23BC,
+ [0x73] = 0x23BD,
+ [0x74] = 0x251C,
+ [0x75] = 0x2524,
+ [0x76] = 0x2534,
+ [0x77] = 0x252C,
+ [0x78] = 0x2502,
+ [0x79] = 0x2A7D,
+ [0x7a] = 0x2A7E,
+ [0x7b] = 0x03C0,
+ [0x7c] = 0x2260,
+ [0x7d] = 0x00A3,
+ [0x7e] = 0x00B7,
+ }
+};
diff --git a/src/libs/3rdparty/libvterm/src/encoding/uk.inc b/src/libs/3rdparty/libvterm/src/encoding/uk.inc
new file mode 100644
index 0000000000..da1445deca
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/encoding/uk.inc
@@ -0,0 +1,6 @@
+static const struct StaticTableEncoding encoding_uk = {
+ { .decode = &decode_table },
+ {
+ [0x23] = 0x00a3,
+ }
+};
diff --git a/src/libs/3rdparty/libvterm/src/fullwidth.inc b/src/libs/3rdparty/libvterm/src/fullwidth.inc
new file mode 100644
index 0000000000..a703529a76
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/fullwidth.inc
@@ -0,0 +1,111 @@
+ { 0x1100, 0x115f },
+ { 0x231a, 0x231b },
+ { 0x2329, 0x232a },
+ { 0x23e9, 0x23ec },
+ { 0x23f0, 0x23f0 },
+ { 0x23f3, 0x23f3 },
+ { 0x25fd, 0x25fe },
+ { 0x2614, 0x2615 },
+ { 0x2648, 0x2653 },
+ { 0x267f, 0x267f },
+ { 0x2693, 0x2693 },
+ { 0x26a1, 0x26a1 },
+ { 0x26aa, 0x26ab },
+ { 0x26bd, 0x26be },
+ { 0x26c4, 0x26c5 },
+ { 0x26ce, 0x26ce },
+ { 0x26d4, 0x26d4 },
+ { 0x26ea, 0x26ea },
+ { 0x26f2, 0x26f3 },
+ { 0x26f5, 0x26f5 },
+ { 0x26fa, 0x26fa },
+ { 0x26fd, 0x26fd },
+ { 0x2705, 0x2705 },
+ { 0x270a, 0x270b },
+ { 0x2728, 0x2728 },
+ { 0x274c, 0x274c },
+ { 0x274e, 0x274e },
+ { 0x2753, 0x2755 },
+ { 0x2757, 0x2757 },
+ { 0x2795, 0x2797 },
+ { 0x27b0, 0x27b0 },
+ { 0x27bf, 0x27bf },
+ { 0x2b1b, 0x2b1c },
+ { 0x2b50, 0x2b50 },
+ { 0x2b55, 0x2b55 },
+ { 0x2e80, 0x2e99 },
+ { 0x2e9b, 0x2ef3 },
+ { 0x2f00, 0x2fd5 },
+ { 0x2ff0, 0x2ffb },
+ { 0x3000, 0x303e },
+ { 0x3041, 0x3096 },
+ { 0x3099, 0x30ff },
+ { 0x3105, 0x312f },
+ { 0x3131, 0x318e },
+ { 0x3190, 0x31ba },
+ { 0x31c0, 0x31e3 },
+ { 0x31f0, 0x321e },
+ { 0x3220, 0x3247 },
+ { 0x3250, 0x4dbf },
+ { 0x4e00, 0xa48c },
+ { 0xa490, 0xa4c6 },
+ { 0xa960, 0xa97c },
+ { 0xac00, 0xd7a3 },
+ { 0xf900, 0xfaff },
+ { 0xfe10, 0xfe19 },
+ { 0xfe30, 0xfe52 },
+ { 0xfe54, 0xfe66 },
+ { 0xfe68, 0xfe6b },
+ { 0xff01, 0xff60 },
+ { 0xffe0, 0xffe6 },
+ { 0x16fe0, 0x16fe3 },
+ { 0x17000, 0x187f7 },
+ { 0x18800, 0x18af2 },
+ { 0x1b000, 0x1b11e },
+ { 0x1b150, 0x1b152 },
+ { 0x1b164, 0x1b167 },
+ { 0x1b170, 0x1b2fb },
+ { 0x1f004, 0x1f004 },
+ { 0x1f0cf, 0x1f0cf },
+ { 0x1f18e, 0x1f18e },
+ { 0x1f191, 0x1f19a },
+ { 0x1f200, 0x1f202 },
+ { 0x1f210, 0x1f23b },
+ { 0x1f240, 0x1f248 },
+ { 0x1f250, 0x1f251 },
+ { 0x1f260, 0x1f265 },
+ { 0x1f300, 0x1f320 },
+ { 0x1f32d, 0x1f335 },
+ { 0x1f337, 0x1f37c },
+ { 0x1f37e, 0x1f393 },
+ { 0x1f3a0, 0x1f3ca },
+ { 0x1f3cf, 0x1f3d3 },
+ { 0x1f3e0, 0x1f3f0 },
+ { 0x1f3f4, 0x1f3f4 },
+ { 0x1f3f8, 0x1f43e },
+ { 0x1f440, 0x1f440 },
+ { 0x1f442, 0x1f4fc },
+ { 0x1f4ff, 0x1f53d },
+ { 0x1f54b, 0x1f54e },
+ { 0x1f550, 0x1f567 },
+ { 0x1f57a, 0x1f57a },
+ { 0x1f595, 0x1f596 },
+ { 0x1f5a4, 0x1f5a4 },
+ { 0x1f5fb, 0x1f64f },
+ { 0x1f680, 0x1f6c5 },
+ { 0x1f6cc, 0x1f6cc },
+ { 0x1f6d0, 0x1f6d2 },
+ { 0x1f6d5, 0x1f6d5 },
+ { 0x1f6eb, 0x1f6ec },
+ { 0x1f6f4, 0x1f6fa },
+ { 0x1f7e0, 0x1f7eb },
+ { 0x1f90d, 0x1f971 },
+ { 0x1f973, 0x1f976 },
+ { 0x1f97a, 0x1f9a2 },
+ { 0x1f9a5, 0x1f9aa },
+ { 0x1f9ae, 0x1f9ca },
+ { 0x1f9cd, 0x1f9ff },
+ { 0x1fa70, 0x1fa73 },
+ { 0x1fa78, 0x1fa7a },
+ { 0x1fa80, 0x1fa82 },
+ { 0x1fa90, 0x1fa95 },
diff --git a/src/libs/3rdparty/libvterm/src/keyboard.c b/src/libs/3rdparty/libvterm/src/keyboard.c
new file mode 100644
index 0000000000..d31c8be12d
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/keyboard.c
@@ -0,0 +1,226 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+
+#include "utf8.h"
+
+void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
+{
+ /* The shift modifier is never important for Unicode characters
+ * apart from Space
+ */
+ if(c != ' ')
+ mod &= ~VTERM_MOD_SHIFT;
+
+ if(mod == 0) {
+ // Normal text - ignore just shift
+ char str[6];
+ int seqlen = fill_utf8(c, str);
+ vterm_push_output_bytes(vt, str, seqlen);
+ return;
+ }
+
+ int needs_CSIu;
+ switch(c) {
+ /* Special Ctrl- letters that can't be represented elsewise */
+ case 'i': case 'j': case 'm': case '[':
+ needs_CSIu = 1;
+ break;
+ /* Ctrl-\ ] ^ _ don't need CSUu */
+ case '\\': case ']': case '^': case '_':
+ needs_CSIu = 0;
+ break;
+ /* Shift-space needs CSIu */
+ case ' ':
+ needs_CSIu = !!(mod & VTERM_MOD_SHIFT);
+ break;
+ /* All other characters needs CSIu except for letters a-z */
+ default:
+ needs_CSIu = (c < 'a' || c > 'z');
+ }
+
+ /* ALT we can just prefix with ESC; anything else requires CSI u */
+ if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod+1);
+ return;
+ }
+
+ if(mod & VTERM_MOD_CTRL)
+ c &= 0x1f;
+
+ vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c);
+}
+
+typedef struct {
+ enum {
+ KEYCODE_NONE,
+ KEYCODE_LITERAL,
+ KEYCODE_TAB,
+ KEYCODE_ENTER,
+ KEYCODE_SS3,
+ KEYCODE_CSI,
+ KEYCODE_CSI_CURSOR,
+ KEYCODE_CSINUM,
+ KEYCODE_KEYPAD,
+ } type;
+ char literal;
+ int csinum;
+} keycodes_s;
+
+static keycodes_s keycodes[] = {
+ { KEYCODE_NONE }, // NONE
+
+ { KEYCODE_ENTER, '\r' }, // ENTER
+ { KEYCODE_TAB, '\t' }, // TAB
+ { KEYCODE_LITERAL, '\x7f' }, // BACKSPACE == ASCII DEL
+ { KEYCODE_LITERAL, '\x1b' }, // ESCAPE
+
+ { KEYCODE_CSI_CURSOR, 'A' }, // UP
+ { KEYCODE_CSI_CURSOR, 'B' }, // DOWN
+ { KEYCODE_CSI_CURSOR, 'D' }, // LEFT
+ { KEYCODE_CSI_CURSOR, 'C' }, // RIGHT
+
+ { KEYCODE_CSINUM, '~', 2 }, // INS
+ { KEYCODE_CSINUM, '~', 3 }, // DEL
+ { KEYCODE_CSI_CURSOR, 'H' }, // HOME
+ { KEYCODE_CSI_CURSOR, 'F' }, // END
+ { KEYCODE_CSINUM, '~', 5 }, // PAGEUP
+ { KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN
+};
+
+static keycodes_s keycodes_fn[] = {
+ { KEYCODE_NONE }, // F0 - shouldn't happen
+ { KEYCODE_SS3, 'P' }, // F1
+ { KEYCODE_SS3, 'Q' }, // F2
+ { KEYCODE_SS3, 'R' }, // F3
+ { KEYCODE_SS3, 'S' }, // F4
+ { KEYCODE_CSINUM, '~', 15 }, // F5
+ { KEYCODE_CSINUM, '~', 17 }, // F6
+ { KEYCODE_CSINUM, '~', 18 }, // F7
+ { KEYCODE_CSINUM, '~', 19 }, // F8
+ { KEYCODE_CSINUM, '~', 20 }, // F9
+ { KEYCODE_CSINUM, '~', 21 }, // F10
+ { KEYCODE_CSINUM, '~', 23 }, // F11
+ { KEYCODE_CSINUM, '~', 24 }, // F12
+};
+
+static keycodes_s keycodes_kp[] = {
+ { KEYCODE_KEYPAD, '0', 'p' }, // KP_0
+ { KEYCODE_KEYPAD, '1', 'q' }, // KP_1
+ { KEYCODE_KEYPAD, '2', 'r' }, // KP_2
+ { KEYCODE_KEYPAD, '3', 's' }, // KP_3
+ { KEYCODE_KEYPAD, '4', 't' }, // KP_4
+ { KEYCODE_KEYPAD, '5', 'u' }, // KP_5
+ { KEYCODE_KEYPAD, '6', 'v' }, // KP_6
+ { KEYCODE_KEYPAD, '7', 'w' }, // KP_7
+ { KEYCODE_KEYPAD, '8', 'x' }, // KP_8
+ { KEYCODE_KEYPAD, '9', 'y' }, // KP_9
+ { KEYCODE_KEYPAD, '*', 'j' }, // KP_MULT
+ { KEYCODE_KEYPAD, '+', 'k' }, // KP_PLUS
+ { KEYCODE_KEYPAD, ',', 'l' }, // KP_COMMA
+ { KEYCODE_KEYPAD, '-', 'm' }, // KP_MINUS
+ { KEYCODE_KEYPAD, '.', 'n' }, // KP_PERIOD
+ { KEYCODE_KEYPAD, '/', 'o' }, // KP_DIVIDE
+ { KEYCODE_KEYPAD, '\n', 'M' }, // KP_ENTER
+ { KEYCODE_KEYPAD, '=', 'X' }, // KP_EQUAL
+};
+
+void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
+{
+ if(key == VTERM_KEY_NONE)
+ return;
+
+ keycodes_s k;
+ if(key < VTERM_KEY_FUNCTION_0) {
+ if(key >= sizeof(keycodes)/sizeof(keycodes[0]))
+ return;
+ k = keycodes[key];
+ }
+ else if(key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
+ if((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0]))
+ return;
+ k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
+ }
+ else if(key >= VTERM_KEY_KP_0) {
+ if((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0]))
+ return;
+ k = keycodes_kp[key - VTERM_KEY_KP_0];
+ }
+
+ switch(k.type) {
+ case KEYCODE_NONE:
+ break;
+
+ case KEYCODE_TAB:
+ /* Shift-Tab is CSI Z but plain Tab is 0x09 */
+ if(mod == VTERM_MOD_SHIFT)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
+ else if(mod & VTERM_MOD_SHIFT)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod+1);
+ else
+ goto case_LITERAL;
+ break;
+
+ case KEYCODE_ENTER:
+ /* Enter is CRLF in newline mode, but just LF in linefeed */
+ if(vt->state->mode.newline)
+ vterm_push_output_sprintf(vt, "\r\n");
+ else
+ goto case_LITERAL;
+ break;
+
+ case KEYCODE_LITERAL: case_LITERAL:
+ if(mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL))
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod+1);
+ else
+ vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal);
+ break;
+
+ case KEYCODE_SS3: case_SS3:
+ if(mod == 0)
+ vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
+ else
+ goto case_CSI;
+ break;
+
+ case KEYCODE_CSI: case_CSI:
+ if(mod == 0)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal);
+ else
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal);
+ break;
+
+ case KEYCODE_CSINUM:
+ if(mod == 0)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
+ else
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
+ break;
+
+ case KEYCODE_CSI_CURSOR:
+ if(vt->state->mode.cursor)
+ goto case_SS3;
+ else
+ goto case_CSI;
+
+ case KEYCODE_KEYPAD:
+ if(vt->state->mode.keypad) {
+ k.literal = k.csinum;
+ goto case_SS3;
+ }
+ else
+ goto case_LITERAL;
+ }
+}
+
+void vterm_keyboard_start_paste(VTerm *vt)
+{
+ if(vt->state->mode.bracketpaste)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "200~");
+}
+
+void vterm_keyboard_end_paste(VTerm *vt)
+{
+ if(vt->state->mode.bracketpaste)
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "201~");
+}
diff --git a/src/libs/3rdparty/libvterm/src/mouse.c b/src/libs/3rdparty/libvterm/src/mouse.c
new file mode 100644
index 0000000000..f74abc3d9d
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/mouse.c
@@ -0,0 +1,99 @@
+#include "vterm_internal.h"
+
+#include "utf8.h"
+
+static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row)
+{
+ modifiers <<= 2;
+
+ switch(state->mouse_protocol) {
+ case MOUSE_X10:
+ if(col + 0x21 > 0xff)
+ col = 0xff - 0x21;
+ if(row + 0x21 > 0xff)
+ row = 0xff - 0x21;
+
+ if(!pressed)
+ code = 3;
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c",
+ (code | modifiers) + 0x20, col + 0x21, row + 0x21);
+ break;
+
+ case MOUSE_UTF8:
+ {
+ char utf8[18]; size_t len = 0;
+
+ if(!pressed)
+ code = 3;
+
+ len += fill_utf8((code | modifiers) + 0x20, utf8 + len);
+ len += fill_utf8(col + 0x21, utf8 + len);
+ len += fill_utf8(row + 0x21, utf8 + len);
+ utf8[len] = 0;
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8);
+ }
+ break;
+
+ case MOUSE_SGR:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c",
+ code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm');
+ break;
+
+ case MOUSE_RXVT:
+ if(!pressed)
+ code = 3;
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM",
+ code | modifiers, col + 1, row + 1);
+ break;
+ }
+}
+
+void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod)
+{
+ VTermState *state = vt->state;
+
+ if(col == state->mouse_col && row == state->mouse_row)
+ return;
+
+ state->mouse_col = col;
+ state->mouse_row = row;
+
+ if((state->mouse_flags & MOUSE_WANT_DRAG && state->mouse_buttons) ||
+ (state->mouse_flags & MOUSE_WANT_MOVE)) {
+ int button = state->mouse_buttons & 0x01 ? 1 :
+ state->mouse_buttons & 0x02 ? 2 :
+ state->mouse_buttons & 0x04 ? 3 : 4;
+ output_mouse(state, button-1 + 0x20, 1, mod, col, row);
+ }
+}
+
+void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod)
+{
+ VTermState *state = vt->state;
+
+ int old_buttons = state->mouse_buttons;
+
+ if(button > 0 && button <= 3) {
+ if(pressed)
+ state->mouse_buttons |= (1 << (button-1));
+ else
+ state->mouse_buttons &= ~(1 << (button-1));
+ }
+
+ /* Most of the time we don't get button releases from 4/5 */
+ if(state->mouse_buttons == old_buttons && button < 4)
+ return;
+
+ if(!state->mouse_flags)
+ return;
+
+ if(button < 4) {
+ output_mouse(state, button-1, pressed, mod, state->mouse_col, state->mouse_row);
+ }
+ else if(button < 8) {
+ output_mouse(state, button-4 + 0x40, pressed, mod, state->mouse_col, state->mouse_row);
+ }
+}
diff --git a/src/libs/3rdparty/libvterm/src/parser.c b/src/libs/3rdparty/libvterm/src/parser.c
new file mode 100644
index 0000000000..b43a549cef
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/parser.c
@@ -0,0 +1,402 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#undef DEBUG_PARSER
+
+static bool is_intermed(unsigned char c)
+{
+ return c >= 0x20 && c <= 0x2f;
+}
+
+static void do_control(VTerm *vt, unsigned char control)
+{
+ if(vt->parser.callbacks && vt->parser.callbacks->control)
+ if((*vt->parser.callbacks->control)(control, vt->parser.cbdata))
+ return;
+
+ DEBUG_LOG("libvterm: Unhandled control 0x%02x\n", control);
+}
+
+static void do_csi(VTerm *vt, char command)
+{
+#ifdef DEBUG_PARSER
+ printf("Parsed CSI args as:\n", arglen, args);
+ printf(" leader: %s\n", vt->parser.v.csi.leader);
+ for(int argi = 0; argi < vt->parser.v.csi.argi; argi++) {
+ printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi]));
+ if(!CSI_ARG_HAS_MORE(vt->parser.v.csi.args[argi]))
+ printf("\n");
+ printf(" intermed: %s\n", vt->parser.intermed);
+ }
+#endif
+
+ if(vt->parser.callbacks && vt->parser.callbacks->csi)
+ if((*vt->parser.callbacks->csi)(
+ vt->parser.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL,
+ vt->parser.v.csi.args,
+ vt->parser.v.csi.argi,
+ vt->parser.intermedlen ? vt->parser.intermed : NULL,
+ command,
+ vt->parser.cbdata))
+ return;
+
+ DEBUG_LOG("libvterm: Unhandled CSI %c\n", command);
+}
+
+static void do_escape(VTerm *vt, char command)
+{
+ char seq[INTERMED_MAX+1];
+
+ size_t len = vt->parser.intermedlen;
+ strncpy(seq, vt->parser.intermed, len);
+ seq[len++] = command;
+ seq[len] = 0;
+
+ if(vt->parser.callbacks && vt->parser.callbacks->escape)
+ if((*vt->parser.callbacks->escape)(seq, len, vt->parser.cbdata))
+ return;
+
+ DEBUG_LOG("libvterm: Unhandled escape ESC 0x%02x\n", command);
+}
+
+static void string_fragment(VTerm *vt, const char *str, size_t len, bool final)
+{
+ VTermStringFragment frag = {
+ .str = str,
+ .len = len,
+ .initial = vt->parser.string_initial,
+ .final = final,
+ };
+
+ switch(vt->parser.state) {
+ case OSC:
+ if(vt->parser.callbacks && vt->parser.callbacks->osc)
+ (*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata);
+ break;
+
+ case DCS:
+ if(vt->parser.callbacks && vt->parser.callbacks->dcs)
+ (*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, vt->parser.v.dcs.commandlen, frag, vt->parser.cbdata);
+ break;
+
+ case APC:
+ if(vt->parser.callbacks && vt->parser.callbacks->apc)
+ (*vt->parser.callbacks->apc)(frag, vt->parser.cbdata);
+ break;
+
+ case PM:
+ if(vt->parser.callbacks && vt->parser.callbacks->pm)
+ (*vt->parser.callbacks->pm)(frag, vt->parser.cbdata);
+ break;
+
+ case SOS:
+ if(vt->parser.callbacks && vt->parser.callbacks->sos)
+ (*vt->parser.callbacks->sos)(frag, vt->parser.cbdata);
+ break;
+
+ case NORMAL:
+ case CSI_LEADER:
+ case CSI_ARGS:
+ case CSI_INTERMED:
+ case OSC_COMMAND:
+ case DCS_COMMAND:
+ break;
+ }
+
+ vt->parser.string_initial = false;
+}
+
+size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
+{
+ size_t pos = 0;
+ const char *string_start;
+
+ switch(vt->parser.state) {
+ case NORMAL:
+ case CSI_LEADER:
+ case CSI_ARGS:
+ case CSI_INTERMED:
+ case OSC_COMMAND:
+ case DCS_COMMAND:
+ string_start = NULL;
+ break;
+ case OSC:
+ case DCS:
+ case APC:
+ case PM:
+ case SOS:
+ string_start = bytes;
+ break;
+ }
+
+#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0)
+#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
+
+#define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND)
+
+ for( ; pos < len; pos++) {
+ unsigned char c = bytes[pos];
+ bool c1_allowed = !vt->mode.utf8;
+
+ if(c == 0x00 || c == 0x7f) { // NUL, DEL
+ if(IS_STRING_STATE()) {
+ string_fragment(vt, string_start, bytes + pos - string_start, false);
+ string_start = bytes + pos + 1;
+ }
+ if(vt->parser.emit_nul)
+ do_control(vt, c);
+ continue;
+ }
+ if(c == 0x18 || c == 0x1a) { // CAN, SUB
+ vt->parser.in_esc = false;
+ ENTER_NORMAL_STATE();
+ if(vt->parser.emit_nul)
+ do_control(vt, c);
+ continue;
+ }
+ else if(c == 0x1b) { // ESC
+ vt->parser.intermedlen = 0;
+ if(!IS_STRING_STATE())
+ vt->parser.state = NORMAL;
+ vt->parser.in_esc = true;
+ continue;
+ }
+ else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state
+ IS_STRING_STATE()) {
+ // fallthrough
+ }
+ else if(c < 0x20) { // other C0
+ if(vt->parser.state == SOS)
+ continue; // All other C0s permitted in SOS
+
+ if(IS_STRING_STATE())
+ string_fragment(vt, string_start, bytes + pos - string_start, false);
+ do_control(vt, c);
+ if(IS_STRING_STATE())
+ string_start = bytes + pos + 1;
+ continue;
+ }
+ // else fallthrough
+
+ size_t string_len = bytes + pos - string_start;
+
+ if(vt->parser.in_esc) {
+ // Hoist an ESC letter into a C1 if we're not in a string mode
+ // Always accept ESC \ == ST even in string mode
+ if(!vt->parser.intermedlen &&
+ c >= 0x40 && c < 0x60 &&
+ ((!IS_STRING_STATE() || c == 0x5c))) {
+ c += 0x40;
+ c1_allowed = true;
+ if(string_len)
+ string_len -= 1;
+ vt->parser.in_esc = false;
+ }
+ else {
+ string_start = NULL;
+ vt->parser.state = NORMAL;
+ }
+ }
+
+ switch(vt->parser.state) {
+ case CSI_LEADER:
+ /* Extract leader bytes 0x3c to 0x3f */
+ if(c >= 0x3c && c <= 0x3f) {
+ if(vt->parser.v.csi.leaderlen < CSI_LEADER_MAX-1)
+ vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = c;
+ break;
+ }
+
+ /* else fallthrough */
+ vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
+
+ vt->parser.v.csi.argi = 0;
+ vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
+ vt->parser.state = CSI_ARGS;
+
+ /* fallthrough */
+ case CSI_ARGS:
+ /* Numerical value of argument */
+ if(c >= '0' && c <= '9') {
+ if(vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING)
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
+ break;
+ }
+ if(c == ':') {
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
+ c = ';';
+ }
+ if(c == ';') {
+ vt->parser.v.csi.argi++;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
+ break;
+ }
+
+ /* else fallthrough */
+ vt->parser.v.csi.argi++;
+ vt->parser.intermedlen = 0;
+ vt->parser.state = CSI_INTERMED;
+ case CSI_INTERMED:
+ if(is_intermed(c)) {
+ if(vt->parser.intermedlen < INTERMED_MAX-1)
+ vt->parser.intermed[vt->parser.intermedlen++] = c;
+ break;
+ }
+ else if(c == 0x1b) {
+ /* ESC in CSI cancels */
+ }
+ else if(c >= 0x40 && c <= 0x7e) {
+ vt->parser.intermed[vt->parser.intermedlen] = 0;
+ do_csi(vt, c);
+ }
+ /* else was invalid CSI */
+
+ ENTER_NORMAL_STATE();
+ break;
+
+ case OSC_COMMAND:
+ /* Numerical value of command */
+ if(c >= '0' && c <= '9') {
+ if(vt->parser.v.osc.command == -1)
+ vt->parser.v.osc.command = 0;
+ else
+ vt->parser.v.osc.command *= 10;
+ vt->parser.v.osc.command += c - '0';
+ break;
+ }
+ if(c == ';') {
+ vt->parser.state = OSC;
+ string_start = bytes + pos + 1;
+ break;
+ }
+
+ /* else fallthrough */
+ string_start = bytes + pos;
+ string_len = 0;
+ vt->parser.state = OSC;
+ goto string_state;
+
+ case DCS_COMMAND:
+ if(vt->parser.v.dcs.commandlen < CSI_LEADER_MAX)
+ vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = c;
+
+ if(c >= 0x40 && c<= 0x7e) {
+ string_start = bytes + pos + 1;
+ vt->parser.state = DCS;
+ }
+ break;
+
+string_state:
+ case OSC:
+ case DCS:
+ case APC:
+ case PM:
+ case SOS:
+ if(c == 0x07 || (c1_allowed && c == 0x9c)) {
+ string_fragment(vt, string_start, string_len, true);
+ ENTER_NORMAL_STATE();
+ }
+ break;
+
+ case NORMAL:
+ if(vt->parser.in_esc) {
+ if(is_intermed(c)) {
+ if(vt->parser.intermedlen < INTERMED_MAX-1)
+ vt->parser.intermed[vt->parser.intermedlen++] = c;
+ }
+ else if(c >= 0x30 && c < 0x7f) {
+ do_escape(vt, c);
+ vt->parser.in_esc = 0;
+ ENTER_NORMAL_STATE();
+ }
+ else {
+ DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c);
+ }
+ break;
+ }
+ if(c1_allowed && c >= 0x80 && c < 0xa0) {
+ switch(c) {
+ case 0x90: // DCS
+ vt->parser.string_initial = true;
+ vt->parser.v.dcs.commandlen = 0;
+ ENTER_STATE(DCS_COMMAND);
+ break;
+ case 0x98: // SOS
+ vt->parser.string_initial = true;
+ ENTER_STATE(SOS);
+ string_start = bytes + pos + 1;
+ string_len = 0;
+ break;
+ case 0x9b: // CSI
+ vt->parser.v.csi.leaderlen = 0;
+ ENTER_STATE(CSI_LEADER);
+ break;
+ case 0x9d: // OSC
+ vt->parser.v.osc.command = -1;
+ vt->parser.string_initial = true;
+ string_start = bytes + pos + 1;
+ ENTER_STATE(OSC_COMMAND);
+ break;
+ case 0x9e: // PM
+ vt->parser.string_initial = true;
+ ENTER_STATE(PM);
+ string_start = bytes + pos + 1;
+ string_len = 0;
+ break;
+ case 0x9f: // APC
+ vt->parser.string_initial = true;
+ ENTER_STATE(APC);
+ string_start = bytes + pos + 1;
+ string_len = 0;
+ break;
+ default:
+ do_control(vt, c);
+ break;
+ }
+ }
+ else {
+ size_t eaten = 0;
+ if(vt->parser.callbacks && vt->parser.callbacks->text)
+ eaten = (*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
+
+ if(!eaten) {
+ DEBUG_LOG("libvterm: Text callback did not consume any input\n");
+ /* force it to make progress */
+ eaten = 1;
+ }
+
+ pos += (eaten - 1); // we'll ++ it again in a moment
+ }
+ break;
+ }
+ }
+
+ if(string_start) {
+ size_t string_len = bytes + pos - string_start;
+ if(vt->parser.in_esc)
+ string_len -= 1;
+ string_fragment(vt, string_start, string_len, false);
+ }
+
+ return len;
+}
+
+void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
+{
+ vt->parser.callbacks = callbacks;
+ vt->parser.cbdata = user;
+}
+
+void *vterm_parser_get_cbdata(VTerm *vt)
+{
+ return vt->parser.cbdata;
+}
+
+void vterm_parser_set_emit_nul(VTerm *vt, bool emit)
+{
+ vt->parser.emit_nul = emit;
+}
diff --git a/src/libs/3rdparty/libvterm/src/pen.c b/src/libs/3rdparty/libvterm/src/pen.c
new file mode 100644
index 0000000000..891a45cec7
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/pen.c
@@ -0,0 +1,613 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+
+/**
+ * Structure used to store RGB triples without the additional metadata stored in
+ * VTermColor.
+ */
+typedef struct {
+ uint8_t red, green, blue;
+} VTermRGB;
+
+static const VTermRGB ansi_colors[] = {
+ /* R G B */
+ { 0, 0, 0 }, // black
+ { 224, 0, 0 }, // red
+ { 0, 224, 0 }, // green
+ { 224, 224, 0 }, // yellow
+ { 0, 0, 224 }, // blue
+ { 224, 0, 224 }, // magenta
+ { 0, 224, 224 }, // cyan
+ { 224, 224, 224 }, // white == light grey
+
+ // high intensity
+ { 128, 128, 128 }, // black
+ { 255, 64, 64 }, // red
+ { 64, 255, 64 }, // green
+ { 255, 255, 64 }, // yellow
+ { 64, 64, 255 }, // blue
+ { 255, 64, 255 }, // magenta
+ { 64, 255, 255 }, // cyan
+ { 255, 255, 255 }, // white for real
+};
+
+static int ramp6[] = {
+ 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF,
+};
+
+static int ramp24[] = {
+ 0x00, 0x0B, 0x16, 0x21, 0x2C, 0x37, 0x42, 0x4D, 0x58, 0x63, 0x6E, 0x79,
+ 0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF,
+};
+
+static void lookup_default_colour_ansi(long idx, VTermColor *col)
+{
+ if (idx >= 0 && idx < 16) {
+ vterm_color_rgb(
+ col,
+ ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
+ }
+}
+
+static bool lookup_colour_ansi(const VTermState *state, long index, VTermColor *col)
+{
+ if(index >= 0 && index < 16) {
+ *col = state->colors[index];
+ return true;
+ }
+
+ return false;
+}
+
+static bool lookup_colour_palette(const VTermState *state, long index, VTermColor *col)
+{
+ if(index >= 0 && index < 16) {
+ // Normal 8 colours or high intensity - parse as palette 0
+ return lookup_colour_ansi(state, index, col);
+ }
+ else if(index >= 16 && index < 232) {
+ // 216-colour cube
+ index -= 16;
+
+ vterm_color_rgb(col, ramp6[index/6/6 % 6],
+ ramp6[index/6 % 6],
+ ramp6[index % 6]);
+
+ return true;
+ }
+ else if(index >= 232 && index < 256) {
+ // 24 greyscales
+ index -= 232;
+
+ vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]);
+
+ return true;
+ }
+
+ return false;
+}
+
+static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col)
+{
+ switch(palette) {
+ case 2: // RGB mode - 3 args contain colour values directly
+ if(argcount < 3)
+ return argcount;
+
+ vterm_color_rgb(col, CSI_ARG(args[0]), CSI_ARG(args[1]), CSI_ARG(args[2]));
+
+ return 3;
+
+ case 5: // XTerm 256-colour mode
+ if (!argcount || CSI_ARG_IS_MISSING(args[0])) {
+ return argcount ? 1 : 0;
+ }
+
+ vterm_color_indexed(col, args[0]);
+
+ return argcount ? 1 : 0;
+
+ default:
+ DEBUG_LOG("Unrecognised colour palette %d\n", palette);
+ return 0;
+ }
+}
+
+// Some conveniences
+
+static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
+{
+#ifdef DEBUG
+ if(type != vterm_get_attr_type(attr)) {
+ DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
+ attr, vterm_get_attr_type(attr), type);
+ return;
+ }
+#endif
+ if(state->callbacks && state->callbacks->setpenattr)
+ (*state->callbacks->setpenattr)(attr, val, state->cbdata);
+}
+
+static void setpenattr_bool(VTermState *state, VTermAttr attr, int boolean)
+{
+ VTermValue val = { .boolean = boolean };
+ setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
+}
+
+static void setpenattr_int(VTermState *state, VTermAttr attr, int number)
+{
+ VTermValue val = { .number = number };
+ setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
+}
+
+static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color)
+{
+ VTermValue val = { .color = color };
+ setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
+}
+
+static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
+{
+ VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
+
+ vterm_color_indexed(colp, col);
+
+ setpenattr_col(state, attr, *colp);
+}
+
+INTERNAL void vterm_state_newpen(VTermState *state)
+{
+ // 90% grey so that pure white is brighter
+ vterm_color_rgb(&state->default_fg, 240, 240, 240);
+ vterm_color_rgb(&state->default_bg, 0, 0, 0);
+ vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg);
+
+ for(int col = 0; col < 16; col++)
+ lookup_default_colour_ansi(col, &state->colors[col]);
+}
+
+INTERNAL void vterm_state_resetpen(VTermState *state)
+{
+ state->pen.bold = 0; setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
+ state->pen.underline = 0; setpenattr_int (state, VTERM_ATTR_UNDERLINE, 0);
+ state->pen.italic = 0; setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
+ state->pen.blink = 0; setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
+ state->pen.reverse = 0; setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
+ state->pen.conceal = 0; setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
+ state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
+ state->pen.font = 0; setpenattr_int (state, VTERM_ATTR_FONT, 0);
+ state->pen.small = 0; setpenattr_bool(state, VTERM_ATTR_SMALL, 0);
+ state->pen.baseline = 0; setpenattr_int (state, VTERM_ATTR_BASELINE, 0);
+
+ state->pen.fg = state->default_fg; setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
+ state->pen.bg = state->default_bg; setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
+}
+
+INTERNAL void vterm_state_savepen(VTermState *state, int save)
+{
+ if(save) {
+ state->saved.pen = state->pen;
+ }
+ else {
+ state->pen = state->saved.pen;
+
+ setpenattr_bool(state, VTERM_ATTR_BOLD, state->pen.bold);
+ setpenattr_int (state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, state->pen.italic);
+ setpenattr_bool(state, VTERM_ATTR_BLINK, state->pen.blink);
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, state->pen.reverse);
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, state->pen.conceal);
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, state->pen.strike);
+ setpenattr_int (state, VTERM_ATTR_FONT, state->pen.font);
+ setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
+ setpenattr_int (state, VTERM_ATTR_BASELINE, state->pen.baseline);
+
+ setpenattr_col( state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ setpenattr_col( state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+ }
+}
+
+int vterm_color_is_equal(const VTermColor *a, const VTermColor *b)
+{
+ /* First make sure that the two colours are of the same type (RGB/Indexed) */
+ if (a->type != b->type) {
+ return false;
+ }
+
+ /* Depending on the type inspect the corresponding members */
+ if (VTERM_COLOR_IS_INDEXED(a)) {
+ return a->indexed.idx == b->indexed.idx;
+ }
+ else if (VTERM_COLOR_IS_RGB(a)) {
+ return (a->rgb.red == b->rgb.red)
+ && (a->rgb.green == b->rgb.green)
+ && (a->rgb.blue == b->rgb.blue);
+ }
+
+ return 0;
+}
+
+void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg)
+{
+ *default_fg = state->default_fg;
+ *default_bg = state->default_bg;
+}
+
+void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col)
+{
+ lookup_colour_palette(state, index, col);
+}
+
+void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg)
+{
+ if(default_fg) {
+ state->default_fg = *default_fg;
+ state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_FG;
+ }
+
+ if(default_bg) {
+ state->default_bg = *default_bg;
+ state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_BG;
+ }
+}
+
+void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
+{
+ if(index >= 0 && index < 16)
+ state->colors[index] = *col;
+}
+
+void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col)
+{
+ if (VTERM_COLOR_IS_INDEXED(col)) { /* Convert indexed colors to RGB */
+ lookup_colour_palette(state, col->indexed.idx, col);
+ }
+ col->type &= VTERM_COLOR_TYPE_MASK; /* Reset any metadata but the type */
+}
+
+void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
+{
+ state->bold_is_highbright = bold_is_highbright;
+}
+
+INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argcount)
+{
+ // SGR - ECMA-48 8.3.117
+
+ int argi = 0;
+ int value;
+
+ while(argi < argcount) {
+ // This logic is easier to do 'done' backwards; set it true, and make it
+ // false again in the 'default' case
+ int done = 1;
+
+ long arg;
+ switch(arg = CSI_ARG(args[argi])) {
+ case CSI_ARG_MISSING:
+ case 0: // Reset
+ vterm_state_resetpen(state);
+ break;
+
+ case 1: { // Bold on
+ const VTermColor *fg = &state->pen.fg;
+ state->pen.bold = 1;
+ setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
+ if(!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8 && state->bold_is_highbright)
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (state->pen.bold ? 8 : 0));
+ break;
+ }
+
+ case 3: // Italic on
+ state->pen.italic = 1;
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
+ break;
+
+ case 4: // Underline
+ state->pen.underline = VTERM_UNDERLINE_SINGLE;
+ if(CSI_ARG_HAS_MORE(args[argi])) {
+ argi++;
+ switch(CSI_ARG(args[argi])) {
+ case 0:
+ state->pen.underline = 0;
+ break;
+ case 1:
+ state->pen.underline = VTERM_UNDERLINE_SINGLE;
+ break;
+ case 2:
+ state->pen.underline = VTERM_UNDERLINE_DOUBLE;
+ break;
+ case 3:
+ state->pen.underline = VTERM_UNDERLINE_CURLY;
+ break;
+ case 4:
+ state->pen.underline = VTERM_UNDERLINE_DOTTED;
+ break;
+ case 5:
+ state->pen.underline = VTERM_UNDERLINE_DASHED;
+ break;
+ }
+ }
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ break;
+
+ case 5: // Blink
+ state->pen.blink = 1;
+ setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
+ break;
+
+ case 7: // Reverse on
+ state->pen.reverse = 1;
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
+ break;
+
+ case 8: // Conceal on
+ state->pen.conceal = 1;
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, 1);
+ break;
+
+ case 9: // Strikethrough on
+ state->pen.strike = 1;
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
+ break;
+
+ case 10: case 11: case 12: case 13: case 14:
+ case 15: case 16: case 17: case 18: case 19: // Select font
+ state->pen.font = CSI_ARG(args[argi]) - 10;
+ setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
+ break;
+
+ case 21: // Underline double
+ state->pen.underline = VTERM_UNDERLINE_DOUBLE;
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ break;
+
+ case 22: // Bold off
+ state->pen.bold = 0;
+ setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
+ break;
+
+ case 23: // Italic and Gothic (currently unsupported) off
+ state->pen.italic = 0;
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
+ break;
+
+ case 24: // Underline off
+ state->pen.underline = 0;
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
+ break;
+
+ case 25: // Blink off
+ state->pen.blink = 0;
+ setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
+ break;
+
+ case 27: // Reverse off
+ state->pen.reverse = 0;
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
+ break;
+
+ case 28: // Conceal off (Reveal)
+ state->pen.conceal = 0;
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
+ break;
+
+ case 29: // Strikethrough off
+ state->pen.strike = 0;
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
+ break;
+
+ case 30: case 31: case 32: case 33:
+ case 34: case 35: case 36: case 37: // Foreground colour palette
+ value = CSI_ARG(args[argi]) - 30;
+ if(state->pen.bold && state->bold_is_highbright)
+ value += 8;
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
+ break;
+
+ case 38: // Foreground colour alternative palette
+ if(argcount - argi < 1)
+ return;
+ argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg);
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ break;
+
+ case 39: // Foreground colour default
+ state->pen.fg = state->default_fg;
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ break;
+
+ case 40: case 41: case 42: case 43:
+ case 44: case 45: case 46: case 47: // Background colour palette
+ value = CSI_ARG(args[argi]) - 40;
+ set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
+ break;
+
+ case 48: // Background colour alternative palette
+ if(argcount - argi < 1)
+ return;
+ argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg);
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+ break;
+
+ case 49: // Default background
+ state->pen.bg = state->default_bg;
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+ break;
+
+ case 73: // Superscript
+ case 74: // Subscript
+ case 75: // Superscript/subscript off
+ state->pen.small = (arg != 75);
+ state->pen.baseline =
+ (arg == 73) ? VTERM_BASELINE_RAISE :
+ (arg == 74) ? VTERM_BASELINE_LOWER :
+ VTERM_BASELINE_NORMAL;
+ setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
+ setpenattr_int (state, VTERM_ATTR_BASELINE, state->pen.baseline);
+ break;
+
+ case 90: case 91: case 92: case 93:
+ case 94: case 95: case 96: case 97: // Foreground colour high-intensity palette
+ value = CSI_ARG(args[argi]) - 90 + 8;
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
+ break;
+
+ case 100: case 101: case 102: case 103:
+ case 104: case 105: case 106: case 107: // Background colour high-intensity palette
+ value = CSI_ARG(args[argi]) - 100 + 8;
+ set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
+ break;
+
+ default:
+ done = 0;
+ break;
+ }
+
+ if(!done)
+ DEBUG_LOG("libvterm: Unhandled CSI SGR %ld\n", arg);
+
+ while(CSI_ARG_HAS_MORE(args[argi++]));
+ }
+}
+
+static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
+{
+ /* Do nothing if the given color is the default color */
+ if (( fg && VTERM_COLOR_IS_DEFAULT_FG(col)) ||
+ (!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
+ return argi;
+ }
+
+ /* Decide whether to send an indexed color or an RGB color */
+ if (VTERM_COLOR_IS_INDEXED(col)) {
+ const uint8_t idx = col->indexed.idx;
+ if (idx < 8) {
+ args[argi++] = (idx + (fg ? 30 : 40));
+ }
+ else if (idx < 16) {
+ args[argi++] = (idx - 8 + (fg ? 90 : 100));
+ }
+ else {
+ args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
+ args[argi++] = CSI_ARG_FLAG_MORE | 5;
+ args[argi++] = idx;
+ }
+ }
+ else if (VTERM_COLOR_IS_RGB(col)) {
+ args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
+ args[argi++] = CSI_ARG_FLAG_MORE | 2;
+ args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red;
+ args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green;
+ args[argi++] = col->rgb.blue;
+ }
+ return argi;
+}
+
+INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount)
+{
+ int argi = 0;
+
+ if(state->pen.bold)
+ args[argi++] = 1;
+
+ if(state->pen.italic)
+ args[argi++] = 3;
+
+ if(state->pen.underline == VTERM_UNDERLINE_SINGLE)
+ args[argi++] = 4;
+ if(state->pen.underline == VTERM_UNDERLINE_CURLY)
+ args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3;
+
+ if(state->pen.blink)
+ args[argi++] = 5;
+
+ if(state->pen.reverse)
+ args[argi++] = 7;
+
+ if(state->pen.conceal)
+ args[argi++] = 8;
+
+ if(state->pen.strike)
+ args[argi++] = 9;
+
+ if(state->pen.font)
+ args[argi++] = 10 + state->pen.font;
+
+ if(state->pen.underline == VTERM_UNDERLINE_DOUBLE)
+ args[argi++] = 21;
+
+ argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true);
+
+ argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false);
+
+ if(state->pen.small) {
+ if(state->pen.baseline == VTERM_BASELINE_RAISE)
+ args[argi++] = 73;
+ else if(state->pen.baseline == VTERM_BASELINE_LOWER)
+ args[argi++] = 74;
+ }
+
+ return argi;
+}
+
+int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
+{
+ switch(attr) {
+ case VTERM_ATTR_BOLD:
+ val->boolean = state->pen.bold;
+ return 1;
+
+ case VTERM_ATTR_UNDERLINE:
+ val->number = state->pen.underline;
+ return 1;
+
+ case VTERM_ATTR_ITALIC:
+ val->boolean = state->pen.italic;
+ return 1;
+
+ case VTERM_ATTR_BLINK:
+ val->boolean = state->pen.blink;
+ return 1;
+
+ case VTERM_ATTR_REVERSE:
+ val->boolean = state->pen.reverse;
+ return 1;
+
+ case VTERM_ATTR_CONCEAL:
+ val->boolean = state->pen.conceal;
+ return 1;
+
+ case VTERM_ATTR_STRIKE:
+ val->boolean = state->pen.strike;
+ return 1;
+
+ case VTERM_ATTR_FONT:
+ val->number = state->pen.font;
+ return 1;
+
+ case VTERM_ATTR_FOREGROUND:
+ val->color = state->pen.fg;
+ return 1;
+
+ case VTERM_ATTR_BACKGROUND:
+ val->color = state->pen.bg;
+ return 1;
+
+ case VTERM_ATTR_SMALL:
+ val->boolean = state->pen.small;
+ return 1;
+
+ case VTERM_ATTR_BASELINE:
+ val->number = state->pen.baseline;
+ return 1;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/libvterm/src/rect.h b/src/libs/3rdparty/libvterm/src/rect.h
new file mode 100644
index 0000000000..2114f24c1b
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/rect.h
@@ -0,0 +1,56 @@
+/*
+ * Some utility functions on VTermRect structures
+ */
+
+#define STRFrect "(%d,%d-%d,%d)"
+#define ARGSrect(r) (r).start_row, (r).start_col, (r).end_row, (r).end_col
+
+/* Expand dst to contain src as well */
+static void rect_expand(VTermRect *dst, VTermRect *src)
+{
+ if(dst->start_row > src->start_row) dst->start_row = src->start_row;
+ if(dst->start_col > src->start_col) dst->start_col = src->start_col;
+ if(dst->end_row < src->end_row) dst->end_row = src->end_row;
+ if(dst->end_col < src->end_col) dst->end_col = src->end_col;
+}
+
+/* Clip the dst to ensure it does not step outside of bounds */
+static void rect_clip(VTermRect *dst, VTermRect *bounds)
+{
+ if(dst->start_row < bounds->start_row) dst->start_row = bounds->start_row;
+ if(dst->start_col < bounds->start_col) dst->start_col = bounds->start_col;
+ if(dst->end_row > bounds->end_row) dst->end_row = bounds->end_row;
+ if(dst->end_col > bounds->end_col) dst->end_col = bounds->end_col;
+ /* Ensure it doesn't end up negatively-sized */
+ if(dst->end_row < dst->start_row) dst->end_row = dst->start_row;
+ if(dst->end_col < dst->start_col) dst->end_col = dst->start_col;
+}
+
+/* True if the two rectangles are equal */
+static int rect_equal(VTermRect *a, VTermRect *b)
+{
+ return (a->start_row == b->start_row) &&
+ (a->start_col == b->start_col) &&
+ (a->end_row == b->end_row) &&
+ (a->end_col == b->end_col);
+}
+
+/* True if small is contained entirely within big */
+static int rect_contains(VTermRect *big, VTermRect *small)
+{
+ if(small->start_row < big->start_row) return 0;
+ if(small->start_col < big->start_col) return 0;
+ if(small->end_row > big->end_row) return 0;
+ if(small->end_col > big->end_col) return 0;
+ return 1;
+}
+
+/* True if the rectangles overlap at all */
+static int rect_intersects(VTermRect *a, VTermRect *b)
+{
+ if(a->start_row > b->end_row || b->start_row > a->end_row)
+ return 0;
+ if(a->start_col > b->end_col || b->start_col > a->end_col)
+ return 0;
+ return 1;
+}
diff --git a/src/libs/3rdparty/libvterm/src/screen.c b/src/libs/3rdparty/libvterm/src/screen.c
new file mode 100644
index 0000000000..6c549094a9
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/screen.c
@@ -0,0 +1,1191 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "rect.h"
+#include "utf8.h"
+
+#define UNICODE_SPACE 0x20
+#define UNICODE_LINEFEED 0x0a
+
+#undef DEBUG_REFLOW
+
+/* State of the pen at some moment in time, also used in a cell */
+typedef struct
+{
+ /* After the bitfield */
+ VTermColor fg, bg;
+
+ unsigned int bold : 1;
+ unsigned int underline : 3;
+ unsigned int italic : 1;
+ unsigned int blink : 1;
+ unsigned int reverse : 1;
+ unsigned int conceal : 1;
+ unsigned int strike : 1;
+ unsigned int font : 4; /* 0 to 9 */
+ unsigned int small : 1;
+ unsigned int baseline : 2;
+
+ /* Extra state storage that isn't strictly pen-related */
+ unsigned int protected_cell : 1;
+ unsigned int dwl : 1; /* on a DECDWL or DECDHL line */
+ unsigned int dhl : 2; /* on a DECDHL line (1=top 2=bottom) */
+} ScreenPen;
+
+/* Internal representation of a screen cell */
+typedef struct
+{
+ uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
+ ScreenPen pen;
+} ScreenCell;
+
+struct VTermScreen
+{
+ VTerm *vt;
+ VTermState *state;
+
+ const VTermScreenCallbacks *callbacks;
+ void *cbdata;
+
+ VTermDamageSize damage_merge;
+ /* start_row == -1 => no damage */
+ VTermRect damaged;
+ VTermRect pending_scrollrect;
+ int pending_scroll_downward, pending_scroll_rightward;
+
+ int rows;
+ int cols;
+
+ unsigned int global_reverse : 1;
+ unsigned int reflow : 1;
+
+ /* Primary and Altscreen. buffers[1] is lazily allocated as needed */
+ ScreenCell *buffers[2];
+
+ /* buffer will == buffers[0] or buffers[1], depending on altscreen */
+ ScreenCell *buffer;
+
+ /* buffer for a single screen row used in scrollback storage callbacks */
+ VTermScreenCell *sb_buffer;
+
+ ScreenPen pen;
+};
+
+static inline void clearcell(const VTermScreen *screen, ScreenCell *cell)
+{
+ cell->chars[0] = 0;
+ cell->pen = screen->pen;
+}
+
+static inline ScreenCell *getcell(const VTermScreen *screen, int row, int col)
+{
+ if(row < 0 || row >= screen->rows)
+ return NULL;
+ if(col < 0 || col >= screen->cols)
+ return NULL;
+ return screen->buffer + (screen->cols * row) + col;
+}
+
+static ScreenCell *alloc_buffer(VTermScreen *screen, int rows, int cols)
+{
+ ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * rows * cols);
+
+ for(int row = 0; row < rows; row++) {
+ for(int col = 0; col < cols; col++) {
+ clearcell(screen, &new_buffer[row * cols + col]);
+ }
+ }
+
+ return new_buffer;
+}
+
+static void damagerect(VTermScreen *screen, VTermRect rect)
+{
+ VTermRect emit;
+
+ switch(screen->damage_merge) {
+ case VTERM_DAMAGE_CELL:
+ /* Always emit damage event */
+ emit = rect;
+ break;
+
+ case VTERM_DAMAGE_ROW:
+ /* Emit damage longer than one row. Try to merge with existing damage in
+ * the same row */
+ if(rect.end_row > rect.start_row + 1) {
+ // Bigger than 1 line - flush existing, emit this
+ vterm_screen_flush_damage(screen);
+ emit = rect;
+ }
+ else if(screen->damaged.start_row == -1) {
+ // None stored yet
+ screen->damaged = rect;
+ return;
+ }
+ else if(rect.start_row == screen->damaged.start_row) {
+ // Merge with the stored line
+ if(screen->damaged.start_col > rect.start_col)
+ screen->damaged.start_col = rect.start_col;
+ if(screen->damaged.end_col < rect.end_col)
+ screen->damaged.end_col = rect.end_col;
+ return;
+ }
+ else {
+ // Emit the currently stored line, store a new one
+ emit = screen->damaged;
+ screen->damaged = rect;
+ }
+ break;
+
+ case VTERM_DAMAGE_SCREEN:
+ case VTERM_DAMAGE_SCROLL:
+ /* Never emit damage event */
+ if(screen->damaged.start_row == -1)
+ screen->damaged = rect;
+ else {
+ rect_expand(&screen->damaged, &rect);
+ }
+ return;
+
+ default:
+ DEBUG_LOG("TODO: Maybe merge damage for level %d\n", screen->damage_merge);
+ return;
+ }
+
+ if(screen->callbacks && screen->callbacks->damage)
+ (*screen->callbacks->damage)(emit, screen->cbdata);
+}
+
+static void damagescreen(VTermScreen *screen)
+{
+ VTermRect rect = {
+ .start_row = 0,
+ .end_row = screen->rows,
+ .start_col = 0,
+ .end_col = screen->cols,
+ };
+
+ damagerect(screen, rect);
+}
+
+static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
+{
+ VTermScreen *screen = user;
+ ScreenCell *cell = getcell(screen, pos.row, pos.col);
+
+ if(!cell)
+ return 0;
+
+ int i;
+ for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) {
+ cell->chars[i] = info->chars[i];
+ cell->pen = screen->pen;
+ }
+ if(i < VTERM_MAX_CHARS_PER_CELL)
+ cell->chars[i] = 0;
+
+ for(int col = 1; col < info->width; col++)
+ getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1;
+
+ VTermRect rect = {
+ .start_row = pos.row,
+ .end_row = pos.row+1,
+ .start_col = pos.col,
+ .end_col = pos.col+info->width,
+ };
+
+ cell->pen.protected_cell = info->protected_cell;
+ cell->pen.dwl = info->dwl;
+ cell->pen.dhl = info->dhl;
+
+ damagerect(screen, rect);
+
+ return 1;
+}
+
+static void sb_pushline_from_row(VTermScreen *screen, int row)
+{
+ VTermPos pos = { .row = row };
+ for(pos.col = 0; pos.col < screen->cols; pos.col++)
+ vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col);
+
+ (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
+}
+
+static int moverect_internal(VTermRect dest, VTermRect src, void *user)
+{
+ VTermScreen *screen = user;
+
+ if(screen->callbacks && screen->callbacks->sb_pushline &&
+ dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner
+ dest.end_col == screen->cols && // full width
+ screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen
+ for(int row = 0; row < src.start_row; row++)
+ sb_pushline_from_row(screen, row);
+ }
+
+ int cols = src.end_col - src.start_col;
+ int downward = src.start_row - dest.start_row;
+
+ int init_row, test_row, inc_row;
+ if(downward < 0) {
+ init_row = dest.end_row - 1;
+ test_row = dest.start_row - 1;
+ inc_row = -1;
+ }
+ else {
+ init_row = dest.start_row;
+ test_row = dest.end_row;
+ inc_row = +1;
+ }
+
+ for(int row = init_row; row != test_row; row += inc_row)
+ memmove(getcell(screen, row, dest.start_col),
+ getcell(screen, row + downward, src.start_col),
+ cols * sizeof(ScreenCell));
+
+ return 1;
+}
+
+static int moverect_user(VTermRect dest, VTermRect src, void *user)
+{
+ VTermScreen *screen = user;
+
+ if(screen->callbacks && screen->callbacks->moverect) {
+ if(screen->damage_merge != VTERM_DAMAGE_SCROLL)
+ // Avoid an infinite loop
+ vterm_screen_flush_damage(screen);
+
+ if((*screen->callbacks->moverect)(dest, src, screen->cbdata))
+ return 1;
+ }
+
+ damagerect(screen, dest);
+
+ return 1;
+}
+
+static int erase_internal(VTermRect rect, int selective, void *user)
+{
+ VTermScreen *screen = user;
+
+ for(int row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) {
+ const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row);
+
+ for(int col = rect.start_col; col < rect.end_col; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+
+ if(selective && cell->pen.protected_cell)
+ continue;
+
+ cell->chars[0] = 0;
+ cell->pen = (ScreenPen){
+ /* Only copy .fg and .bg; leave things like rv in reset state */
+ .fg = screen->pen.fg,
+ .bg = screen->pen.bg,
+ };
+ cell->pen.dwl = info->doublewidth;
+ cell->pen.dhl = info->doubleheight;
+ }
+ }
+
+ return 1;
+}
+
+static int erase_user(VTermRect rect, int selective, void *user)
+{
+ VTermScreen *screen = user;
+
+ damagerect(screen, rect);
+
+ return 1;
+}
+
+static int erase(VTermRect rect, int selective, void *user)
+{
+ erase_internal(rect, selective, user);
+ return erase_user(rect, 0, user);
+}
+
+static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
+{
+ VTermScreen *screen = user;
+
+ if(screen->damage_merge != VTERM_DAMAGE_SCROLL) {
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_internal, erase_internal, screen);
+
+ vterm_screen_flush_damage(screen);
+
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_user, erase_user, screen);
+
+ return 1;
+ }
+
+ if(screen->damaged.start_row != -1 &&
+ !rect_intersects(&rect, &screen->damaged)) {
+ vterm_screen_flush_damage(screen);
+ }
+
+ if(screen->pending_scrollrect.start_row == -1) {
+ screen->pending_scrollrect = rect;
+ screen->pending_scroll_downward = downward;
+ screen->pending_scroll_rightward = rightward;
+ }
+ else if(rect_equal(&screen->pending_scrollrect, &rect) &&
+ ((screen->pending_scroll_downward == 0 && downward == 0) ||
+ (screen->pending_scroll_rightward == 0 && rightward == 0))) {
+ screen->pending_scroll_downward += downward;
+ screen->pending_scroll_rightward += rightward;
+ }
+ else {
+ vterm_screen_flush_damage(screen);
+
+ screen->pending_scrollrect = rect;
+ screen->pending_scroll_downward = downward;
+ screen->pending_scroll_rightward = rightward;
+ }
+
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_internal, erase_internal, screen);
+
+ if(screen->damaged.start_row == -1)
+ return 1;
+
+ if(rect_contains(&rect, &screen->damaged)) {
+ /* Scroll region entirely contains the damage; just move it */
+ vterm_rect_move(&screen->damaged, -downward, -rightward);
+ rect_clip(&screen->damaged, &rect);
+ }
+ /* There are a number of possible cases here, but lets restrict this to only
+ * the common case where we might actually gain some performance by
+ * optimising it. Namely, a vertical scroll that neatly cuts the damage
+ * region in half.
+ */
+ else if(rect.start_col <= screen->damaged.start_col &&
+ rect.end_col >= screen->damaged.end_col &&
+ rightward == 0) {
+ if(screen->damaged.start_row >= rect.start_row &&
+ screen->damaged.start_row < rect.end_row) {
+ screen->damaged.start_row -= downward;
+ if(screen->damaged.start_row < rect.start_row)
+ screen->damaged.start_row = rect.start_row;
+ if(screen->damaged.start_row > rect.end_row)
+ screen->damaged.start_row = rect.end_row;
+ }
+ if(screen->damaged.end_row >= rect.start_row &&
+ screen->damaged.end_row < rect.end_row) {
+ screen->damaged.end_row -= downward;
+ if(screen->damaged.end_row < rect.start_row)
+ screen->damaged.end_row = rect.start_row;
+ if(screen->damaged.end_row > rect.end_row)
+ screen->damaged.end_row = rect.end_row;
+ }
+ }
+ else {
+ DEBUG_LOG("TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n",
+ ARGSrect(screen->damaged), ARGSrect(rect));
+ }
+
+ return 1;
+}
+
+static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
+{
+ VTermScreen *screen = user;
+
+ if(screen->callbacks && screen->callbacks->movecursor)
+ return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata);
+
+ return 0;
+}
+
+static int setpenattr(VTermAttr attr, VTermValue *val, void *user)
+{
+ VTermScreen *screen = user;
+
+ switch(attr) {
+ case VTERM_ATTR_BOLD:
+ screen->pen.bold = val->boolean;
+ return 1;
+ case VTERM_ATTR_UNDERLINE:
+ screen->pen.underline = val->number;
+ return 1;
+ case VTERM_ATTR_ITALIC:
+ screen->pen.italic = val->boolean;
+ return 1;
+ case VTERM_ATTR_BLINK:
+ screen->pen.blink = val->boolean;
+ return 1;
+ case VTERM_ATTR_REVERSE:
+ screen->pen.reverse = val->boolean;
+ return 1;
+ case VTERM_ATTR_CONCEAL:
+ screen->pen.conceal = val->boolean;
+ return 1;
+ case VTERM_ATTR_STRIKE:
+ screen->pen.strike = val->boolean;
+ return 1;
+ case VTERM_ATTR_FONT:
+ screen->pen.font = val->number;
+ return 1;
+ case VTERM_ATTR_FOREGROUND:
+ screen->pen.fg = val->color;
+ return 1;
+ case VTERM_ATTR_BACKGROUND:
+ screen->pen.bg = val->color;
+ return 1;
+ case VTERM_ATTR_SMALL:
+ screen->pen.small = val->boolean;
+ return 1;
+ case VTERM_ATTR_BASELINE:
+ screen->pen.baseline = val->number;
+ return 1;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int settermprop(VTermProp prop, VTermValue *val, void *user)
+{
+ VTermScreen *screen = user;
+
+ switch(prop) {
+ case VTERM_PROP_ALTSCREEN:
+ if(val->boolean && !screen->buffers[BUFIDX_ALTSCREEN])
+ return 0;
+
+ screen->buffer = val->boolean ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY];
+ /* only send a damage event on disable; because during enable there's an
+ * erase that sends a damage anyway
+ */
+ if(!val->boolean)
+ damagescreen(screen);
+ break;
+ case VTERM_PROP_REVERSE:
+ screen->global_reverse = val->boolean;
+ damagescreen(screen);
+ break;
+ default:
+ ; /* ignore */
+ }
+
+ if(screen->callbacks && screen->callbacks->settermprop)
+ return (*screen->callbacks->settermprop)(prop, val, screen->cbdata);
+
+ return 1;
+}
+
+static int bell(void *user)
+{
+ VTermScreen *screen = user;
+
+ if(screen->callbacks && screen->callbacks->bell)
+ return (*screen->callbacks->bell)(screen->cbdata);
+
+ return 0;
+}
+
+/* How many cells are non-blank
+ * Returns the position of the first blank cell in the trailing blank end */
+static int line_popcount(ScreenCell *buffer, int row, int rows, int cols)
+{
+ int col = cols - 1;
+ while(col >= 0 && buffer[row * cols + col].chars[0] == 0)
+ col--;
+ return col + 1;
+}
+
+#define REFLOW (screen->reflow)
+
+static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new_cols, bool active, VTermStateFields *statefields)
+{
+ int old_rows = screen->rows;
+ int old_cols = screen->cols;
+
+ ScreenCell *old_buffer = screen->buffers[bufidx];
+ VTermLineInfo *old_lineinfo = statefields->lineinfos[bufidx];
+
+ ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols);
+ VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt, sizeof(new_lineinfo[0]) * new_rows);
+
+ int old_row = old_rows - 1;
+ int new_row = new_rows - 1;
+
+ VTermPos old_cursor = statefields->pos;
+ VTermPos new_cursor = { -1, -1 };
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, "Resizing from %dx%d to %dx%d; cursor was at (%d,%d)\n",
+ old_cols, old_rows, new_cols, new_rows, old_cursor.col, old_cursor.row);
+#endif
+
+ /* Keep track of the final row that is knonw to be blank, so we know what
+ * spare space we have for scrolling into
+ */
+ int final_blank_row = new_rows;
+
+ while(old_row >= 0) {
+ int old_row_end = old_row;
+ /* TODO: Stop if dwl or dhl */
+ while(REFLOW && old_lineinfo && old_row >= 0 && old_lineinfo[old_row].continuation)
+ old_row--;
+ int old_row_start = old_row;
+
+ int width = 0;
+ for(int row = old_row_start; row <= old_row_end; row++) {
+ if(REFLOW && row < (old_rows - 1) && old_lineinfo[row + 1].continuation)
+ width += old_cols;
+ else
+ width += line_popcount(old_buffer, row, old_rows, old_cols);
+ }
+
+ if(final_blank_row == (new_row + 1) && width == 0)
+ final_blank_row = new_row;
+
+ int new_height = REFLOW
+ ? width ? (width + new_cols - 1) / new_cols : 1
+ : 1;
+
+ int new_row_end = new_row;
+ int new_row_start = new_row - new_height + 1;
+
+ old_row = old_row_start;
+ int old_col = 0;
+
+ int spare_rows = new_rows - final_blank_row;
+
+ if(new_row_start < 0 && /* we'd fall off the top */
+ spare_rows >= 0 && /* we actually have spare rows */
+ (!active || new_cursor.row == -1 || (new_cursor.row - new_row_start) < new_rows))
+ {
+ /* Attempt to scroll content down into the blank rows at the bottom to
+ * make it fit
+ */
+ int downwards = -new_row_start;
+ if(downwards > spare_rows)
+ downwards = spare_rows;
+ int rowcount = new_rows - downwards;
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, " scroll %d rows +%d downwards\n", rowcount, downwards);
+#endif
+
+ memmove(&new_buffer[downwards * new_cols], &new_buffer[0], rowcount * new_cols * sizeof(ScreenCell));
+ memmove(&new_lineinfo[downwards], &new_lineinfo[0], rowcount * sizeof(new_lineinfo[0]));
+
+ new_row += downwards;
+ new_row_start += downwards;
+ new_row_end += downwards;
+
+ if(new_cursor.row >= 0)
+ new_cursor.row += downwards;
+
+ final_blank_row += downwards;
+ }
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, " rows [%d..%d] <- [%d..%d] width=%d\n",
+ new_row_start, new_row_end, old_row_start, old_row_end, width);
+#endif
+
+ if(new_row_start < 0) {
+ if(old_row_start <= old_cursor.row && old_cursor.row < old_row_end) {
+ new_cursor.row = 0;
+ new_cursor.col = old_cursor.col;
+ if(new_cursor.col >= new_cols)
+ new_cursor.col = new_cols-1;
+ }
+ break;
+ }
+
+ for(new_row = new_row_start, old_row = old_row_start; new_row <= new_row_end; new_row++) {
+ int count = width >= new_cols ? new_cols : width;
+ width -= count;
+
+ int new_col = 0;
+
+ while(count) {
+ /* TODO: This could surely be done a lot faster by memcpy()'ing the entire range */
+ new_buffer[new_row * new_cols + new_col] = old_buffer[old_row * old_cols + old_col];
+
+ if(old_cursor.row == old_row && old_cursor.col == old_col)
+ new_cursor.row = new_row, new_cursor.col = new_col;
+
+ old_col++;
+ if(old_col == old_cols) {
+ old_row++;
+
+ if(!REFLOW) {
+ new_col++;
+ break;
+ }
+ old_col = 0;
+ }
+
+ new_col++;
+ count--;
+ }
+
+ if(old_cursor.row == old_row && old_cursor.col >= old_col) {
+ new_cursor.row = new_row, new_cursor.col = (old_cursor.col - old_col + new_col);
+ if(new_cursor.col >= new_cols)
+ new_cursor.col = new_cols-1;
+ }
+
+ while(new_col < new_cols) {
+ clearcell(screen, &new_buffer[new_row * new_cols + new_col]);
+ new_col++;
+ }
+
+ new_lineinfo[new_row].continuation = (new_row > new_row_start);
+ }
+
+ old_row = old_row_start - 1;
+ new_row = new_row_start - 1;
+ }
+
+ if(old_cursor.row <= old_row) {
+ /* cursor would have moved entirely off the top of the screen; lets just
+ * bring it within range */
+ new_cursor.row = 0, new_cursor.col = old_cursor.col;
+ if(new_cursor.col >= new_cols)
+ new_cursor.col = new_cols-1;
+ }
+
+ /* We really expect the cursor position to be set by now */
+ if(active && (new_cursor.row == -1 || new_cursor.col == -1)) {
+ fprintf(stderr, "screen_resize failed to update cursor position\n");
+ abort();
+ }
+
+ if(old_row >= 0 && bufidx == BUFIDX_PRIMARY) {
+ /* Push spare lines to scrollback buffer */
+ if(screen->callbacks && screen->callbacks->sb_pushline)
+ for(int row = 0; row <= old_row; row++)
+ sb_pushline_from_row(screen, row);
+ if(active)
+ statefields->pos.row -= (old_row + 1);
+ }
+ if(new_row >= 0 && bufidx == BUFIDX_PRIMARY &&
+ screen->callbacks && screen->callbacks->sb_popline) {
+ /* Try to backfill rows by popping scrollback buffer */
+ while(new_row >= 0) {
+ if(!(screen->callbacks->sb_popline(old_cols, screen->sb_buffer, screen->cbdata)))
+ break;
+
+ VTermPos pos = { .row = new_row };
+ for(pos.col = 0; pos.col < old_cols && pos.col < new_cols; pos.col += screen->sb_buffer[pos.col].width) {
+ VTermScreenCell *src = &screen->sb_buffer[pos.col];
+ ScreenCell *dst = &new_buffer[pos.row * new_cols + pos.col];
+
+ for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
+ dst->chars[i] = src->chars[i];
+ if(!src->chars[i])
+ break;
+ }
+
+ dst->pen.bold = src->attrs.bold;
+ dst->pen.underline = src->attrs.underline;
+ dst->pen.italic = src->attrs.italic;
+ dst->pen.blink = src->attrs.blink;
+ dst->pen.reverse = src->attrs.reverse ^ screen->global_reverse;
+ dst->pen.conceal = src->attrs.conceal;
+ dst->pen.strike = src->attrs.strike;
+ dst->pen.font = src->attrs.font;
+ dst->pen.small = src->attrs.small;
+ dst->pen.baseline = src->attrs.baseline;
+
+ dst->pen.fg = src->fg;
+ dst->pen.bg = src->bg;
+
+ if(src->width == 2 && pos.col < (new_cols-1))
+ (dst + 1)->chars[0] = (uint32_t) -1;
+ }
+ for( ; pos.col < new_cols; pos.col++)
+ clearcell(screen, &new_buffer[pos.row * new_cols + pos.col]);
+ new_row--;
+
+ if(active)
+ statefields->pos.row++;
+ }
+ }
+ if(new_row >= 0) {
+ /* Scroll new rows back up to the top and fill in blanks at the bottom */
+ int moverows = new_rows - new_row - 1;
+ memmove(&new_buffer[0], &new_buffer[(new_row + 1) * new_cols], moverows * new_cols * sizeof(ScreenCell));
+ memmove(&new_lineinfo[0], &new_lineinfo[new_row + 1], moverows * sizeof(new_lineinfo[0]));
+
+ new_cursor.row -= (new_row + 1);
+
+ for(new_row = moverows; new_row < new_rows; new_row++) {
+ for(int col = 0; col < new_cols; col++)
+ clearcell(screen, &new_buffer[new_row * new_cols + col]);
+ new_lineinfo[new_row] = (VTermLineInfo){ 0 };
+ }
+ }
+
+ vterm_allocator_free(screen->vt, old_buffer);
+ screen->buffers[bufidx] = new_buffer;
+
+ vterm_allocator_free(screen->vt, old_lineinfo);
+ statefields->lineinfos[bufidx] = new_lineinfo;
+
+ if(active)
+ statefields->pos = new_cursor;
+
+ return;
+}
+
+static int resize(int new_rows, int new_cols, VTermStateFields *fields, void *user)
+{
+ VTermScreen *screen = user;
+
+ int altscreen_active = (screen->buffers[BUFIDX_ALTSCREEN] && screen->buffer == screen->buffers[BUFIDX_ALTSCREEN]);
+
+ int old_rows = screen->rows;
+ int old_cols = screen->cols;
+
+ if(new_cols > old_cols) {
+ /* Ensure that ->sb_buffer is large enough for a new or and old row */
+ if(screen->sb_buffer)
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols);
+ }
+
+ resize_buffer(screen, 0, new_rows, new_cols, !altscreen_active, fields);
+ if(screen->buffers[BUFIDX_ALTSCREEN])
+ resize_buffer(screen, 1, new_rows, new_cols, altscreen_active, fields);
+ else if(new_rows != old_rows) {
+ /* We don't need a full resize of the altscreen because it isn't enabled
+ * but we should at least keep the lineinfo the right size */
+ vterm_allocator_free(screen->vt, fields->lineinfos[BUFIDX_ALTSCREEN]);
+
+ VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt, sizeof(new_lineinfo[0]) * new_rows);
+ for(int row = 0; row < new_rows; row++)
+ new_lineinfo[row] = (VTermLineInfo){ 0 };
+
+ fields->lineinfos[BUFIDX_ALTSCREEN] = new_lineinfo;
+ }
+
+ screen->buffer = altscreen_active ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY];
+
+ screen->rows = new_rows;
+ screen->cols = new_cols;
+
+ if(new_cols <= old_cols) {
+ if(screen->sb_buffer)
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols);
+ }
+
+ /* TODO: Maaaaybe we can optimise this if there's no reflow happening */
+ damagescreen(screen);
+
+ if(screen->callbacks && screen->callbacks->resize)
+ return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata);
+
+ return 1;
+}
+
+static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user)
+{
+ VTermScreen *screen = user;
+
+ if(newinfo->doublewidth != oldinfo->doublewidth ||
+ newinfo->doubleheight != oldinfo->doubleheight) {
+ for(int col = 0; col < screen->cols; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+ cell->pen.dwl = newinfo->doublewidth;
+ cell->pen.dhl = newinfo->doubleheight;
+ }
+
+ VTermRect rect = {
+ .start_row = row,
+ .end_row = row + 1,
+ .start_col = 0,
+ .end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols,
+ };
+ damagerect(screen, rect);
+
+ if(newinfo->doublewidth) {
+ rect.start_col = screen->cols / 2;
+ rect.end_col = screen->cols;
+
+ erase_internal(rect, 0, user);
+ }
+ }
+
+ return 1;
+}
+
+static int sb_clear(void *user) {
+ VTermScreen *screen = user;
+
+ if(screen->callbacks && screen->callbacks->sb_clear)
+ if((*screen->callbacks->sb_clear)(screen->cbdata))
+ return 1;
+
+ return 0;
+}
+
+static VTermStateCallbacks state_cbs = {
+ .putglyph = &putglyph,
+ .movecursor = &movecursor,
+ .scrollrect = &scrollrect,
+ .erase = &erase,
+ .setpenattr = &setpenattr,
+ .settermprop = &settermprop,
+ .bell = &bell,
+ .resize = &resize,
+ .setlineinfo = &setlineinfo,
+ .sb_clear = &sb_clear,
+};
+
+static VTermScreen *screen_new(VTerm *vt)
+{
+ VTermState *state = vterm_obtain_state(vt);
+ if(!state)
+ return NULL;
+
+ VTermScreen *screen = vterm_allocator_malloc(vt, sizeof(VTermScreen));
+ int rows, cols;
+
+ vterm_get_size(vt, &rows, &cols);
+
+ screen->vt = vt;
+ screen->state = state;
+
+ screen->damage_merge = VTERM_DAMAGE_CELL;
+ screen->damaged.start_row = -1;
+ screen->pending_scrollrect.start_row = -1;
+
+ screen->rows = rows;
+ screen->cols = cols;
+
+ screen->global_reverse = false;
+ screen->reflow = false;
+
+ screen->callbacks = NULL;
+ screen->cbdata = NULL;
+
+ screen->buffers[BUFIDX_PRIMARY] = alloc_buffer(screen, rows, cols);
+
+ screen->buffer = screen->buffers[BUFIDX_PRIMARY];
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * cols);
+
+ vterm_state_set_callbacks(screen->state, &state_cbs, screen);
+
+ return screen;
+}
+
+INTERNAL void vterm_screen_free(VTermScreen *screen)
+{
+ vterm_allocator_free(screen->vt, screen->buffers[BUFIDX_PRIMARY]);
+ if(screen->buffers[BUFIDX_ALTSCREEN])
+ vterm_allocator_free(screen->vt, screen->buffers[BUFIDX_ALTSCREEN]);
+
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+
+ vterm_allocator_free(screen->vt, screen);
+}
+
+void vterm_screen_reset(VTermScreen *screen, int hard)
+{
+ screen->damaged.start_row = -1;
+ screen->pending_scrollrect.start_row = -1;
+ vterm_state_reset(screen->state, hard);
+ vterm_screen_flush_damage(screen);
+}
+
+static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect)
+{
+ size_t outpos = 0;
+ int padding = 0;
+
+#define PUT(c) \
+ if(utf8) { \
+ size_t thislen = utf8_seqlen(c); \
+ if(buffer && outpos + thislen <= len) \
+ outpos += fill_utf8((c), (char *)buffer + outpos); \
+ else \
+ outpos += thislen; \
+ } \
+ else { \
+ if(buffer && outpos + 1 <= len) \
+ ((uint32_t*)buffer)[outpos++] = (c); \
+ else \
+ outpos++; \
+ }
+
+ for(int row = rect.start_row; row < rect.end_row; row++) {
+ for(int col = rect.start_col; col < rect.end_col; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+
+ if(cell->chars[0] == 0)
+ // Erased cell, might need a space
+ padding++;
+ else if(cell->chars[0] == (uint32_t)-1)
+ // Gap behind a double-width char, do nothing
+ ;
+ else {
+ while(padding) {
+ PUT(UNICODE_SPACE);
+ padding--;
+ }
+ for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) {
+ PUT(cell->chars[i]);
+ }
+ }
+ }
+
+ if(row < rect.end_row - 1) {
+ PUT(UNICODE_LINEFEED);
+ padding = 0;
+ }
+ }
+
+ return outpos;
+}
+
+size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect)
+{
+ return _get_chars(screen, 0, chars, len, rect);
+}
+
+size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect)
+{
+ return _get_chars(screen, 1, str, len, rect);
+}
+
+/* Copy internal to external representation of a screen cell */
+int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell)
+{
+ ScreenCell *intcell = getcell(screen, pos.row, pos.col);
+ if(!intcell)
+ return 0;
+
+ for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
+ cell->chars[i] = intcell->chars[i];
+ if(!intcell->chars[i])
+ break;
+ }
+
+ cell->attrs.bold = intcell->pen.bold;
+ cell->attrs.underline = intcell->pen.underline;
+ cell->attrs.italic = intcell->pen.italic;
+ cell->attrs.blink = intcell->pen.blink;
+ cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse;
+ cell->attrs.conceal = intcell->pen.conceal;
+ cell->attrs.strike = intcell->pen.strike;
+ cell->attrs.font = intcell->pen.font;
+ cell->attrs.small = intcell->pen.small;
+ cell->attrs.baseline = intcell->pen.baseline;
+
+ cell->attrs.dwl = intcell->pen.dwl;
+ cell->attrs.dhl = intcell->pen.dhl;
+
+ cell->fg = intcell->pen.fg;
+ cell->bg = intcell->pen.bg;
+
+ if(pos.col < (screen->cols - 1) &&
+ getcell(screen, pos.row, pos.col + 1)->chars[0] == (uint32_t)-1)
+ cell->width = 2;
+ else
+ cell->width = 1;
+
+ return 1;
+}
+
+int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos)
+{
+ /* This cell is EOL if this and every cell to the right is black */
+ for(; pos.col < screen->cols; pos.col++) {
+ ScreenCell *cell = getcell(screen, pos.row, pos.col);
+ if(cell->chars[0] != 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+VTermScreen *vterm_obtain_screen(VTerm *vt)
+{
+ if(vt->screen)
+ return vt->screen;
+
+ VTermScreen *screen = screen_new(vt);
+ vt->screen = screen;
+
+ return screen;
+}
+
+void vterm_screen_enable_reflow(VTermScreen *screen, bool reflow)
+{
+ screen->reflow = reflow;
+}
+
+#undef vterm_screen_set_reflow
+void vterm_screen_set_reflow(VTermScreen *screen, bool reflow)
+{
+ vterm_screen_enable_reflow(screen, reflow);
+}
+
+void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen)
+{
+ if(!screen->buffers[BUFIDX_ALTSCREEN] && altscreen) {
+ int rows, cols;
+ vterm_get_size(screen->vt, &rows, &cols);
+
+ screen->buffers[BUFIDX_ALTSCREEN] = alloc_buffer(screen, rows, cols);
+ }
+}
+
+void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user)
+{
+ screen->callbacks = callbacks;
+ screen->cbdata = user;
+}
+
+void *vterm_screen_get_cbdata(VTermScreen *screen)
+{
+ return screen->cbdata;
+}
+
+void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user)
+{
+ vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user);
+}
+
+void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen)
+{
+ return vterm_state_get_unrecognised_fbdata(screen->state);
+}
+
+void vterm_screen_flush_damage(VTermScreen *screen)
+{
+ if(screen->pending_scrollrect.start_row != -1) {
+ vterm_scroll_rect(screen->pending_scrollrect, screen->pending_scroll_downward, screen->pending_scroll_rightward,
+ moverect_user, erase_user, screen);
+
+ screen->pending_scrollrect.start_row = -1;
+ }
+
+ if(screen->damaged.start_row != -1) {
+ if(screen->callbacks && screen->callbacks->damage)
+ (*screen->callbacks->damage)(screen->damaged, screen->cbdata);
+
+ screen->damaged.start_row = -1;
+ }
+}
+
+void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size)
+{
+ vterm_screen_flush_damage(screen);
+ screen->damage_merge = size;
+}
+
+static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b)
+{
+ if((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold))
+ return 1;
+ if((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline))
+ return 1;
+ if((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic))
+ return 1;
+ if((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink))
+ return 1;
+ if((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse))
+ return 1;
+ if((attrs & VTERM_ATTR_CONCEAL_MASK) && (a->pen.conceal != b->pen.conceal))
+ return 1;
+ if((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike))
+ return 1;
+ if((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font))
+ return 1;
+ if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_is_equal(&a->pen.fg, &b->pen.fg))
+ return 1;
+ if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_equal(&a->pen.bg, &b->pen.bg))
+ return 1;
+ if((attrs & VTERM_ATTR_SMALL_MASK) && (a->pen.small != b->pen.small))
+ return 1;
+ if((attrs & VTERM_ATTR_BASELINE_MASK) && (a->pen.baseline != b->pen.baseline))
+ return 1;
+
+ return 0;
+}
+
+int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs)
+{
+ ScreenCell *target = getcell(screen, pos.row, pos.col);
+
+ // TODO: bounds check
+ extent->start_row = pos.row;
+ extent->end_row = pos.row + 1;
+
+ if(extent->start_col < 0)
+ extent->start_col = 0;
+ if(extent->end_col < 0)
+ extent->end_col = screen->cols;
+
+ int col;
+
+ for(col = pos.col - 1; col >= extent->start_col; col--)
+ if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
+ break;
+ extent->start_col = col + 1;
+
+ for(col = pos.col + 1; col < extent->end_col; col++)
+ if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
+ break;
+ extent->end_col = col - 1;
+
+ return 1;
+}
+
+void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col)
+{
+ vterm_state_convert_color_to_rgb(screen->state, col);
+}
+
+static void reset_default_colours(VTermScreen *screen, ScreenCell *buffer)
+{
+ for(int row = 0; row <= screen->rows - 1; row++)
+ for(int col = 0; col <= screen->cols - 1; col++) {
+ ScreenCell *cell = &buffer[row * screen->cols + col];
+ if(VTERM_COLOR_IS_DEFAULT_FG(&cell->pen.fg))
+ cell->pen.fg = screen->pen.fg;
+ if(VTERM_COLOR_IS_DEFAULT_BG(&cell->pen.bg))
+ cell->pen.bg = screen->pen.bg;
+ }
+}
+
+void vterm_screen_set_default_colors(VTermScreen *screen, const VTermColor *default_fg, const VTermColor *default_bg)
+{
+ vterm_state_set_default_colors(screen->state, default_fg, default_bg);
+
+ if(default_fg && VTERM_COLOR_IS_DEFAULT_FG(&screen->pen.fg)) {
+ screen->pen.fg = *default_fg;
+ screen->pen.fg.type = (screen->pen.fg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_FG;
+ }
+
+ if(default_bg && VTERM_COLOR_IS_DEFAULT_BG(&screen->pen.bg)) {
+ screen->pen.bg = *default_bg;
+ screen->pen.bg.type = (screen->pen.bg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_BG;
+ }
+
+ reset_default_colours(screen, screen->buffers[0]);
+ if(screen->buffers[1])
+ reset_default_colours(screen, screen->buffers[1]);
+}
diff --git a/src/libs/3rdparty/libvterm/src/state.c b/src/libs/3rdparty/libvterm/src/state.c
new file mode 100644
index 0000000000..ce8e034237
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/state.c
@@ -0,0 +1,2347 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#define strneq(a,b,n) (strncmp(a,b,n)==0)
+
+#if defined(DEBUG) && DEBUG > 1
+# define DEBUG_GLYPH_COMBINE
+#endif
+
+/* Some convenient wrappers to make callback functions easier */
+
+static void putglyph(VTermState *state, const uint32_t chars[], int width, VTermPos pos)
+{
+ VTermGlyphInfo info = {
+ .chars = chars,
+ .width = width,
+ .protected_cell = state->protected_cell,
+ .dwl = state->lineinfo[pos.row].doublewidth,
+ .dhl = state->lineinfo[pos.row].doubleheight,
+ };
+
+ if(state->callbacks && state->callbacks->putglyph)
+ if((*state->callbacks->putglyph)(&info, pos, state->cbdata))
+ return;
+
+ DEBUG_LOG("libvterm: Unhandled putglyph U+%04x at (%d,%d)\n", chars[0], pos.col, pos.row);
+}
+
+static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom)
+{
+ if(state->pos.col == oldpos->col && state->pos.row == oldpos->row)
+ return;
+
+ if(cancel_phantom)
+ state->at_phantom = 0;
+
+ if(state->callbacks && state->callbacks->movecursor)
+ if((*state->callbacks->movecursor)(state->pos, *oldpos, state->mode.cursor_visible, state->cbdata))
+ return;
+}
+
+static void erase(VTermState *state, VTermRect rect, int selective)
+{
+ if(rect.end_col == state->cols) {
+ /* If we're erasing the final cells of any lines, cancel the continuation
+ * marker on the subsequent line
+ */
+ for(int row = rect.start_row + 1; row < rect.end_row + 1 && row < state->rows; row++)
+ state->lineinfo[row].continuation = 0;
+ }
+
+ if(state->callbacks && state->callbacks->erase)
+ if((*state->callbacks->erase)(rect, selective, state->cbdata))
+ return;
+}
+
+static VTermState *vterm_state_new(VTerm *vt)
+{
+ VTermState *state = vterm_allocator_malloc(vt, sizeof(VTermState));
+
+ state->vt = vt;
+
+ state->rows = vt->rows;
+ state->cols = vt->cols;
+
+ state->mouse_col = 0;
+ state->mouse_row = 0;
+ state->mouse_buttons = 0;
+
+ state->mouse_protocol = MOUSE_X10;
+
+ state->callbacks = NULL;
+ state->cbdata = NULL;
+
+ state->selection.callbacks = NULL;
+ state->selection.user = NULL;
+ state->selection.buffer = NULL;
+
+ vterm_state_newpen(state);
+
+ state->bold_is_highbright = 0;
+
+ state->combine_chars_size = 16;
+ state->combine_chars = vterm_allocator_malloc(state->vt, state->combine_chars_size * sizeof(state->combine_chars[0]));
+
+ state->tabstops = vterm_allocator_malloc(state->vt, (state->cols + 7) / 8);
+
+ state->lineinfos[BUFIDX_PRIMARY] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
+ /* TODO: Make an 'enable' function */
+ state->lineinfos[BUFIDX_ALTSCREEN] = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo));
+ state->lineinfo = state->lineinfos[BUFIDX_PRIMARY];
+
+ state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u');
+ if(*state->encoding_utf8.enc->init)
+ (*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data);
+
+ return state;
+}
+
+INTERNAL void vterm_state_free(VTermState *state)
+{
+ vterm_allocator_free(state->vt, state->tabstops);
+ vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_PRIMARY]);
+ if(state->lineinfos[BUFIDX_ALTSCREEN])
+ vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_ALTSCREEN]);
+ vterm_allocator_free(state->vt, state->combine_chars);
+ vterm_allocator_free(state->vt, state);
+}
+
+static void scroll(VTermState *state, VTermRect rect, int downward, int rightward)
+{
+ if(!downward && !rightward)
+ return;
+
+ int rows = rect.end_row - rect.start_row;
+ if(downward > rows)
+ downward = rows;
+ else if(downward < -rows)
+ downward = -rows;
+
+ int cols = rect.end_col - rect.start_col;
+ if(rightward > cols)
+ rightward = cols;
+ else if(rightward < -cols)
+ rightward = -cols;
+
+ // Update lineinfo if full line
+ if(rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) {
+ int height = rect.end_row - rect.start_row - abs(downward);
+
+ if(downward > 0) {
+ memmove(state->lineinfo + rect.start_row,
+ state->lineinfo + rect.start_row + downward,
+ height * sizeof(state->lineinfo[0]));
+ for(int row = rect.end_row - downward; row < rect.end_row; row++)
+ state->lineinfo[row] = (VTermLineInfo){ 0 };
+ }
+ else {
+ memmove(state->lineinfo + rect.start_row - downward,
+ state->lineinfo + rect.start_row,
+ height * sizeof(state->lineinfo[0]));
+ for(int row = rect.start_row; row < rect.start_row - downward; row++)
+ state->lineinfo[row] = (VTermLineInfo){ 0 };
+ }
+ }
+
+ if(state->callbacks && state->callbacks->scrollrect)
+ if((*state->callbacks->scrollrect)(rect, downward, rightward, state->cbdata))
+ return;
+
+ if(state->callbacks)
+ vterm_scroll_rect(rect, downward, rightward,
+ state->callbacks->moverect, state->callbacks->erase, state->cbdata);
+}
+
+static void linefeed(VTermState *state)
+{
+ if(state->pos.row == SCROLLREGION_BOTTOM(state) - 1) {
+ VTermRect rect = {
+ .start_row = state->scrollregion_top,
+ .end_row = SCROLLREGION_BOTTOM(state),
+ .start_col = SCROLLREGION_LEFT(state),
+ .end_col = SCROLLREGION_RIGHT(state),
+ };
+
+ scroll(state, rect, 1, 0);
+ }
+ else if(state->pos.row < state->rows-1)
+ state->pos.row++;
+}
+
+static void grow_combine_buffer(VTermState *state)
+{
+ size_t new_size = state->combine_chars_size * 2;
+ uint32_t *new_chars = vterm_allocator_malloc(state->vt, new_size * sizeof(new_chars[0]));
+
+ memcpy(new_chars, state->combine_chars, state->combine_chars_size * sizeof(new_chars[0]));
+
+ vterm_allocator_free(state->vt, state->combine_chars);
+
+ state->combine_chars = new_chars;
+ state->combine_chars_size = new_size;
+}
+
+static void set_col_tabstop(VTermState *state, int col)
+{
+ unsigned char mask = 1 << (col & 7);
+ state->tabstops[col >> 3] |= mask;
+}
+
+static void clear_col_tabstop(VTermState *state, int col)
+{
+ unsigned char mask = 1 << (col & 7);
+ state->tabstops[col >> 3] &= ~mask;
+}
+
+static int is_col_tabstop(VTermState *state, int col)
+{
+ unsigned char mask = 1 << (col & 7);
+ return state->tabstops[col >> 3] & mask;
+}
+
+static int is_cursor_in_scrollregion(const VTermState *state)
+{
+ if(state->pos.row < state->scrollregion_top ||
+ state->pos.row >= SCROLLREGION_BOTTOM(state))
+ return 0;
+ if(state->pos.col < SCROLLREGION_LEFT(state) ||
+ state->pos.col >= SCROLLREGION_RIGHT(state))
+ return 0;
+
+ return 1;
+}
+
+static void tab(VTermState *state, int count, int direction)
+{
+ while(count > 0) {
+ if(direction > 0) {
+ if(state->pos.col >= THISROWWIDTH(state)-1)
+ return;
+
+ state->pos.col++;
+ }
+ else if(direction < 0) {
+ if(state->pos.col < 1)
+ return;
+
+ state->pos.col--;
+ }
+
+ if(is_col_tabstop(state, state->pos.col))
+ count--;
+ }
+}
+
+#define NO_FORCE 0
+#define FORCE 1
+
+#define DWL_OFF 0
+#define DWL_ON 1
+
+#define DHL_OFF 0
+#define DHL_TOP 1
+#define DHL_BOTTOM 2
+
+static void set_lineinfo(VTermState *state, int row, int force, int dwl, int dhl)
+{
+ VTermLineInfo info = state->lineinfo[row];
+
+ if(dwl == DWL_OFF)
+ info.doublewidth = DWL_OFF;
+ else if(dwl == DWL_ON)
+ info.doublewidth = DWL_ON;
+ // else -1 to ignore
+
+ if(dhl == DHL_OFF)
+ info.doubleheight = DHL_OFF;
+ else if(dhl == DHL_TOP)
+ info.doubleheight = DHL_TOP;
+ else if(dhl == DHL_BOTTOM)
+ info.doubleheight = DHL_BOTTOM;
+
+ if((state->callbacks &&
+ state->callbacks->setlineinfo &&
+ (*state->callbacks->setlineinfo)(row, &info, state->lineinfo + row, state->cbdata))
+ || force)
+ state->lineinfo[row] = info;
+}
+
+static int on_text(const char bytes[], size_t len, void *user)
+{
+ VTermState *state = user;
+
+ VTermPos oldpos = state->pos;
+
+ uint32_t *codepoints = (uint32_t *)(state->vt->tmpbuffer);
+ size_t maxpoints = (state->vt->tmpbuffer_len) / sizeof(uint32_t);
+
+ int npoints = 0;
+ size_t eaten = 0;
+
+ VTermEncodingInstance *encoding =
+ state->gsingle_set ? &state->encoding[state->gsingle_set] :
+ !(bytes[eaten] & 0x80) ? &state->encoding[state->gl_set] :
+ state->vt->mode.utf8 ? &state->encoding_utf8 :
+ &state->encoding[state->gr_set];
+
+ (*encoding->enc->decode)(encoding->enc, encoding->data,
+ codepoints, &npoints, state->gsingle_set ? 1 : maxpoints,
+ bytes, &eaten, len);
+
+ /* There's a chance an encoding (e.g. UTF-8) hasn't found enough bytes yet
+ * for even a single codepoint
+ */
+ if(!npoints)
+ return eaten;
+
+ if(state->gsingle_set && npoints)
+ state->gsingle_set = 0;
+
+ int i = 0;
+
+ /* This is a combining char. that needs to be merged with the previous
+ * glyph output */
+ if(vterm_unicode_is_combining(codepoints[i])) {
+ /* See if the cursor has moved since */
+ if(state->pos.row == state->combine_pos.row && state->pos.col == state->combine_pos.col + state->combine_width) {
+#ifdef DEBUG_GLYPH_COMBINE
+ int printpos;
+ printf("DEBUG: COMBINING SPLIT GLYPH of chars {");
+ for(printpos = 0; state->combine_chars[printpos]; printpos++)
+ printf("U+%04x ", state->combine_chars[printpos]);
+ printf("} + {");
+#endif
+
+ /* Find where we need to append these combining chars */
+ int saved_i = 0;
+ while(state->combine_chars[saved_i])
+ saved_i++;
+
+ /* Add extra ones */
+ while(i < npoints && vterm_unicode_is_combining(codepoints[i])) {
+ if(saved_i >= state->combine_chars_size)
+ grow_combine_buffer(state);
+ state->combine_chars[saved_i++] = codepoints[i++];
+ }
+ if(saved_i >= state->combine_chars_size)
+ grow_combine_buffer(state);
+ state->combine_chars[saved_i] = 0;
+
+#ifdef DEBUG_GLYPH_COMBINE
+ for(; state->combine_chars[printpos]; printpos++)
+ printf("U+%04x ", state->combine_chars[printpos]);
+ printf("}\n");
+#endif
+
+ /* Now render it */
+ putglyph(state, state->combine_chars, state->combine_width, state->combine_pos);
+ }
+ else {
+ DEBUG_LOG("libvterm: TODO: Skip over split char+combining\n");
+ }
+ }
+
+ for(; i < npoints; i++) {
+ // Try to find combining characters following this
+ int glyph_starts = i;
+ int glyph_ends;
+ for(glyph_ends = i + 1;
+ (glyph_ends < npoints) && (glyph_ends < glyph_starts + VTERM_MAX_CHARS_PER_CELL);
+ glyph_ends++)
+ if(!vterm_unicode_is_combining(codepoints[glyph_ends]))
+ break;
+
+ int width = 0;
+
+ uint32_t chars[VTERM_MAX_CHARS_PER_CELL + 1];
+
+ for( ; i < glyph_ends; i++) {
+ chars[i - glyph_starts] = codepoints[i];
+ int this_width = vterm_unicode_width(codepoints[i]);
+#ifdef DEBUG
+ if(this_width < 0) {
+ fprintf(stderr, "Text with negative-width codepoint U+%04x\n", codepoints[i]);
+ abort();
+ }
+#endif
+ width += this_width;
+ }
+
+ while(i < npoints && vterm_unicode_is_combining(codepoints[i]))
+ i++;
+
+ chars[glyph_ends - glyph_starts] = 0;
+ i--;
+
+#ifdef DEBUG_GLYPH_COMBINE
+ int printpos;
+ printf("DEBUG: COMBINED GLYPH of %d chars {", glyph_ends - glyph_starts);
+ for(printpos = 0; printpos < glyph_ends - glyph_starts; printpos++)
+ printf("U+%04x ", chars[printpos]);
+ printf("}, onscreen width %d\n", width);
+#endif
+
+ if(state->at_phantom || state->pos.col + width > THISROWWIDTH(state)) {
+ linefeed(state);
+ state->pos.col = 0;
+ state->at_phantom = 0;
+ state->lineinfo[state->pos.row].continuation = 1;
+ }
+
+ if(state->mode.insert) {
+ /* TODO: This will be a little inefficient for large bodies of text, as
+ * it'll have to 'ICH' effectively before every glyph. We should scan
+ * ahead and ICH as many times as required
+ */
+ VTermRect rect = {
+ .start_row = state->pos.row,
+ .end_row = state->pos.row + 1,
+ .start_col = state->pos.col,
+ .end_col = THISROWWIDTH(state),
+ };
+ scroll(state, rect, 0, -1);
+ }
+
+ putglyph(state, chars, width, state->pos);
+
+ if(i == npoints - 1) {
+ /* End of the buffer. Save the chars in case we have to combine with
+ * more on the next call */
+ int save_i;
+ for(save_i = 0; chars[save_i]; save_i++) {
+ if(save_i >= state->combine_chars_size)
+ grow_combine_buffer(state);
+ state->combine_chars[save_i] = chars[save_i];
+ }
+ if(save_i >= state->combine_chars_size)
+ grow_combine_buffer(state);
+ state->combine_chars[save_i] = 0;
+ state->combine_width = width;
+ state->combine_pos = state->pos;
+ }
+
+ if(state->pos.col + width >= THISROWWIDTH(state)) {
+ if(state->mode.autowrap)
+ state->at_phantom = 1;
+ }
+ else {
+ state->pos.col += width;
+ }
+ }
+
+ updatecursor(state, &oldpos, 0);
+
+#ifdef DEBUG
+ if(state->pos.row < 0 || state->pos.row >= state->rows ||
+ state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after text: (%d,%d)\n",
+ state->pos.row, state->pos.col);
+ abort();
+ }
+#endif
+
+ return eaten;
+}
+
+static int on_control(unsigned char control, void *user)
+{
+ VTermState *state = user;
+
+ VTermPos oldpos = state->pos;
+
+ switch(control) {
+ case 0x07: // BEL - ECMA-48 8.3.3
+ if(state->callbacks && state->callbacks->bell)
+ (*state->callbacks->bell)(state->cbdata);
+ break;
+
+ case 0x08: // BS - ECMA-48 8.3.5
+ if(state->pos.col > 0)
+ state->pos.col--;
+ break;
+
+ case 0x09: // HT - ECMA-48 8.3.60
+ tab(state, 1, +1);
+ break;
+
+ case 0x0a: // LF - ECMA-48 8.3.74
+ case 0x0b: // VT
+ case 0x0c: // FF
+ linefeed(state);
+ if(state->mode.newline)
+ state->pos.col = 0;
+ break;
+
+ case 0x0d: // CR - ECMA-48 8.3.15
+ state->pos.col = 0;
+ break;
+
+ case 0x0e: // LS1 - ECMA-48 8.3.76
+ state->gl_set = 1;
+ break;
+
+ case 0x0f: // LS0 - ECMA-48 8.3.75
+ state->gl_set = 0;
+ break;
+
+ case 0x84: // IND - DEPRECATED but implemented for completeness
+ linefeed(state);
+ break;
+
+ case 0x85: // NEL - ECMA-48 8.3.86
+ linefeed(state);
+ state->pos.col = 0;
+ break;
+
+ case 0x88: // HTS - ECMA-48 8.3.62
+ set_col_tabstop(state, state->pos.col);
+ break;
+
+ case 0x8d: // RI - ECMA-48 8.3.104
+ if(state->pos.row == state->scrollregion_top) {
+ VTermRect rect = {
+ .start_row = state->scrollregion_top,
+ .end_row = SCROLLREGION_BOTTOM(state),
+ .start_col = SCROLLREGION_LEFT(state),
+ .end_col = SCROLLREGION_RIGHT(state),
+ };
+
+ scroll(state, rect, -1, 0);
+ }
+ else if(state->pos.row > 0)
+ state->pos.row--;
+ break;
+
+ case 0x8e: // SS2 - ECMA-48 8.3.141
+ state->gsingle_set = 2;
+ break;
+
+ case 0x8f: // SS3 - ECMA-48 8.3.142
+ state->gsingle_set = 3;
+ break;
+
+ default:
+ if(state->fallbacks && state->fallbacks->control)
+ if((*state->fallbacks->control)(control, state->fbdata))
+ return 1;
+
+ return 0;
+ }
+
+ updatecursor(state, &oldpos, 1);
+
+#ifdef DEBUG
+ if(state->pos.row < 0 || state->pos.row >= state->rows ||
+ state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after Ctrl %02x: (%d,%d)\n",
+ control, state->pos.row, state->pos.col);
+ abort();
+ }
+#endif
+
+ return 1;
+}
+
+static int settermprop_bool(VTermState *state, VTermProp prop, int v)
+{
+ VTermValue val = { .boolean = v };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static int settermprop_int(VTermState *state, VTermProp prop, int v)
+{
+ VTermValue val = { .number = v };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static int settermprop_string(VTermState *state, VTermProp prop, VTermStringFragment frag)
+{
+ VTermValue val = { .string = frag };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static void savecursor(VTermState *state, int save)
+{
+ if(save) {
+ state->saved.pos = state->pos;
+ state->saved.mode.cursor_visible = state->mode.cursor_visible;
+ state->saved.mode.cursor_blink = state->mode.cursor_blink;
+ state->saved.mode.cursor_shape = state->mode.cursor_shape;
+
+ vterm_state_savepen(state, 1);
+ }
+ else {
+ VTermPos oldpos = state->pos;
+
+ state->pos = state->saved.pos;
+
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, state->saved.mode.cursor_visible);
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, state->saved.mode.cursor_blink);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, state->saved.mode.cursor_shape);
+
+ vterm_state_savepen(state, 0);
+
+ updatecursor(state, &oldpos, 1);
+ }
+}
+
+static int on_escape(const char *bytes, size_t len, void *user)
+{
+ VTermState *state = user;
+
+ /* Easier to decode this from the first byte, even though the final
+ * byte terminates it
+ */
+ switch(bytes[0]) {
+ case ' ':
+ if(len != 2)
+ return 0;
+
+ switch(bytes[1]) {
+ case 'F': // S7C1T
+ state->vt->mode.ctrl8bit = 0;
+ break;
+
+ case 'G': // S8C1T
+ state->vt->mode.ctrl8bit = 1;
+ break;
+
+ default:
+ return 0;
+ }
+ return 2;
+
+ case '#':
+ if(len != 2)
+ return 0;
+
+ switch(bytes[1]) {
+ case '3': // DECDHL top
+ if(state->mode.leftrightmargin)
+ break;
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_TOP);
+ break;
+
+ case '4': // DECDHL bottom
+ if(state->mode.leftrightmargin)
+ break;
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_BOTTOM);
+ break;
+
+ case '5': // DECSWL
+ if(state->mode.leftrightmargin)
+ break;
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_OFF, DHL_OFF);
+ break;
+
+ case '6': // DECDWL
+ if(state->mode.leftrightmargin)
+ break;
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_OFF);
+ break;
+
+ case '8': // DECALN
+ {
+ VTermPos pos;
+ uint32_t E[] = { 'E', 0 };
+ for(pos.row = 0; pos.row < state->rows; pos.row++)
+ for(pos.col = 0; pos.col < ROWWIDTH(state, pos.row); pos.col++)
+ putglyph(state, E, 1, pos);
+ break;
+ }
+
+ default:
+ return 0;
+ }
+ return 2;
+
+ case '(': case ')': case '*': case '+': // SCS
+ if(len != 2)
+ return 0;
+
+ {
+ int setnum = bytes[0] - 0x28;
+ VTermEncoding *newenc = vterm_lookup_encoding(ENC_SINGLE_94, bytes[1]);
+
+ if(newenc) {
+ state->encoding[setnum].enc = newenc;
+
+ if(newenc->init)
+ (*newenc->init)(newenc, state->encoding[setnum].data);
+ }
+ }
+
+ return 2;
+
+ case '7': // DECSC
+ savecursor(state, 1);
+ return 1;
+
+ case '8': // DECRC
+ savecursor(state, 0);
+ return 1;
+
+ case '<': // Ignored by VT100. Used in VT52 mode to switch up to VT100
+ return 1;
+
+ case '=': // DECKPAM
+ state->mode.keypad = 1;
+ return 1;
+
+ case '>': // DECKPNM
+ state->mode.keypad = 0;
+ return 1;
+
+ case 'c': // RIS - ECMA-48 8.3.105
+ {
+ VTermPos oldpos = state->pos;
+ vterm_state_reset(state, 1);
+ if(state->callbacks && state->callbacks->movecursor)
+ (*state->callbacks->movecursor)(state->pos, oldpos, state->mode.cursor_visible, state->cbdata);
+ return 1;
+ }
+
+ case 'n': // LS2 - ECMA-48 8.3.78
+ state->gl_set = 2;
+ return 1;
+
+ case 'o': // LS3 - ECMA-48 8.3.80
+ state->gl_set = 3;
+ return 1;
+
+ case '~': // LS1R - ECMA-48 8.3.77
+ state->gr_set = 1;
+ return 1;
+
+ case '}': // LS2R - ECMA-48 8.3.79
+ state->gr_set = 2;
+ return 1;
+
+ case '|': // LS3R - ECMA-48 8.3.81
+ state->gr_set = 3;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static void set_mode(VTermState *state, int num, int val)
+{
+ switch(num) {
+ case 4: // IRM - ECMA-48 7.2.10
+ state->mode.insert = val;
+ break;
+
+ case 20: // LNM - ANSI X3.4-1977
+ state->mode.newline = val;
+ break;
+
+ default:
+ DEBUG_LOG("libvterm: Unknown mode %d\n", num);
+ return;
+ }
+}
+
+static void set_dec_mode(VTermState *state, int num, int val)
+{
+ switch(num) {
+ case 1:
+ state->mode.cursor = val;
+ break;
+
+ case 5: // DECSCNM - screen mode
+ settermprop_bool(state, VTERM_PROP_REVERSE, val);
+ break;
+
+ case 6: // DECOM - origin mode
+ {
+ VTermPos oldpos = state->pos;
+ state->mode.origin = val;
+ state->pos.row = state->mode.origin ? state->scrollregion_top : 0;
+ state->pos.col = state->mode.origin ? SCROLLREGION_LEFT(state) : 0;
+ updatecursor(state, &oldpos, 1);
+ }
+ break;
+
+ case 7:
+ state->mode.autowrap = val;
+ break;
+
+ case 12:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, val);
+ break;
+
+ case 25:
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, val);
+ break;
+
+ case 69: // DECVSSM - vertical split screen mode
+ // DECLRMM - left/right margin mode
+ state->mode.leftrightmargin = val;
+ if(val) {
+ // Setting DECVSSM must clear doublewidth/doubleheight state of every line
+ for(int row = 0; row < state->rows; row++)
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ }
+
+ break;
+
+ case 1000:
+ case 1002:
+ case 1003:
+ settermprop_int(state, VTERM_PROP_MOUSE,
+ !val ? VTERM_PROP_MOUSE_NONE :
+ (num == 1000) ? VTERM_PROP_MOUSE_CLICK :
+ (num == 1002) ? VTERM_PROP_MOUSE_DRAG :
+ VTERM_PROP_MOUSE_MOVE);
+ break;
+
+ case 1004:
+ settermprop_bool(state, VTERM_PROP_FOCUSREPORT, val);
+ state->mode.report_focus = val;
+ break;
+
+ case 1005:
+ state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10;
+ break;
+
+ case 1006:
+ state->mouse_protocol = val ? MOUSE_SGR : MOUSE_X10;
+ break;
+
+ case 1015:
+ state->mouse_protocol = val ? MOUSE_RXVT : MOUSE_X10;
+ break;
+
+ case 1047:
+ settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
+ break;
+
+ case 1048:
+ savecursor(state, val);
+ break;
+
+ case 1049:
+ settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
+ savecursor(state, val);
+ break;
+
+ case 2004:
+ state->mode.bracketpaste = val;
+ break;
+
+ default:
+ DEBUG_LOG("libvterm: Unknown DEC mode %d\n", num);
+ return;
+ }
+}
+
+static void request_dec_mode(VTermState *state, int num)
+{
+ int reply;
+
+ switch(num) {
+ case 1:
+ reply = state->mode.cursor;
+ break;
+
+ case 5:
+ reply = state->mode.screen;
+ break;
+
+ case 6:
+ reply = state->mode.origin;
+ break;
+
+ case 7:
+ reply = state->mode.autowrap;
+ break;
+
+ case 12:
+ reply = state->mode.cursor_blink;
+ break;
+
+ case 25:
+ reply = state->mode.cursor_visible;
+ break;
+
+ case 69:
+ reply = state->mode.leftrightmargin;
+ break;
+
+ case 1000:
+ reply = state->mouse_flags == MOUSE_WANT_CLICK;
+ break;
+
+ case 1002:
+ reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_DRAG);
+ break;
+
+ case 1003:
+ reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_MOVE);
+ break;
+
+ case 1004:
+ reply = state->mode.report_focus;
+ break;
+
+ case 1005:
+ reply = state->mouse_protocol == MOUSE_UTF8;
+ break;
+
+ case 1006:
+ reply = state->mouse_protocol == MOUSE_SGR;
+ break;
+
+ case 1015:
+ reply = state->mouse_protocol == MOUSE_RXVT;
+ break;
+
+ case 1047:
+ reply = state->mode.alt_screen;
+ break;
+
+ case 2004:
+ reply = state->mode.bracketpaste;
+ break;
+
+ default:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0);
+ return;
+ }
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, reply ? 1 : 2);
+}
+
+static void request_version_string(VTermState *state)
+{
+ vterm_push_output_sprintf_str(state->vt, C1_DCS, true, ">|libvterm(%d.%d)",
+ VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR);
+}
+
+static int on_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
+{
+ VTermState *state = user;
+ int leader_byte = 0;
+ int intermed_byte = 0;
+ int cancel_phantom = 1;
+
+ if(leader && leader[0]) {
+ if(leader[1]) // longer than 1 char
+ return 0;
+
+ switch(leader[0]) {
+ case '?':
+ case '>':
+ leader_byte = leader[0];
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ if(intermed && intermed[0]) {
+ if(intermed[1]) // longer than 1 char
+ return 0;
+
+ switch(intermed[0]) {
+ case ' ':
+ case '!':
+ case '"':
+ case '$':
+ case '\'':
+ intermed_byte = intermed[0];
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ VTermPos oldpos = state->pos;
+
+ // Some temporaries for later code
+ int count, val;
+ int row, col;
+ VTermRect rect;
+ int selective;
+
+#define LBOUND(v,min) if((v) < (min)) (v) = (min)
+#define UBOUND(v,max) if((v) > (max)) (v) = (max)
+
+#define LEADER(l,b) ((l << 8) | b)
+#define INTERMED(i,b) ((i << 16) | b)
+
+ switch(intermed_byte << 16 | leader_byte << 8 | command) {
+ case 0x40: // ICH - ECMA-48 8.3.64
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ if(state->mode.leftrightmargin)
+ rect.end_col = SCROLLREGION_RIGHT(state);
+ else
+ rect.end_col = THISROWWIDTH(state);
+
+ scroll(state, rect, 0, -count);
+
+ break;
+
+ case 0x41: // CUU - ECMA-48 8.3.22
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x42: // CUD - ECMA-48 8.3.19
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x43: // CUF - ECMA-48 8.3.20
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x44: // CUB - ECMA-48 8.3.18
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x45: // CNL - ECMA-48 8.3.12
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col = 0;
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x46: // CPL - ECMA-48 8.3.13
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col = 0;
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x47: // CHA - ECMA-48 8.3.9
+ val = CSI_ARG_OR(args[0], 1);
+ state->pos.col = val-1;
+ state->at_phantom = 0;
+ break;
+
+ case 0x48: // CUP - ECMA-48 8.3.21
+ row = CSI_ARG_OR(args[0], 1);
+ col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
+ // zero-based
+ state->pos.row = row-1;
+ state->pos.col = col-1;
+ if(state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+ state->at_phantom = 0;
+ break;
+
+ case 0x49: // CHT - ECMA-48 8.3.10
+ count = CSI_ARG_COUNT(args[0]);
+ tab(state, count, +1);
+ break;
+
+ case 0x4a: // ED - ECMA-48 8.3.39
+ case LEADER('?', 0x4a): // DECSED - Selective Erase in Display
+ selective = (leader_byte == '?');
+ switch(CSI_ARG(args[0])) {
+ case CSI_ARG_MISSING:
+ case 0:
+ rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col; rect.end_col = state->cols;
+ if(rect.end_col > rect.start_col)
+ erase(state, rect, selective);
+
+ rect.start_row = state->pos.row + 1; rect.end_row = state->rows;
+ rect.start_col = 0;
+ for(int row = rect.start_row; row < rect.end_row; row++)
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ if(rect.end_row > rect.start_row)
+ erase(state, rect, selective);
+ break;
+
+ case 1:
+ rect.start_row = 0; rect.end_row = state->pos.row;
+ rect.start_col = 0; rect.end_col = state->cols;
+ for(int row = rect.start_row; row < rect.end_row; row++)
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ if(rect.end_col > rect.start_col)
+ erase(state, rect, selective);
+
+ rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
+ rect.end_col = state->pos.col + 1;
+ if(rect.end_row > rect.start_row)
+ erase(state, rect, selective);
+ break;
+
+ case 2:
+ rect.start_row = 0; rect.end_row = state->rows;
+ rect.start_col = 0; rect.end_col = state->cols;
+ for(int row = rect.start_row; row < rect.end_row; row++)
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ erase(state, rect, selective);
+ break;
+
+ case 3:
+ if(state->callbacks && state->callbacks->sb_clear)
+ if((*state->callbacks->sb_clear)(state->cbdata))
+ return 1;
+ break;
+ }
+ break;
+
+ case 0x4b: // EL - ECMA-48 8.3.41
+ case LEADER('?', 0x4b): // DECSEL - Selective Erase in Line
+ selective = (leader_byte == '?');
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+
+ switch(CSI_ARG(args[0])) {
+ case CSI_ARG_MISSING:
+ case 0:
+ rect.start_col = state->pos.col; rect.end_col = THISROWWIDTH(state); break;
+ case 1:
+ rect.start_col = 0; rect.end_col = state->pos.col + 1; break;
+ case 2:
+ rect.start_col = 0; rect.end_col = THISROWWIDTH(state); break;
+ default:
+ return 0;
+ }
+
+ if(rect.end_col > rect.start_col)
+ erase(state, rect, selective);
+
+ break;
+
+ case 0x4c: // IL - ECMA-48 8.3.67
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->pos.row;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, -count, 0);
+
+ break;
+
+ case 0x4d: // DL - ECMA-48 8.3.32
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->pos.row;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, count, 0);
+
+ break;
+
+ case 0x50: // DCH - ECMA-48 8.3.26
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ if(state->mode.leftrightmargin)
+ rect.end_col = SCROLLREGION_RIGHT(state);
+ else
+ rect.end_col = THISROWWIDTH(state);
+
+ scroll(state, rect, 0, count);
+
+ break;
+
+ case 0x53: // SU - ECMA-48 8.3.147
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, count, 0);
+
+ break;
+
+ case 0x54: // SD - ECMA-48 8.3.113
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, -count, 0);
+
+ break;
+
+ case 0x58: // ECH - ECMA-48 8.3.38
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ rect.end_col = state->pos.col + count;
+ UBOUND(rect.end_col, THISROWWIDTH(state));
+
+ erase(state, rect, 0);
+ break;
+
+ case 0x5a: // CBT - ECMA-48 8.3.7
+ count = CSI_ARG_COUNT(args[0]);
+ tab(state, count, -1);
+ break;
+
+ case 0x60: // HPA - ECMA-48 8.3.57
+ col = CSI_ARG_OR(args[0], 1);
+ state->pos.col = col-1;
+ state->at_phantom = 0;
+ break;
+
+ case 0x61: // HPR - ECMA-48 8.3.59
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x62: { // REP - ECMA-48 8.3.103
+ const int row_width = THISROWWIDTH(state);
+ count = CSI_ARG_COUNT(args[0]);
+ col = state->pos.col + count;
+ UBOUND(col, row_width);
+ while (state->pos.col < col) {
+ putglyph(state, state->combine_chars, state->combine_width, state->pos);
+ state->pos.col += state->combine_width;
+ }
+ if (state->pos.col + state->combine_width >= row_width) {
+ if (state->mode.autowrap) {
+ state->at_phantom = 1;
+ cancel_phantom = 0;
+ }
+ }
+ break;
+ }
+
+ case 0x63: // DA - ECMA-48 8.3.24
+ val = CSI_ARG_OR(args[0], 0);
+ if(val == 0)
+ // DEC VT100 response
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?1;2c");
+ break;
+
+ case LEADER('>', 0x63): // DEC secondary Device Attributes
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, ">%d;%d;%dc", 0, 100, 0);
+ break;
+
+ case 0x64: // VPA - ECMA-48 8.3.158
+ row = CSI_ARG_OR(args[0], 1);
+ state->pos.row = row-1;
+ if(state->mode.origin)
+ state->pos.row += state->scrollregion_top;
+ state->at_phantom = 0;
+ break;
+
+ case 0x65: // VPR - ECMA-48 8.3.160
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x66: // HVP - ECMA-48 8.3.63
+ row = CSI_ARG_OR(args[0], 1);
+ col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
+ // zero-based
+ state->pos.row = row-1;
+ state->pos.col = col-1;
+ if(state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+ state->at_phantom = 0;
+ break;
+
+ case 0x67: // TBC - ECMA-48 8.3.154
+ val = CSI_ARG_OR(args[0], 0);
+
+ switch(val) {
+ case 0:
+ clear_col_tabstop(state, state->pos.col);
+ break;
+ case 3:
+ case 5:
+ for(col = 0; col < state->cols; col++)
+ clear_col_tabstop(state, col);
+ break;
+ case 1:
+ case 2:
+ case 4:
+ break;
+ /* TODO: 1, 2 and 4 aren't meaningful yet without line tab stops */
+ default:
+ return 0;
+ }
+ break;
+
+ case 0x68: // SM - ECMA-48 8.3.125
+ if(!CSI_ARG_IS_MISSING(args[0]))
+ set_mode(state, CSI_ARG(args[0]), 1);
+ break;
+
+ case LEADER('?', 0x68): // DEC private mode set
+ for(int i = 0; i < argcount; i++) {
+ if(!CSI_ARG_IS_MISSING(args[i]))
+ set_dec_mode(state, CSI_ARG(args[i]), 1);
+ }
+ break;
+
+ case 0x6a: // HPB - ECMA-48 8.3.58
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x6b: // VPB - ECMA-48 8.3.159
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x6c: // RM - ECMA-48 8.3.106
+ if(!CSI_ARG_IS_MISSING(args[0]))
+ set_mode(state, CSI_ARG(args[0]), 0);
+ break;
+
+ case LEADER('?', 0x6c): // DEC private mode reset
+ for(int i = 0; i < argcount; i++) {
+ if(!CSI_ARG_IS_MISSING(args[i]))
+ set_dec_mode(state, CSI_ARG(args[i]), 0);
+ }
+ break;
+
+ case 0x6d: // SGR - ECMA-48 8.3.117
+ vterm_state_setpen(state, args, argcount);
+ break;
+
+ case LEADER('?', 0x6d): // DECSGR
+ /* No actual DEC terminal recognised these, but some printers did. These
+ * are alternative ways to request subscript/superscript/off
+ */
+ for(int argi = 0; argi < argcount; argi++) {
+ long arg;
+ switch(arg = CSI_ARG(args[argi])) {
+ case 4: // Superscript on
+ arg = 73;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ case 5: // Subscript on
+ arg = 74;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ case 24: // Super+subscript off
+ arg = 75;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ }
+ }
+ break;
+
+ case 0x6e: // DSR - ECMA-48 8.3.35
+ case LEADER('?', 0x6e): // DECDSR
+ val = CSI_ARG_OR(args[0], 0);
+
+ {
+ char *qmark = (leader_byte == '?') ? "?" : "";
+
+ switch(val) {
+ case 0: case 1: case 2: case 3: case 4:
+ // ignore - these are replies
+ break;
+ case 5:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s0n", qmark);
+ break;
+ case 6: // CPR - cursor position report
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1, state->pos.col + 1);
+ break;
+ }
+ }
+ break;
+
+
+ case INTERMED('!', 0x70): // DECSTR - DEC soft terminal reset
+ vterm_state_reset(state, 0);
+ break;
+
+ case LEADER('?', INTERMED('$', 0x70)):
+ request_dec_mode(state, CSI_ARG(args[0]));
+ break;
+
+ case LEADER('>', 0x71): // XTVERSION - xterm query version string
+ request_version_string(state);
+ break;
+
+ case INTERMED(' ', 0x71): // DECSCUSR - DEC set cursor shape
+ val = CSI_ARG_OR(args[0], 1);
+
+ switch(val) {
+ case 0: case 1:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+ break;
+ case 2:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+ break;
+ case 3:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
+ break;
+ case 4:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
+ break;
+ case 5:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
+ break;
+ case 6:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
+ break;
+ }
+
+ break;
+
+ case INTERMED('"', 0x71): // DECSCA - DEC select character protection attribute
+ val = CSI_ARG_OR(args[0], 0);
+
+ switch(val) {
+ case 0: case 2:
+ state->protected_cell = 0;
+ break;
+ case 1:
+ state->protected_cell = 1;
+ break;
+ }
+
+ break;
+
+ case 0x72: // DECSTBM - DEC custom
+ state->scrollregion_top = CSI_ARG_OR(args[0], 1) - 1;
+ state->scrollregion_bottom = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
+ LBOUND(state->scrollregion_top, 0);
+ UBOUND(state->scrollregion_top, state->rows);
+ LBOUND(state->scrollregion_bottom, -1);
+ if(state->scrollregion_top == 0 && state->scrollregion_bottom == state->rows)
+ state->scrollregion_bottom = -1;
+ else
+ UBOUND(state->scrollregion_bottom, state->rows);
+
+ if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
+ // Invalid
+ state->scrollregion_top = 0;
+ state->scrollregion_bottom = -1;
+ }
+
+ // Setting the scrolling region restores the cursor to the home position
+ state->pos.row = 0;
+ state->pos.col = 0;
+ if(state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+
+ break;
+
+ case 0x73: // DECSLRM - DEC custom
+ // Always allow setting these margins, just they won't take effect without DECVSSM
+ state->scrollregion_left = CSI_ARG_OR(args[0], 1) - 1;
+ state->scrollregion_right = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
+ LBOUND(state->scrollregion_left, 0);
+ UBOUND(state->scrollregion_left, state->cols);
+ LBOUND(state->scrollregion_right, -1);
+ if(state->scrollregion_left == 0 && state->scrollregion_right == state->cols)
+ state->scrollregion_right = -1;
+ else
+ UBOUND(state->scrollregion_right, state->cols);
+
+ if(state->scrollregion_right > -1 &&
+ state->scrollregion_right <= state->scrollregion_left) {
+ // Invalid
+ state->scrollregion_left = 0;
+ state->scrollregion_right = -1;
+ }
+
+ // Setting the scrolling region restores the cursor to the home position
+ state->pos.row = 0;
+ state->pos.col = 0;
+ if(state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+
+ break;
+
+ case INTERMED('\'', 0x7D): // DECIC
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = state->pos.col;
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, 0, -count);
+
+ break;
+
+ case INTERMED('\'', 0x7E): // DECDC
+ count = CSI_ARG_COUNT(args[0]);
+
+ if(!is_cursor_in_scrollregion(state))
+ break;
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = state->pos.col;
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, 0, count);
+
+ break;
+
+ default:
+ if(state->fallbacks && state->fallbacks->csi)
+ if((*state->fallbacks->csi)(leader, args, argcount, intermed, command, state->fbdata))
+ return 1;
+
+ return 0;
+ }
+
+ if(state->mode.origin) {
+ LBOUND(state->pos.row, state->scrollregion_top);
+ UBOUND(state->pos.row, SCROLLREGION_BOTTOM(state)-1);
+ LBOUND(state->pos.col, SCROLLREGION_LEFT(state));
+ UBOUND(state->pos.col, SCROLLREGION_RIGHT(state)-1);
+ }
+ else {
+ LBOUND(state->pos.row, 0);
+ UBOUND(state->pos.row, state->rows-1);
+ LBOUND(state->pos.col, 0);
+ UBOUND(state->pos.col, THISROWWIDTH(state)-1);
+ }
+
+ updatecursor(state, &oldpos, cancel_phantom);
+
+#ifdef DEBUG
+ if(state->pos.row < 0 || state->pos.row >= state->rows ||
+ state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after CSI %c: (%d,%d)\n",
+ command, state->pos.row, state->pos.col);
+ abort();
+ }
+
+ if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
+ fprintf(stderr, "Scroll region height out of bounds after CSI %c: %d <= %d\n",
+ command, SCROLLREGION_BOTTOM(state), state->scrollregion_top);
+ abort();
+ }
+
+ if(SCROLLREGION_RIGHT(state) <= SCROLLREGION_LEFT(state)) {
+ fprintf(stderr, "Scroll region width out of bounds after CSI %c: %d <= %d\n",
+ command, SCROLLREGION_RIGHT(state), SCROLLREGION_LEFT(state));
+ abort();
+ }
+#endif
+
+ return 1;
+}
+
+static char base64_one(uint8_t b)
+{
+ if(b < 26)
+ return 'A' + b;
+ else if(b < 52)
+ return 'a' + b - 26;
+ else if(b < 62)
+ return '0' + b - 52;
+ else if(b == 62)
+ return '+';
+ else if(b == 63)
+ return '/';
+ return 0;
+}
+
+static uint8_t unbase64one(char c)
+{
+ if(c >= 'A' && c <= 'Z')
+ return c - 'A';
+ else if(c >= 'a' && c <= 'z')
+ return c - 'a' + 26;
+ else if(c >= '0' && c <= '9')
+ return c - '0' + 52;
+ else if(c == '+')
+ return 62;
+ else if(c == '/')
+ return 63;
+
+ return 0xFF;
+}
+
+static void osc_selection(VTermState *state, VTermStringFragment frag)
+{
+ if(frag.initial) {
+ state->tmp.selection.mask = 0;
+ state->tmp.selection.state = SELECTION_INITIAL;
+ }
+
+ while(!state->tmp.selection.state && frag.len) {
+ /* Parse selection parameter */
+ switch(frag.str[0]) {
+ case 'c':
+ state->tmp.selection.mask |= VTERM_SELECTION_CLIPBOARD;
+ break;
+ case 'p':
+ state->tmp.selection.mask |= VTERM_SELECTION_PRIMARY;
+ break;
+ case 'q':
+ state->tmp.selection.mask |= VTERM_SELECTION_SECONDARY;
+ break;
+ case 's':
+ state->tmp.selection.mask |= VTERM_SELECTION_SELECT;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ state->tmp.selection.mask |= (VTERM_SELECTION_CUT0 << (frag.str[0] - '0'));
+ break;
+
+ case ';':
+ state->tmp.selection.state = SELECTION_SELECTED;
+ if(!state->tmp.selection.mask)
+ state->tmp.selection.mask = VTERM_SELECTION_SELECT|VTERM_SELECTION_CUT0;
+ break;
+ }
+
+ frag.str++;
+ frag.len--;
+ }
+
+ if(!frag.len) {
+ /* Clear selection if we're already finished but didn't do anything */
+ if(frag.final && state->selection.callbacks->set) {
+ (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
+ .str = NULL,
+ .len = 0,
+ .initial = state->tmp.selection.state != SELECTION_SET,
+ .final = true,
+ }, state->selection.user);
+ }
+ return;
+ }
+
+ if(state->tmp.selection.state == SELECTION_SELECTED) {
+ if(frag.str[0] == '?') {
+ state->tmp.selection.state = SELECTION_QUERY;
+ }
+ else {
+ state->tmp.selection.state = SELECTION_SET_INITIAL;
+ state->tmp.selection.recvpartial = 0;
+ }
+ }
+
+ if(state->tmp.selection.state == SELECTION_QUERY) {
+ if(state->selection.callbacks->query)
+ (*state->selection.callbacks->query)(state->tmp.selection.mask, state->selection.user);
+ return;
+ }
+
+ if(state->tmp.selection.state == SELECTION_INVALID)
+ return;
+
+ if(state->selection.callbacks->set) {
+ size_t bufcur = 0;
+ char *buffer = state->selection.buffer;
+
+ uint32_t x = 0; /* Current decoding value */
+ int n = 0; /* Number of sextets consumed */
+
+ if(state->tmp.selection.recvpartial) {
+ n = state->tmp.selection.recvpartial >> 24;
+ x = state->tmp.selection.recvpartial & 0x03FFFF; /* could be up to 18 bits of state in here */
+
+ state->tmp.selection.recvpartial = 0;
+ }
+
+ while((state->selection.buflen - bufcur) >= 3 && frag.len) {
+ if(frag.str[0] == '=') {
+ if(n == 2) {
+ buffer[0] = (x >> 4) & 0xFF;
+ buffer += 1, bufcur += 1;
+ }
+ if(n == 3) {
+ buffer[0] = (x >> 10) & 0xFF;
+ buffer[1] = (x >> 2) & 0xFF;
+ buffer += 2, bufcur += 2;
+ }
+
+ while(frag.len && frag.str[0] == '=')
+ frag.str++, frag.len--;
+
+ n = 0;
+ }
+ else {
+ uint8_t b = unbase64one(frag.str[0]);
+ if(b == 0xFF) {
+ DEBUG_LOG("base64decode bad input %02X\n", (uint8_t)frag.str[0]);
+
+ state->tmp.selection.state = SELECTION_INVALID;
+ if(state->selection.callbacks->set) {
+ (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
+ .str = NULL,
+ .len = 0,
+ .initial = true,
+ .final = true,
+ }, state->selection.user);
+ }
+ break;
+ }
+
+ x = (x << 6) | b;
+ n++;
+ frag.str++, frag.len--;
+
+ if(n == 4) {
+ buffer[0] = (x >> 16) & 0xFF;
+ buffer[1] = (x >> 8) & 0xFF;
+ buffer[2] = (x >> 0) & 0xFF;
+
+ buffer += 3, bufcur += 3;
+ x = 0;
+ n = 0;
+ }
+ }
+
+ if(!frag.len || (state->selection.buflen - bufcur) < 3) {
+ if(bufcur) {
+ (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
+ .str = state->selection.buffer,
+ .len = bufcur,
+ .initial = state->tmp.selection.state == SELECTION_SET_INITIAL,
+ .final = frag.final && !frag.len,
+ }, state->selection.user);
+ state->tmp.selection.state = SELECTION_SET;
+ }
+
+ buffer = state->selection.buffer;
+ bufcur = 0;
+ }
+ }
+
+ if(n)
+ state->tmp.selection.recvpartial = (n << 24) | x;
+ }
+}
+
+static int on_osc(int command, VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ switch(command) {
+ case 0:
+ settermprop_string(state, VTERM_PROP_ICONNAME, frag);
+ settermprop_string(state, VTERM_PROP_TITLE, frag);
+ return 1;
+
+ case 1:
+ settermprop_string(state, VTERM_PROP_ICONNAME, frag);
+ return 1;
+
+ case 2:
+ settermprop_string(state, VTERM_PROP_TITLE, frag);
+ return 1;
+
+ case 52:
+ if(state->selection.callbacks)
+ osc_selection(state, frag);
+
+ return 1;
+
+ default:
+ if(state->fallbacks && state->fallbacks->osc)
+ if((*state->fallbacks->osc)(command, frag, state->fbdata))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void request_status_string(VTermState *state, VTermStringFragment frag)
+{
+ VTerm *vt = state->vt;
+
+ char *tmp = state->tmp.decrqss;
+
+ if(frag.initial)
+ tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
+
+ int i = 0;
+ while(i < sizeof(state->tmp.decrqss)-1 && tmp[i])
+ i++;
+ while(i < sizeof(state->tmp.decrqss)-1 && frag.len--)
+ tmp[i++] = (frag.str++)[0];
+ tmp[i] = 0;
+
+ if(!frag.final)
+ return;
+
+ switch(tmp[0] | tmp[1]<<8 | tmp[2]<<16) {
+ case 'm': {
+ // Query SGR
+ long args[20];
+ int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
+ size_t cur = 0;
+
+ cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
+ if(cur >= vt->tmpbuffer_len)
+ return;
+
+ for(int argi = 0; argi < argc; argi++) {
+ cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ argi == argc - 1 ? "%ld" :
+ CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" :
+ "%ld;",
+ CSI_ARG(args[argi]));
+ if(cur >= vt->tmpbuffer_len)
+ return;
+ }
+
+ cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
+ if(cur >= vt->tmpbuffer_len)
+ return;
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+ return;
+ }
+
+ case 'r':
+ // Query DECSTBM
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state));
+ return;
+
+ case 's':
+ // Query DECSLRM
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state));
+ return;
+
+ case ' '|('q'<<8): {
+ // Query DECSCUSR
+ int reply;
+ switch(state->mode.cursor_shape) {
+ case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break;
+ case VTERM_PROP_CURSORSHAPE_UNDERLINE: reply = 4; break;
+ case VTERM_PROP_CURSORSHAPE_BAR_LEFT: reply = 6; break;
+ }
+ if(state->mode.cursor_blink)
+ reply--;
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d q", reply);
+ return;
+ }
+
+ case '\"'|('q'<<8):
+ // Query DECSCA
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d\"q", state->protected_cell ? 1 : 2);
+ return;
+ }
+
+ vterm_push_output_sprintf_str(state->vt, C1_DCS, true, "0$r");
+}
+
+static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if(commandlen == 2 && strneq(command, "$q", 2)) {
+ request_status_string(state, frag);
+ return 1;
+ }
+ else if(state->fallbacks && state->fallbacks->dcs)
+ if((*state->fallbacks->dcs)(command, commandlen, frag, state->fbdata))
+ return 1;
+
+ DEBUG_LOG("libvterm: Unhandled DCS %.*s\n", (int)commandlen, command);
+ return 0;
+}
+
+static int on_apc(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if(state->fallbacks && state->fallbacks->apc)
+ if((*state->fallbacks->apc)(frag, state->fbdata))
+ return 1;
+
+ /* No DEBUG_LOG because all APCs are unhandled */
+ return 0;
+}
+
+static int on_pm(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if(state->fallbacks && state->fallbacks->pm)
+ if((*state->fallbacks->pm)(frag, state->fbdata))
+ return 1;
+
+ /* No DEBUG_LOG because all PMs are unhandled */
+ return 0;
+}
+
+static int on_sos(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if(state->fallbacks && state->fallbacks->sos)
+ if((*state->fallbacks->sos)(frag, state->fbdata))
+ return 1;
+
+ /* No DEBUG_LOG because all SOSs are unhandled */
+ return 0;
+}
+
+static int on_resize(int rows, int cols, void *user)
+{
+ VTermState *state = user;
+ VTermPos oldpos = state->pos;
+
+ if(cols != state->cols) {
+ unsigned char *newtabstops = vterm_allocator_malloc(state->vt, (cols + 7) / 8);
+
+ /* TODO: This can all be done much more efficiently bytewise */
+ int col;
+ for(col = 0; col < state->cols && col < cols; col++) {
+ unsigned char mask = 1 << (col & 7);
+ if(state->tabstops[col >> 3] & mask)
+ newtabstops[col >> 3] |= mask;
+ else
+ newtabstops[col >> 3] &= ~mask;
+ }
+
+ for( ; col < cols; col++) {
+ unsigned char mask = 1 << (col & 7);
+ if(col % 8 == 0)
+ newtabstops[col >> 3] |= mask;
+ else
+ newtabstops[col >> 3] &= ~mask;
+ }
+
+ vterm_allocator_free(state->vt, state->tabstops);
+ state->tabstops = newtabstops;
+ }
+
+ state->rows = rows;
+ state->cols = cols;
+
+ if(state->scrollregion_bottom > -1)
+ UBOUND(state->scrollregion_bottom, state->rows);
+ if(state->scrollregion_right > -1)
+ UBOUND(state->scrollregion_right, state->cols);
+
+ VTermStateFields fields = {
+ .pos = state->pos,
+ .lineinfos = { [0] = state->lineinfos[0], [1] = state->lineinfos[1] },
+ };
+
+ if(state->callbacks && state->callbacks->resize) {
+ (*state->callbacks->resize)(rows, cols, &fields, state->cbdata);
+ state->pos = fields.pos;
+
+ state->lineinfos[0] = fields.lineinfos[0];
+ state->lineinfos[1] = fields.lineinfos[1];
+ }
+ else {
+ if(rows != state->rows) {
+ for(int bufidx = BUFIDX_PRIMARY; bufidx <= BUFIDX_ALTSCREEN; bufidx++) {
+ VTermLineInfo *oldlineinfo = state->lineinfos[bufidx];
+ if(!oldlineinfo)
+ continue;
+
+ VTermLineInfo *newlineinfo = vterm_allocator_malloc(state->vt, rows * sizeof(VTermLineInfo));
+
+ int row;
+ for(row = 0; row < state->rows && row < rows; row++) {
+ newlineinfo[row] = oldlineinfo[row];
+ }
+
+ for( ; row < rows; row++) {
+ newlineinfo[row] = (VTermLineInfo){
+ .doublewidth = 0,
+ };
+ }
+
+ vterm_allocator_free(state->vt, state->lineinfos[bufidx]);
+ state->lineinfos[bufidx] = newlineinfo;
+ }
+ }
+ }
+
+ state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
+
+ if(state->at_phantom && state->pos.col < cols-1) {
+ state->at_phantom = 0;
+ state->pos.col++;
+ }
+
+ if(state->pos.row < 0)
+ state->pos.row = 0;
+ if(state->pos.row >= rows)
+ state->pos.row = rows - 1;
+ if(state->pos.col < 0)
+ state->pos.col = 0;
+ if(state->pos.col >= cols)
+ state->pos.col = cols - 1;
+
+ updatecursor(state, &oldpos, 1);
+
+ return 1;
+}
+
+static const VTermParserCallbacks parser_callbacks = {
+ .text = on_text,
+ .control = on_control,
+ .escape = on_escape,
+ .csi = on_csi,
+ .osc = on_osc,
+ .dcs = on_dcs,
+ .apc = on_apc,
+ .pm = on_pm,
+ .sos = on_sos,
+ .resize = on_resize,
+};
+
+VTermState *vterm_obtain_state(VTerm *vt)
+{
+ if(vt->state)
+ return vt->state;
+
+ VTermState *state = vterm_state_new(vt);
+ vt->state = state;
+
+ vterm_parser_set_callbacks(vt, &parser_callbacks, state);
+
+ return state;
+}
+
+void vterm_state_reset(VTermState *state, int hard)
+{
+ state->scrollregion_top = 0;
+ state->scrollregion_bottom = -1;
+ state->scrollregion_left = 0;
+ state->scrollregion_right = -1;
+
+ state->mode.keypad = 0;
+ state->mode.cursor = 0;
+ state->mode.autowrap = 1;
+ state->mode.insert = 0;
+ state->mode.newline = 0;
+ state->mode.alt_screen = 0;
+ state->mode.origin = 0;
+ state->mode.leftrightmargin = 0;
+ state->mode.bracketpaste = 0;
+ state->mode.report_focus = 0;
+
+ state->mouse_flags = 0;
+
+ state->vt->mode.ctrl8bit = 0;
+
+ for(int col = 0; col < state->cols; col++)
+ if(col % 8 == 0)
+ set_col_tabstop(state, col);
+ else
+ clear_col_tabstop(state, col);
+
+ for(int row = 0; row < state->rows; row++)
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+
+ if(state->callbacks && state->callbacks->initpen)
+ (*state->callbacks->initpen)(state->cbdata);
+
+ vterm_state_resetpen(state);
+
+ VTermEncoding *default_enc = state->vt->mode.utf8 ?
+ vterm_lookup_encoding(ENC_UTF8, 'u') :
+ vterm_lookup_encoding(ENC_SINGLE_94, 'B');
+
+ for(int i = 0; i < 4; i++) {
+ state->encoding[i].enc = default_enc;
+ if(default_enc->init)
+ (*default_enc->init)(default_enc, state->encoding[i].data);
+ }
+
+ state->gl_set = 0;
+ state->gr_set = 1;
+ state->gsingle_set = 0;
+
+ state->protected_cell = 0;
+
+ // Initialise the props
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, 1);
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+
+ if(hard) {
+ state->pos.row = 0;
+ state->pos.col = 0;
+ state->at_phantom = 0;
+
+ VTermRect rect = { 0, state->rows, 0, state->cols };
+ erase(state, rect, 0);
+ }
+}
+
+void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos)
+{
+ *cursorpos = state->pos;
+}
+
+void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user)
+{
+ if(callbacks) {
+ state->callbacks = callbacks;
+ state->cbdata = user;
+
+ if(state->callbacks && state->callbacks->initpen)
+ (*state->callbacks->initpen)(state->cbdata);
+ }
+ else {
+ state->callbacks = NULL;
+ state->cbdata = NULL;
+ }
+}
+
+void *vterm_state_get_cbdata(VTermState *state)
+{
+ return state->cbdata;
+}
+
+void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user)
+{
+ if(fallbacks) {
+ state->fallbacks = fallbacks;
+ state->fbdata = user;
+ }
+ else {
+ state->fallbacks = NULL;
+ state->fbdata = NULL;
+ }
+}
+
+void *vterm_state_get_unrecognised_fbdata(VTermState *state)
+{
+ return state->fbdata;
+}
+
+int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val)
+{
+ /* Only store the new value of the property if usercode said it was happy.
+ * This is especially important for altscreen switching */
+ if(state->callbacks && state->callbacks->settermprop)
+ if(!(*state->callbacks->settermprop)(prop, val, state->cbdata))
+ return 0;
+
+ switch(prop) {
+ case VTERM_PROP_TITLE:
+ case VTERM_PROP_ICONNAME:
+ // we don't store these, just transparently pass through
+ return 1;
+ case VTERM_PROP_CURSORVISIBLE:
+ state->mode.cursor_visible = val->boolean;
+ return 1;
+ case VTERM_PROP_CURSORBLINK:
+ state->mode.cursor_blink = val->boolean;
+ return 1;
+ case VTERM_PROP_CURSORSHAPE:
+ state->mode.cursor_shape = val->number;
+ return 1;
+ case VTERM_PROP_REVERSE:
+ state->mode.screen = val->boolean;
+ return 1;
+ case VTERM_PROP_ALTSCREEN:
+ state->mode.alt_screen = val->boolean;
+ state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
+ if(state->mode.alt_screen) {
+ VTermRect rect = {
+ .start_row = 0,
+ .start_col = 0,
+ .end_row = state->rows,
+ .end_col = state->cols,
+ };
+ erase(state, rect, 0);
+ }
+ return 1;
+ case VTERM_PROP_MOUSE:
+ state->mouse_flags = 0;
+ if(val->number)
+ state->mouse_flags |= MOUSE_WANT_CLICK;
+ if(val->number == VTERM_PROP_MOUSE_DRAG)
+ state->mouse_flags |= MOUSE_WANT_DRAG;
+ if(val->number == VTERM_PROP_MOUSE_MOVE)
+ state->mouse_flags |= MOUSE_WANT_MOVE;
+ return 1;
+ case VTERM_PROP_FOCUSREPORT:
+ state->mode.report_focus = val->boolean;
+ return 1;
+
+ case VTERM_N_PROPS:
+ return 0;
+ }
+
+ return 0;
+}
+
+void vterm_state_focus_in(VTermState *state)
+{
+ if(state->mode.report_focus)
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "I");
+}
+
+void vterm_state_focus_out(VTermState *state)
+{
+ if(state->mode.report_focus)
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "O");
+}
+
+const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row)
+{
+ return state->lineinfo + row;
+}
+
+void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user,
+ char *buffer, size_t buflen)
+{
+ if(buflen && !buffer)
+ buffer = vterm_allocator_malloc(state->vt, buflen);
+
+ state->selection.callbacks = callbacks;
+ state->selection.user = user;
+ state->selection.buffer = buffer;
+ state->selection.buflen = buflen;
+}
+
+void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag)
+{
+ VTerm *vt = state->vt;
+
+ if(frag.initial) {
+ /* TODO: support sending more than one mask bit */
+ const static char selection_chars[] = "cpqs";
+ int idx;
+ for(idx = 0; idx < 4; idx++)
+ if(mask & (1 << idx))
+ break;
+
+ vterm_push_output_sprintf_str(vt, C1_OSC, false, "52;%c;", selection_chars[idx]);
+
+ state->tmp.selection.sendpartial = 0;
+ }
+
+ if(frag.len) {
+ size_t bufcur = 0;
+ char *buffer = state->selection.buffer;
+
+ uint32_t x = 0;
+ int n = 0;
+
+ if(state->tmp.selection.sendpartial) {
+ n = state->tmp.selection.sendpartial >> 24;
+ x = state->tmp.selection.sendpartial & 0xFFFFFF;
+
+ state->tmp.selection.sendpartial = 0;
+ }
+
+ while((state->selection.buflen - bufcur) >= 4 && frag.len) {
+ x = (x << 8) | frag.str[0];
+ n++;
+ frag.str++, frag.len--;
+
+ if(n == 3) {
+ buffer[0] = base64_one((x >> 18) & 0x3F);
+ buffer[1] = base64_one((x >> 12) & 0x3F);
+ buffer[2] = base64_one((x >> 6) & 0x3F);
+ buffer[3] = base64_one((x >> 0) & 0x3F);
+
+ buffer += 4, bufcur += 4;
+ x = 0;
+ n = 0;
+ }
+
+ if(!frag.len || (state->selection.buflen - bufcur) < 4) {
+ if(bufcur)
+ vterm_push_output_bytes(vt, state->selection.buffer, bufcur);
+
+ buffer = state->selection.buffer;
+ bufcur = 0;
+ }
+ }
+
+ if(n)
+ state->tmp.selection.sendpartial = (n << 24) | x;
+ }
+
+ if(frag.final) {
+ if(state->tmp.selection.sendpartial) {
+ int n = state->tmp.selection.sendpartial >> 24;
+ uint32_t x = state->tmp.selection.sendpartial & 0xFFFFFF;
+ char *buffer = state->selection.buffer;
+
+ /* n is either 1 or 2 now */
+ x <<= (n == 1) ? 16 : 8;
+
+ buffer[0] = base64_one((x >> 18) & 0x3F);
+ buffer[1] = base64_one((x >> 12) & 0x3F);
+ buffer[2] = (n == 1) ? '=' : base64_one((x >> 6) & 0x3F);
+ buffer[3] = '=';
+
+ vterm_push_output_sprintf_str(vt, 0, true, "%.*s", 4, buffer);
+ }
+ else
+ vterm_push_output_sprintf_str(vt, 0, true, "");
+ }
+}
diff --git a/src/libs/3rdparty/libvterm/src/unicode.c b/src/libs/3rdparty/libvterm/src/unicode.c
new file mode 100644
index 0000000000..269244ff6b
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/unicode.c
@@ -0,0 +1,313 @@
+#include "vterm_internal.h"
+
+// ### The following from http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+// With modifications:
+// made functions static
+// moved 'combining' table to file scope, so other functions can see it
+// ###################################################################
+
+/*
+ * This is an implementation of wcwidth() and wcswidth() (defined in
+ * IEEE Std 1002.1-2001) for Unicode.
+ *
+ * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
+ * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
+ *
+ * In fixed-width output devices, Latin characters all occupy a single
+ * "cell" position of equal width, whereas ideographic CJK characters
+ * occupy two such cells. Interoperability between terminal-line
+ * applications and (teletype-style) character terminals using the
+ * UTF-8 encoding requires agreement on which character should advance
+ * the cursor by how many cell positions. No established formal
+ * standards exist at present on which Unicode character shall occupy
+ * how many cell positions on character terminals. These routines are
+ * a first attempt of defining such behavior based on simple rules
+ * applied to data provided by the Unicode Consortium.
+ *
+ * For some graphical characters, the Unicode standard explicitly
+ * defines a character-cell width via the definition of the East Asian
+ * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
+ * In all these cases, there is no ambiguity about which width a
+ * terminal shall use. For characters in the East Asian Ambiguous (A)
+ * class, the width choice depends purely on a preference of backward
+ * compatibility with either historic CJK or Western practice.
+ * Choosing single-width for these characters is easy to justify as
+ * the appropriate long-term solution, as the CJK practice of
+ * displaying these characters as double-width comes from historic
+ * implementation simplicity (8-bit encoded characters were displayed
+ * single-width and 16-bit ones double-width, even for Greek,
+ * Cyrillic, etc.) and not any typographic considerations.
+ *
+ * Much less clear is the choice of width for the Not East Asian
+ * (Neutral) class. Existing practice does not dictate a width for any
+ * of these characters. It would nevertheless make sense
+ * typographically to allocate two character cells to characters such
+ * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
+ * represented adequately with a single-width glyph. The following
+ * routines at present merely assign a single-cell width to all
+ * neutral characters, in the interest of simplicity. This is not
+ * entirely satisfactory and should be reconsidered before
+ * establishing a formal standard in this area. At the moment, the
+ * decision which Not East Asian (Neutral) characters should be
+ * represented by double-width glyphs cannot yet be answered by
+ * applying a simple rule from the Unicode database content. Setting
+ * up a proper standard for the behavior of UTF-8 character terminals
+ * will require a careful analysis not only of each Unicode character,
+ * but also of each presentation form, something the author of these
+ * routines has avoided to do so far.
+ *
+ * http://www.unicode.org/unicode/reports/tr11/
+ *
+ * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * for any purpose and without fee is hereby granted. The author
+ * disclaims all warranties with regard to this software.
+ *
+ * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+ */
+
+struct interval {
+ int first;
+ int last;
+};
+
+/* sorted list of non-overlapping intervals of non-spacing characters */
+/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
+static const struct interval combining[] = {
+ { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
+ { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
+ { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
+ { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
+ { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
+ { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
+ { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
+ { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
+ { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
+ { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
+ { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
+ { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
+ { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
+ { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
+ { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
+ { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
+ { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
+ { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
+ { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
+ { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
+ { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
+ { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
+ { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
+ { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
+ { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
+ { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
+ { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
+ { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
+ { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
+ { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
+ { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
+ { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
+ { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
+ { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
+ { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
+ { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
+ { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
+ { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
+ { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
+ { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
+ { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
+ { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
+ { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
+ { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
+ { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
+ { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
+ { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
+ { 0xE0100, 0xE01EF }
+};
+
+
+/* auxiliary function for binary search in interval table */
+static int bisearch(uint32_t ucs, const struct interval *table, int max) {
+ int min = 0;
+ int mid;
+
+ if (ucs < table[0].first || ucs > table[max].last)
+ return 0;
+ while (max >= min) {
+ mid = (min + max) / 2;
+ if (ucs > table[mid].last)
+ min = mid + 1;
+ else if (ucs < table[mid].first)
+ max = mid - 1;
+ else
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* The following two functions define the column width of an ISO 10646
+ * character as follows:
+ *
+ * - The null character (U+0000) has a column width of 0.
+ *
+ * - Other C0/C1 control characters and DEL will lead to a return
+ * value of -1.
+ *
+ * - Non-spacing and enclosing combining characters (general
+ * category code Mn or Me in the Unicode database) have a
+ * column width of 0.
+ *
+ * - SOFT HYPHEN (U+00AD) has a column width of 1.
+ *
+ * - Other format characters (general category code Cf in the Unicode
+ * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
+ *
+ * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
+ * have a column width of 0.
+ *
+ * - Spacing characters in the East Asian Wide (W) or East Asian
+ * Full-width (F) category as defined in Unicode Technical
+ * Report #11 have a column width of 2.
+ *
+ * - All remaining characters (including all printable
+ * ISO 8859-1 and WGL4 characters, Unicode control characters,
+ * etc.) have a column width of 1.
+ *
+ * This implementation assumes that uint32_t characters are encoded
+ * in ISO 10646.
+ */
+
+
+static int mk_wcwidth(uint32_t ucs)
+{
+ /* test for 8-bit control characters */
+ if (ucs == 0)
+ return 0;
+ if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
+ return -1;
+
+ /* binary search in table of non-spacing characters */
+ if (bisearch(ucs, combining,
+ sizeof(combining) / sizeof(struct interval) - 1))
+ return 0;
+
+ /* if we arrive here, ucs is not a combining or C0/C1 control character */
+
+ return 1 +
+ (ucs >= 0x1100 &&
+ (ucs <= 0x115f || /* Hangul Jamo init. consonants */
+ ucs == 0x2329 || ucs == 0x232a ||
+ (ucs >= 0x2e80 && ucs <= 0xa4cf &&
+ ucs != 0x303f) || /* CJK ... Yi */
+ (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
+ (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
+ (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
+ (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
+ (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
+ (ucs >= 0xffe0 && ucs <= 0xffe6) ||
+ (ucs >= 0x20000 && ucs <= 0x2fffd) ||
+ (ucs >= 0x30000 && ucs <= 0x3fffd)));
+}
+
+
+#ifdef USE_MK_WCWIDTH_CJK
+
+/*
+ * The following functions are the same as mk_wcwidth() and
+ * mk_wcswidth(), except that spacing characters in the East Asian
+ * Ambiguous (A) category as defined in Unicode Technical Report #11
+ * have a column width of 2. This variant might be useful for users of
+ * CJK legacy encodings who want to migrate to UCS without changing
+ * the traditional terminal character-width behaviour. It is not
+ * otherwise recommended for general use.
+ */
+static int mk_wcwidth_cjk(uint32_t ucs)
+{
+ /* sorted list of non-overlapping intervals of East Asian Ambiguous
+ * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
+ static const struct interval ambiguous[] = {
+ { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
+ { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
+ { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
+ { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
+ { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
+ { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
+ { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
+ { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
+ { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
+ { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
+ { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
+ { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
+ { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
+ { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
+ { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
+ { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
+ { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
+ { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
+ { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
+ { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
+ { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
+ { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
+ { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
+ { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
+ { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
+ { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
+ { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
+ { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
+ { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
+ { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
+ { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
+ { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
+ { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
+ { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
+ { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
+ { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
+ { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
+ { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
+ { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
+ { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
+ { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
+ { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
+ { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
+ { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
+ { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
+ { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
+ { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
+ { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
+ { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
+ { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
+ { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
+ { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
+ };
+
+ /* binary search in table of non-spacing characters */
+ if (bisearch(ucs, ambiguous,
+ sizeof(ambiguous) / sizeof(struct interval) - 1))
+ return 2;
+
+ return mk_wcwidth(ucs);
+}
+
+#endif
+
+// ################################
+// ### The rest added by Paul Evans
+
+static const struct interval fullwidth[] = {
+#include "fullwidth.inc"
+};
+
+INTERNAL int vterm_unicode_width(uint32_t codepoint)
+{
+ if(bisearch(codepoint, fullwidth, sizeof(fullwidth) / sizeof(fullwidth[0]) - 1))
+ return 2;
+
+ return mk_wcwidth(codepoint);
+}
+
+INTERNAL int vterm_unicode_is_combining(uint32_t codepoint)
+{
+ return bisearch(codepoint, combining, sizeof(combining) / sizeof(struct interval) - 1);
+}
diff --git a/src/libs/3rdparty/libvterm/src/utf8.h b/src/libs/3rdparty/libvterm/src/utf8.h
new file mode 100644
index 0000000000..9a336d357f
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/utf8.h
@@ -0,0 +1,39 @@
+/* The following functions copied and adapted from libtermkey
+ *
+ * http://www.leonerd.org.uk/code/libtermkey/
+ */
+static inline unsigned int utf8_seqlen(long codepoint)
+{
+ if(codepoint < 0x0000080) return 1;
+ if(codepoint < 0x0000800) return 2;
+ if(codepoint < 0x0010000) return 3;
+ if(codepoint < 0x0200000) return 4;
+ if(codepoint < 0x4000000) return 5;
+ return 6;
+}
+
+/* Does NOT NUL-terminate the buffer */
+static int fill_utf8(long codepoint, char *str)
+{
+ int nbytes = utf8_seqlen(codepoint);
+
+ // This is easier done backwards
+ int b = nbytes;
+ while(b > 1) {
+ b--;
+ str[b] = 0x80 | (codepoint & 0x3f);
+ codepoint >>= 6;
+ }
+
+ switch(nbytes) {
+ case 1: str[0] = (codepoint & 0x7f); break;
+ case 2: str[0] = 0xc0 | (codepoint & 0x1f); break;
+ case 3: str[0] = 0xe0 | (codepoint & 0x0f); break;
+ case 4: str[0] = 0xf0 | (codepoint & 0x07); break;
+ case 5: str[0] = 0xf8 | (codepoint & 0x03); break;
+ case 6: str[0] = 0xfc | (codepoint & 0x01); break;
+ }
+
+ return nbytes;
+}
+/* end copy */
diff --git a/src/libs/3rdparty/libvterm/src/vterm.c b/src/libs/3rdparty/libvterm/src/vterm.c
new file mode 100644
index 0000000000..e1f676f5b6
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/vterm.c
@@ -0,0 +1,430 @@
+#include "vterm_internal.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/*****************
+ * API functions *
+ *****************/
+
+static void *default_malloc(size_t size, void *allocdata)
+{
+ void *ptr = malloc(size);
+ if(ptr)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+static void default_free(void *ptr, void *allocdata)
+{
+ free(ptr);
+}
+
+static VTermAllocatorFunctions default_allocator = {
+ .malloc = &default_malloc,
+ .free = &default_free,
+};
+
+VTerm *vterm_new(int rows, int cols)
+{
+ return vterm_build(&(const struct VTermBuilder){
+ .rows = rows,
+ .cols = cols,
+ });
+}
+
+VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata)
+{
+ return vterm_build(&(const struct VTermBuilder){
+ .rows = rows,
+ .cols = cols,
+ .allocator = funcs,
+ .allocdata = allocdata,
+ });
+}
+
+/* A handy macro for defaulting values out of builder fields */
+#define DEFAULT(v, def) ((v) ? (v) : (def))
+
+VTerm *vterm_build(const struct VTermBuilder *builder)
+{
+ const VTermAllocatorFunctions *allocator = DEFAULT(builder->allocator, &default_allocator);
+
+ /* Need to bootstrap using the allocator function directly */
+ VTerm *vt = (*allocator->malloc)(sizeof(VTerm), builder->allocdata);
+
+ vt->allocator = allocator;
+ vt->allocdata = builder->allocdata;
+
+ vt->rows = builder->rows;
+ vt->cols = builder->cols;
+
+ vt->parser.state = NORMAL;
+
+ vt->parser.callbacks = NULL;
+ vt->parser.cbdata = NULL;
+
+ vt->parser.emit_nul = false;
+
+ vt->outfunc = NULL;
+ vt->outdata = NULL;
+
+ vt->outbuffer_len = DEFAULT(builder->outbuffer_len, 4096);
+ vt->outbuffer_cur = 0;
+ vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
+
+ vt->tmpbuffer_len = DEFAULT(builder->tmpbuffer_len, 4096);
+ vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
+
+ return vt;
+}
+
+void vterm_free(VTerm *vt)
+{
+ if(vt->screen)
+ vterm_screen_free(vt->screen);
+
+ if(vt->state)
+ vterm_state_free(vt->state);
+
+ vterm_allocator_free(vt, vt->outbuffer);
+ vterm_allocator_free(vt, vt->tmpbuffer);
+
+ vterm_allocator_free(vt, vt);
+}
+
+INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size)
+{
+ return (*vt->allocator->malloc)(size, vt->allocdata);
+}
+
+INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
+{
+ (*vt->allocator->free)(ptr, vt->allocdata);
+}
+
+void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
+{
+ if(rowsp)
+ *rowsp = vt->rows;
+ if(colsp)
+ *colsp = vt->cols;
+}
+
+void vterm_set_size(VTerm *vt, int rows, int cols)
+{
+ if(rows < 1 || cols < 1)
+ return;
+
+ vt->rows = rows;
+ vt->cols = cols;
+
+ if(vt->parser.callbacks && vt->parser.callbacks->resize)
+ (*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
+}
+
+int vterm_get_utf8(const VTerm *vt)
+{
+ return vt->mode.utf8;
+}
+
+void vterm_set_utf8(VTerm *vt, int is_utf8)
+{
+ vt->mode.utf8 = is_utf8;
+}
+
+void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user)
+{
+ vt->outfunc = func;
+ vt->outdata = user;
+}
+
+INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
+{
+ if(vt->outfunc) {
+ (vt->outfunc)(bytes, len, vt->outdata);
+ return;
+ }
+
+ if(len > vt->outbuffer_len - vt->outbuffer_cur)
+ return;
+
+ memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
+ vt->outbuffer_cur += len;
+}
+
+INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args)
+{
+ size_t len = vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len,
+ format, args);
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, len);
+}
+
+INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vterm_push_output_vsprintf(vt, format, args);
+ va_end(args);
+}
+
+INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...)
+{
+ size_t cur;
+
+ if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
+ cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
+ ESC_S "%c", ctrl - 0x40);
+ else
+ cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
+ "%c", ctrl);
+
+ if(cur >= vt->tmpbuffer_len)
+ return;
+
+ va_list args;
+ va_start(args, fmt);
+ cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ fmt, args);
+ va_end(args);
+
+ if(cur >= vt->tmpbuffer_len)
+ return;
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+}
+
+INTERNAL void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt, ...)
+{
+ size_t cur = 0;
+
+ if(ctrl) {
+ if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
+ cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
+ ESC_S "%c", ctrl - 0x40);
+ else
+ cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
+ "%c", ctrl);
+
+ if(cur >= vt->tmpbuffer_len)
+ return;
+ }
+
+ va_list args;
+ va_start(args, fmt);
+ cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ fmt, args);
+ va_end(args);
+
+ if(cur >= vt->tmpbuffer_len)
+ return;
+
+ if(term) {
+ cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST
+
+ if(cur >= vt->tmpbuffer_len)
+ return;
+ }
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+}
+
+size_t vterm_output_get_buffer_size(const VTerm *vt)
+{
+ return vt->outbuffer_len;
+}
+
+size_t vterm_output_get_buffer_current(const VTerm *vt)
+{
+ return vt->outbuffer_cur;
+}
+
+size_t vterm_output_get_buffer_remaining(const VTerm *vt)
+{
+ return vt->outbuffer_len - vt->outbuffer_cur;
+}
+
+size_t vterm_output_read(VTerm *vt, char *buffer, size_t len)
+{
+ if(len > vt->outbuffer_cur)
+ len = vt->outbuffer_cur;
+
+ memcpy(buffer, vt->outbuffer, len);
+
+ if(len < vt->outbuffer_cur)
+ memmove(vt->outbuffer, vt->outbuffer + len, vt->outbuffer_cur - len);
+
+ vt->outbuffer_cur -= len;
+
+ return len;
+}
+
+VTermValueType vterm_get_attr_type(VTermAttr attr)
+{
+ switch(attr) {
+ case VTERM_ATTR_BOLD: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_UNDERLINE: return VTERM_VALUETYPE_INT;
+ case VTERM_ATTR_ITALIC: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_BLINK: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_REVERSE: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_CONCEAL: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_STRIKE: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_FONT: return VTERM_VALUETYPE_INT;
+ case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;
+ case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR;
+ case VTERM_ATTR_SMALL: return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_BASELINE: return VTERM_VALUETYPE_INT;
+
+ case VTERM_N_ATTRS: return 0;
+ }
+ return 0; /* UNREACHABLE */
+}
+
+VTermValueType vterm_get_prop_type(VTermProp prop)
+{
+ switch(prop) {
+ case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_CURSORBLINK: return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_ALTSCREEN: return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_TITLE: return VTERM_VALUETYPE_STRING;
+ case VTERM_PROP_ICONNAME: return VTERM_VALUETYPE_STRING;
+ case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL;
+ case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT;
+ case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT;
+ case VTERM_PROP_FOCUSREPORT: return VTERM_VALUETYPE_BOOL;
+
+ case VTERM_N_PROPS: return 0;
+ }
+ return 0; /* UNREACHABLE */
+}
+
+void vterm_scroll_rect(VTermRect rect,
+ int downward,
+ int rightward,
+ int (*moverect)(VTermRect src, VTermRect dest, void *user),
+ int (*eraserect)(VTermRect rect, int selective, void *user),
+ void *user)
+{
+ VTermRect src;
+ VTermRect dest;
+
+ if(abs(downward) >= rect.end_row - rect.start_row ||
+ abs(rightward) >= rect.end_col - rect.start_col) {
+ /* Scroll more than area; just erase the lot */
+ (*eraserect)(rect, 0, user);
+ return;
+ }
+
+ if(rightward >= 0) {
+ /* rect: [XXX................]
+ * src: [----------------]
+ * dest: [----------------]
+ */
+ dest.start_col = rect.start_col;
+ dest.end_col = rect.end_col - rightward;
+ src.start_col = rect.start_col + rightward;
+ src.end_col = rect.end_col;
+ }
+ else {
+ /* rect: [................XXX]
+ * src: [----------------]
+ * dest: [----------------]
+ */
+ int leftward = -rightward;
+ dest.start_col = rect.start_col + leftward;
+ dest.end_col = rect.end_col;
+ src.start_col = rect.start_col;
+ src.end_col = rect.end_col - leftward;
+ }
+
+ if(downward >= 0) {
+ dest.start_row = rect.start_row;
+ dest.end_row = rect.end_row - downward;
+ src.start_row = rect.start_row + downward;
+ src.end_row = rect.end_row;
+ }
+ else {
+ int upward = -downward;
+ dest.start_row = rect.start_row + upward;
+ dest.end_row = rect.end_row;
+ src.start_row = rect.start_row;
+ src.end_row = rect.end_row - upward;
+ }
+
+ if(moverect)
+ (*moverect)(dest, src, user);
+
+ if(downward > 0)
+ rect.start_row = rect.end_row - downward;
+ else if(downward < 0)
+ rect.end_row = rect.start_row - downward;
+
+ if(rightward > 0)
+ rect.start_col = rect.end_col - rightward;
+ else if(rightward < 0)
+ rect.end_col = rect.start_col - rightward;
+
+ (*eraserect)(rect, 0, user);
+}
+
+void vterm_copy_cells(VTermRect dest,
+ VTermRect src,
+ void (*copycell)(VTermPos dest, VTermPos src, void *user),
+ void *user)
+{
+ int downward = src.start_row - dest.start_row;
+ int rightward = src.start_col - dest.start_col;
+
+ int init_row, test_row, init_col, test_col;
+ int inc_row, inc_col;
+
+ if(downward < 0) {
+ init_row = dest.end_row - 1;
+ test_row = dest.start_row - 1;
+ inc_row = -1;
+ }
+ else /* downward >= 0 */ {
+ init_row = dest.start_row;
+ test_row = dest.end_row;
+ inc_row = +1;
+ }
+
+ if(rightward < 0) {
+ init_col = dest.end_col - 1;
+ test_col = dest.start_col - 1;
+ inc_col = -1;
+ }
+ else /* rightward >= 0 */ {
+ init_col = dest.start_col;
+ test_col = dest.end_col;
+ inc_col = +1;
+ }
+
+ VTermPos pos;
+ for(pos.row = init_row; pos.row != test_row; pos.row += inc_row)
+ for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
+ VTermPos srcpos = { pos.row + downward, pos.col + rightward };
+ (*copycell)(pos, srcpos, user);
+ }
+}
+
+void vterm_check_version(int major, int minor)
+{
+ if(major != VTERM_VERSION_MAJOR) {
+ fprintf(stderr, "libvterm major version mismatch; %d (wants) != %d (library)\n",
+ major, VTERM_VERSION_MAJOR);
+ exit(1);
+ }
+
+ if(minor > VTERM_VERSION_MINOR) {
+ fprintf(stderr, "libvterm minor version mismatch; %d (wants) > %d (library)\n",
+ minor, VTERM_VERSION_MINOR);
+ exit(1);
+ }
+
+ // Happy
+}
diff --git a/src/libs/3rdparty/libvterm/src/vterm_internal.h b/src/libs/3rdparty/libvterm/src/vterm_internal.h
new file mode 100644
index 0000000000..5d934aff07
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/src/vterm_internal.h
@@ -0,0 +1,297 @@
+#ifndef __VTERM_INTERNAL_H__
+#define __VTERM_INTERNAL_H__
+
+#include "vterm.h"
+
+#include <stdarg.h>
+
+#if defined(__GNUC__)
+# define INTERNAL __attribute__((visibility("internal")))
+#else
+# define INTERNAL
+#endif
+
+#ifdef DEBUG
+# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define DEBUG_LOG(...)
+#endif
+
+#define ESC_S "\x1b"
+
+#define INTERMED_MAX 16
+
+#define CSI_ARGS_MAX 16
+#define CSI_LEADER_MAX 16
+
+#define BUFIDX_PRIMARY 0
+#define BUFIDX_ALTSCREEN 1
+
+typedef struct VTermEncoding VTermEncoding;
+
+typedef struct {
+ VTermEncoding *enc;
+
+ // This size should be increased if required by other stateful encodings
+ char data[4*sizeof(uint32_t)];
+} VTermEncodingInstance;
+
+struct VTermPen
+{
+ VTermColor fg;
+ VTermColor bg;
+ unsigned int bold:1;
+ unsigned int underline:3;
+ unsigned int italic:1;
+ unsigned int blink:1;
+ unsigned int reverse:1;
+ unsigned int conceal:1;
+ unsigned int strike:1;
+ unsigned int font:4; /* To store 0-9 */
+ unsigned int small:1;
+ unsigned int baseline:2;
+};
+
+struct VTermState
+{
+ VTerm *vt;
+
+ const VTermStateCallbacks *callbacks;
+ void *cbdata;
+
+ const VTermStateFallbacks *fallbacks;
+ void *fbdata;
+
+ int rows;
+ int cols;
+
+ /* Current cursor position */
+ VTermPos pos;
+
+ int at_phantom; /* True if we're on the "81st" phantom column to defer a wraparound */
+
+ int scrollregion_top;
+ int scrollregion_bottom; /* -1 means unbounded */
+#define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > -1 ? (state)->scrollregion_bottom : (state)->rows)
+ int scrollregion_left;
+#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
+ int scrollregion_right; /* -1 means unbounded */
+#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin && (state)->scrollregion_right > -1 ? (state)->scrollregion_right : (state)->cols)
+
+ /* Bitvector of tab stops */
+ unsigned char *tabstops;
+
+ /* Primary and Altscreen; lineinfos[1] is lazily allocated as needed */
+ VTermLineInfo *lineinfos[2];
+
+ /* lineinfo will == lineinfos[0] or lineinfos[1], depending on altscreen */
+ VTermLineInfo *lineinfo;
+#define ROWWIDTH(state,row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
+#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
+
+ /* Mouse state */
+ int mouse_col, mouse_row;
+ int mouse_buttons;
+ int mouse_flags;
+#define MOUSE_WANT_CLICK 0x01
+#define MOUSE_WANT_DRAG 0x02
+#define MOUSE_WANT_MOVE 0x04
+
+ enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT } mouse_protocol;
+
+ /* Last glyph output, for Unicode recombining purposes */
+ uint32_t *combine_chars;
+ size_t combine_chars_size; // Number of ELEMENTS in the above
+ int combine_width; // The width of the glyph above
+ VTermPos combine_pos; // Position before movement
+
+ struct {
+ unsigned int keypad:1;
+ unsigned int cursor:1;
+ unsigned int autowrap:1;
+ unsigned int insert:1;
+ unsigned int newline:1;
+ unsigned int cursor_visible:1;
+ unsigned int cursor_blink:1;
+ unsigned int cursor_shape:2;
+ unsigned int alt_screen:1;
+ unsigned int origin:1;
+ unsigned int screen:1;
+ unsigned int leftrightmargin:1;
+ unsigned int bracketpaste:1;
+ unsigned int report_focus:1;
+ } mode;
+
+ VTermEncodingInstance encoding[4], encoding_utf8;
+ int gl_set, gr_set, gsingle_set;
+
+ struct VTermPen pen;
+
+ VTermColor default_fg;
+ VTermColor default_bg;
+ VTermColor colors[16]; // Store the 8 ANSI and the 8 ANSI high-brights only
+
+ int bold_is_highbright;
+
+ unsigned int protected_cell : 1;
+
+ /* Saved state under DEC mode 1048/1049 */
+ struct {
+ VTermPos pos;
+ struct VTermPen pen;
+
+ struct {
+ unsigned int cursor_visible:1;
+ unsigned int cursor_blink:1;
+ unsigned int cursor_shape:2;
+ } mode;
+ } saved;
+
+ /* Temporary state for DECRQSS parsing */
+ union {
+ char decrqss[4];
+ struct {
+ uint16_t mask;
+ enum {
+ SELECTION_INITIAL,
+ SELECTION_SELECTED,
+ SELECTION_QUERY,
+ SELECTION_SET_INITIAL,
+ SELECTION_SET,
+ SELECTION_INVALID,
+ } state : 8;
+ uint32_t recvpartial;
+ uint32_t sendpartial;
+ } selection;
+ } tmp;
+
+ struct {
+ const VTermSelectionCallbacks *callbacks;
+ void *user;
+ char *buffer;
+ size_t buflen;
+ } selection;
+};
+
+struct VTerm
+{
+ const VTermAllocatorFunctions *allocator;
+ void *allocdata;
+
+ int rows;
+ int cols;
+
+ struct {
+ unsigned int utf8:1;
+ unsigned int ctrl8bit:1;
+ } mode;
+
+ struct {
+ enum VTermParserState {
+ NORMAL,
+ CSI_LEADER,
+ CSI_ARGS,
+ CSI_INTERMED,
+ DCS_COMMAND,
+ /* below here are the "string states" */
+ OSC_COMMAND,
+ OSC,
+ DCS,
+ APC,
+ PM,
+ SOS,
+ } state;
+
+ bool in_esc : 1;
+
+ int intermedlen;
+ char intermed[INTERMED_MAX];
+
+ union {
+ struct {
+ int leaderlen;
+ char leader[CSI_LEADER_MAX];
+
+ int argi;
+ long args[CSI_ARGS_MAX];
+ } csi;
+ struct {
+ int command;
+ } osc;
+ struct {
+ int commandlen;
+ char command[CSI_LEADER_MAX];
+ } dcs;
+ } v;
+
+ const VTermParserCallbacks *callbacks;
+ void *cbdata;
+
+ bool string_initial;
+
+ bool emit_nul;
+ } parser;
+
+ /* len == malloc()ed size; cur == number of valid bytes */
+
+ VTermOutputCallback *outfunc;
+ void *outdata;
+
+ char *outbuffer;
+ size_t outbuffer_len;
+ size_t outbuffer_cur;
+
+ char *tmpbuffer;
+ size_t tmpbuffer_len;
+
+ VTermState *state;
+ VTermScreen *screen;
+};
+
+struct VTermEncoding {
+ void (*init) (VTermEncoding *enc, void *data);
+ void (*decode)(VTermEncoding *enc, void *data,
+ uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t len);
+};
+
+typedef enum {
+ ENC_UTF8,
+ ENC_SINGLE_94
+} VTermEncodingType;
+
+void *vterm_allocator_malloc(VTerm *vt, size_t size);
+void vterm_allocator_free(VTerm *vt, void *ptr);
+
+void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len);
+void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args);
+void vterm_push_output_sprintf(VTerm *vt, const char *format, ...);
+void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...);
+void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt, ...);
+
+void vterm_state_free(VTermState *state);
+
+void vterm_state_newpen(VTermState *state);
+void vterm_state_resetpen(VTermState *state);
+void vterm_state_setpen(VTermState *state, const long args[], int argcount);
+int vterm_state_getpen(VTermState *state, long args[], int argcount);
+void vterm_state_savepen(VTermState *state, int save);
+
+enum {
+ C1_SS3 = 0x8f,
+ C1_DCS = 0x90,
+ C1_CSI = 0x9b,
+ C1_ST = 0x9c,
+ C1_OSC = 0x9d,
+};
+
+void vterm_state_push_output_sprintf_CSI(VTermState *vts, const char *format, ...);
+
+void vterm_screen_free(VTermScreen *screen);
+
+VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation);
+
+int vterm_unicode_width(uint32_t codepoint);
+int vterm_unicode_is_combining(uint32_t codepoint);
+
+#endif
diff --git a/src/libs/3rdparty/libvterm/vterm.pc.in b/src/libs/3rdparty/libvterm/vterm.pc.in
new file mode 100644
index 0000000000..681a270d51
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/vterm.pc.in
@@ -0,0 +1,8 @@
+libdir=@LIBDIR@
+includedir=@INCDIR@
+
+Name: vterm
+Description: Abstract VT220/Xterm/ECMA-48 emulation library
+Version: 0.3.1
+Libs: -L${libdir} -lvterm
+Cflags: -I${includedir}
diff --git a/src/libs/3rdparty/libvterm/vterm.qbs b/src/libs/3rdparty/libvterm/vterm.qbs
new file mode 100644
index 0000000000..e35658908d
--- /dev/null
+++ b/src/libs/3rdparty/libvterm/vterm.qbs
@@ -0,0 +1,35 @@
+QtcLibrary {
+ name: "vterm"
+ type: "staticlibrary"
+
+ useQt: false
+
+ Depends { name: "cpp" }
+
+ cpp.includePaths: base.concat("include")
+ cpp.warningLevel: "none"
+
+ Group {
+ prefix: "src/"
+ files: [
+ "encoding.c",
+ "fullwidth.inc",
+ "keyboard.c",
+ "mouse.c",
+ "parser.c",
+ "pen.c",
+ "rect.h",
+ "screen.c",
+ "state.c",
+ "unicode.c",
+ "utf8.h",
+ "vterm.c",
+ "vterm_internal.h",
+ ]
+ }
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: "include"
+ }
+}
diff --git a/src/libs/3rdparty/lua/CMakeLists.txt b/src/libs/3rdparty/lua/CMakeLists.txt
new file mode 100644
index 0000000000..121e063f19
--- /dev/null
+++ b/src/libs/3rdparty/lua/CMakeLists.txt
@@ -0,0 +1,40 @@
+
+add_qtc_library(lua546
+ PUBLIC_INCLUDES src
+ STATIC
+ SOURCES
+ src/lapi.c
+ src/lauxlib.c
+ src/lbaselib.c
+ src/lcode.c
+ src/lcorolib.c
+ src/lctype.c
+ src/ldblib.c
+ src/ldebug.c
+ src/ldo.c
+ src/ldump.c
+ src/lfunc.c
+ src/lgc.c
+ src/linit.c
+ src/liolib.c
+ src/llex.c
+ src/lmathlib.c
+ src/lmem.c
+ src/loadlib.c
+ src/lobject.c
+ src/lopcodes.c
+ src/loslib.c
+ src/lparser.c
+ src/lstate.c
+ src/lstring.c
+ src/lstrlib.c
+ src/ltable.c
+ src/ltablib.c
+ src/ltm.c
+ src/lua.c
+ src/lundump.c
+ src/lutf8lib.c
+ src/lvm.c
+ src/lzio.c
+)
+
diff --git a/src/libs/3rdparty/lua/LICENSE b/src/libs/3rdparty/lua/LICENSE
new file mode 100644
index 0000000000..441cc1981c
--- /dev/null
+++ b/src/libs/3rdparty/lua/LICENSE
@@ -0,0 +1,16 @@
+Copyright © 1994–2023 Lua.org, PUC-Rio.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/src/libs/3rdparty/lua/README b/src/libs/3rdparty/lua/README
new file mode 100644
index 0000000000..1ae97165ba
--- /dev/null
+++ b/src/libs/3rdparty/lua/README
@@ -0,0 +1,6 @@
+
+This is Lua 5.4.6, released on 02 May 2023.
+
+For installation instructions, license details, and
+further information about Lua, see doc/readme.html.
+
diff --git a/src/libs/3rdparty/lua/doc/contents.html b/src/libs/3rdparty/lua/doc/contents.html
new file mode 100644
index 0000000000..1231e6d248
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/contents.html
@@ -0,0 +1,678 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML>
+<HEAD>
+<TITLE>Lua 5.4 Reference Manual - contents</TITLE>
+<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
+<LINK REL="stylesheet" TYPE="text/css" HREF="index.css">
+<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
+</HEAD>
+
+<BODY>
+
+<H1>
+<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
+Lua 5.4 Reference Manual
+</H1>
+
+<P>
+The reference manual is the official definition of the Lua language.
+<BR>
+For a complete introduction to Lua programming, see the book
+<A HREF="http://www.lua.org/pil/">Programming in Lua</A>.
+
+<DIV CLASS="menubar">
+<A HREF="manual.html">start</A>
+&middot;
+<A HREF="#contents">contents</A>
+&middot;
+<A HREF="#index">index</A>
+&middot;
+<A HREF="http://www.lua.org/manual/">other versions</A>
+</DIV>
+
+<P>
+<SMALL>
+Copyright &copy; 2020&ndash;2023 Lua.org, PUC-Rio.
+Freely available under the terms of the
+<A HREF="http://www.lua.org/license.html">Lua license</A>.
+</SMALL>
+
+<H2><A NAME="contents">Contents</A></H2>
+<UL CLASS="contents menubar">
+<LI><A HREF="manual.html">1 &ndash; Introduction</A>
+<P>
+<LI><A HREF="manual.html#2">2 &ndash; Basic Concepts</A>
+<UL>
+<LI><A HREF="manual.html#2.1">2.1 &ndash; Values and Types</A>
+<LI><A HREF="manual.html#2.2">2.2 &ndash; Environments and the Global Environment</A>
+<LI><A HREF="manual.html#2.3">2.3 &ndash; Error Handling</A>
+<LI><A HREF="manual.html#2.4">2.4 &ndash; Metatables and Metamethods</A>
+<LI><A HREF="manual.html#2.5">2.5 &ndash; Garbage Collection</A>
+<UL>
+<LI><A HREF="manual.html#2.5.1">2.5.1 &ndash; Incremental Garbage Collection</A>
+<LI><A HREF="manual.html#2.5.2">2.5.2 &ndash; Generational Garbage Collection</A>
+<LI><A HREF="manual.html#2.5.3">2.5.3 &ndash; Garbage-Collection Metamethods</A>
+<LI><A HREF="manual.html#2.5.4">2.5.4 &ndash; Weak Tables</A>
+</UL>
+<LI><A HREF="manual.html#2.6">2.6 &ndash; Coroutines</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#3">3 &ndash; The Language</A>
+<UL>
+<LI><A HREF="manual.html#3.1">3.1 &ndash; Lexical Conventions</A>
+<LI><A HREF="manual.html#3.2">3.2 &ndash; Variables</A>
+<LI><A HREF="manual.html#3.3">3.3 &ndash; Statements</A>
+<UL>
+<LI><A HREF="manual.html#3.3.1">3.3.1 &ndash; Blocks</A>
+<LI><A HREF="manual.html#3.3.2">3.3.2 &ndash; Chunks</A>
+<LI><A HREF="manual.html#3.3.3">3.3.3 &ndash; Assignment</A>
+<LI><A HREF="manual.html#3.3.4">3.3.4 &ndash; Control Structures</A>
+<LI><A HREF="manual.html#3.3.5">3.3.5 &ndash; For Statement</A>
+<LI><A HREF="manual.html#3.3.6">3.3.6 &ndash; Function Calls as Statements</A>
+<LI><A HREF="manual.html#3.3.7">3.3.7 &ndash; Local Declarations</A>
+<LI><A HREF="manual.html#3.3.8">3.3.8 &ndash; To-be-closed Variables</A>
+</UL>
+<LI><A HREF="manual.html#3.4">3.4 &ndash; Expressions</A>
+<UL>
+<LI><A HREF="manual.html#3.4.1">3.4.1 &ndash; Arithmetic Operators</A>
+<LI><A HREF="manual.html#3.4.2">3.4.2 &ndash; Bitwise Operators</A>
+<LI><A HREF="manual.html#3.4.3">3.4.3 &ndash; Coercions and Conversions</A>
+<LI><A HREF="manual.html#3.4.4">3.4.4 &ndash; Relational Operators</A>
+<LI><A HREF="manual.html#3.4.5">3.4.5 &ndash; Logical Operators</A>
+<LI><A HREF="manual.html#3.4.6">3.4.6 &ndash; Concatenation</A>
+<LI><A HREF="manual.html#3.4.7">3.4.7 &ndash; The Length Operator</A>
+<LI><A HREF="manual.html#3.4.8">3.4.8 &ndash; Precedence</A>
+<LI><A HREF="manual.html#3.4.9">3.4.9 &ndash; Table Constructors</A>
+<LI><A HREF="manual.html#3.4.10">3.4.10 &ndash; Function Calls</A>
+<LI><A HREF="manual.html#3.4.11">3.4.11 &ndash; Function Definitions</A>
+<LI><A HREF="manual.html#3.4.12">3.4.12 &ndash; Lists of expressions, multiple results, and adjustment<A>
+
+</UL>
+<LI><A HREF="manual.html#3.5">3.5 &ndash; Visibility Rules</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#4">4 &ndash; The Application Program Interface</A>
+<UL>
+<LI><A HREF="manual.html#4.1">4.1 &ndash; The Stack</A>
+<UL>
+<LI><A HREF="manual.html#4.1.1">4.1.1 &ndash; Stack Size</A>
+<LI><A HREF="manual.html#4.1.2">4.1.2 &ndash; Valid and Acceptable Indices</A>
+<LI><A HREF="manual.html#4.1.3">4.1.3 &ndash; Pointers to strings</A>
+</UL>
+<LI><A HREF="manual.html#4.2">4.2 &ndash; C Closures</A>
+<LI><A HREF="manual.html#4.3">4.3 &ndash; Registry</A>
+<LI><A HREF="manual.html#4.4">4.4 &ndash; Error Handling in C</A>
+<UL>
+<LI><A HREF="manual.html#4.4.1">4.4.1 &ndash; Status Codes</A>
+</UL>
+<LI><A HREF="manual.html#4.5">4.5 &ndash; Handling Yields in C</A>
+<LI><A HREF="manual.html#4.6">4.6 &ndash; Functions and Types</A>
+<LI><A HREF="manual.html#4.7">4.7 &ndash; The Debug Interface</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#5">5 &ndash; The Auxiliary Library</A>
+<UL>
+<LI><A HREF="manual.html#5.1">5.1 &ndash; Functions and Types</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#6">6 &ndash; The Standard Libraries</A>
+<UL>
+<LI><A HREF="manual.html#6.1">6.1 &ndash; Basic Functions</A>
+<LI><A HREF="manual.html#6.2">6.2 &ndash; Coroutine Manipulation</A>
+<LI><A HREF="manual.html#6.3">6.3 &ndash; Modules</A>
+<LI><A HREF="manual.html#6.4">6.4 &ndash; String Manipulation</A>
+<UL>
+<LI><A HREF="manual.html#6.4.1">6.4.1 &ndash; Patterns</A>
+<LI><A HREF="manual.html#6.4.2">6.4.2 &ndash; Format Strings for Pack and Unpack</A>
+</UL>
+<LI><A HREF="manual.html#6.5">6.5 &ndash; UTF-8 Support</A>
+<LI><A HREF="manual.html#6.6">6.6 &ndash; Table Manipulation</A>
+<LI><A HREF="manual.html#6.7">6.7 &ndash; Mathematical Functions</A>
+<LI><A HREF="manual.html#6.8">6.8 &ndash; Input and Output Facilities</A>
+<LI><A HREF="manual.html#6.9">6.9 &ndash; Operating System Facilities</A>
+<LI><A HREF="manual.html#6.10">6.10 &ndash; The Debug Library</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#7">7 &ndash; Lua Standalone</A>
+<P>
+<LI><A HREF="manual.html#8">8 &ndash; Incompatibilities with the Previous Version</A>
+<UL>
+<LI><A HREF="manual.html#8.1">8.1 &ndash; Incompatibilities in the Language</A>
+<LI><A HREF="manual.html#8.2">8.2 &ndash; Incompatibilities in the Libraries</A>
+<LI><A HREF="manual.html#8.3">8.3 &ndash; Incompatibilities in the API</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#9">9 &ndash; The Complete Syntax of Lua</A>
+</UL>
+
+<H2><A NAME="index">Index</A></H2>
+<TABLE CLASS="menubar" WIDTH="100%">
+<TR>
+<TD>
+<H3><A NAME="functions">Lua functions</A></H3>
+<P>
+<A HREF="manual.html#6.1">basic</A><BR>
+<A HREF="manual.html#pdf-_G">_G</A><BR>
+<A HREF="manual.html#pdf-_VERSION">_VERSION</A><BR>
+<A HREF="manual.html#pdf-assert">assert</A><BR>
+<A HREF="manual.html#pdf-collectgarbage">collectgarbage</A><BR>
+<A HREF="manual.html#pdf-dofile">dofile</A><BR>
+<A HREF="manual.html#pdf-error">error</A><BR>
+<A HREF="manual.html#pdf-getmetatable">getmetatable</A><BR>
+<A HREF="manual.html#pdf-ipairs">ipairs</A><BR>
+<A HREF="manual.html#pdf-load">load</A><BR>
+<A HREF="manual.html#pdf-loadfile">loadfile</A><BR>
+<A HREF="manual.html#pdf-next">next</A><BR>
+<A HREF="manual.html#pdf-pairs">pairs</A><BR>
+<A HREF="manual.html#pdf-pcall">pcall</A><BR>
+<A HREF="manual.html#pdf-print">print</A><BR>
+<A HREF="manual.html#pdf-rawequal">rawequal</A><BR>
+<A HREF="manual.html#pdf-rawget">rawget</A><BR>
+<A HREF="manual.html#pdf-rawlen">rawlen</A><BR>
+<A HREF="manual.html#pdf-rawset">rawset</A><BR>
+<A HREF="manual.html#pdf-require">require</A><BR>
+<A HREF="manual.html#pdf-select">select</A><BR>
+<A HREF="manual.html#pdf-setmetatable">setmetatable</A><BR>
+<A HREF="manual.html#pdf-tonumber">tonumber</A><BR>
+<A HREF="manual.html#pdf-tostring">tostring</A><BR>
+<A HREF="manual.html#pdf-type">type</A><BR>
+<A HREF="manual.html#pdf-warn">warn</A><BR>
+<A HREF="manual.html#pdf-xpcall">xpcall</A><BR>
+
+<P>
+<A HREF="manual.html#6.2">coroutine</A><BR>
+<A HREF="manual.html#pdf-coroutine.close">coroutine.close</A><BR>
+<A HREF="manual.html#pdf-coroutine.create">coroutine.create</A><BR>
+<A HREF="manual.html#pdf-coroutine.isyieldable">coroutine.isyieldable</A><BR>
+<A HREF="manual.html#pdf-coroutine.resume">coroutine.resume</A><BR>
+<A HREF="manual.html#pdf-coroutine.running">coroutine.running</A><BR>
+<A HREF="manual.html#pdf-coroutine.status">coroutine.status</A><BR>
+<A HREF="manual.html#pdf-coroutine.wrap">coroutine.wrap</A><BR>
+<A HREF="manual.html#pdf-coroutine.yield">coroutine.yield</A><BR>
+
+<P>
+<A HREF="manual.html#6.10">debug</A><BR>
+<A HREF="manual.html#pdf-debug.debug">debug.debug</A><BR>
+<A HREF="manual.html#pdf-debug.gethook">debug.gethook</A><BR>
+<A HREF="manual.html#pdf-debug.getinfo">debug.getinfo</A><BR>
+<A HREF="manual.html#pdf-debug.getlocal">debug.getlocal</A><BR>
+<A HREF="manual.html#pdf-debug.getmetatable">debug.getmetatable</A><BR>
+<A HREF="manual.html#pdf-debug.getregistry">debug.getregistry</A><BR>
+<A HREF="manual.html#pdf-debug.getupvalue">debug.getupvalue</A><BR>
+<A HREF="manual.html#pdf-debug.getuservalue">debug.getuservalue</A><BR>
+<A HREF="manual.html#pdf-debug.sethook">debug.sethook</A><BR>
+<A HREF="manual.html#pdf-debug.setlocal">debug.setlocal</A><BR>
+<A HREF="manual.html#pdf-debug.setmetatable">debug.setmetatable</A><BR>
+<A HREF="manual.html#pdf-debug.setupvalue">debug.setupvalue</A><BR>
+<A HREF="manual.html#pdf-debug.setuservalue">debug.setuservalue</A><BR>
+<A HREF="manual.html#pdf-debug.traceback">debug.traceback</A><BR>
+<A HREF="manual.html#pdf-debug.upvalueid">debug.upvalueid</A><BR>
+<A HREF="manual.html#pdf-debug.upvaluejoin">debug.upvaluejoin</A><BR>
+
+<P>
+<A HREF="manual.html#6.8">io</A><BR>
+<A HREF="manual.html#pdf-io.close">io.close</A><BR>
+<A HREF="manual.html#pdf-io.flush">io.flush</A><BR>
+<A HREF="manual.html#pdf-io.input">io.input</A><BR>
+<A HREF="manual.html#pdf-io.lines">io.lines</A><BR>
+<A HREF="manual.html#pdf-io.open">io.open</A><BR>
+<A HREF="manual.html#pdf-io.output">io.output</A><BR>
+<A HREF="manual.html#pdf-io.popen">io.popen</A><BR>
+<A HREF="manual.html#pdf-io.read">io.read</A><BR>
+<A HREF="manual.html#pdf-io.stderr">io.stderr</A><BR>
+<A HREF="manual.html#pdf-io.stdin">io.stdin</A><BR>
+<A HREF="manual.html#pdf-io.stdout">io.stdout</A><BR>
+<A HREF="manual.html#pdf-io.tmpfile">io.tmpfile</A><BR>
+<A HREF="manual.html#pdf-io.type">io.type</A><BR>
+<A HREF="manual.html#pdf-io.write">io.write</A><BR>
+
+<A HREF="manual.html#pdf-file:close">file:close</A><BR>
+<A HREF="manual.html#pdf-file:flush">file:flush</A><BR>
+<A HREF="manual.html#pdf-file:lines">file:lines</A><BR>
+<A HREF="manual.html#pdf-file:read">file:read</A><BR>
+<A HREF="manual.html#pdf-file:seek">file:seek</A><BR>
+<A HREF="manual.html#pdf-file:setvbuf">file:setvbuf</A><BR>
+<A HREF="manual.html#pdf-file:write">file:write</A><BR>
+
+</TD>
+<TD>
+<H3>&nbsp;</H3>
+<P>
+<A HREF="manual.html#6.7">math</A><BR>
+<A HREF="manual.html#pdf-math.abs">math.abs</A><BR>
+<A HREF="manual.html#pdf-math.acos">math.acos</A><BR>
+<A HREF="manual.html#pdf-math.asin">math.asin</A><BR>
+<A HREF="manual.html#pdf-math.atan">math.atan</A><BR>
+<A HREF="manual.html#pdf-math.ceil">math.ceil</A><BR>
+<A HREF="manual.html#pdf-math.cos">math.cos</A><BR>
+<A HREF="manual.html#pdf-math.deg">math.deg</A><BR>
+<A HREF="manual.html#pdf-math.exp">math.exp</A><BR>
+<A HREF="manual.html#pdf-math.floor">math.floor</A><BR>
+<A HREF="manual.html#pdf-math.fmod">math.fmod</A><BR>
+<A HREF="manual.html#pdf-math.huge">math.huge</A><BR>
+<A HREF="manual.html#pdf-math.log">math.log</A><BR>
+<A HREF="manual.html#pdf-math.max">math.max</A><BR>
+<A HREF="manual.html#pdf-math.maxinteger">math.maxinteger</A><BR>
+<A HREF="manual.html#pdf-math.min">math.min</A><BR>
+<A HREF="manual.html#pdf-math.mininteger">math.mininteger</A><BR>
+<A HREF="manual.html#pdf-math.modf">math.modf</A><BR>
+<A HREF="manual.html#pdf-math.pi">math.pi</A><BR>
+<A HREF="manual.html#pdf-math.rad">math.rad</A><BR>
+<A HREF="manual.html#pdf-math.random">math.random</A><BR>
+<A HREF="manual.html#pdf-math.randomseed">math.randomseed</A><BR>
+<A HREF="manual.html#pdf-math.sin">math.sin</A><BR>
+<A HREF="manual.html#pdf-math.sqrt">math.sqrt</A><BR>
+<A HREF="manual.html#pdf-math.tan">math.tan</A><BR>
+<A HREF="manual.html#pdf-math.tointeger">math.tointeger</A><BR>
+<A HREF="manual.html#pdf-math.type">math.type</A><BR>
+<A HREF="manual.html#pdf-math.ult">math.ult</A><BR>
+
+<P>
+<A HREF="manual.html#6.9">os</A><BR>
+<A HREF="manual.html#pdf-os.clock">os.clock</A><BR>
+<A HREF="manual.html#pdf-os.date">os.date</A><BR>
+<A HREF="manual.html#pdf-os.difftime">os.difftime</A><BR>
+<A HREF="manual.html#pdf-os.execute">os.execute</A><BR>
+<A HREF="manual.html#pdf-os.exit">os.exit</A><BR>
+<A HREF="manual.html#pdf-os.getenv">os.getenv</A><BR>
+<A HREF="manual.html#pdf-os.remove">os.remove</A><BR>
+<A HREF="manual.html#pdf-os.rename">os.rename</A><BR>
+<A HREF="manual.html#pdf-os.setlocale">os.setlocale</A><BR>
+<A HREF="manual.html#pdf-os.time">os.time</A><BR>
+<A HREF="manual.html#pdf-os.tmpname">os.tmpname</A><BR>
+
+<P>
+<A HREF="manual.html#6.3">package</A><BR>
+<A HREF="manual.html#pdf-package.config">package.config</A><BR>
+<A HREF="manual.html#pdf-package.cpath">package.cpath</A><BR>
+<A HREF="manual.html#pdf-package.loaded">package.loaded</A><BR>
+<A HREF="manual.html#pdf-package.loadlib">package.loadlib</A><BR>
+<A HREF="manual.html#pdf-package.path">package.path</A><BR>
+<A HREF="manual.html#pdf-package.preload">package.preload</A><BR>
+<A HREF="manual.html#pdf-package.searchers">package.searchers</A><BR>
+<A HREF="manual.html#pdf-package.searchpath">package.searchpath</A><BR>
+
+<P>
+<A HREF="manual.html#6.4">string</A><BR>
+<A HREF="manual.html#pdf-string.byte">string.byte</A><BR>
+<A HREF="manual.html#pdf-string.char">string.char</A><BR>
+<A HREF="manual.html#pdf-string.dump">string.dump</A><BR>
+<A HREF="manual.html#pdf-string.find">string.find</A><BR>
+<A HREF="manual.html#pdf-string.format">string.format</A><BR>
+<A HREF="manual.html#pdf-string.gmatch">string.gmatch</A><BR>
+<A HREF="manual.html#pdf-string.gsub">string.gsub</A><BR>
+<A HREF="manual.html#pdf-string.len">string.len</A><BR>
+<A HREF="manual.html#pdf-string.lower">string.lower</A><BR>
+<A HREF="manual.html#pdf-string.match">string.match</A><BR>
+<A HREF="manual.html#pdf-string.pack">string.pack</A><BR>
+<A HREF="manual.html#pdf-string.packsize">string.packsize</A><BR>
+<A HREF="manual.html#pdf-string.rep">string.rep</A><BR>
+<A HREF="manual.html#pdf-string.reverse">string.reverse</A><BR>
+<A HREF="manual.html#pdf-string.sub">string.sub</A><BR>
+<A HREF="manual.html#pdf-string.unpack">string.unpack</A><BR>
+<A HREF="manual.html#pdf-string.upper">string.upper</A><BR>
+
+<P>
+<A HREF="manual.html#6.6">table</A><BR>
+<A HREF="manual.html#pdf-table.concat">table.concat</A><BR>
+<A HREF="manual.html#pdf-table.insert">table.insert</A><BR>
+<A HREF="manual.html#pdf-table.move">table.move</A><BR>
+<A HREF="manual.html#pdf-table.pack">table.pack</A><BR>
+<A HREF="manual.html#pdf-table.remove">table.remove</A><BR>
+<A HREF="manual.html#pdf-table.sort">table.sort</A><BR>
+<A HREF="manual.html#pdf-table.unpack">table.unpack</A><BR>
+
+<P>
+<A HREF="manual.html#6.5">utf8</A><BR>
+<A HREF="manual.html#pdf-utf8.char">utf8.char</A><BR>
+<A HREF="manual.html#pdf-utf8.charpattern">utf8.charpattern</A><BR>
+<A HREF="manual.html#pdf-utf8.codepoint">utf8.codepoint</A><BR>
+<A HREF="manual.html#pdf-utf8.codes">utf8.codes</A><BR>
+<A HREF="manual.html#pdf-utf8.len">utf8.len</A><BR>
+<A HREF="manual.html#pdf-utf8.offset">utf8.offset</A><BR>
+
+<H3><A NAME="metamethods">metamethods</A></H3>
+<P>
+<A HREF="manual.html#2.4">__add</A><BR>
+<A HREF="manual.html#2.4">__band</A><BR>
+<A HREF="manual.html#2.4">__bnot</A><BR>
+<A HREF="manual.html#2.4">__bor</A><BR>
+<A HREF="manual.html#2.4">__bxor</A><BR>
+<A HREF="manual.html#2.4">__call</A><BR>
+<A HREF="manual.html#3.3.8">__close</A><BR>
+<A HREF="manual.html#2.4">__concat</A><BR>
+<A HREF="manual.html#2.4">__div</A><BR>
+<A HREF="manual.html#2.4">__eq</A><BR>
+<A HREF="manual.html#2.5.3">__gc</A><BR>
+<A HREF="manual.html#2.4">__idiv</A><BR>
+<A HREF="manual.html#2.4">__index</A><BR>
+<A HREF="manual.html#2.4">__le</A><BR>
+<A HREF="manual.html#2.4">__len</A><BR>
+<A HREF="manual.html#2.4">__lt</A><BR>
+<A HREF="manual.html#pdf-getmetatable">__metatable</A><BR>
+<A HREF="manual.html#2.4">__mod</A><BR>
+<A HREF="manual.html#2.5.4">__mode</A><BR>
+<A HREF="manual.html#2.4">__mul</A><BR>
+<A HREF="manual.html#luaL_newmetatable">__name</A><BR>
+<A HREF="manual.html#2.4">__newindex</A><BR>
+<A HREF="manual.html#pdf-pairs">__pairs</A><BR>
+<A HREF="manual.html#2.4">__pow</A><BR>
+<A HREF="manual.html#2.4">__shl</A><BR>
+<A HREF="manual.html#2.4">__shr</A><BR>
+<A HREF="manual.html#2.4">__sub</A><BR>
+<A HREF="manual.html#pdf-tostring">__tostring</A><BR>
+<A HREF="manual.html#2.4">__unm</A><BR>
+
+<H3><A NAME="env">environment<BR>variables</A></H3>
+<P>
+<A HREF="manual.html#pdf-LUA_CPATH">LUA_CPATH</A><BR>
+<A HREF="manual.html#pdf-LUA_CPATH_5_4">LUA_CPATH_5_4</A><BR>
+<A HREF="manual.html#pdf-LUA_INIT">LUA_INIT</A><BR>
+<A HREF="manual.html#pdf-LUA_INIT_5_4">LUA_INIT_5_4</A><BR>
+<A HREF="manual.html#pdf-LUA_PATH">LUA_PATH</A><BR>
+<A HREF="manual.html#pdf-LUA_PATH_5_4">LUA_PATH_5_4</A><BR>
+
+</TD>
+<TD>
+<H3><A NAME="api">C API</A></H3>
+<P>
+<A HREF="manual.html#lua_Alloc">lua_Alloc</A><BR>
+<A HREF="manual.html#lua_CFunction">lua_CFunction</A><BR>
+<A HREF="manual.html#lua_Debug">lua_Debug</A><BR>
+<A HREF="manual.html#lua_Hook">lua_Hook</A><BR>
+<A HREF="manual.html#lua_Integer">lua_Integer</A><BR>
+<A HREF="manual.html#lua_KContext">lua_KContext</A><BR>
+<A HREF="manual.html#lua_KFunction">lua_KFunction</A><BR>
+<A HREF="manual.html#lua_Number">lua_Number</A><BR>
+<A HREF="manual.html#lua_Reader">lua_Reader</A><BR>
+<A HREF="manual.html#lua_State">lua_State</A><BR>
+<A HREF="manual.html#lua_Unsigned">lua_Unsigned</A><BR>
+<A HREF="manual.html#lua_WarnFunction">lua_WarnFunction</A><BR>
+<A HREF="manual.html#lua_Writer">lua_Writer</A><BR>
+
+<P>
+<A HREF="manual.html#lua_absindex">lua_absindex</A><BR>
+<A HREF="manual.html#lua_arith">lua_arith</A><BR>
+<A HREF="manual.html#lua_atpanic">lua_atpanic</A><BR>
+<A HREF="manual.html#lua_call">lua_call</A><BR>
+<A HREF="manual.html#lua_callk">lua_callk</A><BR>
+<A HREF="manual.html#lua_checkstack">lua_checkstack</A><BR>
+<A HREF="manual.html#lua_close">lua_close</A><BR>
+<A HREF="manual.html#lua_closeslot">lua_closeslot</A><BR>
+<A HREF="manual.html#lua_compare">lua_compare</A><BR>
+<A HREF="manual.html#lua_concat">lua_concat</A><BR>
+<A HREF="manual.html#lua_copy">lua_copy</A><BR>
+<A HREF="manual.html#lua_createtable">lua_createtable</A><BR>
+<A HREF="manual.html#lua_dump">lua_dump</A><BR>
+<A HREF="manual.html#lua_error">lua_error</A><BR>
+<A HREF="manual.html#lua_gc">lua_gc</A><BR>
+<A HREF="manual.html#lua_getallocf">lua_getallocf</A><BR>
+<A HREF="manual.html#lua_getextraspace">lua_getextraspace</A><BR>
+<A HREF="manual.html#lua_getfield">lua_getfield</A><BR>
+<A HREF="manual.html#lua_getglobal">lua_getglobal</A><BR>
+<A HREF="manual.html#lua_gethook">lua_gethook</A><BR>
+<A HREF="manual.html#lua_gethookcount">lua_gethookcount</A><BR>
+<A HREF="manual.html#lua_gethookmask">lua_gethookmask</A><BR>
+<A HREF="manual.html#lua_geti">lua_geti</A><BR>
+<A HREF="manual.html#lua_getinfo">lua_getinfo</A><BR>
+<A HREF="manual.html#lua_getiuservalue">lua_getiuservalue</A><BR>
+<A HREF="manual.html#lua_getlocal">lua_getlocal</A><BR>
+<A HREF="manual.html#lua_getmetatable">lua_getmetatable</A><BR>
+<A HREF="manual.html#lua_getstack">lua_getstack</A><BR>
+<A HREF="manual.html#lua_gettable">lua_gettable</A><BR>
+<A HREF="manual.html#lua_gettop">lua_gettop</A><BR>
+<A HREF="manual.html#lua_getupvalue">lua_getupvalue</A><BR>
+<A HREF="manual.html#lua_insert">lua_insert</A><BR>
+<A HREF="manual.html#lua_isboolean">lua_isboolean</A><BR>
+<A HREF="manual.html#lua_iscfunction">lua_iscfunction</A><BR>
+<A HREF="manual.html#lua_isfunction">lua_isfunction</A><BR>
+<A HREF="manual.html#lua_isinteger">lua_isinteger</A><BR>
+<A HREF="manual.html#lua_islightuserdata">lua_islightuserdata</A><BR>
+<A HREF="manual.html#lua_isnil">lua_isnil</A><BR>
+<A HREF="manual.html#lua_isnone">lua_isnone</A><BR>
+<A HREF="manual.html#lua_isnoneornil">lua_isnoneornil</A><BR>
+<A HREF="manual.html#lua_isnumber">lua_isnumber</A><BR>
+<A HREF="manual.html#lua_isstring">lua_isstring</A><BR>
+<A HREF="manual.html#lua_istable">lua_istable</A><BR>
+<A HREF="manual.html#lua_isthread">lua_isthread</A><BR>
+<A HREF="manual.html#lua_isuserdata">lua_isuserdata</A><BR>
+<A HREF="manual.html#lua_isyieldable">lua_isyieldable</A><BR>
+<A HREF="manual.html#lua_len">lua_len</A><BR>
+<A HREF="manual.html#lua_load">lua_load</A><BR>
+<A HREF="manual.html#lua_newstate">lua_newstate</A><BR>
+<A HREF="manual.html#lua_newtable">lua_newtable</A><BR>
+<A HREF="manual.html#lua_newthread">lua_newthread</A><BR>
+<A HREF="manual.html#lua_newuserdatauv">lua_newuserdatauv</A><BR>
+<A HREF="manual.html#lua_next">lua_next</A><BR>
+<A HREF="manual.html#lua_numbertointeger">lua_numbertointeger</A><BR>
+<A HREF="manual.html#lua_pcall">lua_pcall</A><BR>
+<A HREF="manual.html#lua_pcallk">lua_pcallk</A><BR>
+<A HREF="manual.html#lua_pop">lua_pop</A><BR>
+<A HREF="manual.html#lua_pushboolean">lua_pushboolean</A><BR>
+<A HREF="manual.html#lua_pushcclosure">lua_pushcclosure</A><BR>
+<A HREF="manual.html#lua_pushcfunction">lua_pushcfunction</A><BR>
+<A HREF="manual.html#lua_pushfstring">lua_pushfstring</A><BR>
+<A HREF="manual.html#lua_pushglobaltable">lua_pushglobaltable</A><BR>
+<A HREF="manual.html#lua_pushinteger">lua_pushinteger</A><BR>
+<A HREF="manual.html#lua_pushlightuserdata">lua_pushlightuserdata</A><BR>
+<A HREF="manual.html#lua_pushliteral">lua_pushliteral</A><BR>
+<A HREF="manual.html#lua_pushlstring">lua_pushlstring</A><BR>
+<A HREF="manual.html#lua_pushnil">lua_pushnil</A><BR>
+<A HREF="manual.html#lua_pushnumber">lua_pushnumber</A><BR>
+<A HREF="manual.html#lua_pushstring">lua_pushstring</A><BR>
+<A HREF="manual.html#lua_pushthread">lua_pushthread</A><BR>
+<A HREF="manual.html#lua_pushvalue">lua_pushvalue</A><BR>
+<A HREF="manual.html#lua_pushvfstring">lua_pushvfstring</A><BR>
+<A HREF="manual.html#lua_rawequal">lua_rawequal</A><BR>
+<A HREF="manual.html#lua_rawget">lua_rawget</A><BR>
+<A HREF="manual.html#lua_rawgeti">lua_rawgeti</A><BR>
+<A HREF="manual.html#lua_rawgetp">lua_rawgetp</A><BR>
+<A HREF="manual.html#lua_rawlen">lua_rawlen</A><BR>
+<A HREF="manual.html#lua_rawset">lua_rawset</A><BR>
+<A HREF="manual.html#lua_rawseti">lua_rawseti</A><BR>
+<A HREF="manual.html#lua_rawsetp">lua_rawsetp</A><BR>
+<A HREF="manual.html#lua_register">lua_register</A><BR>
+<A HREF="manual.html#lua_remove">lua_remove</A><BR>
+<A HREF="manual.html#lua_replace">lua_replace</A><BR>
+<A HREF="manual.html#lua_resetthread">lua_resetthread</A><BR>
+<A HREF="manual.html#lua_resume">lua_resume</A><BR>
+<A HREF="manual.html#lua_rotate">lua_rotate</A><BR>
+<A HREF="manual.html#lua_setallocf">lua_setallocf</A><BR>
+<A HREF="manual.html#lua_setfield">lua_setfield</A><BR>
+<A HREF="manual.html#lua_setglobal">lua_setglobal</A><BR>
+<A HREF="manual.html#lua_sethook">lua_sethook</A><BR>
+<A HREF="manual.html#lua_seti">lua_seti</A><BR>
+<A HREF="manual.html#lua_setiuservalue">lua_setiuservalue</A><BR>
+<A HREF="manual.html#lua_setlocal">lua_setlocal</A><BR>
+<A HREF="manual.html#lua_setmetatable">lua_setmetatable</A><BR>
+<A HREF="manual.html#lua_settable">lua_settable</A><BR>
+<A HREF="manual.html#lua_settop">lua_settop</A><BR>
+<A HREF="manual.html#lua_setupvalue">lua_setupvalue</A><BR>
+<A HREF="manual.html#lua_setwarnf">lua_setwarnf</A><BR>
+<A HREF="manual.html#lua_status">lua_status</A><BR>
+<A HREF="manual.html#lua_stringtonumber">lua_stringtonumber</A><BR>
+<A HREF="manual.html#lua_toboolean">lua_toboolean</A><BR>
+<A HREF="manual.html#lua_tocfunction">lua_tocfunction</A><BR>
+<A HREF="manual.html#lua_toclose">lua_toclose</A><BR>
+<A HREF="manual.html#lua_tointeger">lua_tointeger</A><BR>
+<A HREF="manual.html#lua_tointegerx">lua_tointegerx</A><BR>
+<A HREF="manual.html#lua_tolstring">lua_tolstring</A><BR>
+<A HREF="manual.html#lua_tonumber">lua_tonumber</A><BR>
+<A HREF="manual.html#lua_tonumberx">lua_tonumberx</A><BR>
+<A HREF="manual.html#lua_topointer">lua_topointer</A><BR>
+<A HREF="manual.html#lua_tostring">lua_tostring</A><BR>
+<A HREF="manual.html#lua_tothread">lua_tothread</A><BR>
+<A HREF="manual.html#lua_touserdata">lua_touserdata</A><BR>
+<A HREF="manual.html#lua_type">lua_type</A><BR>
+<A HREF="manual.html#lua_typename">lua_typename</A><BR>
+<A HREF="manual.html#lua_upvalueid">lua_upvalueid</A><BR>
+<A HREF="manual.html#lua_upvalueindex">lua_upvalueindex</A><BR>
+<A HREF="manual.html#lua_upvaluejoin">lua_upvaluejoin</A><BR>
+<A HREF="manual.html#lua_version">lua_version</A><BR>
+<A HREF="manual.html#lua_warning">lua_warning</A><BR>
+<A HREF="manual.html#lua_xmove">lua_xmove</A><BR>
+<A HREF="manual.html#lua_yield">lua_yield</A><BR>
+<A HREF="manual.html#lua_yieldk">lua_yieldk</A><BR>
+
+</TD>
+<TD>
+<H3><A NAME="auxlib">auxiliary library</A></H3>
+<P>
+<A HREF="manual.html#luaL_Buffer">luaL_Buffer</A><BR>
+<A HREF="manual.html#luaL_Reg">luaL_Reg</A><BR>
+<A HREF="manual.html#luaL_Stream">luaL_Stream</A><BR>
+
+<P>
+<A HREF="manual.html#luaL_addchar">luaL_addchar</A><BR>
+<A HREF="manual.html#luaL_addgsub">luaL_addgsub</A><BR>
+<A HREF="manual.html#luaL_addlstring">luaL_addlstring</A><BR>
+<A HREF="manual.html#luaL_addsize">luaL_addsize</A><BR>
+<A HREF="manual.html#luaL_addstring">luaL_addstring</A><BR>
+<A HREF="manual.html#luaL_addvalue">luaL_addvalue</A><BR>
+<A HREF="manual.html#luaL_argcheck">luaL_argcheck</A><BR>
+<A HREF="manual.html#luaL_argerror">luaL_argerror</A><BR>
+<A HREF="manual.html#luaL_argexpected">luaL_argexpected</A><BR>
+<A HREF="manual.html#luaL_buffaddr">luaL_buffaddr</A><BR>
+<A HREF="manual.html#luaL_buffinit">luaL_buffinit</A><BR>
+<A HREF="manual.html#luaL_buffinitsize">luaL_buffinitsize</A><BR>
+<A HREF="manual.html#luaL_bufflen">luaL_bufflen</A><BR>
+<A HREF="manual.html#luaL_buffsub">luaL_buffsub</A><BR>
+<A HREF="manual.html#luaL_callmeta">luaL_callmeta</A><BR>
+<A HREF="manual.html#luaL_checkany">luaL_checkany</A><BR>
+<A HREF="manual.html#luaL_checkinteger">luaL_checkinteger</A><BR>
+<A HREF="manual.html#luaL_checklstring">luaL_checklstring</A><BR>
+<A HREF="manual.html#luaL_checknumber">luaL_checknumber</A><BR>
+<A HREF="manual.html#luaL_checkoption">luaL_checkoption</A><BR>
+<A HREF="manual.html#luaL_checkstack">luaL_checkstack</A><BR>
+<A HREF="manual.html#luaL_checkstring">luaL_checkstring</A><BR>
+<A HREF="manual.html#luaL_checktype">luaL_checktype</A><BR>
+<A HREF="manual.html#luaL_checkudata">luaL_checkudata</A><BR>
+<A HREF="manual.html#luaL_checkversion">luaL_checkversion</A><BR>
+<A HREF="manual.html#luaL_dofile">luaL_dofile</A><BR>
+<A HREF="manual.html#luaL_dostring">luaL_dostring</A><BR>
+<A HREF="manual.html#luaL_error">luaL_error</A><BR>
+<A HREF="manual.html#luaL_execresult">luaL_execresult</A><BR>
+<A HREF="manual.html#luaL_fileresult">luaL_fileresult</A><BR>
+<A HREF="manual.html#luaL_getmetafield">luaL_getmetafield</A><BR>
+<A HREF="manual.html#luaL_getmetatable">luaL_getmetatable</A><BR>
+<A HREF="manual.html#luaL_getsubtable">luaL_getsubtable</A><BR>
+<A HREF="manual.html#luaL_gsub">luaL_gsub</A><BR>
+<A HREF="manual.html#luaL_len">luaL_len</A><BR>
+<A HREF="manual.html#luaL_loadbuffer">luaL_loadbuffer</A><BR>
+<A HREF="manual.html#luaL_loadbufferx">luaL_loadbufferx</A><BR>
+<A HREF="manual.html#luaL_loadfile">luaL_loadfile</A><BR>
+<A HREF="manual.html#luaL_loadfilex">luaL_loadfilex</A><BR>
+<A HREF="manual.html#luaL_loadstring">luaL_loadstring</A><BR>
+<A HREF="manual.html#luaL_newlib">luaL_newlib</A><BR>
+<A HREF="manual.html#luaL_newlibtable">luaL_newlibtable</A><BR>
+<A HREF="manual.html#luaL_newmetatable">luaL_newmetatable</A><BR>
+<A HREF="manual.html#luaL_newstate">luaL_newstate</A><BR>
+<A HREF="manual.html#luaL_openlibs">luaL_openlibs</A><BR>
+<A HREF="manual.html#luaL_opt">luaL_opt</A><BR>
+<A HREF="manual.html#luaL_optinteger">luaL_optinteger</A><BR>
+<A HREF="manual.html#luaL_optlstring">luaL_optlstring</A><BR>
+<A HREF="manual.html#luaL_optnumber">luaL_optnumber</A><BR>
+<A HREF="manual.html#luaL_optstring">luaL_optstring</A><BR>
+<A HREF="manual.html#luaL_prepbuffer">luaL_prepbuffer</A><BR>
+<A HREF="manual.html#luaL_prepbuffsize">luaL_prepbuffsize</A><BR>
+<A HREF="manual.html#luaL_pushfail">luaL_pushfail</A><BR>
+<A HREF="manual.html#luaL_pushresult">luaL_pushresult</A><BR>
+<A HREF="manual.html#luaL_pushresultsize">luaL_pushresultsize</A><BR>
+<A HREF="manual.html#luaL_ref">luaL_ref</A><BR>
+<A HREF="manual.html#luaL_requiref">luaL_requiref</A><BR>
+<A HREF="manual.html#luaL_setfuncs">luaL_setfuncs</A><BR>
+<A HREF="manual.html#luaL_setmetatable">luaL_setmetatable</A><BR>
+<A HREF="manual.html#luaL_testudata">luaL_testudata</A><BR>
+<A HREF="manual.html#luaL_tolstring">luaL_tolstring</A><BR>
+<A HREF="manual.html#luaL_traceback">luaL_traceback</A><BR>
+<A HREF="manual.html#luaL_typeerror">luaL_typeerror</A><BR>
+<A HREF="manual.html#luaL_typename">luaL_typename</A><BR>
+<A HREF="manual.html#luaL_unref">luaL_unref</A><BR>
+<A HREF="manual.html#luaL_where">luaL_where</A><BR>
+
+<H3><A NAME="library">standard library</A></H3>
+<P>
+<A HREF="manual.html#pdf-luaopen_base">luaopen_base</A><BR>
+<A HREF="manual.html#pdf-luaopen_coroutine">luaopen_coroutine</A><BR>
+<A HREF="manual.html#pdf-luaopen_debug">luaopen_debug</A><BR>
+<A HREF="manual.html#pdf-luaopen_io">luaopen_io</A><BR>
+<A HREF="manual.html#pdf-luaopen_math">luaopen_math</A><BR>
+<A HREF="manual.html#pdf-luaopen_os">luaopen_os</A><BR>
+<A HREF="manual.html#pdf-luaopen_package">luaopen_package</A><BR>
+<A HREF="manual.html#pdf-luaopen_string">luaopen_string</A><BR>
+<A HREF="manual.html#pdf-luaopen_table">luaopen_table</A><BR>
+<A HREF="manual.html#pdf-luaopen_utf8">luaopen_utf8</A><BR>
+
+<H3><A NAME="constants">constants</A></H3>
+<P>
+<A HREF="manual.html#pdf-LUA_ERRERR">LUA_ERRERR</A><BR>
+<A HREF="manual.html#pdf-LUA_ERRFILE">LUA_ERRFILE</A><BR>
+<A HREF="manual.html#pdf-LUA_ERRMEM">LUA_ERRMEM</A><BR>
+<A HREF="manual.html#pdf-LUA_ERRRUN">LUA_ERRRUN</A><BR>
+<A HREF="manual.html#pdf-LUA_ERRSYNTAX">LUA_ERRSYNTAX</A><BR>
+<A HREF="manual.html#pdf-LUA_HOOKCALL">LUA_HOOKCALL</A><BR>
+<A HREF="manual.html#pdf-LUA_HOOKCOUNT">LUA_HOOKCOUNT</A><BR>
+<A HREF="manual.html#pdf-LUA_HOOKLINE">LUA_HOOKLINE</A><BR>
+<A HREF="manual.html#pdf-LUA_HOOKRET">LUA_HOOKRET</A><BR>
+<A HREF="manual.html#pdf-LUA_HOOKTAILCALL">LUA_HOOKTAILCALL</A><BR>
+<A HREF="manual.html#pdf-LUA_LOADED_TABLE">LUA_LOADED_TABLE</A><BR>
+<A HREF="manual.html#pdf-LUA_MASKCALL">LUA_MASKCALL</A><BR>
+<A HREF="manual.html#pdf-LUA_MASKCOUNT">LUA_MASKCOUNT</A><BR>
+<A HREF="manual.html#pdf-LUA_MASKLINE">LUA_MASKLINE</A><BR>
+<A HREF="manual.html#pdf-LUA_MASKRET">LUA_MASKRET</A><BR>
+<A HREF="manual.html#pdf-LUA_MAXINTEGER">LUA_MAXINTEGER</A><BR>
+<A HREF="manual.html#pdf-LUA_MININTEGER">LUA_MININTEGER</A><BR>
+<A HREF="manual.html#pdf-LUA_MINSTACK">LUA_MINSTACK</A><BR>
+<A HREF="manual.html#pdf-LUA_MULTRET">LUA_MULTRET</A><BR>
+<A HREF="manual.html#pdf-LUA_NOREF">LUA_NOREF</A><BR>
+<A HREF="manual.html#pdf-LUA_OK">LUA_OK</A><BR>
+<A HREF="manual.html#pdf-LUA_OPADD">LUA_OPADD</A><BR>
+<A HREF="manual.html#pdf-LUA_OPBAND">LUA_OPBAND</A><BR>
+<A HREF="manual.html#pdf-LUA_OPBNOT">LUA_OPBNOT</A><BR>
+<A HREF="manual.html#pdf-LUA_OPBOR">LUA_OPBOR</A><BR>
+<A HREF="manual.html#pdf-LUA_OPBXOR">LUA_OPBXOR</A><BR>
+<A HREF="manual.html#pdf-LUA_OPDIV">LUA_OPDIV</A><BR>
+<A HREF="manual.html#pdf-LUA_OPEQ">LUA_OPEQ</A><BR>
+<A HREF="manual.html#pdf-LUA_OPIDIV">LUA_OPIDIV</A><BR>
+<A HREF="manual.html#pdf-LUA_OPLE">LUA_OPLE</A><BR>
+<A HREF="manual.html#pdf-LUA_OPLT">LUA_OPLT</A><BR>
+<A HREF="manual.html#pdf-LUA_OPMOD">LUA_OPMOD</A><BR>
+<A HREF="manual.html#pdf-LUA_OPMUL">LUA_OPMUL</A><BR>
+<A HREF="manual.html#pdf-LUA_OPPOW">LUA_OPPOW</A><BR>
+<A HREF="manual.html#pdf-LUA_OPSHL">LUA_OPSHL</A><BR>
+<A HREF="manual.html#pdf-LUA_OPSHR">LUA_OPSHR</A><BR>
+<A HREF="manual.html#pdf-LUA_OPSUB">LUA_OPSUB</A><BR>
+<A HREF="manual.html#pdf-LUA_OPUNM">LUA_OPUNM</A><BR>
+<A HREF="manual.html#pdf-LUA_PRELOAD_TABLE">LUA_PRELOAD_TABLE</A><BR>
+<A HREF="manual.html#pdf-LUA_REFNIL">LUA_REFNIL</A><BR>
+<A HREF="manual.html#pdf-LUA_REGISTRYINDEX">LUA_REGISTRYINDEX</A><BR>
+<A HREF="manual.html#pdf-LUA_RIDX_GLOBALS">LUA_RIDX_GLOBALS</A><BR>
+<A HREF="manual.html#pdf-LUA_RIDX_MAINTHREAD">LUA_RIDX_MAINTHREAD</A><BR>
+<A HREF="manual.html#pdf-LUA_TBOOLEAN">LUA_TBOOLEAN</A><BR>
+<A HREF="manual.html#pdf-LUA_TFUNCTION">LUA_TFUNCTION</A><BR>
+<A HREF="manual.html#pdf-LUA_TLIGHTUSERDATA">LUA_TLIGHTUSERDATA</A><BR>
+<A HREF="manual.html#pdf-LUA_TNIL">LUA_TNIL</A><BR>
+<A HREF="manual.html#pdf-LUA_TNONE">LUA_TNONE</A><BR>
+<A HREF="manual.html#pdf-LUA_TNUMBER">LUA_TNUMBER</A><BR>
+<A HREF="manual.html#pdf-LUA_TSTRING">LUA_TSTRING</A><BR>
+<A HREF="manual.html#pdf-LUA_TTABLE">LUA_TTABLE</A><BR>
+<A HREF="manual.html#pdf-LUA_TTHREAD">LUA_TTHREAD</A><BR>
+<A HREF="manual.html#pdf-LUA_TUSERDATA">LUA_TUSERDATA</A><BR>
+<A HREF="manual.html#pdf-LUA_USE_APICHECK">LUA_USE_APICHECK</A><BR>
+<A HREF="manual.html#pdf-LUA_YIELD">LUA_YIELD</A><BR>
+<A HREF="manual.html#pdf-LUAL_BUFFERSIZE">LUAL_BUFFERSIZE</A><BR>
+
+</TD>
+</TR>
+</TABLE>
+
+<P CLASS="footer">
+Last update:
+Sat Apr 1 17:57:05 UTC 2023
+</P>
+<!--
+Last change: revised for Lua 5.4.5
+-->
+
+</BODY>
+</HTML>
diff --git a/src/libs/3rdparty/lua/doc/index.css b/src/libs/3rdparty/lua/doc/index.css
new file mode 100644
index 0000000000..c961835731
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/index.css
@@ -0,0 +1,21 @@
+ul {
+ list-style-type: none ;
+}
+
+ul.contents {
+ padding: 0 ;
+}
+
+table {
+ border: none ;
+ border-spacing: 0 ;
+ border-collapse: collapse ;
+}
+
+td {
+ vertical-align: top ;
+ padding: 0 ;
+ text-align: left ;
+ line-height: 1.25 ;
+ width: 15% ;
+}
diff --git a/src/libs/3rdparty/lua/doc/logo.gif b/src/libs/3rdparty/lua/doc/logo.gif
new file mode 100644
index 0000000000..5c77eacc3b
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/logo.gif
Binary files differ
diff --git a/src/libs/3rdparty/lua/doc/lua.1 b/src/libs/3rdparty/lua/doc/lua.1
new file mode 100644
index 0000000000..3f472fd81f
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/lua.1
@@ -0,0 +1,155 @@
+.\" $Id: lua.man,v 1.14 2022/09/23 09:06:36 lhf Exp $
+.TH LUA 1 "$Date: 2022/09/23 09:06:36 $"
+.SH NAME
+lua \- Lua interpreter
+.SH SYNOPSIS
+.B lua
+[
+.I options
+]
+[
+.I script
+[
+.I args
+]
+]
+.SH DESCRIPTION
+.B lua
+is the standalone Lua interpreter.
+It loads and executes Lua programs,
+either in textual source form or
+in precompiled binary form.
+(Precompiled binaries are output by
+.BR luac ,
+the Lua compiler.)
+.B lua
+can be used as a batch interpreter and also interactively.
+.LP
+After handling the
+.IR options ,
+the Lua program in file
+.I script
+is loaded and executed.
+The
+.I args
+are available to
+.I script
+as strings in a global table named
+.B arg
+and also as arguments to its main function.
+When called without arguments,
+.B lua
+behaves as
+.B "lua \-v \-i"
+if the standard input is a terminal,
+and as
+.B "lua \-"
+otherwise.
+.LP
+In interactive mode,
+.B lua
+prompts the user,
+reads lines from the standard input,
+and executes them as they are read.
+If the line contains an expression,
+then the line is evaluated and the result is printed.
+If a line does not contain a complete statement,
+then a secondary prompt is displayed and
+lines are read until a complete statement is formed or
+a syntax error is found.
+.LP
+Before handling command line options and scripts,
+.B lua
+checks the contents of the environment variables
+.B LUA_INIT_5_4
+and
+.BR LUA_INIT ,
+in that order.
+If the contents are of the form
+.RI '@ filename ',
+then
+.I filename
+is executed.
+Otherwise, the contents are assumed to be a Lua statement and is executed.
+When
+.B LUA_INIT_5_4
+is defined,
+.B LUA_INIT
+is ignored.
+.SH OPTIONS
+.TP
+.BI \-e " stat"
+execute statement
+.IR stat .
+.TP
+.B \-i
+enter interactive mode after executing
+.IR script .
+.TP
+.BI \-l " mod"
+require library
+.I mod
+into global
+.IR mod .
+.TP
+.BI \-l " g=mod"
+require library
+.I mod
+into global
+.IR g .
+.TP
+.B \-v
+show version information.
+.TP
+.B \-E
+ignore environment variables.
+.TP
+.B \-W
+turn warnings on.
+.TP
+.B \-\-
+stop handling options.
+.TP
+.B \-
+stop handling options and execute the standard input as a file.
+.SH ENVIRONMENT VARIABLES
+The following environment variables affect the execution of
+.BR lua .
+When defined,
+the version-specific variants take priority
+and the version-neutral variants are ignored.
+.TP
+.B LUA_INIT, LUA_INIT_5_4
+Code to be executed before command line options and scripts.
+.TP
+.B LUA_PATH, LUA_PATH_5_4
+Initial value of package.cpath,
+the path used by require to search for Lua loaders.
+.TP
+.B LUA_CPATH, LUA_CPATH_5_4
+Initial value of package.cpath,
+the path used by require to search for C loaders.
+.SH EXIT STATUS
+If a script calls os.exit,
+then
+.B lua
+exits with the given exit status.
+Otherwise,
+.B lua
+exits
+with EXIT_SUCCESS (0 on POSIX systems) if there were no errors
+and
+with EXIT_FAILURE (1 on POSIX systems) if there were errors.
+Errors raised in interactive mode do not cause exits.
+.SH DIAGNOSTICS
+Error messages should be self explanatory.
+.SH "SEE ALSO"
+.BR luac (1)
+.br
+The documentation at lua.org,
+especially section 7 of the reference manual.
+.SH AUTHORS
+R. Ierusalimschy,
+L. H. de Figueiredo,
+W. Celes
+.\" EOF
diff --git a/src/libs/3rdparty/lua/doc/lua.css b/src/libs/3rdparty/lua/doc/lua.css
new file mode 100644
index 0000000000..cbd0799d15
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/lua.css
@@ -0,0 +1,161 @@
+html {
+ background-color: #F8F8F8 ;
+}
+
+body {
+ background-color: #FFFFFF ;
+ color: #000000 ;
+ font-family: Helvetica, Arial, sans-serif ;
+ text-align: justify ;
+ line-height: 1.25 ;
+ margin: 16px auto ;
+ padding: 32px ;
+ border: solid #ccc 1px ;
+ border-radius: 20px ;
+ max-width: 70em ;
+ width: 90% ;
+}
+
+h1, h2, h3, h4 {
+ color: #000080 ;
+ font-family: Verdana, Geneva, sans-serif ;
+ font-weight: normal ;
+ font-style: normal ;
+ text-align: left ;
+}
+
+h1 {
+ font-size: 28pt ;
+}
+
+h1 img {
+ vertical-align: text-bottom ;
+}
+
+h2:before {
+ content: "\2756" ;
+ padding-right: 0.5em ;
+}
+
+a {
+ text-decoration: none ;
+}
+
+a:link {
+ color: #000080 ;
+}
+
+a:link:hover, a:visited:hover {
+ background-color: #D0D0FF ;
+ color: #000080 ;
+ border-radius: 4px ;
+}
+
+a:link:active, a:visited:active {
+ color: #FF0000 ;
+}
+
+div.menubar {
+ padding-bottom: 0.5em ;
+}
+
+p.menubar {
+ margin-left: 2.5em ;
+}
+
+.menubar a:hover {
+ margin: -3px -3px -3px -3px ;
+ padding: 3px 3px 3px 3px ;
+ border-radius: 4px ;
+}
+
+:target {
+ background-color: #F0F0F0 ;
+ margin: -8px ;
+ padding: 8px ;
+ border-radius: 8px ;
+ outline: none ;
+}
+
+hr {
+ display: none ;
+}
+
+table hr {
+ background-color: #a0a0a0 ;
+ color: #a0a0a0 ;
+ border: 0 ;
+ height: 1px ;
+ display: block ;
+}
+
+.footer {
+ color: gray ;
+ font-size: x-small ;
+ text-transform: lowercase ;
+}
+
+input[type=text] {
+ border: solid #a0a0a0 2px ;
+ border-radius: 2em ;
+ background-image: url('images/search.png') ;
+ background-repeat: no-repeat ;
+ background-position: 4px center ;
+ padding-left: 20px ;
+ height: 2em ;
+}
+
+pre.session {
+ background-color: #F8F8F8 ;
+ padding: 1em ;
+ border-radius: 8px ;
+}
+
+table {
+ border: none ;
+ border-spacing: 0 ;
+ border-collapse: collapse ;
+}
+
+td {
+ padding: 0 ;
+ margin: 0 ;
+}
+
+td.gutter {
+ width: 4% ;
+}
+
+table.columns td {
+ vertical-align: top ;
+ padding-bottom: 1em ;
+ text-align: justify ;
+ line-height: 1.25 ;
+}
+
+table.book td {
+ vertical-align: top ;
+}
+
+table.book td.cover {
+ padding-right: 1em ;
+}
+
+table.book img {
+ border: solid #000080 1px ;
+}
+
+table.book span {
+ font-size: small ;
+ text-align: left ;
+ display: block ;
+ margin-top: 0.25em ;
+}
+
+p.logos a:link:hover, p.logos a:visited:hover {
+ background-color: inherit ;
+}
+
+img {
+ background-color: white ;
+}
diff --git a/src/libs/3rdparty/lua/doc/luac.1 b/src/libs/3rdparty/lua/doc/luac.1
new file mode 100644
index 0000000000..33a4ed00ac
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/luac.1
@@ -0,0 +1,118 @@
+.\" $Id: luac.man,v 1.29 2011/11/16 13:53:40 lhf Exp $
+.TH LUAC 1 "$Date: 2011/11/16 13:53:40 $"
+.SH NAME
+luac \- Lua compiler
+.SH SYNOPSIS
+.B luac
+[
+.I options
+] [
+.I filenames
+]
+.SH DESCRIPTION
+.B luac
+is the Lua compiler.
+It translates programs written in the Lua programming language
+into binary files containing precompiled chunks
+that can be later loaded and executed.
+.LP
+The main advantages of precompiling chunks are:
+faster loading,
+protecting source code from accidental user changes,
+and
+off-line syntax checking.
+Precompiling does not imply faster execution
+because in Lua chunks are always compiled into bytecodes before being executed.
+.B luac
+simply allows those bytecodes to be saved in a file for later execution.
+Precompiled chunks are not necessarily smaller than the corresponding source.
+The main goal in precompiling is faster loading.
+.LP
+In the command line,
+you can mix
+text files containing Lua source and
+binary files containing precompiled chunks.
+.B luac
+produces a single output file containing the combined bytecodes
+for all files given.
+Executing the combined file is equivalent to executing the given files.
+By default,
+the output file is named
+.BR luac.out ,
+but you can change this with the
+.B \-o
+option.
+.LP
+Precompiled chunks are
+.I not
+portable across different architectures.
+Moreover,
+the internal format of precompiled chunks
+is likely to change when a new version of Lua is released.
+Make sure you save the source files of all Lua programs that you precompile.
+.LP
+.SH OPTIONS
+.TP
+.B \-l
+produce a listing of the compiled bytecode for Lua's virtual machine.
+Listing bytecodes is useful to learn about Lua's virtual machine.
+If no files are given, then
+.B luac
+loads
+.B luac.out
+and lists its contents.
+Use
+.B \-l \-l
+for a full listing.
+.TP
+.BI \-o " file"
+output to
+.IR file ,
+instead of the default
+.BR luac.out .
+(You can use
+.B "'\-'"
+for standard output,
+but not on platforms that open standard output in text mode.)
+The output file may be one of the given files because
+all files are loaded before the output file is written.
+Be careful not to overwrite precious files.
+.TP
+.B \-p
+load files but do not generate any output file.
+Used mainly for syntax checking and for testing precompiled chunks:
+corrupted files will probably generate errors when loaded.
+If no files are given, then
+.B luac
+loads
+.B luac.out
+and tests its contents.
+No messages are displayed if the file loads without errors.
+.TP
+.B \-s
+strip debug information before writing the output file.
+This saves some space in very large chunks,
+but if errors occur when running a stripped chunk,
+then the error messages may not contain the full information they usually do.
+In particular,
+line numbers and names of local variables are lost.
+.TP
+.B \-v
+show version information.
+.TP
+.B \-\-
+stop handling options.
+.TP
+.B \-
+stop handling options and process standard input.
+.SH "SEE ALSO"
+.BR lua (1)
+.br
+The documentation at lua.org.
+.SH DIAGNOSTICS
+Error messages should be self explanatory.
+.SH AUTHORS
+R. Ierusalimschy,
+L. H. de Figueiredo,
+W. Celes
+.\" EOF
diff --git a/src/libs/3rdparty/lua/doc/manual.css b/src/libs/3rdparty/lua/doc/manual.css
new file mode 100644
index 0000000000..aa0e677dd5
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/manual.css
@@ -0,0 +1,21 @@
+h3 code {
+ font-family: inherit ;
+ font-size: inherit ;
+}
+
+pre, code {
+ font-size: 12pt ;
+}
+
+span.apii {
+ color: gray ;
+ float: right ;
+ font-family: inherit ;
+ font-style: normal ;
+ font-size: small ;
+}
+
+h2:before {
+ content: "" ;
+ padding-right: 0em ;
+}
diff --git a/src/libs/3rdparty/lua/doc/manual.html b/src/libs/3rdparty/lua/doc/manual.html
new file mode 100644
index 0000000000..0af688b343
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/manual.html
@@ -0,0 +1,12046 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML>
+<HEAD>
+<TITLE>Lua 5.4 Reference Manual</TITLE>
+<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
+<LINK REL="stylesheet" TYPE="text/css" HREF="manual.css">
+<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
+</HEAD>
+
+<BODY>
+
+<H1>
+<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
+Lua 5.4 Reference Manual
+</H1>
+
+<P>
+by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
+
+<P>
+<SMALL>
+Copyright &copy; 2020&ndash;2023 Lua.org, PUC-Rio.
+Freely available under the terms of the
+<a href="http://www.lua.org/license.html">Lua license</a>.
+</SMALL>
+
+<DIV CLASS="menubar">
+<A HREF="contents.html#contents">contents</A>
+&middot;
+<A HREF="contents.html#index">index</A>
+&middot;
+<A HREF="http://www.lua.org/manual/">other versions</A>
+</DIV>
+
+<!-- ====================================================================== -->
+<p>
+
+<!-- $Id: manual.of $ -->
+
+
+
+
+<h1>1 &ndash; <a name="1">Introduction</a></h1>
+
+<p>
+Lua is a powerful, efficient, lightweight, embeddable scripting language.
+It supports procedural programming,
+object-oriented programming, functional programming,
+data-driven programming, and data description.
+
+
+<p>
+Lua combines simple procedural syntax with powerful data description
+constructs based on associative arrays and extensible semantics.
+Lua is dynamically typed,
+runs by interpreting bytecode with a register-based
+virtual machine,
+and has automatic memory management with
+a generational garbage collection,
+making it ideal for configuration, scripting,
+and rapid prototyping.
+
+
+<p>
+Lua is implemented as a library, written in <em>clean C</em>,
+the common subset of standard&nbsp;C and C++.
+The Lua distribution includes a host program called <code>lua</code>,
+which uses the Lua library to offer a complete,
+standalone Lua interpreter,
+for interactive or batch use.
+Lua is intended to be used both as a powerful, lightweight,
+embeddable scripting language for any program that needs one,
+and as a powerful but lightweight and efficient stand-alone language.
+
+
+<p>
+As an extension language, Lua has no notion of a "main" program:
+it works <em>embedded</em> in a host client,
+called the <em>embedding program</em> or simply the <em>host</em>.
+(Frequently, this host is the stand-alone <code>lua</code> program.)
+The host program can invoke functions to execute a piece of Lua code,
+can write and read Lua variables,
+and can register C&nbsp;functions to be called by Lua code.
+Through the use of C&nbsp;functions, Lua can be augmented to cope with
+a wide range of different domains,
+thus creating customized programming languages sharing a syntactical framework.
+
+
+<p>
+Lua is free software,
+and is provided as usual with no guarantees,
+as stated in its license.
+The implementation described in this manual is available
+at Lua's official web site, <code>www.lua.org</code>.
+
+
+<p>
+Like any other reference manual,
+this document is dry in places.
+For a discussion of the decisions behind the design of Lua,
+see the technical papers available at Lua's web site.
+For a detailed introduction to programming in Lua,
+see Roberto's book, <em>Programming in Lua</em>.
+
+
+
+<h1>2 &ndash; <a name="2">Basic Concepts</a></h1>
+
+
+
+<p>
+This section describes the basic concepts of the language.
+
+
+
+
+
+<h2>2.1 &ndash; <a name="2.1">Values and Types</a></h2>
+
+<p>
+Lua is a dynamically typed language.
+This means that
+variables do not have types; only values do.
+There are no type definitions in the language.
+All values carry their own type.
+
+
+<p>
+All values in Lua are first-class values.
+This means that all values can be stored in variables,
+passed as arguments to other functions, and returned as results.
+
+
+<p>
+There are eight basic types in Lua:
+<em>nil</em>, <em>boolean</em>, <em>number</em>,
+<em>string</em>, <em>function</em>, <em>userdata</em>,
+<em>thread</em>, and <em>table</em>.
+The type <em>nil</em> has one single value, <b>nil</b>,
+whose main property is to be different from any other value;
+it often represents the absence of a useful value.
+The type <em>boolean</em> has two values, <b>false</b> and <b>true</b>.
+Both <b>nil</b> and <b>false</b> make a condition false;
+they are collectively called <em>false values</em>.
+Any other value makes a condition true.
+Despite its name,
+<b>false</b> is frequently used as an alternative to <b>nil</b>,
+with the key difference that <b>false</b> behaves
+like a regular value in a table,
+while a <b>nil</b> in a table represents an absent key.
+
+
+<p>
+The type <em>number</em> represents both
+integer numbers and real (floating-point) numbers,
+using two subtypes: <em>integer</em> and <em>float</em>.
+Standard Lua uses 64-bit integers and double-precision (64-bit) floats,
+but you can also compile Lua so that it
+uses 32-bit integers and/or single-precision (32-bit) floats.
+The option with 32 bits for both integers and floats
+is particularly attractive
+for small machines and embedded systems.
+(See macro <code>LUA_32BITS</code> in file <code>luaconf.h</code>.)
+
+
+<p>
+Unless stated otherwise,
+any overflow when manipulating integer values <em>wrap around</em>,
+according to the usual rules of two-complement arithmetic.
+(In other words,
+the actual result is the unique representable integer
+that is equal modulo <em>2<sup>n</sup></em> to the mathematical result,
+where <em>n</em> is the number of bits of the integer type.)
+
+
+<p>
+Lua has explicit rules about when each subtype is used,
+but it also converts between them automatically as needed (see <a href="#3.4.3">&sect;3.4.3</a>).
+Therefore,
+the programmer may choose to mostly ignore the difference
+between integers and floats
+or to assume complete control over the representation of each number.
+
+
+<p>
+The type <em>string</em> represents immutable sequences of bytes.
+
+Lua is 8-bit clean:
+strings can contain any 8-bit value,
+including embedded zeros ('<code>\0</code>').
+Lua is also encoding-agnostic;
+it makes no assumptions about the contents of a string.
+The length of any string in Lua must fit in a Lua integer.
+
+
+<p>
+Lua can call (and manipulate) functions written in Lua and
+functions written in C (see <a href="#3.4.10">&sect;3.4.10</a>).
+Both are represented by the type <em>function</em>.
+
+
+<p>
+The type <em>userdata</em> is provided to allow arbitrary C&nbsp;data to
+be stored in Lua variables.
+A userdata value represents a block of raw memory.
+There are two kinds of userdata:
+<em>full userdata</em>,
+which is an object with a block of memory managed by Lua,
+and <em>light userdata</em>,
+which is simply a C&nbsp;pointer value.
+Userdata has no predefined operations in Lua,
+except assignment and identity test.
+By using <em>metatables</em>,
+the programmer can define operations for full userdata values
+(see <a href="#2.4">&sect;2.4</a>).
+Userdata values cannot be created or modified in Lua,
+only through the C&nbsp;API.
+This guarantees the integrity of data owned by
+the host program and C&nbsp;libraries.
+
+
+<p>
+The type <em>thread</em> represents independent threads of execution
+and it is used to implement coroutines (see <a href="#2.6">&sect;2.6</a>).
+Lua threads are not related to operating-system threads.
+Lua supports coroutines on all systems,
+even those that do not support threads natively.
+
+
+<p>
+The type <em>table</em> implements associative arrays,
+that is, arrays that can have as indices not only numbers,
+but any Lua value except <b>nil</b> and NaN.
+(<em>Not a Number</em> is a special floating-point value
+used by the IEEE 754 standard to represent
+undefined numerical results, such as <code>0/0</code>.)
+Tables can be <em>heterogeneous</em>;
+that is, they can contain values of all types (except <b>nil</b>).
+Any key associated to the value <b>nil</b> is not considered part of the table.
+Conversely, any key that is not part of a table has
+an associated value <b>nil</b>.
+
+
+<p>
+Tables are the sole data-structuring mechanism in Lua;
+they can be used to represent ordinary arrays, lists,
+symbol tables, sets, records, graphs, trees, etc.
+To represent records, Lua uses the field name as an index.
+The language supports this representation by
+providing <code>a.name</code> as syntactic sugar for <code>a["name"]</code>.
+There are several convenient ways to create tables in Lua
+(see <a href="#3.4.9">&sect;3.4.9</a>).
+
+
+<p>
+Like indices,
+the values of table fields can be of any type.
+In particular,
+because functions are first-class values,
+table fields can contain functions.
+Thus tables can also carry <em>methods</em> (see <a href="#3.4.11">&sect;3.4.11</a>).
+
+
+<p>
+The indexing of tables follows
+the definition of raw equality in the language.
+The expressions <code>a[i]</code> and <code>a[j]</code>
+denote the same table element
+if and only if <code>i</code> and <code>j</code> are raw equal
+(that is, equal without metamethods).
+In particular, floats with integral values
+are equal to their respective integers
+(e.g., <code>1.0 == 1</code>).
+To avoid ambiguities,
+any float used as a key that is equal to an integer
+is converted to that integer.
+For instance, if you write <code>a[2.0] = true</code>,
+the actual key inserted into the table will be the integer <code>2</code>.
+
+
+<p>
+Tables, functions, threads, and (full) userdata values are <em>objects</em>:
+variables do not actually <em>contain</em> these values,
+only <em>references</em> to them.
+Assignment, parameter passing, and function returns
+always manipulate references to such values;
+these operations do not imply any kind of copy.
+
+
+<p>
+The library function <a href="#pdf-type"><code>type</code></a> returns a string describing the type
+of a given value (see <a href="#pdf-type"><code>type</code></a>).
+
+
+
+
+
+<h2>2.2 &ndash; <a name="2.2">Environments and the Global Environment</a></h2>
+
+<p>
+As we will discuss further in <a href="#3.2">&sect;3.2</a> and <a href="#3.3.3">&sect;3.3.3</a>,
+any reference to a free name
+(that is, a name not bound to any declaration) <code>var</code>
+is syntactically translated to <code>_ENV.var</code>.
+Moreover, every chunk is compiled in the scope of
+an external local variable named <code>_ENV</code> (see <a href="#3.3.2">&sect;3.3.2</a>),
+so <code>_ENV</code> itself is never a free name in a chunk.
+
+
+<p>
+Despite the existence of this external <code>_ENV</code> variable and
+the translation of free names,
+<code>_ENV</code> is a completely regular name.
+In particular,
+you can define new variables and parameters with that name.
+Each reference to a free name uses the <code>_ENV</code> that is
+visible at that point in the program,
+following the usual visibility rules of Lua (see <a href="#3.5">&sect;3.5</a>).
+
+
+<p>
+Any table used as the value of <code>_ENV</code> is called an <em>environment</em>.
+
+
+<p>
+Lua keeps a distinguished environment called the <em>global environment</em>.
+This value is kept at a special index in the C registry (see <a href="#4.3">&sect;4.3</a>).
+In Lua, the global variable <a href="#pdf-_G"><code>_G</code></a> is initialized with this same value.
+(<a href="#pdf-_G"><code>_G</code></a> is never used internally,
+so changing its value will affect only your own code.)
+
+
+<p>
+When Lua loads a chunk,
+the default value for its <code>_ENV</code> variable
+is the global environment (see <a href="#pdf-load"><code>load</code></a>).
+Therefore, by default,
+free names in Lua code refer to entries in the global environment
+and, therefore, they are also called <em>global variables</em>.
+Moreover, all standard libraries are loaded in the global environment
+and some functions there operate on that environment.
+You can use <a href="#pdf-load"><code>load</code></a> (or <a href="#pdf-loadfile"><code>loadfile</code></a>)
+to load a chunk with a different environment.
+(In C, you have to load the chunk and then change the value
+of its first upvalue; see <a href="#lua_setupvalue"><code>lua_setupvalue</code></a>.)
+
+
+
+
+
+<h2>2.3 &ndash; <a name="2.3">Error Handling</a></h2>
+
+<p>
+Several operations in Lua can <em>raise</em> an error.
+An error interrupts the normal flow of the program,
+which can continue by <em>catching</em> the error.
+
+
+<p>
+Lua code can explicitly raise an error by calling the
+<a href="#pdf-error"><code>error</code></a> function.
+(This function never returns.)
+
+
+<p>
+To catch errors in Lua,
+you can do a <em>protected call</em>,
+using <a href="#pdf-pcall"><code>pcall</code></a> (or <a href="#pdf-xpcall"><code>xpcall</code></a>).
+The function <a href="#pdf-pcall"><code>pcall</code></a> calls a given function in <em>protected mode</em>.
+Any error while running the function stops its execution,
+and control returns immediately to <code>pcall</code>,
+which returns a status code.
+
+
+<p>
+Because Lua is an embedded extension language,
+Lua code starts running by a call
+from C&nbsp;code in the host program.
+(When you use Lua standalone,
+the <code>lua</code> application is the host program.)
+Usually, this call is protected;
+so, when an otherwise unprotected error occurs during
+the compilation or execution of a Lua chunk,
+control returns to the host,
+which can take appropriate measures,
+such as printing an error message.
+
+
+<p>
+Whenever there is an error,
+an <em>error object</em>
+is propagated with information about the error.
+Lua itself only generates errors whose error object is a string,
+but programs may generate errors with
+any value as the error object.
+It is up to the Lua program or its host to handle such error objects.
+For historical reasons,
+an error object is often called an <em>error message</em>,
+even though it does not have to be a string.
+
+
+<p>
+When you use <a href="#pdf-xpcall"><code>xpcall</code></a> (or <a href="#lua_pcall"><code>lua_pcall</code></a>, in C)
+you may give a <em>message handler</em>
+to be called in case of errors.
+This function is called with the original error object
+and returns a new error object.
+It is called before the error unwinds the stack,
+so that it can gather more information about the error,
+for instance by inspecting the stack and creating a stack traceback.
+This message handler is still protected by the protected call;
+so, an error inside the message handler
+will call the message handler again.
+If this loop goes on for too long,
+Lua breaks it and returns an appropriate message.
+The message handler is called only for regular runtime errors.
+It is not called for memory-allocation errors
+nor for errors while running finalizers or other message handlers.
+
+
+<p>
+Lua also offers a system of <em>warnings</em> (see <a href="#pdf-warn"><code>warn</code></a>).
+Unlike errors, warnings do not interfere
+in any way with program execution.
+They typically only generate a message to the user,
+although this behavior can be adapted from C (see <a href="#lua_setwarnf"><code>lua_setwarnf</code></a>).
+
+
+
+
+
+<h2>2.4 &ndash; <a name="2.4">Metatables and Metamethods</a></h2>
+
+<p>
+Every value in Lua can have a <em>metatable</em>.
+This <em>metatable</em> is an ordinary Lua table
+that defines the behavior of the original value
+under certain events.
+You can change several aspects of the behavior
+of a value by setting specific fields in its metatable.
+For instance, when a non-numeric value is the operand of an addition,
+Lua checks for a function in the field <code>__add</code> of the value's metatable.
+If it finds one,
+Lua calls this function to perform the addition.
+
+
+<p>
+The key for each event in a metatable is a string
+with the event name prefixed by two underscores;
+the corresponding value is called a <em>metavalue</em>.
+For most events, the metavalue must be a function,
+which is then called a <em>metamethod</em>.
+In the previous example, the key is the string "<code>__add</code>"
+and the metamethod is the function that performs the addition.
+Unless stated otherwise,
+a metamethod may in fact be any callable value,
+which is either a function or a value with a <code>__call</code> metamethod.
+
+
+<p>
+You can query the metatable of any value
+using the <a href="#pdf-getmetatable"><code>getmetatable</code></a> function.
+Lua queries metamethods in metatables using a raw access (see <a href="#pdf-rawget"><code>rawget</code></a>).
+
+
+<p>
+You can replace the metatable of tables
+using the <a href="#pdf-setmetatable"><code>setmetatable</code></a> function.
+You cannot change the metatable of other types from Lua code,
+except by using the debug library (<a href="#6.10">&sect;6.10</a>).
+
+
+<p>
+Tables and full userdata have individual metatables,
+although multiple tables and userdata can share their metatables.
+Values of all other types share one single metatable per type;
+that is, there is one single metatable for all numbers,
+one for all strings, etc.
+By default, a value has no metatable,
+but the string library sets a metatable for the string type (see <a href="#6.4">&sect;6.4</a>).
+
+
+<p>
+A detailed list of operations controlled by metatables is given next.
+Each event is identified by its corresponding key.
+By convention, all metatable keys used by Lua are composed by
+two underscores followed by lowercase Latin letters.
+
+
+
+<ul>
+
+<li><b><code>__add</code>: </b>
+the addition (<code>+</code>) operation.
+If any operand for an addition is not a number,
+Lua will try to call a metamethod.
+It starts by checking the first operand (even if it is a number);
+if that operand does not define a metamethod for <code>__add</code>,
+then Lua will check the second operand.
+If Lua can find a metamethod,
+it calls the metamethod with the two operands as arguments,
+and the result of the call
+(adjusted to one value)
+is the result of the operation.
+Otherwise, if no metamethod is found,
+Lua raises an error.
+</li>
+
+<li><b><code>__sub</code>: </b>
+the subtraction (<code>-</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__mul</code>: </b>
+the multiplication (<code>*</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__div</code>: </b>
+the division (<code>/</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__mod</code>: </b>
+the modulo (<code>%</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__pow</code>: </b>
+the exponentiation (<code>^</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__unm</code>: </b>
+the negation (unary <code>-</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__idiv</code>: </b>
+the floor division (<code>//</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__band</code>: </b>
+the bitwise AND (<code>&amp;</code>) operation.
+Behavior similar to the addition operation,
+except that Lua will try a metamethod
+if any operand is neither an integer
+nor a float coercible to an integer (see <a href="#3.4.3">&sect;3.4.3</a>).
+</li>
+
+<li><b><code>__bor</code>: </b>
+the bitwise OR (<code>|</code>) operation.
+Behavior similar to the bitwise AND operation.
+</li>
+
+<li><b><code>__bxor</code>: </b>
+the bitwise exclusive OR (binary <code>~</code>) operation.
+Behavior similar to the bitwise AND operation.
+</li>
+
+<li><b><code>__bnot</code>: </b>
+the bitwise NOT (unary <code>~</code>) operation.
+Behavior similar to the bitwise AND operation.
+</li>
+
+<li><b><code>__shl</code>: </b>
+the bitwise left shift (<code>&lt;&lt;</code>) operation.
+Behavior similar to the bitwise AND operation.
+</li>
+
+<li><b><code>__shr</code>: </b>
+the bitwise right shift (<code>&gt;&gt;</code>) operation.
+Behavior similar to the bitwise AND operation.
+</li>
+
+<li><b><code>__concat</code>: </b>
+the concatenation (<code>..</code>) operation.
+Behavior similar to the addition operation,
+except that Lua will try a metamethod
+if any operand is neither a string nor a number
+(which is always coercible to a string).
+</li>
+
+<li><b><code>__len</code>: </b>
+the length (<code>#</code>) operation.
+If the object is not a string,
+Lua will try its metamethod.
+If there is a metamethod,
+Lua calls it with the object as argument,
+and the result of the call
+(always adjusted to one value)
+is the result of the operation.
+If there is no metamethod but the object is a table,
+then Lua uses the table length operation (see <a href="#3.4.7">&sect;3.4.7</a>).
+Otherwise, Lua raises an error.
+</li>
+
+<li><b><code>__eq</code>: </b>
+the equal (<code>==</code>) operation.
+Behavior similar to the addition operation,
+except that Lua will try a metamethod only when the values
+being compared are either both tables or both full userdata
+and they are not primitively equal.
+The result of the call is always converted to a boolean.
+</li>
+
+<li><b><code>__lt</code>: </b>
+the less than (<code>&lt;</code>) operation.
+Behavior similar to the addition operation,
+except that Lua will try a metamethod only when the values
+being compared are neither both numbers nor both strings.
+Moreover, the result of the call is always converted to a boolean.
+</li>
+
+<li><b><code>__le</code>: </b>
+the less equal (<code>&lt;=</code>) operation.
+Behavior similar to the less than operation.
+</li>
+
+<li><b><code>__index</code>: </b>
+The indexing access operation <code>table[key]</code>.
+This event happens when <code>table</code> is not a table or
+when <code>key</code> is not present in <code>table</code>.
+The metavalue is looked up in the metatable of <code>table</code>.
+
+
+<p>
+The metavalue for this event can be either a function, a table,
+or any value with an <code>__index</code> metavalue.
+If it is a function,
+it is called with <code>table</code> and <code>key</code> as arguments,
+and the result of the call
+(adjusted to one value)
+is the result of the operation.
+Otherwise,
+the final result is the result of indexing this metavalue with <code>key</code>.
+This indexing is regular, not raw,
+and therefore can trigger another <code>__index</code> metavalue.
+</li>
+
+<li><b><code>__newindex</code>: </b>
+The indexing assignment <code>table[key] = value</code>.
+Like the index event,
+this event happens when <code>table</code> is not a table or
+when <code>key</code> is not present in <code>table</code>.
+The metavalue is looked up in the metatable of <code>table</code>.
+
+
+<p>
+Like with indexing,
+the metavalue for this event can be either a function, a table,
+or any value with an <code>__newindex</code> metavalue.
+If it is a function,
+it is called with <code>table</code>, <code>key</code>, and <code>value</code> as arguments.
+Otherwise,
+Lua repeats the indexing assignment over this metavalue
+with the same key and value.
+This assignment is regular, not raw,
+and therefore can trigger another <code>__newindex</code> metavalue.
+
+
+<p>
+Whenever a <code>__newindex</code> metavalue is invoked,
+Lua does not perform the primitive assignment.
+If needed,
+the metamethod itself can call <a href="#pdf-rawset"><code>rawset</code></a>
+to do the assignment.
+</li>
+
+<li><b><code>__call</code>: </b>
+The call operation <code>func(args)</code>.
+This event happens when Lua tries to call a non-function value
+(that is, <code>func</code> is not a function).
+The metamethod is looked up in <code>func</code>.
+If present,
+the metamethod is called with <code>func</code> as its first argument,
+followed by the arguments of the original call (<code>args</code>).
+All results of the call
+are the results of the operation.
+This is the only metamethod that allows multiple results.
+</li>
+
+</ul>
+
+<p>
+In addition to the previous list,
+the interpreter also respects the following keys in metatables:
+<code>__gc</code> (see <a href="#2.5.3">&sect;2.5.3</a>),
+<code>__close</code> (see <a href="#3.3.8">&sect;3.3.8</a>),
+<code>__mode</code> (see <a href="#2.5.4">&sect;2.5.4</a>),
+and <code>__name</code>.
+(The entry <code>__name</code>,
+when it contains a string,
+may be used by <a href="#pdf-tostring"><code>tostring</code></a> and in error messages.)
+
+
+<p>
+For the unary operators (negation, length, and bitwise NOT),
+the metamethod is computed and called with a dummy second operand,
+equal to the first one.
+This extra operand is only to simplify Lua's internals
+(by making these operators behave like a binary operation)
+and may be removed in future versions.
+For most uses this extra operand is irrelevant.
+
+
+<p>
+Because metatables are regular tables,
+they can contain arbitrary fields,
+not only the event names defined above.
+Some functions in the standard library
+(e.g., <a href="#pdf-tostring"><code>tostring</code></a>)
+use other fields in metatables for their own purposes.
+
+
+<p>
+It is a good practice to add all needed metamethods to a table
+before setting it as a metatable of some object.
+In particular, the <code>__gc</code> metamethod works only when this order
+is followed (see <a href="#2.5.3">&sect;2.5.3</a>).
+It is also a good practice to set the metatable of an object
+right after its creation.
+
+
+
+
+
+<h2>2.5 &ndash; <a name="2.5">Garbage Collection</a></h2>
+
+
+
+<p>
+Lua performs automatic memory management.
+This means that
+you do not have to worry about allocating memory for new objects
+or freeing it when the objects are no longer needed.
+Lua manages memory automatically by running
+a <em>garbage collector</em> to collect all <em>dead</em> objects.
+All memory used by Lua is subject to automatic management:
+strings, tables, userdata, functions, threads, internal structures, etc.
+
+
+<p>
+An object is considered <em>dead</em>
+as soon as the collector can be sure the object
+will not be accessed again in the normal execution of the program.
+("Normal execution" here excludes finalizers,
+which can resurrect dead objects (see <a href="#2.5.3">&sect;2.5.3</a>),
+and excludes also operations using the debug library.)
+Note that the time when the collector can be sure that an object
+is dead may not coincide with the programmer's expectations.
+The only guarantees are that Lua will not collect an object
+that may still be accessed in the normal execution of the program,
+and it will eventually collect an object
+that is inaccessible from Lua.
+(Here,
+<em>inaccessible from Lua</em> means that neither a variable nor
+another live object refer to the object.)
+Because Lua has no knowledge about C&nbsp;code,
+it never collects objects accessible through the registry (see <a href="#4.3">&sect;4.3</a>),
+which includes the global environment (see <a href="#2.2">&sect;2.2</a>).
+
+
+<p>
+The garbage collector (GC) in Lua can work in two modes:
+incremental and generational.
+
+
+<p>
+The default GC mode with the default parameters
+are adequate for most uses.
+However, programs that waste a large proportion of their time
+allocating and freeing memory can benefit from other settings.
+Keep in mind that the GC behavior is non-portable
+both across platforms and across different Lua releases;
+therefore, optimal settings are also non-portable.
+
+
+<p>
+You can change the GC mode and parameters by calling
+<a href="#lua_gc"><code>lua_gc</code></a> in&nbsp;C
+or <a href="#pdf-collectgarbage"><code>collectgarbage</code></a> in Lua.
+You can also use these functions to control
+the collector directly (e.g., to stop and restart it).
+
+
+
+
+
+<h3>2.5.1 &ndash; <a name="2.5.1">Incremental Garbage Collection</a></h3>
+
+<p>
+In incremental mode,
+each GC cycle performs a mark-and-sweep collection in small steps
+interleaved with the program's execution.
+In this mode,
+the collector uses three numbers to control its garbage-collection cycles:
+the <em>garbage-collector pause</em>,
+the <em>garbage-collector step multiplier</em>,
+and the <em>garbage-collector step size</em>.
+
+
+<p>
+The garbage-collector pause
+controls how long the collector waits before starting a new cycle.
+The collector starts a new cycle when the use of memory
+hits <em>n%</em> of the use after the previous collection.
+Larger values make the collector less aggressive.
+Values equal to or less than 100 mean the collector will not wait to
+start a new cycle.
+A value of 200 means that the collector waits for the total memory in use
+to double before starting a new cycle.
+The default value is 200; the maximum value is 1000.
+
+
+<p>
+The garbage-collector step multiplier
+controls the speed of the collector relative to
+memory allocation,
+that is,
+how many elements it marks or sweeps for each
+kilobyte of memory allocated.
+Larger values make the collector more aggressive but also increase
+the size of each incremental step.
+You should not use values less than 100,
+because they make the collector too slow and
+can result in the collector never finishing a cycle.
+The default value is 100; the maximum value is 1000.
+
+
+<p>
+The garbage-collector step size controls the
+size of each incremental step,
+specifically how many bytes the interpreter allocates
+before performing a step.
+This parameter is logarithmic:
+A value of <em>n</em> means the interpreter will allocate <em>2<sup>n</sup></em>
+bytes between steps and perform equivalent work during the step.
+A large value (e.g., 60) makes the collector a stop-the-world
+(non-incremental) collector.
+The default value is 13,
+which means steps of approximately 8&nbsp;Kbytes.
+
+
+
+
+
+<h3>2.5.2 &ndash; <a name="2.5.2">Generational Garbage Collection</a></h3>
+
+<p>
+In generational mode,
+the collector does frequent <em>minor</em> collections,
+which traverses only objects recently created.
+If after a minor collection the use of memory is still above a limit,
+the collector does a stop-the-world <em>major</em> collection,
+which traverses all objects.
+The generational mode uses two parameters:
+the <em>minor multiplier</em> and the <em>the major multiplier</em>.
+
+
+<p>
+The minor multiplier controls the frequency of minor collections.
+For a minor multiplier <em>x</em>,
+a new minor collection will be done when memory
+grows <em>x%</em> larger than the memory in use after the previous major
+collection.
+For instance, for a multiplier of 20,
+the collector will do a minor collection when the use of memory
+gets 20% larger than the use after the previous major collection.
+The default value is 20; the maximum value is 200.
+
+
+<p>
+The major multiplier controls the frequency of major collections.
+For a major multiplier <em>x</em>,
+a new major collection will be done when memory
+grows <em>x%</em> larger than the memory in use after the previous major
+collection.
+For instance, for a multiplier of 100,
+the collector will do a major collection when the use of memory
+gets larger than twice the use after the previous collection.
+The default value is 100; the maximum value is 1000.
+
+
+
+
+
+<h3>2.5.3 &ndash; <a name="2.5.3">Garbage-Collection Metamethods</a></h3>
+
+<p>
+You can set garbage-collector metamethods for tables
+and, using the C&nbsp;API,
+for full userdata (see <a href="#2.4">&sect;2.4</a>).
+These metamethods, called <em>finalizers</em>,
+are called when the garbage collector detects that the
+corresponding table or userdata is dead.
+Finalizers allow you to coordinate Lua's garbage collection
+with external resource management such as closing files,
+network or database connections,
+or freeing your own memory.
+
+
+<p>
+For an object (table or userdata) to be finalized when collected,
+you must <em>mark</em> it for finalization.
+
+You mark an object for finalization when you set its metatable
+and the metatable has a <code>__gc</code> metamethod.
+Note that if you set a metatable without a <code>__gc</code> field
+and later create that field in the metatable,
+the object will not be marked for finalization.
+
+
+<p>
+When a marked object becomes dead,
+it is not collected immediately by the garbage collector.
+Instead, Lua puts it in a list.
+After the collection,
+Lua goes through that list.
+For each object in the list,
+it checks the object's <code>__gc</code> metamethod:
+If it is present,
+Lua calls it with the object as its single argument.
+
+
+<p>
+At the end of each garbage-collection cycle,
+the finalizers are called in
+the reverse order that the objects were marked for finalization,
+among those collected in that cycle;
+that is, the first finalizer to be called is the one associated
+with the object marked last in the program.
+The execution of each finalizer may occur at any point during
+the execution of the regular code.
+
+
+<p>
+Because the object being collected must still be used by the finalizer,
+that object (and other objects accessible only through it)
+must be <em>resurrected</em> by Lua.
+Usually, this resurrection is transient,
+and the object memory is freed in the next garbage-collection cycle.
+However, if the finalizer stores the object in some global place
+(e.g., a global variable),
+then the resurrection is permanent.
+Moreover, if the finalizer marks a finalizing object for finalization again,
+its finalizer will be called again in the next cycle where the
+object is dead.
+In any case,
+the object memory is freed only in a GC cycle where
+the object is dead and not marked for finalization.
+
+
+<p>
+When you close a state (see <a href="#lua_close"><code>lua_close</code></a>),
+Lua calls the finalizers of all objects marked for finalization,
+following the reverse order that they were marked.
+If any finalizer marks objects for collection during that phase,
+these marks have no effect.
+
+
+<p>
+Finalizers cannot yield nor run the garbage collector.
+Because they can run in unpredictable times,
+it is good practice to restrict each finalizer
+to the minimum necessary to properly release
+its associated resource.
+
+
+<p>
+Any error while running a finalizer generates a warning;
+the error is not propagated.
+
+
+
+
+
+<h3>2.5.4 &ndash; <a name="2.5.4">Weak Tables</a></h3>
+
+<p>
+A <em>weak table</em> is a table whose elements are
+<em>weak references</em>.
+A weak reference is ignored by the garbage collector.
+In other words,
+if the only references to an object are weak references,
+then the garbage collector will collect that object.
+
+
+<p>
+A weak table can have weak keys, weak values, or both.
+A table with weak values allows the collection of its values,
+but prevents the collection of its keys.
+A table with both weak keys and weak values allows the collection of
+both keys and values.
+In any case, if either the key or the value is collected,
+the whole pair is removed from the table.
+The weakness of a table is controlled by the
+<code>__mode</code> field of its metatable.
+This metavalue, if present, must be one of the following strings:
+"<code>k</code>", for a table with weak keys;
+"<code>v</code>", for a table with weak values;
+or "<code>kv</code>", for a table with both weak keys and values.
+
+
+<p>
+A table with weak keys and strong values
+is also called an <em>ephemeron table</em>.
+In an ephemeron table,
+a value is considered reachable only if its key is reachable.
+In particular,
+if the only reference to a key comes through its value,
+the pair is removed.
+
+
+<p>
+Any change in the weakness of a table may take effect only
+at the next collect cycle.
+In particular, if you change the weakness to a stronger mode,
+Lua may still collect some items from that table
+before the change takes effect.
+
+
+<p>
+Only objects that have an explicit construction
+are removed from weak tables.
+Values, such as numbers and light C&nbsp;functions,
+are not subject to garbage collection,
+and therefore are not removed from weak tables
+(unless their associated values are collected).
+Although strings are subject to garbage collection,
+they do not have an explicit construction and
+their equality is by value;
+they behave more like values than like objects.
+Therefore, they are not removed from weak tables.
+
+
+<p>
+Resurrected objects
+(that is, objects being finalized
+and objects accessible only through objects being finalized)
+have a special behavior in weak tables.
+They are removed from weak values before running their finalizers,
+but are removed from weak keys only in the next collection
+after running their finalizers, when such objects are actually freed.
+This behavior allows the finalizer to access properties
+associated with the object through weak tables.
+
+
+<p>
+If a weak table is among the resurrected objects in a collection cycle,
+it may not be properly cleared until the next cycle.
+
+
+
+
+
+
+
+<h2>2.6 &ndash; <a name="2.6">Coroutines</a></h2>
+
+<p>
+Lua supports coroutines,
+also called <em>collaborative multithreading</em>.
+A coroutine in Lua represents an independent thread of execution.
+Unlike threads in multithread systems, however,
+a coroutine only suspends its execution by explicitly calling
+a yield function.
+
+
+<p>
+You create a coroutine by calling <a href="#pdf-coroutine.create"><code>coroutine.create</code></a>.
+Its sole argument is a function
+that is the main function of the coroutine.
+The <code>create</code> function only creates a new coroutine and
+returns a handle to it (an object of type <em>thread</em>);
+it does not start the coroutine.
+
+
+<p>
+You execute a coroutine by calling <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>.
+When you first call <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>,
+passing as its first argument
+a thread returned by <a href="#pdf-coroutine.create"><code>coroutine.create</code></a>,
+the coroutine starts its execution by
+calling its main function.
+Extra arguments passed to <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> are passed
+as arguments to that function.
+After the coroutine starts running,
+it runs until it terminates or <em>yields</em>.
+
+
+<p>
+A coroutine can terminate its execution in two ways:
+normally, when its main function returns
+(explicitly or implicitly, after the last instruction);
+and abnormally, if there is an unprotected error.
+In case of normal termination,
+<a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> returns <b>true</b>,
+plus any values returned by the coroutine main function.
+In case of errors, <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> returns <b>false</b>
+plus the error object.
+In this case, the coroutine does not unwind its stack,
+so that it is possible to inspect it after the error
+with the debug API.
+
+
+<p>
+A coroutine yields by calling <a href="#pdf-coroutine.yield"><code>coroutine.yield</code></a>.
+When a coroutine yields,
+the corresponding <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> returns immediately,
+even if the yield happens inside nested function calls
+(that is, not in the main function,
+but in a function directly or indirectly called by the main function).
+In the case of a yield, <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> also returns <b>true</b>,
+plus any values passed to <a href="#pdf-coroutine.yield"><code>coroutine.yield</code></a>.
+The next time you resume the same coroutine,
+it continues its execution from the point where it yielded,
+with the call to <a href="#pdf-coroutine.yield"><code>coroutine.yield</code></a> returning any extra
+arguments passed to <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>.
+
+
+<p>
+Like <a href="#pdf-coroutine.create"><code>coroutine.create</code></a>,
+the <a href="#pdf-coroutine.wrap"><code>coroutine.wrap</code></a> function also creates a coroutine,
+but instead of returning the coroutine itself,
+it returns a function that, when called, resumes the coroutine.
+Any arguments passed to this function
+go as extra arguments to <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>.
+<a href="#pdf-coroutine.wrap"><code>coroutine.wrap</code></a> returns all the values returned by <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>,
+except the first one (the boolean error code).
+Unlike <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>,
+the function created by <a href="#pdf-coroutine.wrap"><code>coroutine.wrap</code></a>
+propagates any error to the caller.
+In this case,
+the function also closes the coroutine (see <a href="#pdf-coroutine.close"><code>coroutine.close</code></a>).
+
+
+<p>
+As an example of how coroutines work,
+consider the following code:
+
+<pre>
+ function foo (a)
+ print("foo", a)
+ return coroutine.yield(2*a)
+ end
+
+ co = coroutine.create(function (a,b)
+ print("co-body", a, b)
+ local r = foo(a+1)
+ print("co-body", r)
+ local r, s = coroutine.yield(a+b, a-b)
+ print("co-body", r, s)
+ return b, "end"
+ end)
+
+ print("main", coroutine.resume(co, 1, 10))
+ print("main", coroutine.resume(co, "r"))
+ print("main", coroutine.resume(co, "x", "y"))
+ print("main", coroutine.resume(co, "x", "y"))
+</pre><p>
+When you run it, it produces the following output:
+
+<pre>
+ co-body 1 10
+ foo 2
+ main true 4
+ co-body r
+ main true 11 -9
+ co-body x y
+ main true 10 end
+ main false cannot resume dead coroutine
+</pre>
+
+<p>
+You can also create and manipulate coroutines through the C API:
+see functions <a href="#lua_newthread"><code>lua_newthread</code></a>, <a href="#lua_resume"><code>lua_resume</code></a>,
+and <a href="#lua_yield"><code>lua_yield</code></a>.
+
+
+
+
+
+<h1>3 &ndash; <a name="3">The Language</a></h1>
+
+
+
+<p>
+This section describes the lexis, the syntax, and the semantics of Lua.
+In other words,
+this section describes
+which tokens are valid,
+how they can be combined,
+and what their combinations mean.
+
+
+<p>
+Language constructs will be explained using the usual extended BNF notation,
+in which
+{<em>a</em>}&nbsp;means&nbsp;0 or more <em>a</em>'s, and
+[<em>a</em>]&nbsp;means an optional <em>a</em>.
+Non-terminals are shown like non-terminal,
+keywords are shown like <b>kword</b>,
+and other terminal symbols are shown like &lsquo;<b>=</b>&rsquo;.
+The complete syntax of Lua can be found in <a href="#9">&sect;9</a>
+at the end of this manual.
+
+
+
+
+
+<h2>3.1 &ndash; <a name="3.1">Lexical Conventions</a></h2>
+
+<p>
+Lua is a free-form language.
+It ignores spaces and comments between lexical elements (tokens),
+except as delimiters between two tokens.
+In source code,
+Lua recognizes as spaces the standard ASCII whitespace
+characters space, form feed, newline,
+carriage return, horizontal tab, and vertical tab.
+
+
+<p>
+<em>Names</em>
+(also called <em>identifiers</em>)
+in Lua can be any string of Latin letters,
+Arabic-Indic digits, and underscores,
+not beginning with a digit and
+not being a reserved word.
+Identifiers are used to name variables, table fields, and labels.
+
+
+<p>
+The following <em>keywords</em> are reserved
+and cannot be used as names:
+
+
+<pre>
+ and break do else elseif end
+ false for function goto if in
+ local nil not or repeat return
+ then true until while
+</pre>
+
+<p>
+Lua is a case-sensitive language:
+<code>and</code> is a reserved word, but <code>And</code> and <code>AND</code>
+are two different, valid names.
+As a convention,
+programs should avoid creating
+names that start with an underscore followed by
+one or more uppercase letters (such as <a href="#pdf-_VERSION"><code>_VERSION</code></a>).
+
+
+<p>
+The following strings denote other tokens:
+
+<pre>
+ + - * / % ^ #
+ &amp; ~ | &lt;&lt; &gt;&gt; //
+ == ~= &lt;= &gt;= &lt; &gt; =
+ ( ) { } [ ] ::
+ ; : , . .. ...
+</pre>
+
+<p>
+A <em>short literal string</em>
+can be delimited by matching single or double quotes,
+and can contain the following C-like escape sequences:
+'<code>\a</code>' (bell),
+'<code>\b</code>' (backspace),
+'<code>\f</code>' (form feed),
+'<code>\n</code>' (newline),
+'<code>\r</code>' (carriage return),
+'<code>\t</code>' (horizontal tab),
+'<code>\v</code>' (vertical tab),
+'<code>\\</code>' (backslash),
+'<code>\"</code>' (quotation mark [double quote]),
+and '<code>\'</code>' (apostrophe [single quote]).
+A backslash followed by a line break
+results in a newline in the string.
+The escape sequence '<code>\z</code>' skips the following span
+of whitespace characters,
+including line breaks;
+it is particularly useful to break and indent a long literal string
+into multiple lines without adding the newlines and spaces
+into the string contents.
+A short literal string cannot contain unescaped line breaks
+nor escapes not forming a valid escape sequence.
+
+
+<p>
+We can specify any byte in a short literal string,
+including embedded zeros,
+by its numeric value.
+This can be done
+with the escape sequence <code>\x<em>XX</em></code>,
+where <em>XX</em> is a sequence of exactly two hexadecimal digits,
+or with the escape sequence <code>\<em>ddd</em></code>,
+where <em>ddd</em> is a sequence of up to three decimal digits.
+(Note that if a decimal escape sequence is to be followed by a digit,
+it must be expressed using exactly three digits.)
+
+
+<p>
+The UTF-8 encoding of a Unicode character
+can be inserted in a literal string with
+the escape sequence <code>\u{<em>XXX</em>}</code>
+(with mandatory enclosing braces),
+where <em>XXX</em> is a sequence of one or more hexadecimal digits
+representing the character code point.
+This code point can be any value less than <em>2<sup>31</sup></em>.
+(Lua uses the original UTF-8 specification here,
+which is not restricted to valid Unicode code points.)
+
+
+<p>
+Literal strings can also be defined using a long format
+enclosed by <em>long brackets</em>.
+We define an <em>opening long bracket of level <em>n</em></em> as an opening
+square bracket followed by <em>n</em> equal signs followed by another
+opening square bracket.
+So, an opening long bracket of level&nbsp;0 is written as <code>[[</code>,
+an opening long bracket of level&nbsp;1 is written as <code>[=[</code>,
+and so on.
+A <em>closing long bracket</em> is defined similarly;
+for instance,
+a closing long bracket of level&nbsp;4 is written as <code>]====]</code>.
+A <em>long literal</em> starts with an opening long bracket of any level and
+ends at the first closing long bracket of the same level.
+It can contain any text except a closing bracket of the same level.
+Literals in this bracketed form can run for several lines,
+do not interpret any escape sequences,
+and ignore long brackets of any other level.
+Any kind of end-of-line sequence
+(carriage return, newline, carriage return followed by newline,
+or newline followed by carriage return)
+is converted to a simple newline.
+When the opening long bracket is immediately followed by a newline,
+the newline is not included in the string.
+
+
+<p>
+As an example, in a system using ASCII
+(in which '<code>a</code>' is coded as&nbsp;97,
+newline is coded as&nbsp;10, and '<code>1</code>' is coded as&nbsp;49),
+the five literal strings below denote the same string:
+
+<pre>
+ a = 'alo\n123"'
+ a = "alo\n123\""
+ a = '\97lo\10\04923"'
+ a = [[alo
+ 123"]]
+ a = [==[
+ alo
+ 123"]==]
+</pre>
+
+<p>
+Any byte in a literal string not
+explicitly affected by the previous rules represents itself.
+However, Lua opens files for parsing in text mode,
+and the system's file functions may have problems with
+some control characters.
+So, it is safer to represent
+binary data as a quoted literal with
+explicit escape sequences for the non-text characters.
+
+
+<p>
+A <em>numeric constant</em> (or <em>numeral</em>)
+can be written with an optional fractional part
+and an optional decimal exponent,
+marked by a letter '<code>e</code>' or '<code>E</code>'.
+Lua also accepts hexadecimal constants,
+which start with <code>0x</code> or <code>0X</code>.
+Hexadecimal constants also accept an optional fractional part
+plus an optional binary exponent,
+marked by a letter '<code>p</code>' or '<code>P</code>' and written in decimal.
+(For instance, <code>0x1.fp10</code> denotes 1984,
+which is <em>0x1f / 16</em> multiplied by <em>2<sup>10</sup></em>.)
+
+
+<p>
+A numeric constant with a radix point or an exponent
+denotes a float;
+otherwise,
+if its value fits in an integer or it is a hexadecimal constant,
+it denotes an integer;
+otherwise (that is, a decimal integer numeral that overflows),
+it denotes a float.
+Hexadecimal numerals with neither a radix point nor an exponent
+always denote an integer value;
+if the value overflows, it <em>wraps around</em>
+to fit into a valid integer.
+
+
+<p>
+Examples of valid integer constants are
+
+<pre>
+ 3 345 0xff 0xBEBADA
+</pre><p>
+Examples of valid float constants are
+
+<pre>
+ 3.0 3.1416 314.16e-2 0.31416E1 34e1
+ 0x0.1E 0xA23p-4 0X1.921FB54442D18P+1
+</pre>
+
+<p>
+A <em>comment</em> starts with a double hyphen (<code>--</code>)
+anywhere outside a string.
+If the text immediately after <code>--</code> is not an opening long bracket,
+the comment is a <em>short comment</em>,
+which runs until the end of the line.
+Otherwise, it is a <em>long comment</em>,
+which runs until the corresponding closing long bracket.
+
+
+
+
+
+<h2>3.2 &ndash; <a name="3.2">Variables</a></h2>
+
+<p>
+Variables are places that store values.
+There are three kinds of variables in Lua:
+global variables, local variables, and table fields.
+
+
+<p>
+A single name can denote a global variable or a local variable
+(or a function's formal parameter,
+which is a particular kind of local variable):
+
+<pre>
+ var ::= Name
+</pre><p>
+Name denotes identifiers (see <a href="#3.1">&sect;3.1</a>).
+
+
+<p>
+Any variable name is assumed to be global unless explicitly declared
+as a local (see <a href="#3.3.7">&sect;3.3.7</a>).
+Local variables are <em>lexically scoped</em>:
+local variables can be freely accessed by functions
+defined inside their scope (see <a href="#3.5">&sect;3.5</a>).
+
+
+<p>
+Before the first assignment to a variable, its value is <b>nil</b>.
+
+
+<p>
+Square brackets are used to index a table:
+
+<pre>
+ var ::= prefixexp &lsquo;<b>[</b>&rsquo; exp &lsquo;<b>]</b>&rsquo;
+</pre><p>
+The meaning of accesses to table fields can be changed via metatables
+(see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+The syntax <code>var.Name</code> is just syntactic sugar for
+<code>var["Name"]</code>:
+
+<pre>
+ var ::= prefixexp &lsquo;<b>.</b>&rsquo; Name
+</pre>
+
+<p>
+An access to a global variable <code>x</code>
+is equivalent to <code>_ENV.x</code>.
+Due to the way that chunks are compiled,
+the variable <code>_ENV</code> itself is never global (see <a href="#2.2">&sect;2.2</a>).
+
+
+
+
+
+<h2>3.3 &ndash; <a name="3.3">Statements</a></h2>
+
+
+
+<p>
+Lua supports an almost conventional set of statements,
+similar to those in other conventional languages.
+This set includes
+blocks, assignments, control structures, function calls,
+and variable declarations.
+
+
+
+
+
+<h3>3.3.1 &ndash; <a name="3.3.1">Blocks</a></h3>
+
+<p>
+A block is a list of statements,
+which are executed sequentially:
+
+<pre>
+ block ::= {stat}
+</pre><p>
+Lua has <em>empty statements</em>
+that allow you to separate statements with semicolons,
+start a block with a semicolon
+or write two semicolons in sequence:
+
+<pre>
+ stat ::= &lsquo;<b>;</b>&rsquo;
+</pre>
+
+<p>
+Both function calls and assignments
+can start with an open parenthesis.
+This possibility leads to an ambiguity in Lua's grammar.
+Consider the following fragment:
+
+<pre>
+ a = b + c
+ (print or io.write)('done')
+</pre><p>
+The grammar could see this fragment in two ways:
+
+<pre>
+ a = b + c(print or io.write)('done')
+
+ a = b + c; (print or io.write)('done')
+</pre><p>
+The current parser always sees such constructions
+in the first way,
+interpreting the open parenthesis
+as the start of the arguments to a call.
+To avoid this ambiguity,
+it is a good practice to always precede with a semicolon
+statements that start with a parenthesis:
+
+<pre>
+ ;(print or io.write)('done')
+</pre>
+
+<p>
+A block can be explicitly delimited to produce a single statement:
+
+<pre>
+ stat ::= <b>do</b> block <b>end</b>
+</pre><p>
+Explicit blocks are useful
+to control the scope of variable declarations.
+Explicit blocks are also sometimes used to
+add a <b>return</b> statement in the middle
+of another block (see <a href="#3.3.4">&sect;3.3.4</a>).
+
+
+
+
+
+<h3>3.3.2 &ndash; <a name="3.3.2">Chunks</a></h3>
+
+<p>
+The unit of compilation of Lua is called a <em>chunk</em>.
+Syntactically,
+a chunk is simply a block:
+
+<pre>
+ chunk ::= block
+</pre>
+
+<p>
+Lua handles a chunk as the body of an anonymous function
+with a variable number of arguments
+(see <a href="#3.4.11">&sect;3.4.11</a>).
+As such, chunks can define local variables,
+receive arguments, and return values.
+Moreover, such anonymous function is compiled as in the
+scope of an external local variable called <code>_ENV</code> (see <a href="#2.2">&sect;2.2</a>).
+The resulting function always has <code>_ENV</code> as its only external variable,
+even if it does not use that variable.
+
+
+<p>
+A chunk can be stored in a file or in a string inside the host program.
+To execute a chunk,
+Lua first <em>loads</em> it,
+precompiling the chunk's code into instructions for a virtual machine,
+and then Lua executes the compiled code
+with an interpreter for the virtual machine.
+
+
+<p>
+Chunks can also be precompiled into binary form;
+see the program <code>luac</code> and the function <a href="#pdf-string.dump"><code>string.dump</code></a> for details.
+Programs in source and compiled forms are interchangeable;
+Lua automatically detects the file type and acts accordingly (see <a href="#pdf-load"><code>load</code></a>).
+
+
+
+
+
+<h3>3.3.3 &ndash; <a name="3.3.3">Assignment</a></h3>
+
+<p>
+Lua allows multiple assignments.
+Therefore, the syntax for assignment
+defines a list of variables on the left side
+and a list of expressions on the right side.
+The elements in both lists are separated by commas:
+
+<pre>
+ stat ::= varlist &lsquo;<b>=</b>&rsquo; explist
+ varlist ::= var {&lsquo;<b>,</b>&rsquo; var}
+ explist ::= exp {&lsquo;<b>,</b>&rsquo; exp}
+</pre><p>
+Expressions are discussed in <a href="#3.4">&sect;3.4</a>.
+
+
+<p>
+Before the assignment,
+the list of values is <em>adjusted</em> to the length of
+the list of variables (see <a href="#3.4.12">&sect;3.4.12</a>).
+
+
+<p>
+If a variable is both assigned and read
+inside a multiple assignment,
+Lua ensures that all reads get the value of the variable
+before the assignment.
+Thus the code
+
+<pre>
+ i = 3
+ i, a[i] = i+1, 20
+</pre><p>
+sets <code>a[3]</code> to 20, without affecting <code>a[4]</code>
+because the <code>i</code> in <code>a[i]</code> is evaluated (to 3)
+before it is assigned&nbsp;4.
+Similarly, the line
+
+<pre>
+ x, y = y, x
+</pre><p>
+exchanges the values of <code>x</code> and <code>y</code>,
+and
+
+<pre>
+ x, y, z = y, z, x
+</pre><p>
+cyclically permutes the values of <code>x</code>, <code>y</code>, and <code>z</code>.
+
+
+<p>
+Note that this guarantee covers only accesses
+syntactically inside the assignment statement.
+If a function or a metamethod called during the assignment
+changes the value of a variable,
+Lua gives no guarantees about the order of that access.
+
+
+<p>
+An assignment to a global name <code>x = val</code>
+is equivalent to the assignment
+<code>_ENV.x = val</code> (see <a href="#2.2">&sect;2.2</a>).
+
+
+<p>
+The meaning of assignments to table fields and
+global variables (which are actually table fields, too)
+can be changed via metatables (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<h3>3.3.4 &ndash; <a name="3.3.4">Control Structures</a></h3><p>
+The control structures
+<b>if</b>, <b>while</b>, and <b>repeat</b> have the usual meaning and
+familiar syntax:
+
+
+
+
+<pre>
+ stat ::= <b>while</b> exp <b>do</b> block <b>end</b>
+ stat ::= <b>repeat</b> block <b>until</b> exp
+ stat ::= <b>if</b> exp <b>then</b> block {<b>elseif</b> exp <b>then</b> block} [<b>else</b> block] <b>end</b>
+</pre><p>
+Lua also has a <b>for</b> statement, in two flavors (see <a href="#3.3.5">&sect;3.3.5</a>).
+
+
+<p>
+The condition expression of a
+control structure can return any value.
+Both <b>false</b> and <b>nil</b> test false.
+All values different from <b>nil</b> and <b>false</b> test true.
+In particular, the number 0 and the empty string also test true.
+
+
+<p>
+In the <b>repeat</b>&ndash;<b>until</b> loop,
+the inner block does not end at the <b>until</b> keyword,
+but only after the condition.
+So, the condition can refer to local variables
+declared inside the loop block.
+
+
+<p>
+The <b>goto</b> statement transfers the program control to a label.
+For syntactical reasons,
+labels in Lua are considered statements too:
+
+
+
+<pre>
+ stat ::= <b>goto</b> Name
+ stat ::= label
+ label ::= &lsquo;<b>::</b>&rsquo; Name &lsquo;<b>::</b>&rsquo;
+</pre>
+
+<p>
+A label is visible in the entire block where it is defined,
+except inside nested functions.
+A goto may jump to any visible label as long as it does not
+enter into the scope of a local variable.
+A label should not be declared
+where a label with the same name is visible,
+even if this other label has been declared in an enclosing block.
+
+
+<p>
+The <b>break</b> statement terminates the execution of a
+<b>while</b>, <b>repeat</b>, or <b>for</b> loop,
+skipping to the next statement after the loop:
+
+
+<pre>
+ stat ::= <b>break</b>
+</pre><p>
+A <b>break</b> ends the innermost enclosing loop.
+
+
+<p>
+The <b>return</b> statement is used to return values
+from a function or a chunk
+(which is handled as an anonymous function).
+
+Functions can return more than one value,
+so the syntax for the <b>return</b> statement is
+
+<pre>
+ stat ::= <b>return</b> [explist] [&lsquo;<b>;</b>&rsquo;]
+</pre>
+
+<p>
+The <b>return</b> statement can only be written
+as the last statement of a block.
+If it is necessary to <b>return</b> in the middle of a block,
+then an explicit inner block can be used,
+as in the idiom <code>do return end</code>,
+because now <b>return</b> is the last statement in its (inner) block.
+
+
+
+
+
+<h3>3.3.5 &ndash; <a name="3.3.5">For Statement</a></h3>
+
+<p>
+
+The <b>for</b> statement has two forms:
+one numerical and one generic.
+
+
+
+<h4>The numerical <b>for</b> loop</h4>
+
+<p>
+The numerical <b>for</b> loop repeats a block of code while a
+control variable goes through an arithmetic progression.
+It has the following syntax:
+
+<pre>
+ stat ::= <b>for</b> Name &lsquo;<b>=</b>&rsquo; exp &lsquo;<b>,</b>&rsquo; exp [&lsquo;<b>,</b>&rsquo; exp] <b>do</b> block <b>end</b>
+</pre><p>
+The given identifier (Name) defines the control variable,
+which is a new variable local to the loop body (<em>block</em>).
+
+
+<p>
+The loop starts by evaluating once the three control expressions.
+Their values are called respectively
+the <em>initial value</em>, the <em>limit</em>, and the <em>step</em>.
+If the step is absent, it defaults to&nbsp;1.
+
+
+<p>
+If both the initial value and the step are integers,
+the loop is done with integers;
+note that the limit may not be an integer.
+Otherwise, the three values are converted to
+floats and the loop is done with floats.
+Beware of floating-point accuracy in this case.
+
+
+<p>
+After that initialization,
+the loop body is repeated with the value of the control variable
+going through an arithmetic progression,
+starting at the initial value,
+with a common difference given by the step.
+A negative step makes a decreasing sequence;
+a step equal to zero raises an error.
+The loop continues while the value is less than
+or equal to the limit
+(greater than or equal to for a negative step).
+If the initial value is already greater than the limit
+(or less than, if the step is negative),
+the body is not executed.
+
+
+<p>
+For integer loops,
+the control variable never wraps around;
+instead, the loop ends in case of an overflow.
+
+
+<p>
+You should not change the value of the control variable
+during the loop.
+If you need its value after the loop,
+assign it to another variable before exiting the loop.
+
+
+
+
+
+<h4>The generic <b>for</b> loop</h4>
+
+<p>
+The generic <b>for</b> statement works over functions,
+called <em>iterators</em>.
+On each iteration, the iterator function is called to produce a new value,
+stopping when this new value is <b>nil</b>.
+The generic <b>for</b> loop has the following syntax:
+
+<pre>
+ stat ::= <b>for</b> namelist <b>in</b> explist <b>do</b> block <b>end</b>
+ namelist ::= Name {&lsquo;<b>,</b>&rsquo; Name}
+</pre><p>
+A <b>for</b> statement like
+
+<pre>
+ for <em>var_1</em>, &middot;&middot;&middot;, <em>var_n</em> in <em>explist</em> do <em>body</em> end
+</pre><p>
+works as follows.
+
+
+<p>
+The names <em>var_i</em> declare loop variables local to the loop body.
+The first of these variables is the <em>control variable</em>.
+
+
+<p>
+The loop starts by evaluating <em>explist</em>
+to produce four values:
+an <em>iterator function</em>,
+a <em>state</em>,
+an initial value for the control variable,
+and a <em>closing value</em>.
+
+
+<p>
+Then, at each iteration,
+Lua calls the iterator function with two arguments:
+the state and the control variable.
+The results from this call are then assigned to the loop variables,
+following the rules of multiple assignments (see <a href="#3.3.3">&sect;3.3.3</a>).
+If the control variable becomes <b>nil</b>,
+the loop terminates.
+Otherwise, the body is executed and the loop goes
+to the next iteration.
+
+
+<p>
+The closing value behaves like a
+to-be-closed variable (see <a href="#3.3.8">&sect;3.3.8</a>),
+which can be used to release resources when the loop ends.
+Otherwise, it does not interfere with the loop.
+
+
+<p>
+You should not change the value of the control variable
+during the loop.
+
+
+
+
+
+
+
+<h3>3.3.6 &ndash; <a name="3.3.6">Function Calls as Statements</a></h3><p>
+To allow possible side-effects,
+function calls can be executed as statements:
+
+<pre>
+ stat ::= functioncall
+</pre><p>
+In this case, all returned values are thrown away.
+Function calls are explained in <a href="#3.4.10">&sect;3.4.10</a>.
+
+
+
+
+
+<h3>3.3.7 &ndash; <a name="3.3.7">Local Declarations</a></h3><p>
+Local variables can be declared anywhere inside a block.
+The declaration can include an initialization:
+
+<pre>
+ stat ::= <b>local</b> attnamelist [&lsquo;<b>=</b>&rsquo; explist]
+ attnamelist ::= Name attrib {&lsquo;<b>,</b>&rsquo; Name attrib}
+</pre><p>
+If present, an initial assignment has the same semantics
+of a multiple assignment (see <a href="#3.3.3">&sect;3.3.3</a>).
+Otherwise, all variables are initialized with <b>nil</b>.
+
+
+<p>
+Each variable name may be postfixed by an attribute
+(a name between angle brackets):
+
+<pre>
+ attrib ::= [&lsquo;<b>&lt;</b>&rsquo; Name &lsquo;<b>&gt;</b>&rsquo;]
+</pre><p>
+There are two possible attributes:
+<code>const</code>, which declares a constant variable,
+that is, a variable that cannot be assigned to
+after its initialization;
+and <code>close</code>, which declares a to-be-closed variable (see <a href="#3.3.8">&sect;3.3.8</a>).
+A list of variables can contain at most one to-be-closed variable.
+
+
+<p>
+A chunk is also a block (see <a href="#3.3.2">&sect;3.3.2</a>),
+and so local variables can be declared in a chunk outside any explicit block.
+
+
+<p>
+The visibility rules for local variables are explained in <a href="#3.5">&sect;3.5</a>.
+
+
+
+
+
+<h3>3.3.8 &ndash; <a name="3.3.8">To-be-closed Variables</a></h3>
+
+<p>
+A to-be-closed variable behaves like a constant local variable,
+except that its value is <em>closed</em> whenever the variable
+goes out of scope, including normal block termination,
+exiting its block by <b>break</b>/<b>goto</b>/<b>return</b>,
+or exiting by an error.
+
+
+<p>
+Here, to <em>close</em> a value means
+to call its <code>__close</code> metamethod.
+When calling the metamethod,
+the value itself is passed as the first argument
+and the error object that caused the exit (if any)
+is passed as a second argument;
+if there was no error, the second argument is <b>nil</b>.
+
+
+<p>
+The value assigned to a to-be-closed variable
+must have a <code>__close</code> metamethod
+or be a false value.
+(<b>nil</b> and <b>false</b> are ignored as to-be-closed values.)
+
+
+<p>
+If several to-be-closed variables go out of scope at the same event,
+they are closed in the reverse order that they were declared.
+
+
+<p>
+If there is any error while running a closing method,
+that error is handled like an error in the regular code
+where the variable was defined.
+After an error,
+the other pending closing methods will still be called.
+
+
+<p>
+If a coroutine yields and is never resumed again,
+some variables may never go out of scope,
+and therefore they will never be closed.
+(These variables are the ones created inside the coroutine
+and in scope at the point where the coroutine yielded.)
+Similarly, if a coroutine ends with an error,
+it does not unwind its stack,
+so it does not close any variable.
+In both cases,
+you can either use finalizers
+or call <a href="#pdf-coroutine.close"><code>coroutine.close</code></a> to close the variables.
+However, if the coroutine was created
+through <a href="#pdf-coroutine.wrap"><code>coroutine.wrap</code></a>,
+then its corresponding function will close the coroutine
+in case of errors.
+
+
+
+
+
+
+
+<h2>3.4 &ndash; <a name="3.4">Expressions</a></h2>
+
+
+
+<p>
+The basic expressions in Lua are the following:
+
+<pre>
+ exp ::= prefixexp
+ exp ::= <b>nil</b> | <b>false</b> | <b>true</b>
+ exp ::= Numeral
+ exp ::= LiteralString
+ exp ::= functiondef
+ exp ::= tableconstructor
+ exp ::= &lsquo;<b>...</b>&rsquo;
+ exp ::= exp binop exp
+ exp ::= unop exp
+ prefixexp ::= var | functioncall | &lsquo;<b>(</b>&rsquo; exp &lsquo;<b>)</b>&rsquo;
+</pre>
+
+<p>
+Numerals and literal strings are explained in <a href="#3.1">&sect;3.1</a>;
+variables are explained in <a href="#3.2">&sect;3.2</a>;
+function definitions are explained in <a href="#3.4.11">&sect;3.4.11</a>;
+function calls are explained in <a href="#3.4.10">&sect;3.4.10</a>;
+table constructors are explained in <a href="#3.4.9">&sect;3.4.9</a>.
+Vararg expressions,
+denoted by three dots ('<code>...</code>'), can only be used when
+directly inside a variadic function;
+they are explained in <a href="#3.4.11">&sect;3.4.11</a>.
+
+
+<p>
+Binary operators comprise arithmetic operators (see <a href="#3.4.1">&sect;3.4.1</a>),
+bitwise operators (see <a href="#3.4.2">&sect;3.4.2</a>),
+relational operators (see <a href="#3.4.4">&sect;3.4.4</a>), logical operators (see <a href="#3.4.5">&sect;3.4.5</a>),
+and the concatenation operator (see <a href="#3.4.6">&sect;3.4.6</a>).
+Unary operators comprise the unary minus (see <a href="#3.4.1">&sect;3.4.1</a>),
+the unary bitwise NOT (see <a href="#3.4.2">&sect;3.4.2</a>),
+the unary logical <b>not</b> (see <a href="#3.4.5">&sect;3.4.5</a>),
+and the unary <em>length operator</em> (see <a href="#3.4.7">&sect;3.4.7</a>).
+
+
+
+
+
+<h3>3.4.1 &ndash; <a name="3.4.1">Arithmetic Operators</a></h3><p>
+Lua supports the following arithmetic operators:
+
+<ul>
+<li><b><code>+</code>: </b>addition</li>
+<li><b><code>-</code>: </b>subtraction</li>
+<li><b><code>*</code>: </b>multiplication</li>
+<li><b><code>/</code>: </b>float division</li>
+<li><b><code>//</code>: </b>floor division</li>
+<li><b><code>%</code>: </b>modulo</li>
+<li><b><code>^</code>: </b>exponentiation</li>
+<li><b><code>-</code>: </b>unary minus</li>
+</ul>
+
+<p>
+With the exception of exponentiation and float division,
+the arithmetic operators work as follows:
+If both operands are integers,
+the operation is performed over integers and the result is an integer.
+Otherwise, if both operands are numbers,
+then they are converted to floats,
+the operation is performed following the machine's rules
+for floating-point arithmetic
+(usually the IEEE 754 standard),
+and the result is a float.
+(The string library coerces strings to numbers in
+arithmetic operations; see <a href="#3.4.3">&sect;3.4.3</a> for details.)
+
+
+<p>
+Exponentiation and float division (<code>/</code>)
+always convert their operands to floats
+and the result is always a float.
+Exponentiation uses the ISO&nbsp;C function <code>pow</code>,
+so that it works for non-integer exponents too.
+
+
+<p>
+Floor division (<code>//</code>) is a division
+that rounds the quotient towards minus infinity,
+resulting in the floor of the division of its operands.
+
+
+<p>
+Modulo is defined as the remainder of a division
+that rounds the quotient towards minus infinity (floor division).
+
+
+<p>
+In case of overflows in integer arithmetic,
+all operations <em>wrap around</em>.
+
+
+
+<h3>3.4.2 &ndash; <a name="3.4.2">Bitwise Operators</a></h3><p>
+Lua supports the following bitwise operators:
+
+<ul>
+<li><b><code>&amp;</code>: </b>bitwise AND</li>
+<li><b><code>&#124;</code>: </b>bitwise OR</li>
+<li><b><code>~</code>: </b>bitwise exclusive OR</li>
+<li><b><code>&gt;&gt;</code>: </b>right shift</li>
+<li><b><code>&lt;&lt;</code>: </b>left shift</li>
+<li><b><code>~</code>: </b>unary bitwise NOT</li>
+</ul>
+
+<p>
+All bitwise operations convert its operands to integers
+(see <a href="#3.4.3">&sect;3.4.3</a>),
+operate on all bits of those integers,
+and result in an integer.
+
+
+<p>
+Both right and left shifts fill the vacant bits with zeros.
+Negative displacements shift to the other direction;
+displacements with absolute values equal to or higher than
+the number of bits in an integer
+result in zero (as all bits are shifted out).
+
+
+
+
+
+<h3>3.4.3 &ndash; <a name="3.4.3">Coercions and Conversions</a></h3><p>
+Lua provides some automatic conversions between some
+types and representations at run time.
+Bitwise operators always convert float operands to integers.
+Exponentiation and float division
+always convert integer operands to floats.
+All other arithmetic operations applied to mixed numbers
+(integers and floats) convert the integer operand to a float.
+The C API also converts both integers to floats and
+floats to integers, as needed.
+Moreover, string concatenation accepts numbers as arguments,
+besides strings.
+
+
+<p>
+In a conversion from integer to float,
+if the integer value has an exact representation as a float,
+that is the result.
+Otherwise,
+the conversion gets the nearest higher or
+the nearest lower representable value.
+This kind of conversion never fails.
+
+
+<p>
+The conversion from float to integer
+checks whether the float has an exact representation as an integer
+(that is, the float has an integral value and
+it is in the range of integer representation).
+If it does, that representation is the result.
+Otherwise, the conversion fails.
+
+
+<p>
+Several places in Lua coerce strings to numbers when necessary.
+In particular,
+the string library sets metamethods that try to coerce
+strings to numbers in all arithmetic operations.
+If the conversion fails,
+the library calls the metamethod of the other operand
+(if present) or it raises an error.
+Note that bitwise operators do not do this coercion.
+
+
+<p>
+It is always a good practice not to rely on the
+implicit coercions from strings to numbers,
+as they are not always applied;
+in particular, <code>"1"==1</code> is false and <code>"1"&lt;1</code> raises an error
+(see <a href="#3.4.4">&sect;3.4.4</a>).
+These coercions exist mainly for compatibility and may be removed
+in future versions of the language.
+
+
+<p>
+A string is converted to an integer or a float
+following its syntax and the rules of the Lua lexer.
+The string may have also leading and trailing whitespaces and a sign.
+All conversions from strings to numbers
+accept both a dot and the current locale mark
+as the radix character.
+(The Lua lexer, however, accepts only a dot.)
+If the string is not a valid numeral,
+the conversion fails.
+If necessary, the result of this first step is then converted
+to a specific number subtype following the previous rules
+for conversions between floats and integers.
+
+
+<p>
+The conversion from numbers to strings uses a
+non-specified human-readable format.
+To convert numbers to strings in any specific way,
+use the function <a href="#pdf-string.format"><code>string.format</code></a>.
+
+
+
+
+
+<h3>3.4.4 &ndash; <a name="3.4.4">Relational Operators</a></h3><p>
+Lua supports the following relational operators:
+
+<ul>
+<li><b><code>==</code>: </b>equality</li>
+<li><b><code>~=</code>: </b>inequality</li>
+<li><b><code>&lt;</code>: </b>less than</li>
+<li><b><code>&gt;</code>: </b>greater than</li>
+<li><b><code>&lt;=</code>: </b>less or equal</li>
+<li><b><code>&gt;=</code>: </b>greater or equal</li>
+</ul><p>
+These operators always result in <b>false</b> or <b>true</b>.
+
+
+<p>
+Equality (<code>==</code>) first compares the type of its operands.
+If the types are different, then the result is <b>false</b>.
+Otherwise, the values of the operands are compared.
+Strings are equal if they have the same byte content.
+Numbers are equal if they denote the same mathematical value.
+
+
+<p>
+Tables, userdata, and threads
+are compared by reference:
+two objects are considered equal only if they are the same object.
+Every time you create a new object
+(a table, a userdata, or a thread),
+this new object is different from any previously existing object.
+A function is always equal to itself.
+Functions with any detectable difference
+(different behavior, different definition) are always different.
+Functions created at different times but with no detectable differences
+may be classified as equal or not
+(depending on internal caching details).
+
+
+<p>
+You can change the way that Lua compares tables and userdata
+by using the <code>__eq</code> metamethod (see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+Equality comparisons do not convert strings to numbers
+or vice versa.
+Thus, <code>"0"==0</code> evaluates to <b>false</b>,
+and <code>t[0]</code> and <code>t["0"]</code> denote different
+entries in a table.
+
+
+<p>
+The operator <code>~=</code> is exactly the negation of equality (<code>==</code>).
+
+
+<p>
+The order operators work as follows.
+If both arguments are numbers,
+then they are compared according to their mathematical values,
+regardless of their subtypes.
+Otherwise, if both arguments are strings,
+then their values are compared according to the current locale.
+Otherwise, Lua tries to call the <code>__lt</code> or the <code>__le</code>
+metamethod (see <a href="#2.4">&sect;2.4</a>).
+A comparison <code>a &gt; b</code> is translated to <code>b &lt; a</code>
+and <code>a &gt;= b</code> is translated to <code>b &lt;= a</code>.
+
+
+<p>
+Following the IEEE 754 standard,
+the special value NaN is considered neither less than,
+nor equal to, nor greater than any value, including itself.
+
+
+
+
+
+<h3>3.4.5 &ndash; <a name="3.4.5">Logical Operators</a></h3><p>
+The logical operators in Lua are
+<b>and</b>, <b>or</b>, and <b>not</b>.
+Like the control structures (see <a href="#3.3.4">&sect;3.3.4</a>),
+all logical operators consider both <b>false</b> and <b>nil</b> as false
+and anything else as true.
+
+
+<p>
+The negation operator <b>not</b> always returns <b>false</b> or <b>true</b>.
+The conjunction operator <b>and</b> returns its first argument
+if this value is <b>false</b> or <b>nil</b>;
+otherwise, <b>and</b> returns its second argument.
+The disjunction operator <b>or</b> returns its first argument
+if this value is different from <b>nil</b> and <b>false</b>;
+otherwise, <b>or</b> returns its second argument.
+Both <b>and</b> and <b>or</b> use short-circuit evaluation;
+that is,
+the second operand is evaluated only if necessary.
+Here are some examples:
+
+<pre>
+ 10 or 20 --&gt; 10
+ 10 or error() --&gt; 10
+ nil or "a" --&gt; "a"
+ nil and 10 --&gt; nil
+ false and error() --&gt; false
+ false and nil --&gt; false
+ false or nil --&gt; nil
+ 10 and 20 --&gt; 20
+</pre>
+
+
+
+
+<h3>3.4.6 &ndash; <a name="3.4.6">Concatenation</a></h3><p>
+The string concatenation operator in Lua is
+denoted by two dots ('<code>..</code>').
+If both operands are strings or numbers,
+then the numbers are converted to strings
+in a non-specified format (see <a href="#3.4.3">&sect;3.4.3</a>).
+Otherwise, the <code>__concat</code> metamethod is called (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<h3>3.4.7 &ndash; <a name="3.4.7">The Length Operator</a></h3>
+
+<p>
+The length operator is denoted by the unary prefix operator <code>#</code>.
+
+
+<p>
+The length of a string is its number of bytes.
+(That is the usual meaning of string length when each
+character is one byte.)
+
+
+<p>
+The length operator applied on a table
+returns a border in that table.
+A <em>border</em> in a table <code>t</code> is any non-negative integer
+that satisfies the following condition:
+
+<pre>
+ (border == 0 or t[border] ~= nil) and
+ (t[border + 1] == nil or border == math.maxinteger)
+</pre><p>
+In words,
+a border is any positive integer index present in the table
+that is followed by an absent index,
+plus two limit cases:
+zero, when index 1 is absent;
+and the maximum value for an integer, when that index is present.
+Note that keys that are not positive integers
+do not interfere with borders.
+
+
+<p>
+A table with exactly one border is called a <em>sequence</em>.
+For instance, the table <code>{10, 20, 30, 40, 50}</code> is a sequence,
+as it has only one border (5).
+The table <code>{10, 20, 30, nil, 50}</code> has two borders (3 and 5),
+and therefore it is not a sequence.
+(The <b>nil</b> at index 4 is called a <em>hole</em>.)
+The table <code>{nil, 20, 30, nil, nil, 60, nil}</code>
+has three borders (0, 3, and 6),
+so it is not a sequence, too.
+The table <code>{}</code> is a sequence with border 0.
+
+
+<p>
+When <code>t</code> is a sequence,
+<code>#t</code> returns its only border,
+which corresponds to the intuitive notion of the length of the sequence.
+When <code>t</code> is not a sequence,
+<code>#t</code> can return any of its borders.
+(The exact one depends on details of
+the internal representation of the table,
+which in turn can depend on how the table was populated and
+the memory addresses of its non-numeric keys.)
+
+
+<p>
+The computation of the length of a table
+has a guaranteed worst time of <em>O(log n)</em>,
+where <em>n</em> is the largest integer key in the table.
+
+
+<p>
+A program can modify the behavior of the length operator for
+any value but strings through the <code>__len</code> metamethod (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<h3>3.4.8 &ndash; <a name="3.4.8">Precedence</a></h3><p>
+Operator precedence in Lua follows the table below,
+from lower to higher priority:
+
+<pre>
+ or
+ and
+ &lt; &gt; &lt;= &gt;= ~= ==
+ |
+ ~
+ &amp;
+ &lt;&lt; &gt;&gt;
+ ..
+ + -
+ * / // %
+ unary operators (not # - ~)
+ ^
+</pre><p>
+As usual,
+you can use parentheses to change the precedences of an expression.
+The concatenation ('<code>..</code>') and exponentiation ('<code>^</code>')
+operators are right associative.
+All other binary operators are left associative.
+
+
+
+
+
+<h3>3.4.9 &ndash; <a name="3.4.9">Table Constructors</a></h3><p>
+Table constructors are expressions that create tables.
+Every time a constructor is evaluated, a new table is created.
+A constructor can be used to create an empty table
+or to create a table and initialize some of its fields.
+The general syntax for constructors is
+
+<pre>
+ tableconstructor ::= &lsquo;<b>{</b>&rsquo; [fieldlist] &lsquo;<b>}</b>&rsquo;
+ fieldlist ::= field {fieldsep field} [fieldsep]
+ field ::= &lsquo;<b>[</b>&rsquo; exp &lsquo;<b>]</b>&rsquo; &lsquo;<b>=</b>&rsquo; exp | Name &lsquo;<b>=</b>&rsquo; exp | exp
+ fieldsep ::= &lsquo;<b>,</b>&rsquo; | &lsquo;<b>;</b>&rsquo;
+</pre>
+
+<p>
+Each field of the form <code>[exp1] = exp2</code> adds to the new table an entry
+with key <code>exp1</code> and value <code>exp2</code>.
+A field of the form <code>name = exp</code> is equivalent to
+<code>["name"] = exp</code>.
+Fields of the form <code>exp</code> are equivalent to
+<code>[i] = exp</code>, where <code>i</code> are consecutive integers
+starting with 1;
+fields in the other formats do not affect this counting.
+For example,
+
+<pre>
+ a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }
+</pre><p>
+is equivalent to
+
+<pre>
+ do
+ local t = {}
+ t[f(1)] = g
+ t[1] = "x" -- 1st exp
+ t[2] = "y" -- 2nd exp
+ t.x = 1 -- t["x"] = 1
+ t[3] = f(x) -- 3rd exp
+ t[30] = 23
+ t[4] = 45 -- 4th exp
+ a = t
+ end
+</pre>
+
+<p>
+The order of the assignments in a constructor is undefined.
+(This order would be relevant only when there are repeated keys.)
+
+
+<p>
+If the last field in the list has the form <code>exp</code>
+and the expression is a multires expression,
+then all values returned by this expression enter the list consecutively
+(see <a href="#3.4.12">&sect;3.4.12</a>).
+
+
+<p>
+The field list can have an optional trailing separator,
+as a convenience for machine-generated code.
+
+
+
+
+
+<h3>3.4.10 &ndash; <a name="3.4.10">Function Calls</a></h3><p>
+A function call in Lua has the following syntax:
+
+<pre>
+ functioncall ::= prefixexp args
+</pre><p>
+In a function call,
+first prefixexp and args are evaluated.
+If the value of prefixexp has type <em>function</em>,
+then this function is called
+with the given arguments.
+Otherwise, if present,
+the prefixexp <code>__call</code> metamethod is called:
+its first argument is the value of prefixexp,
+followed by the original call arguments
+(see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+The form
+
+<pre>
+ functioncall ::= prefixexp &lsquo;<b>:</b>&rsquo; Name args
+</pre><p>
+can be used to emulate methods.
+A call <code>v:name(<em>args</em>)</code>
+is syntactic sugar for <code>v.name(v,<em>args</em>)</code>,
+except that <code>v</code> is evaluated only once.
+
+
+<p>
+Arguments have the following syntax:
+
+<pre>
+ args ::= &lsquo;<b>(</b>&rsquo; [explist] &lsquo;<b>)</b>&rsquo;
+ args ::= tableconstructor
+ args ::= LiteralString
+</pre><p>
+All argument expressions are evaluated before the call.
+A call of the form <code>f{<em>fields</em>}</code> is
+syntactic sugar for <code>f({<em>fields</em>})</code>;
+that is, the argument list is a single new table.
+A call of the form <code>f'<em>string</em>'</code>
+(or <code>f"<em>string</em>"</code> or <code>f[[<em>string</em>]]</code>)
+is syntactic sugar for <code>f('<em>string</em>')</code>;
+that is, the argument list is a single literal string.
+
+
+<p>
+A call of the form <code>return <em>functioncall</em></code> not in the
+scope of a to-be-closed variable is called a <em>tail call</em>.
+Lua implements <em>proper tail calls</em>
+(or <em>proper tail recursion</em>):
+In a tail call,
+the called function reuses the stack entry of the calling function.
+Therefore, there is no limit on the number of nested tail calls that
+a program can execute.
+However, a tail call erases any debug information about the
+calling function.
+Note that a tail call only happens with a particular syntax,
+where the <b>return</b> has one single function call as argument,
+and it is outside the scope of any to-be-closed variable.
+This syntax makes the calling function return exactly
+the returns of the called function,
+without any intervening action.
+So, none of the following examples are tail calls:
+
+<pre>
+ return (f(x)) -- results adjusted to 1
+ return 2 * f(x) -- result multiplied by 2
+ return x, f(x) -- additional results
+ f(x); return -- results discarded
+ return x or f(x) -- results adjusted to 1
+</pre>
+
+
+
+
+<h3>3.4.11 &ndash; <a name="3.4.11">Function Definitions</a></h3>
+
+<p>
+The syntax for function definition is
+
+<pre>
+ functiondef ::= <b>function</b> funcbody
+ funcbody ::= &lsquo;<b>(</b>&rsquo; [parlist] &lsquo;<b>)</b>&rsquo; block <b>end</b>
+</pre>
+
+<p>
+The following syntactic sugar simplifies function definitions:
+
+<pre>
+ stat ::= <b>function</b> funcname funcbody
+ stat ::= <b>local</b> <b>function</b> Name funcbody
+ funcname ::= Name {&lsquo;<b>.</b>&rsquo; Name} [&lsquo;<b>:</b>&rsquo; Name]
+</pre><p>
+The statement
+
+<pre>
+ function f () <em>body</em> end
+</pre><p>
+translates to
+
+<pre>
+ f = function () <em>body</em> end
+</pre><p>
+The statement
+
+<pre>
+ function t.a.b.c.f () <em>body</em> end
+</pre><p>
+translates to
+
+<pre>
+ t.a.b.c.f = function () <em>body</em> end
+</pre><p>
+The statement
+
+<pre>
+ local function f () <em>body</em> end
+</pre><p>
+translates to
+
+<pre>
+ local f; f = function () <em>body</em> end
+</pre><p>
+not to
+
+<pre>
+ local f = function () <em>body</em> end
+</pre><p>
+(This only makes a difference when the body of the function
+contains references to <code>f</code>.)
+
+
+<p>
+A function definition is an executable expression,
+whose value has type <em>function</em>.
+When Lua precompiles a chunk,
+all its function bodies are precompiled too,
+but they are not created yet.
+Then, whenever Lua executes the function definition,
+the function is <em>instantiated</em> (or <em>closed</em>).
+This function instance, or <em>closure</em>,
+is the final value of the expression.
+
+
+<p>
+Parameters act as local variables that are
+initialized with the argument values:
+
+<pre>
+ parlist ::= namelist [&lsquo;<b>,</b>&rsquo; &lsquo;<b>...</b>&rsquo;] | &lsquo;<b>...</b>&rsquo;
+</pre><p>
+When a Lua function is called,
+it adjusts its list of arguments to
+the length of its list of parameters (see <a href="#3.4.12">&sect;3.4.12</a>),
+unless the function is a <em>variadic function</em>,
+which is indicated by three dots ('<code>...</code>')
+at the end of its parameter list.
+A variadic function does not adjust its argument list;
+instead, it collects all extra arguments and supplies them
+to the function through a <em>vararg expression</em>,
+which is also written as three dots.
+The value of this expression is a list of all actual extra arguments,
+similar to a function with multiple results (see <a href="#3.4.12">&sect;3.4.12</a>).
+
+
+<p>
+As an example, consider the following definitions:
+
+<pre>
+ function f(a, b) end
+ function g(a, b, ...) end
+ function r() return 1,2,3 end
+</pre><p>
+Then, we have the following mapping from arguments to parameters and
+to the vararg expression:
+
+<pre>
+ CALL PARAMETERS
+
+ f(3) a=3, b=nil
+ f(3, 4) a=3, b=4
+ f(3, 4, 5) a=3, b=4
+ f(r(), 10) a=1, b=10
+ f(r()) a=1, b=2
+
+ g(3) a=3, b=nil, ... --&gt; (nothing)
+ g(3, 4) a=3, b=4, ... --&gt; (nothing)
+ g(3, 4, 5, 8) a=3, b=4, ... --&gt; 5 8
+ g(5, r()) a=5, b=1, ... --&gt; 2 3
+</pre>
+
+<p>
+Results are returned using the <b>return</b> statement (see <a href="#3.3.4">&sect;3.3.4</a>).
+If control reaches the end of a function
+without encountering a <b>return</b> statement,
+then the function returns with no results.
+
+
+<p>
+
+There is a system-dependent limit on the number of values
+that a function may return.
+This limit is guaranteed to be greater than 1000.
+
+
+<p>
+The <em>colon</em> syntax
+is used to emulate <em>methods</em>,
+adding an implicit extra parameter <code>self</code> to the function.
+Thus, the statement
+
+<pre>
+ function t.a.b.c:f (<em>params</em>) <em>body</em> end
+</pre><p>
+is syntactic sugar for
+
+<pre>
+ t.a.b.c.f = function (self, <em>params</em>) <em>body</em> end
+</pre>
+
+
+
+
+<h3>3.4.12 &ndash; <a name="3.4.12">Lists of expressions, multiple results,
+and adjustment</a></h3>
+
+<p>
+Both function calls and vararg expressions can result in multiple values.
+These expressions are called <em>multires expressions</em>.
+
+
+<p>
+When a multires expression is used as the last element
+of a list of expressions,
+all results from the expression are added to the
+list of values produced by the list of expressions.
+Note that a single expression
+in a place that expects a list of expressions
+is the last expression in that (singleton) list.
+
+
+<p>
+These are the places where Lua expects a list of expressions:
+
+<ul>
+
+<li>A <b>return</b> statement,
+for instance <code>return e1, e2, e3</code> (see <a href="#3.3.4">&sect;3.3.4</a>).</li>
+
+<li>A table constructor,
+for instance <code>{e1, e2, e3}</code> (see <a href="#3.4.9">&sect;3.4.9</a>).</li>
+
+<li>The arguments of a function call,
+for instance <code>foo(e1, e2, e3)</code> (see <a href="#3.4.10">&sect;3.4.10</a>).</li>
+
+<li>A multiple assignment,
+for instance <code>a , b, c = e1, e2, e3</code> (see <a href="#3.3.3">&sect;3.3.3</a>).</li>
+
+<li>A local declaration,
+for instance <code>local a , b, c = e1, e2, e3</code> (see <a href="#3.3.7">&sect;3.3.7</a>).</li>
+
+<li>The initial values in a generic <b>for</b> loop,
+for instance <code>for k in e1, e2, e3 do ... end</code> (see <a href="#3.3.5">&sect;3.3.5</a>).</li>
+
+</ul><p>
+In the last four cases,
+the list of values from the list of expressions
+must be <em>adjusted</em> to a specific length:
+the number of parameters in a call to a non-variadic function
+(see <a href="#3.4.11">&sect;3.4.11</a>),
+the number of variables in a multiple assignment or
+a local declaration,
+and exactly four values for a generic <b>for</b> loop.
+The <em>adjustment</em> follows these rules:
+If there are more values than needed,
+the extra values are thrown away;
+if there are fewer values than needed,
+the list is extended with <b>nil</b>'s.
+When the list of expressions ends with a multires expression,
+all results from that expression enter the list of values
+before the adjustment.
+
+
+<p>
+When a multires expression is used
+in a list of expressions without being the last element,
+or in a place where the syntax expects a single expression,
+Lua adjusts the result list of that expression to one element.
+As a particular case,
+the syntax expects a single expression inside a parenthesized expression;
+therefore, adding parentheses around a multires expression
+forces it to produce exactly one result.
+
+
+<p>
+We seldom need to use a vararg expression in a place
+where the syntax expects a single expression.
+(Usually it is simpler to add a regular parameter before
+the variadic part and use that parameter.)
+When there is such a need,
+we recommend assigning the vararg expression
+to a single variable and using that variable
+in its place.
+
+
+<p>
+Here are some examples of uses of mutlres expressions.
+In all cases, when the construction needs
+"the n-th result" and there is no such result,
+it uses a <b>nil</b>.
+
+<pre>
+ print(x, f()) -- prints x and all results from f().
+ print(x, (f())) -- prints x and the first result from f().
+ print(f(), x) -- prints the first result from f() and x.
+ print(1 + f()) -- prints 1 added to the first result from f().
+ local x = ... -- x gets the first vararg argument.
+ x,y = ... -- x gets the first vararg argument,
+ -- y gets the second vararg argument.
+ x,y,z = w, f() -- x gets w, y gets the first result from f(),
+ -- z gets the second result from f().
+ x,y,z = f() -- x gets the first result from f(),
+ -- y gets the second result from f(),
+ -- z gets the third result from f().
+ x,y,z = f(), g() -- x gets the first result from f(),
+ -- y gets the first result from g(),
+ -- z gets the second result from g().
+ x,y,z = (f()) -- x gets the first result from f(), y and z get nil.
+ return f() -- returns all results from f().
+ return x, ... -- returns x and all received vararg arguments.
+ return x,y,f() -- returns x, y, and all results from f().
+ {f()} -- creates a list with all results from f().
+ {...} -- creates a list with all vararg arguments.
+ {f(), 5} -- creates a list with the first result from f() and 5.
+</pre>
+
+
+
+
+
+
+<h2>3.5 &ndash; <a name="3.5">Visibility Rules</a></h2>
+
+<p>
+
+Lua is a lexically scoped language.
+The scope of a local variable begins at the first statement after
+its declaration and lasts until the last non-void statement
+of the innermost block that includes the declaration.
+(<em>Void statements</em> are labels and empty statements.)
+Consider the following example:
+
+<pre>
+ x = 10 -- global variable
+ do -- new block
+ local x = x -- new 'x', with value 10
+ print(x) --&gt; 10
+ x = x+1
+ do -- another block
+ local x = x+1 -- another 'x'
+ print(x) --&gt; 12
+ end
+ print(x) --&gt; 11
+ end
+ print(x) --&gt; 10 (the global one)
+</pre>
+
+<p>
+Notice that, in a declaration like <code>local x = x</code>,
+the new <code>x</code> being declared is not in scope yet,
+and so the second <code>x</code> refers to the outside variable.
+
+
+<p>
+Because of the lexical scoping rules,
+local variables can be freely accessed by functions
+defined inside their scope.
+A local variable used by an inner function is called an <em>upvalue</em>
+(or <em>external local variable</em>, or simply <em>external variable</em>)
+inside the inner function.
+
+
+<p>
+Notice that each execution of a <b>local</b> statement
+defines new local variables.
+Consider the following example:
+
+<pre>
+ a = {}
+ local x = 20
+ for i = 1, 10 do
+ local y = 0
+ a[i] = function () y = y + 1; return x + y end
+ end
+</pre><p>
+The loop creates ten closures
+(that is, ten instances of the anonymous function).
+Each of these closures uses a different <code>y</code> variable,
+while all of them share the same <code>x</code>.
+
+
+
+
+
+<h1>4 &ndash; <a name="4">The Application Program Interface</a></h1>
+
+
+
+<p>
+
+This section describes the C&nbsp;API for Lua, that is,
+the set of C&nbsp;functions available to the host program to communicate
+with Lua.
+All API functions and related types and constants
+are declared in the header file <a name="pdf-lua.h"><code>lua.h</code></a>.
+
+
+<p>
+Even when we use the term "function",
+any facility in the API may be provided as a macro instead.
+Except where stated otherwise,
+all such macros use each of their arguments exactly once
+(except for the first argument, which is always a Lua state),
+and so do not generate any hidden side-effects.
+
+
+<p>
+As in most C&nbsp;libraries,
+the Lua API functions do not check their arguments
+for validity or consistency.
+However, you can change this behavior by compiling Lua
+with the macro <a name="pdf-LUA_USE_APICHECK"><code>LUA_USE_APICHECK</code></a> defined.
+
+
+<p>
+The Lua library is fully reentrant:
+it has no global variables.
+It keeps all information it needs in a dynamic structure,
+called the <em>Lua state</em>.
+
+
+<p>
+Each Lua state has one or more threads,
+which correspond to independent, cooperative lines of execution.
+The type <a href="#lua_State"><code>lua_State</code></a> (despite its name) refers to a thread.
+(Indirectly, through the thread, it also refers to the
+Lua state associated to the thread.)
+
+
+<p>
+A pointer to a thread must be passed as the first argument to
+every function in the library, except to <a href="#lua_newstate"><code>lua_newstate</code></a>,
+which creates a Lua state from scratch and returns a pointer
+to the <em>main thread</em> in the new state.
+
+
+
+
+
+<h2>4.1 &ndash; <a name="4.1">The Stack</a></h2>
+
+
+
+<p>
+Lua uses a <em>virtual stack</em> to pass values to and from C.
+Each element in this stack represents a Lua value
+(<b>nil</b>, number, string, etc.).
+Functions in the API can access this stack through the
+Lua state parameter that they receive.
+
+
+<p>
+Whenever Lua calls C, the called function gets a new stack,
+which is independent of previous stacks and of stacks of
+C&nbsp;functions that are still active.
+This stack initially contains any arguments to the C&nbsp;function
+and it is where the C&nbsp;function can store temporary
+Lua values and must push its results
+to be returned to the caller (see <a href="#lua_CFunction"><code>lua_CFunction</code></a>).
+
+
+<p>
+For convenience,
+most query operations in the API do not follow a strict stack discipline.
+Instead, they can refer to any element in the stack
+by using an <em>index</em>:
+A positive index represents an absolute stack position,
+starting at&nbsp;1 as the bottom of the stack;
+a negative index represents an offset relative to the top of the stack.
+More specifically, if the stack has <em>n</em> elements,
+then index&nbsp;1 represents the first element
+(that is, the element that was pushed onto the stack first)
+and
+index&nbsp;<em>n</em> represents the last element;
+index&nbsp;-1 also represents the last element
+(that is, the element at the&nbsp;top)
+and index <em>-n</em> represents the first element.
+
+
+
+
+
+<h3>4.1.1 &ndash; <a name="4.1.1">Stack Size</a></h3>
+
+<p>
+When you interact with the Lua API,
+you are responsible for ensuring consistency.
+In particular,
+<em>you are responsible for controlling stack overflow</em>.
+When you call any API function,
+you must ensure the stack has enough room to accommodate the results.
+
+
+<p>
+There is one exception to the above rule:
+When you call a Lua function
+without a fixed number of results (see <a href="#lua_call"><code>lua_call</code></a>),
+Lua ensures that the stack has enough space for all results.
+However, it does not ensure any extra space.
+So, before pushing anything on the stack after such a call
+you should use <a href="#lua_checkstack"><code>lua_checkstack</code></a>.
+
+
+<p>
+Whenever Lua calls C,
+it ensures that the stack has space for
+at least <a name="pdf-LUA_MINSTACK"><code>LUA_MINSTACK</code></a> extra elements;
+that is, you can safely push up to <code>LUA_MINSTACK</code> values into it.
+<code>LUA_MINSTACK</code> is defined as 20,
+so that usually you do not have to worry about stack space
+unless your code has loops pushing elements onto the stack.
+Whenever necessary,
+you can use the function <a href="#lua_checkstack"><code>lua_checkstack</code></a>
+to ensure that the stack has enough space for pushing new elements.
+
+
+
+
+
+<h3>4.1.2 &ndash; <a name="4.1.2">Valid and Acceptable Indices</a></h3>
+
+<p>
+Any function in the API that receives stack indices
+works only with <em>valid indices</em> or <em>acceptable indices</em>.
+
+
+<p>
+A <em>valid index</em> is an index that refers to a
+position that stores a modifiable Lua value.
+It comprises stack indices between&nbsp;1 and the stack top
+(<code>1 &le; abs(index) &le; top</code>)
+
+plus <em>pseudo-indices</em>,
+which represent some positions that are accessible to C&nbsp;code
+but that are not in the stack.
+Pseudo-indices are used to access the registry (see <a href="#4.3">&sect;4.3</a>)
+and the upvalues of a C&nbsp;function (see <a href="#4.2">&sect;4.2</a>).
+
+
+<p>
+Functions that do not need a specific mutable position,
+but only a value (e.g., query functions),
+can be called with acceptable indices.
+An <em>acceptable index</em> can be any valid index,
+but it also can be any positive index after the stack top
+within the space allocated for the stack,
+that is, indices up to the stack size.
+(Note that 0 is never an acceptable index.)
+Indices to upvalues (see <a href="#4.2">&sect;4.2</a>) greater than the real number
+of upvalues in the current C&nbsp;function are also acceptable (but invalid).
+Except when noted otherwise,
+functions in the API work with acceptable indices.
+
+
+<p>
+Acceptable indices serve to avoid extra tests
+against the stack top when querying the stack.
+For instance, a C&nbsp;function can query its third argument
+without the need to check whether there is a third argument,
+that is, without the need to check whether 3 is a valid index.
+
+
+<p>
+For functions that can be called with acceptable indices,
+any non-valid index is treated as if it
+contains a value of a virtual type <a name="pdf-LUA_TNONE"><code>LUA_TNONE</code></a>,
+which behaves like a nil value.
+
+
+
+
+
+<h3>4.1.3 &ndash; <a name="4.1.3">Pointers to strings</a></h3>
+
+<p>
+Several functions in the API return pointers (<code>const char*</code>)
+to Lua strings in the stack.
+(See <a href="#lua_pushfstring"><code>lua_pushfstring</code></a>, <a href="#lua_pushlstring"><code>lua_pushlstring</code></a>,
+<a href="#lua_pushstring"><code>lua_pushstring</code></a>, and <a href="#lua_tolstring"><code>lua_tolstring</code></a>.
+See also <a href="#luaL_checklstring"><code>luaL_checklstring</code></a>, <a href="#luaL_checkstring"><code>luaL_checkstring</code></a>,
+and <a href="#luaL_tolstring"><code>luaL_tolstring</code></a> in the auxiliary library.)
+
+
+<p>
+In general,
+Lua's garbage collection can free or move internal memory
+and then invalidate pointers to internal strings.
+To allow a safe use of these pointers,
+the API guarantees that any pointer to a string in a stack index
+is valid while the string value at that index is not removed from the stack.
+(It can be moved to another index, though.)
+When the index is a pseudo-index (referring to an upvalue),
+the pointer is valid while the corresponding call is active and
+the corresponding upvalue is not modified.
+
+
+<p>
+Some functions in the debug interface
+also return pointers to strings,
+namely <a href="#lua_getlocal"><code>lua_getlocal</code></a>, <a href="#lua_getupvalue"><code>lua_getupvalue</code></a>,
+<a href="#lua_setlocal"><code>lua_setlocal</code></a>, and <a href="#lua_setupvalue"><code>lua_setupvalue</code></a>.
+For these functions, the pointer is guaranteed to
+be valid while the caller function is active and
+the given closure (if one was given) is in the stack.
+
+
+<p>
+Except for these guarantees,
+the garbage collector is free to invalidate
+any pointer to internal strings.
+
+
+
+
+
+
+
+<h2>4.2 &ndash; <a name="4.2">C Closures</a></h2>
+
+<p>
+When a C&nbsp;function is created,
+it is possible to associate some values with it,
+thus creating a <em>C&nbsp;closure</em>
+(see <a href="#lua_pushcclosure"><code>lua_pushcclosure</code></a>);
+these values are called <em>upvalues</em> and are
+accessible to the function whenever it is called.
+
+
+<p>
+Whenever a C&nbsp;function is called,
+its upvalues are located at specific pseudo-indices.
+These pseudo-indices are produced by the macro
+<a href="#lua_upvalueindex"><code>lua_upvalueindex</code></a>.
+The first upvalue associated with a function is at index
+<code>lua_upvalueindex(1)</code>, and so on.
+Any access to <code>lua_upvalueindex(<em>n</em>)</code>,
+where <em>n</em> is greater than the number of upvalues of the
+current function
+(but not greater than 256,
+which is one plus the maximum number of upvalues in a closure),
+produces an acceptable but invalid index.
+
+
+<p>
+A C&nbsp;closure can also change the values
+of its corresponding upvalues.
+
+
+
+
+
+<h2>4.3 &ndash; <a name="4.3">Registry</a></h2>
+
+<p>
+Lua provides a <em>registry</em>,
+a predefined table that can be used by any C&nbsp;code to
+store whatever Lua values it needs to store.
+The registry table is always accessible at pseudo-index
+<a name="pdf-LUA_REGISTRYINDEX"><code>LUA_REGISTRYINDEX</code></a>.
+Any C&nbsp;library can store data into this table,
+but it must take care to choose keys
+that are different from those used
+by other libraries, to avoid collisions.
+Typically, you should use as key a string containing your library name,
+or a light userdata with the address of a C&nbsp;object in your code,
+or any Lua object created by your code.
+As with variable names,
+string keys starting with an underscore followed by
+uppercase letters are reserved for Lua.
+
+
+<p>
+The integer keys in the registry are used
+by the reference mechanism (see <a href="#luaL_ref"><code>luaL_ref</code></a>)
+and by some predefined values.
+Therefore, integer keys in the registry
+must not be used for other purposes.
+
+
+<p>
+When you create a new Lua state,
+its registry comes with some predefined values.
+These predefined values are indexed with integer keys
+defined as constants in <code>lua.h</code>.
+The following constants are defined:
+
+<ul>
+<li><b><a name="pdf-LUA_RIDX_MAINTHREAD"><code>LUA_RIDX_MAINTHREAD</code></a>: </b> At this index the registry has
+the main thread of the state.
+(The main thread is the one created together with the state.)
+</li>
+
+<li><b><a name="pdf-LUA_RIDX_GLOBALS"><code>LUA_RIDX_GLOBALS</code></a>: </b> At this index the registry has
+the global environment.
+</li>
+</ul>
+
+
+
+
+<h2>4.4 &ndash; <a name="4.4">Error Handling in C</a></h2>
+
+
+
+<p>
+Internally, Lua uses the C <code>longjmp</code> facility to handle errors.
+(Lua will use exceptions if you compile it as C++;
+search for <code>LUAI_THROW</code> in the source code for details.)
+When Lua faces any error,
+such as a memory allocation error or a type error,
+it <em>raises</em> an error;
+that is, it does a long jump.
+A <em>protected environment</em> uses <code>setjmp</code>
+to set a recovery point;
+any error jumps to the most recent active recovery point.
+
+
+<p>
+Inside a C&nbsp;function you can raise an error explicitly
+by calling <a href="#lua_error"><code>lua_error</code></a>.
+
+
+<p>
+Most functions in the API can raise an error,
+for instance due to a memory allocation error.
+The documentation for each function indicates whether
+it can raise errors.
+
+
+<p>
+If an error happens outside any protected environment,
+Lua calls a <em>panic function</em> (see <a href="#lua_atpanic"><code>lua_atpanic</code></a>)
+and then calls <code>abort</code>,
+thus exiting the host application.
+Your panic function can avoid this exit by
+never returning
+(e.g., doing a long jump to your own recovery point outside Lua).
+
+
+<p>
+The panic function,
+as its name implies,
+is a mechanism of last resort.
+Programs should avoid it.
+As a general rule,
+when a C&nbsp;function is called by Lua with a Lua state,
+it can do whatever it wants on that Lua state,
+as it should be already protected.
+However,
+when C code operates on other Lua states
+(e.g., a Lua-state argument to the function,
+a Lua state stored in the registry, or
+the result of <a href="#lua_newthread"><code>lua_newthread</code></a>),
+it should use them only in API calls that cannot raise errors.
+
+
+<p>
+The panic function runs as if it were a message handler (see <a href="#2.3">&sect;2.3</a>);
+in particular, the error object is on the top of the stack.
+However, there is no guarantee about stack space.
+To push anything on the stack,
+the panic function must first check the available space (see <a href="#4.1.1">&sect;4.1.1</a>).
+
+
+
+
+
+<h3>4.4.1 &ndash; <a name="4.4.1">Status Codes</a></h3>
+
+<p>
+Several functions that report errors in the API use the following
+status codes to indicate different kinds of errors or other conditions:
+
+<ul>
+
+<li><b><a name="pdf-LUA_OK"><code>LUA_OK</code></a> (0): </b> no errors.</li>
+
+<li><b><a name="pdf-LUA_ERRRUN"><code>LUA_ERRRUN</code></a>: </b> a runtime error.</li>
+
+<li><b><a name="pdf-LUA_ERRMEM"><code>LUA_ERRMEM</code></a>: </b>
+memory allocation error.
+For such errors, Lua does not call the message handler.
+</li>
+
+<li><b><a name="pdf-LUA_ERRERR"><code>LUA_ERRERR</code></a>: </b> error while running the message handler.</li>
+
+<li><b><a name="pdf-LUA_ERRSYNTAX"><code>LUA_ERRSYNTAX</code></a>: </b> syntax error during precompilation.</li>
+
+<li><b><a name="pdf-LUA_YIELD"><code>LUA_YIELD</code></a>: </b> the thread (coroutine) yields.</li>
+
+<li><b><a name="pdf-LUA_ERRFILE"><code>LUA_ERRFILE</code></a>: </b> a file-related error;
+e.g., it cannot open or read the file.</li>
+
+</ul><p>
+These constants are defined in the header file <code>lua.h</code>.
+
+
+
+
+
+
+
+<h2>4.5 &ndash; <a name="4.5">Handling Yields in C</a></h2>
+
+<p>
+Internally, Lua uses the C <code>longjmp</code> facility to yield a coroutine.
+Therefore, if a C&nbsp;function <code>foo</code> calls an API function
+and this API function yields
+(directly or indirectly by calling another function that yields),
+Lua cannot return to <code>foo</code> any more,
+because the <code>longjmp</code> removes its frame from the C&nbsp;stack.
+
+
+<p>
+To avoid this kind of problem,
+Lua raises an error whenever it tries to yield across an API call,
+except for three functions:
+<a href="#lua_yieldk"><code>lua_yieldk</code></a>, <a href="#lua_callk"><code>lua_callk</code></a>, and <a href="#lua_pcallk"><code>lua_pcallk</code></a>.
+All those functions receive a <em>continuation function</em>
+(as a parameter named <code>k</code>) to continue execution after a yield.
+
+
+<p>
+We need to set some terminology to explain continuations.
+We have a C&nbsp;function called from Lua which we will call
+the <em>original function</em>.
+This original function then calls one of those three functions in the C API,
+which we will call the <em>callee function</em>,
+that then yields the current thread.
+This can happen when the callee function is <a href="#lua_yieldk"><code>lua_yieldk</code></a>,
+or when the callee function is either <a href="#lua_callk"><code>lua_callk</code></a> or <a href="#lua_pcallk"><code>lua_pcallk</code></a>
+and the function called by them yields.
+
+
+<p>
+Suppose the running thread yields while executing the callee function.
+After the thread resumes,
+it eventually will finish running the callee function.
+However,
+the callee function cannot return to the original function,
+because its frame in the C&nbsp;stack was destroyed by the yield.
+Instead, Lua calls a <em>continuation function</em>,
+which was given as an argument to the callee function.
+As the name implies,
+the continuation function should continue the task
+of the original function.
+
+
+<p>
+As an illustration, consider the following function:
+
+<pre>
+ int original_function (lua_State *L) {
+ ... /* code 1 */
+ status = lua_pcall(L, n, m, h); /* calls Lua */
+ ... /* code 2 */
+ }
+</pre><p>
+Now we want to allow
+the Lua code being run by <a href="#lua_pcall"><code>lua_pcall</code></a> to yield.
+First, we can rewrite our function like here:
+
+<pre>
+ int k (lua_State *L, int status, lua_KContext ctx) {
+ ... /* code 2 */
+ }
+
+ int original_function (lua_State *L) {
+ ... /* code 1 */
+ return k(L, lua_pcall(L, n, m, h), ctx);
+ }
+</pre><p>
+In the above code,
+the new function <code>k</code> is a
+<em>continuation function</em> (with type <a href="#lua_KFunction"><code>lua_KFunction</code></a>),
+which should do all the work that the original function
+was doing after calling <a href="#lua_pcall"><code>lua_pcall</code></a>.
+Now, we must inform Lua that it must call <code>k</code> if the Lua code
+being executed by <a href="#lua_pcall"><code>lua_pcall</code></a> gets interrupted in some way
+(errors or yielding),
+so we rewrite the code as here,
+replacing <a href="#lua_pcall"><code>lua_pcall</code></a> by <a href="#lua_pcallk"><code>lua_pcallk</code></a>:
+
+<pre>
+ int original_function (lua_State *L) {
+ ... /* code 1 */
+ return k(L, lua_pcallk(L, n, m, h, ctx2, k), ctx1);
+ }
+</pre><p>
+Note the external, explicit call to the continuation:
+Lua will call the continuation only if needed, that is,
+in case of errors or resuming after a yield.
+If the called function returns normally without ever yielding,
+<a href="#lua_pcallk"><code>lua_pcallk</code></a> (and <a href="#lua_callk"><code>lua_callk</code></a>) will also return normally.
+(Of course, instead of calling the continuation in that case,
+you can do the equivalent work directly inside the original function.)
+
+
+<p>
+Besides the Lua state,
+the continuation function has two other parameters:
+the final status of the call and the context value (<code>ctx</code>) that
+was passed originally to <a href="#lua_pcallk"><code>lua_pcallk</code></a>.
+Lua does not use this context value;
+it only passes this value from the original function to the
+continuation function.
+For <a href="#lua_pcallk"><code>lua_pcallk</code></a>,
+the status is the same value that would be returned by <a href="#lua_pcallk"><code>lua_pcallk</code></a>,
+except that it is <a href="#pdf-LUA_YIELD"><code>LUA_YIELD</code></a> when being executed after a yield
+(instead of <a href="#pdf-LUA_OK"><code>LUA_OK</code></a>).
+For <a href="#lua_yieldk"><code>lua_yieldk</code></a> and <a href="#lua_callk"><code>lua_callk</code></a>,
+the status is always <a href="#pdf-LUA_YIELD"><code>LUA_YIELD</code></a> when Lua calls the continuation.
+(For these two functions,
+Lua will not call the continuation in case of errors,
+because they do not handle errors.)
+Similarly, when using <a href="#lua_callk"><code>lua_callk</code></a>,
+you should call the continuation function
+with <a href="#pdf-LUA_OK"><code>LUA_OK</code></a> as the status.
+(For <a href="#lua_yieldk"><code>lua_yieldk</code></a>, there is not much point in calling
+directly the continuation function,
+because <a href="#lua_yieldk"><code>lua_yieldk</code></a> usually does not return.)
+
+
+<p>
+Lua treats the continuation function as if it were the original function.
+The continuation function receives the same Lua stack
+from the original function,
+in the same state it would be if the callee function had returned.
+(For instance,
+after a <a href="#lua_callk"><code>lua_callk</code></a> the function and its arguments are
+removed from the stack and replaced by the results from the call.)
+It also has the same upvalues.
+Whatever it returns is handled by Lua as if it were the return
+of the original function.
+
+
+
+
+
+<h2>4.6 &ndash; <a name="4.6">Functions and Types</a></h2>
+
+<p>
+Here we list all functions and types from the C&nbsp;API in
+alphabetical order.
+Each function has an indicator like this:
+<span class="apii">[-o, +p, <em>x</em>]</span>
+
+
+<p>
+The first field, <code>o</code>,
+is how many elements the function pops from the stack.
+The second field, <code>p</code>,
+is how many elements the function pushes onto the stack.
+(Any function always pushes its results after popping its arguments.)
+A field in the form <code>x|y</code> means the function can push (or pop)
+<code>x</code> or <code>y</code> elements,
+depending on the situation;
+an interrogation mark '<code>?</code>' means that
+we cannot know how many elements the function pops/pushes
+by looking only at its arguments.
+(For instance, they may depend on what is in the stack.)
+The third field, <code>x</code>,
+tells whether the function may raise errors:
+'<code>-</code>' means the function never raises any error;
+'<code>m</code>' means the function may raise only out-of-memory errors;
+'<code>v</code>' means the function may raise the errors explained in the text;
+'<code>e</code>' means the function can run arbitrary Lua code,
+either directly or through metamethods,
+and therefore may raise any errors.
+
+
+
+<hr><h3><a name="lua_absindex"><code>lua_absindex</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_absindex (lua_State *L, int idx);</pre>
+
+<p>
+Converts the acceptable index <code>idx</code>
+into an equivalent absolute index
+(that is, one that does not depend on the stack size).
+
+
+
+
+
+<hr><h3><a name="lua_Alloc"><code>lua_Alloc</code></a></h3>
+<pre>typedef void * (*lua_Alloc) (void *ud,
+ void *ptr,
+ size_t osize,
+ size_t nsize);</pre>
+
+<p>
+The type of the memory-allocation function used by Lua states.
+The allocator function must provide a
+functionality similar to <code>realloc</code>,
+but not exactly the same.
+Its arguments are
+<code>ud</code>, an opaque pointer passed to <a href="#lua_newstate"><code>lua_newstate</code></a>;
+<code>ptr</code>, a pointer to the block being allocated/reallocated/freed;
+<code>osize</code>, the original size of the block or some code about what
+is being allocated;
+and <code>nsize</code>, the new size of the block.
+
+
+<p>
+When <code>ptr</code> is not <code>NULL</code>,
+<code>osize</code> is the size of the block pointed by <code>ptr</code>,
+that is, the size given when it was allocated or reallocated.
+
+
+<p>
+When <code>ptr</code> is <code>NULL</code>,
+<code>osize</code> encodes the kind of object that Lua is allocating.
+<code>osize</code> is any of
+<a href="#pdf-LUA_TSTRING"><code>LUA_TSTRING</code></a>, <a href="#pdf-LUA_TTABLE"><code>LUA_TTABLE</code></a>, <a href="#pdf-LUA_TFUNCTION"><code>LUA_TFUNCTION</code></a>,
+<a href="#pdf-LUA_TUSERDATA"><code>LUA_TUSERDATA</code></a>, or <a href="#pdf-LUA_TTHREAD"><code>LUA_TTHREAD</code></a> when (and only when)
+Lua is creating a new object of that type.
+When <code>osize</code> is some other value,
+Lua is allocating memory for something else.
+
+
+<p>
+Lua assumes the following behavior from the allocator function:
+
+
+<p>
+When <code>nsize</code> is zero,
+the allocator must behave like <code>free</code>
+and then return <code>NULL</code>.
+
+
+<p>
+When <code>nsize</code> is not zero,
+the allocator must behave like <code>realloc</code>.
+In particular, the allocator returns <code>NULL</code>
+if and only if it cannot fulfill the request.
+
+
+<p>
+Here is a simple implementation for the allocator function.
+It is used in the auxiliary library by <a href="#luaL_newstate"><code>luaL_newstate</code></a>.
+
+<pre>
+ static void *l_alloc (void *ud, void *ptr, size_t osize,
+ size_t nsize) {
+ (void)ud; (void)osize; /* not used */
+ if (nsize == 0) {
+ free(ptr);
+ return NULL;
+ }
+ else
+ return realloc(ptr, nsize);
+ }
+</pre><p>
+Note that ISO&nbsp;C ensures
+that <code>free(NULL)</code> has no effect and that
+<code>realloc(NULL,size)</code> is equivalent to <code>malloc(size)</code>.
+
+
+
+
+
+<hr><h3><a name="lua_arith"><code>lua_arith</code></a></h3><p>
+<span class="apii">[-(2|1), +1, <em>e</em>]</span>
+<pre>void lua_arith (lua_State *L, int op);</pre>
+
+<p>
+Performs an arithmetic or bitwise operation over the two values
+(or one, in the case of negations)
+at the top of the stack,
+with the value on the top being the second operand,
+pops these values, and pushes the result of the operation.
+The function follows the semantics of the corresponding Lua operator
+(that is, it may call metamethods).
+
+
+<p>
+The value of <code>op</code> must be one of the following constants:
+
+<ul>
+
+<li><b><a name="pdf-LUA_OPADD"><code>LUA_OPADD</code></a>: </b> performs addition (<code>+</code>)</li>
+<li><b><a name="pdf-LUA_OPSUB"><code>LUA_OPSUB</code></a>: </b> performs subtraction (<code>-</code>)</li>
+<li><b><a name="pdf-LUA_OPMUL"><code>LUA_OPMUL</code></a>: </b> performs multiplication (<code>*</code>)</li>
+<li><b><a name="pdf-LUA_OPDIV"><code>LUA_OPDIV</code></a>: </b> performs float division (<code>/</code>)</li>
+<li><b><a name="pdf-LUA_OPIDIV"><code>LUA_OPIDIV</code></a>: </b> performs floor division (<code>//</code>)</li>
+<li><b><a name="pdf-LUA_OPMOD"><code>LUA_OPMOD</code></a>: </b> performs modulo (<code>%</code>)</li>
+<li><b><a name="pdf-LUA_OPPOW"><code>LUA_OPPOW</code></a>: </b> performs exponentiation (<code>^</code>)</li>
+<li><b><a name="pdf-LUA_OPUNM"><code>LUA_OPUNM</code></a>: </b> performs mathematical negation (unary <code>-</code>)</li>
+<li><b><a name="pdf-LUA_OPBNOT"><code>LUA_OPBNOT</code></a>: </b> performs bitwise NOT (<code>~</code>)</li>
+<li><b><a name="pdf-LUA_OPBAND"><code>LUA_OPBAND</code></a>: </b> performs bitwise AND (<code>&amp;</code>)</li>
+<li><b><a name="pdf-LUA_OPBOR"><code>LUA_OPBOR</code></a>: </b> performs bitwise OR (<code>|</code>)</li>
+<li><b><a name="pdf-LUA_OPBXOR"><code>LUA_OPBXOR</code></a>: </b> performs bitwise exclusive OR (<code>~</code>)</li>
+<li><b><a name="pdf-LUA_OPSHL"><code>LUA_OPSHL</code></a>: </b> performs left shift (<code>&lt;&lt;</code>)</li>
+<li><b><a name="pdf-LUA_OPSHR"><code>LUA_OPSHR</code></a>: </b> performs right shift (<code>&gt;&gt;</code>)</li>
+
+</ul>
+
+
+
+
+<hr><h3><a name="lua_atpanic"><code>lua_atpanic</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);</pre>
+
+<p>
+Sets a new panic function and returns the old one (see <a href="#4.4">&sect;4.4</a>).
+
+
+
+
+
+<hr><h3><a name="lua_call"><code>lua_call</code></a></h3><p>
+<span class="apii">[-(nargs+1), +nresults, <em>e</em>]</span>
+<pre>void lua_call (lua_State *L, int nargs, int nresults);</pre>
+
+<p>
+Calls a function.
+Like regular Lua calls,
+<code>lua_call</code> respects the <code>__call</code> metamethod.
+So, here the word "function"
+means any callable value.
+
+
+<p>
+To do a call you must use the following protocol:
+first, the function to be called is pushed onto the stack;
+then, the arguments to the call are pushed
+in direct order;
+that is, the first argument is pushed first.
+Finally you call <a href="#lua_call"><code>lua_call</code></a>;
+<code>nargs</code> is the number of arguments that you pushed onto the stack.
+When the function returns,
+all arguments and the function value are popped
+and the call results are pushed onto the stack.
+The number of results is adjusted to <code>nresults</code>,
+unless <code>nresults</code> is <a name="pdf-LUA_MULTRET"><code>LUA_MULTRET</code></a>.
+In this case, all results from the function are pushed;
+Lua takes care that the returned values fit into the stack space,
+but it does not ensure any extra space in the stack.
+The function results are pushed onto the stack in direct order
+(the first result is pushed first),
+so that after the call the last result is on the top of the stack.
+
+
+<p>
+Any error while calling and running the function is propagated upwards
+(with a <code>longjmp</code>).
+
+
+<p>
+The following example shows how the host program can do the
+equivalent to this Lua code:
+
+<pre>
+ a = f("how", t.x, 14)
+</pre><p>
+Here it is in&nbsp;C:
+
+<pre>
+ lua_getglobal(L, "f"); /* function to be called */
+ lua_pushliteral(L, "how"); /* 1st argument */
+ lua_getglobal(L, "t"); /* table to be indexed */
+ lua_getfield(L, -1, "x"); /* push result of t.x (2nd arg) */
+ lua_remove(L, -2); /* remove 't' from the stack */
+ lua_pushinteger(L, 14); /* 3rd argument */
+ lua_call(L, 3, 1); /* call 'f' with 3 arguments and 1 result */
+ lua_setglobal(L, "a"); /* set global 'a' */
+</pre><p>
+Note that the code above is <em>balanced</em>:
+at its end, the stack is back to its original configuration.
+This is considered good programming practice.
+
+
+
+
+
+<hr><h3><a name="lua_callk"><code>lua_callk</code></a></h3><p>
+<span class="apii">[-(nargs + 1), +nresults, <em>e</em>]</span>
+<pre>void lua_callk (lua_State *L,
+ int nargs,
+ int nresults,
+ lua_KContext ctx,
+ lua_KFunction k);</pre>
+
+<p>
+This function behaves exactly like <a href="#lua_call"><code>lua_call</code></a>,
+but allows the called function to yield (see <a href="#4.5">&sect;4.5</a>).
+
+
+
+
+
+<hr><h3><a name="lua_CFunction"><code>lua_CFunction</code></a></h3>
+<pre>typedef int (*lua_CFunction) (lua_State *L);</pre>
+
+<p>
+Type for C&nbsp;functions.
+
+
+<p>
+In order to communicate properly with Lua,
+a C&nbsp;function must use the following protocol,
+which defines the way parameters and results are passed:
+a C&nbsp;function receives its arguments from Lua in its stack
+in direct order (the first argument is pushed first).
+So, when the function starts,
+<code>lua_gettop(L)</code> returns the number of arguments received by the function.
+The first argument (if any) is at index 1
+and its last argument is at index <code>lua_gettop(L)</code>.
+To return values to Lua, a C&nbsp;function just pushes them onto the stack,
+in direct order (the first result is pushed first),
+and returns in C the number of results.
+Any other value in the stack below the results will be properly
+discarded by Lua.
+Like a Lua function, a C&nbsp;function called by Lua can also return
+many results.
+
+
+<p>
+As an example, the following function receives a variable number
+of numeric arguments and returns their average and their sum:
+
+<pre>
+ static int foo (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ lua_Number sum = 0.0;
+ int i;
+ for (i = 1; i &lt;= n; i++) {
+ if (!lua_isnumber(L, i)) {
+ lua_pushliteral(L, "incorrect argument");
+ lua_error(L);
+ }
+ sum += lua_tonumber(L, i);
+ }
+ lua_pushnumber(L, sum/n); /* first result */
+ lua_pushnumber(L, sum); /* second result */
+ return 2; /* number of results */
+ }
+</pre>
+
+
+
+
+<hr><h3><a name="lua_checkstack"><code>lua_checkstack</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_checkstack (lua_State *L, int n);</pre>
+
+<p>
+Ensures that the stack has space for at least <code>n</code> extra elements,
+that is, that you can safely push up to <code>n</code> values into it.
+It returns false if it cannot fulfill the request,
+either because it would cause the stack
+to be greater than a fixed maximum size
+(typically at least several thousand elements) or
+because it cannot allocate memory for the extra space.
+This function never shrinks the stack;
+if the stack already has space for the extra elements,
+it is left unchanged.
+
+
+
+
+
+<hr><h3><a name="lua_close"><code>lua_close</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_close (lua_State *L);</pre>
+
+<p>
+Close all active to-be-closed variables in the main thread,
+release all objects in the given Lua state
+(calling the corresponding garbage-collection metamethods, if any),
+and frees all dynamic memory used by this state.
+
+
+<p>
+On several platforms, you may not need to call this function,
+because all resources are naturally released when the host program ends.
+On the other hand, long-running programs that create multiple states,
+such as daemons or web servers,
+will probably need to close states as soon as they are not needed.
+
+
+
+
+
+<hr><h3><a name="lua_closeslot"><code>lua_closeslot</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>void lua_closeslot (lua_State *L, int index);</pre>
+
+<p>
+Close the to-be-closed slot at the given index and set its value to <b>nil</b>.
+The index must be the last index previously marked to be closed
+(see <a href="#lua_toclose"><code>lua_toclose</code></a>) that is still active (that is, not closed yet).
+
+
+<p>
+A <code>__close</code> metamethod cannot yield
+when called through this function.
+
+
+<p>
+(This function was introduced in release&nbsp;5.4.3.)
+
+
+
+
+
+<hr><h3><a name="lua_closethread"><code>lua_closethread</code></a></h3><p>
+<span class="apii">[-0, +?, &ndash;]</span>
+<pre>int lua_closethread (lua_State *L, lua_State *from);</pre>
+
+<p>
+Resets a thread, cleaning its call stack and closing all pending
+to-be-closed variables.
+Returns a status code:
+<a href="#pdf-LUA_OK"><code>LUA_OK</code></a> for no errors in the thread
+(either the original error that stopped the thread or
+errors in closing methods),
+or an error status otherwise.
+In case of error,
+leaves the error object on the top of the stack.
+
+
+<p>
+The parameter <code>from</code> represents the coroutine that is resetting <code>L</code>.
+If there is no such coroutine,
+this parameter can be <code>NULL</code>.
+
+
+<p>
+(This function was introduced in release&nbsp;5.4.6.)
+
+
+
+
+
+<hr><h3><a name="lua_compare"><code>lua_compare</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>int lua_compare (lua_State *L, int index1, int index2, int op);</pre>
+
+<p>
+Compares two Lua values.
+Returns 1 if the value at index <code>index1</code> satisfies <code>op</code>
+when compared with the value at index <code>index2</code>,
+following the semantics of the corresponding Lua operator
+(that is, it may call metamethods).
+Otherwise returns&nbsp;0.
+Also returns&nbsp;0 if any of the indices is not valid.
+
+
+<p>
+The value of <code>op</code> must be one of the following constants:
+
+<ul>
+
+<li><b><a name="pdf-LUA_OPEQ"><code>LUA_OPEQ</code></a>: </b> compares for equality (<code>==</code>)</li>
+<li><b><a name="pdf-LUA_OPLT"><code>LUA_OPLT</code></a>: </b> compares for less than (<code>&lt;</code>)</li>
+<li><b><a name="pdf-LUA_OPLE"><code>LUA_OPLE</code></a>: </b> compares for less or equal (<code>&lt;=</code>)</li>
+
+</ul>
+
+
+
+
+<hr><h3><a name="lua_concat"><code>lua_concat</code></a></h3><p>
+<span class="apii">[-n, +1, <em>e</em>]</span>
+<pre>void lua_concat (lua_State *L, int n);</pre>
+
+<p>
+Concatenates the <code>n</code> values at the top of the stack,
+pops them, and leaves the result on the top.
+If <code>n</code>&nbsp;is&nbsp;1, the result is the single value on the stack
+(that is, the function does nothing);
+if <code>n</code> is 0, the result is the empty string.
+Concatenation is performed following the usual semantics of Lua
+(see <a href="#3.4.6">&sect;3.4.6</a>).
+
+
+
+
+
+<hr><h3><a name="lua_copy"><code>lua_copy</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_copy (lua_State *L, int fromidx, int toidx);</pre>
+
+<p>
+Copies the element at index <code>fromidx</code>
+into the valid index <code>toidx</code>,
+replacing the value at that position.
+Values at other positions are not affected.
+
+
+
+
+
+<hr><h3><a name="lua_createtable"><code>lua_createtable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void lua_createtable (lua_State *L, int narr, int nrec);</pre>
+
+<p>
+Creates a new empty table and pushes it onto the stack.
+Parameter <code>narr</code> is a hint for how many elements the table
+will have as a sequence;
+parameter <code>nrec</code> is a hint for how many other elements
+the table will have.
+Lua may use these hints to preallocate memory for the new table.
+This preallocation may help performance when you know in advance
+how many elements the table will have.
+Otherwise you can use the function <a href="#lua_newtable"><code>lua_newtable</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_dump"><code>lua_dump</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_dump (lua_State *L,
+ lua_Writer writer,
+ void *data,
+ int strip);</pre>
+
+<p>
+Dumps a function as a binary chunk.
+Receives a Lua function on the top of the stack
+and produces a binary chunk that,
+if loaded again,
+results in a function equivalent to the one dumped.
+As it produces parts of the chunk,
+<a href="#lua_dump"><code>lua_dump</code></a> calls function <code>writer</code> (see <a href="#lua_Writer"><code>lua_Writer</code></a>)
+with the given <code>data</code>
+to write them.
+
+
+<p>
+If <code>strip</code> is true,
+the binary representation may not include all debug information
+about the function,
+to save space.
+
+
+<p>
+The value returned is the error code returned by the last
+call to the writer;
+0&nbsp;means no errors.
+
+
+<p>
+This function does not pop the Lua function from the stack.
+
+
+
+
+
+<hr><h3><a name="lua_error"><code>lua_error</code></a></h3><p>
+<span class="apii">[-1, +0, <em>v</em>]</span>
+<pre>int lua_error (lua_State *L);</pre>
+
+<p>
+Raises a Lua error,
+using the value on the top of the stack as the error object.
+This function does a long jump,
+and therefore never returns
+(see <a href="#luaL_error"><code>luaL_error</code></a>).
+
+
+
+
+
+<hr><h3><a name="lua_gc"><code>lua_gc</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_gc (lua_State *L, int what, ...);</pre>
+
+<p>
+Controls the garbage collector.
+
+
+<p>
+This function performs several tasks,
+according to the value of the parameter <code>what</code>.
+For options that need extra arguments,
+they are listed after the option.
+
+<ul>
+
+<li><b><code>LUA_GCCOLLECT</code>: </b>
+Performs a full garbage-collection cycle.
+</li>
+
+<li><b><code>LUA_GCSTOP</code>: </b>
+Stops the garbage collector.
+</li>
+
+<li><b><code>LUA_GCRESTART</code>: </b>
+Restarts the garbage collector.
+</li>
+
+<li><b><code>LUA_GCCOUNT</code>: </b>
+Returns the current amount of memory (in Kbytes) in use by Lua.
+</li>
+
+<li><b><code>LUA_GCCOUNTB</code>: </b>
+Returns the remainder of dividing the current amount of bytes of
+memory in use by Lua by 1024.
+</li>
+
+<li><b><code>LUA_GCSTEP</code> <code>(int stepsize)</code>: </b>
+Performs an incremental step of garbage collection,
+corresponding to the allocation of <code>stepsize</code> Kbytes.
+</li>
+
+<li><b><code>LUA_GCISRUNNING</code>: </b>
+Returns a boolean that tells whether the collector is running
+(i.e., not stopped).
+</li>
+
+<li><b><code>LUA_GCINC</code> (int pause, int stepmul, stepsize): </b>
+Changes the collector to incremental mode
+with the given parameters (see <a href="#2.5.1">&sect;2.5.1</a>).
+Returns the previous mode (<code>LUA_GCGEN</code> or <code>LUA_GCINC</code>).
+</li>
+
+<li><b><code>LUA_GCGEN</code> (int minormul, int majormul): </b>
+Changes the collector to generational mode
+with the given parameters (see <a href="#2.5.2">&sect;2.5.2</a>).
+Returns the previous mode (<code>LUA_GCGEN</code> or <code>LUA_GCINC</code>).
+</li>
+
+</ul><p>
+For more details about these options,
+see <a href="#pdf-collectgarbage"><code>collectgarbage</code></a>.
+
+
+<p>
+This function should not be called by a finalizer.
+
+
+
+
+
+<hr><h3><a name="lua_getallocf"><code>lua_getallocf</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Alloc lua_getallocf (lua_State *L, void **ud);</pre>
+
+<p>
+Returns the memory-allocation function of a given state.
+If <code>ud</code> is not <code>NULL</code>, Lua stores in <code>*ud</code> the
+opaque pointer given when the memory-allocator function was set.
+
+
+
+
+
+<hr><h3><a name="lua_getfield"><code>lua_getfield</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>int lua_getfield (lua_State *L, int index, const char *k);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[k]</code>,
+where <code>t</code> is the value at the given index.
+As in Lua, this function may trigger a metamethod
+for the "index" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="lua_getextraspace"><code>lua_getextraspace</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void *lua_getextraspace (lua_State *L);</pre>
+
+<p>
+Returns a pointer to a raw memory area associated with the
+given Lua state.
+The application can use this area for any purpose;
+Lua does not use it for anything.
+
+
+<p>
+Each new thread has this area initialized with a copy
+of the area of the main thread.
+
+
+<p>
+By default, this area has the size of a pointer to void,
+but you can recompile Lua with a different size for this area.
+(See <code>LUA_EXTRASPACE</code> in <code>luaconf.h</code>.)
+
+
+
+
+
+<hr><h3><a name="lua_getglobal"><code>lua_getglobal</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>int lua_getglobal (lua_State *L, const char *name);</pre>
+
+<p>
+Pushes onto the stack the value of the global <code>name</code>.
+Returns the type of that value.
+
+
+
+
+
+<hr><h3><a name="lua_geti"><code>lua_geti</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>int lua_geti (lua_State *L, int index, lua_Integer i);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[i]</code>,
+where <code>t</code> is the value at the given index.
+As in Lua, this function may trigger a metamethod
+for the "index" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="lua_getmetatable"><code>lua_getmetatable</code></a></h3><p>
+<span class="apii">[-0, +(0|1), &ndash;]</span>
+<pre>int lua_getmetatable (lua_State *L, int index);</pre>
+
+<p>
+If the value at the given index has a metatable,
+the function pushes that metatable onto the stack and returns&nbsp;1.
+Otherwise,
+the function returns&nbsp;0 and pushes nothing on the stack.
+
+
+
+
+
+<hr><h3><a name="lua_gettable"><code>lua_gettable</code></a></h3><p>
+<span class="apii">[-1, +1, <em>e</em>]</span>
+<pre>int lua_gettable (lua_State *L, int index);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[k]</code>,
+where <code>t</code> is the value at the given index
+and <code>k</code> is the value on the top of the stack.
+
+
+<p>
+This function pops the key from the stack,
+pushing the resulting value in its place.
+As in Lua, this function may trigger a metamethod
+for the "index" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="lua_gettop"><code>lua_gettop</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_gettop (lua_State *L);</pre>
+
+<p>
+Returns the index of the top element in the stack.
+Because indices start at&nbsp;1,
+this result is equal to the number of elements in the stack;
+in particular, 0&nbsp;means an empty stack.
+
+
+
+
+
+<hr><h3><a name="lua_getiuservalue"><code>lua_getiuservalue</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int lua_getiuservalue (lua_State *L, int index, int n);</pre>
+
+<p>
+Pushes onto the stack the <code>n</code>-th user value associated with the
+full userdata at the given index and
+returns the type of the pushed value.
+
+
+<p>
+If the userdata does not have that value,
+pushes <b>nil</b> and returns <a href="#pdf-LUA_TNONE"><code>LUA_TNONE</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_insert"><code>lua_insert</code></a></h3><p>
+<span class="apii">[-1, +1, &ndash;]</span>
+<pre>void lua_insert (lua_State *L, int index);</pre>
+
+<p>
+Moves the top element into the given valid index,
+shifting up the elements above this index to open space.
+This function cannot be called with a pseudo-index,
+because a pseudo-index is not an actual stack position.
+
+
+
+
+
+<hr><h3><a name="lua_Integer"><code>lua_Integer</code></a></h3>
+<pre>typedef ... lua_Integer;</pre>
+
+<p>
+The type of integers in Lua.
+
+
+<p>
+By default this type is <code>long long</code>,
+(usually a 64-bit two-complement integer),
+but that can be changed to <code>long</code> or <code>int</code>
+(usually a 32-bit two-complement integer).
+(See <code>LUA_INT_TYPE</code> in <code>luaconf.h</code>.)
+
+
+<p>
+Lua also defines the constants
+<a name="pdf-LUA_MININTEGER"><code>LUA_MININTEGER</code></a> and <a name="pdf-LUA_MAXINTEGER"><code>LUA_MAXINTEGER</code></a>,
+with the minimum and the maximum values that fit in this type.
+
+
+
+
+
+<hr><h3><a name="lua_isboolean"><code>lua_isboolean</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isboolean (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a boolean,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_iscfunction"><code>lua_iscfunction</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_iscfunction (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a C&nbsp;function,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isfunction"><code>lua_isfunction</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isfunction (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a function
+(either C or Lua), and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isinteger"><code>lua_isinteger</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isinteger (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is an integer
+(that is, the value is a number and is represented as an integer),
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_islightuserdata"><code>lua_islightuserdata</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_islightuserdata (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a light userdata,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnil"><code>lua_isnil</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isnil (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is <b>nil</b>,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnone"><code>lua_isnone</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isnone (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the given index is not valid,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnoneornil"><code>lua_isnoneornil</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isnoneornil (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the given index is not valid
+or if the value at this index is <b>nil</b>,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnumber"><code>lua_isnumber</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isnumber (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a number
+or a string convertible to a number,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isstring"><code>lua_isstring</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isstring (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a string
+or a number (which is always convertible to a string),
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_istable"><code>lua_istable</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_istable (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a table,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isthread"><code>lua_isthread</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isthread (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a thread,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isuserdata"><code>lua_isuserdata</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isuserdata (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a userdata
+(either full or light), and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isyieldable"><code>lua_isyieldable</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isyieldable (lua_State *L);</pre>
+
+<p>
+Returns 1 if the given coroutine can yield,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_KContext"><code>lua_KContext</code></a></h3>
+<pre>typedef ... lua_KContext;</pre>
+
+<p>
+The type for continuation-function contexts.
+It must be a numeric type.
+This type is defined as <code>intptr_t</code>
+when <code>intptr_t</code> is available,
+so that it can store pointers too.
+Otherwise, it is defined as <code>ptrdiff_t</code>.
+
+
+
+
+
+<hr><h3><a name="lua_KFunction"><code>lua_KFunction</code></a></h3>
+<pre>typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);</pre>
+
+<p>
+Type for continuation functions (see <a href="#4.5">&sect;4.5</a>).
+
+
+
+
+
+<hr><h3><a name="lua_len"><code>lua_len</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>void lua_len (lua_State *L, int index);</pre>
+
+<p>
+Returns the length of the value at the given index.
+It is equivalent to the '<code>#</code>' operator in Lua (see <a href="#3.4.7">&sect;3.4.7</a>) and
+may trigger a metamethod for the "length" event (see <a href="#2.4">&sect;2.4</a>).
+The result is pushed on the stack.
+
+
+
+
+
+<hr><h3><a name="lua_load"><code>lua_load</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int lua_load (lua_State *L,
+ lua_Reader reader,
+ void *data,
+ const char *chunkname,
+ const char *mode);</pre>
+
+<p>
+Loads a Lua chunk without running it.
+If there are no errors,
+<code>lua_load</code> pushes the compiled chunk as a Lua
+function on top of the stack.
+Otherwise, it pushes an error message.
+
+
+<p>
+The <code>lua_load</code> function uses a user-supplied <code>reader</code> function
+to read the chunk (see <a href="#lua_Reader"><code>lua_Reader</code></a>).
+The <code>data</code> argument is an opaque value passed to the reader function.
+
+
+<p>
+The <code>chunkname</code> argument gives a name to the chunk,
+which is used for error messages and in debug information (see <a href="#4.7">&sect;4.7</a>).
+
+
+<p>
+<code>lua_load</code> automatically detects whether the chunk is text or binary
+and loads it accordingly (see program <code>luac</code>).
+The string <code>mode</code> works as in function <a href="#pdf-load"><code>load</code></a>,
+with the addition that
+a <code>NULL</code> value is equivalent to the string "<code>bt</code>".
+
+
+<p>
+<code>lua_load</code> uses the stack internally,
+so the reader function must always leave the stack
+unmodified when returning.
+
+
+<p>
+<code>lua_load</code> can return
+<a href="#pdf-LUA_OK"><code>LUA_OK</code></a>, <a href="#pdf-LUA_ERRSYNTAX"><code>LUA_ERRSYNTAX</code></a>, or <a href="#pdf-LUA_ERRMEM"><code>LUA_ERRMEM</code></a>.
+The function may also return other values corresponding to
+errors raised by the read function (see <a href="#4.4.1">&sect;4.4.1</a>).
+
+
+<p>
+If the resulting function has upvalues,
+its first upvalue is set to the value of the global environment
+stored at index <code>LUA_RIDX_GLOBALS</code> in the registry (see <a href="#4.3">&sect;4.3</a>).
+When loading main chunks,
+this upvalue will be the <code>_ENV</code> variable (see <a href="#2.2">&sect;2.2</a>).
+Other upvalues are initialized with <b>nil</b>.
+
+
+
+
+
+<hr><h3><a name="lua_newstate"><code>lua_newstate</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_State *lua_newstate (lua_Alloc f, void *ud);</pre>
+
+<p>
+Creates a new independent state and returns its main thread.
+Returns <code>NULL</code> if it cannot create the state
+(due to lack of memory).
+The argument <code>f</code> is the allocator function;
+Lua will do all memory allocation for this state
+through this function (see <a href="#lua_Alloc"><code>lua_Alloc</code></a>).
+The second argument, <code>ud</code>, is an opaque pointer that Lua
+passes to the allocator in every call.
+
+
+
+
+
+<hr><h3><a name="lua_newtable"><code>lua_newtable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void lua_newtable (lua_State *L);</pre>
+
+<p>
+Creates a new empty table and pushes it onto the stack.
+It is equivalent to <code>lua_createtable(L, 0, 0)</code>.
+
+
+
+
+
+<hr><h3><a name="lua_newthread"><code>lua_newthread</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>lua_State *lua_newthread (lua_State *L);</pre>
+
+<p>
+Creates a new thread, pushes it on the stack,
+and returns a pointer to a <a href="#lua_State"><code>lua_State</code></a> that represents this new thread.
+The new thread returned by this function shares with the original thread
+its global environment,
+but has an independent execution stack.
+
+
+<p>
+Threads are subject to garbage collection,
+like any Lua object.
+
+
+
+
+
+<hr><h3><a name="lua_newuserdatauv"><code>lua_newuserdatauv</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue);</pre>
+
+<p>
+This function creates and pushes on the stack a new full userdata,
+with <code>nuvalue</code> associated Lua values, called <code>user values</code>,
+plus an associated block of raw memory with <code>size</code> bytes.
+(The user values can be set and read with the functions
+<a href="#lua_setiuservalue"><code>lua_setiuservalue</code></a> and <a href="#lua_getiuservalue"><code>lua_getiuservalue</code></a>.)
+
+
+<p>
+The function returns the address of the block of memory.
+Lua ensures that this address is valid as long as
+the corresponding userdata is alive (see <a href="#2.5">&sect;2.5</a>).
+Moreover, if the userdata is marked for finalization (see <a href="#2.5.3">&sect;2.5.3</a>),
+its address is valid at least until the call to its finalizer.
+
+
+
+
+
+<hr><h3><a name="lua_next"><code>lua_next</code></a></h3><p>
+<span class="apii">[-1, +(2|0), <em>v</em>]</span>
+<pre>int lua_next (lua_State *L, int index);</pre>
+
+<p>
+Pops a key from the stack,
+and pushes a key&ndash;value pair from the table at the given index,
+the "next" pair after the given key.
+If there are no more elements in the table,
+then <a href="#lua_next"><code>lua_next</code></a> returns&nbsp;0 and pushes nothing.
+
+
+<p>
+A typical table traversal looks like this:
+
+<pre>
+ /* table is in the stack at index 't' */
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, t) != 0) {
+ /* uses 'key' (at index -2) and 'value' (at index -1) */
+ printf("%s - %s\n",
+ lua_typename(L, lua_type(L, -2)),
+ lua_typename(L, lua_type(L, -1)));
+ /* removes 'value'; keeps 'key' for next iteration */
+ lua_pop(L, 1);
+ }
+</pre>
+
+<p>
+While traversing a table,
+avoid calling <a href="#lua_tolstring"><code>lua_tolstring</code></a> directly on a key,
+unless you know that the key is actually a string.
+Recall that <a href="#lua_tolstring"><code>lua_tolstring</code></a> may change
+the value at the given index;
+this confuses the next call to <a href="#lua_next"><code>lua_next</code></a>.
+
+
+<p>
+This function may raise an error if the given key
+is neither <b>nil</b> nor present in the table.
+See function <a href="#pdf-next"><code>next</code></a> for the caveats of modifying
+the table during its traversal.
+
+
+
+
+
+<hr><h3><a name="lua_Number"><code>lua_Number</code></a></h3>
+<pre>typedef ... lua_Number;</pre>
+
+<p>
+The type of floats in Lua.
+
+
+<p>
+By default this type is double,
+but that can be changed to a single float or a long double.
+(See <code>LUA_FLOAT_TYPE</code> in <code>luaconf.h</code>.)
+
+
+
+
+
+<hr><h3><a name="lua_numbertointeger"><code>lua_numbertointeger</code></a></h3>
+<pre>int lua_numbertointeger (lua_Number n, lua_Integer *p);</pre>
+
+<p>
+Tries to convert a Lua float to a Lua integer;
+the float <code>n</code> must have an integral value.
+If that value is within the range of Lua integers,
+it is converted to an integer and assigned to <code>*p</code>.
+The macro results in a boolean indicating whether the
+conversion was successful.
+(Note that this range test can be tricky to do
+correctly without this macro, due to rounding.)
+
+
+<p>
+This macro may evaluate its arguments more than once.
+
+
+
+
+
+<hr><h3><a name="lua_pcall"><code>lua_pcall</code></a></h3><p>
+<span class="apii">[-(nargs + 1), +(nresults|1), &ndash;]</span>
+<pre>int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);</pre>
+
+<p>
+Calls a function (or a callable object) in protected mode.
+
+
+<p>
+Both <code>nargs</code> and <code>nresults</code> have the same meaning as
+in <a href="#lua_call"><code>lua_call</code></a>.
+If there are no errors during the call,
+<a href="#lua_pcall"><code>lua_pcall</code></a> behaves exactly like <a href="#lua_call"><code>lua_call</code></a>.
+However, if there is any error,
+<a href="#lua_pcall"><code>lua_pcall</code></a> catches it,
+pushes a single value on the stack (the error object),
+and returns an error code.
+Like <a href="#lua_call"><code>lua_call</code></a>,
+<a href="#lua_pcall"><code>lua_pcall</code></a> always removes the function
+and its arguments from the stack.
+
+
+<p>
+If <code>msgh</code> is 0,
+then the error object returned on the stack
+is exactly the original error object.
+Otherwise, <code>msgh</code> is the stack index of a
+<em>message handler</em>.
+(This index cannot be a pseudo-index.)
+In case of runtime errors,
+this handler will be called with the error object
+and its return value will be the object
+returned on the stack by <a href="#lua_pcall"><code>lua_pcall</code></a>.
+
+
+<p>
+Typically, the message handler is used to add more debug
+information to the error object, such as a stack traceback.
+Such information cannot be gathered after the return of <a href="#lua_pcall"><code>lua_pcall</code></a>,
+since by then the stack has unwound.
+
+
+<p>
+The <a href="#lua_pcall"><code>lua_pcall</code></a> function returns one of the following status codes:
+<a href="#pdf-LUA_OK"><code>LUA_OK</code></a>, <a href="#pdf-LUA_ERRRUN"><code>LUA_ERRRUN</code></a>, <a href="#pdf-LUA_ERRMEM"><code>LUA_ERRMEM</code></a>, or <a href="#pdf-LUA_ERRERR"><code>LUA_ERRERR</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_pcallk"><code>lua_pcallk</code></a></h3><p>
+<span class="apii">[-(nargs + 1), +(nresults|1), &ndash;]</span>
+<pre>int lua_pcallk (lua_State *L,
+ int nargs,
+ int nresults,
+ int msgh,
+ lua_KContext ctx,
+ lua_KFunction k);</pre>
+
+<p>
+This function behaves exactly like <a href="#lua_pcall"><code>lua_pcall</code></a>,
+except that it allows the called function to yield (see <a href="#4.5">&sect;4.5</a>).
+
+
+
+
+
+<hr><h3><a name="lua_pop"><code>lua_pop</code></a></h3><p>
+<span class="apii">[-n, +0, <em>e</em>]</span>
+<pre>void lua_pop (lua_State *L, int n);</pre>
+
+<p>
+Pops <code>n</code> elements from the stack.
+It is implemented as a macro over <a href="#lua_settop"><code>lua_settop</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_pushboolean"><code>lua_pushboolean</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushboolean (lua_State *L, int b);</pre>
+
+<p>
+Pushes a boolean value with value <code>b</code> onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushcclosure"><code>lua_pushcclosure</code></a></h3><p>
+<span class="apii">[-n, +1, <em>m</em>]</span>
+<pre>void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);</pre>
+
+<p>
+Pushes a new C&nbsp;closure onto the stack.
+This function receives a pointer to a C&nbsp;function
+and pushes onto the stack a Lua value of type <code>function</code> that,
+when called, invokes the corresponding C&nbsp;function.
+The parameter <code>n</code> tells how many upvalues this function will have
+(see <a href="#4.2">&sect;4.2</a>).
+
+
+<p>
+Any function to be callable by Lua must
+follow the correct protocol to receive its parameters
+and return its results (see <a href="#lua_CFunction"><code>lua_CFunction</code></a>).
+
+
+<p>
+When a C&nbsp;function is created,
+it is possible to associate some values with it,
+the so called upvalues;
+these upvalues are then accessible to the function whenever it is called.
+This association is called a C&nbsp;closure (see <a href="#4.2">&sect;4.2</a>).
+To create a C&nbsp;closure,
+first the initial values for its upvalues must be pushed onto the stack.
+(When there are multiple upvalues, the first value is pushed first.)
+Then <a href="#lua_pushcclosure"><code>lua_pushcclosure</code></a>
+is called to create and push the C&nbsp;function onto the stack,
+with the argument <code>n</code> telling how many values will be
+associated with the function.
+<a href="#lua_pushcclosure"><code>lua_pushcclosure</code></a> also pops these values from the stack.
+
+
+<p>
+The maximum value for <code>n</code> is 255.
+
+
+<p>
+When <code>n</code> is zero,
+this function creates a <em>light C&nbsp;function</em>,
+which is just a pointer to the C&nbsp;function.
+In that case, it never raises a memory error.
+
+
+
+
+
+<hr><h3><a name="lua_pushcfunction"><code>lua_pushcfunction</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushcfunction (lua_State *L, lua_CFunction f);</pre>
+
+<p>
+Pushes a C&nbsp;function onto the stack.
+This function is equivalent to <a href="#lua_pushcclosure"><code>lua_pushcclosure</code></a> with no upvalues.
+
+
+
+
+
+<hr><h3><a name="lua_pushfstring"><code>lua_pushfstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>v</em>]</span>
+<pre>const char *lua_pushfstring (lua_State *L, const char *fmt, ...);</pre>
+
+<p>
+Pushes onto the stack a formatted string
+and returns a pointer to this string (see <a href="#4.1.3">&sect;4.1.3</a>).
+It is similar to the ISO&nbsp;C function <code>sprintf</code>,
+but has two important differences.
+First,
+you do not have to allocate space for the result;
+the result is a Lua string and Lua takes care of memory allocation
+(and deallocation, through garbage collection).
+Second,
+the conversion specifiers are quite restricted.
+There are no flags, widths, or precisions.
+The conversion specifiers can only be
+'<code>%%</code>' (inserts the character '<code>%</code>'),
+'<code>%s</code>' (inserts a zero-terminated string, with no size restrictions),
+'<code>%f</code>' (inserts a <a href="#lua_Number"><code>lua_Number</code></a>),
+'<code>%I</code>' (inserts a <a href="#lua_Integer"><code>lua_Integer</code></a>),
+'<code>%p</code>' (inserts a pointer),
+'<code>%d</code>' (inserts an <code>int</code>),
+'<code>%c</code>' (inserts an <code>int</code> as a one-byte character), and
+'<code>%U</code>' (inserts a <code>long int</code> as a UTF-8 byte sequence).
+
+
+<p>
+This function may raise errors due to memory overflow
+or an invalid conversion specifier.
+
+
+
+
+
+<hr><h3><a name="lua_pushglobaltable"><code>lua_pushglobaltable</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushglobaltable (lua_State *L);</pre>
+
+<p>
+Pushes the global environment onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushinteger"><code>lua_pushinteger</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushinteger (lua_State *L, lua_Integer n);</pre>
+
+<p>
+Pushes an integer with value <code>n</code> onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushlightuserdata"><code>lua_pushlightuserdata</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushlightuserdata (lua_State *L, void *p);</pre>
+
+<p>
+Pushes a light userdata onto the stack.
+
+
+<p>
+Userdata represent C&nbsp;values in Lua.
+A <em>light userdata</em> represents a pointer, a <code>void*</code>.
+It is a value (like a number):
+you do not create it, it has no individual metatable,
+and it is not collected (as it was never created).
+A light userdata is equal to "any"
+light userdata with the same C&nbsp;address.
+
+
+
+
+
+<hr><h3><a name="lua_pushliteral"><code>lua_pushliteral</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>const char *lua_pushliteral (lua_State *L, const char *s);</pre>
+
+<p>
+This macro is equivalent to <a href="#lua_pushstring"><code>lua_pushstring</code></a>,
+but should be used only when <code>s</code> is a literal string.
+(Lua may optimize this case.)
+
+
+
+
+
+<hr><h3><a name="lua_pushlstring"><code>lua_pushlstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>const char *lua_pushlstring (lua_State *L, const char *s, size_t len);</pre>
+
+<p>
+Pushes the string pointed to by <code>s</code> with size <code>len</code>
+onto the stack.
+Lua will make or reuse an internal copy of the given string,
+so the memory at <code>s</code> can be freed or reused immediately after
+the function returns.
+The string can contain any binary data,
+including embedded zeros.
+
+
+<p>
+Returns a pointer to the internal copy of the string (see <a href="#4.1.3">&sect;4.1.3</a>).
+
+
+
+
+
+<hr><h3><a name="lua_pushnil"><code>lua_pushnil</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushnil (lua_State *L);</pre>
+
+<p>
+Pushes a nil value onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushnumber"><code>lua_pushnumber</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushnumber (lua_State *L, lua_Number n);</pre>
+
+<p>
+Pushes a float with value <code>n</code> onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushstring"><code>lua_pushstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>const char *lua_pushstring (lua_State *L, const char *s);</pre>
+
+<p>
+Pushes the zero-terminated string pointed to by <code>s</code>
+onto the stack.
+Lua will make or reuse an internal copy of the given string,
+so the memory at <code>s</code> can be freed or reused immediately after
+the function returns.
+
+
+<p>
+Returns a pointer to the internal copy of the string (see <a href="#4.1.3">&sect;4.1.3</a>).
+
+
+<p>
+If <code>s</code> is <code>NULL</code>, pushes <b>nil</b> and returns <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_pushthread"><code>lua_pushthread</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int lua_pushthread (lua_State *L);</pre>
+
+<p>
+Pushes the thread represented by <code>L</code> onto the stack.
+Returns 1 if this thread is the main thread of its state.
+
+
+
+
+
+<hr><h3><a name="lua_pushvalue"><code>lua_pushvalue</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushvalue (lua_State *L, int index);</pre>
+
+<p>
+Pushes a copy of the element at the given index
+onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushvfstring"><code>lua_pushvfstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>v</em>]</span>
+<pre>const char *lua_pushvfstring (lua_State *L,
+ const char *fmt,
+ va_list argp);</pre>
+
+<p>
+Equivalent to <a href="#lua_pushfstring"><code>lua_pushfstring</code></a>, except that it receives a <code>va_list</code>
+instead of a variable number of arguments.
+
+
+
+
+
+<hr><h3><a name="lua_rawequal"><code>lua_rawequal</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_rawequal (lua_State *L, int index1, int index2);</pre>
+
+<p>
+Returns 1 if the two values in indices <code>index1</code> and
+<code>index2</code> are primitively equal
+(that is, equal without calling the <code>__eq</code> metamethod).
+Otherwise returns&nbsp;0.
+Also returns&nbsp;0 if any of the indices are not valid.
+
+
+
+
+
+<hr><h3><a name="lua_rawget"><code>lua_rawget</code></a></h3><p>
+<span class="apii">[-1, +1, &ndash;]</span>
+<pre>int lua_rawget (lua_State *L, int index);</pre>
+
+<p>
+Similar to <a href="#lua_gettable"><code>lua_gettable</code></a>, but does a raw access
+(i.e., without metamethods).
+The value at <code>index</code> must be a table.
+
+
+
+
+
+<hr><h3><a name="lua_rawgeti"><code>lua_rawgeti</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int lua_rawgeti (lua_State *L, int index, lua_Integer n);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[n]</code>,
+where <code>t</code> is the table at the given index.
+The access is raw,
+that is, it does not use the <code>__index</code> metavalue.
+
+
+<p>
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="lua_rawgetp"><code>lua_rawgetp</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int lua_rawgetp (lua_State *L, int index, const void *p);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[k]</code>,
+where <code>t</code> is the table at the given index and
+<code>k</code> is the pointer <code>p</code> represented as a light userdata.
+The access is raw;
+that is, it does not use the <code>__index</code> metavalue.
+
+
+<p>
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="lua_rawlen"><code>lua_rawlen</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Unsigned lua_rawlen (lua_State *L, int index);</pre>
+
+<p>
+Returns the raw "length" of the value at the given index:
+for strings, this is the string length;
+for tables, this is the result of the length operator ('<code>#</code>')
+with no metamethods;
+for userdata, this is the size of the block of memory allocated
+for the userdata.
+For other values, this call returns&nbsp;0.
+
+
+
+
+
+<hr><h3><a name="lua_rawset"><code>lua_rawset</code></a></h3><p>
+<span class="apii">[-2, +0, <em>m</em>]</span>
+<pre>void lua_rawset (lua_State *L, int index);</pre>
+
+<p>
+Similar to <a href="#lua_settable"><code>lua_settable</code></a>, but does a raw assignment
+(i.e., without metamethods).
+The value at <code>index</code> must be a table.
+
+
+
+
+
+<hr><h3><a name="lua_rawseti"><code>lua_rawseti</code></a></h3><p>
+<span class="apii">[-1, +0, <em>m</em>]</span>
+<pre>void lua_rawseti (lua_State *L, int index, lua_Integer i);</pre>
+
+<p>
+Does the equivalent of <code>t[i] = v</code>,
+where <code>t</code> is the table at the given index
+and <code>v</code> is the value on the top of the stack.
+
+
+<p>
+This function pops the value from the stack.
+The assignment is raw,
+that is, it does not use the <code>__newindex</code> metavalue.
+
+
+
+
+
+<hr><h3><a name="lua_rawsetp"><code>lua_rawsetp</code></a></h3><p>
+<span class="apii">[-1, +0, <em>m</em>]</span>
+<pre>void lua_rawsetp (lua_State *L, int index, const void *p);</pre>
+
+<p>
+Does the equivalent of <code>t[p] = v</code>,
+where <code>t</code> is the table at the given index,
+<code>p</code> is encoded as a light userdata,
+and <code>v</code> is the value on the top of the stack.
+
+
+<p>
+This function pops the value from the stack.
+The assignment is raw,
+that is, it does not use the <code>__newindex</code> metavalue.
+
+
+
+
+
+<hr><h3><a name="lua_Reader"><code>lua_Reader</code></a></h3>
+<pre>typedef const char * (*lua_Reader) (lua_State *L,
+ void *data,
+ size_t *size);</pre>
+
+<p>
+The reader function used by <a href="#lua_load"><code>lua_load</code></a>.
+Every time <a href="#lua_load"><code>lua_load</code></a> needs another piece of the chunk,
+it calls the reader,
+passing along its <code>data</code> parameter.
+The reader must return a pointer to a block of memory
+with a new piece of the chunk
+and set <code>size</code> to the block size.
+The block must exist until the reader function is called again.
+To signal the end of the chunk,
+the reader must return <code>NULL</code> or set <code>size</code> to zero.
+The reader function may return pieces of any size greater than zero.
+
+
+
+
+
+<hr><h3><a name="lua_register"><code>lua_register</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>void lua_register (lua_State *L, const char *name, lua_CFunction f);</pre>
+
+<p>
+Sets the C&nbsp;function <code>f</code> as the new value of global <code>name</code>.
+It is defined as a macro:
+
+<pre>
+ #define lua_register(L,n,f) \
+ (lua_pushcfunction(L, f), lua_setglobal(L, n))
+</pre>
+
+
+
+
+<hr><h3><a name="lua_remove"><code>lua_remove</code></a></h3><p>
+<span class="apii">[-1, +0, &ndash;]</span>
+<pre>void lua_remove (lua_State *L, int index);</pre>
+
+<p>
+Removes the element at the given valid index,
+shifting down the elements above this index to fill the gap.
+This function cannot be called with a pseudo-index,
+because a pseudo-index is not an actual stack position.
+
+
+
+
+
+<hr><h3><a name="lua_replace"><code>lua_replace</code></a></h3><p>
+<span class="apii">[-1, +0, &ndash;]</span>
+<pre>void lua_replace (lua_State *L, int index);</pre>
+
+<p>
+Moves the top element into the given valid index
+without shifting any element
+(therefore replacing the value at that given index),
+and then pops the top element.
+
+
+
+
+
+<hr><h3><a name="lua_resetthread"><code>lua_resetthread</code></a></h3><p>
+<span class="apii">[-0, +?, &ndash;]</span>
+<pre>int lua_resetthread (lua_State *L);</pre>
+
+<p>
+This function is deprecated;
+it is equivalent to <a href="#lua_closethread"><code>lua_closethread</code></a> with
+<code>from</code> being <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_resume"><code>lua_resume</code></a></h3><p>
+<span class="apii">[-?, +?, &ndash;]</span>
+<pre>int lua_resume (lua_State *L, lua_State *from, int nargs,
+ int *nresults);</pre>
+
+<p>
+Starts and resumes a coroutine in the given thread <code>L</code>.
+
+
+<p>
+To start a coroutine,
+you push the main function plus any arguments
+onto the empty stack of the thread.
+then you call <a href="#lua_resume"><code>lua_resume</code></a>,
+with <code>nargs</code> being the number of arguments.
+This call returns when the coroutine suspends or finishes its execution.
+When it returns,
+<code>*nresults</code> is updated and
+the top of the stack contains
+the <code>*nresults</code> values passed to <a href="#lua_yield"><code>lua_yield</code></a>
+or returned by the body function.
+<a href="#lua_resume"><code>lua_resume</code></a> returns
+<a href="#pdf-LUA_YIELD"><code>LUA_YIELD</code></a> if the coroutine yields,
+<a href="#pdf-LUA_OK"><code>LUA_OK</code></a> if the coroutine finishes its execution
+without errors,
+or an error code in case of errors (see <a href="#4.4.1">&sect;4.4.1</a>).
+In case of errors,
+the error object is on the top of the stack.
+
+
+<p>
+To resume a coroutine,
+you remove the <code>*nresults</code> yielded values from its stack,
+push the values to be passed as results from <code>yield</code>,
+and then call <a href="#lua_resume"><code>lua_resume</code></a>.
+
+
+<p>
+The parameter <code>from</code> represents the coroutine that is resuming <code>L</code>.
+If there is no such coroutine,
+this parameter can be <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_rotate"><code>lua_rotate</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_rotate (lua_State *L, int idx, int n);</pre>
+
+<p>
+Rotates the stack elements between the valid index <code>idx</code>
+and the top of the stack.
+The elements are rotated <code>n</code> positions in the direction of the top,
+for a positive <code>n</code>,
+or <code>-n</code> positions in the direction of the bottom,
+for a negative <code>n</code>.
+The absolute value of <code>n</code> must not be greater than the size
+of the slice being rotated.
+This function cannot be called with a pseudo-index,
+because a pseudo-index is not an actual stack position.
+
+
+
+
+
+<hr><h3><a name="lua_setallocf"><code>lua_setallocf</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);</pre>
+
+<p>
+Changes the allocator function of a given state to <code>f</code>
+with user data <code>ud</code>.
+
+
+
+
+
+<hr><h3><a name="lua_setfield"><code>lua_setfield</code></a></h3><p>
+<span class="apii">[-1, +0, <em>e</em>]</span>
+<pre>void lua_setfield (lua_State *L, int index, const char *k);</pre>
+
+<p>
+Does the equivalent to <code>t[k] = v</code>,
+where <code>t</code> is the value at the given index
+and <code>v</code> is the value on the top of the stack.
+
+
+<p>
+This function pops the value from the stack.
+As in Lua, this function may trigger a metamethod
+for the "newindex" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<hr><h3><a name="lua_setglobal"><code>lua_setglobal</code></a></h3><p>
+<span class="apii">[-1, +0, <em>e</em>]</span>
+<pre>void lua_setglobal (lua_State *L, const char *name);</pre>
+
+<p>
+Pops a value from the stack and
+sets it as the new value of global <code>name</code>.
+
+
+
+
+
+<hr><h3><a name="lua_seti"><code>lua_seti</code></a></h3><p>
+<span class="apii">[-1, +0, <em>e</em>]</span>
+<pre>void lua_seti (lua_State *L, int index, lua_Integer n);</pre>
+
+<p>
+Does the equivalent to <code>t[n] = v</code>,
+where <code>t</code> is the value at the given index
+and <code>v</code> is the value on the top of the stack.
+
+
+<p>
+This function pops the value from the stack.
+As in Lua, this function may trigger a metamethod
+for the "newindex" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<hr><h3><a name="lua_setiuservalue"><code>lua_setiuservalue</code></a></h3><p>
+<span class="apii">[-1, +0, &ndash;]</span>
+<pre>int lua_setiuservalue (lua_State *L, int index, int n);</pre>
+
+<p>
+Pops a value from the stack and sets it as
+the new <code>n</code>-th user value associated to the
+full userdata at the given index.
+Returns 0 if the userdata does not have that value.
+
+
+
+
+
+<hr><h3><a name="lua_setmetatable"><code>lua_setmetatable</code></a></h3><p>
+<span class="apii">[-1, +0, &ndash;]</span>
+<pre>int lua_setmetatable (lua_State *L, int index);</pre>
+
+<p>
+Pops a table or <b>nil</b> from the stack and
+sets that value as the new metatable for the value at the given index.
+(<b>nil</b> means no metatable.)
+
+
+<p>
+(For historical reasons, this function returns an <code>int</code>,
+which now is always 1.)
+
+
+
+
+
+<hr><h3><a name="lua_settable"><code>lua_settable</code></a></h3><p>
+<span class="apii">[-2, +0, <em>e</em>]</span>
+<pre>void lua_settable (lua_State *L, int index);</pre>
+
+<p>
+Does the equivalent to <code>t[k] = v</code>,
+where <code>t</code> is the value at the given index,
+<code>v</code> is the value on the top of the stack,
+and <code>k</code> is the value just below the top.
+
+
+<p>
+This function pops both the key and the value from the stack.
+As in Lua, this function may trigger a metamethod
+for the "newindex" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<hr><h3><a name="lua_settop"><code>lua_settop</code></a></h3><p>
+<span class="apii">[-?, +?, <em>e</em>]</span>
+<pre>void lua_settop (lua_State *L, int index);</pre>
+
+<p>
+Accepts any index, or&nbsp;0,
+and sets the stack top to this index.
+If the new top is greater than the old one,
+then the new elements are filled with <b>nil</b>.
+If <code>index</code> is&nbsp;0, then all stack elements are removed.
+
+
+<p>
+This function can run arbitrary code when removing an index
+marked as to-be-closed from the stack.
+
+
+
+
+
+<hr><h3><a name="lua_setwarnf"><code>lua_setwarnf</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud);</pre>
+
+<p>
+Sets the warning function to be used by Lua to emit warnings
+(see <a href="#lua_WarnFunction"><code>lua_WarnFunction</code></a>).
+The <code>ud</code> parameter sets the value <code>ud</code> passed to
+the warning function.
+
+
+
+
+
+<hr><h3><a name="lua_State"><code>lua_State</code></a></h3>
+<pre>typedef struct lua_State lua_State;</pre>
+
+<p>
+An opaque structure that points to a thread and indirectly
+(through the thread) to the whole state of a Lua interpreter.
+The Lua library is fully reentrant:
+it has no global variables.
+All information about a state is accessible through this structure.
+
+
+<p>
+A pointer to this structure must be passed as the first argument to
+every function in the library, except to <a href="#lua_newstate"><code>lua_newstate</code></a>,
+which creates a Lua state from scratch.
+
+
+
+
+
+<hr><h3><a name="lua_status"><code>lua_status</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_status (lua_State *L);</pre>
+
+<p>
+Returns the status of the thread <code>L</code>.
+
+
+<p>
+The status can be <a href="#pdf-LUA_OK"><code>LUA_OK</code></a> for a normal thread,
+an error code if the thread finished the execution
+of a <a href="#lua_resume"><code>lua_resume</code></a> with an error,
+or <a href="#pdf-LUA_YIELD"><code>LUA_YIELD</code></a> if the thread is suspended.
+
+
+<p>
+You can call functions only in threads with status <a href="#pdf-LUA_OK"><code>LUA_OK</code></a>.
+You can resume threads with status <a href="#pdf-LUA_OK"><code>LUA_OK</code></a>
+(to start a new coroutine) or <a href="#pdf-LUA_YIELD"><code>LUA_YIELD</code></a>
+(to resume a coroutine).
+
+
+
+
+
+<hr><h3><a name="lua_stringtonumber"><code>lua_stringtonumber</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>size_t lua_stringtonumber (lua_State *L, const char *s);</pre>
+
+<p>
+Converts the zero-terminated string <code>s</code> to a number,
+pushes that number into the stack,
+and returns the total size of the string,
+that is, its length plus one.
+The conversion can result in an integer or a float,
+according to the lexical conventions of Lua (see <a href="#3.1">&sect;3.1</a>).
+The string may have leading and trailing whitespaces and a sign.
+If the string is not a valid numeral,
+returns 0 and pushes nothing.
+(Note that the result can be used as a boolean,
+true if the conversion succeeds.)
+
+
+
+
+
+<hr><h3><a name="lua_toboolean"><code>lua_toboolean</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_toboolean (lua_State *L, int index);</pre>
+
+<p>
+Converts the Lua value at the given index to a C&nbsp;boolean
+value (0&nbsp;or&nbsp;1).
+Like all tests in Lua,
+<a href="#lua_toboolean"><code>lua_toboolean</code></a> returns true for any Lua value
+different from <b>false</b> and <b>nil</b>;
+otherwise it returns false.
+(If you want to accept only actual boolean values,
+use <a href="#lua_isboolean"><code>lua_isboolean</code></a> to test the value's type.)
+
+
+
+
+
+<hr><h3><a name="lua_tocfunction"><code>lua_tocfunction</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_CFunction lua_tocfunction (lua_State *L, int index);</pre>
+
+<p>
+Converts a value at the given index to a C&nbsp;function.
+That value must be a C&nbsp;function;
+otherwise, returns <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_toclose"><code>lua_toclose</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>void lua_toclose (lua_State *L, int index);</pre>
+
+<p>
+Marks the given index in the stack as a
+to-be-closed slot (see <a href="#3.3.8">&sect;3.3.8</a>).
+Like a to-be-closed variable in Lua,
+the value at that slot in the stack will be closed
+when it goes out of scope.
+Here, in the context of a C function,
+to go out of scope means that the running function returns to Lua,
+or there is an error,
+or the slot is removed from the stack through
+<a href="#lua_settop"><code>lua_settop</code></a> or <a href="#lua_pop"><code>lua_pop</code></a>,
+or there is a call to <a href="#lua_closeslot"><code>lua_closeslot</code></a>.
+A slot marked as to-be-closed should not be removed from the stack
+by any other function in the API except <a href="#lua_settop"><code>lua_settop</code></a> or <a href="#lua_pop"><code>lua_pop</code></a>,
+unless previously deactivated by <a href="#lua_closeslot"><code>lua_closeslot</code></a>.
+
+
+<p>
+This function should not be called for an index
+that is equal to or below an active to-be-closed slot.
+
+
+<p>
+Note that, both in case of errors and of a regular return,
+by the time the <code>__close</code> metamethod runs,
+the C&nbsp;stack was already unwound,
+so that any automatic C&nbsp;variable declared in the calling function
+(e.g., a buffer) will be out of scope.
+
+
+
+
+
+<hr><h3><a name="lua_tointeger"><code>lua_tointeger</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Integer lua_tointeger (lua_State *L, int index);</pre>
+
+<p>
+Equivalent to <a href="#lua_tointegerx"><code>lua_tointegerx</code></a> with <code>isnum</code> equal to <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_tointegerx"><code>lua_tointegerx</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Integer lua_tointegerx (lua_State *L, int index, int *isnum);</pre>
+
+<p>
+Converts the Lua value at the given index
+to the signed integral type <a href="#lua_Integer"><code>lua_Integer</code></a>.
+The Lua value must be an integer,
+or a number or string convertible to an integer (see <a href="#3.4.3">&sect;3.4.3</a>);
+otherwise, <code>lua_tointegerx</code> returns&nbsp;0.
+
+
+<p>
+If <code>isnum</code> is not <code>NULL</code>,
+its referent is assigned a boolean value that
+indicates whether the operation succeeded.
+
+
+
+
+
+<hr><h3><a name="lua_tolstring"><code>lua_tolstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>const char *lua_tolstring (lua_State *L, int index, size_t *len);</pre>
+
+<p>
+Converts the Lua value at the given index to a C&nbsp;string.
+If <code>len</code> is not <code>NULL</code>,
+it sets <code>*len</code> with the string length.
+The Lua value must be a string or a number;
+otherwise, the function returns <code>NULL</code>.
+If the value is a number,
+then <code>lua_tolstring</code> also
+<em>changes the actual value in the stack to a string</em>.
+(This change confuses <a href="#lua_next"><code>lua_next</code></a>
+when <code>lua_tolstring</code> is applied to keys during a table traversal.)
+
+
+<p>
+<code>lua_tolstring</code> returns a pointer
+to a string inside the Lua state (see <a href="#4.1.3">&sect;4.1.3</a>).
+This string always has a zero ('<code>\0</code>')
+after its last character (as in&nbsp;C),
+but can contain other zeros in its body.
+
+
+
+
+
+<hr><h3><a name="lua_tonumber"><code>lua_tonumber</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Number lua_tonumber (lua_State *L, int index);</pre>
+
+<p>
+Equivalent to <a href="#lua_tonumberx"><code>lua_tonumberx</code></a> with <code>isnum</code> equal to <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_tonumberx"><code>lua_tonumberx</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Number lua_tonumberx (lua_State *L, int index, int *isnum);</pre>
+
+<p>
+Converts the Lua value at the given index
+to the C&nbsp;type <a href="#lua_Number"><code>lua_Number</code></a> (see <a href="#lua_Number"><code>lua_Number</code></a>).
+The Lua value must be a number or a string convertible to a number
+(see <a href="#3.4.3">&sect;3.4.3</a>);
+otherwise, <a href="#lua_tonumberx"><code>lua_tonumberx</code></a> returns&nbsp;0.
+
+
+<p>
+If <code>isnum</code> is not <code>NULL</code>,
+its referent is assigned a boolean value that
+indicates whether the operation succeeded.
+
+
+
+
+
+<hr><h3><a name="lua_topointer"><code>lua_topointer</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>const void *lua_topointer (lua_State *L, int index);</pre>
+
+<p>
+Converts the value at the given index to a generic
+C&nbsp;pointer (<code>void*</code>).
+The value can be a userdata, a table, a thread, a string, or a function;
+otherwise, <code>lua_topointer</code> returns <code>NULL</code>.
+Different objects will give different pointers.
+There is no way to convert the pointer back to its original value.
+
+
+<p>
+Typically this function is used only for hashing and debug information.
+
+
+
+
+
+<hr><h3><a name="lua_tostring"><code>lua_tostring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>const char *lua_tostring (lua_State *L, int index);</pre>
+
+<p>
+Equivalent to <a href="#lua_tolstring"><code>lua_tolstring</code></a> with <code>len</code> equal to <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_tothread"><code>lua_tothread</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_State *lua_tothread (lua_State *L, int index);</pre>
+
+<p>
+Converts the value at the given index to a Lua thread
+(represented as <code>lua_State*</code>).
+This value must be a thread;
+otherwise, the function returns <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_touserdata"><code>lua_touserdata</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void *lua_touserdata (lua_State *L, int index);</pre>
+
+<p>
+If the value at the given index is a full userdata,
+returns its memory-block address.
+If the value is a light userdata,
+returns its value (a pointer).
+Otherwise, returns <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_type"><code>lua_type</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_type (lua_State *L, int index);</pre>
+
+<p>
+Returns the type of the value in the given valid index,
+or <code>LUA_TNONE</code> for a non-valid but acceptable index.
+The types returned by <a href="#lua_type"><code>lua_type</code></a> are coded by the following constants
+defined in <code>lua.h</code>:
+<a name="pdf-LUA_TNIL"><code>LUA_TNIL</code></a>,
+<a name="pdf-LUA_TNUMBER"><code>LUA_TNUMBER</code></a>,
+<a name="pdf-LUA_TBOOLEAN"><code>LUA_TBOOLEAN</code></a>,
+<a name="pdf-LUA_TSTRING"><code>LUA_TSTRING</code></a>,
+<a name="pdf-LUA_TTABLE"><code>LUA_TTABLE</code></a>,
+<a name="pdf-LUA_TFUNCTION"><code>LUA_TFUNCTION</code></a>,
+<a name="pdf-LUA_TUSERDATA"><code>LUA_TUSERDATA</code></a>,
+<a name="pdf-LUA_TTHREAD"><code>LUA_TTHREAD</code></a>,
+and
+<a name="pdf-LUA_TLIGHTUSERDATA"><code>LUA_TLIGHTUSERDATA</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_typename"><code>lua_typename</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>const char *lua_typename (lua_State *L, int tp);</pre>
+
+<p>
+Returns the name of the type encoded by the value <code>tp</code>,
+which must be one the values returned by <a href="#lua_type"><code>lua_type</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_Unsigned"><code>lua_Unsigned</code></a></h3>
+<pre>typedef ... lua_Unsigned;</pre>
+
+<p>
+The unsigned version of <a href="#lua_Integer"><code>lua_Integer</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_upvalueindex"><code>lua_upvalueindex</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_upvalueindex (int i);</pre>
+
+<p>
+Returns the pseudo-index that represents the <code>i</code>-th upvalue of
+the running function (see <a href="#4.2">&sect;4.2</a>).
+<code>i</code> must be in the range <em>[1,256]</em>.
+
+
+
+
+
+<hr><h3><a name="lua_version"><code>lua_version</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Number lua_version (lua_State *L);</pre>
+
+<p>
+Returns the version number of this core.
+
+
+
+
+
+<hr><h3><a name="lua_WarnFunction"><code>lua_WarnFunction</code></a></h3>
+<pre>typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);</pre>
+
+<p>
+The type of warning functions, called by Lua to emit warnings.
+The first parameter is an opaque pointer
+set by <a href="#lua_setwarnf"><code>lua_setwarnf</code></a>.
+The second parameter is the warning message.
+The third parameter is a boolean that
+indicates whether the message is
+to be continued by the message in the next call.
+
+
+<p>
+See <a href="#pdf-warn"><code>warn</code></a> for more details about warnings.
+
+
+
+
+
+<hr><h3><a name="lua_warning"><code>lua_warning</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_warning (lua_State *L, const char *msg, int tocont);</pre>
+
+<p>
+Emits a warning with the given message.
+A message in a call with <code>tocont</code> true should be
+continued in another call to this function.
+
+
+<p>
+See <a href="#pdf-warn"><code>warn</code></a> for more details about warnings.
+
+
+
+
+
+<hr><h3><a name="lua_Writer"><code>lua_Writer</code></a></h3>
+<pre>typedef int (*lua_Writer) (lua_State *L,
+ const void* p,
+ size_t sz,
+ void* ud);</pre>
+
+<p>
+The type of the writer function used by <a href="#lua_dump"><code>lua_dump</code></a>.
+Every time <a href="#lua_dump"><code>lua_dump</code></a> produces another piece of chunk,
+it calls the writer,
+passing along the buffer to be written (<code>p</code>),
+its size (<code>sz</code>),
+and the <code>ud</code> parameter supplied to <a href="#lua_dump"><code>lua_dump</code></a>.
+
+
+<p>
+The writer returns an error code:
+0&nbsp;means no errors;
+any other value means an error and stops <a href="#lua_dump"><code>lua_dump</code></a> from
+calling the writer again.
+
+
+
+
+
+<hr><h3><a name="lua_xmove"><code>lua_xmove</code></a></h3><p>
+<span class="apii">[-?, +?, &ndash;]</span>
+<pre>void lua_xmove (lua_State *from, lua_State *to, int n);</pre>
+
+<p>
+Exchange values between different threads of the same state.
+
+
+<p>
+This function pops <code>n</code> values from the stack <code>from</code>,
+and pushes them onto the stack <code>to</code>.
+
+
+
+
+
+<hr><h3><a name="lua_yield"><code>lua_yield</code></a></h3><p>
+<span class="apii">[-?, +?, <em>v</em>]</span>
+<pre>int lua_yield (lua_State *L, int nresults);</pre>
+
+<p>
+This function is equivalent to <a href="#lua_yieldk"><code>lua_yieldk</code></a>,
+but it has no continuation (see <a href="#4.5">&sect;4.5</a>).
+Therefore, when the thread resumes,
+it continues the function that called
+the function calling <code>lua_yield</code>.
+To avoid surprises,
+this function should be called only in a tail call.
+
+
+
+
+
+<hr><h3><a name="lua_yieldk"><code>lua_yieldk</code></a></h3><p>
+<span class="apii">[-?, +?, <em>v</em>]</span>
+<pre>int lua_yieldk (lua_State *L,
+ int nresults,
+ lua_KContext ctx,
+ lua_KFunction k);</pre>
+
+<p>
+Yields a coroutine (thread).
+
+
+<p>
+When a C&nbsp;function calls <a href="#lua_yieldk"><code>lua_yieldk</code></a>,
+the running coroutine suspends its execution,
+and the call to <a href="#lua_resume"><code>lua_resume</code></a> that started this coroutine returns.
+The parameter <code>nresults</code> is the number of values from the stack
+that will be passed as results to <a href="#lua_resume"><code>lua_resume</code></a>.
+
+
+<p>
+When the coroutine is resumed again,
+Lua calls the given continuation function <code>k</code> to continue
+the execution of the C&nbsp;function that yielded (see <a href="#4.5">&sect;4.5</a>).
+This continuation function receives the same stack
+from the previous function,
+with the <code>n</code> results removed and
+replaced by the arguments passed to <a href="#lua_resume"><code>lua_resume</code></a>.
+Moreover,
+the continuation function receives the value <code>ctx</code>
+that was passed to <a href="#lua_yieldk"><code>lua_yieldk</code></a>.
+
+
+<p>
+Usually, this function does not return;
+when the coroutine eventually resumes,
+it continues executing the continuation function.
+However, there is one special case,
+which is when this function is called
+from inside a line or a count hook (see <a href="#4.7">&sect;4.7</a>).
+In that case, <code>lua_yieldk</code> should be called with no continuation
+(probably in the form of <a href="#lua_yield"><code>lua_yield</code></a>) and no results,
+and the hook should return immediately after the call.
+Lua will yield and,
+when the coroutine resumes again,
+it will continue the normal execution
+of the (Lua) function that triggered the hook.
+
+
+<p>
+This function can raise an error if it is called from a thread
+with a pending C call with no continuation function
+(what is called a <em>C-call boundary</em>),
+or it is called from a thread that is not running inside a resume
+(typically the main thread).
+
+
+
+
+
+
+
+<h2>4.7 &ndash; <a name="4.7">The Debug Interface</a></h2>
+
+<p>
+Lua has no built-in debugging facilities.
+Instead, it offers a special interface
+by means of functions and <em>hooks</em>.
+This interface allows the construction of different
+kinds of debuggers, profilers, and other tools
+that need "inside information" from the interpreter.
+
+
+
+<hr><h3><a name="lua_Debug"><code>lua_Debug</code></a></h3>
+<pre>typedef struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) */
+ const char *what; /* (S) */
+ const char *source; /* (S) */
+ size_t srclen; /* (S) */
+ int currentline; /* (l) */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ unsigned char nups; /* (u) number of upvalues */
+ unsigned char nparams; /* (u) number of parameters */
+ char isvararg; /* (u) */
+ char istailcall; /* (t) */
+ unsigned short ftransfer; /* (r) index of first value transferred */
+ unsigned short ntransfer; /* (r) number of transferred values */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ <em>other fields</em>
+} lua_Debug;</pre>
+
+<p>
+A structure used to carry different pieces of
+information about a function or an activation record.
+<a href="#lua_getstack"><code>lua_getstack</code></a> fills only the private part
+of this structure, for later use.
+To fill the other fields of <a href="#lua_Debug"><code>lua_Debug</code></a> with useful information,
+you must call <a href="#lua_getinfo"><code>lua_getinfo</code></a> with an appropriate parameter.
+(Specifically, to get a field,
+you must add the letter between parentheses in the field's comment
+to the parameter <code>what</code> of <a href="#lua_getinfo"><code>lua_getinfo</code></a>.)
+
+
+<p>
+The fields of <a href="#lua_Debug"><code>lua_Debug</code></a> have the following meaning:
+
+<ul>
+
+<li><b><code>source</code>: </b>
+the source of the chunk that created the function.
+If <code>source</code> starts with a '<code>@</code>',
+it means that the function was defined in a file where
+the file name follows the '<code>@</code>'.
+If <code>source</code> starts with a '<code>=</code>',
+the remainder of its contents describes the source in a user-dependent manner.
+Otherwise,
+the function was defined in a string where
+<code>source</code> is that string.
+</li>
+
+<li><b><code>srclen</code>: </b>
+The length of the string <code>source</code>.
+</li>
+
+<li><b><code>short_src</code>: </b>
+a "printable" version of <code>source</code>, to be used in error messages.
+</li>
+
+<li><b><code>linedefined</code>: </b>
+the line number where the definition of the function starts.
+</li>
+
+<li><b><code>lastlinedefined</code>: </b>
+the line number where the definition of the function ends.
+</li>
+
+<li><b><code>what</code>: </b>
+the string <code>"Lua"</code> if the function is a Lua function,
+<code>"C"</code> if it is a C&nbsp;function,
+<code>"main"</code> if it is the main part of a chunk.
+</li>
+
+<li><b><code>currentline</code>: </b>
+the current line where the given function is executing.
+When no line information is available,
+<code>currentline</code> is set to -1.
+</li>
+
+<li><b><code>name</code>: </b>
+a reasonable name for the given function.
+Because functions in Lua are first-class values,
+they do not have a fixed name:
+some functions can be the value of multiple global variables,
+while others can be stored only in a table field.
+The <code>lua_getinfo</code> function checks how the function was
+called to find a suitable name.
+If it cannot find a name,
+then <code>name</code> is set to <code>NULL</code>.
+</li>
+
+<li><b><code>namewhat</code>: </b>
+explains the <code>name</code> field.
+The value of <code>namewhat</code> can be
+<code>"global"</code>, <code>"local"</code>, <code>"method"</code>,
+<code>"field"</code>, <code>"upvalue"</code>, or <code>""</code> (the empty string),
+according to how the function was called.
+(Lua uses the empty string when no other option seems to apply.)
+</li>
+
+<li><b><code>istailcall</code>: </b>
+true if this function invocation was called by a tail call.
+In this case, the caller of this level is not in the stack.
+</li>
+
+<li><b><code>nups</code>: </b>
+the number of upvalues of the function.
+</li>
+
+<li><b><code>nparams</code>: </b>
+the number of parameters of the function
+(always 0&nbsp;for C&nbsp;functions).
+</li>
+
+<li><b><code>isvararg</code>: </b>
+true if the function is a variadic function
+(always true for C&nbsp;functions).
+</li>
+
+<li><b><code>ftransfer</code>: </b>
+the index in the stack of the first value being "transferred",
+that is, parameters in a call or return values in a return.
+(The other values are in consecutive indices.)
+Using this index, you can access and modify these values
+through <a href="#lua_getlocal"><code>lua_getlocal</code></a> and <a href="#lua_setlocal"><code>lua_setlocal</code></a>.
+This field is only meaningful during a
+call hook, denoting the first parameter,
+or a return hook, denoting the first value being returned.
+(For call hooks, this value is always 1.)
+</li>
+
+<li><b><code>ntransfer</code>: </b>
+The number of values being transferred (see previous item).
+(For calls of Lua functions,
+this value is always equal to <code>nparams</code>.)
+</li>
+
+</ul>
+
+
+
+
+<hr><h3><a name="lua_gethook"><code>lua_gethook</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Hook lua_gethook (lua_State *L);</pre>
+
+<p>
+Returns the current hook function.
+
+
+
+
+
+<hr><h3><a name="lua_gethookcount"><code>lua_gethookcount</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_gethookcount (lua_State *L);</pre>
+
+<p>
+Returns the current hook count.
+
+
+
+
+
+<hr><h3><a name="lua_gethookmask"><code>lua_gethookmask</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_gethookmask (lua_State *L);</pre>
+
+<p>
+Returns the current hook mask.
+
+
+
+
+
+<hr><h3><a name="lua_getinfo"><code>lua_getinfo</code></a></h3><p>
+<span class="apii">[-(0|1), +(0|1|2), <em>m</em>]</span>
+<pre>int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);</pre>
+
+<p>
+Gets information about a specific function or function invocation.
+
+
+<p>
+To get information about a function invocation,
+the parameter <code>ar</code> must be a valid activation record that was
+filled by a previous call to <a href="#lua_getstack"><code>lua_getstack</code></a> or
+given as argument to a hook (see <a href="#lua_Hook"><code>lua_Hook</code></a>).
+
+
+<p>
+To get information about a function, you push it onto the stack
+and start the <code>what</code> string with the character '<code>&gt;</code>'.
+(In that case,
+<code>lua_getinfo</code> pops the function from the top of the stack.)
+For instance, to know in which line a function <code>f</code> was defined,
+you can write the following code:
+
+<pre>
+ lua_Debug ar;
+ lua_getglobal(L, "f"); /* get global 'f' */
+ lua_getinfo(L, "&gt;S", &amp;ar);
+ printf("%d\n", ar.linedefined);
+</pre>
+
+<p>
+Each character in the string <code>what</code>
+selects some fields of the structure <code>ar</code> to be filled or
+a value to be pushed on the stack.
+(These characters are also documented in the declaration of
+the structure <a href="#lua_Debug"><code>lua_Debug</code></a>,
+between parentheses in the comments following each field.)
+
+<ul>
+
+<li><b>'<code>f</code>': </b>
+pushes onto the stack the function that is
+running at the given level;
+</li>
+
+<li><b>'<code>l</code>': </b> fills in the field <code>currentline</code>;
+</li>
+
+<li><b>'<code>n</code>': </b> fills in the fields <code>name</code> and <code>namewhat</code>;
+</li>
+
+<li><b>'<code>r</code>': </b> fills in the fields <code>ftransfer</code> and <code>ntransfer</code>;
+</li>
+
+<li><b>'<code>S</code>': </b>
+fills in the fields <code>source</code>, <code>short_src</code>,
+<code>linedefined</code>, <code>lastlinedefined</code>, and <code>what</code>;
+</li>
+
+<li><b>'<code>t</code>': </b> fills in the field <code>istailcall</code>;
+</li>
+
+<li><b>'<code>u</code>': </b> fills in the fields
+<code>nups</code>, <code>nparams</code>, and <code>isvararg</code>;
+</li>
+
+<li><b>'<code>L</code>': </b>
+pushes onto the stack a table whose indices are
+the lines on the function with some associated code,
+that is, the lines where you can put a break point.
+(Lines with no code include empty lines and comments.)
+If this option is given together with option '<code>f</code>',
+its table is pushed after the function.
+This is the only option that can raise a memory error.
+</li>
+
+</ul>
+
+<p>
+This function returns 0 to signal an invalid option in <code>what</code>;
+even then the valid options are handled correctly.
+
+
+
+
+
+<hr><h3><a name="lua_getlocal"><code>lua_getlocal</code></a></h3><p>
+<span class="apii">[-0, +(0|1), &ndash;]</span>
+<pre>const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);</pre>
+
+<p>
+Gets information about a local variable or a temporary value
+of a given activation record or a given function.
+
+
+<p>
+In the first case,
+the parameter <code>ar</code> must be a valid activation record that was
+filled by a previous call to <a href="#lua_getstack"><code>lua_getstack</code></a> or
+given as argument to a hook (see <a href="#lua_Hook"><code>lua_Hook</code></a>).
+The index <code>n</code> selects which local variable to inspect;
+see <a href="#pdf-debug.getlocal"><code>debug.getlocal</code></a> for details about variable indices
+and names.
+
+
+<p>
+<a href="#lua_getlocal"><code>lua_getlocal</code></a> pushes the variable's value onto the stack
+and returns its name.
+
+
+<p>
+In the second case, <code>ar</code> must be <code>NULL</code> and the function
+to be inspected must be on the top of the stack.
+In this case, only parameters of Lua functions are visible
+(as there is no information about what variables are active)
+and no values are pushed onto the stack.
+
+
+<p>
+Returns <code>NULL</code> (and pushes nothing)
+when the index is greater than
+the number of active local variables.
+
+
+
+
+
+<hr><h3><a name="lua_getstack"><code>lua_getstack</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_getstack (lua_State *L, int level, lua_Debug *ar);</pre>
+
+<p>
+Gets information about the interpreter runtime stack.
+
+
+<p>
+This function fills parts of a <a href="#lua_Debug"><code>lua_Debug</code></a> structure with
+an identification of the <em>activation record</em>
+of the function executing at a given level.
+Level&nbsp;0 is the current running function,
+whereas level <em>n+1</em> is the function that has called level <em>n</em>
+(except for tail calls, which do not count in the stack).
+When called with a level greater than the stack depth,
+<a href="#lua_getstack"><code>lua_getstack</code></a> returns 0;
+otherwise it returns 1.
+
+
+
+
+
+<hr><h3><a name="lua_getupvalue"><code>lua_getupvalue</code></a></h3><p>
+<span class="apii">[-0, +(0|1), &ndash;]</span>
+<pre>const char *lua_getupvalue (lua_State *L, int funcindex, int n);</pre>
+
+<p>
+Gets information about the <code>n</code>-th upvalue
+of the closure at index <code>funcindex</code>.
+It pushes the upvalue's value onto the stack
+and returns its name.
+Returns <code>NULL</code> (and pushes nothing)
+when the index <code>n</code> is greater than the number of upvalues.
+
+
+<p>
+See <a href="#pdf-debug.getupvalue"><code>debug.getupvalue</code></a> for more information about upvalues.
+
+
+
+
+
+<hr><h3><a name="lua_Hook"><code>lua_Hook</code></a></h3>
+<pre>typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);</pre>
+
+<p>
+Type for debugging hook functions.
+
+
+<p>
+Whenever a hook is called, its <code>ar</code> argument has its field
+<code>event</code> set to the specific event that triggered the hook.
+Lua identifies these events with the following constants:
+<a name="pdf-LUA_HOOKCALL"><code>LUA_HOOKCALL</code></a>, <a name="pdf-LUA_HOOKRET"><code>LUA_HOOKRET</code></a>,
+<a name="pdf-LUA_HOOKTAILCALL"><code>LUA_HOOKTAILCALL</code></a>, <a name="pdf-LUA_HOOKLINE"><code>LUA_HOOKLINE</code></a>,
+and <a name="pdf-LUA_HOOKCOUNT"><code>LUA_HOOKCOUNT</code></a>.
+Moreover, for line events, the field <code>currentline</code> is also set.
+To get the value of any other field in <code>ar</code>,
+the hook must call <a href="#lua_getinfo"><code>lua_getinfo</code></a>.
+
+
+<p>
+For call events, <code>event</code> can be <code>LUA_HOOKCALL</code>,
+the normal value, or <code>LUA_HOOKTAILCALL</code>, for a tail call;
+in this case, there will be no corresponding return event.
+
+
+<p>
+While Lua is running a hook, it disables other calls to hooks.
+Therefore, if a hook calls back Lua to execute a function or a chunk,
+this execution occurs without any calls to hooks.
+
+
+<p>
+Hook functions cannot have continuations,
+that is, they cannot call <a href="#lua_yieldk"><code>lua_yieldk</code></a>,
+<a href="#lua_pcallk"><code>lua_pcallk</code></a>, or <a href="#lua_callk"><code>lua_callk</code></a> with a non-null <code>k</code>.
+
+
+<p>
+Hook functions can yield under the following conditions:
+Only count and line events can yield;
+to yield, a hook function must finish its execution
+calling <a href="#lua_yield"><code>lua_yield</code></a> with <code>nresults</code> equal to zero
+(that is, with no values).
+
+
+
+
+
+<hr><h3><a name="lua_sethook"><code>lua_sethook</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);</pre>
+
+<p>
+Sets the debugging hook function.
+
+
+<p>
+Argument <code>f</code> is the hook function.
+<code>mask</code> specifies on which events the hook will be called:
+it is formed by a bitwise OR of the constants
+<a name="pdf-LUA_MASKCALL"><code>LUA_MASKCALL</code></a>,
+<a name="pdf-LUA_MASKRET"><code>LUA_MASKRET</code></a>,
+<a name="pdf-LUA_MASKLINE"><code>LUA_MASKLINE</code></a>,
+and <a name="pdf-LUA_MASKCOUNT"><code>LUA_MASKCOUNT</code></a>.
+The <code>count</code> argument is only meaningful when the mask
+includes <code>LUA_MASKCOUNT</code>.
+For each event, the hook is called as explained below:
+
+<ul>
+
+<li><b>The call hook: </b> is called when the interpreter calls a function.
+The hook is called just after Lua enters the new function.
+</li>
+
+<li><b>The return hook: </b> is called when the interpreter returns from a function.
+The hook is called just before Lua leaves the function.
+</li>
+
+<li><b>The line hook: </b> is called when the interpreter is about to
+start the execution of a new line of code,
+or when it jumps back in the code (even to the same line).
+This event only happens while Lua is executing a Lua function.
+</li>
+
+<li><b>The count hook: </b> is called after the interpreter executes every
+<code>count</code> instructions.
+This event only happens while Lua is executing a Lua function.
+</li>
+
+</ul>
+
+<p>
+Hooks are disabled by setting <code>mask</code> to zero.
+
+
+
+
+
+<hr><h3><a name="lua_setlocal"><code>lua_setlocal</code></a></h3><p>
+<span class="apii">[-(0|1), +0, &ndash;]</span>
+<pre>const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);</pre>
+
+<p>
+Sets the value of a local variable of a given activation record.
+It assigns the value on the top of the stack
+to the variable and returns its name.
+It also pops the value from the stack.
+
+
+<p>
+Returns <code>NULL</code> (and pops nothing)
+when the index is greater than
+the number of active local variables.
+
+
+<p>
+Parameters <code>ar</code> and <code>n</code> are as in the function <a href="#lua_getlocal"><code>lua_getlocal</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_setupvalue"><code>lua_setupvalue</code></a></h3><p>
+<span class="apii">[-(0|1), +0, &ndash;]</span>
+<pre>const char *lua_setupvalue (lua_State *L, int funcindex, int n);</pre>
+
+<p>
+Sets the value of a closure's upvalue.
+It assigns the value on the top of the stack
+to the upvalue and returns its name.
+It also pops the value from the stack.
+
+
+<p>
+Returns <code>NULL</code> (and pops nothing)
+when the index <code>n</code> is greater than the number of upvalues.
+
+
+<p>
+Parameters <code>funcindex</code> and <code>n</code> are as in
+the function <a href="#lua_getupvalue"><code>lua_getupvalue</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_upvalueid"><code>lua_upvalueid</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void *lua_upvalueid (lua_State *L, int funcindex, int n);</pre>
+
+<p>
+Returns a unique identifier for the upvalue numbered <code>n</code>
+from the closure at index <code>funcindex</code>.
+
+
+<p>
+These unique identifiers allow a program to check whether different
+closures share upvalues.
+Lua closures that share an upvalue
+(that is, that access a same external local variable)
+will return identical ids for those upvalue indices.
+
+
+<p>
+Parameters <code>funcindex</code> and <code>n</code> are as in
+the function <a href="#lua_getupvalue"><code>lua_getupvalue</code></a>,
+but <code>n</code> cannot be greater than the number of upvalues.
+
+
+
+
+
+<hr><h3><a name="lua_upvaluejoin"><code>lua_upvaluejoin</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_upvaluejoin (lua_State *L, int funcindex1, int n1,
+ int funcindex2, int n2);</pre>
+
+<p>
+Make the <code>n1</code>-th upvalue of the Lua closure at index <code>funcindex1</code>
+refer to the <code>n2</code>-th upvalue of the Lua closure at index <code>funcindex2</code>.
+
+
+
+
+
+
+
+<h1>5 &ndash; <a name="5">The Auxiliary Library</a></h1>
+
+
+
+<p>
+
+The <em>auxiliary library</em> provides several convenient functions
+to interface C with Lua.
+While the basic API provides the primitive functions for all
+interactions between C and Lua,
+the auxiliary library provides higher-level functions for some
+common tasks.
+
+
+<p>
+All functions and types from the auxiliary library
+are defined in header file <code>lauxlib.h</code> and
+have a prefix <code>luaL_</code>.
+
+
+<p>
+All functions in the auxiliary library are built on
+top of the basic API,
+and so they provide nothing that cannot be done with that API.
+Nevertheless, the use of the auxiliary library ensures
+more consistency to your code.
+
+
+<p>
+Several functions in the auxiliary library use internally some
+extra stack slots.
+When a function in the auxiliary library uses less than five slots,
+it does not check the stack size;
+it simply assumes that there are enough slots.
+
+
+<p>
+Several functions in the auxiliary library are used to
+check C&nbsp;function arguments.
+Because the error message is formatted for arguments
+(e.g., "<code>bad argument #1</code>"),
+you should not use these functions for other stack values.
+
+
+<p>
+Functions called <code>luaL_check*</code>
+always raise an error if the check is not satisfied.
+
+
+
+
+
+<h2>5.1 &ndash; <a name="5.1">Functions and Types</a></h2>
+
+<p>
+Here we list all functions and types from the auxiliary library
+in alphabetical order.
+
+
+
+<hr><h3><a name="luaL_addchar"><code>luaL_addchar</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>void luaL_addchar (luaL_Buffer *B, char c);</pre>
+
+<p>
+Adds the byte <code>c</code> to the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_addgsub"><code>luaL_addgsub</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>const void luaL_addgsub (luaL_Buffer *B, const char *s,
+ const char *p, const char *r);</pre>
+
+<p>
+Adds a copy of the string <code>s</code> to the buffer <code>B</code> (see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>),
+replacing any occurrence of the string <code>p</code>
+with the string <code>r</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_addlstring"><code>luaL_addlstring</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);</pre>
+
+<p>
+Adds the string pointed to by <code>s</code> with length <code>l</code> to
+the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+The string can contain embedded zeros.
+
+
+
+
+
+<hr><h3><a name="luaL_addsize"><code>luaL_addsize</code></a></h3><p>
+<span class="apii">[-?, +?, &ndash;]</span>
+<pre>void luaL_addsize (luaL_Buffer *B, size_t n);</pre>
+
+<p>
+Adds to the buffer <code>B</code>
+a string of length <code>n</code> previously copied to the
+buffer area (see <a href="#luaL_prepbuffer"><code>luaL_prepbuffer</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_addstring"><code>luaL_addstring</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>void luaL_addstring (luaL_Buffer *B, const char *s);</pre>
+
+<p>
+Adds the zero-terminated string pointed to by <code>s</code>
+to the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_addvalue"><code>luaL_addvalue</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>void luaL_addvalue (luaL_Buffer *B);</pre>
+
+<p>
+Adds the value on the top of the stack
+to the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+Pops the value.
+
+
+<p>
+This is the only function on string buffers that can (and must)
+be called with an extra element on the stack,
+which is the value to be added to the buffer.
+
+
+
+
+
+<hr><h3><a name="luaL_argcheck"><code>luaL_argcheck</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_argcheck (lua_State *L,
+ int cond,
+ int arg,
+ const char *extramsg);</pre>
+
+<p>
+Checks whether <code>cond</code> is true.
+If it is not, raises an error with a standard message (see <a href="#luaL_argerror"><code>luaL_argerror</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_argerror"><code>luaL_argerror</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_argerror (lua_State *L, int arg, const char *extramsg);</pre>
+
+<p>
+Raises an error reporting a problem with argument <code>arg</code>
+of the C&nbsp;function that called it,
+using a standard message
+that includes <code>extramsg</code> as a comment:
+
+<pre>
+ bad argument #<em>arg</em> to '<em>funcname</em>' (<em>extramsg</em>)
+</pre><p>
+This function never returns.
+
+
+
+
+
+<hr><h3><a name="luaL_argexpected"><code>luaL_argexpected</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_argexpected (lua_State *L,
+ int cond,
+ int arg,
+ const char *tname);</pre>
+
+<p>
+Checks whether <code>cond</code> is true.
+If it is not, raises an error about the type of the argument <code>arg</code>
+with a standard message (see <a href="#luaL_typeerror"><code>luaL_typeerror</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_Buffer"><code>luaL_Buffer</code></a></h3>
+<pre>typedef struct luaL_Buffer luaL_Buffer;</pre>
+
+<p>
+Type for a <em>string buffer</em>.
+
+
+<p>
+A string buffer allows C&nbsp;code to build Lua strings piecemeal.
+Its pattern of use is as follows:
+
+<ul>
+
+<li>First declare a variable <code>b</code> of type <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>.</li>
+
+<li>Then initialize it with a call <code>luaL_buffinit(L, &amp;b)</code>.</li>
+
+<li>
+Then add string pieces to the buffer calling any of
+the <code>luaL_add*</code> functions.
+</li>
+
+<li>
+Finish by calling <code>luaL_pushresult(&amp;b)</code>.
+This call leaves the final string on the top of the stack.
+</li>
+
+</ul>
+
+<p>
+If you know beforehand the maximum size of the resulting string,
+you can use the buffer like this:
+
+<ul>
+
+<li>First declare a variable <code>b</code> of type <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>.</li>
+
+<li>Then initialize it and preallocate a space of
+size <code>sz</code> with a call <code>luaL_buffinitsize(L, &amp;b, sz)</code>.</li>
+
+<li>Then produce the string into that space.</li>
+
+<li>
+Finish by calling <code>luaL_pushresultsize(&amp;b, sz)</code>,
+where <code>sz</code> is the total size of the resulting string
+copied into that space (which may be less than or
+equal to the preallocated size).
+</li>
+
+</ul>
+
+<p>
+During its normal operation,
+a string buffer uses a variable number of stack slots.
+So, while using a buffer, you cannot assume that you know where
+the top of the stack is.
+You can use the stack between successive calls to buffer operations
+as long as that use is balanced;
+that is,
+when you call a buffer operation,
+the stack is at the same level
+it was immediately after the previous buffer operation.
+(The only exception to this rule is <a href="#luaL_addvalue"><code>luaL_addvalue</code></a>.)
+After calling <a href="#luaL_pushresult"><code>luaL_pushresult</code></a>,
+the stack is back to its level when the buffer was initialized,
+plus the final string on its top.
+
+
+
+
+
+<hr><h3><a name="luaL_buffaddr"><code>luaL_buffaddr</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>char *luaL_buffaddr (luaL_Buffer *B);</pre>
+
+<p>
+Returns the address of the current content of buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+Note that any addition to the buffer may invalidate this address.
+
+
+
+
+
+<hr><h3><a name="luaL_buffinit"><code>luaL_buffinit</code></a></h3><p>
+<span class="apii">[-0, +?, &ndash;]</span>
+<pre>void luaL_buffinit (lua_State *L, luaL_Buffer *B);</pre>
+
+<p>
+Initializes a buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+This function does not allocate any space;
+the buffer must be declared as a variable.
+
+
+
+
+
+<hr><h3><a name="luaL_bufflen"><code>luaL_bufflen</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>size_t luaL_bufflen (luaL_Buffer *B);</pre>
+
+<p>
+Returns the length of the current content of buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_buffinitsize"><code>luaL_buffinitsize</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz);</pre>
+
+<p>
+Equivalent to the sequence
+<a href="#luaL_buffinit"><code>luaL_buffinit</code></a>, <a href="#luaL_prepbuffsize"><code>luaL_prepbuffsize</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_buffsub"><code>luaL_buffsub</code></a></h3><p>
+<span class="apii">[-?, +?, &ndash;]</span>
+<pre>void luaL_buffsub (luaL_Buffer *B, int n);</pre>
+
+<p>
+Removes <code>n</code> bytes from the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+The buffer must have at least that many bytes.
+
+
+
+
+
+<hr><h3><a name="luaL_callmeta"><code>luaL_callmeta</code></a></h3><p>
+<span class="apii">[-0, +(0|1), <em>e</em>]</span>
+<pre>int luaL_callmeta (lua_State *L, int obj, const char *e);</pre>
+
+<p>
+Calls a metamethod.
+
+
+<p>
+If the object at index <code>obj</code> has a metatable and this
+metatable has a field <code>e</code>,
+this function calls this field passing the object as its only argument.
+In this case this function returns true and pushes onto the
+stack the value returned by the call.
+If there is no metatable or no metamethod,
+this function returns false without pushing any value on the stack.
+
+
+
+
+
+<hr><h3><a name="luaL_checkany"><code>luaL_checkany</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_checkany (lua_State *L, int arg);</pre>
+
+<p>
+Checks whether the function has an argument
+of any type (including <b>nil</b>) at position <code>arg</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_checkinteger"><code>luaL_checkinteger</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Integer luaL_checkinteger (lua_State *L, int arg);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is an integer
+(or can be converted to an integer)
+and returns this integer.
+
+
+
+
+
+<hr><h3><a name="luaL_checklstring"><code>luaL_checklstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_checklstring (lua_State *L, int arg, size_t *l);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is a string
+and returns this string;
+if <code>l</code> is not <code>NULL</code> fills its referent
+with the string's length.
+
+
+<p>
+This function uses <a href="#lua_tolstring"><code>lua_tolstring</code></a> to get its result,
+so all conversions and caveats of that function apply here.
+
+
+
+
+
+<hr><h3><a name="luaL_checknumber"><code>luaL_checknumber</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Number luaL_checknumber (lua_State *L, int arg);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is a number
+and returns this number converted to a <code>lua_Number</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_checkoption"><code>luaL_checkoption</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_checkoption (lua_State *L,
+ int arg,
+ const char *def,
+ const char *const lst[]);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is a string and
+searches for this string in the array <code>lst</code>
+(which must be NULL-terminated).
+Returns the index in the array where the string was found.
+Raises an error if the argument is not a string or
+if the string cannot be found.
+
+
+<p>
+If <code>def</code> is not <code>NULL</code>,
+the function uses <code>def</code> as a default value when
+there is no argument <code>arg</code> or when this argument is <b>nil</b>.
+
+
+<p>
+This is a useful function for mapping strings to C&nbsp;enums.
+(The usual convention in Lua libraries is
+to use strings instead of numbers to select options.)
+
+
+
+
+
+<hr><h3><a name="luaL_checkstack"><code>luaL_checkstack</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_checkstack (lua_State *L, int sz, const char *msg);</pre>
+
+<p>
+Grows the stack size to <code>top + sz</code> elements,
+raising an error if the stack cannot grow to that size.
+<code>msg</code> is an additional text to go into the error message
+(or <code>NULL</code> for no additional text).
+
+
+
+
+
+<hr><h3><a name="luaL_checkstring"><code>luaL_checkstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_checkstring (lua_State *L, int arg);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is a string
+and returns this string.
+
+
+<p>
+This function uses <a href="#lua_tolstring"><code>lua_tolstring</code></a> to get its result,
+so all conversions and caveats of that function apply here.
+
+
+
+
+
+<hr><h3><a name="luaL_checktype"><code>luaL_checktype</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_checktype (lua_State *L, int arg, int t);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> has type <code>t</code>.
+See <a href="#lua_type"><code>lua_type</code></a> for the encoding of types for <code>t</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_checkudata"><code>luaL_checkudata</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void *luaL_checkudata (lua_State *L, int arg, const char *tname);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is a userdata
+of the type <code>tname</code> (see <a href="#luaL_newmetatable"><code>luaL_newmetatable</code></a>) and
+returns the userdata's memory-block address (see <a href="#lua_touserdata"><code>lua_touserdata</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_checkversion"><code>luaL_checkversion</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_checkversion (lua_State *L);</pre>
+
+<p>
+Checks whether the code making the call and the Lua library being called
+are using the same version of Lua and the same numeric types.
+
+
+
+
+
+<hr><h3><a name="luaL_dofile"><code>luaL_dofile</code></a></h3><p>
+<span class="apii">[-0, +?, <em>m</em>]</span>
+<pre>int luaL_dofile (lua_State *L, const char *filename);</pre>
+
+<p>
+Loads and runs the given file.
+It is defined as the following macro:
+
+<pre>
+ (luaL_loadfile(L, filename) || lua_pcall(L, 0, LUA_MULTRET, 0))
+</pre><p>
+It returns&nbsp;0 (<a href="#pdf-LUA_OK"><code>LUA_OK</code></a>) if there are no errors,
+or 1 in case of errors.
+
+
+
+
+
+<hr><h3><a name="luaL_dostring"><code>luaL_dostring</code></a></h3><p>
+<span class="apii">[-0, +?, &ndash;]</span>
+<pre>int luaL_dostring (lua_State *L, const char *str);</pre>
+
+<p>
+Loads and runs the given string.
+It is defined as the following macro:
+
+<pre>
+ (luaL_loadstring(L, str) || lua_pcall(L, 0, LUA_MULTRET, 0))
+</pre><p>
+It returns&nbsp;0 (<a href="#pdf-LUA_OK"><code>LUA_OK</code></a>) if there are no errors,
+or 1 in case of errors.
+
+
+
+
+
+<hr><h3><a name="luaL_error"><code>luaL_error</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_error (lua_State *L, const char *fmt, ...);</pre>
+
+<p>
+Raises an error.
+The error message format is given by <code>fmt</code>
+plus any extra arguments,
+following the same rules of <a href="#lua_pushfstring"><code>lua_pushfstring</code></a>.
+It also adds at the beginning of the message the file name and
+the line number where the error occurred,
+if this information is available.
+
+
+<p>
+This function never returns,
+but it is an idiom to use it in C&nbsp;functions
+as <code>return luaL_error(<em>args</em>)</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_execresult"><code>luaL_execresult</code></a></h3><p>
+<span class="apii">[-0, +3, <em>m</em>]</span>
+<pre>int luaL_execresult (lua_State *L, int stat);</pre>
+
+<p>
+This function produces the return values for
+process-related functions in the standard library
+(<a href="#pdf-os.execute"><code>os.execute</code></a> and <a href="#pdf-io.close"><code>io.close</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_fileresult"><code>luaL_fileresult</code></a></h3><p>
+<span class="apii">[-0, +(1|3), <em>m</em>]</span>
+<pre>int luaL_fileresult (lua_State *L, int stat, const char *fname);</pre>
+
+<p>
+This function produces the return values for
+file-related functions in the standard library
+(<a href="#pdf-io.open"><code>io.open</code></a>, <a href="#pdf-os.rename"><code>os.rename</code></a>, <a href="#pdf-file:seek"><code>file:seek</code></a>, etc.).
+
+
+
+
+
+<hr><h3><a name="luaL_getmetafield"><code>luaL_getmetafield</code></a></h3><p>
+<span class="apii">[-0, +(0|1), <em>m</em>]</span>
+<pre>int luaL_getmetafield (lua_State *L, int obj, const char *e);</pre>
+
+<p>
+Pushes onto the stack the field <code>e</code> from the metatable
+of the object at index <code>obj</code> and returns the type of the pushed value.
+If the object does not have a metatable,
+or if the metatable does not have this field,
+pushes nothing and returns <code>LUA_TNIL</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_getmetatable"><code>luaL_getmetatable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_getmetatable (lua_State *L, const char *tname);</pre>
+
+<p>
+Pushes onto the stack the metatable associated with the name <code>tname</code>
+in the registry (see <a href="#luaL_newmetatable"><code>luaL_newmetatable</code></a>),
+or <b>nil</b> if there is no metatable associated with that name.
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="luaL_getsubtable"><code>luaL_getsubtable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>int luaL_getsubtable (lua_State *L, int idx, const char *fname);</pre>
+
+<p>
+Ensures that the value <code>t[fname]</code>,
+where <code>t</code> is the value at index <code>idx</code>,
+is a table,
+and pushes that table onto the stack.
+Returns true if it finds a previous table there
+and false if it creates a new table.
+
+
+
+
+
+<hr><h3><a name="luaL_gsub"><code>luaL_gsub</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>const char *luaL_gsub (lua_State *L,
+ const char *s,
+ const char *p,
+ const char *r);</pre>
+
+<p>
+Creates a copy of string <code>s</code>,
+replacing any occurrence of the string <code>p</code>
+with the string <code>r</code>.
+Pushes the resulting string on the stack and returns it.
+
+
+
+
+
+<hr><h3><a name="luaL_len"><code>luaL_len</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>lua_Integer luaL_len (lua_State *L, int index);</pre>
+
+<p>
+Returns the "length" of the value at the given index
+as a number;
+it is equivalent to the '<code>#</code>' operator in Lua (see <a href="#3.4.7">&sect;3.4.7</a>).
+Raises an error if the result of the operation is not an integer.
+(This case can only happen through metamethods.)
+
+
+
+
+
+<hr><h3><a name="luaL_loadbuffer"><code>luaL_loadbuffer</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int luaL_loadbuffer (lua_State *L,
+ const char *buff,
+ size_t sz,
+ const char *name);</pre>
+
+<p>
+Equivalent to <a href="#luaL_loadbufferx"><code>luaL_loadbufferx</code></a> with <code>mode</code> equal to <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_loadbufferx"><code>luaL_loadbufferx</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int luaL_loadbufferx (lua_State *L,
+ const char *buff,
+ size_t sz,
+ const char *name,
+ const char *mode);</pre>
+
+<p>
+Loads a buffer as a Lua chunk.
+This function uses <a href="#lua_load"><code>lua_load</code></a> to load the chunk in the
+buffer pointed to by <code>buff</code> with size <code>sz</code>.
+
+
+<p>
+This function returns the same results as <a href="#lua_load"><code>lua_load</code></a>.
+<code>name</code> is the chunk name,
+used for debug information and error messages.
+The string <code>mode</code> works as in the function <a href="#lua_load"><code>lua_load</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_loadfile"><code>luaL_loadfile</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_loadfile (lua_State *L, const char *filename);</pre>
+
+<p>
+Equivalent to <a href="#luaL_loadfilex"><code>luaL_loadfilex</code></a> with <code>mode</code> equal to <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_loadfilex"><code>luaL_loadfilex</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_loadfilex (lua_State *L, const char *filename,
+ const char *mode);</pre>
+
+<p>
+Loads a file as a Lua chunk.
+This function uses <a href="#lua_load"><code>lua_load</code></a> to load the chunk in the file
+named <code>filename</code>.
+If <code>filename</code> is <code>NULL</code>,
+then it loads from the standard input.
+The first line in the file is ignored if it starts with a <code>#</code>.
+
+
+<p>
+The string <code>mode</code> works as in the function <a href="#lua_load"><code>lua_load</code></a>.
+
+
+<p>
+This function returns the same results as <a href="#lua_load"><code>lua_load</code></a>
+or <a href="#pdf-LUA_ERRFILE"><code>LUA_ERRFILE</code></a> for file-related errors.
+
+
+<p>
+As <a href="#lua_load"><code>lua_load</code></a>, this function only loads the chunk;
+it does not run it.
+
+
+
+
+
+<hr><h3><a name="luaL_loadstring"><code>luaL_loadstring</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int luaL_loadstring (lua_State *L, const char *s);</pre>
+
+<p>
+Loads a string as a Lua chunk.
+This function uses <a href="#lua_load"><code>lua_load</code></a> to load the chunk in
+the zero-terminated string <code>s</code>.
+
+
+<p>
+This function returns the same results as <a href="#lua_load"><code>lua_load</code></a>.
+
+
+<p>
+Also as <a href="#lua_load"><code>lua_load</code></a>, this function only loads the chunk;
+it does not run it.
+
+
+
+
+
+<hr><h3><a name="luaL_newlib"><code>luaL_newlib</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void luaL_newlib (lua_State *L, const luaL_Reg l[]);</pre>
+
+<p>
+Creates a new table and registers there
+the functions in the list <code>l</code>.
+
+
+<p>
+It is implemented as the following macro:
+
+<pre>
+ (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
+</pre><p>
+The array <code>l</code> must be the actual array,
+not a pointer to it.
+
+
+
+
+
+<hr><h3><a name="luaL_newlibtable"><code>luaL_newlibtable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void luaL_newlibtable (lua_State *L, const luaL_Reg l[]);</pre>
+
+<p>
+Creates a new table with a size optimized
+to store all entries in the array <code>l</code>
+(but does not actually store them).
+It is intended to be used in conjunction with <a href="#luaL_setfuncs"><code>luaL_setfuncs</code></a>
+(see <a href="#luaL_newlib"><code>luaL_newlib</code></a>).
+
+
+<p>
+It is implemented as a macro.
+The array <code>l</code> must be the actual array,
+not a pointer to it.
+
+
+
+
+
+<hr><h3><a name="luaL_newmetatable"><code>luaL_newmetatable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_newmetatable (lua_State *L, const char *tname);</pre>
+
+<p>
+If the registry already has the key <code>tname</code>,
+returns 0.
+Otherwise,
+creates a new table to be used as a metatable for userdata,
+adds to this new table the pair <code>__name = tname</code>,
+adds to the registry the pair <code>[tname] = new table</code>,
+and returns 1.
+
+
+<p>
+In both cases,
+the function pushes onto the stack the final value associated
+with <code>tname</code> in the registry.
+
+
+
+
+
+<hr><h3><a name="luaL_newstate"><code>luaL_newstate</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_State *luaL_newstate (void);</pre>
+
+<p>
+Creates a new Lua state.
+It calls <a href="#lua_newstate"><code>lua_newstate</code></a> with an
+allocator based on the ISO&nbsp;C allocation functions
+and then sets a warning function and a panic function (see <a href="#4.4">&sect;4.4</a>)
+that print messages to the standard error output.
+
+
+<p>
+Returns the new state,
+or <code>NULL</code> if there is a memory allocation error.
+
+
+
+
+
+<hr><h3><a name="luaL_openlibs"><code>luaL_openlibs</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>void luaL_openlibs (lua_State *L);</pre>
+
+<p>
+Opens all standard Lua libraries into the given state.
+
+
+
+
+
+<hr><h3><a name="luaL_opt"><code>luaL_opt</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>T luaL_opt (L, func, arg, dflt);</pre>
+
+<p>
+This macro is defined as follows:
+
+<pre>
+ (lua_isnoneornil(L,(arg)) ? (dflt) : func(L,(arg)))
+</pre><p>
+In words, if the argument <code>arg</code> is nil or absent,
+the macro results in the default <code>dflt</code>.
+Otherwise, it results in the result of calling <code>func</code>
+with the state <code>L</code> and the argument index <code>arg</code> as
+arguments.
+Note that it evaluates the expression <code>dflt</code> only if needed.
+
+
+
+
+
+<hr><h3><a name="luaL_optinteger"><code>luaL_optinteger</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Integer luaL_optinteger (lua_State *L,
+ int arg,
+ lua_Integer d);</pre>
+
+<p>
+If the function argument <code>arg</code> is an integer
+(or it is convertible to an integer),
+returns this integer.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+
+
+
+<hr><h3><a name="luaL_optlstring"><code>luaL_optlstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_optlstring (lua_State *L,
+ int arg,
+ const char *d,
+ size_t *l);</pre>
+
+<p>
+If the function argument <code>arg</code> is a string,
+returns this string.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+<p>
+If <code>l</code> is not <code>NULL</code>,
+fills its referent with the result's length.
+If the result is <code>NULL</code>
+(only possible when returning <code>d</code> and <code>d == NULL</code>),
+its length is considered zero.
+
+
+<p>
+This function uses <a href="#lua_tolstring"><code>lua_tolstring</code></a> to get its result,
+so all conversions and caveats of that function apply here.
+
+
+
+
+
+<hr><h3><a name="luaL_optnumber"><code>luaL_optnumber</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number d);</pre>
+
+<p>
+If the function argument <code>arg</code> is a number,
+returns this number as a <code>lua_Number</code>.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+
+
+
+<hr><h3><a name="luaL_optstring"><code>luaL_optstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_optstring (lua_State *L,
+ int arg,
+ const char *d);</pre>
+
+<p>
+If the function argument <code>arg</code> is a string,
+returns this string.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+
+
+
+<hr><h3><a name="luaL_prepbuffer"><code>luaL_prepbuffer</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>char *luaL_prepbuffer (luaL_Buffer *B);</pre>
+
+<p>
+Equivalent to <a href="#luaL_prepbuffsize"><code>luaL_prepbuffsize</code></a>
+with the predefined size <a name="pdf-LUAL_BUFFERSIZE"><code>LUAL_BUFFERSIZE</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_prepbuffsize"><code>luaL_prepbuffsize</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz);</pre>
+
+<p>
+Returns an address to a space of size <code>sz</code>
+where you can copy a string to be added to buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+After copying the string into this space you must call
+<a href="#luaL_addsize"><code>luaL_addsize</code></a> with the size of the string to actually add
+it to the buffer.
+
+
+
+
+
+<hr><h3><a name="luaL_pushfail"><code>luaL_pushfail</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void luaL_pushfail (lua_State *L);</pre>
+
+<p>
+Pushes the <b>fail</b> value onto the stack (see <a href="#6">&sect;6</a>).
+
+
+
+
+
+<hr><h3><a name="luaL_pushresult"><code>luaL_pushresult</code></a></h3><p>
+<span class="apii">[-?, +1, <em>m</em>]</span>
+<pre>void luaL_pushresult (luaL_Buffer *B);</pre>
+
+<p>
+Finishes the use of buffer <code>B</code> leaving the final string on
+the top of the stack.
+
+
+
+
+
+<hr><h3><a name="luaL_pushresultsize"><code>luaL_pushresultsize</code></a></h3><p>
+<span class="apii">[-?, +1, <em>m</em>]</span>
+<pre>void luaL_pushresultsize (luaL_Buffer *B, size_t sz);</pre>
+
+<p>
+Equivalent to the sequence <a href="#luaL_addsize"><code>luaL_addsize</code></a>, <a href="#luaL_pushresult"><code>luaL_pushresult</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_ref"><code>luaL_ref</code></a></h3><p>
+<span class="apii">[-1, +0, <em>m</em>]</span>
+<pre>int luaL_ref (lua_State *L, int t);</pre>
+
+<p>
+Creates and returns a <em>reference</em>,
+in the table at index <code>t</code>,
+for the object on the top of the stack (and pops the object).
+
+
+<p>
+A reference is a unique integer key.
+As long as you do not manually add integer keys into the table <code>t</code>,
+<a href="#luaL_ref"><code>luaL_ref</code></a> ensures the uniqueness of the key it returns.
+You can retrieve an object referred by the reference <code>r</code>
+by calling <code>lua_rawgeti(L, t, r)</code>.
+The function <a href="#luaL_unref"><code>luaL_unref</code></a> frees a reference.
+
+
+<p>
+If the object on the top of the stack is <b>nil</b>,
+<a href="#luaL_ref"><code>luaL_ref</code></a> returns the constant <a name="pdf-LUA_REFNIL"><code>LUA_REFNIL</code></a>.
+The constant <a name="pdf-LUA_NOREF"><code>LUA_NOREF</code></a> is guaranteed to be different
+from any reference returned by <a href="#luaL_ref"><code>luaL_ref</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_Reg"><code>luaL_Reg</code></a></h3>
+<pre>typedef struct luaL_Reg {
+ const char *name;
+ lua_CFunction func;
+} luaL_Reg;</pre>
+
+<p>
+Type for arrays of functions to be registered by
+<a href="#luaL_setfuncs"><code>luaL_setfuncs</code></a>.
+<code>name</code> is the function name and <code>func</code> is a pointer to
+the function.
+Any array of <a href="#luaL_Reg"><code>luaL_Reg</code></a> must end with a sentinel entry
+in which both <code>name</code> and <code>func</code> are <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_requiref"><code>luaL_requiref</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>void luaL_requiref (lua_State *L, const char *modname,
+ lua_CFunction openf, int glb);</pre>
+
+<p>
+If <code>package.loaded[modname]</code> is not true,
+calls the function <code>openf</code> with the string <code>modname</code> as an argument
+and sets the call result to <code>package.loaded[modname]</code>,
+as if that function has been called through <a href="#pdf-require"><code>require</code></a>.
+
+
+<p>
+If <code>glb</code> is true,
+also stores the module into the global <code>modname</code>.
+
+
+<p>
+Leaves a copy of the module on the stack.
+
+
+
+
+
+<hr><h3><a name="luaL_setfuncs"><code>luaL_setfuncs</code></a></h3><p>
+<span class="apii">[-nup, +0, <em>m</em>]</span>
+<pre>void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);</pre>
+
+<p>
+Registers all functions in the array <code>l</code>
+(see <a href="#luaL_Reg"><code>luaL_Reg</code></a>) into the table on the top of the stack
+(below optional upvalues, see next).
+
+
+<p>
+When <code>nup</code> is not zero,
+all functions are created with <code>nup</code> upvalues,
+initialized with copies of the <code>nup</code> values
+previously pushed on the stack
+on top of the library table.
+These values are popped from the stack after the registration.
+
+
+<p>
+A function with a <code>NULL</code> value represents a placeholder,
+which is filled with <b>false</b>.
+
+
+
+
+
+<hr><h3><a name="luaL_setmetatable"><code>luaL_setmetatable</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void luaL_setmetatable (lua_State *L, const char *tname);</pre>
+
+<p>
+Sets the metatable of the object on the top of the stack
+as the metatable associated with name <code>tname</code>
+in the registry (see <a href="#luaL_newmetatable"><code>luaL_newmetatable</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_Stream"><code>luaL_Stream</code></a></h3>
+<pre>typedef struct luaL_Stream {
+ FILE *f;
+ lua_CFunction closef;
+} luaL_Stream;</pre>
+
+<p>
+The standard representation for file handles
+used by the standard I/O library.
+
+
+<p>
+A file handle is implemented as a full userdata,
+with a metatable called <code>LUA_FILEHANDLE</code>
+(where <code>LUA_FILEHANDLE</code> is a macro with the actual metatable's name).
+The metatable is created by the I/O library
+(see <a href="#luaL_newmetatable"><code>luaL_newmetatable</code></a>).
+
+
+<p>
+This userdata must start with the structure <code>luaL_Stream</code>;
+it can contain other data after this initial structure.
+The field <code>f</code> points to the corresponding C stream
+(or it can be <code>NULL</code> to indicate an incompletely created handle).
+The field <code>closef</code> points to a Lua function
+that will be called to close the stream
+when the handle is closed or collected;
+this function receives the file handle as its sole argument and
+must return either a true value, in case of success,
+or a false value plus an error message, in case of error.
+Once Lua calls this field,
+it changes the field value to <code>NULL</code>
+to signal that the handle is closed.
+
+
+
+
+
+<hr><h3><a name="luaL_testudata"><code>luaL_testudata</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>void *luaL_testudata (lua_State *L, int arg, const char *tname);</pre>
+
+<p>
+This function works like <a href="#luaL_checkudata"><code>luaL_checkudata</code></a>,
+except that, when the test fails,
+it returns <code>NULL</code> instead of raising an error.
+
+
+
+
+
+<hr><h3><a name="luaL_tolstring"><code>luaL_tolstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>const char *luaL_tolstring (lua_State *L, int idx, size_t *len);</pre>
+
+<p>
+Converts any Lua value at the given index to a C&nbsp;string
+in a reasonable format.
+The resulting string is pushed onto the stack and also
+returned by the function (see <a href="#4.1.3">&sect;4.1.3</a>).
+If <code>len</code> is not <code>NULL</code>,
+the function also sets <code>*len</code> with the string length.
+
+
+<p>
+If the value has a metatable with a <code>__tostring</code> field,
+then <code>luaL_tolstring</code> calls the corresponding metamethod
+with the value as argument,
+and uses the result of the call as its result.
+
+
+
+
+
+<hr><h3><a name="luaL_traceback"><code>luaL_traceback</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void luaL_traceback (lua_State *L, lua_State *L1, const char *msg,
+ int level);</pre>
+
+<p>
+Creates and pushes a traceback of the stack <code>L1</code>.
+If <code>msg</code> is not <code>NULL</code>, it is appended
+at the beginning of the traceback.
+The <code>level</code> parameter tells at which level
+to start the traceback.
+
+
+
+
+
+<hr><h3><a name="luaL_typeerror"><code>luaL_typeerror</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_typeerror (lua_State *L, int arg, const char *tname);</pre>
+
+<p>
+Raises a type error for the argument <code>arg</code>
+of the C&nbsp;function that called it,
+using a standard message;
+<code>tname</code> is a "name" for the expected type.
+This function never returns.
+
+
+
+
+
+<hr><h3><a name="luaL_typename"><code>luaL_typename</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>const char *luaL_typename (lua_State *L, int index);</pre>
+
+<p>
+Returns the name of the type of the value at the given index.
+
+
+
+
+
+<hr><h3><a name="luaL_unref"><code>luaL_unref</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void luaL_unref (lua_State *L, int t, int ref);</pre>
+
+<p>
+Releases the reference <code>ref</code> from the table at index <code>t</code>
+(see <a href="#luaL_ref"><code>luaL_ref</code></a>).
+The entry is removed from the table,
+so that the referred object can be collected.
+The reference <code>ref</code> is also freed to be used again.
+
+
+<p>
+If <code>ref</code> is <a href="#pdf-LUA_NOREF"><code>LUA_NOREF</code></a> or <a href="#pdf-LUA_REFNIL"><code>LUA_REFNIL</code></a>,
+<a href="#luaL_unref"><code>luaL_unref</code></a> does nothing.
+
+
+
+
+
+<hr><h3><a name="luaL_where"><code>luaL_where</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void luaL_where (lua_State *L, int lvl);</pre>
+
+<p>
+Pushes onto the stack a string identifying the current position
+of the control at level <code>lvl</code> in the call stack.
+Typically this string has the following format:
+
+<pre>
+ <em>chunkname</em>:<em>currentline</em>:
+</pre><p>
+Level&nbsp;0 is the running function,
+level&nbsp;1 is the function that called the running function,
+etc.
+
+
+<p>
+This function is used to build a prefix for error messages.
+
+
+
+
+
+
+
+<h1>6 &ndash; <a name="6">The Standard Libraries</a></h1>
+
+
+
+<p>
+The standard Lua libraries provide useful functions
+that are implemented in&nbsp;C through the C&nbsp;API.
+Some of these functions provide essential services to the language
+(e.g., <a href="#pdf-type"><code>type</code></a> and <a href="#pdf-getmetatable"><code>getmetatable</code></a>);
+others provide access to outside services (e.g., I/O);
+and others could be implemented in Lua itself,
+but that for different reasons
+deserve an implementation in C (e.g., <a href="#pdf-table.sort"><code>table.sort</code></a>).
+
+
+<p>
+All libraries are implemented through the official C&nbsp;API
+and are provided as separate C&nbsp;modules.
+Unless otherwise noted,
+these library functions do not adjust its number of arguments
+to its expected parameters.
+For instance, a function documented as <code>foo(arg)</code>
+should not be called without an argument.
+
+
+<p>
+The notation <b>fail</b> means a false value representing
+some kind of failure.
+(Currently, <b>fail</b> is equal to <b>nil</b>,
+but that may change in future versions.
+The recommendation is to always test the success of these functions
+with <code>(not status)</code>, instead of <code>(status == nil)</code>.)
+
+
+<p>
+Currently, Lua has the following standard libraries:
+
+<ul>
+
+<li>basic library (<a href="#6.1">&sect;6.1</a>);</li>
+
+<li>coroutine library (<a href="#6.2">&sect;6.2</a>);</li>
+
+<li>package library (<a href="#6.3">&sect;6.3</a>);</li>
+
+<li>string manipulation (<a href="#6.4">&sect;6.4</a>);</li>
+
+<li>basic UTF-8 support (<a href="#6.5">&sect;6.5</a>);</li>
+
+<li>table manipulation (<a href="#6.6">&sect;6.6</a>);</li>
+
+<li>mathematical functions (<a href="#6.7">&sect;6.7</a>) (sin, log, etc.);</li>
+
+<li>input and output (<a href="#6.8">&sect;6.8</a>);</li>
+
+<li>operating system facilities (<a href="#6.9">&sect;6.9</a>);</li>
+
+<li>debug facilities (<a href="#6.10">&sect;6.10</a>).</li>
+
+</ul><p>
+Except for the basic and the package libraries,
+each library provides all its functions as fields of a global table
+or as methods of its objects.
+
+
+<p>
+To have access to these libraries,
+the C&nbsp;host program should call the <a href="#luaL_openlibs"><code>luaL_openlibs</code></a> function,
+which opens all standard libraries.
+Alternatively,
+the host program can open them individually by using
+<a href="#luaL_requiref"><code>luaL_requiref</code></a> to call
+<a name="pdf-luaopen_base"><code>luaopen_base</code></a> (for the basic library),
+<a name="pdf-luaopen_package"><code>luaopen_package</code></a> (for the package library),
+<a name="pdf-luaopen_coroutine"><code>luaopen_coroutine</code></a> (for the coroutine library),
+<a name="pdf-luaopen_string"><code>luaopen_string</code></a> (for the string library),
+<a name="pdf-luaopen_utf8"><code>luaopen_utf8</code></a> (for the UTF-8 library),
+<a name="pdf-luaopen_table"><code>luaopen_table</code></a> (for the table library),
+<a name="pdf-luaopen_math"><code>luaopen_math</code></a> (for the mathematical library),
+<a name="pdf-luaopen_io"><code>luaopen_io</code></a> (for the I/O library),
+<a name="pdf-luaopen_os"><code>luaopen_os</code></a> (for the operating system library),
+and <a name="pdf-luaopen_debug"><code>luaopen_debug</code></a> (for the debug library).
+These functions are declared in <a name="pdf-lualib.h"><code>lualib.h</code></a>.
+
+
+
+
+
+<h2>6.1 &ndash; <a name="6.1">Basic Functions</a></h2>
+
+<p>
+The basic library provides core functions to Lua.
+If you do not include this library in your application,
+you should check carefully whether you need to provide
+implementations for some of its facilities.
+
+
+<p>
+<hr><h3><a name="pdf-assert"><code>assert (v [, message])</code></a></h3>
+
+
+<p>
+Raises an error if
+the value of its argument <code>v</code> is false (i.e., <b>nil</b> or <b>false</b>);
+otherwise, returns all its arguments.
+In case of error,
+<code>message</code> is the error object;
+when absent, it defaults to "<code>assertion failed!</code>"
+
+
+
+
+<p>
+<hr><h3><a name="pdf-collectgarbage"><code>collectgarbage ([opt [, arg]])</code></a></h3>
+
+
+<p>
+This function is a generic interface to the garbage collector.
+It performs different functions according to its first argument, <code>opt</code>:
+
+<ul>
+
+<li><b>"<code>collect</code>": </b>
+Performs a full garbage-collection cycle.
+This is the default option.
+</li>
+
+<li><b>"<code>stop</code>": </b>
+Stops automatic execution of the garbage collector.
+The collector will run only when explicitly invoked,
+until a call to restart it.
+</li>
+
+<li><b>"<code>restart</code>": </b>
+Restarts automatic execution of the garbage collector.
+</li>
+
+<li><b>"<code>count</code>": </b>
+Returns the total memory in use by Lua in Kbytes.
+The value has a fractional part,
+so that it multiplied by 1024
+gives the exact number of bytes in use by Lua.
+</li>
+
+<li><b>"<code>step</code>": </b>
+Performs a garbage-collection step.
+The step "size" is controlled by <code>arg</code>.
+With a zero value,
+the collector will perform one basic (indivisible) step.
+For non-zero values,
+the collector will perform as if that amount of memory
+(in Kbytes) had been allocated by Lua.
+Returns <b>true</b> if the step finished a collection cycle.
+</li>
+
+<li><b>"<code>isrunning</code>": </b>
+Returns a boolean that tells whether the collector is running
+(i.e., not stopped).
+</li>
+
+<li><b>"<code>incremental</code>": </b>
+Change the collector mode to incremental.
+This option can be followed by three numbers:
+the garbage-collector pause,
+the step multiplier,
+and the step size (see <a href="#2.5.1">&sect;2.5.1</a>).
+A zero means to not change that value.
+</li>
+
+<li><b>"<code>generational</code>": </b>
+Change the collector mode to generational.
+This option can be followed by two numbers:
+the garbage-collector minor multiplier
+and the major multiplier (see <a href="#2.5.2">&sect;2.5.2</a>).
+A zero means to not change that value.
+</li>
+
+</ul><p>
+See <a href="#2.5">&sect;2.5</a> for more details about garbage collection
+and some of these options.
+
+
+<p>
+This function should not be called by a finalizer.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-dofile"><code>dofile ([filename])</code></a></h3>
+Opens the named file and executes its content as a Lua chunk.
+When called without arguments,
+<code>dofile</code> executes the content of the standard input (<code>stdin</code>).
+Returns all values returned by the chunk.
+In case of errors, <code>dofile</code> propagates the error
+to its caller.
+(That is, <code>dofile</code> does not run in protected mode.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-error"><code>error (message [, level])</code></a></h3>
+Raises an error (see <a href="#2.3">&sect;2.3</a>) with <code>message</code> as the error object.
+This function never returns.
+
+
+<p>
+Usually, <code>error</code> adds some information about the error position
+at the beginning of the message, if the message is a string.
+The <code>level</code> argument specifies how to get the error position.
+With level&nbsp;1 (the default), the error position is where the
+<code>error</code> function was called.
+Level&nbsp;2 points the error to where the function
+that called <code>error</code> was called; and so on.
+Passing a level&nbsp;0 avoids the addition of error position information
+to the message.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-_G"><code>_G</code></a></h3>
+A global variable (not a function) that
+holds the global environment (see <a href="#2.2">&sect;2.2</a>).
+Lua itself does not use this variable;
+changing its value does not affect any environment,
+nor vice versa.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-getmetatable"><code>getmetatable (object)</code></a></h3>
+
+
+<p>
+If <code>object</code> does not have a metatable, returns <b>nil</b>.
+Otherwise,
+if the object's metatable has a <code>__metatable</code> field,
+returns the associated value.
+Otherwise, returns the metatable of the given object.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-ipairs"><code>ipairs (t)</code></a></h3>
+
+
+<p>
+Returns three values (an iterator function, the table <code>t</code>, and 0)
+so that the construction
+
+<pre>
+ for i,v in ipairs(t) do <em>body</em> end
+</pre><p>
+will iterate over the key&ndash;value pairs
+(<code>1,t[1]</code>), (<code>2,t[2]</code>), ...,
+up to the first absent index.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-load"><code>load (chunk [, chunkname [, mode [, env]]])</code></a></h3>
+
+
+<p>
+Loads a chunk.
+
+
+<p>
+If <code>chunk</code> is a string, the chunk is this string.
+If <code>chunk</code> is a function,
+<code>load</code> calls it repeatedly to get the chunk pieces.
+Each call to <code>chunk</code> must return a string that concatenates
+with previous results.
+A return of an empty string, <b>nil</b>, or no value signals the end of the chunk.
+
+
+<p>
+If there are no syntactic errors,
+<code>load</code> returns the compiled chunk as a function;
+otherwise, it returns <b>fail</b> plus the error message.
+
+
+<p>
+When you load a main chunk,
+the resulting function will always have exactly one upvalue,
+the <code>_ENV</code> variable (see <a href="#2.2">&sect;2.2</a>).
+However,
+when you load a binary chunk created from a function (see <a href="#pdf-string.dump"><code>string.dump</code></a>),
+the resulting function can have an arbitrary number of upvalues,
+and there is no guarantee that its first upvalue will be
+the <code>_ENV</code> variable.
+(A non-main function may not even have an <code>_ENV</code> upvalue.)
+
+
+<p>
+Regardless, if the resulting function has any upvalues,
+its first upvalue is set to the value of <code>env</code>,
+if that parameter is given,
+or to the value of the global environment.
+Other upvalues are initialized with <b>nil</b>.
+All upvalues are fresh, that is,
+they are not shared with any other function.
+
+
+<p>
+<code>chunkname</code> is used as the name of the chunk for error messages
+and debug information (see <a href="#4.7">&sect;4.7</a>).
+When absent,
+it defaults to <code>chunk</code>, if <code>chunk</code> is a string,
+or to "<code>=(load)</code>" otherwise.
+
+
+<p>
+The string <code>mode</code> controls whether the chunk can be text or binary
+(that is, a precompiled chunk).
+It may be the string "<code>b</code>" (only binary chunks),
+"<code>t</code>" (only text chunks),
+or "<code>bt</code>" (both binary and text).
+The default is "<code>bt</code>".
+
+
+<p>
+It is safe to load malformed binary chunks;
+<code>load</code> signals an appropriate error.
+However,
+Lua does not check the consistency of the code inside binary chunks;
+running maliciously crafted bytecode can crash the interpreter.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-loadfile"><code>loadfile ([filename [, mode [, env]]])</code></a></h3>
+
+
+<p>
+Similar to <a href="#pdf-load"><code>load</code></a>,
+but gets the chunk from file <code>filename</code>
+or from the standard input,
+if no file name is given.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-next"><code>next (table [, index])</code></a></h3>
+
+
+<p>
+Allows a program to traverse all fields of a table.
+Its first argument is a table and its second argument
+is an index in this table.
+A call to <code>next</code> returns the next index of the table
+and its associated value.
+When called with <b>nil</b> as its second argument,
+<code>next</code> returns an initial index
+and its associated value.
+When called with the last index,
+or with <b>nil</b> in an empty table,
+<code>next</code> returns <b>nil</b>.
+If the second argument is absent, then it is interpreted as <b>nil</b>.
+In particular,
+you can use <code>next(t)</code> to check whether a table is empty.
+
+
+<p>
+The order in which the indices are enumerated is not specified,
+<em>even for numeric indices</em>.
+(To traverse a table in numerical order,
+use a numerical <b>for</b>.)
+
+
+<p>
+You should not assign any value to a non-existent field in a table
+during its traversal.
+You may however modify existing fields.
+In particular, you may set existing fields to nil.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-pairs"><code>pairs (t)</code></a></h3>
+
+
+<p>
+If <code>t</code> has a metamethod <code>__pairs</code>,
+calls it with <code>t</code> as argument and returns the first three
+results from the call.
+
+
+<p>
+Otherwise,
+returns three values: the <a href="#pdf-next"><code>next</code></a> function, the table <code>t</code>, and <b>nil</b>,
+so that the construction
+
+<pre>
+ for k,v in pairs(t) do <em>body</em> end
+</pre><p>
+will iterate over all key&ndash;value pairs of table <code>t</code>.
+
+
+<p>
+See function <a href="#pdf-next"><code>next</code></a> for the caveats of modifying
+the table during its traversal.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-pcall"><code>pcall (f [, arg1, &middot;&middot;&middot;])</code></a></h3>
+
+
+<p>
+Calls the function <code>f</code> with
+the given arguments in <em>protected mode</em>.
+This means that any error inside&nbsp;<code>f</code> is not propagated;
+instead, <code>pcall</code> catches the error
+and returns a status code.
+Its first result is the status code (a boolean),
+which is <b>true</b> if the call succeeds without errors.
+In such case, <code>pcall</code> also returns all results from the call,
+after this first result.
+In case of any error, <code>pcall</code> returns <b>false</b> plus the error object.
+Note that errors caught by <code>pcall</code> do not call a message handler.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-print"><code>print (&middot;&middot;&middot;)</code></a></h3>
+Receives any number of arguments
+and prints their values to <code>stdout</code>,
+converting each argument to a string
+following the same rules of <a href="#pdf-tostring"><code>tostring</code></a>.
+
+
+<p>
+The function <code>print</code> is not intended for formatted output,
+but only as a quick way to show a value,
+for instance for debugging.
+For complete control over the output,
+use <a href="#pdf-string.format"><code>string.format</code></a> and <a href="#pdf-io.write"><code>io.write</code></a>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-rawequal"><code>rawequal (v1, v2)</code></a></h3>
+Checks whether <code>v1</code> is equal to <code>v2</code>,
+without invoking the <code>__eq</code> metamethod.
+Returns a boolean.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-rawget"><code>rawget (table, index)</code></a></h3>
+Gets the real value of <code>table[index]</code>,
+without using the <code>__index</code> metavalue.
+<code>table</code> must be a table;
+<code>index</code> may be any value.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-rawlen"><code>rawlen (v)</code></a></h3>
+Returns the length of the object <code>v</code>,
+which must be a table or a string,
+without invoking the <code>__len</code> metamethod.
+Returns an integer.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-rawset"><code>rawset (table, index, value)</code></a></h3>
+Sets the real value of <code>table[index]</code> to <code>value</code>,
+without using the <code>__newindex</code> metavalue.
+<code>table</code> must be a table,
+<code>index</code> any value different from <b>nil</b> and NaN,
+and <code>value</code> any Lua value.
+
+
+<p>
+This function returns <code>table</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-select"><code>select (index, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+If <code>index</code> is a number,
+returns all arguments after argument number <code>index</code>;
+a negative number indexes from the end (-1 is the last argument).
+Otherwise, <code>index</code> must be the string <code>"#"</code>,
+and <code>select</code> returns the total number of extra arguments it received.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-setmetatable"><code>setmetatable (table, metatable)</code></a></h3>
+
+
+<p>
+Sets the metatable for the given table.
+If <code>metatable</code> is <b>nil</b>,
+removes the metatable of the given table.
+If the original metatable has a <code>__metatable</code> field,
+raises an error.
+
+
+<p>
+This function returns <code>table</code>.
+
+
+<p>
+To change the metatable of other types from Lua code,
+you must use the debug library (<a href="#6.10">&sect;6.10</a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-tonumber"><code>tonumber (e [, base])</code></a></h3>
+
+
+<p>
+When called with no <code>base</code>,
+<code>tonumber</code> tries to convert its argument to a number.
+If the argument is already a number or
+a string convertible to a number,
+then <code>tonumber</code> returns this number;
+otherwise, it returns <b>fail</b>.
+
+
+<p>
+The conversion of strings can result in integers or floats,
+according to the lexical conventions of Lua (see <a href="#3.1">&sect;3.1</a>).
+The string may have leading and trailing spaces and a sign.
+
+
+<p>
+When called with <code>base</code>,
+then <code>e</code> must be a string to be interpreted as
+an integer numeral in that base.
+The base may be any integer between 2 and 36, inclusive.
+In bases above&nbsp;10, the letter '<code>A</code>' (in either upper or lower case)
+represents&nbsp;10, '<code>B</code>' represents&nbsp;11, and so forth,
+with '<code>Z</code>' representing 35.
+If the string <code>e</code> is not a valid numeral in the given base,
+the function returns <b>fail</b>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-tostring"><code>tostring (v)</code></a></h3>
+
+
+<p>
+Receives a value of any type and
+converts it to a string in a human-readable format.
+
+
+<p>
+If the metatable of <code>v</code> has a <code>__tostring</code> field,
+then <code>tostring</code> calls the corresponding value
+with <code>v</code> as argument,
+and uses the result of the call as its result.
+Otherwise, if the metatable of <code>v</code> has a <code>__name</code> field
+with a string value,
+<code>tostring</code> may use that string in its final result.
+
+
+<p>
+For complete control of how numbers are converted,
+use <a href="#pdf-string.format"><code>string.format</code></a>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-type"><code>type (v)</code></a></h3>
+
+
+<p>
+Returns the type of its only argument, coded as a string.
+The possible results of this function are
+"<code>nil</code>" (a string, not the value <b>nil</b>),
+"<code>number</code>",
+"<code>string</code>",
+"<code>boolean</code>",
+"<code>table</code>",
+"<code>function</code>",
+"<code>thread</code>",
+and "<code>userdata</code>".
+
+
+
+
+<p>
+<hr><h3><a name="pdf-_VERSION"><code>_VERSION</code></a></h3>
+
+
+<p>
+A global variable (not a function) that
+holds a string containing the running Lua version.
+The current value of this variable is "<code>Lua 5.4</code>".
+
+
+
+
+<p>
+<hr><h3><a name="pdf-warn"><code>warn (msg1, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Emits a warning with a message composed by the concatenation
+of all its arguments (which should be strings).
+
+
+<p>
+By convention,
+a one-piece message starting with '<code>@</code>'
+is intended to be a <em>control message</em>,
+which is a message to the warning system itself.
+In particular, the standard warning function in Lua
+recognizes the control messages "<code>@off</code>",
+to stop the emission of warnings,
+and "<code>@on</code>", to (re)start the emission;
+it ignores unknown control messages.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-xpcall"><code>xpcall (f, msgh [, arg1, &middot;&middot;&middot;])</code></a></h3>
+
+
+<p>
+This function is similar to <a href="#pdf-pcall"><code>pcall</code></a>,
+except that it sets a new message handler <code>msgh</code>.
+
+
+
+
+
+
+
+<h2>6.2 &ndash; <a name="6.2">Coroutine Manipulation</a></h2>
+
+<p>
+This library comprises the operations to manipulate coroutines,
+which come inside the table <a name="pdf-coroutine"><code>coroutine</code></a>.
+See <a href="#2.6">&sect;2.6</a> for a general description of coroutines.
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.close"><code>coroutine.close (co)</code></a></h3>
+
+
+<p>
+Closes coroutine <code>co</code>,
+that is,
+closes all its pending to-be-closed variables
+and puts the coroutine in a dead state.
+The given coroutine must be dead or suspended.
+In case of error
+(either the original error that stopped the coroutine or
+errors in closing methods),
+returns <b>false</b> plus the error object;
+otherwise returns <b>true</b>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.create"><code>coroutine.create (f)</code></a></h3>
+
+
+<p>
+Creates a new coroutine, with body <code>f</code>.
+<code>f</code> must be a function.
+Returns this new coroutine,
+an object with type <code>"thread"</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.isyieldable"><code>coroutine.isyieldable ([co])</code></a></h3>
+
+
+<p>
+Returns <b>true</b> when the coroutine <code>co</code> can yield.
+The default for <code>co</code> is the running coroutine.
+
+
+<p>
+A coroutine is yieldable if it is not the main thread and
+it is not inside a non-yieldable C&nbsp;function.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.resume"><code>coroutine.resume (co [, val1, &middot;&middot;&middot;])</code></a></h3>
+
+
+<p>
+Starts or continues the execution of coroutine <code>co</code>.
+The first time you resume a coroutine,
+it starts running its body.
+The values <code>val1</code>, ... are passed
+as the arguments to the body function.
+If the coroutine has yielded,
+<code>resume</code> restarts it;
+the values <code>val1</code>, ... are passed
+as the results from the yield.
+
+
+<p>
+If the coroutine runs without any errors,
+<code>resume</code> returns <b>true</b> plus any values passed to <code>yield</code>
+(when the coroutine yields) or any values returned by the body function
+(when the coroutine terminates).
+If there is any error,
+<code>resume</code> returns <b>false</b> plus the error message.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.running"><code>coroutine.running ()</code></a></h3>
+
+
+<p>
+Returns the running coroutine plus a boolean,
+<b>true</b> when the running coroutine is the main one.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.status"><code>coroutine.status (co)</code></a></h3>
+
+
+<p>
+Returns the status of the coroutine <code>co</code>, as a string:
+<code>"running"</code>,
+if the coroutine is running
+(that is, it is the one that called <code>status</code>);
+<code>"suspended"</code>, if the coroutine is suspended in a call to <code>yield</code>,
+or if it has not started running yet;
+<code>"normal"</code> if the coroutine is active but not running
+(that is, it has resumed another coroutine);
+and <code>"dead"</code> if the coroutine has finished its body function,
+or if it has stopped with an error.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.wrap"><code>coroutine.wrap (f)</code></a></h3>
+
+
+<p>
+Creates a new coroutine, with body <code>f</code>;
+<code>f</code> must be a function.
+Returns a function that resumes the coroutine each time it is called.
+Any arguments passed to this function behave as the
+extra arguments to <code>resume</code>.
+The function returns the same values returned by <code>resume</code>,
+except the first boolean.
+In case of error,
+the function closes the coroutine and propagates the error.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.yield"><code>coroutine.yield (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Suspends the execution of the calling coroutine.
+Any arguments to <code>yield</code> are passed as extra results to <code>resume</code>.
+
+
+
+
+
+
+
+<h2>6.3 &ndash; <a name="6.3">Modules</a></h2>
+
+<p>
+The package library provides basic
+facilities for loading modules in Lua.
+It exports one function directly in the global environment:
+<a href="#pdf-require"><code>require</code></a>.
+Everything else is exported in the table <a name="pdf-package"><code>package</code></a>.
+
+
+<p>
+<hr><h3><a name="pdf-require"><code>require (modname)</code></a></h3>
+
+
+<p>
+Loads the given module.
+The function starts by looking into the <a href="#pdf-package.loaded"><code>package.loaded</code></a> table
+to determine whether <code>modname</code> is already loaded.
+If it is, then <code>require</code> returns the value stored
+at <code>package.loaded[modname]</code>.
+(The absence of a second result in this case
+signals that this call did not have to load the module.)
+Otherwise, it tries to find a <em>loader</em> for the module.
+
+
+<p>
+To find a loader,
+<code>require</code> is guided by the table <a href="#pdf-package.searchers"><code>package.searchers</code></a>.
+Each item in this table is a search function,
+that searches for the module in a particular way.
+By changing this table,
+we can change how <code>require</code> looks for a module.
+The following explanation is based on the default configuration
+for <a href="#pdf-package.searchers"><code>package.searchers</code></a>.
+
+
+<p>
+First <code>require</code> queries <code>package.preload[modname]</code>.
+If it has a value,
+this value (which must be a function) is the loader.
+Otherwise <code>require</code> searches for a Lua loader using the
+path stored in <a href="#pdf-package.path"><code>package.path</code></a>.
+If that also fails, it searches for a C&nbsp;loader using the
+path stored in <a href="#pdf-package.cpath"><code>package.cpath</code></a>.
+If that also fails,
+it tries an <em>all-in-one</em> loader (see <a href="#pdf-package.searchers"><code>package.searchers</code></a>).
+
+
+<p>
+Once a loader is found,
+<code>require</code> calls the loader with two arguments:
+<code>modname</code> and an extra value,
+a <em>loader data</em>,
+also returned by the searcher.
+The loader data can be any value useful to the module;
+for the default searchers,
+it indicates where the loader was found.
+(For instance, if the loader came from a file,
+this extra value is the file path.)
+If the loader returns any non-nil value,
+<code>require</code> assigns the returned value to <code>package.loaded[modname]</code>.
+If the loader does not return a non-nil value and
+has not assigned any value to <code>package.loaded[modname]</code>,
+then <code>require</code> assigns <b>true</b> to this entry.
+In any case, <code>require</code> returns the
+final value of <code>package.loaded[modname]</code>.
+Besides that value, <code>require</code> also returns as a second result
+the loader data returned by the searcher,
+which indicates how <code>require</code> found the module.
+
+
+<p>
+If there is any error loading or running the module,
+or if it cannot find any loader for the module,
+then <code>require</code> raises an error.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.config"><code>package.config</code></a></h3>
+
+
+<p>
+A string describing some compile-time configurations for packages.
+This string is a sequence of lines:
+
+<ul>
+
+<li>The first line is the directory separator string.
+Default is '<code>\</code>' for Windows and '<code>/</code>' for all other systems.</li>
+
+<li>The second line is the character that separates templates in a path.
+Default is '<code>;</code>'.</li>
+
+<li>The third line is the string that marks the
+substitution points in a template.
+Default is '<code>?</code>'.</li>
+
+<li>The fourth line is a string that, in a path in Windows,
+is replaced by the executable's directory.
+Default is '<code>!</code>'.</li>
+
+<li>The fifth line is a mark to ignore all text after it
+when building the <code>luaopen_</code> function name.
+Default is '<code>-</code>'.</li>
+
+</ul>
+
+
+
+<p>
+<hr><h3><a name="pdf-package.cpath"><code>package.cpath</code></a></h3>
+
+
+<p>
+A string with the path used by <a href="#pdf-require"><code>require</code></a>
+to search for a C&nbsp;loader.
+
+
+<p>
+Lua initializes the C&nbsp;path <a href="#pdf-package.cpath"><code>package.cpath</code></a> in the same way
+it initializes the Lua path <a href="#pdf-package.path"><code>package.path</code></a>,
+using the environment variable <a name="pdf-LUA_CPATH_5_4"><code>LUA_CPATH_5_4</code></a>,
+or the environment variable <a name="pdf-LUA_CPATH"><code>LUA_CPATH</code></a>,
+or a default path defined in <code>luaconf.h</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.loaded"><code>package.loaded</code></a></h3>
+
+
+<p>
+A table used by <a href="#pdf-require"><code>require</code></a> to control which
+modules are already loaded.
+When you require a module <code>modname</code> and
+<code>package.loaded[modname]</code> is not false,
+<a href="#pdf-require"><code>require</code></a> simply returns the value stored there.
+
+
+<p>
+This variable is only a reference to the real table;
+assignments to this variable do not change the
+table used by <a href="#pdf-require"><code>require</code></a>.
+The real table is stored in the C registry (see <a href="#4.3">&sect;4.3</a>),
+indexed by the key <a name="pdf-LUA_LOADED_TABLE"><code>LUA_LOADED_TABLE</code></a>, a string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.loadlib"><code>package.loadlib (libname, funcname)</code></a></h3>
+
+
+<p>
+Dynamically links the host program with the C&nbsp;library <code>libname</code>.
+
+
+<p>
+If <code>funcname</code> is "<code>*</code>",
+then it only links with the library,
+making the symbols exported by the library
+available to other dynamically linked libraries.
+Otherwise,
+it looks for a function <code>funcname</code> inside the library
+and returns this function as a C&nbsp;function.
+So, <code>funcname</code> must follow the <a href="#lua_CFunction"><code>lua_CFunction</code></a> prototype
+(see <a href="#lua_CFunction"><code>lua_CFunction</code></a>).
+
+
+<p>
+This is a low-level function.
+It completely bypasses the package and module system.
+Unlike <a href="#pdf-require"><code>require</code></a>,
+it does not perform any path searching and
+does not automatically adds extensions.
+<code>libname</code> must be the complete file name of the C&nbsp;library,
+including if necessary a path and an extension.
+<code>funcname</code> must be the exact name exported by the C&nbsp;library
+(which may depend on the C&nbsp;compiler and linker used).
+
+
+<p>
+This functionality is not supported by ISO&nbsp;C.
+As such, it is only available on some platforms
+(Windows, Linux, Mac OS X, Solaris, BSD,
+plus other Unix systems that support the <code>dlfcn</code> standard).
+
+
+<p>
+This function is inherently insecure,
+as it allows Lua to call any function in any readable dynamic
+library in the system.
+(Lua calls any function assuming the function
+has a proper prototype and respects a proper protocol
+(see <a href="#lua_CFunction"><code>lua_CFunction</code></a>).
+Therefore,
+calling an arbitrary function in an arbitrary dynamic library
+more often than not results in an access violation.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.path"><code>package.path</code></a></h3>
+
+
+<p>
+A string with the path used by <a href="#pdf-require"><code>require</code></a>
+to search for a Lua loader.
+
+
+<p>
+At start-up, Lua initializes this variable with
+the value of the environment variable <a name="pdf-LUA_PATH_5_4"><code>LUA_PATH_5_4</code></a> or
+the environment variable <a name="pdf-LUA_PATH"><code>LUA_PATH</code></a> or
+with a default path defined in <code>luaconf.h</code>,
+if those environment variables are not defined.
+A "<code>;;</code>" in the value of the environment variable
+is replaced by the default path.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.preload"><code>package.preload</code></a></h3>
+
+
+<p>
+A table to store loaders for specific modules
+(see <a href="#pdf-require"><code>require</code></a>).
+
+
+<p>
+This variable is only a reference to the real table;
+assignments to this variable do not change the
+table used by <a href="#pdf-require"><code>require</code></a>.
+The real table is stored in the C registry (see <a href="#4.3">&sect;4.3</a>),
+indexed by the key <a name="pdf-LUA_PRELOAD_TABLE"><code>LUA_PRELOAD_TABLE</code></a>, a string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.searchers"><code>package.searchers</code></a></h3>
+
+
+<p>
+A table used by <a href="#pdf-require"><code>require</code></a> to control how to find modules.
+
+
+<p>
+Each entry in this table is a <em>searcher function</em>.
+When looking for a module,
+<a href="#pdf-require"><code>require</code></a> calls each of these searchers in ascending order,
+with the module name (the argument given to <a href="#pdf-require"><code>require</code></a>) as its
+sole argument.
+If the searcher finds the module,
+it returns another function, the module <em>loader</em>,
+plus an extra value, a <em>loader data</em>,
+that will be passed to that loader and
+returned as a second result by <a href="#pdf-require"><code>require</code></a>.
+If it cannot find the module,
+it returns a string explaining why
+(or <b>nil</b> if it has nothing to say).
+
+
+<p>
+Lua initializes this table with four searcher functions.
+
+
+<p>
+The first searcher simply looks for a loader in the
+<a href="#pdf-package.preload"><code>package.preload</code></a> table.
+
+
+<p>
+The second searcher looks for a loader as a Lua library,
+using the path stored at <a href="#pdf-package.path"><code>package.path</code></a>.
+The search is done as described in function <a href="#pdf-package.searchpath"><code>package.searchpath</code></a>.
+
+
+<p>
+The third searcher looks for a loader as a C&nbsp;library,
+using the path given by the variable <a href="#pdf-package.cpath"><code>package.cpath</code></a>.
+Again,
+the search is done as described in function <a href="#pdf-package.searchpath"><code>package.searchpath</code></a>.
+For instance,
+if the C&nbsp;path is the string
+
+<pre>
+ "./?.so;./?.dll;/usr/local/?/init.so"
+</pre><p>
+the searcher for module <code>foo</code>
+will try to open the files <code>./foo.so</code>, <code>./foo.dll</code>,
+and <code>/usr/local/foo/init.so</code>, in that order.
+Once it finds a C&nbsp;library,
+this searcher first uses a dynamic link facility to link the
+application with the library.
+Then it tries to find a C&nbsp;function inside the library to
+be used as the loader.
+The name of this C&nbsp;function is the string "<code>luaopen_</code>"
+concatenated with a copy of the module name where each dot
+is replaced by an underscore.
+Moreover, if the module name has a hyphen,
+its suffix after (and including) the first hyphen is removed.
+For instance, if the module name is <code>a.b.c-v2.1</code>,
+the function name will be <code>luaopen_a_b_c</code>.
+
+
+<p>
+The fourth searcher tries an <em>all-in-one loader</em>.
+It searches the C&nbsp;path for a library for
+the root name of the given module.
+For instance, when requiring <code>a.b.c</code>,
+it will search for a C&nbsp;library for <code>a</code>.
+If found, it looks into it for an open function for
+the submodule;
+in our example, that would be <code>luaopen_a_b_c</code>.
+With this facility, a package can pack several C&nbsp;submodules
+into one single library,
+with each submodule keeping its original open function.
+
+
+<p>
+All searchers except the first one (preload) return as the extra value
+the file path where the module was found,
+as returned by <a href="#pdf-package.searchpath"><code>package.searchpath</code></a>.
+The first searcher always returns the string "<code>:preload:</code>".
+
+
+<p>
+Searchers should raise no errors and have no side effects in Lua.
+(They may have side effects in C,
+for instance by linking the application with a library.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.searchpath"><code>package.searchpath (name, path [, sep [, rep]])</code></a></h3>
+
+
+<p>
+Searches for the given <code>name</code> in the given <code>path</code>.
+
+
+<p>
+A path is a string containing a sequence of
+<em>templates</em> separated by semicolons.
+For each template,
+the function replaces each interrogation mark (if any)
+in the template with a copy of <code>name</code>
+wherein all occurrences of <code>sep</code>
+(a dot, by default)
+were replaced by <code>rep</code>
+(the system's directory separator, by default),
+and then tries to open the resulting file name.
+
+
+<p>
+For instance, if the path is the string
+
+<pre>
+ "./?.lua;./?.lc;/usr/local/?/init.lua"
+</pre><p>
+the search for the name <code>foo.a</code>
+will try to open the files
+<code>./foo/a.lua</code>, <code>./foo/a.lc</code>, and
+<code>/usr/local/foo/a/init.lua</code>, in that order.
+
+
+<p>
+Returns the resulting name of the first file that it can
+open in read mode (after closing the file),
+or <b>fail</b> plus an error message if none succeeds.
+(This error message lists all file names it tried to open.)
+
+
+
+
+
+
+
+<h2>6.4 &ndash; <a name="6.4">String Manipulation</a></h2>
+
+
+
+<p>
+This library provides generic functions for string manipulation,
+such as finding and extracting substrings, and pattern matching.
+When indexing a string in Lua, the first character is at position&nbsp;1
+(not at&nbsp;0, as in C).
+Indices are allowed to be negative and are interpreted as indexing backwards,
+from the end of the string.
+Thus, the last character is at position -1, and so on.
+
+
+<p>
+The string library provides all its functions inside the table
+<a name="pdf-string"><code>string</code></a>.
+It also sets a metatable for strings
+where the <code>__index</code> field points to the <code>string</code> table.
+Therefore, you can use the string functions in object-oriented style.
+For instance, <code>string.byte(s,i)</code>
+can be written as <code>s:byte(i)</code>.
+
+
+<p>
+The string library assumes one-byte character encodings.
+
+
+<p>
+<hr><h3><a name="pdf-string.byte"><code>string.byte (s [, i [, j]])</code></a></h3>
+Returns the internal numeric codes of the characters <code>s[i]</code>,
+<code>s[i+1]</code>, ..., <code>s[j]</code>.
+The default value for <code>i</code> is&nbsp;1;
+the default value for <code>j</code> is&nbsp;<code>i</code>.
+These indices are corrected
+following the same rules of function <a href="#pdf-string.sub"><code>string.sub</code></a>.
+
+
+<p>
+Numeric codes are not necessarily portable across platforms.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.char"><code>string.char (&middot;&middot;&middot;)</code></a></h3>
+Receives zero or more integers.
+Returns a string with length equal to the number of arguments,
+in which each character has the internal numeric code equal
+to its corresponding argument.
+
+
+<p>
+Numeric codes are not necessarily portable across platforms.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.dump"><code>string.dump (function [, strip])</code></a></h3>
+
+
+<p>
+Returns a string containing a binary representation
+(a <em>binary chunk</em>)
+of the given function,
+so that a later <a href="#pdf-load"><code>load</code></a> on this string returns
+a copy of the function (but with new upvalues).
+If <code>strip</code> is a true value,
+the binary representation may not include all debug information
+about the function,
+to save space.
+
+
+<p>
+Functions with upvalues have only their number of upvalues saved.
+When (re)loaded,
+those upvalues receive fresh instances.
+(See the <a href="#pdf-load"><code>load</code></a> function for details about
+how these upvalues are initialized.
+You can use the debug library to serialize
+and reload the upvalues of a function
+in a way adequate to your needs.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.find"><code>string.find (s, pattern [, init [, plain]])</code></a></h3>
+
+
+<p>
+Looks for the first match of
+<code>pattern</code> (see <a href="#6.4.1">&sect;6.4.1</a>) in the string <code>s</code>.
+If it finds a match, then <code>find</code> returns the indices of&nbsp;<code>s</code>
+where this occurrence starts and ends;
+otherwise, it returns <b>fail</b>.
+A third, optional numeric argument <code>init</code> specifies
+where to start the search;
+its default value is&nbsp;1 and can be negative.
+A <b>true</b> as a fourth, optional argument <code>plain</code>
+turns off the pattern matching facilities,
+so the function does a plain "find substring" operation,
+with no characters in <code>pattern</code> being considered magic.
+
+
+<p>
+If the pattern has captures,
+then in a successful match
+the captured values are also returned,
+after the two indices.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.format"><code>string.format (formatstring, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns a formatted version of its variable number of arguments
+following the description given in its first argument,
+which must be a string.
+The format string follows the same rules as the ISO&nbsp;C function <code>sprintf</code>.
+The only differences are that the conversion specifiers and modifiers
+<code>F</code>, <code>n</code>, <code>*</code>, <code>h</code>, <code>L</code>, and <code>l</code> are not supported
+and that there is an extra specifier, <code>q</code>.
+Both width and precision, when present,
+are limited to two digits.
+
+
+<p>
+The specifier <code>q</code> formats booleans, nil, numbers, and strings
+in a way that the result is a valid constant in Lua source code.
+Booleans and nil are written in the obvious way
+(<code>true</code>, <code>false</code>, <code>nil</code>).
+Floats are written in hexadecimal,
+to preserve full precision.
+A string is written between double quotes,
+using escape sequences when necessary to ensure that
+it can safely be read back by the Lua interpreter.
+For instance, the call
+
+<pre>
+ string.format('%q', 'a string with "quotes" and \n new line')
+</pre><p>
+may produce the string:
+
+<pre>
+ "a string with \"quotes\" and \
+ new line"
+</pre><p>
+This specifier does not support modifiers (flags, width, precision).
+
+
+<p>
+The conversion specifiers
+<code>A</code>, <code>a</code>, <code>E</code>, <code>e</code>, <code>f</code>,
+<code>G</code>, and <code>g</code> all expect a number as argument.
+The specifiers <code>c</code>, <code>d</code>,
+<code>i</code>, <code>o</code>, <code>u</code>, <code>X</code>, and <code>x</code>
+expect an integer.
+When Lua is compiled with a C89 compiler,
+the specifiers <code>A</code> and <code>a</code> (hexadecimal floats)
+do not support modifiers.
+
+
+<p>
+The specifier <code>s</code> expects a string;
+if its argument is not a string,
+it is converted to one following the same rules of <a href="#pdf-tostring"><code>tostring</code></a>.
+If the specifier has any modifier,
+the corresponding string argument should not contain embedded zeros.
+
+
+<p>
+The specifier <code>p</code> formats the pointer
+returned by <a href="#lua_topointer"><code>lua_topointer</code></a>.
+That gives a unique string identifier for tables, userdata,
+threads, strings, and functions.
+For other values (numbers, nil, booleans),
+this specifier results in a string representing
+the pointer <code>NULL</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.gmatch"><code>string.gmatch (s, pattern [, init])</code></a></h3>
+Returns an iterator function that,
+each time it is called,
+returns the next captures from <code>pattern</code> (see <a href="#6.4.1">&sect;6.4.1</a>)
+over the string <code>s</code>.
+If <code>pattern</code> specifies no captures,
+then the whole match is produced in each call.
+A third, optional numeric argument <code>init</code> specifies
+where to start the search;
+its default value is&nbsp;1 and can be negative.
+
+
+<p>
+As an example, the following loop
+will iterate over all the words from string <code>s</code>,
+printing one per line:
+
+<pre>
+ s = "hello world from Lua"
+ for w in string.gmatch(s, "%a+") do
+ print(w)
+ end
+</pre><p>
+The next example collects all pairs <code>key=value</code> from the
+given string into a table:
+
+<pre>
+ t = {}
+ s = "from=world, to=Lua"
+ for k, v in string.gmatch(s, "(%w+)=(%w+)") do
+ t[k] = v
+ end
+</pre>
+
+<p>
+For this function, a caret '<code>^</code>' at the start of a pattern does not
+work as an anchor, as this would prevent the iteration.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.gsub"><code>string.gsub (s, pattern, repl [, n])</code></a></h3>
+Returns a copy of <code>s</code>
+in which all (or the first <code>n</code>, if given)
+occurrences of the <code>pattern</code> (see <a href="#6.4.1">&sect;6.4.1</a>) have been
+replaced by a replacement string specified by <code>repl</code>,
+which can be a string, a table, or a function.
+<code>gsub</code> also returns, as its second value,
+the total number of matches that occurred.
+The name <code>gsub</code> comes from <em>Global SUBstitution</em>.
+
+
+<p>
+If <code>repl</code> is a string, then its value is used for replacement.
+The character&nbsp;<code>%</code> works as an escape character:
+any sequence in <code>repl</code> of the form <code>%<em>d</em></code>,
+with <em>d</em> between 1 and 9,
+stands for the value of the <em>d</em>-th captured substring;
+the sequence <code>%0</code> stands for the whole match;
+the sequence <code>%%</code> stands for a single&nbsp;<code>%</code>.
+
+
+<p>
+If <code>repl</code> is a table, then the table is queried for every match,
+using the first capture as the key.
+
+
+<p>
+If <code>repl</code> is a function, then this function is called every time a
+match occurs, with all captured substrings passed as arguments,
+in order.
+
+
+<p>
+In any case,
+if the pattern specifies no captures,
+then it behaves as if the whole pattern was inside a capture.
+
+
+<p>
+If the value returned by the table query or by the function call
+is a string or a number,
+then it is used as the replacement string;
+otherwise, if it is <b>false</b> or <b>nil</b>,
+then there is no replacement
+(that is, the original match is kept in the string).
+
+
+<p>
+Here are some examples:
+
+<pre>
+ x = string.gsub("hello world", "(%w+)", "%1 %1")
+ --&gt; x="hello hello world world"
+
+ x = string.gsub("hello world", "%w+", "%0 %0", 1)
+ --&gt; x="hello hello world"
+
+ x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
+ --&gt; x="world hello Lua from"
+
+ x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
+ --&gt; x="home = /home/roberto, user = roberto"
+
+ x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
+ return load(s)()
+ end)
+ --&gt; x="4+5 = 9"
+
+ local t = {name="lua", version="5.4"}
+ x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
+ --&gt; x="lua-5.4.tar.gz"
+</pre>
+
+
+
+<p>
+<hr><h3><a name="pdf-string.len"><code>string.len (s)</code></a></h3>
+
+
+<p>
+Receives a string and returns its length.
+The empty string <code>""</code> has length 0.
+Embedded zeros are counted,
+so <code>"a\000bc\000"</code> has length 5.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.lower"><code>string.lower (s)</code></a></h3>
+
+
+<p>
+Receives a string and returns a copy of this string with all
+uppercase letters changed to lowercase.
+All other characters are left unchanged.
+The definition of what an uppercase letter is depends on the current locale.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.match"><code>string.match (s, pattern [, init])</code></a></h3>
+
+
+<p>
+Looks for the first <em>match</em> of
+the <code>pattern</code> (see <a href="#6.4.1">&sect;6.4.1</a>) in the string <code>s</code>.
+If it finds one, then <code>match</code> returns
+the captures from the pattern;
+otherwise it returns <b>fail</b>.
+If <code>pattern</code> specifies no captures,
+then the whole match is returned.
+A third, optional numeric argument <code>init</code> specifies
+where to start the search;
+its default value is&nbsp;1 and can be negative.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.pack"><code>string.pack (fmt, v1, v2, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns a binary string containing the values <code>v1</code>, <code>v2</code>, etc.
+serialized in binary form (packed)
+according to the format string <code>fmt</code> (see <a href="#6.4.2">&sect;6.4.2</a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.packsize"><code>string.packsize (fmt)</code></a></h3>
+
+
+<p>
+Returns the length of a string resulting from <a href="#pdf-string.pack"><code>string.pack</code></a>
+with the given format.
+The format string cannot have the variable-length options
+'<code>s</code>' or '<code>z</code>' (see <a href="#6.4.2">&sect;6.4.2</a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.rep"><code>string.rep (s, n [, sep])</code></a></h3>
+
+
+<p>
+Returns a string that is the concatenation of <code>n</code> copies of
+the string <code>s</code> separated by the string <code>sep</code>.
+The default value for <code>sep</code> is the empty string
+(that is, no separator).
+Returns the empty string if <code>n</code> is not positive.
+
+
+<p>
+(Note that it is very easy to exhaust the memory of your machine
+with a single call to this function.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.reverse"><code>string.reverse (s)</code></a></h3>
+
+
+<p>
+Returns a string that is the string <code>s</code> reversed.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.sub"><code>string.sub (s, i [, j])</code></a></h3>
+
+
+<p>
+Returns the substring of <code>s</code> that
+starts at <code>i</code> and continues until <code>j</code>;
+<code>i</code> and <code>j</code> can be negative.
+If <code>j</code> is absent, then it is assumed to be equal to -1
+(which is the same as the string length).
+In particular,
+the call <code>string.sub(s,1,j)</code> returns a prefix of <code>s</code>
+with length <code>j</code>,
+and <code>string.sub(s, -i)</code> (for a positive <code>i</code>)
+returns a suffix of <code>s</code>
+with length <code>i</code>.
+
+
+<p>
+If, after the translation of negative indices,
+<code>i</code> is less than 1,
+it is corrected to 1.
+If <code>j</code> is greater than the string length,
+it is corrected to that length.
+If, after these corrections,
+<code>i</code> is greater than <code>j</code>,
+the function returns the empty string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.unpack"><code>string.unpack (fmt, s [, pos])</code></a></h3>
+
+
+<p>
+Returns the values packed in string <code>s</code> (see <a href="#pdf-string.pack"><code>string.pack</code></a>)
+according to the format string <code>fmt</code> (see <a href="#6.4.2">&sect;6.4.2</a>).
+An optional <code>pos</code> marks where
+to start reading in <code>s</code> (default is 1).
+After the read values,
+this function also returns the index of the first unread byte in <code>s</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.upper"><code>string.upper (s)</code></a></h3>
+
+
+<p>
+Receives a string and returns a copy of this string with all
+lowercase letters changed to uppercase.
+All other characters are left unchanged.
+The definition of what a lowercase letter is depends on the current locale.
+
+
+
+
+
+
+
+<h3>6.4.1 &ndash; <a name="6.4.1">Patterns</a></h3>
+
+
+
+<p>
+Patterns in Lua are described by regular strings,
+which are interpreted as patterns by the pattern-matching functions
+<a href="#pdf-string.find"><code>string.find</code></a>,
+<a href="#pdf-string.gmatch"><code>string.gmatch</code></a>,
+<a href="#pdf-string.gsub"><code>string.gsub</code></a>,
+and <a href="#pdf-string.match"><code>string.match</code></a>.
+This section describes the syntax and the meaning
+(that is, what they match) of these strings.
+
+
+
+
+
+<h4>Character Class:</h4><p>
+A <em>character class</em> is used to represent a set of characters.
+The following combinations are allowed in describing a character class:
+
+<ul>
+
+<li><b><em>x</em>: </b>
+(where <em>x</em> is not one of the <em>magic characters</em>
+<code>^$()%.[]*+-?</code>)
+represents the character <em>x</em> itself.
+</li>
+
+<li><b><code>.</code>: </b> (a dot) represents all characters.</li>
+
+<li><b><code>%a</code>: </b> represents all letters.</li>
+
+<li><b><code>%c</code>: </b> represents all control characters.</li>
+
+<li><b><code>%d</code>: </b> represents all digits.</li>
+
+<li><b><code>%g</code>: </b> represents all printable characters except space.</li>
+
+<li><b><code>%l</code>: </b> represents all lowercase letters.</li>
+
+<li><b><code>%p</code>: </b> represents all punctuation characters.</li>
+
+<li><b><code>%s</code>: </b> represents all space characters.</li>
+
+<li><b><code>%u</code>: </b> represents all uppercase letters.</li>
+
+<li><b><code>%w</code>: </b> represents all alphanumeric characters.</li>
+
+<li><b><code>%x</code>: </b> represents all hexadecimal digits.</li>
+
+<li><b><code>%<em>x</em></code>: </b> (where <em>x</em> is any non-alphanumeric character)
+represents the character <em>x</em>.
+This is the standard way to escape the magic characters.
+Any non-alphanumeric character
+(including all punctuation characters, even the non-magical)
+can be preceded by a '<code>%</code>' to represent itself in a pattern.
+</li>
+
+<li><b><code>[<em>set</em>]</code>: </b>
+represents the class which is the union of all
+characters in <em>set</em>.
+A range of characters can be specified by
+separating the end characters of the range,
+in ascending order, with a '<code>-</code>'.
+All classes <code>%</code><em>x</em> described above can also be used as
+components in <em>set</em>.
+All other characters in <em>set</em> represent themselves.
+For example, <code>[%w_]</code> (or <code>[_%w]</code>)
+represents all alphanumeric characters plus the underscore,
+<code>[0-7]</code> represents the octal digits,
+and <code>[0-7%l%-]</code> represents the octal digits plus
+the lowercase letters plus the '<code>-</code>' character.
+
+
+<p>
+You can put a closing square bracket in a set
+by positioning it as the first character in the set.
+You can put a hyphen in a set
+by positioning it as the first or the last character in the set.
+(You can also use an escape for both cases.)
+
+
+<p>
+The interaction between ranges and classes is not defined.
+Therefore, patterns like <code>[%a-z]</code> or <code>[a-%%]</code>
+have no meaning.
+</li>
+
+<li><b><code>[^<em>set</em>]</code>: </b>
+represents the complement of <em>set</em>,
+where <em>set</em> is interpreted as above.
+</li>
+
+</ul><p>
+For all classes represented by single letters (<code>%a</code>, <code>%c</code>, etc.),
+the corresponding uppercase letter represents the complement of the class.
+For instance, <code>%S</code> represents all non-space characters.
+
+
+<p>
+The definitions of letter, space, and other character groups
+depend on the current locale.
+In particular, the class <code>[a-z]</code> may not be equivalent to <code>%l</code>.
+
+
+
+
+
+<h4>Pattern Item:</h4><p>
+A <em>pattern item</em> can be
+
+<ul>
+
+<li>
+a single character class,
+which matches any single character in the class;
+</li>
+
+<li>
+a single character class followed by '<code>*</code>',
+which matches sequences of zero or more characters in the class.
+These repetition items will always match the longest possible sequence;
+</li>
+
+<li>
+a single character class followed by '<code>+</code>',
+which matches sequences of one or more characters in the class.
+These repetition items will always match the longest possible sequence;
+</li>
+
+<li>
+a single character class followed by '<code>-</code>',
+which also matches sequences of zero or more characters in the class.
+Unlike '<code>*</code>',
+these repetition items will always match the shortest possible sequence;
+</li>
+
+<li>
+a single character class followed by '<code>?</code>',
+which matches zero or one occurrence of a character in the class.
+It always matches one occurrence if possible;
+</li>
+
+<li>
+<code>%<em>n</em></code>, for <em>n</em> between 1 and 9;
+such item matches a substring equal to the <em>n</em>-th captured string
+(see below);
+</li>
+
+<li>
+<code>%b<em>xy</em></code>, where <em>x</em> and <em>y</em> are two distinct characters;
+such item matches strings that start with&nbsp;<em>x</em>, end with&nbsp;<em>y</em>,
+and where the <em>x</em> and <em>y</em> are <em>balanced</em>.
+This means that, if one reads the string from left to right,
+counting <em>+1</em> for an <em>x</em> and <em>-1</em> for a <em>y</em>,
+the ending <em>y</em> is the first <em>y</em> where the count reaches 0.
+For instance, the item <code>%b()</code> matches expressions with
+balanced parentheses.
+</li>
+
+<li>
+<code>%f[<em>set</em>]</code>, a <em>frontier pattern</em>;
+such item matches an empty string at any position such that
+the next character belongs to <em>set</em>
+and the previous character does not belong to <em>set</em>.
+The set <em>set</em> is interpreted as previously described.
+The beginning and the end of the subject are handled as if
+they were the character '<code>\0</code>'.
+</li>
+
+</ul>
+
+
+
+
+<h4>Pattern:</h4><p>
+A <em>pattern</em> is a sequence of pattern items.
+A caret '<code>^</code>' at the beginning of a pattern anchors the match at the
+beginning of the subject string.
+A '<code>$</code>' at the end of a pattern anchors the match at the
+end of the subject string.
+At other positions,
+'<code>^</code>' and '<code>$</code>' have no special meaning and represent themselves.
+
+
+
+
+
+<h4>Captures:</h4><p>
+A pattern can contain sub-patterns enclosed in parentheses;
+they describe <em>captures</em>.
+When a match succeeds, the substrings of the subject string
+that match captures are stored (<em>captured</em>) for future use.
+Captures are numbered according to their left parentheses.
+For instance, in the pattern <code>"(a*(.)%w(%s*))"</code>,
+the part of the string matching <code>"a*(.)%w(%s*)"</code> is
+stored as the first capture, and therefore has number&nbsp;1;
+the character matching "<code>.</code>" is captured with number&nbsp;2,
+and the part matching "<code>%s*</code>" has number&nbsp;3.
+
+
+<p>
+As a special case, the capture <code>()</code> captures
+the current string position (a number).
+For instance, if we apply the pattern <code>"()aa()"</code> on the
+string <code>"flaaap"</code>, there will be two captures: 3&nbsp;and&nbsp;5.
+
+
+
+
+
+<h4>Multiple matches:</h4><p>
+The function <a href="#pdf-string.gsub"><code>string.gsub</code></a> and the iterator <a href="#pdf-string.gmatch"><code>string.gmatch</code></a>
+match multiple occurrences of the given pattern in the subject.
+For these functions,
+a new match is considered valid only
+if it ends at least one byte after the end of the previous match.
+In other words, the pattern machine never accepts the
+empty string as a match immediately after another match.
+As an example,
+consider the results of the following code:
+
+<pre>
+ &gt; string.gsub("abc", "()a*()", print);
+ --&gt; 1 2
+ --&gt; 3 3
+ --&gt; 4 4
+</pre><p>
+The second and third results come from Lua matching an empty
+string after '<code>b</code>' and another one after '<code>c</code>'.
+Lua does not match an empty string after '<code>a</code>',
+because it would end at the same position of the previous match.
+
+
+
+
+
+
+
+<h3>6.4.2 &ndash; <a name="6.4.2">Format Strings for Pack and Unpack</a></h3>
+
+<p>
+The first argument to <a href="#pdf-string.pack"><code>string.pack</code></a>,
+<a href="#pdf-string.packsize"><code>string.packsize</code></a>, and <a href="#pdf-string.unpack"><code>string.unpack</code></a>
+is a format string,
+which describes the layout of the structure being created or read.
+
+
+<p>
+A format string is a sequence of conversion options.
+The conversion options are as follows:
+
+<ul>
+<li><b><code>&lt;</code>: </b>sets little endian</li>
+<li><b><code>&gt;</code>: </b>sets big endian</li>
+<li><b><code>=</code>: </b>sets native endian</li>
+<li><b><code>![<em>n</em>]</code>: </b>sets maximum alignment to <code>n</code>
+(default is native alignment)</li>
+<li><b><code>b</code>: </b>a signed byte (<code>char</code>)</li>
+<li><b><code>B</code>: </b>an unsigned byte (<code>char</code>)</li>
+<li><b><code>h</code>: </b>a signed <code>short</code> (native size)</li>
+<li><b><code>H</code>: </b>an unsigned <code>short</code> (native size)</li>
+<li><b><code>l</code>: </b>a signed <code>long</code> (native size)</li>
+<li><b><code>L</code>: </b>an unsigned <code>long</code> (native size)</li>
+<li><b><code>j</code>: </b>a <code>lua_Integer</code></li>
+<li><b><code>J</code>: </b>a <code>lua_Unsigned</code></li>
+<li><b><code>T</code>: </b>a <code>size_t</code> (native size)</li>
+<li><b><code>i[<em>n</em>]</code>: </b>a signed <code>int</code> with <code>n</code> bytes
+(default is native size)</li>
+<li><b><code>I[<em>n</em>]</code>: </b>an unsigned <code>int</code> with <code>n</code> bytes
+(default is native size)</li>
+<li><b><code>f</code>: </b>a <code>float</code> (native size)</li>
+<li><b><code>d</code>: </b>a <code>double</code> (native size)</li>
+<li><b><code>n</code>: </b>a <code>lua_Number</code></li>
+<li><b><code>c<em>n</em></code>: </b>a fixed-sized string with <code>n</code> bytes</li>
+<li><b><code>z</code>: </b>a zero-terminated string</li>
+<li><b><code>s[<em>n</em>]</code>: </b>a string preceded by its length
+coded as an unsigned integer with <code>n</code> bytes
+(default is a <code>size_t</code>)</li>
+<li><b><code>x</code>: </b>one byte of padding</li>
+<li><b><code>X<em>op</em></code>: </b>an empty item that aligns
+according to option <code>op</code>
+(which is otherwise ignored)</li>
+<li><b>'<code> </code>': </b>(space) ignored</li>
+</ul><p>
+(A "<code>[<em>n</em>]</code>" means an optional integral numeral.)
+Except for padding, spaces, and configurations
+(options "<code>xX &lt;=&gt;!</code>"),
+each option corresponds to an argument in <a href="#pdf-string.pack"><code>string.pack</code></a>
+or a result in <a href="#pdf-string.unpack"><code>string.unpack</code></a>.
+
+
+<p>
+For options "<code>!<em>n</em></code>", "<code>s<em>n</em></code>", "<code>i<em>n</em></code>", and "<code>I<em>n</em></code>",
+<code>n</code> can be any integer between 1 and 16.
+All integral options check overflows;
+<a href="#pdf-string.pack"><code>string.pack</code></a> checks whether the given value fits in the given size;
+<a href="#pdf-string.unpack"><code>string.unpack</code></a> checks whether the read value fits in a Lua integer.
+For the unsigned options,
+Lua integers are treated as unsigned values too.
+
+
+<p>
+Any format string starts as if prefixed by "<code>!1=</code>",
+that is,
+with maximum alignment of 1 (no alignment)
+and native endianness.
+
+
+<p>
+Native endianness assumes that the whole system is
+either big or little endian.
+The packing functions will not emulate correctly the behavior
+of mixed-endian formats.
+
+
+<p>
+Alignment works as follows:
+For each option,
+the format gets extra padding until the data starts
+at an offset that is a multiple of the minimum between the
+option size and the maximum alignment;
+this minimum must be a power of 2.
+Options "<code>c</code>" and "<code>z</code>" are not aligned;
+option "<code>s</code>" follows the alignment of its starting integer.
+
+
+<p>
+All padding is filled with zeros by <a href="#pdf-string.pack"><code>string.pack</code></a>
+and ignored by <a href="#pdf-string.unpack"><code>string.unpack</code></a>.
+
+
+
+
+
+
+
+<h2>6.5 &ndash; <a name="6.5">UTF-8 Support</a></h2>
+
+<p>
+This library provides basic support for UTF-8 encoding.
+It provides all its functions inside the table <a name="pdf-utf8"><code>utf8</code></a>.
+This library does not provide any support for Unicode other
+than the handling of the encoding.
+Any operation that needs the meaning of a character,
+such as character classification, is outside its scope.
+
+
+<p>
+Unless stated otherwise,
+all functions that expect a byte position as a parameter
+assume that the given position is either the start of a byte sequence
+or one plus the length of the subject string.
+As in the string library,
+negative indices count from the end of the string.
+
+
+<p>
+Functions that create byte sequences
+accept all values up to <code>0x7FFFFFFF</code>,
+as defined in the original UTF-8 specification;
+that implies byte sequences of up to six bytes.
+
+
+<p>
+Functions that interpret byte sequences only accept
+valid sequences (well formed and not overlong).
+By default, they only accept byte sequences
+that result in valid Unicode code points,
+rejecting values greater than <code>10FFFF</code> and surrogates.
+A boolean argument <code>lax</code>, when available,
+lifts these checks,
+so that all values up to <code>0x7FFFFFFF</code> are accepted.
+(Not well formed and overlong sequences are still rejected.)
+
+
+<p>
+<hr><h3><a name="pdf-utf8.char"><code>utf8.char (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Receives zero or more integers,
+converts each one to its corresponding UTF-8 byte sequence
+and returns a string with the concatenation of all these sequences.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-utf8.charpattern"><code>utf8.charpattern</code></a></h3>
+
+
+<p>
+The pattern (a string, not a function) "<code>[\0-\x7F\xC2-\xFD][\x80-\xBF]*</code>"
+(see <a href="#6.4.1">&sect;6.4.1</a>),
+which matches exactly one UTF-8 byte sequence,
+assuming that the subject is a valid UTF-8 string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-utf8.codes"><code>utf8.codes (s [, lax])</code></a></h3>
+
+
+<p>
+Returns values so that the construction
+
+<pre>
+ for p, c in utf8.codes(s) do <em>body</em> end
+</pre><p>
+will iterate over all UTF-8 characters in string <code>s</code>,
+with <code>p</code> being the position (in bytes) and <code>c</code> the code point
+of each character.
+It raises an error if it meets any invalid byte sequence.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-utf8.codepoint"><code>utf8.codepoint (s [, i [, j [, lax]]])</code></a></h3>
+
+
+<p>
+Returns the code points (as integers) from all characters in <code>s</code>
+that start between byte position <code>i</code> and <code>j</code> (both included).
+The default for <code>i</code> is 1 and for <code>j</code> is <code>i</code>.
+It raises an error if it meets any invalid byte sequence.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-utf8.len"><code>utf8.len (s [, i [, j [, lax]]])</code></a></h3>
+
+
+<p>
+Returns the number of UTF-8 characters in string <code>s</code>
+that start between positions <code>i</code> and <code>j</code> (both inclusive).
+The default for <code>i</code> is 1 and for <code>j</code> is -1.
+If it finds any invalid byte sequence,
+returns <b>fail</b> plus the position of the first invalid byte.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-utf8.offset"><code>utf8.offset (s, n [, i])</code></a></h3>
+
+
+<p>
+Returns the position (in bytes) where the encoding of the
+<code>n</code>-th character of <code>s</code>
+(counting from position <code>i</code>) starts.
+A negative <code>n</code> gets characters before position <code>i</code>.
+The default for <code>i</code> is 1 when <code>n</code> is non-negative
+and <code>#s + 1</code> otherwise,
+so that <code>utf8.offset(s, -n)</code> gets the offset of the
+<code>n</code>-th character from the end of the string.
+If the specified character is neither in the subject
+nor right after its end,
+the function returns <b>fail</b>.
+
+
+<p>
+As a special case,
+when <code>n</code> is 0 the function returns the start of the encoding
+of the character that contains the <code>i</code>-th byte of <code>s</code>.
+
+
+<p>
+This function assumes that <code>s</code> is a valid UTF-8 string.
+
+
+
+
+
+
+
+<h2>6.6 &ndash; <a name="6.6">Table Manipulation</a></h2>
+
+<p>
+This library provides generic functions for table manipulation.
+It provides all its functions inside the table <a name="pdf-table"><code>table</code></a>.
+
+
+<p>
+Remember that, whenever an operation needs the length of a table,
+all caveats about the length operator apply (see <a href="#3.4.7">&sect;3.4.7</a>).
+All functions ignore non-numeric keys
+in the tables given as arguments.
+
+
+<p>
+<hr><h3><a name="pdf-table.concat"><code>table.concat (list [, sep [, i [, j]]])</code></a></h3>
+
+
+<p>
+Given a list where all elements are strings or numbers,
+returns the string <code>list[i]..sep..list[i+1] &middot;&middot;&middot; sep..list[j]</code>.
+The default value for <code>sep</code> is the empty string,
+the default for <code>i</code> is 1,
+and the default for <code>j</code> is <code>#list</code>.
+If <code>i</code> is greater than <code>j</code>, returns the empty string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.insert"><code>table.insert (list, [pos,] value)</code></a></h3>
+
+
+<p>
+Inserts element <code>value</code> at position <code>pos</code> in <code>list</code>,
+shifting up the elements
+<code>list[pos], list[pos+1], &middot;&middot;&middot;, list[#list]</code>.
+The default value for <code>pos</code> is <code>#list+1</code>,
+so that a call <code>table.insert(t,x)</code> inserts <code>x</code> at the end
+of the list <code>t</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.move"><code>table.move (a1, f, e, t [,a2])</code></a></h3>
+
+
+<p>
+Moves elements from the table <code>a1</code> to the table <code>a2</code>,
+performing the equivalent to the following
+multiple assignment:
+<code>a2[t],&middot;&middot;&middot; = a1[f],&middot;&middot;&middot;,a1[e]</code>.
+The default for <code>a2</code> is <code>a1</code>.
+The destination range can overlap with the source range.
+The number of elements to be moved must fit in a Lua integer.
+
+
+<p>
+Returns the destination table <code>a2</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.pack"><code>table.pack (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns a new table with all arguments stored into keys 1, 2, etc.
+and with a field "<code>n</code>" with the total number of arguments.
+Note that the resulting table may not be a sequence,
+if some arguments are <b>nil</b>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.remove"><code>table.remove (list [, pos])</code></a></h3>
+
+
+<p>
+Removes from <code>list</code> the element at position <code>pos</code>,
+returning the value of the removed element.
+When <code>pos</code> is an integer between 1 and <code>#list</code>,
+it shifts down the elements
+<code>list[pos+1], list[pos+2], &middot;&middot;&middot;, list[#list]</code>
+and erases element <code>list[#list]</code>;
+The index <code>pos</code> can also be 0 when <code>#list</code> is 0,
+or <code>#list + 1</code>.
+
+
+<p>
+The default value for <code>pos</code> is <code>#list</code>,
+so that a call <code>table.remove(l)</code> removes the last element
+of the list <code>l</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.sort"><code>table.sort (list [, comp])</code></a></h3>
+
+
+<p>
+Sorts the list elements in a given order, <em>in-place</em>,
+from <code>list[1]</code> to <code>list[#list]</code>.
+If <code>comp</code> is given,
+then it must be a function that receives two list elements
+and returns true when the first element must come
+before the second in the final order,
+so that, after the sort,
+<code>i &lt;= j</code> implies <code>not comp(list[j],list[i])</code>.
+If <code>comp</code> is not given,
+then the standard Lua operator <code>&lt;</code> is used instead.
+
+
+<p>
+The <code>comp</code> function must define a consistent order;
+more formally, the function must define a strict weak order.
+(A weak order is similar to a total order,
+but it can equate different elements for comparison purposes.)
+
+
+<p>
+The sort algorithm is not stable:
+Different elements considered equal by the given order
+may have their relative positions changed by the sort.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.unpack"><code>table.unpack (list [, i [, j]])</code></a></h3>
+
+
+<p>
+Returns the elements from the given list.
+This function is equivalent to
+
+<pre>
+ return list[i], list[i+1], &middot;&middot;&middot;, list[j]
+</pre><p>
+By default, <code>i</code> is&nbsp;1 and <code>j</code> is <code>#list</code>.
+
+
+
+
+
+
+
+<h2>6.7 &ndash; <a name="6.7">Mathematical Functions</a></h2>
+
+<p>
+This library provides basic mathematical functions.
+It provides all its functions and constants inside the table <a name="pdf-math"><code>math</code></a>.
+Functions with the annotation "<code>integer/float</code>" give
+integer results for integer arguments
+and float results for non-integer arguments.
+The rounding functions
+<a href="#pdf-math.ceil"><code>math.ceil</code></a>, <a href="#pdf-math.floor"><code>math.floor</code></a>, and <a href="#pdf-math.modf"><code>math.modf</code></a>
+return an integer when the result fits in the range of an integer,
+or a float otherwise.
+
+
+<p>
+<hr><h3><a name="pdf-math.abs"><code>math.abs (x)</code></a></h3>
+
+
+<p>
+Returns the maximum value between <code>x</code> and <code>-x</code>. (integer/float)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.acos"><code>math.acos (x)</code></a></h3>
+
+
+<p>
+Returns the arc cosine of <code>x</code> (in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.asin"><code>math.asin (x)</code></a></h3>
+
+
+<p>
+Returns the arc sine of <code>x</code> (in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.atan"><code>math.atan (y [, x])</code></a></h3>
+
+
+<p>
+
+Returns the arc tangent of <code>y/x</code> (in radians),
+using the signs of both arguments to find the
+quadrant of the result.
+It also handles correctly the case of <code>x</code> being zero.
+
+
+<p>
+The default value for <code>x</code> is 1,
+so that the call <code>math.atan(y)</code>
+returns the arc tangent of <code>y</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.ceil"><code>math.ceil (x)</code></a></h3>
+
+
+<p>
+Returns the smallest integral value greater than or equal to <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.cos"><code>math.cos (x)</code></a></h3>
+
+
+<p>
+Returns the cosine of <code>x</code> (assumed to be in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.deg"><code>math.deg (x)</code></a></h3>
+
+
+<p>
+Converts the angle <code>x</code> from radians to degrees.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.exp"><code>math.exp (x)</code></a></h3>
+
+
+<p>
+Returns the value <em>e<sup>x</sup></em>
+(where <code>e</code> is the base of natural logarithms).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.floor"><code>math.floor (x)</code></a></h3>
+
+
+<p>
+Returns the largest integral value less than or equal to <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.fmod"><code>math.fmod (x, y)</code></a></h3>
+
+
+<p>
+Returns the remainder of the division of <code>x</code> by <code>y</code>
+that rounds the quotient towards zero. (integer/float)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.huge"><code>math.huge</code></a></h3>
+
+
+<p>
+The float value <code>HUGE_VAL</code>,
+a value greater than any other numeric value.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.log"><code>math.log (x [, base])</code></a></h3>
+
+
+<p>
+Returns the logarithm of <code>x</code> in the given base.
+The default for <code>base</code> is <em>e</em>
+(so that the function returns the natural logarithm of <code>x</code>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.max"><code>math.max (x, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns the argument with the maximum value,
+according to the Lua operator <code>&lt;</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.maxinteger"><code>math.maxinteger</code></a></h3>
+An integer with the maximum value for an integer.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.min"><code>math.min (x, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns the argument with the minimum value,
+according to the Lua operator <code>&lt;</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.mininteger"><code>math.mininteger</code></a></h3>
+An integer with the minimum value for an integer.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.modf"><code>math.modf (x)</code></a></h3>
+
+
+<p>
+Returns the integral part of <code>x</code> and the fractional part of <code>x</code>.
+Its second result is always a float.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.pi"><code>math.pi</code></a></h3>
+
+
+<p>
+The value of <em>&pi;</em>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.rad"><code>math.rad (x)</code></a></h3>
+
+
+<p>
+Converts the angle <code>x</code> from degrees to radians.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.random"><code>math.random ([m [, n]])</code></a></h3>
+
+
+<p>
+When called without arguments,
+returns a pseudo-random float with uniform distribution
+in the range <em>[0,1)</em>.
+When called with two integers <code>m</code> and <code>n</code>,
+<code>math.random</code> returns a pseudo-random integer
+with uniform distribution in the range <em>[m, n]</em>.
+The call <code>math.random(n)</code>, for a positive <code>n</code>,
+is equivalent to <code>math.random(1,n)</code>.
+The call <code>math.random(0)</code> produces an integer with
+all bits (pseudo)random.
+
+
+<p>
+This function uses the <code>xoshiro256**</code> algorithm to produce
+pseudo-random 64-bit integers,
+which are the results of calls with argument&nbsp;0.
+Other results (ranges and floats)
+are unbiased extracted from these integers.
+
+
+<p>
+Lua initializes its pseudo-random generator with the equivalent of
+a call to <a href="#pdf-math.randomseed"><code>math.randomseed</code></a> with no arguments,
+so that <code>math.random</code> should generate
+different sequences of results each time the program runs.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.randomseed"><code>math.randomseed ([x [, y]])</code></a></h3>
+
+
+<p>
+When called with at least one argument,
+the integer parameters <code>x</code> and <code>y</code> are
+joined into a 128-bit <em>seed</em> that
+is used to reinitialize the pseudo-random generator;
+equal seeds produce equal sequences of numbers.
+The default for <code>y</code> is zero.
+
+
+<p>
+When called with no arguments,
+Lua generates a seed with
+a weak attempt for randomness.
+
+
+<p>
+This function returns the two seed components
+that were effectively used,
+so that setting them again repeats the sequence.
+
+
+<p>
+To ensure a required level of randomness to the initial state
+(or contrarily, to have a deterministic sequence,
+for instance when debugging a program),
+you should call <a href="#pdf-math.randomseed"><code>math.randomseed</code></a> with explicit arguments.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.sin"><code>math.sin (x)</code></a></h3>
+
+
+<p>
+Returns the sine of <code>x</code> (assumed to be in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.sqrt"><code>math.sqrt (x)</code></a></h3>
+
+
+<p>
+Returns the square root of <code>x</code>.
+(You can also use the expression <code>x^0.5</code> to compute this value.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.tan"><code>math.tan (x)</code></a></h3>
+
+
+<p>
+Returns the tangent of <code>x</code> (assumed to be in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.tointeger"><code>math.tointeger (x)</code></a></h3>
+
+
+<p>
+If the value <code>x</code> is convertible to an integer,
+returns that integer.
+Otherwise, returns <b>fail</b>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.type"><code>math.type (x)</code></a></h3>
+
+
+<p>
+Returns "<code>integer</code>" if <code>x</code> is an integer,
+"<code>float</code>" if it is a float,
+or <b>fail</b> if <code>x</code> is not a number.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.ult"><code>math.ult (m, n)</code></a></h3>
+
+
+<p>
+Returns a boolean,
+<b>true</b> if and only if integer <code>m</code> is below integer <code>n</code> when
+they are compared as unsigned integers.
+
+
+
+
+
+
+
+<h2>6.8 &ndash; <a name="6.8">Input and Output Facilities</a></h2>
+
+<p>
+The I/O library provides two different styles for file manipulation.
+The first one uses implicit file handles;
+that is, there are operations to set a default input file and a
+default output file,
+and all input/output operations are done over these default files.
+The second style uses explicit file handles.
+
+
+<p>
+When using implicit file handles,
+all operations are supplied by table <a name="pdf-io"><code>io</code></a>.
+When using explicit file handles,
+the operation <a href="#pdf-io.open"><code>io.open</code></a> returns a file handle
+and then all operations are supplied as methods of the file handle.
+
+
+<p>
+The metatable for file handles provides metamethods
+for <code>__gc</code> and <code>__close</code> that try
+to close the file when called.
+
+
+<p>
+The table <code>io</code> also provides
+three predefined file handles with their usual meanings from C:
+<a name="pdf-io.stdin"><code>io.stdin</code></a>, <a name="pdf-io.stdout"><code>io.stdout</code></a>, and <a name="pdf-io.stderr"><code>io.stderr</code></a>.
+The I/O library never closes these files.
+
+
+<p>
+Unless otherwise stated,
+all I/O functions return <b>fail</b> on failure,
+plus an error message as a second result and
+a system-dependent error code as a third result,
+and some non-false value on success.
+On non-POSIX systems,
+the computation of the error message and error code
+in case of errors
+may be not thread safe,
+because they rely on the global C variable <code>errno</code>.
+
+
+<p>
+<hr><h3><a name="pdf-io.close"><code>io.close ([file])</code></a></h3>
+
+
+<p>
+Equivalent to <code>file:close()</code>.
+Without a <code>file</code>, closes the default output file.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.flush"><code>io.flush ()</code></a></h3>
+
+
+<p>
+Equivalent to <code>io.output():flush()</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.input"><code>io.input ([file])</code></a></h3>
+
+
+<p>
+When called with a file name, it opens the named file (in text mode),
+and sets its handle as the default input file.
+When called with a file handle,
+it simply sets this file handle as the default input file.
+When called without arguments,
+it returns the current default input file.
+
+
+<p>
+In case of errors this function raises the error,
+instead of returning an error code.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.lines"><code>io.lines ([filename, &middot;&middot;&middot;])</code></a></h3>
+
+
+<p>
+Opens the given file name in read mode
+and returns an iterator function that
+works like <code>file:lines(&middot;&middot;&middot;)</code> over the opened file.
+When the iterator function fails to read any value,
+it automatically closes the file.
+Besides the iterator function,
+<code>io.lines</code> returns three other values:
+two <b>nil</b> values as placeholders,
+plus the created file handle.
+Therefore, when used in a generic <b>for</b> loop,
+the file is closed also if the loop is interrupted by an
+error or a <b>break</b>.
+
+
+<p>
+The call <code>io.lines()</code> (with no file name) is equivalent
+to <code>io.input():lines("l")</code>;
+that is, it iterates over the lines of the default input file.
+In this case, the iterator does not close the file when the loop ends.
+
+
+<p>
+In case of errors opening the file,
+this function raises the error,
+instead of returning an error code.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.open"><code>io.open (filename [, mode])</code></a></h3>
+
+
+<p>
+This function opens a file,
+in the mode specified in the string <code>mode</code>.
+In case of success,
+it returns a new file handle.
+
+
+<p>
+The <code>mode</code> string can be any of the following:
+
+<ul>
+<li><b>"<code>r</code>": </b> read mode (the default);</li>
+<li><b>"<code>w</code>": </b> write mode;</li>
+<li><b>"<code>a</code>": </b> append mode;</li>
+<li><b>"<code>r+</code>": </b> update mode, all previous data is preserved;</li>
+<li><b>"<code>w+</code>": </b> update mode, all previous data is erased;</li>
+<li><b>"<code>a+</code>": </b> append update mode, previous data is preserved,
+ writing is only allowed at the end of file.</li>
+</ul><p>
+The <code>mode</code> string can also have a '<code>b</code>' at the end,
+which is needed in some systems to open the file in binary mode.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.output"><code>io.output ([file])</code></a></h3>
+
+
+<p>
+Similar to <a href="#pdf-io.input"><code>io.input</code></a>, but operates over the default output file.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.popen"><code>io.popen (prog [, mode])</code></a></h3>
+
+
+<p>
+This function is system dependent and is not available
+on all platforms.
+
+
+<p>
+Starts the program <code>prog</code> in a separated process and returns
+a file handle that you can use to read data from this program
+(if <code>mode</code> is <code>"r"</code>, the default)
+or to write data to this program
+(if <code>mode</code> is <code>"w"</code>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.read"><code>io.read (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Equivalent to <code>io.input():read(&middot;&middot;&middot;)</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.tmpfile"><code>io.tmpfile ()</code></a></h3>
+
+
+<p>
+In case of success,
+returns a handle for a temporary file.
+This file is opened in update mode
+and it is automatically removed when the program ends.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.type"><code>io.type (obj)</code></a></h3>
+
+
+<p>
+Checks whether <code>obj</code> is a valid file handle.
+Returns the string <code>"file"</code> if <code>obj</code> is an open file handle,
+<code>"closed file"</code> if <code>obj</code> is a closed file handle,
+or <b>fail</b> if <code>obj</code> is not a file handle.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.write"><code>io.write (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Equivalent to <code>io.output():write(&middot;&middot;&middot;)</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:close"><code>file:close ()</code></a></h3>
+
+
+<p>
+Closes <code>file</code>.
+Note that files are automatically closed when
+their handles are garbage collected,
+but that takes an unpredictable amount of time to happen.
+
+
+<p>
+When closing a file handle created with <a href="#pdf-io.popen"><code>io.popen</code></a>,
+<a href="#pdf-file:close"><code>file:close</code></a> returns the same values
+returned by <a href="#pdf-os.execute"><code>os.execute</code></a>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:flush"><code>file:flush ()</code></a></h3>
+
+
+<p>
+Saves any written data to <code>file</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:lines"><code>file:lines (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns an iterator function that,
+each time it is called,
+reads the file according to the given formats.
+When no format is given,
+uses "<code>l</code>" as a default.
+As an example, the construction
+
+<pre>
+ for c in file:lines(1) do <em>body</em> end
+</pre><p>
+will iterate over all characters of the file,
+starting at the current position.
+Unlike <a href="#pdf-io.lines"><code>io.lines</code></a>, this function does not close the file
+when the loop ends.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:read"><code>file:read (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Reads the file <code>file</code>,
+according to the given formats, which specify what to read.
+For each format,
+the function returns a string or a number with the characters read,
+or <b>fail</b> if it cannot read data with the specified format.
+(In this latter case,
+the function does not read subsequent formats.)
+When called without arguments,
+it uses a default format that reads the next line
+(see below).
+
+
+<p>
+The available formats are
+
+<ul>
+
+<li><b>"<code>n</code>": </b>
+reads a numeral and returns it as a float or an integer,
+following the lexical conventions of Lua.
+(The numeral may have leading whitespaces and a sign.)
+This format always reads the longest input sequence that
+is a valid prefix for a numeral;
+if that prefix does not form a valid numeral
+(e.g., an empty string, "<code>0x</code>", or "<code>3.4e-</code>")
+or it is too long (more than 200 characters),
+it is discarded and the format returns <b>fail</b>.
+</li>
+
+<li><b>"<code>a</code>": </b>
+reads the whole file, starting at the current position.
+On end of file, it returns the empty string;
+this format never fails.
+</li>
+
+<li><b>"<code>l</code>": </b>
+reads the next line skipping the end of line,
+returning <b>fail</b> on end of file.
+This is the default format.
+</li>
+
+<li><b>"<code>L</code>": </b>
+reads the next line keeping the end-of-line character (if present),
+returning <b>fail</b> on end of file.
+</li>
+
+<li><b><em>number</em>: </b>
+reads a string with up to this number of bytes,
+returning <b>fail</b> on end of file.
+If <code>number</code> is zero,
+it reads nothing and returns an empty string,
+or <b>fail</b> on end of file.
+</li>
+
+</ul><p>
+The formats "<code>l</code>" and "<code>L</code>" should be used only for text files.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:seek"><code>file:seek ([whence [, offset]])</code></a></h3>
+
+
+<p>
+Sets and gets the file position,
+measured from the beginning of the file,
+to the position given by <code>offset</code> plus a base
+specified by the string <code>whence</code>, as follows:
+
+<ul>
+<li><b>"<code>set</code>": </b> base is position 0 (beginning of the file);</li>
+<li><b>"<code>cur</code>": </b> base is current position;</li>
+<li><b>"<code>end</code>": </b> base is end of file;</li>
+</ul><p>
+In case of success, <code>seek</code> returns the final file position,
+measured in bytes from the beginning of the file.
+If <code>seek</code> fails, it returns <b>fail</b>,
+plus a string describing the error.
+
+
+<p>
+The default value for <code>whence</code> is <code>"cur"</code>,
+and for <code>offset</code> is 0.
+Therefore, the call <code>file:seek()</code> returns the current
+file position, without changing it;
+the call <code>file:seek("set")</code> sets the position to the
+beginning of the file (and returns 0);
+and the call <code>file:seek("end")</code> sets the position to the
+end of the file, and returns its size.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:setvbuf"><code>file:setvbuf (mode [, size])</code></a></h3>
+
+
+<p>
+Sets the buffering mode for a file.
+There are three available modes:
+
+<ul>
+<li><b>"<code>no</code>": </b> no buffering.</li>
+<li><b>"<code>full</code>": </b> full buffering.</li>
+<li><b>"<code>line</code>": </b> line buffering.</li>
+</ul>
+
+<p>
+For the last two cases,
+<code>size</code> is a hint for the size of the buffer, in bytes.
+The default is an appropriate size.
+
+
+<p>
+The specific behavior of each mode is non portable;
+check the underlying ISO&nbsp;C function <code>setvbuf</code> in your platform for
+more details.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:write"><code>file:write (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Writes the value of each of its arguments to <code>file</code>.
+The arguments must be strings or numbers.
+
+
+<p>
+In case of success, this function returns <code>file</code>.
+
+
+
+
+
+
+
+<h2>6.9 &ndash; <a name="6.9">Operating System Facilities</a></h2>
+
+<p>
+This library is implemented through table <a name="pdf-os"><code>os</code></a>.
+
+
+<p>
+<hr><h3><a name="pdf-os.clock"><code>os.clock ()</code></a></h3>
+
+
+<p>
+Returns an approximation of the amount in seconds of CPU time
+used by the program,
+as returned by the underlying ISO&nbsp;C function <code>clock</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.date"><code>os.date ([format [, time]])</code></a></h3>
+
+
+<p>
+Returns a string or a table containing date and time,
+formatted according to the given string <code>format</code>.
+
+
+<p>
+If the <code>time</code> argument is present,
+this is the time to be formatted
+(see the <a href="#pdf-os.time"><code>os.time</code></a> function for a description of this value).
+Otherwise, <code>date</code> formats the current time.
+
+
+<p>
+If <code>format</code> starts with '<code>!</code>',
+then the date is formatted in Coordinated Universal Time.
+After this optional character,
+if <code>format</code> is the string "<code>*t</code>",
+then <code>date</code> returns a table with the following fields:
+<code>year</code>, <code>month</code> (1&ndash;12), <code>day</code> (1&ndash;31),
+<code>hour</code> (0&ndash;23), <code>min</code> (0&ndash;59),
+<code>sec</code> (0&ndash;61, due to leap seconds),
+<code>wday</code> (weekday, 1&ndash;7, Sunday is&nbsp;1),
+<code>yday</code> (day of the year, 1&ndash;366),
+and <code>isdst</code> (daylight saving flag, a boolean).
+This last field may be absent
+if the information is not available.
+
+
+<p>
+If <code>format</code> is not "<code>*t</code>",
+then <code>date</code> returns the date as a string,
+formatted according to the same rules as the ISO&nbsp;C function <code>strftime</code>.
+
+
+<p>
+If <code>format</code> is absent, it defaults to "<code>%c</code>",
+which gives a human-readable date and time representation
+using the current locale.
+
+
+<p>
+On non-POSIX systems,
+this function may be not thread safe
+because of its reliance on C&nbsp;function <code>gmtime</code> and C&nbsp;function <code>localtime</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.difftime"><code>os.difftime (t2, t1)</code></a></h3>
+
+
+<p>
+Returns the difference, in seconds,
+from time <code>t1</code> to time <code>t2</code>
+(where the times are values returned by <a href="#pdf-os.time"><code>os.time</code></a>).
+In POSIX, Windows, and some other systems,
+this value is exactly <code>t2</code><em>-</em><code>t1</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.execute"><code>os.execute ([command])</code></a></h3>
+
+
+<p>
+This function is equivalent to the ISO&nbsp;C function <code>system</code>.
+It passes <code>command</code> to be executed by an operating system shell.
+Its first result is <b>true</b>
+if the command terminated successfully,
+or <b>fail</b> otherwise.
+After this first result
+the function returns a string plus a number,
+as follows:
+
+<ul>
+
+<li><b>"<code>exit</code>": </b>
+the command terminated normally;
+the following number is the exit status of the command.
+</li>
+
+<li><b>"<code>signal</code>": </b>
+the command was terminated by a signal;
+the following number is the signal that terminated the command.
+</li>
+
+</ul>
+
+<p>
+When called without a <code>command</code>,
+<code>os.execute</code> returns a boolean that is true if a shell is available.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.exit"><code>os.exit ([code [, close]])</code></a></h3>
+
+
+<p>
+Calls the ISO&nbsp;C function <code>exit</code> to terminate the host program.
+If <code>code</code> is <b>true</b>,
+the returned status is <code>EXIT_SUCCESS</code>;
+if <code>code</code> is <b>false</b>,
+the returned status is <code>EXIT_FAILURE</code>;
+if <code>code</code> is a number,
+the returned status is this number.
+The default value for <code>code</code> is <b>true</b>.
+
+
+<p>
+If the optional second argument <code>close</code> is true,
+the function closes the Lua state before exiting (see <a href="#lua_close"><code>lua_close</code></a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.getenv"><code>os.getenv (varname)</code></a></h3>
+
+
+<p>
+Returns the value of the process environment variable <code>varname</code>
+or <b>fail</b> if the variable is not defined.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.remove"><code>os.remove (filename)</code></a></h3>
+
+
+<p>
+Deletes the file (or empty directory, on POSIX systems)
+with the given name.
+If this function fails, it returns <b>fail</b>
+plus a string describing the error and the error code.
+Otherwise, it returns true.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.rename"><code>os.rename (oldname, newname)</code></a></h3>
+
+
+<p>
+Renames the file or directory named <code>oldname</code> to <code>newname</code>.
+If this function fails, it returns <b>fail</b>,
+plus a string describing the error and the error code.
+Otherwise, it returns true.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.setlocale"><code>os.setlocale (locale [, category])</code></a></h3>
+
+
+<p>
+Sets the current locale of the program.
+<code>locale</code> is a system-dependent string specifying a locale;
+<code>category</code> is an optional string describing which category to change:
+<code>"all"</code>, <code>"collate"</code>, <code>"ctype"</code>,
+<code>"monetary"</code>, <code>"numeric"</code>, or <code>"time"</code>;
+the default category is <code>"all"</code>.
+The function returns the name of the new locale,
+or <b>fail</b> if the request cannot be honored.
+
+
+<p>
+If <code>locale</code> is the empty string,
+the current locale is set to an implementation-defined native locale.
+If <code>locale</code> is the string "<code>C</code>",
+the current locale is set to the standard C locale.
+
+
+<p>
+When called with <b>nil</b> as the first argument,
+this function only returns the name of the current locale
+for the given category.
+
+
+<p>
+This function may be not thread safe
+because of its reliance on C&nbsp;function <code>setlocale</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.time"><code>os.time ([table])</code></a></h3>
+
+
+<p>
+Returns the current time when called without arguments,
+or a time representing the local date and time specified by the given table.
+This table must have fields <code>year</code>, <code>month</code>, and <code>day</code>,
+and may have fields
+<code>hour</code> (default is 12),
+<code>min</code> (default is 0),
+<code>sec</code> (default is 0),
+and <code>isdst</code> (default is <b>nil</b>).
+Other fields are ignored.
+For a description of these fields, see the <a href="#pdf-os.date"><code>os.date</code></a> function.
+
+
+<p>
+When the function is called,
+the values in these fields do not need to be inside their valid ranges.
+For instance, if <code>sec</code> is -10,
+it means 10 seconds before the time specified by the other fields;
+if <code>hour</code> is 1000,
+it means 1000 hours after the time specified by the other fields.
+
+
+<p>
+The returned value is a number, whose meaning depends on your system.
+In POSIX, Windows, and some other systems,
+this number counts the number
+of seconds since some given start time (the "epoch").
+In other systems, the meaning is not specified,
+and the number returned by <code>time</code> can be used only as an argument to
+<a href="#pdf-os.date"><code>os.date</code></a> and <a href="#pdf-os.difftime"><code>os.difftime</code></a>.
+
+
+<p>
+When called with a table,
+<code>os.time</code> also normalizes all the fields
+documented in the <a href="#pdf-os.date"><code>os.date</code></a> function,
+so that they represent the same time as before the call
+but with values inside their valid ranges.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.tmpname"><code>os.tmpname ()</code></a></h3>
+
+
+<p>
+Returns a string with a file name that can
+be used for a temporary file.
+The file must be explicitly opened before its use
+and explicitly removed when no longer needed.
+
+
+<p>
+In POSIX systems,
+this function also creates a file with that name,
+to avoid security risks.
+(Someone else might create the file with wrong permissions
+in the time between getting the name and creating the file.)
+You still have to open the file to use it
+and to remove it (even if you do not use it).
+
+
+<p>
+When possible,
+you may prefer to use <a href="#pdf-io.tmpfile"><code>io.tmpfile</code></a>,
+which automatically removes the file when the program ends.
+
+
+
+
+
+
+
+<h2>6.10 &ndash; <a name="6.10">The Debug Library</a></h2>
+
+<p>
+This library provides
+the functionality of the debug interface (<a href="#4.7">&sect;4.7</a>) to Lua programs.
+You should exert care when using this library.
+Several of its functions
+violate basic assumptions about Lua code
+(e.g., that variables local to a function
+cannot be accessed from outside;
+that userdata metatables cannot be changed by Lua code;
+that Lua programs do not crash)
+and therefore can compromise otherwise secure code.
+Moreover, some functions in this library may be slow.
+
+
+<p>
+All functions in this library are provided
+inside the <a name="pdf-debug"><code>debug</code></a> table.
+All functions that operate over a thread
+have an optional first argument which is the
+thread to operate over.
+The default is always the current thread.
+
+
+<p>
+<hr><h3><a name="pdf-debug.debug"><code>debug.debug ()</code></a></h3>
+
+
+<p>
+Enters an interactive mode with the user,
+running each string that the user enters.
+Using simple commands and other debug facilities,
+the user can inspect global and local variables,
+change their values, evaluate expressions, and so on.
+A line containing only the word <code>cont</code> finishes this function,
+so that the caller continues its execution.
+
+
+<p>
+Note that commands for <code>debug.debug</code> are not lexically nested
+within any function and so have no direct access to local variables.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.gethook"><code>debug.gethook ([thread])</code></a></h3>
+
+
+<p>
+Returns the current hook settings of the thread, as three values:
+the current hook function, the current hook mask,
+and the current hook count,
+as set by the <a href="#pdf-debug.sethook"><code>debug.sethook</code></a> function.
+
+
+<p>
+Returns <b>fail</b> if there is no active hook.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getinfo"><code>debug.getinfo ([thread,] f [, what])</code></a></h3>
+
+
+<p>
+Returns a table with information about a function.
+You can give the function directly
+or you can give a number as the value of <code>f</code>,
+which means the function running at level <code>f</code> of the call stack
+of the given thread:
+level&nbsp;0 is the current function (<code>getinfo</code> itself);
+level&nbsp;1 is the function that called <code>getinfo</code>
+(except for tail calls, which do not count in the stack);
+and so on.
+If <code>f</code> is a number greater than the number of active functions,
+then <code>getinfo</code> returns <b>fail</b>.
+
+
+<p>
+The returned table can contain all the fields returned by <a href="#lua_getinfo"><code>lua_getinfo</code></a>,
+with the string <code>what</code> describing which fields to fill in.
+The default for <code>what</code> is to get all information available,
+except the table of valid lines.
+If present,
+the option '<code>f</code>'
+adds a field named <code>func</code> with the function itself.
+If present,
+the option '<code>L</code>'
+adds a field named <code>activelines</code> with the table of
+valid lines.
+
+
+<p>
+For instance, the expression <code>debug.getinfo(1,"n").name</code> returns
+a name for the current function,
+if a reasonable name can be found,
+and the expression <code>debug.getinfo(print)</code>
+returns a table with all available information
+about the <a href="#pdf-print"><code>print</code></a> function.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getlocal"><code>debug.getlocal ([thread,] f, local)</code></a></h3>
+
+
+<p>
+This function returns the name and the value of the local variable
+with index <code>local</code> of the function at level <code>f</code> of the stack.
+This function accesses not only explicit local variables,
+but also parameters and temporary values.
+
+
+<p>
+The first parameter or local variable has index&nbsp;1, and so on,
+following the order that they are declared in the code,
+counting only the variables that are active
+in the current scope of the function.
+Compile-time constants may not appear in this listing,
+if they were optimized away by the compiler.
+Negative indices refer to vararg arguments;
+-1 is the first vararg argument.
+The function returns <b>fail</b>
+if there is no variable with the given index,
+and raises an error when called with a level out of range.
+(You can call <a href="#pdf-debug.getinfo"><code>debug.getinfo</code></a> to check whether the level is valid.)
+
+
+<p>
+Variable names starting with '<code>(</code>' (open parenthesis)
+represent variables with no known names
+(internal variables such as loop control variables,
+and variables from chunks saved without debug information).
+
+
+<p>
+The parameter <code>f</code> may also be a function.
+In that case, <code>getlocal</code> returns only the name of function parameters.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getmetatable"><code>debug.getmetatable (value)</code></a></h3>
+
+
+<p>
+Returns the metatable of the given <code>value</code>
+or <b>nil</b> if it does not have a metatable.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getregistry"><code>debug.getregistry ()</code></a></h3>
+
+
+<p>
+Returns the registry table (see <a href="#4.3">&sect;4.3</a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getupvalue"><code>debug.getupvalue (f, up)</code></a></h3>
+
+
+<p>
+This function returns the name and the value of the upvalue
+with index <code>up</code> of the function <code>f</code>.
+The function returns <b>fail</b>
+if there is no upvalue with the given index.
+
+
+<p>
+(For Lua functions,
+upvalues are the external local variables that the function uses,
+and that are consequently included in its closure.)
+
+
+<p>
+For C&nbsp;functions, this function uses the empty string <code>""</code>
+as a name for all upvalues.
+
+
+<p>
+Variable name '<code>?</code>' (interrogation mark)
+represents variables with no known names
+(variables from chunks saved without debug information).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getuservalue"><code>debug.getuservalue (u, n)</code></a></h3>
+
+
+<p>
+Returns the <code>n</code>-th user value associated
+to the userdata <code>u</code> plus a boolean,
+<b>false</b> if the userdata does not have that value.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.sethook"><code>debug.sethook ([thread,] hook, mask [, count])</code></a></h3>
+
+
+<p>
+Sets the given function as the debug hook.
+The string <code>mask</code> and the number <code>count</code> describe
+when the hook will be called.
+The string mask may have any combination of the following characters,
+with the given meaning:
+
+<ul>
+<li><b>'<code>c</code>': </b> the hook is called every time Lua calls a function;</li>
+<li><b>'<code>r</code>': </b> the hook is called every time Lua returns from a function;</li>
+<li><b>'<code>l</code>': </b> the hook is called every time Lua enters a new line of code.</li>
+</ul><p>
+Moreover,
+with a <code>count</code> different from zero,
+the hook is called also after every <code>count</code> instructions.
+
+
+<p>
+When called without arguments,
+<a href="#pdf-debug.sethook"><code>debug.sethook</code></a> turns off the hook.
+
+
+<p>
+When the hook is called, its first parameter is a string
+describing the event that has triggered its call:
+<code>"call"</code>, <code>"tail call"</code>, <code>"return"</code>,
+<code>"line"</code>, and <code>"count"</code>.
+For line events,
+the hook also gets the new line number as its second parameter.
+Inside a hook,
+you can call <code>getinfo</code> with level&nbsp;2 to get more information about
+the running function.
+(Level&nbsp;0 is the <code>getinfo</code> function,
+and level&nbsp;1 is the hook function.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setlocal"><code>debug.setlocal ([thread,] level, local, value)</code></a></h3>
+
+
+<p>
+This function assigns the value <code>value</code> to the local variable
+with index <code>local</code> of the function at level <code>level</code> of the stack.
+The function returns <b>fail</b> if there is no local
+variable with the given index,
+and raises an error when called with a <code>level</code> out of range.
+(You can call <code>getinfo</code> to check whether the level is valid.)
+Otherwise, it returns the name of the local variable.
+
+
+<p>
+See <a href="#pdf-debug.getlocal"><code>debug.getlocal</code></a> for more information about
+variable indices and names.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setmetatable"><code>debug.setmetatable (value, table)</code></a></h3>
+
+
+<p>
+Sets the metatable for the given <code>value</code> to the given <code>table</code>
+(which can be <b>nil</b>).
+Returns <code>value</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setupvalue"><code>debug.setupvalue (f, up, value)</code></a></h3>
+
+
+<p>
+This function assigns the value <code>value</code> to the upvalue
+with index <code>up</code> of the function <code>f</code>.
+The function returns <b>fail</b> if there is no upvalue
+with the given index.
+Otherwise, it returns the name of the upvalue.
+
+
+<p>
+See <a href="#pdf-debug.getupvalue"><code>debug.getupvalue</code></a> for more information about upvalues.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setuservalue"><code>debug.setuservalue (udata, value, n)</code></a></h3>
+
+
+<p>
+Sets the given <code>value</code> as
+the <code>n</code>-th user value associated to the given <code>udata</code>.
+<code>udata</code> must be a full userdata.
+
+
+<p>
+Returns <code>udata</code>,
+or <b>fail</b> if the userdata does not have that value.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.traceback"><code>debug.traceback ([thread,] [message [, level]])</code></a></h3>
+
+
+<p>
+If <code>message</code> is present but is neither a string nor <b>nil</b>,
+this function returns <code>message</code> without further processing.
+Otherwise,
+it returns a string with a traceback of the call stack.
+The optional <code>message</code> string is appended
+at the beginning of the traceback.
+An optional <code>level</code> number tells at which level
+to start the traceback
+(default is 1, the function calling <code>traceback</code>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.upvalueid"><code>debug.upvalueid (f, n)</code></a></h3>
+
+
+<p>
+Returns a unique identifier (as a light userdata)
+for the upvalue numbered <code>n</code>
+from the given function.
+
+
+<p>
+These unique identifiers allow a program to check whether different
+closures share upvalues.
+Lua closures that share an upvalue
+(that is, that access a same external local variable)
+will return identical ids for those upvalue indices.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.upvaluejoin"><code>debug.upvaluejoin (f1, n1, f2, n2)</code></a></h3>
+
+
+<p>
+Make the <code>n1</code>-th upvalue of the Lua closure <code>f1</code>
+refer to the <code>n2</code>-th upvalue of the Lua closure <code>f2</code>.
+
+
+
+
+
+
+
+<h1>7 &ndash; <a name="7">Lua Standalone</a></h1>
+
+<p>
+Although Lua has been designed as an extension language,
+to be embedded in a host C&nbsp;program,
+it is also frequently used as a standalone language.
+An interpreter for Lua as a standalone language,
+called simply <code>lua</code>,
+is provided with the standard distribution.
+The standalone interpreter includes
+all standard libraries.
+Its usage is:
+
+<pre>
+ lua [options] [script [args]]
+</pre><p>
+The options are:
+
+<ul>
+<li><b><code>-e <em>stat</em></code>: </b> execute string <em>stat</em>;</li>
+<li><b><code>-i</code>: </b> enter interactive mode after running <em>script</em>;</li>
+<li><b><code>-l <em>mod</em></code>: </b> "require" <em>mod</em> and assign the
+ result to global <em>mod</em>;</li>
+<li><b><code>-l <em>g=mod</em></code>: </b> "require" <em>mod</em> and assign the
+ result to global <em>g</em>;</li>
+<li><b><code>-v</code>: </b> print version information;</li>
+<li><b><code>-E</code>: </b> ignore environment variables;</li>
+<li><b><code>-W</code>: </b> turn warnings on;</li>
+<li><b><code>--</code>: </b> stop handling options;</li>
+<li><b><code>-</code>: </b> execute <code>stdin</code> as a file and stop handling options.</li>
+</ul><p>
+(The form <code>-l <em>g=mod</em></code> was introduced in release&nbsp;5.4.4.)
+
+
+<p>
+After handling its options, <code>lua</code> runs the given <em>script</em>.
+When called without arguments,
+<code>lua</code> behaves as <code>lua -v -i</code>
+when the standard input (<code>stdin</code>) is a terminal,
+and as <code>lua -</code> otherwise.
+
+
+<p>
+When called without the option <code>-E</code>,
+the interpreter checks for an environment variable <a name="pdf-LUA_INIT_5_4"><code>LUA_INIT_5_4</code></a>
+(or <a name="pdf-LUA_INIT"><code>LUA_INIT</code></a> if the versioned name is not defined)
+before running any argument.
+If the variable content has the format <code>@<em>filename</em></code>,
+then <code>lua</code> executes the file.
+Otherwise, <code>lua</code> executes the string itself.
+
+
+<p>
+When called with the option <code>-E</code>,
+Lua does not consult any environment variables.
+In particular,
+the values of <a href="#pdf-package.path"><code>package.path</code></a> and <a href="#pdf-package.cpath"><code>package.cpath</code></a>
+are set with the default paths defined in <code>luaconf.h</code>.
+
+
+<p>
+The options <code>-e</code>, <code>-l</code>, and <code>-W</code> are handled in
+the order they appear.
+For instance, an invocation like
+
+<pre>
+ $ lua -e 'a=1' -llib1 script.lua
+</pre><p>
+will first set <code>a</code> to 1, then require the library <code>lib1</code>,
+and finally run the file <code>script.lua</code> with no arguments.
+(Here <code>$</code> is the shell prompt. Your prompt may be different.)
+
+
+<p>
+Before running any code,
+<code>lua</code> collects all command-line arguments
+in a global table called <code>arg</code>.
+The script name goes to index 0,
+the first argument after the script name goes to index 1,
+and so on.
+Any arguments before the script name
+(that is, the interpreter name plus its options)
+go to negative indices.
+For instance, in the call
+
+<pre>
+ $ lua -la b.lua t1 t2
+</pre><p>
+the table is like this:
+
+<pre>
+ arg = { [-2] = "lua", [-1] = "-la",
+ [0] = "b.lua",
+ [1] = "t1", [2] = "t2" }
+</pre><p>
+If there is no script in the call,
+the interpreter name goes to index 0,
+followed by the other arguments.
+For instance, the call
+
+<pre>
+ $ lua -e "print(arg[1])"
+</pre><p>
+will print "<code>-e</code>".
+If there is a script,
+the script is called with arguments
+<code>arg[1]</code>, &middot;&middot;&middot;, <code>arg[#arg]</code>.
+Like all chunks in Lua,
+the script is compiled as a variadic function.
+
+
+<p>
+In interactive mode,
+Lua repeatedly prompts and waits for a line.
+After reading a line,
+Lua first try to interpret the line as an expression.
+If it succeeds, it prints its value.
+Otherwise, it interprets the line as a statement.
+If you write an incomplete statement,
+the interpreter waits for its completion
+by issuing a different prompt.
+
+
+<p>
+If the global variable <a name="pdf-_PROMPT"><code>_PROMPT</code></a> contains a string,
+then its value is used as the prompt.
+Similarly, if the global variable <a name="pdf-_PROMPT2"><code>_PROMPT2</code></a> contains a string,
+its value is used as the secondary prompt
+(issued during incomplete statements).
+
+
+<p>
+In case of unprotected errors in the script,
+the interpreter reports the error to the standard error stream.
+If the error object is not a string but
+has a metamethod <code>__tostring</code>,
+the interpreter calls this metamethod to produce the final message.
+Otherwise, the interpreter converts the error object to a string
+and adds a stack traceback to it.
+When warnings are on,
+they are simply printed in the standard error output.
+
+
+<p>
+When finishing normally,
+the interpreter closes its main Lua state
+(see <a href="#lua_close"><code>lua_close</code></a>).
+The script can avoid this step by
+calling <a href="#pdf-os.exit"><code>os.exit</code></a> to terminate.
+
+
+<p>
+To allow the use of Lua as a
+script interpreter in Unix systems,
+Lua skips the first line of a file chunk if it starts with <code>#</code>.
+Therefore, Lua scripts can be made into executable programs
+by using <code>chmod +x</code> and the&nbsp;<code>#!</code> form,
+as in
+
+<pre>
+ #!/usr/local/bin/lua
+</pre><p>
+Of course,
+the location of the Lua interpreter may be different in your machine.
+If <code>lua</code> is in your <code>PATH</code>,
+then
+
+<pre>
+ #!/usr/bin/env lua
+</pre><p>
+is a more portable solution.
+
+
+
+<h1>8 &ndash; <a name="8">Incompatibilities with the Previous Version</a></h1>
+
+
+
+<p>
+Here we list the incompatibilities that you may find when moving a program
+from Lua&nbsp;5.3 to Lua&nbsp;5.4.
+
+
+<p>
+You can avoid some incompatibilities by compiling Lua with
+appropriate options (see file <code>luaconf.h</code>).
+However,
+all these compatibility options will be removed in the future.
+More often than not,
+compatibility issues arise when these compatibility options
+are removed.
+So, whenever you have the chance,
+you should try to test your code with a version of Lua compiled
+with all compatibility options turned off.
+That will ease transitions to newer versions of Lua.
+
+
+<p>
+Lua versions can always change the C API in ways that
+do not imply source-code changes in a program,
+such as the numeric values for constants
+or the implementation of functions as macros.
+Therefore,
+you should never assume that binaries are compatible between
+different Lua versions.
+Always recompile clients of the Lua API when
+using a new version.
+
+
+<p>
+Similarly, Lua versions can always change the internal representation
+of precompiled chunks;
+precompiled chunks are not compatible between different Lua versions.
+
+
+<p>
+The standard paths in the official distribution may
+change between versions.
+
+
+
+
+
+<h2>8.1 &ndash; <a name="8.1">Incompatibilities in the Language</a></h2>
+<ul>
+
+<li>
+The coercion of strings to numbers in
+arithmetic and bitwise operations
+has been removed from the core language.
+The string library does a similar job
+for arithmetic (but not for bitwise) operations
+using the string metamethods.
+However, unlike in previous versions,
+the new implementation preserves the implicit type of the numeral
+in the string.
+For instance, the result of <code>"1" + "2"</code> now is an integer,
+not a float.
+</li>
+
+<li>
+Literal decimal integer constants that overflow are read as floats,
+instead of wrapping around.
+You can use hexadecimal notation for such constants if you
+want the old behavior
+(reading them as integers with wrap around).
+</li>
+
+<li>
+The use of the <code>__lt</code> metamethod to emulate <code>__le</code>
+has been removed.
+When needed, this metamethod must be explicitly defined.
+</li>
+
+<li>
+The semantics of the numerical <b>for</b> loop
+over integers changed in some details.
+In particular, the control variable never wraps around.
+</li>
+
+<li>
+A label for a <b>goto</b> cannot be declared where a label with the same
+name is visible, even if this other label is declared in an enclosing
+block.
+</li>
+
+<li>
+When finalizing an object,
+Lua does not ignore <code>__gc</code> metamethods that are not functions.
+Any value will be called, if present.
+(Non-callable values will generate a warning,
+like any other error when calling a finalizer.)
+</li>
+
+</ul>
+
+
+
+
+<h2>8.2 &ndash; <a name="8.2">Incompatibilities in the Libraries</a></h2>
+<ul>
+
+<li>
+The function <a href="#pdf-print"><code>print</code></a> does not call <a href="#pdf-tostring"><code>tostring</code></a>
+to format its arguments;
+instead, it has this functionality hardwired.
+You should use <code>__tostring</code> to modify how values are printed.
+</li>
+
+<li>
+The pseudo-random number generator used by the function <a href="#pdf-math.random"><code>math.random</code></a>
+now starts with a somewhat random seed.
+Moreover, it uses a different algorithm.
+</li>
+
+<li>
+By default, the decoding functions in the <a href="#pdf-utf8"><code>utf8</code></a> library
+do not accept surrogates as valid code points.
+An extra parameter in these functions makes them more permissive.
+</li>
+
+<li>
+The options "<code>setpause</code>" and "<code>setstepmul</code>"
+of the function <a href="#pdf-collectgarbage"><code>collectgarbage</code></a> are deprecated.
+You should use the new option "<code>incremental</code>" to set them.
+</li>
+
+<li>
+The function <a href="#pdf-io.lines"><code>io.lines</code></a> now returns four values,
+instead of just one.
+That can be a problem when it is used as the sole
+argument to another function that has optional parameters,
+such as in <code>load(io.lines(filename, "L"))</code>.
+To fix that issue,
+you can wrap the call into parentheses,
+to adjust its number of results to one.
+</li>
+
+</ul>
+
+
+
+
+<h2>8.3 &ndash; <a name="8.3">Incompatibilities in the API</a></h2>
+
+
+<ul>
+
+<li>
+Full userdata now has an arbitrary number of associated user values.
+Therefore, the functions <code>lua_newuserdata</code>,
+<code>lua_setuservalue</code>, and <code>lua_getuservalue</code> were
+replaced by <a href="#lua_newuserdatauv"><code>lua_newuserdatauv</code></a>,
+<a href="#lua_setiuservalue"><code>lua_setiuservalue</code></a>, and <a href="#lua_getiuservalue"><code>lua_getiuservalue</code></a>,
+which have an extra argument.
+
+
+<p>
+For compatibility, the old names still work as macros assuming
+one single user value.
+Note, however, that userdata with zero user values
+are more efficient memory-wise.
+</li>
+
+<li>
+The function <a href="#lua_resume"><code>lua_resume</code></a> has an extra parameter.
+This out parameter returns the number of values on
+the top of the stack that were yielded or returned by the coroutine.
+(In previous versions,
+those values were the entire stack.)
+</li>
+
+<li>
+The function <a href="#lua_version"><code>lua_version</code></a> returns the version number,
+instead of an address of the version number.
+The Lua core should work correctly with libraries using their
+own static copies of the same core,
+so there is no need to check whether they are using the same
+address space.
+</li>
+
+<li>
+The constant <code>LUA_ERRGCMM</code> was removed.
+Errors in finalizers are never propagated;
+instead, they generate a warning.
+</li>
+
+<li>
+The options <code>LUA_GCSETPAUSE</code> and <code>LUA_GCSETSTEPMUL</code>
+of the function <a href="#lua_gc"><code>lua_gc</code></a> are deprecated.
+You should use the new option <code>LUA_GCINC</code> to set them.
+</li>
+
+</ul>
+
+
+
+
+<h1>9 &ndash; <a name="9">The Complete Syntax of Lua</a></h1>
+
+<p>
+Here is the complete syntax of Lua in extended BNF.
+As usual in extended BNF,
+{A} means 0 or more As,
+and [A] means an optional A.
+(For operator precedences, see <a href="#3.4.8">&sect;3.4.8</a>;
+for a description of the terminals
+Name, Numeral,
+and LiteralString, see <a href="#3.1">&sect;3.1</a>.)
+
+
+
+
+<pre>
+
+ chunk ::= block
+
+ block ::= {stat} [retstat]
+
+ stat ::= &lsquo;<b>;</b>&rsquo; |
+ varlist &lsquo;<b>=</b>&rsquo; explist |
+ functioncall |
+ label |
+ <b>break</b> |
+ <b>goto</b> Name |
+ <b>do</b> block <b>end</b> |
+ <b>while</b> exp <b>do</b> block <b>end</b> |
+ <b>repeat</b> block <b>until</b> exp |
+ <b>if</b> exp <b>then</b> block {<b>elseif</b> exp <b>then</b> block} [<b>else</b> block] <b>end</b> |
+ <b>for</b> Name &lsquo;<b>=</b>&rsquo; exp &lsquo;<b>,</b>&rsquo; exp [&lsquo;<b>,</b>&rsquo; exp] <b>do</b> block <b>end</b> |
+ <b>for</b> namelist <b>in</b> explist <b>do</b> block <b>end</b> |
+ <b>function</b> funcname funcbody |
+ <b>local</b> <b>function</b> Name funcbody |
+ <b>local</b> attnamelist [&lsquo;<b>=</b>&rsquo; explist]
+
+ attnamelist ::= Name attrib {&lsquo;<b>,</b>&rsquo; Name attrib}
+
+ attrib ::= [&lsquo;<b>&lt;</b>&rsquo; Name &lsquo;<b>&gt;</b>&rsquo;]
+
+ retstat ::= <b>return</b> [explist] [&lsquo;<b>;</b>&rsquo;]
+
+ label ::= &lsquo;<b>::</b>&rsquo; Name &lsquo;<b>::</b>&rsquo;
+
+ funcname ::= Name {&lsquo;<b>.</b>&rsquo; Name} [&lsquo;<b>:</b>&rsquo; Name]
+
+ varlist ::= var {&lsquo;<b>,</b>&rsquo; var}
+
+ var ::= Name | prefixexp &lsquo;<b>[</b>&rsquo; exp &lsquo;<b>]</b>&rsquo; | prefixexp &lsquo;<b>.</b>&rsquo; Name
+
+ namelist ::= Name {&lsquo;<b>,</b>&rsquo; Name}
+
+ explist ::= exp {&lsquo;<b>,</b>&rsquo; exp}
+
+ exp ::= <b>nil</b> | <b>false</b> | <b>true</b> | Numeral | LiteralString | &lsquo;<b>...</b>&rsquo; | functiondef |
+ prefixexp | tableconstructor | exp binop exp | unop exp
+
+ prefixexp ::= var | functioncall | &lsquo;<b>(</b>&rsquo; exp &lsquo;<b>)</b>&rsquo;
+
+ functioncall ::= prefixexp args | prefixexp &lsquo;<b>:</b>&rsquo; Name args
+
+ args ::= &lsquo;<b>(</b>&rsquo; [explist] &lsquo;<b>)</b>&rsquo; | tableconstructor | LiteralString
+
+ functiondef ::= <b>function</b> funcbody
+
+ funcbody ::= &lsquo;<b>(</b>&rsquo; [parlist] &lsquo;<b>)</b>&rsquo; block <b>end</b>
+
+ parlist ::= namelist [&lsquo;<b>,</b>&rsquo; &lsquo;<b>...</b>&rsquo;] | &lsquo;<b>...</b>&rsquo;
+
+ tableconstructor ::= &lsquo;<b>{</b>&rsquo; [fieldlist] &lsquo;<b>}</b>&rsquo;
+
+ fieldlist ::= field {fieldsep field} [fieldsep]
+
+ field ::= &lsquo;<b>[</b>&rsquo; exp &lsquo;<b>]</b>&rsquo; &lsquo;<b>=</b>&rsquo; exp | Name &lsquo;<b>=</b>&rsquo; exp | exp
+
+ fieldsep ::= &lsquo;<b>,</b>&rsquo; | &lsquo;<b>;</b>&rsquo;
+
+ binop ::= &lsquo;<b>+</b>&rsquo; | &lsquo;<b>-</b>&rsquo; | &lsquo;<b>*</b>&rsquo; | &lsquo;<b>/</b>&rsquo; | &lsquo;<b>//</b>&rsquo; | &lsquo;<b>^</b>&rsquo; | &lsquo;<b>%</b>&rsquo; |
+ &lsquo;<b>&amp;</b>&rsquo; | &lsquo;<b>~</b>&rsquo; | &lsquo;<b>|</b>&rsquo; | &lsquo;<b>&gt;&gt;</b>&rsquo; | &lsquo;<b>&lt;&lt;</b>&rsquo; | &lsquo;<b>..</b>&rsquo; |
+ &lsquo;<b>&lt;</b>&rsquo; | &lsquo;<b>&lt;=</b>&rsquo; | &lsquo;<b>&gt;</b>&rsquo; | &lsquo;<b>&gt;=</b>&rsquo; | &lsquo;<b>==</b>&rsquo; | &lsquo;<b>~=</b>&rsquo; |
+ <b>and</b> | <b>or</b>
+
+ unop ::= &lsquo;<b>-</b>&rsquo; | <b>not</b> | &lsquo;<b>#</b>&rsquo; | &lsquo;<b>~</b>&rsquo;
+
+</pre>
+
+<p>
+
+
+
+
+
+
+
+<P CLASS="footer">
+Last update:
+Tue May 2 20:09:38 UTC 2023
+</P>
+<!--
+Last change: revised for Lua 5.4.6
+-->
+
+</body></html>
+
diff --git a/src/libs/3rdparty/lua/doc/osi-certified-72x60.png b/src/libs/3rdparty/lua/doc/osi-certified-72x60.png
new file mode 100644
index 0000000000..07df5f6ee7
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/osi-certified-72x60.png
Binary files differ
diff --git a/src/libs/3rdparty/lua/doc/readme.html b/src/libs/3rdparty/lua/doc/readme.html
new file mode 100644
index 0000000000..918ec8ed93
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/readme.html
@@ -0,0 +1,337 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML>
+<HEAD>
+<TITLE>Lua 5.4 readme</TITLE>
+<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
+<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE TYPE="text/css">
+blockquote, .display {
+ border: solid #a0a0a0 2px ;
+ border-radius: 8px ;
+ padding: 1em ;
+ margin: 0px ;
+}
+
+.display {
+ word-spacing: 0.25em ;
+}
+
+dl.display dd {
+ padding-bottom: 0.2em ;
+}
+
+tt, kbd, code {
+ font-size: 12pt ;
+}
+</STYLE>
+</HEAD>
+
+<BODY>
+
+<H1>
+<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
+Welcome to Lua 5.4
+</H1>
+
+<DIV CLASS="menubar">
+<A HREF="#about">about</A>
+&middot;
+<A HREF="#install">installation</A>
+&middot;
+<A HREF="#changes">changes</A>
+&middot;
+<A HREF="#license">license</A>
+&middot;
+<A HREF="contents.html">reference manual</A>
+</DIV>
+
+<H2><A NAME="about">About Lua</A></H2>
+<P>
+Lua is a powerful, efficient, lightweight, embeddable scripting language
+developed by a
+<A HREF="http://www.lua.org/authors.html">team</A>
+at
+<A HREF="http://www.puc-rio.br/">PUC-Rio</A>,
+the Pontifical Catholic University of Rio de Janeiro in Brazil.
+Lua is
+<A HREF="#license">free software</A>
+used in
+<A HREF="http://www.lua.org/uses.html">many products and projects</A>
+around the world.
+
+<P>
+Lua's
+<A HREF="http://www.lua.org/">official web site</A>
+provides complete information
+about Lua,
+including
+an
+<A HREF="http://www.lua.org/about.html">executive summary</A>
+and
+updated
+<A HREF="http://www.lua.org/docs.html">documentation</A>,
+especially the
+<A HREF="http://www.lua.org/manual/5.4/">reference manual</A>,
+which may differ slightly from the
+<A HREF="contents.html">local copy</A>
+distributed in this package.
+
+<H2><A NAME="install">Installing Lua</A></H2>
+<P>
+Lua is distributed in
+<A HREF="http://www.lua.org/ftp/">source</A>
+form.
+You need to build it before using it.
+Building Lua should be straightforward
+because
+Lua is implemented in pure ANSI C and compiles unmodified in all known
+platforms that have an ANSI C compiler.
+Lua also compiles unmodified as C++.
+The instructions given below for building Lua are for Unix-like platforms,
+such as Linux and Mac OS X.
+See also
+<A HREF="#other">instructions for other systems</A>
+and
+<A HREF="#customization">customization options</A>.
+
+<P>
+If you don't have the time or the inclination to compile Lua yourself,
+get a binary from
+<A HREF="http://lua-users.org/wiki/LuaBinaries">LuaBinaries</A>.
+
+<H3>Building Lua</H3>
+<P>
+In most common Unix-like platforms, simply do "<KBD>make</KBD>".
+Here are the details.
+
+<OL>
+<LI>
+Open a terminal window and move to
+the top-level directory, which is named <TT>lua-5.4.6</TT>.
+The <TT>Makefile</TT> there controls both the build process and the installation process.
+<P>
+<LI>
+ Do "<KBD>make</KBD>". The <TT>Makefile</TT> will guess your platform and build Lua for it.
+<P>
+<LI>
+ If the guess failed, do "<KBD>make help</KBD>" and see if your platform is listed.
+ The platforms currently supported are:
+<P>
+<P CLASS="display">
+ guess aix bsd c89 freebsd generic ios linux linux-readline macosx mingw posix solaris
+</P>
+<P>
+ If your platform is listed, just do "<KBD>make xxx</KBD>", where xxx
+ is your platform name.
+<P>
+ If your platform is not listed, try the closest one or posix, generic,
+ c89, in this order.
+<P>
+<LI>
+The compilation takes only a few moments
+and produces three files in the <TT>src</TT> directory:
+lua (the interpreter),
+luac (the compiler),
+and liblua.a (the library).
+<P>
+<LI>
+ To check that Lua has been built correctly, do "<KBD>make test</KBD>"
+ after building Lua. This will run the interpreter and print its version.
+</OL>
+<P>
+If you're running Linux, try "<KBD>make linux-readline</KBD>" to build the interactive Lua interpreter with handy line-editing and history capabilities.
+If you get compilation errors,
+make sure you have installed the <TT>readline</TT> development package
+(which is probably named <TT>libreadline-dev</TT> or <TT>readline-devel</TT>).
+If you get link errors after that,
+then try "<KBD>make linux-readline MYLIBS=-ltermcap</KBD>".
+
+<H3>Installing Lua</H3>
+<P>
+ Once you have built Lua, you may want to install it in an official
+ place in your system. In this case, do "<KBD>make install</KBD>". The official
+ place and the way to install files are defined in the <TT>Makefile</TT>. You'll
+ probably need the right permissions to install files, and so may need to do "<KBD>sudo make install</KBD>".
+
+<P>
+ To build and install Lua in one step, do "<KBD>make all install</KBD>",
+ or "<KBD>make xxx install</KBD>",
+ where xxx is your platform name.
+
+<P>
+ To install Lua locally after building it, do "<KBD>make local</KBD>".
+ This will create a directory <TT>install</TT> with subdirectories
+ <TT>bin</TT>, <TT>include</TT>, <TT>lib</TT>, <TT>man</TT>, <TT>share</TT>,
+ and install Lua as listed below.
+
+ To install Lua locally, but in some other directory, do
+ "<KBD>make install INSTALL_TOP=xxx</KBD>", where xxx is your chosen directory.
+ The installation starts in the <TT>src</TT> and <TT>doc</TT> directories,
+ so take care if <TT>INSTALL_TOP</TT> is not an absolute path.
+
+<DL CLASS="display">
+<DT>
+ bin:
+<DD>
+ lua luac
+<DT>
+ include:
+<DD>
+ lua.h luaconf.h lualib.h lauxlib.h lua.hpp
+<DT>
+ lib:
+<DD>
+ liblua.a
+<DT>
+ man/man1:
+<DD>
+ lua.1 luac.1
+</DL>
+
+<P>
+ These are the only directories you need for development.
+ If you only want to run Lua programs,
+ you only need the files in <TT>bin</TT> and <TT>man</TT>.
+ The files in <TT>include</TT> and <TT>lib</TT> are needed for
+ embedding Lua in C or C++ programs.
+
+<H3><A NAME="customization">Customization</A></H3>
+<P>
+ Three kinds of things can be customized by editing a file:
+<UL>
+ <LI> Where and how to install Lua &mdash; edit <TT>Makefile</TT>.
+ <LI> How to build Lua &mdash; edit <TT>src/Makefile</TT>.
+ <LI> Lua features &mdash; edit <TT>src/luaconf.h</TT>.
+</UL>
+
+<P>
+ You don't actually need to edit the Makefiles because you may set the
+ relevant variables in the command line when invoking make.
+ Nevertheless, it's probably best to edit and save the Makefiles to
+ record the changes you've made.
+
+<P>
+ On the other hand, if you need to customize some Lua features, you'll need
+ to edit <TT>src/luaconf.h</TT> before building and installing Lua.
+ The edited file will be the one installed, and
+ it will be used by any Lua clients that you build, to ensure consistency.
+ Further customization is available to experts by editing the Lua sources.
+
+<H3><A NAME="other">Building Lua on other systems</A></H3>
+<P>
+ If you're not using the usual Unix tools, then the instructions for
+ building Lua depend on the compiler you use. You'll need to create
+ projects (or whatever your compiler uses) for building the library,
+ the interpreter, and the compiler, as follows:
+
+<DL CLASS="display">
+<DT>
+library:
+<DD>
+lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c
+lauxlib.c lbaselib.c lcorolib.c ldblib.c liolib.c lmathlib.c loadlib.c loslib.c lstrlib.c ltablib.c lutf8lib.c linit.c
+<DT>
+interpreter:
+<DD>
+ library, lua.c
+<DT>
+compiler:
+<DD>
+ library, luac.c
+</DL>
+
+<P>
+ To use Lua as a library in your own programs, you'll need to know how to
+ create and use libraries with your compiler. Moreover, to dynamically load
+ C libraries for Lua, you'll need to know how to create dynamic libraries
+ and you'll need to make sure that the Lua API functions are accessible to
+ those dynamic libraries &mdash; but <EM>don't</EM> link the Lua library
+ into each dynamic library. For Unix, we recommend that the Lua library
+ be linked statically into the host program and its symbols exported for
+ dynamic linking; <TT>src/Makefile</TT> does this for the Lua interpreter.
+ For Windows, we recommend that the Lua library be a DLL.
+ In all cases, the compiler luac should be linked statically.
+
+<P>
+ As mentioned above, you may edit <TT>src/luaconf.h</TT> to customize
+ some features before building Lua.
+
+<H2><A NAME="changes">Changes since Lua 5.3</A></H2>
+<P>
+Here are the main changes introduced in Lua 5.4.
+The
+<A HREF="contents.html">reference manual</A>
+lists the
+<A HREF="manual.html#8">incompatibilities</A> that had to be introduced.
+
+<H3>Main changes</H3>
+<UL>
+<LI> new generational mode for garbage collection
+<LI> to-be-closed variables
+<LI> const variables
+<LI> userdata can have multiple user values
+<LI> new implementation for math.random
+<LI> warning system
+<LI> debug information about function arguments and returns
+<LI> new semantics for the integer 'for' loop
+<LI> optional 'init' argument to 'string.gmatch'
+<LI> new functions 'lua_resetthread' and 'coroutine.close'
+<LI> string-to-number coercions moved to the string library
+<LI> allocation function allowed to fail when shrinking a memory block
+<LI> new format '%p' in 'string.format'
+<LI> utf8 library accepts codepoints up to 2^31
+</UL>
+
+<H2><A NAME="license">License</A></H2>
+<P>
+<A HREF="http://www.opensource.org/docs/definition.php">
+<IMG SRC="osi-certified-72x60.png" ALIGN="right" ALT="[osi certified]" STYLE="padding-left: 30px ;">
+</A>
+Lua is free software distributed under the terms of the
+<A HREF="http://www.opensource.org/licenses/mit-license.html">MIT license</A>
+reproduced below;
+it may be used for any purpose, including commercial purposes,
+at absolutely no cost without having to ask us.
+
+The only requirement is that if you do use Lua,
+then you should give us credit by including the appropriate copyright notice somewhere in your product or its documentation.
+
+For details, see
+<A HREF="http://www.lua.org/license.html">this</A>.
+
+<BLOCKQUOTE STYLE="padding-bottom: 0em">
+Copyright &copy; 1994&ndash;2023 Lua.org, PUC-Rio.
+
+<P>
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+<P>
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+<P>
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+</BLOCKQUOTE>
+<P>
+
+<P CLASS="footer">
+Last update:
+Tue May 2 20:08:55 UTC 2023
+</P>
+<!--
+Last change: revised for Lua 5.4.6
+-->
+
+</BODY>
+</HTML>
diff --git a/src/libs/3rdparty/lua/src/lapi.c b/src/libs/3rdparty/lua/src/lapi.c
new file mode 100644
index 0000000000..34e64af142
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lapi.c
@@ -0,0 +1,1463 @@
+/*
+** $Id: lapi.c $
+** Lua API
+** See Copyright Notice in lua.h
+*/
+
+#define lapi_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <limits.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lundump.h"
+#include "lvm.h"
+
+
+
+const char lua_ident[] =
+ "$LuaVersion: " LUA_COPYRIGHT " $"
+ "$LuaAuthors: " LUA_AUTHORS " $";
+
+
+
+/*
+** Test for a valid index (one that is not the 'nilvalue').
+** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed.
+** However, it covers the most common cases in a faster way.
+*/
+#define isvalid(L, o) (!ttisnil(o) || o != &G(L)->nilvalue)
+
+
+/* test for pseudo index */
+#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX)
+
+/* test for upvalue */
+#define isupvalue(i) ((i) < LUA_REGISTRYINDEX)
+
+
+/*
+** Convert an acceptable index to a pointer to its respective value.
+** Non-valid indices return the special nil value 'G(L)->nilvalue'.
+*/
+static TValue *index2value (lua_State *L, int idx) {
+ CallInfo *ci = L->ci;
+ if (idx > 0) {
+ StkId o = ci->func.p + idx;
+ api_check(L, idx <= ci->top.p - (ci->func.p + 1), "unacceptable index");
+ if (o >= L->top.p) return &G(L)->nilvalue;
+ else return s2v(o);
+ }
+ else if (!ispseudo(idx)) { /* negative index */
+ api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
+ "invalid index");
+ return s2v(L->top.p + idx);
+ }
+ else if (idx == LUA_REGISTRYINDEX)
+ return &G(L)->l_registry;
+ else { /* upvalues */
+ idx = LUA_REGISTRYINDEX - idx;
+ api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
+ if (ttisCclosure(s2v(ci->func.p))) { /* C closure? */
+ CClosure *func = clCvalue(s2v(ci->func.p));
+ return (idx <= func->nupvalues) ? &func->upvalue[idx-1]
+ : &G(L)->nilvalue;
+ }
+ else { /* light C function or Lua function (through a hook)?) */
+ api_check(L, ttislcf(s2v(ci->func.p)), "caller not a C function");
+ return &G(L)->nilvalue; /* no upvalues */
+ }
+ }
+}
+
+
+
+/*
+** Convert a valid actual index (not a pseudo-index) to its address.
+*/
+l_sinline StkId index2stack (lua_State *L, int idx) {
+ CallInfo *ci = L->ci;
+ if (idx > 0) {
+ StkId o = ci->func.p + idx;
+ api_check(L, o < L->top.p, "invalid index");
+ return o;
+ }
+ else { /* non-positive index */
+ api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
+ "invalid index");
+ api_check(L, !ispseudo(idx), "invalid index");
+ return L->top.p + idx;
+ }
+}
+
+
+LUA_API int lua_checkstack (lua_State *L, int n) {
+ int res;
+ CallInfo *ci;
+ lua_lock(L);
+ ci = L->ci;
+ api_check(L, n >= 0, "negative 'n'");
+ if (L->stack_last.p - L->top.p > n) /* stack large enough? */
+ res = 1; /* yes; check is OK */
+ else /* need to grow stack */
+ res = luaD_growstack(L, n, 0);
+ if (res && ci->top.p < L->top.p + n)
+ ci->top.p = L->top.p + n; /* adjust frame top */
+ lua_unlock(L);
+ return res;
+}
+
+
+LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {
+ int i;
+ if (from == to) return;
+ lua_lock(to);
+ api_checknelems(from, n);
+ api_check(from, G(from) == G(to), "moving among independent states");
+ api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow");
+ from->top.p -= n;
+ for (i = 0; i < n; i++) {
+ setobjs2s(to, to->top.p, from->top.p + i);
+ to->top.p++; /* stack already checked by previous 'api_check' */
+ }
+ lua_unlock(to);
+}
+
+
+LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {
+ lua_CFunction old;
+ lua_lock(L);
+ old = G(L)->panic;
+ G(L)->panic = panicf;
+ lua_unlock(L);
+ return old;
+}
+
+
+LUA_API lua_Number lua_version (lua_State *L) {
+ UNUSED(L);
+ return LUA_VERSION_NUM;
+}
+
+
+
+/*
+** basic stack manipulation
+*/
+
+
+/*
+** convert an acceptable stack index into an absolute index
+*/
+LUA_API int lua_absindex (lua_State *L, int idx) {
+ return (idx > 0 || ispseudo(idx))
+ ? idx
+ : cast_int(L->top.p - L->ci->func.p) + idx;
+}
+
+
+LUA_API int lua_gettop (lua_State *L) {
+ return cast_int(L->top.p - (L->ci->func.p + 1));
+}
+
+
+LUA_API void lua_settop (lua_State *L, int idx) {
+ CallInfo *ci;
+ StkId func, newtop;
+ ptrdiff_t diff; /* difference for new top */
+ lua_lock(L);
+ ci = L->ci;
+ func = ci->func.p;
+ if (idx >= 0) {
+ api_check(L, idx <= ci->top.p - (func + 1), "new top too large");
+ diff = ((func + 1) + idx) - L->top.p;
+ for (; diff > 0; diff--)
+ setnilvalue(s2v(L->top.p++)); /* clear new slots */
+ }
+ else {
+ api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top");
+ diff = idx + 1; /* will "subtract" index (as it is negative) */
+ }
+ api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot");
+ newtop = L->top.p + diff;
+ if (diff < 0 && L->tbclist.p >= newtop) {
+ lua_assert(hastocloseCfunc(ci->nresults));
+ newtop = luaF_close(L, newtop, CLOSEKTOP, 0);
+ }
+ L->top.p = newtop; /* correct top only after closing any upvalue */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_closeslot (lua_State *L, int idx) {
+ StkId level;
+ lua_lock(L);
+ level = index2stack(L, idx);
+ api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level,
+ "no variable to close at given level");
+ level = luaF_close(L, level, CLOSEKTOP, 0);
+ setnilvalue(s2v(level));
+ lua_unlock(L);
+}
+
+
+/*
+** Reverse the stack segment from 'from' to 'to'
+** (auxiliary to 'lua_rotate')
+** Note that we move(copy) only the value inside the stack.
+** (We do not move additional fields that may exist.)
+*/
+l_sinline void reverse (lua_State *L, StkId from, StkId to) {
+ for (; from < to; from++, to--) {
+ TValue temp;
+ setobj(L, &temp, s2v(from));
+ setobjs2s(L, from, to);
+ setobj2s(L, to, &temp);
+ }
+}
+
+
+/*
+** Let x = AB, where A is a prefix of length 'n'. Then,
+** rotate x n == BA. But BA == (A^r . B^r)^r.
+*/
+LUA_API void lua_rotate (lua_State *L, int idx, int n) {
+ StkId p, t, m;
+ lua_lock(L);
+ t = L->top.p - 1; /* end of stack segment being rotated */
+ p = index2stack(L, idx); /* start of segment */
+ api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'");
+ m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */
+ reverse(L, p, m); /* reverse the prefix with length 'n' */
+ reverse(L, m + 1, t); /* reverse the suffix */
+ reverse(L, p, t); /* reverse the entire segment */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) {
+ TValue *fr, *to;
+ lua_lock(L);
+ fr = index2value(L, fromidx);
+ to = index2value(L, toidx);
+ api_check(L, isvalid(L, to), "invalid index");
+ setobj(L, to, fr);
+ if (isupvalue(toidx)) /* function upvalue? */
+ luaC_barrier(L, clCvalue(s2v(L->ci->func.p)), fr);
+ /* LUA_REGISTRYINDEX does not need gc barrier
+ (collector revisits it before finishing collection) */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushvalue (lua_State *L, int idx) {
+ lua_lock(L);
+ setobj2s(L, L->top.p, index2value(L, idx));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+
+/*
+** access functions (stack -> C)
+*/
+
+
+LUA_API int lua_type (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return (isvalid(L, o) ? ttype(o) : LUA_TNONE);
+}
+
+
+LUA_API const char *lua_typename (lua_State *L, int t) {
+ UNUSED(L);
+ api_check(L, LUA_TNONE <= t && t < LUA_NUMTYPES, "invalid type");
+ return ttypename(t);
+}
+
+
+LUA_API int lua_iscfunction (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return (ttislcf(o) || (ttisCclosure(o)));
+}
+
+
+LUA_API int lua_isinteger (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return ttisinteger(o);
+}
+
+
+LUA_API int lua_isnumber (lua_State *L, int idx) {
+ lua_Number n;
+ const TValue *o = index2value(L, idx);
+ return tonumber(o, &n);
+}
+
+
+LUA_API int lua_isstring (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return (ttisstring(o) || cvt2str(o));
+}
+
+
+LUA_API int lua_isuserdata (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return (ttisfulluserdata(o) || ttislightuserdata(o));
+}
+
+
+LUA_API int lua_rawequal (lua_State *L, int index1, int index2) {
+ const TValue *o1 = index2value(L, index1);
+ const TValue *o2 = index2value(L, index2);
+ return (isvalid(L, o1) && isvalid(L, o2)) ? luaV_rawequalobj(o1, o2) : 0;
+}
+
+
+LUA_API void lua_arith (lua_State *L, int op) {
+ lua_lock(L);
+ if (op != LUA_OPUNM && op != LUA_OPBNOT)
+ api_checknelems(L, 2); /* all other operations expect two operands */
+ else { /* for unary operations, add fake 2nd operand */
+ api_checknelems(L, 1);
+ setobjs2s(L, L->top.p, L->top.p - 1);
+ api_incr_top(L);
+ }
+ /* first operand at top - 2, second at top - 1; result go to top - 2 */
+ luaO_arith(L, op, s2v(L->top.p - 2), s2v(L->top.p - 1), L->top.p - 2);
+ L->top.p--; /* remove second operand */
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) {
+ const TValue *o1;
+ const TValue *o2;
+ int i = 0;
+ lua_lock(L); /* may call tag method */
+ o1 = index2value(L, index1);
+ o2 = index2value(L, index2);
+ if (isvalid(L, o1) && isvalid(L, o2)) {
+ switch (op) {
+ case LUA_OPEQ: i = luaV_equalobj(L, o1, o2); break;
+ case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break;
+ case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break;
+ default: api_check(L, 0, "invalid option");
+ }
+ }
+ lua_unlock(L);
+ return i;
+}
+
+
+LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) {
+ size_t sz = luaO_str2num(s, s2v(L->top.p));
+ if (sz != 0)
+ api_incr_top(L);
+ return sz;
+}
+
+
+LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum) {
+ lua_Number n = 0;
+ const TValue *o = index2value(L, idx);
+ int isnum = tonumber(o, &n);
+ if (pisnum)
+ *pisnum = isnum;
+ return n;
+}
+
+
+LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) {
+ lua_Integer res = 0;
+ const TValue *o = index2value(L, idx);
+ int isnum = tointeger(o, &res);
+ if (pisnum)
+ *pisnum = isnum;
+ return res;
+}
+
+
+LUA_API int lua_toboolean (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return !l_isfalse(o);
+}
+
+
+LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
+ TValue *o;
+ lua_lock(L);
+ o = index2value(L, idx);
+ if (!ttisstring(o)) {
+ if (!cvt2str(o)) { /* not convertible? */
+ if (len != NULL) *len = 0;
+ lua_unlock(L);
+ return NULL;
+ }
+ luaO_tostring(L, o);
+ luaC_checkGC(L);
+ o = index2value(L, idx); /* previous call may reallocate the stack */
+ }
+ if (len != NULL)
+ *len = vslen(o);
+ lua_unlock(L);
+ return svalue(o);
+}
+
+
+LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ switch (ttypetag(o)) {
+ case LUA_VSHRSTR: return tsvalue(o)->shrlen;
+ case LUA_VLNGSTR: return tsvalue(o)->u.lnglen;
+ case LUA_VUSERDATA: return uvalue(o)->len;
+ case LUA_VTABLE: return luaH_getn(hvalue(o));
+ default: return 0;
+ }
+}
+
+
+LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ if (ttislcf(o)) return fvalue(o);
+ else if (ttisCclosure(o))
+ return clCvalue(o)->f;
+ else return NULL; /* not a C function */
+}
+
+
+l_sinline void *touserdata (const TValue *o) {
+ switch (ttype(o)) {
+ case LUA_TUSERDATA: return getudatamem(uvalue(o));
+ case LUA_TLIGHTUSERDATA: return pvalue(o);
+ default: return NULL;
+ }
+}
+
+
+LUA_API void *lua_touserdata (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return touserdata(o);
+}
+
+
+LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return (!ttisthread(o)) ? NULL : thvalue(o);
+}
+
+
+/*
+** Returns a pointer to the internal representation of an object.
+** Note that ANSI C does not allow the conversion of a pointer to
+** function to a 'void*', so the conversion here goes through
+** a 'size_t'. (As the returned pointer is only informative, this
+** conversion should not be a problem.)
+*/
+LUA_API const void *lua_topointer (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ switch (ttypetag(o)) {
+ case LUA_VLCF: return cast_voidp(cast_sizet(fvalue(o)));
+ case LUA_VUSERDATA: case LUA_VLIGHTUSERDATA:
+ return touserdata(o);
+ default: {
+ if (iscollectable(o))
+ return gcvalue(o);
+ else
+ return NULL;
+ }
+ }
+}
+
+
+
+/*
+** push functions (C -> stack)
+*/
+
+
+LUA_API void lua_pushnil (lua_State *L) {
+ lua_lock(L);
+ setnilvalue(s2v(L->top.p));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushnumber (lua_State *L, lua_Number n) {
+ lua_lock(L);
+ setfltvalue(s2v(L->top.p), n);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) {
+ lua_lock(L);
+ setivalue(s2v(L->top.p), n);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+/*
+** Pushes on the stack a string with given length. Avoid using 's' when
+** 'len' == 0 (as 's' can be NULL in that case), due to later use of
+** 'memcmp' and 'memcpy'.
+*/
+LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) {
+ TString *ts;
+ lua_lock(L);
+ ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len);
+ setsvalue2s(L, L->top.p, ts);
+ api_incr_top(L);
+ luaC_checkGC(L);
+ lua_unlock(L);
+ return getstr(ts);
+}
+
+
+LUA_API const char *lua_pushstring (lua_State *L, const char *s) {
+ lua_lock(L);
+ if (s == NULL)
+ setnilvalue(s2v(L->top.p));
+ else {
+ TString *ts;
+ ts = luaS_new(L, s);
+ setsvalue2s(L, L->top.p, ts);
+ s = getstr(ts); /* internal copy's address */
+ }
+ api_incr_top(L);
+ luaC_checkGC(L);
+ lua_unlock(L);
+ return s;
+}
+
+
+LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt,
+ va_list argp) {
+ const char *ret;
+ lua_lock(L);
+ ret = luaO_pushvfstring(L, fmt, argp);
+ luaC_checkGC(L);
+ lua_unlock(L);
+ return ret;
+}
+
+
+LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {
+ const char *ret;
+ va_list argp;
+ lua_lock(L);
+ va_start(argp, fmt);
+ ret = luaO_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ luaC_checkGC(L);
+ lua_unlock(L);
+ return ret;
+}
+
+
+LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
+ lua_lock(L);
+ if (n == 0) {
+ setfvalue(s2v(L->top.p), fn);
+ api_incr_top(L);
+ }
+ else {
+ CClosure *cl;
+ api_checknelems(L, n);
+ api_check(L, n <= MAXUPVAL, "upvalue index too large");
+ cl = luaF_newCclosure(L, n);
+ cl->f = fn;
+ L->top.p -= n;
+ while (n--) {
+ setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n));
+ /* does not need barrier because closure is white */
+ lua_assert(iswhite(cl));
+ }
+ setclCvalue(L, s2v(L->top.p), cl);
+ api_incr_top(L);
+ luaC_checkGC(L);
+ }
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushboolean (lua_State *L, int b) {
+ lua_lock(L);
+ if (b)
+ setbtvalue(s2v(L->top.p));
+ else
+ setbfvalue(s2v(L->top.p));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
+ lua_lock(L);
+ setpvalue(s2v(L->top.p), p);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_pushthread (lua_State *L) {
+ lua_lock(L);
+ setthvalue(L, s2v(L->top.p), L);
+ api_incr_top(L);
+ lua_unlock(L);
+ return (G(L)->mainthread == L);
+}
+
+
+
+/*
+** get functions (Lua -> stack)
+*/
+
+
+l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) {
+ const TValue *slot;
+ TString *str = luaS_new(L, k);
+ if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
+ setobj2s(L, L->top.p, slot);
+ api_incr_top(L);
+ }
+ else {
+ setsvalue2s(L, L->top.p, str);
+ api_incr_top(L);
+ luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
+ }
+ lua_unlock(L);
+ return ttype(s2v(L->top.p - 1));
+}
+
+
+/*
+** Get the global table in the registry. Since all predefined
+** indices in the registry were inserted right when the registry
+** was created and never removed, they must always be in the array
+** part of the registry.
+*/
+#define getGtable(L) \
+ (&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1])
+
+
+LUA_API int lua_getglobal (lua_State *L, const char *name) {
+ const TValue *G;
+ lua_lock(L);
+ G = getGtable(L);
+ return auxgetstr(L, G, name);
+}
+
+
+LUA_API int lua_gettable (lua_State *L, int idx) {
+ const TValue *slot;
+ TValue *t;
+ lua_lock(L);
+ t = index2value(L, idx);
+ if (luaV_fastget(L, t, s2v(L->top.p - 1), slot, luaH_get)) {
+ setobj2s(L, L->top.p - 1, slot);
+ }
+ else
+ luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
+ lua_unlock(L);
+ return ttype(s2v(L->top.p - 1));
+}
+
+
+LUA_API int lua_getfield (lua_State *L, int idx, const char *k) {
+ lua_lock(L);
+ return auxgetstr(L, index2value(L, idx), k);
+}
+
+
+LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
+ TValue *t;
+ const TValue *slot;
+ lua_lock(L);
+ t = index2value(L, idx);
+ if (luaV_fastgeti(L, t, n, slot)) {
+ setobj2s(L, L->top.p, slot);
+ }
+ else {
+ TValue aux;
+ setivalue(&aux, n);
+ luaV_finishget(L, t, &aux, L->top.p, slot);
+ }
+ api_incr_top(L);
+ lua_unlock(L);
+ return ttype(s2v(L->top.p - 1));
+}
+
+
+l_sinline int finishrawget (lua_State *L, const TValue *val) {
+ if (isempty(val)) /* avoid copying empty items to the stack */
+ setnilvalue(s2v(L->top.p));
+ else
+ setobj2s(L, L->top.p, val);
+ api_incr_top(L);
+ lua_unlock(L);
+ return ttype(s2v(L->top.p - 1));
+}
+
+
+static Table *gettable (lua_State *L, int idx) {
+ TValue *t = index2value(L, idx);
+ api_check(L, ttistable(t), "table expected");
+ return hvalue(t);
+}
+
+
+LUA_API int lua_rawget (lua_State *L, int idx) {
+ Table *t;
+ const TValue *val;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ t = gettable(L, idx);
+ val = luaH_get(t, s2v(L->top.p - 1));
+ L->top.p--; /* remove key */
+ return finishrawget(L, val);
+}
+
+
+LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) {
+ Table *t;
+ lua_lock(L);
+ t = gettable(L, idx);
+ return finishrawget(L, luaH_getint(t, n));
+}
+
+
+LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) {
+ Table *t;
+ TValue k;
+ lua_lock(L);
+ t = gettable(L, idx);
+ setpvalue(&k, cast_voidp(p));
+ return finishrawget(L, luaH_get(t, &k));
+}
+
+
+LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
+ Table *t;
+ lua_lock(L);
+ t = luaH_new(L);
+ sethvalue2s(L, L->top.p, t);
+ api_incr_top(L);
+ if (narray > 0 || nrec > 0)
+ luaH_resize(L, t, narray, nrec);
+ luaC_checkGC(L);
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_getmetatable (lua_State *L, int objindex) {
+ const TValue *obj;
+ Table *mt;
+ int res = 0;
+ lua_lock(L);
+ obj = index2value(L, objindex);
+ switch (ttype(obj)) {
+ case LUA_TTABLE:
+ mt = hvalue(obj)->metatable;
+ break;
+ case LUA_TUSERDATA:
+ mt = uvalue(obj)->metatable;
+ break;
+ default:
+ mt = G(L)->mt[ttype(obj)];
+ break;
+ }
+ if (mt != NULL) {
+ sethvalue2s(L, L->top.p, mt);
+ api_incr_top(L);
+ res = 1;
+ }
+ lua_unlock(L);
+ return res;
+}
+
+
+LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) {
+ TValue *o;
+ int t;
+ lua_lock(L);
+ o = index2value(L, idx);
+ api_check(L, ttisfulluserdata(o), "full userdata expected");
+ if (n <= 0 || n > uvalue(o)->nuvalue) {
+ setnilvalue(s2v(L->top.p));
+ t = LUA_TNONE;
+ }
+ else {
+ setobj2s(L, L->top.p, &uvalue(o)->uv[n - 1].uv);
+ t = ttype(s2v(L->top.p));
+ }
+ api_incr_top(L);
+ lua_unlock(L);
+ return t;
+}
+
+
+/*
+** set functions (stack -> Lua)
+*/
+
+/*
+** t[k] = value at the top of the stack (where 'k' is a string)
+*/
+static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
+ const TValue *slot;
+ TString *str = luaS_new(L, k);
+ api_checknelems(L, 1);
+ if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
+ luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
+ L->top.p--; /* pop value */
+ }
+ else {
+ setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */
+ api_incr_top(L);
+ luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot);
+ L->top.p -= 2; /* pop value and key */
+ }
+ lua_unlock(L); /* lock done by caller */
+}
+
+
+LUA_API void lua_setglobal (lua_State *L, const char *name) {
+ const TValue *G;
+ lua_lock(L); /* unlock done in 'auxsetstr' */
+ G = getGtable(L);
+ auxsetstr(L, G, name);
+}
+
+
+LUA_API void lua_settable (lua_State *L, int idx) {
+ TValue *t;
+ const TValue *slot;
+ lua_lock(L);
+ api_checknelems(L, 2);
+ t = index2value(L, idx);
+ if (luaV_fastget(L, t, s2v(L->top.p - 2), slot, luaH_get)) {
+ luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
+ }
+ else
+ luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot);
+ L->top.p -= 2; /* pop index and value */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_setfield (lua_State *L, int idx, const char *k) {
+ lua_lock(L); /* unlock done in 'auxsetstr' */
+ auxsetstr(L, index2value(L, idx), k);
+}
+
+
+LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) {
+ TValue *t;
+ const TValue *slot;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ t = index2value(L, idx);
+ if (luaV_fastgeti(L, t, n, slot)) {
+ luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
+ }
+ else {
+ TValue aux;
+ setivalue(&aux, n);
+ luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot);
+ }
+ L->top.p--; /* pop value */
+ lua_unlock(L);
+}
+
+
+static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
+ Table *t;
+ lua_lock(L);
+ api_checknelems(L, n);
+ t = gettable(L, idx);
+ luaH_set(L, t, key, s2v(L->top.p - 1));
+ invalidateTMcache(t);
+ luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
+ L->top.p -= n;
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_rawset (lua_State *L, int idx) {
+ aux_rawset(L, idx, s2v(L->top.p - 2), 2);
+}
+
+
+LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) {
+ TValue k;
+ setpvalue(&k, cast_voidp(p));
+ aux_rawset(L, idx, &k, 1);
+}
+
+
+LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) {
+ Table *t;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ t = gettable(L, idx);
+ luaH_setint(L, t, n, s2v(L->top.p - 1));
+ luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
+ L->top.p--;
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_setmetatable (lua_State *L, int objindex) {
+ TValue *obj;
+ Table *mt;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ obj = index2value(L, objindex);
+ if (ttisnil(s2v(L->top.p - 1)))
+ mt = NULL;
+ else {
+ api_check(L, ttistable(s2v(L->top.p - 1)), "table expected");
+ mt = hvalue(s2v(L->top.p - 1));
+ }
+ switch (ttype(obj)) {
+ case LUA_TTABLE: {
+ hvalue(obj)->metatable = mt;
+ if (mt) {
+ luaC_objbarrier(L, gcvalue(obj), mt);
+ luaC_checkfinalizer(L, gcvalue(obj), mt);
+ }
+ break;
+ }
+ case LUA_TUSERDATA: {
+ uvalue(obj)->metatable = mt;
+ if (mt) {
+ luaC_objbarrier(L, uvalue(obj), mt);
+ luaC_checkfinalizer(L, gcvalue(obj), mt);
+ }
+ break;
+ }
+ default: {
+ G(L)->mt[ttype(obj)] = mt;
+ break;
+ }
+ }
+ L->top.p--;
+ lua_unlock(L);
+ return 1;
+}
+
+
+LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
+ TValue *o;
+ int res;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ o = index2value(L, idx);
+ api_check(L, ttisfulluserdata(o), "full userdata expected");
+ if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue)))
+ res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */
+ else {
+ setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top.p - 1));
+ luaC_barrierback(L, gcvalue(o), s2v(L->top.p - 1));
+ res = 1;
+ }
+ L->top.p--;
+ lua_unlock(L);
+ return res;
+}
+
+
+/*
+** 'load' and 'call' functions (run Lua code)
+*/
+
+
+#define checkresults(L,na,nr) \
+ api_check(L, (nr) == LUA_MULTRET \
+ || (L->ci->top.p - L->top.p >= (nr) - (na)), \
+ "results from function overflow current stack size")
+
+
+LUA_API void lua_callk (lua_State *L, int nargs, int nresults,
+ lua_KContext ctx, lua_KFunction k) {
+ StkId func;
+ lua_lock(L);
+ api_check(L, k == NULL || !isLua(L->ci),
+ "cannot use continuations inside hooks");
+ api_checknelems(L, nargs+1);
+ api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
+ checkresults(L, nargs, nresults);
+ func = L->top.p - (nargs+1);
+ if (k != NULL && yieldable(L)) { /* need to prepare continuation? */
+ L->ci->u.c.k = k; /* save continuation */
+ L->ci->u.c.ctx = ctx; /* save context */
+ luaD_call(L, func, nresults); /* do the call */
+ }
+ else /* no continuation or no yieldable */
+ luaD_callnoyield(L, func, nresults); /* just do the call */
+ adjustresults(L, nresults);
+ lua_unlock(L);
+}
+
+
+
+/*
+** Execute a protected call.
+*/
+struct CallS { /* data to 'f_call' */
+ StkId func;
+ int nresults;
+};
+
+
+static void f_call (lua_State *L, void *ud) {
+ struct CallS *c = cast(struct CallS *, ud);
+ luaD_callnoyield(L, c->func, c->nresults);
+}
+
+
+
+LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
+ lua_KContext ctx, lua_KFunction k) {
+ struct CallS c;
+ int status;
+ ptrdiff_t func;
+ lua_lock(L);
+ api_check(L, k == NULL || !isLua(L->ci),
+ "cannot use continuations inside hooks");
+ api_checknelems(L, nargs+1);
+ api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
+ checkresults(L, nargs, nresults);
+ if (errfunc == 0)
+ func = 0;
+ else {
+ StkId o = index2stack(L, errfunc);
+ api_check(L, ttisfunction(s2v(o)), "error handler must be a function");
+ func = savestack(L, o);
+ }
+ c.func = L->top.p - (nargs+1); /* function to be called */
+ if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */
+ c.nresults = nresults; /* do a 'conventional' protected call */
+ status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
+ }
+ else { /* prepare continuation (call is already protected by 'resume') */
+ CallInfo *ci = L->ci;
+ ci->u.c.k = k; /* save continuation */
+ ci->u.c.ctx = ctx; /* save context */
+ /* save information for error recovery */
+ ci->u2.funcidx = cast_int(savestack(L, c.func));
+ ci->u.c.old_errfunc = L->errfunc;
+ L->errfunc = func;
+ setoah(ci->callstatus, L->allowhook); /* save value of 'allowhook' */
+ ci->callstatus |= CIST_YPCALL; /* function can do error recovery */
+ luaD_call(L, c.func, nresults); /* do the call */
+ ci->callstatus &= ~CIST_YPCALL;
+ L->errfunc = ci->u.c.old_errfunc;
+ status = LUA_OK; /* if it is here, there were no errors */
+ }
+ adjustresults(L, nresults);
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
+ const char *chunkname, const char *mode) {
+ ZIO z;
+ int status;
+ lua_lock(L);
+ if (!chunkname) chunkname = "?";
+ luaZ_init(L, &z, reader, data);
+ status = luaD_protectedparser(L, &z, chunkname, mode);
+ if (status == LUA_OK) { /* no errors? */
+ LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */
+ if (f->nupvalues >= 1) { /* does it have an upvalue? */
+ /* get global table from registry */
+ const TValue *gt = getGtable(L);
+ /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
+ setobj(L, f->upvals[0]->v.p, gt);
+ luaC_barrier(L, f->upvals[0], gt);
+ }
+ }
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) {
+ int status;
+ TValue *o;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ o = s2v(L->top.p - 1);
+ if (isLfunction(o))
+ status = luaU_dump(L, getproto(o), writer, data, strip);
+ else
+ status = 1;
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_status (lua_State *L) {
+ return L->status;
+}
+
+
+/*
+** Garbage-collection function
+*/
+LUA_API int lua_gc (lua_State *L, int what, ...) {
+ va_list argp;
+ int res = 0;
+ global_State *g = G(L);
+ if (g->gcstp & GCSTPGC) /* internal stop? */
+ return -1; /* all options are invalid when stopped */
+ lua_lock(L);
+ va_start(argp, what);
+ switch (what) {
+ case LUA_GCSTOP: {
+ g->gcstp = GCSTPUSR; /* stopped by the user */
+ break;
+ }
+ case LUA_GCRESTART: {
+ luaE_setdebt(g, 0);
+ g->gcstp = 0; /* (GCSTPGC must be already zero here) */
+ break;
+ }
+ case LUA_GCCOLLECT: {
+ luaC_fullgc(L, 0);
+ break;
+ }
+ case LUA_GCCOUNT: {
+ /* GC values are expressed in Kbytes: #bytes/2^10 */
+ res = cast_int(gettotalbytes(g) >> 10);
+ break;
+ }
+ case LUA_GCCOUNTB: {
+ res = cast_int(gettotalbytes(g) & 0x3ff);
+ break;
+ }
+ case LUA_GCSTEP: {
+ int data = va_arg(argp, int);
+ l_mem debt = 1; /* =1 to signal that it did an actual step */
+ lu_byte oldstp = g->gcstp;
+ g->gcstp = 0; /* allow GC to run (GCSTPGC must be zero here) */
+ if (data == 0) {
+ luaE_setdebt(g, 0); /* do a basic step */
+ luaC_step(L);
+ }
+ else { /* add 'data' to total debt */
+ debt = cast(l_mem, data) * 1024 + g->GCdebt;
+ luaE_setdebt(g, debt);
+ luaC_checkGC(L);
+ }
+ g->gcstp = oldstp; /* restore previous state */
+ if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */
+ res = 1; /* signal it */
+ break;
+ }
+ case LUA_GCSETPAUSE: {
+ int data = va_arg(argp, int);
+ res = getgcparam(g->gcpause);
+ setgcparam(g->gcpause, data);
+ break;
+ }
+ case LUA_GCSETSTEPMUL: {
+ int data = va_arg(argp, int);
+ res = getgcparam(g->gcstepmul);
+ setgcparam(g->gcstepmul, data);
+ break;
+ }
+ case LUA_GCISRUNNING: {
+ res = gcrunning(g);
+ break;
+ }
+ case LUA_GCGEN: {
+ int minormul = va_arg(argp, int);
+ int majormul = va_arg(argp, int);
+ res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC;
+ if (minormul != 0)
+ g->genminormul = minormul;
+ if (majormul != 0)
+ setgcparam(g->genmajormul, majormul);
+ luaC_changemode(L, KGC_GEN);
+ break;
+ }
+ case LUA_GCINC: {
+ int pause = va_arg(argp, int);
+ int stepmul = va_arg(argp, int);
+ int stepsize = va_arg(argp, int);
+ res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC;
+ if (pause != 0)
+ setgcparam(g->gcpause, pause);
+ if (stepmul != 0)
+ setgcparam(g->gcstepmul, stepmul);
+ if (stepsize != 0)
+ g->gcstepsize = stepsize;
+ luaC_changemode(L, KGC_INC);
+ break;
+ }
+ default: res = -1; /* invalid option */
+ }
+ va_end(argp);
+ lua_unlock(L);
+ return res;
+}
+
+
+
+/*
+** miscellaneous functions
+*/
+
+
+LUA_API int lua_error (lua_State *L) {
+ TValue *errobj;
+ lua_lock(L);
+ errobj = s2v(L->top.p - 1);
+ api_checknelems(L, 1);
+ /* error object is the memory error message? */
+ if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg))
+ luaM_error(L); /* raise a memory error */
+ else
+ luaG_errormsg(L); /* raise a regular error */
+ /* code unreachable; will unlock when control actually leaves the kernel */
+ return 0; /* to avoid warnings */
+}
+
+
+LUA_API int lua_next (lua_State *L, int idx) {
+ Table *t;
+ int more;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ t = gettable(L, idx);
+ more = luaH_next(L, t, L->top.p - 1);
+ if (more) {
+ api_incr_top(L);
+ }
+ else /* no more elements */
+ L->top.p -= 1; /* remove key */
+ lua_unlock(L);
+ return more;
+}
+
+
+LUA_API void lua_toclose (lua_State *L, int idx) {
+ int nresults;
+ StkId o;
+ lua_lock(L);
+ o = index2stack(L, idx);
+ nresults = L->ci->nresults;
+ api_check(L, L->tbclist.p < o, "given index below or equal a marked one");
+ luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
+ if (!hastocloseCfunc(nresults)) /* function not marked yet? */
+ L->ci->nresults = codeNresults(nresults); /* mark it */
+ lua_assert(hastocloseCfunc(L->ci->nresults));
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_concat (lua_State *L, int n) {
+ lua_lock(L);
+ api_checknelems(L, n);
+ if (n > 0)
+ luaV_concat(L, n);
+ else { /* nothing to concatenate */
+ setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */
+ api_incr_top(L);
+ }
+ luaC_checkGC(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_len (lua_State *L, int idx) {
+ TValue *t;
+ lua_lock(L);
+ t = index2value(L, idx);
+ luaV_objlen(L, L->top.p, t);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) {
+ lua_Alloc f;
+ lua_lock(L);
+ if (ud) *ud = G(L)->ud;
+ f = G(L)->frealloc;
+ lua_unlock(L);
+ return f;
+}
+
+
+LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) {
+ lua_lock(L);
+ G(L)->ud = ud;
+ G(L)->frealloc = f;
+ lua_unlock(L);
+}
+
+
+void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) {
+ lua_lock(L);
+ G(L)->ud_warn = ud;
+ G(L)->warnf = f;
+ lua_unlock(L);
+}
+
+
+void lua_warning (lua_State *L, const char *msg, int tocont) {
+ lua_lock(L);
+ luaE_warning(L, msg, tocont);
+ lua_unlock(L);
+}
+
+
+
+LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
+ Udata *u;
+ lua_lock(L);
+ api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value");
+ u = luaS_newudata(L, size, nuvalue);
+ setuvalue(L, s2v(L->top.p), u);
+ api_incr_top(L);
+ luaC_checkGC(L);
+ lua_unlock(L);
+ return getudatamem(u);
+}
+
+
+
+static const char *aux_upvalue (TValue *fi, int n, TValue **val,
+ GCObject **owner) {
+ switch (ttypetag(fi)) {
+ case LUA_VCCL: { /* C closure */
+ CClosure *f = clCvalue(fi);
+ if (!(cast_uint(n) - 1u < cast_uint(f->nupvalues)))
+ return NULL; /* 'n' not in [1, f->nupvalues] */
+ *val = &f->upvalue[n-1];
+ if (owner) *owner = obj2gco(f);
+ return "";
+ }
+ case LUA_VLCL: { /* Lua closure */
+ LClosure *f = clLvalue(fi);
+ TString *name;
+ Proto *p = f->p;
+ if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues)))
+ return NULL; /* 'n' not in [1, p->sizeupvalues] */
+ *val = f->upvals[n-1]->v.p;
+ if (owner) *owner = obj2gco(f->upvals[n - 1]);
+ name = p->upvalues[n-1].name;
+ return (name == NULL) ? "(no name)" : getstr(name);
+ }
+ default: return NULL; /* not a closure */
+ }
+}
+
+
+LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {
+ const char *name;
+ TValue *val = NULL; /* to avoid warnings */
+ lua_lock(L);
+ name = aux_upvalue(index2value(L, funcindex), n, &val, NULL);
+ if (name) {
+ setobj2s(L, L->top.p, val);
+ api_incr_top(L);
+ }
+ lua_unlock(L);
+ return name;
+}
+
+
+LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
+ const char *name;
+ TValue *val = NULL; /* to avoid warnings */
+ GCObject *owner = NULL; /* to avoid warnings */
+ TValue *fi;
+ lua_lock(L);
+ fi = index2value(L, funcindex);
+ api_checknelems(L, 1);
+ name = aux_upvalue(fi, n, &val, &owner);
+ if (name) {
+ L->top.p--;
+ setobj(L, val, s2v(L->top.p));
+ luaC_barrier(L, owner, val);
+ }
+ lua_unlock(L);
+ return name;
+}
+
+
+static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) {
+ static const UpVal *const nullup = NULL;
+ LClosure *f;
+ TValue *fi = index2value(L, fidx);
+ api_check(L, ttisLclosure(fi), "Lua function expected");
+ f = clLvalue(fi);
+ if (pf) *pf = f;
+ if (1 <= n && n <= f->p->sizeupvalues)
+ return &f->upvals[n - 1]; /* get its upvalue pointer */
+ else
+ return (UpVal**)&nullup;
+}
+
+
+LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
+ TValue *fi = index2value(L, fidx);
+ switch (ttypetag(fi)) {
+ case LUA_VLCL: { /* lua closure */
+ return *getupvalref(L, fidx, n, NULL);
+ }
+ case LUA_VCCL: { /* C closure */
+ CClosure *f = clCvalue(fi);
+ if (1 <= n && n <= f->nupvalues)
+ return &f->upvalue[n - 1];
+ /* else */
+ } /* FALLTHROUGH */
+ case LUA_VLCF:
+ return NULL; /* light C functions have no upvalues */
+ default: {
+ api_check(L, 0, "function expected");
+ return NULL;
+ }
+ }
+}
+
+
+LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1,
+ int fidx2, int n2) {
+ LClosure *f1;
+ UpVal **up1 = getupvalref(L, fidx1, n1, &f1);
+ UpVal **up2 = getupvalref(L, fidx2, n2, NULL);
+ api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index");
+ *up1 = *up2;
+ luaC_objbarrier(L, f1, *up1);
+}
+
+
diff --git a/src/libs/3rdparty/lua/src/lapi.h b/src/libs/3rdparty/lua/src/lapi.h
new file mode 100644
index 0000000000..a742427cdc
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lapi.h
@@ -0,0 +1,52 @@
+/*
+** $Id: lapi.h $
+** Auxiliary functions from Lua API
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lapi_h
+#define lapi_h
+
+
+#include "llimits.h"
+#include "lstate.h"
+
+
+/* Increments 'L->top.p', checking for stack overflows */
+#define api_incr_top(L) {L->top.p++; \
+ api_check(L, L->top.p <= L->ci->top.p, \
+ "stack overflow");}
+
+
+/*
+** If a call returns too many multiple returns, the callee may not have
+** stack space to accommodate all results. In this case, this macro
+** increases its stack space ('L->ci->top.p').
+*/
+#define adjustresults(L,nres) \
+ { if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \
+ L->ci->top.p = L->top.p; }
+
+
+/* Ensure the stack has at least 'n' elements */
+#define api_checknelems(L,n) \
+ api_check(L, (n) < (L->top.p - L->ci->func.p), \
+ "not enough elements in the stack")
+
+
+/*
+** To reduce the overhead of returning from C functions, the presence of
+** to-be-closed variables in these functions is coded in the CallInfo's
+** field 'nresults', in a way that functions with no to-be-closed variables
+** with zero, one, or "all" wanted results have no overhead. Functions
+** with other number of wanted results, as well as functions with
+** variables to be closed, have an extra check.
+*/
+
+#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
+
+/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
+#define codeNresults(n) (-(n) - 3)
+#define decodeNresults(n) (-(n) - 3)
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lauxlib.c b/src/libs/3rdparty/lua/src/lauxlib.c
new file mode 100644
index 0000000000..4ca6c65488
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lauxlib.c
@@ -0,0 +1,1112 @@
+/*
+** $Id: lauxlib.c $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+#define lauxlib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/*
+** This file uses only the official API of Lua.
+** Any function declared here could be written as an application function.
+*/
+
+#include "lua.h"
+
+#include "lauxlib.h"
+
+
+#if !defined(MAX_SIZET)
+/* maximum value for size_t */
+#define MAX_SIZET ((size_t)(~(size_t)0))
+#endif
+
+
+/*
+** {======================================================
+** Traceback
+** =======================================================
+*/
+
+
+#define LEVELS1 10 /* size of the first part of the stack */
+#define LEVELS2 11 /* size of the second part of the stack */
+
+
+
+/*
+** Search for 'objidx' in table at index -1. ('objidx' must be an
+** absolute index.) Return 1 + string at top if it found a good name.
+*/
+static int findfield (lua_State *L, int objidx, int level) {
+ if (level == 0 || !lua_istable(L, -1))
+ return 0; /* not found */
+ lua_pushnil(L); /* start 'next' loop */
+ while (lua_next(L, -2)) { /* for each pair in table */
+ if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */
+ if (lua_rawequal(L, objidx, -1)) { /* found object? */
+ lua_pop(L, 1); /* remove value (but keep name) */
+ return 1;
+ }
+ else if (findfield(L, objidx, level - 1)) { /* try recursively */
+ /* stack: lib_name, lib_table, field_name (top) */
+ lua_pushliteral(L, "."); /* place '.' between the two names */
+ lua_replace(L, -3); /* (in the slot occupied by table) */
+ lua_concat(L, 3); /* lib_name.field_name */
+ return 1;
+ }
+ }
+ lua_pop(L, 1); /* remove value */
+ }
+ return 0; /* not found */
+}
+
+
+/*
+** Search for a name for a function in all loaded modules
+*/
+static int pushglobalfuncname (lua_State *L, lua_Debug *ar) {
+ int top = lua_gettop(L);
+ lua_getinfo(L, "f", ar); /* push function */
+ lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
+ if (findfield(L, top + 1, 2)) {
+ const char *name = lua_tostring(L, -1);
+ if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */
+ lua_pushstring(L, name + 3); /* push name without prefix */
+ lua_remove(L, -2); /* remove original name */
+ }
+ lua_copy(L, -1, top + 1); /* copy name to proper place */
+ lua_settop(L, top + 1); /* remove table "loaded" and name copy */
+ return 1;
+ }
+ else {
+ lua_settop(L, top); /* remove function and global table */
+ return 0;
+ }
+}
+
+
+static void pushfuncname (lua_State *L, lua_Debug *ar) {
+ if (pushglobalfuncname(L, ar)) { /* try first a global name */
+ lua_pushfstring(L, "function '%s'", lua_tostring(L, -1));
+ lua_remove(L, -2); /* remove name */
+ }
+ else if (*ar->namewhat != '\0') /* is there a name from code? */
+ lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */
+ else if (*ar->what == 'm') /* main? */
+ lua_pushliteral(L, "main chunk");
+ else if (*ar->what != 'C') /* for Lua functions, use <file:line> */
+ lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
+ else /* nothing left... */
+ lua_pushliteral(L, "?");
+}
+
+
+static int lastlevel (lua_State *L) {
+ lua_Debug ar;
+ int li = 1, le = 1;
+ /* find an upper bound */
+ while (lua_getstack(L, le, &ar)) { li = le; le *= 2; }
+ /* do a binary search */
+ while (li < le) {
+ int m = (li + le)/2;
+ if (lua_getstack(L, m, &ar)) li = m + 1;
+ else le = m;
+ }
+ return le - 1;
+}
+
+
+LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1,
+ const char *msg, int level) {
+ luaL_Buffer b;
+ lua_Debug ar;
+ int last = lastlevel(L1);
+ int limit2show = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1;
+ luaL_buffinit(L, &b);
+ if (msg) {
+ luaL_addstring(&b, msg);
+ luaL_addchar(&b, '\n');
+ }
+ luaL_addstring(&b, "stack traceback:");
+ while (lua_getstack(L1, level++, &ar)) {
+ if (limit2show-- == 0) { /* too many levels? */
+ int n = last - level - LEVELS2 + 1; /* number of levels to skip */
+ lua_pushfstring(L, "\n\t...\t(skipping %d levels)", n);
+ luaL_addvalue(&b); /* add warning about skip */
+ level += n; /* and skip to last levels */
+ }
+ else {
+ lua_getinfo(L1, "Slnt", &ar);
+ if (ar.currentline <= 0)
+ lua_pushfstring(L, "\n\t%s: in ", ar.short_src);
+ else
+ lua_pushfstring(L, "\n\t%s:%d: in ", ar.short_src, ar.currentline);
+ luaL_addvalue(&b);
+ pushfuncname(L, &ar);
+ luaL_addvalue(&b);
+ if (ar.istailcall)
+ luaL_addstring(&b, "\n\t(...tail calls...)");
+ }
+ }
+ luaL_pushresult(&b);
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Error-report functions
+** =======================================================
+*/
+
+LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
+ lua_Debug ar;
+ if (!lua_getstack(L, 0, &ar)) /* no stack frame? */
+ return luaL_error(L, "bad argument #%d (%s)", arg, extramsg);
+ lua_getinfo(L, "n", &ar);
+ if (strcmp(ar.namewhat, "method") == 0) {
+ arg--; /* do not count 'self' */
+ if (arg == 0) /* error is in the self argument itself? */
+ return luaL_error(L, "calling '%s' on bad self (%s)",
+ ar.name, extramsg);
+ }
+ if (ar.name == NULL)
+ ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?";
+ return luaL_error(L, "bad argument #%d to '%s' (%s)",
+ arg, ar.name, extramsg);
+}
+
+
+LUALIB_API int luaL_typeerror (lua_State *L, int arg, const char *tname) {
+ const char *msg;
+ const char *typearg; /* name for the type of the actual argument */
+ if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
+ typearg = lua_tostring(L, -1); /* use the given type name */
+ else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA)
+ typearg = "light userdata"; /* special name for messages */
+ else
+ typearg = luaL_typename(L, arg); /* standard name */
+ msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg);
+ return luaL_argerror(L, arg, msg);
+}
+
+
+static void tag_error (lua_State *L, int arg, int tag) {
+ luaL_typeerror(L, arg, lua_typename(L, tag));
+}
+
+
+/*
+** The use of 'lua_pushfstring' ensures this function does not
+** need reserved stack space when called.
+*/
+LUALIB_API void luaL_where (lua_State *L, int level) {
+ lua_Debug ar;
+ if (lua_getstack(L, level, &ar)) { /* check function at level */
+ lua_getinfo(L, "Sl", &ar); /* get info about it */
+ if (ar.currentline > 0) { /* is there info? */
+ lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
+ return;
+ }
+ }
+ lua_pushfstring(L, ""); /* else, no information available... */
+}
+
+
+/*
+** Again, the use of 'lua_pushvfstring' ensures this function does
+** not need reserved stack space when called. (At worst, it generates
+** an error with "stack overflow" instead of the given message.)
+*/
+LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) {
+ va_list argp;
+ va_start(argp, fmt);
+ luaL_where(L, 1);
+ lua_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ lua_concat(L, 2);
+ return lua_error(L);
+}
+
+
+LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
+ int en = errno; /* calls to Lua API may change this value */
+ if (stat) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ luaL_pushfail(L);
+ if (fname)
+ lua_pushfstring(L, "%s: %s", fname, strerror(en));
+ else
+ lua_pushstring(L, strerror(en));
+ lua_pushinteger(L, en);
+ return 3;
+ }
+}
+
+
+#if !defined(l_inspectstat) /* { */
+
+#if defined(LUA_USE_POSIX)
+
+#include <sys/wait.h>
+
+/*
+** use appropriate macros to interpret 'pclose' return status
+*/
+#define l_inspectstat(stat,what) \
+ if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \
+ else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; }
+
+#else
+
+#define l_inspectstat(stat,what) /* no op */
+
+#endif
+
+#endif /* } */
+
+
+LUALIB_API int luaL_execresult (lua_State *L, int stat) {
+ if (stat != 0 && errno != 0) /* error with an 'errno'? */
+ return luaL_fileresult(L, 0, NULL);
+ else {
+ const char *what = "exit"; /* type of termination */
+ l_inspectstat(stat, what); /* interpret result */
+ if (*what == 'e' && stat == 0) /* successful termination? */
+ lua_pushboolean(L, 1);
+ else
+ luaL_pushfail(L);
+ lua_pushstring(L, what);
+ lua_pushinteger(L, stat);
+ return 3; /* return true/fail,what,code */
+ }
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** Userdata's metatable manipulation
+** =======================================================
+*/
+
+LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {
+ if (luaL_getmetatable(L, tname) != LUA_TNIL) /* name already in use? */
+ return 0; /* leave previous value on top, but return 0 */
+ lua_pop(L, 1);
+ lua_createtable(L, 0, 2); /* create metatable */
+ lua_pushstring(L, tname);
+ lua_setfield(L, -2, "__name"); /* metatable.__name = tname */
+ lua_pushvalue(L, -1);
+ lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */
+ return 1;
+}
+
+
+LUALIB_API void luaL_setmetatable (lua_State *L, const char *tname) {
+ luaL_getmetatable(L, tname);
+ lua_setmetatable(L, -2);
+}
+
+
+LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) {
+ void *p = lua_touserdata(L, ud);
+ if (p != NULL) { /* value is a userdata? */
+ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
+ luaL_getmetatable(L, tname); /* get correct metatable */
+ if (!lua_rawequal(L, -1, -2)) /* not the same? */
+ p = NULL; /* value is a userdata with wrong metatable */
+ lua_pop(L, 2); /* remove both metatables */
+ return p;
+ }
+ }
+ return NULL; /* value is not a userdata with a metatable */
+}
+
+
+LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {
+ void *p = luaL_testudata(L, ud, tname);
+ luaL_argexpected(L, p != NULL, ud, tname);
+ return p;
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Argument check functions
+** =======================================================
+*/
+
+LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def,
+ const char *const lst[]) {
+ const char *name = (def) ? luaL_optstring(L, arg, def) :
+ luaL_checkstring(L, arg);
+ int i;
+ for (i=0; lst[i]; i++)
+ if (strcmp(lst[i], name) == 0)
+ return i;
+ return luaL_argerror(L, arg,
+ lua_pushfstring(L, "invalid option '%s'", name));
+}
+
+
+/*
+** Ensures the stack has at least 'space' extra slots, raising an error
+** if it cannot fulfill the request. (The error handling needs a few
+** extra slots to format the error message. In case of an error without
+** this extra space, Lua will generate the same 'stack overflow' error,
+** but without 'msg'.)
+*/
+LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
+ if (l_unlikely(!lua_checkstack(L, space))) {
+ if (msg)
+ luaL_error(L, "stack overflow (%s)", msg);
+ else
+ luaL_error(L, "stack overflow");
+ }
+}
+
+
+LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) {
+ if (l_unlikely(lua_type(L, arg) != t))
+ tag_error(L, arg, t);
+}
+
+
+LUALIB_API void luaL_checkany (lua_State *L, int arg) {
+ if (l_unlikely(lua_type(L, arg) == LUA_TNONE))
+ luaL_argerror(L, arg, "value expected");
+}
+
+
+LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) {
+ const char *s = lua_tolstring(L, arg, len);
+ if (l_unlikely(!s)) tag_error(L, arg, LUA_TSTRING);
+ return s;
+}
+
+
+LUALIB_API const char *luaL_optlstring (lua_State *L, int arg,
+ const char *def, size_t *len) {
+ if (lua_isnoneornil(L, arg)) {
+ if (len)
+ *len = (def ? strlen(def) : 0);
+ return def;
+ }
+ else return luaL_checklstring(L, arg, len);
+}
+
+
+LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) {
+ int isnum;
+ lua_Number d = lua_tonumberx(L, arg, &isnum);
+ if (l_unlikely(!isnum))
+ tag_error(L, arg, LUA_TNUMBER);
+ return d;
+}
+
+
+LUALIB_API lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number def) {
+ return luaL_opt(L, luaL_checknumber, arg, def);
+}
+
+
+static void interror (lua_State *L, int arg) {
+ if (lua_isnumber(L, arg))
+ luaL_argerror(L, arg, "number has no integer representation");
+ else
+ tag_error(L, arg, LUA_TNUMBER);
+}
+
+
+LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) {
+ int isnum;
+ lua_Integer d = lua_tointegerx(L, arg, &isnum);
+ if (l_unlikely(!isnum)) {
+ interror(L, arg);
+ }
+ return d;
+}
+
+
+LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int arg,
+ lua_Integer def) {
+ return luaL_opt(L, luaL_checkinteger, arg, def);
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+/* userdata to box arbitrary data */
+typedef struct UBox {
+ void *box;
+ size_t bsize;
+} UBox;
+
+
+static void *resizebox (lua_State *L, int idx, size_t newsize) {
+ void *ud;
+ lua_Alloc allocf = lua_getallocf(L, &ud);
+ UBox *box = (UBox *)lua_touserdata(L, idx);
+ void *temp = allocf(ud, box->box, box->bsize, newsize);
+ if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */
+ lua_pushliteral(L, "not enough memory");
+ lua_error(L); /* raise a memory error */
+ }
+ box->box = temp;
+ box->bsize = newsize;
+ return temp;
+}
+
+
+static int boxgc (lua_State *L) {
+ resizebox(L, 1, 0);
+ return 0;
+}
+
+
+static const luaL_Reg boxmt[] = { /* box metamethods */
+ {"__gc", boxgc},
+ {"__close", boxgc},
+ {NULL, NULL}
+};
+
+
+static void newbox (lua_State *L) {
+ UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0);
+ box->box = NULL;
+ box->bsize = 0;
+ if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */
+ luaL_setfuncs(L, boxmt, 0); /* set its metamethods */
+ lua_setmetatable(L, -2);
+}
+
+
+/*
+** check whether buffer is using a userdata on the stack as a temporary
+** buffer
+*/
+#define buffonstack(B) ((B)->b != (B)->init.b)
+
+
+/*
+** Whenever buffer is accessed, slot 'idx' must either be a box (which
+** cannot be NULL) or it is a placeholder for the buffer.
+*/
+#define checkbufferlevel(B,idx) \
+ lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \
+ : lua_touserdata(B->L, idx) == (void*)B)
+
+
+/*
+** Compute new size for buffer 'B', enough to accommodate extra 'sz'
+** bytes. (The test for "not big enough" also gets the case when the
+** computation of 'newsize' overflows.)
+*/
+static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
+ size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */
+ if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */
+ return luaL_error(B->L, "buffer too large");
+ if (newsize < B->n + sz) /* not big enough? */
+ newsize = B->n + sz;
+ return newsize;
+}
+
+
+/*
+** Returns a pointer to a free area with at least 'sz' bytes in buffer
+** 'B'. 'boxidx' is the relative position in the stack where is the
+** buffer's box or its placeholder.
+*/
+static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
+ checkbufferlevel(B, boxidx);
+ if (B->size - B->n >= sz) /* enough space? */
+ return B->b + B->n;
+ else {
+ lua_State *L = B->L;
+ char *newbuff;
+ size_t newsize = newbuffsize(B, sz);
+ /* create larger buffer */
+ if (buffonstack(B)) /* buffer already has a box? */
+ newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
+ else { /* no box yet */
+ lua_remove(L, boxidx); /* remove placeholder */
+ newbox(L); /* create a new box */
+ lua_insert(L, boxidx); /* move box to its intended position */
+ lua_toclose(L, boxidx);
+ newbuff = (char *)resizebox(L, boxidx, newsize);
+ memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
+ }
+ B->b = newbuff;
+ B->size = newsize;
+ return newbuff + B->n;
+ }
+}
+
+/*
+** returns a pointer to a free area with at least 'sz' bytes
+*/
+LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) {
+ return prepbuffsize(B, sz, -1);
+}
+
+
+LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {
+ if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */
+ char *b = prepbuffsize(B, l, -1);
+ memcpy(b, s, l * sizeof(char));
+ luaL_addsize(B, l);
+ }
+}
+
+
+LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
+ luaL_addlstring(B, s, strlen(s));
+}
+
+
+LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
+ lua_State *L = B->L;
+ checkbufferlevel(B, -1);
+ lua_pushlstring(L, B->b, B->n);
+ if (buffonstack(B))
+ lua_closeslot(L, -2); /* close the box */
+ lua_remove(L, -2); /* remove box or placeholder from the stack */
+}
+
+
+LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) {
+ luaL_addsize(B, sz);
+ luaL_pushresult(B);
+}
+
+
+/*
+** 'luaL_addvalue' is the only function in the Buffer system where the
+** box (if existent) is not on the top of the stack. So, instead of
+** calling 'luaL_addlstring', it replicates the code using -2 as the
+** last argument to 'prepbuffsize', signaling that the box is (or will
+** be) below the string being added to the buffer. (Box creation can
+** trigger an emergency GC, so we should not remove the string from the
+** stack before we have the space guaranteed.)
+*/
+LUALIB_API void luaL_addvalue (luaL_Buffer *B) {
+ lua_State *L = B->L;
+ size_t len;
+ const char *s = lua_tolstring(L, -1, &len);
+ char *b = prepbuffsize(B, len, -2);
+ memcpy(b, s, len * sizeof(char));
+ luaL_addsize(B, len);
+ lua_pop(L, 1); /* pop string */
+}
+
+
+LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
+ B->L = L;
+ B->b = B->init.b;
+ B->n = 0;
+ B->size = LUAL_BUFFERSIZE;
+ lua_pushlightuserdata(L, (void*)B); /* push placeholder */
+}
+
+
+LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
+ luaL_buffinit(L, B);
+ return prepbuffsize(B, sz, -1);
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Reference system
+** =======================================================
+*/
+
+/* index of free-list header (after the predefined values) */
+#define freelist (LUA_RIDX_LAST + 1)
+
+/*
+** The previously freed references form a linked list:
+** t[freelist] is the index of a first free index, or zero if list is
+** empty; t[t[freelist]] is the index of the second element; etc.
+*/
+LUALIB_API int luaL_ref (lua_State *L, int t) {
+ int ref;
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1); /* remove from stack */
+ return LUA_REFNIL; /* 'nil' has a unique fixed reference */
+ }
+ t = lua_absindex(L, t);
+ if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */
+ ref = 0; /* list is empty */
+ lua_pushinteger(L, 0); /* initialize as an empty list */
+ lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */
+ }
+ else { /* already initialized */
+ lua_assert(lua_isinteger(L, -1));
+ ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
+ }
+ lua_pop(L, 1); /* remove element from stack */
+ if (ref != 0) { /* any free element? */
+ lua_rawgeti(L, t, ref); /* remove it from list */
+ lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */
+ }
+ else /* no free elements */
+ ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */
+ lua_rawseti(L, t, ref);
+ return ref;
+}
+
+
+LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
+ if (ref >= 0) {
+ t = lua_absindex(L, t);
+ lua_rawgeti(L, t, freelist);
+ lua_assert(lua_isinteger(L, -1));
+ lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */
+ lua_pushinteger(L, ref);
+ lua_rawseti(L, t, freelist); /* t[freelist] = ref */
+ }
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Load functions
+** =======================================================
+*/
+
+typedef struct LoadF {
+ int n; /* number of pre-read characters */
+ FILE *f; /* file being read */
+ char buff[BUFSIZ]; /* area for reading file */
+} LoadF;
+
+
+static const char *getF (lua_State *L, void *ud, size_t *size) {
+ LoadF *lf = (LoadF *)ud;
+ (void)L; /* not used */
+ if (lf->n > 0) { /* are there pre-read characters to be read? */
+ *size = lf->n; /* return them (chars already in buffer) */
+ lf->n = 0; /* no more pre-read characters */
+ }
+ else { /* read a block from file */
+ /* 'fread' can return > 0 *and* set the EOF flag. If next call to
+ 'getF' called 'fread', it might still wait for user input.
+ The next check avoids this problem. */
+ if (feof(lf->f)) return NULL;
+ *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */
+ }
+ return lf->buff;
+}
+
+
+static int errfile (lua_State *L, const char *what, int fnameindex) {
+ const char *serr = strerror(errno);
+ const char *filename = lua_tostring(L, fnameindex) + 1;
+ lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
+ lua_remove(L, fnameindex);
+ return LUA_ERRFILE;
+}
+
+
+/*
+** Skip an optional BOM at the start of a stream. If there is an
+** incomplete BOM (the first character is correct but the rest is
+** not), returns the first character anyway to force an error
+** (as no chunk can start with 0xEF).
+*/
+static int skipBOM (FILE *f) {
+ int c = getc(f); /* read first character */
+ if (c == 0xEF && getc(f) == 0xBB && getc(f) == 0xBF) /* correct BOM? */
+ return getc(f); /* ignore BOM and return next char */
+ else /* no (valid) BOM */
+ return c; /* return first character */
+}
+
+
+/*
+** reads the first character of file 'f' and skips an optional BOM mark
+** in its beginning plus its first line if it starts with '#'. Returns
+** true if it skipped the first line. In any case, '*cp' has the
+** first "valid" character of the file (after the optional BOM and
+** a first-line comment).
+*/
+static int skipcomment (FILE *f, int *cp) {
+ int c = *cp = skipBOM(f);
+ if (c == '#') { /* first line is a comment (Unix exec. file)? */
+ do { /* skip first line */
+ c = getc(f);
+ } while (c != EOF && c != '\n');
+ *cp = getc(f); /* next character after comment, if present */
+ return 1; /* there was a comment */
+ }
+ else return 0; /* no comment */
+}
+
+
+LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
+ const char *mode) {
+ LoadF lf;
+ int status, readstatus;
+ int c;
+ int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
+ if (filename == NULL) {
+ lua_pushliteral(L, "=stdin");
+ lf.f = stdin;
+ }
+ else {
+ lua_pushfstring(L, "@%s", filename);
+ lf.f = fopen(filename, "r");
+ if (lf.f == NULL) return errfile(L, "open", fnameindex);
+ }
+ lf.n = 0;
+ if (skipcomment(lf.f, &c)) /* read initial portion */
+ lf.buff[lf.n++] = '\n'; /* add newline to correct line numbers */
+ if (c == LUA_SIGNATURE[0]) { /* binary file? */
+ lf.n = 0; /* remove possible newline */
+ if (filename) { /* "real" file? */
+ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
+ if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
+ skipcomment(lf.f, &c); /* re-read initial portion */
+ }
+ }
+ if (c != EOF)
+ lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
+ status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode);
+ readstatus = ferror(lf.f);
+ if (filename) fclose(lf.f); /* close file (even in case of errors) */
+ if (readstatus) {
+ lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
+ return errfile(L, "read", fnameindex);
+ }
+ lua_remove(L, fnameindex);
+ return status;
+}
+
+
+typedef struct LoadS {
+ const char *s;
+ size_t size;
+} LoadS;
+
+
+static const char *getS (lua_State *L, void *ud, size_t *size) {
+ LoadS *ls = (LoadS *)ud;
+ (void)L; /* not used */
+ if (ls->size == 0) return NULL;
+ *size = ls->size;
+ ls->size = 0;
+ return ls->s;
+}
+
+
+LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size,
+ const char *name, const char *mode) {
+ LoadS ls;
+ ls.s = buff;
+ ls.size = size;
+ return lua_load(L, getS, &ls, name, mode);
+}
+
+
+LUALIB_API int luaL_loadstring (lua_State *L, const char *s) {
+ return luaL_loadbuffer(L, s, strlen(s), s);
+}
+
+/* }====================================================== */
+
+
+
+LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) {
+ if (!lua_getmetatable(L, obj)) /* no metatable? */
+ return LUA_TNIL;
+ else {
+ int tt;
+ lua_pushstring(L, event);
+ tt = lua_rawget(L, -2);
+ if (tt == LUA_TNIL) /* is metafield nil? */
+ lua_pop(L, 2); /* remove metatable and metafield */
+ else
+ lua_remove(L, -2); /* remove only metatable */
+ return tt; /* return metafield type */
+ }
+}
+
+
+LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) {
+ obj = lua_absindex(L, obj);
+ if (luaL_getmetafield(L, obj, event) == LUA_TNIL) /* no metafield? */
+ return 0;
+ lua_pushvalue(L, obj);
+ lua_call(L, 1, 1);
+ return 1;
+}
+
+
+LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
+ lua_Integer l;
+ int isnum;
+ lua_len(L, idx);
+ l = lua_tointegerx(L, -1, &isnum);
+ if (l_unlikely(!isnum))
+ luaL_error(L, "object length is not an integer");
+ lua_pop(L, 1); /* remove object */
+ return l;
+}
+
+
+LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
+ idx = lua_absindex(L,idx);
+ if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */
+ if (!lua_isstring(L, -1))
+ luaL_error(L, "'__tostring' must return a string");
+ }
+ else {
+ switch (lua_type(L, idx)) {
+ case LUA_TNUMBER: {
+ if (lua_isinteger(L, idx))
+ lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx));
+ else
+ lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx));
+ break;
+ }
+ case LUA_TSTRING:
+ lua_pushvalue(L, idx);
+ break;
+ case LUA_TBOOLEAN:
+ lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false"));
+ break;
+ case LUA_TNIL:
+ lua_pushliteral(L, "nil");
+ break;
+ default: {
+ int tt = luaL_getmetafield(L, idx, "__name"); /* try name */
+ const char *kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) :
+ luaL_typename(L, idx);
+ lua_pushfstring(L, "%s: %p", kind, lua_topointer(L, idx));
+ if (tt != LUA_TNIL)
+ lua_remove(L, -2); /* remove '__name' */
+ break;
+ }
+ }
+ }
+ return lua_tolstring(L, -1, len);
+}
+
+
+/*
+** set functions from list 'l' into table at top - 'nup'; each
+** function gets the 'nup' elements at the top as upvalues.
+** Returns with only the table at the stack.
+*/
+LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
+ luaL_checkstack(L, nup, "too many upvalues");
+ for (; l->name != NULL; l++) { /* fill the table with given functions */
+ if (l->func == NULL) /* place holder? */
+ lua_pushboolean(L, 0);
+ else {
+ int i;
+ for (i = 0; i < nup; i++) /* copy upvalues to the top */
+ lua_pushvalue(L, -nup);
+ lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
+ }
+ lua_setfield(L, -(nup + 2), l->name);
+ }
+ lua_pop(L, nup); /* remove upvalues */
+}
+
+
+/*
+** ensure that stack[idx][fname] has a table and push that table
+** into the stack
+*/
+LUALIB_API int luaL_getsubtable (lua_State *L, int idx, const char *fname) {
+ if (lua_getfield(L, idx, fname) == LUA_TTABLE)
+ return 1; /* table already there */
+ else {
+ lua_pop(L, 1); /* remove previous result */
+ idx = lua_absindex(L, idx);
+ lua_newtable(L);
+ lua_pushvalue(L, -1); /* copy to be left at top */
+ lua_setfield(L, idx, fname); /* assign new table to field */
+ return 0; /* false, because did not find table there */
+ }
+}
+
+
+/*
+** Stripped-down 'require': After checking "loaded" table, calls 'openf'
+** to open a module, registers the result in 'package.loaded' table and,
+** if 'glb' is true, also registers the result in the global table.
+** Leaves resulting module on the top.
+*/
+LUALIB_API void luaL_requiref (lua_State *L, const char *modname,
+ lua_CFunction openf, int glb) {
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
+ lua_getfield(L, -1, modname); /* LOADED[modname] */
+ if (!lua_toboolean(L, -1)) { /* package not already loaded? */
+ lua_pop(L, 1); /* remove field */
+ lua_pushcfunction(L, openf);
+ lua_pushstring(L, modname); /* argument to open function */
+ lua_call(L, 1, 1); /* call 'openf' to open module */
+ lua_pushvalue(L, -1); /* make copy of module (call result) */
+ lua_setfield(L, -3, modname); /* LOADED[modname] = module */
+ }
+ lua_remove(L, -2); /* remove LOADED table */
+ if (glb) {
+ lua_pushvalue(L, -1); /* copy of module */
+ lua_setglobal(L, modname); /* _G[modname] = module */
+ }
+}
+
+
+LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s,
+ const char *p, const char *r) {
+ const char *wild;
+ size_t l = strlen(p);
+ while ((wild = strstr(s, p)) != NULL) {
+ luaL_addlstring(b, s, wild - s); /* push prefix */
+ luaL_addstring(b, r); /* push replacement in place of pattern */
+ s = wild + l; /* continue after 'p' */
+ }
+ luaL_addstring(b, s); /* push last suffix */
+}
+
+
+LUALIB_API const char *luaL_gsub (lua_State *L, const char *s,
+ const char *p, const char *r) {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ luaL_addgsub(&b, s, p, r);
+ luaL_pushresult(&b);
+ return lua_tostring(L, -1);
+}
+
+
+static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
+ (void)ud; (void)osize; /* not used */
+ if (nsize == 0) {
+ free(ptr);
+ return NULL;
+ }
+ else
+ return realloc(ptr, nsize);
+}
+
+
+static int panic (lua_State *L) {
+ const char *msg = lua_tostring(L, -1);
+ if (msg == NULL) msg = "error object is not a string";
+ lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
+ msg);
+ return 0; /* return to Lua to abort */
+}
+
+
+/*
+** Warning functions:
+** warnfoff: warning system is off
+** warnfon: ready to start a new message
+** warnfcont: previous message is to be continued
+*/
+static void warnfoff (void *ud, const char *message, int tocont);
+static void warnfon (void *ud, const char *message, int tocont);
+static void warnfcont (void *ud, const char *message, int tocont);
+
+
+/*
+** Check whether message is a control message. If so, execute the
+** control or ignore it if unknown.
+*/
+static int checkcontrol (lua_State *L, const char *message, int tocont) {
+ if (tocont || *(message++) != '@') /* not a control message? */
+ return 0;
+ else {
+ if (strcmp(message, "off") == 0)
+ lua_setwarnf(L, warnfoff, L); /* turn warnings off */
+ else if (strcmp(message, "on") == 0)
+ lua_setwarnf(L, warnfon, L); /* turn warnings on */
+ return 1; /* it was a control message */
+ }
+}
+
+
+static void warnfoff (void *ud, const char *message, int tocont) {
+ checkcontrol((lua_State *)ud, message, tocont);
+}
+
+
+/*
+** Writes the message and handle 'tocont', finishing the message
+** if needed and setting the next warn function.
+*/
+static void warnfcont (void *ud, const char *message, int tocont) {
+ lua_State *L = (lua_State *)ud;
+ lua_writestringerror("%s", message); /* write message */
+ if (tocont) /* not the last part? */
+ lua_setwarnf(L, warnfcont, L); /* to be continued */
+ else { /* last part */
+ lua_writestringerror("%s", "\n"); /* finish message with end-of-line */
+ lua_setwarnf(L, warnfon, L); /* next call is a new message */
+ }
+}
+
+
+static void warnfon (void *ud, const char *message, int tocont) {
+ if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */
+ return; /* nothing else to be done */
+ lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
+ warnfcont(ud, message, tocont); /* finish processing */
+}
+
+
+LUALIB_API lua_State *luaL_newstate (void) {
+ lua_State *L = lua_newstate(l_alloc, NULL);
+ if (l_likely(L)) {
+ lua_atpanic(L, &panic);
+ lua_setwarnf(L, warnfoff, L); /* default is warnings off */
+ }
+ return L;
+}
+
+
+LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver, size_t sz) {
+ lua_Number v = lua_version(L);
+ if (sz != LUAL_NUMSIZES) /* check numeric types */
+ luaL_error(L, "core and library have incompatible numeric types");
+ else if (v != ver)
+ luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f",
+ (LUAI_UACNUMBER)ver, (LUAI_UACNUMBER)v);
+}
+
diff --git a/src/libs/3rdparty/lua/src/lauxlib.h b/src/libs/3rdparty/lua/src/lauxlib.h
new file mode 100644
index 0000000000..5b977e2a39
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lauxlib.h
@@ -0,0 +1,301 @@
+/*
+** $Id: lauxlib.h $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lauxlib_h
+#define lauxlib_h
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "luaconf.h"
+#include "lua.h"
+
+
+/* global table */
+#define LUA_GNAME "_G"
+
+
+typedef struct luaL_Buffer luaL_Buffer;
+
+
+/* extra error code for 'luaL_loadfilex' */
+#define LUA_ERRFILE (LUA_ERRERR+1)
+
+
+/* key, in the registry, for table of loaded modules */
+#define LUA_LOADED_TABLE "_LOADED"
+
+
+/* key, in the registry, for table of preloaded loaders */
+#define LUA_PRELOAD_TABLE "_PRELOAD"
+
+
+typedef struct luaL_Reg {
+ const char *name;
+ lua_CFunction func;
+} luaL_Reg;
+
+
+#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number))
+
+LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
+#define luaL_checkversion(L) \
+ luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)
+
+LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
+LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
+LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
+LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
+LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname);
+LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
+ size_t *l);
+LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
+ const char *def, size_t *l);
+LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
+LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);
+
+LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
+LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
+ lua_Integer def);
+
+LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
+LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
+LUALIB_API void (luaL_checkany) (lua_State *L, int arg);
+
+LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
+LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
+LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
+LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
+
+LUALIB_API void (luaL_where) (lua_State *L, int lvl);
+LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
+
+LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
+ const char *const lst[]);
+
+LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
+LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
+
+
+/* predefined references */
+#define LUA_NOREF (-2)
+#define LUA_REFNIL (-1)
+
+LUALIB_API int (luaL_ref) (lua_State *L, int t);
+LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
+
+LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
+ const char *mode);
+
+#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
+
+LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
+ const char *name, const char *mode);
+LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
+
+LUALIB_API lua_State *(luaL_newstate) (void);
+
+LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
+
+LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s,
+ const char *p, const char *r);
+LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
+ const char *p, const char *r);
+
+LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
+
+LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);
+
+LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
+ const char *msg, int level);
+
+LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
+ lua_CFunction openf, int glb);
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+
+#define luaL_newlibtable(L,l) \
+ lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
+
+#define luaL_newlib(L,l) \
+ (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
+
+#define luaL_argcheck(L, cond,arg,extramsg) \
+ ((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))
+
+#define luaL_argexpected(L,cond,arg,tname) \
+ ((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))
+
+#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
+#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
+
+#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
+
+#define luaL_dofile(L, fn) \
+ (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_dostring(L, s) \
+ (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
+
+#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
+
+#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
+
+
+/*
+** Perform arithmetic operations on lua_Integer values with wrap-around
+** semantics, as the Lua core does.
+*/
+#define luaL_intop(op,v1,v2) \
+ ((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2)))
+
+
+/* push the value used to represent failure/error */
+#define luaL_pushfail(L) lua_pushnil(L)
+
+
+/*
+** Internal assertions for in-house debugging
+*/
+#if !defined(lua_assert)
+
+#if defined LUAI_ASSERT
+ #include <assert.h>
+ #define lua_assert(c) assert(c)
+#else
+ #define lua_assert(c) ((void)0)
+#endif
+
+#endif
+
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+struct luaL_Buffer {
+ char *b; /* buffer address */
+ size_t size; /* buffer size */
+ size_t n; /* number of characters in buffer */
+ lua_State *L;
+ union {
+ LUAI_MAXALIGN; /* ensure maximum alignment for buffer */
+ char b[LUAL_BUFFERSIZE]; /* initial buffer */
+ } init;
+};
+
+
+#define luaL_bufflen(bf) ((bf)->n)
+#define luaL_buffaddr(bf) ((bf)->b)
+
+
+#define luaL_addchar(B,c) \
+ ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
+ ((B)->b[(B)->n++] = (c)))
+
+#define luaL_addsize(B,s) ((B)->n += (s))
+
+#define luaL_buffsub(B,s) ((B)->n -= (s))
+
+LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
+LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
+LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
+LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
+LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
+LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
+LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
+LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
+
+#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** File handles for IO library
+** =======================================================
+*/
+
+/*
+** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and
+** initial structure 'luaL_Stream' (it may contain other fields
+** after that initial structure).
+*/
+
+#define LUA_FILEHANDLE "FILE*"
+
+
+typedef struct luaL_Stream {
+ FILE *f; /* stream (NULL for incompletely created streams) */
+ lua_CFunction closef; /* to close stream (NULL for closed streams) */
+} luaL_Stream;
+
+/* }====================================================== */
+
+/*
+** {==================================================================
+** "Abstraction Layer" for basic report of messages and errors
+** ===================================================================
+*/
+
+/* print a string */
+#if !defined(lua_writestring)
+#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
+#endif
+
+/* print a newline and flush the output */
+#if !defined(lua_writeline)
+#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
+#endif
+
+/* print an error message */
+#if !defined(lua_writestringerror)
+#define lua_writestringerror(s,p) \
+ (fprintf(stderr, (s), (p)), fflush(stderr))
+#endif
+
+/* }================================================================== */
+
+
+/*
+** {============================================================
+** Compatibility with deprecated conversions
+** =============================================================
+*/
+#if defined(LUA_COMPAT_APIINTCASTS)
+
+#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a))
+#define luaL_optunsigned(L,a,d) \
+ ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))
+
+#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
+#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
+
+#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
+#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
+
+#endif
+/* }============================================================ */
+
+
+
+#endif
+
+
diff --git a/src/libs/3rdparty/lua/src/lbaselib.c b/src/libs/3rdparty/lua/src/lbaselib.c
new file mode 100644
index 0000000000..1d60c9dede
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lbaselib.c
@@ -0,0 +1,549 @@
+/*
+** $Id: lbaselib.c $
+** Basic library
+** See Copyright Notice in lua.h
+*/
+
+#define lbaselib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+static int luaB_print (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ for (i = 1; i <= n; i++) { /* for each argument */
+ size_t l;
+ const char *s = luaL_tolstring(L, i, &l); /* convert it to string */
+ if (i > 1) /* not the first element? */
+ lua_writestring("\t", 1); /* add a tab before it */
+ lua_writestring(s, l); /* print it */
+ lua_pop(L, 1); /* pop result */
+ }
+ lua_writeline();
+ return 0;
+}
+
+
+/*
+** Creates a warning with all given arguments.
+** Check first for errors; otherwise an error may interrupt
+** the composition of a warning, leaving it unfinished.
+*/
+static int luaB_warn (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ luaL_checkstring(L, 1); /* at least one argument */
+ for (i = 2; i <= n; i++)
+ luaL_checkstring(L, i); /* make sure all arguments are strings */
+ for (i = 1; i < n; i++) /* compose warning */
+ lua_warning(L, lua_tostring(L, i), 1);
+ lua_warning(L, lua_tostring(L, n), 0); /* close warning */
+ return 0;
+}
+
+
+#define SPACECHARS " \f\n\r\t\v"
+
+static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
+ lua_Unsigned n = 0;
+ int neg = 0;
+ s += strspn(s, SPACECHARS); /* skip initial spaces */
+ if (*s == '-') { s++; neg = 1; } /* handle sign */
+ else if (*s == '+') s++;
+ if (!isalnum((unsigned char)*s)) /* no digit? */
+ return NULL;
+ do {
+ int digit = (isdigit((unsigned char)*s)) ? *s - '0'
+ : (toupper((unsigned char)*s) - 'A') + 10;
+ if (digit >= base) return NULL; /* invalid numeral */
+ n = n * base + digit;
+ s++;
+ } while (isalnum((unsigned char)*s));
+ s += strspn(s, SPACECHARS); /* skip trailing spaces */
+ *pn = (lua_Integer)((neg) ? (0u - n) : n);
+ return s;
+}
+
+
+static int luaB_tonumber (lua_State *L) {
+ if (lua_isnoneornil(L, 2)) { /* standard conversion? */
+ if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */
+ lua_settop(L, 1); /* yes; return it */
+ return 1;
+ }
+ else {
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (s != NULL && lua_stringtonumber(L, s) == l + 1)
+ return 1; /* successful conversion to number */
+ /* else not a number */
+ luaL_checkany(L, 1); /* (but there must be some parameter) */
+ }
+ }
+ else {
+ size_t l;
+ const char *s;
+ lua_Integer n = 0; /* to avoid warnings */
+ lua_Integer base = luaL_checkinteger(L, 2);
+ luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */
+ s = lua_tolstring(L, 1, &l);
+ luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
+ if (b_str2int(s, (int)base, &n) == s + l) {
+ lua_pushinteger(L, n);
+ return 1;
+ } /* else not a number */
+ } /* else not a number */
+ luaL_pushfail(L); /* not a number */
+ return 1;
+}
+
+
+static int luaB_error (lua_State *L) {
+ int level = (int)luaL_optinteger(L, 2, 1);
+ lua_settop(L, 1);
+ if (lua_type(L, 1) == LUA_TSTRING && level > 0) {
+ luaL_where(L, level); /* add extra information */
+ lua_pushvalue(L, 1);
+ lua_concat(L, 2);
+ }
+ return lua_error(L);
+}
+
+
+static int luaB_getmetatable (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (!lua_getmetatable(L, 1)) {
+ lua_pushnil(L);
+ return 1; /* no metatable */
+ }
+ luaL_getmetafield(L, 1, "__metatable");
+ return 1; /* returns either __metatable field (if present) or metatable */
+}
+
+
+static int luaB_setmetatable (lua_State *L) {
+ int t = lua_type(L, 2);
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
+ if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL))
+ return luaL_error(L, "cannot change a protected metatable");
+ lua_settop(L, 2);
+ lua_setmetatable(L, 1);
+ return 1;
+}
+
+
+static int luaB_rawequal (lua_State *L) {
+ luaL_checkany(L, 1);
+ luaL_checkany(L, 2);
+ lua_pushboolean(L, lua_rawequal(L, 1, 2));
+ return 1;
+}
+
+
+static int luaB_rawlen (lua_State *L) {
+ int t = lua_type(L, 1);
+ luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1,
+ "table or string");
+ lua_pushinteger(L, lua_rawlen(L, 1));
+ return 1;
+}
+
+
+static int luaB_rawget (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ lua_settop(L, 2);
+ lua_rawget(L, 1);
+ return 1;
+}
+
+static int luaB_rawset (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ luaL_checkany(L, 3);
+ lua_settop(L, 3);
+ lua_rawset(L, 1);
+ return 1;
+}
+
+
+static int pushmode (lua_State *L, int oldmode) {
+ if (oldmode == -1)
+ luaL_pushfail(L); /* invalid call to 'lua_gc' */
+ else
+ lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
+ : "generational");
+ return 1;
+}
+
+
+/*
+** check whether call to 'lua_gc' was valid (not inside a finalizer)
+*/
+#define checkvalres(res) { if (res == -1) break; }
+
+static int luaB_collectgarbage (lua_State *L) {
+ static const char *const opts[] = {"stop", "restart", "collect",
+ "count", "step", "setpause", "setstepmul",
+ "isrunning", "generational", "incremental", NULL};
+ static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
+ LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
+ LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC};
+ int o = optsnum[luaL_checkoption(L, 1, "collect", opts)];
+ switch (o) {
+ case LUA_GCCOUNT: {
+ int k = lua_gc(L, o);
+ int b = lua_gc(L, LUA_GCCOUNTB);
+ checkvalres(k);
+ lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024));
+ return 1;
+ }
+ case LUA_GCSTEP: {
+ int step = (int)luaL_optinteger(L, 2, 0);
+ int res = lua_gc(L, o, step);
+ checkvalres(res);
+ lua_pushboolean(L, res);
+ return 1;
+ }
+ case LUA_GCSETPAUSE:
+ case LUA_GCSETSTEPMUL: {
+ int p = (int)luaL_optinteger(L, 2, 0);
+ int previous = lua_gc(L, o, p);
+ checkvalres(previous);
+ lua_pushinteger(L, previous);
+ return 1;
+ }
+ case LUA_GCISRUNNING: {
+ int res = lua_gc(L, o);
+ checkvalres(res);
+ lua_pushboolean(L, res);
+ return 1;
+ }
+ case LUA_GCGEN: {
+ int minormul = (int)luaL_optinteger(L, 2, 0);
+ int majormul = (int)luaL_optinteger(L, 3, 0);
+ return pushmode(L, lua_gc(L, o, minormul, majormul));
+ }
+ case LUA_GCINC: {
+ int pause = (int)luaL_optinteger(L, 2, 0);
+ int stepmul = (int)luaL_optinteger(L, 3, 0);
+ int stepsize = (int)luaL_optinteger(L, 4, 0);
+ return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize));
+ }
+ default: {
+ int res = lua_gc(L, o);
+ checkvalres(res);
+ lua_pushinteger(L, res);
+ return 1;
+ }
+ }
+ luaL_pushfail(L); /* invalid call (inside a finalizer) */
+ return 1;
+}
+
+
+static int luaB_type (lua_State *L) {
+ int t = lua_type(L, 1);
+ luaL_argcheck(L, t != LUA_TNONE, 1, "value expected");
+ lua_pushstring(L, lua_typename(L, t));
+ return 1;
+}
+
+
+static int luaB_next (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 2); /* create a 2nd argument if there isn't one */
+ if (lua_next(L, 1))
+ return 2;
+ else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+
+static int pairscont (lua_State *L, int status, lua_KContext k) {
+ (void)L; (void)status; (void)k; /* unused */
+ return 3;
+}
+
+static int luaB_pairs (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
+ lua_pushcfunction(L, luaB_next); /* will return generator, */
+ lua_pushvalue(L, 1); /* state, */
+ lua_pushnil(L); /* and initial value */
+ }
+ else {
+ lua_pushvalue(L, 1); /* argument 'self' to metamethod */
+ lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
+ }
+ return 3;
+}
+
+
+/*
+** Traversal function for 'ipairs'
+*/
+static int ipairsaux (lua_State *L) {
+ lua_Integer i = luaL_checkinteger(L, 2);
+ i = luaL_intop(+, i, 1);
+ lua_pushinteger(L, i);
+ return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
+}
+
+
+/*
+** 'ipairs' function. Returns 'ipairsaux', given "table", 0.
+** (The given "table" may not be a table.)
+*/
+static int luaB_ipairs (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_pushcfunction(L, ipairsaux); /* iteration function */
+ lua_pushvalue(L, 1); /* state */
+ lua_pushinteger(L, 0); /* initial value */
+ return 3;
+}
+
+
+static int load_aux (lua_State *L, int status, int envidx) {
+ if (l_likely(status == LUA_OK)) {
+ if (envidx != 0) { /* 'env' parameter? */
+ lua_pushvalue(L, envidx); /* environment for loaded function */
+ if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
+ lua_pop(L, 1); /* remove 'env' if not used by previous call */
+ }
+ return 1;
+ }
+ else { /* error (message is on top of the stack) */
+ luaL_pushfail(L);
+ lua_insert(L, -2); /* put before error message */
+ return 2; /* return fail plus error message */
+ }
+}
+
+
+static int luaB_loadfile (lua_State *L) {
+ const char *fname = luaL_optstring(L, 1, NULL);
+ const char *mode = luaL_optstring(L, 2, NULL);
+ int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */
+ int status = luaL_loadfilex(L, fname, mode);
+ return load_aux(L, status, env);
+}
+
+
+/*
+** {======================================================
+** Generic Read function
+** =======================================================
+*/
+
+
+/*
+** reserved slot, above all arguments, to hold a copy of the returned
+** string to avoid it being collected while parsed. 'load' has four
+** optional arguments (chunk, source name, mode, and environment).
+*/
+#define RESERVEDSLOT 5
+
+
+/*
+** Reader for generic 'load' function: 'lua_load' uses the
+** stack for internal stuff, so the reader cannot change the
+** stack top. Instead, it keeps its resulting string in a
+** reserved slot inside the stack.
+*/
+static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
+ (void)(ud); /* not used */
+ luaL_checkstack(L, 2, "too many nested functions");
+ lua_pushvalue(L, 1); /* get function */
+ lua_call(L, 0, 1); /* call it */
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1); /* pop result */
+ *size = 0;
+ return NULL;
+ }
+ else if (l_unlikely(!lua_isstring(L, -1)))
+ luaL_error(L, "reader function must return a string");
+ lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
+ return lua_tolstring(L, RESERVEDSLOT, size);
+}
+
+
+static int luaB_load (lua_State *L) {
+ int status;
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ const char *mode = luaL_optstring(L, 3, "bt");
+ int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */
+ if (s != NULL) { /* loading a string? */
+ const char *chunkname = luaL_optstring(L, 2, s);
+ status = luaL_loadbufferx(L, s, l, chunkname, mode);
+ }
+ else { /* loading from a reader function */
+ const char *chunkname = luaL_optstring(L, 2, "=(load)");
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ lua_settop(L, RESERVEDSLOT); /* create reserved slot */
+ status = lua_load(L, generic_reader, NULL, chunkname, mode);
+ }
+ return load_aux(L, status, env);
+}
+
+/* }====================================================== */
+
+
+static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
+ (void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */
+ return lua_gettop(L) - 1;
+}
+
+
+static int luaB_dofile (lua_State *L) {
+ const char *fname = luaL_optstring(L, 1, NULL);
+ lua_settop(L, 1);
+ if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
+ return lua_error(L);
+ lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
+ return dofilecont(L, 0, 0);
+}
+
+
+static int luaB_assert (lua_State *L) {
+ if (l_likely(lua_toboolean(L, 1))) /* condition is true? */
+ return lua_gettop(L); /* return all arguments */
+ else { /* error */
+ luaL_checkany(L, 1); /* there must be a condition */
+ lua_remove(L, 1); /* remove it */
+ lua_pushliteral(L, "assertion failed!"); /* default message */
+ lua_settop(L, 1); /* leave only message (default if no other one) */
+ return luaB_error(L); /* call 'error' */
+ }
+}
+
+
+static int luaB_select (lua_State *L) {
+ int n = lua_gettop(L);
+ if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {
+ lua_pushinteger(L, n-1);
+ return 1;
+ }
+ else {
+ lua_Integer i = luaL_checkinteger(L, 1);
+ if (i < 0) i = n + i;
+ else if (i > n) i = n;
+ luaL_argcheck(L, 1 <= i, 1, "index out of range");
+ return n - (int)i;
+ }
+}
+
+
+/*
+** Continuation function for 'pcall' and 'xpcall'. Both functions
+** already pushed a 'true' before doing the call, so in case of success
+** 'finishpcall' only has to return everything in the stack minus
+** 'extra' values (where 'extra' is exactly the number of items to be
+** ignored).
+*/
+static int finishpcall (lua_State *L, int status, lua_KContext extra) {
+ if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */
+ lua_pushboolean(L, 0); /* first result (false) */
+ lua_pushvalue(L, -2); /* error message */
+ return 2; /* return false, msg */
+ }
+ else
+ return lua_gettop(L) - (int)extra; /* return all results */
+}
+
+
+static int luaB_pcall (lua_State *L) {
+ int status;
+ luaL_checkany(L, 1);
+ lua_pushboolean(L, 1); /* first result if no errors */
+ lua_insert(L, 1); /* put it in place */
+ status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall);
+ return finishpcall(L, status, 0);
+}
+
+
+/*
+** Do a protected call with error handling. After 'lua_rotate', the
+** stack will have <f, err, true, f, [args...]>; so, the function passes
+** 2 to 'finishpcall' to skip the 2 first values when returning results.
+*/
+static int luaB_xpcall (lua_State *L) {
+ int status;
+ int n = lua_gettop(L);
+ luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */
+ lua_pushboolean(L, 1); /* first result */
+ lua_pushvalue(L, 1); /* function */
+ lua_rotate(L, 3, 2); /* move them below function's arguments */
+ status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall);
+ return finishpcall(L, status, 2);
+}
+
+
+static int luaB_tostring (lua_State *L) {
+ luaL_checkany(L, 1);
+ luaL_tolstring(L, 1, NULL);
+ return 1;
+}
+
+
+static const luaL_Reg base_funcs[] = {
+ {"assert", luaB_assert},
+ {"collectgarbage", luaB_collectgarbage},
+ {"dofile", luaB_dofile},
+ {"error", luaB_error},
+ {"getmetatable", luaB_getmetatable},
+ {"ipairs", luaB_ipairs},
+ {"loadfile", luaB_loadfile},
+ {"load", luaB_load},
+ {"next", luaB_next},
+ {"pairs", luaB_pairs},
+ {"pcall", luaB_pcall},
+ {"print", luaB_print},
+ {"warn", luaB_warn},
+ {"rawequal", luaB_rawequal},
+ {"rawlen", luaB_rawlen},
+ {"rawget", luaB_rawget},
+ {"rawset", luaB_rawset},
+ {"select", luaB_select},
+ {"setmetatable", luaB_setmetatable},
+ {"tonumber", luaB_tonumber},
+ {"tostring", luaB_tostring},
+ {"type", luaB_type},
+ {"xpcall", luaB_xpcall},
+ /* placeholders */
+ {LUA_GNAME, NULL},
+ {"_VERSION", NULL},
+ {NULL, NULL}
+};
+
+
+LUAMOD_API int luaopen_base (lua_State *L) {
+ /* open lib into global table */
+ lua_pushglobaltable(L);
+ luaL_setfuncs(L, base_funcs, 0);
+ /* set global _G */
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, LUA_GNAME);
+ /* set global _VERSION */
+ lua_pushliteral(L, LUA_VERSION);
+ lua_setfield(L, -2, "_VERSION");
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lcode.c b/src/libs/3rdparty/lua/src/lcode.c
new file mode 100644
index 0000000000..1a371ca943
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lcode.c
@@ -0,0 +1,1871 @@
+/*
+** $Id: lcode.c $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+#define lcode_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "lvm.h"
+
+
+/* Maximum number of registers in a Lua function (must fit in 8 bits) */
+#define MAXREGS 255
+
+
+#define hasjumps(e) ((e)->t != (e)->f)
+
+
+static int codesJ (FuncState *fs, OpCode o, int sj, int k);
+
+
+
+/* semantic error */
+l_noret luaK_semerror (LexState *ls, const char *msg) {
+ ls->t.token = 0; /* remove "near <token>" from final message */
+ luaX_syntaxerror(ls, msg);
+}
+
+
+/*
+** If expression is a numeric constant, fills 'v' with its value
+** and returns 1. Otherwise, returns 0.
+*/
+static int tonumeral (const expdesc *e, TValue *v) {
+ if (hasjumps(e))
+ return 0; /* not a numeral */
+ switch (e->k) {
+ case VKINT:
+ if (v) setivalue(v, e->u.ival);
+ return 1;
+ case VKFLT:
+ if (v) setfltvalue(v, e->u.nval);
+ return 1;
+ default: return 0;
+ }
+}
+
+
+/*
+** Get the constant value from a constant expression
+*/
+static TValue *const2val (FuncState *fs, const expdesc *e) {
+ lua_assert(e->k == VCONST);
+ return &fs->ls->dyd->actvar.arr[e->u.info].k;
+}
+
+
+/*
+** If expression is a constant, fills 'v' with its value
+** and returns 1. Otherwise, returns 0.
+*/
+int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) {
+ if (hasjumps(e))
+ return 0; /* not a constant */
+ switch (e->k) {
+ case VFALSE:
+ setbfvalue(v);
+ return 1;
+ case VTRUE:
+ setbtvalue(v);
+ return 1;
+ case VNIL:
+ setnilvalue(v);
+ return 1;
+ case VKSTR: {
+ setsvalue(fs->ls->L, v, e->u.strval);
+ return 1;
+ }
+ case VCONST: {
+ setobj(fs->ls->L, v, const2val(fs, e));
+ return 1;
+ }
+ default: return tonumeral(e, v);
+ }
+}
+
+
+/*
+** Return the previous instruction of the current code. If there
+** may be a jump target between the current instruction and the
+** previous one, return an invalid instruction (to avoid wrong
+** optimizations).
+*/
+static Instruction *previousinstruction (FuncState *fs) {
+ static const Instruction invalidinstruction = ~(Instruction)0;
+ if (fs->pc > fs->lasttarget)
+ return &fs->f->code[fs->pc - 1]; /* previous instruction */
+ else
+ return cast(Instruction*, &invalidinstruction);
+}
+
+
+/*
+** Create a OP_LOADNIL instruction, but try to optimize: if the previous
+** instruction is also OP_LOADNIL and ranges are compatible, adjust
+** range of previous instruction instead of emitting a new one. (For
+** instance, 'local a; local b' will generate a single opcode.)
+*/
+void luaK_nil (FuncState *fs, int from, int n) {
+ int l = from + n - 1; /* last register to set nil */
+ Instruction *previous = previousinstruction(fs);
+ if (GET_OPCODE(*previous) == OP_LOADNIL) { /* previous is LOADNIL? */
+ int pfrom = GETARG_A(*previous); /* get previous range */
+ int pl = pfrom + GETARG_B(*previous);
+ if ((pfrom <= from && from <= pl + 1) ||
+ (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */
+ if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */
+ if (pl > l) l = pl; /* l = max(l, pl) */
+ SETARG_A(*previous, from);
+ SETARG_B(*previous, l - from);
+ return;
+ } /* else go through */
+ }
+ luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */
+}
+
+
+/*
+** Gets the destination address of a jump instruction. Used to traverse
+** a list of jumps.
+*/
+static int getjump (FuncState *fs, int pc) {
+ int offset = GETARG_sJ(fs->f->code[pc]);
+ if (offset == NO_JUMP) /* point to itself represents end of list */
+ return NO_JUMP; /* end of list */
+ else
+ return (pc+1)+offset; /* turn offset into absolute position */
+}
+
+
+/*
+** Fix jump instruction at position 'pc' to jump to 'dest'.
+** (Jump addresses are relative in Lua)
+*/
+static void fixjump (FuncState *fs, int pc, int dest) {
+ Instruction *jmp = &fs->f->code[pc];
+ int offset = dest - (pc + 1);
+ lua_assert(dest != NO_JUMP);
+ if (!(-OFFSET_sJ <= offset && offset <= MAXARG_sJ - OFFSET_sJ))
+ luaX_syntaxerror(fs->ls, "control structure too long");
+ lua_assert(GET_OPCODE(*jmp) == OP_JMP);
+ SETARG_sJ(*jmp, offset);
+}
+
+
+/*
+** Concatenate jump-list 'l2' into jump-list 'l1'
+*/
+void luaK_concat (FuncState *fs, int *l1, int l2) {
+ if (l2 == NO_JUMP) return; /* nothing to concatenate? */
+ else if (*l1 == NO_JUMP) /* no original list? */
+ *l1 = l2; /* 'l1' points to 'l2' */
+ else {
+ int list = *l1;
+ int next;
+ while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */
+ list = next;
+ fixjump(fs, list, l2); /* last element links to 'l2' */
+ }
+}
+
+
+/*
+** Create a jump instruction and return its position, so its destination
+** can be fixed later (with 'fixjump').
+*/
+int luaK_jump (FuncState *fs) {
+ return codesJ(fs, OP_JMP, NO_JUMP, 0);
+}
+
+
+/*
+** Code a 'return' instruction
+*/
+void luaK_ret (FuncState *fs, int first, int nret) {
+ OpCode op;
+ switch (nret) {
+ case 0: op = OP_RETURN0; break;
+ case 1: op = OP_RETURN1; break;
+ default: op = OP_RETURN; break;
+ }
+ luaK_codeABC(fs, op, first, nret + 1, 0);
+}
+
+
+/*
+** Code a "conditional jump", that is, a test or comparison opcode
+** followed by a jump. Return jump position.
+*/
+static int condjump (FuncState *fs, OpCode op, int A, int B, int C, int k) {
+ luaK_codeABCk(fs, op, A, B, C, k);
+ return luaK_jump(fs);
+}
+
+
+/*
+** returns current 'pc' and marks it as a jump target (to avoid wrong
+** optimizations with consecutive instructions not in the same basic block).
+*/
+int luaK_getlabel (FuncState *fs) {
+ fs->lasttarget = fs->pc;
+ return fs->pc;
+}
+
+
+/*
+** Returns the position of the instruction "controlling" a given
+** jump (that is, its condition), or the jump itself if it is
+** unconditional.
+*/
+static Instruction *getjumpcontrol (FuncState *fs, int pc) {
+ Instruction *pi = &fs->f->code[pc];
+ if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))
+ return pi-1;
+ else
+ return pi;
+}
+
+
+/*
+** Patch destination register for a TESTSET instruction.
+** If instruction in position 'node' is not a TESTSET, return 0 ("fails").
+** Otherwise, if 'reg' is not 'NO_REG', set it as the destination
+** register. Otherwise, change instruction to a simple 'TEST' (produces
+** no register value)
+*/
+static int patchtestreg (FuncState *fs, int node, int reg) {
+ Instruction *i = getjumpcontrol(fs, node);
+ if (GET_OPCODE(*i) != OP_TESTSET)
+ return 0; /* cannot patch other instructions */
+ if (reg != NO_REG && reg != GETARG_B(*i))
+ SETARG_A(*i, reg);
+ else {
+ /* no register to put value or register already has the value;
+ change instruction to simple test */
+ *i = CREATE_ABCk(OP_TEST, GETARG_B(*i), 0, 0, GETARG_k(*i));
+ }
+ return 1;
+}
+
+
+/*
+** Traverse a list of tests ensuring no one produces a value
+*/
+static void removevalues (FuncState *fs, int list) {
+ for (; list != NO_JUMP; list = getjump(fs, list))
+ patchtestreg(fs, list, NO_REG);
+}
+
+
+/*
+** Traverse a list of tests, patching their destination address and
+** registers: tests producing values jump to 'vtarget' (and put their
+** values in 'reg'), other tests jump to 'dtarget'.
+*/
+static void patchlistaux (FuncState *fs, int list, int vtarget, int reg,
+ int dtarget) {
+ while (list != NO_JUMP) {
+ int next = getjump(fs, list);
+ if (patchtestreg(fs, list, reg))
+ fixjump(fs, list, vtarget);
+ else
+ fixjump(fs, list, dtarget); /* jump to default target */
+ list = next;
+ }
+}
+
+
+/*
+** Path all jumps in 'list' to jump to 'target'.
+** (The assert means that we cannot fix a jump to a forward address
+** because we only know addresses once code is generated.)
+*/
+void luaK_patchlist (FuncState *fs, int list, int target) {
+ lua_assert(target <= fs->pc);
+ patchlistaux(fs, list, target, NO_REG, target);
+}
+
+
+void luaK_patchtohere (FuncState *fs, int list) {
+ int hr = luaK_getlabel(fs); /* mark "here" as a jump target */
+ luaK_patchlist(fs, list, hr);
+}
+
+
+/* limit for difference between lines in relative line info. */
+#define LIMLINEDIFF 0x80
+
+
+/*
+** Save line info for a new instruction. If difference from last line
+** does not fit in a byte, of after that many instructions, save a new
+** absolute line info; (in that case, the special value 'ABSLINEINFO'
+** in 'lineinfo' signals the existence of this absolute information.)
+** Otherwise, store the difference from last line in 'lineinfo'.
+*/
+static void savelineinfo (FuncState *fs, Proto *f, int line) {
+ int linedif = line - fs->previousline;
+ int pc = fs->pc - 1; /* last instruction coded */
+ if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ >= MAXIWTHABS) {
+ luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo,
+ f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
+ f->abslineinfo[fs->nabslineinfo].pc = pc;
+ f->abslineinfo[fs->nabslineinfo++].line = line;
+ linedif = ABSLINEINFO; /* signal that there is absolute information */
+ fs->iwthabs = 1; /* restart counter */
+ }
+ luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
+ MAX_INT, "opcodes");
+ f->lineinfo[pc] = linedif;
+ fs->previousline = line; /* last line saved */
+}
+
+
+/*
+** Remove line information from the last instruction.
+** If line information for that instruction is absolute, set 'iwthabs'
+** above its max to force the new (replacing) instruction to have
+** absolute line info, too.
+*/
+static void removelastlineinfo (FuncState *fs) {
+ Proto *f = fs->f;
+ int pc = fs->pc - 1; /* last instruction coded */
+ if (f->lineinfo[pc] != ABSLINEINFO) { /* relative line info? */
+ fs->previousline -= f->lineinfo[pc]; /* correct last line saved */
+ fs->iwthabs--; /* undo previous increment */
+ }
+ else { /* absolute line information */
+ lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == pc);
+ fs->nabslineinfo--; /* remove it */
+ fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */
+ }
+}
+
+
+/*
+** Remove the last instruction created, correcting line information
+** accordingly.
+*/
+static void removelastinstruction (FuncState *fs) {
+ removelastlineinfo(fs);
+ fs->pc--;
+}
+
+
+/*
+** Emit instruction 'i', checking for array sizes and saving also its
+** line information. Return 'i' position.
+*/
+int luaK_code (FuncState *fs, Instruction i) {
+ Proto *f = fs->f;
+ /* put new instruction in code array */
+ luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction,
+ MAX_INT, "opcodes");
+ f->code[fs->pc++] = i;
+ savelineinfo(fs, f, fs->ls->lastline);
+ return fs->pc - 1; /* index of new instruction */
+}
+
+
+/*
+** Format and emit an 'iABC' instruction. (Assertions check consistency
+** of parameters versus opcode.)
+*/
+int luaK_codeABCk (FuncState *fs, OpCode o, int a, int b, int c, int k) {
+ lua_assert(getOpMode(o) == iABC);
+ lua_assert(a <= MAXARG_A && b <= MAXARG_B &&
+ c <= MAXARG_C && (k & ~1) == 0);
+ return luaK_code(fs, CREATE_ABCk(o, a, b, c, k));
+}
+
+
+/*
+** Format and emit an 'iABx' instruction.
+*/
+int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
+ lua_assert(getOpMode(o) == iABx);
+ lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx);
+ return luaK_code(fs, CREATE_ABx(o, a, bc));
+}
+
+
+/*
+** Format and emit an 'iAsBx' instruction.
+*/
+int luaK_codeAsBx (FuncState *fs, OpCode o, int a, int bc) {
+ unsigned int b = bc + OFFSET_sBx;
+ lua_assert(getOpMode(o) == iAsBx);
+ lua_assert(a <= MAXARG_A && b <= MAXARG_Bx);
+ return luaK_code(fs, CREATE_ABx(o, a, b));
+}
+
+
+/*
+** Format and emit an 'isJ' instruction.
+*/
+static int codesJ (FuncState *fs, OpCode o, int sj, int k) {
+ unsigned int j = sj + OFFSET_sJ;
+ lua_assert(getOpMode(o) == isJ);
+ lua_assert(j <= MAXARG_sJ && (k & ~1) == 0);
+ return luaK_code(fs, CREATE_sJ(o, j, k));
+}
+
+
+/*
+** Emit an "extra argument" instruction (format 'iAx')
+*/
+static int codeextraarg (FuncState *fs, int a) {
+ lua_assert(a <= MAXARG_Ax);
+ return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a));
+}
+
+
+/*
+** Emit a "load constant" instruction, using either 'OP_LOADK'
+** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX'
+** instruction with "extra argument".
+*/
+static int luaK_codek (FuncState *fs, int reg, int k) {
+ if (k <= MAXARG_Bx)
+ return luaK_codeABx(fs, OP_LOADK, reg, k);
+ else {
+ int p = luaK_codeABx(fs, OP_LOADKX, reg, 0);
+ codeextraarg(fs, k);
+ return p;
+ }
+}
+
+
+/*
+** Check register-stack level, keeping track of its maximum size
+** in field 'maxstacksize'
+*/
+void luaK_checkstack (FuncState *fs, int n) {
+ int newstack = fs->freereg + n;
+ if (newstack > fs->f->maxstacksize) {
+ if (newstack >= MAXREGS)
+ luaX_syntaxerror(fs->ls,
+ "function or expression needs too many registers");
+ fs->f->maxstacksize = cast_byte(newstack);
+ }
+}
+
+
+/*
+** Reserve 'n' registers in register stack
+*/
+void luaK_reserveregs (FuncState *fs, int n) {
+ luaK_checkstack(fs, n);
+ fs->freereg += n;
+}
+
+
+/*
+** Free register 'reg', if it is neither a constant index nor
+** a local variable.
+)
+*/
+static void freereg (FuncState *fs, int reg) {
+ if (reg >= luaY_nvarstack(fs)) {
+ fs->freereg--;
+ lua_assert(reg == fs->freereg);
+ }
+}
+
+
+/*
+** Free two registers in proper order
+*/
+static void freeregs (FuncState *fs, int r1, int r2) {
+ if (r1 > r2) {
+ freereg(fs, r1);
+ freereg(fs, r2);
+ }
+ else {
+ freereg(fs, r2);
+ freereg(fs, r1);
+ }
+}
+
+
+/*
+** Free register used by expression 'e' (if any)
+*/
+static void freeexp (FuncState *fs, expdesc *e) {
+ if (e->k == VNONRELOC)
+ freereg(fs, e->u.info);
+}
+
+
+/*
+** Free registers used by expressions 'e1' and 'e2' (if any) in proper
+** order.
+*/
+static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) {
+ int r1 = (e1->k == VNONRELOC) ? e1->u.info : -1;
+ int r2 = (e2->k == VNONRELOC) ? e2->u.info : -1;
+ freeregs(fs, r1, r2);
+}
+
+
+/*
+** Add constant 'v' to prototype's list of constants (field 'k').
+** Use scanner's table to cache position of constants in constant list
+** and try to reuse constants. Because some values should not be used
+** as keys (nil cannot be a key, integer keys can collapse with float
+** keys), the caller must provide a useful 'key' for indexing the cache.
+** Note that all functions share the same table, so entering or exiting
+** a function can make some indices wrong.
+*/
+static int addk (FuncState *fs, TValue *key, TValue *v) {
+ TValue val;
+ lua_State *L = fs->ls->L;
+ Proto *f = fs->f;
+ const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */
+ int k, oldsize;
+ if (ttisinteger(idx)) { /* is there an index there? */
+ k = cast_int(ivalue(idx));
+ /* correct value? (warning: must distinguish floats from integers!) */
+ if (k < fs->nk && ttypetag(&f->k[k]) == ttypetag(v) &&
+ luaV_rawequalobj(&f->k[k], v))
+ return k; /* reuse index */
+ }
+ /* constant not found; create a new entry */
+ oldsize = f->sizek;
+ k = fs->nk;
+ /* numerical value does not need GC barrier;
+ table has no metatable, so it does not need to invalidate cache */
+ setivalue(&val, k);
+ luaH_finishset(L, fs->ls->h, key, idx, &val);
+ luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
+ while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
+ setobj(L, &f->k[k], v);
+ fs->nk++;
+ luaC_barrier(L, f, v);
+ return k;
+}
+
+
+/*
+** Add a string to list of constants and return its index.
+*/
+static int stringK (FuncState *fs, TString *s) {
+ TValue o;
+ setsvalue(fs->ls->L, &o, s);
+ return addk(fs, &o, &o); /* use string itself as key */
+}
+
+
+/*
+** Add an integer to list of constants and return its index.
+*/
+static int luaK_intK (FuncState *fs, lua_Integer n) {
+ TValue o;
+ setivalue(&o, n);
+ return addk(fs, &o, &o); /* use integer itself as key */
+}
+
+/*
+** Add a float to list of constants and return its index. Floats
+** with integral values need a different key, to avoid collision
+** with actual integers. To that, we add to the number its smaller
+** power-of-two fraction that is still significant in its scale.
+** For doubles, that would be 1/2^52.
+** (This method is not bulletproof: there may be another float
+** with that value, and for floats larger than 2^53 the result is
+** still an integer. At worst, this only wastes an entry with
+** a duplicate.)
+*/
+static int luaK_numberK (FuncState *fs, lua_Number r) {
+ TValue o;
+ lua_Integer ik;
+ setfltvalue(&o, r);
+ if (!luaV_flttointeger(r, &ik, F2Ieq)) /* not an integral value? */
+ return addk(fs, &o, &o); /* use number itself as key */
+ else { /* must build an alternative key */
+ const int nbm = l_floatatt(MANT_DIG);
+ const lua_Number q = l_mathop(ldexp)(l_mathop(1.0), -nbm + 1);
+ const lua_Number k = (ik == 0) ? q : r + r*q; /* new key */
+ TValue kv;
+ setfltvalue(&kv, k);
+ /* result is not an integral value, unless value is too large */
+ lua_assert(!luaV_flttointeger(k, &ik, F2Ieq) ||
+ l_mathop(fabs)(r) >= l_mathop(1e6));
+ return addk(fs, &kv, &o);
+ }
+}
+
+
+/*
+** Add a false to list of constants and return its index.
+*/
+static int boolF (FuncState *fs) {
+ TValue o;
+ setbfvalue(&o);
+ return addk(fs, &o, &o); /* use boolean itself as key */
+}
+
+
+/*
+** Add a true to list of constants and return its index.
+*/
+static int boolT (FuncState *fs) {
+ TValue o;
+ setbtvalue(&o);
+ return addk(fs, &o, &o); /* use boolean itself as key */
+}
+
+
+/*
+** Add nil to list of constants and return its index.
+*/
+static int nilK (FuncState *fs) {
+ TValue k, v;
+ setnilvalue(&v);
+ /* cannot use nil as key; instead use table itself to represent nil */
+ sethvalue(fs->ls->L, &k, fs->ls->h);
+ return addk(fs, &k, &v);
+}
+
+
+/*
+** Check whether 'i' can be stored in an 'sC' operand. Equivalent to
+** (0 <= int2sC(i) && int2sC(i) <= MAXARG_C) but without risk of
+** overflows in the hidden addition inside 'int2sC'.
+*/
+static int fitsC (lua_Integer i) {
+ return (l_castS2U(i) + OFFSET_sC <= cast_uint(MAXARG_C));
+}
+
+
+/*
+** Check whether 'i' can be stored in an 'sBx' operand.
+*/
+static int fitsBx (lua_Integer i) {
+ return (-OFFSET_sBx <= i && i <= MAXARG_Bx - OFFSET_sBx);
+}
+
+
+void luaK_int (FuncState *fs, int reg, lua_Integer i) {
+ if (fitsBx(i))
+ luaK_codeAsBx(fs, OP_LOADI, reg, cast_int(i));
+ else
+ luaK_codek(fs, reg, luaK_intK(fs, i));
+}
+
+
+static void luaK_float (FuncState *fs, int reg, lua_Number f) {
+ lua_Integer fi;
+ if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi))
+ luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
+ else
+ luaK_codek(fs, reg, luaK_numberK(fs, f));
+}
+
+
+/*
+** Convert a constant in 'v' into an expression description 'e'
+*/
+static void const2exp (TValue *v, expdesc *e) {
+ switch (ttypetag(v)) {
+ case LUA_VNUMINT:
+ e->k = VKINT; e->u.ival = ivalue(v);
+ break;
+ case LUA_VNUMFLT:
+ e->k = VKFLT; e->u.nval = fltvalue(v);
+ break;
+ case LUA_VFALSE:
+ e->k = VFALSE;
+ break;
+ case LUA_VTRUE:
+ e->k = VTRUE;
+ break;
+ case LUA_VNIL:
+ e->k = VNIL;
+ break;
+ case LUA_VSHRSTR: case LUA_VLNGSTR:
+ e->k = VKSTR; e->u.strval = tsvalue(v);
+ break;
+ default: lua_assert(0);
+ }
+}
+
+
+/*
+** Fix an expression to return the number of results 'nresults'.
+** 'e' must be a multi-ret expression (function call or vararg).
+*/
+void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
+ Instruction *pc = &getinstruction(fs, e);
+ if (e->k == VCALL) /* expression is an open function call? */
+ SETARG_C(*pc, nresults + 1);
+ else {
+ lua_assert(e->k == VVARARG);
+ SETARG_C(*pc, nresults + 1);
+ SETARG_A(*pc, fs->freereg);
+ luaK_reserveregs(fs, 1);
+ }
+}
+
+
+/*
+** Convert a VKSTR to a VK
+*/
+static void str2K (FuncState *fs, expdesc *e) {
+ lua_assert(e->k == VKSTR);
+ e->u.info = stringK(fs, e->u.strval);
+ e->k = VK;
+}
+
+
+/*
+** Fix an expression to return one result.
+** If expression is not a multi-ret expression (function call or
+** vararg), it already returns one result, so nothing needs to be done.
+** Function calls become VNONRELOC expressions (as its result comes
+** fixed in the base register of the call), while vararg expressions
+** become VRELOC (as OP_VARARG puts its results where it wants).
+** (Calls are created returning one result, so that does not need
+** to be fixed.)
+*/
+void luaK_setoneret (FuncState *fs, expdesc *e) {
+ if (e->k == VCALL) { /* expression is an open function call? */
+ /* already returns 1 value */
+ lua_assert(GETARG_C(getinstruction(fs, e)) == 2);
+ e->k = VNONRELOC; /* result has fixed position */
+ e->u.info = GETARG_A(getinstruction(fs, e));
+ }
+ else if (e->k == VVARARG) {
+ SETARG_C(getinstruction(fs, e), 2);
+ e->k = VRELOC; /* can relocate its simple result */
+ }
+}
+
+
+/*
+** Ensure that expression 'e' is not a variable (nor a <const>).
+** (Expression still may have jump lists.)
+*/
+void luaK_dischargevars (FuncState *fs, expdesc *e) {
+ switch (e->k) {
+ case VCONST: {
+ const2exp(const2val(fs, e), e);
+ break;
+ }
+ case VLOCAL: { /* already in a register */
+ e->u.info = e->u.var.ridx;
+ e->k = VNONRELOC; /* becomes a non-relocatable value */
+ break;
+ }
+ case VUPVAL: { /* move value to some (pending) register */
+ e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0);
+ e->k = VRELOC;
+ break;
+ }
+ case VINDEXUP: {
+ e->u.info = luaK_codeABC(fs, OP_GETTABUP, 0, e->u.ind.t, e->u.ind.idx);
+ e->k = VRELOC;
+ break;
+ }
+ case VINDEXI: {
+ freereg(fs, e->u.ind.t);
+ e->u.info = luaK_codeABC(fs, OP_GETI, 0, e->u.ind.t, e->u.ind.idx);
+ e->k = VRELOC;
+ break;
+ }
+ case VINDEXSTR: {
+ freereg(fs, e->u.ind.t);
+ e->u.info = luaK_codeABC(fs, OP_GETFIELD, 0, e->u.ind.t, e->u.ind.idx);
+ e->k = VRELOC;
+ break;
+ }
+ case VINDEXED: {
+ freeregs(fs, e->u.ind.t, e->u.ind.idx);
+ e->u.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.ind.t, e->u.ind.idx);
+ e->k = VRELOC;
+ break;
+ }
+ case VVARARG: case VCALL: {
+ luaK_setoneret(fs, e);
+ break;
+ }
+ default: break; /* there is one value available (somewhere) */
+ }
+}
+
+
+/*
+** Ensure expression value is in register 'reg', making 'e' a
+** non-relocatable expression.
+** (Expression still may have jump lists.)
+*/
+static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VNIL: {
+ luaK_nil(fs, reg, 1);
+ break;
+ }
+ case VFALSE: {
+ luaK_codeABC(fs, OP_LOADFALSE, reg, 0, 0);
+ break;
+ }
+ case VTRUE: {
+ luaK_codeABC(fs, OP_LOADTRUE, reg, 0, 0);
+ break;
+ }
+ case VKSTR: {
+ str2K(fs, e);
+ } /* FALLTHROUGH */
+ case VK: {
+ luaK_codek(fs, reg, e->u.info);
+ break;
+ }
+ case VKFLT: {
+ luaK_float(fs, reg, e->u.nval);
+ break;
+ }
+ case VKINT: {
+ luaK_int(fs, reg, e->u.ival);
+ break;
+ }
+ case VRELOC: {
+ Instruction *pc = &getinstruction(fs, e);
+ SETARG_A(*pc, reg); /* instruction will put result in 'reg' */
+ break;
+ }
+ case VNONRELOC: {
+ if (reg != e->u.info)
+ luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0);
+ break;
+ }
+ default: {
+ lua_assert(e->k == VJMP);
+ return; /* nothing to do... */
+ }
+ }
+ e->u.info = reg;
+ e->k = VNONRELOC;
+}
+
+
+/*
+** Ensure expression value is in a register, making 'e' a
+** non-relocatable expression.
+** (Expression still may have jump lists.)
+*/
+static void discharge2anyreg (FuncState *fs, expdesc *e) {
+ if (e->k != VNONRELOC) { /* no fixed register yet? */
+ luaK_reserveregs(fs, 1); /* get a register */
+ discharge2reg(fs, e, fs->freereg-1); /* put value there */
+ }
+}
+
+
+static int code_loadbool (FuncState *fs, int A, OpCode op) {
+ luaK_getlabel(fs); /* those instructions may be jump targets */
+ return luaK_codeABC(fs, op, A, 0, 0);
+}
+
+
+/*
+** check whether list has any jump that do not produce a value
+** or produce an inverted value
+*/
+static int need_value (FuncState *fs, int list) {
+ for (; list != NO_JUMP; list = getjump(fs, list)) {
+ Instruction i = *getjumpcontrol(fs, list);
+ if (GET_OPCODE(i) != OP_TESTSET) return 1;
+ }
+ return 0; /* not found */
+}
+
+
+/*
+** Ensures final expression result (which includes results from its
+** jump lists) is in register 'reg'.
+** If expression has jumps, need to patch these jumps either to
+** its final position or to "load" instructions (for those tests
+** that do not produce values).
+*/
+static void exp2reg (FuncState *fs, expdesc *e, int reg) {
+ discharge2reg(fs, e, reg);
+ if (e->k == VJMP) /* expression itself is a test? */
+ luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */
+ if (hasjumps(e)) {
+ int final; /* position after whole expression */
+ int p_f = NO_JUMP; /* position of an eventual LOAD false */
+ int p_t = NO_JUMP; /* position of an eventual LOAD true */
+ if (need_value(fs, e->t) || need_value(fs, e->f)) {
+ int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);
+ p_f = code_loadbool(fs, reg, OP_LFALSESKIP); /* skip next inst. */
+ p_t = code_loadbool(fs, reg, OP_LOADTRUE);
+ /* jump around these booleans if 'e' is not a test */
+ luaK_patchtohere(fs, fj);
+ }
+ final = luaK_getlabel(fs);
+ patchlistaux(fs, e->f, final, reg, p_f);
+ patchlistaux(fs, e->t, final, reg, p_t);
+ }
+ e->f = e->t = NO_JUMP;
+ e->u.info = reg;
+ e->k = VNONRELOC;
+}
+
+
+/*
+** Ensures final expression result is in next available register.
+*/
+void luaK_exp2nextreg (FuncState *fs, expdesc *e) {
+ luaK_dischargevars(fs, e);
+ freeexp(fs, e);
+ luaK_reserveregs(fs, 1);
+ exp2reg(fs, e, fs->freereg - 1);
+}
+
+
+/*
+** Ensures final expression result is in some (any) register
+** and return that register.
+*/
+int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
+ luaK_dischargevars(fs, e);
+ if (e->k == VNONRELOC) { /* expression already has a register? */
+ if (!hasjumps(e)) /* no jumps? */
+ return e->u.info; /* result is already in a register */
+ if (e->u.info >= luaY_nvarstack(fs)) { /* reg. is not a local? */
+ exp2reg(fs, e, e->u.info); /* put final result in it */
+ return e->u.info;
+ }
+ /* else expression has jumps and cannot change its register
+ to hold the jump values, because it is a local variable.
+ Go through to the default case. */
+ }
+ luaK_exp2nextreg(fs, e); /* default: use next available register */
+ return e->u.info;
+}
+
+
+/*
+** Ensures final expression result is either in a register
+** or in an upvalue.
+*/
+void luaK_exp2anyregup (FuncState *fs, expdesc *e) {
+ if (e->k != VUPVAL || hasjumps(e))
+ luaK_exp2anyreg(fs, e);
+}
+
+
+/*
+** Ensures final expression result is either in a register
+** or it is a constant.
+*/
+void luaK_exp2val (FuncState *fs, expdesc *e) {
+ if (hasjumps(e))
+ luaK_exp2anyreg(fs, e);
+ else
+ luaK_dischargevars(fs, e);
+}
+
+
+/*
+** Try to make 'e' a K expression with an index in the range of R/K
+** indices. Return true iff succeeded.
+*/
+static int luaK_exp2K (FuncState *fs, expdesc *e) {
+ if (!hasjumps(e)) {
+ int info;
+ switch (e->k) { /* move constants to 'k' */
+ case VTRUE: info = boolT(fs); break;
+ case VFALSE: info = boolF(fs); break;
+ case VNIL: info = nilK(fs); break;
+ case VKINT: info = luaK_intK(fs, e->u.ival); break;
+ case VKFLT: info = luaK_numberK(fs, e->u.nval); break;
+ case VKSTR: info = stringK(fs, e->u.strval); break;
+ case VK: info = e->u.info; break;
+ default: return 0; /* not a constant */
+ }
+ if (info <= MAXINDEXRK) { /* does constant fit in 'argC'? */
+ e->k = VK; /* make expression a 'K' expression */
+ e->u.info = info;
+ return 1;
+ }
+ }
+ /* else, expression doesn't fit; leave it unchanged */
+ return 0;
+}
+
+
+/*
+** Ensures final expression result is in a valid R/K index
+** (that is, it is either in a register or in 'k' with an index
+** in the range of R/K indices).
+** Returns 1 iff expression is K.
+*/
+int luaK_exp2RK (FuncState *fs, expdesc *e) {
+ if (luaK_exp2K(fs, e))
+ return 1;
+ else { /* not a constant in the right range: put it in a register */
+ luaK_exp2anyreg(fs, e);
+ return 0;
+ }
+}
+
+
+static void codeABRK (FuncState *fs, OpCode o, int a, int b,
+ expdesc *ec) {
+ int k = luaK_exp2RK(fs, ec);
+ luaK_codeABCk(fs, o, a, b, ec->u.info, k);
+}
+
+
+/*
+** Generate code to store result of expression 'ex' into variable 'var'.
+*/
+void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
+ switch (var->k) {
+ case VLOCAL: {
+ freeexp(fs, ex);
+ exp2reg(fs, ex, var->u.var.ridx); /* compute 'ex' into proper place */
+ return;
+ }
+ case VUPVAL: {
+ int e = luaK_exp2anyreg(fs, ex);
+ luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
+ break;
+ }
+ case VINDEXUP: {
+ codeABRK(fs, OP_SETTABUP, var->u.ind.t, var->u.ind.idx, ex);
+ break;
+ }
+ case VINDEXI: {
+ codeABRK(fs, OP_SETI, var->u.ind.t, var->u.ind.idx, ex);
+ break;
+ }
+ case VINDEXSTR: {
+ codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex);
+ break;
+ }
+ case VINDEXED: {
+ codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex);
+ break;
+ }
+ default: lua_assert(0); /* invalid var kind to store */
+ }
+ freeexp(fs, ex);
+}
+
+
+/*
+** Emit SELF instruction (convert expression 'e' into 'e:key(e,').
+*/
+void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
+ int ereg;
+ luaK_exp2anyreg(fs, e);
+ ereg = e->u.info; /* register where 'e' was placed */
+ freeexp(fs, e);
+ e->u.info = fs->freereg; /* base register for op_self */
+ e->k = VNONRELOC; /* self expression has a fixed register */
+ luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */
+ codeABRK(fs, OP_SELF, e->u.info, ereg, key);
+ freeexp(fs, key);
+}
+
+
+/*
+** Negate condition 'e' (where 'e' is a comparison).
+*/
+static void negatecondition (FuncState *fs, expdesc *e) {
+ Instruction *pc = getjumpcontrol(fs, e->u.info);
+ lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
+ GET_OPCODE(*pc) != OP_TEST);
+ SETARG_k(*pc, (GETARG_k(*pc) ^ 1));
+}
+
+
+/*
+** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond'
+** is true, code will jump if 'e' is true.) Return jump position.
+** Optimize when 'e' is 'not' something, inverting the condition
+** and removing the 'not'.
+*/
+static int jumponcond (FuncState *fs, expdesc *e, int cond) {
+ if (e->k == VRELOC) {
+ Instruction ie = getinstruction(fs, e);
+ if (GET_OPCODE(ie) == OP_NOT) {
+ removelastinstruction(fs); /* remove previous OP_NOT */
+ return condjump(fs, OP_TEST, GETARG_B(ie), 0, 0, !cond);
+ }
+ /* else go through */
+ }
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ return condjump(fs, OP_TESTSET, NO_REG, e->u.info, 0, cond);
+}
+
+
+/*
+** Emit code to go through if 'e' is true, jump otherwise.
+*/
+void luaK_goiftrue (FuncState *fs, expdesc *e) {
+ int pc; /* pc of new jump */
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VJMP: { /* condition? */
+ negatecondition(fs, e); /* jump when it is false */
+ pc = e->u.info; /* save jump position */
+ break;
+ }
+ case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: {
+ pc = NO_JUMP; /* always true; do nothing */
+ break;
+ }
+ default: {
+ pc = jumponcond(fs, e, 0); /* jump when false */
+ break;
+ }
+ }
+ luaK_concat(fs, &e->f, pc); /* insert new jump in false list */
+ luaK_patchtohere(fs, e->t); /* true list jumps to here (to go through) */
+ e->t = NO_JUMP;
+}
+
+
+/*
+** Emit code to go through if 'e' is false, jump otherwise.
+*/
+void luaK_goiffalse (FuncState *fs, expdesc *e) {
+ int pc; /* pc of new jump */
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VJMP: {
+ pc = e->u.info; /* already jump if true */
+ break;
+ }
+ case VNIL: case VFALSE: {
+ pc = NO_JUMP; /* always false; do nothing */
+ break;
+ }
+ default: {
+ pc = jumponcond(fs, e, 1); /* jump if true */
+ break;
+ }
+ }
+ luaK_concat(fs, &e->t, pc); /* insert new jump in 't' list */
+ luaK_patchtohere(fs, e->f); /* false list jumps to here (to go through) */
+ e->f = NO_JUMP;
+}
+
+
+/*
+** Code 'not e', doing constant folding.
+*/
+static void codenot (FuncState *fs, expdesc *e) {
+ switch (e->k) {
+ case VNIL: case VFALSE: {
+ e->k = VTRUE; /* true == not nil == not false */
+ break;
+ }
+ case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: {
+ e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */
+ break;
+ }
+ case VJMP: {
+ negatecondition(fs, e);
+ break;
+ }
+ case VRELOC:
+ case VNONRELOC: {
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ e->u.info = luaK_codeABC(fs, OP_NOT, 0, e->u.info, 0);
+ e->k = VRELOC;
+ break;
+ }
+ default: lua_assert(0); /* cannot happen */
+ }
+ /* interchange true and false lists */
+ { int temp = e->f; e->f = e->t; e->t = temp; }
+ removevalues(fs, e->f); /* values are useless when negated */
+ removevalues(fs, e->t);
+}
+
+
+/*
+** Check whether expression 'e' is a small literal string
+*/
+static int isKstr (FuncState *fs, expdesc *e) {
+ return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B &&
+ ttisshrstring(&fs->f->k[e->u.info]));
+}
+
+/*
+** Check whether expression 'e' is a literal integer.
+*/
+int luaK_isKint (expdesc *e) {
+ return (e->k == VKINT && !hasjumps(e));
+}
+
+
+/*
+** Check whether expression 'e' is a literal integer in
+** proper range to fit in register C
+*/
+static int isCint (expdesc *e) {
+ return luaK_isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C));
+}
+
+
+/*
+** Check whether expression 'e' is a literal integer in
+** proper range to fit in register sC
+*/
+static int isSCint (expdesc *e) {
+ return luaK_isKint(e) && fitsC(e->u.ival);
+}
+
+
+/*
+** Check whether expression 'e' is a literal integer or float in
+** proper range to fit in a register (sB or sC).
+*/
+static int isSCnumber (expdesc *e, int *pi, int *isfloat) {
+ lua_Integer i;
+ if (e->k == VKINT)
+ i = e->u.ival;
+ else if (e->k == VKFLT && luaV_flttointeger(e->u.nval, &i, F2Ieq))
+ *isfloat = 1;
+ else
+ return 0; /* not a number */
+ if (!hasjumps(e) && fitsC(i)) {
+ *pi = int2sC(cast_int(i));
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/*
+** Create expression 't[k]'. 't' must have its final result already in a
+** register or upvalue. Upvalues can only be indexed by literal strings.
+** Keys can be literal strings in the constant table or arbitrary
+** values in registers.
+*/
+void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
+ if (k->k == VKSTR)
+ str2K(fs, k);
+ lua_assert(!hasjumps(t) &&
+ (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL));
+ if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */
+ luaK_exp2anyreg(fs, t); /* put it in a register */
+ if (t->k == VUPVAL) {
+ t->u.ind.t = t->u.info; /* upvalue index */
+ t->u.ind.idx = k->u.info; /* literal string */
+ t->k = VINDEXUP;
+ }
+ else {
+ /* register index of the table */
+ t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info;
+ if (isKstr(fs, k)) {
+ t->u.ind.idx = k->u.info; /* literal string */
+ t->k = VINDEXSTR;
+ }
+ else if (isCint(k)) {
+ t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */
+ t->k = VINDEXI;
+ }
+ else {
+ t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */
+ t->k = VINDEXED;
+ }
+ }
+}
+
+
+/*
+** Return false if folding can raise an error.
+** Bitwise operations need operands convertible to integers; division
+** operations cannot have 0 as divisor.
+*/
+static int validop (int op, TValue *v1, TValue *v2) {
+ switch (op) {
+ case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
+ case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */
+ lua_Integer i;
+ return (luaV_tointegerns(v1, &i, LUA_FLOORN2I) &&
+ luaV_tointegerns(v2, &i, LUA_FLOORN2I));
+ }
+ case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */
+ return (nvalue(v2) != 0);
+ default: return 1; /* everything else is valid */
+ }
+}
+
+
+/*
+** Try to "constant-fold" an operation; return 1 iff successful.
+** (In this case, 'e1' has the final result.)
+*/
+static int constfolding (FuncState *fs, int op, expdesc *e1,
+ const expdesc *e2) {
+ TValue v1, v2, res;
+ if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2))
+ return 0; /* non-numeric operands or not safe to fold */
+ luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */
+ if (ttisinteger(&res)) {
+ e1->k = VKINT;
+ e1->u.ival = ivalue(&res);
+ }
+ else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */
+ lua_Number n = fltvalue(&res);
+ if (luai_numisnan(n) || n == 0)
+ return 0;
+ e1->k = VKFLT;
+ e1->u.nval = n;
+ }
+ return 1;
+}
+
+
+/*
+** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP)
+*/
+l_sinline OpCode binopr2op (BinOpr opr, BinOpr baser, OpCode base) {
+ lua_assert(baser <= opr &&
+ ((baser == OPR_ADD && opr <= OPR_SHR) ||
+ (baser == OPR_LT && opr <= OPR_LE)));
+ return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base));
+}
+
+
+/*
+** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP)
+*/
+l_sinline OpCode unopr2op (UnOpr opr) {
+ return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) +
+ cast_int(OP_UNM));
+}
+
+
+/*
+** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM)
+*/
+l_sinline TMS binopr2TM (BinOpr opr) {
+ lua_assert(OPR_ADD <= opr && opr <= OPR_SHR);
+ return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD));
+}
+
+
+/*
+** Emit code for unary expressions that "produce values"
+** (everything but 'not').
+** Expression to produce final result will be encoded in 'e'.
+*/
+static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) {
+ int r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */
+ freeexp(fs, e);
+ e->u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */
+ e->k = VRELOC; /* all those operations are relocatable */
+ luaK_fixline(fs, line);
+}
+
+
+/*
+** Emit code for binary expressions that "produce values"
+** (everything but logical operators 'and'/'or' and comparison
+** operators).
+** Expression to produce final result will be encoded in 'e1'.
+*/
+static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
+ OpCode op, int v2, int flip, int line,
+ OpCode mmop, TMS event) {
+ int v1 = luaK_exp2anyreg(fs, e1);
+ int pc = luaK_codeABCk(fs, op, 0, v1, v2, 0);
+ freeexps(fs, e1, e2);
+ e1->u.info = pc;
+ e1->k = VRELOC; /* all those operations are relocatable */
+ luaK_fixline(fs, line);
+ luaK_codeABCk(fs, mmop, v1, v2, event, flip); /* to call metamethod */
+ luaK_fixline(fs, line);
+}
+
+
+/*
+** Emit code for binary expressions that "produce values" over
+** two registers.
+*/
+static void codebinexpval (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int line) {
+ OpCode op = binopr2op(opr, OPR_ADD, OP_ADD);
+ int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */
+ /* 'e1' must be already in a register or it is a constant */
+ lua_assert((VNIL <= e1->k && e1->k <= VKSTR) ||
+ e1->k == VNONRELOC || e1->k == VRELOC);
+ lua_assert(OP_ADD <= op && op <= OP_SHR);
+ finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr));
+}
+
+
+/*
+** Code binary operators with immediate operands.
+*/
+static void codebini (FuncState *fs, OpCode op,
+ expdesc *e1, expdesc *e2, int flip, int line,
+ TMS event) {
+ int v2 = int2sC(cast_int(e2->u.ival)); /* immediate operand */
+ lua_assert(e2->k == VKINT);
+ finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINI, event);
+}
+
+
+/*
+** Code binary operators with K operand.
+*/
+static void codebinK (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int flip, int line) {
+ TMS event = binopr2TM(opr);
+ int v2 = e2->u.info; /* K index */
+ OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK);
+ finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
+}
+
+
+/* Try to code a binary operator negating its second operand.
+** For the metamethod, 2nd operand must keep its original value.
+*/
+static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2,
+ OpCode op, int line, TMS event) {
+ if (!luaK_isKint(e2))
+ return 0; /* not an integer constant */
+ else {
+ lua_Integer i2 = e2->u.ival;
+ if (!(fitsC(i2) && fitsC(-i2)))
+ return 0; /* not in the proper range */
+ else { /* operating a small integer constant */
+ int v2 = cast_int(i2);
+ finishbinexpval(fs, e1, e2, op, int2sC(-v2), 0, line, OP_MMBINI, event);
+ /* correct metamethod argument */
+ SETARG_B(fs->f->code[fs->pc - 1], int2sC(v2));
+ return 1; /* successfully coded */
+ }
+ }
+}
+
+
+static void swapexps (expdesc *e1, expdesc *e2) {
+ expdesc temp = *e1; *e1 = *e2; *e2 = temp; /* swap 'e1' and 'e2' */
+}
+
+
+/*
+** Code binary operators with no constant operand.
+*/
+static void codebinNoK (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int flip, int line) {
+ if (flip)
+ swapexps(e1, e2); /* back to original order */
+ codebinexpval(fs, opr, e1, e2, line); /* use standard operators */
+}
+
+
+/*
+** Code arithmetic operators ('+', '-', ...). If second operand is a
+** constant in the proper range, use variant opcodes with K operands.
+*/
+static void codearith (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int flip, int line) {
+ if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) /* K operand? */
+ codebinK(fs, opr, e1, e2, flip, line);
+ else /* 'e2' is neither an immediate nor a K operand */
+ codebinNoK(fs, opr, e1, e2, flip, line);
+}
+
+
+/*
+** Code commutative operators ('+', '*'). If first operand is a
+** numeric constant, change order of operands to try to use an
+** immediate or K operator.
+*/
+static void codecommutative (FuncState *fs, BinOpr op,
+ expdesc *e1, expdesc *e2, int line) {
+ int flip = 0;
+ if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */
+ swapexps(e1, e2); /* change order */
+ flip = 1;
+ }
+ if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */
+ codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD);
+ else
+ codearith(fs, op, e1, e2, flip, line);
+}
+
+
+/*
+** Code bitwise operations; they are all commutative, so the function
+** tries to put an integer constant as the 2nd operand (a K operand).
+*/
+static void codebitwise (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int line) {
+ int flip = 0;
+ if (e1->k == VKINT) {
+ swapexps(e1, e2); /* 'e2' will be the constant operand */
+ flip = 1;
+ }
+ if (e2->k == VKINT && luaK_exp2K(fs, e2)) /* K operand? */
+ codebinK(fs, opr, e1, e2, flip, line);
+ else /* no constants */
+ codebinNoK(fs, opr, e1, e2, flip, line);
+}
+
+
+/*
+** Emit code for order comparisons. When using an immediate operand,
+** 'isfloat' tells whether the original value was a float.
+*/
+static void codeorder (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
+ int r1, r2;
+ int im;
+ int isfloat = 0;
+ OpCode op;
+ if (isSCnumber(e2, &im, &isfloat)) {
+ /* use immediate operand */
+ r1 = luaK_exp2anyreg(fs, e1);
+ r2 = im;
+ op = binopr2op(opr, OPR_LT, OP_LTI);
+ }
+ else if (isSCnumber(e1, &im, &isfloat)) {
+ /* transform (A < B) to (B > A) and (A <= B) to (B >= A) */
+ r1 = luaK_exp2anyreg(fs, e2);
+ r2 = im;
+ op = binopr2op(opr, OPR_LT, OP_GTI);
+ }
+ else { /* regular case, compare two registers */
+ r1 = luaK_exp2anyreg(fs, e1);
+ r2 = luaK_exp2anyreg(fs, e2);
+ op = binopr2op(opr, OPR_LT, OP_LT);
+ }
+ freeexps(fs, e1, e2);
+ e1->u.info = condjump(fs, op, r1, r2, isfloat, 1);
+ e1->k = VJMP;
+}
+
+
+/*
+** Emit code for equality comparisons ('==', '~=').
+** 'e1' was already put as RK by 'luaK_infix'.
+*/
+static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
+ int r1, r2;
+ int im;
+ int isfloat = 0; /* not needed here, but kept for symmetry */
+ OpCode op;
+ if (e1->k != VNONRELOC) {
+ lua_assert(e1->k == VK || e1->k == VKINT || e1->k == VKFLT);
+ swapexps(e1, e2);
+ }
+ r1 = luaK_exp2anyreg(fs, e1); /* 1st expression must be in register */
+ if (isSCnumber(e2, &im, &isfloat)) {
+ op = OP_EQI;
+ r2 = im; /* immediate operand */
+ }
+ else if (luaK_exp2RK(fs, e2)) { /* 2nd expression is constant? */
+ op = OP_EQK;
+ r2 = e2->u.info; /* constant index */
+ }
+ else {
+ op = OP_EQ; /* will compare two registers */
+ r2 = luaK_exp2anyreg(fs, e2);
+ }
+ freeexps(fs, e1, e2);
+ e1->u.info = condjump(fs, op, r1, r2, isfloat, (opr == OPR_EQ));
+ e1->k = VJMP;
+}
+
+
+/*
+** Apply prefix operation 'op' to expression 'e'.
+*/
+void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) {
+ static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
+ luaK_dischargevars(fs, e);
+ switch (opr) {
+ case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
+ if (constfolding(fs, opr + LUA_OPUNM, e, &ef))
+ break;
+ /* else */ /* FALLTHROUGH */
+ case OPR_LEN:
+ codeunexpval(fs, unopr2op(opr), e, line);
+ break;
+ case OPR_NOT: codenot(fs, e); break;
+ default: lua_assert(0);
+ }
+}
+
+
+/*
+** Process 1st operand 'v' of binary operation 'op' before reading
+** 2nd operand.
+*/
+void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
+ luaK_dischargevars(fs, v);
+ switch (op) {
+ case OPR_AND: {
+ luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */
+ break;
+ }
+ case OPR_OR: {
+ luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */
+ break;
+ }
+ case OPR_CONCAT: {
+ luaK_exp2nextreg(fs, v); /* operand must be on the stack */
+ break;
+ }
+ case OPR_ADD: case OPR_SUB:
+ case OPR_MUL: case OPR_DIV: case OPR_IDIV:
+ case OPR_MOD: case OPR_POW:
+ case OPR_BAND: case OPR_BOR: case OPR_BXOR:
+ case OPR_SHL: case OPR_SHR: {
+ if (!tonumeral(v, NULL))
+ luaK_exp2anyreg(fs, v);
+ /* else keep numeral, which may be folded or used as an immediate
+ operand */
+ break;
+ }
+ case OPR_EQ: case OPR_NE: {
+ if (!tonumeral(v, NULL))
+ luaK_exp2RK(fs, v);
+ /* else keep numeral, which may be an immediate operand */
+ break;
+ }
+ case OPR_LT: case OPR_LE:
+ case OPR_GT: case OPR_GE: {
+ int dummy, dummy2;
+ if (!isSCnumber(v, &dummy, &dummy2))
+ luaK_exp2anyreg(fs, v);
+ /* else keep numeral, which may be an immediate operand */
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+/*
+** Create code for '(e1 .. e2)'.
+** For '(e1 .. e2.1 .. e2.2)' (which is '(e1 .. (e2.1 .. e2.2))',
+** because concatenation is right associative), merge both CONCATs.
+*/
+static void codeconcat (FuncState *fs, expdesc *e1, expdesc *e2, int line) {
+ Instruction *ie2 = previousinstruction(fs);
+ if (GET_OPCODE(*ie2) == OP_CONCAT) { /* is 'e2' a concatenation? */
+ int n = GETARG_B(*ie2); /* # of elements concatenated in 'e2' */
+ lua_assert(e1->u.info + 1 == GETARG_A(*ie2));
+ freeexp(fs, e2);
+ SETARG_A(*ie2, e1->u.info); /* correct first element ('e1') */
+ SETARG_B(*ie2, n + 1); /* will concatenate one more element */
+ }
+ else { /* 'e2' is not a concatenation */
+ luaK_codeABC(fs, OP_CONCAT, e1->u.info, 2, 0); /* new concat opcode */
+ freeexp(fs, e2);
+ luaK_fixline(fs, line);
+ }
+}
+
+
+/*
+** Finalize code for binary operation, after reading 2nd operand.
+*/
+void luaK_posfix (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int line) {
+ luaK_dischargevars(fs, e2);
+ if (foldbinop(opr) && constfolding(fs, opr + LUA_OPADD, e1, e2))
+ return; /* done by folding */
+ switch (opr) {
+ case OPR_AND: {
+ lua_assert(e1->t == NO_JUMP); /* list closed by 'luaK_infix' */
+ luaK_concat(fs, &e2->f, e1->f);
+ *e1 = *e2;
+ break;
+ }
+ case OPR_OR: {
+ lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */
+ luaK_concat(fs, &e2->t, e1->t);
+ *e1 = *e2;
+ break;
+ }
+ case OPR_CONCAT: { /* e1 .. e2 */
+ luaK_exp2nextreg(fs, e2);
+ codeconcat(fs, e1, e2, line);
+ break;
+ }
+ case OPR_ADD: case OPR_MUL: {
+ codecommutative(fs, opr, e1, e2, line);
+ break;
+ }
+ case OPR_SUB: {
+ if (finishbinexpneg(fs, e1, e2, OP_ADDI, line, TM_SUB))
+ break; /* coded as (r1 + -I) */
+ /* ELSE */
+ } /* FALLTHROUGH */
+ case OPR_DIV: case OPR_IDIV: case OPR_MOD: case OPR_POW: {
+ codearith(fs, opr, e1, e2, 0, line);
+ break;
+ }
+ case OPR_BAND: case OPR_BOR: case OPR_BXOR: {
+ codebitwise(fs, opr, e1, e2, line);
+ break;
+ }
+ case OPR_SHL: {
+ if (isSCint(e1)) {
+ swapexps(e1, e2);
+ codebini(fs, OP_SHLI, e1, e2, 1, line, TM_SHL); /* I << r2 */
+ }
+ else if (finishbinexpneg(fs, e1, e2, OP_SHRI, line, TM_SHL)) {
+ /* coded as (r1 >> -I) */;
+ }
+ else /* regular case (two registers) */
+ codebinexpval(fs, opr, e1, e2, line);
+ break;
+ }
+ case OPR_SHR: {
+ if (isSCint(e2))
+ codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */
+ else /* regular case (two registers) */
+ codebinexpval(fs, opr, e1, e2, line);
+ break;
+ }
+ case OPR_EQ: case OPR_NE: {
+ codeeq(fs, opr, e1, e2);
+ break;
+ }
+ case OPR_GT: case OPR_GE: {
+ /* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */
+ swapexps(e1, e2);
+ opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT);
+ } /* FALLTHROUGH */
+ case OPR_LT: case OPR_LE: {
+ codeorder(fs, opr, e1, e2);
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+
+/*
+** Change line information associated with current position, by removing
+** previous info and adding it again with new line.
+*/
+void luaK_fixline (FuncState *fs, int line) {
+ removelastlineinfo(fs);
+ savelineinfo(fs, fs->f, line);
+}
+
+
+void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) {
+ Instruction *inst = &fs->f->code[pc];
+ int rb = (hsize != 0) ? luaO_ceillog2(hsize) + 1 : 0; /* hash size */
+ int extra = asize / (MAXARG_C + 1); /* higher bits of array size */
+ int rc = asize % (MAXARG_C + 1); /* lower bits of array size */
+ int k = (extra > 0); /* true iff needs extra argument */
+ *inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k);
+ *(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra);
+}
+
+
+/*
+** Emit a SETLIST instruction.
+** 'base' is register that keeps table;
+** 'nelems' is #table plus those to be stored now;
+** 'tostore' is number of values (in registers 'base + 1',...) to add to
+** table (or LUA_MULTRET to add up to stack top).
+*/
+void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
+ lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH);
+ if (tostore == LUA_MULTRET)
+ tostore = 0;
+ if (nelems <= MAXARG_C)
+ luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems);
+ else {
+ int extra = nelems / (MAXARG_C + 1);
+ nelems %= (MAXARG_C + 1);
+ luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1);
+ codeextraarg(fs, extra);
+ }
+ fs->freereg = base + 1; /* free registers with list values */
+}
+
+
+/*
+** return the final target of a jump (skipping jumps to jumps)
+*/
+static int finaltarget (Instruction *code, int i) {
+ int count;
+ for (count = 0; count < 100; count++) { /* avoid infinite loops */
+ Instruction pc = code[i];
+ if (GET_OPCODE(pc) != OP_JMP)
+ break;
+ else
+ i += GETARG_sJ(pc) + 1;
+ }
+ return i;
+}
+
+
+/*
+** Do a final pass over the code of a function, doing small peephole
+** optimizations and adjustments.
+*/
+void luaK_finish (FuncState *fs) {
+ int i;
+ Proto *p = fs->f;
+ for (i = 0; i < fs->pc; i++) {
+ Instruction *pc = &p->code[i];
+ lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc));
+ switch (GET_OPCODE(*pc)) {
+ case OP_RETURN0: case OP_RETURN1: {
+ if (!(fs->needclose || p->is_vararg))
+ break; /* no extra work */
+ /* else use OP_RETURN to do the extra work */
+ SET_OPCODE(*pc, OP_RETURN);
+ } /* FALLTHROUGH */
+ case OP_RETURN: case OP_TAILCALL: {
+ if (fs->needclose)
+ SETARG_k(*pc, 1); /* signal that it needs to close */
+ if (p->is_vararg)
+ SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */
+ break;
+ }
+ case OP_JMP: {
+ int target = finaltarget(p->code, i);
+ fixjump(fs, i, target);
+ break;
+ }
+ default: break;
+ }
+ }
+}
diff --git a/src/libs/3rdparty/lua/src/lcode.h b/src/libs/3rdparty/lua/src/lcode.h
new file mode 100644
index 0000000000..3265824452
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lcode.h
@@ -0,0 +1,104 @@
+/*
+** $Id: lcode.h $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lcode_h
+#define lcode_h
+
+#include "llex.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+
+
+/*
+** Marks the end of a patch list. It is an invalid value both as an absolute
+** address, and as a list link (would link an element to itself).
+*/
+#define NO_JUMP (-1)
+
+
+/*
+** grep "ORDER OPR" if you change these enums (ORDER OP)
+*/
+typedef enum BinOpr {
+ /* arithmetic operators */
+ OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW,
+ OPR_DIV, OPR_IDIV,
+ /* bitwise operators */
+ OPR_BAND, OPR_BOR, OPR_BXOR,
+ OPR_SHL, OPR_SHR,
+ /* string operator */
+ OPR_CONCAT,
+ /* comparison operators */
+ OPR_EQ, OPR_LT, OPR_LE,
+ OPR_NE, OPR_GT, OPR_GE,
+ /* logical operators */
+ OPR_AND, OPR_OR,
+ OPR_NOBINOPR
+} BinOpr;
+
+
+/* true if operation is foldable (that is, it is arithmetic or bitwise) */
+#define foldbinop(op) ((op) <= OPR_SHR)
+
+
+#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0)
+
+
+typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
+
+
+/* get (pointer to) instruction of given 'expdesc' */
+#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info])
+
+
+#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET)
+
+#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t)
+
+LUAI_FUNC int luaK_code (FuncState *fs, Instruction i);
+LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
+LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx);
+LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
+ int B, int C, int k);
+LUAI_FUNC int luaK_isKint (expdesc *e);
+LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
+LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
+LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
+LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
+LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
+LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n);
+LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
+LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
+LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
+LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
+LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_jump (FuncState *fs);
+LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
+LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
+LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
+LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
+LUAI_FUNC int luaK_getlabel (FuncState *fs);
+LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
+LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
+LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1,
+ expdesc *v2, int line);
+LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc,
+ int ra, int asize, int hsize);
+LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
+LUAI_FUNC void luaK_finish (FuncState *fs);
+LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lcorolib.c b/src/libs/3rdparty/lua/src/lcorolib.c
new file mode 100644
index 0000000000..c64adf08a8
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lcorolib.c
@@ -0,0 +1,210 @@
+/*
+** $Id: lcorolib.c $
+** Coroutine Library
+** See Copyright Notice in lua.h
+*/
+
+#define lcorolib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <stdlib.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+static lua_State *getco (lua_State *L) {
+ lua_State *co = lua_tothread(L, 1);
+ luaL_argexpected(L, co, 1, "thread");
+ return co;
+}
+
+
+/*
+** Resumes a coroutine. Returns the number of results for non-error
+** cases or -1 for errors.
+*/
+static int auxresume (lua_State *L, lua_State *co, int narg) {
+ int status, nres;
+ if (l_unlikely(!lua_checkstack(co, narg))) {
+ lua_pushliteral(L, "too many arguments to resume");
+ return -1; /* error flag */
+ }
+ lua_xmove(L, co, narg);
+ status = lua_resume(co, L, narg, &nres);
+ if (l_likely(status == LUA_OK || status == LUA_YIELD)) {
+ if (l_unlikely(!lua_checkstack(L, nres + 1))) {
+ lua_pop(co, nres); /* remove results anyway */
+ lua_pushliteral(L, "too many results to resume");
+ return -1; /* error flag */
+ }
+ lua_xmove(co, L, nres); /* move yielded values */
+ return nres;
+ }
+ else {
+ lua_xmove(co, L, 1); /* move error message */
+ return -1; /* error flag */
+ }
+}
+
+
+static int luaB_coresume (lua_State *L) {
+ lua_State *co = getco(L);
+ int r;
+ r = auxresume(L, co, lua_gettop(L) - 1);
+ if (l_unlikely(r < 0)) {
+ lua_pushboolean(L, 0);
+ lua_insert(L, -2);
+ return 2; /* return false + error message */
+ }
+ else {
+ lua_pushboolean(L, 1);
+ lua_insert(L, -(r + 1));
+ return r + 1; /* return true + 'resume' returns */
+ }
+}
+
+
+static int luaB_auxwrap (lua_State *L) {
+ lua_State *co = lua_tothread(L, lua_upvalueindex(1));
+ int r = auxresume(L, co, lua_gettop(L));
+ if (l_unlikely(r < 0)) { /* error? */
+ int stat = lua_status(co);
+ if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
+ stat = lua_closethread(co, L); /* close its tbc variables */
+ lua_assert(stat != LUA_OK);
+ lua_xmove(co, L, 1); /* move error message to the caller */
+ }
+ if (stat != LUA_ERRMEM && /* not a memory error and ... */
+ lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
+ luaL_where(L, 1); /* add extra info, if available */
+ lua_insert(L, -2);
+ lua_concat(L, 2);
+ }
+ return lua_error(L); /* propagate error */
+ }
+ return r;
+}
+
+
+static int luaB_cocreate (lua_State *L) {
+ lua_State *NL;
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ NL = lua_newthread(L);
+ lua_pushvalue(L, 1); /* move function to top */
+ lua_xmove(L, NL, 1); /* move function from L to NL */
+ return 1;
+}
+
+
+static int luaB_cowrap (lua_State *L) {
+ luaB_cocreate(L);
+ lua_pushcclosure(L, luaB_auxwrap, 1);
+ return 1;
+}
+
+
+static int luaB_yield (lua_State *L) {
+ return lua_yield(L, lua_gettop(L));
+}
+
+
+#define COS_RUN 0
+#define COS_DEAD 1
+#define COS_YIELD 2
+#define COS_NORM 3
+
+
+static const char *const statname[] =
+ {"running", "dead", "suspended", "normal"};
+
+
+static int auxstatus (lua_State *L, lua_State *co) {
+ if (L == co) return COS_RUN;
+ else {
+ switch (lua_status(co)) {
+ case LUA_YIELD:
+ return COS_YIELD;
+ case LUA_OK: {
+ lua_Debug ar;
+ if (lua_getstack(co, 0, &ar)) /* does it have frames? */
+ return COS_NORM; /* it is running */
+ else if (lua_gettop(co) == 0)
+ return COS_DEAD;
+ else
+ return COS_YIELD; /* initial state */
+ }
+ default: /* some error occurred */
+ return COS_DEAD;
+ }
+ }
+}
+
+
+static int luaB_costatus (lua_State *L) {
+ lua_State *co = getco(L);
+ lua_pushstring(L, statname[auxstatus(L, co)]);
+ return 1;
+}
+
+
+static int luaB_yieldable (lua_State *L) {
+ lua_State *co = lua_isnone(L, 1) ? L : getco(L);
+ lua_pushboolean(L, lua_isyieldable(co));
+ return 1;
+}
+
+
+static int luaB_corunning (lua_State *L) {
+ int ismain = lua_pushthread(L);
+ lua_pushboolean(L, ismain);
+ return 2;
+}
+
+
+static int luaB_close (lua_State *L) {
+ lua_State *co = getco(L);
+ int status = auxstatus(L, co);
+ switch (status) {
+ case COS_DEAD: case COS_YIELD: {
+ status = lua_closethread(co, L);
+ if (status == LUA_OK) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ lua_pushboolean(L, 0);
+ lua_xmove(co, L, 1); /* move error message */
+ return 2;
+ }
+ }
+ default: /* normal or running coroutine */
+ return luaL_error(L, "cannot close a %s coroutine", statname[status]);
+ }
+}
+
+
+static const luaL_Reg co_funcs[] = {
+ {"create", luaB_cocreate},
+ {"resume", luaB_coresume},
+ {"running", luaB_corunning},
+ {"status", luaB_costatus},
+ {"wrap", luaB_cowrap},
+ {"yield", luaB_yield},
+ {"isyieldable", luaB_yieldable},
+ {"close", luaB_close},
+ {NULL, NULL}
+};
+
+
+
+LUAMOD_API int luaopen_coroutine (lua_State *L) {
+ luaL_newlib(L, co_funcs);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lctype.c b/src/libs/3rdparty/lua/src/lctype.c
new file mode 100644
index 0000000000..9542280942
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lctype.c
@@ -0,0 +1,64 @@
+/*
+** $Id: lctype.c $
+** 'ctype' functions for Lua
+** See Copyright Notice in lua.h
+*/
+
+#define lctype_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include "lctype.h"
+
+#if !LUA_USE_CTYPE /* { */
+
+#include <limits.h>
+
+
+#if defined (LUA_UCID) /* accept UniCode IDentifiers? */
+/* consider all non-ascii codepoints to be alphabetic */
+#define NONA 0x01
+#else
+#define NONA 0x00 /* default */
+#endif
+
+
+LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = {
+ 0x00, /* EOZ */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */
+ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */
+ 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */
+ 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05,
+ 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */
+ 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ 0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#endif /* } */
diff --git a/src/libs/3rdparty/lua/src/lctype.h b/src/libs/3rdparty/lua/src/lctype.h
new file mode 100644
index 0000000000..864e190188
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lctype.h
@@ -0,0 +1,101 @@
+/*
+** $Id: lctype.h $
+** 'ctype' functions for Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lctype_h
+#define lctype_h
+
+#include "lua.h"
+
+
+/*
+** WARNING: the functions defined here do not necessarily correspond
+** to the similar functions in the standard C ctype.h. They are
+** optimized for the specific needs of Lua.
+*/
+
+#if !defined(LUA_USE_CTYPE)
+
+#if 'A' == 65 && '0' == 48
+/* ASCII case: can use its own tables; faster and fixed */
+#define LUA_USE_CTYPE 0
+#else
+/* must use standard C ctype */
+#define LUA_USE_CTYPE 1
+#endif
+
+#endif
+
+
+#if !LUA_USE_CTYPE /* { */
+
+#include <limits.h>
+
+#include "llimits.h"
+
+
+#define ALPHABIT 0
+#define DIGITBIT 1
+#define PRINTBIT 2
+#define SPACEBIT 3
+#define XDIGITBIT 4
+
+
+#define MASK(B) (1 << (B))
+
+
+/*
+** add 1 to char to allow index -1 (EOZ)
+*/
+#define testprop(c,p) (luai_ctype_[(c)+1] & (p))
+
+/*
+** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_'
+*/
+#define lislalpha(c) testprop(c, MASK(ALPHABIT))
+#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT)))
+#define lisdigit(c) testprop(c, MASK(DIGITBIT))
+#define lisspace(c) testprop(c, MASK(SPACEBIT))
+#define lisprint(c) testprop(c, MASK(PRINTBIT))
+#define lisxdigit(c) testprop(c, MASK(XDIGITBIT))
+
+
+/*
+** In ASCII, this 'ltolower' is correct for alphabetic characters and
+** for '.'. That is enough for Lua needs. ('check_exp' ensures that
+** the character either is an upper-case letter or is unchanged by
+** the transformation, which holds for lower-case letters and '.'.)
+*/
+#define ltolower(c) \
+ check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \
+ (c) | ('A' ^ 'a'))
+
+
+/* one entry for each character and for -1 (EOZ) */
+LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];)
+
+
+#else /* }{ */
+
+/*
+** use standard C ctypes
+*/
+
+#include <ctype.h>
+
+
+#define lislalpha(c) (isalpha(c) || (c) == '_')
+#define lislalnum(c) (isalnum(c) || (c) == '_')
+#define lisdigit(c) (isdigit(c))
+#define lisspace(c) (isspace(c))
+#define lisprint(c) (isprint(c))
+#define lisxdigit(c) (isxdigit(c))
+
+#define ltolower(c) (tolower(c))
+
+#endif /* } */
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/ldblib.c b/src/libs/3rdparty/lua/src/ldblib.c
new file mode 100644
index 0000000000..6dcbaa9824
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldblib.c
@@ -0,0 +1,483 @@
+/*
+** $Id: ldblib.c $
+** Interface from Lua to its debug API
+** See Copyright Notice in lua.h
+*/
+
+#define ldblib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** The hook table at registry[HOOKKEY] maps threads to their current
+** hook function.
+*/
+static const char *const HOOKKEY = "_HOOKKEY";
+
+
+/*
+** If L1 != L, L1 can be in any state, and therefore there are no
+** guarantees about its stack space; any push in L1 must be
+** checked.
+*/
+static void checkstack (lua_State *L, lua_State *L1, int n) {
+ if (l_unlikely(L != L1 && !lua_checkstack(L1, n)))
+ luaL_error(L, "stack overflow");
+}
+
+
+static int db_getregistry (lua_State *L) {
+ lua_pushvalue(L, LUA_REGISTRYINDEX);
+ return 1;
+}
+
+
+static int db_getmetatable (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (!lua_getmetatable(L, 1)) {
+ lua_pushnil(L); /* no metatable */
+ }
+ return 1;
+}
+
+
+static int db_setmetatable (lua_State *L) {
+ int t = lua_type(L, 2);
+ luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
+ lua_settop(L, 2);
+ lua_setmetatable(L, 1);
+ return 1; /* return 1st argument */
+}
+
+
+static int db_getuservalue (lua_State *L) {
+ int n = (int)luaL_optinteger(L, 2, 1);
+ if (lua_type(L, 1) != LUA_TUSERDATA)
+ luaL_pushfail(L);
+ else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) {
+ lua_pushboolean(L, 1);
+ return 2;
+ }
+ return 1;
+}
+
+
+static int db_setuservalue (lua_State *L) {
+ int n = (int)luaL_optinteger(L, 3, 1);
+ luaL_checktype(L, 1, LUA_TUSERDATA);
+ luaL_checkany(L, 2);
+ lua_settop(L, 2);
+ if (!lua_setiuservalue(L, 1, n))
+ luaL_pushfail(L);
+ return 1;
+}
+
+
+/*
+** Auxiliary function used by several library functions: check for
+** an optional thread as function's first argument and set 'arg' with
+** 1 if this argument is present (so that functions can skip it to
+** access their other arguments)
+*/
+static lua_State *getthread (lua_State *L, int *arg) {
+ if (lua_isthread(L, 1)) {
+ *arg = 1;
+ return lua_tothread(L, 1);
+ }
+ else {
+ *arg = 0;
+ return L; /* function will operate over current thread */
+ }
+}
+
+
+/*
+** Variations of 'lua_settable', used by 'db_getinfo' to put results
+** from 'lua_getinfo' into result table. Key is always a string;
+** value can be a string, an int, or a boolean.
+*/
+static void settabss (lua_State *L, const char *k, const char *v) {
+ lua_pushstring(L, v);
+ lua_setfield(L, -2, k);
+}
+
+static void settabsi (lua_State *L, const char *k, int v) {
+ lua_pushinteger(L, v);
+ lua_setfield(L, -2, k);
+}
+
+static void settabsb (lua_State *L, const char *k, int v) {
+ lua_pushboolean(L, v);
+ lua_setfield(L, -2, k);
+}
+
+
+/*
+** In function 'db_getinfo', the call to 'lua_getinfo' may push
+** results on the stack; later it creates the result table to put
+** these objects. Function 'treatstackoption' puts the result from
+** 'lua_getinfo' on top of the result table so that it can call
+** 'lua_setfield'.
+*/
+static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
+ if (L == L1)
+ lua_rotate(L, -2, 1); /* exchange object and table */
+ else
+ lua_xmove(L1, L, 1); /* move object to the "main" stack */
+ lua_setfield(L, -2, fname); /* put object into table */
+}
+
+
+/*
+** Calls 'lua_getinfo' and collects all results in a new table.
+** L1 needs stack space for an optional input (function) plus
+** two optional outputs (function and line table) from function
+** 'lua_getinfo'.
+*/
+static int db_getinfo (lua_State *L) {
+ lua_Debug ar;
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ const char *options = luaL_optstring(L, arg+2, "flnSrtu");
+ checkstack(L, L1, 3);
+ luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
+ if (lua_isfunction(L, arg + 1)) { /* info about a function? */
+ options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
+ lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
+ lua_xmove(L, L1, 1);
+ }
+ else { /* stack level */
+ if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) {
+ luaL_pushfail(L); /* level out of range */
+ return 1;
+ }
+ }
+ if (!lua_getinfo(L1, options, &ar))
+ return luaL_argerror(L, arg+2, "invalid option");
+ lua_newtable(L); /* table to collect results */
+ if (strchr(options, 'S')) {
+ lua_pushlstring(L, ar.source, ar.srclen);
+ lua_setfield(L, -2, "source");
+ settabss(L, "short_src", ar.short_src);
+ settabsi(L, "linedefined", ar.linedefined);
+ settabsi(L, "lastlinedefined", ar.lastlinedefined);
+ settabss(L, "what", ar.what);
+ }
+ if (strchr(options, 'l'))
+ settabsi(L, "currentline", ar.currentline);
+ if (strchr(options, 'u')) {
+ settabsi(L, "nups", ar.nups);
+ settabsi(L, "nparams", ar.nparams);
+ settabsb(L, "isvararg", ar.isvararg);
+ }
+ if (strchr(options, 'n')) {
+ settabss(L, "name", ar.name);
+ settabss(L, "namewhat", ar.namewhat);
+ }
+ if (strchr(options, 'r')) {
+ settabsi(L, "ftransfer", ar.ftransfer);
+ settabsi(L, "ntransfer", ar.ntransfer);
+ }
+ if (strchr(options, 't'))
+ settabsb(L, "istailcall", ar.istailcall);
+ if (strchr(options, 'L'))
+ treatstackoption(L, L1, "activelines");
+ if (strchr(options, 'f'))
+ treatstackoption(L, L1, "func");
+ return 1; /* return table */
+}
+
+
+static int db_getlocal (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */
+ if (lua_isfunction(L, arg + 1)) { /* function argument? */
+ lua_pushvalue(L, arg + 1); /* push function */
+ lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */
+ return 1; /* return only name (there is no value) */
+ }
+ else { /* stack-level argument */
+ lua_Debug ar;
+ const char *name;
+ int level = (int)luaL_checkinteger(L, arg + 1);
+ if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
+ return luaL_argerror(L, arg+1, "level out of range");
+ checkstack(L, L1, 1);
+ name = lua_getlocal(L1, &ar, nvar);
+ if (name) {
+ lua_xmove(L1, L, 1); /* move local value */
+ lua_pushstring(L, name); /* push name */
+ lua_rotate(L, -2, 1); /* re-order */
+ return 2;
+ }
+ else {
+ luaL_pushfail(L); /* no name (nor value) */
+ return 1;
+ }
+ }
+}
+
+
+static int db_setlocal (lua_State *L) {
+ int arg;
+ const char *name;
+ lua_State *L1 = getthread(L, &arg);
+ lua_Debug ar;
+ int level = (int)luaL_checkinteger(L, arg + 1);
+ int nvar = (int)luaL_checkinteger(L, arg + 2);
+ if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
+ return luaL_argerror(L, arg+1, "level out of range");
+ luaL_checkany(L, arg+3);
+ lua_settop(L, arg+3);
+ checkstack(L, L1, 1);
+ lua_xmove(L, L1, 1);
+ name = lua_setlocal(L1, &ar, nvar);
+ if (name == NULL)
+ lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */
+ lua_pushstring(L, name);
+ return 1;
+}
+
+
+/*
+** get (if 'get' is true) or set an upvalue from a closure
+*/
+static int auxupvalue (lua_State *L, int get) {
+ const char *name;
+ int n = (int)luaL_checkinteger(L, 2); /* upvalue index */
+ luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */
+ name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
+ if (name == NULL) return 0;
+ lua_pushstring(L, name);
+ lua_insert(L, -(get+1)); /* no-op if get is false */
+ return get + 1;
+}
+
+
+static int db_getupvalue (lua_State *L) {
+ return auxupvalue(L, 1);
+}
+
+
+static int db_setupvalue (lua_State *L) {
+ luaL_checkany(L, 3);
+ return auxupvalue(L, 0);
+}
+
+
+/*
+** Check whether a given upvalue from a given closure exists and
+** returns its index
+*/
+static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) {
+ void *id;
+ int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
+ luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
+ id = lua_upvalueid(L, argf, nup);
+ if (pnup) {
+ luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index");
+ *pnup = nup;
+ }
+ return id;
+}
+
+
+static int db_upvalueid (lua_State *L) {
+ void *id = checkupval(L, 1, 2, NULL);
+ if (id != NULL)
+ lua_pushlightuserdata(L, id);
+ else
+ luaL_pushfail(L);
+ return 1;
+}
+
+
+static int db_upvaluejoin (lua_State *L) {
+ int n1, n2;
+ checkupval(L, 1, 2, &n1);
+ checkupval(L, 3, 4, &n2);
+ luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
+ luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
+ lua_upvaluejoin(L, 1, n1, 3, n2);
+ return 0;
+}
+
+
+/*
+** Call hook function registered at hook table for the current
+** thread (if there is one)
+*/
+static void hookf (lua_State *L, lua_Debug *ar) {
+ static const char *const hooknames[] =
+ {"call", "return", "line", "count", "tail call"};
+ lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
+ lua_pushthread(L);
+ if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */
+ lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */
+ if (ar->currentline >= 0)
+ lua_pushinteger(L, ar->currentline); /* push current line */
+ else lua_pushnil(L);
+ lua_assert(lua_getinfo(L, "lS", ar));
+ lua_call(L, 2, 0); /* call hook function */
+ }
+}
+
+
+/*
+** Convert a string mask (for 'sethook') into a bit mask
+*/
+static int makemask (const char *smask, int count) {
+ int mask = 0;
+ if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
+ if (strchr(smask, 'r')) mask |= LUA_MASKRET;
+ if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
+ if (count > 0) mask |= LUA_MASKCOUNT;
+ return mask;
+}
+
+
+/*
+** Convert a bit mask (for 'gethook') into a string mask
+*/
+static char *unmakemask (int mask, char *smask) {
+ int i = 0;
+ if (mask & LUA_MASKCALL) smask[i++] = 'c';
+ if (mask & LUA_MASKRET) smask[i++] = 'r';
+ if (mask & LUA_MASKLINE) smask[i++] = 'l';
+ smask[i] = '\0';
+ return smask;
+}
+
+
+static int db_sethook (lua_State *L) {
+ int arg, mask, count;
+ lua_Hook func;
+ lua_State *L1 = getthread(L, &arg);
+ if (lua_isnoneornil(L, arg+1)) { /* no hook? */
+ lua_settop(L, arg+1);
+ func = NULL; mask = 0; count = 0; /* turn off hooks */
+ }
+ else {
+ const char *smask = luaL_checkstring(L, arg+2);
+ luaL_checktype(L, arg+1, LUA_TFUNCTION);
+ count = (int)luaL_optinteger(L, arg + 3, 0);
+ func = hookf; mask = makemask(smask, count);
+ }
+ if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
+ /* table just created; initialize it */
+ lua_pushliteral(L, "k");
+ lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
+ lua_pushvalue(L, -1);
+ lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
+ }
+ checkstack(L, L1, 1);
+ lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */
+ lua_pushvalue(L, arg + 1); /* value (hook function) */
+ lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */
+ lua_sethook(L1, func, mask, count);
+ return 0;
+}
+
+
+static int db_gethook (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ char buff[5];
+ int mask = lua_gethookmask(L1);
+ lua_Hook hook = lua_gethook(L1);
+ if (hook == NULL) { /* no hook? */
+ luaL_pushfail(L);
+ return 1;
+ }
+ else if (hook != hookf) /* external hook? */
+ lua_pushliteral(L, "external hook");
+ else { /* hook table must exist */
+ lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
+ checkstack(L, L1, 1);
+ lua_pushthread(L1); lua_xmove(L1, L, 1);
+ lua_rawget(L, -2); /* 1st result = hooktable[L1] */
+ lua_remove(L, -2); /* remove hook table */
+ }
+ lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */
+ lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */
+ return 3;
+}
+
+
+static int db_debug (lua_State *L) {
+ for (;;) {
+ char buffer[250];
+ lua_writestringerror("%s", "lua_debug> ");
+ if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
+ strcmp(buffer, "cont\n") == 0)
+ return 0;
+ if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
+ lua_pcall(L, 0, 0, 0))
+ lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL));
+ lua_settop(L, 0); /* remove eventual returns */
+ }
+}
+
+
+static int db_traceback (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ const char *msg = lua_tostring(L, arg + 1);
+ if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */
+ lua_pushvalue(L, arg + 1); /* return it untouched */
+ else {
+ int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0);
+ luaL_traceback(L, L1, msg, level);
+ }
+ return 1;
+}
+
+
+static int db_setcstacklimit (lua_State *L) {
+ int limit = (int)luaL_checkinteger(L, 1);
+ int res = lua_setcstacklimit(L, limit);
+ lua_pushinteger(L, res);
+ return 1;
+}
+
+
+static const luaL_Reg dblib[] = {
+ {"debug", db_debug},
+ {"getuservalue", db_getuservalue},
+ {"gethook", db_gethook},
+ {"getinfo", db_getinfo},
+ {"getlocal", db_getlocal},
+ {"getregistry", db_getregistry},
+ {"getmetatable", db_getmetatable},
+ {"getupvalue", db_getupvalue},
+ {"upvaluejoin", db_upvaluejoin},
+ {"upvalueid", db_upvalueid},
+ {"setuservalue", db_setuservalue},
+ {"sethook", db_sethook},
+ {"setlocal", db_setlocal},
+ {"setmetatable", db_setmetatable},
+ {"setupvalue", db_setupvalue},
+ {"traceback", db_traceback},
+ {"setcstacklimit", db_setcstacklimit},
+ {NULL, NULL}
+};
+
+
+LUAMOD_API int luaopen_debug (lua_State *L) {
+ luaL_newlib(L, dblib);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/ldebug.c b/src/libs/3rdparty/lua/src/ldebug.c
new file mode 100644
index 0000000000..28b1caabf7
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldebug.c
@@ -0,0 +1,924 @@
+/*
+** $Id: ldebug.c $
+** Debug Interface
+** See Copyright Notice in lua.h
+*/
+
+#define ldebug_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+
+#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
+
+
+static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
+ const char **name);
+
+
+static int currentpc (CallInfo *ci) {
+ lua_assert(isLua(ci));
+ return pcRel(ci->u.l.savedpc, ci_func(ci)->p);
+}
+
+
+/*
+** Get a "base line" to find the line corresponding to an instruction.
+** Base lines are regularly placed at MAXIWTHABS intervals, so usually
+** an integer division gets the right place. When the source file has
+** large sequences of empty/comment lines, it may need extra entries,
+** so the original estimate needs a correction.
+** If the original estimate is -1, the initial 'if' ensures that the
+** 'while' will run at least once.
+** The assertion that the estimate is a lower bound for the correct base
+** is valid as long as the debug info has been generated with the same
+** value for MAXIWTHABS or smaller. (Previous releases use a little
+** smaller value.)
+*/
+static int getbaseline (const Proto *f, int pc, int *basepc) {
+ if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) {
+ *basepc = -1; /* start from the beginning */
+ return f->linedefined;
+ }
+ else {
+ int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */
+ /* estimate must be a lower bound of the correct base */
+ lua_assert(i < 0 ||
+ (i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc));
+ while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc)
+ i++; /* low estimate; adjust it */
+ *basepc = f->abslineinfo[i].pc;
+ return f->abslineinfo[i].line;
+ }
+}
+
+
+/*
+** Get the line corresponding to instruction 'pc' in function 'f';
+** first gets a base line and from there does the increments until
+** the desired instruction.
+*/
+int luaG_getfuncline (const Proto *f, int pc) {
+ if (f->lineinfo == NULL) /* no debug information? */
+ return -1;
+ else {
+ int basepc;
+ int baseline = getbaseline(f, pc, &basepc);
+ while (basepc++ < pc) { /* walk until given instruction */
+ lua_assert(f->lineinfo[basepc] != ABSLINEINFO);
+ baseline += f->lineinfo[basepc]; /* correct line */
+ }
+ return baseline;
+ }
+}
+
+
+static int getcurrentline (CallInfo *ci) {
+ return luaG_getfuncline(ci_func(ci)->p, currentpc(ci));
+}
+
+
+/*
+** Set 'trap' for all active Lua frames.
+** This function can be called during a signal, under "reasonable"
+** assumptions. A new 'ci' is completely linked in the list before it
+** becomes part of the "active" list, and we assume that pointers are
+** atomic; see comment in next function.
+** (A compiler doing interprocedural optimizations could, theoretically,
+** reorder memory writes in such a way that the list could be
+** temporarily broken while inserting a new element. We simply assume it
+** has no good reasons to do that.)
+*/
+static void settraps (CallInfo *ci) {
+ for (; ci != NULL; ci = ci->previous)
+ if (isLua(ci))
+ ci->u.l.trap = 1;
+}
+
+
+/*
+** This function can be called during a signal, under "reasonable"
+** assumptions.
+** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
+** are for debug only, and it is no problem if they get arbitrary
+** values (causes at most one wrong hook call). 'hookmask' is an atomic
+** value. We assume that pointers are atomic too (e.g., gcc ensures that
+** for all platforms where it runs). Moreover, 'hook' is always checked
+** before being called (see 'luaD_hook').
+*/
+LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
+ if (func == NULL || mask == 0) { /* turn off hooks? */
+ mask = 0;
+ func = NULL;
+ }
+ L->hook = func;
+ L->basehookcount = count;
+ resethookcount(L);
+ L->hookmask = cast_byte(mask);
+ if (mask)
+ settraps(L->ci); /* to trace inside 'luaV_execute' */
+}
+
+
+LUA_API lua_Hook lua_gethook (lua_State *L) {
+ return L->hook;
+}
+
+
+LUA_API int lua_gethookmask (lua_State *L) {
+ return L->hookmask;
+}
+
+
+LUA_API int lua_gethookcount (lua_State *L) {
+ return L->basehookcount;
+}
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
+ int status;
+ CallInfo *ci;
+ if (level < 0) return 0; /* invalid (negative) level */
+ lua_lock(L);
+ for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous)
+ level--;
+ if (level == 0 && ci != &L->base_ci) { /* level found? */
+ status = 1;
+ ar->i_ci = ci;
+ }
+ else status = 0; /* no such level */
+ lua_unlock(L);
+ return status;
+}
+
+
+static const char *upvalname (const Proto *p, int uv) {
+ TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name);
+ if (s == NULL) return "?";
+ else return getstr(s);
+}
+
+
+static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
+ if (clLvalue(s2v(ci->func.p))->p->is_vararg) {
+ int nextra = ci->u.l.nextraargs;
+ if (n >= -nextra) { /* 'n' is negative */
+ *pos = ci->func.p - nextra - (n + 1);
+ return "(vararg)"; /* generic name for any vararg */
+ }
+ }
+ return NULL; /* no such vararg */
+}
+
+
+const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
+ StkId base = ci->func.p + 1;
+ const char *name = NULL;
+ if (isLua(ci)) {
+ if (n < 0) /* access to vararg values? */
+ return findvararg(ci, n, pos);
+ else
+ name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
+ }
+ if (name == NULL) { /* no 'standard' name? */
+ StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p;
+ if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */
+ /* generic name for any valid slot */
+ name = isLua(ci) ? "(temporary)" : "(C temporary)";
+ }
+ else
+ return NULL; /* no name */
+ }
+ if (pos)
+ *pos = base + (n - 1);
+ return name;
+}
+
+
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
+ const char *name;
+ lua_lock(L);
+ if (ar == NULL) { /* information about non-active function? */
+ if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */
+ name = NULL;
+ else /* consider live variables at function start (parameters) */
+ name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0);
+ }
+ else { /* active function; get information through 'ar' */
+ StkId pos = NULL; /* to avoid warnings */
+ name = luaG_findlocal(L, ar->i_ci, n, &pos);
+ if (name) {
+ setobjs2s(L, L->top.p, pos);
+ api_incr_top(L);
+ }
+ }
+ lua_unlock(L);
+ return name;
+}
+
+
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
+ StkId pos = NULL; /* to avoid warnings */
+ const char *name;
+ lua_lock(L);
+ name = luaG_findlocal(L, ar->i_ci, n, &pos);
+ if (name) {
+ setobjs2s(L, pos, L->top.p - 1);
+ L->top.p--; /* pop value */
+ }
+ lua_unlock(L);
+ return name;
+}
+
+
+static void funcinfo (lua_Debug *ar, Closure *cl) {
+ if (noLuaClosure(cl)) {
+ ar->source = "=[C]";
+ ar->srclen = LL("=[C]");
+ ar->linedefined = -1;
+ ar->lastlinedefined = -1;
+ ar->what = "C";
+ }
+ else {
+ const Proto *p = cl->l.p;
+ if (p->source) {
+ ar->source = getstr(p->source);
+ ar->srclen = tsslen(p->source);
+ }
+ else {
+ ar->source = "=?";
+ ar->srclen = LL("=?");
+ }
+ ar->linedefined = p->linedefined;
+ ar->lastlinedefined = p->lastlinedefined;
+ ar->what = (ar->linedefined == 0) ? "main" : "Lua";
+ }
+ luaO_chunkid(ar->short_src, ar->source, ar->srclen);
+}
+
+
+static int nextline (const Proto *p, int currentline, int pc) {
+ if (p->lineinfo[pc] != ABSLINEINFO)
+ return currentline + p->lineinfo[pc];
+ else
+ return luaG_getfuncline(p, pc);
+}
+
+
+static void collectvalidlines (lua_State *L, Closure *f) {
+ if (noLuaClosure(f)) {
+ setnilvalue(s2v(L->top.p));
+ api_incr_top(L);
+ }
+ else {
+ int i;
+ TValue v;
+ const Proto *p = f->l.p;
+ int currentline = p->linedefined;
+ Table *t = luaH_new(L); /* new table to store active lines */
+ sethvalue2s(L, L->top.p, t); /* push it on stack */
+ api_incr_top(L);
+ setbtvalue(&v); /* boolean 'true' to be the value of all indices */
+ if (!p->is_vararg) /* regular function? */
+ i = 0; /* consider all instructions */
+ else { /* vararg function */
+ lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
+ currentline = nextline(p, currentline, 0);
+ i = 1; /* skip first instruction (OP_VARARGPREP) */
+ }
+ for (; i < p->sizelineinfo; i++) { /* for each instruction */
+ currentline = nextline(p, currentline, i); /* get its line */
+ luaH_setint(L, t, currentline, &v); /* table[line] = true */
+ }
+ }
+}
+
+
+static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
+ /* calling function is a known function? */
+ if (ci != NULL && !(ci->callstatus & CIST_TAIL))
+ return funcnamefromcall(L, ci->previous, name);
+ else return NULL; /* no way to find a name */
+}
+
+
+static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
+ Closure *f, CallInfo *ci) {
+ int status = 1;
+ for (; *what; what++) {
+ switch (*what) {
+ case 'S': {
+ funcinfo(ar, f);
+ break;
+ }
+ case 'l': {
+ ar->currentline = (ci && isLua(ci)) ? getcurrentline(ci) : -1;
+ break;
+ }
+ case 'u': {
+ ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
+ if (noLuaClosure(f)) {
+ ar->isvararg = 1;
+ ar->nparams = 0;
+ }
+ else {
+ ar->isvararg = f->l.p->is_vararg;
+ ar->nparams = f->l.p->numparams;
+ }
+ break;
+ }
+ case 't': {
+ ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0;
+ break;
+ }
+ case 'n': {
+ ar->namewhat = getfuncname(L, ci, &ar->name);
+ if (ar->namewhat == NULL) {
+ ar->namewhat = ""; /* not found */
+ ar->name = NULL;
+ }
+ break;
+ }
+ case 'r': {
+ if (ci == NULL || !(ci->callstatus & CIST_TRAN))
+ ar->ftransfer = ar->ntransfer = 0;
+ else {
+ ar->ftransfer = ci->u2.transferinfo.ftransfer;
+ ar->ntransfer = ci->u2.transferinfo.ntransfer;
+ }
+ break;
+ }
+ case 'L':
+ case 'f': /* handled by lua_getinfo */
+ break;
+ default: status = 0; /* invalid option */
+ }
+ }
+ return status;
+}
+
+
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
+ int status;
+ Closure *cl;
+ CallInfo *ci;
+ TValue *func;
+ lua_lock(L);
+ if (*what == '>') {
+ ci = NULL;
+ func = s2v(L->top.p - 1);
+ api_check(L, ttisfunction(func), "function expected");
+ what++; /* skip the '>' */
+ L->top.p--; /* pop function */
+ }
+ else {
+ ci = ar->i_ci;
+ func = s2v(ci->func.p);
+ lua_assert(ttisfunction(func));
+ }
+ cl = ttisclosure(func) ? clvalue(func) : NULL;
+ status = auxgetinfo(L, what, ar, cl, ci);
+ if (strchr(what, 'f')) {
+ setobj2s(L, L->top.p, func);
+ api_incr_top(L);
+ }
+ if (strchr(what, 'L'))
+ collectvalidlines(L, cl);
+ lua_unlock(L);
+ return status;
+}
+
+
+/*
+** {======================================================
+** Symbolic Execution
+** =======================================================
+*/
+
+static const char *getobjname (const Proto *p, int lastpc, int reg,
+ const char **name);
+
+
+/*
+** Find a "name" for the constant 'c'.
+*/
+static void kname (const Proto *p, int c, const char **name) {
+ TValue *kvalue = &p->k[c];
+ *name = (ttisstring(kvalue)) ? svalue(kvalue) : "?";
+}
+
+
+/*
+** Find a "name" for the register 'c'.
+*/
+static void rname (const Proto *p, int pc, int c, const char **name) {
+ const char *what = getobjname(p, pc, c, name); /* search for 'c' */
+ if (!(what && *what == 'c')) /* did not find a constant name? */
+ *name = "?";
+}
+
+
+/*
+** Find a "name" for a 'C' value in an RK instruction.
+*/
+static void rkname (const Proto *p, int pc, Instruction i, const char **name) {
+ int c = GETARG_C(i); /* key index */
+ if (GETARG_k(i)) /* is 'c' a constant? */
+ kname(p, c, name);
+ else /* 'c' is a register */
+ rname(p, pc, c, name);
+}
+
+
+static int filterpc (int pc, int jmptarget) {
+ if (pc < jmptarget) /* is code conditional (inside a jump)? */
+ return -1; /* cannot know who sets that register */
+ else return pc; /* current position sets that register */
+}
+
+
+/*
+** Try to find last instruction before 'lastpc' that modified register 'reg'.
+*/
+static int findsetreg (const Proto *p, int lastpc, int reg) {
+ int pc;
+ int setreg = -1; /* keep last instruction that changed 'reg' */
+ int jmptarget = 0; /* any code before this address is conditional */
+ if (testMMMode(GET_OPCODE(p->code[lastpc])))
+ lastpc--; /* previous instruction was not actually executed */
+ for (pc = 0; pc < lastpc; pc++) {
+ Instruction i = p->code[pc];
+ OpCode op = GET_OPCODE(i);
+ int a = GETARG_A(i);
+ int change; /* true if current instruction changed 'reg' */
+ switch (op) {
+ case OP_LOADNIL: { /* set registers from 'a' to 'a+b' */
+ int b = GETARG_B(i);
+ change = (a <= reg && reg <= a + b);
+ break;
+ }
+ case OP_TFORCALL: { /* affect all regs above its base */
+ change = (reg >= a + 2);
+ break;
+ }
+ case OP_CALL:
+ case OP_TAILCALL: { /* affect all registers above base */
+ change = (reg >= a);
+ break;
+ }
+ case OP_JMP: { /* doesn't change registers, but changes 'jmptarget' */
+ int b = GETARG_sJ(i);
+ int dest = pc + 1 + b;
+ /* jump does not skip 'lastpc' and is larger than current one? */
+ if (dest <= lastpc && dest > jmptarget)
+ jmptarget = dest; /* update 'jmptarget' */
+ change = 0;
+ break;
+ }
+ default: /* any instruction that sets A */
+ change = (testAMode(op) && reg == a);
+ break;
+ }
+ if (change)
+ setreg = filterpc(pc, jmptarget);
+ }
+ return setreg;
+}
+
+
+/*
+** Check whether table being indexed by instruction 'i' is the
+** environment '_ENV'
+*/
+static const char *gxf (const Proto *p, int pc, Instruction i, int isup) {
+ int t = GETARG_B(i); /* table index */
+ const char *name; /* name of indexed variable */
+ if (isup) /* is an upvalue? */
+ name = upvalname(p, t);
+ else
+ getobjname(p, pc, t, &name);
+ return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
+}
+
+
+static const char *getobjname (const Proto *p, int lastpc, int reg,
+ const char **name) {
+ int pc;
+ *name = luaF_getlocalname(p, reg + 1, lastpc);
+ if (*name) /* is a local? */
+ return "local";
+ /* else try symbolic execution */
+ pc = findsetreg(p, lastpc, reg);
+ if (pc != -1) { /* could find instruction? */
+ Instruction i = p->code[pc];
+ OpCode op = GET_OPCODE(i);
+ switch (op) {
+ case OP_MOVE: {
+ int b = GETARG_B(i); /* move from 'b' to 'a' */
+ if (b < GETARG_A(i))
+ return getobjname(p, pc, b, name); /* get name for 'b' */
+ break;
+ }
+ case OP_GETTABUP: {
+ int k = GETARG_C(i); /* key index */
+ kname(p, k, name);
+ return gxf(p, pc, i, 1);
+ }
+ case OP_GETTABLE: {
+ int k = GETARG_C(i); /* key index */
+ rname(p, pc, k, name);
+ return gxf(p, pc, i, 0);
+ }
+ case OP_GETI: {
+ *name = "integer index";
+ return "field";
+ }
+ case OP_GETFIELD: {
+ int k = GETARG_C(i); /* key index */
+ kname(p, k, name);
+ return gxf(p, pc, i, 0);
+ }
+ case OP_GETUPVAL: {
+ *name = upvalname(p, GETARG_B(i));
+ return "upvalue";
+ }
+ case OP_LOADK:
+ case OP_LOADKX: {
+ int b = (op == OP_LOADK) ? GETARG_Bx(i)
+ : GETARG_Ax(p->code[pc + 1]);
+ if (ttisstring(&p->k[b])) {
+ *name = svalue(&p->k[b]);
+ return "constant";
+ }
+ break;
+ }
+ case OP_SELF: {
+ rkname(p, pc, i, name);
+ return "method";
+ }
+ default: break; /* go through to return NULL */
+ }
+ }
+ return NULL; /* could not find reasonable name */
+}
+
+
+/*
+** Try to find a name for a function based on the code that called it.
+** (Only works when function was called by a Lua function.)
+** Returns what the name is (e.g., "for iterator", "method",
+** "metamethod") and sets '*name' to point to the name.
+*/
+static const char *funcnamefromcode (lua_State *L, const Proto *p,
+ int pc, const char **name) {
+ TMS tm = (TMS)0; /* (initial value avoids warnings) */
+ Instruction i = p->code[pc]; /* calling instruction */
+ switch (GET_OPCODE(i)) {
+ case OP_CALL:
+ case OP_TAILCALL:
+ return getobjname(p, pc, GETARG_A(i), name); /* get function name */
+ case OP_TFORCALL: { /* for iterator */
+ *name = "for iterator";
+ return "for iterator";
+ }
+ /* other instructions can do calls through metamethods */
+ case OP_SELF: case OP_GETTABUP: case OP_GETTABLE:
+ case OP_GETI: case OP_GETFIELD:
+ tm = TM_INDEX;
+ break;
+ case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD:
+ tm = TM_NEWINDEX;
+ break;
+ case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
+ tm = cast(TMS, GETARG_C(i));
+ break;
+ }
+ case OP_UNM: tm = TM_UNM; break;
+ case OP_BNOT: tm = TM_BNOT; break;
+ case OP_LEN: tm = TM_LEN; break;
+ case OP_CONCAT: tm = TM_CONCAT; break;
+ case OP_EQ: tm = TM_EQ; break;
+ /* no cases for OP_EQI and OP_EQK, as they don't call metamethods */
+ case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break;
+ case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break;
+ case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break;
+ default:
+ return NULL; /* cannot find a reasonable name */
+ }
+ *name = getstr(G(L)->tmname[tm]) + 2;
+ return "metamethod";
+}
+
+
+/*
+** Try to find a name for a function based on how it was called.
+*/
+static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
+ const char **name) {
+ if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
+ *name = "?";
+ return "hook";
+ }
+ else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */
+ *name = "__gc";
+ return "metamethod"; /* report it as such */
+ }
+ else if (isLua(ci))
+ return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name);
+ else
+ return NULL;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** Check whether pointer 'o' points to some value in the stack frame of
+** the current function and, if so, returns its index. Because 'o' may
+** not point to a value in this stack, we cannot compare it with the
+** region boundaries (undefined behavior in ISO C).
+*/
+static int instack (CallInfo *ci, const TValue *o) {
+ int pos;
+ StkId base = ci->func.p + 1;
+ for (pos = 0; base + pos < ci->top.p; pos++) {
+ if (o == s2v(base + pos))
+ return pos;
+ }
+ return -1; /* not found */
+}
+
+
+/*
+** Checks whether value 'o' came from an upvalue. (That can only happen
+** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on
+** upvalues.)
+*/
+static const char *getupvalname (CallInfo *ci, const TValue *o,
+ const char **name) {
+ LClosure *c = ci_func(ci);
+ int i;
+ for (i = 0; i < c->nupvalues; i++) {
+ if (c->upvals[i]->v.p == o) {
+ *name = upvalname(c->p, i);
+ return "upvalue";
+ }
+ }
+ return NULL;
+}
+
+
+static const char *formatvarinfo (lua_State *L, const char *kind,
+ const char *name) {
+ if (kind == NULL)
+ return ""; /* no information */
+ else
+ return luaO_pushfstring(L, " (%s '%s')", kind, name);
+}
+
+/*
+** Build a string with a "description" for the value 'o', such as
+** "variable 'x'" or "upvalue 'y'".
+*/
+static const char *varinfo (lua_State *L, const TValue *o) {
+ CallInfo *ci = L->ci;
+ const char *name = NULL; /* to avoid warnings */
+ const char *kind = NULL;
+ if (isLua(ci)) {
+ kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
+ if (!kind) { /* not an upvalue? */
+ int reg = instack(ci, o); /* try a register */
+ if (reg >= 0) /* is 'o' a register? */
+ kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name);
+ }
+ }
+ return formatvarinfo(L, kind, name);
+}
+
+
+/*
+** Raise a type error
+*/
+static l_noret typeerror (lua_State *L, const TValue *o, const char *op,
+ const char *extra) {
+ const char *t = luaT_objtypename(L, o);
+ luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra);
+}
+
+
+/*
+** Raise a type error with "standard" information about the faulty
+** object 'o' (using 'varinfo').
+*/
+l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
+ typeerror(L, o, op, varinfo(L, o));
+}
+
+
+/*
+** Raise an error for calling a non-callable object. Try to find a name
+** for the object based on how it was called ('funcnamefromcall'); if it
+** cannot get a name there, try 'varinfo'.
+*/
+l_noret luaG_callerror (lua_State *L, const TValue *o) {
+ CallInfo *ci = L->ci;
+ const char *name = NULL; /* to avoid warnings */
+ const char *kind = funcnamefromcall(L, ci, &name);
+ const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o);
+ typeerror(L, o, "call", extra);
+}
+
+
+l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
+ luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
+ what, luaT_objtypename(L, o));
+}
+
+
+l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) {
+ if (ttisstring(p1) || cvt2str(p1)) p1 = p2;
+ luaG_typeerror(L, p1, "concatenate");
+}
+
+
+l_noret luaG_opinterror (lua_State *L, const TValue *p1,
+ const TValue *p2, const char *msg) {
+ if (!ttisnumber(p1)) /* first operand is wrong? */
+ p2 = p1; /* now second is wrong */
+ luaG_typeerror(L, p2, msg);
+}
+
+
+/*
+** Error when both values are convertible to numbers, but not to integers
+*/
+l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
+ lua_Integer temp;
+ if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I))
+ p2 = p1;
+ luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
+}
+
+
+l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
+ const char *t1 = luaT_objtypename(L, p1);
+ const char *t2 = luaT_objtypename(L, p2);
+ if (strcmp(t1, t2) == 0)
+ luaG_runerror(L, "attempt to compare two %s values", t1);
+ else
+ luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
+}
+
+
+/* add src:line information to 'msg' */
+const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
+ int line) {
+ char buff[LUA_IDSIZE];
+ if (src)
+ luaO_chunkid(buff, getstr(src), tsslen(src));
+ else { /* no source available; use "?" instead */
+ buff[0] = '?'; buff[1] = '\0';
+ }
+ return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
+}
+
+
+l_noret luaG_errormsg (lua_State *L) {
+ if (L->errfunc != 0) { /* is there an error handling function? */
+ StkId errfunc = restorestack(L, L->errfunc);
+ lua_assert(ttisfunction(s2v(errfunc)));
+ setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */
+ setobjs2s(L, L->top.p - 1, errfunc); /* push function */
+ L->top.p++; /* assume EXTRA_STACK */
+ luaD_callnoyield(L, L->top.p - 2, 1); /* call it */
+ }
+ luaD_throw(L, LUA_ERRRUN);
+}
+
+
+l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
+ CallInfo *ci = L->ci;
+ const char *msg;
+ va_list argp;
+ luaC_checkGC(L); /* error message uses memory */
+ va_start(argp, fmt);
+ msg = luaO_pushvfstring(L, fmt, argp); /* format message */
+ va_end(argp);
+ if (isLua(ci)) { /* if Lua function, add source:line information */
+ luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
+ setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */
+ L->top.p--;
+ }
+ luaG_errormsg(L);
+}
+
+
+/*
+** Check whether new instruction 'newpc' is in a different line from
+** previous instruction 'oldpc'. More often than not, 'newpc' is only
+** one or a few instructions after 'oldpc' (it must be after, see
+** caller), so try to avoid calling 'luaG_getfuncline'. If they are
+** too far apart, there is a good chance of a ABSLINEINFO in the way,
+** so it goes directly to 'luaG_getfuncline'.
+*/
+static int changedline (const Proto *p, int oldpc, int newpc) {
+ if (p->lineinfo == NULL) /* no debug information? */
+ return 0;
+ if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
+ int delta = 0; /* line difference */
+ int pc = oldpc;
+ for (;;) {
+ int lineinfo = p->lineinfo[++pc];
+ if (lineinfo == ABSLINEINFO)
+ break; /* cannot compute delta; fall through */
+ delta += lineinfo;
+ if (pc == newpc)
+ return (delta != 0); /* delta computed successfully */
+ }
+ }
+ /* either instructions are too far apart or there is an absolute line
+ info in the way; compute line difference explicitly */
+ return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc));
+}
+
+
+/*
+** Traces the execution of a Lua function. Called before the execution
+** of each opcode, when debug is on. 'L->oldpc' stores the last
+** instruction traced, to detect line changes. When entering a new
+** function, 'npci' will be zero and will test as a new line whatever
+** the value of 'oldpc'. Some exceptional conditions may return to
+** a function without setting 'oldpc'. In that case, 'oldpc' may be
+** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
+** at most causes an extra call to a line hook.)
+** This function is not "Protected" when called, so it should correct
+** 'L->top.p' before calling anything that can run the GC.
+*/
+int luaG_traceexec (lua_State *L, const Instruction *pc) {
+ CallInfo *ci = L->ci;
+ lu_byte mask = L->hookmask;
+ const Proto *p = ci_func(ci)->p;
+ int counthook;
+ if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
+ ci->u.l.trap = 0; /* don't need to stop again */
+ return 0; /* turn off 'trap' */
+ }
+ pc++; /* reference is always next instruction */
+ ci->u.l.savedpc = pc; /* save 'pc' */
+ counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT));
+ if (counthook)
+ resethookcount(L); /* reset count */
+ else if (!(mask & LUA_MASKLINE))
+ return 1; /* no line hook and count != 0; nothing to be done now */
+ if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */
+ ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
+ return 1; /* do not call hook again (VM yielded, so it did not move) */
+ }
+ if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
+ L->top.p = ci->top.p; /* correct top */
+ if (counthook)
+ luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
+ if (mask & LUA_MASKLINE) {
+ /* 'L->oldpc' may be invalid; use zero in this case */
+ int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
+ int npci = pcRel(pc, p);
+ if (npci <= oldpc || /* call hook when jump back (loop), */
+ changedline(p, oldpc, npci)) { /* or when enter new line */
+ int newline = luaG_getfuncline(p, npci);
+ luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
+ }
+ L->oldpc = npci; /* 'pc' of last call to line hook */
+ }
+ if (L->status == LUA_YIELD) { /* did hook yield? */
+ if (counthook)
+ L->hookcount = 1; /* undo decrement to zero */
+ ci->u.l.savedpc--; /* undo increment (resume will increment it again) */
+ ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
+ luaD_throw(L, LUA_YIELD);
+ }
+ return 1; /* keep 'trap' on */
+}
+
diff --git a/src/libs/3rdparty/lua/src/ldebug.h b/src/libs/3rdparty/lua/src/ldebug.h
new file mode 100644
index 0000000000..2c3074c61b
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldebug.h
@@ -0,0 +1,63 @@
+/*
+** $Id: ldebug.h $
+** Auxiliary functions from Debug Interface module
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldebug_h
+#define ldebug_h
+
+
+#include "lstate.h"
+
+
+#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
+
+
+/* Active Lua function (given call info) */
+#define ci_func(ci) (clLvalue(s2v((ci)->func.p)))
+
+
+#define resethookcount(L) (L->hookcount = L->basehookcount)
+
+/*
+** mark for entries in 'lineinfo' array that has absolute information in
+** 'abslineinfo' array
+*/
+#define ABSLINEINFO (-0x80)
+
+
+/*
+** MAXimum number of successive Instructions WiTHout ABSolute line
+** information. (A power of two allows fast divisions.)
+*/
+#if !defined(MAXIWTHABS)
+#define MAXIWTHABS 128
+#endif
+
+
+LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
+LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
+ StkId *pos);
+LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
+ const char *opname);
+LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o);
+LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
+ const char *what);
+LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
+ const TValue *p2);
+LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1,
+ const TValue *p2,
+ const char *msg);
+LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1,
+ const TValue *p2);
+LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
+ const TValue *p2);
+LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
+LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
+ TString *src, int line);
+LUAI_FUNC l_noret luaG_errormsg (lua_State *L);
+LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/ldo.c b/src/libs/3rdparty/lua/src/ldo.c
new file mode 100644
index 0000000000..2a0017ca62
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldo.c
@@ -0,0 +1,1024 @@
+/*
+** $Id: ldo.c $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+#define ldo_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <setjmp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lundump.h"
+#include "lvm.h"
+#include "lzio.h"
+
+
+
+#define errorstatus(s) ((s) > LUA_YIELD)
+
+
+/*
+** {======================================================
+** Error-recovery functions
+** =======================================================
+*/
+
+/*
+** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By
+** default, Lua handles errors with exceptions when compiling as
+** C++ code, with _longjmp/_setjmp when asked to use them, and with
+** longjmp/setjmp otherwise.
+*/
+#if !defined(LUAI_THROW) /* { */
+
+#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* { */
+
+/* C++ exceptions */
+#define LUAI_THROW(L,c) throw(c)
+#define LUAI_TRY(L,c,a) \
+ try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; }
+#define luai_jmpbuf int /* dummy variable */
+
+#elif defined(LUA_USE_POSIX) /* }{ */
+
+/* in POSIX, try _longjmp/_setjmp (more efficient) */
+#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
+#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
+#define luai_jmpbuf jmp_buf
+
+#else /* }{ */
+
+/* ISO C handling with long jumps */
+#define LUAI_THROW(L,c) longjmp((c)->b, 1)
+#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
+#define luai_jmpbuf jmp_buf
+
+#endif /* } */
+
+#endif /* } */
+
+
+
+/* chain list of long jump buffers */
+struct lua_longjmp {
+ struct lua_longjmp *previous;
+ luai_jmpbuf b;
+ volatile int status; /* error code */
+};
+
+
+void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
+ switch (errcode) {
+ case LUA_ERRMEM: { /* memory error? */
+ setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
+ break;
+ }
+ case LUA_ERRERR: {
+ setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
+ break;
+ }
+ case LUA_OK: { /* special case only for closing upvalues */
+ setnilvalue(s2v(oldtop)); /* no error message */
+ break;
+ }
+ default: {
+ lua_assert(errorstatus(errcode)); /* real error */
+ setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */
+ break;
+ }
+ }
+ L->top.p = oldtop + 1;
+}
+
+
+l_noret luaD_throw (lua_State *L, int errcode) {
+ if (L->errorJmp) { /* thread has an error handler? */
+ L->errorJmp->status = errcode; /* set status */
+ LUAI_THROW(L, L->errorJmp); /* jump to it */
+ }
+ else { /* thread has no error handler */
+ global_State *g = G(L);
+ errcode = luaE_resetthread(L, errcode); /* close all upvalues */
+ if (g->mainthread->errorJmp) { /* main thread has a handler? */
+ setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */
+ luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
+ }
+ else { /* no handler at all; abort */
+ if (g->panic) { /* panic function? */
+ lua_unlock(L);
+ g->panic(L); /* call panic function (last chance to jump out) */
+ }
+ abort();
+ }
+ }
+}
+
+
+int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
+ l_uint32 oldnCcalls = L->nCcalls;
+ struct lua_longjmp lj;
+ lj.status = LUA_OK;
+ lj.previous = L->errorJmp; /* chain new error handler */
+ L->errorJmp = &lj;
+ LUAI_TRY(L, &lj,
+ (*f)(L, ud);
+ );
+ L->errorJmp = lj.previous; /* restore old error handler */
+ L->nCcalls = oldnCcalls;
+ return lj.status;
+}
+
+/* }====================================================== */
+
+
+/*
+** {==================================================================
+** Stack reallocation
+** ===================================================================
+*/
+
+
+/*
+** Change all pointers to the stack into offsets.
+*/
+static void relstack (lua_State *L) {
+ CallInfo *ci;
+ UpVal *up;
+ L->top.offset = savestack(L, L->top.p);
+ L->tbclist.offset = savestack(L, L->tbclist.p);
+ for (up = L->openupval; up != NULL; up = up->u.open.next)
+ up->v.offset = savestack(L, uplevel(up));
+ for (ci = L->ci; ci != NULL; ci = ci->previous) {
+ ci->top.offset = savestack(L, ci->top.p);
+ ci->func.offset = savestack(L, ci->func.p);
+ }
+}
+
+
+/*
+** Change back all offsets into pointers.
+*/
+static void correctstack (lua_State *L) {
+ CallInfo *ci;
+ UpVal *up;
+ L->top.p = restorestack(L, L->top.offset);
+ L->tbclist.p = restorestack(L, L->tbclist.offset);
+ for (up = L->openupval; up != NULL; up = up->u.open.next)
+ up->v.p = s2v(restorestack(L, up->v.offset));
+ for (ci = L->ci; ci != NULL; ci = ci->previous) {
+ ci->top.p = restorestack(L, ci->top.offset);
+ ci->func.p = restorestack(L, ci->func.offset);
+ if (isLua(ci))
+ ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */
+ }
+}
+
+
+/* some space for error handling */
+#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
+
+/*
+** Reallocate the stack to a new size, correcting all pointers into it.
+** In ISO C, any pointer use after the pointer has been deallocated is
+** undefined behavior. So, before the reallocation, all pointers are
+** changed to offsets, and after the reallocation they are changed back
+** to pointers. As during the reallocation the pointers are invalid, the
+** reallocation cannot run emergency collections.
+**
+** In case of allocation error, raise an error or return false according
+** to 'raiseerror'.
+*/
+int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
+ int oldsize = stacksize(L);
+ int i;
+ StkId newstack;
+ int oldgcstop = G(L)->gcstopem;
+ lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
+ relstack(L); /* change pointers to offsets */
+ G(L)->gcstopem = 1; /* stop emergency collection */
+ newstack = luaM_reallocvector(L, L->stack.p, oldsize + EXTRA_STACK,
+ newsize + EXTRA_STACK, StackValue);
+ G(L)->gcstopem = oldgcstop; /* restore emergency collection */
+ if (l_unlikely(newstack == NULL)) { /* reallocation failed? */
+ correctstack(L); /* change offsets back to pointers */
+ if (raiseerror)
+ luaM_error(L);
+ else return 0; /* do not raise an error */
+ }
+ L->stack.p = newstack;
+ correctstack(L); /* change offsets back to pointers */
+ L->stack_last.p = L->stack.p + newsize;
+ for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++)
+ setnilvalue(s2v(newstack + i)); /* erase new segment */
+ return 1;
+}
+
+
+/*
+** Try to grow the stack by at least 'n' elements. When 'raiseerror'
+** is true, raises any error; otherwise, return 0 in case of errors.
+*/
+int luaD_growstack (lua_State *L, int n, int raiseerror) {
+ int size = stacksize(L);
+ if (l_unlikely(size > LUAI_MAXSTACK)) {
+ /* if stack is larger than maximum, thread is already using the
+ extra space reserved for errors, that is, thread is handling
+ a stack error; cannot grow further than that. */
+ lua_assert(stacksize(L) == ERRORSTACKSIZE);
+ if (raiseerror)
+ luaD_throw(L, LUA_ERRERR); /* error inside message handler */
+ return 0; /* if not 'raiseerror', just signal it */
+ }
+ else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */
+ int newsize = 2 * size; /* tentative new size */
+ int needed = cast_int(L->top.p - L->stack.p) + n;
+ if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */
+ newsize = LUAI_MAXSTACK;
+ if (newsize < needed) /* but must respect what was asked for */
+ newsize = needed;
+ if (l_likely(newsize <= LUAI_MAXSTACK))
+ return luaD_reallocstack(L, newsize, raiseerror);
+ }
+ /* else stack overflow */
+ /* add extra size to be able to handle the error message */
+ luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror);
+ if (raiseerror)
+ luaG_runerror(L, "stack overflow");
+ return 0;
+}
+
+
+/*
+** Compute how much of the stack is being used, by computing the
+** maximum top of all call frames in the stack and the current top.
+*/
+static int stackinuse (lua_State *L) {
+ CallInfo *ci;
+ int res;
+ StkId lim = L->top.p;
+ for (ci = L->ci; ci != NULL; ci = ci->previous) {
+ if (lim < ci->top.p) lim = ci->top.p;
+ }
+ lua_assert(lim <= L->stack_last.p + EXTRA_STACK);
+ res = cast_int(lim - L->stack.p) + 1; /* part of stack in use */
+ if (res < LUA_MINSTACK)
+ res = LUA_MINSTACK; /* ensure a minimum size */
+ return res;
+}
+
+
+/*
+** If stack size is more than 3 times the current use, reduce that size
+** to twice the current use. (So, the final stack size is at most 2/3 the
+** previous size, and half of its entries are empty.)
+** As a particular case, if stack was handling a stack overflow and now
+** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than
+** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack
+** will be reduced to a "regular" size.
+*/
+void luaD_shrinkstack (lua_State *L) {
+ int inuse = stackinuse(L);
+ int max = (inuse > LUAI_MAXSTACK / 3) ? LUAI_MAXSTACK : inuse * 3;
+ /* if thread is currently not handling a stack overflow and its
+ size is larger than maximum "reasonable" size, shrink it */
+ if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) {
+ int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2;
+ luaD_reallocstack(L, nsize, 0); /* ok if that fails */
+ }
+ else /* don't change stack */
+ condmovestack(L,{},{}); /* (change only for debugging) */
+ luaE_shrinkCI(L); /* shrink CI list */
+}
+
+
+void luaD_inctop (lua_State *L) {
+ luaD_checkstack(L, 1);
+ L->top.p++;
+}
+
+/* }================================================================== */
+
+
+/*
+** Call a hook for the given event. Make sure there is a hook to be
+** called. (Both 'L->hook' and 'L->hookmask', which trigger this
+** function, can be changed asynchronously by signals.)
+*/
+void luaD_hook (lua_State *L, int event, int line,
+ int ftransfer, int ntransfer) {
+ lua_Hook hook = L->hook;
+ if (hook && L->allowhook) { /* make sure there is a hook */
+ int mask = CIST_HOOKED;
+ CallInfo *ci = L->ci;
+ ptrdiff_t top = savestack(L, L->top.p); /* preserve original 'top' */
+ ptrdiff_t ci_top = savestack(L, ci->top.p); /* idem for 'ci->top' */
+ lua_Debug ar;
+ ar.event = event;
+ ar.currentline = line;
+ ar.i_ci = ci;
+ if (ntransfer != 0) {
+ mask |= CIST_TRAN; /* 'ci' has transfer information */
+ ci->u2.transferinfo.ftransfer = ftransfer;
+ ci->u2.transferinfo.ntransfer = ntransfer;
+ }
+ if (isLua(ci) && L->top.p < ci->top.p)
+ L->top.p = ci->top.p; /* protect entire activation register */
+ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
+ if (ci->top.p < L->top.p + LUA_MINSTACK)
+ ci->top.p = L->top.p + LUA_MINSTACK;
+ L->allowhook = 0; /* cannot call hooks inside a hook */
+ ci->callstatus |= mask;
+ lua_unlock(L);
+ (*hook)(L, &ar);
+ lua_lock(L);
+ lua_assert(!L->allowhook);
+ L->allowhook = 1;
+ ci->top.p = restorestack(L, ci_top);
+ L->top.p = restorestack(L, top);
+ ci->callstatus &= ~mask;
+ }
+}
+
+
+/*
+** Executes a call hook for Lua functions. This function is called
+** whenever 'hookmask' is not zero, so it checks whether call hooks are
+** active.
+*/
+void luaD_hookcall (lua_State *L, CallInfo *ci) {
+ L->oldpc = 0; /* set 'oldpc' for new function */
+ if (L->hookmask & LUA_MASKCALL) { /* is call hook on? */
+ int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL
+ : LUA_HOOKCALL;
+ Proto *p = ci_func(ci)->p;
+ ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
+ luaD_hook(L, event, -1, 1, p->numparams);
+ ci->u.l.savedpc--; /* correct 'pc' */
+ }
+}
+
+
+/*
+** Executes a return hook for Lua and C functions and sets/corrects
+** 'oldpc'. (Note that this correction is needed by the line hook, so it
+** is done even when return hooks are off.)
+*/
+static void rethook (lua_State *L, CallInfo *ci, int nres) {
+ if (L->hookmask & LUA_MASKRET) { /* is return hook on? */
+ StkId firstres = L->top.p - nres; /* index of first result */
+ int delta = 0; /* correction for vararg functions */
+ int ftransfer;
+ if (isLua(ci)) {
+ Proto *p = ci_func(ci)->p;
+ if (p->is_vararg)
+ delta = ci->u.l.nextraargs + p->numparams + 1;
+ }
+ ci->func.p += delta; /* if vararg, back to virtual 'func' */
+ ftransfer = cast(unsigned short, firstres - ci->func.p);
+ luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
+ ci->func.p -= delta;
+ }
+ if (isLua(ci = ci->previous))
+ L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */
+}
+
+
+/*
+** Check whether 'func' has a '__call' metafield. If so, put it in the
+** stack, below original 'func', so that 'luaD_precall' can call it. Raise
+** an error if there is no '__call' metafield.
+*/
+StkId luaD_tryfuncTM (lua_State *L, StkId func) {
+ const TValue *tm;
+ StkId p;
+ checkstackGCp(L, 1, func); /* space for metamethod */
+ tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */
+ if (l_unlikely(ttisnil(tm)))
+ luaG_callerror(L, s2v(func)); /* nothing to call */
+ for (p = L->top.p; p > func; p--) /* open space for metamethod */
+ setobjs2s(L, p, p-1);
+ L->top.p++; /* stack space pre-allocated by the caller */
+ setobj2s(L, func, tm); /* metamethod is the new function to be called */
+ return func;
+}
+
+
+/*
+** Given 'nres' results at 'firstResult', move 'wanted' of them to 'res'.
+** Handle most typical cases (zero results for commands, one result for
+** expressions, multiple results for tail calls/single parameters)
+** separated.
+*/
+l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) {
+ StkId firstresult;
+ int i;
+ switch (wanted) { /* handle typical cases separately */
+ case 0: /* no values needed */
+ L->top.p = res;
+ return;
+ case 1: /* one value needed */
+ if (nres == 0) /* no results? */
+ setnilvalue(s2v(res)); /* adjust with nil */
+ else /* at least one result */
+ setobjs2s(L, res, L->top.p - nres); /* move it to proper place */
+ L->top.p = res + 1;
+ return;
+ case LUA_MULTRET:
+ wanted = nres; /* we want all results */
+ break;
+ default: /* two/more results and/or to-be-closed variables */
+ if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
+ L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
+ L->ci->u2.nres = nres;
+ res = luaF_close(L, res, CLOSEKTOP, 1);
+ L->ci->callstatus &= ~CIST_CLSRET;
+ if (L->hookmask) { /* if needed, call hook after '__close's */
+ ptrdiff_t savedres = savestack(L, res);
+ rethook(L, L->ci, nres);
+ res = restorestack(L, savedres); /* hook can move stack */
+ }
+ wanted = decodeNresults(wanted);
+ if (wanted == LUA_MULTRET)
+ wanted = nres; /* we want all results */
+ }
+ break;
+ }
+ /* generic case */
+ firstresult = L->top.p - nres; /* index of first result */
+ if (nres > wanted) /* extra results? */
+ nres = wanted; /* don't need them */
+ for (i = 0; i < nres; i++) /* move all results to correct place */
+ setobjs2s(L, res + i, firstresult + i);
+ for (; i < wanted; i++) /* complete wanted number of results */
+ setnilvalue(s2v(res + i));
+ L->top.p = res + wanted; /* top points after the last result */
+}
+
+
+/*
+** Finishes a function call: calls hook if necessary, moves current
+** number of results to proper place, and returns to previous call
+** info. If function has to close variables, hook must be called after
+** that.
+*/
+void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
+ int wanted = ci->nresults;
+ if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted)))
+ rethook(L, ci, nres);
+ /* move results to proper place */
+ moveresults(L, ci->func.p, nres, wanted);
+ /* function cannot be in any of these cases when returning */
+ lua_assert(!(ci->callstatus &
+ (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)));
+ L->ci = ci->previous; /* back to caller (after closing variables) */
+}
+
+
+
+#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L))
+
+
+l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret,
+ int mask, StkId top) {
+ CallInfo *ci = L->ci = next_ci(L); /* new frame */
+ ci->func.p = func;
+ ci->nresults = nret;
+ ci->callstatus = mask;
+ ci->top.p = top;
+ return ci;
+}
+
+
+/*
+** precall for C functions
+*/
+l_sinline int precallC (lua_State *L, StkId func, int nresults,
+ lua_CFunction f) {
+ int n; /* number of returns */
+ CallInfo *ci;
+ checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
+ L->ci = ci = prepCallInfo(L, func, nresults, CIST_C,
+ L->top.p + LUA_MINSTACK);
+ lua_assert(ci->top.p <= L->stack_last.p);
+ if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
+ int narg = cast_int(L->top.p - func) - 1;
+ luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
+ }
+ lua_unlock(L);
+ n = (*f)(L); /* do the actual call */
+ lua_lock(L);
+ api_checknelems(L, n);
+ luaD_poscall(L, ci, n);
+ return n;
+}
+
+
+/*
+** Prepare a function for a tail call, building its call info on top
+** of the current call info. 'narg1' is the number of arguments plus 1
+** (so that it includes the function itself). Return the number of
+** results, if it was a C function, or -1 for a Lua function.
+*/
+int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
+ int narg1, int delta) {
+ retry:
+ switch (ttypetag(s2v(func))) {
+ case LUA_VCCL: /* C closure */
+ return precallC(L, func, LUA_MULTRET, clCvalue(s2v(func))->f);
+ case LUA_VLCF: /* light C function */
+ return precallC(L, func, LUA_MULTRET, fvalue(s2v(func)));
+ case LUA_VLCL: { /* Lua function */
+ Proto *p = clLvalue(s2v(func))->p;
+ int fsize = p->maxstacksize; /* frame size */
+ int nfixparams = p->numparams;
+ int i;
+ checkstackGCp(L, fsize - delta, func);
+ ci->func.p -= delta; /* restore 'func' (if vararg) */
+ for (i = 0; i < narg1; i++) /* move down function and arguments */
+ setobjs2s(L, ci->func.p + i, func + i);
+ func = ci->func.p; /* moved-down function */
+ for (; narg1 <= nfixparams; narg1++)
+ setnilvalue(s2v(func + narg1)); /* complete missing arguments */
+ ci->top.p = func + 1 + fsize; /* top for new function */
+ lua_assert(ci->top.p <= L->stack_last.p);
+ ci->u.l.savedpc = p->code; /* starting point */
+ ci->callstatus |= CIST_TAIL;
+ L->top.p = func + narg1; /* set top */
+ return -1;
+ }
+ default: { /* not a function */
+ func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
+ /* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */
+ narg1++;
+ goto retry; /* try again */
+ }
+ }
+}
+
+
+/*
+** Prepares the call to a function (C or Lua). For C functions, also do
+** the call. The function to be called is at '*func'. The arguments
+** are on the stack, right after the function. Returns the CallInfo
+** to be executed, if it was a Lua function. Otherwise (a C function)
+** returns NULL, with all the results on the stack, starting at the
+** original function position.
+*/
+CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
+ retry:
+ switch (ttypetag(s2v(func))) {
+ case LUA_VCCL: /* C closure */
+ precallC(L, func, nresults, clCvalue(s2v(func))->f);
+ return NULL;
+ case LUA_VLCF: /* light C function */
+ precallC(L, func, nresults, fvalue(s2v(func)));
+ return NULL;
+ case LUA_VLCL: { /* Lua function */
+ CallInfo *ci;
+ Proto *p = clLvalue(s2v(func))->p;
+ int narg = cast_int(L->top.p - func) - 1; /* number of real arguments */
+ int nfixparams = p->numparams;
+ int fsize = p->maxstacksize; /* frame size */
+ checkstackGCp(L, fsize, func);
+ L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize);
+ ci->u.l.savedpc = p->code; /* starting point */
+ for (; narg < nfixparams; narg++)
+ setnilvalue(s2v(L->top.p++)); /* complete missing arguments */
+ lua_assert(ci->top.p <= L->stack_last.p);
+ return ci;
+ }
+ default: { /* not a function */
+ func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
+ /* return luaD_precall(L, func, nresults); */
+ goto retry; /* try again with metamethod */
+ }
+ }
+}
+
+
+/*
+** Call a function (C or Lua) through C. 'inc' can be 1 (increment
+** number of recursive invocations in the C stack) or nyci (the same
+** plus increment number of non-yieldable calls).
+** This function can be called with some use of EXTRA_STACK, so it should
+** check the stack before doing anything else. 'luaD_precall' already
+** does that.
+*/
+l_sinline void ccall (lua_State *L, StkId func, int nResults, l_uint32 inc) {
+ CallInfo *ci;
+ L->nCcalls += inc;
+ if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) {
+ checkstackp(L, 0, func); /* free any use of EXTRA_STACK */
+ luaE_checkcstack(L);
+ }
+ if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
+ ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
+ luaV_execute(L, ci); /* call it */
+ }
+ L->nCcalls -= inc;
+}
+
+
+/*
+** External interface for 'ccall'
+*/
+void luaD_call (lua_State *L, StkId func, int nResults) {
+ ccall(L, func, nResults, 1);
+}
+
+
+/*
+** Similar to 'luaD_call', but does not allow yields during the call.
+*/
+void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
+ ccall(L, func, nResults, nyci);
+}
+
+
+/*
+** Finish the job of 'lua_pcallk' after it was interrupted by an yield.
+** (The caller, 'finishCcall', does the final call to 'adjustresults'.)
+** The main job is to complete the 'luaD_pcall' called by 'lua_pcallk'.
+** If a '__close' method yields here, eventually control will be back
+** to 'finishCcall' (when that '__close' method finally returns) and
+** 'finishpcallk' will run again and close any still pending '__close'
+** methods. Similarly, if a '__close' method errs, 'precover' calls
+** 'unroll' which calls ''finishCcall' and we are back here again, to
+** close any pending '__close' methods.
+** Note that, up to the call to 'luaF_close', the corresponding
+** 'CallInfo' is not modified, so that this repeated run works like the
+** first one (except that it has at least one less '__close' to do). In
+** particular, field CIST_RECST preserves the error status across these
+** multiple runs, changing only if there is a new error.
+*/
+static int finishpcallk (lua_State *L, CallInfo *ci) {
+ int status = getcistrecst(ci); /* get original status */
+ if (l_likely(status == LUA_OK)) /* no error? */
+ status = LUA_YIELD; /* was interrupted by an yield */
+ else { /* error */
+ StkId func = restorestack(L, ci->u2.funcidx);
+ L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */
+ func = luaF_close(L, func, status, 1); /* can yield or raise an error */
+ luaD_seterrorobj(L, status, func);
+ luaD_shrinkstack(L); /* restore stack size in case of overflow */
+ setcistrecst(ci, LUA_OK); /* clear original status */
+ }
+ ci->callstatus &= ~CIST_YPCALL;
+ L->errfunc = ci->u.c.old_errfunc;
+ /* if it is here, there were errors or yields; unlike 'lua_pcallk',
+ do not change status */
+ return status;
+}
+
+
+/*
+** Completes the execution of a C function interrupted by an yield.
+** The interruption must have happened while the function was either
+** closing its tbc variables in 'moveresults' or executing
+** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes
+** 'luaD_poscall'. In the second case, the call to 'finishpcallk'
+** finishes the interrupted execution of 'lua_pcallk'. After that, it
+** calls the continuation of the interrupted function and finally it
+** completes the job of the 'luaD_call' that called the function. In
+** the call to 'adjustresults', we do not know the number of results
+** of the function called by 'lua_callk'/'lua_pcallk', so we are
+** conservative and use LUA_MULTRET (always adjust).
+*/
+static void finishCcall (lua_State *L, CallInfo *ci) {
+ int n; /* actual number of results from C function */
+ if (ci->callstatus & CIST_CLSRET) { /* was returning? */
+ lua_assert(hastocloseCfunc(ci->nresults));
+ n = ci->u2.nres; /* just redo 'luaD_poscall' */
+ /* don't need to reset CIST_CLSRET, as it will be set again anyway */
+ }
+ else {
+ int status = LUA_YIELD; /* default if there were no errors */
+ /* must have a continuation and must be able to call it */
+ lua_assert(ci->u.c.k != NULL && yieldable(L));
+ if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */
+ status = finishpcallk(L, ci); /* finish it */
+ adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */
+ lua_unlock(L);
+ n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */
+ lua_lock(L);
+ api_checknelems(L, n);
+ }
+ luaD_poscall(L, ci, n); /* finish 'luaD_call' */
+}
+
+
+/*
+** Executes "full continuation" (everything in the stack) of a
+** previously interrupted coroutine until the stack is empty (or another
+** interruption long-jumps out of the loop).
+*/
+static void unroll (lua_State *L, void *ud) {
+ CallInfo *ci;
+ UNUSED(ud);
+ while ((ci = L->ci) != &L->base_ci) { /* something in the stack */
+ if (!isLua(ci)) /* C function? */
+ finishCcall(L, ci); /* complete its execution */
+ else { /* Lua function */
+ luaV_finishOp(L); /* finish interrupted instruction */
+ luaV_execute(L, ci); /* execute down to higher C 'boundary' */
+ }
+ }
+}
+
+
+/*
+** Try to find a suspended protected call (a "recover point") for the
+** given thread.
+*/
+static CallInfo *findpcall (lua_State *L) {
+ CallInfo *ci;
+ for (ci = L->ci; ci != NULL; ci = ci->previous) { /* search for a pcall */
+ if (ci->callstatus & CIST_YPCALL)
+ return ci;
+ }
+ return NULL; /* no pending pcall */
+}
+
+
+/*
+** Signal an error in the call to 'lua_resume', not in the execution
+** of the coroutine itself. (Such errors should not be handled by any
+** coroutine error handler and should not kill the coroutine.)
+*/
+static int resume_error (lua_State *L, const char *msg, int narg) {
+ L->top.p -= narg; /* remove args from the stack */
+ setsvalue2s(L, L->top.p, luaS_new(L, msg)); /* push error message */
+ api_incr_top(L);
+ lua_unlock(L);
+ return LUA_ERRRUN;
+}
+
+
+/*
+** Do the work for 'lua_resume' in protected mode. Most of the work
+** depends on the status of the coroutine: initial state, suspended
+** inside a hook, or regularly suspended (optionally with a continuation
+** function), plus erroneous cases: non-suspended coroutine or dead
+** coroutine.
+*/
+static void resume (lua_State *L, void *ud) {
+ int n = *(cast(int*, ud)); /* number of arguments */
+ StkId firstArg = L->top.p - n; /* first argument */
+ CallInfo *ci = L->ci;
+ if (L->status == LUA_OK) /* starting a coroutine? */
+ ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */
+ else { /* resuming from previous yield */
+ lua_assert(L->status == LUA_YIELD);
+ L->status = LUA_OK; /* mark that it is running (again) */
+ if (isLua(ci)) { /* yielded inside a hook? */
+ L->top.p = firstArg; /* discard arguments */
+ luaV_execute(L, ci); /* just continue running Lua code */
+ }
+ else { /* 'common' yield */
+ if (ci->u.c.k != NULL) { /* does it have a continuation function? */
+ lua_unlock(L);
+ n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */
+ lua_lock(L);
+ api_checknelems(L, n);
+ }
+ luaD_poscall(L, ci, n); /* finish 'luaD_call' */
+ }
+ unroll(L, NULL); /* run continuation */
+ }
+}
+
+
+/*
+** Unrolls a coroutine in protected mode while there are recoverable
+** errors, that is, errors inside a protected call. (Any error
+** interrupts 'unroll', and this loop protects it again so it can
+** continue.) Stops with a normal end (status == LUA_OK), an yield
+** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't
+** find a recover point).
+*/
+static int precover (lua_State *L, int status) {
+ CallInfo *ci;
+ while (errorstatus(status) && (ci = findpcall(L)) != NULL) {
+ L->ci = ci; /* go down to recovery functions */
+ setcistrecst(ci, status); /* status to finish 'pcall' */
+ status = luaD_rawrunprotected(L, unroll, NULL);
+ }
+ return status;
+}
+
+
+LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
+ int *nresults) {
+ int status;
+ lua_lock(L);
+ if (L->status == LUA_OK) { /* may be starting a coroutine */
+ if (L->ci != &L->base_ci) /* not in base level? */
+ return resume_error(L, "cannot resume non-suspended coroutine", nargs);
+ else if (L->top.p - (L->ci->func.p + 1) == nargs) /* no function? */
+ return resume_error(L, "cannot resume dead coroutine", nargs);
+ }
+ else if (L->status != LUA_YIELD) /* ended with errors? */
+ return resume_error(L, "cannot resume dead coroutine", nargs);
+ L->nCcalls = (from) ? getCcalls(from) : 0;
+ if (getCcalls(L) >= LUAI_MAXCCALLS)
+ return resume_error(L, "C stack overflow", nargs);
+ L->nCcalls++;
+ luai_userstateresume(L, nargs);
+ api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
+ status = luaD_rawrunprotected(L, resume, &nargs);
+ /* continue running after recoverable errors */
+ status = precover(L, status);
+ if (l_likely(!errorstatus(status)))
+ lua_assert(status == L->status); /* normal end or yield */
+ else { /* unrecoverable error */
+ L->status = cast_byte(status); /* mark thread as 'dead' */
+ luaD_seterrorobj(L, status, L->top.p); /* push error message */
+ L->ci->top.p = L->top.p;
+ }
+ *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield
+ : cast_int(L->top.p - (L->ci->func.p + 1));
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_isyieldable (lua_State *L) {
+ return yieldable(L);
+}
+
+
+LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
+ lua_KFunction k) {
+ CallInfo *ci;
+ luai_userstateyield(L, nresults);
+ lua_lock(L);
+ ci = L->ci;
+ api_checknelems(L, nresults);
+ if (l_unlikely(!yieldable(L))) {
+ if (L != G(L)->mainthread)
+ luaG_runerror(L, "attempt to yield across a C-call boundary");
+ else
+ luaG_runerror(L, "attempt to yield from outside a coroutine");
+ }
+ L->status = LUA_YIELD;
+ ci->u2.nyield = nresults; /* save number of results */
+ if (isLua(ci)) { /* inside a hook? */
+ lua_assert(!isLuacode(ci));
+ api_check(L, nresults == 0, "hooks cannot yield values");
+ api_check(L, k == NULL, "hooks cannot continue after yielding");
+ }
+ else {
+ if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
+ ci->u.c.ctx = ctx; /* save context */
+ luaD_throw(L, LUA_YIELD);
+ }
+ lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */
+ lua_unlock(L);
+ return 0; /* return to 'luaD_hook' */
+}
+
+
+/*
+** Auxiliary structure to call 'luaF_close' in protected mode.
+*/
+struct CloseP {
+ StkId level;
+ int status;
+};
+
+
+/*
+** Auxiliary function to call 'luaF_close' in protected mode.
+*/
+static void closepaux (lua_State *L, void *ud) {
+ struct CloseP *pcl = cast(struct CloseP *, ud);
+ luaF_close(L, pcl->level, pcl->status, 0);
+}
+
+
+/*
+** Calls 'luaF_close' in protected mode. Return the original status
+** or, in case of errors, the new status.
+*/
+int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) {
+ CallInfo *old_ci = L->ci;
+ lu_byte old_allowhooks = L->allowhook;
+ for (;;) { /* keep closing upvalues until no more errors */
+ struct CloseP pcl;
+ pcl.level = restorestack(L, level); pcl.status = status;
+ status = luaD_rawrunprotected(L, &closepaux, &pcl);
+ if (l_likely(status == LUA_OK)) /* no more errors? */
+ return pcl.status;
+ else { /* an error occurred; restore saved state and repeat */
+ L->ci = old_ci;
+ L->allowhook = old_allowhooks;
+ }
+ }
+}
+
+
+/*
+** Call the C function 'func' in protected mode, restoring basic
+** thread information ('allowhook', etc.) and in particular
+** its stack level in case of errors.
+*/
+int luaD_pcall (lua_State *L, Pfunc func, void *u,
+ ptrdiff_t old_top, ptrdiff_t ef) {
+ int status;
+ CallInfo *old_ci = L->ci;
+ lu_byte old_allowhooks = L->allowhook;
+ ptrdiff_t old_errfunc = L->errfunc;
+ L->errfunc = ef;
+ status = luaD_rawrunprotected(L, func, u);
+ if (l_unlikely(status != LUA_OK)) { /* an error occurred? */
+ L->ci = old_ci;
+ L->allowhook = old_allowhooks;
+ status = luaD_closeprotected(L, old_top, status);
+ luaD_seterrorobj(L, status, restorestack(L, old_top));
+ luaD_shrinkstack(L); /* restore stack size in case of overflow */
+ }
+ L->errfunc = old_errfunc;
+ return status;
+}
+
+
+
+/*
+** Execute a protected parser.
+*/
+struct SParser { /* data to 'f_parser' */
+ ZIO *z;
+ Mbuffer buff; /* dynamic structure used by the scanner */
+ Dyndata dyd; /* dynamic structures used by the parser */
+ const char *mode;
+ const char *name;
+};
+
+
+static void checkmode (lua_State *L, const char *mode, const char *x) {
+ if (mode && strchr(mode, x[0]) == NULL) {
+ luaO_pushfstring(L,
+ "attempt to load a %s chunk (mode is '%s')", x, mode);
+ luaD_throw(L, LUA_ERRSYNTAX);
+ }
+}
+
+
+static void f_parser (lua_State *L, void *ud) {
+ LClosure *cl;
+ struct SParser *p = cast(struct SParser *, ud);
+ int c = zgetc(p->z); /* read first character */
+ if (c == LUA_SIGNATURE[0]) {
+ checkmode(L, p->mode, "binary");
+ cl = luaU_undump(L, p->z, p->name);
+ }
+ else {
+ checkmode(L, p->mode, "text");
+ cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
+ }
+ lua_assert(cl->nupvalues == cl->p->sizeupvalues);
+ luaF_initupvals(L, cl);
+}
+
+
+int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
+ const char *mode) {
+ struct SParser p;
+ int status;
+ incnny(L); /* cannot yield during parsing */
+ p.z = z; p.name = name; p.mode = mode;
+ p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0;
+ p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
+ p.dyd.label.arr = NULL; p.dyd.label.size = 0;
+ luaZ_initbuffer(L, &p.buff);
+ status = luaD_pcall(L, f_parser, &p, savestack(L, L->top.p), L->errfunc);
+ luaZ_freebuffer(L, &p.buff);
+ luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
+ luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
+ luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size);
+ decnny(L);
+ return status;
+}
+
+
diff --git a/src/libs/3rdparty/lua/src/ldo.h b/src/libs/3rdparty/lua/src/ldo.h
new file mode 100644
index 0000000000..1aa446ad09
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldo.h
@@ -0,0 +1,88 @@
+/*
+** $Id: ldo.h $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldo_h
+#define ldo_h
+
+
+#include "llimits.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lzio.h"
+
+
+/*
+** Macro to check stack size and grow stack if needed. Parameters
+** 'pre'/'pos' allow the macro to preserve a pointer into the
+** stack across reallocations, doing the work only when needed.
+** It also allows the running of one GC step when the stack is
+** reallocated.
+** 'condmovestack' is used in heavy tests to force a stack reallocation
+** at every check.
+*/
+#define luaD_checkstackaux(L,n,pre,pos) \
+ if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \
+ { pre; luaD_growstack(L, n, 1); pos; } \
+ else { condmovestack(L,pre,pos); }
+
+/* In general, 'pre'/'pos' are empty (nothing to save) */
+#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0)
+
+
+
+#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p))
+#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n))
+
+
+/* macro to check stack size, preserving 'p' */
+#define checkstackp(L,n,p) \
+ luaD_checkstackaux(L, n, \
+ ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \
+ p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
+
+
+/* macro to check stack size and GC, preserving 'p' */
+#define checkstackGCp(L,n,p) \
+ luaD_checkstackaux(L, n, \
+ ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
+ luaC_checkGC(L), /* stack grow uses memory */ \
+ p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
+
+
+/* macro to check stack size and GC */
+#define checkstackGC(L,fsize) \
+ luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0)
+
+
+/* type of protected functions, to be ran by 'runprotected' */
+typedef void (*Pfunc) (lua_State *L, void *ud);
+
+LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
+LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
+ const char *mode);
+LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
+ int fTransfer, int nTransfer);
+LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
+LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
+ int narg1, int delta);
+LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
+LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
+LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
+LUAI_FUNC StkId luaD_tryfuncTM (lua_State *L, StkId func);
+LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status);
+LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
+ ptrdiff_t oldtop, ptrdiff_t ef);
+LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
+LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror);
+LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror);
+LUAI_FUNC void luaD_shrinkstack (lua_State *L);
+LUAI_FUNC void luaD_inctop (lua_State *L);
+
+LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode);
+LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/ldump.c b/src/libs/3rdparty/lua/src/ldump.c
new file mode 100644
index 0000000000..f231691b77
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldump.c
@@ -0,0 +1,230 @@
+/*
+** $Id: ldump.c $
+** save precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#define ldump_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <limits.h>
+#include <stddef.h>
+
+#include "lua.h"
+
+#include "lobject.h"
+#include "lstate.h"
+#include "lundump.h"
+
+
+typedef struct {
+ lua_State *L;
+ lua_Writer writer;
+ void *data;
+ int strip;
+ int status;
+} DumpState;
+
+
+/*
+** All high-level dumps go through dumpVector; you can change it to
+** change the endianness of the result
+*/
+#define dumpVector(D,v,n) dumpBlock(D,v,(n)*sizeof((v)[0]))
+
+#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char))
+
+
+static void dumpBlock (DumpState *D, const void *b, size_t size) {
+ if (D->status == 0 && size > 0) {
+ lua_unlock(D->L);
+ D->status = (*D->writer)(D->L, b, size, D->data);
+ lua_lock(D->L);
+ }
+}
+
+
+#define dumpVar(D,x) dumpVector(D,&x,1)
+
+
+static void dumpByte (DumpState *D, int y) {
+ lu_byte x = (lu_byte)y;
+ dumpVar(D, x);
+}
+
+
+/*
+** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6"
+** rounds up the division.)
+*/
+#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7)
+
+static void dumpSize (DumpState *D, size_t x) {
+ lu_byte buff[DIBS];
+ int n = 0;
+ do {
+ buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */
+ x >>= 7;
+ } while (x != 0);
+ buff[DIBS - 1] |= 0x80; /* mark last byte */
+ dumpVector(D, buff + DIBS - n, n);
+}
+
+
+static void dumpInt (DumpState *D, int x) {
+ dumpSize(D, x);
+}
+
+
+static void dumpNumber (DumpState *D, lua_Number x) {
+ dumpVar(D, x);
+}
+
+
+static void dumpInteger (DumpState *D, lua_Integer x) {
+ dumpVar(D, x);
+}
+
+
+static void dumpString (DumpState *D, const TString *s) {
+ if (s == NULL)
+ dumpSize(D, 0);
+ else {
+ size_t size = tsslen(s);
+ const char *str = getstr(s);
+ dumpSize(D, size + 1);
+ dumpVector(D, str, size);
+ }
+}
+
+
+static void dumpCode (DumpState *D, const Proto *f) {
+ dumpInt(D, f->sizecode);
+ dumpVector(D, f->code, f->sizecode);
+}
+
+
+static void dumpFunction(DumpState *D, const Proto *f, TString *psource);
+
+static void dumpConstants (DumpState *D, const Proto *f) {
+ int i;
+ int n = f->sizek;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++) {
+ const TValue *o = &f->k[i];
+ int tt = ttypetag(o);
+ dumpByte(D, tt);
+ switch (tt) {
+ case LUA_VNUMFLT:
+ dumpNumber(D, fltvalue(o));
+ break;
+ case LUA_VNUMINT:
+ dumpInteger(D, ivalue(o));
+ break;
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR:
+ dumpString(D, tsvalue(o));
+ break;
+ default:
+ lua_assert(tt == LUA_VNIL || tt == LUA_VFALSE || tt == LUA_VTRUE);
+ }
+ }
+}
+
+
+static void dumpProtos (DumpState *D, const Proto *f) {
+ int i;
+ int n = f->sizep;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++)
+ dumpFunction(D, f->p[i], f->source);
+}
+
+
+static void dumpUpvalues (DumpState *D, const Proto *f) {
+ int i, n = f->sizeupvalues;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++) {
+ dumpByte(D, f->upvalues[i].instack);
+ dumpByte(D, f->upvalues[i].idx);
+ dumpByte(D, f->upvalues[i].kind);
+ }
+}
+
+
+static void dumpDebug (DumpState *D, const Proto *f) {
+ int i, n;
+ n = (D->strip) ? 0 : f->sizelineinfo;
+ dumpInt(D, n);
+ dumpVector(D, f->lineinfo, n);
+ n = (D->strip) ? 0 : f->sizeabslineinfo;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++) {
+ dumpInt(D, f->abslineinfo[i].pc);
+ dumpInt(D, f->abslineinfo[i].line);
+ }
+ n = (D->strip) ? 0 : f->sizelocvars;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++) {
+ dumpString(D, f->locvars[i].varname);
+ dumpInt(D, f->locvars[i].startpc);
+ dumpInt(D, f->locvars[i].endpc);
+ }
+ n = (D->strip) ? 0 : f->sizeupvalues;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++)
+ dumpString(D, f->upvalues[i].name);
+}
+
+
+static void dumpFunction (DumpState *D, const Proto *f, TString *psource) {
+ if (D->strip || f->source == psource)
+ dumpString(D, NULL); /* no debug info or same source as its parent */
+ else
+ dumpString(D, f->source);
+ dumpInt(D, f->linedefined);
+ dumpInt(D, f->lastlinedefined);
+ dumpByte(D, f->numparams);
+ dumpByte(D, f->is_vararg);
+ dumpByte(D, f->maxstacksize);
+ dumpCode(D, f);
+ dumpConstants(D, f);
+ dumpUpvalues(D, f);
+ dumpProtos(D, f);
+ dumpDebug(D, f);
+}
+
+
+static void dumpHeader (DumpState *D) {
+ dumpLiteral(D, LUA_SIGNATURE);
+ dumpByte(D, LUAC_VERSION);
+ dumpByte(D, LUAC_FORMAT);
+ dumpLiteral(D, LUAC_DATA);
+ dumpByte(D, sizeof(Instruction));
+ dumpByte(D, sizeof(lua_Integer));
+ dumpByte(D, sizeof(lua_Number));
+ dumpInteger(D, LUAC_INT);
+ dumpNumber(D, LUAC_NUM);
+}
+
+
+/*
+** dump Lua function as precompiled chunk
+*/
+int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data,
+ int strip) {
+ DumpState D;
+ D.L = L;
+ D.writer = w;
+ D.data = data;
+ D.strip = strip;
+ D.status = 0;
+ dumpHeader(&D);
+ dumpByte(&D, f->sizeupvalues);
+ dumpFunction(&D, f, NULL);
+ return D.status;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lfunc.c b/src/libs/3rdparty/lua/src/lfunc.c
new file mode 100644
index 0000000000..0945f241de
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lfunc.c
@@ -0,0 +1,294 @@
+/*
+** $Id: lfunc.c $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+#define lfunc_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <stddef.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+CClosure *luaF_newCclosure (lua_State *L, int nupvals) {
+ GCObject *o = luaC_newobj(L, LUA_VCCL, sizeCclosure(nupvals));
+ CClosure *c = gco2ccl(o);
+ c->nupvalues = cast_byte(nupvals);
+ return c;
+}
+
+
+LClosure *luaF_newLclosure (lua_State *L, int nupvals) {
+ GCObject *o = luaC_newobj(L, LUA_VLCL, sizeLclosure(nupvals));
+ LClosure *c = gco2lcl(o);
+ c->p = NULL;
+ c->nupvalues = cast_byte(nupvals);
+ while (nupvals--) c->upvals[nupvals] = NULL;
+ return c;
+}
+
+
+/*
+** fill a closure with new closed upvalues
+*/
+void luaF_initupvals (lua_State *L, LClosure *cl) {
+ int i;
+ for (i = 0; i < cl->nupvalues; i++) {
+ GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
+ UpVal *uv = gco2upv(o);
+ uv->v.p = &uv->u.value; /* make it closed */
+ setnilvalue(uv->v.p);
+ cl->upvals[i] = uv;
+ luaC_objbarrier(L, cl, uv);
+ }
+}
+
+
+/*
+** Create a new upvalue at the given level, and link it to the list of
+** open upvalues of 'L' after entry 'prev'.
+**/
+static UpVal *newupval (lua_State *L, StkId level, UpVal **prev) {
+ GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
+ UpVal *uv = gco2upv(o);
+ UpVal *next = *prev;
+ uv->v.p = s2v(level); /* current value lives in the stack */
+ uv->u.open.next = next; /* link it to list of open upvalues */
+ uv->u.open.previous = prev;
+ if (next)
+ next->u.open.previous = &uv->u.open.next;
+ *prev = uv;
+ if (!isintwups(L)) { /* thread not in list of threads with upvalues? */
+ L->twups = G(L)->twups; /* link it to the list */
+ G(L)->twups = L;
+ }
+ return uv;
+}
+
+
+/*
+** Find and reuse, or create if it does not exist, an upvalue
+** at the given level.
+*/
+UpVal *luaF_findupval (lua_State *L, StkId level) {
+ UpVal **pp = &L->openupval;
+ UpVal *p;
+ lua_assert(isintwups(L) || L->openupval == NULL);
+ while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */
+ lua_assert(!isdead(G(L), p));
+ if (uplevel(p) == level) /* corresponding upvalue? */
+ return p; /* return it */
+ pp = &p->u.open.next;
+ }
+ /* not found: create a new upvalue after 'pp' */
+ return newupval(L, level, pp);
+}
+
+
+/*
+** Call closing method for object 'obj' with error message 'err'. The
+** boolean 'yy' controls whether the call is yieldable.
+** (This function assumes EXTRA_STACK.)
+*/
+static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
+ StkId top = L->top.p;
+ const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
+ setobj2s(L, top, tm); /* will call metamethod... */
+ setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
+ setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
+ L->top.p = top + 3; /* add function and arguments */
+ if (yy)
+ luaD_call(L, top, 0);
+ else
+ luaD_callnoyield(L, top, 0);
+}
+
+
+/*
+** Check whether object at given level has a close metamethod and raise
+** an error if not.
+*/
+static void checkclosemth (lua_State *L, StkId level) {
+ const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE);
+ if (ttisnil(tm)) { /* no metamethod? */
+ int idx = cast_int(level - L->ci->func.p); /* variable index */
+ const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
+ if (vname == NULL) vname = "?";
+ luaG_runerror(L, "variable '%s' got a non-closable value", vname);
+ }
+}
+
+
+/*
+** Prepare and call a closing method.
+** If status is CLOSEKTOP, the call to the closing method will be pushed
+** at the top of the stack. Otherwise, values can be pushed right after
+** the 'level' of the upvalue being closed, as everything after that
+** won't be used again.
+*/
+static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
+ TValue *uv = s2v(level); /* value being closed */
+ TValue *errobj;
+ if (status == CLOSEKTOP)
+ errobj = &G(L)->nilvalue; /* error object is nil */
+ else { /* 'luaD_seterrorobj' will set top to level + 2 */
+ errobj = s2v(level + 1); /* error object goes after 'uv' */
+ luaD_seterrorobj(L, status, level + 1); /* set error object */
+ }
+ callclosemethod(L, uv, errobj, yy);
+}
+
+
+/*
+** Maximum value for deltas in 'tbclist', dependent on the type
+** of delta. (This macro assumes that an 'L' is in scope where it
+** is used.)
+*/
+#define MAXDELTA \
+ ((256ul << ((sizeof(L->stack.p->tbclist.delta) - 1) * 8)) - 1)
+
+
+/*
+** Insert a variable in the list of to-be-closed variables.
+*/
+void luaF_newtbcupval (lua_State *L, StkId level) {
+ lua_assert(level > L->tbclist.p);
+ if (l_isfalse(s2v(level)))
+ return; /* false doesn't need to be closed */
+ checkclosemth(L, level); /* value must have a close method */
+ while (cast_uint(level - L->tbclist.p) > MAXDELTA) {
+ L->tbclist.p += MAXDELTA; /* create a dummy node at maximum delta */
+ L->tbclist.p->tbclist.delta = 0;
+ }
+ level->tbclist.delta = cast(unsigned short, level - L->tbclist.p);
+ L->tbclist.p = level;
+}
+
+
+void luaF_unlinkupval (UpVal *uv) {
+ lua_assert(upisopen(uv));
+ *uv->u.open.previous = uv->u.open.next;
+ if (uv->u.open.next)
+ uv->u.open.next->u.open.previous = uv->u.open.previous;
+}
+
+
+/*
+** Close all upvalues up to the given stack level.
+*/
+void luaF_closeupval (lua_State *L, StkId level) {
+ UpVal *uv;
+ StkId upl; /* stack index pointed by 'uv' */
+ while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
+ TValue *slot = &uv->u.value; /* new position for value */
+ lua_assert(uplevel(uv) < L->top.p);
+ luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */
+ setobj(L, slot, uv->v.p); /* move value to upvalue slot */
+ uv->v.p = slot; /* now current value lives here */
+ if (!iswhite(uv)) { /* neither white nor dead? */
+ nw2black(uv); /* closed upvalues cannot be gray */
+ luaC_barrier(L, uv, slot);
+ }
+ }
+}
+
+
+/*
+** Remove first element from the tbclist plus its dummy nodes.
+*/
+static void poptbclist (lua_State *L) {
+ StkId tbc = L->tbclist.p;
+ lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */
+ tbc -= tbc->tbclist.delta;
+ while (tbc > L->stack.p && tbc->tbclist.delta == 0)
+ tbc -= MAXDELTA; /* remove dummy nodes */
+ L->tbclist.p = tbc;
+}
+
+
+/*
+** Close all upvalues and to-be-closed variables up to the given stack
+** level. Return restored 'level'.
+*/
+StkId luaF_close (lua_State *L, StkId level, int status, int yy) {
+ ptrdiff_t levelrel = savestack(L, level);
+ luaF_closeupval(L, level); /* first, close the upvalues */
+ while (L->tbclist.p >= level) { /* traverse tbc's down to that level */
+ StkId tbc = L->tbclist.p; /* get variable index */
+ poptbclist(L); /* remove it from list */
+ prepcallclosemth(L, tbc, status, yy); /* close variable */
+ level = restorestack(L, levelrel);
+ }
+ return level;
+}
+
+
+Proto *luaF_newproto (lua_State *L) {
+ GCObject *o = luaC_newobj(L, LUA_VPROTO, sizeof(Proto));
+ Proto *f = gco2p(o);
+ f->k = NULL;
+ f->sizek = 0;
+ f->p = NULL;
+ f->sizep = 0;
+ f->code = NULL;
+ f->sizecode = 0;
+ f->lineinfo = NULL;
+ f->sizelineinfo = 0;
+ f->abslineinfo = NULL;
+ f->sizeabslineinfo = 0;
+ f->upvalues = NULL;
+ f->sizeupvalues = 0;
+ f->numparams = 0;
+ f->is_vararg = 0;
+ f->maxstacksize = 0;
+ f->locvars = NULL;
+ f->sizelocvars = 0;
+ f->linedefined = 0;
+ f->lastlinedefined = 0;
+ f->source = NULL;
+ return f;
+}
+
+
+void luaF_freeproto (lua_State *L, Proto *f) {
+ luaM_freearray(L, f->code, f->sizecode);
+ luaM_freearray(L, f->p, f->sizep);
+ luaM_freearray(L, f->k, f->sizek);
+ luaM_freearray(L, f->lineinfo, f->sizelineinfo);
+ luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo);
+ luaM_freearray(L, f->locvars, f->sizelocvars);
+ luaM_freearray(L, f->upvalues, f->sizeupvalues);
+ luaM_free(L, f);
+}
+
+
+/*
+** Look for n-th local variable at line 'line' in function 'func'.
+** Returns NULL if not found.
+*/
+const char *luaF_getlocalname (const Proto *f, int local_number, int pc) {
+ int i;
+ for (i = 0; i<f->sizelocvars && f->locvars[i].startpc <= pc; i++) {
+ if (pc < f->locvars[i].endpc) { /* is variable active? */
+ local_number--;
+ if (local_number == 0)
+ return getstr(f->locvars[i].varname);
+ }
+ }
+ return NULL; /* not found */
+}
+
diff --git a/src/libs/3rdparty/lua/src/lfunc.h b/src/libs/3rdparty/lua/src/lfunc.h
new file mode 100644
index 0000000000..3be265efb5
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lfunc.h
@@ -0,0 +1,64 @@
+/*
+** $Id: lfunc.h $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lfunc_h
+#define lfunc_h
+
+
+#include "lobject.h"
+
+
+#define sizeCclosure(n) (cast_int(offsetof(CClosure, upvalue)) + \
+ cast_int(sizeof(TValue)) * (n))
+
+#define sizeLclosure(n) (cast_int(offsetof(LClosure, upvals)) + \
+ cast_int(sizeof(TValue *)) * (n))
+
+
+/* test whether thread is in 'twups' list */
+#define isintwups(L) (L->twups != L)
+
+
+/*
+** maximum number of upvalues in a closure (both C and Lua). (Value
+** must fit in a VM register.)
+*/
+#define MAXUPVAL 255
+
+
+#define upisopen(up) ((up)->v.p != &(up)->u.value)
+
+
+#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p))
+
+
+/*
+** maximum number of misses before giving up the cache of closures
+** in prototypes
+*/
+#define MAXMISS 10
+
+
+
+/* special status to close upvalues preserving the top of the stack */
+#define CLOSEKTOP (-1)
+
+
+LUAI_FUNC Proto *luaF_newproto (lua_State *L);
+LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nupvals);
+LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals);
+LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
+LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
+LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
+LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level);
+LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, int status, int yy);
+LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
+LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
+LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
+ int pc);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lgc.c b/src/libs/3rdparty/lua/src/lgc.c
new file mode 100644
index 0000000000..a3094ff571
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lgc.c
@@ -0,0 +1,1739 @@
+/*
+** $Id: lgc.c $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#define lgc_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+#include <stdio.h>
+#include <string.h>
+
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+/*
+** Maximum number of elements to sweep in each single step.
+** (Large enough to dissipate fixed overheads but small enough
+** to allow small steps for the collector.)
+*/
+#define GCSWEEPMAX 100
+
+/*
+** Maximum number of finalizers to call in each single step.
+*/
+#define GCFINMAX 10
+
+
+/*
+** Cost of calling one finalizer.
+*/
+#define GCFINALIZECOST 50
+
+
+/*
+** The equivalent, in bytes, of one unit of "work" (visiting a slot,
+** sweeping an object, etc.)
+*/
+#define WORK2MEM sizeof(TValue)
+
+
+/*
+** macro to adjust 'pause': 'pause' is actually used like
+** 'pause / PAUSEADJ' (value chosen by tests)
+*/
+#define PAUSEADJ 100
+
+
+/* mask with all color bits */
+#define maskcolors (bitmask(BLACKBIT) | WHITEBITS)
+
+/* mask with all GC bits */
+#define maskgcbits (maskcolors | AGEBITS)
+
+
+/* macro to erase all color bits then set only the current white bit */
+#define makewhite(g,x) \
+ (x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g)))
+
+/* make an object gray (neither white nor black) */
+#define set2gray(x) resetbits(x->marked, maskcolors)
+
+
+/* make an object black (coming from any color) */
+#define set2black(x) \
+ (x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT)))
+
+
+#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
+
+#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n)))
+
+
+/*
+** Protected access to objects in values
+*/
+#define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL)
+
+
+#define markvalue(g,o) { checkliveness(g->mainthread,o); \
+ if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); }
+
+#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); }
+
+#define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); }
+
+/*
+** mark an object that can be NULL (either because it is really optional,
+** or it was stripped as debug info, or inside an uncompleted structure)
+*/
+#define markobjectN(g,t) { if (t) markobject(g,t); }
+
+static void reallymarkobject (global_State *g, GCObject *o);
+static lu_mem atomic (lua_State *L);
+static void entersweep (lua_State *L);
+
+
+/*
+** {======================================================
+** Generic functions
+** =======================================================
+*/
+
+
+/*
+** one after last element in a hash array
+*/
+#define gnodelast(h) gnode(h, cast_sizet(sizenode(h)))
+
+
+static GCObject **getgclist (GCObject *o) {
+ switch (o->tt) {
+ case LUA_VTABLE: return &gco2t(o)->gclist;
+ case LUA_VLCL: return &gco2lcl(o)->gclist;
+ case LUA_VCCL: return &gco2ccl(o)->gclist;
+ case LUA_VTHREAD: return &gco2th(o)->gclist;
+ case LUA_VPROTO: return &gco2p(o)->gclist;
+ case LUA_VUSERDATA: {
+ Udata *u = gco2u(o);
+ lua_assert(u->nuvalue > 0);
+ return &u->gclist;
+ }
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+/*
+** Link a collectable object 'o' with a known type into the list 'p'.
+** (Must be a macro to access the 'gclist' field in different types.)
+*/
+#define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p))
+
+static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
+ lua_assert(!isgray(o)); /* cannot be in a gray list */
+ *pnext = *list;
+ *list = o;
+ set2gray(o); /* now it is */
+}
+
+
+/*
+** Link a generic collectable object 'o' into the list 'p'.
+*/
+#define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p))
+
+
+
+/*
+** Clear keys for empty entries in tables. If entry is empty, mark its
+** entry as dead. This allows the collection of the key, but keeps its
+** entry in the table: its removal could break a chain and could break
+** a table traversal. Other places never manipulate dead keys, because
+** its associated empty value is enough to signal that the entry is
+** logically empty.
+*/
+static void clearkey (Node *n) {
+ lua_assert(isempty(gval(n)));
+ if (keyiscollectable(n))
+ setdeadkey(n); /* unused key; remove it */
+}
+
+
+/*
+** tells whether a key or value can be cleared from a weak
+** table. Non-collectable objects are never removed from weak
+** tables. Strings behave as 'values', so are never removed too. for
+** other objects: if really collected, cannot keep them; for objects
+** being finalized, keep them in keys, but not in values
+*/
+static int iscleared (global_State *g, const GCObject *o) {
+ if (o == NULL) return 0; /* non-collectable value */
+ else if (novariant(o->tt) == LUA_TSTRING) {
+ markobject(g, o); /* strings are 'values', so are never weak */
+ return 0;
+ }
+ else return iswhite(o);
+}
+
+
+/*
+** Barrier that moves collector forward, that is, marks the white object
+** 'v' being pointed by the black object 'o'. In the generational
+** mode, 'v' must also become old, if 'o' is old; however, it cannot
+** be changed directly to OLD, because it may still point to non-old
+** objects. So, it is marked as OLD0. In the next cycle it will become
+** OLD1, and in the next it will finally become OLD (regular old). By
+** then, any object it points to will also be old. If called in the
+** incremental sweep phase, it clears the black object to white (sweep
+** it) to avoid other barrier calls for this same object. (That cannot
+** be done is generational mode, as its sweep does not distinguish
+** whites from deads.)
+*/
+void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
+ global_State *g = G(L);
+ lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
+ if (keepinvariant(g)) { /* must keep invariant? */
+ reallymarkobject(g, v); /* restore invariant */
+ if (isold(o)) {
+ lua_assert(!isold(v)); /* white object could not be old */
+ setage(v, G_OLD0); /* restore generational invariant */
+ }
+ }
+ else { /* sweep phase */
+ lua_assert(issweepphase(g));
+ if (g->gckind == KGC_INC) /* incremental mode? */
+ makewhite(g, o); /* mark 'o' as white to avoid other barriers */
+ }
+}
+
+
+/*
+** barrier that moves collector backward, that is, mark the black object
+** pointing to a white object as gray again.
+*/
+void luaC_barrierback_ (lua_State *L, GCObject *o) {
+ global_State *g = G(L);
+ lua_assert(isblack(o) && !isdead(g, o));
+ lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1));
+ if (getage(o) == G_TOUCHED2) /* already in gray list? */
+ set2gray(o); /* make it gray to become touched1 */
+ else /* link it in 'grayagain' and paint it gray */
+ linkobjgclist(o, g->grayagain);
+ if (isold(o)) /* generational mode? */
+ setage(o, G_TOUCHED1); /* touched in current cycle */
+}
+
+
+void luaC_fix (lua_State *L, GCObject *o) {
+ global_State *g = G(L);
+ lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */
+ set2gray(o); /* they will be gray forever */
+ setage(o, G_OLD); /* and old forever */
+ g->allgc = o->next; /* remove object from 'allgc' list */
+ o->next = g->fixedgc; /* link it to 'fixedgc' list */
+ g->fixedgc = o;
+}
+
+
+/*
+** create a new collectable object (with given type, size, and offset)
+** and link it to 'allgc' list.
+*/
+GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) {
+ global_State *g = G(L);
+ char *p = cast_charp(luaM_newobject(L, novariant(tt), sz));
+ GCObject *o = cast(GCObject *, p + offset);
+ o->marked = luaC_white(g);
+ o->tt = tt;
+ o->next = g->allgc;
+ g->allgc = o;
+ return o;
+}
+
+
+GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
+ return luaC_newobjdt(L, tt, sz, 0);
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** Mark functions
+** =======================================================
+*/
+
+
+/*
+** Mark an object. Userdata with no user values, strings, and closed
+** upvalues are visited and turned black here. Open upvalues are
+** already indirectly linked through their respective threads in the
+** 'twups' list, so they don't go to the gray list; nevertheless, they
+** are kept gray to avoid barriers, as their values will be revisited
+** by the thread or by 'remarkupvals'. Other objects are added to the
+** gray list to be visited (and turned black) later. Both userdata and
+** upvalues can call this function recursively, but this recursion goes
+** for at most two levels: An upvalue cannot refer to another upvalue
+** (only closures can), and a userdata's metatable must be a table.
+*/
+static void reallymarkobject (global_State *g, GCObject *o) {
+ switch (o->tt) {
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR: {
+ set2black(o); /* nothing to visit */
+ break;
+ }
+ case LUA_VUPVAL: {
+ UpVal *uv = gco2upv(o);
+ if (upisopen(uv))
+ set2gray(uv); /* open upvalues are kept gray */
+ else
+ set2black(uv); /* closed upvalues are visited here */
+ markvalue(g, uv->v.p); /* mark its content */
+ break;
+ }
+ case LUA_VUSERDATA: {
+ Udata *u = gco2u(o);
+ if (u->nuvalue == 0) { /* no user values? */
+ markobjectN(g, u->metatable); /* mark its metatable */
+ set2black(u); /* nothing else to mark */
+ break;
+ }
+ /* else... */
+ } /* FALLTHROUGH */
+ case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE:
+ case LUA_VTHREAD: case LUA_VPROTO: {
+ linkobjgclist(o, g->gray); /* to be visited later */
+ break;
+ }
+ default: lua_assert(0); break;
+ }
+}
+
+
+/*
+** mark metamethods for basic types
+*/
+static void markmt (global_State *g) {
+ int i;
+ for (i=0; i < LUA_NUMTAGS; i++)
+ markobjectN(g, g->mt[i]);
+}
+
+
+/*
+** mark all objects in list of being-finalized
+*/
+static lu_mem markbeingfnz (global_State *g) {
+ GCObject *o;
+ lu_mem count = 0;
+ for (o = g->tobefnz; o != NULL; o = o->next) {
+ count++;
+ markobject(g, o);
+ }
+ return count;
+}
+
+
+/*
+** For each non-marked thread, simulates a barrier between each open
+** upvalue and its value. (If the thread is collected, the value will be
+** assigned to the upvalue, but then it can be too late for the barrier
+** to act. The "barrier" does not need to check colors: A non-marked
+** thread must be young; upvalues cannot be older than their threads; so
+** any visited upvalue must be young too.) Also removes the thread from
+** the list, as it was already visited. Removes also threads with no
+** upvalues, as they have nothing to be checked. (If the thread gets an
+** upvalue later, it will be linked in the list again.)
+*/
+static int remarkupvals (global_State *g) {
+ lua_State *thread;
+ lua_State **p = &g->twups;
+ int work = 0; /* estimate of how much work was done here */
+ while ((thread = *p) != NULL) {
+ work++;
+ if (!iswhite(thread) && thread->openupval != NULL)
+ p = &thread->twups; /* keep marked thread with upvalues in the list */
+ else { /* thread is not marked or without upvalues */
+ UpVal *uv;
+ lua_assert(!isold(thread) || thread->openupval == NULL);
+ *p = thread->twups; /* remove thread from the list */
+ thread->twups = thread; /* mark that it is out of list */
+ for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) {
+ lua_assert(getage(uv) <= getage(thread));
+ work++;
+ if (!iswhite(uv)) { /* upvalue already visited? */
+ lua_assert(upisopen(uv) && isgray(uv));
+ markvalue(g, uv->v.p); /* mark its value */
+ }
+ }
+ }
+ }
+ return work;
+}
+
+
+static void cleargraylists (global_State *g) {
+ g->gray = g->grayagain = NULL;
+ g->weak = g->allweak = g->ephemeron = NULL;
+}
+
+
+/*
+** mark root set and reset all gray lists, to start a new collection
+*/
+static void restartcollection (global_State *g) {
+ cleargraylists(g);
+ markobject(g, g->mainthread);
+ markvalue(g, &g->l_registry);
+ markmt(g);
+ markbeingfnz(g); /* mark any finalizing object left from previous cycle */
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Traverse functions
+** =======================================================
+*/
+
+
+/*
+** Check whether object 'o' should be kept in the 'grayagain' list for
+** post-processing by 'correctgraylist'. (It could put all old objects
+** in the list and leave all the work to 'correctgraylist', but it is
+** more efficient to avoid adding elements that will be removed.) Only
+** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go
+** back to a gray list, but then it must become OLD. (That is what
+** 'correctgraylist' does when it finds a TOUCHED2 object.)
+*/
+static void genlink (global_State *g, GCObject *o) {
+ lua_assert(isblack(o));
+ if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */
+ linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */
+ } /* everything else do not need to be linked back */
+ else if (getage(o) == G_TOUCHED2)
+ changeage(o, G_TOUCHED2, G_OLD); /* advance age */
+}
+
+
+/*
+** Traverse a table with weak values and link it to proper list. During
+** propagate phase, keep it in 'grayagain' list, to be revisited in the
+** atomic phase. In the atomic phase, if table has any white value,
+** put it in 'weak' list, to be cleared.
+*/
+static void traverseweakvalue (global_State *g, Table *h) {
+ Node *n, *limit = gnodelast(h);
+ /* if there is array part, assume it may have white values (it is not
+ worth traversing it now just to check) */
+ int hasclears = (h->alimit > 0);
+ for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
+ if (isempty(gval(n))) /* entry is empty? */
+ clearkey(n); /* clear its key */
+ else {
+ lua_assert(!keyisnil(n));
+ markkey(g, n);
+ if (!hasclears && iscleared(g, gcvalueN(gval(n)))) /* a white value? */
+ hasclears = 1; /* table will have to be cleared */
+ }
+ }
+ if (g->gcstate == GCSatomic && hasclears)
+ linkgclist(h, g->weak); /* has to be cleared later */
+ else
+ linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */
+}
+
+
+/*
+** Traverse an ephemeron table and link it to proper list. Returns true
+** iff any object was marked during this traversal (which implies that
+** convergence has to continue). During propagation phase, keep table
+** in 'grayagain' list, to be visited again in the atomic phase. In
+** the atomic phase, if table has any white->white entry, it has to
+** be revisited during ephemeron convergence (as that key may turn
+** black). Otherwise, if it has any white key, table has to be cleared
+** (in the atomic phase). In generational mode, some tables
+** must be kept in some gray list for post-processing; this is done
+** by 'genlink'.
+*/
+static int traverseephemeron (global_State *g, Table *h, int inv) {
+ int marked = 0; /* true if an object is marked in this traversal */
+ int hasclears = 0; /* true if table has white keys */
+ int hasww = 0; /* true if table has entry "white-key -> white-value" */
+ unsigned int i;
+ unsigned int asize = luaH_realasize(h);
+ unsigned int nsize = sizenode(h);
+ /* traverse array part */
+ for (i = 0; i < asize; i++) {
+ if (valiswhite(&h->array[i])) {
+ marked = 1;
+ reallymarkobject(g, gcvalue(&h->array[i]));
+ }
+ }
+ /* traverse hash part; if 'inv', traverse descending
+ (see 'convergeephemerons') */
+ for (i = 0; i < nsize; i++) {
+ Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i);
+ if (isempty(gval(n))) /* entry is empty? */
+ clearkey(n); /* clear its key */
+ else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */
+ hasclears = 1; /* table must be cleared */
+ if (valiswhite(gval(n))) /* value not marked yet? */
+ hasww = 1; /* white-white entry */
+ }
+ else if (valiswhite(gval(n))) { /* value not marked yet? */
+ marked = 1;
+ reallymarkobject(g, gcvalue(gval(n))); /* mark it now */
+ }
+ }
+ /* link table into proper list */
+ if (g->gcstate == GCSpropagate)
+ linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */
+ else if (hasww) /* table has white->white entries? */
+ linkgclist(h, g->ephemeron); /* have to propagate again */
+ else if (hasclears) /* table has white keys? */
+ linkgclist(h, g->allweak); /* may have to clean white keys */
+ else
+ genlink(g, obj2gco(h)); /* check whether collector still needs to see it */
+ return marked;
+}
+
+
+static void traversestrongtable (global_State *g, Table *h) {
+ Node *n, *limit = gnodelast(h);
+ unsigned int i;
+ unsigned int asize = luaH_realasize(h);
+ for (i = 0; i < asize; i++) /* traverse array part */
+ markvalue(g, &h->array[i]);
+ for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
+ if (isempty(gval(n))) /* entry is empty? */
+ clearkey(n); /* clear its key */
+ else {
+ lua_assert(!keyisnil(n));
+ markkey(g, n);
+ markvalue(g, gval(n));
+ }
+ }
+ genlink(g, obj2gco(h));
+}
+
+
+static lu_mem traversetable (global_State *g, Table *h) {
+ const char *weakkey, *weakvalue;
+ const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
+ markobjectN(g, h->metatable);
+ if (mode && ttisstring(mode) && /* is there a weak mode? */
+ (cast_void(weakkey = strchr(svalue(mode), 'k')),
+ cast_void(weakvalue = strchr(svalue(mode), 'v')),
+ (weakkey || weakvalue))) { /* is really weak? */
+ if (!weakkey) /* strong keys? */
+ traverseweakvalue(g, h);
+ else if (!weakvalue) /* strong values? */
+ traverseephemeron(g, h, 0);
+ else /* all weak */
+ linkgclist(h, g->allweak); /* nothing to traverse now */
+ }
+ else /* not weak */
+ traversestrongtable(g, h);
+ return 1 + h->alimit + 2 * allocsizenode(h);
+}
+
+
+static int traverseudata (global_State *g, Udata *u) {
+ int i;
+ markobjectN(g, u->metatable); /* mark its metatable */
+ for (i = 0; i < u->nuvalue; i++)
+ markvalue(g, &u->uv[i].uv);
+ genlink(g, obj2gco(u));
+ return 1 + u->nuvalue;
+}
+
+
+/*
+** Traverse a prototype. (While a prototype is being build, its
+** arrays can be larger than needed; the extra slots are filled with
+** NULL, so the use of 'markobjectN')
+*/
+static int traverseproto (global_State *g, Proto *f) {
+ int i;
+ markobjectN(g, f->source);
+ for (i = 0; i < f->sizek; i++) /* mark literals */
+ markvalue(g, &f->k[i]);
+ for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */
+ markobjectN(g, f->upvalues[i].name);
+ for (i = 0; i < f->sizep; i++) /* mark nested protos */
+ markobjectN(g, f->p[i]);
+ for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */
+ markobjectN(g, f->locvars[i].varname);
+ return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars;
+}
+
+
+static int traverseCclosure (global_State *g, CClosure *cl) {
+ int i;
+ for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */
+ markvalue(g, &cl->upvalue[i]);
+ return 1 + cl->nupvalues;
+}
+
+/*
+** Traverse a Lua closure, marking its prototype and its upvalues.
+** (Both can be NULL while closure is being created.)
+*/
+static int traverseLclosure (global_State *g, LClosure *cl) {
+ int i;
+ markobjectN(g, cl->p); /* mark its prototype */
+ for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */
+ UpVal *uv = cl->upvals[i];
+ markobjectN(g, uv); /* mark upvalue */
+ }
+ return 1 + cl->nupvalues;
+}
+
+
+/*
+** Traverse a thread, marking the elements in the stack up to its top
+** and cleaning the rest of the stack in the final traversal. That
+** ensures that the entire stack have valid (non-dead) objects.
+** Threads have no barriers. In gen. mode, old threads must be visited
+** at every cycle, because they might point to young objects. In inc.
+** mode, the thread can still be modified before the end of the cycle,
+** and therefore it must be visited again in the atomic phase. To ensure
+** these visits, threads must return to a gray list if they are not new
+** (which can only happen in generational mode) or if the traverse is in
+** the propagate phase (which can only happen in incremental mode).
+*/
+static int traversethread (global_State *g, lua_State *th) {
+ UpVal *uv;
+ StkId o = th->stack.p;
+ if (isold(th) || g->gcstate == GCSpropagate)
+ linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
+ if (o == NULL)
+ return 1; /* stack not completely built yet */
+ lua_assert(g->gcstate == GCSatomic ||
+ th->openupval == NULL || isintwups(th));
+ for (; o < th->top.p; o++) /* mark live elements in the stack */
+ markvalue(g, s2v(o));
+ for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
+ markobject(g, uv); /* open upvalues cannot be collected */
+ if (g->gcstate == GCSatomic) { /* final traversal? */
+ for (; o < th->stack_last.p + EXTRA_STACK; o++)
+ setnilvalue(s2v(o)); /* clear dead stack slice */
+ /* 'remarkupvals' may have removed thread from 'twups' list */
+ if (!isintwups(th) && th->openupval != NULL) {
+ th->twups = g->twups; /* link it back to the list */
+ g->twups = th;
+ }
+ }
+ else if (!g->gcemergency)
+ luaD_shrinkstack(th); /* do not change stack in emergency cycle */
+ return 1 + stacksize(th);
+}
+
+
+/*
+** traverse one gray object, turning it to black.
+*/
+static lu_mem propagatemark (global_State *g) {
+ GCObject *o = g->gray;
+ nw2black(o);
+ g->gray = *getgclist(o); /* remove from 'gray' list */
+ switch (o->tt) {
+ case LUA_VTABLE: return traversetable(g, gco2t(o));
+ case LUA_VUSERDATA: return traverseudata(g, gco2u(o));
+ case LUA_VLCL: return traverseLclosure(g, gco2lcl(o));
+ case LUA_VCCL: return traverseCclosure(g, gco2ccl(o));
+ case LUA_VPROTO: return traverseproto(g, gco2p(o));
+ case LUA_VTHREAD: return traversethread(g, gco2th(o));
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+static lu_mem propagateall (global_State *g) {
+ lu_mem tot = 0;
+ while (g->gray)
+ tot += propagatemark(g);
+ return tot;
+}
+
+
+/*
+** Traverse all ephemeron tables propagating marks from keys to values.
+** Repeat until it converges, that is, nothing new is marked. 'dir'
+** inverts the direction of the traversals, trying to speed up
+** convergence on chains in the same table.
+**
+*/
+static void convergeephemerons (global_State *g) {
+ int changed;
+ int dir = 0;
+ do {
+ GCObject *w;
+ GCObject *next = g->ephemeron; /* get ephemeron list */
+ g->ephemeron = NULL; /* tables may return to this list when traversed */
+ changed = 0;
+ while ((w = next) != NULL) { /* for each ephemeron table */
+ Table *h = gco2t(w);
+ next = h->gclist; /* list is rebuilt during loop */
+ nw2black(h); /* out of the list (for now) */
+ if (traverseephemeron(g, h, dir)) { /* marked some value? */
+ propagateall(g); /* propagate changes */
+ changed = 1; /* will have to revisit all ephemeron tables */
+ }
+ }
+ dir = !dir; /* invert direction next time */
+ } while (changed); /* repeat until no more changes */
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Sweep Functions
+** =======================================================
+*/
+
+
+/*
+** clear entries with unmarked keys from all weaktables in list 'l'
+*/
+static void clearbykeys (global_State *g, GCObject *l) {
+ for (; l; l = gco2t(l)->gclist) {
+ Table *h = gco2t(l);
+ Node *limit = gnodelast(h);
+ Node *n;
+ for (n = gnode(h, 0); n < limit; n++) {
+ if (iscleared(g, gckeyN(n))) /* unmarked key? */
+ setempty(gval(n)); /* remove entry */
+ if (isempty(gval(n))) /* is entry empty? */
+ clearkey(n); /* clear its key */
+ }
+ }
+}
+
+
+/*
+** clear entries with unmarked values from all weaktables in list 'l' up
+** to element 'f'
+*/
+static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) {
+ for (; l != f; l = gco2t(l)->gclist) {
+ Table *h = gco2t(l);
+ Node *n, *limit = gnodelast(h);
+ unsigned int i;
+ unsigned int asize = luaH_realasize(h);
+ for (i = 0; i < asize; i++) {
+ TValue *o = &h->array[i];
+ if (iscleared(g, gcvalueN(o))) /* value was collected? */
+ setempty(o); /* remove entry */
+ }
+ for (n = gnode(h, 0); n < limit; n++) {
+ if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */
+ setempty(gval(n)); /* remove entry */
+ if (isempty(gval(n))) /* is entry empty? */
+ clearkey(n); /* clear its key */
+ }
+ }
+}
+
+
+static void freeupval (lua_State *L, UpVal *uv) {
+ if (upisopen(uv))
+ luaF_unlinkupval(uv);
+ luaM_free(L, uv);
+}
+
+
+static void freeobj (lua_State *L, GCObject *o) {
+ switch (o->tt) {
+ case LUA_VPROTO:
+ luaF_freeproto(L, gco2p(o));
+ break;
+ case LUA_VUPVAL:
+ freeupval(L, gco2upv(o));
+ break;
+ case LUA_VLCL: {
+ LClosure *cl = gco2lcl(o);
+ luaM_freemem(L, cl, sizeLclosure(cl->nupvalues));
+ break;
+ }
+ case LUA_VCCL: {
+ CClosure *cl = gco2ccl(o);
+ luaM_freemem(L, cl, sizeCclosure(cl->nupvalues));
+ break;
+ }
+ case LUA_VTABLE:
+ luaH_free(L, gco2t(o));
+ break;
+ case LUA_VTHREAD:
+ luaE_freethread(L, gco2th(o));
+ break;
+ case LUA_VUSERDATA: {
+ Udata *u = gco2u(o);
+ luaM_freemem(L, o, sizeudata(u->nuvalue, u->len));
+ break;
+ }
+ case LUA_VSHRSTR: {
+ TString *ts = gco2ts(o);
+ luaS_remove(L, ts); /* remove it from hash table */
+ luaM_freemem(L, ts, sizelstring(ts->shrlen));
+ break;
+ }
+ case LUA_VLNGSTR: {
+ TString *ts = gco2ts(o);
+ luaM_freemem(L, ts, sizelstring(ts->u.lnglen));
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+
+/*
+** sweep at most 'countin' elements from a list of GCObjects erasing dead
+** objects, where a dead object is one marked with the old (non current)
+** white; change all non-dead objects back to white, preparing for next
+** collection cycle. Return where to continue the traversal or NULL if
+** list is finished. ('*countout' gets the number of elements traversed.)
+*/
+static GCObject **sweeplist (lua_State *L, GCObject **p, int countin,
+ int *countout) {
+ global_State *g = G(L);
+ int ow = otherwhite(g);
+ int i;
+ int white = luaC_white(g); /* current white */
+ for (i = 0; *p != NULL && i < countin; i++) {
+ GCObject *curr = *p;
+ int marked = curr->marked;
+ if (isdeadm(ow, marked)) { /* is 'curr' dead? */
+ *p = curr->next; /* remove 'curr' from list */
+ freeobj(L, curr); /* erase 'curr' */
+ }
+ else { /* change mark to 'white' */
+ curr->marked = cast_byte((marked & ~maskgcbits) | white);
+ p = &curr->next; /* go to next element */
+ }
+ }
+ if (countout)
+ *countout = i; /* number of elements traversed */
+ return (*p == NULL) ? NULL : p;
+}
+
+
+/*
+** sweep a list until a live object (or end of list)
+*/
+static GCObject **sweeptolive (lua_State *L, GCObject **p) {
+ GCObject **old = p;
+ do {
+ p = sweeplist(L, p, 1, NULL);
+ } while (p == old);
+ return p;
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Finalization
+** =======================================================
+*/
+
+/*
+** If possible, shrink string table.
+*/
+static void checkSizes (lua_State *L, global_State *g) {
+ if (!g->gcemergency) {
+ if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */
+ l_mem olddebt = g->GCdebt;
+ luaS_resize(L, g->strt.size / 2);
+ g->GCestimate += g->GCdebt - olddebt; /* correct estimate */
+ }
+ }
+}
+
+
+/*
+** Get the next udata to be finalized from the 'tobefnz' list, and
+** link it back into the 'allgc' list.
+*/
+static GCObject *udata2finalize (global_State *g) {
+ GCObject *o = g->tobefnz; /* get first element */
+ lua_assert(tofinalize(o));
+ g->tobefnz = o->next; /* remove it from 'tobefnz' list */
+ o->next = g->allgc; /* return it to 'allgc' list */
+ g->allgc = o;
+ resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */
+ if (issweepphase(g))
+ makewhite(g, o); /* "sweep" object */
+ else if (getage(o) == G_OLD1)
+ g->firstold1 = o; /* it is the first OLD1 object in the list */
+ return o;
+}
+
+
+static void dothecall (lua_State *L, void *ud) {
+ UNUSED(ud);
+ luaD_callnoyield(L, L->top.p - 2, 0);
+}
+
+
+static void GCTM (lua_State *L) {
+ global_State *g = G(L);
+ const TValue *tm;
+ TValue v;
+ lua_assert(!g->gcemergency);
+ setgcovalue(L, &v, udata2finalize(g));
+ tm = luaT_gettmbyobj(L, &v, TM_GC);
+ if (!notm(tm)) { /* is there a finalizer? */
+ int status;
+ lu_byte oldah = L->allowhook;
+ int oldgcstp = g->gcstp;
+ g->gcstp |= GCSTPGC; /* avoid GC steps */
+ L->allowhook = 0; /* stop debug hooks during GC metamethod */
+ setobj2s(L, L->top.p++, tm); /* push finalizer... */
+ setobj2s(L, L->top.p++, &v); /* ... and its argument */
+ L->ci->callstatus |= CIST_FIN; /* will run a finalizer */
+ status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0);
+ L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
+ L->allowhook = oldah; /* restore hooks */
+ g->gcstp = oldgcstp; /* restore state */
+ if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */
+ luaE_warnerror(L, "__gc");
+ L->top.p--; /* pops error object */
+ }
+ }
+}
+
+
+/*
+** Call a few finalizers
+*/
+static int runafewfinalizers (lua_State *L, int n) {
+ global_State *g = G(L);
+ int i;
+ for (i = 0; i < n && g->tobefnz; i++)
+ GCTM(L); /* call one finalizer */
+ return i;
+}
+
+
+/*
+** call all pending finalizers
+*/
+static void callallpendingfinalizers (lua_State *L) {
+ global_State *g = G(L);
+ while (g->tobefnz)
+ GCTM(L);
+}
+
+
+/*
+** find last 'next' field in list 'p' list (to add elements in its end)
+*/
+static GCObject **findlast (GCObject **p) {
+ while (*p != NULL)
+ p = &(*p)->next;
+ return p;
+}
+
+
+/*
+** Move all unreachable objects (or 'all' objects) that need
+** finalization from list 'finobj' to list 'tobefnz' (to be finalized).
+** (Note that objects after 'finobjold1' cannot be white, so they
+** don't need to be traversed. In incremental mode, 'finobjold1' is NULL,
+** so the whole list is traversed.)
+*/
+static void separatetobefnz (global_State *g, int all) {
+ GCObject *curr;
+ GCObject **p = &g->finobj;
+ GCObject **lastnext = findlast(&g->tobefnz);
+ while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */
+ lua_assert(tofinalize(curr));
+ if (!(iswhite(curr) || all)) /* not being collected? */
+ p = &curr->next; /* don't bother with it */
+ else {
+ if (curr == g->finobjsur) /* removing 'finobjsur'? */
+ g->finobjsur = curr->next; /* correct it */
+ *p = curr->next; /* remove 'curr' from 'finobj' list */
+ curr->next = *lastnext; /* link at the end of 'tobefnz' list */
+ *lastnext = curr;
+ lastnext = &curr->next;
+ }
+ }
+}
+
+
+/*
+** If pointer 'p' points to 'o', move it to the next element.
+*/
+static void checkpointer (GCObject **p, GCObject *o) {
+ if (o == *p)
+ *p = o->next;
+}
+
+
+/*
+** Correct pointers to objects inside 'allgc' list when
+** object 'o' is being removed from the list.
+*/
+static void correctpointers (global_State *g, GCObject *o) {
+ checkpointer(&g->survival, o);
+ checkpointer(&g->old1, o);
+ checkpointer(&g->reallyold, o);
+ checkpointer(&g->firstold1, o);
+}
+
+
+/*
+** if object 'o' has a finalizer, remove it from 'allgc' list (must
+** search the list to find it) and link it in 'finobj' list.
+*/
+void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
+ global_State *g = G(L);
+ if (tofinalize(o) || /* obj. is already marked... */
+ gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */
+ (g->gcstp & GCSTPCLS)) /* or closing state? */
+ return; /* nothing to be done */
+ else { /* move 'o' to 'finobj' list */
+ GCObject **p;
+ if (issweepphase(g)) {
+ makewhite(g, o); /* "sweep" object 'o' */
+ if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */
+ g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */
+ }
+ else
+ correctpointers(g, o);
+ /* search for pointer pointing to 'o' */
+ for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ }
+ *p = o->next; /* remove 'o' from 'allgc' list */
+ o->next = g->finobj; /* link it in 'finobj' list */
+ g->finobj = o;
+ l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */
+ }
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Generational Collector
+** =======================================================
+*/
+
+
+/*
+** Set the "time" to wait before starting a new GC cycle; cycle will
+** start when memory use hits the threshold of ('estimate' * pause /
+** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero,
+** because Lua cannot even start with less than PAUSEADJ bytes).
+*/
+static void setpause (global_State *g) {
+ l_mem threshold, debt;
+ int pause = getgcparam(g->gcpause);
+ l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */
+ lua_assert(estimate > 0);
+ threshold = (pause < MAX_LMEM / estimate) /* overflow? */
+ ? estimate * pause /* no overflow */
+ : MAX_LMEM; /* overflow; truncate to maximum */
+ debt = gettotalbytes(g) - threshold;
+ if (debt > 0) debt = 0;
+ luaE_setdebt(g, debt);
+}
+
+
+/*
+** Sweep a list of objects to enter generational mode. Deletes dead
+** objects and turns the non dead to old. All non-dead threads---which
+** are now old---must be in a gray list. Everything else is not in a
+** gray list. Open upvalues are also kept gray.
+*/
+static void sweep2old (lua_State *L, GCObject **p) {
+ GCObject *curr;
+ global_State *g = G(L);
+ while ((curr = *p) != NULL) {
+ if (iswhite(curr)) { /* is 'curr' dead? */
+ lua_assert(isdead(g, curr));
+ *p = curr->next; /* remove 'curr' from list */
+ freeobj(L, curr); /* erase 'curr' */
+ }
+ else { /* all surviving objects become old */
+ setage(curr, G_OLD);
+ if (curr->tt == LUA_VTHREAD) { /* threads must be watched */
+ lua_State *th = gco2th(curr);
+ linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
+ }
+ else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr)))
+ set2gray(curr); /* open upvalues are always gray */
+ else /* everything else is black */
+ nw2black(curr);
+ p = &curr->next; /* go to next element */
+ }
+ }
+}
+
+
+/*
+** Sweep for generational mode. Delete dead objects. (Because the
+** collection is not incremental, there are no "new white" objects
+** during the sweep. So, any white object must be dead.) For
+** non-dead objects, advance their ages and clear the color of
+** new objects. (Old objects keep their colors.)
+** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced
+** here, because these old-generation objects are usually not swept
+** here. They will all be advanced in 'correctgraylist'. That function
+** will also remove objects turned white here from any gray list.
+*/
+static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
+ GCObject *limit, GCObject **pfirstold1) {
+ static const lu_byte nextage[] = {
+ G_SURVIVAL, /* from G_NEW */
+ G_OLD1, /* from G_SURVIVAL */
+ G_OLD1, /* from G_OLD0 */
+ G_OLD, /* from G_OLD1 */
+ G_OLD, /* from G_OLD (do not change) */
+ G_TOUCHED1, /* from G_TOUCHED1 (do not change) */
+ G_TOUCHED2 /* from G_TOUCHED2 (do not change) */
+ };
+ int white = luaC_white(g);
+ GCObject *curr;
+ while ((curr = *p) != limit) {
+ if (iswhite(curr)) { /* is 'curr' dead? */
+ lua_assert(!isold(curr) && isdead(g, curr));
+ *p = curr->next; /* remove 'curr' from list */
+ freeobj(L, curr); /* erase 'curr' */
+ }
+ else { /* correct mark and age */
+ if (getage(curr) == G_NEW) { /* new objects go back to white */
+ int marked = curr->marked & ~maskgcbits; /* erase GC bits */
+ curr->marked = cast_byte(marked | G_SURVIVAL | white);
+ }
+ else { /* all other objects will be old, and so keep their color */
+ setage(curr, nextage[getage(curr)]);
+ if (getage(curr) == G_OLD1 && *pfirstold1 == NULL)
+ *pfirstold1 = curr; /* first OLD1 object in the list */
+ }
+ p = &curr->next; /* go to next element */
+ }
+ }
+ return p;
+}
+
+
+/*
+** Traverse a list making all its elements white and clearing their
+** age. In incremental mode, all objects are 'new' all the time,
+** except for fixed strings (which are always old).
+*/
+static void whitelist (global_State *g, GCObject *p) {
+ int white = luaC_white(g);
+ for (; p != NULL; p = p->next)
+ p->marked = cast_byte((p->marked & ~maskgcbits) | white);
+}
+
+
+/*
+** Correct a list of gray objects. Return pointer to where rest of the
+** list should be linked.
+** Because this correction is done after sweeping, young objects might
+** be turned white and still be in the list. They are only removed.
+** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list;
+** Non-white threads also remain on the list; 'TOUCHED2' objects become
+** regular old; they and anything else are removed from the list.
+*/
+static GCObject **correctgraylist (GCObject **p) {
+ GCObject *curr;
+ while ((curr = *p) != NULL) {
+ GCObject **next = getgclist(curr);
+ if (iswhite(curr))
+ goto remove; /* remove all white objects */
+ else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */
+ lua_assert(isgray(curr));
+ nw2black(curr); /* make it black, for next barrier */
+ changeage(curr, G_TOUCHED1, G_TOUCHED2);
+ goto remain; /* keep it in the list and go to next element */
+ }
+ else if (curr->tt == LUA_VTHREAD) {
+ lua_assert(isgray(curr));
+ goto remain; /* keep non-white threads on the list */
+ }
+ else { /* everything else is removed */
+ lua_assert(isold(curr)); /* young objects should be white here */
+ if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */
+ changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */
+ nw2black(curr); /* make object black (to be removed) */
+ goto remove;
+ }
+ remove: *p = *next; continue;
+ remain: p = next; continue;
+ }
+ return p;
+}
+
+
+/*
+** Correct all gray lists, coalescing them into 'grayagain'.
+*/
+static void correctgraylists (global_State *g) {
+ GCObject **list = correctgraylist(&g->grayagain);
+ *list = g->weak; g->weak = NULL;
+ list = correctgraylist(list);
+ *list = g->allweak; g->allweak = NULL;
+ list = correctgraylist(list);
+ *list = g->ephemeron; g->ephemeron = NULL;
+ correctgraylist(list);
+}
+
+
+/*
+** Mark black 'OLD1' objects when starting a new young collection.
+** Gray objects are already in some gray list, and so will be visited
+** in the atomic step.
+*/
+static void markold (global_State *g, GCObject *from, GCObject *to) {
+ GCObject *p;
+ for (p = from; p != to; p = p->next) {
+ if (getage(p) == G_OLD1) {
+ lua_assert(!iswhite(p));
+ changeage(p, G_OLD1, G_OLD); /* now they are old */
+ if (isblack(p))
+ reallymarkobject(g, p);
+ }
+ }
+}
+
+
+/*
+** Finish a young-generation collection.
+*/
+static void finishgencycle (lua_State *L, global_State *g) {
+ correctgraylists(g);
+ checkSizes(L, g);
+ g->gcstate = GCSpropagate; /* skip restart */
+ if (!g->gcemergency)
+ callallpendingfinalizers(L);
+}
+
+
+/*
+** Does a young collection. First, mark 'OLD1' objects. Then does the
+** atomic step. Then, sweep all lists and advance pointers. Finally,
+** finish the collection.
+*/
+static void youngcollection (lua_State *L, global_State *g) {
+ GCObject **psurvival; /* to point to first non-dead survival object */
+ GCObject *dummy; /* dummy out parameter to 'sweepgen' */
+ lua_assert(g->gcstate == GCSpropagate);
+ if (g->firstold1) { /* are there regular OLD1 objects? */
+ markold(g, g->firstold1, g->reallyold); /* mark them */
+ g->firstold1 = NULL; /* no more OLD1 objects (for now) */
+ }
+ markold(g, g->finobj, g->finobjrold);
+ markold(g, g->tobefnz, NULL);
+ atomic(L);
+
+ /* sweep nursery and get a pointer to its last live element */
+ g->gcstate = GCSswpallgc;
+ psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1);
+ /* sweep 'survival' */
+ sweepgen(L, g, psurvival, g->old1, &g->firstold1);
+ g->reallyold = g->old1;
+ g->old1 = *psurvival; /* 'survival' survivals are old now */
+ g->survival = g->allgc; /* all news are survivals */
+
+ /* repeat for 'finobj' lists */
+ dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */
+ psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy);
+ /* sweep 'survival' */
+ sweepgen(L, g, psurvival, g->finobjold1, &dummy);
+ g->finobjrold = g->finobjold1;
+ g->finobjold1 = *psurvival; /* 'survival' survivals are old now */
+ g->finobjsur = g->finobj; /* all news are survivals */
+
+ sweepgen(L, g, &g->tobefnz, NULL, &dummy);
+ finishgencycle(L, g);
+}
+
+
+/*
+** Clears all gray lists, sweeps objects, and prepare sublists to enter
+** generational mode. The sweeps remove dead objects and turn all
+** surviving objects to old. Threads go back to 'grayagain'; everything
+** else is turned black (not in any gray list).
+*/
+static void atomic2gen (lua_State *L, global_State *g) {
+ cleargraylists(g);
+ /* sweep all elements making them old */
+ g->gcstate = GCSswpallgc;
+ sweep2old(L, &g->allgc);
+ /* everything alive now is old */
+ g->reallyold = g->old1 = g->survival = g->allgc;
+ g->firstold1 = NULL; /* there are no OLD1 objects anywhere */
+
+ /* repeat for 'finobj' lists */
+ sweep2old(L, &g->finobj);
+ g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj;
+
+ sweep2old(L, &g->tobefnz);
+
+ g->gckind = KGC_GEN;
+ g->lastatomic = 0;
+ g->GCestimate = gettotalbytes(g); /* base for memory control */
+ finishgencycle(L, g);
+}
+
+
+/*
+** Set debt for the next minor collection, which will happen when
+** memory grows 'genminormul'%.
+*/
+static void setminordebt (global_State *g) {
+ luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul));
+}
+
+
+/*
+** Enter generational mode. Must go until the end of an atomic cycle
+** to ensure that all objects are correctly marked and weak tables
+** are cleared. Then, turn all objects into old and finishes the
+** collection.
+*/
+static lu_mem entergen (lua_State *L, global_State *g) {
+ lu_mem numobjs;
+ luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */
+ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
+ numobjs = atomic(L); /* propagates all and then do the atomic stuff */
+ atomic2gen(L, g);
+ setminordebt(g); /* set debt assuming next cycle will be minor */
+ return numobjs;
+}
+
+
+/*
+** Enter incremental mode. Turn all objects white, make all
+** intermediate lists point to NULL (to avoid invalid pointers),
+** and go to the pause state.
+*/
+static void enterinc (global_State *g) {
+ whitelist(g, g->allgc);
+ g->reallyold = g->old1 = g->survival = NULL;
+ whitelist(g, g->finobj);
+ whitelist(g, g->tobefnz);
+ g->finobjrold = g->finobjold1 = g->finobjsur = NULL;
+ g->gcstate = GCSpause;
+ g->gckind = KGC_INC;
+ g->lastatomic = 0;
+}
+
+
+/*
+** Change collector mode to 'newmode'.
+*/
+void luaC_changemode (lua_State *L, int newmode) {
+ global_State *g = G(L);
+ if (newmode != g->gckind) {
+ if (newmode == KGC_GEN) /* entering generational mode? */
+ entergen(L, g);
+ else
+ enterinc(g); /* entering incremental mode */
+ }
+ g->lastatomic = 0;
+}
+
+
+/*
+** Does a full collection in generational mode.
+*/
+static lu_mem fullgen (lua_State *L, global_State *g) {
+ enterinc(g);
+ return entergen(L, g);
+}
+
+
+/*
+** Does a major collection after last collection was a "bad collection".
+**
+** When the program is building a big structure, it allocates lots of
+** memory but generates very little garbage. In those scenarios,
+** the generational mode just wastes time doing small collections, and
+** major collections are frequently what we call a "bad collection", a
+** collection that frees too few objects. To avoid the cost of switching
+** between generational mode and the incremental mode needed for full
+** (major) collections, the collector tries to stay in incremental mode
+** after a bad collection, and to switch back to generational mode only
+** after a "good" collection (one that traverses less than 9/8 objects
+** of the previous one).
+** The collector must choose whether to stay in incremental mode or to
+** switch back to generational mode before sweeping. At this point, it
+** does not know the real memory in use, so it cannot use memory to
+** decide whether to return to generational mode. Instead, it uses the
+** number of objects traversed (returned by 'atomic') as a proxy. The
+** field 'g->lastatomic' keeps this count from the last collection.
+** ('g->lastatomic != 0' also means that the last collection was bad.)
+*/
+static void stepgenfull (lua_State *L, global_State *g) {
+ lu_mem newatomic; /* count of traversed objects */
+ lu_mem lastatomic = g->lastatomic; /* count from last collection */
+ if (g->gckind == KGC_GEN) /* still in generational mode? */
+ enterinc(g); /* enter incremental mode */
+ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
+ newatomic = atomic(L); /* mark everybody */
+ if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */
+ atomic2gen(L, g); /* return to generational mode */
+ setminordebt(g);
+ }
+ else { /* another bad collection; stay in incremental mode */
+ g->GCestimate = gettotalbytes(g); /* first estimate */;
+ entersweep(L);
+ luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
+ setpause(g);
+ g->lastatomic = newatomic;
+ }
+}
+
+
+/*
+** Does a generational "step".
+** Usually, this means doing a minor collection and setting the debt to
+** make another collection when memory grows 'genminormul'% larger.
+**
+** However, there are exceptions. If memory grows 'genmajormul'%
+** larger than it was at the end of the last major collection (kept
+** in 'g->GCestimate'), the function does a major collection. At the
+** end, it checks whether the major collection was able to free a
+** decent amount of memory (at least half the growth in memory since
+** previous major collection). If so, the collector keeps its state,
+** and the next collection will probably be minor again. Otherwise,
+** we have what we call a "bad collection". In that case, set the field
+** 'g->lastatomic' to signal that fact, so that the next collection will
+** go to 'stepgenfull'.
+**
+** 'GCdebt <= 0' means an explicit call to GC step with "size" zero;
+** in that case, do a minor collection.
+*/
+static void genstep (lua_State *L, global_State *g) {
+ if (g->lastatomic != 0) /* last collection was a bad one? */
+ stepgenfull(L, g); /* do a full step */
+ else {
+ lu_mem majorbase = g->GCestimate; /* memory after last major collection */
+ lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul);
+ if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) {
+ lu_mem numobjs = fullgen(L, g); /* do a major collection */
+ if (gettotalbytes(g) < majorbase + (majorinc / 2)) {
+ /* collected at least half of memory growth since last major
+ collection; keep doing minor collections. */
+ lua_assert(g->lastatomic == 0);
+ }
+ else { /* bad collection */
+ g->lastatomic = numobjs; /* signal that last collection was bad */
+ setpause(g); /* do a long wait for next (major) collection */
+ }
+ }
+ else { /* regular case; do a minor collection */
+ youngcollection(L, g);
+ setminordebt(g);
+ g->GCestimate = majorbase; /* preserve base value */
+ }
+ }
+ lua_assert(isdecGCmodegen(g));
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** GC control
+** =======================================================
+*/
+
+
+/*
+** Enter first sweep phase.
+** The call to 'sweeptolive' makes the pointer point to an object
+** inside the list (instead of to the header), so that the real sweep do
+** not need to skip objects created between "now" and the start of the
+** real sweep.
+*/
+static void entersweep (lua_State *L) {
+ global_State *g = G(L);
+ g->gcstate = GCSswpallgc;
+ lua_assert(g->sweepgc == NULL);
+ g->sweepgc = sweeptolive(L, &g->allgc);
+}
+
+
+/*
+** Delete all objects in list 'p' until (but not including) object
+** 'limit'.
+*/
+static void deletelist (lua_State *L, GCObject *p, GCObject *limit) {
+ while (p != limit) {
+ GCObject *next = p->next;
+ freeobj(L, p);
+ p = next;
+ }
+}
+
+
+/*
+** Call all finalizers of the objects in the given Lua state, and
+** then free all objects, except for the main thread.
+*/
+void luaC_freeallobjects (lua_State *L) {
+ global_State *g = G(L);
+ g->gcstp = GCSTPCLS; /* no extra finalizers after here */
+ luaC_changemode(L, KGC_INC);
+ separatetobefnz(g, 1); /* separate all objects with finalizers */
+ lua_assert(g->finobj == NULL);
+ callallpendingfinalizers(L);
+ deletelist(L, g->allgc, obj2gco(g->mainthread));
+ lua_assert(g->finobj == NULL); /* no new finalizers */
+ deletelist(L, g->fixedgc, NULL); /* collect fixed objects */
+ lua_assert(g->strt.nuse == 0);
+}
+
+
+static lu_mem atomic (lua_State *L) {
+ global_State *g = G(L);
+ lu_mem work = 0;
+ GCObject *origweak, *origall;
+ GCObject *grayagain = g->grayagain; /* save original list */
+ g->grayagain = NULL;
+ lua_assert(g->ephemeron == NULL && g->weak == NULL);
+ lua_assert(!iswhite(g->mainthread));
+ g->gcstate = GCSatomic;
+ markobject(g, L); /* mark running thread */
+ /* registry and global metatables may be changed by API */
+ markvalue(g, &g->l_registry);
+ markmt(g); /* mark global metatables */
+ work += propagateall(g); /* empties 'gray' list */
+ /* remark occasional upvalues of (maybe) dead threads */
+ work += remarkupvals(g);
+ work += propagateall(g); /* propagate changes */
+ g->gray = grayagain;
+ work += propagateall(g); /* traverse 'grayagain' list */
+ convergeephemerons(g);
+ /* at this point, all strongly accessible objects are marked. */
+ /* Clear values from weak tables, before checking finalizers */
+ clearbyvalues(g, g->weak, NULL);
+ clearbyvalues(g, g->allweak, NULL);
+ origweak = g->weak; origall = g->allweak;
+ separatetobefnz(g, 0); /* separate objects to be finalized */
+ work += markbeingfnz(g); /* mark objects that will be finalized */
+ work += propagateall(g); /* remark, to propagate 'resurrection' */
+ convergeephemerons(g);
+ /* at this point, all resurrected objects are marked. */
+ /* remove dead objects from weak tables */
+ clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */
+ clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */
+ /* clear values from resurrected weak tables */
+ clearbyvalues(g, g->weak, origweak);
+ clearbyvalues(g, g->allweak, origall);
+ luaS_clearcache(g);
+ g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */
+ lua_assert(g->gray == NULL);
+ return work; /* estimate of slots marked by 'atomic' */
+}
+
+
+static int sweepstep (lua_State *L, global_State *g,
+ int nextstate, GCObject **nextlist) {
+ if (g->sweepgc) {
+ l_mem olddebt = g->GCdebt;
+ int count;
+ g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count);
+ g->GCestimate += g->GCdebt - olddebt; /* update estimate */
+ return count;
+ }
+ else { /* enter next state */
+ g->gcstate = nextstate;
+ g->sweepgc = nextlist;
+ return 0; /* no work done */
+ }
+}
+
+
+static lu_mem singlestep (lua_State *L) {
+ global_State *g = G(L);
+ lu_mem work;
+ lua_assert(!g->gcstopem); /* collector is not reentrant */
+ g->gcstopem = 1; /* no emergency collections while collecting */
+ switch (g->gcstate) {
+ case GCSpause: {
+ restartcollection(g);
+ g->gcstate = GCSpropagate;
+ work = 1;
+ break;
+ }
+ case GCSpropagate: {
+ if (g->gray == NULL) { /* no more gray objects? */
+ g->gcstate = GCSenteratomic; /* finish propagate phase */
+ work = 0;
+ }
+ else
+ work = propagatemark(g); /* traverse one gray object */
+ break;
+ }
+ case GCSenteratomic: {
+ work = atomic(L); /* work is what was traversed by 'atomic' */
+ entersweep(L);
+ g->GCestimate = gettotalbytes(g); /* first estimate */;
+ break;
+ }
+ case GCSswpallgc: { /* sweep "regular" objects */
+ work = sweepstep(L, g, GCSswpfinobj, &g->finobj);
+ break;
+ }
+ case GCSswpfinobj: { /* sweep objects with finalizers */
+ work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
+ break;
+ }
+ case GCSswptobefnz: { /* sweep objects to be finalized */
+ work = sweepstep(L, g, GCSswpend, NULL);
+ break;
+ }
+ case GCSswpend: { /* finish sweeps */
+ checkSizes(L, g);
+ g->gcstate = GCScallfin;
+ work = 0;
+ break;
+ }
+ case GCScallfin: { /* call remaining finalizers */
+ if (g->tobefnz && !g->gcemergency) {
+ g->gcstopem = 0; /* ok collections during finalizers */
+ work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST;
+ }
+ else { /* emergency mode or no more finalizers */
+ g->gcstate = GCSpause; /* finish collection */
+ work = 0;
+ }
+ break;
+ }
+ default: lua_assert(0); return 0;
+ }
+ g->gcstopem = 0;
+ return work;
+}
+
+
+/*
+** advances the garbage collector until it reaches a state allowed
+** by 'statemask'
+*/
+void luaC_runtilstate (lua_State *L, int statesmask) {
+ global_State *g = G(L);
+ while (!testbit(statesmask, g->gcstate))
+ singlestep(L);
+}
+
+
+
+/*
+** Performs a basic incremental step. The debt and step size are
+** converted from bytes to "units of work"; then the function loops
+** running single steps until adding that many units of work or
+** finishing a cycle (pause state). Finally, it sets the debt that
+** controls when next step will be performed.
+*/
+static void incstep (lua_State *L, global_State *g) {
+ int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */
+ l_mem debt = (g->GCdebt / WORK2MEM) * stepmul;
+ l_mem stepsize = (g->gcstepsize <= log2maxs(l_mem))
+ ? ((cast(l_mem, 1) << g->gcstepsize) / WORK2MEM) * stepmul
+ : MAX_LMEM; /* overflow; keep maximum value */
+ do { /* repeat until pause or enough "credit" (negative debt) */
+ lu_mem work = singlestep(L); /* perform one single step */
+ debt -= work;
+ } while (debt > -stepsize && g->gcstate != GCSpause);
+ if (g->gcstate == GCSpause)
+ setpause(g); /* pause until next cycle */
+ else {
+ debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */
+ luaE_setdebt(g, debt);
+ }
+}
+
+/*
+** Performs a basic GC step if collector is running. (If collector is
+** not running, set a reasonable debt to avoid it being called at
+** every single check.)
+*/
+void luaC_step (lua_State *L) {
+ global_State *g = G(L);
+ if (!gcrunning(g)) /* not running? */
+ luaE_setdebt(g, -2000);
+ else {
+ if(isdecGCmodegen(g))
+ genstep(L, g);
+ else
+ incstep(L, g);
+ }
+}
+
+
+/*
+** Perform a full collection in incremental mode.
+** Before running the collection, check 'keepinvariant'; if it is true,
+** there may be some objects marked as black, so the collector has
+** to sweep all objects to turn them back to white (as white has not
+** changed, nothing will be collected).
+*/
+static void fullinc (lua_State *L, global_State *g) {
+ if (keepinvariant(g)) /* black objects? */
+ entersweep(L); /* sweep everything to turn them back to white */
+ /* finish any pending sweep phase to start a new cycle */
+ luaC_runtilstate(L, bitmask(GCSpause));
+ luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */
+ /* estimate must be correct after a full GC cycle */
+ lua_assert(g->GCestimate == gettotalbytes(g));
+ luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
+ setpause(g);
+}
+
+
+/*
+** Performs a full GC cycle; if 'isemergency', set a flag to avoid
+** some operations which could change the interpreter state in some
+** unexpected ways (running finalizers and shrinking some structures).
+*/
+void luaC_fullgc (lua_State *L, int isemergency) {
+ global_State *g = G(L);
+ lua_assert(!g->gcemergency);
+ g->gcemergency = isemergency; /* set flag */
+ if (g->gckind == KGC_INC)
+ fullinc(L, g);
+ else
+ fullgen(L, g);
+ g->gcemergency = 0;
+}
+
+/* }====================================================== */
+
+
diff --git a/src/libs/3rdparty/lua/src/lgc.h b/src/libs/3rdparty/lua/src/lgc.h
new file mode 100644
index 0000000000..538f6edccc
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lgc.h
@@ -0,0 +1,202 @@
+/*
+** $Id: lgc.h $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lgc_h
+#define lgc_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+
+/*
+** Collectable objects may have one of three colors: white, which means
+** the object is not marked; gray, which means the object is marked, but
+** its references may be not marked; and black, which means that the
+** object and all its references are marked. The main invariant of the
+** garbage collector, while marking objects, is that a black object can
+** never point to a white one. Moreover, any gray object must be in a
+** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it
+** can be visited again before finishing the collection cycle. (Open
+** upvalues are an exception to this rule.) These lists have no meaning
+** when the invariant is not being enforced (e.g., sweep phase).
+*/
+
+
+/*
+** Possible states of the Garbage Collector
+*/
+#define GCSpropagate 0
+#define GCSenteratomic 1
+#define GCSatomic 2
+#define GCSswpallgc 3
+#define GCSswpfinobj 4
+#define GCSswptobefnz 5
+#define GCSswpend 6
+#define GCScallfin 7
+#define GCSpause 8
+
+
+#define issweepphase(g) \
+ (GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend)
+
+
+/*
+** macro to tell when main invariant (white objects cannot point to black
+** ones) must be kept. During a collection, the sweep
+** phase may break the invariant, as objects turned white may point to
+** still-black objects. The invariant is restored when sweep ends and
+** all objects are white again.
+*/
+
+#define keepinvariant(g) ((g)->gcstate <= GCSatomic)
+
+
+/*
+** some useful bit tricks
+*/
+#define resetbits(x,m) ((x) &= cast_byte(~(m)))
+#define setbits(x,m) ((x) |= (m))
+#define testbits(x,m) ((x) & (m))
+#define bitmask(b) (1<<(b))
+#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2))
+#define l_setbit(x,b) setbits(x, bitmask(b))
+#define resetbit(x,b) resetbits(x, bitmask(b))
+#define testbit(x,b) testbits(x, bitmask(b))
+
+
+/*
+** Layout for bit use in 'marked' field. First three bits are
+** used for object "age" in generational mode. Last bit is used
+** by tests.
+*/
+#define WHITE0BIT 3 /* object is white (type 0) */
+#define WHITE1BIT 4 /* object is white (type 1) */
+#define BLACKBIT 5 /* object is black */
+#define FINALIZEDBIT 6 /* object has been marked for finalization */
+
+#define TESTBIT 7
+
+
+
+#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
+
+
+#define iswhite(x) testbits((x)->marked, WHITEBITS)
+#define isblack(x) testbit((x)->marked, BLACKBIT)
+#define isgray(x) /* neither white nor black */ \
+ (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT)))
+
+#define tofinalize(x) testbit((x)->marked, FINALIZEDBIT)
+
+#define otherwhite(g) ((g)->currentwhite ^ WHITEBITS)
+#define isdeadm(ow,m) ((m) & (ow))
+#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked)
+
+#define changewhite(x) ((x)->marked ^= WHITEBITS)
+#define nw2black(x) \
+ check_exp(!iswhite(x), l_setbit((x)->marked, BLACKBIT))
+
+#define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS)
+
+
+/* object age in generational mode */
+#define G_NEW 0 /* created in current cycle */
+#define G_SURVIVAL 1 /* created in previous cycle */
+#define G_OLD0 2 /* marked old by frw. barrier in this cycle */
+#define G_OLD1 3 /* first full cycle as old */
+#define G_OLD 4 /* really old object (not to be visited) */
+#define G_TOUCHED1 5 /* old object touched this cycle */
+#define G_TOUCHED2 6 /* old object touched in previous cycle */
+
+#define AGEBITS 7 /* all age bits (111) */
+
+#define getage(o) ((o)->marked & AGEBITS)
+#define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a))
+#define isold(o) (getage(o) > G_SURVIVAL)
+
+#define changeage(o,f,t) \
+ check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t)))
+
+
+/* Default Values for GC parameters */
+#define LUAI_GENMAJORMUL 100
+#define LUAI_GENMINORMUL 20
+
+/* wait memory to double before starting new cycle */
+#define LUAI_GCPAUSE 200
+
+/*
+** some gc parameters are stored divided by 4 to allow a maximum value
+** up to 1023 in a 'lu_byte'.
+*/
+#define getgcparam(p) ((p) * 4)
+#define setgcparam(p,v) ((p) = (v) / 4)
+
+#define LUAI_GCMUL 100
+
+/* how much to allocate before next GC step (log2) */
+#define LUAI_GCSTEPSIZE 13 /* 8 KB */
+
+
+/*
+** Check whether the declared GC mode is generational. While in
+** generational mode, the collector can go temporarily to incremental
+** mode to improve performance. This is signaled by 'g->lastatomic != 0'.
+*/
+#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0)
+
+
+/*
+** Control when GC is running:
+*/
+#define GCSTPUSR 1 /* bit true when GC stopped by user */
+#define GCSTPGC 2 /* bit true when GC stopped by itself */
+#define GCSTPCLS 4 /* bit true when closing Lua state */
+#define gcrunning(g) ((g)->gcstp == 0)
+
+
+/*
+** Does one step of collection when debt becomes positive. 'pre'/'pos'
+** allows some adjustments to be done only when needed. macro
+** 'condchangemem' is used only for heavy tests (forcing a full
+** GC cycle on every opportunity)
+*/
+#define luaC_condGC(L,pre,pos) \
+ { if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \
+ condchangemem(L,pre,pos); }
+
+/* more often than not, 'pre'/'pos' are empty */
+#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0)
+
+
+#define luaC_objbarrier(L,p,o) ( \
+ (isblack(p) && iswhite(o)) ? \
+ luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
+
+#define luaC_barrier(L,p,v) ( \
+ iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0))
+
+#define luaC_objbarrierback(L,p,o) ( \
+ (isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0))
+
+#define luaC_barrierback(L,p,v) ( \
+ iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0))
+
+LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
+LUAI_FUNC void luaC_freeallobjects (lua_State *L);
+LUAI_FUNC void luaC_step (lua_State *L);
+LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask);
+LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
+LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
+LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz,
+ size_t offset);
+LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
+LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
+LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
+LUAI_FUNC void luaC_changemode (lua_State *L, int newmode);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/linit.c b/src/libs/3rdparty/lua/src/linit.c
new file mode 100644
index 0000000000..69808f84f4
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/linit.c
@@ -0,0 +1,65 @@
+/*
+** $Id: linit.c $
+** Initialization of libraries for lua.c and other clients
+** See Copyright Notice in lua.h
+*/
+
+
+#define linit_c
+#define LUA_LIB
+
+/*
+** If you embed Lua in your program and need to open the standard
+** libraries, call luaL_openlibs in your program. If you need a
+** different set of libraries, copy this file to your project and edit
+** it to suit your needs.
+**
+** You can also *preload* libraries, so that a later 'require' can
+** open the library, which is already linked to the application.
+** For that, do the following code:
+**
+** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
+** lua_pushcfunction(L, luaopen_modname);
+** lua_setfield(L, -2, modname);
+** lua_pop(L, 1); // remove PRELOAD table
+*/
+
+#include "lprefix.h"
+
+
+#include <stddef.h>
+
+#include "lua.h"
+
+#include "lualib.h"
+#include "lauxlib.h"
+
+
+/*
+** these libs are loaded by lua.c and are readily available to any Lua
+** program
+*/
+static const luaL_Reg loadedlibs[] = {
+ {LUA_GNAME, luaopen_base},
+ {LUA_LOADLIBNAME, luaopen_package},
+ {LUA_COLIBNAME, luaopen_coroutine},
+ {LUA_TABLIBNAME, luaopen_table},
+ {LUA_IOLIBNAME, luaopen_io},
+ {LUA_OSLIBNAME, luaopen_os},
+ {LUA_STRLIBNAME, luaopen_string},
+ {LUA_MATHLIBNAME, luaopen_math},
+ {LUA_UTF8LIBNAME, luaopen_utf8},
+ {LUA_DBLIBNAME, luaopen_debug},
+ {NULL, NULL}
+};
+
+
+LUALIB_API void luaL_openlibs (lua_State *L) {
+ const luaL_Reg *lib;
+ /* "require" functions from 'loadedlibs' and set results to global table */
+ for (lib = loadedlibs; lib->func; lib++) {
+ luaL_requiref(L, lib->name, lib->func, 1);
+ lua_pop(L, 1); /* remove lib */
+ }
+}
+
diff --git a/src/libs/3rdparty/lua/src/liolib.c b/src/libs/3rdparty/lua/src/liolib.c
new file mode 100644
index 0000000000..b08397da45
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/liolib.c
@@ -0,0 +1,828 @@
+/*
+** $Id: liolib.c $
+** Standard I/O (and system) library
+** See Copyright Notice in lua.h
+*/
+
+#define liolib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <ctype.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+
+/*
+** Change this macro to accept other modes for 'fopen' besides
+** the standard ones.
+*/
+#if !defined(l_checkmode)
+
+/* accepted extensions to 'mode' in 'fopen' */
+#if !defined(L_MODEEXT)
+#define L_MODEEXT "b"
+#endif
+
+/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */
+static int l_checkmode (const char *mode) {
+ return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL &&
+ (*mode != '+' || ((void)(++mode), 1)) && /* skip if char is '+' */
+ (strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */
+}
+
+#endif
+
+/*
+** {======================================================
+** l_popen spawns a new process connected to the current
+** one through the file streams.
+** =======================================================
+*/
+
+#if !defined(l_popen) /* { */
+
+#if defined(LUA_USE_POSIX) /* { */
+
+#define l_popen(L,c,m) (fflush(NULL), popen(c,m))
+#define l_pclose(L,file) (pclose(file))
+
+#elif defined(LUA_USE_WINDOWS) /* }{ */
+
+#define l_popen(L,c,m) (_popen(c,m))
+#define l_pclose(L,file) (_pclose(file))
+
+#if !defined(l_checkmodep)
+/* Windows accepts "[rw][bt]?" as valid modes */
+#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && \
+ (m[1] == '\0' || ((m[1] == 'b' || m[1] == 't') && m[2] == '\0')))
+#endif
+
+#else /* }{ */
+
+/* ISO C definitions */
+#define l_popen(L,c,m) \
+ ((void)c, (void)m, \
+ luaL_error(L, "'popen' not supported"), \
+ (FILE*)0)
+#define l_pclose(L,file) ((void)L, (void)file, -1)
+
+#endif /* } */
+
+#endif /* } */
+
+
+#if !defined(l_checkmodep)
+/* By default, Lua accepts only "r" or "w" as valid modes */
+#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
+#endif
+
+/* }====================================================== */
+
+
+#if !defined(l_getc) /* { */
+
+#if defined(LUA_USE_POSIX)
+#define l_getc(f) getc_unlocked(f)
+#define l_lockfile(f) flockfile(f)
+#define l_unlockfile(f) funlockfile(f)
+#else
+#define l_getc(f) getc(f)
+#define l_lockfile(f) ((void)0)
+#define l_unlockfile(f) ((void)0)
+#endif
+
+#endif /* } */
+
+
+/*
+** {======================================================
+** l_fseek: configuration for longer offsets
+** =======================================================
+*/
+
+#if !defined(l_fseek) /* { */
+
+#if defined(LUA_USE_POSIX) /* { */
+
+#include <sys/types.h>
+
+#define l_fseek(f,o,w) fseeko(f,o,w)
+#define l_ftell(f) ftello(f)
+#define l_seeknum off_t
+
+#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \
+ && defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */
+
+/* Windows (but not DDK) and Visual C++ 2005 or higher */
+#define l_fseek(f,o,w) _fseeki64(f,o,w)
+#define l_ftell(f) _ftelli64(f)
+#define l_seeknum __int64
+
+#else /* }{ */
+
+/* ISO C definitions */
+#define l_fseek(f,o,w) fseek(f,o,w)
+#define l_ftell(f) ftell(f)
+#define l_seeknum long
+
+#endif /* } */
+
+#endif /* } */
+
+/* }====================================================== */
+
+
+
+#define IO_PREFIX "_IO_"
+#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1)
+#define IO_INPUT (IO_PREFIX "input")
+#define IO_OUTPUT (IO_PREFIX "output")
+
+
+typedef luaL_Stream LStream;
+
+
+#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE))
+
+#define isclosed(p) ((p)->closef == NULL)
+
+
+static int io_type (lua_State *L) {
+ LStream *p;
+ luaL_checkany(L, 1);
+ p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE);
+ if (p == NULL)
+ luaL_pushfail(L); /* not a file */
+ else if (isclosed(p))
+ lua_pushliteral(L, "closed file");
+ else
+ lua_pushliteral(L, "file");
+ return 1;
+}
+
+
+static int f_tostring (lua_State *L) {
+ LStream *p = tolstream(L);
+ if (isclosed(p))
+ lua_pushliteral(L, "file (closed)");
+ else
+ lua_pushfstring(L, "file (%p)", p->f);
+ return 1;
+}
+
+
+static FILE *tofile (lua_State *L) {
+ LStream *p = tolstream(L);
+ if (l_unlikely(isclosed(p)))
+ luaL_error(L, "attempt to use a closed file");
+ lua_assert(p->f);
+ return p->f;
+}
+
+
+/*
+** When creating file handles, always creates a 'closed' file handle
+** before opening the actual file; so, if there is a memory error, the
+** handle is in a consistent state.
+*/
+static LStream *newprefile (lua_State *L) {
+ LStream *p = (LStream *)lua_newuserdatauv(L, sizeof(LStream), 0);
+ p->closef = NULL; /* mark file handle as 'closed' */
+ luaL_setmetatable(L, LUA_FILEHANDLE);
+ return p;
+}
+
+
+/*
+** Calls the 'close' function from a file handle. The 'volatile' avoids
+** a bug in some versions of the Clang compiler (e.g., clang 3.0 for
+** 32 bits).
+*/
+static int aux_close (lua_State *L) {
+ LStream *p = tolstream(L);
+ volatile lua_CFunction cf = p->closef;
+ p->closef = NULL; /* mark stream as closed */
+ return (*cf)(L); /* close it */
+}
+
+
+static int f_close (lua_State *L) {
+ tofile(L); /* make sure argument is an open stream */
+ return aux_close(L);
+}
+
+
+static int io_close (lua_State *L) {
+ if (lua_isnone(L, 1)) /* no argument? */
+ lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use default output */
+ return f_close(L);
+}
+
+
+static int f_gc (lua_State *L) {
+ LStream *p = tolstream(L);
+ if (!isclosed(p) && p->f != NULL)
+ aux_close(L); /* ignore closed and incompletely open files */
+ return 0;
+}
+
+
+/*
+** function to close regular files
+*/
+static int io_fclose (lua_State *L) {
+ LStream *p = tolstream(L);
+ int res = fclose(p->f);
+ return luaL_fileresult(L, (res == 0), NULL);
+}
+
+
+static LStream *newfile (lua_State *L) {
+ LStream *p = newprefile(L);
+ p->f = NULL;
+ p->closef = &io_fclose;
+ return p;
+}
+
+
+static void opencheck (lua_State *L, const char *fname, const char *mode) {
+ LStream *p = newfile(L);
+ p->f = fopen(fname, mode);
+ if (l_unlikely(p->f == NULL))
+ luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno));
+}
+
+
+static int io_open (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ const char *mode = luaL_optstring(L, 2, "r");
+ LStream *p = newfile(L);
+ const char *md = mode; /* to traverse/check mode */
+ luaL_argcheck(L, l_checkmode(md), 2, "invalid mode");
+ p->f = fopen(filename, mode);
+ return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
+}
+
+
+/*
+** function to close 'popen' files
+*/
+static int io_pclose (lua_State *L) {
+ LStream *p = tolstream(L);
+ errno = 0;
+ return luaL_execresult(L, l_pclose(L, p->f));
+}
+
+
+static int io_popen (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ const char *mode = luaL_optstring(L, 2, "r");
+ LStream *p = newprefile(L);
+ luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode");
+ p->f = l_popen(L, filename, mode);
+ p->closef = &io_pclose;
+ return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
+}
+
+
+static int io_tmpfile (lua_State *L) {
+ LStream *p = newfile(L);
+ p->f = tmpfile();
+ return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
+}
+
+
+static FILE *getiofile (lua_State *L, const char *findex) {
+ LStream *p;
+ lua_getfield(L, LUA_REGISTRYINDEX, findex);
+ p = (LStream *)lua_touserdata(L, -1);
+ if (l_unlikely(isclosed(p)))
+ luaL_error(L, "default %s file is closed", findex + IOPREF_LEN);
+ return p->f;
+}
+
+
+static int g_iofile (lua_State *L, const char *f, const char *mode) {
+ if (!lua_isnoneornil(L, 1)) {
+ const char *filename = lua_tostring(L, 1);
+ if (filename)
+ opencheck(L, filename, mode);
+ else {
+ tofile(L); /* check that it's a valid file handle */
+ lua_pushvalue(L, 1);
+ }
+ lua_setfield(L, LUA_REGISTRYINDEX, f);
+ }
+ /* return current value */
+ lua_getfield(L, LUA_REGISTRYINDEX, f);
+ return 1;
+}
+
+
+static int io_input (lua_State *L) {
+ return g_iofile(L, IO_INPUT, "r");
+}
+
+
+static int io_output (lua_State *L) {
+ return g_iofile(L, IO_OUTPUT, "w");
+}
+
+
+static int io_readline (lua_State *L);
+
+
+/*
+** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit
+** in the limit for upvalues of a closure)
+*/
+#define MAXARGLINE 250
+
+/*
+** Auxiliary function to create the iteration function for 'lines'.
+** The iteration function is a closure over 'io_readline', with
+** the following upvalues:
+** 1) The file being read (first value in the stack)
+** 2) the number of arguments to read
+** 3) a boolean, true iff file has to be closed when finished ('toclose')
+** *) a variable number of format arguments (rest of the stack)
+*/
+static void aux_lines (lua_State *L, int toclose) {
+ int n = lua_gettop(L) - 1; /* number of arguments to read */
+ luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments");
+ lua_pushvalue(L, 1); /* file */
+ lua_pushinteger(L, n); /* number of arguments to read */
+ lua_pushboolean(L, toclose); /* close/not close file when finished */
+ lua_rotate(L, 2, 3); /* move the three values to their positions */
+ lua_pushcclosure(L, io_readline, 3 + n);
+}
+
+
+static int f_lines (lua_State *L) {
+ tofile(L); /* check that it's a valid file handle */
+ aux_lines(L, 0);
+ return 1;
+}
+
+
+/*
+** Return an iteration function for 'io.lines'. If file has to be
+** closed, also returns the file itself as a second result (to be
+** closed as the state at the exit of a generic for).
+*/
+static int io_lines (lua_State *L) {
+ int toclose;
+ if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */
+ if (lua_isnil(L, 1)) { /* no file name? */
+ lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */
+ lua_replace(L, 1); /* put it at index 1 */
+ tofile(L); /* check that it's a valid file handle */
+ toclose = 0; /* do not close it after iteration */
+ }
+ else { /* open a new file */
+ const char *filename = luaL_checkstring(L, 1);
+ opencheck(L, filename, "r");
+ lua_replace(L, 1); /* put file at index 1 */
+ toclose = 1; /* close it after iteration */
+ }
+ aux_lines(L, toclose); /* push iteration function */
+ if (toclose) {
+ lua_pushnil(L); /* state */
+ lua_pushnil(L); /* control */
+ lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */
+ return 4;
+ }
+ else
+ return 1;
+}
+
+
+/*
+** {======================================================
+** READ
+** =======================================================
+*/
+
+
+/* maximum length of a numeral */
+#if !defined (L_MAXLENNUM)
+#define L_MAXLENNUM 200
+#endif
+
+
+/* auxiliary structure used by 'read_number' */
+typedef struct {
+ FILE *f; /* file being read */
+ int c; /* current character (look ahead) */
+ int n; /* number of elements in buffer 'buff' */
+ char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */
+} RN;
+
+
+/*
+** Add current char to buffer (if not out of space) and read next one
+*/
+static int nextc (RN *rn) {
+ if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */
+ rn->buff[0] = '\0'; /* invalidate result */
+ return 0; /* fail */
+ }
+ else {
+ rn->buff[rn->n++] = rn->c; /* save current char */
+ rn->c = l_getc(rn->f); /* read next one */
+ return 1;
+ }
+}
+
+
+/*
+** Accept current char if it is in 'set' (of size 2)
+*/
+static int test2 (RN *rn, const char *set) {
+ if (rn->c == set[0] || rn->c == set[1])
+ return nextc(rn);
+ else return 0;
+}
+
+
+/*
+** Read a sequence of (hex)digits
+*/
+static int readdigits (RN *rn, int hex) {
+ int count = 0;
+ while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn))
+ count++;
+ return count;
+}
+
+
+/*
+** Read a number: first reads a valid prefix of a numeral into a buffer.
+** Then it calls 'lua_stringtonumber' to check whether the format is
+** correct and to convert it to a Lua number.
+*/
+static int read_number (lua_State *L, FILE *f) {
+ RN rn;
+ int count = 0;
+ int hex = 0;
+ char decp[2];
+ rn.f = f; rn.n = 0;
+ decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */
+ decp[1] = '.'; /* always accept a dot */
+ l_lockfile(rn.f);
+ do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */
+ test2(&rn, "-+"); /* optional sign */
+ if (test2(&rn, "00")) {
+ if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */
+ else count = 1; /* count initial '0' as a valid digit */
+ }
+ count += readdigits(&rn, hex); /* integral part */
+ if (test2(&rn, decp)) /* decimal point? */
+ count += readdigits(&rn, hex); /* fractional part */
+ if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */
+ test2(&rn, "-+"); /* exponent sign */
+ readdigits(&rn, 0); /* exponent digits */
+ }
+ ungetc(rn.c, rn.f); /* unread look-ahead char */
+ l_unlockfile(rn.f);
+ rn.buff[rn.n] = '\0'; /* finish string */
+ if (l_likely(lua_stringtonumber(L, rn.buff)))
+ return 1; /* ok, it is a valid number */
+ else { /* invalid format */
+ lua_pushnil(L); /* "result" to be removed */
+ return 0; /* read fails */
+ }
+}
+
+
+static int test_eof (lua_State *L, FILE *f) {
+ int c = getc(f);
+ ungetc(c, f); /* no-op when c == EOF */
+ lua_pushliteral(L, "");
+ return (c != EOF);
+}
+
+
+static int read_line (lua_State *L, FILE *f, int chop) {
+ luaL_Buffer b;
+ int c;
+ luaL_buffinit(L, &b);
+ do { /* may need to read several chunks to get whole line */
+ char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */
+ int i = 0;
+ l_lockfile(f); /* no memory errors can happen inside the lock */
+ while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n')
+ buff[i++] = c; /* read up to end of line or buffer limit */
+ l_unlockfile(f);
+ luaL_addsize(&b, i);
+ } while (c != EOF && c != '\n'); /* repeat until end of line */
+ if (!chop && c == '\n') /* want a newline and have one? */
+ luaL_addchar(&b, c); /* add ending newline to result */
+ luaL_pushresult(&b); /* close buffer */
+ /* return ok if read something (either a newline or something else) */
+ return (c == '\n' || lua_rawlen(L, -1) > 0);
+}
+
+
+static void read_all (lua_State *L, FILE *f) {
+ size_t nr;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ do { /* read file in chunks of LUAL_BUFFERSIZE bytes */
+ char *p = luaL_prepbuffer(&b);
+ nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f);
+ luaL_addsize(&b, nr);
+ } while (nr == LUAL_BUFFERSIZE);
+ luaL_pushresult(&b); /* close buffer */
+}
+
+
+static int read_chars (lua_State *L, FILE *f, size_t n) {
+ size_t nr; /* number of chars actually read */
+ char *p;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */
+ nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */
+ luaL_addsize(&b, nr);
+ luaL_pushresult(&b); /* close buffer */
+ return (nr > 0); /* true iff read something */
+}
+
+
+static int g_read (lua_State *L, FILE *f, int first) {
+ int nargs = lua_gettop(L) - 1;
+ int n, success;
+ clearerr(f);
+ if (nargs == 0) { /* no arguments? */
+ success = read_line(L, f, 1);
+ n = first + 1; /* to return 1 result */
+ }
+ else {
+ /* ensure stack space for all results and for auxlib's buffer */
+ luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
+ success = 1;
+ for (n = first; nargs-- && success; n++) {
+ if (lua_type(L, n) == LUA_TNUMBER) {
+ size_t l = (size_t)luaL_checkinteger(L, n);
+ success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
+ }
+ else {
+ const char *p = luaL_checkstring(L, n);
+ if (*p == '*') p++; /* skip optional '*' (for compatibility) */
+ switch (*p) {
+ case 'n': /* number */
+ success = read_number(L, f);
+ break;
+ case 'l': /* line */
+ success = read_line(L, f, 1);
+ break;
+ case 'L': /* line with end-of-line */
+ success = read_line(L, f, 0);
+ break;
+ case 'a': /* file */
+ read_all(L, f); /* read entire file */
+ success = 1; /* always success */
+ break;
+ default:
+ return luaL_argerror(L, n, "invalid format");
+ }
+ }
+ }
+ }
+ if (ferror(f))
+ return luaL_fileresult(L, 0, NULL);
+ if (!success) {
+ lua_pop(L, 1); /* remove last result */
+ luaL_pushfail(L); /* push nil instead */
+ }
+ return n - first;
+}
+
+
+static int io_read (lua_State *L) {
+ return g_read(L, getiofile(L, IO_INPUT), 1);
+}
+
+
+static int f_read (lua_State *L) {
+ return g_read(L, tofile(L), 2);
+}
+
+
+/*
+** Iteration function for 'lines'.
+*/
+static int io_readline (lua_State *L) {
+ LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1));
+ int i;
+ int n = (int)lua_tointeger(L, lua_upvalueindex(2));
+ if (isclosed(p)) /* file is already closed? */
+ return luaL_error(L, "file is already closed");
+ lua_settop(L , 1);
+ luaL_checkstack(L, n, "too many arguments");
+ for (i = 1; i <= n; i++) /* push arguments to 'g_read' */
+ lua_pushvalue(L, lua_upvalueindex(3 + i));
+ n = g_read(L, p->f, 2); /* 'n' is number of results */
+ lua_assert(n > 0); /* should return at least a nil */
+ if (lua_toboolean(L, -n)) /* read at least one value? */
+ return n; /* return them */
+ else { /* first result is false: EOF or error */
+ if (n > 1) { /* is there error information? */
+ /* 2nd result is error message */
+ return luaL_error(L, "%s", lua_tostring(L, -n + 1));
+ }
+ if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */
+ lua_settop(L, 0); /* clear stack */
+ lua_pushvalue(L, lua_upvalueindex(1)); /* push file at index 1 */
+ aux_close(L); /* close it */
+ }
+ return 0;
+ }
+}
+
+/* }====================================================== */
+
+
+static int g_write (lua_State *L, FILE *f, int arg) {
+ int nargs = lua_gettop(L) - arg;
+ int status = 1;
+ for (; nargs--; arg++) {
+ if (lua_type(L, arg) == LUA_TNUMBER) {
+ /* optimization: could be done exactly as for strings */
+ int len = lua_isinteger(L, arg)
+ ? fprintf(f, LUA_INTEGER_FMT,
+ (LUAI_UACINT)lua_tointeger(L, arg))
+ : fprintf(f, LUA_NUMBER_FMT,
+ (LUAI_UACNUMBER)lua_tonumber(L, arg));
+ status = status && (len > 0);
+ }
+ else {
+ size_t l;
+ const char *s = luaL_checklstring(L, arg, &l);
+ status = status && (fwrite(s, sizeof(char), l, f) == l);
+ }
+ }
+ if (l_likely(status))
+ return 1; /* file handle already on stack top */
+ else return luaL_fileresult(L, status, NULL);
+}
+
+
+static int io_write (lua_State *L) {
+ return g_write(L, getiofile(L, IO_OUTPUT), 1);
+}
+
+
+static int f_write (lua_State *L) {
+ FILE *f = tofile(L);
+ lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */
+ return g_write(L, f, 2);
+}
+
+
+static int f_seek (lua_State *L) {
+ static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
+ static const char *const modenames[] = {"set", "cur", "end", NULL};
+ FILE *f = tofile(L);
+ int op = luaL_checkoption(L, 2, "cur", modenames);
+ lua_Integer p3 = luaL_optinteger(L, 3, 0);
+ l_seeknum offset = (l_seeknum)p3;
+ luaL_argcheck(L, (lua_Integer)offset == p3, 3,
+ "not an integer in proper range");
+ op = l_fseek(f, offset, mode[op]);
+ if (l_unlikely(op))
+ return luaL_fileresult(L, 0, NULL); /* error */
+ else {
+ lua_pushinteger(L, (lua_Integer)l_ftell(f));
+ return 1;
+ }
+}
+
+
+static int f_setvbuf (lua_State *L) {
+ static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
+ static const char *const modenames[] = {"no", "full", "line", NULL};
+ FILE *f = tofile(L);
+ int op = luaL_checkoption(L, 2, NULL, modenames);
+ lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
+ int res = setvbuf(f, NULL, mode[op], (size_t)sz);
+ return luaL_fileresult(L, res == 0, NULL);
+}
+
+
+
+static int io_flush (lua_State *L) {
+ return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
+}
+
+
+static int f_flush (lua_State *L) {
+ return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL);
+}
+
+
+/*
+** functions for 'io' library
+*/
+static const luaL_Reg iolib[] = {
+ {"close", io_close},
+ {"flush", io_flush},
+ {"input", io_input},
+ {"lines", io_lines},
+ {"open", io_open},
+ {"output", io_output},
+ {"popen", io_popen},
+ {"read", io_read},
+ {"tmpfile", io_tmpfile},
+ {"type", io_type},
+ {"write", io_write},
+ {NULL, NULL}
+};
+
+
+/*
+** methods for file handles
+*/
+static const luaL_Reg meth[] = {
+ {"read", f_read},
+ {"write", f_write},
+ {"lines", f_lines},
+ {"flush", f_flush},
+ {"seek", f_seek},
+ {"close", f_close},
+ {"setvbuf", f_setvbuf},
+ {NULL, NULL}
+};
+
+
+/*
+** metamethods for file handles
+*/
+static const luaL_Reg metameth[] = {
+ {"__index", NULL}, /* place holder */
+ {"__gc", f_gc},
+ {"__close", f_gc},
+ {"__tostring", f_tostring},
+ {NULL, NULL}
+};
+
+
+static void createmeta (lua_State *L) {
+ luaL_newmetatable(L, LUA_FILEHANDLE); /* metatable for file handles */
+ luaL_setfuncs(L, metameth, 0); /* add metamethods to new metatable */
+ luaL_newlibtable(L, meth); /* create method table */
+ luaL_setfuncs(L, meth, 0); /* add file methods to method table */
+ lua_setfield(L, -2, "__index"); /* metatable.__index = method table */
+ lua_pop(L, 1); /* pop metatable */
+}
+
+
+/*
+** function to (not) close the standard files stdin, stdout, and stderr
+*/
+static int io_noclose (lua_State *L) {
+ LStream *p = tolstream(L);
+ p->closef = &io_noclose; /* keep file opened */
+ luaL_pushfail(L);
+ lua_pushliteral(L, "cannot close standard file");
+ return 2;
+}
+
+
+static void createstdfile (lua_State *L, FILE *f, const char *k,
+ const char *fname) {
+ LStream *p = newprefile(L);
+ p->f = f;
+ p->closef = &io_noclose;
+ if (k != NULL) {
+ lua_pushvalue(L, -1);
+ lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */
+ }
+ lua_setfield(L, -2, fname); /* add file to module */
+}
+
+
+LUAMOD_API int luaopen_io (lua_State *L) {
+ luaL_newlib(L, iolib); /* new module */
+ createmeta(L);
+ /* create (and set) default files */
+ createstdfile(L, stdin, IO_INPUT, "stdin");
+ createstdfile(L, stdout, IO_OUTPUT, "stdout");
+ createstdfile(L, stderr, NULL, "stderr");
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/ljumptab.h b/src/libs/3rdparty/lua/src/ljumptab.h
new file mode 100644
index 0000000000..8306f250cc
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ljumptab.h
@@ -0,0 +1,112 @@
+/*
+** $Id: ljumptab.h $
+** Jump Table for the Lua interpreter
+** See Copyright Notice in lua.h
+*/
+
+
+#undef vmdispatch
+#undef vmcase
+#undef vmbreak
+
+#define vmdispatch(x) goto *disptab[x];
+
+#define vmcase(l) L_##l:
+
+#define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i));
+
+
+static const void *const disptab[NUM_OPCODES] = {
+
+#if 0
+** you can update the following list with this command:
+**
+** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h
+**
+#endif
+
+&&L_OP_MOVE,
+&&L_OP_LOADI,
+&&L_OP_LOADF,
+&&L_OP_LOADK,
+&&L_OP_LOADKX,
+&&L_OP_LOADFALSE,
+&&L_OP_LFALSESKIP,
+&&L_OP_LOADTRUE,
+&&L_OP_LOADNIL,
+&&L_OP_GETUPVAL,
+&&L_OP_SETUPVAL,
+&&L_OP_GETTABUP,
+&&L_OP_GETTABLE,
+&&L_OP_GETI,
+&&L_OP_GETFIELD,
+&&L_OP_SETTABUP,
+&&L_OP_SETTABLE,
+&&L_OP_SETI,
+&&L_OP_SETFIELD,
+&&L_OP_NEWTABLE,
+&&L_OP_SELF,
+&&L_OP_ADDI,
+&&L_OP_ADDK,
+&&L_OP_SUBK,
+&&L_OP_MULK,
+&&L_OP_MODK,
+&&L_OP_POWK,
+&&L_OP_DIVK,
+&&L_OP_IDIVK,
+&&L_OP_BANDK,
+&&L_OP_BORK,
+&&L_OP_BXORK,
+&&L_OP_SHRI,
+&&L_OP_SHLI,
+&&L_OP_ADD,
+&&L_OP_SUB,
+&&L_OP_MUL,
+&&L_OP_MOD,
+&&L_OP_POW,
+&&L_OP_DIV,
+&&L_OP_IDIV,
+&&L_OP_BAND,
+&&L_OP_BOR,
+&&L_OP_BXOR,
+&&L_OP_SHL,
+&&L_OP_SHR,
+&&L_OP_MMBIN,
+&&L_OP_MMBINI,
+&&L_OP_MMBINK,
+&&L_OP_UNM,
+&&L_OP_BNOT,
+&&L_OP_NOT,
+&&L_OP_LEN,
+&&L_OP_CONCAT,
+&&L_OP_CLOSE,
+&&L_OP_TBC,
+&&L_OP_JMP,
+&&L_OP_EQ,
+&&L_OP_LT,
+&&L_OP_LE,
+&&L_OP_EQK,
+&&L_OP_EQI,
+&&L_OP_LTI,
+&&L_OP_LEI,
+&&L_OP_GTI,
+&&L_OP_GEI,
+&&L_OP_TEST,
+&&L_OP_TESTSET,
+&&L_OP_CALL,
+&&L_OP_TAILCALL,
+&&L_OP_RETURN,
+&&L_OP_RETURN0,
+&&L_OP_RETURN1,
+&&L_OP_FORLOOP,
+&&L_OP_FORPREP,
+&&L_OP_TFORPREP,
+&&L_OP_TFORCALL,
+&&L_OP_TFORLOOP,
+&&L_OP_SETLIST,
+&&L_OP_CLOSURE,
+&&L_OP_VARARG,
+&&L_OP_VARARGPREP,
+&&L_OP_EXTRAARG
+
+};
diff --git a/src/libs/3rdparty/lua/src/llex.c b/src/libs/3rdparty/lua/src/llex.c
new file mode 100644
index 0000000000..5fc39a5cde
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/llex.c
@@ -0,0 +1,581 @@
+/*
+** $Id: llex.c $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+#define llex_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <locale.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lctype.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lobject.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "lzio.h"
+
+
+
+#define next(ls) (ls->current = zgetc(ls->z))
+
+
+
+#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
+
+
+/* ORDER RESERVED */
+static const char *const luaX_tokens [] = {
+ "and", "break", "do", "else", "elseif",
+ "end", "false", "for", "function", "goto", "if",
+ "in", "local", "nil", "not", "or", "repeat",
+ "return", "then", "true", "until", "while",
+ "//", "..", "...", "==", ">=", "<=", "~=",
+ "<<", ">>", "::", "<eof>",
+ "<number>", "<integer>", "<name>", "<string>"
+};
+
+
+#define save_and_next(ls) (save(ls, ls->current), next(ls))
+
+
+static l_noret lexerror (LexState *ls, const char *msg, int token);
+
+
+static void save (LexState *ls, int c) {
+ Mbuffer *b = ls->buff;
+ if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) {
+ size_t newsize;
+ if (luaZ_sizebuffer(b) >= MAX_SIZE/2)
+ lexerror(ls, "lexical element too long", 0);
+ newsize = luaZ_sizebuffer(b) * 2;
+ luaZ_resizebuffer(ls->L, b, newsize);
+ }
+ b->buffer[luaZ_bufflen(b)++] = cast_char(c);
+}
+
+
+void luaX_init (lua_State *L) {
+ int i;
+ TString *e = luaS_newliteral(L, LUA_ENV); /* create env name */
+ luaC_fix(L, obj2gco(e)); /* never collect this name */
+ for (i=0; i<NUM_RESERVED; i++) {
+ TString *ts = luaS_new(L, luaX_tokens[i]);
+ luaC_fix(L, obj2gco(ts)); /* reserved words are never collected */
+ ts->extra = cast_byte(i+1); /* reserved word */
+ }
+}
+
+
+const char *luaX_token2str (LexState *ls, int token) {
+ if (token < FIRST_RESERVED) { /* single-byte symbols? */
+ if (lisprint(token))
+ return luaO_pushfstring(ls->L, "'%c'", token);
+ else /* control character */
+ return luaO_pushfstring(ls->L, "'<\\%d>'", token);
+ }
+ else {
+ const char *s = luaX_tokens[token - FIRST_RESERVED];
+ if (token < TK_EOS) /* fixed format (symbols and reserved words)? */
+ return luaO_pushfstring(ls->L, "'%s'", s);
+ else /* names, strings, and numerals */
+ return s;
+ }
+}
+
+
+static const char *txtToken (LexState *ls, int token) {
+ switch (token) {
+ case TK_NAME: case TK_STRING:
+ case TK_FLT: case TK_INT:
+ save(ls, '\0');
+ return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff));
+ default:
+ return luaX_token2str(ls, token);
+ }
+}
+
+
+static l_noret lexerror (LexState *ls, const char *msg, int token) {
+ msg = luaG_addinfo(ls->L, msg, ls->source, ls->linenumber);
+ if (token)
+ luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token));
+ luaD_throw(ls->L, LUA_ERRSYNTAX);
+}
+
+
+l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
+ lexerror(ls, msg, ls->t.token);
+}
+
+
+/*
+** Creates a new string and anchors it in scanner's table so that it
+** will not be collected until the end of the compilation; by that time
+** it should be anchored somewhere. It also internalizes long strings,
+** ensuring there is only one copy of each unique string. The table
+** here is used as a set: the string enters as the key, while its value
+** is irrelevant. We use the string itself as the value only because it
+** is a TValue readily available. Later, the code generation can change
+** this value.
+*/
+TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
+ lua_State *L = ls->L;
+ TString *ts = luaS_newlstr(L, str, l); /* create new string */
+ const TValue *o = luaH_getstr(ls->h, ts);
+ if (!ttisnil(o)) /* string already present? */
+ ts = keystrval(nodefromval(o)); /* get saved copy */
+ else { /* not in use yet */
+ TValue *stv = s2v(L->top.p++); /* reserve stack space for string */
+ setsvalue(L, stv, ts); /* temporarily anchor the string */
+ luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */
+ /* table is not a metatable, so it does not need to invalidate cache */
+ luaC_checkGC(L);
+ L->top.p--; /* remove string from stack */
+ }
+ return ts;
+}
+
+
+/*
+** increment line number and skips newline sequence (any of
+** \n, \r, \n\r, or \r\n)
+*/
+static void inclinenumber (LexState *ls) {
+ int old = ls->current;
+ lua_assert(currIsNewline(ls));
+ next(ls); /* skip '\n' or '\r' */
+ if (currIsNewline(ls) && ls->current != old)
+ next(ls); /* skip '\n\r' or '\r\n' */
+ if (++ls->linenumber >= MAX_INT)
+ lexerror(ls, "chunk has too many lines", 0);
+}
+
+
+void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source,
+ int firstchar) {
+ ls->t.token = 0;
+ ls->L = L;
+ ls->current = firstchar;
+ ls->lookahead.token = TK_EOS; /* no look-ahead token */
+ ls->z = z;
+ ls->fs = NULL;
+ ls->linenumber = 1;
+ ls->lastline = 1;
+ ls->source = source;
+ ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */
+ luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */
+}
+
+
+
+/*
+** =======================================================
+** LEXICAL ANALYZER
+** =======================================================
+*/
+
+
+static int check_next1 (LexState *ls, int c) {
+ if (ls->current == c) {
+ next(ls);
+ return 1;
+ }
+ else return 0;
+}
+
+
+/*
+** Check whether current char is in set 'set' (with two chars) and
+** saves it
+*/
+static int check_next2 (LexState *ls, const char *set) {
+ lua_assert(set[2] == '\0');
+ if (ls->current == set[0] || ls->current == set[1]) {
+ save_and_next(ls);
+ return 1;
+ }
+ else return 0;
+}
+
+
+/* LUA_NUMBER */
+/*
+** This function is quite liberal in what it accepts, as 'luaO_str2num'
+** will reject ill-formed numerals. Roughly, it accepts the following
+** pattern:
+**
+** %d(%x|%.|([Ee][+-]?))* | 0[Xx](%x|%.|([Pp][+-]?))*
+**
+** The only tricky part is to accept [+-] only after a valid exponent
+** mark, to avoid reading '3-4' or '0xe+1' as a single number.
+**
+** The caller might have already read an initial dot.
+*/
+static int read_numeral (LexState *ls, SemInfo *seminfo) {
+ TValue obj;
+ const char *expo = "Ee";
+ int first = ls->current;
+ lua_assert(lisdigit(ls->current));
+ save_and_next(ls);
+ if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */
+ expo = "Pp";
+ for (;;) {
+ if (check_next2(ls, expo)) /* exponent mark? */
+ check_next2(ls, "-+"); /* optional exponent sign */
+ else if (lisxdigit(ls->current) || ls->current == '.') /* '%x|%.' */
+ save_and_next(ls);
+ else break;
+ }
+ if (lislalpha(ls->current)) /* is numeral touching a letter? */
+ save_and_next(ls); /* force an error */
+ save(ls, '\0');
+ if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */
+ lexerror(ls, "malformed number", TK_FLT);
+ if (ttisinteger(&obj)) {
+ seminfo->i = ivalue(&obj);
+ return TK_INT;
+ }
+ else {
+ lua_assert(ttisfloat(&obj));
+ seminfo->r = fltvalue(&obj);
+ return TK_FLT;
+ }
+}
+
+
+/*
+** read a sequence '[=*[' or ']=*]', leaving the last bracket. If
+** sequence is well formed, return its number of '='s + 2; otherwise,
+** return 1 if it is a single bracket (no '='s and no 2nd bracket);
+** otherwise (an unfinished '[==...') return 0.
+*/
+static size_t skip_sep (LexState *ls) {
+ size_t count = 0;
+ int s = ls->current;
+ lua_assert(s == '[' || s == ']');
+ save_and_next(ls);
+ while (ls->current == '=') {
+ save_and_next(ls);
+ count++;
+ }
+ return (ls->current == s) ? count + 2
+ : (count == 0) ? 1
+ : 0;
+}
+
+
+static void read_long_string (LexState *ls, SemInfo *seminfo, size_t sep) {
+ int line = ls->linenumber; /* initial line (for error message) */
+ save_and_next(ls); /* skip 2nd '[' */
+ if (currIsNewline(ls)) /* string starts with a newline? */
+ inclinenumber(ls); /* skip it */
+ for (;;) {
+ switch (ls->current) {
+ case EOZ: { /* error */
+ const char *what = (seminfo ? "string" : "comment");
+ const char *msg = luaO_pushfstring(ls->L,
+ "unfinished long %s (starting at line %d)", what, line);
+ lexerror(ls, msg, TK_EOS);
+ break; /* to avoid warnings */
+ }
+ case ']': {
+ if (skip_sep(ls) == sep) {
+ save_and_next(ls); /* skip 2nd ']' */
+ goto endloop;
+ }
+ break;
+ }
+ case '\n': case '\r': {
+ save(ls, '\n');
+ inclinenumber(ls);
+ if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */
+ break;
+ }
+ default: {
+ if (seminfo) save_and_next(ls);
+ else next(ls);
+ }
+ }
+ } endloop:
+ if (seminfo)
+ seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep,
+ luaZ_bufflen(ls->buff) - 2 * sep);
+}
+
+
+static void esccheck (LexState *ls, int c, const char *msg) {
+ if (!c) {
+ if (ls->current != EOZ)
+ save_and_next(ls); /* add current to buffer for error message */
+ lexerror(ls, msg, TK_STRING);
+ }
+}
+
+
+static int gethexa (LexState *ls) {
+ save_and_next(ls);
+ esccheck (ls, lisxdigit(ls->current), "hexadecimal digit expected");
+ return luaO_hexavalue(ls->current);
+}
+
+
+static int readhexaesc (LexState *ls) {
+ int r = gethexa(ls);
+ r = (r << 4) + gethexa(ls);
+ luaZ_buffremove(ls->buff, 2); /* remove saved chars from buffer */
+ return r;
+}
+
+
+static unsigned long readutf8esc (LexState *ls) {
+ unsigned long r;
+ int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */
+ save_and_next(ls); /* skip 'u' */
+ esccheck(ls, ls->current == '{', "missing '{'");
+ r = gethexa(ls); /* must have at least one digit */
+ while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) {
+ i++;
+ esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large");
+ r = (r << 4) + luaO_hexavalue(ls->current);
+ }
+ esccheck(ls, ls->current == '}', "missing '}'");
+ next(ls); /* skip '}' */
+ luaZ_buffremove(ls->buff, i); /* remove saved chars from buffer */
+ return r;
+}
+
+
+static void utf8esc (LexState *ls) {
+ char buff[UTF8BUFFSZ];
+ int n = luaO_utf8esc(buff, readutf8esc(ls));
+ for (; n > 0; n--) /* add 'buff' to string */
+ save(ls, buff[UTF8BUFFSZ - n]);
+}
+
+
+static int readdecesc (LexState *ls) {
+ int i;
+ int r = 0; /* result accumulator */
+ for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */
+ r = 10*r + ls->current - '0';
+ save_and_next(ls);
+ }
+ esccheck(ls, r <= UCHAR_MAX, "decimal escape too large");
+ luaZ_buffremove(ls->buff, i); /* remove read digits from buffer */
+ return r;
+}
+
+
+static void read_string (LexState *ls, int del, SemInfo *seminfo) {
+ save_and_next(ls); /* keep delimiter (for error messages) */
+ while (ls->current != del) {
+ switch (ls->current) {
+ case EOZ:
+ lexerror(ls, "unfinished string", TK_EOS);
+ break; /* to avoid warnings */
+ case '\n':
+ case '\r':
+ lexerror(ls, "unfinished string", TK_STRING);
+ break; /* to avoid warnings */
+ case '\\': { /* escape sequences */
+ int c; /* final character to be saved */
+ save_and_next(ls); /* keep '\\' for error messages */
+ switch (ls->current) {
+ case 'a': c = '\a'; goto read_save;
+ case 'b': c = '\b'; goto read_save;
+ case 'f': c = '\f'; goto read_save;
+ case 'n': c = '\n'; goto read_save;
+ case 'r': c = '\r'; goto read_save;
+ case 't': c = '\t'; goto read_save;
+ case 'v': c = '\v'; goto read_save;
+ case 'x': c = readhexaesc(ls); goto read_save;
+ case 'u': utf8esc(ls); goto no_save;
+ case '\n': case '\r':
+ inclinenumber(ls); c = '\n'; goto only_save;
+ case '\\': case '\"': case '\'':
+ c = ls->current; goto read_save;
+ case EOZ: goto no_save; /* will raise an error next loop */
+ case 'z': { /* zap following span of spaces */
+ luaZ_buffremove(ls->buff, 1); /* remove '\\' */
+ next(ls); /* skip the 'z' */
+ while (lisspace(ls->current)) {
+ if (currIsNewline(ls)) inclinenumber(ls);
+ else next(ls);
+ }
+ goto no_save;
+ }
+ default: {
+ esccheck(ls, lisdigit(ls->current), "invalid escape sequence");
+ c = readdecesc(ls); /* digital escape '\ddd' */
+ goto only_save;
+ }
+ }
+ read_save:
+ next(ls);
+ /* go through */
+ only_save:
+ luaZ_buffremove(ls->buff, 1); /* remove '\\' */
+ save(ls, c);
+ /* go through */
+ no_save: break;
+ }
+ default:
+ save_and_next(ls);
+ }
+ }
+ save_and_next(ls); /* skip delimiter */
+ seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1,
+ luaZ_bufflen(ls->buff) - 2);
+}
+
+
+static int llex (LexState *ls, SemInfo *seminfo) {
+ luaZ_resetbuffer(ls->buff);
+ for (;;) {
+ switch (ls->current) {
+ case '\n': case '\r': { /* line breaks */
+ inclinenumber(ls);
+ break;
+ }
+ case ' ': case '\f': case '\t': case '\v': { /* spaces */
+ next(ls);
+ break;
+ }
+ case '-': { /* '-' or '--' (comment) */
+ next(ls);
+ if (ls->current != '-') return '-';
+ /* else is a comment */
+ next(ls);
+ if (ls->current == '[') { /* long comment? */
+ size_t sep = skip_sep(ls);
+ luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */
+ if (sep >= 2) {
+ read_long_string(ls, NULL, sep); /* skip long comment */
+ luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */
+ break;
+ }
+ }
+ /* else short comment */
+ while (!currIsNewline(ls) && ls->current != EOZ)
+ next(ls); /* skip until end of line (or end of file) */
+ break;
+ }
+ case '[': { /* long string or simply '[' */
+ size_t sep = skip_sep(ls);
+ if (sep >= 2) {
+ read_long_string(ls, seminfo, sep);
+ return TK_STRING;
+ }
+ else if (sep == 0) /* '[=...' missing second bracket? */
+ lexerror(ls, "invalid long string delimiter", TK_STRING);
+ return '[';
+ }
+ case '=': {
+ next(ls);
+ if (check_next1(ls, '=')) return TK_EQ; /* '==' */
+ else return '=';
+ }
+ case '<': {
+ next(ls);
+ if (check_next1(ls, '=')) return TK_LE; /* '<=' */
+ else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */
+ else return '<';
+ }
+ case '>': {
+ next(ls);
+ if (check_next1(ls, '=')) return TK_GE; /* '>=' */
+ else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */
+ else return '>';
+ }
+ case '/': {
+ next(ls);
+ if (check_next1(ls, '/')) return TK_IDIV; /* '//' */
+ else return '/';
+ }
+ case '~': {
+ next(ls);
+ if (check_next1(ls, '=')) return TK_NE; /* '~=' */
+ else return '~';
+ }
+ case ':': {
+ next(ls);
+ if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */
+ else return ':';
+ }
+ case '"': case '\'': { /* short literal strings */
+ read_string(ls, ls->current, seminfo);
+ return TK_STRING;
+ }
+ case '.': { /* '.', '..', '...', or number */
+ save_and_next(ls);
+ if (check_next1(ls, '.')) {
+ if (check_next1(ls, '.'))
+ return TK_DOTS; /* '...' */
+ else return TK_CONCAT; /* '..' */
+ }
+ else if (!lisdigit(ls->current)) return '.';
+ else return read_numeral(ls, seminfo);
+ }
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': {
+ return read_numeral(ls, seminfo);
+ }
+ case EOZ: {
+ return TK_EOS;
+ }
+ default: {
+ if (lislalpha(ls->current)) { /* identifier or reserved word? */
+ TString *ts;
+ do {
+ save_and_next(ls);
+ } while (lislalnum(ls->current));
+ ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
+ luaZ_bufflen(ls->buff));
+ seminfo->ts = ts;
+ if (isreserved(ts)) /* reserved word? */
+ return ts->extra - 1 + FIRST_RESERVED;
+ else {
+ return TK_NAME;
+ }
+ }
+ else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */
+ int c = ls->current;
+ next(ls);
+ return c;
+ }
+ }
+ }
+ }
+}
+
+
+void luaX_next (LexState *ls) {
+ ls->lastline = ls->linenumber;
+ if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
+ ls->t = ls->lookahead; /* use this one */
+ ls->lookahead.token = TK_EOS; /* and discharge it */
+ }
+ else
+ ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
+}
+
+
+int luaX_lookahead (LexState *ls) {
+ lua_assert(ls->lookahead.token == TK_EOS);
+ ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
+ return ls->lookahead.token;
+}
+
diff --git a/src/libs/3rdparty/lua/src/llex.h b/src/libs/3rdparty/lua/src/llex.h
new file mode 100644
index 0000000000..389d2f8635
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/llex.h
@@ -0,0 +1,91 @@
+/*
+** $Id: llex.h $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llex_h
+#define llex_h
+
+#include <limits.h>
+
+#include "lobject.h"
+#include "lzio.h"
+
+
+/*
+** Single-char tokens (terminal symbols) are represented by their own
+** numeric code. Other tokens start at the following value.
+*/
+#define FIRST_RESERVED (UCHAR_MAX + 1)
+
+
+#if !defined(LUA_ENV)
+#define LUA_ENV "_ENV"
+#endif
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER RESERVED"
+*/
+enum RESERVED {
+ /* terminal symbols denoted by reserved words */
+ TK_AND = FIRST_RESERVED, TK_BREAK,
+ TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
+ TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
+ TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
+ /* other terminal symbols */
+ TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE,
+ TK_SHL, TK_SHR,
+ TK_DBCOLON, TK_EOS,
+ TK_FLT, TK_INT, TK_NAME, TK_STRING
+};
+
+/* number of reserved words */
+#define NUM_RESERVED (cast_int(TK_WHILE-FIRST_RESERVED + 1))
+
+
+typedef union {
+ lua_Number r;
+ lua_Integer i;
+ TString *ts;
+} SemInfo; /* semantics information */
+
+
+typedef struct Token {
+ int token;
+ SemInfo seminfo;
+} Token;
+
+
+/* state of the lexer plus state of the parser when shared by all
+ functions */
+typedef struct LexState {
+ int current; /* current character (charint) */
+ int linenumber; /* input line counter */
+ int lastline; /* line of last token 'consumed' */
+ Token t; /* current token */
+ Token lookahead; /* look ahead token */
+ struct FuncState *fs; /* current function (parser) */
+ struct lua_State *L;
+ ZIO *z; /* input stream */
+ Mbuffer *buff; /* buffer for tokens */
+ Table *h; /* to avoid collection/reuse strings */
+ struct Dyndata *dyd; /* dynamic structures used by the parser */
+ TString *source; /* current source name */
+ TString *envn; /* environment variable name */
+} LexState;
+
+
+LUAI_FUNC void luaX_init (lua_State *L);
+LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z,
+ TString *source, int firstchar);
+LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l);
+LUAI_FUNC void luaX_next (LexState *ls);
+LUAI_FUNC int luaX_lookahead (LexState *ls);
+LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s);
+LUAI_FUNC const char *luaX_token2str (LexState *ls, int token);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/llimits.h b/src/libs/3rdparty/lua/src/llimits.h
new file mode 100644
index 0000000000..1c826f7be2
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/llimits.h
@@ -0,0 +1,380 @@
+/*
+** $Id: llimits.h $
+** Limits, basic types, and some other 'installation-dependent' definitions
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llimits_h
+#define llimits_h
+
+
+#include <limits.h>
+#include <stddef.h>
+
+
+#include "lua.h"
+
+
+/*
+** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count
+** the total memory used by Lua (in bytes). Usually, 'size_t' and
+** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines.
+*/
+#if defined(LUAI_MEM) /* { external definitions? */
+typedef LUAI_UMEM lu_mem;
+typedef LUAI_MEM l_mem;
+#elif LUAI_IS32INT /* }{ */
+typedef size_t lu_mem;
+typedef ptrdiff_t l_mem;
+#else /* 16-bit ints */ /* }{ */
+typedef unsigned long lu_mem;
+typedef long l_mem;
+#endif /* } */
+
+
+/* chars used as small naturals (so that 'char' is reserved for characters) */
+typedef unsigned char lu_byte;
+typedef signed char ls_byte;
+
+
+/* maximum value for size_t */
+#define MAX_SIZET ((size_t)(~(size_t)0))
+
+/* maximum size visible for Lua (must be representable in a lua_Integer) */
+#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
+ : (size_t)(LUA_MAXINTEGER))
+
+
+#define MAX_LUMEM ((lu_mem)(~(lu_mem)0))
+
+#define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1))
+
+
+#define MAX_INT INT_MAX /* maximum value of an int */
+
+
+/*
+** floor of the log2 of the maximum signed value for integral type 't'.
+** (That is, maximum 'n' such that '2^n' fits in the given signed type.)
+*/
+#define log2maxs(t) (sizeof(t) * 8 - 2)
+
+
+/*
+** test whether an unsigned value is a power of 2 (or zero)
+*/
+#define ispow2(x) (((x) & ((x) - 1)) == 0)
+
+
+/* number of chars of a literal string without the ending \0 */
+#define LL(x) (sizeof(x)/sizeof(char) - 1)
+
+
+/*
+** conversion of pointer to unsigned integer: this is for hashing only;
+** there is no problem if the integer cannot hold the whole pointer
+** value. (In strict ISO C this may cause undefined behavior, but no
+** actual machine seems to bother.)
+*/
+#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
+ __STDC_VERSION__ >= 199901L
+#include <stdint.h>
+#if defined(UINTPTR_MAX) /* even in C99 this type is optional */
+#define L_P2I uintptr_t
+#else /* no 'intptr'? */
+#define L_P2I uintmax_t /* use the largest available integer */
+#endif
+#else /* C89 option */
+#define L_P2I size_t
+#endif
+
+#define point2uint(p) ((unsigned int)((L_P2I)(p) & UINT_MAX))
+
+
+
+/* types of 'usual argument conversions' for lua_Number and lua_Integer */
+typedef LUAI_UACNUMBER l_uacNumber;
+typedef LUAI_UACINT l_uacInt;
+
+
+/*
+** Internal assertions for in-house debugging
+*/
+#if defined LUAI_ASSERT
+#undef NDEBUG
+#include <assert.h>
+#define lua_assert(c) assert(c)
+#endif
+
+#if defined(lua_assert)
+#define check_exp(c,e) (lua_assert(c), (e))
+/* to avoid problems with conditions too long */
+#define lua_longassert(c) ((c) ? (void)0 : lua_assert(0))
+#else
+#define lua_assert(c) ((void)0)
+#define check_exp(c,e) (e)
+#define lua_longassert(c) ((void)0)
+#endif
+
+/*
+** assertion for checking API calls
+*/
+#if !defined(luai_apicheck)
+#define luai_apicheck(l,e) ((void)l, lua_assert(e))
+#endif
+
+#define api_check(l,e,msg) luai_apicheck(l,(e) && msg)
+
+
+/* macro to avoid warnings about unused variables */
+#if !defined(UNUSED)
+#define UNUSED(x) ((void)(x))
+#endif
+
+
+/* type casts (a macro highlights casts in the code) */
+#define cast(t, exp) ((t)(exp))
+
+#define cast_void(i) cast(void, (i))
+#define cast_voidp(i) cast(void *, (i))
+#define cast_num(i) cast(lua_Number, (i))
+#define cast_int(i) cast(int, (i))
+#define cast_uint(i) cast(unsigned int, (i))
+#define cast_byte(i) cast(lu_byte, (i))
+#define cast_uchar(i) cast(unsigned char, (i))
+#define cast_char(i) cast(char, (i))
+#define cast_charp(i) cast(char *, (i))
+#define cast_sizet(i) cast(size_t, (i))
+
+
+/* cast a signed lua_Integer to lua_Unsigned */
+#if !defined(l_castS2U)
+#define l_castS2U(i) ((lua_Unsigned)(i))
+#endif
+
+/*
+** cast a lua_Unsigned to a signed lua_Integer; this cast is
+** not strict ISO C, but two-complement architectures should
+** work fine.
+*/
+#if !defined(l_castU2S)
+#define l_castU2S(i) ((lua_Integer)(i))
+#endif
+
+
+/*
+** non-return type
+*/
+#if !defined(l_noret)
+
+#if defined(__GNUC__)
+#define l_noret void __attribute__((noreturn))
+#elif defined(_MSC_VER) && _MSC_VER >= 1200
+#define l_noret void __declspec(noreturn)
+#else
+#define l_noret void
+#endif
+
+#endif
+
+
+/*
+** Inline functions
+*/
+#if !defined(LUA_USE_C89)
+#define l_inline inline
+#elif defined(__GNUC__)
+#define l_inline __inline__
+#else
+#define l_inline /* empty */
+#endif
+
+#define l_sinline static l_inline
+
+
+/*
+** type for virtual-machine instructions;
+** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
+*/
+#if LUAI_IS32INT
+typedef unsigned int l_uint32;
+#else
+typedef unsigned long l_uint32;
+#endif
+
+typedef l_uint32 Instruction;
+
+
+
+/*
+** Maximum length for short strings, that is, strings that are
+** internalized. (Cannot be smaller than reserved words or tags for
+** metamethods, as these strings must be internalized;
+** #("function") = 8, #("__newindex") = 10.)
+*/
+#if !defined(LUAI_MAXSHORTLEN)
+#define LUAI_MAXSHORTLEN 40
+#endif
+
+
+/*
+** Initial size for the string table (must be power of 2).
+** The Lua core alone registers ~50 strings (reserved words +
+** metaevent keys + a few others). Libraries would typically add
+** a few dozens more.
+*/
+#if !defined(MINSTRTABSIZE)
+#define MINSTRTABSIZE 128
+#endif
+
+
+/*
+** Size of cache for strings in the API. 'N' is the number of
+** sets (better be a prime) and "M" is the size of each set (M == 1
+** makes a direct cache.)
+*/
+#if !defined(STRCACHE_N)
+#define STRCACHE_N 53
+#define STRCACHE_M 2
+#endif
+
+
+/* minimum size for string buffer */
+#if !defined(LUA_MINBUFFER)
+#define LUA_MINBUFFER 32
+#endif
+
+
+/*
+** Maximum depth for nested C calls, syntactical nested non-terminals,
+** and other features implemented through recursion in C. (Value must
+** fit in a 16-bit unsigned integer. It must also be compatible with
+** the size of the C stack.)
+*/
+#if !defined(LUAI_MAXCCALLS)
+#define LUAI_MAXCCALLS 200
+#endif
+
+
+/*
+** macros that are executed whenever program enters the Lua core
+** ('lua_lock') and leaves the core ('lua_unlock')
+*/
+#if !defined(lua_lock)
+#define lua_lock(L) ((void) 0)
+#define lua_unlock(L) ((void) 0)
+#endif
+
+/*
+** macro executed during Lua functions at points where the
+** function can yield.
+*/
+#if !defined(luai_threadyield)
+#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
+#endif
+
+
+/*
+** these macros allow user-specific actions when a thread is
+** created/deleted/resumed/yielded.
+*/
+#if !defined(luai_userstateopen)
+#define luai_userstateopen(L) ((void)L)
+#endif
+
+#if !defined(luai_userstateclose)
+#define luai_userstateclose(L) ((void)L)
+#endif
+
+#if !defined(luai_userstatethread)
+#define luai_userstatethread(L,L1) ((void)L)
+#endif
+
+#if !defined(luai_userstatefree)
+#define luai_userstatefree(L,L1) ((void)L)
+#endif
+
+#if !defined(luai_userstateresume)
+#define luai_userstateresume(L,n) ((void)L)
+#endif
+
+#if !defined(luai_userstateyield)
+#define luai_userstateyield(L,n) ((void)L)
+#endif
+
+
+
+/*
+** The luai_num* macros define the primitive operations over numbers.
+*/
+
+/* floor division (defined as 'floor(a/b)') */
+#if !defined(luai_numidiv)
+#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b)))
+#endif
+
+/* float division */
+#if !defined(luai_numdiv)
+#define luai_numdiv(L,a,b) ((a)/(b))
+#endif
+
+/*
+** modulo: defined as 'a - floor(a/b)*b'; the direct computation
+** using this definition has several problems with rounding errors,
+** so it is better to use 'fmod'. 'fmod' gives the result of
+** 'a - trunc(a/b)*b', and therefore must be corrected when
+** 'trunc(a/b) ~= floor(a/b)'. That happens when the division has a
+** non-integer negative result: non-integer result is equivalent to
+** a non-zero remainder 'm'; negative result is equivalent to 'a' and
+** 'b' with different signs, or 'm' and 'b' with different signs
+** (as the result 'm' of 'fmod' has the same sign of 'a').
+*/
+#if !defined(luai_nummod)
+#define luai_nummod(L,a,b,m) \
+ { (void)L; (m) = l_mathop(fmod)(a,b); \
+ if (((m) > 0) ? (b) < 0 : ((m) < 0 && (b) > 0)) (m) += (b); }
+#endif
+
+/* exponentiation */
+#if !defined(luai_numpow)
+#define luai_numpow(L,a,b) \
+ ((void)L, (b == 2) ? (a)*(a) : l_mathop(pow)(a,b))
+#endif
+
+/* the others are quite standard operations */
+#if !defined(luai_numadd)
+#define luai_numadd(L,a,b) ((a)+(b))
+#define luai_numsub(L,a,b) ((a)-(b))
+#define luai_nummul(L,a,b) ((a)*(b))
+#define luai_numunm(L,a) (-(a))
+#define luai_numeq(a,b) ((a)==(b))
+#define luai_numlt(a,b) ((a)<(b))
+#define luai_numle(a,b) ((a)<=(b))
+#define luai_numgt(a,b) ((a)>(b))
+#define luai_numge(a,b) ((a)>=(b))
+#define luai_numisnan(a) (!luai_numeq((a), (a)))
+#endif
+
+
+
+
+
+/*
+** macro to control inclusion of some hard tests on stack reallocation
+*/
+#if !defined(HARDSTACKTESTS)
+#define condmovestack(L,pre,pos) ((void)0)
+#else
+/* realloc stack keeping its size */
+#define condmovestack(L,pre,pos) \
+ { int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; }
+#endif
+
+#if !defined(HARDMEMTESTS)
+#define condchangemem(L,pre,pos) ((void)0)
+#else
+#define condchangemem(L,pre,pos) \
+ { if (gcrunning(G(L))) { pre; luaC_fullgc(L, 0); pos; } }
+#endif
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lmathlib.c b/src/libs/3rdparty/lua/src/lmathlib.c
new file mode 100644
index 0000000000..d0b1e1e5d6
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lmathlib.c
@@ -0,0 +1,764 @@
+/*
+** $Id: lmathlib.c $
+** Standard mathematical library
+** See Copyright Notice in lua.h
+*/
+
+#define lmathlib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+#undef PI
+#define PI (l_mathop(3.141592653589793238462643383279502884))
+
+
+static int math_abs (lua_State *L) {
+ if (lua_isinteger(L, 1)) {
+ lua_Integer n = lua_tointeger(L, 1);
+ if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n);
+ lua_pushinteger(L, n);
+ }
+ else
+ lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_sin (lua_State *L) {
+ lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_cos (lua_State *L) {
+ lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_tan (lua_State *L) {
+ lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_asin (lua_State *L) {
+ lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_acos (lua_State *L) {
+ lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_atan (lua_State *L) {
+ lua_Number y = luaL_checknumber(L, 1);
+ lua_Number x = luaL_optnumber(L, 2, 1);
+ lua_pushnumber(L, l_mathop(atan2)(y, x));
+ return 1;
+}
+
+
+static int math_toint (lua_State *L) {
+ int valid;
+ lua_Integer n = lua_tointegerx(L, 1, &valid);
+ if (l_likely(valid))
+ lua_pushinteger(L, n);
+ else {
+ luaL_checkany(L, 1);
+ luaL_pushfail(L); /* value is not convertible to integer */
+ }
+ return 1;
+}
+
+
+static void pushnumint (lua_State *L, lua_Number d) {
+ lua_Integer n;
+ if (lua_numbertointeger(d, &n)) /* does 'd' fit in an integer? */
+ lua_pushinteger(L, n); /* result is integer */
+ else
+ lua_pushnumber(L, d); /* result is float */
+}
+
+
+static int math_floor (lua_State *L) {
+ if (lua_isinteger(L, 1))
+ lua_settop(L, 1); /* integer is its own floor */
+ else {
+ lua_Number d = l_mathop(floor)(luaL_checknumber(L, 1));
+ pushnumint(L, d);
+ }
+ return 1;
+}
+
+
+static int math_ceil (lua_State *L) {
+ if (lua_isinteger(L, 1))
+ lua_settop(L, 1); /* integer is its own ceil */
+ else {
+ lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1));
+ pushnumint(L, d);
+ }
+ return 1;
+}
+
+
+static int math_fmod (lua_State *L) {
+ if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) {
+ lua_Integer d = lua_tointeger(L, 2);
+ if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */
+ luaL_argcheck(L, d != 0, 2, "zero");
+ lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */
+ }
+ else
+ lua_pushinteger(L, lua_tointeger(L, 1) % d);
+ }
+ else
+ lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1),
+ luaL_checknumber(L, 2)));
+ return 1;
+}
+
+
+/*
+** next function does not use 'modf', avoiding problems with 'double*'
+** (which is not compatible with 'float*') when lua_Number is not
+** 'double'.
+*/
+static int math_modf (lua_State *L) {
+ if (lua_isinteger(L ,1)) {
+ lua_settop(L, 1); /* number is its own integer part */
+ lua_pushnumber(L, 0); /* no fractional part */
+ }
+ else {
+ lua_Number n = luaL_checknumber(L, 1);
+ /* integer part (rounds toward zero) */
+ lua_Number ip = (n < 0) ? l_mathop(ceil)(n) : l_mathop(floor)(n);
+ pushnumint(L, ip);
+ /* fractional part (test needed for inf/-inf) */
+ lua_pushnumber(L, (n == ip) ? l_mathop(0.0) : (n - ip));
+ }
+ return 2;
+}
+
+
+static int math_sqrt (lua_State *L) {
+ lua_pushnumber(L, l_mathop(sqrt)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+
+static int math_ult (lua_State *L) {
+ lua_Integer a = luaL_checkinteger(L, 1);
+ lua_Integer b = luaL_checkinteger(L, 2);
+ lua_pushboolean(L, (lua_Unsigned)a < (lua_Unsigned)b);
+ return 1;
+}
+
+static int math_log (lua_State *L) {
+ lua_Number x = luaL_checknumber(L, 1);
+ lua_Number res;
+ if (lua_isnoneornil(L, 2))
+ res = l_mathop(log)(x);
+ else {
+ lua_Number base = luaL_checknumber(L, 2);
+#if !defined(LUA_USE_C89)
+ if (base == l_mathop(2.0))
+ res = l_mathop(log2)(x);
+ else
+#endif
+ if (base == l_mathop(10.0))
+ res = l_mathop(log10)(x);
+ else
+ res = l_mathop(log)(x)/l_mathop(log)(base);
+ }
+ lua_pushnumber(L, res);
+ return 1;
+}
+
+static int math_exp (lua_State *L) {
+ lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_deg (lua_State *L) {
+ lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI));
+ return 1;
+}
+
+static int math_rad (lua_State *L) {
+ lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0)));
+ return 1;
+}
+
+
+static int math_min (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int imin = 1; /* index of current minimum value */
+ int i;
+ luaL_argcheck(L, n >= 1, 1, "value expected");
+ for (i = 2; i <= n; i++) {
+ if (lua_compare(L, i, imin, LUA_OPLT))
+ imin = i;
+ }
+ lua_pushvalue(L, imin);
+ return 1;
+}
+
+
+static int math_max (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int imax = 1; /* index of current maximum value */
+ int i;
+ luaL_argcheck(L, n >= 1, 1, "value expected");
+ for (i = 2; i <= n; i++) {
+ if (lua_compare(L, imax, i, LUA_OPLT))
+ imax = i;
+ }
+ lua_pushvalue(L, imax);
+ return 1;
+}
+
+
+static int math_type (lua_State *L) {
+ if (lua_type(L, 1) == LUA_TNUMBER)
+ lua_pushstring(L, (lua_isinteger(L, 1)) ? "integer" : "float");
+ else {
+ luaL_checkany(L, 1);
+ luaL_pushfail(L);
+ }
+ return 1;
+}
+
+
+
+/*
+** {==================================================================
+** Pseudo-Random Number Generator based on 'xoshiro256**'.
+** ===================================================================
+*/
+
+/* number of binary digits in the mantissa of a float */
+#define FIGS l_floatatt(MANT_DIG)
+
+#if FIGS > 64
+/* there are only 64 random bits; use them all */
+#undef FIGS
+#define FIGS 64
+#endif
+
+
+/*
+** LUA_RAND32 forces the use of 32-bit integers in the implementation
+** of the PRN generator (mainly for testing).
+*/
+#if !defined(LUA_RAND32) && !defined(Rand64)
+
+/* try to find an integer type with at least 64 bits */
+
+#if ((ULONG_MAX >> 31) >> 31) >= 3
+
+/* 'long' has at least 64 bits */
+#define Rand64 unsigned long
+
+#elif !defined(LUA_USE_C89) && defined(LLONG_MAX)
+
+/* there is a 'long long' type (which must have at least 64 bits) */
+#define Rand64 unsigned long long
+
+#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3
+
+/* 'lua_Unsigned' has at least 64 bits */
+#define Rand64 lua_Unsigned
+
+#endif
+
+#endif
+
+
+#if defined(Rand64) /* { */
+
+/*
+** Standard implementation, using 64-bit integers.
+** If 'Rand64' has more than 64 bits, the extra bits do not interfere
+** with the 64 initial bits, except in a right shift. Moreover, the
+** final result has to discard the extra bits.
+*/
+
+/* avoid using extra bits when needed */
+#define trim64(x) ((x) & 0xffffffffffffffffu)
+
+
+/* rotate left 'x' by 'n' bits */
+static Rand64 rotl (Rand64 x, int n) {
+ return (x << n) | (trim64(x) >> (64 - n));
+}
+
+static Rand64 nextrand (Rand64 *state) {
+ Rand64 state0 = state[0];
+ Rand64 state1 = state[1];
+ Rand64 state2 = state[2] ^ state0;
+ Rand64 state3 = state[3] ^ state1;
+ Rand64 res = rotl(state1 * 5, 7) * 9;
+ state[0] = state0 ^ state3;
+ state[1] = state1 ^ state2;
+ state[2] = state2 ^ (state1 << 17);
+ state[3] = rotl(state3, 45);
+ return res;
+}
+
+
+/* must take care to not shift stuff by more than 63 slots */
+
+
+/*
+** Convert bits from a random integer into a float in the
+** interval [0,1), getting the higher FIG bits from the
+** random unsigned integer and converting that to a float.
+*/
+
+/* must throw out the extra (64 - FIGS) bits */
+#define shift64_FIG (64 - FIGS)
+
+/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */
+#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1)))
+
+static lua_Number I2d (Rand64 x) {
+ return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG;
+}
+
+/* convert a 'Rand64' to a 'lua_Unsigned' */
+#define I2UInt(x) ((lua_Unsigned)trim64(x))
+
+/* convert a 'lua_Unsigned' to a 'Rand64' */
+#define Int2I(x) ((Rand64)(x))
+
+
+#else /* no 'Rand64' }{ */
+
+/* get an integer with at least 32 bits */
+#if LUAI_IS32INT
+typedef unsigned int lu_int32;
+#else
+typedef unsigned long lu_int32;
+#endif
+
+
+/*
+** Use two 32-bit integers to represent a 64-bit quantity.
+*/
+typedef struct Rand64 {
+ lu_int32 h; /* higher half */
+ lu_int32 l; /* lower half */
+} Rand64;
+
+
+/*
+** If 'lu_int32' has more than 32 bits, the extra bits do not interfere
+** with the 32 initial bits, except in a right shift and comparisons.
+** Moreover, the final result has to discard the extra bits.
+*/
+
+/* avoid using extra bits when needed */
+#define trim32(x) ((x) & 0xffffffffu)
+
+
+/*
+** basic operations on 'Rand64' values
+*/
+
+/* build a new Rand64 value */
+static Rand64 packI (lu_int32 h, lu_int32 l) {
+ Rand64 result;
+ result.h = h;
+ result.l = l;
+ return result;
+}
+
+/* return i << n */
+static Rand64 Ishl (Rand64 i, int n) {
+ lua_assert(n > 0 && n < 32);
+ return packI((i.h << n) | (trim32(i.l) >> (32 - n)), i.l << n);
+}
+
+/* i1 ^= i2 */
+static void Ixor (Rand64 *i1, Rand64 i2) {
+ i1->h ^= i2.h;
+ i1->l ^= i2.l;
+}
+
+/* return i1 + i2 */
+static Rand64 Iadd (Rand64 i1, Rand64 i2) {
+ Rand64 result = packI(i1.h + i2.h, i1.l + i2.l);
+ if (trim32(result.l) < trim32(i1.l)) /* carry? */
+ result.h++;
+ return result;
+}
+
+/* return i * 5 */
+static Rand64 times5 (Rand64 i) {
+ return Iadd(Ishl(i, 2), i); /* i * 5 == (i << 2) + i */
+}
+
+/* return i * 9 */
+static Rand64 times9 (Rand64 i) {
+ return Iadd(Ishl(i, 3), i); /* i * 9 == (i << 3) + i */
+}
+
+/* return 'i' rotated left 'n' bits */
+static Rand64 rotl (Rand64 i, int n) {
+ lua_assert(n > 0 && n < 32);
+ return packI((i.h << n) | (trim32(i.l) >> (32 - n)),
+ (trim32(i.h) >> (32 - n)) | (i.l << n));
+}
+
+/* for offsets larger than 32, rotate right by 64 - offset */
+static Rand64 rotl1 (Rand64 i, int n) {
+ lua_assert(n > 32 && n < 64);
+ n = 64 - n;
+ return packI((trim32(i.h) >> n) | (i.l << (32 - n)),
+ (i.h << (32 - n)) | (trim32(i.l) >> n));
+}
+
+/*
+** implementation of 'xoshiro256**' algorithm on 'Rand64' values
+*/
+static Rand64 nextrand (Rand64 *state) {
+ Rand64 res = times9(rotl(times5(state[1]), 7));
+ Rand64 t = Ishl(state[1], 17);
+ Ixor(&state[2], state[0]);
+ Ixor(&state[3], state[1]);
+ Ixor(&state[1], state[2]);
+ Ixor(&state[0], state[3]);
+ Ixor(&state[2], t);
+ state[3] = rotl1(state[3], 45);
+ return res;
+}
+
+
+/*
+** Converts a 'Rand64' into a float.
+*/
+
+/* an unsigned 1 with proper type */
+#define UONE ((lu_int32)1)
+
+
+#if FIGS <= 32
+
+/* 2^(-FIGS) */
+#define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1)))
+
+/*
+** get up to 32 bits from higher half, shifting right to
+** throw out the extra bits.
+*/
+static lua_Number I2d (Rand64 x) {
+ lua_Number h = (lua_Number)(trim32(x.h) >> (32 - FIGS));
+ return h * scaleFIG;
+}
+
+#else /* 32 < FIGS <= 64 */
+
+/* must take care to not shift stuff by more than 31 slots */
+
+/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */
+#define scaleFIG \
+ (l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33)))
+
+/*
+** use FIGS - 32 bits from lower half, throwing out the other
+** (32 - (FIGS - 32)) = (64 - FIGS) bits
+*/
+#define shiftLOW (64 - FIGS)
+
+/*
+** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32)
+*/
+#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * l_mathop(2.0))
+
+
+static lua_Number I2d (Rand64 x) {
+ lua_Number h = (lua_Number)trim32(x.h) * shiftHI;
+ lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW);
+ return (h + l) * scaleFIG;
+}
+
+#endif
+
+
+/* convert a 'Rand64' to a 'lua_Unsigned' */
+static lua_Unsigned I2UInt (Rand64 x) {
+ return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l);
+}
+
+/* convert a 'lua_Unsigned' to a 'Rand64' */
+static Rand64 Int2I (lua_Unsigned n) {
+ return packI((lu_int32)((n >> 31) >> 1), (lu_int32)n);
+}
+
+#endif /* } */
+
+
+/*
+** A state uses four 'Rand64' values.
+*/
+typedef struct {
+ Rand64 s[4];
+} RanState;
+
+
+/*
+** Project the random integer 'ran' into the interval [0, n].
+** Because 'ran' has 2^B possible values, the projection can only be
+** uniform when the size of the interval is a power of 2 (exact
+** division). Otherwise, to get a uniform projection into [0, n], we
+** first compute 'lim', the smallest Mersenne number not smaller than
+** 'n'. We then project 'ran' into the interval [0, lim]. If the result
+** is inside [0, n], we are done. Otherwise, we try with another 'ran',
+** until we have a result inside the interval.
+*/
+static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n,
+ RanState *state) {
+ if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */
+ return ran & n; /* no bias */
+ else {
+ lua_Unsigned lim = n;
+ /* compute the smallest (2^b - 1) not smaller than 'n' */
+ lim |= (lim >> 1);
+ lim |= (lim >> 2);
+ lim |= (lim >> 4);
+ lim |= (lim >> 8);
+ lim |= (lim >> 16);
+#if (LUA_MAXUNSIGNED >> 31) >= 3
+ lim |= (lim >> 32); /* integer type has more than 32 bits */
+#endif
+ lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */
+ && lim >= n /* not smaller than 'n', */
+ && (lim >> 1) < n); /* and it is the smallest one */
+ while ((ran &= lim) > n) /* project 'ran' into [0..lim] */
+ ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */
+ return ran;
+ }
+}
+
+
+static int math_random (lua_State *L) {
+ lua_Integer low, up;
+ lua_Unsigned p;
+ RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
+ Rand64 rv = nextrand(state->s); /* next pseudo-random value */
+ switch (lua_gettop(L)) { /* check number of arguments */
+ case 0: { /* no arguments */
+ lua_pushnumber(L, I2d(rv)); /* float between 0 and 1 */
+ return 1;
+ }
+ case 1: { /* only upper limit */
+ low = 1;
+ up = luaL_checkinteger(L, 1);
+ if (up == 0) { /* single 0 as argument? */
+ lua_pushinteger(L, I2UInt(rv)); /* full random integer */
+ return 1;
+ }
+ break;
+ }
+ case 2: { /* lower and upper limits */
+ low = luaL_checkinteger(L, 1);
+ up = luaL_checkinteger(L, 2);
+ break;
+ }
+ default: return luaL_error(L, "wrong number of arguments");
+ }
+ /* random integer in the interval [low, up] */
+ luaL_argcheck(L, low <= up, 1, "interval is empty");
+ /* project random integer into the interval [0, up - low] */
+ p = project(I2UInt(rv), (lua_Unsigned)up - (lua_Unsigned)low, state);
+ lua_pushinteger(L, p + (lua_Unsigned)low);
+ return 1;
+}
+
+
+static void setseed (lua_State *L, Rand64 *state,
+ lua_Unsigned n1, lua_Unsigned n2) {
+ int i;
+ state[0] = Int2I(n1);
+ state[1] = Int2I(0xff); /* avoid a zero state */
+ state[2] = Int2I(n2);
+ state[3] = Int2I(0);
+ for (i = 0; i < 16; i++)
+ nextrand(state); /* discard initial values to "spread" seed */
+ lua_pushinteger(L, n1);
+ lua_pushinteger(L, n2);
+}
+
+
+/*
+** Set a "random" seed. To get some randomness, use the current time
+** and the address of 'L' (in case the machine does address space layout
+** randomization).
+*/
+static void randseed (lua_State *L, RanState *state) {
+ lua_Unsigned seed1 = (lua_Unsigned)time(NULL);
+ lua_Unsigned seed2 = (lua_Unsigned)(size_t)L;
+ setseed(L, state->s, seed1, seed2);
+}
+
+
+static int math_randomseed (lua_State *L) {
+ RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
+ if (lua_isnone(L, 1)) {
+ randseed(L, state);
+ }
+ else {
+ lua_Integer n1 = luaL_checkinteger(L, 1);
+ lua_Integer n2 = luaL_optinteger(L, 2, 0);
+ setseed(L, state->s, n1, n2);
+ }
+ return 2; /* return seeds */
+}
+
+
+static const luaL_Reg randfuncs[] = {
+ {"random", math_random},
+ {"randomseed", math_randomseed},
+ {NULL, NULL}
+};
+
+
+/*
+** Register the random functions and initialize their state.
+*/
+static void setrandfunc (lua_State *L) {
+ RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0);
+ randseed(L, state); /* initialize with a "random" seed */
+ lua_pop(L, 2); /* remove pushed seeds */
+ luaL_setfuncs(L, randfuncs, 1);
+}
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Deprecated functions (for compatibility only)
+** ===================================================================
+*/
+#if defined(LUA_COMPAT_MATHLIB)
+
+static int math_cosh (lua_State *L) {
+ lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_sinh (lua_State *L) {
+ lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_tanh (lua_State *L) {
+ lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_pow (lua_State *L) {
+ lua_Number x = luaL_checknumber(L, 1);
+ lua_Number y = luaL_checknumber(L, 2);
+ lua_pushnumber(L, l_mathop(pow)(x, y));
+ return 1;
+}
+
+static int math_frexp (lua_State *L) {
+ int e;
+ lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e));
+ lua_pushinteger(L, e);
+ return 2;
+}
+
+static int math_ldexp (lua_State *L) {
+ lua_Number x = luaL_checknumber(L, 1);
+ int ep = (int)luaL_checkinteger(L, 2);
+ lua_pushnumber(L, l_mathop(ldexp)(x, ep));
+ return 1;
+}
+
+static int math_log10 (lua_State *L) {
+ lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+#endif
+/* }================================================================== */
+
+
+
+static const luaL_Reg mathlib[] = {
+ {"abs", math_abs},
+ {"acos", math_acos},
+ {"asin", math_asin},
+ {"atan", math_atan},
+ {"ceil", math_ceil},
+ {"cos", math_cos},
+ {"deg", math_deg},
+ {"exp", math_exp},
+ {"tointeger", math_toint},
+ {"floor", math_floor},
+ {"fmod", math_fmod},
+ {"ult", math_ult},
+ {"log", math_log},
+ {"max", math_max},
+ {"min", math_min},
+ {"modf", math_modf},
+ {"rad", math_rad},
+ {"sin", math_sin},
+ {"sqrt", math_sqrt},
+ {"tan", math_tan},
+ {"type", math_type},
+#if defined(LUA_COMPAT_MATHLIB)
+ {"atan2", math_atan},
+ {"cosh", math_cosh},
+ {"sinh", math_sinh},
+ {"tanh", math_tanh},
+ {"pow", math_pow},
+ {"frexp", math_frexp},
+ {"ldexp", math_ldexp},
+ {"log10", math_log10},
+#endif
+ /* placeholders */
+ {"random", NULL},
+ {"randomseed", NULL},
+ {"pi", NULL},
+ {"huge", NULL},
+ {"maxinteger", NULL},
+ {"mininteger", NULL},
+ {NULL, NULL}
+};
+
+
+/*
+** Open math library
+*/
+LUAMOD_API int luaopen_math (lua_State *L) {
+ luaL_newlib(L, mathlib);
+ lua_pushnumber(L, PI);
+ lua_setfield(L, -2, "pi");
+ lua_pushnumber(L, (lua_Number)HUGE_VAL);
+ lua_setfield(L, -2, "huge");
+ lua_pushinteger(L, LUA_MAXINTEGER);
+ lua_setfield(L, -2, "maxinteger");
+ lua_pushinteger(L, LUA_MININTEGER);
+ lua_setfield(L, -2, "mininteger");
+ setrandfunc(L);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lmem.c b/src/libs/3rdparty/lua/src/lmem.c
new file mode 100644
index 0000000000..9800a86fc0
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lmem.c
@@ -0,0 +1,215 @@
+/*
+** $Id: lmem.c $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+#define lmem_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <stddef.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+/*
+** About the realloc function:
+** void *frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
+** ('osize' is the old size, 'nsize' is the new size)
+**
+** - frealloc(ud, p, x, 0) frees the block 'p' and returns NULL.
+** Particularly, frealloc(ud, NULL, 0, 0) does nothing,
+** which is equivalent to free(NULL) in ISO C.
+**
+** - frealloc(ud, NULL, x, s) creates a new block of size 's'
+** (no matter 'x'). Returns NULL if it cannot create the new block.
+**
+** - otherwise, frealloc(ud, b, x, y) reallocates the block 'b' from
+** size 'x' to size 'y'. Returns NULL if it cannot reallocate the
+** block to the new size.
+*/
+
+
+/*
+** Macro to call the allocation function.
+*/
+#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
+
+
+/*
+** When an allocation fails, it will try again after an emergency
+** collection, except when it cannot run a collection. The GC should
+** not be called while the state is not fully built, as the collector
+** is not yet fully initialized. Also, it should not be called when
+** 'gcstopem' is true, because then the interpreter is in the middle of
+** a collection step.
+*/
+#define cantryagain(g) (completestate(g) && !g->gcstopem)
+
+
+
+
+#if defined(EMERGENCYGCTESTS)
+/*
+** First allocation will fail except when freeing a block (frees never
+** fail) and when it cannot try again; this fail will trigger 'tryagain'
+** and a full GC cycle at every allocation.
+*/
+static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
+ if (ns > 0 && cantryagain(g))
+ return NULL; /* fail */
+ else /* normal allocation */
+ return callfrealloc(g, block, os, ns);
+}
+#else
+#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns)
+#endif
+
+
+
+
+
+/*
+** {==================================================================
+** Functions to allocate/deallocate arrays for the Parser
+** ===================================================================
+*/
+
+/*
+** Minimum size for arrays during parsing, to avoid overhead of
+** reallocating to size 1, then 2, and then 4. All these arrays
+** will be reallocated to exact sizes or erased when parsing ends.
+*/
+#define MINSIZEARRAY 4
+
+
+void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize,
+ int size_elems, int limit, const char *what) {
+ void *newblock;
+ int size = *psize;
+ if (nelems + 1 <= size) /* does one extra element still fit? */
+ return block; /* nothing to be done */
+ if (size >= limit / 2) { /* cannot double it? */
+ if (l_unlikely(size >= limit)) /* cannot grow even a little? */
+ luaG_runerror(L, "too many %s (limit is %d)", what, limit);
+ size = limit; /* still have at least one free place */
+ }
+ else {
+ size *= 2;
+ if (size < MINSIZEARRAY)
+ size = MINSIZEARRAY; /* minimum size */
+ }
+ lua_assert(nelems + 1 <= size && size <= limit);
+ /* 'limit' ensures that multiplication will not overflow */
+ newblock = luaM_saferealloc_(L, block, cast_sizet(*psize) * size_elems,
+ cast_sizet(size) * size_elems);
+ *psize = size; /* update only when everything else is OK */
+ return newblock;
+}
+
+
+/*
+** In prototypes, the size of the array is also its number of
+** elements (to save memory). So, if it cannot shrink an array
+** to its number of elements, the only option is to raise an
+** error.
+*/
+void *luaM_shrinkvector_ (lua_State *L, void *block, int *size,
+ int final_n, int size_elem) {
+ void *newblock;
+ size_t oldsize = cast_sizet((*size) * size_elem);
+ size_t newsize = cast_sizet(final_n * size_elem);
+ lua_assert(newsize <= oldsize);
+ newblock = luaM_saferealloc_(L, block, oldsize, newsize);
+ *size = final_n;
+ return newblock;
+}
+
+/* }================================================================== */
+
+
+l_noret luaM_toobig (lua_State *L) {
+ luaG_runerror(L, "memory allocation error: block too big");
+}
+
+
+/*
+** Free memory
+*/
+void luaM_free_ (lua_State *L, void *block, size_t osize) {
+ global_State *g = G(L);
+ lua_assert((osize == 0) == (block == NULL));
+ callfrealloc(g, block, osize, 0);
+ g->GCdebt -= osize;
+}
+
+
+/*
+** In case of allocation fail, this function will do an emergency
+** collection to free some memory and then try the allocation again.
+*/
+static void *tryagain (lua_State *L, void *block,
+ size_t osize, size_t nsize) {
+ global_State *g = G(L);
+ if (cantryagain(g)) {
+ luaC_fullgc(L, 1); /* try to free some memory... */
+ return callfrealloc(g, block, osize, nsize); /* try again */
+ }
+ else return NULL; /* cannot run an emergency collection */
+}
+
+
+/*
+** Generic allocation routine.
+*/
+void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
+ void *newblock;
+ global_State *g = G(L);
+ lua_assert((osize == 0) == (block == NULL));
+ newblock = firsttry(g, block, osize, nsize);
+ if (l_unlikely(newblock == NULL && nsize > 0)) {
+ newblock = tryagain(L, block, osize, nsize);
+ if (newblock == NULL) /* still no memory? */
+ return NULL; /* do not update 'GCdebt' */
+ }
+ lua_assert((nsize == 0) == (newblock == NULL));
+ g->GCdebt = (g->GCdebt + nsize) - osize;
+ return newblock;
+}
+
+
+void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize,
+ size_t nsize) {
+ void *newblock = luaM_realloc_(L, block, osize, nsize);
+ if (l_unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */
+ luaM_error(L);
+ return newblock;
+}
+
+
+void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
+ if (size == 0)
+ return NULL; /* that's all */
+ else {
+ global_State *g = G(L);
+ void *newblock = firsttry(g, NULL, tag, size);
+ if (l_unlikely(newblock == NULL)) {
+ newblock = tryagain(L, NULL, tag, size);
+ if (newblock == NULL)
+ luaM_error(L);
+ }
+ g->GCdebt += size;
+ return newblock;
+ }
+}
diff --git a/src/libs/3rdparty/lua/src/lmem.h b/src/libs/3rdparty/lua/src/lmem.h
new file mode 100644
index 0000000000..8c75a44beb
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lmem.h
@@ -0,0 +1,93 @@
+/*
+** $Id: lmem.h $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lmem_h
+#define lmem_h
+
+
+#include <stddef.h>
+
+#include "llimits.h"
+#include "lua.h"
+
+
+#define luaM_error(L) luaD_throw(L, LUA_ERRMEM)
+
+
+/*
+** This macro tests whether it is safe to multiply 'n' by the size of
+** type 't' without overflows. Because 'e' is always constant, it avoids
+** the runtime division MAX_SIZET/(e).
+** (The macro is somewhat complex to avoid warnings: The 'sizeof'
+** comparison avoids a runtime comparison when overflow cannot occur.
+** The compiler should be able to optimize the real test by itself, but
+** when it does it, it may give a warning about "comparison is always
+** false due to limited range of data type"; the +1 tricks the compiler,
+** avoiding this warning but also this optimization.)
+*/
+#define luaM_testsize(n,e) \
+ (sizeof(n) >= sizeof(size_t) && cast_sizet((n)) + 1 > MAX_SIZET/(e))
+
+#define luaM_checksize(L,n,e) \
+ (luaM_testsize(n,e) ? luaM_toobig(L) : cast_void(0))
+
+
+/*
+** Computes the minimum between 'n' and 'MAX_SIZET/sizeof(t)', so that
+** the result is not larger than 'n' and cannot overflow a 'size_t'
+** when multiplied by the size of type 't'. (Assumes that 'n' is an
+** 'int' or 'unsigned int' and that 'int' is not larger than 'size_t'.)
+*/
+#define luaM_limitN(n,t) \
+ ((cast_sizet(n) <= MAX_SIZET/sizeof(t)) ? (n) : \
+ cast_uint((MAX_SIZET/sizeof(t))))
+
+
+/*
+** Arrays of chars do not need any test
+*/
+#define luaM_reallocvchar(L,b,on,n) \
+ cast_charp(luaM_saferealloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char)))
+
+#define luaM_freemem(L, b, s) luaM_free_(L, (b), (s))
+#define luaM_free(L, b) luaM_free_(L, (b), sizeof(*(b)))
+#define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b)))
+
+#define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0))
+#define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0))
+#define luaM_newvectorchecked(L,n,t) \
+ (luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t))
+
+#define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag)
+
+#define luaM_growvector(L,v,nelems,size,t,limit,e) \
+ ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \
+ luaM_limitN(limit,t),e)))
+
+#define luaM_reallocvector(L, v,oldn,n,t) \
+ (cast(t *, luaM_realloc_(L, v, cast_sizet(oldn) * sizeof(t), \
+ cast_sizet(n) * sizeof(t))))
+
+#define luaM_shrinkvector(L,v,size,fs,t) \
+ ((v)=cast(t *, luaM_shrinkvector_(L, v, &(size), fs, sizeof(t))))
+
+LUAI_FUNC l_noret luaM_toobig (lua_State *L);
+
+/* not to be called directly */
+LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize,
+ size_t size);
+LUAI_FUNC void *luaM_saferealloc_ (lua_State *L, void *block, size_t oldsize,
+ size_t size);
+LUAI_FUNC void luaM_free_ (lua_State *L, void *block, size_t osize);
+LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int nelems,
+ int *size, int size_elem, int limit,
+ const char *what);
+LUAI_FUNC void *luaM_shrinkvector_ (lua_State *L, void *block, int *nelem,
+ int final_n, int size_elem);
+LUAI_FUNC void *luaM_malloc_ (lua_State *L, size_t size, int tag);
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/loadlib.c b/src/libs/3rdparty/lua/src/loadlib.c
new file mode 100644
index 0000000000..d792dffaa0
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/loadlib.c
@@ -0,0 +1,767 @@
+/*
+** $Id: loadlib.c $
+** Dynamic library loader for Lua
+** See Copyright Notice in lua.h
+**
+** This module contains an implementation of loadlib for Unix systems
+** that have dlfcn, an implementation for Windows, and a stub for other
+** systems.
+*/
+
+#define loadlib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** LUA_IGMARK is a mark to ignore all before it when building the
+** luaopen_ function name.
+*/
+#if !defined (LUA_IGMARK)
+#define LUA_IGMARK "-"
+#endif
+
+
+/*
+** LUA_CSUBSEP is the character that replaces dots in submodule names
+** when searching for a C loader.
+** LUA_LSUBSEP is the character that replaces dots in submodule names
+** when searching for a Lua loader.
+*/
+#if !defined(LUA_CSUBSEP)
+#define LUA_CSUBSEP LUA_DIRSEP
+#endif
+
+#if !defined(LUA_LSUBSEP)
+#define LUA_LSUBSEP LUA_DIRSEP
+#endif
+
+
+/* prefix for open functions in C libraries */
+#define LUA_POF "luaopen_"
+
+/* separator for open functions in C libraries */
+#define LUA_OFSEP "_"
+
+
+/*
+** key for table in the registry that keeps handles
+** for all loaded C libraries
+*/
+static const char *const CLIBS = "_CLIBS";
+
+#define LIB_FAIL "open"
+
+
+#define setprogdir(L) ((void)0)
+
+
+/*
+** Special type equivalent to '(void*)' for functions in gcc
+** (to suppress warnings when converting function pointers)
+*/
+typedef void (*voidf)(void);
+
+
+/*
+** system-dependent functions
+*/
+
+/*
+** unload library 'lib'
+*/
+static void lsys_unloadlib (void *lib);
+
+/*
+** load C library in file 'path'. If 'seeglb', load with all names in
+** the library global.
+** Returns the library; in case of error, returns NULL plus an
+** error string in the stack.
+*/
+static void *lsys_load (lua_State *L, const char *path, int seeglb);
+
+/*
+** Try to find a function named 'sym' in library 'lib'.
+** Returns the function; in case of error, returns NULL plus an
+** error string in the stack.
+*/
+static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym);
+
+
+
+
+#if defined(LUA_USE_DLOPEN) /* { */
+/*
+** {========================================================================
+** This is an implementation of loadlib based on the dlfcn interface.
+** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
+** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
+** as an emulation layer on top of native functions.
+** =========================================================================
+*/
+
+#include <dlfcn.h>
+
+/*
+** Macro to convert pointer-to-void* to pointer-to-function. This cast
+** is undefined according to ISO C, but POSIX assumes that it works.
+** (The '__extension__' in gnu compilers is only to avoid warnings.)
+*/
+#if defined(__GNUC__)
+#define cast_func(p) (__extension__ (lua_CFunction)(p))
+#else
+#define cast_func(p) ((lua_CFunction)(p))
+#endif
+
+
+static void lsys_unloadlib (void *lib) {
+ dlclose(lib);
+}
+
+
+static void *lsys_load (lua_State *L, const char *path, int seeglb) {
+ void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL));
+ if (l_unlikely(lib == NULL))
+ lua_pushstring(L, dlerror());
+ return lib;
+}
+
+
+static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
+ lua_CFunction f = cast_func(dlsym(lib, sym));
+ if (l_unlikely(f == NULL))
+ lua_pushstring(L, dlerror());
+ return f;
+}
+
+/* }====================================================== */
+
+
+
+#elif defined(LUA_DL_DLL) /* }{ */
+/*
+** {======================================================================
+** This is an implementation of loadlib for Windows using native functions.
+** =======================================================================
+*/
+
+#include <windows.h>
+
+
+/*
+** optional flags for LoadLibraryEx
+*/
+#if !defined(LUA_LLE_FLAGS)
+#define LUA_LLE_FLAGS 0
+#endif
+
+
+#undef setprogdir
+
+
+/*
+** Replace in the path (on the top of the stack) any occurrence
+** of LUA_EXEC_DIR with the executable's path.
+*/
+static void setprogdir (lua_State *L) {
+ char buff[MAX_PATH + 1];
+ char *lb;
+ DWORD nsize = sizeof(buff)/sizeof(char);
+ DWORD n = GetModuleFileNameA(NULL, buff, nsize); /* get exec. name */
+ if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL)
+ luaL_error(L, "unable to get ModuleFileName");
+ else {
+ *lb = '\0'; /* cut name on the last '\\' to get the path */
+ luaL_gsub(L, lua_tostring(L, -1), LUA_EXEC_DIR, buff);
+ lua_remove(L, -2); /* remove original string */
+ }
+}
+
+
+
+
+static void pusherror (lua_State *L) {
+ int error = GetLastError();
+ char buffer[128];
+ if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, error, 0, buffer, sizeof(buffer)/sizeof(char), NULL))
+ lua_pushstring(L, buffer);
+ else
+ lua_pushfstring(L, "system error %d\n", error);
+}
+
+static void lsys_unloadlib (void *lib) {
+ FreeLibrary((HMODULE)lib);
+}
+
+
+static void *lsys_load (lua_State *L, const char *path, int seeglb) {
+ HMODULE lib = LoadLibraryExA(path, NULL, LUA_LLE_FLAGS);
+ (void)(seeglb); /* not used: symbols are 'global' by default */
+ if (lib == NULL) pusherror(L);
+ return lib;
+}
+
+
+static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
+ lua_CFunction f = (lua_CFunction)(voidf)GetProcAddress((HMODULE)lib, sym);
+ if (f == NULL) pusherror(L);
+ return f;
+}
+
+/* }====================================================== */
+
+
+#else /* }{ */
+/*
+** {======================================================
+** Fallback for other systems
+** =======================================================
+*/
+
+#undef LIB_FAIL
+#define LIB_FAIL "absent"
+
+
+#define DLMSG "dynamic libraries not enabled; check your Lua installation"
+
+
+static void lsys_unloadlib (void *lib) {
+ (void)(lib); /* not used */
+}
+
+
+static void *lsys_load (lua_State *L, const char *path, int seeglb) {
+ (void)(path); (void)(seeglb); /* not used */
+ lua_pushliteral(L, DLMSG);
+ return NULL;
+}
+
+
+static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
+ (void)(lib); (void)(sym); /* not used */
+ lua_pushliteral(L, DLMSG);
+ return NULL;
+}
+
+/* }====================================================== */
+#endif /* } */
+
+
+/*
+** {==================================================================
+** Set Paths
+** ===================================================================
+*/
+
+/*
+** LUA_PATH_VAR and LUA_CPATH_VAR are the names of the environment
+** variables that Lua check to set its paths.
+*/
+#if !defined(LUA_PATH_VAR)
+#define LUA_PATH_VAR "LUA_PATH"
+#endif
+
+#if !defined(LUA_CPATH_VAR)
+#define LUA_CPATH_VAR "LUA_CPATH"
+#endif
+
+
+
+/*
+** return registry.LUA_NOENV as a boolean
+*/
+static int noenv (lua_State *L) {
+ int b;
+ lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
+ b = lua_toboolean(L, -1);
+ lua_pop(L, 1); /* remove value */
+ return b;
+}
+
+
+/*
+** Set a path
+*/
+static void setpath (lua_State *L, const char *fieldname,
+ const char *envname,
+ const char *dft) {
+ const char *dftmark;
+ const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX);
+ const char *path = getenv(nver); /* try versioned name */
+ if (path == NULL) /* no versioned environment variable? */
+ path = getenv(envname); /* try unversioned name */
+ if (path == NULL || noenv(L)) /* no environment variable? */
+ lua_pushstring(L, dft); /* use default */
+ else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL)
+ lua_pushstring(L, path); /* nothing to change */
+ else { /* path contains a ";;": insert default path in its place */
+ size_t len = strlen(path);
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ if (path < dftmark) { /* is there a prefix before ';;'? */
+ luaL_addlstring(&b, path, dftmark - path); /* add it */
+ luaL_addchar(&b, *LUA_PATH_SEP);
+ }
+ luaL_addstring(&b, dft); /* add default */
+ if (dftmark < path + len - 2) { /* is there a suffix after ';;'? */
+ luaL_addchar(&b, *LUA_PATH_SEP);
+ luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark);
+ }
+ luaL_pushresult(&b);
+ }
+ setprogdir(L);
+ lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */
+ lua_pop(L, 1); /* pop versioned variable name ('nver') */
+}
+
+/* }================================================================== */
+
+
+/*
+** return registry.CLIBS[path]
+*/
+static void *checkclib (lua_State *L, const char *path) {
+ void *plib;
+ lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
+ lua_getfield(L, -1, path);
+ plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */
+ lua_pop(L, 2); /* pop CLIBS table and 'plib' */
+ return plib;
+}
+
+
+/*
+** registry.CLIBS[path] = plib -- for queries
+** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries
+*/
+static void addtoclib (lua_State *L, const char *path, void *plib) {
+ lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
+ lua_pushlightuserdata(L, plib);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, path); /* CLIBS[path] = plib */
+ lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */
+ lua_pop(L, 1); /* pop CLIBS table */
+}
+
+
+/*
+** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib
+** handles in list CLIBS
+*/
+static int gctm (lua_State *L) {
+ lua_Integer n = luaL_len(L, 1);
+ for (; n >= 1; n--) { /* for each handle, in reverse order */
+ lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */
+ lsys_unloadlib(lua_touserdata(L, -1));
+ lua_pop(L, 1); /* pop handle */
+ }
+ return 0;
+}
+
+
+
+/* error codes for 'lookforfunc' */
+#define ERRLIB 1
+#define ERRFUNC 2
+
+/*
+** Look for a C function named 'sym' in a dynamically loaded library
+** 'path'.
+** First, check whether the library is already loaded; if not, try
+** to load it.
+** Then, if 'sym' is '*', return true (as library has been loaded).
+** Otherwise, look for symbol 'sym' in the library and push a
+** C function with that symbol.
+** Return 0 and 'true' or a function in the stack; in case of
+** errors, return an error code and an error message in the stack.
+*/
+static int lookforfunc (lua_State *L, const char *path, const char *sym) {
+ void *reg = checkclib(L, path); /* check loaded C libraries */
+ if (reg == NULL) { /* must load library? */
+ reg = lsys_load(L, path, *sym == '*'); /* global symbols if 'sym'=='*' */
+ if (reg == NULL) return ERRLIB; /* unable to load library */
+ addtoclib(L, path, reg);
+ }
+ if (*sym == '*') { /* loading only library (no function)? */
+ lua_pushboolean(L, 1); /* return 'true' */
+ return 0; /* no errors */
+ }
+ else {
+ lua_CFunction f = lsys_sym(L, reg, sym);
+ if (f == NULL)
+ return ERRFUNC; /* unable to find function */
+ lua_pushcfunction(L, f); /* else create new function */
+ return 0; /* no errors */
+ }
+}
+
+
+static int ll_loadlib (lua_State *L) {
+ const char *path = luaL_checkstring(L, 1);
+ const char *init = luaL_checkstring(L, 2);
+ int stat = lookforfunc(L, path, init);
+ if (l_likely(stat == 0)) /* no errors? */
+ return 1; /* return the loaded function */
+ else { /* error; error message is on stack top */
+ luaL_pushfail(L);
+ lua_insert(L, -2);
+ lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init");
+ return 3; /* return fail, error message, and where */
+ }
+}
+
+
+
+/*
+** {======================================================
+** 'require' function
+** =======================================================
+*/
+
+
+static int readable (const char *filename) {
+ FILE *f = fopen(filename, "r"); /* try to open file */
+ if (f == NULL) return 0; /* open failed */
+ fclose(f);
+ return 1;
+}
+
+
+/*
+** Get the next name in '*path' = 'name1;name2;name3;...', changing
+** the ending ';' to '\0' to create a zero-terminated string. Return
+** NULL when list ends.
+*/
+static const char *getnextfilename (char **path, char *end) {
+ char *sep;
+ char *name = *path;
+ if (name == end)
+ return NULL; /* no more names */
+ else if (*name == '\0') { /* from previous iteration? */
+ *name = *LUA_PATH_SEP; /* restore separator */
+ name++; /* skip it */
+ }
+ sep = strchr(name, *LUA_PATH_SEP); /* find next separator */
+ if (sep == NULL) /* separator not found? */
+ sep = end; /* name goes until the end */
+ *sep = '\0'; /* finish file name */
+ *path = sep; /* will start next search from here */
+ return name;
+}
+
+
+/*
+** Given a path such as ";blabla.so;blublu.so", pushes the string
+**
+** no file 'blabla.so'
+** no file 'blublu.so'
+*/
+static void pusherrornotfound (lua_State *L, const char *path) {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ luaL_addstring(&b, "no file '");
+ luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '");
+ luaL_addstring(&b, "'");
+ luaL_pushresult(&b);
+}
+
+
+static const char *searchpath (lua_State *L, const char *name,
+ const char *path,
+ const char *sep,
+ const char *dirsep) {
+ luaL_Buffer buff;
+ char *pathname; /* path with name inserted */
+ char *endpathname; /* its end */
+ const char *filename;
+ /* separator is non-empty and appears in 'name'? */
+ if (*sep != '\0' && strchr(name, *sep) != NULL)
+ name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */
+ luaL_buffinit(L, &buff);
+ /* add path to the buffer, replacing marks ('?') with the file name */
+ luaL_addgsub(&buff, path, LUA_PATH_MARK, name);
+ luaL_addchar(&buff, '\0');
+ pathname = luaL_buffaddr(&buff); /* writable list of file names */
+ endpathname = pathname + luaL_bufflen(&buff) - 1;
+ while ((filename = getnextfilename(&pathname, endpathname)) != NULL) {
+ if (readable(filename)) /* does file exist and is readable? */
+ return lua_pushstring(L, filename); /* save and return name */
+ }
+ luaL_pushresult(&buff); /* push path to create error message */
+ pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */
+ return NULL; /* not found */
+}
+
+
+static int ll_searchpath (lua_State *L) {
+ const char *f = searchpath(L, luaL_checkstring(L, 1),
+ luaL_checkstring(L, 2),
+ luaL_optstring(L, 3, "."),
+ luaL_optstring(L, 4, LUA_DIRSEP));
+ if (f != NULL) return 1;
+ else { /* error message is on top of the stack */
+ luaL_pushfail(L);
+ lua_insert(L, -2);
+ return 2; /* return fail + error message */
+ }
+}
+
+
+static const char *findfile (lua_State *L, const char *name,
+ const char *pname,
+ const char *dirsep) {
+ const char *path;
+ lua_getfield(L, lua_upvalueindex(1), pname);
+ path = lua_tostring(L, -1);
+ if (l_unlikely(path == NULL))
+ luaL_error(L, "'package.%s' must be a string", pname);
+ return searchpath(L, name, path, ".", dirsep);
+}
+
+
+static int checkload (lua_State *L, int stat, const char *filename) {
+ if (l_likely(stat)) { /* module loaded successfully? */
+ lua_pushstring(L, filename); /* will be 2nd argument to module */
+ return 2; /* return open function and file name */
+ }
+ else
+ return luaL_error(L, "error loading module '%s' from file '%s':\n\t%s",
+ lua_tostring(L, 1), filename, lua_tostring(L, -1));
+}
+
+
+static int searcher_Lua (lua_State *L) {
+ const char *filename;
+ const char *name = luaL_checkstring(L, 1);
+ filename = findfile(L, name, "path", LUA_LSUBSEP);
+ if (filename == NULL) return 1; /* module not found in this path */
+ return checkload(L, (luaL_loadfile(L, filename) == LUA_OK), filename);
+}
+
+
+/*
+** Try to find a load function for module 'modname' at file 'filename'.
+** First, change '.' to '_' in 'modname'; then, if 'modname' has
+** the form X-Y (that is, it has an "ignore mark"), build a function
+** name "luaopen_X" and look for it. (For compatibility, if that
+** fails, it also tries "luaopen_Y".) If there is no ignore mark,
+** look for a function named "luaopen_modname".
+*/
+static int loadfunc (lua_State *L, const char *filename, const char *modname) {
+ const char *openfunc;
+ const char *mark;
+ modname = luaL_gsub(L, modname, ".", LUA_OFSEP);
+ mark = strchr(modname, *LUA_IGMARK);
+ if (mark) {
+ int stat;
+ openfunc = lua_pushlstring(L, modname, mark - modname);
+ openfunc = lua_pushfstring(L, LUA_POF"%s", openfunc);
+ stat = lookforfunc(L, filename, openfunc);
+ if (stat != ERRFUNC) return stat;
+ modname = mark + 1; /* else go ahead and try old-style name */
+ }
+ openfunc = lua_pushfstring(L, LUA_POF"%s", modname);
+ return lookforfunc(L, filename, openfunc);
+}
+
+
+static int searcher_C (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ const char *filename = findfile(L, name, "cpath", LUA_CSUBSEP);
+ if (filename == NULL) return 1; /* module not found in this path */
+ return checkload(L, (loadfunc(L, filename, name) == 0), filename);
+}
+
+
+static int searcher_Croot (lua_State *L) {
+ const char *filename;
+ const char *name = luaL_checkstring(L, 1);
+ const char *p = strchr(name, '.');
+ int stat;
+ if (p == NULL) return 0; /* is root */
+ lua_pushlstring(L, name, p - name);
+ filename = findfile(L, lua_tostring(L, -1), "cpath", LUA_CSUBSEP);
+ if (filename == NULL) return 1; /* root not found */
+ if ((stat = loadfunc(L, filename, name)) != 0) {
+ if (stat != ERRFUNC)
+ return checkload(L, 0, filename); /* real error */
+ else { /* open function not found */
+ lua_pushfstring(L, "no module '%s' in file '%s'", name, filename);
+ return 1;
+ }
+ }
+ lua_pushstring(L, filename); /* will be 2nd argument to module */
+ return 2;
+}
+
+
+static int searcher_preload (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
+ if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */
+ lua_pushfstring(L, "no field package.preload['%s']", name);
+ return 1;
+ }
+ else {
+ lua_pushliteral(L, ":preload:");
+ return 2;
+ }
+}
+
+
+static void findloader (lua_State *L, const char *name) {
+ int i;
+ luaL_Buffer msg; /* to build error message */
+ /* push 'package.searchers' to index 3 in the stack */
+ if (l_unlikely(lua_getfield(L, lua_upvalueindex(1), "searchers")
+ != LUA_TTABLE))
+ luaL_error(L, "'package.searchers' must be a table");
+ luaL_buffinit(L, &msg);
+ /* iterate over available searchers to find a loader */
+ for (i = 1; ; i++) {
+ luaL_addstring(&msg, "\n\t"); /* error-message prefix */
+ if (l_unlikely(lua_rawgeti(L, 3, i) == LUA_TNIL)) { /* no more searchers? */
+ lua_pop(L, 1); /* remove nil */
+ luaL_buffsub(&msg, 2); /* remove prefix */
+ luaL_pushresult(&msg); /* create error message */
+ luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1));
+ }
+ lua_pushstring(L, name);
+ lua_call(L, 1, 2); /* call it */
+ if (lua_isfunction(L, -2)) /* did it find a loader? */
+ return; /* module loader found */
+ else if (lua_isstring(L, -2)) { /* searcher returned error message? */
+ lua_pop(L, 1); /* remove extra return */
+ luaL_addvalue(&msg); /* concatenate error message */
+ }
+ else { /* no error message */
+ lua_pop(L, 2); /* remove both returns */
+ luaL_buffsub(&msg, 2); /* remove prefix */
+ }
+ }
+}
+
+
+static int ll_require (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ lua_settop(L, 1); /* LOADED table will be at index 2 */
+ lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
+ lua_getfield(L, 2, name); /* LOADED[name] */
+ if (lua_toboolean(L, -1)) /* is it there? */
+ return 1; /* package is already loaded */
+ /* else must load package */
+ lua_pop(L, 1); /* remove 'getfield' result */
+ findloader(L, name);
+ lua_rotate(L, -2, 1); /* function <-> loader data */
+ lua_pushvalue(L, 1); /* name is 1st argument to module loader */
+ lua_pushvalue(L, -3); /* loader data is 2nd argument */
+ /* stack: ...; loader data; loader function; mod. name; loader data */
+ lua_call(L, 2, 1); /* run loader to load module */
+ /* stack: ...; loader data; result from loader */
+ if (!lua_isnil(L, -1)) /* non-nil return? */
+ lua_setfield(L, 2, name); /* LOADED[name] = returned value */
+ else
+ lua_pop(L, 1); /* pop nil */
+ if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */
+ lua_pushboolean(L, 1); /* use true as result */
+ lua_copy(L, -1, -2); /* replace loader result */
+ lua_setfield(L, 2, name); /* LOADED[name] = true */
+ }
+ lua_rotate(L, -2, 1); /* loader data <-> module result */
+ return 2; /* return module result and loader data */
+}
+
+/* }====================================================== */
+
+
+
+
+static const luaL_Reg pk_funcs[] = {
+ {"loadlib", ll_loadlib},
+ {"searchpath", ll_searchpath},
+ /* placeholders */
+ {"preload", NULL},
+ {"cpath", NULL},
+ {"path", NULL},
+ {"searchers", NULL},
+ {"loaded", NULL},
+ {NULL, NULL}
+};
+
+
+static const luaL_Reg ll_funcs[] = {
+ {"require", ll_require},
+ {NULL, NULL}
+};
+
+
+static void createsearcherstable (lua_State *L) {
+ static const lua_CFunction searchers[] = {
+ searcher_preload,
+ searcher_Lua,
+ searcher_C,
+ searcher_Croot,
+ NULL
+ };
+ int i;
+ /* create 'searchers' table */
+ lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0);
+ /* fill it with predefined searchers */
+ for (i=0; searchers[i] != NULL; i++) {
+ lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */
+ lua_pushcclosure(L, searchers[i], 1);
+ lua_rawseti(L, -2, i+1);
+ }
+ lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */
+}
+
+
+/*
+** create table CLIBS to keep track of loaded C libraries,
+** setting a finalizer to close all libraries when closing state.
+*/
+static void createclibstable (lua_State *L) {
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */
+ lua_createtable(L, 0, 1); /* create metatable for CLIBS */
+ lua_pushcfunction(L, gctm);
+ lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */
+ lua_setmetatable(L, -2);
+}
+
+
+LUAMOD_API int luaopen_package (lua_State *L) {
+ createclibstable(L);
+ luaL_newlib(L, pk_funcs); /* create 'package' table */
+ createsearcherstable(L);
+ /* set paths */
+ setpath(L, "path", LUA_PATH_VAR, LUA_PATH_DEFAULT);
+ setpath(L, "cpath", LUA_CPATH_VAR, LUA_CPATH_DEFAULT);
+ /* store config information */
+ lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATH_SEP "\n" LUA_PATH_MARK "\n"
+ LUA_EXEC_DIR "\n" LUA_IGMARK "\n");
+ lua_setfield(L, -2, "config");
+ /* set field 'loaded' */
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
+ lua_setfield(L, -2, "loaded");
+ /* set field 'preload' */
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
+ lua_setfield(L, -2, "preload");
+ lua_pushglobaltable(L);
+ lua_pushvalue(L, -2); /* set 'package' as upvalue for next lib */
+ luaL_setfuncs(L, ll_funcs, 1); /* open lib into global table */
+ lua_pop(L, 1); /* pop global table */
+ return 1; /* return 'package' table */
+}
+
diff --git a/src/libs/3rdparty/lua/src/lobject.c b/src/libs/3rdparty/lua/src/lobject.c
new file mode 100644
index 0000000000..f73ffc6d92
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lobject.c
@@ -0,0 +1,602 @@
+/*
+** $Id: lobject.c $
+** Some generic functions over Lua objects
+** See Copyright Notice in lua.h
+*/
+
+#define lobject_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <locale.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lctype.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "lvm.h"
+
+
+/*
+** Computes ceil(log2(x))
+*/
+int luaO_ceillog2 (unsigned int x) {
+ static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+ };
+ int l = 0;
+ x--;
+ while (x >= 256) { l += 8; x >>= 8; }
+ return l + log_2[x];
+}
+
+
+static lua_Integer intarith (lua_State *L, int op, lua_Integer v1,
+ lua_Integer v2) {
+ switch (op) {
+ case LUA_OPADD: return intop(+, v1, v2);
+ case LUA_OPSUB:return intop(-, v1, v2);
+ case LUA_OPMUL:return intop(*, v1, v2);
+ case LUA_OPMOD: return luaV_mod(L, v1, v2);
+ case LUA_OPIDIV: return luaV_idiv(L, v1, v2);
+ case LUA_OPBAND: return intop(&, v1, v2);
+ case LUA_OPBOR: return intop(|, v1, v2);
+ case LUA_OPBXOR: return intop(^, v1, v2);
+ case LUA_OPSHL: return luaV_shiftl(v1, v2);
+ case LUA_OPSHR: return luaV_shiftr(v1, v2);
+ case LUA_OPUNM: return intop(-, 0, v1);
+ case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1);
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+static lua_Number numarith (lua_State *L, int op, lua_Number v1,
+ lua_Number v2) {
+ switch (op) {
+ case LUA_OPADD: return luai_numadd(L, v1, v2);
+ case LUA_OPSUB: return luai_numsub(L, v1, v2);
+ case LUA_OPMUL: return luai_nummul(L, v1, v2);
+ case LUA_OPDIV: return luai_numdiv(L, v1, v2);
+ case LUA_OPPOW: return luai_numpow(L, v1, v2);
+ case LUA_OPIDIV: return luai_numidiv(L, v1, v2);
+ case LUA_OPUNM: return luai_numunm(L, v1);
+ case LUA_OPMOD: return luaV_modf(L, v1, v2);
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2,
+ TValue *res) {
+ switch (op) {
+ case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
+ case LUA_OPSHL: case LUA_OPSHR:
+ case LUA_OPBNOT: { /* operate only on integers */
+ lua_Integer i1; lua_Integer i2;
+ if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) {
+ setivalue(res, intarith(L, op, i1, i2));
+ return 1;
+ }
+ else return 0; /* fail */
+ }
+ case LUA_OPDIV: case LUA_OPPOW: { /* operate only on floats */
+ lua_Number n1; lua_Number n2;
+ if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
+ setfltvalue(res, numarith(L, op, n1, n2));
+ return 1;
+ }
+ else return 0; /* fail */
+ }
+ default: { /* other operations */
+ lua_Number n1; lua_Number n2;
+ if (ttisinteger(p1) && ttisinteger(p2)) {
+ setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2)));
+ return 1;
+ }
+ else if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
+ setfltvalue(res, numarith(L, op, n1, n2));
+ return 1;
+ }
+ else return 0; /* fail */
+ }
+ }
+}
+
+
+void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2,
+ StkId res) {
+ if (!luaO_rawarith(L, op, p1, p2, s2v(res))) {
+ /* could not perform raw operation; try metamethod */
+ luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD));
+ }
+}
+
+
+int luaO_hexavalue (int c) {
+ if (lisdigit(c)) return c - '0';
+ else return (ltolower(c) - 'a') + 10;
+}
+
+
+static int isneg (const char **s) {
+ if (**s == '-') { (*s)++; return 1; }
+ else if (**s == '+') (*s)++;
+ return 0;
+}
+
+
+
+/*
+** {==================================================================
+** Lua's implementation for 'lua_strx2number'
+** ===================================================================
+*/
+
+#if !defined(lua_strx2number)
+
+/* maximum number of significant digits to read (to avoid overflows
+ even with single floats) */
+#define MAXSIGDIG 30
+
+/*
+** convert a hexadecimal numeric string to a number, following
+** C99 specification for 'strtod'
+*/
+static lua_Number lua_strx2number (const char *s, char **endptr) {
+ int dot = lua_getlocaledecpoint();
+ lua_Number r = l_mathop(0.0); /* result (accumulator) */
+ int sigdig = 0; /* number of significant digits */
+ int nosigdig = 0; /* number of non-significant digits */
+ int e = 0; /* exponent correction */
+ int neg; /* 1 if number is negative */
+ int hasdot = 0; /* true after seen a dot */
+ *endptr = cast_charp(s); /* nothing is valid yet */
+ while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */
+ neg = isneg(&s); /* check sign */
+ if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */
+ return l_mathop(0.0); /* invalid format (no '0x') */
+ for (s += 2; ; s++) { /* skip '0x' and read numeral */
+ if (*s == dot) {
+ if (hasdot) break; /* second dot? stop loop */
+ else hasdot = 1;
+ }
+ else if (lisxdigit(cast_uchar(*s))) {
+ if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */
+ nosigdig++;
+ else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */
+ r = (r * l_mathop(16.0)) + luaO_hexavalue(*s);
+ else e++; /* too many digits; ignore, but still count for exponent */
+ if (hasdot) e--; /* decimal digit? correct exponent */
+ }
+ else break; /* neither a dot nor a digit */
+ }
+ if (nosigdig + sigdig == 0) /* no digits? */
+ return l_mathop(0.0); /* invalid format */
+ *endptr = cast_charp(s); /* valid up to here */
+ e *= 4; /* each digit multiplies/divides value by 2^4 */
+ if (*s == 'p' || *s == 'P') { /* exponent part? */
+ int exp1 = 0; /* exponent value */
+ int neg1; /* exponent sign */
+ s++; /* skip 'p' */
+ neg1 = isneg(&s); /* sign */
+ if (!lisdigit(cast_uchar(*s)))
+ return l_mathop(0.0); /* invalid; must have at least one digit */
+ while (lisdigit(cast_uchar(*s))) /* read exponent */
+ exp1 = exp1 * 10 + *(s++) - '0';
+ if (neg1) exp1 = -exp1;
+ e += exp1;
+ *endptr = cast_charp(s); /* valid up to here */
+ }
+ if (neg) r = -r;
+ return l_mathop(ldexp)(r, e);
+}
+
+#endif
+/* }====================================================== */
+
+
+/* maximum length of a numeral to be converted to a number */
+#if !defined (L_MAXLENNUM)
+#define L_MAXLENNUM 200
+#endif
+
+/*
+** Convert string 's' to a Lua number (put in 'result'). Return NULL on
+** fail or the address of the ending '\0' on success. ('mode' == 'x')
+** means a hexadecimal numeral.
+*/
+static const char *l_str2dloc (const char *s, lua_Number *result, int mode) {
+ char *endptr;
+ *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */
+ : lua_str2number(s, &endptr);
+ if (endptr == s) return NULL; /* nothing recognized? */
+ while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */
+ return (*endptr == '\0') ? endptr : NULL; /* OK iff no trailing chars */
+}
+
+
+/*
+** Convert string 's' to a Lua number (put in 'result') handling the
+** current locale.
+** This function accepts both the current locale or a dot as the radix
+** mark. If the conversion fails, it may mean number has a dot but
+** locale accepts something else. In that case, the code copies 's'
+** to a buffer (because 's' is read-only), changes the dot to the
+** current locale radix mark, and tries to convert again.
+** The variable 'mode' checks for special characters in the string:
+** - 'n' means 'inf' or 'nan' (which should be rejected)
+** - 'x' means a hexadecimal numeral
+** - '.' just optimizes the search for the common case (no special chars)
+*/
+static const char *l_str2d (const char *s, lua_Number *result) {
+ const char *endptr;
+ const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */
+ int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0;
+ if (mode == 'n') /* reject 'inf' and 'nan' */
+ return NULL;
+ endptr = l_str2dloc(s, result, mode); /* try to convert */
+ if (endptr == NULL) { /* failed? may be a different locale */
+ char buff[L_MAXLENNUM + 1];
+ const char *pdot = strchr(s, '.');
+ if (pdot == NULL || strlen(s) > L_MAXLENNUM)
+ return NULL; /* string too long or no dot; fail */
+ strcpy(buff, s); /* copy string to buffer */
+ buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */
+ endptr = l_str2dloc(buff, result, mode); /* try again */
+ if (endptr != NULL)
+ endptr = s + (endptr - buff); /* make relative to 's' */
+ }
+ return endptr;
+}
+
+
+#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10)
+#define MAXLASTD cast_int(LUA_MAXINTEGER % 10)
+
+static const char *l_str2int (const char *s, lua_Integer *result) {
+ lua_Unsigned a = 0;
+ int empty = 1;
+ int neg;
+ while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */
+ neg = isneg(&s);
+ if (s[0] == '0' &&
+ (s[1] == 'x' || s[1] == 'X')) { /* hex? */
+ s += 2; /* skip '0x' */
+ for (; lisxdigit(cast_uchar(*s)); s++) {
+ a = a * 16 + luaO_hexavalue(*s);
+ empty = 0;
+ }
+ }
+ else { /* decimal */
+ for (; lisdigit(cast_uchar(*s)); s++) {
+ int d = *s - '0';
+ if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */
+ return NULL; /* do not accept it (as integer) */
+ a = a * 10 + d;
+ empty = 0;
+ }
+ }
+ while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */
+ if (empty || *s != '\0') return NULL; /* something wrong in the numeral */
+ else {
+ *result = l_castU2S((neg) ? 0u - a : a);
+ return s;
+ }
+}
+
+
+size_t luaO_str2num (const char *s, TValue *o) {
+ lua_Integer i; lua_Number n;
+ const char *e;
+ if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */
+ setivalue(o, i);
+ }
+ else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */
+ setfltvalue(o, n);
+ }
+ else
+ return 0; /* conversion failed */
+ return (e - s) + 1; /* success; return string size */
+}
+
+
+int luaO_utf8esc (char *buff, unsigned long x) {
+ int n = 1; /* number of bytes put in buffer (backwards) */
+ lua_assert(x <= 0x7FFFFFFFu);
+ if (x < 0x80) /* ascii? */
+ buff[UTF8BUFFSZ - 1] = cast_char(x);
+ else { /* need continuation bytes */
+ unsigned int mfb = 0x3f; /* maximum that fits in first byte */
+ do { /* add continuation bytes */
+ buff[UTF8BUFFSZ - (n++)] = cast_char(0x80 | (x & 0x3f));
+ x >>= 6; /* remove added bits */
+ mfb >>= 1; /* now there is one less bit available in first byte */
+ } while (x > mfb); /* still needs continuation byte? */
+ buff[UTF8BUFFSZ - n] = cast_char((~mfb << 1) | x); /* add first byte */
+ }
+ return n;
+}
+
+
+/*
+** Maximum length of the conversion of a number to a string. Must be
+** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT.
+** (For a long long int, this is 19 digits plus a sign and a final '\0',
+** adding to 21. For a long double, it can go to a sign, 33 digits,
+** the dot, an exponent letter, an exponent sign, 5 exponent digits,
+** and a final '\0', adding to 43.)
+*/
+#define MAXNUMBER2STR 44
+
+
+/*
+** Convert a number object to a string, adding it to a buffer
+*/
+static int tostringbuff (TValue *obj, char *buff) {
+ int len;
+ lua_assert(ttisnumber(obj));
+ if (ttisinteger(obj))
+ len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj));
+ else {
+ len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj));
+ if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */
+ buff[len++] = lua_getlocaledecpoint();
+ buff[len++] = '0'; /* adds '.0' to result */
+ }
+ }
+ return len;
+}
+
+
+/*
+** Convert a number object to a Lua string, replacing the value at 'obj'
+*/
+void luaO_tostring (lua_State *L, TValue *obj) {
+ char buff[MAXNUMBER2STR];
+ int len = tostringbuff(obj, buff);
+ setsvalue(L, obj, luaS_newlstr(L, buff, len));
+}
+
+
+
+
+/*
+** {==================================================================
+** 'luaO_pushvfstring'
+** ===================================================================
+*/
+
+/*
+** Size for buffer space used by 'luaO_pushvfstring'. It should be
+** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages,
+** so that 'luaG_addinfo' can work directly on the buffer.
+*/
+#define BUFVFS (LUA_IDSIZE + MAXNUMBER2STR + 95)
+
+/* buffer used by 'luaO_pushvfstring' */
+typedef struct BuffFS {
+ lua_State *L;
+ int pushed; /* true if there is a part of the result on the stack */
+ int blen; /* length of partial string in 'space' */
+ char space[BUFVFS]; /* holds last part of the result */
+} BuffFS;
+
+
+/*
+** Push given string to the stack, as part of the result, and
+** join it to previous partial result if there is one.
+** It may call 'luaV_concat' while using one slot from EXTRA_STACK.
+** This call cannot invoke metamethods, as both operands must be
+** strings. It can, however, raise an error if the result is too
+** long. In that case, 'luaV_concat' frees the extra slot before
+** raising the error.
+*/
+static void pushstr (BuffFS *buff, const char *str, size_t lstr) {
+ lua_State *L = buff->L;
+ setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr));
+ L->top.p++; /* may use one slot from EXTRA_STACK */
+ if (!buff->pushed) /* no previous string on the stack? */
+ buff->pushed = 1; /* now there is one */
+ else /* join previous string with new one */
+ luaV_concat(L, 2);
+}
+
+
+/*
+** empty the buffer space into the stack
+*/
+static void clearbuff (BuffFS *buff) {
+ pushstr(buff, buff->space, buff->blen); /* push buffer contents */
+ buff->blen = 0; /* space now is empty */
+}
+
+
+/*
+** Get a space of size 'sz' in the buffer. If buffer has not enough
+** space, empty it. 'sz' must fit in an empty buffer.
+*/
+static char *getbuff (BuffFS *buff, int sz) {
+ lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS);
+ if (sz > BUFVFS - buff->blen) /* not enough space? */
+ clearbuff(buff);
+ return buff->space + buff->blen;
+}
+
+
+#define addsize(b,sz) ((b)->blen += (sz))
+
+
+/*
+** Add 'str' to the buffer. If string is larger than the buffer space,
+** push the string directly to the stack.
+*/
+static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
+ if (slen <= BUFVFS) { /* does string fit into buffer? */
+ char *bf = getbuff(buff, cast_int(slen));
+ memcpy(bf, str, slen); /* add string to buffer */
+ addsize(buff, cast_int(slen));
+ }
+ else { /* string larger than buffer */
+ clearbuff(buff); /* string comes after buffer's content */
+ pushstr(buff, str, slen); /* push string */
+ }
+}
+
+
+/*
+** Add a numeral to the buffer.
+*/
+static void addnum2buff (BuffFS *buff, TValue *num) {
+ char *numbuff = getbuff(buff, MAXNUMBER2STR);
+ int len = tostringbuff(num, numbuff); /* format number into 'numbuff' */
+ addsize(buff, len);
+}
+
+
+/*
+** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%'
+ conventional formats, plus Lua-specific '%I' and '%U'
+*/
+const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
+ BuffFS buff; /* holds last part of the result */
+ const char *e; /* points to next '%' */
+ buff.pushed = buff.blen = 0;
+ buff.L = L;
+ while ((e = strchr(fmt, '%')) != NULL) {
+ addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */
+ switch (*(e + 1)) { /* conversion specifier */
+ case 's': { /* zero-terminated string */
+ const char *s = va_arg(argp, char *);
+ if (s == NULL) s = "(null)";
+ addstr2buff(&buff, s, strlen(s));
+ break;
+ }
+ case 'c': { /* an 'int' as a character */
+ char c = cast_uchar(va_arg(argp, int));
+ addstr2buff(&buff, &c, sizeof(char));
+ break;
+ }
+ case 'd': { /* an 'int' */
+ TValue num;
+ setivalue(&num, va_arg(argp, int));
+ addnum2buff(&buff, &num);
+ break;
+ }
+ case 'I': { /* a 'lua_Integer' */
+ TValue num;
+ setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt)));
+ addnum2buff(&buff, &num);
+ break;
+ }
+ case 'f': { /* a 'lua_Number' */
+ TValue num;
+ setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber)));
+ addnum2buff(&buff, &num);
+ break;
+ }
+ case 'p': { /* a pointer */
+ const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */
+ char *bf = getbuff(&buff, sz);
+ void *p = va_arg(argp, void *);
+ int len = lua_pointer2str(bf, sz, p);
+ addsize(&buff, len);
+ break;
+ }
+ case 'U': { /* a 'long' as a UTF-8 sequence */
+ char bf[UTF8BUFFSZ];
+ int len = luaO_utf8esc(bf, va_arg(argp, long));
+ addstr2buff(&buff, bf + UTF8BUFFSZ - len, len);
+ break;
+ }
+ case '%': {
+ addstr2buff(&buff, "%", 1);
+ break;
+ }
+ default: {
+ luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'",
+ *(e + 1));
+ }
+ }
+ fmt = e + 2; /* skip '%' and the specifier */
+ }
+ addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
+ clearbuff(&buff); /* empty buffer into the stack */
+ lua_assert(buff.pushed == 1);
+ return svalue(s2v(L->top.p - 1));
+}
+
+
+const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
+ const char *msg;
+ va_list argp;
+ va_start(argp, fmt);
+ msg = luaO_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ return msg;
+}
+
+/* }================================================================== */
+
+
+#define RETS "..."
+#define PRE "[string \""
+#define POS "\"]"
+
+#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) )
+
+void luaO_chunkid (char *out, const char *source, size_t srclen) {
+ size_t bufflen = LUA_IDSIZE; /* free space in buffer */
+ if (*source == '=') { /* 'literal' source */
+ if (srclen <= bufflen) /* small enough? */
+ memcpy(out, source + 1, srclen * sizeof(char));
+ else { /* truncate it */
+ addstr(out, source + 1, bufflen - 1);
+ *out = '\0';
+ }
+ }
+ else if (*source == '@') { /* file name */
+ if (srclen <= bufflen) /* small enough? */
+ memcpy(out, source + 1, srclen * sizeof(char));
+ else { /* add '...' before rest of name */
+ addstr(out, RETS, LL(RETS));
+ bufflen -= LL(RETS);
+ memcpy(out, source + 1 + srclen - bufflen, bufflen * sizeof(char));
+ }
+ }
+ else { /* string; format as [string "source"] */
+ const char *nl = strchr(source, '\n'); /* find first new line (if any) */
+ addstr(out, PRE, LL(PRE)); /* add prefix */
+ bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */
+ if (srclen < bufflen && nl == NULL) { /* small one-line source? */
+ addstr(out, source, srclen); /* keep it */
+ }
+ else {
+ if (nl != NULL) srclen = nl - source; /* stop at first newline */
+ if (srclen > bufflen) srclen = bufflen;
+ addstr(out, source, srclen);
+ addstr(out, RETS, LL(RETS));
+ }
+ memcpy(out, POS, (LL(POS) + 1) * sizeof(char));
+ }
+}
+
diff --git a/src/libs/3rdparty/lua/src/lobject.h b/src/libs/3rdparty/lua/src/lobject.h
new file mode 100644
index 0000000000..556608e4aa
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lobject.h
@@ -0,0 +1,815 @@
+/*
+** $Id: lobject.h $
+** Type definitions for Lua objects
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lobject_h
+#define lobject_h
+
+
+#include <stdarg.h>
+
+
+#include "llimits.h"
+#include "lua.h"
+
+
+/*
+** Extra types for collectable non-values
+*/
+#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */
+#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */
+#define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */
+
+
+
+/*
+** number of all possible types (including LUA_TNONE but excluding DEADKEY)
+*/
+#define LUA_TOTALTYPES (LUA_TPROTO + 2)
+
+
+/*
+** tags for Tagged Values have the following use of bits:
+** bits 0-3: actual tag (a LUA_T* constant)
+** bits 4-5: variant bits
+** bit 6: whether value is collectable
+*/
+
+/* add variant bits to a type */
+#define makevariant(t,v) ((t) | ((v) << 4))
+
+
+
+/*
+** Union of all Lua values
+*/
+typedef union Value {
+ struct GCObject *gc; /* collectable objects */
+ void *p; /* light userdata */
+ lua_CFunction f; /* light C functions */
+ lua_Integer i; /* integer numbers */
+ lua_Number n; /* float numbers */
+ /* not used, but may avoid warnings for uninitialized value */
+ lu_byte ub;
+} Value;
+
+
+/*
+** Tagged Values. This is the basic representation of values in Lua:
+** an actual value plus a tag with its type.
+*/
+
+#define TValuefields Value value_; lu_byte tt_
+
+typedef struct TValue {
+ TValuefields;
+} TValue;
+
+
+#define val_(o) ((o)->value_)
+#define valraw(o) (val_(o))
+
+
+/* raw type tag of a TValue */
+#define rawtt(o) ((o)->tt_)
+
+/* tag with no variants (bits 0-3) */
+#define novariant(t) ((t) & 0x0F)
+
+/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */
+#define withvariant(t) ((t) & 0x3F)
+#define ttypetag(o) withvariant(rawtt(o))
+
+/* type of a TValue */
+#define ttype(o) (novariant(rawtt(o)))
+
+
+/* Macros to test type */
+#define checktag(o,t) (rawtt(o) == (t))
+#define checktype(o,t) (ttype(o) == (t))
+
+
+/* Macros for internal tests */
+
+/* collectable object has the same tag as the original value */
+#define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt)
+
+/*
+** Any value being manipulated by the program either is non
+** collectable, or the collectable object has the right tag
+** and it is not dead. The option 'L == NULL' allows other
+** macros using this one to be used where L is not available.
+*/
+#define checkliveness(L,obj) \
+ ((void)L, lua_longassert(!iscollectable(obj) || \
+ (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj))))))
+
+
+/* Macros to set values */
+
+/* set a value's tag */
+#define settt_(o,t) ((o)->tt_=(t))
+
+
+/* main macro to copy values (from 'obj2' to 'obj1') */
+#define setobj(L,obj1,obj2) \
+ { TValue *io1=(obj1); const TValue *io2=(obj2); \
+ io1->value_ = io2->value_; settt_(io1, io2->tt_); \
+ checkliveness(L,io1); lua_assert(!isnonstrictnil(io1)); }
+
+/*
+** Different types of assignments, according to source and destination.
+** (They are mostly equal now, but may be different in the future.)
+*/
+
+/* from stack to stack */
+#define setobjs2s(L,o1,o2) setobj(L,s2v(o1),s2v(o2))
+/* to stack (not from same stack) */
+#define setobj2s(L,o1,o2) setobj(L,s2v(o1),o2)
+/* from table to same table */
+#define setobjt2t setobj
+/* to new object */
+#define setobj2n setobj
+/* to table */
+#define setobj2t setobj
+
+
+/*
+** Entries in a Lua stack. Field 'tbclist' forms a list of all
+** to-be-closed variables active in this stack. Dummy entries are
+** used when the distance between two tbc variables does not fit
+** in an unsigned short. They are represented by delta==0, and
+** their real delta is always the maximum value that fits in
+** that field.
+*/
+typedef union StackValue {
+ TValue val;
+ struct {
+ TValuefields;
+ unsigned short delta;
+ } tbclist;
+} StackValue;
+
+
+/* index to stack elements */
+typedef StackValue *StkId;
+
+
+/*
+** When reallocating the stack, change all pointers to the stack into
+** proper offsets.
+*/
+typedef union {
+ StkId p; /* actual pointer */
+ ptrdiff_t offset; /* used while the stack is being reallocated */
+} StkIdRel;
+
+
+/* convert a 'StackValue' to a 'TValue' */
+#define s2v(o) (&(o)->val)
+
+
+
+/*
+** {==================================================================
+** Nil
+** ===================================================================
+*/
+
+/* Standard nil */
+#define LUA_VNIL makevariant(LUA_TNIL, 0)
+
+/* Empty slot (which might be different from a slot containing nil) */
+#define LUA_VEMPTY makevariant(LUA_TNIL, 1)
+
+/* Value returned for a key not found in a table (absent key) */
+#define LUA_VABSTKEY makevariant(LUA_TNIL, 2)
+
+
+/* macro to test for (any kind of) nil */
+#define ttisnil(v) checktype((v), LUA_TNIL)
+
+
+/* macro to test for a standard nil */
+#define ttisstrictnil(o) checktag((o), LUA_VNIL)
+
+
+#define setnilvalue(obj) settt_(obj, LUA_VNIL)
+
+
+#define isabstkey(v) checktag((v), LUA_VABSTKEY)
+
+
+/*
+** macro to detect non-standard nils (used only in assertions)
+*/
+#define isnonstrictnil(v) (ttisnil(v) && !ttisstrictnil(v))
+
+
+/*
+** By default, entries with any kind of nil are considered empty.
+** (In any definition, values associated with absent keys must also
+** be accepted as empty.)
+*/
+#define isempty(v) ttisnil(v)
+
+
+/* macro defining a value corresponding to an absent key */
+#define ABSTKEYCONSTANT {NULL}, LUA_VABSTKEY
+
+
+/* mark an entry as empty */
+#define setempty(v) settt_(v, LUA_VEMPTY)
+
+
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Booleans
+** ===================================================================
+*/
+
+
+#define LUA_VFALSE makevariant(LUA_TBOOLEAN, 0)
+#define LUA_VTRUE makevariant(LUA_TBOOLEAN, 1)
+
+#define ttisboolean(o) checktype((o), LUA_TBOOLEAN)
+#define ttisfalse(o) checktag((o), LUA_VFALSE)
+#define ttistrue(o) checktag((o), LUA_VTRUE)
+
+
+#define l_isfalse(o) (ttisfalse(o) || ttisnil(o))
+
+
+#define setbfvalue(obj) settt_(obj, LUA_VFALSE)
+#define setbtvalue(obj) settt_(obj, LUA_VTRUE)
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Threads
+** ===================================================================
+*/
+
+#define LUA_VTHREAD makevariant(LUA_TTHREAD, 0)
+
+#define ttisthread(o) checktag((o), ctb(LUA_VTHREAD))
+
+#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc))
+
+#define setthvalue(L,obj,x) \
+ { TValue *io = (obj); lua_State *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTHREAD)); \
+ checkliveness(L,io); }
+
+#define setthvalue2s(L,o,t) setthvalue(L,s2v(o),t)
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Collectable Objects
+** ===================================================================
+*/
+
+/*
+** Common Header for all collectable objects (in macro form, to be
+** included in other objects)
+*/
+#define CommonHeader struct GCObject *next; lu_byte tt; lu_byte marked
+
+
+/* Common type for all collectable objects */
+typedef struct GCObject {
+ CommonHeader;
+} GCObject;
+
+
+/* Bit mark for collectable types */
+#define BIT_ISCOLLECTABLE (1 << 6)
+
+#define iscollectable(o) (rawtt(o) & BIT_ISCOLLECTABLE)
+
+/* mark a tag as collectable */
+#define ctb(t) ((t) | BIT_ISCOLLECTABLE)
+
+#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc)
+
+#define gcvalueraw(v) ((v).gc)
+
+#define setgcovalue(L,obj,x) \
+ { TValue *io = (obj); GCObject *i_g=(x); \
+ val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); }
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Numbers
+** ===================================================================
+*/
+
+/* Variant tags for numbers */
+#define LUA_VNUMINT makevariant(LUA_TNUMBER, 0) /* integer numbers */
+#define LUA_VNUMFLT makevariant(LUA_TNUMBER, 1) /* float numbers */
+
+#define ttisnumber(o) checktype((o), LUA_TNUMBER)
+#define ttisfloat(o) checktag((o), LUA_VNUMFLT)
+#define ttisinteger(o) checktag((o), LUA_VNUMINT)
+
+#define nvalue(o) check_exp(ttisnumber(o), \
+ (ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o)))
+#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n)
+#define ivalue(o) check_exp(ttisinteger(o), val_(o).i)
+
+#define fltvalueraw(v) ((v).n)
+#define ivalueraw(v) ((v).i)
+
+#define setfltvalue(obj,x) \
+ { TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_VNUMFLT); }
+
+#define chgfltvalue(obj,x) \
+ { TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); }
+
+#define setivalue(obj,x) \
+ { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_VNUMINT); }
+
+#define chgivalue(obj,x) \
+ { TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); }
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Strings
+** ===================================================================
+*/
+
+/* Variant tags for strings */
+#define LUA_VSHRSTR makevariant(LUA_TSTRING, 0) /* short strings */
+#define LUA_VLNGSTR makevariant(LUA_TSTRING, 1) /* long strings */
+
+#define ttisstring(o) checktype((o), LUA_TSTRING)
+#define ttisshrstring(o) checktag((o), ctb(LUA_VSHRSTR))
+#define ttislngstring(o) checktag((o), ctb(LUA_VLNGSTR))
+
+#define tsvalueraw(v) (gco2ts((v).gc))
+
+#define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc))
+
+#define setsvalue(L,obj,x) \
+ { TValue *io = (obj); TString *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \
+ checkliveness(L,io); }
+
+/* set a string to the stack */
+#define setsvalue2s(L,o,s) setsvalue(L,s2v(o),s)
+
+/* set a string to a new object */
+#define setsvalue2n setsvalue
+
+
+/*
+** Header for a string value.
+*/
+typedef struct TString {
+ CommonHeader;
+ lu_byte extra; /* reserved words for short strings; "has hash" for longs */
+ lu_byte shrlen; /* length for short strings */
+ unsigned int hash;
+ union {
+ size_t lnglen; /* length for long strings */
+ struct TString *hnext; /* linked list for hash table */
+ } u;
+ char contents[1];
+} TString;
+
+
+
+/*
+** Get the actual string (array of bytes) from a 'TString'.
+*/
+#define getstr(ts) ((ts)->contents)
+
+
+/* get the actual string (array of bytes) from a Lua value */
+#define svalue(o) getstr(tsvalue(o))
+
+/* get string length from 'TString *s' */
+#define tsslen(s) ((s)->tt == LUA_VSHRSTR ? (s)->shrlen : (s)->u.lnglen)
+
+/* get string length from 'TValue *o' */
+#define vslen(o) tsslen(tsvalue(o))
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Userdata
+** ===================================================================
+*/
+
+
+/*
+** Light userdata should be a variant of userdata, but for compatibility
+** reasons they are also different types.
+*/
+#define LUA_VLIGHTUSERDATA makevariant(LUA_TLIGHTUSERDATA, 0)
+
+#define LUA_VUSERDATA makevariant(LUA_TUSERDATA, 0)
+
+#define ttislightuserdata(o) checktag((o), LUA_VLIGHTUSERDATA)
+#define ttisfulluserdata(o) checktag((o), ctb(LUA_VUSERDATA))
+
+#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p)
+#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc))
+
+#define pvalueraw(v) ((v).p)
+
+#define setpvalue(obj,x) \
+ { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_VLIGHTUSERDATA); }
+
+#define setuvalue(L,obj,x) \
+ { TValue *io = (obj); Udata *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VUSERDATA)); \
+ checkliveness(L,io); }
+
+
+/* Ensures that addresses after this type are always fully aligned. */
+typedef union UValue {
+ TValue uv;
+ LUAI_MAXALIGN; /* ensures maximum alignment for udata bytes */
+} UValue;
+
+
+/*
+** Header for userdata with user values;
+** memory area follows the end of this structure.
+*/
+typedef struct Udata {
+ CommonHeader;
+ unsigned short nuvalue; /* number of user values */
+ size_t len; /* number of bytes */
+ struct Table *metatable;
+ GCObject *gclist;
+ UValue uv[1]; /* user values */
+} Udata;
+
+
+/*
+** Header for userdata with no user values. These userdata do not need
+** to be gray during GC, and therefore do not need a 'gclist' field.
+** To simplify, the code always use 'Udata' for both kinds of userdata,
+** making sure it never accesses 'gclist' on userdata with no user values.
+** This structure here is used only to compute the correct size for
+** this representation. (The 'bindata' field in its end ensures correct
+** alignment for binary data following this header.)
+*/
+typedef struct Udata0 {
+ CommonHeader;
+ unsigned short nuvalue; /* number of user values */
+ size_t len; /* number of bytes */
+ struct Table *metatable;
+ union {LUAI_MAXALIGN;} bindata;
+} Udata0;
+
+
+/* compute the offset of the memory area of a userdata */
+#define udatamemoffset(nuv) \
+ ((nuv) == 0 ? offsetof(Udata0, bindata) \
+ : offsetof(Udata, uv) + (sizeof(UValue) * (nuv)))
+
+/* get the address of the memory block inside 'Udata' */
+#define getudatamem(u) (cast_charp(u) + udatamemoffset((u)->nuvalue))
+
+/* compute the size of a userdata */
+#define sizeudata(nuv,nb) (udatamemoffset(nuv) + (nb))
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Prototypes
+** ===================================================================
+*/
+
+#define LUA_VPROTO makevariant(LUA_TPROTO, 0)
+
+
+/*
+** Description of an upvalue for function prototypes
+*/
+typedef struct Upvaldesc {
+ TString *name; /* upvalue name (for debug information) */
+ lu_byte instack; /* whether it is in stack (register) */
+ lu_byte idx; /* index of upvalue (in stack or in outer function's list) */
+ lu_byte kind; /* kind of corresponding variable */
+} Upvaldesc;
+
+
+/*
+** Description of a local variable for function prototypes
+** (used for debug information)
+*/
+typedef struct LocVar {
+ TString *varname;
+ int startpc; /* first point where variable is active */
+ int endpc; /* first point where variable is dead */
+} LocVar;
+
+
+/*
+** Associates the absolute line source for a given instruction ('pc').
+** The array 'lineinfo' gives, for each instruction, the difference in
+** lines from the previous instruction. When that difference does not
+** fit into a byte, Lua saves the absolute line for that instruction.
+** (Lua also saves the absolute line periodically, to speed up the
+** computation of a line number: we can use binary search in the
+** absolute-line array, but we must traverse the 'lineinfo' array
+** linearly to compute a line.)
+*/
+typedef struct AbsLineInfo {
+ int pc;
+ int line;
+} AbsLineInfo;
+
+/*
+** Function Prototypes
+*/
+typedef struct Proto {
+ CommonHeader;
+ lu_byte numparams; /* number of fixed (named) parameters */
+ lu_byte is_vararg;
+ lu_byte maxstacksize; /* number of registers needed by this function */
+ int sizeupvalues; /* size of 'upvalues' */
+ int sizek; /* size of 'k' */
+ int sizecode;
+ int sizelineinfo;
+ int sizep; /* size of 'p' */
+ int sizelocvars;
+ int sizeabslineinfo; /* size of 'abslineinfo' */
+ int linedefined; /* debug information */
+ int lastlinedefined; /* debug information */
+ TValue *k; /* constants used by the function */
+ Instruction *code; /* opcodes */
+ struct Proto **p; /* functions defined inside the function */
+ Upvaldesc *upvalues; /* upvalue information */
+ ls_byte *lineinfo; /* information about source lines (debug information) */
+ AbsLineInfo *abslineinfo; /* idem */
+ LocVar *locvars; /* information about local variables (debug information) */
+ TString *source; /* used for debug information */
+ GCObject *gclist;
+} Proto;
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Functions
+** ===================================================================
+*/
+
+#define LUA_VUPVAL makevariant(LUA_TUPVAL, 0)
+
+
+/* Variant tags for functions */
+#define LUA_VLCL makevariant(LUA_TFUNCTION, 0) /* Lua closure */
+#define LUA_VLCF makevariant(LUA_TFUNCTION, 1) /* light C function */
+#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */
+
+#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
+#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL))
+#define ttislcf(o) checktag((o), LUA_VLCF)
+#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL))
+#define ttisclosure(o) (ttisLclosure(o) || ttisCclosure(o))
+
+
+#define isLfunction(o) ttisLclosure(o)
+
+#define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc))
+#define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc))
+#define fvalue(o) check_exp(ttislcf(o), val_(o).f)
+#define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc))
+
+#define fvalueraw(v) ((v).f)
+
+#define setclLvalue(L,obj,x) \
+ { TValue *io = (obj); LClosure *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VLCL)); \
+ checkliveness(L,io); }
+
+#define setclLvalue2s(L,o,cl) setclLvalue(L,s2v(o),cl)
+
+#define setfvalue(obj,x) \
+ { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_VLCF); }
+
+#define setclCvalue(L,obj,x) \
+ { TValue *io = (obj); CClosure *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VCCL)); \
+ checkliveness(L,io); }
+
+
+/*
+** Upvalues for Lua closures
+*/
+typedef struct UpVal {
+ CommonHeader;
+ union {
+ TValue *p; /* points to stack or to its own value */
+ ptrdiff_t offset; /* used while the stack is being reallocated */
+ } v;
+ union {
+ struct { /* (when open) */
+ struct UpVal *next; /* linked list */
+ struct UpVal **previous;
+ } open;
+ TValue value; /* the value (when closed) */
+ } u;
+} UpVal;
+
+
+
+#define ClosureHeader \
+ CommonHeader; lu_byte nupvalues; GCObject *gclist
+
+typedef struct CClosure {
+ ClosureHeader;
+ lua_CFunction f;
+ TValue upvalue[1]; /* list of upvalues */
+} CClosure;
+
+
+typedef struct LClosure {
+ ClosureHeader;
+ struct Proto *p;
+ UpVal *upvals[1]; /* list of upvalues */
+} LClosure;
+
+
+typedef union Closure {
+ CClosure c;
+ LClosure l;
+} Closure;
+
+
+#define getproto(o) (clLvalue(o)->p)
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Tables
+** ===================================================================
+*/
+
+#define LUA_VTABLE makevariant(LUA_TTABLE, 0)
+
+#define ttistable(o) checktag((o), ctb(LUA_VTABLE))
+
+#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc))
+
+#define sethvalue(L,obj,x) \
+ { TValue *io = (obj); Table *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTABLE)); \
+ checkliveness(L,io); }
+
+#define sethvalue2s(L,o,h) sethvalue(L,s2v(o),h)
+
+
+/*
+** Nodes for Hash tables: A pack of two TValue's (key-value pairs)
+** plus a 'next' field to link colliding entries. The distribution
+** of the key's fields ('key_tt' and 'key_val') not forming a proper
+** 'TValue' allows for a smaller size for 'Node' both in 4-byte
+** and 8-byte alignments.
+*/
+typedef union Node {
+ struct NodeKey {
+ TValuefields; /* fields for value */
+ lu_byte key_tt; /* key type */
+ int next; /* for chaining */
+ Value key_val; /* key value */
+ } u;
+ TValue i_val; /* direct access to node's value as a proper 'TValue' */
+} Node;
+
+
+/* copy a value into a key */
+#define setnodekey(L,node,obj) \
+ { Node *n_=(node); const TValue *io_=(obj); \
+ n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \
+ checkliveness(L,io_); }
+
+
+/* copy a value from a key */
+#define getnodekey(L,obj,node) \
+ { TValue *io_=(obj); const Node *n_=(node); \
+ io_->value_ = n_->u.key_val; io_->tt_ = n_->u.key_tt; \
+ checkliveness(L,io_); }
+
+
+/*
+** About 'alimit': if 'isrealasize(t)' is true, then 'alimit' is the
+** real size of 'array'. Otherwise, the real size of 'array' is the
+** smallest power of two not smaller than 'alimit' (or zero iff 'alimit'
+** is zero); 'alimit' is then used as a hint for #t.
+*/
+
+#define BITRAS (1 << 7)
+#define isrealasize(t) (!((t)->flags & BITRAS))
+#define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS))
+#define setnorealasize(t) ((t)->flags |= BITRAS)
+
+
+typedef struct Table {
+ CommonHeader;
+ lu_byte flags; /* 1<<p means tagmethod(p) is not present */
+ lu_byte lsizenode; /* log2 of size of 'node' array */
+ unsigned int alimit; /* "limit" of 'array' array */
+ TValue *array; /* array part */
+ Node *node;
+ Node *lastfree; /* any free position is before this position */
+ struct Table *metatable;
+ GCObject *gclist;
+} Table;
+
+
+/*
+** Macros to manipulate keys inserted in nodes
+*/
+#define keytt(node) ((node)->u.key_tt)
+#define keyval(node) ((node)->u.key_val)
+
+#define keyisnil(node) (keytt(node) == LUA_TNIL)
+#define keyisinteger(node) (keytt(node) == LUA_VNUMINT)
+#define keyival(node) (keyval(node).i)
+#define keyisshrstr(node) (keytt(node) == ctb(LUA_VSHRSTR))
+#define keystrval(node) (gco2ts(keyval(node).gc))
+
+#define setnilkey(node) (keytt(node) = LUA_TNIL)
+
+#define keyiscollectable(n) (keytt(n) & BIT_ISCOLLECTABLE)
+
+#define gckey(n) (keyval(n).gc)
+#define gckeyN(n) (keyiscollectable(n) ? gckey(n) : NULL)
+
+
+/*
+** Dead keys in tables have the tag DEADKEY but keep their original
+** gcvalue. This distinguishes them from regular keys but allows them to
+** be found when searched in a special way. ('next' needs that to find
+** keys removed from a table during a traversal.)
+*/
+#define setdeadkey(node) (keytt(node) = LUA_TDEADKEY)
+#define keyisdead(node) (keytt(node) == LUA_TDEADKEY)
+
+/* }================================================================== */
+
+
+
+/*
+** 'module' operation for hashing (size is always a power of 2)
+*/
+#define lmod(s,size) \
+ (check_exp((size&(size-1))==0, (cast_int((s) & ((size)-1)))))
+
+
+#define twoto(x) (1<<(x))
+#define sizenode(t) (twoto((t)->lsizenode))
+
+
+/* size of buffer for 'luaO_utf8esc' function */
+#define UTF8BUFFSZ 8
+
+LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x);
+LUAI_FUNC int luaO_ceillog2 (unsigned int x);
+LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1,
+ const TValue *p2, TValue *res);
+LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1,
+ const TValue *p2, StkId res);
+LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o);
+LUAI_FUNC int luaO_hexavalue (int c);
+LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj);
+LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,
+ va_list argp);
+LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);
+LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t srclen);
+
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/lopcodes.c b/src/libs/3rdparty/lua/src/lopcodes.c
new file mode 100644
index 0000000000..c67aa227c5
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lopcodes.c
@@ -0,0 +1,104 @@
+/*
+** $Id: lopcodes.c $
+** Opcodes for Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#define lopcodes_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include "lopcodes.h"
+
+
+/* ORDER OP */
+
+LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
+/* MM OT IT T A mode opcode */
+ opmode(0, 0, 0, 0, 1, iABC) /* OP_MOVE */
+ ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADI */
+ ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADF */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADK */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADKX */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADFALSE */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LFALSESKIP */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADTRUE */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADNIL */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETUPVAL */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETUPVAL */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABUP */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABLE */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETI */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETFIELD */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABUP */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABLE */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETI */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETFIELD */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NEWTABLE */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SELF */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDI */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUBK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MULK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MODK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POWK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIVK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIVK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MOD */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POW */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIV */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIV */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BAND */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BOR */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXOR */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHL */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHR */
+ ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBIN */
+ ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI*/
+ ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK*/
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_UNM */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BNOT */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NOT */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LEN */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_CONCAT */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_CLOSE */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TBC */
+ ,opmode(0, 0, 0, 0, 0, isJ) /* OP_JMP */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQ */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LT */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LE */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQK */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQI */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LTI */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LEI */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GTI */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GEI */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_TEST */
+ ,opmode(0, 0, 0, 1, 1, iABC) /* OP_TESTSET */
+ ,opmode(0, 1, 1, 0, 1, iABC) /* OP_CALL */
+ ,opmode(0, 1, 1, 0, 1, iABC) /* OP_TAILCALL */
+ ,opmode(0, 0, 1, 0, 0, iABC) /* OP_RETURN */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN0 */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN1 */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORLOOP */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORPREP */
+ ,opmode(0, 0, 0, 0, 0, iABx) /* OP_TFORPREP */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TFORCALL */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */
+ ,opmode(0, 0, 1, 0, 0, iABC) /* OP_SETLIST */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
+ ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
+ ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */
+ ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
+};
+
diff --git a/src/libs/3rdparty/lua/src/lopcodes.h b/src/libs/3rdparty/lua/src/lopcodes.h
new file mode 100644
index 0000000000..4c55145399
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lopcodes.h
@@ -0,0 +1,405 @@
+/*
+** $Id: lopcodes.h $
+** Opcodes for Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lopcodes_h
+#define lopcodes_h
+
+#include "llimits.h"
+
+
+/*===========================================================================
+ We assume that instructions are unsigned 32-bit integers.
+ All instructions have an opcode in the first 7 bits.
+ Instructions can have the following formats:
+
+ 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
+ 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+iABC C(8) | B(8) |k| A(8) | Op(7) |
+iABx Bx(17) | A(8) | Op(7) |
+iAsBx sBx (signed)(17) | A(8) | Op(7) |
+iAx Ax(25) | Op(7) |
+isJ sJ (signed)(25) | Op(7) |
+
+ A signed argument is represented in excess K: the represented value is
+ the written unsigned value minus K, where K is half the maximum for the
+ corresponding unsigned argument.
+===========================================================================*/
+
+
+enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
+
+
+/*
+** size and position of opcode arguments.
+*/
+#define SIZE_C 8
+#define SIZE_B 8
+#define SIZE_Bx (SIZE_C + SIZE_B + 1)
+#define SIZE_A 8
+#define SIZE_Ax (SIZE_Bx + SIZE_A)
+#define SIZE_sJ (SIZE_Bx + SIZE_A)
+
+#define SIZE_OP 7
+
+#define POS_OP 0
+
+#define POS_A (POS_OP + SIZE_OP)
+#define POS_k (POS_A + SIZE_A)
+#define POS_B (POS_k + 1)
+#define POS_C (POS_B + SIZE_B)
+
+#define POS_Bx POS_k
+
+#define POS_Ax POS_A
+
+#define POS_sJ POS_A
+
+
+/*
+** limits for opcode arguments.
+** we use (signed) 'int' to manipulate most arguments,
+** so they must fit in ints.
+*/
+
+/* Check whether type 'int' has at least 'b' bits ('b' < 32) */
+#define L_INTHASBITS(b) ((UINT_MAX >> ((b) - 1)) >= 1)
+
+
+#if L_INTHASBITS(SIZE_Bx)
+#define MAXARG_Bx ((1<<SIZE_Bx)-1)
+#else
+#define MAXARG_Bx MAX_INT
+#endif
+
+#define OFFSET_sBx (MAXARG_Bx>>1) /* 'sBx' is signed */
+
+
+#if L_INTHASBITS(SIZE_Ax)
+#define MAXARG_Ax ((1<<SIZE_Ax)-1)
+#else
+#define MAXARG_Ax MAX_INT
+#endif
+
+#if L_INTHASBITS(SIZE_sJ)
+#define MAXARG_sJ ((1 << SIZE_sJ) - 1)
+#else
+#define MAXARG_sJ MAX_INT
+#endif
+
+#define OFFSET_sJ (MAXARG_sJ >> 1)
+
+
+#define MAXARG_A ((1<<SIZE_A)-1)
+#define MAXARG_B ((1<<SIZE_B)-1)
+#define MAXARG_C ((1<<SIZE_C)-1)
+#define OFFSET_sC (MAXARG_C >> 1)
+
+#define int2sC(i) ((i) + OFFSET_sC)
+#define sC2int(i) ((i) - OFFSET_sC)
+
+
+/* creates a mask with 'n' 1 bits at position 'p' */
+#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p))
+
+/* creates a mask with 'n' 0 bits at position 'p' */
+#define MASK0(n,p) (~MASK1(n,p))
+
+/*
+** the following macros help to manipulate instructions
+*/
+
+#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
+#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
+ ((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
+
+#define checkopm(i,m) (getOpMode(GET_OPCODE(i)) == m)
+
+
+#define getarg(i,pos,size) (cast_int(((i)>>(pos)) & MASK1(size,0)))
+#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \
+ ((cast(Instruction, v)<<pos)&MASK1(size,pos))))
+
+#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
+#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
+
+#define GETARG_B(i) check_exp(checkopm(i, iABC), getarg(i, POS_B, SIZE_B))
+#define GETARG_sB(i) sC2int(GETARG_B(i))
+#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B)
+
+#define GETARG_C(i) check_exp(checkopm(i, iABC), getarg(i, POS_C, SIZE_C))
+#define GETARG_sC(i) sC2int(GETARG_C(i))
+#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C)
+
+#define TESTARG_k(i) check_exp(checkopm(i, iABC), (cast_int(((i) & (1u << POS_k)))))
+#define GETARG_k(i) check_exp(checkopm(i, iABC), getarg(i, POS_k, 1))
+#define SETARG_k(i,v) setarg(i, v, POS_k, 1)
+
+#define GETARG_Bx(i) check_exp(checkopm(i, iABx), getarg(i, POS_Bx, SIZE_Bx))
+#define SETARG_Bx(i,v) setarg(i, v, POS_Bx, SIZE_Bx)
+
+#define GETARG_Ax(i) check_exp(checkopm(i, iAx), getarg(i, POS_Ax, SIZE_Ax))
+#define SETARG_Ax(i,v) setarg(i, v, POS_Ax, SIZE_Ax)
+
+#define GETARG_sBx(i) \
+ check_exp(checkopm(i, iAsBx), getarg(i, POS_Bx, SIZE_Bx) - OFFSET_sBx)
+#define SETARG_sBx(i,b) SETARG_Bx((i),cast_uint((b)+OFFSET_sBx))
+
+#define GETARG_sJ(i) \
+ check_exp(checkopm(i, isJ), getarg(i, POS_sJ, SIZE_sJ) - OFFSET_sJ)
+#define SETARG_sJ(i,j) \
+ setarg(i, cast_uint((j)+OFFSET_sJ), POS_sJ, SIZE_sJ)
+
+
+#define CREATE_ABCk(o,a,b,c,k) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, b)<<POS_B) \
+ | (cast(Instruction, c)<<POS_C) \
+ | (cast(Instruction, k)<<POS_k))
+
+#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, bc)<<POS_Bx))
+
+#define CREATE_Ax(o,a) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_Ax))
+
+#define CREATE_sJ(o,j,k) ((cast(Instruction, o) << POS_OP) \
+ | (cast(Instruction, j) << POS_sJ) \
+ | (cast(Instruction, k) << POS_k))
+
+
+#if !defined(MAXINDEXRK) /* (for debugging only) */
+#define MAXINDEXRK MAXARG_B
+#endif
+
+
+/*
+** invalid register that fits in 8 bits
+*/
+#define NO_REG MAXARG_A
+
+
+/*
+** R[x] - register
+** K[x] - constant (in constant table)
+** RK(x) == if k(i) then K[x] else R[x]
+*/
+
+
+/*
+** Grep "ORDER OP" if you change these enums. Opcodes marked with a (*)
+** has extra descriptions in the notes after the enumeration.
+*/
+
+typedef enum {
+/*----------------------------------------------------------------------
+ name args description
+------------------------------------------------------------------------*/
+OP_MOVE,/* A B R[A] := R[B] */
+OP_LOADI,/* A sBx R[A] := sBx */
+OP_LOADF,/* A sBx R[A] := (lua_Number)sBx */
+OP_LOADK,/* A Bx R[A] := K[Bx] */
+OP_LOADKX,/* A R[A] := K[extra arg] */
+OP_LOADFALSE,/* A R[A] := false */
+OP_LFALSESKIP,/*A R[A] := false; pc++ (*) */
+OP_LOADTRUE,/* A R[A] := true */
+OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */
+OP_GETUPVAL,/* A B R[A] := UpValue[B] */
+OP_SETUPVAL,/* A B UpValue[B] := R[A] */
+
+OP_GETTABUP,/* A B C R[A] := UpValue[B][K[C]:string] */
+OP_GETTABLE,/* A B C R[A] := R[B][R[C]] */
+OP_GETI,/* A B C R[A] := R[B][C] */
+OP_GETFIELD,/* A B C R[A] := R[B][K[C]:string] */
+
+OP_SETTABUP,/* A B C UpValue[A][K[B]:string] := RK(C) */
+OP_SETTABLE,/* A B C R[A][R[B]] := RK(C) */
+OP_SETI,/* A B C R[A][B] := RK(C) */
+OP_SETFIELD,/* A B C R[A][K[B]:string] := RK(C) */
+
+OP_NEWTABLE,/* A B C k R[A] := {} */
+
+OP_SELF,/* A B C R[A+1] := R[B]; R[A] := R[B][RK(C):string] */
+
+OP_ADDI,/* A B sC R[A] := R[B] + sC */
+
+OP_ADDK,/* A B C R[A] := R[B] + K[C]:number */
+OP_SUBK,/* A B C R[A] := R[B] - K[C]:number */
+OP_MULK,/* A B C R[A] := R[B] * K[C]:number */
+OP_MODK,/* A B C R[A] := R[B] % K[C]:number */
+OP_POWK,/* A B C R[A] := R[B] ^ K[C]:number */
+OP_DIVK,/* A B C R[A] := R[B] / K[C]:number */
+OP_IDIVK,/* A B C R[A] := R[B] // K[C]:number */
+
+OP_BANDK,/* A B C R[A] := R[B] & K[C]:integer */
+OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */
+OP_BXORK,/* A B C R[A] := R[B] ~ K[C]:integer */
+
+OP_SHRI,/* A B sC R[A] := R[B] >> sC */
+OP_SHLI,/* A B sC R[A] := sC << R[B] */
+
+OP_ADD,/* A B C R[A] := R[B] + R[C] */
+OP_SUB,/* A B C R[A] := R[B] - R[C] */
+OP_MUL,/* A B C R[A] := R[B] * R[C] */
+OP_MOD,/* A B C R[A] := R[B] % R[C] */
+OP_POW,/* A B C R[A] := R[B] ^ R[C] */
+OP_DIV,/* A B C R[A] := R[B] / R[C] */
+OP_IDIV,/* A B C R[A] := R[B] // R[C] */
+
+OP_BAND,/* A B C R[A] := R[B] & R[C] */
+OP_BOR,/* A B C R[A] := R[B] | R[C] */
+OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */
+OP_SHL,/* A B C R[A] := R[B] << R[C] */
+OP_SHR,/* A B C R[A] := R[B] >> R[C] */
+
+OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] (*) */
+OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */
+OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */
+
+OP_UNM,/* A B R[A] := -R[B] */
+OP_BNOT,/* A B R[A] := ~R[B] */
+OP_NOT,/* A B R[A] := not R[B] */
+OP_LEN,/* A B R[A] := #R[B] (length operator) */
+
+OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */
+
+OP_CLOSE,/* A close all upvalues >= R[A] */
+OP_TBC,/* A mark variable A "to be closed" */
+OP_JMP,/* sJ pc += sJ */
+OP_EQ,/* A B k if ((R[A] == R[B]) ~= k) then pc++ */
+OP_LT,/* A B k if ((R[A] < R[B]) ~= k) then pc++ */
+OP_LE,/* A B k if ((R[A] <= R[B]) ~= k) then pc++ */
+
+OP_EQK,/* A B k if ((R[A] == K[B]) ~= k) then pc++ */
+OP_EQI,/* A sB k if ((R[A] == sB) ~= k) then pc++ */
+OP_LTI,/* A sB k if ((R[A] < sB) ~= k) then pc++ */
+OP_LEI,/* A sB k if ((R[A] <= sB) ~= k) then pc++ */
+OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */
+OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */
+
+OP_TEST,/* A k if (not R[A] == k) then pc++ */
+OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] (*) */
+
+OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */
+OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */
+
+OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */
+OP_RETURN0,/* return */
+OP_RETURN1,/* A return R[A] */
+
+OP_FORLOOP,/* A Bx update counters; if loop continues then pc-=Bx; */
+OP_FORPREP,/* A Bx <check values and prepare counters>;
+ if not to run then pc+=Bx+1; */
+
+OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */
+OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */
+OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */
+
+OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */
+
+OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */
+
+OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */
+
+OP_VARARGPREP,/*A (adjust vararg parameters) */
+
+OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
+} OpCode;
+
+
+#define NUM_OPCODES ((int)(OP_EXTRAARG) + 1)
+
+
+
+/*===========================================================================
+ Notes:
+
+ (*) Opcode OP_LFALSESKIP is used to convert a condition to a boolean
+ value, in a code equivalent to (not cond ? false : true). (It
+ produces false and skips the next instruction producing true.)
+
+ (*) Opcodes OP_MMBIN and variants follow each arithmetic and
+ bitwise opcode. If the operation succeeds, it skips this next
+ opcode. Otherwise, this opcode calls the corresponding metamethod.
+
+ (*) Opcode OP_TESTSET is used in short-circuit expressions that need
+ both to jump and to produce a value, such as (a = b or c).
+
+ (*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then
+ 'top' is set to last_result+1, so next open instruction (OP_CALL,
+ OP_RETURN*, OP_SETLIST) may use 'top'.
+
+ (*) In OP_VARARG, if (C == 0) then use actual number of varargs and
+ set top (like in OP_CALL with C == 0).
+
+ (*) In OP_RETURN, if (B == 0) then return up to 'top'.
+
+ (*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always
+ OP_EXTRAARG.
+
+ (*) In OP_SETLIST, if (B == 0) then real B = 'top'; if k, then
+ real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the
+ bits of C).
+
+ (*) In OP_NEWTABLE, B is log2 of the hash size (which is always a
+ power of 2) plus 1, or zero for size zero. If not k, the array size
+ is C. Otherwise, the array size is EXTRAARG _ C.
+
+ (*) For comparisons, k specifies what condition the test should accept
+ (true or false).
+
+ (*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped
+ (the constant is the first operand).
+
+ (*) All 'skips' (pc++) assume that next instruction is a jump.
+
+ (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the
+ function builds upvalues, which may need to be closed. C > 0 means
+ the function is vararg, so that its 'func' must be corrected before
+ returning; in this case, (C - 1) is its number of fixed parameters.
+
+ (*) In comparisons with an immediate operand, C signals whether the
+ original operand was a float. (It must be corrected in case of
+ metamethods.)
+
+===========================================================================*/
+
+
+/*
+** masks for instruction properties. The format is:
+** bits 0-2: op mode
+** bit 3: instruction set register A
+** bit 4: operator is a test (next instruction must be a jump)
+** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0)
+** bit 6: instruction sets 'L->top' for next instruction (when C == 0)
+** bit 7: instruction is an MM instruction (call a metamethod)
+*/
+
+LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
+
+#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7))
+#define testAMode(m) (luaP_opmodes[m] & (1 << 3))
+#define testTMode(m) (luaP_opmodes[m] & (1 << 4))
+#define testITMode(m) (luaP_opmodes[m] & (1 << 5))
+#define testOTMode(m) (luaP_opmodes[m] & (1 << 6))
+#define testMMMode(m) (luaP_opmodes[m] & (1 << 7))
+
+/* "out top" (set top for next instruction) */
+#define isOT(i) \
+ ((testOTMode(GET_OPCODE(i)) && GETARG_C(i) == 0) || \
+ GET_OPCODE(i) == OP_TAILCALL)
+
+/* "in top" (uses top from previous instruction) */
+#define isIT(i) (testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0)
+
+#define opmode(mm,ot,it,t,a,m) \
+ (((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m))
+
+
+/* number of list items to accumulate before a SETLIST instruction */
+#define LFIELDS_PER_FLUSH 50
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lopnames.h b/src/libs/3rdparty/lua/src/lopnames.h
new file mode 100644
index 0000000000..965cec9bf2
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lopnames.h
@@ -0,0 +1,103 @@
+/*
+** $Id: lopnames.h $
+** Opcode names
+** See Copyright Notice in lua.h
+*/
+
+#if !defined(lopnames_h)
+#define lopnames_h
+
+#include <stddef.h>
+
+
+/* ORDER OP */
+
+static const char *const opnames[] = {
+ "MOVE",
+ "LOADI",
+ "LOADF",
+ "LOADK",
+ "LOADKX",
+ "LOADFALSE",
+ "LFALSESKIP",
+ "LOADTRUE",
+ "LOADNIL",
+ "GETUPVAL",
+ "SETUPVAL",
+ "GETTABUP",
+ "GETTABLE",
+ "GETI",
+ "GETFIELD",
+ "SETTABUP",
+ "SETTABLE",
+ "SETI",
+ "SETFIELD",
+ "NEWTABLE",
+ "SELF",
+ "ADDI",
+ "ADDK",
+ "SUBK",
+ "MULK",
+ "MODK",
+ "POWK",
+ "DIVK",
+ "IDIVK",
+ "BANDK",
+ "BORK",
+ "BXORK",
+ "SHRI",
+ "SHLI",
+ "ADD",
+ "SUB",
+ "MUL",
+ "MOD",
+ "POW",
+ "DIV",
+ "IDIV",
+ "BAND",
+ "BOR",
+ "BXOR",
+ "SHL",
+ "SHR",
+ "MMBIN",
+ "MMBINI",
+ "MMBINK",
+ "UNM",
+ "BNOT",
+ "NOT",
+ "LEN",
+ "CONCAT",
+ "CLOSE",
+ "TBC",
+ "JMP",
+ "EQ",
+ "LT",
+ "LE",
+ "EQK",
+ "EQI",
+ "LTI",
+ "LEI",
+ "GTI",
+ "GEI",
+ "TEST",
+ "TESTSET",
+ "CALL",
+ "TAILCALL",
+ "RETURN",
+ "RETURN0",
+ "RETURN1",
+ "FORLOOP",
+ "FORPREP",
+ "TFORPREP",
+ "TFORCALL",
+ "TFORLOOP",
+ "SETLIST",
+ "CLOSURE",
+ "VARARG",
+ "VARARGPREP",
+ "EXTRAARG",
+ NULL
+};
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/loslib.c b/src/libs/3rdparty/lua/src/loslib.c
new file mode 100644
index 0000000000..ad5a927688
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/loslib.c
@@ -0,0 +1,428 @@
+/*
+** $Id: loslib.c $
+** Standard Operating System library
+** See Copyright Notice in lua.h
+*/
+
+#define loslib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <errno.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** {==================================================================
+** List of valid conversion specifiers for the 'strftime' function;
+** options are grouped by length; group of length 2 start with '||'.
+** ===================================================================
+*/
+#if !defined(LUA_STRFTIMEOPTIONS) /* { */
+
+#if defined(LUA_USE_WINDOWS)
+#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \
+ "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
+#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */
+#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%"
+#else /* C99 specification */
+#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
+ "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
+#endif
+
+#endif /* } */
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Configuration for time-related stuff
+** ===================================================================
+*/
+
+/*
+** type to represent time_t in Lua
+*/
+#if !defined(LUA_NUMTIME) /* { */
+
+#define l_timet lua_Integer
+#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t))
+#define l_gettime(L,arg) luaL_checkinteger(L, arg)
+
+#else /* }{ */
+
+#define l_timet lua_Number
+#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t))
+#define l_gettime(L,arg) luaL_checknumber(L, arg)
+
+#endif /* } */
+
+
+#if !defined(l_gmtime) /* { */
+/*
+** By default, Lua uses gmtime/localtime, except when POSIX is available,
+** where it uses gmtime_r/localtime_r
+*/
+
+#if defined(LUA_USE_POSIX) /* { */
+
+#define l_gmtime(t,r) gmtime_r(t,r)
+#define l_localtime(t,r) localtime_r(t,r)
+
+#else /* }{ */
+
+/* ISO C definitions */
+#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t))
+#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t))
+
+#endif /* } */
+
+#endif /* } */
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Configuration for 'tmpnam':
+** By default, Lua uses tmpnam except when POSIX is available, where
+** it uses mkstemp.
+** ===================================================================
+*/
+#if !defined(lua_tmpnam) /* { */
+
+#if defined(LUA_USE_POSIX) /* { */
+
+#include <unistd.h>
+
+#define LUA_TMPNAMBUFSIZE 32
+
+#if !defined(LUA_TMPNAMTEMPLATE)
+#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX"
+#endif
+
+#define lua_tmpnam(b,e) { \
+ strcpy(b, LUA_TMPNAMTEMPLATE); \
+ e = mkstemp(b); \
+ if (e != -1) close(e); \
+ e = (e == -1); }
+
+#else /* }{ */
+
+/* ISO C definitions */
+#define LUA_TMPNAMBUFSIZE L_tmpnam
+#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); }
+
+#endif /* } */
+
+#endif /* } */
+/* }================================================================== */
+
+
+#if !defined(l_system)
+#if defined(LUA_USE_IOS)
+/* Despite claiming to be ISO C, iOS does not implement 'system'. */
+#define l_system(cmd) ((cmd) == NULL ? 0 : -1)
+#else
+#define l_system(cmd) system(cmd) /* default definition */
+#endif
+#endif
+
+
+static int os_execute (lua_State *L) {
+ const char *cmd = luaL_optstring(L, 1, NULL);
+ int stat;
+ errno = 0;
+ stat = l_system(cmd);
+ if (cmd != NULL)
+ return luaL_execresult(L, stat);
+ else {
+ lua_pushboolean(L, stat); /* true if there is a shell */
+ return 1;
+ }
+}
+
+
+static int os_remove (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ return luaL_fileresult(L, remove(filename) == 0, filename);
+}
+
+
+static int os_rename (lua_State *L) {
+ const char *fromname = luaL_checkstring(L, 1);
+ const char *toname = luaL_checkstring(L, 2);
+ return luaL_fileresult(L, rename(fromname, toname) == 0, NULL);
+}
+
+
+static int os_tmpname (lua_State *L) {
+ char buff[LUA_TMPNAMBUFSIZE];
+ int err;
+ lua_tmpnam(buff, err);
+ if (l_unlikely(err))
+ return luaL_error(L, "unable to generate a unique filename");
+ lua_pushstring(L, buff);
+ return 1;
+}
+
+
+static int os_getenv (lua_State *L) {
+ lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */
+ return 1;
+}
+
+
+static int os_clock (lua_State *L) {
+ lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
+ return 1;
+}
+
+
+/*
+** {======================================================
+** Time/Date operations
+** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
+** wday=%w+1, yday=%j, isdst=? }
+** =======================================================
+*/
+
+/*
+** About the overflow check: an overflow cannot occur when time
+** is represented by a lua_Integer, because either lua_Integer is
+** large enough to represent all int fields or it is not large enough
+** to represent a time that cause a field to overflow. However, if
+** times are represented as doubles and lua_Integer is int, then the
+** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900
+** to compute the year.
+*/
+static void setfield (lua_State *L, const char *key, int value, int delta) {
+ #if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX)
+ if (l_unlikely(value > LUA_MAXINTEGER - delta))
+ luaL_error(L, "field '%s' is out-of-bound", key);
+ #endif
+ lua_pushinteger(L, (lua_Integer)value + delta);
+ lua_setfield(L, -2, key);
+}
+
+
+static void setboolfield (lua_State *L, const char *key, int value) {
+ if (value < 0) /* undefined? */
+ return; /* does not set field */
+ lua_pushboolean(L, value);
+ lua_setfield(L, -2, key);
+}
+
+
+/*
+** Set all fields from structure 'tm' in the table on top of the stack
+*/
+static void setallfields (lua_State *L, struct tm *stm) {
+ setfield(L, "year", stm->tm_year, 1900);
+ setfield(L, "month", stm->tm_mon, 1);
+ setfield(L, "day", stm->tm_mday, 0);
+ setfield(L, "hour", stm->tm_hour, 0);
+ setfield(L, "min", stm->tm_min, 0);
+ setfield(L, "sec", stm->tm_sec, 0);
+ setfield(L, "yday", stm->tm_yday, 1);
+ setfield(L, "wday", stm->tm_wday, 1);
+ setboolfield(L, "isdst", stm->tm_isdst);
+}
+
+
+static int getboolfield (lua_State *L, const char *key) {
+ int res;
+ res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ return res;
+}
+
+
+static int getfield (lua_State *L, const char *key, int d, int delta) {
+ int isnum;
+ int t = lua_getfield(L, -1, key); /* get field and its type */
+ lua_Integer res = lua_tointegerx(L, -1, &isnum);
+ if (!isnum) { /* field is not an integer? */
+ if (l_unlikely(t != LUA_TNIL)) /* some other value? */
+ return luaL_error(L, "field '%s' is not an integer", key);
+ else if (l_unlikely(d < 0)) /* absent field; no default? */
+ return luaL_error(L, "field '%s' missing in date table", key);
+ res = d;
+ }
+ else {
+ if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res))
+ return luaL_error(L, "field '%s' is out-of-bound", key);
+ res -= delta;
+ }
+ lua_pop(L, 1);
+ return (int)res;
+}
+
+
+static const char *checkoption (lua_State *L, const char *conv,
+ ptrdiff_t convlen, char *buff) {
+ const char *option = LUA_STRFTIMEOPTIONS;
+ int oplen = 1; /* length of options being checked */
+ for (; *option != '\0' && oplen <= convlen; option += oplen) {
+ if (*option == '|') /* next block? */
+ oplen++; /* will check options with next length (+1) */
+ else if (memcmp(conv, option, oplen) == 0) { /* match? */
+ memcpy(buff, conv, oplen); /* copy valid option to buffer */
+ buff[oplen] = '\0';
+ return conv + oplen; /* return next item */
+ }
+ }
+ luaL_argerror(L, 1,
+ lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv));
+ return conv; /* to avoid warnings */
+}
+
+
+static time_t l_checktime (lua_State *L, int arg) {
+ l_timet t = l_gettime(L, arg);
+ luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds");
+ return (time_t)t;
+}
+
+
+/* maximum size for an individual 'strftime' item */
+#define SIZETIMEFMT 250
+
+
+static int os_date (lua_State *L) {
+ size_t slen;
+ const char *s = luaL_optlstring(L, 1, "%c", &slen);
+ time_t t = luaL_opt(L, l_checktime, 2, time(NULL));
+ const char *se = s + slen; /* 's' end */
+ struct tm tmr, *stm;
+ if (*s == '!') { /* UTC? */
+ stm = l_gmtime(&t, &tmr);
+ s++; /* skip '!' */
+ }
+ else
+ stm = l_localtime(&t, &tmr);
+ if (stm == NULL) /* invalid date? */
+ return luaL_error(L,
+ "date result cannot be represented in this installation");
+ if (strcmp(s, "*t") == 0) {
+ lua_createtable(L, 0, 9); /* 9 = number of fields */
+ setallfields(L, stm);
+ }
+ else {
+ char cc[4]; /* buffer for individual conversion specifiers */
+ luaL_Buffer b;
+ cc[0] = '%';
+ luaL_buffinit(L, &b);
+ while (s < se) {
+ if (*s != '%') /* not a conversion specifier? */
+ luaL_addchar(&b, *s++);
+ else {
+ size_t reslen;
+ char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT);
+ s++; /* skip '%' */
+ s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */
+ reslen = strftime(buff, SIZETIMEFMT, cc, stm);
+ luaL_addsize(&b, reslen);
+ }
+ }
+ luaL_pushresult(&b);
+ }
+ return 1;
+}
+
+
+static int os_time (lua_State *L) {
+ time_t t;
+ if (lua_isnoneornil(L, 1)) /* called without args? */
+ t = time(NULL); /* get current time */
+ else {
+ struct tm ts;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 1); /* make sure table is at the top */
+ ts.tm_year = getfield(L, "year", -1, 1900);
+ ts.tm_mon = getfield(L, "month", -1, 1);
+ ts.tm_mday = getfield(L, "day", -1, 0);
+ ts.tm_hour = getfield(L, "hour", 12, 0);
+ ts.tm_min = getfield(L, "min", 0, 0);
+ ts.tm_sec = getfield(L, "sec", 0, 0);
+ ts.tm_isdst = getboolfield(L, "isdst");
+ t = mktime(&ts);
+ setallfields(L, &ts); /* update fields with normalized values */
+ }
+ if (t != (time_t)(l_timet)t || t == (time_t)(-1))
+ return luaL_error(L,
+ "time result cannot be represented in this installation");
+ l_pushtime(L, t);
+ return 1;
+}
+
+
+static int os_difftime (lua_State *L) {
+ time_t t1 = l_checktime(L, 1);
+ time_t t2 = l_checktime(L, 2);
+ lua_pushnumber(L, (lua_Number)difftime(t1, t2));
+ return 1;
+}
+
+/* }====================================================== */
+
+
+static int os_setlocale (lua_State *L) {
+ static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
+ LC_NUMERIC, LC_TIME};
+ static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
+ "numeric", "time", NULL};
+ const char *l = luaL_optstring(L, 1, NULL);
+ int op = luaL_checkoption(L, 2, "all", catnames);
+ lua_pushstring(L, setlocale(cat[op], l));
+ return 1;
+}
+
+
+static int os_exit (lua_State *L) {
+ int status;
+ if (lua_isboolean(L, 1))
+ status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE);
+ else
+ status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS);
+ if (lua_toboolean(L, 2))
+ lua_close(L);
+ if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */
+ return 0;
+}
+
+
+static const luaL_Reg syslib[] = {
+ {"clock", os_clock},
+ {"date", os_date},
+ {"difftime", os_difftime},
+ {"execute", os_execute},
+ {"exit", os_exit},
+ {"getenv", os_getenv},
+ {"remove", os_remove},
+ {"rename", os_rename},
+ {"setlocale", os_setlocale},
+ {"time", os_time},
+ {"tmpname", os_tmpname},
+ {NULL, NULL}
+};
+
+/* }====================================================== */
+
+
+
+LUAMOD_API int luaopen_os (lua_State *L) {
+ luaL_newlib(L, syslib);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lparser.c b/src/libs/3rdparty/lua/src/lparser.c
new file mode 100644
index 0000000000..b745f236f0
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lparser.c
@@ -0,0 +1,1967 @@
+/*
+** $Id: lparser.c $
+** Lua Parser
+** See Copyright Notice in lua.h
+*/
+
+#define lparser_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <limits.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+
+
+
+/* maximum number of local variables per function (must be smaller
+ than 250, due to the bytecode format) */
+#define MAXVARS 200
+
+
+#define hasmultret(k) ((k) == VCALL || (k) == VVARARG)
+
+
+/* because all strings are unified by the scanner, the parser
+ can use pointer equality for string equality */
+#define eqstr(a,b) ((a) == (b))
+
+
+/*
+** nodes for block list (list of active blocks)
+*/
+typedef struct BlockCnt {
+ struct BlockCnt *previous; /* chain */
+ int firstlabel; /* index of first label in this block */
+ int firstgoto; /* index of first pending goto in this block */
+ lu_byte nactvar; /* # active locals outside the block */
+ lu_byte upval; /* true if some variable in the block is an upvalue */
+ lu_byte isloop; /* true if 'block' is a loop */
+ lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */
+} BlockCnt;
+
+
+
+/*
+** prototypes for recursive non-terminal functions
+*/
+static void statement (LexState *ls);
+static void expr (LexState *ls, expdesc *v);
+
+
+static l_noret error_expected (LexState *ls, int token) {
+ luaX_syntaxerror(ls,
+ luaO_pushfstring(ls->L, "%s expected", luaX_token2str(ls, token)));
+}
+
+
+static l_noret errorlimit (FuncState *fs, int limit, const char *what) {
+ lua_State *L = fs->ls->L;
+ const char *msg;
+ int line = fs->f->linedefined;
+ const char *where = (line == 0)
+ ? "main function"
+ : luaO_pushfstring(L, "function at line %d", line);
+ msg = luaO_pushfstring(L, "too many %s (limit is %d) in %s",
+ what, limit, where);
+ luaX_syntaxerror(fs->ls, msg);
+}
+
+
+static void checklimit (FuncState *fs, int v, int l, const char *what) {
+ if (v > l) errorlimit(fs, l, what);
+}
+
+
+/*
+** Test whether next token is 'c'; if so, skip it.
+*/
+static int testnext (LexState *ls, int c) {
+ if (ls->t.token == c) {
+ luaX_next(ls);
+ return 1;
+ }
+ else return 0;
+}
+
+
+/*
+** Check that next token is 'c'.
+*/
+static void check (LexState *ls, int c) {
+ if (ls->t.token != c)
+ error_expected(ls, c);
+}
+
+
+/*
+** Check that next token is 'c' and skip it.
+*/
+static void checknext (LexState *ls, int c) {
+ check(ls, c);
+ luaX_next(ls);
+}
+
+
+#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); }
+
+
+/*
+** Check that next token is 'what' and skip it. In case of error,
+** raise an error that the expected 'what' should match a 'who'
+** in line 'where' (if that is not the current line).
+*/
+static void check_match (LexState *ls, int what, int who, int where) {
+ if (l_unlikely(!testnext(ls, what))) {
+ if (where == ls->linenumber) /* all in the same line? */
+ error_expected(ls, what); /* do not need a complex message */
+ else {
+ luaX_syntaxerror(ls, luaO_pushfstring(ls->L,
+ "%s expected (to close %s at line %d)",
+ luaX_token2str(ls, what), luaX_token2str(ls, who), where));
+ }
+ }
+}
+
+
+static TString *str_checkname (LexState *ls) {
+ TString *ts;
+ check(ls, TK_NAME);
+ ts = ls->t.seminfo.ts;
+ luaX_next(ls);
+ return ts;
+}
+
+
+static void init_exp (expdesc *e, expkind k, int i) {
+ e->f = e->t = NO_JUMP;
+ e->k = k;
+ e->u.info = i;
+}
+
+
+static void codestring (expdesc *e, TString *s) {
+ e->f = e->t = NO_JUMP;
+ e->k = VKSTR;
+ e->u.strval = s;
+}
+
+
+static void codename (LexState *ls, expdesc *e) {
+ codestring(e, str_checkname(ls));
+}
+
+
+/*
+** Register a new local variable in the active 'Proto' (for debug
+** information).
+*/
+static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) {
+ Proto *f = fs->f;
+ int oldsize = f->sizelocvars;
+ luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars,
+ LocVar, SHRT_MAX, "local variables");
+ while (oldsize < f->sizelocvars)
+ f->locvars[oldsize++].varname = NULL;
+ f->locvars[fs->ndebugvars].varname = varname;
+ f->locvars[fs->ndebugvars].startpc = fs->pc;
+ luaC_objbarrier(ls->L, f, varname);
+ return fs->ndebugvars++;
+}
+
+
+/*
+** Create a new local variable with the given 'name'. Return its index
+** in the function.
+*/
+static int new_localvar (LexState *ls, TString *name) {
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Dyndata *dyd = ls->dyd;
+ Vardesc *var;
+ checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
+ MAXVARS, "local variables");
+ luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
+ dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
+ var = &dyd->actvar.arr[dyd->actvar.n++];
+ var->vd.kind = VDKREG; /* default */
+ var->vd.name = name;
+ return dyd->actvar.n - 1 - fs->firstlocal;
+}
+
+#define new_localvarliteral(ls,v) \
+ new_localvar(ls, \
+ luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
+
+
+
+/*
+** Return the "variable description" (Vardesc) of a given variable.
+** (Unless noted otherwise, all variables are referred to by their
+** compiler indices.)
+*/
+static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
+ return &fs->ls->dyd->actvar.arr[fs->firstlocal + vidx];
+}
+
+
+/*
+** Convert 'nvar', a compiler index level, to its corresponding
+** register. For that, search for the highest variable below that level
+** that is in a register and uses its register index ('ridx') plus one.
+*/
+static int reglevel (FuncState *fs, int nvar) {
+ while (nvar-- > 0) {
+ Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */
+ if (vd->vd.kind != RDKCTC) /* is in a register? */
+ return vd->vd.ridx + 1;
+ }
+ return 0; /* no variables in registers */
+}
+
+
+/*
+** Return the number of variables in the register stack for the given
+** function.
+*/
+int luaY_nvarstack (FuncState *fs) {
+ return reglevel(fs, fs->nactvar);
+}
+
+
+/*
+** Get the debug-information entry for current variable 'vidx'.
+*/
+static LocVar *localdebuginfo (FuncState *fs, int vidx) {
+ Vardesc *vd = getlocalvardesc(fs, vidx);
+ if (vd->vd.kind == RDKCTC)
+ return NULL; /* no debug info. for constants */
+ else {
+ int idx = vd->vd.pidx;
+ lua_assert(idx < fs->ndebugvars);
+ return &fs->f->locvars[idx];
+ }
+}
+
+
+/*
+** Create an expression representing variable 'vidx'
+*/
+static void init_var (FuncState *fs, expdesc *e, int vidx) {
+ e->f = e->t = NO_JUMP;
+ e->k = VLOCAL;
+ e->u.var.vidx = vidx;
+ e->u.var.ridx = getlocalvardesc(fs, vidx)->vd.ridx;
+}
+
+
+/*
+** Raises an error if variable described by 'e' is read only
+*/
+static void check_readonly (LexState *ls, expdesc *e) {
+ FuncState *fs = ls->fs;
+ TString *varname = NULL; /* to be set if variable is const */
+ switch (e->k) {
+ case VCONST: {
+ varname = ls->dyd->actvar.arr[e->u.info].vd.name;
+ break;
+ }
+ case VLOCAL: {
+ Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
+ if (vardesc->vd.kind != VDKREG) /* not a regular variable? */
+ varname = vardesc->vd.name;
+ break;
+ }
+ case VUPVAL: {
+ Upvaldesc *up = &fs->f->upvalues[e->u.info];
+ if (up->kind != VDKREG)
+ varname = up->name;
+ break;
+ }
+ default:
+ return; /* other cases cannot be read-only */
+ }
+ if (varname) {
+ const char *msg = luaO_pushfstring(ls->L,
+ "attempt to assign to const variable '%s'", getstr(varname));
+ luaK_semerror(ls, msg); /* error */
+ }
+}
+
+
+/*
+** Start the scope for the last 'nvars' created variables.
+*/
+static void adjustlocalvars (LexState *ls, int nvars) {
+ FuncState *fs = ls->fs;
+ int reglevel = luaY_nvarstack(fs);
+ int i;
+ for (i = 0; i < nvars; i++) {
+ int vidx = fs->nactvar++;
+ Vardesc *var = getlocalvardesc(fs, vidx);
+ var->vd.ridx = reglevel++;
+ var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
+ }
+}
+
+
+/*
+** Close the scope for all variables up to level 'tolevel'.
+** (debug info.)
+*/
+static void removevars (FuncState *fs, int tolevel) {
+ fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel);
+ while (fs->nactvar > tolevel) {
+ LocVar *var = localdebuginfo(fs, --fs->nactvar);
+ if (var) /* does it have debug information? */
+ var->endpc = fs->pc;
+ }
+}
+
+
+/*
+** Search the upvalues of the function 'fs' for one
+** with the given 'name'.
+*/
+static int searchupvalue (FuncState *fs, TString *name) {
+ int i;
+ Upvaldesc *up = fs->f->upvalues;
+ for (i = 0; i < fs->nups; i++) {
+ if (eqstr(up[i].name, name)) return i;
+ }
+ return -1; /* not found */
+}
+
+
+static Upvaldesc *allocupvalue (FuncState *fs) {
+ Proto *f = fs->f;
+ int oldsize = f->sizeupvalues;
+ checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues");
+ luaM_growvector(fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues,
+ Upvaldesc, MAXUPVAL, "upvalues");
+ while (oldsize < f->sizeupvalues)
+ f->upvalues[oldsize++].name = NULL;
+ return &f->upvalues[fs->nups++];
+}
+
+
+static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
+ Upvaldesc *up = allocupvalue(fs);
+ FuncState *prev = fs->prev;
+ if (v->k == VLOCAL) {
+ up->instack = 1;
+ up->idx = v->u.var.ridx;
+ up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
+ lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
+ }
+ else {
+ up->instack = 0;
+ up->idx = cast_byte(v->u.info);
+ up->kind = prev->f->upvalues[v->u.info].kind;
+ lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name));
+ }
+ up->name = name;
+ luaC_objbarrier(fs->ls->L, fs->f, name);
+ return fs->nups - 1;
+}
+
+
+/*
+** Look for an active local variable with the name 'n' in the
+** function 'fs'. If found, initialize 'var' with it and return
+** its expression kind; otherwise return -1.
+*/
+static int searchvar (FuncState *fs, TString *n, expdesc *var) {
+ int i;
+ for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
+ Vardesc *vd = getlocalvardesc(fs, i);
+ if (eqstr(n, vd->vd.name)) { /* found? */
+ if (vd->vd.kind == RDKCTC) /* compile-time constant? */
+ init_exp(var, VCONST, fs->firstlocal + i);
+ else /* real variable */
+ init_var(fs, var, i);
+ return var->k;
+ }
+ }
+ return -1; /* not found */
+}
+
+
+/*
+** Mark block where variable at given level was defined
+** (to emit close instructions later).
+*/
+static void markupval (FuncState *fs, int level) {
+ BlockCnt *bl = fs->bl;
+ while (bl->nactvar > level)
+ bl = bl->previous;
+ bl->upval = 1;
+ fs->needclose = 1;
+}
+
+
+/*
+** Mark that current block has a to-be-closed variable.
+*/
+static void marktobeclosed (FuncState *fs) {
+ BlockCnt *bl = fs->bl;
+ bl->upval = 1;
+ bl->insidetbc = 1;
+ fs->needclose = 1;
+}
+
+
+/*
+** Find a variable with the given name 'n'. If it is an upvalue, add
+** this upvalue into all intermediate functions. If it is a global, set
+** 'var' as 'void' as a flag.
+*/
+static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
+ if (fs == NULL) /* no more levels? */
+ init_exp(var, VVOID, 0); /* default is global */
+ else {
+ int v = searchvar(fs, n, var); /* look up locals at current level */
+ if (v >= 0) { /* found? */
+ if (v == VLOCAL && !base)
+ markupval(fs, var->u.var.vidx); /* local will be used as an upval */
+ }
+ else { /* not found as local at current level; try upvalues */
+ int idx = searchupvalue(fs, n); /* try existing upvalues */
+ if (idx < 0) { /* not found? */
+ singlevaraux(fs->prev, n, var, 0); /* try upper levels */
+ if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
+ idx = newupvalue(fs, n, var); /* will be a new upvalue */
+ else /* it is a global or a constant */
+ return; /* don't need to do anything at this level */
+ }
+ init_exp(var, VUPVAL, idx); /* new or old upvalue */
+ }
+ }
+}
+
+
+/*
+** Find a variable with the given name 'n', handling global variables
+** too.
+*/
+static void singlevar (LexState *ls, expdesc *var) {
+ TString *varname = str_checkname(ls);
+ FuncState *fs = ls->fs;
+ singlevaraux(fs, varname, var, 1);
+ if (var->k == VVOID) { /* global name? */
+ expdesc key;
+ singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
+ lua_assert(var->k != VVOID); /* this one must exist */
+ luaK_exp2anyregup(fs, var); /* but could be a constant */
+ codestring(&key, varname); /* key is variable name */
+ luaK_indexed(fs, var, &key); /* env[varname] */
+ }
+}
+
+
+/*
+** Adjust the number of results from an expression list 'e' with 'nexps'
+** expressions to 'nvars' values.
+*/
+static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
+ FuncState *fs = ls->fs;
+ int needed = nvars - nexps; /* extra values needed */
+ if (hasmultret(e->k)) { /* last expression has multiple returns? */
+ int extra = needed + 1; /* discount last expression itself */
+ if (extra < 0)
+ extra = 0;
+ luaK_setreturns(fs, e, extra); /* last exp. provides the difference */
+ }
+ else {
+ if (e->k != VVOID) /* at least one expression? */
+ luaK_exp2nextreg(fs, e); /* close last expression */
+ if (needed > 0) /* missing values? */
+ luaK_nil(fs, fs->freereg, needed); /* complete with nils */
+ }
+ if (needed > 0)
+ luaK_reserveregs(fs, needed); /* registers for extra values */
+ else /* adding 'needed' is actually a subtraction */
+ fs->freereg += needed; /* remove extra values */
+}
+
+
+#define enterlevel(ls) luaE_incCstack(ls->L)
+
+
+#define leavelevel(ls) ((ls)->L->nCcalls--)
+
+
+/*
+** Generates an error that a goto jumps into the scope of some
+** local variable.
+*/
+static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
+ const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name);
+ const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
+ msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
+ luaK_semerror(ls, msg); /* raise the error */
+}
+
+
+/*
+** Solves the goto at index 'g' to given 'label' and removes it
+** from the list of pending gotos.
+** If it jumps into the scope of some variable, raises an error.
+*/
+static void solvegoto (LexState *ls, int g, Labeldesc *label) {
+ int i;
+ Labellist *gl = &ls->dyd->gt; /* list of gotos */
+ Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
+ lua_assert(eqstr(gt->name, label->name));
+ if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
+ jumpscopeerror(ls, gt);
+ luaK_patchlist(ls->fs, gt->pc, label->pc);
+ for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
+ gl->arr[i] = gl->arr[i + 1];
+ gl->n--;
+}
+
+
+/*
+** Search for an active label with the given name.
+*/
+static Labeldesc *findlabel (LexState *ls, TString *name) {
+ int i;
+ Dyndata *dyd = ls->dyd;
+ /* check labels in current function for a match */
+ for (i = ls->fs->firstlabel; i < dyd->label.n; i++) {
+ Labeldesc *lb = &dyd->label.arr[i];
+ if (eqstr(lb->name, name)) /* correct label? */
+ return lb;
+ }
+ return NULL; /* label not found */
+}
+
+
+/*
+** Adds a new label/goto in the corresponding list.
+*/
+static int newlabelentry (LexState *ls, Labellist *l, TString *name,
+ int line, int pc) {
+ int n = l->n;
+ luaM_growvector(ls->L, l->arr, n, l->size,
+ Labeldesc, SHRT_MAX, "labels/gotos");
+ l->arr[n].name = name;
+ l->arr[n].line = line;
+ l->arr[n].nactvar = ls->fs->nactvar;
+ l->arr[n].close = 0;
+ l->arr[n].pc = pc;
+ l->n = n + 1;
+ return n;
+}
+
+
+static int newgotoentry (LexState *ls, TString *name, int line, int pc) {
+ return newlabelentry(ls, &ls->dyd->gt, name, line, pc);
+}
+
+
+/*
+** Solves forward jumps. Check whether new label 'lb' matches any
+** pending gotos in current block and solves them. Return true
+** if any of the gotos need to close upvalues.
+*/
+static int solvegotos (LexState *ls, Labeldesc *lb) {
+ Labellist *gl = &ls->dyd->gt;
+ int i = ls->fs->bl->firstgoto;
+ int needsclose = 0;
+ while (i < gl->n) {
+ if (eqstr(gl->arr[i].name, lb->name)) {
+ needsclose |= gl->arr[i].close;
+ solvegoto(ls, i, lb); /* will remove 'i' from the list */
+ }
+ else
+ i++;
+ }
+ return needsclose;
+}
+
+
+/*
+** Create a new label with the given 'name' at the given 'line'.
+** 'last' tells whether label is the last non-op statement in its
+** block. Solves all pending gotos to this new label and adds
+** a close instruction if necessary.
+** Returns true iff it added a close instruction.
+*/
+static int createlabel (LexState *ls, TString *name, int line,
+ int last) {
+ FuncState *fs = ls->fs;
+ Labellist *ll = &ls->dyd->label;
+ int l = newlabelentry(ls, ll, name, line, luaK_getlabel(fs));
+ if (last) { /* label is last no-op statement in the block? */
+ /* assume that locals are already out of scope */
+ ll->arr[l].nactvar = fs->bl->nactvar;
+ }
+ if (solvegotos(ls, &ll->arr[l])) { /* need close? */
+ luaK_codeABC(fs, OP_CLOSE, luaY_nvarstack(fs), 0, 0);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+** Adjust pending gotos to outer level of a block.
+*/
+static void movegotosout (FuncState *fs, BlockCnt *bl) {
+ int i;
+ Labellist *gl = &fs->ls->dyd->gt;
+ /* correct pending gotos to current block */
+ for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
+ Labeldesc *gt = &gl->arr[i];
+ /* leaving a variable scope? */
+ if (reglevel(fs, gt->nactvar) > reglevel(fs, bl->nactvar))
+ gt->close |= bl->upval; /* jump may need a close */
+ gt->nactvar = bl->nactvar; /* update goto level */
+ }
+}
+
+
+static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
+ bl->isloop = isloop;
+ bl->nactvar = fs->nactvar;
+ bl->firstlabel = fs->ls->dyd->label.n;
+ bl->firstgoto = fs->ls->dyd->gt.n;
+ bl->upval = 0;
+ bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
+ bl->previous = fs->bl;
+ fs->bl = bl;
+ lua_assert(fs->freereg == luaY_nvarstack(fs));
+}
+
+
+/*
+** generates an error for an undefined 'goto'.
+*/
+static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
+ const char *msg;
+ if (eqstr(gt->name, luaS_newliteral(ls->L, "break"))) {
+ msg = "break outside loop at line %d";
+ msg = luaO_pushfstring(ls->L, msg, gt->line);
+ }
+ else {
+ msg = "no visible label '%s' for <goto> at line %d";
+ msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line);
+ }
+ luaK_semerror(ls, msg);
+}
+
+
+static void leaveblock (FuncState *fs) {
+ BlockCnt *bl = fs->bl;
+ LexState *ls = fs->ls;
+ int hasclose = 0;
+ int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */
+ removevars(fs, bl->nactvar); /* remove block locals */
+ lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */
+ if (bl->isloop) /* has to fix pending breaks? */
+ hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
+ if (!hasclose && bl->previous && bl->upval) /* still need a 'close'? */
+ luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0);
+ fs->freereg = stklevel; /* free registers */
+ ls->dyd->label.n = bl->firstlabel; /* remove local labels */
+ fs->bl = bl->previous; /* current block now is previous one */
+ if (bl->previous) /* was it a nested block? */
+ movegotosout(fs, bl); /* update pending gotos to enclosing block */
+ else {
+ if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */
+ undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */
+ }
+}
+
+
+/*
+** adds a new prototype into list of prototypes
+*/
+static Proto *addprototype (LexState *ls) {
+ Proto *clp;
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f; /* prototype of current function */
+ if (fs->np >= f->sizep) {
+ int oldsize = f->sizep;
+ luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions");
+ while (oldsize < f->sizep)
+ f->p[oldsize++] = NULL;
+ }
+ f->p[fs->np++] = clp = luaF_newproto(L);
+ luaC_objbarrier(L, f, clp);
+ return clp;
+}
+
+
+/*
+** codes instruction to create new closure in parent function.
+** The OP_CLOSURE instruction uses the last available register,
+** so that, if it invokes the GC, the GC knows which registers
+** are in use at that time.
+
+*/
+static void codeclosure (LexState *ls, expdesc *v) {
+ FuncState *fs = ls->fs->prev;
+ init_exp(v, VRELOC, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1));
+ luaK_exp2nextreg(fs, v); /* fix it at the last register */
+}
+
+
+static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
+ Proto *f = fs->f;
+ fs->prev = ls->fs; /* linked list of funcstates */
+ fs->ls = ls;
+ ls->fs = fs;
+ fs->pc = 0;
+ fs->previousline = f->linedefined;
+ fs->iwthabs = 0;
+ fs->lasttarget = 0;
+ fs->freereg = 0;
+ fs->nk = 0;
+ fs->nabslineinfo = 0;
+ fs->np = 0;
+ fs->nups = 0;
+ fs->ndebugvars = 0;
+ fs->nactvar = 0;
+ fs->needclose = 0;
+ fs->firstlocal = ls->dyd->actvar.n;
+ fs->firstlabel = ls->dyd->label.n;
+ fs->bl = NULL;
+ f->source = ls->source;
+ luaC_objbarrier(ls->L, f, f->source);
+ f->maxstacksize = 2; /* registers 0/1 are always valid */
+ enterblock(fs, bl, 0);
+}
+
+
+static void close_func (LexState *ls) {
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */
+ leaveblock(fs);
+ lua_assert(fs->bl == NULL);
+ luaK_finish(fs);
+ luaM_shrinkvector(L, f->code, f->sizecode, fs->pc, Instruction);
+ luaM_shrinkvector(L, f->lineinfo, f->sizelineinfo, fs->pc, ls_byte);
+ luaM_shrinkvector(L, f->abslineinfo, f->sizeabslineinfo,
+ fs->nabslineinfo, AbsLineInfo);
+ luaM_shrinkvector(L, f->k, f->sizek, fs->nk, TValue);
+ luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *);
+ luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar);
+ luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc);
+ ls->fs = fs->prev;
+ luaC_checkGC(L);
+}
+
+
+
+/*============================================================*/
+/* GRAMMAR RULES */
+/*============================================================*/
+
+
+/*
+** check whether current token is in the follow set of a block.
+** 'until' closes syntactical blocks, but do not close scope,
+** so it is handled in separate.
+*/
+static int block_follow (LexState *ls, int withuntil) {
+ switch (ls->t.token) {
+ case TK_ELSE: case TK_ELSEIF:
+ case TK_END: case TK_EOS:
+ return 1;
+ case TK_UNTIL: return withuntil;
+ default: return 0;
+ }
+}
+
+
+static void statlist (LexState *ls) {
+ /* statlist -> { stat [';'] } */
+ while (!block_follow(ls, 1)) {
+ if (ls->t.token == TK_RETURN) {
+ statement(ls);
+ return; /* 'return' must be last statement */
+ }
+ statement(ls);
+ }
+}
+
+
+static void fieldsel (LexState *ls, expdesc *v) {
+ /* fieldsel -> ['.' | ':'] NAME */
+ FuncState *fs = ls->fs;
+ expdesc key;
+ luaK_exp2anyregup(fs, v);
+ luaX_next(ls); /* skip the dot or colon */
+ codename(ls, &key);
+ luaK_indexed(fs, v, &key);
+}
+
+
+static void yindex (LexState *ls, expdesc *v) {
+ /* index -> '[' expr ']' */
+ luaX_next(ls); /* skip the '[' */
+ expr(ls, v);
+ luaK_exp2val(ls->fs, v);
+ checknext(ls, ']');
+}
+
+
+/*
+** {======================================================================
+** Rules for Constructors
+** =======================================================================
+*/
+
+
+typedef struct ConsControl {
+ expdesc v; /* last list item read */
+ expdesc *t; /* table descriptor */
+ int nh; /* total number of 'record' elements */
+ int na; /* number of array elements already stored */
+ int tostore; /* number of array elements pending to be stored */
+} ConsControl;
+
+
+static void recfield (LexState *ls, ConsControl *cc) {
+ /* recfield -> (NAME | '['exp']') = exp */
+ FuncState *fs = ls->fs;
+ int reg = ls->fs->freereg;
+ expdesc tab, key, val;
+ if (ls->t.token == TK_NAME) {
+ checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
+ codename(ls, &key);
+ }
+ else /* ls->t.token == '[' */
+ yindex(ls, &key);
+ cc->nh++;
+ checknext(ls, '=');
+ tab = *cc->t;
+ luaK_indexed(fs, &tab, &key);
+ expr(ls, &val);
+ luaK_storevar(fs, &tab, &val);
+ fs->freereg = reg; /* free registers */
+}
+
+
+static void closelistfield (FuncState *fs, ConsControl *cc) {
+ if (cc->v.k == VVOID) return; /* there is no list item */
+ luaK_exp2nextreg(fs, &cc->v);
+ cc->v.k = VVOID;
+ if (cc->tostore == LFIELDS_PER_FLUSH) {
+ luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */
+ cc->na += cc->tostore;
+ cc->tostore = 0; /* no more items pending */
+ }
+}
+
+
+static void lastlistfield (FuncState *fs, ConsControl *cc) {
+ if (cc->tostore == 0) return;
+ if (hasmultret(cc->v.k)) {
+ luaK_setmultret(fs, &cc->v);
+ luaK_setlist(fs, cc->t->u.info, cc->na, LUA_MULTRET);
+ cc->na--; /* do not count last expression (unknown number of elements) */
+ }
+ else {
+ if (cc->v.k != VVOID)
+ luaK_exp2nextreg(fs, &cc->v);
+ luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore);
+ }
+ cc->na += cc->tostore;
+}
+
+
+static void listfield (LexState *ls, ConsControl *cc) {
+ /* listfield -> exp */
+ expr(ls, &cc->v);
+ cc->tostore++;
+}
+
+
+static void field (LexState *ls, ConsControl *cc) {
+ /* field -> listfield | recfield */
+ switch(ls->t.token) {
+ case TK_NAME: { /* may be 'listfield' or 'recfield' */
+ if (luaX_lookahead(ls) != '=') /* expression? */
+ listfield(ls, cc);
+ else
+ recfield(ls, cc);
+ break;
+ }
+ case '[': {
+ recfield(ls, cc);
+ break;
+ }
+ default: {
+ listfield(ls, cc);
+ break;
+ }
+ }
+}
+
+
+static void constructor (LexState *ls, expdesc *t) {
+ /* constructor -> '{' [ field { sep field } [sep] ] '}'
+ sep -> ',' | ';' */
+ FuncState *fs = ls->fs;
+ int line = ls->linenumber;
+ int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);
+ ConsControl cc;
+ luaK_code(fs, 0); /* space for extra arg. */
+ cc.na = cc.nh = cc.tostore = 0;
+ cc.t = t;
+ init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */
+ luaK_reserveregs(fs, 1);
+ init_exp(&cc.v, VVOID, 0); /* no value (yet) */
+ checknext(ls, '{');
+ do {
+ lua_assert(cc.v.k == VVOID || cc.tostore > 0);
+ if (ls->t.token == '}') break;
+ closelistfield(fs, &cc);
+ field(ls, &cc);
+ } while (testnext(ls, ',') || testnext(ls, ';'));
+ check_match(ls, '}', '{', line);
+ lastlistfield(fs, &cc);
+ luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh);
+}
+
+/* }====================================================================== */
+
+
+static void setvararg (FuncState *fs, int nparams) {
+ fs->f->is_vararg = 1;
+ luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0);
+}
+
+
+static void parlist (LexState *ls) {
+ /* parlist -> [ {NAME ','} (NAME | '...') ] */
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int nparams = 0;
+ int isvararg = 0;
+ if (ls->t.token != ')') { /* is 'parlist' not empty? */
+ do {
+ switch (ls->t.token) {
+ case TK_NAME: {
+ new_localvar(ls, str_checkname(ls));
+ nparams++;
+ break;
+ }
+ case TK_DOTS: {
+ luaX_next(ls);
+ isvararg = 1;
+ break;
+ }
+ default: luaX_syntaxerror(ls, "<name> or '...' expected");
+ }
+ } while (!isvararg && testnext(ls, ','));
+ }
+ adjustlocalvars(ls, nparams);
+ f->numparams = cast_byte(fs->nactvar);
+ if (isvararg)
+ setvararg(fs, f->numparams); /* declared vararg */
+ luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */
+}
+
+
+static void body (LexState *ls, expdesc *e, int ismethod, int line) {
+ /* body -> '(' parlist ')' block END */
+ FuncState new_fs;
+ BlockCnt bl;
+ new_fs.f = addprototype(ls);
+ new_fs.f->linedefined = line;
+ open_func(ls, &new_fs, &bl);
+ checknext(ls, '(');
+ if (ismethod) {
+ new_localvarliteral(ls, "self"); /* create 'self' parameter */
+ adjustlocalvars(ls, 1);
+ }
+ parlist(ls);
+ checknext(ls, ')');
+ statlist(ls);
+ new_fs.f->lastlinedefined = ls->linenumber;
+ check_match(ls, TK_END, TK_FUNCTION, line);
+ codeclosure(ls, e);
+ close_func(ls);
+}
+
+
+static int explist (LexState *ls, expdesc *v) {
+ /* explist -> expr { ',' expr } */
+ int n = 1; /* at least one expression */
+ expr(ls, v);
+ while (testnext(ls, ',')) {
+ luaK_exp2nextreg(ls->fs, v);
+ expr(ls, v);
+ n++;
+ }
+ return n;
+}
+
+
+static void funcargs (LexState *ls, expdesc *f, int line) {
+ FuncState *fs = ls->fs;
+ expdesc args;
+ int base, nparams;
+ switch (ls->t.token) {
+ case '(': { /* funcargs -> '(' [ explist ] ')' */
+ luaX_next(ls);
+ if (ls->t.token == ')') /* arg list is empty? */
+ args.k = VVOID;
+ else {
+ explist(ls, &args);
+ if (hasmultret(args.k))
+ luaK_setmultret(fs, &args);
+ }
+ check_match(ls, ')', '(', line);
+ break;
+ }
+ case '{': { /* funcargs -> constructor */
+ constructor(ls, &args);
+ break;
+ }
+ case TK_STRING: { /* funcargs -> STRING */
+ codestring(&args, ls->t.seminfo.ts);
+ luaX_next(ls); /* must use 'seminfo' before 'next' */
+ break;
+ }
+ default: {
+ luaX_syntaxerror(ls, "function arguments expected");
+ }
+ }
+ lua_assert(f->k == VNONRELOC);
+ base = f->u.info; /* base register for call */
+ if (hasmultret(args.k))
+ nparams = LUA_MULTRET; /* open call */
+ else {
+ if (args.k != VVOID)
+ luaK_exp2nextreg(fs, &args); /* close last argument */
+ nparams = fs->freereg - (base+1);
+ }
+ init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));
+ luaK_fixline(fs, line);
+ fs->freereg = base+1; /* call remove function and arguments and leaves
+ (unless changed) one result */
+}
+
+
+
+
+/*
+** {======================================================================
+** Expression parsing
+** =======================================================================
+*/
+
+
+static void primaryexp (LexState *ls, expdesc *v) {
+ /* primaryexp -> NAME | '(' expr ')' */
+ switch (ls->t.token) {
+ case '(': {
+ int line = ls->linenumber;
+ luaX_next(ls);
+ expr(ls, v);
+ check_match(ls, ')', '(', line);
+ luaK_dischargevars(ls->fs, v);
+ return;
+ }
+ case TK_NAME: {
+ singlevar(ls, v);
+ return;
+ }
+ default: {
+ luaX_syntaxerror(ls, "unexpected symbol");
+ }
+ }
+}
+
+
+static void suffixedexp (LexState *ls, expdesc *v) {
+ /* suffixedexp ->
+ primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
+ FuncState *fs = ls->fs;
+ int line = ls->linenumber;
+ primaryexp(ls, v);
+ for (;;) {
+ switch (ls->t.token) {
+ case '.': { /* fieldsel */
+ fieldsel(ls, v);
+ break;
+ }
+ case '[': { /* '[' exp ']' */
+ expdesc key;
+ luaK_exp2anyregup(fs, v);
+ yindex(ls, &key);
+ luaK_indexed(fs, v, &key);
+ break;
+ }
+ case ':': { /* ':' NAME funcargs */
+ expdesc key;
+ luaX_next(ls);
+ codename(ls, &key);
+ luaK_self(fs, v, &key);
+ funcargs(ls, v, line);
+ break;
+ }
+ case '(': case TK_STRING: case '{': { /* funcargs */
+ luaK_exp2nextreg(fs, v);
+ funcargs(ls, v, line);
+ break;
+ }
+ default: return;
+ }
+ }
+}
+
+
+static void simpleexp (LexState *ls, expdesc *v) {
+ /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... |
+ constructor | FUNCTION body | suffixedexp */
+ switch (ls->t.token) {
+ case TK_FLT: {
+ init_exp(v, VKFLT, 0);
+ v->u.nval = ls->t.seminfo.r;
+ break;
+ }
+ case TK_INT: {
+ init_exp(v, VKINT, 0);
+ v->u.ival = ls->t.seminfo.i;
+ break;
+ }
+ case TK_STRING: {
+ codestring(v, ls->t.seminfo.ts);
+ break;
+ }
+ case TK_NIL: {
+ init_exp(v, VNIL, 0);
+ break;
+ }
+ case TK_TRUE: {
+ init_exp(v, VTRUE, 0);
+ break;
+ }
+ case TK_FALSE: {
+ init_exp(v, VFALSE, 0);
+ break;
+ }
+ case TK_DOTS: { /* vararg */
+ FuncState *fs = ls->fs;
+ check_condition(ls, fs->f->is_vararg,
+ "cannot use '...' outside a vararg function");
+ init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1));
+ break;
+ }
+ case '{': { /* constructor */
+ constructor(ls, v);
+ return;
+ }
+ case TK_FUNCTION: {
+ luaX_next(ls);
+ body(ls, v, 0, ls->linenumber);
+ return;
+ }
+ default: {
+ suffixedexp(ls, v);
+ return;
+ }
+ }
+ luaX_next(ls);
+}
+
+
+static UnOpr getunopr (int op) {
+ switch (op) {
+ case TK_NOT: return OPR_NOT;
+ case '-': return OPR_MINUS;
+ case '~': return OPR_BNOT;
+ case '#': return OPR_LEN;
+ default: return OPR_NOUNOPR;
+ }
+}
+
+
+static BinOpr getbinopr (int op) {
+ switch (op) {
+ case '+': return OPR_ADD;
+ case '-': return OPR_SUB;
+ case '*': return OPR_MUL;
+ case '%': return OPR_MOD;
+ case '^': return OPR_POW;
+ case '/': return OPR_DIV;
+ case TK_IDIV: return OPR_IDIV;
+ case '&': return OPR_BAND;
+ case '|': return OPR_BOR;
+ case '~': return OPR_BXOR;
+ case TK_SHL: return OPR_SHL;
+ case TK_SHR: return OPR_SHR;
+ case TK_CONCAT: return OPR_CONCAT;
+ case TK_NE: return OPR_NE;
+ case TK_EQ: return OPR_EQ;
+ case '<': return OPR_LT;
+ case TK_LE: return OPR_LE;
+ case '>': return OPR_GT;
+ case TK_GE: return OPR_GE;
+ case TK_AND: return OPR_AND;
+ case TK_OR: return OPR_OR;
+ default: return OPR_NOBINOPR;
+ }
+}
+
+
+/*
+** Priority table for binary operators.
+*/
+static const struct {
+ lu_byte left; /* left priority for each binary operator */
+ lu_byte right; /* right priority */
+} priority[] = { /* ORDER OPR */
+ {10, 10}, {10, 10}, /* '+' '-' */
+ {11, 11}, {11, 11}, /* '*' '%' */
+ {14, 13}, /* '^' (right associative) */
+ {11, 11}, {11, 11}, /* '/' '//' */
+ {6, 6}, {4, 4}, {5, 5}, /* '&' '|' '~' */
+ {7, 7}, {7, 7}, /* '<<' '>>' */
+ {9, 8}, /* '..' (right associative) */
+ {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */
+ {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */
+ {2, 2}, {1, 1} /* and, or */
+};
+
+#define UNARY_PRIORITY 12 /* priority for unary operators */
+
+
+/*
+** subexpr -> (simpleexp | unop subexpr) { binop subexpr }
+** where 'binop' is any binary operator with a priority higher than 'limit'
+*/
+static BinOpr subexpr (LexState *ls, expdesc *v, int limit) {
+ BinOpr op;
+ UnOpr uop;
+ enterlevel(ls);
+ uop = getunopr(ls->t.token);
+ if (uop != OPR_NOUNOPR) { /* prefix (unary) operator? */
+ int line = ls->linenumber;
+ luaX_next(ls); /* skip operator */
+ subexpr(ls, v, UNARY_PRIORITY);
+ luaK_prefix(ls->fs, uop, v, line);
+ }
+ else simpleexp(ls, v);
+ /* expand while operators have priorities higher than 'limit' */
+ op = getbinopr(ls->t.token);
+ while (op != OPR_NOBINOPR && priority[op].left > limit) {
+ expdesc v2;
+ BinOpr nextop;
+ int line = ls->linenumber;
+ luaX_next(ls); /* skip operator */
+ luaK_infix(ls->fs, op, v);
+ /* read sub-expression with higher priority */
+ nextop = subexpr(ls, &v2, priority[op].right);
+ luaK_posfix(ls->fs, op, v, &v2, line);
+ op = nextop;
+ }
+ leavelevel(ls);
+ return op; /* return first untreated operator */
+}
+
+
+static void expr (LexState *ls, expdesc *v) {
+ subexpr(ls, v, 0);
+}
+
+/* }==================================================================== */
+
+
+
+/*
+** {======================================================================
+** Rules for Statements
+** =======================================================================
+*/
+
+
+static void block (LexState *ls) {
+ /* block -> statlist */
+ FuncState *fs = ls->fs;
+ BlockCnt bl;
+ enterblock(fs, &bl, 0);
+ statlist(ls);
+ leaveblock(fs);
+}
+
+
+/*
+** structure to chain all variables in the left-hand side of an
+** assignment
+*/
+struct LHS_assign {
+ struct LHS_assign *prev;
+ expdesc v; /* variable (global, local, upvalue, or indexed) */
+};
+
+
+/*
+** check whether, in an assignment to an upvalue/local variable, the
+** upvalue/local variable is begin used in a previous assignment to a
+** table. If so, save original upvalue/local value in a safe place and
+** use this safe copy in the previous assignment.
+*/
+static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
+ FuncState *fs = ls->fs;
+ int extra = fs->freereg; /* eventual position to save local variable */
+ int conflict = 0;
+ for (; lh; lh = lh->prev) { /* check all previous assignments */
+ if (vkisindexed(lh->v.k)) { /* assignment to table field? */
+ if (lh->v.k == VINDEXUP) { /* is table an upvalue? */
+ if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) {
+ conflict = 1; /* table is the upvalue being assigned now */
+ lh->v.k = VINDEXSTR;
+ lh->v.u.ind.t = extra; /* assignment will use safe copy */
+ }
+ }
+ else { /* table is a register */
+ if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.ridx) {
+ conflict = 1; /* table is the local being assigned now */
+ lh->v.u.ind.t = extra; /* assignment will use safe copy */
+ }
+ /* is index the local being assigned? */
+ if (lh->v.k == VINDEXED && v->k == VLOCAL &&
+ lh->v.u.ind.idx == v->u.var.ridx) {
+ conflict = 1;
+ lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
+ }
+ }
+ }
+ }
+ if (conflict) {
+ /* copy upvalue/local value to a temporary (in position 'extra') */
+ if (v->k == VLOCAL)
+ luaK_codeABC(fs, OP_MOVE, extra, v->u.var.ridx, 0);
+ else
+ luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0);
+ luaK_reserveregs(fs, 1);
+ }
+}
+
+/*
+** Parse and compile a multiple assignment. The first "variable"
+** (a 'suffixedexp') was already read by the caller.
+**
+** assignment -> suffixedexp restassign
+** restassign -> ',' suffixedexp restassign | '=' explist
+*/
+static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
+ expdesc e;
+ check_condition(ls, vkisvar(lh->v.k), "syntax error");
+ check_readonly(ls, &lh->v);
+ if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */
+ struct LHS_assign nv;
+ nv.prev = lh;
+ suffixedexp(ls, &nv.v);
+ if (!vkisindexed(nv.v.k))
+ check_conflict(ls, lh, &nv.v);
+ enterlevel(ls); /* control recursion depth */
+ restassign(ls, &nv, nvars+1);
+ leavelevel(ls);
+ }
+ else { /* restassign -> '=' explist */
+ int nexps;
+ checknext(ls, '=');
+ nexps = explist(ls, &e);
+ if (nexps != nvars)
+ adjust_assign(ls, nvars, nexps, &e);
+ else {
+ luaK_setoneret(ls->fs, &e); /* close last expression */
+ luaK_storevar(ls->fs, &lh->v, &e);
+ return; /* avoid default */
+ }
+ }
+ init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */
+ luaK_storevar(ls->fs, &lh->v, &e);
+}
+
+
+static int cond (LexState *ls) {
+ /* cond -> exp */
+ expdesc v;
+ expr(ls, &v); /* read condition */
+ if (v.k == VNIL) v.k = VFALSE; /* 'falses' are all equal here */
+ luaK_goiftrue(ls->fs, &v);
+ return v.f;
+}
+
+
+static void gotostat (LexState *ls) {
+ FuncState *fs = ls->fs;
+ int line = ls->linenumber;
+ TString *name = str_checkname(ls); /* label's name */
+ Labeldesc *lb = findlabel(ls, name);
+ if (lb == NULL) /* no label? */
+ /* forward jump; will be resolved when the label is declared */
+ newgotoentry(ls, name, line, luaK_jump(fs));
+ else { /* found a label */
+ /* backward jump; will be resolved here */
+ int lblevel = reglevel(fs, lb->nactvar); /* label level */
+ if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */
+ luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0);
+ /* create jump and link it to the label */
+ luaK_patchlist(fs, luaK_jump(fs), lb->pc);
+ }
+}
+
+
+/*
+** Break statement. Semantically equivalent to "goto break".
+*/
+static void breakstat (LexState *ls) {
+ int line = ls->linenumber;
+ luaX_next(ls); /* skip break */
+ newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, luaK_jump(ls->fs));
+}
+
+
+/*
+** Check whether there is already a label with the given 'name'.
+*/
+static void checkrepeated (LexState *ls, TString *name) {
+ Labeldesc *lb = findlabel(ls, name);
+ if (l_unlikely(lb != NULL)) { /* already defined? */
+ const char *msg = "label '%s' already defined on line %d";
+ msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
+ luaK_semerror(ls, msg); /* error */
+ }
+}
+
+
+static void labelstat (LexState *ls, TString *name, int line) {
+ /* label -> '::' NAME '::' */
+ checknext(ls, TK_DBCOLON); /* skip double colon */
+ while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
+ statement(ls); /* skip other no-op statements */
+ checkrepeated(ls, name); /* check for repeated labels */
+ createlabel(ls, name, line, block_follow(ls, 0));
+}
+
+
+static void whilestat (LexState *ls, int line) {
+ /* whilestat -> WHILE cond DO block END */
+ FuncState *fs = ls->fs;
+ int whileinit;
+ int condexit;
+ BlockCnt bl;
+ luaX_next(ls); /* skip WHILE */
+ whileinit = luaK_getlabel(fs);
+ condexit = cond(ls);
+ enterblock(fs, &bl, 1);
+ checknext(ls, TK_DO);
+ block(ls);
+ luaK_jumpto(fs, whileinit);
+ check_match(ls, TK_END, TK_WHILE, line);
+ leaveblock(fs);
+ luaK_patchtohere(fs, condexit); /* false conditions finish the loop */
+}
+
+
+static void repeatstat (LexState *ls, int line) {
+ /* repeatstat -> REPEAT block UNTIL cond */
+ int condexit;
+ FuncState *fs = ls->fs;
+ int repeat_init = luaK_getlabel(fs);
+ BlockCnt bl1, bl2;
+ enterblock(fs, &bl1, 1); /* loop block */
+ enterblock(fs, &bl2, 0); /* scope block */
+ luaX_next(ls); /* skip REPEAT */
+ statlist(ls);
+ check_match(ls, TK_UNTIL, TK_REPEAT, line);
+ condexit = cond(ls); /* read condition (inside scope block) */
+ leaveblock(fs); /* finish scope */
+ if (bl2.upval) { /* upvalues? */
+ int exit = luaK_jump(fs); /* normal exit must jump over fix */
+ luaK_patchtohere(fs, condexit); /* repetition must close upvalues */
+ luaK_codeABC(fs, OP_CLOSE, reglevel(fs, bl2.nactvar), 0, 0);
+ condexit = luaK_jump(fs); /* repeat after closing upvalues */
+ luaK_patchtohere(fs, exit); /* normal exit comes to here */
+ }
+ luaK_patchlist(fs, condexit, repeat_init); /* close the loop */
+ leaveblock(fs); /* finish loop */
+}
+
+
+/*
+** Read an expression and generate code to put its results in next
+** stack slot.
+**
+*/
+static void exp1 (LexState *ls) {
+ expdesc e;
+ expr(ls, &e);
+ luaK_exp2nextreg(ls->fs, &e);
+ lua_assert(e.k == VNONRELOC);
+}
+
+
+/*
+** Fix for instruction at position 'pc' to jump to 'dest'.
+** (Jump addresses are relative in Lua). 'back' true means
+** a back jump.
+*/
+static void fixforjump (FuncState *fs, int pc, int dest, int back) {
+ Instruction *jmp = &fs->f->code[pc];
+ int offset = dest - (pc + 1);
+ if (back)
+ offset = -offset;
+ if (l_unlikely(offset > MAXARG_Bx))
+ luaX_syntaxerror(fs->ls, "control structure too long");
+ SETARG_Bx(*jmp, offset);
+}
+
+
+/*
+** Generate code for a 'for' loop.
+*/
+static void forbody (LexState *ls, int base, int line, int nvars, int isgen) {
+ /* forbody -> DO block */
+ static const OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP};
+ static const OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP};
+ BlockCnt bl;
+ FuncState *fs = ls->fs;
+ int prep, endfor;
+ checknext(ls, TK_DO);
+ prep = luaK_codeABx(fs, forprep[isgen], base, 0);
+ enterblock(fs, &bl, 0); /* scope for declared variables */
+ adjustlocalvars(ls, nvars);
+ luaK_reserveregs(fs, nvars);
+ block(ls);
+ leaveblock(fs); /* end of scope for declared variables */
+ fixforjump(fs, prep, luaK_getlabel(fs), 0);
+ if (isgen) { /* generic for? */
+ luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars);
+ luaK_fixline(fs, line);
+ }
+ endfor = luaK_codeABx(fs, forloop[isgen], base, 0);
+ fixforjump(fs, endfor, prep + 1, 1);
+ luaK_fixline(fs, line);
+}
+
+
+static void fornum (LexState *ls, TString *varname, int line) {
+ /* fornum -> NAME = exp,exp[,exp] forbody */
+ FuncState *fs = ls->fs;
+ int base = fs->freereg;
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvar(ls, varname);
+ checknext(ls, '=');
+ exp1(ls); /* initial value */
+ checknext(ls, ',');
+ exp1(ls); /* limit */
+ if (testnext(ls, ','))
+ exp1(ls); /* optional step */
+ else { /* default step = 1 */
+ luaK_int(fs, fs->freereg, 1);
+ luaK_reserveregs(fs, 1);
+ }
+ adjustlocalvars(ls, 3); /* control variables */
+ forbody(ls, base, line, 1, 0);
+}
+
+
+static void forlist (LexState *ls, TString *indexname) {
+ /* forlist -> NAME {,NAME} IN explist forbody */
+ FuncState *fs = ls->fs;
+ expdesc e;
+ int nvars = 5; /* gen, state, control, toclose, 'indexname' */
+ int line;
+ int base = fs->freereg;
+ /* create control variables */
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ /* create declared variables */
+ new_localvar(ls, indexname);
+ while (testnext(ls, ',')) {
+ new_localvar(ls, str_checkname(ls));
+ nvars++;
+ }
+ checknext(ls, TK_IN);
+ line = ls->linenumber;
+ adjust_assign(ls, 4, explist(ls, &e), &e);
+ adjustlocalvars(ls, 4); /* control variables */
+ marktobeclosed(fs); /* last control var. must be closed */
+ luaK_checkstack(fs, 3); /* extra space to call generator */
+ forbody(ls, base, line, nvars - 4, 1);
+}
+
+
+static void forstat (LexState *ls, int line) {
+ /* forstat -> FOR (fornum | forlist) END */
+ FuncState *fs = ls->fs;
+ TString *varname;
+ BlockCnt bl;
+ enterblock(fs, &bl, 1); /* scope for loop and control variables */
+ luaX_next(ls); /* skip 'for' */
+ varname = str_checkname(ls); /* first variable name */
+ switch (ls->t.token) {
+ case '=': fornum(ls, varname, line); break;
+ case ',': case TK_IN: forlist(ls, varname); break;
+ default: luaX_syntaxerror(ls, "'=' or 'in' expected");
+ }
+ check_match(ls, TK_END, TK_FOR, line);
+ leaveblock(fs); /* loop scope ('break' jumps to this point) */
+}
+
+
+static void test_then_block (LexState *ls, int *escapelist) {
+ /* test_then_block -> [IF | ELSEIF] cond THEN block */
+ BlockCnt bl;
+ FuncState *fs = ls->fs;
+ expdesc v;
+ int jf; /* instruction to skip 'then' code (if condition is false) */
+ luaX_next(ls); /* skip IF or ELSEIF */
+ expr(ls, &v); /* read condition */
+ checknext(ls, TK_THEN);
+ if (ls->t.token == TK_BREAK) { /* 'if x then break' ? */
+ int line = ls->linenumber;
+ luaK_goiffalse(ls->fs, &v); /* will jump if condition is true */
+ luaX_next(ls); /* skip 'break' */
+ enterblock(fs, &bl, 0); /* must enter block before 'goto' */
+ newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t);
+ while (testnext(ls, ';')) {} /* skip semicolons */
+ if (block_follow(ls, 0)) { /* jump is the entire block? */
+ leaveblock(fs);
+ return; /* and that is it */
+ }
+ else /* must skip over 'then' part if condition is false */
+ jf = luaK_jump(fs);
+ }
+ else { /* regular case (not a break) */
+ luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */
+ enterblock(fs, &bl, 0);
+ jf = v.f;
+ }
+ statlist(ls); /* 'then' part */
+ leaveblock(fs);
+ if (ls->t.token == TK_ELSE ||
+ ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */
+ luaK_concat(fs, escapelist, luaK_jump(fs)); /* must jump over it */
+ luaK_patchtohere(fs, jf);
+}
+
+
+static void ifstat (LexState *ls, int line) {
+ /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
+ FuncState *fs = ls->fs;
+ int escapelist = NO_JUMP; /* exit list for finished parts */
+ test_then_block(ls, &escapelist); /* IF cond THEN block */
+ while (ls->t.token == TK_ELSEIF)
+ test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */
+ if (testnext(ls, TK_ELSE))
+ block(ls); /* 'else' part */
+ check_match(ls, TK_END, TK_IF, line);
+ luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */
+}
+
+
+static void localfunc (LexState *ls) {
+ expdesc b;
+ FuncState *fs = ls->fs;
+ int fvar = fs->nactvar; /* function's variable index */
+ new_localvar(ls, str_checkname(ls)); /* new local variable */
+ adjustlocalvars(ls, 1); /* enter its scope */
+ body(ls, &b, 0, ls->linenumber); /* function created in next register */
+ /* debug information will only see the variable after this point! */
+ localdebuginfo(fs, fvar)->startpc = fs->pc;
+}
+
+
+static int getlocalattribute (LexState *ls) {
+ /* ATTRIB -> ['<' Name '>'] */
+ if (testnext(ls, '<')) {
+ const char *attr = getstr(str_checkname(ls));
+ checknext(ls, '>');
+ if (strcmp(attr, "const") == 0)
+ return RDKCONST; /* read-only variable */
+ else if (strcmp(attr, "close") == 0)
+ return RDKTOCLOSE; /* to-be-closed variable */
+ else
+ luaK_semerror(ls,
+ luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
+ }
+ return VDKREG; /* regular variable */
+}
+
+
+static void checktoclose (FuncState *fs, int level) {
+ if (level != -1) { /* is there a to-be-closed variable? */
+ marktobeclosed(fs);
+ luaK_codeABC(fs, OP_TBC, reglevel(fs, level), 0, 0);
+ }
+}
+
+
+static void localstat (LexState *ls) {
+ /* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */
+ FuncState *fs = ls->fs;
+ int toclose = -1; /* index of to-be-closed variable (if any) */
+ Vardesc *var; /* last variable */
+ int vidx, kind; /* index and kind of last variable */
+ int nvars = 0;
+ int nexps;
+ expdesc e;
+ do {
+ vidx = new_localvar(ls, str_checkname(ls));
+ kind = getlocalattribute(ls);
+ getlocalvardesc(fs, vidx)->vd.kind = kind;
+ if (kind == RDKTOCLOSE) { /* to-be-closed? */
+ if (toclose != -1) /* one already present? */
+ luaK_semerror(ls, "multiple to-be-closed variables in local list");
+ toclose = fs->nactvar + nvars;
+ }
+ nvars++;
+ } while (testnext(ls, ','));
+ if (testnext(ls, '='))
+ nexps = explist(ls, &e);
+ else {
+ e.k = VVOID;
+ nexps = 0;
+ }
+ var = getlocalvardesc(fs, vidx); /* get last variable */
+ if (nvars == nexps && /* no adjustments? */
+ var->vd.kind == RDKCONST && /* last variable is const? */
+ luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */
+ var->vd.kind = RDKCTC; /* variable is a compile-time constant */
+ adjustlocalvars(ls, nvars - 1); /* exclude last variable */
+ fs->nactvar++; /* but count it */
+ }
+ else {
+ adjust_assign(ls, nvars, nexps, &e);
+ adjustlocalvars(ls, nvars);
+ }
+ checktoclose(fs, toclose);
+}
+
+
+static int funcname (LexState *ls, expdesc *v) {
+ /* funcname -> NAME {fieldsel} [':' NAME] */
+ int ismethod = 0;
+ singlevar(ls, v);
+ while (ls->t.token == '.')
+ fieldsel(ls, v);
+ if (ls->t.token == ':') {
+ ismethod = 1;
+ fieldsel(ls, v);
+ }
+ return ismethod;
+}
+
+
+static void funcstat (LexState *ls, int line) {
+ /* funcstat -> FUNCTION funcname body */
+ int ismethod;
+ expdesc v, b;
+ luaX_next(ls); /* skip FUNCTION */
+ ismethod = funcname(ls, &v);
+ body(ls, &b, ismethod, line);
+ check_readonly(ls, &v);
+ luaK_storevar(ls->fs, &v, &b);
+ luaK_fixline(ls->fs, line); /* definition "happens" in the first line */
+}
+
+
+static void exprstat (LexState *ls) {
+ /* stat -> func | assignment */
+ FuncState *fs = ls->fs;
+ struct LHS_assign v;
+ suffixedexp(ls, &v.v);
+ if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */
+ v.prev = NULL;
+ restassign(ls, &v, 1);
+ }
+ else { /* stat -> func */
+ Instruction *inst;
+ check_condition(ls, v.v.k == VCALL, "syntax error");
+ inst = &getinstruction(fs, &v.v);
+ SETARG_C(*inst, 1); /* call statement uses no results */
+ }
+}
+
+
+static void retstat (LexState *ls) {
+ /* stat -> RETURN [explist] [';'] */
+ FuncState *fs = ls->fs;
+ expdesc e;
+ int nret; /* number of values being returned */
+ int first = luaY_nvarstack(fs); /* first slot to be returned */
+ if (block_follow(ls, 1) || ls->t.token == ';')
+ nret = 0; /* return no values */
+ else {
+ nret = explist(ls, &e); /* optional return values */
+ if (hasmultret(e.k)) {
+ luaK_setmultret(fs, &e);
+ if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */
+ SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL);
+ lua_assert(GETARG_A(getinstruction(fs,&e)) == luaY_nvarstack(fs));
+ }
+ nret = LUA_MULTRET; /* return all values */
+ }
+ else {
+ if (nret == 1) /* only one single value? */
+ first = luaK_exp2anyreg(fs, &e); /* can use original slot */
+ else { /* values must go to the top of the stack */
+ luaK_exp2nextreg(fs, &e);
+ lua_assert(nret == fs->freereg - first);
+ }
+ }
+ }
+ luaK_ret(fs, first, nret);
+ testnext(ls, ';'); /* skip optional semicolon */
+}
+
+
+static void statement (LexState *ls) {
+ int line = ls->linenumber; /* may be needed for error messages */
+ enterlevel(ls);
+ switch (ls->t.token) {
+ case ';': { /* stat -> ';' (empty statement) */
+ luaX_next(ls); /* skip ';' */
+ break;
+ }
+ case TK_IF: { /* stat -> ifstat */
+ ifstat(ls, line);
+ break;
+ }
+ case TK_WHILE: { /* stat -> whilestat */
+ whilestat(ls, line);
+ break;
+ }
+ case TK_DO: { /* stat -> DO block END */
+ luaX_next(ls); /* skip DO */
+ block(ls);
+ check_match(ls, TK_END, TK_DO, line);
+ break;
+ }
+ case TK_FOR: { /* stat -> forstat */
+ forstat(ls, line);
+ break;
+ }
+ case TK_REPEAT: { /* stat -> repeatstat */
+ repeatstat(ls, line);
+ break;
+ }
+ case TK_FUNCTION: { /* stat -> funcstat */
+ funcstat(ls, line);
+ break;
+ }
+ case TK_LOCAL: { /* stat -> localstat */
+ luaX_next(ls); /* skip LOCAL */
+ if (testnext(ls, TK_FUNCTION)) /* local function? */
+ localfunc(ls);
+ else
+ localstat(ls);
+ break;
+ }
+ case TK_DBCOLON: { /* stat -> label */
+ luaX_next(ls); /* skip double colon */
+ labelstat(ls, str_checkname(ls), line);
+ break;
+ }
+ case TK_RETURN: { /* stat -> retstat */
+ luaX_next(ls); /* skip RETURN */
+ retstat(ls);
+ break;
+ }
+ case TK_BREAK: { /* stat -> breakstat */
+ breakstat(ls);
+ break;
+ }
+ case TK_GOTO: { /* stat -> 'goto' NAME */
+ luaX_next(ls); /* skip 'goto' */
+ gotostat(ls);
+ break;
+ }
+ default: { /* stat -> func | assignment */
+ exprstat(ls);
+ break;
+ }
+ }
+ lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
+ ls->fs->freereg >= luaY_nvarstack(ls->fs));
+ ls->fs->freereg = luaY_nvarstack(ls->fs); /* free registers */
+ leavelevel(ls);
+}
+
+/* }====================================================================== */
+
+
+/*
+** compiles the main function, which is a regular vararg function with an
+** upvalue named LUA_ENV
+*/
+static void mainfunc (LexState *ls, FuncState *fs) {
+ BlockCnt bl;
+ Upvaldesc *env;
+ open_func(ls, fs, &bl);
+ setvararg(fs, 0); /* main function is always declared vararg */
+ env = allocupvalue(fs); /* ...set environment upvalue */
+ env->instack = 1;
+ env->idx = 0;
+ env->kind = VDKREG;
+ env->name = ls->envn;
+ luaC_objbarrier(ls->L, fs->f, env->name);
+ luaX_next(ls); /* read first token */
+ statlist(ls); /* parse main body */
+ check(ls, TK_EOS);
+ close_func(ls);
+}
+
+
+LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
+ Dyndata *dyd, const char *name, int firstchar) {
+ LexState lexstate;
+ FuncState funcstate;
+ LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */
+ setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */
+ luaD_inctop(L);
+ lexstate.h = luaH_new(L); /* create table for scanner */
+ sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */
+ luaD_inctop(L);
+ funcstate.f = cl->p = luaF_newproto(L);
+ luaC_objbarrier(L, cl, cl->p);
+ funcstate.f->source = luaS_new(L, name); /* create and anchor TString */
+ luaC_objbarrier(L, funcstate.f, funcstate.f->source);
+ lexstate.buff = buff;
+ lexstate.dyd = dyd;
+ dyd->actvar.n = dyd->gt.n = dyd->label.n = 0;
+ luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar);
+ mainfunc(&lexstate, &funcstate);
+ lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
+ /* all scopes should be correctly finished */
+ lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
+ L->top.p--; /* remove scanner's table */
+ return cl; /* closure is on the stack, too */
+}
+
diff --git a/src/libs/3rdparty/lua/src/lparser.h b/src/libs/3rdparty/lua/src/lparser.h
new file mode 100644
index 0000000000..5e4500f181
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lparser.h
@@ -0,0 +1,171 @@
+/*
+** $Id: lparser.h $
+** Lua Parser
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lparser_h
+#define lparser_h
+
+#include "llimits.h"
+#include "lobject.h"
+#include "lzio.h"
+
+
+/*
+** Expression and variable descriptor.
+** Code generation for variables and expressions can be delayed to allow
+** optimizations; An 'expdesc' structure describes a potentially-delayed
+** variable/expression. It has a description of its "main" value plus a
+** list of conditional jumps that can also produce its value (generated
+** by short-circuit operators 'and'/'or').
+*/
+
+/* kinds of variables/expressions */
+typedef enum {
+ VVOID, /* when 'expdesc' describes the last expression of a list,
+ this kind means an empty list (so, no expression) */
+ VNIL, /* constant nil */
+ VTRUE, /* constant true */
+ VFALSE, /* constant false */
+ VK, /* constant in 'k'; info = index of constant in 'k' */
+ VKFLT, /* floating constant; nval = numerical float value */
+ VKINT, /* integer constant; ival = numerical integer value */
+ VKSTR, /* string constant; strval = TString address;
+ (string is fixed by the lexer) */
+ VNONRELOC, /* expression has its value in a fixed register;
+ info = result register */
+ VLOCAL, /* local variable; var.ridx = register index;
+ var.vidx = relative index in 'actvar.arr' */
+ VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
+ VCONST, /* compile-time <const> variable;
+ info = absolute index in 'actvar.arr' */
+ VINDEXED, /* indexed variable;
+ ind.t = table register;
+ ind.idx = key's R index */
+ VINDEXUP, /* indexed upvalue;
+ ind.t = table upvalue;
+ ind.idx = key's K index */
+ VINDEXI, /* indexed variable with constant integer;
+ ind.t = table register;
+ ind.idx = key's value */
+ VINDEXSTR, /* indexed variable with literal string;
+ ind.t = table register;
+ ind.idx = key's K index */
+ VJMP, /* expression is a test/comparison;
+ info = pc of corresponding jump instruction */
+ VRELOC, /* expression can put result in any register;
+ info = instruction pc */
+ VCALL, /* expression is a function call; info = instruction pc */
+ VVARARG /* vararg expression; info = instruction pc */
+} expkind;
+
+
+#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR)
+#define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR)
+
+
+typedef struct expdesc {
+ expkind k;
+ union {
+ lua_Integer ival; /* for VKINT */
+ lua_Number nval; /* for VKFLT */
+ TString *strval; /* for VKSTR */
+ int info; /* for generic use */
+ struct { /* for indexed variables */
+ short idx; /* index (R or "long" K) */
+ lu_byte t; /* table (register or upvalue) */
+ } ind;
+ struct { /* for local variables */
+ lu_byte ridx; /* register holding the variable */
+ unsigned short vidx; /* compiler index (in 'actvar.arr') */
+ } var;
+ } u;
+ int t; /* patch list of 'exit when true' */
+ int f; /* patch list of 'exit when false' */
+} expdesc;
+
+
+/* kinds of variables */
+#define VDKREG 0 /* regular */
+#define RDKCONST 1 /* constant */
+#define RDKTOCLOSE 2 /* to-be-closed */
+#define RDKCTC 3 /* compile-time constant */
+
+/* description of an active local variable */
+typedef union Vardesc {
+ struct {
+ TValuefields; /* constant value (if it is a compile-time constant) */
+ lu_byte kind;
+ lu_byte ridx; /* register holding the variable */
+ short pidx; /* index of the variable in the Proto's 'locvars' array */
+ TString *name; /* variable name */
+ } vd;
+ TValue k; /* constant value (if any) */
+} Vardesc;
+
+
+
+/* description of pending goto statements and label statements */
+typedef struct Labeldesc {
+ TString *name; /* label identifier */
+ int pc; /* position in code */
+ int line; /* line where it appeared */
+ lu_byte nactvar; /* number of active variables in that position */
+ lu_byte close; /* goto that escapes upvalues */
+} Labeldesc;
+
+
+/* list of labels or gotos */
+typedef struct Labellist {
+ Labeldesc *arr; /* array */
+ int n; /* number of entries in use */
+ int size; /* array size */
+} Labellist;
+
+
+/* dynamic structures used by the parser */
+typedef struct Dyndata {
+ struct { /* list of all active local variables */
+ Vardesc *arr;
+ int n;
+ int size;
+ } actvar;
+ Labellist gt; /* list of pending gotos */
+ Labellist label; /* list of active labels */
+} Dyndata;
+
+
+/* control of blocks */
+struct BlockCnt; /* defined in lparser.c */
+
+
+/* state needed to generate code for a given function */
+typedef struct FuncState {
+ Proto *f; /* current function header */
+ struct FuncState *prev; /* enclosing function */
+ struct LexState *ls; /* lexical state */
+ struct BlockCnt *bl; /* chain of current blocks */
+ int pc; /* next position to code (equivalent to 'ncode') */
+ int lasttarget; /* 'label' of last 'jump label' */
+ int previousline; /* last line that was saved in 'lineinfo' */
+ int nk; /* number of elements in 'k' */
+ int np; /* number of elements in 'p' */
+ int nabslineinfo; /* number of elements in 'abslineinfo' */
+ int firstlocal; /* index of first local var (in Dyndata array) */
+ int firstlabel; /* index of first label (in 'dyd->label->arr') */
+ short ndebugvars; /* number of elements in 'f->locvars' */
+ lu_byte nactvar; /* number of active local variables */
+ lu_byte nups; /* number of upvalues */
+ lu_byte freereg; /* first free register */
+ lu_byte iwthabs; /* instructions issued since last absolute line info */
+ lu_byte needclose; /* function needs to close upvalues when returning */
+} FuncState;
+
+
+LUAI_FUNC int luaY_nvarstack (FuncState *fs);
+LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
+ Dyndata *dyd, const char *name, int firstchar);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lprefix.h b/src/libs/3rdparty/lua/src/lprefix.h
new file mode 100644
index 0000000000..484f2ad6fb
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lprefix.h
@@ -0,0 +1,45 @@
+/*
+** $Id: lprefix.h $
+** Definitions for Lua code that must come before any other header file
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lprefix_h
+#define lprefix_h
+
+
+/*
+** Allows POSIX/XSI stuff
+*/
+#if !defined(LUA_USE_C89) /* { */
+
+#if !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 600
+#elif _XOPEN_SOURCE == 0
+#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */
+#endif
+
+/*
+** Allows manipulation of large files in gcc and some other compilers
+*/
+#if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS)
+#define _LARGEFILE_SOURCE 1
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#endif /* } */
+
+
+/*
+** Windows stuff
+*/
+#if defined(_WIN32) /* { */
+
+#if !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */
+#endif
+
+#endif /* } */
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/lstate.c b/src/libs/3rdparty/lua/src/lstate.c
new file mode 100644
index 0000000000..1e925e5ad4
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lstate.c
@@ -0,0 +1,445 @@
+/*
+** $Id: lstate.c $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+#define lstate_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <stddef.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+
+/*
+** thread state + extra space
+*/
+typedef struct LX {
+ lu_byte extra_[LUA_EXTRASPACE];
+ lua_State l;
+} LX;
+
+
+/*
+** Main thread combines a thread state and the global state
+*/
+typedef struct LG {
+ LX l;
+ global_State g;
+} LG;
+
+
+
+#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l)))
+
+
+/*
+** A macro to create a "random" seed when a state is created;
+** the seed is used to randomize string hashes.
+*/
+#if !defined(luai_makeseed)
+
+#include <time.h>
+
+/*
+** Compute an initial seed with some level of randomness.
+** Rely on Address Space Layout Randomization (if present) and
+** current time.
+*/
+#define addbuff(b,p,e) \
+ { size_t t = cast_sizet(e); \
+ memcpy(b + p, &t, sizeof(t)); p += sizeof(t); }
+
+static unsigned int luai_makeseed (lua_State *L) {
+ char buff[3 * sizeof(size_t)];
+ unsigned int h = cast_uint(time(NULL));
+ int p = 0;
+ addbuff(buff, p, L); /* heap variable */
+ addbuff(buff, p, &h); /* local variable */
+ addbuff(buff, p, &lua_newstate); /* public function */
+ lua_assert(p == sizeof(buff));
+ return luaS_hash(buff, p, h);
+}
+
+#endif
+
+
+/*
+** set GCdebt to a new value keeping the value (totalbytes + GCdebt)
+** invariant (and avoiding underflows in 'totalbytes')
+*/
+void luaE_setdebt (global_State *g, l_mem debt) {
+ l_mem tb = gettotalbytes(g);
+ lua_assert(tb > 0);
+ if (debt < tb - MAX_LMEM)
+ debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */
+ g->totalbytes = tb - debt;
+ g->GCdebt = debt;
+}
+
+
+LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
+ UNUSED(L); UNUSED(limit);
+ return LUAI_MAXCCALLS; /* warning?? */
+}
+
+
+CallInfo *luaE_extendCI (lua_State *L) {
+ CallInfo *ci;
+ lua_assert(L->ci->next == NULL);
+ ci = luaM_new(L, CallInfo);
+ lua_assert(L->ci->next == NULL);
+ L->ci->next = ci;
+ ci->previous = L->ci;
+ ci->next = NULL;
+ ci->u.l.trap = 0;
+ L->nci++;
+ return ci;
+}
+
+
+/*
+** free all CallInfo structures not in use by a thread
+*/
+void luaE_freeCI (lua_State *L) {
+ CallInfo *ci = L->ci;
+ CallInfo *next = ci->next;
+ ci->next = NULL;
+ while ((ci = next) != NULL) {
+ next = ci->next;
+ luaM_free(L, ci);
+ L->nci--;
+ }
+}
+
+
+/*
+** free half of the CallInfo structures not in use by a thread,
+** keeping the first one.
+*/
+void luaE_shrinkCI (lua_State *L) {
+ CallInfo *ci = L->ci->next; /* first free CallInfo */
+ CallInfo *next;
+ if (ci == NULL)
+ return; /* no extra elements */
+ while ((next = ci->next) != NULL) { /* two extra elements? */
+ CallInfo *next2 = next->next; /* next's next */
+ ci->next = next2; /* remove next from the list */
+ L->nci--;
+ luaM_free(L, next); /* free next */
+ if (next2 == NULL)
+ break; /* no more elements */
+ else {
+ next2->previous = ci;
+ ci = next2; /* continue */
+ }
+ }
+}
+
+
+/*
+** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS.
+** If equal, raises an overflow error. If value is larger than
+** LUAI_MAXCCALLS (which means it is handling an overflow) but
+** not much larger, does not report an error (to allow overflow
+** handling to work).
+*/
+void luaE_checkcstack (lua_State *L) {
+ if (getCcalls(L) == LUAI_MAXCCALLS)
+ luaG_runerror(L, "C stack overflow");
+ else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
+ luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
+}
+
+
+LUAI_FUNC void luaE_incCstack (lua_State *L) {
+ L->nCcalls++;
+ if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
+ luaE_checkcstack(L);
+}
+
+
+static void stack_init (lua_State *L1, lua_State *L) {
+ int i; CallInfo *ci;
+ /* initialize stack array */
+ L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
+ L1->tbclist.p = L1->stack.p;
+ for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
+ setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */
+ L1->top.p = L1->stack.p;
+ L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE;
+ /* initialize first ci */
+ ci = &L1->base_ci;
+ ci->next = ci->previous = NULL;
+ ci->callstatus = CIST_C;
+ ci->func.p = L1->top.p;
+ ci->u.c.k = NULL;
+ ci->nresults = 0;
+ setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */
+ L1->top.p++;
+ ci->top.p = L1->top.p + LUA_MINSTACK;
+ L1->ci = ci;
+}
+
+
+static void freestack (lua_State *L) {
+ if (L->stack.p == NULL)
+ return; /* stack not completely built yet */
+ L->ci = &L->base_ci; /* free the entire 'ci' list */
+ luaE_freeCI(L);
+ lua_assert(L->nci == 0);
+ luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */
+}
+
+
+/*
+** Create registry table and its predefined values
+*/
+static void init_registry (lua_State *L, global_State *g) {
+ /* create registry */
+ Table *registry = luaH_new(L);
+ sethvalue(L, &g->l_registry, registry);
+ luaH_resize(L, registry, LUA_RIDX_LAST, 0);
+ /* registry[LUA_RIDX_MAINTHREAD] = L */
+ setthvalue(L, &registry->array[LUA_RIDX_MAINTHREAD - 1], L);
+ /* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */
+ sethvalue(L, &registry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L));
+}
+
+
+/*
+** open parts of the state that may cause memory-allocation errors.
+*/
+static void f_luaopen (lua_State *L, void *ud) {
+ global_State *g = G(L);
+ UNUSED(ud);
+ stack_init(L, L); /* init stack */
+ init_registry(L, g);
+ luaS_init(L);
+ luaT_init(L);
+ luaX_init(L);
+ g->gcstp = 0; /* allow gc */
+ setnilvalue(&g->nilvalue); /* now state is complete */
+ luai_userstateopen(L);
+}
+
+
+/*
+** preinitialize a thread with consistent values without allocating
+** any memory (to avoid errors)
+*/
+static void preinit_thread (lua_State *L, global_State *g) {
+ G(L) = g;
+ L->stack.p = NULL;
+ L->ci = NULL;
+ L->nci = 0;
+ L->twups = L; /* thread has no upvalues */
+ L->nCcalls = 0;
+ L->errorJmp = NULL;
+ L->hook = NULL;
+ L->hookmask = 0;
+ L->basehookcount = 0;
+ L->allowhook = 1;
+ resethookcount(L);
+ L->openupval = NULL;
+ L->status = LUA_OK;
+ L->errfunc = 0;
+ L->oldpc = 0;
+}
+
+
+static void close_state (lua_State *L) {
+ global_State *g = G(L);
+ if (!completestate(g)) /* closing a partially built state? */
+ luaC_freeallobjects(L); /* just collect its objects */
+ else { /* closing a fully built state */
+ L->ci = &L->base_ci; /* unwind CallInfo list */
+ luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */
+ luaC_freeallobjects(L); /* collect all objects */
+ luai_userstateclose(L);
+ }
+ luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
+ freestack(L);
+ lua_assert(gettotalbytes(g) == sizeof(LG));
+ (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */
+}
+
+
+LUA_API lua_State *lua_newthread (lua_State *L) {
+ global_State *g = G(L);
+ GCObject *o;
+ lua_State *L1;
+ lua_lock(L);
+ luaC_checkGC(L);
+ /* create new thread */
+ o = luaC_newobjdt(L, LUA_TTHREAD, sizeof(LX), offsetof(LX, l));
+ L1 = gco2th(o);
+ /* anchor it on L stack */
+ setthvalue2s(L, L->top.p, L1);
+ api_incr_top(L);
+ preinit_thread(L1, g);
+ L1->hookmask = L->hookmask;
+ L1->basehookcount = L->basehookcount;
+ L1->hook = L->hook;
+ resethookcount(L1);
+ /* initialize L1 extra space */
+ memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread),
+ LUA_EXTRASPACE);
+ luai_userstatethread(L, L1);
+ stack_init(L1, L); /* init stack */
+ lua_unlock(L);
+ return L1;
+}
+
+
+void luaE_freethread (lua_State *L, lua_State *L1) {
+ LX *l = fromstate(L1);
+ luaF_closeupval(L1, L1->stack.p); /* close all upvalues */
+ lua_assert(L1->openupval == NULL);
+ luai_userstatefree(L, L1);
+ freestack(L1);
+ luaM_free(L, l);
+}
+
+
+int luaE_resetthread (lua_State *L, int status) {
+ CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */
+ setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */
+ ci->func.p = L->stack.p;
+ ci->callstatus = CIST_C;
+ if (status == LUA_YIELD)
+ status = LUA_OK;
+ L->status = LUA_OK; /* so it can run __close metamethods */
+ status = luaD_closeprotected(L, 1, status);
+ if (status != LUA_OK) /* errors? */
+ luaD_seterrorobj(L, status, L->stack.p + 1);
+ else
+ L->top.p = L->stack.p + 1;
+ ci->top.p = L->top.p + LUA_MINSTACK;
+ luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0);
+ return status;
+}
+
+
+LUA_API int lua_closethread (lua_State *L, lua_State *from) {
+ int status;
+ lua_lock(L);
+ L->nCcalls = (from) ? getCcalls(from) : 0;
+ status = luaE_resetthread(L, L->status);
+ lua_unlock(L);
+ return status;
+}
+
+
+/*
+** Deprecated! Use 'lua_closethread' instead.
+*/
+LUA_API int lua_resetthread (lua_State *L) {
+ return lua_closethread(L, NULL);
+}
+
+
+LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
+ int i;
+ lua_State *L;
+ global_State *g;
+ LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
+ if (l == NULL) return NULL;
+ L = &l->l.l;
+ g = &l->g;
+ L->tt = LUA_VTHREAD;
+ g->currentwhite = bitmask(WHITE0BIT);
+ L->marked = luaC_white(g);
+ preinit_thread(L, g);
+ g->allgc = obj2gco(L); /* by now, only object is the main thread */
+ L->next = NULL;
+ incnny(L); /* main thread is always non yieldable */
+ g->frealloc = f;
+ g->ud = ud;
+ g->warnf = NULL;
+ g->ud_warn = NULL;
+ g->mainthread = L;
+ g->seed = luai_makeseed(L);
+ g->gcstp = GCSTPGC; /* no GC while building state */
+ g->strt.size = g->strt.nuse = 0;
+ g->strt.hash = NULL;
+ setnilvalue(&g->l_registry);
+ g->panic = NULL;
+ g->gcstate = GCSpause;
+ g->gckind = KGC_INC;
+ g->gcstopem = 0;
+ g->gcemergency = 0;
+ g->finobj = g->tobefnz = g->fixedgc = NULL;
+ g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
+ g->finobjsur = g->finobjold1 = g->finobjrold = NULL;
+ g->sweepgc = NULL;
+ g->gray = g->grayagain = NULL;
+ g->weak = g->ephemeron = g->allweak = NULL;
+ g->twups = NULL;
+ g->totalbytes = sizeof(LG);
+ g->GCdebt = 0;
+ g->lastatomic = 0;
+ setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */
+ setgcparam(g->gcpause, LUAI_GCPAUSE);
+ setgcparam(g->gcstepmul, LUAI_GCMUL);
+ g->gcstepsize = LUAI_GCSTEPSIZE;
+ setgcparam(g->genmajormul, LUAI_GENMAJORMUL);
+ g->genminormul = LUAI_GENMINORMUL;
+ for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL;
+ if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) {
+ /* memory allocation error: free partial state */
+ close_state(L);
+ L = NULL;
+ }
+ return L;
+}
+
+
+LUA_API void lua_close (lua_State *L) {
+ lua_lock(L);
+ L = G(L)->mainthread; /* only the main thread can be closed */
+ close_state(L);
+}
+
+
+void luaE_warning (lua_State *L, const char *msg, int tocont) {
+ lua_WarnFunction wf = G(L)->warnf;
+ if (wf != NULL)
+ wf(G(L)->ud_warn, msg, tocont);
+}
+
+
+/*
+** Generate a warning from an error message
+*/
+void luaE_warnerror (lua_State *L, const char *where) {
+ TValue *errobj = s2v(L->top.p - 1); /* error object */
+ const char *msg = (ttisstring(errobj))
+ ? svalue(errobj)
+ : "error object is not a string";
+ /* produce warning "error in %s (%s)" (where, msg) */
+ luaE_warning(L, "error in ", 1);
+ luaE_warning(L, where, 1);
+ luaE_warning(L, " (", 1);
+ luaE_warning(L, msg, 1);
+ luaE_warning(L, ")", 0);
+}
+
diff --git a/src/libs/3rdparty/lua/src/lstate.h b/src/libs/3rdparty/lua/src/lstate.h
new file mode 100644
index 0000000000..8bf6600e34
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lstate.h
@@ -0,0 +1,409 @@
+/*
+** $Id: lstate.h $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstate_h
+#define lstate_h
+
+#include "lua.h"
+
+
+/* Some header files included here need this definition */
+typedef struct CallInfo CallInfo;
+
+
+#include "lobject.h"
+#include "ltm.h"
+#include "lzio.h"
+
+
+/*
+** Some notes about garbage-collected objects: All objects in Lua must
+** be kept somehow accessible until being freed, so all objects always
+** belong to one (and only one) of these lists, using field 'next' of
+** the 'CommonHeader' for the link:
+**
+** 'allgc': all objects not marked for finalization;
+** 'finobj': all objects marked for finalization;
+** 'tobefnz': all objects ready to be finalized;
+** 'fixedgc': all objects that are not to be collected (currently
+** only small strings, such as reserved words).
+**
+** For the generational collector, some of these lists have marks for
+** generations. Each mark points to the first element in the list for
+** that particular generation; that generation goes until the next mark.
+**
+** 'allgc' -> 'survival': new objects;
+** 'survival' -> 'old': objects that survived one collection;
+** 'old1' -> 'reallyold': objects that became old in last collection;
+** 'reallyold' -> NULL: objects old for more than one cycle.
+**
+** 'finobj' -> 'finobjsur': new objects marked for finalization;
+** 'finobjsur' -> 'finobjold1': survived """";
+** 'finobjold1' -> 'finobjrold': just old """";
+** 'finobjrold' -> NULL: really old """".
+**
+** All lists can contain elements older than their main ages, due
+** to 'luaC_checkfinalizer' and 'udata2finalize', which move
+** objects between the normal lists and the "marked for finalization"
+** lists. Moreover, barriers can age young objects in young lists as
+** OLD0, which then become OLD1. However, a list never contains
+** elements younger than their main ages.
+**
+** The generational collector also uses a pointer 'firstold1', which
+** points to the first OLD1 object in the list. It is used to optimize
+** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc'
+** and 'reallyold', but often the list has no OLD1 objects or they are
+** after 'old1'.) Note the difference between it and 'old1':
+** 'firstold1': no OLD1 objects before this point; there can be all
+** ages after it.
+** 'old1': no objects younger than OLD1 after this point.
+*/
+
+/*
+** Moreover, there is another set of lists that control gray objects.
+** These lists are linked by fields 'gclist'. (All objects that
+** can become gray have such a field. The field is not the same
+** in all objects, but it always has this name.) Any gray object
+** must belong to one of these lists, and all objects in these lists
+** must be gray (with two exceptions explained below):
+**
+** 'gray': regular gray objects, still waiting to be visited.
+** 'grayagain': objects that must be revisited at the atomic phase.
+** That includes
+** - black objects got in a write barrier;
+** - all kinds of weak tables during propagation phase;
+** - all threads.
+** 'weak': tables with weak values to be cleared;
+** 'ephemeron': ephemeron tables with white->white entries;
+** 'allweak': tables with weak keys and/or weak values to be cleared.
+**
+** The exceptions to that "gray rule" are:
+** - TOUCHED2 objects in generational mode stay in a gray list (because
+** they must be visited again at the end of the cycle), but they are
+** marked black because assignments to them must activate barriers (to
+** move them back to TOUCHED1).
+** - Open upvales are kept gray to avoid barriers, but they stay out
+** of gray lists. (They don't even have a 'gclist' field.)
+*/
+
+
+
+/*
+** About 'nCcalls': This count has two parts: the lower 16 bits counts
+** the number of recursive invocations in the C stack; the higher
+** 16 bits counts the number of non-yieldable calls in the stack.
+** (They are together so that we can change and save both with one
+** instruction.)
+*/
+
+
+/* true if this thread does not have non-yieldable calls in the stack */
+#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
+
+/* real number of C calls */
+#define getCcalls(L) ((L)->nCcalls & 0xffff)
+
+
+/* Increment the number of non-yieldable calls */
+#define incnny(L) ((L)->nCcalls += 0x10000)
+
+/* Decrement the number of non-yieldable calls */
+#define decnny(L) ((L)->nCcalls -= 0x10000)
+
+/* Non-yieldable call increment */
+#define nyci (0x10000 | 1)
+
+
+
+
+struct lua_longjmp; /* defined in ldo.c */
+
+
+/*
+** Atomic type (relative to signals) to better ensure that 'lua_sethook'
+** is thread safe
+*/
+#if !defined(l_signalT)
+#include <signal.h>
+#define l_signalT sig_atomic_t
+#endif
+
+
+/*
+** Extra stack space to handle TM calls and some other extras. This
+** space is not included in 'stack_last'. It is used only to avoid stack
+** checks, either because the element will be promptly popped or because
+** there will be a stack check soon after the push. Function frames
+** never use this extra space, so it does not need to be kept clean.
+*/
+#define EXTRA_STACK 5
+
+
+#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
+
+#define stacksize(th) cast_int((th)->stack_last.p - (th)->stack.p)
+
+
+/* kinds of Garbage Collection */
+#define KGC_INC 0 /* incremental gc */
+#define KGC_GEN 1 /* generational gc */
+
+
+typedef struct stringtable {
+ TString **hash;
+ int nuse; /* number of elements */
+ int size;
+} stringtable;
+
+
+/*
+** Information about a call.
+** About union 'u':
+** - field 'l' is used only for Lua functions;
+** - field 'c' is used only for C functions.
+** About union 'u2':
+** - field 'funcidx' is used only by C functions while doing a
+** protected call;
+** - field 'nyield' is used only while a function is "doing" an
+** yield (from the yield until the next resume);
+** - field 'nres' is used only while closing tbc variables when
+** returning from a function;
+** - field 'transferinfo' is used only during call/returnhooks,
+** before the function starts or after it ends.
+*/
+struct CallInfo {
+ StkIdRel func; /* function index in the stack */
+ StkIdRel top; /* top for this function */
+ struct CallInfo *previous, *next; /* dynamic call link */
+ union {
+ struct { /* only for Lua functions */
+ const Instruction *savedpc;
+ volatile l_signalT trap;
+ int nextraargs; /* # of extra arguments in vararg functions */
+ } l;
+ struct { /* only for C functions */
+ lua_KFunction k; /* continuation in case of yields */
+ ptrdiff_t old_errfunc;
+ lua_KContext ctx; /* context info. in case of yields */
+ } c;
+ } u;
+ union {
+ int funcidx; /* called-function index */
+ int nyield; /* number of values yielded */
+ int nres; /* number of values returned */
+ struct { /* info about transferred values (for call/return hooks) */
+ unsigned short ftransfer; /* offset of first value transferred */
+ unsigned short ntransfer; /* number of values transferred */
+ } transferinfo;
+ } u2;
+ short nresults; /* expected number of results from this function */
+ unsigned short callstatus;
+};
+
+
+/*
+** Bits in CallInfo status
+*/
+#define CIST_OAH (1<<0) /* original value of 'allowhook' */
+#define CIST_C (1<<1) /* call is running a C function */
+#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
+#define CIST_HOOKED (1<<3) /* call is running a debug hook */
+#define CIST_YPCALL (1<<4) /* doing a yieldable protected call */
+#define CIST_TAIL (1<<5) /* call was tail called */
+#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
+#define CIST_FIN (1<<7) /* function "called" a finalizer */
+#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
+#define CIST_CLSRET (1<<9) /* function is closing tbc variables */
+/* Bits 10-12 are used for CIST_RECST (see below) */
+#define CIST_RECST 10
+#if defined(LUA_COMPAT_LT_LE)
+#define CIST_LEQ (1<<13) /* using __lt for __le */
+#endif
+
+
+/*
+** Field CIST_RECST stores the "recover status", used to keep the error
+** status while closing to-be-closed variables in coroutines, so that
+** Lua can correctly resume after an yield from a __close method called
+** because of an error. (Three bits are enough for error status.)
+*/
+#define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7)
+#define setcistrecst(ci,st) \
+ check_exp(((st) & 7) == (st), /* status must fit in three bits */ \
+ ((ci)->callstatus = ((ci)->callstatus & ~(7 << CIST_RECST)) \
+ | ((st) << CIST_RECST)))
+
+
+/* active function is a Lua function */
+#define isLua(ci) (!((ci)->callstatus & CIST_C))
+
+/* call is running Lua code (not a hook) */
+#define isLuacode(ci) (!((ci)->callstatus & (CIST_C | CIST_HOOKED)))
+
+/* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */
+#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v))
+#define getoah(st) ((st) & CIST_OAH)
+
+
+/*
+** 'global state', shared by all threads of this state
+*/
+typedef struct global_State {
+ lua_Alloc frealloc; /* function to reallocate memory */
+ void *ud; /* auxiliary data to 'frealloc' */
+ l_mem totalbytes; /* number of bytes currently allocated - GCdebt */
+ l_mem GCdebt; /* bytes allocated not yet compensated by the collector */
+ lu_mem GCestimate; /* an estimate of the non-garbage memory in use */
+ lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */
+ stringtable strt; /* hash table for strings */
+ TValue l_registry;
+ TValue nilvalue; /* a nil value */
+ unsigned int seed; /* randomized seed for hashes */
+ lu_byte currentwhite;
+ lu_byte gcstate; /* state of garbage collector */
+ lu_byte gckind; /* kind of GC running */
+ lu_byte gcstopem; /* stops emergency collections */
+ lu_byte genminormul; /* control for minor generational collections */
+ lu_byte genmajormul; /* control for major generational collections */
+ lu_byte gcstp; /* control whether GC is running */
+ lu_byte gcemergency; /* true if this is an emergency collection */
+ lu_byte gcpause; /* size of pause between successive GCs */
+ lu_byte gcstepmul; /* GC "speed" */
+ lu_byte gcstepsize; /* (log2 of) GC granularity */
+ GCObject *allgc; /* list of all collectable objects */
+ GCObject **sweepgc; /* current position of sweep in list */
+ GCObject *finobj; /* list of collectable objects with finalizers */
+ GCObject *gray; /* list of gray objects */
+ GCObject *grayagain; /* list of objects to be traversed atomically */
+ GCObject *weak; /* list of tables with weak values */
+ GCObject *ephemeron; /* list of ephemeron tables (weak keys) */
+ GCObject *allweak; /* list of all-weak tables */
+ GCObject *tobefnz; /* list of userdata to be GC */
+ GCObject *fixedgc; /* list of objects not to be collected */
+ /* fields for generational collector */
+ GCObject *survival; /* start of objects that survived one GC cycle */
+ GCObject *old1; /* start of old1 objects */
+ GCObject *reallyold; /* objects more than one cycle old ("really old") */
+ GCObject *firstold1; /* first OLD1 object in the list (if any) */
+ GCObject *finobjsur; /* list of survival objects with finalizers */
+ GCObject *finobjold1; /* list of old1 objects with finalizers */
+ GCObject *finobjrold; /* list of really old objects with finalizers */
+ struct lua_State *twups; /* list of threads with open upvalues */
+ lua_CFunction panic; /* to be called in unprotected errors */
+ struct lua_State *mainthread;
+ TString *memerrmsg; /* message for memory-allocation errors */
+ TString *tmname[TM_N]; /* array with tag-method names */
+ struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */
+ TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
+ lua_WarnFunction warnf; /* warning function */
+ void *ud_warn; /* auxiliary data to 'warnf' */
+} global_State;
+
+
+/*
+** 'per thread' state
+*/
+struct lua_State {
+ CommonHeader;
+ lu_byte status;
+ lu_byte allowhook;
+ unsigned short nci; /* number of items in 'ci' list */
+ StkIdRel top; /* first free slot in the stack */
+ global_State *l_G;
+ CallInfo *ci; /* call info for current function */
+ StkIdRel stack_last; /* end of stack (last element + 1) */
+ StkIdRel stack; /* stack base */
+ UpVal *openupval; /* list of open upvalues in this stack */
+ StkIdRel tbclist; /* list of to-be-closed variables */
+ GCObject *gclist;
+ struct lua_State *twups; /* list of threads with open upvalues */
+ struct lua_longjmp *errorJmp; /* current error recover point */
+ CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
+ volatile lua_Hook hook;
+ ptrdiff_t errfunc; /* current error handling function (stack index) */
+ l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */
+ int oldpc; /* last pc traced */
+ int basehookcount;
+ int hookcount;
+ volatile l_signalT hookmask;
+};
+
+
+#define G(L) (L->l_G)
+
+/*
+** 'g->nilvalue' being a nil value flags that the state was completely
+** build.
+*/
+#define completestate(g) ttisnil(&g->nilvalue)
+
+
+/*
+** Union of all collectable objects (only for conversions)
+** ISO C99, 6.5.2.3 p.5:
+** "if a union contains several structures that share a common initial
+** sequence [...], and if the union object currently contains one
+** of these structures, it is permitted to inspect the common initial
+** part of any of them anywhere that a declaration of the complete type
+** of the union is visible."
+*/
+union GCUnion {
+ GCObject gc; /* common header */
+ struct TString ts;
+ struct Udata u;
+ union Closure cl;
+ struct Table h;
+ struct Proto p;
+ struct lua_State th; /* thread */
+ struct UpVal upv;
+};
+
+
+/*
+** ISO C99, 6.7.2.1 p.14:
+** "A pointer to a union object, suitably converted, points to each of
+** its members [...], and vice versa."
+*/
+#define cast_u(o) cast(union GCUnion *, (o))
+
+/* macros to convert a GCObject into a specific value */
+#define gco2ts(o) \
+ check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts))
+#define gco2u(o) check_exp((o)->tt == LUA_VUSERDATA, &((cast_u(o))->u))
+#define gco2lcl(o) check_exp((o)->tt == LUA_VLCL, &((cast_u(o))->cl.l))
+#define gco2ccl(o) check_exp((o)->tt == LUA_VCCL, &((cast_u(o))->cl.c))
+#define gco2cl(o) \
+ check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl))
+#define gco2t(o) check_exp((o)->tt == LUA_VTABLE, &((cast_u(o))->h))
+#define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p))
+#define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th))
+#define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((cast_u(o))->upv))
+
+
+/*
+** macro to convert a Lua object into a GCObject
+** (The access to 'tt' tries to ensure that 'v' is actually a Lua object.)
+*/
+#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc))
+
+
+/* actual number of total bytes allocated */
+#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt)
+
+LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt);
+LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
+LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
+LUAI_FUNC void luaE_freeCI (lua_State *L);
+LUAI_FUNC void luaE_shrinkCI (lua_State *L);
+LUAI_FUNC void luaE_checkcstack (lua_State *L);
+LUAI_FUNC void luaE_incCstack (lua_State *L);
+LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
+LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
+LUAI_FUNC int luaE_resetthread (lua_State *L, int status);
+
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/lstring.c b/src/libs/3rdparty/lua/src/lstring.c
new file mode 100644
index 0000000000..13dcaf4259
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lstring.c
@@ -0,0 +1,273 @@
+/*
+** $Id: lstring.c $
+** String table (keeps all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+#define lstring_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+
+
+/*
+** Maximum size for string table.
+*/
+#define MAXSTRTB cast_int(luaM_limitN(MAX_INT, TString*))
+
+
+/*
+** equality for long strings
+*/
+int luaS_eqlngstr (TString *a, TString *b) {
+ size_t len = a->u.lnglen;
+ lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR);
+ return (a == b) || /* same instance or... */
+ ((len == b->u.lnglen) && /* equal length and ... */
+ (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */
+}
+
+
+unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
+ unsigned int h = seed ^ cast_uint(l);
+ for (; l > 0; l--)
+ h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
+ return h;
+}
+
+
+unsigned int luaS_hashlongstr (TString *ts) {
+ lua_assert(ts->tt == LUA_VLNGSTR);
+ if (ts->extra == 0) { /* no hash? */
+ size_t len = ts->u.lnglen;
+ ts->hash = luaS_hash(getstr(ts), len, ts->hash);
+ ts->extra = 1; /* now it has its hash */
+ }
+ return ts->hash;
+}
+
+
+static void tablerehash (TString **vect, int osize, int nsize) {
+ int i;
+ for (i = osize; i < nsize; i++) /* clear new elements */
+ vect[i] = NULL;
+ for (i = 0; i < osize; i++) { /* rehash old part of the array */
+ TString *p = vect[i];
+ vect[i] = NULL;
+ while (p) { /* for each string in the list */
+ TString *hnext = p->u.hnext; /* save next */
+ unsigned int h = lmod(p->hash, nsize); /* new position */
+ p->u.hnext = vect[h]; /* chain it into array */
+ vect[h] = p;
+ p = hnext;
+ }
+ }
+}
+
+
+/*
+** Resize the string table. If allocation fails, keep the current size.
+** (This can degrade performance, but any non-zero size should work
+** correctly.)
+*/
+void luaS_resize (lua_State *L, int nsize) {
+ stringtable *tb = &G(L)->strt;
+ int osize = tb->size;
+ TString **newvect;
+ if (nsize < osize) /* shrinking table? */
+ tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */
+ newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*);
+ if (l_unlikely(newvect == NULL)) { /* reallocation failed? */
+ if (nsize < osize) /* was it shrinking table? */
+ tablerehash(tb->hash, nsize, osize); /* restore to original size */
+ /* leave table as it was */
+ }
+ else { /* allocation succeeded */
+ tb->hash = newvect;
+ tb->size = nsize;
+ if (nsize > osize)
+ tablerehash(newvect, osize, nsize); /* rehash for new size */
+ }
+}
+
+
+/*
+** Clear API string cache. (Entries cannot be empty, so fill them with
+** a non-collectable string.)
+*/
+void luaS_clearcache (global_State *g) {
+ int i, j;
+ for (i = 0; i < STRCACHE_N; i++)
+ for (j = 0; j < STRCACHE_M; j++) {
+ if (iswhite(g->strcache[i][j])) /* will entry be collected? */
+ g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */
+ }
+}
+
+
+/*
+** Initialize the string table and the string cache
+*/
+void luaS_init (lua_State *L) {
+ global_State *g = G(L);
+ int i, j;
+ stringtable *tb = &G(L)->strt;
+ tb->hash = luaM_newvector(L, MINSTRTABSIZE, TString*);
+ tablerehash(tb->hash, 0, MINSTRTABSIZE); /* clear array */
+ tb->size = MINSTRTABSIZE;
+ /* pre-create memory-error message */
+ g->memerrmsg = luaS_newliteral(L, MEMERRMSG);
+ luaC_fix(L, obj2gco(g->memerrmsg)); /* it should never be collected */
+ for (i = 0; i < STRCACHE_N; i++) /* fill cache with valid strings */
+ for (j = 0; j < STRCACHE_M; j++)
+ g->strcache[i][j] = g->memerrmsg;
+}
+
+
+
+/*
+** creates a new string object
+*/
+static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) {
+ TString *ts;
+ GCObject *o;
+ size_t totalsize; /* total size of TString object */
+ totalsize = sizelstring(l);
+ o = luaC_newobj(L, tag, totalsize);
+ ts = gco2ts(o);
+ ts->hash = h;
+ ts->extra = 0;
+ getstr(ts)[l] = '\0'; /* ending 0 */
+ return ts;
+}
+
+
+TString *luaS_createlngstrobj (lua_State *L, size_t l) {
+ TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed);
+ ts->u.lnglen = l;
+ return ts;
+}
+
+
+void luaS_remove (lua_State *L, TString *ts) {
+ stringtable *tb = &G(L)->strt;
+ TString **p = &tb->hash[lmod(ts->hash, tb->size)];
+ while (*p != ts) /* find previous element */
+ p = &(*p)->u.hnext;
+ *p = (*p)->u.hnext; /* remove element from its list */
+ tb->nuse--;
+}
+
+
+static void growstrtab (lua_State *L, stringtable *tb) {
+ if (l_unlikely(tb->nuse == MAX_INT)) { /* too many strings? */
+ luaC_fullgc(L, 1); /* try to free some... */
+ if (tb->nuse == MAX_INT) /* still too many? */
+ luaM_error(L); /* cannot even create a message... */
+ }
+ if (tb->size <= MAXSTRTB / 2) /* can grow string table? */
+ luaS_resize(L, tb->size * 2);
+}
+
+
+/*
+** Checks whether short string exists and reuses it or creates a new one.
+*/
+static TString *internshrstr (lua_State *L, const char *str, size_t l) {
+ TString *ts;
+ global_State *g = G(L);
+ stringtable *tb = &g->strt;
+ unsigned int h = luaS_hash(str, l, g->seed);
+ TString **list = &tb->hash[lmod(h, tb->size)];
+ lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */
+ for (ts = *list; ts != NULL; ts = ts->u.hnext) {
+ if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) {
+ /* found! */
+ if (isdead(g, ts)) /* dead (but not collected yet)? */
+ changewhite(ts); /* resurrect it */
+ return ts;
+ }
+ }
+ /* else must create a new string */
+ if (tb->nuse >= tb->size) { /* need to grow string table? */
+ growstrtab(L, tb);
+ list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */
+ }
+ ts = createstrobj(L, l, LUA_VSHRSTR, h);
+ memcpy(getstr(ts), str, l * sizeof(char));
+ ts->shrlen = cast_byte(l);
+ ts->u.hnext = *list;
+ *list = ts;
+ tb->nuse++;
+ return ts;
+}
+
+
+/*
+** new string (with explicit length)
+*/
+TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
+ if (l <= LUAI_MAXSHORTLEN) /* short string? */
+ return internshrstr(L, str, l);
+ else {
+ TString *ts;
+ if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
+ luaM_toobig(L);
+ ts = luaS_createlngstrobj(L, l);
+ memcpy(getstr(ts), str, l * sizeof(char));
+ return ts;
+ }
+}
+
+
+/*
+** Create or reuse a zero-terminated string, first checking in the
+** cache (using the string address as a key). The cache can contain
+** only zero-terminated strings, so it is safe to use 'strcmp' to
+** check hits.
+*/
+TString *luaS_new (lua_State *L, const char *str) {
+ unsigned int i = point2uint(str) % STRCACHE_N; /* hash */
+ int j;
+ TString **p = G(L)->strcache[i];
+ for (j = 0; j < STRCACHE_M; j++) {
+ if (strcmp(str, getstr(p[j])) == 0) /* hit? */
+ return p[j]; /* that is it */
+ }
+ /* normal route */
+ for (j = STRCACHE_M - 1; j > 0; j--)
+ p[j] = p[j - 1]; /* move out last element */
+ /* new element is first in the list */
+ p[0] = luaS_newlstr(L, str, strlen(str));
+ return p[0];
+}
+
+
+Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) {
+ Udata *u;
+ int i;
+ GCObject *o;
+ if (l_unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
+ luaM_toobig(L);
+ o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s));
+ u = gco2u(o);
+ u->len = s;
+ u->nuvalue = nuvalue;
+ u->metatable = NULL;
+ for (i = 0; i < nuvalue; i++)
+ setnilvalue(&u->uv[i].uv);
+ return u;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lstring.h b/src/libs/3rdparty/lua/src/lstring.h
new file mode 100644
index 0000000000..450c2390d1
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lstring.h
@@ -0,0 +1,57 @@
+/*
+** $Id: lstring.h $
+** String table (keep all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstring_h
+#define lstring_h
+
+#include "lgc.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+/*
+** Memory-allocation error message must be preallocated (it cannot
+** be created after memory is exhausted)
+*/
+#define MEMERRMSG "not enough memory"
+
+
+/*
+** Size of a TString: Size of the header plus space for the string
+** itself (including final '\0').
+*/
+#define sizelstring(l) (offsetof(TString, contents) + ((l) + 1) * sizeof(char))
+
+#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
+ (sizeof(s)/sizeof(char))-1))
+
+
+/*
+** test whether a string is a reserved word
+*/
+#define isreserved(s) ((s)->tt == LUA_VSHRSTR && (s)->extra > 0)
+
+
+/*
+** equality for short strings, which are always internalized
+*/
+#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b))
+
+
+LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
+LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts);
+LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
+LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
+LUAI_FUNC void luaS_clearcache (global_State *g);
+LUAI_FUNC void luaS_init (lua_State *L);
+LUAI_FUNC void luaS_remove (lua_State *L, TString *ts);
+LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue);
+LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
+LUAI_FUNC TString *luaS_new (lua_State *L, const char *str);
+LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lstrlib.c b/src/libs/3rdparty/lua/src/lstrlib.c
new file mode 100644
index 0000000000..03167161df
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lstrlib.c
@@ -0,0 +1,1874 @@
+/*
+** $Id: lstrlib.c $
+** Standard library for string operations and pattern-matching
+** See Copyright Notice in lua.h
+*/
+
+#define lstrlib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <ctype.h>
+#include <float.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** maximum number of captures that a pattern can do during
+** pattern-matching. This limit is arbitrary, but must fit in
+** an unsigned char.
+*/
+#if !defined(LUA_MAXCAPTURES)
+#define LUA_MAXCAPTURES 32
+#endif
+
+
+/* macro to 'unsign' a character */
+#define uchar(c) ((unsigned char)(c))
+
+
+/*
+** Some sizes are better limited to fit in 'int', but must also fit in
+** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.)
+*/
+#define MAX_SIZET ((size_t)(~(size_t)0))
+
+#define MAXSIZE \
+ (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX))
+
+
+
+
+static int str_len (lua_State *L) {
+ size_t l;
+ luaL_checklstring(L, 1, &l);
+ lua_pushinteger(L, (lua_Integer)l);
+ return 1;
+}
+
+
+/*
+** translate a relative initial string position
+** (negative means back from end): clip result to [1, inf).
+** The length of any string in Lua must fit in a lua_Integer,
+** so there are no overflows in the casts.
+** The inverted comparison avoids a possible overflow
+** computing '-pos'.
+*/
+static size_t posrelatI (lua_Integer pos, size_t len) {
+ if (pos > 0)
+ return (size_t)pos;
+ else if (pos == 0)
+ return 1;
+ else if (pos < -(lua_Integer)len) /* inverted comparison */
+ return 1; /* clip to 1 */
+ else return len + (size_t)pos + 1;
+}
+
+
+/*
+** Gets an optional ending string position from argument 'arg',
+** with default value 'def'.
+** Negative means back from end: clip result to [0, len]
+*/
+static size_t getendpos (lua_State *L, int arg, lua_Integer def,
+ size_t len) {
+ lua_Integer pos = luaL_optinteger(L, arg, def);
+ if (pos > (lua_Integer)len)
+ return len;
+ else if (pos >= 0)
+ return (size_t)pos;
+ else if (pos < -(lua_Integer)len)
+ return 0;
+ else return len + (size_t)pos + 1;
+}
+
+
+static int str_sub (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ size_t start = posrelatI(luaL_checkinteger(L, 2), l);
+ size_t end = getendpos(L, 3, -1, l);
+ if (start <= end)
+ lua_pushlstring(L, s + start - 1, (end - start) + 1);
+ else lua_pushliteral(L, "");
+ return 1;
+}
+
+
+static int str_reverse (lua_State *L) {
+ size_t l, i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ char *p = luaL_buffinitsize(L, &b, l);
+ for (i = 0; i < l; i++)
+ p[i] = s[l - i - 1];
+ luaL_pushresultsize(&b, l);
+ return 1;
+}
+
+
+static int str_lower (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ char *p = luaL_buffinitsize(L, &b, l);
+ for (i=0; i<l; i++)
+ p[i] = tolower(uchar(s[i]));
+ luaL_pushresultsize(&b, l);
+ return 1;
+}
+
+
+static int str_upper (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ char *p = luaL_buffinitsize(L, &b, l);
+ for (i=0; i<l; i++)
+ p[i] = toupper(uchar(s[i]));
+ luaL_pushresultsize(&b, l);
+ return 1;
+}
+
+
+static int str_rep (lua_State *L) {
+ size_t l, lsep;
+ const char *s = luaL_checklstring(L, 1, &l);
+ lua_Integer n = luaL_checkinteger(L, 2);
+ const char *sep = luaL_optlstring(L, 3, "", &lsep);
+ if (n <= 0)
+ lua_pushliteral(L, "");
+ else if (l_unlikely(l + lsep < l || l + lsep > MAXSIZE / n))
+ return luaL_error(L, "resulting string too large");
+ else {
+ size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep;
+ luaL_Buffer b;
+ char *p = luaL_buffinitsize(L, &b, totallen);
+ while (n-- > 1) { /* first n-1 copies (followed by separator) */
+ memcpy(p, s, l * sizeof(char)); p += l;
+ if (lsep > 0) { /* empty 'memcpy' is not that cheap */
+ memcpy(p, sep, lsep * sizeof(char));
+ p += lsep;
+ }
+ }
+ memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */
+ luaL_pushresultsize(&b, totallen);
+ }
+ return 1;
+}
+
+
+static int str_byte (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ lua_Integer pi = luaL_optinteger(L, 2, 1);
+ size_t posi = posrelatI(pi, l);
+ size_t pose = getendpos(L, 3, pi, l);
+ int n, i;
+ if (posi > pose) return 0; /* empty interval; return no values */
+ if (l_unlikely(pose - posi >= (size_t)INT_MAX)) /* arithmetic overflow? */
+ return luaL_error(L, "string slice too long");
+ n = (int)(pose - posi) + 1;
+ luaL_checkstack(L, n, "string slice too long");
+ for (i=0; i<n; i++)
+ lua_pushinteger(L, uchar(s[posi+i-1]));
+ return n;
+}
+
+
+static int str_char (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ luaL_Buffer b;
+ char *p = luaL_buffinitsize(L, &b, n);
+ for (i=1; i<=n; i++) {
+ lua_Unsigned c = (lua_Unsigned)luaL_checkinteger(L, i);
+ luaL_argcheck(L, c <= (lua_Unsigned)UCHAR_MAX, i, "value out of range");
+ p[i - 1] = uchar(c);
+ }
+ luaL_pushresultsize(&b, n);
+ return 1;
+}
+
+
+/*
+** Buffer to store the result of 'string.dump'. It must be initialized
+** after the call to 'lua_dump', to ensure that the function is on the
+** top of the stack when 'lua_dump' is called. ('luaL_buffinit' might
+** push stuff.)
+*/
+struct str_Writer {
+ int init; /* true iff buffer has been initialized */
+ luaL_Buffer B;
+};
+
+
+static int writer (lua_State *L, const void *b, size_t size, void *ud) {
+ struct str_Writer *state = (struct str_Writer *)ud;
+ if (!state->init) {
+ state->init = 1;
+ luaL_buffinit(L, &state->B);
+ }
+ luaL_addlstring(&state->B, (const char *)b, size);
+ return 0;
+}
+
+
+static int str_dump (lua_State *L) {
+ struct str_Writer state;
+ int strip = lua_toboolean(L, 2);
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ lua_settop(L, 1); /* ensure function is on the top of the stack */
+ state.init = 0;
+ if (l_unlikely(lua_dump(L, writer, &state, strip) != 0))
+ return luaL_error(L, "unable to dump given function");
+ luaL_pushresult(&state.B);
+ return 1;
+}
+
+
+
+/*
+** {======================================================
+** METAMETHODS
+** =======================================================
+*/
+
+#if defined(LUA_NOCVTS2N) /* { */
+
+/* no coercion from strings to numbers */
+
+static const luaL_Reg stringmetamethods[] = {
+ {"__index", NULL}, /* placeholder */
+ {NULL, NULL}
+};
+
+#else /* }{ */
+
+static int tonum (lua_State *L, int arg) {
+ if (lua_type(L, arg) == LUA_TNUMBER) { /* already a number? */
+ lua_pushvalue(L, arg);
+ return 1;
+ }
+ else { /* check whether it is a numerical string */
+ size_t len;
+ const char *s = lua_tolstring(L, arg, &len);
+ return (s != NULL && lua_stringtonumber(L, s) == len + 1);
+ }
+}
+
+
+static void trymt (lua_State *L, const char *mtname) {
+ lua_settop(L, 2); /* back to the original arguments */
+ if (l_unlikely(lua_type(L, 2) == LUA_TSTRING ||
+ !luaL_getmetafield(L, 2, mtname)))
+ luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2,
+ luaL_typename(L, -2), luaL_typename(L, -1));
+ lua_insert(L, -3); /* put metamethod before arguments */
+ lua_call(L, 2, 1); /* call metamethod */
+}
+
+
+static int arith (lua_State *L, int op, const char *mtname) {
+ if (tonum(L, 1) && tonum(L, 2))
+ lua_arith(L, op); /* result will be on the top */
+ else
+ trymt(L, mtname);
+ return 1;
+}
+
+
+static int arith_add (lua_State *L) {
+ return arith(L, LUA_OPADD, "__add");
+}
+
+static int arith_sub (lua_State *L) {
+ return arith(L, LUA_OPSUB, "__sub");
+}
+
+static int arith_mul (lua_State *L) {
+ return arith(L, LUA_OPMUL, "__mul");
+}
+
+static int arith_mod (lua_State *L) {
+ return arith(L, LUA_OPMOD, "__mod");
+}
+
+static int arith_pow (lua_State *L) {
+ return arith(L, LUA_OPPOW, "__pow");
+}
+
+static int arith_div (lua_State *L) {
+ return arith(L, LUA_OPDIV, "__div");
+}
+
+static int arith_idiv (lua_State *L) {
+ return arith(L, LUA_OPIDIV, "__idiv");
+}
+
+static int arith_unm (lua_State *L) {
+ return arith(L, LUA_OPUNM, "__unm");
+}
+
+
+static const luaL_Reg stringmetamethods[] = {
+ {"__add", arith_add},
+ {"__sub", arith_sub},
+ {"__mul", arith_mul},
+ {"__mod", arith_mod},
+ {"__pow", arith_pow},
+ {"__div", arith_div},
+ {"__idiv", arith_idiv},
+ {"__unm", arith_unm},
+ {"__index", NULL}, /* placeholder */
+ {NULL, NULL}
+};
+
+#endif /* } */
+
+/* }====================================================== */
+
+/*
+** {======================================================
+** PATTERN MATCHING
+** =======================================================
+*/
+
+
+#define CAP_UNFINISHED (-1)
+#define CAP_POSITION (-2)
+
+
+typedef struct MatchState {
+ const char *src_init; /* init of source string */
+ const char *src_end; /* end ('\0') of source string */
+ const char *p_end; /* end ('\0') of pattern */
+ lua_State *L;
+ int matchdepth; /* control for recursive depth (to avoid C stack overflow) */
+ unsigned char level; /* total number of captures (finished or unfinished) */
+ struct {
+ const char *init;
+ ptrdiff_t len;
+ } capture[LUA_MAXCAPTURES];
+} MatchState;
+
+
+/* recursive function */
+static const char *match (MatchState *ms, const char *s, const char *p);
+
+
+/* maximum recursion depth for 'match' */
+#if !defined(MAXCCALLS)
+#define MAXCCALLS 200
+#endif
+
+
+#define L_ESC '%'
+#define SPECIALS "^$*+?.([%-"
+
+
+static int check_capture (MatchState *ms, int l) {
+ l -= '1';
+ if (l_unlikely(l < 0 || l >= ms->level ||
+ ms->capture[l].len == CAP_UNFINISHED))
+ return luaL_error(ms->L, "invalid capture index %%%d", l + 1);
+ return l;
+}
+
+
+static int capture_to_close (MatchState *ms) {
+ int level = ms->level;
+ for (level--; level>=0; level--)
+ if (ms->capture[level].len == CAP_UNFINISHED) return level;
+ return luaL_error(ms->L, "invalid pattern capture");
+}
+
+
+static const char *classend (MatchState *ms, const char *p) {
+ switch (*p++) {
+ case L_ESC: {
+ if (l_unlikely(p == ms->p_end))
+ luaL_error(ms->L, "malformed pattern (ends with '%%')");
+ return p+1;
+ }
+ case '[': {
+ if (*p == '^') p++;
+ do { /* look for a ']' */
+ if (l_unlikely(p == ms->p_end))
+ luaL_error(ms->L, "malformed pattern (missing ']')");
+ if (*(p++) == L_ESC && p < ms->p_end)
+ p++; /* skip escapes (e.g. '%]') */
+ } while (*p != ']');
+ return p+1;
+ }
+ default: {
+ return p;
+ }
+ }
+}
+
+
+static int match_class (int c, int cl) {
+ int res;
+ switch (tolower(cl)) {
+ case 'a' : res = isalpha(c); break;
+ case 'c' : res = iscntrl(c); break;
+ case 'd' : res = isdigit(c); break;
+ case 'g' : res = isgraph(c); break;
+ case 'l' : res = islower(c); break;
+ case 'p' : res = ispunct(c); break;
+ case 's' : res = isspace(c); break;
+ case 'u' : res = isupper(c); break;
+ case 'w' : res = isalnum(c); break;
+ case 'x' : res = isxdigit(c); break;
+ case 'z' : res = (c == 0); break; /* deprecated option */
+ default: return (cl == c);
+ }
+ return (islower(cl) ? res : !res);
+}
+
+
+static int matchbracketclass (int c, const char *p, const char *ec) {
+ int sig = 1;
+ if (*(p+1) == '^') {
+ sig = 0;
+ p++; /* skip the '^' */
+ }
+ while (++p < ec) {
+ if (*p == L_ESC) {
+ p++;
+ if (match_class(c, uchar(*p)))
+ return sig;
+ }
+ else if ((*(p+1) == '-') && (p+2 < ec)) {
+ p+=2;
+ if (uchar(*(p-2)) <= c && c <= uchar(*p))
+ return sig;
+ }
+ else if (uchar(*p) == c) return sig;
+ }
+ return !sig;
+}
+
+
+static int singlematch (MatchState *ms, const char *s, const char *p,
+ const char *ep) {
+ if (s >= ms->src_end)
+ return 0;
+ else {
+ int c = uchar(*s);
+ switch (*p) {
+ case '.': return 1; /* matches any char */
+ case L_ESC: return match_class(c, uchar(*(p+1)));
+ case '[': return matchbracketclass(c, p, ep-1);
+ default: return (uchar(*p) == c);
+ }
+ }
+}
+
+
+static const char *matchbalance (MatchState *ms, const char *s,
+ const char *p) {
+ if (l_unlikely(p >= ms->p_end - 1))
+ luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')");
+ if (*s != *p) return NULL;
+ else {
+ int b = *p;
+ int e = *(p+1);
+ int cont = 1;
+ while (++s < ms->src_end) {
+ if (*s == e) {
+ if (--cont == 0) return s+1;
+ }
+ else if (*s == b) cont++;
+ }
+ }
+ return NULL; /* string ends out of balance */
+}
+
+
+static const char *max_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ ptrdiff_t i = 0; /* counts maximum expand for item */
+ while (singlematch(ms, s + i, p, ep))
+ i++;
+ /* keeps trying to match with the maximum repetitions */
+ while (i>=0) {
+ const char *res = match(ms, (s+i), ep+1);
+ if (res) return res;
+ i--; /* else didn't match; reduce 1 repetition to try again */
+ }
+ return NULL;
+}
+
+
+static const char *min_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ for (;;) {
+ const char *res = match(ms, s, ep+1);
+ if (res != NULL)
+ return res;
+ else if (singlematch(ms, s, p, ep))
+ s++; /* try with one more repetition */
+ else return NULL;
+ }
+}
+
+
+static const char *start_capture (MatchState *ms, const char *s,
+ const char *p, int what) {
+ const char *res;
+ int level = ms->level;
+ if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
+ ms->capture[level].init = s;
+ ms->capture[level].len = what;
+ ms->level = level+1;
+ if ((res=match(ms, s, p)) == NULL) /* match failed? */
+ ms->level--; /* undo capture */
+ return res;
+}
+
+
+static const char *end_capture (MatchState *ms, const char *s,
+ const char *p) {
+ int l = capture_to_close(ms);
+ const char *res;
+ ms->capture[l].len = s - ms->capture[l].init; /* close capture */
+ if ((res = match(ms, s, p)) == NULL) /* match failed? */
+ ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
+ return res;
+}
+
+
+static const char *match_capture (MatchState *ms, const char *s, int l) {
+ size_t len;
+ l = check_capture(ms, l);
+ len = ms->capture[l].len;
+ if ((size_t)(ms->src_end-s) >= len &&
+ memcmp(ms->capture[l].init, s, len) == 0)
+ return s+len;
+ else return NULL;
+}
+
+
+static const char *match (MatchState *ms, const char *s, const char *p) {
+ if (l_unlikely(ms->matchdepth-- == 0))
+ luaL_error(ms->L, "pattern too complex");
+ init: /* using goto to optimize tail recursion */
+ if (p != ms->p_end) { /* end of pattern? */
+ switch (*p) {
+ case '(': { /* start capture */
+ if (*(p + 1) == ')') /* position capture? */
+ s = start_capture(ms, s, p + 2, CAP_POSITION);
+ else
+ s = start_capture(ms, s, p + 1, CAP_UNFINISHED);
+ break;
+ }
+ case ')': { /* end capture */
+ s = end_capture(ms, s, p + 1);
+ break;
+ }
+ case '$': {
+ if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */
+ goto dflt; /* no; go to default */
+ s = (s == ms->src_end) ? s : NULL; /* check end of string */
+ break;
+ }
+ case L_ESC: { /* escaped sequences not in the format class[*+?-]? */
+ switch (*(p + 1)) {
+ case 'b': { /* balanced string? */
+ s = matchbalance(ms, s, p + 2);
+ if (s != NULL) {
+ p += 4; goto init; /* return match(ms, s, p + 4); */
+ } /* else fail (s == NULL) */
+ break;
+ }
+ case 'f': { /* frontier? */
+ const char *ep; char previous;
+ p += 2;
+ if (l_unlikely(*p != '['))
+ luaL_error(ms->L, "missing '[' after '%%f' in pattern");
+ ep = classend(ms, p); /* points to what is next */
+ previous = (s == ms->src_init) ? '\0' : *(s - 1);
+ if (!matchbracketclass(uchar(previous), p, ep - 1) &&
+ matchbracketclass(uchar(*s), p, ep - 1)) {
+ p = ep; goto init; /* return match(ms, s, ep); */
+ }
+ s = NULL; /* match failed */
+ break;
+ }
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9': { /* capture results (%0-%9)? */
+ s = match_capture(ms, s, uchar(*(p + 1)));
+ if (s != NULL) {
+ p += 2; goto init; /* return match(ms, s, p + 2) */
+ }
+ break;
+ }
+ default: goto dflt;
+ }
+ break;
+ }
+ default: dflt: { /* pattern class plus optional suffix */
+ const char *ep = classend(ms, p); /* points to optional suffix */
+ /* does not match at least once? */
+ if (!singlematch(ms, s, p, ep)) {
+ if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */
+ p = ep + 1; goto init; /* return match(ms, s, ep + 1); */
+ }
+ else /* '+' or no suffix */
+ s = NULL; /* fail */
+ }
+ else { /* matched once */
+ switch (*ep) { /* handle optional suffix */
+ case '?': { /* optional */
+ const char *res;
+ if ((res = match(ms, s + 1, ep + 1)) != NULL)
+ s = res;
+ else {
+ p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */
+ }
+ break;
+ }
+ case '+': /* 1 or more repetitions */
+ s++; /* 1 match already done */
+ /* FALLTHROUGH */
+ case '*': /* 0 or more repetitions */
+ s = max_expand(ms, s, p, ep);
+ break;
+ case '-': /* 0 or more repetitions (minimum) */
+ s = min_expand(ms, s, p, ep);
+ break;
+ default: /* no suffix */
+ s++; p = ep; goto init; /* return match(ms, s + 1, ep); */
+ }
+ }
+ break;
+ }
+ }
+ }
+ ms->matchdepth++;
+ return s;
+}
+
+
+
+static const char *lmemfind (const char *s1, size_t l1,
+ const char *s2, size_t l2) {
+ if (l2 == 0) return s1; /* empty strings are everywhere */
+ else if (l2 > l1) return NULL; /* avoids a negative 'l1' */
+ else {
+ const char *init; /* to search for a '*s2' inside 's1' */
+ l2--; /* 1st char will be checked by 'memchr' */
+ l1 = l1-l2; /* 's2' cannot be found after that */
+ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
+ init++; /* 1st char is already checked */
+ if (memcmp(init, s2+1, l2) == 0)
+ return init-1;
+ else { /* correct 'l1' and 's1' to try again */
+ l1 -= init-s1;
+ s1 = init;
+ }
+ }
+ return NULL; /* not found */
+ }
+}
+
+
+/*
+** get information about the i-th capture. If there are no captures
+** and 'i==0', return information about the whole match, which
+** is the range 's'..'e'. If the capture is a string, return
+** its length and put its address in '*cap'. If it is an integer
+** (a position), push it on the stack and return CAP_POSITION.
+*/
+static size_t get_onecapture (MatchState *ms, int i, const char *s,
+ const char *e, const char **cap) {
+ if (i >= ms->level) {
+ if (l_unlikely(i != 0))
+ luaL_error(ms->L, "invalid capture index %%%d", i + 1);
+ *cap = s;
+ return e - s;
+ }
+ else {
+ ptrdiff_t capl = ms->capture[i].len;
+ *cap = ms->capture[i].init;
+ if (l_unlikely(capl == CAP_UNFINISHED))
+ luaL_error(ms->L, "unfinished capture");
+ else if (capl == CAP_POSITION)
+ lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1);
+ return capl;
+ }
+}
+
+
+/*
+** Push the i-th capture on the stack.
+*/
+static void push_onecapture (MatchState *ms, int i, const char *s,
+ const char *e) {
+ const char *cap;
+ ptrdiff_t l = get_onecapture(ms, i, s, e, &cap);
+ if (l != CAP_POSITION)
+ lua_pushlstring(ms->L, cap, l);
+ /* else position was already pushed */
+}
+
+
+static int push_captures (MatchState *ms, const char *s, const char *e) {
+ int i;
+ int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
+ luaL_checkstack(ms->L, nlevels, "too many captures");
+ for (i = 0; i < nlevels; i++)
+ push_onecapture(ms, i, s, e);
+ return nlevels; /* number of strings pushed */
+}
+
+
+/* check whether pattern has no special characters */
+static int nospecials (const char *p, size_t l) {
+ size_t upto = 0;
+ do {
+ if (strpbrk(p + upto, SPECIALS))
+ return 0; /* pattern has a special character */
+ upto += strlen(p + upto) + 1; /* may have more after \0 */
+ } while (upto <= l);
+ return 1; /* no special chars found */
+}
+
+
+static void prepstate (MatchState *ms, lua_State *L,
+ const char *s, size_t ls, const char *p, size_t lp) {
+ ms->L = L;
+ ms->matchdepth = MAXCCALLS;
+ ms->src_init = s;
+ ms->src_end = s + ls;
+ ms->p_end = p + lp;
+}
+
+
+static void reprepstate (MatchState *ms) {
+ ms->level = 0;
+ lua_assert(ms->matchdepth == MAXCCALLS);
+}
+
+
+static int str_find_aux (lua_State *L, int find) {
+ size_t ls, lp;
+ const char *s = luaL_checklstring(L, 1, &ls);
+ const char *p = luaL_checklstring(L, 2, &lp);
+ size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1;
+ if (init > ls) { /* start after string's end? */
+ luaL_pushfail(L); /* cannot find anything */
+ return 1;
+ }
+ /* explicit request or no special characters? */
+ if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) {
+ /* do a plain search */
+ const char *s2 = lmemfind(s + init, ls - init, p, lp);
+ if (s2) {
+ lua_pushinteger(L, (s2 - s) + 1);
+ lua_pushinteger(L, (s2 - s) + lp);
+ return 2;
+ }
+ }
+ else {
+ MatchState ms;
+ const char *s1 = s + init;
+ int anchor = (*p == '^');
+ if (anchor) {
+ p++; lp--; /* skip anchor character */
+ }
+ prepstate(&ms, L, s, ls, p, lp);
+ do {
+ const char *res;
+ reprepstate(&ms);
+ if ((res=match(&ms, s1, p)) != NULL) {
+ if (find) {
+ lua_pushinteger(L, (s1 - s) + 1); /* start */
+ lua_pushinteger(L, res - s); /* end */
+ return push_captures(&ms, NULL, 0) + 2;
+ }
+ else
+ return push_captures(&ms, s1, res);
+ }
+ } while (s1++ < ms.src_end && !anchor);
+ }
+ luaL_pushfail(L); /* not found */
+ return 1;
+}
+
+
+static int str_find (lua_State *L) {
+ return str_find_aux(L, 1);
+}
+
+
+static int str_match (lua_State *L) {
+ return str_find_aux(L, 0);
+}
+
+
+/* state for 'gmatch' */
+typedef struct GMatchState {
+ const char *src; /* current position */
+ const char *p; /* pattern */
+ const char *lastmatch; /* end of last match */
+ MatchState ms; /* match state */
+} GMatchState;
+
+
+static int gmatch_aux (lua_State *L) {
+ GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3));
+ const char *src;
+ gm->ms.L = L;
+ for (src = gm->src; src <= gm->ms.src_end; src++) {
+ const char *e;
+ reprepstate(&gm->ms);
+ if ((e = match(&gm->ms, src, gm->p)) != NULL && e != gm->lastmatch) {
+ gm->src = gm->lastmatch = e;
+ return push_captures(&gm->ms, src, e);
+ }
+ }
+ return 0; /* not found */
+}
+
+
+static int gmatch (lua_State *L) {
+ size_t ls, lp;
+ const char *s = luaL_checklstring(L, 1, &ls);
+ const char *p = luaL_checklstring(L, 2, &lp);
+ size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1;
+ GMatchState *gm;
+ lua_settop(L, 2); /* keep strings on closure to avoid being collected */
+ gm = (GMatchState *)lua_newuserdatauv(L, sizeof(GMatchState), 0);
+ if (init > ls) /* start after string's end? */
+ init = ls + 1; /* avoid overflows in 's + init' */
+ prepstate(&gm->ms, L, s, ls, p, lp);
+ gm->src = s + init; gm->p = p; gm->lastmatch = NULL;
+ lua_pushcclosure(L, gmatch_aux, 3);
+ return 1;
+}
+
+
+static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e) {
+ size_t l;
+ lua_State *L = ms->L;
+ const char *news = lua_tolstring(L, 3, &l);
+ const char *p;
+ while ((p = (char *)memchr(news, L_ESC, l)) != NULL) {
+ luaL_addlstring(b, news, p - news);
+ p++; /* skip ESC */
+ if (*p == L_ESC) /* '%%' */
+ luaL_addchar(b, *p);
+ else if (*p == '0') /* '%0' */
+ luaL_addlstring(b, s, e - s);
+ else if (isdigit(uchar(*p))) { /* '%n' */
+ const char *cap;
+ ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap);
+ if (resl == CAP_POSITION)
+ luaL_addvalue(b); /* add position to accumulated result */
+ else
+ luaL_addlstring(b, cap, resl);
+ }
+ else
+ luaL_error(L, "invalid use of '%c' in replacement string", L_ESC);
+ l -= p + 1 - news;
+ news = p + 1;
+ }
+ luaL_addlstring(b, news, l);
+}
+
+
+/*
+** Add the replacement value to the string buffer 'b'.
+** Return true if the original string was changed. (Function calls and
+** table indexing resulting in nil or false do not change the subject.)
+*/
+static int add_value (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e, int tr) {
+ lua_State *L = ms->L;
+ switch (tr) {
+ case LUA_TFUNCTION: { /* call the function */
+ int n;
+ lua_pushvalue(L, 3); /* push the function */
+ n = push_captures(ms, s, e); /* all captures as arguments */
+ lua_call(L, n, 1); /* call it */
+ break;
+ }
+ case LUA_TTABLE: { /* index the table */
+ push_onecapture(ms, 0, s, e); /* first capture is the index */
+ lua_gettable(L, 3);
+ break;
+ }
+ default: { /* LUA_TNUMBER or LUA_TSTRING */
+ add_s(ms, b, s, e); /* add value to the buffer */
+ return 1; /* something changed */
+ }
+ }
+ if (!lua_toboolean(L, -1)) { /* nil or false? */
+ lua_pop(L, 1); /* remove value */
+ luaL_addlstring(b, s, e - s); /* keep original text */
+ return 0; /* no changes */
+ }
+ else if (l_unlikely(!lua_isstring(L, -1)))
+ return luaL_error(L, "invalid replacement value (a %s)",
+ luaL_typename(L, -1));
+ else {
+ luaL_addvalue(b); /* add result to accumulator */
+ return 1; /* something changed */
+ }
+}
+
+
+static int str_gsub (lua_State *L) {
+ size_t srcl, lp;
+ const char *src = luaL_checklstring(L, 1, &srcl); /* subject */
+ const char *p = luaL_checklstring(L, 2, &lp); /* pattern */
+ const char *lastmatch = NULL; /* end of last match */
+ int tr = lua_type(L, 3); /* replacement type */
+ lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */
+ int anchor = (*p == '^');
+ lua_Integer n = 0; /* replacement count */
+ int changed = 0; /* change flag */
+ MatchState ms;
+ luaL_Buffer b;
+ luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
+ tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
+ "string/function/table");
+ luaL_buffinit(L, &b);
+ if (anchor) {
+ p++; lp--; /* skip anchor character */
+ }
+ prepstate(&ms, L, src, srcl, p, lp);
+ while (n < max_s) {
+ const char *e;
+ reprepstate(&ms); /* (re)prepare state for new match */
+ if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */
+ n++;
+ changed = add_value(&ms, &b, src, e, tr) | changed;
+ src = lastmatch = e;
+ }
+ else if (src < ms.src_end) /* otherwise, skip one character */
+ luaL_addchar(&b, *src++);
+ else break; /* end of subject */
+ if (anchor) break;
+ }
+ if (!changed) /* no changes? */
+ lua_pushvalue(L, 1); /* return original string */
+ else { /* something changed */
+ luaL_addlstring(&b, src, ms.src_end-src);
+ luaL_pushresult(&b); /* create and return new string */
+ }
+ lua_pushinteger(L, n); /* number of substitutions */
+ return 2;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** STRING FORMAT
+** =======================================================
+*/
+
+#if !defined(lua_number2strx) /* { */
+
+/*
+** Hexadecimal floating-point formatter
+*/
+
+#define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char))
+
+
+/*
+** Number of bits that goes into the first digit. It can be any value
+** between 1 and 4; the following definition tries to align the number
+** to nibble boundaries by making what is left after that first digit a
+** multiple of 4.
+*/
+#define L_NBFD ((l_floatatt(MANT_DIG) - 1)%4 + 1)
+
+
+/*
+** Add integer part of 'x' to buffer and return new 'x'
+*/
+static lua_Number adddigit (char *buff, int n, lua_Number x) {
+ lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */
+ int d = (int)dd;
+ buff[n] = (d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */
+ return x - dd; /* return what is left */
+}
+
+
+static int num2straux (char *buff, int sz, lua_Number x) {
+ /* if 'inf' or 'NaN', format it like '%g' */
+ if (x != x || x == (lua_Number)HUGE_VAL || x == -(lua_Number)HUGE_VAL)
+ return l_sprintf(buff, sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)x);
+ else if (x == 0) { /* can be -0... */
+ /* create "0" or "-0" followed by exponent */
+ return l_sprintf(buff, sz, LUA_NUMBER_FMT "x0p+0", (LUAI_UACNUMBER)x);
+ }
+ else {
+ int e;
+ lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */
+ int n = 0; /* character count */
+ if (m < 0) { /* is number negative? */
+ buff[n++] = '-'; /* add sign */
+ m = -m; /* make it positive */
+ }
+ buff[n++] = '0'; buff[n++] = 'x'; /* add "0x" */
+ m = adddigit(buff, n++, m * (1 << L_NBFD)); /* add first digit */
+ e -= L_NBFD; /* this digit goes before the radix point */
+ if (m > 0) { /* more digits? */
+ buff[n++] = lua_getlocaledecpoint(); /* add radix point */
+ do { /* add as many digits as needed */
+ m = adddigit(buff, n++, m * 16);
+ } while (m > 0);
+ }
+ n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */
+ lua_assert(n < sz);
+ return n;
+ }
+}
+
+
+static int lua_number2strx (lua_State *L, char *buff, int sz,
+ const char *fmt, lua_Number x) {
+ int n = num2straux(buff, sz, x);
+ if (fmt[SIZELENMOD] == 'A') {
+ int i;
+ for (i = 0; i < n; i++)
+ buff[i] = toupper(uchar(buff[i]));
+ }
+ else if (l_unlikely(fmt[SIZELENMOD] != 'a'))
+ return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented");
+ return n;
+}
+
+#endif /* } */
+
+
+/*
+** Maximum size for items formatted with '%f'. This size is produced
+** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.',
+** and '\0') + number of decimal digits to represent maxfloat (which
+** is maximum exponent + 1). (99+3+1, adding some extra, 110)
+*/
+#define MAX_ITEMF (110 + l_floatatt(MAX_10_EXP))
+
+
+/*
+** All formats except '%f' do not need that large limit. The other
+** float formats use exponents, so that they fit in the 99 limit for
+** significant digits; 's' for large strings and 'q' add items directly
+** to the buffer; all integer formats also fit in the 99 limit. The
+** worst case are floats: they may need 99 significant digits, plus
+** '0x', '-', '.', 'e+XXXX', and '\0'. Adding some extra, 120.
+*/
+#define MAX_ITEM 120
+
+
+/* valid flags in a format specification */
+#if !defined(L_FMTFLAGSF)
+
+/* valid flags for a, A, e, E, f, F, g, and G conversions */
+#define L_FMTFLAGSF "-+#0 "
+
+/* valid flags for o, x, and X conversions */
+#define L_FMTFLAGSX "-#0"
+
+/* valid flags for d and i conversions */
+#define L_FMTFLAGSI "-+0 "
+
+/* valid flags for u conversions */
+#define L_FMTFLAGSU "-0"
+
+/* valid flags for c, p, and s conversions */
+#define L_FMTFLAGSC "-"
+
+#endif
+
+
+/*
+** Maximum size of each format specification (such as "%-099.99d"):
+** Initial '%', flags (up to 5), width (2), period, precision (2),
+** length modifier (8), conversion specifier, and final '\0', plus some
+** extra.
+*/
+#define MAX_FORMAT 32
+
+
+static void addquoted (luaL_Buffer *b, const char *s, size_t len) {
+ luaL_addchar(b, '"');
+ while (len--) {
+ if (*s == '"' || *s == '\\' || *s == '\n') {
+ luaL_addchar(b, '\\');
+ luaL_addchar(b, *s);
+ }
+ else if (iscntrl(uchar(*s))) {
+ char buff[10];
+ if (!isdigit(uchar(*(s+1))))
+ l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s));
+ else
+ l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s));
+ luaL_addstring(b, buff);
+ }
+ else
+ luaL_addchar(b, *s);
+ s++;
+ }
+ luaL_addchar(b, '"');
+}
+
+
+/*
+** Serialize a floating-point number in such a way that it can be
+** scanned back by Lua. Use hexadecimal format for "common" numbers
+** (to preserve precision); inf, -inf, and NaN are handled separately.
+** (NaN cannot be expressed as a numeral, so we write '(0/0)' for it.)
+*/
+static int quotefloat (lua_State *L, char *buff, lua_Number n) {
+ const char *s; /* for the fixed representations */
+ if (n == (lua_Number)HUGE_VAL) /* inf? */
+ s = "1e9999";
+ else if (n == -(lua_Number)HUGE_VAL) /* -inf? */
+ s = "-1e9999";
+ else if (n != n) /* NaN? */
+ s = "(0/0)";
+ else { /* format number as hexadecimal */
+ int nb = lua_number2strx(L, buff, MAX_ITEM,
+ "%" LUA_NUMBER_FRMLEN "a", n);
+ /* ensures that 'buff' string uses a dot as the radix character */
+ if (memchr(buff, '.', nb) == NULL) { /* no dot? */
+ char point = lua_getlocaledecpoint(); /* try locale point */
+ char *ppoint = (char *)memchr(buff, point, nb);
+ if (ppoint) *ppoint = '.'; /* change it to a dot */
+ }
+ return nb;
+ }
+ /* for the fixed representations */
+ return l_sprintf(buff, MAX_ITEM, "%s", s);
+}
+
+
+static void addliteral (lua_State *L, luaL_Buffer *b, int arg) {
+ switch (lua_type(L, arg)) {
+ case LUA_TSTRING: {
+ size_t len;
+ const char *s = lua_tolstring(L, arg, &len);
+ addquoted(b, s, len);
+ break;
+ }
+ case LUA_TNUMBER: {
+ char *buff = luaL_prepbuffsize(b, MAX_ITEM);
+ int nb;
+ if (!lua_isinteger(L, arg)) /* float? */
+ nb = quotefloat(L, buff, lua_tonumber(L, arg));
+ else { /* integers */
+ lua_Integer n = lua_tointeger(L, arg);
+ const char *format = (n == LUA_MININTEGER) /* corner case? */
+ ? "0x%" LUA_INTEGER_FRMLEN "x" /* use hex */
+ : LUA_INTEGER_FMT; /* else use default format */
+ nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n);
+ }
+ luaL_addsize(b, nb);
+ break;
+ }
+ case LUA_TNIL: case LUA_TBOOLEAN: {
+ luaL_tolstring(L, arg, NULL);
+ luaL_addvalue(b);
+ break;
+ }
+ default: {
+ luaL_argerror(L, arg, "value has no literal form");
+ }
+ }
+}
+
+
+static const char *get2digits (const char *s) {
+ if (isdigit(uchar(*s))) {
+ s++;
+ if (isdigit(uchar(*s))) s++; /* (2 digits at most) */
+ }
+ return s;
+}
+
+
+/*
+** Check whether a conversion specification is valid. When called,
+** first character in 'form' must be '%' and last character must
+** be a valid conversion specifier. 'flags' are the accepted flags;
+** 'precision' signals whether to accept a precision.
+*/
+static void checkformat (lua_State *L, const char *form, const char *flags,
+ int precision) {
+ const char *spec = form + 1; /* skip '%' */
+ spec += strspn(spec, flags); /* skip flags */
+ if (*spec != '0') { /* a width cannot start with '0' */
+ spec = get2digits(spec); /* skip width */
+ if (*spec == '.' && precision) {
+ spec++;
+ spec = get2digits(spec); /* skip precision */
+ }
+ }
+ if (!isalpha(uchar(*spec))) /* did not go to the end? */
+ luaL_error(L, "invalid conversion specification: '%s'", form);
+}
+
+
+/*
+** Get a conversion specification and copy it to 'form'.
+** Return the address of its last character.
+*/
+static const char *getformat (lua_State *L, const char *strfrmt,
+ char *form) {
+ /* spans flags, width, and precision ('0' is included as a flag) */
+ size_t len = strspn(strfrmt, L_FMTFLAGSF "123456789.");
+ len++; /* adds following character (should be the specifier) */
+ /* still needs space for '%', '\0', plus a length modifier */
+ if (len >= MAX_FORMAT - 10)
+ luaL_error(L, "invalid format (too long)");
+ *(form++) = '%';
+ memcpy(form, strfrmt, len * sizeof(char));
+ *(form + len) = '\0';
+ return strfrmt + len - 1;
+}
+
+
+/*
+** add length modifier into formats
+*/
+static void addlenmod (char *form, const char *lenmod) {
+ size_t l = strlen(form);
+ size_t lm = strlen(lenmod);
+ char spec = form[l - 1];
+ strcpy(form + l - 1, lenmod);
+ form[l + lm - 1] = spec;
+ form[l + lm] = '\0';
+}
+
+
+static int str_format (lua_State *L) {
+ int top = lua_gettop(L);
+ int arg = 1;
+ size_t sfl;
+ const char *strfrmt = luaL_checklstring(L, arg, &sfl);
+ const char *strfrmt_end = strfrmt+sfl;
+ const char *flags;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (strfrmt < strfrmt_end) {
+ if (*strfrmt != L_ESC)
+ luaL_addchar(&b, *strfrmt++);
+ else if (*++strfrmt == L_ESC)
+ luaL_addchar(&b, *strfrmt++); /* %% */
+ else { /* format item */
+ char form[MAX_FORMAT]; /* to store the format ('%...') */
+ int maxitem = MAX_ITEM; /* maximum length for the result */
+ char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */
+ int nb = 0; /* number of bytes in result */
+ if (++arg > top)
+ return luaL_argerror(L, arg, "no value");
+ strfrmt = getformat(L, strfrmt, form);
+ switch (*strfrmt++) {
+ case 'c': {
+ checkformat(L, form, L_FMTFLAGSC, 0);
+ nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg));
+ break;
+ }
+ case 'd': case 'i':
+ flags = L_FMTFLAGSI;
+ goto intcase;
+ case 'u':
+ flags = L_FMTFLAGSU;
+ goto intcase;
+ case 'o': case 'x': case 'X':
+ flags = L_FMTFLAGSX;
+ intcase: {
+ lua_Integer n = luaL_checkinteger(L, arg);
+ checkformat(L, form, flags, 1);
+ addlenmod(form, LUA_INTEGER_FRMLEN);
+ nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n);
+ break;
+ }
+ case 'a': case 'A':
+ checkformat(L, form, L_FMTFLAGSF, 1);
+ addlenmod(form, LUA_NUMBER_FRMLEN);
+ nb = lua_number2strx(L, buff, maxitem, form,
+ luaL_checknumber(L, arg));
+ break;
+ case 'f':
+ maxitem = MAX_ITEMF; /* extra space for '%f' */
+ buff = luaL_prepbuffsize(&b, maxitem);
+ /* FALLTHROUGH */
+ case 'e': case 'E': case 'g': case 'G': {
+ lua_Number n = luaL_checknumber(L, arg);
+ checkformat(L, form, L_FMTFLAGSF, 1);
+ addlenmod(form, LUA_NUMBER_FRMLEN);
+ nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
+ break;
+ }
+ case 'p': {
+ const void *p = lua_topointer(L, arg);
+ checkformat(L, form, L_FMTFLAGSC, 0);
+ if (p == NULL) { /* avoid calling 'printf' with argument NULL */
+ p = "(null)"; /* result */
+ form[strlen(form) - 1] = 's'; /* format it as a string */
+ }
+ nb = l_sprintf(buff, maxitem, form, p);
+ break;
+ }
+ case 'q': {
+ if (form[2] != '\0') /* modifiers? */
+ return luaL_error(L, "specifier '%%q' cannot have modifiers");
+ addliteral(L, &b, arg);
+ break;
+ }
+ case 's': {
+ size_t l;
+ const char *s = luaL_tolstring(L, arg, &l);
+ if (form[2] == '\0') /* no modifiers? */
+ luaL_addvalue(&b); /* keep entire string */
+ else {
+ luaL_argcheck(L, l == strlen(s), arg, "string contains zeros");
+ checkformat(L, form, L_FMTFLAGSC, 1);
+ if (strchr(form, '.') == NULL && l >= 100) {
+ /* no precision and string is too long to be formatted */
+ luaL_addvalue(&b); /* keep entire string */
+ }
+ else { /* format the string into 'buff' */
+ nb = l_sprintf(buff, maxitem, form, s);
+ lua_pop(L, 1); /* remove result from 'luaL_tolstring' */
+ }
+ }
+ break;
+ }
+ default: { /* also treat cases 'pnLlh' */
+ return luaL_error(L, "invalid conversion '%s' to 'format'", form);
+ }
+ }
+ lua_assert(nb < maxitem);
+ luaL_addsize(&b, nb);
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** PACK/UNPACK
+** =======================================================
+*/
+
+
+/* value used for padding */
+#if !defined(LUAL_PACKPADBYTE)
+#define LUAL_PACKPADBYTE 0x00
+#endif
+
+/* maximum size for the binary representation of an integer */
+#define MAXINTSIZE 16
+
+/* number of bits in a character */
+#define NB CHAR_BIT
+
+/* mask for one character (NB 1's) */
+#define MC ((1 << NB) - 1)
+
+/* size of a lua_Integer */
+#define SZINT ((int)sizeof(lua_Integer))
+
+
+/* dummy union to get native endianness */
+static const union {
+ int dummy;
+ char little; /* true iff machine is little endian */
+} nativeendian = {1};
+
+
+/*
+** information to pack/unpack stuff
+*/
+typedef struct Header {
+ lua_State *L;
+ int islittle;
+ int maxalign;
+} Header;
+
+
+/*
+** options for pack/unpack
+*/
+typedef enum KOption {
+ Kint, /* signed integers */
+ Kuint, /* unsigned integers */
+ Kfloat, /* single-precision floating-point numbers */
+ Knumber, /* Lua "native" floating-point numbers */
+ Kdouble, /* double-precision floating-point numbers */
+ Kchar, /* fixed-length strings */
+ Kstring, /* strings with prefixed length */
+ Kzstr, /* zero-terminated strings */
+ Kpadding, /* padding */
+ Kpaddalign, /* padding for alignment */
+ Knop /* no-op (configuration or spaces) */
+} KOption;
+
+
+/*
+** Read an integer numeral from string 'fmt' or return 'df' if
+** there is no numeral
+*/
+static int digit (int c) { return '0' <= c && c <= '9'; }
+
+static int getnum (const char **fmt, int df) {
+ if (!digit(**fmt)) /* no number? */
+ return df; /* return default value */
+ else {
+ int a = 0;
+ do {
+ a = a*10 + (*((*fmt)++) - '0');
+ } while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10);
+ return a;
+ }
+}
+
+
+/*
+** Read an integer numeral and raises an error if it is larger
+** than the maximum size for integers.
+*/
+static int getnumlimit (Header *h, const char **fmt, int df) {
+ int sz = getnum(fmt, df);
+ if (l_unlikely(sz > MAXINTSIZE || sz <= 0))
+ return luaL_error(h->L, "integral size (%d) out of limits [1,%d]",
+ sz, MAXINTSIZE);
+ return sz;
+}
+
+
+/*
+** Initialize Header
+*/
+static void initheader (lua_State *L, Header *h) {
+ h->L = L;
+ h->islittle = nativeendian.little;
+ h->maxalign = 1;
+}
+
+
+/*
+** Read and classify next option. 'size' is filled with option's size.
+*/
+static KOption getoption (Header *h, const char **fmt, int *size) {
+ /* dummy structure to get native alignment requirements */
+ struct cD { char c; union { LUAI_MAXALIGN; } u; };
+ int opt = *((*fmt)++);
+ *size = 0; /* default */
+ switch (opt) {
+ case 'b': *size = sizeof(char); return Kint;
+ case 'B': *size = sizeof(char); return Kuint;
+ case 'h': *size = sizeof(short); return Kint;
+ case 'H': *size = sizeof(short); return Kuint;
+ case 'l': *size = sizeof(long); return Kint;
+ case 'L': *size = sizeof(long); return Kuint;
+ case 'j': *size = sizeof(lua_Integer); return Kint;
+ case 'J': *size = sizeof(lua_Integer); return Kuint;
+ case 'T': *size = sizeof(size_t); return Kuint;
+ case 'f': *size = sizeof(float); return Kfloat;
+ case 'n': *size = sizeof(lua_Number); return Knumber;
+ case 'd': *size = sizeof(double); return Kdouble;
+ case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint;
+ case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint;
+ case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring;
+ case 'c':
+ *size = getnum(fmt, -1);
+ if (l_unlikely(*size == -1))
+ luaL_error(h->L, "missing size for format option 'c'");
+ return Kchar;
+ case 'z': return Kzstr;
+ case 'x': *size = 1; return Kpadding;
+ case 'X': return Kpaddalign;
+ case ' ': break;
+ case '<': h->islittle = 1; break;
+ case '>': h->islittle = 0; break;
+ case '=': h->islittle = nativeendian.little; break;
+ case '!': {
+ const int maxalign = offsetof(struct cD, u);
+ h->maxalign = getnumlimit(h, fmt, maxalign);
+ break;
+ }
+ default: luaL_error(h->L, "invalid format option '%c'", opt);
+ }
+ return Knop;
+}
+
+
+/*
+** Read, classify, and fill other details about the next option.
+** 'psize' is filled with option's size, 'notoalign' with its
+** alignment requirements.
+** Local variable 'size' gets the size to be aligned. (Kpadal option
+** always gets its full alignment, other options are limited by
+** the maximum alignment ('maxalign'). Kchar option needs no alignment
+** despite its size.
+*/
+static KOption getdetails (Header *h, size_t totalsize,
+ const char **fmt, int *psize, int *ntoalign) {
+ KOption opt = getoption(h, fmt, psize);
+ int align = *psize; /* usually, alignment follows size */
+ if (opt == Kpaddalign) { /* 'X' gets alignment from following option */
+ if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0)
+ luaL_argerror(h->L, 1, "invalid next option for option 'X'");
+ }
+ if (align <= 1 || opt == Kchar) /* need no alignment? */
+ *ntoalign = 0;
+ else {
+ if (align > h->maxalign) /* enforce maximum alignment */
+ align = h->maxalign;
+ if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */
+ luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
+ *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
+ }
+ return opt;
+}
+
+
+/*
+** Pack integer 'n' with 'size' bytes and 'islittle' endianness.
+** The final 'if' handles the case when 'size' is larger than
+** the size of a Lua integer, correcting the extra sign-extension
+** bytes if necessary (by default they would be zeros).
+*/
+static void packint (luaL_Buffer *b, lua_Unsigned n,
+ int islittle, int size, int neg) {
+ char *buff = luaL_prepbuffsize(b, size);
+ int i;
+ buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */
+ for (i = 1; i < size; i++) {
+ n >>= NB;
+ buff[islittle ? i : size - 1 - i] = (char)(n & MC);
+ }
+ if (neg && size > SZINT) { /* negative number need sign extension? */
+ for (i = SZINT; i < size; i++) /* correct extra bytes */
+ buff[islittle ? i : size - 1 - i] = (char)MC;
+ }
+ luaL_addsize(b, size); /* add result to buffer */
+}
+
+
+/*
+** Copy 'size' bytes from 'src' to 'dest', correcting endianness if
+** given 'islittle' is different from native endianness.
+*/
+static void copywithendian (char *dest, const char *src,
+ int size, int islittle) {
+ if (islittle == nativeendian.little)
+ memcpy(dest, src, size);
+ else {
+ dest += size - 1;
+ while (size-- != 0)
+ *(dest--) = *(src++);
+ }
+}
+
+
+static int str_pack (lua_State *L) {
+ luaL_Buffer b;
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1); /* format string */
+ int arg = 1; /* current argument to pack */
+ size_t totalsize = 0; /* accumulate total size of result */
+ initheader(L, &h);
+ lua_pushnil(L); /* mark to separate arguments from string buffer */
+ luaL_buffinit(L, &b);
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
+ totalsize += ntoalign + size;
+ while (ntoalign-- > 0)
+ luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */
+ arg++;
+ switch (opt) {
+ case Kint: { /* signed integers */
+ lua_Integer n = luaL_checkinteger(L, arg);
+ if (size < SZINT) { /* need overflow check? */
+ lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1);
+ luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow");
+ }
+ packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0));
+ break;
+ }
+ case Kuint: { /* unsigned integers */
+ lua_Integer n = luaL_checkinteger(L, arg);
+ if (size < SZINT) /* need overflow check? */
+ luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)),
+ arg, "unsigned overflow");
+ packint(&b, (lua_Unsigned)n, h.islittle, size, 0);
+ break;
+ }
+ case Kfloat: { /* C float */
+ float f = (float)luaL_checknumber(L, arg); /* get argument */
+ char *buff = luaL_prepbuffsize(&b, sizeof(f));
+ /* move 'f' to final result, correcting endianness if needed */
+ copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
+ luaL_addsize(&b, size);
+ break;
+ }
+ case Knumber: { /* Lua float */
+ lua_Number f = luaL_checknumber(L, arg); /* get argument */
+ char *buff = luaL_prepbuffsize(&b, sizeof(f));
+ /* move 'f' to final result, correcting endianness if needed */
+ copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
+ luaL_addsize(&b, size);
+ break;
+ }
+ case Kdouble: { /* C double */
+ double f = (double)luaL_checknumber(L, arg); /* get argument */
+ char *buff = luaL_prepbuffsize(&b, sizeof(f));
+ /* move 'f' to final result, correcting endianness if needed */
+ copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
+ luaL_addsize(&b, size);
+ break;
+ }
+ case Kchar: { /* fixed-size string */
+ size_t len;
+ const char *s = luaL_checklstring(L, arg, &len);
+ luaL_argcheck(L, len <= (size_t)size, arg,
+ "string longer than given size");
+ luaL_addlstring(&b, s, len); /* add string */
+ while (len++ < (size_t)size) /* pad extra space */
+ luaL_addchar(&b, LUAL_PACKPADBYTE);
+ break;
+ }
+ case Kstring: { /* strings with length count */
+ size_t len;
+ const char *s = luaL_checklstring(L, arg, &len);
+ luaL_argcheck(L, size >= (int)sizeof(size_t) ||
+ len < ((size_t)1 << (size * NB)),
+ arg, "string length does not fit in given size");
+ packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */
+ luaL_addlstring(&b, s, len);
+ totalsize += len;
+ break;
+ }
+ case Kzstr: { /* zero-terminated string */
+ size_t len;
+ const char *s = luaL_checklstring(L, arg, &len);
+ luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros");
+ luaL_addlstring(&b, s, len);
+ luaL_addchar(&b, '\0'); /* add zero at the end */
+ totalsize += len + 1;
+ break;
+ }
+ case Kpadding: luaL_addchar(&b, LUAL_PACKPADBYTE); /* FALLTHROUGH */
+ case Kpaddalign: case Knop:
+ arg--; /* undo increment */
+ break;
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_packsize (lua_State *L) {
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1); /* format string */
+ size_t totalsize = 0; /* accumulate total size of result */
+ initheader(L, &h);
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
+ luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1,
+ "variable-length format");
+ size += ntoalign; /* total space used by option */
+ luaL_argcheck(L, totalsize <= MAXSIZE - size, 1,
+ "format result too large");
+ totalsize += size;
+ }
+ lua_pushinteger(L, (lua_Integer)totalsize);
+ return 1;
+}
+
+
+/*
+** Unpack an integer with 'size' bytes and 'islittle' endianness.
+** If size is smaller than the size of a Lua integer and integer
+** is signed, must do sign extension (propagating the sign to the
+** higher bits); if size is larger than the size of a Lua integer,
+** it must check the unread bytes to see whether they do not cause an
+** overflow.
+*/
+static lua_Integer unpackint (lua_State *L, const char *str,
+ int islittle, int size, int issigned) {
+ lua_Unsigned res = 0;
+ int i;
+ int limit = (size <= SZINT) ? size : SZINT;
+ for (i = limit - 1; i >= 0; i--) {
+ res <<= NB;
+ res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i];
+ }
+ if (size < SZINT) { /* real size smaller than lua_Integer? */
+ if (issigned) { /* needs sign extension? */
+ lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1);
+ res = ((res ^ mask) - mask); /* do sign extension */
+ }
+ }
+ else if (size > SZINT) { /* must check unread bytes */
+ int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC;
+ for (i = limit; i < size; i++) {
+ if (l_unlikely((unsigned char)str[islittle ? i : size - 1 - i] != mask))
+ luaL_error(L, "%d-byte integer does not fit into Lua Integer", size);
+ }
+ }
+ return (lua_Integer)res;
+}
+
+
+static int str_unpack (lua_State *L) {
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1);
+ size_t ld;
+ const char *data = luaL_checklstring(L, 2, &ld);
+ size_t pos = posrelatI(luaL_optinteger(L, 3, 1), ld) - 1;
+ int n = 0; /* number of results */
+ luaL_argcheck(L, pos <= ld, 3, "initial position out of string");
+ initheader(L, &h);
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign);
+ luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2,
+ "data string too short");
+ pos += ntoalign; /* skip alignment */
+ /* stack space for item + next position */
+ luaL_checkstack(L, 2, "too many results");
+ n++;
+ switch (opt) {
+ case Kint:
+ case Kuint: {
+ lua_Integer res = unpackint(L, data + pos, h.islittle, size,
+ (opt == Kint));
+ lua_pushinteger(L, res);
+ break;
+ }
+ case Kfloat: {
+ float f;
+ copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
+ lua_pushnumber(L, (lua_Number)f);
+ break;
+ }
+ case Knumber: {
+ lua_Number f;
+ copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
+ lua_pushnumber(L, f);
+ break;
+ }
+ case Kdouble: {
+ double f;
+ copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
+ lua_pushnumber(L, (lua_Number)f);
+ break;
+ }
+ case Kchar: {
+ lua_pushlstring(L, data + pos, size);
+ break;
+ }
+ case Kstring: {
+ size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0);
+ luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short");
+ lua_pushlstring(L, data + pos + size, len);
+ pos += len; /* skip string */
+ break;
+ }
+ case Kzstr: {
+ size_t len = strlen(data + pos);
+ luaL_argcheck(L, pos + len < ld, 2,
+ "unfinished string for format 'z'");
+ lua_pushlstring(L, data + pos, len);
+ pos += len + 1; /* skip string plus final '\0' */
+ break;
+ }
+ case Kpaddalign: case Kpadding: case Knop:
+ n--; /* undo increment */
+ break;
+ }
+ pos += size;
+ }
+ lua_pushinteger(L, pos + 1); /* next position */
+ return n + 1;
+}
+
+/* }====================================================== */
+
+
+static const luaL_Reg strlib[] = {
+ {"byte", str_byte},
+ {"char", str_char},
+ {"dump", str_dump},
+ {"find", str_find},
+ {"format", str_format},
+ {"gmatch", gmatch},
+ {"gsub", str_gsub},
+ {"len", str_len},
+ {"lower", str_lower},
+ {"match", str_match},
+ {"rep", str_rep},
+ {"reverse", str_reverse},
+ {"sub", str_sub},
+ {"upper", str_upper},
+ {"pack", str_pack},
+ {"packsize", str_packsize},
+ {"unpack", str_unpack},
+ {NULL, NULL}
+};
+
+
+static void createmetatable (lua_State *L) {
+ /* table to be metatable for strings */
+ luaL_newlibtable(L, stringmetamethods);
+ luaL_setfuncs(L, stringmetamethods, 0);
+ lua_pushliteral(L, ""); /* dummy string */
+ lua_pushvalue(L, -2); /* copy table */
+ lua_setmetatable(L, -2); /* set table as metatable for strings */
+ lua_pop(L, 1); /* pop dummy string */
+ lua_pushvalue(L, -2); /* get string library */
+ lua_setfield(L, -2, "__index"); /* metatable.__index = string */
+ lua_pop(L, 1); /* pop metatable */
+}
+
+
+/*
+** Open string library
+*/
+LUAMOD_API int luaopen_string (lua_State *L) {
+ luaL_newlib(L, strlib);
+ createmetatable(L);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/ltable.c b/src/libs/3rdparty/lua/src/ltable.c
new file mode 100644
index 0000000000..3c690c5f17
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ltable.c
@@ -0,0 +1,980 @@
+/*
+** $Id: ltable.c $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+#define ltable_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+/*
+** Implementation of tables (aka arrays, objects, or hash tables).
+** Tables keep its elements in two parts: an array part and a hash part.
+** Non-negative integer keys are all candidates to be kept in the array
+** part. The actual size of the array is the largest 'n' such that
+** more than half the slots between 1 and n are in use.
+** Hash uses a mix of chained scatter table with Brent's variation.
+** A main invariant of these tables is that, if an element is not
+** in its main position (i.e. the 'original' position that its hash gives
+** to it), then the colliding element is in its own main position.
+** Hence even when the load factor reaches 100%, performance remains good.
+*/
+
+#include <math.h>
+#include <limits.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "lvm.h"
+
+
+/*
+** MAXABITS is the largest integer such that MAXASIZE fits in an
+** unsigned int.
+*/
+#define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1)
+
+
+/*
+** MAXASIZE is the maximum size of the array part. It is the minimum
+** between 2^MAXABITS and the maximum size that, measured in bytes,
+** fits in a 'size_t'.
+*/
+#define MAXASIZE luaM_limitN(1u << MAXABITS, TValue)
+
+/*
+** MAXHBITS is the largest integer such that 2^MAXHBITS fits in a
+** signed int.
+*/
+#define MAXHBITS (MAXABITS - 1)
+
+
+/*
+** MAXHSIZE is the maximum size of the hash part. It is the minimum
+** between 2^MAXHBITS and the maximum size such that, measured in bytes,
+** it fits in a 'size_t'.
+*/
+#define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node)
+
+
+/*
+** When the original hash value is good, hashing by a power of 2
+** avoids the cost of '%'.
+*/
+#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
+
+/*
+** for other types, it is better to avoid modulo by power of 2, as
+** they can have many 2 factors.
+*/
+#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
+
+
+#define hashstr(t,str) hashpow2(t, (str)->hash)
+#define hashboolean(t,p) hashpow2(t, p)
+
+
+#define hashpointer(t,p) hashmod(t, point2uint(p))
+
+
+#define dummynode (&dummynode_)
+
+static const Node dummynode_ = {
+ {{NULL}, LUA_VEMPTY, /* value's value and type */
+ LUA_VNIL, 0, {NULL}} /* key type, next, and key value */
+};
+
+
+static const TValue absentkey = {ABSTKEYCONSTANT};
+
+
+/*
+** Hash for integers. To allow a good hash, use the remainder operator
+** ('%'). If integer fits as a non-negative int, compute an int
+** remainder, which is faster. Otherwise, use an unsigned-integer
+** remainder, which uses all bits and ensures a non-negative result.
+*/
+static Node *hashint (const Table *t, lua_Integer i) {
+ lua_Unsigned ui = l_castS2U(i);
+ if (ui <= cast_uint(INT_MAX))
+ return hashmod(t, cast_int(ui));
+ else
+ return hashmod(t, ui);
+}
+
+
+/*
+** Hash for floating-point numbers.
+** The main computation should be just
+** n = frexp(n, &i); return (n * INT_MAX) + i
+** but there are some numerical subtleties.
+** In a two-complement representation, INT_MAX does not has an exact
+** representation as a float, but INT_MIN does; because the absolute
+** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the
+** absolute value of the product 'frexp * -INT_MIN' is smaller or equal
+** to INT_MAX. Next, the use of 'unsigned int' avoids overflows when
+** adding 'i'; the use of '~u' (instead of '-u') avoids problems with
+** INT_MIN.
+*/
+#if !defined(l_hashfloat)
+static int l_hashfloat (lua_Number n) {
+ int i;
+ lua_Integer ni;
+ n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN);
+ if (!lua_numbertointeger(n, &ni)) { /* is 'n' inf/-inf/NaN? */
+ lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == cast_num(HUGE_VAL));
+ return 0;
+ }
+ else { /* normal case */
+ unsigned int u = cast_uint(i) + cast_uint(ni);
+ return cast_int(u <= cast_uint(INT_MAX) ? u : ~u);
+ }
+}
+#endif
+
+
+/*
+** returns the 'main' position of an element in a table (that is,
+** the index of its hash value).
+*/
+static Node *mainpositionTV (const Table *t, const TValue *key) {
+ switch (ttypetag(key)) {
+ case LUA_VNUMINT: {
+ lua_Integer i = ivalue(key);
+ return hashint(t, i);
+ }
+ case LUA_VNUMFLT: {
+ lua_Number n = fltvalue(key);
+ return hashmod(t, l_hashfloat(n));
+ }
+ case LUA_VSHRSTR: {
+ TString *ts = tsvalue(key);
+ return hashstr(t, ts);
+ }
+ case LUA_VLNGSTR: {
+ TString *ts = tsvalue(key);
+ return hashpow2(t, luaS_hashlongstr(ts));
+ }
+ case LUA_VFALSE:
+ return hashboolean(t, 0);
+ case LUA_VTRUE:
+ return hashboolean(t, 1);
+ case LUA_VLIGHTUSERDATA: {
+ void *p = pvalue(key);
+ return hashpointer(t, p);
+ }
+ case LUA_VLCF: {
+ lua_CFunction f = fvalue(key);
+ return hashpointer(t, f);
+ }
+ default: {
+ GCObject *o = gcvalue(key);
+ return hashpointer(t, o);
+ }
+ }
+}
+
+
+l_sinline Node *mainpositionfromnode (const Table *t, Node *nd) {
+ TValue key;
+ getnodekey(cast(lua_State *, NULL), &key, nd);
+ return mainpositionTV(t, &key);
+}
+
+
+/*
+** Check whether key 'k1' is equal to the key in node 'n2'. This
+** equality is raw, so there are no metamethods. Floats with integer
+** values have been normalized, so integers cannot be equal to
+** floats. It is assumed that 'eqshrstr' is simply pointer equality, so
+** that short strings are handled in the default case.
+** A true 'deadok' means to accept dead keys as equal to their original
+** values. All dead keys are compared in the default case, by pointer
+** identity. (Only collectable objects can produce dead keys.) Note that
+** dead long strings are also compared by identity.
+** Once a key is dead, its corresponding value may be collected, and
+** then another value can be created with the same address. If this
+** other value is given to 'next', 'equalkey' will signal a false
+** positive. In a regular traversal, this situation should never happen,
+** as all keys given to 'next' came from the table itself, and therefore
+** could not have been collected. Outside a regular traversal, we
+** have garbage in, garbage out. What is relevant is that this false
+** positive does not break anything. (In particular, 'next' will return
+** some other valid item on the table or nil.)
+*/
+static int equalkey (const TValue *k1, const Node *n2, int deadok) {
+ if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */
+ !(deadok && keyisdead(n2) && iscollectable(k1)))
+ return 0; /* cannot be same key */
+ switch (keytt(n2)) {
+ case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
+ return 1;
+ case LUA_VNUMINT:
+ return (ivalue(k1) == keyival(n2));
+ case LUA_VNUMFLT:
+ return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2)));
+ case LUA_VLIGHTUSERDATA:
+ return pvalue(k1) == pvalueraw(keyval(n2));
+ case LUA_VLCF:
+ return fvalue(k1) == fvalueraw(keyval(n2));
+ case ctb(LUA_VLNGSTR):
+ return luaS_eqlngstr(tsvalue(k1), keystrval(n2));
+ default:
+ return gcvalue(k1) == gcvalueraw(keyval(n2));
+ }
+}
+
+
+/*
+** True if value of 'alimit' is equal to the real size of the array
+** part of table 't'. (Otherwise, the array part must be larger than
+** 'alimit'.)
+*/
+#define limitequalsasize(t) (isrealasize(t) || ispow2((t)->alimit))
+
+
+/*
+** Returns the real size of the 'array' array
+*/
+LUAI_FUNC unsigned int luaH_realasize (const Table *t) {
+ if (limitequalsasize(t))
+ return t->alimit; /* this is the size */
+ else {
+ unsigned int size = t->alimit;
+ /* compute the smallest power of 2 not smaller than 'n' */
+ size |= (size >> 1);
+ size |= (size >> 2);
+ size |= (size >> 4);
+ size |= (size >> 8);
+#if (UINT_MAX >> 14) > 3 /* unsigned int has more than 16 bits */
+ size |= (size >> 16);
+#if (UINT_MAX >> 30) > 3
+ size |= (size >> 32); /* unsigned int has more than 32 bits */
+#endif
+#endif
+ size++;
+ lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size);
+ return size;
+ }
+}
+
+
+/*
+** Check whether real size of the array is a power of 2.
+** (If it is not, 'alimit' cannot be changed to any other value
+** without changing the real size.)
+*/
+static int ispow2realasize (const Table *t) {
+ return (!isrealasize(t) || ispow2(t->alimit));
+}
+
+
+static unsigned int setlimittosize (Table *t) {
+ t->alimit = luaH_realasize(t);
+ setrealasize(t);
+ return t->alimit;
+}
+
+
+#define limitasasize(t) check_exp(isrealasize(t), t->alimit)
+
+
+
+/*
+** "Generic" get version. (Not that generic: not valid for integers,
+** which may be in array part, nor for floats with integral values.)
+** See explanation about 'deadok' in function 'equalkey'.
+*/
+static const TValue *getgeneric (Table *t, const TValue *key, int deadok) {
+ Node *n = mainpositionTV(t, key);
+ for (;;) { /* check whether 'key' is somewhere in the chain */
+ if (equalkey(key, n, deadok))
+ return gval(n); /* that's it */
+ else {
+ int nx = gnext(n);
+ if (nx == 0)
+ return &absentkey; /* not found */
+ n += nx;
+ }
+ }
+}
+
+
+/*
+** returns the index for 'k' if 'k' is an appropriate key to live in
+** the array part of a table, 0 otherwise.
+*/
+static unsigned int arrayindex (lua_Integer k) {
+ if (l_castS2U(k) - 1u < MAXASIZE) /* 'k' in [1, MAXASIZE]? */
+ return cast_uint(k); /* 'key' is an appropriate array index */
+ else
+ return 0;
+}
+
+
+/*
+** returns the index of a 'key' for table traversals. First goes all
+** elements in the array part, then elements in the hash part. The
+** beginning of a traversal is signaled by 0.
+*/
+static unsigned int findindex (lua_State *L, Table *t, TValue *key,
+ unsigned int asize) {
+ unsigned int i;
+ if (ttisnil(key)) return 0; /* first iteration */
+ i = ttisinteger(key) ? arrayindex(ivalue(key)) : 0;
+ if (i - 1u < asize) /* is 'key' inside array part? */
+ return i; /* yes; that's the index */
+ else {
+ const TValue *n = getgeneric(t, key, 1);
+ if (l_unlikely(isabstkey(n)))
+ luaG_runerror(L, "invalid key to 'next'"); /* key not found */
+ i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
+ /* hash elements are numbered after array ones */
+ return (i + 1) + asize;
+ }
+}
+
+
+int luaH_next (lua_State *L, Table *t, StkId key) {
+ unsigned int asize = luaH_realasize(t);
+ unsigned int i = findindex(L, t, s2v(key), asize); /* find original key */
+ for (; i < asize; i++) { /* try first array part */
+ if (!isempty(&t->array[i])) { /* a non-empty entry? */
+ setivalue(s2v(key), i + 1);
+ setobj2s(L, key + 1, &t->array[i]);
+ return 1;
+ }
+ }
+ for (i -= asize; cast_int(i) < sizenode(t); i++) { /* hash part */
+ if (!isempty(gval(gnode(t, i)))) { /* a non-empty entry? */
+ Node *n = gnode(t, i);
+ getnodekey(L, s2v(key), n);
+ setobj2s(L, key + 1, gval(n));
+ return 1;
+ }
+ }
+ return 0; /* no more elements */
+}
+
+
+static void freehash (lua_State *L, Table *t) {
+ if (!isdummy(t))
+ luaM_freearray(L, t->node, cast_sizet(sizenode(t)));
+}
+
+
+/*
+** {=============================================================
+** Rehash
+** ==============================================================
+*/
+
+/*
+** Compute the optimal size for the array part of table 't'. 'nums' is a
+** "count array" where 'nums[i]' is the number of integers in the table
+** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of
+** integer keys in the table and leaves with the number of keys that
+** will go to the array part; return the optimal size. (The condition
+** 'twotoi > 0' in the for loop stops the loop if 'twotoi' overflows.)
+*/
+static unsigned int computesizes (unsigned int nums[], unsigned int *pna) {
+ int i;
+ unsigned int twotoi; /* 2^i (candidate for optimal size) */
+ unsigned int a = 0; /* number of elements smaller than 2^i */
+ unsigned int na = 0; /* number of elements to go to array part */
+ unsigned int optimal = 0; /* optimal size for array part */
+ /* loop while keys can fill more than half of total size */
+ for (i = 0, twotoi = 1;
+ twotoi > 0 && *pna > twotoi / 2;
+ i++, twotoi *= 2) {
+ a += nums[i];
+ if (a > twotoi/2) { /* more than half elements present? */
+ optimal = twotoi; /* optimal size (till now) */
+ na = a; /* all elements up to 'optimal' will go to array part */
+ }
+ }
+ lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal);
+ *pna = na;
+ return optimal;
+}
+
+
+static int countint (lua_Integer key, unsigned int *nums) {
+ unsigned int k = arrayindex(key);
+ if (k != 0) { /* is 'key' an appropriate array index? */
+ nums[luaO_ceillog2(k)]++; /* count as such */
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/*
+** Count keys in array part of table 't': Fill 'nums[i]' with
+** number of keys that will go into corresponding slice and return
+** total number of non-nil keys.
+*/
+static unsigned int numusearray (const Table *t, unsigned int *nums) {
+ int lg;
+ unsigned int ttlg; /* 2^lg */
+ unsigned int ause = 0; /* summation of 'nums' */
+ unsigned int i = 1; /* count to traverse all array keys */
+ unsigned int asize = limitasasize(t); /* real array size */
+ /* traverse each slice */
+ for (lg = 0, ttlg = 1; lg <= MAXABITS; lg++, ttlg *= 2) {
+ unsigned int lc = 0; /* counter */
+ unsigned int lim = ttlg;
+ if (lim > asize) {
+ lim = asize; /* adjust upper limit */
+ if (i > lim)
+ break; /* no more elements to count */
+ }
+ /* count elements in range (2^(lg - 1), 2^lg] */
+ for (; i <= lim; i++) {
+ if (!isempty(&t->array[i-1]))
+ lc++;
+ }
+ nums[lg] += lc;
+ ause += lc;
+ }
+ return ause;
+}
+
+
+static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) {
+ int totaluse = 0; /* total number of elements */
+ int ause = 0; /* elements added to 'nums' (can go to array part) */
+ int i = sizenode(t);
+ while (i--) {
+ Node *n = &t->node[i];
+ if (!isempty(gval(n))) {
+ if (keyisinteger(n))
+ ause += countint(keyival(n), nums);
+ totaluse++;
+ }
+ }
+ *pna += ause;
+ return totaluse;
+}
+
+
+/*
+** Creates an array for the hash part of a table with the given
+** size, or reuses the dummy node if size is zero.
+** The computation for size overflow is in two steps: the first
+** comparison ensures that the shift in the second one does not
+** overflow.
+*/
+static void setnodevector (lua_State *L, Table *t, unsigned int size) {
+ if (size == 0) { /* no elements to hash part? */
+ t->node = cast(Node *, dummynode); /* use common 'dummynode' */
+ t->lsizenode = 0;
+ t->lastfree = NULL; /* signal that it is using dummy node */
+ }
+ else {
+ int i;
+ int lsize = luaO_ceillog2(size);
+ if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE)
+ luaG_runerror(L, "table overflow");
+ size = twoto(lsize);
+ t->node = luaM_newvector(L, size, Node);
+ for (i = 0; i < cast_int(size); i++) {
+ Node *n = gnode(t, i);
+ gnext(n) = 0;
+ setnilkey(n);
+ setempty(gval(n));
+ }
+ t->lsizenode = cast_byte(lsize);
+ t->lastfree = gnode(t, size); /* all positions are free */
+ }
+}
+
+
+/*
+** (Re)insert all elements from the hash part of 'ot' into table 't'.
+*/
+static void reinsert (lua_State *L, Table *ot, Table *t) {
+ int j;
+ int size = sizenode(ot);
+ for (j = 0; j < size; j++) {
+ Node *old = gnode(ot, j);
+ if (!isempty(gval(old))) {
+ /* doesn't need barrier/invalidate cache, as entry was
+ already present in the table */
+ TValue k;
+ getnodekey(L, &k, old);
+ luaH_set(L, t, &k, gval(old));
+ }
+ }
+}
+
+
+/*
+** Exchange the hash part of 't1' and 't2'.
+*/
+static void exchangehashpart (Table *t1, Table *t2) {
+ lu_byte lsizenode = t1->lsizenode;
+ Node *node = t1->node;
+ Node *lastfree = t1->lastfree;
+ t1->lsizenode = t2->lsizenode;
+ t1->node = t2->node;
+ t1->lastfree = t2->lastfree;
+ t2->lsizenode = lsizenode;
+ t2->node = node;
+ t2->lastfree = lastfree;
+}
+
+
+/*
+** Resize table 't' for the new given sizes. Both allocations (for
+** the hash part and for the array part) can fail, which creates some
+** subtleties. If the first allocation, for the hash part, fails, an
+** error is raised and that is it. Otherwise, it copies the elements from
+** the shrinking part of the array (if it is shrinking) into the new
+** hash. Then it reallocates the array part. If that fails, the table
+** is in its original state; the function frees the new hash part and then
+** raises the allocation error. Otherwise, it sets the new hash part
+** into the table, initializes the new part of the array (if any) with
+** nils and reinserts the elements of the old hash back into the new
+** parts of the table.
+*/
+void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
+ unsigned int nhsize) {
+ unsigned int i;
+ Table newt; /* to keep the new hash part */
+ unsigned int oldasize = setlimittosize(t);
+ TValue *newarray;
+ /* create new hash part with appropriate size into 'newt' */
+ setnodevector(L, &newt, nhsize);
+ if (newasize < oldasize) { /* will array shrink? */
+ t->alimit = newasize; /* pretend array has new size... */
+ exchangehashpart(t, &newt); /* and new hash */
+ /* re-insert into the new hash the elements from vanishing slice */
+ for (i = newasize; i < oldasize; i++) {
+ if (!isempty(&t->array[i]))
+ luaH_setint(L, t, i + 1, &t->array[i]);
+ }
+ t->alimit = oldasize; /* restore current size... */
+ exchangehashpart(t, &newt); /* and hash (in case of errors) */
+ }
+ /* allocate new array */
+ newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue);
+ if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */
+ freehash(L, &newt); /* release new hash part */
+ luaM_error(L); /* raise error (with array unchanged) */
+ }
+ /* allocation ok; initialize new part of the array */
+ exchangehashpart(t, &newt); /* 't' has the new hash ('newt' has the old) */
+ t->array = newarray; /* set new array part */
+ t->alimit = newasize;
+ for (i = oldasize; i < newasize; i++) /* clear new slice of the array */
+ setempty(&t->array[i]);
+ /* re-insert elements from old hash part into new parts */
+ reinsert(L, &newt, t); /* 'newt' now has the old hash */
+ freehash(L, &newt); /* free old hash part */
+}
+
+
+void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize) {
+ int nsize = allocsizenode(t);
+ luaH_resize(L, t, nasize, nsize);
+}
+
+/*
+** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i
+*/
+static void rehash (lua_State *L, Table *t, const TValue *ek) {
+ unsigned int asize; /* optimal size for array part */
+ unsigned int na; /* number of keys in the array part */
+ unsigned int nums[MAXABITS + 1];
+ int i;
+ int totaluse;
+ for (i = 0; i <= MAXABITS; i++) nums[i] = 0; /* reset counts */
+ setlimittosize(t);
+ na = numusearray(t, nums); /* count keys in array part */
+ totaluse = na; /* all those keys are integer keys */
+ totaluse += numusehash(t, nums, &na); /* count keys in hash part */
+ /* count extra key */
+ if (ttisinteger(ek))
+ na += countint(ivalue(ek), nums);
+ totaluse++;
+ /* compute new size for array part */
+ asize = computesizes(nums, &na);
+ /* resize the table to new computed sizes */
+ luaH_resize(L, t, asize, totaluse - na);
+}
+
+
+
+/*
+** }=============================================================
+*/
+
+
+Table *luaH_new (lua_State *L) {
+ GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table));
+ Table *t = gco2t(o);
+ t->metatable = NULL;
+ t->flags = cast_byte(maskflags); /* table has no metamethod fields */
+ t->array = NULL;
+ t->alimit = 0;
+ setnodevector(L, t, 0);
+ return t;
+}
+
+
+void luaH_free (lua_State *L, Table *t) {
+ freehash(L, t);
+ luaM_freearray(L, t->array, luaH_realasize(t));
+ luaM_free(L, t);
+}
+
+
+static Node *getfreepos (Table *t) {
+ if (!isdummy(t)) {
+ while (t->lastfree > t->node) {
+ t->lastfree--;
+ if (keyisnil(t->lastfree))
+ return t->lastfree;
+ }
+ }
+ return NULL; /* could not find a free place */
+}
+
+
+
+/*
+** inserts a new key into a hash table; first, check whether key's main
+** position is free. If not, check whether colliding node is in its main
+** position or not: if it is not, move colliding node to an empty place and
+** put new key in its main position; otherwise (colliding node is in its main
+** position), new key goes to an empty position.
+*/
+void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) {
+ Node *mp;
+ TValue aux;
+ if (l_unlikely(ttisnil(key)))
+ luaG_runerror(L, "table index is nil");
+ else if (ttisfloat(key)) {
+ lua_Number f = fltvalue(key);
+ lua_Integer k;
+ if (luaV_flttointeger(f, &k, F2Ieq)) { /* does key fit in an integer? */
+ setivalue(&aux, k);
+ key = &aux; /* insert it as an integer */
+ }
+ else if (l_unlikely(luai_numisnan(f)))
+ luaG_runerror(L, "table index is NaN");
+ }
+ if (ttisnil(value))
+ return; /* do not insert nil values */
+ mp = mainpositionTV(t, key);
+ if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */
+ Node *othern;
+ Node *f = getfreepos(t); /* get a free place */
+ if (f == NULL) { /* cannot find a free place? */
+ rehash(L, t, key); /* grow table */
+ /* whatever called 'newkey' takes care of TM cache */
+ luaH_set(L, t, key, value); /* insert key into grown table */
+ return;
+ }
+ lua_assert(!isdummy(t));
+ othern = mainpositionfromnode(t, mp);
+ if (othern != mp) { /* is colliding node out of its main position? */
+ /* yes; move colliding node into free position */
+ while (othern + gnext(othern) != mp) /* find previous */
+ othern += gnext(othern);
+ gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */
+ *f = *mp; /* copy colliding node into free pos. (mp->next also goes) */
+ if (gnext(mp) != 0) {
+ gnext(f) += cast_int(mp - f); /* correct 'next' */
+ gnext(mp) = 0; /* now 'mp' is free */
+ }
+ setempty(gval(mp));
+ }
+ else { /* colliding node is in its own main position */
+ /* new node will go into free position */
+ if (gnext(mp) != 0)
+ gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */
+ else lua_assert(gnext(f) == 0);
+ gnext(mp) = cast_int(f - mp);
+ mp = f;
+ }
+ }
+ setnodekey(L, mp, key);
+ luaC_barrierback(L, obj2gco(t), key);
+ lua_assert(isempty(gval(mp)));
+ setobj2t(L, gval(mp), value);
+}
+
+
+/*
+** Search function for integers. If integer is inside 'alimit', get it
+** directly from the array part. Otherwise, if 'alimit' is not equal to
+** the real size of the array, key still can be in the array part. In
+** this case, try to avoid a call to 'luaH_realasize' when key is just
+** one more than the limit (so that it can be incremented without
+** changing the real size of the array).
+*/
+const TValue *luaH_getint (Table *t, lua_Integer key) {
+ if (l_castS2U(key) - 1u < t->alimit) /* 'key' in [1, t->alimit]? */
+ return &t->array[key - 1];
+ else if (!limitequalsasize(t) && /* key still may be in the array part? */
+ (l_castS2U(key) == t->alimit + 1 ||
+ l_castS2U(key) - 1u < luaH_realasize(t))) {
+ t->alimit = cast_uint(key); /* probably '#t' is here now */
+ return &t->array[key - 1];
+ }
+ else {
+ Node *n = hashint(t, key);
+ for (;;) { /* check whether 'key' is somewhere in the chain */
+ if (keyisinteger(n) && keyival(n) == key)
+ return gval(n); /* that's it */
+ else {
+ int nx = gnext(n);
+ if (nx == 0) break;
+ n += nx;
+ }
+ }
+ return &absentkey;
+ }
+}
+
+
+/*
+** search function for short strings
+*/
+const TValue *luaH_getshortstr (Table *t, TString *key) {
+ Node *n = hashstr(t, key);
+ lua_assert(key->tt == LUA_VSHRSTR);
+ for (;;) { /* check whether 'key' is somewhere in the chain */
+ if (keyisshrstr(n) && eqshrstr(keystrval(n), key))
+ return gval(n); /* that's it */
+ else {
+ int nx = gnext(n);
+ if (nx == 0)
+ return &absentkey; /* not found */
+ n += nx;
+ }
+ }
+}
+
+
+const TValue *luaH_getstr (Table *t, TString *key) {
+ if (key->tt == LUA_VSHRSTR)
+ return luaH_getshortstr(t, key);
+ else { /* for long strings, use generic case */
+ TValue ko;
+ setsvalue(cast(lua_State *, NULL), &ko, key);
+ return getgeneric(t, &ko, 0);
+ }
+}
+
+
+/*
+** main search function
+*/
+const TValue *luaH_get (Table *t, const TValue *key) {
+ switch (ttypetag(key)) {
+ case LUA_VSHRSTR: return luaH_getshortstr(t, tsvalue(key));
+ case LUA_VNUMINT: return luaH_getint(t, ivalue(key));
+ case LUA_VNIL: return &absentkey;
+ case LUA_VNUMFLT: {
+ lua_Integer k;
+ if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */
+ return luaH_getint(t, k); /* use specialized version */
+ /* else... */
+ } /* FALLTHROUGH */
+ default:
+ return getgeneric(t, key, 0);
+ }
+}
+
+
+/*
+** Finish a raw "set table" operation, where 'slot' is where the value
+** should have been (the result of a previous "get table").
+** Beware: when using this function you probably need to check a GC
+** barrier and invalidate the TM cache.
+*/
+void luaH_finishset (lua_State *L, Table *t, const TValue *key,
+ const TValue *slot, TValue *value) {
+ if (isabstkey(slot))
+ luaH_newkey(L, t, key, value);
+ else
+ setobj2t(L, cast(TValue *, slot), value);
+}
+
+
+/*
+** beware: when using this function you probably need to check a GC
+** barrier and invalidate the TM cache.
+*/
+void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) {
+ const TValue *slot = luaH_get(t, key);
+ luaH_finishset(L, t, key, slot, value);
+}
+
+
+void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
+ const TValue *p = luaH_getint(t, key);
+ if (isabstkey(p)) {
+ TValue k;
+ setivalue(&k, key);
+ luaH_newkey(L, t, &k, value);
+ }
+ else
+ setobj2t(L, cast(TValue *, p), value);
+}
+
+
+/*
+** Try to find a boundary in the hash part of table 't'. From the
+** caller, we know that 'j' is zero or present and that 'j + 1' is
+** present. We want to find a larger key that is absent from the
+** table, so that we can do a binary search between the two keys to
+** find a boundary. We keep doubling 'j' until we get an absent index.
+** If the doubling would overflow, we try LUA_MAXINTEGER. If it is
+** absent, we are ready for the binary search. ('j', being max integer,
+** is larger or equal to 'i', but it cannot be equal because it is
+** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a
+** boundary. ('j + 1' cannot be a present integer key because it is
+** not a valid integer in Lua.)
+*/
+static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
+ lua_Unsigned i;
+ if (j == 0) j++; /* the caller ensures 'j + 1' is present */
+ do {
+ i = j; /* 'i' is a present index */
+ if (j <= l_castS2U(LUA_MAXINTEGER) / 2)
+ j *= 2;
+ else {
+ j = LUA_MAXINTEGER;
+ if (isempty(luaH_getint(t, j))) /* t[j] not present? */
+ break; /* 'j' now is an absent index */
+ else /* weird case */
+ return j; /* well, max integer is a boundary... */
+ }
+ } while (!isempty(luaH_getint(t, j))); /* repeat until an absent t[j] */
+ /* i < j && t[i] present && t[j] absent */
+ while (j - i > 1u) { /* do a binary search between them */
+ lua_Unsigned m = (i + j) / 2;
+ if (isempty(luaH_getint(t, m))) j = m;
+ else i = m;
+ }
+ return i;
+}
+
+
+static unsigned int binsearch (const TValue *array, unsigned int i,
+ unsigned int j) {
+ while (j - i > 1u) { /* binary search */
+ unsigned int m = (i + j) / 2;
+ if (isempty(&array[m - 1])) j = m;
+ else i = m;
+ }
+ return i;
+}
+
+
+/*
+** Try to find a boundary in table 't'. (A 'boundary' is an integer index
+** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent
+** and 'maxinteger' if t[maxinteger] is present.)
+** (In the next explanation, we use Lua indices, that is, with base 1.
+** The code itself uses base 0 when indexing the array part of the table.)
+** The code starts with 'limit = t->alimit', a position in the array
+** part that may be a boundary.
+**
+** (1) If 't[limit]' is empty, there must be a boundary before it.
+** As a common case (e.g., after 't[#t]=nil'), check whether 'limit-1'
+** is present. If so, it is a boundary. Otherwise, do a binary search
+** between 0 and limit to find a boundary. In both cases, try to
+** use this boundary as the new 'alimit', as a hint for the next call.
+**
+** (2) If 't[limit]' is not empty and the array has more elements
+** after 'limit', try to find a boundary there. Again, try first
+** the special case (which should be quite frequent) where 'limit+1'
+** is empty, so that 'limit' is a boundary. Otherwise, check the
+** last element of the array part. If it is empty, there must be a
+** boundary between the old limit (present) and the last element
+** (absent), which is found with a binary search. (This boundary always
+** can be a new limit.)
+**
+** (3) The last case is when there are no elements in the array part
+** (limit == 0) or its last element (the new limit) is present.
+** In this case, must check the hash part. If there is no hash part
+** or 'limit+1' is absent, 'limit' is a boundary. Otherwise, call
+** 'hash_search' to find a boundary in the hash part of the table.
+** (In those cases, the boundary is not inside the array part, and
+** therefore cannot be used as a new limit.)
+*/
+lua_Unsigned luaH_getn (Table *t) {
+ unsigned int limit = t->alimit;
+ if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */
+ /* there must be a boundary before 'limit' */
+ if (limit >= 2 && !isempty(&t->array[limit - 2])) {
+ /* 'limit - 1' is a boundary; can it be a new limit? */
+ if (ispow2realasize(t) && !ispow2(limit - 1)) {
+ t->alimit = limit - 1;
+ setnorealasize(t); /* now 'alimit' is not the real size */
+ }
+ return limit - 1;
+ }
+ else { /* must search for a boundary in [0, limit] */
+ unsigned int boundary = binsearch(t->array, 0, limit);
+ /* can this boundary represent the real size of the array? */
+ if (ispow2realasize(t) && boundary > luaH_realasize(t) / 2) {
+ t->alimit = boundary; /* use it as the new limit */
+ setnorealasize(t);
+ }
+ return boundary;
+ }
+ }
+ /* 'limit' is zero or present in table */
+ if (!limitequalsasize(t)) { /* (2)? */
+ /* 'limit' > 0 and array has more elements after 'limit' */
+ if (isempty(&t->array[limit])) /* 'limit + 1' is empty? */
+ return limit; /* this is the boundary */
+ /* else, try last element in the array */
+ limit = luaH_realasize(t);
+ if (isempty(&t->array[limit - 1])) { /* empty? */
+ /* there must be a boundary in the array after old limit,
+ and it must be a valid new limit */
+ unsigned int boundary = binsearch(t->array, t->alimit, limit);
+ t->alimit = boundary;
+ return boundary;
+ }
+ /* else, new limit is present in the table; check the hash part */
+ }
+ /* (3) 'limit' is the last element and either is zero or present in table */
+ lua_assert(limit == luaH_realasize(t) &&
+ (limit == 0 || !isempty(&t->array[limit - 1])));
+ if (isdummy(t) || isempty(luaH_getint(t, cast(lua_Integer, limit + 1))))
+ return limit; /* 'limit + 1' is absent */
+ else /* 'limit + 1' is also present */
+ return hash_search(t, limit);
+}
+
+
+
+#if defined(LUA_DEBUG)
+
+/* export these functions for the test library */
+
+Node *luaH_mainposition (const Table *t, const TValue *key) {
+ return mainpositionTV(t, key);
+}
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/ltable.h b/src/libs/3rdparty/lua/src/ltable.h
new file mode 100644
index 0000000000..75dd9e26e0
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ltable.h
@@ -0,0 +1,65 @@
+/*
+** $Id: ltable.h $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltable_h
+#define ltable_h
+
+#include "lobject.h"
+
+
+#define gnode(t,i) (&(t)->node[i])
+#define gval(n) (&(n)->i_val)
+#define gnext(n) ((n)->u.next)
+
+
+/*
+** Clear all bits of fast-access metamethods, which means that the table
+** may have any of these metamethods. (First access that fails after the
+** clearing will set the bit again.)
+*/
+#define invalidateTMcache(t) ((t)->flags &= ~maskflags)
+
+
+/* true when 't' is using 'dummynode' as its hash part */
+#define isdummy(t) ((t)->lastfree == NULL)
+
+
+/* allocated size for hash nodes */
+#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t))
+
+
+/* returns the Node, given the value of a table entry */
+#define nodefromval(v) cast(Node *, (v))
+
+
+LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key);
+LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
+ TValue *value);
+LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key);
+LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
+LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
+LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key,
+ TValue *value);
+LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key,
+ TValue *value);
+LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key,
+ const TValue *slot, TValue *value);
+LUAI_FUNC Table *luaH_new (lua_State *L);
+LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
+ unsigned int nhsize);
+LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize);
+LUAI_FUNC void luaH_free (lua_State *L, Table *t);
+LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
+LUAI_FUNC lua_Unsigned luaH_getn (Table *t);
+LUAI_FUNC unsigned int luaH_realasize (const Table *t);
+
+
+#if defined(LUA_DEBUG)
+LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
+#endif
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/ltablib.c b/src/libs/3rdparty/lua/src/ltablib.c
new file mode 100644
index 0000000000..e6bc4d04af
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ltablib.c
@@ -0,0 +1,430 @@
+/*
+** $Id: ltablib.c $
+** Library for Table Manipulation
+** See Copyright Notice in lua.h
+*/
+
+#define ltablib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** Operations that an object must define to mimic a table
+** (some functions only need some of them)
+*/
+#define TAB_R 1 /* read */
+#define TAB_W 2 /* write */
+#define TAB_L 4 /* length */
+#define TAB_RW (TAB_R | TAB_W) /* read/write */
+
+
+#define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n))
+
+
+static int checkfield (lua_State *L, const char *key, int n) {
+ lua_pushstring(L, key);
+ return (lua_rawget(L, -n) != LUA_TNIL);
+}
+
+
+/*
+** Check that 'arg' either is a table or can behave like one (that is,
+** has a metatable with the required metamethods)
+*/
+static void checktab (lua_State *L, int arg, int what) {
+ if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */
+ int n = 1; /* number of elements to pop */
+ if (lua_getmetatable(L, arg) && /* must have metatable */
+ (!(what & TAB_R) || checkfield(L, "__index", ++n)) &&
+ (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) &&
+ (!(what & TAB_L) || checkfield(L, "__len", ++n))) {
+ lua_pop(L, n); /* pop metatable and tested metamethods */
+ }
+ else
+ luaL_checktype(L, arg, LUA_TTABLE); /* force an error */
+ }
+}
+
+
+static int tinsert (lua_State *L) {
+ lua_Integer pos; /* where to insert new element */
+ lua_Integer e = aux_getn(L, 1, TAB_RW);
+ e = luaL_intop(+, e, 1); /* first empty element */
+ switch (lua_gettop(L)) {
+ case 2: { /* called with only 2 arguments */
+ pos = e; /* insert new element at the end */
+ break;
+ }
+ case 3: {
+ lua_Integer i;
+ pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */
+ /* check whether 'pos' is in [1, e] */
+ luaL_argcheck(L, (lua_Unsigned)pos - 1u < (lua_Unsigned)e, 2,
+ "position out of bounds");
+ for (i = e; i > pos; i--) { /* move up elements */
+ lua_geti(L, 1, i - 1);
+ lua_seti(L, 1, i); /* t[i] = t[i - 1] */
+ }
+ break;
+ }
+ default: {
+ return luaL_error(L, "wrong number of arguments to 'insert'");
+ }
+ }
+ lua_seti(L, 1, pos); /* t[pos] = v */
+ return 0;
+}
+
+
+static int tremove (lua_State *L) {
+ lua_Integer size = aux_getn(L, 1, TAB_RW);
+ lua_Integer pos = luaL_optinteger(L, 2, size);
+ if (pos != size) /* validate 'pos' if given */
+ /* check whether 'pos' is in [1, size + 1] */
+ luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2,
+ "position out of bounds");
+ lua_geti(L, 1, pos); /* result = t[pos] */
+ for ( ; pos < size; pos++) {
+ lua_geti(L, 1, pos + 1);
+ lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */
+ }
+ lua_pushnil(L);
+ lua_seti(L, 1, pos); /* remove entry t[pos] */
+ return 1;
+}
+
+
+/*
+** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever
+** possible, copy in increasing order, which is better for rehashing.
+** "possible" means destination after original range, or smaller
+** than origin, or copying to another table.
+*/
+static int tmove (lua_State *L) {
+ lua_Integer f = luaL_checkinteger(L, 2);
+ lua_Integer e = luaL_checkinteger(L, 3);
+ lua_Integer t = luaL_checkinteger(L, 4);
+ int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */
+ checktab(L, 1, TAB_R);
+ checktab(L, tt, TAB_W);
+ if (e >= f) { /* otherwise, nothing to move */
+ lua_Integer n, i;
+ luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3,
+ "too many elements to move");
+ n = e - f + 1; /* number of elements to move */
+ luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4,
+ "destination wrap around");
+ if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) {
+ for (i = 0; i < n; i++) {
+ lua_geti(L, 1, f + i);
+ lua_seti(L, tt, t + i);
+ }
+ }
+ else {
+ for (i = n - 1; i >= 0; i--) {
+ lua_geti(L, 1, f + i);
+ lua_seti(L, tt, t + i);
+ }
+ }
+ }
+ lua_pushvalue(L, tt); /* return destination table */
+ return 1;
+}
+
+
+static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
+ lua_geti(L, 1, i);
+ if (l_unlikely(!lua_isstring(L, -1)))
+ luaL_error(L, "invalid value (%s) at index %I in table for 'concat'",
+ luaL_typename(L, -1), (LUAI_UACINT)i);
+ luaL_addvalue(b);
+}
+
+
+static int tconcat (lua_State *L) {
+ luaL_Buffer b;
+ lua_Integer last = aux_getn(L, 1, TAB_R);
+ size_t lsep;
+ const char *sep = luaL_optlstring(L, 2, "", &lsep);
+ lua_Integer i = luaL_optinteger(L, 3, 1);
+ last = luaL_optinteger(L, 4, last);
+ luaL_buffinit(L, &b);
+ for (; i < last; i++) {
+ addfield(L, &b, i);
+ luaL_addlstring(&b, sep, lsep);
+ }
+ if (i == last) /* add last value (if interval was not empty) */
+ addfield(L, &b, i);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+/*
+** {======================================================
+** Pack/unpack
+** =======================================================
+*/
+
+static int tpack (lua_State *L) {
+ int i;
+ int n = lua_gettop(L); /* number of elements to pack */
+ lua_createtable(L, n, 1); /* create result table */
+ lua_insert(L, 1); /* put it at index 1 */
+ for (i = n; i >= 1; i--) /* assign elements */
+ lua_seti(L, 1, i);
+ lua_pushinteger(L, n);
+ lua_setfield(L, 1, "n"); /* t.n = number of elements */
+ return 1; /* return table */
+}
+
+
+static int tunpack (lua_State *L) {
+ lua_Unsigned n;
+ lua_Integer i = luaL_optinteger(L, 2, 1);
+ lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
+ if (i > e) return 0; /* empty range */
+ n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */
+ if (l_unlikely(n >= (unsigned int)INT_MAX ||
+ !lua_checkstack(L, (int)(++n))))
+ return luaL_error(L, "too many results to unpack");
+ for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */
+ lua_geti(L, 1, i);
+ }
+ lua_geti(L, 1, e); /* push last element */
+ return (int)n;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** Quicksort
+** (based on 'Algorithms in MODULA-3', Robert Sedgewick;
+** Addison-Wesley, 1993.)
+** =======================================================
+*/
+
+
+/* type for array indices */
+typedef unsigned int IdxT;
+
+
+/*
+** Produce a "random" 'unsigned int' to randomize pivot choice. This
+** macro is used only when 'sort' detects a big imbalance in the result
+** of a partition. (If you don't want/need this "randomness", ~0 is a
+** good choice.)
+*/
+#if !defined(l_randomizePivot) /* { */
+
+#include <time.h>
+
+/* size of 'e' measured in number of 'unsigned int's */
+#define sof(e) (sizeof(e) / sizeof(unsigned int))
+
+/*
+** Use 'time' and 'clock' as sources of "randomness". Because we don't
+** know the types 'clock_t' and 'time_t', we cannot cast them to
+** anything without risking overflows. A safe way to use their values
+** is to copy them to an array of a known type and use the array values.
+*/
+static unsigned int l_randomizePivot (void) {
+ clock_t c = clock();
+ time_t t = time(NULL);
+ unsigned int buff[sof(c) + sof(t)];
+ unsigned int i, rnd = 0;
+ memcpy(buff, &c, sof(c) * sizeof(unsigned int));
+ memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int));
+ for (i = 0; i < sof(buff); i++)
+ rnd += buff[i];
+ return rnd;
+}
+
+#endif /* } */
+
+
+/* arrays larger than 'RANLIMIT' may use randomized pivots */
+#define RANLIMIT 100u
+
+
+static void set2 (lua_State *L, IdxT i, IdxT j) {
+ lua_seti(L, 1, i);
+ lua_seti(L, 1, j);
+}
+
+
+/*
+** Return true iff value at stack index 'a' is less than the value at
+** index 'b' (according to the order of the sort).
+*/
+static int sort_comp (lua_State *L, int a, int b) {
+ if (lua_isnil(L, 2)) /* no function? */
+ return lua_compare(L, a, b, LUA_OPLT); /* a < b */
+ else { /* function */
+ int res;
+ lua_pushvalue(L, 2); /* push function */
+ lua_pushvalue(L, a-1); /* -1 to compensate function */
+ lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */
+ lua_call(L, 2, 1); /* call function */
+ res = lua_toboolean(L, -1); /* get result */
+ lua_pop(L, 1); /* pop result */
+ return res;
+ }
+}
+
+
+/*
+** Does the partition: Pivot P is at the top of the stack.
+** precondition: a[lo] <= P == a[up-1] <= a[up],
+** so it only needs to do the partition from lo + 1 to up - 2.
+** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up]
+** returns 'i'.
+*/
+static IdxT partition (lua_State *L, IdxT lo, IdxT up) {
+ IdxT i = lo; /* will be incremented before first use */
+ IdxT j = up - 1; /* will be decremented before first use */
+ /* loop invariant: a[lo .. i] <= P <= a[j .. up] */
+ for (;;) {
+ /* next loop: repeat ++i while a[i] < P */
+ while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
+ if (l_unlikely(i == up - 1)) /* a[i] < P but a[up - 1] == P ?? */
+ luaL_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[i] */
+ }
+ /* after the loop, a[i] >= P and a[lo .. i - 1] < P */
+ /* next loop: repeat --j while P < a[j] */
+ while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
+ if (l_unlikely(j < i)) /* j < i but a[j] > P ?? */
+ luaL_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[j] */
+ }
+ /* after the loop, a[j] <= P and a[j + 1 .. up] >= P */
+ if (j < i) { /* no elements out of place? */
+ /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */
+ lua_pop(L, 1); /* pop a[j] */
+ /* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */
+ set2(L, up - 1, i);
+ return i;
+ }
+ /* otherwise, swap a[i] - a[j] to restore invariant and repeat */
+ set2(L, i, j);
+ }
+}
+
+
+/*
+** Choose an element in the middle (2nd-3th quarters) of [lo,up]
+** "randomized" by 'rnd'
+*/
+static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) {
+ IdxT r4 = (up - lo) / 4; /* range/4 */
+ IdxT p = rnd % (r4 * 2) + (lo + r4);
+ lua_assert(lo + r4 <= p && p <= up - r4);
+ return p;
+}
+
+
+/*
+** Quicksort algorithm (recursive function)
+*/
+static void auxsort (lua_State *L, IdxT lo, IdxT up,
+ unsigned int rnd) {
+ while (lo < up) { /* loop for tail recursion */
+ IdxT p; /* Pivot index */
+ IdxT n; /* to be used later */
+ /* sort elements 'lo', 'p', and 'up' */
+ lua_geti(L, 1, lo);
+ lua_geti(L, 1, up);
+ if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */
+ set2(L, lo, up); /* swap a[lo] - a[up] */
+ else
+ lua_pop(L, 2); /* remove both values */
+ if (up - lo == 1) /* only 2 elements? */
+ return; /* already sorted */
+ if (up - lo < RANLIMIT || rnd == 0) /* small interval or no randomize? */
+ p = (lo + up)/2; /* middle element is a good pivot */
+ else /* for larger intervals, it is worth a random pivot */
+ p = choosePivot(lo, up, rnd);
+ lua_geti(L, 1, p);
+ lua_geti(L, 1, lo);
+ if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */
+ set2(L, p, lo); /* swap a[p] - a[lo] */
+ else {
+ lua_pop(L, 1); /* remove a[lo] */
+ lua_geti(L, 1, up);
+ if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */
+ set2(L, p, up); /* swap a[up] - a[p] */
+ else
+ lua_pop(L, 2);
+ }
+ if (up - lo == 2) /* only 3 elements? */
+ return; /* already sorted */
+ lua_geti(L, 1, p); /* get middle element (Pivot) */
+ lua_pushvalue(L, -1); /* push Pivot */
+ lua_geti(L, 1, up - 1); /* push a[up - 1] */
+ set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */
+ p = partition(L, lo, up);
+ /* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */
+ if (p - lo < up - p) { /* lower interval is smaller? */
+ auxsort(L, lo, p - 1, rnd); /* call recursively for lower interval */
+ n = p - lo; /* size of smaller interval */
+ lo = p + 1; /* tail call for [p + 1 .. up] (upper interval) */
+ }
+ else {
+ auxsort(L, p + 1, up, rnd); /* call recursively for upper interval */
+ n = up - p; /* size of smaller interval */
+ up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */
+ }
+ if ((up - lo) / 128 > n) /* partition too imbalanced? */
+ rnd = l_randomizePivot(); /* try a new randomization */
+ } /* tail call auxsort(L, lo, up, rnd) */
+}
+
+
+static int sort (lua_State *L) {
+ lua_Integer n = aux_getn(L, 1, TAB_RW);
+ if (n > 1) { /* non-trivial interval? */
+ luaL_argcheck(L, n < INT_MAX, 1, "array too big");
+ if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */
+ luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */
+ lua_settop(L, 2); /* make sure there are two arguments */
+ auxsort(L, 1, (IdxT)n, 0);
+ }
+ return 0;
+}
+
+/* }====================================================== */
+
+
+static const luaL_Reg tab_funcs[] = {
+ {"concat", tconcat},
+ {"insert", tinsert},
+ {"pack", tpack},
+ {"unpack", tunpack},
+ {"remove", tremove},
+ {"move", tmove},
+ {"sort", sort},
+ {NULL, NULL}
+};
+
+
+LUAMOD_API int luaopen_table (lua_State *L) {
+ luaL_newlib(L, tab_funcs);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/ltm.c b/src/libs/3rdparty/lua/src/ltm.c
new file mode 100644
index 0000000000..07a060811d
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ltm.c
@@ -0,0 +1,271 @@
+/*
+** $Id: ltm.c $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+#define ltm_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+static const char udatatypename[] = "userdata";
+
+LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTYPES] = {
+ "no value",
+ "nil", "boolean", udatatypename, "number",
+ "string", "table", "function", udatatypename, "thread",
+ "upvalue", "proto" /* these last cases are used for tests only */
+};
+
+
+void luaT_init (lua_State *L) {
+ static const char *const luaT_eventname[] = { /* ORDER TM */
+ "__index", "__newindex",
+ "__gc", "__mode", "__len", "__eq",
+ "__add", "__sub", "__mul", "__mod", "__pow",
+ "__div", "__idiv",
+ "__band", "__bor", "__bxor", "__shl", "__shr",
+ "__unm", "__bnot", "__lt", "__le",
+ "__concat", "__call", "__close"
+ };
+ int i;
+ for (i=0; i<TM_N; i++) {
+ G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]);
+ luaC_fix(L, obj2gco(G(L)->tmname[i])); /* never collect these names */
+ }
+}
+
+
+/*
+** function to be used with macro "fasttm": optimized for absence of
+** tag methods
+*/
+const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {
+ const TValue *tm = luaH_getshortstr(events, ename);
+ lua_assert(event <= TM_EQ);
+ if (notm(tm)) { /* no tag method? */
+ events->flags |= cast_byte(1u<<event); /* cache this fact */
+ return NULL;
+ }
+ else return tm;
+}
+
+
+const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {
+ Table *mt;
+ switch (ttype(o)) {
+ case LUA_TTABLE:
+ mt = hvalue(o)->metatable;
+ break;
+ case LUA_TUSERDATA:
+ mt = uvalue(o)->metatable;
+ break;
+ default:
+ mt = G(L)->mt[ttype(o)];
+ }
+ return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue);
+}
+
+
+/*
+** Return the name of the type of an object. For tables and userdata
+** with metatable, use their '__name' metafield, if present.
+*/
+const char *luaT_objtypename (lua_State *L, const TValue *o) {
+ Table *mt;
+ if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) ||
+ (ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) {
+ const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name"));
+ if (ttisstring(name)) /* is '__name' a string? */
+ return getstr(tsvalue(name)); /* use it as type name */
+ }
+ return ttypename(ttype(o)); /* else use standard type name */
+}
+
+
+void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
+ const TValue *p2, const TValue *p3) {
+ StkId func = L->top.p;
+ setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
+ setobj2s(L, func + 1, p1); /* 1st argument */
+ setobj2s(L, func + 2, p2); /* 2nd argument */
+ setobj2s(L, func + 3, p3); /* 3rd argument */
+ L->top.p = func + 4;
+ /* metamethod may yield only when called from Lua code */
+ if (isLuacode(L->ci))
+ luaD_call(L, func, 0);
+ else
+ luaD_callnoyield(L, func, 0);
+}
+
+
+void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1,
+ const TValue *p2, StkId res) {
+ ptrdiff_t result = savestack(L, res);
+ StkId func = L->top.p;
+ setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
+ setobj2s(L, func + 1, p1); /* 1st argument */
+ setobj2s(L, func + 2, p2); /* 2nd argument */
+ L->top.p += 3;
+ /* metamethod may yield only when called from Lua code */
+ if (isLuacode(L->ci))
+ luaD_call(L, func, 1);
+ else
+ luaD_callnoyield(L, func, 1);
+ res = restorestack(L, result);
+ setobjs2s(L, res, --L->top.p); /* move result to its place */
+}
+
+
+static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2,
+ StkId res, TMS event) {
+ const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
+ if (notm(tm))
+ tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
+ if (notm(tm)) return 0;
+ luaT_callTMres(L, tm, p1, p2, res);
+ return 1;
+}
+
+
+void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
+ StkId res, TMS event) {
+ if (l_unlikely(!callbinTM(L, p1, p2, res, event))) {
+ switch (event) {
+ case TM_BAND: case TM_BOR: case TM_BXOR:
+ case TM_SHL: case TM_SHR: case TM_BNOT: {
+ if (ttisnumber(p1) && ttisnumber(p2))
+ luaG_tointerror(L, p1, p2);
+ else
+ luaG_opinterror(L, p1, p2, "perform bitwise operation on");
+ }
+ /* calls never return, but to avoid warnings: *//* FALLTHROUGH */
+ default:
+ luaG_opinterror(L, p1, p2, "perform arithmetic on");
+ }
+ }
+}
+
+
+void luaT_tryconcatTM (lua_State *L) {
+ StkId top = L->top.p;
+ if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2,
+ TM_CONCAT)))
+ luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
+}
+
+
+void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2,
+ int flip, StkId res, TMS event) {
+ if (flip)
+ luaT_trybinTM(L, p2, p1, res, event);
+ else
+ luaT_trybinTM(L, p1, p2, res, event);
+}
+
+
+void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
+ int flip, StkId res, TMS event) {
+ TValue aux;
+ setivalue(&aux, i2);
+ luaT_trybinassocTM(L, p1, &aux, flip, res, event);
+}
+
+
+/*
+** Calls an order tag method.
+** For lessequal, LUA_COMPAT_LT_LE keeps compatibility with old
+** behavior: if there is no '__le', try '__lt', based on l <= r iff
+** !(r < l) (assuming a total order). If the metamethod yields during
+** this substitution, the continuation has to know about it (to negate
+** the result of r<l); bit CIST_LEQ in the call status keeps that
+** information.
+*/
+int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
+ TMS event) {
+ if (callbinTM(L, p1, p2, L->top.p, event)) /* try original event */
+ return !l_isfalse(s2v(L->top.p));
+#if defined(LUA_COMPAT_LT_LE)
+ else if (event == TM_LE) {
+ /* try '!(p2 < p1)' for '(p1 <= p2)' */
+ L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
+ if (callbinTM(L, p2, p1, L->top.p, TM_LT)) {
+ L->ci->callstatus ^= CIST_LEQ; /* clear mark */
+ return l_isfalse(s2v(L->top.p));
+ }
+ /* else error will remove this 'ci'; no need to clear mark */
+ }
+#endif
+ luaG_ordererror(L, p1, p2); /* no metamethod found */
+ return 0; /* to avoid warnings */
+}
+
+
+int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
+ int flip, int isfloat, TMS event) {
+ TValue aux; const TValue *p2;
+ if (isfloat) {
+ setfltvalue(&aux, cast_num(v2));
+ }
+ else
+ setivalue(&aux, v2);
+ if (flip) { /* arguments were exchanged? */
+ p2 = p1; p1 = &aux; /* correct them */
+ }
+ else
+ p2 = &aux;
+ return luaT_callorderTM(L, p1, p2, event);
+}
+
+
+void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
+ const Proto *p) {
+ int i;
+ int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */
+ int nextra = actual - nfixparams; /* number of extra arguments */
+ ci->u.l.nextraargs = nextra;
+ luaD_checkstack(L, p->maxstacksize + 1);
+ /* copy function to the top of the stack */
+ setobjs2s(L, L->top.p++, ci->func.p);
+ /* move fixed parameters to the top of the stack */
+ for (i = 1; i <= nfixparams; i++) {
+ setobjs2s(L, L->top.p++, ci->func.p + i);
+ setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */
+ }
+ ci->func.p += actual + 1;
+ ci->top.p += actual + 1;
+ lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p);
+}
+
+
+void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
+ int i;
+ int nextra = ci->u.l.nextraargs;
+ if (wanted < 0) {
+ wanted = nextra; /* get all extra arguments available */
+ checkstackGCp(L, nextra, where); /* ensure stack space */
+ L->top.p = where + nextra; /* next instruction will need top */
+ }
+ for (i = 0; i < wanted && i < nextra; i++)
+ setobjs2s(L, where + i, ci->func.p - nextra + i);
+ for (; i < wanted; i++) /* complete required results with nil */
+ setnilvalue(s2v(where + i));
+}
+
diff --git a/src/libs/3rdparty/lua/src/ltm.h b/src/libs/3rdparty/lua/src/ltm.h
new file mode 100644
index 0000000000..c309e2ae10
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ltm.h
@@ -0,0 +1,104 @@
+/*
+** $Id: ltm.h $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltm_h
+#define ltm_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER TM" and "ORDER OP"
+*/
+typedef enum {
+ TM_INDEX,
+ TM_NEWINDEX,
+ TM_GC,
+ TM_MODE,
+ TM_LEN,
+ TM_EQ, /* last tag method with fast access */
+ TM_ADD,
+ TM_SUB,
+ TM_MUL,
+ TM_MOD,
+ TM_POW,
+ TM_DIV,
+ TM_IDIV,
+ TM_BAND,
+ TM_BOR,
+ TM_BXOR,
+ TM_SHL,
+ TM_SHR,
+ TM_UNM,
+ TM_BNOT,
+ TM_LT,
+ TM_LE,
+ TM_CONCAT,
+ TM_CALL,
+ TM_CLOSE,
+ TM_N /* number of elements in the enum */
+} TMS;
+
+
+/*
+** Mask with 1 in all fast-access methods. A 1 in any of these bits
+** in the flag of a (meta)table means the metatable does not have the
+** corresponding metamethod field. (Bit 7 of the flag is used for
+** 'isrealasize'.)
+*/
+#define maskflags (~(~0u << (TM_EQ + 1)))
+
+
+/*
+** Test whether there is no tagmethod.
+** (Because tagmethods use raw accesses, the result may be an "empty" nil.)
+*/
+#define notm(tm) ttisnil(tm)
+
+
+#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
+ ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
+
+#define fasttm(l,et,e) gfasttm(G(l), et, e)
+
+#define ttypename(x) luaT_typenames_[(x) + 1]
+
+LUAI_DDEC(const char *const luaT_typenames_[LUA_TOTALTYPES];)
+
+
+LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o);
+
+LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename);
+LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o,
+ TMS event);
+LUAI_FUNC void luaT_init (lua_State *L);
+
+LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
+ const TValue *p2, const TValue *p3);
+LUAI_FUNC void luaT_callTMres (lua_State *L, const TValue *f,
+ const TValue *p1, const TValue *p2, StkId p3);
+LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
+ StkId res, TMS event);
+LUAI_FUNC void luaT_tryconcatTM (lua_State *L);
+LUAI_FUNC void luaT_trybinassocTM (lua_State *L, const TValue *p1,
+ const TValue *p2, int inv, StkId res, TMS event);
+LUAI_FUNC void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
+ int inv, StkId res, TMS event);
+LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1,
+ const TValue *p2, TMS event);
+LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
+ int inv, int isfloat, TMS event);
+
+LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
+ CallInfo *ci, const Proto *p);
+LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci,
+ StkId where, int wanted);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lua.c b/src/libs/3rdparty/lua/src/lua.c
new file mode 100644
index 0000000000..0ff8845453
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lua.c
@@ -0,0 +1,679 @@
+/*
+** $Id: lua.c $
+** Lua stand-alone interpreter
+** See Copyright Notice in lua.h
+*/
+
+#define lua_c
+
+#include "lprefix.h"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <signal.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+#if !defined(LUA_PROGNAME)
+#define LUA_PROGNAME "lua"
+#endif
+
+#if !defined(LUA_INIT_VAR)
+#define LUA_INIT_VAR "LUA_INIT"
+#endif
+
+#define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX
+
+
+static lua_State *globalL = NULL;
+
+static const char *progname = LUA_PROGNAME;
+
+
+#if defined(LUA_USE_POSIX) /* { */
+
+/*
+** Use 'sigaction' when available.
+*/
+static void setsignal (int sig, void (*handler)(int)) {
+ struct sigaction sa;
+ sa.sa_handler = handler;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask); /* do not mask any signal */
+ sigaction(sig, &sa, NULL);
+}
+
+#else /* }{ */
+
+#define setsignal signal
+
+#endif /* } */
+
+
+/*
+** Hook set by signal function to stop the interpreter.
+*/
+static void lstop (lua_State *L, lua_Debug *ar) {
+ (void)ar; /* unused arg. */
+ lua_sethook(L, NULL, 0, 0); /* reset hook */
+ luaL_error(L, "interrupted!");
+}
+
+
+/*
+** Function to be called at a C signal. Because a C signal cannot
+** just change a Lua state (as there is no proper synchronization),
+** this function only sets a hook that, when called, will stop the
+** interpreter.
+*/
+static void laction (int i) {
+ int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
+ setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
+ lua_sethook(globalL, lstop, flag, 1);
+}
+
+
+static void print_usage (const char *badoption) {
+ lua_writestringerror("%s: ", progname);
+ if (badoption[1] == 'e' || badoption[1] == 'l')
+ lua_writestringerror("'%s' needs argument\n", badoption);
+ else
+ lua_writestringerror("unrecognized option '%s'\n", badoption);
+ lua_writestringerror(
+ "usage: %s [options] [script [args]]\n"
+ "Available options are:\n"
+ " -e stat execute string 'stat'\n"
+ " -i enter interactive mode after executing 'script'\n"
+ " -l mod require library 'mod' into global 'mod'\n"
+ " -l g=mod require library 'mod' into global 'g'\n"
+ " -v show version information\n"
+ " -E ignore environment variables\n"
+ " -W turn warnings on\n"
+ " -- stop handling options\n"
+ " - stop handling options and execute stdin\n"
+ ,
+ progname);
+}
+
+
+/*
+** Prints an error message, adding the program name in front of it
+** (if present)
+*/
+static void l_message (const char *pname, const char *msg) {
+ if (pname) lua_writestringerror("%s: ", pname);
+ lua_writestringerror("%s\n", msg);
+}
+
+
+/*
+** Check whether 'status' is not OK and, if so, prints the error
+** message on the top of the stack. It assumes that the error object
+** is a string, as it was either generated by Lua or by 'msghandler'.
+*/
+static int report (lua_State *L, int status) {
+ if (status != LUA_OK) {
+ const char *msg = lua_tostring(L, -1);
+ l_message(progname, msg);
+ lua_pop(L, 1); /* remove message */
+ }
+ return status;
+}
+
+
+/*
+** Message handler used to run all chunks
+*/
+static int msghandler (lua_State *L) {
+ const char *msg = lua_tostring(L, 1);
+ if (msg == NULL) { /* is error object not a string? */
+ if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */
+ lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */
+ return 1; /* that is the message */
+ else
+ msg = lua_pushfstring(L, "(error object is a %s value)",
+ luaL_typename(L, 1));
+ }
+ luaL_traceback(L, L, msg, 1); /* append a standard traceback */
+ return 1; /* return the traceback */
+}
+
+
+/*
+** Interface to 'lua_pcall', which sets appropriate message function
+** and C-signal handler. Used to run all chunks.
+*/
+static int docall (lua_State *L, int narg, int nres) {
+ int status;
+ int base = lua_gettop(L) - narg; /* function index */
+ lua_pushcfunction(L, msghandler); /* push message handler */
+ lua_insert(L, base); /* put it under function and args */
+ globalL = L; /* to be available to 'laction' */
+ setsignal(SIGINT, laction); /* set C-signal handler */
+ status = lua_pcall(L, narg, nres, base);
+ setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */
+ lua_remove(L, base); /* remove message handler from the stack */
+ return status;
+}
+
+
+static void print_version (void) {
+ lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT));
+ lua_writeline();
+}
+
+
+/*
+** Create the 'arg' table, which stores all arguments from the
+** command line ('argv'). It should be aligned so that, at index 0,
+** it has 'argv[script]', which is the script name. The arguments
+** to the script (everything after 'script') go to positive indices;
+** other arguments (before the script name) go to negative indices.
+** If there is no script name, assume interpreter's name as base.
+** (If there is no interpreter's name either, 'script' is -1, so
+** table sizes are zero.)
+*/
+static void createargtable (lua_State *L, char **argv, int argc, int script) {
+ int i, narg;
+ narg = argc - (script + 1); /* number of positive indices */
+ lua_createtable(L, narg, script + 1);
+ for (i = 0; i < argc; i++) {
+ lua_pushstring(L, argv[i]);
+ lua_rawseti(L, -2, i - script);
+ }
+ lua_setglobal(L, "arg");
+}
+
+
+static int dochunk (lua_State *L, int status) {
+ if (status == LUA_OK) status = docall(L, 0, 0);
+ return report(L, status);
+}
+
+
+static int dofile (lua_State *L, const char *name) {
+ return dochunk(L, luaL_loadfile(L, name));
+}
+
+
+static int dostring (lua_State *L, const char *s, const char *name) {
+ return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name));
+}
+
+
+/*
+** Receives 'globname[=modname]' and runs 'globname = require(modname)'.
+*/
+static int dolibrary (lua_State *L, char *globname) {
+ int status;
+ char *modname = strchr(globname, '=');
+ if (modname == NULL) /* no explicit name? */
+ modname = globname; /* module name is equal to global name */
+ else {
+ *modname = '\0'; /* global name ends here */
+ modname++; /* module name starts after the '=' */
+ }
+ lua_getglobal(L, "require");
+ lua_pushstring(L, modname);
+ status = docall(L, 1, 1); /* call 'require(modname)' */
+ if (status == LUA_OK)
+ lua_setglobal(L, globname); /* globname = require(modname) */
+ return report(L, status);
+}
+
+
+/*
+** Push on the stack the contents of table 'arg' from 1 to #arg
+*/
+static int pushargs (lua_State *L) {
+ int i, n;
+ if (lua_getglobal(L, "arg") != LUA_TTABLE)
+ luaL_error(L, "'arg' is not a table");
+ n = (int)luaL_len(L, -1);
+ luaL_checkstack(L, n + 3, "too many arguments to script");
+ for (i = 1; i <= n; i++)
+ lua_rawgeti(L, -i, i);
+ lua_remove(L, -i); /* remove table from the stack */
+ return n;
+}
+
+
+static int handle_script (lua_State *L, char **argv) {
+ int status;
+ const char *fname = argv[0];
+ if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0)
+ fname = NULL; /* stdin */
+ status = luaL_loadfile(L, fname);
+ if (status == LUA_OK) {
+ int n = pushargs(L); /* push arguments to script */
+ status = docall(L, n, LUA_MULTRET);
+ }
+ return report(L, status);
+}
+
+
+/* bits of various argument indicators in 'args' */
+#define has_error 1 /* bad option */
+#define has_i 2 /* -i */
+#define has_v 4 /* -v */
+#define has_e 8 /* -e */
+#define has_E 16 /* -E */
+
+
+/*
+** Traverses all arguments from 'argv', returning a mask with those
+** needed before running any Lua code or an error code if it finds any
+** invalid argument. In case of error, 'first' is the index of the bad
+** argument. Otherwise, 'first' is -1 if there is no program name,
+** 0 if there is no script name, or the index of the script name.
+*/
+static int collectargs (char **argv, int *first) {
+ int args = 0;
+ int i;
+ if (argv[0] != NULL) { /* is there a program name? */
+ if (argv[0][0]) /* not empty? */
+ progname = argv[0]; /* save it */
+ }
+ else { /* no program name */
+ *first = -1;
+ return 0;
+ }
+ for (i = 1; argv[i] != NULL; i++) { /* handle arguments */
+ *first = i;
+ if (argv[i][0] != '-') /* not an option? */
+ return args; /* stop handling options */
+ switch (argv[i][1]) { /* else check option */
+ case '-': /* '--' */
+ if (argv[i][2] != '\0') /* extra characters after '--'? */
+ return has_error; /* invalid option */
+ *first = i + 1;
+ return args;
+ case '\0': /* '-' */
+ return args; /* script "name" is '-' */
+ case 'E':
+ if (argv[i][2] != '\0') /* extra characters? */
+ return has_error; /* invalid option */
+ args |= has_E;
+ break;
+ case 'W':
+ if (argv[i][2] != '\0') /* extra characters? */
+ return has_error; /* invalid option */
+ break;
+ case 'i':
+ args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */
+ case 'v':
+ if (argv[i][2] != '\0') /* extra characters? */
+ return has_error; /* invalid option */
+ args |= has_v;
+ break;
+ case 'e':
+ args |= has_e; /* FALLTHROUGH */
+ case 'l': /* both options need an argument */
+ if (argv[i][2] == '\0') { /* no concatenated argument? */
+ i++; /* try next 'argv' */
+ if (argv[i] == NULL || argv[i][0] == '-')
+ return has_error; /* no next argument or it is another option */
+ }
+ break;
+ default: /* invalid option */
+ return has_error;
+ }
+ }
+ *first = 0; /* no script name */
+ return args;
+}
+
+
+/*
+** Processes options 'e' and 'l', which involve running Lua code, and
+** 'W', which also affects the state.
+** Returns 0 if some code raises an error.
+*/
+static int runargs (lua_State *L, char **argv, int n) {
+ int i;
+ for (i = 1; i < n; i++) {
+ int option = argv[i][1];
+ lua_assert(argv[i][0] == '-'); /* already checked */
+ switch (option) {
+ case 'e': case 'l': {
+ int status;
+ char *extra = argv[i] + 2; /* both options need an argument */
+ if (*extra == '\0') extra = argv[++i];
+ lua_assert(extra != NULL);
+ status = (option == 'e')
+ ? dostring(L, extra, "=(command line)")
+ : dolibrary(L, extra);
+ if (status != LUA_OK) return 0;
+ break;
+ }
+ case 'W':
+ lua_warning(L, "@on", 0); /* warnings on */
+ break;
+ }
+ }
+ return 1;
+}
+
+
+static int handle_luainit (lua_State *L) {
+ const char *name = "=" LUA_INITVARVERSION;
+ const char *init = getenv(name + 1);
+ if (init == NULL) {
+ name = "=" LUA_INIT_VAR;
+ init = getenv(name + 1); /* try alternative name */
+ }
+ if (init == NULL) return LUA_OK;
+ else if (init[0] == '@')
+ return dofile(L, init+1);
+ else
+ return dostring(L, init, name);
+}
+
+
+/*
+** {==================================================================
+** Read-Eval-Print Loop (REPL)
+** ===================================================================
+*/
+
+#if !defined(LUA_PROMPT)
+#define LUA_PROMPT "> "
+#define LUA_PROMPT2 ">> "
+#endif
+
+#if !defined(LUA_MAXINPUT)
+#define LUA_MAXINPUT 512
+#endif
+
+
+/*
+** lua_stdin_is_tty detects whether the standard input is a 'tty' (that
+** is, whether we're running lua interactively).
+*/
+#if !defined(lua_stdin_is_tty) /* { */
+
+#if defined(LUA_USE_POSIX) /* { */
+
+#include <unistd.h>
+#define lua_stdin_is_tty() isatty(0)
+
+#elif defined(LUA_USE_WINDOWS) /* }{ */
+
+#include <io.h>
+#include <windows.h>
+
+#define lua_stdin_is_tty() _isatty(_fileno(stdin))
+
+#else /* }{ */
+
+/* ISO C definition */
+#define lua_stdin_is_tty() 1 /* assume stdin is a tty */
+
+#endif /* } */
+
+#endif /* } */
+
+
+/*
+** lua_readline defines how to show a prompt and then read a line from
+** the standard input.
+** lua_saveline defines how to "save" a read line in a "history".
+** lua_freeline defines how to free a line read by lua_readline.
+*/
+#if !defined(lua_readline) /* { */
+
+#if defined(LUA_USE_READLINE) /* { */
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#define lua_initreadline(L) ((void)L, rl_readline_name="lua")
+#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
+#define lua_saveline(L,line) ((void)L, add_history(line))
+#define lua_freeline(L,b) ((void)L, free(b))
+
+#else /* }{ */
+
+#define lua_initreadline(L) ((void)L)
+#define lua_readline(L,b,p) \
+ ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \
+ fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */
+#define lua_saveline(L,line) { (void)L; (void)line; }
+#define lua_freeline(L,b) { (void)L; (void)b; }
+
+#endif /* } */
+
+#endif /* } */
+
+
+/*
+** Return the string to be used as a prompt by the interpreter. Leave
+** the string (or nil, if using the default value) on the stack, to keep
+** it anchored.
+*/
+static const char *get_prompt (lua_State *L, int firstline) {
+ if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
+ return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */
+ else { /* apply 'tostring' over the value */
+ const char *p = luaL_tolstring(L, -1, NULL);
+ lua_remove(L, -2); /* remove original value */
+ return p;
+ }
+}
+
+/* mark in error messages for incomplete statements */
+#define EOFMARK "<eof>"
+#define marklen (sizeof(EOFMARK)/sizeof(char) - 1)
+
+
+/*
+** Check whether 'status' signals a syntax error and the error
+** message at the top of the stack ends with the above mark for
+** incomplete statements.
+*/
+static int incomplete (lua_State *L, int status) {
+ if (status == LUA_ERRSYNTAX) {
+ size_t lmsg;
+ const char *msg = lua_tolstring(L, -1, &lmsg);
+ if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) {
+ lua_pop(L, 1);
+ return 1;
+ }
+ }
+ return 0; /* else... */
+}
+
+
+/*
+** Prompt the user, read a line, and push it into the Lua stack.
+*/
+static int pushline (lua_State *L, int firstline) {
+ char buffer[LUA_MAXINPUT];
+ char *b = buffer;
+ size_t l;
+ const char *prmt = get_prompt(L, firstline);
+ int readstatus = lua_readline(L, b, prmt);
+ if (readstatus == 0)
+ return 0; /* no input (prompt will be popped by caller) */
+ lua_pop(L, 1); /* remove prompt */
+ l = strlen(b);
+ if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
+ b[--l] = '\0'; /* remove it */
+ if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */
+ lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */
+ else
+ lua_pushlstring(L, b, l);
+ lua_freeline(L, b);
+ return 1;
+}
+
+
+/*
+** Try to compile line on the stack as 'return <line>;'; on return, stack
+** has either compiled chunk or original line (if compilation failed).
+*/
+static int addreturn (lua_State *L) {
+ const char *line = lua_tostring(L, -1); /* original line */
+ const char *retline = lua_pushfstring(L, "return %s;", line);
+ int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin");
+ if (status == LUA_OK) {
+ lua_remove(L, -2); /* remove modified line */
+ if (line[0] != '\0') /* non empty? */
+ lua_saveline(L, line); /* keep history */
+ }
+ else
+ lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */
+ return status;
+}
+
+
+/*
+** Read multiple lines until a complete Lua statement
+*/
+static int multiline (lua_State *L) {
+ for (;;) { /* repeat until gets a complete statement */
+ size_t len;
+ const char *line = lua_tolstring(L, 1, &len); /* get what it has */
+ int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */
+ if (!incomplete(L, status) || !pushline(L, 0)) {
+ lua_saveline(L, line); /* keep history */
+ return status; /* cannot or should not try to add continuation line */
+ }
+ lua_pushliteral(L, "\n"); /* add newline... */
+ lua_insert(L, -2); /* ...between the two lines */
+ lua_concat(L, 3); /* join them */
+ }
+}
+
+
+/*
+** Read a line and try to load (compile) it first as an expression (by
+** adding "return " in front of it) and second as a statement. Return
+** the final status of load/call with the resulting function (if any)
+** in the top of the stack.
+*/
+static int loadline (lua_State *L) {
+ int status;
+ lua_settop(L, 0);
+ if (!pushline(L, 1))
+ return -1; /* no input */
+ if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */
+ status = multiline(L); /* try as command, maybe with continuation lines */
+ lua_remove(L, 1); /* remove line from the stack */
+ lua_assert(lua_gettop(L) == 1);
+ return status;
+}
+
+
+/*
+** Prints (calling the Lua 'print' function) any values on the stack
+*/
+static void l_print (lua_State *L) {
+ int n = lua_gettop(L);
+ if (n > 0) { /* any result to be printed? */
+ luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
+ lua_getglobal(L, "print");
+ lua_insert(L, 1);
+ if (lua_pcall(L, n, 0, 0) != LUA_OK)
+ l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)",
+ lua_tostring(L, -1)));
+ }
+}
+
+
+/*
+** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and
+** print any results.
+*/
+static void doREPL (lua_State *L) {
+ int status;
+ const char *oldprogname = progname;
+ progname = NULL; /* no 'progname' on errors in interactive mode */
+ lua_initreadline(L);
+ while ((status = loadline(L)) != -1) {
+ if (status == LUA_OK)
+ status = docall(L, 0, LUA_MULTRET);
+ if (status == LUA_OK) l_print(L);
+ else report(L, status);
+ }
+ lua_settop(L, 0); /* clear stack */
+ lua_writeline();
+ progname = oldprogname;
+}
+
+/* }================================================================== */
+
+
+/*
+** Main body of stand-alone interpreter (to be called in protected mode).
+** Reads the options and handles them all.
+*/
+static int pmain (lua_State *L) {
+ int argc = (int)lua_tointeger(L, 1);
+ char **argv = (char **)lua_touserdata(L, 2);
+ int script;
+ int args = collectargs(argv, &script);
+ int optlim = (script > 0) ? script : argc; /* first argv not an option */
+ luaL_checkversion(L); /* check that interpreter has correct version */
+ if (args == has_error) { /* bad arg? */
+ print_usage(argv[script]); /* 'script' has index of bad arg. */
+ return 0;
+ }
+ if (args & has_v) /* option '-v'? */
+ print_version();
+ if (args & has_E) { /* option '-E'? */
+ lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
+ lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
+ }
+ luaL_openlibs(L); /* open standard libraries */
+ createargtable(L, argv, argc, script); /* create table 'arg' */
+ lua_gc(L, LUA_GCRESTART); /* start GC... */
+ lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */
+ if (!(args & has_E)) { /* no option '-E'? */
+ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
+ return 0; /* error running LUA_INIT */
+ }
+ if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */
+ return 0; /* something failed */
+ if (script > 0) { /* execute main script (if there is one) */
+ if (handle_script(L, argv + script) != LUA_OK)
+ return 0; /* interrupt in case of error */
+ }
+ if (args & has_i) /* -i option? */
+ doREPL(L); /* do read-eval-print loop */
+ else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */
+ if (lua_stdin_is_tty()) { /* running in interactive mode? */
+ print_version();
+ doREPL(L); /* do read-eval-print loop */
+ }
+ else dofile(L, NULL); /* executes stdin as a file */
+ }
+ lua_pushboolean(L, 1); /* signal no errors */
+ return 1;
+}
+
+
+int main (int argc, char **argv) {
+ int status, result;
+ lua_State *L = luaL_newstate(); /* create state */
+ if (L == NULL) {
+ l_message(argv[0], "cannot create state: not enough memory");
+ return EXIT_FAILURE;
+ }
+ lua_gc(L, LUA_GCSTOP); /* stop GC while building state */
+ lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
+ lua_pushinteger(L, argc); /* 1st argument */
+ lua_pushlightuserdata(L, argv); /* 2nd argument */
+ status = lua_pcall(L, 2, 1, 0); /* do the call */
+ result = lua_toboolean(L, -1); /* get result */
+ report(L, status);
+ lua_close(L);
+ return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lua.h b/src/libs/3rdparty/lua/src/lua.h
new file mode 100644
index 0000000000..fd16cf8050
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lua.h
@@ -0,0 +1,523 @@
+/*
+** $Id: lua.h $
+** Lua - A Scripting Language
+** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
+** See Copyright Notice at the end of this file
+*/
+
+
+#ifndef lua_h
+#define lua_h
+
+#include <stdarg.h>
+#include <stddef.h>
+
+
+#include "luaconf.h"
+
+
+#define LUA_VERSION_MAJOR "5"
+#define LUA_VERSION_MINOR "4"
+#define LUA_VERSION_RELEASE "6"
+
+#define LUA_VERSION_NUM 504
+#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 6)
+
+#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
+#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
+#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio"
+#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
+
+
+/* mark for precompiled code ('<esc>Lua') */
+#define LUA_SIGNATURE "\x1bLua"
+
+/* option for multiple returns in 'lua_pcall' and 'lua_call' */
+#define LUA_MULTRET (-1)
+
+
+/*
+** Pseudo-indices
+** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty
+** space after that to help overflow detection)
+*/
+#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000)
+#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
+
+
+/* thread status */
+#define LUA_OK 0
+#define LUA_YIELD 1
+#define LUA_ERRRUN 2
+#define LUA_ERRSYNTAX 3
+#define LUA_ERRMEM 4
+#define LUA_ERRERR 5
+
+
+typedef struct lua_State lua_State;
+
+
+/*
+** basic types
+*/
+#define LUA_TNONE (-1)
+
+#define LUA_TNIL 0
+#define LUA_TBOOLEAN 1
+#define LUA_TLIGHTUSERDATA 2
+#define LUA_TNUMBER 3
+#define LUA_TSTRING 4
+#define LUA_TTABLE 5
+#define LUA_TFUNCTION 6
+#define LUA_TUSERDATA 7
+#define LUA_TTHREAD 8
+
+#define LUA_NUMTYPES 9
+
+
+
+/* minimum Lua stack available to a C function */
+#define LUA_MINSTACK 20
+
+
+/* predefined values in the registry */
+#define LUA_RIDX_MAINTHREAD 1
+#define LUA_RIDX_GLOBALS 2
+#define LUA_RIDX_LAST LUA_RIDX_GLOBALS
+
+
+/* type of numbers in Lua */
+typedef LUA_NUMBER lua_Number;
+
+
+/* type for integer functions */
+typedef LUA_INTEGER lua_Integer;
+
+/* unsigned integer type */
+typedef LUA_UNSIGNED lua_Unsigned;
+
+/* type for continuation-function contexts */
+typedef LUA_KCONTEXT lua_KContext;
+
+
+/*
+** Type for C functions registered with Lua
+*/
+typedef int (*lua_CFunction) (lua_State *L);
+
+/*
+** Type for continuation functions
+*/
+typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);
+
+
+/*
+** Type for functions that read/write blocks when loading/dumping Lua chunks
+*/
+typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
+
+typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud);
+
+
+/*
+** Type for memory-allocation functions
+*/
+typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
+
+
+/*
+** Type for warning functions
+*/
+typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);
+
+
+/*
+** Type used by the debug API to collect debug information
+*/
+typedef struct lua_Debug lua_Debug;
+
+
+/*
+** Functions to be called by the debugger in specific events
+*/
+typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
+
+
+/*
+** generic extra include file
+*/
+#if defined(LUA_USER_H)
+#include LUA_USER_H
+#endif
+
+
+/*
+** RCS ident string
+*/
+extern const char lua_ident[];
+
+
+/*
+** state manipulation
+*/
+LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
+LUA_API void (lua_close) (lua_State *L);
+LUA_API lua_State *(lua_newthread) (lua_State *L);
+LUA_API int (lua_closethread) (lua_State *L, lua_State *from);
+LUA_API int (lua_resetthread) (lua_State *L); /* Deprecated! */
+
+LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
+
+
+LUA_API lua_Number (lua_version) (lua_State *L);
+
+
+/*
+** basic stack manipulation
+*/
+LUA_API int (lua_absindex) (lua_State *L, int idx);
+LUA_API int (lua_gettop) (lua_State *L);
+LUA_API void (lua_settop) (lua_State *L, int idx);
+LUA_API void (lua_pushvalue) (lua_State *L, int idx);
+LUA_API void (lua_rotate) (lua_State *L, int idx, int n);
+LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx);
+LUA_API int (lua_checkstack) (lua_State *L, int n);
+
+LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
+
+
+/*
+** access functions (stack -> C)
+*/
+
+LUA_API int (lua_isnumber) (lua_State *L, int idx);
+LUA_API int (lua_isstring) (lua_State *L, int idx);
+LUA_API int (lua_iscfunction) (lua_State *L, int idx);
+LUA_API int (lua_isinteger) (lua_State *L, int idx);
+LUA_API int (lua_isuserdata) (lua_State *L, int idx);
+LUA_API int (lua_type) (lua_State *L, int idx);
+LUA_API const char *(lua_typename) (lua_State *L, int tp);
+
+LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum);
+LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum);
+LUA_API int (lua_toboolean) (lua_State *L, int idx);
+LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
+LUA_API lua_Unsigned (lua_rawlen) (lua_State *L, int idx);
+LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
+LUA_API void *(lua_touserdata) (lua_State *L, int idx);
+LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
+LUA_API const void *(lua_topointer) (lua_State *L, int idx);
+
+
+/*
+** Comparison and arithmetic functions
+*/
+
+#define LUA_OPADD 0 /* ORDER TM, ORDER OP */
+#define LUA_OPSUB 1
+#define LUA_OPMUL 2
+#define LUA_OPMOD 3
+#define LUA_OPPOW 4
+#define LUA_OPDIV 5
+#define LUA_OPIDIV 6
+#define LUA_OPBAND 7
+#define LUA_OPBOR 8
+#define LUA_OPBXOR 9
+#define LUA_OPSHL 10
+#define LUA_OPSHR 11
+#define LUA_OPUNM 12
+#define LUA_OPBNOT 13
+
+LUA_API void (lua_arith) (lua_State *L, int op);
+
+#define LUA_OPEQ 0
+#define LUA_OPLT 1
+#define LUA_OPLE 2
+
+LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op);
+
+
+/*
+** push functions (C -> stack)
+*/
+LUA_API void (lua_pushnil) (lua_State *L);
+LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
+LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
+LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
+LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
+LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
+ va_list argp);
+LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
+LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
+LUA_API void (lua_pushboolean) (lua_State *L, int b);
+LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
+LUA_API int (lua_pushthread) (lua_State *L);
+
+
+/*
+** get functions (Lua -> stack)
+*/
+LUA_API int (lua_getglobal) (lua_State *L, const char *name);
+LUA_API int (lua_gettable) (lua_State *L, int idx);
+LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
+LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
+LUA_API int (lua_rawget) (lua_State *L, int idx);
+LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
+LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);
+
+LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
+LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue);
+LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
+LUA_API int (lua_getiuservalue) (lua_State *L, int idx, int n);
+
+
+/*
+** set functions (stack -> Lua)
+*/
+LUA_API void (lua_setglobal) (lua_State *L, const char *name);
+LUA_API void (lua_settable) (lua_State *L, int idx);
+LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n);
+LUA_API void (lua_rawset) (lua_State *L, int idx);
+LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
+LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p);
+LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
+LUA_API int (lua_setiuservalue) (lua_State *L, int idx, int n);
+
+
+/*
+** 'load' and 'call' functions (load and run Lua code)
+*/
+LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults,
+ lua_KContext ctx, lua_KFunction k);
+#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL)
+
+LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc,
+ lua_KContext ctx, lua_KFunction k);
+#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL)
+
+LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
+ const char *chunkname, const char *mode);
+
+LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip);
+
+
+/*
+** coroutine functions
+*/
+LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx,
+ lua_KFunction k);
+LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg,
+ int *nres);
+LUA_API int (lua_status) (lua_State *L);
+LUA_API int (lua_isyieldable) (lua_State *L);
+
+#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
+
+
+/*
+** Warning-related functions
+*/
+LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud);
+LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont);
+
+
+/*
+** garbage-collection function and options
+*/
+
+#define LUA_GCSTOP 0
+#define LUA_GCRESTART 1
+#define LUA_GCCOLLECT 2
+#define LUA_GCCOUNT 3
+#define LUA_GCCOUNTB 4
+#define LUA_GCSTEP 5
+#define LUA_GCSETPAUSE 6
+#define LUA_GCSETSTEPMUL 7
+#define LUA_GCISRUNNING 9
+#define LUA_GCGEN 10
+#define LUA_GCINC 11
+
+LUA_API int (lua_gc) (lua_State *L, int what, ...);
+
+
+/*
+** miscellaneous functions
+*/
+
+LUA_API int (lua_error) (lua_State *L);
+
+LUA_API int (lua_next) (lua_State *L, int idx);
+
+LUA_API void (lua_concat) (lua_State *L, int n);
+LUA_API void (lua_len) (lua_State *L, int idx);
+
+LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
+
+LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
+LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
+
+LUA_API void (lua_toclose) (lua_State *L, int idx);
+LUA_API void (lua_closeslot) (lua_State *L, int idx);
+
+
+/*
+** {==============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE))
+
+#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL)
+#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL)
+
+#define lua_pop(L,n) lua_settop(L, -(n)-1)
+
+#define lua_newtable(L) lua_createtable(L, 0, 0)
+
+#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
+
+#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
+
+#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
+#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
+#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
+#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
+#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
+#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
+#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
+#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
+
+#define lua_pushliteral(L, s) lua_pushstring(L, "" s)
+
+#define lua_pushglobaltable(L) \
+ ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS))
+
+#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
+
+
+#define lua_insert(L,idx) lua_rotate(L, (idx), 1)
+
+#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1))
+
+#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1))
+
+/* }============================================================== */
+
+
+/*
+** {==============================================================
+** compatibility macros
+** ===============================================================
+*/
+#if defined(LUA_COMPAT_APIINTCASTS)
+
+#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n))
+#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is))
+#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL)
+
+#endif
+
+#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1)
+#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1)
+#define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1)
+
+#define LUA_NUMTAGS LUA_NUMTYPES
+
+/* }============================================================== */
+
+/*
+** {======================================================================
+** Debug API
+** =======================================================================
+*/
+
+
+/*
+** Event codes
+*/
+#define LUA_HOOKCALL 0
+#define LUA_HOOKRET 1
+#define LUA_HOOKLINE 2
+#define LUA_HOOKCOUNT 3
+#define LUA_HOOKTAILCALL 4
+
+
+/*
+** Event masks
+*/
+#define LUA_MASKCALL (1 << LUA_HOOKCALL)
+#define LUA_MASKRET (1 << LUA_HOOKRET)
+#define LUA_MASKLINE (1 << LUA_HOOKLINE)
+#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
+
+
+LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
+LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
+LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n);
+LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n);
+
+LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n);
+LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1,
+ int fidx2, int n2);
+
+LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count);
+LUA_API lua_Hook (lua_gethook) (lua_State *L);
+LUA_API int (lua_gethookmask) (lua_State *L);
+LUA_API int (lua_gethookcount) (lua_State *L);
+
+LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit);
+
+struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */
+ const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */
+ const char *source; /* (S) */
+ size_t srclen; /* (S) */
+ int currentline; /* (l) */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ unsigned char nups; /* (u) number of upvalues */
+ unsigned char nparams;/* (u) number of parameters */
+ char isvararg; /* (u) */
+ char istailcall; /* (t) */
+ unsigned short ftransfer; /* (r) index of first value transferred */
+ unsigned short ntransfer; /* (r) number of transferred values */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ struct CallInfo *i_ci; /* active function */
+};
+
+/* }====================================================================== */
+
+
+/******************************************************************************
+* Copyright (C) 1994-2023 Lua.org, PUC-Rio.
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+******************************************************************************/
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lua.hpp b/src/libs/3rdparty/lua/src/lua.hpp
new file mode 100644
index 0000000000..ec417f5946
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lua.hpp
@@ -0,0 +1,9 @@
+// lua.hpp
+// Lua header files for C++
+// <<extern "C">> not supplied automatically because Lua also compiles as C++
+
+extern "C" {
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+}
diff --git a/src/libs/3rdparty/lua/src/luac.c b/src/libs/3rdparty/lua/src/luac.c
new file mode 100644
index 0000000000..5f4a141bdd
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/luac.c
@@ -0,0 +1,723 @@
+/*
+** $Id: luac.c $
+** Lua compiler (saves bytecodes to files; also lists bytecodes)
+** See Copyright Notice in lua.h
+*/
+
+#define luac_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "ldebug.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lopnames.h"
+#include "lstate.h"
+#include "lundump.h"
+
+static void PrintFunction(const Proto* f, int full);
+#define luaU_print PrintFunction
+
+#define PROGNAME "luac" /* default program name */
+#define OUTPUT PROGNAME ".out" /* default output file */
+
+static int listing=0; /* list bytecodes? */
+static int dumping=1; /* dump bytecodes? */
+static int stripping=0; /* strip debug information? */
+static char Output[]={ OUTPUT }; /* default output file name */
+static const char* output=Output; /* actual output file name */
+static const char* progname=PROGNAME; /* actual program name */
+static TString **tmname;
+
+static void fatal(const char* message)
+{
+ fprintf(stderr,"%s: %s\n",progname,message);
+ exit(EXIT_FAILURE);
+}
+
+static void cannot(const char* what)
+{
+ fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+static void usage(const char* message)
+{
+ if (*message=='-')
+ fprintf(stderr,"%s: unrecognized option '%s'\n",progname,message);
+ else
+ fprintf(stderr,"%s: %s\n",progname,message);
+ fprintf(stderr,
+ "usage: %s [options] [filenames]\n"
+ "Available options are:\n"
+ " -l list (use -l -l for full listing)\n"
+ " -o name output to file 'name' (default is \"%s\")\n"
+ " -p parse only\n"
+ " -s strip debug information\n"
+ " -v show version information\n"
+ " -- stop handling options\n"
+ " - stop handling options and process stdin\n"
+ ,progname,Output);
+ exit(EXIT_FAILURE);
+}
+
+#define IS(s) (strcmp(argv[i],s)==0)
+
+static int doargs(int argc, char* argv[])
+{
+ int i;
+ int version=0;
+ if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0];
+ for (i=1; i<argc; i++)
+ {
+ if (*argv[i]!='-') /* end of options; keep it */
+ break;
+ else if (IS("--")) /* end of options; skip it */
+ {
+ ++i;
+ if (version) ++version;
+ break;
+ }
+ else if (IS("-")) /* end of options; use stdin */
+ break;
+ else if (IS("-l")) /* list */
+ ++listing;
+ else if (IS("-o")) /* output file */
+ {
+ output=argv[++i];
+ if (output==NULL || *output==0 || (*output=='-' && output[1]!=0))
+ usage("'-o' needs argument");
+ if (IS("-")) output=NULL;
+ }
+ else if (IS("-p")) /* parse only */
+ dumping=0;
+ else if (IS("-s")) /* strip debug information */
+ stripping=1;
+ else if (IS("-v")) /* show version */
+ ++version;
+ else /* unknown option */
+ usage(argv[i]);
+ }
+ if (i==argc && (listing || !dumping))
+ {
+ dumping=0;
+ argv[--i]=Output;
+ }
+ if (version)
+ {
+ printf("%s\n",LUA_COPYRIGHT);
+ if (version==argc-1) exit(EXIT_SUCCESS);
+ }
+ return i;
+}
+
+#define FUNCTION "(function()end)();\n"
+
+static const char* reader(lua_State* L, void* ud, size_t* size)
+{
+ UNUSED(L);
+ if ((*(int*)ud)--)
+ {
+ *size=sizeof(FUNCTION)-1;
+ return FUNCTION;
+ }
+ else
+ {
+ *size=0;
+ return NULL;
+ }
+}
+
+#define toproto(L,i) getproto(s2v(L->top.p+(i)))
+
+static const Proto* combine(lua_State* L, int n)
+{
+ if (n==1)
+ return toproto(L,-1);
+ else
+ {
+ Proto* f;
+ int i=n;
+ if (lua_load(L,reader,&i,"=(" PROGNAME ")",NULL)!=LUA_OK) fatal(lua_tostring(L,-1));
+ f=toproto(L,-1);
+ for (i=0; i<n; i++)
+ {
+ f->p[i]=toproto(L,i-n-1);
+ if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0;
+ }
+ return f;
+ }
+}
+
+static int writer(lua_State* L, const void* p, size_t size, void* u)
+{
+ UNUSED(L);
+ return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);
+}
+
+static int pmain(lua_State* L)
+{
+ int argc=(int)lua_tointeger(L,1);
+ char** argv=(char**)lua_touserdata(L,2);
+ const Proto* f;
+ int i;
+ tmname=G(L)->tmname;
+ if (!lua_checkstack(L,argc)) fatal("too many input files");
+ for (i=0; i<argc; i++)
+ {
+ const char* filename=IS("-") ? NULL : argv[i];
+ if (luaL_loadfile(L,filename)!=LUA_OK) fatal(lua_tostring(L,-1));
+ }
+ f=combine(L,argc);
+ if (listing) luaU_print(f,listing>1);
+ if (dumping)
+ {
+ FILE* D= (output==NULL) ? stdout : fopen(output,"wb");
+ if (D==NULL) cannot("open");
+ lua_lock(L);
+ luaU_dump(L,f,writer,D,stripping);
+ lua_unlock(L);
+ if (ferror(D)) cannot("write");
+ if (fclose(D)) cannot("close");
+ }
+ return 0;
+}
+
+int main(int argc, char* argv[])
+{
+ lua_State* L;
+ int i=doargs(argc,argv);
+ argc-=i; argv+=i;
+ if (argc<=0) usage("no input files given");
+ L=luaL_newstate();
+ if (L==NULL) fatal("cannot create state: not enough memory");
+ lua_pushcfunction(L,&pmain);
+ lua_pushinteger(L,argc);
+ lua_pushlightuserdata(L,argv);
+ if (lua_pcall(L,2,0,0)!=LUA_OK) fatal(lua_tostring(L,-1));
+ lua_close(L);
+ return EXIT_SUCCESS;
+}
+
+/*
+** print bytecodes
+*/
+
+#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-")
+#define VOID(p) ((const void*)(p))
+#define eventname(i) (getstr(tmname[i]))
+
+static void PrintString(const TString* ts)
+{
+ const char* s=getstr(ts);
+ size_t i,n=tsslen(ts);
+ printf("\"");
+ for (i=0; i<n; i++)
+ {
+ int c=(int)(unsigned char)s[i];
+ switch (c)
+ {
+ case '"':
+ printf("\\\"");
+ break;
+ case '\\':
+ printf("\\\\");
+ break;
+ case '\a':
+ printf("\\a");
+ break;
+ case '\b':
+ printf("\\b");
+ break;
+ case '\f':
+ printf("\\f");
+ break;
+ case '\n':
+ printf("\\n");
+ break;
+ case '\r':
+ printf("\\r");
+ break;
+ case '\t':
+ printf("\\t");
+ break;
+ case '\v':
+ printf("\\v");
+ break;
+ default:
+ if (isprint(c)) printf("%c",c); else printf("\\%03d",c);
+ break;
+ }
+ }
+ printf("\"");
+}
+
+static void PrintType(const Proto* f, int i)
+{
+ const TValue* o=&f->k[i];
+ switch (ttypetag(o))
+ {
+ case LUA_VNIL:
+ printf("N");
+ break;
+ case LUA_VFALSE:
+ case LUA_VTRUE:
+ printf("B");
+ break;
+ case LUA_VNUMFLT:
+ printf("F");
+ break;
+ case LUA_VNUMINT:
+ printf("I");
+ break;
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR:
+ printf("S");
+ break;
+ default: /* cannot happen */
+ printf("?%d",ttypetag(o));
+ break;
+ }
+ printf("\t");
+}
+
+static void PrintConstant(const Proto* f, int i)
+{
+ const TValue* o=&f->k[i];
+ switch (ttypetag(o))
+ {
+ case LUA_VNIL:
+ printf("nil");
+ break;
+ case LUA_VFALSE:
+ printf("false");
+ break;
+ case LUA_VTRUE:
+ printf("true");
+ break;
+ case LUA_VNUMFLT:
+ {
+ char buff[100];
+ sprintf(buff,LUA_NUMBER_FMT,fltvalue(o));
+ printf("%s",buff);
+ if (buff[strspn(buff,"-0123456789")]=='\0') printf(".0");
+ break;
+ }
+ case LUA_VNUMINT:
+ printf(LUA_INTEGER_FMT,ivalue(o));
+ break;
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR:
+ PrintString(tsvalue(o));
+ break;
+ default: /* cannot happen */
+ printf("?%d",ttypetag(o));
+ break;
+ }
+}
+
+#define COMMENT "\t; "
+#define EXTRAARG GETARG_Ax(code[pc+1])
+#define EXTRAARGC (EXTRAARG*(MAXARG_C+1))
+#define ISK (isk ? "k" : "")
+
+static void PrintCode(const Proto* f)
+{
+ const Instruction* code=f->code;
+ int pc,n=f->sizecode;
+ for (pc=0; pc<n; pc++)
+ {
+ Instruction i=code[pc];
+ OpCode o=GET_OPCODE(i);
+ int a=GETARG_A(i);
+ int b=GETARG_B(i);
+ int c=GETARG_C(i);
+ int ax=GETARG_Ax(i);
+ int bx=GETARG_Bx(i);
+ int sb=GETARG_sB(i);
+ int sc=GETARG_sC(i);
+ int sbx=GETARG_sBx(i);
+ int isk=GETARG_k(i);
+ int line=luaG_getfuncline(f,pc);
+ printf("\t%d\t",pc+1);
+ if (line>0) printf("[%d]\t",line); else printf("[-]\t");
+ printf("%-9s\t",opnames[o]);
+ switch (o)
+ {
+ case OP_MOVE:
+ printf("%d %d",a,b);
+ break;
+ case OP_LOADI:
+ printf("%d %d",a,sbx);
+ break;
+ case OP_LOADF:
+ printf("%d %d",a,sbx);
+ break;
+ case OP_LOADK:
+ printf("%d %d",a,bx);
+ printf(COMMENT); PrintConstant(f,bx);
+ break;
+ case OP_LOADKX:
+ printf("%d",a);
+ printf(COMMENT); PrintConstant(f,EXTRAARG);
+ break;
+ case OP_LOADFALSE:
+ printf("%d",a);
+ break;
+ case OP_LFALSESKIP:
+ printf("%d",a);
+ break;
+ case OP_LOADTRUE:
+ printf("%d",a);
+ break;
+ case OP_LOADNIL:
+ printf("%d %d",a,b);
+ printf(COMMENT "%d out",b+1);
+ break;
+ case OP_GETUPVAL:
+ printf("%d %d",a,b);
+ printf(COMMENT "%s",UPVALNAME(b));
+ break;
+ case OP_SETUPVAL:
+ printf("%d %d",a,b);
+ printf(COMMENT "%s",UPVALNAME(b));
+ break;
+ case OP_GETTABUP:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT "%s",UPVALNAME(b));
+ printf(" "); PrintConstant(f,c);
+ break;
+ case OP_GETTABLE:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_GETI:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_GETFIELD:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_SETTABUP:
+ printf("%d %d %d%s",a,b,c,ISK);
+ printf(COMMENT "%s",UPVALNAME(a));
+ printf(" "); PrintConstant(f,b);
+ if (isk) { printf(" "); PrintConstant(f,c); }
+ break;
+ case OP_SETTABLE:
+ printf("%d %d %d%s",a,b,c,ISK);
+ if (isk) { printf(COMMENT); PrintConstant(f,c); }
+ break;
+ case OP_SETI:
+ printf("%d %d %d%s",a,b,c,ISK);
+ if (isk) { printf(COMMENT); PrintConstant(f,c); }
+ break;
+ case OP_SETFIELD:
+ printf("%d %d %d%s",a,b,c,ISK);
+ printf(COMMENT); PrintConstant(f,b);
+ if (isk) { printf(" "); PrintConstant(f,c); }
+ break;
+ case OP_NEWTABLE:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT "%d",c+EXTRAARGC);
+ break;
+ case OP_SELF:
+ printf("%d %d %d%s",a,b,c,ISK);
+ if (isk) { printf(COMMENT); PrintConstant(f,c); }
+ break;
+ case OP_ADDI:
+ printf("%d %d %d",a,b,sc);
+ break;
+ case OP_ADDK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_SUBK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_MULK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_MODK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_POWK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_DIVK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_IDIVK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_BANDK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_BORK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_BXORK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_SHRI:
+ printf("%d %d %d",a,b,sc);
+ break;
+ case OP_SHLI:
+ printf("%d %d %d",a,b,sc);
+ break;
+ case OP_ADD:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_SUB:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_MUL:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_MOD:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_POW:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_DIV:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_IDIV:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_BAND:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_BOR:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_BXOR:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_SHL:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_SHR:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_MMBIN:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT "%s",eventname(c));
+ break;
+ case OP_MMBINI:
+ printf("%d %d %d %d",a,sb,c,isk);
+ printf(COMMENT "%s",eventname(c));
+ if (isk) printf(" flip");
+ break;
+ case OP_MMBINK:
+ printf("%d %d %d %d",a,b,c,isk);
+ printf(COMMENT "%s ",eventname(c)); PrintConstant(f,b);
+ if (isk) printf(" flip");
+ break;
+ case OP_UNM:
+ printf("%d %d",a,b);
+ break;
+ case OP_BNOT:
+ printf("%d %d",a,b);
+ break;
+ case OP_NOT:
+ printf("%d %d",a,b);
+ break;
+ case OP_LEN:
+ printf("%d %d",a,b);
+ break;
+ case OP_CONCAT:
+ printf("%d %d",a,b);
+ break;
+ case OP_CLOSE:
+ printf("%d",a);
+ break;
+ case OP_TBC:
+ printf("%d",a);
+ break;
+ case OP_JMP:
+ printf("%d",GETARG_sJ(i));
+ printf(COMMENT "to %d",GETARG_sJ(i)+pc+2);
+ break;
+ case OP_EQ:
+ printf("%d %d %d",a,b,isk);
+ break;
+ case OP_LT:
+ printf("%d %d %d",a,b,isk);
+ break;
+ case OP_LE:
+ printf("%d %d %d",a,b,isk);
+ break;
+ case OP_EQK:
+ printf("%d %d %d",a,b,isk);
+ printf(COMMENT); PrintConstant(f,b);
+ break;
+ case OP_EQI:
+ printf("%d %d %d",a,sb,isk);
+ break;
+ case OP_LTI:
+ printf("%d %d %d",a,sb,isk);
+ break;
+ case OP_LEI:
+ printf("%d %d %d",a,sb,isk);
+ break;
+ case OP_GTI:
+ printf("%d %d %d",a,sb,isk);
+ break;
+ case OP_GEI:
+ printf("%d %d %d",a,sb,isk);
+ break;
+ case OP_TEST:
+ printf("%d %d",a,isk);
+ break;
+ case OP_TESTSET:
+ printf("%d %d %d",a,b,isk);
+ break;
+ case OP_CALL:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT);
+ if (b==0) printf("all in "); else printf("%d in ",b-1);
+ if (c==0) printf("all out"); else printf("%d out",c-1);
+ break;
+ case OP_TAILCALL:
+ printf("%d %d %d%s",a,b,c,ISK);
+ printf(COMMENT "%d in",b-1);
+ break;
+ case OP_RETURN:
+ printf("%d %d %d%s",a,b,c,ISK);
+ printf(COMMENT);
+ if (b==0) printf("all out"); else printf("%d out",b-1);
+ break;
+ case OP_RETURN0:
+ break;
+ case OP_RETURN1:
+ printf("%d",a);
+ break;
+ case OP_FORLOOP:
+ printf("%d %d",a,bx);
+ printf(COMMENT "to %d",pc-bx+2);
+ break;
+ case OP_FORPREP:
+ printf("%d %d",a,bx);
+ printf(COMMENT "exit to %d",pc+bx+3);
+ break;
+ case OP_TFORPREP:
+ printf("%d %d",a,bx);
+ printf(COMMENT "to %d",pc+bx+2);
+ break;
+ case OP_TFORCALL:
+ printf("%d %d",a,c);
+ break;
+ case OP_TFORLOOP:
+ printf("%d %d",a,bx);
+ printf(COMMENT "to %d",pc-bx+2);
+ break;
+ case OP_SETLIST:
+ printf("%d %d %d",a,b,c);
+ if (isk) printf(COMMENT "%d",c+EXTRAARGC);
+ break;
+ case OP_CLOSURE:
+ printf("%d %d",a,bx);
+ printf(COMMENT "%p",VOID(f->p[bx]));
+ break;
+ case OP_VARARG:
+ printf("%d %d",a,c);
+ printf(COMMENT);
+ if (c==0) printf("all out"); else printf("%d out",c-1);
+ break;
+ case OP_VARARGPREP:
+ printf("%d",a);
+ break;
+ case OP_EXTRAARG:
+ printf("%d",ax);
+ break;
+#if 0
+ default:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT "not handled");
+ break;
+#endif
+ }
+ printf("\n");
+ }
+}
+
+
+#define SS(x) ((x==1)?"":"s")
+#define S(x) (int)(x),SS(x)
+
+static void PrintHeader(const Proto* f)
+{
+ const char* s=f->source ? getstr(f->source) : "=?";
+ if (*s=='@' || *s=='=')
+ s++;
+ else if (*s==LUA_SIGNATURE[0])
+ s="(bstring)";
+ else
+ s="(string)";
+ printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n",
+ (f->linedefined==0)?"main":"function",s,
+ f->linedefined,f->lastlinedefined,
+ S(f->sizecode),VOID(f));
+ printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
+ (int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams),
+ S(f->maxstacksize),S(f->sizeupvalues));
+ printf("%d local%s, %d constant%s, %d function%s\n",
+ S(f->sizelocvars),S(f->sizek),S(f->sizep));
+}
+
+static void PrintDebug(const Proto* f)
+{
+ int i,n;
+ n=f->sizek;
+ printf("constants (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t",i);
+ PrintType(f,i);
+ PrintConstant(f,i);
+ printf("\n");
+ }
+ n=f->sizelocvars;
+ printf("locals (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t%s\t%d\t%d\n",
+ i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);
+ }
+ n=f->sizeupvalues;
+ printf("upvalues (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t%s\t%d\t%d\n",
+ i,UPVALNAME(i),f->upvalues[i].instack,f->upvalues[i].idx);
+ }
+}
+
+static void PrintFunction(const Proto* f, int full)
+{
+ int i,n=f->sizep;
+ PrintHeader(f);
+ PrintCode(f);
+ if (full) PrintDebug(f);
+ for (i=0; i<n; i++) PrintFunction(f->p[i],full);
+}
diff --git a/src/libs/3rdparty/lua/src/luaconf.h b/src/libs/3rdparty/lua/src/luaconf.h
new file mode 100644
index 0000000000..137103edec
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/luaconf.h
@@ -0,0 +1,793 @@
+/*
+** $Id: luaconf.h $
+** Configuration file for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef luaconf_h
+#define luaconf_h
+
+#include <limits.h>
+#include <stddef.h>
+
+
+/*
+** ===================================================================
+** General Configuration File for Lua
+**
+** Some definitions here can be changed externally, through the compiler
+** (e.g., with '-D' options): They are commented out or protected
+** by '#if !defined' guards. However, several other definitions
+** should be changed directly here, either because they affect the
+** Lua ABI (by making the changes here, you ensure that all software
+** connected to Lua, such as C libraries, will be compiled with the same
+** configuration); or because they are seldom changed.
+**
+** Search for "@@" to find all configurable definitions.
+** ===================================================================
+*/
+
+
+/*
+** {====================================================================
+** System Configuration: macros to adapt (if needed) Lua to some
+** particular platform, for instance restricting it to C89.
+** =====================================================================
+*/
+
+/*
+@@ LUA_USE_C89 controls the use of non-ISO-C89 features.
+** Define it if you want Lua to avoid the use of a few C99 features
+** or Windows-specific features on Windows.
+*/
+/* #define LUA_USE_C89 */
+
+
+/*
+** By default, Lua on Windows use (some) specific Windows features
+*/
+#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE)
+#define LUA_USE_WINDOWS /* enable goodies for regular Windows */
+#endif
+
+
+#if defined(LUA_USE_WINDOWS)
+#define LUA_DL_DLL /* enable support for DLL */
+#define LUA_USE_C89 /* broadly, Windows is C89 */
+#endif
+
+
+#if defined(LUA_USE_LINUX)
+#define LUA_USE_POSIX
+#define LUA_USE_DLOPEN /* needs an extra library: -ldl */
+#endif
+
+
+#if defined(LUA_USE_MACOSX)
+#define LUA_USE_POSIX
+#define LUA_USE_DLOPEN /* MacOS does not need -ldl */
+#endif
+
+
+#if defined(LUA_USE_IOS)
+#define LUA_USE_POSIX
+#define LUA_USE_DLOPEN
+#endif
+
+
+/*
+@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits.
+*/
+#define LUAI_IS32INT ((UINT_MAX >> 30) >= 3)
+
+/* }================================================================== */
+
+
+
+/*
+** {==================================================================
+** Configuration for Number types. These options should not be
+** set externally, because any other code connected to Lua must
+** use the same configuration.
+** ===================================================================
+*/
+
+/*
+@@ LUA_INT_TYPE defines the type for Lua integers.
+@@ LUA_FLOAT_TYPE defines the type for Lua floats.
+** Lua should work fine with any mix of these options supported
+** by your C compiler. The usual configurations are 64-bit integers
+** and 'double' (the default), 32-bit integers and 'float' (for
+** restricted platforms), and 'long'/'double' (for C compilers not
+** compliant with C99, which may not have support for 'long long').
+*/
+
+/* predefined options for LUA_INT_TYPE */
+#define LUA_INT_INT 1
+#define LUA_INT_LONG 2
+#define LUA_INT_LONGLONG 3
+
+/* predefined options for LUA_FLOAT_TYPE */
+#define LUA_FLOAT_FLOAT 1
+#define LUA_FLOAT_DOUBLE 2
+#define LUA_FLOAT_LONGDOUBLE 3
+
+
+/* Default configuration ('long long' and 'double', for 64-bit Lua) */
+#define LUA_INT_DEFAULT LUA_INT_LONGLONG
+#define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE
+
+
+/*
+@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
+*/
+#define LUA_32BITS 0
+
+
+/*
+@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
+** C89 ('long' and 'double'); Windows always has '__int64', so it does
+** not need to use this case.
+*/
+#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS)
+#define LUA_C89_NUMBERS 1
+#else
+#define LUA_C89_NUMBERS 0
+#endif
+
+
+#if LUA_32BITS /* { */
+/*
+** 32-bit integers and 'float'
+*/
+#if LUAI_IS32INT /* use 'int' if big enough */
+#define LUA_INT_TYPE LUA_INT_INT
+#else /* otherwise use 'long' */
+#define LUA_INT_TYPE LUA_INT_LONG
+#endif
+#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT
+
+#elif LUA_C89_NUMBERS /* }{ */
+/*
+** largest types available for C89 ('long' and 'double')
+*/
+#define LUA_INT_TYPE LUA_INT_LONG
+#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
+
+#else /* }{ */
+/* use defaults */
+
+#define LUA_INT_TYPE LUA_INT_DEFAULT
+#define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT
+
+#endif /* } */
+
+
+/* }================================================================== */
+
+
+
+/*
+** {==================================================================
+** Configuration for Paths.
+** ===================================================================
+*/
+
+/*
+** LUA_PATH_SEP is the character that separates templates in a path.
+** LUA_PATH_MARK is the string that marks the substitution points in a
+** template.
+** LUA_EXEC_DIR in a Windows path is replaced by the executable's
+** directory.
+*/
+#define LUA_PATH_SEP ";"
+#define LUA_PATH_MARK "?"
+#define LUA_EXEC_DIR "!"
+
+
+/*
+@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for
+** Lua libraries.
+@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for
+** C libraries.
+** CHANGE them if your machine has a non-conventional directory
+** hierarchy or if you want to install your libraries in
+** non-conventional directories.
+*/
+
+#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
+#if defined(_WIN32) /* { */
+/*
+** In Windows, any exclamation mark ('!') in the path is replaced by the
+** path of the directory of the executable file of the current process.
+*/
+#define LUA_LDIR "!\\lua\\"
+#define LUA_CDIR "!\\"
+#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\"
+
+#if !defined(LUA_PATH_DEFAULT)
+#define LUA_PATH_DEFAULT \
+ LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \
+ LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \
+ ".\\?.lua;" ".\\?\\init.lua"
+#endif
+
+#if !defined(LUA_CPATH_DEFAULT)
+#define LUA_CPATH_DEFAULT \
+ LUA_CDIR"?.dll;" \
+ LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \
+ LUA_CDIR"loadall.dll;" ".\\?.dll"
+#endif
+
+#else /* }{ */
+
+#define LUA_ROOT "/usr/local/"
+#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/"
+#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/"
+
+#if !defined(LUA_PATH_DEFAULT)
+#define LUA_PATH_DEFAULT \
+ LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \
+ "./?.lua;" "./?/init.lua"
+#endif
+
+#if !defined(LUA_CPATH_DEFAULT)
+#define LUA_CPATH_DEFAULT \
+ LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so"
+#endif
+
+#endif /* } */
+
+
+/*
+@@ LUA_DIRSEP is the directory separator (for submodules).
+** CHANGE it if your machine does not use "/" as the directory separator
+** and is not Windows. (On Windows Lua automatically uses "\".)
+*/
+#if !defined(LUA_DIRSEP)
+
+#if defined(_WIN32)
+#define LUA_DIRSEP "\\"
+#else
+#define LUA_DIRSEP "/"
+#endif
+
+#endif
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Marks for exported symbols in the C code
+** ===================================================================
+*/
+
+/*
+@@ LUA_API is a mark for all core API functions.
+@@ LUALIB_API is a mark for all auxiliary library functions.
+@@ LUAMOD_API is a mark for all standard library opening functions.
+** CHANGE them if you need to define those functions in some special way.
+** For instance, if you want to create one Windows DLL with the core and
+** the libraries, you may want to use the following definition (define
+** LUA_BUILD_AS_DLL to get it).
+*/
+#if defined(LUA_BUILD_AS_DLL) /* { */
+
+#if defined(LUA_CORE) || defined(LUA_LIB) /* { */
+#define LUA_API __declspec(dllexport)
+#else /* }{ */
+#define LUA_API __declspec(dllimport)
+#endif /* } */
+
+#else /* }{ */
+
+#define LUA_API extern
+
+#endif /* } */
+
+
+/*
+** More often than not the libs go together with the core.
+*/
+#define LUALIB_API LUA_API
+#define LUAMOD_API LUA_API
+
+
+/*
+@@ LUAI_FUNC is a mark for all extern functions that are not to be
+** exported to outside modules.
+@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables,
+** none of which to be exported to outside modules (LUAI_DDEF for
+** definitions and LUAI_DDEC for declarations).
+** CHANGE them if you need to mark them in some special way. Elf/gcc
+** (versions 3.2 and later) mark them as "hidden" to optimize access
+** when Lua is compiled as a shared library. Not all elf targets support
+** this attribute. Unfortunately, gcc does not offer a way to check
+** whether the target offers that support, and those without support
+** give a warning about it. To avoid these warnings, change to the
+** default definition.
+*/
+#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \
+ defined(__ELF__) /* { */
+#define LUAI_FUNC __attribute__((visibility("internal"))) extern
+#else /* }{ */
+#define LUAI_FUNC extern
+#endif /* } */
+
+#define LUAI_DDEC(dec) LUAI_FUNC dec
+#define LUAI_DDEF /* empty */
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Compatibility with previous versions
+** ===================================================================
+*/
+
+/*
+@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3.
+** You can define it to get all options, or change specific options
+** to fit your specific needs.
+*/
+#if defined(LUA_COMPAT_5_3) /* { */
+
+/*
+@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated
+** functions in the mathematical library.
+** (These functions were already officially removed in 5.3;
+** nevertheless they are still available here.)
+*/
+#define LUA_COMPAT_MATHLIB
+
+/*
+@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for
+** manipulating other integer types (lua_pushunsigned, lua_tounsigned,
+** luaL_checkint, luaL_checklong, etc.)
+** (These macros were also officially removed in 5.3, but they are still
+** available here.)
+*/
+#define LUA_COMPAT_APIINTCASTS
+
+
+/*
+@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod
+** using '__lt'.
+*/
+#define LUA_COMPAT_LT_LE
+
+
+/*
+@@ The following macros supply trivial compatibility for some
+** changes in the API. The macros themselves document how to
+** change your code to avoid using them.
+** (Once more, these macros were officially removed in 5.3, but they are
+** still available here.)
+*/
+#define lua_strlen(L,i) lua_rawlen(L, (i))
+
+#define lua_objlen(L,i) lua_rawlen(L, (i))
+
+#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
+#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT)
+
+#endif /* } */
+
+/* }================================================================== */
+
+
+
+/*
+** {==================================================================
+** Configuration for Numbers (low-level part).
+** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_*
+** satisfy your needs.
+** ===================================================================
+*/
+
+/*
+@@ LUAI_UACNUMBER is the result of a 'default argument promotion'
+@@ over a floating number.
+@@ l_floatatt(x) corrects float attribute 'x' to the proper float type
+** by prefixing it with one of FLT/DBL/LDBL.
+@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats.
+@@ LUA_NUMBER_FMT is the format for writing floats.
+@@ lua_number2str converts a float to a string.
+@@ l_mathop allows the addition of an 'l' or 'f' to all math operations.
+@@ l_floor takes the floor of a float.
+@@ lua_str2number converts a decimal numeral to a number.
+*/
+
+
+/* The following definitions are good for most cases here */
+
+#define l_floor(x) (l_mathop(floor)(x))
+
+#define lua_number2str(s,sz,n) \
+ l_sprintf((s), sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)(n))
+
+/*
+@@ lua_numbertointeger converts a float number with an integral value
+** to an integer, or returns 0 if float is not within the range of
+** a lua_Integer. (The range comparisons are tricky because of
+** rounding. The tests here assume a two-complement representation,
+** where MININTEGER always has an exact representation as a float;
+** MAXINTEGER may not have one, and therefore its conversion to float
+** may have an ill-defined value.)
+*/
+#define lua_numbertointeger(n,p) \
+ ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \
+ (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \
+ (*(p) = (LUA_INTEGER)(n), 1))
+
+
+/* now the variable definitions */
+
+#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */
+
+#define LUA_NUMBER float
+
+#define l_floatatt(n) (FLT_##n)
+
+#define LUAI_UACNUMBER double
+
+#define LUA_NUMBER_FRMLEN ""
+#define LUA_NUMBER_FMT "%.7g"
+
+#define l_mathop(op) op##f
+
+#define lua_str2number(s,p) strtof((s), (p))
+
+
+#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */
+
+#define LUA_NUMBER long double
+
+#define l_floatatt(n) (LDBL_##n)
+
+#define LUAI_UACNUMBER long double
+
+#define LUA_NUMBER_FRMLEN "L"
+#define LUA_NUMBER_FMT "%.19Lg"
+
+#define l_mathop(op) op##l
+
+#define lua_str2number(s,p) strtold((s), (p))
+
+#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */
+
+#define LUA_NUMBER double
+
+#define l_floatatt(n) (DBL_##n)
+
+#define LUAI_UACNUMBER double
+
+#define LUA_NUMBER_FRMLEN ""
+#define LUA_NUMBER_FMT "%.14g"
+
+#define l_mathop(op) op
+
+#define lua_str2number(s,p) strtod((s), (p))
+
+#else /* }{ */
+
+#error "numeric float type not defined"
+
+#endif /* } */
+
+
+
+/*
+@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
+@@ LUAI_UACINT is the result of a 'default argument promotion'
+@@ over a LUA_INTEGER.
+@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
+@@ LUA_INTEGER_FMT is the format for writing integers.
+@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER.
+@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER.
+@@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED.
+@@ lua_integer2str converts an integer to a string.
+*/
+
+
+/* The following definitions are good for most cases here */
+
+#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d"
+
+#define LUAI_UACINT LUA_INTEGER
+
+#define lua_integer2str(s,sz,n) \
+ l_sprintf((s), sz, LUA_INTEGER_FMT, (LUAI_UACINT)(n))
+
+/*
+** use LUAI_UACINT here to avoid problems with promotions (which
+** can turn a comparison between unsigneds into a signed comparison)
+*/
+#define LUA_UNSIGNED unsigned LUAI_UACINT
+
+
+/* now the variable definitions */
+
+#if LUA_INT_TYPE == LUA_INT_INT /* { int */
+
+#define LUA_INTEGER int
+#define LUA_INTEGER_FRMLEN ""
+
+#define LUA_MAXINTEGER INT_MAX
+#define LUA_MININTEGER INT_MIN
+
+#define LUA_MAXUNSIGNED UINT_MAX
+
+#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */
+
+#define LUA_INTEGER long
+#define LUA_INTEGER_FRMLEN "l"
+
+#define LUA_MAXINTEGER LONG_MAX
+#define LUA_MININTEGER LONG_MIN
+
+#define LUA_MAXUNSIGNED ULONG_MAX
+
+#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */
+
+/* use presence of macro LLONG_MAX as proxy for C99 compliance */
+#if defined(LLONG_MAX) /* { */
+/* use ISO C99 stuff */
+
+#define LUA_INTEGER long long
+#define LUA_INTEGER_FRMLEN "ll"
+
+#define LUA_MAXINTEGER LLONG_MAX
+#define LUA_MININTEGER LLONG_MIN
+
+#define LUA_MAXUNSIGNED ULLONG_MAX
+
+#elif defined(LUA_USE_WINDOWS) /* }{ */
+/* in Windows, can use specific Windows types */
+
+#define LUA_INTEGER __int64
+#define LUA_INTEGER_FRMLEN "I64"
+
+#define LUA_MAXINTEGER _I64_MAX
+#define LUA_MININTEGER _I64_MIN
+
+#define LUA_MAXUNSIGNED _UI64_MAX
+
+#else /* }{ */
+
+#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \
+ or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)"
+
+#endif /* } */
+
+#else /* }{ */
+
+#error "numeric integer type not defined"
+
+#endif /* } */
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Dependencies with C99 and other C details
+** ===================================================================
+*/
+
+/*
+@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89.
+** (All uses in Lua have only one format item.)
+*/
+#if !defined(LUA_USE_C89)
+#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i)
+#else
+#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i))
+#endif
+
+
+/*
+@@ lua_strx2number converts a hexadecimal numeral to a number.
+** In C99, 'strtod' does that conversion. Otherwise, you can
+** leave 'lua_strx2number' undefined and Lua will provide its own
+** implementation.
+*/
+#if !defined(LUA_USE_C89)
+#define lua_strx2number(s,p) lua_str2number(s,p)
+#endif
+
+
+/*
+@@ lua_pointer2str converts a pointer to a readable string in a
+** non-specified way.
+*/
+#define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p)
+
+
+/*
+@@ lua_number2strx converts a float to a hexadecimal numeral.
+** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that.
+** Otherwise, you can leave 'lua_number2strx' undefined and Lua will
+** provide its own implementation.
+*/
+#if !defined(LUA_USE_C89)
+#define lua_number2strx(L,b,sz,f,n) \
+ ((void)L, l_sprintf(b,sz,f,(LUAI_UACNUMBER)(n)))
+#endif
+
+
+/*
+** 'strtof' and 'opf' variants for math functions are not valid in
+** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the
+** availability of these variants. ('math.h' is already included in
+** all files that use these macros.)
+*/
+#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF))
+#undef l_mathop /* variants not available */
+#undef lua_str2number
+#define l_mathop(op) (lua_Number)op /* no variant */
+#define lua_str2number(s,p) ((lua_Number)strtod((s), (p)))
+#endif
+
+
+/*
+@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation
+** functions. It must be a numerical type; Lua will use 'intptr_t' if
+** available, otherwise it will use 'ptrdiff_t' (the nearest thing to
+** 'intptr_t' in C89)
+*/
+#define LUA_KCONTEXT ptrdiff_t
+
+#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
+ __STDC_VERSION__ >= 199901L
+#include <stdint.h>
+#if defined(INTPTR_MAX) /* even in C99 this type is optional */
+#undef LUA_KCONTEXT
+#define LUA_KCONTEXT intptr_t
+#endif
+#endif
+
+
+/*
+@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point).
+** Change that if you do not want to use C locales. (Code using this
+** macro must include the header 'locale.h'.)
+*/
+#if !defined(lua_getlocaledecpoint)
+#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
+#endif
+
+
+/*
+** macros to improve jump prediction, used mostly for error handling
+** and debug facilities. (Some macros in the Lua API use these macros.
+** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your
+** code.)
+*/
+#if !defined(luai_likely)
+
+#if defined(__GNUC__) && !defined(LUA_NOBUILTIN)
+#define luai_likely(x) (__builtin_expect(((x) != 0), 1))
+#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0))
+#else
+#define luai_likely(x) (x)
+#define luai_unlikely(x) (x)
+#endif
+
+#endif
+
+
+#if defined(LUA_CORE) || defined(LUA_LIB)
+/* shorter names for Lua's own use */
+#define l_likely(x) luai_likely(x)
+#define l_unlikely(x) luai_unlikely(x)
+#endif
+
+
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Language Variations
+** =====================================================================
+*/
+
+/*
+@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some
+** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from
+** numbers to strings. Define LUA_NOCVTS2N to turn off automatic
+** coercion from strings to numbers.
+*/
+/* #define LUA_NOCVTN2S */
+/* #define LUA_NOCVTS2N */
+
+
+/*
+@@ LUA_USE_APICHECK turns on several consistency checks on the C API.
+** Define it as a help when debugging C code.
+*/
+#if defined(LUA_USE_APICHECK)
+#include <assert.h>
+#define luai_apicheck(l,e) assert(e)
+#endif
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Macros that affect the API and must be stable (that is, must be the
+** same when you compile Lua and when you compile code that links to
+** Lua).
+** =====================================================================
+*/
+
+/*
+@@ LUAI_MAXSTACK limits the size of the Lua stack.
+** CHANGE it if you need a different limit. This limit is arbitrary;
+** its only purpose is to stop Lua from consuming unlimited stack
+** space (and to reserve some numbers for pseudo-indices).
+** (It must fit into max(size_t)/32 and max(int)/2.)
+*/
+#if LUAI_IS32INT
+#define LUAI_MAXSTACK 1000000
+#else
+#define LUAI_MAXSTACK 15000
+#endif
+
+
+/*
+@@ LUA_EXTRASPACE defines the size of a raw memory area associated with
+** a Lua state with very fast access.
+** CHANGE it if you need a different size.
+*/
+#define LUA_EXTRASPACE (sizeof(void *))
+
+
+/*
+@@ LUA_IDSIZE gives the maximum size for the description of the source
+** of a function in debug information.
+** CHANGE it if you want a different size.
+*/
+#define LUA_IDSIZE 60
+
+
+/*
+@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib
+** buffer system.
+*/
+#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number)))
+
+
+/*
+@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure
+** maximum alignment for the other items in that union.
+*/
+#define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l
+
+/* }================================================================== */
+
+
+
+
+
+/* =================================================================== */
+
+/*
+** Local configuration. You can use this space to add your redefinitions
+** without modifying the main part of the file.
+*/
+
+
+
+
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/lualib.h b/src/libs/3rdparty/lua/src/lualib.h
new file mode 100644
index 0000000000..2625529076
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lualib.h
@@ -0,0 +1,52 @@
+/*
+** $Id: lualib.h $
+** Lua standard libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lualib_h
+#define lualib_h
+
+#include "lua.h"
+
+
+/* version suffix for environment variable names */
+#define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR
+
+
+LUAMOD_API int (luaopen_base) (lua_State *L);
+
+#define LUA_COLIBNAME "coroutine"
+LUAMOD_API int (luaopen_coroutine) (lua_State *L);
+
+#define LUA_TABLIBNAME "table"
+LUAMOD_API int (luaopen_table) (lua_State *L);
+
+#define LUA_IOLIBNAME "io"
+LUAMOD_API int (luaopen_io) (lua_State *L);
+
+#define LUA_OSLIBNAME "os"
+LUAMOD_API int (luaopen_os) (lua_State *L);
+
+#define LUA_STRLIBNAME "string"
+LUAMOD_API int (luaopen_string) (lua_State *L);
+
+#define LUA_UTF8LIBNAME "utf8"
+LUAMOD_API int (luaopen_utf8) (lua_State *L);
+
+#define LUA_MATHLIBNAME "math"
+LUAMOD_API int (luaopen_math) (lua_State *L);
+
+#define LUA_DBLIBNAME "debug"
+LUAMOD_API int (luaopen_debug) (lua_State *L);
+
+#define LUA_LOADLIBNAME "package"
+LUAMOD_API int (luaopen_package) (lua_State *L);
+
+
+/* open all previous libraries */
+LUALIB_API void (luaL_openlibs) (lua_State *L);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lundump.c b/src/libs/3rdparty/lua/src/lundump.c
new file mode 100644
index 0000000000..02aed64fb6
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lundump.c
@@ -0,0 +1,335 @@
+/*
+** $Id: lundump.c $
+** load precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#define lundump_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <limits.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstring.h"
+#include "lundump.h"
+#include "lzio.h"
+
+
+#if !defined(luai_verifycode)
+#define luai_verifycode(L,f) /* empty */
+#endif
+
+
+typedef struct {
+ lua_State *L;
+ ZIO *Z;
+ const char *name;
+} LoadState;
+
+
+static l_noret error (LoadState *S, const char *why) {
+ luaO_pushfstring(S->L, "%s: bad binary format (%s)", S->name, why);
+ luaD_throw(S->L, LUA_ERRSYNTAX);
+}
+
+
+/*
+** All high-level loads go through loadVector; you can change it to
+** adapt to the endianness of the input
+*/
+#define loadVector(S,b,n) loadBlock(S,b,(n)*sizeof((b)[0]))
+
+static void loadBlock (LoadState *S, void *b, size_t size) {
+ if (luaZ_read(S->Z, b, size) != 0)
+ error(S, "truncated chunk");
+}
+
+
+#define loadVar(S,x) loadVector(S,&x,1)
+
+
+static lu_byte loadByte (LoadState *S) {
+ int b = zgetc(S->Z);
+ if (b == EOZ)
+ error(S, "truncated chunk");
+ return cast_byte(b);
+}
+
+
+static size_t loadUnsigned (LoadState *S, size_t limit) {
+ size_t x = 0;
+ int b;
+ limit >>= 7;
+ do {
+ b = loadByte(S);
+ if (x >= limit)
+ error(S, "integer overflow");
+ x = (x << 7) | (b & 0x7f);
+ } while ((b & 0x80) == 0);
+ return x;
+}
+
+
+static size_t loadSize (LoadState *S) {
+ return loadUnsigned(S, ~(size_t)0);
+}
+
+
+static int loadInt (LoadState *S) {
+ return cast_int(loadUnsigned(S, INT_MAX));
+}
+
+
+static lua_Number loadNumber (LoadState *S) {
+ lua_Number x;
+ loadVar(S, x);
+ return x;
+}
+
+
+static lua_Integer loadInteger (LoadState *S) {
+ lua_Integer x;
+ loadVar(S, x);
+ return x;
+}
+
+
+/*
+** Load a nullable string into prototype 'p'.
+*/
+static TString *loadStringN (LoadState *S, Proto *p) {
+ lua_State *L = S->L;
+ TString *ts;
+ size_t size = loadSize(S);
+ if (size == 0) /* no string? */
+ return NULL;
+ else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */
+ char buff[LUAI_MAXSHORTLEN];
+ loadVector(S, buff, size); /* load string into buffer */
+ ts = luaS_newlstr(L, buff, size); /* create string */
+ }
+ else { /* long string */
+ ts = luaS_createlngstrobj(L, size); /* create string */
+ setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */
+ luaD_inctop(L);
+ loadVector(S, getstr(ts), size); /* load directly in final place */
+ L->top.p--; /* pop string */
+ }
+ luaC_objbarrier(L, p, ts);
+ return ts;
+}
+
+
+/*
+** Load a non-nullable string into prototype 'p'.
+*/
+static TString *loadString (LoadState *S, Proto *p) {
+ TString *st = loadStringN(S, p);
+ if (st == NULL)
+ error(S, "bad format for constant string");
+ return st;
+}
+
+
+static void loadCode (LoadState *S, Proto *f) {
+ int n = loadInt(S);
+ f->code = luaM_newvectorchecked(S->L, n, Instruction);
+ f->sizecode = n;
+ loadVector(S, f->code, n);
+}
+
+
+static void loadFunction(LoadState *S, Proto *f, TString *psource);
+
+
+static void loadConstants (LoadState *S, Proto *f) {
+ int i;
+ int n = loadInt(S);
+ f->k = luaM_newvectorchecked(S->L, n, TValue);
+ f->sizek = n;
+ for (i = 0; i < n; i++)
+ setnilvalue(&f->k[i]);
+ for (i = 0; i < n; i++) {
+ TValue *o = &f->k[i];
+ int t = loadByte(S);
+ switch (t) {
+ case LUA_VNIL:
+ setnilvalue(o);
+ break;
+ case LUA_VFALSE:
+ setbfvalue(o);
+ break;
+ case LUA_VTRUE:
+ setbtvalue(o);
+ break;
+ case LUA_VNUMFLT:
+ setfltvalue(o, loadNumber(S));
+ break;
+ case LUA_VNUMINT:
+ setivalue(o, loadInteger(S));
+ break;
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR:
+ setsvalue2n(S->L, o, loadString(S, f));
+ break;
+ default: lua_assert(0);
+ }
+ }
+}
+
+
+static void loadProtos (LoadState *S, Proto *f) {
+ int i;
+ int n = loadInt(S);
+ f->p = luaM_newvectorchecked(S->L, n, Proto *);
+ f->sizep = n;
+ for (i = 0; i < n; i++)
+ f->p[i] = NULL;
+ for (i = 0; i < n; i++) {
+ f->p[i] = luaF_newproto(S->L);
+ luaC_objbarrier(S->L, f, f->p[i]);
+ loadFunction(S, f->p[i], f->source);
+ }
+}
+
+
+/*
+** Load the upvalues for a function. The names must be filled first,
+** because the filling of the other fields can raise read errors and
+** the creation of the error message can call an emergency collection;
+** in that case all prototypes must be consistent for the GC.
+*/
+static void loadUpvalues (LoadState *S, Proto *f) {
+ int i, n;
+ n = loadInt(S);
+ f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
+ f->sizeupvalues = n;
+ for (i = 0; i < n; i++) /* make array valid for GC */
+ f->upvalues[i].name = NULL;
+ for (i = 0; i < n; i++) { /* following calls can raise errors */
+ f->upvalues[i].instack = loadByte(S);
+ f->upvalues[i].idx = loadByte(S);
+ f->upvalues[i].kind = loadByte(S);
+ }
+}
+
+
+static void loadDebug (LoadState *S, Proto *f) {
+ int i, n;
+ n = loadInt(S);
+ f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte);
+ f->sizelineinfo = n;
+ loadVector(S, f->lineinfo, n);
+ n = loadInt(S);
+ f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo);
+ f->sizeabslineinfo = n;
+ for (i = 0; i < n; i++) {
+ f->abslineinfo[i].pc = loadInt(S);
+ f->abslineinfo[i].line = loadInt(S);
+ }
+ n = loadInt(S);
+ f->locvars = luaM_newvectorchecked(S->L, n, LocVar);
+ f->sizelocvars = n;
+ for (i = 0; i < n; i++)
+ f->locvars[i].varname = NULL;
+ for (i = 0; i < n; i++) {
+ f->locvars[i].varname = loadStringN(S, f);
+ f->locvars[i].startpc = loadInt(S);
+ f->locvars[i].endpc = loadInt(S);
+ }
+ n = loadInt(S);
+ if (n != 0) /* does it have debug information? */
+ n = f->sizeupvalues; /* must be this many */
+ for (i = 0; i < n; i++)
+ f->upvalues[i].name = loadStringN(S, f);
+}
+
+
+static void loadFunction (LoadState *S, Proto *f, TString *psource) {
+ f->source = loadStringN(S, f);
+ if (f->source == NULL) /* no source in dump? */
+ f->source = psource; /* reuse parent's source */
+ f->linedefined = loadInt(S);
+ f->lastlinedefined = loadInt(S);
+ f->numparams = loadByte(S);
+ f->is_vararg = loadByte(S);
+ f->maxstacksize = loadByte(S);
+ loadCode(S, f);
+ loadConstants(S, f);
+ loadUpvalues(S, f);
+ loadProtos(S, f);
+ loadDebug(S, f);
+}
+
+
+static void checkliteral (LoadState *S, const char *s, const char *msg) {
+ char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */
+ size_t len = strlen(s);
+ loadVector(S, buff, len);
+ if (memcmp(s, buff, len) != 0)
+ error(S, msg);
+}
+
+
+static void fchecksize (LoadState *S, size_t size, const char *tname) {
+ if (loadByte(S) != size)
+ error(S, luaO_pushfstring(S->L, "%s size mismatch", tname));
+}
+
+
+#define checksize(S,t) fchecksize(S,sizeof(t),#t)
+
+static void checkHeader (LoadState *S) {
+ /* skip 1st char (already read and checked) */
+ checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk");
+ if (loadByte(S) != LUAC_VERSION)
+ error(S, "version mismatch");
+ if (loadByte(S) != LUAC_FORMAT)
+ error(S, "format mismatch");
+ checkliteral(S, LUAC_DATA, "corrupted chunk");
+ checksize(S, Instruction);
+ checksize(S, lua_Integer);
+ checksize(S, lua_Number);
+ if (loadInteger(S) != LUAC_INT)
+ error(S, "integer format mismatch");
+ if (loadNumber(S) != LUAC_NUM)
+ error(S, "float format mismatch");
+}
+
+
+/*
+** Load precompiled chunk.
+*/
+LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) {
+ LoadState S;
+ LClosure *cl;
+ if (*name == '@' || *name == '=')
+ S.name = name + 1;
+ else if (*name == LUA_SIGNATURE[0])
+ S.name = "binary string";
+ else
+ S.name = name;
+ S.L = L;
+ S.Z = Z;
+ checkHeader(&S);
+ cl = luaF_newLclosure(L, loadByte(&S));
+ setclLvalue2s(L, L->top.p, cl);
+ luaD_inctop(L);
+ cl->p = luaF_newproto(L);
+ luaC_objbarrier(L, cl, cl->p);
+ loadFunction(&S, cl->p, NULL);
+ lua_assert(cl->nupvalues == cl->p->sizeupvalues);
+ luai_verifycode(L, cl->p);
+ return cl;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lundump.h b/src/libs/3rdparty/lua/src/lundump.h
new file mode 100644
index 0000000000..f3748a9980
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lundump.h
@@ -0,0 +1,36 @@
+/*
+** $Id: lundump.h $
+** load precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lundump_h
+#define lundump_h
+
+#include "llimits.h"
+#include "lobject.h"
+#include "lzio.h"
+
+
+/* data to catch conversion errors */
+#define LUAC_DATA "\x19\x93\r\n\x1a\n"
+
+#define LUAC_INT 0x5678
+#define LUAC_NUM cast_num(370.5)
+
+/*
+** Encode major-minor version in one byte, one nibble for each
+*/
+#define MYINT(s) (s[0]-'0') /* assume one-digit numerals */
+#define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR))
+
+#define LUAC_FORMAT 0 /* this is the official format */
+
+/* load one chunk; from lundump.c */
+LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name);
+
+/* dump one chunk; from ldump.c */
+LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w,
+ void* data, int strip);
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lutf8lib.c b/src/libs/3rdparty/lua/src/lutf8lib.c
new file mode 100644
index 0000000000..3a5b9bc38a
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lutf8lib.c
@@ -0,0 +1,291 @@
+/*
+** $Id: lutf8lib.c $
+** Standard library for UTF-8 manipulation
+** See Copyright Notice in lua.h
+*/
+
+#define lutf8lib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+#define MAXUNICODE 0x10FFFFu
+
+#define MAXUTF 0x7FFFFFFFu
+
+
+#define MSGInvalid "invalid UTF-8 code"
+
+/*
+** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits.
+*/
+#if (UINT_MAX >> 30) >= 1
+typedef unsigned int utfint;
+#else
+typedef unsigned long utfint;
+#endif
+
+
+#define iscont(c) (((c) & 0xC0) == 0x80)
+#define iscontp(p) iscont(*(p))
+
+
+/* from strlib */
+/* translate a relative string position: negative means back from end */
+static lua_Integer u_posrelat (lua_Integer pos, size_t len) {
+ if (pos >= 0) return pos;
+ else if (0u - (size_t)pos > len) return 0;
+ else return (lua_Integer)len + pos + 1;
+}
+
+
+/*
+** Decode one UTF-8 sequence, returning NULL if byte sequence is
+** invalid. The array 'limits' stores the minimum value for each
+** sequence length, to check for overlong representations. Its first
+** entry forces an error for non-ascii bytes with no continuation
+** bytes (count == 0).
+*/
+static const char *utf8_decode (const char *s, utfint *val, int strict) {
+ static const utfint limits[] =
+ {~(utfint)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u};
+ unsigned int c = (unsigned char)s[0];
+ utfint res = 0; /* final result */
+ if (c < 0x80) /* ascii? */
+ res = c;
+ else {
+ int count = 0; /* to count number of continuation bytes */
+ for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */
+ unsigned int cc = (unsigned char)s[++count]; /* read next byte */
+ if (!iscont(cc)) /* not a continuation byte? */
+ return NULL; /* invalid byte sequence */
+ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
+ }
+ res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */
+ if (count > 5 || res > MAXUTF || res < limits[count])
+ return NULL; /* invalid byte sequence */
+ s += count; /* skip continuation bytes read */
+ }
+ if (strict) {
+ /* check for invalid code points; too large or surrogates */
+ if (res > MAXUNICODE || (0xD800u <= res && res <= 0xDFFFu))
+ return NULL;
+ }
+ if (val) *val = res;
+ return s + 1; /* +1 to include first byte */
+}
+
+
+/*
+** utf8len(s [, i [, j [, lax]]]) --> number of characters that
+** start in the range [i,j], or nil + current position if 's' is not
+** well formed in that interval
+*/
+static int utflen (lua_State *L) {
+ lua_Integer n = 0; /* counter for the number of characters */
+ size_t len; /* string length in bytes */
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
+ lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len);
+ int lax = lua_toboolean(L, 4);
+ luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2,
+ "initial position out of bounds");
+ luaL_argcheck(L, --posj < (lua_Integer)len, 3,
+ "final position out of bounds");
+ while (posi <= posj) {
+ const char *s1 = utf8_decode(s + posi, NULL, !lax);
+ if (s1 == NULL) { /* conversion error? */
+ luaL_pushfail(L); /* return fail ... */
+ lua_pushinteger(L, posi + 1); /* ... and current position */
+ return 2;
+ }
+ posi = s1 - s;
+ n++;
+ }
+ lua_pushinteger(L, n);
+ return 1;
+}
+
+
+/*
+** codepoint(s, [i, [j [, lax]]]) -> returns codepoints for all
+** characters that start in the range [i,j]
+*/
+static int codepoint (lua_State *L) {
+ size_t len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
+ lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len);
+ int lax = lua_toboolean(L, 4);
+ int n;
+ const char *se;
+ luaL_argcheck(L, posi >= 1, 2, "out of bounds");
+ luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of bounds");
+ if (posi > pose) return 0; /* empty interval; return no values */
+ if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */
+ return luaL_error(L, "string slice too long");
+ n = (int)(pose - posi) + 1; /* upper bound for number of returns */
+ luaL_checkstack(L, n, "string slice too long");
+ n = 0; /* count the number of returns */
+ se = s + pose; /* string end */
+ for (s += posi - 1; s < se;) {
+ utfint code;
+ s = utf8_decode(s, &code, !lax);
+ if (s == NULL)
+ return luaL_error(L, MSGInvalid);
+ lua_pushinteger(L, code);
+ n++;
+ }
+ return n;
+}
+
+
+static void pushutfchar (lua_State *L, int arg) {
+ lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg);
+ luaL_argcheck(L, code <= MAXUTF, arg, "value out of range");
+ lua_pushfstring(L, "%U", (long)code);
+}
+
+
+/*
+** utfchar(n1, n2, ...) -> char(n1)..char(n2)...
+*/
+static int utfchar (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ if (n == 1) /* optimize common case of single char */
+ pushutfchar(L, 1);
+ else {
+ int i;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ for (i = 1; i <= n; i++) {
+ pushutfchar(L, i);
+ luaL_addvalue(&b);
+ }
+ luaL_pushresult(&b);
+ }
+ return 1;
+}
+
+
+/*
+** offset(s, n, [i]) -> index where n-th character counting from
+** position 'i' starts; 0 means character at 'i'.
+*/
+static int byteoffset (lua_State *L) {
+ size_t len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Integer n = luaL_checkinteger(L, 2);
+ lua_Integer posi = (n >= 0) ? 1 : len + 1;
+ posi = u_posrelat(luaL_optinteger(L, 3, posi), len);
+ luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3,
+ "position out of bounds");
+ if (n == 0) {
+ /* find beginning of current byte sequence */
+ while (posi > 0 && iscontp(s + posi)) posi--;
+ }
+ else {
+ if (iscontp(s + posi))
+ return luaL_error(L, "initial position is a continuation byte");
+ if (n < 0) {
+ while (n < 0 && posi > 0) { /* move back */
+ do { /* find beginning of previous character */
+ posi--;
+ } while (posi > 0 && iscontp(s + posi));
+ n++;
+ }
+ }
+ else {
+ n--; /* do not move for 1st character */
+ while (n > 0 && posi < (lua_Integer)len) {
+ do { /* find beginning of next character */
+ posi++;
+ } while (iscontp(s + posi)); /* (cannot pass final '\0') */
+ n--;
+ }
+ }
+ }
+ if (n == 0) /* did it find given character? */
+ lua_pushinteger(L, posi + 1);
+ else /* no such character */
+ luaL_pushfail(L);
+ return 1;
+}
+
+
+static int iter_aux (lua_State *L, int strict) {
+ size_t len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2);
+ if (n < len) {
+ while (iscontp(s + n)) n++; /* go to next character */
+ }
+ if (n >= len) /* (also handles original 'n' being negative) */
+ return 0; /* no more codepoints */
+ else {
+ utfint code;
+ const char *next = utf8_decode(s + n, &code, strict);
+ if (next == NULL || iscontp(next))
+ return luaL_error(L, MSGInvalid);
+ lua_pushinteger(L, n + 1);
+ lua_pushinteger(L, code);
+ return 2;
+ }
+}
+
+
+static int iter_auxstrict (lua_State *L) {
+ return iter_aux(L, 1);
+}
+
+static int iter_auxlax (lua_State *L) {
+ return iter_aux(L, 0);
+}
+
+
+static int iter_codes (lua_State *L) {
+ int lax = lua_toboolean(L, 2);
+ const char *s = luaL_checkstring(L, 1);
+ luaL_argcheck(L, !iscontp(s), 1, MSGInvalid);
+ lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict);
+ lua_pushvalue(L, 1);
+ lua_pushinteger(L, 0);
+ return 3;
+}
+
+
+/* pattern to match a single UTF-8 character */
+#define UTF8PATT "[\0-\x7F\xC2-\xFD][\x80-\xBF]*"
+
+
+static const luaL_Reg funcs[] = {
+ {"offset", byteoffset},
+ {"codepoint", codepoint},
+ {"char", utfchar},
+ {"len", utflen},
+ {"codes", iter_codes},
+ /* placeholders */
+ {"charpattern", NULL},
+ {NULL, NULL}
+};
+
+
+LUAMOD_API int luaopen_utf8 (lua_State *L) {
+ luaL_newlib(L, funcs);
+ lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1);
+ lua_setfield(L, -2, "charpattern");
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lvm.c b/src/libs/3rdparty/lua/src/lvm.c
new file mode 100644
index 0000000000..8493a770c5
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lvm.c
@@ -0,0 +1,1901 @@
+/*
+** $Id: lvm.c $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#define lvm_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+/*
+** By default, use jump tables in the main interpreter loop on gcc
+** and compatible compilers.
+*/
+#if !defined(LUA_USE_JUMPTABLE)
+#if defined(__GNUC__)
+#define LUA_USE_JUMPTABLE 1
+#else
+#define LUA_USE_JUMPTABLE 0
+#endif
+#endif
+
+
+
+/* limit for table tag-method chains (to avoid infinite loops) */
+#define MAXTAGLOOP 2000
+
+
+/*
+** 'l_intfitsf' checks whether a given integer is in the range that
+** can be converted to a float without rounding. Used in comparisons.
+*/
+
+/* number of bits in the mantissa of a float */
+#define NBM (l_floatatt(MANT_DIG))
+
+/*
+** Check whether some integers may not fit in a float, testing whether
+** (maxinteger >> NBM) > 0. (That implies (1 << NBM) <= maxinteger.)
+** (The shifts are done in parts, to avoid shifting by more than the size
+** of an integer. In a worst case, NBM == 113 for long double and
+** sizeof(long) == 32.)
+*/
+#if ((((LUA_MAXINTEGER >> (NBM / 4)) >> (NBM / 4)) >> (NBM / 4)) \
+ >> (NBM - (3 * (NBM / 4)))) > 0
+
+/* limit for integers that fit in a float */
+#define MAXINTFITSF ((lua_Unsigned)1 << NBM)
+
+/* check whether 'i' is in the interval [-MAXINTFITSF, MAXINTFITSF] */
+#define l_intfitsf(i) ((MAXINTFITSF + l_castS2U(i)) <= (2 * MAXINTFITSF))
+
+#else /* all integers fit in a float precisely */
+
+#define l_intfitsf(i) 1
+
+#endif
+
+
+/*
+** Try to convert a value from string to a number value.
+** If the value is not a string or is a string not representing
+** a valid numeral (or if coercions from strings to numbers
+** are disabled via macro 'cvt2num'), do not modify 'result'
+** and return 0.
+*/
+static int l_strton (const TValue *obj, TValue *result) {
+ lua_assert(obj != result);
+ if (!cvt2num(obj)) /* is object not a string? */
+ return 0;
+ else
+ return (luaO_str2num(svalue(obj), result) == vslen(obj) + 1);
+}
+
+
+/*
+** Try to convert a value to a float. The float case is already handled
+** by the macro 'tonumber'.
+*/
+int luaV_tonumber_ (const TValue *obj, lua_Number *n) {
+ TValue v;
+ if (ttisinteger(obj)) {
+ *n = cast_num(ivalue(obj));
+ return 1;
+ }
+ else if (l_strton(obj, &v)) { /* string coercible to number? */
+ *n = nvalue(&v); /* convert result of 'luaO_str2num' to a float */
+ return 1;
+ }
+ else
+ return 0; /* conversion failed */
+}
+
+
+/*
+** try to convert a float to an integer, rounding according to 'mode'.
+*/
+int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode) {
+ lua_Number f = l_floor(n);
+ if (n != f) { /* not an integral value? */
+ if (mode == F2Ieq) return 0; /* fails if mode demands integral value */
+ else if (mode == F2Iceil) /* needs ceil? */
+ f += 1; /* convert floor to ceil (remember: n != f) */
+ }
+ return lua_numbertointeger(f, p);
+}
+
+
+/*
+** try to convert a value to an integer, rounding according to 'mode',
+** without string coercion.
+** ("Fast track" handled by macro 'tointegerns'.)
+*/
+int luaV_tointegerns (const TValue *obj, lua_Integer *p, F2Imod mode) {
+ if (ttisfloat(obj))
+ return luaV_flttointeger(fltvalue(obj), p, mode);
+ else if (ttisinteger(obj)) {
+ *p = ivalue(obj);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/*
+** try to convert a value to an integer.
+*/
+int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode) {
+ TValue v;
+ if (l_strton(obj, &v)) /* does 'obj' point to a numerical string? */
+ obj = &v; /* change it to point to its corresponding number */
+ return luaV_tointegerns(obj, p, mode);
+}
+
+
+/*
+** Try to convert a 'for' limit to an integer, preserving the semantics
+** of the loop. Return true if the loop must not run; otherwise, '*p'
+** gets the integer limit.
+** (The following explanation assumes a positive step; it is valid for
+** negative steps mutatis mutandis.)
+** If the limit is an integer or can be converted to an integer,
+** rounding down, that is the limit.
+** Otherwise, check whether the limit can be converted to a float. If
+** the float is too large, clip it to LUA_MAXINTEGER. If the float
+** is too negative, the loop should not run, because any initial
+** integer value is greater than such limit; so, the function returns
+** true to signal that. (For this latter case, no integer limit would be
+** correct; even a limit of LUA_MININTEGER would run the loop once for
+** an initial value equal to LUA_MININTEGER.)
+*/
+static int forlimit (lua_State *L, lua_Integer init, const TValue *lim,
+ lua_Integer *p, lua_Integer step) {
+ if (!luaV_tointeger(lim, p, (step < 0 ? F2Iceil : F2Ifloor))) {
+ /* not coercible to in integer */
+ lua_Number flim; /* try to convert to float */
+ if (!tonumber(lim, &flim)) /* cannot convert to float? */
+ luaG_forerror(L, lim, "limit");
+ /* else 'flim' is a float out of integer bounds */
+ if (luai_numlt(0, flim)) { /* if it is positive, it is too large */
+ if (step < 0) return 1; /* initial value must be less than it */
+ *p = LUA_MAXINTEGER; /* truncate */
+ }
+ else { /* it is less than min integer */
+ if (step > 0) return 1; /* initial value must be greater than it */
+ *p = LUA_MININTEGER; /* truncate */
+ }
+ }
+ return (step > 0 ? init > *p : init < *p); /* not to run? */
+}
+
+
+/*
+** Prepare a numerical for loop (opcode OP_FORPREP).
+** Return true to skip the loop. Otherwise,
+** after preparation, stack will be as follows:
+** ra : internal index (safe copy of the control variable)
+** ra + 1 : loop counter (integer loops) or limit (float loops)
+** ra + 2 : step
+** ra + 3 : control variable
+*/
+static int forprep (lua_State *L, StkId ra) {
+ TValue *pinit = s2v(ra);
+ TValue *plimit = s2v(ra + 1);
+ TValue *pstep = s2v(ra + 2);
+ if (ttisinteger(pinit) && ttisinteger(pstep)) { /* integer loop? */
+ lua_Integer init = ivalue(pinit);
+ lua_Integer step = ivalue(pstep);
+ lua_Integer limit;
+ if (step == 0)
+ luaG_runerror(L, "'for' step is zero");
+ setivalue(s2v(ra + 3), init); /* control variable */
+ if (forlimit(L, init, plimit, &limit, step))
+ return 1; /* skip the loop */
+ else { /* prepare loop counter */
+ lua_Unsigned count;
+ if (step > 0) { /* ascending loop? */
+ count = l_castS2U(limit) - l_castS2U(init);
+ if (step != 1) /* avoid division in the too common case */
+ count /= l_castS2U(step);
+ }
+ else { /* step < 0; descending loop */
+ count = l_castS2U(init) - l_castS2U(limit);
+ /* 'step+1' avoids negating 'mininteger' */
+ count /= l_castS2U(-(step + 1)) + 1u;
+ }
+ /* store the counter in place of the limit (which won't be
+ needed anymore) */
+ setivalue(plimit, l_castU2S(count));
+ }
+ }
+ else { /* try making all values floats */
+ lua_Number init; lua_Number limit; lua_Number step;
+ if (l_unlikely(!tonumber(plimit, &limit)))
+ luaG_forerror(L, plimit, "limit");
+ if (l_unlikely(!tonumber(pstep, &step)))
+ luaG_forerror(L, pstep, "step");
+ if (l_unlikely(!tonumber(pinit, &init)))
+ luaG_forerror(L, pinit, "initial value");
+ if (step == 0)
+ luaG_runerror(L, "'for' step is zero");
+ if (luai_numlt(0, step) ? luai_numlt(limit, init)
+ : luai_numlt(init, limit))
+ return 1; /* skip the loop */
+ else {
+ /* make sure internal values are all floats */
+ setfltvalue(plimit, limit);
+ setfltvalue(pstep, step);
+ setfltvalue(s2v(ra), init); /* internal index */
+ setfltvalue(s2v(ra + 3), init); /* control variable */
+ }
+ }
+ return 0;
+}
+
+
+/*
+** Execute a step of a float numerical for loop, returning
+** true iff the loop must continue. (The integer case is
+** written online with opcode OP_FORLOOP, for performance.)
+*/
+static int floatforloop (StkId ra) {
+ lua_Number step = fltvalue(s2v(ra + 2));
+ lua_Number limit = fltvalue(s2v(ra + 1));
+ lua_Number idx = fltvalue(s2v(ra)); /* internal index */
+ idx = luai_numadd(L, idx, step); /* increment index */
+ if (luai_numlt(0, step) ? luai_numle(idx, limit)
+ : luai_numle(limit, idx)) {
+ chgfltvalue(s2v(ra), idx); /* update internal index */
+ setfltvalue(s2v(ra + 3), idx); /* and control variable */
+ return 1; /* jump back */
+ }
+ else
+ return 0; /* finish the loop */
+}
+
+
+/*
+** Finish the table access 'val = t[key]'.
+** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to
+** t[k] entry (which must be empty).
+*/
+void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
+ const TValue *slot) {
+ int loop; /* counter to avoid infinite loops */
+ const TValue *tm; /* metamethod */
+ for (loop = 0; loop < MAXTAGLOOP; loop++) {
+ if (slot == NULL) { /* 't' is not a table? */
+ lua_assert(!ttistable(t));
+ tm = luaT_gettmbyobj(L, t, TM_INDEX);
+ if (l_unlikely(notm(tm)))
+ luaG_typeerror(L, t, "index"); /* no metamethod */
+ /* else will try the metamethod */
+ }
+ else { /* 't' is a table */
+ lua_assert(isempty(slot));
+ tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */
+ if (tm == NULL) { /* no metamethod? */
+ setnilvalue(s2v(val)); /* result is nil */
+ return;
+ }
+ /* else will try the metamethod */
+ }
+ if (ttisfunction(tm)) { /* is metamethod a function? */
+ luaT_callTMres(L, tm, t, key, val); /* call it */
+ return;
+ }
+ t = tm; /* else try to access 'tm[key]' */
+ if (luaV_fastget(L, t, key, slot, luaH_get)) { /* fast track? */
+ setobj2s(L, val, slot); /* done */
+ return;
+ }
+ /* else repeat (tail call 'luaV_finishget') */
+ }
+ luaG_runerror(L, "'__index' chain too long; possible loop");
+}
+
+
+/*
+** Finish a table assignment 't[key] = val'.
+** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points
+** to the entry 't[key]', or to a value with an absent key if there
+** is no such entry. (The value at 'slot' must be empty, otherwise
+** 'luaV_fastget' would have done the job.)
+*/
+void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
+ TValue *val, const TValue *slot) {
+ int loop; /* counter to avoid infinite loops */
+ for (loop = 0; loop < MAXTAGLOOP; loop++) {
+ const TValue *tm; /* '__newindex' metamethod */
+ if (slot != NULL) { /* is 't' a table? */
+ Table *h = hvalue(t); /* save 't' table */
+ lua_assert(isempty(slot)); /* slot must be empty */
+ tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */
+ if (tm == NULL) { /* no metamethod? */
+ luaH_finishset(L, h, key, slot, val); /* set new value */
+ invalidateTMcache(h);
+ luaC_barrierback(L, obj2gco(h), val);
+ return;
+ }
+ /* else will try the metamethod */
+ }
+ else { /* not a table; check metamethod */
+ tm = luaT_gettmbyobj(L, t, TM_NEWINDEX);
+ if (l_unlikely(notm(tm)))
+ luaG_typeerror(L, t, "index");
+ }
+ /* try the metamethod */
+ if (ttisfunction(tm)) {
+ luaT_callTM(L, tm, t, key, val);
+ return;
+ }
+ t = tm; /* else repeat assignment over 'tm' */
+ if (luaV_fastget(L, t, key, slot, luaH_get)) {
+ luaV_finishfastset(L, t, slot, val);
+ return; /* done */
+ }
+ /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */
+ }
+ luaG_runerror(L, "'__newindex' chain too long; possible loop");
+}
+
+
+/*
+** Compare two strings 'ls' x 'rs', returning an integer less-equal-
+** -greater than zero if 'ls' is less-equal-greater than 'rs'.
+** The code is a little tricky because it allows '\0' in the strings
+** and it uses 'strcoll' (to respect locales) for each segments
+** of the strings.
+*/
+static int l_strcmp (const TString *ls, const TString *rs) {
+ const char *l = getstr(ls);
+ size_t ll = tsslen(ls);
+ const char *r = getstr(rs);
+ size_t lr = tsslen(rs);
+ for (;;) { /* for each segment */
+ int temp = strcoll(l, r);
+ if (temp != 0) /* not equal? */
+ return temp; /* done */
+ else { /* strings are equal up to a '\0' */
+ size_t len = strlen(l); /* index of first '\0' in both strings */
+ if (len == lr) /* 'rs' is finished? */
+ return (len == ll) ? 0 : 1; /* check 'ls' */
+ else if (len == ll) /* 'ls' is finished? */
+ return -1; /* 'ls' is less than 'rs' ('rs' is not finished) */
+ /* both strings longer than 'len'; go on comparing after the '\0' */
+ len++;
+ l += len; ll -= len; r += len; lr -= len;
+ }
+ }
+}
+
+
+/*
+** Check whether integer 'i' is less than float 'f'. If 'i' has an
+** exact representation as a float ('l_intfitsf'), compare numbers as
+** floats. Otherwise, use the equivalence 'i < f <=> i < ceil(f)'.
+** If 'ceil(f)' is out of integer range, either 'f' is greater than
+** all integers or less than all integers.
+** (The test with 'l_intfitsf' is only for performance; the else
+** case is correct for all values, but it is slow due to the conversion
+** from float to int.)
+** When 'f' is NaN, comparisons must result in false.
+*/
+l_sinline int LTintfloat (lua_Integer i, lua_Number f) {
+ if (l_intfitsf(i))
+ return luai_numlt(cast_num(i), f); /* compare them as floats */
+ else { /* i < f <=> i < ceil(f) */
+ lua_Integer fi;
+ if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */
+ return i < fi; /* compare them as integers */
+ else /* 'f' is either greater or less than all integers */
+ return f > 0; /* greater? */
+ }
+}
+
+
+/*
+** Check whether integer 'i' is less than or equal to float 'f'.
+** See comments on previous function.
+*/
+l_sinline int LEintfloat (lua_Integer i, lua_Number f) {
+ if (l_intfitsf(i))
+ return luai_numle(cast_num(i), f); /* compare them as floats */
+ else { /* i <= f <=> i <= floor(f) */
+ lua_Integer fi;
+ if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */
+ return i <= fi; /* compare them as integers */
+ else /* 'f' is either greater or less than all integers */
+ return f > 0; /* greater? */
+ }
+}
+
+
+/*
+** Check whether float 'f' is less than integer 'i'.
+** See comments on previous function.
+*/
+l_sinline int LTfloatint (lua_Number f, lua_Integer i) {
+ if (l_intfitsf(i))
+ return luai_numlt(f, cast_num(i)); /* compare them as floats */
+ else { /* f < i <=> floor(f) < i */
+ lua_Integer fi;
+ if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */
+ return fi < i; /* compare them as integers */
+ else /* 'f' is either greater or less than all integers */
+ return f < 0; /* less? */
+ }
+}
+
+
+/*
+** Check whether float 'f' is less than or equal to integer 'i'.
+** See comments on previous function.
+*/
+l_sinline int LEfloatint (lua_Number f, lua_Integer i) {
+ if (l_intfitsf(i))
+ return luai_numle(f, cast_num(i)); /* compare them as floats */
+ else { /* f <= i <=> ceil(f) <= i */
+ lua_Integer fi;
+ if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */
+ return fi <= i; /* compare them as integers */
+ else /* 'f' is either greater or less than all integers */
+ return f < 0; /* less? */
+ }
+}
+
+
+/*
+** Return 'l < r', for numbers.
+*/
+l_sinline int LTnum (const TValue *l, const TValue *r) {
+ lua_assert(ttisnumber(l) && ttisnumber(r));
+ if (ttisinteger(l)) {
+ lua_Integer li = ivalue(l);
+ if (ttisinteger(r))
+ return li < ivalue(r); /* both are integers */
+ else /* 'l' is int and 'r' is float */
+ return LTintfloat(li, fltvalue(r)); /* l < r ? */
+ }
+ else {
+ lua_Number lf = fltvalue(l); /* 'l' must be float */
+ if (ttisfloat(r))
+ return luai_numlt(lf, fltvalue(r)); /* both are float */
+ else /* 'l' is float and 'r' is int */
+ return LTfloatint(lf, ivalue(r));
+ }
+}
+
+
+/*
+** Return 'l <= r', for numbers.
+*/
+l_sinline int LEnum (const TValue *l, const TValue *r) {
+ lua_assert(ttisnumber(l) && ttisnumber(r));
+ if (ttisinteger(l)) {
+ lua_Integer li = ivalue(l);
+ if (ttisinteger(r))
+ return li <= ivalue(r); /* both are integers */
+ else /* 'l' is int and 'r' is float */
+ return LEintfloat(li, fltvalue(r)); /* l <= r ? */
+ }
+ else {
+ lua_Number lf = fltvalue(l); /* 'l' must be float */
+ if (ttisfloat(r))
+ return luai_numle(lf, fltvalue(r)); /* both are float */
+ else /* 'l' is float and 'r' is int */
+ return LEfloatint(lf, ivalue(r));
+ }
+}
+
+
+/*
+** return 'l < r' for non-numbers.
+*/
+static int lessthanothers (lua_State *L, const TValue *l, const TValue *r) {
+ lua_assert(!ttisnumber(l) || !ttisnumber(r));
+ if (ttisstring(l) && ttisstring(r)) /* both are strings? */
+ return l_strcmp(tsvalue(l), tsvalue(r)) < 0;
+ else
+ return luaT_callorderTM(L, l, r, TM_LT);
+}
+
+
+/*
+** Main operation less than; return 'l < r'.
+*/
+int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
+ if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */
+ return LTnum(l, r);
+ else return lessthanothers(L, l, r);
+}
+
+
+/*
+** return 'l <= r' for non-numbers.
+*/
+static int lessequalothers (lua_State *L, const TValue *l, const TValue *r) {
+ lua_assert(!ttisnumber(l) || !ttisnumber(r));
+ if (ttisstring(l) && ttisstring(r)) /* both are strings? */
+ return l_strcmp(tsvalue(l), tsvalue(r)) <= 0;
+ else
+ return luaT_callorderTM(L, l, r, TM_LE);
+}
+
+
+/*
+** Main operation less than or equal to; return 'l <= r'.
+*/
+int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) {
+ if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */
+ return LEnum(l, r);
+ else return lessequalothers(L, l, r);
+}
+
+
+/*
+** Main operation for equality of Lua values; return 't1 == t2'.
+** L == NULL means raw equality (no metamethods)
+*/
+int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
+ const TValue *tm;
+ if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */
+ if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER)
+ return 0; /* only numbers can be equal with different variants */
+ else { /* two numbers with different variants */
+ /* One of them is an integer. If the other does not have an
+ integer value, they cannot be equal; otherwise, compare their
+ integer values. */
+ lua_Integer i1, i2;
+ return (luaV_tointegerns(t1, &i1, F2Ieq) &&
+ luaV_tointegerns(t2, &i2, F2Ieq) &&
+ i1 == i2);
+ }
+ }
+ /* values have same type and same variant */
+ switch (ttypetag(t1)) {
+ case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1;
+ case LUA_VNUMINT: return (ivalue(t1) == ivalue(t2));
+ case LUA_VNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2));
+ case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
+ case LUA_VLCF: return fvalue(t1) == fvalue(t2);
+ case LUA_VSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2));
+ case LUA_VLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2));
+ case LUA_VUSERDATA: {
+ if (uvalue(t1) == uvalue(t2)) return 1;
+ else if (L == NULL) return 0;
+ tm = fasttm(L, uvalue(t1)->metatable, TM_EQ);
+ if (tm == NULL)
+ tm = fasttm(L, uvalue(t2)->metatable, TM_EQ);
+ break; /* will try TM */
+ }
+ case LUA_VTABLE: {
+ if (hvalue(t1) == hvalue(t2)) return 1;
+ else if (L == NULL) return 0;
+ tm = fasttm(L, hvalue(t1)->metatable, TM_EQ);
+ if (tm == NULL)
+ tm = fasttm(L, hvalue(t2)->metatable, TM_EQ);
+ break; /* will try TM */
+ }
+ default:
+ return gcvalue(t1) == gcvalue(t2);
+ }
+ if (tm == NULL) /* no TM? */
+ return 0; /* objects are different */
+ else {
+ luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */
+ return !l_isfalse(s2v(L->top.p));
+ }
+}
+
+
+/* macro used by 'luaV_concat' to ensure that element at 'o' is a string */
+#define tostring(L,o) \
+ (ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1)))
+
+#define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0)
+
+/* copy strings in stack from top - n up to top - 1 to buffer */
+static void copy2buff (StkId top, int n, char *buff) {
+ size_t tl = 0; /* size already copied */
+ do {
+ size_t l = vslen(s2v(top - n)); /* length of string being copied */
+ memcpy(buff + tl, svalue(s2v(top - n)), l * sizeof(char));
+ tl += l;
+ } while (--n > 0);
+}
+
+
+/*
+** Main operation for concatenation: concat 'total' values in the stack,
+** from 'L->top.p - total' up to 'L->top.p - 1'.
+*/
+void luaV_concat (lua_State *L, int total) {
+ if (total == 1)
+ return; /* "all" values already concatenated */
+ do {
+ StkId top = L->top.p;
+ int n = 2; /* number of elements handled in this pass (at least 2) */
+ if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) ||
+ !tostring(L, s2v(top - 1)))
+ luaT_tryconcatTM(L); /* may invalidate 'top' */
+ else if (isemptystr(s2v(top - 1))) /* second operand is empty? */
+ cast_void(tostring(L, s2v(top - 2))); /* result is first operand */
+ else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */
+ setobjs2s(L, top - 2, top - 1); /* result is second op. */
+ }
+ else {
+ /* at least two non-empty string values; get as many as possible */
+ size_t tl = vslen(s2v(top - 1));
+ TString *ts;
+ /* collect total length and number of strings */
+ for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
+ size_t l = vslen(s2v(top - n - 1));
+ if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) {
+ L->top.p = top - total; /* pop strings to avoid wasting stack */
+ luaG_runerror(L, "string length overflow");
+ }
+ tl += l;
+ }
+ if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */
+ char buff[LUAI_MAXSHORTLEN];
+ copy2buff(top, n, buff); /* copy strings to buffer */
+ ts = luaS_newlstr(L, buff, tl);
+ }
+ else { /* long string; copy strings directly to final result */
+ ts = luaS_createlngstrobj(L, tl);
+ copy2buff(top, n, getstr(ts));
+ }
+ setsvalue2s(L, top - n, ts); /* create result */
+ }
+ total -= n - 1; /* got 'n' strings to create one new */
+ L->top.p -= n - 1; /* popped 'n' strings and pushed one */
+ } while (total > 1); /* repeat until only 1 result left */
+}
+
+
+/*
+** Main operation 'ra = #rb'.
+*/
+void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
+ const TValue *tm;
+ switch (ttypetag(rb)) {
+ case LUA_VTABLE: {
+ Table *h = hvalue(rb);
+ tm = fasttm(L, h->metatable, TM_LEN);
+ if (tm) break; /* metamethod? break switch to call it */
+ setivalue(s2v(ra), luaH_getn(h)); /* else primitive len */
+ return;
+ }
+ case LUA_VSHRSTR: {
+ setivalue(s2v(ra), tsvalue(rb)->shrlen);
+ return;
+ }
+ case LUA_VLNGSTR: {
+ setivalue(s2v(ra), tsvalue(rb)->u.lnglen);
+ return;
+ }
+ default: { /* try metamethod */
+ tm = luaT_gettmbyobj(L, rb, TM_LEN);
+ if (l_unlikely(notm(tm))) /* no metamethod? */
+ luaG_typeerror(L, rb, "get length of");
+ break;
+ }
+ }
+ luaT_callTMres(L, tm, rb, rb, ra);
+}
+
+
+/*
+** Integer division; return 'm // n', that is, floor(m/n).
+** C division truncates its result (rounds towards zero).
+** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer,
+** otherwise 'floor(q) == trunc(q) - 1'.
+*/
+lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) {
+ if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
+ if (n == 0)
+ luaG_runerror(L, "attempt to divide by zero");
+ return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */
+ }
+ else {
+ lua_Integer q = m / n; /* perform C division */
+ if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */
+ q -= 1; /* correct result for different rounding */
+ return q;
+ }
+}
+
+
+/*
+** Integer modulus; return 'm % n'. (Assume that C '%' with
+** negative operands follows C99 behavior. See previous comment
+** about luaV_idiv.)
+*/
+lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) {
+ if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
+ if (n == 0)
+ luaG_runerror(L, "attempt to perform 'n%%0'");
+ return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */
+ }
+ else {
+ lua_Integer r = m % n;
+ if (r != 0 && (r ^ n) < 0) /* 'm/n' would be non-integer negative? */
+ r += n; /* correct result for different rounding */
+ return r;
+ }
+}
+
+
+/*
+** Float modulus
+*/
+lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) {
+ lua_Number r;
+ luai_nummod(L, m, n, r);
+ return r;
+}
+
+
+/* number of bits in an integer */
+#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT)
+
+
+/*
+** Shift left operation. (Shift right just negates 'y'.)
+*/
+lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) {
+ if (y < 0) { /* shift right? */
+ if (y <= -NBITS) return 0;
+ else return intop(>>, x, -y);
+ }
+ else { /* shift left */
+ if (y >= NBITS) return 0;
+ else return intop(<<, x, y);
+ }
+}
+
+
+/*
+** create a new Lua closure, push it in the stack, and initialize
+** its upvalues.
+*/
+static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base,
+ StkId ra) {
+ int nup = p->sizeupvalues;
+ Upvaldesc *uv = p->upvalues;
+ int i;
+ LClosure *ncl = luaF_newLclosure(L, nup);
+ ncl->p = p;
+ setclLvalue2s(L, ra, ncl); /* anchor new closure in stack */
+ for (i = 0; i < nup; i++) { /* fill in its upvalues */
+ if (uv[i].instack) /* upvalue refers to local variable? */
+ ncl->upvals[i] = luaF_findupval(L, base + uv[i].idx);
+ else /* get upvalue from enclosing function */
+ ncl->upvals[i] = encup[uv[i].idx];
+ luaC_objbarrier(L, ncl, ncl->upvals[i]);
+ }
+}
+
+
+/*
+** finish execution of an opcode interrupted by a yield
+*/
+void luaV_finishOp (lua_State *L) {
+ CallInfo *ci = L->ci;
+ StkId base = ci->func.p + 1;
+ Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */
+ OpCode op = GET_OPCODE(inst);
+ switch (op) { /* finish its execution */
+ case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
+ setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top.p);
+ break;
+ }
+ case OP_UNM: case OP_BNOT: case OP_LEN:
+ case OP_GETTABUP: case OP_GETTABLE: case OP_GETI:
+ case OP_GETFIELD: case OP_SELF: {
+ setobjs2s(L, base + GETARG_A(inst), --L->top.p);
+ break;
+ }
+ case OP_LT: case OP_LE:
+ case OP_LTI: case OP_LEI:
+ case OP_GTI: case OP_GEI:
+ case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */
+ int res = !l_isfalse(s2v(L->top.p - 1));
+ L->top.p--;
+#if defined(LUA_COMPAT_LT_LE)
+ if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
+ ci->callstatus ^= CIST_LEQ; /* clear mark */
+ res = !res; /* negate result */
+ }
+#endif
+ lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
+ if (res != GETARG_k(inst)) /* condition failed? */
+ ci->u.l.savedpc++; /* skip jump instruction */
+ break;
+ }
+ case OP_CONCAT: {
+ StkId top = L->top.p - 1; /* top when 'luaT_tryconcatTM' was called */
+ int a = GETARG_A(inst); /* first element to concatenate */
+ int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */
+ setobjs2s(L, top - 2, top); /* put TM result in proper position */
+ L->top.p = top - 1; /* top is one after last element (at top-2) */
+ luaV_concat(L, total); /* concat them (may yield again) */
+ break;
+ }
+ case OP_CLOSE: { /* yielded closing variables */
+ ci->u.l.savedpc--; /* repeat instruction to close other vars. */
+ break;
+ }
+ case OP_RETURN: { /* yielded closing variables */
+ StkId ra = base + GETARG_A(inst);
+ /* adjust top to signal correct number of returns, in case the
+ return is "up to top" ('isIT') */
+ L->top.p = ra + ci->u2.nres;
+ /* repeat instruction to close other vars. and complete the return */
+ ci->u.l.savedpc--;
+ break;
+ }
+ default: {
+ /* only these other opcodes can yield */
+ lua_assert(op == OP_TFORCALL || op == OP_CALL ||
+ op == OP_TAILCALL || op == OP_SETTABUP || op == OP_SETTABLE ||
+ op == OP_SETI || op == OP_SETFIELD);
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+** {==================================================================
+** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute'
+** ===================================================================
+*/
+
+#define l_addi(L,a,b) intop(+, a, b)
+#define l_subi(L,a,b) intop(-, a, b)
+#define l_muli(L,a,b) intop(*, a, b)
+#define l_band(a,b) intop(&, a, b)
+#define l_bor(a,b) intop(|, a, b)
+#define l_bxor(a,b) intop(^, a, b)
+
+#define l_lti(a,b) (a < b)
+#define l_lei(a,b) (a <= b)
+#define l_gti(a,b) (a > b)
+#define l_gei(a,b) (a >= b)
+
+
+/*
+** Arithmetic operations with immediate operands. 'iop' is the integer
+** operation, 'fop' is the float operation.
+*/
+#define op_arithI(L,iop,fop) { \
+ StkId ra = RA(i); \
+ TValue *v1 = vRB(i); \
+ int imm = GETARG_sC(i); \
+ if (ttisinteger(v1)) { \
+ lua_Integer iv1 = ivalue(v1); \
+ pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \
+ } \
+ else if (ttisfloat(v1)) { \
+ lua_Number nb = fltvalue(v1); \
+ lua_Number fimm = cast_num(imm); \
+ pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \
+ }}
+
+
+/*
+** Auxiliary function for arithmetic operations over floats and others
+** with two register operands.
+*/
+#define op_arithf_aux(L,v1,v2,fop) { \
+ lua_Number n1; lua_Number n2; \
+ if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
+ pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
+ }}
+
+
+/*
+** Arithmetic operations over floats and others with register operands.
+*/
+#define op_arithf(L,fop) { \
+ StkId ra = RA(i); \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = vRC(i); \
+ op_arithf_aux(L, v1, v2, fop); }
+
+
+/*
+** Arithmetic operations with K operands for floats.
+*/
+#define op_arithfK(L,fop) { \
+ StkId ra = RA(i); \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
+ op_arithf_aux(L, v1, v2, fop); }
+
+
+/*
+** Arithmetic operations over integers and floats.
+*/
+#define op_arith_aux(L,v1,v2,iop,fop) { \
+ StkId ra = RA(i); \
+ if (ttisinteger(v1) && ttisinteger(v2)) { \
+ lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
+ pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
+ } \
+ else op_arithf_aux(L, v1, v2, fop); }
+
+
+/*
+** Arithmetic operations with register operands.
+*/
+#define op_arith(L,iop,fop) { \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = vRC(i); \
+ op_arith_aux(L, v1, v2, iop, fop); }
+
+
+/*
+** Arithmetic operations with K operands.
+*/
+#define op_arithK(L,iop,fop) { \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
+ op_arith_aux(L, v1, v2, iop, fop); }
+
+
+/*
+** Bitwise operations with constant operand.
+*/
+#define op_bitwiseK(L,op) { \
+ StkId ra = RA(i); \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = KC(i); \
+ lua_Integer i1; \
+ lua_Integer i2 = ivalue(v2); \
+ if (tointegerns(v1, &i1)) { \
+ pc++; setivalue(s2v(ra), op(i1, i2)); \
+ }}
+
+
+/*
+** Bitwise operations with register operands.
+*/
+#define op_bitwise(L,op) { \
+ StkId ra = RA(i); \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = vRC(i); \
+ lua_Integer i1; lua_Integer i2; \
+ if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \
+ pc++; setivalue(s2v(ra), op(i1, i2)); \
+ }}
+
+
+/*
+** Order operations with register operands. 'opn' actually works
+** for all numbers, but the fast track improves performance for
+** integers.
+*/
+#define op_order(L,opi,opn,other) { \
+ StkId ra = RA(i); \
+ int cond; \
+ TValue *rb = vRB(i); \
+ if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \
+ lua_Integer ia = ivalue(s2v(ra)); \
+ lua_Integer ib = ivalue(rb); \
+ cond = opi(ia, ib); \
+ } \
+ else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \
+ cond = opn(s2v(ra), rb); \
+ else \
+ Protect(cond = other(L, s2v(ra), rb)); \
+ docondjump(); }
+
+
+/*
+** Order operations with immediate operand. (Immediate operand is
+** always small enough to have an exact representation as a float.)
+*/
+#define op_orderI(L,opi,opf,inv,tm) { \
+ StkId ra = RA(i); \
+ int cond; \
+ int im = GETARG_sB(i); \
+ if (ttisinteger(s2v(ra))) \
+ cond = opi(ivalue(s2v(ra)), im); \
+ else if (ttisfloat(s2v(ra))) { \
+ lua_Number fa = fltvalue(s2v(ra)); \
+ lua_Number fim = cast_num(im); \
+ cond = opf(fa, fim); \
+ } \
+ else { \
+ int isf = GETARG_C(i); \
+ Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \
+ } \
+ docondjump(); }
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Function 'luaV_execute': main interpreter loop
+** ===================================================================
+*/
+
+/*
+** some macros for common tasks in 'luaV_execute'
+*/
+
+
+#define RA(i) (base+GETARG_A(i))
+#define RB(i) (base+GETARG_B(i))
+#define vRB(i) s2v(RB(i))
+#define KB(i) (k+GETARG_B(i))
+#define RC(i) (base+GETARG_C(i))
+#define vRC(i) s2v(RC(i))
+#define KC(i) (k+GETARG_C(i))
+#define RKC(i) ((TESTARG_k(i)) ? k + GETARG_C(i) : s2v(base + GETARG_C(i)))
+
+
+
+#define updatetrap(ci) (trap = ci->u.l.trap)
+
+#define updatebase(ci) (base = ci->func.p + 1)
+
+
+#define updatestack(ci) \
+ { if (l_unlikely(trap)) { updatebase(ci); ra = RA(i); } }
+
+
+/*
+** Execute a jump instruction. The 'updatetrap' allows signals to stop
+** tight loops. (Without it, the local copy of 'trap' could never change.)
+*/
+#define dojump(ci,i,e) { pc += GETARG_sJ(i) + e; updatetrap(ci); }
+
+
+/* for test instructions, execute the jump instruction that follows it */
+#define donextjump(ci) { Instruction ni = *pc; dojump(ci, ni, 1); }
+
+/*
+** do a conditional jump: skip next instruction if 'cond' is not what
+** was expected (parameter 'k'), else do next instruction, which must
+** be a jump.
+*/
+#define docondjump() if (cond != GETARG_k(i)) pc++; else donextjump(ci);
+
+
+/*
+** Correct global 'pc'.
+*/
+#define savepc(L) (ci->u.l.savedpc = pc)
+
+
+/*
+** Whenever code can raise errors, the global 'pc' and the global
+** 'top' must be correct to report occasional errors.
+*/
+#define savestate(L,ci) (savepc(L), L->top.p = ci->top.p)
+
+
+/*
+** Protect code that, in general, can raise errors, reallocate the
+** stack, and change the hooks.
+*/
+#define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci))
+
+/* special version that does not change the top */
+#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci))
+
+/*
+** Protect code that can only raise errors. (That is, it cannot change
+** the stack or hooks.)
+*/
+#define halfProtect(exp) (savestate(L,ci), (exp))
+
+/* 'c' is the limit of live values in the stack */
+#define checkGC(L,c) \
+ { luaC_condGC(L, (savepc(L), L->top.p = (c)), \
+ updatetrap(ci)); \
+ luai_threadyield(L); }
+
+
+/* fetch an instruction and prepare its execution */
+#define vmfetch() { \
+ if (l_unlikely(trap)) { /* stack reallocation or hooks? */ \
+ trap = luaG_traceexec(L, pc); /* handle hooks */ \
+ updatebase(ci); /* correct stack */ \
+ } \
+ i = *(pc++); \
+}
+
+#define vmdispatch(o) switch(o)
+#define vmcase(l) case l:
+#define vmbreak break
+
+
+void luaV_execute (lua_State *L, CallInfo *ci) {
+ LClosure *cl;
+ TValue *k;
+ StkId base;
+ const Instruction *pc;
+ int trap;
+#if LUA_USE_JUMPTABLE
+#include "ljumptab.h"
+#endif
+ startfunc:
+ trap = L->hookmask;
+ returning: /* trap already set */
+ cl = clLvalue(s2v(ci->func.p));
+ k = cl->p->k;
+ pc = ci->u.l.savedpc;
+ if (l_unlikely(trap)) {
+ if (pc == cl->p->code) { /* first instruction (not resuming)? */
+ if (cl->p->is_vararg)
+ trap = 0; /* hooks will start after VARARGPREP instruction */
+ else /* check 'call' hook */
+ luaD_hookcall(L, ci);
+ }
+ ci->u.l.trap = 1; /* assume trap is on, for now */
+ }
+ base = ci->func.p + 1;
+ /* main loop of interpreter */
+ for (;;) {
+ Instruction i; /* instruction being executed */
+ vmfetch();
+ #if 0
+ /* low-level line tracing for debugging Lua */
+ printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p)));
+ #endif
+ lua_assert(base == ci->func.p + 1);
+ lua_assert(base <= L->top.p && L->top.p <= L->stack_last.p);
+ /* invalidate top for instructions not expecting it */
+ lua_assert(isIT(i) || (cast_void(L->top.p = base), 1));
+ vmdispatch (GET_OPCODE(i)) {
+ vmcase(OP_MOVE) {
+ StkId ra = RA(i);
+ setobjs2s(L, ra, RB(i));
+ vmbreak;
+ }
+ vmcase(OP_LOADI) {
+ StkId ra = RA(i);
+ lua_Integer b = GETARG_sBx(i);
+ setivalue(s2v(ra), b);
+ vmbreak;
+ }
+ vmcase(OP_LOADF) {
+ StkId ra = RA(i);
+ int b = GETARG_sBx(i);
+ setfltvalue(s2v(ra), cast_num(b));
+ vmbreak;
+ }
+ vmcase(OP_LOADK) {
+ StkId ra = RA(i);
+ TValue *rb = k + GETARG_Bx(i);
+ setobj2s(L, ra, rb);
+ vmbreak;
+ }
+ vmcase(OP_LOADKX) {
+ StkId ra = RA(i);
+ TValue *rb;
+ rb = k + GETARG_Ax(*pc); pc++;
+ setobj2s(L, ra, rb);
+ vmbreak;
+ }
+ vmcase(OP_LOADFALSE) {
+ StkId ra = RA(i);
+ setbfvalue(s2v(ra));
+ vmbreak;
+ }
+ vmcase(OP_LFALSESKIP) {
+ StkId ra = RA(i);
+ setbfvalue(s2v(ra));
+ pc++; /* skip next instruction */
+ vmbreak;
+ }
+ vmcase(OP_LOADTRUE) {
+ StkId ra = RA(i);
+ setbtvalue(s2v(ra));
+ vmbreak;
+ }
+ vmcase(OP_LOADNIL) {
+ StkId ra = RA(i);
+ int b = GETARG_B(i);
+ do {
+ setnilvalue(s2v(ra++));
+ } while (b--);
+ vmbreak;
+ }
+ vmcase(OP_GETUPVAL) {
+ StkId ra = RA(i);
+ int b = GETARG_B(i);
+ setobj2s(L, ra, cl->upvals[b]->v.p);
+ vmbreak;
+ }
+ vmcase(OP_SETUPVAL) {
+ StkId ra = RA(i);
+ UpVal *uv = cl->upvals[GETARG_B(i)];
+ setobj(L, uv->v.p, s2v(ra));
+ luaC_barrier(L, uv, s2v(ra));
+ vmbreak;
+ }
+ vmcase(OP_GETTABUP) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *upval = cl->upvals[GETARG_B(i)]->v.p;
+ TValue *rc = KC(i);
+ TString *key = tsvalue(rc); /* key must be a string */
+ if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) {
+ setobj2s(L, ra, slot);
+ }
+ else
+ Protect(luaV_finishget(L, upval, rc, ra, slot));
+ vmbreak;
+ }
+ vmcase(OP_GETTABLE) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = vRB(i);
+ TValue *rc = vRC(i);
+ lua_Unsigned n;
+ if (ttisinteger(rc) /* fast track for integers? */
+ ? (cast_void(n = ivalue(rc)), luaV_fastgeti(L, rb, n, slot))
+ : luaV_fastget(L, rb, rc, slot, luaH_get)) {
+ setobj2s(L, ra, slot);
+ }
+ else
+ Protect(luaV_finishget(L, rb, rc, ra, slot));
+ vmbreak;
+ }
+ vmcase(OP_GETI) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = vRB(i);
+ int c = GETARG_C(i);
+ if (luaV_fastgeti(L, rb, c, slot)) {
+ setobj2s(L, ra, slot);
+ }
+ else {
+ TValue key;
+ setivalue(&key, c);
+ Protect(luaV_finishget(L, rb, &key, ra, slot));
+ }
+ vmbreak;
+ }
+ vmcase(OP_GETFIELD) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = vRB(i);
+ TValue *rc = KC(i);
+ TString *key = tsvalue(rc); /* key must be a string */
+ if (luaV_fastget(L, rb, key, slot, luaH_getshortstr)) {
+ setobj2s(L, ra, slot);
+ }
+ else
+ Protect(luaV_finishget(L, rb, rc, ra, slot));
+ vmbreak;
+ }
+ vmcase(OP_SETTABUP) {
+ const TValue *slot;
+ TValue *upval = cl->upvals[GETARG_A(i)]->v.p;
+ TValue *rb = KB(i);
+ TValue *rc = RKC(i);
+ TString *key = tsvalue(rb); /* key must be a string */
+ if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) {
+ luaV_finishfastset(L, upval, slot, rc);
+ }
+ else
+ Protect(luaV_finishset(L, upval, rb, rc, slot));
+ vmbreak;
+ }
+ vmcase(OP_SETTABLE) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = vRB(i); /* key (table is in 'ra') */
+ TValue *rc = RKC(i); /* value */
+ lua_Unsigned n;
+ if (ttisinteger(rb) /* fast track for integers? */
+ ? (cast_void(n = ivalue(rb)), luaV_fastgeti(L, s2v(ra), n, slot))
+ : luaV_fastget(L, s2v(ra), rb, slot, luaH_get)) {
+ luaV_finishfastset(L, s2v(ra), slot, rc);
+ }
+ else
+ Protect(luaV_finishset(L, s2v(ra), rb, rc, slot));
+ vmbreak;
+ }
+ vmcase(OP_SETI) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ int c = GETARG_B(i);
+ TValue *rc = RKC(i);
+ if (luaV_fastgeti(L, s2v(ra), c, slot)) {
+ luaV_finishfastset(L, s2v(ra), slot, rc);
+ }
+ else {
+ TValue key;
+ setivalue(&key, c);
+ Protect(luaV_finishset(L, s2v(ra), &key, rc, slot));
+ }
+ vmbreak;
+ }
+ vmcase(OP_SETFIELD) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = KB(i);
+ TValue *rc = RKC(i);
+ TString *key = tsvalue(rb); /* key must be a string */
+ if (luaV_fastget(L, s2v(ra), key, slot, luaH_getshortstr)) {
+ luaV_finishfastset(L, s2v(ra), slot, rc);
+ }
+ else
+ Protect(luaV_finishset(L, s2v(ra), rb, rc, slot));
+ vmbreak;
+ }
+ vmcase(OP_NEWTABLE) {
+ StkId ra = RA(i);
+ int b = GETARG_B(i); /* log2(hash size) + 1 */
+ int c = GETARG_C(i); /* array size */
+ Table *t;
+ if (b > 0)
+ b = 1 << (b - 1); /* size is 2^(b - 1) */
+ lua_assert((!TESTARG_k(i)) == (GETARG_Ax(*pc) == 0));
+ if (TESTARG_k(i)) /* non-zero extra argument? */
+ c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */
+ pc++; /* skip extra argument */
+ L->top.p = ra + 1; /* correct top in case of emergency GC */
+ t = luaH_new(L); /* memory allocation */
+ sethvalue2s(L, ra, t);
+ if (b != 0 || c != 0)
+ luaH_resize(L, t, c, b); /* idem */
+ checkGC(L, ra + 1);
+ vmbreak;
+ }
+ vmcase(OP_SELF) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = vRB(i);
+ TValue *rc = RKC(i);
+ TString *key = tsvalue(rc); /* key must be a string */
+ setobj2s(L, ra + 1, rb);
+ if (luaV_fastget(L, rb, key, slot, luaH_getstr)) {
+ setobj2s(L, ra, slot);
+ }
+ else
+ Protect(luaV_finishget(L, rb, rc, ra, slot));
+ vmbreak;
+ }
+ vmcase(OP_ADDI) {
+ op_arithI(L, l_addi, luai_numadd);
+ vmbreak;
+ }
+ vmcase(OP_ADDK) {
+ op_arithK(L, l_addi, luai_numadd);
+ vmbreak;
+ }
+ vmcase(OP_SUBK) {
+ op_arithK(L, l_subi, luai_numsub);
+ vmbreak;
+ }
+ vmcase(OP_MULK) {
+ op_arithK(L, l_muli, luai_nummul);
+ vmbreak;
+ }
+ vmcase(OP_MODK) {
+ savestate(L, ci); /* in case of division by 0 */
+ op_arithK(L, luaV_mod, luaV_modf);
+ vmbreak;
+ }
+ vmcase(OP_POWK) {
+ op_arithfK(L, luai_numpow);
+ vmbreak;
+ }
+ vmcase(OP_DIVK) {
+ op_arithfK(L, luai_numdiv);
+ vmbreak;
+ }
+ vmcase(OP_IDIVK) {
+ savestate(L, ci); /* in case of division by 0 */
+ op_arithK(L, luaV_idiv, luai_numidiv);
+ vmbreak;
+ }
+ vmcase(OP_BANDK) {
+ op_bitwiseK(L, l_band);
+ vmbreak;
+ }
+ vmcase(OP_BORK) {
+ op_bitwiseK(L, l_bor);
+ vmbreak;
+ }
+ vmcase(OP_BXORK) {
+ op_bitwiseK(L, l_bxor);
+ vmbreak;
+ }
+ vmcase(OP_SHRI) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ int ic = GETARG_sC(i);
+ lua_Integer ib;
+ if (tointegerns(rb, &ib)) {
+ pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic));
+ }
+ vmbreak;
+ }
+ vmcase(OP_SHLI) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ int ic = GETARG_sC(i);
+ lua_Integer ib;
+ if (tointegerns(rb, &ib)) {
+ pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib));
+ }
+ vmbreak;
+ }
+ vmcase(OP_ADD) {
+ op_arith(L, l_addi, luai_numadd);
+ vmbreak;
+ }
+ vmcase(OP_SUB) {
+ op_arith(L, l_subi, luai_numsub);
+ vmbreak;
+ }
+ vmcase(OP_MUL) {
+ op_arith(L, l_muli, luai_nummul);
+ vmbreak;
+ }
+ vmcase(OP_MOD) {
+ savestate(L, ci); /* in case of division by 0 */
+ op_arith(L, luaV_mod, luaV_modf);
+ vmbreak;
+ }
+ vmcase(OP_POW) {
+ op_arithf(L, luai_numpow);
+ vmbreak;
+ }
+ vmcase(OP_DIV) { /* float division (always with floats) */
+ op_arithf(L, luai_numdiv);
+ vmbreak;
+ }
+ vmcase(OP_IDIV) { /* floor division */
+ savestate(L, ci); /* in case of division by 0 */
+ op_arith(L, luaV_idiv, luai_numidiv);
+ vmbreak;
+ }
+ vmcase(OP_BAND) {
+ op_bitwise(L, l_band);
+ vmbreak;
+ }
+ vmcase(OP_BOR) {
+ op_bitwise(L, l_bor);
+ vmbreak;
+ }
+ vmcase(OP_BXOR) {
+ op_bitwise(L, l_bxor);
+ vmbreak;
+ }
+ vmcase(OP_SHR) {
+ op_bitwise(L, luaV_shiftr);
+ vmbreak;
+ }
+ vmcase(OP_SHL) {
+ op_bitwise(L, luaV_shiftl);
+ vmbreak;
+ }
+ vmcase(OP_MMBIN) {
+ StkId ra = RA(i);
+ Instruction pi = *(pc - 2); /* original arith. expression */
+ TValue *rb = vRB(i);
+ TMS tm = (TMS)GETARG_C(i);
+ StkId result = RA(pi);
+ lua_assert(OP_ADD <= GET_OPCODE(pi) && GET_OPCODE(pi) <= OP_SHR);
+ Protect(luaT_trybinTM(L, s2v(ra), rb, result, tm));
+ vmbreak;
+ }
+ vmcase(OP_MMBINI) {
+ StkId ra = RA(i);
+ Instruction pi = *(pc - 2); /* original arith. expression */
+ int imm = GETARG_sB(i);
+ TMS tm = (TMS)GETARG_C(i);
+ int flip = GETARG_k(i);
+ StkId result = RA(pi);
+ Protect(luaT_trybiniTM(L, s2v(ra), imm, flip, result, tm));
+ vmbreak;
+ }
+ vmcase(OP_MMBINK) {
+ StkId ra = RA(i);
+ Instruction pi = *(pc - 2); /* original arith. expression */
+ TValue *imm = KB(i);
+ TMS tm = (TMS)GETARG_C(i);
+ int flip = GETARG_k(i);
+ StkId result = RA(pi);
+ Protect(luaT_trybinassocTM(L, s2v(ra), imm, flip, result, tm));
+ vmbreak;
+ }
+ vmcase(OP_UNM) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ lua_Number nb;
+ if (ttisinteger(rb)) {
+ lua_Integer ib = ivalue(rb);
+ setivalue(s2v(ra), intop(-, 0, ib));
+ }
+ else if (tonumberns(rb, nb)) {
+ setfltvalue(s2v(ra), luai_numunm(L, nb));
+ }
+ else
+ Protect(luaT_trybinTM(L, rb, rb, ra, TM_UNM));
+ vmbreak;
+ }
+ vmcase(OP_BNOT) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ lua_Integer ib;
+ if (tointegerns(rb, &ib)) {
+ setivalue(s2v(ra), intop(^, ~l_castS2U(0), ib));
+ }
+ else
+ Protect(luaT_trybinTM(L, rb, rb, ra, TM_BNOT));
+ vmbreak;
+ }
+ vmcase(OP_NOT) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ if (l_isfalse(rb))
+ setbtvalue(s2v(ra));
+ else
+ setbfvalue(s2v(ra));
+ vmbreak;
+ }
+ vmcase(OP_LEN) {
+ StkId ra = RA(i);
+ Protect(luaV_objlen(L, ra, vRB(i)));
+ vmbreak;
+ }
+ vmcase(OP_CONCAT) {
+ StkId ra = RA(i);
+ int n = GETARG_B(i); /* number of elements to concatenate */
+ L->top.p = ra + n; /* mark the end of concat operands */
+ ProtectNT(luaV_concat(L, n));
+ checkGC(L, L->top.p); /* 'luaV_concat' ensures correct top */
+ vmbreak;
+ }
+ vmcase(OP_CLOSE) {
+ StkId ra = RA(i);
+ Protect(luaF_close(L, ra, LUA_OK, 1));
+ vmbreak;
+ }
+ vmcase(OP_TBC) {
+ StkId ra = RA(i);
+ /* create new to-be-closed upvalue */
+ halfProtect(luaF_newtbcupval(L, ra));
+ vmbreak;
+ }
+ vmcase(OP_JMP) {
+ dojump(ci, i, 0);
+ vmbreak;
+ }
+ vmcase(OP_EQ) {
+ StkId ra = RA(i);
+ int cond;
+ TValue *rb = vRB(i);
+ Protect(cond = luaV_equalobj(L, s2v(ra), rb));
+ docondjump();
+ vmbreak;
+ }
+ vmcase(OP_LT) {
+ op_order(L, l_lti, LTnum, lessthanothers);
+ vmbreak;
+ }
+ vmcase(OP_LE) {
+ op_order(L, l_lei, LEnum, lessequalothers);
+ vmbreak;
+ }
+ vmcase(OP_EQK) {
+ StkId ra = RA(i);
+ TValue *rb = KB(i);
+ /* basic types do not use '__eq'; we can use raw equality */
+ int cond = luaV_rawequalobj(s2v(ra), rb);
+ docondjump();
+ vmbreak;
+ }
+ vmcase(OP_EQI) {
+ StkId ra = RA(i);
+ int cond;
+ int im = GETARG_sB(i);
+ if (ttisinteger(s2v(ra)))
+ cond = (ivalue(s2v(ra)) == im);
+ else if (ttisfloat(s2v(ra)))
+ cond = luai_numeq(fltvalue(s2v(ra)), cast_num(im));
+ else
+ cond = 0; /* other types cannot be equal to a number */
+ docondjump();
+ vmbreak;
+ }
+ vmcase(OP_LTI) {
+ op_orderI(L, l_lti, luai_numlt, 0, TM_LT);
+ vmbreak;
+ }
+ vmcase(OP_LEI) {
+ op_orderI(L, l_lei, luai_numle, 0, TM_LE);
+ vmbreak;
+ }
+ vmcase(OP_GTI) {
+ op_orderI(L, l_gti, luai_numgt, 1, TM_LT);
+ vmbreak;
+ }
+ vmcase(OP_GEI) {
+ op_orderI(L, l_gei, luai_numge, 1, TM_LE);
+ vmbreak;
+ }
+ vmcase(OP_TEST) {
+ StkId ra = RA(i);
+ int cond = !l_isfalse(s2v(ra));
+ docondjump();
+ vmbreak;
+ }
+ vmcase(OP_TESTSET) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ if (l_isfalse(rb) == GETARG_k(i))
+ pc++;
+ else {
+ setobj2s(L, ra, rb);
+ donextjump(ci);
+ }
+ vmbreak;
+ }
+ vmcase(OP_CALL) {
+ StkId ra = RA(i);
+ CallInfo *newci;
+ int b = GETARG_B(i);
+ int nresults = GETARG_C(i) - 1;
+ if (b != 0) /* fixed number of arguments? */
+ L->top.p = ra + b; /* top signals number of arguments */
+ /* else previous instruction set top */
+ savepc(L); /* in case of errors */
+ if ((newci = luaD_precall(L, ra, nresults)) == NULL)
+ updatetrap(ci); /* C call; nothing else to be done */
+ else { /* Lua call: run function in this same C frame */
+ ci = newci;
+ goto startfunc;
+ }
+ vmbreak;
+ }
+ vmcase(OP_TAILCALL) {
+ StkId ra = RA(i);
+ int b = GETARG_B(i); /* number of arguments + 1 (function) */
+ int n; /* number of results when calling a C function */
+ int nparams1 = GETARG_C(i);
+ /* delta is virtual 'func' - real 'func' (vararg functions) */
+ int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0;
+ if (b != 0)
+ L->top.p = ra + b;
+ else /* previous instruction set top */
+ b = cast_int(L->top.p - ra);
+ savepc(ci); /* several calls here can raise errors */
+ if (TESTARG_k(i)) {
+ luaF_closeupval(L, base); /* close upvalues from current call */
+ lua_assert(L->tbclist.p < base); /* no pending tbc variables */
+ lua_assert(base == ci->func.p + 1);
+ }
+ if ((n = luaD_pretailcall(L, ci, ra, b, delta)) < 0) /* Lua function? */
+ goto startfunc; /* execute the callee */
+ else { /* C function? */
+ ci->func.p -= delta; /* restore 'func' (if vararg) */
+ luaD_poscall(L, ci, n); /* finish caller */
+ updatetrap(ci); /* 'luaD_poscall' can change hooks */
+ goto ret; /* caller returns after the tail call */
+ }
+ }
+ vmcase(OP_RETURN) {
+ StkId ra = RA(i);
+ int n = GETARG_B(i) - 1; /* number of results */
+ int nparams1 = GETARG_C(i);
+ if (n < 0) /* not fixed? */
+ n = cast_int(L->top.p - ra); /* get what is available */
+ savepc(ci);
+ if (TESTARG_k(i)) { /* may there be open upvalues? */
+ ci->u2.nres = n; /* save number of returns */
+ if (L->top.p < ci->top.p)
+ L->top.p = ci->top.p;
+ luaF_close(L, base, CLOSEKTOP, 1);
+ updatetrap(ci);
+ updatestack(ci);
+ }
+ if (nparams1) /* vararg function? */
+ ci->func.p -= ci->u.l.nextraargs + nparams1;
+ L->top.p = ra + n; /* set call for 'luaD_poscall' */
+ luaD_poscall(L, ci, n);
+ updatetrap(ci); /* 'luaD_poscall' can change hooks */
+ goto ret;
+ }
+ vmcase(OP_RETURN0) {
+ if (l_unlikely(L->hookmask)) {
+ StkId ra = RA(i);
+ L->top.p = ra;
+ savepc(ci);
+ luaD_poscall(L, ci, 0); /* no hurry... */
+ trap = 1;
+ }
+ else { /* do the 'poscall' here */
+ int nres;
+ L->ci = ci->previous; /* back to caller */
+ L->top.p = base - 1;
+ for (nres = ci->nresults; l_unlikely(nres > 0); nres--)
+ setnilvalue(s2v(L->top.p++)); /* all results are nil */
+ }
+ goto ret;
+ }
+ vmcase(OP_RETURN1) {
+ if (l_unlikely(L->hookmask)) {
+ StkId ra = RA(i);
+ L->top.p = ra + 1;
+ savepc(ci);
+ luaD_poscall(L, ci, 1); /* no hurry... */
+ trap = 1;
+ }
+ else { /* do the 'poscall' here */
+ int nres = ci->nresults;
+ L->ci = ci->previous; /* back to caller */
+ if (nres == 0)
+ L->top.p = base - 1; /* asked for no results */
+ else {
+ StkId ra = RA(i);
+ setobjs2s(L, base - 1, ra); /* at least this result */
+ L->top.p = base;
+ for (; l_unlikely(nres > 1); nres--)
+ setnilvalue(s2v(L->top.p++)); /* complete missing results */
+ }
+ }
+ ret: /* return from a Lua function */
+ if (ci->callstatus & CIST_FRESH)
+ return; /* end this frame */
+ else {
+ ci = ci->previous;
+ goto returning; /* continue running caller in this frame */
+ }
+ }
+ vmcase(OP_FORLOOP) {
+ StkId ra = RA(i);
+ if (ttisinteger(s2v(ra + 2))) { /* integer loop? */
+ lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1)));
+ if (count > 0) { /* still more iterations? */
+ lua_Integer step = ivalue(s2v(ra + 2));
+ lua_Integer idx = ivalue(s2v(ra)); /* internal index */
+ chgivalue(s2v(ra + 1), count - 1); /* update counter */
+ idx = intop(+, idx, step); /* add step to index */
+ chgivalue(s2v(ra), idx); /* update internal index */
+ setivalue(s2v(ra + 3), idx); /* and control variable */
+ pc -= GETARG_Bx(i); /* jump back */
+ }
+ }
+ else if (floatforloop(ra)) /* float loop */
+ pc -= GETARG_Bx(i); /* jump back */
+ updatetrap(ci); /* allows a signal to break the loop */
+ vmbreak;
+ }
+ vmcase(OP_FORPREP) {
+ StkId ra = RA(i);
+ savestate(L, ci); /* in case of errors */
+ if (forprep(L, ra))
+ pc += GETARG_Bx(i) + 1; /* skip the loop */
+ vmbreak;
+ }
+ vmcase(OP_TFORPREP) {
+ StkId ra = RA(i);
+ /* create to-be-closed upvalue (if needed) */
+ halfProtect(luaF_newtbcupval(L, ra + 3));
+ pc += GETARG_Bx(i);
+ i = *(pc++); /* go to next instruction */
+ lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i));
+ goto l_tforcall;
+ }
+ vmcase(OP_TFORCALL) {
+ l_tforcall: {
+ StkId ra = RA(i);
+ /* 'ra' has the iterator function, 'ra + 1' has the state,
+ 'ra + 2' has the control variable, and 'ra + 3' has the
+ to-be-closed variable. The call will use the stack after
+ these values (starting at 'ra + 4')
+ */
+ /* push function, state, and control variable */
+ memcpy(ra + 4, ra, 3 * sizeof(*ra));
+ L->top.p = ra + 4 + 3;
+ ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */
+ updatestack(ci); /* stack may have changed */
+ i = *(pc++); /* go to next instruction */
+ lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i));
+ goto l_tforloop;
+ }}
+ vmcase(OP_TFORLOOP) {
+ l_tforloop: {
+ StkId ra = RA(i);
+ if (!ttisnil(s2v(ra + 4))) { /* continue loop? */
+ setobjs2s(L, ra + 2, ra + 4); /* save control variable */
+ pc -= GETARG_Bx(i); /* jump back */
+ }
+ vmbreak;
+ }}
+ vmcase(OP_SETLIST) {
+ StkId ra = RA(i);
+ int n = GETARG_B(i);
+ unsigned int last = GETARG_C(i);
+ Table *h = hvalue(s2v(ra));
+ if (n == 0)
+ n = cast_int(L->top.p - ra) - 1; /* get up to the top */
+ else
+ L->top.p = ci->top.p; /* correct top in case of emergency GC */
+ last += n;
+ if (TESTARG_k(i)) {
+ last += GETARG_Ax(*pc) * (MAXARG_C + 1);
+ pc++;
+ }
+ if (last > luaH_realasize(h)) /* needs more space? */
+ luaH_resizearray(L, h, last); /* preallocate it at once */
+ for (; n > 0; n--) {
+ TValue *val = s2v(ra + n);
+ setobj2t(L, &h->array[last - 1], val);
+ last--;
+ luaC_barrierback(L, obj2gco(h), val);
+ }
+ vmbreak;
+ }
+ vmcase(OP_CLOSURE) {
+ StkId ra = RA(i);
+ Proto *p = cl->p->p[GETARG_Bx(i)];
+ halfProtect(pushclosure(L, p, cl->upvals, base, ra));
+ checkGC(L, ra + 1);
+ vmbreak;
+ }
+ vmcase(OP_VARARG) {
+ StkId ra = RA(i);
+ int n = GETARG_C(i) - 1; /* required results */
+ Protect(luaT_getvarargs(L, ci, ra, n));
+ vmbreak;
+ }
+ vmcase(OP_VARARGPREP) {
+ ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
+ if (l_unlikely(trap)) { /* previous "Protect" updated trap */
+ luaD_hookcall(L, ci);
+ L->oldpc = 1; /* next opcode will be seen as a "new" line */
+ }
+ updatebase(ci); /* function has new base after adjustment */
+ vmbreak;
+ }
+ vmcase(OP_EXTRAARG) {
+ lua_assert(0);
+ vmbreak;
+ }
+ }
+ }
+}
+
+/* }================================================================== */
diff --git a/src/libs/3rdparty/lua/src/lvm.h b/src/libs/3rdparty/lua/src/lvm.h
new file mode 100644
index 0000000000..dba1ad2770
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lvm.h
@@ -0,0 +1,141 @@
+/*
+** $Id: lvm.h $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lvm_h
+#define lvm_h
+
+
+#include "ldo.h"
+#include "lobject.h"
+#include "ltm.h"
+
+
+#if !defined(LUA_NOCVTN2S)
+#define cvt2str(o) ttisnumber(o)
+#else
+#define cvt2str(o) 0 /* no conversion from numbers to strings */
+#endif
+
+
+#if !defined(LUA_NOCVTS2N)
+#define cvt2num(o) ttisstring(o)
+#else
+#define cvt2num(o) 0 /* no conversion from strings to numbers */
+#endif
+
+
+/*
+** You can define LUA_FLOORN2I if you want to convert floats to integers
+** by flooring them (instead of raising an error if they are not
+** integral values)
+*/
+#if !defined(LUA_FLOORN2I)
+#define LUA_FLOORN2I F2Ieq
+#endif
+
+
+/*
+** Rounding modes for float->integer coercion
+ */
+typedef enum {
+ F2Ieq, /* no rounding; accepts only integral values */
+ F2Ifloor, /* takes the floor of the number */
+ F2Iceil /* takes the ceil of the number */
+} F2Imod;
+
+
+/* convert an object to a float (including string coercion) */
+#define tonumber(o,n) \
+ (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n))
+
+
+/* convert an object to a float (without string coercion) */
+#define tonumberns(o,n) \
+ (ttisfloat(o) ? ((n) = fltvalue(o), 1) : \
+ (ttisinteger(o) ? ((n) = cast_num(ivalue(o)), 1) : 0))
+
+
+/* convert an object to an integer (including string coercion) */
+#define tointeger(o,i) \
+ (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \
+ : luaV_tointeger(o,i,LUA_FLOORN2I))
+
+
+/* convert an object to an integer (without string coercion) */
+#define tointegerns(o,i) \
+ (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \
+ : luaV_tointegerns(o,i,LUA_FLOORN2I))
+
+
+#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2))
+
+#define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2)
+
+
+/*
+** fast track for 'gettable': if 't' is a table and 't[k]' is present,
+** return 1 with 'slot' pointing to 't[k]' (position of final result).
+** Otherwise, return 0 (meaning it will have to check metamethod)
+** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL
+** (otherwise). 'f' is the raw get function to use.
+*/
+#define luaV_fastget(L,t,k,slot,f) \
+ (!ttistable(t) \
+ ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
+ : (slot = f(hvalue(t), k), /* else, do raw access */ \
+ !isempty(slot))) /* result not empty? */
+
+
+/*
+** Special case of 'luaV_fastget' for integers, inlining the fast case
+** of 'luaH_getint'.
+*/
+#define luaV_fastgeti(L,t,k,slot) \
+ (!ttistable(t) \
+ ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
+ : (slot = (l_castS2U(k) - 1u < hvalue(t)->alimit) \
+ ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \
+ !isempty(slot))) /* result not empty? */
+
+
+/*
+** Finish a fast set operation (when fast get succeeds). In that case,
+** 'slot' points to the place to put the value.
+*/
+#define luaV_finishfastset(L,t,slot,v) \
+ { setobj2t(L, cast(TValue *,slot), v); \
+ luaC_barrierback(L, gcvalue(t), v); }
+
+
+/*
+** Shift right is the same as shift left with a negative 'y'
+*/
+#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y))
+
+
+
+LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2);
+LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r);
+LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r);
+LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n);
+LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode);
+LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p,
+ F2Imod mode);
+LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode);
+LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key,
+ StkId val, const TValue *slot);
+LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
+ TValue *val, const TValue *slot);
+LUAI_FUNC void luaV_finishOp (lua_State *L);
+LUAI_FUNC void luaV_execute (lua_State *L, CallInfo *ci);
+LUAI_FUNC void luaV_concat (lua_State *L, int total);
+LUAI_FUNC lua_Integer luaV_idiv (lua_State *L, lua_Integer x, lua_Integer y);
+LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y);
+LUAI_FUNC lua_Number luaV_modf (lua_State *L, lua_Number x, lua_Number y);
+LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y);
+LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb);
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lzio.c b/src/libs/3rdparty/lua/src/lzio.c
new file mode 100644
index 0000000000..cd0a02d5f9
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lzio.c
@@ -0,0 +1,68 @@
+/*
+** $Id: lzio.c $
+** Buffered streams
+** See Copyright Notice in lua.h
+*/
+
+#define lzio_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <string.h>
+
+#include "lua.h"
+
+#include "llimits.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lzio.h"
+
+
+int luaZ_fill (ZIO *z) {
+ size_t size;
+ lua_State *L = z->L;
+ const char *buff;
+ lua_unlock(L);
+ buff = z->reader(L, z->data, &size);
+ lua_lock(L);
+ if (buff == NULL || size == 0)
+ return EOZ;
+ z->n = size - 1; /* discount char being returned */
+ z->p = buff;
+ return cast_uchar(*(z->p++));
+}
+
+
+void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {
+ z->L = L;
+ z->reader = reader;
+ z->data = data;
+ z->n = 0;
+ z->p = NULL;
+}
+
+
+/* --------------------------------------------------------------- read --- */
+size_t luaZ_read (ZIO *z, void *b, size_t n) {
+ while (n) {
+ size_t m;
+ if (z->n == 0) { /* no bytes in buffer? */
+ if (luaZ_fill(z) == EOZ) /* try to read more */
+ return n; /* no more input; return number of missing bytes */
+ else {
+ z->n++; /* luaZ_fill consumed first byte; put it back */
+ z->p--;
+ }
+ }
+ m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
+ memcpy(b, z->p, m);
+ z->n -= m;
+ z->p += m;
+ b = (char *)b + m;
+ n -= m;
+ }
+ return 0;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lzio.h b/src/libs/3rdparty/lua/src/lzio.h
new file mode 100644
index 0000000000..38f397fd28
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lzio.h
@@ -0,0 +1,66 @@
+/*
+** $Id: lzio.h $
+** Buffered streams
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lzio_h
+#define lzio_h
+
+#include "lua.h"
+
+#include "lmem.h"
+
+
+#define EOZ (-1) /* end of stream */
+
+typedef struct Zio ZIO;
+
+#define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z))
+
+
+typedef struct Mbuffer {
+ char *buffer;
+ size_t n;
+ size_t buffsize;
+} Mbuffer;
+
+#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0)
+
+#define luaZ_buffer(buff) ((buff)->buffer)
+#define luaZ_sizebuffer(buff) ((buff)->buffsize)
+#define luaZ_bufflen(buff) ((buff)->n)
+
+#define luaZ_buffremove(buff,i) ((buff)->n -= (i))
+#define luaZ_resetbuffer(buff) ((buff)->n = 0)
+
+
+#define luaZ_resizebuffer(L, buff, size) \
+ ((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \
+ (buff)->buffsize, size), \
+ (buff)->buffsize = size)
+
+#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0)
+
+
+LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader,
+ void *data);
+LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */
+
+
+
+/* --------- Private Part ------------------ */
+
+struct Zio {
+ size_t n; /* bytes still unread */
+ const char *p; /* current position in buffer */
+ lua_Reader reader; /* reader function */
+ void *data; /* additional data */
+ lua_State *L; /* Lua state (for reader) */
+};
+
+
+LUAI_FUNC int luaZ_fill (ZIO *z);
+
+#endif
diff --git a/src/libs/3rdparty/optional/CMakeLists.txt b/src/libs/3rdparty/optional/CMakeLists.txt
deleted file mode 100644
index 07f38f7993..0000000000
--- a/src/libs/3rdparty/optional/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-project(optional)
-cmake_minimum_required(VERSION 2.8)
-enable_testing()
-
-set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra")
-
-add_executable(test_optional test_optional.cpp)
-add_executable(test_type_traits test_type_traits.cpp)
-
-add_test(test_optional test_optional)
-add_test(test_type_traits test_type_traits)
diff --git a/src/libs/3rdparty/optional/README.md b/src/libs/3rdparty/optional/README.md
deleted file mode 100644
index 948516d1ba..0000000000
--- a/src/libs/3rdparty/optional/README.md
+++ /dev/null
@@ -1,39 +0,0 @@
-Optional
-========
-
-A single-header header-only library for representing optional (nullable) objects for C++14 (and C++11 to some extent) and passing them by value. This is the reference implementation of proposal N3793 (see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html). Optional is now accepted into Library Fundamentals Technical Specification (see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3848.html). The interface is based on Fernando Cacciola's Boost.Optional library (see http://www.boost.org/doc/libs/1_52_0/libs/optional/doc/html/index.html)
-
-
-Usage
------
-
-```cpp
-optional<int> readInt(); // this function may return int or a not-an-int
-
-if (optional<int> oi = readInt()) // did I get a real int
- cout << "my int is: " << *oi; // use my int
-else
- cout << "I have no int";
-```
-
-For more usage examples and the overview see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3527.html
-
-
-Supported compilers
--------------------
-
-Clang 3.2, Clang 3.4, G++ 4.7.2, G++ 4.8.1. Tested only with libstdc++, versions 20130531, 20120920, 20110428. Others have reported it also works with libc++.
-
-
-
-Known Issues
-------------
-
- - Currently, the reference (and the only known) implementation of certain pieces of functionality explore what C++11 identifies as undefined behavior (see national body comment FI 15: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3770.html#FI15). This is mostly why Optional was removed from C++14 and put into Library Fundamentals TS. Luckily what the Standard identifies as UB is well defined on all known platforms. We expect that the C++14 wil fix this problem, so that our trick becomes well-defined.
- - In libstdc++ versions below 20130531 the constructor taking `initializer_list` argument is not `constexpr`. This is because `initializer_list` operations are not `constexpr` in C++11. This works however in version 20130531. It is also not enabled for libc++ because I do not have access to it and do nto know if it provides `constexpr` `initializer_list`.
- - In G++ 4.7.2 and 4.8.0 member function `value_or` does not have rvalue reference overload. These compilers do not support rvalue overloding on `*this`.
- - In order for the library to work with slightly older compilers, we emulate some missing type traits. On some platforms we cannot correctly detect the available features, and attempts at workarounds for missing type trait definitions can cause compile-time errors. Define macro `TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS` if you know that all the necessary type traits are defined, and no emulation is required.
-
-License
--------
-Distributed under the [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt).
diff --git a/src/libs/3rdparty/optional/copyright.txt b/src/libs/3rdparty/optional/copyright.txt
deleted file mode 100644
index 1641c3cdd0..0000000000
--- a/src/libs/3rdparty/optional/copyright.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Copyright (C) 2011-2012 Andrzej Krzemienski
-
-Distributed under the Boost Software License, Version 1.0
-(see accompanying file LICENSE_1_0.txt or a copy at
-http://www.boost.org/LICENSE_1_0.txt)
-
-The idea and interface is based on Boost.Optional library
-authored by Fernando Luis Cacciola Carballal
-
-Home at https://github.com/akrzemi1/Optional \ No newline at end of file
diff --git a/src/libs/3rdparty/optional/optional.hpp b/src/libs/3rdparty/optional/optional.hpp
deleted file mode 100644
index 607696d8e1..0000000000
--- a/src/libs/3rdparty/optional/optional.hpp
+++ /dev/null
@@ -1,1046 +0,0 @@
-// Copyright (C) 2011 - 2012 Andrzej Krzemienski.
-//
-// Use, modification, and distribution is subject to the Boost Software
-// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-//
-// The idea and interface is based on Boost.Optional library
-// authored by Fernando Luis Cacciola Carballal
-
-# ifndef ___OPTIONAL_HPP___
-# define ___OPTIONAL_HPP___
-
-# include <utility>
-# include <type_traits>
-# include <initializer_list>
-# include <cassert>
-# include <functional>
-# include <string>
-# include <stdexcept>
-
-# define TR2_OPTIONAL_REQUIRES(...) typename enable_if<__VA_ARGS__::value, bool>::type = false
-
-# if defined __GNUC__ // NOTE: GNUC is also defined for Clang
-# if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)
-# define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___
-# elif (__GNUC__ > 4)
-# define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___
-# endif
-#
-# if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)
-# define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___
-# elif (__GNUC__ > 4)
-# define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___
-# endif
-#
-# if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && (__GNUC_PATCHLEVEL__ >= 1)
-# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___
-# elif (__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)
-# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___
-# elif (__GNUC__ > 4)
-# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___
-# endif
-# endif
-#
-# if defined __clang_major__
-# if (__clang_major__ == 3 && __clang_minor__ >= 5)
-# define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_
-# elif (__clang_major__ > 3)
-# define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_
-# endif
-# if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_
-# define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_
-# elif (__clang_major__ == 3 && __clang_minor__ == 4 && __clang_patchlevel__ >= 2)
-# define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_
-# endif
-# endif
-#
-# if defined _MSC_VER
-# if (_MSC_VER >= 1900)
-# define TR2_OPTIONAL_MSVC_2015_AND_HIGHER___
-# endif
-# endif
-
-# if defined __clang__
-# if (__clang_major__ > 2) || (__clang_major__ == 2) && (__clang_minor__ >= 9)
-# define OPTIONAL_HAS_THIS_RVALUE_REFS 1
-# else
-# define OPTIONAL_HAS_THIS_RVALUE_REFS 0
-# endif
-# elif defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___
-# define OPTIONAL_HAS_THIS_RVALUE_REFS 1
-# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___
-# define OPTIONAL_HAS_THIS_RVALUE_REFS 1
-# else
-# define OPTIONAL_HAS_THIS_RVALUE_REFS 0
-# endif
-
-
-# if defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___
-# define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 1
-# define OPTIONAL_CONSTEXPR_INIT_LIST constexpr
-# else
-# define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 0
-# define OPTIONAL_CONSTEXPR_INIT_LIST
-# endif
-
-# if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ && (defined __cplusplus) && (__cplusplus != 201103L)
-# define OPTIONAL_HAS_MOVE_ACCESSORS 1
-# else
-# define OPTIONAL_HAS_MOVE_ACCESSORS 0
-# endif
-
-# // In C++11 constexpr implies const, so we need to make non-const members also non-constexpr
-# if (defined __cplusplus) && (__cplusplus == 201103L)
-# define OPTIONAL_MUTABLE_CONSTEXPR
-# else
-# define OPTIONAL_MUTABLE_CONSTEXPR constexpr
-# endif
-
-namespace std{
-
-namespace experimental{
-
-// BEGIN workaround for missing is_trivially_destructible
-# if defined TR2_OPTIONAL_GCC_4_8_AND_HIGHER___
- // leave it: it is already there
-# elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_
- // leave it: it is already there
-# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___
- // leave it: it is already there
-# elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS
- // leave it: the user doesn't want it
-# else
- template <typename T>
- using is_trivially_destructible = std::has_trivial_destructor<T>;
-# endif
-// END workaround for missing is_trivially_destructible
-
-# if (defined TR2_OPTIONAL_GCC_4_7_AND_HIGHER___)
- // leave it; our metafunctions are already defined.
-# elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_
- // leave it; our metafunctions are already defined.
-# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___
- // leave it: it is already there
-# elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS
- // leave it: the user doesn't want it
-# else
-
-
-// workaround for missing traits in GCC and CLANG
-template <class T>
-struct is_nothrow_move_constructible
-{
- constexpr static bool value = std::is_nothrow_constructible<T, T&&>::value;
-};
-
-
-template <class T, class U>
-struct is_assignable
-{
- template <class X, class Y>
- constexpr static bool has_assign(...) { return false; }
-
- template <class X, class Y, size_t S = sizeof((std::declval<X>() = std::declval<Y>(), true)) >
- // the comma operator is necessary for the cases where operator= returns void
- constexpr static bool has_assign(bool) { return true; }
-
- constexpr static bool value = has_assign<T, U>(true);
-};
-
-
-template <class T>
-struct is_nothrow_move_assignable
-{
- template <class X, bool has_any_move_assign>
- struct has_nothrow_move_assign {
- constexpr static bool value = false;
- };
-
- template <class X>
- struct has_nothrow_move_assign<X, true> {
- constexpr static bool value = noexcept( std::declval<X&>() = std::declval<X&&>() );
- };
-
- constexpr static bool value = has_nothrow_move_assign<T, is_assignable<T&, T&&>::value>::value;
-};
-// end workaround
-
-
-# endif
-
-
-
-// 20.5.4, optional for object types
-template <class T> class optional;
-
-// 20.5.5, optional for lvalue reference types
-template <class T> class optional<T&>;
-
-
-// workaround: std utility functions aren't constexpr yet
-template <class T> inline constexpr T&& constexpr_forward(typename std::remove_reference<T>::type& t) noexcept
-{
- return static_cast<T&&>(t);
-}
-
-template <class T> inline constexpr T&& constexpr_forward(typename std::remove_reference<T>::type&& t) noexcept
-{
- static_assert(!std::is_lvalue_reference<T>::value, "!!");
- return static_cast<T&&>(t);
-}
-
-template <class T> inline constexpr typename std::remove_reference<T>::type&& constexpr_move(T&& t) noexcept
-{
- return static_cast<typename std::remove_reference<T>::type&&>(t);
-}
-
-
-#if defined NDEBUG
-# define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR)
-#else
-# define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : ([]{assert(!#CHECK);}(), (EXPR)))
-#endif
-
-
-namespace detail_
-{
-
-// static_addressof: a constexpr version of addressof
-template <typename T>
-struct has_overloaded_addressof
-{
- template <class X>
- constexpr static bool has_overload(...) { return false; }
-
- template <class X, size_t S = sizeof(std::declval<X&>().operator&()) >
- constexpr static bool has_overload(bool) { return true; }
-
- constexpr static bool value = has_overload<T>(true);
-};
-
-template <typename T, TR2_OPTIONAL_REQUIRES(!has_overloaded_addressof<T>)>
-constexpr T* static_addressof(T& ref)
-{
- return &ref;
-}
-
-template <typename T, TR2_OPTIONAL_REQUIRES(has_overloaded_addressof<T>)>
-T* static_addressof(T& ref)
-{
- return std::addressof(ref);
-}
-
-
-// the call to convert<A>(b) has return type A and converts b to type A iff b decltype(b) is implicitly convertible to A
-template <class U>
-constexpr U convert(U v) { return v; }
-
-} // namespace detail
-
-
-constexpr struct trivial_init_t{} trivial_init{};
-
-
-// 20.5.6, In-place construction
-constexpr struct in_place_t{} in_place{};
-
-
-// 20.5.7, Disengaged state indicator
-struct nullopt_t
-{
- struct init{};
- constexpr explicit nullopt_t(init){}
-};
-constexpr nullopt_t nullopt{nullopt_t::init()};
-
-
-// 20.5.8, class bad_optional_access
-class bad_optional_access : public logic_error {
-public:
- explicit bad_optional_access(const string& what_arg) : logic_error{what_arg} {}
- explicit bad_optional_access(const char* what_arg) : logic_error{what_arg} {}
-};
-
-
-template <class T>
-union storage_t
-{
- unsigned char dummy_;
- T value_;
-
- constexpr storage_t( trivial_init_t ) noexcept : dummy_() {};
-
- template <class... Args>
- constexpr storage_t( Args&&... args ) : value_(constexpr_forward<Args>(args)...) {}
-
- ~storage_t(){}
-};
-
-
-template <class T>
-union constexpr_storage_t
-{
- unsigned char dummy_;
- T value_;
-
- constexpr constexpr_storage_t( trivial_init_t ) noexcept : dummy_() {};
-
- template <class... Args>
- constexpr constexpr_storage_t( Args&&... args ) : value_(constexpr_forward<Args>(args)...) {}
-
- ~constexpr_storage_t() = default;
-};
-
-
-template <class T>
-struct optional_base
-{
- bool init_;
- storage_t<T> storage_;
-
- constexpr optional_base() noexcept : init_(false), storage_(trivial_init) {};
-
- explicit constexpr optional_base(const T& v) : init_(true), storage_(v) {}
-
- explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {}
-
- template <class... Args> explicit optional_base(in_place_t, Args&&... args)
- : init_(true), storage_(constexpr_forward<Args>(args)...) {}
-
- template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
- explicit optional_base(in_place_t, std::initializer_list<U> il, Args&&... args)
- : init_(true), storage_(il, std::forward<Args>(args)...) {}
-
- ~optional_base() { if (init_) storage_.value_.T::~T(); }
-};
-
-
-template <class T>
-struct constexpr_optional_base
-{
- bool init_;
- constexpr_storage_t<T> storage_;
-
- constexpr constexpr_optional_base() noexcept : init_(false), storage_(trivial_init) {};
-
- explicit constexpr constexpr_optional_base(const T& v) : init_(true), storage_(v) {}
-
- explicit constexpr constexpr_optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {}
-
- template <class... Args> explicit constexpr constexpr_optional_base(in_place_t, Args&&... args)
- : init_(true), storage_(constexpr_forward<Args>(args)...) {}
-
- template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
- OPTIONAL_CONSTEXPR_INIT_LIST explicit constexpr_optional_base(in_place_t, std::initializer_list<U> il, Args&&... args)
- : init_(true), storage_(il, std::forward<Args>(args)...) {}
-
- ~constexpr_optional_base() = default;
-};
-
-template <class T>
-using OptionalBase = typename std::conditional<
- is_trivially_destructible<T>::value,
- constexpr_optional_base<typename std::remove_const<T>::type>,
- optional_base<typename std::remove_const<T>::type>
->::type;
-
-
-
-template <class T>
-class optional : private OptionalBase<T>
-{
- static_assert( !std::is_same<typename std::decay<T>::type, nullopt_t>::value, "bad T" );
- static_assert( !std::is_same<typename std::decay<T>::type, in_place_t>::value, "bad T" );
-
-
- constexpr bool initialized() const noexcept { return OptionalBase<T>::init_; }
- typename std::remove_const<T>::type* dataptr() { return std::addressof(OptionalBase<T>::storage_.value_); }
- constexpr const T* dataptr() const { return detail_::static_addressof(OptionalBase<T>::storage_.value_); }
-
-# if OPTIONAL_HAS_THIS_RVALUE_REFS == 1
- constexpr const T& contained_val() const& { return OptionalBase<T>::storage_.value_; }
-# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
- OPTIONAL_MUTABLE_CONSTEXPR T&& contained_val() && { return std::move(OptionalBase<T>::storage_.value_); }
- OPTIONAL_MUTABLE_CONSTEXPR T& contained_val() & { return OptionalBase<T>::storage_.value_; }
-# else
- T& contained_val() & { return OptionalBase<T>::storage_.value_; }
- T&& contained_val() && { return std::move(OptionalBase<T>::storage_.value_); }
-# endif
-# else
- constexpr const T& contained_val() const { return OptionalBase<T>::storage_.value_; }
- T& contained_val() { return OptionalBase<T>::storage_.value_; }
-# endif
-
- void clear() noexcept {
- if (initialized()) dataptr()->T::~T();
- OptionalBase<T>::init_ = false;
- }
-
- template <class... Args>
- void initialize(Args&&... args) noexcept(noexcept(T(std::forward<Args>(args)...)))
- {
- assert(!OptionalBase<T>::init_);
- ::new (static_cast<void*>(dataptr())) T(std::forward<Args>(args)...);
- OptionalBase<T>::init_ = true;
- }
-
- template <class U, class... Args>
- void initialize(std::initializer_list<U> il, Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
- {
- assert(!OptionalBase<T>::init_);
- ::new (static_cast<void*>(dataptr())) T(il, std::forward<Args>(args)...);
- OptionalBase<T>::init_ = true;
- }
-
-public:
- typedef T value_type;
-
- // 20.5.5.1, constructors
- constexpr optional() noexcept : OptionalBase<T>() {};
- constexpr optional(nullopt_t) noexcept : OptionalBase<T>() {};
-
- optional(const optional& rhs)
- : OptionalBase<T>()
- {
- if (rhs.initialized()) {
- ::new (static_cast<void*>(dataptr())) T(*rhs);
- OptionalBase<T>::init_ = true;
- }
- }
-
- optional(optional&& rhs) noexcept(is_nothrow_move_constructible<T>::value)
- : OptionalBase<T>()
- {
- if (rhs.initialized()) {
- ::new (static_cast<void*>(dataptr())) T(std::move(*rhs));
- OptionalBase<T>::init_ = true;
- }
- }
-
- constexpr optional(const T& v) : OptionalBase<T>(v) {}
-
- constexpr optional(T&& v) : OptionalBase<T>(constexpr_move(v)) {}
-
- template <class... Args>
- explicit constexpr optional(in_place_t, Args&&... args)
- : OptionalBase<T>(in_place_t{}, constexpr_forward<Args>(args)...) {}
-
- template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
- OPTIONAL_CONSTEXPR_INIT_LIST explicit optional(in_place_t, std::initializer_list<U> il, Args&&... args)
- : OptionalBase<T>(in_place_t{}, il, constexpr_forward<Args>(args)...) {}
-
- // 20.5.4.2, Destructor
- ~optional() = default;
-
- void reset() noexcept { clear(); }
-
- // 20.5.4.3, assignment
- optional& operator=(nullopt_t) noexcept
- {
- clear();
- return *this;
- }
-
- optional& operator=(const optional& rhs)
- {
- if (initialized() == true && rhs.initialized() == false) clear();
- else if (initialized() == false && rhs.initialized() == true) initialize(*rhs);
- else if (initialized() == true && rhs.initialized() == true) contained_val() = *rhs;
- return *this;
- }
-
- optional& operator=(optional&& rhs)
- noexcept(is_nothrow_move_assignable<T>::value && is_nothrow_move_constructible<T>::value)
- {
- if (initialized() == true && rhs.initialized() == false) clear();
- else if (initialized() == false && rhs.initialized() == true) initialize(std::move(*rhs));
- else if (initialized() == true && rhs.initialized() == true) contained_val() = std::move(*rhs);
- return *this;
- }
-
- template <class U>
- auto operator=(U&& v)
- -> typename enable_if
- <
- is_same<typename decay<U>::type, T>::value,
- optional&
- >::type
- {
- if (initialized()) { contained_val() = std::forward<U>(v); }
- else { initialize(std::forward<U>(v)); }
- return *this;
- }
-
-
- template <class... Args>
- void emplace(Args&&... args)
- {
- clear();
- initialize(std::forward<Args>(args)...);
- }
-
- template <class U, class... Args>
- void emplace(initializer_list<U> il, Args&&... args)
- {
- clear();
- initialize<U, Args...>(il, std::forward<Args>(args)...);
- }
-
- // 20.5.4.4, Swap
- void swap(optional<T>& rhs) noexcept(is_nothrow_move_constructible<T>::value && noexcept(swap(declval<T&>(), declval<T&>())))
- {
- if (initialized() == true && rhs.initialized() == false) { rhs.initialize(std::move(**this)); clear(); }
- else if (initialized() == false && rhs.initialized() == true) { initialize(std::move(*rhs)); rhs.clear(); }
- else if (initialized() == true && rhs.initialized() == true) { using std::swap; swap(**this, *rhs); }
- }
-
- // 20.5.4.5, Observers
-
- explicit constexpr operator bool() const noexcept { return initialized(); }
- constexpr bool has_value() const noexcept { return initialized(); }
-
- constexpr T const* operator ->() const {
- return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), dataptr());
- }
-
-# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
-
- OPTIONAL_MUTABLE_CONSTEXPR T* operator ->() {
- assert (initialized());
- return dataptr();
- }
-
- constexpr T const& operator *() const& {
- return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val());
- }
-
- OPTIONAL_MUTABLE_CONSTEXPR T& operator *() & {
- assert (initialized());
- return contained_val();
- }
-
- OPTIONAL_MUTABLE_CONSTEXPR T&& operator *() && {
- assert (initialized());
- return constexpr_move(contained_val());
- }
-
- constexpr T const& value() const& {
- return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
- }
-
- OPTIONAL_MUTABLE_CONSTEXPR T& value() & {
- return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
- }
-
- OPTIONAL_MUTABLE_CONSTEXPR T&& value() && {
- if (!initialized()) throw bad_optional_access("bad optional access");
- return std::move(contained_val());
- }
-
-# else
-
- T* operator ->() {
- assert (initialized());
- return dataptr();
- }
-
- constexpr T const& operator *() const {
- return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val());
- }
-
- T& operator *() {
- assert (initialized());
- return contained_val();
- }
-
- constexpr T const& value() const {
- return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
- }
-
- T& value() {
- return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
- }
-
-# endif
-
-# if OPTIONAL_HAS_THIS_RVALUE_REFS == 1
-
- template <class V>
- constexpr T value_or(V&& v) const&
- {
- return *this ? **this : detail_::convert<T>(constexpr_forward<V>(v));
- }
-
-# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
-
- template <class V>
- OPTIONAL_MUTABLE_CONSTEXPR T value_or(V&& v) &&
- {
- return *this ? constexpr_move(const_cast<optional<T>&>(*this).contained_val()) : detail_::convert<T>(constexpr_forward<V>(v));
- }
-
-# else
-
- template <class V>
- T value_or(V&& v) &&
- {
- return *this ? constexpr_move(const_cast<optional<T>&>(*this).contained_val()) : detail_::convert<T>(constexpr_forward<V>(v));
- }
-
-# endif
-
-# else
-
- template <class V>
- constexpr T value_or(V&& v) const
- {
- return *this ? **this : detail_::convert<T>(constexpr_forward<V>(v));
- }
-
-# endif
-
-};
-
-
-template <class T>
-class optional<T&>
-{
- static_assert( !std::is_same<T, nullopt_t>::value, "bad T" );
- static_assert( !std::is_same<T, in_place_t>::value, "bad T" );
- T* ref;
-
-public:
-
- // 20.5.5.1, construction/destruction
- constexpr optional() noexcept : ref(nullptr) {}
-
- constexpr optional(nullopt_t) noexcept : ref(nullptr) {}
-
- constexpr optional(T& v) noexcept : ref(detail_::static_addressof(v)) {}
-
- optional(T&&) = delete;
-
- constexpr optional(const optional& rhs) noexcept : ref(rhs.ref) {}
-
- explicit constexpr optional(in_place_t, T& v) noexcept : ref(detail_::static_addressof(v)) {}
-
- explicit optional(in_place_t, T&&) = delete;
-
- ~optional() = default;
-
- // 20.5.5.2, mutation
- optional& operator=(nullopt_t) noexcept {
- ref = nullptr;
- return *this;
- }
-
- // optional& operator=(const optional& rhs) noexcept {
- // ref = rhs.ref;
- // return *this;
- // }
-
- // optional& operator=(optional&& rhs) noexcept {
- // ref = rhs.ref;
- // return *this;
- // }
-
- template <typename U>
- auto operator=(U&& rhs) noexcept
- -> typename enable_if
- <
- is_same<typename decay<U>::type, optional<T&>>::value,
- optional&
- >::type
- {
- ref = rhs.ref;
- return *this;
- }
-
- template <typename U>
- auto operator=(U&& rhs) noexcept
- -> typename enable_if
- <
- !is_same<typename decay<U>::type, optional<T&>>::value,
- optional&
- >::type
- = delete;
-
- void emplace(T& v) noexcept {
- ref = detail_::static_addressof(v);
- }
-
- void emplace(T&&) = delete;
-
-
- void swap(optional<T&>& rhs) noexcept
- {
- std::swap(ref, rhs.ref);
- }
-
- // 20.5.5.3, observers
- constexpr T* operator->() const {
- return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, ref);
- }
-
- constexpr T& operator*() const {
- return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, *ref);
- }
-
- constexpr T& value() const {
- return ref ? *ref : (throw bad_optional_access("bad optional access"), *ref);
- }
-
- explicit constexpr operator bool() const noexcept {
- return ref != nullptr;
- }
- constexpr bool has_value() const noexcept { return ref != nullptr; }
-
- template <class V>
- constexpr typename decay<T>::type value_or(V&& v) const
- {
- return *this ? **this : detail_::convert<typename decay<T>::type>(constexpr_forward<V>(v));
- }
-};
-
-
-template <class T>
-class optional<T&&>
-{
- static_assert( sizeof(T) == 0, "optional rvalue references disallowed" );
-};
-
-
-// 20.5.8, Relational operators
-template <class T> constexpr bool operator==(const optional<T>& x, const optional<T>& y)
-{
- return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y;
-}
-
-template <class T> constexpr bool operator!=(const optional<T>& x, const optional<T>& y)
-{
- return !(x == y);
-}
-
-template <class T> constexpr bool operator<(const optional<T>& x, const optional<T>& y)
-{
- return (!y) ? false : (!x) ? true : *x < *y;
-}
-
-template <class T> constexpr bool operator>(const optional<T>& x, const optional<T>& y)
-{
- return (y < x);
-}
-
-template <class T> constexpr bool operator<=(const optional<T>& x, const optional<T>& y)
-{
- return !(y < x);
-}
-
-template <class T> constexpr bool operator>=(const optional<T>& x, const optional<T>& y)
-{
- return !(x < y);
-}
-
-
-// 20.5.9, Comparison with nullopt
-template <class T> constexpr bool operator==(const optional<T>& x, nullopt_t) noexcept
-{
- return (!x);
-}
-
-template <class T> constexpr bool operator==(nullopt_t, const optional<T>& x) noexcept
-{
- return (!x);
-}
-
-template <class T> constexpr bool operator!=(const optional<T>& x, nullopt_t) noexcept
-{
- return bool(x);
-}
-
-template <class T> constexpr bool operator!=(nullopt_t, const optional<T>& x) noexcept
-{
- return bool(x);
-}
-
-template <class T> constexpr bool operator<(const optional<T>&, nullopt_t) noexcept
-{
- return false;
-}
-
-template <class T> constexpr bool operator<(nullopt_t, const optional<T>& x) noexcept
-{
- return bool(x);
-}
-
-template <class T> constexpr bool operator<=(const optional<T>& x, nullopt_t) noexcept
-{
- return (!x);
-}
-
-template <class T> constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept
-{
- return true;
-}
-
-template <class T> constexpr bool operator>(const optional<T>& x, nullopt_t) noexcept
-{
- return bool(x);
-}
-
-template <class T> constexpr bool operator>(nullopt_t, const optional<T>&) noexcept
-{
- return false;
-}
-
-template <class T> constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept
-{
- return true;
-}
-
-template <class T> constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept
-{
- return (!x);
-}
-
-
-
-// 20.5.10, Comparison with T
-template <class T> constexpr bool operator==(const optional<T>& x, const T& v)
-{
- return bool(x) ? *x == v : false;
-}
-
-template <class T> constexpr bool operator==(const T& v, const optional<T>& x)
-{
- return bool(x) ? v == *x : false;
-}
-
-template <class T> constexpr bool operator!=(const optional<T>& x, const T& v)
-{
- return bool(x) ? *x != v : true;
-}
-
-template <class T> constexpr bool operator!=(const T& v, const optional<T>& x)
-{
- return bool(x) ? v != *x : true;
-}
-
-template <class T> constexpr bool operator<(const optional<T>& x, const T& v)
-{
- return bool(x) ? *x < v : true;
-}
-
-template <class T> constexpr bool operator>(const T& v, const optional<T>& x)
-{
- return bool(x) ? v > *x : true;
-}
-
-template <class T> constexpr bool operator>(const optional<T>& x, const T& v)
-{
- return bool(x) ? *x > v : false;
-}
-
-template <class T> constexpr bool operator<(const T& v, const optional<T>& x)
-{
- return bool(x) ? v < *x : false;
-}
-
-template <class T> constexpr bool operator>=(const optional<T>& x, const T& v)
-{
- return bool(x) ? *x >= v : false;
-}
-
-template <class T> constexpr bool operator<=(const T& v, const optional<T>& x)
-{
- return bool(x) ? v <= *x : false;
-}
-
-template <class T> constexpr bool operator<=(const optional<T>& x, const T& v)
-{
- return bool(x) ? *x <= v : true;
-}
-
-template <class T> constexpr bool operator>=(const T& v, const optional<T>& x)
-{
- return bool(x) ? v >= *x : true;
-}
-
-
-// Comparison of optional<T&> with T
-template <class T> constexpr bool operator==(const optional<T&>& x, const T& v)
-{
- return bool(x) ? *x == v : false;
-}
-
-template <class T> constexpr bool operator==(const T& v, const optional<T&>& x)
-{
- return bool(x) ? v == *x : false;
-}
-
-template <class T> constexpr bool operator!=(const optional<T&>& x, const T& v)
-{
- return bool(x) ? *x != v : true;
-}
-
-template <class T> constexpr bool operator!=(const T& v, const optional<T&>& x)
-{
- return bool(x) ? v != *x : true;
-}
-
-template <class T> constexpr bool operator<(const optional<T&>& x, const T& v)
-{
- return bool(x) ? *x < v : true;
-}
-
-template <class T> constexpr bool operator>(const T& v, const optional<T&>& x)
-{
- return bool(x) ? v > *x : true;
-}
-
-template <class T> constexpr bool operator>(const optional<T&>& x, const T& v)
-{
- return bool(x) ? *x > v : false;
-}
-
-template <class T> constexpr bool operator<(const T& v, const optional<T&>& x)
-{
- return bool(x) ? v < *x : false;
-}
-
-template <class T> constexpr bool operator>=(const optional<T&>& x, const T& v)
-{
- return bool(x) ? *x >= v : false;
-}
-
-template <class T> constexpr bool operator<=(const T& v, const optional<T&>& x)
-{
- return bool(x) ? v <= *x : false;
-}
-
-template <class T> constexpr bool operator<=(const optional<T&>& x, const T& v)
-{
- return bool(x) ? *x <= v : true;
-}
-
-template <class T> constexpr bool operator>=(const T& v, const optional<T&>& x)
-{
- return bool(x) ? v >= *x : true;
-}
-
-// Comparison of optional<T const&> with T
-template <class T> constexpr bool operator==(const optional<const T&>& x, const T& v)
-{
- return bool(x) ? *x == v : false;
-}
-
-template <class T> constexpr bool operator==(const T& v, const optional<const T&>& x)
-{
- return bool(x) ? v == *x : false;
-}
-
-template <class T> constexpr bool operator!=(const optional<const T&>& x, const T& v)
-{
- return bool(x) ? *x != v : true;
-}
-
-template <class T> constexpr bool operator!=(const T& v, const optional<const T&>& x)
-{
- return bool(x) ? v != *x : true;
-}
-
-template <class T> constexpr bool operator<(const optional<const T&>& x, const T& v)
-{
- return bool(x) ? *x < v : true;
-}
-
-template <class T> constexpr bool operator>(const T& v, const optional<const T&>& x)
-{
- return bool(x) ? v > *x : true;
-}
-
-template <class T> constexpr bool operator>(const optional<const T&>& x, const T& v)
-{
- return bool(x) ? *x > v : false;
-}
-
-template <class T> constexpr bool operator<(const T& v, const optional<const T&>& x)
-{
- return bool(x) ? v < *x : false;
-}
-
-template <class T> constexpr bool operator>=(const optional<const T&>& x, const T& v)
-{
- return bool(x) ? *x >= v : false;
-}
-
-template <class T> constexpr bool operator<=(const T& v, const optional<const T&>& x)
-{
- return bool(x) ? v <= *x : false;
-}
-
-template <class T> constexpr bool operator<=(const optional<const T&>& x, const T& v)
-{
- return bool(x) ? *x <= v : true;
-}
-
-template <class T> constexpr bool operator>=(const T& v, const optional<const T&>& x)
-{
- return bool(x) ? v >= *x : true;
-}
-
-
-// 20.5.12, Specialized algorithms
-template <class T>
-void swap(optional<T>& x, optional<T>& y) noexcept(noexcept(x.swap(y)))
-{
- x.swap(y);
-}
-
-
-template <class T>
-constexpr optional<typename decay<T>::type> make_optional(T&& v)
-{
- return optional<typename decay<T>::type>(constexpr_forward<T>(v));
-}
-
-template <class X>
-constexpr optional<X&> make_optional(reference_wrapper<X> v)
-{
- return optional<X&>(v.get());
-}
-
-
-} // namespace experimental
-} // namespace std
-
-namespace std
-{
- template <typename T>
- struct hash<std::experimental::optional<T>>
- {
- typedef typename hash<T>::result_type result_type;
- typedef std::experimental::optional<T> argument_type;
-
- constexpr result_type operator()(argument_type const& arg) const {
- return arg ? std::hash<T>{}(*arg) : result_type{};
- }
- };
-
- template <typename T>
- struct hash<std::experimental::optional<T&>>
- {
- typedef typename hash<T>::result_type result_type;
- typedef std::experimental::optional<T&> argument_type;
-
- constexpr result_type operator()(argument_type const& arg) const {
- return arg ? std::hash<T>{}(*arg) : result_type{};
- }
- };
-}
-
-# undef TR2_OPTIONAL_REQUIRES
-# undef TR2_OPTIONAL_ASSERTED_EXPRESSION
-
-# endif //___OPTIONAL_HPP___
diff --git a/src/libs/3rdparty/optional/test_optional.cpp b/src/libs/3rdparty/optional/test_optional.cpp
deleted file mode 100644
index 8ed356a562..0000000000
--- a/src/libs/3rdparty/optional/test_optional.cpp
+++ /dev/null
@@ -1,1459 +0,0 @@
-// Copyright (C) 2011 - 2012 Andrzej Krzemienski.
-//
-// Use, modification, and distribution is subject to the Boost Software
-// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-//
-// The idea and interface is based on Boost.Optional library
-// authored by Fernando Luis Cacciola Carballal
-
-# include "optional.hpp"
-# include <vector>
-# include <iostream>
-# include <functional>
-# include <complex>
-
-
-
-struct caller {
- template <class T> caller(T fun) { fun(); }
-};
-# define CAT2(X, Y) X ## Y
-# define CAT(X, Y) CAT2(X, Y)
-# define TEST(NAME) caller CAT(__VAR, __LINE__) = []
-
-enum State
-{
- sDefaultConstructed,
- sValueCopyConstructed,
- sValueMoveConstructed,
- sCopyConstructed,
- sMoveConstructed,
- sMoveAssigned,
- sCopyAssigned,
- sValueCopyAssigned,
- sValueMoveAssigned,
- sMovedFrom,
- sValueConstructed
-};
-
-struct OracleVal
-{
- State s;
- int i;
- OracleVal(int i = 0) : s(sValueConstructed), i(i) {}
-};
-
-struct Oracle
-{
- State s;
- OracleVal val;
-
- Oracle() : s(sDefaultConstructed) {}
- Oracle(const OracleVal& v) : s(sValueCopyConstructed), val(v) {}
- Oracle(OracleVal&& v) : s(sValueMoveConstructed), val(std::move(v)) {v.s = sMovedFrom;}
- Oracle(const Oracle& o) : s(sCopyConstructed), val(o.val) {}
- Oracle(Oracle&& o) : s(sMoveConstructed), val(std::move(o.val)) {o.s = sMovedFrom;}
-
- Oracle& operator=(const OracleVal& v) { s = sValueCopyConstructed; val = v; return *this; }
- Oracle& operator=(OracleVal&& v) { s = sValueMoveConstructed; val = std::move(v); v.s = sMovedFrom; return *this; }
- Oracle& operator=(const Oracle& o) { s = sCopyConstructed; val = o.val; return *this; }
- Oracle& operator=(Oracle&& o) { s = sMoveConstructed; val = std::move(o.val); o.s = sMovedFrom; return *this; }
-};
-
-struct Guard
-{
- std::string val;
- Guard() : val{} {}
- explicit Guard(std::string s, int = 0) : val(s) {}
- Guard(const Guard&) = delete;
- Guard(Guard&&) = delete;
- void operator=(const Guard&) = delete;
- void operator=(Guard&&) = delete;
-};
-
-struct ExplicitStr
-{
- std::string s;
- explicit ExplicitStr(const char* chp) : s(chp) {};
-};
-
-struct Date
-{
- int i;
- Date() = delete;
- Date(int i) : i{i} {};
- Date(Date&& d) : i(d.i) { d.i = 0; }
- Date(const Date&) = delete;
- Date& operator=(const Date&) = delete;
- Date& operator=(Date&& d) { i = d.i; d.i = 0; return *this;};
-};
-
-bool operator==( Oracle const& a, Oracle const& b ) { return a.val.i == b.val.i; }
-bool operator!=( Oracle const& a, Oracle const& b ) { return a.val.i != b.val.i; }
-
-
-namespace tr2 = std::experimental;
-
-
-TEST(disengaged_ctor)
-{
- tr2::optional<int> o1;
- assert (!o1);
-
- tr2::optional<int> o2 = tr2::nullopt;
- assert (!o2);
-
- tr2::optional<int> o3 = o2;
- assert (!o3);
-
- assert (o1 == tr2::nullopt);
- assert (o1 == tr2::optional<int>{});
- assert (!o1);
- assert (bool(o1) == false);
-
- assert (o2 == tr2::nullopt);
- assert (o2 == tr2::optional<int>{});
- assert (!o2);
- assert (bool(o2) == false);
-
- assert (o3 == tr2::nullopt);
- assert (o3 == tr2::optional<int>{});
- assert (!o3);
- assert (bool(o3) == false);
-
- assert (o1 == o2);
- assert (o2 == o1);
- assert (o1 == o3);
- assert (o3 == o1);
- assert (o2 == o3);
- assert (o3 == o2);
-};
-
-
-TEST(value_ctor)
-{
- OracleVal v;
- tr2::optional<Oracle> oo1(v);
- assert (oo1 != tr2::nullopt);
- assert (oo1 != tr2::optional<Oracle>{});
- assert (oo1 == tr2::optional<Oracle>{v});
- assert (!!oo1);
- assert (bool(oo1));
- // NA: assert (oo1->s == sValueCopyConstructed);
- assert (oo1->s == sMoveConstructed);
- assert (v.s == sValueConstructed);
-
- tr2::optional<Oracle> oo2(std::move(v));
- assert (oo2 != tr2::nullopt);
- assert (oo2 != tr2::optional<Oracle>{});
- assert (oo2 == oo1);
- assert (!!oo2);
- assert (bool(oo2));
- // NA: assert (oo2->s == sValueMoveConstructed);
- assert (oo2->s == sMoveConstructed);
- assert (v.s == sMovedFrom);
-
- {
- OracleVal v;
- tr2::optional<Oracle> oo1{tr2::in_place, v};
- assert (oo1 != tr2::nullopt);
- assert (oo1 != tr2::optional<Oracle>{});
- assert (oo1 == tr2::optional<Oracle>{v});
- assert (!!oo1);
- assert (bool(oo1));
- assert (oo1->s == sValueCopyConstructed);
- assert (v.s == sValueConstructed);
-
- tr2::optional<Oracle> oo2{tr2::in_place, std::move(v)};
- assert (oo2 != tr2::nullopt);
- assert (oo2 != tr2::optional<Oracle>{});
- assert (oo2 == oo1);
- assert (!!oo2);
- assert (bool(oo2));
- assert (oo2->s == sValueMoveConstructed);
- assert (v.s == sMovedFrom);
- }
-};
-
-
-TEST(assignment)
-{
- tr2::optional<int> oi;
- oi = tr2::optional<int>{1};
- assert (*oi == 1);
-
- oi = tr2::nullopt;
- assert (!oi);
-
- oi = 2;
- assert (*oi == 2);
-
- oi = {};
- assert (!oi);
-};
-
-
-template <class T>
-struct MoveAware
-{
- T val;
- bool moved;
- MoveAware(T val) : val(val), moved(false) {}
- MoveAware(MoveAware const&) = delete;
- MoveAware(MoveAware&& rhs) : val(rhs.val), moved(rhs.moved) {
- rhs.moved = true;
- }
- MoveAware& operator=(MoveAware const&) = delete;
- MoveAware& operator=(MoveAware&& rhs) {
- val = (rhs.val);
- moved = (rhs.moved);
- rhs.moved = true;
- return *this;
- }
-};
-
-TEST(moved_from_state)
-{
- // first, test mock:
- MoveAware<int> i{1}, j{2};
- assert (i.val == 1);
- assert (!i.moved);
- assert (j.val == 2);
- assert (!j.moved);
-
- MoveAware<int> k = std::move(i);
- assert (k.val == 1);
- assert (!k.moved);
- assert (i.val == 1);
- assert (i.moved);
-
- k = std::move(j);
- assert (k.val == 2);
- assert (!k.moved);
- assert (j.val == 2);
- assert (j.moved);
-
- // now, test optional
- tr2::optional<MoveAware<int>> oi{1}, oj{2};
- assert (oi);
- assert (!oi->moved);
- assert (oj);
- assert (!oj->moved);
-
- tr2::optional<MoveAware<int>> ok = std::move(oi);
- assert (ok);
- assert (!ok->moved);
- assert (oi);
- assert (oi->moved);
-
- ok = std::move(oj);
- assert (ok);
- assert (!ok->moved);
- assert (oj);
- assert (oj->moved);
-};
-
-
-TEST(copy_move_ctor_optional_int)
-{
- tr2::optional<int> oi;
- tr2::optional<int> oj = oi;
-
- assert (!oj);
- assert (oj == oi);
- assert (oj == tr2::nullopt);
- assert (!bool(oj));
-
- oi = 1;
- tr2::optional<int> ok = oi;
- assert (!!ok);
- assert (bool(ok));
- assert (ok == oi);
- assert (ok != oj);
- assert (*ok == 1);
-
- tr2::optional<int> ol = std::move(oi);
- assert (!!ol);
- assert (bool(ol));
- assert (ol == oi);
- assert (ol != oj);
- assert (*ol == 1);
-};
-
-
-TEST(optional_optional)
-{
- tr2::optional<tr2::optional<int>> oi1 = tr2::nullopt;
- assert (oi1 == tr2::nullopt);
- assert (!oi1);
-
- {
- tr2::optional<tr2::optional<int>> oi2 {tr2::in_place};
- assert (oi2 != tr2::nullopt);
- assert (bool(oi2));
- assert (*oi2 == tr2::nullopt);
- //assert (!(*oi2));
- //std::cout << typeid(**oi2).name() << std::endl;
- }
-
- {
- tr2::optional<tr2::optional<int>> oi2 {tr2::in_place, tr2::nullopt};
- assert (oi2 != tr2::nullopt);
- assert (bool(oi2));
- assert (*oi2 == tr2::nullopt);
- assert (!*oi2);
- }
-
- {
- tr2::optional<tr2::optional<int>> oi2 {tr2::optional<int>{}};
- assert (oi2 != tr2::nullopt);
- assert (bool(oi2));
- assert (*oi2 == tr2::nullopt);
- assert (!*oi2);
- }
-
- tr2::optional<int> oi;
- auto ooi = tr2::make_optional(oi);
- static_assert( std::is_same<tr2::optional<tr2::optional<int>>, decltype(ooi)>::value, "");
-
-};
-
-TEST(example_guard)
-{
- using namespace tr2;
- //FAILS: optional<Guard> ogx(Guard("res1"));
- //FAILS: optional<Guard> ogx = "res1";
- //FAILS: optional<Guard> ogx("res1");
- optional<Guard> oga; // Guard is non-copyable (and non-moveable)
- optional<Guard> ogb(in_place, "res1"); // initialzes the contained value with "res1"
- assert (bool(ogb));
- assert (ogb->val == "res1");
-
- optional<Guard> ogc(in_place); // default-constructs the contained value
- assert (bool(ogc));
- assert (ogc->val == "");
-
- oga.emplace("res1"); // initialzes the contained value with "res1"
- assert (bool(oga));
- assert (oga->val == "res1");
-
- oga.emplace(); // destroys the contained value and
- // default-constructs the new one
- assert (bool(oga));
- assert (oga->val == "");
-
- oga = nullopt; // OK: make disengaged the optional Guard
- assert (!(oga));
- //FAILS: ogb = {}; // ERROR: Guard is not Moveable
-};
-
-
-void process(){}
-void process(int ){}
-void processNil(){}
-
-
-TEST(example1)
-{
- using namespace tr2;
- optional<int> oi; // create disengaged object
- optional<int> oj = nullopt; // alternative syntax
- oi = oj; // assign disengaged object
- optional<int> ok = oj; // ok is disengaged
-
- if (oi) assert(false); // 'if oi is engaged...'
- if (!oi) assert(true); // 'if oi is disengaged...'
-
- if (oi != nullopt) assert(false); // 'if oi is engaged...'
- if (oi == nullopt) assert(true); // 'if oi is disengaged...'
-
- assert(oi == ok); // two disengaged optionals compare equal
-
- ///////////////////////////////////////////////////////////////////////////
- optional<int> ol{1}; // ol is engaged; its contained value is 1
- ok = 2; // ok becomes engaged; its contained value is 2
- oj = ol; // oj becomes engaged; its contained value is 1
-
- assert(oi != ol); // disengaged != engaged
- assert(ok != ol); // different contained values
- assert(oj == ol); // same contained value
- assert(oi < ol); // disengaged < engaged
- assert(ol < ok); // less by contained value
-
- /////////////////////////////////////////////////////////////////////////////
- optional<int> om{1}; // om is engaged; its contained value is 1
- optional<int> on = om; // on is engaged; its contained value is 1
- om = 2; // om is engaged; its contained value is 2
- assert (on != om); // on still contains 3. They are not pointers
-
- /////////////////////////////////////////////////////////////////////////////
- int i = *ol; // i obtains the value contained in ol
- assert (i == 1);
- *ol = 9; // the object contained in ol becomes 9
- assert(*ol == 9);
- assert(ol == make_optional(9));
-
- ///////////////////////////////////
- int p = 1;
- optional<int> op = p;
- assert(*op == 1);
- p = 2;
- assert(*op == 1); // value contained in op is separated from p
-
- ////////////////////////////////
- if (ol)
- process(*ol); // use contained value if present
- else
- process(); // proceed without contained value
-
- if (!om)
- processNil();
- else
- process(*om);
-
- /////////////////////////////////////////
- process(ol.value_or(0)); // use 0 if ol is disengaged
-
- ////////////////////////////////////////////
- ok = nullopt; // if ok was engaged calls T's dtor
- oj = {}; // assigns a temporary disengaged optional
-};
-
-
-TEST(example_guard)
-{
- using std::experimental::optional;
- const optional<int> c = 4;
- int i = *c; // i becomes 4
- assert (i == 4);
- // FAILS: *c = i; // ERROR: cannot assign to const int&
-};
-
-
-TEST(example_ref)
-{
- using namespace std::experimental;
- int i = 1;
- int j = 2;
- optional<int&> ora; // disengaged optional reference to int
- optional<int&> orb = i; // contained reference refers to object i
-
- *orb = 3; // i becomes 3
- // FAILS: ora = j; // ERROR: optional refs do not have assignment from T
- // FAILS: ora = {j}; // ERROR: optional refs do not have copy/move assignment
- // FAILS: ora = orb; // ERROR: no copy/move assignment
- ora.emplace(j); // OK: contained reference refers to object j
- ora.emplace(i); // OK: contained reference now refers to object i
-
- ora = nullopt; // OK: ora becomes disengaged
-};
-
-
-template <typename T>
-T getValue( tr2::optional<T> newVal = tr2::nullopt, tr2::optional<T&> storeHere = tr2::nullopt )
-{
- T cached{};
-
- if (newVal) {
- cached = *newVal;
-
- if (storeHere) {
- *storeHere = *newVal; // LEGAL: assigning T to T
- }
- }
- return cached;
-}
-
-TEST(example_optional_arg)
-{
- int iii = 0;
- iii = getValue<int>(iii, iii);
- iii = getValue<int>(iii);
- iii = getValue<int>();
-
- {
- using namespace std::experimental;
- optional<Guard> grd1{in_place, "res1", 1}; // guard 1 initialized
- optional<Guard> grd2;
-
- grd2.emplace("res2", 2); // guard 2 initialized
- grd1 = nullopt; // guard 1 released
-
- } // guard 2 released (in dtor)
-};
-
-
-std::tuple<Date, Date, Date> getStartMidEnd() { return std::tuple<Date, Date, Date>{Date{1}, Date{2}, Date{3}}; }
-void run(Date const&, Date const&, Date const&) {}
-
-TEST(example_date)
-{
- using namespace std::experimental;
- optional<Date> start, mid, end; // Date doesn't have default ctor (no good default date)
-
- std::tie(start, mid, end) = getStartMidEnd();
- run(*start, *mid, *end);
-};
-
-
-std::experimental::optional<char> readNextChar(){ return{}; }
-
-void run(std::experimental::optional<std::string>) {}
-void run(std::complex<double>) {}
-
-
-template <class T>
-void assign_norebind(tr2::optional<T&>& optref, T& obj)
-{
- if (optref) *optref = obj;
- else optref.emplace(obj);
-}
-
-template <typename T> void unused(T&&) {}
-
-TEST(example_conceptual_model)
-{
- using namespace std::experimental;
-
- optional<int> oi = 0;
- optional<int> oj = 1;
- optional<int> ok = nullopt;
-
- oi = 1;
- oj = nullopt;
- ok = 0;
-
- unused(oi == nullopt);
- unused(oj == 0);
- unused(ok == 1);
-};
-
-TEST(example_rationale)
-{
- using namespace std::experimental;
- if (optional<char> ch = readNextChar()) {
- unused(ch);
- // ...
- }
-
- //////////////////////////////////
- optional<int> opt1 = nullopt;
- optional<int> opt2 = {};
-
- opt1 = nullopt;
- opt2 = {};
-
- if (opt1 == nullopt) {}
- if (!opt2) {}
- if (opt2 == optional<int>{}) {}
-
-
-
- ////////////////////////////////
-
- run(nullopt); // pick the second overload
- // FAILS: run({}); // ambiguous
-
- if (opt1 == nullopt) {} // fine
- // FAILS: if (opt2 == {}) {} // ilegal
-
- ////////////////////////////////
- assert (optional<unsigned>{} < optional<unsigned>{0});
- assert (optional<unsigned>{0} < optional<unsigned>{1});
- assert (!(optional<unsigned>{} < optional<unsigned>{}) );
- assert (!(optional<unsigned>{1} < optional<unsigned>{1}));
-
- assert (optional<unsigned>{} != optional<unsigned>{0});
- assert (optional<unsigned>{0} != optional<unsigned>{1});
- assert (optional<unsigned>{} == optional<unsigned>{} );
- assert (optional<unsigned>{0} == optional<unsigned>{0});
-
- /////////////////////////////////
- optional<int> o;
- o = make_optional(1); // copy/move assignment
- o = 1; // assignment from T
- o.emplace(1); // emplacement
-
- ////////////////////////////////////
- int isas = 0, i = 9;
- optional<int&> asas = i;
- assign_norebind(asas, isas);
-
- /////////////////////////////////////
- ////tr2::optional<std::vector<int>> ov2 = {2, 3};
- ////assert (bool(ov2));
- ////assert ((*ov2)[1] == 3);
- ////
- ////////////////////////////////
- ////std::vector<int> v = {1, 2, 4, 8};
- ////optional<std::vector<int>> ov = {1, 2, 4, 8};
-
- ////assert (v == *ov);
- ////
- ////ov = {1, 2, 4, 8};
-
- ////std::allocator<int> a;
- ////optional<std::vector<int>> ou { in_place, {1, 2, 4, 8}, a };
-
- ////assert (ou == ov);
-
- //////////////////////////////
- // inconvenient syntax:
- {
-
- tr2::optional<std::vector<int>> ov2{tr2::in_place, {2, 3}};
-
- assert (bool(ov2));
- assert ((*ov2)[1] == 3);
-
- ////////////////////////////
-
- std::vector<int> v = {1, 2, 4, 8};
- optional<std::vector<int>> ov{tr2::in_place, {1, 2, 4, 8}};
-
- assert (v == *ov);
-
- ov.emplace({1, 2, 4, 8});
-/*
- std::allocator<int> a;
- optional<std::vector<int>> ou { in_place, {1, 2, 4, 8}, a };
-
- assert (ou == ov);
-*/
- }
-
- /////////////////////////////////
- {
- typedef int T;
- optional<optional<T>> ot {in_place};
- optional<optional<T>> ou {in_place, nullopt};
- optional<optional<T>> ov {optional<T>{}};
-
- optional<int> oi;
- auto ooi = make_optional(oi);
- static_assert( std::is_same<optional<optional<int>>, decltype(ooi)>::value, "");
- }
-};
-
-
-bool fun(std::string , std::experimental::optional<int> oi = std::experimental::nullopt)
-{
- return bool(oi);
-}
-
-TEST(example_converting_ctor)
-{
- using namespace std::experimental;
-
- assert (true == fun("dog", 2));
- assert (false == fun("dog"));
- assert (false == fun("dog", nullopt)); // just to be explicit
-};
-
-
-TEST(bad_comparison)
-{
- tr2::optional<int> oi, oj;
- int i;
- bool b = (oi == oj);
- b = (oi >= i);
- b = (oi == i);
- unused(b);
-};
-
-
-//// NOT APPLICABLE ANYMORE
-////TEST(perfect_ctor)
-////{
-//// //tr2::optional<std::string> ois = "OS";
-//// assert (*ois == "OS");
-////
-//// // FAILS: tr2::optional<ExplicitStr> oes = "OS";
-//// tr2::optional<ExplicitStr> oes{"OS"};
-//// assert (oes->s == "OS");
-////};
-
-TEST(value_or)
-{
- tr2::optional<int> oi = 1;
- int i = oi.value_or(0);
- assert (i == 1);
-
- oi = tr2::nullopt;
- assert (oi.value_or(3) == 3);
-
- tr2::optional<std::string> os{"AAA"};
- assert (os.value_or("BBB") == "AAA");
- os = {};
- assert (os.value_or("BBB") == "BBB");
-};
-
-TEST(mixed_order)
-{
- using namespace std::experimental;
-
- optional<int> oN {nullopt};
- optional<int> o0 {0};
- optional<int> o1 {1};
-
- assert ( (oN < 0));
- assert ( (oN < 1));
- assert (!(o0 < 0));
- assert ( (o0 < 1));
- assert (!(o1 < 0));
- assert (!(o1 < 1));
-
- assert (!(oN >= 0));
- assert (!(oN >= 1));
- assert ( (o0 >= 0));
- assert (!(o0 >= 1));
- assert ( (o1 >= 0));
- assert ( (o1 >= 1));
-
- assert (!(oN > 0));
- assert (!(oN > 1));
- assert (!(o0 > 0));
- assert (!(o0 > 1));
- assert ( (o1 > 0));
- assert (!(o1 > 1));
-
- assert ( (oN <= 0));
- assert ( (oN <= 1));
- assert ( (o0 <= 0));
- assert ( (o0 <= 1));
- assert (!(o1 <= 0));
- assert ( (o1 <= 1));
-
- assert ( (0 > oN));
- assert ( (1 > oN));
- assert (!(0 > o0));
- assert ( (1 > o0));
- assert (!(0 > o1));
- assert (!(1 > o1));
-
- assert (!(0 <= oN));
- assert (!(1 <= oN));
- assert ( (0 <= o0));
- assert (!(1 <= o0));
- assert ( (0 <= o1));
- assert ( (1 <= o1));
-
- assert (!(0 < oN));
- assert (!(1 < oN));
- assert (!(0 < o0));
- assert (!(1 < o0));
- assert ( (0 < o1));
- assert (!(1 < o1));
-
- assert ( (0 >= oN));
- assert ( (1 >= oN));
- assert ( (0 >= o0));
- assert ( (1 >= o0));
- assert (!(0 >= o1));
- assert ( (1 >= o1));
-};
-
-struct BadRelops
-{
- int i;
-};
-
-constexpr bool operator<(BadRelops a, BadRelops b) { return a.i < b.i; }
-constexpr bool operator>(BadRelops a, BadRelops b) { return a.i < b.i; } // intentional error!
-
-TEST(bad_relops)
-{
- using namespace std::experimental;
- BadRelops a{1}, b{2};
- assert (a < b);
- assert (a > b);
-
- optional<BadRelops> oa = a, ob = b;
- assert (oa < ob);
- assert (!(oa > ob));
-
- assert (oa < b);
- assert (oa > b);
-
- optional<BadRelops&> ra = a, rb = b;
- assert (ra < rb);
- assert (!(ra > rb));
-
- assert (ra < b);
- assert (ra > b);
-};
-
-
-TEST(mixed_equality)
-{
- using namespace std::experimental;
-
- assert (make_optional(0) == 0);
- assert (make_optional(1) == 1);
- assert (make_optional(0) != 1);
- assert (make_optional(1) != 0);
-
- optional<int> oN {nullopt};
- optional<int> o0 {0};
- optional<int> o1 {1};
-
- assert (o0 == 0);
- assert ( 0 == o0);
- assert (o1 == 1);
- assert ( 1 == o1);
- assert (o1 != 0);
- assert ( 0 != o1);
- assert (o0 != 1);
- assert ( 1 != o0);
-
- assert ( 1 != oN);
- assert ( 0 != oN);
- assert (oN != 1);
- assert (oN != 0);
- assert (!( 1 == oN));
- assert (!( 0 == oN));
- assert (!(oN == 1));
- assert (!(oN == 0));
-
- std::string cat{"cat"}, dog{"dog"};
- optional<std::string> oNil{}, oDog{"dog"}, oCat{"cat"};
-
- assert (oCat == cat);
- assert ( cat == oCat);
- assert (oDog == dog);
- assert ( dog == oDog);
- assert (oDog != cat);
- assert ( cat != oDog);
- assert (oCat != dog);
- assert ( dog != oCat);
-
- assert ( dog != oNil);
- assert ( cat != oNil);
- assert (oNil != dog);
- assert (oNil != cat);
- assert (!( dog == oNil));
- assert (!( cat == oNil));
- assert (!(oNil == dog));
- assert (!(oNil == cat));
-};
-
-TEST(const_propagation)
-{
- using namespace std::experimental;
-
- optional<int> mmi{0};
- static_assert(std::is_same<decltype(*mmi), int&>::value, "WTF");
-
- const optional<int> cmi{0};
- static_assert(std::is_same<decltype(*cmi), const int&>::value, "WTF");
-
- optional<const int> mci{0};
- static_assert(std::is_same<decltype(*mci), const int&>::value, "WTF");
-
- optional<const int> cci{0};
- static_assert(std::is_same<decltype(*cci), const int&>::value, "WTF");
-};
-
-
-static_assert(std::is_base_of<std::logic_error, std::experimental::bad_optional_access>::value, "");
-
-TEST(safe_value)
-{
- using namespace std::experimental;
-
- try {
- optional<int> ovN{}, ov1{1};
-
- int& r1 = ov1.value();
- assert (r1 == 1);
-
- try {
- ovN.value();
- assert (false);
- }
- catch (bad_optional_access const&) {
- }
-
- { // ref variant
- int i1 = 1;
- optional<int&> orN{}, or1{i1};
-
- int& r2 = or1.value();
- assert (r2 == 1);
-
- try {
- orN.value();
- assert (false);
- }
- catch (bad_optional_access const&) {
- }
- }
- }
- catch(...) {
- assert (false);
- }
-};
-
-TEST(optional_ref)
-{
- using namespace tr2;
- // FAILS: optional<int&&> orr;
- // FAILS: optional<nullopt_t&> on;
- int i = 8;
- optional<int&> ori;
- assert (!ori);
- ori.emplace(i);
- assert (bool(ori));
- assert (*ori == 8);
- assert (&*ori == &i);
- *ori = 9;
- assert (i == 9);
-
- // FAILS: int& ir = ori.value_or(i);
- int ii = ori.value_or(i);
- assert (ii == 9);
- ii = 7;
- assert (*ori == 9);
-
- int j = 22;
- auto&& oj = make_optional(std::ref(j));
- *oj = 23;
- assert (&*oj == &j);
- assert (j == 23);
-};
-
-TEST(optional_ref_const_propagation)
-{
- using namespace std::experimental;
-
- int i = 9;
- const optional<int&> mi = i;
- int& r = *mi;
- optional<const int&> ci = i;
- static_assert(std::is_same<decltype(*mi), int&>::value, "WTF");
- static_assert(std::is_same<decltype(*ci), const int&>::value, "WTF");
-
- unused(r);
-};
-
-TEST(optional_ref_assign)
-{
- using namespace std::experimental;
-
- int i = 9;
- optional<int&> ori = i;
-
- int j = 1;
- ori = optional<int&>{j};
- ori = {j};
- // FAILS: ori = j;
-
- optional<int&> orx = ori;
- ori = orx;
-
- optional<int&> orj = j;
-
- assert (ori);
- assert (*ori == 1);
- assert (ori == orj);
- assert (i == 9);
-
- *ori = 2;
- assert (*ori == 2);
- assert (ori == 2);
- assert (2 == ori);
- assert (ori != 3);
-
- assert (ori == orj);
- assert (j == 2);
- assert (i == 9);
-
- ori = {};
- assert (!ori);
- assert (ori != orj);
- assert (j == 2);
- assert (i == 9);
-};
-
-
-TEST(optional_ref_swap)
-{
- using namespace std::experimental;
- int i = 0;
- int j = 1;
- optional<int&> oi = i;
- optional<int&> oj = j;
-
- assert (&*oi == &i);
- assert (&*oj == &j);
-
- swap(oi, oj);
- assert (&*oi == &j);
- assert (&*oj == &i);
-};
-
-TEST(optional_initialization)
-{
- using namespace tr2;
- using std::string;
- string s = "STR";
-
- optional<string> os{s};
- optional<string> ot = s;
- optional<string> ou{"STR"};
- optional<string> ov = string{"STR"};
-
-};
-
-#include <unordered_set>
-
-TEST(optional_hashing)
-{
- using namespace tr2;
- using std::string;
-
- std::hash<int> hi;
- std::hash<optional<int>> hoi;
- std::hash<string> hs;
- std::hash<optional<string>> hos;
-
- assert (hi(0) == hoi(optional<int>{0}));
- assert (hi(1) == hoi(optional<int>{1}));
- assert (hi(3198) == hoi(optional<int>{3198}));
-
- assert (hs("") == hos(optional<string>{""}));
- assert (hs("0") == hos(optional<string>{"0"}));
- assert (hs("Qa1#") == hos(optional<string>{"Qa1#"}));
-
- std::unordered_set<optional<string>> set;
- assert(set.find({"Qa1#"}) == set.end());
-
- set.insert({"0"});
- assert(set.find({"Qa1#"}) == set.end());
-
- set.insert({"Qa1#"});
- assert(set.find({"Qa1#"}) != set.end());
-};
-
-
-// optional_ref_emulation
-template <class T>
-struct generic
-{
- typedef T type;
-};
-
-template <class U>
-struct generic<U&>
-{
- typedef std::reference_wrapper<U> type;
-};
-
-template <class T>
-using Generic = typename generic<T>::type;
-
-template <class X>
-bool generic_fun()
-{
- std::experimental::optional<Generic<X>> op;
- return bool(op);
-}
-
-TEST(optional_ref_emulation)
-{
- using namespace std::experimental;
- optional<Generic<int>> oi = 1;
- assert (*oi == 1);
-
- int i = 8;
- int j = 4;
- optional<Generic<int&>> ori {i};
- assert (*ori == 8);
- assert ((void*)&*ori != (void*)&i); // !DIFFERENT THAN optional<T&>
-
- *ori = j;
- assert (*ori == 4);
-};
-
-
-# if OPTIONAL_HAS_THIS_RVALUE_REFS == 1
-TEST(moved_on_value_or)
-{
- using namespace tr2;
- optional<Oracle> oo{in_place};
-
- assert (oo);
- assert (oo->s == sDefaultConstructed);
-
- Oracle o = std::move(oo).value_or( Oracle{OracleVal{}} );
- assert (oo);
- assert (oo->s == sMovedFrom);
- assert (o.s == sMoveConstructed);
-
- optional<MoveAware<int>> om {in_place, 1};
- assert (om);
- assert (om->moved == false);
-
- /*MoveAware<int> m =*/ std::move(om).value_or( MoveAware<int>{1} );
- assert (om);
- assert (om->moved == true);
-
-# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
- {
- Date d = optional<Date>{in_place, 1}.value();
- assert (d.i); // to silence compiler warning
-
- Date d2 = *optional<Date>{in_place, 1};
- assert (d2.i); // to silence compiler warning
- }
-# endif
-};
-# endif
-
-
-TEST(optional_ref_hashing)
-{
- using namespace tr2;
- using std::string;
-
- std::hash<int> hi;
- std::hash<optional<int&>> hoi;
- std::hash<string> hs;
- std::hash<optional<string&>> hos;
-
- int i0 = 0;
- int i1 = 1;
- assert (hi(0) == hoi(optional<int&>{i0}));
- assert (hi(1) == hoi(optional<int&>{i1}));
-
- string s{""};
- string s0{"0"};
- string sCAT{"CAT"};
- assert (hs("") == hos(optional<string&>{s}));
- assert (hs("0") == hos(optional<string&>{s0}));
- assert (hs("CAT") == hos(optional<string&>{sCAT}));
-
- std::unordered_set<optional<string&>> set;
- assert(set.find({sCAT}) == set.end());
-
- set.insert({s0});
- assert(set.find({sCAT}) == set.end());
-
- set.insert({sCAT});
- assert(set.find({sCAT}) != set.end());
-};
-
-struct Combined
-{
- int m = 0;
- int n = 1;
-
- constexpr Combined() : m{5}, n{6} {}
- constexpr Combined(int m, int n) : m{m}, n{n} {}
-};
-
-struct Nasty
-{
- int m = 0;
- int n = 1;
-
- constexpr Nasty() : m{5}, n{6} {}
- constexpr Nasty(int m, int n) : m{m}, n{n} {}
-
- int operator&() { return n; }
- int operator&() const { return n; }
-};
-
-TEST(arrow_operator)
-{
- using namespace std::experimental;
-
- optional<Combined> oc1{in_place, 1, 2};
- assert (oc1);
- assert (oc1->m == 1);
- assert (oc1->n == 2);
-
- optional<Nasty> on{in_place, 1, 2};
- assert (on);
- assert (on->m == 1);
- assert (on->n == 2);
-};
-
-TEST(arrow_wit_optional_ref)
-{
- using namespace std::experimental;
-
- Combined c{1, 2};
- optional<Combined&> oc = c;
- assert (oc);
- assert (oc->m == 1);
- assert (oc->n == 2);
-
- Nasty n{1, 2};
- Nasty m{3, 4};
- Nasty p{5, 6};
-
- optional<Nasty&> on{n};
- assert (on);
- assert (on->m == 1);
- assert (on->n == 2);
-
- on = {m};
- assert (on);
- assert (on->m == 3);
- assert (on->n == 4);
-
- on.emplace(p);
- assert (on);
- assert (on->m == 5);
- assert (on->n == 6);
-
- optional<Nasty&> om{in_place, n};
- assert (om);
- assert (om->m == 1);
- assert (om->n == 2);
-};
-
-TEST(no_dangling_reference_in_value)
-{
- // this mostly tests compiler warnings
- using namespace std::experimental;
- optional<int> oi {2};
- unused (oi.value());
- const optional<int> coi {3};
- unused (coi.value());
-};
-
-struct CountedObject
-{
- static int _counter;
- bool _throw;
- CountedObject(bool b) : _throw(b) { ++_counter; }
- CountedObject(CountedObject const& rhs) : _throw(rhs._throw) { if (_throw) throw int(); }
- ~CountedObject() { --_counter; }
-};
-
-int CountedObject::_counter = 0;
-
-TEST(exception_safety)
-{
- using namespace std::experimental;
- try {
- optional<CountedObject> oo(in_place, true); // throw
- optional<CountedObject> o1(oo);
- }
- catch(...)
- {
- //
- }
- assert(CountedObject::_counter == 0);
-
- try {
- optional<CountedObject> oo(in_place, true); // throw
- optional<CountedObject> o1(std::move(oo)); // now move
- }
- catch(...)
- {
- //
- }
- assert(CountedObject::_counter == 0);
-};
-
-//// constexpr tests
-
-// these 4 classes have different noexcept signatures in move operations
-struct NothrowBoth {
- NothrowBoth(NothrowBoth&&) noexcept(true) {};
- void operator=(NothrowBoth&&) noexcept(true) {};
-};
-struct NothrowCtor {
- NothrowCtor(NothrowCtor&&) noexcept(true) {};
- void operator=(NothrowCtor&&) noexcept(false) {};
-};
-struct NothrowAssign {
- NothrowAssign(NothrowAssign&&) noexcept(false) {};
- void operator=(NothrowAssign&&) noexcept(true) {};
-};
-struct NothrowNone {
- NothrowNone(NothrowNone&&) noexcept(false) {};
- void operator=(NothrowNone&&) noexcept(false) {};
-};
-
-void test_noexcept()
-{
- {
- tr2::optional<NothrowBoth> b1, b2;
- static_assert(noexcept(tr2::optional<NothrowBoth>{tr2::constexpr_move(b1)}), "bad noexcept!");
- static_assert(noexcept(b1 = tr2::constexpr_move(b2)), "bad noexcept!");
- }
- {
- tr2::optional<NothrowCtor> c1, c2;
- static_assert(noexcept(tr2::optional<NothrowCtor>{tr2::constexpr_move(c1)}), "bad noexcept!");
- static_assert(!noexcept(c1 = tr2::constexpr_move(c2)), "bad noexcept!");
- }
- {
- tr2::optional<NothrowAssign> a1, a2;
- static_assert(!noexcept(tr2::optional<NothrowAssign>{tr2::constexpr_move(a1)}), "bad noexcept!");
- static_assert(!noexcept(a1 = tr2::constexpr_move(a2)), "bad noexcept!");
- }
- {
- tr2::optional<NothrowNone> n1, n2;
- static_assert(!noexcept(tr2::optional<NothrowNone>{tr2::constexpr_move(n1)}), "bad noexcept!");
- static_assert(!noexcept(n1 = tr2::constexpr_move(n2)), "bad noexcept!");
- }
-}
-
-
-void constexpr_test_disengaged()
-{
- constexpr tr2::optional<int> g0{};
- constexpr tr2::optional<int> g1{tr2::nullopt};
- static_assert( !g0, "initialized!" );
- static_assert( !g1, "initialized!" );
-
- static_assert( bool(g1) == bool(g0), "ne!" );
-
- static_assert( g1 == g0, "ne!" );
- static_assert( !(g1 != g0), "ne!" );
- static_assert( g1 >= g0, "ne!" );
- static_assert( !(g1 > g0), "ne!" );
- static_assert( g1 <= g0, "ne!" );
- static_assert( !(g1 <g0), "ne!" );
-
- static_assert( g1 == tr2::nullopt, "!" );
- static_assert( !(g1 != tr2::nullopt), "!" );
- static_assert( g1 <= tr2::nullopt, "!" );
- static_assert( !(g1 < tr2::nullopt), "!" );
- static_assert( g1 >= tr2::nullopt, "!" );
- static_assert( !(g1 > tr2::nullopt), "!" );
-
- static_assert( (tr2::nullopt == g0), "!" );
- static_assert( !(tr2::nullopt != g0), "!" );
- static_assert( (tr2::nullopt >= g0), "!" );
- static_assert( !(tr2::nullopt > g0), "!" );
- static_assert( (tr2::nullopt <= g0), "!" );
- static_assert( !(tr2::nullopt < g0), "!" );
-
- static_assert( (g1 != tr2::optional<int>(1)), "!" );
- static_assert( !(g1 == tr2::optional<int>(1)), "!" );
- static_assert( (g1 < tr2::optional<int>(1)), "!" );
- static_assert( (g1 <= tr2::optional<int>(1)), "!" );
- static_assert( !(g1 > tr2::optional<int>(1)), "!" );
- static_assert( !(g1 > tr2::optional<int>(1)), "!" );
-}
-
-
-constexpr tr2::optional<int> g0{};
-constexpr tr2::optional<int> g2{2};
-static_assert( g2, "not initialized!" );
-static_assert( *g2 == 2, "not 2!" );
-static_assert( g2 == tr2::optional<int>(2), "not 2!" );
-static_assert( g2 != g0, "eq!" );
-
-# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
-static_assert( *tr2::optional<int>{3} == 3, "WTF!" );
-static_assert( tr2::optional<int>{3}.value() == 3, "WTF!" );
-static_assert( tr2::optional<int>{3}.value_or(1) == 3, "WTF!" );
-static_assert( tr2::optional<int>{}.value_or(4) == 4, "WTF!" );
-# endif
-
-constexpr tr2::optional<Combined> gc0{tr2::in_place};
-static_assert(gc0->n == 6, "WTF!");
-
-// optional refs
-int gi = 0;
-constexpr tr2::optional<int&> gori = gi;
-constexpr tr2::optional<int&> gorn{};
-constexpr int& gri = *gori;
-static_assert(gori, "WTF");
-static_assert(!gorn, "WTF");
-static_assert(gori != tr2::nullopt, "WTF");
-static_assert(gorn == tr2::nullopt, "WTF");
-static_assert(&gri == &*gori, "WTF");
-
-constexpr int gci = 1;
-constexpr tr2::optional<int const&> gorci = gci;
-constexpr tr2::optional<int const&> gorcn{};
-
-static_assert(gorcn < gorci, "WTF");
-static_assert(gorcn <= gorci, "WTF");
-static_assert(gorci == gorci, "WTF");
-static_assert(*gorci == 1, "WTF");
-static_assert(gorci == gci, "WTF");
-
-namespace constexpr_optional_ref_and_arrow
-{
- using namespace std::experimental;
- constexpr Combined c{1, 2};
- constexpr optional<Combined const&> oc = c;
- static_assert(oc, "WTF!");
- static_assert(oc->m == 1, "WTF!");
- static_assert(oc->n == 2, "WTF!");
-}
-
-#if OPTIONAL_HAS_CONSTEXPR_INIT_LIST
-
-namespace InitList
-{
- using namespace std::experimental;
-
- struct ConstInitLister
- {
- template <typename T>
- constexpr ConstInitLister(std::initializer_list<T> il) : len (il.size()) {}
- size_t len;
- };
-
- constexpr ConstInitLister CIL {2, 3, 4};
- static_assert(CIL.len == 3, "WTF!");
-
- constexpr optional<ConstInitLister> oil {in_place, {4, 5, 6, 7}};
- static_assert(oil, "WTF!");
- static_assert(oil->len == 4, "WTF!");
-}
-
-#endif // OPTIONAL_HAS_CONSTEXPR_INIT_LIST
-
-// end constexpr tests
-
-
-#include <string>
-
-
-struct VEC
-{
- std::vector<int> v;
- template <typename... X>
- VEC( X&&...x) : v(std::forward<X>(x)...) {}
-
- template <typename U, typename... X>
- VEC(std::initializer_list<U> il, X&&...x) : v(il, std::forward<X>(x)...) {}
-};
-
-
-
-int main() {
- tr2::optional<int> oi = 1;
- assert (bool(oi));
- oi.operator=({});
- assert (!oi);
-
- VEC v = {5, 6};
-
- if (OPTIONAL_HAS_THIS_RVALUE_REFS)
- std::cout << "Optional has rvalue references for *this" << std::endl;
- else
- std::cout << "Optional doesn't have rvalue references for *this" << std::endl;
-
- if (OPTIONAL_HAS_CONSTEXPR_INIT_LIST)
- std::cout << "Optional has constexpr initializer_list" << std::endl;
- else
- std::cout << "Optional doesn't have constexpr initializer_list" << std::endl;
-
- if (OPTIONAL_HAS_MOVE_ACCESSORS)
- std::cout << "Optional has constexpr move accessors" << std::endl;
- else
- std::cout << "Optional doesn't have constexpr move accessors" << std::endl;
-}
-
diff --git a/src/libs/3rdparty/optional/test_type_traits.cpp b/src/libs/3rdparty/optional/test_type_traits.cpp
deleted file mode 100644
index 8ec82b7087..0000000000
--- a/src/libs/3rdparty/optional/test_type_traits.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (C) 2011 - 2012 Andrzej Krzemienski.
-//
-// Use, modification, and distribution is subject to the Boost Software
-// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
-// http://www.boost.org/LICENSE_1_0.txt)
-
-#if (defined __clang__)
- namespace std { class type_info; }
-#endif
-
-# include "optional.hpp"
-
-namespace std { namespace experimental {
-
-struct Val
-{
- Val(){}
- Val( Val const & ){}
- Val( Val && ) noexcept {}
-
- Val & operator=( Val const & ) = delete;
- Val & operator=( Val && ) noexcept = delete;
-};
-
-struct Safe
-{
- Safe(){}
- Safe( Safe const & ){}
- Safe( Safe && ) noexcept {}
-
- Safe & operator=( Safe const & ){ return *this; }
- Safe & operator=( Safe && ) noexcept { return *this; }
-};
-
-struct Unsafe
-{
- Unsafe(){}
- Unsafe( Unsafe const & ){}
- Unsafe( Unsafe && ){}
-
- Unsafe & operator=( Unsafe const & ){ return *this; }
- Unsafe & operator=( Unsafe && ) { return *this; }
-};
-
-struct VoidNothrowBoth
-{
- VoidNothrowBoth(VoidNothrowBoth&&) noexcept(true) {};
- void operator=(VoidNothrowBoth&&) noexcept(true) {}; // note void return type
-};
-
-
-static_assert(is_nothrow_move_constructible<Safe>::value, "WTF!");
-static_assert(!is_nothrow_move_constructible<Unsafe>::value, "WTF!");
-
-static_assert(is_assignable<Safe&, Safe&&>::value, "WTF!");
-static_assert(!is_assignable<Val&, Val&&>::value, "WTF!");
-
-static_assert(is_nothrow_move_assignable<Safe>::value, "WTF!");
-static_assert(!is_nothrow_move_assignable<Unsafe>::value, "WTF!");
-
-static_assert(is_nothrow_move_constructible<VoidNothrowBoth>::value, "WTF!");
-static_assert(is_nothrow_move_assignable<VoidNothrowBoth>::value, "WTF!");
-
-}} // namespace std::experimental
-
-int main() { }
diff --git a/src/libs/3rdparty/qrcodegen/CMakeLists.txt b/src/libs/3rdparty/qrcodegen/CMakeLists.txt
new file mode 100644
index 0000000000..15a03147ee
--- /dev/null
+++ b/src/libs/3rdparty/qrcodegen/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_qtc_library(QrCodeGenerator STATIC
+ CONDITION TARGET Qt6::Quick AND TARGET Qt6::Svg
+ PUBLIC_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/src
+ DEPENDS
+ Qt6::Qml Qt6::Quick Qt6::Svg
+ SOURCES
+ src/qrcodegen.cpp
+ src/qrcodegen.h
+ src/qrcodeimageprovider.cpp
+ src/qrcodeimageprovider.h
+)
diff --git a/src/libs/3rdparty/qrcodegen/LICENSE b/src/libs/3rdparty/qrcodegen/LICENSE
new file mode 100644
index 0000000000..da7a8e0aae
--- /dev/null
+++ b/src/libs/3rdparty/qrcodegen/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Alex Spataru
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/libs/3rdparty/qrcodegen/README.md b/src/libs/3rdparty/qrcodegen/README.md
new file mode 100644
index 0000000000..dc81138477
--- /dev/null
+++ b/src/libs/3rdparty/qrcodegen/README.md
@@ -0,0 +1,25 @@
+# Qt QR Code Generator Library
+
+Qt QR Code Generator is a simple C++ class that uses the [qrcodegen](https://github.com/nayuki/QR-Code-generator) library to generate QR codes from QStrings in Qt applications.
+
+[![Screenshot](example/screenshot.png)](example/screenshot.png)
+
+## Usage
+
+1. Copy the *Qt-QrCodeGenerator* folder in your `lib` folder.
+2. Include the *Qt-QrCodeGenerator* project include (pri) file using the `include()` qmake function.
+3. Use the `QrCodeGenerator` class in your code:
+
+```cpp
+#include <QrCodeGenerator>
+
+QrCodeGenerator generator;
+QString data = "https://www.example.com";
+QImage qrCodeImage = generator.generateQr(data);
+```
+
+4. That's all! Check the [example](example) project as a reference for your project if needed.
+
+## License
+
+This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
diff --git a/src/libs/3rdparty/qrcodegen/src/qrcodegen.cpp b/src/libs/3rdparty/qrcodegen/src/qrcodegen.cpp
new file mode 100644
index 0000000000..e594b8b6b7
--- /dev/null
+++ b/src/libs/3rdparty/qrcodegen/src/qrcodegen.cpp
@@ -0,0 +1,863 @@
+/*
+ * QR Code generator library (C++)
+ *
+ * Copyright (c) Project Nayuki. (MIT License)
+ * https://www.nayuki.io/page/qr-code-generator-library
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * - The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * - The Software is provided "as is", without warranty of any kind, express or
+ * implied, including but not limited to the warranties of merchantability,
+ * fitness for a particular purpose and noninfringement. In no event shall the
+ * authors or copyright holders be liable for any claim, damages or other
+ * liability, whether in an action of contract, tort or otherwise, arising from,
+ * out of or in connection with the Software or the use or other dealings in the
+ * Software.
+ */
+
+#include "qrcodegen.h"
+#include <algorithm>
+#include <cassert>
+#include <climits>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <sstream>
+#include <utility>
+
+using std::int8_t;
+using std::size_t;
+using std::uint8_t;
+using std::vector;
+
+namespace qrcodegen {
+
+/*---- Class QrSegment ----*/
+
+QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2)
+ : modeBits(mode)
+{
+ numBitsCharCount[0] = cc0;
+ numBitsCharCount[1] = cc1;
+ numBitsCharCount[2] = cc2;
+}
+
+int QrSegment::Mode::getModeBits() const
+{
+ return modeBits;
+}
+
+int QrSegment::Mode::numCharCountBits(int ver) const
+{
+ return numBitsCharCount[(ver + 7) / 17];
+}
+
+const QrSegment::Mode QrSegment::Mode::NUMERIC(0x1, 10, 12, 14);
+const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13);
+const QrSegment::Mode QrSegment::Mode::BYTE(0x4, 8, 16, 16);
+const QrSegment::Mode QrSegment::Mode::KANJI(0x8, 8, 10, 12);
+const QrSegment::Mode QrSegment::Mode::ECI(0x7, 0, 0, 0);
+
+QrSegment QrSegment::makeBytes(const vector<uint8_t> &data)
+{
+ if (data.size() > static_cast<unsigned int>(INT_MAX))
+ throw std::length_error("Data too long");
+ BitBuffer bb;
+ for (uint8_t b : data)
+ bb.appendBits(b, 8);
+ return QrSegment(Mode::BYTE, static_cast<int>(data.size()), std::move(bb));
+}
+
+QrSegment QrSegment::makeNumeric(const char *digits)
+{
+ BitBuffer bb;
+ int accumData = 0;
+ int accumCount = 0;
+ int charCount = 0;
+ for (; *digits != '\0'; digits++, charCount++) {
+ char c = *digits;
+ if (c < '0' || c > '9')
+ throw std::domain_error("String contains non-numeric characters");
+ accumData = accumData * 10 + (c - '0');
+ accumCount++;
+ if (accumCount == 3) {
+ bb.appendBits(static_cast<uint32_t>(accumData), 10);
+ accumData = 0;
+ accumCount = 0;
+ }
+ }
+ if (accumCount > 0) // 1 or 2 digits remaining
+ bb.appendBits(static_cast<uint32_t>(accumData), accumCount * 3 + 1);
+ return QrSegment(Mode::NUMERIC, charCount, std::move(bb));
+}
+
+QrSegment QrSegment::makeAlphanumeric(const char *text)
+{
+ BitBuffer bb;
+ int accumData = 0;
+ int accumCount = 0;
+ int charCount = 0;
+ for (; *text != '\0'; text++, charCount++) {
+ const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text);
+ if (temp == nullptr)
+ throw std::domain_error("String contains unencodable characters in alphanumeric mode");
+ accumData = accumData * 45 + static_cast<int>(temp - ALPHANUMERIC_CHARSET);
+ accumCount++;
+ if (accumCount == 2) {
+ bb.appendBits(static_cast<uint32_t>(accumData), 11);
+ accumData = 0;
+ accumCount = 0;
+ }
+ }
+ if (accumCount > 0) // 1 character remaining
+ bb.appendBits(static_cast<uint32_t>(accumData), 6);
+ return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb));
+}
+
+vector<QrSegment> QrSegment::makeSegments(const char *text)
+{
+ // Select the most efficient segment encoding automatically
+ vector<QrSegment> result;
+ if (*text == '\0')
+ ; // Leave result empty
+ else if (isNumeric(text))
+ result.push_back(makeNumeric(text));
+ else if (isAlphanumeric(text))
+ result.push_back(makeAlphanumeric(text));
+ else {
+ vector<uint8_t> bytes;
+ for (; *text != '\0'; text++)
+ bytes.push_back(static_cast<uint8_t>(*text));
+ result.push_back(makeBytes(bytes));
+ }
+ return result;
+}
+
+QrSegment QrSegment::makeEci(long assignVal)
+{
+ BitBuffer bb;
+ if (assignVal < 0)
+ throw std::domain_error("ECI assignment value out of range");
+ else if (assignVal < (1 << 7))
+ bb.appendBits(static_cast<uint32_t>(assignVal), 8);
+ else if (assignVal < (1 << 14)) {
+ bb.appendBits(2, 2);
+ bb.appendBits(static_cast<uint32_t>(assignVal), 14);
+ } else if (assignVal < 1000000L) {
+ bb.appendBits(6, 3);
+ bb.appendBits(static_cast<uint32_t>(assignVal), 21);
+ } else
+ throw std::domain_error("ECI assignment value out of range");
+ return QrSegment(Mode::ECI, 0, std::move(bb));
+}
+
+QrSegment::QrSegment(const Mode &md, int numCh, const std::vector<bool> &dt)
+ : mode(&md)
+ , numChars(numCh)
+ , data(dt)
+{
+ if (numCh < 0)
+ throw std::domain_error("Invalid value");
+}
+
+QrSegment::QrSegment(const Mode &md, int numCh, std::vector<bool> &&dt)
+ : mode(&md)
+ , numChars(numCh)
+ , data(std::move(dt))
+{
+ if (numCh < 0)
+ throw std::domain_error("Invalid value");
+}
+
+int QrSegment::getTotalBits(const vector<QrSegment> &segs, int version)
+{
+ int result = 0;
+ for (const QrSegment &seg : segs) {
+ int ccbits = seg.mode->numCharCountBits(version);
+ if (seg.numChars >= (1L << ccbits))
+ return -1; // The segment's length doesn't fit the field's bit width
+ if (4 + ccbits > INT_MAX - result)
+ return -1; // The sum will overflow an int type
+ result += 4 + ccbits;
+ if (seg.data.size() > static_cast<unsigned int>(INT_MAX - result))
+ return -1; // The sum will overflow an int type
+ result += static_cast<int>(seg.data.size());
+ }
+ return result;
+}
+
+bool QrSegment::isNumeric(const char *text)
+{
+ for (; *text != '\0'; text++) {
+ char c = *text;
+ if (c < '0' || c > '9')
+ return false;
+ }
+ return true;
+}
+
+bool QrSegment::isAlphanumeric(const char *text)
+{
+ for (; *text != '\0'; text++) {
+ if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr)
+ return false;
+ }
+ return true;
+}
+
+const QrSegment::Mode &QrSegment::getMode() const
+{
+ return *mode;
+}
+
+int QrSegment::getNumChars() const
+{
+ return numChars;
+}
+
+const std::vector<bool> &QrSegment::getData() const
+{
+ return data;
+}
+
+const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
+
+/*---- Class QrCode ----*/
+
+int QrCode::getFormatBits(Ecc ecl)
+{
+ switch (ecl) {
+ case Ecc::LOW:
+ return 1;
+ case Ecc::MEDIUM:
+ return 0;
+ case Ecc::QUARTILE:
+ return 3;
+ case Ecc::HIGH:
+ return 2;
+ default:
+ throw std::logic_error("Unreachable");
+ }
+}
+
+QrCode QrCode::encodeText(const char *text, Ecc ecl)
+{
+ vector<QrSegment> segs = QrSegment::makeSegments(text);
+ return encodeSegments(segs, ecl);
+}
+
+QrCode QrCode::encodeBinary(const vector<uint8_t> &data, Ecc ecl)
+{
+ vector<QrSegment> segs{QrSegment::makeBytes(data)};
+ return encodeSegments(segs, ecl);
+}
+
+QrCode QrCode::encodeSegments(
+ const vector<QrSegment> &segs, Ecc ecl, int minVersion, int maxVersion, int mask, bool boostEcl)
+{
+ if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION)
+ || mask < -1 || mask > 7)
+ throw std::invalid_argument("Invalid value");
+
+ // Find the minimal version number to use
+ int version, dataUsedBits;
+ for (version = minVersion;; version++) {
+ int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available
+ dataUsedBits = QrSegment::getTotalBits(segs, version);
+ if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits)
+ break; // This version number is found to be suitable
+ if (version >= maxVersion) { // All versions in the range could not fit the given data
+ std::ostringstream sb;
+ if (dataUsedBits == -1)
+ sb << "Segment too long";
+ else {
+ sb << "Data length = " << dataUsedBits << " bits, ";
+ sb << "Max capacity = " << dataCapacityBits << " bits";
+ }
+ throw data_too_long(sb.str());
+ }
+ }
+ assert(dataUsedBits != -1);
+
+ // Increase the error correction level while the data still fits in the current version number
+ for (Ecc newEcl : {Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high
+ if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8)
+ ecl = newEcl;
+ }
+
+ // Concatenate all segments to create the data bit string
+ BitBuffer bb;
+ for (const QrSegment &seg : segs) {
+ bb.appendBits(static_cast<uint32_t>(seg.getMode().getModeBits()), 4);
+ bb.appendBits(static_cast<uint32_t>(seg.getNumChars()),
+ seg.getMode().numCharCountBits(version));
+ bb.insert(bb.end(), seg.getData().begin(), seg.getData().end());
+ }
+ assert(bb.size() == static_cast<unsigned int>(dataUsedBits));
+
+ // Add terminator and pad up to a byte if applicable
+ size_t dataCapacityBits = static_cast<size_t>(getNumDataCodewords(version, ecl)) * 8;
+ assert(bb.size() <= dataCapacityBits);
+ bb.appendBits(0, std::min(4, static_cast<int>(dataCapacityBits - bb.size())));
+ bb.appendBits(0, (8 - static_cast<int>(bb.size() % 8)) % 8);
+ assert(bb.size() % 8 == 0);
+
+ // Pad with alternating bytes until data capacity is reached
+ for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11)
+ bb.appendBits(padByte, 8);
+
+ // Pack bits into bytes in big endian
+ vector<uint8_t> dataCodewords(bb.size() / 8);
+ for (size_t i = 0; i < bb.size(); i++)
+ dataCodewords.at(i >> 3) |= (bb.at(i) ? 1 : 0) << (7 - (i & 7));
+
+ // Create the QR Code object
+ return QrCode(version, ecl, dataCodewords, mask);
+}
+
+QrCode::QrCode(int ver, Ecc ecl, const vector<uint8_t> &dataCodewords, int msk)
+ : // Initialize fields and check arguments
+ version(ver)
+ , errorCorrectionLevel(ecl)
+{
+ if (ver < MIN_VERSION || ver > MAX_VERSION)
+ throw std::domain_error("Version value out of range");
+ if (msk < -1 || msk > 7)
+ throw std::domain_error("Mask value out of range");
+ size = ver * 4 + 17;
+ size_t sz = static_cast<size_t>(size);
+ modules = vector<vector<bool>>(sz, vector<bool>(sz)); // Initially all light
+ isFunction = vector<vector<bool>>(sz, vector<bool>(sz));
+
+ // Compute ECC, draw modules
+ drawFunctionPatterns();
+ const vector<uint8_t> allCodewords = addEccAndInterleave(dataCodewords);
+ drawCodewords(allCodewords);
+
+ // Do masking
+ if (msk == -1) { // Automatically choose best mask
+ long minPenalty = LONG_MAX;
+ for (int i = 0; i < 8; i++) {
+ applyMask(i);
+ drawFormatBits(i);
+ long penalty = getPenaltyScore();
+ if (penalty < minPenalty) {
+ msk = i;
+ minPenalty = penalty;
+ }
+ applyMask(i); // Undoes the mask due to XOR
+ }
+ }
+ assert(0 <= msk && msk <= 7);
+ mask = msk;
+ applyMask(msk); // Apply the final choice of mask
+ drawFormatBits(msk); // Overwrite old format bits
+
+ isFunction.clear();
+ isFunction.shrink_to_fit();
+}
+
+int QrCode::getVersion() const
+{
+ return version;
+}
+
+int QrCode::getSize() const
+{
+ return size;
+}
+
+QrCode::Ecc QrCode::getErrorCorrectionLevel() const
+{
+ return errorCorrectionLevel;
+}
+
+int QrCode::getMask() const
+{
+ return mask;
+}
+
+bool QrCode::getModule(int x, int y) const
+{
+ return 0 <= x && x < size && 0 <= y && y < size && module(x, y);
+}
+
+void QrCode::drawFunctionPatterns()
+{
+ // Draw horizontal and vertical timing patterns
+ for (int i = 0; i < size; i++) {
+ setFunctionModule(6, i, i % 2 == 0);
+ setFunctionModule(i, 6, i % 2 == 0);
+ }
+
+ // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
+ drawFinderPattern(3, 3);
+ drawFinderPattern(size - 4, 3);
+ drawFinderPattern(3, size - 4);
+
+ // Draw numerous alignment patterns
+ const vector<int> alignPatPos = getAlignmentPatternPositions();
+ size_t numAlign = alignPatPos.size();
+ for (size_t i = 0; i < numAlign; i++) {
+ for (size_t j = 0; j < numAlign; j++) {
+ // Don't draw on the three finder corners
+ if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)))
+ drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j));
+ }
+ }
+
+ // Draw configuration data
+ drawFormatBits(0); // Dummy mask value; overwritten later in the constructor
+ drawVersion();
+}
+
+void QrCode::drawFormatBits(int msk)
+{
+ // Calculate error correction code and pack bits
+ int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3
+ int rem = data;
+ for (int i = 0; i < 10; i++)
+ rem = (rem << 1) ^ ((rem >> 9) * 0x537);
+ int bits = (data << 10 | rem) ^ 0x5412; // uint15
+ assert(bits >> 15 == 0);
+
+ // Draw first copy
+ for (int i = 0; i <= 5; i++)
+ setFunctionModule(8, i, getBit(bits, i));
+ setFunctionModule(8, 7, getBit(bits, 6));
+ setFunctionModule(8, 8, getBit(bits, 7));
+ setFunctionModule(7, 8, getBit(bits, 8));
+ for (int i = 9; i < 15; i++)
+ setFunctionModule(14 - i, 8, getBit(bits, i));
+
+ // Draw second copy
+ for (int i = 0; i < 8; i++)
+ setFunctionModule(size - 1 - i, 8, getBit(bits, i));
+ for (int i = 8; i < 15; i++)
+ setFunctionModule(8, size - 15 + i, getBit(bits, i));
+ setFunctionModule(8, size - 8, true); // Always dark
+}
+
+void QrCode::drawVersion()
+{
+ if (version < 7)
+ return;
+
+ // Calculate error correction code and pack bits
+ int rem = version; // version is uint6, in the range [7, 40]
+ for (int i = 0; i < 12; i++)
+ rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
+ long bits = static_cast<long>(version) << 12 | rem; // uint18
+ assert(bits >> 18 == 0);
+
+ // Draw two copies
+ for (int i = 0; i < 18; i++) {
+ bool bit = getBit(bits, i);
+ int a = size - 11 + i % 3;
+ int b = i / 3;
+ setFunctionModule(a, b, bit);
+ setFunctionModule(b, a, bit);
+ }
+}
+
+void QrCode::drawFinderPattern(int x, int y)
+{
+ for (int dy = -4; dy <= 4; dy++) {
+ for (int dx = -4; dx <= 4; dx++) {
+ int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm
+ int xx = x + dx, yy = y + dy;
+ if (0 <= xx && xx < size && 0 <= yy && yy < size)
+ setFunctionModule(xx, yy, dist != 2 && dist != 4);
+ }
+ }
+}
+
+void QrCode::drawAlignmentPattern(int x, int y)
+{
+ for (int dy = -2; dy <= 2; dy++) {
+ for (int dx = -2; dx <= 2; dx++)
+ setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1);
+ }
+}
+
+void QrCode::setFunctionModule(int x, int y, bool isDark)
+{
+ size_t ux = static_cast<size_t>(x);
+ size_t uy = static_cast<size_t>(y);
+ modules.at(uy).at(ux) = isDark;
+ isFunction.at(uy).at(ux) = true;
+}
+
+bool QrCode::module(int x, int y) const
+{
+ return modules.at(static_cast<size_t>(y)).at(static_cast<size_t>(x));
+}
+
+vector<uint8_t> QrCode::addEccAndInterleave(const vector<uint8_t> &data) const
+{
+ if (data.size() != static_cast<unsigned int>(getNumDataCodewords(version, errorCorrectionLevel)))
+ throw std::invalid_argument("Invalid argument");
+
+ // Calculate parameter numbers
+ int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(errorCorrectionLevel)][version];
+ int blockEccLen = ECC_CODEWORDS_PER_BLOCK[static_cast<int>(errorCorrectionLevel)][version];
+ int rawCodewords = getNumRawDataModules(version) / 8;
+ int numShortBlocks = numBlocks - rawCodewords % numBlocks;
+ int shortBlockLen = rawCodewords / numBlocks;
+
+ // Split data into blocks and append ECC to each block
+ vector<vector<uint8_t>> blocks;
+ const vector<uint8_t> rsDiv = reedSolomonComputeDivisor(blockEccLen);
+ for (int i = 0, k = 0; i < numBlocks; i++) {
+ vector<uint8_t> dat(data.cbegin() + k,
+ data.cbegin()
+ + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)));
+ k += static_cast<int>(dat.size());
+ const vector<uint8_t> ecc = reedSolomonComputeRemainder(dat, rsDiv);
+ if (i < numShortBlocks)
+ dat.push_back(0);
+ dat.insert(dat.end(), ecc.cbegin(), ecc.cend());
+ blocks.push_back(std::move(dat));
+ }
+
+ // Interleave (not concatenate) the bytes from every block into a single sequence
+ vector<uint8_t> result;
+ for (size_t i = 0; i < blocks.at(0).size(); i++) {
+ for (size_t j = 0; j < blocks.size(); j++) {
+ // Skip the padding byte in short blocks
+ if (i != static_cast<unsigned int>(shortBlockLen - blockEccLen)
+ || j >= static_cast<unsigned int>(numShortBlocks))
+ result.push_back(blocks.at(j).at(i));
+ }
+ }
+ assert(result.size() == static_cast<unsigned int>(rawCodewords));
+ return result;
+}
+
+void QrCode::drawCodewords(const vector<uint8_t> &data)
+{
+ if (data.size() != static_cast<unsigned int>(getNumRawDataModules(version) / 8))
+ throw std::invalid_argument("Invalid argument");
+
+ size_t i = 0; // Bit index into the data
+ // Do the funny zigzag scan
+ for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair
+ if (right == 6)
+ right = 5;
+ for (int vert = 0; vert < size; vert++) { // Vertical counter
+ for (int j = 0; j < 2; j++) {
+ size_t x = static_cast<size_t>(right - j); // Actual x coordinate
+ bool upward = ((right + 1) & 2) == 0;
+ size_t y = static_cast<size_t>(upward ? size - 1 - vert : vert); // Actual y coordinate
+ if (!isFunction.at(y).at(x) && i < data.size() * 8) {
+ modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast<int>(i & 7));
+ i++;
+ }
+ // If this QR Code has any remainder bits (0 to 7), they were assigned as
+ // 0/false/light by the constructor and are left unchanged by this method
+ }
+ }
+ }
+ assert(i == data.size() * 8);
+}
+
+void QrCode::applyMask(int msk)
+{
+ if (msk < 0 || msk > 7)
+ throw std::domain_error("Mask value out of range");
+ size_t sz = static_cast<size_t>(size);
+ for (size_t y = 0; y < sz; y++) {
+ for (size_t x = 0; x < sz; x++) {
+ bool invert;
+ switch (msk) {
+ case 0:
+ invert = (x + y) % 2 == 0;
+ break;
+ case 1:
+ invert = y % 2 == 0;
+ break;
+ case 2:
+ invert = x % 3 == 0;
+ break;
+ case 3:
+ invert = (x + y) % 3 == 0;
+ break;
+ case 4:
+ invert = (x / 3 + y / 2) % 2 == 0;
+ break;
+ case 5:
+ invert = x * y % 2 + x * y % 3 == 0;
+ break;
+ case 6:
+ invert = (x * y % 2 + x * y % 3) % 2 == 0;
+ break;
+ case 7:
+ invert = ((x + y) % 2 + x * y % 3) % 2 == 0;
+ break;
+ default:
+ throw std::logic_error("Unreachable");
+ }
+ modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x));
+ }
+ }
+}
+
+long QrCode::getPenaltyScore() const
+{
+ long result = 0;
+
+ // Adjacent modules in row having same color, and finder-like patterns
+ for (int y = 0; y < size; y++) {
+ bool runColor = false;
+ int runX = 0;
+ std::array<int, 7> runHistory = {};
+ for (int x = 0; x < size; x++) {
+ if (module(x, y) == runColor) {
+ runX++;
+ if (runX == 5)
+ result += PENALTY_N1;
+ else if (runX > 5)
+ result++;
+ } else {
+ finderPenaltyAddHistory(runX, runHistory);
+ if (!runColor)
+ result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
+ runColor = module(x, y);
+ runX = 1;
+ }
+ }
+ result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3;
+ }
+ // Adjacent modules in column having same color, and finder-like patterns
+ for (int x = 0; x < size; x++) {
+ bool runColor = false;
+ int runY = 0;
+ std::array<int, 7> runHistory = {};
+ for (int y = 0; y < size; y++) {
+ if (module(x, y) == runColor) {
+ runY++;
+ if (runY == 5)
+ result += PENALTY_N1;
+ else if (runY > 5)
+ result++;
+ } else {
+ finderPenaltyAddHistory(runY, runHistory);
+ if (!runColor)
+ result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3;
+ runColor = module(x, y);
+ runY = 1;
+ }
+ }
+ result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3;
+ }
+
+ // 2*2 blocks of modules having same color
+ for (int y = 0; y < size - 1; y++) {
+ for (int x = 0; x < size - 1; x++) {
+ bool color = module(x, y);
+ if (color == module(x + 1, y) && color == module(x, y + 1)
+ && color == module(x + 1, y + 1))
+ result += PENALTY_N2;
+ }
+ }
+
+ // Balance of dark and light modules
+ int dark = 0;
+ for (const vector<bool> &row : modules) {
+ for (bool color : row) {
+ if (color)
+ dark++;
+ }
+ }
+ int total = size * size; // Note that size is odd, so dark/total != 1/2
+ // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)%
+ int k = static_cast<int>((std::abs(dark * 20L - total * 10L) + total - 1) / total) - 1;
+ assert(0 <= k && k <= 9);
+ result += k * PENALTY_N4;
+ assert(0 <= result
+ && result <= 2568888L); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4
+ return result;
+}
+
+vector<int> QrCode::getAlignmentPatternPositions() const
+{
+ if (version == 1)
+ return vector<int>();
+ else {
+ int numAlign = version / 7 + 2;
+ int step = (version == 32) ? 26 : (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2;
+ vector<int> result;
+ for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step)
+ result.insert(result.begin(), pos);
+ result.insert(result.begin(), 6);
+ return result;
+ }
+}
+
+int QrCode::getNumRawDataModules(int ver)
+{
+ if (ver < MIN_VERSION || ver > MAX_VERSION)
+ throw std::domain_error("Version number out of range");
+ int result = (16 * ver + 128) * ver + 64;
+ if (ver >= 2) {
+ int numAlign = ver / 7 + 2;
+ result -= (25 * numAlign - 10) * numAlign - 55;
+ if (ver >= 7)
+ result -= 36;
+ }
+ assert(208 <= result && result <= 29648);
+ return result;
+}
+
+int QrCode::getNumDataCodewords(int ver, Ecc ecl)
+{
+ return getNumRawDataModules(ver) / 8
+ - ECC_CODEWORDS_PER_BLOCK[static_cast<int>(ecl)][ver]
+ * NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(ecl)][ver];
+}
+
+vector<uint8_t> QrCode::reedSolomonComputeDivisor(int degree)
+{
+ if (degree < 1 || degree > 255)
+ throw std::domain_error("Degree out of range");
+ // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.
+ // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}.
+ vector<uint8_t> result(static_cast<size_t>(degree));
+ result.at(result.size() - 1) = 1; // Start off with the monomial x^0
+
+ // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
+ // and drop the highest monomial term which is always 1x^degree.
+ // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
+ uint8_t root = 1;
+ for (int i = 0; i < degree; i++) {
+ // Multiply the current product by (x - r^i)
+ for (size_t j = 0; j < result.size(); j++) {
+ result.at(j) = reedSolomonMultiply(result.at(j), root);
+ if (j + 1 < result.size())
+ result.at(j) ^= result.at(j + 1);
+ }
+ root = reedSolomonMultiply(root, 0x02);
+ }
+ return result;
+}
+
+vector<uint8_t> QrCode::reedSolomonComputeRemainder(const vector<uint8_t> &data,
+ const vector<uint8_t> &divisor)
+{
+ vector<uint8_t> result(divisor.size());
+ for (uint8_t b : data) { // Polynomial division
+ uint8_t factor = b ^ result.at(0);
+ result.erase(result.begin());
+ result.push_back(0);
+ for (size_t i = 0; i < result.size(); i++)
+ result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor);
+ }
+ return result;
+}
+
+uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y)
+{
+ // Russian peasant multiplication
+ int z = 0;
+ for (int i = 7; i >= 0; i--) {
+ z = (z << 1) ^ ((z >> 7) * 0x11D);
+ z ^= ((y >> i) & 1) * x;
+ }
+ assert(z >> 8 == 0);
+ return static_cast<uint8_t>(z);
+}
+
+int QrCode::finderPenaltyCountPatterns(const std::array<int, 7> &runHistory) const
+{
+ int n = runHistory.at(1);
+ assert(n <= size * 3);
+ bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n
+ && runHistory.at(5) == n;
+ return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0)
+ + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0);
+}
+
+int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor,
+ int currentRunLength,
+ std::array<int, 7> &runHistory) const
+{
+ if (currentRunColor) { // Terminate dark run
+ finderPenaltyAddHistory(currentRunLength, runHistory);
+ currentRunLength = 0;
+ }
+ currentRunLength += size; // Add light border to final run
+ finderPenaltyAddHistory(currentRunLength, runHistory);
+ return finderPenaltyCountPatterns(runHistory);
+}
+
+void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array<int, 7> &runHistory) const
+{
+ if (runHistory.at(0) == 0)
+ currentRunLength += size; // Add light border to initial run
+ std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end());
+ runHistory.at(0) = currentRunLength;
+}
+
+bool QrCode::getBit(long x, int i)
+{
+ return ((x >> i) & 1) != 0;
+}
+
+/*---- Tables of constants ----*/
+
+const int QrCode::PENALTY_N1 = 3;
+const int QrCode::PENALTY_N2 = 3;
+const int QrCode::PENALTY_N3 = 40;
+const int QrCode::PENALTY_N4 = 10;
+
+const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = {
+ // Version: (note that index 0 is for padding, and is set to an illegal value)
+ //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
+ {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28,
+ 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low
+ {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26,
+ 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium
+ {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30,
+ 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile
+ {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28,
+ 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High
+};
+
+const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = {
+ // Version: (note that index 0 is for padding, and is set to an illegal value)
+ //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
+ {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8,
+ 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low
+ {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16,
+ 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium
+ {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20,
+ 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile
+ {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25,
+ 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High
+};
+
+data_too_long::data_too_long(const std::string &msg)
+ : std::length_error(msg)
+{}
+
+/*---- Class BitBuffer ----*/
+
+BitBuffer::BitBuffer()
+ : std::vector<bool>()
+{}
+
+void BitBuffer::appendBits(std::uint32_t val, int len)
+{
+ if (len < 0 || len > 31 || val >> len != 0)
+ throw std::domain_error("Value out of range");
+ for (int i = len - 1; i >= 0; i--) // Append bit by bit
+ this->push_back(((val >> i) & 1) != 0);
+}
+
+} // namespace qrcodegen
diff --git a/src/libs/3rdparty/qrcodegen/src/qrcodegen.h b/src/libs/3rdparty/qrcodegen/src/qrcodegen.h
new file mode 100644
index 0000000000..e2d285cf3e
--- /dev/null
+++ b/src/libs/3rdparty/qrcodegen/src/qrcodegen.h
@@ -0,0 +1,566 @@
+/*
+ * QR Code generator library (C++)
+ *
+ * Copyright (c) Project Nayuki. (MIT License)
+ * https://www.nayuki.io/page/qr-code-generator-library
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * - The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * - The Software is provided "as is", without warranty of any kind, express or
+ * implied, including but not limited to the warranties of merchantability,
+ * fitness for a particular purpose and noninfringement. In no event shall the
+ * authors or copyright holders be liable for any claim, damages or other
+ * liability, whether in an action of contract, tort or otherwise, arising from,
+ * out of or in connection with the Software or the use or other dealings in the
+ * Software.
+ */
+
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace qrcodegen {
+
+/*
+ * A segment of character/binary/control data in a QR Code symbol.
+ * Instances of this class are immutable.
+ * The mid-level way to create a segment is to take the payload data
+ * and call a static factory function such as QrSegment::makeNumeric().
+ * The low-level way to create a segment is to custom-make the bit buffer
+ * and call the QrSegment() constructor with appropriate values.
+ * This segment class imposes no length restrictions, but QR Codes have restrictions.
+ * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
+ * Any segment longer than this is meaningless for the purpose of generating QR Codes.
+ */
+class QrSegment final
+{
+ /*---- Public helper enumeration ----*/
+
+ /*
+ * Describes how a segment's data bits are interpreted. Immutable.
+ */
+public:
+ class Mode final
+ {
+ /*-- Constants --*/
+
+ public:
+ static const Mode NUMERIC;
+
+ public:
+ static const Mode ALPHANUMERIC;
+
+ public:
+ static const Mode BYTE;
+
+ public:
+ static const Mode KANJI;
+
+ public:
+ static const Mode ECI;
+
+ /*-- Fields --*/
+
+ // The mode indicator bits, which is a uint4 value (range 0 to 15).
+ private:
+ int modeBits;
+
+ // Number of character count bits for three different version ranges.
+ private:
+ int numBitsCharCount[3];
+
+ /*-- Constructor --*/
+
+ private:
+ Mode(int mode, int cc0, int cc1, int cc2);
+
+ /*-- Methods --*/
+
+ /*
+ * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15).
+ */
+ public:
+ int getModeBits() const;
+
+ /*
+ * (Package-private) Returns the bit width of the character count field for a segment in
+ * this mode in a QR Code at the given version number. The result is in the range [0, 16].
+ */
+ public:
+ int numCharCountBits(int ver) const;
+ };
+
+ /*---- Static factory functions (mid level) ----*/
+
+ /*
+ * Returns a segment representing the given binary data encoded in
+ * byte mode. All input byte vectors are acceptable. Any text string
+ * can be converted to UTF-8 bytes and encoded as a byte mode segment.
+ */
+public:
+ static QrSegment makeBytes(const std::vector<std::uint8_t> &data);
+
+ /*
+ * Returns a segment representing the given string of decimal digits encoded in numeric mode.
+ */
+public:
+ static QrSegment makeNumeric(const char *digits);
+
+ /*
+ * Returns a segment representing the given text string encoded in alphanumeric mode.
+ * The characters allowed are: 0 to 9, A to Z (uppercase only), space,
+ * dollar, percent, asterisk, plus, hyphen, period, slash, colon.
+ */
+public:
+ static QrSegment makeAlphanumeric(const char *text);
+
+ /*
+ * Returns a list of zero or more segments to represent the given text string. The result
+ * may use various segment modes and switch modes to optimize the length of the bit stream.
+ */
+public:
+ static std::vector<QrSegment> makeSegments(const char *text);
+
+ /*
+ * Returns a segment representing an Extended Channel Interpretation
+ * (ECI) designator with the given assignment value.
+ */
+public:
+ static QrSegment makeEci(long assignVal);
+
+ /*---- Public static helper functions ----*/
+
+ /*
+ * Tests whether the given string can be encoded as a segment in numeric mode.
+ * A string is encodable iff each character is in the range 0 to 9.
+ */
+public:
+ static bool isNumeric(const char *text);
+
+ /*
+ * Tests whether the given string can be encoded as a segment in alphanumeric mode.
+ * A string is encodable iff each character is in the following set: 0 to 9, A to Z
+ * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
+ */
+public:
+ static bool isAlphanumeric(const char *text);
+
+ /*---- Instance fields ----*/
+
+ /* The mode indicator of this segment. Accessed through getMode(). */
+private:
+ const Mode *mode;
+
+ /* The length of this segment's unencoded data. Measured in characters for
+ * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
+ * Always zero or positive. Not the same as the data's bit length.
+ * Accessed through getNumChars(). */
+private:
+ int numChars;
+
+ /* The data bits of this segment. Accessed through getData(). */
+private:
+ std::vector<bool> data;
+
+ /*---- Constructors (low level) ----*/
+
+ /*
+ * Creates a new QR Code segment with the given attributes and data.
+ * The character count (numCh) must agree with the mode and the bit buffer length,
+ * but the constraint isn't checked. The given bit buffer is copied and stored.
+ */
+public:
+ QrSegment(const Mode &md, int numCh, const std::vector<bool> &dt);
+
+ /*
+ * Creates a new QR Code segment with the given parameters and data.
+ * The character count (numCh) must agree with the mode and the bit buffer length,
+ * but the constraint isn't checked. The given bit buffer is moved and stored.
+ */
+public:
+ QrSegment(const Mode &md, int numCh, std::vector<bool> &&dt);
+
+ /*---- Methods ----*/
+
+ /*
+ * Returns the mode field of this segment.
+ */
+public:
+ const Mode &getMode() const;
+
+ /*
+ * Returns the character count field of this segment.
+ */
+public:
+ int getNumChars() const;
+
+ /*
+ * Returns the data bits of this segment.
+ */
+public:
+ const std::vector<bool> &getData() const;
+
+ // (Package-private) Calculates the number of bits needed to encode the given segments at
+ // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a
+ // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX.
+public:
+ static int getTotalBits(const std::vector<QrSegment> &segs, int version);
+
+ /*---- Private constant ----*/
+
+ /* The set of all legal characters in alphanumeric mode, where
+ * each character value maps to the index in the string. */
+private:
+ static const char *ALPHANUMERIC_CHARSET;
+};
+
+/*
+ * A QR Code symbol, which is a type of two-dimension barcode.
+ * Invented by Denso Wave and described in the ISO/IEC 18004 standard.
+ * Instances of this class represent an immutable square grid of dark and light cells.
+ * The class provides static factory functions to create a QR Code from text or binary data.
+ * The class covers the QR Code Model 2 specification, supporting all versions (sizes)
+ * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
+ *
+ * Ways to create a QR Code object:
+ * - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary().
+ * - Mid level: Custom-make the list of segments and call QrCode::encodeSegments().
+ * - Low level: Custom-make the array of data codeword bytes (including
+ * segment headers and final padding, excluding error correction codewords),
+ * supply the appropriate version number, and call the QrCode() constructor.
+ * (Note that all ways require supplying the desired error correction level.)
+ */
+class QrCode final
+{
+ /*---- Public helper enumeration ----*/
+
+ /*
+ * The error correction level in a QR Code symbol.
+ */
+public:
+ enum class Ecc {
+ LOW = 0, // The QR Code can tolerate about 7% erroneous codewords
+ MEDIUM, // The QR Code can tolerate about 15% erroneous codewords
+ QUARTILE, // The QR Code can tolerate about 25% erroneous codewords
+ HIGH, // The QR Code can tolerate about 30% erroneous codewords
+ };
+
+ // Returns a value in the range 0 to 3 (unsigned 2-bit integer).
+private:
+ static int getFormatBits(Ecc ecl);
+
+ /*---- Static factory functions (high level) ----*/
+
+ /*
+ * Returns a QR Code representing the given Unicode text string at the given error correction level.
+ * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer
+ * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible
+ * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than
+ * the ecl argument if it can be done without increasing the version.
+ */
+public:
+ static QrCode encodeText(const char *text, Ecc ecl);
+
+ /*
+ * Returns a QR Code representing the given binary data at the given error correction level.
+ * This function always encodes using the binary segment mode, not any text mode. The maximum number of
+ * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.
+ * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.
+ */
+public:
+ static QrCode encodeBinary(const std::vector<std::uint8_t> &data, Ecc ecl);
+
+ /*---- Static factory functions (mid level) ----*/
+
+ /*
+ * Returns a QR Code representing the given segments with the given encoding parameters.
+ * The smallest possible QR Code version within the given range is automatically
+ * chosen for the output. Iff boostEcl is true, then the ECC level of the result
+ * may be higher than the ecl argument if it can be done without increasing the
+ * version. The mask number is either between 0 to 7 (inclusive) to force that
+ * mask, or -1 to automatically choose an appropriate mask (which may be slow).
+ * This function allows the user to create a custom sequence of segments that switches
+ * between modes (such as alphanumeric and byte) to encode text in less space.
+ * This is a mid-level API; the high-level API is encodeText() and encodeBinary().
+ */
+public:
+ static QrCode encodeSegments(const std::vector<QrSegment> &segs,
+ Ecc ecl,
+ int minVersion = 1,
+ int maxVersion = 40,
+ int mask = -1,
+ bool boostEcl = true); // All optional parameters
+
+ /*---- Instance fields ----*/
+
+ // Immutable scalar parameters:
+
+ /* The version number of this QR Code, which is between 1 and 40 (inclusive).
+ * This determines the size of this barcode. */
+private:
+ int version;
+
+ /* The width and height of this QR Code, measured in modules, between
+ * 21 and 177 (inclusive). This is equal to version * 4 + 17. */
+private:
+ int size;
+
+ /* The error correction level used in this QR Code. */
+private:
+ Ecc errorCorrectionLevel;
+
+ /* The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).
+ * Even if a QR Code is created with automatic masking requested (mask = -1),
+ * the resulting object still has a mask value between 0 and 7. */
+private:
+ int mask;
+
+ // Private grids of modules/pixels, with dimensions of size*size:
+
+ // The modules of this QR Code (false = light, true = dark).
+ // Immutable after constructor finishes. Accessed through getModule().
+private:
+ std::vector<std::vector<bool>> modules;
+
+ // Indicates function modules that are not subjected to masking. Discarded when constructor finishes.
+private:
+ std::vector<std::vector<bool>> isFunction;
+
+ /*---- Constructor (low level) ----*/
+
+ /*
+ * Creates a new QR Code with the given version number,
+ * error correction level, data codeword bytes, and mask number.
+ * This is a low-level API that most users should not use directly.
+ * A mid-level API is the encodeSegments() function.
+ */
+public:
+ QrCode(int ver, Ecc ecl, const std::vector<std::uint8_t> &dataCodewords, int msk);
+
+ /*---- Public instance methods ----*/
+
+ /*
+ * Returns this QR Code's version, in the range [1, 40].
+ */
+public:
+ int getVersion() const;
+
+ /*
+ * Returns this QR Code's size, in the range [21, 177].
+ */
+public:
+ int getSize() const;
+
+ /*
+ * Returns this QR Code's error correction level.
+ */
+public:
+ Ecc getErrorCorrectionLevel() const;
+
+ /*
+ * Returns this QR Code's mask, in the range [0, 7].
+ */
+public:
+ int getMask() const;
+
+ /*
+ * Returns the color of the module (pixel) at the given coordinates, which is false
+ * for light or true for dark. The top left corner has the coordinates (x=0, y=0).
+ * If the given coordinates are out of bounds, then false (light) is returned.
+ */
+public:
+ bool getModule(int x, int y) const;
+
+ /*---- Private helper methods for constructor: Drawing function modules ----*/
+
+ // Reads this object's version field, and draws and marks all function modules.
+private:
+ void drawFunctionPatterns();
+
+ // Draws two copies of the format bits (with its own error correction code)
+ // based on the given mask and this object's error correction level field.
+private:
+ void drawFormatBits(int msk);
+
+ // Draws two copies of the version bits (with its own error correction code),
+ // based on this object's version field, iff 7 <= version <= 40.
+private:
+ void drawVersion();
+
+ // Draws a 9*9 finder pattern including the border separator,
+ // with the center module at (x, y). Modules can be out of bounds.
+private:
+ void drawFinderPattern(int x, int y);
+
+ // Draws a 5*5 alignment pattern, with the center module
+ // at (x, y). All modules must be in bounds.
+private:
+ void drawAlignmentPattern(int x, int y);
+
+ // Sets the color of a module and marks it as a function module.
+ // Only used by the constructor. Coordinates must be in bounds.
+private:
+ void setFunctionModule(int x, int y, bool isDark);
+
+ // Returns the color of the module at the given coordinates, which must be in range.
+private:
+ bool module(int x, int y) const;
+
+ /*---- Private helper methods for constructor: Codewords and masking ----*/
+
+ // Returns a new byte string representing the given data with the appropriate error correction
+ // codewords appended to it, based on this object's version and error correction level.
+private:
+ std::vector<std::uint8_t> addEccAndInterleave(const std::vector<std::uint8_t> &data) const;
+
+ // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
+ // data area of this QR Code. Function modules need to be marked off before this is called.
+private:
+ void drawCodewords(const std::vector<std::uint8_t> &data);
+
+ // XORs the codeword modules in this QR Code with the given mask pattern.
+ // The function modules must be marked and the codeword bits must be drawn
+ // before masking. Due to the arithmetic of XOR, calling applyMask() with
+ // the same mask value a second time will undo the mask. A final well-formed
+ // QR Code needs exactly one (not zero, two, etc.) mask applied.
+private:
+ void applyMask(int msk);
+
+ // Calculates and returns the penalty score based on state of this QR Code's current modules.
+ // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
+private:
+ long getPenaltyScore() const;
+
+ /*---- Private helper functions ----*/
+
+ // Returns an ascending list of positions of alignment patterns for this version number.
+ // Each position is in the range [0,177), and are used on both the x and y axes.
+ // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes.
+private:
+ std::vector<int> getAlignmentPatternPositions() const;
+
+ // Returns the number of data bits that can be stored in a QR Code of the given version number, after
+ // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
+ // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.
+private:
+ static int getNumRawDataModules(int ver);
+
+ // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any
+ // QR Code of the given version number and error correction level, with remainder bits discarded.
+ // This stateless pure function could be implemented as a (40*4)-cell lookup table.
+private:
+ static int getNumDataCodewords(int ver, Ecc ecl);
+
+ // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be
+ // implemented as a lookup table over all possible parameter values, instead of as an algorithm.
+private:
+ static std::vector<std::uint8_t> reedSolomonComputeDivisor(int degree);
+
+ // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.
+private:
+ static std::vector<std::uint8_t> reedSolomonComputeRemainder(
+ const std::vector<std::uint8_t> &data, const std::vector<std::uint8_t> &divisor);
+
+ // Returns the product of the two given field elements modulo GF(2^8/0x11D).
+ // All inputs are valid. This could be implemented as a 256*256 lookup table.
+private:
+ static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y);
+
+ // Can only be called immediately after a light run is added, and
+ // returns either 0, 1, or 2. A helper function for getPenaltyScore().
+private:
+ int finderPenaltyCountPatterns(const std::array<int, 7> &runHistory) const;
+
+ // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
+private:
+ int finderPenaltyTerminateAndCount(bool currentRunColor,
+ int currentRunLength,
+ std::array<int, 7> &runHistory) const;
+
+ // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
+private:
+ void finderPenaltyAddHistory(int currentRunLength, std::array<int, 7> &runHistory) const;
+
+ // Returns true iff the i'th bit of x is set to 1.
+private:
+ static bool getBit(long x, int i);
+
+ /*---- Constants and tables ----*/
+
+ // The minimum version number supported in the QR Code Model 2 standard.
+public:
+ static constexpr int MIN_VERSION = 1;
+
+ // The maximum version number supported in the QR Code Model 2 standard.
+public:
+ static constexpr int MAX_VERSION = 40;
+
+ // For use in getPenaltyScore(), when evaluating which mask is best.
+private:
+ static const int PENALTY_N1;
+
+private:
+ static const int PENALTY_N2;
+
+private:
+ static const int PENALTY_N3;
+
+private:
+ static const int PENALTY_N4;
+
+private:
+ static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41];
+
+private:
+ static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41];
+};
+
+/*---- Public exception class ----*/
+
+/*
+ * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include:
+ * - Decrease the error correction level if it was greater than Ecc::LOW.
+ * - If the encodeSegments() function was called with a maxVersion argument, then increase
+ * it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other
+ * factory functions because they search all versions up to QrCode::MAX_VERSION.)
+ * - Split the text data into better or optimal segments in order to reduce the number of bits required.
+ * - Change the text or binary data to be shorter.
+ * - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric).
+ * - Propagate the error upward to the caller/user.
+ */
+class data_too_long : public std::length_error
+{
+public:
+ explicit data_too_long(const std::string &msg);
+};
+
+/*
+ * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment.
+ */
+class BitBuffer final : public std::vector<bool>
+{
+ /*---- Constructor ----*/
+
+ // Creates an empty bit buffer (length 0).
+public:
+ BitBuffer();
+
+ /*---- Method ----*/
+
+ // Appends the given number of low-order bits of the given value
+ // to this buffer. Requires 0 <= len <= 31 and val < 2^len.
+public:
+ void appendBits(std::uint32_t val, int len);
+};
+
+} // namespace qrcodegen
diff --git a/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp
new file mode 100644
index 0000000000..b889b67760
--- /dev/null
+++ b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.cpp
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** This file is part of the Custom Merge QtDesignStudio plugin.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://www.qt.io/contact-us
+**
+******************************************************************************/
+
+#include "qrcodeimageprovider.h"
+#include "qrcodegen.h"
+#include <QPainter>
+#include <QSvgRenderer>
+
+QrCodeImageProvider::QrCodeImageProvider()
+ : QQuickImageProvider(QQuickImageProvider::Pixmap)
+{
+}
+
+
+static QString qrToSvgString(const qrcodegen::QrCode &qr, int border) {
+ if (border < 0)
+ throw std::domain_error("Border must be non-negative");
+ if (border > INT_MAX / 2 || border * 2 > INT_MAX - qr.getSize())
+ throw std::overflow_error("Border too large");
+
+ QString svgString;
+ QTextStream stream(&svgString);
+
+ stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ stream << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
+ stream << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 ";
+ stream << (qr.getSize() + border * 2) << " " << (qr.getSize() + border * 2) << "\" stroke=\"none\">\n";
+ stream << "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n";
+ stream << "\t<path d=\"";
+ for (int y = 0; y < qr.getSize(); y++) {
+ for (int x = 0; x < qr.getSize(); x++) {
+ if (qr.getModule(x, y)) {
+ if (x != 0 || y != 0)
+ stream << " ";
+ stream << "M" << (x + border) << "," << (y + border) << "h1v1h-1z";
+ }
+ }
+ }
+ stream << "\" fill=\"#000000\"/>\n";
+ stream << "</svg>\n";
+
+ return svgString;
+
+}
+
+QPixmap QrCodeImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
+{
+ int width = 1000;
+ int height = 1000;
+
+ const qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(id.toLatin1(), qrcodegen::QrCode::Ecc::LOW);
+
+ QString svgString = qrToSvgString(qr, 3);
+ QSvgRenderer svgRenderer(svgString.toLatin1());
+
+
+ if (size)
+ *size = QSize(width, height);
+
+ QPixmap pixmap(requestedSize.width() > 0 ? requestedSize.width() : width,
+ requestedSize.height() > 0 ? requestedSize.height() : height);
+
+ QPainter painter(&pixmap);
+
+ svgRenderer.render(&painter);
+
+ return pixmap;
+
+}
+
+
diff --git a/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h
new file mode 100644
index 0000000000..1516d214bd
--- /dev/null
+++ b/src/libs/3rdparty/qrcodegen/src/qrcodeimageprovider.h
@@ -0,0 +1,36 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** This file is part of the Custom Merge QtDesignStudio plugin.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://www.qt.io/contact-us
+**
+******************************************************************************/
+
+#ifndef QRCODEIMAGEPROVIDER_H
+#define QRCODEIMAGEPROVIDER_H
+
+#include <QPixmap>
+#include <QQuickImageProvider>
+
+class QrCodeImageProvider : public QQuickImageProvider
+{
+public:
+ QrCodeImageProvider();
+
+ QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);
+};
+
+#endif // QRCODEIMAGEPROVIDER_H
diff --git a/src/libs/3rdparty/qtkeychain/CMakeLists.txt b/src/libs/3rdparty/qtkeychain/CMakeLists.txt
new file mode 100644
index 0000000000..7b2cc6d88f
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/CMakeLists.txt
@@ -0,0 +1,67 @@
+add_qtc_library(qtkeychain
+ DEPENDS Qt::Core
+ SOURCES
+ keychain.cpp keychain.h
+ qkeychain_export.h
+ PROPERTIES
+ QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
+)
+
+if (WIN32)
+ option(USE_CREDENTIAL_STORE "Build with windows CredentialStore support" ON)
+
+ extend_qtc_library(qtkeychain SOURCES keychain_win.cpp)
+
+ extend_qtc_library(qtkeychain
+ CONDITION USE_CREDENTIAL_STORE
+ FEATURE_INFO "CredentialStore keychain support"
+ DEFINES USE_CREDENTIAL_STORE=1
+ )
+ extend_qtc_library(qtkeychain
+ CONDITION NOT USE_CREDENTIAL_STORE
+ SOURCES plaintextstore.cpp
+ DEPENDS crypt32
+ )
+endif()
+
+extend_qtc_library(qtkeychain
+ CONDITION APPLE
+ SOURCES keychain_apple.mm
+ DEPENDS ${FWFoundation} ${FWSecurity}
+)
+
+if (UNIX AND NOT APPLE)
+ find_package(Qt6 COMPONENTS DBus)
+
+ option(LIBSECRET_SUPPORT "Build with libsecret support if available" ON)
+ if (LIBSECRET_SUPPORT)
+ find_package(PkgConfig)
+
+ include(FindPkgConfig)
+ pkg_check_modules(LIBSECRET libsecret-1)
+
+ extend_qtc_library(qtkeychain
+ CONDITION LIBSECRET_FOUND
+ FEATURE_INFO "libsecret keychain support"
+ DEFINES HAVE_LIBSECRET=1
+ INCLUDES ${LIBSECRET_INCLUDE_DIRS}
+ DEPENDS ${LIBSECRET_LIBRARIES}
+ )
+ endif()
+
+ qt6_add_dbus_interface(dbus_SOURCES
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface)
+
+ extend_qtc_library(qtkeychain
+ CONDITION TARGET Qt::DBus
+ FEATURE_INFO "keychain dbus support"
+ DEFINES KEYCHAIN_DBUS=1
+ DEPENDS Qt::DBus
+ SOURCES
+ gnomekeyring.cpp
+ keychain_unix.cpp
+ libsecret.cpp
+ plaintextstore.cpp
+ ${dbus_SOURCES}
+ )
+endif()
diff --git a/src/libs/3rdparty/qtkeychain/CMakeLists.txt.upstream b/src/libs/3rdparty/qtkeychain/CMakeLists.txt.upstream
new file mode 100644
index 0000000000..3b4e30d129
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/CMakeLists.txt.upstream
@@ -0,0 +1,198 @@
+set(QTKEYCHAIN_VERSION 0.14.99)
+set(QTKEYCHAIN_SOVERSION 1)
+
+project(qtkeychain VERSION ${QTKEYCHAIN_VERSION} LANGUAGES CXX)
+
+include(FindPkgConfig)
+
+###
+
+set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules")
+include(GNUInstallDirs)
+include(GenerateExportHeader)
+include(CMakePackageConfigHelpers)
+include(ECMSetupVersion)
+include(CMakeDependentOption)
+
+option(BUILD_TRANSLATIONS "Build translations" ON)
+option(BUILD_SHARED_LIBS "Build dynamic library" OFF)
+
+CMAKE_DEPENDENT_OPTION(BUILD_TRANSLATIONS_AS_RESOURCES "Bundle translations with the library" OFF
+ "BUILD_TRANSLATIONS" OFF)
+
+if (WIN32)
+ option(USE_CREDENTIAL_STORE "Build with windows CredentialStore support" ON)
+
+ if (USE_CREDENTIAL_STORE)
+ add_definitions(-DUSE_CREDENTIAL_STORE=1)
+ endif()
+endif()
+
+
+find_package(Qt6 COMPONENTS Core REQUIRED)
+
+if(UNIX AND NOT APPLE)
+ find_package(Qt6 COMPONENTS DBus REQUIRED)
+ include_directories(${Qt6DBus_INCLUDE_DIRS})
+ set(QTDBUS_LIBRARIES ${Qt6DBus_LIBRARIES})
+endif()
+
+if(BUILD_TRANSLATIONS)
+ find_package(Qt6 COMPONENTS LinguistTools REQUIRED)
+endif()
+
+set(QTCORE_LIBRARIES ${Qt6Core_LIBRARIES})
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+list(APPEND qtkeychain_LIBRARIES ${QTCORE_LIBRARIES})
+set(qtkeychain_SOURCES
+ keychain.cpp
+ qkeychain_export.h
+ keychain.h
+)
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
+ # CMake < 3.15 sneaks in /W# flags for us, so we need a replacement,
+ # or we'll get a warning (cf. CMP0092)
+ if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
+ string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
+ endif()
+else()
+ # MSVC's STL / Qt headers are not MSVC -Wall clean, so don't enable it there
+ add_definitions( -Wall -Werror=return-type )
+endif()
+
+if(WIN32)
+ list(APPEND qtkeychain_SOURCES keychain_win.cpp)
+ if (NOT USE_CREDENTIAL_STORE)
+ list(APPEND qtkeychain_LIBRARIES crypt32)
+ list(APPEND qtkeychain_SOURCES plaintextstore.cpp)
+ endif()
+ #FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there
+ if(MINGW)
+ add_definitions( -O2 )
+ endif()
+endif()
+
+if(APPLE)
+ list(APPEND qtkeychain_SOURCES keychain_apple.mm)
+ list(APPEND qtkeychain_LIBRARIES "-framework Foundation" "-framework Security")
+endif()
+
+if(UNIX AND NOT APPLE)
+ option(LIBSECRET_SUPPORT "Build with libsecret support if available" ON)
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(LIBSECRET libsecret-1)
+
+ if(LIBSECRET_SUPPORT AND LIBSECRET_FOUND)
+ add_definitions(-DHAVE_LIBSECRET=1)
+ INCLUDE_DIRECTORIES(${LIBSECRET_INCLUDE_DIRS})
+ LINK_DIRECTORIES(${LIBSECRET_LIBRARY_DIRS})
+ list(APPEND qtkeychain_LIBRARIES_PRIVATE ${LIBSECRET_LIBRARIES})
+ endif()
+
+ add_definitions(-DKEYCHAIN_DBUS=1)
+ list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.cpp libsecret.cpp plaintextstore.cpp)
+ qt6_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface)
+ list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES} )
+endif()
+
+qt6_wrap_cpp(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h gnomekeyring_p.h)
+
+set(qtkeychain_TR_FILES
+ translations/qtkeychain_de.ts
+ translations/qtkeychain_fr.ts
+ translations/qtkeychain_ro.ts
+ translations/qtkeychain_ru.ts
+ translations/qtkeychain_zh.ts
+)
+
+set(QTKEYCHAIN_TARGET_NAME qtkeychain)
+add_library(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES})
+if(WIN32)
+ set_target_properties( ${QTKEYCHAIN_TARGET_NAME} PROPERTIES DEBUG_POSTFIX "d" )
+endif()
+
+file(GLOB qtkeychain_TR_SOURCES *.cpp *.h *.ui)
+if ( BUILD_TRANSLATIONS )
+ qt6_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES})
+ qt6_add_translation(qtkeychain_QM_FILES ${qtkeychain_TR_FILES})
+ add_custom_target(messages DEPENDS ${qtkeychain_MESSAGES})
+ add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES} messages)
+ # https://github.com/frankosterfeld/qtkeychain/issues/185
+ add_dependencies(${QTKEYCHAIN_TARGET_NAME} translations)
+
+ if (BUILD_TRANSLATIONS_AS_RESOURCES)
+ set(QM_FILE_LIST "")
+ foreach(FILE ${qtkeychain_QM_FILES})
+ list(APPEND QM_FILE_LIST "<file>${FILE}</file>")
+ endforeach()
+ string(REPLACE ";" "" QM_FILE_LIST ${QM_FILE_LIST})
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/translations/translations.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
+ target_sources(${QTKEYCHAIN_TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
+ else()
+ set(QTKEYCHAIN_TRANSLATIONS_DIR
+ ${CMAKE_INSTALL_DATADIR}/qtkeychain/translations
+ CACHE PATH "The location of the QtKeychain translations" )
+ install(FILES ${qtkeychain_QM_FILES} DESTINATION ${QTKEYCHAIN_TRANSLATIONS_DIR})
+ endif()
+endif( BUILD_TRANSLATIONS )
+
+target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES} PRIVATE ${qtkeychain_LIBRARIES_PRIVATE})
+if(NOT INTERFACE_INCLUDE_SUFFIX)
+ set(INTERFACE_INCLUDE_SUFFIX include)
+endif()
+target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $<INSTALL_INTERFACE:${INTERFACE_INCLUDE_SUFFIX}/>)
+
+generate_export_header(${QTKEYCHAIN_TARGET_NAME}
+ EXPORT_FILE_NAME qkeychain_export.h
+ EXPORT_MACRO_NAME QKEYCHAIN_EXPORT
+)
+
+set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES
+ VERSION ${QTKEYCHAIN_VERSION}
+ SOVERSION ${QTKEYCHAIN_SOVERSION}
+ INSTALL_RPATH_USE_LINK_PATH TRUE
+)
+
+if (NOT APPLE)
+ set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES
+ INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}"
+ )
+endif()
+
+install(FILES keychain.h ${CMAKE_CURRENT_BINARY_DIR}/qkeychain_export.h
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qtkeychain/
+)
+
+install(TARGETS ${QTKEYCHAIN_TARGET_NAME}
+ EXPORT QtKeychainLibraryDepends
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+###
+### CMake config file
+###
+
+configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/QtKeychainConfig.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfig.cmake"
+ INSTALL_DESTINATION QtKeychain)
+
+ecm_setup_version("${QTKEYCHAIN_VERSION}" VARIABLE_PREFIX SNORE
+ PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfigVersion.cmake"
+ SOVERSION ${QTKEYCHAIN_VERSION})
+
+install(EXPORT QtKeychainLibraryDepends
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/QtKeychain"
+)
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfig.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/QtKeychainConfigVersion.cmake
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/QtKeychain
+)
+
diff --git a/src/libs/3rdparty/qtkeychain/COPYING b/src/libs/3rdparty/qtkeychain/COPYING
new file mode 100644
index 0000000000..69f70fff71
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/COPYING
@@ -0,0 +1,23 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/libs/3rdparty/qtkeychain/ChangeLog b/src/libs/3rdparty/qtkeychain/ChangeLog
new file mode 100644
index 0000000000..cb383c5296
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/ChangeLog
@@ -0,0 +1,109 @@
+ChangeLog
+=========
+
+version 0.14.1 (release 2023-06-01)
+
+ - Export QKeychain::isAvailable() to make it usable in a shared build (Volker Krause <vkrause@kde.org>)
+ - Protect against creating the QtKeychain::QtKeychain alias target twice (Volker Krause <vkrause@kde.org>)
+
+version 0.14.0 (release 2023-05-12)
+
+ - Add Qt 6 Android support (Igor Bugaev <freedbrt@gmail.com>)
+ - Add QtQuick client example ((Igor Bugaev <freedbrt@gmail.com>)
+ - Added Dutch translation (Heimen Stoffels <vistausss@fastmail.com>)
+ - Fix potential freezing with Apple keychain (Claudio Cambra <developer@claudiocambra.com>)
+ - Add API to check whether a secure backend is available at all (Volker Krause <vkrause@kde.org>)
+
+version 0.13.2 (release 2021-11-18)
+
+ - CMake: Deprecate QTKEYCHAIN_STATIC in favor of BUILD_SHARED_LIBS (be@mixxx.org)
+
+version 0.13.1 (release 2021-11-08)
+
+ - KWallet: Fix deletion of entries (Issue #199)
+
+version 0.13.0 (release 2021-11-07)
+
+ - Linux: Require libsecret if not explicitly disabled
+ - Unify implementations for macOS and iOS
+ - CMake: lots of fixes
+
+version 0.12.0 (release 2020-12-16)
+
+ * Add Qt 6 support, drop Qt 4 support
+ * Require C++11
+ * Add Android support (Mathias Hasselmann)
+
+version 0.11.1 (release 2020-09-08)
+
+ * Build system fixes
+
+version 0.11.0 (release 2020-09-08)
+
+ * Important: Debug builds on Windows now get the "d" suffix
+ * Various build system fixes
+ * Add Haiku support (François Revol <revol@free.fr>)
+ * Translation: Russian (Alexander Gorishnyak <kefir500@gmail.com>)
+ * Translation: Update French (David Geiger <david.david@mageialinux-online.org>)
+
+version 0.10.0 (release 2019-12-17)
+
+ * Detect XFCE desktop correctly. (Sandro Knauß <hefee@debian.org>)
+ * Windows Use CRED_PERSIST_ENTERPRISE (Olivier Goffart <ogoffart@woboq.com>)
+ * Windows: Improve CredWrite() error handling (Christian Kamm <mail@ckamm.de>)
+ * Fix build with Qt 5.12.x (Sergey Ilinykh <rion4ik@gmail.com>)
+ * Fix Qt 4 build (Robert-André Mauchin <zebob.m@gmail.com>)
+ * Translation: Mandarin (Taiwan) (Poren Chiang <ren.chiang@gmail.com>)
+ * Translation: French (François Revol <revol@free.fr>)
+
+version 0.9.1 (release 2018-08-20)
+ * Windows Credential Store: Use CRED_PERSIST_ENTERPRISE (Olivier Goffart <ogoffart@woboq.com>)
+ * Secret: Don't match the schema name #114 (Christian Kamm <mail@ckamm.de>)
+ * Fix qmake build on Windows (Alexander Gorishnyak <kefir500@gmail.com>)
+
+version 0.9.0 (release 2018-07-13)
+ * Fall back on libsecret if kwallet is not available (Christian Kamm <mail@ckamm.de>)
+ * Only require QtLinguist if building translations (Victor Kropp <victor.kropp@jetbrains.com>)
+ * Fix building on Windows without credential store (Dmitry Ivanov <dm.vl.ivanov@gmail.com>)
+ * Fix Qt 4 build (Sandro Knauß <hefee@debian.org>)
+ * Make build of test application optional (Boris Pek <tehnick-8@yandex.ru>)
+
+version 0.8.0 (release 2017-04-19)
+ * Buildsystem improvements (Kristofer Tingdahl <kristofer.tingdahl@dgbes.com>, Hannah von Reth <hannah.vonreth@kdab.com>, Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>)
+ * Enable C++11 support for Qt >= 5.7 (Dmitry Ivanov <dm.vl.ivanov@gmail.com>)
+ * Doxygen documentation ( Elvis Angelaccio <elvis.angelaccio@kdemail.net>)
+ * Libsecret support (Armin Novak <armin.novak@thincast.com>)
+ * iOS support (Mathias Hasselmann <mathias.hasselmann@kdab.com>)
+
+version 0.7.0 (release 2016-05-23)
+ * Bump SO version due to 0.6 being binary-incompatible to previous releases
+
+version 0.6.2 (release 2016-04-04)
+ * KWallet: Fixes a crash when storing passwords, seen on Debian/KDE4
+
+version 0.6.1 (release 2016-03-31)
+ * Fix KWallet not working (regressions in 0.6.0)
+
+version 0.6.0 (release 2016-03-18)
+ * Added support for the Windows Credential Store
+
+version 0.5.0 (release 2015-05-04)
+ * Added support for KWallet5 (KDE5/KF)
+
+version 0.4.0 (release 2014-09-01)
+ * KWallet: Handle case where no wallet exists yet (Liviu Cristian Mirea Ghiban <contact@liviucmg.com>)
+ * Improved desktop environment detection at runtime (Daniel Molkentin <daniel@molkentin.de>)
+
+version 0.3.0 (release 2014-03-13)
+ * Gnome Keyring supported added (Francois Ferrand <thetypz@gmail.com>)
+ * Improved Qt 5 support
+ * KWallet: Distinguish empty passwords from non-existing entries
+ * KWallet: Do not use hardcoded wallet name
+ * German translation (Daniel Molkentin <daniel@molkentin.de>)
+ * Romanian translation (Arthur Țițeică <arthur@psw.ro>)
+
+version 0.2.0: no official release
+
+version 0.1.0 (release 2013-01-16)
+ * Initial release
+
diff --git a/src/libs/3rdparty/qtkeychain/QtKeychainConfig.cmake.in b/src/libs/3rdparty/qtkeychain/QtKeychainConfig.cmake.in
new file mode 100644
index 0000000000..084d45e57f
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/QtKeychainConfig.cmake.in
@@ -0,0 +1,28 @@
+# - Config file for the QtKeychain package
+# It defines the following variables
+# QTKEYCHAIN_INCLUDE_DIRS - include directories for QtKeychain
+# QTKEYCHAIN_LIBRARIES - libraries to link against
+# as well as the following imported targets
+# qt5keychain / qt6keychain
+# Qt5Keychain::Qt5Keychain / Qt6Keychain::Qt6Keychain
+
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/QtKeychainLibraryDepends.cmake")
+
+include(CMakeFindDependencyMacro)
+
+find_dependency(Qt6Core)
+
+if(UNIX AND NOT APPLE AND NOT ANDROID)
+ find_dependency(Qt6DBus)
+endif()
+
+set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@")
+get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES)
+
+if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0 AND NOT TARGET QtKeychain::QtKeychain)
+ add_library(QtKeychain::QtKeychain ALIAS qtkeychain)
+endif()
+
+check_required_components(QtKeychain)
diff --git a/src/libs/3rdparty/qtkeychain/ReadMe.md b/src/libs/3rdparty/qtkeychain/ReadMe.md
new file mode 100644
index 0000000000..cf0a2e1311
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/ReadMe.md
@@ -0,0 +1,29 @@
+QtKeychain
+==========
+
+QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform:
+
+ * **macOS:** Passwords are stored in the macOS Keychain.
+
+ * **Linux/Unix:** If running, GNOME Keyring is used, otherwise QtKeychain tries to use KWallet (via D-Bus), if available. Libsecret (common API for desktop-specific solutions)
+ is also supported.
+
+ * **Windows:** By default, the Windows Credential Store is used (requires Windows 7 or newer).
+Pass `-DUSE_CREDENTIAL_STORE=OFF` to cmake to disable it. If disabled, QtKeychain uses the Windows API function
+[CryptProtectData](http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261%28v=vs.85%29.aspx "CryptProtectData function")
+to encrypt the password with the user's logon credentials. The encrypted data is then persisted via QSettings.
+
+ * **Android and iOS:** Passwords are stored in the Android keystore system and iOS keychain, respectively.
+
+In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (`setInsecureFallback( true )`).
+
+
+Requirements
+------------
+
+QtKeychain 0.12 and newer supports Qt 5 and Qt 6 and requires a compiler with C++11 support. Older versions support Qt 4 and Qt 5.
+
+License
+-------
+
+QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details.
diff --git a/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake
new file mode 100644
index 0000000000..8d48772b70
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake
@@ -0,0 +1,202 @@
+#.rst:
+# ECMPackageConfigHelpers
+# -----------------------
+#
+# Helper macros for generating CMake package config files.
+#
+# ``write_basic_package_version_file()`` is the same as the one provided by the
+# `CMakePackageConfigHelpers
+# <https://www.cmake.org/cmake/help/v2.8.12/cmake.html#module:CMakePackageConfigHelpers>`_
+# module in CMake; see that module's documentation for
+# more information.
+#
+# ::
+#
+# ecm_configure_package_config_file(<input> <output>
+# INSTALL_DESTINATION <path>
+# [PATH_VARS <var1> [<var2> [...]]
+# [NO_SET_AND_CHECK_MACRO]
+# [NO_CHECK_REQUIRED_COMPONENTS_MACRO])
+#
+#
+# This behaves in the same way as configure_package_config_file() from CMake
+# 2.8.12, except that it adds an extra helper macro: find_dependency(). It is
+# highly recommended that you read the `documentation for
+# CMakePackageConfigHelpers
+# <https://www.cmake.org/cmake/help/v2.8.12/cmake.html#module:CMakePackageConfigHelpers>`_
+# for more information, particularly with regard to the PATH_VARS argument.
+#
+# Note that there is no argument that will disable the find_dependency() macro;
+# if you do not require this macro, you should use
+# ``configure_package_config_file`` from the CMakePackageConfigHelpers module.
+#
+# CMake 3.0 includes a CMakeFindDependencyMacro module that provides the
+# find_dependency() macro (which you can ``include()`` in your package config
+# file), so this file is only useful for projects wishing to provide config
+# files that will work with CMake 2.8.12.
+#
+# Additional Config File Macros
+# =============================
+#
+# ::
+#
+# find_dependency(<dep> [<version> [EXACT]])
+#
+# find_dependency() should be used instead of find_package() to find package
+# dependencies. It forwards the correct parameters for EXACT, QUIET and
+# REQUIRED which were passed to the original find_package() call. It also sets
+# an informative diagnostic message if the dependency could not be found.
+#
+# Since pre-1.0.0.
+
+#=============================================================================
+# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kdemail.net>
+# SPDX-FileCopyrightText: 2013 Stephen Kelly <steveire@gmail.com>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+
+include(${CMAKE_ROOT}/Modules/CMakePackageConfigHelpers.cmake)
+
+set(_ecm_package_config_helpers_included TRUE)
+
+if(NOT CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.13)
+ message(AUTHOR_WARNING "Your project already requires a version of CMake that includes the find_dependency macro via the CMakeFindDependencyMacro module. You should use CMakePackageConfigHelpers instead of ECMPackageConfigHelpers.")
+endif()
+
+function(ECM_CONFIGURE_PACKAGE_CONFIG_FILE _inputFile _outputFile)
+ set(options NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO)
+ set(oneValueArgs INSTALL_DESTINATION )
+ set(multiValueArgs PATH_VARS )
+
+ cmake_parse_arguments(CCF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(CCF_UNPARSED_ARGUMENTS)
+ message(FATAL_ERROR "Unknown keywords given to CONFIGURE_PACKAGE_CONFIG_FILE(): \"${CCF_UNPARSED_ARGUMENTS}\"")
+ endif()
+
+ if(NOT CCF_INSTALL_DESTINATION)
+ message(FATAL_ERROR "No INSTALL_DESTINATION given to CONFIGURE_PACKAGE_CONFIG_FILE()")
+ endif()
+
+ if(IS_ABSOLUTE "${CCF_INSTALL_DESTINATION}")
+ set(absInstallDir "${CCF_INSTALL_DESTINATION}")
+ else()
+ set(absInstallDir "${CMAKE_INSTALL_PREFIX}/${CCF_INSTALL_DESTINATION}")
+ endif()
+
+ file(RELATIVE_PATH PACKAGE_RELATIVE_PATH "${absInstallDir}" "${CMAKE_INSTALL_PREFIX}" )
+
+ foreach(var ${CCF_PATH_VARS})
+ if(NOT DEFINED ${var})
+ message(FATAL_ERROR "Variable ${var} does not exist")
+ else()
+ if(IS_ABSOLUTE "${${var}}")
+ string(REPLACE "${CMAKE_INSTALL_PREFIX}" "\${PACKAGE_PREFIX_DIR}"
+ PACKAGE_${var} "${${var}}")
+ else()
+ set(PACKAGE_${var} "\${PACKAGE_PREFIX_DIR}/${${var}}")
+ endif()
+ endif()
+ endforeach()
+
+ get_filename_component(inputFileName "${_inputFile}" NAME)
+
+ set(PACKAGE_INIT "
+####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() (ECM variant) #######
+####### Any changes to this file will be overwritten by the next CMake run #######
+####### The input file was ${inputFileName} #######
+
+get_filename_component(PACKAGE_PREFIX_DIR \"\${CMAKE_CURRENT_LIST_DIR}/${PACKAGE_RELATIVE_PATH}\" ABSOLUTE)
+")
+
+ if("${absInstallDir}" MATCHES "^(/usr)?/lib(64)?/.+")
+ # Handle "/usr move" symlinks created by some Linux distros.
+ set(PACKAGE_INIT "${PACKAGE_INIT}
+# Use original install prefix when loaded through a \"/usr move\"
+# cross-prefix symbolic link such as /lib -> /usr/lib.
+get_filename_component(_realCurr \"\${CMAKE_CURRENT_LIST_DIR}\" REALPATH)
+get_filename_component(_realOrig \"${absInstallDir}\" REALPATH)
+if(_realCurr STREQUAL _realOrig)
+ set(PACKAGE_PREFIX_DIR \"${CMAKE_INSTALL_PREFIX}\")
+endif()
+unset(_realOrig)
+unset(_realCurr)
+")
+ endif()
+
+ if(NOT CCF_NO_SET_AND_CHECK_MACRO)
+ set(PACKAGE_INIT "${PACKAGE_INIT}
+macro(set_and_check _var _file)
+ set(\${_var} \"\${_file}\")
+ if(NOT EXISTS \"\${_file}\")
+ message(FATAL_ERROR \"File or directory \${_file} referenced by variable \${_var} does not exist !\")
+ endif()
+endmacro()
+
+include(CMakeFindDependencyMacro OPTIONAL RESULT_VARIABLE _CMakeFindDependencyMacro_FOUND)
+
+if (NOT _CMakeFindDependencyMacro_FOUND)
+ macro(find_dependency dep)
+ if (NOT \${dep}_FOUND)
+
+ set(ecm_fd_version)
+ if (\${ARGC} GREATER 1)
+ set(ecm_fd_version \${ARGV1})
+ endif()
+ set(ecm_fd_exact_arg)
+ if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION_EXACT)
+ set(ecm_fd_exact_arg EXACT)
+ endif()
+ set(ecm_fd_quiet_arg)
+ if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
+ set(ecm_fd_quiet_arg QUIET)
+ endif()
+ set(ecm_fd_required_arg)
+ if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
+ set(ecm_fd_required_arg REQUIRED)
+ endif()
+
+ find_package(\${dep} \${ecm_fd_version}
+ \${ecm_fd_exact_arg}
+ \${ecm_fd_quiet_arg}
+ \${ecm_fd_required_arg}
+ )
+
+ if (NOT \${dep}_FOUND)
+ set(\${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE \"\${CMAKE_FIND_PACKAGE_NAME} could not be found because dependency \${dep} could not be found.\")
+ set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND False)
+ return()
+ endif()
+
+ set(ecm_fd_version)
+ set(ecm_fd_required_arg)
+ set(ecm_fd_quiet_arg)
+ set(ecm_fd_exact_arg)
+ endif()
+ endmacro()
+endif()
+
+")
+ endif()
+
+
+ if(NOT CCF_NO_CHECK_REQUIRED_COMPONENTS_MACRO)
+ set(PACKAGE_INIT "${PACKAGE_INIT}
+macro(check_required_components _NAME)
+ foreach(comp \${\${_NAME}_FIND_COMPONENTS})
+ if(NOT \${_NAME}_\${comp}_FOUND)
+ if(\${_NAME}_FIND_REQUIRED_\${comp})
+ set(\${_NAME}_FOUND FALSE)
+ endif()
+ endif()
+ endforeach()
+endmacro()
+")
+ endif()
+
+ set(PACKAGE_INIT "${PACKAGE_INIT}
+####################################################################################")
+
+ configure_file("${_inputFile}" "${_outputFile}" @ONLY)
+
+endfunction()
diff --git a/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMQueryQt.cmake b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMQueryQt.cmake
new file mode 100644
index 0000000000..98eb50089e
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMQueryQt.cmake
@@ -0,0 +1,100 @@
+# SPDX-FileCopyrightText: 2014 Rohan Garg <rohan16garg@gmail.com>
+# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
+# SPDX-FileCopyrightText: 2014-2016 Aleix Pol <aleixpol@kde.org>
+# SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau <kossebau@kde.org>
+# SPDX-FileCopyrightText: 2022 Ahmad Samir <a.samir78@gmail.com>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#[=======================================================================[.rst:
+ECMQueryQt
+---------------
+This module can be used to query the installation paths used by Qt.
+
+For Qt5 this uses ``qmake``, and for Qt6 this used ``qtpaths`` (the latter has built-in
+support to query the paths of a target platform when cross-compiling).
+
+This module defines the following function:
+::
+
+ ecm_query_qt(<result_variable> <qt_variable> [TRY])
+
+Passing ``TRY`` will result in the method not making the build fail if the executable
+used for querying has not been found, but instead simply print a warning message and
+return an empty string.
+
+Example usage:
+
+.. code-block:: cmake
+
+ include(ECMQueryQt)
+ ecm_query_qt(bin_dir QT_INSTALL_BINS)
+
+If the call succeeds ``${bin_dir}`` will be set to ``<prefix>/path/to/bin/dir`` (e.g.
+``/usr/lib64/qt/bin/``).
+
+Since: 5.93
+#]=======================================================================]
+
+include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
+include(CheckLanguage)
+check_language(CXX)
+if (CMAKE_CXX_COMPILER)
+ # Enable the CXX language to let CMake look for config files in library dirs.
+ # See: https://gitlab.kitware.com/cmake/cmake/-/issues/23266
+ enable_language(CXX)
+endif()
+
+if (QT_MAJOR_VERSION STREQUAL "5")
+ # QUIET to accommodate the TRY option
+ find_package(Qt${QT_MAJOR_VERSION}Core QUIET)
+ if(TARGET Qt5::qmake)
+ get_target_property(_qmake_executable_default Qt5::qmake LOCATION)
+
+ set(QUERY_EXECUTABLE ${_qmake_executable_default}
+ CACHE FILEPATH "Location of the Qt5 qmake executable")
+ set(_exec_name_text "Qt5 qmake")
+ set(_cli_option "-query")
+ endif()
+elseif(QT_MAJOR_VERSION STREQUAL "6")
+ # QUIET to accommodate the TRY option
+ find_package(Qt6 COMPONENTS CoreTools QUIET CONFIG)
+ if (TARGET Qt6::qtpaths)
+ get_target_property(_qtpaths_executable Qt6::qtpaths LOCATION)
+
+ set(QUERY_EXECUTABLE ${_qtpaths_executable}
+ CACHE FILEPATH "Location of the Qt6 qtpaths executable")
+ set(_exec_name_text "Qt6 qtpaths")
+ set(_cli_option "--query")
+ endif()
+endif()
+
+function(ecm_query_qt result_variable qt_variable)
+ set(options TRY)
+ set(oneValueArgs)
+ set(multiValueArgs)
+
+ cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(NOT QUERY_EXECUTABLE)
+ if(ARGS_TRY)
+ set(${result_variable} "" PARENT_SCOPE)
+ message(STATUS "No ${_exec_name_text} executable found. Can't check ${qt_variable}")
+ return()
+ else()
+ message(FATAL_ERROR "No ${_exec_name_text} executable found. Can't check ${qt_variable} as required")
+ endif()
+ endif()
+ execute_process(
+ COMMAND ${QUERY_EXECUTABLE} ${_cli_option} "${qt_variable}"
+ RESULT_VARIABLE return_code
+ OUTPUT_VARIABLE output
+ )
+ if(return_code EQUAL 0)
+ string(STRIP "${output}" output)
+ file(TO_CMAKE_PATH "${output}" output_path)
+ set(${result_variable} "${output_path}" PARENT_SCOPE)
+ else()
+ message(WARNING "Failed call: ${_command} \"${qt_variable}\"")
+ message(FATAL_ERROR "${_exec_name_text} call failed: ${return_code}")
+ endif()
+endfunction()
diff --git a/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMSetupVersion.cmake b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMSetupVersion.cmake
new file mode 100644
index 0000000000..65c1688ab0
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/cmake/Modules/ECMSetupVersion.cmake
@@ -0,0 +1,202 @@
+#.rst:
+# ECMSetupVersion
+# ---------------
+#
+# Handle library version information.
+#
+# ::
+#
+# ecm_setup_version(<version>
+# VARIABLE_PREFIX <prefix>
+# [SOVERSION <soversion>]
+# [VERSION_HEADER <filename>]
+# [PACKAGE_VERSION_FILE <filename> [COMPATIBILITY <compat>]] )
+#
+# This parses a version string and sets up a standard set of version variables.
+# It can optionally also create a C version header file and a CMake package
+# version file to install along with the library.
+#
+# If the ``<version>`` argument is of the form ``<major>.<minor>.<patch>``
+# (or ``<major>.<minor>.<patch>.<tweak>``), The following CMake variables are
+# set::
+#
+# <prefix>_VERSION_MAJOR - <major>
+# <prefix>_VERSION_MINOR - <minor>
+# <prefix>_VERSION_PATCH - <patch>
+# <prefix>_VERSION - <version>
+# <prefix>_VERSION_STRING - <version> (for compatibility: use <prefix>_VERSION instead)
+# <prefix>_SOVERSION - <soversion>, or <major> if SOVERSION was not given
+#
+# If CMake policy CMP0048 is not NEW, the following CMake variables will also
+# be set::
+#
+# PROJECT_VERSION_MAJOR - <major>
+# PROJECT_VERSION_MINOR - <minor>
+# PROJECT_VERSION_PATCH - <patch>
+# PROJECT_VERSION - <version>
+# PROJECT_VERSION_STRING - <version> (for compatibility: use PROJECT_VERSION instead)
+#
+# If the VERSION_HEADER option is used, a simple C header is generated with the
+# given filename. If filename is a relative path, it is interpreted as relative
+# to CMAKE_CURRENT_BINARY_DIR. The generated header contains the following
+# macros::
+#
+# <prefix>_VERSION_MAJOR - <major> as an integer
+# <prefix>_VERSION_MINOR - <minor> as an integer
+# <prefix>_VERSION_PATCH - <patch> as an integer
+# <prefix>_VERSION_STRING - <version> as a C string
+# <prefix>_VERSION - the version as an integer
+#
+# ``<prefix>_VERSION`` has ``<patch>`` in the bottom 8 bits, ``<minor>`` in the
+# next 8 bits and ``<major>`` in the remaining bits. Note that ``<patch>`` and
+# ``<minor>`` must be less than 256.
+#
+# If the PACKAGE_VERSION_FILE option is used, a simple CMake package version
+# file is created using the write_basic_package_version_file() macro provided by
+# CMake. It should be installed in the same location as the Config.cmake file of
+# the library so that it can be found by find_package(). If the filename is a
+# relative path, it is interpreted as relative to CMAKE_CURRENT_BINARY_DIR. The
+# optional COMPATIBILITY option is forwarded to
+# write_basic_package_version_file(), and defaults to AnyNewerVersion.
+#
+# If CMake policy CMP0048 is NEW, an alternative form of the command is
+# available::
+#
+# ecm_setup_version(PROJECT
+# [VARIABLE_PREFIX <prefix>]
+# [SOVERSION <soversion>]
+# [VERSION_HEADER <filename>]
+# [PACKAGE_VERSION_FILE <filename>] )
+#
+# This will use the version information set by the project() command.
+# VARIABLE_PREFIX defaults to the project name. Note that PROJECT must be the
+# first argument. In all other respects, it behaves like the other form of the
+# command.
+#
+# Since pre-1.0.0.
+#
+# COMPATIBILITY option available since 1.6.0.
+
+#=============================================================================
+# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
+# SPDX-FileCopyrightText: 2012 Alexander Neundorf <neundorf@kde.org>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+
+include(CMakePackageConfigHelpers)
+
+# save the location of the header template while CMAKE_CURRENT_LIST_DIR
+# has the value we want
+set(_ECM_SETUP_VERSION_HEADER_TEMPLATE "${CMAKE_CURRENT_LIST_DIR}/ECMVersionHeader.h.in")
+
+function(ecm_setup_version _version)
+ set(options )
+ set(oneValueArgs VARIABLE_PREFIX SOVERSION VERSION_HEADER PACKAGE_VERSION_FILE COMPATIBILITY)
+ set(multiValueArgs )
+
+ cmake_parse_arguments(ESV "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(ESV_UNPARSED_ARGUMENTS)
+ message(FATAL_ERROR "Unknown keywords given to ECM_SETUP_VERSION(): \"${ESV_UNPARSED_ARGUMENTS}\"")
+ endif()
+
+ set(project_manages_version FALSE)
+ set(use_project_version FALSE)
+ # CMP0048 only exists in CMake 3.0.0 and later
+ if(CMAKE_VERSION VERSION_LESS 3.0.0)
+ set(project_version_policy "OLD")
+ else()
+ cmake_policy(GET CMP0048 project_version_policy)
+ endif()
+ if(project_version_policy STREQUAL "NEW")
+ set(project_manages_version TRUE)
+ if(_version STREQUAL "PROJECT")
+ set(use_project_version TRUE)
+ endif()
+ elseif(_version STREQUAL "PROJECT")
+ message(FATAL_ERROR "ecm_setup_version given PROJECT argument, but CMP0048 is not NEW")
+ endif()
+
+ set(should_set_prefixed_vars TRUE)
+ if(NOT ESV_VARIABLE_PREFIX)
+ if(use_project_version)
+ set(ESV_VARIABLE_PREFIX "${PROJECT_NAME}")
+ set(should_set_prefixed_vars FALSE)
+ else()
+ message(FATAL_ERROR "Required argument PREFIX missing in ECM_SETUP_VERSION() call")
+ endif()
+ endif()
+
+ if(use_project_version)
+ set(_version "${PROJECT_VERSION}")
+ set(_major "${PROJECT_VERSION_MAJOR}")
+ set(_minor "${PROJECT_VERSION_MINOR}")
+ set(_patch "${PROJECT_VERSION_PATCH}")
+ else()
+ string(REGEX REPLACE "^0*([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" _major "${_version}")
+ string(REGEX REPLACE "^[0-9]+\\.0*([0-9]+)\\.[0-9]+.*" "\\1" _minor "${_version}")
+ string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.0*([0-9]+).*" "\\1" _patch "${_version}")
+ endif()
+
+ if(NOT ESV_SOVERSION)
+ set(ESV_SOVERSION ${_major})
+ endif()
+
+ if(should_set_prefixed_vars)
+ set(${ESV_VARIABLE_PREFIX}_VERSION "${_version}")
+ set(${ESV_VARIABLE_PREFIX}_VERSION_MAJOR ${_major})
+ set(${ESV_VARIABLE_PREFIX}_VERSION_MINOR ${_minor})
+ set(${ESV_VARIABLE_PREFIX}_VERSION_PATCH ${_patch})
+ endif()
+
+ set(${ESV_VARIABLE_PREFIX}_SOVERSION ${ESV_SOVERSION})
+
+ if(NOT project_manages_version)
+ set(PROJECT_VERSION "${_version}")
+ set(PROJECT_VERSION_MAJOR "${_major}")
+ set(PROJECT_VERSION_MINOR "${_minor}")
+ set(PROJECT_VERSION_PATCH "${_patch}")
+ endif()
+
+ # compat
+ set(PROJECT_VERSION_STRING "${PROJECT_VERSION}")
+ set(${ESV_VARIABLE_PREFIX}_VERSION_STRING "${${ESV_VARIABLE_PREFIX}_VERSION}")
+
+ if(ESV_VERSION_HEADER)
+ set(HEADER_PREFIX "${ESV_VARIABLE_PREFIX}")
+ set(HEADER_VERSION "${_version}")
+ set(HEADER_VERSION_MAJOR "${_major}")
+ set(HEADER_VERSION_MINOR "${_minor}")
+ set(HEADER_VERSION_PATCH "${_patch}")
+ configure_file("${_ECM_SETUP_VERSION_HEADER_TEMPLATE}" "${ESV_VERSION_HEADER}")
+ endif()
+
+ if(ESV_PACKAGE_VERSION_FILE)
+ if(NOT ESV_COMPATIBILITY)
+ set(ESV_COMPATIBILITY AnyNewerVersion)
+ endif()
+ write_basic_package_version_file("${ESV_PACKAGE_VERSION_FILE}" VERSION ${_version} COMPATIBILITY ${ESV_COMPATIBILITY})
+ endif()
+
+ if(should_set_prefixed_vars)
+ set(${ESV_VARIABLE_PREFIX}_VERSION_MAJOR "${${ESV_VARIABLE_PREFIX}_VERSION_MAJOR}" PARENT_SCOPE)
+ set(${ESV_VARIABLE_PREFIX}_VERSION_MINOR "${${ESV_VARIABLE_PREFIX}_VERSION_MINOR}" PARENT_SCOPE)
+ set(${ESV_VARIABLE_PREFIX}_VERSION_PATCH "${${ESV_VARIABLE_PREFIX}_VERSION_PATCH}" PARENT_SCOPE)
+ set(${ESV_VARIABLE_PREFIX}_VERSION "${${ESV_VARIABLE_PREFIX}_VERSION}" PARENT_SCOPE)
+ endif()
+
+ # always set the soversion
+ set(${ESV_VARIABLE_PREFIX}_SOVERSION "${${ESV_VARIABLE_PREFIX}_SOVERSION}" PARENT_SCOPE)
+
+ if(NOT project_manages_version)
+ set(PROJECT_VERSION "${PROJECT_VERSION}" PARENT_SCOPE)
+ set(PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}" PARENT_SCOPE)
+ set(PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}" PARENT_SCOPE)
+ set(PROJECT_VERSION_PATCH "${PROJECT_VERSION_PATCH}" PARENT_SCOPE)
+ endif()
+
+ # always set the compatibility variables
+ set(PROJECT_VERSION_STRING "${PROJECT_VERSION_STRING}" PARENT_SCOPE)
+ set(${ESV_VARIABLE_PREFIX}_VERSION_STRING "${${ESV_VARIABLE_PREFIX}_VERSION}" PARENT_SCOPE)
+
+endfunction()
diff --git a/src/libs/3rdparty/qtkeychain/cmake/Modules/QtVersionOption.cmake b/src/libs/3rdparty/qtkeychain/cmake/Modules/QtVersionOption.cmake
new file mode 100644
index 0000000000..ea37da22de
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/cmake/Modules/QtVersionOption.cmake
@@ -0,0 +1,36 @@
+# SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
+#
+# SPDX-License-Identifier: BSD-3-Clause
+
+#[=======================================================================[.rst:
+QtVersionOption
+---------------
+
+Adds a build option to select the major Qt version if necessary,
+that is, if the major Qt version has not yet been determined otherwise
+(e.g. by a corresponding ``find_package()`` call).
+This module is typically included by other modules requiring knowledge
+about the major Qt version.
+
+``QT_MAJOR_VERSION`` is defined to either be "5" or "6".
+
+Since 5.82.0.
+#]=======================================================================]
+
+if (DEFINED QT_MAJOR_VERSION)
+ return()
+endif()
+
+if (TARGET Qt5::Core)
+ set(QT_MAJOR_VERSION 5)
+elseif (TARGET Qt6::Core)
+ set(QT_MAJOR_VERSION 6)
+else()
+ option(BUILD_WITH_QT6 "Build against Qt 6" OFF)
+
+ if (BUILD_WITH_QT6)
+ set(QT_MAJOR_VERSION 6)
+ else()
+ set(QT_MAJOR_VERSION 5)
+ endif()
+endif()
diff --git a/src/libs/3rdparty/qtkeychain/gnomekeyring.cpp b/src/libs/3rdparty/qtkeychain/gnomekeyring.cpp
new file mode 100644
index 0000000000..6347052dcb
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/gnomekeyring.cpp
@@ -0,0 +1,86 @@
+#include "gnomekeyring_p.h"
+
+const char* GnomeKeyring::GNOME_KEYRING_DEFAULT = nullptr;
+
+bool GnomeKeyring::isAvailable()
+{
+ const GnomeKeyring& keyring = instance();
+ return keyring.isLoaded() &&
+ keyring.NETWORK_PASSWORD &&
+ keyring.is_available &&
+ keyring.find_password &&
+ keyring.store_password &&
+ keyring.delete_password &&
+ keyring.is_available();
+}
+
+GnomeKeyring::gpointer GnomeKeyring::store_network_password(
+ const gchar* keyring,
+ const gchar* display_name,
+ const gchar* user,
+ const gchar* server,
+ const gchar* type,
+ const gchar* password,
+ OperationDoneCallback callback,
+ gpointer data,
+ GDestroyNotify destroy_data )
+{
+ if ( !isAvailable() )
+ return 0;
+ return instance().store_password( instance().NETWORK_PASSWORD,
+ keyring, display_name, password, callback,
+ data, destroy_data,
+ "user", user,
+ "server", server,
+ "type", type,
+ static_cast<char*>(0) );
+}
+
+GnomeKeyring::gpointer GnomeKeyring::find_network_password(
+ const gchar* user, const gchar* server, const gchar* type,
+ OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data )
+{
+ if ( !isAvailable() )
+ return 0;
+
+ return instance().find_password( instance().NETWORK_PASSWORD,
+ callback, data, destroy_data,
+ "user", user, "server", server, "type", type,
+ static_cast<char*>(0) );
+}
+
+GnomeKeyring::gpointer GnomeKeyring::delete_network_password( const gchar* user,
+ const gchar* server,
+ OperationDoneCallback callback,
+ gpointer data,
+ GDestroyNotify destroy_data )
+{
+ if ( !isAvailable() )
+ return 0;
+ return instance().delete_password( instance().NETWORK_PASSWORD,
+ callback, data, destroy_data,
+ "user", user, "server", server, static_cast<char*>(0) );
+}
+
+GnomeKeyring::GnomeKeyring()
+ : QLibrary(QLatin1String("gnome-keyring"), 0)
+{
+ static const PasswordSchema schema = {
+ ITEM_NETWORK_PASSWORD,
+ {{ "user", ATTRIBUTE_TYPE_STRING },
+ { "server", ATTRIBUTE_TYPE_STRING },
+ { "type", ATTRIBUTE_TYPE_STRING },
+ { 0, static_cast<AttributeType>( 0 ) }}
+ };
+
+ NETWORK_PASSWORD = &schema;
+ is_available = reinterpret_cast<is_available_fn*>( resolve( "gnome_keyring_is_available" ) );
+ find_password = reinterpret_cast<find_password_fn*>( resolve( "gnome_keyring_find_password" ) );
+ store_password = reinterpret_cast<store_password_fn*>( resolve( "gnome_keyring_store_password" ) );
+ delete_password = reinterpret_cast<delete_password_fn*>( resolve( "gnome_keyring_delete_password" ) );
+}
+
+GnomeKeyring& GnomeKeyring::instance() {
+ static GnomeKeyring keyring;
+ return keyring;
+}
diff --git a/src/libs/3rdparty/qtkeychain/gnomekeyring_p.h b/src/libs/3rdparty/qtkeychain/gnomekeyring_p.h
new file mode 100644
index 0000000000..87c062c375
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/gnomekeyring_p.h
@@ -0,0 +1,94 @@
+#ifndef QTKEYCHAIN_GNOME_P_H
+#define QTKEYCHAIN_GNOME_P_H
+
+#include <QLibrary>
+
+class GnomeKeyring : private QLibrary {
+ Q_OBJECT
+
+public:
+ enum Result {
+ RESULT_OK,
+ RESULT_DENIED,
+ RESULT_NO_KEYRING_DAEMON,
+ RESULT_ALREADY_UNLOCKED,
+ RESULT_NO_SUCH_KEYRING,
+ RESULT_BAD_ARGUMENTS,
+ RESULT_IO_ERROR,
+ RESULT_CANCELLED,
+ RESULT_KEYRING_ALREADY_EXISTS,
+ RESULT_NO_MATCH
+ };
+
+ enum ItemType {
+ ITEM_GENERIC_SECRET = 0,
+ ITEM_NETWORK_PASSWORD,
+ ITEM_NOTE,
+ ITEM_CHAINED_KEYRING_PASSWORD,
+ ITEM_ENCRYPTION_KEY_PASSWORD,
+ ITEM_PK_STORAGE = 0x100
+ };
+
+ enum AttributeType {
+ ATTRIBUTE_TYPE_STRING,
+ ATTRIBUTE_TYPE_UINT32
+ };
+
+ typedef char gchar;
+ typedef void* gpointer;
+ typedef bool gboolean;
+ typedef struct {
+ ItemType item_type;
+ struct {
+ const gchar* name;
+ AttributeType type;
+ } attributes[32];
+ } PasswordSchema;
+
+ typedef void ( *OperationGetStringCallback )( Result result, bool binary,
+ const char* string, gpointer data );
+ typedef void ( *OperationDoneCallback )( Result result, gpointer data );
+ typedef void ( *GDestroyNotify )( gpointer data );
+
+ static const char* GNOME_KEYRING_DEFAULT;
+
+ static bool isAvailable();
+
+ static gpointer store_network_password( const gchar* keyring, const gchar* display_name,
+ const gchar* user, const gchar* server,
+ const gchar* type, const gchar* password,
+ OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data );
+
+ static gpointer find_network_password( const gchar* user, const gchar* server,
+ const gchar* type,
+ OperationGetStringCallback callback,
+ gpointer data, GDestroyNotify destroy_data );
+
+ static gpointer delete_network_password( const gchar* user, const gchar* server,
+ OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data );
+private:
+ GnomeKeyring();
+
+ static GnomeKeyring& instance();
+
+ const PasswordSchema* NETWORK_PASSWORD;
+ typedef gboolean ( is_available_fn )( void );
+ typedef gpointer ( store_password_fn )( const PasswordSchema* schema, const gchar* keyring,
+ const gchar* display_name, const gchar* password,
+ OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data,
+ ... );
+ typedef gpointer ( find_password_fn )( const PasswordSchema* schema,
+ OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data,
+ ... );
+ typedef gpointer ( delete_password_fn )( const PasswordSchema* schema,
+ OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data,
+ ... );
+
+ is_available_fn* is_available;
+ find_password_fn* find_password;
+ store_password_fn* store_password;
+ delete_password_fn* delete_password;
+};
+
+
+#endif
diff --git a/src/libs/3rdparty/qtkeychain/keychain.cpp b/src/libs/3rdparty/qtkeychain/keychain.cpp
new file mode 100644
index 0000000000..90ee4eb502
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain.cpp
@@ -0,0 +1,235 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * *
+ * 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. For licensing and distribution *
+ * details, check the accompanying file 'COPYING'. *
+ *****************************************************************************/
+#include "keychain.h"
+#include "keychain_p.h"
+
+using namespace QKeychain;
+
+Job::Job( JobPrivate *q, QObject *parent )
+ : QObject( parent )
+ , d ( q ) {
+}
+
+Job::~Job() {
+ delete d;
+}
+
+QString Job::service() const {
+ return d->service;
+}
+
+QSettings* Job::settings() const {
+ return d->settings;
+}
+
+void Job::setSettings( QSettings* settings ) {
+ d->settings = settings;
+}
+
+void Job::start() {
+ QMetaObject::invokeMethod( this, "doStart", Qt::QueuedConnection );
+}
+
+bool Job::autoDelete() const {
+ return d->autoDelete;
+}
+
+void Job::setAutoDelete( bool autoDelete ) {
+ d->autoDelete = autoDelete;
+}
+
+bool Job::insecureFallback() const {
+ return d->insecureFallback;
+}
+
+void Job::setInsecureFallback( bool insecureFallback ) {
+ d->insecureFallback = insecureFallback;
+}
+
+void Job::doStart() {
+ JobExecutor::instance()->enqueue( this );
+}
+
+void Job::emitFinished() {
+ emit finished( this );
+ if ( d->autoDelete )
+ deleteLater();
+}
+
+void Job::emitFinishedWithError( Error error, const QString& errorString ) {
+ d->error = error;
+ d->errorString = errorString;
+ emitFinished();
+}
+
+void Job::scheduledStart() {
+ d->scheduledStart();
+}
+
+Error Job::error() const {
+ return d->error;
+}
+
+QString Job::errorString() const {
+ return d->errorString;
+}
+
+void Job::setError( Error error ) {
+ d->error = error;
+}
+
+void Job::setErrorString( const QString& errorString ) {
+ d->errorString = errorString;
+}
+
+ReadPasswordJob::ReadPasswordJob( const QString& service, QObject* parent )
+ : Job( new ReadPasswordJobPrivate( service, this ), parent ) {
+
+}
+
+ReadPasswordJob::~ReadPasswordJob() {
+}
+
+QString ReadPasswordJob::textData() const {
+ return QString::fromUtf8( d->data );
+}
+
+QByteArray ReadPasswordJob::binaryData() const {
+ return d->data;
+}
+
+QString Job::key() const {
+ return d->key;
+}
+
+void Job::setKey( const QString& key_ ) {
+ d->key = key_;
+}
+
+WritePasswordJob::WritePasswordJob( const QString& service, QObject* parent )
+ : Job( new WritePasswordJobPrivate( service, this ), parent ) {
+}
+
+WritePasswordJob::~WritePasswordJob() {
+}
+
+void WritePasswordJob::setBinaryData( const QByteArray& data ) {
+ d->data = data;
+ d->mode = JobPrivate::Binary;
+}
+
+void WritePasswordJob::setTextData( const QString& data ) {
+ d->data = data.toUtf8();
+ d->mode = JobPrivate::Text;
+}
+
+DeletePasswordJob::DeletePasswordJob( const QString& service, QObject* parent )
+ : Job( new DeletePasswordJobPrivate( service, this ), parent ) {
+}
+
+DeletePasswordJob::~DeletePasswordJob() {
+}
+
+DeletePasswordJobPrivate::DeletePasswordJobPrivate(const QString &service_, DeletePasswordJob *qq) :
+ JobPrivate(service_, qq) {
+
+}
+
+JobExecutor::JobExecutor()
+ : QObject( 0 )
+ , m_jobRunning( false ) {
+}
+
+void JobExecutor::enqueue( Job* job ) {
+ m_queue.enqueue( job );
+ startNextIfNoneRunning();
+}
+
+void JobExecutor::startNextIfNoneRunning() {
+ if ( m_queue.isEmpty() || m_jobRunning )
+ return;
+ QPointer<Job> next;
+ while ( !next && !m_queue.isEmpty() ) {
+ next = m_queue.dequeue();
+ }
+ if ( next ) {
+ connect( next, SIGNAL(finished(QKeychain::Job*)), this, SLOT(jobFinished(QKeychain::Job*)) );
+ connect( next, SIGNAL(destroyed(QObject*)), this, SLOT(jobDestroyed(QObject*)) );
+ m_jobRunning = true;
+ next->scheduledStart();
+ }
+}
+
+void JobExecutor::jobDestroyed( QObject* object ) {
+ Job* job = static_cast<Job*>(object);
+ Q_UNUSED( object ) // for release mode
+ job->disconnect( this );
+ m_jobRunning = false;
+ startNextIfNoneRunning();
+}
+
+void JobExecutor::jobFinished( Job* job ) {
+ Q_UNUSED( job ) // for release mode
+ job->disconnect( this );
+ m_jobRunning = false;
+ startNextIfNoneRunning();
+}
+
+JobExecutor* JobExecutor::s_instance = 0;
+
+JobExecutor* JobExecutor::instance() {
+ if ( !s_instance )
+ s_instance = new JobExecutor;
+ return s_instance;
+}
+
+ReadPasswordJobPrivate::ReadPasswordJobPrivate(const QString &service_, ReadPasswordJob *qq) :
+ JobPrivate(service_, qq) {
+
+}
+
+JobPrivate::JobPrivate(const QString &service_, Job *qq)
+ : q(qq)
+ , mode( Text )
+ , error( NoError )
+ , service( service_ )
+ , autoDelete( true )
+ , insecureFallback( false )
+{
+}
+
+QString JobPrivate::modeToString(Mode m)
+{
+ switch (m) {
+ case Text:
+ return QLatin1String("Text");
+ case Binary:
+ return QLatin1String("Binary");
+ }
+
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Unhandled Mode value");
+ return QString();
+}
+
+JobPrivate::Mode JobPrivate::stringToMode(const QString& s)
+{
+ if (s == QLatin1String("Text") || s == QLatin1String("1"))
+ return Text;
+ if (s == QLatin1String("Binary") || s == QLatin1String("2"))
+ return Binary;
+
+ qCritical("Unexpected mode string '%s'", qPrintable(s));
+
+ return Text;
+}
+
+WritePasswordJobPrivate::WritePasswordJobPrivate(const QString &service_, WritePasswordJob *qq) :
+ JobPrivate(service_, qq) {
+
+}
diff --git a/src/libs/3rdparty/qtkeychain/keychain.h b/src/libs/3rdparty/qtkeychain/keychain.h
new file mode 100644
index 0000000000..8c006cbcdd
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain.h
@@ -0,0 +1,282 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * *
+ * 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. For licensing and distribution *
+ * details, check the accompanying file 'COPYING'. *
+ *****************************************************************************/
+#ifndef KEYCHAIN_H
+#define KEYCHAIN_H
+
+#if !defined(QTKEYCHAIN_NO_EXPORT)
+#include "qkeychain_export.h"
+#else
+#define QKEYCHAIN_EXPORT
+#endif
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+
+class QSettings;
+
+#define QTKEYCHAIN_VERSION 0x000100
+
+namespace QKeychain {
+
+/**
+ * Error codes
+ */
+enum Error {
+ NoError=0, /**< No error occurred, operation was successful */
+ EntryNotFound, /**< For the given key no data was found */
+ CouldNotDeleteEntry, /**< Could not delete existing secret data */
+ AccessDeniedByUser, /**< User denied access to keychain */
+ AccessDenied, /**< Access denied for other reasons */
+ NoBackendAvailable, /**< No platform-specific keychain service available */
+ NotImplemented, /**< Not implemented on platform */
+ OtherError /**< Something else went wrong (errorString() might provide details) */
+};
+
+class JobExecutor;
+class JobPrivate;
+
+/**
+ * @brief Abstract base class for all QKeychain jobs.
+ */
+class QKEYCHAIN_EXPORT Job : public QObject {
+ Q_OBJECT
+public:
+ ~Job() override;
+
+ /**
+ * @return The QSettings instance used as plaintext storage if insecureFallback() is true.
+ * @see setSettings()
+ * @see insecureFallback()
+ */
+ QSettings* settings() const;
+
+ /**
+ * @return Set the QSettings instance that will be used as plaintext storage if insecureFallback() is true.
+ * @see settings()
+ * @see insecureFallback()
+ */
+ void setSettings( QSettings* settings );
+
+ /**
+ * Call this method to start the job.
+ * Typically you want to connect some slot to the finished() signal first:
+ *
+ * \code
+ * SomeClass::startJob()
+ * {
+ * connect(job, &Job::finished, this, &SomeClass::slotJobFinished);
+ * job->start();
+ * }
+ *
+ * SomeClass::slotJobFinished(Job *job)
+ * {
+ * if (job->error()) {
+ * // handle error
+ * } else {
+ * // do job-specific stuff
+ * }
+ * }
+ * \endcode
+ *
+ * @see finished()
+ */
+ void start();
+
+ QString service() const;
+
+ /**
+ * @note Call this method only after finished() has been emitted.
+ * @return The error code of the job (0 if no error).
+ */
+ Error error() const;
+
+ /**
+ * @return An error message that might provide details if error() returns OtherError.
+ */
+ QString errorString() const;
+
+ /**
+ * @return Whether this job autodeletes itself once finished() has been emitted. Default is true.
+ * @see setAutoDelete()
+ */
+ bool autoDelete() const;
+
+ /**
+ * Set whether this job should autodelete itself once finished() has been emitted.
+ * @see autoDelete()
+ */
+ void setAutoDelete( bool autoDelete );
+
+ /**
+ * @return Whether this job will use plaintext storage on unsupported platforms. Default is false.
+ * @see setInsecureFallback()
+ */
+ bool insecureFallback() const;
+
+ /**
+ * Set whether this job should use plaintext storage on unsupported platforms.
+ * @see insecureFallback()
+ */
+ void setInsecureFallback( bool insecureFallback );
+
+ /**
+ * @return The string used as key by this job.
+ * @see setKey()
+ */
+ QString key() const;
+
+ /**
+ * Set the @p key that this job will use to read or write data from/to the keychain.
+ * The key can be an empty string.
+ * @see key()
+ */
+ void setKey( const QString& key );
+
+ void emitFinished();
+ void emitFinishedWithError(Error, const QString& errorString);
+
+Q_SIGNALS:
+ /**
+ * Emitted when this job is finished.
+ * You can connect to this signal to be notified about the job's completion.
+ * @see start()
+ */
+ void finished( QKeychain::Job* );
+
+protected:
+ explicit Job( JobPrivate *q, QObject* parent=nullptr );
+ Q_INVOKABLE void doStart();
+
+private:
+ void setError( Error error );
+ void setErrorString( const QString& errorString );
+
+ void scheduledStart();
+
+protected:
+ JobPrivate* const d;
+
+friend class JobExecutor;
+friend class JobPrivate;
+friend class ReadPasswordJobPrivate;
+friend class WritePasswordJobPrivate;
+friend class DeletePasswordJobPrivate;
+};
+
+class ReadPasswordJobPrivate;
+
+/**
+ * @brief Job for reading secrets from the keychain.
+ * You can use a ReadPasswordJob to read passwords or binary data from the keychain.
+ * This job requires a "service" string, which is basically a namespace of keys within the keychain.
+ * This means that you can read all the pairs <key, secret> stored in the same service string.
+ */
+class QKEYCHAIN_EXPORT ReadPasswordJob : public Job {
+ Q_OBJECT
+public:
+ /**
+ * Create a new ReadPasswordJob.
+ * @param service The service string used by this job (can be empty).
+ * @param parent The parent of this job.
+ */
+ explicit ReadPasswordJob( const QString& service, QObject* parent=nullptr );
+ ~ReadPasswordJob() override;
+
+ /**
+ * @return The binary data stored as value of this job's key().
+ * @see Job::key()
+ */
+ QByteArray binaryData() const;
+
+ /**
+ * @return The string stored as value of this job's key().
+ * @see Job::key()
+ * @warning Returns meaningless data if the data was stored as binary data.
+ * @see WritePasswordJob::setTextData()
+ */
+ QString textData() const;
+
+private:
+ friend class QKeychain::ReadPasswordJobPrivate;
+};
+
+class WritePasswordJobPrivate;
+
+/**
+ * @brief Job for writing secrets to the keychain.
+ * You can use a WritePasswordJob to store passwords or binary data in the keychain.
+ * This job requires a "service" string, which is basically a namespace of keys within the keychain.
+ * This means that you can store different pairs <key, secret> under the same service string.
+ */
+class QKEYCHAIN_EXPORT WritePasswordJob : public Job {
+ Q_OBJECT
+public:
+ /**
+ * Create a new WritePasswordJob.
+ * @param service The service string used by this job (can be empty).
+ * @param parent The parent of this job.
+ */
+ explicit WritePasswordJob( const QString& service, QObject* parent=nullptr );
+ ~WritePasswordJob() override;
+
+ /**
+ * Set the @p data that the job will store in the keychain as binary data.
+ * @warning setBinaryData() and setTextData() are mutually exclusive.
+ */
+ void setBinaryData( const QByteArray& data );
+
+ /**
+ * Set the @p data that the job will store in the keychain as string.
+ * Typically @p data is a password.
+ * @warning setBinaryData() and setTextData() are mutually exclusive.
+ */
+ void setTextData( const QString& data );
+
+private:
+
+ friend class QKeychain::WritePasswordJobPrivate;
+};
+
+class DeletePasswordJobPrivate;
+
+/**
+ * @brief Job for deleting secrets from the keychain.
+ * You can use a DeletePasswordJob to delete passwords or binary data from the keychain.
+ * This job requires a "service" string, which is basically a namespace of keys within the keychain.
+ * This means that you can delete all the pairs <key, secret> stored in the same service string.
+ */
+class QKEYCHAIN_EXPORT DeletePasswordJob : public Job {
+ Q_OBJECT
+public:
+ /**
+ * Create a new DeletePasswordJob.
+ * @param service The service string used by this job (can be empty).
+ * @param parent The parent of this job.
+ */
+ explicit DeletePasswordJob( const QString& service, QObject* parent=nullptr );
+ ~DeletePasswordJob() override;
+
+private:
+ friend class QKeychain::DeletePasswordJobPrivate;
+};
+
+/**
+ * Checks whether there is a viable secure backend available.
+ * This particularly matters on UNIX platforms where multiple different backends
+ * exist and none might be available.
+ *
+ * Note that using the insecure fallback will work even if no secure backend is available.
+ *
+ * @since 0.14.0
+ */
+QKEYCHAIN_EXPORT bool isAvailable();
+
+} // namespace QtKeychain
+
+#endif
diff --git a/src/libs/3rdparty/qtkeychain/keychain_apple.mm b/src/libs/3rdparty/qtkeychain/keychain_apple.mm
new file mode 100644
index 0000000000..f9a8266be0
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain_apple.mm
@@ -0,0 +1,263 @@
+/******************************************************************************
+ * Copyright (C) 2016 Mathias Hasselmann <mathias.hasselmann@kdab.com> *
+ * *
+ * 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. For licensing and distribution *
+ * details, check the accompanying file 'COPYING'. *
+ *****************************************************************************/
+
+#include "keychain_p.h"
+
+#import <Foundation/Foundation.h>
+#import <Security/Security.h>
+
+using namespace QKeychain;
+
+struct ErrorDescription
+{
+ QKeychain::Error code;
+ QString message;
+
+ ErrorDescription(QKeychain::Error code, const QString &message)
+ : code(code), message(message) {}
+
+ static ErrorDescription fromStatus(OSStatus status)
+ {
+ switch(status) {
+ case errSecSuccess:
+ return ErrorDescription(QKeychain::NoError, Job::tr("No error"));
+ case errSecItemNotFound:
+ return ErrorDescription(QKeychain::EntryNotFound, Job::tr("The specified item could not be found in the keychain"));
+ case errSecUserCanceled:
+ return ErrorDescription(QKeychain::AccessDeniedByUser, Job::tr("User canceled the operation"));
+ case errSecInteractionNotAllowed:
+ return ErrorDescription(QKeychain::AccessDenied, Job::tr("User interaction is not allowed"));
+ case errSecNotAvailable:
+ return ErrorDescription(QKeychain::AccessDenied, Job::tr("No keychain is available. You may need to restart your computer"));
+ case errSecAuthFailed:
+ return ErrorDescription(QKeychain::AccessDenied, Job::tr("The user name or passphrase you entered is not correct"));
+ case errSecVerifyFailed:
+ return ErrorDescription(QKeychain::AccessDenied, Job::tr("A cryptographic verification failure has occurred"));
+ case errSecUnimplemented:
+ return ErrorDescription(QKeychain::NotImplemented, Job::tr("Function or operation not implemented"));
+ case errSecIO:
+ return ErrorDescription(QKeychain::OtherError, Job::tr("I/O error"));
+ case errSecOpWr:
+ return ErrorDescription(QKeychain::OtherError, Job::tr("Already open with with write permission"));
+ case errSecParam:
+ return ErrorDescription(QKeychain::OtherError, Job::tr("Invalid parameters passed to a function"));
+ case errSecAllocate:
+ return ErrorDescription(QKeychain::OtherError, Job::tr("Failed to allocate memory"));
+ case errSecBadReq:
+ return ErrorDescription(QKeychain::OtherError, Job::tr("Bad parameter or invalid state for operation"));
+ case errSecInternalComponent:
+ return ErrorDescription(QKeychain::OtherError, Job::tr("An internal component failed"));
+ case errSecDuplicateItem:
+ return ErrorDescription(QKeychain::OtherError, Job::tr("The specified item already exists in the keychain"));
+ case errSecDecode:
+ return ErrorDescription(QKeychain::OtherError, Job::tr("Unable to decode the provided data"));
+ }
+
+ return ErrorDescription(QKeychain::OtherError, Job::tr("Unknown error"));
+ }
+};
+
+@interface AppleKeychainInterface : NSObject
+
+- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob;
+- (void)keychainTaskFinished;
+- (void)keychainReadTaskFinished:(NSData *)retrievedData;
+- (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSString *)descriptiveMessage;
+
+@end
+
+@interface AppleKeychainInterface()
+{
+ QPointer<Job> _job;
+ QPointer<JobPrivate> _privateJob;
+}
+@end
+
+@implementation AppleKeychainInterface
+
+- (instancetype)initWithJob:(Job *)job andPrivateJob:(JobPrivate *)privateJob
+{
+ self = [super init];
+ if (self) {
+ _job = job;
+ _privateJob = privateJob;
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [NSNotificationCenter.defaultCenter removeObserver:self];
+ [super dealloc];
+}
+
+- (void)keychainTaskFinished
+{
+ if (_job) {
+ _job->emitFinished();
+ }
+}
+
+- (void)keychainReadTaskFinished:(NSData *)retrievedData
+{
+ _privateJob->data.clear();
+ _privateJob->mode = JobPrivate::Binary;
+
+ if (retrievedData != nil) {
+ if (_privateJob) {
+ _privateJob->data = QByteArray::fromNSData(retrievedData);
+ }
+ }
+
+ if (_job) {
+ _job->emitFinished();
+ }
+}
+
+- (void)keychainTaskFinishedWithError:(OSStatus)status descriptiveMessage:(NSString *)descriptiveMessage
+{
+ const auto localisedDescriptiveMessage = Job::tr([descriptiveMessage UTF8String]);
+
+ const ErrorDescription error = ErrorDescription::fromStatus(status);
+ const auto fullMessage = localisedDescriptiveMessage.isEmpty() ? error.message : QStringLiteral("%1: %2").arg(localisedDescriptiveMessage, error.message);
+
+ if (_job) {
+ _job->emitFinishedWithError(error.code, fullMessage);
+ }
+}
+
+@end
+
+
+static void StartReadPassword(const QString &service, const QString &key, AppleKeychainInterface * const interface)
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+ NSDictionary * const query = @{
+ (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
+ (__bridge NSString *)kSecAttrService: service.toNSString(),
+ (__bridge NSString *)kSecAttrAccount: key.toNSString(),
+ (__bridge NSString *)kSecReturnData: @YES,
+ };
+
+ CFTypeRef dataRef = nil;
+ const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef);
+
+ if (status == errSecSuccess) {
+ const CFDataRef castedDataRef = (CFDataRef)dataRef;
+ NSData * const data = (__bridge NSData *)castedDataRef;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [interface keychainReadTaskFinished:data];
+ [interface release];
+ });
+ } else {
+ NSString * const descriptiveErrorString = @"Could not retrieve private key from keystore";
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString];
+ [interface release];
+ });
+ }
+
+ if (dataRef) {
+ CFRelease(dataRef);
+ }
+ });
+}
+
+static void StartWritePassword(const QString &service, const QString &key, const QByteArray &data, AppleKeychainInterface * const interface)
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+ NSDictionary * const query = @{
+ (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
+ (__bridge NSString *)kSecAttrService: service.toNSString(),
+ (__bridge NSString *)kSecAttrAccount: key.toNSString(),
+ };
+
+ OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, nil);
+
+ if (status == errSecSuccess) {
+ NSDictionary * const update = @{
+ (__bridge NSString *)kSecValueData: data.toNSData(),
+ };
+
+ status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update);
+ } else {
+ NSDictionary * const insert = @{
+ (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
+ (__bridge NSString *)kSecAttrService: service.toNSString(),
+ (__bridge NSString *)kSecAttrAccount: key.toNSString(),
+ (__bridge NSString *)kSecValueData: data.toNSData(),
+ };
+
+ status = SecItemAdd((__bridge const CFDictionaryRef)insert, nil);
+ }
+
+ if (status == errSecSuccess) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [interface keychainTaskFinished];
+ [interface release];
+ });
+ } else {
+ NSString * const descriptiveErrorString = @"Could not store data in settings";
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString];
+ [interface release];
+ });
+ }
+ });
+}
+
+static void StartDeletePassword(const QString &service, const QString &key, AppleKeychainInterface * const interface)
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+ NSDictionary * const query = @{
+ (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
+ (__bridge NSString *)kSecAttrService: service.toNSString(),
+ (__bridge NSString *)kSecAttrAccount: key.toNSString(),
+ };
+
+ const OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
+
+ if (status == errSecSuccess) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [interface keychainTaskFinished];
+ [interface release];
+ });
+ } else {
+ NSString * const descriptiveErrorString = @"Could not remove private key from keystore";
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString];
+ [interface release];
+ });
+ }
+ });
+}
+
+void ReadPasswordJobPrivate::scheduledStart()
+{
+ AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this];
+ StartReadPassword(service, key, interface);
+}
+
+void WritePasswordJobPrivate::scheduledStart()
+{
+ AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this];
+ StartWritePassword(service, key, data, interface);
+}
+
+void DeletePasswordJobPrivate::scheduledStart()
+{
+ AppleKeychainInterface * const interface = [[AppleKeychainInterface alloc] initWithJob:q andPrivateJob:this];
+ StartDeletePassword(service, key, interface);
+}
+
+bool QKeychain::isAvailable()
+{
+ return true;
+}
diff --git a/src/libs/3rdparty/qtkeychain/keychain_p.h b/src/libs/3rdparty/qtkeychain/keychain_p.h
new file mode 100644
index 0000000000..a66cb423d1
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain_p.h
@@ -0,0 +1,167 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * *
+ * 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. For licensing and distribution *
+ * details, check the accompanying file 'COPYING'. *
+ *****************************************************************************/
+#ifndef KEYCHAIN_P_H
+#define KEYCHAIN_P_H
+
+#include <QCoreApplication>
+#include <QObject>
+#include <QPointer>
+#include <QSettings>
+#include <QQueue>
+
+#if defined(KEYCHAIN_DBUS)
+
+#include <QDBusPendingCallWatcher>
+
+#include "kwallet_interface.h"
+#else
+
+class QDBusPendingCallWatcher;
+
+#endif
+
+#include "keychain.h"
+
+namespace QKeychain {
+
+class JobExecutor;
+
+class JobPrivate : public QObject {
+ Q_OBJECT
+public:
+ enum Mode {
+ Text,
+ Binary
+ };
+
+ virtual void scheduledStart() = 0;
+
+ static QString modeToString(Mode m);
+ static Mode stringToMode(const QString& s);
+
+ Job* const q;
+ Mode mode;
+ QByteArray data;
+
+#if defined(KEYCHAIN_DBUS)
+ org::kde::KWallet* iface;
+ int walletHandle;
+
+ static void gnomeKeyring_readCb( int result, const char* string, JobPrivate* data );
+ static void gnomeKeyring_writeCb( int result, JobPrivate* self );
+
+ virtual void fallbackOnError(const QDBusError& err) = 0;
+
+protected Q_SLOTS:
+ void kwalletWalletFound( QDBusPendingCallWatcher* watcher );
+ virtual void kwalletFinished( QDBusPendingCallWatcher* watcher );
+ virtual void kwalletOpenFinished( QDBusPendingCallWatcher* watcher );
+#else
+ void kwalletWalletFound( QDBusPendingCallWatcher* ) {}
+ virtual void kwalletFinished( QDBusPendingCallWatcher* ) {}
+ virtual void kwalletOpenFinished( QDBusPendingCallWatcher* ) {}
+#endif
+
+protected:
+ JobPrivate( const QString& service_, Job *q );
+
+protected:
+ QKeychain::Error error;
+ QString errorString;
+ QString service;
+ bool autoDelete;
+ bool insecureFallback;
+ QPointer<QSettings> settings;
+ QString key;
+
+friend class Job;
+friend class JobExecutor;
+friend class ReadPasswordJob;
+friend class WritePasswordJob;
+friend class PlainTextStore;
+};
+
+class ReadPasswordJobPrivate : public JobPrivate {
+ Q_OBJECT
+public:
+ explicit ReadPasswordJobPrivate( const QString &service_, ReadPasswordJob* qq );
+ void scheduledStart() override;
+
+#if defined(KEYCHAIN_DBUS)
+ void fallbackOnError(const QDBusError& err) override;
+
+private Q_SLOTS:
+ void kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) override;
+ void kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher );
+ void kwalletFinished( QDBusPendingCallWatcher* watcher ) override;
+#else //moc's too dumb to respect above macros, so just define empty slot implementations
+private Q_SLOTS:
+ void kwalletOpenFinished( QDBusPendingCallWatcher* ) override {}
+ void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {}
+ void kwalletFinished( QDBusPendingCallWatcher* ) override {}
+#endif
+
+ friend class ReadPasswordJob;
+};
+
+class WritePasswordJobPrivate : public JobPrivate {
+ Q_OBJECT
+public:
+ explicit WritePasswordJobPrivate( const QString &service_, WritePasswordJob* qq );
+ void scheduledStart() override;
+
+#if defined(KEYCHAIN_DBUS)
+ void fallbackOnError(const QDBusError& err) override;
+#endif
+
+ friend class WritePasswordJob;
+};
+
+class DeletePasswordJobPrivate : public JobPrivate {
+ Q_OBJECT
+public:
+ explicit DeletePasswordJobPrivate( const QString &service_, DeletePasswordJob* qq );
+
+ void scheduledStart() override;
+
+#if defined(KEYCHAIN_DBUS)
+ void fallbackOnError(const QDBusError& err) override;
+#endif
+
+protected:
+ void doStart();
+
+ friend class DeletePasswordJob;
+};
+
+class JobExecutor : public QObject {
+ Q_OBJECT
+public:
+
+ static JobExecutor* instance();
+
+ void enqueue( Job* job );
+
+private:
+ explicit JobExecutor();
+ void startNextIfNoneRunning();
+
+private Q_SLOTS:
+ void jobFinished( QKeychain::Job* );
+ void jobDestroyed( QObject* object );
+
+private:
+ static JobExecutor* s_instance;
+ QQueue<QPointer<Job> > m_queue;
+ bool m_jobRunning;
+};
+
+}
+
+#endif // KEYCHAIN_P_H
diff --git a/src/libs/3rdparty/qtkeychain/keychain_unix.cpp b/src/libs/3rdparty/qtkeychain/keychain_unix.cpp
new file mode 100644
index 0000000000..a3e83a9687
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain_unix.cpp
@@ -0,0 +1,633 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * *
+ * 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. For licensing and distribution *
+ * details, check the accompanying file 'COPYING'. *
+ *****************************************************************************/
+#include "keychain_p.h"
+#include "gnomekeyring_p.h"
+#include "libsecret_p.h"
+#include "plaintextstore_p.h"
+
+#include <QScopedPointer>
+
+using namespace QKeychain;
+
+enum KeyringBackend {
+ Backend_LibSecretKeyring,
+ Backend_GnomeKeyring,
+ Backend_Kwallet4,
+ Backend_Kwallet5,
+ Backend_Kwallet6,
+};
+
+enum DesktopEnvironment {
+ DesktopEnv_Gnome,
+ DesktopEnv_Kde4,
+ DesktopEnv_Plasma5,
+ DesktopEnv_Plasma6,
+ DesktopEnv_Unity,
+ DesktopEnv_Xfce,
+ DesktopEnv_Other
+};
+
+static constexpr const char KWALLET6_DBUS_IFACE[] = "org.kde.kwalletd6";
+static constexpr const char KWALLET6_DBUS_PATH[] = "/modules/kwalletd6";
+static constexpr const char KWALLET5_DBUS_IFACE[] = "org.kde.kwalletd5";
+static constexpr const char KWALLET5_DBUS_PATH[] = "/modules/kwalletd5";
+static constexpr const char KWALLET4_DBUS_IFACE[] = "org.kde.kwalletd";
+static constexpr const char KWALLET4_DBUS_PATH[] = "/modules/kwalletd";
+
+// the following detection algorithm is derived from chromium,
+// licensed under BSD, see base/nix/xdg_util.cc
+
+static DesktopEnvironment getKdeVersion() {
+ QByteArray value = qgetenv("KDE_SESSION_VERSION");
+ if ( value == "6" ) {
+ return DesktopEnv_Plasma6;
+ } else if ( value == "5" ) {
+ return DesktopEnv_Plasma5;
+ } else if (value == "4" ) {
+ return DesktopEnv_Kde4;
+ } else {
+ // most likely KDE3
+ return DesktopEnv_Other;
+ }
+}
+
+static DesktopEnvironment detectDesktopEnvironment() {
+ QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP");
+ if ( xdgCurrentDesktop == "GNOME" ) {
+ return DesktopEnv_Gnome;
+ } else if ( xdgCurrentDesktop == "Unity" ) {
+ return DesktopEnv_Unity;
+ } else if ( xdgCurrentDesktop == "KDE" ) {
+ return getKdeVersion();
+ } else if ( xdgCurrentDesktop == "XFCE" ) {
+ return DesktopEnv_Xfce;
+ }
+
+ QByteArray desktopSession = qgetenv("DESKTOP_SESSION");
+ if ( desktopSession == "gnome" ) {
+ return DesktopEnv_Gnome;
+ } else if ( desktopSession == "kde" ) {
+ return getKdeVersion();
+ } else if ( desktopSession == "kde4" ) {
+ return DesktopEnv_Kde4;
+ } else if ( desktopSession.contains("xfce") || desktopSession == "xubuntu" ) {
+ return DesktopEnv_Xfce;
+ }
+
+ if ( !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty() ) {
+ return DesktopEnv_Gnome;
+ } else if ( !qgetenv("KDE_FULL_SESSION").isEmpty() ) {
+ return getKdeVersion();
+ }
+
+ return DesktopEnv_Other;
+}
+
+static bool isKwalletAvailable(const char *dbusIface, const char *dbusPath)
+{
+ if (!QDBusConnection::sessionBus().isConnected())
+ return false;
+
+ org::kde::KWallet iface(
+ QLatin1String(dbusIface),
+ QLatin1String(dbusPath),
+ QDBusConnection::sessionBus());
+
+ // At this point iface.isValid() can return false even though the
+ // interface is activatable by making a call. Hence we check whether
+ // a wallet can be opened.
+
+ iface.setTimeout(500);
+ QDBusMessage reply = iface.call(QLatin1String("networkWallet"));
+ return reply.type() == QDBusMessage::ReplyMessage;
+}
+
+static KeyringBackend detectKeyringBackend()
+{
+ /* The secret service dbus api, accessible through libsecret, is supposed
+ * to unify password services.
+ *
+ * Unfortunately at the time of Kubuntu 18.04 the secret service backend
+ * in KDE is gnome-keyring-daemon - using it has several complications:
+ * - the default collection isn't opened on session start, so users need
+ * to manually unlock it when the first application uses it
+ * - it's separate from the kwallet5 keyring, so switching to it means the
+ * existing keyring data can't be accessed anymore
+ *
+ * Thus we still prefer kwallet backends on KDE even if libsecret is
+ * available.
+ */
+
+ switch (detectDesktopEnvironment()) {
+ case DesktopEnv_Kde4:
+ return Backend_Kwallet4;
+
+ case DesktopEnv_Plasma5:
+ if (isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH)) {
+ return Backend_Kwallet5;
+ }
+ if (LibSecretKeyring::isAvailable()) {
+ return Backend_LibSecretKeyring;
+ }
+ if (GnomeKeyring::isAvailable()) {
+ return Backend_GnomeKeyring;
+ }
+ // During startup the keychain backend might just not have started yet
+ return Backend_Kwallet5;
+
+ case DesktopEnv_Plasma6:
+ if (isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH)) {
+ return Backend_Kwallet6;
+ }
+ if (LibSecretKeyring::isAvailable()) {
+ return Backend_LibSecretKeyring;
+ }
+ if (GnomeKeyring::isAvailable()) {
+ return Backend_GnomeKeyring;
+ }
+ // During startup the keychain backend might just not have started yet
+ return Backend_Kwallet6;
+
+ case DesktopEnv_Gnome:
+ case DesktopEnv_Unity:
+ case DesktopEnv_Xfce:
+ case DesktopEnv_Other:
+ default:
+ if (LibSecretKeyring::isAvailable()) {
+ return Backend_LibSecretKeyring;
+ }
+ if (GnomeKeyring::isAvailable()) {
+ return Backend_GnomeKeyring;
+ }
+ if (isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH)) {
+ return Backend_Kwallet6;
+ }
+ if (isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH)) {
+ return Backend_Kwallet5;
+ }
+ // During startup the keychain backend might just not have started yet
+ //
+ // This doesn't need to be libsecret because LibSecretKeyring::isAvailable()
+ // only fails if the libsecret shared library couldn't be loaded. In contrast
+ // to that GnomeKeyring::isAvailable() can return false if the shared library
+ // *was* loaded but its libgnome_keyring::is_available() returned false.
+ //
+ // In the future there should be a difference between "API available" and
+ // "keychain available".
+ return Backend_GnomeKeyring;
+ }
+
+}
+
+static KeyringBackend getKeyringBackend()
+{
+ static KeyringBackend backend = detectKeyringBackend();
+ return backend;
+}
+
+static void kwalletReadPasswordScheduledStartImpl(const char * service, const char * path, ReadPasswordJobPrivate * priv) {
+ if ( QDBusConnection::sessionBus().isConnected() )
+ {
+ priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv );
+ const QDBusPendingReply<QString> reply = priv->iface->networkWallet();
+ QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, priv );
+ priv->connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), priv, SLOT(kwalletWalletFound(QDBusPendingCallWatcher*)) );
+ }
+ else
+ {
+ // D-Bus is not reachable so none can tell us something about KWalletd
+ QDBusError err( QDBusError::NoServer, ReadPasswordJobPrivate::tr("D-Bus is not running") );
+ priv->fallbackOnError( err );
+ }
+}
+
+void ReadPasswordJobPrivate::scheduledStart() {
+ switch ( getKeyringBackend() ) {
+ case Backend_LibSecretKeyring: {
+ if ( !LibSecretKeyring::findPassword(key, q->service(), this) ) {
+ q->emitFinishedWithError( OtherError, tr("Unknown error") );
+ }
+ } break;
+ case Backend_GnomeKeyring:
+ this->mode = JobPrivate::Text;
+ if ( !GnomeKeyring::find_network_password( key.toUtf8().constData(),
+ q->service().toUtf8().constData(),
+ "plaintext",
+ reinterpret_cast<GnomeKeyring::OperationGetStringCallback>( &JobPrivate::gnomeKeyring_readCb ),
+ this, 0 ) )
+ q->emitFinishedWithError( OtherError, tr("Unknown error") );
+ break;
+
+ case Backend_Kwallet4:
+ kwalletReadPasswordScheduledStartImpl(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet5:
+ kwalletReadPasswordScheduledStartImpl(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet6:
+ kwalletReadPasswordScheduledStartImpl(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this);
+ break;
+ }
+}
+
+void JobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher)
+{
+ watcher->deleteLater();
+ const QDBusPendingReply<QString> reply = *watcher;
+ const QDBusPendingReply<int> pendingReply = iface->open( reply.value(), 0, q->service() );
+ QDBusPendingCallWatcher* pendingWatcher = new QDBusPendingCallWatcher( pendingReply, this );
+ connect( pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) );
+}
+
+static QPair<Error, QString> mapGnomeKeyringError( int result )
+{
+ Q_ASSERT( result != GnomeKeyring::RESULT_OK );
+
+ switch ( result ) {
+ case GnomeKeyring::RESULT_DENIED:
+ return qMakePair( AccessDenied, QObject::tr("Access to keychain denied") );
+ case GnomeKeyring::RESULT_NO_KEYRING_DAEMON:
+ return qMakePair( NoBackendAvailable, QObject::tr("No keyring daemon") );
+ case GnomeKeyring::RESULT_ALREADY_UNLOCKED:
+ return qMakePair( OtherError, QObject::tr("Already unlocked") );
+ case GnomeKeyring::RESULT_NO_SUCH_KEYRING:
+ return qMakePair( OtherError, QObject::tr("No such keyring") );
+ case GnomeKeyring::RESULT_BAD_ARGUMENTS:
+ return qMakePair( OtherError, QObject::tr("Bad arguments") );
+ case GnomeKeyring::RESULT_IO_ERROR:
+ return qMakePair( OtherError, QObject::tr("I/O error") );
+ case GnomeKeyring::RESULT_CANCELLED:
+ return qMakePair( OtherError, QObject::tr("Cancelled") );
+ case GnomeKeyring::RESULT_KEYRING_ALREADY_EXISTS:
+ return qMakePair( OtherError, QObject::tr("Keyring already exists") );
+ case GnomeKeyring::RESULT_NO_MATCH:
+ return qMakePair( EntryNotFound, QObject::tr("No match") );
+ default:
+ break;
+ }
+
+ return qMakePair( OtherError, QObject::tr("Unknown error") );
+}
+
+void JobPrivate::gnomeKeyring_readCb( int result, const char* string, JobPrivate* self )
+{
+ if ( result == GnomeKeyring::RESULT_OK ) {
+ if (self->mode == JobPrivate::Text)
+ self->data = QByteArray(string);
+ else
+ self->data = QByteArray::fromBase64(string);
+
+ self->q->emitFinished();
+ } else if (self->mode == JobPrivate::Text) {
+ self->mode = JobPrivate::Binary;
+ if ( !GnomeKeyring::find_network_password( self->key.toUtf8().constData(),
+ self->q->service().toUtf8().constData(),
+ "base64",
+ reinterpret_cast<GnomeKeyring::OperationGetStringCallback>( &JobPrivate::gnomeKeyring_readCb ),
+ self, 0 ) )
+ self->q->emitFinishedWithError( OtherError, tr("Unknown error") );
+ } else {
+ const QPair<Error, QString> errorResult = mapGnomeKeyringError( result );
+ self->q->emitFinishedWithError( errorResult.first, errorResult.second );
+ }
+}
+
+void ReadPasswordJobPrivate::fallbackOnError(const QDBusError& err )
+{
+ PlainTextStore plainTextStore( q->service(), q->settings() );
+
+ if ( q->insecureFallback() && plainTextStore.contains( key ) ) {
+ mode = plainTextStore.readMode( key );
+ data = plainTextStore.readData( key );
+
+ if ( plainTextStore.error() != NoError )
+ q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
+ else
+ q->emitFinished();
+ } else {
+ if ( err.type() == QDBusError::ServiceUnknown ) //KWalletd not running
+ q->emitFinishedWithError( NoBackendAvailable, tr("No keychain service available") );
+ else
+ q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
+ }
+}
+
+void ReadPasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
+ watcher->deleteLater();
+ const QDBusPendingReply<int> reply = *watcher;
+
+ if ( reply.isError() ) {
+ fallbackOnError( reply.error() );
+ return;
+ }
+
+ PlainTextStore plainTextStore( q->service(), q->settings() );
+
+ if ( plainTextStore.contains( key ) ) {
+ // We previously stored data in the insecure QSettings, but now have KWallet available.
+ // Do the migration
+
+ data = plainTextStore.readData( key );
+ const WritePasswordJobPrivate::Mode mode = plainTextStore.readMode( key );
+ plainTextStore.remove( key );
+
+ q->emitFinished();
+
+
+ WritePasswordJob* j = new WritePasswordJob( q->service(), 0 );
+ j->setSettings( q->settings() );
+ j->setKey( key );
+ j->setAutoDelete( true );
+ if ( mode == WritePasswordJobPrivate::Binary )
+ j->setBinaryData( data );
+ else if ( mode == WritePasswordJobPrivate::Text )
+ j->setTextData( QString::fromUtf8( data ) );
+ else
+ Q_ASSERT( false );
+
+ j->start();
+
+ return;
+ }
+
+ walletHandle = reply.value();
+
+ if ( walletHandle < 0 ) {
+ q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
+ return;
+ }
+
+ const QDBusPendingReply<int> nextReply = iface->entryType( walletHandle, q->service(), key, q->service() );
+ QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
+ connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletEntryTypeFinished(QDBusPendingCallWatcher*)) );
+}
+
+//Must be in sync with KWallet::EntryType (kwallet.h)
+enum KWalletEntryType {
+ Unknown=0,
+ Password,
+ Stream,
+ Map
+};
+
+void ReadPasswordJobPrivate::kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ) {
+ watcher->deleteLater();
+ if ( watcher->isError() ) {
+ const QDBusError err = watcher->error();
+ q->emitFinishedWithError( OtherError, tr("Could not determine data type: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
+ return;
+ }
+
+ const QDBusPendingReply<int> reply = *watcher;
+ const int value = reply.value();
+
+ switch ( value ) {
+ case Unknown:
+ q->emitFinishedWithError( EntryNotFound, tr("Entry not found") );
+ return;
+ case Password:
+ mode = Text;
+ break;
+ case Stream:
+ mode = Binary;
+ break;
+ case Map:
+ q->emitFinishedWithError( EntryNotFound, tr("Unsupported entry type 'Map'") );
+ return;
+ default:
+ q->emitFinishedWithError( OtherError, tr("Unknown kwallet entry type '%1'").arg( value ) );
+ return;
+ }
+
+ const QDBusPendingCall nextReply = (mode == Text)
+ ? QDBusPendingCall( iface->readPassword( walletHandle, q->service(), key, q->service() ) )
+ : QDBusPendingCall( iface->readEntry( walletHandle, q->service(), key, q->service() ) );
+ QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
+ connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(QDBusPendingCallWatcher*)) );
+}
+
+void ReadPasswordJobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) {
+ if ( !watcher->isError() ) {
+ if ( mode == Binary ) {
+ QDBusPendingReply<QByteArray> reply = *watcher;
+ if (reply.isValid()) {
+ data = reply.value();
+ }
+ } else {
+ QDBusPendingReply<QString> reply = *watcher;
+ if (reply.isValid()) {
+ data = reply.value().toUtf8();
+ }
+ }
+ }
+
+ JobPrivate::kwalletFinished(watcher);
+}
+
+static void kwalletWritePasswordScheduledStart( const char * service, const char * path, JobPrivate * priv ) {
+ if ( QDBusConnection::sessionBus().isConnected() )
+ {
+ priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv );
+ const QDBusPendingReply<QString> reply = priv->iface->networkWallet();
+ QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, priv );
+ priv->connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), priv, SLOT(kwalletWalletFound(QDBusPendingCallWatcher*)) );
+ }
+ else
+ {
+ // D-Bus is not reachable so none can tell us something about KWalletd
+ QDBusError err( QDBusError::NoServer, WritePasswordJobPrivate::tr("D-Bus is not running") );
+ priv->fallbackOnError( err );
+ }
+}
+
+void WritePasswordJobPrivate::scheduledStart() {
+ switch ( getKeyringBackend() ) {
+ case Backend_LibSecretKeyring: {
+ if ( !LibSecretKeyring::writePassword(service, key, service, mode,
+ data, this) ) {
+ q->emitFinishedWithError( OtherError, tr("Unknown error") );
+ }
+ } break;
+ case Backend_GnomeKeyring: {
+ QString type;
+ QByteArray password;
+
+ switch(mode) {
+ case JobPrivate::Text:
+ type = QLatin1String("plaintext");
+ password = data;
+ break;
+ default:
+ type = QLatin1String("base64");
+ password = data.toBase64();
+ break;
+ }
+
+ QByteArray service = q->service().toUtf8();
+ if ( !GnomeKeyring::store_network_password( GnomeKeyring::GNOME_KEYRING_DEFAULT,
+ service.constData(),
+ key.toUtf8().constData(),
+ service.constData(),
+ type.toUtf8().constData(),
+ password.constData(),
+ reinterpret_cast<GnomeKeyring::OperationDoneCallback>( &JobPrivate::gnomeKeyring_writeCb ),
+ this, 0 ) )
+ q->emitFinishedWithError( OtherError, tr("Unknown error") );
+ }
+ break;
+
+ case Backend_Kwallet4:
+ kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet5:
+ kwalletWritePasswordScheduledStart(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet6:
+ kwalletWritePasswordScheduledStart(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this);
+ break;
+ }
+}
+
+void WritePasswordJobPrivate::fallbackOnError(const QDBusError &err)
+{
+ if ( !q->insecureFallback() ) {
+ q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
+ return;
+ }
+
+ PlainTextStore plainTextStore( q->service(), q->settings() );
+ plainTextStore.write( key, data, mode );
+
+ if ( plainTextStore.error() != NoError )
+ q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
+ else
+ q->emitFinished();
+}
+
+void JobPrivate::gnomeKeyring_writeCb(int result, JobPrivate* self )
+{
+ if ( result == GnomeKeyring::RESULT_OK ) {
+ self->q->emitFinished();
+ } else {
+ const QPair<Error, QString> errorResult = mapGnomeKeyringError( result );
+ self->q->emitFinishedWithError( errorResult.first, errorResult.second );
+ }
+}
+
+void JobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
+ watcher->deleteLater();
+ QDBusPendingReply<int> reply = *watcher;
+
+ if ( reply.isError() ) {
+ fallbackOnError( reply.error() );
+ return;
+ }
+
+ PlainTextStore plainTextStore( q->service(), q->settings() );
+ if ( plainTextStore.contains( key ) ) {
+ // If we had previously written to QSettings, but we now have a kwallet available, migrate and delete old insecure data
+ plainTextStore.remove( key );
+ }
+
+ const int handle = reply.value();
+
+ if ( handle < 0 ) {
+ q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
+ return;
+ }
+
+ QDBusPendingReply<int> nextReply;
+
+ if ( !data.isNull() ) {
+ if ( mode == Text ) {
+ nextReply = iface->writePassword( handle, q->service(), key, QString::fromUtf8(data), q->service() );
+ } else {
+ Q_ASSERT( mode == Binary );
+ nextReply = iface->writeEntry( handle, q->service(), key, data, q->service() );
+ }
+ } else {
+ nextReply = iface->removeEntry( handle, q->service(), key, q->service() );
+ }
+
+ QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
+ connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(QDBusPendingCallWatcher*)) );
+}
+
+void JobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) {
+ if ( !watcher->isError() ) {
+ if ( mode == Binary ) {
+ QDBusPendingReply<QByteArray> reply = *watcher;
+ if (reply.isValid()) {
+ data = reply.value();
+ }
+ } else {
+ QDBusPendingReply<QString> reply = *watcher;
+ if (reply.isValid()) {
+ data = reply.value().toUtf8();
+ }
+ }
+ }
+
+ q->emitFinished();
+}
+
+void DeletePasswordJobPrivate::scheduledStart() {
+ switch ( getKeyringBackend() ) {
+ case Backend_LibSecretKeyring: {
+ if ( !LibSecretKeyring::deletePassword(key, q->service(), this) ) {
+ q->emitFinishedWithError( OtherError, tr("Unknown error") );
+ }
+ } break;
+ case Backend_GnomeKeyring: {
+ if ( !GnomeKeyring::delete_network_password(
+ key.toUtf8().constData(), q->service().toUtf8().constData(),
+ reinterpret_cast<GnomeKeyring::OperationDoneCallback>( &JobPrivate::gnomeKeyring_writeCb ),
+ this, 0 ) )
+ q->emitFinishedWithError( OtherError, tr("Unknown error") );
+ }
+ break;
+
+ case Backend_Kwallet4:
+ kwalletWritePasswordScheduledStart(KWALLET4_DBUS_IFACE, KWALLET4_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet5:
+ kwalletWritePasswordScheduledStart(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH, this);
+ break;
+ case Backend_Kwallet6:
+ kwalletWritePasswordScheduledStart(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH, this);
+ break;
+ }
+}
+
+void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) {
+ QScopedPointer<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
+ QSettings* actual = q->settings() ? q->settings() : local.data();
+
+ if ( !q->insecureFallback() ) {
+ q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2")
+ .arg( QDBusError::errorString( err.type() ), err.message() ) );
+ return;
+ }
+
+ actual->remove( key );
+ actual->sync();
+
+ q->emitFinished();
+
+
+ q->emitFinished();
+}
+
+bool QKeychain::isAvailable()
+{
+ return LibSecretKeyring::isAvailable() || GnomeKeyring::isAvailable()
+ || isKwalletAvailable(KWALLET6_DBUS_IFACE, KWALLET6_DBUS_PATH)
+ || isKwalletAvailable(KWALLET5_DBUS_IFACE, KWALLET5_DBUS_PATH);
+}
diff --git a/src/libs/3rdparty/qtkeychain/keychain_win.cpp b/src/libs/3rdparty/qtkeychain/keychain_win.cpp
new file mode 100644
index 0000000000..98dd34cbab
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/keychain_win.cpp
@@ -0,0 +1,193 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * *
+ * 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. For licensing and distribution *
+ * details, check the accompanying file 'COPYING'. *
+ *****************************************************************************/
+#include "keychain_p.h"
+#include "plaintextstore_p.h"
+
+#include <windows.h>
+#include <wincrypt.h>
+
+#include <memory>
+
+using namespace QKeychain;
+
+#if defined(USE_CREDENTIAL_STORE)
+#include <wincred.h>
+
+void ReadPasswordJobPrivate::scheduledStart() {
+ LPCWSTR name = (LPCWSTR)key.utf16();
+ PCREDENTIALW cred;
+
+ if (!CredReadW(name, CRED_TYPE_GENERIC, 0, &cred)) {
+ Error err;
+ QString msg;
+ switch(GetLastError()) {
+ case ERROR_NOT_FOUND:
+ err = EntryNotFound;
+ msg = tr("Password entry not found");
+ break;
+ default:
+ err = OtherError;
+ msg = tr("Could not decrypt data");
+ break;
+ }
+
+ q->emitFinishedWithError( err, msg );
+ return;
+ }
+
+ data = QByteArray((char*)cred->CredentialBlob, cred->CredentialBlobSize);
+ CredFree(cred);
+
+ q->emitFinished();
+}
+
+void WritePasswordJobPrivate::scheduledStart() {
+ CREDENTIALW cred;
+ char *pwd = data.data();
+ LPWSTR name = (LPWSTR)key.utf16();
+
+ memset(&cred, 0, sizeof(cred));
+ cred.Comment = const_cast<wchar_t*>(L"QtKeychain");
+ cred.Type = CRED_TYPE_GENERIC;
+ cred.TargetName = name;
+ cred.CredentialBlobSize = data.size();
+ cred.CredentialBlob = (LPBYTE)pwd;
+ cred.Persist = CRED_PERSIST_ENTERPRISE;
+
+ if (CredWriteW(&cred, 0)) {
+ q->emitFinished();
+ return;
+ }
+
+ DWORD err = GetLastError();
+
+ // Detect size-exceeded errors and provide nicer messages.
+ // Unfortunately these error codes aren't documented.
+ // Found empirically on Win10 1803 build 17134.523.
+ if (err == RPC_X_BAD_STUB_DATA) {
+ const size_t maxBlob = CRED_MAX_CREDENTIAL_BLOB_SIZE;
+ if (cred.CredentialBlobSize > maxBlob) {
+ q->emitFinishedWithError(
+ OtherError,
+ tr("Credential size exceeds maximum size of %1").arg(maxBlob));
+ return;
+ }
+ }
+ if (err == RPC_S_INVALID_BOUND) {
+ const size_t maxTargetName = CRED_MAX_GENERIC_TARGET_NAME_LENGTH;
+ if (key.size() > maxTargetName) {
+ q->emitFinishedWithError(
+ OtherError,
+ tr("Credential key exceeds maximum size of %1").arg(maxTargetName));
+ return;
+ }
+ }
+
+ q->emitFinishedWithError( OtherError, tr("Writing credentials failed: Win32 error code %1").arg(err) );
+}
+
+void DeletePasswordJobPrivate::scheduledStart() {
+ LPCWSTR name = (LPCWSTR)key.utf16();
+
+ if (!CredDeleteW(name, CRED_TYPE_GENERIC, 0)) {
+ Error err;
+ QString msg;
+ switch(GetLastError()) {
+ case ERROR_NOT_FOUND:
+ err = EntryNotFound;
+ msg = tr("Password entry not found");
+ break;
+ default:
+ err = OtherError;
+ msg = tr("Could not decrypt data");
+ break;
+ }
+
+ q->emitFinishedWithError( err, msg );
+ } else {
+ q->emitFinished();
+ }
+}
+#else
+void ReadPasswordJobPrivate::scheduledStart() {
+ PlainTextStore plainTextStore( q->service(), q->settings() );
+ QByteArray encrypted = plainTextStore.readData( key );
+ if ( plainTextStore.error() != NoError ) {
+ q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
+ return;
+ }
+
+ DATA_BLOB blob_in, blob_out;
+
+ blob_in.pbData = reinterpret_cast<BYTE*>( encrypted.data() );
+ blob_in.cbData = encrypted.size();
+
+ const BOOL ret = CryptUnprotectData( &blob_in,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ &blob_out );
+ if ( !ret ) {
+ q->emitFinishedWithError( OtherError, tr("Could not decrypt data") );
+ return;
+ }
+
+ data = QByteArray( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
+ SecureZeroMemory( blob_out.pbData, blob_out.cbData );
+ LocalFree( blob_out.pbData );
+
+ q->emitFinished();
+}
+
+void WritePasswordJobPrivate::scheduledStart() {
+ DATA_BLOB blob_in, blob_out;
+ blob_in.pbData = reinterpret_cast<BYTE*>( data.data() );
+ blob_in.cbData = data.size();
+ const BOOL res = CryptProtectData( &blob_in,
+ L"QKeychain-encrypted data",
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ &blob_out );
+ if ( !res ) {
+ q->emitFinishedWithError( OtherError, tr("Encryption failed") ); //TODO more details available?
+ return;
+ }
+
+ const QByteArray encrypted( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
+ LocalFree( blob_out.pbData );
+
+ PlainTextStore plainTextStore( q->service(), q->settings() );
+ plainTextStore.write( key, encrypted, Binary );
+ if ( plainTextStore.error() != NoError ) {
+ q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
+ return;
+ }
+
+ q->emitFinished();
+}
+
+void DeletePasswordJobPrivate::scheduledStart() {
+ PlainTextStore plainTextStore( q->service(), q->settings() );
+ plainTextStore.remove( key );
+ if ( plainTextStore.error() != NoError ) {
+ q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
+ } else {
+ q->emitFinished();
+ }
+}
+#endif
+
+bool QKeychain::isAvailable()
+{
+ return true;
+}
diff --git a/src/libs/3rdparty/qtkeychain/libsecret.cpp b/src/libs/3rdparty/qtkeychain/libsecret.cpp
new file mode 100644
index 0000000000..6ee024989e
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/libsecret.cpp
@@ -0,0 +1,349 @@
+#if defined(HAVE_LIBSECRET)
+
+#undef signals
+#include <libsecret/secret.h>
+#define signals
+
+#endif
+
+#include "libsecret_p.h"
+
+
+#include <QLibrary>
+
+#if defined(HAVE_LIBSECRET)
+const SecretSchema* qtkeychainSchema(void) {
+ static const SecretSchema schema = {
+ "org.qt.keychain", SECRET_SCHEMA_DONT_MATCH_NAME,
+ {
+ { "user", SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { "server", SECRET_SCHEMA_ATTRIBUTE_STRING },
+ { "type", SECRET_SCHEMA_ATTRIBUTE_STRING }
+ }
+ };
+
+ return &schema;
+}
+
+typedef struct {
+ QKeychain::JobPrivate *self;
+ QString user;
+ QString server;
+} callbackArg;
+
+typedef void (*secret_password_lookup_t) (const SecretSchema *schema,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ ...) G_GNUC_NULL_TERMINATED;
+typedef gchar *(*secret_password_lookup_finish_t) (GAsyncResult *result,
+ GError **error);
+typedef void (*secret_password_store_t) (const SecretSchema *schema,
+ const gchar *collection,
+ const gchar *label,
+ const gchar *password,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ ...) G_GNUC_NULL_TERMINATED;
+typedef gboolean (*secret_password_store_finish_t) (GAsyncResult *result,
+ GError **error);
+typedef void (*secret_password_clear_t) (const SecretSchema *schema,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ ...) G_GNUC_NULL_TERMINATED;
+typedef gboolean (*secret_password_clear_finish_t) (GAsyncResult *result,
+ GError **error);
+typedef void (*secret_password_free_t) (gchar *password);
+typedef GQuark (*secret_error_get_quark_t) (void) G_GNUC_CONST;
+
+static secret_password_lookup_t secret_password_lookup_fn = nullptr;
+static secret_password_lookup_finish_t secret_password_lookup_finish_fn = nullptr;
+static secret_password_store_t secret_password_store_fn = nullptr;
+static secret_password_store_finish_t secret_password_store_finish_fn = nullptr;
+static secret_password_clear_t secret_password_clear_fn = nullptr;
+static secret_password_clear_finish_t secret_password_clear_finish_fn = nullptr;
+static secret_password_free_t secret_password_free_fn = nullptr;
+static secret_error_get_quark_t secret_error_get_quark_fn = nullptr;
+
+static QKeychain::Error gerrorToCode(const GError *error) {
+ if (error->domain != secret_error_get_quark_fn()) {
+ return QKeychain::OtherError;
+ }
+
+ switch(error->code) {
+ case SECRET_ERROR_NO_SUCH_OBJECT:
+ return QKeychain::EntryNotFound;
+ case SECRET_ERROR_IS_LOCKED:
+ return QKeychain::AccessDenied;
+ default:
+ return QKeychain::OtherError;
+ }
+}
+
+static void
+on_password_lookup (GObject *source,
+ GAsyncResult *result,
+ gpointer inst)
+{
+ GError *error = nullptr;
+ callbackArg *arg = (callbackArg*)inst;
+ gchar *password = secret_password_lookup_finish_fn (result, &error);
+
+ Q_UNUSED(source);
+
+ if (arg) {
+ if (error) {
+ QKeychain::Error code = gerrorToCode(error);
+
+ arg->self->q->emitFinishedWithError( code, QString::fromUtf8(error->message) );
+ } else {
+ if (password) {
+ QByteArray raw = QByteArray(password);
+ switch(arg->self->mode) {
+ case QKeychain::JobPrivate::Binary:
+ arg->self->data = QByteArray::fromBase64(raw);
+ break;
+ case QKeychain::JobPrivate::Text:
+ default:
+ arg->self->data = raw;
+ }
+
+ arg->self->q->emitFinished();
+ } else if (arg->self->mode == QKeychain::JobPrivate::Text) {
+ arg->self->mode = QKeychain::JobPrivate::Binary;
+ secret_password_lookup_fn (qtkeychainSchema(), nullptr,
+ on_password_lookup, arg,
+ "user", arg->user.toUtf8().constData(),
+ "server", arg->server.toUtf8().constData(),
+ "type", "base64",
+ nullptr);
+ return;
+ } else {
+ arg->self->q->emitFinishedWithError( QKeychain::EntryNotFound, QObject::tr("Entry not found") );
+ }
+ }
+ }
+ if (error) {
+ g_error_free (error);
+ }
+
+ if (password) {
+ secret_password_free_fn (password);
+ }
+
+ if (arg) {
+ delete arg;
+ }
+}
+
+static void
+on_password_stored (GObject *source,
+ GAsyncResult *result,
+ gpointer inst)
+{
+ GError *error = nullptr;
+ QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst;
+
+ Q_UNUSED(source);
+
+ secret_password_store_finish_fn (result, &error);
+
+ if (self) {
+ if (error) {
+ self->q->emitFinishedWithError( gerrorToCode(error),
+ QString::fromUtf8(error->message) );
+ } else {
+ self->q->emitFinished();
+ }
+ }
+ if (error) {
+ g_error_free (error);
+ }
+}
+
+static void
+on_password_cleared (GObject *source,
+ GAsyncResult *result,
+ gpointer inst)
+{
+ GError *error = nullptr;
+ QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst;
+ gboolean removed = secret_password_clear_finish_fn (result, &error);
+
+ Q_UNUSED(source);
+ if (self) {
+ if ( error ) {
+ self->q->emitFinishedWithError( gerrorToCode(error),
+ QString::fromUtf8(error->message) );
+ } else {
+ Q_UNUSED(removed);
+ self->q->emitFinished();
+ }
+ }
+ if (error) {
+ g_error_free (error);
+ }
+}
+
+static QString modeToString(QKeychain::JobPrivate::Mode mode) {
+ switch(mode) {
+ case QKeychain::JobPrivate::Binary:
+ return "base64";
+ default:
+ return "plaintext";
+ }
+}
+#endif
+
+bool LibSecretKeyring::isAvailable() {
+#if defined(HAVE_LIBSECRET)
+ const LibSecretKeyring& keyring = instance();
+ if (!keyring.isLoaded())
+ return false;
+ if (secret_password_lookup_fn == nullptr)
+ return false;
+ if (secret_password_lookup_finish_fn == nullptr)
+ return false;
+ if (secret_password_store_fn == nullptr)
+ return false;
+ if (secret_password_store_finish_fn == nullptr)
+ return false;
+ if (secret_password_clear_fn == nullptr)
+ return false;
+ if (secret_password_clear_finish_fn == nullptr)
+ return false;
+ if (secret_password_free_fn == nullptr)
+ return false;
+ if (secret_error_get_quark_fn == nullptr)
+ return false;
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool LibSecretKeyring::findPassword(const QString &user, const QString &server,
+ QKeychain::JobPrivate *self)
+{
+#if defined(HAVE_LIBSECRET)
+ if (!isAvailable()) {
+ return false;
+ }
+
+ self->mode = QKeychain::JobPrivate::Text;
+ self->data = QByteArray();
+
+ callbackArg *arg = new callbackArg;
+ arg->self = self;
+ arg->user = user;
+ arg->server = server;
+
+ secret_password_lookup_fn (qtkeychainSchema(), nullptr, on_password_lookup, arg,
+ "user", user.toUtf8().constData(),
+ "server", server.toUtf8().constData(),
+ "type", "plaintext",
+ nullptr);
+ return true;
+#else
+ Q_UNUSED(user)
+ Q_UNUSED(server)
+ Q_UNUSED(self)
+ return false;
+#endif
+}
+
+bool LibSecretKeyring::writePassword(const QString &display_name,
+ const QString &user,
+ const QString &server,
+ const QKeychain::JobPrivate::Mode mode,
+ const QByteArray &password,
+ QKeychain::JobPrivate *self)
+{
+#if defined(HAVE_LIBSECRET)
+ if (!isAvailable()) {
+ return false;
+ }
+
+ QString type = modeToString(mode);
+ QByteArray pwd;
+ switch(mode) {
+ case QKeychain::JobPrivate::Binary:
+ pwd = password.toBase64();
+ break;
+ default:
+ pwd = password;
+ break;
+ }
+
+ secret_password_store_fn (qtkeychainSchema(), SECRET_COLLECTION_DEFAULT,
+ display_name.toUtf8().constData(),
+ pwd.constData(), nullptr, on_password_stored, self,
+ "user", user.toUtf8().constData(),
+ "server", server.toUtf8().constData(),
+ "type", type.toUtf8().constData(),
+ nullptr);
+ return true;
+#else
+ Q_UNUSED(display_name)
+ Q_UNUSED(user)
+ Q_UNUSED(server)
+ Q_UNUSED(mode)
+ Q_UNUSED(password)
+ Q_UNUSED(self)
+ return false;
+#endif
+}
+
+bool LibSecretKeyring::deletePassword(const QString &key, const QString &service,
+ QKeychain::JobPrivate* self)
+{
+#if defined(HAVE_LIBSECRET)
+ if (!isAvailable()) {
+ return false;
+ }
+
+ secret_password_clear_fn (qtkeychainSchema(), nullptr, on_password_cleared, self,
+ "user", key.toUtf8().constData(),
+ "server", service.toUtf8().constData(),
+ nullptr);
+ return true;
+#else
+ Q_UNUSED(key)
+ Q_UNUSED(service)
+ Q_UNUSED(self)
+ return false;
+#endif
+}
+
+LibSecretKeyring::LibSecretKeyring()
+ : QLibrary(QLatin1String("secret-1"), 0)
+{
+#ifdef HAVE_LIBSECRET
+ if (load()) {
+ secret_password_lookup_fn =
+ (secret_password_lookup_t)resolve("secret_password_lookup");
+ secret_password_lookup_finish_fn =
+ (secret_password_lookup_finish_t)resolve("secret_password_lookup_finish");
+ secret_password_store_fn =
+ (secret_password_store_t)resolve("secret_password_store");
+ secret_password_store_finish_fn =
+ (secret_password_store_finish_t)resolve("secret_password_store_finish");
+ secret_password_clear_fn =
+ (secret_password_clear_t)resolve("secret_password_clear");
+ secret_password_clear_finish_fn =
+ (secret_password_clear_finish_t)resolve("secret_password_clear_finish");
+ secret_password_free_fn =
+ (secret_password_free_t)resolve("secret_password_free");
+ secret_error_get_quark_fn =
+ (secret_error_get_quark_t)resolve("secret_error_get_quark");
+ }
+#endif
+}
+
+LibSecretKeyring &LibSecretKeyring::instance() {
+ static LibSecretKeyring instance;
+
+ return instance;
+}
diff --git a/src/libs/3rdparty/qtkeychain/libsecret_p.h b/src/libs/3rdparty/qtkeychain/libsecret_p.h
new file mode 100644
index 0000000000..bd966fe0f7
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/libsecret_p.h
@@ -0,0 +1,33 @@
+#ifndef QTKEYCHAIN_LIBSECRET_P_H
+#define QTKEYCHAIN_LIBSECRET_P_H
+
+#include <QLibrary>
+
+#include "keychain_p.h"
+
+class LibSecretKeyring : public QLibrary {
+public:
+ static bool isAvailable();
+
+ static bool findPassword(const QString& user,
+ const QString& server,
+ QKeychain::JobPrivate* self);
+
+ static bool writePassword(const QString& display_name,
+ const QString& user,
+ const QString& server,
+ const QKeychain::JobPrivate::Mode type,
+ const QByteArray& password,
+ QKeychain::JobPrivate* self);
+
+ static bool deletePassword(const QString &key, const QString &service,
+ QKeychain::JobPrivate* self);
+
+private:
+ LibSecretKeyring();
+
+ static LibSecretKeyring &instance();
+};
+
+
+#endif
diff --git a/src/libs/3rdparty/qtkeychain/org.kde.KWallet.xml b/src/libs/3rdparty/qtkeychain/org.kde.KWallet.xml
new file mode 100644
index 0000000000..ec4bd6e9c3
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/org.kde.KWallet.xml
@@ -0,0 +1,276 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.kde.KWallet">
+ <signal name="walletListDirty">
+ </signal>
+ <signal name="walletCreated">
+ <arg name="wallet" type="s" direction="out"/>
+ </signal>
+ <signal name="walletOpened">
+ <arg name="wallet" type="s" direction="out"/>
+ </signal>
+ <signal name="walletAsyncOpened">
+ <arg name="tId" type="i" direction="out"/>
+ <arg name="handle" type="i" direction="out"/>
+ </signal>
+ <signal name="walletDeleted">
+ <arg name="wallet" type="s" direction="out"/>
+ </signal>
+ <signal name="walletClosed">
+ <arg name="wallet" type="s" direction="out"/>
+ </signal>
+ <signal name="walletClosed">
+ <arg name="handle" type="i" direction="out"/>
+ </signal>
+ <signal name="allWalletsClosed">
+ </signal>
+ <signal name="folderListUpdated">
+ <arg name="wallet" type="s" direction="out"/>
+ </signal>
+ <signal name="folderUpdated">
+ <arg type="s" direction="out"/>
+ <arg type="s" direction="out"/>
+ </signal>
+ <signal name="applicationDisconnected">
+ <arg name="wallet" type="s" direction="out"/>
+ <arg name="application" type="s" direction="out"/>
+ </signal>
+ <method name="isEnabled">
+ <arg type="b" direction="out"/>
+ </method>
+ <method name="open">
+ <arg type="i" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="wId" type="x" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="openPath">
+ <arg type="i" direction="out"/>
+ <arg name="path" type="s" direction="in"/>
+ <arg name="wId" type="x" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="openAsync">
+ <arg type="i" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="wId" type="x" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ <arg name="handleSession" type="b" direction="in"/>
+ </method>
+ <method name="openPathAsync">
+ <arg type="i" direction="out"/>
+ <arg name="path" type="s" direction="in"/>
+ <arg name="wId" type="x" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ <arg name="handleSession" type="b" direction="in"/>
+ </method>
+ <method name="close">
+ <arg type="i" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="force" type="b" direction="in"/>
+ </method>
+ <method name="close">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="force" type="b" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="sync">
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
+ </method>
+ <method name="deleteWallet">
+ <arg type="i" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ </method>
+ <method name="isOpen">
+ <arg type="b" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ </method>
+ <method name="isOpen">
+ <arg type="b" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ </method>
+ <method name="users">
+ <arg type="as" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ </method>
+ <method name="changePassword">
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="wId" type="x" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="wallets">
+ <arg type="as" direction="out"/>
+ </method>
+ <method name="folderList">
+ <arg type="as" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="hasFolder">
+ <arg type="b" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="createFolder">
+ <arg type="b" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="removeFolder">
+ <arg type="b" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="entryList">
+ <arg type="as" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readEntry">
+ <arg type="ay" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readMap">
+ <arg type="ay" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readPassword">
+ <arg type="s" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readEntryList">
+ <arg type="a{sv}" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readMapList">
+ <arg type="a{sv}" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="readPasswordList">
+ <arg type="a{sv}" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="renameEntry">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="oldName" type="s" direction="in"/>
+ <arg name="newName" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="writeEntry">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="value" type="ay" direction="in"/>
+ <arg name="entryType" type="i" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="writeEntry">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="value" type="ay" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="writeMap">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="value" type="ay" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="writePassword">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="value" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="hasEntry">
+ <arg type="b" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="entryType">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="removeEntry">
+ <arg type="i" direction="out"/>
+ <arg name="handle" type="i" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ <arg name="appid" type="s" direction="in"/>
+ </method>
+ <method name="disconnectApplication">
+ <arg type="b" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="application" type="s" direction="in"/>
+ </method>
+ <method name="reconfigure">
+ </method>
+ <method name="folderDoesNotExist">
+ <arg type="b" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ </method>
+ <method name="keyDoesNotExist">
+ <arg type="b" direction="out"/>
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="folder" type="s" direction="in"/>
+ <arg name="key" type="s" direction="in"/>
+ </method>
+ <method name="closeAllWallets">
+ </method>
+ <method name="networkWallet">
+ <arg type="s" direction="out"/>
+ </method>
+ <method name="localWallet">
+ <arg type="s" direction="out"/>
+ </method>
+ <method name="pamOpen">
+ <arg name="wallet" type="s" direction="in"/>
+ <arg name="passwordHash" type="ay" direction="in"/>
+ <arg name="sessionTimeout" type="i" direction="in"/>
+ <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
+ </method>
+ </interface>
+</node>
diff --git a/src/libs/3rdparty/qtkeychain/plaintextstore.cpp b/src/libs/3rdparty/qtkeychain/plaintextstore.cpp
new file mode 100644
index 0000000000..b9d027264d
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/plaintextstore.cpp
@@ -0,0 +1,110 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * Copyright (C) 2016 Mathias Hasselmann <mathias.hasselmann@kdab.com> *
+ * *
+ * 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. For licensing and distribution *
+ * details, check the accompanying file 'COPYING'. *
+ *****************************************************************************/
+
+#include "plaintextstore_p.h"
+
+using namespace QKeychain;
+
+namespace {
+#ifdef Q_OS_WIN
+inline QString dataKey(const QString &key) { return key; }
+#else // Q_OS_WIN
+inline QString dataKey(const QString &key) { return key + QLatin1String("/data"); }
+inline QString typeKey(const QString &key) { return key + QLatin1String("/type"); }
+#endif // Q_OS_WIN
+}
+
+
+PlainTextStore::PlainTextStore(const QString &service, QSettings *settings)
+ : m_localSettings(settings ? 0 : new QSettings(service))
+ , m_actualSettings(settings ? settings : m_localSettings.data())
+ , m_error(NoError)
+{
+}
+
+bool PlainTextStore::contains(const QString &key) const
+{
+ return m_actualSettings->contains(dataKey(key));
+}
+
+QByteArray PlainTextStore::readData(const QString &key)
+{
+ return read(dataKey(key)).toByteArray();
+}
+
+#ifndef Q_OS_WIN
+
+JobPrivate::Mode PlainTextStore::readMode(const QString &key)
+{
+ return JobPrivate::stringToMode(read(typeKey(key)).toString());
+}
+
+#endif // Q_OS_WIN
+
+void PlainTextStore::write(const QString &key, const QByteArray &data, JobPrivate::Mode mode)
+{
+ if (m_actualSettings->status() != QSettings::NoError)
+ return;
+
+#ifndef Q_OS_WIN
+ m_actualSettings->setValue(typeKey(key), JobPrivate::modeToString(mode));
+#else // Q_OS_WIN
+ Q_UNUSED(mode);
+#endif // Q_OS_WIN
+ m_actualSettings->setValue(dataKey(key), data);
+ m_actualSettings->sync();
+
+ if (m_actualSettings->status() == QSettings::AccessError) {
+ setError(AccessDenied, tr("Could not store data in settings: access error"));
+ } else if (m_actualSettings->status() != QSettings::NoError) {
+ setError(OtherError, tr("Could not store data in settings: format error"));
+ } else {
+ setError(NoError, QString());
+ }
+}
+
+void PlainTextStore::remove(const QString &key)
+{
+ if (m_actualSettings->status() != QSettings::NoError)
+ return;
+
+#ifndef Q_OS_WIN
+ m_actualSettings->remove(typeKey(key));
+#endif // Q_OS_WIN
+ m_actualSettings->remove(dataKey(key));
+ m_actualSettings->sync();
+
+ if (m_actualSettings->status() == QSettings::AccessError) {
+ setError(AccessDenied, tr("Could not delete data from settings: access error"));
+ } else if (m_actualSettings->status() != QSettings::NoError) {
+ setError(OtherError, tr("Could not delete data from settings: format error"));
+ } else {
+ setError(NoError, QString());
+ }
+}
+
+void PlainTextStore::setError(Error error, const QString &errorString)
+{
+ m_error = error;
+ m_errorString = errorString;
+}
+
+QVariant PlainTextStore::read(const QString &key)
+{
+ const QVariant value = m_actualSettings->value(key);
+
+ if (value.isNull()) {
+ setError(EntryNotFound, tr("Entry not found"));
+ } else {
+ setError(NoError, QString());
+ }
+
+ return value;
+}
diff --git a/src/libs/3rdparty/qtkeychain/plaintextstore_p.h b/src/libs/3rdparty/qtkeychain/plaintextstore_p.h
new file mode 100644
index 0000000000..7ec05aaca1
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/plaintextstore_p.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
+ * Copyright (C) 2016 Mathias Hasselmann <mathias.hasselmann@kdab.com> *
+ * *
+ * 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. For licensing and distribution *
+ * details, check the accompanying file 'COPYING'. *
+ *****************************************************************************/
+
+#ifndef QTKEYCHAIN_PLAINTEXTSTORE_P_H
+#define QTKEYCHAIN_PLAINTEXTSTORE_P_H
+
+#include "keychain_p.h"
+
+namespace QKeychain {
+
+class PlainTextStore {
+ Q_DECLARE_TR_FUNCTIONS(QKeychain::PlainTextStore)
+
+public:
+ explicit PlainTextStore(const QString &service, QSettings *settings);
+
+ Error error() const { return m_error; }
+ QString errorString() const { return m_errorString; }
+
+ bool contains(const QString &key) const;
+
+ QByteArray readData(const QString &key);
+ JobPrivate::Mode readMode(const QString &key);
+
+ void write(const QString &key, const QByteArray &data, JobPrivate::Mode mode);
+ void remove(const QString &key);
+
+private:
+ void setError(Error error, const QString &errorString);
+ QVariant read(const QString &key);
+
+ const QScopedPointer<QSettings> m_localSettings;
+ QSettings *const m_actualSettings;
+ QString m_errorString;
+ Error m_error;
+};
+
+}
+
+#endif // QTKEYCHAIN_PLAINTEXTSTORE_P_H
diff --git a/src/libs/3rdparty/qtkeychain/qkeychain_export.h b/src/libs/3rdparty/qtkeychain/qkeychain_export.h
new file mode 100644
index 0000000000..14dc72287f
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/qkeychain_export.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <qglobal.h>
+
+#if defined(QTKEYCHAIN_LIBRARY)
+# define QKEYCHAIN_EXPORT Q_DECL_EXPORT
+#elif defined(QTKEYCHAIN_STATIC_LIBRARY)
+# define QKEYCHAIN_EXPORT
+#else
+# define QKEYCHAIN_EXPORT Q_DECL_IMPORT
+#endif
diff --git a/src/libs/3rdparty/qtkeychain/qtkeychain.qbs b/src/libs/3rdparty/qtkeychain/qtkeychain.qbs
new file mode 100644
index 0000000000..b5f8e7dc13
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/qtkeychain.qbs
@@ -0,0 +1,92 @@
+QtcLibrary {
+ name: "qtkeychain"
+
+ property bool useWinCredentialsStore: qbs.targetOS.contains("windows")
+
+ Depends { name: "cpp" }
+ Depends { name: "Qt.core" }
+ Depends { name: "Qt.dbus"; condition: qbs.targetOS.contains("linux") }
+ Depends { name: "libsecret-1"; required: false }
+
+ cpp.defines: base.concat(["QTKEYCHAIN_LIBRARY"])
+
+ Properties {
+ condition: useWinCredentialsStore
+ cpp.defines: outer.concat(["USE_CREDENTIAL_STORE=1"])
+ cpp.dynamicLibraries: ["advapi32"]
+ }
+
+ Properties {
+ condition: qbs.targetOS.contains("windows") && !useWinCredentialsStore
+ cpp.dynamicLibraries: ["crypt32"]
+ }
+
+ Properties {
+ condition: qbs.targetOS.contains("macos")
+ cpp.frameworks: [ "Foundation", "Security" ]
+ }
+
+ files: [
+ "keychain.cpp",
+ "keychain.h",
+ "keychain_p.h",
+ "qkeychain_export.h",
+ ]
+
+ Group {
+ name: "qtkeychain Windows files"
+ condition: qbs.targetOS.contains("windows")
+ files: [
+ "keychain_win.cpp",
+ "plaintextstore_p.h",
+ ]
+
+ Group {
+ name: "qtkeychain Windows no credentials store"
+ condition: !product.useWinCredentialsStore
+ files: [ "plaintextstore.cpp" ]
+ }
+ }
+
+ Group {
+ name: "qtkeychain macOS files"
+ condition: qbs.targetOS.contains("macos")
+ files: [ "keychain_apple.mm" ]
+ }
+
+ Group {
+ name: "qtkeychain Linux files"
+ condition: qbs.targetOS.contains("linux")
+
+ Group {
+ name: "qtkeychain libsecret support"
+ condition: "libsecret-1".present
+ cpp.defines: outer.concat(["HAVE_LIBSECRET=1"])
+ }
+ Group {
+ name: "dbus sources"
+ fileTags: "qt.dbus.interface"
+ files: ["org.kde.KWallet.xml"]
+ }
+
+ Group {
+ name: "qtkeychain dbus support"
+ cpp.defines: outer.concat(["KEYCHAIN_DBUS=1"])
+ cpp.cxxFlags: outer.concat("-Wno-cast-function-type")
+ files: [
+ "gnomekeyring.cpp",
+ "gnomekeyring_p.h",
+ "keychain_unix.cpp",
+ "libsecret.cpp",
+ "libsecret_p.h",
+ "plaintextstore.cpp",
+ "plaintextstore_p.h",
+ ]
+ }
+ }
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: project.ide_source_tree + "/src/libs/3rdparty/"
+ }
+}
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_de.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_de.ts
new file mode 100644
index 0000000000..59ed992ed1
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_de.ts
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation type="unfinished">Kann Daten nicht entschlüsseln</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="585"/>
+ <location filename="../keychain_unix.cpp" line="593"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">Unbekannter Fehler</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="614"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation type="unfinished">Konnte Brieftasche nicht öffnen: %1; %2</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Passwort nicht gefunden</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="295"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">Unbekannter Fehler</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="542"/>
+ <source>Access to keychain denied</source>
+ <translation type="unfinished">Zugriff auf Schlüsselbund verweigert</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="../plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">Eintrag nicht gefunden</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="214"/>
+ <location filename="../keychain_unix.cpp" line="224"/>
+ <source>Unknown error</source>
+ <translation>Unbekannter Fehler</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="205"/>
+ <source>D-Bus is not running</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="316"/>
+ <source>No keychain service available</source>
+ <translation>Kein Schlüsselbund-Dienst verfügbar</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="318"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Konnte Brieftasche nicht öffnen: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="363"/>
+ <source>Access to keychain denied</source>
+ <translation>Zugriff auf Schlüsselbund verweigert</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="384"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>Datentyp kann nicht ermittelt werden: %1: %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="402"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="405"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="393"/>
+ <source>Entry not found</source>
+ <translation>Eintrag nicht gefunden</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="36"/>
+ <location filename="../keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>Kann Daten nicht entschlüsseln</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="455"/>
+ <location filename="../keychain_unix.cpp" line="482"/>
+ <source>Unknown error</source>
+ <translation>Unbekannter Fehler</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="445"/>
+ <source>D-Bus is not running</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="501"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Konnte Brieftasche nicht öffnen: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>Verschlüsselung fehlgeschlagen</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Passwort nicht gefunden</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="255"/>
+ <source>Access to keychain denied</source>
+ <translation>Zugriff auf Schlüsselbund verweigert</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="257"/>
+ <source>No keyring daemon</source>
+ <translation>Kein Schlüsselbund-Dienst </translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="259"/>
+ <source>Already unlocked</source>
+ <translation>Bereits entsperrt</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="261"/>
+ <source>No such keyring</source>
+ <translation>Kein solcher Schlüsselbund</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="263"/>
+ <source>Bad arguments</source>
+ <translation>Ungültige Argumente</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="265"/>
+ <source>I/O error</source>
+ <translation>Ein-/Ausgabe-Fehler</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="267"/>
+ <source>Cancelled</source>
+ <translation>Abgebrochen</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="269"/>
+ <source>Keyring already exists</source>
+ <translation>Schlüsselbund existiert bereits</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="271"/>
+ <source>No match</source>
+ <translation>Kein Treffer</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="276"/>
+ <source>Unknown error</source>
+ <translation>Unbekannter Fehler</translation>
+ </message>
+ <message>
+ <location filename="../libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">Eintrag nicht gefunden</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_fr.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_fr.ts
new file mode 100644
index 0000000000..5eee2e34da
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_fr.ts
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="fr_FR">
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation>Mot de passe introuvable</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation>Impossible de déchiffrer les données</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="585"/>
+ <location filename="../keychain_unix.cpp" line="593"/>
+ <source>Unknown error</source>
+ <translation>Erreur inconnue</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="614"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Impossible d&apos;ouvrir le portefeuille : %1; %2</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="295"/>
+ <source>Unknown error</source>
+ <translation>Erreur inconnue</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="542"/>
+ <source>Access to keychain denied</source>
+ <translation>Accès au trousseau refusé</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="../plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation>Impossible de stocker les données dans les paramètres : Erreur d&apos;accès</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation>Impossible de stocker les données dans les paramètres : Erreur de format</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation>Impossible de supprimer les données depuis les paramètres : Erreur d&apos;accès</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation>Impossible de supprimer les données depuis les paramètres : Erreur de format</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation>Entrée introuvable</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="214"/>
+ <location filename="../keychain_unix.cpp" line="224"/>
+ <source>Unknown error</source>
+ <translation>Erreur inconnue</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="205"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus n&apos;est pas en cours d&apos;exécution</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="316"/>
+ <source>No keychain service available</source>
+ <translation>Aucun service de trousseau disponible</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="318"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Impossible d&apos;ouvrir le trousseau : %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="363"/>
+ <source>Access to keychain denied</source>
+ <translation>Accès au trousseau refusé</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="384"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>Impossible de déterminer le type de données : %1: %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="402"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation>Type d&apos;entrée non supporté &apos;Map&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="405"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation>Type de trousseau inconnu &apos;%1&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="393"/>
+ <source>Entry not found</source>
+ <translation>Entrée introuvable</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation>Entrée de mot de passe introuvable</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="36"/>
+ <location filename="../keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>Impossible de déchiffrer les données</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="455"/>
+ <location filename="../keychain_unix.cpp" line="482"/>
+ <source>Unknown error</source>
+ <translation>Erreur inconnue</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="445"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus n&apos;est pas en cours d&apos;exécution</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="501"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Impossible d&apos;ouvrir le trousseau : %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>Le chiffrement a échoué</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="255"/>
+ <source>Access to keychain denied</source>
+ <translation>Accès au trousseau refusé</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="257"/>
+ <source>No keyring daemon</source>
+ <translation>Aucun démon de trousseau</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="259"/>
+ <source>Already unlocked</source>
+ <translation>Déjà déverrouillé</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="261"/>
+ <source>No such keyring</source>
+ <translation>Aucun trousseau</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="263"/>
+ <source>Bad arguments</source>
+ <translation>Mauvais arguments</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="265"/>
+ <source>I/O error</source>
+ <translation>Erreur d&apos;E/S</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="267"/>
+ <source>Cancelled</source>
+ <translation>Annulé</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="269"/>
+ <source>Keyring already exists</source>
+ <translation>Trousseau déjà existant</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="271"/>
+ <source>No match</source>
+ <translation>Aucune correspondance</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="276"/>
+ <source>Unknown error</source>
+ <translation>Erreur inconnue</translation>
+ </message>
+ <message>
+ <location filename="../libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation>Entrée introuvable</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_nl.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_nl.ts
new file mode 100644
index 0000000000..7033a98457
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_nl.ts
@@ -0,0 +1,320 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nl_NL">
+<context>
+ <name>KeyChainClass</name>
+ <message>
+ <location filename="TestAppExample/keychainclass.cpp" line="22"/>
+ <source>Read key failed: %1</source>
+ <translation>De sleutel kan niet worden ingelezen: %1</translation>
+ </message>
+ <message>
+ <location filename="TestAppExample/keychainclass.cpp" line="37"/>
+ <source>Write key failed: %1</source>
+ <translation>De sleutel kan niet worden weggeschreven: %1</translation>
+ </message>
+ <message>
+ <location filename="TestAppExample/keychainclass.cpp" line="54"/>
+ <source>Delete key failed: %1</source>
+ <translation>De sleutel kan niet worden verwijderd: %1</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="keychain_android.cpp" line="173"/>
+ <source>Could not open keystore</source>
+ <translation>De sleutelbos kan niet worden geopend</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="179"/>
+ <source>Could not remove private key from keystore</source>
+ <translation>De privésleutel kan niet worden verwijderd uit de sleutelbos</translation>
+ </message>
+ <message>
+ <location filename="keychain_haiku.cpp" line="177"/>
+ <source>Password not found</source>
+ <translation>Het wachtwoord is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="552"/>
+ <location filename="keychain_unix.cpp" line="560"/>
+ <source>Unknown error</source>
+ <translation>Onbekende foutmelding</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="578"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>De sleutelbos kan niet worden geopend: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation>Het wachtwoord is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation>De gegevens kunnen niet worden ongrendeld</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="keychain_unix.cpp" line="265"/>
+ <source>Unknown error</source>
+ <translation>Onbekende foutmelding</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="509"/>
+ <source>Access to keychain denied</source>
+ <translation>U heeft geen toegang tot de sleutelbos</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation>De gegevens kunnen niet worden opgeslagen in de instellingen: toegangsfout</translation>
+ </message>
+ <message>
+ <location filename="plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation>De gegevens kunnen niet worden opgeslagen in de instellingen: opmaakfout</translation>
+ </message>
+ <message>
+ <location filename="plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation>De gegevens kunnen niet worden verwijderd uit de instellingen: toegangsfout</translation>
+ </message>
+ <message>
+ <location filename="plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation>De gegevens kunnen niet worden verwijderd uit de instellingen: opmaakfout</translation>
+ </message>
+ <message>
+ <location filename="plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation>Het item is niet gevonden</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="keychain_android.cpp" line="52"/>
+ <location filename="keychain_unix.cpp" line="363"/>
+ <source>Entry not found</source>
+ <translation>Het item is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="60"/>
+ <source>Could not open keystore</source>
+ <translation>De sleutelbos kan niet worden geopend</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="68"/>
+ <source>Could not retrieve private key from keystore</source>
+ <translation>De privésleutel kan niet worden verwijderd uit de sleutelbos</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="75"/>
+ <source>Could not create decryption cipher</source>
+ <translation>De ontgrendelcode kan niet worden aangemaakt</translation>
+ </message>
+ <message>
+ <location filename="keychain_haiku.cpp" line="96"/>
+ <source>Password not found</source>
+ <translation>Het wachtwoord is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="178"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus is niet actief</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="187"/>
+ <location filename="keychain_unix.cpp" line="197"/>
+ <source>Unknown error</source>
+ <translation>Onbekende foutmelding</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="286"/>
+ <source>No keychain service available</source>
+ <translation>De sleutelbosdienst is niet beschikbaar</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="288"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>De sleutelbos kan niet worden geopend: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="333"/>
+ <source>Access to keychain denied</source>
+ <translation>U heeft geen toegang tot de sleutelbos</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="354"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>De gegevensdrager kan niet worden bepaald: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="372"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation>Niet-ondersteund itemtype ‘Map’</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="375"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation>Onbekend KWallet-item van type ‘%1’</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation>Het wachtwoord is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="36"/>
+ <location filename="keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>De gegevens kunnen niet worden ongrendeld</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="keychain_android.cpp" line="95"/>
+ <source>Could not open keystore</source>
+ <translation>De sleutelbos kan niet worden geopend</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="124"/>
+ <source>Could not create private key generator</source>
+ <translation>De privésleutelgenerator kan niet worden gestart</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="131"/>
+ <source>Could not generate new private key</source>
+ <translation>Er kan geen nieuwe privésleutel worden gegenereerd</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="139"/>
+ <source>Could not retrieve private key from keystore</source>
+ <translation>De privésleutel kan niet worden opgehaald uit de sleutelbos</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="147"/>
+ <source>Could not create encryption cipher</source>
+ <translation>De vergrendelcode kan niet worden aangemaakt</translation>
+ </message>
+ <message>
+ <location filename="keychain_android.cpp" line="155"/>
+ <source>Could not encrypt data</source>
+ <translation>De gegevens kunnen niet worden ontgrendeld</translation>
+ </message>
+ <message>
+ <location filename="keychain_haiku.cpp" line="144"/>
+ <source>Password not found</source>
+ <translation>Het wachtwoord is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="415"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus is niet actief</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="425"/>
+ <location filename="keychain_unix.cpp" line="452"/>
+ <source>Unknown error</source>
+ <translation>Onbekende foutmelding</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="468"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>De sleutelbos kan niet worden geopend: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation>De omvang overschrijdt de maximumomvang van %1</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation>De sleutel overschrijdt de maximumomvang van %1</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation>De gegevens kunnen niet worden weggeschreven: Win32-foutcode %1</translation>
+ </message>
+ <message>
+ <location filename="keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>Het ontgrendelen is mislukt</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="keychain_haiku.cpp" line="72"/>
+ <source>error 0x%1: %2</source>
+ <translation>foutmelding 0x%1: %2</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="225"/>
+ <source>Access to keychain denied</source>
+ <translation>U heeft geen toegang tot de sleutelbos</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="227"/>
+ <source>No keyring daemon</source>
+ <translation>De sleutelbosdienst is niet actief</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="229"/>
+ <source>Already unlocked</source>
+ <translation>De sleutelbos is reeds ontgrendeld</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="231"/>
+ <source>No such keyring</source>
+ <translation>De sleutelbos bestaat niet</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="233"/>
+ <source>Bad arguments</source>
+ <translation>Onjuiste opties</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="235"/>
+ <source>I/O error</source>
+ <translation>I/O-fout</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="237"/>
+ <source>Cancelled</source>
+ <translation>Geannuleerd</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="239"/>
+ <source>Keyring already exists</source>
+ <translation>De sleutelbos bestaat reeds</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="241"/>
+ <source>No match</source>
+ <translation>Er zijn geen overeenkomsten</translation>
+ </message>
+ <message>
+ <location filename="keychain_unix.cpp" line="246"/>
+ <source>Unknown error</source>
+ <translation>Onbekende foutmelding</translation>
+ </message>
+ <message>
+ <location filename="libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation>Het item is niet gevonden</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ro.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ro.ts
new file mode 100644
index 0000000000..fb4e590676
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ro.ts
@@ -0,0 +1,235 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ro_RO">
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation type="unfinished">Nu se poate decripta data</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="585"/>
+ <location filename="../keychain_unix.cpp" line="593"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">Eroare necunoscută</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="614"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation type="unfinished">Nu se poate deschide portofelul: %1; %2</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Parola nu a fost găsită</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="295"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">Eroare necunoscută</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="542"/>
+ <source>Access to keychain denied</source>
+ <translation type="unfinished">Acces interzis la serviciul de chei</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="../plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">Înregistrarea nu a fost găsită</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="214"/>
+ <location filename="../keychain_unix.cpp" line="224"/>
+ <source>Unknown error</source>
+ <translation>Eroare necunoscută</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="205"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus nu rulează</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="316"/>
+ <source>No keychain service available</source>
+ <translatorcomment>Nu există niciun serviciu de chei disponibil</translatorcomment>
+ <translation>Kein Schlüsselbund-Dienst verfügbar</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="318"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Nu se poate deschide portofelul: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="363"/>
+ <source>Access to keychain denied</source>
+ <translation>Acces interzis la serviciul de chei</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="384"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>Nu se poate stabili tipul de date: %1: %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="402"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation>Tip de înregistrare nesuportat &apos;Map&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="405"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation>Tip de înregistrare kwallet necunoscut &apos;%1&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="393"/>
+ <source>Entry not found</source>
+ <translation>Înregistrarea nu a fost găsită</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="36"/>
+ <location filename="../keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>Nu se poate decripta data</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="455"/>
+ <location filename="../keychain_unix.cpp" line="482"/>
+ <source>Unknown error</source>
+ <translation>Eroare necunoscută</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="445"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus nu rulează</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="501"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Nu se poate deschide portofelul: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>Criptarea a eșuat</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Parola nu a fost găsită</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="255"/>
+ <source>Access to keychain denied</source>
+ <translation>Acces interzis la serviciul de chei</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="257"/>
+ <source>No keyring daemon</source>
+ <translation>Niciun demon pentru inelul de chei</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="259"/>
+ <source>Already unlocked</source>
+ <translation>Deja deblocat</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="261"/>
+ <source>No such keyring</source>
+ <translation>Nu există astfel de inel de chei</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="263"/>
+ <source>Bad arguments</source>
+ <translation>Argumente greșite</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="265"/>
+ <source>I/O error</source>
+ <translation>Eroare de I/E</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="267"/>
+ <source>Cancelled</source>
+ <translation>Anulat</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="269"/>
+ <source>Keyring already exists</source>
+ <translation>Inelul de chei deja există</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="271"/>
+ <source>No match</source>
+ <translation>Nicio potrivire</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="276"/>
+ <source>Unknown error</source>
+ <translation>Eroare necunoscută</translation>
+ </message>
+ <message>
+ <location filename="../libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">Înregistrarea nu a fost găsită</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ru.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ru.ts
new file mode 100644
index 0000000000..c4cec0bb52
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_ru.ts
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru_RU">
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="585"/>
+ <location filename="../keychain_unix.cpp" line="593"/>
+ <source>Unknown error</source>
+ <translation>Неизвестная ошибка</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="614"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Не удалось открыть бумажник: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation>Пароль не найден</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation>Не удалось расшифровать данные</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Пароль не найден</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="295"/>
+ <source>Unknown error</source>
+ <translation>Неизвестная ошибка</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="542"/>
+ <source>Access to keychain denied</source>
+ <translation>Доступ к связке ключей запрещён</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="../plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation>Не удалось сохранить данные в настройках: ошибка доступа</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation>Не удалось сохранить данные в настройках: ошибка формата</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation>Не удалось удалить данные из настроек: ошибка доступа</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation>Не удалось удалить данные из настроек: ошибка формата</translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation>Запись не найдена</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="205"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus не запущен</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="214"/>
+ <location filename="../keychain_unix.cpp" line="224"/>
+ <source>Unknown error</source>
+ <translation>Неизвестная ошибка</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="316"/>
+ <source>No keychain service available</source>
+ <translation>Служба связки ключей недоступна</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="318"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Не удалось открыть кошелёк: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="363"/>
+ <source>Access to keychain denied</source>
+ <translation>Доступ к связке ключей запрещён</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="384"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>Не удалось определить тип данных: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="393"/>
+ <source>Entry not found</source>
+ <translation>Запись не найдена</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="402"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation>Неподдерживаемый тип записи &apos;Map&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="405"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation>Неизвестный тип записи kwallet &apos;%1&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation>Пароль не найден</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="36"/>
+ <location filename="../keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>Не удалось расшифровать данные</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="445"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus не запущен</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="455"/>
+ <location filename="../keychain_unix.cpp" line="482"/>
+ <source>Unknown error</source>
+ <translation>Неизвестная ошибка</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="501"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>Не удалось открыть кошелёк: %1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation>Учётные данные превышают максимальный размер %1</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation>Ключ учётных данных превышает максимальный размер %1</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation>Не удалось сохранить учётные данные: код ошибки win32 %1</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>Шифрование не удалось</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">Пароль не найден</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="255"/>
+ <source>Access to keychain denied</source>
+ <translation>Доступ к связке ключей запрещён</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="257"/>
+ <source>No keyring daemon</source>
+ <translation>Нет демона связки ключей</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="259"/>
+ <source>Already unlocked</source>
+ <translation>Уже разблокировано</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="261"/>
+ <source>No such keyring</source>
+ <translation>Связка ключей не найдена</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="263"/>
+ <source>Bad arguments</source>
+ <translation>Неверные аргументы</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="265"/>
+ <source>I/O error</source>
+ <translation>Ошибка ввода/вывода</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="267"/>
+ <source>Cancelled</source>
+ <translation>Отменено</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="269"/>
+ <source>Keyring already exists</source>
+ <translation>Связка ключей уже существует</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="271"/>
+ <source>No match</source>
+ <translation>Нет совпадений</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="276"/>
+ <source>Unknown error</source>
+ <translation>Неизвестная ошибка</translation>
+ </message>
+ <message>
+ <location filename="../libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation>Запись не найдена</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/qtkeychain_zh.ts b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_zh.ts
new file mode 100644
index 0000000000..5d4d64a499
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/qtkeychain_zh.ts
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_TW">
+<context>
+ <name>QKeychain::DeletePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_win.cpp" line="104"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="108"/>
+ <source>Could not decrypt data</source>
+ <translation type="unfinished">無法解密資料</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="585"/>
+ <location filename="../keychain_unix.cpp" line="593"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">未知的錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="614"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation type="unfinished">無法開啟錢包:%1; %2</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">找不到密碼</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::JobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="295"/>
+ <source>Unknown error</source>
+ <translation type="unfinished">未知的錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="542"/>
+ <source>Access to keychain denied</source>
+ <translation type="unfinished">鑰匙圈存取被拒絕</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::PlainTextStore</name>
+ <message>
+ <location filename="../plaintextstore.cpp" line="65"/>
+ <source>Could not store data in settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="67"/>
+ <source>Could not store data in settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="85"/>
+ <source>Could not delete data from settings: access error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="87"/>
+ <source>Could not delete data from settings: format error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../plaintextstore.cpp" line="104"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">找不到項目</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::ReadPasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="214"/>
+ <location filename="../keychain_unix.cpp" line="224"/>
+ <source>Unknown error</source>
+ <translation>未知的錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="205"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus 不在執行中</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="316"/>
+ <source>No keychain service available</source>
+ <translation>沒有可用的鑰匙圈服務</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="318"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>無法開啟錢包:%1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="363"/>
+ <source>Access to keychain denied</source>
+ <translation>鑰匙圈存取被拒絕</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="384"/>
+ <source>Could not determine data type: %1; %2</source>
+ <translation>無法判斷資料型別:%1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="402"/>
+ <source>Unsupported entry type &apos;Map&apos;</source>
+ <translation>不支援的項目類型 &apos;Map&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="405"/>
+ <source>Unknown kwallet entry type &apos;%1&apos;</source>
+ <translation>未知的 kwallet 項目類型 &apos;%1&apos;</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="393"/>
+ <source>Entry not found</source>
+ <translation>找不到項目</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="32"/>
+ <source>Password entry not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="36"/>
+ <location filename="../keychain_win.cpp" line="139"/>
+ <source>Could not decrypt data</source>
+ <translation>無法解密資料</translation>
+ </message>
+</context>
+<context>
+ <name>QKeychain::WritePasswordJobPrivate</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="455"/>
+ <location filename="../keychain_unix.cpp" line="482"/>
+ <source>Unknown error</source>
+ <translation>未知的錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="445"/>
+ <source>D-Bus is not running</source>
+ <translation>D-Bus 不在執行中</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="501"/>
+ <source>Could not open wallet: %1; %2</source>
+ <translation>無法開啟錢包:%1; %2</translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="78"/>
+ <source>Credential size exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="87"/>
+ <source>Credential key exceeds maximum size of %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="92"/>
+ <source>Writing credentials failed: Win32 error code %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../keychain_win.cpp" line="162"/>
+ <source>Encryption failed</source>
+ <translation>加密失敗</translation>
+ </message>
+ <message>
+ <source>Password not found</source>
+ <translation type="obsolete">找不到密碼</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../keychain_unix.cpp" line="255"/>
+ <source>Access to keychain denied</source>
+ <translation>鑰匙圈存取被拒絕</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="257"/>
+ <source>No keyring daemon</source>
+ <translation>沒有可用的鑰匙圈背景程式</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="259"/>
+ <source>Already unlocked</source>
+ <translation>已解鎖</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="261"/>
+ <source>No such keyring</source>
+ <translation>鑰匙圈不存在</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="263"/>
+ <source>Bad arguments</source>
+ <translation>引數錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="265"/>
+ <source>I/O error</source>
+ <translation>I/O 錯誤</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="267"/>
+ <source>Cancelled</source>
+ <translation>已取消</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="269"/>
+ <source>Keyring already exists</source>
+ <translation>鑰匙圈已存在</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="271"/>
+ <source>No match</source>
+ <translation>無相符項目</translation>
+ </message>
+ <message>
+ <location filename="../keychain_unix.cpp" line="276"/>
+ <source>Unknown error</source>
+ <translation>未知的錯誤</translation>
+ </message>
+ <message>
+ <location filename="../libsecret.cpp" line="119"/>
+ <source>Entry not found</source>
+ <translation type="unfinished">找不到項目</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/libs/3rdparty/qtkeychain/translations/translations.qrc.in b/src/libs/3rdparty/qtkeychain/translations/translations.qrc.in
new file mode 100644
index 0000000000..f49df66150
--- /dev/null
+++ b/src/libs/3rdparty/qtkeychain/translations/translations.qrc.in
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/translations">
+ @QM_FILE_LIST@
+ </qresource>
+</RCC>
diff --git a/src/libs/3rdparty/sol2/CMakeLists.txt b/src/libs/3rdparty/sol2/CMakeLists.txt
new file mode 100644
index 0000000000..1afdb1bdbb
--- /dev/null
+++ b/src/libs/3rdparty/sol2/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_library(sol2 INTERFACE)
+
+target_include_directories(sol2 INTERFACE
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:include>
+)
+
+install(TARGETS sol2 EXPORT QtCreator)
+
+qtc_add_public_header(include/sol/sol.hpp)
+qtc_add_public_header(include/sol/config.hpp)
+qtc_add_public_header(include/sol/forward.hpp)
diff --git a/src/libs/3rdparty/sol2/LICENSE.txt b/src/libs/3rdparty/sol2/LICENSE.txt
new file mode 100644
index 0000000000..5813440548
--- /dev/null
+++ b/src/libs/3rdparty/sol2/LICENSE.txt
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2022 Rapptz, ThePhD, and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/libs/3rdparty/sol2/include/sol/config.hpp b/src/libs/3rdparty/sol2/include/sol/config.hpp
new file mode 100644
index 0000000000..36ef3fe47e
--- /dev/null
+++ b/src/libs/3rdparty/sol2/include/sol/config.hpp
@@ -0,0 +1,57 @@
+// The MIT License (MIT)
+
+// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// This file was generated with a script.
+// Generated 2024-01-13 14:25:56.570832 UTC
+// This header was generated with sol v3.3.1 (revision 9c882a28)
+// https://github.com/ThePhD/sol2
+
+#ifndef SOL_SINGLE_SOL_CONFIG_HPP
+#define SOL_SINGLE_SOL_CONFIG_HPP
+
+// beginning of sol/config.hpp
+
+/* Base, empty configuration file!
+
+ To override, place a file in your include paths of the form:
+
+. (your include path here)
+| sol (directory, or equivalent)
+ | config.hpp (your config.hpp file)
+
+ So that when sol2 includes the file
+
+#include <sol/config.hpp>
+
+ it gives you the configuration values you desire. Configuration values can be
+seen in the safety.rst of the doc/src, or at
+https://sol2.readthedocs.io/en/latest/safety.html ! You can also pass them through
+the build system, or the command line options of your compiler.
+
+*/
+
+#define SOL_SAFE_FUNCTIONS 1
+#define SOL_SAFE_USERTYPE 1
+#define SOL_NO_NIL 1
+
+// end of sol/config.hpp
+
+#endif // SOL_SINGLE_SOL_CONFIG_HPP
diff --git a/src/libs/3rdparty/sol2/include/sol/forward.hpp b/src/libs/3rdparty/sol2/include/sol/forward.hpp
new file mode 100644
index 0000000000..83fba4d254
--- /dev/null
+++ b/src/libs/3rdparty/sol2/include/sol/forward.hpp
@@ -0,0 +1,1340 @@
+// The MIT License (MIT)
+
+// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// This file was generated with a script.
+// Generated 2024-01-13 14:25:56.569197 UTC
+// This header was generated with sol v3.3.1 (revision 9c882a28)
+// https://github.com/ThePhD/sol2
+
+#ifndef SOL_SINGLE_INCLUDE_SOL_FORWARD_HPP
+#define SOL_SINGLE_INCLUDE_SOL_FORWARD_HPP
+
+// beginning of sol/forward.hpp
+
+#ifndef SOL_FORWARD_HPP
+#define SOL_FORWARD_HPP
+
+// beginning of sol/version.hpp
+
+#include "config.hpp"
+
+#define SOL_VERSION_MAJOR 3
+#define SOL_VERSION_MINOR 2
+#define SOL_VERSION_PATCH 3
+#define SOL_VERSION_STRING "3.2.3"
+#define SOL_VERSION ((SOL_VERSION_MAJOR * 100000) + (SOL_VERSION_MINOR * 100) + (SOL_VERSION_PATCH))
+
+#define SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN) #_TOKEN
+#define SOL_TOKEN_TO_STRING_I_(_TOKEN) SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN)
+
+#define SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT) _LEFT##_RIGHT
+#define SOL_CONCAT_TOKENS_I_(_LEFT, _RIGHT) SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT)
+
+#define SOL_RAW_IS_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) != 0)
+#define SOL_RAW_IS_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3) == 0)
+#define SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) > 3)
+#define SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3 OP_SYMBOL 3) < 0)
+
+#define SOL_IS_ON(OP_SYMBOL) SOL_RAW_IS_ON(OP_SYMBOL ## _I_)
+#define SOL_IS_OFF(OP_SYMBOL) SOL_RAW_IS_OFF(OP_SYMBOL ## _I_)
+#define SOL_IS_DEFAULT_ON(OP_SYMBOL) SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL ## _I_)
+#define SOL_IS_DEFAULT_OFF(OP_SYMBOL) SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL ## _I_)
+
+#define SOL_ON |
+#define SOL_OFF ^
+#define SOL_DEFAULT_ON +
+#define SOL_DEFAULT_OFF -
+
+#if defined(SOL_BUILD_CXX_MODE)
+ #if (SOL_BUILD_CXX_MODE != 0)
+ #define SOL_BUILD_CXX_MODE_I_ SOL_ON
+ #else
+ #define SOL_BUILD_CXX_MODE_I_ SOL_OFF
+ #endif
+#elif defined(__cplusplus)
+ #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_BUILD_C_MODE)
+ #if (SOL_BUILD_C_MODE != 0)
+ #define SOL_BUILD_C_MODE_I_ SOL_ON
+ #else
+ #define SOL_BUILD_C_MODE_I_ SOL_OFF
+ #endif
+#elif defined(__STDC__)
+ #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if SOL_IS_ON(SOL_BUILD_C_MODE)
+ #include <stddef.h>
+ #include <stdint.h>
+ #include <limits.h>
+#else
+ #include <cstddef>
+ #include <cstdint>
+ #include <climits>
+#endif
+
+#if defined(SOL_HAS_BUILTIN)
+ #define SOL_HAS_BUILTIN_I_(...) SOL_HAS_BUILTIN(__VA_ARGS__)
+#elif defined(__has_builtin)
+ #define SOL_HAS_BUILTIN_I_(...) __has_builtin(__VA_ARGS__)
+#else
+ #define SOL_HAS_BUILTIN_I_(...) 0
+#endif
+
+#if defined(SOL_COMPILER_VCXX)
+ #if defined(SOL_COMPILER_VCXX != 0)
+ #define SOL_COMPILER_VCXX_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_VCXX_I_ SOL_OFF
+ #endif
+#elif defined(_MSC_VER)
+ #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_GCC)
+ #if defined(SOL_COMPILER_GCC != 0)
+ #define SOL_COMPILER_GCC_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_GCC_I_ SOL_OFF
+ #endif
+#elif defined(__GNUC__)
+ #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_CLANG)
+ #if defined(SOL_COMPILER_CLANG != 0)
+ #define SOL_COMPILER_CLANG_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_CLANG_I_ SOL_OFF
+ #endif
+#elif defined(__clang__)
+ #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_EDG)
+ #if defined(SOL_COMPILER_EDG != 0)
+ #define SOL_COMPILER_EDG_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_EDG_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_COMPILER_EDG_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_MINGW)
+ #if (SOL_COMPILER_MINGW != 0)
+ #define SOL_COMPILER_MINGW_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_MINGW_I_ SOL_OFF
+ #endif
+#elif defined(__MINGW32__)
+ #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_OFF
+#endif
+
+#if SIZE_MAX <= 0xFFFFULL
+ #define SOL_PLATFORM_X16_I_ SOL_ON
+ #define SOL_PLATFORM_X86_I_ SOL_OFF
+ #define SOL_PLATFORM_X64_I_ SOL_OFF
+#elif SIZE_MAX <= 0xFFFFFFFFULL
+ #define SOL_PLATFORM_X16_I_ SOL_OFF
+ #define SOL_PLATFORM_X86_I_ SOL_ON
+ #define SOL_PLATFORM_X64_I_ SOL_OFF
+#else
+ #define SOL_PLATFORM_X16_I_ SOL_OFF
+ #define SOL_PLATFORM_X86_I_ SOL_OFF
+ #define SOL_PLATFORM_X64_I_ SOL_ON
+#endif
+
+#define SOL_PLATFORM_ARM32_I_ SOL_OFF
+#define SOL_PLATFORM_ARM64_I_ SOL_OFF
+
+#if defined(SOL_PLATFORM_WINDOWS)
+ #if (SOL_PLATFORM_WINDOWS != 0)
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_OFF
+ #endif
+#elif defined(_WIN32)
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_CYGWIN)
+ #if (SOL_PLATFORM_CYGWIN != 0)
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_ON
+ #endif
+#elif defined(__CYGWIN__)
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_APPLE)
+ #if (SOL_PLATFORM_APPLE != 0)
+ #define SOL_PLATFORM_APPLE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_APPLE_I_ SOL_OFF
+ #endif
+#elif defined(__APPLE__)
+ #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_UNIX)
+ #if (SOL_PLATFORM_UNIX != 0)
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_OFF
+ #endif
+#elif defined(__unix__)
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAUKT_ON
+#else
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_LINUX)
+ #if (SOL_PLATFORM_LINUX != 0)
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_OFF
+ #endif
+#elif defined(__LINUX__)
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAUKT_ON
+#else
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAULT_OFF
+#endif
+
+#define SOL_PLATFORM_APPLE_IPHONE_I_ SOL_OFF
+#define SOL_PLATFORM_BSDLIKE_I_ SOL_OFF
+
+#if defined(SOL_IN_DEBUG_DETECTED)
+ #if SOL_IN_DEBUG_DETECTED != 0
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #else
+ #define SOL_DEBUG_BUILD_I_ SOL_OFF
+ #endif
+#elif !defined(NDEBUG)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX) && defined(_DEBUG)
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #elif (SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)) && !defined(__OPTIMIZE__)
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #else
+ #define SOL_DEBUG_BUILD_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEBUG_BUILD_I_ SOL_DEFAULT_OFF
+#endif // We are in a debug mode of some sort
+
+#if defined(SOL_NO_EXCEPTIONS)
+ #if (SOL_NO_EXCEPTIONS != 0)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if !defined(_CPPUNWIND)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)
+ #if !defined(__EXCEPTIONS)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#else
+ #define SOL_EXCEPTIONS_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_NO_RTTI)
+ #if (SOL_NO_RTTI != 0)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if !defined(_CPPRTTI)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)
+ #if !defined(__GXX_RTTI)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#else
+ #define SOL_RTTI_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_NO_THREAD_LOCAL)
+ #if SOL_NO_THREAD_LOCAL != 0
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_OFF
+ #else
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_ON
+ #endif
+#else
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_DEFAULT_ON
+#endif // thread_local keyword is bjorked on some platforms
+
+#if defined(SOL_ALL_SAFETIES_ON)
+ #if SOL_ALL_SAFETIES_ON != 0
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_ON
+ #else
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_SAFE_GETTER)
+ #if SOL_SAFE_GETTER != 0
+ #define SOL_SAFE_GETTER_I_ SOL_ON
+ #else
+ #define SOL_SAFE_GETTER_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_GETTER_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_USERTYPE)
+ #if SOL_SAFE_USERTYPE != 0
+ #define SOL_SAFE_USERTYPE_I_ SOL_ON
+ #else
+ #define SOL_SAFE_USERTYPE_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_USERTYPE_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_REFERENCES)
+ #if SOL_SAFE_REFERENCES != 0
+ #define SOL_SAFE_REFERENCES_I_ SOL_ON
+ #else
+ #define SOL_SAFE_REFERENCES_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_REFERENCES_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_FUNCTIONS)
+ #if SOL_SAFE_FUNCTIONS != 0
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF
+ #endif
+#elif defined (SOL_SAFE_FUNCTION_OBJECTS)
+ #if SOL_SAFE_FUNCTION_OBJECTS != 0
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_FUNCTION_CALLS)
+ #if SOL_SAFE_FUNCTION_CALLS != 0
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_PROXIES)
+ #if SOL_SAFE_PROXIES != 0
+ #define SOL_SAFE_PROXIES_I_ SOL_ON
+ #else
+ #define SOL_SAFE_PROXIES_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_PROXIES_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_NUMERICS)
+ #if SOL_SAFE_NUMERICS != 0
+ #define SOL_SAFE_NUMERICS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_NUMERICS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_NUMERICS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_ALL_INTEGER_VALUES_FIT)
+ #if (SOL_ALL_INTEGER_VALUES_FIT != 0)
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_ON
+ #else
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_OFF
+ #endif
+#elif !SOL_IS_DEFAULT_OFF(SOL_SAFE_NUMERICS) && SOL_IS_OFF(SOL_SAFE_NUMERICS)
+ // if numerics is intentionally turned off, flip this on
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_ON
+#else
+ // default to off
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_SAFE_STACK_CHECK)
+ #if SOL_SAFE_STACK_CHECK != 0
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_ON
+ #else
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_NO_CHECK_NUMBER_PRECISION)
+ #if SOL_NO_CHECK_NUMBER_PRECISION != 0
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #endif
+#elif defined(SOL_NO_CHECKING_NUMBER_PRECISION)
+ #if SOL_NO_CHECKING_NUMBER_PRECISION != 0
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_SAFE_NUMERICS)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_STRINGS_ARE_NUMBERS)
+ #if (SOL_STRINGS_ARE_NUMBERS != 0)
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_ON
+ #else
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ENABLE_INTEROP)
+ #if SOL_ENABLE_INTEROP != 0
+ #define SOL_USE_INTEROP_I_ SOL_ON
+ #else
+ #define SOL_USE_INTEROP_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_INTEROP)
+ #if SOL_USE_INTEROP != 0
+ #define SOL_USE_INTEROP_I_ SOL_ON
+ #else
+ #define SOL_USE_INTEROP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_INTEROP_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_NO_NIL)
+ #if (SOL_NO_NIL != 0)
+ #define SOL_NIL_I_ SOL_OFF
+ #else
+ #define SOL_NIL_I_ SOL_ON
+ #endif
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || defined(__OBJC__) || defined(nil)
+ #define SOL_NIL_I_ SOL_DEFAULT_OFF
+#else
+ #define SOL_NIL_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_USERTYPE_TYPE_BINDING_INFO)
+ #if (SOL_USERTYPE_TYPE_BINDING_INFO != 0)
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_ON
+ #else
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_DEFAULT_ON
+#endif // We should generate a my_type.__type table with lots of class information for usertypes
+
+#if defined(SOL_AUTOMAGICAL_TYPES_BY_DEFAULT)
+ #if (SOL_AUTOMAGICAL_TYPES_BY_DEFAULT != 0)
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF
+ #endif
+#elif defined(SOL_DEFAULT_AUTOMAGICAL_USERTYPES)
+ #if (SOL_DEFAULT_AUTOMAGICAL_USERTYPES != 0)
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_DEFAULT_ON
+#endif // make is_automagical on/off by default
+
+#if defined(SOL_STD_VARIANT)
+ #if (SOL_STD_VARIANT != 0)
+ #define SOL_STD_VARIANT_I_ SOL_ON
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_COMPILER_CLANG) && SOL_IS_ON(SOL_PLATFORM_APPLE)
+ #if defined(__has_include)
+ #if __has_include(<variant>)
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF
+ #endif
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF
+ #endif
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON
+ #endif
+#endif // make is_automagical on/off by default
+
+#if defined(SOL_NOEXCEPT_FUNCTION_TYPE)
+ #if (SOL_NOEXCEPT_FUNCTION_TYPE != 0)
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON
+ #else
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF
+ #endif
+#else
+ #if defined(__cpp_noexcept_function_type)
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(_MSVC_LANG) && (_MSVC_LANG < 201403L))
+ // There is a bug in the VC++ compiler??
+ // on /std:c++latest under x86 conditions (VS 15.5.2),
+ // compiler errors are tossed for noexcept markings being on function types
+ // that are identical in every other way to their non-noexcept marked types function types...
+ // VS 2019: There is absolutely a bug.
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF
+ #else
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_DEFAULT_ON
+ #endif
+#endif // noexcept is part of a function's type
+
+#if defined(SOL_STACK_STRING_OPTIMIZATION_SIZE) && SOL_STACK_STRING_OPTIMIZATION_SIZE > 0
+ #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ SOL_STACK_STRING_OPTIMIZATION_SIZE
+#else
+ #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ 1024
+#endif
+
+#if defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0
+ #define SOL_ID_SIZE_I_ SOL_ID_SIZE
+#else
+ #define SOL_ID_SIZE_I_ 512
+#endif
+
+#if defined(LUA_IDSIZE) && LUA_IDSIZE > 0
+ #define SOL_FILE_ID_SIZE_I_ LUA_IDSIZE
+#elif defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0
+ #define SOL_FILE_ID_SIZE_I_ SOL_FILE_ID_SIZE
+#else
+ #define SOL_FILE_ID_SIZE_I_ 2048
+#endif
+
+#if defined(SOL_PRINT_ERRORS)
+ #if (SOL_PRINT_ERRORS != 0)
+ #define SOL_PRINT_ERRORS_I_ SOL_ON
+ #else
+ #define SOL_PRINT_ERRORS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_PRINT_ERRORS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_PRINT_ERRORS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_PRINT_ERRORS_I_ SOL_OFF
+ #endif
+#endif
+
+#if defined(SOL_DEFAULT_PASS_ON_ERROR)
+ #if (SOL_DEFAULT_PASS_ON_ERROR != 0)
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USING_CXX_LUA)
+ #if (SOL_USING_CXX_LUA != 0)
+ #define SOL_USING_CXX_LUA_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUA_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_CXX_LUA)
+ // alternative spelling
+ #if (SOL_USE_CXX_LUA != 0)
+ #define SOL_USING_CXX_LUA_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUA_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USING_CXX_LUA_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USING_CXX_LUAJIT)
+ #if (SOL_USING_CXX_LUAJIT != 0)
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_CXX_LUAJIT)
+ #if (SOL_USE_CXX_LUAJIT != 0)
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_NO_LUA_HPP)
+ #if (SOL_NO_LUA_HPP != 0)
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+ #else
+ #define SOL_USE_LUA_HPP_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_USING_CXX_LUA)
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+#elif defined(__has_include)
+ #if __has_include(<lua.hpp>)
+ #define SOL_USE_LUA_HPP_I_ SOL_ON
+ #else
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_LUA_HPP_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_CONTAINERS_START)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START
+#elif defined(SOL_CONTAINERS_START_INDEX)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START_INDEX
+#elif defined(SOL_CONTAINER_START_INDEX)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINER_START_INDEX
+#else
+ #define SOL_CONTAINER_START_INDEX_I_ 1
+#endif
+
+#if defined (SOL_NO_MEMORY_ALIGNMENT)
+ #if (SOL_NO_MEMORY_ALIGNMENT != 0)
+ #define SOL_ALIGN_MEMORY_I_ SOL_OFF
+ #else
+ #define SOL_ALIGN_MEMORY_I_ SOL_ON
+ #endif
+#else
+ #define SOL_ALIGN_MEMORY_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_USE_BOOST)
+ #if (SOL_USE_BOOST != 0)
+ #define SOL_USE_BOOST_I_ SOL_ON
+ #else
+ #define SOL_USE_BOOST_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_BOOST_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USE_UNSAFE_BASE_LOOKUP)
+ #if (SOL_USE_UNSAFE_BASE_LOOKUP != 0)
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_ON
+ #else
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_INSIDE_UNREAL)
+ #if (SOL_INSIDE_UNREAL != 0)
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON
+ #else
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_OFF
+ #endif
+#else
+ #if defined(UE_BUILD_DEBUG) || defined(UE_BUILD_DEVELOPMENT) || defined(UE_BUILD_TEST) || defined(UE_BUILD_SHIPPING) || defined(UE_SERVER)
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_NO_COMPAT)
+ #if (SOL_NO_COMPAT != 0)
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_OFF
+ #else
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_ON
+ #endif
+#else
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ #if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0)
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON
+ #else
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_CONTAINER_CHECK_IS_EXHAUSTIVE)
+ #if (SOL_CONTAINER_CHECK_IS_EXHAUSTIVE != 0)
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_ON
+ #else
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_FUNCTION_CALL_VALUE_SEMANTICS)
+ #if (SOL_FUNCTION_CALL_VALUE_SEMANTICS != 0)
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_ON
+ #else
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_MINGW_CCTYPE_IS_POISONED)
+ #if (SOL_MINGW_CCTYPE_IS_POISONED != 0)
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON
+ #else
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_MINGW) && defined(__GNUC__) && (__GNUC__ < 6)
+ // MinGW is off its rocker in some places...
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_CHAR8_T)
+ #if (SOL_CHAR8_T != 0)
+ #define SOL_CHAR8_T_I_ SOL_ON
+ #else
+ #define SOL_CHAR8_T_I_ SOL_OFF
+ #endif
+#else
+ #if defined(__cpp_char8_t)
+ #define SOL_CHAR8_T_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_CHAR8_T_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+ #include <boost/version.hpp>
+
+ #if BOOST_VERSION >= 107500 // Since Boost 1.75.0 boost::none is constexpr
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr
+ #else
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ const
+ #endif // BOOST_VERSION
+#else
+ // assume boost isn't using a garbage version
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr
+#endif
+
+#if defined(SOL2_CI)
+ #if (SOL2_CI != 0)
+ #define SOL2_CI_I_ SOL_ON
+ #else
+ #define SOL2_CI_I_ SOL_OFF
+ #endif
+#else
+ #define SOL2_CI_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ASSERT)
+ #define SOL_USER_ASSERT_I_ SOL_ON
+#else
+ #define SOL_USER_ASSERT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ASSERT_MSG)
+ #define SOL_USER_ASSERT_MSG_I_ SOL_ON
+#else
+ #define SOL_USER_ASSERT_MSG_I_ SOL_DEFAULT_OFF
+#endif
+
+// beginning of sol/prologue.hpp
+
+#if defined(SOL_PROLOGUE_I_)
+ #error "[sol2] Library Prologue was already included in translation unit and not properly ended with an epilogue."
+#endif
+
+#define SOL_PROLOGUE_I_ 1
+
+#if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #define _FWD(...) static_cast<decltype( __VA_ARGS__ )&&>( __VA_ARGS__ )
+
+ #if SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define _MOVE(...) static_cast<__typeof( __VA_ARGS__ )&&>( __VA_ARGS__ )
+ #else
+ #include <type_traits>
+
+ #define _MOVE(...) static_cast<::std::remove_reference_t<( __VA_ARGS__ )>&&>( __VA_OPT__(,) )
+ #endif
+#endif
+
+// end of sol/prologue.hpp
+
+// beginning of sol/epilogue.hpp
+
+#if !defined(SOL_PROLOGUE_I_)
+ #error "[sol2] Library Prologue is missing from this translation unit."
+#else
+ #undef SOL_PROLOGUE_I_
+#endif
+
+#if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #undef _FWD
+ #undef _MOVE
+#endif
+
+// end of sol/epilogue.hpp
+
+// beginning of sol/detail/build_version.hpp
+
+#if defined(SOL_DLL)
+ #if (SOL_DLL != 0)
+ #define SOL_DLL_I_ SOL_ON
+ #else
+ #define SOL_DLL_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(DLL_) || defined(_DLL))
+ #define SOL_DLL_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_DLL_I_ SOL_DEFAULT_OFF
+#endif // DLL definition
+
+#if defined(SOL_HEADER_ONLY)
+ #if (SOL_HEADER_ONLY != 0)
+ #define SOL_HEADER_ONLY_I_ SOL_ON
+ #else
+ #define SOL_HEADER_ONLY_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_HEADER_ONLY_I_ SOL_DEFAULT_OFF
+#endif // Header only library
+
+#if defined(SOL_BUILD)
+ #if (SOL_BUILD != 0)
+ #define SOL_BUILD_I_ SOL_ON
+ #else
+ #define SOL_BUILD_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_BUILD_I_ SOL_DEFAULT_OFF
+#else
+ #define SOL_BUILD_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_UNITY_BUILD)
+ #if (SOL_UNITY_BUILD != 0)
+ #define SOL_UNITY_BUILD_I_ SOL_ON
+ #else
+ #define SOL_UNITY_BUILD_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_UNITY_BUILD_I_ SOL_DEFAULT_OFF
+#endif // Header only library
+
+#if defined(SOL_C_FUNCTION_LINKAGE)
+ #define SOL_C_FUNCTION_LINKAGE_I_ SOL_C_FUNCTION_LINKAGE
+#else
+ #if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ // C++
+ #define SOL_C_FUNCTION_LINKAGE_I_ extern "C"
+ #else
+ // normal
+ #define SOL_C_FUNCTION_LINKAGE_I_
+ #endif // C++ or not
+#endif // Linkage specification for C functions
+
+#if defined(SOL_API_LINKAGE)
+ #define SOL_API_LINKAGE_I_ SOL_API_LINKAGE
+#else
+ #if SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX) || SOL_IS_ON(SOL_PLATFORM_WINDOWS) || SOL_IS_ON(SOL_PLATFORM_CYGWIN)
+ // MSVC Compiler; or, Windows, or Cygwin platforms
+ #if SOL_IS_ON(SOL_BUILD)
+ // Building the library
+ #if SOL_IS_ON(SOL_COMPILER_GCC)
+ // Using GCC
+ #define SOL_API_LINKAGE_I_ __attribute__((dllexport))
+ #else
+ // Using Clang, MSVC, etc...
+ #define SOL_API_LINKAGE_I_ __declspec(dllexport)
+ #endif
+ #else
+ #if SOL_IS_ON(SOL_COMPILER_GCC)
+ #define SOL_API_LINKAGE_I_ __attribute__((dllimport))
+ #else
+ #define SOL_API_LINKAGE_I_ __declspec(dllimport)
+ #endif
+ #endif
+ #else
+ // extern if building normally on non-MSVC
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #elif SOL_IS_ON(SOL_UNITY_BUILD)
+ // Built-in library, like how stb typical works
+ #if SOL_IS_ON(SOL_HEADER_ONLY)
+ // Header only, so functions are defined "inline"
+ #define SOL_API_LINKAGE_I_ inline
+ #else
+ // Not header only, so seperately compiled files
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #else
+ // Normal static library
+ #if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #define SOL_API_LINKAGE_I_
+ #else
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #endif // DLL or not
+#endif // Build definitions
+
+#if defined(SOL_PUBLIC_FUNC_DECL)
+ #define SOL_PUBLIC_FUNC_DECL_I_ SOL_PUBLIC_FUNC_DECL
+#else
+ #define SOL_PUBLIC_FUNC_DECL_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_INTERNAL_FUNC_DECL_)
+ #define SOL_INTERNAL_FUNC_DECL_I_ SOL_INTERNAL_FUNC_DECL_
+#else
+ #define SOL_INTERNAL_FUNC_DECL_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_PUBLIC_FUNC_DEF)
+ #define SOL_PUBLIC_FUNC_DEF_I_ SOL_PUBLIC_FUNC_DEF
+#else
+ #define SOL_PUBLIC_FUNC_DEF_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_INTERNAL_FUNC_DEF)
+ #define SOL_INTERNAL_FUNC_DEF_I_ SOL_INTERNAL_FUNC_DEF
+#else
+ #define SOL_INTERNAL_FUNC_DEF_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_FUNC_DECL)
+ #define SOL_FUNC_DECL_I_ SOL_FUNC_DECL
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_FUNC_DECL_I_
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_FUNC_DECL_I_ extern __declspec(dllexport)
+ #else
+ #define SOL_FUNC_DECL_I_ extern __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_FUNC_DECL_I_ extern __attribute__((visibility("default")))
+ #else
+ #define SOL_FUNC_DECL_I_ extern
+ #endif
+#endif
+
+#if defined(SOL_FUNC_DEFN)
+ #define SOL_FUNC_DEFN_I_ SOL_FUNC_DEFN
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_FUNC_DEFN_I_ inline
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_FUNC_DEFN_I_ __declspec(dllexport)
+ #else
+ #define SOL_FUNC_DEFN_I_ __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_FUNC_DEFN_I_ __attribute__((visibility("default")))
+ #else
+ #define SOL_FUNC_DEFN_I_
+ #endif
+#endif
+
+#if defined(SOL_HIDDEN_FUNC_DECL)
+ #define SOL_HIDDEN_FUNC_DECL_I_ SOL_HIDDEN_FUNC_DECL
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_HIDDEN_FUNC_DECL_I_
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllexport)
+ #else
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __attribute__((visibility("default")))
+ #else
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern
+ #endif
+#endif
+
+#if defined(SOL_HIDDEN_FUNC_DEFN)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ SOL_HIDDEN_FUNC_DEFN
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ inline
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #else
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ __attribute__((visibility("hidden")))
+ #else
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #endif
+#endif
+
+// end of sol/detail/build_version.hpp
+
+// end of sol/version.hpp
+
+#include <utility>
+#include <type_traits>
+#include <string_view>
+
+#if SOL_IS_ON(SOL_USING_CXX_LUA) || SOL_IS_ON(SOL_USING_CXX_LUAJIT)
+struct lua_State;
+#else
+extern "C" {
+struct lua_State;
+}
+#endif // C++ Mangling for Lua vs. Not
+
+namespace sol {
+
+ enum class type;
+
+ class stateless_reference;
+ template <bool b>
+ class basic_reference;
+ using reference = basic_reference<false>;
+ using main_reference = basic_reference<true>;
+ class stateless_stack_reference;
+ class stack_reference;
+
+ template <typename A>
+ class basic_bytecode;
+
+ struct lua_value;
+
+ struct proxy_base_tag;
+ template <typename>
+ struct proxy_base;
+ template <typename, typename>
+ struct table_proxy;
+
+ template <bool, typename>
+ class basic_table_core;
+ template <bool b>
+ using table_core = basic_table_core<b, reference>;
+ template <bool b>
+ using main_table_core = basic_table_core<b, main_reference>;
+ template <bool b>
+ using stack_table_core = basic_table_core<b, stack_reference>;
+ template <typename base_type>
+ using basic_table = basic_table_core<false, base_type>;
+ using table = table_core<false>;
+ using global_table = table_core<true>;
+ using main_table = main_table_core<false>;
+ using main_global_table = main_table_core<true>;
+ using stack_table = stack_table_core<false>;
+ using stack_global_table = stack_table_core<true>;
+
+ template <typename>
+ struct basic_lua_table;
+ using lua_table = basic_lua_table<reference>;
+ using stack_lua_table = basic_lua_table<stack_reference>;
+
+ template <typename T, typename base_type>
+ class basic_usertype;
+ template <typename T>
+ using usertype = basic_usertype<T, reference>;
+ template <typename T>
+ using stack_usertype = basic_usertype<T, stack_reference>;
+
+ template <typename base_type>
+ class basic_metatable;
+ using metatable = basic_metatable<reference>;
+ using stack_metatable = basic_metatable<stack_reference>;
+
+ template <typename base_t>
+ struct basic_environment;
+ using environment = basic_environment<reference>;
+ using main_environment = basic_environment<main_reference>;
+ using stack_environment = basic_environment<stack_reference>;
+
+ template <typename T, bool>
+ class basic_function;
+ template <typename T, bool, typename H>
+ class basic_protected_function;
+ using unsafe_function = basic_function<reference, false>;
+ using safe_function = basic_protected_function<reference, false, reference>;
+ using main_unsafe_function = basic_function<main_reference, false>;
+ using main_safe_function = basic_protected_function<main_reference, false, reference>;
+ using stack_unsafe_function = basic_function<stack_reference, false>;
+ using stack_safe_function = basic_protected_function<stack_reference, false, reference>;
+ using stack_aligned_unsafe_function = basic_function<stack_reference, true>;
+ using stack_aligned_safe_function = basic_protected_function<stack_reference, true, reference>;
+ using protected_function = safe_function;
+ using main_protected_function = main_safe_function;
+ using stack_protected_function = stack_safe_function;
+ using stack_aligned_protected_function = stack_aligned_safe_function;
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS)
+ using function = protected_function;
+ using main_function = main_protected_function;
+ using stack_function = stack_protected_function;
+ using stack_aligned_function = stack_aligned_safe_function;
+#else
+ using function = unsafe_function;
+ using main_function = main_unsafe_function;
+ using stack_function = stack_unsafe_function;
+ using stack_aligned_function = stack_aligned_unsafe_function;
+#endif
+ using stack_aligned_stack_handler_function = basic_protected_function<stack_reference, true, stack_reference>;
+
+ struct unsafe_function_result;
+ struct protected_function_result;
+ using safe_function_result = protected_function_result;
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS)
+ using function_result = safe_function_result;
+#else
+ using function_result = unsafe_function_result;
+#endif
+
+ template <typename base_t>
+ class basic_object_base;
+ template <typename base_t>
+ class basic_object;
+ template <typename base_t>
+ class basic_userdata;
+ template <typename base_t>
+ class basic_lightuserdata;
+ template <typename base_t>
+ class basic_coroutine;
+ template <typename base_t>
+ class basic_packaged_coroutine;
+ template <typename base_t>
+ class basic_thread;
+
+ using object = basic_object<reference>;
+ using userdata = basic_userdata<reference>;
+ using lightuserdata = basic_lightuserdata<reference>;
+ using thread = basic_thread<reference>;
+ using coroutine = basic_coroutine<reference>;
+ using packaged_coroutine = basic_packaged_coroutine<reference>;
+ using main_object = basic_object<main_reference>;
+ using main_userdata = basic_userdata<main_reference>;
+ using main_lightuserdata = basic_lightuserdata<main_reference>;
+ using main_coroutine = basic_coroutine<main_reference>;
+ using stack_object = basic_object<stack_reference>;
+ using stack_userdata = basic_userdata<stack_reference>;
+ using stack_lightuserdata = basic_lightuserdata<stack_reference>;
+ using stack_thread = basic_thread<stack_reference>;
+ using stack_coroutine = basic_coroutine<stack_reference>;
+
+ struct stack_proxy_base;
+ struct stack_proxy;
+ struct variadic_args;
+ struct variadic_results;
+ struct stack_count;
+ struct this_state;
+ struct this_main_state;
+ struct this_environment;
+
+ class state_view;
+ class state;
+
+ template <typename T>
+ struct as_table_t;
+ template <typename T>
+ struct as_container_t;
+ template <typename T>
+ struct nested;
+ template <typename T>
+ struct light;
+ template <typename T>
+ struct user;
+ template <typename T>
+ struct as_args_t;
+ template <typename T>
+ struct protect_t;
+ template <typename F, typename... Policies>
+ struct policy_wrapper;
+
+ template <typename T>
+ struct usertype_traits;
+ template <typename T>
+ struct unique_usertype_traits;
+
+ template <typename... Args>
+ struct types {
+ typedef std::make_index_sequence<sizeof...(Args)> indices;
+ static constexpr std::size_t size() {
+ return sizeof...(Args);
+ }
+ };
+
+ template <typename T>
+ struct derive : std::false_type {
+ typedef types<> type;
+ };
+
+ template <typename T>
+ struct base : std::false_type {
+ typedef types<> type;
+ };
+
+ template <typename T>
+ struct weak_derive {
+ static bool value;
+ };
+
+ template <typename T>
+ bool weak_derive<T>::value = false;
+
+ namespace stack {
+ struct record;
+ }
+
+#if SOL_IS_OFF(SOL_USE_BOOST)
+ template <class T>
+ class optional;
+
+ template <class T>
+ class optional<T&>;
+#endif
+
+ using check_handler_type = int(lua_State*, int, type, type, const char*);
+
+} // namespace sol
+
+#define SOL_BASE_CLASSES(T, ...) \
+ namespace sol { \
+ template <> \
+ struct base<T> : std::true_type { \
+ typedef ::sol::types<__VA_ARGS__> type; \
+ }; \
+ } \
+ static_assert(true, "")
+#define SOL_DERIVED_CLASSES(T, ...) \
+ namespace sol { \
+ template <> \
+ struct derive<T> : std::true_type { \
+ typedef ::sol::types<__VA_ARGS__> type; \
+ }; \
+ } \
+ static_assert(true, "")
+
+#endif // SOL_FORWARD_HPP
+// end of sol/forward.hpp
+
+#endif // SOL_SINGLE_INCLUDE_SOL_FORWARD_HPP
diff --git a/src/libs/3rdparty/sol2/include/sol/sol.hpp b/src/libs/3rdparty/sol2/include/sol/sol.hpp
new file mode 100644
index 0000000000..063ea72165
--- /dev/null
+++ b/src/libs/3rdparty/sol2/include/sol/sol.hpp
@@ -0,0 +1,29202 @@
+// The MIT License (MIT)
+
+// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// This file was generated with a script.
+// Generated 2024-01-13 14:25:56.532275 UTC
+// This header was generated with sol v3.3.1 (revision 9c882a28)
+// https://github.com/ThePhD/sol2
+
+#ifndef SOL_SINGLE_INCLUDE_SOL_HPP
+#define SOL_SINGLE_INCLUDE_SOL_HPP
+
+// beginning of sol/sol.hpp
+
+#ifndef SOL_HPP
+#define SOL_HPP
+
+// beginning of sol/version.hpp
+
+#include "config.hpp"
+
+#define SOL_VERSION_MAJOR 3
+#define SOL_VERSION_MINOR 2
+#define SOL_VERSION_PATCH 3
+#define SOL_VERSION_STRING "3.2.3"
+#define SOL_VERSION ((SOL_VERSION_MAJOR * 100000) + (SOL_VERSION_MINOR * 100) + (SOL_VERSION_PATCH))
+
+#define SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN) #_TOKEN
+#define SOL_TOKEN_TO_STRING_I_(_TOKEN) SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN)
+
+#define SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT) _LEFT##_RIGHT
+#define SOL_CONCAT_TOKENS_I_(_LEFT, _RIGHT) SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT)
+
+#define SOL_RAW_IS_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) != 0)
+#define SOL_RAW_IS_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3) == 0)
+#define SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) > 3)
+#define SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3 OP_SYMBOL 3) < 0)
+
+#define SOL_IS_ON(OP_SYMBOL) SOL_RAW_IS_ON(OP_SYMBOL ## _I_)
+#define SOL_IS_OFF(OP_SYMBOL) SOL_RAW_IS_OFF(OP_SYMBOL ## _I_)
+#define SOL_IS_DEFAULT_ON(OP_SYMBOL) SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL ## _I_)
+#define SOL_IS_DEFAULT_OFF(OP_SYMBOL) SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL ## _I_)
+
+#define SOL_ON |
+#define SOL_OFF ^
+#define SOL_DEFAULT_ON +
+#define SOL_DEFAULT_OFF -
+
+#if defined(SOL_BUILD_CXX_MODE)
+ #if (SOL_BUILD_CXX_MODE != 0)
+ #define SOL_BUILD_CXX_MODE_I_ SOL_ON
+ #else
+ #define SOL_BUILD_CXX_MODE_I_ SOL_OFF
+ #endif
+#elif defined(__cplusplus)
+ #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_BUILD_C_MODE)
+ #if (SOL_BUILD_C_MODE != 0)
+ #define SOL_BUILD_C_MODE_I_ SOL_ON
+ #else
+ #define SOL_BUILD_C_MODE_I_ SOL_OFF
+ #endif
+#elif defined(__STDC__)
+ #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if SOL_IS_ON(SOL_BUILD_C_MODE)
+ #include <stddef.h>
+ #include <stdint.h>
+ #include <limits.h>
+#else
+ #include <cstddef>
+ #include <cstdint>
+ #include <climits>
+#endif
+
+#if defined(SOL_HAS_BUILTIN)
+ #define SOL_HAS_BUILTIN_I_(...) SOL_HAS_BUILTIN(__VA_ARGS__)
+#elif defined(__has_builtin)
+ #define SOL_HAS_BUILTIN_I_(...) __has_builtin(__VA_ARGS__)
+#else
+ #define SOL_HAS_BUILTIN_I_(...) 0
+#endif
+
+#if defined(SOL_COMPILER_VCXX)
+ #if defined(SOL_COMPILER_VCXX != 0)
+ #define SOL_COMPILER_VCXX_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_VCXX_I_ SOL_OFF
+ #endif
+#elif defined(_MSC_VER)
+ #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_GCC)
+ #if defined(SOL_COMPILER_GCC != 0)
+ #define SOL_COMPILER_GCC_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_GCC_I_ SOL_OFF
+ #endif
+#elif defined(__GNUC__)
+ #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_CLANG)
+ #if defined(SOL_COMPILER_CLANG != 0)
+ #define SOL_COMPILER_CLANG_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_CLANG_I_ SOL_OFF
+ #endif
+#elif defined(__clang__)
+ #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_EDG)
+ #if defined(SOL_COMPILER_EDG != 0)
+ #define SOL_COMPILER_EDG_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_EDG_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_COMPILER_EDG_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_MINGW)
+ #if (SOL_COMPILER_MINGW != 0)
+ #define SOL_COMPILER_MINGW_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_MINGW_I_ SOL_OFF
+ #endif
+#elif defined(__MINGW32__)
+ #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_OFF
+#endif
+
+#if SIZE_MAX <= 0xFFFFULL
+ #define SOL_PLATFORM_X16_I_ SOL_ON
+ #define SOL_PLATFORM_X86_I_ SOL_OFF
+ #define SOL_PLATFORM_X64_I_ SOL_OFF
+#elif SIZE_MAX <= 0xFFFFFFFFULL
+ #define SOL_PLATFORM_X16_I_ SOL_OFF
+ #define SOL_PLATFORM_X86_I_ SOL_ON
+ #define SOL_PLATFORM_X64_I_ SOL_OFF
+#else
+ #define SOL_PLATFORM_X16_I_ SOL_OFF
+ #define SOL_PLATFORM_X86_I_ SOL_OFF
+ #define SOL_PLATFORM_X64_I_ SOL_ON
+#endif
+
+#define SOL_PLATFORM_ARM32_I_ SOL_OFF
+#define SOL_PLATFORM_ARM64_I_ SOL_OFF
+
+#if defined(SOL_PLATFORM_WINDOWS)
+ #if (SOL_PLATFORM_WINDOWS != 0)
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_OFF
+ #endif
+#elif defined(_WIN32)
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_CYGWIN)
+ #if (SOL_PLATFORM_CYGWIN != 0)
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_ON
+ #endif
+#elif defined(__CYGWIN__)
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_APPLE)
+ #if (SOL_PLATFORM_APPLE != 0)
+ #define SOL_PLATFORM_APPLE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_APPLE_I_ SOL_OFF
+ #endif
+#elif defined(__APPLE__)
+ #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_UNIX)
+ #if (SOL_PLATFORM_UNIX != 0)
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_OFF
+ #endif
+#elif defined(__unix__)
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAUKT_ON
+#else
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_LINUX)
+ #if (SOL_PLATFORM_LINUX != 0)
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_OFF
+ #endif
+#elif defined(__LINUX__)
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAUKT_ON
+#else
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAULT_OFF
+#endif
+
+#define SOL_PLATFORM_APPLE_IPHONE_I_ SOL_OFF
+#define SOL_PLATFORM_BSDLIKE_I_ SOL_OFF
+
+#if defined(SOL_IN_DEBUG_DETECTED)
+ #if SOL_IN_DEBUG_DETECTED != 0
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #else
+ #define SOL_DEBUG_BUILD_I_ SOL_OFF
+ #endif
+#elif !defined(NDEBUG)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX) && defined(_DEBUG)
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #elif (SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)) && !defined(__OPTIMIZE__)
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #else
+ #define SOL_DEBUG_BUILD_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEBUG_BUILD_I_ SOL_DEFAULT_OFF
+#endif // We are in a debug mode of some sort
+
+#if defined(SOL_NO_EXCEPTIONS)
+ #if (SOL_NO_EXCEPTIONS != 0)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if !defined(_CPPUNWIND)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)
+ #if !defined(__EXCEPTIONS)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#else
+ #define SOL_EXCEPTIONS_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_NO_RTTI)
+ #if (SOL_NO_RTTI != 0)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if !defined(_CPPRTTI)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)
+ #if !defined(__GXX_RTTI)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#else
+ #define SOL_RTTI_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_NO_THREAD_LOCAL)
+ #if SOL_NO_THREAD_LOCAL != 0
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_OFF
+ #else
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_ON
+ #endif
+#else
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_DEFAULT_ON
+#endif // thread_local keyword is bjorked on some platforms
+
+#if defined(SOL_ALL_SAFETIES_ON)
+ #if SOL_ALL_SAFETIES_ON != 0
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_ON
+ #else
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_SAFE_GETTER)
+ #if SOL_SAFE_GETTER != 0
+ #define SOL_SAFE_GETTER_I_ SOL_ON
+ #else
+ #define SOL_SAFE_GETTER_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_GETTER_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_USERTYPE)
+ #if SOL_SAFE_USERTYPE != 0
+ #define SOL_SAFE_USERTYPE_I_ SOL_ON
+ #else
+ #define SOL_SAFE_USERTYPE_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_USERTYPE_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_REFERENCES)
+ #if SOL_SAFE_REFERENCES != 0
+ #define SOL_SAFE_REFERENCES_I_ SOL_ON
+ #else
+ #define SOL_SAFE_REFERENCES_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_REFERENCES_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_FUNCTIONS)
+ #if SOL_SAFE_FUNCTIONS != 0
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF
+ #endif
+#elif defined (SOL_SAFE_FUNCTION_OBJECTS)
+ #if SOL_SAFE_FUNCTION_OBJECTS != 0
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_FUNCTION_CALLS)
+ #if SOL_SAFE_FUNCTION_CALLS != 0
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_PROXIES)
+ #if SOL_SAFE_PROXIES != 0
+ #define SOL_SAFE_PROXIES_I_ SOL_ON
+ #else
+ #define SOL_SAFE_PROXIES_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_PROXIES_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_NUMERICS)
+ #if SOL_SAFE_NUMERICS != 0
+ #define SOL_SAFE_NUMERICS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_NUMERICS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_NUMERICS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_ALL_INTEGER_VALUES_FIT)
+ #if (SOL_ALL_INTEGER_VALUES_FIT != 0)
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_ON
+ #else
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_OFF
+ #endif
+#elif !SOL_IS_DEFAULT_OFF(SOL_SAFE_NUMERICS) && SOL_IS_OFF(SOL_SAFE_NUMERICS)
+ // if numerics is intentionally turned off, flip this on
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_ON
+#else
+ // default to off
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_SAFE_STACK_CHECK)
+ #if SOL_SAFE_STACK_CHECK != 0
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_ON
+ #else
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_NO_CHECK_NUMBER_PRECISION)
+ #if SOL_NO_CHECK_NUMBER_PRECISION != 0
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #endif
+#elif defined(SOL_NO_CHECKING_NUMBER_PRECISION)
+ #if SOL_NO_CHECKING_NUMBER_PRECISION != 0
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_SAFE_NUMERICS)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_STRINGS_ARE_NUMBERS)
+ #if (SOL_STRINGS_ARE_NUMBERS != 0)
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_ON
+ #else
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ENABLE_INTEROP)
+ #if SOL_ENABLE_INTEROP != 0
+ #define SOL_USE_INTEROP_I_ SOL_ON
+ #else
+ #define SOL_USE_INTEROP_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_INTEROP)
+ #if SOL_USE_INTEROP != 0
+ #define SOL_USE_INTEROP_I_ SOL_ON
+ #else
+ #define SOL_USE_INTEROP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_INTEROP_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_NO_NIL)
+ #if (SOL_NO_NIL != 0)
+ #define SOL_NIL_I_ SOL_OFF
+ #else
+ #define SOL_NIL_I_ SOL_ON
+ #endif
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || defined(__OBJC__) || defined(nil)
+ #define SOL_NIL_I_ SOL_DEFAULT_OFF
+#else
+ #define SOL_NIL_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_USERTYPE_TYPE_BINDING_INFO)
+ #if (SOL_USERTYPE_TYPE_BINDING_INFO != 0)
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_ON
+ #else
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_DEFAULT_ON
+#endif // We should generate a my_type.__type table with lots of class information for usertypes
+
+#if defined(SOL_AUTOMAGICAL_TYPES_BY_DEFAULT)
+ #if (SOL_AUTOMAGICAL_TYPES_BY_DEFAULT != 0)
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF
+ #endif
+#elif defined(SOL_DEFAULT_AUTOMAGICAL_USERTYPES)
+ #if (SOL_DEFAULT_AUTOMAGICAL_USERTYPES != 0)
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_DEFAULT_ON
+#endif // make is_automagical on/off by default
+
+#if defined(SOL_STD_VARIANT)
+ #if (SOL_STD_VARIANT != 0)
+ #define SOL_STD_VARIANT_I_ SOL_ON
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_COMPILER_CLANG) && SOL_IS_ON(SOL_PLATFORM_APPLE)
+ #if defined(__has_include)
+ #if __has_include(<variant>)
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF
+ #endif
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF
+ #endif
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON
+ #endif
+#endif // make is_automagical on/off by default
+
+#if defined(SOL_NOEXCEPT_FUNCTION_TYPE)
+ #if (SOL_NOEXCEPT_FUNCTION_TYPE != 0)
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON
+ #else
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF
+ #endif
+#else
+ #if defined(__cpp_noexcept_function_type)
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(_MSVC_LANG) && (_MSVC_LANG < 201403L))
+ // There is a bug in the VC++ compiler??
+ // on /std:c++latest under x86 conditions (VS 15.5.2),
+ // compiler errors are tossed for noexcept markings being on function types
+ // that are identical in every other way to their non-noexcept marked types function types...
+ // VS 2019: There is absolutely a bug.
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF
+ #else
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_DEFAULT_ON
+ #endif
+#endif // noexcept is part of a function's type
+
+#if defined(SOL_STACK_STRING_OPTIMIZATION_SIZE) && SOL_STACK_STRING_OPTIMIZATION_SIZE > 0
+ #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ SOL_STACK_STRING_OPTIMIZATION_SIZE
+#else
+ #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ 1024
+#endif
+
+#if defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0
+ #define SOL_ID_SIZE_I_ SOL_ID_SIZE
+#else
+ #define SOL_ID_SIZE_I_ 512
+#endif
+
+#if defined(LUA_IDSIZE) && LUA_IDSIZE > 0
+ #define SOL_FILE_ID_SIZE_I_ LUA_IDSIZE
+#elif defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0
+ #define SOL_FILE_ID_SIZE_I_ SOL_FILE_ID_SIZE
+#else
+ #define SOL_FILE_ID_SIZE_I_ 2048
+#endif
+
+#if defined(SOL_PRINT_ERRORS)
+ #if (SOL_PRINT_ERRORS != 0)
+ #define SOL_PRINT_ERRORS_I_ SOL_ON
+ #else
+ #define SOL_PRINT_ERRORS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_PRINT_ERRORS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_PRINT_ERRORS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_PRINT_ERRORS_I_ SOL_OFF
+ #endif
+#endif
+
+#if defined(SOL_DEFAULT_PASS_ON_ERROR)
+ #if (SOL_DEFAULT_PASS_ON_ERROR != 0)
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USING_CXX_LUA)
+ #if (SOL_USING_CXX_LUA != 0)
+ #define SOL_USING_CXX_LUA_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUA_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_CXX_LUA)
+ // alternative spelling
+ #if (SOL_USE_CXX_LUA != 0)
+ #define SOL_USING_CXX_LUA_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUA_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USING_CXX_LUA_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USING_CXX_LUAJIT)
+ #if (SOL_USING_CXX_LUAJIT != 0)
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_CXX_LUAJIT)
+ #if (SOL_USE_CXX_LUAJIT != 0)
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_NO_LUA_HPP)
+ #if (SOL_NO_LUA_HPP != 0)
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+ #else
+ #define SOL_USE_LUA_HPP_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_USING_CXX_LUA)
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+#elif defined(__has_include)
+ #if __has_include(<lua.hpp>)
+ #define SOL_USE_LUA_HPP_I_ SOL_ON
+ #else
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_LUA_HPP_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_CONTAINERS_START)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START
+#elif defined(SOL_CONTAINERS_START_INDEX)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START_INDEX
+#elif defined(SOL_CONTAINER_START_INDEX)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINER_START_INDEX
+#else
+ #define SOL_CONTAINER_START_INDEX_I_ 1
+#endif
+
+#if defined (SOL_NO_MEMORY_ALIGNMENT)
+ #if (SOL_NO_MEMORY_ALIGNMENT != 0)
+ #define SOL_ALIGN_MEMORY_I_ SOL_OFF
+ #else
+ #define SOL_ALIGN_MEMORY_I_ SOL_ON
+ #endif
+#else
+ #define SOL_ALIGN_MEMORY_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_USE_BOOST)
+ #if (SOL_USE_BOOST != 0)
+ #define SOL_USE_BOOST_I_ SOL_ON
+ #else
+ #define SOL_USE_BOOST_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_BOOST_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USE_UNSAFE_BASE_LOOKUP)
+ #if (SOL_USE_UNSAFE_BASE_LOOKUP != 0)
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_ON
+ #else
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_INSIDE_UNREAL)
+ #if (SOL_INSIDE_UNREAL != 0)
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON
+ #else
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_OFF
+ #endif
+#else
+ #if defined(UE_BUILD_DEBUG) || defined(UE_BUILD_DEVELOPMENT) || defined(UE_BUILD_TEST) || defined(UE_BUILD_SHIPPING) || defined(UE_SERVER)
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_NO_COMPAT)
+ #if (SOL_NO_COMPAT != 0)
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_OFF
+ #else
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_ON
+ #endif
+#else
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ #if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0)
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON
+ #else
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_CONTAINER_CHECK_IS_EXHAUSTIVE)
+ #if (SOL_CONTAINER_CHECK_IS_EXHAUSTIVE != 0)
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_ON
+ #else
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_FUNCTION_CALL_VALUE_SEMANTICS)
+ #if (SOL_FUNCTION_CALL_VALUE_SEMANTICS != 0)
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_ON
+ #else
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_MINGW_CCTYPE_IS_POISONED)
+ #if (SOL_MINGW_CCTYPE_IS_POISONED != 0)
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON
+ #else
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_MINGW) && defined(__GNUC__) && (__GNUC__ < 6)
+ // MinGW is off its rocker in some places...
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_CHAR8_T)
+ #if (SOL_CHAR8_T != 0)
+ #define SOL_CHAR8_T_I_ SOL_ON
+ #else
+ #define SOL_CHAR8_T_I_ SOL_OFF
+ #endif
+#else
+ #if defined(__cpp_char8_t)
+ #define SOL_CHAR8_T_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_CHAR8_T_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+ #include <boost/version.hpp>
+
+ #if BOOST_VERSION >= 107500 // Since Boost 1.75.0 boost::none is constexpr
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr
+ #else
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ const
+ #endif // BOOST_VERSION
+#else
+ // assume boost isn't using a garbage version
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr
+#endif
+
+#if defined(SOL2_CI)
+ #if (SOL2_CI != 0)
+ #define SOL2_CI_I_ SOL_ON
+ #else
+ #define SOL2_CI_I_ SOL_OFF
+ #endif
+#else
+ #define SOL2_CI_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ASSERT)
+ #define SOL_USER_ASSERT_I_ SOL_ON
+#else
+ #define SOL_USER_ASSERT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ASSERT_MSG)
+ #define SOL_USER_ASSERT_MSG_I_ SOL_ON
+#else
+ #define SOL_USER_ASSERT_MSG_I_ SOL_DEFAULT_OFF
+#endif
+
+// beginning of sol/prologue.hpp
+
+#if defined(SOL_PROLOGUE_I_)
+ #error "[sol2] Library Prologue was already included in translation unit and not properly ended with an epilogue."
+#endif
+
+#define SOL_PROLOGUE_I_ 1
+
+#if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #define _FWD(...) static_cast<decltype( __VA_ARGS__ )&&>( __VA_ARGS__ )
+
+ #if SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define _MOVE(...) static_cast<__typeof( __VA_ARGS__ )&&>( __VA_ARGS__ )
+ #else
+ #include <type_traits>
+
+ #define _MOVE(...) static_cast<::std::remove_reference_t<( __VA_ARGS__ )>&&>( __VA_OPT__(,) )
+ #endif
+#endif
+
+// end of sol/prologue.hpp
+
+// beginning of sol/epilogue.hpp
+
+#if !defined(SOL_PROLOGUE_I_)
+ #error "[sol2] Library Prologue is missing from this translation unit."
+#else
+ #undef SOL_PROLOGUE_I_
+#endif
+
+#if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #undef _FWD
+ #undef _MOVE
+#endif
+
+// end of sol/epilogue.hpp
+
+// beginning of sol/detail/build_version.hpp
+
+#if defined(SOL_DLL)
+ #if (SOL_DLL != 0)
+ #define SOL_DLL_I_ SOL_ON
+ #else
+ #define SOL_DLL_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(DLL_) || defined(_DLL))
+ #define SOL_DLL_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_DLL_I_ SOL_DEFAULT_OFF
+#endif // DLL definition
+
+#if defined(SOL_HEADER_ONLY)
+ #if (SOL_HEADER_ONLY != 0)
+ #define SOL_HEADER_ONLY_I_ SOL_ON
+ #else
+ #define SOL_HEADER_ONLY_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_HEADER_ONLY_I_ SOL_DEFAULT_OFF
+#endif // Header only library
+
+#if defined(SOL_BUILD)
+ #if (SOL_BUILD != 0)
+ #define SOL_BUILD_I_ SOL_ON
+ #else
+ #define SOL_BUILD_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_BUILD_I_ SOL_DEFAULT_OFF
+#else
+ #define SOL_BUILD_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_UNITY_BUILD)
+ #if (SOL_UNITY_BUILD != 0)
+ #define SOL_UNITY_BUILD_I_ SOL_ON
+ #else
+ #define SOL_UNITY_BUILD_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_UNITY_BUILD_I_ SOL_DEFAULT_OFF
+#endif // Header only library
+
+#if defined(SOL_C_FUNCTION_LINKAGE)
+ #define SOL_C_FUNCTION_LINKAGE_I_ SOL_C_FUNCTION_LINKAGE
+#else
+ #if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ // C++
+ #define SOL_C_FUNCTION_LINKAGE_I_ extern "C"
+ #else
+ // normal
+ #define SOL_C_FUNCTION_LINKAGE_I_
+ #endif // C++ or not
+#endif // Linkage specification for C functions
+
+#if defined(SOL_API_LINKAGE)
+ #define SOL_API_LINKAGE_I_ SOL_API_LINKAGE
+#else
+ #if SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX) || SOL_IS_ON(SOL_PLATFORM_WINDOWS) || SOL_IS_ON(SOL_PLATFORM_CYGWIN)
+ // MSVC Compiler; or, Windows, or Cygwin platforms
+ #if SOL_IS_ON(SOL_BUILD)
+ // Building the library
+ #if SOL_IS_ON(SOL_COMPILER_GCC)
+ // Using GCC
+ #define SOL_API_LINKAGE_I_ __attribute__((dllexport))
+ #else
+ // Using Clang, MSVC, etc...
+ #define SOL_API_LINKAGE_I_ __declspec(dllexport)
+ #endif
+ #else
+ #if SOL_IS_ON(SOL_COMPILER_GCC)
+ #define SOL_API_LINKAGE_I_ __attribute__((dllimport))
+ #else
+ #define SOL_API_LINKAGE_I_ __declspec(dllimport)
+ #endif
+ #endif
+ #else
+ // extern if building normally on non-MSVC
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #elif SOL_IS_ON(SOL_UNITY_BUILD)
+ // Built-in library, like how stb typical works
+ #if SOL_IS_ON(SOL_HEADER_ONLY)
+ // Header only, so functions are defined "inline"
+ #define SOL_API_LINKAGE_I_ inline
+ #else
+ // Not header only, so seperately compiled files
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #else
+ // Normal static library
+ #if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #define SOL_API_LINKAGE_I_
+ #else
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #endif // DLL or not
+#endif // Build definitions
+
+#if defined(SOL_PUBLIC_FUNC_DECL)
+ #define SOL_PUBLIC_FUNC_DECL_I_ SOL_PUBLIC_FUNC_DECL
+#else
+ #define SOL_PUBLIC_FUNC_DECL_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_INTERNAL_FUNC_DECL_)
+ #define SOL_INTERNAL_FUNC_DECL_I_ SOL_INTERNAL_FUNC_DECL_
+#else
+ #define SOL_INTERNAL_FUNC_DECL_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_PUBLIC_FUNC_DEF)
+ #define SOL_PUBLIC_FUNC_DEF_I_ SOL_PUBLIC_FUNC_DEF
+#else
+ #define SOL_PUBLIC_FUNC_DEF_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_INTERNAL_FUNC_DEF)
+ #define SOL_INTERNAL_FUNC_DEF_I_ SOL_INTERNAL_FUNC_DEF
+#else
+ #define SOL_INTERNAL_FUNC_DEF_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_FUNC_DECL)
+ #define SOL_FUNC_DECL_I_ SOL_FUNC_DECL
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_FUNC_DECL_I_
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_FUNC_DECL_I_ extern __declspec(dllexport)
+ #else
+ #define SOL_FUNC_DECL_I_ extern __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_FUNC_DECL_I_ extern __attribute__((visibility("default")))
+ #else
+ #define SOL_FUNC_DECL_I_ extern
+ #endif
+#endif
+
+#if defined(SOL_FUNC_DEFN)
+ #define SOL_FUNC_DEFN_I_ SOL_FUNC_DEFN
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_FUNC_DEFN_I_ inline
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_FUNC_DEFN_I_ __declspec(dllexport)
+ #else
+ #define SOL_FUNC_DEFN_I_ __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_FUNC_DEFN_I_ __attribute__((visibility("default")))
+ #else
+ #define SOL_FUNC_DEFN_I_
+ #endif
+#endif
+
+#if defined(SOL_HIDDEN_FUNC_DECL)
+ #define SOL_HIDDEN_FUNC_DECL_I_ SOL_HIDDEN_FUNC_DECL
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_HIDDEN_FUNC_DECL_I_
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllexport)
+ #else
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __attribute__((visibility("default")))
+ #else
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern
+ #endif
+#endif
+
+#if defined(SOL_HIDDEN_FUNC_DEFN)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ SOL_HIDDEN_FUNC_DEFN
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ inline
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #else
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ __attribute__((visibility("hidden")))
+ #else
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #endif
+#endif
+
+// end of sol/detail/build_version.hpp
+
+// end of sol/version.hpp
+
+#if SOL_IS_ON(SOL_INSIDE_UNREAL_ENGINE)
+#ifdef check
+#pragma push_macro("check")
+#undef check
+#endif
+#endif // Unreal Engine 4 Bullshit
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wconversion"
+#if __GNUC__ > 6
+#pragma GCC diagnostic ignored "-Wnoexcept-type"
+#endif
+#elif SOL_IS_ON(SOL_COMPILER_CLANG)
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+#pragma warning(push)
+#pragma warning(disable : 4505) // unreferenced local function has been removed GEE THANKS
+#endif // clang++ vs. g++ vs. VC++
+
+// beginning of sol/forward.hpp
+
+#ifndef SOL_FORWARD_HPP
+#define SOL_FORWARD_HPP
+
+#include <utility>
+#include <type_traits>
+#include <string_view>
+
+#if SOL_IS_ON(SOL_USING_CXX_LUA) || SOL_IS_ON(SOL_USING_CXX_LUAJIT)
+struct lua_State;
+#else
+extern "C" {
+struct lua_State;
+}
+#endif // C++ Mangling for Lua vs. Not
+
+namespace sol {
+
+ enum class type;
+
+ class stateless_reference;
+ template <bool b>
+ class basic_reference;
+ using reference = basic_reference<false>;
+ using main_reference = basic_reference<true>;
+ class stateless_stack_reference;
+ class stack_reference;
+
+ template <typename A>
+ class basic_bytecode;
+
+ struct lua_value;
+
+ struct proxy_base_tag;
+ template <typename>
+ struct proxy_base;
+ template <typename, typename>
+ struct table_proxy;
+
+ template <bool, typename>
+ class basic_table_core;
+ template <bool b>
+ using table_core = basic_table_core<b, reference>;
+ template <bool b>
+ using main_table_core = basic_table_core<b, main_reference>;
+ template <bool b>
+ using stack_table_core = basic_table_core<b, stack_reference>;
+ template <typename base_type>
+ using basic_table = basic_table_core<false, base_type>;
+ using table = table_core<false>;
+ using global_table = table_core<true>;
+ using main_table = main_table_core<false>;
+ using main_global_table = main_table_core<true>;
+ using stack_table = stack_table_core<false>;
+ using stack_global_table = stack_table_core<true>;
+
+ template <typename>
+ struct basic_lua_table;
+ using lua_table = basic_lua_table<reference>;
+ using stack_lua_table = basic_lua_table<stack_reference>;
+
+ template <typename T, typename base_type>
+ class basic_usertype;
+ template <typename T>
+ using usertype = basic_usertype<T, reference>;
+ template <typename T>
+ using stack_usertype = basic_usertype<T, stack_reference>;
+
+ template <typename base_type>
+ class basic_metatable;
+ using metatable = basic_metatable<reference>;
+ using stack_metatable = basic_metatable<stack_reference>;
+
+ template <typename base_t>
+ struct basic_environment;
+ using environment = basic_environment<reference>;
+ using main_environment = basic_environment<main_reference>;
+ using stack_environment = basic_environment<stack_reference>;
+
+ template <typename T, bool>
+ class basic_function;
+ template <typename T, bool, typename H>
+ class basic_protected_function;
+ using unsafe_function = basic_function<reference, false>;
+ using safe_function = basic_protected_function<reference, false, reference>;
+ using main_unsafe_function = basic_function<main_reference, false>;
+ using main_safe_function = basic_protected_function<main_reference, false, reference>;
+ using stack_unsafe_function = basic_function<stack_reference, false>;
+ using stack_safe_function = basic_protected_function<stack_reference, false, reference>;
+ using stack_aligned_unsafe_function = basic_function<stack_reference, true>;
+ using stack_aligned_safe_function = basic_protected_function<stack_reference, true, reference>;
+ using protected_function = safe_function;
+ using main_protected_function = main_safe_function;
+ using stack_protected_function = stack_safe_function;
+ using stack_aligned_protected_function = stack_aligned_safe_function;
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS)
+ using function = protected_function;
+ using main_function = main_protected_function;
+ using stack_function = stack_protected_function;
+ using stack_aligned_function = stack_aligned_safe_function;
+#else
+ using function = unsafe_function;
+ using main_function = main_unsafe_function;
+ using stack_function = stack_unsafe_function;
+ using stack_aligned_function = stack_aligned_unsafe_function;
+#endif
+ using stack_aligned_stack_handler_function = basic_protected_function<stack_reference, true, stack_reference>;
+
+ struct unsafe_function_result;
+ struct protected_function_result;
+ using safe_function_result = protected_function_result;
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS)
+ using function_result = safe_function_result;
+#else
+ using function_result = unsafe_function_result;
+#endif
+
+ template <typename base_t>
+ class basic_object_base;
+ template <typename base_t>
+ class basic_object;
+ template <typename base_t>
+ class basic_userdata;
+ template <typename base_t>
+ class basic_lightuserdata;
+ template <typename base_t>
+ class basic_coroutine;
+ template <typename base_t>
+ class basic_packaged_coroutine;
+ template <typename base_t>
+ class basic_thread;
+
+ using object = basic_object<reference>;
+ using userdata = basic_userdata<reference>;
+ using lightuserdata = basic_lightuserdata<reference>;
+ using thread = basic_thread<reference>;
+ using coroutine = basic_coroutine<reference>;
+ using packaged_coroutine = basic_packaged_coroutine<reference>;
+ using main_object = basic_object<main_reference>;
+ using main_userdata = basic_userdata<main_reference>;
+ using main_lightuserdata = basic_lightuserdata<main_reference>;
+ using main_coroutine = basic_coroutine<main_reference>;
+ using stack_object = basic_object<stack_reference>;
+ using stack_userdata = basic_userdata<stack_reference>;
+ using stack_lightuserdata = basic_lightuserdata<stack_reference>;
+ using stack_thread = basic_thread<stack_reference>;
+ using stack_coroutine = basic_coroutine<stack_reference>;
+
+ struct stack_proxy_base;
+ struct stack_proxy;
+ struct variadic_args;
+ struct variadic_results;
+ struct stack_count;
+ struct this_state;
+ struct this_main_state;
+ struct this_environment;
+
+ class state_view;
+ class state;
+
+ template <typename T>
+ struct as_table_t;
+ template <typename T>
+ struct as_container_t;
+ template <typename T>
+ struct nested;
+ template <typename T>
+ struct light;
+ template <typename T>
+ struct user;
+ template <typename T>
+ struct as_args_t;
+ template <typename T>
+ struct protect_t;
+ template <typename F, typename... Policies>
+ struct policy_wrapper;
+
+ template <typename T>
+ struct usertype_traits;
+ template <typename T>
+ struct unique_usertype_traits;
+
+ template <typename... Args>
+ struct types {
+ typedef std::make_index_sequence<sizeof...(Args)> indices;
+ static constexpr std::size_t size() {
+ return sizeof...(Args);
+ }
+ };
+
+ template <typename T>
+ struct derive : std::false_type {
+ typedef types<> type;
+ };
+
+ template <typename T>
+ struct base : std::false_type {
+ typedef types<> type;
+ };
+
+ template <typename T>
+ struct weak_derive {
+ static bool value;
+ };
+
+ template <typename T>
+ bool weak_derive<T>::value = false;
+
+ namespace stack {
+ struct record;
+ }
+
+#if SOL_IS_OFF(SOL_USE_BOOST)
+ template <class T>
+ class optional;
+
+ template <class T>
+ class optional<T&>;
+#endif
+
+ using check_handler_type = int(lua_State*, int, type, type, const char*);
+
+} // namespace sol
+
+#define SOL_BASE_CLASSES(T, ...) \
+ namespace sol { \
+ template <> \
+ struct base<T> : std::true_type { \
+ typedef ::sol::types<__VA_ARGS__> type; \
+ }; \
+ } \
+ static_assert(true, "")
+#define SOL_DERIVED_CLASSES(T, ...) \
+ namespace sol { \
+ template <> \
+ struct derive<T> : std::true_type { \
+ typedef ::sol::types<__VA_ARGS__> type; \
+ }; \
+ } \
+ static_assert(true, "")
+
+#endif // SOL_FORWARD_HPP
+// end of sol/forward.hpp
+
+// beginning of sol/forward_detail.hpp
+
+#ifndef SOL_FORWARD_DETAIL_HPP
+#define SOL_FORWARD_DETAIL_HPP
+
+// beginning of sol/traits.hpp
+
+// beginning of sol/tuple.hpp
+
+// beginning of sol/base_traits.hpp
+
+#include <type_traits>
+
+namespace sol {
+ namespace detail {
+ struct unchecked_t { };
+ const unchecked_t unchecked = unchecked_t {};
+ } // namespace detail
+
+ namespace meta {
+ using sfinae_yes_t = std::true_type;
+ using sfinae_no_t = std::false_type;
+
+ template <typename...>
+ using void_t = void;
+
+ template <typename T>
+ using unqualified = std::remove_cv<std::remove_reference_t<T>>;
+
+ template <typename T>
+ using unqualified_t = typename unqualified<T>::type;
+
+ namespace meta_detail {
+ template <typename T>
+ struct unqualified_non_alias : unqualified<T> { };
+
+ template <template <class...> class Test, class, class... Args>
+ struct is_detected : std::false_type { };
+
+ template <template <class...> class Test, class... Args>
+ struct is_detected<Test, void_t<Test<Args...>>, Args...> : std::true_type { };
+ } // namespace meta_detail
+
+ template <template <class...> class Trait, class... Args>
+ using is_detected = typename meta_detail::is_detected<Trait, void, Args...>::type;
+
+ template <template <class...> class Trait, class... Args>
+ constexpr inline bool is_detected_v = is_detected<Trait, Args...>::value;
+
+ template <typename _Default, typename _Void, template <typename...> typename _Op, typename... _Args>
+ class detector {
+ public:
+ using value_t = ::std::false_type;
+ using type = _Default;
+ };
+
+ template <typename _Default, template <typename...> typename _Op, typename... _Args>
+ class detector<_Default, void_t<_Op<_Args...>>, _Op, _Args...> {
+ public:
+ using value_t = ::std::true_type;
+ using type = _Op<_Args...>;
+ };
+
+ class nonesuch {
+ public:
+ ~nonesuch() = delete;
+ nonesuch(nonesuch const&) = delete;
+ nonesuch& operator=(nonesuch const&) = delete;
+ };
+
+ template <template <typename...> typename _Op, typename... _Args>
+ using detected_t = typename detector<nonesuch, void, _Op, _Args...>::type;
+
+ template <typename _Default, template <typename...> typename _Op, typename... _Args>
+ using detected_or = detector<_Default, void, _Op, _Args...>;
+
+ template <typename _Default, template <typename...> typename _Op, typename... _Args>
+ using detected_or_t = typename detector<_Default, void, _Op, _Args...>::type;
+
+ template <typename _Default, template <typename...> typename _Op, typename... _Args>
+ constexpr inline bool detected_or_v = detector<_Default, void, _Op, _Args...>::value;
+
+ template <std::size_t I>
+ using index_value = std::integral_constant<std::size_t, I>;
+
+ template <bool>
+ struct conditional {
+ template <typename T, typename U>
+ using type = T;
+ };
+
+ template <>
+ struct conditional<false> {
+ template <typename T, typename U>
+ using type = U;
+ };
+
+ template <bool B, typename T, typename U>
+ using conditional_t = typename conditional<B>::template type<T, U>;
+
+ namespace meta_detail {
+ template <typename T, template <typename...> class Templ>
+ struct is_specialization_of : std::false_type { };
+ template <typename... T, template <typename...> class Templ>
+ struct is_specialization_of<Templ<T...>, Templ> : std::true_type { };
+ } // namespace meta_detail
+
+ template <typename T, template <typename...> class Templ>
+ using is_specialization_of = meta_detail::is_specialization_of<std::remove_cv_t<T>, Templ>;
+
+ template <typename T, template <typename...> class Templ>
+ inline constexpr bool is_specialization_of_v = is_specialization_of<std::remove_cv_t<T>, Templ>::value;
+
+ template <typename T>
+ struct identity {
+ typedef T type;
+ };
+
+ template <typename T>
+ using identity_t = typename identity<T>::type;
+
+ template <typename T>
+ using is_builtin_type = std::integral_constant<bool, std::is_arithmetic<T>::value || std::is_pointer<T>::value || std::is_array<T>::value>;
+
+ namespace meta_detail {
+ template <typename T, typename = void>
+ struct has_internal_marker_impl : std::false_type { };
+ template <typename T>
+ struct has_internal_marker_impl<T, void_t<typename T::SOL_INTERNAL_UNSPECIALIZED_MARKER_>> : std::true_type { };
+
+ template <typename T>
+ using has_internal_marker = has_internal_marker_impl<T>;
+
+ template <typename T>
+ constexpr inline bool has_internal_marker_v = has_internal_marker<T>::value;
+ } // namespace meta_detail
+
+ } // namespace meta
+} // namespace sol
+
+// end of sol/base_traits.hpp
+
+#include <tuple>
+#include <cstddef>
+
+namespace sol {
+ namespace detail {
+ using swallow = std::initializer_list<int>;
+ } // namespace detail
+
+ namespace meta {
+ template <typename T>
+ using is_tuple = is_specialization_of<T, std::tuple>;
+
+ template <typename T>
+ constexpr inline bool is_tuple_v = is_tuple<T>::value;
+
+ namespace detail {
+ template <typename... Args>
+ struct tuple_types_ {
+ typedef types<Args...> type;
+ };
+
+ template <typename... Args>
+ struct tuple_types_<std::tuple<Args...>> {
+ typedef types<Args...> type;
+ };
+ } // namespace detail
+
+ template <typename... Args>
+ using tuple_types = typename detail::tuple_types_<Args...>::type;
+
+ template <typename Arg>
+ struct pop_front_type;
+
+ template <typename Arg>
+ using pop_front_type_t = typename pop_front_type<Arg>::type;
+
+ template <typename... Args>
+ struct pop_front_type<types<Args...>> {
+ typedef void front_type;
+ typedef types<Args...> type;
+ };
+
+ template <typename Arg, typename... Args>
+ struct pop_front_type<types<Arg, Args...>> {
+ typedef Arg front_type;
+ typedef types<Args...> type;
+ };
+
+ template <std::size_t N, typename Tuple>
+ using tuple_element = std::tuple_element<N, std::remove_reference_t<Tuple>>;
+
+ template <std::size_t N, typename Tuple>
+ using tuple_element_t = std::tuple_element_t<N, std::remove_reference_t<Tuple>>;
+
+ template <std::size_t N, typename Tuple>
+ using unqualified_tuple_element = unqualified<tuple_element_t<N, Tuple>>;
+
+ template <std::size_t N, typename Tuple>
+ using unqualified_tuple_element_t = unqualified_t<tuple_element_t<N, Tuple>>;
+
+ } // namespace meta
+} // namespace sol
+
+// end of sol/tuple.hpp
+
+// beginning of sol/bind_traits.hpp
+
+namespace sol { namespace meta {
+ namespace meta_detail {
+ template <typename F>
+ using detect_deducible_signature = decltype(&F::operator());
+ } // namespace meta_detail
+
+ template <typename F>
+ using call_operator_deducible = typename is_detected<meta_detail::detect_deducible_signature, F>::type;
+
+ template <typename F>
+ constexpr inline bool call_operator_deducible_v = call_operator_deducible<F>::value;
+
+ namespace meta_detail {
+
+ template <std::size_t I, typename T>
+ struct void_tuple_element : meta::tuple_element<I, T> { };
+
+ template <std::size_t I>
+ struct void_tuple_element<I, std::tuple<>> {
+ typedef void type;
+ };
+
+ template <std::size_t I, typename T>
+ using void_tuple_element_t = typename void_tuple_element<I, T>::type;
+
+ template <bool it_is_noexcept, bool has_c_variadic, typename T, typename R, typename... Args>
+ struct basic_traits {
+ private:
+ using first_type = meta::conditional_t<std::is_void<T>::value, int, T>&;
+
+ public:
+ inline static constexpr const bool is_noexcept = it_is_noexcept;
+ inline static constexpr bool is_member_function = std::is_void<T>::value;
+ inline static constexpr bool has_c_var_arg = has_c_variadic;
+ inline static constexpr std::size_t arity = sizeof...(Args);
+ inline static constexpr std::size_t free_arity = sizeof...(Args) + static_cast<std::size_t>(!std::is_void<T>::value);
+ typedef types<Args...> args_list;
+ typedef std::tuple<Args...> args_tuple;
+ typedef T object_type;
+ typedef R return_type;
+ typedef tuple_types<R> returns_list;
+ typedef R(function_type)(Args...);
+ typedef meta::conditional_t<std::is_void<T>::value, args_list, types<first_type, Args...>> free_args_list;
+ typedef meta::conditional_t<std::is_void<T>::value, R(Args...), R(first_type, Args...)> free_function_type;
+ typedef meta::conditional_t<std::is_void<T>::value, R (*)(Args...), R (*)(first_type, Args...)> free_function_pointer_type;
+ typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
+ template <std::size_t i>
+ using arg_at = void_tuple_element_t<i, args_tuple>;
+ };
+
+ template <typename Signature, bool b = call_operator_deducible<Signature>::value>
+ struct fx_traits : public basic_traits<false, false, void, void> { };
+
+ // Free Functions
+ template <typename R, typename... Args>
+ struct fx_traits<R(Args...), false> : public basic_traits<false, false, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args...);
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R (*)(Args...), false> : public basic_traits<false, false, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args...);
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R(Args..., ...), false> : public basic_traits<false, true, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args..., ...);
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R (*)(Args..., ...), false> : public basic_traits<false, true, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args..., ...);
+ };
+
+ // Member Functions
+ /* C-Style Variadics */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...), false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...);
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...), false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...);
+ };
+
+ /* Const Volatile */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile;
+ };
+
+ /* Member Function Qualifiers */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...)&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) &;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...)&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) &;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...)&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) &&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...)&&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) &&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const&&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const&&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const&&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile&&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile&&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile&&;
+ };
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+
+ template <typename R, typename... Args>
+ struct fx_traits<R(Args...) noexcept, false> : public basic_traits<true, false, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args...) noexcept;
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R (*)(Args...) noexcept, false> : public basic_traits<true, false, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args...) noexcept;
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R(Args..., ...) noexcept, false> : public basic_traits<true, true, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args..., ...) noexcept;
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R (*)(Args..., ...) noexcept, false> : public basic_traits<true, true, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args..., ...) noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) noexcept;
+ };
+
+ /* Const Volatile */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...)& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) & noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...)& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) & noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...)&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) && noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...)&& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) && noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const&& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const&& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const&& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile&& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile&& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile&& noexcept;
+ };
+
+#endif // noexcept is part of a function's type
+
+#if SOL_IS_ON(SOL_COMPILER_VCXX) && SOL_IS_ON(SOL_PLATFORM_X86)
+ template <typename R, typename... Args>
+ struct fx_traits<R __stdcall(Args...), false> : public basic_traits<false, false, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args...);
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R(__stdcall*)(Args...), false> : public basic_traits<false, false, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args...);
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...), false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...);
+ };
+
+ /* Const Volatile */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile;
+ };
+
+ /* Member Function Qualifiers */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...)&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) &;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...)&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) &&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const&&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile&&;
+ };
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+
+ template <typename R, typename... Args>
+ struct fx_traits<R __stdcall(Args...) noexcept, false> : public basic_traits<true, false, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args...) noexcept;
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R(__stdcall*)(Args...) noexcept, false> : public basic_traits<true, false, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args...) noexcept;
+ };
+
+ /* __stdcall cannot be applied to functions with varargs*/
+ /*template <typename R, typename... Args>
+ struct fx_traits<__stdcall R(Args..., ...) noexcept, false> : public basic_traits<true, true, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args..., ...) noexcept;
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R (__stdcall *)(Args..., ...) noexcept, false> : public basic_traits<true, true, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args..., ...) noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) noexcept;
+ };*/
+
+ /* Const Volatile */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const volatile noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const volatile noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...)& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) & noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) & noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) & noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const& noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const& noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile& noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const volatile& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const volatile& noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...)&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) && noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) && noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) && noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const&& noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const&& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const&& noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile&& noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const volatile&& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const volatile&& noexcept;
+ };*/
+#endif // noexcept is part of a function's type
+#endif // __stdcall x86 VC++ bug
+
+ template <typename Signature>
+ struct fx_traits<Signature, true> : public fx_traits<typename fx_traits<decltype(&Signature::operator())>::function_type, false> { };
+
+ template <typename Signature, bool b = std::is_member_object_pointer<Signature>::value>
+ struct callable_traits : public fx_traits<std::decay_t<Signature>> { };
+
+ template <typename R, typename T>
+ struct callable_traits<R(T::*), true> {
+ typedef meta::conditional_t<std::is_array_v<R>, std::add_lvalue_reference_t<R>, R> return_type;
+ typedef return_type Arg;
+ typedef T object_type;
+ using signature_type = R(T::*);
+ inline static constexpr bool is_noexcept = false;
+ inline static constexpr bool is_member_function = false;
+ inline static constexpr std::size_t arity = 1;
+ inline static constexpr std::size_t free_arity = 2;
+ typedef std::tuple<Arg> args_tuple;
+ typedef types<Arg> args_list;
+ typedef types<T, Arg> free_args_list;
+ typedef meta::tuple_types<return_type> returns_list;
+ typedef return_type(function_type)(T&, return_type);
+ typedef return_type (*function_pointer_type)(T&, Arg);
+ typedef return_type (*free_function_pointer_type)(T&, Arg);
+ template <std::size_t i>
+ using arg_at = void_tuple_element_t<i, args_tuple>;
+ };
+
+ } // namespace meta_detail
+
+ template <typename Signature>
+ using bind_traits = meta_detail::callable_traits<Signature>;
+
+ namespace meta_detail {
+ template <typename, bool>
+ struct is_probably_stateless_lambda : std::false_type { };
+
+ template <typename T>
+ struct is_probably_stateless_lambda<T, true> : std::is_convertible<T, typename bind_traits<T>::function_type*>::type { };
+ } // namespace meta_detail
+
+ template <typename T>
+ using is_probably_stateless_lambda = typename meta_detail::is_probably_stateless_lambda<T, std::is_empty_v<T> && call_operator_deducible_v<T>>::type;
+
+ template <typename T>
+ inline constexpr bool is_probably_stateless_lambda_v = is_probably_stateless_lambda<T>::value;
+
+ template <typename Signature>
+ using function_args_t = typename bind_traits<Signature>::args_list;
+
+ template <typename Signature>
+ using function_signature_t = typename bind_traits<Signature>::signature_type;
+
+ template <typename Signature>
+ using function_return_t = typename bind_traits<Signature>::return_type;
+}} // namespace sol::meta
+
+// end of sol/bind_traits.hpp
+
+// beginning of sol/pointer_like.hpp
+
+#include <utility>
+#include <type_traits>
+#include <memory>
+
+namespace sol {
+
+ namespace meta {
+ namespace meta_detail {
+ template <typename T>
+ using is_dereferenceable_test = decltype(*std::declval<T>());
+
+ template <typename T>
+ using is_explicitly_dereferenceable_test = decltype(std::declval<T>().operator*());
+ } // namespace meta_detail
+
+ template <typename T>
+ using is_pointer_like = std::integral_constant<bool,
+ !std::is_array_v<T> && (std::is_pointer_v<T> || is_detected_v<meta_detail::is_explicitly_dereferenceable_test, T>)>;
+
+ template <typename T>
+ constexpr inline bool is_pointer_like_v = is_pointer_like<T>::value;
+ } // namespace meta
+
+ namespace detail {
+
+ template <typename T>
+ auto unwrap(T&& item) -> decltype(std::forward<T>(item)) {
+ return std::forward<T>(item);
+ }
+
+ template <typename T>
+ T& unwrap(std::reference_wrapper<T> arg) {
+ return arg.get();
+ }
+
+ template <typename T>
+ inline decltype(auto) deref(T&& item) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::is_pointer_like_v<Tu>) {
+ return *std::forward<T>(item);
+ }
+ else {
+ return std::forward<T>(item);
+ }
+ }
+
+ template <typename T>
+ inline decltype(auto) deref_move_only(T&& item) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::is_pointer_like_v<Tu> && !std::is_pointer_v<Tu> && !std::is_copy_constructible_v<Tu>) {
+ return *std::forward<T>(item);
+ }
+ else {
+ return std::forward<T>(item);
+ }
+ }
+
+ template <typename T>
+ inline T* ptr(T& val) {
+ return std::addressof(val);
+ }
+
+ template <typename T>
+ inline T* ptr(std::reference_wrapper<T> val) {
+ return std::addressof(val.get());
+ }
+
+ template <typename T>
+ inline T* ptr(T* val) {
+ return val;
+ }
+ } // namespace detail
+} // namespace sol
+
+// end of sol/pointer_like.hpp
+
+// beginning of sol/string_view.hpp
+
+#include <cstddef>
+#include <string>
+#include <string_view>
+#include <functional>
+
+namespace sol {
+ template <typename C, typename T = std::char_traits<C>>
+ using basic_string_view = std::basic_string_view<C, T>;
+
+ typedef std::string_view string_view;
+ typedef std::wstring_view wstring_view;
+ typedef std::u16string_view u16string_view;
+ typedef std::u32string_view u32string_view;
+ typedef std::hash<std::string_view> string_view_hash;
+} // namespace sol
+
+// end of sol/string_view.hpp
+
+#include <type_traits>
+#include <cstdint>
+#include <memory>
+#include <functional>
+#include <array>
+#include <iterator>
+#include <iosfwd>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // variant is weird on XCode, thanks XCode
+
+namespace sol { namespace meta {
+ template <typename T>
+ struct unwrapped {
+ typedef T type;
+ };
+
+ template <typename T>
+ struct unwrapped<std::reference_wrapper<T>> {
+ typedef T type;
+ };
+
+ template <typename T>
+ using unwrapped_t = typename unwrapped<T>::type;
+
+ template <typename T>
+ struct unwrap_unqualified : unwrapped<unqualified_t<T>> { };
+
+ template <typename T>
+ using unwrap_unqualified_t = typename unwrap_unqualified<T>::type;
+
+ template <typename T>
+ struct remove_member_pointer;
+
+ template <typename R, typename T>
+ struct remove_member_pointer<R T::*> {
+ typedef R type;
+ };
+
+ template <typename R, typename T>
+ struct remove_member_pointer<R T::*const> {
+ typedef R type;
+ };
+
+ template <typename T>
+ using remove_member_pointer_t = remove_member_pointer<T>;
+
+ template <typename T, typename...>
+ struct all_same : std::true_type { };
+
+ template <typename T, typename U, typename... Args>
+ struct all_same<T, U, Args...> : std::integral_constant<bool, std::is_same<T, U>::value && all_same<T, Args...>::value> { };
+
+ template <typename T, typename...>
+ struct any_same : std::false_type { };
+
+ template <typename T, typename U, typename... Args>
+ struct any_same<T, U, Args...> : std::integral_constant<bool, std::is_same<T, U>::value || any_same<T, Args...>::value> { };
+
+ template <typename T, typename... Args>
+ constexpr inline bool any_same_v = any_same<T, Args...>::value;
+
+ template <bool B>
+ using boolean = std::integral_constant<bool, B>;
+
+ template <bool B>
+ constexpr inline bool boolean_v = boolean<B>::value;
+
+ template <typename T>
+ using neg = boolean<!T::value>;
+
+ template <typename T>
+ constexpr inline bool neg_v = neg<T>::value;
+
+ template <typename... Args>
+ struct all : boolean<true> { };
+
+ template <typename T, typename... Args>
+ struct all<T, Args...> : std::conditional_t<T::value, all<Args...>, boolean<false>> { };
+
+ template <typename... Args>
+ struct any : boolean<false> { };
+
+ template <typename T, typename... Args>
+ struct any<T, Args...> : std::conditional_t<T::value, boolean<true>, any<Args...>> { };
+
+ template <typename... Args>
+ constexpr inline bool all_v = all<Args...>::value;
+
+ template <typename... Args>
+ constexpr inline bool any_v = any<Args...>::value;
+
+ enum class enable_t { _ };
+
+ constexpr const auto enabler = enable_t::_;
+
+ template <bool value, typename T = void>
+ using disable_if_t = std::enable_if_t<!value, T>;
+
+ template <typename... Args>
+ using enable = std::enable_if_t<all<Args...>::value, enable_t>;
+
+ template <typename... Args>
+ using disable = std::enable_if_t<neg<all<Args...>>::value, enable_t>;
+
+ template <typename... Args>
+ using enable_any = std::enable_if_t<any<Args...>::value, enable_t>;
+
+ template <typename... Args>
+ using disable_any = std::enable_if_t<neg<any<Args...>>::value, enable_t>;
+
+ template <typename V, typename... Vs>
+ struct find_in_pack_v : boolean<false> { };
+
+ template <typename V, typename Vs1, typename... Vs>
+ struct find_in_pack_v<V, Vs1, Vs...> : any<boolean<(V::value == Vs1::value)>, find_in_pack_v<V, Vs...>> { };
+
+ namespace meta_detail {
+ template <std::size_t I, typename T, typename... Args>
+ struct index_in_pack : std::integral_constant<std::size_t, SIZE_MAX> { };
+
+ template <std::size_t I, typename T, typename T1, typename... Args>
+ struct index_in_pack<I, T, T1, Args...>
+ : conditional_t<std::is_same<T, T1>::value, std::integral_constant<std::ptrdiff_t, I>, index_in_pack<I + 1, T, Args...>> { };
+ } // namespace meta_detail
+
+ template <typename T, typename... Args>
+ struct index_in_pack : meta_detail::index_in_pack<0, T, Args...> { };
+
+ template <typename T, typename List>
+ struct index_in : meta_detail::index_in_pack<0, T, List> { };
+
+ template <typename T, typename... Args>
+ struct index_in<T, types<Args...>> : meta_detail::index_in_pack<0, T, Args...> { };
+
+ template <std::size_t I, typename... Args>
+ struct at_in_pack { };
+
+ template <std::size_t I, typename... Args>
+ using at_in_pack_t = typename at_in_pack<I, Args...>::type;
+
+ template <std::size_t I, typename Arg, typename... Args>
+ struct at_in_pack<I, Arg, Args...> : std::conditional<I == 0, Arg, at_in_pack_t<I - 1, Args...>> { };
+
+ template <typename Arg, typename... Args>
+ struct at_in_pack<0, Arg, Args...> {
+ typedef Arg type;
+ };
+
+ namespace meta_detail {
+ template <typename, typename TI>
+ using on_even = meta::boolean<(TI::value % 2) == 0>;
+
+ template <typename, typename TI>
+ using on_odd = meta::boolean<(TI::value % 2) == 1>;
+
+ template <typename, typename>
+ using on_always = std::true_type;
+
+ template <template <typename...> class When, std::size_t Limit, std::size_t I, template <typename...> class Pred, typename... Ts>
+ struct count_when_for_pack : std::integral_constant<std::size_t, 0> { };
+ template <template <typename...> class When, std::size_t Limit, std::size_t I, template <typename...> class Pred, typename T, typename... Ts>
+ struct count_when_for_pack<When, Limit, I, Pred, T, Ts...> : conditional_t < sizeof...(Ts)
+ == 0
+ || Limit<2, std::integral_constant<std::size_t, I + static_cast<std::size_t>(Limit != 0 && Pred<T>::value)>,
+ count_when_for_pack<When, Limit - static_cast<std::size_t>(When<T, std::integral_constant<std::size_t, I>>::value),
+ I + static_cast<std::size_t>(When<T, std::integral_constant<std::size_t, I>>::value&& Pred<T>::value), Pred, Ts...>> { };
+ } // namespace meta_detail
+
+ template <template <typename...> class Pred, typename... Ts>
+ struct count_for_pack : meta_detail::count_when_for_pack<meta_detail::on_always, sizeof...(Ts), 0, Pred, Ts...> { };
+
+ template <template <typename...> class Pred, typename... Ts>
+ inline constexpr std::size_t count_for_pack_v = count_for_pack<Pred, Ts...>::value;
+
+ template <template <typename...> class Pred, typename List>
+ struct count_for;
+
+ template <template <typename...> class Pred, typename... Args>
+ struct count_for<Pred, types<Args...>> : count_for_pack<Pred, Args...> { };
+
+ template <std::size_t Limit, template <typename...> class Pred, typename... Ts>
+ struct count_for_to_pack : meta_detail::count_when_for_pack<meta_detail::on_always, Limit, 0, Pred, Ts...> { };
+
+ template <std::size_t Limit, template <typename...> class Pred, typename... Ts>
+ inline constexpr std::size_t count_for_to_pack_v = count_for_to_pack<Limit, Pred, Ts...>::value;
+
+ template <template <typename...> class When, std::size_t Limit, template <typename...> class Pred, typename... Ts>
+ struct count_when_for_to_pack : meta_detail::count_when_for_pack<When, Limit, 0, Pred, Ts...> { };
+
+ template <template <typename...> class When, std::size_t Limit, template <typename...> class Pred, typename... Ts>
+ inline constexpr std::size_t count_when_for_to_pack_v = count_when_for_to_pack<When, Limit, Pred, Ts...>::value;
+
+ template <template <typename...> class Pred, typename... Ts>
+ using count_even_for_pack = count_when_for_to_pack<meta_detail::on_even, sizeof...(Ts), Pred, Ts...>;
+
+ template <template <typename...> class Pred, typename... Ts>
+ inline constexpr std::size_t count_even_for_pack_v = count_even_for_pack<Pred, Ts...>::value;
+
+ template <template <typename...> class Pred, typename... Ts>
+ using count_odd_for_pack = count_when_for_to_pack<meta_detail::on_odd, sizeof...(Ts), Pred, Ts...>;
+
+ template <template <typename...> class Pred, typename... Ts>
+ inline constexpr std::size_t count_odd_for_pack_v = count_odd_for_pack<Pred, Ts...>::value;
+
+ template <typename... Args>
+ struct return_type {
+ typedef std::tuple<Args...> type;
+ };
+
+ template <typename T>
+ struct return_type<T> {
+ typedef T type;
+ };
+
+ template <>
+ struct return_type<> {
+ typedef void type;
+ };
+
+ template <typename... Args>
+ using return_type_t = typename return_type<Args...>::type;
+
+ namespace meta_detail {
+ template <typename>
+ struct always_true : std::true_type { };
+ struct is_invokable_tester {
+ template <typename Fun, typename... Args>
+ static always_true<decltype(std::declval<Fun>()(std::declval<Args>()...))> test(int);
+ template <typename...>
+ static std::false_type test(...);
+ };
+ } // namespace meta_detail
+
+ template <typename T>
+ struct is_invokable;
+ template <typename Fun, typename... Args>
+ struct is_invokable<Fun(Args...)> : decltype(meta_detail::is_invokable_tester::test<Fun, Args...>(0)) { };
+
+ namespace meta_detail {
+
+ template <typename T, typename = void>
+ struct is_invocable : std::is_function<std::remove_pointer_t<T>> { };
+
+ template <typename T>
+ struct is_invocable<T,
+ std::enable_if_t<std::is_final<unqualified_t<T>>::value && std::is_class<unqualified_t<T>>::value
+ && std::is_same<decltype(void(&T::operator())), void>::value>> { };
+
+ template <typename T>
+ struct is_invocable<T,
+ std::enable_if_t<!std::is_final<unqualified_t<T>>::value && std::is_class<unqualified_t<T>>::value
+ && std::is_destructible<unqualified_t<T>>::value>> {
+ struct F {
+ void operator()() {};
+ };
+ struct Derived : T, F { };
+ template <typename U, U>
+ struct Check;
+
+ template <typename V>
+ static sfinae_no_t test(Check<void (F::*)(), &V::operator()>*);
+
+ template <typename>
+ static sfinae_yes_t test(...);
+
+ static constexpr bool value = std::is_same_v<decltype(test<Derived>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct is_invocable<T,
+ std::enable_if_t<!std::is_final<unqualified_t<T>>::value && std::is_class<unqualified_t<T>>::value
+ && !std::is_destructible<unqualified_t<T>>::value>> {
+ struct F {
+ void operator()() {};
+ };
+ struct Derived : T, F {
+ ~Derived() = delete;
+ };
+ template <typename U, U>
+ struct Check;
+
+ template <typename V>
+ static sfinae_no_t test(Check<void (F::*)(), &V::operator()>*);
+
+ template <typename>
+ static sfinae_yes_t test(...);
+
+ static constexpr bool value = std::is_same_v<decltype(test<Derived>(0)), sfinae_yes_t>;
+ };
+
+ struct has_begin_end_impl {
+ template <typename T, typename U = unqualified_t<T>, typename B = decltype(std::declval<U&>().begin()),
+ typename E = decltype(std::declval<U&>().end())>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_key_type_impl {
+ template <typename T, typename U = unqualified_t<T>, typename V = typename U::key_type>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_key_comp_impl {
+ template <typename T, typename V = decltype(std::declval<unqualified_t<T>>().key_comp())>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_load_factor_impl {
+ template <typename T, typename V = decltype(std::declval<unqualified_t<T>>().load_factor())>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_mapped_type_impl {
+ template <typename T, typename V = typename unqualified_t<T>::mapped_type>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_value_type_impl {
+ template <typename T, typename V = typename unqualified_t<T>::value_type>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_iterator_impl {
+ template <typename T, typename V = typename unqualified_t<T>::iterator>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_key_value_pair_impl {
+ template <typename T, typename U = unqualified_t<T>, typename V = typename U::value_type, typename F = decltype(std::declval<V&>().first),
+ typename S = decltype(std::declval<V&>().second)>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ template <typename T>
+ struct has_push_back_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().push_back(std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_insert_with_iterator_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().insert(
+ std::declval<std::add_rvalue_reference_t<typename C::iterator>>(), std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = !std::is_same_v<decltype(test<T>(0)), sfinae_no_t>;
+ };
+
+ template <typename T>
+ struct has_insert_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().insert(std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = !std::is_same_v<decltype(test<T>(0)), sfinae_no_t>;
+ };
+
+ template <typename T>
+ struct has_insert_after_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().insert_after(std::declval<std::add_rvalue_reference_t<typename C::const_iterator>>(),
+ std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_size_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().size())*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_max_size_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().max_size())*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_to_string_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().to_string())*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T, typename U, typename = void>
+ class supports_op_less_test : public std::false_type { };
+ template <typename T, typename U>
+ class supports_op_less_test<T, U, void_t<decltype(std::declval<T&>() < std::declval<U&>())>>
+ : public std::integral_constant<bool,
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ !is_specialization_of_v<unqualified_t<T>, std::variant> && !is_specialization_of_v<unqualified_t<U>, std::variant>
+#else
+ true
+#endif
+ > {
+ };
+
+ template <typename T, typename U, typename = void>
+ class supports_op_equal_test : public std::false_type { };
+ template <typename T, typename U>
+ class supports_op_equal_test<T, U, void_t<decltype(std::declval<T&>() == std::declval<U&>())>>
+ : public std::integral_constant<bool,
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ !is_specialization_of_v<unqualified_t<T>, std::variant> && !is_specialization_of_v<unqualified_t<U>, std::variant>
+#else
+ true
+#endif
+ > {
+ };
+
+ template <typename T, typename U, typename = void>
+ class supports_op_less_equal_test : public std::false_type { };
+ template <typename T, typename U>
+ class supports_op_less_equal_test<T, U, void_t<decltype(std::declval<T&>() <= std::declval<U&>())>>
+ : public std::integral_constant<bool,
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ !is_specialization_of_v<unqualified_t<T>, std::variant> && !is_specialization_of_v<unqualified_t<U>, std::variant>
+#else
+ true
+#endif
+ > {
+ };
+
+ template <typename T, typename U, typename = void>
+ class supports_op_left_shift_test : public std::false_type { };
+ template <typename T, typename U>
+ class supports_op_left_shift_test<T, U, void_t<decltype(std::declval<T&>() << std::declval<U&>())>> : public std::true_type { };
+
+ template <typename T, typename = void>
+ class supports_adl_to_string_test : public std::false_type { };
+ template <typename T>
+ class supports_adl_to_string_test<T, void_t<decltype(to_string(std::declval<const T&>()))>> : public std::true_type { };
+
+ template <typename T, bool b>
+ struct is_matched_lookup_impl : std::false_type { };
+ template <typename T>
+ struct is_matched_lookup_impl<T, true> : std::is_same<typename T::key_type, typename T::value_type> { };
+
+ template <typename T>
+ using non_void_t = meta::conditional_t<std::is_void_v<T>, ::sol::detail::unchecked_t, T>;
+
+ template <typename T>
+ using detect_sentinel = typename T::sentinel;
+ } // namespace meta_detail
+
+ template <typename T, typename Fallback>
+ class sentinel_or {
+ public:
+ using type = detected_or_t<Fallback, meta_detail::detect_sentinel, T>;
+ };
+
+ template <typename T, typename Fallback>
+ using sentinel_or_t = typename sentinel_or<T, Fallback>::type;
+
+ template <typename T, typename U = T>
+ class supports_op_less : public meta_detail::supports_op_less_test<T, U> { };
+
+ template <typename T, typename U = T>
+ class supports_op_equal : public meta_detail::supports_op_equal_test<T, U> { };
+
+ template <typename T, typename U = T>
+ class supports_op_less_equal : public meta_detail::supports_op_less_equal_test<T, U> { };
+
+ template <typename T, typename U = T>
+ class supports_op_left_shift : public meta_detail::supports_op_left_shift_test<T, U> { };
+
+ template <typename T>
+ class supports_adl_to_string : public meta_detail::supports_adl_to_string_test<T> { };
+
+ template <typename T>
+ class supports_to_string_member : public meta::boolean<meta_detail::has_to_string_test<meta_detail::non_void_t<T>>::value> { };
+
+ template <typename T>
+ using is_invocable = boolean<meta_detail::is_invocable<T>::value>;
+
+ template <typename T>
+ constexpr inline bool is_invocable_v = is_invocable<T>::value;
+
+ template <typename T>
+ struct has_begin_end : decltype(meta_detail::has_begin_end_impl::test<T>(0)) { };
+
+ template <typename T>
+ constexpr inline bool has_begin_end_v = has_begin_end<T>::value;
+
+ template <typename T>
+ struct has_key_value_pair : decltype(meta_detail::has_key_value_pair_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_key_type : decltype(meta_detail::has_key_type_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_key_comp : decltype(meta_detail::has_key_comp_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_load_factor : decltype(meta_detail::has_load_factor_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_mapped_type : decltype(meta_detail::has_mapped_type_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_iterator : decltype(meta_detail::has_iterator_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_value_type : decltype(meta_detail::has_value_type_impl::test<T>(0)) { };
+
+ template <typename T>
+ using has_push_back = meta::boolean<meta_detail::has_push_back_test<T>::value>;
+
+ template <typename T>
+ using has_max_size = meta::boolean<meta_detail::has_max_size_test<T>::value>;
+
+ template <typename T>
+ using has_insert = meta::boolean<meta_detail::has_insert_test<T>::value>;
+
+ template <typename T>
+ using has_insert_with_iterator = meta::boolean<meta_detail::has_insert_with_iterator_test<T>::value>;
+
+ template <typename T>
+ using has_insert_after = meta::boolean<meta_detail::has_insert_after_test<T>::value>;
+
+ template <typename T>
+ using has_size = meta::boolean<meta_detail::has_size_test<T>::value>;
+
+ template <typename T>
+ using is_associative = meta::all<has_key_type<T>, has_key_value_pair<T>, has_mapped_type<T>>;
+
+ template <typename T>
+ using is_lookup = meta::all<has_key_type<T>, has_value_type<T>>;
+
+ template <typename T>
+ using is_ordered = meta::all<has_key_comp<T>, meta::neg<has_load_factor<T>>>;
+
+ template <typename T>
+ using is_matched_lookup = meta_detail::is_matched_lookup_impl<T, is_lookup<T>::value>;
+
+ template <typename T>
+ using is_initializer_list = meta::is_specialization_of<T, std::initializer_list>;
+
+ template <typename T>
+ constexpr inline bool is_initializer_list_v = is_initializer_list<T>::value;
+
+ template <typename T, typename CharT = char>
+ using is_string_literal_array_of = boolean<std::is_array_v<T> && std::is_same_v<std::remove_all_extents_t<T>, CharT>>;
+
+ template <typename T, typename CharT = char>
+ constexpr inline bool is_string_literal_array_of_v = is_string_literal_array_of<T, CharT>::value;
+
+ template <typename T>
+ using is_string_literal_array = boolean<std::is_array_v<T>
+ && any_same_v<std::remove_all_extents_t<T>, char,
+#if SOL_IS_ON(SOL_CHAR8_T)
+ char8_t,
+#endif
+ char16_t, char32_t, wchar_t>>;
+
+ template <typename T>
+ constexpr inline bool is_string_literal_array_v = is_string_literal_array<T>::value;
+
+ template <typename T, typename CharT>
+ struct is_string_of : std::false_type { };
+
+ template <typename CharT, typename CharTargetT, typename TraitsT, typename AllocT>
+ struct is_string_of<std::basic_string<CharT, TraitsT, AllocT>, CharTargetT> : std::is_same<CharT, CharTargetT> { };
+
+ template <typename T, typename CharT>
+ constexpr inline bool is_string_of_v = is_string_of<T, CharT>::value;
+
+ template <typename T, typename CharT>
+ struct is_string_view_of : std::false_type { };
+
+ template <typename CharT, typename CharTargetT, typename TraitsT>
+ struct is_string_view_of<std::basic_string_view<CharT, TraitsT>, CharTargetT> : std::is_same<CharT, CharTargetT> { };
+
+ template <typename T, typename CharT>
+ constexpr inline bool is_string_view_of_v = is_string_view_of<T, CharT>::value;
+
+ template <typename T>
+ using is_string_like
+ = meta::boolean<is_specialization_of_v<T, std::basic_string> || is_specialization_of_v<T, std::basic_string_view> || is_string_literal_array_v<T>>;
+
+ template <typename T>
+ constexpr inline bool is_string_like_v = is_string_like<T>::value;
+
+ template <typename T, typename CharT = char>
+ using is_string_constructible = meta::boolean<is_string_literal_array_of_v<T, CharT> || std::is_same_v<T, const CharT*> || std::is_same_v<T, CharT>
+ || is_string_of_v<T, CharT> || std::is_same_v<T, std::initializer_list<CharT>> || is_string_view_of_v<T, CharT> || std::is_null_pointer_v<T>>;
+
+ template <typename T, typename CharT = char>
+ constexpr inline bool is_string_constructible_v = is_string_constructible<T, CharT>::value;
+
+ template <typename T>
+ using is_string_like_or_constructible = meta::boolean<is_string_like_v<T> || is_string_constructible_v<T>>;
+
+ template <typename T>
+ struct is_pair : std::false_type { };
+
+ template <typename T1, typename T2>
+ struct is_pair<std::pair<T1, T2>> : std::true_type { };
+
+ template <typename T, typename Char>
+ using is_c_str_of = any<std::is_same<T, const Char*>, std::is_same<T, Char const* const>, std::is_same<T, Char*>, is_string_literal_array_of<T, Char>>;
+
+ template <typename T, typename Char>
+ constexpr inline bool is_c_str_of_v = is_c_str_of<T, Char>::value;
+
+ template <typename T>
+ using is_c_str = is_c_str_of<T, char>;
+
+ template <typename T>
+ constexpr inline bool is_c_str_v = is_c_str<T>::value;
+
+ template <typename T, typename Char>
+ using is_c_str_or_string_of = any<is_c_str_of<T, Char>, is_string_of<T, Char>>;
+
+ template <typename T, typename Char>
+ constexpr inline bool is_c_str_or_string_of_v = is_c_str_or_string_of<T, Char>::value;
+
+ template <typename T>
+ using is_c_str_or_string = is_c_str_or_string_of<T, char>;
+
+ template <typename T>
+ constexpr inline bool is_c_str_or_string_v = is_c_str_or_string<T>::value;
+
+ template <typename T>
+ struct is_move_only : all<neg<std::is_reference<T>>, neg<std::is_copy_constructible<unqualified_t<T>>>, std::is_move_constructible<unqualified_t<T>>> { };
+
+ template <typename T>
+ using is_not_move_only = neg<is_move_only<T>>;
+
+ namespace meta_detail {
+ template <typename T>
+ decltype(auto) force_tuple(T&& x) {
+ if constexpr (meta::is_specialization_of_v<meta::unqualified_t<T>, std::tuple>) {
+ return std::forward<T>(x);
+ }
+ else {
+ return std::tuple<T>(std::forward<T>(x));
+ }
+ }
+ } // namespace meta_detail
+
+ template <typename... X>
+ decltype(auto) tuplefy(X&&... x) {
+ return std::tuple_cat(meta_detail::force_tuple(std::forward<X>(x))...);
+ }
+
+ template <typename T, typename = void>
+ struct iterator_tag {
+ using type = std::input_iterator_tag;
+ };
+
+ template <typename T>
+ struct iterator_tag<T, conditional_t<false, typename std::iterator_traits<T>::iterator_category, void>> {
+ using type = typename std::iterator_traits<T>::iterator_category;
+ };
+}} // namespace sol::meta
+
+// end of sol/traits.hpp
+
+namespace sol {
+ namespace detail {
+ const bool default_safe_function_calls =
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_CALLS)
+ true;
+#else
+ false;
+#endif
+ } // namespace detail
+
+ namespace meta { namespace meta_detail {
+ }} // namespace meta::meta_detail
+
+ namespace stack { namespace stack_detail {
+ using undefined_method_func = void (*)(stack_reference);
+
+ template <typename T>
+ void set_undefined_methods_on(stack_reference);
+
+ struct undefined_metatable;
+ }} // namespace stack::stack_detail
+} // namespace sol
+
+#endif // SOL_FORWARD_DETAIL_HPP
+// end of sol/forward_detail.hpp
+
+// beginning of sol/assert.hpp
+
+#if SOL_IS_ON(SOL2_CI)
+
+struct pre_main {
+ pre_main() {
+#ifdef _MSC_VER
+ _set_abort_behavior(0, _WRITE_ABORT_MSG);
+#endif
+ }
+} inline sol2_ci_dont_lock_ci_please = {};
+
+#endif // Prevent lockup when doing Continuous Integration
+
+#if SOL_IS_ON(SOL_USER_ASSERT)
+ #define SOL_ASSERT(...) SOL_C_ASSERT(__VA_ARGS__)
+#else
+ #if SOL_IS_ON(SOL_DEBUG_BUILD)
+ #include <exception>
+ #include <iostream>
+ #include <cstdlib>
+
+ #define SOL_ASSERT(...) \
+ do { \
+ if (!(__VA_ARGS__)) { \
+ std::cerr << "Assertion `" #__VA_ARGS__ "` failed in " << __FILE__ << " line " << __LINE__ << std::endl; \
+ std::terminate(); \
+ } \
+ } while (false)
+ #else
+ #define SOL_ASSERT(...) \
+ do { \
+ if (false) { \
+ (void)(__VA_ARGS__); \
+ } \
+ } while (false)
+ #endif
+#endif
+
+#if SOL_IS_ON(SOL_USER_ASSERT_MSG)
+ #define SOL_ASSERT_MSG(message, ...) SOL_ASSERT_MSG(message, __VA_ARGS__)
+#else
+ #if SOL_IS_ON(SOL_DEBUG_BUILD)
+ #include <exception>
+ #include <iostream>
+ #include <cstdlib>
+
+ #define SOL_ASSERT_MSG(message, ...) \
+ do { \
+ if (!(__VA_ARGS__)) { \
+ std::cerr << "Assertion `" #__VA_ARGS__ "` failed in " << __FILE__ << " line " << __LINE__ << ": " << message << std::endl; \
+ std::terminate(); \
+ } \
+ } while (false)
+ #else
+ #define SOL_ASSERT_MSG(message, ...) \
+ do { \
+ if (false) { \
+ (void)(__VA_ARGS__); \
+ (void)sizeof(message); \
+ } \
+ } while (false)
+ #endif
+#endif
+
+// end of sol/assert.hpp
+
+// beginning of sol/bytecode.hpp
+
+// beginning of sol/compatibility.hpp
+
+// beginning of sol/compatibility/lua_version.hpp
+
+#if SOL_IS_ON(SOL_USING_CXX_LUA)
+ #include <lua.h>
+ #include <lualib.h>
+ #include <lauxlib.h>
+#elif SOL_IS_ON(SOL_USE_LUA_HPP)
+ #include <lua.hpp>
+#else
+ extern "C" {
+ #include <lua.h>
+ #include <lauxlib.h>
+ #include <lualib.h>
+ }
+#endif // C++ Mangling for Lua vs. Not
+
+#if defined(SOL_LUAJIT)
+ #if (SOL_LUAJIT != 0)
+ #define SOL_USE_LUAJIT_I_ SOL_ON
+ #else
+ #define SOL_USE_LUAJIT_I_ SOL_OFF
+ #endif
+#elif defined(LUAJIT_VERSION)
+ #define SOL_USE_LUAJIT_I_ SOL_ON
+#else
+ #define SOL_USE_LUAJIT_I_ SOL_DEFAULT_OFF
+#endif // luajit
+
+#if SOL_IS_ON(SOL_USING_CXX_LUAJIT)
+ #include <luajit.h>
+#elif SOL_IS_ON(SOL_USE_LUAJIT)
+ extern "C" {
+ #include <luajit.h>
+ }
+#endif // C++ LuaJIT ... whatever that means
+
+#if defined(SOL_LUAJIT_VERSION)
+ #define SOL_LUAJIT_VERSION_I_ SOL_LUAJIT_VERSION
+#elif SOL_IS_ON(SOL_USE_LUAJIT)
+ #define SOL_LUAJIT_VERSION_I_ LUAJIT_VERSION_NUM
+#else
+ #define SOL_LUAJIT_VERSION_I_ 0
+#endif
+
+#if defined(SOL_LUAJIT_FFI_DISABLED)
+ #define SOL_LUAJIT_FFI_DISABLED_I_ SOL_ON
+#elif defined(LUAJIT_DISABLE_FFI)
+ #define SOL_LUAJIT_FFI_DISABLED_I_ SOL_ON
+#else
+ #define SOL_LUAJIT_FFI_DISABLED_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(MOONJIT_VERSION)
+ #define SOL_USE_MOONJIT_I_ SOL_ON
+#else
+ #define SOL_USE_MOONJIT_I_ SOL_OFF
+#endif
+
+#if !defined(SOL_LUA_VERSION)
+ #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502
+ #define SOL_LUA_VERSION LUA_VERSION_NUM
+ #elif defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
+ #define SOL_LUA_VERSION LUA_VERSION_NUM
+ #elif !defined(LUA_VERSION_NUM) || !(LUA_VERSION_NUM)
+ // Definitely 5.0
+ #define SOL_LUA_VERSION 500
+ #else
+ // ??? Not sure, assume latest?
+ #define SOL_LUA_VERSION 504
+ #endif // Lua Version 503, 502, 501 || luajit, 500
+#endif // SOL_LUA_VERSION
+
+#if defined(SOL_LUA_VERSION)
+ #define SOL_LUA_VERSION_I_ SOL_LUA_VERSION
+#else
+ #define SOL_LUA_VERSION_I_ 504
+#endif
+
+#if defined(SOL_EXCEPTIONS_ALWAYS_UNSAFE)
+ #if (SOL_EXCEPTIONS_ALWAYS_UNSAFE != 0)
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_ON
+ #endif
+#elif defined(SOL_EXCEPTIONS_SAFE_PROPAGATION)
+ #if (SOL_EXCEPTIONS_SAFE_PROPAGATION != 0)
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_ON
+ #else
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_OFF
+ #endif
+#elif SOL_LUAJIT_VERSION_I_ >= 20100
+ // LuaJIT 2.1.0-beta3 and better have exception support locked in for all platforms (mostly)
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_DEFAULT_ON
+#elif SOL_LUAJIT_VERSION_I_ >= 20000
+ // LuaJIT 2.0.x have exception support only on x64 builds
+ #if SOL_IS_ON(SOL_PLATFORM_X64)
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_OFF
+ #endif
+#else
+ // otherwise, there is no exception safety for
+ // shoving exceptions through Lua and errors should
+ // always be serialized
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_EXCEPTIONS_CATCH_ALL)
+ #if (SOL_EXCEPTIONS_CATCH_ALL != 0)
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_ON
+ #else
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_USE_LUAJIT)
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_DEFAULT_OFF
+ #elif SOL_IS_ON(SOL_USING_CXX_LUAJIT)
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_DEFAULT_OFF
+ #elif SOL_IS_ON(SOL_USING_CXX_LUA)
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_DEFAULT_OFF
+ #else
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_DEFAULT_ON
+ #endif
+#endif
+
+#if defined(SOL_LUAJIT_USE_EXCEPTION_TRAMPOLINE)
+ #if (SOL_LUAJIT_USE_EXCEPTION_TRAMPOLINE != 0)
+ #define SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE_I_ SOL_ON
+ #else
+ #define SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_OFF(SOL_PROPAGATE_EXCEPTIONS) && SOL_IS_ON(SOL_USE_LUAJIT)
+ #define SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE_I_ SOL_ON
+ #else
+ #define SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_LUAL_STREAM_HAS_CLOSE_FUNCTION)
+ #if (SOL_LUAL_STREAM_HAS_CLOSE_FUNCTION != 0)
+ #define SOL_LUAL_STREAM_USE_CLOSE_FUNCTION_I_ SOL_ON
+ #else
+ #define SOL_LUAL_STREAM_USE_CLOSE_FUNCTION_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_OFF(SOL_USE_LUAJIT) && (SOL_LUA_VERSION > 501)
+ #define SOL_LUAL_STREAM_USE_CLOSE_FUNCTION_I_ SOL_ON
+ #else
+ #define SOL_LUAL_STREAM_USE_CLOSE_FUNCTION_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined (SOL_LUA_BIT32_LIB)
+ #if SOL_LUA_BIT32_LIB != 0
+ #define SOL_LUA_BIT32_LIB_I_ SOL_ON
+ #else
+ #define SOL_LUA_BIT32_LIB_I_ SOL_OFF
+ #endif
+#else
+ // Lua 5.2 only (deprecated in 5.3 (503)) (Can be turned on with Compat flags)
+ // Lua 5.2, or other versions of Lua with the compat flag, or Lua that is not 5.2 with the specific define (5.4.1 either removed it entirely or broke it)
+ #if (SOL_LUA_VERSION_I_ == 502)
+ #define SOL_LUA_BIT32_LIB_I_ SOL_ON
+ #elif defined(LUA_COMPAT_BITLIB)
+ #define SOL_LUA_BIT32_LIB_I_ SOL_ON
+ #elif (SOL_LUA_VERSION_I_ < 504 && defined(LUA_COMPAT_5_2))
+ #define SOL_LUA_BIT32_LIB_I_ SOL_ON
+ #else
+ #define SOL_LUA_BIT32_LIB_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined (SOL_LUA_NIL_IN_TABLES)
+ #if SOL_LUA_NIL_IN_TABLES != 0
+ #define SOL_LUA_NIL_IN_TABLES_I_ SOL_ON
+ #else
+ #define SOL_LUA_NIL_IN_TABLES_I_ SOL_OFF
+ #endif
+#else
+ #if defined(LUA_NILINTABLE) && (LUA_NILINTABLE != 0)
+ #define SOL_LUA_NIL_IN_TABLES_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_LUA_NIL_IN_TABLES_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+// end of sol/compatibility/lua_version.hpp
+
+#if SOL_IS_ON(SOL_USE_COMPATIBILITY_LAYER)
+
+#if SOL_IS_ON(SOL_USING_CXX_LUA) || SOL_IS_ON(SOL_USING_CXX_LUAJIT)
+ #ifndef COMPAT53_LUA_CPP
+ #define COMPAT53_LUA_CPP 1
+ #endif // Build Lua Compat layer as C++
+#endif
+ #ifndef COMPAT53_INCLUDE_SOURCE
+ #define COMPAT53_INCLUDE_SOURCE 1
+ #endif // Build Compat Layer Inline
+// beginning of sol/compatibility/compat-5.3.h
+
+#ifndef KEPLER_PROJECT_COMPAT53_H_
+#define KEPLER_PROJECT_COMPAT53_H_
+
+#include <stddef.h>
+#include <limits.h>
+#include <string.h>
+#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
+extern "C" {
+#endif
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
+}
+#endif
+
+#ifndef COMPAT53_PREFIX
+/* we chose this name because many other lua bindings / libs have
+* their own compatibility layer, and that use the compat53 declaration
+* frequently, causing all kinds of linker / compiler issues
+*/
+# define COMPAT53_PREFIX kp_compat53
+#endif // COMPAT53_PREFIX
+
+#ifndef COMPAT53_API
+# if defined(COMPAT53_INCLUDE_SOURCE) && COMPAT53_INCLUDE_SOURCE
+# if defined(__GNUC__) || defined(__clang__)
+# define COMPAT53_API __attribute__((__unused__)) static inline
+# else
+# define COMPAT53_API static inline
+# endif /* Clang/GCC */
+# else /* COMPAT53_INCLUDE_SOURCE */
+/* we are not including source, so everything is extern */
+# define COMPAT53_API extern
+# endif /* COMPAT53_INCLUDE_SOURCE */
+#endif /* COMPAT53_PREFIX */
+
+#define COMPAT53_CONCAT_HELPER(a, b) a##b
+#define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b)
+
+/* declarations for Lua 5.1 */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
+
+/* XXX not implemented:
+* lua_arith (new operators)
+* lua_upvalueid
+* lua_upvaluejoin
+* lua_version
+* lua_yieldk
+*/
+
+#ifndef LUA_OK
+# define LUA_OK 0
+#endif
+#ifndef LUA_OPADD
+# define LUA_OPADD 0
+#endif
+#ifndef LUA_OPSUB
+# define LUA_OPSUB 1
+#endif
+#ifndef LUA_OPMUL
+# define LUA_OPMUL 2
+#endif
+#ifndef LUA_OPDIV
+# define LUA_OPDIV 3
+#endif
+#ifndef LUA_OPMOD
+# define LUA_OPMOD 4
+#endif
+#ifndef LUA_OPPOW
+# define LUA_OPPOW 5
+#endif
+#ifndef LUA_OPUNM
+# define LUA_OPUNM 6
+#endif
+#ifndef LUA_OPEQ
+# define LUA_OPEQ 0
+#endif
+#ifndef LUA_OPLT
+# define LUA_OPLT 1
+#endif
+#ifndef LUA_OPLE
+# define LUA_OPLE 2
+#endif
+
+/* LuaJIT/Lua 5.1 does not have the updated
+* error codes for thread status/function returns (but some patched versions do)
+* define it only if it's not found
+*/
+#if !defined(LUA_ERRGCMM)
+/* Use + 2 because in some versions of Lua (Lua 5.1)
+* LUA_ERRFILE is defined as (LUA_ERRERR+1)
+* so we need to avoid it (LuaJIT might have something at this
+* integer value too)
+*/
+# define LUA_ERRGCMM (LUA_ERRERR + 2)
+#endif /* LUA_ERRGCMM define */
+
+#if !defined(MOONJIT_VERSION)
+typedef size_t lua_Unsigned;
+#endif
+
+typedef struct luaL_Buffer_53 {
+ luaL_Buffer b; /* make incorrect code crash! */
+ char *ptr;
+ size_t nelems;
+ size_t capacity;
+ lua_State *L2;
+} luaL_Buffer_53;
+#define luaL_Buffer luaL_Buffer_53
+
+/* In PUC-Rio 5.1, userdata is a simple FILE*
+* In LuaJIT, it's a struct where the first member is a FILE*
+* We can't support the `closef` member
+*/
+typedef struct luaL_Stream {
+ FILE *f;
+} luaL_Stream;
+
+#define lua_absindex COMPAT53_CONCAT(COMPAT53_PREFIX, _absindex)
+COMPAT53_API int lua_absindex(lua_State *L, int i);
+
+#define lua_arith COMPAT53_CONCAT(COMPAT53_PREFIX, _arith)
+COMPAT53_API void lua_arith(lua_State *L, int op);
+
+#define lua_compare COMPAT53_CONCAT(COMPAT53_PREFIX, _compare)
+COMPAT53_API int lua_compare(lua_State *L, int idx1, int idx2, int op);
+
+#define lua_copy COMPAT53_CONCAT(COMPAT53_PREFIX, _copy)
+COMPAT53_API void lua_copy(lua_State *L, int from, int to);
+
+#define lua_getuservalue(L, i) \
+ (lua_getfenv((L), (i)), lua_type((L), -1))
+#define lua_setuservalue(L, i) \
+ (luaL_checktype((L), -1, LUA_TTABLE), lua_setfenv((L), (i)))
+
+#define lua_len COMPAT53_CONCAT(COMPAT53_PREFIX, _len)
+COMPAT53_API void lua_len(lua_State *L, int i);
+
+#define lua_pushstring(L, s) \
+ (lua_pushstring((L), (s)), lua_tostring((L), -1))
+
+#define lua_pushlstring(L, s, len) \
+ ((((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len))), lua_tostring((L), -1))
+
+#ifndef luaL_newlibtable
+# define luaL_newlibtable(L, l) \
+ (lua_createtable((L), 0, sizeof((l))/sizeof(*(l))-1))
+#endif
+#ifndef luaL_newlib
+# define luaL_newlib(L, l) \
+ (luaL_newlibtable((L), (l)), luaL_register((L), NULL, (l)))
+#endif
+
+#ifndef lua_pushglobaltable
+# define lua_pushglobaltable(L) \
+ lua_pushvalue((L), LUA_GLOBALSINDEX)
+#endif
+#define lua_rawgetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawgetp)
+COMPAT53_API int lua_rawgetp(lua_State *L, int i, const void *p);
+
+#define lua_rawsetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawsetp)
+COMPAT53_API void lua_rawsetp(lua_State *L, int i, const void *p);
+
+#define lua_rawlen(L, i) lua_objlen((L), (i))
+
+#define lua_tointeger(L, i) lua_tointegerx((L), (i), NULL)
+
+#define lua_tonumberx COMPAT53_CONCAT(COMPAT53_PREFIX, _tonumberx)
+COMPAT53_API lua_Number lua_tonumberx(lua_State *L, int i, int *isnum);
+
+#define luaL_checkversion COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkversion)
+COMPAT53_API void luaL_checkversion(lua_State *L);
+
+#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53)
+COMPAT53_API int lua_load(lua_State *L, lua_Reader reader, void *data, const char* source, const char* mode);
+
+#define luaL_loadfilex COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadfilex)
+COMPAT53_API int luaL_loadfilex(lua_State *L, const char *filename, const char *mode);
+
+#define luaL_loadbufferx COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadbufferx)
+COMPAT53_API int luaL_loadbufferx(lua_State *L, const char *buff, size_t sz, const char *name, const char *mode);
+
+#define luaL_checkstack COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkstack_53)
+COMPAT53_API void luaL_checkstack(lua_State *L, int sp, const char *msg);
+
+#define luaL_getsubtable COMPAT53_CONCAT(COMPAT53_PREFIX, L_getsubtable)
+COMPAT53_API int luaL_getsubtable(lua_State* L, int i, const char *name);
+
+#define luaL_len COMPAT53_CONCAT(COMPAT53_PREFIX, L_len)
+COMPAT53_API lua_Integer luaL_len(lua_State *L, int i);
+
+#define luaL_setfuncs COMPAT53_CONCAT(COMPAT53_PREFIX, L_setfuncs)
+COMPAT53_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup);
+
+#define luaL_setmetatable COMPAT53_CONCAT(COMPAT53_PREFIX, L_setmetatable)
+COMPAT53_API void luaL_setmetatable(lua_State *L, const char *tname);
+
+#define luaL_testudata COMPAT53_CONCAT(COMPAT53_PREFIX, L_testudata)
+COMPAT53_API void *luaL_testudata(lua_State *L, int i, const char *tname);
+
+#define luaL_traceback COMPAT53_CONCAT(COMPAT53_PREFIX, L_traceback)
+COMPAT53_API void luaL_traceback(lua_State *L, lua_State *L1, const char *msg, int level);
+
+#define luaL_fileresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_fileresult)
+COMPAT53_API int luaL_fileresult(lua_State *L, int stat, const char *fname);
+
+#define luaL_execresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_execresult)
+COMPAT53_API int luaL_execresult(lua_State *L, int stat);
+
+#define lua_callk(L, na, nr, ctx, cont) \
+ ((void)(ctx), (void)(cont), lua_call((L), (na), (nr)))
+#define lua_pcallk(L, na, nr, err, ctx, cont) \
+ ((void)(ctx), (void)(cont), lua_pcall((L), (na), (nr), (err)))
+
+#define lua_resume(L, from, nargs) \
+ ((void)(from), lua_resume((L), (nargs)))
+
+#define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53)
+COMPAT53_API void luaL_buffinit(lua_State *L, luaL_Buffer_53 *B);
+
+#define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53)
+COMPAT53_API char *luaL_prepbuffsize(luaL_Buffer_53 *B, size_t s);
+
+#define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53)
+COMPAT53_API void luaL_addlstring(luaL_Buffer_53 *B, const char *s, size_t l);
+
+#define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53)
+COMPAT53_API void luaL_addvalue(luaL_Buffer_53 *B);
+
+#define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53)
+COMPAT53_API void luaL_pushresult(luaL_Buffer_53 *B);
+
+#undef luaL_buffinitsize
+#define luaL_buffinitsize(L, B, s) \
+ (luaL_buffinit((L), (B)), luaL_prepbuffsize((B), (s)))
+
+#undef luaL_prepbuffer
+#define luaL_prepbuffer(B) \
+ luaL_prepbuffsize((B), LUAL_BUFFERSIZE)
+
+#undef luaL_addchar
+#define luaL_addchar(B, c) \
+ ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize((B), 1)), \
+ ((B)->ptr[(B)->nelems++] = (c)))
+
+#undef luaL_addsize
+#define luaL_addsize(B, s) \
+ ((B)->nelems += (s))
+
+#undef luaL_addstring
+#define luaL_addstring(B, s) \
+ luaL_addlstring((B), (s), strlen((s)))
+
+#undef luaL_pushresultsize
+#define luaL_pushresultsize(B, s) \
+ (luaL_addsize((B), (s)), luaL_pushresult((B)))
+
+#if defined(LUA_COMPAT_APIINTCASTS)
+#define lua_pushunsigned(L, n) \
+ lua_pushinteger((L), (lua_Integer)(n))
+#define lua_tounsignedx(L, i, is) \
+ ((lua_Unsigned)lua_tointegerx((L), (i), (is)))
+#define lua_tounsigned(L, i) \
+ lua_tounsignedx((L), (i), NULL)
+#define luaL_checkunsigned(L, a) \
+ ((lua_Unsigned)luaL_checkinteger((L), (a)))
+#define luaL_optunsigned(L, a, d) \
+ ((lua_Unsigned)luaL_optinteger((L), (a), (lua_Integer)(d)))
+#endif
+
+#endif /* Lua 5.1 only */
+
+/* declarations for Lua 5.1 and 5.2 */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502
+
+typedef int lua_KContext;
+
+typedef int(*lua_KFunction)(lua_State *L, int status, lua_KContext ctx);
+
+#define lua_dump(L, w, d, s) \
+ ((void)(s), lua_dump((L), (w), (d)))
+
+#define lua_getfield(L, i, k) \
+ (lua_getfield((L), (i), (k)), lua_type((L), -1))
+
+#define lua_gettable(L, i) \
+ (lua_gettable((L), (i)), lua_type((L), -1))
+
+#define lua_geti COMPAT53_CONCAT(COMPAT53_PREFIX, _geti)
+COMPAT53_API int lua_geti(lua_State *L, int index, lua_Integer i);
+
+#define lua_isinteger COMPAT53_CONCAT(COMPAT53_PREFIX, _isinteger)
+COMPAT53_API int lua_isinteger(lua_State *L, int index);
+
+#define lua_tointegerx COMPAT53_CONCAT(COMPAT53_PREFIX, _tointegerx_53)
+COMPAT53_API lua_Integer lua_tointegerx(lua_State *L, int i, int *isnum);
+
+#define lua_numbertointeger(n, p) \
+ ((*(p) = (lua_Integer)(n)), 1)
+
+#define lua_rawget(L, i) \
+ (lua_rawget((L), (i)), lua_type((L), -1))
+
+#define lua_rawgeti(L, i, n) \
+ (lua_rawgeti((L), (i), (n)), lua_type((L), -1))
+
+#define lua_rotate COMPAT53_CONCAT(COMPAT53_PREFIX, _rotate)
+COMPAT53_API void lua_rotate(lua_State *L, int idx, int n);
+
+#define lua_seti COMPAT53_CONCAT(COMPAT53_PREFIX, _seti)
+COMPAT53_API void lua_seti(lua_State *L, int index, lua_Integer i);
+
+#define lua_stringtonumber COMPAT53_CONCAT(COMPAT53_PREFIX, _stringtonumber)
+COMPAT53_API size_t lua_stringtonumber(lua_State *L, const char *s);
+
+#define luaL_tolstring COMPAT53_CONCAT(COMPAT53_PREFIX, L_tolstring)
+COMPAT53_API const char *luaL_tolstring(lua_State *L, int idx, size_t *len);
+
+#define luaL_getmetafield(L, o, e) \
+ (luaL_getmetafield((L), (o), (e)) ? lua_type((L), -1) : LUA_TNIL)
+
+#define luaL_newmetatable(L, tn) \
+ (luaL_newmetatable((L), (tn)) ? (lua_pushstring((L), (tn)), lua_setfield((L), -2, "__name"), 1) : 0)
+
+#define luaL_requiref COMPAT53_CONCAT(COMPAT53_PREFIX, L_requiref_53)
+COMPAT53_API void luaL_requiref(lua_State *L, const char *modname,
+ lua_CFunction openf, int glb);
+
+#endif /* Lua 5.1 and Lua 5.2 */
+
+/* declarations for Lua 5.2 */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 502
+
+/* XXX not implemented:
+* lua_isyieldable
+* lua_getextraspace
+* lua_arith (new operators)
+* lua_pushfstring (new formats)
+*/
+
+#define lua_getglobal(L, n) \
+ (lua_getglobal((L), (n)), lua_type((L), -1))
+
+#define lua_getuservalue(L, i) \
+ (lua_getuservalue((L), (i)), lua_type((L), -1))
+
+#define lua_pushlstring(L, s, len) \
+ (((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len)))
+
+#define lua_rawgetp(L, i, p) \
+ (lua_rawgetp((L), (i), (p)), lua_type((L), -1))
+
+#define LUA_KFUNCTION(_name) \
+ static int (_name)(lua_State *L, int status, lua_KContext ctx); \
+ static int (_name ## _52)(lua_State *L) { \
+ lua_KContext ctx; \
+ int status = lua_getctx(L, &ctx); \
+ return (_name)(L, status, ctx); \
+ } \
+ static int (_name)(lua_State *L, int status, lua_KContext ctx)
+
+#define lua_pcallk(L, na, nr, err, ctx, cont) \
+ lua_pcallk((L), (na), (nr), (err), (ctx), cont ## _52)
+
+#define lua_callk(L, na, nr, ctx, cont) \
+ lua_callk((L), (na), (nr), (ctx), cont ## _52)
+
+#define lua_yieldk(L, nr, ctx, cont) \
+ lua_yieldk((L), (nr), (ctx), cont ## _52)
+
+#ifdef lua_call
+# undef lua_call
+# define lua_call(L, na, nr) \
+ (lua_callk)((L), (na), (nr), 0, NULL)
+#endif
+
+#ifdef lua_pcall
+# undef lua_pcall
+# define lua_pcall(L, na, nr, err) \
+ (lua_pcallk)((L), (na), (nr), (err), 0, NULL)
+#endif
+
+#ifdef lua_yield
+# undef lua_yield
+# define lua_yield(L, nr) \
+ (lua_yieldk)((L), (nr), 0, NULL)
+#endif
+
+#endif /* Lua 5.2 only */
+
+/* other Lua versions */
+#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 || LUA_VERSION_NUM > 504
+
+# error "unsupported Lua version (i.e. not Lua 5.1, 5.2, 5.3, or 5.4)"
+
+#endif /* other Lua versions except 5.1, 5.2, 5.3, and 5.4 */
+
+/* helper macro for defining continuation functions (for every version
+* *except* Lua 5.2) */
+#ifndef LUA_KFUNCTION
+#define LUA_KFUNCTION(_name) \
+ static int (_name)(lua_State *L, int status, lua_KContext ctx)
+#endif
+
+#if defined(COMPAT53_INCLUDE_SOURCE) && COMPAT53_INCLUDE_SOURCE == 1
+// beginning of sol/compatibility/compat-5.3.c.h
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+
+/* don't compile it again if it already is included via compat53.h */
+#ifndef KEPLER_PROJECT_COMPAT53_C_
+#define KEPLER_PROJECT_COMPAT53_C_
+
+/* definitions for Lua 5.1 only */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
+
+#ifndef COMPAT53_FOPEN_NO_LOCK
+#if defined(_MSC_VER)
+#define COMPAT53_FOPEN_NO_LOCK 1
+#else /* otherwise */
+#define COMPAT53_FOPEN_NO_LOCK 0
+#endif /* VC++ only so far */
+#endif /* No-lock fopen_s usage if possible */
+
+#if defined(_MSC_VER) && COMPAT53_FOPEN_NO_LOCK
+#include <share.h>
+#endif /* VC++ _fsopen for share-allowed file read */
+
+#ifndef COMPAT53_HAVE_STRERROR_R
+#if defined(__GLIBC__) || defined(_POSIX_VERSION) || defined(__APPLE__) || (!defined(__MINGW32__) && defined(__GNUC__) && (__GNUC__ < 6))
+#define COMPAT53_HAVE_STRERROR_R 1
+#else /* none of the defines matched: define to 0 */
+#define COMPAT53_HAVE_STRERROR_R 0
+#endif /* have strerror_r of some form */
+#endif /* strerror_r */
+
+#ifndef COMPAT53_HAVE_STRERROR_S
+#if defined(_MSC_VER) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || (defined(__STDC_LIB_EXT1__) && __STDC_LIB_EXT1__)
+#define COMPAT53_HAVE_STRERROR_S 1
+#else /* not VC++ or C11 */
+#define COMPAT53_HAVE_STRERROR_S 0
+#endif /* strerror_s from VC++ or C11 */
+#endif /* strerror_s */
+
+#ifndef COMPAT53_LUA_FILE_BUFFER_SIZE
+#define COMPAT53_LUA_FILE_BUFFER_SIZE 4096
+#endif /* Lua File Buffer Size */
+
+static char* compat53_strerror(int en, char* buff, size_t sz) {
+#if COMPAT53_HAVE_STRERROR_R
+ /* use strerror_r here, because it's available on these specific platforms */
+ if (sz > 0) {
+ buff[0] = '\0';
+ /* we don't care whether the GNU version or the XSI version is used: */
+ if (strerror_r(en, buff, sz)) {
+ /* Yes, we really DO want to ignore the return value!
+ * GCC makes that extra hard, not even a (void) cast will do. */
+ }
+ if (buff[0] == '\0') {
+ /* Buffer is unchanged, so we probably have called GNU strerror_r which
+ * returned a static constant string. Chances are that strerror will
+ * return the same static constant string and therefore be thread-safe. */
+ return strerror(en);
+ }
+ }
+ return buff; /* sz is 0 *or* strerror_r wrote into the buffer */
+#elif COMPAT53_HAVE_STRERROR_S
+ /* for MSVC and other C11 implementations, use strerror_s since it's
+ * provided by default by the libraries */
+ strerror_s(buff, sz, en);
+ return buff;
+#else
+ /* fallback, but strerror is not guaranteed to be threadsafe due to modifying
+ * errno itself and some impls not locking a static buffer for it ... but most
+ * known systems have threadsafe errno: this might only change if the locale
+ * is changed out from under someone while this function is being called */
+ (void)buff;
+ (void)sz;
+ return strerror(en);
+#endif
+}
+
+COMPAT53_API int lua_absindex(lua_State* L, int i) {
+ if (i < 0 && i > LUA_REGISTRYINDEX)
+ i += lua_gettop(L) + 1;
+ return i;
+}
+
+static void compat53_call_lua(lua_State* L, char const code[], size_t len, int nargs, int nret) {
+ lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code);
+ if (lua_type(L, -1) != LUA_TFUNCTION) {
+ lua_pop(L, 1);
+ if (luaL_loadbuffer(L, code, len, "=none"))
+ lua_error(L);
+ lua_pushvalue(L, -1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code);
+ }
+ lua_insert(L, -nargs - 1);
+ lua_call(L, nargs, nret);
+}
+
+COMPAT53_API void lua_arith(lua_State* L, int op) {
+ static const char compat53_arith_code[]
+ = "local op,a,b=...\n"
+ "if op==0 then return a+b\n"
+ "elseif op==1 then return a-b\n"
+ "elseif op==2 then return a*b\n"
+ "elseif op==3 then return a/b\n"
+ "elseif op==4 then return a%b\n"
+ "elseif op==5 then return a^b\n"
+ "elseif op==6 then return -a\n"
+ "end\n";
+
+ if (op < LUA_OPADD || op > LUA_OPUNM)
+ luaL_error(L, "invalid 'op' argument for lua_arith");
+ luaL_checkstack(L, 5, "not enough stack slots");
+ if (op == LUA_OPUNM)
+ lua_pushvalue(L, -1);
+ lua_pushnumber(L, op);
+ lua_insert(L, -3);
+ compat53_call_lua(L, compat53_arith_code, sizeof(compat53_arith_code) - 1, 3, 1);
+}
+
+COMPAT53_API int lua_compare(lua_State* L, int idx1, int idx2, int op) {
+ static const char compat53_compare_code[]
+ = "local a,b=...\n"
+ "return a<=b\n";
+
+ int result = 0;
+ switch (op) {
+ case LUA_OPEQ:
+ return lua_equal(L, idx1, idx2);
+ case LUA_OPLT:
+ return lua_lessthan(L, idx1, idx2);
+ case LUA_OPLE:
+ luaL_checkstack(L, 5, "not enough stack slots");
+ idx1 = lua_absindex(L, idx1);
+ idx2 = lua_absindex(L, idx2);
+ lua_pushvalue(L, idx1);
+ lua_pushvalue(L, idx2);
+ compat53_call_lua(L, compat53_compare_code, sizeof(compat53_compare_code) - 1, 2, 1);
+ result = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ return result;
+ default:
+ luaL_error(L, "invalid 'op' argument for lua_compare");
+ }
+ return 0;
+}
+
+COMPAT53_API void lua_copy(lua_State* L, int from, int to) {
+ int abs_to = lua_absindex(L, to);
+ luaL_checkstack(L, 1, "not enough stack slots");
+ lua_pushvalue(L, from);
+ lua_replace(L, abs_to);
+}
+
+COMPAT53_API void lua_len(lua_State* L, int i) {
+ switch (lua_type(L, i)) {
+ case LUA_TSTRING:
+ lua_pushnumber(L, (lua_Number)lua_objlen(L, i));
+ break;
+ case LUA_TTABLE:
+ if (!luaL_callmeta(L, i, "__len"))
+ lua_pushnumber(L, (lua_Number)lua_objlen(L, i));
+ break;
+ case LUA_TUSERDATA:
+ if (luaL_callmeta(L, i, "__len"))
+ break;
+ /* FALLTHROUGH */
+ default:
+ luaL_error(L, "attempt to get length of a %s value", lua_typename(L, lua_type(L, i)));
+ }
+}
+
+COMPAT53_API int lua_rawgetp(lua_State* L, int i, const void* p) {
+ int abs_i = lua_absindex(L, i);
+ lua_pushlightuserdata(L, (void*)p);
+ lua_rawget(L, abs_i);
+ return lua_type(L, -1);
+}
+
+COMPAT53_API void lua_rawsetp(lua_State* L, int i, const void* p) {
+ int abs_i = lua_absindex(L, i);
+ luaL_checkstack(L, 1, "not enough stack slots");
+ lua_pushlightuserdata(L, (void*)p);
+ lua_insert(L, -2);
+ lua_rawset(L, abs_i);
+}
+
+COMPAT53_API lua_Number lua_tonumberx(lua_State* L, int i, int* isnum) {
+ lua_Number n = lua_tonumber(L, i);
+ if (isnum != NULL) {
+ *isnum = (n != 0 || lua_isnumber(L, i));
+ }
+ return n;
+}
+
+COMPAT53_API void luaL_checkversion(lua_State* L) {
+ (void)L;
+}
+
+COMPAT53_API void luaL_checkstack(lua_State* L, int sp, const char* msg) {
+ if (!lua_checkstack(L, sp + LUA_MINSTACK)) {
+ if (msg != NULL)
+ luaL_error(L, "stack overflow (%s)", msg);
+ else {
+ lua_pushliteral(L, "stack overflow");
+ lua_error(L);
+ }
+ }
+}
+
+COMPAT53_API int luaL_getsubtable(lua_State* L, int i, const char* name) {
+ int abs_i = lua_absindex(L, i);
+ luaL_checkstack(L, 3, "not enough stack slots");
+ lua_pushstring(L, name);
+ lua_gettable(L, abs_i);
+ if (lua_istable(L, -1))
+ return 1;
+ lua_pop(L, 1);
+ lua_newtable(L);
+ lua_pushstring(L, name);
+ lua_pushvalue(L, -2);
+ lua_settable(L, abs_i);
+ return 0;
+}
+
+COMPAT53_API lua_Integer luaL_len(lua_State* L, int i) {
+ lua_Integer res = 0;
+ int isnum = 0;
+ luaL_checkstack(L, 1, "not enough stack slots");
+ lua_len(L, i);
+ res = lua_tointegerx(L, -1, &isnum);
+ lua_pop(L, 1);
+ if (!isnum)
+ luaL_error(L, "object length is not an integer");
+ return res;
+}
+
+COMPAT53_API void luaL_setfuncs(lua_State* L, const luaL_Reg* l, int nup) {
+ luaL_checkstack(L, nup + 1, "too many upvalues");
+ for (; l->name != NULL; l++) { /* fill the table with given functions */
+ int i;
+ lua_pushstring(L, l->name);
+ for (i = 0; i < nup; i++) /* copy upvalues to the top */
+ lua_pushvalue(L, -(nup + 1));
+ lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
+ lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */
+ }
+ lua_pop(L, nup); /* remove upvalues */
+}
+
+COMPAT53_API void luaL_setmetatable(lua_State* L, const char* tname) {
+ luaL_checkstack(L, 1, "not enough stack slots");
+ luaL_getmetatable(L, tname);
+ lua_setmetatable(L, -2);
+}
+
+COMPAT53_API void* luaL_testudata(lua_State* L, int i, const char* tname) {
+ void* p = lua_touserdata(L, i);
+ luaL_checkstack(L, 2, "not enough stack slots");
+ if (p == NULL || !lua_getmetatable(L, i))
+ return NULL;
+ else {
+ int res = 0;
+ luaL_getmetatable(L, tname);
+ res = lua_rawequal(L, -1, -2);
+ lua_pop(L, 2);
+ if (!res)
+ p = NULL;
+ }
+ return p;
+}
+
+static int compat53_countlevels(lua_State* L) {
+ lua_Debug ar;
+ int li = 1, le = 1;
+ /* find an upper bound */
+ while (lua_getstack(L, le, &ar)) {
+ li = le;
+ le *= 2;
+ }
+ /* do a binary search */
+ while (li < le) {
+ int m = (li + le) / 2;
+ if (lua_getstack(L, m, &ar))
+ li = m + 1;
+ else
+ le = m;
+ }
+ return le - 1;
+}
+
+static int compat53_findfield(lua_State* L, int objidx, int level) {
+ if (level == 0 || !lua_istable(L, -1))
+ return 0; /* not found */
+ lua_pushnil(L); /* start 'next' loop */
+ while (lua_next(L, -2)) { /* for each pair in table */
+ if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */
+ if (lua_rawequal(L, objidx, -1)) { /* found object? */
+ lua_pop(L, 1); /* remove value (but keep name) */
+ return 1;
+ }
+ else if (compat53_findfield(L, objidx, level - 1)) { /* try recursively */
+ lua_remove(L, -2); /* remove table (but keep name) */
+ lua_pushliteral(L, ".");
+ lua_insert(L, -2); /* place '.' between the two names */
+ lua_concat(L, 3);
+ return 1;
+ }
+ }
+ lua_pop(L, 1); /* remove value */
+ }
+ return 0; /* not found */
+}
+
+static int compat53_pushglobalfuncname(lua_State* L, lua_Debug* ar) {
+ int top = lua_gettop(L);
+ lua_getinfo(L, "f", ar); /* push function */
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ if (compat53_findfield(L, top + 1, 2)) {
+ lua_copy(L, -1, top + 1); /* move name to proper place */
+ lua_pop(L, 2); /* remove pushed values */
+ return 1;
+ }
+ else {
+ lua_settop(L, top); /* remove function and global table */
+ return 0;
+ }
+}
+
+static void compat53_pushfuncname(lua_State* L, lua_Debug* ar) {
+ if (*ar->namewhat != '\0') /* is there a name? */
+ lua_pushfstring(L, "function " LUA_QS, ar->name);
+ else if (*ar->what == 'm') /* main? */
+ lua_pushliteral(L, "main chunk");
+ else if (*ar->what == 'C') {
+ if (compat53_pushglobalfuncname(L, ar)) {
+ lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1));
+ lua_remove(L, -2); /* remove name */
+ }
+ else
+ lua_pushliteral(L, "?");
+ }
+ else
+ lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
+}
+
+#define COMPAT53_LEVELS1 12 /* size of the first part of the stack */
+#define COMPAT53_LEVELS2 10 /* size of the second part of the stack */
+
+COMPAT53_API void luaL_traceback(lua_State* L, lua_State* L1, const char* msg, int level) {
+ lua_Debug ar;
+ int top = lua_gettop(L);
+ int numlevels = compat53_countlevels(L1);
+ int mark = (numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2) ? COMPAT53_LEVELS1 : 0;
+ if (msg)
+ lua_pushfstring(L, "%s\n", msg);
+ lua_pushliteral(L, "stack traceback:");
+ while (lua_getstack(L1, level++, &ar)) {
+ if (level == mark) { /* too many levels? */
+ lua_pushliteral(L, "\n\t..."); /* add a '...' */
+ level = numlevels - COMPAT53_LEVELS2; /* and skip to last ones */
+ }
+ else {
+ lua_getinfo(L1, "Slnt", &ar);
+ lua_pushfstring(L, "\n\t%s:", ar.short_src);
+ if (ar.currentline > 0)
+ lua_pushfstring(L, "%d:", ar.currentline);
+ lua_pushliteral(L, " in ");
+ compat53_pushfuncname(L, &ar);
+ lua_concat(L, lua_gettop(L) - top);
+ }
+ }
+ lua_concat(L, lua_gettop(L) - top);
+}
+
+COMPAT53_API int luaL_fileresult(lua_State* L, int stat, const char* fname) {
+ const char* serr = NULL;
+ int en = errno; /* calls to Lua API may change this value */
+ char buf[512] = { 0 };
+ if (stat) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ lua_pushnil(L);
+ serr = compat53_strerror(en, buf, sizeof(buf));
+ if (fname)
+ lua_pushfstring(L, "%s: %s", fname, serr);
+ else
+ lua_pushstring(L, serr);
+ lua_pushnumber(L, (lua_Number)en);
+ return 3;
+ }
+}
+
+static int compat53_checkmode(lua_State* L, const char* mode, const char* modename, int err) {
+ if (mode && strchr(mode, modename[0]) == NULL) {
+ lua_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", modename, mode);
+ return err;
+ }
+ return LUA_OK;
+}
+
+typedef struct {
+ lua_Reader reader;
+ void* ud;
+ int has_peeked_data;
+ const char* peeked_data;
+ size_t peeked_data_size;
+} compat53_reader_data;
+
+static const char* compat53_reader(lua_State* L, void* ud, size_t* size) {
+ compat53_reader_data* data = (compat53_reader_data*)ud;
+ if (data->has_peeked_data) {
+ data->has_peeked_data = 0;
+ *size = data->peeked_data_size;
+ return data->peeked_data;
+ }
+ else
+ return data->reader(L, data->ud, size);
+}
+
+COMPAT53_API int lua_load(lua_State* L, lua_Reader reader, void* data, const char* source, const char* mode) {
+ int status = LUA_OK;
+ compat53_reader_data compat53_data = { reader, data, 1, 0, 0 };
+ compat53_data.peeked_data = reader(L, data, &(compat53_data.peeked_data_size));
+ if (compat53_data.peeked_data && compat53_data.peeked_data_size && compat53_data.peeked_data[0] == LUA_SIGNATURE[0]) /* binary file? */
+ status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX);
+ else
+ status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX);
+ if (status != LUA_OK)
+ return status;
+ /* we need to call the original 5.1 version of lua_load! */
+#undef lua_load
+ return lua_load(L, compat53_reader, &compat53_data, source);
+#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53)
+}
+
+typedef struct {
+ int n; /* number of pre-read characters */
+ FILE* f; /* file being read */
+ char buff[COMPAT53_LUA_FILE_BUFFER_SIZE]; /* area for reading file */
+} compat53_LoadF;
+
+static const char* compat53_getF(lua_State* L, void* ud, size_t* size) {
+ compat53_LoadF* lf = (compat53_LoadF*)ud;
+ (void)L; /* not used */
+ if (lf->n > 0) { /* are there pre-read characters to be read? */
+ *size = lf->n; /* return them (chars already in buffer) */
+ lf->n = 0; /* no more pre-read characters */
+ }
+ else { /* read a block from file */
+ /* 'fread' can return > 0 *and* set the EOF flag. If next call to
+ 'compat53_getF' called 'fread', it might still wait for user input.
+ The next check avoids this problem. */
+ if (feof(lf->f))
+ return NULL;
+ *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */
+ }
+ return lf->buff;
+}
+
+static int compat53_errfile(lua_State* L, const char* what, int fnameindex) {
+ char buf[512] = { 0 };
+ const char* serr = compat53_strerror(errno, buf, sizeof(buf));
+ const char* filename = lua_tostring(L, fnameindex) + 1;
+ lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
+ lua_remove(L, fnameindex);
+ return LUA_ERRFILE;
+}
+
+static int compat53_skipBOM(compat53_LoadF* lf) {
+ const char* p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */
+ int c;
+ lf->n = 0;
+ do {
+ c = getc(lf->f);
+ if (c == EOF || c != *(const unsigned char*)p++)
+ return c;
+ lf->buff[lf->n++] = (char)c; /* to be read by the parser */
+ } while (*p != '\0');
+ lf->n = 0; /* prefix matched; discard it */
+ return getc(lf->f); /* return next character */
+}
+
+/*
+** reads the first character of file 'f' and skips an optional BOM mark
+** in its beginning plus its first line if it starts with '#'. Returns
+** true if it skipped the first line. In any case, '*cp' has the
+** first "valid" character of the file (after the optional BOM and
+** a first-line comment).
+*/
+static int compat53_skipcomment(compat53_LoadF* lf, int* cp) {
+ int c = *cp = compat53_skipBOM(lf);
+ if (c == '#') { /* first line is a comment (Unix exec. file)? */
+ do { /* skip first line */
+ c = getc(lf->f);
+ } while (c != EOF && c != '\n');
+ *cp = getc(lf->f); /* skip end-of-line, if present */
+ return 1; /* there was a comment */
+ }
+ else
+ return 0; /* no comment */
+}
+
+COMPAT53_API int luaL_loadfilex(lua_State* L, const char* filename, const char* mode) {
+ compat53_LoadF lf;
+ int status, readstatus;
+ int c;
+ int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
+ if (filename == NULL) {
+ lua_pushliteral(L, "=stdin");
+ lf.f = stdin;
+ }
+ else {
+ lua_pushfstring(L, "@%s", filename);
+#if defined(_MSC_VER)
+ /* This code is here to stop a deprecation error that stops builds
+ * if a certain macro is defined. While normally not caring would
+ * be best, some header-only libraries and builds can't afford to
+ * dictate this to the user. A quick check shows that fopen_s this
+ * goes back to VS 2005, and _fsopen goes back to VS 2003 .NET,
+ * possibly even before that so we don't need to do any version
+ * number checks, since this has been there since forever. */
+
+ /* TO USER: if you want the behavior of typical fopen_s/fopen,
+ * which does lock the file on VC++, define the macro used below to 0 */
+#if COMPAT53_FOPEN_NO_LOCK
+ lf.f = _fsopen(filename, "r", _SH_DENYNO); /* do not lock the file in any way */
+ if (lf.f == NULL)
+ return compat53_errfile(L, "open", fnameindex);
+#else /* use default locking version */
+ if (fopen_s(&lf.f, filename, "r") != 0)
+ return compat53_errfile(L, "open", fnameindex);
+#endif /* Locking vs. No-locking fopen variants */
+#else
+ lf.f = fopen(filename, "r"); /* default stdlib doesn't forcefully lock files here */
+ if (lf.f == NULL)
+ return compat53_errfile(L, "open", fnameindex);
+#endif
+ }
+ if (compat53_skipcomment(&lf, &c)) /* read initial portion */
+ lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
+ if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
+#if defined(_MSC_VER)
+ if (freopen_s(&lf.f, filename, "rb", lf.f) != 0)
+ return compat53_errfile(L, "reopen", fnameindex);
+#else
+ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
+ if (lf.f == NULL)
+ return compat53_errfile(L, "reopen", fnameindex);
+#endif
+ compat53_skipcomment(&lf, &c); /* re-read initial portion */
+ }
+ if (c != EOF)
+ lf.buff[lf.n++] = (char)c; /* 'c' is the first character of the stream */
+ status = lua_load(L, &compat53_getF, &lf, lua_tostring(L, -1), mode);
+ readstatus = ferror(lf.f);
+ if (filename)
+ fclose(lf.f); /* close file (even in case of errors) */
+ if (readstatus) {
+ lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
+ return compat53_errfile(L, "read", fnameindex);
+ }
+ lua_remove(L, fnameindex);
+ return status;
+}
+
+COMPAT53_API int luaL_loadbufferx(lua_State* L, const char* buff, size_t sz, const char* name, const char* mode) {
+ int status = LUA_OK;
+ if (sz > 0 && buff[0] == LUA_SIGNATURE[0]) {
+ status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX);
+ }
+ else {
+ status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX);
+ }
+ if (status != LUA_OK)
+ return status;
+ return luaL_loadbuffer(L, buff, sz, name);
+}
+
+#if !defined(l_inspectstat) \
+ && (defined(unix) || defined(__unix) || defined(__unix__) || defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || (defined(__APPLE__) && defined(__MACH__)))
+/* some form of unix; check feature macros in unistd.h for details */
+#include <unistd.h>
+/* check posix version; the relevant include files and macros probably
+ * were available before 2001, but I'm not sure */
+#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L
+#include <sys/wait.h>
+#define l_inspectstat(stat, what) \
+ if (WIFEXITED(stat)) { \
+ stat = WEXITSTATUS(stat); \
+ } \
+ else if (WIFSIGNALED(stat)) { \
+ stat = WTERMSIG(stat); \
+ what = "signal"; \
+ }
+#endif
+#endif
+
+/* provide default (no-op) version */
+#if !defined(l_inspectstat)
+#define l_inspectstat(stat, what) ((void)0)
+#endif
+
+COMPAT53_API int luaL_execresult(lua_State* L, int stat) {
+ const char* what = "exit";
+ if (stat == -1)
+ return luaL_fileresult(L, 0, NULL);
+ else {
+ l_inspectstat(stat, what);
+ if (*what == 'e' && stat == 0)
+ lua_pushboolean(L, 1);
+ else
+ lua_pushnil(L);
+ lua_pushstring(L, what);
+ lua_pushinteger(L, stat);
+ return 3;
+ }
+}
+
+COMPAT53_API void luaL_buffinit(lua_State* L, luaL_Buffer_53* B) {
+ /* make it crash if used via pointer to a 5.1-style luaL_Buffer */
+ B->b.p = NULL;
+ B->b.L = NULL;
+ B->b.lvl = 0;
+ /* reuse the buffer from the 5.1-style luaL_Buffer though! */
+ B->ptr = B->b.buffer;
+ B->capacity = LUAL_BUFFERSIZE;
+ B->nelems = 0;
+ B->L2 = L;
+}
+
+COMPAT53_API char* luaL_prepbuffsize(luaL_Buffer_53* B, size_t s) {
+ if (B->capacity - B->nelems < s) { /* needs to grow */
+ char* newptr = NULL;
+ size_t newcap = B->capacity * 2;
+ if (newcap - B->nelems < s)
+ newcap = B->nelems + s;
+ if (newcap < B->capacity) /* overflow */
+ luaL_error(B->L2, "buffer too large");
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 504
+ newptr = (char*)lua_newuserdatauv(B->L2, newcap, 0);
+#else
+ newptr = (char*)lua_newuserdata(B->L2, newcap);
+#endif
+ memcpy(newptr, B->ptr, B->nelems);
+ if (B->ptr != B->b.buffer)
+ lua_replace(B->L2, -2); /* remove old buffer */
+ B->ptr = newptr;
+ B->capacity = newcap;
+ }
+ return B->ptr + B->nelems;
+}
+
+COMPAT53_API void luaL_addlstring(luaL_Buffer_53* B, const char* s, size_t l) {
+ memcpy(luaL_prepbuffsize(B, l), s, l);
+ luaL_addsize(B, l);
+}
+
+COMPAT53_API void luaL_addvalue(luaL_Buffer_53* B) {
+ size_t len = 0;
+ const char* s = lua_tolstring(B->L2, -1, &len);
+ if (!s)
+ luaL_error(B->L2, "cannot convert value to string");
+ if (B->ptr != B->b.buffer)
+ lua_insert(B->L2, -2); /* userdata buffer must be at stack top */
+ luaL_addlstring(B, s, len);
+ lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1);
+}
+
+void luaL_pushresult(luaL_Buffer_53* B) {
+ lua_pushlstring(B->L2, B->ptr, B->nelems);
+ if (B->ptr != B->b.buffer)
+ lua_replace(B->L2, -2); /* remove userdata buffer */
+}
+
+#endif /* Lua 5.1 */
+
+/* definitions for Lua 5.1 and Lua 5.2 */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502
+
+COMPAT53_API int lua_geti(lua_State* L, int index, lua_Integer i) {
+ index = lua_absindex(L, index);
+ lua_pushinteger(L, i);
+ lua_gettable(L, index);
+ return lua_type(L, -1);
+}
+
+COMPAT53_API int lua_isinteger(lua_State* L, int index) {
+ if (lua_type(L, index) == LUA_TNUMBER) {
+ lua_Number n = lua_tonumber(L, index);
+ lua_Integer i = lua_tointeger(L, index);
+ if (i == n)
+ return 1;
+ }
+ return 0;
+}
+
+COMPAT53_API lua_Integer lua_tointegerx(lua_State* L, int i, int* isnum) {
+ int ok = 0;
+ lua_Number n = lua_tonumberx(L, i, &ok);
+ if (ok) {
+ if (n == (lua_Integer)n) {
+ if (isnum)
+ *isnum = 1;
+ return (lua_Integer)n;
+ }
+ }
+ if (isnum)
+ *isnum = 0;
+ return 0;
+}
+
+static void compat53_reverse(lua_State* L, int a, int b) {
+ for (; a < b; ++a, --b) {
+ lua_pushvalue(L, a);
+ lua_pushvalue(L, b);
+ lua_replace(L, a);
+ lua_replace(L, b);
+ }
+}
+
+COMPAT53_API void lua_rotate(lua_State* L, int idx, int n) {
+ int n_elems = 0;
+ idx = lua_absindex(L, idx);
+ n_elems = lua_gettop(L) - idx + 1;
+ if (n < 0)
+ n += n_elems;
+ if (n > 0 && n < n_elems) {
+ luaL_checkstack(L, 2, "not enough stack slots available");
+ n = n_elems - n;
+ compat53_reverse(L, idx, idx + n - 1);
+ compat53_reverse(L, idx + n, idx + n_elems - 1);
+ compat53_reverse(L, idx, idx + n_elems - 1);
+ }
+}
+
+COMPAT53_API void lua_seti(lua_State* L, int index, lua_Integer i) {
+ luaL_checkstack(L, 1, "not enough stack slots available");
+ index = lua_absindex(L, index);
+ lua_pushinteger(L, i);
+ lua_insert(L, -2);
+ lua_settable(L, index);
+}
+
+#if !defined(lua_str2number)
+#define lua_str2number(s, p) strtod((s), (p))
+#endif
+
+COMPAT53_API size_t lua_stringtonumber(lua_State* L, const char* s) {
+ char* endptr;
+ lua_Number n = lua_str2number(s, &endptr);
+ if (endptr != s) {
+ while (*endptr != '\0' && isspace((unsigned char)*endptr))
+ ++endptr;
+ if (*endptr == '\0') {
+ lua_pushnumber(L, n);
+ return endptr - s + 1;
+ }
+ }
+ return 0;
+}
+
+COMPAT53_API const char* luaL_tolstring(lua_State* L, int idx, size_t* len) {
+ if (!luaL_callmeta(L, idx, "__tostring")) {
+ int t = lua_type(L, idx), tt = 0;
+ char const* name = NULL;
+ switch (t) {
+ case LUA_TNIL:
+ lua_pushliteral(L, "nil");
+ break;
+ case LUA_TSTRING:
+ case LUA_TNUMBER:
+ lua_pushvalue(L, idx);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, idx))
+ lua_pushliteral(L, "true");
+ else
+ lua_pushliteral(L, "false");
+ break;
+ default:
+ tt = luaL_getmetafield(L, idx, "__name");
+ name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t);
+ lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx));
+ if (tt != LUA_TNIL)
+ lua_replace(L, -2);
+ break;
+ }
+ }
+ else {
+ if (!lua_isstring(L, -1))
+ luaL_error(L, "'__tostring' must return a string");
+ }
+ return lua_tolstring(L, -1, len);
+}
+
+COMPAT53_API void luaL_requiref(lua_State* L, const char* modname, lua_CFunction openf, int glb) {
+ luaL_checkstack(L, 3, "not enough stack slots available");
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED");
+ if (lua_getfield(L, -1, modname) == LUA_TNIL) {
+ lua_pop(L, 1);
+ lua_pushcfunction(L, openf);
+ lua_pushstring(L, modname);
+ lua_call(L, 1, 1);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, modname);
+ }
+ if (glb) {
+ lua_pushvalue(L, -1);
+ lua_setglobal(L, modname);
+ }
+ lua_replace(L, -2);
+}
+
+#endif /* Lua 5.1 and 5.2 */
+
+#endif /* KEPLER_PROJECT_COMPAT53_C_ */
+
+/*********************************************************************
+ * This file contains parts of Lua 5.2's and Lua 5.3's source code:
+ *
+ * Copyright (C) 1994-2014 Lua.org, PUC-Rio.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *********************************************************************/
+// end of sol/compatibility/compat-5.3.c.h
+
+#endif
+
+#endif /* KEPLER_PROJECT_COMPAT53_H_ */
+
+// end of sol/compatibility/compat-5.3.h
+
+// beginning of sol/compatibility/compat-5.4.h
+
+#ifndef NOT_KEPLER_PROJECT_COMPAT54_H_
+#define NOT_KEPLER_PROJECT_COMPAT54_H_
+
+#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
+extern "C" {
+#endif
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
+}
+#endif
+
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 504
+
+#if !defined(LUA_ERRGCMM)
+/* So Lua 5.4 actually removes this, which breaks sol2...
+ man, this API is quite unstable...!
+*/
+# define LUA_ERRGCMM (LUA_ERRERR + 2)
+#endif /* LUA_ERRGCMM define */
+
+#endif // Lua 5.4 only
+
+#endif // NOT_KEPLER_PROJECT_COMPAT54_H_// end of sol/compatibility/compat-5.4.h
+
+#endif
+
+// end of sol/compatibility.hpp
+
+#include <vector>
+#include <cstdint>
+#include <cstddef>
+
+namespace sol {
+
+ template <typename Allocator = std::allocator<std::byte>>
+ class basic_bytecode : private std::vector<std::byte, Allocator> {
+ private:
+ using base_t = std::vector<std::byte, Allocator>;
+
+ public:
+ using typename base_t::allocator_type;
+ using typename base_t::const_iterator;
+ using typename base_t::const_pointer;
+ using typename base_t::const_reference;
+ using typename base_t::const_reverse_iterator;
+ using typename base_t::difference_type;
+ using typename base_t::iterator;
+ using typename base_t::pointer;
+ using typename base_t::reference;
+ using typename base_t::reverse_iterator;
+ using typename base_t::size_type;
+ using typename base_t::value_type;
+
+ using base_t::base_t;
+ using base_t::operator=;
+
+ using base_t::data;
+ using base_t::empty;
+ using base_t::max_size;
+ using base_t::size;
+
+ using base_t::at;
+ using base_t::operator[];
+ using base_t::back;
+ using base_t::front;
+
+ using base_t::begin;
+ using base_t::cbegin;
+ using base_t::cend;
+ using base_t::end;
+
+ using base_t::crbegin;
+ using base_t::crend;
+ using base_t::rbegin;
+ using base_t::rend;
+
+ using base_t::get_allocator;
+ using base_t::swap;
+
+ using base_t::clear;
+ using base_t::emplace;
+ using base_t::emplace_back;
+ using base_t::erase;
+ using base_t::insert;
+ using base_t::pop_back;
+ using base_t::push_back;
+ using base_t::reserve;
+ using base_t::resize;
+ using base_t::shrink_to_fit;
+
+ string_view as_string_view() const {
+ return string_view(reinterpret_cast<const char*>(this->data()), this->size());
+ }
+ };
+
+ template <typename Container>
+ inline int basic_insert_dump_writer(lua_State*, const void* memory, size_t memory_size, void* userdata_pointer) {
+ using storage_t = Container;
+ const std::byte* p_code = static_cast<const std::byte*>(memory);
+ storage_t& bc = *static_cast<storage_t*>(userdata_pointer);
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ bc.insert(bc.cend(), p_code, p_code + memory_size);
+#else
+ try {
+ bc.insert(bc.cend(), p_code, p_code + memory_size);
+ }
+ catch (...) {
+ return -1;
+ }
+#endif
+ return 0;
+ }
+
+ using bytecode = basic_bytecode<>;
+
+ constexpr inline auto bytecode_dump_writer = &basic_insert_dump_writer<bytecode>;
+
+} // namespace sol
+
+// end of sol/bytecode.hpp
+
+// beginning of sol/stack.hpp
+
+// beginning of sol/trampoline.hpp
+
+// beginning of sol/types.hpp
+
+// beginning of sol/error.hpp
+
+#include <stdexcept>
+#include <string>
+#include <array>
+
+namespace sol {
+ namespace detail {
+ struct direct_error_tag { };
+ const auto direct_error = direct_error_tag {};
+
+ struct error_result {
+ int results;
+ const char* format_string;
+ std::array<const char*, 4> argument_strings;
+
+ error_result() : results(0), format_string(nullptr) {
+ }
+
+ error_result(int results_) : results(results_), format_string(nullptr) {
+ }
+
+ error_result(const char* format_string_, const char* first_message_) : results(0), format_string(format_string_), argument_strings() {
+ argument_strings[0] = first_message_;
+ }
+ };
+
+ inline int handle_errors(lua_State* L, const error_result& er) {
+ if (er.format_string == nullptr) {
+ return er.results;
+ }
+ return luaL_error(L, er.format_string, er.argument_strings[0], er.argument_strings[1], er.argument_strings[2], er.argument_strings[3]);
+ }
+ } // namespace detail
+
+ class error : public std::runtime_error {
+ private:
+ // Because VC++ is upsetting, most of the time!
+ std::string what_reason;
+
+ public:
+ error(const std::string& str) : error(detail::direct_error, "lua: error: " + str) {
+ }
+ error(std::string&& str) : error(detail::direct_error, "lua: error: " + std::move(str)) {
+ }
+ error(detail::direct_error_tag, const std::string& str) : std::runtime_error(""), what_reason(str) {
+ }
+ error(detail::direct_error_tag, std::string&& str) : std::runtime_error(""), what_reason(std::move(str)) {
+ }
+
+ error(const error& e) = default;
+ error(error&& e) = default;
+ error& operator=(const error& e) = default;
+ error& operator=(error&& e) = default;
+
+ virtual const char* what() const noexcept override {
+ return what_reason.c_str();
+ }
+ };
+
+} // namespace sol
+
+// end of sol/error.hpp
+
+// beginning of sol/optional.hpp
+
+// beginning of sol/in_place.hpp
+
+#include <cstddef>
+#include <utility>
+
+namespace sol {
+
+ using in_place_t = std::in_place_t;
+ constexpr std::in_place_t in_place {};
+ constexpr std::in_place_t in_place_of {};
+
+ template <typename T>
+ using in_place_type_t = std::in_place_type_t<T>;
+ template <typename T>
+ constexpr std::in_place_type_t<T> in_place_type {};
+
+ template <size_t I>
+ using in_place_index_t = std::in_place_index_t<I>;
+ template <size_t I>
+ constexpr in_place_index_t<I> in_place_index {};
+
+} // namespace sol
+
+// end of sol/in_place.hpp
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+#include <boost/optional.hpp>
+#else
+// beginning of sol/optional_implementation.hpp
+
+#define SOL_TL_OPTIONAL_VERSION_MAJOR 0
+#define SOL_TL_OPTIONAL_VERSION_MINOR 5
+
+#include <exception>
+#include <functional>
+#include <new>
+#include <type_traits>
+#include <utility>
+#include <cstdlib>
+#include <optional>
+
+#if (defined(_MSC_VER) && _MSC_VER == 1900)
+#define SOL_TL_OPTIONAL_MSVC2015
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__))
+#define SOL_TL_OPTIONAL_GCC49
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && !defined(__clang__))
+#define SOL_TL_OPTIONAL_GCC54
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && !defined(__clang__))
+#define SOL_TL_OPTIONAL_GCC55
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__))
+#define SOL_TL_OPTIONAL_NO_CONSTRR
+
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) std::has_trivial_copy_constructor<T>::value
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign<T>::value
+
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
+
+#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
+#ifndef SOL_TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+#define SOL_TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+namespace sol { namespace detail {
+ template <class T>
+ struct is_trivially_copy_constructible : std::is_trivially_copy_constructible<T> { };
+#ifdef _GLIBCXX_VECTOR
+ template <class T, class A>
+ struct is_trivially_copy_constructible<std::vector<T, A>> : std::is_trivially_copy_constructible<T> { };
+#endif
+}} // namespace sol::detail
+#endif
+
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) sol::detail::is_trivially_copy_constructible<T>::value
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::is_trivially_copy_assignable<T>::value
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
+#else
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) std::is_trivially_copy_constructible<T>::value
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::is_trivially_copy_assignable<T>::value
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
+#endif
+
+#if __cplusplus > 201103L
+#define SOL_TL_OPTIONAL_CXX14
+#endif
+
+#if (__cplusplus == 201103L || defined(SOL_TL_OPTIONAL_MSVC2015) || defined(SOL_TL_OPTIONAL_GCC49))
+#define SOL_TL_OPTIONAL_11_CONSTEXPR
+#else
+ /// \exclude
+#define SOL_TL_OPTIONAL_11_CONSTEXPR constexpr
+#endif
+
+namespace sol {
+#ifndef SOL_TL_MONOSTATE_INPLACE_MUTEX
+#define SOL_TL_MONOSTATE_INPLACE_MUTEX
+ /// \brief Used to represent an optional with no data; essentially a bool
+ class monostate { };
+#endif
+
+ template <class T>
+ class optional;
+
+ /// \exclude
+ namespace detail {
+#ifndef SOL_TL_TRAITS_MUTEX
+#define SOL_TL_TRAITS_MUTEX
+ // C++14-style aliases for brevity
+ template <class T>
+ using remove_const_t = typename std::remove_const<T>::type;
+ template <class T>
+ using remove_reference_t = typename std::remove_reference<T>::type;
+ template <class T>
+ using decay_t = typename std::decay<T>::type;
+ template <bool E, class T = void>
+ using enable_if_t = typename std::enable_if<E, T>::type;
+ template <bool B, class T, class F>
+ using conditional_t = typename std::conditional<B, T, F>::type;
+
+ // std::conjunction from C++17
+ template <class...>
+ struct conjunction : std::true_type { };
+ template <class B>
+ struct conjunction<B> : B { };
+ template <class B, class... Bs>
+ struct conjunction<B, Bs...> : std::conditional<bool(B::value), conjunction<Bs...>, B>::type { };
+
+#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
+#define SOL_TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND
+#endif
+
+#ifdef SOL_TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND
+ template <class T>
+ struct is_pointer_to_non_const_member_func : std::false_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)> : std::true_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)&> : std::true_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&> : std::true_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile> : std::true_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&> : std::true_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&&> : std::true_type { };
+
+ template <class T>
+ struct is_const_or_const_ref : std::false_type { };
+ template <class T>
+ struct is_const_or_const_ref<T const&> : std::true_type { };
+ template <class T>
+ struct is_const_or_const_ref<T const> : std::true_type { };
+#endif
+
+ // std::invoke from C++17
+ // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
+ template <typename Fn, typename... Args,
+#ifdef SOL_TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND
+ typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value && is_const_or_const_ref<Args...>::value)>,
+#endif
+ typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
+ constexpr auto invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
+ -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
+ return std::mem_fn(f)(std::forward<Args>(args)...);
+ }
+
+ template <typename Fn, typename... Args, typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
+ constexpr auto invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
+ -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
+ return std::forward<Fn>(f)(std::forward<Args>(args)...);
+ }
+
+ // std::invoke_result from C++17
+ template <class F, class, class... Us>
+ struct invoke_result_impl;
+
+ template <class F, class... Us>
+ struct invoke_result_impl<F, decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()), Us...> {
+ using type = decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
+ };
+
+ template <class F, class... Us>
+ using invoke_result = invoke_result_impl<F, void, Us...>;
+
+ template <class F, class... Us>
+ using invoke_result_t = typename invoke_result<F, Us...>::type;
+#endif
+
+ // std::void_t from C++17
+ template <class...>
+ struct voider {
+ using type = void;
+ };
+ template <class... Ts>
+ using void_t = typename voider<Ts...>::type;
+
+ // Trait for checking if a type is a sol::optional
+ template <class T>
+ struct is_optional_impl : std::false_type { };
+ template <class T>
+ struct is_optional_impl<optional<T>> : std::true_type { };
+ template <class T>
+ using is_optional = is_optional_impl<decay_t<T>>;
+
+ // Change void to sol::monostate
+ template <class U>
+ using fixup_void = conditional_t<std::is_void<U>::value, monostate, U>;
+
+ template <class F, class U, class = invoke_result_t<F, U>>
+ using get_map_return = optional<fixup_void<invoke_result_t<F, U>>>;
+
+ // Check if invoking F for some Us returns void
+ template <class F, class = void, class... U>
+ struct returns_void_impl;
+ template <class F, class... U>
+ struct returns_void_impl<F, void_t<invoke_result_t<F, U...>>, U...> : std::is_void<invoke_result_t<F, U...>> { };
+ template <class F, class... U>
+ using returns_void = returns_void_impl<F, void, U...>;
+
+ template <class T, class... U>
+ using enable_if_ret_void = enable_if_t<returns_void<T&&, U...>::value>;
+
+ template <class T, class... U>
+ using disable_if_ret_void = enable_if_t<!returns_void<T&&, U...>::value>;
+
+ template <class T, class U>
+ using enable_forward_value = detail::enable_if_t<std::is_constructible<T, U&&>::value && !std::is_same<detail::decay_t<U>, in_place_t>::value
+ && !std::is_same<optional<T>, detail::decay_t<U>>::value>;
+
+ template <class T, class U, class Other>
+ using enable_from_other = detail::enable_if_t<std::is_constructible<T, Other>::value && !std::is_constructible<T, optional<U>&>::value
+ && !std::is_constructible<T, optional<U>&&>::value && !std::is_constructible<T, const optional<U>&>::value
+ && !std::is_constructible<T, const optional<U>&&>::value && !std::is_convertible<optional<U>&, T>::value
+ && !std::is_convertible<optional<U>&&, T>::value && !std::is_convertible<const optional<U>&, T>::value
+ && !std::is_convertible<const optional<U>&&, T>::value>;
+
+ template <class T, class U>
+ using enable_assign_forward = detail::enable_if_t<!std::is_same<optional<T>, detail::decay_t<U>>::value
+ && !detail::conjunction<std::is_scalar<T>, std::is_same<T, detail::decay_t<U>>>::value && std::is_constructible<T, U>::value
+ && std::is_assignable<T&, U>::value>;
+
+ template <class T, class U, class Other>
+ using enable_assign_from_other = detail::enable_if_t<std::is_constructible<T, Other>::value && std::is_assignable<T&, Other>::value
+ && !std::is_constructible<T, optional<U>&>::value && !std::is_constructible<T, optional<U>&&>::value
+ && !std::is_constructible<T, const optional<U>&>::value && !std::is_constructible<T, const optional<U>&&>::value
+ && !std::is_convertible<optional<U>&, T>::value && !std::is_convertible<optional<U>&&, T>::value
+ && !std::is_convertible<const optional<U>&, T>::value && !std::is_convertible<const optional<U>&&, T>::value
+ && !std::is_assignable<T&, optional<U>&>::value && !std::is_assignable<T&, optional<U>&&>::value
+ && !std::is_assignable<T&, const optional<U>&>::value && !std::is_assignable<T&, const optional<U>&&>::value>;
+
+#ifdef _MSC_VER
+ // TODO make a version which works with MSVC
+ template <class T, class U = T>
+ struct is_swappable : std::true_type { };
+
+ template <class T, class U = T>
+ struct is_nothrow_swappable : std::true_type { };
+#else
+ // https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
+ namespace swap_adl_tests {
+ // if swap ADL finds this then it would call std::swap otherwise (same
+ // signature)
+ struct tag { };
+
+ template <class T>
+ tag swap(T&, T&);
+ template <class T, std::size_t N>
+ tag swap(T (&a)[N], T (&b)[N]);
+
+ // helper functions to test if an unqualified swap is possible, and if it
+ // becomes std::swap
+ template <class, class>
+ std::false_type can_swap(...) noexcept(false);
+ template <class T, class U, class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
+ std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(), std::declval<U&>())));
+
+ template <class, class>
+ std::false_type uses_std(...);
+ template <class T, class U>
+ std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag> uses_std(int);
+
+ template <class T>
+ struct is_std_swap_noexcept
+ : std::integral_constant<bool, std::is_nothrow_move_constructible<T>::value && std::is_nothrow_move_assignable<T>::value> { };
+
+ template <class T, std::size_t N>
+ struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> { };
+
+ template <class T, class U>
+ struct is_adl_swap_noexcept : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> { };
+ } // namespace swap_adl_tests
+
+ template <class T, class U = T>
+ struct is_swappable : std::integral_constant<bool,
+ decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value
+ && (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value
+ || (std::is_move_assignable<T>::value && std::is_move_constructible<T>::value))> { };
+
+ template <class T, std::size_t N>
+ struct is_swappable<T[N], T[N]> : std::integral_constant<bool,
+ decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value
+ && (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(0))::value || is_swappable<T, T>::value)> { };
+
+ template <class T, class U = T>
+ struct is_nothrow_swappable
+ : std::integral_constant<bool,
+ is_swappable<T, U>::value
+ && ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value&& detail::swap_adl_tests::is_std_swap_noexcept<T>::value)
+ || (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value&& detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> { };
+#endif
+
+ // The storage base manages the actual storage, and correctly propagates
+ // trivial destroyion from T. This case is for when T is not trivially
+ // destructible.
+ template <class T, bool = ::std::is_trivially_destructible<T>::value>
+ struct optional_storage_base {
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {
+ }
+
+ template <class... U>
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U&&... u) : m_value(std::forward<U>(u)...), m_has_value(true) {
+ }
+
+ ~optional_storage_base() {
+ if (m_has_value) {
+ m_value.~T();
+ m_has_value = false;
+ }
+ }
+
+ struct dummy { };
+ union {
+ dummy m_dummy;
+ T m_value;
+ };
+
+ bool m_has_value;
+ };
+
+ // This case is for when T is trivially destructible.
+ template <class T>
+ struct optional_storage_base<T, true> {
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {
+ }
+
+ template <class... U>
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U&&... u) : m_value(std::forward<U>(u)...), m_has_value(true) {
+ }
+
+ // No destructor, so this class is trivially destructible
+
+ struct dummy { };
+ union {
+ dummy m_dummy;
+ T m_value;
+ };
+
+ bool m_has_value = false;
+ };
+
+ // This base class provides some handy member functions which can be used in
+ // further derived classes
+ template <class T>
+ struct optional_operations_base : optional_storage_base<T> {
+ using optional_storage_base<T>::optional_storage_base;
+
+ void hard_reset() noexcept {
+ get().~T();
+ this->m_has_value = false;
+ }
+
+ template <class... Args>
+ void construct(Args&&... args) noexcept {
+ new (std::addressof(this->m_value)) T(std::forward<Args>(args)...);
+ this->m_has_value = true;
+ }
+
+ template <class Opt>
+ void assign(Opt&& rhs) {
+ if (this->has_value()) {
+ if (rhs.has_value()) {
+ this->m_value = std::forward<Opt>(rhs).get();
+ }
+ else {
+ this->m_value.~T();
+ this->m_has_value = false;
+ }
+ }
+
+ else if (rhs.has_value()) {
+ construct(std::forward<Opt>(rhs).get());
+ }
+ }
+
+ bool has_value() const {
+ return this->m_has_value;
+ }
+
+ SOL_TL_OPTIONAL_11_CONSTEXPR T& get() & {
+ return this->m_value;
+ }
+ SOL_TL_OPTIONAL_11_CONSTEXPR const T& get() const& {
+ return this->m_value;
+ }
+ SOL_TL_OPTIONAL_11_CONSTEXPR T&& get() && {
+ return std::move(this->m_value);
+ }
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ constexpr const T&& get() const&& {
+ return std::move(this->m_value);
+ }
+#endif
+ };
+
+ // This class manages conditionally having a trivial copy constructor
+ // This specialization is for when T is trivially copy constructible
+ template <class T, bool = SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>
+ struct optional_copy_base : optional_operations_base<T> {
+ using optional_operations_base<T>::optional_operations_base;
+ };
+
+ // This specialization is for when T is not trivially copy constructible
+ template <class T>
+ struct optional_copy_base<T, false> : optional_operations_base<T> {
+ using base_t = optional_operations_base<T>;
+
+ using base_t::base_t;
+
+ optional_copy_base() = default;
+ optional_copy_base(const optional_copy_base& rhs) : base_t() {
+ if (rhs.has_value()) {
+ this->construct(rhs.get());
+ }
+ else {
+ this->m_has_value = false;
+ }
+ }
+
+ optional_copy_base(optional_copy_base&& rhs) = default;
+ optional_copy_base& operator=(const optional_copy_base& rhs) = default;
+ optional_copy_base& operator=(optional_copy_base&& rhs) = default;
+ };
+
+#ifndef SOL_TL_OPTIONAL_GCC49
+ template <class T, bool = std::is_trivially_move_constructible<T>::value>
+ struct optional_move_base : optional_copy_base<T> {
+ using optional_copy_base<T>::optional_copy_base;
+ };
+#else
+ template <class T, bool = false>
+ struct optional_move_base;
+#endif
+ template <class T>
+ struct optional_move_base<T, false> : optional_copy_base<T> {
+ using optional_copy_base<T>::optional_copy_base;
+
+ optional_move_base() = default;
+ optional_move_base(const optional_move_base& rhs) = default;
+
+ optional_move_base(optional_move_base&& rhs) noexcept(std::is_nothrow_move_constructible<T>::value) {
+ if (rhs.has_value()) {
+ this->construct(std::move(rhs.get()));
+ }
+ else {
+ this->m_has_value = false;
+ }
+ }
+ optional_move_base& operator=(const optional_move_base& rhs) = default;
+ optional_move_base& operator=(optional_move_base&& rhs) = default;
+ };
+
+ // This class manages conditionally having a trivial copy assignment operator
+ template <class T,
+ bool = SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) && SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)
+ && SOL_TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T)>
+ struct optional_copy_assign_base : optional_move_base<T> {
+ using optional_move_base<T>::optional_move_base;
+ };
+
+ template <class T>
+ struct optional_copy_assign_base<T, false> : optional_move_base<T> {
+ using optional_move_base<T>::optional_move_base;
+
+ optional_copy_assign_base() = default;
+ optional_copy_assign_base(const optional_copy_assign_base& rhs) = default;
+
+ optional_copy_assign_base(optional_copy_assign_base&& rhs) = default;
+ optional_copy_assign_base& operator=(const optional_copy_assign_base& rhs) {
+ this->assign(rhs);
+ return *this;
+ }
+ optional_copy_assign_base& operator=(optional_copy_assign_base&& rhs) = default;
+ };
+
+#ifndef SOL_TL_OPTIONAL_GCC49
+ template <class T,
+ bool = std::is_trivially_destructible<T>::value&& std::is_trivially_move_constructible<T>::value&& std::is_trivially_move_assignable<T>::value>
+ struct optional_move_assign_base : optional_copy_assign_base<T> {
+ using optional_copy_assign_base<T>::optional_copy_assign_base;
+ };
+#else
+ template <class T, bool = false>
+ struct optional_move_assign_base;
+#endif
+
+ template <class T>
+ struct optional_move_assign_base<T, false> : optional_copy_assign_base<T> {
+ using optional_copy_assign_base<T>::optional_copy_assign_base;
+
+ optional_move_assign_base() = default;
+ optional_move_assign_base(const optional_move_assign_base& rhs) = default;
+
+ optional_move_assign_base(optional_move_assign_base&& rhs) = default;
+
+ optional_move_assign_base& operator=(const optional_move_assign_base& rhs) = default;
+
+ optional_move_assign_base& operator=(optional_move_assign_base&& rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value&& std::is_nothrow_move_assignable<T>::value) {
+ this->assign(std::move(rhs));
+ return *this;
+ }
+ };
+
+ // optional_delete_ctor_base will conditionally delete copy and move
+ // constructors depending on whether T is copy/move constructible
+ template <class T, bool EnableCopy = std::is_copy_constructible<T>::value, bool EnableMove = std::is_move_constructible<T>::value>
+ struct optional_delete_ctor_base {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = default;
+ optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default;
+ };
+
+ template <class T>
+ struct optional_delete_ctor_base<T, true, false> {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = delete;
+ optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default;
+ };
+
+ template <class T>
+ struct optional_delete_ctor_base<T, false, true> {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base&) = delete;
+ optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = default;
+ optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default;
+ };
+
+ template <class T>
+ struct optional_delete_ctor_base<T, false, false> {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base&) = delete;
+ optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = delete;
+ optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default;
+ };
+
+ // optional_delete_assign_base will conditionally delete copy and move
+ // constructors depending on whether T is copy/move constructible + assignable
+ template <class T, bool EnableCopy = (std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value),
+ bool EnableMove = (std::is_move_constructible<T>::value && std::is_move_assignable<T>::value)>
+ struct optional_delete_assign_base {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
+ optional_delete_assign_base& operator=(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = default;
+ };
+
+ template <class T>
+ struct optional_delete_assign_base<T, true, false> {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
+ optional_delete_assign_base& operator=(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = delete;
+ };
+
+ template <class T>
+ struct optional_delete_assign_base<T, false, true> {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
+ optional_delete_assign_base& operator=(const optional_delete_assign_base&) = delete;
+ optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = default;
+ };
+
+ template <class T>
+ struct optional_delete_assign_base<T, false, false> {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
+ optional_delete_assign_base& operator=(const optional_delete_assign_base&) = delete;
+ optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = delete;
+ };
+
+ } // namespace detail
+
+ /// \brief A tag type to represent an empty optional
+ using nullopt_t = std::nullopt_t;
+
+ /// \brief Represents an empty optional
+ /// \synopsis static constexpr nullopt_t nullopt;
+ ///
+ /// *Examples*:
+ /// ```
+ /// sol::optional<int> a = sol::nullopt;
+ /// void foo (sol::optional<int>);
+ /// foo(sol::nullopt); //pass an empty optional
+ /// ```
+ using std::nullopt;
+
+ /// @brief An exception for when an optional is accessed through specific methods while it is not engaged.
+ class bad_optional_access : public std::exception {
+ public:
+ /// @brief Default-constructs an optional exception.
+ bad_optional_access() = default;
+ /// @brief Returns a pointer to a null-terminated string containing the reason for the exception.
+ const char* what() const noexcept override {
+ return "Optional has no value";
+ }
+ };
+
+ /// An optional object is an object that contains the storage for another
+ /// object and manages the lifetime of this contained object, if any. The
+ /// contained object may be initialized after the optional object has been
+ /// initialized, and may be destroyed before the optional object has been
+ /// destroyed. The initialization state of the contained object is tracked by
+ /// the optional object.
+ template <class T>
+ class optional : private detail::optional_move_assign_base<T>,
+ private detail::optional_delete_ctor_base<T>,
+ private detail::optional_delete_assign_base<T> {
+ using base = detail::optional_move_assign_base<T>;
+
+ static_assert(!std::is_same<T, in_place_t>::value, "instantiation of optional with in_place_t is ill-formed");
+ static_assert(!std::is_same<detail::decay_t<T>, nullopt_t>::value, "instantiation of optional with nullopt_t is ill-formed");
+
+ public:
+#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55)
+ /// \group and_then
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one. \requires `std::invoke(std::forward<F>(f),
+ /// value())` returns a `std::optional<U>` for some `U`. \returns Let `U` be
+ /// the result of `std::invoke(std::forward<F>(f), value())`. Returns a
+ /// `std::optional<U>`. The return value is empty if `*this` is empty,
+ /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
+ /// is returned.
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) & {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) && {
+ using result = detail::invoke_result_t<F, T&&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &;
+ template <class F>
+ constexpr auto and_then(F&& f) const& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &&;
+ template <class F>
+ constexpr auto and_then(F&& f) const&& {
+ using result = detail::invoke_result_t<F, const T&&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : result(nullopt);
+ }
+#endif
+#else
+ /// \group and_then
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one. \requires `std::invoke(std::forward<F>(f),
+ /// value())` returns a `std::optional<U>` for some `U`.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. Returns a `std::optional<U>`. The return value is empty if
+ /// `*this` is empty, otherwise the return value of
+ /// `std::invoke(std::forward<F>(f), value())` is returned.
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&> and_then(F&& f) & {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&&> and_then(F&& f) && {
+ using result = detail::invoke_result_t<F, T&&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &;
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T&> and_then(F&& f) const& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &&;
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T&&> and_then(F&& f) const&& {
+ using result = detail::invoke_result_t<F, const T&&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : result(nullopt);
+ }
+#endif
+#endif
+
+#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55)
+ /// \brief Carries out some operation on the stored object if there is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. Returns a `std::optional<U>`. The return value is empty if
+ /// `*this` is empty, otherwise an `optional<U>` is constructed from the
+ /// return value of `std::invoke(std::forward<F>(f), value())` and is
+ /// returned.
+ ///
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const&;
+ template <class F>
+ constexpr auto map(F&& f) const& {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const&&;
+ template <class F>
+ constexpr auto map(F&& f) const&& {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ /// \brief Carries out some operation on the stored object if there is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. Returns a `std::optional<U>`. The return value is empty if
+ /// `*this` is empty, otherwise an `optional<U>` is constructed from the
+ /// return value of `std::invoke(std::forward<F>(f), value())` and is
+ /// returned.
+ ///
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&>(), std::declval<F&&>())) map(F&& f) & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&&>(), std::declval<F&&>())) map(F&& f) && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) const&;
+ template <class F>
+ constexpr decltype(optional_map_impl(std::declval<const optional&>(), std::declval<F&&>())) map(F&& f) const& {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) const&&;
+ template <class F>
+ constexpr decltype(optional_map_impl(std::declval<const optional&&>(), std::declval<F&&>())) map(F&& f) const&& {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+ /// \brief Calls `f` if the optional is empty
+ /// \requires `std::invoke_result_t<F>` must be void or convertible to
+ /// `optional<T>`.
+ /// \effects If `*this` has a value, returns `*this`.
+ /// Otherwise, if `f` returns `void`, calls `std::forward<F>(f)` and returns
+ /// `std::nullopt`. Otherwise, returns `std::forward<F>(f)()`.
+ ///
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) &;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) &&;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) && {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) && {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) const &;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const& {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) const& {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \exclude
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const&& {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const&& {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+#endif
+
+ /// \brief Maps the stored value with `f` if there is one, otherwise returns
+ /// `u`.
+ ///
+ /// \details If there is a value stored, then `f` is called with `**this`
+ /// and the value is returned. Otherwise `u` is returned.
+ ///
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u);
+ }
+
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u);
+ }
+
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) const& {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) const&& {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u);
+ }
+#endif
+
+ /// \brief Maps the stored value with `f` if there is one, otherwise calls
+ /// `u` and returns the result.
+ ///
+ /// \details If there is a value stored, then `f` is
+ /// called with `**this` and the value is returned. Otherwise
+ /// `std::forward<U>(u)()` is returned.
+ ///
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u) &;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u)();
+ }
+
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// &&;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u)();
+ }
+
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// const &;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const& {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u)();
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// const &&;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const&& {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u)();
+ }
+#endif
+
+ /// \returns `u` if `*this` has a value, otherwise an empty optional.
+ template <class U>
+ constexpr optional<typename std::decay<U>::type> conjunction(U&& u) const {
+ using result = optional<detail::decay_t<U>>;
+ return has_value() ? result { u } : result { nullopt };
+ }
+
+ /// \returns `rhs` if `*this` is empty, otherwise the current value.
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) & {
+ return has_value() ? *this : rhs;
+ }
+
+ /// \group disjunction
+ constexpr optional disjunction(const optional& rhs) const& {
+ return has_value() ? *this : rhs;
+ }
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) && {
+ return has_value() ? std::move(*this) : rhs;
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group disjunction
+ constexpr optional disjunction(const optional& rhs) const&& {
+ return has_value() ? std::move(*this) : rhs;
+ }
+#endif
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) & {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ /// \group disjunction
+ constexpr optional disjunction(optional&& rhs) const& {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) && {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group disjunction
+ constexpr optional disjunction(optional&& rhs) const&& {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+#endif
+
+ /// Takes the value out of the optional, leaving it empty
+ /// \group take
+ optional take() & {
+ optional ret = *this;
+ reset();
+ return ret;
+ }
+
+ /// \group take
+ optional take() const& {
+ optional ret = *this;
+ reset();
+ return ret;
+ }
+
+ /// \group take
+ optional take() && {
+ optional ret = std::move(*this);
+ reset();
+ return ret;
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group take
+ optional take() const&& {
+ optional ret = std::move(*this);
+ reset();
+ return ret;
+ }
+#endif
+
+ using value_type = T;
+
+ /// Constructs an optional that does not contain a value.
+ /// \group ctor_empty
+ constexpr optional() noexcept = default;
+
+ /// \group ctor_empty
+ constexpr optional(nullopt_t) noexcept {
+ }
+
+ /// Copy constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional(const optional& rhs) = default;
+
+ /// Move constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional(optional&& rhs) = default;
+
+ /// Constructs the stored value in-place using the given arguments.
+ /// \group in_place
+ /// \synopsis template <class... Args> constexpr explicit optional(in_place_t, Args&&... args);
+ template <class... Args>
+ constexpr explicit optional(detail::enable_if_t<std::is_constructible<T, Args...>::value, in_place_t>, Args&&... args)
+ : base(in_place, std::forward<Args>(args)...) {
+ }
+
+ /// \group in_place
+ /// \synopsis template <class U, class... Args> \n constexpr explicit optional(in_place_t, std::initializer_list<U>&, Args&&... args);
+ template <class U, class... Args>
+ SOL_TL_OPTIONAL_11_CONSTEXPR explicit optional(detail::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, in_place_t>,
+ std::initializer_list<U> il, Args&&... args) {
+ this->construct(il, std::forward<Args>(args)...);
+ }
+
+#if 0 // SOL_MODIFICATION
+ /// Constructs the stored value with `u`.
+ /// \synopsis template <class U=T> constexpr optional(U &&u);
+ template <class U = T, detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr, detail::enable_forward_value<T, U>* = nullptr>
+ constexpr optional(U&& u) : base(in_place, std::forward<U>(u)) {
+ }
+
+ /// \exclude
+ template <class U = T, detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr, detail::enable_forward_value<T, U>* = nullptr>
+ constexpr explicit optional(U&& u) : base(in_place, std::forward<U>(u)) {
+ }
+#else
+ /// Constructs the stored value with `u`.
+ /// \synopsis template <class U=T> constexpr optional(U &&u);
+ constexpr optional(T&& u) : base(in_place, std::move(u)) {
+ }
+
+ /// \exclude
+ constexpr optional(const T& u) : base(in_place, u) {
+ }
+#endif // sol2 modification
+
+ /// Converting copy constructor.
+ /// \synopsis template <class U> optional(const optional<U> &rhs);
+ template <class U, detail::enable_from_other<T, U, const U&>* = nullptr, detail::enable_if_t<std::is_convertible<const U&, T>::value>* = nullptr>
+ optional(const optional<U>& rhs) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ }
+ }
+
+ /// \exclude
+ template <class U, detail::enable_from_other<T, U, const U&>* = nullptr, detail::enable_if_t<!std::is_convertible<const U&, T>::value>* = nullptr>
+ explicit optional(const optional<U>& rhs) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ }
+ }
+
+ /// Converting move constructor.
+ /// \synopsis template <class U> optional(optional<U> &&rhs);
+ template <class U, detail::enable_from_other<T, U, U&&>* = nullptr, detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr>
+ optional(optional<U>&& rhs) {
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ }
+ }
+
+ /// \exclude
+ template <class U, detail::enable_from_other<T, U, U&&>* = nullptr, detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr>
+ explicit optional(optional<U>&& rhs) {
+ this->construct(std::move(*rhs));
+ }
+
+ /// Destroys the stored value if there is one.
+ ~optional() = default;
+
+ /// Assignment to empty.
+ ///
+ /// Destroys the current value if there is one.
+ optional& operator=(nullopt_t) noexcept {
+ if (has_value()) {
+ this->m_value.~T();
+ this->m_has_value = false;
+ }
+
+ return *this;
+ }
+
+ /// Copy assignment.
+ ///
+ /// Copies the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ optional& operator=(const optional& rhs) = default;
+
+ /// Move assignment.
+ ///
+ /// Moves the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ optional& operator=(optional&& rhs) = default;
+
+ /// Assigns the stored value from `u`, destroying the old value if there was
+ /// one.
+ /// \synopsis optional &operator=(U &&u);
+ template <class U = T, detail::enable_assign_forward<T, U>* = nullptr>
+ optional& operator=(U&& u) {
+ if (has_value()) {
+ this->m_value = std::forward<U>(u);
+ }
+ else {
+ this->construct(std::forward<U>(u));
+ }
+
+ return *this;
+ }
+
+ /// Converting copy assignment operator.
+ ///
+ /// Copies the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ /// \synopsis optional &operator=(const optional<U> & rhs);
+ template <class U, detail::enable_assign_from_other<T, U, const U&>* = nullptr>
+ optional& operator=(const optional<U>& rhs) {
+ if (has_value()) {
+ if (rhs.has_value()) {
+ this->m_value = *rhs;
+ }
+ else {
+ this->hard_reset();
+ }
+ }
+
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ }
+
+ return *this;
+ }
+
+ // TODO check exception guarantee
+ /// Converting move assignment operator.
+ ///
+ /// Moves the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ /// \synopsis optional &operator=(optional<U> && rhs);
+ template <class U, detail::enable_assign_from_other<T, U, U>* = nullptr>
+ optional& operator=(optional<U>&& rhs) {
+ if (has_value()) {
+ if (rhs.has_value()) {
+ this->m_value = std::move(*rhs);
+ }
+ else {
+ this->hard_reset();
+ }
+ }
+
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ }
+
+ return *this;
+ }
+
+ /// Constructs the value in-place, destroying the current one if there is
+ /// one.
+ /// \group emplace
+ template <class... Args>
+ T& emplace(Args&&... args) {
+ static_assert(std::is_constructible<T, Args&&...>::value, "T must be constructible with Args");
+
+ *this = nullopt;
+ this->construct(std::forward<Args>(args)...);
+ return value();
+ }
+
+ /// \group emplace
+ /// \synopsis template <class U, class... Args> \n T& emplace(std::initializer_list<U> il, Args &&... args);
+ template <class U, class... Args>
+ detail::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, T&> emplace(std::initializer_list<U> il, Args&&... args) {
+ *this = nullopt;
+ this->construct(il, std::forward<Args>(args)...);
+ return value();
+ }
+
+ /// Swaps this optional with the other.
+ ///
+ /// If neither optionals have a value, nothing happens.
+ /// If both have a value, the values are swapped.
+ /// If one has a value, it is moved to the other and the movee is left
+ /// valueless.
+ void swap(optional& rhs) noexcept(std::is_nothrow_move_constructible<T>::value&& detail::is_nothrow_swappable<T>::value) {
+ if (has_value()) {
+ if (rhs.has_value()) {
+ using std::swap;
+ swap(**this, *rhs);
+ }
+ else {
+ new (std::addressof(rhs.m_value)) T(std::move(this->m_value));
+ this->m_value.T::~T();
+ }
+ }
+ else if (rhs.has_value()) {
+ new (std::addressof(this->m_value)) T(std::move(rhs.m_value));
+ rhs.m_value.T::~T();
+ }
+ }
+
+ /// \returns a pointer to the stored value
+ /// \requires a value is stored
+ /// \group pointer
+ /// \synopsis constexpr const T *operator->() const;
+ constexpr const T* operator->() const {
+ return std::addressof(this->m_value);
+ }
+
+ /// \group pointer
+ /// \synopsis constexpr T *operator->();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T* operator->() {
+ return std::addressof(this->m_value);
+ }
+
+ /// \returns the stored value
+ /// \requires a value is stored
+ /// \group deref
+ /// \synopsis constexpr T &operator*();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T& operator*() & {
+ return this->m_value;
+ }
+
+ /// \group deref
+ /// \synopsis constexpr const T &operator*() const;
+ constexpr const T& operator*() const& {
+ return this->m_value;
+ }
+
+ /// \exclude
+ SOL_TL_OPTIONAL_11_CONSTEXPR T&& operator*() && {
+ return std::move(this->m_value);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \exclude
+ constexpr const T&& operator*() const&& {
+ return std::move(this->m_value);
+ }
+#endif
+
+ /// \returns whether or not the optional has a value
+ /// \group has_value
+ constexpr bool has_value() const noexcept {
+ return this->m_has_value;
+ }
+
+ /// \group has_value
+ constexpr explicit operator bool() const noexcept {
+ return this->m_has_value;
+ }
+
+ /// \returns the contained value if there is one, otherwise throws
+ /// [bad_optional_access]
+ /// \group value
+ /// \synopsis constexpr T &value();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T& value() & {
+ if (has_value())
+ return this->m_value;
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+ /// \group value
+ /// \synopsis constexpr const T &value() const;
+ SOL_TL_OPTIONAL_11_CONSTEXPR const T& value() const& {
+ if (has_value())
+ return this->m_value;
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+ /// \exclude
+ SOL_TL_OPTIONAL_11_CONSTEXPR T&& value() && {
+ if (has_value())
+ return std::move(this->m_value);
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \exclude
+ SOL_TL_OPTIONAL_11_CONSTEXPR const T&& value() const&& {
+ if (has_value())
+ return std::move(this->m_value);
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+#endif
+
+ /// \returns the stored value if there is one, otherwise returns `u`
+ /// \group value_or
+ template <class U>
+ constexpr T value_or(U&& u) const& {
+ static_assert(std::is_copy_constructible<T>::value && std::is_convertible<U&&, T>::value, "T must be copy constructible and convertible from U");
+ return has_value() ? **this : static_cast<T>(std::forward<U>(u));
+ }
+
+ /// \group value_or
+ template <class U>
+ SOL_TL_OPTIONAL_11_CONSTEXPR T value_or(U&& u) && {
+ static_assert(std::is_move_constructible<T>::value && std::is_convertible<U&&, T>::value, "T must be move constructible and convertible from U");
+ return has_value() ? **this : static_cast<T>(std::forward<U>(u));
+ }
+
+ /// Destroys the stored value if one exists, making the optional empty
+ void reset() noexcept {
+ if (has_value()) {
+ this->m_value.~T();
+ this->m_has_value = false;
+ }
+ }
+ }; // namespace sol
+
+ /// \group relop
+ /// \brief Compares two optional objects
+ /// \details If both optionals contain a value, they are compared with `T`s
+ /// relational operators. Otherwise `lhs` and `rhs` are equal only if they are
+ /// both empty, and `lhs` is less than `rhs` only if `rhs` is empty and `lhs`
+ /// is not.
+ template <class T, class U>
+ inline constexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs) {
+ return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs);
+ }
+ /// \group relop
+ template <class T, class U>
+ inline constexpr bool operator!=(const optional<T>& lhs, const optional<U>& rhs) {
+ return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs);
+ }
+ /// \group relop
+ template <class T, class U>
+ inline constexpr bool operator<(const optional<T>& lhs, const optional<U>& rhs) {
+ return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs);
+ }
+ /// \group relop
+ template <class T, class U>
+ inline constexpr bool operator>(const optional<T>& lhs, const optional<U>& rhs) {
+ return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs);
+ }
+ /// \group relop
+ template <class T, class U>
+ inline constexpr bool operator<=(const optional<T>& lhs, const optional<U>& rhs) {
+ return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs);
+ }
+ /// \group relop
+ template <class T, class U>
+ inline constexpr bool operator>=(const optional<T>& lhs, const optional<U>& rhs) {
+ return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs);
+ }
+
+ /// \group relop_nullopt
+ /// \brief Compares an optional to a `nullopt`
+ /// \details Equivalent to comparing the optional to an empty optional
+ template <class T>
+ inline constexpr bool operator==(const optional<T>& lhs, nullopt_t) noexcept {
+ return !lhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator==(nullopt_t, const optional<T>& rhs) noexcept {
+ return !rhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator!=(const optional<T>& lhs, nullopt_t) noexcept {
+ return lhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator!=(nullopt_t, const optional<T>& rhs) noexcept {
+ return rhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator<(const optional<T>&, nullopt_t) noexcept {
+ return false;
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator<(nullopt_t, const optional<T>& rhs) noexcept {
+ return rhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator<=(const optional<T>& lhs, nullopt_t) noexcept {
+ return !lhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept {
+ return true;
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator>(const optional<T>& lhs, nullopt_t) noexcept {
+ return lhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator>(nullopt_t, const optional<T>&) noexcept {
+ return false;
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept {
+ return true;
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator>=(nullopt_t, const optional<T>& rhs) noexcept {
+ return !rhs.has_value();
+ }
+
+ /// \group relop_t
+ /// \brief Compares the optional with a value.
+ /// \details If the optional has a value, it is compared with the other value
+ /// using `T`s relational operators. Otherwise, the optional is considered
+ /// less than the value.
+ template <class T, class U>
+ inline constexpr bool operator==(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs == rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator==(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs == *rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator!=(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs != rhs : true;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator!=(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs != *rhs : true;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator<(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs < rhs : true;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator<(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs < *rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator<=(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs <= rhs : true;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator<=(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs <= *rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator>(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs > rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator>(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs > *rhs : true;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator>=(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs >= rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator>=(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs >= *rhs : true;
+ }
+
+ /// \synopsis template <class T> \n void swap(optional<T> &lhs, optional<T> &rhs);
+ template <class T, detail::enable_if_t<std::is_move_constructible<T>::value>* = nullptr, detail::enable_if_t<detail::is_swappable<T>::value>* = nullptr>
+ void swap(optional<T>& lhs, optional<T>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
+ return lhs.swap(rhs);
+ }
+
+ namespace detail {
+ struct i_am_secret { };
+ } // namespace detail
+
+ template <class T = detail::i_am_secret, class U, class Ret = detail::conditional_t<std::is_same<T, detail::i_am_secret>::value, detail::decay_t<U>, T>>
+ inline constexpr optional<Ret> make_optional(U&& v) {
+ return optional<Ret>(std::forward<U>(v));
+ }
+
+ template <class T, class... Args>
+ inline constexpr optional<T> make_optional(Args&&... args) {
+ return optional<T>(in_place, std::forward<Args>(args)...);
+ }
+ template <class T, class U, class... Args>
+ inline constexpr optional<T> make_optional(std::initializer_list<U> il, Args&&... args) {
+ return optional<T>(in_place, il, std::forward<Args>(args)...);
+ }
+
+#if __cplusplus >= 201703L
+ template <class T>
+ optional(T) -> optional<T>;
+#endif
+
+ /// \exclude
+ namespace detail {
+#ifdef SOL_TL_OPTIONAL_CXX14
+ template <class Opt, class F, class Ret = decltype(detail::invoke(std::declval<F>(), *std::declval<Opt>())),
+ detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
+ constexpr auto optional_map_impl(Opt&& opt, F&& f) {
+ return opt.has_value() ? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt)) : optional<Ret>(nullopt);
+ }
+
+ template <class Opt, class F, class Ret = decltype(detail::invoke(std::declval<F>(), *std::declval<Opt>())),
+ detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
+ auto optional_map_impl(Opt&& opt, F&& f) {
+ if (opt.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt));
+ return make_optional(monostate {});
+ }
+
+ return optional<monostate>(nullopt);
+ }
+#else
+ template <class Opt, class F, class Ret = decltype(detail::invoke(std::declval<F>(), *std::declval<Opt>())),
+ detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
+
+ constexpr auto optional_map_impl(Opt&& opt, F&& f) -> optional<Ret> {
+ return opt.has_value() ? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt)) : optional<Ret>(nullopt);
+ }
+
+ template <class Opt, class F, class Ret = decltype(detail::invoke(std::declval<F>(), *std::declval<Opt>())),
+ detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
+
+ auto optional_map_impl(Opt&& opt, F&& f) -> optional<monostate> {
+ if (opt.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt));
+ return monostate {};
+ }
+
+ return nullopt;
+ }
+#endif
+ } // namespace detail
+
+ /// Specialization for when `T` is a reference. `optional<T&>` acts similarly
+ /// to a `T*`, but provides more operations and shows intent more clearly.
+ ///
+ /// *Examples*:
+ ///
+ /// ```
+ /// int i = 42;
+ /// sol::optional<int&> o = i;
+ /// *o == 42; //true
+ /// i = 12;
+ /// *o = 12; //true
+ /// &*o == &i; //true
+ /// ```
+ ///
+ /// Assignment has rebind semantics rather than assign-through semantics:
+ ///
+ /// ```
+ /// int j = 8;
+ /// o = j;
+ ///
+ /// &*o == &j; //true
+ /// ```
+ template <class T>
+ class optional<T&> {
+ public:
+#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55)
+ /// \group and_then
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one. \requires `std::invoke(std::forward<F>(f),
+ /// value())` returns a `std::optional<U>` for some `U`. \returns Let `U` be
+ /// the result of `std::invoke(std::forward<F>(f), value())`. Returns a
+ /// `std::optional<U>`. The return value is empty if `*this` is empty,
+ /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
+ /// is returned.
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) & {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) && {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &;
+ template <class F>
+ constexpr auto and_then(F&& f) const& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &&;
+ template <class F>
+ constexpr auto and_then(F&& f) const&& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+#endif
+#else
+ /// \group and_then
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one. \requires `std::invoke(std::forward<F>(f),
+ /// value())` returns a `std::optional<U>` for some `U`. \returns Let `U` be
+ /// the result of `std::invoke(std::forward<F>(f), value())`. Returns a
+ /// `std::optional<U>`. The return value is empty if `*this` is empty,
+ /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
+ /// is returned.
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&> and_then(F&& f) & {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&> and_then(F&& f) && {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &;
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T&> and_then(F&& f) const& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &&;
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T&> and_then(F&& f) const&& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+#endif
+#endif
+
+#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55)
+ /// \brief Carries out some operation on the stored object if there is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. Returns a `std::optional<U>`. The return value is empty if
+ /// `*this` is empty, otherwise an `optional<U>` is constructed from the
+ /// return value of `std::invoke(std::forward<F>(f), value())` and is
+ /// returned.
+ ///
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const&;
+ template <class F>
+ constexpr auto map(F&& f) const& {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const&&;
+ template <class F>
+ constexpr auto map(F&& f) const&& {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ /// \brief Carries out some operation on the stored object if there is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. Returns a `std::optional<U>`. The return value is empty if
+ /// `*this` is empty, otherwise an `optional<U>` is constructed from the
+ /// return value of `std::invoke(std::forward<F>(f), value())` and is
+ /// returned.
+ ///
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional&>(), std::declval<F&&>())) map(F&& f) & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional&&>(), std::declval<F&&>())) map(F&& f) && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) const&;
+ template <class F>
+ constexpr decltype(detail::optional_map_impl(std::declval<const optional&>(), std::declval<F&&>())) map(F&& f) const& {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) const&&;
+ template <class F>
+ constexpr decltype(detail::optional_map_impl(std::declval<const optional&&>(), std::declval<F&&>())) map(F&& f) const&& {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+ /// \brief Calls `f` if the optional is empty
+ /// \requires `std::invoke_result_t<F>` must be void or convertible to
+ /// `optional<T>`. \effects If `*this` has a value, returns `*this`.
+ /// Otherwise, if `f` returns `void`, calls `std::forward<F>(f)` and returns
+ /// `std::nullopt`. Otherwise, returns `std::forward<F>(f)()`.
+ ///
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) &;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) &&;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) && {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) && {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) const &;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const& {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) const& {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \exclude
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const&& {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const&& {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+#endif
+
+ /// \brief Maps the stored value with `f` if there is one, otherwise returns
+ /// `u`.
+ ///
+ /// \details If there is a value stored, then `f` is called with `**this`
+ /// and the value is returned. Otherwise `u` is returned.
+ ///
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u);
+ }
+
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u);
+ }
+
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) const& {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) const&& {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u);
+ }
+#endif
+
+ /// \brief Maps the stored value with `f` if there is one, otherwise calls
+ /// `u` and returns the result.
+ ///
+ /// \details If there is a value stored, then `f` is
+ /// called with `**this` and the value is returned. Otherwise
+ /// `std::forward<U>(u)()` is returned.
+ ///
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u) &;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u)();
+ }
+
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// &&;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u)();
+ }
+
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// const &;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const& {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u)();
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// const &&;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const&& {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u)();
+ }
+#endif
+
+ /// \returns `u` if `*this` has a value, otherwise an empty optional.
+ template <class U>
+ constexpr optional<typename std::decay<U>::type> conjunction(U&& u) const {
+ using result = optional<detail::decay_t<U>>;
+ return has_value() ? result { u } : result { nullopt };
+ }
+
+ /// \returns `rhs` if `*this` is empty, otherwise the current value.
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) & {
+ return has_value() ? *this : rhs;
+ }
+
+ /// \group disjunction
+ constexpr optional disjunction(const optional& rhs) const& {
+ return has_value() ? *this : rhs;
+ }
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) && {
+ return has_value() ? std::move(*this) : rhs;
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group disjunction
+ constexpr optional disjunction(const optional& rhs) const&& {
+ return has_value() ? std::move(*this) : rhs;
+ }
+#endif
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) & {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ /// \group disjunction
+ constexpr optional disjunction(optional&& rhs) const& {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) && {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group disjunction
+ constexpr optional disjunction(optional&& rhs) const&& {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+#endif
+
+ /// Takes the value out of the optional, leaving it empty
+ /// \group take
+ optional take() & {
+ optional ret = *this;
+ reset();
+ return ret;
+ }
+
+ /// \group take
+ optional take() const& {
+ optional ret = *this;
+ reset();
+ return ret;
+ }
+
+ /// \group take
+ optional take() && {
+ optional ret = std::move(*this);
+ reset();
+ return ret;
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group take
+ optional take() const&& {
+ optional ret = std::move(*this);
+ reset();
+ return ret;
+ }
+#endif
+
+ using value_type = T&;
+
+ /// Constructs an optional that does not contain a value.
+ /// \group ctor_empty
+ constexpr optional() noexcept : m_value(nullptr) {
+ }
+
+ /// \group ctor_empty
+ constexpr optional(nullopt_t) noexcept : m_value(nullptr) {
+ }
+
+ /// Copy constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional(const optional& rhs) noexcept = default;
+
+ /// Move constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional(optional&& rhs) = default;
+
+ /// Constructs the stored value with `u`.
+ /// \synopsis template <class U=T> constexpr optional(U &&u);
+ template <class U = T, detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value>* = nullptr>
+ constexpr optional(U&& u) : m_value(std::addressof(u)) {
+ static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue");
+ }
+
+ /// \exclude
+ template <class U>
+ constexpr explicit optional(const optional<U>& rhs) : optional(*rhs) {
+ }
+
+ /// No-op
+ ~optional() = default;
+
+ /// Assignment to empty.
+ ///
+ /// Destroys the current value if there is one.
+ optional& operator=(nullopt_t) noexcept {
+ m_value = nullptr;
+ return *this;
+ }
+
+ /// Copy assignment.
+ ///
+ /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise
+ /// resets the stored value in `*this`.
+ optional& operator=(const optional& rhs) = default;
+
+ /// Rebinds this optional to `u`.
+ ///
+ /// \requires `U` must be an lvalue reference.
+ /// \synopsis optional &operator=(U &&u);
+ template <class U = T, detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value>* = nullptr>
+ optional& operator=(U&& u) {
+ static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue");
+ m_value = std::addressof(u);
+ return *this;
+ }
+
+ /// Converting copy assignment operator.
+ ///
+ /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise
+ /// resets the stored value in `*this`.
+ template <class U>
+ optional& operator=(const optional<U>& rhs) {
+ m_value = std::addressof(rhs.value());
+ return *this;
+ }
+
+ /// Constructs the value in-place, destroying the current one if there is
+ /// one.
+ ///
+ /// \group emplace
+ template <class... Args>
+ T& emplace(Args&&... args) noexcept {
+ static_assert(std::is_constructible<T, Args&&...>::value, "T must be constructible with Args");
+
+ *this = nullopt;
+ this->construct(std::forward<Args>(args)...);
+ }
+
+ /// Swaps this optional with the other.
+ ///
+ /// If neither optionals have a value, nothing happens.
+ /// If both have a value, the values are swapped.
+ /// If one has a value, it is moved to the other and the movee is left
+ /// valueless.
+ void swap(optional& rhs) noexcept {
+ std::swap(m_value, rhs.m_value);
+ }
+
+ /// \returns a pointer to the stored value
+ /// \requires a value is stored
+ /// \group pointer
+ /// \synopsis constexpr const T *operator->() const;
+ constexpr const T* operator->() const {
+ return m_value;
+ }
+
+ /// \group pointer
+ /// \synopsis constexpr T *operator->();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T* operator->() {
+ return m_value;
+ }
+
+ /// \returns the stored value
+ /// \requires a value is stored
+ /// \group deref
+ /// \synopsis constexpr T &operator*();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T& operator*() {
+ return *m_value;
+ }
+
+ /// \group deref
+ /// \synopsis constexpr const T &operator*() const;
+ constexpr const T& operator*() const {
+ return *m_value;
+ }
+
+ /// \returns whether or not the optional has a value
+ /// \group has_value
+ constexpr bool has_value() const noexcept {
+ return m_value != nullptr;
+ }
+
+ /// \group has_value
+ constexpr explicit operator bool() const noexcept {
+ return m_value != nullptr;
+ }
+
+ /// \returns the contained value if there is one, otherwise throws
+ /// [bad_optional_access]
+ /// \group value
+ /// synopsis constexpr T &value();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T& value() {
+ if (has_value())
+ return *m_value;
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+ /// \group value
+ /// \synopsis constexpr const T &value() const;
+ SOL_TL_OPTIONAL_11_CONSTEXPR const T& value() const {
+ if (has_value())
+ return *m_value;
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+
+ /// \returns the stored value if there is one, otherwise returns `u`
+ /// \group value_or
+ template <class U>
+ constexpr T& value_or(U&& u) const {
+ static_assert(std::is_convertible<U&&, T&>::value, "T must be convertible from U");
+ return has_value() ? const_cast<T&>(**this) : static_cast<T&>(std::forward<U>(u));
+ }
+
+ /// Destroys the stored value if one exists, making the optional empty
+ void reset() noexcept {
+ m_value = nullptr;
+ }
+
+ private:
+ T* m_value;
+ };
+
+} // namespace sol
+
+namespace std {
+ // TODO SFINAE
+ template <class T>
+ struct hash<::sol::optional<T>> {
+ ::std::size_t operator()(const ::sol::optional<T>& o) const {
+ if (!o.has_value())
+ return 0;
+
+ return ::std::hash<::sol::detail::remove_const_t<T>>()(*o);
+ }
+ };
+} // namespace std
+
+// end of sol/optional_implementation.hpp
+
+#endif // Boost vs. Better optional
+
+#include <optional>
+
+namespace sol {
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+ template <typename T>
+ using optional = boost::optional<T>;
+ using nullopt_t = boost::none_t;
+ SOL_BOOST_NONE_CONSTEXPR_I_ nullopt_t nullopt = boost::none;
+#endif // Boost vs. Better optional
+
+ namespace meta {
+ template <typename T>
+ using is_optional = any<is_specialization_of<T, optional>, is_specialization_of<T, std::optional>>;
+
+ template <typename T>
+ constexpr inline bool is_optional_v = is_optional<T>::value;
+ } // namespace meta
+
+ namespace detail {
+ template <typename T>
+ struct associated_nullopt {
+ inline static constexpr std::nullopt_t value = std::nullopt;
+ };
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+ template <typename T>
+ struct associated_nullopt<boost::optional<T>> {
+ inline static SOL_BOOST_NONE_CONSTEXPR_I_ boost::none_t value = boost::none;
+ };
+#endif // Boost nullopt
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+ template <typename T>
+ inline SOL_BOOST_NONE_CONSTEXPR_I_ auto associated_nullopt_v = associated_nullopt<T>::value;
+#else
+ template <typename T>
+ inline constexpr auto associated_nullopt_v = associated_nullopt<T>::value;
+#endif // Boost continues to lag behind, to not many people's surprise...
+ } // namespace detail
+} // namespace sol
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+#undef SOL_BOOST_NONE_CONSTEXPR_I_
+#endif
+
+// end of sol/optional.hpp
+
+// beginning of sol/raii.hpp
+
+#include <memory>
+
+namespace sol {
+ namespace detail {
+ struct default_construct {
+ template <typename T, typename... Args>
+ static void construct(T&& obj, Args&&... args) {
+ typedef meta::unqualified_t<T> Tu;
+ std::allocator<Tu> alloc {};
+ std::allocator_traits<std::allocator<Tu>>::construct(alloc, std::forward<T>(obj), std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename... Args>
+ void operator()(T&& obj, Args&&... args) const {
+ construct(std::forward<T>(obj), std::forward<Args>(args)...);
+ }
+ };
+
+ struct default_destroy {
+ template <typename T>
+ static void destroy(T&& obj) {
+ std::allocator<meta::unqualified_t<T>> alloc {};
+ alloc.destroy(obj);
+ }
+
+ template <typename T>
+ void operator()(T&& obj) const {
+ destroy(std::forward<T>(obj));
+ }
+ };
+
+ struct deleter {
+ template <typename T>
+ void operator()(T* p) const {
+ delete p;
+ }
+ };
+
+ struct state_deleter {
+ void operator()(lua_State* L) const {
+ lua_close(L);
+ }
+ };
+
+ template <typename T, typename Dx, typename... Args>
+ inline std::unique_ptr<T, Dx> make_unique_deleter(Args&&... args) {
+ return std::unique_ptr<T, Dx>(new T(std::forward<Args>(args)...));
+ }
+
+ template <typename Tag, typename T>
+ struct tagged {
+ private:
+ T value_;
+
+ public:
+ template <typename Arg, typename... Args, meta::disable<std::is_same<meta::unqualified_t<Arg>, tagged>> = meta::enabler>
+ tagged(Arg&& arg, Args&&... args) : value_(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ T& value() & {
+ return value_;
+ }
+
+ T const& value() const& {
+ return value_;
+ }
+
+ T&& value() && {
+ return std::move(value_);
+ }
+ };
+ } // namespace detail
+
+ template <typename... Args>
+ struct constructor_list { };
+
+ template <typename... Args>
+ using constructors = constructor_list<Args...>;
+
+ const auto default_constructor = constructors<types<>> {};
+
+ struct no_construction { };
+ const auto no_constructor = no_construction {};
+
+ struct call_construction { };
+ const auto call_constructor = call_construction {};
+
+ template <typename... Functions>
+ struct constructor_wrapper {
+ std::tuple<Functions...> functions;
+ template <typename Arg, typename... Args, meta::disable<std::is_same<meta::unqualified_t<Arg>, constructor_wrapper>> = meta::enabler>
+ constructor_wrapper(Arg&& arg, Args&&... args) : functions(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+ };
+
+ template <typename... Functions>
+ inline auto initializers(Functions&&... functions) {
+ return constructor_wrapper<std::decay_t<Functions>...>(std::forward<Functions>(functions)...);
+ }
+
+ template <typename... Functions>
+ struct factory_wrapper {
+ std::tuple<Functions...> functions;
+ template <typename Arg, typename... Args, meta::disable<std::is_same<meta::unqualified_t<Arg>, factory_wrapper>> = meta::enabler>
+ factory_wrapper(Arg&& arg, Args&&... args) : functions(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+ };
+
+ template <typename... Functions>
+ inline auto factories(Functions&&... functions) {
+ return factory_wrapper<std::decay_t<Functions>...>(std::forward<Functions>(functions)...);
+ }
+
+ template <typename Function>
+ struct destructor_wrapper {
+ Function fx;
+ destructor_wrapper(Function f) : fx(std::move(f)) {
+ }
+ };
+
+ template <>
+ struct destructor_wrapper<void> { };
+
+ const destructor_wrapper<void> default_destructor {};
+
+ template <typename Fx>
+ inline auto destructor(Fx&& fx) {
+ return destructor_wrapper<std::decay_t<Fx>>(std::forward<Fx>(fx));
+ }
+
+} // namespace sol
+
+// end of sol/raii.hpp
+
+// beginning of sol/policies.hpp
+
+#include <array>
+
+namespace sol {
+ namespace detail {
+ struct policy_base_tag { };
+ } // namespace detail
+
+ template <int Target, int... In>
+ struct static_stack_dependencies : detail::policy_base_tag { };
+ typedef static_stack_dependencies<-1, 1> self_dependency;
+ template <int... In>
+ struct returns_self_with : detail::policy_base_tag { };
+ typedef returns_self_with<> returns_self;
+
+ struct stack_dependencies : detail::policy_base_tag {
+ int target;
+ std::array<int, 64> stack_indices;
+ std::size_t len;
+
+ template <typename... Args>
+ stack_dependencies(int stack_target, Args&&... args) : target(stack_target), stack_indices(), len(sizeof...(Args)) {
+ std::size_t i = 0;
+ (void)detail::swallow { int(), (stack_indices[i++] = static_cast<int>(std::forward<Args>(args)), int())... };
+ }
+
+ int& operator[](std::size_t i) {
+ return stack_indices[i];
+ }
+
+ const int& operator[](std::size_t i) const {
+ return stack_indices[i];
+ }
+
+ std::size_t size() const {
+ return len;
+ }
+ };
+
+ template <typename F, typename... Policies>
+ struct policy_wrapper {
+ typedef std::index_sequence_for<Policies...> indices;
+
+ F value;
+ std::tuple<Policies...> policies;
+
+ template <typename Fx, typename... Args, meta::enable<meta::neg<std::is_same<meta::unqualified_t<Fx>, policy_wrapper>>> = meta::enabler>
+ policy_wrapper(Fx&& fx, Args&&... args) : value(std::forward<Fx>(fx)), policies(std::forward<Args>(args)...) {
+ }
+
+ policy_wrapper(const policy_wrapper&) = default;
+ policy_wrapper& operator=(const policy_wrapper&) = default;
+ policy_wrapper(policy_wrapper&&) = default;
+ policy_wrapper& operator=(policy_wrapper&&) = default;
+ };
+
+ template <typename F, typename... Args>
+ auto policies(F&& f, Args&&... args) {
+ return policy_wrapper<std::decay_t<F>, std::decay_t<Args>...>(std::forward<F>(f), std::forward<Args>(args)...);
+ }
+
+ namespace detail {
+ template <typename T>
+ using is_policy = meta::is_specialization_of<T, policy_wrapper>;
+
+ template <typename T>
+ inline constexpr bool is_policy_v = is_policy<T>::value;
+ } // namespace detail
+} // namespace sol
+
+// end of sol/policies.hpp
+
+// beginning of sol/ebco.hpp
+
+#include <type_traits>
+#include <utility>
+#include <memory>
+
+namespace sol { namespace detail {
+
+ template <typename T, std::size_t tag = 0, typename = void>
+ struct ebco {
+ T m_value;
+
+ ebco() = default;
+ ebco(const ebco&) = default;
+ ebco(ebco&&) = default;
+ ebco& operator=(const ebco&) = default;
+ ebco& operator=(ebco&&) = default;
+ ebco(const T& v) noexcept(std::is_nothrow_copy_constructible_v<T>) : m_value(v) {};
+ ebco(T&& v) noexcept(std::is_nothrow_move_constructible_v<T>) : m_value(std::move(v)) {};
+ ebco& operator=(const T& v) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ m_value = v;
+ return *this;
+ }
+ ebco& operator=(T&& v) noexcept(std::is_nothrow_move_assignable_v<T>) {
+ m_value = std::move(v);
+ return *this;
+ };
+ template <typename Arg, typename... Args,
+ typename = std::enable_if_t<
+ !std::is_same_v<std::remove_reference_t<std::remove_cv_t<Arg>>,
+ ebco> && !std::is_same_v<std::remove_reference_t<std::remove_cv_t<Arg>>, T> && (sizeof...(Args) > 0 || !std::is_convertible_v<Arg, T>)>>
+ ebco(Arg&& arg, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Arg, Args...>)
+ : m_value(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ T& value() & noexcept {
+ return m_value;
+ }
+
+ T const& value() const& noexcept {
+ return m_value;
+ }
+
+ T&& value() && noexcept {
+ return std::move(m_value);
+ }
+ };
+
+ template <typename T, std::size_t tag>
+ struct ebco<T, tag, std::enable_if_t<!std::is_reference_v<T> && std::is_class_v<T> && !std::is_final_v<T>>> : T {
+ ebco() = default;
+ ebco(const ebco&) = default;
+ ebco(ebco&&) = default;
+ ebco(const T& v) noexcept(std::is_nothrow_copy_constructible_v<T>) : T(v) {};
+ ebco(T&& v) noexcept(std::is_nothrow_move_constructible_v<T>) : T(std::move(v)) {};
+ template <typename Arg, typename... Args,
+ typename = std::enable_if_t<
+ !std::is_same_v<std::remove_reference_t<std::remove_cv_t<Arg>>,
+ ebco> && !std::is_same_v<std::remove_reference_t<std::remove_cv_t<Arg>>, T> && (sizeof...(Args) > 0 || !std::is_convertible_v<Arg, T>)>>
+ ebco(Arg&& arg, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Arg, Args...>) : T(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ ebco& operator=(const ebco&) = default;
+ ebco& operator=(ebco&&) = default;
+ ebco& operator=(const T& v) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ static_cast<T&>(*this) = v;
+ return *this;
+ }
+ ebco& operator=(T&& v) noexcept(std::is_nothrow_move_assignable_v<T>) {
+ static_cast<T&>(*this) = std::move(v);
+ return *this;
+ };
+
+ T& value() & noexcept {
+ return static_cast<T&>(*this);
+ }
+
+ T const& value() const& noexcept {
+ return static_cast<T const&>(*this);
+ }
+
+ T&& value() && noexcept {
+ return std::move(static_cast<T&>(*this));
+ }
+ };
+
+ template <typename T, std::size_t tag>
+ struct ebco<T&, tag> {
+ private:
+ T* m_ref;
+
+ public:
+ ebco() = default;
+ ebco(const ebco&) = default;
+ ebco(ebco&&) = default;
+ ebco(T& v) noexcept : m_ref(std::addressof(v)) {};
+
+ ebco& operator=(const ebco&) = default;
+ ebco& operator=(ebco&&) = default;
+ ebco& operator=(T& v) noexcept {
+ m_ref = std::addressof(v);
+ return *this;
+ }
+
+ T& value() const noexcept {
+ return *(const_cast<ebco<T&, tag>&>(*this).m_ref);
+ }
+ };
+
+ template <typename T, std::size_t tag>
+ struct ebco<T&&, tag> {
+ T&& ref;
+
+ ebco() = default;
+ ebco(const ebco&) = delete;
+ ebco(ebco&&) = default;
+ ebco(T&& v) noexcept : ref(v) {};
+
+ ebco& operator=(const ebco&) = delete;
+ ebco& operator=(ebco&&) = delete;
+
+ T& value() & noexcept {
+ return ref;
+ }
+
+ const T& value() const& noexcept {
+ return ref;
+ }
+
+ T&& value() && noexcept {
+ return std::move(ref);
+ }
+ };
+
+}} // namespace sol::detail
+
+// end of sol/ebco.hpp
+
+#include <array>
+#include <initializer_list>
+#include <string>
+#include <string_view>
+#include <limits>
+#include <optional>
+#include <memory>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // variant shenanigans (thanks, Mac OSX)
+
+namespace sol {
+ namespace d {
+ // shortest possible hidden detail namespace
+ // when types are transcribed, this saves
+ // quite a bit of space, actually.
+ // it's a little unfortunate, but here we are?
+ template <typename T>
+ struct u { };
+ } // namespace d
+
+ namespace detail {
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ typedef int (*lua_CFunction_noexcept)(lua_State* L) noexcept;
+#else
+ typedef int (*lua_CFunction_noexcept)(lua_State* L);
+#endif // noexcept function type for lua_CFunction
+
+ template <typename T>
+ struct implicit_wrapper {
+ T& value;
+
+ implicit_wrapper(T* value_) : value(*value_) {
+ }
+
+ implicit_wrapper(T& value_) : value(value_) {
+ }
+
+ operator T&() {
+ return value;
+ }
+
+ operator T*() {
+ return std::addressof(value);
+ }
+ };
+
+ struct yield_tag_t { };
+ inline constexpr yield_tag_t yield_tag {};
+ } // namespace detail
+
+ struct lua_nil_t { };
+ inline constexpr lua_nil_t lua_nil {};
+ inline bool operator==(lua_nil_t, lua_nil_t) {
+ return true;
+ }
+ inline bool operator!=(lua_nil_t, lua_nil_t) {
+ return false;
+ }
+#if SOL_IS_ON(SOL_NIL)
+ using nil_t = lua_nil_t;
+ inline constexpr const nil_t& nil = lua_nil;
+#endif
+
+ namespace detail {
+ struct non_lua_nil_t { };
+ } // namespace detail
+
+ struct metatable_key_t { };
+ inline constexpr metatable_key_t metatable_key {};
+
+ struct global_tag_t {
+ } inline constexpr global_tag {};
+
+ struct env_key_t { };
+ inline constexpr env_key_t env_key {};
+
+ struct no_metatable_t { };
+ inline constexpr no_metatable_t no_metatable {};
+
+ template <typename T>
+ struct yielding_t {
+ T func;
+
+ yielding_t() = default;
+ yielding_t(const yielding_t&) = default;
+ yielding_t(yielding_t&&) = default;
+ yielding_t& operator=(const yielding_t&) = default;
+ yielding_t& operator=(yielding_t&&) = default;
+ template <typename Arg,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<Arg>, yielding_t>>,
+ meta::neg<std::is_base_of<proxy_base_tag, meta::unqualified_t<Arg>>>> = meta::enabler>
+ yielding_t(Arg&& arg) : func(std::forward<Arg>(arg)) {
+ }
+ template <typename Arg0, typename Arg1, typename... Args>
+ yielding_t(Arg0&& arg0, Arg1&& arg1, Args&&... args) : func(std::forward<Arg0>(arg0), std::forward<Arg1>(arg1), std::forward<Args>(args)...) {
+ }
+ };
+
+ template <typename F>
+ inline yielding_t<std::decay_t<F>> yielding(F&& f) {
+ return yielding_t<std::decay_t<F>>(std::forward<F>(f));
+ }
+
+ typedef std::remove_pointer_t<lua_CFunction> lua_CFunction_ref;
+
+ template <typename T>
+ struct non_null { };
+
+ template <typename... Args>
+ struct function_sig { };
+
+ struct upvalue_index {
+ int index;
+ upvalue_index(int idx) : index(lua_upvalueindex(idx)) {
+ }
+
+ operator int() const {
+ return index;
+ }
+ };
+
+ struct raw_index {
+ int index;
+ raw_index(int i) : index(i) {
+ }
+
+ operator int() const {
+ return index;
+ }
+ };
+
+ struct absolute_index {
+ int index;
+ absolute_index(lua_State* L, int idx) : index(lua_absindex(L, idx)) {
+ }
+
+ operator int() const {
+ return index;
+ }
+ };
+
+ struct ref_index {
+ int index;
+ ref_index(int idx) : index(idx) {
+ }
+
+ operator int() const {
+ return index;
+ }
+ };
+
+ struct stack_count {
+ int count;
+
+ stack_count(int cnt) : count(cnt) {
+ }
+ };
+
+ struct lightuserdata_value {
+ void* value;
+ lightuserdata_value(void* data) : value(data) {
+ }
+ operator void*() const {
+ return value;
+ }
+ };
+
+ struct userdata_value {
+ private:
+ void* m_value;
+
+ public:
+ userdata_value(void* data) : m_value(data) {
+ }
+
+ void* value() const {
+ return m_value;
+ }
+
+ operator void*() const {
+ return value();
+ }
+ };
+
+ template <typename T>
+ struct light {
+ private:
+ static_assert(!std::is_void_v<T>, "the type for light will never be void");
+ T* m_value;
+
+ public:
+ light(T& x) : m_value(std::addressof(x)) {
+ }
+ light(T* x) : m_value(x) {
+ }
+ explicit light(void* x) : m_value(static_cast<T*>(x)) {
+ }
+
+ T* value() const {
+ return m_value;
+ }
+
+ operator T*() const {
+ return m_value;
+ }
+ operator T&() const {
+ return *m_value;
+ }
+
+ void* void_value() const {
+ return m_value;
+ }
+ };
+
+ template <typename T>
+ auto make_light(T& l) {
+ typedef meta::unwrapped_t<std::remove_pointer_t<std::remove_pointer_t<T>>> L;
+ return light<L>(l);
+ }
+
+ template <typename T>
+ struct user : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+
+ using base_t::value;
+
+ operator std::add_pointer_t<std::remove_reference_t<T>>() {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_pointer_t<std::add_const_t<std::remove_reference_t<T>>>() const {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_lvalue_reference_t<T>() {
+ return this->base_t::value();
+ }
+
+ operator std::add_const_t<std::add_lvalue_reference_t<T>>&() const {
+ return this->base_t::value();
+ }
+ };
+
+ template <typename T>
+ auto make_user(T&& u) {
+ typedef meta::unwrapped_t<meta::unqualified_t<T>> U;
+ return user<U>(std::forward<T>(u));
+ }
+
+ template <typename T>
+ struct metatable_registry_key : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+
+ using base_t::value;
+ };
+
+ template <typename T>
+ auto meta_registry_key(T&& key) {
+ typedef meta::unqualified_t<T> K;
+ return metatable_registry_key<K>(std::forward<T>(key));
+ }
+
+ template <typename... Upvalues>
+ struct closure {
+ lua_CFunction c_function;
+ std::tuple<Upvalues...> upvalues;
+ closure(lua_CFunction f, Upvalues... targetupvalues) : c_function(f), upvalues(std::forward<Upvalues>(targetupvalues)...) {
+ }
+ };
+
+ template <>
+ struct closure<> {
+ lua_CFunction c_function;
+ int upvalues;
+ closure(lua_CFunction f, int upvalue_count = 0) : c_function(f), upvalues(upvalue_count) {
+ }
+ };
+
+ typedef closure<> c_closure;
+
+ template <typename... Args>
+ closure<Args...> make_closure(lua_CFunction f, Args&&... args) {
+ return closure<Args...>(f, std::forward<Args>(args)...);
+ }
+
+ template <typename Sig, typename... Ps>
+ struct function_arguments {
+ std::tuple<Ps...> arguments;
+ template <typename Arg, typename... Args, meta::disable<std::is_same<meta::unqualified_t<Arg>, function_arguments>> = meta::enabler>
+ function_arguments(Arg&& arg, Args&&... args) : arguments(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+ };
+
+ template <typename Sig = function_sig<>, typename... Args>
+ auto as_function(Args&&... args) {
+ return function_arguments<Sig, std::decay_t<Args>...>(std::forward<Args>(args)...);
+ }
+
+ template <typename Sig = function_sig<>, typename... Args>
+ auto as_function_reference(Args&&... args) {
+ return function_arguments<Sig, Args...>(std::forward<Args>(args)...);
+ }
+
+ template <typename T>
+ struct as_table_t : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ as_table_t() = default;
+ as_table_t(const as_table_t&) = default;
+ as_table_t(as_table_t&&) = default;
+ as_table_t& operator=(const as_table_t&) = default;
+ as_table_t& operator=(as_table_t&&) = default;
+ as_table_t(const meta::unqualified_t<T>& obj) noexcept(std::is_nothrow_constructible_v<base_t, const meta::unqualified_t<T>&>) : base_t(obj) {
+ }
+ as_table_t(meta::unqualified_t<T>&& obj) noexcept(std::is_nothrow_constructible_v<base_t, meta::unqualified_t<T>&&>) : base_t(std::move(obj)) {
+ }
+ template <typename Arg, typename... Args,
+ std::enable_if_t<
+ !std::is_same_v<as_table_t, meta::unqualified_t<Arg>> && !std::is_same_v<meta::unqualified_t<T>, meta::unqualified_t<Arg>>>* = nullptr>
+ as_table_t(Arg&& arg, Args&&... args) noexcept(std::is_nothrow_constructible_v<base_t, Arg, Args...>)
+ : base_t(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ using base_t::value;
+
+ operator std::add_lvalue_reference_t<T>() {
+ return this->base_t::value();
+ }
+
+ operator std::add_const_t<std::add_lvalue_reference_t<T>>() const {
+ return this->base_t::value();
+ }
+ };
+
+ template <typename T>
+ struct nested : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using nested_type = T;
+
+ nested() = default;
+ nested(const nested&) = default;
+ nested(nested&&) = default;
+ nested& operator=(const nested&) = default;
+ nested& operator=(nested&&) = default;
+ nested(const meta::unqualified_t<T>& obj) noexcept(std::is_nothrow_constructible_v<base_t, const meta::unqualified_t<T>&>) : base_t(obj) {
+ }
+ nested(meta::unqualified_t<T>&& obj) noexcept(std::is_nothrow_constructible_v<base_t, meta::unqualified_t<T>&&>) : base_t(std::move(obj)) {
+ }
+ template <typename Arg, typename... Args,
+ std::enable_if_t<
+ !std::is_same_v<nested, meta::unqualified_t<Arg>> && !std::is_same_v<meta::unqualified_t<T>, meta::unqualified_t<Arg>>>* = nullptr>
+ nested(Arg&& arg, Args&&... args) noexcept(std::is_nothrow_constructible_v<base_t, Arg, Args...>)
+ : base_t(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ using base_t::value;
+
+ operator std::add_lvalue_reference_t<T>() {
+ return this->base_t::value();
+ }
+
+ operator std::add_const_t<std::add_lvalue_reference_t<T>>() const {
+ return this->base_t::value();
+ }
+ };
+
+ struct nested_tag_t { };
+ constexpr inline nested_tag_t nested_tag {};
+
+ template <typename T>
+ as_table_t<T> as_table_ref(T&& container) {
+ return as_table_t<T>(std::forward<T>(container));
+ }
+
+ template <typename T>
+ as_table_t<meta::unqualified_t<T>> as_table(T&& container) {
+ return as_table_t<meta::unqualified_t<T>>(std::forward<T>(container));
+ }
+
+ template <typename T>
+ nested<T> as_nested_ref(T&& container) {
+ return nested<T>(std::forward<T>(container));
+ }
+
+ template <typename T>
+ nested<meta::unqualified_t<T>> as_nested(T&& container) {
+ return nested<meta::unqualified_t<T>>(std::forward<T>(container));
+ }
+
+ template <typename T>
+ struct as_container_t : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using type = T;
+
+ as_container_t() = default;
+ as_container_t(const as_container_t&) = default;
+ as_container_t(as_container_t&&) = default;
+ as_container_t& operator=(const as_container_t&) = default;
+ as_container_t& operator=(as_container_t&&) = default;
+
+ using base_t::base_t;
+
+ using base_t::value;
+
+ operator std::add_lvalue_reference_t<T>() {
+ return value();
+ }
+ };
+
+ template <typename T>
+ auto as_container(T&& value) {
+ return as_container_t<T>(std::forward<T>(value));
+ }
+
+ template <typename T, std::size_t Limit = 15>
+ struct exhaustive_until : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+
+ using base_t::value;
+
+ operator std::add_pointer_t<std::remove_reference_t<T>>() {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_pointer_t<std::add_const_t<std::remove_reference_t<T>>>() const {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_lvalue_reference_t<T>() {
+ return this->base_t::value();
+ }
+
+ operator std::add_const_t<std::add_lvalue_reference_t<T>>&() const {
+ return this->base_t::value();
+ }
+ };
+
+ template <typename T>
+ using exhaustive = exhaustive_until<T, (std::numeric_limits<size_t>::max)()>;
+
+ template <typename T>
+ struct non_exhaustive : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+
+ using base_t::value;
+
+ operator std::add_pointer_t<std::remove_reference_t<T>>() {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_pointer_t<std::add_const_t<std::remove_reference_t<T>>>() const {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_lvalue_reference_t<T>() {
+ return this->base_t::value();
+ }
+
+ operator std::add_const_t<std::add_lvalue_reference_t<T>>&() const {
+ return this->base_t::value();
+ }
+ };
+
+ template <typename T>
+ struct push_invoke_t : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ push_invoke_t() = default;
+ push_invoke_t(const push_invoke_t&) = default;
+ push_invoke_t(push_invoke_t&&) = default;
+ push_invoke_t& operator=(const push_invoke_t&) = default;
+ push_invoke_t& operator=(push_invoke_t&&) = default;
+
+ using base_t::base_t;
+
+ using base_t::value;
+ };
+
+ template <typename Fx>
+ auto push_invoke(Fx&& fx) {
+ return push_invoke_t<Fx>(std::forward<Fx>(fx));
+ }
+
+ template <typename T>
+ struct forward_as_value_t : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ forward_as_value_t() = default;
+ forward_as_value_t(const forward_as_value_t&) = default;
+ forward_as_value_t(forward_as_value_t&&) = default;
+ forward_as_value_t& operator=(const forward_as_value_t&) = default;
+ forward_as_value_t& operator=(forward_as_value_t&&) = default;
+
+ using base_t::base_t;
+
+ using base_t::value;
+ };
+
+ template <typename T>
+ auto pass_as_value(T& value_ref_) {
+ return forward_as_value_t<T>(value_ref_);
+ }
+
+ struct override_value_t { };
+ constexpr inline override_value_t override_value = override_value_t();
+ struct update_if_empty_t { };
+ constexpr inline update_if_empty_t update_if_empty = update_if_empty_t();
+ struct create_if_nil_t { };
+ constexpr inline create_if_nil_t create_if_nil = create_if_nil_t();
+
+ namespace detail {
+ enum insert_mode { none = 0x0, update_if_empty = 0x01, override_value = 0x02, create_if_nil = 0x04 };
+
+ template <typename T, typename...>
+ using is_insert_mode = std::integral_constant<bool,
+ std::is_same_v<T, override_value_t> || std::is_same_v<T, update_if_empty_t> || std::is_same_v<T, create_if_nil_t>>;
+
+ template <typename T, typename...>
+ using is_not_insert_mode = meta::neg<is_insert_mode<T>>;
+ } // namespace detail
+
+ struct this_state {
+ lua_State* L;
+
+ this_state(lua_State* Ls) : L(Ls) {
+ }
+
+ operator lua_State*() const noexcept {
+ return lua_state();
+ }
+
+ lua_State* operator->() const noexcept {
+ return lua_state();
+ }
+
+ lua_State* lua_state() const noexcept {
+ return L;
+ }
+ };
+
+ struct this_main_state {
+ lua_State* L;
+
+ this_main_state(lua_State* Ls) : L(Ls) {
+ }
+
+ operator lua_State*() const noexcept {
+ return lua_state();
+ }
+
+ lua_State* operator->() const noexcept {
+ return lua_state();
+ }
+
+ lua_State* lua_state() const noexcept {
+ return L;
+ }
+ };
+
+ struct new_table {
+ int sequence_hint = 0;
+ int map_hint = 0;
+
+ new_table() = default;
+ new_table(const new_table&) = default;
+ new_table(new_table&&) = default;
+ new_table& operator=(const new_table&) = default;
+ new_table& operator=(new_table&&) = default;
+
+ new_table(int sequence_hint_, int map_hint_ = 0) noexcept : sequence_hint(sequence_hint_), map_hint(map_hint_) {
+ }
+ };
+
+ const new_table create = {};
+
+ enum class lib : unsigned char {
+ // print, assert, and other base functions
+ base,
+ // require and other package functions
+ package,
+ // coroutine functions and utilities
+ coroutine,
+ // string library
+ string,
+ // functionality from the OS
+ os,
+ // all things math
+ math,
+ // the table manipulator and observer functions
+ table,
+ // the debug library
+ debug,
+ // the bit library: different based on which you're using
+ bit32,
+ // input/output library
+ io,
+ // LuaJIT only
+ ffi,
+ // LuaJIT only
+ jit,
+ // library for handling utf8: new to Lua
+ utf8,
+ // do not use
+ count
+ };
+
+ enum class call_syntax { dot = 0, colon = 1 };
+
+ enum class load_mode {
+ any = 0,
+ text = 1,
+ binary = 2,
+ };
+
+ enum class call_status : int {
+ ok = LUA_OK,
+ yielded = LUA_YIELD,
+ runtime = LUA_ERRRUN,
+ memory = LUA_ERRMEM,
+ handler = LUA_ERRERR,
+ gc = LUA_ERRGCMM,
+ syntax = LUA_ERRSYNTAX,
+ file = LUA_ERRFILE,
+ };
+
+ enum class thread_status : int {
+ ok = LUA_OK,
+ yielded = LUA_YIELD,
+ runtime = LUA_ERRRUN,
+ memory = LUA_ERRMEM,
+ gc = LUA_ERRGCMM,
+ handler = LUA_ERRERR,
+ dead = -1,
+ };
+
+ enum class load_status : int {
+ ok = LUA_OK,
+ syntax = LUA_ERRSYNTAX,
+ memory = LUA_ERRMEM,
+ gc = LUA_ERRGCMM,
+ file = LUA_ERRFILE,
+ };
+
+ enum class gc_mode : int {
+ incremental = 0,
+ generational = 1,
+ default_value = incremental,
+ };
+
+ enum class type : int {
+ none = LUA_TNONE,
+ lua_nil = LUA_TNIL,
+#if SOL_IS_ON(SOL_NIL)
+ nil = lua_nil,
+#endif // Objective C/C++ Keyword that's found in OSX SDK and OBJC -- check for all forms to protect
+ string = LUA_TSTRING,
+ number = LUA_TNUMBER,
+ thread = LUA_TTHREAD,
+ boolean = LUA_TBOOLEAN,
+ function = LUA_TFUNCTION,
+ userdata = LUA_TUSERDATA,
+ lightuserdata = LUA_TLIGHTUSERDATA,
+ table = LUA_TTABLE,
+ poly = -0xFFFF
+ };
+
+ inline const std::string& to_string(call_status c) {
+ static const std::array<std::string, 10> names { { "ok",
+ "yielded",
+ "runtime",
+ "memory",
+ "handler",
+ "gc",
+ "syntax",
+ "file",
+ "CRITICAL_EXCEPTION_FAILURE",
+ "CRITICAL_INDETERMINATE_STATE_FAILURE" } };
+ switch (c) {
+ case call_status::ok:
+ return names[0];
+ case call_status::yielded:
+ return names[1];
+ case call_status::runtime:
+ return names[2];
+ case call_status::memory:
+ return names[3];
+ case call_status::handler:
+ return names[4];
+ case call_status::gc:
+ return names[5];
+ case call_status::syntax:
+ return names[6];
+ case call_status::file:
+ return names[7];
+ }
+ if (static_cast<std::ptrdiff_t>(c) == -1) {
+ // One of the many cases where a critical exception error has occurred
+ return names[8];
+ }
+ return names[9];
+ }
+
+ inline bool is_indeterminate_call_failure(call_status c) {
+ switch (c) {
+ case call_status::ok:
+ case call_status::yielded:
+ case call_status::runtime:
+ case call_status::memory:
+ case call_status::handler:
+ case call_status::gc:
+ case call_status::syntax:
+ case call_status::file:
+ return false;
+ }
+ return true;
+ }
+
+ inline const std::string& to_string(load_status c) {
+ static const std::array<std::string, 7> names {
+ { "ok", "memory", "gc", "syntax", "file", "CRITICAL_EXCEPTION_FAILURE", "CRITICAL_INDETERMINATE_STATE_FAILURE" }
+ };
+ switch (c) {
+ case load_status::ok:
+ return names[0];
+ case load_status::memory:
+ return names[1];
+ case load_status::gc:
+ return names[2];
+ case load_status::syntax:
+ return names[3];
+ case load_status::file:
+ return names[4];
+ }
+ if (static_cast<int>(c) == -1) {
+ // One of the many cases where a critical exception error has occurred
+ return names[5];
+ }
+ return names[6];
+ }
+
+ inline const std::string& to_string(load_mode c) {
+ static const std::array<std::string, 3> names { {
+ "bt",
+ "t",
+ "b",
+ } };
+ return names[static_cast<std::size_t>(c)];
+ }
+
+ enum class meta_function : unsigned {
+ construct,
+ index,
+ new_index,
+ mode,
+ call,
+ call_function = call,
+ metatable,
+ to_string,
+ length,
+ unary_minus,
+ addition,
+ subtraction,
+ multiplication,
+ division,
+ modulus,
+ power_of,
+ involution = power_of,
+ concatenation,
+ equal_to,
+ less_than,
+ less_than_or_equal_to,
+ garbage_collect,
+ floor_division,
+ bitwise_left_shift,
+ bitwise_right_shift,
+ bitwise_not,
+ bitwise_and,
+ bitwise_or,
+ bitwise_xor,
+ pairs,
+ ipairs,
+ next,
+ type,
+ type_info,
+ call_construct,
+ storage,
+ gc_names,
+ static_index,
+ static_new_index,
+ };
+
+ typedef meta_function meta_method;
+
+ inline const std::array<std::string, 37>& meta_function_names() {
+ static const std::array<std::string, 37> names = { { "new",
+ "__index",
+ "__newindex",
+ "__mode",
+ "__call",
+ "__metatable",
+ "__tostring",
+ "__len",
+ "__unm",
+ "__add",
+ "__sub",
+ "__mul",
+ "__div",
+ "__mod",
+ "__pow",
+ "__concat",
+ "__eq",
+ "__lt",
+ "__le",
+ "__gc",
+
+ "__idiv",
+ "__shl",
+ "__shr",
+ "__bnot",
+ "__band",
+ "__bor",
+ "__bxor",
+
+ "__pairs",
+ "__ipairs",
+ "next",
+
+ "__type",
+ "__typeinfo",
+ "__sol.call_new",
+ "__sol.storage",
+ "__sol.gc_names",
+ "__sol.static_index",
+ "__sol.static_new_index" } };
+ return names;
+ }
+
+ inline const std::string& to_string(meta_function mf) {
+ return meta_function_names()[static_cast<std::size_t>(mf)];
+ }
+
+ inline type type_of(lua_State* L, int index) {
+ return static_cast<type>(lua_type(L, index));
+ }
+
+ inline std::string type_name(lua_State* L, type t) {
+ return lua_typename(L, static_cast<int>(t));
+ }
+
+ template <typename T>
+ struct is_stateless_lua_reference
+ : std::integral_constant<bool,
+ (std::is_base_of_v<stateless_stack_reference, T> || std::is_base_of_v<stateless_reference, T>)&&(
+ !std::is_base_of_v<stack_reference, T> && !std::is_base_of_v<reference, T> && !std::is_base_of_v<main_reference, T>)> { };
+
+ template <typename T>
+ inline constexpr bool is_stateless_lua_reference_v = is_stateless_lua_reference<T>::value;
+
+ template <typename T>
+ struct is_lua_reference
+ : std::integral_constant<bool,
+ std::is_base_of_v<reference,
+ T> || std::is_base_of_v<main_reference, T> || std::is_base_of_v<stack_reference, T> || std::is_base_of_v<stateless_stack_reference, T> || std::is_base_of_v<stateless_reference, T>> {
+ };
+
+ template <typename T>
+ inline constexpr bool is_lua_reference_v = is_lua_reference<T>::value;
+
+ template <typename T>
+ struct is_lua_reference_or_proxy : std::integral_constant<bool, is_lua_reference_v<T> || meta::is_specialization_of_v<T, table_proxy>> { };
+
+ template <typename T>
+ inline constexpr bool is_lua_reference_or_proxy_v = is_lua_reference_or_proxy<T>::value;
+
+ template <typename T>
+ struct is_transparent_argument
+ : std::integral_constant<bool,
+ std::is_same_v<meta::unqualified_t<T>,
+ this_state> || std::is_same_v<meta::unqualified_t<T>, this_main_state> || std::is_same_v<meta::unqualified_t<T>, this_environment> || std::is_same_v<meta::unqualified_t<T>, variadic_args>> {
+ };
+
+ template <typename T>
+ constexpr inline bool is_transparent_argument_v = is_transparent_argument<T>::value;
+
+ template <typename T>
+ struct is_variadic_arguments : meta::any<std::is_same<T, variadic_args>, meta::is_optional<T>> { };
+
+ template <typename T>
+ struct is_container
+ : std::integral_constant<bool,
+ !std::is_same_v<state_view,
+ T> && !std::is_same_v<state, T> && !meta::is_initializer_list_v<T> && !meta::is_string_like_v<T> && !meta::is_string_literal_array_v<T> && !is_transparent_argument_v<T> && !is_lua_reference_v<T> && (meta::has_begin_end_v<T> || std::is_array_v<T>)> {
+ };
+
+ template <typename T>
+ constexpr inline bool is_container_v = is_container<T>::value;
+
+ template <typename T>
+ struct is_to_stringable : meta::any<meta::supports_to_string_member<meta::unqualified_t<T>>, meta::supports_adl_to_string<meta::unqualified_t<T>>,
+ meta::supports_op_left_shift<std::ostream, meta::unqualified_t<T>>> { };
+
+ template <typename T>
+ inline constexpr bool is_to_stringable_v = is_to_stringable<T>::value;
+
+ template <typename T>
+ struct is_callable : std::true_type { };
+
+ template <typename T>
+ inline constexpr bool is_callable_v = is_callable<T>::value;
+
+ namespace detail {
+ template <typename T, typename = void>
+ struct lua_type_of : std::integral_constant<type, type::userdata> { };
+
+ template <typename C, typename T, typename A>
+ struct lua_type_of<std::basic_string<C, T, A>> : std::integral_constant<type, type::string> { };
+
+ template <typename C, typename T>
+ struct lua_type_of<basic_string_view<C, T>> : std::integral_constant<type, type::string> { };
+
+ template <std::size_t N>
+ struct lua_type_of<char[N]> : std::integral_constant<type, type::string> { };
+
+ template <std::size_t N>
+ struct lua_type_of<wchar_t[N]> : std::integral_constant<type, type::string> { };
+
+#if SOL_IS_ON(SOL_CHAR8_T)
+ template <std::size_t N>
+ struct lua_type_of<char8_t[N]> : std::integral_constant<type, type::string> { };
+#endif
+
+ template <std::size_t N>
+ struct lua_type_of<char16_t[N]> : std::integral_constant<type, type::string> { };
+
+ template <std::size_t N>
+ struct lua_type_of<char32_t[N]> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<char> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<wchar_t> : std::integral_constant<type, type::string> { };
+
+#if SOL_IS_ON(SOL_CHAR8_T)
+ template <>
+ struct lua_type_of<char8_t> : std::integral_constant<type, type::string> { };
+#endif
+
+ template <>
+ struct lua_type_of<char16_t> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<char32_t> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<const char*> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<const wchar_t*> : std::integral_constant<type, type::string> { };
+
+#if SOL_IS_ON(SOL_CHAR8_T)
+ template <>
+ struct lua_type_of<const char8_t*> : std::integral_constant<type, type::string> { };
+#endif
+
+ template <>
+ struct lua_type_of<const char16_t*> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<const char32_t*> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<bool> : std::integral_constant<type, type::boolean> { };
+
+ template <>
+ struct lua_type_of<lua_nil_t> : std::integral_constant<type, type::lua_nil> { };
+
+ template <>
+ struct lua_type_of<nullopt_t> : std::integral_constant<type, type::lua_nil> { };
+
+ template <>
+ struct lua_type_of<lua_value> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<detail::non_lua_nil_t> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<std::nullptr_t> : std::integral_constant<type, type::lua_nil> { };
+
+ template <>
+ struct lua_type_of<error> : std::integral_constant<type, type::string> { };
+
+ template <bool b, typename Base>
+ struct lua_type_of<basic_table_core<b, Base>> : std::integral_constant<type, type::table> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_lua_table<Base>> : std::integral_constant<type, type::table> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_metatable<Base>> : std::integral_constant<type, type::table> { };
+
+ template <typename T, typename Base>
+ struct lua_type_of<basic_usertype<T, Base>> : std::integral_constant<type, type::table> { };
+
+ template <>
+ struct lua_type_of<metatable_key_t> : std::integral_constant<type, type::table> { };
+
+ template <typename B>
+ struct lua_type_of<basic_environment<B>> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<env_key_t> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<new_table> : std::integral_constant<type, type::table> { };
+
+ template <typename T>
+ struct lua_type_of<as_table_t<T>> : std::integral_constant<type, type::table> { };
+
+ template <typename T>
+ struct lua_type_of<std::initializer_list<T>> : std::integral_constant<type, type::table> { };
+
+ template <bool b>
+ struct lua_type_of<basic_reference<b>> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<stack_reference> : std::integral_constant<type, type::poly> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_object<Base>> : std::integral_constant<type, type::poly> { };
+
+ template <typename... Args>
+ struct lua_type_of<std::tuple<Args...>> : std::integral_constant<type, type::poly> { };
+
+ template <typename A, typename B>
+ struct lua_type_of<std::pair<A, B>> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<void*> : std::integral_constant<type, type::lightuserdata> { };
+
+ template <>
+ struct lua_type_of<const void*> : std::integral_constant<type, type::lightuserdata> { };
+
+ template <>
+ struct lua_type_of<lightuserdata_value> : std::integral_constant<type, type::lightuserdata> { };
+
+ template <>
+ struct lua_type_of<userdata_value> : std::integral_constant<type, type::userdata> { };
+
+ template <typename T>
+ struct lua_type_of<light<T>> : std::integral_constant<type, type::lightuserdata> { };
+
+ template <typename T>
+ struct lua_type_of<user<T>> : std::integral_constant<type, type::userdata> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_lightuserdata<Base>> : std::integral_constant<type, type::lightuserdata> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_userdata<Base>> : std::integral_constant<type, type::userdata> { };
+
+ template <>
+ struct lua_type_of<lua_CFunction> : std::integral_constant<type, type::function> { };
+
+ template <>
+ struct lua_type_of<std::remove_pointer_t<lua_CFunction>> : std::integral_constant<type, type::function> { };
+
+ template <typename Base, bool aligned>
+ struct lua_type_of<basic_function<Base, aligned>> : std::integral_constant<type, type::function> { };
+
+ template <typename Base, bool aligned, typename Handler>
+ struct lua_type_of<basic_protected_function<Base, aligned, Handler>> : std::integral_constant<type, type::function> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_coroutine<Base>> : std::integral_constant<type, type::function> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_thread<Base>> : std::integral_constant<type, type::thread> { };
+
+ template <typename Signature>
+ struct lua_type_of<std::function<Signature>> : std::integral_constant<type, type::function> { };
+
+ template <typename T>
+ struct lua_type_of<optional<T>> : std::integral_constant<type, type::poly> { };
+
+ template <typename T>
+ struct lua_type_of<std::optional<T>> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<variadic_args> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<variadic_results> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<stack_count> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<this_state> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<this_main_state> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<this_environment> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<type> : std::integral_constant<type, type::poly> { };
+
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ template <typename T>
+ struct lua_type_of<T*> : std::integral_constant<type, std::is_function_v<T> ? type::function : type::userdata> { };
+#else
+ template <typename T>
+ struct lua_type_of<T*> : std::integral_constant<type, type::userdata> { };
+#endif
+
+ template <typename T>
+ struct lua_type_of<T, std::enable_if_t<std::is_arithmetic_v<T> || std::is_same_v<T, lua_Number> || std::is_same_v<T, lua_Integer>>>
+ : std::integral_constant<type, type::number> { };
+
+ template <typename T>
+ struct lua_type_of<T, std::enable_if_t<std::is_function_v<T>>> : std::integral_constant<type, type::function> { };
+
+ template <typename T>
+ struct lua_type_of<T, std::enable_if_t<std::is_enum_v<T>>> : std::integral_constant<type, type::number> { };
+
+ template <>
+ struct lua_type_of<meta_function> : std::integral_constant<type, type::string> { };
+
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ template <typename... Tn>
+ struct lua_type_of<std::variant<Tn...>> : std::integral_constant<type, type::poly> { };
+#endif // std::variant deployment sucks on Clang
+
+ template <typename T>
+ struct lua_type_of<nested<T>> : meta::conditional_t<::sol::is_container_v<T>, std::integral_constant<type, type::table>, lua_type_of<T>> { };
+
+ template <typename C, C v, template <typename...> class V, typename... Args>
+ struct accumulate : std::integral_constant<C, v> { };
+
+ template <typename C, C v, template <typename...> class V, typename T, typename... Args>
+ struct accumulate<C, v, V, T, Args...> : accumulate<C, v + V<T>::value, V, Args...> { };
+
+ template <typename C, C v, template <typename...> class V, typename List>
+ struct accumulate_list;
+
+ template <typename C, C v, template <typename...> class V, typename... Args>
+ struct accumulate_list<C, v, V, types<Args...>> : accumulate<C, v, V, Args...> { };
+ } // namespace detail
+
+ template <typename T>
+ struct lua_type_of : detail::lua_type_of<T> {
+ typedef int SOL_INTERNAL_UNSPECIALIZED_MARKER_;
+ };
+
+ template <typename T>
+ inline constexpr type lua_type_of_v = lua_type_of<T>::value;
+
+ template <typename T>
+ struct lua_size : std::integral_constant<int, 1> {
+ typedef int SOL_INTERNAL_UNSPECIALIZED_MARKER_;
+ };
+
+ template <typename A, typename B>
+ struct lua_size<std::pair<A, B>> : std::integral_constant<int, lua_size<A>::value + lua_size<B>::value> { };
+
+ template <typename... Args>
+ struct lua_size<std::tuple<Args...>> : std::integral_constant<int, detail::accumulate<int, 0, lua_size, Args...>::value> { };
+
+ template <typename T>
+ inline constexpr int lua_size_v = lua_size<T>::value;
+
+ namespace detail {
+ // MSVC's decltype detection is broken, which breaks other
+ // parts of the code. So we add more workarounds. The moment it's fixed,
+ // we take it away and break everyone that doesn't upgrade.
+ template <typename T>
+ using is_msvc_callable_rigged = meta::any<meta::is_specialization_of<T, push_invoke_t>, meta::is_specialization_of<T, as_table_t>,
+ meta::is_specialization_of<T, forward_as_value_t>, meta::is_specialization_of<T, as_container_t>, meta::is_specialization_of<T, nested>,
+ meta::is_specialization_of<T, yielding_t>>;
+
+ template <typename T>
+ inline constexpr bool is_msvc_callable_rigged_v = is_msvc_callable_rigged<T>::value;
+ } // namespace detail
+
+ template <typename T>
+ struct is_lua_primitive : std::integral_constant<bool,
+ type::userdata != lua_type_of_v<T> // cf
+ || ((type::userdata == lua_type_of_v<T>) // cf
+ &&meta::meta_detail::has_internal_marker_v<lua_type_of<T>> // cf
+ && !meta::meta_detail::has_internal_marker_v<lua_size<T>>) // cf
+ || is_lua_reference_or_proxy_v<T> // cf
+ || meta::is_specialization_of_v<T, std::tuple> // cf
+ || meta::is_specialization_of_v<T, std::pair>> { };
+
+ template <typename T>
+ constexpr inline bool is_lua_primitive_v = is_lua_primitive<T>::value;
+
+ template <typename T>
+ struct is_value_semantic_for_function
+#if SOL_IS_ON(SOL_FUNCTION_CALL_VALUE_SEMANTICS)
+ : std::true_type {
+ };
+#else
+ : std::false_type {
+ };
+#endif
+
+ template <typename T>
+ constexpr inline bool is_value_semantic_for_function_v = is_value_semantic_for_function<T>::value;
+
+ template <typename T>
+ struct is_main_threaded : std::is_base_of<main_reference, T> { };
+
+ template <typename T>
+ inline constexpr bool is_main_threaded_v = is_main_threaded<T>::value;
+
+ template <typename T>
+ struct is_stack_based : std::is_base_of<stack_reference, T> { };
+ template <>
+ struct is_stack_based<variadic_args> : std::true_type { };
+ template <>
+ struct is_stack_based<unsafe_function_result> : std::true_type { };
+ template <>
+ struct is_stack_based<protected_function_result> : std::true_type { };
+ template <>
+ struct is_stack_based<stack_proxy> : std::true_type { };
+ template <>
+ struct is_stack_based<stack_proxy_base> : std::true_type { };
+ template <>
+ struct is_stack_based<stack_count> : std::true_type { };
+
+ template <typename T>
+ constexpr inline bool is_stack_based_v = is_stack_based<T>::value;
+
+ template <typename T>
+ struct is_lua_primitive<T*> : std::true_type { };
+ template <>
+ struct is_lua_primitive<unsafe_function_result> : std::true_type { };
+ template <>
+ struct is_lua_primitive<protected_function_result> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<std::reference_wrapper<T>> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<user<T>> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<light<T>> : is_lua_primitive<T*> { };
+ template <typename T>
+ struct is_lua_primitive<optional<T>> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<std::optional<T>> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<as_table_t<T>> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<nested<T>> : std::true_type { };
+ template <>
+ struct is_lua_primitive<userdata_value> : std::true_type { };
+ template <>
+ struct is_lua_primitive<lightuserdata_value> : std::true_type { };
+ template <>
+ struct is_lua_primitive<stack_proxy> : std::true_type { };
+ template <>
+ struct is_lua_primitive<stack_proxy_base> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<non_null<T>> : is_lua_primitive<T*> { };
+
+ template <typename T>
+ struct is_lua_index : std::is_integral<T> { };
+ template <>
+ struct is_lua_index<raw_index> : std::true_type { };
+ template <>
+ struct is_lua_index<absolute_index> : std::true_type { };
+ template <>
+ struct is_lua_index<ref_index> : std::true_type { };
+ template <>
+ struct is_lua_index<upvalue_index> : std::true_type { };
+
+ template <typename Signature>
+ struct lua_bind_traits : meta::bind_traits<Signature> {
+ private:
+ typedef meta::bind_traits<Signature> base_t;
+
+ public:
+ typedef std::integral_constant<bool, meta::count_for<is_variadic_arguments, typename base_t::args_list>::value != 0> runtime_variadics_t;
+ static const std::size_t true_arity = base_t::arity;
+ static const std::size_t arity = detail::accumulate_list<std::size_t, 0, lua_size, typename base_t::args_list>::value
+ - meta::count_for<is_transparent_argument, typename base_t::args_list>::value;
+ static const std::size_t true_free_arity = base_t::free_arity;
+ static const std::size_t free_arity = detail::accumulate_list<std::size_t, 0, lua_size, typename base_t::free_args_list>::value
+ - meta::count_for<is_transparent_argument, typename base_t::args_list>::value;
+ };
+
+ template <typename T>
+ struct is_table : std::false_type { };
+ template <bool x, typename T>
+ struct is_table<basic_table_core<x, T>> : std::true_type { };
+ template <typename T>
+ struct is_table<basic_lua_table<T>> : std::true_type { };
+
+ template <typename T>
+ inline constexpr bool is_table_v = is_table<T>::value;
+
+ template <typename T>
+ struct is_global_table : std::false_type { };
+ template <typename T>
+ struct is_global_table<basic_table_core<true, T>> : std::true_type { };
+
+ template <typename T>
+ inline constexpr bool is_global_table_v = is_global_table<T>::value;
+
+ template <typename T>
+ struct is_stack_table : std::false_type { };
+ template <bool x, typename T>
+ struct is_stack_table<basic_table_core<x, T>> : std::integral_constant<bool, std::is_base_of_v<stack_reference, T>> { };
+ template <typename T>
+ struct is_stack_table<basic_lua_table<T>> : std::integral_constant<bool, std::is_base_of_v<stack_reference, T>> { };
+
+ template <typename T>
+ inline constexpr bool is_stack_table_v = is_stack_table<T>::value;
+
+ template <typename T>
+ struct is_function : std::false_type { };
+ template <typename T, bool aligned>
+ struct is_function<basic_function<T, aligned>> : std::true_type { };
+ template <typename T, bool aligned, typename Handler>
+ struct is_function<basic_protected_function<T, aligned, Handler>> : std::true_type { };
+
+ template <typename T>
+ using is_lightuserdata = meta::is_specialization_of<T, basic_lightuserdata>;
+
+ template <typename T>
+ inline constexpr bool is_lightuserdata_v = is_lightuserdata<T>::value;
+
+ template <typename T>
+ using is_userdata = meta::is_specialization_of<T, basic_userdata>;
+
+ template <typename T>
+ inline constexpr bool is_userdata_v = is_userdata<T>::value;
+
+ template <typename T>
+ using is_environment = std::integral_constant<bool, is_userdata_v<T> || is_table_v<T> || meta::is_specialization_of_v<T, basic_environment>>;
+
+ template <typename T>
+ inline constexpr bool is_environment_v = is_environment<T>::value;
+
+ template <typename T>
+ using is_table_like = std::integral_constant<bool, is_table_v<T> || is_environment_v<T> || is_userdata_v<T>>;
+
+ template <typename T>
+ inline constexpr bool is_table_like_v = is_table_like<T>::value;
+
+ template <typename T>
+ struct is_automagical
+ : std::integral_constant<bool,
+ (SOL_IS_ON(SOL_DEFAULT_AUTOMAGICAL_USERTYPES))
+ || (std::is_array_v<
+ meta::unqualified_t<T>> || (!std::is_same_v<meta::unqualified_t<T>, state> && !std::is_same_v<meta::unqualified_t<T>, state_view>))> {
+ };
+
+ template <typename T>
+ inline type type_of() {
+ return lua_type_of<meta::unqualified_t<T>>::value;
+ }
+
+ namespace detail {
+ template <typename T>
+ struct is_non_factory_constructor : std::false_type { };
+
+ template <typename... Args>
+ struct is_non_factory_constructor<constructors<Args...>> : std::true_type { };
+
+ template <typename... Args>
+ struct is_non_factory_constructor<constructor_wrapper<Args...>> : std::true_type { };
+
+ template <>
+ struct is_non_factory_constructor<no_construction> : std::true_type { };
+
+ template <typename T>
+ inline constexpr bool is_non_factory_constructor_v = is_non_factory_constructor<T>::value;
+
+ template <typename T>
+ struct is_constructor : is_non_factory_constructor<T> { };
+
+ template <typename... Args>
+ struct is_constructor<factory_wrapper<Args...>> : std::true_type { };
+
+ template <typename T>
+ struct is_constructor<protect_t<T>> : is_constructor<meta::unqualified_t<T>> { };
+
+ template <typename F, typename... Policies>
+ struct is_constructor<policy_wrapper<F, Policies...>> : is_constructor<meta::unqualified_t<F>> { };
+
+ template <typename T>
+ inline constexpr bool is_constructor_v = is_constructor<T>::value;
+
+ template <typename... Args>
+ using any_is_constructor = meta::any<is_constructor<meta::unqualified_t<Args>>...>;
+
+ template <typename... Args>
+ inline constexpr bool any_is_constructor_v = any_is_constructor<Args...>::value;
+
+ template <typename T>
+ struct is_destructor : std::false_type { };
+
+ template <typename Fx>
+ struct is_destructor<destructor_wrapper<Fx>> : std::true_type { };
+
+ template <typename... Args>
+ using any_is_destructor = meta::any<is_destructor<meta::unqualified_t<Args>>...>;
+
+ template <typename... Args>
+ inline constexpr bool any_is_destructor_v = any_is_destructor<Args...>::value;
+ } // namespace detail
+
+ template <typename T>
+ using is_lua_c_function = meta::any<std::is_same<lua_CFunction, T>, std::is_same<detail::lua_CFunction_noexcept, T>, std::is_same<lua_CFunction_ref, T>>;
+
+ template <typename T>
+ inline constexpr bool is_lua_c_function_v = is_lua_c_function<T>::value;
+
+ enum class automagic_flags : unsigned {
+ none = 0x000u,
+ default_constructor = 0x001,
+ destructor = 0x002u,
+ pairs_operator = 0x004u,
+ to_string_operator = 0x008u,
+ call_operator = 0x010u,
+ less_than_operator = 0x020u,
+ less_than_or_equal_to_operator = 0x040u,
+ length_operator = 0x080u,
+ equal_to_operator = 0x100u,
+ all = default_constructor | destructor | pairs_operator | to_string_operator | call_operator | less_than_operator | less_than_or_equal_to_operator
+ | length_operator | equal_to_operator
+ };
+
+ inline constexpr automagic_flags operator|(automagic_flags left, automagic_flags right) noexcept {
+ return static_cast<automagic_flags>(
+ static_cast<std::underlying_type_t<automagic_flags>>(left) | static_cast<std::underlying_type_t<automagic_flags>>(right));
+ }
+
+ inline constexpr automagic_flags operator&(automagic_flags left, automagic_flags right) noexcept {
+ return static_cast<automagic_flags>(
+ static_cast<std::underlying_type_t<automagic_flags>>(left) & static_cast<std::underlying_type_t<automagic_flags>>(right));
+ }
+
+ inline constexpr automagic_flags& operator|=(automagic_flags& left, automagic_flags right) noexcept {
+ left = left | right;
+ return left;
+ }
+
+ inline constexpr automagic_flags& operator&=(automagic_flags& left, automagic_flags right) noexcept {
+ left = left & right;
+ return left;
+ }
+
+ template <typename Left, typename Right>
+ constexpr bool has_flag(Left left, Right right) noexcept {
+ return (left & right) == right;
+ }
+
+ template <typename Left, typename Right>
+ constexpr bool has_any_flag(Left left, Right right) noexcept {
+ return (left & right) != static_cast<Left>(static_cast<std::underlying_type_t<Left>>(0));
+ }
+
+ template <typename Left, typename Right>
+ constexpr auto clear_flags(Left left, Right right) noexcept {
+ return static_cast<Left>(static_cast<std::underlying_type_t<Left>>(left) & ~static_cast<std::underlying_type_t<Right>>(right));
+ }
+
+ struct automagic_enrollments {
+ bool default_constructor = true;
+ bool destructor = true;
+ bool pairs_operator = true;
+ bool to_string_operator = true;
+ bool call_operator = true;
+ bool less_than_operator = true;
+ bool less_than_or_equal_to_operator = true;
+ bool length_operator = true;
+ bool equal_to_operator = true;
+ };
+
+ template <automagic_flags compile_time_defaults = automagic_flags::all>
+ struct constant_automagic_enrollments : public automagic_enrollments { };
+
+} // namespace sol
+
+// end of sol/types.hpp
+
+#include <exception>
+#include <cstring>
+
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+#include <iostream>
+#endif
+
+namespace sol {
+ // must push a single object to be the error object
+ // NOTE: the VAST MAJORITY of all Lua libraries -- C or otherwise -- expect a string for the type of error
+ // break this convention at your own risk
+ using exception_handler_function = int (*)(lua_State*, optional<const std::exception&>, string_view);
+
+ namespace detail {
+ inline const char (&default_exception_handler_name())[11] {
+ static const char name[11] = "sol.\xE2\x98\xA2\xE2\x98\xA2";
+ return name;
+ }
+
+ // must push at least 1 object on the stack
+ inline int default_exception_handler(lua_State* L, optional<const std::exception&>, string_view what) {
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+ std::cerr << "[sol2] An exception occurred: ";
+ std::cerr.write(what.data(), static_cast<std::streamsize>(what.size()));
+ std::cerr << std::endl;
+#endif
+ lua_pushlstring(L, what.data(), what.size());
+ return 1;
+ }
+
+ inline int call_exception_handler(lua_State* L, optional<const std::exception&> maybe_ex, string_view what) {
+ lua_getglobal(L, default_exception_handler_name());
+ type t = static_cast<type>(lua_type(L, -1));
+ if (t != type::lightuserdata) {
+ lua_pop(L, 1);
+ return default_exception_handler(L, std::move(maybe_ex), std::move(what));
+ }
+ void* vfunc = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+ if (vfunc == nullptr) {
+ return default_exception_handler(L, std::move(maybe_ex), std::move(what));
+ }
+ exception_handler_function exfunc = reinterpret_cast<exception_handler_function>(vfunc);
+ return exfunc(L, std::move(maybe_ex), std::move(what));
+ }
+
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ template <lua_CFunction f>
+ int static_trampoline(lua_State* L) noexcept {
+ return f(L);
+ }
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ template <lua_CFunction_noexcept f>
+ int static_trampoline_noexcept(lua_State* L) noexcept {
+ return f(L);
+ }
+#else
+ template <lua_CFunction f>
+ int static_trampoline_noexcept(lua_State* L) noexcept {
+ return f(L);
+ }
+#endif
+
+ template <typename Fx, typename... Args>
+ int trampoline(lua_State* L, Fx&& f, Args&&... args) noexcept {
+ return f(L, std::forward<Args>(args)...);
+ }
+
+ inline int c_trampoline(lua_State* L, lua_CFunction f) noexcept {
+ return trampoline(L, f);
+ }
+#else
+
+ inline int lua_cfunction_trampoline(lua_State* L, lua_CFunction f) {
+#if SOL_IS_ON(SOL_PROPAGATE_EXCEPTIONS)
+ return f(L);
+#else
+ try {
+ return f(L);
+ }
+ catch (const char* cs) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), string_view(cs));
+ }
+ catch (const std::string& s) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), string_view(s.c_str(), s.size()));
+ }
+ catch (const std::exception& e) {
+ call_exception_handler(L, optional<const std::exception&>(e), e.what());
+ }
+#if SOL_IS_ON(SOL_EXCEPTIONS_CATCH_ALL)
+ // LuaJIT cannot have the catchall when the safe propagation is on
+ // but LuaJIT will swallow all C++ errors
+ // if we don't at least catch std::exception ones
+ catch (...) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), "caught (...) exception");
+ }
+#endif // LuaJIT cannot have the catchall, but we must catch std::exceps for it
+ return lua_error(L);
+#endif // Safe exceptions
+ }
+
+ template <lua_CFunction f>
+ int static_trampoline(lua_State* L) {
+ return lua_cfunction_trampoline(L, f);
+ }
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ template <lua_CFunction_noexcept f>
+ int static_trampoline_noexcept(lua_State* L) noexcept {
+ return f(L);
+ }
+#else
+ template <lua_CFunction f>
+ int static_trampoline_noexcept(lua_State* L) noexcept {
+ return f(L);
+ }
+#endif
+
+ template <typename Fx, typename... Args>
+ int trampoline(lua_State* L, Fx&& f, Args&&... args) {
+ if constexpr (meta::bind_traits<meta::unqualified_t<Fx>>::is_noexcept) {
+ return f(L, std::forward<Args>(args)...);
+ }
+ else {
+#if SOL_IS_ON(SOL_PROPAGATE_EXCEPTIONS)
+ return f(L, std::forward<Args>(args)...);
+#else
+ try {
+ return f(L, std::forward<Args>(args)...);
+ }
+ catch (const char* cs) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), string_view(cs));
+ }
+ catch (const std::string& s) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), string_view(s.c_str(), s.size()));
+ }
+ catch (const std::exception& e) {
+ call_exception_handler(L, optional<const std::exception&>(e), e.what());
+ }
+#if SOL_IS_ON(SOL_EXCEPTIONS_CATCH_ALL)
+ // LuaJIT cannot have the catchall when the safe propagation is on
+ // but LuaJIT will swallow all C++ errors
+ // if we don't at least catch std::exception ones
+ catch (...) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), "caught (...) exception");
+ }
+#endif
+ return lua_error(L);
+#endif
+ }
+ }
+
+ inline int c_trampoline(lua_State* L, lua_CFunction f) {
+ return trampoline(L, f);
+ }
+#endif // Exceptions vs. No Exceptions
+
+ template <typename F, F fx>
+ inline int typed_static_trampoline(lua_State* L) {
+#if 0
+ // TODO: you must evaluate the get/check_get of every
+ // argument, to ensure it doesn't throw
+ // (e.g., for the sol_lua_check_access extension point!)
+ // This incluudes properly noexcept-ing all the above
+ // trampolines / safety nets
+ if constexpr (meta::bind_traits<F>::is_noexcept) {
+ return static_trampoline_noexcept<fx>(L);
+ }
+ else
+#endif
+ { return static_trampoline<fx>(L); }
+ }
+ } // namespace detail
+
+ inline void set_default_exception_handler(lua_State* L, exception_handler_function exf = &detail::default_exception_handler) {
+ static_assert(sizeof(void*) >= sizeof(exception_handler_function),
+ "void* storage is too small to transport the exception handler: please file a bug on the sol2 issue tracker to get this looked at!");
+ void* storage;
+ std::memcpy(&storage, &exf, sizeof(exception_handler_function));
+ lua_pushlightuserdata(L, storage);
+ lua_setglobal(L, detail::default_exception_handler_name());
+ }
+} // namespace sol
+
+// end of sol/trampoline.hpp
+
+// beginning of sol/stack_core.hpp
+
+// beginning of sol/inheritance.hpp
+
+// beginning of sol/usertype_traits.hpp
+
+// beginning of sol/demangle.hpp
+
+#include <string>
+#include <array>
+#include <cctype>
+#if SOL_IS_ON(SOL_MINGW_CCTYPE_IS_POISONED)
+extern "C" {
+#include <ctype.h>
+}
+#endif // MinGW is on some stuff
+#include <locale>
+
+namespace sol { namespace detail {
+ inline constexpr std::array<string_view, 9> removals { { "{anonymous}",
+ "(anonymous namespace)",
+ "public:",
+ "private:",
+ "protected:",
+ "struct ",
+ "class ",
+ "`anonymous-namespace'",
+ "`anonymous namespace'" } };
+
+#if SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ inline std::string ctti_get_type_name_from_sig(std::string name) {
+ // cardinal sins from MINGW
+ using namespace std;
+ std::size_t start = name.find_first_of('[');
+ start = name.find_first_of('=', start);
+ std::size_t end = name.find_last_of(']');
+ if (end == std::string::npos)
+ end = name.size();
+ if (start == std::string::npos)
+ start = 0;
+ if (start < name.size() - 1)
+ start += 1;
+ name = name.substr(start, end - start);
+ start = name.rfind("seperator_mark");
+ if (start != std::string::npos) {
+ name.erase(start - 2, name.length());
+ }
+ while (!name.empty() && isblank(name.front()))
+ name.erase(name.begin());
+ while (!name.empty() && isblank(name.back()))
+ name.pop_back();
+
+ for (std::size_t r = 0; r < removals.size(); ++r) {
+ auto found = name.find(removals[r]);
+ while (found != std::string::npos) {
+ name.erase(found, removals[r].size());
+ found = name.find(removals[r]);
+ }
+ }
+
+ return name;
+ }
+
+ template <typename T, class seperator_mark = int>
+ inline std::string ctti_get_type_name() {
+ return ctti_get_type_name_from_sig(__PRETTY_FUNCTION__);
+ }
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+ inline std::string ctti_get_type_name_from_sig(std::string name) {
+ std::size_t start = name.find("get_type_name");
+ if (start == std::string::npos)
+ start = 0;
+ else
+ start += 13;
+ if (start < name.size() - 1)
+ start += 1;
+ std::size_t end = name.find_last_of('>');
+ if (end == std::string::npos)
+ end = name.size();
+ name = name.substr(start, end - start);
+ if (name.find("struct", 0) == 0)
+ name.replace(0, 6, "", 0);
+ if (name.find("class", 0) == 0)
+ name.replace(0, 5, "", 0);
+ while (!name.empty() && isblank(name.front()))
+ name.erase(name.begin());
+ while (!name.empty() && isblank(name.back()))
+ name.pop_back();
+
+ for (std::size_t r = 0; r < removals.size(); ++r) {
+ auto found = name.find(removals[r]);
+ while (found != std::string::npos) {
+ name.erase(found, removals[r].size());
+ found = name.find(removals[r]);
+ }
+ }
+
+ return name;
+ }
+
+ template <typename T>
+ std::string ctti_get_type_name() {
+ return ctti_get_type_name_from_sig(__FUNCSIG__);
+ }
+#else
+#error Compiler not supported for demangling
+#endif // compilers
+
+ template <typename T>
+ std::string demangle_once() {
+ std::string realname = ctti_get_type_name<T>();
+ return realname;
+ }
+
+ inline std::string short_demangle_from_type_name(std::string realname) {
+ // This isn't the most complete but it'll do for now...?
+ static const std::array<std::string, 10> ops = {
+ { "operator<", "operator<<", "operator<<=", "operator<=", "operator>", "operator>>", "operator>>=", "operator>=", "operator->", "operator->*" }
+ };
+ int level = 0;
+ std::size_t idx = 0;
+ for (idx = static_cast<std::size_t>(realname.empty() ? 0 : realname.size() - 1); idx > 0; --idx) {
+ if (level == 0 && realname[idx] == ':') {
+ break;
+ }
+ bool isleft = realname[idx] == '<';
+ bool isright = realname[idx] == '>';
+ if (!isleft && !isright)
+ continue;
+ bool earlybreak = false;
+ for (const auto& op : ops) {
+ std::size_t nisop = realname.rfind(op, idx);
+ if (nisop == std::string::npos)
+ continue;
+ std::size_t nisopidx = idx - op.size() + 1;
+ if (nisop == nisopidx) {
+ idx = static_cast<std::size_t>(nisopidx);
+ earlybreak = true;
+ }
+ break;
+ }
+ if (earlybreak) {
+ continue;
+ }
+ level += isleft ? -1 : 1;
+ }
+ if (idx > 0) {
+ realname.erase(0, realname.length() < static_cast<std::size_t>(idx) ? realname.length() : idx + 1);
+ }
+ return realname;
+ }
+
+ template <typename T>
+ std::string short_demangle_once() {
+ std::string realname = ctti_get_type_name<T>();
+ return short_demangle_from_type_name(realname);
+ }
+
+ template <typename T>
+ const std::string& demangle() {
+ static const std::string d = demangle_once<T>();
+ return d;
+ }
+
+ template <typename T>
+ const std::string& short_demangle() {
+ static const std::string d = short_demangle_once<T>();
+ return d;
+ }
+}} // namespace sol::detail
+
+// end of sol/demangle.hpp
+
+namespace sol {
+
+ template <typename T>
+ struct usertype_traits {
+ static const std::string& name() {
+ static const std::string& n = detail::short_demangle<T>();
+ return n;
+ }
+ static const std::string& qualified_name() {
+ static const std::string& q_n = detail::demangle<T>();
+ return q_n;
+ }
+ static const std::string& metatable() {
+ static const std::string m = std::string("sol.").append(detail::demangle<T>());
+ return m;
+ }
+ static const std::string& user_metatable() {
+ static const std::string u_m = std::string("sol.").append(detail::demangle<T>()).append(".user");
+ return u_m;
+ }
+ static const std::string& user_gc_metatable() {
+ static const std::string u_g_m = std::string("sol.").append(detail::demangle<T>()).append(".user\xE2\x99\xBB");
+ return u_g_m;
+ }
+ static const std::string& gc_table() {
+ static const std::string g_t = std::string("sol.").append(detail::demangle<T>()).append(".\xE2\x99\xBB");
+ return g_t;
+ }
+ };
+
+} // namespace sol
+
+// end of sol/usertype_traits.hpp
+
+// beginning of sol/unique_usertype_traits.hpp
+
+#include <memory>
+
+namespace sol {
+
+ namespace detail {
+ template <typename T>
+ struct unique_fallback {
+ using SOL_INTERNAL_UNSPECIALIZED_MARKER_ = int;
+ };
+
+ template <typename T>
+ struct unique_fallback<std::shared_ptr<T>> {
+ private:
+ using pointer = typename std::pointer_traits<std::shared_ptr<T>>::element_type*;
+
+ public:
+ // rebind is non-void
+ // if and only if unique usertype
+ // is cast-capable
+ template <typename X>
+ using rebind_actual_type = std::shared_ptr<X>;
+
+ static bool is_null(lua_State*, const std::shared_ptr<T>& p) noexcept {
+ return p == nullptr;
+ }
+
+ static pointer get(lua_State*, const std::shared_ptr<T>& p) noexcept {
+ return p.get();
+ }
+ };
+
+ template <typename T, typename D>
+ struct unique_fallback<std::unique_ptr<T, D>> {
+ private:
+ using pointer = typename std::unique_ptr<T, D>::pointer;
+
+ public:
+ static bool is_null(lua_State*, const std::unique_ptr<T, D>& p) noexcept {
+ return p == nullptr;
+ }
+
+ static pointer get(lua_State*, const std::unique_ptr<T, D>& p) noexcept {
+ return p.get();
+ }
+ };
+ } // namespace detail
+
+ namespace meta { namespace meta_detail {
+ template <typename T, typename = void>
+ struct unique_actual_type;
+
+ template <typename T>
+ struct unique_actual_type<T, meta::void_t<typename T::actual_type>> {
+ using type = typename T::actual_type;
+ };
+
+ template <typename T, typename... Rest, template <typename...> class Templ>
+ struct unique_actual_type<Templ<T, Rest...>> {
+ using type = T;
+ };
+
+ }} // namespace meta::meta_detail
+
+ template <typename T>
+ using unique_usertype_actual_t = typename meta::meta_detail::unique_actual_type<unique_usertype_traits<T>>::type;
+
+ namespace meta { namespace meta_detail {
+ template <typename T>
+ using value_test_t = decltype(T::value);
+
+ template <typename T>
+ using type_test_t = typename T::type;
+
+ template <typename T>
+ using type_element_type_t = typename T::element_type;
+
+ template <typename T, typename = void>
+ struct unique_element_type {
+ using type = typename std::pointer_traits<typename unique_actual_type<T>::type>::element_type;
+ };
+
+ template <typename T>
+ struct unique_element_type<T, std::enable_if_t<meta::is_detected_v<type_element_type_t, T>>> {
+ using type = typename T::element_type;
+ };
+
+ template <typename T>
+ struct unique_element_type<T, std::enable_if_t<meta::is_detected_v<type_test_t, T>>> {
+ using type = typename T::type;
+ };
+
+ template <typename T, typename = void>
+ struct unique_valid : std::integral_constant<bool, !has_internal_marker_v<T>> { };
+
+ template <typename T>
+ struct unique_valid<T, meta::void_t<decltype(T::value)>> : std::integral_constant<bool, T::value> { };
+ }} // namespace meta::meta_detail
+
+ template <typename T>
+ using unique_usertype_element_t = typename meta::meta_detail::unique_element_type<unique_usertype_traits<T>>::type;
+
+ template <typename T, typename Element = void>
+ using unique_usertype_rebind_actual_t = typename unique_usertype_traits<T>::template rebind_actual_type<Element>;
+
+ template <typename T>
+ struct unique_usertype_traits : public detail::unique_fallback<T> { };
+
+ template <typename T>
+ struct is_unique_usertype : std::integral_constant<bool, meta::meta_detail::unique_valid<unique_usertype_traits<T>>::value> { };
+
+ template <typename T>
+ inline constexpr bool is_unique_usertype_v = is_unique_usertype<T>::value;
+
+ namespace meta { namespace meta_detail {
+ template <typename T>
+ using adl_sol_lua_check_access_test_t
+ = decltype(sol_lua_check_access(types<T>(), static_cast<lua_State*>(nullptr), -1, std::declval<stack::record&>()));
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_check_access_v = meta::is_detected_v<adl_sol_lua_check_access_test_t, T>;
+
+ template <typename T>
+ using unique_usertype_get_with_state_test_t
+ = decltype(unique_usertype_traits<T>::get(static_cast<lua_State*>(nullptr), std::declval<unique_usertype_actual_t<T>>()));
+
+ template <typename T>
+ inline constexpr bool unique_usertype_get_with_state_v = meta::is_detected_v<unique_usertype_get_with_state_test_t, T>;
+
+ template <typename T>
+ using unique_usertype_is_null_with_state_test_t
+ = decltype(unique_usertype_traits<T>::is_null(static_cast<lua_State*>(nullptr), std::declval<unique_usertype_actual_t<T>>()));
+
+ template <typename T>
+ inline constexpr bool unique_usertype_is_null_with_state_v = meta::is_detected_v<unique_usertype_is_null_with_state_test_t, T>;
+ }} // namespace meta::meta_detail
+
+ namespace detail {
+ template <typename T>
+ constexpr bool unique_is_null_noexcept() noexcept {
+ if constexpr (meta::meta_detail::unique_usertype_is_null_with_state_v<std::remove_cv_t<T>>) {
+ return noexcept(
+ unique_usertype_traits<T>::is_null(static_cast<lua_State*>(nullptr), std::declval<unique_usertype_actual_t<std::remove_cv_t<T>>>()));
+ }
+ else {
+ return noexcept(unique_usertype_traits<T>::is_null(std::declval<unique_usertype_actual_t<std::remove_cv_t<T>>>()));
+ }
+ }
+
+ template <typename T>
+ bool unique_is_null(lua_State* L_, T& value_) noexcept(unique_is_null_noexcept<std::remove_cv_t<T>>()) {
+ using Tu = std::remove_cv_t<T>;
+ if constexpr (meta::meta_detail::unique_usertype_is_null_with_state_v<Tu>) {
+ return unique_usertype_traits<Tu>::is_null(L_, value_);
+ }
+ else {
+ return unique_usertype_traits<Tu>::is_null(value_);
+ }
+ }
+
+ template <typename T>
+ constexpr bool unique_get_noexcept() noexcept {
+ if constexpr (meta::meta_detail::unique_usertype_get_with_state_v<std::remove_cv_t<T>>) {
+ return noexcept(
+ unique_usertype_traits<T>::get(static_cast<lua_State*>(nullptr), std::declval<unique_usertype_actual_t<std::remove_cv_t<T>>>()));
+ }
+ else {
+ return noexcept(unique_usertype_traits<T>::get(std::declval<unique_usertype_actual_t<std::remove_cv_t<T>>>()));
+ }
+ }
+
+ template <typename T>
+ auto unique_get(lua_State* L_, T& value_) noexcept(unique_get_noexcept<std::remove_cv_t<T>>()) {
+ using Tu = std::remove_cv_t<T>;
+ if constexpr (meta::meta_detail::unique_usertype_get_with_state_v<Tu>) {
+ return unique_usertype_traits<Tu>::get(L_, value_);
+ }
+ else {
+ return unique_usertype_traits<Tu>::get(value_);
+ }
+ }
+ } // namespace detail
+
+ namespace meta { namespace meta_detail {
+ template <typename T, typename Element = void>
+ using is_rebind_actual_type_test_t = typename T::template rebind_actual_type<Element>;
+
+ template <typename T, typename Element = void>
+ using is_rebind_actual_type = meta::is_detected<is_rebind_actual_type_test_t, T, Element>;
+
+ template <typename T, typename Element = void>
+ inline constexpr bool is_rebind_actual_type_v = is_rebind_actual_type<T, Element>::value;
+
+ template <typename T, typename Element, bool = is_rebind_actual_type_v<T, Element>>
+ struct is_actual_type_rebindable_for_test : std::false_type { };
+
+ template <typename T, typename Element>
+ struct is_actual_type_rebindable_for_test<T, Element, true>
+ : std::integral_constant<bool, !std::is_void_v<typename T::template rebind_actual_type<Element>>> { };
+ }} // namespace meta::meta_detail
+
+ template <typename T, typename Element = void>
+ using is_actual_type_rebindable_for = typename meta::meta_detail::is_actual_type_rebindable_for_test<unique_usertype_traits<T>, Element>::type;
+
+ template <typename T, typename Element = void>
+ inline constexpr bool is_actual_type_rebindable_for_v = is_actual_type_rebindable_for<T, Element>::value;
+
+} // namespace sol
+
+// end of sol/unique_usertype_traits.hpp
+
+namespace sol {
+ template <typename... Args>
+ struct base_list { };
+ template <typename... Args>
+ using bases = base_list<Args...>;
+
+ typedef bases<> base_classes_tag;
+ const auto base_classes = base_classes_tag();
+
+ template <typename... Args>
+ struct is_to_stringable<base_list<Args...>> : std::false_type { };
+
+ namespace detail {
+
+ inline decltype(auto) base_class_check_key() {
+ static const auto& key = "class_check";
+ return key;
+ }
+
+ inline decltype(auto) base_class_cast_key() {
+ static const auto& key = "class_cast";
+ return key;
+ }
+
+ inline decltype(auto) base_class_index_propogation_key() {
+ static const auto& key = u8"\xF0\x9F\x8C\xB2.index";
+ return key;
+ }
+
+ inline decltype(auto) base_class_new_index_propogation_key() {
+ static const auto& key = u8"\xF0\x9F\x8C\xB2.new_index";
+ return key;
+ }
+
+ template <typename T>
+ struct inheritance {
+ typedef typename base<T>::type bases_t;
+
+ static bool type_check_bases(types<>, const string_view&) {
+ return false;
+ }
+
+ template <typename Base, typename... Args>
+ static bool type_check_bases(types<Base, Args...>, const string_view& ti) {
+ return ti == usertype_traits<Base>::qualified_name() || type_check_bases(types<Args...>(), ti);
+ }
+
+ static bool type_check(const string_view& ti) {
+ return ti == usertype_traits<T>::qualified_name() || type_check_bases(bases_t(), ti);
+ }
+
+ template <typename... Bases>
+ static bool type_check_with(const string_view& ti) {
+ return ti == usertype_traits<T>::qualified_name() || type_check_bases(types<Bases...>(), ti);
+ }
+
+ static void* type_cast_bases(types<>, T*, const string_view&) {
+ return nullptr;
+ }
+
+ template <typename Base, typename... Args>
+ static void* type_cast_bases(types<Base, Args...>, T* data, const string_view& ti) {
+ // Make sure to convert to T first, and then dynamic cast to the proper type
+ return ti != usertype_traits<Base>::qualified_name() ? type_cast_bases(types<Args...>(), data, ti)
+ : static_cast<void*>(static_cast<Base*>(data));
+ }
+
+ static void* type_cast(void* voiddata, const string_view& ti) {
+ T* data = static_cast<T*>(voiddata);
+ return static_cast<void*>(ti != usertype_traits<T>::qualified_name() ? type_cast_bases(bases_t(), data, ti) : data);
+ }
+
+ template <typename... Bases>
+ static void* type_cast_with(void* voiddata, const string_view& ti) {
+ T* data = static_cast<T*>(voiddata);
+ return static_cast<void*>(ti != usertype_traits<T>::qualified_name() ? type_cast_bases(types<Bases...>(), data, ti) : data);
+ }
+
+ template <typename U>
+ static bool type_unique_cast_bases(types<>, void*, void*, const string_view&) {
+ return 0;
+ }
+
+ template <typename U, typename Base, typename... Args>
+ static int type_unique_cast_bases(types<Base, Args...>, void* source_data, void* target_data, const string_view& ti) {
+ using uu_traits = unique_usertype_traits<U>;
+ using base_ptr = typename uu_traits::template rebind_actual_type<Base>;
+ string_view base_ti = usertype_traits<Base>::qualified_name();
+ if (base_ti == ti) {
+ if (target_data != nullptr) {
+ U* source = static_cast<U*>(source_data);
+ base_ptr* target = static_cast<base_ptr*>(target_data);
+ // perform proper derived -> base conversion
+ *target = *source;
+ }
+ return 2;
+ }
+ return type_unique_cast_bases<U>(types<Args...>(), source_data, target_data, ti);
+ }
+
+ template <typename U>
+ static int type_unique_cast(void* source_data, void* target_data, const string_view& ti, const string_view& rebind_ti) {
+ if constexpr (is_actual_type_rebindable_for_v<U>) {
+ using rebound_actual_type = unique_usertype_rebind_actual_t<U>;
+ using maybe_bases_or_empty = meta::conditional_t<std::is_void_v<rebound_actual_type>, types<>, bases_t>;
+ string_view this_rebind_ti = usertype_traits<rebound_actual_type>::qualified_name();
+ if (rebind_ti != this_rebind_ti) {
+ // this is not even of the same unique type
+ return 0;
+ }
+ string_view this_ti = usertype_traits<T>::qualified_name();
+ if (ti == this_ti) {
+ // direct match, return 1
+ return 1;
+ }
+ return type_unique_cast_bases<U>(maybe_bases_or_empty(), source_data, target_data, ti);
+ }
+ else {
+ (void)rebind_ti;
+ string_view this_ti = usertype_traits<T>::qualified_name();
+ if (ti == this_ti) {
+ // direct match, return 1
+ return 1;
+ }
+ return type_unique_cast_bases<U>(types<>(), source_data, target_data, ti);
+ }
+ }
+
+ template <typename U, typename... Bases>
+ static int type_unique_cast_with(void* source_data, void* target_data, const string_view& ti, const string_view& rebind_ti) {
+ using uc_bases_t = types<Bases...>;
+ if constexpr (is_actual_type_rebindable_for_v<U>) {
+ using rebound_actual_type = unique_usertype_rebind_actual_t<U>;
+ using cond_bases_t = meta::conditional_t<std::is_void_v<rebound_actual_type>, types<>, uc_bases_t>;
+ string_view this_rebind_ti = usertype_traits<rebound_actual_type>::qualified_name();
+ if (rebind_ti != this_rebind_ti) {
+ // this is not even of the same unique type
+ return 0;
+ }
+ string_view this_ti = usertype_traits<T>::qualified_name();
+ if (ti == this_ti) {
+ // direct match, return 1
+ return 1;
+ }
+ return type_unique_cast_bases<U>(cond_bases_t(), source_data, target_data, ti);
+ }
+ else {
+ (void)rebind_ti;
+ string_view this_ti = usertype_traits<T>::qualified_name();
+ if (ti == this_ti) {
+ // direct match, return 1
+ return 1;
+ }
+ return type_unique_cast_bases<U>(types<>(), source_data, target_data, ti);
+ }
+ }
+ };
+
+ using inheritance_check_function = decltype(&inheritance<void>::type_check);
+ using inheritance_cast_function = decltype(&inheritance<void>::type_cast);
+ using inheritance_unique_cast_function = decltype(&inheritance<void>::type_unique_cast<void>);
+ } // namespace detail
+} // namespace sol
+
+// end of sol/inheritance.hpp
+
+// beginning of sol/error_handler.hpp
+
+#include <cstdio>
+
+namespace sol {
+
+ namespace detail {
+ constexpr const char* not_a_number = "not a numeric type";
+ constexpr const char* not_a_number_or_number_string = "not a numeric type or numeric string";
+ constexpr const char* not_a_number_integral = "not a numeric type that fits exactly an integer (number maybe has significant decimals)";
+ constexpr const char* not_a_number_or_number_string_integral
+ = "not a numeric type or a numeric string that fits exactly an integer (e.g. number maybe has significant decimals)";
+
+ constexpr const char* not_enough_stack_space = "not enough space left on Lua stack";
+ constexpr const char* not_enough_stack_space_floating = "not enough space left on Lua stack for a floating point number";
+ constexpr const char* not_enough_stack_space_integral = "not enough space left on Lua stack for an integral number";
+ constexpr const char* not_enough_stack_space_string = "not enough space left on Lua stack for a string";
+ constexpr const char* not_enough_stack_space_meta_function_name = "not enough space left on Lua stack for the name of a meta_function";
+ constexpr const char* not_enough_stack_space_userdata = "not enough space left on Lua stack to create a sol2 userdata";
+ constexpr const char* not_enough_stack_space_generic = "not enough space left on Lua stack to push valuees";
+ constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment";
+ constexpr const char* protected_function_error = "caught (...) unknown error during protected_function call";
+
+ inline void accumulate_and_mark(const std::string& n, std::string& aux_message, int& marker) {
+ if (marker > 0) {
+ aux_message += ", ";
+ }
+ aux_message += n;
+ ++marker;
+ }
+ } // namespace detail
+
+ inline std::string associated_type_name(lua_State* L, int index, type t) {
+ switch (t) {
+ case type::poly:
+ return "anything";
+ case type::userdata: {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 2, "not enough space to push get the type name");
+#endif // make sure stack doesn't overflow
+ if (lua_getmetatable(L, index) == 0) {
+ break;
+ }
+ lua_pushlstring(L, "__name", 6);
+ lua_rawget(L, -2);
+ size_t sz;
+ const char* name = lua_tolstring(L, -1, &sz);
+ std::string tn(name, static_cast<std::string::size_type>(sz));
+ lua_pop(L, 2);
+ return tn;
+ }
+ default:
+ break;
+ }
+ return lua_typename(L, static_cast<int>(t));
+ }
+
+ inline int push_type_panic_string(lua_State* L, int index, type expected, type actual, string_view message, string_view aux_message) noexcept {
+ const char* err = message.size() == 0
+ ? (aux_message.size() == 0 ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s: %s")
+ : "stack index %d, expected %s, received %s: %s %s";
+ const char* type_name = expected == type::poly ? "anything" : lua_typename(L, static_cast<int>(expected));
+ {
+ std::string actual_name = associated_type_name(L, index, actual);
+ lua_pushfstring(L, err, index, type_name, actual_name.c_str(), message.data(), aux_message.data());
+ }
+ return 1;
+ }
+
+ inline int type_panic_string(lua_State* L, int index, type expected, type actual, string_view message = "") noexcept(false) {
+ push_type_panic_string(L, index, expected, actual, message, "");
+ return lua_error(L);
+ }
+
+ inline int type_panic_c_str(lua_State* L, int index, type expected, type actual, const char* message = nullptr) noexcept(false) {
+ push_type_panic_string(L, index, expected, actual, message == nullptr ? "" : message, "");
+ return lua_error(L);
+ }
+
+ struct type_panic_t {
+ int operator()(lua_State* L, int index, type expected, type actual) const noexcept(false) {
+ return type_panic_c_str(L, index, expected, actual, nullptr);
+ }
+ int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
+ return type_panic_c_str(L, index, expected, actual, message.data());
+ }
+ };
+
+ const type_panic_t type_panic = {};
+
+ struct constructor_handler {
+ int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
+ push_type_panic_string(L, index, expected, actual, message, "(type check failed in constructor)");
+ return lua_error(L);
+ }
+ };
+
+ template <typename F = void>
+ struct argument_handler {
+ int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
+ push_type_panic_string(L, index, expected, actual, message, "(bad argument to variable or function call)");
+ return lua_error(L);
+ }
+ };
+
+ template <typename R, typename... Args>
+ struct argument_handler<types<R, Args...>> {
+ int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
+ {
+ std::string aux_message = "(bad argument into '";
+ aux_message += detail::demangle<R>();
+ aux_message += "(";
+ int marker = 0;
+ (void)detail::swallow { int(), (detail::accumulate_and_mark(detail::demangle<Args>(), aux_message, marker), int())... };
+ aux_message += ")')";
+ push_type_panic_string(L, index, expected, actual, message, aux_message);
+ }
+ return lua_error(L);
+ }
+ };
+
+ // Specify this function as the handler for lua::check if you know there's nothing wrong
+ inline int no_panic(lua_State*, int, type, type, const char* = nullptr) noexcept {
+ return 0;
+ }
+
+ inline void type_error(lua_State* L, int expected, int actual) noexcept(false) {
+ luaL_error(L, "expected %s, received %s", lua_typename(L, expected), lua_typename(L, actual));
+ }
+
+ inline void type_error(lua_State* L, type expected, type actual) noexcept(false) {
+ type_error(L, static_cast<int>(expected), static_cast<int>(actual));
+ }
+
+ inline void type_assert(lua_State* L, int index, type expected, type actual) noexcept(false) {
+ if (expected != type::poly && expected != actual) {
+ type_panic_c_str(L, index, expected, actual, nullptr);
+ }
+ }
+
+ inline void type_assert(lua_State* L, int index, type expected) {
+ type actual = type_of(L, index);
+ type_assert(L, index, expected, actual);
+ }
+
+} // namespace sol
+
+// end of sol/error_handler.hpp
+
+// beginning of sol/reference.hpp
+
+// beginning of sol/stack_reference.hpp
+
+namespace sol {
+ namespace detail {
+ inline bool xmovable(lua_State* leftL, lua_State* rightL) {
+ if (rightL == nullptr || leftL == nullptr || leftL == rightL) {
+ return false;
+ }
+ const void* leftregistry = lua_topointer(leftL, LUA_REGISTRYINDEX);
+ const void* rightregistry = lua_topointer(rightL, LUA_REGISTRYINDEX);
+ return leftregistry == rightregistry;
+ }
+ } // namespace detail
+
+ class stateless_stack_reference {
+ private:
+ friend class stack_reference;
+
+ int m_index = 0;
+
+ int registry_index() const noexcept {
+ return LUA_NOREF;
+ }
+
+ public:
+ stateless_stack_reference() noexcept = default;
+ stateless_stack_reference(lua_nil_t) noexcept : stateless_stack_reference() {};
+ stateless_stack_reference(lua_State* L_, int index_) noexcept : stateless_stack_reference(absolute_index(L_, index_)) {
+ }
+ stateless_stack_reference(lua_State*, absolute_index index_) noexcept : stateless_stack_reference(index_) {
+ }
+ stateless_stack_reference(lua_State*, raw_index index_) noexcept : stateless_stack_reference(index_) {
+ }
+ stateless_stack_reference(absolute_index index_) noexcept : m_index(index_) {
+ }
+ stateless_stack_reference(raw_index index_) noexcept : m_index(index_) {
+ }
+ stateless_stack_reference(lua_State*, ref_index) noexcept = delete;
+ stateless_stack_reference(ref_index) noexcept = delete;
+ stateless_stack_reference(const reference&) noexcept = delete;
+ stateless_stack_reference(const stateless_stack_reference&) noexcept = default;
+ stateless_stack_reference(stateless_stack_reference&& o) noexcept = default;
+ stateless_stack_reference& operator=(stateless_stack_reference&&) noexcept = default;
+ stateless_stack_reference& operator=(const stateless_stack_reference&) noexcept = default;
+
+ int push(lua_State* L_) const noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push a single reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L_, m_index);
+ return 1;
+ }
+
+ void pop(lua_State* L_, int pop_count = 1) const noexcept {
+ lua_pop(L_, pop_count);
+ }
+
+ int stack_index() const noexcept {
+ return m_index;
+ }
+
+ const void* pointer(lua_State* L_) const noexcept {
+ const void* pointer_id = lua_topointer(L_, stack_index());
+ return pointer_id;
+ }
+
+ type get_type(lua_State* L_) const noexcept {
+ int untyped_value = lua_type(L_, stack_index());
+ return static_cast<type>(untyped_value);
+ }
+
+ bool valid(lua_State* L) const noexcept {
+ type t = get_type(L);
+ return t != type::lua_nil && t != type::none;
+ }
+
+ void reset(lua_State*) noexcept {
+ m_index = 0;
+ }
+
+ void reset(lua_State* L_, int index_) noexcept {
+ m_index = absolute_index(L_, index_);
+ }
+
+ void abandon(lua_State* = nullptr) noexcept {
+ m_index = 0;
+ }
+
+ stateless_stack_reference copy(lua_State* L_) const noexcept {
+ return stateless_stack_reference(L_, raw_index(m_index));
+ }
+
+ void copy_assign(lua_State*, const stateless_stack_reference& right) noexcept {
+ m_index = right.m_index;
+ }
+
+ bool equals(lua_State* L_, const stateless_stack_reference& r) const noexcept {
+ return lua_compare(L_, this->stack_index(), r.stack_index(), LUA_OPEQ) == 1;
+ }
+
+ bool equals(lua_State* L_, lua_nil_t) const noexcept {
+ return valid(L_);
+ }
+ };
+
+ class stack_reference : public stateless_stack_reference {
+ private:
+ lua_State* luastate = nullptr;
+
+ public:
+ stack_reference() noexcept = default;
+ stack_reference(lua_nil_t) noexcept : stack_reference() {};
+ stack_reference(lua_State* L, lua_nil_t) noexcept : stateless_stack_reference(L, 0), luastate(L) {
+ }
+ stack_reference(lua_State* L, int i) noexcept : stateless_stack_reference(L, i), luastate(L) {
+ }
+ stack_reference(lua_State* L, absolute_index i) noexcept : stateless_stack_reference(L, i), luastate(L) {
+ }
+ stack_reference(lua_State* L, raw_index i) noexcept : stateless_stack_reference(L, i), luastate(L) {
+ }
+ stack_reference(lua_State* L, ref_index i) noexcept = delete;
+ stack_reference(lua_State* L, const reference& r) noexcept = delete;
+ stack_reference(lua_State* L, const stack_reference& r) noexcept : luastate(L) {
+ if (!r.valid()) {
+ m_index = 0;
+ return;
+ }
+ int i = r.stack_index();
+ if (detail::xmovable(lua_state(), r.lua_state())) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, "not enough Lua stack space to push a single reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(r.lua_state(), r.stack_index());
+ lua_xmove(r.lua_state(), luastate, 1);
+ i = absolute_index(luastate, -1);
+ }
+ m_index = i;
+ }
+ stack_reference(stack_reference&& o) noexcept = default;
+ stack_reference& operator=(stack_reference&&) noexcept = default;
+ stack_reference(const stack_reference&) noexcept = default;
+ stack_reference& operator=(const stack_reference&) noexcept = default;
+
+ int push() const noexcept {
+ return push(lua_state());
+ }
+
+ int push(lua_State* L_) const noexcept {
+ return stateless_stack_reference::push(L_);
+ }
+
+ void pop() const noexcept {
+ pop(lua_state());
+ }
+
+ void pop(lua_State* L_, int pop_count_ = 1) const noexcept {
+ stateless_stack_reference::pop(L_, pop_count_);
+ }
+
+ const void* pointer() const noexcept {
+ return stateless_stack_reference::pointer(lua_state());
+ }
+
+ type get_type() const noexcept {
+ return stateless_stack_reference::get_type(lua_state());
+ }
+
+ lua_State* lua_state() const noexcept {
+ return luastate;
+ }
+
+ bool valid() const noexcept {
+ return stateless_stack_reference::valid(lua_state());
+ }
+
+ void abandon() {
+ stateless_stack_reference::abandon(lua_state());
+ }
+ };
+
+ inline bool operator==(const stack_reference& l, const stack_reference& r) {
+ return lua_compare(l.lua_state(), l.stack_index(), r.stack_index(), LUA_OPEQ) == 1;
+ }
+
+ inline bool operator!=(const stack_reference& l, const stack_reference& r) {
+ return !operator==(l, r);
+ }
+
+ inline bool operator==(const stack_reference& lhs, const lua_nil_t&) {
+ return !lhs.valid();
+ }
+
+ inline bool operator==(const lua_nil_t&, const stack_reference& rhs) {
+ return !rhs.valid();
+ }
+
+ inline bool operator!=(const stack_reference& lhs, const lua_nil_t&) {
+ return lhs.valid();
+ }
+
+ inline bool operator!=(const lua_nil_t&, const stack_reference& rhs) {
+ return rhs.valid();
+ }
+
+ inline bool operator==(const stateless_stack_reference& l, const stateless_stack_reference& r) {
+ return l.stack_index() == r.stack_index();
+ }
+
+ inline bool operator!=(const stateless_stack_reference& l, const stateless_stack_reference& r) {
+ return l.stack_index() != r.stack_index();
+ }
+
+ struct stateless_stack_reference_equals {
+ using is_transparent = std::true_type;
+
+ stateless_stack_reference_equals(lua_State* L_) noexcept : m_L(L_) {
+ }
+
+ lua_State* lua_state() const noexcept {
+ return m_L;
+ }
+
+ bool operator()(const stateless_stack_reference& lhs, const stateless_stack_reference& rhs) const {
+ return lhs.equals(lua_state(), rhs);
+ }
+
+ bool operator()(lua_nil_t lhs, const stateless_stack_reference& rhs) const {
+ return rhs.equals(lua_state(), lhs);
+ }
+
+ bool operator()(const stateless_stack_reference& lhs, lua_nil_t rhs) const {
+ return lhs.equals(lua_state(), rhs);
+ }
+
+ private:
+ lua_State* m_L;
+ };
+
+ struct stack_reference_equals {
+ using is_transparent = std::true_type;
+
+ bool operator()(const lua_nil_t& lhs, const stack_reference& rhs) const {
+ return lhs == rhs;
+ }
+
+ bool operator()(const stack_reference& lhs, const lua_nil_t& rhs) const {
+ return lhs == rhs;
+ }
+
+ bool operator()(const stack_reference& lhs, const stack_reference& rhs) const {
+ return lhs == rhs;
+ }
+ };
+
+ struct stateless_stack_reference_hash {
+ using argument_type = stateless_stack_reference;
+ using result_type = std::size_t;
+ using is_transparent = std::true_type;
+
+ stateless_stack_reference_hash(lua_State* L_) noexcept : m_L(L_) {
+ }
+
+ lua_State* lua_state() const noexcept {
+ return m_L;
+ }
+
+ result_type operator()(const argument_type& lhs) const noexcept {
+ std::hash<const void*> h;
+ return h(lhs.pointer(lua_state()));
+ }
+
+ private:
+ lua_State* m_L;
+ };
+
+ struct stack_reference_hash {
+ using argument_type = stack_reference;
+ using result_type = std::size_t;
+ using is_transparent = std::true_type;
+
+ result_type operator()(const argument_type& lhs) const noexcept {
+ std::hash<const void*> h;
+ return h(lhs.pointer());
+ }
+ };
+} // namespace sol
+
+// end of sol/stack_reference.hpp
+
+#include <functional>
+
+namespace sol {
+ namespace detail {
+ inline const char (&default_main_thread_name())[9] {
+ static const char name[9] = "sol.\xF0\x9F\x93\x8C";
+ return name;
+ }
+ } // namespace detail
+
+ namespace stack {
+ inline void remove(lua_State* L_, int rawindex, int count) {
+ if (count < 1)
+ return;
+ int top = lua_gettop(L_);
+ if (top < 1) {
+ return;
+ }
+ if (rawindex == -count || top == rawindex) {
+ // Slice them right off the top
+ lua_pop(L_, static_cast<int>(count));
+ return;
+ }
+
+ // Remove each item one at a time using stack operations
+ // Probably slower, maybe, haven't benchmarked,
+ // but necessary
+ int index = lua_absindex(L_, rawindex);
+ if (index < 0) {
+ index = lua_gettop(L_) + (index + 1);
+ }
+ int last = index + count;
+ for (int i = index; i < last; ++i) {
+ lua_remove(L_, index);
+ }
+ }
+
+ struct push_popper_at {
+ lua_State* L;
+ int index;
+ int count;
+ push_popper_at(lua_State* L_, int index_ = -1, int count_ = 1) : L(L_), index(index_), count(count_) {
+ }
+ ~push_popper_at() {
+ remove(L, index, count);
+ }
+ };
+
+ template <bool top_level>
+ struct push_popper_n {
+ lua_State* L;
+ int pop_count;
+ push_popper_n(lua_State* L_, int pop_count_) : L(L_), pop_count(pop_count_) {
+ }
+ push_popper_n(const push_popper_n&) = delete;
+ push_popper_n(push_popper_n&&) = default;
+ push_popper_n& operator=(const push_popper_n&) = delete;
+ push_popper_n& operator=(push_popper_n&&) = default;
+ ~push_popper_n() {
+ lua_pop(L, pop_count);
+ }
+ };
+
+ template <>
+ struct push_popper_n<true> {
+ push_popper_n(lua_State*, int) {
+ }
+ };
+
+ template <bool, typename T, typename = void>
+ struct push_popper {
+ using Tu = meta::unqualified_t<T>;
+ T m_object;
+ int m_index;
+
+ push_popper(T object_) noexcept : m_object(object_), m_index(lua_absindex(m_object.lua_state(), -m_object.push())) {
+ }
+
+ int index_of(const Tu&) const noexcept {
+ return m_index;
+ }
+
+ ~push_popper() {
+ m_object.pop();
+ }
+ };
+
+ template <typename T, typename C>
+ struct push_popper<true, T, C> {
+ using Tu = meta::unqualified_t<T>;
+
+ push_popper(T) noexcept {
+ }
+
+ int index_of(const Tu&) const noexcept {
+ return -1;
+ }
+
+ ~push_popper() {
+ }
+ };
+
+ template <typename T>
+ struct push_popper<false, T, std::enable_if_t<is_stack_based_v<meta::unqualified_t<T>>>> {
+ using Tu = meta::unqualified_t<T>;
+
+ push_popper(T) noexcept {
+ }
+
+ int index_of(const Tu& object_) const noexcept {
+ return object_.stack_index();
+ }
+
+ ~push_popper() {
+ }
+ };
+
+ template <bool, typename T, typename = void>
+ struct stateless_push_popper {
+ using Tu = meta::unqualified_t<T>;
+ lua_State* m_L;
+ T m_object;
+ int m_index;
+
+ stateless_push_popper(lua_State* L_, T object_) noexcept : m_L(L_), m_object(object_), m_index(lua_absindex(m_L, -m_object.push(m_L))) {
+ }
+
+ int index_of(const Tu&) const noexcept {
+ return m_index;
+ }
+
+ ~stateless_push_popper() {
+ m_object.pop(m_L);
+ }
+ };
+
+ template <typename T, typename C>
+ struct stateless_push_popper<true, T, C> {
+ using Tu = meta::unqualified_t<T>;
+
+ stateless_push_popper(lua_State*, T) noexcept {
+ }
+
+ int index_of(lua_State*, const Tu&) const noexcept {
+ return -1;
+ }
+
+ ~stateless_push_popper() {
+ }
+ };
+
+ template <typename T>
+ struct stateless_push_popper<false, T, std::enable_if_t<is_stack_based_v<meta::unqualified_t<T>>>> {
+ using Tu = meta::unqualified_t<T>;
+ lua_State* m_L;
+
+ stateless_push_popper(lua_State* L_, T) noexcept : m_L(L_) {
+ }
+
+ int index_of(const Tu& object_) const noexcept {
+ return object_.stack_index();
+ }
+
+ ~stateless_push_popper() {
+ }
+ };
+
+ template <bool top_level = false, typename T>
+ push_popper<top_level, T> push_pop(T&& x) {
+ return push_popper<top_level, T>(std::forward<T>(x));
+ }
+
+ template <bool top_level = false, typename T>
+ stateless_push_popper<top_level, T> push_pop(lua_State* L_, T&& object_) {
+ return stateless_push_popper<top_level, T>(L_, std::forward<T>(object_));
+ }
+
+ template <typename T>
+ push_popper_at push_pop_at(T&& object_) {
+ int push_count = object_.push();
+ lua_State* L = object_.lua_state();
+ return push_popper_at(L, lua_absindex(L, -push_count), push_count);
+ }
+
+ template <bool top_level = false>
+ push_popper_n<top_level> pop_n(lua_State* L_, int pop_count_) {
+ return push_popper_n<top_level>(L_, pop_count_);
+ }
+ } // namespace stack
+
+ inline lua_State* main_thread(lua_State* L_, lua_State* backup_if_unsupported_ = nullptr) {
+#if SOL_LUA_VERSION_I_ < 502
+ if (L_ == nullptr)
+ return backup_if_unsupported_;
+ lua_getglobal(L_, detail::default_main_thread_name());
+ auto pp = stack::pop_n(L_, 1);
+ if (type_of(L_, -1) == type::thread) {
+ return lua_tothread(L_, -1);
+ }
+ return backup_if_unsupported_;
+#else
+ if (L_ == nullptr)
+ return backup_if_unsupported_;
+ lua_rawgeti(L_, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
+ lua_State* Lmain = lua_tothread(L_, -1);
+ lua_pop(L_, 1);
+ return Lmain;
+#endif // Lua 5.2+ has the main thread unqualified_getter
+ }
+
+ namespace detail {
+ struct no_safety_tag {
+ } inline constexpr no_safety {};
+
+ template <bool b>
+ inline lua_State* pick_main_thread(lua_State* L_, lua_State* backup_if_unsupported = nullptr) {
+ (void)L_;
+ (void)backup_if_unsupported;
+ if (b) {
+ return main_thread(L_, backup_if_unsupported);
+ }
+ return L_;
+ }
+ } // namespace detail
+
+ class stateless_reference {
+ private:
+ template <bool o_main_only>
+ friend class basic_reference;
+
+ int ref = LUA_NOREF;
+
+ int copy_ref(lua_State* L_) const noexcept {
+ if (ref == LUA_NOREF)
+ return LUA_NOREF;
+ push(L_);
+ return luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+
+ lua_State* copy_assign_ref(lua_State* L_, lua_State* rL, const stateless_reference& r) {
+ if (valid(L_)) {
+ deref(L_);
+ }
+ ref = r.copy_ref(L_);
+ return rL;
+ }
+
+ lua_State* move_assign(lua_State* L_, lua_State* rL, stateless_reference&& r) {
+ if (valid(L_)) {
+ deref(L_);
+ }
+ ref = r.ref;
+ r.ref = LUA_NOREF;
+ return rL;
+ }
+
+ protected:
+ int stack_index() const noexcept {
+ return -1;
+ }
+
+ stateless_reference(lua_State* L_, global_tag_t) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushglobaltable(L_);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+
+ stateless_reference(int raw_ref_index) noexcept : ref(raw_ref_index) {
+ }
+
+ public:
+ stateless_reference() noexcept = default;
+ stateless_reference(lua_nil_t) noexcept : stateless_reference() {
+ }
+ stateless_reference(const stack_reference& r) noexcept : stateless_reference(r.lua_state(), r.stack_index()) {
+ }
+ stateless_reference(stack_reference&& r) noexcept : stateless_reference(r.lua_state(), r.stack_index()) {
+ }
+ stateless_reference(lua_State* L_, const stateless_reference& r) noexcept {
+ if (r.ref == LUA_REFNIL) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF || L_ == nullptr) {
+ ref = LUA_NOREF;
+ return;
+ }
+ ref = r.copy_ref(L_);
+ }
+
+ stateless_reference(lua_State* L_, stateless_reference&& r) noexcept {
+ if (r.ref == LUA_REFNIL) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF || L_ == nullptr) {
+ ref = LUA_NOREF;
+ return;
+ }
+ ref = r.ref;
+ r.ref = LUA_NOREF;
+ }
+
+ stateless_reference(lua_State* L_, const stack_reference& r) noexcept {
+ if (L_ == nullptr || r.lua_state() == nullptr || r.get_type() == type::none) {
+ ref = LUA_NOREF;
+ return;
+ }
+ if (r.get_type() == type::lua_nil) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (L_ != r.lua_state() && !detail::xmovable(L_, r.lua_state())) {
+ return;
+ }
+ r.push(L_);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+
+ stateless_reference(lua_State* L_, const stateless_stack_reference& r) noexcept : stateless_reference(L_, r.stack_index()) {
+ }
+
+ stateless_reference(lua_State* L_, int index = -1) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L_, index);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+ stateless_reference(lua_State* L_, absolute_index index_) noexcept : stateless_reference(L_, index_.index) {
+ }
+ stateless_reference(lua_State* L_, ref_index index_) noexcept {
+ lua_rawgeti(L_, LUA_REGISTRYINDEX, index_.index);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+ stateless_reference(lua_State*, lua_nil_t) noexcept {
+ }
+
+ ~stateless_reference() noexcept = default;
+
+ stateless_reference(const stateless_reference& o) noexcept = delete;
+ stateless_reference& operator=(const stateless_reference& r) noexcept = delete;
+
+ stateless_reference(stateless_reference&& o) noexcept : ref(o.ref) {
+ o.ref = LUA_NOREF;
+ }
+
+ stateless_reference& operator=(stateless_reference&& o) noexcept {
+ ref = o.ref;
+ o.ref = LUA_NOREF;
+ return *this;
+ }
+
+ int push(lua_State* L_) const noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ lua_rawgeti(L_, LUA_REGISTRYINDEX, ref);
+ return 1;
+ }
+
+ void pop(lua_State* L_, int n = 1) const noexcept {
+ lua_pop(L_, n);
+ }
+
+ int registry_index() const noexcept {
+ return ref;
+ }
+
+ void reset(lua_State* L_) noexcept {
+ if (valid(L_)) {
+ deref(L_);
+ }
+ ref = LUA_NOREF;
+ }
+
+ void reset(lua_State* L_, int index_) noexcept {
+ reset(L_);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L_, index_);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+
+ bool valid(lua_State*) const noexcept {
+ return !(ref == LUA_NOREF || ref == LUA_REFNIL);
+ }
+
+ const void* pointer(lua_State* L_) const noexcept {
+ int si = push(L_);
+ const void* vp = lua_topointer(L_, -si);
+ lua_pop(L_, si);
+ return vp;
+ }
+
+ type get_type(lua_State* L_) const noexcept {
+ int p = push(L_);
+ int result = lua_type(L_, -1);
+ pop(L_, p);
+ return static_cast<type>(result);
+ }
+
+ void abandon(lua_State* = nullptr) {
+ ref = LUA_NOREF;
+ }
+
+ void deref(lua_State* L_) const noexcept {
+ luaL_unref(L_, LUA_REGISTRYINDEX, ref);
+ }
+
+ stateless_reference copy(lua_State* L_) const noexcept {
+ if (!valid(L_)) {
+ return {};
+ }
+ return stateless_reference(copy_ref(L_));
+ }
+
+ void copy_assign(lua_State* L_, const stateless_reference& right) noexcept {
+ if (valid(L_)) {
+ deref(L_);
+ }
+ if (!right.valid(L_)) {
+ return;
+ }
+ ref = right.copy_ref(L_);
+ }
+
+ bool equals(lua_State* L_, const stateless_reference& r) const noexcept {
+ auto ppl = stack::push_pop(L_, *this);
+ auto ppr = stack::push_pop(L_, r);
+ return lua_compare(L_, -1, -2, LUA_OPEQ) == 1;
+ }
+
+ bool equals(lua_State* L_, const stateless_stack_reference& r) const noexcept {
+ auto ppl = stack::push_pop(L_, *this);
+ return lua_compare(L_, -1, r.stack_index(), LUA_OPEQ) == 1;
+ }
+
+ bool equals(lua_State* L_, lua_nil_t) const noexcept {
+ return valid(L_);
+ }
+ };
+
+ template <bool main_only = false>
+ class basic_reference : public stateless_reference {
+ private:
+ template <bool o_main_only>
+ friend class basic_reference;
+ lua_State* luastate = nullptr; // non-owning
+
+ template <bool r_main_only>
+ void copy_assign_complex(const basic_reference<r_main_only>& r) {
+ if (valid()) {
+ deref();
+ }
+ if (r.ref == LUA_REFNIL) {
+ luastate = detail::pick_main_thread < main_only && !r_main_only > (r.lua_state(), r.lua_state());
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF) {
+ luastate = r.luastate;
+ ref = LUA_NOREF;
+ return;
+ }
+ if (detail::xmovable(lua_state(), r.lua_state())) {
+ r.push(lua_state());
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ return;
+ }
+ luastate = detail::pick_main_thread < main_only && !r_main_only > (r.lua_state(), r.lua_state());
+ ref = r.copy_ref();
+ }
+
+ template <bool r_main_only>
+ void move_assign(basic_reference<r_main_only>&& r) {
+ if (valid()) {
+ deref();
+ }
+ if (r.ref == LUA_REFNIL) {
+ luastate = detail::pick_main_thread < main_only && !r_main_only > (r.lua_state(), r.lua_state());
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF) {
+ luastate = r.luastate;
+ ref = LUA_NOREF;
+ return;
+ }
+ if (detail::xmovable(lua_state(), r.lua_state())) {
+ r.push(lua_state());
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ return;
+ }
+
+ luastate = detail::pick_main_thread < main_only && !r_main_only > (r.lua_state(), r.lua_state());
+ ref = r.ref;
+ r.ref = LUA_NOREF;
+ r.luastate = nullptr;
+ }
+
+ protected:
+ basic_reference(lua_State* L_, global_tag_t) noexcept : basic_reference(detail::pick_main_thread<main_only>(L_, L_), global_tag, global_tag) {
+ }
+
+ basic_reference(lua_State* L_, global_tag_t, global_tag_t) noexcept : stateless_reference(L_, global_tag), luastate(L_) {
+ }
+
+ basic_reference(lua_State* oL, const basic_reference<!main_only>& o) noexcept : stateless_reference(oL, o), luastate(oL) {
+ }
+
+ void deref() const noexcept {
+ return stateless_reference::deref(lua_state());
+ }
+
+ int copy_ref() const noexcept {
+ return copy_ref(lua_state());
+ }
+
+ int copy_ref(lua_State* L_) const noexcept {
+ return stateless_reference::copy_ref(L_);
+ }
+
+ public:
+ basic_reference() noexcept = default;
+ basic_reference(lua_nil_t) noexcept : basic_reference() {
+ }
+ basic_reference(const stack_reference& r) noexcept : basic_reference(r.lua_state(), r.stack_index()) {
+ }
+ basic_reference(stack_reference&& r) noexcept : basic_reference(r.lua_state(), r.stack_index()) {
+ }
+ template <bool r_main_only>
+ basic_reference(lua_State* L_, const basic_reference<r_main_only>& r) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ if (r.ref == LUA_REFNIL) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF || lua_state() == nullptr) {
+ ref = LUA_NOREF;
+ return;
+ }
+ if (detail::xmovable(lua_state(), r.lua_state())) {
+ r.push(lua_state());
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ return;
+ }
+ ref = r.copy_ref();
+ }
+
+ template <bool r_main_only>
+ basic_reference(lua_State* L_, basic_reference<r_main_only>&& r) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ if (r.ref == LUA_REFNIL) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF || lua_state() == nullptr) {
+ ref = LUA_NOREF;
+ return;
+ }
+ if (detail::xmovable(lua_state(), r.lua_state())) {
+ r.push(lua_state());
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ return;
+ }
+ ref = r.ref;
+ r.ref = LUA_NOREF;
+ r.luastate = nullptr;
+ }
+
+ basic_reference(lua_State* L_, const stack_reference& r) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ if (lua_state() == nullptr || r.lua_state() == nullptr || r.get_type() == type::none) {
+ ref = LUA_NOREF;
+ return;
+ }
+ if (r.get_type() == type::lua_nil) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (lua_state() != r.lua_state() && !detail::xmovable(lua_state(), r.lua_state())) {
+ return;
+ }
+ r.push(lua_state());
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ }
+ basic_reference(lua_State* L_, int index = -1) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ // use L_ to stick with that state's execution stack
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L_, index);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+ basic_reference(lua_State* L_, ref_index index) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ lua_rawgeti(lua_state(), LUA_REGISTRYINDEX, index.index);
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ }
+ basic_reference(lua_State* L_, lua_nil_t) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ }
+
+ ~basic_reference() noexcept {
+ if (lua_state() == nullptr || ref == LUA_NOREF)
+ return;
+ deref();
+ }
+
+ basic_reference(const basic_reference& o) noexcept : stateless_reference(o.copy_ref()), luastate(o.lua_state()) {
+ }
+
+ basic_reference(basic_reference&& o) noexcept : stateless_reference(std::move(o)), luastate(o.lua_state()) {
+ o.luastate = nullptr;
+ }
+
+ basic_reference(const basic_reference<!main_only>& o) noexcept
+ : basic_reference(detail::pick_main_thread<main_only>(o.lua_state(), o.lua_state()), o) {
+ }
+
+ basic_reference(basic_reference<!main_only>&& o) noexcept
+ : stateless_reference(std::move(o)), luastate(detail::pick_main_thread<main_only>(o.lua_state(), o.lua_state())) {
+ o.luastate = nullptr;
+ o.ref = LUA_NOREF;
+ }
+
+ basic_reference& operator=(basic_reference&& r) noexcept {
+ move_assign(std::move(r));
+ return *this;
+ }
+
+ basic_reference& operator=(const basic_reference& r) noexcept {
+ copy_assign_complex(r);
+ return *this;
+ }
+
+ basic_reference& operator=(basic_reference<!main_only>&& r) noexcept {
+ move_assign(std::move(r));
+ return *this;
+ }
+
+ basic_reference& operator=(const basic_reference<!main_only>& r) noexcept {
+ copy_assign_complex(r);
+ return *this;
+ }
+
+ basic_reference& operator=(const lua_nil_t&) noexcept {
+ reset();
+ return *this;
+ }
+
+ template <typename Super>
+ basic_reference& operator=(proxy_base<Super>&& r);
+
+ template <typename Super>
+ basic_reference& operator=(const proxy_base<Super>& r);
+
+ int push() const noexcept {
+ return push(lua_state());
+ }
+
+ void reset() noexcept {
+ stateless_reference::reset(luastate);
+ luastate = nullptr;
+ }
+
+ int push(lua_State* L_) const noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ if (lua_state() == nullptr) {
+ lua_pushnil(L_);
+ return 1;
+ }
+ lua_rawgeti(lua_state(), LUA_REGISTRYINDEX, ref);
+ if (L_ != lua_state()) {
+ lua_xmove(lua_state(), L_, 1);
+ }
+ return 1;
+ }
+
+ void pop() const noexcept {
+ pop(lua_state());
+ }
+
+ void pop(lua_State* L_, int n = 1) const noexcept {
+ stateless_reference::pop(L_, n);
+ }
+
+ int registry_index() const noexcept {
+ return stateless_reference::registry_index();
+ }
+
+ bool valid() const noexcept {
+ return stateless_reference::valid(lua_state());
+ }
+
+ bool valid(lua_State* L_) const noexcept {
+ return stateless_reference::valid(L_);
+ }
+
+ const void* pointer() const noexcept {
+ return stateless_reference::pointer(lua_state());
+ }
+
+ explicit operator bool() const noexcept {
+ return valid();
+ }
+
+ type get_type() const noexcept {
+ return stateless_reference::get_type(lua_state());
+ }
+
+ lua_State* lua_state() const noexcept {
+ return luastate;
+ }
+ };
+
+ template <bool lb, bool rb>
+ inline bool operator==(const basic_reference<lb>& l, const basic_reference<rb>& r) noexcept {
+ auto ppl = stack::push_pop(l);
+ auto ppr = stack::push_pop(r);
+ return lua_compare(l.lua_state(), -1, -2, LUA_OPEQ) == 1;
+ }
+
+ template <bool lb, bool rb>
+ inline bool operator!=(const basic_reference<lb>& l, const basic_reference<rb>& r) noexcept {
+ return !operator==(l, r);
+ }
+
+ template <bool lb>
+ inline bool operator==(const basic_reference<lb>& l, const stack_reference& r) noexcept {
+ auto ppl = stack::push_pop(l);
+ return lua_compare(l.lua_state(), -1, r.stack_index(), LUA_OPEQ) == 1;
+ }
+
+ template <bool lb>
+ inline bool operator!=(const basic_reference<lb>& l, const stack_reference& r) noexcept {
+ return !operator==(l, r);
+ }
+
+ template <bool rb>
+ inline bool operator==(const stack_reference& l, const basic_reference<rb>& r) noexcept {
+ auto ppr = stack::push_pop(r);
+ return lua_compare(l.lua_state(), -1, r.stack_index(), LUA_OPEQ) == 1;
+ }
+
+ template <bool rb>
+ inline bool operator!=(const stack_reference& l, const basic_reference<rb>& r) noexcept {
+ return !operator==(l, r);
+ }
+
+ template <bool lb>
+ inline bool operator==(const basic_reference<lb>& lhs, const lua_nil_t&) noexcept {
+ return !lhs.valid();
+ }
+
+ template <bool rb>
+ inline bool operator==(const lua_nil_t&, const basic_reference<rb>& rhs) noexcept {
+ return !rhs.valid();
+ }
+
+ template <bool lb>
+ inline bool operator!=(const basic_reference<lb>& lhs, const lua_nil_t&) noexcept {
+ return lhs.valid();
+ }
+
+ template <bool rb>
+ inline bool operator!=(const lua_nil_t&, const basic_reference<rb>& rhs) noexcept {
+ return rhs.valid();
+ }
+
+ inline bool operator==(const stateless_reference& l, const stateless_reference& r) noexcept {
+ return l.registry_index() == r.registry_index();
+ }
+
+ inline bool operator!=(const stateless_reference& l, const stateless_reference& r) noexcept {
+ return l.registry_index() != r.registry_index();
+ }
+
+ inline bool operator==(const stateless_reference& lhs, const lua_nil_t&) noexcept {
+ return lhs.registry_index() == LUA_REFNIL;
+ }
+
+ inline bool operator==(const lua_nil_t&, const stateless_reference& rhs) noexcept {
+ return rhs.registry_index() == LUA_REFNIL;
+ }
+
+ inline bool operator!=(const stateless_reference& lhs, const lua_nil_t&) noexcept {
+ return lhs.registry_index() != LUA_REFNIL;
+ }
+
+ inline bool operator!=(const lua_nil_t&, const stateless_reference& rhs) noexcept {
+ return rhs.registry_index() != LUA_REFNIL;
+ }
+
+ struct stateless_reference_equals : public stateless_stack_reference_equals {
+ using is_transparent = std::true_type;
+
+ stateless_reference_equals(lua_State* L_) noexcept : stateless_stack_reference_equals(L_) {
+ }
+
+ bool operator()(const lua_nil_t& lhs, const stateless_reference& rhs) const noexcept {
+ return rhs.equals(lua_state(), lhs);
+ }
+
+ bool operator()(const stateless_reference& lhs, const lua_nil_t& rhs) const noexcept {
+ return lhs.equals(lua_state(), rhs);
+ }
+
+ bool operator()(const stateless_reference& lhs, const stateless_reference& rhs) const noexcept {
+ return lhs.equals(lua_state(), rhs);
+ }
+ };
+
+ struct reference_equals : public stack_reference_equals {
+ using is_transparent = std::true_type;
+
+ template <bool rb>
+ bool operator()(const lua_nil_t& lhs, const basic_reference<rb>& rhs) const noexcept {
+ return lhs == rhs;
+ }
+
+ template <bool lb>
+ bool operator()(const basic_reference<lb>& lhs, const lua_nil_t& rhs) const noexcept {
+ return lhs == rhs;
+ }
+
+ template <bool lb, bool rb>
+ bool operator()(const basic_reference<lb>& lhs, const basic_reference<rb>& rhs) const noexcept {
+ return lhs == rhs;
+ }
+
+ template <bool lb>
+ bool operator()(const basic_reference<lb>& lhs, const stack_reference& rhs) const noexcept {
+ return lhs == rhs;
+ }
+
+ template <bool rb>
+ bool operator()(const stack_reference& lhs, const basic_reference<rb>& rhs) const noexcept {
+ return lhs == rhs;
+ }
+ };
+
+ struct stateless_reference_hash : public stateless_stack_reference_hash {
+ using argument_type = stateless_reference;
+ using result_type = std::size_t;
+ using is_transparent = std::true_type;
+
+ stateless_reference_hash(lua_State* L_) noexcept : stateless_stack_reference_hash(L_) {
+ }
+
+ result_type operator()(const stateless_reference& lhs) const noexcept {
+ std::hash<const void*> h;
+ return h(lhs.pointer(lua_state()));
+ }
+ };
+
+ struct reference_hash : public stack_reference_hash {
+ using argument_type = reference;
+ using result_type = std::size_t;
+ using is_transparent = std::true_type;
+
+ template <bool lb>
+ result_type operator()(const basic_reference<lb>& lhs) const noexcept {
+ std::hash<const void*> h;
+ return h(lhs.pointer());
+ }
+ };
+} // namespace sol
+
+// end of sol/reference.hpp
+
+// beginning of sol/tie.hpp
+
+namespace sol {
+
+ namespace detail {
+ template <typename T>
+ struct is_speshul : std::false_type { };
+ } // namespace detail
+
+ template <typename T>
+ struct tie_size : std::tuple_size<T> { };
+
+ template <typename T>
+ struct is_tieable : std::integral_constant<bool, (::sol::tie_size<T>::value > 0)> { };
+
+ template <typename... Tn>
+ struct tie_t : public std::tuple<std::add_lvalue_reference_t<Tn>...> {
+ private:
+ typedef std::tuple<std::add_lvalue_reference_t<Tn>...> base_t;
+
+ template <typename T>
+ void set(std::false_type, T&& target) {
+ std::get<0>(*this) = std::forward<T>(target);
+ }
+
+ template <typename T>
+ void set(std::true_type, T&& target) {
+ typedef tie_size<meta::unqualified_t<T>> value_size;
+ typedef tie_size<std::tuple<Tn...>> tie_size;
+ typedef meta::conditional_t<(value_size::value < tie_size::value), value_size, tie_size> indices_size;
+ typedef std::make_index_sequence<indices_size::value> indices;
+ set_extra(detail::is_speshul<meta::unqualified_t<T>>(), indices(), std::forward<T>(target));
+ }
+
+ template <std::size_t... I, typename T>
+ void set_extra(std::true_type, std::index_sequence<I...>, T&& target) {
+ using std::get;
+ (void)detail::swallow { 0, (get<I>(static_cast<base_t&>(*this)) = get<I>(types<Tn...>(), target), 0)..., 0 };
+ }
+
+ template <std::size_t... I, typename T>
+ void set_extra(std::false_type, std::index_sequence<I...>, T&& target) {
+ using std::get;
+ (void)detail::swallow { 0, (get<I>(static_cast<base_t&>(*this)) = get<I>(target), 0)..., 0 };
+ }
+
+ public:
+ using base_t::base_t;
+
+ template <typename T>
+ tie_t& operator=(T&& value) {
+ typedef is_tieable<meta::unqualified_t<T>> tieable;
+ set(tieable(), std::forward<T>(value));
+ return *this;
+ }
+ };
+
+ template <typename... Tn>
+ struct tie_size<tie_t<Tn...>> : std::tuple_size<std::tuple<Tn...>> { };
+
+ namespace adl_barrier_detail {
+ template <typename... Tn>
+ inline tie_t<std::remove_reference_t<Tn>...> tie(Tn&&... argn) {
+ return tie_t<std::remove_reference_t<Tn>...>(std::forward<Tn>(argn)...);
+ }
+ } // namespace adl_barrier_detail
+
+ using namespace adl_barrier_detail;
+
+} // namespace sol
+
+// end of sol/tie.hpp
+
+// beginning of sol/stack_guard.hpp
+
+#include <functional>
+
+namespace sol {
+ namespace detail {
+ inline void stack_fail(int, int) {
+#if SOL_IS_ON(SOL_EXCEPTIONS)
+ throw error(detail::direct_error, "imbalanced stack after operation finish");
+#else
+ // Lol, what do you want, an error printout? :3c
+ // There's no sane default here. The right way would be C-style abort(), and that's not acceptable, so
+ // hopefully someone will register their own stack_fail thing for the `fx` parameter of stack_guard.
+#endif // No Exceptions
+ }
+ } // namespace detail
+
+ struct stack_guard {
+ lua_State* L;
+ int top;
+ std::function<void(int, int)> on_mismatch;
+
+ stack_guard(lua_State* L) : stack_guard(L, lua_gettop(L)) {
+ }
+ stack_guard(lua_State* L, int top, std::function<void(int, int)> fx = detail::stack_fail) : L(L), top(top), on_mismatch(std::move(fx)) {
+ }
+ bool check_stack(int modification = 0) const {
+ int bottom = lua_gettop(L) + modification;
+ if (top == bottom) {
+ return true;
+ }
+ on_mismatch(top, bottom);
+ return false;
+ }
+ ~stack_guard() {
+ check_stack();
+ }
+ };
+} // namespace sol
+
+// end of sol/stack_guard.hpp
+
+#include <vector>
+#include <bitset>
+#include <forward_list>
+#include <string>
+#include <limits>
+#include <algorithm>
+#include <sstream>
+#include <optional>
+#include <type_traits>
+
+namespace sol {
+ namespace detail {
+ struct with_function_tag { };
+ struct as_reference_tag { };
+ template <typename T>
+ struct as_pointer_tag { };
+ template <typename T>
+ struct as_value_tag { };
+ template <typename T>
+ struct as_unique_tag { };
+ template <typename T>
+ struct as_table_tag { };
+
+ template <typename Tag>
+ inline constexpr bool is_tagged_v
+ = meta::is_specialization_of_v<Tag,
+ detail::
+ as_pointer_tag> || meta::is_specialization_of_v<Tag, as_value_tag> || meta::is_specialization_of_v<Tag, as_unique_tag> || meta::is_specialization_of_v<Tag, as_table_tag> || std::is_same_v<Tag, as_reference_tag> || std::is_same_v<Tag, with_function_tag>;
+
+ using lua_reg_table = luaL_Reg[64];
+
+ using unique_destructor = void (*)(void*);
+ using unique_tag = detail::inheritance_unique_cast_function;
+
+ inline void* alloc_newuserdata(lua_State* L, std::size_t bytesize) {
+#if SOL_LUA_VERSION_I_ >= 504
+ return lua_newuserdatauv(L, bytesize, 1);
+#else
+ return lua_newuserdata(L, bytesize);
+#endif
+ }
+
+ constexpr std::uintptr_t align(std::size_t alignment, std::uintptr_t ptr, std::size_t& space) {
+ // this handles arbitrary alignments...
+ // make this into a power-of-2-only?
+ // actually can't: this is a C++14-compatible framework,
+ // power of 2 alignment is C++17
+ std::uintptr_t offby = static_cast<std::uintptr_t>(ptr % alignment);
+ std::uintptr_t padding = (alignment - offby) % alignment;
+ ptr += padding;
+ space -= padding;
+ return ptr;
+ }
+
+ inline void* align(std::size_t alignment, void* ptr, std::size_t& space) {
+ return reinterpret_cast<void*>(align(alignment, reinterpret_cast<std::uintptr_t>(ptr), space));
+ }
+
+ constexpr std::uintptr_t align_one(std::size_t alignment, std::size_t size, std::uintptr_t ptr) {
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(alignment, ptr, space) + size;
+ }
+
+ template <typename... Args>
+ constexpr std::size_t aligned_space_for(std::uintptr_t ptr) {
+ std::uintptr_t end = ptr;
+ ((end = align_one(alignof(Args), sizeof(Args), end)), ...);
+ return static_cast<std::size_t>(end - ptr);
+ }
+
+ template <typename... Args>
+ constexpr std::size_t aligned_space_for() {
+ static_assert(sizeof...(Args) > 0);
+
+ constexpr std::size_t max_arg_alignment = (std::max)({ alignof(Args)... });
+ if constexpr (max_arg_alignment <= alignof(std::max_align_t)) {
+ // If all types are `good enough`, simply calculate alignment in case of the worst allocator
+ std::size_t worst_required_size = 0;
+ for (std::size_t ptr = 0; ptr < max_arg_alignment; ptr++) {
+ worst_required_size = (std::max)(worst_required_size, aligned_space_for<Args...>(ptr));
+ }
+ return worst_required_size;
+ }
+ else {
+ // For over-aligned types let's assume that every Arg in Args starts at the worst aligned address
+ return (aligned_space_for<Args>(0x1) + ...);
+ }
+ }
+
+ inline void* align_usertype_pointer(void* ptr) {
+ using use_align = std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<void*>::value > 1)
+#endif
+ >;
+ if (!use_align::value) {
+ return ptr;
+ }
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(std::alignment_of<void*>::value, ptr, space);
+ }
+
+ template <bool pre_aligned = false, bool pre_shifted = false>
+ void* align_usertype_unique_destructor(void* ptr) {
+ using use_align = std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<unique_destructor>::value > 1)
+#endif
+ >;
+ if (!pre_aligned) {
+ ptr = align_usertype_pointer(ptr);
+ }
+ if (!pre_shifted) {
+ ptr = static_cast<void*>(static_cast<char*>(ptr) + sizeof(void*));
+ }
+ if (!use_align::value) {
+ return static_cast<void*>(static_cast<void**>(ptr) + 1);
+ }
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(std::alignment_of<unique_destructor>::value, ptr, space);
+ }
+
+ template <bool pre_aligned = false, bool pre_shifted = false>
+ void* align_usertype_unique_tag(void* ptr) {
+ using use_align = std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<unique_tag>::value > 1)
+#endif
+ >;
+ if (!pre_aligned) {
+ ptr = align_usertype_unique_destructor(ptr);
+ }
+ if (!pre_shifted) {
+ ptr = static_cast<void*>(static_cast<char*>(ptr) + sizeof(unique_destructor));
+ }
+ if (!use_align::value) {
+ return ptr;
+ }
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(std::alignment_of<unique_tag>::value, ptr, space);
+ }
+
+ template <typename T, bool pre_aligned = false, bool pre_shifted = false>
+ void* align_usertype_unique(void* ptr) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of_v<T> > 1)
+#endif
+ >
+ use_align;
+ if (!pre_aligned) {
+ ptr = align_usertype_unique_tag(ptr);
+ }
+ if (!pre_shifted) {
+ ptr = static_cast<void*>(static_cast<char*>(ptr) + sizeof(unique_tag));
+ }
+ if (!use_align::value) {
+ return ptr;
+ }
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(std::alignment_of_v<T>, ptr, space);
+ }
+
+ template <typename T>
+ void* align_user(void* ptr) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of_v<T> > 1)
+#endif
+ >
+ use_align;
+ if (!use_align::value) {
+ return ptr;
+ }
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(std::alignment_of_v<T>, ptr, space);
+ }
+
+ template <typename T>
+ T** usertype_allocate_pointer(lua_State* L) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<T*>::value > 1)
+#endif
+ >
+ use_align;
+ if (!use_align::value) {
+ T** pointerpointer = static_cast<T**>(alloc_newuserdata(L, sizeof(T*)));
+ return pointerpointer;
+ }
+ constexpr std::size_t initial_size = aligned_space_for<T*>();
+
+ std::size_t allocated_size = initial_size;
+ void* unadjusted = alloc_newuserdata(L, initial_size);
+ void* adjusted = align(std::alignment_of<T*>::value, unadjusted, allocated_size);
+ if (adjusted == nullptr) {
+ // trash allocator can burn in hell
+ lua_pop(L, 1);
+ // luaL_error(L, "if you are the one that wrote this allocator you should feel bad for doing a
+ // worse job than malloc/realloc and should go read some books, yeah?");
+ luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T*>().data());
+ }
+ return static_cast<T**>(adjusted);
+ }
+
+ inline bool attempt_alloc(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t value_align,
+ std::size_t allocated_size, void*& pointer_adjusted, void*& data_adjusted) {
+ void* adjusted = alloc_newuserdata(L, allocated_size);
+ pointer_adjusted = align(ptr_align, adjusted, allocated_size);
+ if (pointer_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ // subtract size of what we're going to allocate there
+ allocated_size -= ptr_size;
+ adjusted = static_cast<void*>(static_cast<char*>(pointer_adjusted) + ptr_size);
+ data_adjusted = align(value_align, adjusted, allocated_size);
+ if (data_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ return true;
+ }
+
+ inline bool attempt_alloc_unique(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t real_align,
+ std::size_t allocated_size, void*& pointer_adjusted, void*& dx_adjusted, void*& id_adjusted, void*& data_adjusted) {
+ void* adjusted = alloc_newuserdata(L, allocated_size);
+ pointer_adjusted = align(ptr_align, adjusted, allocated_size);
+ if (pointer_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ allocated_size -= ptr_size;
+
+ adjusted = static_cast<void*>(static_cast<char*>(pointer_adjusted) + ptr_size);
+ dx_adjusted = align(std::alignment_of_v<unique_destructor>, adjusted, allocated_size);
+ if (dx_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ allocated_size -= sizeof(unique_destructor);
+
+ adjusted = static_cast<void*>(static_cast<char*>(dx_adjusted) + sizeof(unique_destructor));
+
+ id_adjusted = align(std::alignment_of_v<unique_tag>, adjusted, allocated_size);
+ if (id_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ allocated_size -= sizeof(unique_tag);
+
+ adjusted = static_cast<void*>(static_cast<char*>(id_adjusted) + sizeof(unique_tag));
+ data_adjusted = align(real_align, adjusted, allocated_size);
+ if (data_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ return true;
+ }
+
+ template <typename T>
+ T* usertype_allocate(lua_State* L) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<T*>::value > 1 || std::alignment_of_v<T> > 1)
+#endif
+ >
+ use_align;
+ if (!use_align::value) {
+ T** pointerpointer = static_cast<T**>(alloc_newuserdata(L, sizeof(T*) + sizeof(T)));
+ T*& pointerreference = *pointerpointer;
+ T* allocationtarget = reinterpret_cast<T*>(pointerpointer + 1);
+ pointerreference = allocationtarget;
+ return allocationtarget;
+ }
+
+ constexpr std::size_t initial_size = aligned_space_for<T*, T>();
+
+ void* pointer_adjusted;
+ void* data_adjusted;
+ bool result
+ = attempt_alloc(L, std::alignment_of_v<T*>, sizeof(T*), std::alignment_of_v<T>, initial_size, pointer_adjusted, data_adjusted);
+ if (!result) {
+ if (pointer_adjusted == nullptr) {
+ luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle<T>().c_str());
+ }
+ else {
+ luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle<T>().c_str());
+ }
+ return nullptr;
+ }
+
+ T** pointerpointer = reinterpret_cast<T**>(pointer_adjusted);
+ T*& pointerreference = *pointerpointer;
+ T* allocationtarget = reinterpret_cast<T*>(data_adjusted);
+ pointerreference = allocationtarget;
+ return allocationtarget;
+ }
+
+ template <typename T, typename Real>
+ Real* usertype_unique_allocate(lua_State* L, T**& pref, unique_destructor*& dx, unique_tag*& id) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<T*>::value > 1 || std::alignment_of<unique_tag>::value > 1 || std::alignment_of<unique_destructor>::value > 1
+ || std::alignment_of<Real>::value > 1)
+#endif
+ >
+ use_align;
+ if (!use_align::value) {
+ pref = static_cast<T**>(alloc_newuserdata(L, sizeof(T*) + sizeof(detail::unique_destructor) + sizeof(unique_tag) + sizeof(Real)));
+ dx = static_cast<detail::unique_destructor*>(static_cast<void*>(pref + 1));
+ id = static_cast<unique_tag*>(static_cast<void*>(dx + 1));
+ Real* mem = static_cast<Real*>(static_cast<void*>(id + 1));
+ return mem;
+ }
+
+ constexpr std::size_t initial_size = aligned_space_for<T*, unique_destructor, unique_tag, Real>();
+
+ void* pointer_adjusted = nullptr;
+ void* dx_adjusted = nullptr;
+ void* id_adjusted = nullptr;
+ void* data_adjusted = nullptr;
+ bool result = attempt_alloc_unique(L,
+ std::alignment_of_v<T*>,
+ sizeof(T*),
+ std::alignment_of_v<Real>,
+ initial_size,
+ pointer_adjusted,
+ dx_adjusted,
+ id_adjusted,
+ data_adjusted);
+ if (!result) {
+ if (pointer_adjusted == nullptr) {
+ luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle<T>().c_str());
+ }
+ else if (dx_adjusted == nullptr) {
+ luaL_error(L, "aligned allocation of userdata block (deleter section) for '%s' failed", detail::demangle<T>().c_str());
+ }
+ else {
+ luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle<T>().c_str());
+ }
+ return nullptr;
+ }
+
+ pref = static_cast<T**>(pointer_adjusted);
+ dx = static_cast<detail::unique_destructor*>(dx_adjusted);
+ id = static_cast<unique_tag*>(id_adjusted);
+ Real* mem = static_cast<Real*>(data_adjusted);
+ return mem;
+ }
+
+ template <typename T>
+ T* user_allocate(lua_State* L) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of_v<T> > 1)
+#endif
+ >
+ use_align;
+ if (!use_align::value) {
+ T* pointer = static_cast<T*>(alloc_newuserdata(L, sizeof(T)));
+ return pointer;
+ }
+
+ constexpr std::size_t initial_size = aligned_space_for<T>();
+
+ std::size_t allocated_size = initial_size;
+ void* unadjusted = alloc_newuserdata(L, allocated_size);
+ void* adjusted = align(std::alignment_of_v<T>, unadjusted, allocated_size);
+ if (adjusted == nullptr) {
+ lua_pop(L, 1);
+ luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T>().data());
+ }
+ return static_cast<T*>(adjusted);
+ }
+
+ template <typename T>
+ int usertype_alloc_destroy(lua_State* L) noexcept {
+ void* memory = lua_touserdata(L, 1);
+ memory = align_usertype_pointer(memory);
+ T** pdata = static_cast<T**>(memory);
+ T* data = *pdata;
+ std::allocator<T> alloc {};
+ std::allocator_traits<std::allocator<T>>::destroy(alloc, data);
+ return 0;
+ }
+
+ template <typename T>
+ int unique_destroy(lua_State* L) noexcept {
+ void* memory = lua_touserdata(L, 1);
+ memory = align_usertype_unique_destructor(memory);
+ unique_destructor& dx = *static_cast<unique_destructor*>(memory);
+ memory = align_usertype_unique_tag<true>(memory);
+ (dx)(memory);
+ return 0;
+ }
+
+ template <typename T>
+ int user_alloc_destroy(lua_State* L) noexcept {
+ void* memory = lua_touserdata(L, 1);
+ void* aligned_memory = align_user<T>(memory);
+ T* typed_memory = static_cast<T*>(aligned_memory);
+ std::allocator<T> alloc;
+ std::allocator_traits<std::allocator<T>>::destroy(alloc, typed_memory);
+ return 0;
+ }
+
+ template <typename T, typename Real>
+ void usertype_unique_alloc_destroy(void* memory) {
+ void* aligned_memory = align_usertype_unique<Real, true>(memory);
+ Real* typed_memory = static_cast<Real*>(aligned_memory);
+ std::allocator<Real> alloc;
+ std::allocator_traits<std::allocator<Real>>::destroy(alloc, typed_memory);
+ }
+
+ template <typename T>
+ int cannot_destroy(lua_State* L) {
+ return luaL_error(L,
+ "cannot call the destructor for '%s': it is either hidden (protected/private) or removed with '= "
+ "delete' and thusly this type is being destroyed without properly destroying, invoking undefined "
+ "behavior: please bind a usertype and specify a custom destructor to define the behavior properly",
+ detail::demangle<T>().data());
+ }
+
+ template <typename T>
+ void reserve(T&, std::size_t) {
+ }
+
+ template <typename T, typename Al>
+ void reserve(std::vector<T, Al>& vec, std::size_t hint) {
+ vec.reserve(hint);
+ }
+
+ template <typename T, typename Tr, typename Al>
+ void reserve(std::basic_string<T, Tr, Al>& str, std::size_t hint) {
+ str.reserve(hint);
+ }
+
+ inline bool property_always_true(meta_function) {
+ return true;
+ }
+
+ struct properties_enrollment_allowed {
+ int& times_through;
+ std::bitset<64>& properties;
+ automagic_enrollments& enrollments;
+
+ properties_enrollment_allowed(int& times_through_, std::bitset<64>& properties_, automagic_enrollments& enrollments_)
+ : times_through(times_through_), properties(properties_), enrollments(enrollments_) {
+ }
+
+ bool operator()(meta_function mf) const {
+ bool p = properties[static_cast<std::size_t>(mf)];
+ if (times_through > 0) {
+ return p;
+ }
+ switch (mf) {
+ case meta_function::length:
+ return enrollments.length_operator && !p;
+ case meta_function::pairs:
+ return enrollments.pairs_operator && !p;
+ case meta_function::call:
+ return enrollments.call_operator && !p;
+ case meta_function::less_than:
+ return enrollments.less_than_operator && !p;
+ case meta_function::less_than_or_equal_to:
+ return enrollments.less_than_or_equal_to_operator && !p;
+ case meta_function::equal_to:
+ return enrollments.equal_to_operator && !p;
+ default:
+ break;
+ }
+ return !p;
+ }
+ };
+
+ struct indexed_insert {
+ lua_reg_table& registration_table;
+ int& index;
+
+ indexed_insert(lua_reg_table& registration_table_, int& index_ref_) : registration_table(registration_table_), index(index_ref_) {
+ }
+ void operator()(meta_function meta_function_name_, lua_CFunction c_function_) {
+ registration_table[index] = luaL_Reg { to_string(meta_function_name_).c_str(), c_function_ };
+ ++index;
+ }
+ };
+ } // namespace detail
+
+ namespace stack {
+
+ template <typename T, bool global = false, bool raw = false, typename = void>
+ struct field_getter;
+ template <typename T, typename P, bool global = false, bool raw = false, typename = void>
+ struct probe_field_getter;
+
+ template <typename T, bool global = false, bool raw = false, typename = void>
+ struct field_setter;
+
+ template <typename T, typename = void>
+ struct unqualified_getter;
+ template <typename T, typename = void>
+ struct qualified_getter;
+
+ template <typename T, typename = void>
+ struct qualified_interop_getter;
+ template <typename T, typename = void>
+ struct unqualified_interop_getter;
+
+ template <typename T, typename = void>
+ struct popper;
+
+ template <typename T, typename = void>
+ struct unqualified_pusher;
+
+ template <typename T, type t, typename = void>
+ struct unqualified_checker;
+ template <typename T, type t, typename = void>
+ struct qualified_checker;
+
+ template <typename T, typename = void>
+ struct unqualified_check_getter;
+ template <typename T, typename = void>
+ struct qualified_check_getter;
+
+ struct probe {
+ bool success;
+ int levels;
+
+ probe(bool s, int l) : success(s), levels(l) {
+ }
+
+ operator bool() const {
+ return success;
+ };
+ };
+
+ struct record {
+ int last;
+ int used;
+
+ record() noexcept : last(), used() {
+ }
+ void use(int count) noexcept {
+ last = count;
+ used += count;
+ }
+ };
+
+ namespace stack_detail {
+ template <typename Function>
+ Function* get_function_pointer(lua_State*, int, record&) noexcept;
+ template <typename Function, typename Handler>
+ bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept;
+ } // namespace stack_detail
+
+ } // namespace stack
+
+ namespace meta { namespace meta_detail {
+ template <typename T>
+ using adl_sol_lua_get_test_t = decltype(sol_lua_get(types<T>(), static_cast<lua_State*>(nullptr), -1, std::declval<stack::record&>()));
+
+ template <typename T>
+ using adl_sol_lua_interop_get_test_t
+ = decltype(sol_lua_interop_get(types<T>(), static_cast<lua_State*>(nullptr), -1, static_cast<void*>(nullptr), std::declval<stack::record&>()));
+
+ template <typename T>
+ using adl_sol_lua_check_test_t = decltype(sol_lua_check(types<T>(), static_cast<lua_State*>(nullptr), -1, &no_panic, std::declval<stack::record&>()));
+
+ template <typename T>
+ using adl_sol_lua_interop_check_test_t
+ = decltype(sol_lua_interop_check(types<T>(), static_cast<lua_State*>(nullptr), -1, type::none, &no_panic, std::declval<stack::record&>()));
+
+ template <typename T>
+ using adl_sol_lua_check_get_test_t
+ = decltype(sol_lua_check_get(types<T>(), static_cast<lua_State*>(nullptr), -1, &no_panic, std::declval<stack::record&>()));
+
+ template <typename... Args>
+ using adl_sol_lua_push_test_t = decltype(sol_lua_push(static_cast<lua_State*>(nullptr), std::declval<Args>()...));
+
+ template <typename T, typename... Args>
+ using adl_sol_lua_push_exact_test_t = decltype(sol_lua_push(types<T>(), static_cast<lua_State*>(nullptr), std::declval<Args>()...));
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_get_v = meta::is_detected_v<adl_sol_lua_get_test_t, T>;
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_interop_get_v = meta::is_detected_v<adl_sol_lua_interop_get_test_t, T>;
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_check_v = meta::is_detected_v<adl_sol_lua_check_test_t, T>;
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_interop_check_v = meta::is_detected_v<adl_sol_lua_interop_check_test_t, T>;
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_check_get_v = meta::is_detected_v<adl_sol_lua_check_get_test_t, T>;
+
+ template <typename... Args>
+ inline constexpr bool is_adl_sol_lua_push_v = meta::is_detected_v<adl_sol_lua_push_test_t, Args...>;
+
+ template <typename T, typename... Args>
+ inline constexpr bool is_adl_sol_lua_push_exact_v = meta::is_detected_v<adl_sol_lua_push_exact_test_t, T, Args...>;
+ }} // namespace meta::meta_detail
+
+ namespace stack {
+ namespace stack_detail {
+ constexpr const char* not_enough_stack_space = "not enough space left on Lua stack";
+ constexpr const char* not_enough_stack_space_floating = "not enough space left on Lua stack for a floating point number";
+ constexpr const char* not_enough_stack_space_integral = "not enough space left on Lua stack for an integral number";
+ constexpr const char* not_enough_stack_space_string = "not enough space left on Lua stack for a string";
+ constexpr const char* not_enough_stack_space_meta_function_name = "not enough space left on Lua stack for the name of a meta_function";
+ constexpr const char* not_enough_stack_space_userdata = "not enough space left on Lua stack to create a sol2 userdata";
+ constexpr const char* not_enough_stack_space_generic = "not enough space left on Lua stack to push valuees";
+ constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment";
+
+ template <typename T>
+ struct strip {
+ typedef T type;
+ };
+ template <typename T>
+ struct strip<std::reference_wrapper<T>> {
+ typedef T& type;
+ };
+ template <typename T>
+ struct strip<user<T>> {
+ typedef T& type;
+ };
+ template <typename T>
+ struct strip<non_null<T>> {
+ typedef T type;
+ };
+ template <typename T>
+ using strip_t = typename strip<T>::type;
+
+ template <typename C>
+ static int get_size_hint(C& c) {
+ return static_cast<int>(c.size());
+ }
+
+ template <typename V, typename Al>
+ static int get_size_hint(const std::forward_list<V, Al>&) {
+ // forward_list makes me sad
+ return static_cast<int>(32);
+ }
+
+ template <typename T>
+ decltype(auto) unchecked_unqualified_get(lua_State* L, int index, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_get_v<Tu>) {
+ return sol_lua_get(types<Tu>(), L, index, tracking);
+ }
+ else {
+ unqualified_getter<Tu> g {};
+ return g.get(L, index, tracking);
+ }
+ }
+
+ template <typename T>
+ decltype(auto) unchecked_get(lua_State* L, int index, record& tracking) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_get_v<T>) {
+ return sol_lua_get(types<T>(), L, index, tracking);
+ }
+ else {
+ qualified_getter<T> g {};
+ return g.get(L, index, tracking);
+ }
+ }
+
+ template <typename T>
+ decltype(auto) unqualified_interop_get(lua_State* L, int index, void* unadjusted_pointer, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_interop_get_v<Tu>) {
+ return sol_lua_interop_get(types<Tu>(), L, index, unadjusted_pointer, tracking);
+ }
+ else {
+ (void)L;
+ (void)index;
+ (void)unadjusted_pointer;
+ (void)tracking;
+ using Ti = stack_detail::strip_t<Tu>;
+ return std::pair<bool, Ti*> { false, nullptr };
+ }
+ }
+
+ template <typename T>
+ decltype(auto) interop_get(lua_State* L, int index, void* unadjusted_pointer, record& tracking) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_interop_get_v<T>) {
+ return sol_lua_interop_get(types<T>(), L, index, unadjusted_pointer, tracking);
+ }
+ else {
+ return unqualified_interop_get<T>(L, index, unadjusted_pointer, tracking);
+ }
+ }
+
+ template <typename T, typename Handler>
+ bool unqualified_interop_check(lua_State* L, int index, type index_type, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_interop_check_v<Tu>) {
+ return sol_lua_interop_check(types<Tu>(), L, index, index_type, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ (void)L;
+ (void)index;
+ (void)index_type;
+ (void)handler;
+ (void)tracking;
+ return false;
+ }
+ }
+
+ template <typename T, typename Handler>
+ bool interop_check(lua_State* L, int index, type index_type, Handler&& handler, record& tracking) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_interop_check_v<T>) {
+ return sol_lua_interop_check(types<T>(), L, index, index_type, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ return unqualified_interop_check<T>(L, index, index_type, std::forward<Handler>(handler), tracking);
+ }
+ }
+
+ using undefined_method_func = void (*)(stack_reference);
+
+ struct undefined_metatable {
+ lua_State* L;
+ const char* key;
+ undefined_method_func on_new_table;
+
+ undefined_metatable(lua_State* l, const char* k, undefined_method_func umf) : L(l), key(k), on_new_table(umf) {
+ }
+
+ void operator()() const {
+ if (luaL_newmetatable(L, key) == 1) {
+ on_new_table(stack_reference(L, -1));
+ }
+ lua_setmetatable(L, -2);
+ }
+ };
+ } // namespace stack_detail
+
+ inline bool maybe_indexable(lua_State* L, int index = -1) {
+ type t = type_of(L, index);
+ return t == type::userdata || t == type::table;
+ }
+
+ inline int top(lua_State* L) {
+ return lua_gettop(L);
+ }
+
+ inline bool is_main_thread(lua_State* L) {
+ int ismainthread = lua_pushthread(L);
+ lua_pop(L, 1);
+ return ismainthread == 1;
+ }
+
+ inline void coroutine_create_guard(lua_State* L) {
+ if (is_main_thread(L)) {
+ return;
+ }
+ int stacksize = lua_gettop(L);
+ if (stacksize < 1) {
+ return;
+ }
+ if (type_of(L, 1) != type::function) {
+ return;
+ }
+ // well now we're screwed...
+ // we can clean the stack and pray it doesn't destroy anything?
+ lua_pop(L, stacksize);
+ }
+
+ inline void clear(lua_State* L, int table_index) {
+ lua_pushnil(L);
+ while (lua_next(L, table_index) != 0) {
+ // remove value
+ lua_pop(L, 1);
+ // duplicate key to protect form rawset
+ lua_pushvalue(L, -1);
+ // push new value
+ lua_pushnil(L);
+ // table_index%[key] = nil
+ lua_rawset(L, table_index);
+ }
+ }
+
+ inline void clear(reference& r) {
+ auto pp = push_pop<false>(r);
+ int stack_index = pp.index_of(r);
+ clear(r.lua_state(), stack_index);
+ }
+
+ inline void clear(stack_reference& r) {
+ clear(r.lua_state(), r.stack_index());
+ }
+
+ inline void clear(lua_State* L_, stateless_reference& r) {
+ r.push(L_);
+ int stack_index = absolute_index(L_, -1);
+ clear(L_, stack_index);
+ r.pop(L_);
+ }
+
+ inline void clear(lua_State* L_, stateless_stack_reference& r) {
+ clear(L_, r.stack_index());
+ }
+
+ template <typename T, typename... Args>
+ int push(lua_State* L, T&& t, Args&&... args) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v<T, T, Args...>) {
+ return sol_lua_push(types<T>(), L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v<Tu, T, Args...>) {
+ return sol_lua_push(types<Tu>(), L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::meta_detail::is_adl_sol_lua_push_v<T, Args...>) {
+ return sol_lua_push(L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+ else {
+ unqualified_pusher<Tu> p {};
+ return p.push(L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+ }
+
+ // overload allows to use a pusher of a specific type, but pass in any kind of args
+ template <typename T, typename Arg, typename... Args, typename = std::enable_if_t<!std::is_same<T, Arg>::value>>
+ int push(lua_State* L, Arg&& arg, Args&&... args) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v<T, Arg, Args...>) {
+ return sol_lua_push(types<T>(), L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v<Tu, Arg, Args...>) {
+ return sol_lua_push(types<Tu>(), L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::meta_detail::is_adl_sol_lua_push_v<Arg, Args...> && !detail::is_tagged_v<Tu>) {
+ return sol_lua_push(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ else {
+ unqualified_pusher<Tu> p {};
+ return p.push(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename T, typename... Args>
+ int push_userdata(lua_State* L, T&& t, Args&&... args) {
+ using U = meta::unqualified_t<T>;
+ using Tr = meta::conditional_t<std::is_pointer_v<U>,
+ detail::as_pointer_tag<std::remove_pointer_t<U>>,
+ meta::conditional_t<is_unique_usertype_v<U>, detail::as_unique_tag<U>, detail::as_value_tag<U>>>;
+ return stack::push<Tr>(L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename Arg, typename... Args>
+ int push_userdata(lua_State* L, Arg&& arg, Args&&... args) {
+ using U = meta::unqualified_t<T>;
+ using Tr = meta::conditional_t<std::is_pointer_v<U>,
+ detail::as_pointer_tag<std::remove_pointer_t<U>>,
+ meta::conditional_t<is_unique_usertype_v<U>, detail::as_unique_tag<U>, detail::as_value_tag<U>>>;
+ return stack::push<Tr>(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+
+ namespace stack_detail {
+
+ template <typename T, typename Arg, typename... Args>
+ int push_reference(lua_State* L, Arg&& arg, Args&&... args) {
+ // clang-format off
+ using use_reference_tag =
+ meta::all<
+ meta::neg<is_value_semantic_for_function<T>>
+#if SOL_IS_OFF(SOL_FUNCTION_CALL_VALUE_SEMANTICS)
+ , std::is_lvalue_reference<T>,
+ meta::neg<std::is_const<std::remove_reference_t<T>>>,
+ meta::neg<is_lua_primitive<meta::unqualified_t<T>>>,
+ meta::neg<is_unique_usertype<meta::unqualified_t<T>>>
+#endif
+ >;
+ // clang-format on
+ using Tr = meta::conditional_t<use_reference_tag::value, detail::as_reference_tag, meta::unqualified_t<T>>;
+ return stack::push<Tr>(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+
+ } // namespace stack_detail
+
+ template <typename T, typename... Args>
+ int push_reference(lua_State* L, T&& t, Args&&... args) {
+ return stack_detail::push_reference<T>(L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename Arg, typename... Args>
+ int push_reference(lua_State* L, Arg&& arg, Args&&... args) {
+ return stack_detail::push_reference<T>(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+
+ inline int multi_push(lua_State*) {
+ // do nothing
+ return 0;
+ }
+
+ template <typename T, typename... Args>
+ int multi_push(lua_State* L, T&& t, Args&&... args) {
+ int pushcount = push(L, std::forward<T>(t));
+ void(detail::swallow { (pushcount += stack::push(L, std::forward<Args>(args)), 0)... });
+ return pushcount;
+ }
+
+ inline int multi_push_reference(lua_State*) {
+ // do nothing
+ return 0;
+ }
+
+ template <typename T, typename... Args>
+ int multi_push_reference(lua_State* L, T&& t, Args&&... args) {
+ int pushcount = stack::push_reference(L, std::forward<T>(t));
+ void(detail::swallow { (pushcount += stack::push_reference(L, std::forward<Args>(args)), 0)... });
+ return pushcount;
+ }
+
+ template <typename T, typename Handler>
+ bool unqualified_check(lua_State* L, int index, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_v<Tu>) {
+ return sol_lua_check(types<Tu>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ unqualified_checker<Tu, lua_type_of_v<Tu>> c{};
+ return c.check(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+
+ template <typename T, typename Handler>
+ bool unqualified_check(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return unqualified_check<T>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename T>
+ bool unqualified_check(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ auto handler = &no_panic;
+ return unqualified_check<T>(L, index, handler);
+ }
+
+ template <typename T, typename Handler>
+ bool check(lua_State* L, int index, Handler&& handler, record& tracking) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_v<T>) {
+ return sol_lua_check(types<T>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ using Tu = meta::unqualified_t<T>;
+ qualified_checker<T, lua_type_of_v<Tu>> c{};
+ return c.check(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+
+ template <typename T, typename Handler>
+ bool check(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return check<T>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename T>
+ bool check(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ auto handler = &no_panic;
+ return check<T>(L, index, handler);
+ }
+
+ template <typename T, typename Handler>
+ bool check_usertype(lua_State* L, int index, type, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ using detail_t = meta::conditional_t<std::is_pointer_v<T>, detail::as_pointer_tag<Tu>, detail::as_value_tag<Tu>>;
+ return check<detail_t>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename T, typename Handler>
+ bool check_usertype(lua_State* L, int index, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ using detail_t = meta::conditional_t<std::is_pointer_v<T>, detail::as_pointer_tag<Tu>, detail::as_value_tag<Tu>>;
+ return check<detail_t>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename T, typename Handler>
+ bool check_usertype(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return check_usertype<T>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename T>
+ bool check_usertype(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ auto handler = &no_panic;
+ return check_usertype<T>(L, index, handler);
+ }
+
+ template <typename T, typename Handler>
+ decltype(auto) unqualified_check_get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v<T>) {
+ return sol_lua_check_get(types<T>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ else if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v<Tu>) {
+ return sol_lua_check_get(types<Tu>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ unqualified_check_getter<Tu> cg {};
+ return cg.get(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+
+ template <typename T, typename Handler>
+ decltype(auto) unqualified_check_get(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return unqualified_check_get<T>(L, index, handler, tracking);
+ }
+
+ template <typename T>
+ decltype(auto) unqualified_check_get(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ auto handler = &no_panic;
+ return unqualified_check_get<T>(L, index, handler);
+ }
+
+ template <typename T, typename Handler>
+ decltype(auto) check_get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v<T>) {
+ return sol_lua_check_get(types<T>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ qualified_check_getter<T> cg {};
+ return cg.get(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+
+ template <typename T, typename Handler>
+ decltype(auto) check_get(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return check_get<T>(L, index, handler, tracking);
+ }
+
+ template <typename T>
+ decltype(auto) check_get(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ auto handler = &no_panic;
+ return check_get<T>(L, index, handler);
+ }
+
+ namespace stack_detail {
+
+ template <typename Handler>
+ bool check_types(lua_State*, int, Handler&&, record&) {
+ return true;
+ }
+
+ template <typename T, typename... Args, typename Handler>
+ bool check_types(lua_State* L, int firstargument, Handler&& handler, record& tracking) {
+ if (!stack::check<T>(L, firstargument + tracking.used, handler, tracking))
+ return false;
+ return check_types<Args...>(L, firstargument, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename... Args, typename Handler>
+ bool check_types(types<Args...>, lua_State* L, int index, Handler&& handler, record& tracking) {
+ return check_types<Args...>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ } // namespace stack_detail
+
+ template <typename... Args, typename Handler>
+ bool multi_check(lua_State* L, int index, Handler&& handler, record& tracking) {
+ return stack_detail::check_types<Args...>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename... Args, typename Handler>
+ bool multi_check(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return multi_check<Args...>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename... Args>
+ bool multi_check(lua_State* L, int index) {
+ return multi_check<Args...>(L, index);
+ }
+
+ template <typename T>
+ auto unqualified_get(lua_State* L, int index, record& tracking) -> decltype(stack_detail::unchecked_unqualified_get<T>(L, index, tracking)) {
+#if SOL_IS_ON(SOL_SAFE_GETTER)
+ static constexpr bool is_op = meta::is_optional_v<T>;
+ if constexpr (is_op) {
+ return stack_detail::unchecked_unqualified_get<T>(L, index, tracking);
+ }
+ else {
+ if (is_lua_reference<T>::value) {
+ return stack_detail::unchecked_unqualified_get<T>(L, index, tracking);
+ }
+ auto op = unqualified_check_get<T>(L, index, type_panic_c_str, tracking);
+ return *std::move(op);
+ }
+#else
+ return stack_detail::unchecked_unqualified_get<T>(L, index, tracking);
+#endif
+ }
+
+ template <typename T>
+ decltype(auto) unqualified_get(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ record tracking {};
+ return unqualified_get<T>(L, index, tracking);
+ }
+
+ template <typename T>
+ auto get(lua_State* L, int index, record& tracking) -> decltype(stack_detail::unchecked_get<T>(L, index, tracking)) {
+#if SOL_IS_ON(SOL_SAFE_GETTER)
+ static constexpr bool is_op = meta::is_optional_v<T>;
+ if constexpr (is_op) {
+ return stack_detail::unchecked_get<T>(L, index, tracking);
+ }
+ else {
+ if (is_lua_reference<T>::value) {
+ return stack_detail::unchecked_get<T>(L, index, tracking);
+ }
+ auto op = check_get<T>(L, index, type_panic_c_str, tracking);
+ return *std::move(op);
+ }
+#else
+ return stack_detail::unchecked_get<T>(L, index, tracking);
+#endif
+ }
+
+ template <typename T>
+ decltype(auto) get(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ record tracking {};
+ return get<T>(L, index, tracking);
+ }
+
+ template <typename T>
+ decltype(auto) get_usertype(lua_State* L, int index, record& tracking) {
+ using UT = meta::conditional_t<std::is_pointer<T>::value, detail::as_pointer_tag<std::remove_pointer_t<T>>, detail::as_value_tag<T>>;
+ return get<UT>(L, index, tracking);
+ }
+
+ template <typename T>
+ decltype(auto) get_usertype(lua_State* L, int index = -lua_size_v<meta::unqualified_t<T>>) {
+ record tracking {};
+ return get_usertype<T>(L, index, tracking);
+ }
+
+ template <typename T>
+ decltype(auto) pop(lua_State* L) {
+ return popper<T> {}.pop(L);
+ }
+
+ template <bool global = false, bool raw = false, typename Key>
+ void get_field(lua_State* L, Key&& key) {
+ field_getter<meta::unqualified_t<Key>, global, raw> {}.get(L, std::forward<Key>(key));
+ }
+
+ template <bool global = false, bool raw = false, typename Key>
+ void get_field(lua_State* L, Key&& key, int tableindex) {
+ field_getter<meta::unqualified_t<Key>, global, raw> {}.get(L, std::forward<Key>(key), tableindex);
+ }
+
+ template <bool global = false, typename Key>
+ void raw_get_field(lua_State* L, Key&& key) {
+ get_field<global, true>(L, std::forward<Key>(key));
+ }
+
+ template <bool global = false, typename Key>
+ void raw_get_field(lua_State* L, Key&& key, int tableindex) {
+ get_field<global, true>(L, std::forward<Key>(key), tableindex);
+ }
+
+ template <bool global = false, bool raw = false, typename C = detail::non_lua_nil_t, typename Key>
+ probe probe_get_field(lua_State* L, Key&& key) {
+ return probe_field_getter<meta::unqualified_t<Key>, C, global, raw> {}.get(L, std::forward<Key>(key));
+ }
+
+ template <bool global = false, bool raw = false, typename C = detail::non_lua_nil_t, typename Key>
+ probe probe_get_field(lua_State* L, Key&& key, int tableindex) {
+ return probe_field_getter<meta::unqualified_t<Key>, C, global, raw> {}.get(L, std::forward<Key>(key), tableindex);
+ }
+
+ template <bool global = false, typename C = detail::non_lua_nil_t, typename Key>
+ probe probe_raw_get_field(lua_State* L, Key&& key) {
+ return probe_get_field<global, true, C>(L, std::forward<Key>(key));
+ }
+
+ template <bool global = false, typename C = detail::non_lua_nil_t, typename Key>
+ probe probe_raw_get_field(lua_State* L, Key&& key, int tableindex) {
+ return probe_get_field<global, true, C>(L, std::forward<Key>(key), tableindex);
+ }
+
+ template <bool global = false, bool raw = false, typename Key, typename Value>
+ void set_field(lua_State* L, Key&& key, Value&& value) {
+ field_setter<meta::unqualified_t<Key>, global, raw> {}.set(L, std::forward<Key>(key), std::forward<Value>(value));
+ }
+
+ template <bool global = false, bool raw = false, typename Key, typename Value>
+ void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) {
+ field_setter<meta::unqualified_t<Key>, global, raw> {}.set(L, std::forward<Key>(key), std::forward<Value>(value), tableindex);
+ }
+
+ template <bool global = false, typename Key, typename Value>
+ void raw_set_field(lua_State* L, Key&& key, Value&& value) {
+ set_field<global, true>(L, std::forward<Key>(key), std::forward<Value>(value));
+ }
+
+ template <bool global = false, typename Key, typename Value>
+ void raw_set_field(lua_State* L, Key&& key, Value&& value, int tableindex) {
+ set_field<global, true>(L, std::forward<Key>(key), std::forward<Value>(value), tableindex);
+ }
+
+ template <typename T, typename F>
+ void modify_unique_usertype_as(const stack_reference& obj, F&& f) {
+ void* raw = lua_touserdata(obj.lua_state(), obj.stack_index());
+ void* ptr_memory = detail::align_usertype_pointer(raw);
+ void* uu_memory = detail::align_usertype_unique<T>(raw);
+ T& uu = *static_cast<T*>(uu_memory);
+ f(uu);
+ *static_cast<void**>(ptr_memory) = static_cast<void*>(detail::unique_get(obj.lua_state(), uu));
+ }
+
+ template <typename F>
+ void modify_unique_usertype(const stack_reference& obj, F&& f) {
+ using bt = meta::bind_traits<meta::unqualified_t<F>>;
+ using T = typename bt::template arg_at<0>;
+ using Tu = meta::unqualified_t<T>;
+ modify_unique_usertype_as<Tu>(obj, std::forward<F>(f));
+ }
+
+ namespace stack_detail {
+ template <typename T, typename Handler>
+ decltype(auto) check_get_arg(lua_State* L_, int index_, Handler&& handler_, record& tracking_) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_access_v<T>) {
+ sol_lua_check_access(types<meta::unqualified_t<T>>(), L_, index_, tracking_);
+ }
+ return check_get<T>(L_, index_, std::forward<Handler>(handler_), tracking_);
+ }
+
+ template <typename T>
+ decltype(auto) unchecked_get_arg(lua_State* L_, int index_, record& tracking_) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_access_v<T>) {
+ sol_lua_check_access(types<meta::unqualified_t<T>>(), L_, index_, tracking_);
+ }
+ return unchecked_get<T>(L_, index_, tracking_);
+ }
+ } // namespace stack_detail
+
+ } // namespace stack
+
+ namespace detail {
+
+ template <typename T>
+ lua_CFunction make_destructor(std::true_type) {
+ if constexpr (is_unique_usertype_v<T>) {
+ return &unique_destroy<T>;
+ }
+ else if constexpr (!std::is_pointer_v<T>) {
+ return &usertype_alloc_destroy<T>;
+ }
+ else {
+ return &cannot_destroy<T>;
+ }
+ }
+
+ template <typename T>
+ lua_CFunction make_destructor(std::false_type) {
+ return &cannot_destroy<T>;
+ }
+
+ template <typename T>
+ lua_CFunction make_destructor() {
+ return make_destructor<T>(std::is_destructible<T>());
+ }
+
+ struct no_comp {
+ template <typename A, typename B>
+ bool operator()(A&&, B&&) const {
+ return false;
+ }
+ };
+
+ template <typename T>
+ int is_check(lua_State* L) {
+ return stack::push(L, stack::check<T>(L, 1, &no_panic));
+ }
+
+ template <typename T>
+ int member_default_to_string(std::true_type, lua_State* L) {
+ decltype(auto) ts = stack::get<T>(L, 1).to_string();
+ return stack::push(L, std::forward<decltype(ts)>(ts));
+ }
+
+ template <typename T>
+ int member_default_to_string(std::false_type, lua_State* L) {
+ return luaL_error(L,
+ "cannot perform to_string on '%s': no 'to_string' overload in namespace, 'to_string' member "
+ "function, or operator<<(ostream&, ...) present",
+ detail::demangle<T>().data());
+ }
+
+ template <typename T>
+ int adl_default_to_string(std::true_type, lua_State* L) {
+ using namespace std;
+ decltype(auto) ts = to_string(stack::get<T>(L, 1));
+ return stack::push(L, std::forward<decltype(ts)>(ts));
+ }
+
+ template <typename T>
+ int adl_default_to_string(std::false_type, lua_State* L) {
+ return member_default_to_string<T>(meta::supports_to_string_member<T>(), L);
+ }
+
+ template <typename T>
+ int oss_default_to_string(std::true_type, lua_State* L) {
+ std::ostringstream oss;
+ oss << stack::unqualified_get<T>(L, 1);
+ return stack::push(L, oss.str());
+ }
+
+ template <typename T>
+ int oss_default_to_string(std::false_type, lua_State* L) {
+ return adl_default_to_string<T>(meta::supports_adl_to_string<T>(), L);
+ }
+
+ template <typename T>
+ int default_to_string(lua_State* L) {
+ return oss_default_to_string<T>(meta::supports_op_left_shift<std::ostream, T>(), L);
+ }
+
+ template <typename T>
+ int default_size(lua_State* L) {
+ decltype(auto) self = stack::unqualified_get<T>(L, 1);
+ return stack::push(L, self.size());
+ }
+
+ template <typename T, typename Op>
+ int comparsion_operator_wrap(lua_State* L) {
+ if constexpr (std::is_void_v<T>) {
+ return stack::push(L, false);
+ }
+ else {
+ auto maybel = stack::unqualified_check_get<T>(L, 1);
+ if (!maybel) {
+ return stack::push(L, false);
+ }
+ auto mayber = stack::unqualified_check_get<T>(L, 2);
+ if (!mayber) {
+ return stack::push(L, false);
+ }
+ decltype(auto) l = *maybel;
+ decltype(auto) r = *mayber;
+ if constexpr (std::is_same_v<no_comp, Op>) {
+ std::equal_to<> op;
+ return stack::push(L, op(detail::ptr(l), detail::ptr(r)));
+ }
+ else {
+ if constexpr (std::is_same_v<std::equal_to<>, Op> // clang-format hack
+ || std::is_same_v<std::less_equal<>, Op> //
+ || std::is_same_v<std::less_equal<>, Op>) { //
+ if (detail::ptr(l) == detail::ptr(r)) {
+ return stack::push(L, true);
+ }
+ }
+ Op op;
+ return stack::push(L, op(detail::deref(l), detail::deref(r)));
+ }
+ }
+ }
+
+ template <typename T, typename IFx, typename Fx>
+ void insert_default_registrations(IFx&& ifx, Fx&& fx);
+
+ template <typename T, bool, bool>
+ struct get_is_primitive : is_lua_primitive<T> { };
+
+ template <typename T>
+ struct get_is_primitive<T, true, false>
+ : meta::neg<std::is_reference<decltype(sol_lua_get(types<T>(), nullptr, -1, std::declval<stack::record&>()))>> { };
+
+ template <typename T>
+ struct get_is_primitive<T, false, true>
+ : meta::neg<std::is_reference<decltype(sol_lua_get(types<meta::unqualified_t<T>>(), nullptr, -1, std::declval<stack::record&>()))>> { };
+
+ template <typename T>
+ struct get_is_primitive<T, true, true> : get_is_primitive<T, true, false> { };
+
+ } // namespace detail
+
+ template <typename T>
+ struct is_proxy_primitive
+ : detail::get_is_primitive<T, meta::meta_detail::is_adl_sol_lua_get_v<T>, meta::meta_detail::is_adl_sol_lua_get_v<meta::unqualified_t<T>>> { };
+
+} // namespace sol
+
+// end of sol/stack_core.hpp
+
+// beginning of sol/stack_check.hpp
+
+// beginning of sol/stack_check_unqualified.hpp
+
+#include <memory>
+#include <functional>
+#include <utility>
+#include <cmath>
+#include <optional>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // variant shenanigans
+
+namespace sol { namespace stack {
+ template <typename Handler>
+ bool loose_table_check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ if (t == type::table) {
+ return true;
+ }
+ if (t != type::userdata) {
+ handler(L_, index, type::table, t, "value is not a table or a userdata that can behave like one");
+ return false;
+ }
+ return true;
+ }
+
+ namespace stack_detail {
+ inline bool impl_check_metatable(lua_State* L_, int index, const std::string& metakey, bool poptable) {
+ luaL_getmetatable(L_, &metakey[0]);
+ const type expectedmetatabletype = static_cast<type>(lua_type(L_, -1));
+ if (expectedmetatabletype != type::lua_nil) {
+ if (lua_rawequal(L_, -1, index) == 1) {
+ lua_pop(L_, 1 + static_cast<int>(poptable));
+ return true;
+ }
+ }
+ lua_pop(L_, 1);
+ return false;
+ }
+
+ template <typename T, bool poptable = true>
+ inline bool check_metatable(lua_State* L_, int index = -2) {
+ return impl_check_metatable(L_, index, usertype_traits<T>::metatable(), poptable);
+ }
+
+ template <type expected, int (*check_func)(lua_State*, int)>
+ struct basic_check {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ tracking.use(1);
+ bool success = check_func(L_, index) == 1;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, expected, type_of(L_, index), "");
+ }
+ return success;
+ }
+ };
+ } // namespace stack_detail
+
+ template <typename T, typename>
+ struct unqualified_interop_checker {
+ template <typename Handler>
+ static bool check(lua_State*, int, type, Handler&&, record&) {
+ return false;
+ }
+ };
+
+ template <typename T, typename>
+ struct qualified_interop_checker {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, type index_type, Handler&& handler, record& tracking) {
+ return stack_detail::unqualified_interop_check<T>(L_, index, index_type, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+ template <typename T, type expected, typename>
+ struct unqualified_checker {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ if constexpr (std::is_same_v<T, bool>) {
+ tracking.use(1);
+ bool success = lua_isboolean(L_, index) == 1;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, expected, type_of(L_, index), "");
+ }
+ return success;
+ }
+ else if constexpr (meta::any_same_v<T,
+ char
+#if SOL_IS_ON(SOL_CHAR8_T)
+ ,
+ char8_t
+#endif
+ ,
+ char16_t,
+ char32_t>) {
+ return stack::check<std::basic_string<T>>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else if constexpr (std::is_integral_v<T> || std::is_same_v<T, lua_Integer>) {
+ tracking.use(1);
+#if SOL_LUA_VERSION_I_ >= 503
+ // Lua 5.3 and greater checks for numeric precision
+#if SOL_IS_ON(SOL_STRINGS_ARE_NUMBERS)
+ // imprecise, sloppy conversions
+ int isnum = 0;
+ lua_tointegerx(L_, index, &isnum);
+ const bool success = isnum != 0;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::number, type_of(L_, index), detail::not_a_number_or_number_string_integral);
+ }
+#elif SOL_IS_ON(SOL_NUMBER_PRECISION_CHECKS)
+ // this check is precise, do not convert
+ if (lua_isinteger(L_, index) == 1) {
+ return true;
+ }
+ const bool success = false;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::number, type_of(L_, index), detail::not_a_number_integral);
+ }
+#else
+ // Numerics are neither safe nor string-convertible
+ type t = type_of(L_, index);
+ const bool success = t == type::number;
+#endif
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::number, type_of(L_, index), detail::not_a_number);
+ }
+ return success;
+#else
+ // Lua 5.2 and below checks
+#if SOL_IS_OFF(SOL_STRINGS_ARE_NUMBERS)
+ // must pre-check, because it will convert
+ type t = type_of(L_, index);
+ if (t != type::number) {
+ // expected type, actual type
+ handler(L_, index, type::number, t, detail::not_a_number);
+ return false;
+ }
+#endif // Do not allow strings to be numbers
+
+#if SOL_IS_ON(SOL_NUMBER_PRECISION_CHECKS)
+ int isnum = 0;
+ const lua_Number v = lua_tonumberx(L_, index, &isnum);
+ const bool success = isnum != 0 && static_cast<lua_Number>(llround(v)) == v;
+#else
+ const bool success = true;
+#endif // Safe numerics and number precision checking
+ if (!success) {
+ // Use defines to provide a better error message!
+#if SOL_IS_ON(SOL_STRINGS_ARE_NUMBERS)
+ handler(L_, index, type::number, type_of(L_, index), detail::not_a_number_or_number_string);
+#elif SOL_IS_ON(SOL_NUMBER_PRECISION_CHECKS)
+ handler(L_, index, type::number, t, detail::not_a_number_or_number_string);
+#else
+ handler(L_, index, type::number, t, detail::not_a_number);
+#endif
+ }
+ return success;
+#endif
+ }
+ else if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, lua_Number>) {
+ tracking.use(1);
+#if SOL_IS_ON(SOL_STRINGS_ARE_NUMBERS)
+ bool success = lua_isnumber(L_, index) == 1;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::number, type_of(L_, index), detail::not_a_number_or_number_string);
+ }
+ return success;
+#else
+ type t = type_of(L_, index);
+ bool success = t == type::number;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::number, t, detail::not_a_number);
+ }
+ return success;
+#endif // Strings are Numbers
+ }
+ else if constexpr (meta::any_same_v<T, type, this_state, this_main_state, this_environment, variadic_args>) {
+ (void)L_;
+ (void)index;
+ (void)handler;
+ tracking.use(0);
+ return true;
+ }
+ else if constexpr (is_unique_usertype_v<T>) {
+ using element = unique_usertype_element_t<T>;
+ using actual = unique_usertype_actual_t<T>;
+ const type indextype = type_of(L_, index);
+ tracking.use(1);
+ if (indextype != type::userdata) {
+ handler(L_, index, type::userdata, indextype, "value is not a userdata");
+ return false;
+ }
+ if (lua_getmetatable(L_, index) == 0) {
+ return true;
+ }
+ int metatableindex = lua_gettop(L_);
+ if (stack_detail::check_metatable<d::u<element>>(L_, metatableindex)) {
+ void* memory = lua_touserdata(L_, index);
+ memory = detail::align_usertype_unique_destructor(memory);
+ detail::unique_destructor& pdx = *static_cast<detail::unique_destructor*>(memory);
+ bool success = &detail::usertype_unique_alloc_destroy<element, actual> == pdx;
+ if (!success) {
+ memory = detail::align_usertype_unique_tag<true>(memory);
+#if 0
+ // New version, one day
+#else
+ const char*& name_tag = *static_cast<const char**>(memory);
+ success = usertype_traits<T>::qualified_name() == name_tag;
+#endif
+ if (!success) {
+ handler(L_, index, type::userdata, indextype, "value is a userdata but is not the correct unique usertype");
+ }
+ }
+ return success;
+ }
+ lua_pop(L_, 1);
+ handler(L_, index, type::userdata, indextype, "unrecognized userdata (not pushed by sol?)");
+ return false;
+ }
+ else if constexpr (meta::any_same_v<T, lua_nil_t, std::nullopt_t, nullopt_t>) {
+ bool success = lua_isnil(L_, index);
+ if (success) {
+ tracking.use(1);
+ return success;
+ }
+ tracking.use(0);
+ success = lua_isnone(L_, index);
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, expected, type_of(L_, index), "");
+ }
+ return success;
+ }
+ else if constexpr (std::is_same_v<T, env_key_t>) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ if (t == type::table || t == type::none || t == type::lua_nil || t == type::userdata) {
+ return true;
+ }
+ handler(L_, index, type::table, t, "value cannot not have a valid environment");
+ return true;
+ }
+ else if constexpr (std::is_same_v<T, detail::non_lua_nil_t>) {
+ return !stack::unqualified_check<lua_nil_t>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else if constexpr (meta::is_specialization_of_v<T, basic_lua_table>) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ if (t != type::table) {
+ handler(L_, index, type::table, t, "value is not a table");
+ return false;
+ }
+ return true;
+ }
+ else if constexpr (meta::is_specialization_of_v<T, basic_bytecode>) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ if (t != type::function) {
+ handler(L_, index, type::function, t, "value is not a function that can be dumped");
+ return false;
+ }
+ return true;
+ }
+ else if constexpr (meta::is_specialization_of_v<T, basic_environment>) {
+ tracking.use(1);
+ if (lua_getmetatable(L_, index) == 0) {
+ return true;
+ }
+ type t = type_of(L_, -1);
+ if (t == type::table || t == type::none || t == type::lua_nil) {
+ lua_pop(L_, 1);
+ return true;
+ }
+ if (t != type::userdata) {
+ lua_pop(L_, 1);
+ handler(L_, index, type::table, t, "value does not have a valid metatable");
+ return false;
+ }
+ return true;
+ }
+ else if constexpr (std::is_same_v<T, metatable_key_t>) {
+ tracking.use(1);
+ if (lua_getmetatable(L_, index) == 0) {
+ return true;
+ }
+ type t = type_of(L_, -1);
+ if (t == type::table || t == type::none || t == type::lua_nil) {
+ lua_pop(L_, 1);
+ return true;
+ }
+ if (t != type::userdata) {
+ lua_pop(L_, 1);
+ handler(L_, index, expected, t, "value does not have a valid metatable");
+ return false;
+ }
+ return true;
+ }
+ else if constexpr (std::is_same_v<T, luaL_Stream*> || std::is_same_v<T, luaL_Stream>) {
+ if (lua_getmetatable(L_, index) == 0) {
+ type t = type_of(L_, index);
+ handler(L_, index, expected, t, "value is not a valid luaL_Stream (has no metatable/is not a valid value)");
+ return false;
+ }
+ luaL_getmetatable(L_, LUA_FILEHANDLE);
+ if (type_of(L_, index) != type::table) {
+ type t = type_of(L_, index);
+ lua_pop(L_, 1);
+ handler(L_,
+ index,
+ expected,
+ t,
+ "value is not a valid luaL_Stream (there is no metatable for luaL_Stream -- did you forget to "
+ "my_lua_state.open_libraries(sol::lib::state) or equivalent?)");
+ return false;
+ }
+ int is_stream_table = lua_compare(L_, -1, -2, LUA_OPEQ);
+ lua_pop(L_, 2);
+ if (is_stream_table == 0) {
+ type t = type_of(L_, index);
+ handler(L_, index, expected, t, "value is not a valid luaL_Stream (incorrect metatable)");
+ return false;
+ }
+ return true;
+ }
+ else if constexpr (meta::is_optional_v<T>) {
+ using ValueType = typename T::value_type;
+ (void)handler;
+ type t = type_of(L_, index);
+ if (t == type::none) {
+ tracking.use(0);
+ return true;
+ }
+ if (t == type::lua_nil) {
+ tracking.use(1);
+ return true;
+ }
+ return stack::unqualified_check<ValueType>(L_, index, &no_panic, tracking);
+ }
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ else if constexpr (std::is_function_v<T> || (std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>)) {
+ return stack_detail::check_function_pointer<std::remove_pointer_t<T>>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+#endif
+ else if constexpr (expected == type::userdata) {
+ if constexpr (meta::any_same_v<T, userdata_value> || meta::is_specialization_of_v<T, basic_userdata>) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ bool success = t == type::userdata;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::userdata, t, "");
+ }
+ return success;
+ }
+ else if constexpr (meta::is_specialization_of_v<T, user>) {
+ unqualified_checker<lightuserdata_value, type::userdata> c;
+ (void)c;
+ return c.check(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ if constexpr (std::is_pointer_v<T>) {
+ return check_usertype<T>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else if constexpr (meta::is_specialization_of_v<T, std::reference_wrapper>) {
+ using T_internal = typename T::type;
+ return stack::check<T_internal>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ return check_usertype<T>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+ }
+ else if constexpr (expected == type::poly) {
+ tracking.use(1);
+ bool success = is_lua_reference_v<T> || !lua_isnone(L_, index);
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::poly, type_of(L_, index), "");
+ }
+ return success;
+ }
+ else if constexpr (expected == type::lightuserdata) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ bool success = t == type::userdata || t == type::lightuserdata;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::lightuserdata, t, "");
+ }
+ return success;
+ }
+ else if constexpr (expected == type::function) {
+ if constexpr (meta::any_same_v<T, lua_CFunction, std::remove_pointer_t<lua_CFunction>, c_closure>) {
+ tracking.use(1);
+ bool success = lua_iscfunction(L_, index) == 1;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, expected, type_of(L_, index), "");
+ }
+ return success;
+ }
+ else {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ if (t == type::lua_nil || t == type::none || t == type::function) {
+ // allow for lua_nil to be returned
+ return true;
+ }
+ if (t != type::userdata && t != type::table) {
+ handler(L_, index, type::function, t, "must be a function or table or a userdata");
+ return false;
+ }
+ // Do advanced check for call-style userdata?
+ static const auto& callkey = to_string(meta_function::call);
+ if (lua_getmetatable(L_, index) == 0) {
+ // No metatable, no __call key possible
+ handler(L_, index, type::function, t, "value is not a function and does not have overriden metatable");
+ return false;
+ }
+ if (lua_isnoneornil(L_, -1)) {
+ lua_pop(L_, 1);
+ handler(L_, index, type::function, t, "value is not a function and does not have valid metatable");
+ return false;
+ }
+ lua_getfield(L_, -1, &callkey[0]);
+ if (lua_isnoneornil(L_, -1)) {
+ lua_pop(L_, 2);
+ handler(L_, index, type::function, t, "value's metatable does not have __call overridden in metatable, cannot call this type");
+ return false;
+ }
+ // has call, is definitely a function
+ lua_pop(L_, 2);
+ return true;
+ }
+ }
+ else if constexpr (expected == type::table) {
+ return stack::loose_table_check(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ tracking.use(1);
+ const type indextype = type_of(L_, index);
+ bool success = expected == indextype;
+ if (!success) {
+ // expected type, actual type, message
+ handler(L_, index, expected, indextype, "");
+ }
+ return success;
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_checker<non_null<T>, type::userdata> : unqualified_checker<T, lua_type_of_v<T>> { };
+
+ template <typename T>
+ struct unqualified_checker<detail::as_value_tag<T>, type::userdata> {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ const type indextype = type_of(L_, index);
+ return check(types<T>(), L_, index, indextype, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename U, typename Handler>
+ static bool check(types<U>, lua_State* L_, int index, type indextype, Handler&& handler, record& tracking) {
+ if constexpr (
+ std::is_same_v<T,
+ lightuserdata_value> || std::is_same_v<T, userdata_value> || std::is_same_v<T, userdata> || std::is_same_v<T, lightuserdata>) {
+ tracking.use(1);
+ if (indextype != type::userdata) {
+ handler(L_, index, type::userdata, indextype, "value is not a valid userdata");
+ return false;
+ }
+ return true;
+ }
+ else {
+#if SOL_IS_ON(SOL_USE_INTEROP)
+ if (stack_detail::interop_check<U>(L_, index, indextype, handler, tracking)) {
+ return true;
+ }
+#endif // interop extensibility
+ tracking.use(1);
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ if (lua_iscfunction(L_, index) != 0) {
+ // a potential match...
+ return true;
+ }
+#endif
+ if (indextype != type::userdata) {
+ handler(L_, index, type::userdata, indextype, "value is not a valid userdata");
+ return false;
+ }
+ if (lua_getmetatable(L_, index) == 0) {
+ return true;
+ }
+ int metatableindex = lua_gettop(L_);
+ if (stack_detail::check_metatable<U>(L_, metatableindex))
+ return true;
+ if (stack_detail::check_metatable<U*>(L_, metatableindex))
+ return true;
+ if (stack_detail::check_metatable<d::u<U>>(L_, metatableindex))
+ return true;
+ if (stack_detail::check_metatable<as_container_t<U>>(L_, metatableindex))
+ return true;
+ bool success = false;
+ bool has_derived = derive<T>::value || weak_derive<T>::value;
+ if (has_derived) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ auto pn = stack::pop_n(L_, 1);
+ lua_pushstring(L_, &detail::base_class_check_key()[0]);
+ lua_rawget(L_, metatableindex);
+ if (type_of(L_, -1) != type::lua_nil) {
+ void* basecastdata = lua_touserdata(L_, -1);
+ detail::inheritance_check_function ic = reinterpret_cast<detail::inheritance_check_function>(basecastdata);
+ success = ic(usertype_traits<T>::qualified_name());
+ }
+ }
+ lua_pop(L_, 1);
+ if (!success) {
+ handler(L_, index, type::userdata, indextype, "value at this index does not properly reflect the desired type");
+ return false;
+ }
+ return true;
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_checker<detail::as_pointer_tag<T>, type::userdata> {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, type indextype, Handler&& handler, record& tracking) {
+ if (indextype == type::lua_nil) {
+ tracking.use(1);
+ return true;
+ }
+ return check_usertype<std::remove_pointer_t<T>>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ const type indextype = type_of(L_, index);
+ return check(L_, index, indextype, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+ template <typename T, std::size_t N, type expect>
+ struct unqualified_checker<exhaustive_until<T, N>, expect> {
+ template <typename K, typename V, typename Handler>
+ static bool check_two(types<K, V>, lua_State* arg_L, int relindex, type, Handler&& handler, record& tracking) {
+ tracking.use(1);
+
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(arg_L, 3, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+
+ int index = lua_absindex(arg_L, relindex);
+ lua_pushnil(arg_L);
+ while (lua_next(arg_L, index) != 0) {
+ const bool is_key_okay = stack::check<K>(arg_L, -2, std::forward<Handler>(handler), tracking);
+ if (!is_key_okay) {
+ lua_pop(arg_L, 2);
+ return false;
+ }
+ const bool is_value_okay = stack::check<V>(arg_L, -1, std::forward<Handler>(handler), tracking);
+ if (!is_value_okay) {
+ lua_pop(arg_L, 2);
+ return false;
+ }
+ lua_pop(arg_L, 1);
+ }
+ return true;
+ }
+
+ template <typename V, typename Handler>
+ static bool check_one(types<V>, lua_State* arg_L, int relindex, type, Handler&& handler, record& tracking) {
+ tracking.use(1);
+
+ size_t index = lua_absindex(arg_L, relindex);
+ // Zzzz slower but necessary thanks to the lower version API and missing functions qq
+ std::size_t idx = 0;
+ int vi = 0;
+ for (lua_Integer i = 0;; (void)(i += lua_size<V>::value), lua_pop(arg_L, static_cast<int>(vi))) {
+ vi = 0;
+ if (idx >= N) {
+ return true;
+ }
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(arg_L, 2, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ bool isnil = false;
+ for (; vi < static_cast<int>(lua_size<V>::value); ++vi) {
+ lua_pushinteger(arg_L, i);
+ lua_gettable(arg_L, static_cast<int>(index));
+ type vt = type_of(arg_L, -1);
+ isnil = vt == type::lua_nil;
+ if (isnil) {
+ if (i == 0) {
+ vi += 1;
+ goto loop_continue;
+ }
+ lua_pop(arg_L, static_cast<int>(vi + 1));
+ return true;
+ }
+ }
+ if (!stack::check<V>(arg_L, -lua_size<V>::value, std::forward<Handler>(handler), tracking)) {
+ lua_pop(arg_L, lua_size<V>::value);
+ return false;
+ }
+ ++idx;
+ loop_continue:;
+ }
+ }
+
+ template <typename Handler>
+ static bool check(lua_State* arg_L, int index, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (is_container_v<Tu>) {
+ if constexpr (meta::is_associative<Tu>::value) {
+ typedef typename Tu::value_type P;
+ typedef typename P::first_type K;
+ typedef typename P::second_type V;
+ return check_two(types<K, V>(), arg_L, index, expect, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ typedef typename Tu::value_type V;
+ return check_one(types<V>(), arg_L, index, expect, std::forward<Handler>(handler), tracking);
+ }
+ }
+ else {
+ unqualified_checker<Tu, expect> c {};
+ return c.check(arg_L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+ };
+
+ template <typename T, type expect>
+ struct unqualified_checker<non_exhaustive<T>, expect> {
+ template <typename Handler>
+ static bool check(lua_State* arg_L, int index, Handler&& handler, record& tracking) {
+ return stack::check<T>(arg_L, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+ template <typename... Args>
+ struct unqualified_checker<std::tuple<Args...>, type::poly> {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ return stack::multi_check<Args...>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+ template <typename A, typename B>
+ struct unqualified_checker<std::pair<A, B>, type::poly> {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ return stack::multi_check<A, B>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+#if SOL_IS_ON(SOL_STD_VARIANT)
+
+ template <typename... Tn>
+ struct unqualified_checker<std::variant<Tn...>, type::poly> {
+ typedef std::variant<Tn...> V;
+ typedef std::variant_size<V> V_size;
+ typedef std::integral_constant<bool, V_size::value == 0> V_is_empty;
+
+ template <typename Handler>
+ static bool is_one(std::integral_constant<std::size_t, 0>, lua_State* L_, int index, Handler&& handler, record& tracking) {
+ if constexpr (V_is_empty::value) {
+ if (lua_isnone(L_, index)) {
+ return true;
+ }
+ }
+ tracking.use(1);
+ handler(L_, index, type::poly, type_of(L_, index), "value does not fit any type present in the variant");
+ return false;
+ }
+
+ template <std::size_t I, typename Handler>
+ static bool is_one(std::integral_constant<std::size_t, I>, lua_State* L_, int index, Handler&& handler, record& tracking) {
+ typedef std::variant_alternative_t<I - 1, V> T;
+ record temp_tracking = tracking;
+ if (stack::check<T>(L_, index, &no_panic, temp_tracking)) {
+ tracking = temp_tracking;
+ return true;
+ }
+ return is_one(std::integral_constant<std::size_t, I - 1>(), L_, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ return is_one(std::integral_constant<std::size_t, V_size::value>(), L_, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+#endif // variant shenanigans
+
+}} // namespace sol::stack
+
+// end of sol/stack_check_unqualified.hpp
+
+// beginning of sol/stack_check_qualified.hpp
+
+namespace sol { namespace stack {
+
+ template <typename X, type expected, typename>
+ struct qualified_checker {
+ template <typename Handler>
+ static bool check(lua_State* L, int index, Handler&& handler, record& tracking) {
+ using no_cv_X = meta::unqualified_t<X>;
+ if constexpr (!std::is_reference_v<X> && is_unique_usertype_v<no_cv_X>) {
+ using element = unique_usertype_element_t<no_cv_X>;
+ if constexpr (is_actual_type_rebindable_for_v<no_cv_X>) {
+ using rebound_actual_type = unique_usertype_rebind_actual_t<no_cv_X>;
+ // we have a unique pointer type that can be
+ // rebound to a base/derived type
+ const type indextype = type_of(L, index);
+ tracking.use(1);
+ if (indextype != type::userdata) {
+ handler(L, index, type::userdata, indextype, "value is not a userdata");
+ return false;
+ }
+ void* memory = lua_touserdata(L, index);
+ memory = detail::align_usertype_unique_destructor(memory);
+ detail::unique_destructor& pdx = *static_cast<detail::unique_destructor*>(memory);
+ if (&detail::usertype_unique_alloc_destroy<element, no_cv_X> == pdx) {
+ return true;
+ }
+ if constexpr (derive<element>::value) {
+ memory = detail::align_usertype_unique_tag<true, false>(memory);
+ detail::unique_tag& ic = *reinterpret_cast<detail::unique_tag*>(memory);
+ string_view ti = usertype_traits<element>::qualified_name();
+ string_view rebind_ti = usertype_traits<rebound_actual_type>::qualified_name();
+ if (ic(nullptr, nullptr, ti, rebind_ti) != 0) {
+ return true;
+ }
+ }
+ handler(L, index, type::userdata, indextype, "value is a userdata but is not the correct unique usertype");
+ return false;
+ }
+ else {
+ return stack::unqualified_check<X>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+ else if constexpr (!std::is_reference_v<X> && is_container_v<no_cv_X>) {
+ if (type_of(L, index) == type::userdata) {
+ return stack::unqualified_check<X>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ return stack::unqualified_check<nested<X>>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+ else if constexpr (!std::is_reference_v<X> && meta::is_specialization_of_v<X, nested>) {
+ using NestedX = typename meta::unqualified_t<X>::nested_type;
+ return stack::check<NestedX>(L, index, ::std::forward<Handler>(handler), tracking);
+ }
+ else {
+ return stack::unqualified_check<X>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+ };
+}} // namespace sol::stack
+
+// end of sol/stack_check_qualified.hpp
+
+// end of sol/stack_check.hpp
+
+// beginning of sol/stack_get.hpp
+
+// beginning of sol/stack_get_unqualified.hpp
+
+// beginning of sol/overload.hpp
+
+#include <utility>
+
+namespace sol {
+ template <typename... Functions>
+ struct overload_set {
+ std::tuple<Functions...> functions;
+ template <typename Arg, typename... Args, meta::disable<std::is_same<overload_set, meta::unqualified_t<Arg>>> = meta::enabler>
+ overload_set(Arg&& arg, Args&&... args) : functions(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+ overload_set(const overload_set&) = default;
+ overload_set(overload_set&&) = default;
+ overload_set& operator=(const overload_set&) = default;
+ overload_set& operator=(overload_set&&) = default;
+ };
+
+ template <typename... Args>
+ decltype(auto) overload(Args&&... args) {
+ return overload_set<std::decay_t<Args>...>(std::forward<Args>(args)...);
+ }
+} // namespace sol
+
+// end of sol/overload.hpp
+
+// beginning of sol/unicode.hpp
+
+#include <array>
+#include <cstring>
+
+namespace sol {
+ // Everything here was lifted pretty much straight out of
+ // ogonek, because fuck figuring it out=
+ namespace unicode {
+ enum class error_code {
+ ok = 0,
+ invalid_code_point,
+ invalid_code_unit,
+ invalid_leading_surrogate,
+ invalid_trailing_surrogate,
+ sequence_too_short,
+ overlong_sequence,
+ };
+
+ inline const string_view& to_string(error_code ec) {
+ static const string_view storage[7] = { "ok",
+ "invalid code points",
+ "invalid code unit",
+ "invalid leading surrogate",
+ "invalid trailing surrogate",
+ "sequence too short",
+ "overlong sequence" };
+ return storage[static_cast<std::size_t>(ec)];
+ }
+
+ template <typename It>
+ struct decoded_result {
+ error_code error;
+ char32_t codepoint;
+ It next;
+ };
+
+ template <typename C>
+ struct encoded_result {
+ error_code error;
+ std::size_t code_units_size;
+ std::array<C, 4> code_units;
+ };
+
+ struct unicode_detail {
+ // codepoint related
+ static constexpr char32_t last_code_point = 0x10FFFF;
+
+ static constexpr char32_t first_lead_surrogate = 0xD800;
+ static constexpr char32_t last_lead_surrogate = 0xDBFF;
+
+ static constexpr char32_t first_trail_surrogate = 0xDC00;
+ static constexpr char32_t last_trail_surrogate = 0xDFFF;
+
+ static constexpr char32_t first_surrogate = first_lead_surrogate;
+ static constexpr char32_t last_surrogate = last_trail_surrogate;
+
+ static constexpr bool is_lead_surrogate(char32_t u) {
+ return u >= first_lead_surrogate && u <= last_lead_surrogate;
+ }
+ static constexpr bool is_trail_surrogate(char32_t u) {
+ return u >= first_trail_surrogate && u <= last_trail_surrogate;
+ }
+ static constexpr bool is_surrogate(char32_t u) {
+ return u >= first_surrogate && u <= last_surrogate;
+ }
+
+ // utf8 related
+ static constexpr auto last_1byte_value = 0x7Fu;
+ static constexpr auto last_2byte_value = 0x7FFu;
+ static constexpr auto last_3byte_value = 0xFFFFu;
+
+ static constexpr auto start_2byte_mask = 0x80u;
+ static constexpr auto start_3byte_mask = 0xE0u;
+ static constexpr auto start_4byte_mask = 0xF0u;
+
+ static constexpr auto continuation_mask = 0xC0u;
+ static constexpr auto continuation_signature = 0x80u;
+
+ static constexpr bool is_invalid(unsigned char b) {
+ return b == 0xC0 || b == 0xC1 || b > 0xF4;
+ }
+
+ static constexpr bool is_continuation(unsigned char b) {
+ return (b & unicode_detail::continuation_mask) == unicode_detail::continuation_signature;
+ }
+
+ static constexpr bool is_overlong(char32_t u, std::size_t bytes) {
+ return u <= unicode_detail::last_1byte_value || (u <= unicode_detail::last_2byte_value && bytes > 2)
+ || (u <= unicode_detail::last_3byte_value && bytes > 3);
+ }
+
+ static constexpr int sequence_length(unsigned char b) {
+ return (b & start_2byte_mask) == 0 ? 1
+ : (b & start_3byte_mask) != start_3byte_mask ? 2
+ : (b & start_4byte_mask) != start_4byte_mask ? 3
+ : 4;
+ }
+
+ static constexpr char32_t decode(unsigned char b0, unsigned char b1) {
+ return (static_cast<char32_t>((b0 & 0x1Fu) << 6u) | static_cast<char32_t>(b1 & 0x3Fu));
+ }
+ static constexpr char32_t decode(unsigned char b0, unsigned char b1, unsigned char b2) {
+ return static_cast<char32_t>((b0 & 0x0Fu) << 12u) | static_cast<char32_t>((b1 & 0x3Fu) << 6u) | static_cast<char32_t>(b2 & 0x3Fu);
+ }
+ static constexpr char32_t decode(unsigned char b0, unsigned char b1, unsigned char b2, unsigned char b3) {
+ return static_cast<char32_t>(static_cast<char32_t>((b0 & 0x07u) << 18u) | static_cast<char32_t>((b1 & 0x3F) << 12)
+ | static_cast<char32_t>((b2 & 0x3Fu) << 6u) | static_cast<char32_t>(b3 & 0x3Fu));
+ }
+
+ // utf16 related
+ static constexpr char32_t last_bmp_value = 0xFFFF;
+ static constexpr char32_t normalizing_value = 0x10000;
+ static constexpr int lead_surrogate_bitmask = 0xFFC00;
+ static constexpr int trail_surrogate_bitmask = 0x3FF;
+ static constexpr int lead_shifted_bits = 10;
+ static constexpr char32_t replacement = 0xFFFD;
+
+ static char32_t combine_surrogates(char16_t lead, char16_t trail) {
+ auto hi = lead - first_lead_surrogate;
+ auto lo = trail - first_trail_surrogate;
+ return normalizing_value + ((hi << lead_shifted_bits) | lo);
+ }
+ };
+
+ inline encoded_result<char> code_point_to_utf8(char32_t codepoint) {
+ encoded_result<char> er;
+ er.error = error_code::ok;
+ if (codepoint <= unicode_detail::last_1byte_value) {
+ er.code_units_size = 1;
+ er.code_units = std::array<char, 4> { { static_cast<char>(codepoint) } };
+ }
+ else if (codepoint <= unicode_detail::last_2byte_value) {
+ er.code_units_size = 2;
+ er.code_units = std::array<char, 4> { {
+ static_cast<char>(0xC0 | ((codepoint & 0x7C0) >> 6)),
+ static_cast<char>(0x80 | (codepoint & 0x3F)),
+ } };
+ }
+ else if (codepoint <= unicode_detail::last_3byte_value) {
+ er.code_units_size = 3;
+ er.code_units = std::array<char, 4> { {
+ static_cast<char>(0xE0 | ((codepoint & 0xF000) >> 12)),
+ static_cast<char>(0x80 | ((codepoint & 0xFC0) >> 6)),
+ static_cast<char>(0x80 | (codepoint & 0x3F)),
+ } };
+ }
+ else {
+ er.code_units_size = 4;
+ er.code_units = std::array<char, 4> { {
+ static_cast<char>(0xF0 | ((codepoint & 0x1C0000) >> 18)),
+ static_cast<char>(0x80 | ((codepoint & 0x3F000) >> 12)),
+ static_cast<char>(0x80 | ((codepoint & 0xFC0) >> 6)),
+ static_cast<char>(0x80 | (codepoint & 0x3F)),
+ } };
+ }
+ return er;
+ }
+
+ inline encoded_result<char16_t> code_point_to_utf16(char32_t codepoint) {
+ encoded_result<char16_t> er;
+
+ if (codepoint <= unicode_detail::last_bmp_value) {
+ er.code_units_size = 1;
+ er.code_units = std::array<char16_t, 4> { { static_cast<char16_t>(codepoint) } };
+ er.error = error_code::ok;
+ }
+ else {
+ auto normal = codepoint - unicode_detail::normalizing_value;
+ auto lead = unicode_detail::first_lead_surrogate + ((normal & unicode_detail::lead_surrogate_bitmask) >> unicode_detail::lead_shifted_bits);
+ auto trail = unicode_detail::first_trail_surrogate + (normal & unicode_detail::trail_surrogate_bitmask);
+ er.code_units = std::array<char16_t, 4> { { static_cast<char16_t>(lead), static_cast<char16_t>(trail) } };
+ er.code_units_size = 2;
+ er.error = error_code::ok;
+ }
+ return er;
+ }
+
+ inline encoded_result<char32_t> code_point_to_utf32(char32_t codepoint) {
+ encoded_result<char32_t> er;
+ er.code_units_size = 1;
+ er.code_units[0] = codepoint;
+ er.error = error_code::ok;
+ return er;
+ }
+
+ template <typename It>
+ inline decoded_result<It> utf8_to_code_point(It it, It last) {
+ decoded_result<It> dr;
+ if (it == last) {
+ dr.next = it;
+ dr.error = error_code::sequence_too_short;
+ return dr;
+ }
+
+ unsigned char b0 = static_cast<unsigned char>(*it);
+ std::size_t length = static_cast<std::size_t>(unicode_detail::sequence_length(b0));
+
+ if (length == 1) {
+ dr.codepoint = static_cast<char32_t>(b0);
+ dr.error = error_code::ok;
+ ++it;
+ dr.next = it;
+ return dr;
+ }
+
+ if (unicode_detail::is_invalid(b0) || unicode_detail::is_continuation(b0)) {
+ dr.error = error_code::invalid_code_unit;
+ dr.next = it;
+ return dr;
+ }
+
+ ++it;
+ std::array<unsigned char, 4> b;
+ b[0] = b0;
+ for (std::size_t i = 1; i < length; ++i) {
+ b[i] = static_cast<unsigned char>(*it);
+ if (!unicode_detail::is_continuation(b[i])) {
+ dr.error = error_code::invalid_code_unit;
+ dr.next = it;
+ return dr;
+ }
+ ++it;
+ }
+
+ char32_t decoded;
+ switch (length) {
+ case 2:
+ decoded = unicode_detail::decode(b[0], b[1]);
+ break;
+ case 3:
+ decoded = unicode_detail::decode(b[0], b[1], b[2]);
+ break;
+ default:
+ decoded = unicode_detail::decode(b[0], b[1], b[2], b[3]);
+ break;
+ }
+
+ if (unicode_detail::is_overlong(decoded, length)) {
+ dr.error = error_code::overlong_sequence;
+ return dr;
+ }
+ if (unicode_detail::is_surrogate(decoded) || decoded > unicode_detail::last_code_point) {
+ dr.error = error_code::invalid_code_point;
+ return dr;
+ }
+
+ // then everything is fine
+ dr.codepoint = decoded;
+ dr.error = error_code::ok;
+ dr.next = it;
+ return dr;
+ }
+
+ template <typename It>
+ inline decoded_result<It> utf16_to_code_point(It it, It last) {
+ decoded_result<It> dr;
+ if (it == last) {
+ dr.next = it;
+ dr.error = error_code::sequence_too_short;
+ return dr;
+ }
+
+ char16_t lead = static_cast<char16_t>(*it);
+
+ if (!unicode_detail::is_surrogate(lead)) {
+ ++it;
+ dr.codepoint = static_cast<char32_t>(lead);
+ dr.next = it;
+ dr.error = error_code::ok;
+ return dr;
+ }
+ if (!unicode_detail::is_lead_surrogate(lead)) {
+ dr.error = error_code::invalid_leading_surrogate;
+ dr.next = it;
+ return dr;
+ }
+
+ ++it;
+ auto trail = *it;
+ if (!unicode_detail::is_trail_surrogate(trail)) {
+ dr.error = error_code::invalid_trailing_surrogate;
+ dr.next = it;
+ return dr;
+ }
+
+ dr.codepoint = unicode_detail::combine_surrogates(lead, trail);
+ dr.next = ++it;
+ dr.error = error_code::ok;
+ return dr;
+ }
+
+ template <typename It>
+ inline decoded_result<It> utf32_to_code_point(It it, It last) {
+ decoded_result<It> dr;
+ if (it == last) {
+ dr.next = it;
+ dr.error = error_code::sequence_too_short;
+ return dr;
+ }
+ dr.codepoint = static_cast<char32_t>(*it);
+ dr.next = ++it;
+ dr.error = error_code::ok;
+ return dr;
+ }
+ } // namespace unicode
+} // namespace sol
+// end of sol/unicode.hpp
+
+// beginning of sol/abort.hpp
+
+#include <cstdlib>
+
+#if SOL_IS_ON(SOL_DEBUG_BUILD)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #define SOL_DEBUG_ABORT() \
+ if (true) { ::std::abort(); } \
+ static_assert(true, "")
+ #else
+ #define SOL_DEBUG_ABORT() ::std::abort()
+ #endif
+#else
+ #define SOL_DEBUG_ABORT() static_assert(true, "")
+#endif
+
+// end of sol/abort.hpp
+
+#include <memory>
+#include <functional>
+#include <utility>
+#include <cstdlib>
+#include <cmath>
+#include <string_view>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // Apple clang screwed up
+
+namespace sol { namespace stack {
+
+ namespace stack_detail {
+ template <typename Ch>
+ struct count_code_units_utf {
+ std::size_t needed_size;
+
+ count_code_units_utf() : needed_size(0) {
+ }
+
+ void operator()(const unicode::encoded_result<Ch> er) {
+ needed_size += er.code_units_size;
+ }
+ };
+
+ template <typename Ch, typename ErCh>
+ struct copy_code_units_utf {
+ Ch* target_;
+
+ copy_code_units_utf(Ch* target) : target_(target) {
+ }
+
+ void operator()(const unicode::encoded_result<ErCh> er) {
+ std::memcpy(target_, er.code_units.data(), er.code_units_size * sizeof(ErCh));
+ target_ += er.code_units_size;
+ }
+ };
+
+ template <typename Ch, typename F>
+ inline void convert(const char* strb, const char* stre, F&& f) {
+ char32_t cp = 0;
+ for (const char* strtarget = strb; strtarget < stre;) {
+ auto dr = unicode::utf8_to_code_point(strtarget, stre);
+ if (dr.error != unicode::error_code::ok) {
+ cp = unicode::unicode_detail::replacement;
+ ++strtarget;
+ }
+ else {
+ cp = dr.codepoint;
+ strtarget = dr.next;
+ }
+ if constexpr (std::is_same_v<Ch, char32_t>) {
+ auto er = unicode::code_point_to_utf32(cp);
+ f(er);
+ }
+ else {
+ auto er = unicode::code_point_to_utf16(cp);
+ f(er);
+ }
+ }
+ }
+
+ template <typename BaseCh, typename S>
+ inline S get_into(lua_State* L, int index, record& tracking) {
+ using Ch = typename S::value_type;
+ tracking.use(1);
+ size_t len;
+ auto utf8p = lua_tolstring(L, index, &len);
+ if (len < 1)
+ return S();
+ const char* strb = utf8p;
+ const char* stre = utf8p + len;
+ stack_detail::count_code_units_utf<BaseCh> count_units;
+ convert<BaseCh>(strb, stre, count_units);
+ S r(count_units.needed_size, static_cast<Ch>(0));
+ r.resize(count_units.needed_size);
+ Ch* target = &r[0];
+ stack_detail::copy_code_units_utf<Ch, BaseCh> copy_units(target);
+ convert<BaseCh>(strb, stre, copy_units);
+ return r;
+ }
+ } // namespace stack_detail
+
+ template <typename T, typename>
+ struct unqualified_getter {
+ static decltype(auto) get(lua_State* L, int index, record& tracking) {
+ if constexpr (std::is_same_v<T, bool>) {
+ tracking.use(1);
+ return lua_toboolean(L, index) != 0;
+ }
+ else if constexpr (std::is_enum_v<T>) {
+ tracking.use(1);
+ return static_cast<T>(lua_tointegerx(L, index, nullptr));
+ }
+ else if constexpr (std::is_integral_v<T> || std::is_same_v<T, lua_Integer>) {
+ tracking.use(1);
+#if SOL_LUA_VERSION_I_ >= 503
+ if (lua_isinteger(L, index) != 0) {
+ return static_cast<T>(lua_tointeger(L, index));
+ }
+#endif
+ return static_cast<T>(llround(lua_tonumber(L, index)));
+ }
+ else if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, lua_Number>) {
+ tracking.use(1);
+ return static_cast<T>(lua_tonumber(L, index));
+ }
+ else if constexpr (is_lua_reference_v<T>) {
+ if constexpr (is_global_table_v<T>) {
+ tracking.use(1);
+ return T(L, global_tag);
+ }
+ else {
+ tracking.use(1);
+ return T(L, index);
+ }
+ }
+ else if constexpr (is_unique_usertype_v<T>) {
+ using actual = unique_usertype_actual_t<T>;
+
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ void* aligned_memory = detail::align_usertype_unique<actual>(memory);
+ actual* typed_memory = static_cast<actual*>(aligned_memory);
+ return *typed_memory;
+ }
+ else if constexpr (meta::is_optional_v<T>) {
+ using ValueType = typename T::value_type;
+ return unqualified_check_getter<ValueType>::template get_using<T>(L, index, &no_panic, tracking);
+ }
+ else if constexpr (std::is_same_v<T, luaL_Stream*>) {
+ luaL_Stream* pstream = static_cast<luaL_Stream*>(lua_touserdata(L, index));
+ return pstream;
+ }
+ else if constexpr (std::is_same_v<T, luaL_Stream>) {
+ luaL_Stream* pstream = static_cast<luaL_Stream*>(lua_touserdata(L, index));
+ return *pstream;
+ }
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ else if constexpr (std::is_function_v<T> || (std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>)) {
+ return stack_detail::get_function_pointer<std::remove_pointer_t<T>>(L, index, tracking);
+ }
+#endif
+ else {
+ return stack_detail::unchecked_unqualified_get<detail::as_value_tag<T>>(L, index, tracking);
+ }
+ }
+ };
+
+ template <typename X, typename>
+ struct qualified_getter {
+ static decltype(auto) get(lua_State* L, int index, record& tracking) {
+ using Tu = meta::unqualified_t<X>;
+ static constexpr bool is_maybe_userdata_of_some_kind
+ = !std::is_reference_v<
+ X> && is_container_v<Tu> && std::is_default_constructible_v<Tu> && !is_lua_primitive_v<Tu> && !is_transparent_argument_v<Tu>;
+ if constexpr (is_maybe_userdata_of_some_kind) {
+ if (type_of(L, index) == type::userdata) {
+ return static_cast<Tu>(stack_detail::unchecked_unqualified_get<Tu>(L, index, tracking));
+ }
+ else {
+ return stack_detail::unchecked_unqualified_get<sol::nested<Tu>>(L, index, tracking);
+ }
+ }
+ else if constexpr (!std::is_reference_v<X> && is_unique_usertype_v<Tu> && !is_actual_type_rebindable_for_v<Tu>) {
+ using element = unique_usertype_element_t<Tu>;
+ using actual = unique_usertype_actual_t<Tu>;
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ memory = detail::align_usertype_unique_destructor(memory);
+ detail::unique_destructor& pdx = *static_cast<detail::unique_destructor*>(memory);
+ if (&detail::usertype_unique_alloc_destroy<element, Tu> == pdx) {
+ memory = detail::align_usertype_unique_tag<true, false>(memory);
+ memory = detail::align_usertype_unique<actual, true, false>(memory);
+ actual* mem = static_cast<actual*>(memory);
+ return static_cast<actual>(*mem);
+ }
+ actual r {};
+ if constexpr (!derive<element>::value) {
+ // In debug mode we would rather abort you for this grave failure rather
+ // than let you deref a null pointer and fuck everything over
+ SOL_DEBUG_ABORT();
+ return static_cast<actual>(std::move(r));
+ }
+ else {
+ memory = detail::align_usertype_unique_tag<true, false>(memory);
+ detail::unique_tag& ic = *reinterpret_cast<detail::unique_tag*>(memory);
+ memory = detail::align_usertype_unique<actual, true, false>(memory);
+ string_view ti = usertype_traits<element>::qualified_name();
+ int cast_operation;
+ if constexpr (is_actual_type_rebindable_for_v<Tu>) {
+ using rebound_actual_type = unique_usertype_rebind_actual_t<Tu, void>;
+ string_view rebind_ti = usertype_traits<rebound_actual_type>::qualified_name();
+ cast_operation = ic(memory, &r, ti, rebind_ti);
+ }
+ else {
+ string_view rebind_ti("");
+ cast_operation = ic(memory, &r, ti, rebind_ti);
+ }
+ switch (cast_operation) {
+ case 1: {
+ // it's a perfect match,
+ // alias memory directly
+ actual* mem = static_cast<actual*>(memory);
+ return static_cast<actual>(*mem);
+ }
+ case 2:
+ // it's a base match, return the
+ // aliased creation
+ return static_cast<actual>(std::move(r));
+ default:
+ // uh oh..
+ break;
+ }
+ SOL_DEBUG_ABORT();
+ return static_cast<actual>(r);
+ }
+ }
+ else {
+ return stack_detail::unchecked_unqualified_get<Tu>(L, index, tracking);
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<as_table_t<T>> {
+ using Tu = meta::unqualified_t<T>;
+
+ template <typename V>
+ static void push_back_at_end(std::true_type, types<V>, lua_State* L, T& cont, std::size_t) {
+ cont.push_back(stack::get<V>(L, -lua_size<V>::value));
+ }
+
+ template <typename V>
+ static void push_back_at_end(std::false_type, types<V> t, lua_State* L, T& cont, std::size_t idx) {
+ insert_at_end(meta::has_insert<Tu>(), t, L, cont, idx);
+ }
+
+ template <typename V>
+ static void insert_at_end(std::true_type, types<V>, lua_State* L, T& cont, std::size_t) {
+ using std::cend;
+ cont.insert(cend(cont), stack::get<V>(L, -lua_size<V>::value));
+ }
+
+ template <typename V>
+ static void insert_at_end(std::false_type, types<V>, lua_State* L, T& cont, std::size_t idx) {
+ cont[idx] = stack::get<V>(L, -lua_size<V>::value);
+ }
+
+ static bool max_size_check(std::false_type, T&, std::size_t) {
+ return false;
+ }
+
+ static bool max_size_check(std::true_type, T& cont, std::size_t idx) {
+ return idx >= cont.max_size();
+ }
+
+ static T get(lua_State* L, int relindex, record& tracking) {
+ return get(meta::is_associative<Tu>(), L, relindex, tracking);
+ }
+
+ static T get(std::false_type, lua_State* L, int relindex, record& tracking) {
+ typedef typename Tu::value_type V;
+ return get(types<V>(), L, relindex, tracking);
+ }
+
+ template <typename V>
+ static T get(types<V> t, lua_State* L, int relindex, record& tracking) {
+ tracking.use(1);
+
+ // the W4 flag is really great,
+ // so great that it can tell my for loops (twice nested)
+ // below never actually terminate
+ // without hitting where the gotos have infested
+
+ // so now I would get the error W4XXX unreachable
+ // me that the return cont at the end of this function
+ // which is fair until other compilers complain
+ // that there isn't a return and that based on
+ // SOME MAGICAL FORCE
+ // control flow falls off the end of a non-void function
+ // so it needs to be there for the compilers that are
+ // too flimsy to analyze the basic blocks...
+ // (I'm sure I should file a bug but those compilers are already
+ // in the wild; it doesn't matter if I fix them,
+ // someone else is still going to get some old-ass compiler
+ // and then bother me about the unclean build for the 30th
+ // time)
+
+ // "Why not an IIFE?"
+ // Because additional lambdas / functions which serve as
+ // capture-all-and-then-invoke bloat binary sizes
+ // by an actually detectable amount
+ // (one user uses sol2 pretty heavily and 22 MB of binary size
+ // was saved by reducing reliance on lambdas in templates)
+
+ // This would really be solved by having break N;
+ // be a real, proper thing...
+ // but instead, we have to use labels and gotos
+ // and earn the universal vitriol of the dogmatic
+ // programming community
+
+ // all in all: W4 is great!~
+
+ int index = lua_absindex(L, relindex);
+ T cont;
+ std::size_t idx = 0;
+#if SOL_LUA_VERSION_I_ >= 503
+ // This method is HIGHLY performant over regular table iteration
+ // thanks to the Lua API changes in 5.3
+ // Questionable in 5.4
+ for (lua_Integer i = 0;; i += lua_size<V>::value) {
+ if (max_size_check(meta::has_max_size<Tu>(), cont, idx)) {
+ // see above comment
+ goto done;
+ }
+ bool isnil = false;
+ for (int vi = 0; vi < lua_size<V>::value; ++vi) {
+#if SOL_IS_ON(SOL_LUA_NIL_IN_TABLES) && SOL_LUA_VERSION_I_ >= 600
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushinteger(L, static_cast<lua_Integer>(i + vi));
+ if (lua_keyin(L, index) == 0) {
+ // it's time to stop
+ isnil = true;
+ }
+ else {
+ // we have a key, have to get the value
+ lua_geti(L, index, i + vi);
+ }
+#else
+ type vt = static_cast<type>(lua_geti(L, index, i + vi));
+ isnil = vt == type::none || vt == type::lua_nil;
+#endif
+ if (isnil) {
+ if (i == 0) {
+ break;
+ }
+#if SOL_IS_ON(SOL_LUA_NIL_IN_TABLES) && SOL_LUA_VERSION_I_ >= 600
+ lua_pop(L, vi);
+#else
+ lua_pop(L, (vi + 1));
+#endif
+ // see above comment
+ goto done;
+ }
+ }
+ if (isnil) {
+#if SOL_IS_ON(SOL_LUA_NIL_IN_TABLES) && SOL_LUA_VERSION_I_ >= 600
+#else
+ lua_pop(L, lua_size<V>::value);
+#endif
+ continue;
+ }
+
+ push_back_at_end(meta::has_push_back<Tu>(), t, L, cont, idx);
+ ++idx;
+ lua_pop(L, lua_size<V>::value);
+ }
+#else
+ // Zzzz slower but necessary thanks to the lower version API and missing functions qq
+ for (lua_Integer i = 0;; i += lua_size<V>::value, lua_pop(L, lua_size<V>::value)) {
+ if (idx >= cont.max_size()) {
+ // see above comment
+ goto done;
+ }
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 2, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ bool isnil = false;
+ for (int vi = 0; vi < lua_size<V>::value; ++vi) {
+ lua_pushinteger(L, i);
+ lua_gettable(L, index);
+ type vt = type_of(L, -1);
+ isnil = vt == type::lua_nil;
+ if (isnil) {
+ if (i == 0) {
+ break;
+ }
+ lua_pop(L, (vi + 1));
+ // see above comment
+ goto done;
+ }
+ }
+ if (isnil)
+ continue;
+ push_back_at_end(meta::has_push_back<Tu>(), t, L, cont, idx);
+ ++idx;
+ }
+#endif
+ done:
+ return cont;
+ }
+
+ static T get(std::true_type, lua_State* L, int index, record& tracking) {
+ typedef typename Tu::value_type P;
+ typedef typename P::first_type K;
+ typedef typename P::second_type V;
+ return get(types<K, V>(), L, index, tracking);
+ }
+
+ template <typename K, typename V>
+ static T get(types<K, V>, lua_State* L, int relindex, record& tracking) {
+ tracking.use(1);
+
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 3, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+
+ T associative;
+ int index = lua_absindex(L, relindex);
+ lua_pushnil(L);
+ while (lua_next(L, index) != 0) {
+ decltype(auto) key = stack::check_get<K>(L, -2);
+ if (!key) {
+ lua_pop(L, 1);
+ continue;
+ }
+ associative.emplace(std::forward<decltype(*key)>(*key), stack::get<V>(L, -1));
+ lua_pop(L, 1);
+ }
+ return associative;
+ }
+ };
+
+ template <typename T, typename Al>
+ struct unqualified_getter<as_table_t<std::forward_list<T, Al>>> {
+ typedef std::forward_list<T, Al> C;
+
+ static C get(lua_State* L, int relindex, record& tracking) {
+ return get(meta::has_key_value_pair<C>(), L, relindex, tracking);
+ }
+
+ static C get(std::true_type, lua_State* L, int index, record& tracking) {
+ typedef typename T::value_type P;
+ typedef typename P::first_type K;
+ typedef typename P::second_type V;
+ return get(types<K, V>(), L, index, tracking);
+ }
+
+ static C get(std::false_type, lua_State* L, int relindex, record& tracking) {
+ typedef typename C::value_type V;
+ return get(types<V>(), L, relindex, tracking);
+ }
+
+ template <typename V>
+ static C get(types<V>, lua_State* L, int relindex, record& tracking) {
+ tracking.use(1);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 3, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+
+ int index = lua_absindex(L, relindex);
+ C cont;
+ auto at = cont.cbefore_begin();
+ std::size_t idx = 0;
+#if SOL_LUA_VERSION_I_ >= 503
+ // This method is HIGHLY performant over regular table iteration thanks to the Lua API changes in 5.3
+ for (lua_Integer i = 0;; i += lua_size<V>::value, lua_pop(L, lua_size<V>::value)) {
+ if (idx >= cont.max_size()) {
+ goto done;
+ }
+ bool isnil = false;
+ for (int vi = 0; vi < lua_size<V>::value; ++vi) {
+ type t = static_cast<type>(lua_geti(L, index, i + vi));
+ isnil = t == type::lua_nil;
+ if (isnil) {
+ if (i == 0) {
+ break;
+ }
+ lua_pop(L, (vi + 1));
+ goto done;
+ }
+ }
+ if (isnil)
+ continue;
+ at = cont.insert_after(at, stack::get<V>(L, -lua_size<V>::value));
+ ++idx;
+ }
+#else
+ // Zzzz slower but necessary thanks to the lower version API and missing functions qq
+ for (lua_Integer i = 0;; i += lua_size<V>::value, lua_pop(L, lua_size<V>::value)) {
+ if (idx >= cont.max_size()) {
+ goto done;
+ }
+ bool isnil = false;
+ for (int vi = 0; vi < lua_size<V>::value; ++vi) {
+ lua_pushinteger(L, i);
+ lua_gettable(L, index);
+ type t = type_of(L, -1);
+ isnil = t == type::lua_nil;
+ if (isnil) {
+ if (i == 0) {
+ break;
+ }
+ lua_pop(L, (vi + 1));
+ goto done;
+ }
+ }
+ if (isnil)
+ continue;
+ at = cont.insert_after(at, stack::get<V>(L, -lua_size<V>::value));
+ ++idx;
+ }
+#endif
+ done:
+ return cont;
+ }
+
+ template <typename K, typename V>
+ static C get(types<K, V>, lua_State* L, int relindex, record& tracking) {
+ tracking.use(1);
+
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 3, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+
+ C associative;
+ auto at = associative.cbefore_begin();
+ int index = lua_absindex(L, relindex);
+ lua_pushnil(L);
+ while (lua_next(L, index) != 0) {
+ decltype(auto) key = stack::check_get<K>(L, -2);
+ if (!key) {
+ lua_pop(L, 1);
+ continue;
+ }
+ at = associative.emplace_after(at, std::forward<decltype(*key)>(*key), stack::get<V>(L, -1));
+ lua_pop(L, 1);
+ }
+ return associative;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<nested<T>> {
+ static T get(lua_State* L, int index, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (is_container_v<Tu>) {
+ if constexpr (meta::is_associative<Tu>::value) {
+ typedef typename Tu::value_type P;
+ typedef typename P::first_type K;
+ typedef typename P::second_type V;
+ unqualified_getter<as_table_t<T>> g {};
+ return g.get(types<K, nested<V>>(), L, index, tracking);
+ }
+ else {
+ typedef typename Tu::value_type V;
+ unqualified_getter<as_table_t<T>> g {};
+ return g.get(types<nested<V>>(), L, index, tracking);
+ }
+ }
+ else {
+ unqualified_getter<Tu> g {};
+ return g.get(L, index, tracking);
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<as_container_t<T>> {
+ static decltype(auto) get(lua_State* L, int index, record& tracking) {
+ return stack::unqualified_get<T>(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<as_container_t<T>*> {
+ static decltype(auto) get(lua_State* L, int index, record& tracking) {
+ return stack::unqualified_get<T*>(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<exhaustive<T>> {
+ static decltype(auto) get(lua_State* arg_L, int index, record& tracking) {
+ return stack::get<T>(arg_L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<non_exhaustive<T>> {
+ static decltype(auto) get(lua_State* arg_L, int index, record& tracking) {
+ return stack::get<T>(arg_L, index, tracking);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<userdata_value> {
+ static userdata_value get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return userdata_value(lua_touserdata(L, index));
+ }
+ };
+
+ template <>
+ struct unqualified_getter<lightuserdata_value> {
+ static lightuserdata_value get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return lightuserdata_value(lua_touserdata(L, index));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<light<T>> {
+ static light<T> get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ return light<T>(static_cast<T*>(memory));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<user<T>> {
+ static std::add_lvalue_reference_t<T> get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ memory = detail::align_user<T>(memory);
+ return *static_cast<std::remove_reference_t<T>*>(memory);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<user<T*>> {
+ static T* get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ memory = detail::align_user<T*>(memory);
+ return static_cast<T*>(memory);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<type> {
+ static type get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return static_cast<type>(lua_type(L, index));
+ }
+ };
+
+ template <>
+ struct unqualified_getter<std::string> {
+ static std::string get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ std::size_t len;
+ auto str = lua_tolstring(L, index, &len);
+ return std::string(str, len);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<const char*> {
+ static const char* get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ size_t sz;
+ return lua_tolstring(L, index, &sz);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<char> {
+ static char get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ size_t len;
+ auto str = lua_tolstring(L, index, &len);
+ return len > 0 ? str[0] : '\0';
+ }
+ };
+
+ template <typename Traits>
+ struct unqualified_getter<basic_string_view<char, Traits>> {
+ static string_view get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ size_t sz;
+ const char* str = lua_tolstring(L, index, &sz);
+ return basic_string_view<char, Traits>(str, sz);
+ }
+ };
+
+ template <typename Traits, typename Al>
+ struct unqualified_getter<std::basic_string<wchar_t, Traits, Al>> {
+ using S = std::basic_string<wchar_t, Traits, Al>;
+ static S get(lua_State* L, int index, record& tracking) {
+ using Ch = meta::conditional_t<sizeof(wchar_t) == 2, char16_t, char32_t>;
+ return stack_detail::get_into<Ch, S>(L, index, tracking);
+ }
+ };
+
+ template <typename Traits, typename Al>
+ struct unqualified_getter<std::basic_string<char16_t, Traits, Al>> {
+ static std::basic_string<char16_t, Traits, Al> get(lua_State* L, int index, record& tracking) {
+ return stack_detail::get_into<char16_t, std::basic_string<char16_t, Traits, Al>>(L, index, tracking);
+ }
+ };
+
+ template <typename Traits, typename Al>
+ struct unqualified_getter<std::basic_string<char32_t, Traits, Al>> {
+ static std::basic_string<char32_t, Traits, Al> get(lua_State* L, int index, record& tracking) {
+ return stack_detail::get_into<char32_t, std::basic_string<char32_t, Traits, Al>>(L, index, tracking);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<char16_t> {
+ static char16_t get(lua_State* L, int index, record& tracking) {
+ string_view utf8 = stack::get<string_view>(L, index, tracking);
+ const char* strb = utf8.data();
+ const char* stre = utf8.data() + utf8.size();
+ char32_t cp = 0;
+ auto dr = unicode::utf8_to_code_point(strb, stre);
+ if (dr.error != unicode::error_code::ok) {
+ cp = unicode::unicode_detail::replacement;
+ }
+ else {
+ cp = dr.codepoint;
+ }
+ auto er = unicode::code_point_to_utf16(cp);
+ return er.code_units[0];
+ }
+ };
+
+ template <>
+ struct unqualified_getter<char32_t> {
+ static char32_t get(lua_State* L, int index, record& tracking) {
+ string_view utf8 = stack::get<string_view>(L, index, tracking);
+ const char* strb = utf8.data();
+ const char* stre = utf8.data() + utf8.size();
+ char32_t cp = 0;
+ auto dr = unicode::utf8_to_code_point(strb, stre);
+ if (dr.error != unicode::error_code::ok) {
+ cp = unicode::unicode_detail::replacement;
+ }
+ else {
+ cp = dr.codepoint;
+ }
+ auto er = unicode::code_point_to_utf32(cp);
+ return er.code_units[0];
+ }
+ };
+
+ template <>
+ struct unqualified_getter<wchar_t> {
+ static wchar_t get(lua_State* L, int index, record& tracking) {
+ typedef meta::conditional_t<sizeof(wchar_t) == 2, char16_t, char32_t> Ch;
+ unqualified_getter<Ch> g;
+ (void)g;
+ auto c = g.get(L, index, tracking);
+ return static_cast<wchar_t>(c);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<meta_function> {
+ static meta_function get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ const char* name = unqualified_getter<const char*> {}.get(L, index, tracking);
+ const auto& mfnames = meta_function_names();
+ for (std::size_t i = 0; i < mfnames.size(); ++i)
+ if (mfnames[i] == name)
+ return static_cast<meta_function>(i);
+ return meta_function::construct;
+ }
+ };
+
+ template <>
+ struct unqualified_getter<lua_nil_t> {
+ static lua_nil_t get(lua_State*, int, record& tracking) {
+ tracking.use(1);
+ return lua_nil;
+ }
+ };
+
+ template <>
+ struct unqualified_getter<std::nullptr_t> {
+ static std::nullptr_t get(lua_State*, int, record& tracking) {
+ tracking.use(1);
+ return nullptr;
+ }
+ };
+
+ template <>
+ struct unqualified_getter<nullopt_t> {
+ static nullopt_t get(lua_State*, int, record& tracking) {
+ tracking.use(1);
+ return nullopt;
+ }
+ };
+
+ template <>
+ struct unqualified_getter<this_state> {
+ static this_state get(lua_State* L, int, record& tracking) {
+ tracking.use(0);
+ return this_state(L);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<this_main_state> {
+ static this_main_state get(lua_State* L, int, record& tracking) {
+ tracking.use(0);
+ return this_main_state(main_thread(L, L));
+ }
+ };
+
+ template <>
+ struct unqualified_getter<lua_CFunction> {
+ static lua_CFunction get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return lua_tocfunction(L, index);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<c_closure> {
+ static c_closure get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return c_closure(lua_tocfunction(L, index), -1);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<error> {
+ static error get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ size_t sz = 0;
+ const char* err = lua_tolstring(L, index, &sz);
+ if (err == nullptr) {
+ return error(detail::direct_error, "");
+ }
+ return error(detail::direct_error, std::string(err, sz));
+ }
+ };
+
+ template <>
+ struct unqualified_getter<void*> {
+ static void* get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return lua_touserdata(L, index);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<const void*> {
+ static const void* get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return lua_touserdata(L, index);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<detail::as_value_tag<T>> {
+ static T* get_no_lua_nil(lua_State* L, int index, record& tracking) {
+ void* memory = lua_touserdata(L, index);
+#if SOL_IS_ON(SOL_USE_INTEROP)
+ auto ugr = stack_detail::interop_get<T>(L, index, memory, tracking);
+ if (ugr.first) {
+ return ugr.second;
+ }
+#endif // interop extensibility
+ tracking.use(1);
+ void* rawdata = detail::align_usertype_pointer(memory);
+ void** pudata = static_cast<void**>(rawdata);
+ void* udata = *pudata;
+ return get_no_lua_nil_from(L, udata, index, tracking);
+ }
+
+ static T* get_no_lua_nil_from(lua_State* L, void* udata, int index, record&) {
+ bool has_derived = derive<T>::value || weak_derive<T>::value;
+ if (has_derived) {
+ if (lua_getmetatable(L, index) == 1) {
+ lua_getfield(L, -1, &detail::base_class_cast_key()[0]);
+ if (type_of(L, -1) != type::lua_nil) {
+ void* basecastdata = lua_touserdata(L, -1);
+ detail::inheritance_cast_function ic = reinterpret_cast<detail::inheritance_cast_function>(basecastdata);
+ // use the casting function to properly adjust the pointer for the desired T
+ udata = ic(udata, usertype_traits<T>::qualified_name());
+ }
+ lua_pop(L, 2);
+ }
+ }
+ if constexpr (std::is_function_v<T>) {
+ T* func = reinterpret_cast<T*>(udata);
+ return func;
+ }
+ else {
+ T* obj = static_cast<T*>(udata);
+ return obj;
+ }
+ }
+
+ static T& get(lua_State* L, int index, record& tracking) {
+ return *get_no_lua_nil(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<detail::as_pointer_tag<T>> {
+ static T* get(lua_State* L, int index, record& tracking) {
+ type t = type_of(L, index);
+ if (t == type::lua_nil) {
+ tracking.use(1);
+ return nullptr;
+ }
+ unqualified_getter<detail::as_value_tag<T>> g{};
+ return g.get_no_lua_nil(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<non_null<T*>> {
+ static T* get(lua_State* L, int index, record& tracking) {
+ unqualified_getter<detail::as_value_tag<T>> g{};
+ return g.get_no_lua_nil(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<T&> {
+ static T& get(lua_State* L, int index, record& tracking) {
+ unqualified_getter<detail::as_value_tag<T>> g{};
+ return g.get(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<std::reference_wrapper<T>> {
+ static T& get(lua_State* L, int index, record& tracking) {
+ unqualified_getter<T&> g{};
+ return g.get(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<T*> {
+ static T* get(lua_State* L, int index, record& tracking) {
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ if constexpr (std::is_function_v<T>) {
+ return stack_detail::get_function_pointer<T>(L, index, tracking);
+ }
+ else {
+ unqualified_getter<detail::as_pointer_tag<T>> g{};
+ return g.get(L, index, tracking);
+ }
+#else
+ unqualified_getter<detail::as_pointer_tag<T>> g{};
+ return g.get(L, index, tracking);
+#endif
+ }
+ };
+
+ template <typename... Tn>
+ struct unqualified_getter<std::tuple<Tn...>> {
+ typedef std::tuple<decltype(stack::get<Tn>(nullptr, 0))...> R;
+
+ template <typename... Args>
+ static R apply(std::index_sequence<>, lua_State*, int, record&, Args&&... args) {
+ // Fuck you too, VC++
+ return R { std::forward<Args>(args)... };
+ }
+
+ template <std::size_t I, std::size_t... Ix, typename... Args>
+ static R apply(std::index_sequence<I, Ix...>, lua_State* L, int index, record& tracking, Args&&... args) {
+ // Fuck you too, VC++
+ typedef std::tuple_element_t<I, std::tuple<Tn...>> T;
+ return apply(std::index_sequence<Ix...>(), L, index, tracking, std::forward<Args>(args)..., stack::get<T>(L, index + tracking.used, tracking));
+ }
+
+ static R get(lua_State* L, int index, record& tracking) {
+ return apply(std::make_index_sequence<sizeof...(Tn)>(), L, index, tracking);
+ }
+ };
+
+ template <typename A, typename B>
+ struct unqualified_getter<std::pair<A, B>> {
+ static decltype(auto) get(lua_State* L, int index, record& tracking) {
+ return std::pair<decltype(stack::get<A>(L, index)), decltype(stack::get<B>(L, index))> { stack::get<A>(L, index, tracking),
+ stack::get<B>(L, index + tracking.used, tracking) };
+ }
+ };
+
+#if SOL_IS_ON(SOL_STD_VARIANT)
+
+ template <typename... Tn>
+ struct unqualified_getter<std::variant<Tn...>> {
+ using V = std::variant<Tn...>;
+
+ static V get_one(std::integral_constant<std::size_t, std::variant_size_v<V>>, lua_State* L, int index, record& tracking) {
+ (void)L;
+ (void)index;
+ (void)tracking;
+ if constexpr (std::variant_size_v<V> == 0) {
+ return V();
+ }
+ else {
+ // using T = std::variant_alternative_t<0, V>;
+ std::abort();
+ // return V(std::in_place_index<0>, stack::get<T>(L, index, tracking));
+ }
+ }
+
+ template <std::size_t I>
+ static V get_one(std::integral_constant<std::size_t, I>, lua_State* L, int index, record& tracking) {
+ typedef std::variant_alternative_t<I, V> T;
+ record temp_tracking = tracking;
+ if (stack::check<T>(L, index, &no_panic, temp_tracking)) {
+ tracking = temp_tracking;
+ return V(std::in_place_index<I>, stack::get<T>(L, index));
+ }
+ return get_one(std::integral_constant<std::size_t, I + 1>(), L, index, tracking);
+ }
+
+ static V get(lua_State* L, int index, record& tracking) {
+ return get_one(std::integral_constant<std::size_t, 0>(), L, index, tracking);
+ }
+ };
+#endif // variant
+
+}} // namespace sol::stack
+
+// end of sol/stack_get_unqualified.hpp
+
+// beginning of sol/stack_get_qualified.hpp
+
+namespace sol { namespace stack {
+
+ // There are no more enable_ifs that can be used here,
+ // so this is just for posterity, I guess?
+ // maybe I'll fill this file in later.
+
+}} // namespace sol::stack
+
+// end of sol/stack_get_qualified.hpp
+
+// end of sol/stack_get.hpp
+
+// beginning of sol/stack_check_get.hpp
+
+// beginning of sol/stack_check_get_unqualified.hpp
+
+#include <cstdlib>
+#include <cmath>
+#include <optional>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // variant shenanigans (thanks, Mac OSX)
+
+namespace sol { namespace stack {
+ template <typename T, typename>
+ struct unqualified_check_getter {
+ typedef decltype(stack_detail::unchecked_unqualified_get<T>(nullptr, -1, std::declval<record&>())) R;
+
+ template <typename Optional, typename Handler>
+ static Optional get_using(lua_State* L, int index, Handler&& handler, record& tracking) {
+ if constexpr (!meta::meta_detail::is_adl_sol_lua_check_v<T> && !meta::meta_detail::is_adl_sol_lua_get_v<T>) {
+ if constexpr (is_lua_reference_v<T>) {
+ if constexpr (is_global_table_v<T>) {
+ (void)L;
+ (void)index;
+ (void)handler;
+ tracking.use(1);
+ return true;
+ }
+ else {
+ // actually check if it's none here, otherwise
+ // we'll have a none object inside an optional!
+ bool success = lua_isnoneornil(L, index) == 0 && stack::check<T>(L, index, &no_panic);
+ if (!success) {
+ // expected type, actual type
+ tracking.use(static_cast<int>(success));
+ handler(L, index, type::poly, type_of(L, index), "");
+ return detail::associated_nullopt_v<Optional>;
+ }
+ return stack_detail::unchecked_get<T>(L, index, tracking);
+ }
+ }
+ else if constexpr ((std::is_integral_v<T> || std::is_same_v<T, lua_Integer>)&&!std::is_same_v<T, bool>) {
+#if SOL_LUA_VERSION_I_ >= 503
+ if (lua_isinteger(L, index) != 0) {
+ tracking.use(1);
+ return static_cast<T>(lua_tointeger(L, index));
+ }
+#endif
+ int isnum = 0;
+ const lua_Number value = lua_tonumberx(L, index, &isnum);
+ if (isnum != 0) {
+#if SOL_IS_ON(SOL_NUMBER_PRECISION_CHECKS)
+ const auto integer_value = llround(value);
+ if (static_cast<lua_Number>(integer_value) == value) {
+ tracking.use(1);
+ return static_cast<T>(integer_value);
+ }
+#else
+ tracking.use(1);
+ return static_cast<T>(value);
+#endif
+ }
+ const type t = type_of(L, index);
+ tracking.use(static_cast<int>(t != type::none));
+ handler(L, index, type::number, t, "not an integer");
+ return detail::associated_nullopt_v<Optional>;
+ }
+ else if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, lua_Number>) {
+ int isnum = 0;
+ lua_Number value = lua_tonumberx(L, index, &isnum);
+ if (isnum == 0) {
+ type t = type_of(L, index);
+ tracking.use(static_cast<int>(t != type::none));
+ handler(L, index, type::number, t, "not a valid floating point number");
+ return detail::associated_nullopt_v<Optional>;
+ }
+ tracking.use(1);
+ return static_cast<T>(value);
+ }
+ else if constexpr (std::is_enum_v<T> && !meta::any_same_v<T, meta_function, type>) {
+ int isnum = 0;
+ lua_Integer value = lua_tointegerx(L, index, &isnum);
+ if (isnum == 0) {
+ type t = type_of(L, index);
+ tracking.use(static_cast<int>(t != type::none));
+ handler(L, index, type::number, t, "not a valid enumeration value");
+ return detail::associated_nullopt_v<Optional>;
+ }
+ tracking.use(1);
+ return static_cast<T>(value);
+ }
+ else {
+ if (!unqualified_check<T>(L, index, std::forward<Handler>(handler))) {
+ tracking.use(static_cast<int>(!lua_isnone(L, index)));
+ return detail::associated_nullopt_v<Optional>;
+ }
+ return stack_detail::unchecked_unqualified_get<T>(L, index, tracking);
+ }
+ }
+ else {
+ if (!unqualified_check<T>(L, index, std::forward<Handler>(handler))) {
+ tracking.use(static_cast<int>(!lua_isnone(L, index)));
+ return detail::associated_nullopt_v<Optional>;
+ }
+ return stack_detail::unchecked_unqualified_get<T>(L, index, tracking);
+ }
+ }
+
+ template <typename Handler>
+ static optional<R> get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ return get_using<optional<R>>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ template <typename... Tn, typename C>
+ struct unqualified_check_getter<std::variant<Tn...>, C> {
+ typedef std::variant<Tn...> V;
+ typedef std::variant_size<V> V_size;
+ typedef std::integral_constant<bool, V_size::value == 0> V_is_empty;
+
+ template <typename Handler>
+ static optional<V> get_empty(std::true_type, lua_State*, int, Handler&&, record&) {
+ return nullopt;
+ }
+
+ template <typename Handler>
+ static optional<V> get_empty(std::false_type, lua_State* L, int index, Handler&& handler, record&) {
+ // This should never be reached...
+ // please check your code and understand what you did to bring yourself here
+ // maybe file a bug report, or 5
+ handler(
+ L, index, type::poly, type_of(L, index), "this variant code should never be reached: if it has, you have done something so terribly wrong");
+ return nullopt;
+ }
+
+ template <typename Handler>
+ static optional<V> get_one(std::integral_constant<std::size_t, 0>, lua_State* L, int index, Handler&& handler, record& tracking) {
+ return get_empty(V_is_empty(), L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <std::size_t I, typename Handler>
+ static optional<V> get_one(std::integral_constant<std::size_t, I>, lua_State* L, int index, Handler&& handler, record& tracking) {
+ typedef std::variant_alternative_t<I - 1, V> T;
+ if (stack::check<T>(L, index, &no_panic, tracking)) {
+ return V(std::in_place_index<I - 1>, stack::get<T>(L, index));
+ }
+ return get_one(std::integral_constant<std::size_t, I - 1>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename Handler>
+ static optional<V> get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ return get_one(std::integral_constant<std::size_t, V_size::value>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+#endif // standard variant
+}} // namespace sol::stack
+
+// end of sol/stack_check_get_unqualified.hpp
+
+// beginning of sol/stack_check_get_qualified.hpp
+
+namespace sol { namespace stack {
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic push
+#if !SOL_IS_ON(SOL_COMPILER_CLANG)
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+#endif
+
+ namespace stack_detail {
+ template <typename OptionalType, typename T, typename Handler>
+ OptionalType get_optional(lua_State* L, int index, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+
+ if constexpr (is_lua_reference_v<T>) {
+ if constexpr (is_global_table_v<Tu>) {
+ (void)L;
+ (void)index;
+ (void)handler;
+ tracking.use(1);
+ return true;
+ }
+ else {
+ // actually check if it's none here, otherwise
+ // we'll have a none object inside an optional!
+ bool success = lua_isnoneornil(L, index) == 0 && stack::check<T>(L, index, &no_panic);
+ if (!success) {
+ // expected type, actual type
+ tracking.use(static_cast<int>(success));
+ handler(L, index, type::poly, type_of(L, index), "");
+ return {};
+ }
+ return OptionalType(stack_detail::unchecked_get<T>(L, index, tracking));
+ }
+ }
+ else if constexpr (!std::is_reference_v<T> && is_unique_usertype_v<Tu> && !is_actual_type_rebindable_for_v<Tu>) {
+ // we can take shortcuts here to save on separate checking, and just return nullopt!
+ using element = unique_usertype_element_t<Tu>;
+ using actual = unique_usertype_actual_t<Tu>;
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ memory = detail::align_usertype_unique_destructor(memory);
+ detail::unique_destructor& pdx = *static_cast<detail::unique_destructor*>(memory);
+ if (&detail::usertype_unique_alloc_destroy<element, Tu> == pdx) {
+ memory = detail::align_usertype_unique_tag<true, false>(memory);
+ memory = detail::align_usertype_unique<actual, true, false>(memory);
+ actual* mem = static_cast<actual*>(memory);
+ return static_cast<actual>(*mem);
+ }
+ if constexpr (!derive<element>::value) {
+ return OptionalType();
+ }
+ else {
+ memory = detail::align_usertype_unique_tag<true, false>(memory);
+ detail::unique_tag& ic = *reinterpret_cast<detail::unique_tag*>(memory);
+ memory = detail::align_usertype_unique<actual, true, false>(memory);
+ string_view ti = usertype_traits<element>::qualified_name();
+ int cast_operation;
+ actual r {};
+ if constexpr (is_actual_type_rebindable_for_v<Tu>) {
+ using rebound_actual_type = unique_usertype_rebind_actual_t<Tu, void>;
+ string_view rebind_ti = usertype_traits<rebound_actual_type>::qualified_name();
+ cast_operation = ic(memory, &r, ti, rebind_ti);
+ }
+ else {
+ string_view rebind_ti("");
+ cast_operation = ic(memory, &r, ti, rebind_ti);
+ }
+ switch (cast_operation) {
+ case 1: {
+ // it's a perfect match,
+ // alias memory directly
+ actual* mem = static_cast<actual*>(memory);
+ return OptionalType(*mem);
+ }
+ case 2:
+ // it's a base match, return the
+ // aliased creation
+ return OptionalType(std::move(r));
+ default:
+ break;
+ }
+ return OptionalType();
+ }
+ }
+ else {
+ if (!check<T>(L, index, std::forward<Handler>(handler))) {
+ tracking.use(static_cast<int>(!lua_isnone(L, index)));
+ return OptionalType();
+ }
+ return OptionalType(stack_detail::unchecked_get<T>(L, index, tracking));
+ }
+ }
+ } // namespace stack_detail
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic pop
+#endif
+
+ template <typename T, typename>
+ struct qualified_check_getter {
+ typedef decltype(stack_detail::unchecked_get<T>(nullptr, -1, std::declval<record&>())) R;
+
+ template <typename Handler>
+ optional<R> get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ return stack_detail::get_optional<optional<R>, T>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+ template <typename Optional>
+ struct qualified_getter<Optional, std::enable_if_t<meta::is_optional_v<Optional>>> {
+ static Optional get(lua_State* L, int index, record& tracking) {
+ using T = typename meta::unqualified_t<Optional>::value_type;
+ return stack_detail::get_optional<Optional, T>(L, index, &no_panic, tracking);
+ }
+ };
+
+}} // namespace sol::stack
+
+// end of sol/stack_check_get_qualified.hpp
+
+// end of sol/stack_check_get.hpp
+
+// beginning of sol/stack_push.hpp
+
+#include <memory>
+#include <type_traits>
+#include <cassert>
+#include <limits>
+#include <cmath>
+#include <string_view>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // Can use variant
+
+// beginning of sol/debug.hpp
+
+#include <iostream>
+
+namespace sol { namespace detail { namespace debug {
+ inline std::string dump_types(lua_State* L) {
+ std::string visual;
+ std::size_t size = lua_gettop(L) + 1;
+ for (std::size_t i = 1; i < size; ++i) {
+ if (i != 1) {
+ visual += " | ";
+ }
+ visual += type_name(L, stack::get<type>(L, static_cast<int>(i)));
+ }
+ return visual;
+ }
+
+ inline void print_stack(lua_State* L) {
+ std::cout << dump_types(L) << std::endl;
+ }
+
+ inline void print_section(const std::string& message, lua_State* L) {
+ std::cout << "-- " << message << " -- [ " << dump_types(L) << " ]" << std::endl;
+ }
+}}} // namespace sol::detail::debug
+
+// end of sol/debug.hpp
+
+namespace sol { namespace stack {
+ namespace stack_detail {
+ template <typename T>
+ inline bool integer_value_fits(const T& value) {
+ // We check if we can rely on casts or a lack of padding bits to satisfy
+ // the requirements here
+ // If it lacks padding bits, we can jump back and forth between lua_Integer and whatever type without
+ // loss of information
+ constexpr bool is_same_signedness
+ = (std::is_signed_v<T> && std::is_signed_v<lua_Integer>) || (std::is_unsigned_v<T> && std::is_unsigned_v<lua_Integer>);
+ constexpr bool probaby_fits_within_lua_Integer = sizeof(T) == sizeof(lua_Integer)
+#if SOL_IS_ON(SOL_ALL_INTEGER_VALUES_FIT)
+ && ((std::has_unique_object_representations_v<T> && std::has_unique_object_representations_v<lua_Integer>) ? true : is_same_signedness)
+#else
+ && is_same_signedness
+#endif
+ ;
+ if constexpr (sizeof(T) < sizeof(lua_Integer) || probaby_fits_within_lua_Integer) {
+ (void)value;
+ return true;
+ }
+ else {
+ auto u_min = static_cast<std::intmax_t>((std::numeric_limits<lua_Integer>::min)());
+ auto u_max = static_cast<std::uintmax_t>((std::numeric_limits<lua_Integer>::max)());
+ auto t_min = static_cast<std::intmax_t>((std::numeric_limits<T>::min)());
+ auto t_max = static_cast<std::uintmax_t>((std::numeric_limits<T>::max)());
+ return (u_min <= t_min || value >= static_cast<T>(u_min)) && (u_max >= t_max || value <= static_cast<T>(u_max));
+ }
+ }
+
+ template <typename T>
+ int msvc_is_ass_with_if_constexpr_push_enum(std::true_type, lua_State* L, const T& value) {
+ if constexpr (meta::any_same_v<std::underlying_type_t<T>,
+ char
+#if SOL_IS_ON(SOL_CHAR8_T)
+ ,
+ char8_t
+#endif
+ ,
+ char16_t,
+ char32_t>) {
+ if constexpr (std::is_signed_v<T>) {
+ return stack::push(L, static_cast<std::int_least32_t>(value));
+ }
+ else {
+ return stack::push(L, static_cast<std::uint_least32_t>(value));
+ }
+ }
+ else {
+ return stack::push(L, static_cast<std::underlying_type_t<T>>(value));
+ }
+ }
+
+ template <typename T>
+ int msvc_is_ass_with_if_constexpr_push_enum(std::false_type, lua_State*, const T&) {
+ return 0;
+ }
+ } // namespace stack_detail
+
+ inline int push_environment_of(lua_State* L, int target_index = -1) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_environment);
+#endif // make sure stack doesn't overflow
+#if SOL_LUA_VERSION_I_ < 502
+ // Use lua_getfenv
+ lua_getfenv(L, target_index);
+#else
+
+ if (lua_iscfunction(L, target_index) != 0) {
+ const char* maybe_upvalue_name = lua_getupvalue(L, target_index, 1);
+ if (maybe_upvalue_name != nullptr) {
+ // it worked, take this one
+ return 1;
+ }
+ }
+ // Nominally, we search for the `"_ENV"` value.
+ // If we don't find it.... uh, well. We've got a problem?
+ for (int upvalue_index = 1;; ++upvalue_index) {
+ const char* maybe_upvalue_name = lua_getupvalue(L, target_index, upvalue_index);
+ if (maybe_upvalue_name == nullptr) {
+ push(L, lua_nil);
+ break;
+ }
+
+ string_view upvalue_name(maybe_upvalue_name);
+ if (upvalue_name == "_ENV") {
+ // Keep this one!
+ break;
+ }
+ // Discard what we received, loop back around
+ lua_pop(L, 1);
+ }
+#endif
+ return 1;
+ }
+
+ template <typename T>
+ int push_environment_of(const T& target) {
+ lua_State* target_L = target.lua_state();
+ int target_index = absolute_index(target_L, -target.push());
+ int env_count = push_environment_of(target_L, target_index);
+ SOL_ASSERT(env_count == 1);
+ lua_rotate(target_L, target_index, 1);
+ lua_pop(target_L, 1);
+ return env_count;
+ }
+
+ template <typename T>
+ struct unqualified_pusher<detail::as_value_tag<T>> {
+ template <typename F, typename... Args>
+ static int push_fx(lua_State* L, F&& f, Args&&... args) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_userdata);
+#endif // make sure stack doesn't overflow
+ // Basically, we store all user-data like this:
+ // If it's a movable/copyable value (no std::ref(x)), then we store the pointer to the new
+ // data in the first sizeof(T*) bytes, and then however many bytes it takes to
+ // do the actual object. Things that are std::ref or plain T* are stored as
+ // just the sizeof(T*), and nothing else.
+ T* obj = detail::usertype_allocate<T>(L);
+ f();
+ std::allocator<T> alloc {};
+ std::allocator_traits<std::allocator<T>>::construct(alloc, obj, std::forward<Args>(args)...);
+ return 1;
+ }
+
+ template <typename K, typename... Args>
+ static int push_keyed(lua_State* L, K&& k, Args&&... args) {
+ stack_detail::undefined_metatable fx(L, &k[0], &stack::stack_detail::set_undefined_methods_on<T>);
+ return push_fx(L, fx, std::forward<Args>(args)...);
+ }
+
+ template <typename Arg, typename... Args>
+ static int push(lua_State* L, Arg&& arg, Args&&... args) {
+ if constexpr (std::is_same_v<meta::unqualified_t<Arg>, detail::with_function_tag>) {
+ (void)arg;
+ return push_fx(L, std::forward<Args>(args)...);
+ }
+ else {
+ return push_keyed(L, usertype_traits<T>::metatable(), std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ }
+
+ static int push(lua_State* L) {
+ return push_keyed(L, usertype_traits<T>::metatable());
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<detail::as_pointer_tag<T>> {
+ typedef meta::unqualified_t<T> U;
+
+ template <typename F>
+ static int push_fx(lua_State* L, F&& f, T* obj) {
+ if (obj == nullptr)
+ return stack::push(L, lua_nil);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_userdata);
+#endif // make sure stack doesn't overflow
+ T** pref = detail::usertype_allocate_pointer<T>(L);
+ f();
+ *pref = obj;
+ return 1;
+ }
+
+ template <typename K>
+ static int push_keyed(lua_State* L, K&& k, T* obj) {
+ stack_detail::undefined_metatable fx(L, &k[0], &stack::stack_detail::set_undefined_methods_on<U*>);
+ return push_fx(L, fx, obj);
+ }
+
+ template <typename Arg, typename... Args>
+ static int push(lua_State* L, Arg&& arg, Args&&... args) {
+ if constexpr (std::is_same_v<meta::unqualified_t<Arg>, detail::with_function_tag>) {
+ (void)arg;
+ return push_fx(L, std::forward<Args>(args)...);
+ }
+ else {
+ return push_keyed(L, usertype_traits<U*>::metatable(), std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<detail::as_reference_tag> {
+ template <typename T>
+ static int push(lua_State* L, T&& obj) {
+ return stack::push(L, detail::ptr(obj));
+ }
+ };
+
+ namespace stack_detail {
+ template <typename T>
+ struct uu_pusher {
+ using element = unique_usertype_element_t<T>;
+ using actual = unique_usertype_actual_t<T>;
+
+ template <typename Arg, typename... Args>
+ static int push(lua_State* L, Arg&& arg, Args&&... args) {
+ if constexpr (std::is_base_of_v<actual, meta::unqualified_t<Arg>>) {
+ if (detail::unique_is_null(L, arg)) {
+ return stack::push(L, lua_nil);
+ }
+ return push_deep(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ else {
+ return push_deep(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ static int push_deep(lua_State* L, Args&&... args) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_userdata);
+#endif // make sure stack doesn't overflow
+ element** pointer_to_memory = nullptr;
+ detail::unique_destructor* fx = nullptr;
+ detail::unique_tag* id = nullptr;
+ actual* typed_memory = detail::usertype_unique_allocate<element, actual>(L, pointer_to_memory, fx, id);
+ if (luaL_newmetatable(L, &usertype_traits<d::u<std::remove_cv_t<element>>>::metatable()[0]) == 1) {
+ detail::lua_reg_table registration_table {};
+ int index = 0;
+ detail::indexed_insert insert_callable(registration_table, index);
+ detail::insert_default_registrations<element>(insert_callable, detail::property_always_true);
+ registration_table[index] = { to_string(meta_function::garbage_collect).c_str(), detail::make_destructor<T>() };
+ luaL_setfuncs(L, registration_table, 0);
+ }
+ lua_setmetatable(L, -2);
+ *fx = detail::usertype_unique_alloc_destroy<element, actual>;
+ *id = &detail::inheritance<element>::template type_unique_cast<actual>;
+ detail::default_construct::construct(typed_memory, std::forward<Args>(args)...);
+ *pointer_to_memory = detail::unique_get<T>(L, *typed_memory);
+ return 1;
+ }
+ };
+ } // namespace stack_detail
+
+ template <typename T>
+ struct unqualified_pusher<detail::as_unique_tag<T>> {
+ template <typename... Args>
+ static int push(lua_State* L, Args&&... args) {
+ stack_detail::uu_pusher<T> p;
+ (void)p;
+ return p.push(L, std::forward<Args>(args)...);
+ }
+ };
+
+ template <typename T, typename>
+ struct unqualified_pusher {
+ template <typename... Args>
+ static int push(lua_State* L, Args&&... args) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (is_lua_reference_v<Tu>) {
+ using int_arr = int[];
+ int_arr p { (std::forward<Args>(args).push(L))... };
+ return p[0];
+ }
+ else if constexpr (std::is_same_v<Tu, bool>) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushboolean(L, std::forward<Args>(args)...);
+ return 1;
+ }
+ else if constexpr (std::is_integral_v<Tu> || std::is_same_v<Tu, lua_Integer>) {
+ const Tu& value(std::forward<Args>(args)...);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_integral);
+#endif // make sure stack doesn't overflow
+#if SOL_LUA_VERSION_I_ >= 503
+ if (stack_detail::integer_value_fits<Tu>(value)) {
+ lua_pushinteger(L, static_cast<lua_Integer>(value));
+ return 1;
+ }
+#endif // Lua 5.3 and above
+#if SOL_IS_ON(SOL_NUMBER_PRECISION_CHECKS)
+ if (static_cast<T>(llround(static_cast<lua_Number>(value))) != value) {
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ // Is this really worth it?
+ SOL_ASSERT_MSG(false, "integer value will be misrepresented in lua");
+ lua_pushinteger(L, static_cast<lua_Integer>(value));
+ return 1;
+#else
+ throw error(detail::direct_error, "integer value will be misrepresented in lua");
+#endif // No Exceptions
+ }
+#endif // Safe Numerics and Number Precision Check
+ lua_pushnumber(L, static_cast<lua_Number>(value));
+ return 1;
+ }
+ else if constexpr (std::is_floating_point_v<Tu> || std::is_same_v<Tu, lua_Number>) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_floating);
+#endif // make sure stack doesn't overflow
+ lua_pushnumber(L, std::forward<Args>(args)...);
+ return 1;
+ }
+ else if constexpr (std::is_same_v<Tu, luaL_Stream*>) {
+ luaL_Stream* source { std::forward<Args>(args)... };
+ luaL_Stream* stream = static_cast<luaL_Stream*>(detail::alloc_newuserdata(L, sizeof(luaL_Stream)));
+ stream->f = source->f;
+#if SOL_IS_ON(SOL_LUAL_STREAM_USE_CLOSE_FUNCTION)
+ stream->closef = source->closef;
+#endif // LuaJIT and Lua 5.1 and below do not have
+ return 1;
+ }
+ else if constexpr (std::is_same_v<Tu, luaL_Stream>) {
+ luaL_Stream& source(std::forward<Args>(args)...);
+ luaL_Stream* stream = static_cast<luaL_Stream*>(detail::alloc_newuserdata(L, sizeof(luaL_Stream)));
+ stream->f = source.f;
+#if SOL_IS_ON(SOL_LUAL_STREAM_USE_CLOSE_FUNCTION)
+ stream->closef = source.closef;
+#endif // LuaJIT and Lua 5.1 and below do not have
+ return 1;
+ }
+ else if constexpr (std::is_enum_v<Tu>) {
+ return stack_detail::msvc_is_ass_with_if_constexpr_push_enum(std::true_type(), L, std::forward<Args>(args)...);
+ }
+ else if constexpr (std::is_pointer_v<Tu>) {
+ return stack::push<detail::as_pointer_tag<std::remove_pointer_t<T>>>(L, std::forward<Args>(args)...);
+ }
+ else if constexpr (is_unique_usertype_v<Tu>) {
+ return stack::push<detail::as_unique_tag<T>>(L, std::forward<Args>(args)...);
+ }
+ else {
+ return stack::push<detail::as_value_tag<T>>(L, std::forward<Args>(args)...);
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<std::reference_wrapper<T>> {
+ static int push(lua_State* L, const std::reference_wrapper<T>& t) {
+ return stack::push(L, std::addressof(detail::deref(t.get())));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<detail::as_table_tag<T>> {
+ using has_kvp = meta::has_key_value_pair<meta::unqualified_t<std::remove_pointer_t<T>>>;
+
+ static int push(lua_State* L, const T& tablecont) {
+ return push(has_kvp(), std::false_type(), L, tablecont);
+ }
+
+ static int push(lua_State* L, const T& tablecont, nested_tag_t) {
+ return push(has_kvp(), std::true_type(), L, tablecont);
+ }
+
+ static int push(std::true_type, lua_State* L, const T& tablecont) {
+ return push(has_kvp(), std::true_type(), L, tablecont);
+ }
+
+ static int push(std::false_type, lua_State* L, const T& tablecont) {
+ return push(has_kvp(), std::false_type(), L, tablecont);
+ }
+
+ template <bool is_nested>
+ static int push(std::true_type, std::integral_constant<bool, is_nested>, lua_State* L, const T& tablecont) {
+ auto& cont = detail::deref(detail::unwrap(tablecont));
+ lua_createtable(L, static_cast<int>(cont.size()), 0);
+ int tableindex = lua_gettop(L);
+ for (const auto& pair : cont) {
+ if (is_nested) {
+ set_field(L, pair.first, as_nested_ref(pair.second), tableindex);
+ }
+ else {
+ set_field(L, pair.first, pair.second, tableindex);
+ }
+ }
+ return 1;
+ }
+
+ template <bool is_nested>
+ static int push(std::false_type, std::integral_constant<bool, is_nested>, lua_State* L, const T& tablecont) {
+ auto& cont = detail::deref(detail::unwrap(tablecont));
+ lua_createtable(L, stack_detail::get_size_hint(cont), 0);
+ int tableindex = lua_gettop(L);
+ std::size_t index = 1;
+ for (const auto& i : cont) {
+#if SOL_LUA_VERSION_I_ >= 503
+ int p = is_nested ? stack::push(L, as_nested_ref(i)) : stack::push(L, i);
+ for (int pi = 0; pi < p; ++pi) {
+ lua_seti(L, tableindex, static_cast<lua_Integer>(index++));
+ }
+#else
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushinteger(L, static_cast<lua_Integer>(index));
+ int p = is_nested ? stack::push(L, as_nested_ref(i)) : stack::push(L, i);
+ if (p == 1) {
+ ++index;
+ lua_settable(L, tableindex);
+ }
+ else {
+ int firstindex = tableindex + 1 + 1;
+ for (int pi = 0; pi < p; ++pi) {
+ stack::push(L, index);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L, firstindex);
+ lua_settable(L, tableindex);
+ ++index;
+ ++firstindex;
+ }
+ lua_pop(L, 1 + p);
+ }
+#endif // Lua Version 5.3 and others
+ }
+ // TODO: figure out a better way to do this...?
+ // set_field(L, -1, cont.size());
+ return 1;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<as_table_t<T>> {
+ static int push(lua_State* L, const as_table_t<T>& value_) {
+ using inner_t = std::remove_pointer_t<meta::unwrap_unqualified_t<T>>;
+ if constexpr (is_container_v<inner_t>) {
+ return stack::push<detail::as_table_tag<T>>(L, value_.value());
+ }
+ else {
+ return stack::push(L, value_.value());
+ }
+ }
+
+ static int push(lua_State* L, const T& value_) {
+ using inner_t = std::remove_pointer_t<meta::unwrap_unqualified_t<T>>;
+ if constexpr (is_container_v<inner_t>) {
+ return stack::push<detail::as_table_tag<T>>(L, value_);
+ }
+ else {
+ return stack::push(L, value_);
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<nested<T>> {
+ static int push(lua_State* L, const T& nested_value) noexcept {
+ using Tu = meta::unwrap_unqualified_t<T>;
+ using inner_t = std::remove_pointer_t<Tu>;
+ if constexpr (is_container_v<inner_t>) {
+ return stack::push<detail::as_table_tag<T>>(L, nested_value, nested_tag);
+ }
+ else {
+ return stack::push<Tu>(L, nested_value);
+ }
+ }
+
+ static int push(lua_State* L, const nested<T>& nested_wrapper_) noexcept {
+ using Tu = meta::unwrap_unqualified_t<T>;
+ using inner_t = std::remove_pointer_t<Tu>;
+ if constexpr (is_container_v<inner_t>) {
+ return stack::push<detail::as_table_tag<T>>(L, nested_wrapper_.value(), nested_tag);
+ }
+ else {
+ return stack::push<Tu>(L, nested_wrapper_.value());
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<std::initializer_list<T>> {
+ static int push(lua_State* L, const std::initializer_list<T>& il) noexcept {
+ unqualified_pusher<detail::as_table_tag<std::initializer_list<T>>> p {};
+ return p.push(L, il);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<lua_nil_t> {
+ static int push(lua_State* L, lua_nil_t) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushnil(L);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<stack_count> {
+ static int push(lua_State*, stack_count st) noexcept {
+ return st.count;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<metatable_key_t> {
+ static int push(lua_State* L, metatable_key_t) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, to_string(meta_function::metatable).c_str(), 4);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<std::remove_pointer_t<lua_CFunction>> {
+ static int push(lua_State* L, lua_CFunction func, int n = 0) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushcclosure(L, func, n);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<lua_CFunction> {
+ static int push(lua_State* L, lua_CFunction func, int n = 0) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushcclosure(L, func, n);
+ return 1;
+ }
+ };
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ template <>
+ struct unqualified_pusher<std::remove_pointer_t<detail::lua_CFunction_noexcept>> {
+ static int push(lua_State* L, detail::lua_CFunction_noexcept func, int n = 0) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushcclosure(L, func, n);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<detail::lua_CFunction_noexcept> {
+ static int push(lua_State* L, detail::lua_CFunction_noexcept func, int n = 0) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushcclosure(L, func, n);
+ return 1;
+ }
+ };
+#endif // noexcept function type
+
+ template <>
+ struct unqualified_pusher<c_closure> {
+ static int push(lua_State* L, c_closure cc) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushcclosure(L, cc.c_function, cc.upvalues);
+ return 1;
+ }
+ };
+
+ template <typename Arg, typename... Args>
+ struct unqualified_pusher<closure<Arg, Args...>> {
+ template <std::size_t... I, typename T>
+ static int push(std::index_sequence<I...>, lua_State* L, T&& c) {
+ using f_tuple = decltype(std::forward<T>(c).upvalues);
+ int pushcount = multi_push(L, std::get<I>(std::forward<f_tuple>(std::forward<T>(c).upvalues))...);
+ return stack::push(L, c_closure(c.c_function, pushcount));
+ }
+
+ template <typename T>
+ static int push(lua_State* L, T&& c) {
+ return push(std::make_index_sequence<1 + sizeof...(Args)>(), L, std::forward<T>(c));
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<void*> {
+ static int push(lua_State* L, void* userdata) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlightuserdata(L, userdata);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<const void*> {
+ static int push(lua_State* L, const void* userdata) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlightuserdata(L, const_cast<void*>(userdata));
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<lightuserdata_value> {
+ static int push(lua_State* L, lightuserdata_value userdata) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlightuserdata(L, userdata);
+ return 1;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<light<T>> {
+ static int push(lua_State* L, light<T> l) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlightuserdata(L, static_cast<void*>(l.value()));
+ return 1;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<user<T>> {
+ template <bool with_meta = true, typename Key, typename... Args>
+ static int push_with(lua_State* L, Key&& name, Args&&... args) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_userdata);
+#endif // make sure stack doesn't overflow
+ // A dumb pusher
+ T* data = detail::user_allocate<T>(L);
+ if (with_meta) {
+ // Make sure we have a plain GC set for this data
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ if (luaL_newmetatable(L, name) != 0) {
+ lua_CFunction cdel = detail::user_alloc_destroy<T>;
+ lua_pushcclosure(L, cdel, 0);
+ lua_setfield(L, -2, "__gc");
+ }
+ lua_setmetatable(L, -2);
+ }
+ std::allocator<T> alloc {};
+ std::allocator_traits<std::allocator<T>>::construct(alloc, data, std::forward<Args>(args)...);
+ return 1;
+ }
+
+ template <typename Arg, typename... Args>
+ static int push(lua_State* L, Arg&& arg, Args&&... args) {
+ if constexpr (std::is_same_v<meta::unqualified_t<Arg>, metatable_key_t>) {
+ const auto name = &arg[0];
+ return push_with<true>(L, name, std::forward<Args>(args)...);
+ }
+ else if constexpr (std::is_same_v<meta::unqualified_t<Arg>, no_metatable_t>) {
+ (void)arg;
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with<false>(L, name, std::forward<Args>(args)...);
+ }
+ else {
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with(L, name, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ }
+
+ static int push(lua_State* L, const user<T>& u) {
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with(L, name, u.value);
+ }
+
+ static int push(lua_State* L, user<T>&& u) {
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with(L, name, std::move(u.value()));
+ }
+
+ static int push(lua_State* L, no_metatable_t, const user<T>& u) {
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with<false>(L, name, u.value());
+ }
+
+ static int push(lua_State* L, no_metatable_t, user<T>&& u) {
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with<false>(L, name, std::move(u.value()));
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<userdata_value> {
+ static int push(lua_State* L, userdata_value data) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_userdata);
+#endif // make sure stack doesn't overflow
+ void** ud = detail::usertype_allocate_pointer<void>(L);
+ *ud = data.value();
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<const char*> {
+ static int push_sized(lua_State* L, const char* str, std::size_t len) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str, len);
+ return 1;
+ }
+
+ static int push(lua_State* L, const char* str) {
+ if (str == nullptr)
+ return stack::push(L, lua_nil);
+ return push_sized(L, str, std::char_traits<char>::length(str));
+ }
+
+ static int push(lua_State* L, const char* strb, const char* stre) {
+ return push_sized(L, strb, static_cast<std::size_t>(stre - strb));
+ }
+
+ static int push(lua_State* L, const char* str, std::size_t len) {
+ return push_sized(L, str, len);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char*> {
+ static int push_sized(lua_State* L, const char* str, std::size_t len) {
+ unqualified_pusher<const char*> p {};
+ (void)p;
+ return p.push_sized(L, str, len);
+ }
+
+ static int push(lua_State* L, const char* str) {
+ unqualified_pusher<const char*> p {};
+ (void)p;
+ return p.push(L, str);
+ }
+
+ static int push(lua_State* L, const char* strb, const char* stre) {
+ unqualified_pusher<const char*> p {};
+ (void)p;
+ return p.push(L, strb, stre);
+ }
+
+ static int push(lua_State* L, const char* str, std::size_t len) {
+ unqualified_pusher<const char*> p {};
+ (void)p;
+ return p.push(L, str, len);
+ }
+ };
+
+ template <size_t N>
+ struct unqualified_pusher<char[N]> {
+ static int push(lua_State* L, const char (&str)[N]) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str, std::char_traits<char>::length(str));
+ return 1;
+ }
+
+ static int push(lua_State* L, const char (&str)[N], std::size_t sz) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str, sz);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char> {
+ static int push(lua_State* L, char c) {
+ const char str[2] = { c, '\0' };
+ return stack::push(L, static_cast<const char*>(str), 1u);
+ }
+ };
+
+#if SOL_IS_ON(SOL_CHAR8_T)
+ template <>
+ struct unqualified_pusher<const char8_t*> {
+ static int push_sized(lua_State* L, const char8_t* str, std::size_t len) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, reinterpret_cast<const char*>(str), len);
+ return 1;
+ }
+
+ static int push(lua_State* L, const char8_t* str) {
+ if (str == nullptr)
+ return stack::push(L, lua_nil);
+ return push_sized(L, str, std::char_traits<char>::length(reinterpret_cast<const char*>(str)));
+ }
+
+ static int push(lua_State* L, const char8_t* strb, const char8_t* stre) {
+ return push_sized(L, strb, static_cast<std::size_t>(stre - strb));
+ }
+
+ static int push(lua_State* L, const char8_t* str, std::size_t len) {
+ return push_sized(L, str, len);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char8_t*> {
+ static int push_sized(lua_State* L, const char8_t* str, std::size_t len) {
+ unqualified_pusher<const char8_t*> p {};
+ (void)p;
+ return p.push_sized(L, str, len);
+ }
+
+ static int push(lua_State* L, const char8_t* str) {
+ unqualified_pusher<const char8_t*> p {};
+ (void)p;
+ return p.push(L, str);
+ }
+
+ static int push(lua_State* L, const char8_t* strb, const char8_t* stre) {
+ unqualified_pusher<const char8_t*> p {};
+ (void)p;
+ return p.push(L, strb, stre);
+ }
+
+ static int push(lua_State* L, const char8_t* str, std::size_t len) {
+ unqualified_pusher<const char8_t*> p {};
+ (void)p;
+ return p.push(L, str, len);
+ }
+ };
+
+ template <size_t N>
+ struct unqualified_pusher<char8_t[N]> {
+ static int push(lua_State* L, const char8_t (&str)[N]) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ const char* str_as_char = reinterpret_cast<const char*>(static_cast<const char8_t*>(str));
+ lua_pushlstring(L, str_as_char, std::char_traits<char>::length(str_as_char));
+ return 1;
+ }
+
+ static int push(lua_State* L, const char8_t (&str)[N], std::size_t sz) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str, sz);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char8_t> {
+ static int push(lua_State* L, char8_t c) {
+ const char8_t str[2] = { c, '\0' };
+ return stack::push(L, static_cast<const char8_t*>(str), 1u);
+ }
+ };
+#endif // char8_t
+
+ template <typename Ch, typename Traits, typename Al>
+ struct unqualified_pusher<std::basic_string<Ch, Traits, Al>> {
+ static int push(lua_State* L, const std::basic_string<Ch, Traits, Al>& str) {
+ if constexpr (!std::is_same_v<Ch, char>) {
+ return stack::push(L, str.data(), str.size());
+ }
+ else {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str.c_str(), str.size());
+ return 1;
+ }
+ }
+
+ static int push(lua_State* L, const std::basic_string<Ch, Traits, Al>& str, std::size_t sz) {
+ if constexpr (!std::is_same_v<Ch, char>) {
+ return stack::push(L, str.data(), sz);
+ }
+ else {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str.c_str(), sz);
+ return 1;
+ }
+ }
+ };
+
+ template <typename Ch, typename Traits>
+ struct unqualified_pusher<basic_string_view<Ch, Traits>> {
+ static int push(lua_State* L, const basic_string_view<Ch, Traits>& sv) {
+ return stack::push(L, sv.data(), sv.length());
+ }
+
+ static int push(lua_State* L, const basic_string_view<Ch, Traits>& sv, std::size_t n) {
+ return stack::push(L, sv.data(), n);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<meta_function> {
+ static int push(lua_State* L, meta_function m) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_meta_function_name);
+#endif // make sure stack doesn't overflow
+ const std::string& str = to_string(m);
+ lua_pushlstring(L, str.c_str(), str.size());
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<absolute_index> {
+ static int push(lua_State* L, absolute_index ai) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L, ai);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<raw_index> {
+ static int push(lua_State* L, raw_index ri) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L, ri);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<ref_index> {
+ static int push(lua_State* L, ref_index ri) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ri);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<const wchar_t*> {
+ static int push(lua_State* L, const wchar_t* wstr) {
+ return push(L, wstr, std::char_traits<wchar_t>::length(wstr));
+ }
+
+ static int push(lua_State* L, const wchar_t* wstr, std::size_t sz) {
+ return push(L, wstr, wstr + sz);
+ }
+
+ static int push(lua_State* L, const wchar_t* strb, const wchar_t* stre) {
+ if constexpr (sizeof(wchar_t) == 2) {
+ const char16_t* sb = reinterpret_cast<const char16_t*>(strb);
+ const char16_t* se = reinterpret_cast<const char16_t*>(stre);
+ return stack::push(L, sb, se);
+ }
+ else {
+ const char32_t* sb = reinterpret_cast<const char32_t*>(strb);
+ const char32_t* se = reinterpret_cast<const char32_t*>(stre);
+ return stack::push(L, sb, se);
+ }
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<wchar_t*> {
+ static int push(lua_State* L, const wchar_t* str) {
+ unqualified_pusher<const wchar_t*> p {};
+ (void)p;
+ return p.push(L, str);
+ }
+
+ static int push(lua_State* L, const wchar_t* strb, const wchar_t* stre) {
+ unqualified_pusher<const wchar_t*> p {};
+ (void)p;
+ return p.push(L, strb, stre);
+ }
+
+ static int push(lua_State* L, const wchar_t* str, std::size_t len) {
+ unqualified_pusher<const wchar_t*> p {};
+ (void)p;
+ return p.push(L, str, len);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<const char16_t*> {
+ static int convert_into(lua_State* L, char* start, std::size_t, const char16_t* strb, const char16_t* stre) {
+ char* target = start;
+ char32_t cp = 0;
+ for (const char16_t* strtarget = strb; strtarget < stre;) {
+ auto dr = unicode::utf16_to_code_point(strtarget, stre);
+ if (dr.error != unicode::error_code::ok) {
+ cp = unicode::unicode_detail::replacement;
+ }
+ else {
+ cp = dr.codepoint;
+ }
+ auto er = unicode::code_point_to_utf8(cp);
+ const char* utf8data = er.code_units.data();
+ std::memcpy(target, utf8data, er.code_units_size);
+ target += er.code_units_size;
+ strtarget = dr.next;
+ }
+
+ return stack::push(L, start, target);
+ }
+
+ static int push(lua_State* L, const char16_t* u16str) {
+ return push(L, u16str, std::char_traits<char16_t>::length(u16str));
+ }
+
+ static int push(lua_State* L, const char16_t* u16str, std::size_t sz) {
+ return push(L, u16str, u16str + sz);
+ }
+
+ static int push(lua_State* L, const char16_t* strb, const char16_t* stre) {
+ char sbo[SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_];
+ // if our max string space is small enough, use SBO
+ // right off the bat
+ std::size_t max_possible_code_units = static_cast<std::size_t>(static_cast<std::size_t>(stre - strb) * static_cast<std::size_t>(4));
+ if (max_possible_code_units <= SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_) {
+ return convert_into(L, sbo, max_possible_code_units, strb, stre);
+ }
+ // otherwise, we must manually count/check size
+ std::size_t needed_size = 0;
+ for (const char16_t* strtarget = strb; strtarget < stre;) {
+ auto dr = unicode::utf16_to_code_point(strtarget, stre);
+ auto er = unicode::code_point_to_utf8(dr.codepoint);
+ needed_size += er.code_units_size;
+ strtarget = dr.next;
+ }
+ if (needed_size < SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_) {
+ return convert_into(L, sbo, needed_size, strb, stre);
+ }
+ std::string u8str("", 0);
+ u8str.resize(needed_size);
+ char* target = const_cast<char*>(u8str.data());
+ return convert_into(L, target, needed_size, strb, stre);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char16_t*> {
+ static int push(lua_State* L, const char16_t* str) {
+ unqualified_pusher<const char16_t*> p {};
+ (void)p;
+ return p.push(L, str);
+ }
+
+ static int push(lua_State* L, const char16_t* strb, const char16_t* stre) {
+ unqualified_pusher<const char16_t*> p {};
+ (void)p;
+ return p.push(L, strb, stre);
+ }
+
+ static int push(lua_State* L, const char16_t* str, std::size_t len) {
+ unqualified_pusher<const char16_t*> p {};
+ (void)p;
+ return p.push(L, str, len);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<const char32_t*> {
+ static int convert_into(lua_State* L, char* start, std::size_t, const char32_t* strb, const char32_t* stre) {
+ char* target = start;
+ char32_t cp = 0;
+ for (const char32_t* strtarget = strb; strtarget < stre;) {
+ auto dr = unicode::utf32_to_code_point(strtarget, stre);
+ if (dr.error != unicode::error_code::ok) {
+ cp = unicode::unicode_detail::replacement;
+ }
+ else {
+ cp = dr.codepoint;
+ }
+ auto er = unicode::code_point_to_utf8(cp);
+ const char* data = er.code_units.data();
+ std::memcpy(target, data, er.code_units_size);
+ target += er.code_units_size;
+ strtarget = dr.next;
+ }
+ return stack::push(L, start, target);
+ }
+
+ static int push(lua_State* L, const char32_t* u32str) {
+ return push(L, u32str, u32str + std::char_traits<char32_t>::length(u32str));
+ }
+
+ static int push(lua_State* L, const char32_t* u32str, std::size_t sz) {
+ return push(L, u32str, u32str + sz);
+ }
+
+ static int push(lua_State* L, const char32_t* strb, const char32_t* stre) {
+ char sbo[SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_];
+ // if our max string space is small enough, use SBO
+ // right off the bat
+ std::size_t max_possible_code_units = static_cast<std::size_t>(static_cast<std::size_t>(stre - strb) * static_cast<std::size_t>(4));
+ if (max_possible_code_units <= SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_) {
+ return convert_into(L, sbo, max_possible_code_units, strb, stre);
+ }
+ // otherwise, we must manually count/check size
+ std::size_t needed_size = 0;
+ for (const char32_t* strtarget = strb; strtarget < stre;) {
+ auto dr = unicode::utf32_to_code_point(strtarget, stre);
+ auto er = unicode::code_point_to_utf8(dr.codepoint);
+ needed_size += er.code_units_size;
+ strtarget = dr.next;
+ }
+ if (needed_size < SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_) {
+ return convert_into(L, sbo, needed_size, strb, stre);
+ }
+ std::string u8str("", 0);
+ u8str.resize(needed_size);
+ char* target = const_cast<char*>(u8str.data());
+ return convert_into(L, target, needed_size, strb, stre);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char32_t*> {
+ static int push(lua_State* L, const char32_t* str) {
+ unqualified_pusher<const char32_t*> p {};
+ (void)p;
+ return p.push(L, str);
+ }
+
+ static int push(lua_State* L, const char32_t* strb, const char32_t* stre) {
+ unqualified_pusher<const char32_t*> p {};
+ (void)p;
+ return p.push(L, strb, stre);
+ }
+
+ static int push(lua_State* L, const char32_t* str, std::size_t len) {
+ unqualified_pusher<const char32_t*> p {};
+ (void)p;
+ return p.push(L, str, len);
+ }
+ };
+
+ template <size_t N>
+ struct unqualified_pusher<wchar_t[N]> {
+ static int push(lua_State* L, const wchar_t (&str)[N]) {
+ return push(L, str, std::char_traits<wchar_t>::length(str));
+ }
+
+ static int push(lua_State* L, const wchar_t (&str)[N], std::size_t sz) {
+ const wchar_t* str_ptr = static_cast<const wchar_t*>(str);
+ return stack::push<const wchar_t*>(L, str_ptr, str_ptr + sz);
+ }
+ };
+
+ template <size_t N>
+ struct unqualified_pusher<char16_t[N]> {
+ static int push(lua_State* L, const char16_t (&str)[N]) {
+ return push(L, str, std::char_traits<char16_t>::length(str));
+ }
+
+ static int push(lua_State* L, const char16_t (&str)[N], std::size_t sz) {
+ const char16_t* str_ptr = static_cast<const char16_t*>(str);
+ return stack::push<const char16_t*>(L, str_ptr, str_ptr + sz);
+ }
+ };
+
+ template <size_t N>
+ struct unqualified_pusher<char32_t[N]> {
+ static int push(lua_State* L, const char32_t (&str)[N]) {
+ return push(L, str, std::char_traits<char32_t>::length(str));
+ }
+
+ static int push(lua_State* L, const char32_t (&str)[N], std::size_t sz) {
+ const char32_t* str_ptr = static_cast<const char32_t*>(str);
+ return stack::push<const char32_t*>(L, str_ptr, str_ptr + sz);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<wchar_t> {
+ static int push(lua_State* L, wchar_t c) {
+ const wchar_t str[2] = { c, '\0' };
+ return stack::push(L, static_cast<const wchar_t*>(str), 1u);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char16_t> {
+ static int push(lua_State* L, char16_t c) {
+ const char16_t str[2] = { c, '\0' };
+ return stack::push(L, static_cast<const char16_t*>(str), 1u);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char32_t> {
+ static int push(lua_State* L, char32_t c) {
+ const char32_t str[2] = { c, '\0' };
+ return stack::push(L, static_cast<const char32_t*>(str), 1u);
+ }
+ };
+
+ template <typename... Args>
+ struct unqualified_pusher<std::tuple<Args...>> {
+ template <std::size_t... I, typename T>
+ static int push(std::index_sequence<I...>, lua_State* L, T&& t) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, static_cast<int>(sizeof...(I)), detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ int pushcount = 0;
+ (void)detail::swallow { 0, (pushcount += stack::push(L, std::get<I>(std::forward<T>(t))), 0)... };
+ return pushcount;
+ }
+
+ template <typename T>
+ static int push(lua_State* L, T&& t) {
+ return push(std::index_sequence_for<Args...>(), L, std::forward<T>(t));
+ }
+ };
+
+ template <typename A, typename B>
+ struct unqualified_pusher<std::pair<A, B>> {
+ template <typename T>
+ static int push(lua_State* L, T&& t) {
+ int pushcount = stack::push(L, std::get<0>(std::forward<T>(t)));
+ pushcount += stack::push(L, std::get<1>(std::forward<T>(t)));
+ return pushcount;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<T, std::enable_if_t<meta::is_optional_v<T>>> {
+ using ValueType = typename meta::unqualified_t<T>::value_type;
+
+ template <typename Optional>
+ static int push(lua_State* L, Optional&& op) {
+ using QualifiedValueType = meta::conditional_t<std::is_lvalue_reference_v<Optional>, ValueType&, ValueType&&>;
+ if (!op) {
+ return stack::push(L, nullopt);
+ }
+ return stack::push(L, static_cast<QualifiedValueType>(op.value()));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<forward_as_value_t<T>> {
+ static int push(lua_State* L, const forward_as_value_t<T>& value_) {
+ return stack::push<T>(L, value_.value());
+ }
+
+ static int push(lua_State* L, forward_as_value_t<T>&& value_) {
+ return stack::push<T>(L, std::move(value_).value());
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<nullopt_t> {
+ static int push(lua_State* L, nullopt_t) noexcept {
+ return stack::push(L, lua_nil);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<std::nullptr_t> {
+ static int push(lua_State* L, std::nullptr_t) noexcept {
+ return stack::push(L, lua_nil);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<this_state> {
+ static int push(lua_State*, const this_state&) noexcept {
+ return 0;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<this_main_state> {
+ static int push(lua_State*, const this_main_state&) noexcept {
+ return 0;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<new_table> {
+ static int push(lua_State* L, const new_table& nt) {
+ lua_createtable(L, nt.sequence_hint, nt.map_hint);
+ return 1;
+ }
+ };
+
+ template <typename Allocator>
+ struct unqualified_pusher<basic_bytecode<Allocator>> {
+ template <typename T>
+ static int push(lua_State* L, T&& bc, const char* bytecode_name) {
+ const auto first = bc.data();
+ const auto bcsize = bc.size();
+ // pushes either the function, or an error
+ // if it errors, shit goes south, and people can test that upstream
+ (void)luaL_loadbuffer(
+ L, reinterpret_cast<const char*>(first), static_cast<std::size_t>(bcsize * (sizeof(*first) / sizeof(const char))), bytecode_name);
+ return 1;
+ }
+
+ template <typename T>
+ static int push(lua_State* L, T&& bc) {
+ return push(L, std::forward<bc>(bc), "bytecode");
+ }
+ };
+
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ namespace stack_detail {
+
+ struct push_function {
+ lua_State* L;
+
+ push_function(lua_State* L_) noexcept : L(L_) {
+ }
+
+ template <typename T>
+ int operator()(T&& value) const {
+ return stack::push<T>(L, std::forward<T>(value));
+ }
+ };
+
+ } // namespace stack_detail
+
+ template <typename... Tn>
+ struct unqualified_pusher<std::variant<Tn...>> {
+ static int push(lua_State* L, const std::variant<Tn...>& v) {
+ return std::visit(stack_detail::push_function(L), v);
+ }
+
+ static int push(lua_State* L, std::variant<Tn...>&& v) {
+ return std::visit(stack_detail::push_function(L), std::move(v));
+ }
+ };
+#endif // Variant because Clang is terrible
+
+}} // namespace sol::stack
+
+// end of sol/stack_push.hpp
+
+// beginning of sol/stack_pop.hpp
+
+#include <utility>
+#include <tuple>
+
+namespace sol { namespace stack {
+ template <typename T, typename>
+ struct popper {
+ inline static decltype(auto) pop(lua_State* L) {
+ if constexpr (is_stack_based_v<meta::unqualified_t<T>>) {
+ static_assert(!is_stack_based_v<meta::unqualified_t<T>>,
+ "You cannot pop something that lives solely on the stack: it will not remain on the stack when popped and thusly will go out of "
+ "scope!");
+ }
+ else {
+ record tracking {};
+ decltype(auto) r = get<T>(L, -lua_size<T>::value, tracking);
+ lua_pop(L, tracking.used);
+ return r;
+ }
+ }
+ };
+}} // namespace sol::stack
+
+// end of sol/stack_pop.hpp
+
+// beginning of sol/stack_field.hpp
+
+namespace sol { namespace stack {
+
+ namespace stack_detail {
+ template <typename T, bool global, bool raw>
+ inline constexpr bool is_get_direct_tableless_v = (global && !raw && meta::is_c_str_or_string_v<T>);
+
+ template <typename T, bool global, bool raw>
+ inline constexpr bool is_get_direct_v = (is_get_direct_tableless_v<T, global, raw>) // cf-hack
+ || (!global && !raw && (meta::is_c_str_or_string_v<T> || meta::is_string_of_v<T, char>)) // cf-hack
+ || (!global && raw && (std::is_integral_v<T> && !std::is_same_v<T, bool>))
+#if SOL_LUA_VERSION_I_ >= 503
+ || (!global && !raw && (std::is_integral_v<T> && !std::is_same_v<T, bool>))
+#endif // integer keys 5.3 or better
+#if SOL_LUA_VERSION_I_ >= 502
+ || (!global && raw && std::is_pointer_v<T> && std::is_void_v<std::remove_pointer_t<T>>)
+#endif // void pointer keys 5.2 or better
+ ;
+
+ template <typename T, bool global, bool raw>
+ inline constexpr bool is_set_direct_tableless_v = (global && !raw && meta::is_c_str_or_string_v<T>);
+
+ template <typename T, bool global, bool raw>
+ inline constexpr bool is_set_direct_v = (is_set_direct_tableless_v<T, global, raw>) // cf-hack
+ || (!global && !raw && (meta::is_c_str_or_string_v<T> || meta::is_string_of_v<T, char>)) // cf-hack
+ || (!global && raw && (std::is_integral_v<T> && !std::is_same_v<T, bool>)) // cf-hack
+#if SOL_LUA_VERSION_I_ >= 503
+ || (!global && !raw && (std::is_integral_v<T> && !std::is_same_v<T, bool>))
+#endif // integer keys 5.3 or better
+#if SOL_LUA_VERSION_I_ >= 502
+ || (!global && raw && (std::is_pointer_v<T> && std::is_void_v<std::remove_pointer_t<T>>))
+#endif // void pointer keys 5.2 or better
+ ;
+ } // namespace stack_detail
+
+ template <typename T, bool global, bool raw, typename>
+ struct field_getter {
+ static inline constexpr int default_table_index
+ = meta::conditional_t<stack_detail::is_get_direct_v<T, global, raw>, std::integral_constant<int, -1>, std::integral_constant<int, -2>>::value;
+
+ template <typename Key>
+ void get(lua_State* L, Key&& key, int tableindex = default_table_index) {
+ if constexpr (std::is_same_v<T, update_if_empty_t> || std::is_same_v<T, override_value_t> || std::is_same_v<T, create_if_nil_t>) {
+ (void)L;
+ (void)key;
+ (void)tableindex;
+ }
+ else if constexpr (std::is_same_v<T, env_key_t>) {
+ (void)key;
+#if SOL_LUA_VERSION_I_ < 502
+ // Use lua_setfenv
+ lua_getfenv(L, tableindex);
+#else
+ // Use upvalues as explained in Lua 5.2 and beyond's manual
+ if (lua_getupvalue(L, tableindex, 1) == nullptr) {
+ push(L, lua_nil);
+ }
+#endif
+ }
+ else if constexpr (std::is_same_v<T, metatable_key_t>) {
+ (void)key;
+ if (lua_getmetatable(L, tableindex) == 0)
+ push(L, lua_nil);
+ }
+ else if constexpr (raw) {
+ if constexpr (std::is_integral_v<T> && !std::is_same_v<bool, T>) {
+ lua_rawgeti(L, tableindex, static_cast<lua_Integer>(key));
+ }
+#if SOL_LUA_VERSION_I_ >= 502
+ else if constexpr (std::is_pointer_v<T> && std::is_void_v<std::remove_pointer_t<T>>) {
+ lua_rawgetp(L, tableindex, key);
+ }
+#endif // Lua 5.2.x+
+ else {
+ push(L, std::forward<Key>(key));
+ lua_rawget(L, tableindex);
+ }
+ }
+ else {
+ if constexpr (meta::is_c_str_or_string_v<T>) {
+ if constexpr (global) {
+ (void)tableindex;
+ lua_getglobal(L, &key[0]);
+ }
+ else {
+ lua_getfield(L, tableindex, &key[0]);
+ }
+ }
+ else if constexpr (std::is_same_v<T, meta_function>) {
+ const auto& real_key = to_string(key);
+ lua_getfield(L, tableindex, &real_key[0]);
+ }
+#if SOL_LUA_VERSION_I_ >= 503
+ else if constexpr (std::is_integral_v<T> && !std::is_same_v<bool, T>) {
+ lua_geti(L, tableindex, static_cast<lua_Integer>(key));
+ }
+#endif // Lua 5.3.x+
+ else {
+ push(L, std::forward<Key>(key));
+ lua_gettable(L, tableindex);
+ }
+ }
+ }
+ };
+
+ template <typename... Args, bool b, bool raw, typename C>
+ struct field_getter<std::tuple<Args...>, b, raw, C> {
+ template <std::size_t... I, typename Keys>
+ void apply(std::index_sequence<0, I...>, lua_State* L, Keys&& keys, int tableindex) {
+ get_field<b, raw>(L, std::get<0>(std::forward<Keys>(keys)), tableindex);
+ void(detail::swallow { (get_field<false, raw>(L, std::get<I>(std::forward<Keys>(keys))), 0)... });
+ reference saved(L, -1);
+ lua_pop(L, static_cast<int>(sizeof...(I)));
+ saved.push();
+ }
+
+ template <typename Keys>
+ void get(lua_State* L, Keys&& keys) {
+ apply(std::make_index_sequence<sizeof...(Args)>(), L, std::forward<Keys>(keys), lua_absindex(L, -1));
+ }
+
+ template <typename Keys>
+ void get(lua_State* L, Keys&& keys, int tableindex) {
+ apply(std::make_index_sequence<sizeof...(Args)>(), L, std::forward<Keys>(keys), tableindex);
+ }
+ };
+
+ template <typename A, typename B, bool b, bool raw, typename C>
+ struct field_getter<std::pair<A, B>, b, raw, C> {
+ template <typename Keys>
+ void get(lua_State* L, Keys&& keys, int tableindex) {
+ get_field<b, raw>(L, std::get<0>(std::forward<Keys>(keys)), tableindex);
+ get_field<false, raw>(L, std::get<1>(std::forward<Keys>(keys)));
+ reference saved(L, -1);
+ lua_pop(L, static_cast<int>(2));
+ saved.push();
+ }
+
+ template <typename Keys>
+ void get(lua_State* L, Keys&& keys) {
+ get_field<b, raw>(L, std::get<0>(std::forward<Keys>(keys)));
+ get_field<false, raw>(L, std::get<1>(std::forward<Keys>(keys)));
+ reference saved(L, -1);
+ lua_pop(L, static_cast<int>(2));
+ saved.push();
+ }
+ };
+
+ template <typename T, bool global, bool raw, typename>
+ struct field_setter {
+ static constexpr int default_table_index
+ = meta::conditional_t<stack_detail::is_set_direct_v<T, global, raw>, std::integral_constant<int, -2>, std::integral_constant<int, -3>>::value;
+
+ template <typename Key, typename Value>
+ void set(lua_State* L, Key&& key, Value&& value, int tableindex = default_table_index) {
+ if constexpr (std::is_same_v<T, update_if_empty_t> || std::is_same_v<T, override_value_t>) {
+ (void)L;
+ (void)key;
+ (void)value;
+ (void)tableindex;
+ }
+ else if constexpr (std::is_same_v<T, metatable_key_t>) {
+ (void)key;
+ push(L, std::forward<Value>(value));
+ lua_setmetatable(L, tableindex);
+ }
+ else if constexpr (raw) {
+ if constexpr (std::is_integral_v<T> && !std::is_same_v<bool, T>) {
+ push(L, std::forward<Value>(value));
+ lua_rawseti(L, tableindex, static_cast<lua_Integer>(key));
+ }
+#if SOL_LUA_VERSION_I_ >= 502
+ else if constexpr (std::is_pointer_v<T> && std::is_void_v<std::remove_pointer_t<T>>) {
+ push(L, std::forward<Value>(value));
+ lua_rawsetp(L, tableindex, std::forward<Key>(key));
+ }
+#endif // Lua 5.2.x
+ else {
+ push(L, std::forward<Key>(key));
+ push(L, std::forward<Value>(value));
+ lua_rawset(L, tableindex);
+ }
+ }
+ else {
+ if constexpr (meta::is_c_str_or_string_v<T>) {
+ if constexpr (global) {
+ push(L, std::forward<Value>(value));
+ lua_setglobal(L, &key[0]);
+ (void)tableindex;
+ }
+ else {
+ push(L, std::forward<Value>(value));
+ lua_setfield(L, tableindex, &key[0]);
+ }
+ }
+#if SOL_LUA_VERSION_I_ >= 503
+ else if constexpr (std::is_integral_v<T> && !std::is_same_v<bool, T>) {
+ push(L, std::forward<Value>(value));
+ lua_seti(L, tableindex, static_cast<lua_Integer>(key));
+ }
+#endif // Lua 5.3.x
+ else {
+ push(L, std::forward<Key>(key));
+ push(L, std::forward<Value>(value));
+ lua_settable(L, tableindex);
+ }
+ }
+ }
+ };
+
+ template <typename... Args, bool b, bool raw, typename C>
+ struct field_setter<std::tuple<Args...>, b, raw, C> {
+ template <bool g, std::size_t I, typename Keys, typename Value>
+ void apply(std::index_sequence<I>, lua_State* L, Keys&& keys, Value&& value, int tableindex) {
+ I < 1 ? set_field<g, raw>(L, std::get<I>(std::forward<Keys>(keys)), std::forward<Value>(value), tableindex)
+ : set_field<g, raw>(L, std::get<I>(std::forward<Keys>(keys)), std::forward<Value>(value));
+ }
+
+ template <bool g, std::size_t I0, std::size_t I1, std::size_t... I, typename Keys, typename Value>
+ void apply(std::index_sequence<I0, I1, I...>, lua_State* L, Keys&& keys, Value&& value, int tableindex) {
+ I0 < 1 ? get_field<g, raw>(L, std::get<I0>(std::forward<Keys>(keys)), tableindex)
+ : get_field<g, raw>(L, std::get<I0>(std::forward<Keys>(keys)), -1);
+ apply<false>(std::index_sequence<I1, I...>(), L, std::forward<Keys>(keys), std::forward<Value>(value), -1);
+ }
+
+ template <bool g, std::size_t I0, std::size_t... I, typename Keys, typename Value>
+ void top_apply(std::index_sequence<I0, I...>, lua_State* L, Keys&& keys, Value&& value, int tableindex) {
+ apply<g>(std::index_sequence<I0, I...>(), L, std::forward<Keys>(keys), std::forward<Value>(value), tableindex);
+ lua_pop(L, static_cast<int>(sizeof...(I)));
+ }
+
+ template <typename Keys, typename Value>
+ void set(lua_State* L, Keys&& keys, Value&& value, int tableindex = -3) {
+ top_apply<b>(std::make_index_sequence<sizeof...(Args)>(), L, std::forward<Keys>(keys), std::forward<Value>(value), tableindex);
+ }
+ };
+
+ template <typename A, typename B, bool b, bool raw, typename C>
+ struct field_setter<std::pair<A, B>, b, raw, C> {
+ template <typename Keys, typename Value>
+ void set(lua_State* L, Keys&& keys, Value&& value, int tableindex = -1) {
+ get_field<b, raw>(L, std::get<0>(std::forward<Keys>(keys)), tableindex);
+ set_field<false, raw>(L, std::get<1>(std::forward<Keys>(keys)), std::forward<Value>(value), lua_gettop(L));
+ lua_pop(L, 1);
+ }
+ };
+}} // namespace sol::stack
+
+// end of sol/stack_field.hpp
+
+// beginning of sol/stack_probe.hpp
+
+namespace sol { namespace stack {
+ template <typename T, typename P, bool b, bool raw, typename>
+ struct probe_field_getter {
+ template <typename Key>
+ probe get(lua_State* L, Key&& key, int tableindex = -2) {
+ if constexpr (!b) {
+ if (!maybe_indexable(L, tableindex)) {
+ return probe(false, 0);
+ }
+ }
+ get_field<b, raw>(L, std::forward<Key>(key), tableindex);
+ return probe(check<P>(L), 1);
+ }
+ };
+
+ template <typename A, typename B, typename P, bool b, bool raw, typename C>
+ struct probe_field_getter<std::pair<A, B>, P, b, raw, C> {
+ template <typename Keys>
+ probe get(lua_State* L, Keys&& keys, int tableindex = -2) {
+ if (!b && !maybe_indexable(L, tableindex)) {
+ return probe(false, 0);
+ }
+ get_field<b, raw>(L, std::get<0>(keys), tableindex);
+ if (!maybe_indexable(L)) {
+ return probe(false, 1);
+ }
+ get_field<false, raw>(L, std::get<1>(keys), tableindex);
+ return probe(check<P>(L), 2);
+ }
+ };
+
+ template <typename... Args, typename P, bool b, bool raw, typename C>
+ struct probe_field_getter<std::tuple<Args...>, P, b, raw, C> {
+ template <std::size_t I, typename Keys>
+ probe apply(std::index_sequence<I>, int sofar, lua_State* L, Keys&& keys, int tableindex) {
+ get_field<(I < 1) && b, raw>(L, std::get<I>(keys), tableindex);
+ return probe(check<P>(L), sofar);
+ }
+
+ template <std::size_t I, std::size_t I1, std::size_t... In, typename Keys>
+ probe apply(std::index_sequence<I, I1, In...>, int sofar, lua_State* L, Keys&& keys, int tableindex) {
+ get_field < I<1 && b, raw>(L, std::get<I>(keys), tableindex);
+ if (!maybe_indexable(L)) {
+ return probe(false, sofar);
+ }
+ return apply(std::index_sequence<I1, In...>(), sofar + 1, L, std::forward<Keys>(keys), -1);
+ }
+
+ template <typename Keys>
+ probe get(lua_State* L, Keys&& keys, int tableindex = -2) {
+ if constexpr (!b) {
+ if (!maybe_indexable(L, tableindex)) {
+ return probe(false, 0);
+ }
+ return apply(std::index_sequence_for<Args...>(), 1, L, std::forward<Keys>(keys), tableindex);
+ }
+ else {
+ return apply(std::index_sequence_for<Args...>(), 1, L, std::forward<Keys>(keys), tableindex);
+ }
+ }
+ };
+}} // namespace sol::stack
+
+// end of sol/stack_probe.hpp
+
+#include <cstring>
+#include <array>
+
+namespace sol {
+ namespace detail {
+ using typical_chunk_name_t = char[SOL_ID_SIZE_I_];
+ using typical_file_chunk_name_t = char[SOL_FILE_ID_SIZE_I_];
+
+ inline const std::string& default_chunk_name() {
+ static const std::string name = "";
+ return name;
+ }
+
+ template <std::size_t N>
+ const char* make_chunk_name(const string_view& code, const std::string& chunkname, char (&basechunkname)[N]) {
+ if (chunkname.empty()) {
+ auto it = code.cbegin();
+ auto e = code.cend();
+ std::size_t i = 0;
+ static const std::size_t n = N - 4;
+ for (i = 0; i < n && it != e; ++i, ++it) {
+ basechunkname[i] = *it;
+ }
+ if (it != e) {
+ for (std::size_t c = 0; c < 3; ++i, ++c) {
+ basechunkname[i] = '.';
+ }
+ }
+ basechunkname[i] = '\0';
+ return &basechunkname[0];
+ }
+ else {
+ return chunkname.c_str();
+ }
+ }
+
+ inline void clear_entries(stack_reference r) {
+ stack::push(r.lua_state(), lua_nil);
+ while (lua_next(r.lua_state(), -2)) {
+ absolute_index key(r.lua_state(), -2);
+ auto pn = stack::pop_n(r.lua_state(), 1);
+ stack::set_field<false, true>(r.lua_state(), key, lua_nil, r.stack_index());
+ }
+ }
+
+ inline void clear_entries(const reference& registry_reference) {
+ auto pp = stack::push_pop(registry_reference);
+ stack_reference ref(registry_reference.lua_state(), -1);
+ clear_entries(ref);
+ }
+ } // namespace detail
+
+ namespace stack {
+ namespace stack_detail {
+ template <typename T>
+ inline int push_as_upvalues(lua_State* L, T& item) {
+ typedef std::decay_t<T> TValue;
+ static const std::size_t itemsize = sizeof(TValue);
+ static const std::size_t voidsize = sizeof(void*);
+ static const std::size_t voidsizem1 = voidsize - 1;
+ static const std::size_t data_t_count = (sizeof(TValue) + voidsizem1) / voidsize;
+ typedef std::array<void*, data_t_count> data_t;
+
+ data_t data { {} };
+ std::memcpy(&data[0], std::addressof(item), itemsize);
+ int pushcount = 0;
+ for (const auto& v : data) {
+ lua_pushlightuserdata(L, v);
+ pushcount += 1;
+ }
+ return pushcount;
+ }
+
+ template <typename T>
+ inline std::pair<T, int> get_as_upvalues(lua_State* L, int index = 2) {
+ static const std::size_t data_t_count = (sizeof(T) + (sizeof(void*) - 1)) / sizeof(void*);
+ typedef std::array<void*, data_t_count> data_t;
+ data_t voiddata { {} };
+ for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) {
+ voiddata[i] = lua_touserdata(L, upvalue_index(index++));
+ }
+ return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
+ }
+
+ template <typename T>
+ inline std::pair<T, int> get_as_upvalues_using_function(lua_State* L, int function_index = -1) {
+ static const std::size_t data_t_count = (sizeof(T) + (sizeof(void*) - 1)) / sizeof(void*);
+ typedef std::array<void*, data_t_count> data_t;
+ function_index = lua_absindex(L, function_index);
+ int index = 0;
+ data_t voiddata { {} };
+ for (std::size_t d = 0; d < sizeof(T); d += sizeof(void*)) {
+ // first upvalue is nullptr to respect environment shenanigans
+ // So +2 instead of +1
+ const char* upvalue_name = lua_getupvalue(L, function_index, index + 2);
+ if (upvalue_name == nullptr) {
+ // We should freak out here...
+ break;
+ }
+ voiddata[index] = lua_touserdata(L, -1);
+ ++index;
+ }
+ lua_pop(L, index);
+ return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
+ }
+
+ template <bool checked, typename Handler, typename Fx, typename... Args>
+ static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, Handler&&, record&, Fx&& fx, Args&&... args) {
+ return std::forward<Fx>(fx)(std::forward<Args>(args)...);
+ }
+
+ template <bool checked, typename Arg, typename... Args, std::size_t I, std::size_t... Is, typename Handler, typename Fx, typename... FxArgs>
+ static decltype(auto) eval(types<Arg, Args...>, std::index_sequence<I, Is...>, lua_State* L_, int start_index_, Handler&& handler_,
+ record& tracking_, Fx&& fx_, FxArgs&&... fxargs_) {
+#if 0 && SOL_IS_ON(SOL_PROPAGATE_EXCEPTIONS)
+ // NOTE: THIS IS TERMPORARILY TURNED OFF BECAUSE IT IMPACTS ACTUAL SEMANTICS W.R.T. THINGS LIKE LUAJIT,
+ // SO IT MUST REMAIN OFF UNTIL WE CAN ESTABLISH SIMILAR BEHAVIOR IN MODES WHERE `checked == false`!
+
+ // We can save performance/time by letting errors unwind produced arguments
+ // rather than checking everything once, and then potentially re-doing work
+ if constexpr (checked) {
+ return eval<checked>(types<Args...>(),
+ std::index_sequence<Is...>(),
+ L_,
+ start_index_,
+ std::forward<Handler>(handler_),
+ tracking_,
+ std::forward<Fx>(fx_),
+ std::forward<FxArgs>(fxargs_)...,
+ *stack_detail::check_get_arg<Arg>(L_, start_index_ + tracking_.used, handler_, tracking_));
+ }
+ else
+#endif
+ {
+ return eval<checked>(types<Args...>(),
+ std::index_sequence<Is...>(),
+ L_,
+ start_index_,
+ std::forward<Handler>(handler_),
+ tracking_,
+ std::forward<Fx>(fx_),
+ std::forward<FxArgs>(fxargs_)...,
+ stack_detail::unchecked_get_arg<Arg>(L_, start_index_ + tracking_.used, tracking_));
+ }
+ }
+
+ template <bool checkargs = detail::default_safe_function_calls, std::size_t... I, typename R, typename... Args, typename Fx, typename... FxArgs>
+ inline decltype(auto) call(types<R>, types<Args...> argument_types_, std::index_sequence<I...> argument_indices_, lua_State* L_,
+ int start_index_, Fx&& fx_, FxArgs&&... args_) {
+ static_assert(meta::all_v<meta::is_not_move_only<Args>...>,
+ "One of the arguments being bound is a move-only type, and it is not being taken by reference: this will break your code. Please take "
+ "a reference and std::move it manually if this was your intention.");
+ argument_handler<types<R, Args...>> handler {};
+ record tracking {};
+#if SOL_IS_OFF(SOL_PROPAGATE_EXCEPTIONS)
+ if constexpr (checkargs) {
+ multi_check<Args...>(L_, start_index_, handler);
+ }
+#endif
+ if constexpr (std::is_void_v<R>) {
+ eval<checkargs>(
+ argument_types_, argument_indices_, L_, start_index_, handler, tracking, std::forward<Fx>(fx_), std::forward<FxArgs>(args_)...);
+ }
+ else {
+ return eval<checkargs>(
+ argument_types_, argument_indices_, L_, start_index_, handler, tracking, std::forward<Fx>(fx_), std::forward<FxArgs>(args_)...);
+ }
+ }
+
+ template <typename T>
+ void raw_table_set(lua_State* L, T&& arg, int tableindex = -2) {
+ int push_count = push(L, std::forward<T>(arg));
+ SOL_ASSERT(push_count == 1);
+ std::size_t unique_index = static_cast<std::size_t>(luaL_len(L, tableindex) + 1u);
+ lua_rawseti(L, tableindex, static_cast<int>(unique_index));
+ }
+
+ } // namespace stack_detail
+
+ template <typename T>
+ int set_ref(lua_State* L, T&& arg, int tableindex = -2) {
+ int push_count = push(L, std::forward<T>(arg));
+ SOL_ASSERT(push_count == 1);
+ return luaL_ref(L, tableindex);
+ }
+
+ template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
+ inline decltype(auto) call(types<R> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
+ using args_indices = std::make_index_sequence<sizeof...(Args)>;
+ if constexpr (std::is_void_v<R>) {
+ stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
+ }
+ else {
+ return stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
+ }
+ }
+
+ template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
+ inline decltype(auto) call(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
+ if constexpr (std::is_void_v<R>) {
+ call<check_args>(tr, ta, L, 1, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
+ }
+ else {
+ return call<check_args>(tr, ta, L, 1, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
+ }
+ }
+
+ template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
+ inline decltype(auto) call_from_top(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
+ using expected_count_t = meta::count_for_pack<lua_size, Args...>;
+ if constexpr (std::is_void_v<R>) {
+ call<check_args>(tr,
+ ta,
+ L,
+ (std::max)(static_cast<int>(lua_gettop(L) - expected_count_t::value), static_cast<int>(0)),
+ std::forward<Fx>(fx),
+ std::forward<FxArgs>(args)...);
+ }
+ else {
+ return call<check_args>(tr,
+ ta,
+ L,
+ (std::max)(static_cast<int>(lua_gettop(L) - expected_count_t::value), static_cast<int>(0)),
+ std::forward<Fx>(fx),
+ std::forward<FxArgs>(args)...);
+ }
+ }
+
+ template <bool check_args = detail::default_safe_function_calls, bool clean_stack = true, typename Ret0, typename... Ret, typename... Args,
+ typename Fx, typename... FxArgs>
+ inline int call_into_lua(types<Ret0, Ret...> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) {
+ if constexpr (std::is_void_v<Ret0>) {
+ call<check_args>(tr, ta, L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
+ if constexpr (clean_stack) {
+ lua_settop(L, 0);
+ }
+ return 0;
+ }
+ else {
+ (void)tr;
+ decltype(auto) r
+ = call<check_args>(types<meta::return_type_t<Ret0, Ret...>>(), ta, L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
+ using R = meta::unqualified_t<decltype(r)>;
+ using is_stack = meta::any<is_stack_based<R>, std::is_same<R, absolute_index>, std::is_same<R, ref_index>, std::is_same<R, raw_index>>;
+ if constexpr (clean_stack && !is_stack::value) {
+ lua_settop(L, 0);
+ }
+ return push_reference(L, std::forward<decltype(r)>(r));
+ }
+ }
+
+ template <bool check_args = detail::default_safe_function_calls, bool clean_stack = true, typename Fx, typename... FxArgs>
+ inline int call_lua(lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) {
+ using traits_type = lua_bind_traits<meta::unqualified_t<Fx>>;
+ using args_list = typename traits_type::args_list;
+ using returns_list = typename traits_type::returns_list;
+ return call_into_lua<check_args, clean_stack>(returns_list(), args_list(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
+ }
+
+ inline call_syntax get_call_syntax(lua_State* L, const string_view& key, int index) {
+ if (lua_gettop(L) < 1) {
+ return call_syntax::dot;
+ }
+ luaL_getmetatable(L, key.data());
+ auto pn = pop_n(L, 1);
+ if (lua_compare(L, -1, index, LUA_OPEQ) != 1) {
+ return call_syntax::dot;
+ }
+ return call_syntax::colon;
+ }
+
+ inline void script(
+ lua_State* L, lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
+ if (lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ }
+
+ inline void script(
+ lua_State* L, const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
+ if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ }
+
+ inline void script_file(lua_State* L, const std::string& filename, load_mode mode = load_mode::any) {
+ if (luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ }
+
+ inline void luajit_exception_handler(lua_State* L, int (*handler)(lua_State*, lua_CFunction) = detail::c_trampoline) {
+#if SOL_IS_ON(SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE)
+ if (L == nullptr) {
+ return;
+ }
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlightuserdata(L, (void*)handler);
+ auto pn = pop_n(L, 1);
+ luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
+#else
+ (void)L;
+ (void)handler;
+#endif
+ }
+
+ inline void luajit_exception_off(lua_State* L) {
+#if SOL_IS_ON(SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE)
+ if (L == nullptr) {
+ return;
+ }
+ luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_OFF);
+#else
+ (void)L;
+#endif
+ }
+ } // namespace stack
+} // namespace sol
+
+// end of sol/stack.hpp
+
+// beginning of sol/object.hpp
+
+// beginning of sol/make_reference.hpp
+
+namespace sol {
+
+ template <typename R = reference, bool should_pop = !is_stack_based_v<R>, typename T>
+ R make_reference(lua_State* L, T&& value) {
+ int backpedal = stack::push(L, std::forward<T>(value));
+ R r = stack::get<R>(L, -backpedal);
+ if (should_pop) {
+ lua_pop(L, backpedal);
+ }
+ return r;
+ }
+
+ template <typename T, typename R = reference, bool should_pop = !is_stack_based_v<R>, typename... Args>
+ R make_reference(lua_State* L, Args&&... args) {
+ int backpedal = stack::push<T>(L, std::forward<Args>(args)...);
+ R r = stack::get<R>(L, -backpedal);
+ if (should_pop) {
+ lua_pop(L, backpedal);
+ }
+ return r;
+ }
+
+ template <typename R = reference, bool should_pop = !is_stack_based_v<R>, typename T>
+ R make_reference_userdata(lua_State* L, T&& value) {
+ int backpedal = stack::push_userdata(L, std::forward<T>(value));
+ R r = stack::get<R>(L, -backpedal);
+ if (should_pop) {
+ lua_pop(L, backpedal);
+ }
+ return r;
+ }
+
+ template <typename T, typename R = reference, bool should_pop = !is_stack_based_v<R>, typename... Args>
+ R make_reference_userdata(lua_State* L, Args&&... args) {
+ int backpedal = stack::push_userdata<T>(L, std::forward<Args>(args)...);
+ R r = stack::get<R>(L, -backpedal);
+ if (should_pop) {
+ lua_pop(L, backpedal);
+ }
+ return r;
+ }
+
+} // namespace sol
+
+// end of sol/make_reference.hpp
+
+// beginning of sol/object_base.hpp
+
+namespace sol {
+
+ template <typename ref_t>
+ class basic_object_base : public ref_t {
+ private:
+ using base_t = ref_t;
+
+ template <typename T>
+ decltype(auto) as_stack(std::true_type) const {
+ return stack::get<T>(base_t::lua_state(), base_t::stack_index());
+ }
+
+ template <typename T>
+ decltype(auto) as_stack(std::false_type) const {
+ base_t::push();
+ return stack::pop<T>(base_t::lua_state());
+ }
+
+ template <typename T>
+ bool is_stack(std::true_type) const {
+ return stack::check<T>(base_t::lua_state(), base_t::stack_index(), &no_panic);
+ }
+
+ template <typename T>
+ bool is_stack(std::false_type) const {
+ int r = base_t::registry_index();
+ if (r == LUA_REFNIL)
+ return meta::any_same<meta::unqualified_t<T>, lua_nil_t, nullopt_t, std::nullptr_t>::value ? true : false;
+ if (r == LUA_NOREF)
+ return false;
+ auto pp = stack::push_pop(*this);
+ return stack::check<T>(base_t::lua_state(), -1, &no_panic);
+ }
+
+ public:
+ basic_object_base() noexcept = default;
+ basic_object_base(const basic_object_base&) = default;
+ basic_object_base(basic_object_base&&) = default;
+ basic_object_base& operator=(const basic_object_base&) = default;
+ basic_object_base& operator=(basic_object_base&&) = default;
+ template <typename T, typename... Args, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_object_base>>> = meta::enabler>
+ basic_object_base(T&& arg, Args&&... args) : base_t(std::forward<T>(arg), std::forward<Args>(args)...) {
+ }
+
+ template <typename T>
+ decltype(auto) as() const {
+ return as_stack<T>(is_stack_based<base_t>());
+ }
+
+ template <typename T>
+ bool is() const {
+ return is_stack<T>(is_stack_based<base_t>());
+ }
+ };
+} // namespace sol
+
+// end of sol/object_base.hpp
+
+namespace sol {
+
+ template <typename base_type>
+ class basic_object : public basic_object_base<base_type> {
+ private:
+ typedef basic_object_base<base_type> base_t;
+
+ template <bool invert_and_pop = false>
+ basic_object(std::integral_constant<bool, invert_and_pop>, lua_State* L_, int index_ = -1) noexcept : base_t(L_, index_) {
+ if (invert_and_pop) {
+ lua_pop(L_, -index_);
+ }
+ }
+
+ protected:
+ basic_object(detail::no_safety_tag, lua_nil_t n) : base_t(n) {
+ }
+ basic_object(detail::no_safety_tag, lua_State* L_, int index_) : base_t(L_, index_) {
+ }
+ basic_object(detail::no_safety_tag, lua_State* L_, ref_index index_) : base_t(L_, index_) {
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_object>>, meta::neg<std::is_same<base_type, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_object(detail::no_safety_tag, T&& r) noexcept : base_t(std::forward<T>(r)) {
+ }
+
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_object(detail::no_safety_tag, lua_State* L_, T&& r) noexcept : base_t(L_, std::forward<T>(r)) {
+ }
+
+ public:
+ basic_object() noexcept = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_object>>, meta::neg<std::is_same<base_type, stack_reference>>,
+ is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_object(T&& r) : base_t(std::forward<T>(r)) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_object(lua_State* L_, T&& r) : base_t(L_, std::forward<T>(r)) {
+ }
+ basic_object(lua_State* L_, global_tag_t t) : base_t(L_, t) {
+ }
+ basic_object(lua_nil_t r) : base_t(r) {
+ }
+ basic_object(const basic_object&) = default;
+ basic_object(basic_object&&) = default;
+ basic_object(const stack_reference& r) noexcept : basic_object(r.lua_state(), r.stack_index()) {
+ }
+ basic_object(stack_reference&& r) noexcept : basic_object(r.lua_state(), r.stack_index()) {
+ }
+ template <typename Super>
+ basic_object(const proxy_base<Super>& r) noexcept : basic_object(r.operator basic_object()) {
+ }
+ template <typename Super>
+ basic_object(proxy_base<Super>&& r) noexcept : basic_object(r.operator basic_object()) {
+ }
+ basic_object(lua_State* L_, lua_nil_t r) noexcept : base_t(L_, r) {
+ }
+ basic_object(lua_State* L_, int index_ = -1) noexcept : base_t(L_, index_) {
+ }
+ basic_object(lua_State* L_, absolute_index index_) noexcept : base_t(L_, index_) {
+ }
+ basic_object(lua_State* L_, raw_index index_) noexcept : base_t(L_, index_) {
+ }
+ basic_object(lua_State* L_, ref_index index_) noexcept : base_t(L_, index_) {
+ }
+ template <typename T, typename... Args>
+ basic_object(lua_State* L_, in_place_type_t<T>, Args&&... args) noexcept
+ : basic_object(std::integral_constant<bool, !is_stack_based<base_t>::value>(), L_, -stack::push<T>(L_, std::forward<Args>(args)...)) {
+ }
+ template <typename T, typename... Args>
+ basic_object(lua_State* L_, in_place_t, T&& arg, Args&&... args) noexcept
+ : basic_object(L_, in_place_type<T>, std::forward<T>(arg), std::forward<Args>(args)...) {
+ }
+ basic_object& operator=(const basic_object&) = default;
+ basic_object& operator=(basic_object&&) = default;
+ basic_object& operator=(const base_type& b) {
+ base_t::operator=(b);
+ return *this;
+ }
+ basic_object& operator=(base_type&& b) {
+ base_t::operator=(std::move(b));
+ return *this;
+ }
+ template <typename Super>
+ basic_object& operator=(const proxy_base<Super>& r) {
+ this->operator=(r.operator basic_object());
+ return *this;
+ }
+ template <typename Super>
+ basic_object& operator=(proxy_base<Super>&& r) {
+ this->operator=(r.operator basic_object());
+ return *this;
+ }
+ };
+
+ template <typename T>
+ object make_object(lua_State* L_, T&& value) {
+ return make_reference<object, true>(L_, std::forward<T>(value));
+ }
+
+ template <typename T, typename... Args>
+ object make_object(lua_State* L_, Args&&... args) {
+ return make_reference<T, object, true>(L_, std::forward<Args>(args)...);
+ }
+
+ template <typename T>
+ object make_object_userdata(lua_State* L_, T&& value) {
+ return make_reference_userdata<object, true>(L_, std::forward<T>(value));
+ }
+
+ template <typename T, typename... Args>
+ object make_object_userdata(lua_State* L_, Args&&... args) {
+ return make_reference_userdata<T, object, true>(L_, std::forward<Args>(args)...);
+ }
+} // namespace sol
+
+// end of sol/object.hpp
+
+// beginning of sol/function.hpp
+
+// beginning of sol/unsafe_function.hpp
+
+// beginning of sol/function_result.hpp
+
+// beginning of sol/protected_function_result.hpp
+
+// beginning of sol/proxy_base.hpp
+
+namespace sol {
+ struct proxy_base_tag { };
+
+ namespace detail {
+ template <typename T>
+ using proxy_key_t = meta::conditional_t<meta::is_specialization_of_v<meta::unqualified_t<T>, std::tuple>, T,
+ std::tuple<meta::conditional_t<std::is_array_v<meta::unqualified_t<T>>, std::remove_reference_t<T>&, meta::unqualified_t<T>>>>;
+ }
+
+ template <typename Super>
+ struct proxy_base : public proxy_base_tag {
+ lua_State* lua_state() const {
+ const Super& super = *static_cast<const Super*>(static_cast<const void*>(this));
+ return super.lua_state();
+ }
+
+ operator std::string() const {
+ const Super& super = *static_cast<const Super*>(static_cast<const void*>(this));
+ return super.template get<std::string>();
+ }
+
+ template <typename T, meta::enable<meta::neg<meta::is_string_constructible<T>>, is_proxy_primitive<meta::unqualified_t<T>>> = meta::enabler>
+ operator T() const {
+ const Super& super = *static_cast<const Super*>(static_cast<const void*>(this));
+ return super.template get<T>();
+ }
+
+ template <typename T,
+ meta::enable<meta::neg<meta::is_string_constructible<T>>, meta::neg<is_proxy_primitive<meta::unqualified_t<T>>>> = meta::enabler>
+ operator T&() const {
+ const Super& super = *static_cast<const Super*>(static_cast<const void*>(this));
+ return super.template get<T&>();
+ }
+ };
+
+} // namespace sol
+
+// end of sol/proxy_base.hpp
+
+// beginning of sol/stack_iterator.hpp
+
+#include <limits>
+#include <iterator>
+
+namespace sol {
+ template <typename proxy_t, bool is_const>
+ struct stack_iterator {
+ typedef meta::conditional_t<is_const, const proxy_t, proxy_t> reference;
+ typedef meta::conditional_t<is_const, const proxy_t*, proxy_t*> pointer;
+ typedef proxy_t value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::random_access_iterator_tag iterator_category;
+ lua_State* L;
+ int index;
+ int stacktop;
+ proxy_t sp;
+
+ stack_iterator() : L(nullptr), index((std::numeric_limits<int>::max)()), stacktop((std::numeric_limits<int>::max)()), sp() {
+ }
+ stack_iterator(const stack_iterator<proxy_t, true>& r) : L(r.L), index(r.index), stacktop(r.stacktop), sp(r.sp) {
+ }
+ stack_iterator(lua_State* luastate, int idx, int topidx) : L(luastate), index(idx), stacktop(topidx), sp(luastate, idx) {
+ }
+
+ reference operator*() {
+ return proxy_t(L, index);
+ }
+
+ reference operator*() const {
+ return proxy_t(L, index);
+ }
+
+ pointer operator->() {
+ sp = proxy_t(L, index);
+ return &sp;
+ }
+
+ pointer operator->() const {
+ const_cast<proxy_t&>(sp) = proxy_t(L, index);
+ return &sp;
+ }
+
+ stack_iterator& operator++() {
+ ++index;
+ return *this;
+ }
+
+ stack_iterator operator++(int) {
+ auto r = *this;
+ this->operator++();
+ return r;
+ }
+
+ stack_iterator& operator--() {
+ --index;
+ return *this;
+ }
+
+ stack_iterator operator--(int) {
+ auto r = *this;
+ this->operator--();
+ return r;
+ }
+
+ stack_iterator& operator+=(difference_type idx) {
+ index += static_cast<int>(idx);
+ return *this;
+ }
+
+ stack_iterator& operator-=(difference_type idx) {
+ index -= static_cast<int>(idx);
+ return *this;
+ }
+
+ difference_type operator-(const stack_iterator& r) const {
+ return index - r.index;
+ }
+
+ stack_iterator operator+(difference_type idx) const {
+ stack_iterator r = *this;
+ r += idx;
+ return r;
+ }
+
+ reference operator[](difference_type idx) const {
+ return proxy_t(L, index + static_cast<int>(idx));
+ }
+
+ bool operator==(const stack_iterator& r) const {
+ if (stacktop == (std::numeric_limits<int>::max)()) {
+ return r.index == r.stacktop;
+ }
+ else if (r.stacktop == (std::numeric_limits<int>::max)()) {
+ return index == stacktop;
+ }
+ return index == r.index;
+ }
+
+ bool operator!=(const stack_iterator& r) const {
+ return !(this->operator==(r));
+ }
+
+ bool operator<(const stack_iterator& r) const {
+ return index < r.index;
+ }
+
+ bool operator>(const stack_iterator& r) const {
+ return index > r.index;
+ }
+
+ bool operator<=(const stack_iterator& r) const {
+ return index <= r.index;
+ }
+
+ bool operator>=(const stack_iterator& r) const {
+ return index >= r.index;
+ }
+ };
+
+ template <typename proxy_t, bool is_const>
+ inline stack_iterator<proxy_t, is_const> operator+(
+ typename stack_iterator<proxy_t, is_const>::difference_type n, const stack_iterator<proxy_t, is_const>& r) {
+ return r + n;
+ }
+} // namespace sol
+
+// end of sol/stack_iterator.hpp
+
+// beginning of sol/stack_proxy.hpp
+
+// beginning of sol/stack_proxy_base.hpp
+
+namespace sol {
+ struct stack_proxy_base : public proxy_base<stack_proxy_base> {
+ private:
+ lua_State* m_L;
+ int m_index;
+
+ public:
+ stack_proxy_base() : m_L(nullptr), m_index(0) {
+ }
+ stack_proxy_base(lua_State* L_, int index_) : m_L(L_), m_index(index_) {
+ }
+
+ template <typename T>
+ decltype(auto) get() const {
+ return stack::get<T>(m_L, stack_index());
+ }
+
+ template <typename T>
+ bool is() const {
+ return stack::check<T>(m_L, stack_index());
+ }
+
+ template <typename T>
+ decltype(auto) as() const {
+ return get<T>();
+ }
+
+ type get_type() const noexcept {
+ return type_of(lua_state(), stack_index());
+ }
+
+ int push() const {
+ return push(m_L);
+ }
+
+ int push(lua_State* L_) const {
+ lua_pushvalue(L_, m_index);
+ return 1;
+ }
+
+ lua_State* lua_state() const {
+ return m_L;
+ }
+ int stack_index() const {
+ return m_index;
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_getter<stack_proxy_base> {
+ static stack_proxy_base get(lua_State* L_, int index_ = -1) {
+ return stack_proxy_base(L_, index_);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<stack_proxy_base> {
+ static int push(lua_State*, const stack_proxy_base& proxy_reference) {
+ return proxy_reference.push();
+ }
+ };
+ } // namespace stack
+
+} // namespace sol
+
+// end of sol/stack_proxy_base.hpp
+
+namespace sol {
+ struct stack_proxy : public stack_proxy_base {
+ public:
+ stack_proxy() : stack_proxy_base() {
+ }
+ stack_proxy(lua_State* L, int index) : stack_proxy_base(L, index) {
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args);
+
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) {
+ return call<>(std::forward<Args>(args)...);
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_getter<stack_proxy> {
+ static stack_proxy get(lua_State* L, int index, record& tracking) {
+ tracking.use(0);
+ return stack_proxy(L, index);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<stack_proxy> {
+ static int push(lua_State*, const stack_proxy& ref) {
+ return ref.push();
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/stack_proxy.hpp
+
+#include <cstdint>
+
+namespace sol {
+ struct protected_function_result : public proxy_base<protected_function_result> {
+ private:
+ lua_State* L;
+ int index;
+ int returncount;
+ int popcount;
+ call_status err;
+
+ public:
+ typedef stack_proxy reference_type;
+ typedef stack_proxy value_type;
+ typedef stack_proxy* pointer;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::size_t size_type;
+ typedef stack_iterator<stack_proxy, false> iterator;
+ typedef stack_iterator<stack_proxy, true> const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ protected_function_result() noexcept : protected_function_result(nullptr) {}
+ protected_function_result(lua_State* Ls, int idx = -1, int retnum = 0, int popped = 0, call_status pferr = call_status::ok) noexcept
+ : L(Ls), index(idx), returncount(retnum), popcount(popped), err(pferr) {
+ }
+
+ // We do not want anyone to copy these around willy-nilly
+ // Will likely break people, but also will probably get rid of quiet bugs that have
+ // been lurking. (E.g., Vanilla Lua will just quietly discard over-pops and under-pops:
+ // LuaJIT and other Lua engines will implode and segfault at random later times.)
+ protected_function_result(const protected_function_result&) = delete;
+ protected_function_result& operator=(const protected_function_result&) = delete;
+
+ protected_function_result(protected_function_result&& o) noexcept
+ : L(o.L), index(o.index), returncount(o.returncount), popcount(o.popcount), err(o.err) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ }
+ protected_function_result& operator=(protected_function_result&& o) noexcept {
+ L = o.L;
+ index = o.index;
+ returncount = o.returncount;
+ popcount = o.popcount;
+ err = o.err;
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ return *this;
+ }
+
+ protected_function_result(const unsafe_function_result& o) = delete;
+ protected_function_result& operator=(const unsafe_function_result& o) = delete;
+ protected_function_result(unsafe_function_result&& o) noexcept;
+ protected_function_result& operator=(unsafe_function_result&& o) noexcept;
+
+ call_status status() const noexcept {
+ return err;
+ }
+
+ bool valid() const noexcept {
+ return status() == call_status::ok || status() == call_status::yielded;
+ }
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic push
+#if !SOL_IS_ON(SOL_COMPILER_CLANG)
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+#endif
+
+ template <typename T>
+ decltype(auto) get(int index_offset = 0) const {
+ using UT = meta::unqualified_t<T>;
+ int target = index + index_offset;
+ if constexpr (meta::is_optional_v<UT>) {
+ using ValueType = typename UT::value_type;
+ if constexpr (std::is_same_v<ValueType, error>) {
+ if (valid()) {
+ return UT();
+ }
+ return UT(error(detail::direct_error, stack::get<std::string>(L, target)));
+ }
+ else {
+ if (!valid()) {
+ return UT();
+ }
+ return stack::get<UT>(L, target);
+ }
+ }
+ else {
+ if constexpr (std::is_same_v<T, error>) {
+#if SOL_IS_ON(SOL_SAFE_PROXIES)
+ if (valid()) {
+ type t = type_of(L, target);
+ type_panic_c_str(L, target, t, type::none, "bad get from protected_function_result (is an error)");
+ }
+#endif // Check Argument Safety
+ return error(detail::direct_error, stack::get<std::string>(L, target));
+ }
+ else {
+#if SOL_IS_ON(SOL_SAFE_PROXIES)
+ if (!valid()) {
+ type t = type_of(L, target);
+ type_panic_c_str(L, target, t, type::none, "bad get from protected_function_result (is not an error)");
+ }
+#endif // Check Argument Safety
+ return stack::get<T>(L, target);
+ }
+ }
+ }
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic pop
+#endif
+
+ type get_type(int index_offset = 0) const noexcept {
+ return type_of(L, index + static_cast<int>(index_offset));
+ }
+
+ stack_proxy operator[](difference_type index_offset) const {
+ return stack_proxy(L, index + static_cast<int>(index_offset));
+ }
+
+ iterator begin() {
+ return iterator(L, index, stack_index() + return_count());
+ }
+ iterator end() {
+ return iterator(L, stack_index() + return_count(), stack_index() + return_count());
+ }
+ const_iterator begin() const {
+ return const_iterator(L, index, stack_index() + return_count());
+ }
+ const_iterator end() const {
+ return const_iterator(L, stack_index() + return_count(), stack_index() + return_count());
+ }
+ const_iterator cbegin() const {
+ return begin();
+ }
+ const_iterator cend() const {
+ return end();
+ }
+
+ reverse_iterator rbegin() {
+ return std::reverse_iterator<iterator>(begin());
+ }
+ reverse_iterator rend() {
+ return std::reverse_iterator<iterator>(end());
+ }
+ const_reverse_iterator rbegin() const {
+ return std::reverse_iterator<const_iterator>(begin());
+ }
+ const_reverse_iterator rend() const {
+ return std::reverse_iterator<const_iterator>(end());
+ }
+ const_reverse_iterator crbegin() const {
+ return std::reverse_iterator<const_iterator>(cbegin());
+ }
+ const_reverse_iterator crend() const {
+ return std::reverse_iterator<const_iterator>(cend());
+ }
+
+ lua_State* lua_state() const noexcept {
+ return L;
+ };
+ int stack_index() const noexcept {
+ return index;
+ };
+ int return_count() const noexcept {
+ return returncount;
+ };
+ int pop_count() const noexcept {
+ return popcount;
+ };
+ void abandon() noexcept {
+ // L = nullptr;
+ index = 0;
+ returncount = 0;
+ popcount = 0;
+ err = call_status::runtime;
+ }
+ ~protected_function_result() {
+ if (L == nullptr)
+ return;
+ stack::remove(L, index, popcount);
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_pusher<protected_function_result> {
+ static int push(lua_State* L, const protected_function_result& pfr) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, static_cast<int>(pfr.pop_count()), detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ int p = 0;
+ for (int i = 0; i < pfr.pop_count(); ++i) {
+ lua_pushvalue(L, i + pfr.stack_index());
+ ++p;
+ }
+ return p;
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/protected_function_result.hpp
+
+// beginning of sol/unsafe_function_result.hpp
+
+#include <cstdint>
+
+namespace sol {
+ struct unsafe_function_result : public proxy_base<unsafe_function_result> {
+ private:
+ lua_State* L;
+ int index;
+ int returncount;
+
+ public:
+ typedef stack_proxy reference_type;
+ typedef stack_proxy value_type;
+ typedef stack_proxy* pointer;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::size_t size_type;
+ typedef stack_iterator<stack_proxy, false> iterator;
+ typedef stack_iterator<stack_proxy, true> const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ unsafe_function_result() noexcept : unsafe_function_result(nullptr) {}
+ unsafe_function_result(lua_State* Ls, int idx = -1, int retnum = 0) noexcept : L(Ls), index(idx), returncount(retnum) {
+ }
+
+ // We do not want anyone to copy these around willy-nilly
+ // Will likely break people, but also will probably get rid of quiet bugs that have
+ // been lurking. (E.g., Vanilla Lua will just quietly discard over-pops and under-pops:
+ // LuaJIT and other Lua engines will implode and segfault at random later times.)
+ unsafe_function_result(const unsafe_function_result&) = delete;
+ unsafe_function_result& operator=(const unsafe_function_result&) = delete;
+
+ unsafe_function_result(unsafe_function_result&& o) noexcept : L(o.L), index(o.index), returncount(o.returncount) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but will be thorough
+ o.abandon();
+ }
+ unsafe_function_result& operator=(unsafe_function_result&& o) noexcept {
+ L = o.L;
+ index = o.index;
+ returncount = o.returncount;
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but will be thorough
+ o.abandon();
+ return *this;
+ }
+
+ unsafe_function_result(const protected_function_result& o) = delete;
+ unsafe_function_result& operator=(const protected_function_result& o) = delete;
+ unsafe_function_result(protected_function_result&& o) noexcept;
+ unsafe_function_result& operator=(protected_function_result&& o) noexcept;
+
+ template <typename T>
+ decltype(auto) get(difference_type index_offset = 0) const {
+ return stack::get<T>(L, index + static_cast<int>(index_offset));
+ }
+
+ type get_type(difference_type index_offset = 0) const noexcept {
+ return type_of(L, index + static_cast<int>(index_offset));
+ }
+
+ stack_proxy operator[](difference_type index_offset) const {
+ return stack_proxy(L, index + static_cast<int>(index_offset));
+ }
+
+ iterator begin() {
+ return iterator(L, index, stack_index() + return_count());
+ }
+ iterator end() {
+ return iterator(L, stack_index() + return_count(), stack_index() + return_count());
+ }
+ const_iterator begin() const {
+ return const_iterator(L, index, stack_index() + return_count());
+ }
+ const_iterator end() const {
+ return const_iterator(L, stack_index() + return_count(), stack_index() + return_count());
+ }
+ const_iterator cbegin() const {
+ return begin();
+ }
+ const_iterator cend() const {
+ return end();
+ }
+
+ reverse_iterator rbegin() {
+ return std::reverse_iterator<iterator>(begin());
+ }
+ reverse_iterator rend() {
+ return std::reverse_iterator<iterator>(end());
+ }
+ const_reverse_iterator rbegin() const {
+ return std::reverse_iterator<const_iterator>(begin());
+ }
+ const_reverse_iterator rend() const {
+ return std::reverse_iterator<const_iterator>(end());
+ }
+ const_reverse_iterator crbegin() const {
+ return std::reverse_iterator<const_iterator>(cbegin());
+ }
+ const_reverse_iterator crend() const {
+ return std::reverse_iterator<const_iterator>(cend());
+ }
+
+ call_status status() const noexcept {
+ return call_status::ok;
+ }
+
+ bool valid() const noexcept {
+ return status() == call_status::ok || status() == call_status::yielded;
+ }
+
+ lua_State* lua_state() const {
+ return L;
+ };
+ int stack_index() const {
+ return index;
+ };
+ int return_count() const {
+ return returncount;
+ };
+ void abandon() noexcept {
+ // L = nullptr;
+ index = 0;
+ returncount = 0;
+ }
+ ~unsafe_function_result() {
+ if (L != nullptr) {
+ lua_pop(L, returncount);
+ }
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_pusher<unsafe_function_result> {
+ static int push(lua_State* L, const unsafe_function_result& fr) {
+ int p = 0;
+ for (int i = 0; i < fr.return_count(); ++i) {
+ lua_pushvalue(L, i + fr.stack_index());
+ ++p;
+ }
+ return p;
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/unsafe_function_result.hpp
+
+#include <cstdint>
+
+namespace sol {
+
+ namespace detail {
+ template <>
+ struct is_speshul<unsafe_function_result> : std::true_type { };
+ template <>
+ struct is_speshul<protected_function_result> : std::true_type { };
+
+ template <std::size_t I, typename... Args, typename T>
+ stack_proxy get(types<Args...>, meta::index_value<0>, meta::index_value<I>, const T& fr) {
+ return stack_proxy(fr.lua_state(), fr.stack_index() + static_cast<int>(I));
+ }
+
+ template <std::size_t I, std::size_t N, typename Arg, typename... Args, typename T, meta::enable<meta::boolean<(N > 0)>> = meta::enabler>
+ stack_proxy get(types<Arg, Args...>, meta::index_value<N>, meta::index_value<I>, const T& fr) {
+ return get(types<Args...>(), meta::index_value<N - 1>(), meta::index_value<I + lua_size<Arg>::value>(), fr);
+ }
+ } // namespace detail
+
+ template <>
+ struct tie_size<unsafe_function_result> : std::integral_constant<std::size_t, SIZE_MAX> { };
+
+ template <>
+ struct tie_size<protected_function_result> : std::integral_constant<std::size_t, SIZE_MAX> { };
+
+ template <std::size_t I>
+ stack_proxy get(const unsafe_function_result& fr) {
+ return stack_proxy(fr.lua_state(), fr.stack_index() + static_cast<int>(I));
+ }
+
+ template <std::size_t I, typename... Args>
+ stack_proxy get(types<Args...> t, const unsafe_function_result& fr) {
+ return detail::get(t, meta::index_value<I>(), meta::index_value<0>(), fr);
+ }
+
+ template <std::size_t I>
+ stack_proxy get(const protected_function_result& fr) {
+ return stack_proxy(fr.lua_state(), fr.stack_index() + static_cast<int>(I));
+ }
+
+ template <std::size_t I, typename... Args>
+ stack_proxy get(types<Args...> t, const protected_function_result& fr) {
+ return detail::get(t, meta::index_value<I>(), meta::index_value<0>(), fr);
+ }
+} // namespace sol
+
+// end of sol/function_result.hpp
+
+// beginning of sol/function_types.hpp
+
+// beginning of sol/function_types_core.hpp
+
+// beginning of sol/wrapper.hpp
+
+namespace sol {
+
+ namespace detail {
+ template <typename T>
+ using array_return_type = meta::conditional_t<std::is_array<T>::value, std::add_lvalue_reference_t<T>, T>;
+ }
+
+ template <typename F, typename = void>
+ struct wrapper {
+ typedef lua_bind_traits<meta::unqualified_t<F>> traits_type;
+ typedef typename traits_type::args_list args_list;
+ typedef typename traits_type::args_list free_args_list;
+ typedef typename traits_type::returns_list returns_list;
+
+ template <typename... Args>
+ static decltype(auto) call(F& f, Args&&... args) {
+ return f(std::forward<Args>(args)...);
+ }
+
+ struct caller {
+ template <typename... Args>
+ decltype(auto) operator()(F& fx, Args&&... args) const {
+ return call(fx, std::forward<Args>(args)...);
+ }
+ };
+ };
+
+ template <typename F>
+ struct wrapper<F, std::enable_if_t<std::is_function<std::remove_pointer_t<meta::unqualified_t<F>>>::value>> {
+ typedef lua_bind_traits<std::remove_pointer_t<meta::unqualified_t<F>>> traits_type;
+ typedef typename traits_type::args_list args_list;
+ typedef typename traits_type::args_list free_args_list;
+ typedef typename traits_type::returns_list returns_list;
+
+ template <F fx, typename... Args>
+ static decltype(auto) invoke(Args&&... args) {
+ return fx(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ static decltype(auto) call(F& fx, Args&&... args) {
+ return fx(std::forward<Args>(args)...);
+ }
+
+ struct caller {
+ template <typename... Args>
+ decltype(auto) operator()(F& fx, Args&&... args) const {
+ return call(fx, std::forward<Args>(args)...);
+ }
+ };
+
+ template <F fx>
+ struct invoker {
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) const {
+ return invoke<fx>(std::forward<Args>(args)...);
+ }
+ };
+ };
+
+ template <typename F>
+ struct wrapper<F, std::enable_if_t<std::is_member_object_pointer<meta::unqualified_t<F>>::value>> {
+ typedef lua_bind_traits<meta::unqualified_t<F>> traits_type;
+ typedef typename traits_type::object_type object_type;
+ typedef typename traits_type::return_type return_type;
+ typedef typename traits_type::args_list args_list;
+ typedef types<object_type&, return_type> free_args_list;
+ typedef typename traits_type::returns_list returns_list;
+
+ template <F fx>
+ static auto call(object_type& mem) -> detail::array_return_type<decltype(mem.*fx)> {
+ return mem.*fx;
+ }
+
+ template <F fx, typename Arg, typename... Args>
+ static decltype(auto) invoke(object_type& mem, Arg&& arg, Args&&...) {
+ return mem.*fx = std::forward<Arg>(arg);
+ }
+
+ template <typename Fx>
+ static auto call(Fx&& fx, object_type& mem) -> detail::array_return_type<decltype(mem.*fx)> {
+ return mem.*fx;
+ }
+
+ template <typename Fx, typename Arg, typename... Args>
+ static void call(Fx&& fx, object_type& mem, Arg&& arg, Args&&...) {
+ using actual_type = meta::unqualified_t<detail::array_return_type<decltype(mem.*fx)>>;
+ if constexpr (std::is_array_v<actual_type>) {
+ using std::cbegin;
+ using std::cend;
+ auto first = cbegin(arg);
+ auto last = cend(arg);
+ for (std::size_t i = 0; first != last; ++i, ++first) {
+ (mem.*fx)[i] = *first;
+ }
+ }
+ else {
+ (mem.*fx) = std::forward<Arg>(arg);
+ }
+ }
+
+ struct caller {
+ template <typename Fx, typename... Args>
+ decltype(auto) operator()(Fx&& fx, object_type& mem, Args&&... args) const {
+ return call(std::forward<Fx>(fx), mem, std::forward<Args>(args)...);
+ }
+ };
+
+ template <F fx>
+ struct invoker {
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) const {
+ return invoke<fx>(std::forward<Args>(args)...);
+ }
+ };
+ };
+
+ template <typename F, typename R, typename O, typename... FArgs>
+ struct member_function_wrapper {
+ typedef O object_type;
+ typedef lua_bind_traits<F> traits_type;
+ typedef typename traits_type::args_list args_list;
+ typedef types<object_type&, FArgs...> free_args_list;
+ typedef meta::tuple_types<R> returns_list;
+
+ template <F fx, typename... Args>
+ static R invoke(O& mem, Args&&... args) {
+ return (mem.*fx)(std::forward<Args>(args)...);
+ }
+
+ template <typename Fx, typename... Args>
+ static R call(Fx&& fx, O& mem, Args&&... args) {
+ return (mem.*fx)(std::forward<Args>(args)...);
+ }
+
+ struct caller {
+ template <typename Fx, typename... Args>
+ decltype(auto) operator()(Fx&& fx, O& mem, Args&&... args) const {
+ return call(std::forward<Fx>(fx), mem, std::forward<Args>(args)...);
+ }
+ };
+
+ template <F fx>
+ struct invoker {
+ template <typename... Args>
+ decltype(auto) operator()(O& mem, Args&&... args) const {
+ return invoke<fx>(mem, std::forward<Args>(args)...);
+ }
+ };
+ };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...)> : public member_function_wrapper<R (O::*)(Args...), R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const> : public member_function_wrapper<R (O::*)(Args...) const, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile> : public member_function_wrapper<R (O::*)(Args...) const volatile, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...)&> : public member_function_wrapper<R (O::*)(Args...)&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const&> : public member_function_wrapper<R (O::*)(Args...) const&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile&> : public member_function_wrapper<R (O::*)(Args...) const volatile&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...)&> : public member_function_wrapper<R (O::*)(Args..., ...)&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const&> : public member_function_wrapper<R (O::*)(Args..., ...) const&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const volatile&> : public member_function_wrapper<R (O::*)(Args..., ...) const volatile&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) &&> : public member_function_wrapper<R (O::*)(Args...)&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const&&> : public member_function_wrapper<R (O::*)(Args...) const&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile&&> : public member_function_wrapper<R (O::*)(Args...) const volatile&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) &&> : public member_function_wrapper<R (O::*)(Args..., ...)&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const&&> : public member_function_wrapper<R (O::*)(Args..., ...) const&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const volatile&&> : public member_function_wrapper<R (O::*)(Args..., ...) const volatile&, R, O, Args...> { };
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ // noexcept has become a part of a function's type
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) noexcept> : public member_function_wrapper<R (O::*)(Args...) noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const noexcept> : public member_function_wrapper<R (O::*)(Args...) const noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile noexcept> : public member_function_wrapper<R (O::*)(Args...) const volatile noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...)& noexcept> : public member_function_wrapper<R (O::*)(Args...)& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const& noexcept> : public member_function_wrapper<R (O::*)(Args...) const& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile& noexcept> : public member_function_wrapper<R (O::*)(Args...) const volatile& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...)& noexcept> : public member_function_wrapper<R (O::*)(Args..., ...)& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const& noexcept> : public member_function_wrapper<R (O::*)(Args..., ...) const& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const volatile& noexcept>
+ : public member_function_wrapper<R (O::*)(Args..., ...) const volatile& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...)&& noexcept> : public member_function_wrapper<R (O::*)(Args...)& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const&& noexcept> : public member_function_wrapper<R (O::*)(Args...) const& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile&& noexcept> : public member_function_wrapper<R (O::*)(Args...) const volatile& noexcept, R, O, Args...> {
+ };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...)&& noexcept> : public member_function_wrapper<R (O::*)(Args..., ...)& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const&& noexcept> : public member_function_wrapper<R (O::*)(Args..., ...) const& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const volatile&& noexcept>
+ : public member_function_wrapper<R (O::*)(Args..., ...) const volatile& noexcept, R, O, Args...> { };
+
+#endif // noexcept is part of a function's type
+
+} // namespace sol
+
+// end of sol/wrapper.hpp
+
+#include <memory>
+
+namespace sol { namespace function_detail {
+ template <typename Fx, int start = 1, bool is_yielding = false>
+ int call(lua_State* L) {
+ Fx& fx = stack::get<user<Fx>>(L, upvalue_index(start));
+ int nr = fx(L);
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+}} // namespace sol::function_detail
+
+// end of sol/function_types_core.hpp
+
+// beginning of sol/function_types_templated.hpp
+
+// beginning of sol/call.hpp
+
+// beginning of sol/property.hpp
+
+#include <type_traits>
+#include <utility>
+
+namespace sol {
+ namespace detail {
+ struct no_prop { };
+ } // namespace detail
+
+ template <typename R, typename W>
+ struct property_wrapper : detail::ebco<R, 0>, detail::ebco<W, 1> {
+ private:
+ using read_base_t = detail::ebco<R, 0>;
+ using write_base_t = detail::ebco<W, 1>;
+
+ public:
+ template <typename Rx, typename Wx>
+ property_wrapper(Rx&& r, Wx&& w) : read_base_t(std::forward<Rx>(r)), write_base_t(std::forward<Wx>(w)) {
+ }
+
+ W& write() {
+ return write_base_t::value();
+ }
+
+ const W& write() const {
+ return write_base_t::value();
+ }
+
+ R& read() {
+ return read_base_t::value();
+ }
+
+ const R& read() const {
+ return read_base_t::value();
+ }
+ };
+
+ template <typename F, typename G>
+ inline decltype(auto) property(F&& f, G&& g) {
+ typedef lua_bind_traits<meta::unqualified_t<F>> left_traits;
+ typedef lua_bind_traits<meta::unqualified_t<G>> right_traits;
+ if constexpr (left_traits::free_arity < right_traits::free_arity) {
+ return property_wrapper<std::decay_t<F>, std::decay_t<G>>(std::forward<F>(f), std::forward<G>(g));
+ }
+ else {
+ return property_wrapper<std::decay_t<G>, std::decay_t<F>>(std::forward<G>(g), std::forward<F>(f));
+ }
+ }
+
+ template <typename F>
+ inline decltype(auto) property(F&& f) {
+ typedef lua_bind_traits<meta::unqualified_t<F>> left_traits;
+ if constexpr (left_traits::free_arity < 2) {
+ return property_wrapper<std::decay_t<F>, detail::no_prop>(std::forward<F>(f), detail::no_prop());
+ }
+ else {
+ return property_wrapper<detail::no_prop, std::decay_t<F>>(detail::no_prop(), std::forward<F>(f));
+ }
+ }
+
+ template <typename F>
+ inline decltype(auto) readonly_property(F&& f) {
+ return property_wrapper<std::decay_t<F>, detail::no_prop>(std::forward<F>(f), detail::no_prop());
+ }
+
+ template <typename F>
+ inline decltype(auto) writeonly_property(F&& f) {
+ return property_wrapper<detail::no_prop, std::decay_t<F>>(detail::no_prop(), std::forward<F>(f));
+ }
+
+ template <typename T>
+ struct readonly_wrapper : detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+
+ operator T&() {
+ return base_t::value();
+ }
+ operator const T&() const {
+ return base_t::value();
+ }
+ };
+
+ // Allow someone to make a member variable readonly (const)
+ template <typename R, typename T>
+ inline auto readonly(R T::*v) {
+ return readonly_wrapper<meta::unqualified_t<decltype(v)>>(v);
+ }
+
+ template <typename T>
+ struct var_wrapper : detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+ };
+
+ template <typename V>
+ inline auto var(V&& v) {
+ typedef std::decay_t<V> T;
+ return var_wrapper<T>(std::forward<V>(v));
+ }
+
+ namespace meta {
+ template <typename T>
+ using is_member_object = std::integral_constant<bool, std::is_member_object_pointer_v<T> || is_specialization_of_v<T, readonly_wrapper>>;
+
+ template <typename T>
+ inline constexpr bool is_member_object_v = is_member_object<T>::value;
+
+ template <typename T>
+ using is_member_object_or_function = std::integral_constant<bool, is_member_object_v<T> || std::is_member_pointer_v<T>>;
+
+ template <typename T>
+ inline constexpr bool is_member_object_or_function_v = is_member_object_or_function<T>::value;
+ } // namespace meta
+
+} // namespace sol
+
+// end of sol/property.hpp
+
+// beginning of sol/protect.hpp
+
+#include <utility>
+
+namespace sol {
+
+ template <typename T>
+ struct protect_t {
+ T value;
+
+ template <typename Arg, typename... Args, meta::disable<std::is_same<protect_t, meta::unqualified_t<Arg>>> = meta::enabler>
+ protect_t(Arg&& arg, Args&&... args) : value(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ protect_t(const protect_t&) = default;
+ protect_t(protect_t&&) = default;
+ protect_t& operator=(const protect_t&) = default;
+ protect_t& operator=(protect_t&&) = default;
+ };
+
+ template <typename T>
+ auto protect(T&& value) {
+ return protect_t<std::decay_t<T>>(std::forward<T>(value));
+ }
+
+} // namespace sol
+
+// end of sol/protect.hpp
+
+namespace sol {
+ namespace u_detail {
+
+ } // namespace u_detail
+
+ namespace policy_detail {
+ template <int I, int... In>
+ inline void handle_policy(static_stack_dependencies<I, In...>, lua_State* L, int&) {
+ if constexpr (sizeof...(In) == 0) {
+ (void)L;
+ return;
+ }
+ else {
+ absolute_index ai(L, I);
+ if (type_of(L, ai) != type::userdata) {
+ return;
+ }
+ lua_createtable(L, static_cast<int>(sizeof...(In)), 0);
+ stack_reference deps(L, -1);
+ auto per_dep = [&L, &deps](int i) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L, i);
+ luaL_ref(L, deps.stack_index());
+ };
+ (void)per_dep;
+ (void)detail::swallow { int(), (per_dep(In), int())... };
+ lua_setuservalue(L, ai);
+ }
+ }
+
+ template <int... In>
+ inline void handle_policy(returns_self_with<In...>, lua_State* L, int& pushed) {
+ pushed = stack::push(L, raw_index(1));
+ handle_policy(static_stack_dependencies<-1, In...>(), L, pushed);
+ }
+
+ inline void handle_policy(const stack_dependencies& sdeps, lua_State* L, int&) {
+ absolute_index ai(L, sdeps.target);
+ if (type_of(L, ai) != type::userdata) {
+ return;
+ }
+ lua_createtable(L, static_cast<int>(sdeps.size()), 0);
+ stack_reference deps(L, -1);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, static_cast<int>(sdeps.size()), detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ for (std::size_t i = 0; i < sdeps.size(); ++i) {
+ lua_pushvalue(L, sdeps.stack_indices[i]);
+ luaL_ref(L, deps.stack_index());
+ }
+ lua_setuservalue(L, ai);
+ }
+
+ template <typename P, meta::disable<std::is_base_of<detail::policy_base_tag, meta::unqualified_t<P>>> = meta::enabler>
+ inline void handle_policy(P&& p, lua_State* L, int& pushed) {
+ pushed = std::forward<P>(p)(L, pushed);
+ }
+ } // namespace policy_detail
+
+ namespace function_detail {
+ inline int no_construction_error(lua_State* L) {
+ return luaL_error(L, "sol: cannot call this constructor (tagged as non-constructible)");
+ }
+ } // namespace function_detail
+
+ namespace call_detail {
+
+ template <typename R, typename W>
+ inline auto& pick(std::true_type, property_wrapper<R, W>& f) {
+ return f.read();
+ }
+
+ template <typename R, typename W>
+ inline auto& pick(std::false_type, property_wrapper<R, W>& f) {
+ return f.write();
+ }
+
+ template <typename T, typename List>
+ struct void_call : void_call<T, meta::function_args_t<List>> { };
+
+ template <typename T, typename... Args>
+ struct void_call<T, types<Args...>> {
+ static void call(Args...) {
+ }
+ };
+
+ template <typename T, bool checked, bool clean_stack>
+ struct constructor_match {
+ T* obj_;
+ reference* obj_lua_ref_;
+ stack::stack_detail::undefined_metatable* p_umf_;
+
+ constructor_match(T* obj_ptr, reference& obj_lua_ref, stack::stack_detail::undefined_metatable& umf)
+ : obj_(obj_ptr), obj_lua_ref_(&obj_lua_ref), p_umf_(&umf) {
+ }
+
+ template <typename Fx, std::size_t I, typename... R, typename... Args>
+ int operator()(types<Fx>, meta::index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) const {
+ detail::default_construct func {};
+ int result = stack::call_into_lua<checked, clean_stack>(r, a, L, start, func, this->obj_);
+ // construct userdata table
+ // SPECIFICALLY, after we've created it successfully.
+ // If the constructor exits for any reason we have to break things down...
+ if constexpr (clean_stack) {
+ obj_lua_ref_->push();
+ (*this->p_umf_)();
+ obj_lua_ref_->pop();
+ }
+ else {
+ (*this->p_umf_)();
+ }
+ return result;
+ }
+ };
+
+ namespace overload_detail {
+ template <std::size_t... M, typename Match, typename... Args>
+ inline int overload_match_arity(types<>, std::index_sequence<>, std::index_sequence<M...>, Match&&, lua_State* L, int, int, Args&&...) {
+ return luaL_error(L, "sol: no matching function call takes this number of arguments and the specified types");
+ }
+
+ template <typename Fx, typename... Fxs, std::size_t I, std::size_t... In, std::size_t... M, typename Match, typename... Args>
+ inline int overload_match_arity(types<Fx, Fxs...>, std::index_sequence<I, In...>, std::index_sequence<M...>, Match&& matchfx, lua_State* L,
+ int fxarity, int start, Args&&... args) {
+ typedef lua_bind_traits<meta::unwrap_unqualified_t<Fx>> traits;
+ typedef meta::tuple_types<typename traits::return_type> return_types;
+ typedef typename traits::free_args_list args_list;
+ // compile-time eliminate any functions that we know ahead of time are of improper arity
+ if constexpr (!traits::runtime_variadics_t::value
+ && meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) {
+ return overload_match_arity(types<Fxs...>(),
+ std::index_sequence<In...>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ else {
+ if constexpr (!traits::runtime_variadics_t::value) {
+ if (traits::free_arity != fxarity) {
+ return overload_match_arity(types<Fxs...>(),
+ std::index_sequence<In...>(),
+ std::index_sequence<traits::free_arity, M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ }
+ stack::record tracking {};
+ if (!stack::stack_detail::check_types(args_list(), L, start, &no_panic, tracking)) {
+ return overload_match_arity(types<Fxs...>(),
+ std::index_sequence<In...>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ return matchfx(types<Fx>(), meta::index_value<I>(), return_types(), args_list(), L, fxarity, start, std::forward<Args>(args)...);
+ }
+ }
+
+ template <std::size_t... M, typename Match, typename... Args>
+ inline int overload_match_arity_single(
+ types<>, std::index_sequence<>, std::index_sequence<M...>, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
+ return overload_match_arity(types<>(),
+ std::index_sequence<>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+
+ template <typename Fx, std::size_t I, std::size_t... M, typename Match, typename... Args>
+ inline int overload_match_arity_single(
+ types<Fx>, std::index_sequence<I>, std::index_sequence<M...>, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
+ typedef lua_bind_traits<meta::unwrap_unqualified_t<Fx>> traits;
+ typedef meta::tuple_types<typename traits::return_type> return_types;
+ typedef typename traits::free_args_list args_list;
+ // compile-time eliminate any functions that we know ahead of time are of improper arity
+ if constexpr (!traits::runtime_variadics_t::value
+ && meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) {
+ return overload_match_arity(types<>(),
+ std::index_sequence<>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ if constexpr (!traits::runtime_variadics_t::value) {
+ if (traits::free_arity != fxarity) {
+ return overload_match_arity(types<>(),
+ std::index_sequence<>(),
+ std::index_sequence<traits::free_arity, M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ }
+ return matchfx(types<Fx>(), meta::index_value<I>(), return_types(), args_list(), L, fxarity, start, std::forward<Args>(args)...);
+ }
+
+ template <typename Fx, typename Fx1, typename... Fxs, std::size_t I, std::size_t I1, std::size_t... In, std::size_t... M, typename Match,
+ typename... Args>
+ inline int overload_match_arity_single(types<Fx, Fx1, Fxs...>, std::index_sequence<I, I1, In...>, std::index_sequence<M...>, Match&& matchfx,
+ lua_State* L, int fxarity, int start, Args&&... args) {
+ typedef lua_bind_traits<meta::unwrap_unqualified_t<Fx>> traits;
+ typedef meta::tuple_types<typename traits::return_type> return_types;
+ typedef typename traits::free_args_list args_list;
+ // compile-time eliminate any functions that we know ahead of time are of improper arity
+ if constexpr (!traits::runtime_variadics_t::value
+ && meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) {
+ return overload_match_arity(types<Fx1, Fxs...>(),
+ std::index_sequence<I1, In...>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ else {
+ if constexpr (!traits::runtime_variadics_t::value) {
+ if (traits::free_arity != fxarity) {
+ return overload_match_arity(types<Fx1, Fxs...>(),
+ std::index_sequence<I1, In...>(),
+ std::index_sequence<traits::free_arity, M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ }
+ stack::record tracking {};
+ if (!stack::stack_detail::check_types(args_list(), L, start, &no_panic, tracking)) {
+ return overload_match_arity(types<Fx1, Fxs...>(),
+ std::index_sequence<I1, In...>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ return matchfx(types<Fx>(), meta::index_value<I>(), return_types(), args_list(), L, fxarity, start, std::forward<Args>(args)...);
+ }
+ }
+ } // namespace overload_detail
+
+ template <typename... Functions, typename Match, typename... Args>
+ inline int overload_match_arity(Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
+ return overload_detail::overload_match_arity_single(types<Functions...>(),
+ std::make_index_sequence<sizeof...(Functions)>(),
+ std::index_sequence<>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+
+ template <typename... Functions, typename Match, typename... Args>
+ inline int overload_match(Match&& matchfx, lua_State* L, int start, Args&&... args) {
+ int fxarity = lua_gettop(L) - (start - 1);
+ return overload_match_arity<Functions...>(std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename... TypeLists, typename Match, typename... Args>
+ inline int construct_match(Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
+ // use same overload resolution matching as all other parts of the framework
+ return overload_match_arity<decltype(void_call<T, TypeLists>::call)...>(
+ std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
+ }
+
+ template <typename T, bool checked, bool clean_stack, typename... TypeLists>
+ inline int construct_trampolined(lua_State* L) {
+ static const auto& meta = usertype_traits<T>::metatable();
+ int argcount = lua_gettop(L);
+ call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, usertype_traits<T>::user_metatable(), 1) : call_syntax::dot;
+ argcount -= static_cast<int>(syntax);
+
+ T* obj = detail::usertype_allocate<T>(L);
+ reference userdataref(L, -1);
+ stack::stack_detail::undefined_metatable umf(L, &meta[0], &stack::stack_detail::set_undefined_methods_on<T>);
+
+ // put userdata at the first index
+ lua_insert(L, 1);
+ construct_match<T, TypeLists...>(constructor_match<T, checked, clean_stack>(obj, userdataref, umf), L, argcount, 1 + static_cast<int>(syntax));
+
+ userdataref.push();
+ return 1;
+ }
+
+ template <typename T, bool checked, bool clean_stack, typename... TypeLists>
+ inline int construct(lua_State* L) {
+ return detail::static_trampoline<&construct_trampolined<T, checked, clean_stack, TypeLists...>>(L);
+ }
+
+ template <typename F, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename = void>
+ struct agnostic_lua_call_wrapper {
+ template <typename Fx, typename... Args>
+ static int call(lua_State* L, Fx&& f, Args&&... args) {
+ using uFx = meta::unqualified_t<Fx>;
+ static constexpr bool is_ref = is_lua_reference_v<uFx>;
+ if constexpr (is_ref) {
+ if constexpr (is_index) {
+ return stack::push(L, std::forward<Fx>(f), std::forward<Args>(args)...);
+ }
+ else {
+ std::forward<Fx>(f) = stack::unqualified_get<F>(L, boost + (is_variable ? 3 : 1));
+ return 0;
+ }
+ }
+ else {
+ using wrap = wrapper<uFx>;
+ using traits_type = typename wrap::traits_type;
+ using fp_t = typename traits_type::function_pointer_type;
+ constexpr bool is_function_pointer_convertible = std::is_class_v<uFx> && std::is_convertible_v<std::decay_t<Fx>, fp_t>;
+ if constexpr (is_function_pointer_convertible) {
+ fp_t fx = f;
+ return agnostic_lua_call_wrapper<fp_t, is_index, is_variable, checked, boost, clean_stack> {}.call(
+ L, fx, std::forward<Args>(args)...);
+ }
+ else {
+ using returns_list = typename wrap::returns_list;
+ using args_list = typename wrap::free_args_list;
+ using caller = typename wrap::caller;
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + 1, caller(), std::forward<Fx>(f), std::forward<Args>(args)...);
+ }
+ }
+ }
+ };
+
+ template <typename T, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<var_wrapper<T>, is_index, is_variable, checked, boost, clean_stack, C> {
+ template <typename F>
+ static int call(lua_State* L, F&& f) {
+ if constexpr (is_index) {
+ constexpr bool is_stack = is_stack_based_v<meta::unqualified_t<decltype(detail::unwrap(f.value()))>>;
+ if constexpr (clean_stack && !is_stack) {
+ lua_settop(L, 0);
+ }
+ return stack::push_reference(L, detail::unwrap(f.value()));
+ }
+ else {
+ if constexpr (std::is_const_v<meta::unwrapped_t<T>>) {
+ (void)f;
+ return luaL_error(L, "sol: cannot write to a readonly (const) variable");
+ }
+ else {
+ using R = meta::unwrapped_t<T>;
+ if constexpr (std::is_assignable_v<std::add_lvalue_reference_t<meta::unqualified_t<R>>, R>) {
+ detail::unwrap(f.value()) = stack::unqualified_get<meta::unwrapped_t<T>>(L, boost + (is_variable ? 3 : 1));
+ if (clean_stack) {
+ lua_settop(L, 0);
+ }
+ return 0;
+ }
+ else {
+ return luaL_error(L, "sol: cannot write to this variable: copy assignment/constructor not available");
+ }
+ }
+ }
+ }
+ };
+
+ template <bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<lua_CFunction_ref, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, lua_CFunction_ref f) {
+ return f(L);
+ }
+ };
+
+ template <bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<lua_CFunction, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, lua_CFunction f) {
+ return f(L);
+ }
+ };
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ template <bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<detail::lua_CFunction_noexcept, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, detail::lua_CFunction_noexcept f) {
+ return f(L);
+ }
+ };
+#endif // noexcept function types
+
+ template <bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<detail::no_prop, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, const detail::no_prop&) {
+ return luaL_error(L, is_index ? "sol: cannot read from a writeonly property" : "sol: cannot write to a readonly property");
+ }
+ };
+
+ template <bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<no_construction, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, const no_construction&) {
+ return function_detail::no_construction_error(L);
+ }
+ };
+
+ template <typename... Args, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<bases<Args...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State*, const bases<Args...>&) {
+ // Uh. How did you even call this, lul
+ return 0;
+ }
+ };
+
+ template <typename T, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<std::reference_wrapper<T>, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, std::reference_wrapper<T> f) {
+ agnostic_lua_call_wrapper<T, is_index, is_variable, checked, boost, clean_stack> alcw {};
+ return alcw.call(L, f.get());
+ }
+ };
+
+ template <typename T, typename F, bool is_index, bool is_variable, bool checked = detail::default_safe_function_calls, int boost = 0,
+ bool clean_stack = true, typename = void>
+ struct lua_call_wrapper {
+ template <typename Fx, typename... Args>
+ static int call(lua_State* L, Fx&& fx, Args&&... args) {
+ if constexpr (std::is_member_function_pointer_v<F>) {
+ using wrap = wrapper<F>;
+ using object_type = typename wrap::object_type;
+ if constexpr (sizeof...(Args) < 1) {
+ using Ta = meta::conditional_t<std::is_void_v<T>, object_type, T>;
+ static_assert(std::is_base_of_v<object_type, Ta>,
+ "It seems like you might have accidentally bound a class type with a member function method that does not correspond to the "
+ "class. For example, there could be a small type in your new_usertype<T>(...) binding, where you specify one class \"T\" "
+ "but then bind member methods from a complete unrelated class. Check things over!");
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ auto maybeo = stack::check_get<Ta*>(L, 1);
+ if (!maybeo || maybeo.value() == nullptr) {
+ return luaL_error(L,
+ "sol: received nil for 'self' argument (use ':' for accessing member functions, make sure member variables are "
+ "preceeded by the "
+ "actual object with '.' syntax)");
+ }
+ object_type* o = static_cast<object_type*>(maybeo.value());
+ return call(L, std::forward<Fx>(fx), *o);
+#else
+ object_type& o = static_cast<object_type&>(*stack::unqualified_get<non_null<Ta*>>(L, 1));
+ return call(L, std::forward<Fx>(fx), o);
+#endif // Safety
+ }
+ else {
+ using returns_list = typename wrap::returns_list;
+ using args_list = typename wrap::args_list;
+ using caller = typename wrap::caller;
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+ else if constexpr (std::is_member_object_pointer_v<F>) {
+ using wrap = wrapper<F>;
+ using object_type = typename wrap::object_type;
+ if constexpr (is_index) {
+ if constexpr (sizeof...(Args) < 1) {
+ using Ta = meta::conditional_t<std::is_void_v<T>, object_type, T>;
+ static_assert(std::is_base_of_v<object_type, Ta>,
+ "It seems like you might have accidentally bound a class type with a member function method that does not correspond "
+ "to the class. For example, there could be a small type in your new_usertype<T>(...) binding, where you specify one "
+ "class \"T\" but then bind member methods from a complete unrelated class. Check things over!");
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ auto maybeo = stack::check_get<Ta*>(L, 1);
+ if (!maybeo || maybeo.value() == nullptr) {
+ if (is_variable) {
+ return luaL_error(L, "sol: 'self' argument is lua_nil (bad '.' access?)");
+ }
+ return luaL_error(L, "sol: 'self' argument is lua_nil (pass 'self' as first argument)");
+ }
+ object_type* o = static_cast<object_type*>(maybeo.value());
+ return call(L, std::forward<Fx>(fx), *o);
+#else
+ object_type& o = static_cast<object_type&>(*stack::get<non_null<Ta*>>(L, 1));
+ return call(L, std::forward<Fx>(fx), o);
+#endif // Safety
+ }
+ else {
+ using returns_list = typename wrap::returns_list;
+ using caller = typename wrap::caller;
+ return stack::call_into_lua<checked, clean_stack>(returns_list(),
+ types<>(),
+ L,
+ boost + (is_variable ? 3 : 2),
+ caller(),
+ std::forward<Fx>(fx),
+ std::forward<Args>(args)...);
+ }
+ }
+ else {
+ using traits_type = lua_bind_traits<F>;
+ using return_type = typename traits_type::return_type;
+ constexpr bool ret_is_const = std::is_const_v<std::remove_reference_t<return_type>>;
+ if constexpr (ret_is_const) {
+ (void)fx;
+ (void)detail::swallow { 0, (static_cast<void>(args), 0)... };
+ return luaL_error(L, "sol: cannot write to a readonly (const) variable");
+ }
+ else {
+ using u_return_type = meta::unqualified_t<return_type>;
+ constexpr bool is_assignable = std::is_copy_assignable_v<u_return_type> || std::is_array_v<u_return_type>;
+ if constexpr (!is_assignable) {
+ (void)fx;
+ (void)detail::swallow { 0, ((void)args, 0)... };
+ return luaL_error(L, "sol: cannot write to this variable: copy assignment/constructor not available");
+ }
+ else {
+ using args_list = typename wrap::args_list;
+ using caller = typename wrap::caller;
+ if constexpr (sizeof...(Args) > 0) {
+ return stack::call_into_lua<checked, clean_stack>(types<void>(),
+ args_list(),
+ L,
+ boost + (is_variable ? 3 : 2),
+ caller(),
+ std::forward<Fx>(fx),
+ std::forward<Args>(args)...);
+ }
+ else {
+ using Ta = meta::conditional_t<std::is_void_v<T>, object_type, T>;
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ auto maybeo = stack::check_get<Ta*>(L, 1);
+ if (!maybeo || maybeo.value() == nullptr) {
+ if (is_variable) {
+ return luaL_error(L, "sol: received nil for 'self' argument (bad '.' access?)");
+ }
+ return luaL_error(L, "sol: received nil for 'self' argument (pass 'self' as first argument)");
+ }
+ object_type* po = static_cast<object_type*>(maybeo.value());
+ object_type& o = *po;
+#else
+ object_type& o = static_cast<object_type&>(*stack::get<non_null<Ta*>>(L, 1));
+#endif // Safety
+
+ return stack::call_into_lua<checked, clean_stack>(
+ types<void>(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), std::forward<Fx>(fx), o);
+ }
+ }
+ }
+ }
+ }
+ else {
+ agnostic_lua_call_wrapper<F, is_index, is_variable, checked, boost, clean_stack> alcw {};
+ return alcw.call(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+ };
+
+ template <typename T, typename F, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, readonly_wrapper<F>, is_index, is_variable, checked, boost, clean_stack, C> {
+ using traits_type = lua_bind_traits<F>;
+ using wrap = wrapper<F>;
+ using object_type = typename wrap::object_type;
+
+ static int call(lua_State* L, readonly_wrapper<F>&& rw) {
+ if constexpr (!is_index) {
+ (void)rw;
+ return luaL_error(L, "sol: cannot write to a sol::readonly variable");
+ }
+ else {
+ lua_call_wrapper<T, F, true, is_variable, checked, boost, clean_stack, C> lcw;
+ return lcw.call(L, std::move(rw.value()));
+ }
+ }
+
+ static int call(lua_State* L, readonly_wrapper<F>&& rw, object_type& o) {
+ if constexpr (!is_index) {
+ (void)o;
+ return call(L, std::move(rw));
+ }
+ else {
+ lua_call_wrapper<T, F, true, is_variable, checked, boost, clean_stack, C> lcw;
+ return lcw.call(L, rw.value(), o);
+ }
+ }
+
+ static int call(lua_State* L, const readonly_wrapper<F>& rw) {
+ if constexpr (!is_index) {
+ (void)rw;
+ return luaL_error(L, "sol: cannot write to a sol::readonly variable");
+ }
+ else {
+ lua_call_wrapper<T, F, true, is_variable, checked, boost, clean_stack, C> lcw;
+ return lcw.call(L, rw.value());
+ }
+ }
+
+ static int call(lua_State* L, const readonly_wrapper<F>& rw, object_type& o) {
+ if constexpr (!is_index) {
+ (void)o;
+ return call(L, rw);
+ }
+ else {
+ lua_call_wrapper<T, F, true, is_variable, checked, boost, clean_stack, C> lcw;
+ return lcw.call(L, rw.value(), o);
+ }
+ }
+ };
+
+ template <typename T, typename... Args, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, constructor_list<Args...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef constructor_list<Args...> F;
+
+ static int call(lua_State* L, F&) {
+ const auto& meta = usertype_traits<T>::metatable();
+ int argcount = lua_gettop(L);
+ call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, usertype_traits<T>::user_metatable(), 1) : call_syntax::dot;
+ argcount -= static_cast<int>(syntax);
+
+ T* obj = detail::usertype_allocate<T>(L);
+ reference userdataref(L, -1);
+ stack::stack_detail::undefined_metatable umf(L, &meta[0], &stack::stack_detail::set_undefined_methods_on<T>);
+
+ // put userdata at the first index
+ lua_insert(L, 1);
+ // Because of the way constructors work,
+ // we have to kill the data, but only if the cosntructor is successfulyl invoked...
+ // if it's not successfully invoked and we panic,
+ // we cannot actually deallcoate/delete the data.
+ construct_match<T, Args...>(
+ constructor_match<T, checked, clean_stack>(obj, userdataref, umf), L, argcount, boost + 1 + 1 + static_cast<int>(syntax));
+
+ userdataref.push();
+ return 1;
+ }
+ };
+
+ template <typename T, typename... Cxs, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, constructor_wrapper<Cxs...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef constructor_wrapper<Cxs...> F;
+
+ struct onmatch {
+ template <typename Fx, std::size_t I, typename... R, typename... Args>
+ int operator()(types<Fx>, meta::index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start, F& f) {
+ const auto& meta = usertype_traits<T>::metatable();
+ T* obj = detail::usertype_allocate<T>(L);
+ reference userdataref(L, -1);
+ stack::stack_detail::undefined_metatable umf(L, &meta[0], &stack::stack_detail::set_undefined_methods_on<T>);
+ umf();
+
+ auto& func = std::get<I>(f.functions);
+ // put userdata at the first index
+ lua_insert(L, 1);
+ stack::call_into_lua<checked, clean_stack>(r, a, L, boost + 1 + start, func, detail::implicit_wrapper<T>(obj));
+
+ userdataref.push();
+ return 1;
+ }
+ };
+
+ static int call(lua_State* L, F& f) {
+ call_syntax syntax = stack::get_call_syntax(L, usertype_traits<T>::user_metatable(), 1);
+ int syntaxval = static_cast<int>(syntax);
+ int argcount = lua_gettop(L) - syntaxval;
+ return construct_match<T, meta::pop_front_type_t<meta::function_args_t<Cxs>>...>(onmatch(), L, argcount, 1 + syntaxval, f);
+ }
+ };
+
+ template <typename T, typename Fx, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, destructor_wrapper<Fx>, is_index, is_variable, checked, boost, clean_stack, C> {
+
+ template <typename F>
+ static int call(lua_State* L, F&& f) {
+ if constexpr (std::is_void_v<Fx>) {
+ return detail::usertype_alloc_destroy<T>(L);
+ }
+ else {
+ using uFx = meta::unqualified_t<Fx>;
+ lua_call_wrapper<T, uFx, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::forward<F>(f).fx);
+ }
+ }
+ };
+
+ template <typename T, typename... Fs, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, overload_set<Fs...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef overload_set<Fs...> F;
+
+ struct on_match {
+ template <typename Fx, std::size_t I, typename... R, typename... Args>
+ int operator()(types<Fx>, meta::index_value<I>, types<R...>, types<Args...>, lua_State* L, int, int, F& fx) {
+ auto& f = std::get<I>(fx.functions);
+ return lua_call_wrapper<T, Fx, is_index, is_variable, checked, boost> {}.call(L, f);
+ }
+ };
+
+ static int call(lua_State* L, F& fx) {
+ return overload_match_arity<Fs...>(on_match(), L, lua_gettop(L), 1, fx);
+ }
+ };
+
+ template <typename T, typename... Fs, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, factory_wrapper<Fs...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef factory_wrapper<Fs...> F;
+
+ struct on_match {
+ template <typename Fx, std::size_t I, typename... R, typename... Args>
+ int operator()(types<Fx>, meta::index_value<I>, types<R...>, types<Args...>, lua_State* L, int, int, F& fx) {
+ auto& f = std::get<I>(fx.functions);
+ return lua_call_wrapper<T, Fx, is_index, is_variable, checked, boost, clean_stack> {}.call(L, f);
+ }
+ };
+
+ static int call(lua_State* L, F& fx) {
+ return overload_match_arity<Fs...>(on_match(), L, lua_gettop(L) - boost, 1 + boost, fx);
+ }
+ };
+
+ template <typename T, typename R, typename W, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, property_wrapper<R, W>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef meta::conditional_t<is_index, R, W> P;
+ typedef meta::unqualified_t<P> U;
+ typedef wrapper<U> wrap;
+ typedef lua_bind_traits<U> traits_type;
+ typedef meta::unqualified_t<typename traits_type::template arg_at<0>> object_type;
+
+ template <typename F, typename... Args>
+ static int call(lua_State* L, F&& f, Args&&... args) {
+ constexpr bool is_specialized = meta::any<std::is_same<U, detail::no_prop>,
+ meta::is_specialization_of<U, var_wrapper>,
+ meta::is_specialization_of<U, constructor_wrapper>,
+ meta::is_specialization_of<U, constructor_list>,
+ std::is_member_pointer<U>>::value;
+ if constexpr (is_specialized) {
+ if constexpr (is_index) {
+ decltype(auto) p = f.read();
+ lua_call_wrapper<T, meta::unqualified_t<decltype(p)>, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, p, std::forward<Args>(args)...);
+ }
+ else {
+ decltype(auto) p = f.write();
+ lua_call_wrapper<T, meta::unqualified_t<decltype(p)>, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, p, std::forward<Args>(args)...);
+ }
+ }
+ else {
+ constexpr bool non_class_object_type = meta::any<std::is_void<object_type>,
+ meta::boolean<lua_type_of<meta::unwrap_unqualified_t<object_type>>::value != type::userdata>>::value;
+ if constexpr (non_class_object_type) {
+ // The type being void means we don't have any arguments, so it might be a free functions?
+ using args_list = typename traits_type::free_args_list;
+ using returns_list = typename wrap::returns_list;
+ using caller = typename wrap::caller;
+ if constexpr (is_index) {
+ decltype(auto) pf = f.read();
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), pf);
+ }
+ else {
+ decltype(auto) pf = f.write();
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), pf);
+ }
+ }
+ else {
+ using args_list = meta::pop_front_type_t<typename traits_type::free_args_list>;
+ using Ta = T;
+ using Oa = std::remove_pointer_t<object_type>;
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ auto maybeo = stack::check_get<Ta*>(L, 1);
+ if (!maybeo || maybeo.value() == nullptr) {
+ if (is_variable) {
+ return luaL_error(L, "sol: 'self' argument is lua_nil (bad '.' access?)");
+ }
+ return luaL_error(L, "sol: 'self' argument is lua_nil (pass 'self' as first argument)");
+ }
+ Oa* o = static_cast<Oa*>(maybeo.value());
+#else
+ Oa* o = static_cast<Oa*>(stack::get<non_null<Ta*>>(L, 1));
+#endif // Safety
+ using returns_list = typename wrap::returns_list;
+ using caller = typename wrap::caller;
+ if constexpr (is_index) {
+ decltype(auto) pf = f.read();
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), pf, detail::implicit_wrapper<Oa>(*o));
+ }
+ else {
+ decltype(auto) pf = f.write();
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), pf, detail::implicit_wrapper<Oa>(*o));
+ }
+ }
+ }
+ }
+ };
+
+ template <typename T, typename V, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, protect_t<V>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef protect_t<V> F;
+
+ template <typename... Args>
+ static int call(lua_State* L, F& fx, Args&&... args) {
+ return lua_call_wrapper<T, V, is_index, is_variable, true, boost, clean_stack> {}.call(L, fx.value, std::forward<Args>(args)...);
+ }
+ };
+
+ template <typename T, typename F, typename... Policies, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, policy_wrapper<F, Policies...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef policy_wrapper<F, Policies...> P;
+
+ template <std::size_t... In>
+ static int call(std::index_sequence<In...>, lua_State* L, P& fx) {
+ int pushed = lua_call_wrapper<T, F, is_index, is_variable, checked, boost, false, C> {}.call(L, fx.value);
+ (void)detail::swallow { int(), (policy_detail::handle_policy(std::get<In>(fx.policies), L, pushed), int())... };
+ return pushed;
+ }
+
+ static int call(lua_State* L, P& fx) {
+ typedef typename P::indices indices;
+ return call(indices(), L, fx);
+ }
+ };
+
+ template <typename T, typename Y, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, yielding_t<Y>, is_index, is_variable, checked, boost, clean_stack, C> {
+ template <typename F>
+ static int call(lua_State* L, F&& f) {
+ return lua_call_wrapper<T, meta::unqualified_t<Y>, is_index, is_variable, checked, boost, clean_stack> {}.call(L, f.func);
+ }
+ };
+
+ template <typename T, typename Sig, typename P, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, function_arguments<Sig, P>, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, const function_arguments<Sig, P>& f) {
+ lua_call_wrapper<T, meta::unqualified_t<P>, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::get<0>(f.arguments));
+ }
+
+ static int call(lua_State* L, function_arguments<Sig, P>& f) {
+ lua_call_wrapper<T, meta::unqualified_t<P>, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::get<0>(f.arguments));
+ }
+
+ static int call(lua_State* L, function_arguments<Sig, P>&& f) {
+ lua_call_wrapper<T, meta::unqualified_t<P>, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::get<0>(std::move(f.arguments)));
+ }
+ };
+
+ template <typename T, bool is_index, bool is_variable, int boost = 0, bool checked = detail::default_safe_function_calls, bool clean_stack = true,
+ typename Fx, typename... Args>
+ inline int call_wrapped(lua_State* L, Fx&& fx, Args&&... args) {
+ using uFx = meta::unqualified_t<Fx>;
+ if constexpr (meta::is_specialization_of_v<uFx, yielding_t>) {
+ using real_fx = meta::unqualified_t<decltype(std::forward<Fx>(fx).func)>;
+ lua_call_wrapper<T, real_fx, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::forward<Fx>(fx).func, std::forward<Args>(args)...);
+ }
+ else {
+ lua_call_wrapper<T, uFx, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename T, bool is_index, bool is_variable, typename F, int start = 1, bool checked = detail::default_safe_function_calls,
+ bool clean_stack = true>
+ inline int call_user(lua_State* L) {
+ auto& fx = stack::unqualified_get<user<F>>(L, upvalue_index(start));
+ using uFx = meta::unqualified_t<F>;
+ int nr = call_wrapped<T, is_index, is_variable, 0, checked, clean_stack>(L, fx);
+ if constexpr (meta::is_specialization_of_v<uFx, yielding_t>) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ template <typename T, typename = void>
+ struct is_var_bind : std::false_type { };
+
+ template <typename T>
+ struct is_var_bind<T, std::enable_if_t<std::is_member_object_pointer<T>::value>> : std::true_type { };
+
+ template <typename T>
+ struct is_var_bind<T, std::enable_if_t<is_lua_reference_or_proxy<T>::value>> : std::true_type { };
+
+ template <>
+ struct is_var_bind<detail::no_prop> : std::true_type { };
+
+ template <typename R, typename W>
+ struct is_var_bind<property_wrapper<R, W>> : std::true_type { };
+
+ template <typename T>
+ struct is_var_bind<var_wrapper<T>> : std::true_type { };
+
+ template <typename T>
+ struct is_var_bind<readonly_wrapper<T>> : is_var_bind<meta::unqualified_t<T>> { };
+
+ template <typename F, typename... Policies>
+ struct is_var_bind<policy_wrapper<F, Policies...>> : is_var_bind<meta::unqualified_t<F>> { };
+ } // namespace call_detail
+
+ template <typename T>
+ struct is_variable_binding : call_detail::is_var_bind<meta::unqualified_t<T>> { };
+
+ template <typename T>
+ using is_var_wrapper = meta::is_specialization_of<T, var_wrapper>;
+
+ template <typename T>
+ struct is_function_binding : meta::neg<is_variable_binding<T>> { };
+
+} // namespace sol
+
+// end of sol/call.hpp
+
+namespace sol {
+ namespace function_detail {
+ template <typename F, F fx>
+ inline int call_wrapper_variable(std::false_type, lua_State* L) {
+ typedef meta::bind_traits<meta::unqualified_t<F>> traits_type;
+ typedef typename traits_type::args_list args_list;
+ typedef meta::tuple_types<typename traits_type::return_type> return_type;
+ return stack::call_into_lua(return_type(), args_list(), L, 1, fx);
+ }
+
+ template <typename R, typename V, V, typename T>
+ inline int call_set_assignable(std::false_type, T&&, lua_State* L) {
+ return luaL_error(L, "cannot write to this type: copy assignment/constructor not available");
+ }
+
+ template <typename R, typename V, V variable, typename T>
+ inline int call_set_assignable(std::true_type, lua_State* L, T&& mem) {
+ (mem.*variable) = stack::get<R>(L, 2);
+ return 0;
+ }
+
+ template <typename R, typename V, V, typename T>
+ inline int call_set_variable(std::false_type, lua_State* L, T&&) {
+ return luaL_error(L, "cannot write to a const variable");
+ }
+
+ template <typename R, typename V, V variable, typename T>
+ inline int call_set_variable(std::true_type, lua_State* L, T&& mem) {
+ return call_set_assignable<R, V, variable>(std::is_assignable<std::add_lvalue_reference_t<R>, R>(), L, std::forward<T>(mem));
+ }
+
+ template <typename V, V variable>
+ inline int call_wrapper_variable(std::true_type, lua_State* L) {
+ typedef meta::bind_traits<meta::unqualified_t<V>> traits_type;
+ typedef typename traits_type::object_type T;
+ typedef typename traits_type::return_type R;
+ auto& mem = stack::get<T>(L, 1);
+ switch (lua_gettop(L)) {
+ case 1: {
+ decltype(auto) r = (mem.*variable);
+ stack::push_reference(L, std::forward<decltype(r)>(r));
+ return 1;
+ }
+ case 2:
+ return call_set_variable<R, V, variable>(meta::neg<std::is_const<R>>(), L, mem);
+ default:
+ return luaL_error(L, "incorrect number of arguments to member variable function call");
+ }
+ }
+
+ template <typename F, F fx>
+ inline int call_wrapper_function(std::false_type, lua_State* L) {
+ return call_wrapper_variable<F, fx>(std::is_member_object_pointer<F>(), L);
+ }
+
+ template <typename F, F fx>
+ inline int call_wrapper_function(std::true_type, lua_State* L) {
+ return call_detail::call_wrapped<void, false, false>(L, fx);
+ }
+
+ template <typename F, F fx>
+ int call_wrapper_entry(lua_State* L) noexcept(meta::bind_traits<F>::is_noexcept) {
+ return call_wrapper_function<F, fx>(std::is_member_function_pointer<meta::unqualified_t<F>>(), L);
+ }
+
+ template <typename... Fxs>
+ struct c_call_matcher {
+ template <typename Fx, std::size_t I, typename R, typename... Args>
+ int operator()(types<Fx>, meta::index_value<I>, types<R>, types<Args...>, lua_State* L, int, int) const {
+ typedef meta::at_in_pack_t<I, Fxs...> target;
+ return target::call(L);
+ }
+ };
+
+ template <typename F, F fx>
+ inline int c_call_raw(std::true_type, lua_State* L) {
+ return fx(L);
+ }
+
+ template <typename F, F fx>
+ inline int c_call_raw(std::false_type, lua_State* L) {
+#ifdef __clang__
+ return detail::trampoline(L, function_detail::call_wrapper_entry<F, fx>);
+#else
+ return detail::typed_static_trampoline<decltype(&function_detail::call_wrapper_entry<F, fx>), (&function_detail::call_wrapper_entry<F, fx>)>(L);
+#endif // fuck you clang :c
+ }
+
+ } // namespace function_detail
+
+ template <typename F, F fx>
+ inline int c_call(lua_State* L) {
+ typedef meta::unqualified_t<F> Fu;
+ typedef std::integral_constant<bool,
+ std::is_same<Fu, lua_CFunction>::value
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ || std::is_same<Fu, detail::lua_CFunction_noexcept>::value
+#endif
+ >
+ is_raw;
+ return function_detail::c_call_raw<F, fx>(is_raw(), L);
+ }
+
+ template <typename F, F f>
+ struct wrap {
+ typedef F type;
+
+ static int call(lua_State* L) noexcept(noexcept(c_call<type, f>(L))) {
+ return c_call<type, f>(L);
+ }
+ };
+
+ template <typename... Fxs>
+ inline int c_call(lua_State* L) {
+ if constexpr (sizeof...(Fxs) < 2) {
+ using target = meta::at_in_pack_t<0, Fxs...>;
+ return target::call(L);
+ }
+ else {
+ return call_detail::overload_match_arity<typename Fxs::type...>(function_detail::c_call_matcher<Fxs...>(), L, lua_gettop(L), 1);
+ }
+ }
+
+} // namespace sol
+
+// end of sol/function_types_templated.hpp
+
+// beginning of sol/function_types_stateless.hpp
+
+namespace sol { namespace function_detail {
+ template <typename Function>
+ struct upvalue_free_function {
+ using function_type = std::remove_pointer_t<std::decay_t<Function>>;
+ using traits_type = meta::bind_traits<function_type>;
+
+ static int real_call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ auto udata = stack::stack_detail::get_as_upvalues<function_type*>(L);
+ function_type* fx = udata.first;
+ return call_detail::call_wrapped<void, true, false>(L, fx);
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L) {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_member_function {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef lua_bind_traits<function_type> traits_type;
+
+ static int real_call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ // Layout:
+ // idx 1...n: verbatim data of member function pointer
+ // idx n + 1: is the object's void pointer
+ // We don't need to store the size, because the other side is templated
+ // with the same member function pointer type
+ function_type& memfx = stack::get<user<function_type>>(L, upvalue_index(2));
+ auto& item = *static_cast<T*>(stack::get<void*>(L, upvalue_index(3)));
+ return call_detail::call_wrapped<T, true, false, -1>(L, memfx, item);
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ return call(L);
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_member_variable {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef lua_bind_traits<function_type> traits_type;
+
+ static int real_call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ // Layout:
+ // idx 1...n: verbatim data of member variable pointer
+ // idx n + 1: is the object's void pointer
+ // We don't need to store the size, because the other side is templated
+ // with the same member function pointer type
+ auto memberdata = stack::stack_detail::get_as_upvalues<function_type>(L);
+ auto objdata = stack::stack_detail::get_as_upvalues<T*>(L, memberdata.second);
+ auto& mem = *objdata.first;
+ function_type& var = memberdata.first;
+ switch (lua_gettop(L)) {
+ case 0:
+ return call_detail::call_wrapped<T, true, false, -1>(L, var, mem);
+ case 1:
+ return call_detail::call_wrapped<T, false, false, -1>(L, var, mem);
+ default:
+ return luaL_error(L, "sol: incorrect number of arguments to member variable function");
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ return call(L);
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_member_variable<T, readonly_wrapper<Function>> {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef lua_bind_traits<function_type> traits_type;
+
+ static int real_call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ // Layout:
+ // idx 1...n: verbatim data of member variable pointer
+ // idx n + 1: is the object's void pointer
+ // We don't need to store the size, because the other side is templated
+ // with the same member function pointer type
+ auto memberdata = stack::stack_detail::get_as_upvalues<function_type>(L);
+ auto objdata = stack::stack_detail::get_as_upvalues<T*>(L, memberdata.second);
+ auto& mem = *objdata.first;
+ function_type& var = memberdata.first;
+ switch (lua_gettop(L)) {
+ case 0:
+ return call_detail::call_wrapped<T, true, false, -1>(L, var, mem);
+ default:
+ return luaL_error(L, "sol: incorrect number of arguments to member variable function");
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ return call(L);
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_this_member_function {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef lua_bind_traits<function_type> traits_type;
+
+ static int real_call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ // Layout:
+ // idx 1...n: verbatim data of member variable pointer
+ function_type& memfx = stack::get<user<function_type>>(L, upvalue_index(2));
+ return call_detail::call_wrapped<T, false, false>(L, memfx);
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ return call(L);
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_this_member_variable {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+
+ static int real_call(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ // Layout:
+ // idx 1...n: verbatim data of member variable pointer
+ auto memberdata = stack::stack_detail::get_as_upvalues<function_type>(L);
+ function_type& var = memberdata.first;
+ switch (lua_gettop(L)) {
+ case 1:
+ return call_detail::call_wrapped<T, true, false>(L, var);
+ case 2:
+ return call_detail::call_wrapped<T, false, false>(L, var);
+ default:
+ return luaL_error(L, "sol: incorrect number of arguments to member variable function");
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ return call(L);
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_this_member_variable<T, readonly_wrapper<Function>> {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef lua_bind_traits<function_type> traits_type;
+
+ static int real_call(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ // Layout:
+ // idx 1...n: verbatim data of member variable pointer
+ auto memberdata = stack::stack_detail::get_as_upvalues<function_type>(L);
+ function_type& var = memberdata.first;
+ switch (lua_gettop(L)) {
+ case 1:
+ return call_detail::call_wrapped<T, true, false>(L, var);
+ default:
+ return luaL_error(L, "sol: incorrect number of arguments to member variable function");
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ return call(L);
+ }
+ };
+}} // namespace sol::function_detail
+
+// end of sol/function_types_stateless.hpp
+
+// beginning of sol/function_types_stateful.hpp
+
+namespace sol { namespace function_detail {
+ template <typename Func, bool is_yielding, bool no_trampoline>
+ struct functor_function {
+ typedef std::decay_t<meta::unwrap_unqualified_t<Func>> function_type;
+ function_type invocation;
+
+ template <typename... Args>
+ functor_function(function_type f, Args&&... args) noexcept(std::is_nothrow_constructible_v<function_type, function_type, Args...>)
+ : invocation(std::move(f), std::forward<Args>(args)...) {
+ }
+
+ static int call(lua_State* L, functor_function& self) noexcept(noexcept(call_detail::call_wrapped<void, true, false>(L, self.invocation))) {
+ int nr = call_detail::call_wrapped<void, true, false>(L, self.invocation);
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L) noexcept(noexcept(call_detail::call_wrapped<void, true, false>(L, invocation))) {
+ if constexpr (no_trampoline) {
+ return call(L, *this);
+ }
+ else {
+ return detail::trampoline(L, &call, *this);
+ }
+ }
+ };
+
+ template <typename T, typename Function, bool is_yielding, bool no_trampoline>
+ struct member_function {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef meta::function_return_t<function_type> return_type;
+ typedef meta::function_args_t<function_type> args_lists;
+ using traits_type = meta::bind_traits<function_type>;
+ function_type invocation;
+ T member;
+
+ template <typename... Args>
+ member_function(function_type f, Args&&... args) noexcept(
+ std::is_nothrow_constructible_v<function_type, function_type>&& std::is_nothrow_constructible_v<T, Args...>)
+ : invocation(std::move(f)), member(std::forward<Args>(args)...) {
+ }
+
+ static int call(lua_State* L, member_function& self)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ int nr = call_detail::call_wrapped<T, true, false, -1>(L, self.invocation, detail::unwrap(detail::deref(self.member)));
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ if constexpr (no_trampoline) {
+ return call(L, *this);
+ }
+ else {
+ return detail::trampoline(L, &call, *this);
+ }
+ }
+ };
+
+ template <typename T, typename Function, bool is_yielding, bool no_trampoline>
+ struct member_variable {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef typename meta::bind_traits<function_type>::return_type return_type;
+ typedef typename meta::bind_traits<function_type>::args_list args_lists;
+ function_type var;
+ T member;
+ typedef std::add_lvalue_reference_t<meta::unwrapped_t<std::remove_reference_t<decltype(detail::deref(member))>>> M;
+
+ template <typename... Args>
+ member_variable(function_type v, Args&&... args) noexcept(
+ std::is_nothrow_constructible_v<function_type, function_type>&& std::is_nothrow_constructible_v<T, Args...>)
+ : var(std::move(v)), member(std::forward<Args>(args)...) {
+ }
+
+ static int call(lua_State* L, member_variable& self) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ int nr;
+ {
+ M mem = detail::unwrap(detail::deref(self.member));
+ switch (lua_gettop(L)) {
+ case 0:
+ nr = call_detail::call_wrapped<T, true, false, -1>(L, self.var, mem);
+ break;
+ case 1:
+ nr = call_detail::call_wrapped<T, false, false, -1>(L, self.var, mem);
+ break;
+ default:
+ nr = luaL_error(L, "sol: incorrect number of arguments to member variable function");
+ break;
+ }
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ if constexpr (no_trampoline) {
+ return call(L, *this);
+ }
+ else {
+ return detail::trampoline(L, &call, *this);
+ }
+ }
+ };
+}} // namespace sol::function_detail
+
+// end of sol/function_types_stateful.hpp
+
+// beginning of sol/function_types_overloaded.hpp
+
+namespace sol { namespace function_detail {
+ template <int start_skew, typename... Functions>
+ struct overloaded_function {
+ typedef std::tuple<Functions...> overload_list;
+ typedef std::make_index_sequence<sizeof...(Functions)> indices;
+ overload_list overloads;
+
+ overloaded_function(overload_list set) : overloads(std::move(set)) {
+ }
+
+ overloaded_function(Functions... fxs) : overloads(fxs...) {
+ }
+
+ template <typename Fx, std::size_t I, typename... R, typename... Args>
+ static int call(types<Fx>, meta::index_value<I>, types<R...>, types<Args...>, lua_State* L, int, int, overload_list& ol) {
+ auto& func = std::get<I>(ol);
+ int nr = call_detail::call_wrapped<void, true, false, start_skew>(L, func);
+ return nr;
+ }
+
+ struct on_success {
+ template <typename... Args>
+ int operator()(Args&&... args) const {
+ return call(std::forward<Args>(args)...);
+ }
+ };
+
+ int operator()(lua_State* L) {
+ on_success call_obj {};
+ return call_detail::overload_match<Functions...>(call_obj, L, 1 + start_skew, overloads);
+ }
+ };
+}} // namespace sol::function_detail
+
+// end of sol/function_types_overloaded.hpp
+
+// beginning of sol/resolve.hpp
+
+namespace sol {
+
+#ifndef __clang__
+ // constexpr is fine for not-clang
+
+ namespace detail {
+ template <typename R, typename... Args, typename F, typename = std::invoke_result_t<meta::unqualified_t<F>, Args...>>
+ inline constexpr auto resolve_i(types<R(Args...)>, F&&) -> R (meta::unqualified_t<F>::*)(Args...) {
+ using Sig = R(Args...);
+ typedef meta::unqualified_t<F> Fu;
+ return static_cast<Sig Fu::*>(&Fu::operator());
+ }
+
+ template <typename F, typename U = meta::unqualified_t<F>>
+ inline constexpr auto resolve_f(std::true_type, F&& f)
+ -> decltype(resolve_i(types<meta::function_signature_t<decltype(&U::operator())>>(), std::forward<F>(f))) {
+ return resolve_i(types<meta::function_signature_t<decltype(&U::operator())>>(), std::forward<F>(f));
+ }
+
+ template <typename F>
+ inline constexpr void resolve_f(std::false_type, F&&) {
+ static_assert(meta::call_operator_deducible_v<F>, "Cannot use no-template-parameter call with an overloaded functor: specify the signature");
+ }
+
+ template <typename F, typename U = meta::unqualified_t<F>>
+ inline constexpr auto resolve_i(types<>, F&& f) -> decltype(resolve_f(meta::call_operator_deducible<U>(), std::forward<F>(f))) {
+ return resolve_f(meta::call_operator_deducible<U> {}, std::forward<F>(f));
+ }
+
+ template <typename... Args, typename F, typename R = std::invoke_result_t<F&, Args...>>
+ inline constexpr auto resolve_i(types<Args...>, F&& f) -> decltype(resolve_i(types<R(Args...)>(), std::forward<F>(f))) {
+ return resolve_i(types<R(Args...)>(), std::forward<F>(f));
+ }
+
+ template <typename Sig, typename C>
+ inline constexpr Sig C::*resolve_v(std::false_type, Sig C::*mem_func_ptr) {
+ return mem_func_ptr;
+ }
+
+ template <typename Sig, typename C>
+ inline constexpr Sig C::*resolve_v(std::true_type, Sig C::*mem_variable_ptr) {
+ return mem_variable_ptr;
+ }
+ } // namespace detail
+
+ template <typename... Args, typename R>
+ inline constexpr auto resolve(R fun_ptr(Args...)) -> R (*)(Args...) {
+ return fun_ptr;
+ }
+
+ template <typename Sig>
+ inline constexpr Sig* resolve(Sig* fun_ptr) {
+ return fun_ptr;
+ }
+
+ template <typename... Args, typename R, typename C>
+ inline constexpr auto resolve(R (C::*mem_ptr)(Args...)) -> R (C::*)(Args...) {
+ return mem_ptr;
+ }
+
+ template <typename Sig, typename C>
+ inline constexpr Sig C::*resolve(Sig C::*mem_ptr) {
+ return detail::resolve_v(std::is_member_object_pointer<Sig C::*>(), mem_ptr);
+ }
+
+ template <typename... Sig, typename F, meta::disable<std::is_function<meta::unqualified_t<F>>> = meta::enabler>
+ inline constexpr auto resolve(F&& f) -> decltype(detail::resolve_i(types<Sig...>(), std::forward<F>(f))) {
+ return detail::resolve_i(types<Sig...>(), std::forward<F>(f));
+ }
+#else
+
+ // Clang has distinct problems with constexpr arguments,
+ // so don't use the constexpr versions inside of clang.
+
+ namespace detail {
+ template <typename R, typename... Args, typename F, typename = std::invoke_result_t<meta::unqualified_t<F>, Args...>>
+ inline auto resolve_i(types<R(Args...)>, F&&) -> R (meta::unqualified_t<F>::*)(Args...) {
+ using Sig = R(Args...);
+ typedef meta::unqualified_t<F> Fu;
+ return static_cast<Sig Fu::*>(&Fu::operator());
+ }
+
+ template <typename F, typename U = meta::unqualified_t<F>>
+ inline auto resolve_f(std::true_type, F&& f)
+ -> decltype(resolve_i(types<meta::function_signature_t<decltype(&U::operator())>>(), std::forward<F>(f))) {
+ return resolve_i(types<meta::function_signature_t<decltype(&U::operator())>>(), std::forward<F>(f));
+ }
+
+ template <typename F>
+ inline void resolve_f(std::false_type, F&&) {
+ static_assert(meta::call_operator_deducible_v<F>, "Cannot use no-template-parameter call with an overloaded functor: specify the signature");
+ }
+
+ template <typename F, typename U = meta::unqualified_t<F>>
+ inline auto resolve_i(types<>, F&& f) -> decltype(resolve_f(meta::call_operator_deducible<U>(), std::forward<F>(f))) {
+ return resolve_f(meta::call_operator_deducible<U> {}, std::forward<F>(f));
+ }
+
+ template <typename... Args, typename F, typename R = std::invoke_result_t<F&, Args...>>
+ inline auto resolve_i(types<Args...>, F&& f) -> decltype(resolve_i(types<R(Args...)>(), std::forward<F>(f))) {
+ return resolve_i(types<R(Args...)>(), std::forward<F>(f));
+ }
+
+ template <typename Sig, typename C>
+ inline Sig C::*resolve_v(std::false_type, Sig C::*mem_func_ptr) {
+ return mem_func_ptr;
+ }
+
+ template <typename Sig, typename C>
+ inline Sig C::*resolve_v(std::true_type, Sig C::*mem_variable_ptr) {
+ return mem_variable_ptr;
+ }
+ } // namespace detail
+
+ template <typename... Args, typename R>
+ inline auto resolve(R fun_ptr(Args...)) -> R (*)(Args...) {
+ return fun_ptr;
+ }
+
+ template <typename Sig>
+ inline Sig* resolve(Sig* fun_ptr) {
+ return fun_ptr;
+ }
+
+ template <typename... Args, typename R, typename C>
+ inline auto resolve(R (C::*mem_ptr)(Args...)) -> R (C::*)(Args...) {
+ return mem_ptr;
+ }
+
+ template <typename Sig, typename C>
+ inline Sig C::*resolve(Sig C::*mem_ptr) {
+ return detail::resolve_v(std::is_member_object_pointer<Sig C::*>(), mem_ptr);
+ }
+
+ template <typename... Sig, typename F>
+ inline auto resolve(F&& f) -> decltype(detail::resolve_i(types<Sig...>(), std::forward<F>(f))) {
+ return detail::resolve_i(types<Sig...>(), std::forward<F>(f));
+ }
+
+#endif
+
+} // namespace sol
+
+// end of sol/resolve.hpp
+
+namespace sol {
+ namespace function_detail {
+ template <typename T>
+ struct class_indicator {
+ using type = T;
+ };
+
+ struct call_indicator { };
+
+ template <bool yielding>
+ int lua_c_wrapper(lua_State* L) {
+ lua_CFunction cf = lua_tocfunction(L, lua_upvalueindex(2));
+ int nr = cf(L);
+ if constexpr (yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ template <bool yielding>
+ int lua_c_noexcept_wrapper(lua_State* L) noexcept {
+ detail::lua_CFunction_noexcept cf = reinterpret_cast<detail::lua_CFunction_noexcept>(lua_tocfunction(L, lua_upvalueindex(2)));
+ int nr = cf(L);
+ if constexpr (yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ struct c_function_invocation { };
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select(lua_State* L, Fx&& fx, Args&&... args);
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select_set_fx(lua_State* L, Args&&... args) {
+ lua_CFunction freefunc = no_trampoline ? function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>
+ : detail::static_trampoline<function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<Fx>>(L, std::forward<Args>(args)...);
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename R, typename... A, typename Fx, typename... Args>
+ void select_convertible(types<R(A...)>, lua_State* L, Fx&& fx, Args&&... args) {
+ using dFx = std::decay_t<meta::unwrap_unqualified_t<Fx>>;
+ using fx_ptr_t = R (*)(A...);
+ constexpr bool is_convertible = std::is_convertible_v<dFx, fx_ptr_t>;
+ if constexpr (is_convertible) {
+ fx_ptr_t fxptr = detail::unwrap(std::forward<Fx>(fx));
+ select<is_yielding, no_trampoline>(L, std::move(fxptr), std::forward<Args>(args)...);
+ }
+ else {
+ using F = function_detail::functor_function<dFx, false, true>;
+ select_set_fx<is_yielding, no_trampoline, F>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select_convertible(types<>, lua_State* L, Fx&& fx, Args&&... args) {
+ typedef meta::function_signature_t<meta::unwrap_unqualified_t<Fx>> Sig;
+ select_convertible<is_yielding, no_trampoline>(types<Sig>(), L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select_member_variable(lua_State* L, Fx&& fx, Args&&... args) {
+ using uFx = meta::unqualified_t<Fx>;
+ if constexpr (sizeof...(Args) < 1) {
+ using C = typename meta::bind_traits<uFx>::object_type;
+ lua_CFunction freefunc = &function_detail::upvalue_this_member_variable<C, Fx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::stack_detail::push_as_upvalues(L, fx);
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else if constexpr (sizeof...(Args) < 2) {
+ using Tu = typename meta::meta_detail::unqualified_non_alias<Args...>::type;
+ constexpr bool is_reference = meta::is_specialization_of_v<Tu, std::reference_wrapper> || std::is_pointer_v<Tu>;
+ if constexpr (meta::is_specialization_of_v<Tu, function_detail::class_indicator>) {
+ lua_CFunction freefunc
+ = &function_detail::upvalue_this_member_variable<typename Tu::type, Fx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::stack_detail::push_as_upvalues(L, fx);
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else if constexpr (is_reference) {
+ typedef std::decay_t<Fx> dFx;
+ dFx memfxptr(std::forward<Fx>(fx));
+ auto userptr = detail::ptr(std::forward<Args>(args)...);
+ lua_CFunction freefunc = &function_detail::upvalue_member_variable<std::decay_t<decltype(*userptr)>,
+ meta::unqualified_t<Fx>>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::stack_detail::push_as_upvalues(L, memfxptr);
+ upvalues += stack::push(L, static_cast<void const*>(userptr));
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else {
+ using clean_fx = std::remove_pointer_t<std::decay_t<Fx>>;
+ using F = function_detail::member_variable<Tu, clean_fx, is_yielding, no_trampoline>;
+ select_set_fx<is_yielding, no_trampoline, F>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+ else {
+ using C = typename meta::bind_traits<uFx>::object_type;
+ using clean_fx = std::remove_pointer_t<std::decay_t<Fx>>;
+ using F = function_detail::member_variable<C, clean_fx, is_yielding, no_trampoline>;
+ select_set_fx<is_yielding, no_trampoline, F>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename T, typename... Args>
+ void select_member_function_with(lua_State* L, Fx&& fx, T&& obj, Args&&... args) {
+ using dFx = std::decay_t<Fx>;
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::is_specialization_of_v<Tu, function_detail::class_indicator>) {
+ (void)obj;
+ using C = typename Tu::type;
+ lua_CFunction freefunc = &function_detail::upvalue_this_member_function<C, dFx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<dFx>>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else {
+ constexpr bool is_reference = meta::is_specialization_of_v<Tu, std::reference_wrapper> || std::is_pointer_v<Tu>;
+ if constexpr (is_reference) {
+ auto userptr = detail::ptr(std::forward<T>(obj));
+ lua_CFunction freefunc
+ = &function_detail::upvalue_member_function<std::decay_t<decltype(*userptr)>, dFx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<dFx>>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ upvalues += stack::push(L, lightuserdata_value(static_cast<void*>(userptr)));
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else {
+ using F = function_detail::member_function<Tu, dFx, is_yielding, no_trampoline>;
+ select_set_fx<is_yielding, no_trampoline, F>(L, std::forward<Fx>(fx), std::forward<T>(obj), std::forward<Args>(args)...);
+ }
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select_member_function(lua_State* L, Fx&& fx, Args&&... args) {
+ using dFx = std::decay_t<Fx>;
+ if constexpr (sizeof...(Args) < 1) {
+ using C = typename meta::bind_traits<meta::unqualified_t<Fx>>::object_type;
+ lua_CFunction freefunc = &function_detail::upvalue_this_member_function<C, dFx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<dFx>>(L, std::forward<Fx>(fx));
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else {
+ select_member_function_with<is_yielding, no_trampoline>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select(lua_State* L, Fx&& fx, Args&&... args) {
+ using uFx = meta::unqualified_t<Fx>;
+ if constexpr (is_lua_reference_v<uFx>) {
+ // TODO: hoist into lambda in this case for yielding???
+ stack::push(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ else if constexpr (is_lua_c_function_v<uFx>) {
+ if constexpr (no_trampoline) {
+ if (is_yielding) {
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push(L, std::forward<Fx>(fx));
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ if constexpr (std::is_nothrow_invocable_r_v<int, uFx, lua_State*>) {
+ detail::lua_CFunction_noexcept cf = &lua_c_noexcept_wrapper<true>;
+ lua_pushcclosure(L, reinterpret_cast<lua_CFunction>(cf), upvalues);
+ }
+ else
+#endif
+ {
+ lua_CFunction cf = &function_detail::lua_c_wrapper<true>;
+ lua_pushcclosure(L, cf, upvalues);
+ }
+ }
+ else {
+ lua_pushcclosure(L, std::forward<Fx>(fx), 0);
+ }
+ }
+ else {
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push(L, std::forward<Fx>(fx));
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ if constexpr (std::is_nothrow_invocable_r_v<int, uFx, lua_State*>) {
+ detail::lua_CFunction_noexcept cf = &lua_c_noexcept_wrapper<is_yielding>;
+ lua_pushcclosure(L, reinterpret_cast<lua_CFunction>(cf), upvalues);
+ }
+ else {
+ lua_CFunction cf = &function_detail::lua_c_wrapper<is_yielding>;
+ lua_pushcclosure(L, cf, upvalues);
+ }
+#else
+ lua_CFunction cf = &function_detail::lua_c_wrapper<is_yielding>;
+ lua_pushcclosure(L, cf, upvalues);
+#endif
+ }
+ }
+ else if constexpr (std::is_function_v<std::remove_pointer_t<uFx>>) {
+ std::decay_t<Fx> target(std::forward<Fx>(fx), std::forward<Args>(args)...);
+ lua_CFunction freefunc = &function_detail::upvalue_free_function<Fx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::stack_detail::push_as_upvalues(L, target);
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else if constexpr (std::is_member_function_pointer_v<uFx>) {
+ select_member_function<is_yielding, no_trampoline>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::is_member_object_v<uFx>) {
+ select_member_variable<is_yielding, no_trampoline>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ else {
+ select_convertible<is_yielding, no_trampoline>(types<>(), L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+ } // namespace function_detail
+
+ namespace stack {
+ template <typename... Sigs>
+ struct unqualified_pusher<function_sig<Sigs...>> {
+ template <bool is_yielding, typename Arg0, typename... Args>
+ static int push_yielding(lua_State* L, Arg0&& arg0, Args&&... args) {
+ if constexpr (meta::is_specialization_of_v<meta::unqualified_t<Arg0>, std::function>) {
+ if constexpr (is_yielding) {
+ return stack::push<meta::unqualified_t<Arg0>>(L, detail::yield_tag, std::forward<Arg0>(arg0), std::forward<Args>(args)...);
+ }
+ else {
+ return stack::push(L, std::forward<Arg0>(arg0), std::forward<Args>(args)...);
+ }
+ }
+ else {
+ function_detail::select<is_yielding, false>(L, std::forward<Arg0>(arg0), std::forward<Args>(args)...);
+ return 1;
+ }
+ }
+
+ template <typename Arg0, typename... Args>
+ static int push(lua_State* L, Arg0&& arg0, Args&&... args) {
+ if constexpr (std::is_same_v<meta::unqualified_t<Arg0>, detail::yield_tag_t>) {
+ push_yielding<true>(L, std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::is_specialization_of_v<meta::unqualified_t<Arg0>, yielding_t>) {
+ push_yielding<true>(L, std::forward<Arg0>(arg0).func, std::forward<Args>(args)...);
+ }
+ else {
+ push_yielding<false>(L, std::forward<Arg0>(arg0), std::forward<Args>(args)...);
+ }
+ return 1;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<yielding_t<T>> {
+ template <typename... Args>
+ static int push(lua_State* L, const yielding_t<T>& f, Args&&... args) {
+ if constexpr (meta::is_specialization_of_v<meta::unqualified_t<T>, std::function>) {
+ return stack::push<T>(L, detail::yield_tag, f.func, std::forward<Args>(args)...);
+ }
+ else {
+ function_detail::select<true, false>(L, f.func, std::forward<Args>(args)...);
+ return 1;
+ }
+ }
+
+ template <typename... Args>
+ static int push(lua_State* L, yielding_t<T>&& f, Args&&... args) {
+ if constexpr (meta::is_specialization_of_v<meta::unqualified_t<T>, std::function>) {
+ return stack::push<T>(L, detail::yield_tag, std::move(f.func), std::forward<Args>(args)...);
+ }
+ else {
+ function_detail::select<true, false>(L, std::move(f.func), std::forward<Args>(args)...);
+ return 1;
+ }
+ }
+ };
+
+ template <typename T, typename... Args>
+ struct unqualified_pusher<function_arguments<T, Args...>> {
+ template <std::size_t... I, typename FP>
+ static int push_func(std::index_sequence<I...>, lua_State* L, FP&& fp) {
+ return stack::push<T>(L, std::get<I>(std::forward<FP>(fp).arguments)...);
+ }
+
+ static int push(lua_State* L, const function_arguments<T, Args...>& fp) {
+ return push_func(std::make_index_sequence<sizeof...(Args)>(), L, fp);
+ }
+
+ static int push(lua_State* L, function_arguments<T, Args...>&& fp) {
+ return push_func(std::make_index_sequence<sizeof...(Args)>(), L, std::move(fp));
+ }
+ };
+
+ template <typename Signature>
+ struct unqualified_pusher<std::function<Signature>> {
+ using TargetFunctor = function_detail::functor_function<std::function<Signature>, false, true>;
+
+ static int push(lua_State* L, detail::yield_tag_t, const std::function<Signature>& fx) {
+ if (fx) {
+ function_detail::select_set_fx<true, false, TargetFunctor>(L, fx);
+ return 1;
+ }
+ return stack::push(L, lua_nil);
+ }
+
+ static int push(lua_State* L, detail::yield_tag_t, std::function<Signature>&& fx) {
+ if (fx) {
+ function_detail::select_set_fx<true, false, TargetFunctor>(L, std::move(fx));
+ return 1;
+ }
+ return stack::push(L, lua_nil);
+ }
+
+ static int push(lua_State* L, const std::function<Signature>& fx) {
+ if (fx) {
+ function_detail::select_set_fx<false, false, TargetFunctor>(L, fx);
+ return 1;
+ }
+ return stack::push(L, lua_nil);
+ }
+
+ static int push(lua_State* L, std::function<Signature>&& fx) {
+ if (fx) {
+ function_detail::select_set_fx<false, false, TargetFunctor>(L, std::move(fx));
+ return 1;
+ }
+ return stack::push(L, lua_nil);
+ }
+ };
+
+ template <typename Signature>
+ struct unqualified_pusher<Signature, std::enable_if_t<meta::is_member_object_or_function_v<Signature>>> {
+ template <typename... Args>
+ static int push(lua_State* L, Args&&... args) {
+ function_detail::select<false, false>(L, std::forward<Args>(args)...);
+ return 1;
+ }
+ };
+
+ template <typename Signature>
+ struct unqualified_pusher<Signature,
+ std::enable_if_t<meta::all<std::is_function<std::remove_pointer_t<Signature>>, meta::neg<std::is_same<Signature, lua_CFunction>>,
+ meta::neg<std::is_same<Signature, std::remove_pointer_t<lua_CFunction>>>
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ ,
+ meta::neg<std::is_same<Signature, detail::lua_CFunction_noexcept>>,
+ meta::neg<std::is_same<Signature, std::remove_pointer_t<detail::lua_CFunction_noexcept>>>
+#endif // noexcept function types
+ >::value>> {
+ template <typename F>
+ static int push(lua_State* L, F&& f) {
+ function_detail::select<false, true>(L, std::forward<F>(f));
+ return 1;
+ }
+ };
+
+ template <typename... Functions>
+ struct unqualified_pusher<overload_set<Functions...>> {
+ static int push(lua_State* L, overload_set<Functions...>&& set) {
+ using F = function_detail::overloaded_function<0, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, std::move(set.functions));
+ return 1;
+ }
+
+ static int push(lua_State* L, const overload_set<Functions...>& set) {
+ using F = function_detail::overloaded_function<0, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, set.functions);
+ return 1;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<protect_t<T>> {
+ static int push(lua_State* L, protect_t<T>&& pw) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, protect_t<T>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<protect_t<T>>>(L, std::move(pw.value));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, const protect_t<T>& pw) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, protect_t<T>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<protect_t<T>>>(L, pw.value);
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename F, typename G>
+ struct unqualified_pusher<property_wrapper<F, G>> {
+ static int push(lua_State* L, property_wrapper<F, G>&& pw) {
+ if constexpr (std::is_void_v<F>) {
+ return stack::push(L, std::move(pw.write()));
+ }
+ else if constexpr (std::is_void_v<G>) {
+ return stack::push(L, std::move(pw.read()));
+ }
+ else {
+ return stack::push(L, overload(std::move(pw.read()), std::move(pw.write())));
+ }
+ }
+
+ static int push(lua_State* L, const property_wrapper<F, G>& pw) {
+ if constexpr (std::is_void_v<F>) {
+ return stack::push(L, pw.write);
+ }
+ else if constexpr (std::is_void_v<G>) {
+ return stack::push(L, pw.read);
+ }
+ else {
+ return stack::push(L, overload(pw.read, pw.write));
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<var_wrapper<T>> {
+ static int push(lua_State* L, var_wrapper<T>&& vw) {
+ return stack::push(L, std::move(vw.value()));
+ }
+ static int push(lua_State* L, const var_wrapper<T>& vw) {
+ return stack::push(L, vw.value());
+ }
+ };
+
+ template <typename... Functions>
+ struct unqualified_pusher<factory_wrapper<Functions...>> {
+ static int push(lua_State* L, const factory_wrapper<Functions...>& fw) {
+ using F = function_detail::overloaded_function<0, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, fw.functions);
+ return 1;
+ }
+
+ static int push(lua_State* L, factory_wrapper<Functions...>&& fw) {
+ using F = function_detail::overloaded_function<0, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, std::move(fw.functions));
+ return 1;
+ }
+
+ static int push(lua_State* L, const factory_wrapper<Functions...>& fw, function_detail::call_indicator) {
+ using F = function_detail::overloaded_function<1, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, fw.functions);
+ return 1;
+ }
+
+ static int push(lua_State* L, factory_wrapper<Functions...>&& fw, function_detail::call_indicator) {
+ using F = function_detail::overloaded_function<1, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, std::move(fw.functions));
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<no_construction> {
+ static int push(lua_State* L, no_construction) {
+ lua_CFunction cf = &function_detail::no_construction_error;
+ return stack::push(L, cf);
+ }
+
+ static int push(lua_State* L, no_construction c, function_detail::call_indicator) {
+ return push(L, c);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<detail::tagged<T, no_construction>> {
+ static int push(lua_State* L, detail::tagged<T, no_construction>) {
+ lua_CFunction cf = &function_detail::no_construction_error;
+ return stack::push(L, cf);
+ }
+
+ static int push(lua_State* L, no_construction, function_detail::call_indicator) {
+ lua_CFunction cf = &function_detail::no_construction_error;
+ return stack::push(L, cf);
+ }
+ };
+
+ template <typename T, typename... Lists>
+ struct unqualified_pusher<detail::tagged<T, constructor_list<Lists...>>> {
+ static int push(lua_State* L, detail::tagged<T, constructor_list<Lists...>>) {
+ lua_CFunction cf = call_detail::construct<T, detail::default_safe_function_calls, true, Lists...>;
+ return stack::push(L, cf);
+ }
+
+ static int push(lua_State* L, constructor_list<Lists...>) {
+ lua_CFunction cf = call_detail::construct<T, detail::default_safe_function_calls, true, Lists...>;
+ return stack::push(L, cf);
+ }
+ };
+
+ template <typename L0, typename... Lists>
+ struct unqualified_pusher<constructor_list<L0, Lists...>> {
+ typedef constructor_list<L0, Lists...> cl_t;
+ static int push(lua_State* L, cl_t cl) {
+ typedef typename meta::bind_traits<L0>::return_type T;
+ return stack::push<detail::tagged<T, cl_t>>(L, cl);
+ }
+ };
+
+ template <typename T, typename... Fxs>
+ struct unqualified_pusher<detail::tagged<T, constructor_wrapper<Fxs...>>> {
+ static int push(lua_State* L, detail::tagged<T, constructor_wrapper<Fxs...>>&& c) {
+ return push(L, std::move(c.value()));
+ }
+
+ static int push(lua_State* L, const detail::tagged<T, const constructor_wrapper<Fxs...>>& c) {
+ return push(L, c.value());
+ }
+
+ static int push(lua_State* L, constructor_wrapper<Fxs...>&& c) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, constructor_wrapper<Fxs...>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<constructor_wrapper<Fxs...>>>(L, std::move(c));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, const constructor_wrapper<Fxs...>& c) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, constructor_wrapper<Fxs...>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<constructor_wrapper<Fxs...>>>(L, c);
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename F, typename... Fxs>
+ struct unqualified_pusher<constructor_wrapper<F, Fxs...>> {
+ static int push(lua_State* L, const constructor_wrapper<F, Fxs...>& c) {
+ typedef typename meta::bind_traits<F>::template arg_at<0> arg0;
+ typedef meta::unqualified_t<std::remove_pointer_t<arg0>> T;
+ return stack::push<detail::tagged<T, constructor_wrapper<F, Fxs...>>>(L, c);
+ }
+
+ static int push(lua_State* L, constructor_wrapper<F, Fxs...>&& c) {
+ typedef typename meta::bind_traits<F>::template arg_at<0> arg0;
+ typedef meta::unqualified_t<std::remove_pointer_t<arg0>> T;
+ return stack::push<detail::tagged<T, constructor_wrapper<F, Fxs...>>>(L, std::move(c));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<detail::tagged<T, destructor_wrapper<void>>> {
+ static int push(lua_State* L, destructor_wrapper<void>) {
+ lua_CFunction cf = detail::usertype_alloc_destroy<T>;
+ return stack::push(L, cf);
+ }
+ };
+
+ template <typename T, typename Fx>
+ struct unqualified_pusher<detail::tagged<T, destructor_wrapper<Fx>>> {
+ static int push(lua_State* L, destructor_wrapper<Fx>&& c) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, destructor_wrapper<Fx>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<destructor_wrapper<Fx>>>(L, std::move(c));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, const destructor_wrapper<Fx>& c) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, destructor_wrapper<Fx>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<destructor_wrapper<Fx>>>(L, c);
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename Fx>
+ struct unqualified_pusher<destructor_wrapper<Fx>> {
+ static int push(lua_State* L, destructor_wrapper<Fx>&& c) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, destructor_wrapper<Fx>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<destructor_wrapper<Fx>>>(L, std::move(c));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, const destructor_wrapper<Fx>& c) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, destructor_wrapper<Fx>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<destructor_wrapper<Fx>>>(L, c);
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename F, typename... Policies>
+ struct unqualified_pusher<policy_wrapper<F, Policies...>> {
+ using P = policy_wrapper<F, Policies...>;
+
+ static int push(lua_State* L, const P& p) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, P, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<P>>(L, p);
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, P&& p) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, P, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<P>>(L, std::move(p));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename T, typename F, typename... Policies>
+ struct unqualified_pusher<detail::tagged<T, policy_wrapper<F, Policies...>>> {
+ using P = policy_wrapper<F, Policies...>;
+ using Tagged = detail::tagged<T, P>;
+
+ static int push(lua_State* L, const Tagged& p) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, P, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<P>>(L, p.value());
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, Tagged&& p) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, P, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<P>>(L, std::move(p.value()));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<push_invoke_t<T>> {
+ static int push(lua_State* L, push_invoke_t<T>&& pi) {
+ if constexpr (std::is_invocable_v<std::add_rvalue_reference_t<T>, lua_State*>) {
+ return stack::push(L, std::move(pi.value())(L));
+ }
+ else {
+ return stack::push(L, std::move(pi.value())());
+ }
+ }
+
+ static int push(lua_State* L, const push_invoke_t<T>& pi) {
+ if constexpr (std::is_invocable_v<const T, lua_State*>) {
+ return stack::push(L, pi.value()(L));
+ }
+ else {
+ return stack::push(L, pi.value()());
+ }
+ }
+ };
+
+ namespace stack_detail {
+ template <typename Function, typename Handler>
+ bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept {
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ tracking.use(1);
+ bool success = lua_iscfunction(L, index) == 1;
+ if (success) {
+ // there must be at LEAST 2 upvalues; otherwise, we didn't serialize it.
+ const char* upvalue_name = lua_getupvalue(L, index, 2);
+ lua_pop(L, 1);
+ success = upvalue_name != nullptr;
+ }
+ if (!success) {
+ // expected type, actual type
+ handler(
+ L, index, type::function, type_of(L, index), "type must be a Lua C Function gotten from a function pointer serialized by sol2");
+ }
+ return success;
+#else
+ (void)L;
+ (void)index;
+ (void)handler;
+ (void)tracking;
+ return false;
+#endif
+ }
+
+ template <typename Function>
+ Function* get_function_pointer(lua_State* L, int index, record& tracking) noexcept {
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ tracking.use(1);
+ auto udata = stack::stack_detail::get_as_upvalues_using_function<Function*>(L, index);
+ Function* fx = udata.first;
+ return fx;
+#else
+ (void)L;
+ (void)index;
+ (void)tracking;
+ static_assert(meta::meta_detail::always_true<Function>::value,
+#if SOL_IS_DEFAULT_OFF(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ "You are attempting to retrieve a function pointer type. "
+ "This is inherently unsafe in sol2. In order to do this, you must turn on the "
+ "SOL_GET_FUNCTION_POINTER_UNSAFE configuration macro, as detailed in the documentation. "
+ "Please be careful!"
+#else
+ "You are attempting to retrieve a function pointer type. "
+ "You explicitly turned off the ability to do this by defining "
+ "SOL_GET_FUNCTION_POINTER_UNSAFE or similar to be off. "
+ "Please reconsider this!"
+#endif
+ );
+ return nullptr;
+#endif
+ }
+ } // namespace stack_detail
+ } // namespace stack
+} // namespace sol
+
+// end of sol/function_types.hpp
+
+// beginning of sol/dump_handler.hpp
+
+#include <cstdint>
+#include <exception>
+
+namespace sol {
+
+ class dump_error : public error {
+ private:
+ int m_ec;
+
+ public:
+ dump_error(int error_code_) : error("dump returned non-zero error of " + std::to_string(error_code_)), m_ec(error_code_) {
+ }
+
+ int error_code() const {
+ return m_ec;
+ }
+ };
+
+ inline int dump_pass_on_error(lua_State* L_, int result_code, lua_Writer writer_function, void* userdata_pointer_, bool strip) {
+ (void)L_;
+ (void)writer_function;
+ (void)userdata_pointer_;
+ (void)strip;
+ return result_code;
+ }
+
+ inline int dump_panic_on_error(lua_State* L_, int result_code, lua_Writer writer_function, void* userdata_pointer_, bool strip) {
+ (void)L_;
+ (void)writer_function;
+ (void)userdata_pointer_;
+ (void)strip;
+ return luaL_error(L_, "a non-zero error code (%d) was returned by the lua_Writer for the dump function", result_code);
+ }
+
+ inline int dump_throw_on_error(lua_State* L_, int result_code, lua_Writer writer_function, void* userdata_pointer_, bool strip) {
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ return dump_panic_on_error(L_, result_code, writer_function, userdata_pointer_, strip);
+#else
+ (void)L_;
+ (void)writer_function;
+ (void)userdata_pointer_;
+ (void)strip;
+ throw dump_error(result_code);
+#endif // no exceptions stuff
+ }
+
+} // namespace sol
+
+// end of sol/dump_handler.hpp
+
+#include <cstdint>
+
+namespace sol {
+ template <typename ref_t, bool aligned = false>
+ class basic_function : public basic_object<ref_t> {
+ private:
+ using base_t = basic_object<ref_t>;
+
+ void luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount) const {
+ lua_call(lua_state(), static_cast<int>(argcount), static_cast<int>(resultcount));
+ }
+
+ template <std::size_t... I, typename... Ret>
+ auto invoke(types<Ret...>, std::index_sequence<I...>, std::ptrdiff_t n) const {
+ luacall(n, lua_size<std::tuple<Ret...>>::value);
+ return stack::pop<std::tuple<Ret...>>(lua_state());
+ }
+
+ template <std::size_t I, typename Ret, meta::enable<meta::neg<std::is_void<Ret>>> = meta::enabler>
+ Ret invoke(types<Ret>, std::index_sequence<I>, std::ptrdiff_t n) const {
+ luacall(n, lua_size<Ret>::value);
+ return stack::pop<Ret>(lua_state());
+ }
+
+ template <std::size_t I>
+ void invoke(types<void>, std::index_sequence<I>, std::ptrdiff_t n) const {
+ luacall(n, 0);
+ }
+
+ unsafe_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n) const {
+ int stacksize = lua_gettop(lua_state());
+ int firstreturn = (std::max)(1, stacksize - static_cast<int>(n));
+ luacall(n, LUA_MULTRET);
+ int poststacksize = lua_gettop(lua_state());
+ int returncount = poststacksize - (firstreturn - 1);
+ return unsafe_function_result(lua_state(), firstreturn, returncount);
+ }
+
+ public:
+ using base_t::lua_state;
+
+ basic_function() = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_function>>, meta::neg<std::is_same<base_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_function(T&& r) noexcept : base_t(std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_function<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_function>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+ basic_function(const basic_function&) = default;
+ basic_function& operator=(const basic_function&) = default;
+ basic_function(basic_function&&) = default;
+ basic_function& operator=(basic_function&&) = default;
+ basic_function(const stack_reference& r) : basic_function(r.lua_state(), r.stack_index()) {
+ }
+ basic_function(stack_reference&& r) : basic_function(r.lua_state(), r.stack_index()) {
+ }
+ basic_function(lua_nil_t n) : base_t(n) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_function(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_function>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_function(lua_State* L, int index = -1) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_function>(L, index, handler);
+#endif // Safety
+ }
+ basic_function(lua_State* L, ref_index index) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_function>(lua_state(), -1, handler);
+#endif // Safety
+ }
+
+ template <typename Fx>
+ int dump(lua_Writer writer, void* userdata, bool strip, Fx&& on_error) const {
+ this->push();
+ auto ppn = stack::push_popper_n<false>(this->lua_state(), 1);
+ int r = lua_dump(this->lua_state(), writer, userdata, strip ? 1 : 0);
+ if (r != 0) {
+ return on_error(this->lua_state(), r, writer, userdata, strip);
+ }
+ return r;
+ }
+
+ int dump(lua_Writer writer, void* userdata, bool strip = false) const {
+ return dump(writer, userdata, strip, &dump_throw_on_error);
+ }
+
+ template <typename Container = bytecode>
+ Container dump() const {
+ Container bc;
+ (void)dump(static_cast<lua_Writer>(&basic_insert_dump_writer<Container>), static_cast<void*>(&bc), false, &dump_panic_on_error);
+ return bc;
+ }
+
+ template <typename Container = bytecode, typename Fx>
+ Container dump(Fx&& on_error) const {
+ Container bc;
+ (void)dump(static_cast<lua_Writer>(&basic_insert_dump_writer<Container>), static_cast<void*>(&bc), false, std::forward<Fx>(on_error));
+ return bc;
+ }
+
+ template <typename... Args>
+ unsafe_function_result operator()(Args&&... args) const {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) operator()(types<Ret...>, Args&&... args) const {
+ return call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) const {
+ if (!aligned) {
+ base_t::push();
+ }
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), static_cast<std::ptrdiff_t>(pushcount));
+ }
+ };
+} // namespace sol
+
+// end of sol/unsafe_function.hpp
+
+// beginning of sol/protected_function.hpp
+
+// beginning of sol/protected_handler.hpp
+
+#include <cstdint>
+
+namespace sol { namespace detail {
+ inline const char (&default_handler_name())[9] {
+ static const char name[9] = "sol.\xF0\x9F\x94\xA9";
+ return name;
+ }
+
+ template <bool ShouldPush, typename Target = reference>
+ struct protected_handler {
+ lua_State* m_L;
+ const Target& target;
+ int stack_index;
+
+ protected_handler(std::false_type, lua_State* L_, const Target& target_) : m_L(L_), target(target_), stack_index(0) {
+ if (ShouldPush) {
+ stack_index = lua_gettop(L_) + 1;
+ target.push(L_);
+ }
+ }
+
+ protected_handler(std::true_type, lua_State* L_, const Target& target_) : m_L(L_), target(target_), stack_index(0) {
+ if (ShouldPush) {
+ stack_index = target.stack_index();
+ }
+ }
+
+ protected_handler(lua_State* L_, const Target& target_) : protected_handler(meta::boolean<is_stack_based_v<Target>>(), L_, target_) {
+ }
+
+ bool valid() const noexcept {
+ return ShouldPush;
+ }
+
+ ~protected_handler() {
+ if constexpr (!is_stack_based_v<Target>) {
+ if (stack_index != 0) {
+ lua_remove(m_L, stack_index);
+ }
+ }
+ }
+ };
+
+ template <typename Base, typename T>
+ inline basic_function<Base> force_cast(T& p) {
+ return p;
+ }
+
+ template <typename Reference, bool IsMainReference = false>
+ inline Reference get_default_handler(lua_State* L_) {
+ if (is_stack_based_v<Reference> || L_ == nullptr)
+ return Reference(L_, lua_nil);
+ L_ = IsMainReference ? main_thread(L_, L_) : L_;
+ lua_getglobal(L_, default_handler_name());
+ auto pp = stack::pop_n(L_, 1);
+ return Reference(L_, -1);
+ }
+
+ template <typename T>
+ inline void set_default_handler(lua_State* L, const T& ref) {
+ if (L == nullptr) {
+ return;
+ }
+ if (!ref.valid()) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushnil(L);
+ lua_setglobal(L, default_handler_name());
+ }
+ else {
+ ref.push(L);
+ lua_setglobal(L, default_handler_name());
+ }
+ }
+}} // namespace sol::detail
+
+// end of sol/protected_handler.hpp
+
+#include <cstdint>
+#include <algorithm>
+
+namespace sol {
+
+ namespace detail {
+ template <bool ShouldPush_, typename Handler_>
+ inline void handle_protected_exception(
+ lua_State* L_, optional<const std::exception&> maybe_ex, const char* error, detail::protected_handler<ShouldPush_, Handler_>& handler_) {
+ handler_.stack_index = 0;
+ if (ShouldPush_) {
+ handler_.target.push(L_);
+ detail::call_exception_handler(L_, maybe_ex, error);
+ lua_call(L_, 1, 1);
+ }
+ else {
+ detail::call_exception_handler(L_, maybe_ex, error);
+ }
+ }
+ } // namespace detail
+
+ template <typename Reference, bool Aligned = false, typename Handler = reference>
+ class basic_protected_function : public basic_object<Reference> {
+ private:
+ using base_t = basic_object<Reference>;
+ using handler_t = Handler;
+ inline static constexpr bool is_stack_handler_v = is_stack_based_v<handler_t>;
+
+ basic_protected_function(std::true_type, const basic_protected_function& other_) noexcept
+ : base_t(other_), m_error_handler(other_.m_error_handler.copy(lua_state())) {
+ }
+
+ basic_protected_function(std::false_type, const basic_protected_function& other_) noexcept : base_t(other_), m_error_handler(other_.m_error_handler) {
+ }
+
+ public:
+ basic_protected_function() = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_protected_function>>,
+ meta::neg<std::is_base_of<proxy_base_tag, meta::unqualified_t<T>>>, meta::neg<std::is_same<base_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_protected_function(T&& r) noexcept : base_t(std::forward<T>(r)), m_error_handler(get_default_handler(r.lua_state())) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_function<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+ basic_protected_function(const basic_protected_function& other_) noexcept
+ : basic_protected_function(meta::boolean<is_stateless_lua_reference_v<Handler>>(), other_) {
+ }
+ basic_protected_function& operator=(const basic_protected_function& other_) {
+ base_t::operator=(other_);
+ if constexpr (is_stateless_lua_reference_v<Handler>) {
+ m_error_handler.copy_assign(lua_state(), other_.m_error_handler);
+ }
+ else {
+ m_error_handler = other_.m_error_handler;
+ }
+ return *this;
+ }
+ basic_protected_function(basic_protected_function&&) = default;
+ basic_protected_function& operator=(basic_protected_function&&) = default;
+ basic_protected_function(const basic_function<base_t>& b) : basic_protected_function(b, get_default_handler(b.lua_state())) {
+ }
+ basic_protected_function(basic_function<base_t>&& b) : basic_protected_function(std::move(b), get_default_handler(b.lua_state())) {
+ }
+ basic_protected_function(const basic_function<base_t>& b, handler_t eh) : base_t(b), m_error_handler(std::move(eh)) {
+ }
+ basic_protected_function(basic_function<base_t>&& b, handler_t eh) : base_t(std::move(b)), m_error_handler(std::move(eh)) {
+ }
+ basic_protected_function(const stack_reference& r) : basic_protected_function(r.lua_state(), r.stack_index(), get_default_handler(r.lua_state())) {
+ }
+ basic_protected_function(stack_reference&& r) : basic_protected_function(r.lua_state(), r.stack_index(), get_default_handler(r.lua_state())) {
+ }
+ basic_protected_function(const stack_reference& r, handler_t eh) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {
+ }
+ basic_protected_function(stack_reference&& r, handler_t eh) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {
+ }
+
+ template <typename Super>
+ basic_protected_function(const proxy_base<Super>& p) : basic_protected_function(p, get_default_handler(p.lua_state())) {
+ }
+ template <typename Super>
+ basic_protected_function(proxy_base<Super>&& p) : basic_protected_function(std::move(p), get_default_handler(p.lua_state())) {
+ }
+ template <typename Proxy, typename HandlerReference,
+ meta::enable<std::is_base_of<proxy_base_tag, meta::unqualified_t<Proxy>>,
+ meta::neg<is_lua_index<meta::unqualified_t<HandlerReference>>>> = meta::enabler>
+ basic_protected_function(Proxy&& p, HandlerReference&& eh)
+ : basic_protected_function(detail::force_cast<base_t>(p), make_reference<handler_t>(p.lua_state(), std::forward<HandlerReference>(eh))) {
+ }
+
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_protected_function(lua_State* L_, T&& r) : basic_protected_function(L_, std::forward<T>(r), get_default_handler(L_)) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_protected_function(lua_State* L_, T&& r, handler_t eh) : base_t(L_, std::forward<T>(r)), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(lua_state(), -1, handler);
+#endif // Safety
+ }
+
+ basic_protected_function(lua_nil_t n) : base_t(n), m_error_handler(n) {
+ }
+
+ basic_protected_function(lua_State* L_, int index_ = -1) : basic_protected_function(L_, index_, get_default_handler(L_)) {
+ }
+ basic_protected_function(lua_State* L_, int index_, handler_t eh) : base_t(L_, index_), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(L_, index_, handler);
+#endif // Safety
+ }
+ basic_protected_function(lua_State* L_, absolute_index index_) : basic_protected_function(L_, index_, get_default_handler(L_)) {
+ }
+ basic_protected_function(lua_State* L_, absolute_index index_, handler_t eh) : base_t(L_, index_), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(L_, index_, handler);
+#endif // Safety
+ }
+ basic_protected_function(lua_State* L_, raw_index index_) : basic_protected_function(L_, index_, get_default_handler(L_)) {
+ }
+ basic_protected_function(lua_State* L_, raw_index index_, handler_t eh) : base_t(L_, index_), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(L_, index_, handler);
+#endif // Safety
+ }
+ basic_protected_function(lua_State* L_, ref_index index_) : basic_protected_function(L_, index_, get_default_handler(L_)) {
+ }
+ basic_protected_function(lua_State* L_, ref_index index_, handler_t eh) : base_t(L_, index_), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(lua_state(), -1, handler);
+#endif // Safety
+ }
+
+ using base_t::lua_state;
+
+ template <typename Fx>
+ int dump(lua_Writer writer, void* userdata_pointer_, bool strip, Fx&& on_error) const {
+ this->push();
+ auto ppn = stack::push_popper_n<false>(this->lua_state(), 1);
+ int r = lua_dump(this->lua_state(), writer, userdata_pointer_, strip ? 1 : 0);
+ if (r != 0) {
+ return on_error(this->lua_state(), r, writer, userdata_pointer_, strip);
+ }
+ return r;
+ }
+
+ int dump(lua_Writer writer, void* userdata_pointer_, bool strip = false) const {
+ return dump(writer, userdata_pointer_, strip, &dump_pass_on_error);
+ }
+
+ template <typename Container = bytecode>
+ Container dump() const {
+ Container bc;
+ (void)dump(static_cast<lua_Writer>(&basic_insert_dump_writer<Container>), static_cast<void*>(&bc), false, &dump_throw_on_error);
+ return bc;
+ }
+
+ template <typename Container = bytecode, typename Fx>
+ Container dump(Fx&& on_error) const {
+ Container bc;
+ (void)dump(static_cast<lua_Writer>(&basic_insert_dump_writer<Container>), static_cast<void*>(&bc), false, std::forward<Fx>(on_error));
+ return bc;
+ }
+
+ template <typename... Args>
+ protected_function_result operator()(Args&&... args) const {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) operator()(types<Ret...>, Args&&... args) const {
+ return call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) const {
+ if constexpr (!Aligned) {
+ // we do not expect the function to already be on the stack: push it
+ if (m_error_handler.valid(lua_state())) {
+ detail::protected_handler<true, handler_t> h(lua_state(), m_error_handler);
+ base_t::push();
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount, h);
+ }
+ else {
+ detail::protected_handler<false, handler_t> h(lua_state(), m_error_handler);
+ base_t::push();
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount, h);
+ }
+ }
+ else {
+ // the function is already on the stack at the right location
+ if (m_error_handler.valid()) {
+ // the handler will be pushed onto the stack manually,
+ // since it's not already on the stack this means we need to push our own
+ // function on the stack too and swap things to be in-place
+ if constexpr (!is_stack_handler_v) {
+ // so, we need to remove the function at the top and then dump the handler out ourselves
+ base_t::push();
+ }
+ detail::protected_handler<true, handler_t> h(lua_state(), m_error_handler);
+ if constexpr (!is_stack_handler_v) {
+ lua_replace(lua_state(), -3);
+ h.stack_index = lua_absindex(lua_state(), -2);
+ }
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount, h);
+ }
+ else {
+ detail::protected_handler<false, handler_t> h(lua_state(), m_error_handler);
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount, h);
+ }
+ }
+ }
+
+ ~basic_protected_function() {
+ if constexpr (is_stateless_lua_reference_v<handler_t>) {
+ this->m_error_handler.reset(lua_state());
+ }
+ }
+
+ static handler_t get_default_handler(lua_State* L_) {
+ return detail::get_default_handler<handler_t, is_main_threaded_v<base_t>>(L_);
+ }
+
+ template <typename T>
+ static void set_default_handler(const T& ref) {
+ detail::set_default_handler(ref.lua_state(), ref);
+ }
+
+ auto get_error_handler() const noexcept {
+ if constexpr (is_stateless_lua_reference_v<handler_t>) {
+ if constexpr (is_stack_based_v<handler_t>) {
+ return stack_reference(lua_state(), m_error_handler.stack_index());
+ }
+ else {
+ return basic_reference<is_main_threaded_v<base_t>>(lua_state(), ref_index(m_error_handler.registry_index()));
+ }
+ }
+ else {
+ return m_error_handler;
+ }
+ }
+
+ template <typename ErrorHandler_>
+ void set_error_handler(ErrorHandler_&& error_handler_) noexcept {
+ static_assert(!is_stack_based_v<handler_t> || is_stack_based_v<ErrorHandler_>,
+ "A stack-based error handler can only be set from a parameter that is also stack-based.");
+ if constexpr (std::is_rvalue_reference_v<ErrorHandler_>) {
+ m_error_handler = std::forward<ErrorHandler_>(error_handler_);
+ }
+ else {
+ m_error_handler.copy_assign(lua_state(), std::forward<ErrorHandler_>(error_handler_));
+ }
+ }
+
+ void abandon () noexcept {
+ this->m_error_handler.abandon();
+ base_t::abandon();
+ }
+
+ private:
+ handler_t m_error_handler;
+
+ template <bool b>
+ call_status luacall(std::ptrdiff_t argcount, std::ptrdiff_t result_count_, detail::protected_handler<b, handler_t>& h) const {
+ return static_cast<call_status>(lua_pcall(lua_state(), static_cast<int>(argcount), static_cast<int>(result_count_), h.stack_index));
+ }
+
+ template <std::size_t... I, bool b, typename... Ret>
+ auto invoke(types<Ret...>, std::index_sequence<I...>, std::ptrdiff_t n, detail::protected_handler<b, handler_t>& h) const {
+ luacall(n, sizeof...(Ret), h);
+ return stack::pop<std::tuple<Ret...>>(lua_state());
+ }
+
+ template <std::size_t I, bool b, typename Ret>
+ Ret invoke(types<Ret>, std::index_sequence<I>, std::ptrdiff_t n, detail::protected_handler<b, handler_t>& h) const {
+ luacall(n, 1, h);
+ return stack::pop<Ret>(lua_state());
+ }
+
+ template <std::size_t I, bool b>
+ void invoke(types<void>, std::index_sequence<I>, std::ptrdiff_t n, detail::protected_handler<b, handler_t>& h) const {
+ luacall(n, 0, h);
+ }
+
+ template <bool b>
+ protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, detail::protected_handler<b, handler_t>& h) const {
+ int stacksize = lua_gettop(lua_state());
+ int poststacksize = stacksize;
+ int firstreturn = 1;
+ int returncount = 0;
+ call_status code = call_status::ok;
+#if SOL_IS_ON(SOL_EXCEPTIONS) && SOL_IS_OFF(SOL_PROPAGATE_EXCEPTIONS)
+ try {
+#endif // No Exceptions
+ firstreturn = (std::max)(1, static_cast<int>(stacksize - n - static_cast<int>(h.valid() && !is_stack_handler_v)));
+ code = luacall(n, LUA_MULTRET, h);
+ poststacksize = lua_gettop(lua_state()) - static_cast<int>(h.valid() && !is_stack_handler_v);
+ returncount = poststacksize - (firstreturn - 1);
+#if SOL_IS_ON(SOL_EXCEPTIONS) && SOL_IS_OFF(SOL_PROPAGATE_EXCEPTIONS)
+ }
+ // Handle C++ errors thrown from C++ functions bound inside of lua
+ catch (const char* error) {
+ detail::handle_protected_exception(lua_state(), optional<const std::exception&>(nullopt), error, h);
+ firstreturn = lua_gettop(lua_state());
+ return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime);
+ }
+ catch (const std::string& error) {
+ detail::handle_protected_exception(lua_state(), optional<const std::exception&>(nullopt), error.c_str(), h);
+ firstreturn = lua_gettop(lua_state());
+ return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime);
+ }
+ catch (const std::exception& error) {
+ detail::handle_protected_exception(lua_state(), optional<const std::exception&>(error), error.what(), h);
+ firstreturn = lua_gettop(lua_state());
+ return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime);
+ }
+#if SOL_IS_ON(SOL_EXCEPTIONS_CATCH_ALL)
+ // LuaJIT cannot have the catchall when the safe propagation is on
+ // but LuaJIT will swallow all C++ errors
+ // if we don't at least catch std::exception ones
+ catch (...) {
+ detail::handle_protected_exception(lua_state(), optional<const std::exception&>(nullopt), detail::protected_function_error, h);
+ firstreturn = lua_gettop(lua_state());
+ return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime);
+ }
+#endif // Always catch edge case
+#else
+ // do not handle exceptions: they can be propogated into C++ and keep all type information / rich information
+#endif // Exceptions vs. No Exceptions
+ return protected_function_result(lua_state(), firstreturn, returncount, returncount, code);
+ }
+ };
+} // namespace sol
+
+// end of sol/protected_function.hpp
+
+#include <functional>
+
+namespace sol {
+ template <typename... Ret, typename... Args>
+ decltype(auto) stack_proxy::call(Args&&... args) {
+ stack_function sf(this->lua_state(), this->stack_index());
+ return sf.template call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ inline protected_function_result::protected_function_result(unsafe_function_result&& o) noexcept
+ : L(o.lua_state()), index(o.stack_index()), returncount(o.return_count()), popcount(o.return_count()), err(o.status()) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ }
+
+ inline protected_function_result& protected_function_result::operator=(unsafe_function_result&& o) noexcept {
+ L = o.lua_state();
+ index = o.stack_index();
+ returncount = o.return_count();
+ popcount = o.return_count();
+ err = o.status();
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ return *this;
+ }
+
+ inline unsafe_function_result::unsafe_function_result(protected_function_result&& o) noexcept
+ : L(o.lua_state()), index(o.stack_index()), returncount(o.return_count()) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ }
+ inline unsafe_function_result& unsafe_function_result::operator=(protected_function_result&& o) noexcept {
+ L = o.lua_state();
+ index = o.stack_index();
+ returncount = o.return_count();
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ return *this;
+ }
+
+ namespace detail {
+ template <typename... R>
+ struct std_shim {
+ unsafe_function lua_func_;
+
+ std_shim(unsafe_function lua_func) : lua_func_(std::move(lua_func)) {
+ }
+
+ template <typename... Args>
+ meta::return_type_t<R...> operator()(Args&&... args) {
+ return lua_func_.call<R...>(std::forward<Args>(args)...);
+ }
+ };
+
+ template <>
+ struct std_shim<void> {
+ unsafe_function lua_func_;
+
+ std_shim(unsafe_function lua_func) : lua_func_(std::move(lua_func)) {
+ }
+
+ template <typename... Args>
+ void operator()(Args&&... args) {
+ lua_func_.call<void>(std::forward<Args>(args)...);
+ }
+ };
+ } // namespace detail
+
+ namespace stack {
+ template <typename Signature>
+ struct unqualified_getter<std::function<Signature>> {
+ typedef meta::bind_traits<Signature> fx_t;
+ typedef typename fx_t::args_list args_lists;
+ typedef meta::tuple_types<typename fx_t::return_type> return_types;
+
+ template <typename... R>
+ static std::function<Signature> get_std_func(types<R...>, lua_State* L, int index) {
+ detail::std_shim<R...> fx(unsafe_function(L, index));
+ return fx;
+ }
+
+ static std::function<Signature> get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ type t = type_of(L, index);
+ if (t == type::none || t == type::lua_nil) {
+ return nullptr;
+ }
+ return get_std_func(return_types(), L, index);
+ }
+ };
+
+ template <typename Allocator>
+ struct unqualified_getter<basic_bytecode<Allocator>> {
+ static basic_bytecode<Allocator> get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ stack_function sf(L, index);
+ return sf.dump(&dump_panic_on_error);
+ }
+ };
+ } // namespace stack
+
+} // namespace sol
+
+// end of sol/function.hpp
+
+// beginning of sol/usertype.hpp
+
+// beginning of sol/usertype_core.hpp
+
+// beginning of sol/deprecate.hpp
+
+#ifndef SOL_DEPRECATED
+#ifdef _MSC_VER
+#define SOL_DEPRECATED __declspec(deprecated)
+#elif __GNUC__
+#define SOL_DEPRECATED __attribute__((deprecated))
+#else
+#define SOL_DEPRECATED [[deprecated]]
+#endif // compilers
+#endif // SOL_DEPRECATED
+
+namespace sol { namespace detail {
+ template <typename T>
+ struct SOL_DEPRECATED deprecate_type {
+ using type = T;
+ };
+}} // namespace sol::detail
+
+// end of sol/deprecate.hpp
+
+// beginning of sol/usertype_container_launch.hpp
+
+// beginning of sol/usertype_container.hpp
+
+namespace sol {
+
+ template <typename T>
+ struct usertype_container;
+
+ namespace container_detail {
+
+ template <typename T>
+ struct has_clear_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::clear));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_empty_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::empty));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_erase_after_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(
+ decltype(std::declval<C>().erase_after(std::declval<std::add_rvalue_reference_t<typename C::const_iterator>>()))*);
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T, typename = void>
+ struct has_find_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(std::declval<C>().find(std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_find_test<T, std::enable_if_t<meta::is_lookup<T>::value>> {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(std::declval<C>().find(std::declval<std::add_rvalue_reference_t<typename C::key_type>>()))*);
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_erase_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(std::declval<C>().erase(std::declval<typename C::iterator>()))*);
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_erase_key_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(std::declval<C>().erase(std::declval<typename C::key_type>()))*);
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_find_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::find));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_index_of_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::index_of));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_insert_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::insert));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_erase_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::erase));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_index_set_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::index_set));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_index_get_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::index_get));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_set_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::set));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_get_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::get));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_at_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::at));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_pairs_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::pairs));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_ipairs_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::ipairs));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_next_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::next));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_add_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::add));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_size_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::size));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ using has_clear = meta::boolean<has_clear_test<T>::value>;
+
+ template <typename T>
+ using has_empty = meta::boolean<has_empty_test<T>::value>;
+
+ template <typename T>
+ using has_find = meta::boolean<has_find_test<T>::value>;
+
+ template <typename T>
+ using has_erase = meta::boolean<has_erase_test<T>::value>;
+
+ template <typename T>
+ using has_erase_key = meta::boolean<has_erase_key_test<T>::value>;
+
+ template <typename T>
+ using has_erase_after = meta::boolean<has_erase_after_test<T>::value>;
+
+ template <typename T>
+ using has_traits_get = meta::boolean<has_traits_get_test<T>::value>;
+
+ template <typename T>
+ using has_traits_at = meta::boolean<has_traits_at_test<T>::value>;
+
+ template <typename T>
+ using has_traits_set = meta::boolean<has_traits_set_test<T>::value>;
+
+ template <typename T>
+ using has_traits_index_get = meta::boolean<has_traits_index_get_test<T>::value>;
+
+ template <typename T>
+ using has_traits_index_set = meta::boolean<has_traits_index_set_test<T>::value>;
+
+ template <typename T>
+ using has_traits_pairs = meta::boolean<has_traits_pairs_test<T>::value>;
+
+ template <typename T>
+ using has_traits_ipairs = meta::boolean<has_traits_ipairs_test<T>::value>;
+
+ template <typename T>
+ using has_traits_next = meta::boolean<has_traits_next_test<T>::value>;
+
+ template <typename T>
+ using has_traits_add = meta::boolean<has_traits_add_test<T>::value>;
+
+ template <typename T>
+ using has_traits_size = meta::boolean<has_traits_size_test<T>::value>;
+
+ template <typename T>
+ using has_traits_clear = has_clear<T>;
+
+ template <typename T>
+ using has_traits_empty = has_empty<T>;
+
+ template <typename T>
+ using has_traits_find = meta::boolean<has_traits_find_test<T>::value>;
+
+ template <typename T>
+ using has_traits_index_of = meta::boolean<has_traits_index_of_test<T>::value>;
+
+ template <typename T>
+ using has_traits_insert = meta::boolean<has_traits_insert_test<T>::value>;
+
+ template <typename T>
+ using has_traits_erase = meta::boolean<has_traits_erase_test<T>::value>;
+
+ template <typename T>
+ struct is_forced_container : is_container<T> { };
+
+ template <typename T>
+ struct is_forced_container<as_container_t<T>> : std::true_type { };
+
+ template <typename T>
+ struct container_decay {
+ typedef T type;
+ };
+
+ template <typename T>
+ struct container_decay<as_container_t<T>> {
+ typedef T type;
+ };
+
+ template <typename T>
+ using container_decay_t = typename container_decay<meta::unqualified_t<T>>::type;
+
+ template <typename T>
+ decltype(auto) get_key(std::false_type, T&& t) {
+ return std::forward<T>(t);
+ }
+
+ template <typename T>
+ decltype(auto) get_key(std::true_type, T&& t) {
+ return t.first;
+ }
+
+ template <typename T>
+ decltype(auto) get_value(std::false_type, T&& t) {
+ return std::forward<T>(t);
+ }
+
+ template <typename T>
+ decltype(auto) get_value(std::true_type, T&& t) {
+ return t.second;
+ }
+
+ template <typename X, typename = void>
+ struct usertype_container_default {
+ private:
+ typedef std::remove_pointer_t<meta::unwrap_unqualified_t<X>> T;
+
+ public:
+ typedef lua_nil_t iterator;
+ typedef iterator sentinel;
+ typedef lua_nil_t value_type;
+
+ static int at(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'at(index)' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int get(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'get(key)' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int index_get(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'container[key]' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int set(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'set(key, value)' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int index_set(lua_State* L_) {
+ return luaL_error(
+ L_, "sol: cannot call 'container[key] = value' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int add(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'add' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int insert(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'insert' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int find(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'find' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int index_of(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'index_of' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int size(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'end' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int clear(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'clear' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int empty(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'empty' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int erase(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'erase' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int next(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'next' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int pairs(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call '__pairs/pairs' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int ipairs(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call '__ipairs' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static iterator begin(lua_State* L_, T&) {
+ luaL_error(L_, "sol: cannot call 'being' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ return lua_nil;
+ }
+
+ static sentinel end(lua_State* L_, T&) {
+ luaL_error(L_, "sol: cannot call 'end' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ return lua_nil;
+ }
+ };
+
+ template <typename X>
+ struct usertype_container_default<X,
+ std::enable_if_t<meta::all<is_forced_container<meta::unqualified_t<X>>, meta::has_value_type<meta::unqualified_t<container_decay_t<X>>>,
+ meta::has_iterator<meta::unqualified_t<container_decay_t<X>>>>::value>> {
+ private:
+ using T = std::remove_pointer_t<meta::unwrap_unqualified_t<container_decay_t<X>>>;
+
+ private:
+ using deferred_uc = usertype_container<X>;
+ using is_associative = meta::is_associative<T>;
+ using is_lookup = meta::is_lookup<T>;
+ using is_ordered = meta::is_ordered<T>;
+ using is_matched_lookup = meta::is_matched_lookup<T>;
+ using iterator = typename T::iterator;
+ using sentinel = meta::sentinel_or_t<T, iterator>;
+ using value_type = typename T::value_type;
+ typedef meta::conditional_t<is_matched_lookup::value, std::pair<value_type, value_type>,
+ meta::conditional_t<is_associative::value || is_lookup::value, value_type, std::pair<std::ptrdiff_t, value_type>>>
+ KV;
+ typedef typename KV::first_type K;
+ typedef typename KV::second_type V;
+ typedef meta::conditional_t<is_matched_lookup::value, std::ptrdiff_t, K> next_K;
+ typedef decltype(*std::declval<iterator&>()) iterator_return;
+ typedef meta::conditional_t<is_associative::value || is_matched_lookup::value, std::add_lvalue_reference_t<V>,
+ meta::conditional_t<is_lookup::value, V, iterator_return>>
+ captured_type;
+ typedef typename meta::iterator_tag<iterator>::type iterator_category;
+ typedef std::is_same<iterator_category, std::input_iterator_tag> is_input_iterator;
+ typedef meta::conditional_t<is_input_iterator::value, V, decltype(detail::deref_move_only(std::declval<captured_type>()))> push_type;
+ typedef std::is_copy_assignable<V> is_copyable;
+ typedef meta::neg<meta::any<std::is_const<V>, std::is_const<std::remove_reference_t<iterator_return>>, meta::neg<is_copyable>>> is_writable;
+ typedef meta::unqualified_t<decltype(get_key(is_associative(), std::declval<std::add_lvalue_reference_t<value_type>>()))> key_type;
+ typedef meta::all<std::is_integral<K>, meta::neg<meta::any<is_associative, is_lookup>>> is_linear_integral;
+
+ struct iter : detail::ebco<iterator, 0>, detail::ebco<sentinel, 1> {
+ using it_base = detail::ebco<iterator, 0>;
+ using sen_base = detail::ebco<sentinel, 1>;
+ main_reference keep_alive;
+ std::size_t index;
+
+ iter(lua_State* L_, int stack_index_, iterator it_, sentinel sen_) noexcept
+ : it_base(std::move(it_)), sen_base(std::move(sen_)), keep_alive(L_, stack_index_), index(0) {
+ }
+
+ iterator& it() noexcept {
+ return it_base::value();
+ }
+
+ const iterator& it() const noexcept {
+ return it_base::value();
+ }
+
+ sentinel& sen() noexcept {
+ return sen_base::value();
+ }
+
+ const sentinel& sen() const noexcept {
+ return sen_base::value();
+ }
+ };
+
+ static auto& get_src(lua_State* L_) {
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ auto p = stack::unqualified_check_get<T*>(L_, 1);
+ if (!p) {
+ luaL_error(L_,
+ "sol: 'self' is not of type '%s' (pass 'self' as first argument with ':' or call on proper type)",
+ detail::demangle<T>().c_str());
+ }
+ if (p.value() == nullptr) {
+ luaL_error(
+ L_, "sol: 'self' argument is nil (pass 'self' as first argument with ':' or call on a '%s' type)", detail::demangle<T>().c_str());
+ }
+ return *p.value();
+#else
+ return stack::unqualified_get<T>(L_, 1);
+#endif // Safe getting with error
+ }
+
+ static detail::error_result at_category(std::input_iterator_tag, lua_State* L_, T& self, std::ptrdiff_t pos) {
+ pos += deferred_uc::index_adjustment(L_, self);
+ if (pos < 0) {
+ return stack::push(L_, lua_nil);
+ }
+ auto it = deferred_uc::begin(L_, self);
+ auto e = deferred_uc::end(L_, self);
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ while (pos > 0) {
+ --pos;
+ ++it;
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ }
+ return get_associative(is_associative(), L_, it);
+ }
+
+ static detail::error_result at_category(std::random_access_iterator_tag, lua_State* L_, T& self, std::ptrdiff_t pos) {
+ std::ptrdiff_t len = static_cast<std::ptrdiff_t>(size_start(L_, self));
+ pos += deferred_uc::index_adjustment(L_, self);
+ if (pos < 0 || pos >= len) {
+ return stack::push(L_, lua_nil);
+ }
+ auto it = std::next(deferred_uc::begin(L_, self), pos);
+ return get_associative(is_associative(), L_, it);
+ }
+
+ static detail::error_result at_start(lua_State* L_, T& self, std::ptrdiff_t pos) {
+ return at_category(iterator_category(), L_, self, pos);
+ }
+
+ template <typename Iter>
+ static detail::error_result get_associative(std::true_type, lua_State* L_, Iter& it) {
+ decltype(auto) v = *it;
+ return stack::stack_detail::push_reference<push_type>(L_, detail::deref_move_only(v.second));
+ }
+
+ template <typename Iter>
+ static detail::error_result get_associative(std::false_type, lua_State* L_, Iter& it) {
+ return stack::stack_detail::push_reference<push_type>(L_, detail::deref_move_only(*it));
+ }
+
+ static detail::error_result get_category(std::input_iterator_tag, lua_State* L_, T& self, K& key) {
+ key = static_cast<K>(key + deferred_uc::index_adjustment(L_, self));
+ if (key < 0) {
+ return stack::push(L_, lua_nil);
+ }
+ auto it = deferred_uc::begin(L_, self);
+ auto e = deferred_uc::end(L_, self);
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ while (key > 0) {
+ --key;
+ ++it;
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ }
+ return get_associative(is_associative(), L_, it);
+ }
+
+ static detail::error_result get_category(std::random_access_iterator_tag, lua_State* L_, T& self, K& key) {
+ std::ptrdiff_t len = static_cast<std::ptrdiff_t>(size_start(L_, self));
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ if (key < 0 || key >= len) {
+ return stack::push(L_, lua_nil);
+ }
+ auto it = std::next(deferred_uc::begin(L_, self), key);
+ return get_associative(is_associative(), L_, it);
+ }
+
+ static detail::error_result get_it(std::true_type, lua_State* L_, T& self, K& key) {
+ return get_category(iterator_category(), L_, self, key);
+ }
+
+ static detail::error_result get_comparative(std::true_type, lua_State* L_, T& self, K& key) {
+ auto fx = [&](const value_type& r) -> bool { return key == get_key(is_associative(), r); };
+ auto e = deferred_uc::end(L_, self);
+ auto it = std::find_if(deferred_uc::begin(L_, self), e, std::ref(fx));
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ return get_associative(is_associative(), L_, it);
+ }
+
+ static detail::error_result get_comparative(std::false_type, lua_State*, T&, K&) {
+ return detail::error_result("cannot get this key on '%s': no suitable way to increment iterator and compare to key value '%s'",
+ detail::demangle<T>().data(),
+ detail::demangle<K>().data());
+ }
+
+ static detail::error_result get_it(std::false_type, lua_State* L_, T& self, K& key) {
+ return get_comparative(meta::supports_op_equal<K, key_type>(), L_, self, key);
+ }
+
+ static detail::error_result set_associative(std::true_type, iterator& it, stack_object value) {
+ decltype(auto) v = *it;
+ v.second = value.as<V>();
+ return {};
+ }
+
+ static detail::error_result set_associative(std::false_type, iterator& it, stack_object value) {
+ decltype(auto) v = *it;
+ v = value.as<V>();
+ return {};
+ }
+
+ static detail::error_result set_writable(std::true_type, lua_State*, T&, iterator& it, stack_object value) {
+ return set_associative(is_associative(), it, std::move(value));
+ }
+
+ static detail::error_result set_writable(std::false_type, lua_State*, T&, iterator&, stack_object) {
+ return detail::error_result(
+ "cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle<T>().data());
+ }
+
+ static detail::error_result set_category(std::input_iterator_tag, lua_State* L_, T& self, stack_object okey, stack_object value) {
+ decltype(auto) key = okey.as<K>();
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ auto e = deferred_uc::end(L_, self);
+ auto it = deferred_uc::begin(L_, self);
+ auto backit = it;
+ for (; key > 0 && it != e; --key, ++it) {
+ backit = it;
+ }
+ if (it == e) {
+ if (key == 0) {
+ return add_copyable(is_copyable(), L_, self, std::move(value), meta::has_insert_after<T>::value ? backit : it);
+ }
+ return detail::error_result("out of bounds (too big) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ return set_writable(is_writable(), L_, self, it, std::move(value));
+ }
+
+ static detail::error_result set_category(std::random_access_iterator_tag, lua_State* L_, T& self, stack_object okey, stack_object value) {
+ decltype(auto) key = okey.as<K>();
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ if (key < 0) {
+ return detail::error_result("sol: out of bounds (too small) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ std::ptrdiff_t len = static_cast<std::ptrdiff_t>(size_start(L_, self));
+ if (key == len) {
+ return add_copyable(is_copyable(), L_, self, std::move(value));
+ }
+ else if (key >= len) {
+ return detail::error_result("sol: out of bounds (too big) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ auto it = std::next(deferred_uc::begin(L_, self), key);
+ return set_writable(is_writable(), L_, self, it, std::move(value));
+ }
+
+ static detail::error_result set_comparative(std::true_type, lua_State* L_, T& self, stack_object okey, stack_object value) {
+ decltype(auto) key = okey.as<K>();
+ if (!is_writable::value) {
+ return detail::error_result(
+ "cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle<T>().data());
+ }
+ auto fx = [&](const value_type& r) -> bool { return key == get_key(is_associative(), r); };
+ auto e = deferred_uc::end(L_, self);
+ auto it = std::find_if(deferred_uc::begin(L_, self), e, std::ref(fx));
+ if (it == e) {
+ return {};
+ }
+ return set_writable(is_writable(), L_, self, it, std::move(value));
+ }
+
+ static detail::error_result set_comparative(std::false_type, lua_State*, T&, stack_object, stack_object) {
+ return detail::error_result("cannot set this value on '%s': no suitable way to increment iterator or compare to '%s' key",
+ detail::demangle<T>().data(),
+ detail::demangle<K>().data());
+ }
+
+ template <typename Iter>
+ static detail::error_result set_associative_insert(std::true_type, lua_State*, T& self, Iter& it, K& key, stack_object value) {
+ if constexpr (meta::has_insert_with_iterator<T>::value) {
+ self.insert(it, value_type(key, value.as<V>()));
+ return {};
+ }
+ else if constexpr (meta::has_insert<T>::value) {
+ self.insert(value_type(key, value.as<V>()));
+ return {};
+ }
+ else {
+ (void)self;
+ (void)it;
+ (void)key;
+ return detail::error_result(
+ "cannot call 'set' on '%s': there is no 'insert' function on this associative type", detail::demangle<T>().c_str());
+ }
+ }
+
+ template <typename Iter>
+ static detail::error_result set_associative_insert(std::false_type, lua_State*, T& self, Iter& it, K& key, stack_object) {
+ if constexpr (meta::has_insert_with_iterator<T>::value) {
+ self.insert(it, key);
+ return {};
+ }
+ else if constexpr (meta::has_insert<T>::value) {
+ self.insert(key);
+ return {};
+ }
+ else {
+ (void)self;
+ (void)it;
+ (void)key;
+ return detail::error_result(
+ "cannot call 'set' on '%s': there is no 'insert' function on this non-associative type", detail::demangle<T>().c_str());
+ }
+ }
+
+ static detail::error_result set_associative_find(std::true_type, lua_State* L_, T& self, stack_object okey, stack_object value) {
+ decltype(auto) key = okey.as<K>();
+ auto it = self.find(key);
+ if (it == deferred_uc::end(L_, self)) {
+ return set_associative_insert(is_associative(), L_, self, it, key, std::move(value));
+ }
+ return set_writable(is_writable(), L_, self, it, std::move(value));
+ }
+
+ static detail::error_result set_associative_find(std::false_type, lua_State* L_, T& self, stack_object key, stack_object value) {
+ return set_comparative(meta::supports_op_equal<K, key_type>(), L_, self, std::move(key), std::move(value));
+ }
+
+ static detail::error_result set_it(std::true_type, lua_State* L_, T& self, stack_object key, stack_object value) {
+ return set_category(iterator_category(), L_, self, std::move(key), std::move(value));
+ }
+
+ static detail::error_result set_it(std::false_type, lua_State* L_, T& self, stack_object key, stack_object value) {
+ return set_associative_find(meta::all<has_find<T>, meta::any<is_associative, is_lookup>>(), L_, self, std::move(key), std::move(value));
+ }
+
+ template <bool idx_of = false>
+ static detail::error_result find_has_associative_lookup(std::true_type, lua_State* L_, T& self) {
+ if constexpr (!is_ordered::value && idx_of) {
+ (void)L_;
+ (void)self;
+ return detail::error_result("cannot perform an 'index_of': '%s's is not an ordered container", detail::demangle<T>().data());
+ }
+ else {
+ decltype(auto) key = stack::unqualified_get<K>(L_, 2);
+ auto it = self.find(key);
+ if (it == deferred_uc::end(L_, self)) {
+ return stack::push(L_, lua_nil);
+ }
+ if constexpr (idx_of) {
+ auto dist = std::distance(deferred_uc::begin(L_, self), it);
+ dist -= deferred_uc::index_adjustment(L_, self);
+ return stack::push(L_, dist);
+ }
+ else {
+ return get_associative(is_associative(), L_, it);
+ }
+ }
+ }
+
+ template <bool idx_of = false>
+ static detail::error_result find_has_associative_lookup(std::false_type, lua_State* L_, T& self) {
+ if constexpr (!is_ordered::value && idx_of) {
+ (void)L_;
+ (void)self;
+ return detail::error_result("cannot perform an 'index_of': '%s's is not an ordered container", detail::demangle<T>().data());
+ }
+ else {
+ decltype(auto) value = stack::unqualified_get<V>(L_, 2);
+ auto it = self.find(value);
+ if (it == deferred_uc::end(L_, self)) {
+ return stack::push(L_, lua_nil);
+ }
+ if constexpr (idx_of) {
+ auto dist = std::distance(deferred_uc::begin(L_, self), it);
+ dist -= deferred_uc::index_adjustment(L_, self);
+ return stack::push(L_, dist);
+ }
+ else {
+ return get_associative(is_associative(), L_, it);
+ }
+ }
+ }
+
+ template <bool idx_of = false>
+ static detail::error_result find_has(std::true_type, lua_State* L_, T& self) {
+ return find_has_associative_lookup<idx_of>(meta::any<is_lookup, is_associative>(), L_, self);
+ }
+
+ template <typename Iter>
+ static detail::error_result find_associative_lookup(std::true_type, lua_State* L_, T&, Iter& it, std::size_t) {
+ return get_associative(is_associative(), L_, it);
+ }
+
+ template <typename Iter>
+ static detail::error_result find_associative_lookup(std::false_type, lua_State* L_, T& self, Iter&, std::size_t idx) {
+ idx = static_cast<std::size_t>(static_cast<std::ptrdiff_t>(idx) - deferred_uc::index_adjustment(L_, self));
+ return stack::push(L_, idx);
+ }
+
+ template <bool = false>
+ static detail::error_result find_comparative(std::false_type, lua_State*, T&) {
+ return detail::error_result("cannot call 'find' on '%s': there is no 'find' function and the value_type is not equality comparable",
+ detail::demangle<T>().c_str());
+ }
+
+ template <bool idx_of = false>
+ static detail::error_result find_comparative(std::true_type, lua_State* L_, T& self) {
+ decltype(auto) value = stack::unqualified_get<V>(L_, 2);
+ auto it = deferred_uc::begin(L_, self);
+ auto e = deferred_uc::end(L_, self);
+ std::size_t idx = 0;
+ for (;; ++it, ++idx) {
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ if (value == get_value(is_associative(), *it)) {
+ break;
+ }
+ }
+ return find_associative_lookup(meta::all<meta::boolean<!idx_of>, meta::any<is_lookup, is_associative>>(), L_, self, it, idx);
+ }
+
+ template <bool idx_of = false>
+ static detail::error_result find_has(std::false_type, lua_State* L_, T& self) {
+ return find_comparative<idx_of>(meta::supports_op_equal<V>(), L_, self);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_insert_after(std::false_type, lua_State* L_, T& self, stack_object value, Iter&) {
+ return add_insert_after(std::false_type(), L_, self, value);
+ }
+
+ static detail::error_result add_insert_after(std::false_type, lua_State*, T&, stack_object) {
+ return detail::error_result("cannot call 'add' on type '%s': no suitable insert/push_back C++ functions", detail::demangle<T>().data());
+ }
+
+ template <typename Iter>
+ static detail::error_result add_insert_after(std::true_type, lua_State*, T& self, stack_object value, Iter& pos) {
+ self.insert_after(pos, value.as<V>());
+ return {};
+ }
+
+ static detail::error_result add_insert_after(std::true_type, lua_State* L_, T& self, stack_object value) {
+ auto backit = self.before_begin();
+ {
+ auto e = deferred_uc::end(L_, self);
+ for (auto it = deferred_uc::begin(L_, self); it != e; ++backit, ++it) { }
+ }
+ return add_insert_after(std::true_type(), L_, self, value, backit);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_insert(std::true_type, lua_State*, T& self, stack_object value, Iter& pos) {
+ self.insert(pos, value.as<V>());
+ return {};
+ }
+
+ static detail::error_result add_insert(std::true_type, lua_State* L_, T& self, stack_object value) {
+ auto pos = deferred_uc::end(L_, self);
+ return add_insert(std::true_type(), L_, self, value, pos);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_insert(std::false_type, lua_State* L_, T& self, stack_object value, Iter& pos) {
+ return add_insert_after(meta::has_insert_after<T>(), L_, self, std::move(value), pos);
+ }
+
+ static detail::error_result add_insert(std::false_type, lua_State* L_, T& self, stack_object value) {
+ return add_insert_after(meta::has_insert_after<T>(), L_, self, std::move(value));
+ }
+
+ template <typename Iter>
+ static detail::error_result add_push_back(std::true_type, lua_State*, T& self, stack_object value, Iter&) {
+ self.push_back(value.as<V>());
+ return {};
+ }
+
+ static detail::error_result add_push_back(std::true_type, lua_State*, T& self, stack_object value) {
+ self.push_back(value.as<V>());
+ return {};
+ }
+
+ template <typename Iter>
+ static detail::error_result add_push_back(std::false_type, lua_State* L_, T& self, stack_object value, Iter& pos) {
+ return add_insert(
+ std::integral_constant < bool, meta::has_insert<T>::value || meta::has_insert_with_iterator<T>::value > (), L_, self, value, pos);
+ }
+
+ static detail::error_result add_push_back(std::false_type, lua_State* L_, T& self, stack_object value) {
+ return add_insert(
+ std::integral_constant < bool, meta::has_insert<T>::value || meta::has_insert_with_iterator<T>::value > (), L_, self, value);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_associative(std::true_type, lua_State* L_, T& self, stack_object key, Iter& pos) {
+ if constexpr (meta::has_insert_with_iterator<T>::value) {
+ self.insert(pos, value_type(key.as<K>(), stack::unqualified_get<V>(L_, 3)));
+ return {};
+ }
+ else if constexpr (meta::has_insert<T>::value) {
+ self.insert(value_type(key.as<K>(), stack::unqualified_get<V>(L_, 3)));
+ return {};
+ }
+ else {
+ (void)L_;
+ (void)self;
+ (void)key;
+ (void)pos;
+ return detail::error_result(
+ "cannot call 'insert' on '%s': there is no 'insert' function on this associative type", detail::demangle<T>().c_str());
+ }
+ }
+
+ static detail::error_result add_associative(std::true_type, lua_State* L_, T& self, stack_object key) {
+ auto pos = deferred_uc::end(L_, self);
+ return add_associative(std::true_type(), L_, self, std::move(key), pos);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_associative(std::false_type, lua_State* L_, T& self, stack_object value, Iter& pos) {
+ return add_push_back(meta::has_push_back<T>(), L_, self, value, pos);
+ }
+
+ static detail::error_result add_associative(std::false_type, lua_State* L_, T& self, stack_object value) {
+ return add_push_back(meta::has_push_back<T>(), L_, self, value);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_copyable(std::true_type, lua_State* L_, T& self, stack_object value, Iter& pos) {
+ return add_associative(is_associative(), L_, self, std::move(value), pos);
+ }
+
+ static detail::error_result add_copyable(std::true_type, lua_State* L_, T& self, stack_object value) {
+ return add_associative(is_associative(), L_, self, value);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_copyable(std::false_type, lua_State* L_, T& self, stack_object value, Iter&) {
+ return add_copyable(std::false_type(), L_, self, std::move(value));
+ }
+
+ static detail::error_result add_copyable(std::false_type, lua_State*, T&, stack_object) {
+ return detail::error_result("cannot call 'add' on '%s': value_type is non-copyable", detail::demangle<T>().data());
+ }
+
+ static detail::error_result insert_lookup(std::true_type, lua_State* L_, T& self, stack_object, stack_object value) {
+ // TODO: should we warn or error about someone calling insert on an ordered / lookup container with no associativity?
+ return add_copyable(std::true_type(), L_, self, std::move(value));
+ }
+
+ static detail::error_result insert_lookup(std::false_type, lua_State* L_, T& self, stack_object where, stack_object value) {
+ auto it = deferred_uc::begin(L_, self);
+ auto key = where.as<K>();
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ std::advance(it, key);
+ self.insert(it, value.as<V>());
+ return {};
+ }
+
+ static detail::error_result insert_after_has(std::true_type, lua_State* L_, T& self, stack_object where, stack_object value) {
+ auto key = where.as<K>();
+ auto backit = self.before_begin();
+ {
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ auto e = deferred_uc::end(L_, self);
+ for (auto it = deferred_uc::begin(L_, self); key > 0; ++backit, ++it, --key) {
+ if (backit == e) {
+ return detail::error_result("sol: out of bounds (too big) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ }
+ }
+ self.insert_after(backit, value.as<V>());
+ return {};
+ }
+
+ static detail::error_result insert_after_has(std::false_type, lua_State*, T&, stack_object, stack_object) {
+ return detail::error_result(
+ "cannot call 'insert' on '%s': no suitable or similar functionality detected on this container", detail::demangle<T>().data());
+ }
+
+ static detail::error_result insert_has(std::true_type, lua_State* L_, T& self, stack_object key, stack_object value) {
+ return insert_lookup(meta::any<is_associative, is_lookup>(), L_, self, std::move(key), std::move(value));
+ }
+
+ static detail::error_result insert_has(std::false_type, lua_State* L_, T& self, stack_object where, stack_object value) {
+ return insert_after_has(meta::has_insert_after<T>(), L_, self, where, value);
+ }
+
+ static detail::error_result insert_copyable(std::true_type, lua_State* L_, T& self, stack_object key, stack_object value) {
+ return insert_has(std::integral_constant < bool,
+ meta::has_insert<T>::value || meta::has_insert_with_iterator<T>::value > (),
+ L_,
+ self,
+ std::move(key),
+ std::move(value));
+ }
+
+ static detail::error_result insert_copyable(std::false_type, lua_State*, T&, stack_object, stack_object) {
+ return detail::error_result("cannot call 'insert' on '%s': value_type is non-copyable", detail::demangle<T>().data());
+ }
+
+ static detail::error_result erase_integral(std::true_type, lua_State* L_, T& self, K& key) {
+ auto it = deferred_uc::begin(L_, self);
+ key = (static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ std::advance(it, key);
+ self.erase(it);
+
+ return {};
+ }
+
+ static detail::error_result erase_integral(std::false_type, lua_State* L_, T& self, const K& key) {
+ auto fx = [&](const value_type& r) -> bool { return key == r; };
+ auto e = deferred_uc::end(L_, self);
+ auto it = std::find_if(deferred_uc::begin(L_, self), e, std::ref(fx));
+ if (it == e) {
+ return {};
+ }
+ self.erase(it);
+
+ return {};
+ }
+
+ static detail::error_result erase_associative_lookup(std::true_type, lua_State*, T& self, const K& key) {
+ self.erase(key);
+ return {};
+ }
+
+ static detail::error_result erase_associative_lookup(std::false_type, lua_State* L_, T& self, K& key) {
+ return erase_integral(std::is_integral<K>(), L_, self, key);
+ }
+
+ static detail::error_result erase_after_has(std::true_type, lua_State* L_, T& self, K& key) {
+ auto backit = self.before_begin();
+ {
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ auto e = deferred_uc::end(L_, self);
+ for (auto it = deferred_uc::begin(L_, self); key > 0; ++backit, ++it, --key) {
+ if (backit == e) {
+ return detail::error_result("sol: out of bounds for erase on '%s'", detail::demangle<T>().c_str());
+ }
+ }
+ }
+ self.erase_after(backit);
+ return {};
+ }
+
+ static detail::error_result erase_after_has(std::false_type, lua_State*, T&, const K&) {
+ return detail::error_result("sol: cannot call erase on '%s'", detail::demangle<T>().c_str());
+ }
+
+ static detail::error_result erase_key_has(std::true_type, lua_State* L_, T& self, K& key) {
+ return erase_associative_lookup(meta::any<is_associative, is_lookup>(), L_, self, key);
+ }
+
+ static detail::error_result erase_key_has(std::false_type, lua_State* L_, T& self, K& key) {
+ return erase_after_has(has_erase_after<T>(), L_, self, key);
+ }
+
+ static detail::error_result erase_has(std::true_type, lua_State* L_, T& self, K& key) {
+ return erase_associative_lookup(meta::any<is_associative, is_lookup>(), L_, self, key);
+ }
+
+ static detail::error_result erase_has(std::false_type, lua_State* L_, T& self, K& key) {
+ return erase_key_has(has_erase_key<T>(), L_, self, key);
+ }
+
+ static auto size_has(std::false_type, lua_State* L_, T& self) {
+ return std::distance(deferred_uc::begin(L_, self), deferred_uc::end(L_, self));
+ }
+
+ static auto size_has(std::true_type, lua_State*, T& self) {
+ return self.size();
+ }
+
+ static void clear_has(std::true_type, lua_State*, T& self) {
+ self.clear();
+ }
+
+ static void clear_has(std::false_type, lua_State* L_, T&) {
+ luaL_error(L_, "sol: cannot call clear on '%s'", detail::demangle<T>().c_str());
+ }
+
+ static bool empty_has(std::true_type, lua_State*, T& self) {
+ return self.empty();
+ }
+
+ static bool empty_has(std::false_type, lua_State* L_, T& self) {
+ return deferred_uc::begin(L_, self) == deferred_uc::end(L_, self);
+ }
+
+ static detail::error_result get_associative_find(std::true_type, lua_State* L_, T& self, K& key) {
+ auto it = self.find(key);
+ if (it == deferred_uc::end(L_, self)) {
+ stack::push(L_, lua_nil);
+ return {};
+ }
+ return get_associative(std::true_type(), L_, it);
+ }
+
+ static detail::error_result get_associative_find(std::false_type, lua_State* L_, T& self, K& key) {
+ return get_it(is_linear_integral(), L_, self, key);
+ }
+
+ static detail::error_result get_start(lua_State* L_, T& self, K& key) {
+ return get_associative_find(std::integral_constant < bool, is_associative::value&& has_find<T>::value > (), L_, self, key);
+ }
+
+ static detail::error_result set_start(lua_State* L_, T& self, stack_object key, stack_object value) {
+ return set_it(is_linear_integral(), L_, self, std::move(key), std::move(value));
+ }
+
+ static std::size_t size_start(lua_State* L_, T& self) {
+ return static_cast<std::size_t>(size_has(meta::has_size<T>(), L_, self));
+ }
+
+ static void clear_start(lua_State* L_, T& self) {
+ clear_has(has_clear<T>(), L_, self);
+ }
+
+ static bool empty_start(lua_State* L_, T& self) {
+ return empty_has(has_empty<T>(), L_, self);
+ }
+
+ static detail::error_result erase_start(lua_State* L_, T& self, K& key) {
+ return erase_has(has_erase<T>(), L_, self, key);
+ }
+
+ template <bool ip>
+ static int next_associative(std::true_type, lua_State* L_) {
+ iter& i = stack::unqualified_get<user<iter>>(L_, 1);
+ auto& it = i.it;
+ auto& end = i.end;
+ if (it == end) {
+ return stack::push(L_, lua_nil);
+ }
+ int p;
+ if constexpr (ip) {
+ ++i.index;
+ p = stack::push_reference(L_, i.index);
+ }
+ else {
+ p = stack::push_reference(L_, it->first);
+ }
+ p += stack::stack_detail::push_reference<push_type>(L_, detail::deref_move_only(it->second));
+ std::advance(it, 1);
+ return p;
+ }
+
+ template <bool>
+ static int next_associative(std::false_type, lua_State* L_) {
+ iter& i = stack::unqualified_get<user<iter>>(L_, 1);
+ auto& it = i.it();
+ auto& end = i.sen();
+ next_K k = stack::unqualified_get<next_K>(L_, 2);
+ if (it == end) {
+ return stack::push(L_, lua_nil);
+ }
+ int p;
+ if constexpr (std::is_integral_v<next_K>) {
+ p = stack::push_reference(L_, k + 1);
+ }
+ else {
+ p = stack::stack_detail::push_reference(L_, k + 1);
+ }
+ p += stack::stack_detail::push_reference<push_type>(L_, detail::deref_move_only(*it));
+ std::advance(it, 1);
+ return p;
+ }
+
+ template <bool ip>
+ static int next_iter(lua_State* L_) {
+ typedef meta::any<is_associative, meta::all<is_lookup, meta::neg<is_matched_lookup>>> is_assoc;
+ return next_associative<ip>(is_assoc(), L_);
+ }
+
+ template <bool ip>
+ static int pairs_associative(std::true_type, lua_State* L_) {
+ auto& src = get_src(L_);
+ stack::push(L_, next_iter<ip>);
+ stack::push<user<iter>>(L_, L_, 1, deferred_uc::begin(L_, src), deferred_uc::begin(L_, src));
+ stack::push(L_, lua_nil);
+ return 3;
+ }
+
+ template <bool ip>
+ static int pairs_associative(std::false_type, lua_State* L_) {
+ auto& src = get_src(L_);
+ stack::push(L_, next_iter<ip>);
+ stack::push<user<iter>>(L_, L_, 1, deferred_uc::begin(L_, src), deferred_uc::end(L_, src));
+ stack::push(L_, 0);
+ return 3;
+ }
+
+ public:
+ static int at(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er;
+ {
+ std::ptrdiff_t pos = stack::unqualified_get<std::ptrdiff_t>(L_, 2);
+ er = at_start(L_, self, pos);
+ }
+ return handle_errors(L_, er);
+ }
+
+ static int get(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er;
+ {
+ decltype(auto) key = stack::unqualified_get<K>(L_);
+ er = get_start(L_, self, key);
+ }
+ return handle_errors(L_, er);
+ }
+
+ static int index_get(lua_State* L_) {
+ return get(L_);
+ }
+
+ static int set(lua_State* L_) {
+ stack_object value = stack_object(L_, raw_index(3));
+ if constexpr (is_linear_integral::value) {
+ // for non-associative containers,
+ // erasure only happens if it is the
+ // last index in the container
+ auto key = stack::get<K>(L_, 2);
+ auto self_size = deferred_uc::size(L_);
+ if (key == static_cast<K>(self_size)) {
+ if (type_of(L_, 3) == type::lua_nil) {
+ return erase(L_);
+ }
+ }
+ }
+ else {
+ if (type_of(L_, 3) == type::lua_nil) {
+ return erase(L_);
+ }
+ }
+ auto& self = get_src(L_);
+ detail::error_result er = set_start(L_, self, stack_object(L_, raw_index(2)), std::move(value));
+ return handle_errors(L_, er);
+ }
+
+ static int index_set(lua_State* L_) {
+ return set(L_);
+ }
+
+ static int add(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er = add_copyable(is_copyable(), L_, self, stack_object(L_, raw_index(2)));
+ return handle_errors(L_, er);
+ }
+
+ static int insert(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er = insert_copyable(is_copyable(), L_, self, stack_object(L_, raw_index(2)), stack_object(L_, raw_index(3)));
+ return handle_errors(L_, er);
+ }
+
+ static int find(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er = find_has(has_find<T>(), L_, self);
+ return handle_errors(L_, er);
+ }
+
+ static int index_of(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er = find_has<true>(has_find<T>(), L_, self);
+ return handle_errors(L_, er);
+ }
+
+ static iterator begin(lua_State*, T& self) {
+ if constexpr (meta::has_begin_end_v<T>) {
+ return self.begin();
+ }
+ else {
+ using std::begin;
+ return begin(self);
+ }
+ }
+
+ static sentinel end(lua_State*, T& self) {
+ if constexpr (meta::has_begin_end_v<T>) {
+ return self.end();
+ }
+ else {
+ using std::end;
+ return end(self);
+ }
+ }
+
+ static int size(lua_State* L_) {
+ auto& self = get_src(L_);
+ std::size_t r = size_start(L_, self);
+ return stack::push(L_, r);
+ }
+
+ static int clear(lua_State* L_) {
+ auto& self = get_src(L_);
+ clear_start(L_, self);
+ return 0;
+ }
+
+ static int erase(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er;
+ {
+ decltype(auto) key = stack::unqualified_get<K>(L_, 2);
+ er = erase_start(L_, self, key);
+ }
+ return handle_errors(L_, er);
+ }
+
+ static int empty(lua_State* L_) {
+ auto& self = get_src(L_);
+ return stack::push(L_, empty_start(L_, self));
+ }
+
+ static std::ptrdiff_t index_adjustment(lua_State*, T&) {
+ return static_cast<std::ptrdiff_t>((SOL_CONTAINER_START_INDEX_I_) == 0 ? 0 : -(SOL_CONTAINER_START_INDEX_I_));
+ }
+
+ static int pairs(lua_State* L_) {
+ typedef meta::any<is_associative, meta::all<is_lookup, meta::neg<is_matched_lookup>>> is_assoc;
+ return pairs_associative<false>(is_assoc(), L_);
+ }
+
+ static int ipairs(lua_State* L_) {
+ typedef meta::any<is_associative, meta::all<is_lookup, meta::neg<is_matched_lookup>>> is_assoc;
+ return pairs_associative<true>(is_assoc(), L_);
+ }
+
+ static int next(lua_State* L_) {
+ return stack::push(L_, next_iter<false>);
+ }
+ };
+
+ template <typename X>
+ struct usertype_container_default<X, std::enable_if_t<std::is_array<std::remove_pointer_t<meta::unwrap_unqualified_t<X>>>::value>> {
+ private:
+ typedef std::remove_pointer_t<meta::unwrap_unqualified_t<X>> T;
+ typedef usertype_container<X> deferred_uc;
+
+ public:
+ typedef std::remove_extent_t<T> value_type;
+ typedef value_type* iterator;
+ typedef iterator sentinel;
+
+ private:
+ struct iter : detail::ebco<iterator, 0>, detail::ebco<sentinel, 1> {
+ using it_base = detail::ebco<iterator, 0>;
+ using sen_base = detail::ebco<sentinel, 1>;
+ reference keep_alive;
+
+ iter(lua_State* L_, int stack_index_, iterator it_, sentinel sen_) noexcept
+ : it_base(std::move(it_)), sen_base(std::move(sen_)), keep_alive(sol::main_thread(L_, L_), stack_index_) {
+ }
+
+ iterator& it() noexcept {
+ return it_base::value();
+ }
+
+ const iterator& it() const noexcept {
+ return it_base::value();
+ }
+
+ sentinel& sen() noexcept {
+ return sen_base::value();
+ }
+
+ const sentinel& sen() const noexcept {
+ return sen_base::value();
+ }
+ };
+
+ static auto& get_src(lua_State* L_) {
+ auto p = stack::unqualified_check_get<T*>(L_, 1);
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ if (!p) {
+ luaL_error(L_,
+ "sol: 'self' is not of type '%s' (pass 'self' as first argument with ':' or call on proper type)",
+ detail::demangle<T>().c_str());
+ }
+ if (p.value() == nullptr) {
+ luaL_error(
+ L_, "sol: 'self' argument is nil (pass 'self' as first argument with ':' or call on a '%s' type)", detail::demangle<T>().c_str());
+ }
+#endif // Safe getting with error
+ return *p.value();
+ }
+
+ static int find(std::true_type, lua_State* L_) {
+ T& self = get_src(L_);
+ decltype(auto) value = stack::unqualified_get<value_type>(L_, 2);
+ std::size_t N = std::extent<T>::value;
+ for (std::size_t idx = 0; idx < N; ++idx) {
+ using v_t = std::add_const_t<decltype(self[idx])>;
+ v_t v = self[idx];
+ if (v == value) {
+ idx = static_cast<std::size_t>(static_cast<std::ptrdiff_t>(idx) - deferred_uc::index_adjustment(L_, self));
+ return stack::push(L_, idx);
+ }
+ }
+ return stack::push(L_, lua_nil);
+ }
+
+ static int find(std::false_type, lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'find' on '%s': no supported comparison operator for the value type", detail::demangle<T>().c_str());
+ }
+
+ static int next_iter(lua_State* L_) {
+ iter& i = stack::unqualified_get<user<iter>>(L_, 1);
+ auto& it = i.it();
+ auto& end = i.sen();
+ std::size_t k = stack::unqualified_get<std::size_t>(L_, 2);
+ if (it == end) {
+ return 0;
+ }
+ int p;
+ p = stack::push(L_, k + 1);
+ p += stack::push_reference(L_, detail::deref_move_only(*it));
+ std::advance(it, 1);
+ return p;
+ }
+
+ public:
+ static int clear(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'clear' on type '%s': cannot remove all items from a fixed array", detail::demangle<T>().c_str());
+ }
+
+ static int erase(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'erase' on type '%s': cannot remove an item from fixed arrays", detail::demangle<T>().c_str());
+ }
+
+ static int add(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'add' on type '%s': cannot add to fixed arrays", detail::demangle<T>().c_str());
+ }
+
+ static int insert(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'insert' on type '%s': cannot insert new entries into fixed arrays", detail::demangle<T>().c_str());
+ }
+
+ static int at(lua_State* L_) {
+ return get(L_);
+ }
+
+ static int get(lua_State* L_) {
+ T& self = get_src(L_);
+ std::ptrdiff_t idx = stack::unqualified_get<std::ptrdiff_t>(L_, 2);
+ idx += deferred_uc::index_adjustment(L_, self);
+ if (idx >= static_cast<std::ptrdiff_t>(std::extent<T>::value) || idx < 0) {
+ return stack::push(L_, lua_nil);
+ }
+ return stack::push_reference(L_, detail::deref_move_only(self[idx]));
+ }
+
+ static int index_get(lua_State* L_) {
+ return get(L_);
+ }
+
+ static int set(lua_State* L_) {
+ T& self = get_src(L_);
+ std::ptrdiff_t idx = stack::unqualified_get<std::ptrdiff_t>(L_, 2);
+ idx += deferred_uc::index_adjustment(L_, self);
+ if (idx >= static_cast<std::ptrdiff_t>(std::extent<T>::value)) {
+ return luaL_error(L_, "sol: index out of bounds (too big) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ if (idx < 0) {
+ return luaL_error(L_, "sol: index out of bounds (too small) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ self[idx] = stack::unqualified_get<value_type>(L_, 3);
+ return 0;
+ }
+
+ static int index_set(lua_State* L_) {
+ return set(L_);
+ }
+
+ static int index_of(lua_State* L_) {
+ return find(L_);
+ }
+
+ static int find(lua_State* L_) {
+ return find(meta::supports_op_equal<value_type, value_type>(), L_);
+ }
+
+ static int size(lua_State* L_) {
+ return stack::push(L_, std::extent<T>::value);
+ }
+
+ static int empty(lua_State* L_) {
+ return stack::push(L_, std::extent<T>::value > 0);
+ }
+
+ static int pairs(lua_State* L_) {
+ auto& src = get_src(L_);
+ stack::push(L_, next_iter);
+ stack::push<user<iter>>(L_, L_, 1, deferred_uc::begin(L_, src), deferred_uc::end(L_, src));
+ stack::push(L_, 0);
+ return 3;
+ }
+
+ static int ipairs(lua_State* L_) {
+ return pairs(L_);
+ }
+
+ static int next(lua_State* L_) {
+ return stack::push(L_, next_iter);
+ }
+
+ static std::ptrdiff_t index_adjustment(lua_State*, T&) {
+ return (SOL_CONTAINER_START_INDEX_I_) == 0 ? 0 : -(SOL_CONTAINER_START_INDEX_I_);
+ }
+
+ static iterator begin(lua_State*, T& self) {
+ return std::addressof(self[0]);
+ }
+
+ static sentinel end(lua_State*, T& self) {
+ return std::addressof(self[0]) + std::extent<T>::value;
+ }
+ };
+
+ template <typename X>
+ struct usertype_container_default<usertype_container<X>> : usertype_container_default<X> { };
+ } // namespace container_detail
+
+ template <typename T>
+ struct usertype_container : container_detail::usertype_container_default<T> { };
+
+} // namespace sol
+
+// end of sol/usertype_container.hpp
+
+#include <unordered_map>
+
+namespace sol {
+
+ namespace container_detail {
+ template <typename X>
+ struct u_c_launch {
+ using T = std::remove_pointer_t<meta::unqualified_t<X>>;
+ using uc = usertype_container<T>;
+ using default_uc = usertype_container_default<T>;
+
+ static inline int real_index_get_traits(std::true_type, lua_State* L) {
+ return uc::index_get(L);
+ }
+
+ static inline int real_index_get_traits(std::false_type, lua_State* L) {
+ return default_uc::index_get(L);
+ }
+
+ static inline int real_index_call(lua_State* L) {
+ static const std::unordered_map<string_view, lua_CFunction> calls {
+ { "at", &real_at_call },
+ { "get", &real_get_call },
+ { "set", &real_set_call },
+ { "size", &real_length_call },
+ { "add", &real_add_call },
+ { "empty", &real_empty_call },
+ { "insert", &real_insert_call },
+ { "clear", &real_clear_call },
+ { "find", &real_find_call },
+ { "index_of", &real_index_of_call },
+ { "erase", &real_erase_call },
+ { "pairs", &pairs_call },
+ { "next", &next_call },
+ };
+ auto maybenameview = stack::unqualified_check_get<string_view>(L, 2);
+ if (maybenameview) {
+ const string_view& name = *maybenameview;
+ auto it = calls.find(name);
+ if (it != calls.cend()) {
+ return stack::push(L, it->second);
+ }
+ }
+ return real_index_get_traits(container_detail::has_traits_index_get<uc>(), L);
+ }
+
+ static inline int real_at_traits(std::true_type, lua_State* L) {
+ return uc::at(L);
+ }
+
+ static inline int real_at_traits(std::false_type, lua_State* L) {
+ return default_uc::at(L);
+ }
+
+ static inline int real_at_call(lua_State* L) {
+ return real_at_traits(container_detail::has_traits_at<uc>(), L);
+ }
+
+ static inline int real_get_traits(std::true_type, lua_State* L) {
+ return uc::get(L);
+ }
+
+ static inline int real_get_traits(std::false_type, lua_State* L) {
+ return default_uc::get(L);
+ }
+
+ static inline int real_get_call(lua_State* L) {
+ return real_get_traits(container_detail::has_traits_get<uc>(), L);
+ }
+
+ static inline int real_set_traits(std::true_type, lua_State* L) {
+ return uc::set(L);
+ }
+
+ static inline int real_set_traits(std::false_type, lua_State* L) {
+ return default_uc::set(L);
+ }
+
+ static inline int real_set_call(lua_State* L) {
+ return real_set_traits(container_detail::has_traits_set<uc>(), L);
+ }
+
+ static inline int real_index_set_traits(std::true_type, lua_State* L) {
+ return uc::index_set(L);
+ }
+
+ static inline int real_index_set_traits(std::false_type, lua_State* L) {
+ return default_uc::index_set(L);
+ }
+
+ static inline int real_new_index_call(lua_State* L) {
+ return real_index_set_traits(container_detail::has_traits_index_set<uc>(), L);
+ }
+
+ static inline int real_pairs_traits(std::true_type, lua_State* L) {
+ return uc::pairs(L);
+ }
+
+ static inline int real_pairs_traits(std::false_type, lua_State* L) {
+ return default_uc::pairs(L);
+ }
+
+ static inline int real_pairs_call(lua_State* L) {
+ return real_pairs_traits(container_detail::has_traits_pairs<uc>(), L);
+ }
+
+ static inline int real_ipairs_traits(std::true_type, lua_State* L) {
+ return uc::ipairs(L);
+ }
+
+ static inline int real_ipairs_traits(std::false_type, lua_State* L) {
+ return default_uc::ipairs(L);
+ }
+
+ static inline int real_ipairs_call(lua_State* L) {
+ return real_ipairs_traits(container_detail::has_traits_ipairs<uc>(), L);
+ }
+
+ static inline int real_next_traits(std::true_type, lua_State* L) {
+ return uc::next(L);
+ }
+
+ static inline int real_next_traits(std::false_type, lua_State* L) {
+ return default_uc::next(L);
+ }
+
+ static inline int real_next_call(lua_State* L) {
+ return real_next_traits(container_detail::has_traits_next<uc>(), L);
+ }
+
+ static inline int real_size_traits(std::true_type, lua_State* L) {
+ return uc::size(L);
+ }
+
+ static inline int real_size_traits(std::false_type, lua_State* L) {
+ return default_uc::size(L);
+ }
+
+ static inline int real_length_call(lua_State* L) {
+ return real_size_traits(container_detail::has_traits_size<uc>(), L);
+ }
+
+ static inline int real_add_traits(std::true_type, lua_State* L) {
+ return uc::add(L);
+ }
+
+ static inline int real_add_traits(std::false_type, lua_State* L) {
+ return default_uc::add(L);
+ }
+
+ static inline int real_add_call(lua_State* L) {
+ return real_add_traits(container_detail::has_traits_add<uc>(), L);
+ }
+
+ static inline int real_insert_traits(std::true_type, lua_State* L) {
+ return uc::insert(L);
+ }
+
+ static inline int real_insert_traits(std::false_type, lua_State* L) {
+ return default_uc::insert(L);
+ }
+
+ static inline int real_insert_call(lua_State* L) {
+ return real_insert_traits(container_detail::has_traits_insert<uc>(), L);
+ }
+
+ static inline int real_clear_traits(std::true_type, lua_State* L) {
+ return uc::clear(L);
+ }
+
+ static inline int real_clear_traits(std::false_type, lua_State* L) {
+ return default_uc::clear(L);
+ }
+
+ static inline int real_clear_call(lua_State* L) {
+ return real_clear_traits(container_detail::has_traits_clear<uc>(), L);
+ }
+
+ static inline int real_empty_traits(std::true_type, lua_State* L) {
+ return uc::empty(L);
+ }
+
+ static inline int real_empty_traits(std::false_type, lua_State* L) {
+ return default_uc::empty(L);
+ }
+
+ static inline int real_empty_call(lua_State* L) {
+ return real_empty_traits(container_detail::has_traits_empty<uc>(), L);
+ }
+
+ static inline int real_erase_traits(std::true_type, lua_State* L) {
+ return uc::erase(L);
+ }
+
+ static inline int real_erase_traits(std::false_type, lua_State* L) {
+ return default_uc::erase(L);
+ }
+
+ static inline int real_erase_call(lua_State* L) {
+ return real_erase_traits(container_detail::has_traits_erase<uc>(), L);
+ }
+
+ static inline int real_find_traits(std::true_type, lua_State* L) {
+ return uc::find(L);
+ }
+
+ static inline int real_find_traits(std::false_type, lua_State* L) {
+ return default_uc::find(L);
+ }
+
+ static inline int real_find_call(lua_State* L) {
+ return real_find_traits(container_detail::has_traits_find<uc>(), L);
+ }
+
+ static inline int real_index_of_call(lua_State* L) {
+ if constexpr (container_detail::has_traits_index_of<uc>()) {
+ return uc::index_of(L);
+ }
+ else {
+ return default_uc::index_of(L);
+ }
+ }
+
+ static inline int add_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_add_call), (&real_add_call)>(L);
+ }
+
+ static inline int erase_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_erase_call), (&real_erase_call)>(L);
+ }
+
+ static inline int insert_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_insert_call), (&real_insert_call)>(L);
+ }
+
+ static inline int clear_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_clear_call), (&real_clear_call)>(L);
+ }
+
+ static inline int empty_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_empty_call), (&real_empty_call)>(L);
+ }
+
+ static inline int find_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_find_call), (&real_find_call)>(L);
+ }
+
+ static inline int index_of_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_index_of_call), (&real_index_of_call)>(L);
+ }
+
+ static inline int length_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_length_call), (&real_length_call)>(L);
+ }
+
+ static inline int pairs_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_pairs_call), (&real_pairs_call)>(L);
+ }
+
+ static inline int ipairs_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_ipairs_call), (&real_ipairs_call)>(L);
+ }
+
+ static inline int next_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_next_call), (&real_next_call)>(L);
+ }
+
+ static inline int at_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_at_call), (&real_at_call)>(L);
+ }
+
+ static inline int get_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_get_call), (&real_get_call)>(L);
+ }
+
+ static inline int set_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_set_call), (&real_set_call)>(L);
+ }
+
+ static inline int index_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_index_call), (&real_index_call)>(L);
+ }
+
+ static inline int new_index_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_new_index_call), (&real_new_index_call)>(L);
+ }
+ };
+ } // namespace container_detail
+
+ namespace stack {
+ namespace stack_detail {
+ template <typename T, bool is_shim = false>
+ struct metatable_setup {
+ lua_State* L;
+
+ metatable_setup(lua_State* L) : L(L) {
+ }
+
+ void operator()() {
+ using meta_usertype_container
+ = container_detail::u_c_launch<meta::conditional_t<is_shim, as_container_t<std::remove_pointer_t<T>>, std::remove_pointer_t<T>>>;
+ static const char* metakey
+ = is_shim ? &usertype_traits<as_container_t<std::remove_pointer_t<T>>>::metatable()[0] : &usertype_traits<T>::metatable()[0];
+ static const std::array<luaL_Reg, 20> reg = { {
+ // clang-format off
+ { "__pairs", &meta_usertype_container::pairs_call },
+ { "__ipairs", &meta_usertype_container::ipairs_call },
+ { "__len", &meta_usertype_container::length_call },
+ { "__index", &meta_usertype_container::index_call },
+ { "__newindex", &meta_usertype_container::new_index_call },
+ { "pairs", &meta_usertype_container::pairs_call },
+ { "next", &meta_usertype_container::next_call },
+ { "at", &meta_usertype_container::at_call },
+ { "get", &meta_usertype_container::get_call },
+ { "set", &meta_usertype_container::set_call },
+ { "size", &meta_usertype_container::length_call },
+ { "empty", &meta_usertype_container::empty_call },
+ { "clear", &meta_usertype_container::clear_call },
+ { "insert", &meta_usertype_container::insert_call },
+ { "add", &meta_usertype_container::add_call },
+ { "find", &meta_usertype_container::find_call },
+ { "index_of", &meta_usertype_container::index_of_call },
+ { "erase", &meta_usertype_container::erase_call },
+ std::is_pointer<T>::value ? luaL_Reg{ nullptr, nullptr } : luaL_Reg{ "__gc", &detail::usertype_alloc_destroy<T> },
+ { nullptr, nullptr }
+ // clang-format on
+ } };
+
+ if (luaL_newmetatable(L, metakey) == 1) {
+ luaL_setfuncs(L, reg.data(), 0);
+ }
+ lua_setmetatable(L, -2);
+ }
+ };
+ } // namespace stack_detail
+
+ template <typename T>
+ struct unqualified_pusher<as_container_t<T>> {
+ using C = meta::unqualified_t<T>;
+
+ static int push_lvalue(std::true_type, lua_State* L, const C& cont) {
+ stack_detail::metatable_setup<C*, true> fx(L);
+ return stack::push<detail::as_pointer_tag<const C>>(L, detail::with_function_tag(), fx, detail::ptr(cont));
+ }
+
+ static int push_lvalue(std::false_type, lua_State* L, const C& cont) {
+ stack_detail::metatable_setup<C, true> fx(L);
+ return stack::push<detail::as_value_tag<C>>(L, detail::with_function_tag(), fx, cont);
+ }
+
+ static int push_rvalue(std::true_type, lua_State* L, C&& cont) {
+ stack_detail::metatable_setup<C, true> fx(L);
+ return stack::push<detail::as_value_tag<C>>(L, detail::with_function_tag(), fx, std::move(cont));
+ }
+
+ static int push_rvalue(std::false_type, lua_State* L, const C& cont) {
+ return push_lvalue(std::is_lvalue_reference<T>(), L, cont);
+ }
+
+ static int push(lua_State* L, const as_container_t<T>& as_cont) {
+ return push_lvalue(std::is_lvalue_reference<T>(), L, as_cont.value());
+ }
+
+ static int push(lua_State* L, as_container_t<T>&& as_cont) {
+ return push_rvalue(meta::all<std::is_rvalue_reference<T>, meta::neg<std::is_lvalue_reference<T>>>(), L, std::forward<T>(as_cont.value()));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<as_container_t<T*>> {
+ using C = std::add_pointer_t<meta::unqualified_t<std::remove_pointer_t<T>>>;
+
+ static int push(lua_State* L, T* cont) {
+ stack_detail::metatable_setup<C> fx(L);
+ return stack::push<detail::as_pointer_tag<T>>(L, detail::with_function_tag(), fx, cont);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<T, std::enable_if_t<is_container_v<T>>> {
+ using C = T;
+
+ template <typename... Args>
+ static int push(lua_State* L, Args&&... args) {
+ stack_detail::metatable_setup<C> fx(L);
+ return stack::push<detail::as_value_tag<T>>(L, detail::with_function_tag(), fx, std::forward<Args>(args)...);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<T*, std::enable_if_t<is_container_v<T>>> {
+ using C = std::add_pointer_t<meta::unqualified_t<std::remove_pointer_t<T>>>;
+
+ static int push(lua_State* L, T* cont) {
+ stack_detail::metatable_setup<C> fx(L);
+ return stack::push<detail::as_pointer_tag<T>>(L, detail::with_function_tag(), fx, cont);
+ }
+ };
+ } // namespace stack
+
+} // namespace sol
+
+// end of sol/usertype_container_launch.hpp
+
+#include <sstream>
+#include <type_traits>
+
+namespace sol {
+ namespace u_detail {
+ constexpr const lua_Integer toplevel_magic = static_cast<lua_Integer>(0xCCC2CCC1);
+
+ constexpr const int environment_index = 1;
+ constexpr const int usertype_storage_index = 2;
+ constexpr const int usertype_storage_base_index = 3;
+ constexpr const int exact_function_index = 4;
+ constexpr const int magic_index = 5;
+
+ constexpr const int simple_usertype_storage_index = 2;
+ constexpr const int index_function_index = 3;
+ constexpr const int new_index_function_index = 4;
+
+ constexpr const int base_walking_failed_index = -32467;
+ constexpr const int lookup_failed_index = -42469;
+
+ enum class submetatable_type {
+ // must be sequential
+ value,
+ reference,
+ unique,
+ const_reference,
+ const_value,
+ // must be LAST!
+ named
+ };
+
+ inline auto make_string_view(string_view s) {
+ return s;
+ }
+
+#if SOL_IS_ON(SOL_CHAR8_T)
+ inline auto make_string_view(const char8_t* s) {
+ return string_view(reinterpret_cast<const char*>(s));
+ }
+#endif
+
+ inline auto make_string_view(call_construction) {
+ return string_view(to_string(meta_function::call_function));
+ }
+
+ inline auto make_string_view(meta_function mf) {
+ return string_view(to_string(mf));
+ }
+
+ inline auto make_string_view(base_classes_tag) {
+ return string_view(detail::base_class_cast_key());
+ }
+
+ template <typename Arg>
+ inline std::string make_string(Arg&& arg) {
+ string_view s = make_string_view(arg);
+ return std::string(s.data(), s.size());
+ }
+
+ inline int is_indexer(string_view s) {
+ if (s == to_string(meta_function::index)) {
+ return 1;
+ }
+ else if (s == to_string(meta_function::new_index)) {
+ return 2;
+ }
+ return 0;
+ }
+
+ inline int is_indexer(meta_function mf) {
+ if (mf == meta_function::index) {
+ return 1;
+ }
+ else if (mf == meta_function::new_index) {
+ return 2;
+ }
+ return 0;
+ }
+
+ inline int is_indexer(call_construction) {
+ return 0;
+ }
+ } // namespace u_detail
+
+ namespace detail {
+
+ template <typename T, typename IFx, typename Fx>
+ inline void insert_default_registrations(IFx&& ifx, Fx&& fx) {
+ (void)ifx;
+ (void)fx;
+ if constexpr (is_automagical<T>::value) {
+ if (fx(meta_function::less_than)) {
+ if constexpr (meta::supports_op_less<T>::value) {
+ lua_CFunction f = &comparsion_operator_wrap<T, std::less<>>;
+ ifx(meta_function::less_than, f);
+ }
+ }
+ if (fx(meta_function::less_than_or_equal_to)) {
+ if constexpr (meta::supports_op_less_equal<T>::value) {
+ lua_CFunction f = &comparsion_operator_wrap<T, std::less_equal<>>;
+ ifx(meta_function::less_than_or_equal_to, f);
+ }
+ }
+ if (fx(meta_function::equal_to)) {
+ if constexpr (meta::supports_op_equal<T>::value) {
+ lua_CFunction f = &comparsion_operator_wrap<T, std::equal_to<>>;
+ ifx(meta_function::equal_to, f);
+ }
+ else {
+ lua_CFunction f = &comparsion_operator_wrap<T, no_comp>;
+ ifx(meta_function::equal_to, f);
+ }
+ }
+ if (fx(meta_function::pairs)) {
+ ifx(meta_function::pairs, &container_detail::u_c_launch<as_container_t<T>>::pairs_call);
+ }
+ if (fx(meta_function::length)) {
+ if constexpr (meta::has_size<const T>::value || meta::has_size<T>::value) {
+ auto f = &default_size<T>;
+ ifx(meta_function::length, f);
+ }
+ }
+ if (fx(meta_function::to_string)) {
+ if constexpr (is_to_stringable_v<T>) {
+ if constexpr (!meta::is_probably_stateless_lambda_v<T> && !std::is_member_pointer_v<T>) {
+ auto f = &detail::static_trampoline<&default_to_string<T>>;
+ ifx(meta_function::to_string, f);
+ }
+ }
+ }
+ if (fx(meta_function::call_function)) {
+ if constexpr (is_callable_v<T>) {
+ if constexpr (meta::call_operator_deducible_v<T>) {
+ auto f = &c_call<decltype(&T::operator()), &T::operator()>;
+ ifx(meta_function::call_function, f);
+ }
+ }
+ }
+ }
+ }
+ } // namespace detail
+
+ namespace stack { namespace stack_detail {
+ template <typename X>
+ void set_undefined_methods_on(stack_reference t) {
+ using T = std::remove_pointer_t<X>;
+
+ lua_State* L = t.lua_state();
+
+ t.push();
+
+ detail::lua_reg_table l {};
+ int index = 0;
+ detail::indexed_insert insert_fx(l, index);
+ detail::insert_default_registrations<T>(insert_fx, detail::property_always_true);
+ if constexpr (!std::is_pointer_v<X>) {
+ l[index] = luaL_Reg { to_string(meta_function::garbage_collect).c_str(), detail::make_destructor<T>() };
+ }
+ luaL_setfuncs(L, l, 0);
+
+ // __type table
+ lua_createtable(L, 0, 2);
+ const std::string& name = detail::demangle<T>();
+ lua_pushlstring(L, name.c_str(), name.size());
+ lua_setfield(L, -2, "name");
+ lua_CFunction is_func = &detail::is_check<T>;
+ lua_pushcclosure(L, is_func, 0);
+ lua_setfield(L, -2, "is");
+ lua_setfield(L, t.stack_index(), to_string(meta_function::type).c_str());
+
+ t.pop();
+ }
+ }} // namespace stack::stack_detail
+} // namespace sol
+
+// end of sol/usertype_core.hpp
+
+// beginning of sol/usertype_storage.hpp
+
+#include <bitset>
+#include <unordered_map>
+#include <memory>
+
+namespace sol { namespace u_detail {
+
+ struct usertype_storage_base;
+ template <typename T>
+ struct usertype_storage;
+
+ optional<usertype_storage_base&> maybe_get_usertype_storage_base(lua_State* L_, int index);
+ usertype_storage_base& get_usertype_storage_base(lua_State* L_, const char* gcmetakey);
+ template <typename T>
+ optional<usertype_storage<T>&> maybe_get_usertype_storage(lua_State* L_);
+ template <typename T>
+ usertype_storage<T>& get_usertype_storage(lua_State* L_);
+
+ using index_call_function = int(lua_State*, void*);
+ using change_indexing_mem_func = void (usertype_storage_base::*)(
+ lua_State*, submetatable_type, void*, stateless_stack_reference&, lua_CFunction, lua_CFunction, lua_CFunction, lua_CFunction);
+
+ struct index_call_storage {
+ index_call_function* index;
+ index_call_function* new_index;
+ void* binding_data;
+ };
+
+ struct new_index_call_storage : index_call_storage {
+ void* new_binding_data;
+ };
+
+ struct binding_base {
+ virtual void* data() = 0;
+ virtual ~binding_base() {
+ }
+ };
+
+ template <typename K, typename Fq, typename T = void>
+ struct binding : binding_base {
+ using uF = meta::unqualified_t<Fq>;
+ using F = meta::conditional_t<meta::is_c_str_of_v<uF, char>
+#if SOL_IS_ON(SOL_CHAR8_T)
+ || meta::is_c_str_of_v<uF, char8_t>
+#endif
+ || meta::is_c_str_of_v<uF, char16_t> || meta::is_c_str_of_v<uF, char32_t> || meta::is_c_str_of_v<uF, wchar_t>,
+ std::add_pointer_t<std::add_const_t<std::remove_all_extents_t<Fq>>>, std::decay_t<Fq>>;
+ F data_;
+
+ template <typename... Args>
+ binding(Args&&... args) : data_(std::forward<Args>(args)...) {
+ }
+
+ virtual void* data() override {
+ return static_cast<void*>(std::addressof(data_));
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int call_with_(lua_State* L_, void* target) {
+ constexpr int boost = !detail::is_non_factory_constructor<F>::value && std::is_same<K, call_construction>::value ? 1 : 0;
+ auto& f = *static_cast<F*>(target);
+ return call_detail::call_wrapped<T, is_index, is_variable, boost>(L_, f);
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int call_(lua_State* L_) {
+ void* f = stack::get<void*>(L_, upvalue_index(usertype_storage_index));
+ return call_with_<is_index, is_variable>(L_, f);
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int call(lua_State* L_) {
+ int r = detail::typed_static_trampoline<decltype(&call_<is_index, is_variable>), (&call_<is_index, is_variable>)>(L_);
+ if constexpr (meta::is_specialization_of_v<uF, yielding_t>) {
+ return lua_yield(L_, r);
+ }
+ else {
+ return r;
+ }
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int index_call_with_(lua_State* L_, void* target) {
+ if constexpr (!is_variable) {
+ if constexpr (is_lua_c_function_v<std::decay_t<F>>) {
+ auto& f = *static_cast<std::decay_t<F>*>(target);
+ return stack::push(L_, f);
+ }
+ else {
+ // set up upvalues
+ // for a chained call
+ int upvalues = 0;
+ upvalues += stack::push(L_, nullptr);
+ upvalues += stack::push(L_, target);
+ auto cfunc = &call<is_index, is_variable>;
+ return stack::push(L_, c_closure(cfunc, upvalues));
+ }
+ }
+ else {
+ constexpr int boost = !detail::is_non_factory_constructor<F>::value && std::is_same<K, call_construction>::value ? 1 : 0;
+ auto& f = *static_cast<F*>(target);
+ return call_detail::call_wrapped<T, is_index, is_variable, boost>(L_, f);
+ }
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int index_call_(lua_State* L_) {
+ void* f = stack::get<void*>(L_, upvalue_index(usertype_storage_index));
+ return index_call_with_<is_index, is_variable>(L_, f);
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int index_call(lua_State* L_) {
+ int r = detail::typed_static_trampoline<decltype(&index_call_<is_index, is_variable>), (&index_call_<is_index, is_variable>)>(L_);
+ if constexpr (meta::is_specialization_of_v<uF, yielding_t>) {
+ return lua_yield(L_, r);
+ }
+ else {
+ return r;
+ }
+ }
+ };
+
+ inline int index_fail(lua_State* L_) {
+ if (lua_getmetatable(L_, 1) == 1) {
+ int metatarget = lua_gettop(L_);
+ stack::get_field<false, true>(L_, stack_reference(L_, raw_index(2)), metatarget);
+ return 1;
+ }
+ // With runtime extensibility, we can't
+ // hard-error things. They have to
+ // return nil, like regular table types
+ return stack::push(L_, lua_nil);
+ }
+
+ inline int index_target_fail(lua_State* L_, void*) {
+ return index_fail(L_);
+ }
+
+ inline int new_index_fail(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot set (new_index) into this object: no defined new_index operation on usertype");
+ }
+
+ inline int new_index_target_fail(lua_State* L_, void*) {
+ return new_index_fail(L_);
+ }
+
+ struct string_for_each_metatable_func {
+ bool is_destruction = false;
+ bool is_index = false;
+ bool is_new_index = false;
+ bool is_static_index = false;
+ bool is_static_new_index = false;
+ bool poison_indexing = false;
+ bool is_unqualified_lua_CFunction = false;
+ bool is_unqualified_lua_reference = false;
+ std::string* p_key = nullptr;
+ reference* p_binding_ref = nullptr;
+ lua_CFunction call_func = nullptr;
+ index_call_storage* p_ics = nullptr;
+ usertype_storage_base* p_usb = nullptr;
+ void* p_derived_usb = nullptr;
+ lua_CFunction idx_call = nullptr, new_idx_call = nullptr, meta_idx_call = nullptr, meta_new_idx_call = nullptr;
+ change_indexing_mem_func change_indexing;
+
+ void operator()(lua_State* L_, submetatable_type smt_, stateless_reference& fast_index_table_) {
+ std::string& key = *p_key;
+ usertype_storage_base& usb = *p_usb;
+ index_call_storage& ics = *p_ics;
+
+ if (smt_ == submetatable_type::named) {
+ // do not override __call or
+ // other specific meta functions on named metatable:
+ // we need that for call construction
+ // and other amenities
+ return;
+ }
+ int fast_index_table_push = fast_index_table_.push(L_);
+ stateless_stack_reference t(L_, -fast_index_table_push);
+ if (poison_indexing) {
+ (usb.*change_indexing)(L_, smt_, p_derived_usb, t, idx_call, new_idx_call, meta_idx_call, meta_new_idx_call);
+ }
+ if (is_destruction
+ && (smt_ == submetatable_type::reference || smt_ == submetatable_type::const_reference || smt_ == submetatable_type::named
+ || smt_ == submetatable_type::unique)) {
+ // gc does not apply to us here
+ // for reference types (raw T*, std::ref)
+ // for the named metatable itself,
+ // or for unique_usertypes, which do their own custom destroyion
+ t.pop(L_);
+ return;
+ }
+ if (is_index || is_new_index || is_static_index || is_static_new_index) {
+ // do not serialize the new_index and index functions here directly
+ // we control those...
+ t.pop(L_);
+ return;
+ }
+ if (is_unqualified_lua_CFunction) {
+ stack::set_field<false, true>(L_, key, call_func, t.stack_index());
+ }
+ else if (is_unqualified_lua_reference) {
+ reference& binding_ref = *p_binding_ref;
+ stack::set_field<false, true>(L_, key, binding_ref, t.stack_index());
+ }
+ else {
+ stack::set_field<false, true>(L_, key, make_closure(call_func, nullptr, ics.binding_data), t.stack_index());
+ }
+ t.pop(L_);
+ }
+ };
+
+ struct lua_reference_func {
+ reference key;
+ reference value;
+
+ void operator()(lua_State* L_, submetatable_type smt_, stateless_reference& fast_index_table_) {
+ if (smt_ == submetatable_type::named) {
+ return;
+ }
+ int fast_index_table_push = fast_index_table_.push(L_);
+ stateless_stack_reference t(L_, -fast_index_table_push);
+ stack::set_field<false, true>(L_, key, value, t.stack_index());
+ t.pop(L_);
+ }
+ };
+
+ struct update_bases_func {
+ detail::inheritance_check_function base_class_check_func;
+ detail::inheritance_cast_function base_class_cast_func;
+ lua_CFunction idx_call, new_idx_call, meta_idx_call, meta_new_idx_call;
+ usertype_storage_base* p_usb;
+ void* p_derived_usb;
+ change_indexing_mem_func change_indexing;
+
+ void operator()(lua_State* L_, submetatable_type smt_, stateless_reference& fast_index_table_) {
+ int fast_index_table_push = fast_index_table_.push(L_);
+ stateless_stack_reference t(L_, -fast_index_table_push);
+ stack::set_field(L_, detail::base_class_check_key(), reinterpret_cast<void*>(base_class_check_func), t.stack_index());
+ stack::set_field(L_, detail::base_class_cast_key(), reinterpret_cast<void*>(base_class_cast_func), t.stack_index());
+ // change indexing, forcefully
+ (p_usb->*change_indexing)(L_, smt_, p_derived_usb, t, idx_call, new_idx_call, meta_idx_call, meta_new_idx_call);
+ t.pop(L_);
+ }
+ };
+
+ struct binding_data_equals {
+ void* binding_data;
+
+ binding_data_equals(void* b) : binding_data(b) {
+ }
+
+ bool operator()(const std::unique_ptr<binding_base>& ptr) const {
+ return binding_data == ptr->data();
+ }
+ };
+
+ struct usertype_storage_base {
+ public:
+ lua_State* m_L;
+ std::vector<std::unique_ptr<binding_base>> storage;
+ std::vector<std::unique_ptr<char[]>> string_keys_storage;
+ std::unordered_map<string_view, index_call_storage> string_keys;
+ std::unordered_map<stateless_reference, stateless_reference, stateless_reference_hash, stateless_reference_equals> auxiliary_keys;
+ stateless_reference value_index_table;
+ stateless_reference reference_index_table;
+ stateless_reference unique_index_table;
+ stateless_reference const_reference_index_table;
+ stateless_reference const_value_index_table;
+ stateless_reference named_index_table;
+ stateless_reference type_table;
+ stateless_reference gc_names_table;
+ stateless_reference named_metatable;
+ new_index_call_storage base_index;
+ new_index_call_storage static_base_index;
+ bool is_using_index;
+ bool is_using_new_index;
+ std::bitset<64> properties;
+
+ usertype_storage_base(lua_State* L_)
+ : m_L(L_)
+ , storage()
+ , string_keys_storage()
+ , string_keys()
+ , auxiliary_keys(0, stateless_reference_hash(L_), stateless_reference_equals(L_))
+ , value_index_table()
+ , reference_index_table()
+ , unique_index_table()
+ , const_reference_index_table()
+ , const_value_index_table()
+ , named_index_table()
+ , type_table(make_reference<stateless_reference>(L_, create))
+ , gc_names_table(make_reference<stateless_reference>(L_, create))
+ , named_metatable(make_reference<stateless_reference>(L_, create))
+ , base_index()
+ , static_base_index()
+ , is_using_index(false)
+ , is_using_new_index(false)
+ , properties() {
+ base_index.binding_data = nullptr;
+ base_index.index = index_target_fail;
+ base_index.new_index = new_index_target_fail;
+ base_index.new_binding_data = nullptr;
+ static_base_index.binding_data = nullptr;
+ static_base_index.index = index_target_fail;
+ static_base_index.new_binding_data = this;
+ static_base_index.new_index = new_index_target_set;
+ }
+
+ template <typename Fx>
+ void for_each_table(lua_State* L_, Fx&& fx) {
+ for (int i = 0; i < 6; ++i) {
+ submetatable_type smt = static_cast<submetatable_type>(i);
+ stateless_reference* p_fast_index_table = nullptr;
+ switch (smt) {
+ case submetatable_type::const_value:
+ p_fast_index_table = &this->const_value_index_table;
+ break;
+ case submetatable_type::reference:
+ p_fast_index_table = &this->reference_index_table;
+ break;
+ case submetatable_type::unique:
+ p_fast_index_table = &this->unique_index_table;
+ break;
+ case submetatable_type::const_reference:
+ p_fast_index_table = &this->const_reference_index_table;
+ break;
+ case submetatable_type::named:
+ p_fast_index_table = &this->named_index_table;
+ break;
+ case submetatable_type::value:
+ default:
+ p_fast_index_table = &this->value_index_table;
+ break;
+ }
+ fx(L_, smt, *p_fast_index_table);
+ }
+ }
+
+ void add_entry(string_view sv, index_call_storage ics) {
+ string_keys_storage.emplace_back(new char[sv.size()]);
+ std::unique_ptr<char[]>& sv_storage = string_keys_storage.back();
+ std::memcpy(sv_storage.get(), sv.data(), sv.size());
+ string_view stored_sv(sv_storage.get(), sv.size());
+ string_keys.insert_or_assign(std::move(stored_sv), std::move(ics));
+ }
+
+ template <typename T, typename... Bases>
+ void update_bases(lua_State* L_, bases<Bases...>) {
+ static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function),
+ "The size of this data pointer is too small to fit the inheritance checking function: Please file "
+ "a bug report.");
+ static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function),
+ "The size of this data pointer is too small to fit the inheritance checking function: Please file "
+ "a bug report.");
+ static_assert(!meta::any_same<T, Bases...>::value, "base classes cannot list the original class as part of the bases");
+ if constexpr (sizeof...(Bases) > 0) {
+ (void)detail::swallow { 0, ((weak_derive<Bases>::value = true), 0)... };
+
+ void* derived_this = static_cast<void*>(static_cast<usertype_storage<T>*>(this));
+
+ update_bases_func for_each_fx;
+ for_each_fx.base_class_check_func = &detail::inheritance<T>::template type_check_with<Bases...>;
+ for_each_fx.base_class_cast_func = &detail::inheritance<T>::template type_cast_with<Bases...>;
+ for_each_fx.idx_call = &usertype_storage<T>::template index_call_with_bases<false, Bases...>;
+ for_each_fx.new_idx_call = &usertype_storage<T>::template index_call_with_bases<true, Bases...>;
+ for_each_fx.meta_idx_call = &usertype_storage<T>::template meta_index_call_with_bases<false, Bases...>;
+ for_each_fx.meta_new_idx_call = &usertype_storage<T>::template meta_index_call_with_bases<true, Bases...>;
+ for_each_fx.p_usb = this;
+ for_each_fx.p_derived_usb = derived_this;
+ for_each_fx.change_indexing = &usertype_storage_base::change_indexing;
+ for_each_fx.p_derived_usb = derived_this;
+ this->for_each_table(L_, for_each_fx);
+ }
+ else {
+ (void)L_;
+ }
+ }
+
+ void clear() {
+ if (value_index_table.valid(m_L)) {
+ stack::clear(m_L, value_index_table);
+ }
+ if (reference_index_table.valid(m_L)) {
+ stack::clear(m_L, reference_index_table);
+ }
+ if (unique_index_table.valid(m_L)) {
+ stack::clear(m_L, unique_index_table);
+ }
+ if (const_reference_index_table.valid(m_L)) {
+ stack::clear(m_L, const_reference_index_table);
+ }
+ if (const_value_index_table.valid(m_L)) {
+ stack::clear(m_L, const_value_index_table);
+ }
+ if (named_index_table.valid(m_L)) {
+ stack::clear(m_L, named_index_table);
+ }
+ if (type_table.valid(m_L)) {
+ stack::clear(m_L, type_table);
+ }
+ if (gc_names_table.valid(m_L)) {
+ stack::clear(m_L, gc_names_table);
+ }
+ if (named_metatable.valid(m_L)) {
+ auto pp = stack::push_pop(m_L, named_metatable);
+ int named_metatable_index = pp.index_of(named_metatable);
+ if (lua_getmetatable(m_L, named_metatable_index) == 1) {
+ stack::clear(m_L, absolute_index(m_L, -1));
+ }
+ stack::clear(m_L, named_metatable);
+ }
+
+ value_index_table.reset(m_L);
+ reference_index_table.reset(m_L);
+ unique_index_table.reset(m_L);
+ const_reference_index_table.reset(m_L);
+ const_value_index_table.reset(m_L);
+ named_index_table.reset(m_L);
+ type_table.reset(m_L);
+ gc_names_table.reset(m_L);
+ named_metatable.reset(m_L);
+
+ storage.clear();
+ string_keys.clear();
+ auxiliary_keys.clear();
+ string_keys_storage.clear();
+ }
+
+ template <bool is_new_index, typename Base>
+ static void base_walk_index(lua_State* L_, usertype_storage_base& self, bool& keep_going, int& base_result) {
+ using bases = typename base<Base>::type;
+ if (!keep_going) {
+ return;
+ }
+ (void)L_;
+ (void)self;
+#if SOL_IS_ON(SOL_USE_UNSAFE_BASE_LOOKUP)
+ usertype_storage_base& base_storage = get_usertype_storage<Base>(L_);
+ base_result = self_index_call<is_new_index, true>(bases(), L_, base_storage);
+#else
+ optional<usertype_storage<Base>&> maybe_base_storage = maybe_get_usertype_storage<Base>(L_);
+ if (static_cast<bool>(maybe_base_storage)) {
+ base_result = self_index_call<is_new_index, true>(bases(), L_, *maybe_base_storage);
+ keep_going = base_result == base_walking_failed_index;
+ }
+#endif // Fast versus slow, safe base lookup
+ }
+
+ template <bool is_new_index = false, bool base_walking = false, bool from_named_metatable = false, typename... Bases>
+ static inline int self_index_call(types<Bases...>, lua_State* L, usertype_storage_base& self) {
+ if constexpr (!from_named_metatable || !is_new_index) {
+ type k_type = stack::get<type>(L, 2);
+ if (k_type == type::string) {
+ index_call_storage* target = nullptr;
+ string_view k = stack::get<string_view>(L, 2);
+ {
+ auto it = self.string_keys.find(k);
+ if (it != self.string_keys.cend()) {
+ target = &it->second;
+ }
+ }
+ if (target != nullptr) {
+ // let the target decide what to do, unless it's named...
+ if constexpr (is_new_index) {
+ return (target->new_index)(L, target->binding_data);
+ }
+ else {
+ return (target->index)(L, target->binding_data);
+ }
+ }
+ }
+ else if (k_type != type::lua_nil && k_type != type::none) {
+ stateless_reference* target = nullptr;
+ {
+ stack_reference k = stack::get<stack_reference>(L, 2);
+ auto it = self.auxiliary_keys.find(k);
+ if (it != self.auxiliary_keys.cend()) {
+ target = &it->second;
+ }
+ }
+ if (target != nullptr) {
+ if constexpr (is_new_index) {
+ // set value and return
+ target->reset(L, 3);
+ return 0;
+ }
+ else {
+ // push target to return
+ // what we found
+ return stack::push(L, *target);
+ }
+ }
+ }
+ }
+
+ // retrieve bases and walk through them.
+ bool keep_going = true;
+ int base_result;
+ (void)keep_going;
+ (void)base_result;
+ (void)detail::swallow { 1, (base_walk_index<is_new_index, Bases>(L, self, keep_going, base_result), 1)... };
+ if constexpr (sizeof...(Bases) > 0) {
+ if (!keep_going) {
+ return base_result;
+ }
+ }
+ if constexpr (base_walking) {
+ // if we're JUST base-walking then don't index-fail, just
+ // return the false bits
+ return base_walking_failed_index;
+ }
+ else if constexpr (from_named_metatable) {
+ if constexpr (is_new_index) {
+ return self.static_base_index.new_index(L, self.static_base_index.new_binding_data);
+ }
+ else {
+ return self.static_base_index.index(L, self.static_base_index.binding_data);
+ }
+ }
+ else {
+ if constexpr (is_new_index) {
+ return self.base_index.new_index(L, self.base_index.new_binding_data);
+ }
+ else {
+ return self.base_index.index(L, self.base_index.binding_data);
+ }
+ }
+ }
+
+ void change_indexing(lua_State* L_, submetatable_type submetatable_, void* derived_this_, stateless_stack_reference& t_, lua_CFunction index_,
+ lua_CFunction new_index_, lua_CFunction meta_index_, lua_CFunction meta_new_index_) {
+ usertype_storage_base& this_base = *this;
+ void* base_this = static_cast<void*>(&this_base);
+
+ this->is_using_index |= true;
+ this->is_using_new_index |= true;
+ if (submetatable_ == submetatable_type::named) {
+ stack::set_field(L_, metatable_key, named_index_table, t_.stack_index());
+ stateless_stack_reference stack_metametatable(L_, -named_metatable.push(L_));
+ stack::set_field<false, true>(L_,
+ meta_function::index,
+ make_closure(meta_index_, nullptr, derived_this_, base_this, nullptr, toplevel_magic),
+ stack_metametatable.stack_index());
+ stack::set_field<false, true>(L_,
+ meta_function::new_index,
+ make_closure(meta_new_index_, nullptr, derived_this_, base_this, nullptr, toplevel_magic),
+ stack_metametatable.stack_index());
+ stack_metametatable.pop(L_);
+ }
+ else {
+ stack::set_field<false, true>(
+ L_, meta_function::index, make_closure(index_, nullptr, derived_this_, base_this, nullptr, toplevel_magic), t_.stack_index());
+ stack::set_field<false, true>(
+ L_, meta_function::new_index, make_closure(new_index_, nullptr, derived_this_, base_this, nullptr, toplevel_magic), t_.stack_index());
+ }
+ }
+
+ template <typename T = void, typename Key, typename Value>
+ void set(lua_State* L, Key&& key, Value&& value);
+
+ static int new_index_target_set(lua_State* L, void* target) {
+ usertype_storage_base& self = *static_cast<usertype_storage_base*>(target);
+ self.set(L, reference(L, raw_index(2)), reference(L, raw_index(3)));
+ return 0;
+ }
+
+ ~usertype_storage_base() {
+ value_index_table.reset(m_L);
+ reference_index_table.reset(m_L);
+ unique_index_table.reset(m_L);
+ const_reference_index_table.reset(m_L);
+ const_value_index_table.reset(m_L);
+ named_index_table.reset(m_L);
+ type_table.reset(m_L);
+ gc_names_table.reset(m_L);
+ named_metatable.reset(m_L);
+
+ auto auxiliary_first = auxiliary_keys.cbegin();
+ auto auxiliary_last = auxiliary_keys.cend();
+ while (auxiliary_first != auxiliary_last) {
+ // save a copy to what we're going to destroy
+ auto auxiliary_target = auxiliary_first;
+ // move the iterator up by 1
+ ++auxiliary_first;
+ // extract the node and destroy the key
+ auto extracted_node = auxiliary_keys.extract(auxiliary_target);
+ extracted_node.key().reset(m_L);
+ extracted_node.mapped().reset(m_L);
+ // continue if auxiliary_first hasn't been exhausted
+ }
+ }
+ };
+
+ template <typename T>
+ struct usertype_storage : usertype_storage_base {
+
+ using usertype_storage_base::usertype_storage_base;
+
+ template <bool is_new_index, bool from_named_metatable>
+ static inline int index_call_(lua_State* L) {
+ using bases = typename base<T>::type;
+ usertype_storage_base& self = stack::get<light<usertype_storage_base>>(L, upvalue_index(usertype_storage_index));
+ return self_index_call<is_new_index, false, from_named_metatable>(bases(), L, self);
+ }
+
+ template <bool is_new_index, bool from_named_metatable, typename... Bases>
+ static inline int index_call_with_bases_(lua_State* L) {
+ using bases = types<Bases...>;
+ usertype_storage_base& self = stack::get<light<usertype_storage_base>>(L, upvalue_index(usertype_storage_index));
+ return self_index_call<is_new_index, false, from_named_metatable>(bases(), L, self);
+ }
+
+ template <bool is_new_index>
+ static inline int index_call(lua_State* L) {
+ return detail::static_trampoline<&index_call_<is_new_index, false>>(L);
+ }
+
+ template <bool is_new_index, typename... Bases>
+ static inline int index_call_with_bases(lua_State* L) {
+ return detail::static_trampoline<&index_call_with_bases_<is_new_index, false, Bases...>>(L);
+ }
+
+ template <bool is_new_index>
+ static inline int meta_index_call(lua_State* L) {
+ return detail::static_trampoline<&index_call_<is_new_index, true>>(L);
+ }
+
+ template <bool is_new_index, typename... Bases>
+ static inline int meta_index_call_with_bases(lua_State* L) {
+ return detail::static_trampoline<&index_call_with_bases_<is_new_index, true, Bases...>>(L);
+ }
+
+ template <typename Key, typename Value>
+ inline void set(lua_State* L, Key&& key, Value&& value);
+ };
+
+ template <typename T, typename Key, typename Value>
+ void usertype_storage_base::set(lua_State* L, Key&& key, Value&& value) {
+ using ValueU = meta::unwrap_unqualified_t<Value>;
+ using KeyU = meta::unwrap_unqualified_t<Key>;
+ using Binding = binding<KeyU, ValueU, T>;
+ using is_var_bind = is_variable_binding<ValueU>;
+ if constexpr (std::is_same_v<KeyU, call_construction>) {
+ (void)key;
+ std::unique_ptr<Binding> p_binding = std::make_unique<Binding>(std::forward<Value>(value));
+ Binding& b = *p_binding;
+ this->storage.push_back(std::move(p_binding));
+
+ this->named_index_table.push(L);
+ absolute_index metametatable_index(L, -1);
+ std::string_view call_metamethod_name = to_string(meta_function::call);
+ lua_pushlstring(L, call_metamethod_name.data(), call_metamethod_name.size());
+ stack::push(L, nullptr);
+ stack::push(L, b.data());
+ lua_CFunction target_func = &b.template call<false, false>;
+ lua_pushcclosure(L, target_func, 2);
+ lua_rawset(L, metametatable_index);
+ this->named_index_table.pop(L);
+ }
+ else if constexpr (std::is_same_v<KeyU, base_classes_tag>) {
+ (void)key;
+ this->update_bases<T>(L, std::forward<Value>(value));
+ }
+ else if constexpr ((meta::is_string_like_or_constructible<KeyU>::value || std::is_same_v<KeyU, meta_function>)) {
+ std::string s = u_detail::make_string(std::forward<Key>(key));
+ auto storage_it = this->storage.end();
+ auto string_it = this->string_keys.find(s);
+ if (string_it != this->string_keys.cend()) {
+ const auto& binding_data = string_it->second.binding_data;
+ storage_it = std::find_if(this->storage.begin(), this->storage.end(), binding_data_equals(binding_data));
+ this->string_keys.erase(string_it);
+ }
+
+ std::unique_ptr<Binding> p_binding = std::make_unique<Binding>(std::forward<Value>(value));
+ Binding& b = *p_binding;
+ if (storage_it != this->storage.cend()) {
+ *storage_it = std::move(p_binding);
+ }
+ else {
+ this->storage.push_back(std::move(p_binding));
+ }
+
+ bool is_index = (s == to_string(meta_function::index));
+ bool is_new_index = (s == to_string(meta_function::new_index));
+ bool is_static_index = (s == to_string(meta_function::static_index));
+ bool is_static_new_index = (s == to_string(meta_function::static_new_index));
+ bool is_destruction = s == to_string(meta_function::garbage_collect);
+ bool poison_indexing = (!is_using_index || !is_using_new_index) && (is_var_bind::value || is_index || is_new_index);
+ void* derived_this = static_cast<void*>(static_cast<usertype_storage<T>*>(this));
+ index_call_storage ics;
+ ics.binding_data = b.data();
+ ics.index = is_index || is_static_index ? &Binding::template call_with_<true, is_var_bind::value>
+ : &Binding::template index_call_with_<true, is_var_bind::value>;
+ ics.new_index = is_new_index || is_static_new_index ? &Binding::template call_with_<false, is_var_bind::value>
+ : &Binding::template index_call_with_<false, is_var_bind::value>;
+
+ string_for_each_metatable_func for_each_fx;
+ for_each_fx.is_destruction = is_destruction;
+ for_each_fx.is_index = is_index;
+ for_each_fx.is_new_index = is_new_index;
+ for_each_fx.is_static_index = is_static_index;
+ for_each_fx.is_static_new_index = is_static_new_index;
+ for_each_fx.poison_indexing = poison_indexing;
+ for_each_fx.p_key = &s;
+ for_each_fx.p_ics = &ics;
+ if constexpr (is_lua_c_function_v<ValueU>) {
+ for_each_fx.is_unqualified_lua_CFunction = true;
+ for_each_fx.call_func = *static_cast<lua_CFunction*>(ics.binding_data);
+ }
+ else if constexpr (is_lua_reference_or_proxy_v<ValueU>) {
+ for_each_fx.is_unqualified_lua_reference = true;
+ for_each_fx.p_binding_ref = static_cast<reference*>(ics.binding_data);
+ }
+ else {
+ for_each_fx.call_func = &b.template call<false, is_var_bind::value>;
+ }
+ for_each_fx.p_usb = this;
+ for_each_fx.p_derived_usb = derived_this;
+ for_each_fx.idx_call = &usertype_storage<T>::template index_call<false>;
+ for_each_fx.new_idx_call = &usertype_storage<T>::template index_call<true>;
+ for_each_fx.meta_idx_call = &usertype_storage<T>::template meta_index_call<false>;
+ for_each_fx.meta_new_idx_call = &usertype_storage<T>::template meta_index_call<true>;
+ for_each_fx.change_indexing = &usertype_storage_base::change_indexing;
+ // set base index and base new_index
+ // functions here
+ if (is_index) {
+ this->base_index.index = ics.index;
+ this->base_index.binding_data = ics.binding_data;
+ }
+ if (is_new_index) {
+ this->base_index.new_index = ics.new_index;
+ this->base_index.new_binding_data = ics.binding_data;
+ }
+ if (is_static_index) {
+ this->static_base_index.index = ics.index;
+ this->static_base_index.binding_data = ics.binding_data;
+ }
+ if (is_static_new_index) {
+ this->static_base_index.new_index = ics.new_index;
+ this->static_base_index.new_binding_data = ics.binding_data;
+ }
+ this->for_each_table(L, for_each_fx);
+ this->add_entry(s, std::move(ics));
+ }
+ else {
+ // the reference-based implementation might compare poorly and hash
+ // poorly in some cases...
+ if constexpr (is_lua_reference_v<KeyU> && is_lua_reference_v<ValueU>) {
+ if (key.get_type() == type::string) {
+ stack::push(L, key);
+ std::string string_key = stack::pop<std::string>(L);
+ this->set<T>(L, string_key, std::forward<Value>(value));
+ }
+ else {
+ lua_reference_func ref_additions_fx { key, value };
+
+ this->for_each_table(L, ref_additions_fx);
+ this->auxiliary_keys.insert_or_assign(std::forward<Key>(key), std::forward<Value>(value));
+ }
+ }
+ else {
+ reference ref_key = make_reference(L, std::forward<Key>(key));
+ reference ref_value = make_reference(L, std::forward<Value>(value));
+ lua_reference_func ref_additions_fx { ref_key, ref_value };
+
+ this->for_each_table(L, ref_additions_fx);
+ this->auxiliary_keys.insert_or_assign(std::move(ref_key), std::move(ref_value));
+ }
+ }
+ }
+
+ template <typename T>
+ template <typename Key, typename Value>
+ void usertype_storage<T>::set(lua_State* L, Key&& key, Value&& value) {
+ static_cast<usertype_storage_base&>(*this).set<T>(L, std::forward<Key>(key), std::forward<Value>(value));
+ }
+
+ template <typename T>
+ inline void clear_usertype_registry_names(lua_State* L) {
+ using u_traits = usertype_traits<T>;
+ using u_const_traits = usertype_traits<const T>;
+ using u_unique_traits = usertype_traits<d::u<T>>;
+ using u_ref_traits = usertype_traits<T*>;
+ using u_const_ref_traits = usertype_traits<T const*>;
+
+ stack_reference registry(L, raw_index(LUA_REGISTRYINDEX));
+ registry.push();
+ // eliminate all named entries for this usertype
+ // in the registry (luaL_newmetatable does
+ // [name] = new table
+ // in registry upon creation
+ stack::set_field(L, &u_traits::metatable()[0], lua_nil, registry.stack_index());
+ stack::set_field(L, &u_const_traits::metatable()[0], lua_nil, registry.stack_index());
+ stack::set_field(L, &u_const_ref_traits::metatable()[0], lua_nil, registry.stack_index());
+ stack::set_field(L, &u_ref_traits::metatable()[0], lua_nil, registry.stack_index());
+ stack::set_field(L, &u_unique_traits::metatable()[0], lua_nil, registry.stack_index());
+ registry.pop();
+ }
+
+ template <typename T>
+ inline int destroy_usertype_storage(lua_State* L) noexcept {
+ clear_usertype_registry_names<T>(L);
+ return detail::user_alloc_destroy<usertype_storage<T>>(L);
+ }
+
+ template <typename T>
+ inline usertype_storage<T>& create_usertype_storage(lua_State* L) {
+ const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
+
+ // Make sure userdata's memory is properly in lua first,
+ // otherwise all the light userdata we make later will become invalid
+ int usertype_storage_push_count = stack::push<user<usertype_storage<T>>>(L, no_metatable, L);
+ stack_reference usertype_storage_ref(L, -usertype_storage_push_count);
+
+ // create and push onto the stack a table to use as metatable for this GC
+ // we create a metatable to attach to the regular gc_table
+ // so that the destructor is called for the usertype storage
+ int usertype_storage_metatabe_count = stack::push(L, new_table(0, 1));
+ stack_reference usertype_storage_metatable(L, -usertype_storage_metatabe_count);
+ // set the destroyion routine on the metatable
+ stack::set_field(L, meta_function::garbage_collect, &destroy_usertype_storage<T>, usertype_storage_metatable.stack_index());
+ // set the metatable on the usertype storage userdata
+ stack::set_field(L, metatable_key, usertype_storage_metatable, usertype_storage_ref.stack_index());
+ usertype_storage_metatable.pop();
+
+ // set the usertype storage and its metatable
+ // into the global table...
+ stack::set_field<true>(L, gcmetakey, usertype_storage_ref);
+ usertype_storage_ref.pop();
+
+ // then retrieve the lua-stored version so we have a well-pinned
+ // reference that does not die
+ stack::get_field<true>(L, gcmetakey);
+ usertype_storage<T>& target_umt = stack::pop<user<usertype_storage<T>>>(L);
+ return target_umt;
+ }
+
+ inline optional<usertype_storage_base&> maybe_as_usertype_storage_base(lua_State* L, int index) {
+ if (type_of(L, index) != type::lightuserdata) {
+ return nullopt;
+ }
+ usertype_storage_base& base_storage = *static_cast<usertype_storage_base*>(stack::get<void*>(L, index));
+ return base_storage;
+ }
+
+ inline optional<usertype_storage_base&> maybe_get_usertype_storage_base_inside(lua_State* L, int index) {
+ // okay, maybe we're looking at a table that is nested?
+ if (type_of(L, index) != type::table) {
+ return nullopt;
+ }
+ stack::get_field(L, meta_function::storage, index);
+ auto maybe_storage_base = maybe_as_usertype_storage_base(L, -1);
+ lua_pop(L, 1);
+ return maybe_storage_base;
+ }
+
+ inline optional<usertype_storage_base&> maybe_get_usertype_storage_base(lua_State* L, int index) {
+ // If we can get the index directly as this type, go for it
+ auto maybe_already_is_usertype_storage_base = maybe_as_usertype_storage_base(L, index);
+ if (maybe_already_is_usertype_storage_base) {
+ return maybe_already_is_usertype_storage_base;
+ }
+ return maybe_get_usertype_storage_base_inside(L, index);
+ }
+
+ inline optional<usertype_storage_base&> maybe_get_usertype_storage_base(lua_State* L, const char* gcmetakey) {
+ stack::get_field<true>(L, gcmetakey);
+ auto maybe_storage = maybe_as_usertype_storage_base(L, lua_gettop(L));
+ lua_pop(L, 1);
+ return maybe_storage;
+ }
+
+ inline usertype_storage_base& get_usertype_storage_base(lua_State* L, const char* gcmetakey) {
+ stack::get_field<true>(L, gcmetakey);
+ stack::record tracking;
+ usertype_storage_base& target_umt = stack::stack_detail::unchecked_unqualified_get<user<usertype_storage_base>>(L, -1, tracking);
+ lua_pop(L, 1);
+ return target_umt;
+ }
+
+ template <typename T>
+ inline optional<usertype_storage<T>&> maybe_get_usertype_storage(lua_State* L) {
+ const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
+ stack::get_field<true>(L, gcmetakey);
+ int target = lua_gettop(L);
+ if (!stack::check<user<usertype_storage<T>>>(L, target)) {
+ return nullopt;
+ }
+ usertype_storage<T>& target_umt = stack::pop<user<usertype_storage<T>>>(L);
+ return target_umt;
+ }
+
+ template <typename T>
+ inline usertype_storage<T>& get_usertype_storage(lua_State* L) {
+ const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
+ stack::get_field<true>(L, gcmetakey);
+ usertype_storage<T>& target_umt = stack::pop<user<usertype_storage<T>>>(L);
+ return target_umt;
+ }
+
+ template <typename T>
+ inline void clear_usertype_storage(lua_State* L) {
+ using u_traits = usertype_traits<T>;
+
+ const char* gcmetakey = &u_traits::gc_table()[0];
+ stack::get_field<true>(L, gcmetakey);
+ if (!stack::check<user<usertype_storage<T>>>(L)) {
+ lua_pop(L, 1);
+ return;
+ }
+ usertype_storage<T>& target_umt = stack::pop<user<usertype_storage<T>>>(L);
+ target_umt.clear();
+
+ clear_usertype_registry_names<T>(L);
+
+ stack::set_field<true>(L, gcmetakey, lua_nil);
+ }
+
+ template <typename T, automagic_flags enrollment_flags>
+ inline int register_usertype(lua_State* L_, automagic_enrollments enrollments_ = {}) {
+ using u_traits = usertype_traits<T>;
+ using u_const_traits = usertype_traits<const T>;
+ using u_unique_traits = usertype_traits<d::u<T>>;
+ using u_ref_traits = usertype_traits<T*>;
+ using u_const_ref_traits = usertype_traits<T const*>;
+ using uts = usertype_storage<T>;
+
+ // always have __new_index point to usertype_storage method
+ // have __index always point to regular fast-lookup
+ // meta_method table
+ // if __new_index is invoked, runtime-swap
+ // to slow __index if necessary
+ // (no speed penalty because function calls
+ // are all read-only -- only depend on __index
+ // to retrieve function and then call happens VIA Lua)
+
+ // __type entry:
+ // table contains key -> value lookup,
+ // where key is entry in metatable
+ // and value is type information as a string as
+ // best as we can give it
+
+ // name entry:
+ // string that contains raw class name,
+ // as defined from C++
+
+ // is entry:
+ // checks if argument supplied is of type T
+
+ // __storage entry:
+ // a light userdata pointing to the storage
+ // mostly to enable this new abstraction
+ // to not require the type name `T`
+ // to get at the C++ usertype storage within
+
+ // we then let typical definitions potentially override these intrinsics
+ // it's the user's fault if they override things or screw them up:
+ // these names have been reserved and documented since sol2
+
+ // STEP 0: tell the old usertype (if it exists)
+ // to fuck off
+ clear_usertype_storage<T>(L_);
+
+ // STEP 1: Create backing store for usertype storage
+ // Pretty much the most important step.
+ // STEP 2: Create Lua tables used for fast method indexing.
+ // This is done inside of the storage table's constructor
+ usertype_storage<T>& storage = create_usertype_storage<T>(L_);
+ usertype_storage_base& base_storage = storage;
+ void* light_storage = static_cast<void*>(&storage);
+ void* light_base_storage = static_cast<void*>(&base_storage);
+
+ // STEP 3: set up GC escape hatch table entirely
+ storage.gc_names_table.push(L_);
+ stateless_stack_reference gnt(L_, -1);
+ stack::set_field(L_, submetatable_type::named, &u_traits::gc_table()[0], gnt.stack_index());
+ stack::set_field(L_, submetatable_type::const_value, &u_const_traits::metatable()[0], gnt.stack_index());
+ stack::set_field(L_, submetatable_type::const_reference, &u_const_ref_traits::metatable()[0], gnt.stack_index());
+ stack::set_field(L_, submetatable_type::reference, &u_ref_traits::metatable()[0], gnt.stack_index());
+ stack::set_field(L_, submetatable_type::unique, &u_unique_traits::metatable()[0], gnt.stack_index());
+ stack::set_field(L_, submetatable_type::value, &u_traits::metatable()[0], gnt.stack_index());
+ gnt.pop(L_);
+
+ // STEP 4: add some useful information to the type table
+ stateless_stack_reference stacked_type_table(L_, -storage.type_table.push(L_));
+ stack::set_field(L_, "name", detail::demangle<T>(), stacked_type_table.stack_index());
+ stack::set_field(L_, "is", &detail::is_check<T>, stacked_type_table.stack_index());
+ stacked_type_table.pop(L_);
+
+ // STEP 5: create and hook up metatable,
+ // add intrinsics
+ // this one is the actual meta-handling table,
+ // the next one will be the one for
+ int for_each_backing_metatable_calls = 0;
+ auto for_each_backing_metatable = [&](lua_State* L_, submetatable_type smt_, stateless_reference& fast_index_table_) {
+ // Pointer types, AKA "references" from C++
+ const char* metakey = nullptr;
+ switch (smt_) {
+ case submetatable_type::const_value:
+ metakey = &u_const_traits::metatable()[0];
+ break;
+ case submetatable_type::reference:
+ metakey = &u_ref_traits::metatable()[0];
+ break;
+ case submetatable_type::unique:
+ metakey = &u_unique_traits::metatable()[0];
+ break;
+ case submetatable_type::const_reference:
+ metakey = &u_const_ref_traits::metatable()[0];
+ break;
+ case submetatable_type::named:
+ metakey = &u_traits::user_metatable()[0];
+ break;
+ case submetatable_type::value:
+ default:
+ metakey = &u_traits::metatable()[0];
+ break;
+ }
+
+ luaL_newmetatable(L_, metakey);
+ if (smt_ == submetatable_type::named) {
+ // the named table itself
+ // gets the associated name value
+ storage.named_metatable.reset(L_, -1);
+ lua_pop(L_, 1);
+ // but the thing we perform the methods on
+ // is still the metatable of the named
+ // table
+ lua_createtable(L_, 0, 6);
+ }
+ stateless_stack_reference t(L_, -1);
+ fast_index_table_.reset(L_, t.stack_index());
+ stack::set_field<false, true>(L_, meta_function::type, storage.type_table, t.stack_index());
+ // destructible? serialize default destructor here
+ // otherwise, not destructible: serialize a "hey you messed up"
+ switch (smt_) {
+ case submetatable_type::const_reference:
+ case submetatable_type::reference:
+ case submetatable_type::named:
+ break;
+ case submetatable_type::unique:
+ if constexpr (std::is_destructible_v<T>) {
+ stack::set_field<false, true>(L_, meta_function::garbage_collect, &detail::unique_destroy<T>, t.stack_index());
+ }
+ else {
+ stack::set_field<false, true>(L_, meta_function::garbage_collect, &detail::cannot_destroy<T>, t.stack_index());
+ }
+ break;
+ case submetatable_type::value:
+ case submetatable_type::const_value:
+ default:
+ if constexpr (std::is_destructible_v<T>) {
+ stack::set_field<false, true>(L_, meta_function::garbage_collect, detail::make_destructor<T>(), t.stack_index());
+ }
+ else {
+ stack::set_field<false, true>(L_, meta_function::garbage_collect, &detail::cannot_destroy<T>, t.stack_index());
+ }
+ break;
+ }
+
+ static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function),
+ "The size of this data pointer is too small to fit the inheritance checking function: file a bug "
+ "report.");
+ static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function),
+ "The size of this data pointer is too small to fit the inheritance checking function: file a bug "
+ "report.");
+ stack::set_field<false, true>(L_, detail::base_class_check_key(), reinterpret_cast<void*>(&detail::inheritance<T>::type_check), t.stack_index());
+ stack::set_field<false, true>(L_, detail::base_class_cast_key(), reinterpret_cast<void*>(&detail::inheritance<T>::type_cast), t.stack_index());
+
+ auto prop_fx = detail::properties_enrollment_allowed(for_each_backing_metatable_calls, storage.properties, enrollments_);
+ auto insert_fx = [&L_, &t, &storage](meta_function mf, lua_CFunction reg) {
+ stack::set_field<false, true>(L_, mf, reg, t.stack_index());
+ storage.properties[static_cast<std::size_t>(mf)] = true;
+ };
+ detail::insert_default_registrations<T>(insert_fx, prop_fx);
+
+ // There are no variables, so serialize the fast function stuff
+ // be sure to reset the index stuff to the non-fast version
+ // if the user ever adds something later!
+ if (smt_ == submetatable_type::named) {
+ // add escape hatch storage pointer and gc names
+ stack::set_field<false, true>(L_, meta_function::storage, light_base_storage, t.stack_index());
+ stack::set_field<false, true>(L_, meta_function::gc_names, storage.gc_names_table, t.stack_index());
+
+ // fancy new_indexing when using the named table
+ {
+ absolute_index named_metatable_index(L_, -storage.named_metatable.push(L_));
+ stack::set_field<false, true>(L_, metatable_key, t, named_metatable_index);
+ storage.named_metatable.pop(L_);
+ }
+ stack_reference stack_metametatable(L_, -storage.named_index_table.push(L_));
+ stack::set_field<false, true>(L_,
+ meta_function::index,
+ make_closure(uts::template meta_index_call<false>, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic),
+ stack_metametatable.stack_index());
+ stack::set_field<false, true>(L_,
+ meta_function::new_index,
+ make_closure(uts::template meta_index_call<true>, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic),
+ stack_metametatable.stack_index());
+ stack_metametatable.pop();
+ }
+ else {
+ // otherwise just plain for index,
+ // and elaborated for new_index
+ stack::set_field<false, true>(L_, meta_function::index, t, t.stack_index());
+ stack::set_field<false, true>(L_,
+ meta_function::new_index,
+ make_closure(uts::template index_call<true>, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic),
+ t.stack_index());
+ storage.is_using_new_index = true;
+ }
+
+ ++for_each_backing_metatable_calls;
+ fast_index_table_.reset(L_, t.stack_index());
+ t.pop(L_);
+ };
+
+ storage.for_each_table(L_, for_each_backing_metatable);
+
+ // can only use set AFTER we initialize all the metatables
+ if constexpr (std::is_default_constructible_v<T> && has_flag(enrollment_flags, automagic_flags::default_constructor)) {
+ if (enrollments_.default_constructor) {
+ storage.set(L_, meta_function::construct, constructors<T()>());
+ }
+ }
+
+ // return the named metatable we want names linked into
+ storage.named_metatable.push(L_);
+ return 1;
+ }
+}} // namespace sol::u_detail
+
+// end of sol/usertype_storage.hpp
+
+// beginning of sol/usertype_proxy.hpp
+
+namespace sol {
+ template <typename Table, typename Key>
+ struct usertype_proxy : public proxy_base<usertype_proxy<Table, Key>> {
+ private:
+ using key_type = detail::proxy_key_t<Key>;
+
+ template <typename T, std::size_t... I>
+ decltype(auto) tuple_get(std::index_sequence<I...>) const& {
+ return tbl.template traverse_get<T>(std::get<I>(key)...);
+ }
+
+ template <typename T, std::size_t... I>
+ decltype(auto) tuple_get(std::index_sequence<I...>) && {
+ return tbl.template traverse_get<T>(std::get<I>(std::move(key))...);
+ }
+
+ template <std::size_t... I, typename T>
+ void tuple_set(std::index_sequence<I...>, T&& value) & {
+ if constexpr (sizeof...(I) > 1) {
+ tbl.traverse_set(std::get<I>(key)..., std::forward<T>(value));
+ }
+ else {
+ tbl.set(std::get<I>(key)..., std::forward<T>(value));
+ }
+ }
+
+ template <std::size_t... I, typename T>
+ void tuple_set(std::index_sequence<I...>, T&& value) && {
+ if constexpr (sizeof...(I) > 1) {
+ tbl.traverse_set(std::get<I>(std::move(key))..., std::forward<T>(value));
+ }
+ else {
+ tbl.set(std::get<I>(std::move(key))..., std::forward<T>(value));
+ }
+ }
+
+ public:
+ Table tbl;
+ key_type key;
+
+ template <typename T>
+ usertype_proxy(Table table, T&& k) : tbl(table), key(std::forward<T>(k)) {
+ }
+
+ template <typename T>
+ usertype_proxy& set(T&& item) & {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ tuple_set(idx_seq(), std::forward<T>(item));
+ return *this;
+ }
+
+ template <typename T>
+ usertype_proxy&& set(T&& item) && {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ std::move(*this).tuple_set(idx_seq(), std::forward<T>(item));
+ return std::move(*this);
+ }
+
+ template <typename T>
+ usertype_proxy& operator=(T&& other) & {
+ return set(std::forward<T>(other));
+ }
+
+ template <typename T>
+ usertype_proxy&& operator=(T&& other) && {
+ return std::move(*this).set(std::forward<T>(other));
+ }
+
+ template <typename T>
+ usertype_proxy& operator=(std::initializer_list<T> other) & {
+ return set(std::move(other));
+ }
+
+ template <typename T>
+ usertype_proxy&& operator=(std::initializer_list<T> other) && {
+ return std::move(*this).set(std::move(other));
+ }
+
+ template <typename T>
+ decltype(auto) get() const& {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ return tuple_get<T>(idx_seq());
+ }
+
+ template <typename T>
+ decltype(auto) get() && {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ return std::move(*this).template tuple_get<T>(idx_seq());
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) const& {
+ auto keys = meta::tuplefy(key, std::forward<K>(k));
+ return usertype_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) & {
+ auto keys = meta::tuplefy(key, std::forward<K>(k));
+ return usertype_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) && {
+ auto keys = meta::tuplefy(std::move(key), std::forward<K>(k));
+ return usertype_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) {
+#if !defined(__clang__) && defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 191200000
+ // MSVC is ass sometimes
+ return get<function>().call<Ret...>(std::forward<Args>(args)...);
+#else
+ return get<function>().template call<Ret...>(std::forward<Args>(args)...);
+#endif
+ }
+
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ bool valid() const {
+ auto pp = stack::push_pop(tbl);
+ auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
+ lua_pop(lua_state(), p.levels);
+ return p;
+ }
+
+ int push() const noexcept {
+ return push(this->lua_state());
+ }
+
+ int push(lua_State* L) const noexcept {
+ return get<reference>().push(L);
+ }
+
+ type get_type() const {
+ type t = type::none;
+ auto pp = stack::push_pop(tbl);
+ auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
+ if (p) {
+ t = type_of(lua_state(), -1);
+ }
+ lua_pop(lua_state(), p.levels);
+ return t;
+ }
+
+ lua_State* lua_state() const {
+ return tbl.lua_state();
+ }
+ };
+} // namespace sol
+
+// end of sol/usertype_proxy.hpp
+
+// beginning of sol/metatable.hpp
+
+// beginning of sol/table_core.hpp
+
+// beginning of sol/table_proxy.hpp
+
+namespace sol {
+
+ template <typename Table, typename Key>
+ struct table_proxy : public proxy_base<table_proxy<Table, Key>> {
+ private:
+ using key_type = detail::proxy_key_t<Key>;
+
+ template <typename T, std::size_t... I>
+ decltype(auto) tuple_get(std::index_sequence<I...>) const& {
+ return tbl.template traverse_get<T>(std::get<I>(key)...);
+ }
+
+ template <typename T, std::size_t... I>
+ decltype(auto) tuple_get(std::index_sequence<I...>) && {
+ return tbl.template traverse_get<T>(std::get<I>(std::move(key))...);
+ }
+
+ template <std::size_t... I, typename T>
+ void tuple_set(std::index_sequence<I...>, T&& value) & {
+ tbl.traverse_set(std::get<I>(key)..., std::forward<T>(value));
+ }
+
+ template <std::size_t... I, typename T>
+ void tuple_set(std::index_sequence<I...>, T&& value) && {
+ tbl.traverse_set(std::get<I>(std::move(key))..., std::forward<T>(value));
+ }
+
+ auto setup_table(std::true_type) {
+ auto p = stack::probe_get_field<std::is_same_v<meta::unqualified_t<Table>, global_table>>(lua_state(), key, tbl.stack_index());
+ lua_pop(lua_state(), p.levels);
+ return p;
+ }
+
+ bool is_valid(std::false_type) {
+ auto pp = stack::push_pop(tbl);
+ auto p = stack::probe_get_field<std::is_same_v<meta::unqualified_t<Table>, global_table>>(lua_state(), key, lua_gettop(lua_state()));
+ lua_pop(lua_state(), p.levels);
+ return p;
+ }
+
+ public:
+ Table tbl;
+ key_type key;
+
+ template <typename T>
+ table_proxy(Table table, T&& k) : tbl(table), key(std::forward<T>(k)) {
+ }
+
+ table_proxy(const table_proxy&) = default;
+ table_proxy(table_proxy&&) = default;
+ table_proxy& operator=(const table_proxy& right) {
+ return set(right);
+ }
+ table_proxy& operator=(table_proxy&& right) {
+ return set(std::move(right));
+ }
+
+ template <typename T>
+ table_proxy& set(T&& item) & {
+ tuple_set(std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>(), std::forward<T>(item));
+ return *this;
+ }
+
+ template <typename T>
+ table_proxy&& set(T&& item) && {
+ std::move(*this).tuple_set(std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>(), std::forward<T>(item));
+ return std::move(*this);
+ }
+
+ template <typename... Args>
+ table_proxy& set_function(Args&&... args) & {
+ tbl.set_function(key, std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename... Args>
+ table_proxy&& set_function(Args&&... args) && {
+ tbl.set_function(std::move(key), std::forward<Args>(args)...);
+ return std::move(*this);
+ }
+
+ template <typename T, std::enable_if_t<!std::is_same_v<meta::unqualified_t<T>, table_proxy>>* = nullptr>
+ table_proxy& operator=(T&& other) & {
+ using Tu = meta::unwrap_unqualified_t<T>;
+ if constexpr (!is_lua_reference_or_proxy_v<Tu> && meta::is_invocable_v<Tu>) {
+ return set_function(std::forward<T>(other));
+ }
+ else {
+ return set(std::forward<T>(other));
+ }
+ }
+
+ template <typename T, std::enable_if_t<!std::is_same_v<meta::unqualified_t<T>, table_proxy>>* = nullptr>
+ table_proxy&& operator=(T&& other) && {
+ using Tu = meta::unwrap_unqualified_t<T>;
+ if constexpr (!is_lua_reference_or_proxy_v<Tu> && meta::is_invocable_v<Tu> && !detail::is_msvc_callable_rigged_v<T>) {
+ return std::move(*this).set_function(std::forward<T>(other));
+ }
+ else {
+ return std::move(*this).set(std::forward<T>(other));
+ }
+ }
+
+ template <typename T>
+ table_proxy& operator=(std::initializer_list<T> other) & {
+ return set(std::move(other));
+ }
+
+ template <typename T>
+ table_proxy&& operator=(std::initializer_list<T> other) && {
+ return std::move(*this).set(std::move(other));
+ }
+
+ template <typename T>
+ bool is() const {
+ typedef decltype(get<T>()) U;
+ optional<U> option = this->get<optional<U>>();
+ return option.has_value();
+ }
+
+ template <typename T>
+ decltype(auto) get() const& {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ return tuple_get<T>(idx_seq());
+ }
+
+ template <typename T>
+ decltype(auto) get() && {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ return std::move(*this).template tuple_get<T>(idx_seq());
+ }
+
+ template <typename T>
+ decltype(auto) get_or(T&& otherwise) const {
+ typedef decltype(get<T>()) U;
+ optional<U> option = get<optional<U>>();
+ if (option) {
+ return static_cast<U>(option.value());
+ }
+ return static_cast<U>(std::forward<T>(otherwise));
+ }
+
+ template <typename T, typename D>
+ decltype(auto) get_or(D&& otherwise) const {
+ optional<T> option = get<optional<T>>();
+ if (option) {
+ return static_cast<T>(option.value());
+ }
+ return static_cast<T>(std::forward<D>(otherwise));
+ }
+
+ template <typename T>
+ decltype(auto) get_or_create() {
+ return get_or_create<T>(new_table());
+ }
+
+ template <typename T, typename Otherwise>
+ decltype(auto) get_or_create(Otherwise&& other) {
+ if (!this->valid()) {
+ this->set(std::forward<Otherwise>(other));
+ }
+ return get<T>();
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) const& {
+ auto keys = meta::tuplefy(key, std::forward<K>(k));
+ return table_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) & {
+ auto keys = meta::tuplefy(key, std::forward<K>(k));
+ return table_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) && {
+ auto keys = meta::tuplefy(std::move(key), std::forward<K>(k));
+ return table_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) {
+ lua_State* L = this->lua_state();
+ push(L);
+ int idx = lua_gettop(L);
+ stack_aligned_function func(L, idx);
+ return func.call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ bool valid() const {
+ auto pp = stack::push_pop(tbl);
+ auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
+ lua_pop(lua_state(), p.levels);
+ return p;
+ }
+
+ int push() const noexcept {
+ return push(this->lua_state());
+ }
+
+ int push(lua_State* L) const noexcept {
+ if constexpr (std::is_same_v<meta::unqualified_t<Table>, global_table> || is_stack_table_v<meta::unqualified_t<Table>>) {
+ auto pp = stack::push_pop<true>(tbl);
+ int tableindex = pp.index_of(tbl);
+ int top_index = lua_gettop(L);
+ stack::get_field<true>(lua_state(), key, tableindex);
+ lua_replace(L, top_index + 1);
+ lua_settop(L, top_index + 1);
+ }
+ else {
+ auto pp = stack::push_pop<false>(tbl);
+ int tableindex = pp.index_of(tbl);
+ int aftertableindex = lua_gettop(L);
+ stack::get_field<false>(lua_state(), key, tableindex);
+ lua_replace(L, tableindex);
+ lua_settop(L, aftertableindex + 1);
+ }
+ return 1;
+ }
+
+ type get_type() const {
+ type t = type::none;
+ auto pp = stack::push_pop(tbl);
+ auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
+ if (p) {
+ t = type_of(lua_state(), -1);
+ }
+ lua_pop(lua_state(), p.levels);
+ return t;
+ }
+
+ lua_State* lua_state() const {
+ return tbl.lua_state();
+ }
+
+ table_proxy& force() {
+ if (!this->valid()) {
+ this->set(new_table());
+ }
+ return *this;
+ }
+ };
+
+ template <typename Table, typename Key, typename T>
+ inline bool operator==(T&& left, const table_proxy<Table, Key>& right) {
+ using G = decltype(stack::get<T>(nullptr, 0));
+ return right.template get<optional<G>>() == left;
+ }
+
+ template <typename Table, typename Key, typename T>
+ inline bool operator==(const table_proxy<Table, Key>& right, T&& left) {
+ using G = decltype(stack::get<T>(nullptr, 0));
+ return right.template get<optional<G>>() == left;
+ }
+
+ template <typename Table, typename Key, typename T>
+ inline bool operator!=(T&& left, const table_proxy<Table, Key>& right) {
+ using G = decltype(stack::get<T>(nullptr, 0));
+ return right.template get<optional<G>>() != left;
+ }
+
+ template <typename Table, typename Key, typename T>
+ inline bool operator!=(const table_proxy<Table, Key>& right, T&& left) {
+ using G = decltype(stack::get<T>(nullptr, 0));
+ return right.template get<optional<G>>() != left;
+ }
+
+ template <typename Table, typename Key>
+ inline bool operator==(lua_nil_t, const table_proxy<Table, Key>& right) {
+ return !right.valid();
+ }
+
+ template <typename Table, typename Key>
+ inline bool operator==(const table_proxy<Table, Key>& right, lua_nil_t) {
+ return !right.valid();
+ }
+
+ template <typename Table, typename Key>
+ inline bool operator!=(lua_nil_t, const table_proxy<Table, Key>& right) {
+ return right.valid();
+ }
+
+ template <typename Table, typename Key>
+ inline bool operator!=(const table_proxy<Table, Key>& right, lua_nil_t) {
+ return right.valid();
+ }
+
+ template <bool b>
+ template <typename Super>
+ basic_reference<b>& basic_reference<b>::operator=(proxy_base<Super>&& r) {
+ basic_reference<b> v = r;
+ this->operator=(std::move(v));
+ return *this;
+ }
+
+ template <bool b>
+ template <typename Super>
+ basic_reference<b>& basic_reference<b>::operator=(const proxy_base<Super>& r) {
+ basic_reference<b> v = r;
+ this->operator=(std::move(v));
+ return *this;
+ }
+
+ namespace stack {
+ template <typename Table, typename Key>
+ struct unqualified_pusher<table_proxy<Table, Key>> {
+ static int push(lua_State* L, const table_proxy<Table, Key>& p) {
+ return p.push(L);
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/table_proxy.hpp
+
+// beginning of sol/table_iterator.hpp
+
+#include <iterator>
+
+namespace sol {
+
+ template <typename reference_type>
+ class basic_table_iterator {
+ public:
+ typedef object key_type;
+ typedef object mapped_type;
+ typedef std::pair<object, object> value_type;
+ typedef std::input_iterator_tag iterator_category;
+ typedef std::ptrdiff_t difference_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+
+ private:
+ std::pair<object, object> kvp;
+ reference_type ref;
+ int tableidx = 0;
+ int keyidx = 0;
+ std::ptrdiff_t idx = 0;
+
+ public:
+ basic_table_iterator() noexcept : keyidx(-1), idx(-1) {
+ }
+
+ basic_table_iterator(reference_type x) noexcept : ref(std::move(x)) {
+ ref.push();
+ tableidx = lua_gettop(ref.lua_state());
+ stack::push(ref.lua_state(), lua_nil);
+ this->operator++();
+ if (idx == -1) {
+ return;
+ }
+ --idx;
+ }
+
+ basic_table_iterator& operator++() noexcept {
+ if (idx == -1)
+ return *this;
+
+ if (lua_next(ref.lua_state(), tableidx) == 0) {
+ idx = -1;
+ keyidx = -1;
+ return *this;
+ }
+ ++idx;
+ kvp.first = object(ref.lua_state(), -2);
+ kvp.second = object(ref.lua_state(), -1);
+ lua_pop(ref.lua_state(), 1);
+ // leave key on the stack
+ keyidx = lua_gettop(ref.lua_state());
+ return *this;
+ }
+
+ basic_table_iterator operator++(int) noexcept {
+ auto saved = *this;
+ this->operator++();
+ return saved;
+ }
+
+ reference operator*() const noexcept {
+ return const_cast<reference>(kvp);
+ }
+
+ bool operator==(const basic_table_iterator& right) const noexcept {
+ return idx == right.idx;
+ }
+
+ bool operator!=(const basic_table_iterator& right) const noexcept {
+ return idx != right.idx;
+ }
+
+ ~basic_table_iterator() {
+ if (keyidx != -1) {
+ stack::remove(ref.lua_state(), keyidx, 1);
+ }
+ if (ref.lua_state() != nullptr && ref.valid()) {
+ stack::remove(ref.lua_state(), tableidx, 1);
+ }
+ }
+ };
+
+} // namespace sol
+
+// end of sol/table_iterator.hpp
+
+// beginning of sol/pairs_iterator.hpp
+
+// beginning of sol/stack/detail/pairs.hpp
+
+#include <optional>
+
+namespace sol { namespace stack { namespace stack_detail {
+
+ inline bool maybe_push_lua_next_function(lua_State* L_) {
+ stack::get_field<true, false>(L_, "next");
+ bool is_next = stack::check<protected_function>(L_);
+ if (is_next) {
+ return true;
+ }
+ stack::get_field<true, false>(L_, "table");
+ stack::record tracking{};
+ if (!stack::loose_table_check(L_, -1, &no_panic, tracking)) {
+ return false;
+ }
+ lua_getfield(L_, -1, "next");
+ bool is_table_next_func = stack::check<protected_function>(L_, -1);
+ if (is_table_next_func) {
+ return true;
+ }
+ lua_pop(L_, 1);
+ return false;
+ }
+
+ inline std::optional<protected_function> find_lua_next_function(lua_State* L_) {
+ if (maybe_push_lua_next_function(L_)) {
+ return stack::pop<protected_function>(L_);
+ }
+ return std::nullopt;
+ }
+
+ inline int c_lua_next(lua_State* L_) noexcept {
+ stack_reference table_stack_ref(L_, raw_index(1));
+ stateless_stack_reference key_stack_ref(L_, raw_index(2));
+ int result = lua_next(table_stack_ref.lua_state(), table_stack_ref.stack_index());
+ if (result == 0) {
+ stack::push(L_, lua_nil);
+ return 1;
+ }
+ return 2;
+ }
+
+ inline int readonly_pairs(lua_State* L_) noexcept {
+ int pushed = 0;
+ if (!maybe_push_lua_next_function(L_)) {
+ // we do not have the "next" function in the global namespace
+ // from the "table" global entiry, use our own
+ pushed += stack::push(L_, &c_lua_next);
+ }
+ else {
+ pushed += 1;
+ }
+ int metatable_exists = lua_getmetatable(L_, 1);
+ SOL_ASSERT(metatable_exists == 1);
+ const auto& index_key = to_string(sol::meta_function::index);
+ lua_getfield(L_, lua_gettop(L_), index_key.c_str());
+ lua_remove(L_, -2);
+ pushed += 1;
+ pushed += stack::push(L_, lua_nil);
+ return pushed;
+ }
+
+}}} // sol::stack::stack_detail
+
+// end of sol/stack/detail/pairs.hpp
+
+namespace sol {
+
+ struct pairs_sentinel { };
+
+ class pairs_iterator {
+ private:
+ inline static constexpr int empty_key_index = -1;
+
+ public:
+ using key_type = object;
+ using mapped_type = object;
+ using value_type = std::pair<object, object>;
+ using iterator_category = std::input_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using const_pointer = value_type const*;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+
+ pairs_iterator() noexcept
+ : m_L(nullptr)
+ , m_next_function_ref(lua_nil)
+ , m_table_ref(lua_nil)
+ , m_cached_key_value_pair({ lua_nil, lua_nil })
+ , m_key_index(empty_key_index)
+ , m_iteration_index(0) {
+ }
+
+ pairs_iterator(const pairs_iterator&) = delete;
+ pairs_iterator& operator=(const pairs_iterator&) = delete;
+
+ pairs_iterator(pairs_iterator&& right) noexcept
+ : m_L(right.m_L)
+ , m_next_function_ref(std::move(right.m_next_function_ref))
+ , m_table_ref(std::move(right.m_table_ref))
+ , m_cached_key_value_pair(std::move(right.m_cached_key_value_pair))
+ , m_key_index(right.m_key_index)
+ , m_iteration_index(right.m_iteration_index) {
+ right.m_key_index = empty_key_index;
+ }
+
+ pairs_iterator& operator=(pairs_iterator&& right) noexcept {
+ m_L = right.m_L;
+ m_next_function_ref = std::move(right.m_next_function_ref);
+ m_table_ref = std::move(right.m_table_ref);
+ m_cached_key_value_pair = std::move(right.m_cached_key_value_pair);
+ m_key_index = right.m_key_index;
+ m_iteration_index = right.m_iteration_index;
+ right.m_key_index = empty_key_index;
+ return *this;
+ }
+
+ template <typename Source>
+ pairs_iterator(const Source& source_) noexcept : m_L(source_.lua_state()), m_key_index(empty_key_index), m_iteration_index(0) {
+ if (m_L == nullptr || !source_.valid()) {
+ m_key_index = empty_key_index;
+ return;
+ }
+ int source_index = -source_.push(m_L);
+ int abs_source_index = lua_absindex(m_L, source_index);
+ int metatable_exists = lua_getmetatable(m_L, abs_source_index);
+ lua_remove(m_L, abs_source_index);
+ if (metatable_exists == 1) {
+ // just has a metatable, but does it have __pairs ?
+ stack_reference metatable(m_L, raw_index(abs_source_index));
+ stack::get_field<is_global_table_v<Source>, true>(m_L, meta_function::pairs, metatable.stack_index());
+ optional<protected_function> maybe_pairs_function = stack::pop<optional<protected_function>>(m_L);
+ if (maybe_pairs_function.has_value()) {
+ protected_function& pairs_function = *maybe_pairs_function;
+ protected_function_result next_fn_and_table_and_first_key = pairs_function(source_);
+ if (next_fn_and_table_and_first_key.valid()) {
+ m_next_function_ref = next_fn_and_table_and_first_key.get<protected_function>(0);
+ m_table_ref = next_fn_and_table_and_first_key.get<sol::reference>(1);
+ m_key_index = next_fn_and_table_and_first_key.stack_index() - 1;
+ // remove next function and table
+ lua_remove(m_L, m_key_index);
+ lua_remove(m_L, m_key_index);
+ next_fn_and_table_and_first_key.abandon();
+ lua_remove(m_L, abs_source_index);
+ this->operator++();
+ m_iteration_index = 0;
+ return;
+ }
+ }
+ }
+
+ {
+ auto maybe_next = stack::stack_detail::find_lua_next_function(m_L);
+ if (maybe_next.has_value()) {
+ m_next_function_ref = std::move(*maybe_next);
+ m_table_ref = source_;
+
+ stack::push(m_L, lua_nil);
+ m_key_index = lua_gettop(m_L);
+ this->operator++();
+ m_iteration_index = 0;
+ return;
+ }
+ }
+
+ // okay, so none of the above worked and now we need to create
+ // a shim / polyfill instead
+ stack::push(m_L, &stack::stack_detail::c_lua_next);
+ m_next_function_ref = stack::pop<protected_function>(m_L);
+ m_table_ref = source_;
+ stack::push(m_L, lua_nil);
+ m_key_index = lua_gettop(m_L);
+ this->operator++();
+ m_iteration_index = 0;
+ }
+
+ pairs_iterator& operator++() {
+ if (m_key_index == empty_key_index) {
+ return *this;
+ }
+ {
+ sol::protected_function_result next_results = m_next_function_ref(m_table_ref, stack_reference(m_L, m_key_index));
+ if (!next_results.valid()) {
+ // TODO: abort, or throw an error?
+ m_clear();
+ m_key_index = empty_key_index;
+ return *this;
+ }
+ int next_results_count = next_results.return_count();
+ if (next_results_count < 2) {
+ // iteration is over!
+ next_results.abandon();
+ lua_settop(m_L, m_key_index - 1);
+ m_key_index = empty_key_index;
+ ++m_iteration_index;
+ return *this;
+ }
+ else {
+ lua_remove(m_L, m_key_index);
+ m_key_index = next_results.stack_index() - 1;
+ m_cached_key_value_pair.first = stack::get<object>(m_L, m_key_index);
+ m_cached_key_value_pair.second = stack::get<object>(m_L, m_key_index + 1);
+ lua_settop(m_L, m_key_index);
+ next_results.abandon();
+ }
+ }
+ ++m_iteration_index;
+ return *this;
+ }
+
+ std::ptrdiff_t index() const {
+ return static_cast<std::ptrdiff_t>(m_iteration_index);
+ }
+
+ const_reference operator*() const noexcept {
+ return m_cached_key_value_pair;
+ }
+
+ reference operator*() noexcept {
+ return m_cached_key_value_pair;
+ }
+
+ friend bool operator==(const pairs_iterator& left, const pairs_iterator& right) noexcept {
+ return left.m_table_ref == right.m_table_ref && left.m_iteration_index == right.m_iteration_index;
+ }
+
+ friend bool operator!=(const pairs_iterator& left, const pairs_iterator& right) noexcept {
+ return left.m_table_ref != right.m_table_ref || left.m_iteration_index != right.m_iteration_index;
+ }
+
+ friend bool operator==(const pairs_iterator& left, const pairs_sentinel&) noexcept {
+ return left.m_key_index == empty_key_index;
+ }
+
+ friend bool operator!=(const pairs_iterator& left, const pairs_sentinel&) noexcept {
+ return left.m_key_index != empty_key_index;
+ }
+
+ friend bool operator==(const pairs_sentinel&, const pairs_iterator& left) noexcept {
+ return left.m_key_index == empty_key_index;
+ }
+
+ friend bool operator!=(const pairs_sentinel&, const pairs_iterator& left) noexcept {
+ return left.m_key_index != empty_key_index;
+ }
+
+ ~pairs_iterator() {
+ if (m_key_index != empty_key_index) {
+ m_clear();
+ }
+ }
+
+ private:
+ void m_clear() noexcept {
+ lua_remove(m_L, m_key_index);
+ }
+
+ lua_State* m_L;
+ protected_function m_next_function_ref;
+ sol::reference m_table_ref;
+ std::pair<object, object> m_cached_key_value_pair;
+ int m_key_index;
+ int m_iteration_index;
+ };
+
+ template <typename Source>
+ class basic_pairs_range {
+ private:
+ using source_t = std::add_lvalue_reference_t<Source>;
+ source_t m_source;
+
+ public:
+ using iterator = pairs_iterator;
+ using const_iterator = pairs_iterator;
+
+ basic_pairs_range(source_t source_) noexcept : m_source(source_) {
+ }
+
+ iterator begin() noexcept {
+ return iterator(m_source);
+ }
+
+ iterator begin() const noexcept {
+ return iterator(m_source);
+ }
+
+ const_iterator cbegin() const noexcept {
+ return const_iterator(m_source);
+ }
+
+ pairs_sentinel end() noexcept {
+ return {};
+ }
+
+ pairs_sentinel end() const noexcept {
+ return {};
+ }
+
+ pairs_sentinel cend() const noexcept {
+ return {};
+ }
+ };
+} // namespace sol
+
+// end of sol/pairs_iterator.hpp
+
+namespace sol {
+ namespace detail {
+ template <std::size_t n>
+ struct clean {
+ lua_State* L;
+ clean(lua_State* luastate) : L(luastate) {
+ }
+ ~clean() {
+ lua_pop(L, static_cast<int>(n));
+ }
+ };
+
+ struct ref_clean {
+ lua_State* L;
+ int& pop_count;
+
+ ref_clean(lua_State* L_, int& pop_count_) noexcept : L(L_), pop_count(pop_count_) {
+ }
+ ~ref_clean() {
+ lua_pop(L, static_cast<int>(pop_count));
+ }
+ };
+
+ inline int fail_on_newindex(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot modify the elements of an enumeration table");
+ }
+
+ } // namespace detail
+
+ template <bool top_level, typename ref_t>
+ class basic_table_core : public basic_object<ref_t> {
+ private:
+ using base_t = basic_object<ref_t>;
+
+ friend class state;
+ friend class state_view;
+ template <typename, typename>
+ friend class basic_usertype;
+ template <typename>
+ friend class basic_metatable;
+
+ template <typename T>
+ using is_get_direct_tableless = meta::boolean<stack::stack_detail::is_get_direct_tableless_v<T, top_level, false>>;
+
+ template <typename T>
+ using is_raw_get_direct_tableless = std::false_type;
+
+ template <typename T>
+ using is_set_direct_tableless = meta::boolean<stack::stack_detail::is_set_direct_tableless_v<T, top_level, false>>;
+
+ template <typename T>
+ using is_raw_set_direct_tableless = std::false_type;
+
+ template <bool raw, typename... Ret, typename... Keys>
+ decltype(auto) tuple_get(int table_index, Keys&&... keys) const {
+ if constexpr (sizeof...(Ret) < 2) {
+ return traverse_get_single_maybe_tuple<raw, Ret...>(table_index, std::forward<Keys>(keys)...);
+ }
+ else {
+ using multi_ret = decltype(stack::pop<std::tuple<Ret...>>(nullptr));
+ return multi_ret(traverse_get_single_maybe_tuple<raw, Ret>(table_index, std::forward<Keys>(keys))...);
+ }
+ }
+
+ template <bool raw, typename Ret, size_t... I, typename Key>
+ decltype(auto) traverse_get_single_tuple(int table_index, std::index_sequence<I...>, Key&& key) const {
+ return traverse_get_single<raw, Ret>(table_index, std::get<I>(std::forward<Key>(key))...);
+ }
+
+ template <bool raw, typename Ret, typename Key>
+ decltype(auto) traverse_get_single_maybe_tuple(int table_index, Key&& key) const {
+ if constexpr (meta::is_tuple_v<meta::unqualified_t<Key>>) {
+ return traverse_get_single_tuple<raw, Ret>(
+ table_index, std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<Key>>>(), std::forward<Key>(key));
+ }
+ else {
+ return traverse_get_single<raw, Ret>(table_index, std::forward<Key>(key));
+ }
+ }
+
+ template <bool raw, typename Ret, typename... Keys>
+ decltype(auto) traverse_get_single(int table_index, Keys&&... keys) const {
+ constexpr static bool global = (meta::count_for_to_pack_v < 1, is_get_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ if constexpr (meta::is_optional_v<meta::unqualified_t<Ret>>) {
+ int popcount = 0;
+ detail::ref_clean c(base_t::lua_state(), popcount);
+ return traverse_get_deep_optional<global, raw, detail::insert_mode::none, Ret>(popcount, table_index, std::forward<Keys>(keys)...);
+ }
+ else {
+ detail::clean<sizeof...(Keys) - meta::count_for_pack_v<detail::is_insert_mode, meta::unqualified_t<Keys>...>> c(base_t::lua_state());
+ return traverse_get_deep<global, raw, detail::insert_mode::none, Ret>(table_index, std::forward<Keys>(keys)...);
+ }
+ }
+
+ template <bool raw, typename Pairs, std::size_t... I>
+ void tuple_set(std::index_sequence<I...>, Pairs&& pairs) {
+ constexpr static bool global = (meta::count_even_for_pack_v < is_set_direct_tableless,
+ meta::unqualified_t<decltype(std::get<I * 2>(std::forward<Pairs>(pairs)))>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ lua_State* L = base_t::lua_state();
+ (void)table_index;
+ (void)L;
+ void(detail::swallow { (stack::set_field<(top_level), raw>(
+ L, std::get<I * 2>(std::forward<Pairs>(pairs)), std::get<I * 2 + 1>(std::forward<Pairs>(pairs)), table_index),
+ 0)... });
+ }
+
+ template <bool global, bool raw, detail::insert_mode mode, typename T, typename Key, typename... Keys>
+ decltype(auto) traverse_get_deep(int table_index, Key&& key, Keys&&... keys) const {
+ if constexpr (std::is_same_v<meta::unqualified_t<Key>, create_if_nil_t>) {
+ (void)key;
+ return traverse_get_deep<false, raw, static_cast<detail::insert_mode>(mode | detail::insert_mode::create_if_nil), T>(
+ table_index, std::forward<Keys>(keys)...);
+ }
+ else {
+ lua_State* L = base_t::lua_state();
+ stack::get_field<global, raw>(L, std::forward<Key>(key), table_index);
+ if constexpr (sizeof...(Keys) > 0) {
+ if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) {
+ type t = type_of(L, -1);
+ if (t == type::lua_nil || t == type::none) {
+ lua_pop(L, 1);
+ stack::push(L, new_table(0, 0));
+ }
+ }
+ return traverse_get_deep<false, raw, mode, T>(lua_gettop(L), std::forward<Keys>(keys)...);
+ }
+ else {
+ if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) {
+ type t = type_of(L, -1);
+ if ((t == type::lua_nil || t == type::none) && (is_table_like_v<T>)) {
+ lua_pop(L, 1);
+ stack::push(L, new_table(0, 0));
+ }
+ }
+ return stack::get<T>(L);
+ }
+ }
+ }
+
+ template <bool global, bool raw, detail::insert_mode mode, typename T, typename Key, typename... Keys>
+ decltype(auto) traverse_get_deep_optional(int& popcount, int table_index, Key&& key, Keys&&... keys) const {
+ if constexpr (std::is_same_v<meta::unqualified_t<Key>, create_if_nil_t>) {
+ constexpr detail::insert_mode new_mode = static_cast<detail::insert_mode>(mode | detail::insert_mode::create_if_nil);
+ (void)key;
+ return traverse_get_deep_optional<global, raw, new_mode, T>(popcount, table_index, std::forward<Keys>(keys)...);
+ }
+ else if constexpr (std::is_same_v<meta::unqualified_t<Key>, update_if_empty_t>) {
+ constexpr detail::insert_mode new_mode = static_cast<detail::insert_mode>(mode | detail::insert_mode::update_if_empty);
+ (void)key;
+ return traverse_get_deep_optional<global, raw, new_mode, T>(popcount, table_index, std::forward<Keys>(keys)...);
+ }
+ else if constexpr (std::is_same_v<meta::unqualified_t<Key>, override_value_t>) {
+ constexpr detail::insert_mode new_mode = static_cast<detail::insert_mode>(mode | detail::insert_mode::override_value);
+ (void)key;
+ return traverse_get_deep_optional<global, raw, new_mode, T>(popcount, table_index, std::forward<Keys>(keys)...);
+ }
+ else {
+ if constexpr (sizeof...(Keys) > 0) {
+ lua_State* L = base_t::lua_state();
+ auto p = stack::probe_get_field<global, raw>(L, std::forward<Key>(key), table_index);
+ popcount += p.levels;
+ if (!p.success) {
+ if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) {
+ lua_pop(L, 1);
+ constexpr bool is_seq = meta::count_for_to_pack_v < 1, std::is_integral, Keys... >> 0;
+ stack::push(L, new_table(static_cast<int>(is_seq), static_cast<int>(!is_seq)));
+ stack::set_field<global, raw>(L, std::forward<Key>(key), stack_reference(L, -1), table_index);
+ }
+ else {
+ return T(nullopt);
+ }
+ }
+ return traverse_get_deep_optional<false, raw, mode, T>(popcount, lua_gettop(L), std::forward<Keys>(keys)...);
+ }
+ else {
+ using R = decltype(stack::get<T>(nullptr));
+ using value_type = typename meta::unqualified_t<R>::value_type;
+ lua_State* L = base_t::lua_state();
+ auto p = stack::probe_get_field<global, raw, value_type>(L, key, table_index);
+ popcount += p.levels;
+ if (!p.success) {
+ if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) {
+ lua_pop(L, 1);
+ stack::push(L, new_table(0, 0));
+ stack::set_field<global, raw>(L, std::forward<Key>(key), stack_reference(L, -1), table_index);
+ if (stack::check<value_type>(L, lua_gettop(L), &no_panic)) {
+ return stack::get<T>(L);
+ }
+ }
+ return R(nullopt);
+ }
+ return stack::get<T>(L);
+ }
+ }
+ }
+
+ template <bool global, bool raw, detail::insert_mode mode, typename Key, typename... Keys>
+ void traverse_set_deep(int table_index, Key&& key, Keys&&... keys) const {
+ using KeyU = meta::unqualified_t<Key>;
+ if constexpr (std::is_same_v<KeyU, update_if_empty_t>) {
+ (void)key;
+ traverse_set_deep<global, raw, static_cast<detail::insert_mode>(mode | detail::insert_mode::update_if_empty)>(
+ table_index, std::forward<Keys>(keys)...);
+ }
+ else if constexpr (std::is_same_v<KeyU, create_if_nil_t>) {
+ (void)key;
+ traverse_set_deep<global, raw, static_cast<detail::insert_mode>(mode | detail::insert_mode::create_if_nil)>(
+ table_index, std::forward<Keys>(keys)...);
+ }
+ else if constexpr (std::is_same_v<KeyU, override_value_t>) {
+ (void)key;
+ traverse_set_deep<global, raw, static_cast<detail::insert_mode>(mode | detail::insert_mode::override_value)>(
+ table_index, std::forward<Keys>(keys)...);
+ }
+ else {
+ lua_State* L = base_t::lua_state();
+ if constexpr (sizeof...(Keys) == 1) {
+ if constexpr ((mode & detail::insert_mode::update_if_empty) == detail::insert_mode::update_if_empty) {
+ auto p = stack::probe_get_field<global, raw>(L, key, table_index);
+ lua_pop(L, p.levels);
+ if (!p.success) {
+ stack::set_field<global, raw>(L, std::forward<Key>(key), std::forward<Keys>(keys)..., table_index);
+ }
+ }
+ else {
+ stack::set_field<global, raw>(L, std::forward<Key>(key), std::forward<Keys>(keys)..., table_index);
+ }
+ }
+ else {
+ if constexpr (mode != detail::insert_mode::none) {
+ stack::get_field<global, raw>(L, key, table_index);
+ type vt = type_of(L, -1);
+ if constexpr ((mode & detail::insert_mode::update_if_empty) == detail::insert_mode::update_if_empty
+ || (mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) {
+ if (vt == type::lua_nil || vt == type::none) {
+ constexpr bool is_seq = meta::count_for_to_pack_v < 1, std::is_integral, Keys... >> 0;
+ lua_pop(L, 1);
+ stack::push(L, new_table(static_cast<int>(is_seq), static_cast<int>(!is_seq)));
+ stack::set_field<global, raw>(L, std::forward<Key>(key), stack_reference(L, -1), table_index);
+ }
+ }
+ else {
+ if (vt != type::table) {
+ constexpr bool is_seq = meta::count_for_to_pack_v < 1, std::is_integral, Keys... >> 0;
+ lua_pop(L, 1);
+ stack::push(L, new_table(static_cast<int>(is_seq), static_cast<int>(!is_seq)));
+ stack::set_field<global, raw>(L, std::forward<Key>(key), stack_reference(L, -1), table_index);
+ }
+ }
+ }
+ else {
+ stack::get_field<global, raw>(L, std::forward<Key>(key), table_index);
+ }
+ traverse_set_deep<false, raw, mode>(lua_gettop(L), std::forward<Keys>(keys)...);
+ }
+ }
+ }
+
+ protected:
+ basic_table_core(detail::no_safety_tag, lua_nil_t n) : base_t(n) {
+ }
+ basic_table_core(detail::no_safety_tag, lua_State* L, int index) : base_t(L, index) {
+ }
+ basic_table_core(detail::no_safety_tag, lua_State* L, ref_index index) : base_t(L, index) {
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_table_core>>, meta::neg<std::is_same<ref_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_table_core(detail::no_safety_tag, T&& r) noexcept : base_t(std::forward<T>(r)) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_table_core(detail::no_safety_tag, lua_State* L, T&& r) noexcept : base_t(L, std::forward<T>(r)) {
+ }
+
+ public:
+ using iterator = basic_table_iterator<ref_t>;
+ using const_iterator = iterator;
+
+ using base_t::lua_state;
+
+ basic_table_core() noexcept = default;
+ basic_table_core(const basic_table_core&) = default;
+ basic_table_core(basic_table_core&&) = default;
+ basic_table_core& operator=(const basic_table_core&) = default;
+ basic_table_core& operator=(basic_table_core&&) = default;
+
+ basic_table_core(const stack_reference& r) : basic_table_core(r.lua_state(), r.stack_index()) {
+ }
+
+ basic_table_core(stack_reference&& r) : basic_table_core(r.lua_state(), r.stack_index()) {
+ }
+
+ template <typename T, meta::enable_any<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_table_core(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ constructor_handler handler {};
+ stack::check<basic_table_core>(lua_state(), table_index, handler);
+#endif // Safety
+ }
+
+ basic_table_core(lua_State* L, const new_table& nt) : base_t(L, -stack::push(L, nt)) {
+ if (!is_stack_based<meta::unqualified_t<ref_t>>::value) {
+ lua_pop(L, 1);
+ }
+ }
+
+ basic_table_core(lua_State* L, int index = -1) : basic_table_core(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_table_core>(L, index, handler);
+#endif // Safety
+ }
+
+ basic_table_core(lua_State* L, ref_index index) : basic_table_core(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ constructor_handler handler {};
+ stack::check<basic_table_core>(lua_state(), table_index, handler);
+#endif // Safety
+ }
+
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_table_core>>, meta::neg<std::is_same<ref_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_table_core(T&& r) noexcept : basic_table_core(detail::no_safety, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_table<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ constructor_handler handler {};
+ stack::check<basic_table_core>(lua_state(), table_index, handler);
+ }
+#endif // Safety
+ }
+
+ basic_table_core(lua_nil_t r) noexcept : basic_table_core(detail::no_safety, r) {
+ }
+
+ basic_table_core(lua_State* L, global_tag_t t) noexcept : base_t(L, t) {
+ }
+
+ iterator begin() const {
+ if (this->get_type() == type::table) {
+ return iterator(*this);
+ }
+ return iterator();
+ }
+
+ iterator end() const {
+ return iterator();
+ }
+
+ const_iterator cbegin() const {
+ return begin();
+ }
+
+ const_iterator cend() const {
+ return end();
+ }
+
+ basic_pairs_range<basic_table_core> pairs() noexcept {
+ return basic_pairs_range<basic_table_core>(*this);
+ }
+
+ basic_pairs_range<const basic_table_core> pairs() const noexcept {
+ return basic_pairs_range<const basic_table_core>(*this);
+ }
+
+ void clear() {
+ auto pp = stack::push_pop<false>(*this);
+ int table_index = pp.index_of(*this);
+ stack::clear(lua_state(), table_index);
+ }
+
+ template <typename... Ret, typename... Keys>
+ decltype(auto) get(Keys&&... keys) const {
+ static_assert(sizeof...(Keys) == sizeof...(Ret), "number of keys and number of return types do not match");
+ constexpr static bool global = meta::all<meta::boolean<top_level>, is_get_direct_tableless<meta::unqualified_t<Keys>>...>::value;
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ return tuple_get<false, Ret...>(table_index, std::forward<Keys>(keys)...);
+ }
+
+ template <typename T, typename Key>
+ decltype(auto) get_or(Key&& key, T&& otherwise) const {
+ typedef decltype(get<T>("")) U;
+ optional<U> option = get<optional<U>>(std::forward<Key>(key));
+ if (option) {
+ return static_cast<U>(option.value());
+ }
+ return static_cast<U>(std::forward<T>(otherwise));
+ }
+
+ template <typename T, typename Key, typename D>
+ decltype(auto) get_or(Key&& key, D&& otherwise) const {
+ optional<T> option = get<optional<T>>(std::forward<Key>(key));
+ if (option) {
+ return static_cast<T>(option.value());
+ }
+ return static_cast<T>(std::forward<D>(otherwise));
+ }
+
+ template <typename T, typename... Keys>
+ decltype(auto) traverse_get(Keys&&... keys) const {
+ static_assert(sizeof...(Keys) > 0, "must pass at least 1 key to get");
+ constexpr static bool global = (meta::count_for_to_pack_v < 1, is_get_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ return traverse_get_single<false, T>(table_index, std::forward<Keys>(keys)...);
+ }
+
+ template <typename... Keys>
+ basic_table_core& traverse_set(Keys&&... keys) {
+ static_assert(sizeof...(Keys) > 1, "must pass at least 1 key and 1 value to set");
+ constexpr static bool global
+ = (meta::count_when_for_to_pack_v < detail::is_not_insert_mode, 1, is_set_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ lua_State* L = base_t::lua_state();
+ auto pn = stack::pop_n(L, static_cast<int>(sizeof...(Keys) - 2 - meta::count_for_pack_v<detail::is_insert_mode, meta::unqualified_t<Keys>...>));
+ traverse_set_deep<top_level, false, detail::insert_mode::none>(table_index, std::forward<Keys>(keys)...);
+ return *this;
+ }
+
+ template <typename... Args>
+ basic_table_core& set(Args&&... args) {
+ if constexpr (sizeof...(Args) == 2) {
+ traverse_set(std::forward<Args>(args)...);
+ }
+ else {
+ tuple_set<false>(std::make_index_sequence<sizeof...(Args) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...));
+ }
+ return *this;
+ }
+
+ template <typename... Ret, typename... Keys>
+ decltype(auto) raw_get(Keys&&... keys) const {
+ static_assert(sizeof...(Keys) == sizeof...(Ret), "number of keys and number of return types do not match");
+ constexpr static bool global = (meta::count_for_to_pack_v < 1, is_raw_get_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ return tuple_get<true, Ret...>(table_index, std::forward<Keys>(keys)...);
+ }
+
+ template <typename T, typename Key>
+ decltype(auto) raw_get_or(Key&& key, T&& otherwise) const {
+ typedef decltype(raw_get<T>("")) U;
+ optional<U> option = raw_get<optional<U>>(std::forward<Key>(key));
+ if (option) {
+ return static_cast<U>(option.value());
+ }
+ return static_cast<U>(std::forward<T>(otherwise));
+ }
+
+ template <typename T, typename Key, typename D>
+ decltype(auto) raw_get_or(Key&& key, D&& otherwise) const {
+ optional<T> option = raw_get<optional<T>>(std::forward<Key>(key));
+ if (option) {
+ return static_cast<T>(option.value());
+ }
+ return static_cast<T>(std::forward<D>(otherwise));
+ }
+
+ template <typename T, typename... Keys>
+ decltype(auto) traverse_raw_get(Keys&&... keys) const {
+ constexpr static bool global = (meta::count_for_to_pack_v < 1, is_raw_get_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ return traverse_get_single<true, T>(table_index, std::forward<Keys>(keys)...);
+ }
+
+ template <typename... Keys>
+ basic_table_core& traverse_raw_set(Keys&&... keys) {
+ constexpr static bool global = (meta::count_for_to_pack_v < 1, is_raw_set_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ lua_State* L = base_t::lua_state();
+ auto pn = stack::pop_n(L, static_cast<int>(sizeof...(Keys) - 2 - meta::count_for_pack_v<detail::is_insert_mode, meta::unqualified_t<Keys>...>));
+ traverse_set_deep<top_level, true, false>(std::forward<Keys>(keys)...);
+ return *this;
+ }
+
+ template <typename... Args>
+ basic_table_core& raw_set(Args&&... args) {
+ tuple_set<true>(std::make_index_sequence<sizeof...(Args) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...));
+ return *this;
+ }
+
+ template <typename Class, typename Key>
+ usertype<Class> new_usertype(Key&& key);
+
+ template <typename Class, typename Key, automagic_flags enrollment_flags>
+ usertype<Class> new_usertype(Key&& key, constant_automagic_enrollments<enrollment_flags> enrollment);
+
+ template <typename Class, typename Key>
+ usertype<Class> new_usertype(Key&& key, automagic_enrollments enrollment);
+
+ template <typename Class, typename Key, typename Arg, typename... Args,
+ typename = std::enable_if_t<!std::is_base_of_v<automagic_enrollments, meta::unqualified_t<Arg>>>>
+ usertype<Class> new_usertype(Key&& key, Arg&& arg, Args&&... args);
+
+ template <bool read_only = true, typename... Args>
+ table new_enum(const string_view& name, Args&&... args) {
+ table target = create_with(std::forward<Args>(args)...);
+ if constexpr (read_only) {
+ // Need to create a special iterator to handle this
+ table x
+ = create_with(meta_function::new_index, detail::fail_on_newindex, meta_function::index, target, meta_function::pairs, stack::stack_detail::readonly_pairs);
+ table shim = create_named(name, metatable_key, x);
+ return shim;
+ }
+ else {
+ set(name, target);
+ return target;
+ }
+ }
+
+ template <typename T, bool read_only = true>
+ table new_enum(const string_view& name, std::initializer_list<std::pair<string_view, T>> items) {
+ table target = create(static_cast<int>(items.size()), static_cast<int>(0));
+ for (const auto& kvp : items) {
+ target.set(kvp.first, kvp.second);
+ }
+ if constexpr (read_only) {
+ table x = create_with(meta_function::new_index, detail::fail_on_newindex, meta_function::index, target);
+ table shim = create_named(name, metatable_key, x);
+ return shim;
+ }
+ else {
+ set(name, target);
+ return target;
+ }
+ }
+
+ template <typename Key = object, typename Value = object, typename Fx>
+ void for_each(Fx&& fx) const {
+ lua_State* L = base_t::lua_state();
+ if constexpr (std::is_invocable_v<Fx, Key, Value>) {
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ stack::push(L, lua_nil);
+ while (lua_next(L, table_index)) {
+ Key key(L, -2);
+ Value value(L, -1);
+ auto pn = stack::pop_n(L, 1);
+ fx(key, value);
+ }
+ }
+ else {
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ stack::push(L, lua_nil);
+ while (lua_next(L, table_index)) {
+ Key key(L, -2);
+ Value value(L, -1);
+ auto pn = stack::pop_n(L, 1);
+ std::pair<Key&, Value&> keyvalue(key, value);
+ fx(keyvalue);
+ }
+ }
+ }
+
+ size_t size() const {
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ lua_State* L = base_t::lua_state();
+ lua_len(L, table_index);
+ return stack::pop<size_t>(L);
+ }
+
+ bool empty() const {
+ return cbegin() == cend();
+ }
+
+ template <typename T>
+ auto operator[](T&& key) & {
+ return table_proxy<basic_table_core&, detail::proxy_key_t<T>>(*this, std::forward<T>(key));
+ }
+
+ template <typename T>
+ auto operator[](T&& key) const& {
+ return table_proxy<const basic_table_core&, detail::proxy_key_t<T>>(*this, std::forward<T>(key));
+ }
+
+ template <typename T>
+ auto operator[](T&& key) && {
+ return table_proxy<basic_table_core, detail::proxy_key_t<T>>(std::move(*this), std::forward<T>(key));
+ }
+
+ template <typename Sig, typename Key, typename... Args>
+ basic_table_core& set_function(Key&& key, Args&&... args) {
+ set_fx(types<Sig>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Key, typename... Args>
+ basic_table_core& set_function(Key&& key, Args&&... args) {
+ set_fx(types<>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename... Args>
+ basic_table_core& add(Args&&... args) {
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ lua_State* L = base_t::lua_state();
+ (void)detail::swallow { 0, (stack::stack_detail::raw_table_set(L, std::forward<Args>(args), table_index), 0)... };
+ return *this;
+ }
+
+ private:
+ template <typename R, typename... Args, typename Fx, typename Key, typename = std::invoke_result_t<Fx, Args...>>
+ void set_fx(types<R(Args...)>, Key&& key, Fx&& fx) {
+ set_resolved_function<R(Args...)>(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, meta::enable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx) {
+ set(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, typename... Args,
+ meta::disable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference(std::forward<Fx>(fx), std::forward<Args>(args)...));
+ }
+
+ template <typename... Sig, typename... Args, typename Key>
+ void set_resolved_function(Key&& key, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference<function_sig<Sig...>>(std::forward<Args>(args)...));
+ }
+
+ public:
+ static inline table create(lua_State* L, int narr = 0, int nrec = 0) {
+ lua_createtable(L, narr, nrec);
+ table result(L);
+ lua_pop(L, 1);
+ return result;
+ }
+
+ template <typename Key, typename Value, typename... Args>
+ static inline table create(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ lua_createtable(L, narr, nrec);
+ table result(L);
+ result.set(std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ lua_pop(L, 1);
+ return result;
+ }
+
+ template <typename... Args>
+ static inline table create_with(lua_State* L, Args&&... args) {
+ static_assert(sizeof...(Args) % 2 == 0, "You must have an even number of arguments for a key, value ... list.");
+ constexpr int narr = static_cast<int>(meta::count_odd_for_pack_v<std::is_integral, Args...>);
+ return create(L, narr, static_cast<int>((sizeof...(Args) / 2) - narr), std::forward<Args>(args)...);
+ }
+
+ table create(int narr = 0, int nrec = 0) {
+ return create(base_t::lua_state(), narr, nrec);
+ }
+
+ template <typename Key, typename Value, typename... Args>
+ table create(int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ return create(base_t::lua_state(), narr, nrec, std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ }
+
+ template <typename Name>
+ table create(Name&& name, int narr = 0, int nrec = 0) {
+ table x = create(base_t::lua_state(), narr, nrec);
+ this->set(std::forward<Name>(name), x);
+ return x;
+ }
+
+ template <typename Name, typename Key, typename Value, typename... Args>
+ table create(Name&& name, int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ table x = create(base_t::lua_state(), narr, nrec, std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ this->set(std::forward<Name>(name), x);
+ return x;
+ }
+
+ template <typename... Args>
+ table create_with(Args&&... args) {
+ return create_with(base_t::lua_state(), std::forward<Args>(args)...);
+ }
+
+ template <typename Name, typename... Args>
+ table create_named(Name&& name, Args&&... args) {
+ static const int narr = static_cast<int>(meta::count_even_for_pack_v<std::is_integral, Args...>);
+ return create(std::forward<Name>(name), narr, (sizeof...(Args) / 2) - narr, std::forward<Args>(args)...);
+ }
+ };
+} // namespace sol
+
+// end of sol/table_core.hpp
+
+namespace sol {
+
+ template <typename base_type>
+ class basic_metatable : public basic_table<base_type> {
+ typedef basic_table<base_type> base_t;
+ friend class state;
+ friend class state_view;
+
+ protected:
+ basic_metatable(detail::no_safety_tag, lua_nil_t n) : base_t(n) {
+ }
+ basic_metatable(detail::no_safety_tag, lua_State* L, int index) : base_t(L, index) {
+ }
+ basic_metatable(detail::no_safety_tag, lua_State* L, ref_index index) : base_t(L, index) {
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_metatable>>, meta::neg<std::is_same<base_type, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_metatable(detail::no_safety_tag, T&& r) noexcept : base_t(std::forward<T>(r)) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_metatable(detail::no_safety_tag, lua_State* L, T&& r) noexcept : base_t(L, std::forward<T>(r)) {
+ }
+
+ template <typename R, typename... Args, typename Fx, typename Key, typename = std::invoke_result_t<Fx, Args...>>
+ void set_fx(types<R(Args...)>, Key&& key, Fx&& fx) {
+ set_resolved_function<R(Args...)>(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, meta::enable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx) {
+ set(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, typename... Args,
+ meta::disable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference(std::forward<Fx>(fx), std::forward<Args>(args)...));
+ }
+
+ template <typename... Sig, typename... Args, typename Key>
+ void set_resolved_function(Key&& key, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference<function_sig<Sig...>>(std::forward<Args>(args)...));
+ }
+
+ public:
+ using base_t::lua_state;
+
+ basic_metatable() noexcept = default;
+ basic_metatable(const basic_metatable&) = default;
+ basic_metatable(basic_metatable&&) = default;
+ basic_metatable& operator=(const basic_metatable&) = default;
+ basic_metatable& operator=(basic_metatable&&) = default;
+ basic_metatable(const stack_reference& r) : basic_metatable(r.lua_state(), r.stack_index()) {
+ }
+ basic_metatable(stack_reference&& r) : basic_metatable(r.lua_state(), r.stack_index()) {
+ }
+ template <typename T, meta::enable_any<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_metatable(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_metatable>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_metatable(lua_State* L, int index = -1) : basic_metatable(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_metatable>(L, index, handler);
+#endif // Safety
+ }
+ basic_metatable(lua_State* L, ref_index index) : basic_metatable(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_metatable>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_metatable>>, meta::neg<std::is_same<base_type, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_metatable(T&& r) noexcept : basic_metatable(detail::no_safety, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_table<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_metatable>(base_t::lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+ basic_metatable(lua_nil_t r) noexcept : basic_metatable(detail::no_safety, r) {
+ }
+
+ template <typename Key, typename Value>
+ basic_metatable<base_type>& set(Key&& key, Value&& value);
+
+ template <typename Sig, typename Key, typename... Args>
+ basic_metatable& set_function(Key&& key, Args&&... args) {
+ set_fx(types<Sig>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Key, typename... Args>
+ basic_metatable& set_function(Key&& key, Args&&... args) {
+ set_fx(types<>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ void unregister() {
+ using ustorage_base = u_detail::usertype_storage_base;
+
+ lua_State* L = this->lua_state();
+
+ auto pp = stack::push_pop(*this);
+ int top = lua_gettop(L);
+
+ stack_reference mt(L, -1);
+ stack::get_field(L, meta_function::gc_names, mt.stack_index());
+ if (type_of(L, -1) != type::table) {
+ lua_settop(L, top);
+ return;
+ }
+ stack_reference gc_names_table(L, -1);
+ stack::get_field(L, meta_function::storage, mt.stack_index());
+ if (type_of(L, -1) != type::lightuserdata) {
+ lua_settop(L, top);
+ return;
+ }
+ ustorage_base& base_storage = *static_cast<ustorage_base*>(stack::get<void*>(L, -1));
+ std::array<string_view, 6> registry_traits;
+ for (std::size_t i = 0; i < registry_traits.size(); ++i) {
+ u_detail::submetatable_type smt = static_cast<u_detail::submetatable_type>(i);
+ stack::get_field<false, true>(L, smt, gc_names_table.stack_index());
+ registry_traits[i] = stack::get<string_view>(L, -1);
+ }
+
+ // get the registry
+ stack_reference registry(L, raw_index(LUA_REGISTRYINDEX));
+ registry.push();
+ // eliminate all named entries for this usertype
+ // in the registry (luaL_newmetatable does
+ // [name] = new table
+ // in registry upon creation)
+ for (std::size_t i = 0; i < registry_traits.size(); ++i) {
+ u_detail::submetatable_type smt = static_cast<u_detail::submetatable_type>(i);
+ const string_view& gcmetakey = registry_traits[i];
+ if (smt == u_detail::submetatable_type::named) {
+ // use .data() to make it treat it like a c string,
+ // which it is...
+ stack::set_field<true>(L, gcmetakey.data(), lua_nil);
+ }
+ else {
+ // do not change the values in the registry: they need to be present
+ // no matter what, for safety's sake
+ // stack::set_field(L, gcmetakey, lua_nil, registry.stack_index());
+ }
+ }
+
+ // destroy all storage and tables
+ base_storage.clear();
+
+ // 6 strings from gc_names table,
+ // + 1 registry,
+ // + 1 gc_names table
+ // + 1 light userdata of storage
+ // + 1 registry
+ // 10 total, 4 left since popping off 6 gc_names tables
+ lua_settop(L, top);
+ }
+ };
+
+} // namespace sol
+
+// end of sol/metatable.hpp
+
+namespace sol {
+
+ template <typename T, typename base_type>
+ class basic_usertype : private basic_metatable<base_type> {
+ private:
+ using base_t = basic_metatable<base_type>;
+ using table_base_t = basic_table<base_type>;
+
+ template <typename>
+ friend class basic_metatable;
+
+ template <bool, typename>
+ friend class basic_table_core;
+
+ template <std::size_t... I, typename... Args>
+ void tuple_set(std::index_sequence<I...>, std::tuple<Args...>&& args) {
+ (void)args;
+ (void)detail::swallow { 0, (this->set(std::get<I * 2>(std::move(args)), std::get<I * 2 + 1>(std::move(args))), 0)... };
+ }
+
+ template <typename R, typename... Args, typename Fx, typename Key, typename = std::invoke_result_t<Fx, Args...>>
+ void set_fx(types<R(Args...)>, Key&& key, Fx&& fx) {
+ set_resolved_function<R(Args...)>(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, meta::enable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx) {
+ set(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, typename... Args,
+ meta::disable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference(std::forward<Fx>(fx), std::forward<Args>(args)...));
+ }
+
+ template <typename... Sig, typename... Args, typename Key>
+ void set_resolved_function(Key&& key, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference<function_sig<Sig...>>(std::forward<Args>(args)...));
+ }
+
+ public:
+ using base_t::base_t;
+
+ using base_t::get;
+ using base_t::lua_state;
+ using base_t::pop;
+ using base_t::push;
+ using base_t::traverse_get;
+ using base_t::traverse_set;
+ using base_t::unregister;
+
+ template <typename Key, typename Value>
+ basic_usertype& set(Key&& key, Value&& value) {
+ optional<u_detail::usertype_storage<T>&> maybe_uts = u_detail::maybe_get_usertype_storage<T>(this->lua_state());
+ if (maybe_uts) {
+ u_detail::usertype_storage<T>& uts = *maybe_uts;
+ uts.set(this->lua_state(), std::forward<Key>(key), std::forward<Value>(value));
+ }
+ else {
+ using ValueU = meta::unqualified_t<Value>;
+ // cannot get metatable: try regular table set?
+ if constexpr (detail::is_non_factory_constructor_v<ValueU> || detail::is_policy_v<ValueU>) {
+ // tag constructors so we don't get destroyed by lack of info
+ table_base_t::set(std::forward<Key>(key), detail::tagged<T, Value>(std::forward<Value>(value)));
+ }
+ else {
+ table_base_t::set(std::forward<Key>(key), std::forward<Value>(value));
+ }
+ }
+ return *this;
+ }
+
+ template <typename Sig, typename Key, typename... Args>
+ basic_usertype& set_function(Key&& key, Args&&... args) {
+ set_fx(types<Sig>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Key, typename... Args>
+ basic_usertype& set_function(Key&& key, Args&&... args) {
+ set_fx(types<>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Key>
+ usertype_proxy<basic_usertype&, std::decay_t<Key>> operator[](Key&& key) {
+ return usertype_proxy<basic_usertype&, std::decay_t<Key>>(*this, std::forward<Key>(key));
+ }
+
+ template <typename Key>
+ usertype_proxy<const basic_usertype&, std::decay_t<Key>> operator[](Key&& key) const {
+ return usertype_proxy<const basic_usertype&, std::decay_t<Key>>(*this, std::forward<Key>(key));
+ }
+ };
+
+} // namespace sol
+
+// end of sol/usertype.hpp
+
+// beginning of sol/table.hpp
+
+// beginning of sol/lua_table.hpp
+
+namespace sol {
+
+ template <typename ref_t>
+ struct basic_lua_table : basic_table_core<false, ref_t> {
+ private:
+ using base_t = basic_table_core<false, ref_t>;
+
+ friend class state;
+ friend class state_view;
+
+ public:
+ using base_t::lua_state;
+
+ basic_lua_table() noexcept = default;
+ basic_lua_table(const basic_lua_table&) = default;
+ basic_lua_table(basic_lua_table&&) = default;
+ basic_lua_table& operator=(const basic_lua_table&) = default;
+ basic_lua_table& operator=(basic_lua_table&&) = default;
+ basic_lua_table(const stack_reference& r) : basic_lua_table(r.lua_state(), r.stack_index()) {
+ }
+ basic_lua_table(stack_reference&& r) : basic_lua_table(r.lua_state(), r.stack_index()) {
+ }
+ template <typename T, meta::enable_any<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_lua_table(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_lua_table>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_lua_table(lua_State* L, const new_table& nt) : base_t(L, nt) {
+ if (!is_stack_based<meta::unqualified_t<ref_t>>::value) {
+ lua_pop(L, 1);
+ }
+ }
+ basic_lua_table(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_lua_table>(L, index, handler);
+#endif // Safety
+ }
+ basic_lua_table(lua_State* L, ref_index index) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_lua_table>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_lua_table>>, meta::neg<std::is_same<ref_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_lua_table(T&& r) noexcept : basic_lua_table(detail::no_safety, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_table<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_lua_table>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+ basic_lua_table(lua_nil_t r) noexcept : basic_lua_table(detail::no_safety, r) {
+ }
+ };
+
+} // namespace sol
+
+// end of sol/lua_table.hpp
+
+namespace sol {
+ typedef table_core<false> table;
+
+ template <bool is_global, typename base_type>
+ template <typename Class, typename Key>
+ usertype<Class> basic_table_core<is_global, base_type>::new_usertype(Key&& key) {
+ constant_automagic_enrollments<> enrollments {};
+ return this->new_usertype<Class>(std::forward<Key>(key), std::move(enrollments));
+ }
+
+ template <bool is_global, typename base_type>
+ template <typename Class, typename Key, automagic_flags enrollment_flags>
+ usertype<Class> basic_table_core<is_global, base_type>::new_usertype(Key&& key, constant_automagic_enrollments<enrollment_flags> enrollments) {
+ int mt_index = u_detail::register_usertype<Class, enrollment_flags>(this->lua_state(), std::move(enrollments));
+ usertype<Class> mt(this->lua_state(), -mt_index);
+ lua_pop(this->lua_state(), 1);
+ set(std::forward<Key>(key), mt);
+ return mt;
+ }
+
+ template <bool is_global, typename base_type>
+ template <typename Class, typename Key>
+ usertype<Class> basic_table_core<is_global, base_type>::new_usertype(Key&& key, automagic_enrollments enrollments) {
+ int mt_index = u_detail::register_usertype<Class, automagic_flags::all>(this->lua_state(), std::move(enrollments));
+ usertype<Class> mt(this->lua_state(), -mt_index);
+ lua_pop(this->lua_state(), 1);
+ set(std::forward<Key>(key), mt);
+ return mt;
+ }
+
+ template <bool is_global, typename base_type>
+ template <typename Class, typename Key, typename Arg, typename... Args, typename>
+ usertype<Class> basic_table_core<is_global, base_type>::new_usertype(Key&& key, Arg&& arg, Args&&... args) {
+ constexpr automagic_flags enrollment_flags = meta::any_same_v<no_construction, meta::unqualified_t<Arg>, meta::unqualified_t<Args>...>
+ ? clear_flags(automagic_flags::all, automagic_flags::default_constructor)
+ : automagic_flags::all;
+ constant_automagic_enrollments<enrollment_flags> enrollments;
+ enrollments.default_constructor = !detail::any_is_constructor_v<Arg, Args...>;
+ enrollments.destructor = !detail::any_is_destructor_v<Arg, Args...>;
+ usertype<Class> ut = this->new_usertype<Class>(std::forward<Key>(key), std::move(enrollments));
+ static_assert(sizeof...(Args) % 2 == static_cast<std::size_t>(!detail::any_is_constructor_v<Arg>),
+ "you must pass an even number of arguments to new_usertype after first passing a constructor");
+ if constexpr (detail::any_is_constructor_v<Arg>) {
+ ut.set(meta_function::construct, std::forward<Arg>(arg));
+ ut.tuple_set(std::make_index_sequence<(sizeof...(Args)) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...));
+ }
+ else {
+ ut.tuple_set(std::make_index_sequence<(sizeof...(Args) + 1) / 2>(), std::forward_as_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...));
+ }
+ return ut;
+ }
+
+ template <typename base_type>
+ template <typename Key, typename Value>
+ basic_metatable<base_type>& basic_metatable<base_type>::set(Key&& key, Value&& value) {
+ this->push();
+ lua_State* L = this->lua_state();
+ int target = lua_gettop(L);
+ optional<u_detail::usertype_storage_base&> maybe_uts = nullopt;
+ maybe_uts = u_detail::maybe_get_usertype_storage_base(L, target);
+ if (maybe_uts) {
+ u_detail::usertype_storage_base& uts = *maybe_uts;
+ uts.set(L, std::forward<Key>(key), std::forward<Value>(value));
+ return *this;
+ }
+ else {
+ base_t::set(std::forward<Key>(key), std::forward<Value>(value));
+ }
+ this->pop();
+ return *this;
+ }
+
+ namespace stack {
+ template <>
+ struct unqualified_getter<metatable_key_t> {
+ static metatable get(lua_State* L, int index = -1) {
+ if (lua_getmetatable(L, index) == 0) {
+ return metatable(L, ref_index(LUA_REFNIL));
+ }
+ return metatable(L, -1);
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/table.hpp
+
+// beginning of sol/state.hpp
+
+// beginning of sol/state_view.hpp
+
+// beginning of sol/environment.hpp
+
+namespace sol {
+
+ template <typename base_type>
+ struct basic_environment : basic_table<base_type> {
+ private:
+ typedef basic_table<base_type> base_t;
+
+ public:
+ using base_t::lua_state;
+
+ basic_environment() noexcept = default;
+ basic_environment(const basic_environment&) = default;
+ basic_environment(basic_environment&&) = default;
+ basic_environment& operator=(const basic_environment&) = default;
+ basic_environment& operator=(basic_environment&&) = default;
+ basic_environment(const stack_reference& r) : basic_environment(r.lua_state(), r.stack_index()) {
+ }
+ basic_environment(stack_reference&& r) : basic_environment(r.lua_state(), r.stack_index()) {
+ }
+
+ basic_environment(lua_State* L, new_table nt) : base_t(L, std::move(nt)) {
+ }
+ template <bool b>
+ basic_environment(lua_State* L, new_table t, const basic_reference<b>& fallback) : basic_environment(L, std::move(t)) {
+ stack_table mt(L, new_table(0, 1));
+ mt.set(meta_function::index, fallback);
+ this->set(metatable_key, mt);
+ mt.pop();
+ }
+
+ basic_environment(env_key_t, const stack_reference& extraction_target)
+ : base_t(detail::no_safety, extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<env_key_t>(this->lua_state(), -1, handler);
+#endif // Safety
+ lua_pop(this->lua_state(), 1);
+ }
+ template <bool b>
+ basic_environment(env_key_t, const basic_reference<b>& extraction_target)
+ : base_t(detail::no_safety, extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<env_key_t>(this->lua_state(), -1, handler);
+#endif // Safety
+ lua_pop(this->lua_state(), 1);
+ }
+ basic_environment(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_environment>(L, index, handler);
+#endif // Safety
+ }
+ basic_environment(lua_State* L, ref_index index) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_environment>(L, -1, handler);
+#endif // Safety
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_environment>>, meta::neg<std::is_same<base_type, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_environment(T&& r) noexcept : base_t(detail::no_safety, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_environment<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_environment>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+ basic_environment(lua_nil_t r) noexcept : base_t(detail::no_safety, r) {
+ }
+
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_environment(lua_State* L, T&& r) noexcept : base_t(detail::no_safety, L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_environment<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_environment>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+
+ template <typename T>
+ bool set_on(const T& target) const {
+ lua_State* L = target.lua_state();
+ auto pp = stack::push_pop(target);
+ int target_index = pp.index_of(target);
+#if SOL_LUA_VERSION_I_ < 502
+ // Use lua_setfenv
+ this->push();
+ int success_result = lua_setfenv(L, target_index);
+ return success_result != 0;
+#else
+ // If this is a C function, the environment is always placed in
+ // the first value, as is expected of sol2 (all upvalues have an empty name, "")
+ if (lua_iscfunction(L, target_index) != 0) {
+ const char* maybe_upvalue_name = lua_getupvalue(L, target_index, 1);
+ if (maybe_upvalue_name == nullptr) {
+ return false;
+ }
+ string_view upvalue_name(maybe_upvalue_name);
+ if (upvalue_name == "") {
+ this->push();
+ const char* success = lua_setupvalue(L, target_index, 1);
+ if (success == nullptr) {
+ // left things alone on the stack, pop them off
+ lua_pop(L, 2);
+ return false;
+ }
+ lua_pop(L, 1);
+ return true;
+ }
+ lua_pop(L, 1);
+ return false;
+ }
+ else {
+ // Must search for the right upvalue target on index
+ for (int upvalue_index = 1;; ++upvalue_index) {
+ const char* maybe_upvalue_name = lua_getupvalue(L, target_index, upvalue_index);
+ if (maybe_upvalue_name == nullptr) {
+ break;
+ }
+ string_view upvalue_name(maybe_upvalue_name);
+ if (upvalue_name == "_ENV") {
+ lua_pop(L, 1);
+ this->push();
+ const char* success = lua_setupvalue(L, target_index, upvalue_index);
+ if (success == nullptr) {
+ // left things alone on the stack, pop them off
+ lua_pop(L, 1);
+ break;
+ }
+ // whether or not we succeeded, we found _ENV
+ // so we need to break
+ return true;
+ }
+ lua_pop(L, 1);
+ }
+ // if we get here,
+ // we did not find an _ENV here...
+ return false;
+ }
+#endif
+ }
+ };
+
+ template <typename T, typename E>
+ bool set_environment(const basic_environment<E>& env, const T& target) {
+ return env.set_on(target);
+ }
+
+ template <typename E = reference, typename T>
+ basic_environment<E> get_environment(const T& target) {
+ lua_State* L = target.lua_state();
+ auto pp = stack::pop_n(L, stack::push_environment_of(target));
+ return basic_environment<E>(L, -1);
+ }
+
+ struct this_environment {
+ optional<environment> env;
+
+ this_environment() : env(nullopt) {
+ }
+ this_environment(environment e) : env(std::move(e)) {
+ }
+ this_environment(const this_environment&) = default;
+ this_environment(this_environment&&) = default;
+ this_environment& operator=(const this_environment&) = default;
+ this_environment& operator=(this_environment&&) = default;
+
+ explicit operator bool() const {
+ return static_cast<bool>(env);
+ }
+
+ operator optional<environment>&() {
+ return env;
+ }
+
+ operator const optional<environment>&() const {
+ return env;
+ }
+
+ operator environment&() {
+ return env.value();
+ }
+
+ operator const environment&() const {
+ return env.value();
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_getter<env_key_t> {
+ static environment get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return get_environment(stack_reference(L, raw_index(index)));
+ }
+ };
+
+ template <>
+ struct unqualified_getter<this_environment> {
+ static this_environment get(lua_State* L, int, record& tracking) {
+ tracking.use(0);
+ lua_Debug info;
+ // Level 0 means current function (this C function, which may or may not be useful for us?)
+ // Level 1 means next call frame up the stack. (Can be nothing if function called directly from C++ with lua_p/call)
+ int pre_stack_size = lua_gettop(L);
+ if (lua_getstack(L, 1, &info) != 1) {
+ if (lua_getstack(L, 0, &info) != 1) {
+ lua_settop(L, pre_stack_size);
+ return this_environment();
+ }
+ }
+ if (lua_getinfo(L, "f", &info) == 0) {
+ lua_settop(L, pre_stack_size);
+ return this_environment();
+ }
+
+ stack_reference f(L, -1);
+ environment env(env_key, f);
+ if (!env.valid()) {
+ lua_settop(L, pre_stack_size);
+ return this_environment();
+ }
+ return this_environment(std::move(env));
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/environment.hpp
+
+// beginning of sol/load_result.hpp
+
+#include <cstdint>
+
+namespace sol {
+ struct load_result : public proxy_base<load_result> {
+ private:
+ lua_State* L;
+ int index;
+ int returncount;
+ int popcount;
+ load_status err;
+
+ public:
+ load_result() noexcept : load_result(nullptr) {}
+ load_result(lua_State* Ls, int stackindex = -1, int retnum = 0, int popnum = 0, load_status lerr = load_status::ok) noexcept
+ : L(Ls), index(stackindex), returncount(retnum), popcount(popnum), err(lerr) {
+ }
+
+ // We do not want anyone to copy these around willy-nilly
+ // Will likely break people, but also will probably get rid of quiet bugs that have
+ // been lurking. (E.g., Vanilla Lua will just quietly discard over-pops and under-pops:
+ // LuaJIT and other Lua engines will implode and segfault at random later times.)
+ load_result(const load_result&) = delete;
+ load_result& operator=(const load_result&) = delete;
+
+ load_result(load_result&& o) noexcept : L(o.L), index(o.index), returncount(o.returncount), popcount(o.popcount), err(o.err) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.L = nullptr;
+ o.index = 0;
+ o.returncount = 0;
+ o.popcount = 0;
+ o.err = load_status::syntax;
+ }
+ load_result& operator=(load_result&& o) noexcept {
+ L = o.L;
+ index = o.index;
+ returncount = o.returncount;
+ popcount = o.popcount;
+ err = o.err;
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.L = nullptr;
+ o.index = 0;
+ o.returncount = 0;
+ o.popcount = 0;
+ o.err = load_status::syntax;
+ return *this;
+ }
+
+ load_status status() const noexcept {
+ return err;
+ }
+
+ bool valid() const noexcept {
+ return status() == load_status::ok;
+ }
+
+ template <typename T>
+ T get() const {
+ using UT = meta::unqualified_t<T>;
+ if constexpr (meta::is_optional_v<UT>) {
+ using ValueType = typename UT::value_type;
+ if constexpr (std::is_same_v<ValueType, error>) {
+ if (valid()) {
+ return UT(nullopt);
+ }
+ return error(detail::direct_error, stack::get<std::string>(L, index));
+ }
+ else {
+ if (!valid()) {
+ return UT(nullopt);
+ }
+ return stack::get<UT>(L, index);
+ }
+ }
+ else {
+ if constexpr (std::is_same_v<T, error>) {
+#if SOL_IS_ON(SOL_SAFE_PROXIES)
+ if (valid()) {
+ type_panic_c_str(L, index, type_of(L, index), type::none, "expecting an error type (a string, from Lua)");
+ }
+#endif // Check proxy type's safety
+ return error(detail::direct_error, stack::get<std::string>(L, index));
+ }
+ else {
+#if SOL_IS_ON(SOL_SAFE_PROXIES)
+ if (!valid()) {
+ type_panic_c_str(L, index, type_of(L, index), type::none);
+ }
+#endif // Check proxy type's safety
+ return stack::get<T>(L, index);
+ }
+ }
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) {
+ return get<protected_function>().template call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ lua_State* lua_state() const noexcept {
+ return L;
+ };
+ int stack_index() const noexcept {
+ return index;
+ };
+
+ ~load_result() {
+ if (L != nullptr) {
+ stack::remove(L, index, popcount);
+ }
+ }
+ };
+} // namespace sol
+
+// end of sol/load_result.hpp
+
+// beginning of sol/state_handling.hpp
+
+// beginning of sol/lua_value.hpp
+
+namespace sol {
+ struct lua_value {
+ public:
+ struct arr : detail::ebco<std::initializer_list<lua_value>> {
+ private:
+ using base_t = detail::ebco<std::initializer_list<lua_value>>;
+
+ public:
+ using base_t::base_t;
+ };
+
+ private:
+ template <typename T>
+ using is_reference_or_lua_value_init_list
+ = meta::any<meta::is_specialization_of<T, std::initializer_list>, std::is_same<T, reference>, std::is_same<T, arr>>;
+
+ template <typename T>
+ using is_lua_value_single_constructible = meta::any<std::is_same<T, lua_value>, is_reference_or_lua_value_init_list<T>>;
+
+ static lua_State*& thread_local_lua_state() {
+#if SOL_IS_ON(SOL_USE_THREAD_LOCAL)
+ static thread_local lua_State* L = nullptr;
+#else
+ static lua_State* L = nullptr;
+#endif
+ return L;
+ }
+
+ reference ref_value;
+
+ public:
+ static void set_lua_state(lua_State* L) {
+ thread_local_lua_state() = L;
+ }
+
+ template <typename T, meta::disable<is_reference_or_lua_value_init_list<meta::unqualified_t<T>>> = meta::enabler>
+ lua_value(lua_State* L_, T&& value) : lua_value(((set_lua_state(L_)), std::forward<T>(value))) {
+ }
+
+ template <typename T, meta::disable<is_lua_value_single_constructible<meta::unqualified_t<T>>> = meta::enabler>
+ lua_value(T&& value) : ref_value(make_reference(thread_local_lua_state(), std::forward<T>(value))) {
+ }
+
+ lua_value(lua_State* L_, std::initializer_list<std::pair<lua_value, lua_value>> il)
+ : lua_value([&L_, &il]() {
+ set_lua_state(L_);
+ return std::move(il);
+ }()) {
+ }
+
+ lua_value(std::initializer_list<std::pair<lua_value, lua_value>> il) : ref_value(make_reference(thread_local_lua_state(), std::move(il))) {
+ }
+
+ lua_value(lua_State* L_, arr il)
+ : lua_value([&L_, &il]() {
+ set_lua_state(L_);
+ return std::move(il);
+ }()) {
+ }
+
+ lua_value(arr il) : ref_value(make_reference(thread_local_lua_state(), std::move(il.value()))) {
+ }
+
+ lua_value(lua_State* L_, reference r)
+ : lua_value([&L_, &r]() {
+ set_lua_state(L_);
+ return std::move(r);
+ }()) {
+ }
+
+ lua_value(reference r) : ref_value(std::move(r)) {
+ }
+
+ lua_value(const lua_value&) noexcept = default;
+ lua_value(lua_value&&) = default;
+ lua_value& operator=(const lua_value&) = default;
+ lua_value& operator=(lua_value&&) = default;
+
+ const reference& value() const& {
+ return ref_value;
+ }
+
+ reference& value() & {
+ return ref_value;
+ }
+
+ reference&& value() && {
+ return std::move(ref_value);
+ }
+
+ template <typename T>
+ decltype(auto) as() const {
+ ref_value.push();
+ return stack::pop<T>(ref_value.lua_state());
+ }
+
+ template <typename T>
+ bool is() const {
+ int r = ref_value.registry_index();
+ if (r == LUA_REFNIL)
+ return meta::any_same<meta::unqualified_t<T>, lua_nil_t, nullopt_t, std::nullptr_t>::value ? true : false;
+ if (r == LUA_NOREF)
+ return false;
+ auto pp = stack::push_pop(ref_value);
+ return stack::check<T>(ref_value.lua_state(), -1, &no_panic);
+ }
+ };
+
+ using array_value = typename lua_value::arr;
+
+ namespace stack {
+ template <>
+ struct unqualified_pusher<lua_value> {
+ static int push(lua_State* L, const lua_value& lv) {
+ return stack::push(L, lv.value());
+ }
+
+ static int push(lua_State* L, lua_value&& lv) {
+ return stack::push(L, std::move(lv).value());
+ }
+ };
+
+ template <>
+ struct unqualified_getter<lua_value> {
+ static lua_value get(lua_State* L, int index, record& tracking) {
+ return lua_value(L, stack::get<reference>(L, index, tracking));
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/lua_value.hpp
+
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+#include <iostream>
+#endif
+
+namespace sol {
+ inline void register_main_thread(lua_State* L) {
+#if SOL_LUA_VERSION_I_ < 502
+ if (L == nullptr) {
+ lua_pushnil(L);
+ lua_setglobal(L, detail::default_main_thread_name());
+ return;
+ }
+ lua_pushthread(L);
+ lua_setglobal(L, detail::default_main_thread_name());
+#else
+ (void)L;
+#endif
+ }
+
+ inline int default_at_panic(lua_State* L) {
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ (void)L;
+ return -1;
+#else
+ size_t messagesize;
+ const char* message = lua_tolstring(L, -1, &messagesize);
+ if (message) {
+ std::string err(message, messagesize);
+ lua_settop(L, 0);
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+ std::cerr << "[sol2] An error occurred and panic has been invoked: ";
+ std::cerr << err;
+ std::cerr << std::endl;
+#endif
+ throw error(err);
+ }
+ lua_settop(L, 0);
+ throw error(std::string("An unexpected error occurred and panic has been invoked"));
+#endif // Printing Errors
+ }
+
+ inline int default_traceback_error_handler(lua_State* L) {
+ std::string msg = "An unknown error has triggered the default error handler";
+ optional<string_view> maybetopmsg = stack::unqualified_check_get<string_view>(L, 1, &no_panic);
+ if (maybetopmsg) {
+ const string_view& topmsg = maybetopmsg.value();
+ msg.assign(topmsg.data(), topmsg.size());
+ }
+ luaL_traceback(L, L, msg.c_str(), 1);
+ optional<string_view> maybetraceback = stack::unqualified_check_get<string_view>(L, -1, &no_panic);
+ if (maybetraceback) {
+ const string_view& traceback = maybetraceback.value();
+ msg.assign(traceback.data(), traceback.size());
+ }
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+ // std::cerr << "[sol2] An error occurred and was caught in traceback: ";
+ // std::cerr << msg;
+ // std::cerr << std::endl;
+#endif // Printing
+ return stack::push(L, msg);
+ }
+
+ inline void set_default_state(lua_State* L, lua_CFunction panic_function = &default_at_panic,
+ lua_CFunction traceback_function = c_call<decltype(&default_traceback_error_handler), &default_traceback_error_handler>,
+ exception_handler_function exf = detail::default_exception_handler) {
+ lua_atpanic(L, panic_function);
+ protected_function::set_default_handler(object(L, in_place, traceback_function));
+ set_default_exception_handler(L, exf);
+ register_main_thread(L);
+ stack::luajit_exception_handler(L);
+ lua_value::set_lua_state(L);
+ }
+
+ inline std::size_t total_memory_used(lua_State* L) {
+ std::size_t kb = static_cast<std::size_t>(lua_gc(L, LUA_GCCOUNT, 0));
+ kb *= 1024;
+ kb += static_cast<std::size_t>(lua_gc(L, LUA_GCCOUNTB, 0));
+ return kb;
+ }
+
+ inline protected_function_result script_pass_on_error(lua_State*, protected_function_result result) {
+ return result;
+ }
+
+ inline protected_function_result script_throw_on_error(lua_State* L, protected_function_result result) {
+ type t = type_of(L, result.stack_index());
+ std::string err = "sol: ";
+ err += to_string(result.status());
+ err += " error";
+#if SOL_IS_ON(SOL_EXCEPTIONS)
+ std::exception_ptr eptr = std::current_exception();
+ if (eptr) {
+ err += " with a ";
+ try {
+ std::rethrow_exception(eptr);
+ }
+ catch (const std::exception& ex) {
+ err += "std::exception -- ";
+ err.append(ex.what());
+ }
+ catch (const std::string& message) {
+ err += "thrown message -- ";
+ err.append(message);
+ }
+ catch (const char* message) {
+ err += "thrown message -- ";
+ err.append(message);
+ }
+ catch (...) {
+ err.append("thrown but unknown type, cannot serialize into error message");
+ }
+ }
+#endif // serialize exception information if possible
+ if (t == type::string) {
+ err += ": ";
+ string_view serr = stack::unqualified_get<string_view>(L, result.stack_index());
+ err.append(serr.data(), serr.size());
+ }
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+ std::cerr << "[sol2] An error occurred and has been passed to an error handler: ";
+ std::cerr << err;
+ std::cerr << std::endl;
+#endif
+ // replacing information of stack error into pfr
+ int target = result.stack_index();
+ if (result.pop_count() > 0) {
+ stack::remove(L, target, result.pop_count());
+ }
+ stack::push(L, err);
+ int top = lua_gettop(L);
+ int towards = top - target;
+ if (towards != 0) {
+ lua_rotate(L, top, towards);
+ }
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ return result;
+#else
+ // just throw our error
+ throw error(detail::direct_error, err);
+#endif // If exceptions are allowed
+ }
+
+ inline protected_function_result script_default_on_error(lua_State* L, protected_function_result pfr) {
+#if SOL_IS_ON(SOL_DEFAULT_PASS_ON_ERROR)
+ return script_pass_on_error(L, std::move(pfr));
+#else
+ return script_throw_on_error(L, std::move(pfr));
+#endif
+ }
+
+ namespace stack {
+ inline error get_traceback_or_errors(lua_State* L) {
+ int p = default_traceback_error_handler(L);
+ sol::error err = stack::get<sol::error>(L, -p);
+ lua_pop(L, p);
+ return err;
+ }
+ } // namespace stack
+} // namespace sol
+
+// end of sol/state_handling.hpp
+
+#include <memory>
+#include <cstddef>
+
+namespace sol {
+
+ class state_view {
+ private:
+ lua_State* L;
+ table reg;
+ global_table global;
+
+ optional<object> is_loaded_package(const std::string& key) {
+ auto loaded = reg.traverse_get<optional<object>>("_LOADED", key);
+ bool is53mod = loaded && !(loaded->is<bool>() && !loaded->as<bool>());
+ if (is53mod)
+ return loaded;
+#if SOL_LUA_VERSION_I_ <= 501
+ auto loaded51 = global.traverse_get<optional<object>>("package", "loaded", key);
+ bool is51mod = loaded51 && !(loaded51->is<bool>() && !loaded51->as<bool>());
+ if (is51mod)
+ return loaded51;
+#endif
+ return nullopt;
+ }
+
+ template <typename T>
+ void ensure_package(const std::string& key, T&& sr) {
+#if SOL_LUA_VERSION_I_ <= 501
+ auto pkg = global["package"];
+ if (!pkg.valid()) {
+ pkg = create_table_with("loaded", create_table_with(key, sr));
+ }
+ else {
+ auto ld = pkg["loaded"];
+ if (!ld.valid()) {
+ ld = create_table_with(key, sr);
+ }
+ else {
+ ld[key] = sr;
+ }
+ }
+#endif
+ auto loaded = reg["_LOADED"];
+ if (!loaded.valid()) {
+ loaded = create_table_with(key, sr);
+ }
+ else {
+ loaded[key] = sr;
+ }
+ }
+
+ template <typename Fx>
+ object require_core(const std::string& key, Fx&& action, bool create_global = true) {
+ optional<object> loaded = is_loaded_package(key);
+ if (loaded && loaded->valid())
+ return std::move(*loaded);
+ int before = lua_gettop(L);
+ action();
+ int after = lua_gettop(L);
+ if (before == after) {
+ // I mean, you were supposed to return
+ // something, ANYTHING, from your requires script. I guess I'll just
+ // but some trash in here, it's on you after that?
+ ensure_package(key, static_cast<void*>(L));
+ return object(L, lua_nil);
+ }
+ stack_reference sr(L, -1);
+ if (create_global)
+ set(key, sr);
+ ensure_package(key, sr);
+ return stack::pop<object>(L);
+ }
+
+ public:
+ using iterator = typename global_table::iterator;
+ using const_iterator = typename global_table::const_iterator;
+
+ state_view(lua_State* Ls) : L(Ls), reg(Ls, LUA_REGISTRYINDEX), global(Ls, global_tag) {
+ }
+
+ state_view(this_state Ls) : state_view(Ls.L) {
+ }
+
+ lua_State* lua_state() const {
+ return L;
+ }
+
+ template <typename... Args>
+ void open_libraries(Args&&... args) {
+ static_assert(meta::all_same<lib, meta::unqualified_t<Args>...>::value, "all types must be libraries");
+ if constexpr (sizeof...(args) == 0) {
+ luaL_openlibs(L);
+ return;
+ }
+ else {
+ lib libraries[1 + sizeof...(args)] = { lib::count, std::forward<Args>(args)... };
+
+ for (auto&& library : libraries) {
+ switch (library) {
+#if SOL_LUA_VERSION_I_ <= 501 && SOL_IS_ON(SOL_USE_LUAJIT)
+ case lib::coroutine:
+#endif // luajit opens coroutine base stuff
+ case lib::base:
+ luaL_requiref(L, "base", luaopen_base, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::package:
+ luaL_requiref(L, "package", luaopen_package, 1);
+ lua_pop(L, 1);
+ break;
+#if SOL_IS_OFF(SOL_USE_LUAJIT)
+ case lib::coroutine:
+#if SOL_LUA_VERSION_I_ > 501
+ luaL_requiref(L, "coroutine", luaopen_coroutine, 1);
+ lua_pop(L, 1);
+#endif // Lua 5.2+ only
+ break;
+#endif // Not LuaJIT - comes builtin
+ case lib::string:
+ luaL_requiref(L, "string", luaopen_string, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::table:
+ luaL_requiref(L, "table", luaopen_table, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::math:
+ luaL_requiref(L, "math", luaopen_math, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::bit32:
+#if SOL_IS_ON(SOL_USE_LUAJIT)
+ luaL_requiref(L, "bit32", luaopen_bit, 1);
+ lua_pop(L, 1);
+#elif SOL_IS_ON(SOL_LUA_BIT32_LIB)
+ luaL_requiref(L, "bit32", luaopen_bit32, 1);
+ lua_pop(L, 1);
+#else
+#endif
+ break;
+ case lib::io:
+ luaL_requiref(L, "io", luaopen_io, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::os:
+ luaL_requiref(L, "os", luaopen_os, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::debug:
+ luaL_requiref(L, "debug", luaopen_debug, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::utf8:
+#if SOL_LUA_VERSION_I_ > 502 && SOL_IS_OFF(SOL_USE_LUAJIT)
+ luaL_requiref(L, "utf8", luaopen_utf8, 1);
+ lua_pop(L, 1);
+#endif // Lua 5.3+ only
+ break;
+ case lib::ffi:
+#if SOL_IS_ON(SOL_USE_LUAJIT) && SOL_IS_OFF(SOL_LUAJIT_FFI_DISABLED)
+ luaL_requiref(L, "ffi", luaopen_ffi, 1);
+ lua_pop(L, 1);
+#endif // LuaJIT only
+ break;
+ case lib::jit:
+#if SOL_IS_ON(SOL_USE_LUAJIT)
+ luaL_requiref(L, "jit", luaopen_jit, 0);
+ lua_pop(L, 1);
+#endif // LuaJIT Only
+ break;
+ case lib::count:
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ object require(const std::string& key, lua_CFunction open_function, bool create_global = true) {
+ luaL_requiref(L, key.c_str(), open_function, create_global ? 1 : 0);
+ return stack::pop<object>(L);
+ }
+
+ object require_script(const std::string& key, const string_view& code, bool create_global = true,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ auto action = [this, &code, &chunkname, &mode]() { stack::script(L, code, chunkname, mode); };
+ return require_core(key, action, create_global);
+ }
+
+ object require_file(const std::string& key, const std::string& filename, bool create_global = true, load_mode mode = load_mode::any) {
+ auto action = [this, &filename, &mode]() { stack::script_file(L, filename, mode); };
+ return require_core(key, action, create_global);
+ }
+
+ void clear_package_loaders() {
+ optional<table> maybe_package = this->global["package"];
+ if (!maybe_package) {
+ // package lib wasn't opened
+ // open package lib
+ return;
+ }
+ table& package = *maybe_package;
+ // yay for version differences...
+ // one day Lua 5.1 will die a peaceful death
+ // and its old bones will find blissful rest
+ auto loaders_proxy = package
+#if SOL_LUA_VERSION_I_ < 502
+ ["loaders"]
+#else
+ ["searchers"]
+#endif
+ ;
+ if (!loaders_proxy.valid()) {
+ // nothing to clear
+ return;
+ }
+ // we need to create the table for loaders
+ // table does not exist, so create and move forward
+ loaders_proxy = new_table(1, 0);
+ }
+
+ template <typename Fx>
+ void add_package_loader(Fx&& fx, bool clear_all_package_loaders = false) {
+ optional<table> maybe_package = this->global["package"];
+ if (!maybe_package) {
+ // package lib wasn't opened
+ // open package lib
+ return;
+ }
+ table& package = *maybe_package;
+ // yay for version differences...
+ // one day Lua 5.1 will die a peaceful death
+ // and its old bones will find blissful rest
+ auto loaders_proxy = package
+#if SOL_LUA_VERSION_I_ < 502
+ ["loaders"]
+#else
+ ["searchers"]
+#endif
+ ;
+ bool make_new_table = clear_all_package_loaders || !loaders_proxy.valid();
+ if (make_new_table) {
+ // we need to create the table for loaders
+ // table does not exist, so create and move forward
+ loaders_proxy = new_table(1, 0);
+ }
+ optional<table> maybe_loaders = loaders_proxy;
+ if (!maybe_loaders) {
+ // loaders/searches
+ // thing exists in package, but it
+ // ain't a table or a table-alike...!
+ return;
+ }
+ table loaders = loaders_proxy;
+ loaders.add(std::forward<Fx>(fx));
+ }
+
+ template <typename E>
+ protected_function_result do_reader(lua_Reader reader, void* data, const basic_environment<E>& env,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
+ load_status x = static_cast<load_status>(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ set_environment(env, pf);
+ return pf();
+ }
+
+ protected_function_result do_reader(
+ lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
+ load_status x = static_cast<load_status>(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ return pf();
+ }
+
+ template <typename E>
+ protected_function_result do_string(const string_view& code, const basic_environment<E>& env,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
+ load_status x = static_cast<load_status>(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ set_environment(env, pf);
+ return pf();
+ }
+
+ protected_function_result do_string(
+ const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
+ load_status x = static_cast<load_status>(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ return pf();
+ }
+
+ template <typename E>
+ protected_function_result do_file(const std::string& filename, const basic_environment<E>& env, load_mode mode = load_mode::any) {
+ load_status x = static_cast<load_status>(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ set_environment(env, pf);
+ return pf();
+ }
+
+ protected_function_result do_file(const std::string& filename, load_mode mode = load_mode::any) {
+ load_status x = static_cast<load_status>(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ return pf();
+ }
+
+ template <typename Fx,
+ meta::disable_any<meta::is_string_constructible<meta::unqualified_t<Fx>>,
+ meta::is_specialization_of<meta::unqualified_t<Fx>, basic_environment>> = meta::enabler>
+ protected_function_result safe_script(
+ lua_Reader reader, void* data, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ protected_function_result pfr = do_reader(reader, data, chunkname, mode);
+ if (!pfr.valid()) {
+ return on_error(L, std::move(pfr));
+ }
+ return pfr;
+ }
+
+ protected_function_result safe_script(
+ lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(reader, data, script_default_on_error, chunkname, mode);
+ }
+
+ template <typename Fx,
+ meta::disable_any<meta::is_string_constructible<meta::unqualified_t<Fx>>,
+ meta::is_specialization_of<meta::unqualified_t<Fx>, basic_environment>> = meta::enabler>
+ protected_function_result safe_script(
+ const string_view& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ protected_function_result pfr = do_string(code, chunkname, mode);
+ if (!pfr.valid()) {
+ return on_error(L, std::move(pfr));
+ }
+ return pfr;
+ }
+
+ template <typename Fx, typename E>
+ protected_function_result safe_script(const string_view& code, const basic_environment<E>& env, Fx&& on_error,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ protected_function_result pfr = do_string(code, env, chunkname, mode);
+ if (!pfr.valid()) {
+ return on_error(L, std::move(pfr));
+ }
+ return pfr;
+ }
+
+ template <typename E>
+ protected_function_result safe_script(const string_view& code, const basic_environment<E>& env,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, env, script_default_on_error, chunkname, mode);
+ }
+
+ protected_function_result safe_script(
+ const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, script_default_on_error, chunkname, mode);
+ }
+
+ template <typename Fx,
+ meta::disable_any<meta::is_string_constructible<meta::unqualified_t<Fx>>,
+ meta::is_specialization_of<meta::unqualified_t<Fx>, basic_environment>> = meta::enabler>
+ protected_function_result safe_script_file(const std::string& filename, Fx&& on_error, load_mode mode = load_mode::any) {
+ protected_function_result pfr = do_file(filename, mode);
+ if (!pfr.valid()) {
+ return on_error(L, std::move(pfr));
+ }
+ return pfr;
+ }
+
+ template <typename Fx, typename E>
+ protected_function_result safe_script_file(
+ const std::string& filename, const basic_environment<E>& env, Fx&& on_error, load_mode mode = load_mode::any) {
+ protected_function_result pfr = do_file(filename, env, mode);
+ if (!pfr.valid()) {
+ return on_error(L, std::move(pfr));
+ }
+ return pfr;
+ }
+
+ template <typename E>
+ protected_function_result safe_script_file(const std::string& filename, const basic_environment<E>& env, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, env, script_default_on_error, mode);
+ }
+
+ protected_function_result safe_script_file(const std::string& filename, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, script_default_on_error, mode);
+ }
+
+ template <typename E>
+ unsafe_function_result unsafe_script(lua_Reader reader, void* data, const basic_environment<E>& env,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
+ int index = lua_gettop(L);
+ if (lua_load(L, reader, data, chunknametarget, to_string(mode).c_str())) {
+ lua_error(L);
+ }
+ set_environment(env, stack_reference(L, raw_index(index + 1)));
+ if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ unsafe_function_result unsafe_script(
+ lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ int index = lua_gettop(L);
+ stack::script(L, reader, data, chunkname, mode);
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ template <typename E>
+ unsafe_function_result unsafe_script(const string_view& code, const basic_environment<E>& env,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
+ int index = lua_gettop(L);
+ if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())) {
+ lua_error(L);
+ }
+ set_environment(env, stack_reference(L, raw_index(index + 1)));
+ if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ unsafe_function_result unsafe_script(
+ const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ int index = lua_gettop(L);
+ stack::script(L, code, chunkname, mode);
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ template <typename E>
+ unsafe_function_result unsafe_script_file(const std::string& filename, const basic_environment<E>& env, load_mode mode = load_mode::any) {
+ int index = lua_gettop(L);
+ if (luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())) {
+ lua_error(L);
+ }
+ set_environment(env, stack_reference(L, raw_index(index + 1)));
+ if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ unsafe_function_result unsafe_script_file(const std::string& filename, load_mode mode = load_mode::any) {
+ int index = lua_gettop(L);
+ stack::script_file(L, filename, mode);
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ template <typename Fx,
+ meta::disable_any<meta::is_string_constructible<meta::unqualified_t<Fx>>,
+ meta::is_specialization_of<meta::unqualified_t<Fx>, basic_environment>> = meta::enabler>
+ protected_function_result script(
+ const string_view& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, std::forward<Fx>(on_error), chunkname, mode);
+ }
+
+ template <typename Fx,
+ meta::disable_any<meta::is_string_constructible<meta::unqualified_t<Fx>>,
+ meta::is_specialization_of<meta::unqualified_t<Fx>, basic_environment>> = meta::enabler>
+ protected_function_result script_file(const std::string& filename, Fx&& on_error, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, std::forward<Fx>(on_error), mode);
+ }
+
+ template <typename Fx, typename E>
+ protected_function_result script(const string_view& code, const basic_environment<E>& env, Fx&& on_error,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, env, std::forward<Fx>(on_error), chunkname, mode);
+ }
+
+ template <typename Fx, typename E>
+ protected_function_result script_file(const std::string& filename, const basic_environment<E>& env, Fx&& on_error, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, env, std::forward<Fx>(on_error), mode);
+ }
+
+ protected_function_result script(
+ const string_view& code, const environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, env, script_default_on_error, chunkname, mode);
+ }
+
+ protected_function_result script_file(const std::string& filename, const environment& env, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, env, script_default_on_error, mode);
+ }
+
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS)
+ protected_function_result script(
+ lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(reader, data, chunkname, mode);
+ }
+
+ protected_function_result script(
+ const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, chunkname, mode);
+ }
+
+ protected_function_result script_file(const std::string& filename, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, mode);
+ }
+#else
+ unsafe_function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return unsafe_script(code, chunkname, mode);
+ }
+
+ unsafe_function_result script_file(const std::string& filename, load_mode mode = load_mode::any) {
+ return unsafe_script_file(filename, mode);
+ }
+#endif
+ load_result load(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
+ load_status x = static_cast<load_status>(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()));
+ return load_result(L, absolute_index(L, -1), 1, 1, x);
+ }
+
+ load_result load_buffer(const char* buff, size_t size, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return load(string_view(buff, size), chunkname, mode);
+ }
+
+ load_result load_buffer(
+ const std::byte* buff, size_t size, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return load(string_view(reinterpret_cast<const char*>(buff), size), chunkname, mode);
+ }
+
+ load_result load_file(const std::string& filename, load_mode mode = load_mode::any) {
+ load_status x = static_cast<load_status>(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str()));
+ return load_result(L, absolute_index(L, -1), 1, 1, x);
+ }
+
+ load_result load(lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
+ load_status x = static_cast<load_status>(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()));
+ return load_result(L, absolute_index(L, -1), 1, 1, x);
+ }
+
+ iterator begin() const {
+ return global.begin();
+ }
+
+ iterator end() const {
+ return global.end();
+ }
+
+ const_iterator cbegin() const {
+ return global.cbegin();
+ }
+
+ const_iterator cend() const {
+ return global.cend();
+ }
+
+ global_table globals() const {
+ // if we return a reference
+ // we'll be screwed a bit
+ return global;
+ }
+
+ global_table& globals() {
+ return global;
+ }
+
+ table registry() const {
+ return reg;
+ }
+
+ std::size_t memory_used() const {
+ return total_memory_used(lua_state());
+ }
+
+ int stack_top() const {
+ return stack::top(L);
+ }
+
+ int stack_clear() {
+ int s = stack_top();
+ lua_pop(L, s);
+ return s;
+ }
+
+ bool supports_gc_mode(gc_mode mode) const noexcept {
+#if SOL_LUA_VERSION_I_ >= 504
+ // supports all modes
+ (void)mode;
+ return true;
+#endif
+ return mode == gc_mode::default_value;
+ }
+
+ bool is_gc_on() const {
+#if SOL_LUA_VERSION_I_ >= 502
+ return lua_gc(lua_state(), LUA_GCISRUNNING, 0) == 1;
+#else
+ // You cannot turn it off in Lua 5.1
+ return true;
+#endif
+ }
+
+ void collect_garbage() {
+ lua_gc(lua_state(), LUA_GCCOLLECT, 0);
+ }
+
+ void collect_gc() {
+ collect_garbage();
+ }
+
+ bool step_gc(int step_size_kilobytes) {
+ // THOUGHT: std::chrono-alikes to map "kilobyte size" here...?
+ // Make it harder to give MB or KB to a B parameter...?
+ // Probably overkill for now.
+#if SOL_LUA_VERSION_I_ >= 504
+ // The manual implies that this function is almost always successful...
+ // is it?? It could depend on the GC mode...
+ return lua_gc(lua_state(), LUA_GCSTEP, step_size_kilobytes) != 0;
+#else
+ return lua_gc(lua_state(), LUA_GCSTEP, step_size_kilobytes) == 1;
+#endif
+ }
+
+ void restart_gc() {
+ lua_gc(lua_state(), LUA_GCRESTART, 0);
+ }
+
+ void stop_gc() {
+ lua_gc(lua_state(), LUA_GCSTOP, 0);
+ }
+
+ // Returns the old GC mode. Check support using the supports_gc_mode function.
+ gc_mode change_gc_mode_incremental(int pause, int step_multiplier, int step_byte_size) {
+ // "What the fuck does any of this mean??"
+ // http://www.lua.org/manual/5.4/manual.html#2.5.1
+
+ // THOUGHT: std::chrono-alikes to map "byte size" here...?
+ // Make it harder to give MB or KB to a B parameter...?
+ // Probably overkill for now.
+#if SOL_LUA_VERSION_I_ >= 504
+ int old_mode = lua_gc(lua_state(), LUA_GCINC, pause, step_multiplier, step_byte_size);
+ if (old_mode == LUA_GCGEN) {
+ return gc_mode::generational;
+ }
+ else if (old_mode == LUA_GCINC) {
+ return gc_mode::incremental;
+ }
+#else
+ lua_gc(lua_state(), LUA_GCSETPAUSE, pause);
+ lua_gc(lua_state(), LUA_GCSETSTEPMUL, step_multiplier);
+ (void)step_byte_size; // means nothing in older versions
+#endif
+ return gc_mode::default_value;
+ }
+
+ // Returns the old GC mode. Check support using the supports_gc_mode function.
+ gc_mode change_gc_mode_generational(int minor_multiplier, int major_multiplier) {
+#if SOL_LUA_VERSION_I_ >= 504
+ // "What does this shit mean?"
+ // http://www.lua.org/manual/5.4/manual.html#2.5.2
+ int old_mode = lua_gc(lua_state(), LUA_GCGEN, minor_multiplier, major_multiplier);
+ if (old_mode == LUA_GCGEN) {
+ return gc_mode::generational;
+ }
+ else if (old_mode == LUA_GCINC) {
+ return gc_mode::incremental;
+ }
+#else
+ (void)minor_multiplier;
+ (void)major_multiplier;
+#endif
+ return gc_mode::default_value;
+ }
+
+ operator lua_State*() const {
+ return lua_state();
+ }
+
+ void set_panic(lua_CFunction panic) {
+ lua_atpanic(lua_state(), panic);
+ }
+
+ void set_exception_handler(exception_handler_function handler) {
+ set_default_exception_handler(lua_state(), handler);
+ }
+
+ template <typename... Args, typename... Keys>
+ decltype(auto) get(Keys&&... keys) const {
+ return global.get<Args...>(std::forward<Keys>(keys)...);
+ }
+
+ template <typename T, typename Key>
+ decltype(auto) get_or(Key&& key, T&& otherwise) const {
+ return global.get_or(std::forward<Key>(key), std::forward<T>(otherwise));
+ }
+
+ template <typename T, typename Key, typename D>
+ decltype(auto) get_or(Key&& key, D&& otherwise) const {
+ return global.get_or<T>(std::forward<Key>(key), std::forward<D>(otherwise));
+ }
+
+ template <typename... Args>
+ state_view& set(Args&&... args) {
+ global.set(std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename T, typename... Keys>
+ decltype(auto) traverse_get(Keys&&... keys) const {
+ return global.traverse_get<T>(std::forward<Keys>(keys)...);
+ }
+
+ template <typename... Args>
+ state_view& traverse_set(Args&&... args) {
+ global.traverse_set(std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Class, typename... Args>
+ usertype<Class> new_usertype(Args&&... args) {
+ return global.new_usertype<Class>(std::forward<Args>(args)...);
+ }
+
+ template <bool read_only = true, typename... Args>
+ state_view& new_enum(const string_view& name, Args&&... args) {
+ global.new_enum<read_only>(name, std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename T, bool read_only = true>
+ state_view& new_enum(const string_view& name, std::initializer_list<std::pair<string_view, T>> items) {
+ global.new_enum<T, read_only>(name, std::move(items));
+ return *this;
+ }
+
+ template <typename Fx>
+ void for_each(Fx&& fx) {
+ global.for_each(std::forward<Fx>(fx));
+ }
+
+ template <typename T>
+ table_proxy<global_table&, detail::proxy_key_t<T>> operator[](T&& key) {
+ return global[std::forward<T>(key)];
+ }
+
+ template <typename T>
+ table_proxy<const global_table&, detail::proxy_key_t<T>> operator[](T&& key) const {
+ return global[std::forward<T>(key)];
+ }
+
+ template <typename Sig, typename... Args, typename Key>
+ state_view& set_function(Key&& key, Args&&... args) {
+ global.set_function<Sig>(std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename... Args, typename Key>
+ state_view& set_function(Key&& key, Args&&... args) {
+ global.set_function(std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Name>
+ table create_table(Name&& name, int narr = 0, int nrec = 0) {
+ return global.create(std::forward<Name>(name), narr, nrec);
+ }
+
+ template <typename Name, typename Key, typename Value, typename... Args>
+ table create_table(Name&& name, int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ return global.create(std::forward<Name>(name), narr, nrec, std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ }
+
+ template <typename Name, typename... Args>
+ table create_named_table(Name&& name, Args&&... args) {
+ table x = global.create_with(std::forward<Args>(args)...);
+ global.set(std::forward<Name>(name), x);
+ return x;
+ }
+
+ table create_table(int narr = 0, int nrec = 0) {
+ return create_table(lua_state(), narr, nrec);
+ }
+
+ template <typename Key, typename Value, typename... Args>
+ table create_table(int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ return create_table(lua_state(), narr, nrec, std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ table create_table_with(Args&&... args) {
+ return create_table_with(lua_state(), std::forward<Args>(args)...);
+ }
+
+ static inline table create_table(lua_State* L, int narr = 0, int nrec = 0) {
+ return global_table::create(L, narr, nrec);
+ }
+
+ template <typename Key, typename Value, typename... Args>
+ static inline table create_table(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ return global_table::create(L, narr, nrec, std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ static inline table create_table_with(lua_State* L, Args&&... args) {
+ return global_table::create_with(L, std::forward<Args>(args)...);
+ }
+ };
+} // namespace sol
+
+// end of sol/state_view.hpp
+
+// beginning of sol/thread.hpp
+
+namespace sol {
+ struct lua_thread_state {
+ lua_State* L;
+
+ lua_thread_state(lua_State* Ls) : L(Ls) {
+ }
+
+ lua_State* lua_state() const noexcept {
+ return L;
+ }
+ operator lua_State*() const noexcept {
+ return lua_state();
+ }
+ lua_State* operator->() const noexcept {
+ return lua_state();
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_pusher<lua_thread_state> {
+ int push(lua_State*, lua_thread_state lts) {
+ lua_pushthread(lts.L);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_getter<lua_thread_state> {
+ lua_thread_state get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ lua_thread_state lts(lua_tothread(L, index));
+ return lts;
+ }
+ };
+
+ template <>
+ struct unqualified_check_getter<lua_thread_state> {
+ template <typename Handler>
+ optional<lua_thread_state> get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ lua_thread_state lts(lua_tothread(L, index));
+ if (lts.lua_state() == nullptr) {
+ handler(L, index, type::thread, type_of(L, index), "value is not a valid thread type");
+ return nullopt;
+ }
+ tracking.use(1);
+ return lts;
+ }
+ };
+ } // namespace stack
+
+ template <typename ref_t>
+ class basic_thread : public basic_object<ref_t> {
+ private:
+ using base_t = basic_object<ref_t>;
+
+ public:
+ using base_t::lua_state;
+
+ basic_thread() noexcept = default;
+ basic_thread(const basic_thread&) = default;
+ basic_thread(basic_thread&&) = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_thread>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_thread(T&& r) : base_t(std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_thread>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_thread(const stack_reference& r) : basic_thread(r.lua_state(), r.stack_index()) {};
+ basic_thread(stack_reference&& r) : basic_thread(r.lua_state(), r.stack_index()) {};
+ basic_thread& operator=(const basic_thread&) = default;
+ basic_thread& operator=(basic_thread&&) = default;
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_thread(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_thread>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_thread(lua_State* L, int index = -1) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_thread>(L, index, handler);
+#endif // Safety
+ }
+ basic_thread(lua_State* L, ref_index index) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_thread>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_thread(lua_State* L, lua_State* actualthread) : basic_thread(L, lua_thread_state { actualthread }) {
+ }
+ basic_thread(lua_State* L, this_state actualthread) : basic_thread(L, lua_thread_state { actualthread.L }) {
+ }
+ basic_thread(lua_State* L, lua_thread_state actualthread) : base_t(L, -stack::push(L, actualthread)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_thread>(lua_state(), -1, handler);
+#endif // Safety
+ if (!is_stack_based<base_t>::value) {
+ lua_pop(lua_state(), 1);
+ }
+ }
+
+ state_view state() const {
+ return state_view(this->thread_state());
+ }
+
+ bool is_main_thread() const {
+ return stack::is_main_thread(this->thread_state());
+ }
+
+ lua_State* thread_state() const {
+ auto pp = stack::push_pop(*this);
+ lua_State* lthread = lua_tothread(lua_state(), -1);
+ return lthread;
+ }
+
+ thread_status status() const {
+ lua_State* lthread = thread_state();
+ auto lstat = static_cast<thread_status>(lua_status(lthread));
+ if (lstat == thread_status::ok) {
+ lua_Debug ar;
+ if (lua_getstack(lthread, 0, &ar) > 0)
+ return thread_status::ok;
+ else if (lua_gettop(lthread) == 0)
+ return thread_status::dead;
+ else
+ return thread_status::yielded;
+ }
+ return lstat;
+ }
+
+ basic_thread create() {
+ return create(lua_state());
+ }
+
+ static basic_thread create(lua_State* L) {
+ lua_newthread(L);
+ basic_thread result(L);
+ if (!is_stack_based<base_t>::value) {
+ lua_pop(L, 1);
+ }
+ return result;
+ }
+ };
+
+ typedef basic_thread<reference> thread;
+ typedef basic_thread<stack_reference> stack_thread;
+} // namespace sol
+
+// end of sol/thread.hpp
+
+namespace sol {
+
+ class state : private std::unique_ptr<lua_State, detail::state_deleter>, public state_view {
+ private:
+ typedef std::unique_ptr<lua_State, detail::state_deleter> unique_base;
+
+ public:
+ state(lua_CFunction panic = default_at_panic) : unique_base(luaL_newstate()), state_view(unique_base::get()) {
+ set_default_state(unique_base::get(), panic);
+ }
+
+ state(lua_CFunction panic, lua_Alloc alfunc, void* alpointer = nullptr)
+ : unique_base(lua_newstate(alfunc, alpointer)), state_view(unique_base::get()) {
+ set_default_state(unique_base::get(), panic);
+ }
+
+ state(const state&) = delete;
+ state(state&&) = default;
+ state& operator=(const state&) = delete;
+ state& operator=(state&& that) {
+ state_view::operator=(std::move(that));
+ unique_base::operator=(std::move(that));
+ return *this;
+ }
+
+ using state_view::get;
+
+ ~state() {
+ }
+ };
+} // namespace sol
+
+// end of sol/state.hpp
+
+// beginning of sol/coroutine.hpp
+
+namespace sol {
+ template <typename Reference>
+ class basic_coroutine : public basic_object<Reference> {
+ private:
+ using base_t = basic_object<Reference>;
+ using handler_t = reference;
+
+ private:
+ call_status stats = call_status::yielded;
+
+ void luacall(std::ptrdiff_t argcount, std::ptrdiff_t) {
+#if SOL_LUA_VERSION_I_ >= 504
+ int nresults;
+ stats = static_cast<call_status>(lua_resume(lua_state(), nullptr, static_cast<int>(argcount), &nresults));
+#else
+ stats = static_cast<call_status>(lua_resume(lua_state(), nullptr, static_cast<int>(argcount)));
+#endif
+ }
+
+ template <std::size_t... I, typename... Ret>
+ auto invoke(types<Ret...>, std::index_sequence<I...>, std::ptrdiff_t n) {
+ luacall(n, sizeof...(Ret));
+ return stack::pop<std::tuple<Ret...>>(lua_state());
+ }
+
+ template <std::size_t I, typename Ret>
+ Ret invoke(types<Ret>, std::index_sequence<I>, std::ptrdiff_t n) {
+ luacall(n, 1);
+ return stack::pop<Ret>(lua_state());
+ }
+
+ template <std::size_t I>
+ void invoke(types<void>, std::index_sequence<I>, std::ptrdiff_t n) {
+ luacall(n, 0);
+ }
+
+ protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n) {
+ int firstreturn = 1;
+ luacall(n, LUA_MULTRET);
+ int poststacksize = lua_gettop(this->lua_state());
+ int returncount = poststacksize - (firstreturn - 1);
+ if (error()) {
+ if (m_error_handler.valid()) {
+ string_view err = stack::get<string_view>(this->lua_state(), poststacksize);
+ m_error_handler.push();
+ stack::push(this->lua_state(), err);
+ lua_call(lua_state(), 1, 1);
+ }
+ return protected_function_result(this->lua_state(), lua_absindex(this->lua_state(), -1), 1, returncount, status());
+ }
+ return protected_function_result(this->lua_state(), firstreturn, returncount, returncount, status());
+ }
+
+ public:
+ using base_t::lua_state;
+
+ basic_coroutine() = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_coroutine>>,
+ meta::neg<std::is_base_of<proxy_base_tag, meta::unqualified_t<T>>>, meta::neg<std::is_same<base_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_coroutine(T&& r) noexcept
+ : base_t(std::forward<T>(r)), m_error_handler(detail::get_default_handler<reference, is_main_threaded<base_t>::value>(r.lua_state())) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_function<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+
+ basic_coroutine(const basic_coroutine& other) = default;
+ basic_coroutine& operator=(const basic_coroutine&) = default;
+
+ basic_coroutine(basic_coroutine&& other) noexcept : base_t(std::move(other)), m_error_handler(this->lua_state(), std::move(other.m_error_handler)) {
+ }
+
+ basic_coroutine& operator=(basic_coroutine&& other) noexcept {
+ base_t::operator=(std::move(other));
+ // must change the state, since it could change on the coroutine type
+ m_error_handler = handler_t(this->lua_state(), std::move(other.m_error_handler));
+ return *this;
+ }
+
+ basic_coroutine(const basic_function<base_t>& b) noexcept
+ : basic_coroutine(b, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(b.lua_state())) {
+ }
+ basic_coroutine(basic_function<base_t>&& b) noexcept
+ : basic_coroutine(std::move(b), detail::get_default_handler<reference, is_main_threaded<base_t>::value>(b.lua_state())) {
+ }
+ basic_coroutine(const basic_function<base_t>& b, handler_t eh) noexcept : base_t(b), m_error_handler(std::move(eh)) {
+ }
+ basic_coroutine(basic_function<base_t>&& b, handler_t eh) noexcept : base_t(std::move(b)), m_error_handler(std::move(eh)) {
+ }
+ basic_coroutine(const stack_reference& r) noexcept
+ : basic_coroutine(r.lua_state(), r.stack_index(), detail::get_default_handler<reference, is_main_threaded<base_t>::value>(r.lua_state())) {
+ }
+ basic_coroutine(stack_reference&& r) noexcept
+ : basic_coroutine(r.lua_state(), r.stack_index(), detail::get_default_handler<reference, is_main_threaded<base_t>::value>(r.lua_state())) {
+ }
+ basic_coroutine(const stack_reference& r, handler_t eh) noexcept : basic_coroutine(r.lua_state(), r.stack_index(), std::move(eh)) {
+ }
+ basic_coroutine(stack_reference&& r, handler_t eh) noexcept : basic_coroutine(r.lua_state(), r.stack_index(), std::move(eh)) {
+ }
+
+ template <typename Super>
+ basic_coroutine(const proxy_base<Super>& p)
+ : basic_coroutine(p, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(p.lua_state())) {
+ }
+ template <typename Super>
+ basic_coroutine(proxy_base<Super>&& p)
+ : basic_coroutine(std::move(p), detail::get_default_handler<reference, is_main_threaded<base_t>::value>(p.lua_state())) {
+ }
+ template <typename Proxy, typename HandlerReference,
+ meta::enable<std::is_base_of<proxy_base_tag, meta::unqualified_t<Proxy>>,
+ meta::neg<is_lua_index<meta::unqualified_t<HandlerReference>>>> = meta::enabler>
+ basic_coroutine(Proxy&& p, HandlerReference&& eh) : basic_coroutine(detail::force_cast<base_t>(p), std::forward<HandlerReference>(eh)) {
+ }
+
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_coroutine(lua_State* L, T&& r) noexcept
+ : basic_coroutine(L, std::forward<T>(r), detail::get_default_handler<reference, is_main_threaded<base_t>::value>(L)) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_coroutine(lua_State* L, T&& r, handler_t eh) : base_t(L, std::forward<T>(r)), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(lua_state(), -1, handler);
+#endif // Safety
+ }
+
+ basic_coroutine(lua_nil_t n) : base_t(n), m_error_handler(n) {
+ }
+
+ basic_coroutine(lua_State* L, int index = -1)
+ : basic_coroutine(L, index, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(L)) {
+ }
+ basic_coroutine(lua_State* L, int index, handler_t eh) : base_t(L, index), m_error_handler(std::move(eh)) {
+#ifdef SOL_SAFE_REFERENCES
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(L, index, handler);
+#endif // Safety
+ }
+ basic_coroutine(lua_State* L, absolute_index index)
+ : basic_coroutine(L, index, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(L)) {
+ }
+ basic_coroutine(lua_State* L, absolute_index index, handler_t eh) : base_t(L, index), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(L, index, handler);
+#endif // Safety
+ }
+ basic_coroutine(lua_State* L, raw_index index)
+ : basic_coroutine(L, index, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(L)) {
+ }
+ basic_coroutine(lua_State* L, raw_index index, handler_t eh) : base_t(L, index), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(L, index, handler);
+#endif // Safety
+ }
+ basic_coroutine(lua_State* L, ref_index index)
+ : basic_coroutine(L, index, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(L)) {
+ }
+ basic_coroutine(lua_State* L, ref_index index, handler_t eh) : base_t(L, index), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(lua_state(), -1, handler);
+#endif // Safety
+ }
+
+ call_status status() const noexcept {
+ return stats;
+ }
+
+ bool error() const noexcept {
+ call_status cs = status();
+ return cs != call_status::ok && cs != call_status::yielded;
+ }
+
+ bool runnable() const noexcept {
+ return base_t::valid() && (status() == call_status::yielded);
+ }
+
+ explicit operator bool() const noexcept {
+ return runnable();
+ }
+
+ template <typename... Args>
+ protected_function_result operator()(Args&&... args) {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) operator()(types<Ret...>, Args&&... args) {
+ return call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) {
+ // some users screw up coroutine.create
+ // and try to use it with sol::coroutine without ever calling the first resume in Lua
+ // this makes the stack incompatible with other kinds of stacks: protect against this
+ // make sure coroutines don't screw us over
+ base_t::push();
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount);
+ }
+
+ private:
+ handler_t m_error_handler;
+ };
+} // namespace sol
+
+// end of sol/coroutine.hpp
+
+// beginning of sol/userdata.hpp
+
+namespace sol {
+ template <typename base_type>
+ class basic_userdata : public basic_table<base_type> {
+ private:
+ using base_t = basic_table<base_type>;
+
+ public:
+ using base_t::lua_state;
+
+ basic_userdata() noexcept = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_userdata>>, meta::neg<std::is_same<base_t, stack_reference>>,
+ is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_userdata(T&& r) noexcept : base_t(std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_userdata<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ type_assert(lua_state(), -1, type::userdata);
+ }
+#endif // Safety
+ }
+ basic_userdata(const basic_userdata&) = default;
+ basic_userdata(basic_userdata&&) = default;
+ basic_userdata& operator=(const basic_userdata&) = default;
+ basic_userdata& operator=(basic_userdata&&) = default;
+ basic_userdata(const stack_reference& r) : basic_userdata(r.lua_state(), r.stack_index()) {
+ }
+ basic_userdata(stack_reference&& r) : basic_userdata(r.lua_state(), r.stack_index()) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_userdata(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_userdata>(L, -1, handler);
+#endif // Safety
+ }
+ basic_userdata(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_userdata>(L, index, handler);
+#endif // Safety
+ }
+ basic_userdata(lua_State* L, ref_index index) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_userdata>(L, -1, handler);
+#endif // Safety
+ }
+ };
+
+ template <typename base_type>
+ class basic_lightuserdata : public basic_object_base<base_type> {
+ typedef basic_object_base<base_type> base_t;
+
+ public:
+ using base_t::lua_state;
+
+ basic_lightuserdata() noexcept = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_lightuserdata>>, meta::neg<std::is_same<base_t, stack_reference>>,
+ is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_lightuserdata(T&& r) noexcept : base_t(std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_lightuserdata<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ type_assert(lua_state(), -1, type::lightuserdata);
+ }
+#endif // Safety
+ }
+ basic_lightuserdata(const basic_lightuserdata&) = default;
+ basic_lightuserdata(basic_lightuserdata&&) = default;
+ basic_lightuserdata& operator=(const basic_lightuserdata&) = default;
+ basic_lightuserdata& operator=(basic_lightuserdata&&) = default;
+ basic_lightuserdata(const stack_reference& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {
+ }
+ basic_lightuserdata(stack_reference&& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_lightuserdata(lua_State* L, T&& r) : basic_lightuserdata(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_lightuserdata>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_lightuserdata(lua_State* L, int index = -1) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_lightuserdata>(L, index, handler);
+#endif // Safety
+ }
+ basic_lightuserdata(lua_State* L, ref_index index) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_lightuserdata>(lua_state(), index, handler);
+#endif // Safety
+ }
+ };
+
+} // namespace sol
+
+// end of sol/userdata.hpp
+
+// beginning of sol/as_args.hpp
+
+namespace sol {
+ template <typename T>
+ struct as_args_t {
+ T src;
+ };
+
+ template <typename Source>
+ auto as_args(Source&& source) {
+ return as_args_t<Source> { std::forward<Source>(source) };
+ }
+
+ namespace stack {
+ template <typename T>
+ struct unqualified_pusher<as_args_t<T>> {
+ int push(lua_State* L, const as_args_t<T>& e) {
+ int p = 0;
+ for (const auto& i : e.src) {
+ p += stack::push(L, i);
+ }
+ return p;
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/as_args.hpp
+
+// beginning of sol/variadic_args.hpp
+
+#include <limits>
+#include <iterator>
+
+namespace sol {
+ struct variadic_args {
+ private:
+ lua_State* L;
+ int index;
+ int stacktop;
+
+ public:
+ typedef stack_proxy reference_type;
+ typedef stack_proxy value_type;
+ typedef stack_proxy* pointer;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::size_t size_type;
+ typedef stack_iterator<stack_proxy, false> iterator;
+ typedef stack_iterator<stack_proxy, true> const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ variadic_args() = default;
+ variadic_args(lua_State* luastate, int stackindex = -1) : L(luastate), index(lua_absindex(luastate, stackindex)), stacktop(lua_gettop(luastate)) {
+ }
+ variadic_args(lua_State* luastate, int stackindex, int lastindex) : L(luastate), index(lua_absindex(luastate, stackindex)), stacktop(lastindex) {
+ }
+ variadic_args(const variadic_args&) = default;
+ variadic_args& operator=(const variadic_args&) = default;
+ variadic_args(variadic_args&& o) : L(o.L), index(o.index), stacktop(o.stacktop) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but will be thorough
+ o.L = nullptr;
+ o.index = 0;
+ o.stacktop = 0;
+ }
+ variadic_args& operator=(variadic_args&& o) {
+ L = o.L;
+ index = o.index;
+ stacktop = o.stacktop;
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but will be thorough
+ o.L = nullptr;
+ o.index = 0;
+ o.stacktop = 0;
+ return *this;
+ }
+
+ iterator begin() {
+ return iterator(L, index, stacktop + 1);
+ }
+ iterator end() {
+ return iterator(L, stacktop + 1, stacktop + 1);
+ }
+ const_iterator begin() const {
+ return const_iterator(L, index, stacktop + 1);
+ }
+ const_iterator end() const {
+ return const_iterator(L, stacktop + 1, stacktop + 1);
+ }
+ const_iterator cbegin() const {
+ return begin();
+ }
+ const_iterator cend() const {
+ return end();
+ }
+
+ reverse_iterator rbegin() {
+ return std::reverse_iterator<iterator>(begin());
+ }
+ reverse_iterator rend() {
+ return std::reverse_iterator<iterator>(end());
+ }
+ const_reverse_iterator rbegin() const {
+ return std::reverse_iterator<const_iterator>(begin());
+ }
+ const_reverse_iterator rend() const {
+ return std::reverse_iterator<const_iterator>(end());
+ }
+ const_reverse_iterator crbegin() const {
+ return std::reverse_iterator<const_iterator>(cbegin());
+ }
+ const_reverse_iterator crend() const {
+ return std::reverse_iterator<const_iterator>(cend());
+ }
+
+ int push() const {
+ return push(L);
+ }
+
+ int push(lua_State* target) const {
+ int pushcount = 0;
+ for (int i = index; i <= stacktop; ++i) {
+ lua_pushvalue(L, i);
+ pushcount += 1;
+ }
+ if (target != L) {
+ lua_xmove(L, target, pushcount);
+ }
+ return pushcount;
+ }
+
+ template <typename T>
+ decltype(auto) get(difference_type index_offset = 0) const {
+ return stack::get<T>(L, index + static_cast<int>(index_offset));
+ }
+
+ type get_type(difference_type index_offset = 0) const noexcept {
+ return type_of(L, index + static_cast<int>(index_offset));
+ }
+
+ stack_proxy operator[](difference_type index_offset) const {
+ return stack_proxy(L, index + static_cast<int>(index_offset));
+ }
+
+ lua_State* lua_state() const {
+ return L;
+ };
+ int stack_index() const {
+ return index;
+ };
+ int leftover_count() const {
+ return stacktop - (index - 1);
+ }
+ std::size_t size() const {
+ return static_cast<std::size_t>(leftover_count());
+ }
+ int top() const {
+ return stacktop;
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_getter<variadic_args> {
+ static variadic_args get(lua_State* L, int index, record& tracking) {
+ tracking.last = 0;
+ return variadic_args(L, index);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<variadic_args> {
+ static int push(lua_State* L, const variadic_args& ref) {
+ return ref.push(L);
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/variadic_args.hpp
+
+// beginning of sol/variadic_results.hpp
+
+// beginning of sol/as_returns.hpp
+
+namespace sol {
+ template <typename T>
+ struct as_returns_t : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+ using base_t::value;
+ };
+
+ template <typename Source>
+ auto as_returns(Source&& source) {
+ return as_returns_t<std::decay_t<Source>> { std::forward<Source>(source) };
+ }
+
+ namespace stack {
+ template <typename T>
+ struct unqualified_pusher<as_returns_t<T>> {
+ int push(lua_State* L, const as_returns_t<T>& e) {
+ auto& src = detail::unwrap(e.value());
+ int p = 0;
+ for (const auto& i : src) {
+ p += stack::push(L, i);
+ }
+ return p;
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/as_returns.hpp
+
+#include <vector>
+
+namespace sol {
+
+ template <typename Al = typename std::allocator<object>>
+ struct basic_variadic_results : public std::vector<object, Al> {
+ private:
+ using base_t = std::vector<object, Al>;
+
+ public:
+ basic_variadic_results() : base_t() {
+ }
+
+ basic_variadic_results(unsafe_function_result fr) : base_t() {
+ this->reserve(fr.return_count());
+ this->insert(this->cend(), fr.begin(), fr.end());
+ }
+
+ basic_variadic_results(protected_function_result fr) : base_t() {
+ this->reserve(fr.return_count());
+ this->insert(this->cend(), fr.begin(), fr.end());
+ }
+
+ template <typename Arg0, typename... Args,
+ meta::disable_any<std::is_same<meta::unqualified_t<Arg0>, basic_variadic_results>, std::is_same<meta::unqualified_t<Arg0>, function_result>,
+ std::is_same<meta::unqualified_t<Arg0>, protected_function_result>> = meta::enabler>
+ basic_variadic_results(Arg0&& arg0, Args&&... args) : base_t(std::forward<Arg0>(arg0), std::forward<Args>(args)...) {
+ }
+
+ basic_variadic_results(const basic_variadic_results&) = default;
+ basic_variadic_results(basic_variadic_results&&) = default;
+ };
+
+ struct variadic_results : public basic_variadic_results<> {
+ private:
+ using base_t = basic_variadic_results<>;
+
+ public:
+ using base_t::base_t;
+ };
+
+ template <typename Al>
+ struct is_container<basic_variadic_results<Al>> : std::false_type { };
+
+ template <>
+ struct is_container<variadic_results> : std::false_type { };
+
+ namespace stack {
+ template <typename Al>
+ struct unqualified_pusher<basic_variadic_results<Al>> {
+ int push(lua_State* L, const basic_variadic_results<Al>& e) {
+ int p = 0;
+ for (const auto& i : e) {
+ p += stack::push(L, i);
+ }
+ return p;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<variadic_results> {
+ int push(lua_State* L, const variadic_results& r) {
+ using base_t = basic_variadic_results<>;
+ return stack::push(L, static_cast<const base_t&>(r));
+ }
+ };
+ } // namespace stack
+
+} // namespace sol
+
+// end of sol/variadic_results.hpp
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic pop
+#elif SOL_IS_ON(SOL_COMPILER_CLANG)
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+#pragma warning(pop)
+#endif // g++
+
+#if SOL_IS_ON(SOL_INSIDE_UNREAL_ENGINE)
+#undef check
+#pragma pop_macro("check")
+#endif // Unreal Engine 4 Bullshit
+
+#endif // SOL_HPP
+// end of sol/sol.hpp
+
+#endif // SOL_SINGLE_INCLUDE_SOL_HPP
diff --git a/src/libs/3rdparty/optional/LICENSE_1_0.txt b/src/libs/3rdparty/span/LICENSE_1_0.txt
index 36b7cd93cd..36b7cd93cd 100644
--- a/src/libs/3rdparty/optional/LICENSE_1_0.txt
+++ b/src/libs/3rdparty/span/LICENSE_1_0.txt
diff --git a/src/libs/3rdparty/span/README.md b/src/libs/3rdparty/span/README.md
new file mode 100644
index 0000000000..dcc95ab6ce
--- /dev/null
+++ b/src/libs/3rdparty/span/README.md
@@ -0,0 +1,557 @@
+<a id="top"></a>
+# span lite: A single-file header-only version of a C++20-like span for C++98, C++11 and later
+
+[![Language](https://img.shields.io/badge/C%2B%2B-98/11/14/17/20-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![License](https://img.shields.io/badge/license-BSL-blue.svg)](https://opensource.org/licenses/BSL-1.0) [![Build Status](https://github.com/martinmoene/span-lite/actions/workflows/ci.yml/badge.svg)](https://github.com/martinmoene/span-lite/actions/workflows/ci.yml) [![Build status](https://ci.appveyor.com/api/projects/status/1ha3wnxtam547m8p?svg=true)](https://ci.appveyor.com/project/martinmoene/span-lite) [![Version](https://badge.fury.io/gh/martinmoene%2Fspan-lite.svg)](https://github.com/martinmoene/span-lite/releases) [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://github.com/martinmoene/span-lite/blob/master/include/nonstd/span.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://conan.io/center/span-lite) [![Try it on wandbox](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/permlink/venR3Ko2Q4tlvcVk) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/htwpnb)
+
+**Contents**
+
+- [Example usage](#example-usage)
+- [In a nutshell](#in-a-nutshell)
+- [License](#license)
+- [Dependencies](#dependencies)
+- [Installation and use](#installation-and-use)
+- [Synopsis](#synopsis)
+- [Reported to work with](#reported-to-work-with)
+- [Building the tests](#building-the-tests)
+- [Other implementations of span](#other-implementations-of-span)
+- [Notes and references](#notes-and-references)
+- [Appendix](#appendix)
+
+## Example usage
+
+```cpp
+#include "nonstd/span.hpp"
+#include <array>
+#include <vector>
+#include <iostream>
+
+std::ptrdiff_t size( nonstd::span<const int> spn )
+{
+ return spn.size();
+}
+
+int main()
+{
+ int arr[] = { 1, };
+
+ std::cout <<
+ "C-array:" << size( arr ) <<
+ " array:" << size( std::array <int, 2>{ 1, 2, } ) <<
+ " vector:" << size( std::vector<int >{ 1, 2, 3, } );
+}
+```
+
+### Compile and run
+
+```bash
+prompt> g++ -std=c++11 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe
+C-array:1 array:2 vector:3
+```
+
+## In a nutshell
+
+**span lite** is a single-file header-only library to provide a bounds-safe view for sequences of objects. The library provides a [C++20-like span](http://en.cppreference.com/w/cpp/container/span) for use with C++98 and later. If available, `std::span` is used, unless [configured otherwise](#configuration). *span-lite* can detect the presence of [*byte-lite*](https://github.com/martinmoene/byte-lite) and if present, it provides `as_bytes()` and `as_writable_bytes()` also for C++14 and earlier.
+
+**Features and properties of span lite** are ease of installation (single header), freedom of dependencies other than the standard library. To compensate for the class template argument deduction that is missing from pre-C++17 compilers, `nonstd::span` can provide `make_span` functions. See [configuration](#configuration).
+
+## License
+
+*span lite* is distributed under the [Boost Software License](https://github.com/martinmoene/span-lite/blob/master/LICENSE.txt).
+
+## Dependencies
+
+*span lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header).
+
+## Installation and use
+
+*span lite* is a single-file header-only library. Put `span.hpp` in the [include](include) folder directly into the project source tree or somewhere reachable from your project.
+
+## Synopsis
+
+**Contents**
+[Documentation of `std::span`](#documentation-of-stdspan)
+[Later additions](#later-additions)
+[Non-standard extensions](#non-standard-extensions)
+[Configuration](#configuration)
+
+## Documentation of `std::span`
+
+Depending on the compiler and C++-standard used, `nonstd::span` behaves less or more like `std::span`. To get an idea of the capabilities of `nonstd::span` with your configuration, look at the output of the [tests](test/span.t.cpp), issuing `span-main.t --pass @`. For `std::span`, see its [documentation at cppreference](http://en.cppreference.com/w/cpp/container/span).
+
+## Later additions
+
+### `back()` and `front()`
+
+*span lite* can provide `back()` and `front()` member functions for element access. See the table below and section [configuration](#configuration).
+
+## Non-standard extensions
+
+### Construct from std::initializer_list (p2447)
+
+*span lite* can provide construction from a std::initializer_list<> as a constant set of values as proposed in [p2447](https://wg21.link/p2447). See the table below and section [configuration](#configuration).
+
+### Construct from container
+
+To construct a span from a container with compilers that cannot constrain such a single-parameter constructor to containers, *span lite* provides a constructor that takes an additional parameter of type `with_container_t`. Use `with_container` as value for this parameter. See the table below and section [configuration](#configuration).
+
+### Construct from `std::array` with const data
+
+*span lite* can provide construction of a span from a `std::array` with const data. See the table below and section [configuration](#configuration).
+
+### `operator()`
+
+*span lite* can provide member function call `operator()` for element access. It is equivalent to `operator[]` and has been marked `[[deprecated]]`. Its main purpose is to provide a migration path.
+
+### `at()`
+
+*span lite* can provide member function `at()` for element access. Unless exceptions have been disabled, `at()` throws std::out_of_range if the index falls outside the span. With exceptions disabled, `at(index_t)` delegates bounds checking to `operator[](index_t)`. See the table below and sections [configuration](#configuration) and [disable exceptions](#disable-exceptions).
+
+### `swap()`
+
+*span lite* can provide a `swap()`member function. See the table below and section [configuration](#configuration).
+
+### `operator==()` and other comparison functions
+
+*span lite* can provide functions to compare the content of two spans. However, C++20's span will not provide comparison and _span lite_ will omit comparison at default in the near future. See the table below and section [configuration](#configuration). See also [Revisiting Regular Types](#regtyp).
+
+### `same()`
+
+*span lite* can provide function `same()` to determine if two spans refer as identical spans to the same data via the same type. If `same()` is enabled, `operator==()` incorporates it in its comparison. See the table below and section [configuration](#configuration).
+
+### `first()`, `last()` and `subspan()`
+
+*span lite* can provide functions `first()`, `last()` and `subspan()` to avoid having to use the *dot template* syntax when the span is a dependent type. See the table below and section [configuration](#configuration).
+
+### `make_span()`
+
+*span lite* can provide `make_span()` creator functions to compensate for the class template argument deduction that is missing from pre-C++17 compilers. See the table below and section [configuration](#configuration).
+
+### `byte_span()`
+
+*span lite* can provide `byte_span()` creator functions to represent an object as a span of bytes. This requires the C++17 type `std::byte` to be available. See the table below and section [configuration](#configuration).
+
+| Kind | std | Function or method |
+|--------------------|------|--------------------|
+| **Macro** |&nbsp;| macro **`span_FEATURE_WITH_INITIALIZER_LIST_P2447`** |
+| **Constructor**<br>&nbsp; |&nbsp;| constexpr explicit **span**( std::initializer_list&lt;value_type> il ) noexcept<br>explicit for non-dynamic extent |
+| &nbsp; |&nbsp;| &nbsp; |
+| **Macro**<br>&nbsp;|&nbsp;| macro **`span_FEATURE_WITH_CONTAINER`**<br>macro **`span_FEATURE_WITH_CONTAINER_TO_STD`** |
+| **Types** |&nbsp;| **with_container_t** type to disambiguate below constructors |
+| **Objects** |&nbsp;| **with_container** value to disambiguate below constructors |
+| **Constructors** |&nbsp;| macro **`span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE`**|
+| &nbsp; |&nbsp;| template&lt;class Container><br>constexpr **span**(with_container_t, Container & cont) |
+| &nbsp; |&nbsp;| template&lt;class Container><br>constexpr **span**(with_container_t, Container const & cont) |
+| &nbsp; |&nbsp;| &nbsp; |
+| **Methods** |&nbsp;| macro **`span_FEATURE_MEMBER_CALL_OPERATOR`** |
+| &nbsp; |&nbsp;| constexpr reference **operator()**(index_t idx) const<br>Equivalent to **operator[]**(), marked `[[deprecated]]` |
+| &nbsp; |&nbsp;| &nbsp; |
+| **Methods** |&nbsp;| macro **`span_FEATURE_MEMBER_AT`** |
+| &nbsp; |&nbsp;| constexpr reference **at**(index_t idx) const<br>May throw std::out_of_range exception |
+| &nbsp; |&nbsp;| &nbsp; |
+| **Methods** |&nbsp;| macro **`span_FEATURE_MEMBER_BACK_FRONT`** (on since v0.5.0) |
+| &nbsp; |&nbsp;| constexpr reference **back()** const noexcept |
+| &nbsp; |&nbsp;| constexpr reference **front()** const noexcept |
+| &nbsp; |&nbsp;| &nbsp; |
+| **Method** |&nbsp;| macro **`span_FEATURE_MEMBER_SWAP`** |
+| &nbsp; |&nbsp;| constexpr void **swap**(span & other) noexcept |
+| &nbsp; |&nbsp;| &nbsp; |
+| **Free functions** |&nbsp;| macro **`span_FEATURE_COMPARISON`** |
+|<br><br>== != < > <= >= |&nbsp;| template&lt;class T1, index_t E1, class T2, index_t E2><br>constexpr bool<br>**operator==**( span<T1,E1> const & l, span<T2,E2> const & r) noexcept |
+| &nbsp; |&nbsp;| &nbsp; |
+| **Free function** |&nbsp;| macro **`span_FEATURE_SAME`** |
+| &nbsp; |&nbsp;| template&lt;class T1, index_t E1, class T2, index_t E2><br>constexpr bool<br>**same**( span<T1,E1> const & l, span<T2,E2> const & r) noexcept |
+| &nbsp; |&nbsp;| &nbsp; |
+| **Free functions**<br>&nbsp;<br>&nbsp; |&nbsp;| macros **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB`**,<br>**`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN`**,<br>**`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER`** |
+| &nbsp; |&nbsp;| &nbsp; |
+| **Free functions** |&nbsp;| macro **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN`** |
+| &nbsp; | &nbsp; | template&lt;extent_t Count, class T, extent_t Extent><br>constexpr span&lt;T,Count><br>**first**(span&lt;T,Extent> spn) |
+| &nbsp; | &nbsp; | template&lt;class T, extent_t Extent ><br>constexpr span&lt;T><br>**first**(span&lt;T,Extent> spn, size_t count) |
+| &nbsp; | &nbsp; | template&lt;extent_t Count, class T, extent_t Extent><br>constexpr span&lt;T,Count><br>**last**(span&lt;T,Extent> spn) |
+| &nbsp; | &nbsp; | template&lt;class T, extent_t Extent ><br>constexpr span&lt;T><br>**last**(span&lt;T,Extent> spn, size_t count) |
+| &nbsp; | &nbsp; | template&lt;size_t Offset, extent_t Count, class T, extent_t Extent><br>constexpr span&lt;T, Count><br>**subspan**(span&lt;T, Extent> spn) |
+| &nbsp; | &nbsp; | template&lt;class T, extent_t Extent><br>constexpr span&lt;T><br>**subspan**( span&lt;T, Extent> spn, size_t offset, extent_t count = dynamic_extent) |
+| &nbsp; |&nbsp;| &nbsp; |
+| **Free functions** |&nbsp;| macro **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER`** |
+| &nbsp; | >= C++11 | template&lt;extent_t Count, class T><br>constexpr auto<br>**first**(T & t) ->... |
+| &nbsp; | >= C++11 | template&lt;class T><br>constexpr auto<br>**first**(T & t, index_t count) ->... |
+| &nbsp; | >= C++11 | template&lt;extent_t Count, class T><br>constexpr auto<br>**last**(T & t) ->... |
+| &nbsp; | >= C++11 | template&lt;class T><br>constexpr auto<br>**last**(T & t, extent_t count) ->... |
+| &nbsp; | >= C++11 | template&lt;index_t Offset, extent_t Count = dynamic_extent, class T><br>constexpr auto<br>**subspan**(T & t) ->... |
+| &nbsp; | >= C++11 | template&lt;class T><br>constexpr auto<br>**subspan**(T & t, index_t offset, extent_t count = dynamic_extent) ->... |
+| &nbsp; | &nbsp; | &nbsp; |
+| **Free functions**<br>&nbsp; |&nbsp;| macro **`span_FEATURE_MAKE_SPAN`**<br>macro **`span_FEATURE_MAKE_SPAN_TO_STD`** |
+| &nbsp; | &nbsp; | template&lt;class T><br>constexpr span&lt;T><br>**make_span**(T \* first, T \* last) noexcept |
+| &nbsp; | &nbsp; | template&lt;class T><br>constexpr span&lt;T><br>**make_span**(T \* ptr, index_t count) noexcept |
+| &nbsp; | &nbsp; | template&lt;class T, size_t N><br>constexpr span&lt;T,N><br>**make_span**(T (&arr)[N]) noexcept |
+| &nbsp; | >= C++11 | template&lt;class T, size_t N><br>constexpr span&lt;T,N><br>**make_span**(std::array&lt;T,N> & arr) noexcept |
+| &nbsp; | >= C++11 | template&lt;class T, size_t N><br>constexpr span&lt;const T,N><br>**make_span**(std::array&lt;T,N > const & arr) noexcept |
+| &nbsp; | >= C++11 | template&lt;class T><br>constexpr span&lt;T><br>**make_span**(std::initializer_list&lt;T> il) noexcept |
+| &nbsp; | >= C++11 | template&lt;class Container><br>constexpr auto<br>**make_span**(Container & cont) -><br>&emsp;span&lt;typename Container::value_type> noexcept |
+| &nbsp; | >= C++11 | template&lt;class Container><br>constexpr auto<br>**make_span**(Container const & cont) -><br>&emsp;span&lt;const typename Container::value_type> noexcept |
+| &nbsp; | &nbsp; | template&lt;class Container><br>span&lt;typename Container::value_type><br>**make_span**( with_container_t, Container & cont ) |
+| &nbsp; | &nbsp; | template&lt;class Container><br>span&lt;const typename Container::value_type><br>**make_span**( with_container_t, Container const & cont ) |
+| &nbsp; | < C++11 | template&lt;class T, Allocator><br>span&lt;T><br>**make_span**(std::vector&lt;T, Allocator> & cont) |
+| &nbsp; | < C++11 | template&lt;class T, Allocator><br>span&lt;const T><br>**make_span**(std::vector&lt;T, Allocator> const & cont) |
+| &nbsp; | &nbsp; | &nbsp; |
+| **Free functions** |&nbsp;| macro **`span_FEATURE_BYTE_SPAN`** |
+| &nbsp; | >= C++11 | template&lt;class T><br>span&lt;T, sizeof(T)><br>**byte_span**(T & t) |
+| &nbsp; | >= C++11 | template&lt;class T><br>span&lt;const T, sizeof(T)><br>**byte_span**(T const & t) |
+
+## Configuration
+
+### Tweak header
+
+If the compiler supports [`__has_include()`](https://en.cppreference.com/w/cpp/preprocessor/include), *span lite* supports the [tweak header](https://vector-of-bool.github.io/2020/10/04/lib-configuration.html) mechanism. Provide your *tweak header* as `nonstd/span.tweak.hpp` in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like `#define span_CONFIG_NO_EXCEPTIONS 1`.
+
+### Standard selection macro
+
+\-D<b>span\_CPLUSPLUS</b>=199711L
+Define this macro to override the auto-detection of the supported C++ standard, if your compiler does not set the `__cplusplus` macro correctly.
+
+### Select `std::span` or `nonstd::span`
+
+At default, *span lite* uses `std::span` if it is available and lets you use it via namespace `nonstd`. You can however override this default and explicitly request to use `std::span` or span lite's `nonstd::span` as `nonstd::span` via the following macros.
+
+-D<b>span\_CONFIG\_SELECT\_SPAN</b>=span_SPAN_DEFAULT
+Define this to `span_SPAN_STD` to select `std::span` as `nonstd::span`. Define this to `span_SPAN_NONSTD` to select `nonstd::span` as `nonstd::span`. Default is undefined, which has the same effect as defining to `span_SPAN_DEFAULT`.
+
+### Select extent type
+
+-D<b>span_CONFIG_EXTENT_TYPE</b>=std::size_t
+Define this to `std::ptrdiff_t` to use the signed type. The default is `std::size_t`, as in C++20 (since v0.7.0).
+
+### Select size type
+
+-D<b>span_CONFIG_SIZE_TYPE</b>=std::size_t
+Define this to `std::ptrdiff_t` to use the signed type. The default is `std::size_t`, as in C++20 (since v0.7.0). Note `span_CONFIG_SIZE_TYPE` replaces `span_CONFIG_INDEX_TYPE` which is deprecated.
+
+### Disable exceptions
+
+-D<b>span_CONFIG_NO_EXCEPTIONS</b>=0
+Define this to 1 if you want to compile without exceptions. If not defined, the header tries and detect if exceptions have been disabled (e.g. via `-fno-exceptions`). Disabling exceptions will force contract violation to use termination, see [contract violation macros](#contract-violation-response-macros). Default is undefined.
+
+### Provide construction from std::initializer_list (p2447)
+
+-D<b>span_FEATURE_WITH_INITIALIZER_LIST_P2447</b>=0
+Define this to 1 to enable constructing a span from a std::initializer_list<> as a constant set of values. See proposal [p2447](https://wg21.link/p2447). Default is undefined.
+
+### Provide construction using `with_container_t`
+
+-D<b>span_FEATURE_WITH_CONTAINER</b>=0
+Define this to 1 to enable constructing a span using `with_container_t`. Note that `span_FEATURE_WITH_CONTAINER` takes precedence over `span_FEATURE_WITH_CONTAINER_TO_STD`. Default is undefined.
+
+-D<b>span_FEATURE_WITH_CONTAINER_TO_STD</b>=*n*
+Define this to the highest C++ language version for which to enable constructing a span using `with_container_t`, like 98, 03, 11, 14, 17, 20. You can use 99 for inclusion with any standard, but prefer to use `span_FEATURE_WITH_CONTAINER` for this. Note that `span_FEATURE_WITH_CONTAINER` takes precedence over `span_FEATURE_WITH_CONTAINER_TO_STD`. Default is undefined.
+
+### Provide construction from `std::array` with const data
+
+-D<b>span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE</b>=0
+Define this to 1 to enable constructing a span from a std::array with const data. Default is undefined.
+
+### Provide `operator()` member function
+
+-D<b>span_FEATURE_MEMBER_CALL_OPERATOR</b>=0
+Define this to 1 to provide member function `operator()`for element access. It is equivalent to `operator[]` and has been marked `[[deprecated]]`. Its main purpose is to provide a migration path. Default is undefined.
+
+### Provide `at()` member function
+
+-D<b>span_FEATURE_MEMBER_AT</b>=0
+Define this to 1 to provide member function `at()`. Define this to 2 to include index and size in message of std::out_of_range exception. Default is undefined.
+
+### Provide `back()` and `front()` member functions
+
+-D<b>span_FEATURE_MEMBER_BACK_FRONT</b>=1 _(on since v0.5.0)_
+Define this to 0 to omit member functions `back()` and `front()`. Default is undefined.
+
+### Provide `swap()` member function
+
+-D<b>span_FEATURE_MEMBER_SWAP</b>=0
+Define this to 1 to provide member function `swap()`. Default is undefined.
+
+### Provide `operator==()` and other comparison functions
+
+-D<b>span_FEATURE_COMPARISON</b>=0
+Define this to 1 to include the comparison functions to compare the content of two spans. C++20's span does not provide comparison and _span lite_ omits comparison from v0.7.0. Default is undefined.
+
+### Provide `same()` function
+
+-D<b>span_FEATURE_SAME</b>=0
+Define this to 1 to provide function `same()` to test if two spans refer as identical spans to the same data via the same type. If `same()` is enabled, `operator==()` incorporates it in its comparison. Default is undefined.
+
+### Provide `first()`, `last()` and `subspan()` functions
+
+-D<b>span_FEATURE_NON_MEMBER_FIRST_LAST_SUB</b>=0
+Define this to 1 to enable both `span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN` and `span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER`. Default is undefined.
+
+-D<b>span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN</b>=0
+Define this to 1 to provide functions `first()`, `last()` and `subspan()` that take a `span<>` (work with C++98). This implies `span_FEATURE_MAKE_SPAN` to provide functions `make_span()` that are required for this feature. Default is undefined.
+
+-D<b>span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER</b>=0
+Define this to 1 to provide functions `first()`, `last()` and `subspan()` that take a compatible container (requires C++11). This implies `span_FEATURE_MAKE_SPAN` to provide functions `make_span()` that are required for this feature. Default is undefined.
+
+### Provide `make_span()` functions
+
+-D<b>span_FEATURE_MAKE_SPAN</b>=0
+Define this to 1 to provide creator functions `nonstd::make_span()`. This feature is implied by using `span_FEATURE_NON_MEMBER_FIRST_LAST_SUB=1`. Note that `span_FEATURE_MAKE_SPAN` takes precedence over `span_FEATURE_MAKE_SPAN_TO_STD`. Default is undefined.
+
+-D<b>span_FEATURE_MAKE_SPAN_TO_STD</b>=*n*
+Define this to the highest C++ language version for which to provide creator functions `nonstd::make_span()`, like 98, 03, 11, 14, 17, 20. You can use 99 for inclusion with any standard, but prefer to use `span_FEATURE_MAKE_SPAN` for this. Note that `span_FEATURE_MAKE_SPAN` takes precedence over `span_FEATURE_MAKE_SPAN_TO_STD`. Default is undefined.
+
+### Provide `byte_span()` functions
+
+-D<b>span_FEATURE_BYTE_SPAN</b>=0
+Define this to 1 to provide creator functions `nonstd::byte_span()`. Default is undefined.
+
+### Contract violation response macros
+
+*span-lite* provides contract violation response control as suggested in proposal [N4415](http://wg21.link/n4415).
+
+\-D<b>span\_CONFIG\_CONTRACT\_LEVEL\_ON</b> (*default*)
+Define this macro to include both `span_EXPECTS` and `span_ENSURES` in the code. This is the default case.
+
+\-D<b>span\_CONFIG\_CONTRACT\_LEVEL\_OFF</b>
+Define this macro to exclude both `span_EXPECTS` and `span_ENSURES` from the code.
+
+\-D<b>span\_CONFIG_CONTRACT\_LEVEL\_EXPECTS\_ONLY</b>
+Define this macro to include `span_EXPECTS` in the code and exclude `span_ENSURES` from the code.
+
+\-D<b>span\_CONFIG\_CONTRACT\_LEVEL\_ENSURES\_ONLY</b>
+Define this macro to exclude `span_EXPECTS` from the code and include `span_ENSURES` in the code.
+
+\-D<b>span\_CONFIG\_CONTRACT\_VIOLATION\_TERMINATES</b> (*default*)
+Define this macro to call `std::terminate()` on a contract violation in `span_EXPECTS`, `span_ENSURES`. This is the default case.
+
+\-D<b>span\_CONFIG\_CONTRACT\_VIOLATION\_THROWS</b>
+Define this macro to throw an exception of implementation-defined type that is derived from `std::runtime_exception` instead of calling `std::terminate()` on a contract violation in `span_EXPECTS` and `span_ENSURES`. See also [disable exceptions](#disable-exceptions).
+
+Reported to work with
+--------------------
+The table below mentions the compiler versions *span lite* is reported to work with.
+
+OS | Compiler | Where | Versions |
+------------:|:-----------|:--------|:---------|
+**GNU/Linux**| Clang/LLVM | Travis | 3.5.0, 3.6.2, 3.7.1, 3.8.0, 3.9.1, 4.0.1 |
+ &nbsp; | GCC | Travis | 5.5.0, 6.4.0, 7.3.0 |
+**OS X** | ? | Local | ? |
+**Windows** | Clang/LLVM | Local | 6.0.0 |
+&nbsp; | GCC | Local | 7.2.0 |
+&nbsp; | Visual C++<br>(Visual Studio)| Local | 8 (2005), 10 (2010), 11 (2012),<br>12 (2013), 14 (2015), 15 (2017) |
+&nbsp; | Visual C++<br>(Visual Studio)| AppVeyor | 10 (2010), 11 (2012),<br>12 (2013), 14 (2015), 15 (2017) |
+
+## Building the tests
+
+To build the tests you need:
+
+- [CMake](http://cmake.org), version 3.0 or later to be installed and in your PATH.
+- A [suitable compiler](#reported-to-work-with).
+
+The [*lest* test framework](https://github.com/martinmoene/lest) is included in the [test folder](test).
+
+The following steps assume that the [*span lite* source code](https://github.com/martinmoene/span-lite) has been cloned into a directory named `./span-lite`.
+
+1. Create a directory for the build outputs.
+
+ cd ./span-lite
+ md build && cd build
+
+2. Configure CMake to use the compiler of your choice (run `cmake --help` for a list).
+
+ cmake -G "Unix Makefiles" -DSPAN_LITE_OPT_BUILD_TESTS=ON ..
+
+3. Optional. You can control above configuration through the following options:
+
+ `-DSPAN_LITE_OPT_BUILD_TESTS=ON`: build the tests for span, default off
+ `-DSPAN_LITE_OPT_BUILD_EXAMPLES=OFF`: build the examples, default off
+
+4. Build the test suite.
+
+ cmake --build .
+
+5. Run the test suite.
+
+ ctest -V
+
+All tests should pass, indicating your platform is supported and you are ready to use *span lite*.
+
+## Other implementations of span
+
+- *gsl-lite* [span](https://github.com/martinmoene/gsl-lite/blob/73c4f16f2b35fc174fc2f09d44d5ab13e5c638c3/include/gsl/gsl-lite.hpp#L1221).
+- Microsoft GSL [span](https://github.com/Microsoft/GSL/blob/master/include/gsl/span).
+- Google Abseil [span](https://github.com/abseil/abseil-cpp/blob/master/absl/types/span.h).
+- Marshall Clow's [libc++ span snippet](https://github.com/mclow/snippets/blob/master/span.cpp).
+- Tristan Brindle's [Implementation of C++20's std::span for older compilers](https://github.com/tcbrindle/span).
+- [Search _span c++_ on GitHub](https://github.com/search?l=C%2B%2B&q=span+c%2B%2B&type=Repositories&utf8=%E2%9C%93).
+
+## Notes and references
+
+*Interface and specification*
+
+- [span on cppreference](https://en.cppreference.com/w/cpp/container/span).
+- [p0122 - C++20 Proposal](http://wg21.link/p0122).
+- [span in C++20 Working Draft](http://eel.is/c++draft/views).
+
+*Presentations*
+
+- TBD
+
+*Proposals*
+
+- [p0122 - span: bounds-safe views for sequences of objects](http://wg21.link/p0122).
+- [p1024 - Usability Enhancements for std::span](http://wg21.link/p1024).
+- [p1419 - A SFINAE-friendly trait to determine the extent of statically sized containers](http://wg21.link/p1419).
+- [p0805 - Comparing Containers](http://wg21.link/p0805).
+- [p1085 - Should Span be Regular?](http://wg21.link/p0805).
+- [p0091 - Template argument deduction for class templates](http://wg21.link/p0091).
+- [p0856 - Restrict Access Property for mdspan and span](http://wg21.link/p0856).
+- [p1428 - Subscripts and sizes should be signed](http://wg21.link/p1428).
+- [p1089 - Sizes Should Only span Unsigned](http://wg21.link/p1089).
+- [p1227 - Signed size() functions](http://wg21.link/p1227).
+- [p1872 - span should have size_type, not index_type](http://wg21.link/p1872).
+- [p2447 - std::span and the missing constructor](https://wg21.link/p2447).
+- [lwg 3101 - span's Container constructors need another constraint](https://cplusplus.github.io/LWG/issue3101).
+- [Reddit - 2018-06 Rapperswil ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/8prqzm/2018_rapperswil_iso_c_committee_trip_report/)
+- [Reddit - 2018-11 San Diego ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/9vwvbz/2018_san_diego_iso_c_committee_trip_report_ranges/).
+- [Reddit - 2019-02 Kona ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/au0c4x/201902_kona_iso_c_committee_trip_report_c20/).
+- [Reddit - 2019-07 Cologne ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/cfk9de/201907_cologne_iso_c_committee_trip_report_the/)
+- [Reddit - 2019-11 Belfast ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/dtuov8/201911_belfast_iso_c_committee_trip_report/)
+- <a id="regtyp"></a>Titus Winters. [Revisiting Regular Types](https://abseil.io/blog/20180531-regular-types). Abseil Blog. 31 May 2018.
+
+## Appendix
+
+### A.1 Compile-time information
+
+The version of *span lite* is available via tag `[.version]`. The following tags are available for information on the compiler and on the C++ standard library used: `[.compiler]`, `[.stdc++]`, `[.stdlanguage]` and `[.stdlibrary]`.
+
+### A.2 Span lite test specification
+
+<details>
+<summary>click to expand</summary>
+<p>
+
+```Text
+span<>: Terminates construction from a nullptr and a non-zero size (C++11)
+span<>: Terminates construction from two pointers in the wrong order
+span<>: Terminates construction from a null pointer and a non-zero size
+span<>: Terminates creation of a sub span of the first n elements for n exceeding the span
+span<>: Terminates creation of a sub span of the last n elements for n exceeding the span
+span<>: Terminates creation of a sub span outside the span
+span<>: Terminates access outside the span
+span<>: Throws on access outside the span via at(): std::out_of_range [span_FEATURE_MEMBER_AT>0][span_CONFIG_NO_EXCEPTIONS=0]
+span<>: Termination throws std::logic_error-derived exception [span_CONFIG_CONTRACT_VIOLATION_THROWS=1]
+span<>: Allows to default-construct
+span<>: Allows to construct from a nullptr and a zero size (C++11)
+span<>: Allows to construct from two pointers
+span<>: Allows to construct from two iterators
+span<>: Allows to construct from two iterators - empty range
+span<>: Allows to construct from two iterators - move-only element
+span<>: Allows to construct from an iterator and a size
+span<>: Allows to construct from an iterator and a size - empty range
+span<>: Allows to construct from an iterator and a size - move-only element
+span<>: Allows to construct from two pointers to const
+span<>: Allows to construct from a non-null pointer and a size
+span<>: Allows to construct from a non-null pointer to const and a size
+span<>: Allows to construct from a temporary pointer and a size
+span<>: Allows to construct from a temporary pointer to const and a size
+span<>: Allows to construct from any pointer and a zero size (C++98)
+span<>: Allows to construct from a pointer and a size via a deduction guide (C++17)
+span<>: Allows to construct from an iterator and a size via a deduction guide (C++17)
+span<>: Allows to construct from two iterators via a deduction guide (C++17)
+span<>: Allows to construct from a C-array
+span<>: Allows to construct from a C-array via a deduction guide (C++17)
+span<>: Allows to construct from a const C-array
+span<>: Allows to construct from a C-array with size via decay to pointer (potentially dangerous)
+span<>: Allows to construct from a const C-array with size via decay to pointer (potentially dangerous)
+span<>: Allows to construct from a std::initializer_list<> (C++11)
+span<>: Allows to construct from a std::initializer_list<> as a constant set of values (C++11, p2447)
+span<>: Allows to construct from a std::array<> (C++11)
+span<>: Allows to construct from a std::array via a deduction guide (C++17)
+span<>: Allows to construct from a std::array<> with const data (C++11, span_FEATURE_CONSTR..._ELEMENT_TYPE=1)
+span<>: Allows to construct from an empty std::array<> (C++11)
+span<>: Allows to construct from a container (std::vector<>)
+span<>: Allows to construct from a container via a deduction guide (std::vector<>, C++17)
+span<>: Allows to tag-construct from a container (std::vector<>)
+span<>: Allows to tag-construct from a const container (std::vector<>)
+span<>: Allows to copy-construct from another span of the same type
+span<>: Allows to copy-construct from another span of a compatible type
+span<>: Allows to copy-construct from a temporary span of the same type (C++11)
+span<>: Allows to copy-assign from another span of the same type
+span<>: Allows to copy-assign from a temporary span of the same type (C++11)
+span<>: Allows to create a sub span of the first n elements
+span<>: Allows to create a sub span of the last n elements
+span<>: Allows to create a sub span starting at a given offset
+span<>: Allows to create a sub span starting at a given offset with a given length
+span<>: Allows to observe an element via array indexing
+span<>: Allows to observe an element via call indexing
+span<>: Allows to observe an element via at() [span_FEATURE_MEMBER_AT>0]
+span<>: Allows to observe an element via data()
+span<>: Allows to observe the first element via front() [span_FEATURE_MEMBER_BACK_FRONT=1]
+span<>: Allows to observe the last element via back() [span_FEATURE_MEMBER_BACK_FRONT=1]
+span<>: Allows to change an element via array indexing
+span<>: Allows to change an element via call indexing
+span<>: Allows to change an element via at() [span_FEATURE_MEMBER_AT>0]
+span<>: Allows to change an element via data()
+span<>: Allows to change the first element via front() [span_FEATURE_MEMBER_BACK_FRONT=1]
+span<>: Allows to change the last element via back() [span_FEATURE_MEMBER_BACK_FRONT=1]
+span<>: Allows to swap with another span [span_FEATURE_MEMBER_SWAP=1]
+span<>: Allows forward iteration
+span<>: Allows const forward iteration
+span<>: Allows reverse iteration
+span<>: Allows const reverse iteration
+span<>: Allows to identify if a span is the same as another span [span_FEATURE_SAME=1]
+span<>: Allows to compare equal to another span of the same type [span_FEATURE_COMPARISON=1]
+span<>: Allows to compare unequal to another span of the same type [span_FEATURE_COMPARISON=1]
+span<>: Allows to compare less than another span of the same type [span_FEATURE_COMPARISON=1]
+span<>: Allows to compare less than or equal to another span of the same type [span_FEATURE_COMPARISON=1]
+span<>: Allows to compare greater than another span of the same type [span_FEATURE_COMPARISON=1]
+span<>: Allows to compare greater than or equal to another span of the same type [span_FEATURE_COMPARISON=1]
+span<>: Allows to compare to another span of the same type and different cv-ness [span_FEATURE_SAME=0]
+span<>: Allows to compare empty spans as equal [span_FEATURE_COMPARISON=1]
+span<>: Allows to test for empty span via empty(), empty case
+span<>: Allows to test for empty span via empty(), non-empty case
+span<>: Allows to obtain the number of elements via size()
+span<>: Allows to obtain the number of elements via ssize()
+span<>: Allows to obtain the number of bytes via size_bytes()
+span<>: Allows to view the elements as read-only bytes
+span<>: Allows to view and change the elements as writable bytes
+make_span() [span_FEATURE_MAKE_SPAN_TO_STD=99]
+make_span(): Allows building from two pointers
+make_span(): Allows building from two const pointers
+make_span(): Allows building from a non-null pointer and a size
+make_span(): Allows building from a non-null const pointer and a size
+make_span(): Allows building from a C-array
+make_span(): Allows building from a const C-array
+make_span(): Allows building from a std::initializer_list<> (C++11)
+make_span(): Allows building from a std::initializer_list<> as a constant set of values (C++11)
+make_span(): Allows building from a std::array<> (C++11)
+make_span(): Allows building from a const std::array<> (C++11)
+make_span(): Allows building from a container (std::vector<>)
+make_span(): Allows building from a const container (std::vector<>)
+make_span(): Allows building from a container (with_container_t, std::vector<>)
+make_span(): Allows building from a const container (with_container_t, std::vector<>)
+byte_span() [span_FEATURE_BYTE_SPAN=1]
+byte_span(): Allows building a span of std::byte from a single object (C++17, byte-lite)
+byte_span(): Allows building a span of const std::byte from a single const object (C++17, byte-lite)
+first(), last(), subspan() [span_FEATURE_NON_MEMBER_FIRST_LAST_SUB=1]
+first(): Allows to create a sub span of the first n elements (span, template parameter)
+first(): Allows to create a sub span of the first n elements (span, function parameter)
+first(): Allows to create a sub span of the first n elements (compatible container, template parameter)
+first(): Allows to create a sub span of the first n elements (compatible container, function parameter)
+last(): Allows to create a sub span of the last n elements (span, template parameter)
+last(): Allows to create a sub span of the last n elements (span, function parameter)
+last(): Allows to create a sub span of the last n elements (compatible container, template parameter)
+last(): Allows to create a sub span of the last n elements (compatible container, function parameter)
+subspan(): Allows to create a sub span starting at a given offset (span, template parameter)
+subspan(): Allows to create a sub span starting at a given offset (span, function parameter)
+subspan(): Allows to create a sub span starting at a given offset (compatible container, template parameter)
+subspan(): Allows to create a sub span starting at a given offset (compatible container, function parameter)
+size(): Allows to obtain the number of elements via size()
+ssize(): Allows to obtain the number of elements via ssize()
+tuple_size<>: Allows to obtain the number of elements via std::tuple_size<> (C++11)
+tuple_element<>: Allows to obtain an element via std::tuple_element<> (C++11)
+tuple_element<>: Allows to obtain an element via std::tuple_element_t<> (C++11)
+get<I>(spn): Allows to access an element via std::get<>()
+tweak header: reads tweak header if supported [tweak]
+```
+
+</p>
+</details>
diff --git a/src/libs/3rdparty/span/span.hpp b/src/libs/3rdparty/span/span.hpp
new file mode 100644
index 0000000000..fa6e51735f
--- /dev/null
+++ b/src/libs/3rdparty/span/span.hpp
@@ -0,0 +1,1947 @@
+//
+// span for C++98 and later.
+// Based on http://wg21.link/p0122r7
+// For more information see https://github.com/martinmoene/span-lite
+//
+// Copyright 2018-2021 Martin Moene
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef NONSTD_SPAN_HPP_INCLUDED
+#define NONSTD_SPAN_HPP_INCLUDED
+
+#define span_lite_MAJOR 0
+#define span_lite_MINOR 10
+#define span_lite_PATCH 3
+
+#define span_lite_VERSION span_STRINGIFY(span_lite_MAJOR) "." span_STRINGIFY(span_lite_MINOR) "." span_STRINGIFY(span_lite_PATCH)
+
+#define span_STRINGIFY( x ) span_STRINGIFY_( x )
+#define span_STRINGIFY_( x ) #x
+
+// span configuration:
+
+#define span_SPAN_DEFAULT 0
+#define span_SPAN_NONSTD 1
+#define span_SPAN_STD 2
+
+// tweak header support:
+
+#ifdef __has_include
+# if __has_include(<nonstd/span.tweak.hpp>)
+# include <nonstd/span.tweak.hpp>
+# endif
+#define span_HAVE_TWEAK_HEADER 1
+#else
+#define span_HAVE_TWEAK_HEADER 0
+//# pragma message("span.hpp: Note: Tweak header not supported.")
+#endif
+
+// span selection and configuration:
+
+#define span_HAVE( feature ) ( span_HAVE_##feature )
+
+#ifndef span_CONFIG_SELECT_SPAN
+# define span_CONFIG_SELECT_SPAN ( span_HAVE_STD_SPAN ? span_SPAN_STD : span_SPAN_NONSTD )
+#endif
+
+#ifndef span_CONFIG_EXTENT_TYPE
+# define span_CONFIG_EXTENT_TYPE std::size_t
+#endif
+
+#ifndef span_CONFIG_SIZE_TYPE
+# define span_CONFIG_SIZE_TYPE std::size_t
+#endif
+
+#ifdef span_CONFIG_INDEX_TYPE
+# error `span_CONFIG_INDEX_TYPE` is deprecated since v0.7.0; it is replaced by `span_CONFIG_SIZE_TYPE`.
+#endif
+
+// span configuration (features):
+
+#ifndef span_FEATURE_WITH_INITIALIZER_LIST_P2447
+# define span_FEATURE_WITH_INITIALIZER_LIST_P2447 0
+#endif
+
+#ifndef span_FEATURE_WITH_CONTAINER
+#ifdef span_FEATURE_WITH_CONTAINER_TO_STD
+# define span_FEATURE_WITH_CONTAINER span_IN_STD( span_FEATURE_WITH_CONTAINER_TO_STD )
+#else
+# define span_FEATURE_WITH_CONTAINER 0
+# define span_FEATURE_WITH_CONTAINER_TO_STD 0
+#endif
+#endif
+
+#ifndef span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE
+# define span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE 0
+#endif
+
+#ifndef span_FEATURE_MEMBER_AT
+# define span_FEATURE_MEMBER_AT 0
+#endif
+
+#ifndef span_FEATURE_MEMBER_BACK_FRONT
+# define span_FEATURE_MEMBER_BACK_FRONT 1
+#endif
+
+#ifndef span_FEATURE_MEMBER_CALL_OPERATOR
+# define span_FEATURE_MEMBER_CALL_OPERATOR 0
+#endif
+
+#ifndef span_FEATURE_MEMBER_SWAP
+# define span_FEATURE_MEMBER_SWAP 0
+#endif
+
+#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB
+# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB 0
+#elif span_FEATURE_NON_MEMBER_FIRST_LAST_SUB
+# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN 1
+# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER 1
+#endif
+
+#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN
+# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN 0
+#endif
+
+#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER
+# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER 0
+#endif
+
+#ifndef span_FEATURE_COMPARISON
+# define span_FEATURE_COMPARISON 0 // Note: C++20 does not provide comparison
+#endif
+
+#ifndef span_FEATURE_SAME
+# define span_FEATURE_SAME 0
+#endif
+
+#if span_FEATURE_SAME && !span_FEATURE_COMPARISON
+# error `span_FEATURE_SAME` requires `span_FEATURE_COMPARISON`
+#endif
+
+#ifndef span_FEATURE_MAKE_SPAN
+#ifdef span_FEATURE_MAKE_SPAN_TO_STD
+# define span_FEATURE_MAKE_SPAN span_IN_STD( span_FEATURE_MAKE_SPAN_TO_STD )
+#else
+# define span_FEATURE_MAKE_SPAN 0
+# define span_FEATURE_MAKE_SPAN_TO_STD 0
+#endif
+#endif
+
+#ifndef span_FEATURE_BYTE_SPAN
+# define span_FEATURE_BYTE_SPAN 0
+#endif
+
+// Control presence of exception handling (try and auto discover):
+
+#ifndef span_CONFIG_NO_EXCEPTIONS
+# if defined(_MSC_VER)
+# include <cstddef> // for _HAS_EXCEPTIONS
+# endif
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
+# define span_CONFIG_NO_EXCEPTIONS 0
+# else
+# define span_CONFIG_NO_EXCEPTIONS 1
+# undef span_CONFIG_CONTRACT_VIOLATION_THROWS
+# undef span_CONFIG_CONTRACT_VIOLATION_TERMINATES
+# define span_CONFIG_CONTRACT_VIOLATION_THROWS 0
+# define span_CONFIG_CONTRACT_VIOLATION_TERMINATES 1
+# endif
+#endif
+
+// Control pre- and postcondition violation behaviour:
+
+#if defined( span_CONFIG_CONTRACT_LEVEL_ON )
+# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11
+#elif defined( span_CONFIG_CONTRACT_LEVEL_OFF )
+# define span_CONFIG_CONTRACT_LEVEL_MASK 0x00
+#elif defined( span_CONFIG_CONTRACT_LEVEL_EXPECTS_ONLY )
+# define span_CONFIG_CONTRACT_LEVEL_MASK 0x01
+#elif defined( span_CONFIG_CONTRACT_LEVEL_ENSURES_ONLY )
+# define span_CONFIG_CONTRACT_LEVEL_MASK 0x10
+#else
+# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11
+#endif
+
+#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS )
+# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V span_CONFIG_CONTRACT_VIOLATION_THROWS
+#else
+# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V 0
+#endif
+
+#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS ) && span_CONFIG_CONTRACT_VIOLATION_THROWS && \
+ defined( span_CONFIG_CONTRACT_VIOLATION_TERMINATES ) && span_CONFIG_CONTRACT_VIOLATION_TERMINATES
+# error Please define none or one of span_CONFIG_CONTRACT_VIOLATION_THROWS and span_CONFIG_CONTRACT_VIOLATION_TERMINATES to 1, but not both.
+#endif
+
+// C++ language version detection (C++23 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef span_CPLUSPLUS
+# if defined(_MSVC_LANG ) && !defined(__clang__)
+# define span_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+# define span_CPLUSPLUS __cplusplus
+# endif
+#endif
+
+#define span_CPP98_OR_GREATER ( span_CPLUSPLUS >= 199711L )
+#define span_CPP11_OR_GREATER ( span_CPLUSPLUS >= 201103L )
+#define span_CPP14_OR_GREATER ( span_CPLUSPLUS >= 201402L )
+#define span_CPP17_OR_GREATER ( span_CPLUSPLUS >= 201703L )
+#define span_CPP20_OR_GREATER ( span_CPLUSPLUS >= 202002L )
+#define span_CPP23_OR_GREATER ( span_CPLUSPLUS >= 202300L )
+
+// C++ language version (represent 98 as 3):
+
+#define span_CPLUSPLUS_V ( span_CPLUSPLUS / 100 - (span_CPLUSPLUS > 200000 ? 2000 : 1994) )
+
+#define span_IN_STD( v ) ( ((v) == 98 ? 3 : (v)) >= span_CPLUSPLUS_V )
+
+#define span_CONFIG( feature ) ( span_CONFIG_##feature )
+#define span_FEATURE( feature ) ( span_FEATURE_##feature )
+#define span_FEATURE_TO_STD( feature ) ( span_IN_STD( span_FEATURE( feature##_TO_STD ) ) )
+
+// Use C++20 std::span if available and requested:
+
+#if span_CPP20_OR_GREATER && defined(__has_include )
+# if __has_include( <span> )
+# define span_HAVE_STD_SPAN 1
+# else
+# define span_HAVE_STD_SPAN 0
+# endif
+#else
+# define span_HAVE_STD_SPAN 0
+#endif
+
+#define span_USES_STD_SPAN ( (span_CONFIG_SELECT_SPAN == span_SPAN_STD) || ((span_CONFIG_SELECT_SPAN == span_SPAN_DEFAULT) && span_HAVE_STD_SPAN) )
+
+//
+// Use C++20 std::span:
+//
+
+#if span_USES_STD_SPAN
+
+#include <span>
+
+namespace nonstd {
+
+using std::span;
+using std::dynamic_extent;
+
+// Note: C++20 does not provide comparison
+// using std::operator==;
+// using std::operator!=;
+// using std::operator<;
+// using std::operator<=;
+// using std::operator>;
+// using std::operator>=;
+} // namespace nonstd
+
+#else // span_USES_STD_SPAN
+
+#include <algorithm>
+
+// Compiler versions:
+//
+// MSVC++ 6.0 _MSC_VER == 1200 span_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0)
+// MSVC++ 7.0 _MSC_VER == 1300 span_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002)
+// MSVC++ 7.1 _MSC_VER == 1310 span_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003)
+// MSVC++ 8.0 _MSC_VER == 1400 span_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005)
+// MSVC++ 9.0 _MSC_VER == 1500 span_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008)
+// MSVC++ 10.0 _MSC_VER == 1600 span_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010)
+// MSVC++ 11.0 _MSC_VER == 1700 span_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012)
+// MSVC++ 12.0 _MSC_VER == 1800 span_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013)
+// MSVC++ 14.0 _MSC_VER == 1900 span_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015)
+// MSVC++ 14.1 _MSC_VER >= 1910 span_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017)
+// MSVC++ 14.2 _MSC_VER >= 1920 span_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019)
+
+#if defined(_MSC_VER ) && !defined(__clang__)
+# define span_COMPILER_MSVC_VER (_MSC_VER )
+# define span_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
+#else
+# define span_COMPILER_MSVC_VER 0
+# define span_COMPILER_MSVC_VERSION 0
+#endif
+
+#define span_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) )
+
+#if defined(__clang__)
+# define span_COMPILER_CLANG_VERSION span_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#else
+# define span_COMPILER_CLANG_VERSION 0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+# define span_COMPILER_GNUC_VERSION span_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#else
+# define span_COMPILER_GNUC_VERSION 0
+#endif
+
+// half-open range [lo..hi):
+#define span_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
+
+// Compiler warning suppression:
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wundef"
+# pragma clang diagnostic ignored "-Wmismatched-tags"
+# define span_RESTORE_WARNINGS() _Pragma( "clang diagnostic pop" )
+
+#elif defined __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wundef"
+# define span_RESTORE_WARNINGS() _Pragma( "GCC diagnostic pop" )
+
+#elif span_COMPILER_MSVC_VER >= 1900
+# define span_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes))
+# define span_RESTORE_WARNINGS() __pragma(warning(pop ))
+
+// Suppress the following MSVC GSL warnings:
+// - C26439, gsl::f.6 : special function 'function' can be declared 'noexcept'
+// - C26440, gsl::f.6 : function 'function' can be declared 'noexcept'
+// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
+// use brace initialization, gsl::narrow_cast or gsl::narrow
+// - C26473: gsl::t.1 : don't cast between pointer types where the source type and the target type are the same
+// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
+// - C26490: gsl::t.1 : don't use reinterpret_cast
+
+span_DISABLE_MSVC_WARNINGS( 26439 26440 26472 26473 26481 26490 )
+
+#else
+# define span_RESTORE_WARNINGS() /*empty*/
+#endif
+
+// Presence of language and library features:
+
+#ifdef _HAS_CPP0X
+# define span_HAS_CPP0X _HAS_CPP0X
+#else
+# define span_HAS_CPP0X 0
+#endif
+
+#define span_CPP11_80 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1400)
+#define span_CPP11_90 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1500)
+#define span_CPP11_100 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1600)
+#define span_CPP11_110 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1700)
+#define span_CPP11_120 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1800)
+#define span_CPP11_140 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1900)
+
+#define span_CPP14_000 (span_CPP14_OR_GREATER)
+#define span_CPP14_120 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1800)
+#define span_CPP14_140 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1900)
+
+#define span_CPP17_000 (span_CPP17_OR_GREATER)
+
+// Presence of C++11 language features:
+
+#define span_HAVE_ALIAS_TEMPLATE span_CPP11_140
+#define span_HAVE_AUTO span_CPP11_100
+#define span_HAVE_CONSTEXPR_11 span_CPP11_140
+#define span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG span_CPP11_120
+#define span_HAVE_EXPLICIT_CONVERSION span_CPP11_140
+#define span_HAVE_INITIALIZER_LIST span_CPP11_120
+#define span_HAVE_IS_DEFAULT span_CPP11_140
+#define span_HAVE_IS_DELETE span_CPP11_140
+#define span_HAVE_NOEXCEPT span_CPP11_140
+#define span_HAVE_NULLPTR span_CPP11_100
+#define span_HAVE_STATIC_ASSERT span_CPP11_100
+
+// Presence of C++14 language features:
+
+#define span_HAVE_CONSTEXPR_14 span_CPP14_000
+
+// Presence of C++17 language features:
+
+#define span_HAVE_DEPRECATED span_CPP17_000
+#define span_HAVE_NODISCARD span_CPP17_000
+#define span_HAVE_NORETURN span_CPP17_000
+
+// MSVC: template parameter deduction guides since Visual Studio 2017 v15.7
+
+#if defined(__cpp_deduction_guides)
+# define span_HAVE_DEDUCTION_GUIDES 1
+#else
+# define span_HAVE_DEDUCTION_GUIDES (span_CPP17_OR_GREATER && ! span_BETWEEN( span_COMPILER_MSVC_VER, 1, 1913 ))
+#endif
+
+// Presence of C++ library features:
+
+#define span_HAVE_ADDRESSOF span_CPP17_000
+#define span_HAVE_ARRAY span_CPP11_110
+#define span_HAVE_BYTE span_CPP17_000
+#define span_HAVE_CONDITIONAL span_CPP11_120
+#define span_HAVE_CONTAINER_DATA_METHOD (span_CPP11_140 || ( span_COMPILER_MSVC_VER >= 1500 && span_HAS_CPP0X ))
+#define span_HAVE_DATA span_CPP17_000
+#define span_HAVE_LONGLONG span_CPP11_80
+#define span_HAVE_REMOVE_CONST span_CPP11_110
+#define span_HAVE_SNPRINTF span_CPP11_140
+#define span_HAVE_STRUCT_BINDING span_CPP11_120
+#define span_HAVE_TYPE_TRAITS span_CPP11_90
+
+// Presence of byte-lite:
+
+#ifdef NONSTD_BYTE_LITE_HPP
+# define span_HAVE_NONSTD_BYTE 1
+#else
+# define span_HAVE_NONSTD_BYTE 0
+#endif
+
+// C++ feature usage:
+
+#if span_HAVE_ADDRESSOF
+# define span_ADDRESSOF(x) std::addressof(x)
+#else
+# define span_ADDRESSOF(x) (&x)
+#endif
+
+#if span_HAVE_CONSTEXPR_11
+# define span_constexpr constexpr
+#else
+# define span_constexpr /*span_constexpr*/
+#endif
+
+#if span_HAVE_CONSTEXPR_14
+# define span_constexpr14 constexpr
+#else
+# define span_constexpr14 /*span_constexpr*/
+#endif
+
+#if span_HAVE_EXPLICIT_CONVERSION
+# define span_explicit explicit
+#else
+# define span_explicit /*explicit*/
+#endif
+
+#if span_HAVE_IS_DELETE
+# define span_is_delete = delete
+#else
+# define span_is_delete
+#endif
+
+#if span_HAVE_IS_DELETE
+# define span_is_delete_access public
+#else
+# define span_is_delete_access private
+#endif
+
+#if span_HAVE_NOEXCEPT && ! span_CONFIG_CONTRACT_VIOLATION_THROWS_V
+# define span_noexcept noexcept
+#else
+# define span_noexcept /*noexcept*/
+#endif
+
+#if span_HAVE_NULLPTR
+# define span_nullptr nullptr
+#else
+# define span_nullptr NULL
+#endif
+
+#if span_HAVE_DEPRECATED
+# define span_deprecated(msg) [[deprecated(msg)]]
+#else
+# define span_deprecated(msg) /*[[deprecated]]*/
+#endif
+
+#if span_HAVE_NODISCARD
+# define span_nodiscard [[nodiscard]]
+#else
+# define span_nodiscard /*[[nodiscard]]*/
+#endif
+
+#if span_HAVE_NORETURN
+# define span_noreturn [[noreturn]]
+#else
+# define span_noreturn /*[[noreturn]]*/
+#endif
+
+// Other features:
+
+#define span_HAVE_CONSTRAINED_SPAN_CONTAINER_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG
+#define span_HAVE_ITERATOR_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG
+
+// Additional includes:
+
+#if span_HAVE( ADDRESSOF )
+# include <memory>
+#endif
+
+#if span_HAVE( ARRAY )
+# include <array>
+#endif
+
+#if span_HAVE( BYTE )
+# include <cstddef>
+#endif
+
+#if span_HAVE( DATA )
+# include <iterator> // for std::data(), std::size()
+#endif
+
+#if span_HAVE( TYPE_TRAITS )
+# include <type_traits>
+#endif
+
+#if ! span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+# include <vector>
+#endif
+
+#if span_FEATURE( MEMBER_AT ) > 1
+# include <cstdio>
+#endif
+
+#if ! span_CONFIG( NO_EXCEPTIONS )
+# include <stdexcept>
+#endif
+
+// Contract violation
+
+#define span_ELIDE_CONTRACT_EXPECTS ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x01 ) )
+#define span_ELIDE_CONTRACT_ENSURES ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x10 ) )
+
+#if span_ELIDE_CONTRACT_EXPECTS
+# define span_constexpr_exp span_constexpr
+# define span_EXPECTS( cond ) /* Expect elided */
+#else
+# define span_constexpr_exp span_constexpr14
+# define span_EXPECTS( cond ) span_CONTRACT_CHECK( "Precondition", cond )
+#endif
+
+#if span_ELIDE_CONTRACT_ENSURES
+# define span_constexpr_ens span_constexpr
+# define span_ENSURES( cond ) /* Ensures elided */
+#else
+# define span_constexpr_ens span_constexpr14
+# define span_ENSURES( cond ) span_CONTRACT_CHECK( "Postcondition", cond )
+#endif
+
+#define span_CONTRACT_CHECK( type, cond ) \
+ cond ? static_cast< void >( 0 ) \
+ : nonstd::span_lite::detail::report_contract_violation( span_LOCATION( __FILE__, __LINE__ ) ": " type " violation." )
+
+#ifdef __GNUG__
+# define span_LOCATION( file, line ) file ":" span_STRINGIFY( line )
+#else
+# define span_LOCATION( file, line ) file "(" span_STRINGIFY( line ) ")"
+#endif
+
+// Method enabling
+
+#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+
+#define span_REQUIRES_0(VA) \
+ template< bool B = (VA), typename std::enable_if<B, int>::type = 0 >
+
+# if span_BETWEEN( span_COMPILER_MSVC_VERSION, 1, 140 )
+// VS 2013 and earlier seem to have trouble with SFINAE for default non-type arguments
+# define span_REQUIRES_T(VA) \
+ , typename = typename std::enable_if< ( VA ), nonstd::span_lite::detail::enabler >::type
+# else
+# define span_REQUIRES_T(VA) \
+ , typename std::enable_if< (VA), int >::type = 0
+# endif
+
+#define span_REQUIRES_R(R, VA) \
+ typename std::enable_if< (VA), R>::type
+
+#define span_REQUIRES_A(VA) \
+ , typename std::enable_if< (VA), void*>::type = nullptr
+
+#else
+
+# define span_REQUIRES_0(VA) /*empty*/
+# define span_REQUIRES_T(VA) /*empty*/
+# define span_REQUIRES_R(R, VA) R
+# define span_REQUIRES_A(VA) /*empty*/
+
+#endif
+
+namespace nonstd {
+namespace span_lite {
+
+// [views.constants], constants
+
+typedef span_CONFIG_EXTENT_TYPE extent_t;
+typedef span_CONFIG_SIZE_TYPE size_t;
+
+span_constexpr const extent_t dynamic_extent = static_cast<extent_t>( -1 );
+
+template< class T, extent_t Extent = dynamic_extent >
+class span;
+
+// Tag to select span constructor taking a container (prevent ms-gsl warning C26426):
+
+struct with_container_t { span_constexpr with_container_t() span_noexcept {} };
+const span_constexpr with_container_t with_container;
+
+// C++11 emulation:
+
+namespace std11 {
+
+#if span_HAVE( REMOVE_CONST )
+
+using std::remove_cv;
+using std::remove_const;
+using std::remove_volatile;
+
+#else
+
+template< class T > struct remove_const { typedef T type; };
+template< class T > struct remove_const< T const > { typedef T type; };
+
+template< class T > struct remove_volatile { typedef T type; };
+template< class T > struct remove_volatile< T volatile > { typedef T type; };
+
+template< class T >
+struct remove_cv
+{
+ typedef typename std11::remove_volatile< typename std11::remove_const< T >::type >::type type;
+};
+
+#endif // span_HAVE( REMOVE_CONST )
+
+#if span_HAVE( TYPE_TRAITS )
+
+using std::is_same;
+using std::is_signed;
+using std::integral_constant;
+using std::true_type;
+using std::false_type;
+using std::remove_reference;
+
+#else
+
+template< class T, T v > struct integral_constant { enum { value = v }; };
+typedef integral_constant< bool, true > true_type;
+typedef integral_constant< bool, false > false_type;
+
+template< class T, class U > struct is_same : false_type{};
+template< class T > struct is_same<T, T> : true_type{};
+
+template< typename T > struct is_signed : false_type {};
+template<> struct is_signed<signed char> : true_type {};
+template<> struct is_signed<signed int > : true_type {};
+template<> struct is_signed<signed long> : true_type {};
+
+#endif
+
+} // namespace std11
+
+// C++17 emulation:
+
+namespace std17 {
+
+template< bool v > struct bool_constant : std11::integral_constant<bool, v>{};
+
+#if span_CPP11_120
+
+template< class...>
+using void_t = void;
+
+#endif
+
+#if span_HAVE( DATA )
+
+using std::data;
+using std::size;
+
+#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+
+template< typename T, std::size_t N >
+inline span_constexpr auto size( const T(&)[N] ) span_noexcept -> size_t
+{
+ return N;
+}
+
+template< typename C >
+inline span_constexpr auto size( C const & cont ) -> decltype( cont.size() )
+{
+ return cont.size();
+}
+
+template< typename T, std::size_t N >
+inline span_constexpr auto data( T(&arr)[N] ) span_noexcept -> T*
+{
+ return &arr[0];
+}
+
+template< typename C >
+inline span_constexpr auto data( C & cont ) -> decltype( cont.data() )
+{
+ return cont.data();
+}
+
+template< typename C >
+inline span_constexpr auto data( C const & cont ) -> decltype( cont.data() )
+{
+ return cont.data();
+}
+
+template< typename E >
+inline span_constexpr auto data( std::initializer_list<E> il ) span_noexcept -> E const *
+{
+ return il.begin();
+}
+
+#endif // span_HAVE( DATA )
+
+#if span_HAVE( BYTE )
+using std::byte;
+#elif span_HAVE( NONSTD_BYTE )
+using nonstd::byte;
+#endif
+
+} // namespace std17
+
+// C++20 emulation:
+
+namespace std20 {
+
+#if span_HAVE( DEDUCTION_GUIDES )
+template< class T >
+using iter_reference_t = decltype( *std::declval<T&>() );
+#endif
+
+} // namespace std20
+
+// Implementation details:
+
+namespace detail {
+
+/*enum*/ struct enabler{};
+
+template< typename T >
+span_constexpr bool is_positive( T x )
+{
+ return std11::is_signed<T>::value ? x >= 0 : true;
+}
+
+#if span_HAVE( TYPE_TRAITS )
+
+template< class Q >
+struct is_span_oracle : std::false_type{};
+
+template< class T, span_CONFIG_EXTENT_TYPE Extent >
+struct is_span_oracle< span<T, Extent> > : std::true_type{};
+
+template< class Q >
+struct is_span : is_span_oracle< typename std::remove_cv<Q>::type >{};
+
+template< class Q >
+struct is_std_array_oracle : std::false_type{};
+
+#if span_HAVE( ARRAY )
+
+template< class T, std::size_t Extent >
+struct is_std_array_oracle< std::array<T, Extent> > : std::true_type{};
+
+#endif
+
+template< class Q >
+struct is_std_array : is_std_array_oracle< typename std::remove_cv<Q>::type >{};
+
+template< class Q >
+struct is_array : std::false_type {};
+
+template< class T >
+struct is_array<T[]> : std::true_type {};
+
+template< class T, std::size_t N >
+struct is_array<T[N]> : std::true_type {};
+
+#if span_CPP11_140 && ! span_BETWEEN( span_COMPILER_GNUC_VERSION, 1, 500 )
+
+template< class, class = void >
+struct has_size_and_data : std::false_type{};
+
+template< class C >
+struct has_size_and_data
+<
+ C, std17::void_t<
+ decltype( std17::size(std::declval<C>()) ),
+ decltype( std17::data(std::declval<C>()) ) >
+> : std::true_type{};
+
+template< class, class, class = void >
+struct is_compatible_element : std::false_type {};
+
+template< class C, class E >
+struct is_compatible_element
+<
+ C, E, std17::void_t<
+ decltype( std17::data(std::declval<C>()) ) >
+> : std::is_convertible< typename std::remove_pointer<decltype( std17::data( std::declval<C&>() ) )>::type(*)[], E(*)[] >{};
+
+template< class C >
+struct is_container : std17::bool_constant
+<
+ ! is_span< C >::value
+ && ! is_array< C >::value
+ && ! is_std_array< C >::value
+ && has_size_and_data< C >::value
+>{};
+
+template< class C, class E >
+struct is_compatible_container : std17::bool_constant
+<
+ is_container<C>::value
+ && is_compatible_element<C,E>::value
+>{};
+
+#else // span_CPP11_140
+
+template<
+ class C, class E
+ span_REQUIRES_T((
+ ! is_span< C >::value
+ && ! is_array< C >::value
+ && ! is_std_array< C >::value
+ && ( std::is_convertible< typename std::remove_pointer<decltype( std17::data( std::declval<C&>() ) )>::type(*)[], E(*)[] >::value)
+ // && has_size_and_data< C >::value
+ ))
+ , class = decltype( std17::size(std::declval<C>()) )
+ , class = decltype( std17::data(std::declval<C>()) )
+>
+struct is_compatible_container : std::true_type{};
+
+#endif // span_CPP11_140
+
+#endif // span_HAVE( TYPE_TRAITS )
+
+#if ! span_CONFIG( NO_EXCEPTIONS )
+#if span_FEATURE( MEMBER_AT ) > 1
+
+// format index and size:
+
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wlong-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wformat=ll"
+# pragma GCC diagnostic ignored "-Wlong-long"
+#endif
+
+span_noreturn inline void throw_out_of_range( size_t idx, size_t size )
+{
+ const char fmt[] = "span::at(): index '%lli' is out of range [0..%lli)";
+ char buffer[ 2 * 20 + sizeof fmt ];
+ sprintf( buffer, fmt, static_cast<long long>(idx), static_cast<long long>(size) );
+
+ throw std::out_of_range( buffer );
+}
+
+#else // MEMBER_AT
+
+span_noreturn inline void throw_out_of_range( size_t /*idx*/, size_t /*size*/ )
+{
+ throw std::out_of_range( "span::at(): index outside span" );
+}
+#endif // MEMBER_AT
+#endif // NO_EXCEPTIONS
+
+#if span_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+
+struct contract_violation : std::logic_error
+{
+ explicit contract_violation( char const * const message )
+ : std::logic_error( message )
+ {}
+};
+
+inline void report_contract_violation( char const * msg )
+{
+ throw contract_violation( msg );
+}
+
+#else // span_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+
+span_noreturn inline void report_contract_violation( char const * /*msg*/ ) span_noexcept
+{
+ std::terminate();
+}
+
+#endif // span_CONFIG( CONTRACT_VIOLATION_THROWS_V )
+
+} // namespace detail
+
+// Prevent signed-unsigned mismatch:
+
+#define span_sizeof(T) static_cast<extent_t>( sizeof(T) )
+
+template< class T >
+inline span_constexpr size_t to_size( T size )
+{
+ return static_cast<size_t>( size );
+}
+
+//
+// [views.span] - A view over a contiguous, single-dimension sequence of objects
+//
+template< class T, extent_t Extent /*= dynamic_extent*/ >
+class span
+{
+public:
+ // constants and types
+
+ typedef T element_type;
+ typedef typename std11::remove_cv< T >::type value_type;
+
+ typedef T & reference;
+ typedef T * pointer;
+ typedef T const * const_pointer;
+ typedef T const & const_reference;
+
+ typedef size_t size_type;
+ typedef extent_t extent_type;
+
+ typedef pointer iterator;
+ typedef const_pointer const_iterator;
+
+ typedef std::ptrdiff_t difference_type;
+
+ typedef std::reverse_iterator< iterator > reverse_iterator;
+ typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
+
+// static constexpr extent_type extent = Extent;
+ enum { extent = Extent };
+
+ // 26.7.3.2 Constructors, copy, and assignment [span.cons]
+
+ span_REQUIRES_0(
+ ( Extent == 0 ) ||
+ ( Extent == dynamic_extent )
+ )
+ span_constexpr span() span_noexcept
+ : data_( span_nullptr )
+ , size_( 0 )
+ {
+ // span_EXPECTS( data() == span_nullptr );
+ // span_EXPECTS( size() == 0 );
+ }
+
+#if span_HAVE( ITERATOR_CTOR )
+ // Didn't yet succeed in combining the next two constructors:
+
+ span_constexpr_exp span( std::nullptr_t, size_type count )
+ : data_( span_nullptr )
+ , size_( count )
+ {
+ span_EXPECTS( data_ == span_nullptr && count == 0 );
+ }
+
+ template< typename It
+ span_REQUIRES_T((
+ std::is_convertible<decltype(*std::declval<It&>()), element_type &>::value
+ ))
+ >
+ span_constexpr_exp span( It first, size_type count )
+ : data_( to_address( first ) )
+ , size_( count )
+ {
+ span_EXPECTS(
+ ( data_ == span_nullptr && count == 0 ) ||
+ ( data_ != span_nullptr && detail::is_positive( count ) )
+ );
+ }
+#else
+ span_constexpr_exp span( pointer ptr, size_type count )
+ : data_( ptr )
+ , size_( count )
+ {
+ span_EXPECTS(
+ ( ptr == span_nullptr && count == 0 ) ||
+ ( ptr != span_nullptr && detail::is_positive( count ) )
+ );
+ }
+#endif
+
+#if span_HAVE( ITERATOR_CTOR )
+ template< typename It, typename End
+ span_REQUIRES_T((
+ std::is_convertible<decltype(&*std::declval<It&>()), element_type *>::value
+ && ! std::is_convertible<End, std::size_t>::value
+ ))
+ >
+ span_constexpr_exp span( It first, End last )
+ : data_( to_address( first ) )
+ , size_( to_size( last - first ) )
+ {
+ span_EXPECTS(
+ last - first >= 0
+ );
+ }
+#else
+ span_constexpr_exp span( pointer first, pointer last )
+ : data_( first )
+ , size_( to_size( last - first ) )
+ {
+ span_EXPECTS(
+ last - first >= 0
+ );
+ }
+#endif
+
+ template< std::size_t N
+ span_REQUIRES_T((
+ (Extent == dynamic_extent || Extent == static_cast<extent_t>(N))
+ && std::is_convertible< value_type(*)[], element_type(*)[] >::value
+ ))
+ >
+ span_constexpr span( element_type ( &arr )[ N ] ) span_noexcept
+ : data_( span_ADDRESSOF( arr[0] ) )
+ , size_( N )
+ {}
+
+#if span_HAVE( ARRAY )
+
+ template< std::size_t N
+ span_REQUIRES_T((
+ (Extent == dynamic_extent || Extent == static_cast<extent_t>(N))
+ && std::is_convertible< value_type(*)[], element_type(*)[] >::value
+ ))
+ >
+# if span_FEATURE( CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE )
+ span_constexpr span( std::array< element_type, N > & arr ) span_noexcept
+# else
+ span_constexpr span( std::array< value_type, N > & arr ) span_noexcept
+# endif
+ : data_( arr.data() )
+ , size_( to_size( arr.size() ) )
+ {}
+
+ template< std::size_t N
+# if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+ span_REQUIRES_T((
+ (Extent == dynamic_extent || Extent == static_cast<extent_t>(N))
+ && std::is_convertible< value_type(*)[], element_type(*)[] >::value
+ ))
+# endif
+ >
+ span_constexpr span( std::array< value_type, N> const & arr ) span_noexcept
+ : data_( arr.data() )
+ , size_( to_size( arr.size() ) )
+ {}
+
+#endif // span_HAVE( ARRAY )
+
+#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+ template< class Container
+ span_REQUIRES_T((
+ detail::is_compatible_container< Container, element_type >::value
+ ))
+ >
+ span_constexpr span( Container & cont )
+ : data_( std17::data( cont ) )
+ , size_( to_size( std17::size( cont ) ) )
+ {}
+
+ template< class Container
+ span_REQUIRES_T((
+ std::is_const< element_type >::value
+ && detail::is_compatible_container< Container, element_type >::value
+ ))
+ >
+ span_constexpr span( Container const & cont )
+ : data_( std17::data( cont ) )
+ , size_( to_size( std17::size( cont ) ) )
+ {}
+
+#endif // span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+
+#if span_FEATURE( WITH_CONTAINER )
+
+ template< class Container >
+ span_constexpr span( with_container_t, Container & cont )
+ : data_( cont.size() == 0 ? span_nullptr : span_ADDRESSOF( cont[0] ) )
+ , size_( to_size( cont.size() ) )
+ {}
+
+ template< class Container >
+ span_constexpr span( with_container_t, Container const & cont )
+ : data_( cont.size() == 0 ? span_nullptr : const_cast<pointer>( span_ADDRESSOF( cont[0] ) ) )
+ , size_( to_size( cont.size() ) )
+ {}
+#endif
+
+#if span_FEATURE( WITH_INITIALIZER_LIST_P2447 ) && span_HAVE( INITIALIZER_LIST )
+
+ // constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il) noexcept;
+
+#if !span_BETWEEN( span_COMPILER_MSVC_VERSION, 120, 130 )
+
+ template< extent_t U = Extent
+ span_REQUIRES_T((
+ U != dynamic_extent
+ ))
+ >
+#if span_COMPILER_GNUC_VERSION >= 900 // prevent GCC's "-Winit-list-lifetime"
+ span_constexpr14 explicit span( std::initializer_list<value_type> il ) span_noexcept
+ {
+ data_ = il.begin();
+ size_ = il.size();
+ }
+#else
+ span_constexpr explicit span( std::initializer_list<value_type> il ) span_noexcept
+ : data_( il.begin() )
+ , size_( il.size() )
+ {}
+#endif
+
+#endif // MSVC 120 (VS2013)
+
+ template< extent_t U = Extent
+ span_REQUIRES_T((
+ U == dynamic_extent
+ ))
+ >
+#if span_COMPILER_GNUC_VERSION >= 900 // prevent GCC's "-Winit-list-lifetime"
+ span_constexpr14 /*explicit*/ span( std::initializer_list<value_type> il ) span_noexcept
+ {
+ data_ = il.begin();
+ size_ = il.size();
+ }
+#else
+ span_constexpr /*explicit*/ span( std::initializer_list<value_type> il ) span_noexcept
+ : data_( il.begin() )
+ , size_( il.size() )
+ {}
+#endif
+
+#endif // P2447
+
+#if span_HAVE( IS_DEFAULT )
+ span_constexpr span( span const & other ) span_noexcept = default;
+
+ ~span() span_noexcept = default;
+
+ span_constexpr14 span & operator=( span const & other ) span_noexcept = default;
+#else
+ span_constexpr span( span const & other ) span_noexcept
+ : data_( other.data_ )
+ , size_( other.size_ )
+ {}
+
+ ~span() span_noexcept
+ {}
+
+ span_constexpr14 span & operator=( span const & other ) span_noexcept
+ {
+ data_ = other.data_;
+ size_ = other.size_;
+
+ return *this;
+ }
+#endif
+
+ template< class OtherElementType, extent_type OtherExtent
+ span_REQUIRES_T((
+ (Extent == dynamic_extent || OtherExtent == dynamic_extent || Extent == OtherExtent)
+ && std::is_convertible<OtherElementType(*)[], element_type(*)[]>::value
+ ))
+ >
+ span_constexpr_exp span( span<OtherElementType, OtherExtent> const & other ) span_noexcept
+ : data_( other.data() )
+ , size_( other.size() )
+ {
+ span_EXPECTS( OtherExtent == dynamic_extent || other.size() == to_size(OtherExtent) );
+ }
+
+ // 26.7.3.3 Subviews [span.sub]
+
+ template< extent_type Count >
+ span_constexpr_exp span< element_type, Count >
+ first() const
+ {
+ span_EXPECTS( detail::is_positive( Count ) && Count <= size() );
+
+ return span< element_type, Count >( data(), Count );
+ }
+
+ template< extent_type Count >
+ span_constexpr_exp span< element_type, Count >
+ last() const
+ {
+ span_EXPECTS( detail::is_positive( Count ) && Count <= size() );
+
+ return span< element_type, Count >( data() + (size() - Count), Count );
+ }
+
+#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG )
+ template< size_type Offset, extent_type Count = dynamic_extent >
+#else
+ template< size_type Offset, extent_type Count /*= dynamic_extent*/ >
+#endif
+ span_constexpr_exp span< element_type, Count >
+ subspan() const
+ {
+ span_EXPECTS(
+ ( detail::is_positive( Offset ) && Offset <= size() ) &&
+ ( Count == dynamic_extent || (detail::is_positive( Count ) && Count + Offset <= size()) )
+ );
+
+ return span< element_type, Count >(
+ data() + Offset, Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : size() - Offset) );
+ }
+
+ span_constexpr_exp span< element_type, dynamic_extent >
+ first( size_type count ) const
+ {
+ span_EXPECTS( detail::is_positive( count ) && count <= size() );
+
+ return span< element_type, dynamic_extent >( data(), count );
+ }
+
+ span_constexpr_exp span< element_type, dynamic_extent >
+ last( size_type count ) const
+ {
+ span_EXPECTS( detail::is_positive( count ) && count <= size() );
+
+ return span< element_type, dynamic_extent >( data() + ( size() - count ), count );
+ }
+
+ span_constexpr_exp span< element_type, dynamic_extent >
+ subspan( size_type offset, size_type count = static_cast<size_type>(dynamic_extent) ) const
+ {
+ span_EXPECTS(
+ ( ( detail::is_positive( offset ) && offset <= size() ) ) &&
+ ( count == static_cast<size_type>(dynamic_extent) || ( detail::is_positive( count ) && offset + count <= size() ) )
+ );
+
+ return span< element_type, dynamic_extent >(
+ data() + offset, count == static_cast<size_type>(dynamic_extent) ? size() - offset : count );
+ }
+
+ // 26.7.3.4 Observers [span.obs]
+
+ span_constexpr size_type size() const span_noexcept
+ {
+ return size_;
+ }
+
+ span_constexpr std::ptrdiff_t ssize() const span_noexcept
+ {
+ return static_cast<std::ptrdiff_t>( size_ );
+ }
+
+ span_constexpr size_type size_bytes() const span_noexcept
+ {
+ return size() * to_size( sizeof( element_type ) );
+ }
+
+ span_nodiscard span_constexpr bool empty() const span_noexcept
+ {
+ return size() == 0;
+ }
+
+ // 26.7.3.5 Element access [span.elem]
+
+ span_constexpr_exp reference operator[]( size_type idx ) const
+ {
+ span_EXPECTS( detail::is_positive( idx ) && idx < size() );
+
+ return *( data() + idx );
+ }
+
+#if span_FEATURE( MEMBER_CALL_OPERATOR )
+ span_deprecated("replace operator() with operator[]")
+
+ span_constexpr_exp reference operator()( size_type idx ) const
+ {
+ span_EXPECTS( detail::is_positive( idx ) && idx < size() );
+
+ return *( data() + idx );
+ }
+#endif
+
+#if span_FEATURE( MEMBER_AT )
+ span_constexpr14 reference at( size_type idx ) const
+ {
+#if span_CONFIG( NO_EXCEPTIONS )
+ return this->operator[]( idx );
+#else
+ if ( !detail::is_positive( idx ) || size() <= idx )
+ {
+ detail::throw_out_of_range( idx, size() );
+ }
+ return *( data() + idx );
+#endif
+ }
+#endif
+
+ span_constexpr pointer data() const span_noexcept
+ {
+ return data_;
+ }
+
+#if span_FEATURE( MEMBER_BACK_FRONT )
+
+ span_constexpr_exp reference front() const span_noexcept
+ {
+ span_EXPECTS( ! empty() );
+
+ return *data();
+ }
+
+ span_constexpr_exp reference back() const span_noexcept
+ {
+ span_EXPECTS( ! empty() );
+
+ return *( data() + size() - 1 );
+ }
+
+#endif
+
+ // xx.x.x.x Modifiers [span.modifiers]
+
+#if span_FEATURE( MEMBER_SWAP )
+
+ span_constexpr14 void swap( span & other ) span_noexcept
+ {
+ using std::swap;
+ swap( data_, other.data_ );
+ swap( size_, other.size_ );
+ }
+#endif
+
+ // 26.7.3.6 Iterator support [span.iterators]
+
+ span_constexpr iterator begin() const span_noexcept
+ {
+#if span_CPP11_OR_GREATER
+ return { data() };
+#else
+ return iterator( data() );
+#endif
+ }
+
+ span_constexpr iterator end() const span_noexcept
+ {
+#if span_CPP11_OR_GREATER
+ return { data() + size() };
+#else
+ return iterator( data() + size() );
+#endif
+ }
+
+ span_constexpr const_iterator cbegin() const span_noexcept
+ {
+#if span_CPP11_OR_GREATER
+ return { data() };
+#else
+ return const_iterator( data() );
+#endif
+ }
+
+ span_constexpr const_iterator cend() const span_noexcept
+ {
+#if span_CPP11_OR_GREATER
+ return { data() + size() };
+#else
+ return const_iterator( data() + size() );
+#endif
+ }
+
+ span_constexpr reverse_iterator rbegin() const span_noexcept
+ {
+ return reverse_iterator( end() );
+ }
+
+ span_constexpr reverse_iterator rend() const span_noexcept
+ {
+ return reverse_iterator( begin() );
+ }
+
+ span_constexpr const_reverse_iterator crbegin() const span_noexcept
+ {
+ return const_reverse_iterator ( cend() );
+ }
+
+ span_constexpr const_reverse_iterator crend() const span_noexcept
+ {
+ return const_reverse_iterator( cbegin() );
+ }
+
+private:
+
+ // Note: C++20 has std::pointer_traits<Ptr>::to_address( it );
+
+#if span_HAVE( ITERATOR_CTOR )
+ static inline span_constexpr pointer to_address( std::nullptr_t ) span_noexcept
+ {
+ return nullptr;
+ }
+
+ template< typename U >
+ static inline span_constexpr U * to_address( U * p ) span_noexcept
+ {
+ return p;
+ }
+
+ template< typename Ptr
+ span_REQUIRES_T(( ! std::is_pointer<Ptr>::value ))
+ >
+ static inline span_constexpr pointer to_address( Ptr const & it ) span_noexcept
+ {
+ return to_address( it.operator->() );
+ }
+#endif // span_HAVE( ITERATOR_CTOR )
+
+private:
+ pointer data_;
+ size_type size_;
+};
+
+// class template argument deduction guides:
+
+#if span_HAVE( DEDUCTION_GUIDES )
+
+template< class T, size_t N >
+span( T (&)[N] ) -> span<T, static_cast<extent_t>(N)>;
+
+template< class T, size_t N >
+span( std::array<T, N> & ) -> span<T, static_cast<extent_t>(N)>;
+
+template< class T, size_t N >
+span( std::array<T, N> const & ) -> span<const T, static_cast<extent_t>(N)>;
+
+#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR )
+
+template< class Container >
+span( Container& ) -> span<typename Container::value_type>;
+
+template< class Container >
+span( Container const & ) -> span<const typename Container::value_type>;
+
+#endif
+
+// iterator: constraints: It satisfies contiguous_­iterator.
+
+template< class It, class EndOrSize >
+span( It, EndOrSize ) -> span< typename std11::remove_reference< typename std20::iter_reference_t<It> >::type >;
+
+#endif // span_HAVE( DEDUCTION_GUIDES )
+
+// 26.7.3.7 Comparison operators [span.comparison]
+
+#if span_FEATURE( COMPARISON )
+#if span_FEATURE( SAME )
+
+template< class T1, extent_t E1, class T2, extent_t E2 >
+inline span_constexpr bool same( span<T1,E1> const & l, span<T2,E2> const & r ) span_noexcept
+{
+ return std11::is_same<T1, T2>::value
+ && l.size() == r.size()
+ && static_cast<void const*>( l.data() ) == r.data();
+}
+
+#endif
+
+template< class T1, extent_t E1, class T2, extent_t E2 >
+inline span_constexpr bool operator==( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+ return
+#if span_FEATURE( SAME )
+ same( l, r ) ||
+#endif
+ ( l.size() == r.size() && std::equal( l.begin(), l.end(), r.begin() ) );
+}
+
+template< class T1, extent_t E1, class T2, extent_t E2 >
+inline span_constexpr bool operator<( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+ return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() );
+}
+
+template< class T1, extent_t E1, class T2, extent_t E2 >
+inline span_constexpr bool operator!=( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+ return !( l == r );
+}
+
+template< class T1, extent_t E1, class T2, extent_t E2 >
+inline span_constexpr bool operator<=( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+ return !( r < l );
+}
+
+template< class T1, extent_t E1, class T2, extent_t E2 >
+inline span_constexpr bool operator>( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+ return ( r < l );
+}
+
+template< class T1, extent_t E1, class T2, extent_t E2 >
+inline span_constexpr bool operator>=( span<T1,E1> const & l, span<T2,E2> const & r )
+{
+ return !( l < r );
+}
+
+#endif // span_FEATURE( COMPARISON )
+
+// 26.7.2.6 views of object representation [span.objectrep]
+
+#if span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE )
+
+// Avoid MSVC 14.1 (1910), VS 2017: warning C4307: '*': integral constant overflow:
+
+template< typename T, extent_t Extent >
+struct BytesExtent
+{
+#if span_CPP11_OR_GREATER
+ enum ET : extent_t { value = span_sizeof(T) * Extent };
+#else
+ enum ET { value = span_sizeof(T) * Extent };
+#endif
+};
+
+template< typename T >
+struct BytesExtent< T, dynamic_extent >
+{
+#if span_CPP11_OR_GREATER
+ enum ET : extent_t { value = dynamic_extent };
+#else
+ enum ET { value = dynamic_extent };
+#endif
+};
+
+template< class T, extent_t Extent >
+inline span_constexpr span< const std17::byte, BytesExtent<T, Extent>::value >
+as_bytes( span<T,Extent> spn ) span_noexcept
+{
+#if 0
+ return { reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() };
+#else
+ return span< const std17::byte, BytesExtent<T, Extent>::value >(
+ reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() ); // NOLINT
+#endif
+}
+
+template< class T, extent_t Extent >
+inline span_constexpr span< std17::byte, BytesExtent<T, Extent>::value >
+as_writable_bytes( span<T,Extent> spn ) span_noexcept
+{
+#if 0
+ return { reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() };
+#else
+ return span< std17::byte, BytesExtent<T, Extent>::value >(
+ reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() ); // NOLINT
+#endif
+}
+
+#endif // span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE )
+
+// 27.8 Container and view access [iterator.container]
+
+template< class T, extent_t Extent /*= dynamic_extent*/ >
+span_constexpr std::size_t size( span<T,Extent> const & spn )
+{
+ return static_cast<std::size_t>( spn.size() );
+}
+
+template< class T, extent_t Extent /*= dynamic_extent*/ >
+span_constexpr std::ptrdiff_t ssize( span<T,Extent> const & spn )
+{
+ return static_cast<std::ptrdiff_t>( spn.size() );
+}
+
+} // namespace span_lite
+} // namespace nonstd
+
+// make available in nonstd:
+
+namespace nonstd {
+
+using span_lite::dynamic_extent;
+
+using span_lite::span;
+
+using span_lite::with_container;
+
+#if span_FEATURE( COMPARISON )
+#if span_FEATURE( SAME )
+using span_lite::same;
+#endif
+
+using span_lite::operator==;
+using span_lite::operator!=;
+using span_lite::operator<;
+using span_lite::operator<=;
+using span_lite::operator>;
+using span_lite::operator>=;
+#endif
+
+#if span_HAVE( BYTE )
+using span_lite::as_bytes;
+using span_lite::as_writable_bytes;
+#endif
+
+using span_lite::size;
+using span_lite::ssize;
+
+} // namespace nonstd
+
+#endif // span_USES_STD_SPAN
+
+// make_span() [span-lite extension]:
+
+#if span_FEATURE( MAKE_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER )
+
+#if span_USES_STD_SPAN
+# define span_constexpr constexpr
+# define span_noexcept noexcept
+# define span_nullptr nullptr
+# ifndef span_CONFIG_EXTENT_TYPE
+# define span_CONFIG_EXTENT_TYPE std::size_t
+# endif
+using extent_t = span_CONFIG_EXTENT_TYPE;
+#endif // span_USES_STD_SPAN
+
+namespace nonstd {
+namespace span_lite {
+
+template< class T >
+inline span_constexpr span<T>
+make_span( T * ptr, size_t count ) span_noexcept
+{
+ return span<T>( ptr, count );
+}
+
+template< class T >
+inline span_constexpr span<T>
+make_span( T * first, T * last ) span_noexcept
+{
+ return span<T>( first, last );
+}
+
+template< class T, std::size_t N >
+inline span_constexpr span<T, static_cast<extent_t>(N)>
+make_span( T ( &arr )[ N ] ) span_noexcept
+{
+ return span<T, static_cast<extent_t>(N)>( &arr[ 0 ], N );
+}
+
+#if span_USES_STD_SPAN || span_HAVE( ARRAY )
+
+template< class T, std::size_t N >
+inline span_constexpr span<T, static_cast<extent_t>(N)>
+make_span( std::array< T, N > & arr ) span_noexcept
+{
+ return span<T, static_cast<extent_t>(N)>( arr );
+}
+
+template< class T, std::size_t N >
+inline span_constexpr span< const T, static_cast<extent_t>(N) >
+make_span( std::array< T, N > const & arr ) span_noexcept
+{
+ return span<const T, static_cast<extent_t>(N)>( arr );
+}
+
+#endif // span_HAVE( ARRAY )
+
+#if span_USES_STD_SPAN || span_HAVE( INITIALIZER_LIST )
+
+template< class T >
+inline span_constexpr span< const T >
+make_span( std::initializer_list<T> il ) span_noexcept
+{
+ return span<const T>( il.begin(), il.size() );
+}
+
+#endif // span_HAVE( INITIALIZER_LIST )
+
+#if span_USES_STD_SPAN
+
+template< class Container, class EP = decltype( std::data(std::declval<Container&>())) >
+inline span_constexpr auto
+make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer<EP>::type >
+{
+ return span< typename std::remove_pointer<EP>::type >( cont );
+}
+
+template< class Container, class EP = decltype( std::data(std::declval<Container&>())) >
+inline span_constexpr auto
+make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer<EP>::type >
+{
+ return span< const typename std::remove_pointer<EP>::type >( cont );
+}
+
+#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) && span_HAVE( AUTO )
+
+template< class Container, class EP = decltype( std17::data(std::declval<Container&>())) >
+inline span_constexpr auto
+make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer<EP>::type >
+{
+ return span< typename std::remove_pointer<EP>::type >( cont );
+}
+
+template< class Container, class EP = decltype( std17::data(std::declval<Container&>())) >
+inline span_constexpr auto
+make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer<EP>::type >
+{
+ return span< const typename std::remove_pointer<EP>::type >( cont );
+}
+
+#else
+
+template< class T >
+inline span_constexpr span<T>
+make_span( span<T> spn ) span_noexcept
+{
+ return spn;
+}
+
+template< class T, class Allocator >
+inline span_constexpr span<T>
+make_span( std::vector<T, Allocator> & cont ) span_noexcept
+{
+ return span<T>( with_container, cont );
+}
+
+template< class T, class Allocator >
+inline span_constexpr span<const T>
+make_span( std::vector<T, Allocator> const & cont ) span_noexcept
+{
+ return span<const T>( with_container, cont );
+}
+
+#endif // span_USES_STD_SPAN || ( ... )
+
+#if ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER )
+
+template< class Container >
+inline span_constexpr span<typename Container::value_type>
+make_span( with_container_t, Container & cont ) span_noexcept
+{
+ return span< typename Container::value_type >( with_container, cont );
+}
+
+template< class Container >
+inline span_constexpr span<const typename Container::value_type>
+make_span( with_container_t, Container const & cont ) span_noexcept
+{
+ return span< const typename Container::value_type >( with_container, cont );
+}
+
+#endif // ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER )
+
+// extensions: non-member views:
+// this feature implies the presence of make_span()
+
+#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN )
+
+template< extent_t Count, class T, extent_t Extent >
+span_constexpr span<T, Count>
+first( span<T, Extent> spn )
+{
+ return spn.template first<Count>();
+}
+
+template< class T, extent_t Extent >
+span_constexpr span<T>
+first( span<T, Extent> spn, size_t count )
+{
+ return spn.first( count );
+}
+
+template< extent_t Count, class T, extent_t Extent >
+span_constexpr span<T, Count>
+last( span<T, Extent> spn )
+{
+ return spn.template last<Count>();
+}
+
+template< class T, extent_t Extent >
+span_constexpr span<T>
+last( span<T, Extent> spn, size_t count )
+{
+ return spn.last( count );
+}
+
+template< size_t Offset, extent_t Count, class T, extent_t Extent >
+span_constexpr span<T, Count>
+subspan( span<T, Extent> spn )
+{
+ return spn.template subspan<Offset, Count>();
+}
+
+template< class T, extent_t Extent >
+span_constexpr span<T>
+subspan( span<T, Extent> spn, size_t offset, extent_t count = dynamic_extent )
+{
+ return spn.subspan( offset, count );
+}
+
+#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN )
+
+#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120
+
+template< extent_t Count, class T >
+span_constexpr auto
+first( T & t ) -> decltype( make_span(t).template first<Count>() )
+{
+ return make_span( t ).template first<Count>();
+}
+
+template< class T >
+span_constexpr auto
+first( T & t, size_t count ) -> decltype( make_span(t).first(count) )
+{
+ return make_span( t ).first( count );
+}
+
+template< extent_t Count, class T >
+span_constexpr auto
+last( T & t ) -> decltype( make_span(t).template last<Count>() )
+{
+ return make_span(t).template last<Count>();
+}
+
+template< class T >
+span_constexpr auto
+last( T & t, extent_t count ) -> decltype( make_span(t).last(count) )
+{
+ return make_span( t ).last( count );
+}
+
+template< size_t Offset, extent_t Count = dynamic_extent, class T >
+span_constexpr auto
+subspan( T & t ) -> decltype( make_span(t).template subspan<Offset, Count>() )
+{
+ return make_span( t ).template subspan<Offset, Count>();
+}
+
+template< class T >
+span_constexpr auto
+subspan( T & t, size_t offset, extent_t count = dynamic_extent ) -> decltype( make_span(t).subspan(offset, count) )
+{
+ return make_span( t ).subspan( offset, count );
+}
+
+#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER )
+
+} // namespace span_lite
+} // namespace nonstd
+
+// make available in nonstd:
+
+namespace nonstd {
+using span_lite::make_span;
+
+#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || ( span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120 )
+
+using span_lite::first;
+using span_lite::last;
+using span_lite::subspan;
+
+#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_[SPAN|CONTAINER] )
+
+} // namespace nonstd
+
+#endif // #if span_FEATURE_TO_STD( MAKE_SPAN )
+
+#if span_CPP11_OR_GREATER && span_FEATURE( BYTE_SPAN ) && ( span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) )
+
+namespace nonstd {
+namespace span_lite {
+
+template< class T >
+inline span_constexpr auto
+byte_span( T & t ) span_noexcept -> span< std17::byte, span_sizeof(T) >
+{
+ return span< std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte * >( &t ), span_sizeof(T) );
+}
+
+template< class T >
+inline span_constexpr auto
+byte_span( T const & t ) span_noexcept -> span< const std17::byte, span_sizeof(T) >
+{
+ return span< const std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte const * >( &t ), span_sizeof(T) );
+}
+
+} // namespace span_lite
+} // namespace nonstd
+
+// make available in nonstd:
+
+namespace nonstd {
+using span_lite::byte_span;
+} // namespace nonstd
+
+#endif // span_FEATURE( BYTE_SPAN )
+
+#if span_HAVE( STRUCT_BINDING )
+
+#if span_CPP14_OR_GREATER
+# include <tuple>
+#elif span_CPP11_OR_GREATER
+# include <tuple>
+namespace std {
+ template< std::size_t I, typename T >
+ using tuple_element_t = typename tuple_element<I, T>::type;
+}
+#else
+namespace std {
+ template< typename T >
+ class tuple_size; /*undefined*/
+
+ template< std::size_t I, typename T >
+ class tuple_element; /* undefined */
+}
+#endif // span_CPP14_OR_GREATER
+
+namespace std {
+
+// 26.7.X Tuple interface
+
+// std::tuple_size<>:
+
+template< typename ElementType, nonstd::span_lite::extent_t Extent >
+class tuple_size< nonstd::span<ElementType, Extent> > : public integral_constant<size_t, static_cast<size_t>(Extent)> {};
+
+// std::tuple_size<>: Leave undefined for dynamic extent:
+
+template< typename ElementType >
+class tuple_size< nonstd::span<ElementType, nonstd::dynamic_extent> >;
+
+// std::tuple_element<>:
+
+template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent >
+class tuple_element< I, nonstd::span<ElementType, Extent> >
+{
+public:
+#if span_HAVE( STATIC_ASSERT )
+ static_assert( Extent != nonstd::dynamic_extent && I < Extent, "tuple_element<I,span>: dynamic extent or index out of range" );
+#endif
+ using type = ElementType;
+};
+
+// std::get<>(), 2 variants:
+
+template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent >
+span_constexpr ElementType & get( nonstd::span<ElementType, Extent> & spn ) span_noexcept
+{
+#if span_HAVE( STATIC_ASSERT )
+ static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" );
+#endif
+ return spn[I];
+}
+
+template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent >
+span_constexpr ElementType const & get( nonstd::span<ElementType, Extent> const & spn ) span_noexcept
+{
+#if span_HAVE( STATIC_ASSERT )
+ static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" );
+#endif
+ return spn[I];
+}
+
+} // end namespace std
+
+#endif // span_HAVE( STRUCT_BINDING )
+
+#if ! span_USES_STD_SPAN
+span_RESTORE_WARNINGS()
+#endif // span_USES_STD_SPAN
+
+#endif // NONSTD_SPAN_HPP_INCLUDED
diff --git a/src/libs/3rdparty/sqlite/carray.c b/src/libs/3rdparty/sqlite/carray.c
new file mode 100644
index 0000000000..b1caa98c3f
--- /dev/null
+++ b/src/libs/3rdparty/sqlite/carray.c
@@ -0,0 +1,561 @@
+/*
+** 2016-06-29
+**
+** 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 demonstrates how to create a table-valued-function that
+** returns the values in a C-language array.
+** Examples:
+**
+** SELECT * FROM carray($ptr,5)
+**
+** The query above returns 5 integers contained in a C-language array
+** at the address $ptr. $ptr is a pointer to the array of integers.
+** The pointer value must be assigned to $ptr using the
+** sqlite3_bind_pointer() interface with a pointer type of "carray".
+** For example:
+**
+** static int aX[] = { 53, 9, 17, 2231, 4, 99 };
+** int i = sqlite3_bind_parameter_index(pStmt, "$ptr");
+** sqlite3_bind_pointer(pStmt, i, aX, "carray", 0);
+**
+** There is an optional third parameter to determine the datatype of
+** the C-language array. Allowed values of the third parameter are
+** 'int32', 'int64', 'double', 'char*', 'struct iovec'. Example:
+**
+** SELECT * FROM carray($ptr,10,'char*');
+**
+** The default value of the third parameter is 'int32'.
+**
+** HOW IT WORKS
+**
+** The carray "function" is really a virtual table with the
+** following schema:
+**
+** CREATE TABLE carray(
+** value,
+** pointer HIDDEN,
+** count HIDDEN,
+** ctype TEXT HIDDEN
+** );
+**
+** If the hidden columns "pointer" and "count" are unconstrained, then
+** the virtual table has no rows. Otherwise, the virtual table interprets
+** the integer value of "pointer" as a pointer to the array and "count"
+** as the number of elements in the array. The virtual table steps through
+** the array, element by element.
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+#ifdef _WIN32
+ struct iovec {
+ void *iov_base;
+ size_t iov_len;
+ };
+#else
+# include <sys/uio.h>
+#endif
+
+/* Allowed values for the mFlags parameter to sqlite3_carray_bind().
+** Must exactly match the definitions in carray.h.
+*/
+#ifndef CARRAY_INT32
+# define CARRAY_INT32 0 /* Data is 32-bit signed integers */
+# define CARRAY_INT64 1 /* Data is 64-bit signed integers */
+# define CARRAY_DOUBLE 2 /* Data is doubles */
+# define CARRAY_TEXT 3 /* Data is char* */
+# define CARRAY_BLOB 4 /* Data is struct iovec* */
+#endif
+
+#ifndef SQLITE_API
+# ifdef _WIN32
+# define SQLITE_API __declspec(dllexport)
+# else
+# define SQLITE_API
+# endif
+#endif
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+/*
+** Names of allowed datatypes
+*/
+static const char *azType[] = { "int32", "int64", "double", "char*",
+ "struct iovec" };
+
+/*
+** Structure used to hold the sqlite3_carray_bind() information
+*/
+typedef struct carray_bind carray_bind;
+struct carray_bind {
+ void *aData; /* The data */
+ int nData; /* Number of elements */
+ int mFlags; /* Control flags */
+ void (*xDel)(void*); /* Destructor for aData */
+};
+
+
+/* carray_cursor is a subclass of sqlite3_vtab_cursor which will
+** serve as the underlying representation of a cursor that scans
+** over rows of the result
+*/
+typedef struct carray_cursor carray_cursor;
+struct carray_cursor {
+ sqlite3_vtab_cursor base; /* Base class - must be first */
+ sqlite3_int64 iRowid; /* The rowid */
+ void *pPtr; /* Pointer to the array of values */
+ sqlite3_int64 iCnt; /* Number of integers in the array */
+ unsigned char eType; /* One of the CARRAY_type values */
+};
+
+/*
+** The carrayConnect() method is invoked to create a new
+** carray_vtab that describes the carray virtual table.
+**
+** Think of this routine as the constructor for carray_vtab objects.
+**
+** All this routine needs to do is:
+**
+** (1) Allocate the carray_vtab object and initialize all fields.
+**
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
+** result set of queries against carray will look like.
+*/
+static int carrayConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ sqlite3_vtab *pNew;
+ int rc;
+
+/* Column numbers */
+#define CARRAY_COLUMN_VALUE 0
+#define CARRAY_COLUMN_POINTER 1
+#define CARRAY_COLUMN_COUNT 2
+#define CARRAY_COLUMN_CTYPE 3
+
+ rc = sqlite3_declare_vtab(db,
+ "CREATE TABLE x(value,pointer hidden,count hidden,ctype hidden)");
+ if( rc==SQLITE_OK ){
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ }
+ return rc;
+}
+
+/*
+** This method is the destructor for carray_cursor objects.
+*/
+static int carrayDisconnect(sqlite3_vtab *pVtab){
+ sqlite3_free(pVtab);
+ return SQLITE_OK;
+}
+
+/*
+** Constructor for a new carray_cursor object.
+*/
+static int carrayOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ carray_cursor *pCur;
+ pCur = sqlite3_malloc( sizeof(*pCur) );
+ if( pCur==0 ) return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(*pCur));
+ *ppCursor = &pCur->base;
+ return SQLITE_OK;
+}
+
+/*
+** Destructor for a carray_cursor.
+*/
+static int carrayClose(sqlite3_vtab_cursor *cur){
+ sqlite3_free(cur);
+ return SQLITE_OK;
+}
+
+
+/*
+** Advance a carray_cursor to its next row of output.
+*/
+static int carrayNext(sqlite3_vtab_cursor *cur){
+ carray_cursor *pCur = (carray_cursor*)cur;
+ pCur->iRowid++;
+ return SQLITE_OK;
+}
+
+/*
+** Return values of columns for the row at which the carray_cursor
+** is currently pointing.
+*/
+static int carrayColumn(
+ sqlite3_vtab_cursor *cur, /* The cursor */
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
+ int i /* Which column to return */
+){
+ carray_cursor *pCur = (carray_cursor*)cur;
+ sqlite3_int64 x = 0;
+ switch( i ){
+ case CARRAY_COLUMN_POINTER: return SQLITE_OK;
+ case CARRAY_COLUMN_COUNT: x = pCur->iCnt; break;
+ case CARRAY_COLUMN_CTYPE: {
+ sqlite3_result_text(ctx, azType[pCur->eType], -1, SQLITE_STATIC);
+ return SQLITE_OK;
+ }
+ default: {
+ switch( pCur->eType ){
+ case CARRAY_INT32: {
+ int *p = (int*)pCur->pPtr;
+ sqlite3_result_int(ctx, p[pCur->iRowid-1]);
+ return SQLITE_OK;
+ }
+ case CARRAY_INT64: {
+ sqlite3_int64 *p = (sqlite3_int64*)pCur->pPtr;
+ sqlite3_result_int64(ctx, p[pCur->iRowid-1]);
+ return SQLITE_OK;
+ }
+ case CARRAY_DOUBLE: {
+ double *p = (double*)pCur->pPtr;
+ sqlite3_result_double(ctx, p[pCur->iRowid-1]);
+ return SQLITE_OK;
+ }
+ case CARRAY_TEXT: {
+ const char **p = (const char**)pCur->pPtr;
+ sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT);
+ return SQLITE_OK;
+ }
+ case CARRAY_BLOB: {
+ const struct iovec *p = (struct iovec*)pCur->pPtr;
+ sqlite3_result_blob(ctx, p[pCur->iRowid-1].iov_base,
+ (int)p[pCur->iRowid-1].iov_len, SQLITE_TRANSIENT);
+ return SQLITE_OK;
+ }
+ }
+ }
+ }
+ sqlite3_result_int64(ctx, x);
+ return SQLITE_OK;
+}
+
+/*
+** Return the rowid for the current row. In this implementation, the
+** rowid is the same as the output value.
+*/
+static int carrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ carray_cursor *pCur = (carray_cursor*)cur;
+ *pRowid = pCur->iRowid;
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the cursor has been moved off of the last
+** row of output.
+*/
+static int carrayEof(sqlite3_vtab_cursor *cur){
+ carray_cursor *pCur = (carray_cursor*)cur;
+ return pCur->iRowid>pCur->iCnt;
+}
+
+/*
+** This method is called to "rewind" the carray_cursor object back
+** to the first row of output.
+*/
+static int carrayFilter(
+ sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ carray_cursor *pCur = (carray_cursor *)pVtabCursor;
+ pCur->pPtr = 0;
+ pCur->iCnt = 0;
+ switch( idxNum ){
+ case 1: {
+ carray_bind *pBind = sqlite3_value_pointer(argv[0], "carray-bind");
+ if( pBind==0 ) break;
+ pCur->pPtr = pBind->aData;
+ pCur->iCnt = pBind->nData;
+ pCur->eType = pBind->mFlags & 0x07;
+ break;
+ }
+ case 2:
+ case 3: {
+ pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
+ pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
+ if( idxNum<3 ){
+ pCur->eType = CARRAY_INT32;
+ }else{
+ unsigned char i;
+ const char *zType = (const char*)sqlite3_value_text(argv[2]);
+ for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
+ if( sqlite3_stricmp(zType, azType[i])==0 ) break;
+ }
+ if( i>=sizeof(azType)/sizeof(azType[0]) ){
+ pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
+ "unknown datatype: %Q", zType);
+ return SQLITE_ERROR;
+ }else{
+ pCur->eType = i;
+ }
+ }
+ break;
+ }
+ }
+ pCur->iRowid = 1;
+ return SQLITE_OK;
+}
+
+/*
+** SQLite will invoke this method one or more times while planning a query
+** that uses the carray virtual table. This routine needs to create
+** a query plan for each invocation and compute an estimated cost for that
+** plan.
+**
+** In this implementation idxNum is used to represent the
+** query plan. idxStr is unused.
+**
+** idxNum is:
+**
+** 1 If only the pointer= constraint exists. In this case, the
+** parameter must be bound using sqlite3_carray_bind().
+**
+** 2 if the pointer= and count= constraints exist.
+**
+** 3 if the ctype= constraint also exists.
+**
+** idxNum is 0 otherwise and carray becomes an empty table.
+*/
+static int carrayBestIndex(
+ sqlite3_vtab *tab,
+ sqlite3_index_info *pIdxInfo
+){
+ int i; /* Loop over constraints */
+ int ptrIdx = -1; /* Index of the pointer= constraint, or -1 if none */
+ int cntIdx = -1; /* Index of the count= constraint, or -1 if none */
+ int ctypeIdx = -1; /* Index of the ctype= constraint, or -1 if none */
+
+ const struct sqlite3_index_constraint *pConstraint;
+ pConstraint = pIdxInfo->aConstraint;
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
+ if( pConstraint->usable==0 ) continue;
+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+ switch( pConstraint->iColumn ){
+ case CARRAY_COLUMN_POINTER:
+ ptrIdx = i;
+ break;
+ case CARRAY_COLUMN_COUNT:
+ cntIdx = i;
+ break;
+ case CARRAY_COLUMN_CTYPE:
+ ctypeIdx = i;
+ break;
+ }
+ }
+ if( ptrIdx>=0 ){
+ pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[ptrIdx].omit = 1;
+ pIdxInfo->estimatedCost = (double)1;
+ pIdxInfo->estimatedRows = 100;
+ pIdxInfo->idxNum = 1;
+ if( cntIdx>=0 ){
+ pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
+ pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
+ pIdxInfo->idxNum = 2;
+ if( ctypeIdx>=0 ){
+ pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
+ pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
+ pIdxInfo->idxNum = 3;
+ }
+ }
+ }else{
+ pIdxInfo->estimatedCost = (double)2147483647;
+ pIdxInfo->estimatedRows = 2147483647;
+ pIdxInfo->idxNum = 0;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** This following structure defines all the methods for the
+** carray virtual table.
+*/
+static sqlite3_module carrayModule = {
+ 0, /* iVersion */
+ 0, /* xCreate */
+ carrayConnect, /* xConnect */
+ carrayBestIndex, /* xBestIndex */
+ carrayDisconnect, /* xDisconnect */
+ 0, /* xDestroy */
+ carrayOpen, /* xOpen - open a cursor */
+ carrayClose, /* xClose - close a cursor */
+ carrayFilter, /* xFilter - configure scan constraints */
+ carrayNext, /* xNext - advance a cursor */
+ carrayEof, /* xEof - check for end of scan */
+ carrayColumn, /* xColumn - read data */
+ carrayRowid, /* xRowid - read data */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadow */
+ 0 /* xIntegrity */
+};
+
+/*
+** Destructor for the carray_bind object
+*/
+static void carrayBindDel(void *pPtr){
+ carray_bind *p = (carray_bind*)pPtr;
+ if( p->xDel!=SQLITE_STATIC ){
+ p->xDel(p->aData);
+ }
+ sqlite3_free(p);
+}
+
+/*
+** Invoke this interface in order to bind to the single-argument
+** version of CARRAY().
+*/
+SQLITE_API int sqlite3_carray_bind(
+ sqlite3_stmt *pStmt,
+ int idx,
+ void *aData,
+ int nData,
+ int mFlags,
+ void (*xDestroy)(void*)
+){
+ carray_bind *pNew;
+ int i;
+ pNew = sqlite3_malloc64(sizeof(*pNew));
+ if( pNew==0 ){
+ if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){
+ xDestroy(aData);
+ }
+ return SQLITE_NOMEM;
+ }
+ pNew->nData = nData;
+ pNew->mFlags = mFlags;
+ if( xDestroy==SQLITE_TRANSIENT ){
+ sqlite3_int64 sz = nData;
+ switch( mFlags & 0x07 ){
+ case CARRAY_INT32: sz *= 4; break;
+ case CARRAY_INT64: sz *= 8; break;
+ case CARRAY_DOUBLE: sz *= 8; break;
+ case CARRAY_TEXT: sz *= sizeof(char*); break;
+ case CARRAY_BLOB: sz *= sizeof(struct iovec); break;
+ }
+ if( (mFlags & 0x07)==CARRAY_TEXT ){
+ for(i=0; i<nData; i++){
+ const char *z = ((char**)aData)[i];
+ if( z ) sz += strlen(z) + 1;
+ }
+ }else if( (mFlags & 0x07)==CARRAY_BLOB ){
+ for(i=0; i<nData; i++){
+ sz += ((struct iovec*)aData)[i].iov_len;
+ }
+ }
+ pNew->aData = sqlite3_malloc64( sz );
+ if( pNew->aData==0 ){
+ sqlite3_free(pNew);
+ return SQLITE_NOMEM;
+ }
+ if( (mFlags & 0x07)==CARRAY_TEXT ){
+ char **az = (char**)pNew->aData;
+ char *z = (char*)&az[nData];
+ for(i=0; i<nData; i++){
+ const char *zData = ((char**)aData)[i];
+ sqlite3_int64 n;
+ if( zData==0 ){
+ az[i] = 0;
+ continue;
+ }
+ az[i] = z;
+ n = strlen(zData);
+ memcpy(z, zData, n+1);
+ z += n+1;
+ }
+ }else if( (mFlags & 0x07)==CARRAY_BLOB ){
+ struct iovec *p = (struct iovec*)pNew->aData;
+ unsigned char *z = (unsigned char*)&p[nData];
+ for(i=0; i<nData; i++){
+ size_t n = ((struct iovec*)aData)[i].iov_len;
+ p[i].iov_len = n;
+ p[i].iov_base = z;
+ z += n;
+ memcpy(p[i].iov_base, ((struct iovec*)aData)[i].iov_base, n);
+ }
+ }else{
+ memcpy(pNew->aData, aData, sz);
+ }
+ pNew->xDel = sqlite3_free;
+ }else{
+ pNew->aData = aData;
+ pNew->xDel = xDestroy;
+ }
+ return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel);
+}
+
+
+/*
+** For testing purpose in the TCL test harness, we need a method for
+** setting the pointer value. The inttoptr(X) SQL function accomplishes
+** this. Tcl script will bind an integer to X and the inttoptr() SQL
+** function will use sqlite3_result_pointer() to convert that integer into
+** a pointer.
+**
+** This is for testing on TCL only.
+*/
+#ifdef SQLITE_TEST
+static void inttoptrFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ void *p;
+ sqlite3_int64 i64;
+ i64 = sqlite3_value_int64(argv[0]);
+ if( sizeof(i64)==sizeof(p) ){
+ memcpy(&p, &i64, sizeof(p));
+ }else{
+ int i32 = i64 & 0xffffffff;
+ memcpy(&p, &i32, sizeof(p));
+ }
+ sqlite3_result_pointer(context, p, "carray", 0);
+}
+#endif /* SQLITE_TEST */
+
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+SQLITE_API int sqlite3_carray_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ rc = sqlite3_create_module(db, "carray", &carrayModule, 0);
+#ifdef SQLITE_TEST
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "inttoptr", 1, SQLITE_UTF8, 0,
+ inttoptrFunc, 0, 0);
+ }
+#endif /* SQLITE_TEST */
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+ return rc;
+}
diff --git a/src/libs/3rdparty/sqlite/config.h b/src/libs/3rdparty/sqlite/config.h
new file mode 100644
index 0000000000..f0e73be300
--- /dev/null
+++ b/src/libs/3rdparty/sqlite/config.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <string.h>
+
+#if __has_include(<unistd.h>)
+#include <unistd.h>
+#endif
+
+#if __has_include(<utime.h>)
+#define HAVE_UTIME 1
+#endif
+
+#if (_XOPEN_SOURCE >= 500) && !(_POSIX_C_SOURCE >= 200809L) || _DEFAULT_SOURCE || _BSD_SOURCE
+#define HAVE_USLEEP 1
+#endif
+
+#if _POSIX_C_SOURCE >= 199309L || _XOPEN_SOURCE >= 500
+#define HAVE_FDATASYNC 1
+#endif
+
+#if _POSIX_C_SOURCE >= 1 || _BSD_SOURCE
+#define HAVE_LOCALTIME_R 1
+#else
+#define HAVE_LOCALTIME_S 1
+#endif
+
+#define HAVE_MALLOC_USABLE_SIZE 1
+#define HAVE_ISNAN 1
+
+#define SQLITE_THREADSAFE 2
+#define SQLITE_ENABLE_FTS5 1
+#define SQLITE_ENABLE_UNLOCK_NOTIFY 1
+#define SQLITE_ENABLE_JSON1 1
+#define SQLITE_DEFAULT_FOREIGN_KEYS 1
+#define SQLITE_TEMP_STORE 3
+#define SQLITE_DEFAULT_WAL_SYNCHRONOUS 1
+#define SQLITE_MAX_WORKER_THREADS 8
+#define SQLITE_DEFAULT_MEMSTATUS 0
+#define SQLITE_OMIT_DEPRECATED 1
+#define SQLITE_OMIT_DECLTYPE 1
+#define SQLITE_MAX_EXPR_DEPTH 0
+#define SQLITE_OMIT_SHARED_CACHE 1
+#define SQLITE_USE_ALLOCA 1
+#define SQLITE_ENABLE_MEMORY_MANAGEMENT 1
+#define SQLITE_ENABLE_NULL_TRIM 1
+#define SQLITE_ALLOW_COVERING_INDEX_SCAN 1
+#define SQLITE_OMIT_EXPLAIN 1
+#define SQLITE_OMIT_LOAD_EXTENSION 1
+#define SQLITE_OMIT_UTF16 1
+#define SQLITE_DQS 0
+#define SQLITE_ENABLE_STAT4 1
+#define SQLITE_DEFAULT_MMAP_SIZE 268435456
+#define SQLITE_ENABLE_SESSION 1
+#define SQLITE_ENABLE_PREUPDATE_HOOK 1
+#define SQLITE_LIKE_DOESNT_MATCH_BLOBS 1
+#define SQLITE_OMIT_AUTOINIT 1
+#define SQLITE_DEFAULT_CACHE_SIZE -100000
+#define SQLITE_OMIT_AUTORESET 1
+#define SQLITE_OMIT_EXPLAIN 1
+#define SQLITE_OMIT_TRACE 1
+#define SQLITE_DEFAULT_LOCKING_MODE 1
+#define SQLITE_WIN32_GETVERSIONEX 0
diff --git a/src/libs/3rdparty/sqlite/okapi_bm25.h b/src/libs/3rdparty/sqlite/okapi_bm25.h
deleted file mode 100644
index d527012c19..0000000000
--- a/src/libs/3rdparty/sqlite/okapi_bm25.h
+++ /dev/null
@@ -1,231 +0,0 @@
-#include <math.h>
-#include <assert.h>
-#include "sqlite3.h"
-
-
-static void okapi_bm25(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal) {
- assert(sizeof(int) == 4);
-
- const unsigned int *matchinfo = (const unsigned int *)sqlite3_value_blob(apVal[0]);
- int searchTextCol = sqlite3_value_int(apVal[1]);
-
- double K1 = ((nVal >= 3) ? sqlite3_value_double(apVal[2]) : 1.2);
- double B = ((nVal >= 4) ? sqlite3_value_double(apVal[3]) : 0.75);
-
- int P_OFFSET = 0;
- int C_OFFSET = 1;
- int X_OFFSET = 2;
-
- int termCount = matchinfo[P_OFFSET];
- int colCount = matchinfo[C_OFFSET];
-
- int N_OFFSET = X_OFFSET + 3*termCount*colCount;
- int A_OFFSET = N_OFFSET + 1;
- int L_OFFSET = (A_OFFSET + colCount);
-
-
- double totalDocs = matchinfo[N_OFFSET];
- double avgLength = matchinfo[A_OFFSET + searchTextCol];
- double docLength = matchinfo[L_OFFSET + searchTextCol];
-
- double sum = 0.0;
-
- for (int i = 0; i < termCount; i++) {
- int currentX = X_OFFSET + (3 * searchTextCol * (i + 1));
- double termFrequency = matchinfo[currentX];
- double docsWithTerm = matchinfo[currentX + 2];
-
- double idf = log(
- (totalDocs - docsWithTerm + 0.5) /
- (docsWithTerm + 0.5)
- );
-
- double rightSide = (
- (termFrequency * (K1 + 1)) /
- (termFrequency + (K1 * (1 - B + (B * (docLength / avgLength)))))
- );
-
- sum += (idf * rightSide);
- }
-
- sqlite3_result_double(pCtx, sum);
-}
-
-//
-// Created by Joshua Wilson on 27/05/14.
-// Copyright (c) 2014 Joshua Wilson. All rights reserved.
-// https://github.com/neozenith/sqlite-okapi-bm25
-//
-// This is an extension to the work of "Radford 'rads' Smith"
-// found at: https://github.com/rads/sqlite-okapi-bm25
-// which is covered by the MIT License
-// http://opensource.org/licenses/MIT
-// the following code shall also be covered by the same MIT License
-
-static void okapi_bm25f(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal) {
- assert(sizeof(int) == 4);
-
- const unsigned int *matchinfo = (const unsigned int *)sqlite3_value_blob(apVal[0]);
-
-
- //Setting the default values and ignoring argument based inputs so the extra
- //arguments can be the column weights instead.
- double K1 = 1.2;// ((nVal >= 3) ? sqlite3_value_double(apVal[2]) : 1.2);
- double B = 0.75;// ((nVal >= 4) ? sqlite3_value_double(apVal[3]) : 0.75);
-
- //For a good explanation fo the maths and how to choose these variables
- //http://stackoverflow.com/a/23161886/622276
-
- //NOTE: the rearranged order of parameters to match the order presented on
- //SQLite3 FTS3 documentation 'pcxnals' (http://www.sqlite.org/fts3.html#matchinfo)
-
- int P_OFFSET = 0;
- int C_OFFSET = 1;
- int X_OFFSET = 2;
-
- int termCount = matchinfo[P_OFFSET];
- int colCount = matchinfo[C_OFFSET];
-
- int N_OFFSET = X_OFFSET + 3*termCount*colCount;
- int A_OFFSET = N_OFFSET + 1;
- int L_OFFSET = (A_OFFSET + colCount);
-// int S_OFFSET = (L_OFFSET + colCount); //useful as a pseudo proximity weighting per field/column
-
- double totalDocs = matchinfo[N_OFFSET];
-
- double avgLength = 0.0;
- double docLength = 0.0;
-
- for (int col = 0; col < colCount; col++)
- {
- avgLength += matchinfo[A_OFFSET + col];
- docLength += matchinfo[L_OFFSET + col];
- }
-
- double epsilon = 1.0 / (totalDocs*avgLength);
- double sum = 0.0;
-
- for (int t = 0; t < termCount; t++) {
- for (int col = 0 ; col < colCount; col++)
- {
- int currentX = X_OFFSET + (3 * col * (t + 1));
-
-
- double termFrequency = matchinfo[currentX];
- double docsWithTerm = matchinfo[currentX + 2];
-
- double idf = log(
- (totalDocs - docsWithTerm + 0.5) /
- (docsWithTerm + 0.5)
- );
- // "...terms appearing in more than half of the corpus will provide negative contributions to the final document score."
- //http://en.wikipedia.org/wiki/Okapi_BM25
-
- idf = (idf < 0) ? epsilon : idf; //common terms could have no effect (\epsilon=0.0) or a very small effect (\epsilon=1/NoOfTokens which asymptotes to 0.0)
-
- double rightSide = (
- (termFrequency * (K1 + 1)) /
- (termFrequency + (K1 * (1 - B + (B * (docLength / avgLength)))))
- );
-
- rightSide += 1.0;
- //To comply with BM25+ that solves a lower bounding issue where large documents that match are unfairly scored as
- //having similar relevancy as short documents that do not contain as many terms
- //Yuanhua Lv and ChengXiang Zhai. 'Lower-bounding term frequency normalization.' In Proceedings of CIKM'2011, pages 7-16.
- //http://sifaka.cs.uiuc.edu/~ylv2/pub/cikm11-lowerbound.pdf
-
- double weight = ((nVal > col+1) ? sqlite3_value_double(apVal[col+1]) : 1.0);
-
-// double subsequence = matchinfo[S_OFFSET + col];
-
- sum += (idf * rightSide) * weight; // * subsequence; //useful as a pseudo proximty weighting
- }
- }
-
- sqlite3_result_double(pCtx, sum);
-}
-
-static void okapi_bm25f_kb(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal) {
- assert(sizeof(int) == 4);
-
- const unsigned int *matchinfo = (const unsigned int *)sqlite3_value_blob(apVal[0]);
-
-
- //Setting the default values and ignoring argument based inputs so the extra
- //arguments can be the column weights instead.
- if (nVal < 2) sqlite3_result_error(pCtx, "wrong number of arguments to function okapi_bm25_kb(), expected k1 parameter", -1);
- if (nVal < 3) sqlite3_result_error(pCtx, "wrong number of arguments to function okapi_bm25_kb(), expected b parameter", -1);
- double K1 = sqlite3_value_double(apVal[1]);
- double B = sqlite3_value_double(apVal[2]);
-
- //For a good explanation fo the maths and how to choose these variables
- //http://stackoverflow.com/a/23161886/622276
-
- //NOTE: the rearranged order of parameters to match the order presented on
- //SQLite3 FTS3 documentation 'pcxnals' (http://www.sqlite.org/fts3.html#matchinfo)
-
- int P_OFFSET = 0;
- int C_OFFSET = 1;
- int X_OFFSET = 2;
-
- int termCount = matchinfo[P_OFFSET];
- int colCount = matchinfo[C_OFFSET];
-
- int N_OFFSET = X_OFFSET + 3*termCount*colCount;
- int A_OFFSET = N_OFFSET + 1;
- int L_OFFSET = (A_OFFSET + colCount);
- // int S_OFFSET = (L_OFFSET + colCount); //useful as a pseudo proximity weighting per field/column
-
- double totalDocs = matchinfo[N_OFFSET];
-
- double avgLength = 0.0;
- double docLength = 0.0;
-
- for (int col = 0; col < colCount; col++)
- {
- avgLength += matchinfo[A_OFFSET + col];
- docLength += matchinfo[L_OFFSET + col];
- }
-
- double epsilon = 1.0 / (totalDocs*avgLength);
- double sum = 0.0;
-
- for (int t = 0; t < termCount; t++) {
- for (int col = 0 ; col < colCount; col++)
- {
- int currentX = X_OFFSET + (3 * col * (t + 1));
-
-
- double termFrequency = matchinfo[currentX];
- double docsWithTerm = matchinfo[currentX + 2];
-
- double idf = log(
- (totalDocs - docsWithTerm + 0.5) /
- (docsWithTerm + 0.5)
- );
- // "...terms appearing in more than half of the corpus will provide negative contributions to the final document score."
- //http://en.wikipedia.org/wiki/Okapi_BM25
-
- idf = (idf < 0) ? epsilon : idf; //common terms could have no effect (\epsilon=0.0) or a very small effect (\epsilon=1/NoOfTokens which asymptotes to 0.0)
-
- double rightSide = (
- (termFrequency * (K1 + 1)) /
- (termFrequency + (K1 * (1 - B + (B * (docLength / avgLength)))))
- );
-
- rightSide += 1.0;
- //To comply with BM25+ that solves a lower bounding issue where large documents that match are unfairly scored as
- //having similar relevancy as short documents that do not contain as many terms
- //Yuanhua Lv and ChengXiang Zhai. 'Lower-bounding term frequency normalization.' In Proceedings of CIKM'2011, pages 7-16.
- //http://sifaka.cs.uiuc.edu/~ylv2/pub/cikm11-lowerbound.pdf
-
- double weight = ((nVal > col+3) ? sqlite3_value_double(apVal[col+3]) : 1.0);
-
- // double subsequence = matchinfo[S_OFFSET + col];
-
- sum += (idf * rightSide) * weight; // * subsequence; //useful as a pseudo proximty weighting
- }
- }
-
- sqlite3_result_double(pCtx, sum);
-}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/ksyntaxhighlighting_export.h b/src/libs/3rdparty/sqlite/sqlite.h
index a39adb5ed6..ba06815b4d 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/ksyntaxhighlighting_export.h
+++ b/src/libs/3rdparty/sqlite/sqlite.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
@@ -25,10 +25,5 @@
#pragma once
-#include <QtGlobal>
-
-#if defined(KSYNTAXHIGHLIGHTING_LIBRARY)
-# define KSYNTAXHIGHLIGHTING_EXPORT Q_DECL_EXPORT
-#else
-# define KSYNTAXHIGHLIGHTING_EXPORT Q_DECL_IMPORT
-#endif
+#include "config.h"
+#include "sqlite3ext.h"
diff --git a/src/libs/3rdparty/sqlite/sqlite.pri b/src/libs/3rdparty/sqlite/sqlite.pri
deleted file mode 100644
index 469a76955e..0000000000
--- a/src/libs/3rdparty/sqlite/sqlite.pri
+++ /dev/null
@@ -1,11 +0,0 @@
-INCLUDEPATH *= $$PWD
-
-HEADERS += $$PWD/okapi_bm25.h \
- $$PWD/sqlite3.h \
- $$PWD/sqlite3ext.h
-
-SOURCES += $$PWD/sqlite3.c
-
-gcc {
-QMAKE_CFLAGS_WARN_ON = -w
-}
diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c
index d015df2c31..139ee46a6a 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.c
+++ b/src/libs/3rdparty/sqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.26.0. By combining all the individual C code files into this
+** version 3.45.1. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -16,782 +16,15 @@
** if you want a wrapper to interface SQLite with your choice of programming
** language. The code for the "sqlite3" command-line shell is also in a
** separate file. This file contains only code for the core SQLite library.
+**
+** The content in this amalgamation comes from Fossil check-in
+** e876e51a0ed5c5b3126f52e532044363a014.
*/
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
#ifndef SQLITE_PRIVATE
# define SQLITE_PRIVATE static
#endif
-/************** Begin file ctime.c *******************************************/
-/*
-** 2010 February 23
-**
-** 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 routines used to report what compile-time options
-** SQLite was built with.
-*/
-
-#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
-
-/*
-** Include the configuration header output by 'configure' if we're using the
-** autoconf-based build
-*/
-#if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H)
-#include "config.h"
-#define SQLITECONFIG_H 1
-#endif
-
-/* These macros are provided to "stringify" the value of the define
-** for those options in which the value is meaningful. */
-#define CTIMEOPT_VAL_(opt) #opt
-#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt)
-
-/* Like CTIMEOPT_VAL, but especially for SQLITE_DEFAULT_LOOKASIDE. This
-** option requires a separate macro because legal values contain a single
-** comma. e.g. (-DSQLITE_DEFAULT_LOOKASIDE="100,100") */
-#define CTIMEOPT_VAL2_(opt1,opt2) #opt1 "," #opt2
-#define CTIMEOPT_VAL2(opt) CTIMEOPT_VAL2_(opt)
-
-/*
-** An array of names of all compile-time options. This array should
-** be sorted A-Z.
-**
-** This array looks large, but in a typical installation actually uses
-** only a handful of compile-time options, so most times this array is usually
-** rather short and uses little memory space.
-*/
-static const char * const sqlite3azCompileOpt[] = {
-
-/*
-** BEGIN CODE GENERATED BY tool/mkctime.tcl
-*/
-#if SQLITE_32BIT_ROWID
- "32BIT_ROWID",
-#endif
-#if SQLITE_4_BYTE_ALIGNED_MALLOC
- "4_BYTE_ALIGNED_MALLOC",
-#endif
-#if SQLITE_64BIT_STATS
- "64BIT_STATS",
-#endif
-#if SQLITE_ALLOW_COVERING_INDEX_SCAN
- "ALLOW_COVERING_INDEX_SCAN",
-#endif
-#if SQLITE_ALLOW_URI_AUTHORITY
- "ALLOW_URI_AUTHORITY",
-#endif
-#ifdef SQLITE_BITMASK_TYPE
- "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE),
-#endif
-#if SQLITE_BUG_COMPATIBLE_20160819
- "BUG_COMPATIBLE_20160819",
-#endif
-#if SQLITE_CASE_SENSITIVE_LIKE
- "CASE_SENSITIVE_LIKE",
-#endif
-#if SQLITE_CHECK_PAGES
- "CHECK_PAGES",
-#endif
-#if defined(__clang__) && defined(__clang_major__)
- "COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "."
- CTIMEOPT_VAL(__clang_minor__) "."
- CTIMEOPT_VAL(__clang_patchlevel__),
-#elif defined(_MSC_VER)
- "COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER),
-#elif defined(__GNUC__) && defined(__VERSION__)
- "COMPILER=gcc-" __VERSION__,
-#endif
-#if SQLITE_COVERAGE_TEST
- "COVERAGE_TEST",
-#endif
-#if SQLITE_DEBUG
- "DEBUG",
-#endif
-#if SQLITE_DEFAULT_AUTOMATIC_INDEX
- "DEFAULT_AUTOMATIC_INDEX",
-#endif
-#if SQLITE_DEFAULT_AUTOVACUUM
- "DEFAULT_AUTOVACUUM",
-#endif
-#ifdef SQLITE_DEFAULT_CACHE_SIZE
- "DEFAULT_CACHE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_CACHE_SIZE),
-#endif
-#if SQLITE_DEFAULT_CKPTFULLFSYNC
- "DEFAULT_CKPTFULLFSYNC",
-#endif
-#ifdef SQLITE_DEFAULT_FILE_FORMAT
- "DEFAULT_FILE_FORMAT=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_FORMAT),
-#endif
-#ifdef SQLITE_DEFAULT_FILE_PERMISSIONS
- "DEFAULT_FILE_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_PERMISSIONS),
-#endif
-#if SQLITE_DEFAULT_FOREIGN_KEYS
- "DEFAULT_FOREIGN_KEYS",
-#endif
-#ifdef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT
- "DEFAULT_JOURNAL_SIZE_LIMIT=" CTIMEOPT_VAL(SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT),
-#endif
-#ifdef SQLITE_DEFAULT_LOCKING_MODE
- "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE),
-#endif
-#ifdef SQLITE_DEFAULT_LOOKASIDE
- "DEFAULT_LOOKASIDE=" CTIMEOPT_VAL2(SQLITE_DEFAULT_LOOKASIDE),
-#endif
-#if SQLITE_DEFAULT_MEMSTATUS
- "DEFAULT_MEMSTATUS",
-#endif
-#ifdef SQLITE_DEFAULT_MMAP_SIZE
- "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE),
-#endif
-#ifdef SQLITE_DEFAULT_PAGE_SIZE
- "DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_PAGE_SIZE),
-#endif
-#ifdef SQLITE_DEFAULT_PCACHE_INITSZ
- "DEFAULT_PCACHE_INITSZ=" CTIMEOPT_VAL(SQLITE_DEFAULT_PCACHE_INITSZ),
-#endif
-#ifdef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS
- "DEFAULT_PROXYDIR_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_PROXYDIR_PERMISSIONS),
-#endif
-#if SQLITE_DEFAULT_RECURSIVE_TRIGGERS
- "DEFAULT_RECURSIVE_TRIGGERS",
-#endif
-#ifdef SQLITE_DEFAULT_ROWEST
- "DEFAULT_ROWEST=" CTIMEOPT_VAL(SQLITE_DEFAULT_ROWEST),
-#endif
-#ifdef SQLITE_DEFAULT_SECTOR_SIZE
- "DEFAULT_SECTOR_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_SECTOR_SIZE),
-#endif
-#ifdef SQLITE_DEFAULT_SYNCHRONOUS
- "DEFAULT_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_SYNCHRONOUS),
-#endif
-#ifdef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT
- "DEFAULT_WAL_AUTOCHECKPOINT=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_AUTOCHECKPOINT),
-#endif
-#ifdef SQLITE_DEFAULT_WAL_SYNCHRONOUS
- "DEFAULT_WAL_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_SYNCHRONOUS),
-#endif
-#ifdef SQLITE_DEFAULT_WORKER_THREADS
- "DEFAULT_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WORKER_THREADS),
-#endif
-#if SQLITE_DIRECT_OVERFLOW_READ
- "DIRECT_OVERFLOW_READ",
-#endif
-#if SQLITE_DISABLE_DIRSYNC
- "DISABLE_DIRSYNC",
-#endif
-#if SQLITE_DISABLE_FTS3_UNICODE
- "DISABLE_FTS3_UNICODE",
-#endif
-#if SQLITE_DISABLE_FTS4_DEFERRED
- "DISABLE_FTS4_DEFERRED",
-#endif
-#if SQLITE_DISABLE_INTRINSIC
- "DISABLE_INTRINSIC",
-#endif
-#if SQLITE_DISABLE_LFS
- "DISABLE_LFS",
-#endif
-#if SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
- "DISABLE_PAGECACHE_OVERFLOW_STATS",
-#endif
-#if SQLITE_DISABLE_SKIPAHEAD_DISTINCT
- "DISABLE_SKIPAHEAD_DISTINCT",
-#endif
-#ifdef SQLITE_ENABLE_8_3_NAMES
- "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES),
-#endif
-#if SQLITE_ENABLE_API_ARMOR
- "ENABLE_API_ARMOR",
-#endif
-#if SQLITE_ENABLE_ATOMIC_WRITE
- "ENABLE_ATOMIC_WRITE",
-#endif
-#if SQLITE_ENABLE_BATCH_ATOMIC_WRITE
- "ENABLE_BATCH_ATOMIC_WRITE",
-#endif
-#if SQLITE_ENABLE_CEROD
- "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
-#endif
-#if SQLITE_ENABLE_COLUMN_METADATA
- "ENABLE_COLUMN_METADATA",
-#endif
-#if SQLITE_ENABLE_COLUMN_USED_MASK
- "ENABLE_COLUMN_USED_MASK",
-#endif
-#if SQLITE_ENABLE_COSTMULT
- "ENABLE_COSTMULT",
-#endif
-#if SQLITE_ENABLE_CURSOR_HINTS
- "ENABLE_CURSOR_HINTS",
-#endif
-#if SQLITE_ENABLE_DBSTAT_VTAB
- "ENABLE_DBSTAT_VTAB",
-#endif
-#if SQLITE_ENABLE_EXPENSIVE_ASSERT
- "ENABLE_EXPENSIVE_ASSERT",
-#endif
-#if SQLITE_ENABLE_FTS1
- "ENABLE_FTS1",
-#endif
-#if SQLITE_ENABLE_FTS2
- "ENABLE_FTS2",
-#endif
-#if SQLITE_ENABLE_FTS3
- "ENABLE_FTS3",
-#endif
-#if SQLITE_ENABLE_FTS3_PARENTHESIS
- "ENABLE_FTS3_PARENTHESIS",
-#endif
-#if SQLITE_ENABLE_FTS3_TOKENIZER
- "ENABLE_FTS3_TOKENIZER",
-#endif
-#if SQLITE_ENABLE_FTS4
- "ENABLE_FTS4",
-#endif
-#if SQLITE_ENABLE_FTS5
- "ENABLE_FTS5",
-#endif
-#if SQLITE_ENABLE_GEOPOLY
- "ENABLE_GEOPOLY",
-#endif
-#if SQLITE_ENABLE_HIDDEN_COLUMNS
- "ENABLE_HIDDEN_COLUMNS",
-#endif
-#if SQLITE_ENABLE_ICU
- "ENABLE_ICU",
-#endif
-#if SQLITE_ENABLE_IOTRACE
- "ENABLE_IOTRACE",
-#endif
-#if SQLITE_ENABLE_JSON1
- "ENABLE_JSON1",
-#endif
-#if SQLITE_ENABLE_LOAD_EXTENSION
- "ENABLE_LOAD_EXTENSION",
-#endif
-#ifdef SQLITE_ENABLE_LOCKING_STYLE
- "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE),
-#endif
-#if SQLITE_ENABLE_MEMORY_MANAGEMENT
- "ENABLE_MEMORY_MANAGEMENT",
-#endif
-#if SQLITE_ENABLE_MEMSYS3
- "ENABLE_MEMSYS3",
-#endif
-#if SQLITE_ENABLE_MEMSYS5
- "ENABLE_MEMSYS5",
-#endif
-#if SQLITE_ENABLE_MULTIPLEX
- "ENABLE_MULTIPLEX",
-#endif
-#if SQLITE_ENABLE_NORMALIZE
- "ENABLE_NORMALIZE",
-#endif
-#if SQLITE_ENABLE_NULL_TRIM
- "ENABLE_NULL_TRIM",
-#endif
-#if SQLITE_ENABLE_OVERSIZE_CELL_CHECK
- "ENABLE_OVERSIZE_CELL_CHECK",
-#endif
-#if SQLITE_ENABLE_PREUPDATE_HOOK
- "ENABLE_PREUPDATE_HOOK",
-#endif
-#if SQLITE_ENABLE_QPSG
- "ENABLE_QPSG",
-#endif
-#if SQLITE_ENABLE_RBU
- "ENABLE_RBU",
-#endif
-#if SQLITE_ENABLE_RTREE
- "ENABLE_RTREE",
-#endif
-#if SQLITE_ENABLE_SELECTTRACE
- "ENABLE_SELECTTRACE",
-#endif
-#if SQLITE_ENABLE_SESSION
- "ENABLE_SESSION",
-#endif
-#if SQLITE_ENABLE_SNAPSHOT
- "ENABLE_SNAPSHOT",
-#endif
-#if SQLITE_ENABLE_SORTER_REFERENCES
- "ENABLE_SORTER_REFERENCES",
-#endif
-#if SQLITE_ENABLE_SQLLOG
- "ENABLE_SQLLOG",
-#endif
-#if defined(SQLITE_ENABLE_STAT4)
- "ENABLE_STAT4",
-#elif defined(SQLITE_ENABLE_STAT3)
- "ENABLE_STAT3",
-#endif
-#if SQLITE_ENABLE_STMTVTAB
- "ENABLE_STMTVTAB",
-#endif
-#if SQLITE_ENABLE_STMT_SCANSTATUS
- "ENABLE_STMT_SCANSTATUS",
-#endif
-#if SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
- "ENABLE_UNKNOWN_SQL_FUNCTION",
-#endif
-#if SQLITE_ENABLE_UNLOCK_NOTIFY
- "ENABLE_UNLOCK_NOTIFY",
-#endif
-#if SQLITE_ENABLE_UPDATE_DELETE_LIMIT
- "ENABLE_UPDATE_DELETE_LIMIT",
-#endif
-#if SQLITE_ENABLE_URI_00_ERROR
- "ENABLE_URI_00_ERROR",
-#endif
-#if SQLITE_ENABLE_VFSTRACE
- "ENABLE_VFSTRACE",
-#endif
-#if SQLITE_ENABLE_WHERETRACE
- "ENABLE_WHERETRACE",
-#endif
-#if SQLITE_ENABLE_ZIPVFS
- "ENABLE_ZIPVFS",
-#endif
-#if SQLITE_EXPLAIN_ESTIMATED_ROWS
- "EXPLAIN_ESTIMATED_ROWS",
-#endif
-#if SQLITE_EXTRA_IFNULLROW
- "EXTRA_IFNULLROW",
-#endif
-#ifdef SQLITE_EXTRA_INIT
- "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT),
-#endif
-#ifdef SQLITE_EXTRA_SHUTDOWN
- "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN),
-#endif
-#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH
- "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH),
-#endif
-#if SQLITE_FTS5_ENABLE_TEST_MI
- "FTS5_ENABLE_TEST_MI",
-#endif
-#if SQLITE_FTS5_NO_WITHOUT_ROWID
- "FTS5_NO_WITHOUT_ROWID",
-#endif
-#if SQLITE_HAS_CODEC
- "HAS_CODEC",
-#endif
-#if HAVE_ISNAN || SQLITE_HAVE_ISNAN
- "HAVE_ISNAN",
-#endif
-#if SQLITE_HOMEGROWN_RECURSIVE_MUTEX
- "HOMEGROWN_RECURSIVE_MUTEX",
-#endif
-#if SQLITE_IGNORE_AFP_LOCK_ERRORS
- "IGNORE_AFP_LOCK_ERRORS",
-#endif
-#if SQLITE_IGNORE_FLOCK_LOCK_ERRORS
- "IGNORE_FLOCK_LOCK_ERRORS",
-#endif
-#if SQLITE_INLINE_MEMCPY
- "INLINE_MEMCPY",
-#endif
-#if SQLITE_INT64_TYPE
- "INT64_TYPE",
-#endif
-#ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX
- "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX),
-#endif
-#if SQLITE_LIKE_DOESNT_MATCH_BLOBS
- "LIKE_DOESNT_MATCH_BLOBS",
-#endif
-#if SQLITE_LOCK_TRACE
- "LOCK_TRACE",
-#endif
-#if SQLITE_LOG_CACHE_SPILL
- "LOG_CACHE_SPILL",
-#endif
-#ifdef SQLITE_MALLOC_SOFT_LIMIT
- "MALLOC_SOFT_LIMIT=" CTIMEOPT_VAL(SQLITE_MALLOC_SOFT_LIMIT),
-#endif
-#ifdef SQLITE_MAX_ATTACHED
- "MAX_ATTACHED=" CTIMEOPT_VAL(SQLITE_MAX_ATTACHED),
-#endif
-#ifdef SQLITE_MAX_COLUMN
- "MAX_COLUMN=" CTIMEOPT_VAL(SQLITE_MAX_COLUMN),
-#endif
-#ifdef SQLITE_MAX_COMPOUND_SELECT
- "MAX_COMPOUND_SELECT=" CTIMEOPT_VAL(SQLITE_MAX_COMPOUND_SELECT),
-#endif
-#ifdef SQLITE_MAX_DEFAULT_PAGE_SIZE
- "MAX_DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_DEFAULT_PAGE_SIZE),
-#endif
-#ifdef SQLITE_MAX_EXPR_DEPTH
- "MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_EXPR_DEPTH),
-#endif
-#ifdef SQLITE_MAX_FUNCTION_ARG
- "MAX_FUNCTION_ARG=" CTIMEOPT_VAL(SQLITE_MAX_FUNCTION_ARG),
-#endif
-#ifdef SQLITE_MAX_LENGTH
- "MAX_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LENGTH),
-#endif
-#ifdef SQLITE_MAX_LIKE_PATTERN_LENGTH
- "MAX_LIKE_PATTERN_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LIKE_PATTERN_LENGTH),
-#endif
-#ifdef SQLITE_MAX_MEMORY
- "MAX_MEMORY=" CTIMEOPT_VAL(SQLITE_MAX_MEMORY),
-#endif
-#ifdef SQLITE_MAX_MMAP_SIZE
- "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE),
-#endif
-#ifdef SQLITE_MAX_MMAP_SIZE_
- "MAX_MMAP_SIZE_=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE_),
-#endif
-#ifdef SQLITE_MAX_PAGE_COUNT
- "MAX_PAGE_COUNT=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_COUNT),
-#endif
-#ifdef SQLITE_MAX_PAGE_SIZE
- "MAX_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_SIZE),
-#endif
-#ifdef SQLITE_MAX_SCHEMA_RETRY
- "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY),
-#endif
-#ifdef SQLITE_MAX_SQL_LENGTH
- "MAX_SQL_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_SQL_LENGTH),
-#endif
-#ifdef SQLITE_MAX_TRIGGER_DEPTH
- "MAX_TRIGGER_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_TRIGGER_DEPTH),
-#endif
-#ifdef SQLITE_MAX_VARIABLE_NUMBER
- "MAX_VARIABLE_NUMBER=" CTIMEOPT_VAL(SQLITE_MAX_VARIABLE_NUMBER),
-#endif
-#ifdef SQLITE_MAX_VDBE_OP
- "MAX_VDBE_OP=" CTIMEOPT_VAL(SQLITE_MAX_VDBE_OP),
-#endif
-#ifdef SQLITE_MAX_WORKER_THREADS
- "MAX_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_MAX_WORKER_THREADS),
-#endif
-#if SQLITE_MEMDEBUG
- "MEMDEBUG",
-#endif
-#if SQLITE_MIXED_ENDIAN_64BIT_FLOAT
- "MIXED_ENDIAN_64BIT_FLOAT",
-#endif
-#if SQLITE_MMAP_READWRITE
- "MMAP_READWRITE",
-#endif
-#if SQLITE_MUTEX_NOOP
- "MUTEX_NOOP",
-#endif
-#if SQLITE_MUTEX_NREF
- "MUTEX_NREF",
-#endif
-#if SQLITE_MUTEX_OMIT
- "MUTEX_OMIT",
-#endif
-#if SQLITE_MUTEX_PTHREADS
- "MUTEX_PTHREADS",
-#endif
-#if SQLITE_MUTEX_W32
- "MUTEX_W32",
-#endif
-#if SQLITE_NEED_ERR_NAME
- "NEED_ERR_NAME",
-#endif
-#if SQLITE_NOINLINE
- "NOINLINE",
-#endif
-#if SQLITE_NO_SYNC
- "NO_SYNC",
-#endif
-#if SQLITE_OMIT_ALTERTABLE
- "OMIT_ALTERTABLE",
-#endif
-#if SQLITE_OMIT_ANALYZE
- "OMIT_ANALYZE",
-#endif
-#if SQLITE_OMIT_ATTACH
- "OMIT_ATTACH",
-#endif
-#if SQLITE_OMIT_AUTHORIZATION
- "OMIT_AUTHORIZATION",
-#endif
-#if SQLITE_OMIT_AUTOINCREMENT
- "OMIT_AUTOINCREMENT",
-#endif
-#if SQLITE_OMIT_AUTOINIT
- "OMIT_AUTOINIT",
-#endif
-#if SQLITE_OMIT_AUTOMATIC_INDEX
- "OMIT_AUTOMATIC_INDEX",
-#endif
-#if SQLITE_OMIT_AUTORESET
- "OMIT_AUTORESET",
-#endif
-#if SQLITE_OMIT_AUTOVACUUM
- "OMIT_AUTOVACUUM",
-#endif
-#if SQLITE_OMIT_BETWEEN_OPTIMIZATION
- "OMIT_BETWEEN_OPTIMIZATION",
-#endif
-#if SQLITE_OMIT_BLOB_LITERAL
- "OMIT_BLOB_LITERAL",
-#endif
-#if SQLITE_OMIT_BTREECOUNT
- "OMIT_BTREECOUNT",
-#endif
-#if SQLITE_OMIT_CAST
- "OMIT_CAST",
-#endif
-#if SQLITE_OMIT_CHECK
- "OMIT_CHECK",
-#endif
-#if SQLITE_OMIT_COMPLETE
- "OMIT_COMPLETE",
-#endif
-#if SQLITE_OMIT_COMPOUND_SELECT
- "OMIT_COMPOUND_SELECT",
-#endif
-#if SQLITE_OMIT_CONFLICT_CLAUSE
- "OMIT_CONFLICT_CLAUSE",
-#endif
-#if SQLITE_OMIT_CTE
- "OMIT_CTE",
-#endif
-#if SQLITE_OMIT_DATETIME_FUNCS
- "OMIT_DATETIME_FUNCS",
-#endif
-#if SQLITE_OMIT_DECLTYPE
- "OMIT_DECLTYPE",
-#endif
-#if SQLITE_OMIT_DEPRECATED
- "OMIT_DEPRECATED",
-#endif
-#if SQLITE_OMIT_DISKIO
- "OMIT_DISKIO",
-#endif
-#if SQLITE_OMIT_EXPLAIN
- "OMIT_EXPLAIN",
-#endif
-#if SQLITE_OMIT_FLAG_PRAGMAS
- "OMIT_FLAG_PRAGMAS",
-#endif
-#if SQLITE_OMIT_FLOATING_POINT
- "OMIT_FLOATING_POINT",
-#endif
-#if SQLITE_OMIT_FOREIGN_KEY
- "OMIT_FOREIGN_KEY",
-#endif
-#if SQLITE_OMIT_GET_TABLE
- "OMIT_GET_TABLE",
-#endif
-#if SQLITE_OMIT_HEX_INTEGER
- "OMIT_HEX_INTEGER",
-#endif
-#if SQLITE_OMIT_INCRBLOB
- "OMIT_INCRBLOB",
-#endif
-#if SQLITE_OMIT_INTEGRITY_CHECK
- "OMIT_INTEGRITY_CHECK",
-#endif
-#if SQLITE_OMIT_LIKE_OPTIMIZATION
- "OMIT_LIKE_OPTIMIZATION",
-#endif
-#if SQLITE_OMIT_LOAD_EXTENSION
- "OMIT_LOAD_EXTENSION",
-#endif
-#if SQLITE_OMIT_LOCALTIME
- "OMIT_LOCALTIME",
-#endif
-#if SQLITE_OMIT_LOOKASIDE
- "OMIT_LOOKASIDE",
-#endif
-#if SQLITE_OMIT_MEMORYDB
- "OMIT_MEMORYDB",
-#endif
-#if SQLITE_OMIT_OR_OPTIMIZATION
- "OMIT_OR_OPTIMIZATION",
-#endif
-#if SQLITE_OMIT_PAGER_PRAGMAS
- "OMIT_PAGER_PRAGMAS",
-#endif
-#if SQLITE_OMIT_PARSER_TRACE
- "OMIT_PARSER_TRACE",
-#endif
-#if SQLITE_OMIT_POPEN
- "OMIT_POPEN",
-#endif
-#if SQLITE_OMIT_PRAGMA
- "OMIT_PRAGMA",
-#endif
-#if SQLITE_OMIT_PROGRESS_CALLBACK
- "OMIT_PROGRESS_CALLBACK",
-#endif
-#if SQLITE_OMIT_QUICKBALANCE
- "OMIT_QUICKBALANCE",
-#endif
-#if SQLITE_OMIT_REINDEX
- "OMIT_REINDEX",
-#endif
-#if SQLITE_OMIT_SCHEMA_PRAGMAS
- "OMIT_SCHEMA_PRAGMAS",
-#endif
-#if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
- "OMIT_SCHEMA_VERSION_PRAGMAS",
-#endif
-#if SQLITE_OMIT_SHARED_CACHE
- "OMIT_SHARED_CACHE",
-#endif
-#if SQLITE_OMIT_SHUTDOWN_DIRECTORIES
- "OMIT_SHUTDOWN_DIRECTORIES",
-#endif
-#if SQLITE_OMIT_SUBQUERY
- "OMIT_SUBQUERY",
-#endif
-#if SQLITE_OMIT_TCL_VARIABLE
- "OMIT_TCL_VARIABLE",
-#endif
-#if SQLITE_OMIT_TEMPDB
- "OMIT_TEMPDB",
-#endif
-#if SQLITE_OMIT_TEST_CONTROL
- "OMIT_TEST_CONTROL",
-#endif
-#if SQLITE_OMIT_TRACE
- "OMIT_TRACE",
-#endif
-#if SQLITE_OMIT_TRIGGER
- "OMIT_TRIGGER",
-#endif
-#if SQLITE_OMIT_TRUNCATE_OPTIMIZATION
- "OMIT_TRUNCATE_OPTIMIZATION",
-#endif
-#if SQLITE_OMIT_UTF16
- "OMIT_UTF16",
-#endif
-#if SQLITE_OMIT_VACUUM
- "OMIT_VACUUM",
-#endif
-#if SQLITE_OMIT_VIEW
- "OMIT_VIEW",
-#endif
-#if SQLITE_OMIT_VIRTUALTABLE
- "OMIT_VIRTUALTABLE",
-#endif
-#if SQLITE_OMIT_WAL
- "OMIT_WAL",
-#endif
-#if SQLITE_OMIT_WSD
- "OMIT_WSD",
-#endif
-#if SQLITE_OMIT_XFER_OPT
- "OMIT_XFER_OPT",
-#endif
-#if SQLITE_PCACHE_SEPARATE_HEADER
- "PCACHE_SEPARATE_HEADER",
-#endif
-#if SQLITE_PERFORMANCE_TRACE
- "PERFORMANCE_TRACE",
-#endif
-#if SQLITE_POWERSAFE_OVERWRITE
- "POWERSAFE_OVERWRITE",
-#endif
-#if SQLITE_PREFER_PROXY_LOCKING
- "PREFER_PROXY_LOCKING",
-#endif
-#if SQLITE_PROXY_DEBUG
- "PROXY_DEBUG",
-#endif
-#if SQLITE_REVERSE_UNORDERED_SELECTS
- "REVERSE_UNORDERED_SELECTS",
-#endif
-#if SQLITE_RTREE_INT_ONLY
- "RTREE_INT_ONLY",
-#endif
-#if SQLITE_SECURE_DELETE
- "SECURE_DELETE",
-#endif
-#if SQLITE_SMALL_STACK
- "SMALL_STACK",
-#endif
-#ifdef SQLITE_SORTER_PMASZ
- "SORTER_PMASZ=" CTIMEOPT_VAL(SQLITE_SORTER_PMASZ),
-#endif
-#if SQLITE_SOUNDEX
- "SOUNDEX",
-#endif
-#ifdef SQLITE_STAT4_SAMPLES
- "STAT4_SAMPLES=" CTIMEOPT_VAL(SQLITE_STAT4_SAMPLES),
-#endif
-#ifdef SQLITE_STMTJRNL_SPILL
- "STMTJRNL_SPILL=" CTIMEOPT_VAL(SQLITE_STMTJRNL_SPILL),
-#endif
-#if SQLITE_SUBSTR_COMPATIBILITY
- "SUBSTR_COMPATIBILITY",
-#endif
-#if SQLITE_SYSTEM_MALLOC
- "SYSTEM_MALLOC",
-#endif
-#if SQLITE_TCL
- "TCL",
-#endif
-#ifdef SQLITE_TEMP_STORE
- "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE),
-#endif
-#if SQLITE_TEST
- "TEST",
-#endif
-#if defined(SQLITE_THREADSAFE)
- "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE),
-#elif defined(THREADSAFE)
- "THREADSAFE=" CTIMEOPT_VAL(THREADSAFE),
-#else
- "THREADSAFE=1",
-#endif
-#if SQLITE_UNLINK_AFTER_CLOSE
- "UNLINK_AFTER_CLOSE",
-#endif
-#if SQLITE_UNTESTABLE
- "UNTESTABLE",
-#endif
-#if SQLITE_USER_AUTHENTICATION
- "USER_AUTHENTICATION",
-#endif
-#if SQLITE_USE_ALLOCA
- "USE_ALLOCA",
-#endif
-#if SQLITE_USE_FCNTL_TRACE
- "USE_FCNTL_TRACE",
-#endif
-#if SQLITE_USE_URI
- "USE_URI",
-#endif
-#if SQLITE_VDBE_COVERAGE
- "VDBE_COVERAGE",
-#endif
-#if SQLITE_WIN32_MALLOC
- "WIN32_MALLOC",
-#endif
-#if SQLITE_ZERO_MALLOC
- "ZERO_MALLOC",
-#endif
-/*
-** END CODE GENERATED BY tool/mkctime.tcl
-*/
-};
-
-SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt){
- *pnOpt = sizeof(sqlite3azCompileOpt) / sizeof(sqlite3azCompileOpt[0]);
- return (const char**)sqlite3azCompileOpt;
-}
-
-#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
-
-/************** End of ctime.c ***********************************************/
/************** Begin file sqliteInt.h ***************************************/
/*
** 2001 September 15
@@ -820,20 +53,20 @@ SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt){
** used on lines of code that actually
** implement parts of coverage testing.
**
-** OPTIMIZATION-IF-TRUE - This branch is allowed to alway be false
+** OPTIMIZATION-IF-TRUE - This branch is allowed to always be false
** and the correct answer is still obtained,
** though perhaps more slowly.
**
-** OPTIMIZATION-IF-FALSE - This branch is allowed to alway be true
+** OPTIMIZATION-IF-FALSE - This branch is allowed to always be true
** and the correct answer is still obtained,
** though perhaps more slowly.
**
** PREVENTS-HARMLESS-OVERREAD - This branch prevents a buffer overread
** that would be harmless and undetectable
-** if it did occur.
+** if it did occur.
**
** In all cases, the special comment must be enclosed in the usual
-** slash-asterisk...asterisk-slash comment marks, with no spaces between the
+** slash-asterisk...asterisk-slash comment marks, with no spaces between the
** asterisks and the comment text.
*/
@@ -888,6 +121,15 @@ SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt){
#pragma warning(disable : 4706)
#endif /* defined(_MSC_VER) */
+#if defined(_MSC_VER) && !defined(_WIN64)
+#undef SQLITE_4_BYTE_ALIGNED_MALLOC
+#define SQLITE_4_BYTE_ALIGNED_MALLOC
+#endif /* defined(_MSC_VER) && !defined(_WIN64) */
+
+#if !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800
+#define HAVE_LOG2 0
+#endif /* !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800 */
+
#endif /* SQLITE_MSVC_H */
/************** End of msvc.h ************************************************/
@@ -990,6 +232,18 @@ SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt){
# define MSVC_VERSION 0
#endif
+/*
+** Some C99 functions in "math.h" are only present for MSVC when its version
+** is associated with Visual Studio 2013 or higher.
+*/
+#ifndef SQLITE_HAVE_C99_MATH_FUNCS
+# if MSVC_VERSION==0 || MSVC_VERSION>=1800
+# define SQLITE_HAVE_C99_MATH_FUNCS (1)
+# else
+# define SQLITE_HAVE_C99_MATH_FUNCS (0)
+# endif
+#endif
+
/* Needed for various definitions... */
#if defined(__GNUC__) && !defined(_GNU_SOURCE)
# define _GNU_SOURCE
@@ -1000,6 +254,15 @@ SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt){
#endif
/*
+** Macro to disable warnings about missing "break" at the end of a "case".
+*/
+#if GCC_VERSION>=7000000
+# define deliberate_fall_through __attribute__((fallthrough));
+#else
+# define deliberate_fall_through
+#endif
+
+/*
** For MinGW, check to see if we can include the header file containing its
** version information, among other things. Normally, this internal MinGW
** header file would [only] be included automatically by other MinGW header
@@ -1031,6 +294,17 @@ SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt){
# define _USE_32BIT_TIME_T
#endif
+/* Optionally #include a user-defined header, whereby compilation options
+** may be set prior to where they take effect, but after platform setup.
+** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include
+** file.
+*/
+#ifdef SQLITE_CUSTOM_INCLUDE
+# define INC_STRINGIFY_(f) #f
+# define INC_STRINGIFY(f) INC_STRINGIFY_(f)
+# include INC_STRINGIFY(SQLITE_CUSTOM_INCLUDE)
+#endif
+
/* The public SQLite interface. The _FILE_OFFSET_BITS macro must appear
** first in QNX. Also, the _USE_32BIT_TIME_T macro must appear first for
** MinGW.
@@ -1082,7 +356,30 @@ extern "C" {
/*
-** Provide the ability to override linkage features of the interface.
+** Facilitate override of interface linkage and calling conventions.
+** Be aware that these macros may not be used within this particular
+** translation of the amalgamation and its associated header file.
+**
+** The SQLITE_EXTERN and SQLITE_API macros are used to instruct the
+** compiler that the target identifier should have external linkage.
+**
+** The SQLITE_CDECL macro is used to set the calling convention for
+** public functions that accept a variable number of arguments.
+**
+** The SQLITE_APICALL macro is used to set the calling convention for
+** public functions that accept a fixed number of arguments.
+**
+** The SQLITE_STDCALL macro is no longer used and is now deprecated.
+**
+** The SQLITE_CALLBACK macro is used to set the calling convention for
+** function pointers.
+**
+** The SQLITE_SYSAPI macro is used to set the calling convention for
+** functions provided by the operating system.
+**
+** Currently, the SQLITE_CDECL, SQLITE_APICALL, SQLITE_CALLBACK, and
+** SQLITE_SYSAPI macros are used only when building for environments
+** that require non-default calling conventions.
*/
#ifndef SQLITE_EXTERN
# define SQLITE_EXTERN extern
@@ -1147,7 +444,7 @@ extern "C" {
** be held constant and Z will be incremented or else Y will be incremented
** and Z will be reset to zero.
**
-** Since [version 3.6.18] ([dateof:3.6.18]),
+** Since [version 3.6.18] ([dateof:3.6.18]),
** SQLite source code has been stored in the
** <a href="http://www.fossil-scm.org/">Fossil configuration management
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
@@ -1162,9 +459,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.26.0"
-#define SQLITE_VERSION_NUMBER 3026000
-#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9"
+#define SQLITE_VERSION "3.45.1"
+#define SQLITE_VERSION_NUMBER 3045001
+#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -1190,8 +487,8 @@ extern "C" {
** function is provided for use in DLLs since DLL users usually do not have
** direct access to string constants within the DLL. ^The
** sqlite3_libversion_number() function returns an integer equal to
-** [SQLITE_VERSION_NUMBER]. ^(The sqlite3_sourceid() function returns
-** a pointer to a string constant whose value is the same as the
+** [SQLITE_VERSION_NUMBER]. ^(The sqlite3_sourceid() function returns
+** a pointer to a string constant whose value is the same as the
** [SQLITE_SOURCE_ID] C preprocessor macro. Except if SQLite is built
** using an edited copy of [the amalgamation], then the last four characters
** of the hash might be different from [SQLITE_SOURCE_ID].)^
@@ -1206,20 +503,20 @@ SQLITE_API int sqlite3_libversion_number(void);
/*
** CAPI3REF: Run-Time Library Compilation Options Diagnostics
**
-** ^The sqlite3_compileoption_used() function returns 0 or 1
-** indicating whether the specified option was defined at
-** compile time. ^The SQLITE_ prefix may be omitted from the
-** option name passed to sqlite3_compileoption_used().
+** ^The sqlite3_compileoption_used() function returns 0 or 1
+** indicating whether the specified option was defined at
+** compile time. ^The SQLITE_ prefix may be omitted from the
+** option name passed to sqlite3_compileoption_used().
**
** ^The sqlite3_compileoption_get() function allows iterating
** over the list of options that were defined at compile time by
** returning the N-th compile time option string. ^If N is out of range,
-** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_
-** prefix is omitted from any strings returned by
+** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_
+** prefix is omitted from any strings returned by
** sqlite3_compileoption_get().
**
** ^Support for the diagnostic functions sqlite3_compileoption_used()
-** and sqlite3_compileoption_get() may be omitted by specifying the
+** and sqlite3_compileoption_get() may be omitted by specifying the
** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time.
**
** See also: SQL functions [sqlite_compileoption_used()] and
@@ -1228,6 +525,9 @@ SQLITE_API int sqlite3_libversion_number(void);
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
SQLITE_API int sqlite3_compileoption_used(const char *zOptName);
SQLITE_API const char *sqlite3_compileoption_get(int N);
+#else
+# define sqlite3_compileoption_used(X) 0
+# define sqlite3_compileoption_get(X) ((void*)0)
#endif
/*
@@ -1240,7 +540,7 @@ SQLITE_API const char *sqlite3_compileoption_get(int N);
** SQLite can be compiled with or without mutexes. When
** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes
** are enabled and SQLite is threadsafe. When the
-** [SQLITE_THREADSAFE] macro is 0,
+** [SQLITE_THREADSAFE] macro is 0,
** the mutexes are omitted. Without the mutexes, it is not safe
** to use SQLite concurrently from more than one thread.
**
@@ -1297,14 +597,14 @@ typedef struct sqlite3 sqlite3;
**
** ^The sqlite3_int64 and sqlite_int64 types can store integer values
** between -9223372036854775808 and +9223372036854775807 inclusive. ^The
-** sqlite3_uint64 and sqlite_uint64 types can store integer values
+** sqlite3_uint64 and sqlite_uint64 types can store integer values
** between 0 and +18446744073709551615 inclusive.
*/
#ifdef SQLITE_INT64_TYPE
typedef SQLITE_INT64_TYPE sqlite_int64;
# ifdef SQLITE_UINT64_TYPE
typedef SQLITE_UINT64_TYPE sqlite_uint64;
-# else
+# else
typedef unsigned SQLITE_INT64_TYPE sqlite_uint64;
# endif
#elif defined(_MSC_VER) || defined(__BORLANDC__)
@@ -1335,26 +635,22 @@ typedef sqlite_uint64 sqlite3_uint64;
** the [sqlite3] object is successfully destroyed and all associated
** resources are deallocated.
**
-** ^If the database connection is associated with unfinalized prepared
-** statements or unfinished sqlite3_backup objects then sqlite3_close()
-** will leave the database connection open and return [SQLITE_BUSY].
-** ^If sqlite3_close_v2() is called with unfinalized prepared statements
-** and/or unfinished sqlite3_backups, then the database connection becomes
-** an unusable "zombie" which will automatically be deallocated when the
-** last prepared statement is finalized or the last sqlite3_backup is
-** finished. The sqlite3_close_v2() interface is intended for use with
-** host languages that are garbage collected, and where the order in which
-** destructors are called is arbitrary.
-**
-** Applications should [sqlite3_finalize | finalize] all [prepared statements],
-** [sqlite3_blob_close | close] all [BLOB handles], and
+** Ideally, applications should [sqlite3_finalize | finalize] all
+** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and
** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
-** with the [sqlite3] object prior to attempting to close the object. ^If
-** sqlite3_close_v2() is called on a [database connection] that still has
-** outstanding [prepared statements], [BLOB handles], and/or
-** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation
-** of resources is deferred until all [prepared statements], [BLOB handles],
-** and [sqlite3_backup] objects are also destroyed.
+** with the [sqlite3] object prior to attempting to close the object.
+** ^If the database connection is associated with unfinalized prepared
+** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then
+** sqlite3_close() will leave the database connection open and return
+** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared
+** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups,
+** it returns [SQLITE_OK] regardless, but instead of deallocating the database
+** connection immediately, it marks the database connection as an unusable
+** "zombie" and makes arrangements to automatically deallocate the database
+** connection after all prepared statements are finalized, all BLOB handles
+** are closed, and all backups have finished. The sqlite3_close_v2() interface
+** is intended for use with host languages that are garbage collected, and
+** where the order in which destructors are called is arbitrary.
**
** ^If an [sqlite3] object is destroyed while a transaction is open,
** the transaction is automatically rolled back.
@@ -1384,7 +680,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** The sqlite3_exec() interface is a convenience wrapper around
** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()],
** that allows an application to run multiple statements of SQL
-** without having to use a lot of C code.
+** without having to use a lot of C code.
**
** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded,
** semicolon-separate SQL statements passed into its 2nd argument,
@@ -1424,7 +720,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** from [sqlite3_column_name()].
**
** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer
-** to an empty string, or a pointer that contains only whitespace and/or
+** to an empty string, or a pointer that contains only whitespace and/or
** SQL comments, then no SQL statements are evaluated and the database
** is not changed.
**
@@ -1543,17 +839,23 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
+#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
+#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
+#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
+#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
+#define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8))
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
+#define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3<<8))
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
@@ -1571,11 +873,15 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8))
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8))
+#define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8))
+#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8))
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
+#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8))
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
+#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */
/*
** CAPI3REF: Flags For File Open Operations
@@ -1583,6 +889,19 @@ SQLITE_API int sqlite3_exec(
** These bit values are intended for use in the
** 3rd parameter to the [sqlite3_open_v2()] interface and
** in the 4th parameter to the [sqlite3_vfs.xOpen] method.
+**
+** Only those flags marked as "Ok for sqlite3_open_v2()" may be
+** used as the third argument to the [sqlite3_open_v2()] interface.
+** The other flags have historically been ignored by sqlite3_open_v2(),
+** though future versions of SQLite might change so that an error is
+** raised if any of the disallowed bits are passed into sqlite3_open_v2().
+** Applications should not depend on the historical behavior.
+**
+** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into
+** [sqlite3_open_v2()] does *not* cause the underlying database file
+** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into
+** [sqlite3_open_v2()] has historically be a no-op and might become an
+** error in future versions of SQLite.
*/
#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
@@ -1598,14 +917,19 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_OPEN_MAIN_JOURNAL 0x00000800 /* VFS only */
#define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 /* VFS only */
#define SQLITE_OPEN_SUBJOURNAL 0x00002000 /* VFS only */
-#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */
+#define SQLITE_OPEN_SUPER_JOURNAL 0x00004000 /* VFS only */
#define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_WAL 0x00080000 /* VFS only */
+#define SQLITE_OPEN_NOFOLLOW 0x01000000 /* Ok for sqlite3_open_v2() */
+#define SQLITE_OPEN_EXRESCODE 0x02000000 /* Extended result codes */
/* Reserved: 0x00F00000 */
+/* Legacy compatibility: */
+#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */
+
/*
** CAPI3REF: Device Characteristics
@@ -1661,13 +985,17 @@ SQLITE_API int sqlite3_exec(
**
** SQLite uses one of these integer values as the second
** argument to calls it makes to the xLock() and xUnlock() methods
-** of an [sqlite3_io_methods] object.
+** of an [sqlite3_io_methods] object. These values are ordered from
+** lest restrictive to most restrictive.
+**
+** The argument to xLock() is always SHARED or higher. The argument to
+** xUnlock is either SHARED or NONE.
*/
-#define SQLITE_LOCK_NONE 0
-#define SQLITE_LOCK_SHARED 1
-#define SQLITE_LOCK_RESERVED 2
-#define SQLITE_LOCK_PENDING 3
-#define SQLITE_LOCK_EXCLUSIVE 4
+#define SQLITE_LOCK_NONE 0 /* xUnlock() only */
+#define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */
+#define SQLITE_LOCK_RESERVED 2 /* xLock() only */
+#define SQLITE_LOCK_PENDING 3 /* xLock() only */
+#define SQLITE_LOCK_EXCLUSIVE 4 /* xLock() only */
/*
** CAPI3REF: Synchronization Type Flags
@@ -1702,7 +1030,7 @@ SQLITE_API int sqlite3_exec(
/*
** CAPI3REF: OS Interface Open File Handle
**
-** An [sqlite3_file] object represents an open file in the
+** An [sqlite3_file] object represents an open file in the
** [sqlite3_vfs | OS interface layer]. Individual OS interface
** implementations will
** want to subclass this object by appending additional fields
@@ -1724,7 +1052,7 @@ struct sqlite3_file {
** This object defines the methods used to perform various operations
** against the open file represented by the [sqlite3_file] object.
**
-** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element
+** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element
** to a non-NULL pointer, then the sqlite3_io_methods.xClose method
** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The
** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen]
@@ -1745,7 +1073,14 @@ struct sqlite3_file {
** <li> [SQLITE_LOCK_PENDING], or
** <li> [SQLITE_LOCK_EXCLUSIVE].
** </ul>
-** xLock() increases the lock. xUnlock() decreases the lock.
+** xLock() upgrades the database file lock. In other words, xLock() moves the
+** database file lock in the direction NONE toward EXCLUSIVE. The argument to
+** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
+** SQLITE_LOCK_NONE. If the database file lock is already at or above the
+** requested lock, then the call to xLock() is a no-op.
+** xUnlock() downgrades the database file lock to either SHARED or NONE.
+* If the lock is already at or below the requested lock state, then the call
+** to xUnlock() is a no-op.
** The xCheckReservedLock() method checks whether any database connection,
** either in this process or in some other process, is holding a RESERVED,
** PENDING, or EXCLUSIVE lock on the file. It returns true
@@ -1850,9 +1185,8 @@ struct sqlite3_io_methods {
** opcode causes the xFileControl method to write the current state of
** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
-** into an integer that the pArg argument points to. This capability
-** is used during testing and is only available when the SQLITE_TEST
-** compile-time option is used.
+** into an integer that the pArg argument points to.
+** This capability is only available if SQLite is compiled with [SQLITE_DEBUG].
**
** <li>[[SQLITE_FCNTL_SIZE_HINT]]
** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
@@ -1862,10 +1196,19 @@ struct sqlite3_io_methods {
** file space based on this hint in order to help writes to the database
** file run faster.
**
+** <li>[[SQLITE_FCNTL_SIZE_LIMIT]]
+** The [SQLITE_FCNTL_SIZE_LIMIT] opcode is used by in-memory VFS that
+** implements [sqlite3_deserialize()] to set an upper bound on the size
+** of the in-memory database. The argument is a pointer to a [sqlite3_int64].
+** If the integer pointed to is negative, then it is filled in with the
+** current limit. Otherwise the limit is set to the larger of the value
+** of the integer pointed to and the current database size. The integer
+** pointed to is set to the new limit.
+**
** <li>[[SQLITE_FCNTL_CHUNK_SIZE]]
** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
** extends and truncates the database file in chunks of a size specified
-** by the user. The fourth argument to [sqlite3_file_control()] should
+** by the user. The fourth argument to [sqlite3_file_control()] should
** point to an integer (type int) containing the new chunk-size to use
** for the nominated database. Allocating database file space in large
** chunks (say 1MB at a time), may reduce file-system fragmentation and
@@ -1888,24 +1231,24 @@ struct sqlite3_io_methods {
** <li>[[SQLITE_FCNTL_SYNC]]
** The [SQLITE_FCNTL_SYNC] opcode is generated internally by SQLite and
** sent to the VFS immediately before the xSync method is invoked on a
-** database file descriptor. Or, if the xSync method is not invoked
-** because the user has configured SQLite with
-** [PRAGMA synchronous | PRAGMA synchronous=OFF] it is invoked in place
+** database file descriptor. Or, if the xSync method is not invoked
+** because the user has configured SQLite with
+** [PRAGMA synchronous | PRAGMA synchronous=OFF] it is invoked in place
** of the xSync method. In most cases, the pointer argument passed with
** this file-control is NULL. However, if the database file is being synced
** as part of a multi-database commit, the argument points to a nul-terminated
-** string containing the transactions master-journal file name. VFSes that
-** do not need this signal should silently ignore this opcode. Applications
-** should not call [sqlite3_file_control()] with this opcode as doing so may
-** disrupt the operation of the specialized VFSes that do require it.
+** string containing the transactions super-journal file name. VFSes that
+** do not need this signal should silently ignore this opcode. Applications
+** should not call [sqlite3_file_control()] with this opcode as doing so may
+** disrupt the operation of the specialized VFSes that do require it.
**
** <li>[[SQLITE_FCNTL_COMMIT_PHASETWO]]
** The [SQLITE_FCNTL_COMMIT_PHASETWO] opcode is generated internally by SQLite
** and sent to the VFS after a transaction has been committed immediately
** but before the database is unlocked. VFSes that do not need this signal
** should silently ignore this opcode. Applications should not call
-** [sqlite3_file_control()] with this opcode as doing so may disrupt the
-** operation of the specialized VFSes that do require it.
+** [sqlite3_file_control()] with this opcode as doing so may disrupt the
+** operation of the specialized VFSes that do require it.
**
** <li>[[SQLITE_FCNTL_WIN32_AV_RETRY]]
** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
@@ -1953,13 +1296,13 @@ struct sqlite3_io_methods {
** <li>[[SQLITE_FCNTL_OVERWRITE]]
** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
** a write transaction to indicate that, unless it is rolled back for some
-** reason, the entire database file will be overwritten by the current
+** reason, the entire database file will be overwritten by the current
** transaction. This is used by VACUUM operations.
**
** <li>[[SQLITE_FCNTL_VFSNAME]]
** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
** all [VFSes] in the VFS stack. The names are of all VFS shims and the
-** final bottom-level VFS are written into memory obtained from
+** final bottom-level VFS are written into memory obtained from
** [sqlite3_malloc()] and the result is stored in the char* variable
** that the fourth parameter of [sqlite3_file_control()] points to.
** The caller is responsible for freeing the memory when done. As with
@@ -1978,7 +1321,7 @@ struct sqlite3_io_methods {
** upper-most shim only.
**
** <li>[[SQLITE_FCNTL_PRAGMA]]
-** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA]
+** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA]
** file control is sent to the open [sqlite3_file] object corresponding
** to the database file to which the pragma statement refers. ^The argument
** to the [SQLITE_FCNTL_PRAGMA] file control is an array of
@@ -1989,7 +1332,7 @@ struct sqlite3_io_methods {
** of the char** argument point to a string obtained from [sqlite3_mprintf()]
** or the equivalent and that string will become the result of the pragma or
** the error message if the pragma fails. ^If the
-** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal
+** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal
** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA]
** file control returns [SQLITE_OK], then the parser assumes that the
** VFS has handled the PRAGMA itself and the parser generates a no-op
@@ -2006,16 +1349,16 @@ struct sqlite3_io_methods {
** ^The [SQLITE_FCNTL_BUSYHANDLER]
** file-control may be invoked by SQLite on the database file handle
** shortly after it is opened in order to provide a custom VFS with access
-** to the connections busy-handler callback. The argument is of type (void **)
+** to the connection's busy-handler callback. The argument is of type (void**)
** - an array of two (void *) values. The first (void *) actually points
-** to a function of type (int (*)(void *)). In order to invoke the connections
+** to a function of type (int (*)(void *)). In order to invoke the connection's
** busy-handler, this function should be invoked with the second (void *) in
** the array as the only argument. If it returns non-zero, then the operation
** should be retried. If it returns zero, the custom VFS should abandon the
** current operation.
**
** <li>[[SQLITE_FCNTL_TEMPFILENAME]]
-** ^Application can invoke the [SQLITE_FCNTL_TEMPFILENAME] file-control
+** ^Applications can invoke the [SQLITE_FCNTL_TEMPFILENAME] file-control
** to have SQLite generate a
** temporary filename using the same algorithm that is followed to generate
** temporary filenames for TEMP tables and other internal uses. The
@@ -2029,7 +1372,7 @@ struct sqlite3_io_methods {
** The argument is a pointer to a value of type sqlite3_int64 that
** is an advisory maximum number of bytes in the file to memory map. The
** pointer is overwritten with the old value. The limit is not changed if
-** the value originally pointed to is negative, and so the current limit
+** the value originally pointed to is negative, and so the current limit
** can be queried by passing in a pointer to a negative number. This
** file-control is used internally to implement [PRAGMA mmap_size].
**
@@ -2073,7 +1416,7 @@ struct sqlite3_io_methods {
** <li>[[SQLITE_FCNTL_RBU]]
** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by
** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for
-** this opcode.
+** this opcode.
**
** <li>[[SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]]
** If the [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] opcode returns SQLITE_OK, then
@@ -2090,7 +1433,7 @@ struct sqlite3_io_methods {
**
** <li>[[SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]]
** The [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] opcode causes all write
-** operations since the previous successful call to
+** operations since the previous successful call to
** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be performed atomically.
** This file control returns [SQLITE_OK] if and only if the writes were
** all performed successfully and have been committed to persistent storage.
@@ -2102,7 +1445,7 @@ struct sqlite3_io_methods {
**
** <li>[[SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE]]
** The [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE] opcode causes all write
-** operations since the previous successful call to
+** operations since the previous successful call to
** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be rolled back.
** ^This file control takes the file descriptor out of batch write mode
** so that all subsequent write operations are independent.
@@ -2110,10 +1453,12 @@ struct sqlite3_io_methods {
** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
**
** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
-** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain
-** a file lock using the xLock or xShmLock methods of the VFS to wait
-** for up to M milliseconds before failing, where M is the single
-** unsigned integer parameter.
+** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS
+** to block for up to M milliseconds before failing when attempting to
+** obtain a file lock using the xLock or xShmLock methods of the VFS.
+** The parameter is a pointer to a 32-bit signed integer that contains
+** the value that M is to be set to. Before returning, the 32-bit signed
+** integer is overwritten with the previous value of M.
**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
@@ -2128,12 +1473,45 @@ struct sqlite3_io_methods {
** not provide a mechanism to detect changes to MAIN only. Also, the
** [sqlite3_total_changes()] interface responds to internal changes only and
** omits changes made by other database connections. The
-** [PRAGMA data_version] command provide a mechanism to detect changes to
+** [PRAGMA data_version] command provides a mechanism to detect changes to
** a single attached database that occur due to other database connections,
** but omits changes implemented by the database connection on which it is
** called. This file control is the only mechanism to detect changes that
** happen either internally or externally and that are associated with
** a particular attached database.
+**
+** <li>[[SQLITE_FCNTL_CKPT_START]]
+** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint
+** in wal mode before the client starts to copy pages from the wal
+** file to the database file.
+**
+** <li>[[SQLITE_FCNTL_CKPT_DONE]]
+** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint
+** in wal mode after the client has finished copying pages from the wal
+** file to the database file, but before the *-shm file is updated to
+** record the fact that the pages have been checkpointed.
+**
+** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
+** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
+** whether or not there is a database client in another process with a wal-mode
+** transaction open on the database or not. It is only available on unix.The
+** (void*) argument passed with this file-control should be a pointer to a
+** value of type (int). The integer value is set to 1 if the database is a wal
+** mode database and there exists at least one client in another process that
+** currently has an SQL transaction open on the database. It is set to 0 if
+** the database is not a wal-mode db, or if there is no such connection in any
+** other process. This opcode cannot be used to detect transactions opened
+** by clients within the current process, only within other processes.
+**
+** <li>[[SQLITE_FCNTL_CKSM_FILE]]
+** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use internally by the
+** [checksum VFS shim] only.
+**
+** <li>[[SQLITE_FCNTL_RESET_CACHE]]
+** If there is currently no transaction open on the database, and the
+** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control
+** purges the contents of the in-memory page cache. If there is an open
+** transaction, or if the db is a temp-db, this opcode is a no-op, not an error.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE 1
@@ -2170,6 +1548,13 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
#define SQLITE_FCNTL_LOCK_TIMEOUT 34
#define SQLITE_FCNTL_DATA_VERSION 35
+#define SQLITE_FCNTL_SIZE_LIMIT 36
+#define SQLITE_FCNTL_CKPT_DONE 37
+#define SQLITE_FCNTL_RESERVE_BYTES 38
+#define SQLITE_FCNTL_CKPT_START 39
+#define SQLITE_FCNTL_EXTERNAL_READER 40
+#define SQLITE_FCNTL_CKSM_FILE 41
+#define SQLITE_FCNTL_RESET_CACHE 42
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -2200,6 +1585,26 @@ typedef struct sqlite3_mutex sqlite3_mutex;
typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
+** CAPI3REF: File Name
+**
+** Type [sqlite3_filename] is used by SQLite to pass filenames to the
+** xOpen method of a [VFS]. It may be cast to (const char*) and treated
+** as a normal, nul-terminated, UTF-8 buffer containing the filename, but
+** may also be passed to special APIs such as:
+**
+** <ul>
+** <li> sqlite3_filename_database()
+** <li> sqlite3_filename_journal()
+** <li> sqlite3_filename_wal()
+** <li> sqlite3_uri_parameter()
+** <li> sqlite3_uri_boolean()
+** <li> sqlite3_uri_int64()
+** <li> sqlite3_uri_key()
+** </ul>
+*/
+typedef const char *sqlite3_filename;
+
+/*
** CAPI3REF: OS Interface Object
**
** An instance of the sqlite3_vfs object defines the interface between
@@ -2215,10 +1620,10 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** to 3 with SQLite [version 3.7.6] on [dateof:3.7.6]. Additional fields
** may be appended to the sqlite3_vfs object and the iVersion value
** may increase again in future versions of SQLite.
-** Note that the structure
-** of the sqlite3_vfs object changes in the transition from
+** Note that due to an oversight, the structure
+** of the sqlite3_vfs object changed in the transition from
** SQLite [version 3.5.9] to [version 3.6.0] on [dateof:3.6.0]
-** and yet the iVersion field was not modified.
+** and yet the iVersion field was not increased.
**
** The szOsFile field is the size of the subclassed [sqlite3_file]
** structure used by this VFS. mxPathname is the maximum length of
@@ -2253,14 +1658,14 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** the [sqlite3_file] can safely store a pointer to the
** filename if it needs to remember the filename for some reason.
** If the zFilename parameter to xOpen is a NULL pointer then xOpen
-** must invent its own temporary name for the file. ^Whenever the
+** must invent its own temporary name for the file. ^Whenever the
** xFilename parameter is NULL it will also be the case that the
** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE].
**
** The flags argument to xOpen() includes all bits set in
** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()]
** or [sqlite3_open16()] is used, then flags includes at least
-** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE].
+** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE].
** If xOpen() opens a file read-only then it sets *pOutFlags to
** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set.
**
@@ -2274,7 +1679,7 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** <li> [SQLITE_OPEN_TEMP_JOURNAL]
** <li> [SQLITE_OPEN_TRANSIENT_DB]
** <li> [SQLITE_OPEN_SUBJOURNAL]
-** <li> [SQLITE_OPEN_MASTER_JOURNAL]
+** <li> [SQLITE_OPEN_SUPER_JOURNAL]
** <li> [SQLITE_OPEN_WAL]
** </ul>)^
**
@@ -2302,14 +1707,14 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction
** with the [SQLITE_OPEN_CREATE] flag, which are both directly
** analogous to the O_EXCL and O_CREAT flags of the POSIX open()
-** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the
+** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the
** SQLITE_OPEN_CREATE, is used to indicate that file should always
** be created, and that it is an error if it already exists.
-** It is <i>not</i> used to indicate the file should be opened
+** It is <i>not</i> used to indicate the file should be opened
** for exclusive access.
**
** ^At least szOsFile bytes of memory are allocated by SQLite
-** to hold the [sqlite3_file] structure passed as the third
+** to hold the [sqlite3_file] structure passed as the third
** argument to xOpen. The xOpen method does not have to
** allocate the structure; it should just fill it in. Note that
** the xOpen method must set the sqlite3_file.pMethods to either
@@ -2322,8 +1727,14 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS]
** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to
** test whether a file is readable and writable, or [SQLITE_ACCESS_READ]
-** to test whether a file is at least readable. The file can be a
-** directory.
+** to test whether a file is at least readable. The SQLITE_ACCESS_READ
+** flag is never actually used and is not implemented in the built-in
+** VFSes of SQLite. The file is named by the second argument and can be a
+** directory. The xAccess method returns [SQLITE_OK] on success or some
+** non-zero error code if there is an I/O error or if the name of
+** the file given in the second argument is illegal. If SQLITE_OK
+** is returned, then non-zero or zero is written into *pResOut to indicate
+** whether or not the file is accessible.
**
** ^SQLite will always allocate at least mxPathname+1 bytes for the
** output buffer xFullPathname. The exact size of the output buffer
@@ -2343,16 +1754,16 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** method returns a Julian Day Number for the current date and time as
** a floating point value.
** ^The xCurrentTimeInt64() method returns, as an integer, the Julian
-** Day Number multiplied by 86400000 (the number of milliseconds in
-** a 24-hour day).
+** Day Number multiplied by 86400000 (the number of milliseconds in
+** a 24-hour day).
** ^SQLite will use the xCurrentTimeInt64() method to get the current
-** date and time if that method is available (if iVersion is 2 or
+** date and time if that method is available (if iVersion is 2 or
** greater and the function pointer is not NULL) and will fall back
** to xCurrentTime() if xCurrentTimeInt64() is unavailable.
**
** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces
** are not used by the SQLite core. These optional interfaces are provided
-** by some VFSes to facilitate testing of the VFS code. By overriding
+** by some VFSes to facilitate testing of the VFS code. By overriding
** system calls with functions under its control, a test program can
** simulate faults and error conditions that would otherwise be difficult
** or impossible to induce. The set of system calls that can be overridden
@@ -2371,7 +1782,7 @@ struct sqlite3_vfs {
sqlite3_vfs *pNext; /* Next registered VFS */
const char *zName; /* Name of this virtual file system */
void *pAppData; /* Pointer to application-specific data */
- int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
+ int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*,
int flags, int *pOutFlags);
int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
@@ -2399,7 +1810,7 @@ struct sqlite3_vfs {
/*
** The methods above are in versions 1 through 3 of the sqlite_vfs object.
** New fields may be appended in future versions. The iVersion
- ** value will increment whenever this happens.
+ ** value will increment whenever this happens.
*/
};
@@ -2443,7 +1854,7 @@ struct sqlite3_vfs {
** </ul>
**
** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as
-** was given on the corresponding lock.
+** was given on the corresponding lock.
**
** The xShmLock method can transition between unlocked and SHARED or
** between unlocked and EXCLUSIVE. It cannot transition between SHARED
@@ -2558,20 +1969,23 @@ SQLITE_API int sqlite3_os_end(void);
** must ensure that no other SQLite interfaces are invoked by other
** threads while sqlite3_config() is running.</b>
**
-** The sqlite3_config() interface
-** may only be invoked prior to library initialization using
-** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
-** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
-** [sqlite3_shutdown()] then it will return SQLITE_MISUSE.
-** Note, however, that ^sqlite3_config() can be called as part of the
-** implementation of an application-defined [sqlite3_os_init()].
-**
** The first argument to sqlite3_config() is an integer
** [configuration option] that determines
** what property of SQLite is to be configured. Subsequent arguments
** vary depending on the [configuration option]
** in the first argument.
**
+** For most configuration options, the sqlite3_config() interface
+** may only be invoked prior to library initialization using
+** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
+** The exceptional configuration options that may be invoked at any time
+** are called "anytime configuration options".
+** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
+** [sqlite3_shutdown()] with a first argument that is not an anytime
+** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE.
+** Note, however, that ^sqlite3_config() can be called as part of the
+** implementation of an application-defined [sqlite3_os_init()].
+**
** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK].
** ^If the option is unknown or SQLite is unable to set the option
** then this routine returns a non-zero [error code].
@@ -2588,7 +2002,7 @@ SQLITE_API int sqlite3_config(int, ...);
** [database connection] (specified in the first argument).
**
** The second argument to sqlite3_db_config(D,V,...) is the
-** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code
+** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code
** that indicates what aspect of the [database connection] is being configured.
** Subsequent arguments vary depending on the configuration verb.
**
@@ -2606,7 +2020,7 @@ SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...);
** This object is used in only one place in the SQLite interface.
** A pointer to an instance of this object is the argument to
** [sqlite3_config()] when the configuration option is
-** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC].
+** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC].
** By creating an instance of this object
** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC])
** during configuration, an application can specify an alternative
@@ -2636,17 +2050,17 @@ SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...);
** allocators round up memory allocations at least to the next multiple
** of 8. Some allocators round up to a larger multiple or to a power of 2.
** Every memory allocation request coming in through [sqlite3_malloc()]
-** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0,
+** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0,
** that causes the corresponding memory allocation to fail.
**
** The xInit method initializes the memory allocator. For example,
-** it might allocate any require mutexes or initialize internal data
+** it might allocate any required mutexes or initialize internal data
** structures. The xShutdown method is invoked (indirectly) by
** [sqlite3_shutdown()] and should deallocate any resources acquired
** by xInit. The pAppData pointer is used as the only parameter to
** xInit and xShutdown.
**
-** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes
+** SQLite holds the [SQLITE_MUTEX_STATIC_MAIN] mutex when it invokes
** the xInit method, so the xInit method need not be threadsafe. The
** xShutdown method is only called from [sqlite3_shutdown()] so it does
** not need to be threadsafe either. For all other methods, SQLite
@@ -2679,6 +2093,23 @@ struct sqlite3_mem_methods {
** These constants are the available integer configuration options that
** can be passed as the first argument to the [sqlite3_config()] interface.
**
+** Most of the configuration options for sqlite3_config()
+** will only work if invoked prior to [sqlite3_initialize()] or after
+** [sqlite3_shutdown()]. The few exceptions to this rule are called
+** "anytime configuration options".
+** ^Calling [sqlite3_config()] with a first argument that is not an
+** anytime configuration option in between calls to [sqlite3_initialize()] and
+** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE.
+**
+** The set of anytime configuration options can change (by insertions
+** and/or deletions) from one release of SQLite to the next.
+** As of SQLite version 3.42.0, the complete set of anytime configuration
+** options is:
+** <ul>
+** <li> SQLITE_CONFIG_LOG
+** <li> SQLITE_CONFIG_PCACHE_HDRSZ
+** </ul>
+**
** New configuration options may be added in future releases of SQLite.
** Existing configuration options might be discontinued. Applications
** should check the return code from [sqlite3_config()] to make sure that
@@ -2694,7 +2125,7 @@ struct sqlite3_mem_methods {
** by a single thread. ^If SQLite is compiled with
** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
** it is not possible to change the [threading mode] from its default
-** value of Single-thread and so [sqlite3_config()] will return
+** value of Single-thread and so [sqlite3_config()] will return
** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD
** configuration option.</dd>
**
@@ -2729,7 +2160,7 @@ struct sqlite3_mem_methods {
** SQLITE_CONFIG_SERIALIZED configuration option.</dd>
**
** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt>
-** <dd> ^(The SQLITE_CONFIG_MALLOC option takes a single argument which is
+** <dd> ^(The SQLITE_CONFIG_MALLOC option takes a single argument which is
** a pointer to an instance of the [sqlite3_mem_methods] structure.
** The argument specifies
** alternative low-level memory allocation routines to be used in place of
@@ -2762,6 +2193,7 @@ struct sqlite3_mem_methods {
** memory allocation statistics. ^(When memory allocation statistics are
** disabled, the following SQLite interfaces become non-operational:
** <ul>
+** <li> [sqlite3_hard_heap_limit64()]
** <li> [sqlite3_memory_used()]
** <li> [sqlite3_memory_highwater()]
** <li> [sqlite3_soft_heap_limit64()]
@@ -2779,8 +2211,8 @@ struct sqlite3_mem_methods {
** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a memory pool
** that SQLite can use for the database page cache with the default page
-** cache implementation.
-** This configuration option is a no-op if an application-define page
+** cache implementation.
+** This configuration option is a no-op if an application-defined page
** cache implementation is loaded using the [SQLITE_CONFIG_PCACHE2].
** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to
** 8-byte aligned memory (pMem), the size of each page cache line (sz),
@@ -2807,7 +2239,7 @@ struct sqlite3_mem_methods {
** additional cache line. </dd>
**
** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
-** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer
+** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer
** that SQLite will use for all of its dynamic memory allocation needs
** beyond those provided for by [SQLITE_CONFIG_PAGECACHE].
** ^The SQLITE_CONFIG_HEAP option is only available if SQLite is compiled
@@ -2862,7 +2294,7 @@ struct sqlite3_mem_methods {
** configuration on individual connections.)^ </dd>
**
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
-** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
+** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
** a pointer to an [sqlite3_pcache_methods2] object. This object specifies
** the interface to a custom page cache implementation.)^
** ^SQLite makes a copy of the [sqlite3_pcache_methods2] object.</dd>
@@ -2876,7 +2308,7 @@ struct sqlite3_mem_methods {
** <dd> The SQLITE_CONFIG_LOG option is used to configure the SQLite
** global [error log].
** (^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
-** function with a call signature of void(*)(void*,int,const char*),
+** function with a call signature of void(*)(void*,int,const char*),
** and a pointer to void. ^If the function pointer is not NULL, it is
** invoked by [sqlite3_log()] to process each logging event. ^If the
** function pointer is NULL, the [sqlite3_log()] interface becomes a no-op.
@@ -2985,7 +2417,7 @@ struct sqlite3_mem_methods {
** [[SQLITE_CONFIG_STMTJRNL_SPILL]]
** <dt>SQLITE_CONFIG_STMTJRNL_SPILL
** <dd>^The SQLITE_CONFIG_STMTJRNL_SPILL option takes a single parameter which
-** becomes the [statement journal] spill-to-disk threshold.
+** becomes the [statement journal] spill-to-disk threshold.
** [Statement journals] are held in memory until their size (in bytes)
** exceeds this threshold, at which point they are written to disk.
** Or if the threshold is -1, statement journals are always held
@@ -3007,40 +2439,52 @@ struct sqlite3_mem_methods {
** than the configured sorter-reference size threshold - then a reference
** is stored in each sorted record and the required column values loaded
** from the database as records are returned in sorted order. The default
-** value for this option is to never use this optimization. Specifying a
-** negative value for this option restores the default behaviour.
+** value for this option is to never use this optimization. Specifying a
+** negative value for this option restores the default behavior.
** This option is only available if SQLite is compiled with the
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
+**
+** [[SQLITE_CONFIG_MEMDB_MAXSIZE]]
+** <dt>SQLITE_CONFIG_MEMDB_MAXSIZE
+** <dd>The SQLITE_CONFIG_MEMDB_MAXSIZE option accepts a single parameter
+** [sqlite3_int64] parameter which is the default maximum size for an in-memory
+** database created using [sqlite3_deserialize()]. This default maximum
+** size can be adjusted up or down for individual databases using the
+** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this
+** configuration setting is never used, then the default maximum is determined
+** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
+** compile-time option is not set, then the default maximum is 1073741824.
** </dl>
*/
-#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
-#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */
-#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
-#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
-#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
-#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */
-#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
-#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
-#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
-#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */
-#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
-/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
-#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
-#define SQLITE_CONFIG_PCACHE 14 /* no-op */
-#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */
-#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
-#define SQLITE_CONFIG_URI 17 /* int */
-#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */
-#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
+#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
+#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */
+#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
+#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
+#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
+#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */
+#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
+#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
+#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
+#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */
+#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
+/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
+#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
+#define SQLITE_CONFIG_PCACHE 14 /* no-op */
+#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */
+#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
+#define SQLITE_CONFIG_URI 17 /* int */
+#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */
+#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */
-#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
-#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
+#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
+#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
+#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -3058,7 +2502,7 @@ struct sqlite3_mem_methods {
** <dl>
** [[SQLITE_DBCONFIG_LOOKASIDE]]
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
-** <dd> ^This option takes three additional arguments that determine the
+** <dd> ^This option takes three additional arguments that determine the
** [lookaside memory allocator] configuration for the [database connection].
** ^The first argument (the third parameter to [sqlite3_db_config()] is a
** pointer to a memory buffer to use for lookaside memory.
@@ -3074,9 +2518,9 @@ struct sqlite3_mem_methods {
** configuration for a database connection can only be changed when that
** connection is not currently using lookaside memory, or in other words
** when the "current value" returned by
-** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero.
+** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
** Any attempt to change the lookaside memory configuration when lookaside
-** memory is in use leaves the configuration unchanged and returns
+** memory is in use leaves the configuration unchanged and returns
** [SQLITE_BUSY].)^</dd>
**
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
@@ -3099,12 +2543,35 @@ struct sqlite3_mem_methods {
** The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether triggers are disabled or enabled
** following this call. The second parameter may be a NULL pointer, in
-** which case the trigger setting is not reported back. </dd>
+** which case the trigger setting is not reported back.
+**
+** <p>Originally this option disabled all triggers. ^(However, since
+** SQLite version 3.35.0, TEMP triggers are still allowed even if
+** this option is off. So, in other words, this option now only disables
+** triggers in the main database schema or in the schemas of ATTACH-ed
+** databases.)^ </dd>
+**
+** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
+** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
+** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable views,
+** positive to enable views or negative to leave the setting unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether views are disabled or enabled
+** following this call. The second parameter may be a NULL pointer, in
+** which case the view setting is not reported back.
+**
+** <p>Originally this option disabled all views. ^(However, since
+** SQLite version 3.35.0, TEMP views are still allowed even if
+** this option is off. So, in other words, this option now only disables
+** views in the main database schema or in the schemas of ATTACH-ed
+** databases.)^ </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
-** <dd> ^This option is used to enable or disable the two-argument
-** version of the [fts3_tokenizer()] function which is part of the
+** <dd> ^This option is used to enable or disable the
+** [fts3_tokenizer()] function which is part of the
** [FTS3] full-text search engine extension.
** There should be two additional arguments.
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
@@ -3142,13 +2609,13 @@ struct sqlite3_mem_methods {
** until after the database connection closes.
** </dd>
**
-** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
+** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
-** <dd> Usually, when a database in wal mode is closed or detached from a
-** database handle, SQLite checks if this will mean that there are now no
-** connections at all to the database. If so, it performs a checkpoint
+** <dd> Usually, when a database in wal mode is closed or detached from a
+** database handle, SQLite checks if this will mean that there are now no
+** connections at all to the database. If so, it performs a checkpoint
** operation before closing the connection. This option may be used to
-** override this behaviour. The first parameter passed to this operation
+** override this behavior. The first parameter passed to this operation
** is an integer - positive to disable checkpoints-on-close, or zero (the
** default) to enable them, and negative to leave the setting unchanged.
** The second parameter is a pointer to an integer
@@ -3165,7 +2632,7 @@ struct sqlite3_mem_methods {
** slower. But the QPSG has the advantage of more predictable behavior. With
** the QPSG active, SQLite will always use the same query plan in the field as
** was used during testing in the lab.
-** The first argument to this setting is an integer which is 0 to disable
+** The first argument to this setting is an integer which is 0 to disable
** the QPSG, positive to enable QPSG, or negative to leave the setting
** unchanged. The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether the QPSG is disabled or enabled
@@ -3173,15 +2640,15 @@ struct sqlite3_mem_methods {
** </dd>
**
** [[SQLITE_DBCONFIG_TRIGGER_EQP]] <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
-** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not
+** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not
** include output for any operations performed by trigger programs. This
** option is used to set or clear (the default) a flag that governs this
** behavior. The first parameter passed to this operation is an integer -
** positive to enable output for trigger programs, or zero to disable it,
** or negative to leave the setting unchanged.
-** The second parameter is a pointer to an integer into which is written
-** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if
-** it is not disabled, 1 if it is.
+** The second parameter is a pointer to an integer into which is written
+** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if
+** it is not disabled, 1 if it is.
** </dd>
**
** [[SQLITE_DBCONFIG_RESET_DATABASE]] <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
@@ -3195,27 +2662,146 @@ struct sqlite3_mem_methods {
** database, or calling sqlite3_table_column_metadata(), ignoring any
** errors. This step is only necessary if the application desires to keep
** the database in WAL mode after the reset if it was in WAL mode before
-** the reset.
+** the reset.
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
** <li> [sqlite3_exec](db, "[VACUUM]", 0, 0, 0);
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
** </ol>
** Because resetting a database is destructive and irreversible, the
-** process requires the use of this obscure API and multiple steps to help
-** ensure that it does not happen by accident.
+** process requires the use of this obscure API and multiple steps to
+** help ensure that it does not happen by accident. Because this
+** feature must be capable of resetting corrupt databases, and
+** shutting down virtual tables may require access to that corrupt
+** storage, the library must abandon any installed virtual tables
+** without calling their xDestroy() methods.
**
** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
** "defensive" flag for a database connection. When the defensive
-** flag is enabled, language features that allow ordinary SQL to
+** flag is enabled, language features that allow ordinary SQL to
** deliberately corrupt the database file are disabled. The disabled
** features include but are not limited to the following:
** <ul>
** <li> The [PRAGMA writable_schema=ON] statement.
+** <li> The [PRAGMA journal_mode=OFF] statement.
+** <li> The [PRAGMA schema_version=N] statement.
** <li> Writes to the [sqlite_dbpage] virtual table.
** <li> Direct writes to [shadow tables].
** </ul>
** </dd>
+**
+** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]] <dt>SQLITE_DBCONFIG_WRITABLE_SCHEMA</dt>
+** <dd>The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the
+** "writable_schema" flag. This has the same effect and is logically equivalent
+** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF].
+** The first argument to this setting is an integer which is 0 to disable
+** the writable_schema, positive to enable writable_schema, or negative to
+** leave the setting unchanged. The second parameter is a pointer to an
+** integer into which is written 0 or 1 to indicate whether the writable_schema
+** is enabled or disabled following this call.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_LEGACY_ALTER_TABLE]]
+** <dt>SQLITE_DBCONFIG_LEGACY_ALTER_TABLE</dt>
+** <dd>The SQLITE_DBCONFIG_LEGACY_ALTER_TABLE option activates or deactivates
+** the legacy behavior of the [ALTER TABLE RENAME] command such it
+** behaves as it did prior to [version 3.24.0] (2018-06-04). See the
+** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for
+** additional information. This feature can also be turned on and off
+** using the [PRAGMA legacy_alter_table] statement.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_DQS_DML]]
+** <dt>SQLITE_DBCONFIG_DQS_DML</dt>
+** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates
+** the legacy [double-quoted string literal] misfeature for DML statements
+** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The
+** default value of this setting is determined by the [-DSQLITE_DQS]
+** compile-time option.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_DQS_DDL]]
+** <dt>SQLITE_DBCONFIG_DQS_DDL</dt>
+** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates
+** the legacy [double-quoted string literal] misfeature for DDL statements,
+** such as CREATE TABLE and CREATE INDEX. The
+** default value of this setting is determined by the [-DSQLITE_DQS]
+** compile-time option.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]]
+** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt>
+** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to
+** assume that database schemas are untainted by malicious content.
+** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite
+** takes additional defensive steps to protect the application from harm
+** including:
+** <ul>
+** <li> Prohibit the use of SQL functions inside triggers, views,
+** CHECK constraints, DEFAULT clauses, expression indexes,
+** partial indexes, or generated columns
+** unless those functions are tagged with [SQLITE_INNOCUOUS].
+** <li> Prohibit the use of virtual tables inside of triggers or views
+** unless those virtual tables are tagged with [SQLITE_VTAB_INNOCUOUS].
+** </ul>
+** This setting defaults to "on" for legacy compatibility, however
+** all applications are advised to turn it off if possible. This setting
+** can also be controlled using the [PRAGMA trusted_schema] statement.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]]
+** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt>
+** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates
+** the legacy file format flag. When activated, this flag causes all newly
+** created database file to have a schema format version number (the 4-byte
+** integer found at offset 44 into the database header) of 1. This in turn
+** means that the resulting database file will be readable and writable by
+** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting,
+** newly created databases are generally not understandable by SQLite versions
+** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there
+** is now scarcely any need to generate database files that are compatible
+** all the way back to version 3.0.0, and so this setting is of little
+** practical use, but is provided so that SQLite can continue to claim the
+** ability to generate new database files that are compatible with version
+** 3.0.0.
+** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on,
+** the [VACUUM] command will fail with an obscure error when attempting to
+** process a table with generated columns and a descending index. This is
+** not considered a bug since SQLite versions 3.3.0 and earlier do not support
+** either generated columns or descending indexes.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]]
+** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt>
+** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in
+** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears
+** a flag that enables collection of the sqlite3_stmt_scanstatus_v2()
+** statistics. For statistics to be collected, the flag must be set on
+** the database handle both when the SQL statement is prepared and when it
+** is stepped. The flag is set (collection of statistics is enabled)
+** by default. This option takes two arguments: an integer and a pointer to
+** an integer.. The first argument is 1, 0, or -1 to enable, disable, or
+** leave unchanged the statement scanstatus option. If the second argument
+** is not NULL, then the value of the statement scanstatus setting after
+** processing the first argument is written into the integer that the second
+** argument points to.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]]
+** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</dt>
+** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order
+** in which tables and indexes are scanned so that the scans start at the end
+** and work toward the beginning rather than starting at the beginning and
+** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the
+** same as setting [PRAGMA reverse_unordered_selects]. This option takes
+** two arguments which are an integer and a pointer to an integer. The first
+** argument is 1, 0, or -1 to enable, disable, or leave unchanged the
+** reverse scan order flag, respectively. If the second argument is not NULL,
+** then 0 or 1 is written into the integer that the second argument points to
+** depending on if the reverse scan order flag is set after processing the
+** first argument.
+** </dd>
+**
** </dl>
*/
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
@@ -3229,7 +2815,16 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
-#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */
+#define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */
+#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */
+#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
+#define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */
+#define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */
+#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */
+#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -3256,8 +2851,8 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff);
** ^The sqlite3_last_insert_rowid(D) interface usually returns the [rowid] of
** the most recent successful [INSERT] into a rowid table or [virtual table]
** on database connection D. ^Inserts into [WITHOUT ROWID] tables are not
-** recorded. ^If no successful [INSERT]s into rowid tables have ever occurred
-** on the database connection D, then sqlite3_last_insert_rowid(D) returns
+** recorded. ^If no successful [INSERT]s into rowid tables have ever occurred
+** on the database connection D, then sqlite3_last_insert_rowid(D) returns
** zero.
**
** As well as being set automatically as rows are inserted into database
@@ -3267,15 +2862,15 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff);
** Some virtual table implementations may INSERT rows into rowid tables as
** part of committing a transaction (e.g. to flush data accumulated in memory
** to disk). In this case subsequent calls to this function return the rowid
-** associated with these internal INSERT operations, which leads to
+** associated with these internal INSERT operations, which leads to
** unintuitive results. Virtual table implementations that do write to rowid
-** tables in this way can avoid this problem by restoring the original
-** rowid value using [sqlite3_set_last_insert_rowid()] before returning
+** tables in this way can avoid this problem by restoring the original
+** rowid value using [sqlite3_set_last_insert_rowid()] before returning
** control to the user.
**
-** ^(If an [INSERT] occurs within a trigger then this routine will
-** return the [rowid] of the inserted row as long as the trigger is
-** running. Once the trigger program ends, the value returned
+** ^(If an [INSERT] occurs within a trigger then this routine will
+** return the [rowid] of the inserted row as long as the trigger is
+** running. Once the trigger program ends, the value returned
** by this routine reverts to what it was before the trigger was fired.)^
**
** ^An [INSERT] that fails due to a constraint violation is not a
@@ -3308,7 +2903,7 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
** METHOD: sqlite3
**
** The sqlite3_set_last_insert_rowid(D, R) method allows the application to
-** set the value returned by calling sqlite3_last_insert_rowid(D) to R
+** set the value returned by calling sqlite3_last_insert_rowid(D) to R
** without inserting a row into the database.
*/
SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
@@ -3317,44 +2912,47 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
** CAPI3REF: Count The Number Of Rows Modified
** METHOD: sqlite3
**
-** ^This function returns the number of rows modified, inserted or
+** ^These functions return the number of rows modified, inserted or
** deleted by the most recently completed INSERT, UPDATE or DELETE
** statement on the database connection specified by the only parameter.
-** ^Executing any other type of SQL statement does not modify the value
-** returned by this function.
+** The two functions are identical except for the type of the return value
+** and that if the number of rows modified by the most recent INSERT, UPDATE
+** or DELETE is greater than the maximum value supported by type "int", then
+** the return value of sqlite3_changes() is undefined. ^Executing any other
+** type of SQL statement does not modify the value returned by these functions.
**
** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are
-** considered - auxiliary changes caused by [CREATE TRIGGER | triggers],
+** considered - auxiliary changes caused by [CREATE TRIGGER | triggers],
** [foreign key actions] or [REPLACE] constraint resolution are not counted.
-**
-** Changes to a view that are intercepted by
-** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value
-** returned by sqlite3_changes() immediately after an INSERT, UPDATE or
-** DELETE statement run on a view is always zero. Only changes made to real
+**
+** Changes to a view that are intercepted by
+** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value
+** returned by sqlite3_changes() immediately after an INSERT, UPDATE or
+** DELETE statement run on a view is always zero. Only changes made to real
** tables are counted.
**
** Things are more complicated if the sqlite3_changes() function is
** executed while a trigger program is running. This may happen if the
** program uses the [changes() SQL function], or if some other callback
** function invokes sqlite3_changes() directly. Essentially:
-**
+**
** <ul>
** <li> ^(Before entering a trigger program the value returned by
-** sqlite3_changes() function is saved. After the trigger program
+** sqlite3_changes() function is saved. After the trigger program
** has finished, the original value is restored.)^
-**
-** <li> ^(Within a trigger program each INSERT, UPDATE and DELETE
-** statement sets the value returned by sqlite3_changes()
-** upon completion as normal. Of course, this value will not include
-** any changes performed by sub-triggers, as the sqlite3_changes()
+**
+** <li> ^(Within a trigger program each INSERT, UPDATE and DELETE
+** statement sets the value returned by sqlite3_changes()
+** upon completion as normal. Of course, this value will not include
+** any changes performed by sub-triggers, as the sqlite3_changes()
** value will be saved and restored after each sub-trigger has run.)^
** </ul>
-**
+**
** ^This means that if the changes() SQL function (or similar) is used
-** by the first INSERT, UPDATE or DELETE statement within a trigger, it
+** by the first INSERT, UPDATE or DELETE statement within a trigger, it
** returns the value as set when the calling statement began executing.
-** ^If it is used by the second or subsequent such statement within a trigger
-** program, the value returned reflects the number of rows modified by the
+** ^If it is used by the second or subsequent such statement within a trigger
+** program, the value returned reflects the number of rows modified by the
** previous INSERT, UPDATE or DELETE statement within the same trigger.
**
** If a separate thread makes changes on the same database connection
@@ -3370,29 +2968,34 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
** </ul>
*/
SQLITE_API int sqlite3_changes(sqlite3*);
+SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3*);
/*
** CAPI3REF: Total Number Of Rows Modified
** METHOD: sqlite3
**
-** ^This function returns the total number of rows inserted, modified or
+** ^These functions return the total number of rows inserted, modified or
** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed
** since the database connection was opened, including those executed as
-** part of trigger programs. ^Executing any other type of SQL statement
-** does not affect the value returned by sqlite3_total_changes().
-**
+** part of trigger programs. The two functions are identical except for the
+** type of the return value and that if the number of rows modified by the
+** connection exceeds the maximum value supported by type "int", then
+** the return value of sqlite3_total_changes() is undefined. ^Executing
+** any other type of SQL statement does not affect the value returned by
+** sqlite3_total_changes().
+**
** ^Changes made as part of [foreign key actions] are included in the
** count, but those made as part of REPLACE constraint resolution are
-** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
+** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
** are not counted.
**
-** This the [sqlite3_total_changes(D)] interface only reports the number
+** The [sqlite3_total_changes(D)] interface only reports the number
** of rows that changed due to SQL statement run against database
** connection D. Any changes by other database connections are ignored.
** To detect changes against a database file from other database
** connections use the [PRAGMA data_version] command or the
** [SQLITE_FCNTL_DATA_VERSION] [file control].
-**
+**
** If a separate thread makes changes on the same database connection
** while [sqlite3_total_changes()] is running then the value
** returned is unpredictable and not meaningful.
@@ -3407,6 +3010,7 @@ SQLITE_API int sqlite3_changes(sqlite3*);
** </ul>
*/
SQLITE_API int sqlite3_total_changes(sqlite3*);
+SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*);
/*
** CAPI3REF: Interrupt A Long-Running Query
@@ -3434,16 +3038,21 @@ SQLITE_API int sqlite3_total_changes(sqlite3*);
**
** ^The sqlite3_interrupt(D) call is in effect until all currently running
** SQL statements on [database connection] D complete. ^Any new SQL statements
-** that are started after the sqlite3_interrupt() call and before the
-** running statements reaches zero are interrupted as if they had been
+** that are started after the sqlite3_interrupt() call and before the
+** running statement count reaches zero are interrupted as if they had been
** running prior to the sqlite3_interrupt() call. ^New SQL statements
** that are started after the running statement count reaches zero are
** not effected by the sqlite3_interrupt().
** ^A call to sqlite3_interrupt(D) that occurs when there are no running
** SQL statements is a no-op and has no effect on SQL statements
** that are started after the sqlite3_interrupt() call returns.
+**
+** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether
+** or not an interrupt is currently in effect for [database connection] D.
+** It returns 1 if an interrupt is currently in effect, or 0 otherwise.
*/
SQLITE_API void sqlite3_interrupt(sqlite3*);
+SQLITE_API int sqlite3_is_interrupted(sqlite3*);
/*
** CAPI3REF: Determine If An SQL Statement Is Complete
@@ -3466,7 +3075,7 @@ SQLITE_API void sqlite3_interrupt(sqlite3*);
** ^These routines do not parse the SQL statements thus
** will not detect syntactically incorrect SQL.
**
-** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior
+** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior
** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked
** automatically by sqlite3_complete16(). If that initialization fails,
** then the return value from sqlite3_complete16() will be non-zero
@@ -3511,7 +3120,7 @@ SQLITE_API int sqlite3_complete16(const void *sql);
** The presence of a busy handler does not guarantee that it will be invoked
** when there is lock contention. ^If SQLite determines that invoking the busy
** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY]
-** to the application instead of invoking the
+** to the application instead of invoking the
** busy handler.
** Consider a scenario where one process is holding a read lock that
** it is trying to promote to a reserved lock and
@@ -3536,7 +3145,7 @@ SQLITE_API int sqlite3_complete16(const void *sql);
** database connection that invoked the busy handler. In other words,
** the busy handler is not reentrant. Any such actions
** result in undefined behavior.
-**
+**
** A busy handler must not close the database connection
** or [prepared statement] that invoked the busy handler.
*/
@@ -3603,9 +3212,9 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
** Cindy | 21
** </pre></blockquote>
**
-** There are two column (M==2) and three rows (N==3). Thus the
+** There are two columns (M==2) and three rows (N==3). Thus the
** result table has 8 entries. Suppose the result table is stored
-** in an array names azResult. Then azResult holds this content:
+** in an array named azResult. Then azResult holds this content:
**
** <blockquote><pre>
** azResult&#91;0] = "Name";
@@ -3654,7 +3263,7 @@ SQLITE_API void sqlite3_free_table(char **result);
** These routines are work-alikes of the "printf()" family of functions
** from the standard C library.
** These routines understand most of the common formatting options from
-** the standard library printf()
+** the standard library printf()
** plus some additional non-standard formats ([%q], [%Q], [%w], and [%z]).
** See the [built-in printf()] documentation for details.
**
@@ -3698,7 +3307,7 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
**
** The SQLite core uses these three routines for all of its own
** internal memory allocation needs. "Core" in the previous sentence
-** does not include operating-system specific VFS implementation. The
+** does not include operating-system specific [VFS] implementation. The
** Windows VFS uses native malloc() and free() for some operations.
**
** ^The sqlite3_malloc() routine returns a pointer to a block
@@ -3759,19 +3368,6 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time
** option is used.
**
-** In SQLite version 3.5.0 and 3.5.1, it was possible to define
-** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in
-** implementation of these routines to be omitted. That capability
-** is no longer provided. Only built-in memory allocators can be used.
-**
-** Prior to SQLite version 3.7.10, the Windows OS interface layer called
-** the system malloc() and free() directly when converting
-** filenames between the UTF-8 encoding used by SQLite
-** and whatever filename encoding is used by the particular Windows
-** installation. Memory allocation errors were detected, but
-** they were reported back as [SQLITE_CANTOPEN] or
-** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
-**
** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
** must be either NULL or else pointers obtained from a prior
** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have
@@ -3820,7 +3416,7 @@ SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
** SQLite contains a high-quality pseudo-random number generator (PRNG) used to
** select random [ROWID | ROWIDs] when inserting new records into a table that
** already uses the largest possible [ROWID]. The PRNG is also used for
-** the build-in random() and randomblob() SQL functions. This interface allows
+** the built-in random() and randomblob() SQL functions. This interface allows
** applications to access the same PRNG for other purposes.
**
** ^A call to this routine stores N bytes of randomness into buffer P.
@@ -3863,7 +3459,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P);
** requested is ok. ^When the callback returns [SQLITE_DENY], the
** [sqlite3_prepare_v2()] or equivalent call that triggered the
** authorizer will fail with an error message explaining that
-** access is denied.
+** access is denied.
**
** ^The first parameter to the authorizer callback is a copy of the third
** parameter to the sqlite3_set_authorizer() interface. ^The second parameter
@@ -3916,7 +3512,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P);
** database connections for the meaning of "modify" in this paragraph.
**
** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the
-** statement might be re-prepared during [sqlite3_step()] due to a
+** statement might be re-prepared during [sqlite3_step()] due to a
** schema change. Hence, the application should ensure that the
** correct authorizer callback remains in place during the [sqlite3_step()].
**
@@ -4030,9 +3626,9 @@ SQLITE_API int sqlite3_set_authorizer(
** time is in units of nanoseconds, however the current implementation
** is only capable of millisecond resolution so the six least significant
** digits in the time are meaningless. Future versions of SQLite
-** might provide greater resolution on the profiler callback. The
-** sqlite3_profile() function is considered experimental and is
-** subject to change in future versions of SQLite.
+** might provide greater resolution on the profiler callback. Invoking
+** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the
+** profile callback.
*/
SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*,
void(*xTrace)(void*,const char*), void*);
@@ -4064,7 +3660,7 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
** execution of the prepared statement, such as at the start of each
** trigger subprogram. ^The P argument is a pointer to the
** [prepared statement]. ^The X argument is a pointer to a string which
-** is the unexpanded SQL text of the prepared statement or an SQL comment
+** is the unexpanded SQL text of the prepared statement or an SQL comment
** that indicates the invocation of a trigger. ^The callback can compute
** the same text that would have been returned by the legacy [sqlite3_trace()]
** interface by using the X argument when X begins with "--" and invoking
@@ -4074,13 +3670,13 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
** <dd>^An SQLITE_TRACE_PROFILE callback provides approximately the same
** information as is provided by the [sqlite3_profile()] callback.
** ^The P argument is a pointer to the [prepared statement] and the
-** X argument points to a 64-bit integer which is the estimated of
-** the number of nanosecond that the prepared statement took to run.
+** X argument points to a 64-bit integer which is approximately
+** the number of nanoseconds that the prepared statement took to run.
** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes.
**
** [[SQLITE_TRACE_ROW]] <dt>SQLITE_TRACE_ROW</dt>
** <dd>^An SQLITE_TRACE_ROW callback is invoked whenever a prepared
-** statement generates a single row of result.
+** statement generates a single row of result.
** ^The P argument is a pointer to the [prepared statement] and the
** X argument is unused.
**
@@ -4107,10 +3703,12 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
** M argument should be the bitwise OR-ed combination of
** zero or more [SQLITE_TRACE] constants.
**
-** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides
-** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2().
+** ^Each call to either sqlite3_trace(D,X,P) or sqlite3_trace_v2(D,M,X,P)
+** overrides (cancels) all prior calls to sqlite3_trace(D,X,P) or
+** sqlite3_trace_v2(D,M,X,P) for the [database connection] D. Each
+** database connection may have at most one trace callback.
**
-** ^The X callback is invoked whenever any of the events identified by
+** ^The X callback is invoked whenever any of the events identified by
** mask M occur. ^The integer return value from the callback is currently
** ignored, though this may change in future releases. Callback
** implementations should return zero to ensure future compatibility.
@@ -4138,12 +3736,12 @@ SQLITE_API int sqlite3_trace_v2(
**
** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback
** function X to be invoked periodically during long running calls to
-** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for
+** [sqlite3_step()] and [sqlite3_prepare()] and similar for
** database connection D. An example use for this
** interface is to keep a GUI updated during a large query.
**
-** ^The parameter P is passed through as the only parameter to the
-** callback function X. ^The parameter N is the approximate number of
+** ^The parameter P is passed through as the only parameter to the
+** callback function X. ^The parameter N is the approximate number of
** [virtual machine instructions] that are evaluated between successive
** invocations of the callback X. ^If N is less than one then the progress
** handler is disabled.
@@ -4163,6 +3761,13 @@ SQLITE_API int sqlite3_trace_v2(
** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
** database connections for the meaning of "modify" in this paragraph.
**
+** The progress handler callback would originally only be invoked from the
+** bytecode engine. It still might be invoked during [sqlite3_prepare()]
+** and similar because those routines might force a reparse of the schema
+** which involves running the bytecode engine. However, beginning with
+** SQLite version 3.41.0, the progress handler callback might also be
+** invoked directly from [sqlite3_prepare()] while analyzing and generating
+** code for complex queries.
*/
SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
@@ -4170,7 +3775,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** CAPI3REF: Opening A New Database Connection
** CONSTRUCTOR: sqlite3
**
-** ^These routines open an SQLite database file as specified by the
+** ^These routines open an SQLite database file as specified by the
** filename argument. ^The filename argument is interpreted as UTF-8 for
** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
** order for sqlite3_open16(). ^(A [database connection] handle is usually
@@ -4194,20 +3799,23 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** The sqlite3_open_v2() interface works like sqlite3_open()
** except that it accepts two additional parameters for additional control
** over the new database connection. ^(The flags parameter to
-** sqlite3_open_v2() can take one of
-** the following three values, optionally combined with the
-** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE],
-** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^
+** sqlite3_open_v2() must include, at a minimum, one of the following
+** three flag combinations:)^
**
** <dl>
** ^(<dt>[SQLITE_OPEN_READONLY]</dt>
-** <dd>The database is opened in read-only mode. If the database does not
-** already exist, an error is returned.</dd>)^
+** <dd>The database is opened in read-only mode. If the database does
+** not already exist, an error is returned.</dd>)^
**
** ^(<dt>[SQLITE_OPEN_READWRITE]</dt>
-** <dd>The database is opened for reading and writing if possible, or reading
-** only if the file is write protected by the operating system. In either
-** case the database must already exist, otherwise an error is returned.</dd>)^
+** <dd>The database is opened for reading and writing if possible, or
+** reading only if the file is write protected by the operating
+** system. In either case the database must already exist, otherwise
+** an error is returned. For historical reasons, if opening in
+** read-write mode fails due to OS-level permissions, an attempt is
+** made to open it in read-only mode. [sqlite3_db_readonly()] can be
+** used to determine whether the database is actually
+** read-write.</dd>)^
**
** ^(<dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt>
** <dd>The database is opened for reading and writing, and is created if
@@ -4215,22 +3823,69 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** sqlite3_open() and sqlite3_open16().</dd>)^
** </dl>
**
+** In addition to the required flags, the following optional flags are
+** also supported:
+**
+** <dl>
+** ^(<dt>[SQLITE_OPEN_URI]</dt>
+** <dd>The filename can be interpreted as a URI if this flag is set.</dd>)^
+**
+** ^(<dt>[SQLITE_OPEN_MEMORY]</dt>
+** <dd>The database will be opened as an in-memory database. The database
+** is named by the "filename" argument for the purposes of cache-sharing,
+** if shared cache mode is enabled, but the "filename" is otherwise ignored.
+** </dd>)^
+**
+** ^(<dt>[SQLITE_OPEN_NOMUTEX]</dt>
+** <dd>The new database connection will use the "multi-thread"
+** [threading mode].)^ This means that separate threads are allowed
+** to use SQLite at the same time, as long as each thread is using
+** a different [database connection].
+**
+** ^(<dt>[SQLITE_OPEN_FULLMUTEX]</dt>
+** <dd>The new database connection will use the "serialized"
+** [threading mode].)^ This means the multiple threads can safely
+** attempt to use the same database connection at the same time.
+** (Mutexes will block any actual concurrency, but in this mode
+** there is no harm in trying.)
+**
+** ^(<dt>[SQLITE_OPEN_SHAREDCACHE]</dt>
+** <dd>The database is opened [shared cache] enabled, overriding
+** the default shared cache setting provided by
+** [sqlite3_enable_shared_cache()].)^
+** The [use of shared cache mode is discouraged] and hence shared cache
+** capabilities may be omitted from many builds of SQLite. In such cases,
+** this option is a no-op.
+**
+** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
+** <dd>The database is opened [shared cache] disabled, overriding
+** the default shared cache setting provided by
+** [sqlite3_enable_shared_cache()].)^
+**
+** [[OPEN_EXRESCODE]] ^(<dt>[SQLITE_OPEN_EXRESCODE]</dt>
+** <dd>The database connection comes up in "extended result code mode".
+** In other words, the database behaves has if
+** [sqlite3_extended_result_codes(db,1)] where called on the database
+** connection as soon as the connection is created. In addition to setting
+** the extended result code mode, this flag also causes [sqlite3_open_v2()]
+** to return an extended result code.</dd>
+**
+** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt>
+** <dd>The database filename is not allowed to contain a symbolic link</dd>
+** </dl>)^
+**
** If the 3rd parameter to sqlite3_open_v2() is not one of the
-** combinations shown above optionally combined with other
+** required combinations shown above optionally combined with other
** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits]
-** then the behavior is undefined.
-**
-** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection
-** opens in the multi-thread [threading mode] as long as the single-thread
-** mode has not been set at compile-time or start-time. ^If the
-** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens
-** in the serialized [threading mode] unless single-thread was
-** previously selected at compile-time or start-time.
-** ^The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be
-** eligible to use [shared cache mode], regardless of whether or not shared
-** cache is enabled using [sqlite3_enable_shared_cache()]. ^The
-** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not
-** participate in [shared cache mode] even if it is enabled.
+** then the behavior is undefined. Historic versions of SQLite
+** have silently ignored surplus bits in the flags parameter to
+** sqlite3_open_v2(), however that behavior might not be carried through
+** into future versions of SQLite and so applications should not rely
+** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op
+** for sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause
+** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE
+** flag is intended for use by the [sqlite3_vfs|VFS interface] only, and not
+** by sqlite3_open_v2().
**
** ^The fourth parameter to sqlite3_open_v2() is the name of the
** [sqlite3_vfs] object that defines the operating system interface that
@@ -4263,17 +3918,17 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** information.
**
** URI filenames are parsed according to RFC 3986. ^If the URI contains an
-** authority, then it must be either an empty string or the string
-** "localhost". ^If the authority is not an empty string or "localhost", an
-** error is returned to the caller. ^The fragment component of a URI, if
+** authority, then it must be either an empty string or the string
+** "localhost". ^If the authority is not an empty string or "localhost", an
+** error is returned to the caller. ^The fragment component of a URI, if
** present, is ignored.
**
** ^SQLite uses the path component of the URI as the name of the disk file
-** which contains the database. ^If the path begins with a '/' character,
-** then it is interpreted as an absolute path. ^If the path does not begin
+** which contains the database. ^If the path begins with a '/' character,
+** then it is interpreted as an absolute path. ^If the path does not begin
** with a '/' (meaning that the authority section is omitted from the URI)
-** then the path is interpreted as a relative path.
-** ^(On windows, the first component of an absolute path
+** then the path is interpreted as a relative path.
+** ^(On windows, the first component of an absolute path
** is a drive specification (e.g. "C:").)^
**
** [[core URI query parameters]]
@@ -4293,13 +3948,13 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
**
** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw",
** "rwc", or "memory". Attempting to set it to any other value is
-** an error)^.
-** ^If "ro" is specified, then the database is opened for read-only
-** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the
-** third argument to sqlite3_open_v2(). ^If the mode option is set to
-** "rw", then the database is opened for read-write (but not create)
-** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had
-** been set. ^Value "rwc" is equivalent to setting both
+** an error)^.
+** ^If "ro" is specified, then the database is opened for read-only
+** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the
+** third argument to sqlite3_open_v2(). ^If the mode option is set to
+** "rw", then the database is opened for read-write (but not create)
+** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had
+** been set. ^Value "rwc" is equivalent to setting both
** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is
** set to "memory" then a pure [in-memory database] that never reads
** or writes from disk is used. ^It is an error to specify a value for
@@ -4309,7 +3964,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or
** "private". ^Setting it to "shared" is equivalent to setting the
** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to
-** sqlite3_open_v2(). ^Setting the cache parameter to "private" is
+** sqlite3_open_v2(). ^Setting the cache parameter to "private" is
** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
** ^If sqlite3_open_v2() is used and the "cache" parameter is present in
** a URI filename, its value overrides any behavior requested by setting
@@ -4335,7 +3990,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** property on a database file that does in fact change can result
** in incorrect query results and/or [SQLITE_CORRUPT] errors.
** See also: [SQLITE_IOCAP_IMMUTABLE].
-**
+**
** </ul>
**
** ^Specifying an unknown parameter in the query component of a URI is not an
@@ -4347,36 +4002,37 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
**
** <table border="1" align=center cellpadding=5>
** <tr><th> URI filenames <th> Results
-** <tr><td> file:data.db <td>
+** <tr><td> file:data.db <td>
** Open the file "data.db" in the current directory.
** <tr><td> file:/home/fred/data.db<br>
-** file:///home/fred/data.db <br>
-** file://localhost/home/fred/data.db <br> <td>
+** file:///home/fred/data.db <br>
+** file://localhost/home/fred/data.db <br> <td>
** Open the database file "/home/fred/data.db".
-** <tr><td> file://darkstar/home/fred/data.db <td>
+** <tr><td> file://darkstar/home/fred/data.db <td>
** An error. "darkstar" is not a recognized authority.
-** <tr><td style="white-space:nowrap">
+** <tr><td style="white-space:nowrap">
** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db
** <td> Windows only: Open the file "data.db" on fred's desktop on drive
-** C:. Note that the %20 escaping in this example is not strictly
+** C:. Note that the %20 escaping in this example is not strictly
** necessary - space characters can be used literally
** in URI filenames.
-** <tr><td> file:data.db?mode=ro&cache=private <td>
+** <tr><td> file:data.db?mode=ro&cache=private <td>
** Open file "data.db" in the current directory for read-only access.
** Regardless of whether or not shared-cache mode is enabled by
** default, use a private cache.
** <tr><td> file:/home/fred/data.db?vfs=unix-dotfile <td>
** Open file "/home/fred/data.db". Use the special VFS "unix-dotfile"
** that uses dot-files in place of posix advisory locking.
-** <tr><td> file:data.db?mode=readonly <td>
+** <tr><td> file:data.db?mode=readonly <td>
** An error. "readonly" is not a valid option for the "mode" parameter.
+** Use "ro" instead: "file:data.db?mode=ro".
** </table>
**
** ^URI hexadecimal escape sequences (%HH) are supported within the path and
** query components of a URI. A hexadecimal escape sequence consists of a
-** percent sign - "%" - followed by exactly two hexadecimal digits
+** percent sign - "%" - followed by exactly two hexadecimal digits
** specifying an octet value. ^Before the path or query components of a
-** URI filename are interpreted, they are encoded using UTF-8 and all
+** URI filename are interpreted, they are encoded using UTF-8 and all
** hexadecimal escape sequences replaced by a single byte containing the
** corresponding octet. If this process generates an invalid UTF-8 encoding,
** the results are undefined.
@@ -4411,17 +4067,27 @@ SQLITE_API int sqlite3_open_v2(
/*
** CAPI3REF: Obtain Values For URI Parameters
**
-** These are utility routines, useful to VFS implementations, that check
-** to see if a database file was a URI that contained a specific query
+** These are utility routines, useful to [VFS|custom VFS implementations],
+** that check if a database file was a URI that contained a specific query
** parameter, and if so obtains the value of that query parameter.
**
-** If F is the database filename pointer passed into the xOpen() method of
-** a VFS implementation when the flags parameter to xOpen() has one or
-** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and
-** P is the name of the query parameter, then
+** The first parameter to these interfaces (hereafter referred to
+** as F) must be one of:
+** <ul>
+** <li> A database filename pointer created by the SQLite core and
+** passed into the xOpen() method of a VFS implementation, or
+** <li> A filename obtained from [sqlite3_db_filename()], or
+** <li> A new filename constructed using [sqlite3_create_filename()].
+** </ul>
+** If the F parameter is not one of the above, then the behavior is
+** undefined and probably undesirable. Older versions of SQLite were
+** more tolerant of invalid F parameters than newer versions.
+**
+** If F is a suitable filename (as described in the previous paragraph)
+** and if P is the name of the query parameter, then
** sqlite3_uri_parameter(F,P) returns the value of the P
-** parameter if it exists or a NULL pointer if P does not appear as a
-** query parameter on F. If P is a query parameter of F
+** parameter if it exists or a NULL pointer if P does not appear as a
+** query parameter on F. If P is a query parameter of F and it
** has no explicit value, then sqlite3_uri_parameter(F,P) returns
** a pointer to an empty string.
**
@@ -4429,39 +4095,160 @@ SQLITE_API int sqlite3_open_v2(
** parameter and returns true (1) or false (0) according to the value
** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the
** value of query parameter P is one of "yes", "true", or "on" in any
-** case or if the value begins with a non-zero number. The
+** case or if the value begins with a non-zero number. The
** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of
** query parameter P is one of "no", "false", or "off" in any case or
** if the value begins with a numeric zero. If P is not a query
-** parameter on F or if the value of P is does not match any of the
+** parameter on F or if the value of P does not match any of the
** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0).
**
** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a
** 64-bit signed integer and returns that integer, or D if P does not
** exist. If the value of P is something other than an integer, then
** zero is returned.
-**
+**
+** The sqlite3_uri_key(F,N) returns a pointer to the name (not
+** the value) of the N-th query parameter for filename F, or a NULL
+** pointer if N is less than zero or greater than the number of query
+** parameters minus 1. The N value is zero-based so N should be 0 to obtain
+** the name of the first query parameter, 1 for the second parameter, and
+** so forth.
+**
** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and
** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and
-** is not a database file pathname pointer that SQLite passed into the xOpen
-** VFS method, then the behavior of this routine is undefined and probably
-** undesirable.
+** is not a database file pathname pointer that the SQLite core passed
+** into the xOpen VFS method, then the behavior of this routine is undefined
+** and probably undesirable.
+**
+** Beginning with SQLite [version 3.31.0] ([dateof:3.31.0]) the input F
+** parameter can also be the name of a rollback journal file or WAL file
+** in addition to the main database file. Prior to version 3.31.0, these
+** routines would only work if F was the name of the main database file.
+** When the F parameter is the name of the rollback journal or WAL file,
+** it has access to all the same query parameters as were found on the
+** main database file.
+**
+** See the [URI filename] documentation for additional information.
+*/
+SQLITE_API const char *sqlite3_uri_parameter(sqlite3_filename z, const char *zParam);
+SQLITE_API int sqlite3_uri_boolean(sqlite3_filename z, const char *zParam, int bDefault);
+SQLITE_API sqlite3_int64 sqlite3_uri_int64(sqlite3_filename, const char*, sqlite3_int64);
+SQLITE_API const char *sqlite3_uri_key(sqlite3_filename z, int N);
+
+/*
+** CAPI3REF: Translate filenames
+**
+** These routines are available to [VFS|custom VFS implementations] for
+** translating filenames between the main database file, the journal file,
+** and the WAL file.
+**
+** If F is the name of an sqlite database file, journal file, or WAL file
+** passed by the SQLite core into the VFS, then sqlite3_filename_database(F)
+** returns the name of the corresponding database file.
+**
+** If F is the name of an sqlite database file, journal file, or WAL file
+** passed by the SQLite core into the VFS, or if F is a database filename
+** obtained from [sqlite3_db_filename()], then sqlite3_filename_journal(F)
+** returns the name of the corresponding rollback journal file.
+**
+** If F is the name of an sqlite database file, journal file, or WAL file
+** that was passed by the SQLite core into the VFS, or if F is a database
+** filename obtained from [sqlite3_db_filename()], then
+** sqlite3_filename_wal(F) returns the name of the corresponding
+** WAL file.
+**
+** In all of the above, if F is not the name of a database, journal or WAL
+** filename passed into the VFS from the SQLite core and F is not the
+** return value from [sqlite3_db_filename()], then the result is
+** undefined and is likely a memory access violation.
+*/
+SQLITE_API const char *sqlite3_filename_database(sqlite3_filename);
+SQLITE_API const char *sqlite3_filename_journal(sqlite3_filename);
+SQLITE_API const char *sqlite3_filename_wal(sqlite3_filename);
+
+/*
+** CAPI3REF: Database File Corresponding To A Journal
+**
+** ^If X is the name of a rollback or WAL-mode journal file that is
+** passed into the xOpen method of [sqlite3_vfs], then
+** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file]
+** object that represents the main database file.
+**
+** This routine is intended for use in custom [VFS] implementations
+** only. It is not a general-purpose interface.
+** The argument sqlite3_file_object(X) must be a filename pointer that
+** has been passed into [sqlite3_vfs].xOpen method where the
+** flags parameter to xOpen contains one of the bits
+** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use
+** of this routine results in undefined and probably undesirable
+** behavior.
*/
-SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
-SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
-SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
+SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
+/*
+** CAPI3REF: Create and Destroy VFS Filenames
+**
+** These interfaces are provided for use by [VFS shim] implementations and
+** are not useful outside of that context.
+**
+** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of
+** database filename D with corresponding journal file J and WAL file W and
+** with N URI parameters key/values pairs in the array P. The result from
+** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that
+** is safe to pass to routines like:
+** <ul>
+** <li> [sqlite3_uri_parameter()],
+** <li> [sqlite3_uri_boolean()],
+** <li> [sqlite3_uri_int64()],
+** <li> [sqlite3_uri_key()],
+** <li> [sqlite3_filename_database()],
+** <li> [sqlite3_filename_journal()], or
+** <li> [sqlite3_filename_wal()].
+** </ul>
+** If a memory allocation error occurs, sqlite3_create_filename() might
+** return a NULL pointer. The memory obtained from sqlite3_create_filename(X)
+** must be released by a corresponding call to sqlite3_free_filename(Y).
+**
+** The P parameter in sqlite3_create_filename(D,J,W,N,P) should be an array
+** of 2*N pointers to strings. Each pair of pointers in this array corresponds
+** to a key and value for a query parameter. The P parameter may be a NULL
+** pointer if N is zero. None of the 2*N pointers in the P array may be
+** NULL pointers and key pointers should not be empty strings.
+** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may
+** be NULL pointers, though they can be empty strings.
+**
+** The sqlite3_free_filename(Y) routine releases a memory allocation
+** previously obtained from sqlite3_create_filename(). Invoking
+** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op.
+**
+** If the Y parameter to sqlite3_free_filename(Y) is anything other
+** than a NULL pointer or a pointer previously acquired from
+** sqlite3_create_filename(), then bad things such as heap
+** corruption or segfaults may occur. The value Y should not be
+** used again after sqlite3_free_filename(Y) has been called. This means
+** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y,
+** then the corresponding [sqlite3_module.xClose() method should also be
+** invoked prior to calling sqlite3_free_filename(Y).
+*/
+SQLITE_API sqlite3_filename sqlite3_create_filename(
+ const char *zDatabase,
+ const char *zJournal,
+ const char *zWal,
+ int nParam,
+ const char **azParam
+);
+SQLITE_API void sqlite3_free_filename(sqlite3_filename);
/*
** CAPI3REF: Error Codes And Messages
** METHOD: sqlite3
**
-** ^If the most recent sqlite3_* API call associated with
+** ^If the most recent sqlite3_* API call associated with
** [database connection] D failed, then the sqlite3_errcode(D) interface
** returns the numeric [result code] or [extended result code] for that
** API call.
** ^The sqlite3_extended_errcode()
-** interface is the same except that it always returns the
+** interface is the same except that it always returns the
** [extended result code] even when extended result codes are
** disabled.
**
@@ -4469,27 +4256,38 @@ SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int
** sqlite3_extended_errcode() might change with each API call.
** Except, there are some interfaces that are guaranteed to never
** change the value of the error code. The error-code preserving
-** interfaces are:
+** interfaces include the following:
**
** <ul>
** <li> sqlite3_errcode()
** <li> sqlite3_extended_errcode()
** <li> sqlite3_errmsg()
** <li> sqlite3_errmsg16()
+** <li> sqlite3_error_offset()
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
-** text that describes the error, as either UTF-8 or UTF-16 respectively.
+** text that describes the error, as either UTF-8 or UTF-16 respectively,
+** or NULL if no error message is available.
+** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
-** ^The sqlite3_errstr() interface returns the English-language text
-** that describes the [result code], as UTF-8.
+** ^The sqlite3_errstr(E) interface returns the English-language text
+** that describes the [result code] E, as UTF-8, or NULL if E is not an
+** result code for which a text error message is available.
** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
+** ^If the most recent error references a specific token in the input
+** SQL, the sqlite3_error_offset() interface returns the byte offset
+** of the start of that token. ^The byte offset returned by
+** sqlite3_error_offset() assumes that the input SQL is UTF8.
+** ^If the most recent error does not reference a specific token in the input
+** SQL, then the sqlite3_error_offset() function returns -1.
+**
** When the serialized [threading mode] is in use, it might be the
** case that a second error occurs on a separate thread in between
** the time of the first error and the call to these interfaces.
@@ -4509,6 +4307,7 @@ SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
SQLITE_API const char *sqlite3_errstr(int);
+SQLITE_API int sqlite3_error_offset(sqlite3 *db);
/*
** CAPI3REF: Prepared Statement Object
@@ -4518,7 +4317,7 @@ SQLITE_API const char *sqlite3_errstr(int);
** has been compiled into binary form and is ready to be evaluated.
**
** Think of each SQL statement as a separate computer program. The
-** original SQL text is source code. A prepared statement object
+** original SQL text is source code. A prepared statement object
** is the compiled object code. All SQL must be converted into a
** prepared statement before it can be run.
**
@@ -4548,7 +4347,7 @@ typedef struct sqlite3_stmt sqlite3_stmt;
** new limit for that construct.)^
**
** ^If the new limit is a negative number, the limit is unchanged.
-** ^(For each limit category SQLITE_LIMIT_<i>NAME</i> there is a
+** ^(For each limit category SQLITE_LIMIT_<i>NAME</i> there is a
** [limits | hard upper bound]
** set at compile-time by a C preprocessor macro called
** [limits | SQLITE_MAX_<i>NAME</i>].
@@ -4556,7 +4355,7 @@ typedef struct sqlite3_stmt sqlite3_stmt;
** ^Attempts to increase a limit above its hard upper bound are
** silently truncated to the hard upper bound.
**
-** ^Regardless of whether or not the limit was changed, the
+** ^Regardless of whether or not the limit was changed, the
** [sqlite3_limit()] interface returns the prior value of the limit.
** ^Hence, to find the current value of a limit without changing it,
** simply invoke this interface with the third parameter set to -1.
@@ -4661,25 +4460,30 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** <dd>The SQLITE_PREPARE_PERSISTENT flag is a hint to the query planner
** that the prepared statement will be retained for a long time and
** probably reused many times.)^ ^Without this flag, [sqlite3_prepare_v3()]
-** and [sqlite3_prepare16_v3()] assume that the prepared statement will
+** and [sqlite3_prepare16_v3()] assume that the prepared statement will
** be used just once or at most a few times and then destroyed using
** [sqlite3_finalize()] relatively soon. The current implementation acts
** on this hint by avoiding the use of [lookaside memory] so as not to
** deplete the limited store of lookaside memory. Future versions of
** SQLite may act on this hint differently.
**
-** [[SQLITE_PREPARE_NORMALIZE]] ^(<dt>SQLITE_PREPARE_NORMALIZE</dt>
-** <dd>The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized
-** representation of the SQL statement should be calculated and then
-** associated with the prepared statement, which can be obtained via
-** the [sqlite3_normalized_sql()] interface.)^ The semantics used to
-** normalize a SQL statement are unspecified and subject to change.
-** At a minimum, literal values will be replaced with suitable
-** placeholders.
+** [[SQLITE_PREPARE_NORMALIZE]] <dt>SQLITE_PREPARE_NORMALIZE</dt>
+** <dd>The SQLITE_PREPARE_NORMALIZE flag is a no-op. This flag used
+** to be required for any prepared statement that wanted to use the
+** [sqlite3_normalized_sql()] interface. However, the
+** [sqlite3_normalized_sql()] interface is now available to all
+** prepared statements, regardless of whether or not they use this
+** flag.
+**
+** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
+** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
+** to return an error (error code SQLITE_ERROR) if the statement uses
+** any virtual tables.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
+#define SQLITE_PREPARE_NO_VTAB 0x04
/*
** CAPI3REF: Compiling An SQL Statement
@@ -4763,15 +4567,15 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** </li>
**
** <li>
-** ^If the specific value bound to [parameter | host parameter] in the
+** ^If the specific value bound to a [parameter | host parameter] in the
** WHERE clause might influence the choice of query plan for a statement,
-** then the statement will be automatically recompiled, as if there had been
-** a schema change, on the first [sqlite3_step()] call following any change
-** to the [sqlite3_bind_text | bindings] of that [parameter].
-** ^The specific value of WHERE-clause [parameter] might influence the
+** then the statement will be automatically recompiled, as if there had been
+** a schema change, on the first [sqlite3_step()] call following any change
+** to the [sqlite3_bind_text | bindings] of that [parameter].
+** ^The specific value of a WHERE-clause [parameter] might influence the
** choice of query plan if the parameter is the left-hand side of a [LIKE]
** or [GLOB] operator or if the parameter is compared to an indexed column
-** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
+** and the [SQLITE_ENABLE_STAT4] compile-time option is enabled.
** </li>
** </ol>
**
@@ -4861,12 +4665,17 @@ SQLITE_API int sqlite3_prepare16_v3(
** are managed by SQLite and are automatically freed when the prepared
** statement is finalized.
** ^The string returned by sqlite3_expanded_sql(P), on the other hand,
-** is obtained from [sqlite3_malloc()] and must be free by the application
+** is obtained from [sqlite3_malloc()] and must be freed by the application
** by passing it to [sqlite3_free()].
+**
+** ^The sqlite3_normalized_sql() interface is only available if
+** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined.
*/
SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
+#ifdef SQLITE_ENABLE_NORMALIZE
SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
+#endif
/*
** CAPI3REF: Determine If An SQL Statement Writes The Database
@@ -4877,8 +4686,8 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
** the content of the database file.
**
** Note that [application-defined SQL functions] or
-** [virtual tables] might change the database indirectly as a side effect.
-** ^(For example, if an application defines a function "eval()" that
+** [virtual tables] might change the database indirectly as a side effect.
+** ^(For example, if an application defines a function "eval()" that
** calls [sqlite3_exec()], then the following SQL statement would
** change the database file through side-effects:
**
@@ -4892,35 +4701,95 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK],
** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true,
** since the statements themselves do not actually modify the database but
-** rather they control the timing of when other statements modify the
+** rather they control the timing of when other statements modify the
** database. ^The [ATTACH] and [DETACH] statements also cause
** sqlite3_stmt_readonly() to return true since, while those statements
-** change the configuration of a database connection, they do not make
+** change the configuration of a database connection, they do not make
** changes to the content of the database files on disk.
** ^The sqlite3_stmt_readonly() interface returns true for [BEGIN] since
** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and
** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so
** sqlite3_stmt_readonly() returns false for those commands.
+**
+** ^This routine returns false if there is any possibility that the
+** statement might change the database file. ^A false return does
+** not guarantee that the statement will change the database file.
+** ^For example, an UPDATE statement might have a WHERE clause that
+** makes it a no-op, but the sqlite3_stmt_readonly() result would still
+** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a
+** read-only no-op if the table already exists, but
+** sqlite3_stmt_readonly() still returns false for such a statement.
+**
+** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN]
+** statement, then sqlite3_stmt_readonly(X) returns the same value as
+** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted.
*/
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
/*
+** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement
+** METHOD: sqlite3_stmt
+**
+** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the
+** prepared statement S is an EXPLAIN statement, or 2 if the
+** statement S is an EXPLAIN QUERY PLAN.
+** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is
+** an ordinary statement or a NULL pointer.
+*/
+SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Change The EXPLAIN Setting For A Prepared Statement
+** METHOD: sqlite3_stmt
+**
+** The sqlite3_stmt_explain(S,E) interface changes the EXPLAIN
+** setting for [prepared statement] S. If E is zero, then S becomes
+** a normal prepared statement. If E is 1, then S behaves as if
+** its SQL text began with "[EXPLAIN]". If E is 2, then S behaves as if
+** its SQL text began with "[EXPLAIN QUERY PLAN]".
+**
+** Calling sqlite3_stmt_explain(S,E) might cause S to be reprepared.
+** SQLite tries to avoid a reprepare, but a reprepare might be necessary
+** on the first transition into EXPLAIN or EXPLAIN QUERY PLAN mode.
+**
+** Because of the potential need to reprepare, a call to
+** sqlite3_stmt_explain(S,E) will fail with SQLITE_ERROR if S cannot be
+** reprepared because it was created using [sqlite3_prepare()] instead of
+** the newer [sqlite3_prepare_v2()] or [sqlite3_prepare_v3()] interfaces and
+** hence has no saved SQL text with which to reprepare.
+**
+** Changing the explain setting for a prepared statement does not change
+** the original SQL text for the statement. Hence, if the SQL text originally
+** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0)
+** is called to convert the statement into an ordinary statement, the EXPLAIN
+** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S)
+** output, even though the statement now acts like a normal SQL statement.
+**
+** This routine returns SQLITE_OK if the explain mode is successfully
+** changed, or an error code if the explain mode could not be changed.
+** The explain mode cannot be changed while a statement is active.
+** Hence, it is good practice to call [sqlite3_reset(S)]
+** immediately prior to calling sqlite3_stmt_explain(S,E).
+*/
+SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode);
+
+/*
** CAPI3REF: Determine If A Prepared Statement Has Been Reset
** METHOD: sqlite3_stmt
**
** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the
-** [prepared statement] S has been stepped at least once using
+** [prepared statement] S has been stepped at least once using
** [sqlite3_step(S)] but has neither run to completion (returned
** [SQLITE_DONE] from [sqlite3_step(S)]) nor
** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S)
-** interface returns false if S is a NULL pointer. If S is not a
+** interface returns false if S is a NULL pointer. If S is not a
** NULL pointer and is not a pointer to a valid [prepared statement]
** object, then the behavior is undefined and probably undesirable.
**
** This interface can be used in combination [sqlite3_next_stmt()]
-** to locate all prepared statements associated with a database
+** to locate all prepared statements associated with a database
** connection that are in need of being reset. This can be used,
-** for example, in diagnostic routines to search for prepared
+** for example, in diagnostic routines to search for prepared
** statements that are holding a transaction open.
*/
SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
@@ -4939,7 +4808,7 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
** will accept either a protected or an unprotected sqlite3_value.
** Every interface that accepts sqlite3_value arguments specifies
** whether or not it requires a protected sqlite3_value. The
-** [sqlite3_value_dup()] interface can be used to construct a new
+** [sqlite3_value_dup()] interface can be used to construct a new
** protected sqlite3_value from an unprotected sqlite3_value.
**
** The terms "protected" and "unprotected" refer to whether or not
@@ -4947,7 +4816,7 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
** sqlite3_value object but no mutex is held for an unprotected
** sqlite3_value object. If SQLite is compiled to be single-threaded
** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0)
-** or if SQLite is run in one of reduced mutex modes
+** or if SQLite is run in one of reduced mutex modes
** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD]
** then there is no distinction between protected and unprotected
** sqlite3_value objects and they can be used interchangeably. However,
@@ -4957,6 +4826,8 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
**
** ^The sqlite3_value objects that are passed as parameters into the
** implementation of [application-defined SQL functions] are protected.
+** ^The sqlite3_value objects returned by [sqlite3_vtab_rhs_value()]
+** are protected.
** ^The sqlite3_value object returned by
** [sqlite3_column_value()] is unprotected.
** Unprotected sqlite3_value objects may only be used as arguments
@@ -5016,12 +4887,30 @@ typedef struct sqlite3_context sqlite3_context;
** [sqlite3_bind_parameter_index()] API if desired. ^The index
** for "?NNN" parameters is the value of NNN.
** ^The NNN value must be between 1 and the [sqlite3_limit()]
-** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999).
+** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 32766).
**
** ^The third argument is the value to bind to the parameter.
** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
** is ignored and the end result is the same as sqlite3_bind_null().
+** ^If the third parameter to sqlite3_bind_text() is not NULL, then
+** it should be a pointer to well-formed UTF8 text.
+** ^If the third parameter to sqlite3_bind_text16() is not NULL, then
+** it should be a pointer to well-formed UTF16 text.
+** ^If the third parameter to sqlite3_bind_text64() is not NULL, then
+** it should be a pointer to a well-formed unicode string that is
+** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16
+** otherwise.
+**
+** [[byte-order determination rules]] ^The byte-order of
+** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF)
+** found in first character, which is removed, or in the absence of a BOM
+** the byte order is the native byte order of the host
+** machine for sqlite3_bind_text16() or the byte order specified in
+** the 6th parameter for sqlite3_bind_text64().)^
+** ^If UTF16 input text contains invalid unicode
+** characters, then SQLite might change those invalid characters
+** into the unicode replacement character: U+FFFD.
**
** ^(In those routines that have a fourth argument, its value is the
** number of bytes in the parameter. To be clear: the value is the
@@ -5035,21 +4924,27 @@ typedef struct sqlite3_context sqlite3_context;
** or sqlite3_bind_text16() or sqlite3_bind_text64() then
** that parameter must be the byte offset
** where the NUL terminator would occur assuming the string were NUL
-** terminated. If any NUL characters occur at byte offsets less than
+** terminated. If any NUL characters occurs at byte offsets less than
** the value of the fourth parameter then the resulting string value will
** contain embedded NULs. The result of expressions involving strings
** with embedded NULs is undefined.
**
-** ^The fifth argument to the BLOB and string binding interfaces
-** is a destructor used to dispose of the BLOB or
-** string after SQLite has finished with it. ^The destructor is called
-** to dispose of the BLOB or string even if the call to bind API fails.
-** ^If the fifth argument is
-** the special value [SQLITE_STATIC], then SQLite assumes that the
-** information is in static, unmanaged space and does not need to be freed.
-** ^If the fifth argument has the value [SQLITE_TRANSIENT], then
-** SQLite makes its own private copy of the data immediately, before
-** the sqlite3_bind_*() routine returns.
+** ^The fifth argument to the BLOB and string binding interfaces controls
+** or indicates the lifetime of the object referenced by the third parameter.
+** These three options exist:
+** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished
+** with it may be passed. ^It is called to dispose of the BLOB or string even
+** if the call to the bind API fails, except the destructor is not called if
+** the third parameter is a NULL pointer or the fourth parameter is negative.
+** ^ (2) The special constant, [SQLITE_STATIC], may be passed to indicate that
+** the application remains responsible for disposing of the object. ^In this
+** case, the object and the provided pointer to it must remain valid until
+** either the prepared statement is finalized or the same SQL parameter is
+** bound to something else, whichever occurs sooner.
+** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the
+** object is to be copied prior to the return from sqlite3_bind_*(). ^The
+** object and pointer to it must remain valid until then. ^SQLite will then
+** manage the lifetime of its private copy.
**
** ^The sixth argument to sqlite3_bind_text64() must be one of
** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]
@@ -5195,7 +5090,7 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*);
** METHOD: sqlite3_stmt
**
** ^Return the number of columns in the result set returned by the
-** [prepared statement]. ^If this routine returns 0, that means the
+** [prepared statement]. ^If this routine returns 0, that means the
** [prepared statement] returns no data (for example an [UPDATE]).
** ^However, just because this routine returns a positive number does not
** mean that one or more rows of data will be returned. ^A SELECT statement
@@ -5263,7 +5158,7 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N);
**
** ^If the Nth column returned by the statement is an expression or
** subquery and is not a column value, then all of these functions return
-** NULL. ^These routine might also return NULL if a memory allocation error
+** NULL. ^These routines might also return NULL if a memory allocation error
** occurs. ^Otherwise, they return the name of the attached database, table,
** or column that query result column was extracted from.
**
@@ -5273,10 +5168,6 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N);
** ^These APIs are only available if the library was compiled with the
** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol.
**
-** If two or more threads call one or more of these routines against the same
-** prepared statement and column at the same time then the results are
-** undefined.
-**
** If two or more threads call one or more
** [sqlite3_column_database_name | column metadata interfaces]
** for the same [prepared statement] and result column
@@ -5381,7 +5272,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
** For all versions of SQLite up to and including 3.6.23.1, a call to
** [sqlite3_reset()] was required after sqlite3_step() returned anything
** other than [SQLITE_ROW] before any subsequent invocation of
-** sqlite3_step(). Failure to reset the prepared statement using
+** sqlite3_step(). Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
** sqlite3_step() began
@@ -5413,7 +5304,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*);
** ^The sqlite3_data_count(P) interface returns the number of columns in the
** current row of the result set of [prepared statement] P.
** ^If prepared statement P does not have results ready to return
-** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of
+** (via calls to the [sqlite3_column_int | sqlite3_column()] family of
** interfaces) then sqlite3_data_count(P) returns 0.
** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer.
** ^The sqlite3_data_count(P) routine returns 0 if the previous call to
@@ -5472,7 +5363,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** <tr><td><b>sqlite3_column_int64</b><td>&rarr;<td>64-bit INTEGER result
** <tr><td><b>sqlite3_column_text</b><td>&rarr;<td>UTF-8 TEXT result
** <tr><td><b>sqlite3_column_text16</b><td>&rarr;<td>UTF-16 TEXT result
-** <tr><td><b>sqlite3_column_value</b><td>&rarr;<td>The result as an
+** <tr><td><b>sqlite3_column_value</b><td>&rarr;<td>The result as an
** [sqlite3_value|unprotected sqlite3_value] object.
** <tr><td>&nbsp;<td>&nbsp;<td>&nbsp;
** <tr><td><b>sqlite3_column_bytes</b><td>&rarr;<td>Size of a BLOB
@@ -5520,7 +5411,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** The return value of sqlite3_column_type() can be used to decide which
** of the first six interface should be used to extract the column value.
** The value returned by sqlite3_column_type() is only meaningful if no
-** automatic type conversions have occurred for the value in question.
+** automatic type conversions have occurred for the value in question.
** After a type conversion, the result of calling sqlite3_column_type()
** is undefined, though harmless. Future
** versions of SQLite may change the behavior of sqlite3_column_type()
@@ -5548,7 +5439,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** the number of bytes in that string.
** ^If the result is NULL, then sqlite3_column_bytes16() returns zero.
**
-** ^The values returned by [sqlite3_column_bytes()] and
+** ^The values returned by [sqlite3_column_bytes()] and
** [sqlite3_column_bytes16()] do not include the zero terminators at the end
** of the string. ^For clarity: the values returned by
** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of
@@ -5558,6 +5449,10 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** even empty strings, are always zero-terminated. ^The return
** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
**
+** ^Strings returned by sqlite3_column_text16() always have the endianness
+** which is native to the platform, regardless of the text encoding set
+** for the database.
+**
** <b>Warning:</b> ^The object returned by [sqlite3_column_value()] is an
** [unprotected sqlite3_value] object. In a multithreaded environment,
** an unprotected sqlite3_value object may only be used safely with
@@ -5567,11 +5462,11 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** to routines like [sqlite3_value_int()], [sqlite3_value_text()],
** or [sqlite3_value_bytes()], the behavior is not threadsafe.
** Hence, the sqlite3_column_value() interface
-** is normally only useful within the implementation of
+** is normally only useful within the implementation of
** [application-defined SQL functions] or [virtual tables], not within
** top-level application code.
**
-** The these routines may attempt to convert the datatype of the result.
+** These routines may attempt to convert the datatype of the result.
** ^For example, if the internal representation is FLOAT and a text result
** is requested, [sqlite3_snprintf()] is used internally to perform the
** conversion automatically. ^(The following table details the conversions
@@ -5596,7 +5491,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** <tr><td> TEXT <td> BLOB <td> No change
** <tr><td> BLOB <td> INTEGER <td> [CAST] to INTEGER
** <tr><td> BLOB <td> FLOAT <td> [CAST] to REAL
-** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed
+** <tr><td> BLOB <td> TEXT <td> [CAST] to TEXT, ensure zero terminator
** </table>
** </blockquote>)^
**
@@ -5720,32 +5615,43 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S
** back to the beginning of its program.
**
-** ^If the most recent call to [sqlite3_step(S)] for the
-** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE],
-** or if [sqlite3_step(S)] has never before been called on S,
-** then [sqlite3_reset(S)] returns [SQLITE_OK].
+** ^The return code from [sqlite3_reset(S)] indicates whether or not
+** the previous evaluation of prepared statement S completed successfully.
+** ^If [sqlite3_step(S)] has never before been called on S or if
+** [sqlite3_step(S)] has not been called since the previous call
+** to [sqlite3_reset(S)], then [sqlite3_reset(S)] will return
+** [SQLITE_OK].
**
** ^If the most recent call to [sqlite3_step(S)] for the
** [prepared statement] S indicated an error, then
** [sqlite3_reset(S)] returns an appropriate [error code].
+** ^The [sqlite3_reset(S)] interface might also return an [error code]
+** if there were no prior errors but the process of resetting
+** the prepared statement caused a new error. ^For example, if an
+** [INSERT] statement with a [RETURNING] clause is only stepped one time,
+** that one call to [sqlite3_step(S)] might return SQLITE_ROW but
+** the overall statement might still fail and the [sqlite3_reset(S)] call
+** might return SQLITE_BUSY if locking constraints prevent the
+** database change from committing. Therefore, it is important that
+** applications check the return code from [sqlite3_reset(S)] even if
+** no prior call to [sqlite3_step(S)] indicated a problem.
**
** ^The [sqlite3_reset(S)] interface does not change the values
** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S.
*/
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
+
/*
** CAPI3REF: Create Or Redefine SQL Functions
** KEYWORDS: {function creation routines}
-** KEYWORDS: {application-defined SQL function}
-** KEYWORDS: {application-defined SQL functions}
** METHOD: sqlite3
**
** ^These functions (collectively known as "function creation routines")
** are used to add SQL functions or aggregates or to redefine the behavior
** of existing SQL functions or aggregates. The only differences between
-** the three "sqlite3_create_function*" routines are the text encoding
-** expected for the second parameter (the name of the function being
+** the three "sqlite3_create_function*" routines are the text encoding
+** expected for the second parameter (the name of the function being
** created) and the presence or absence of a destructor callback for
** the application data pointer. Function sqlite3_create_window_function()
** is similar, but allows the user to supply the extra callback functions
@@ -5759,7 +5665,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** ^The second parameter is the name of the SQL function to be created or
** redefined. ^The length of the name is limited to 255 bytes in a UTF-8
** representation, exclusive of the zero-terminator. ^Note that the name
-** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes.
+** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes.
** ^Any attempt to create a function with a longer name
** will result in [SQLITE_MISUSE] being returned.
**
@@ -5774,7 +5680,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** ^The fourth parameter, eTextRep, specifies what
** [SQLITE_UTF8 | text encoding] this SQL function prefers for
** its parameters. The application should set this parameter to
-** [SQLITE_UTF16LE] if the function implementation invokes
+** [SQLITE_UTF16LE] if the function implementation invokes
** [sqlite3_value_text16le()] on an input, or [SQLITE_UTF16BE] if the
** implementation invokes [sqlite3_value_text16be()] on an input, or
** [SQLITE_UTF16] if [sqlite3_value_text16()] is used, or [SQLITE_UTF8]
@@ -5792,6 +5698,21 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** perform additional optimizations on deterministic functions, so use
** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
**
+** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY]
+** flag, which if present prevents the function from being invoked from
+** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions,
+** index expressions, or the WHERE clause of partial indexes.
+**
+** For best security, the [SQLITE_DIRECTONLY] flag is recommended for
+** all application-defined SQL functions that do not need to be
+** used inside of triggers, view, CHECK constraints, or other elements of
+** the database schema. This flags is especially recommended for SQL
+** functions that have side effects or reveal internal application state.
+** Without this flag, an attacker might be able to modify the schema of
+** a database file to include invocations of the function with parameters
+** chosen by the attacker, which the application will then execute when
+** the database file is opened and read.
+**
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
** function can gain access to this pointer using [sqlite3_user_data()].)^
**
@@ -5805,21 +5726,21 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** SQL function or aggregate, pass NULL pointers for all three function
** callbacks.
**
-** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue
+** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue
** and xInverse) passed to sqlite3_create_window_function are pointers to
** C-language callbacks that implement the new function. xStep and xFinal
** must both be non-NULL. xValue and xInverse may either both be NULL, in
-** which case a regular aggregate function is created, or must both be
+** which case a regular aggregate function is created, or must both be
** non-NULL, in which case the new function may be used as either an aggregate
** or aggregate window function. More details regarding the implementation
-** of aggregate window functions are
+** of aggregate window functions are
** [user-defined window functions|available here].
**
** ^(If the final parameter to sqlite3_create_function_v2() or
** sqlite3_create_window_function() is not NULL, then it is destructor for
-** the application data pointer. The destructor is invoked when the function
-** is deleted, either by being overloaded or when the database connection
-** closes.)^ ^The destructor is also invoked if the call to
+** the application data pointer. The destructor is invoked when the function
+** is deleted, either by being overloaded or when the database connection
+** closes.)^ ^The destructor is also invoked if the call to
** sqlite3_create_function_v2() fails. ^When the destructor callback is
** invoked, it is passed a single argument which is a copy of the application
** data pointer which was the fifth parameter to sqlite3_create_function_v2().
@@ -5832,7 +5753,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** nArg parameter is a better match than a function implementation with
** a negative nArg. ^A function where the preferred text encoding
** matches the database encoding is a better
-** match than a function where the encoding is different.
+** match than a function where the encoding is different.
** ^A function where the encoding difference is between UTF16le and UTF16be
** is a closer match than a function where the encoding difference is
** between UTF8 and UTF16.
@@ -5904,19 +5825,105 @@ SQLITE_API int sqlite3_create_window_function(
/*
** CAPI3REF: Function Flags
**
-** These constants may be ORed together with the
+** These constants may be ORed together with the
** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
** to [sqlite3_create_function()], [sqlite3_create_function16()], or
** [sqlite3_create_function_v2()].
+**
+** <dl>
+** [[SQLITE_DETERMINISTIC]] <dt>SQLITE_DETERMINISTIC</dt><dd>
+** The SQLITE_DETERMINISTIC flag means that the new function always gives
+** the same output when the input parameters are the same.
+** The [abs|abs() function] is deterministic, for example, but
+** [randomblob|randomblob()] is not. Functions must
+** be deterministic in order to be used in certain contexts such as
+** with the WHERE clause of [partial indexes] or in [generated columns].
+** SQLite might also optimize deterministic functions by factoring them
+** out of inner loops.
+** </dd>
+**
+** [[SQLITE_DIRECTONLY]] <dt>SQLITE_DIRECTONLY</dt><dd>
+** The SQLITE_DIRECTONLY flag means that the function may only be invoked
+** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in
+** schema structures such as [CHECK constraints], [DEFAULT clauses],
+** [expression indexes], [partial indexes], or [generated columns].
+** <p>
+** The SQLITE_DIRECTONLY flag is recommended for any
+** [application-defined SQL function]
+** that has side-effects or that could potentially leak sensitive information.
+** This will prevent attacks in which an application is tricked
+** into using a database file that has had its schema surreptitiously
+** modified to invoke the application-defined function in ways that are
+** harmful.
+** <p>
+** Some people say it is good practice to set SQLITE_DIRECTONLY on all
+** [application-defined SQL functions], regardless of whether or not they
+** are security sensitive, as doing so prevents those functions from being used
+** inside of the database schema, and thus ensures that the database
+** can be inspected and modified using generic tools (such as the [CLI])
+** that do not have access to the application-defined functions.
+** </dd>
+**
+** [[SQLITE_INNOCUOUS]] <dt>SQLITE_INNOCUOUS</dt><dd>
+** The SQLITE_INNOCUOUS flag means that the function is unlikely
+** to cause problems even if misused. An innocuous function should have
+** no side effects and should not depend on any values other than its
+** input parameters. The [abs|abs() function] is an example of an
+** innocuous function.
+** The [load_extension() SQL function] is not innocuous because of its
+** side effects.
+** <p> SQLITE_INNOCUOUS is similar to SQLITE_DETERMINISTIC, but is not
+** exactly the same. The [random|random() function] is an example of a
+** function that is innocuous but not deterministic.
+** <p>Some heightened security settings
+** ([SQLITE_DBCONFIG_TRUSTED_SCHEMA] and [PRAGMA trusted_schema=OFF])
+** disable the use of SQL functions inside views and triggers and in
+** schema structures such as [CHECK constraints], [DEFAULT clauses],
+** [expression indexes], [partial indexes], and [generated columns] unless
+** the function is tagged with SQLITE_INNOCUOUS. Most built-in functions
+** are innocuous. Developers are advised to avoid using the
+** SQLITE_INNOCUOUS flag for application-defined functions unless the
+** function has been carefully audited and found to be free of potentially
+** security-adverse side-effects and information-leaks.
+** </dd>
+**
+** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd>
+** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call
+** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
+** This flag instructs SQLite to omit some corner-case optimizations that
+** might disrupt the operation of the [sqlite3_value_subtype()] function,
+** causing it to return zero rather than the correct subtype().
+** SQL functions that invokes [sqlite3_value_subtype()] should have this
+** property. If the SQLITE_SUBTYPE property is omitted, then the return
+** value from [sqlite3_value_subtype()] might sometimes be zero even though
+** a non-zero subtype was specified by the function argument expression.
+**
+** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd>
+** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call
+** [sqlite3_result_subtype()] to cause a sub-type to be associated with its
+** result.
+** Every function that invokes [sqlite3_result_subtype()] should have this
+** property. If it does not, then the call to [sqlite3_result_subtype()]
+** might become a no-op if the function is used as term in an
+** [expression index]. On the other hand, SQL functions that never invoke
+** [sqlite3_result_subtype()] should avoid setting this property, as the
+** purpose of this property is to disable certain optimizations that are
+** incompatible with subtypes.
+** </dd>
+** </dl>
*/
-#define SQLITE_DETERMINISTIC 0x800
+#define SQLITE_DETERMINISTIC 0x000000800
+#define SQLITE_DIRECTONLY 0x000080000
+#define SQLITE_SUBTYPE 0x000100000
+#define SQLITE_INNOCUOUS 0x000200000
+#define SQLITE_RESULT_SUBTYPE 0x001000000
/*
** CAPI3REF: Deprecated Functions
** DEPRECATED
**
** These functions are [deprecated]. In order to maintain
-** backwards compatibility with older code, these functions continue
+** backwards compatibility with older code, these functions continue
** to be supported. However, new applications should avoid
** the use of these functions. To encourage programmers to avoid
** these functions, we will not explain what they do.
@@ -5960,14 +5967,16 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** <tr><td><b>sqlite3_value_nochange&nbsp;&nbsp;</b>
** <td>&rarr;&nbsp;&nbsp;<td>True if the column is unchanged in an UPDATE
** against a virtual table.
+** <tr><td><b>sqlite3_value_frombind&nbsp;&nbsp;</b>
+** <td>&rarr;&nbsp;&nbsp;<td>True if value originated from a [bound parameter]
** </table></blockquote>
**
** <b>Details:</b>
**
** These routines extract type, size, and content information from
** [protected sqlite3_value] objects. Protected sqlite3_value objects
-** are used to pass parameter information into implementation of
-** [application-defined SQL functions] and [virtual tables].
+** are used to pass parameter information into the functions that
+** implement [application-defined SQL functions] and [virtual tables].
**
** These routines work only with [protected sqlite3_value] objects.
** Any attempt to use these routines on an [unprotected sqlite3_value]
@@ -5982,11 +5991,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces
** extract UTF-16 strings as big-endian and little-endian respectively.
**
-** ^If [sqlite3_value] object V was initialized
+** ^If [sqlite3_value] object V was initialized
** using [sqlite3_bind_pointer(S,I,P,X,D)] or [sqlite3_result_pointer(C,P,X,D)]
** and if X and Y are strings that compare equal according to strcmp(X,Y),
** then sqlite3_value_pointer(V,Y) will return the pointer P. ^Otherwise,
-** sqlite3_value_pointer(V,Y) returns a NULL. The sqlite3_bind_pointer()
+** sqlite3_value_pointer(V,Y) returns a NULL. The sqlite3_bind_pointer()
** routine is part of the [pointer passing interface] added for SQLite 3.20.0.
**
** ^(The sqlite3_value_type(V) interface returns the
@@ -6021,6 +6030,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** than within an [xUpdate] method call for an UPDATE statement, then
** the return value is arbitrary and meaningless.
**
+** ^The sqlite3_value_frombind(X) interface returns non-zero if the
+** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()]
+** interfaces. ^If X comes from an SQL literal value, or a table column,
+** or an expression, then sqlite3_value_frombind(X) returns zero.
+**
** Please pay particular attention to the fact that the pointer returned
** from [sqlite3_value_blob()], [sqlite3_value_text()], or
** [sqlite3_value_text16()] can be invalidated by a subsequent call to
@@ -6066,6 +6080,29 @@ SQLITE_API int sqlite3_value_bytes16(sqlite3_value*);
SQLITE_API int sqlite3_value_type(sqlite3_value*);
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
+SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
+
+/*
+** CAPI3REF: Report the internal text encoding state of an sqlite3_value object
+** METHOD: sqlite3_value
+**
+** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
+** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding
+** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X)
+** returns something other than SQLITE_TEXT, then the return value from
+** sqlite3_value_encoding(X) is meaningless. ^Calls to
+** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)],
+** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or
+** [sqlite3_value_bytes16(X)] might change the encoding of the value X and
+** thus change the return from subsequent calls to sqlite3_value_encoding(X).
+**
+** This routine is intended for used by applications that test and validate
+** the SQLite implementation. This routine is inquiring about the opaque
+** internal state of an [sqlite3_value] object. Ordinary applications should
+** not need to know what the internal state of an sqlite3_value object is and
+** hence should not need to use this interface.
+*/
+SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
/*
** CAPI3REF: Finding The Subtype Of SQL Values
@@ -6076,6 +6113,12 @@ SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
** information can be used to pass a limited amount of context from
** one SQL function to another. Use the [sqlite3_result_subtype()]
** routine to set the subtype for the return value of an SQL function.
+**
+** Every [application-defined SQL function] that invoke this interface
+** should include the [SQLITE_SUBTYPE] property in the text
+** encoding argument when the function is [sqlite3_create_function|registered].
+** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype()
+** might return zero instead of the upstream subtype in some corner cases.
*/
SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
@@ -6087,7 +6130,8 @@ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
** object D and returns a pointer to that copy. ^The [sqlite3_value] returned
** is a [protected sqlite3_value] object even if the input is not.
** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
-** memory allocation fails.
+** memory allocation fails. ^If V is a [pointer value], then the result
+** of sqlite3_value_dup(V) is a NULL value.
**
** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object
** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
@@ -6103,9 +6147,9 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*);
** Implementations of aggregate SQL functions use this
** routine to allocate memory for storing their state.
**
-** ^The first time the sqlite3_aggregate_context(C,N) routine is called
-** for a particular aggregate function, SQLite
-** allocates N of memory, zeroes out that memory, and returns a pointer
+** ^The first time the sqlite3_aggregate_context(C,N) routine is called
+** for a particular aggregate function, SQLite allocates
+** N bytes of memory, zeroes out that memory, and returns a pointer
** to the new memory. ^On second and subsequent calls to
** sqlite3_aggregate_context() for the same aggregate function instance,
** the same buffer is returned. Sqlite3_aggregate_context() is normally
@@ -6116,19 +6160,19 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*);
** In those cases, sqlite3_aggregate_context() might be called for the
** first time from within xFinal().)^
**
-** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
+** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
** when first called if N is less than or equal to zero or if a memory
-** allocate error occurs.
+** allocation error occurs.
**
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
** determined by the N parameter on first successful call. Changing the
-** value of N in subsequent call to sqlite3_aggregate_context() within
+** value of N in any subsequent call to sqlite3_aggregate_context() within
** the same aggregate function instance will not resize the memory
** allocation.)^ Within the xFinal callback, it is customary to set
-** N=0 in calls to sqlite3_aggregate_context(C,N) so that no
+** N=0 in calls to sqlite3_aggregate_context(C,N) so that no
** pointless memory allocations occur.
**
-** ^SQLite automatically frees the memory allocated by
+** ^SQLite automatically frees the memory allocated by
** sqlite3_aggregate_context() when the aggregate query concludes.
**
** The first parameter must be a copy of the
@@ -6173,48 +6217,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** METHOD: sqlite3_context
**
** These functions may be used by (non-aggregate) SQL functions to
-** associate metadata with argument values. If the same value is passed to
-** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated metadata may be preserved. An example
-** of where this might be useful is in a regular-expression matching
-** function. The compiled version of the regular expression can be stored as
-** metadata associated with the pattern string.
+** associate auxiliary data with argument values. If the same argument
+** value is passed to multiple invocations of the same SQL function during
+** query execution, under some circumstances the associated auxiliary data
+** might be preserved. An example of where this might be useful is in a
+** regular-expression matching function. The compiled version of the regular
+** expression can be stored as auxiliary data associated with the pattern string.
** Then as long as the pattern string remains the same,
** the compiled regular expression can be reused on multiple
** invocations of the same function.
**
-** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata
+** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data
** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument
** value to the application-defined function. ^N is zero for the left-most
-** function argument. ^If there is no metadata
+** function argument. ^If there is no auxiliary data
** associated with the function argument, the sqlite3_get_auxdata(C,N) interface
** returns a NULL pointer.
**
-** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
-** argument of the application-defined function. ^Subsequent
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the
+** N-th argument of the application-defined function. ^Subsequent
** calls to sqlite3_get_auxdata(C,N) return P from the most recent
-** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
-** NULL if the metadata has been discarded.
+** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or
+** NULL if the auxiliary data has been discarded.
** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
** SQLite will invoke the destructor function X with parameter P exactly
-** once, when the metadata is discarded.
-** SQLite is free to discard the metadata at any time, including: <ul>
+** once, when the auxiliary data is discarded.
+** SQLite is free to discard the auxiliary data at any time, including: <ul>
** <li> ^(when the corresponding function parameter changes)^, or
** <li> ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
** SQL statement)^, or
** <li> ^(when sqlite3_set_auxdata() is invoked again on the same
** parameter)^, or
-** <li> ^(during the original sqlite3_set_auxdata() call when a memory
-** allocation error occurs.)^ </ul>
+** <li> ^(during the original sqlite3_set_auxdata() call when a memory
+** allocation error occurs.)^
+** <li> ^(during the original sqlite3_set_auxdata() call if the function
+** is evaluated during query planning instead of during query execution,
+** as sometimes happens with [SQLITE_ENABLE_STAT4].)^ </ul>
**
-** Note the last bullet in particular. The destructor X in
+** Note the last two bullets in particular. The destructor X in
** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
** should be called near the end of the function implementation and the
** function implementation should not make any use of P after
-** sqlite3_set_auxdata() has been called.
-**
-** ^(In practice, metadata is preserved between function calls for
+** sqlite3_set_auxdata() has been called. Furthermore, a call to
+** sqlite3_get_auxdata() that occurs immediately after a corresponding call
+** to sqlite3_set_auxdata() might still return NULL if an out-of-memory
+** condition occurred during the sqlite3_set_auxdata() call or if the
+** function is being evaluated during query planning rather than during
+** query execution.
+**
+** ^(In practice, auxiliary data is preserved between function calls for
** function parameters that are compile-time constants, including literal
** values and [parameters] and expressions composed from the same.)^
**
@@ -6224,10 +6276,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
**
** These routines must be called from the same thread in which
** the SQL function is running.
+**
+** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()].
*/
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N);
SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
+/*
+** CAPI3REF: Database Connection Client Data
+** METHOD: sqlite3
+**
+** These functions are used to associate one or more named pointers
+** with a [database connection].
+** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P
+** to be attached to [database connection] D using name N. Subsequent
+** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
+** or a NULL pointer if there were no prior calls to
+** sqlite3_set_clientdata() with the same values of D and N.
+** Names are compared using strcmp() and are thus case sensitive.
+**
+** If P and X are both non-NULL, then the destructor X is invoked with
+** argument P on the first of the following occurrences:
+** <ul>
+** <li> An out-of-memory error occurs during the call to
+** sqlite3_set_clientdata() which attempts to register pointer P.
+** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made
+** with the same D and N parameters.
+** <li> The database connection closes. SQLite does not make any guarantees
+** about the order in which destructors are called, only that all
+** destructors will be called exactly once at some point during the
+** database connection closing process.
+** </ul>
+**
+** SQLite does not do anything with client data other than invoke
+** destructors on the client data at the appropriate time. The intended
+** use for client data is to provide a mechanism for wrapper libraries
+** to store additional information about an SQLite database connection.
+**
+** There is no limit (other than available memory) on the number of different
+** client data pointers (with different names) that can be attached to a
+** single database connection. However, the implementation is optimized
+** for the case of having only one or two different client data names.
+** Applications and wrapper libraries are discouraged from using more than
+** one client data name each.
+**
+** There is no way to enumerate the client data pointers
+** associated with a database connection. The N parameter can be thought
+** of as a secret key such that only code that knows the secret key is able
+** to access the associated data.
+**
+** Security Warning: These interfaces should not be exposed in scripting
+** languages or in other circumstances where it might be possible for an
+** an attacker to invoke them. Any agent that can invoke these interfaces
+** can probably also take control of the process.
+**
+** Database connection client data is only available for SQLite
+** version 3.44.0 ([dateof:3.44.0]) and later.
+**
+** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()].
+*/
+SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*);
+SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*));
/*
** CAPI3REF: Constants Defining Special Destructor Behavior
@@ -6279,8 +6388,9 @@ typedef void (*sqlite3_destructor_type)(void*);
** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16()
** as the text of an error message. ^SQLite interprets the error
** message string from sqlite3_result_error() as UTF-8. ^SQLite
-** interprets the string from sqlite3_result_error16() as UTF-16 in native
-** byte order. ^If the third parameter to sqlite3_result_error()
+** interprets the string from sqlite3_result_error16() as UTF-16 using
+** the same [byte-order determination rules] as [sqlite3_bind_text16()].
+** ^If the third parameter to sqlite3_result_error()
** or sqlite3_result_error16() is negative then SQLite takes as the error
** message all text up through the first zero character.
** ^If the third parameter to sqlite3_result_error() or
@@ -6322,9 +6432,10 @@ typedef void (*sqlite3_destructor_type)(void*);
** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
** ^SQLite takes the text result from the application from
** the 2nd parameter of the sqlite3_result_text* interfaces.
-** ^If the 3rd parameter to the sqlite3_result_text* interfaces
-** is negative, then SQLite takes result text from the 2nd parameter
-** through the first zero character.
+** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces
+** other than sqlite3_result_text64() is negative, then SQLite computes
+** the string length itself by searching the 2nd parameter for the first
+** zero character.
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
** is non-negative, then as many bytes (not characters) of the text
** pointed to by the 2nd parameter are taken as the application-defined
@@ -6348,6 +6459,25 @@ typedef void (*sqlite3_destructor_type)(void*);
** then SQLite makes a copy of the result into space obtained
** from [sqlite3_malloc()] before it returns.
**
+** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and
+** sqlite3_result_text16be() routines, and for sqlite3_result_text64()
+** when the encoding is not UTF8, if the input UTF16 begins with a
+** byte-order mark (BOM, U+FEFF) then the BOM is removed from the
+** string and the rest of the string is interpreted according to the
+** byte-order specified by the BOM. ^The byte-order specified by
+** the BOM at the beginning of the text overrides the byte-order
+** specified by the interface procedure. ^So, for example, if
+** sqlite3_result_text16le() is invoked with text that begins
+** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the
+** first two bytes of input are skipped and the remaining input
+** is interpreted as UTF16BE text.
+**
+** ^For UTF16 input text to the sqlite3_result_text16(),
+** sqlite3_result_text16be(), sqlite3_result_text16le(), and
+** sqlite3_result_text64() routines, if the text contains invalid
+** UTF16 characters, the invalid characters might be converted
+** into the unicode replacement character, U+FFFD.
+**
** ^The sqlite3_result_value() interface sets the result of
** the application-defined function to be a copy of the
** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
@@ -6360,7 +6490,7 @@ typedef void (*sqlite3_destructor_type)(void*);
**
** ^The sqlite3_result_pointer(C,P,T,D) interface sets the result to an
** SQL NULL value, just like [sqlite3_result_null(C)], except that it
-** also associates the host-language pointer P or type T with that
+** also associates the host-language pointer P or type T with that
** NULL value such that the pointer can be retrieved within an
** [application-defined SQL function] using [sqlite3_value_pointer()].
** ^If the D parameter is not NULL, then it is a pointer to a destructor
@@ -6402,12 +6532,26 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
** METHOD: sqlite3_context
**
** The sqlite3_result_subtype(C,T) function causes the subtype of
-** the result from the [application-defined SQL function] with
-** [sqlite3_context] C to be the value T. Only the lower 8 bits
+** the result from the [application-defined SQL function] with
+** [sqlite3_context] C to be the value T. Only the lower 8 bits
** of the subtype T are preserved in current versions of SQLite;
** higher order bits are discarded.
** The number of subtype bytes preserved by SQLite might increase
** in future releases of SQLite.
+**
+** Every [application-defined SQL function] that invokes this interface
+** should include the [SQLITE_RESULT_SUBTYPE] property in its
+** text encoding argument when the SQL function is
+** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE]
+** property is omitted from the function that invokes sqlite3_result_subtype(),
+** then in some cases the sqlite3_result_subtype() might fail to set
+** the result subtype.
+**
+** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any
+** SQL function that invokes the sqlite3_result_subtype() interface
+** and that does not have the SQLITE_RESULT_SUBTYPE property will raise
+** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1
+** by default.
*/
SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
@@ -6433,7 +6577,7 @@ SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
** <li> [SQLITE_UTF16_ALIGNED].
** </ul>)^
** ^The eTextRep argument determines the encoding of strings passed
-** to the collating function callback, xCallback.
+** to the collating function callback, xCompare.
** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep
** force strings to be UTF16 with native byte order.
** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin
@@ -6442,18 +6586,19 @@ SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
** ^The fourth argument, pArg, is an application data pointer that is passed
** through as the first argument to the collating function callback.
**
-** ^The fifth argument, xCallback, is a pointer to the collating function.
+** ^The fifth argument, xCompare, is a pointer to the collating function.
** ^Multiple collating functions can be registered using the same name but
** with different eTextRep parameters and SQLite will use whichever
** function requires the least amount of data transformation.
-** ^If the xCallback argument is NULL then the collating function is
+** ^If the xCompare argument is NULL then the collating function is
** deleted. ^When all collating functions having the same name are deleted,
** that collation is no longer usable.
**
-** ^The collating function callback is invoked with a copy of the pArg
+** ^The collating function callback is invoked with a copy of the pArg
** application data pointer and with two strings in the encoding specified
-** by the eTextRep argument. The collating function must return an
-** integer that is negative, zero, or positive
+** by the eTextRep argument. The two integer parameters to the collating
+** function callback are the length of the two strings, in bytes. The collating
+** function must return an integer that is negative, zero, or positive
** if the first string is less than, equal to, or greater than the second,
** respectively. A collating function must always return the same answer
** given the same inputs. If two or more collating functions are registered
@@ -6470,7 +6615,7 @@ SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
** </ol>
**
** If a collating function fails any of the above constraints and that
-** collating function is registered and used, then the behavior of SQLite
+** collating function is registered and used, then the behavior of SQLite
** is undefined.
**
** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation()
@@ -6480,36 +6625,36 @@ SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
** calls to the collation creation functions or when the
** [database connection] is closed using [sqlite3_close()].
**
-** ^The xDestroy callback is <u>not</u> called if the
+** ^The xDestroy callback is <u>not</u> called if the
** sqlite3_create_collation_v2() function fails. Applications that invoke
-** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should
+** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should
** check the return code and dispose of the application data pointer
** themselves rather than expecting SQLite to deal with it for them.
-** This is different from every other SQLite interface. The inconsistency
-** is unfortunate but cannot be changed without breaking backwards
+** This is different from every other SQLite interface. The inconsistency
+** is unfortunate but cannot be changed without breaking backwards
** compatibility.
**
** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()].
*/
SQLITE_API int sqlite3_create_collation(
- sqlite3*,
- const char *zName,
- int eTextRep,
+ sqlite3*,
+ const char *zName,
+ int eTextRep,
void *pArg,
int(*xCompare)(void*,int,const void*,int,const void*)
);
SQLITE_API int sqlite3_create_collation_v2(
- sqlite3*,
- const char *zName,
- int eTextRep,
+ sqlite3*,
+ const char *zName,
+ int eTextRep,
void *pArg,
int(*xCompare)(void*,int,const void*,int,const void*),
void(*xDestroy)(void*)
);
SQLITE_API int sqlite3_create_collation16(
- sqlite3*,
+ sqlite3*,
const void *zName,
- int eTextRep,
+ int eTextRep,
void *pArg,
int(*xCompare)(void*,int,const void*,int,const void*)
);
@@ -6542,64 +6687,19 @@ SQLITE_API int sqlite3_create_collation16(
** [sqlite3_create_collation_v2()].
*/
SQLITE_API int sqlite3_collation_needed(
- sqlite3*,
- void*,
+ sqlite3*,
+ void*,
void(*)(void*,sqlite3*,int eTextRep,const char*)
);
SQLITE_API int sqlite3_collation_needed16(
- sqlite3*,
+ sqlite3*,
void*,
void(*)(void*,sqlite3*,int eTextRep,const void*)
);
-#ifdef SQLITE_HAS_CODEC
-/*
-** Specify the key for an encrypted database. This routine should be
-** called right after sqlite3_open().
-**
-** The code to implement this API is not available in the public release
-** of SQLite.
-*/
-SQLITE_API int sqlite3_key(
- sqlite3 *db, /* Database to be rekeyed */
- const void *pKey, int nKey /* The key */
-);
-SQLITE_API int sqlite3_key_v2(
- sqlite3 *db, /* Database to be rekeyed */
- const char *zDbName, /* Name of the database */
- const void *pKey, int nKey /* The key */
-);
-
-/*
-** Change the key on an open database. If the current database is not
-** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the
-** database is decrypted.
-**
-** The code to implement this API is not available in the public release
-** of SQLite.
-*/
-SQLITE_API int sqlite3_rekey(
- sqlite3 *db, /* Database to be rekeyed */
- const void *pKey, int nKey /* The new key */
-);
-SQLITE_API int sqlite3_rekey_v2(
- sqlite3 *db, /* Database to be rekeyed */
- const char *zDbName, /* Name of the database */
- const void *pKey, int nKey /* The new key */
-);
-
-/*
-** Specify the activation key for a SEE database. Unless
-** activated, none of the SEE routines will work.
-*/
-SQLITE_API void sqlite3_activate_see(
- const char *zPassPhrase /* Activation phrase */
-);
-#endif
-
#ifdef SQLITE_ENABLE_CEROD
/*
-** Specify the activation key for a CEROD database. Unless
+** Specify the activation key for a CEROD database. Unless
** activated, none of the CEROD routines will work.
*/
SQLITE_API void sqlite3_activate_cerod(
@@ -6623,6 +6723,13 @@ SQLITE_API void sqlite3_activate_cerod(
** of the default VFS is not implemented correctly, or not implemented at
** all, then the behavior of sqlite3_sleep() may deviate from the description
** in the previous paragraphs.
+**
+** If a negative argument is passed to sqlite3_sleep() the results vary by
+** VFS and operating system. Some system treat a negative argument as an
+** instruction to sleep forever. Others understand it to mean do not sleep
+** at all. ^In SQLite version 3.42.0 and later, a negative
+** argument passed into sqlite3_sleep() is changed to zero before it is relayed
+** down into the xSleep method of the VFS.
*/
SQLITE_API int sqlite3_sleep(int);
@@ -6655,7 +6762,7 @@ SQLITE_API int sqlite3_sleep(int);
** ^The [temp_store_directory pragma] may modify this variable and cause
** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore,
** the [temp_store_directory pragma] always assumes that any string
-** that this variable points to is held in memory obtained from
+** that this variable points to is held in memory obtained from
** [sqlite3_malloc] and the pragma may attempt to free that memory
** using [sqlite3_free].
** Hence, if this variable is modified directly, either it should be
@@ -6712,7 +6819,7 @@ SQLITE_API char *sqlite3_temp_directory;
** ^The [data_store_directory pragma] may modify this variable and cause
** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore,
** the [data_store_directory pragma] always assumes that any string
-** that this variable points to is held in memory obtained from
+** that this variable points to is held in memory obtained from
** [sqlite3_malloc] and the pragma may attempt to free that memory
** using [sqlite3_free].
** Hence, if this variable is modified directly, either it should be
@@ -6794,21 +6901,58 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*);
SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
/*
+** CAPI3REF: Return The Schema Name For A Database Connection
+** METHOD: sqlite3
+**
+** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name
+** for the N-th database on database connection D, or a NULL pointer of N is
+** out of range. An N value of 0 means the main database file. An N of 1 is
+** the "temp" schema. Larger values of N correspond to various ATTACH-ed
+** databases.
+**
+** Space to hold the string that is returned by sqlite3_db_name() is managed
+** by SQLite itself. The string might be deallocated by any operation that
+** changes the schema, including [ATTACH] or [DETACH] or calls to
+** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that
+** occur on a different thread. Applications that need to
+** remember the string long-term should make their own copy. Applications that
+** are accessing the same database connection simultaneously on multiple
+** threads should mutex-protect calls to this API and should make their own
+** private copy of the result prior to releasing the mutex.
+*/
+SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N);
+
+/*
** CAPI3REF: Return The Filename For A Database Connection
** METHOD: sqlite3
**
-** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename
-** associated with database N of connection D. ^The main database file
-** has the name "main". If there is no attached database N on the database
+** ^The sqlite3_db_filename(D,N) interface returns a pointer to the filename
+** associated with database N of connection D.
+** ^If there is no attached database N on the database
** connection D, or if database N is a temporary or in-memory database, then
-** a NULL pointer is returned.
+** this function will return either a NULL pointer or an empty string.
+**
+** ^The string value returned by this routine is owned and managed by
+** the database connection. ^The value will be valid until the database N
+** is [DETACH]-ed or until the database connection closes.
**
** ^The filename returned by this function is the output of the
** xFullPathname method of the [VFS]. ^In other words, the filename
** will be an absolute pathname, even if the filename used
** to open the database originally was a URI or relative pathname.
+**
+** If the filename pointer returned by this routine is not NULL, then it
+** can be used as the filename input parameter to these routines:
+** <ul>
+** <li> [sqlite3_uri_parameter()]
+** <li> [sqlite3_uri_boolean()]
+** <li> [sqlite3_uri_int64()]
+** <li> [sqlite3_filename_database()]
+** <li> [sqlite3_filename_journal()]
+** <li> [sqlite3_filename_wal()]
+** </ul>
*/
-SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
+SQLITE_API sqlite3_filename sqlite3_db_filename(sqlite3 *db, const char *zDbName);
/*
** CAPI3REF: Determine if a database is read-only
@@ -6821,6 +6965,57 @@ SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
/*
+** CAPI3REF: Determine the transaction state of a database
+** METHOD: sqlite3
+**
+** ^The sqlite3_txn_state(D,S) interface returns the current
+** [transaction state] of schema S in database connection D. ^If S is NULL,
+** then the highest transaction state of any schema on database connection D
+** is returned. Transaction states are (in order of lowest to highest):
+** <ol>
+** <li value="0"> SQLITE_TXN_NONE
+** <li value="1"> SQLITE_TXN_READ
+** <li value="2"> SQLITE_TXN_WRITE
+** </ol>
+** ^If the S argument to sqlite3_txn_state(D,S) is not the name of
+** a valid schema, then -1 is returned.
+*/
+SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema);
+
+/*
+** CAPI3REF: Allowed return values from sqlite3_txn_state()
+** KEYWORDS: {transaction state}
+**
+** These constants define the current transaction state of a database file.
+** ^The [sqlite3_txn_state(D,S)] interface returns one of these
+** constants in order to describe the transaction state of schema S
+** in [database connection] D.
+**
+** <dl>
+** [[SQLITE_TXN_NONE]] <dt>SQLITE_TXN_NONE</dt>
+** <dd>The SQLITE_TXN_NONE state means that no transaction is currently
+** pending.</dd>
+**
+** [[SQLITE_TXN_READ]] <dt>SQLITE_TXN_READ</dt>
+** <dd>The SQLITE_TXN_READ state means that the database is currently
+** in a read transaction. Content has been read from the database file
+** but nothing in the database file has changed. The transaction state
+** will advanced to SQLITE_TXN_WRITE if any changes occur and there are
+** no other conflicting concurrent write transactions. The transaction
+** state will revert to SQLITE_TXN_NONE following a [ROLLBACK] or
+** [COMMIT].</dd>
+**
+** [[SQLITE_TXN_WRITE]] <dt>SQLITE_TXN_WRITE</dt>
+** <dd>The SQLITE_TXN_WRITE state means that the database is currently
+** in a write transaction. Content has been written to the database file
+** but has not yet committed. The transaction state will change to
+** to SQLITE_TXN_NONE at the next [ROLLBACK] or [COMMIT].</dd>
+*/
+#define SQLITE_TXN_NONE 0
+#define SQLITE_TXN_READ 1
+#define SQLITE_TXN_WRITE 2
+
+/*
** CAPI3REF: Find the next prepared statement
** METHOD: sqlite3
**
@@ -6887,6 +7082,72 @@ SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
/*
+** CAPI3REF: Autovacuum Compaction Amount Callback
+** METHOD: sqlite3
+**
+** ^The sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback
+** function C that is invoked prior to each autovacuum of the database
+** file. ^The callback is passed a copy of the generic data pointer (P),
+** the schema-name of the attached database that is being autovacuumed,
+** the size of the database file in pages, the number of free pages,
+** and the number of bytes per page, respectively. The callback should
+** return the number of free pages that should be removed by the
+** autovacuum. ^If the callback returns zero, then no autovacuum happens.
+** ^If the value returned is greater than or equal to the number of
+** free pages, then a complete autovacuum happens.
+**
+** <p>^If there are multiple ATTACH-ed database files that are being
+** modified as part of a transaction commit, then the autovacuum pages
+** callback is invoked separately for each file.
+**
+** <p><b>The callback is not reentrant.</b> The callback function should
+** not attempt to invoke any other SQLite interface. If it does, bad
+** things may happen, including segmentation faults and corrupt database
+** files. The callback function should be a simple function that
+** does some arithmetic on its input parameters and returns a result.
+**
+** ^The X parameter to sqlite3_autovacuum_pages(D,C,P,X) is an optional
+** destructor for the P parameter. ^If X is not NULL, then X(P) is
+** invoked whenever the database connection closes or when the callback
+** is overwritten by another invocation of sqlite3_autovacuum_pages().
+**
+** <p>^There is only one autovacuum pages callback per database connection.
+** ^Each call to the sqlite3_autovacuum_pages() interface overrides all
+** previous invocations for that database connection. ^If the callback
+** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer,
+** then the autovacuum steps callback is canceled. The return value
+** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might
+** be some other error code if something goes wrong. The current
+** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other
+** return codes might be added in future releases.
+**
+** <p>If no autovacuum pages callback is specified (the usual case) or
+** a NULL pointer is provided for the callback,
+** then the default behavior is to vacuum all free pages. So, in other
+** words, the default behavior is the same as if the callback function
+** were something like this:
+**
+** <blockquote><pre>
+** &nbsp; unsigned int demonstration_autovac_pages_callback(
+** &nbsp; void *pClientData,
+** &nbsp; const char *zSchema,
+** &nbsp; unsigned int nDbPage,
+** &nbsp; unsigned int nFreePage,
+** &nbsp; unsigned int nBytePerPage
+** &nbsp; ){
+** &nbsp; return nFreePage;
+** &nbsp; }
+** </pre></blockquote>
+*/
+SQLITE_API int sqlite3_autovacuum_pages(
+ sqlite3 *db,
+ unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
+ void*,
+ void(*)(void*)
+);
+
+
+/*
** CAPI3REF: Data Change Notification Callbacks
** METHOD: sqlite3
**
@@ -6910,7 +7171,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** ^In the case of an update, this is the [rowid] after the update takes place.
**
** ^(The update hook is not invoked when internal system tables are
-** modified (i.e. sqlite_master and sqlite_sequence).)^
+** modified (i.e. sqlite_sequence).)^
** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified.
**
** ^In the current implementation, the update hook
@@ -6936,7 +7197,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** and [sqlite3_preupdate_hook()] interfaces.
*/
SQLITE_API void *sqlite3_update_hook(
- sqlite3*,
+ sqlite3*,
void(*)(void *,int ,char const *,char const *,sqlite3_int64),
void*
);
@@ -6949,26 +7210,35 @@ SQLITE_API void *sqlite3_update_hook(
** to the same database. Sharing is enabled if the argument is true
** and disabled if the argument is false.)^
**
+** This interface is omitted if SQLite is compiled with
+** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE]
+** compile-time option is recommended because the
+** [use of shared cache mode is discouraged].
+**
** ^Cache sharing is enabled and disabled for an entire process.
-** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
+** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
** In prior versions of SQLite,
** sharing was enabled or disabled for each thread separately.
**
** ^(The cache sharing mode set by this interface effects all subsequent
** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()].
-** Existing database connections continue use the sharing mode
+** Existing database connections continue to use the sharing mode
** that was in effect at the time they were opened.)^
**
** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled
** successfully. An [error code] is returned otherwise.)^
**
-** ^Shared cache is disabled by default. But this might change in
-** future releases of SQLite. Applications that care about shared
-** cache setting should set it explicitly.
+** ^Shared cache is disabled by default. It is recommended that it stay
+** that way. In other words, do not use this routine. This interface
+** continues to be provided for historical compatibility, but its use is
+** discouraged. Any use of shared cache is discouraged. If shared cache
+** must be used, it is recommended that shared cache only be enabled for
+** individual database connections using the [sqlite3_open_v2()] interface
+** with the [SQLITE_OPEN_SHAREDCACHE] flag.
**
** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0
-** and will always return SQLITE_MISUSE. On those systems,
-** shared cache mode should be enabled per-database connection via
+** and will always return SQLITE_MISUSE. On those systems,
+** shared cache mode should be enabled per-database connection via
** [sqlite3_open_v2()] with [SQLITE_OPEN_SHAREDCACHE].
**
** This interface is threadsafe on processors where writing a
@@ -7011,6 +7281,9 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
/*
** CAPI3REF: Impose A Limit On Heap Size
**
+** These interfaces impose limits on the amount of heap memory that will be
+** by all database connections within a single process.
+**
** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the
** soft limit on the amount of heap memory that may be allocated by SQLite.
** ^SQLite strives to keep heap memory utilization below the soft heap
@@ -7018,23 +7291,44 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
** as heap memory usages approaches the limit.
** ^The soft heap limit is "soft" because even though SQLite strives to stay
** below the limit, it will exceed the limit rather than generate
-** an [SQLITE_NOMEM] error. In other words, the soft heap limit
+** an [SQLITE_NOMEM] error. In other words, the soft heap limit
** is advisory only.
**
-** ^The return value from sqlite3_soft_heap_limit64() is the size of
-** the soft heap limit prior to the call, or negative in the case of an
-** error. ^If the argument N is negative
-** then no change is made to the soft heap limit. Hence, the current
-** size of the soft heap limit can be determined by invoking
-** sqlite3_soft_heap_limit64() with a negative argument.
-**
-** ^If the argument N is zero then the soft heap limit is disabled.
+** ^The sqlite3_hard_heap_limit64(N) interface sets a hard upper bound of
+** N bytes on the amount of memory that will be allocated. ^The
+** sqlite3_hard_heap_limit64(N) interface is similar to
+** sqlite3_soft_heap_limit64(N) except that memory allocations will fail
+** when the hard heap limit is reached.
**
-** ^(The soft heap limit is not enforced in the current implementation
+** ^The return value from both sqlite3_soft_heap_limit64() and
+** sqlite3_hard_heap_limit64() is the size of
+** the heap limit prior to the call, or negative in the case of an
+** error. ^If the argument N is negative
+** then no change is made to the heap limit. Hence, the current
+** size of heap limits can be determined by invoking
+** sqlite3_soft_heap_limit64(-1) or sqlite3_hard_heap_limit(-1).
+**
+** ^Setting the heap limits to zero disables the heap limiter mechanism.
+**
+** ^The soft heap limit may not be greater than the hard heap limit.
+** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N)
+** is invoked with a value of N that is greater than the hard heap limit,
+** the soft heap limit is set to the value of the hard heap limit.
+** ^The soft heap limit is automatically enabled whenever the hard heap
+** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and
+** the soft heap limit is outside the range of 1..N, then the soft heap
+** limit is set to N. ^Invoking sqlite3_soft_heap_limit64(0) when the
+** hard heap limit is enabled makes the soft heap limit equal to the
+** hard heap limit.
+**
+** The memory allocation limits can also be adjusted using
+** [PRAGMA soft_heap_limit] and [PRAGMA hard_heap_limit].
+**
+** ^(The heap limits are not enforced in the current implementation
** if one or more of following conditions are true:
**
** <ul>
-** <li> The soft heap limit is set to zero.
+** <li> The limit value is set to zero.
** <li> Memory accounting is disabled using a combination of the
** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and
** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option.
@@ -7045,21 +7339,11 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
** from the heap.
** </ul>)^
**
-** Beginning with SQLite [version 3.7.3] ([dateof:3.7.3]),
-** the soft heap limit is enforced
-** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT]
-** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT],
-** the soft heap limit is enforced on every memory allocation. Without
-** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced
-** when memory is allocated by the page cache. Testing suggests that because
-** the page cache is the predominate memory user in SQLite, most
-** applications will achieve adequate soft heap limit enforcement without
-** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT].
-**
-** The circumstances under which SQLite will enforce the soft heap limit may
+** The circumstances under which SQLite will enforce the heap limits may
** changes in future releases of SQLite.
*/
SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N);
+SQLITE_API sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 N);
/*
** CAPI3REF: Deprecated Soft Heap Limit Interface
@@ -7083,7 +7367,7 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
** interface returns SQLITE_OK and fills in the non-NULL pointers in
** the final five arguments with appropriate values if the specified
** column exists. ^The sqlite3_table_column_metadata() interface returns
-** SQLITE_ERROR and if the specified column does not exist.
+** SQLITE_ERROR if the specified column does not exist.
** ^If the column-name parameter to sqlite3_table_column_metadata() is a
** NULL pointer, then this routine simply checks for the existence of the
** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it
@@ -7123,7 +7407,7 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
**
** ^If the specified table is actually a view, an [error code] is returned.
**
-** ^If the specified column is "rowid", "oid" or "_rowid_" and the table
+** ^If the specified column is "rowid", "oid" or "_rowid_" and the table
** is not a [WITHOUT ROWID] table and an
** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output
** parameters are set for the explicitly declared column. ^(If there is no
@@ -7189,7 +7473,7 @@ SQLITE_API int sqlite3_table_column_metadata(
** prior to calling this API,
** otherwise an error will be returned.
**
-** <b>Security warning:</b> It is recommended that the
+** <b>Security warning:</b> It is recommended that the
** [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method be used to enable only this
** interface. The use of the [sqlite3_enable_load_extension()] interface
** should be avoided. This will keep the SQL function [load_extension()]
@@ -7225,7 +7509,7 @@ SQLITE_API int sqlite3_load_extension(
** to enable or disable only the C-API.)^
**
** <b>Security warning:</b> It is recommended that extension loading
-** be disabled using the [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method
+** be enabled using the [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method
** rather than this interface, so the [load_extension()] SQL function
** remains disabled. This will prevent SQL injections from giving attackers
** access to extension loading capabilities.
@@ -7276,7 +7560,7 @@ SQLITE_API int sqlite3_auto_extension(void(*xEntryPoint)(void));
** ^The [sqlite3_cancel_auto_extension(X)] interface unregisters the
** initialization routine X that was registered using a prior call to
** [sqlite3_auto_extension(X)]. ^The [sqlite3_cancel_auto_extension(X)]
-** routine returns 1 if initialization routine X was successfully
+** routine returns 1 if initialization routine X was successfully
** unregistered and it returns 0 if X was not on the list of initialization
** routines.
*/
@@ -7291,15 +7575,6 @@ SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
SQLITE_API void sqlite3_reset_auto_extension(void);
/*
-** The interface to the virtual-table mechanism is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stabilizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-*/
-
-/*
** Structures used by the virtual table interface
*/
typedef struct sqlite3_vtab sqlite3_vtab;
@@ -7311,8 +7586,8 @@ typedef struct sqlite3_module sqlite3_module;
** CAPI3REF: Virtual Table Object
** KEYWORDS: sqlite3_module {virtual table module}
**
-** This structure, sometimes called a "virtual table module",
-** defines the implementation of a [virtual tables].
+** This structure, sometimes called a "virtual table module",
+** defines the implementation of a [virtual table].
** This structure consists mostly of methods for the module.
**
** ^A virtual table module is created by filling in a persistent
@@ -7351,7 +7626,7 @@ struct sqlite3_module {
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
void **ppArg);
int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
- /* The methods above are in version 1 of the sqlite_module object. Those
+ /* The methods above are in version 1 of the sqlite_module object. Those
** below are for version 2 and greater. */
int (*xSavepoint)(sqlite3_vtab *pVTab, int);
int (*xRelease)(sqlite3_vtab *pVTab, int);
@@ -7359,6 +7634,10 @@ struct sqlite3_module {
/* The methods above are in versions 1 and 2 of the sqlite_module object.
** Those below are for version 3 and greater. */
int (*xShadowName)(const char*);
+ /* The methods above are in versions 1 through 3 of the sqlite_module object.
+ ** Those below are for version 4 and greater. */
+ int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
+ const char *zTabName, int mFlags, char **pzErr);
};
/*
@@ -7401,7 +7680,7 @@ struct sqlite3_module {
** required by SQLite. If the table has at least 64 columns and any column
** to the right of the first 63 is required, then bit 63 of colUsed is also
** set. In other words, column iCol may be required if the expression
-** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to
+** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to
** non-zero.
**
** The [xBestIndex] method must fill aConstraintUsage[] with information
@@ -7409,12 +7688,18 @@ struct sqlite3_module {
** the right-hand side of the corresponding aConstraint[] is evaluated
** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit
** is true, then the constraint is assumed to be fully handled by the
-** virtual table and is not checked again by SQLite.)^
-**
-** ^The idxNum and idxPtr values are recorded and passed into the
+** virtual table and might not be checked again by the byte code.)^ ^(The
+** aConstraintUsage[].omit flag is an optimization hint. When the omit flag
+** is left in its default setting of false, the constraint will always be
+** checked separately in byte code. If the omit flag is change to true, then
+** the constraint may or may not be checked in byte code. In other words,
+** when the omit flag is true there is no guarantee that the constraint will
+** not be checked again using byte code.)^
+**
+** ^The idxNum and idxStr values are recorded and passed into the
** [xFilter] method.
-** ^[sqlite3_free()] is used to free idxPtr if and only if
-** needToFreeIdxPtr is true.
+** ^[sqlite3_free()] is used to free idxStr if and only if
+** needToFreeIdxStr is true.
**
** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in
** the correct order to satisfy the ORDER BY clause so that no separate
@@ -7422,17 +7707,17 @@ struct sqlite3_module {
**
** ^The estimatedCost value is an estimate of the cost of a particular
** strategy. A cost of N indicates that the cost of the strategy is similar
-** to a linear scan of an SQLite table with N rows. A cost of log(N)
+** to a linear scan of an SQLite table with N rows. A cost of log(N)
** indicates that the expense of the operation is similar to that of a
** binary search on a unique indexed field of an SQLite table with N rows.
**
** ^The estimatedRows value is an estimate of the number of rows that
** will be returned by the strategy.
**
-** The xBestIndex method may optionally populate the idxFlags field with a
+** The xBestIndex method may optionally populate the idxFlags field with a
** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag -
** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
-** assumes that the strategy may visit at most one row.
+** assumes that the strategy may visit at most one row.
**
** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then
** SQLite also assumes that if a call to the xUpdate() method is made as
@@ -7445,14 +7730,14 @@ struct sqlite3_module {
** the xUpdate method are automatically rolled back by SQLite.
**
** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
-** structure for SQLite [version 3.8.2] ([dateof:3.8.2]).
+** structure for SQLite [version 3.8.2] ([dateof:3.8.2]).
** If a virtual table extension is
-** used with an SQLite version earlier than 3.8.2, the results of attempting
-** to read or write the estimatedRows field are undefined (but are likely
-** to included crashing the application). The estimatedRows field should
+** used with an SQLite version earlier than 3.8.2, the results of attempting
+** to read or write the estimatedRows field are undefined (but are likely
+** to include crashing the application). The estimatedRows field should
** therefore only be used if [sqlite3_libversion_number()] returns a
** value greater than or equal to 3008002. Similarly, the idxFlags field
-** was added for [version 3.9.0] ([dateof:3.9.0]).
+** was added for [version 3.9.0] ([dateof:3.9.0]).
** It may therefore only be used if
** sqlite3_libversion_number() returns a value greater than or equal to
** 3009000.
@@ -7492,7 +7777,7 @@ struct sqlite3_index_info {
/*
** CAPI3REF: Virtual Table Scan Flags
**
-** Virtual table implementations are allowed to set the
+** Virtual table implementations are allowed to set the
** [sqlite3_index_info].idxFlags field to some combination of
** these bits.
*/
@@ -7501,26 +7786,58 @@ struct sqlite3_index_info {
/*
** CAPI3REF: Virtual Table Constraint Operator Codes
**
-** These macros defined the allowed values for the
+** These macros define the allowed values for the
** [sqlite3_index_info].aConstraint[].op field. Each value represents
-** an operator that is part of a constraint term in the wHERE clause of
+** an operator that is part of a constraint term in the WHERE clause of
** a query that uses a [virtual table].
-*/
-#define SQLITE_INDEX_CONSTRAINT_EQ 2
-#define SQLITE_INDEX_CONSTRAINT_GT 4
-#define SQLITE_INDEX_CONSTRAINT_LE 8
-#define SQLITE_INDEX_CONSTRAINT_LT 16
-#define SQLITE_INDEX_CONSTRAINT_GE 32
-#define SQLITE_INDEX_CONSTRAINT_MATCH 64
-#define SQLITE_INDEX_CONSTRAINT_LIKE 65
-#define SQLITE_INDEX_CONSTRAINT_GLOB 66
-#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
-#define SQLITE_INDEX_CONSTRAINT_NE 68
-#define SQLITE_INDEX_CONSTRAINT_ISNOT 69
-#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
-#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
-#define SQLITE_INDEX_CONSTRAINT_IS 72
-#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
+**
+** ^The left-hand operand of the operator is given by the corresponding
+** aConstraint[].iColumn field. ^An iColumn of -1 indicates the left-hand
+** operand is the rowid.
+** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET
+** operators have no left-hand operand, and so for those operators the
+** corresponding aConstraint[].iColumn is meaningless and should not be
+** used.
+**
+** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through
+** value 255 are reserved to represent functions that are overloaded
+** by the [xFindFunction|xFindFunction method] of the virtual table
+** implementation.
+**
+** The right-hand operands for each constraint might be accessible using
+** the [sqlite3_vtab_rhs_value()] interface. Usually the right-hand
+** operand is only available if it appears as a single constant literal
+** in the input SQL. If the right-hand operand is another column or an
+** expression (even a constant expression) or a parameter, then the
+** sqlite3_vtab_rhs_value() probably will not be able to extract it.
+** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and
+** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand
+** and hence calls to sqlite3_vtab_rhs_value() for those operators will
+** always return SQLITE_NOTFOUND.
+**
+** The collating sequence to be used for comparison can be found using
+** the [sqlite3_vtab_collation()] interface. For most real-world virtual
+** tables, the collating sequence of constraints does not matter (for example
+** because the constraints are numeric) and so the sqlite3_vtab_collation()
+** interface is not commonly needed.
+*/
+#define SQLITE_INDEX_CONSTRAINT_EQ 2
+#define SQLITE_INDEX_CONSTRAINT_GT 4
+#define SQLITE_INDEX_CONSTRAINT_LE 8
+#define SQLITE_INDEX_CONSTRAINT_LT 16
+#define SQLITE_INDEX_CONSTRAINT_GE 32
+#define SQLITE_INDEX_CONSTRAINT_MATCH 64
+#define SQLITE_INDEX_CONSTRAINT_LIKE 65
+#define SQLITE_INDEX_CONSTRAINT_GLOB 66
+#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
+#define SQLITE_INDEX_CONSTRAINT_NE 68
+#define SQLITE_INDEX_CONSTRAINT_ISNOT 69
+#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
+#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
+#define SQLITE_INDEX_CONSTRAINT_IS 72
+#define SQLITE_INDEX_CONSTRAINT_LIMIT 73
+#define SQLITE_INDEX_CONSTRAINT_OFFSET 74
+#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
/*
** CAPI3REF: Register A Virtual Table Implementation
@@ -7532,7 +7849,7 @@ struct sqlite3_index_info {
** preexisting [virtual table] for the module.
**
** ^The module name is registered on the [database connection] specified
-** by the first parameter. ^The name of the module is given by the
+** by the first parameter. ^The name of the module is given by the
** second parameter. ^The third parameter is a pointer to
** the implementation of the [virtual table module]. ^The fourth
** parameter is an arbitrary client data pointer that is passed through
@@ -7547,6 +7864,12 @@ struct sqlite3_index_info {
** ^The sqlite3_create_module()
** interface is equivalent to sqlite3_create_module_v2() with a NULL
** destructor.
+**
+** ^If the third parameter (the pointer to the sqlite3_module object) is
+** NULL then no new module is created and any existing modules with the
+** same name are dropped.
+**
+** See also: [sqlite3_drop_modules()]
*/
SQLITE_API int sqlite3_create_module(
sqlite3 *db, /* SQLite connection to register module with */
@@ -7563,6 +7886,23 @@ SQLITE_API int sqlite3_create_module_v2(
);
/*
+** CAPI3REF: Remove Unnecessary Virtual Table Implementations
+** METHOD: sqlite3
+**
+** ^The sqlite3_drop_modules(D,L) interface removes all virtual
+** table modules from database connection D except those named on list L.
+** The L parameter must be either NULL or a pointer to an array of pointers
+** to strings where the array is terminated by a single NULL pointer.
+** ^If the L parameter is NULL, then all virtual table modules are removed.
+**
+** See also: [sqlite3_create_module()]
+*/
+SQLITE_API int sqlite3_drop_modules(
+ sqlite3 *db, /* Remove modules from this connection */
+ const char **azKeep /* Except, do not remove the ones named here */
+);
+
+/*
** CAPI3REF: Virtual Table Instance Object
** KEYWORDS: sqlite3_vtab
**
@@ -7624,7 +7964,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
** METHOD: sqlite3
**
** ^(Virtual tables can provide alternative implementations of functions
-** using the [xFindFunction] method of the [virtual table module].
+** using the [xFindFunction] method of the [virtual table module].
** But global versions of those functions
** must exist in order to be overloaded.)^
**
@@ -7639,16 +7979,6 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
/*
-** The interface to the virtual-table mechanism defined above (back up
-** to a comment remarkably similar to this one) is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stabilizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-*/
-
-/*
** CAPI3REF: A Handle To An Open BLOB
** KEYWORDS: {BLOB handle} {BLOB handles}
**
@@ -7675,7 +8005,7 @@ typedef struct sqlite3_blob sqlite3_blob;
** SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
** </pre>)^
**
-** ^(Parameter zDb is not the filename that contains the database, but
+** ^(Parameter zDb is not the filename that contains the database, but
** rather the symbolic name of the database. For attached databases, this is
** the name that appears after the AS keyword in the [ATTACH] statement.
** For the main database file, the database name is "main". For TEMP
@@ -7688,28 +8018,28 @@ typedef struct sqlite3_blob sqlite3_blob;
** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored
** in *ppBlob. Otherwise an [error code] is returned and, unless the error
** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
-** the API is not misused, it is always safe to call [sqlite3_blob_close()]
+** the API is not misused, it is always safe to call [sqlite3_blob_close()]
** on *ppBlob after this function it returns.
**
** This function fails with SQLITE_ERROR if any of the following are true:
** <ul>
-** <li> ^(Database zDb does not exist)^,
-** <li> ^(Table zTable does not exist within database zDb)^,
-** <li> ^(Table zTable is a WITHOUT ROWID table)^,
+** <li> ^(Database zDb does not exist)^,
+** <li> ^(Table zTable does not exist within database zDb)^,
+** <li> ^(Table zTable is a WITHOUT ROWID table)^,
** <li> ^(Column zColumn does not exist)^,
** <li> ^(Row iRow is not present in the table)^,
** <li> ^(The specified column of row iRow contains a value that is not
** a TEXT or BLOB value)^,
-** <li> ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE
+** <li> ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE
** constraint and the blob is being opened for read/write access)^,
-** <li> ^([foreign key constraints | Foreign key constraints] are enabled,
+** <li> ^([foreign key constraints | Foreign key constraints] are enabled,
** column zColumn is part of a [child key] definition and the blob is
** being opened for read/write access)^.
** </ul>
**
-** ^Unless it returns SQLITE_MISUSE, this function sets the
-** [database connection] error code and message accessible via
-** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
+** ^Unless it returns SQLITE_MISUSE, this function sets the
+** [database connection] error code and message accessible via
+** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
**
** A BLOB referenced by sqlite3_blob_open() may be read using the
** [sqlite3_blob_read()] interface and modified by using
@@ -7735,7 +8065,7 @@ typedef struct sqlite3_blob sqlite3_blob;
** blob.
**
** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces
-** and the built-in [zeroblob] SQL function may be used to create a
+** and the built-in [zeroblob] SQL function may be used to create a
** zero-filled blob to read or write using the incremental-blob interface.
**
** To avoid a resource leak, every open [BLOB handle] should eventually
@@ -7785,7 +8115,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
** DESTRUCTOR: sqlite3_blob
**
** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed
-** unconditionally. Even if this routine returns an error code, the
+** unconditionally. Even if this routine returns an error code, the
** handle is still closed.)^
**
** ^If the blob handle being closed was opened for read-write access, and if
@@ -7795,10 +8125,10 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
** code is returned and the transaction rolled back.
**
** Calling this function with an argument that is not a NULL pointer or an
-** open blob handle results in undefined behaviour. ^Calling this routine
-** with a null pointer (such as would be returned by a failed call to
+** open blob handle results in undefined behavior. ^Calling this routine
+** with a null pointer (such as would be returned by a failed call to
** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
-** is passed a valid open blob handle, the values returned by the
+** is passed a valid open blob handle, the values returned by the
** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning.
*/
SQLITE_API int sqlite3_blob_close(sqlite3_blob *);
@@ -7807,7 +8137,7 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *);
** CAPI3REF: Return The Size Of An Open BLOB
** METHOD: sqlite3_blob
**
-** ^Returns the size in bytes of the BLOB accessible via the
+** ^Returns the size in bytes of the BLOB accessible via the
** successfully opened [BLOB handle] in its only argument. ^The
** incremental blob I/O routines can only read or overwriting existing
** blob content; they cannot change the size of a blob.
@@ -7858,9 +8188,9 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
**
** ^(On success, sqlite3_blob_write() returns SQLITE_OK.
** Otherwise, an [error code] or an [extended error code] is returned.)^
-** ^Unless SQLITE_MISUSE is returned, this function sets the
-** [database connection] error code and message accessible via
-** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
+** ^Unless SQLITE_MISUSE is returned, this function sets the
+** [database connection] error code and message accessible via
+** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
**
** ^If the [BLOB handle] passed as the first argument was not opened for
** writing (the flags parameter to [sqlite3_blob_open()] was zero),
@@ -7869,9 +8199,9 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
** This function may only modify the contents of the BLOB; it is
** not possible to increase the size of a BLOB using this API.
** ^If offset iOffset is less than N bytes from the end of the BLOB,
-** [SQLITE_ERROR] is returned and no data is written. The size of the
-** BLOB (and hence the maximum value of N+iOffset) can be determined
-** using the [sqlite3_blob_bytes()] interface. ^If N or iOffset are less
+** [SQLITE_ERROR] is returned and no data is written. The size of the
+** BLOB (and hence the maximum value of N+iOffset) can be determined
+** using the [sqlite3_blob_bytes()] interface. ^If N or iOffset are less
** than zero [SQLITE_ERROR] is returned and no data is written.
**
** ^An attempt to write to an expired [BLOB handle] fails with an
@@ -7965,7 +8295,7 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
** <ul>
** <li> SQLITE_MUTEX_FAST
** <li> SQLITE_MUTEX_RECURSIVE
-** <li> SQLITE_MUTEX_STATIC_MASTER
+** <li> SQLITE_MUTEX_STATIC_MAIN
** <li> SQLITE_MUTEX_STATIC_MEM
** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
@@ -8022,18 +8352,20 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
-** will always return SQLITE_BUSY. The SQLite core only ever uses
-** sqlite3_mutex_try() as an optimization so this is acceptable
-** behavior.)^
+** will always return SQLITE_BUSY. In most cases the SQLite core only uses
+** sqlite3_mutex_try() as an optimization, so this is acceptable
+** behavior. The exceptions are unix builds that set the
+** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working
+** sqlite3_mutex_try() is required.)^
**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread. The behavior
** is undefined if the mutex is not currently entered by the
** calling thread or is not currently allocated.
**
-** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or
-** sqlite3_mutex_leave() is a NULL pointer, then all three routines
-** behave as no-ops.
+** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(),
+** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer,
+** then any of the four routines behaves as a no-op.
**
** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()].
*/
@@ -8088,7 +8420,7 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*);
** The only difference is that the public sqlite3_XXX functions enumerated
** above silently ignore any invocations that pass a NULL pointer instead
** of a valid mutex handle. The implementations of the methods defined
-** by this structure are not required to handle this case, the results
+** by this structure are not required to handle this case. The results
** of passing a NULL pointer instead of a valid mutex handle are undefined
** (i.e. it is acceptable to provide an implementation that segfaults if
** it is passed a NULL pointer).
@@ -8167,7 +8499,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*);
*/
#define SQLITE_MUTEX_FAST 0
#define SQLITE_MUTEX_RECURSIVE 1
-#define SQLITE_MUTEX_STATIC_MASTER 2
+#define SQLITE_MUTEX_STATIC_MAIN 2
#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */
#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */
#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */
@@ -8182,11 +8514,15 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */
#define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */
+/* Legacy compatibility: */
+#define SQLITE_MUTEX_STATIC_MASTER 2
+
+
/*
** CAPI3REF: Retrieve the mutex for a database connection
** METHOD: sqlite3
**
-** ^This interface returns a pointer the [sqlite3_mutex] object that
+** ^This interface returns a pointer the [sqlite3_mutex] object that
** serializes access to the [database connection] given in the argument
** when the [threading mode] is Serialized.
** ^If the [threading mode] is Single-thread or Multi-thread then this
@@ -8213,7 +8549,7 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*);
** method becomes the return value of this routine.
**
** A few opcodes for [sqlite3_file_control()] are handled directly
-** by the SQLite core and never invoke the
+** by the SQLite core and never invoke the
** sqlite3_io_methods.xFileControl method.
** ^The [SQLITE_FCNTL_FILE_POINTER] value for the op parameter causes
** a pointer to the underlying [sqlite3_file] object to be written into
@@ -8270,14 +8606,16 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_FIRST 5
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
-#define SQLITE_TESTCTRL_PRNG_RESET 7
+#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
+#define SQLITE_TESTCTRL_FK_NO_ACTION 7
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
#define SQLITE_TESTCTRL_PENDING_BYTE 11
#define SQLITE_TESTCTRL_ASSERT 12
#define SQLITE_TESTCTRL_ALWAYS 13
-#define SQLITE_TESTCTRL_RESERVE 14
+#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
+#define SQLITE_TESTCTRL_JSON_SELFCHECK 14
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
@@ -8292,12 +8630,20 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_SORTER_MMAP 24
#define SQLITE_TESTCTRL_IMPOSTER 25
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
-#define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */
+#define SQLITE_TESTCTRL_RESULT_INTREAL 27
+#define SQLITE_TESTCTRL_PRNG_SEED 28
+#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
+#define SQLITE_TESTCTRL_SEEK_COUNT 30
+#define SQLITE_TESTCTRL_TRACEFLAGS 31
+#define SQLITE_TESTCTRL_TUNE 32
+#define SQLITE_TESTCTRL_LOGEST 33
+#define SQLITE_TESTCTRL_USELONGDOUBLE 34
+#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */
/*
** CAPI3REF: SQL Keyword Checking
**
-** These routines provide access to the set of SQL language keywords
+** These routines provide access to the set of SQL language keywords
** recognized by SQLite. Applications can uses these routines to determine
** whether or not a specific identifier needs to be escaped (for example,
** by enclosing in double-quotes) so as not to confuse the parser.
@@ -8369,14 +8715,14 @@ typedef struct sqlite3_str sqlite3_str;
**
** ^The [sqlite3_str_new(D)] interface allocates and initializes
** a new [sqlite3_str] object. To avoid memory leaks, the object returned by
-** [sqlite3_str_new()] must be freed by a subsequent call to
+** [sqlite3_str_new()] must be freed by a subsequent call to
** [sqlite3_str_finish(X)].
**
** ^The [sqlite3_str_new(D)] interface always returns a pointer to a
** valid [sqlite3_str] object, though in the event of an out-of-memory
** error the returned object might be a special singleton that will
-** silently reject new text, always return SQLITE_NOMEM from
-** [sqlite3_str_errcode()], always return 0 for
+** silently reject new text, always return SQLITE_NOMEM from
+** [sqlite3_str_errcode()], always return 0 for
** [sqlite3_str_length()], and always return NULL from
** [sqlite3_str_finish(X)]. It is always safe to use the value
** returned by [sqlite3_str_new(D)] as the sqlite3_str parameter
@@ -8412,9 +8758,9 @@ SQLITE_API char *sqlite3_str_finish(sqlite3_str*);
** These interfaces add content to an sqlite3_str object previously obtained
** from [sqlite3_str_new()].
**
-** ^The [sqlite3_str_appendf(X,F,...)] and
+** ^The [sqlite3_str_appendf(X,F,...)] and
** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf]
-** functionality of SQLite to append formatted text onto the end of
+** functionality of SQLite to append formatted text onto the end of
** [sqlite3_str] object X.
**
** ^The [sqlite3_str_append(X,S,N)] method appends exactly N bytes from string S
@@ -8431,7 +8777,7 @@ SQLITE_API char *sqlite3_str_finish(sqlite3_str*);
** ^This method can be used, for example, to add whitespace indentation.
**
** ^The [sqlite3_str_reset(X)] method resets the string under construction
-** inside [sqlite3_str] object X back to zero bytes in length.
+** inside [sqlite3_str] object X back to zero bytes in length.
**
** These methods do not return a result code. ^If an error occurs, that fact
** is recorded in the [sqlite3_str] object and can be recovered by a
@@ -8533,7 +8879,7 @@ SQLITE_API int sqlite3_status64(
** <dd>This parameter records the largest memory allocation request
** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their
** internal equivalents). Only the value returned in the
-** *pHighwater parameter to [sqlite3_status()] is of interest.
+** *pHighwater parameter to [sqlite3_status()] is of interest.
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
** [[SQLITE_STATUS_MALLOC_COUNT]] ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt>
@@ -8542,11 +8888,11 @@ SQLITE_API int sqlite3_status64(
**
** [[SQLITE_STATUS_PAGECACHE_USED]] ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt>
** <dd>This parameter returns the number of pages used out of the
-** [pagecache memory allocator] that was configured using
+** [pagecache memory allocator] that was configured using
** [SQLITE_CONFIG_PAGECACHE]. The
** value returned is in pages, not in bytes.</dd>)^
**
-** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]]
+** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]]
** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt>
** <dd>This parameter returns the number of bytes of page cache
** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE]
@@ -8558,8 +8904,8 @@ SQLITE_API int sqlite3_status64(
**
** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt>
** <dd>This parameter records the largest memory allocation request
-** handed to [pagecache memory allocator]. Only the value returned in the
-** *pHighwater parameter to [sqlite3_status()] is of interest.
+** handed to the [pagecache memory allocator]. Only the value returned in the
+** *pHighwater parameter to [sqlite3_status()] is of interest.
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
** [[SQLITE_STATUS_SCRATCH_USED]] <dt>SQLITE_STATUS_SCRATCH_USED</dt>
@@ -8572,7 +8918,7 @@ SQLITE_API int sqlite3_status64(
** <dd>No longer used.</dd>
**
** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
-** <dd>The *pHighwater parameter records the deepest parser stack.
+** <dd>The *pHighwater parameter records the deepest parser stack.
** The *pCurrent value is undefined. The *pHighwater value is only
** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
** </dl>
@@ -8594,12 +8940,12 @@ SQLITE_API int sqlite3_status64(
** CAPI3REF: Database Connection Status
** METHOD: sqlite3
**
-** ^This interface is used to retrieve runtime status information
+** ^This interface is used to retrieve runtime status information
** about a single [database connection]. ^The first argument is the
** database connection object to be interrogated. ^The second argument
** is an integer constant, taken from the set of
** [SQLITE_DBSTATUS options], that
-** determines the parameter to interrogate. The set of
+** determines the parameter to interrogate. The set of
** [SQLITE_DBSTATUS options] is likely
** to grow in future releases of SQLite.
**
@@ -8634,7 +8980,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** checked out.</dd>)^
**
** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>
-** <dd>This parameter returns the number malloc attempts that were
+** <dd>This parameter returns the number of malloc attempts that were
** satisfied using lookaside memory. Only the high-water value is meaningful;
** the current value is always zero.)^
**
@@ -8659,7 +9005,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** memory used by all pager caches associated with the database connection.)^
** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
**
-** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]]
+** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]]
** ^(<dt>SQLITE_DBSTATUS_CACHE_USED_SHARED</dt>
** <dd>This parameter is similar to DBSTATUS_CACHE_USED, except that if a
** pager cache is shared between two or more connections the bytes of heap
@@ -8674,7 +9020,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
** <dd>This parameter returns the approximate number of bytes of heap
** memory used to store the schema for all databases associated
-** with the connection - main, temp, and any [ATTACH]-ed databases.)^
+** with the connection - main, temp, and any [ATTACH]-ed databases.)^
** ^The full amount of memory used by the schemas is reported, even if the
** schema memory is shared with other database connections due to
** [shared cache mode] being enabled.
@@ -8689,13 +9035,13 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
**
** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(<dt>SQLITE_DBSTATUS_CACHE_HIT</dt>
** <dd>This parameter returns the number of pager cache hits that have
-** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT
+** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT
** is always 0.
** </dd>
**
** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(<dt>SQLITE_DBSTATUS_CACHE_MISS</dt>
** <dd>This parameter returns the number of pager cache misses that have
-** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS
+** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS
** is always 0.
** </dd>
**
@@ -8716,7 +9062,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** cache overflowing. Transactions are more efficient if they are written
** to disk all at once. When pages spill mid-transaction, that introduces
** additional overhead. This parameter can be used help identify
-** inefficiencies that can be resolve by increasing the cache size.
+** inefficiencies that can be resolved by increasing the cache size.
** </dd>
**
** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
@@ -8753,7 +9099,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** statements. For example, if the number of table steps greatly exceeds
** the number of table searches or result rows, that would tend to indicate
** that the prepared statement is using a full table scan rather than
-** an index.
+** an index.
**
** ^(This interface is used to retrieve and reset counter values from
** a [prepared statement]. The first argument is the prepared statement
@@ -8780,7 +9126,7 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]] <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt>
** <dd>^This is the number of times that SQLite has stepped forward in
** a table as part of a full table scan. Large numbers for this counter
-** may indicate opportunities for performance improvement through
+** may indicate opportunities for performance improvement through
** careful use of indices.</dd>
**
** [[SQLITE_STMTSTATUS_SORT]] <dt>SQLITE_STMTSTATUS_SORT</dt>
@@ -8798,14 +9144,14 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** [[SQLITE_STMTSTATUS_VM_STEP]] <dt>SQLITE_STMTSTATUS_VM_STEP</dt>
** <dd>^This is the number of virtual machine operations executed
** by the prepared statement if that number is less than or equal
-** to 2147483647. The number of virtual machine operations can be
+** to 2147483647. The number of virtual machine operations can be
** used as a proxy for the total work done by the prepared statement.
** If the number of virtual machine operations exceeds 2147483647
** then the value returned by this statement status code is undefined.
**
** [[SQLITE_STMTSTATUS_REPREPARE]] <dt>SQLITE_STMTSTATUS_REPREPARE</dt>
** <dd>^This is the number of times that the prepare statement has been
-** automatically regenerated due to schema changes or change to
+** automatically regenerated due to schema changes or changes to
** [bound parameters] that might affect the query plan.
**
** [[SQLITE_STMTSTATUS_RUN]] <dt>SQLITE_STMTSTATUS_RUN</dt>
@@ -8815,6 +9161,16 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** The counter is incremented on the first [sqlite3_step()] call of each
** cycle.
**
+** [[SQLITE_STMTSTATUS_FILTER_MISS]]
+** [[SQLITE_STMTSTATUS_FILTER HIT]]
+** <dt>SQLITE_STMTSTATUS_FILTER_HIT<br>
+** SQLITE_STMTSTATUS_FILTER_MISS</dt>
+** <dd>^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join
+** step was bypassed because a Bloom filter returned not-found. The
+** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of
+** times that the Bloom filter returned a find, and thus the join step
+** had to be processed as normal.
+**
** [[SQLITE_STMTSTATUS_MEMUSED]] <dt>SQLITE_STMTSTATUS_MEMUSED</dt>
** <dd>^This is the approximate number of bytes of heap memory
** used to store the prepared statement. ^This value is not actually
@@ -8829,6 +9185,8 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
#define SQLITE_STMTSTATUS_VM_STEP 4
#define SQLITE_STMTSTATUS_REPREPARE 5
#define SQLITE_STMTSTATUS_RUN 6
+#define SQLITE_STMTSTATUS_FILTER_MISS 7
+#define SQLITE_STMTSTATUS_FILTER_HIT 8
#define SQLITE_STMTSTATUS_MEMUSED 99
/*
@@ -8865,15 +9223,15 @@ struct sqlite3_pcache_page {
** KEYWORDS: {page cache}
**
** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can
-** register an alternative page cache implementation by passing in an
+** register an alternative page cache implementation by passing in an
** instance of the sqlite3_pcache_methods2 structure.)^
-** In many applications, most of the heap memory allocated by
+** In many applications, most of the heap memory allocated by
** SQLite is used for the page cache.
-** By implementing a
+** By implementing a
** custom page cache using this API, an application can better control
-** the amount of memory consumed by SQLite, the way in which
-** that memory is allocated and released, and the policies used to
-** determine exactly which parts of a database file are cached and for
+** the amount of memory consumed by SQLite, the way in which
+** that memory is allocated and released, and the policies used to
+** determine exactly which parts of a database file are cached and for
** how long.
**
** The alternative page cache mechanism is an
@@ -8886,19 +9244,19 @@ struct sqlite3_pcache_page {
** [sqlite3_config()] returns.)^
**
** [[the xInit() page cache method]]
-** ^(The xInit() method is called once for each effective
+** ^(The xInit() method is called once for each effective
** call to [sqlite3_initialize()])^
** (usually only once during the lifetime of the process). ^(The xInit()
** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^
-** The intent of the xInit() method is to set up global data structures
-** required by the custom page cache implementation.
-** ^(If the xInit() method is NULL, then the
+** The intent of the xInit() method is to set up global data structures
+** required by the custom page cache implementation.
+** ^(If the xInit() method is NULL, then the
** built-in default page cache is used instead of the application defined
** page cache.)^
**
** [[the xShutdown() page cache method]]
** ^The xShutdown() method is called by [sqlite3_shutdown()].
-** It can be used to clean up
+** It can be used to clean up
** any outstanding resources before process shutdown, if required.
** ^The xShutdown() method may be NULL.
**
@@ -8917,7 +9275,7 @@ struct sqlite3_pcache_page {
** though this is not guaranteed. ^The
** first parameter, szPage, is the size in bytes of the pages that must
** be allocated by the cache. ^szPage will always a power of two. ^The
-** second parameter szExtra is a number of bytes of extra storage
+** second parameter szExtra is a number of bytes of extra storage
** associated with each page cache entry. ^The szExtra parameter will
** a number less than 250. SQLite will use the
** extra szExtra bytes on each page to store metadata about the underlying
@@ -8930,7 +9288,7 @@ struct sqlite3_pcache_page {
** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will
** never invoke xUnpin() except to deliberately delete a page.
** ^In other words, calls to xUnpin() on a cache with bPurgeable set to
-** false will always have the "discard" flag set to true.
+** false will always have the "discard" flag set to true.
** ^Hence, a cache created with bPurgeable false will
** never contain any unpinned pages.
**
@@ -8945,12 +9303,12 @@ struct sqlite3_pcache_page {
** [[the xPagecount() page cache methods]]
** The xPagecount() method must return the number of pages currently
** stored in the cache, both pinned and unpinned.
-**
+**
** [[the xFetch() page cache methods]]
-** The xFetch() method locates a page in the cache and returns a pointer to
+** The xFetch() method locates a page in the cache and returns a pointer to
** an sqlite3_pcache_page object associated with that page, or a NULL pointer.
** The pBuf element of the returned sqlite3_pcache_page object will be a
-** pointer to a buffer of szPage bytes used to store the content of a
+** pointer to a buffer of szPage bytes used to store the content of a
** single database page. The pExtra element of sqlite3_pcache_page will be
** a pointer to the szExtra bytes of extra storage that SQLite has requested
** for each entry in the page cache.
@@ -8976,7 +9334,7 @@ struct sqlite3_pcache_page {
**
** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite
** will only use a createFlag of 2 after a prior call with a createFlag of 1
-** failed.)^ In between the to xFetch() calls, SQLite may
+** failed.)^ In between the xFetch() calls, SQLite may
** attempt to unpin one or more cache pages by spilling the content of
** pinned pages to disk and synching the operating system disk cache.
**
@@ -8989,8 +9347,8 @@ struct sqlite3_pcache_page {
** page cache implementation. ^The page cache implementation
** may choose to evict unpinned pages at any time.
**
-** The cache must not perform any reference counting. A single
-** call to xUnpin() unpins the page regardless of the number of prior calls
+** The cache must not perform any reference counting. A single
+** call to xUnpin() unpins the page regardless of the number of prior calls
** to xFetch().
**
** [[the xRekey() page cache methods]]
@@ -9030,7 +9388,7 @@ struct sqlite3_pcache_methods2 {
int (*xPagecount)(sqlite3_pcache*);
sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);
void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard);
- void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*,
+ void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*,
unsigned oldKey, unsigned newKey);
void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);
void (*xDestroy)(sqlite3_pcache*);
@@ -9075,7 +9433,7 @@ typedef struct sqlite3_backup sqlite3_backup;
**
** The backup API copies the content of one database into another.
** It is useful either for creating backups of databases or
-** for copying in-memory databases to or from persistent files.
+** for copying in-memory databases to or from persistent files.
**
** See Also: [Using the SQLite Online Backup API]
**
@@ -9086,36 +9444,36 @@ typedef struct sqlite3_backup sqlite3_backup;
** ^Thus, the backup may be performed on a live source database without
** preventing other database connections from
** reading or writing to the source database while the backup is underway.
-**
-** ^(To perform a backup operation:
+**
+** ^(To perform a backup operation:
** <ol>
** <li><b>sqlite3_backup_init()</b> is called once to initialize the
-** backup,
-** <li><b>sqlite3_backup_step()</b> is called one or more times to transfer
+** backup,
+** <li><b>sqlite3_backup_step()</b> is called one or more times to transfer
** the data between the two databases, and finally
-** <li><b>sqlite3_backup_finish()</b> is called to release all resources
-** associated with the backup operation.
+** <li><b>sqlite3_backup_finish()</b> is called to release all resources
+** associated with the backup operation.
** </ol>)^
** There should be exactly one call to sqlite3_backup_finish() for each
** successful call to sqlite3_backup_init().
**
** [[sqlite3_backup_init()]] <b>sqlite3_backup_init()</b>
**
-** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the
-** [database connection] associated with the destination database
+** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the
+** [database connection] associated with the destination database
** and the database name, respectively.
** ^The database name is "main" for the main database, "temp" for the
** temporary database, or the name specified after the AS keyword in
** an [ATTACH] statement for an attached database.
-** ^The S and M arguments passed to
+** ^The S and M arguments passed to
** sqlite3_backup_init(D,N,S,M) identify the [database connection]
** and database name of the source database, respectively.
** ^The source and destination [database connections] (parameters S and D)
** must be different or else sqlite3_backup_init(D,N,S,M) will fail with
** an error.
**
-** ^A call to sqlite3_backup_init() will fail, returning NULL, if
-** there is already a read or read-write transaction open on the
+** ^A call to sqlite3_backup_init() will fail, returning NULL, if
+** there is already a read or read-write transaction open on the
** destination database.
**
** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is
@@ -9127,14 +9485,14 @@ typedef struct sqlite3_backup sqlite3_backup;
** ^A successful call to sqlite3_backup_init() returns a pointer to an
** [sqlite3_backup] object.
** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and
-** sqlite3_backup_finish() functions to perform the specified backup
+** sqlite3_backup_finish() functions to perform the specified backup
** operation.
**
** [[sqlite3_backup_step()]] <b>sqlite3_backup_step()</b>
**
-** ^Function sqlite3_backup_step(B,N) will copy up to N pages between
+** ^Function sqlite3_backup_step(B,N) will copy up to N pages between
** the source and destination databases specified by [sqlite3_backup] object B.
-** ^If N is negative, all remaining source pages are copied.
+** ^If N is negative, all remaining source pages are copied.
** ^If sqlite3_backup_step(B,N) successfully copies N pages and there
** are still more pages to be copied, then the function returns [SQLITE_OK].
** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages
@@ -9156,8 +9514,8 @@ typedef struct sqlite3_backup sqlite3_backup;
**
** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then
** the [sqlite3_busy_handler | busy-handler function]
-** is invoked (if one is specified). ^If the
-** busy-handler returns non-zero before the lock is available, then
+** is invoked (if one is specified). ^If the
+** busy-handler returns non-zero before the lock is available, then
** [SQLITE_BUSY] is returned to the caller. ^In this case the call to
** sqlite3_backup_step() can be retried later. ^If the source
** [database connection]
@@ -9165,15 +9523,15 @@ typedef struct sqlite3_backup sqlite3_backup;
** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this
** case the call to sqlite3_backup_step() can be retried later on. ^(If
** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or
-** [SQLITE_READONLY] is returned, then
-** there is no point in retrying the call to sqlite3_backup_step(). These
-** errors are considered fatal.)^ The application must accept
-** that the backup operation has failed and pass the backup operation handle
+** [SQLITE_READONLY] is returned, then
+** there is no point in retrying the call to sqlite3_backup_step(). These
+** errors are considered fatal.)^ The application must accept
+** that the backup operation has failed and pass the backup operation handle
** to the sqlite3_backup_finish() to release associated resources.
**
** ^The first call to sqlite3_backup_step() obtains an exclusive lock
-** on the destination file. ^The exclusive lock is not released until either
-** sqlite3_backup_finish() is called or the backup operation is complete
+** on the destination file. ^The exclusive lock is not released until either
+** sqlite3_backup_finish() is called or the backup operation is complete
** and sqlite3_backup_step() returns [SQLITE_DONE]. ^Every call to
** sqlite3_backup_step() obtains a [shared lock] on the source database that
** lasts for the duration of the sqlite3_backup_step() call.
@@ -9182,18 +9540,18 @@ typedef struct sqlite3_backup sqlite3_backup;
** through the backup process. ^If the source database is modified by an
** external process or via a database connection other than the one being
** used by the backup operation, then the backup will be automatically
-** restarted by the next call to sqlite3_backup_step(). ^If the source
+** restarted by the next call to sqlite3_backup_step(). ^If the source
** database is modified by the using the same database connection as is used
** by the backup operation, then the backup database is automatically
** updated at the same time.
**
** [[sqlite3_backup_finish()]] <b>sqlite3_backup_finish()</b>
**
-** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the
+** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the
** application wishes to abandon the backup operation, the application
** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish().
** ^The sqlite3_backup_finish() interfaces releases all
-** resources associated with the [sqlite3_backup] object.
+** resources associated with the [sqlite3_backup] object.
** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any
** active write-transaction on the destination database is rolled back.
** The [sqlite3_backup] object is invalid
@@ -9233,23 +9591,23 @@ typedef struct sqlite3_backup sqlite3_backup;
** connections, then the source database connection may be used concurrently
** from within other threads.
**
-** However, the application must guarantee that the destination
-** [database connection] is not passed to any other API (by any thread) after
+** However, the application must guarantee that the destination
+** [database connection] is not passed to any other API (by any thread) after
** sqlite3_backup_init() is called and before the corresponding call to
** sqlite3_backup_finish(). SQLite does not currently check to see
** if the application incorrectly accesses the destination [database connection]
** and so no error code is reported, but the operations may malfunction
** nevertheless. Use of the destination database connection while a
-** backup is in progress might also also cause a mutex deadlock.
+** backup is in progress might also cause a mutex deadlock.
**
** If running in [shared cache mode], the application must
** guarantee that the shared cache used by the destination database
** is not accessed while the backup is running. In practice this means
-** that the application must guarantee that the disk file being
+** that the application must guarantee that the disk file being
** backed up to is not accessed by any connection within the process,
** not just the specific connection that was passed to sqlite3_backup_init().
**
-** The [sqlite3_backup] object itself is partially threadsafe. Multiple
+** The [sqlite3_backup] object itself is partially threadsafe. Multiple
** threads may safely make multiple concurrent calls to sqlite3_backup_step().
** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount()
** APIs are not strictly speaking threadsafe. If they are invoked at the
@@ -9274,8 +9632,8 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
** ^When running in shared-cache mode, a database operation may fail with
** an [SQLITE_LOCKED] error if the required locks on the shared-cache or
** individual tables within the shared-cache cannot be obtained. See
-** [SQLite Shared-Cache Mode] for a description of shared-cache locking.
-** ^This API may be used to register a callback that SQLite will invoke
+** [SQLite Shared-Cache Mode] for a description of shared-cache locking.
+** ^This API may be used to register a callback that SQLite will invoke
** when the connection currently holding the required lock relinquishes it.
** ^This API is only available if the library was compiled with the
** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined.
@@ -9283,18 +9641,18 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
** See Also: [Using the SQLite Unlock Notification Feature].
**
** ^Shared-cache locks are released when a database connection concludes
-** its current transaction, either by committing it or rolling it back.
+** its current transaction, either by committing it or rolling it back.
**
** ^When a connection (known as the blocked connection) fails to obtain a
** shared-cache lock and SQLITE_LOCKED is returned to the caller, the
** identity of the database connection (the blocking connection) that
-** has locked the required resource is stored internally. ^After an
+** has locked the required resource is stored internally. ^After an
** application receives an SQLITE_LOCKED error, it may call the
-** sqlite3_unlock_notify() method with the blocked connection handle as
+** sqlite3_unlock_notify() method with the blocked connection handle as
** the first argument to register for a callback that will be invoked
** when the blocking connections current transaction is concluded. ^The
** callback is invoked from within the [sqlite3_step] or [sqlite3_close]
-** call that concludes the blocking connections transaction.
+** call that concludes the blocking connection's transaction.
**
** ^(If sqlite3_unlock_notify() is called in a multi-threaded application,
** there is a chance that the blocking connection will have already
@@ -9304,15 +9662,15 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
**
** ^If the blocked connection is attempting to obtain a write-lock on a
** shared-cache table, and more than one other connection currently holds
-** a read-lock on the same table, then SQLite arbitrarily selects one of
+** a read-lock on the same table, then SQLite arbitrarily selects one of
** the other connections to use as the blocking connection.
**
-** ^(There may be at most one unlock-notify callback registered by a
+** ^(There may be at most one unlock-notify callback registered by a
** blocked connection. If sqlite3_unlock_notify() is called when the
** blocked connection already has a registered unlock-notify callback,
** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
** called with a NULL pointer as its second argument, then any existing
-** unlock-notify callback is canceled. ^The blocked connections
+** unlock-notify callback is canceled. ^The blocked connections
** unlock-notify callback may also be canceled by closing the blocked
** connection using [sqlite3_close()].
**
@@ -9325,25 +9683,25 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
**
** <b>Callback Invocation Details</b>
**
-** When an unlock-notify callback is registered, the application provides a
+** When an unlock-notify callback is registered, the application provides a
** single void* pointer that is passed to the callback when it is invoked.
** However, the signature of the callback function allows SQLite to pass
** it an array of void* context pointers. The first argument passed to
** an unlock-notify callback is a pointer to an array of void* pointers,
** and the second is the number of entries in the array.
**
-** When a blocking connections transaction is concluded, there may be
+** When a blocking connection's transaction is concluded, there may be
** more than one blocked connection that has registered for an unlock-notify
** callback. ^If two or more such blocked connections have specified the
** same callback function, then instead of invoking the callback function
** multiple times, it is invoked once with the set of void* context pointers
** specified by the blocked connections bundled together into an array.
-** This gives the application an opportunity to prioritize any actions
+** This gives the application an opportunity to prioritize any actions
** related to the set of unblocked database connections.
**
** <b>Deadlock Detection</b>
**
-** Assuming that after registering for an unlock-notify callback a
+** Assuming that after registering for an unlock-notify callback a
** database waits for the callback to be issued before taking any further
** action (a reasonable assumption), then using this API may cause the
** application to deadlock. For example, if connection X is waiting for
@@ -9366,7 +9724,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
**
** <b>The "DROP TABLE" Exception</b>
**
-** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost
+** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost
** always appropriate to call sqlite3_unlock_notify(). There is however,
** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement,
** SQLite checks if there are any currently executing SELECT statements
@@ -9379,7 +9737,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
** One way around this problem is to check the extended error code returned
** by an sqlite3_step() call. ^(If there is a blocking connection, then the
** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in
-** the special "DROP TABLE/INDEX" case, the extended error code is just
+** the special "DROP TABLE/INDEX" case, the extended error code is just
** SQLITE_LOCKED.)^
*/
SQLITE_API int sqlite3_unlock_notify(
@@ -9470,8 +9828,8 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
** ^The [sqlite3_wal_hook()] function is used to register a callback that
** is invoked each time data is committed to a database in wal mode.
**
-** ^(The callback is invoked by SQLite after the commit has taken place and
-** the associated write-lock on the database released)^, so the implementation
+** ^(The callback is invoked by SQLite after the commit has taken place and
+** the associated write-lock on the database released)^, so the implementation
** may read, write or [checkpoint] the database as required.
**
** ^The first parameter passed to the callback function when it is invoked
@@ -9490,15 +9848,16 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
** that does not correspond to any valid SQLite error code, the results
** are undefined.
**
-** A single database handle may have at most a single write-ahead log callback
+** A single database handle may have at most a single write-ahead log callback
** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any
-** previously registered write-ahead log callback. ^Note that the
-** [sqlite3_wal_autocheckpoint()] interface and the
+** previously registered write-ahead log callback. ^The return value is
+** a copy of the third parameter from the previous call, if any, or 0.
+** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the
** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
** overwrite any prior [sqlite3_wal_hook()] settings.
*/
SQLITE_API void *sqlite3_wal_hook(
- sqlite3*,
+ sqlite3*,
int(*)(void *,sqlite3*,const char*,int),
void*
);
@@ -9511,7 +9870,7 @@ SQLITE_API void *sqlite3_wal_hook(
** [sqlite3_wal_hook()] that causes any database on [database connection] D
** to automatically [checkpoint]
** after committing a transaction if there are N or
-** more frames in the [write-ahead log] file. ^Passing zero or
+** more frames in the [write-ahead log] file. ^Passing zero or
** a negative value as the nFrame parameter disables automatic
** checkpoints entirely.
**
@@ -9541,7 +9900,7 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to
** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^
**
-** In brief, sqlite3_wal_checkpoint(D,X) causes the content in the
+** In brief, sqlite3_wal_checkpoint(D,X) causes the content in the
** [write-ahead log] for database X on [database connection] D to be
** transferred into the database file and for the write-ahead log to
** be reset. See the [checkpointing] documentation for addition
@@ -9567,10 +9926,10 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
**
** <dl>
** <dt>SQLITE_CHECKPOINT_PASSIVE<dd>
-** ^Checkpoint as many frames as possible without waiting for any database
-** readers or writers to finish, then sync the database file if all frames
+** ^Checkpoint as many frames as possible without waiting for any database
+** readers or writers to finish, then sync the database file if all frames
** in the log were checkpointed. ^The [busy-handler callback]
-** is never invoked in the SQLITE_CHECKPOINT_PASSIVE mode.
+** is never invoked in the SQLITE_CHECKPOINT_PASSIVE mode.
** ^On the other hand, passive mode might leave the checkpoint unfinished
** if there are concurrent readers or writers.
**
@@ -9584,9 +9943,9 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
**
** <dt>SQLITE_CHECKPOINT_RESTART<dd>
** ^This mode works the same way as SQLITE_CHECKPOINT_FULL with the addition
-** that after checkpointing the log file it blocks (calls the
+** that after checkpointing the log file it blocks (calls the
** [busy-handler callback])
-** until all readers are reading from the database file only. ^This ensures
+** until all readers are reading from the database file only. ^This ensures
** that the next writer will restart the log file from the beginning.
** ^Like SQLITE_CHECKPOINT_FULL, this mode blocks new
** database writer attempts while it is pending, but does not impede readers.
@@ -9608,31 +9967,31 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
** truncated to zero bytes and so both *pnLog and *pnCkpt will be set to zero.
**
** ^All calls obtain an exclusive "checkpoint" lock on the database file. ^If
-** any other process is running a checkpoint operation at the same time, the
-** lock cannot be obtained and SQLITE_BUSY is returned. ^Even if there is a
+** any other process is running a checkpoint operation at the same time, the
+** lock cannot be obtained and SQLITE_BUSY is returned. ^Even if there is a
** busy-handler configured, it will not be invoked in this case.
**
-** ^The SQLITE_CHECKPOINT_FULL, RESTART and TRUNCATE modes also obtain the
+** ^The SQLITE_CHECKPOINT_FULL, RESTART and TRUNCATE modes also obtain the
** exclusive "writer" lock on the database file. ^If the writer lock cannot be
** obtained immediately, and a busy-handler is configured, it is invoked and
** the writer lock retried until either the busy-handler returns 0 or the lock
** is successfully obtained. ^The busy-handler is also invoked while waiting for
** database readers as described above. ^If the busy-handler returns 0 before
** the writer lock is obtained or while waiting for database readers, the
-** checkpoint operation proceeds from that point in the same way as
-** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible
+** checkpoint operation proceeds from that point in the same way as
+** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible
** without blocking any further. ^SQLITE_BUSY is returned in this case.
**
** ^If parameter zDb is NULL or points to a zero length string, then the
-** specified operation is attempted on all WAL databases [attached] to
+** specified operation is attempted on all WAL databases [attached] to
** [database connection] db. In this case the
-** values written to output parameters *pnLog and *pnCkpt are undefined. ^If
-** an SQLITE_BUSY error is encountered when processing one or more of the
-** attached WAL databases, the operation is still attempted on any remaining
-** attached databases and SQLITE_BUSY is returned at the end. ^If any other
-** error occurs while processing an attached database, processing is abandoned
-** and the error code is returned to the caller immediately. ^If no error
-** (SQLITE_BUSY or otherwise) is encountered while processing the attached
+** values written to output parameters *pnLog and *pnCkpt are undefined. ^If
+** an SQLITE_BUSY error is encountered when processing one or more of the
+** attached WAL databases, the operation is still attempted on any remaining
+** attached databases and SQLITE_BUSY is returned at the end. ^If any other
+** error occurs while processing an attached database, processing is abandoned
+** and the error code is returned to the caller immediately. ^If no error
+** (SQLITE_BUSY or otherwise) is encountered while processing the attached
** databases, SQLITE_OK is returned.
**
** ^If database zDb is the name of an attached database that is not in WAL
@@ -9667,7 +10026,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
*/
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
-#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */
+#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
/*
@@ -9680,14 +10039,20 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
** If this interface is invoked outside the context of an xConnect or
** xCreate virtual table method then the behavior is undefined.
**
-** At present, there is only one option that may be configured using
-** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options
-** may be added in the future.
+** In the call sqlite3_vtab_config(D,C,...) the D parameter is the
+** [database connection] in which the virtual table is being created and
+** which is passed in as the first argument to the [xConnect] or [xCreate]
+** method that is invoking sqlite3_vtab_config(). The C parameter is one
+** of the [virtual table configuration options]. The presence and meaning
+** of parameters after C depend on which [virtual table configuration option]
+** is used.
*/
SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
/*
** CAPI3REF: Virtual Table Configuration Options
+** KEYWORDS: {virtual table configuration options}
+** KEYWORDS: {virtual table configuration option}
**
** These macros define the various options to the
** [sqlite3_vtab_config()] interface that [virtual table] implementations
@@ -9695,7 +10060,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
**
** <dl>
** [[SQLITE_VTAB_CONSTRAINT_SUPPORT]]
-** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT
+** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT</dt>
** <dd>Calls of the form
** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported,
** where X is an integer. If X is zero, then the [virtual table] whose
@@ -9709,24 +10074,56 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
** If X is non-zero, then the virtual table implementation guarantees
** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before
** any modifications to internal or persistent data structures have been made.
-** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite
+** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite
** is able to roll back a statement or database transaction, and abandon
-** or continue processing the current SQL statement as appropriate.
+** or continue processing the current SQL statement as appropriate.
** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns
** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode
** had been ABORT.
**
** Virtual table implementations that are required to handle OR REPLACE
-** must do so within the [xUpdate] method. If a call to the
-** [sqlite3_vtab_on_conflict()] function indicates that the current ON
-** CONFLICT policy is REPLACE, the virtual table implementation should
+** must do so within the [xUpdate] method. If a call to the
+** [sqlite3_vtab_on_conflict()] function indicates that the current ON
+** CONFLICT policy is REPLACE, the virtual table implementation should
** silently replace the appropriate rows within the xUpdate callback and
** return SQLITE_OK. Or, if this is not possible, it may return
-** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT
+** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT
** constraint handling.
+** </dd>
+**
+** [[SQLITE_VTAB_DIRECTONLY]]<dt>SQLITE_VTAB_DIRECTONLY</dt>
+** <dd>Calls of the form
+** [sqlite3_vtab_config](db,SQLITE_VTAB_DIRECTONLY) from within the
+** the [xConnect] or [xCreate] methods of a [virtual table] implementation
+** prohibits that virtual table from being used from within triggers and
+** views.
+** </dd>
+**
+** [[SQLITE_VTAB_INNOCUOUS]]<dt>SQLITE_VTAB_INNOCUOUS</dt>
+** <dd>Calls of the form
+** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the
+** the [xConnect] or [xCreate] methods of a [virtual table] implementation
+** identify that virtual table as being safe to use from within triggers
+** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the
+** virtual table can do no serious harm even if it is controlled by a
+** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS
+** flag unless absolutely necessary.
+** </dd>
+**
+** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]<dt>SQLITE_VTAB_USES_ALL_SCHEMAS</dt>
+** <dd>Calls of the form
+** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the
+** the [xConnect] or [xCreate] methods of a [virtual table] implementation
+** instruct the query planner to begin at least a read transaction on
+** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the
+** virtual table is used.
+** </dd>
** </dl>
*/
#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
+#define SQLITE_VTAB_INNOCUOUS 2
+#define SQLITE_VTAB_DIRECTONLY 3
+#define SQLITE_VTAB_USES_ALL_SCHEMAS 4
/*
** CAPI3REF: Determine The Virtual Table Conflict Policy
@@ -9744,10 +10141,11 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE
**
** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn]
-** method of a [virtual table], then it returns true if and only if the
+** method of a [virtual table], then it might return true if the
** column is being fetched as part of an UPDATE operation during which the
-** column value will not change. Applications might use this to substitute
-** a return value that is less expensive to compute and that the corresponding
+** column value will not change. The virtual table implementation can use
+** this hint as permission to substitute a return value that is less
+** expensive to compute and that the corresponding
** [xUpdate] method understands as a "no-change" value.
**
** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that
@@ -9756,23 +10154,285 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces].
** In that case, [sqlite3_value_nochange(X)] will return true for the
** same column in the [xUpdate] method.
+**
+** The sqlite3_vtab_nochange() routine is an optimization. Virtual table
+** implementations should continue to give a correct answer even if the
+** sqlite3_vtab_nochange() interface were to always return false. In the
+** current implementation, the sqlite3_vtab_nochange() interface does always
+** returns false for the enhanced [UPDATE FROM] statement.
*/
SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*);
/*
** CAPI3REF: Determine The Collation For a Virtual Table Constraint
+** METHOD: sqlite3_index_info
**
** This function may only be called from within a call to the [xBestIndex]
-** method of a [virtual table].
+** method of a [virtual table]. This function returns a pointer to a string
+** that is the name of the appropriate collation sequence to use for text
+** comparisons on the constraint identified by its arguments.
+**
+** The first argument must be the pointer to the [sqlite3_index_info] object
+** that is the first parameter to the xBestIndex() method. The second argument
+** must be an index into the aConstraint[] array belonging to the
+** sqlite3_index_info structure passed to xBestIndex.
+**
+** Important:
+** The first parameter must be the same pointer that is passed into the
+** xBestMethod() method. The first parameter may not be a pointer to a
+** different [sqlite3_index_info] object, even an exact copy.
**
-** The first argument must be the sqlite3_index_info object that is the
-** first parameter to the xBestIndex() method. The second argument must be
-** an index into the aConstraint[] array belonging to the sqlite3_index_info
-** structure passed to xBestIndex. This function returns a pointer to a buffer
-** containing the name of the collation sequence for the corresponding
-** constraint.
+** The return value is computed as follows:
+**
+** <ol>
+** <li><p> If the constraint comes from a WHERE clause expression that contains
+** a [COLLATE operator], then the name of the collation specified by
+** that COLLATE operator is returned.
+** <li><p> If there is no COLLATE operator, but the column that is the subject
+** of the constraint specifies an alternative collating sequence via
+** a [COLLATE clause] on the column definition within the CREATE TABLE
+** statement that was passed into [sqlite3_declare_vtab()], then the
+** name of that alternative collating sequence is returned.
+** <li><p> Otherwise, "BINARY" is returned.
+** </ol>
*/
-SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
+SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
+
+/*
+** CAPI3REF: Determine if a virtual table query is DISTINCT
+** METHOD: sqlite3_index_info
+**
+** This API may only be used from within an [xBestIndex|xBestIndex method]
+** of a [virtual table] implementation. The result of calling this
+** interface from outside of xBestIndex() is undefined and probably harmful.
+**
+** ^The sqlite3_vtab_distinct() interface returns an integer between 0 and
+** 3. The integer returned by sqlite3_vtab_distinct()
+** gives the virtual table additional information about how the query
+** planner wants the output to be ordered. As long as the virtual table
+** can meet the ordering requirements of the query planner, it may set
+** the "orderByConsumed" flag.
+**
+** <ol><li value="0"><p>
+** ^If the sqlite3_vtab_distinct() interface returns 0, that means
+** that the query planner needs the virtual table to return all rows in the
+** sort order defined by the "nOrderBy" and "aOrderBy" fields of the
+** [sqlite3_index_info] object. This is the default expectation. If the
+** virtual table outputs all rows in sorted order, then it is always safe for
+** the xBestIndex method to set the "orderByConsumed" flag, regardless of
+** the return value from sqlite3_vtab_distinct().
+** <li value="1"><p>
+** ^(If the sqlite3_vtab_distinct() interface returns 1, that means
+** that the query planner does not need the rows to be returned in sorted order
+** as long as all rows with the same values in all columns identified by the
+** "aOrderBy" field are adjacent.)^ This mode is used when the query planner
+** is doing a GROUP BY.
+** <li value="2"><p>
+** ^(If the sqlite3_vtab_distinct() interface returns 2, that means
+** that the query planner does not need the rows returned in any particular
+** order, as long as rows with the same values in all "aOrderBy" columns
+** are adjacent.)^ ^(Furthermore, only a single row for each particular
+** combination of values in the columns identified by the "aOrderBy" field
+** needs to be returned.)^ ^It is always ok for two or more rows with the same
+** values in all "aOrderBy" columns to be returned, as long as all such rows
+** are adjacent. ^The virtual table may, if it chooses, omit extra rows
+** that have the same value for all columns identified by "aOrderBy".
+** ^However omitting the extra rows is optional.
+** This mode is used for a DISTINCT query.
+** <li value="3"><p>
+** ^(If the sqlite3_vtab_distinct() interface returns 3, that means
+** that the query planner needs only distinct rows but it does need the
+** rows to be sorted.)^ ^The virtual table implementation is free to omit
+** rows that are identical in all aOrderBy columns, if it wants to, but
+** it is not required to omit any rows. This mode is used for queries
+** that have both DISTINCT and ORDER BY clauses.
+** </ol>
+**
+** ^For the purposes of comparing virtual table output values to see if the
+** values are same value for sorting purposes, two NULL values are considered
+** to be the same. In other words, the comparison operator is "IS"
+** (or "IS NOT DISTINCT FROM") and not "==".
+**
+** If a virtual table implementation is unable to meet the requirements
+** specified above, then it must not set the "orderByConsumed" flag in the
+** [sqlite3_index_info] object or an incorrect answer may result.
+**
+** ^A virtual table implementation is always free to return rows in any order
+** it wants, as long as the "orderByConsumed" flag is not set. ^When the
+** the "orderByConsumed" flag is unset, the query planner will add extra
+** [bytecode] to ensure that the final results returned by the SQL query are
+** ordered correctly. The use of the "orderByConsumed" flag and the
+** sqlite3_vtab_distinct() interface is merely an optimization. ^Careful
+** use of the sqlite3_vtab_distinct() interface and the "orderByConsumed"
+** flag might help queries against a virtual table to run faster. Being
+** overly aggressive and setting the "orderByConsumed" flag when it is not
+** valid to do so, on the other hand, might cause SQLite to return incorrect
+** results.
+*/
+SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*);
+
+/*
+** CAPI3REF: Identify and handle IN constraints in xBestIndex
+**
+** This interface may only be used from within an
+** [xBestIndex|xBestIndex() method] of a [virtual table] implementation.
+** The result of invoking this interface from any other context is
+** undefined and probably harmful.
+**
+** ^(A constraint on a virtual table of the form
+** "[IN operator|column IN (...)]" is
+** communicated to the xBestIndex method as a
+** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use
+** this constraint, it must set the corresponding
+** aConstraintUsage[].argvIndex to a positive integer. ^(Then, under
+** the usual mode of handling IN operators, SQLite generates [bytecode]
+** that invokes the [xFilter|xFilter() method] once for each value
+** on the right-hand side of the IN operator.)^ Thus the virtual table
+** only sees a single value from the right-hand side of the IN operator
+** at a time.
+**
+** In some cases, however, it would be advantageous for the virtual
+** table to see all values on the right-hand of the IN operator all at
+** once. The sqlite3_vtab_in() interfaces facilitates this in two ways:
+**
+** <ol>
+** <li><p>
+** ^A call to sqlite3_vtab_in(P,N,-1) will return true (non-zero)
+** if and only if the [sqlite3_index_info|P->aConstraint][N] constraint
+** is an [IN operator] that can be processed all at once. ^In other words,
+** sqlite3_vtab_in() with -1 in the third argument is a mechanism
+** by which the virtual table can ask SQLite if all-at-once processing
+** of the IN operator is even possible.
+**
+** <li><p>
+** ^A call to sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates
+** to SQLite that the virtual table does or does not want to process
+** the IN operator all-at-once, respectively. ^Thus when the third
+** parameter (F) is non-negative, this interface is the mechanism by
+** which the virtual table tells SQLite how it wants to process the
+** IN operator.
+** </ol>
+**
+** ^The sqlite3_vtab_in(P,N,F) interface can be invoked multiple times
+** within the same xBestIndex method call. ^For any given P,N pair,
+** the return value from sqlite3_vtab_in(P,N,F) will always be the same
+** within the same xBestIndex call. ^If the interface returns true
+** (non-zero), that means that the constraint is an IN operator
+** that can be processed all-at-once. ^If the constraint is not an IN
+** operator or cannot be processed all-at-once, then the interface returns
+** false.
+**
+** ^(All-at-once processing of the IN operator is selected if both of the
+** following conditions are met:
+**
+** <ol>
+** <li><p> The P->aConstraintUsage[N].argvIndex value is set to a positive
+** integer. This is how the virtual table tells SQLite that it wants to
+** use the N-th constraint.
+**
+** <li><p> The last call to sqlite3_vtab_in(P,N,F) for which F was
+** non-negative had F>=1.
+** </ol>)^
+**
+** ^If either or both of the conditions above are false, then SQLite uses
+** the traditional one-at-a-time processing strategy for the IN constraint.
+** ^If both conditions are true, then the argvIndex-th parameter to the
+** xFilter method will be an [sqlite3_value] that appears to be NULL,
+** but which can be passed to [sqlite3_vtab_in_first()] and
+** [sqlite3_vtab_in_next()] to find all values on the right-hand side
+** of the IN constraint.
+*/
+SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle);
+
+/*
+** CAPI3REF: Find all elements on the right-hand side of an IN constraint.
+**
+** These interfaces are only useful from within the
+** [xFilter|xFilter() method] of a [virtual table] implementation.
+** The result of invoking these interfaces from any other context
+** is undefined and probably harmful.
+**
+** The X parameter in a call to sqlite3_vtab_in_first(X,P) or
+** sqlite3_vtab_in_next(X,P) should be one of the parameters to the
+** xFilter method which invokes these routines, and specifically
+** a parameter that was previously selected for all-at-once IN constraint
+** processing use the [sqlite3_vtab_in()] interface in the
+** [xBestIndex|xBestIndex method]. ^(If the X parameter is not
+** an xFilter argument that was selected for all-at-once IN constraint
+** processing, then these routines return [SQLITE_ERROR].)^
+**
+** ^(Use these routines to access all values on the right-hand side
+** of the IN constraint using code like the following:
+**
+** <blockquote><pre>
+** &nbsp; for(rc=sqlite3_vtab_in_first(pList, &pVal);
+** &nbsp; rc==SQLITE_OK && pVal;
+** &nbsp; rc=sqlite3_vtab_in_next(pList, &pVal)
+** &nbsp; ){
+** &nbsp; // do something with pVal
+** &nbsp; }
+** &nbsp; if( rc!=SQLITE_OK ){
+** &nbsp; // an error has occurred
+** &nbsp; }
+** </pre></blockquote>)^
+**
+** ^On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P)
+** routines return SQLITE_OK and set *P to point to the first or next value
+** on the RHS of the IN constraint. ^If there are no more values on the
+** right hand side of the IN constraint, then *P is set to NULL and these
+** routines return [SQLITE_DONE]. ^The return value might be
+** some other value, such as SQLITE_NOMEM, in the event of a malfunction.
+**
+** The *ppOut values returned by these routines are only valid until the
+** next call to either of these routines or until the end of the xFilter
+** method from which these routines were called. If the virtual table
+** implementation needs to retain the *ppOut values for longer, it must make
+** copies. The *ppOut values are [protected sqlite3_value|protected].
+*/
+SQLITE_API int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut);
+SQLITE_API int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut);
+
+/*
+** CAPI3REF: Constraint values in xBestIndex()
+** METHOD: sqlite3_index_info
+**
+** This API may only be used from within the [xBestIndex|xBestIndex method]
+** of a [virtual table] implementation. The result of calling this interface
+** from outside of an xBestIndex method are undefined and probably harmful.
+**
+** ^When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within
+** the [xBestIndex] method of a [virtual table] implementation, with P being
+** a copy of the [sqlite3_index_info] object pointer passed into xBestIndex and
+** J being a 0-based index into P->aConstraint[], then this routine
+** attempts to set *V to the value of the right-hand operand of
+** that constraint if the right-hand operand is known. ^If the
+** right-hand operand is not known, then *V is set to a NULL pointer.
+** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if
+** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V)
+** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th
+** constraint is not available. ^The sqlite3_vtab_rhs_value() interface
+** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if
+** something goes wrong.
+**
+** The sqlite3_vtab_rhs_value() interface is usually only successful if
+** the right-hand operand of a constraint is a literal value in the original
+** SQL statement. If the right-hand operand is an expression or a reference
+** to some other column or a [host parameter], then sqlite3_vtab_rhs_value()
+** will probably return [SQLITE_NOTFOUND].
+**
+** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and
+** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such
+** constraints, sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^
+**
+** ^The [sqlite3_value] object returned in *V is a protected sqlite3_value
+** and remains valid for the duration of the xBestIndex method call.
+** ^When xBestIndex returns, the sqlite3_value object returned by
+** sqlite3_vtab_rhs_value() is automatically deallocated.
+**
+** The "_rhs_" in the name of this routine is an abbreviation for
+** "Right-Hand Side".
+*/
+SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
/*
** CAPI3REF: Conflict resolution modes
@@ -9804,17 +10464,21 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
** managed by the prepared statement S and will be automatically freed when
** S is finalized.
**
+** Not all values are available for all query elements. When a value is
+** not available, the output variable is set to -1 if the value is numeric,
+** or to NULL if it is a string (SQLITE_SCANSTAT_NAME).
+**
** <dl>
** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt>
-** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be
+** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be
** set to the total number of times that the X-th loop has run.</dd>
**
** [[SQLITE_SCANSTAT_NVISIT]] <dt>SQLITE_SCANSTAT_NVISIT</dt>
-** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be set
+** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be set
** to the total number of rows examined by all iterations of the X-th loop.</dd>
**
** [[SQLITE_SCANSTAT_EST]] <dt>SQLITE_SCANSTAT_EST</dt>
-** <dd>^The "double" variable pointed to by the T parameter will be set to the
+** <dd>^The "double" variable pointed to by the V parameter will be set to the
** query planner's estimate for the average number of rows output from each
** iteration of the X-th loop. If the query planner's estimates was accurate,
** then this value will approximate the quotient NVISIT/NLOOP and the
@@ -9822,21 +10486,33 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
** be the NLOOP value for the current loop.
**
** [[SQLITE_SCANSTAT_NAME]] <dt>SQLITE_SCANSTAT_NAME</dt>
-** <dd>^The "const char *" variable pointed to by the T parameter will be set
+** <dd>^The "const char *" variable pointed to by the V parameter will be set
** to a zero-terminated UTF-8 string containing the name of the index or table
** used for the X-th loop.
**
** [[SQLITE_SCANSTAT_EXPLAIN]] <dt>SQLITE_SCANSTAT_EXPLAIN</dt>
-** <dd>^The "const char *" variable pointed to by the T parameter will be set
+** <dd>^The "const char *" variable pointed to by the V parameter will be set
** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN]
** description for the X-th loop.
**
-** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECT</dt>
-** <dd>^The "int" variable pointed to by the T parameter will be set to the
-** "select-id" for the X-th loop. The select-id identifies which query or
-** subquery the loop is part of. The main query has a select-id of zero.
-** The select-id is the same value as is output in the first column
-** of an [EXPLAIN QUERY PLAN] query.
+** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECTID</dt>
+** <dd>^The "int" variable pointed to by the V parameter will be set to the
+** id for the X-th query plan element. The id value is unique within the
+** statement. The select-id is the same value as is output in the first
+** column of an [EXPLAIN QUERY PLAN] query.
+**
+** [[SQLITE_SCANSTAT_PARENTID]] <dt>SQLITE_SCANSTAT_PARENTID</dt>
+** <dd>The "int" variable pointed to by the V parameter will be set to the
+** the id of the parent of the current query element, if applicable, or
+** to zero if the query element has no parent. This is the same value as
+** returned in the second column of an [EXPLAIN QUERY PLAN] query.
+**
+** [[SQLITE_SCANSTAT_NCYCLE]] <dt>SQLITE_SCANSTAT_NCYCLE</dt>
+** <dd>The sqlite3_int64 output value is set to the number of cycles,
+** according to the processor time-stamp counter, that elapsed while the
+** query element was being processed. This value is not available for
+** all query elements - if it is unavailable the output variable is
+** set to -1.
** </dl>
*/
#define SQLITE_SCANSTAT_NLOOP 0
@@ -9845,12 +10521,14 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
#define SQLITE_SCANSTAT_NAME 3
#define SQLITE_SCANSTAT_EXPLAIN 4
#define SQLITE_SCANSTAT_SELECTID 5
+#define SQLITE_SCANSTAT_PARENTID 6
+#define SQLITE_SCANSTAT_NCYCLE 7
/*
** CAPI3REF: Prepared Statement Scan Status
** METHOD: sqlite3_stmt
**
-** This interface returns information about the predicted and measured
+** These interfaces return information about the predicted and measured
** performance for pStmt. Advanced applications can use this
** interface to compare the predicted and the measured performance and
** issue warnings and/or rerun [ANALYZE] if discrepancies are found.
@@ -9861,19 +10539,25 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
**
** The "iScanStatusOp" parameter determines which status information to return.
** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior
-** of this interface is undefined.
-** ^The requested measurement is written into a variable pointed to by
-** the "pOut" parameter.
-** Parameter "idx" identifies the specific loop to retrieve statistics for.
-** Loops are numbered starting from zero. ^If idx is out of range - less than
-** zero or greater than or equal to the total number of loops used to implement
-** the statement - a non-zero value is returned and the variable that pOut
-** points to is unchanged.
-**
-** ^Statistics might not be available for all loops in all statements. ^In cases
-** where there exist loops with no available statistics, this function behaves
-** as if the loop did not exist - it returns non-zero and leave the variable
-** that pOut points to unchanged.
+** of this interface is undefined. ^The requested measurement is written into
+** a variable pointed to by the "pOut" parameter.
+**
+** The "flags" parameter must be passed a mask of flags. At present only
+** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX
+** is specified, then status information is available for all elements
+** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If
+** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements
+** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of
+** the EXPLAIN QUERY PLAN output) are available. Invoking API
+** sqlite3_stmt_scanstatus() is equivalent to calling
+** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter.
+**
+** Parameter "idx" identifies the specific query element to retrieve statistics
+** for. Query elements are numbered starting from zero. A value of -1 may be
+** to query for statistics regarding the entire query. ^If idx is out of range
+** - less than -1 or greater than or equal to the total number of query
+** elements used to implement the statement - a non-zero value is returned and
+** the variable that pOut points to is unchanged.
**
** See also: [sqlite3_stmt_scanstatus_reset()]
*/
@@ -9882,7 +10566,20 @@ SQLITE_API int sqlite3_stmt_scanstatus(
int idx, /* Index of loop to report on */
int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
void *pOut /* Result written here */
-);
+);
+SQLITE_API int sqlite3_stmt_scanstatus_v2(
+ sqlite3_stmt *pStmt, /* Prepared statement for which info desired */
+ int idx, /* Index of loop to report on */
+ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
+ int flags, /* Mask of flags defined below */
+ void *pOut /* Result written here */
+);
+
+/*
+** CAPI3REF: Prepared Statement Scan Status
+** KEYWORDS: {scan status flags}
+*/
+#define SQLITE_SCANSTAT_COMPLEX 0x0001
/*
** CAPI3REF: Zero Scan-Status Counters
@@ -9897,18 +10594,19 @@ SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
/*
** CAPI3REF: Flush caches to disk mid-transaction
+** METHOD: sqlite3
**
** ^If a write-transaction is open on [database connection] D when the
** [sqlite3_db_cacheflush(D)] interface invoked, any dirty
-** pages in the pager-cache that are not currently in use are written out
+** pages in the pager-cache that are not currently in use are written out
** to disk. A dirty page may be in use if a database cursor created by an
** active SQL statement is reading from it, or if it is page 1 of a database
** file (page 1 is always "in use"). ^The [sqlite3_db_cacheflush(D)]
** interface flushes caches for all schemas - "main", "temp", and
** any [attached] databases.
**
-** ^If this function needs to obtain extra database locks before dirty pages
-** can be flushed to disk, it does so. ^If those locks cannot be obtained
+** ^If this function needs to obtain extra database locks before dirty pages
+** can be flushed to disk, it does so. ^If those locks cannot be obtained
** immediately and there is a busy-handler callback configured, it is invoked
** in the usual manner. ^If the required lock still cannot be obtained, then
** the database is skipped and an attempt made to flush any dirty pages
@@ -9929,6 +10627,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
/*
** CAPI3REF: The pre-update hook.
+** METHOD: sqlite3
**
** ^These interfaces are only available if SQLite is compiled using the
** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option.
@@ -9946,7 +10645,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
**
** ^The preupdate hook only fires for changes to real database tables; the
** preupdate hook is not invoked for changes to [virtual tables] or to
-** system tables like sqlite_master or sqlite_stat1.
+** system tables like sqlite_sequence or sqlite_stat1.
**
** ^The second parameter to the preupdate callback is a pointer to
** the [database connection] that registered the preupdate hook.
@@ -9955,21 +10654,25 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
** kind of update operation that is about to occur.
** ^(The fourth parameter to the preupdate callback is the name of the
** database within the database connection that is being modified. This
-** will be "main" for the main database or "temp" for TEMP tables or
+** will be "main" for the main database or "temp" for TEMP tables or
** the name given after the AS keyword in the [ATTACH] statement for attached
** databases.)^
** ^The fifth parameter to the preupdate callback is the name of the
** table that is being modified.
**
** For an UPDATE or DELETE operation on a [rowid table], the sixth
-** parameter passed to the preupdate callback is the initial [rowid] of the
+** parameter passed to the preupdate callback is the initial [rowid] of the
** row being modified or deleted. For an INSERT operation on a rowid table,
-** or any operation on a WITHOUT ROWID table, the value of the sixth
+** or any operation on a WITHOUT ROWID table, the value of the sixth
** parameter is undefined. For an INSERT or UPDATE on a rowid table the
** seventh parameter is the final rowid value of the row being inserted
** or updated. The value of the seventh parameter passed to the callback
** function is not defined for operations on WITHOUT ROWID tables, or for
-** INSERT operations on rowid tables.
+** DELETE operations on rowid tables.
+**
+** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from
+** the previous call on the same [database connection] D, or NULL for
+** the first call on D.
**
** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
@@ -10003,10 +10706,19 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
**
** ^The [sqlite3_preupdate_depth(D)] interface returns 0 if the preupdate
** callback was invoked as a result of a direct insert, update, or delete
-** operation; or 1 for inserts, updates, or deletes invoked by top-level
+** operation; or 1 for inserts, updates, or deletes invoked by top-level
** triggers; or 2 for changes resulting from triggers called by top-level
** triggers; and so forth.
**
+** When the [sqlite3_blob_write()] API is used to update a blob column,
+** the pre-update hook is invoked with SQLITE_DELETE. This is because the
+** in this case the new values are not available. In this case, when a
+** callback made with op==SQLITE_DELETE is actually a write using the
+** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns
+** the index of the column being written. In other cases, where the
+** pre-update hook is being invoked for some other reason, including a
+** regular DELETE, sqlite3_preupdate_blobwrite() returns -1.
+**
** See also: [sqlite3_update_hook()]
*/
#if defined(SQLITE_ENABLE_PREUPDATE_HOOK)
@@ -10027,17 +10739,19 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
SQLITE_API int sqlite3_preupdate_count(sqlite3 *);
SQLITE_API int sqlite3_preupdate_depth(sqlite3 *);
SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
+SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *);
#endif
/*
** CAPI3REF: Low-level system error code
+** METHOD: sqlite3
**
** ^Attempt to return the underlying operating system error code or error
** number that caused the most recent I/O error or failure to open a file.
** The return value is OS-dependent. For example, on unix systems, after
** [sqlite3_open_v2()] returns [SQLITE_CANTOPEN], this interface could be
** called to get back the underlying "errno" that caused the problem, such
-** as ENOSPC, EAUTH, EISDIR, and so forth.
+** as ENOSPC, EAUTH, EISDIR, and so forth.
*/
SQLITE_API int sqlite3_system_errno(sqlite3*);
@@ -10075,12 +10789,12 @@ typedef struct sqlite3_snapshot {
** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly
** created [sqlite3_snapshot] object into *P and returns SQLITE_OK.
** If there is not already a read-transaction open on schema S when
-** this function is called, one is opened automatically.
+** this function is called, one is opened automatically.
**
** The following must be true for this function to succeed. If any of
** the following statements are false when sqlite3_snapshot_get() is
** called, SQLITE_ERROR is returned. The final value of *P is undefined
-** in this case.
+** in this case.
**
** <ul>
** <li> The database handle must not be in [autocommit mode].
@@ -10092,13 +10806,13 @@ typedef struct sqlite3_snapshot {
**
** <li> One or more transactions must have been written to the current wal
** file since it was created on disk (by any connection). This means
-** that a snapshot cannot be taken on a wal mode database with no wal
+** that a snapshot cannot be taken on a wal mode database with no wal
** file immediately after it is first opened. At least one transaction
** must be written to it first.
** </ul>
**
** This function may also return SQLITE_NOMEM. If it is called with the
-** database handle in autocommit mode but fails for some other reason,
+** database handle in autocommit mode but fails for some other reason,
** whether or not a read transaction is opened on schema S is undefined.
**
** The [sqlite3_snapshot] object returned from a successful call to
@@ -10118,38 +10832,38 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
** CAPI3REF: Start a read transaction on an historical snapshot
** METHOD: sqlite3_snapshot
**
-** ^The [sqlite3_snapshot_open(D,S,P)] interface either starts a new read
-** transaction or upgrades an existing one for schema S of
-** [database connection] D such that the read transaction refers to
-** historical [snapshot] P, rather than the most recent change to the
-** database. ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK
+** ^The [sqlite3_snapshot_open(D,S,P)] interface either starts a new read
+** transaction or upgrades an existing one for schema S of
+** [database connection] D such that the read transaction refers to
+** historical [snapshot] P, rather than the most recent change to the
+** database. ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK
** on success or an appropriate [error code] if it fails.
**
-** ^In order to succeed, the database connection must not be in
+** ^In order to succeed, the database connection must not be in
** [autocommit mode] when [sqlite3_snapshot_open(D,S,P)] is called. If there
** is already a read transaction open on schema S, then the database handle
** must have no active statements (SELECT statements that have been passed
-** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()).
+** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()).
** SQLITE_ERROR is returned if either of these conditions is violated, or
** if schema S does not exist, or if the snapshot object is invalid.
**
** ^A call to sqlite3_snapshot_open() will fail to open if the specified
-** snapshot has been overwritten by a [checkpoint]. In this case
+** snapshot has been overwritten by a [checkpoint]. In this case
** SQLITE_ERROR_SNAPSHOT is returned.
**
-** If there is already a read transaction open when this function is
+** If there is already a read transaction open when this function is
** invoked, then the same read transaction remains open (on the same
** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_ERROR_SNAPSHOT
** is returned. If another error code - for example SQLITE_PROTOCOL or an
** SQLITE_IOERR error code - is returned, then the final state of the
-** read transaction is undefined. If SQLITE_OK is returned, then the
+** read transaction is undefined. If SQLITE_OK is returned, then the
** read transaction is now open on database snapshot P.
**
** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
** database connection D does not know that the database file for
** schema S is in [WAL mode]. A database connection might not know
** that the database file is in [WAL mode] if there has been no prior
-** I/O on that database connection, or if the database entered [WAL mode]
+** I/O on that database connection, or if the database entered [WAL mode]
** after the most recent I/O on the database connection.)^
** (Hint: Run "[PRAGMA application_id]" against a newly opened
** database connection in order to make it ready to use snapshots.)
@@ -10181,17 +10895,17 @@ SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
** METHOD: sqlite3_snapshot
**
** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages
-** of two valid snapshot handles.
+** of two valid snapshot handles.
**
-** If the two snapshot handles are not associated with the same database
-** file, the result of the comparison is undefined.
+** If the two snapshot handles are not associated with the same database
+** file, the result of the comparison is undefined.
**
** Additionally, the result of the comparison is only valid if both of the
** snapshot handles were obtained by calling sqlite3_snapshot_get() since the
** last time the wal file was deleted. The wal file is deleted when the
** database is changed back to rollback mode or when the number of database
-** clients drops to zero. If either snapshot handle was obtained before the
-** wal file was last deleted, the value returned by this function
+** clients drops to zero. If either snapshot handle was obtained before the
+** wal file was last deleted, the value returned by this function
** is undefined.
**
** Otherwise, this API returns a negative value if P1 refers to an older
@@ -10256,16 +10970,23 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
** representation of the database will usually only exist if there has
** been a prior call to [sqlite3_deserialize(D,S,...)] with the same
** values of D and S.
-** The size of the database is written into *P even if the
+** The size of the database is written into *P even if the
** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
** of the database exists.
**
+** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set,
+** the returned buffer content will remain accessible and unchanged
+** until either the next write operation on the connection or when
+** the connection is closed, and applications must not modify the
+** buffer. If the bit had been clear, the returned buffer will not
+** be accessed by SQLite after the call.
+**
** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
** allocation error occurs.
**
-** This interface is only available if SQLite is compiled with the
-** [SQLITE_ENABLE_DESERIALIZE] option.
+** This interface is omitted if SQLite is compiled with the
+** [SQLITE_OMIT_DESERIALIZE] option.
*/
SQLITE_API unsigned char *sqlite3_serialize(
sqlite3 *db, /* The database connection */
@@ -10293,7 +11014,7 @@ SQLITE_API unsigned char *sqlite3_serialize(
/*
** CAPI3REF: Deserialize a database
**
-** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
+** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
** [database connection] D to disconnect from database S and then
** reopen S as an in-memory database based on the serialization contained
** in P. The serialized database P is N bytes in size. M is the size of
@@ -10308,16 +11029,30 @@ SQLITE_API unsigned char *sqlite3_serialize(
** SQLite will try to increase the buffer size using sqlite3_realloc64()
** if writes on the database cause it to grow larger than M bytes.
**
+** Applications must not modify the buffer P or invalidate it before
+** the database connection D is closed.
+**
** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the
** database is currently in a read transaction or is involved in a backup
** operation.
**
-** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
+** It is not possible to deserialized into the TEMP database. If the
+** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the
+** function returns SQLITE_ERROR.
+**
+** The deserialized database should not be in [WAL mode]. If the database
+** is in WAL mode, then any attempt to use the database file will result
+** in an [SQLITE_CANTOPEN] error. The application can set the
+** [file format version numbers] (bytes 18 and 19) of the input database P
+** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the
+** database file into rollback mode and work around this limitation.
+**
+** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
** [sqlite3_free()] is invoked on argument P prior to returning.
**
-** This interface is only available if SQLite is compiled with the
-** [SQLITE_ENABLE_DESERIALIZE] option.
+** This interface is omitted if SQLite is compiled with the
+** [SQLITE_OMIT_DESERIALIZE] option.
*/
SQLITE_API int sqlite3_deserialize(
sqlite3 *db, /* The database connection */
@@ -10361,6 +11096,19 @@ SQLITE_API int sqlite3_deserialize(
# undef double
#endif
+#if defined(__wasi__)
+# undef SQLITE_WASI
+# define SQLITE_WASI 1
+# undef SQLITE_OMIT_WAL
+# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */
+# ifndef SQLITE_OMIT_LOAD_EXTENSION
+# define SQLITE_OMIT_LOAD_EXTENSION
+# endif
+# ifndef SQLITE_THREADSAFE
+# define SQLITE_THREADSAFE 0
+# endif
+#endif
+
#if 0
} /* End of the 'extern "C"' block */
#endif
@@ -10427,7 +11175,7 @@ struct sqlite3_rtree_geometry {
};
/*
-** Register a 2nd-generation geometry callback named zScore that can be
+** Register a 2nd-generation geometry callback named zScore that can be
** used as part of an R-Tree geometry query as follows:
**
** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...)
@@ -10442,7 +11190,7 @@ SQLITE_API int sqlite3_rtree_query_callback(
/*
-** A pointer to a structure of the following type is passed as the
+** A pointer to a structure of the following type is passed as the
** argument to scored geometry callback registered using
** sqlite3_rtree_query_callback().
**
@@ -10537,7 +11285,7 @@ typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;
** is not possible for an application to register a pre-update hook on a
** database handle that has one or more session objects attached. Nor is
** it possible to create a session object attached to a database handle for
-** which a pre-update hook is already defined. The results of attempting
+** which a pre-update hook is already defined. The results of attempting
** either of these things are undefined.
**
** The session object will be used to create changesets for tables in
@@ -10555,17 +11303,62 @@ SQLITE_API int sqlite3session_create(
** CAPI3REF: Delete A Session Object
** DESTRUCTOR: sqlite3_session
**
-** Delete a session object previously allocated using
+** Delete a session object previously allocated using
** [sqlite3session_create()]. Once a session object has been deleted, the
** results of attempting to use pSession with any other session module
** function are undefined.
**
** Session objects must be deleted before the database handle to which they
-** are attached is closed. Refer to the documentation for
+** are attached is closed. Refer to the documentation for
** [sqlite3session_create()] for details.
*/
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
+/*
+** CAPI3REF: Configure a Session Object
+** METHOD: sqlite3_session
+**
+** This method is used to configure a session object after it has been
+** created. At present the only valid values for the second parameter are
+** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID].
+**
+*/
+SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);
+
+/*
+** CAPI3REF: Options for sqlite3session_object_config
+**
+** The following values may passed as the the 2nd parameter to
+** sqlite3session_object_config().
+**
+** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd>
+** This option is used to set, clear or query the flag that enables
+** the [sqlite3session_changeset_size()] API. Because it imposes some
+** computational overhead, this API is disabled by default. Argument
+** pArg must point to a value of type (int). If the value is initially
+** 0, then the sqlite3session_changeset_size() API is disabled. If it
+** is greater than 0, then the same API is enabled. Or, if the initial
+** value is less than zero, no change is made. In all cases the (int)
+** variable is set to 1 if the sqlite3session_changeset_size() API is
+** enabled following the current call, or 0 otherwise.
+**
+** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
+** the first table has been attached to the session object.
+**
+** <dt>SQLITE_SESSION_OBJCONFIG_ROWID <dd>
+** This option is used to set, clear or query the flag that enables
+** collection of data for tables with no explicit PRIMARY KEY.
+**
+** Normally, tables with no explicit PRIMARY KEY are simply ignored
+** by the sessions module. However, if this flag is set, it behaves
+** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted
+** as their leftmost columns.
+**
+** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
+** the first table has been attached to the session object.
+*/
+#define SQLITE_SESSION_OBJCONFIG_SIZE 1
+#define SQLITE_SESSION_OBJCONFIG_ROWID 2
/*
** CAPI3REF: Enable Or Disable A Session Object
@@ -10579,10 +11372,10 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
** the eventual changesets.
**
** Passing zero to this function disables the session. Passing a value
-** greater than zero enables it. Passing a value less than zero is a
+** greater than zero enables it. Passing a value less than zero is a
** no-op, and may be used to query the current state of the session.
**
-** The return value indicates the final state of the session object: 0 if
+** The return value indicates the final state of the session object: 0 if
** the session is disabled, or 1 if it is enabled.
*/
SQLITE_API int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
@@ -10597,7 +11390,7 @@ SQLITE_API int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
** <ul>
** <li> The session object "indirect" flag is set when the change is
** made, or
-** <li> The change is made by an SQL trigger or foreign key action
+** <li> The change is made by an SQL trigger or foreign key action
** instead of directly as a result of a users SQL statement.
** </ul>
**
@@ -10609,10 +11402,10 @@ SQLITE_API int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
** flag. If the second argument passed to this function is zero, then the
** indirect flag is cleared. If it is greater than zero, the indirect flag
** is set. Passing a value less than zero does not modify the current value
-** of the indirect flag, and may be used to query the current state of the
+** of the indirect flag, and may be used to query the current state of the
** indirect flag for the specified session object.
**
-** The return value indicates the final state of the indirect flag: 0 if
+** The return value indicates the final state of the indirect flag: 0 if
** it is clear, or 1 if it is set.
*/
SQLITE_API int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect);
@@ -10622,20 +11415,20 @@ SQLITE_API int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect)
** METHOD: sqlite3_session
**
** If argument zTab is not NULL, then it is the name of a table to attach
-** to the session object passed as the first argument. All subsequent changes
-** made to the table while the session object is enabled will be recorded. See
+** to the session object passed as the first argument. All subsequent changes
+** made to the table while the session object is enabled will be recorded. See
** documentation for [sqlite3session_changeset()] for further details.
**
** Or, if argument zTab is NULL, then changes are recorded for all tables
-** in the database. If additional tables are added to the database (by
-** executing "CREATE TABLE" statements) after this call is made, changes for
+** in the database. If additional tables are added to the database (by
+** executing "CREATE TABLE" statements) after this call is made, changes for
** the new tables are also recorded.
**
** Changes can only be recorded for tables that have a PRIMARY KEY explicitly
-** defined as part of their CREATE TABLE statement. It does not matter if the
+** defined as part of their CREATE TABLE statement. It does not matter if the
** PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias) or not. The PRIMARY
** KEY may consist of a single column, or may be a composite key.
-**
+**
** It is not an error if the named table does not exist in the database. Nor
** is it an error if the named table does not have a PRIMARY KEY. However,
** no changes will be recorded in either of these scenarios.
@@ -10643,29 +11436,29 @@ SQLITE_API int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect)
** Changes are not recorded for individual rows that have NULL values stored
** in one or more of their PRIMARY KEY columns.
**
-** SQLITE_OK is returned if the call completes without error. Or, if an error
+** SQLITE_OK is returned if the call completes without error. Or, if an error
** occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
**
** <h3>Special sqlite_stat1 Handling</h3>
**
-** As of SQLite version 3.22.0, the "sqlite_stat1" table is an exception to
+** As of SQLite version 3.22.0, the "sqlite_stat1" table is an exception to
** some of the rules above. In SQLite, the schema of sqlite_stat1 is:
** <pre>
-** &nbsp; CREATE TABLE sqlite_stat1(tbl,idx,stat)
+** &nbsp; CREATE TABLE sqlite_stat1(tbl,idx,stat)
** </pre>
**
-** Even though sqlite_stat1 does not have a PRIMARY KEY, changes are
-** recorded for it as if the PRIMARY KEY is (tbl,idx). Additionally, changes
+** Even though sqlite_stat1 does not have a PRIMARY KEY, changes are
+** recorded for it as if the PRIMARY KEY is (tbl,idx). Additionally, changes
** are recorded for rows for which (idx IS NULL) is true. However, for such
** rows a zero-length blob (SQL value X'') is stored in the changeset or
** patchset instead of a NULL value. This allows such changesets to be
** manipulated by legacy implementations of sqlite3changeset_invert(),
** concat() and similar.
**
-** The sqlite3changeset_apply() function automatically converts the
+** The sqlite3changeset_apply() function automatically converts the
** zero-length blob back to a NULL value when updating the sqlite_stat1
** table. However, if the application calls sqlite3changeset_new(),
-** sqlite3changeset_old() or sqlite3changeset_conflict on a changeset
+** sqlite3changeset_old() or sqlite3changeset_conflict on a changeset
** iterator directly (including on a changeset iterator passed to a
** conflict-handler callback) then the X'' value is returned. The application
** must translate X'' to NULL itself if required.
@@ -10684,10 +11477,10 @@ SQLITE_API int sqlite3session_attach(
** CAPI3REF: Set a table filter on a Session Object.
** METHOD: sqlite3_session
**
-** The second argument (xFilter) is the "filter callback". For changes to rows
+** The second argument (xFilter) is the "filter callback". For changes to rows
** in tables that are not attached to the Session object, the filter is called
-** to determine whether changes to the table's rows should be tracked or not.
-** If xFilter returns 0, changes is not tracked. Note that once a table is
+** to determine whether changes to the table's rows should be tracked or not.
+** If xFilter returns 0, changes are not tracked. Note that once a table is
** attached, xFilter will not be called again.
*/
SQLITE_API void sqlite3session_table_filter(
@@ -10703,9 +11496,9 @@ SQLITE_API void sqlite3session_table_filter(
** CAPI3REF: Generate A Changeset From A Session Object
** METHOD: sqlite3_session
**
-** Obtain a changeset containing changes to the tables attached to the
-** session object passed as the first argument. If successful,
-** set *ppChangeset to point to a buffer containing the changeset
+** Obtain a changeset containing changes to the tables attached to the
+** session object passed as the first argument. If successful,
+** set *ppChangeset to point to a buffer containing the changeset
** and *pnChangeset to the size of the changeset in bytes before returning
** SQLITE_OK. If an error occurs, set both *ppChangeset and *pnChangeset to
** zero and return an SQLite error code.
@@ -10720,7 +11513,7 @@ SQLITE_API void sqlite3session_table_filter(
** modifies the values of primary key columns. If such a change is made, it
** is represented in a changeset as a DELETE followed by an INSERT.
**
-** Changes are not recorded for rows that have NULL values stored in one or
+** Changes are not recorded for rows that have NULL values stored in one or
** more of their PRIMARY KEY columns. If such a row is inserted or deleted,
** no corresponding change is present in the changesets returned by this
** function. If an existing row with one or more NULL values stored in
@@ -10773,14 +11566,14 @@ SQLITE_API void sqlite3session_table_filter(
** <ul>
** <li> For each record generated by an insert, the database is queried
** for a row with a matching primary key. If one is found, an INSERT
-** change is added to the changeset. If no such row is found, no change
+** change is added to the changeset. If no such row is found, no change
** is added to the changeset.
**
-** <li> For each record generated by an update or delete, the database is
+** <li> For each record generated by an update or delete, the database is
** queried for a row with a matching primary key. If such a row is
** found and one or more of the non-primary key fields have been
-** modified from their original values, an UPDATE change is added to
-** the changeset. Or, if no such row is found in the table, a DELETE
+** modified from their original values, an UPDATE change is added to
+** the changeset. Or, if no such row is found in the table, a DELETE
** change is added to the changeset. If there is a row with a matching
** primary key in the database, but all fields contain their original
** values, no change is added to the changeset.
@@ -10788,7 +11581,7 @@ SQLITE_API void sqlite3session_table_filter(
**
** This means, amongst other things, that if a row is inserted and then later
** deleted while a session object is active, neither the insert nor the delete
-** will be present in the changeset. Or if a row is deleted and then later a
+** will be present in the changeset. Or if a row is deleted and then later a
** row with the same primary key values inserted while a session object is
** active, the resulting changeset will contain an UPDATE change instead of
** a DELETE and an INSERT.
@@ -10797,10 +11590,10 @@ SQLITE_API void sqlite3session_table_filter(
** it does not accumulate records when rows are inserted, updated or deleted.
** This may appear to have some counter-intuitive effects if a single row
** is written to more than once during a session. For example, if a row
-** is inserted while a session object is enabled, then later deleted while
+** is inserted while a session object is enabled, then later deleted while
** the same session object is disabled, no INSERT record will appear in the
** changeset, even though the delete took place while the session was disabled.
-** Or, if one field of a row is updated while a session is disabled, and
+** Or, if one field of a row is updated while a session is disabled, and
** another field of the same row is updated while the session is enabled, the
** resulting changeset will contain an UPDATE change that updates both fields.
*/
@@ -10811,6 +11604,22 @@ SQLITE_API int sqlite3session_changeset(
);
/*
+** CAPI3REF: Return An Upper-limit For The Size Of The Changeset
+** METHOD: sqlite3_session
+**
+** By default, this function always returns 0. For it to return
+** a useful result, the sqlite3_session object must have been configured
+** to enable this API using sqlite3session_object_config() with the
+** SQLITE_SESSION_OBJCONFIG_SIZE verb.
+**
+** When enabled, this function returns an upper limit, in bytes, for the size
+** of the changeset that might be produced if sqlite3session_changeset() were
+** called. The final changeset size might be equal to or smaller than the
+** size in bytes returned by this function.
+*/
+SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession);
+
+/*
** CAPI3REF: Load The Difference Between Tables Into A Session
** METHOD: sqlite3_session
**
@@ -10821,7 +11630,7 @@ SQLITE_API int sqlite3session_changeset(
** an error).
**
** Argument zFromDb must be the name of a database ("main", "temp" etc.)
-** attached to the same database handle as the session object that contains
+** attached to the same database handle as the session object that contains
** a table compatible with the table attached to the session by this function.
** A table is considered compatible if it:
**
@@ -10837,33 +11646,33 @@ SQLITE_API int sqlite3session_changeset(
** APIs, tables without PRIMARY KEYs are simply ignored.
**
** This function adds a set of changes to the session object that could be
-** used to update the table in database zFrom (call this the "from-table")
-** so that its content is the same as the table attached to the session
+** used to update the table in database zFrom (call this the "from-table")
+** so that its content is the same as the table attached to the session
** object (call this the "to-table"). Specifically:
**
** <ul>
-** <li> For each row (primary key) that exists in the to-table but not in
+** <li> For each row (primary key) that exists in the to-table but not in
** the from-table, an INSERT record is added to the session object.
**
-** <li> For each row (primary key) that exists in the to-table but not in
+** <li> For each row (primary key) that exists in the to-table but not in
** the from-table, a DELETE record is added to the session object.
**
-** <li> For each row (primary key) that exists in both tables, but features
+** <li> For each row (primary key) that exists in both tables, but features
** different non-PK values in each, an UPDATE record is added to the
-** session.
+** session.
** </ul>
**
** To clarify, if this function is called and then a changeset constructed
-** using [sqlite3session_changeset()], then after applying that changeset to
-** database zFrom the contents of the two compatible tables would be
+** using [sqlite3session_changeset()], then after applying that changeset to
+** database zFrom the contents of the two compatible tables would be
** identical.
**
** It an error if database zFrom does not exist or does not contain the
** required compatible table.
**
-** If the operation successful, SQLITE_OK is returned. Otherwise, an SQLite
+** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
-** may be set to point to a buffer containing an English language error
+** may be set to point to a buffer containing an English language error
** message. It is the responsibility of the caller to free this buffer using
** sqlite3_free().
*/
@@ -10882,19 +11691,19 @@ SQLITE_API int sqlite3session_diff(
** The differences between a patchset and a changeset are that:
**
** <ul>
-** <li> DELETE records consist of the primary key fields only. The
+** <li> DELETE records consist of the primary key fields only. The
** original values of other fields are omitted.
-** <li> The original values of any modified fields are omitted from
+** <li> The original values of any modified fields are omitted from
** UPDATE records.
** </ul>
**
-** A patchset blob may be used with up to date versions of all
-** sqlite3changeset_xxx API functions except for sqlite3changeset_invert(),
+** A patchset blob may be used with up to date versions of all
+** sqlite3changeset_xxx API functions except for sqlite3changeset_invert(),
** which returns SQLITE_CORRUPT if it is passed a patchset. Similarly,
** attempting to use a patchset blob with old versions of the
-** sqlite3changeset_xxx APIs also provokes an SQLITE_CORRUPT error.
+** sqlite3changeset_xxx APIs also provokes an SQLITE_CORRUPT error.
**
-** Because the non-primary key "old.*" fields are omitted, no
+** Because the non-primary key "old.*" fields are omitted, no
** SQLITE_CHANGESET_DATA conflicts can be detected or reported if a patchset
** is passed to the sqlite3changeset_apply() API. Other conflict types work
** in the same way as for changesets.
@@ -10913,22 +11722,30 @@ SQLITE_API int sqlite3session_patchset(
/*
** CAPI3REF: Test if a changeset has recorded any changes.
**
-** Return non-zero if no changes to attached tables have been recorded by
-** the session object passed as the first argument. Otherwise, if one or
+** Return non-zero if no changes to attached tables have been recorded by
+** the session object passed as the first argument. Otherwise, if one or
** more changes have been recorded, return zero.
**
** Even if this function returns zero, it is possible that calling
** [sqlite3session_changeset()] on the session handle may still return a
-** changeset that contains no changes. This can happen when a row in
-** an attached table is modified and then later on the original values
+** changeset that contains no changes. This can happen when a row in
+** an attached table is modified and then later on the original values
** are restored. However, if this function returns non-zero, then it is
-** guaranteed that a call to sqlite3session_changeset() will return a
+** guaranteed that a call to sqlite3session_changeset() will return a
** changeset containing zero changes.
*/
SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);
/*
-** CAPI3REF: Create An Iterator To Traverse A Changeset
+** CAPI3REF: Query for the amount of heap memory used by a session object.
+**
+** This API returns the total amount of heap memory in bytes currently
+** used by the session object passed as the only argument.
+*/
+SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession);
+
+/*
+** CAPI3REF: Create An Iterator To Traverse A Changeset
** CONSTRUCTOR: sqlite3_changeset_iter
**
** Create an iterator used to iterate through the contents of a changeset.
@@ -10936,7 +11753,7 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);
** is returned. Otherwise, if an error occurs, *pp is set to zero and an
** SQLite error code is returned.
**
-** The following functions can be used to advance and query a changeset
+** The following functions can be used to advance and query a changeset
** iterator created by this function:
**
** <ul>
@@ -10953,12 +11770,12 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);
**
** Assuming the changeset blob was created by one of the
** [sqlite3session_changeset()], [sqlite3changeset_concat()] or
-** [sqlite3changeset_invert()] functions, all changes within the changeset
-** that apply to a single table are grouped together. This means that when
-** an application iterates through a changeset using an iterator created by
-** this function, all changes that relate to a single table are visited
-** consecutively. There is no chance that the iterator will visit a change
-** the applies to table X, then one for table Y, and then later on visit
+** [sqlite3changeset_invert()] functions, all changes within the changeset
+** that apply to a single table are grouped together. This means that when
+** an application iterates through a changeset using an iterator created by
+** this function, all changes that relate to a single table are visited
+** consecutively. There is no chance that the iterator will visit a change
+** the applies to table X, then one for table Y, and then later on visit
** another change for table X.
**
** The behavior of sqlite3changeset_start_v2() and its streaming equivalent
@@ -10998,7 +11815,7 @@ SQLITE_API int sqlite3changeset_start_v2(
** CAPI3REF: Advance A Changeset Iterator
** METHOD: sqlite3_changeset_iter
**
-** This function may only be used with iterators created by function
+** This function may only be used with iterators created by the function
** [sqlite3changeset_start()]. If it is called on an iterator passed to
** a conflict-handler callback by [sqlite3changeset_apply()], SQLITE_MISUSE
** is returned and the call has no effect.
@@ -11009,12 +11826,12 @@ SQLITE_API int sqlite3changeset_start_v2(
** point to the first change in the changeset. Each subsequent call advances
** the iterator to point to the next change in the changeset (if any). If
** no error occurs and the iterator points to a valid change after a call
-** to sqlite3changeset_next() has advanced it, SQLITE_ROW is returned.
+** to sqlite3changeset_next() has advanced it, SQLITE_ROW is returned.
** Otherwise, if all changes in the changeset have already been visited,
** SQLITE_DONE is returned.
**
-** If an error occurs, an SQLite error code is returned. Possible error
-** codes include SQLITE_CORRUPT (if the changeset buffer is corrupt) or
+** If an error occurs, an SQLite error code is returned. Possible error
+** codes include SQLITE_CORRUPT (if the changeset buffer is corrupt) or
** SQLITE_NOMEM.
*/
SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
@@ -11029,18 +11846,23 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
** is not the case, this function returns [SQLITE_MISUSE].
**
-** If argument pzTab is not NULL, then *pzTab is set to point to a
-** nul-terminated utf-8 encoded string containing the name of the table
-** affected by the current change. The buffer remains valid until either
-** sqlite3changeset_next() is called on the iterator or until the
-** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
-** set to the number of columns in the table affected by the change. If
-** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change
+** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three
+** outputs are set through these pointers:
+**
+** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE],
+** depending on the type of change that the iterator currently points to;
+**
+** *pnCol is set to the number of columns in the table affected by the change; and
+**
+** *pzTab is set to point to a nul-terminated utf-8 encoded string containing
+** the name of the table affected by the current change. The buffer remains
+** valid until either sqlite3changeset_next() is called on the iterator
+** or until the conflict-handler function returns.
+**
+** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
** is an indirect change, or false (0) otherwise. See the documentation for
** [sqlite3session_indirect()] for a description of direct and indirect
-** changes. Finally, if pOp is not NULL, then *pOp is set to one of
-** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the
-** type of change that the iterator currently points to.
+** changes.
**
** If no error occurs, SQLITE_OK is returned. If an error does occur, an
** SQLite error code is returned. The values of the output variables may not
@@ -11093,7 +11915,7 @@ SQLITE_API int sqlite3changeset_pk(
** The pIter argument passed to this function may either be an iterator
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
** created by [sqlite3changeset_start()]. In the latter case, the most recent
-** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
+** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
** Furthermore, it may only be called if the type of change that the iterator
** currently points to is either [SQLITE_DELETE] or [SQLITE_UPDATE]. Otherwise,
** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL.
@@ -11103,9 +11925,9 @@ SQLITE_API int sqlite3changeset_pk(
** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
**
** If successful, this function sets *ppValue to point to a protected
-** sqlite3_value object containing the iVal'th value from the vector of
+** sqlite3_value object containing the iVal'th value from the vector of
** original row values stored as part of the UPDATE or DELETE change and
-** returns SQLITE_OK. The name of the function comes from the fact that this
+** returns SQLITE_OK. The name of the function comes from the fact that this
** is similar to the "old.*" columns available to update or delete triggers.
**
** If some other error occurs (e.g. an OOM condition), an SQLite error code
@@ -11124,7 +11946,7 @@ SQLITE_API int sqlite3changeset_old(
** The pIter argument passed to this function may either be an iterator
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
** created by [sqlite3changeset_start()]. In the latter case, the most recent
-** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
+** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
** Furthermore, it may only be called if the type of change that the iterator
** currently points to is either [SQLITE_UPDATE] or [SQLITE_INSERT]. Otherwise,
** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL.
@@ -11134,12 +11956,12 @@ SQLITE_API int sqlite3changeset_old(
** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
**
** If successful, this function sets *ppValue to point to a protected
-** sqlite3_value object containing the iVal'th value from the vector of
+** sqlite3_value object containing the iVal'th value from the vector of
** new row values stored as part of the UPDATE or INSERT change and
** returns SQLITE_OK. If the change is an UPDATE and does not include
-** a new value for the requested column, *ppValue is set to NULL and
-** SQLITE_OK returned. The name of the function comes from the fact that
-** this is similar to the "new.*" columns available to update or delete
+** a new value for the requested column, *ppValue is set to NULL and
+** SQLITE_OK returned. The name of the function comes from the fact that
+** this is similar to the "new.*" columns available to update or delete
** triggers.
**
** If some other error occurs (e.g. an OOM condition), an SQLite error code
@@ -11166,7 +11988,7 @@ SQLITE_API int sqlite3changeset_new(
** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
**
** If successful, this function sets *ppValue to point to a protected
-** sqlite3_value object containing the iVal'th value from the
+** sqlite3_value object containing the iVal'th value from the
** "conflicting row" associated with the current conflict-handler callback
** and returns SQLITE_OK.
**
@@ -11210,7 +12032,7 @@ SQLITE_API int sqlite3changeset_fk_conflicts(
** call has no effect.
**
** If an error was encountered within a call to an sqlite3changeset_xxx()
-** function (for example an [SQLITE_CORRUPT] in [sqlite3changeset_next()] or an
+** function (for example an [SQLITE_CORRUPT] in [sqlite3changeset_next()] or an
** [SQLITE_NOMEM] in [sqlite3changeset_new()]) then an error code corresponding
** to that error is returned by this function. Otherwise, SQLITE_OK is
** returned. This is to allow the following pattern (pseudo-code):
@@ -11222,7 +12044,7 @@ SQLITE_API int sqlite3changeset_fk_conflicts(
** }
** rc = sqlite3changeset_finalize();
** if( rc!=SQLITE_OK ){
-** // An error has occurred
+** // An error has occurred
** }
** </pre>
*/
@@ -11250,7 +12072,7 @@ SQLITE_API int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);
** zeroed and an SQLite error code returned.
**
** It is the responsibility of the caller to eventually call sqlite3_free()
-** on the *ppOut pointer to free the buffer allocation following a successful
+** on the *ppOut pointer to free the buffer allocation following a successful
** call to this function.
**
** WARNING/TODO: This function currently assumes that the input is a valid
@@ -11264,11 +12086,11 @@ SQLITE_API int sqlite3changeset_invert(
/*
** CAPI3REF: Concatenate Two Changeset Objects
**
-** This function is used to concatenate two changesets, A and B, into a
+** This function is used to concatenate two changesets, A and B, into a
** single changeset. The result is a changeset equivalent to applying
-** changeset A followed by changeset B.
+** changeset A followed by changeset B.
**
-** This function combines the two input changesets using an
+** This function combines the two input changesets using an
** sqlite3_changegroup object. Calling it produces similar results as the
** following code fragment:
**
@@ -11298,9 +12120,21 @@ SQLITE_API int sqlite3changeset_concat(
/*
+** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
+*/
+SQLITE_API int sqlite3changeset_upgrade(
+ sqlite3 *db,
+ const char *zDb,
+ int nIn, const void *pIn, /* Input changeset */
+ int *pnOut, void **ppOut /* OUT: Inverse of input */
+);
+
+
+
+/*
** CAPI3REF: Changegroup Handle
**
-** A changegroup is an object used to combine two or more
+** A changegroup is an object used to combine two or more
** [changesets] or [patchsets]
*/
typedef struct sqlite3_changegroup sqlite3_changegroup;
@@ -11316,7 +12150,7 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
**
** If successful, this function returns SQLITE_OK and populates (*pp) with
** a pointer to a new sqlite3_changegroup object before returning. The caller
-** should eventually free the returned object using a call to
+** should eventually free the returned object using a call to
** sqlite3changegroup_delete(). If an error occurs, an SQLite error code
** (i.e. SQLITE_NOMEM) is returned and *pp is set to NULL.
**
@@ -11328,7 +12162,7 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
** <li> Zero or more changesets (or patchsets) are added to the object
** by calling sqlite3changegroup_add().
**
-** <li> The result of combining all input changesets together is obtained
+** <li> The result of combining all input changesets together is obtained
** by the application via a call to sqlite3changegroup_output().
**
** <li> The object is deleted using a call to sqlite3changegroup_delete().
@@ -11337,18 +12171,50 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
** Any number of calls to add() and output() may be made between the calls to
** new() and delete(), and in any order.
**
-** As well as the regular sqlite3changegroup_add() and
+** As well as the regular sqlite3changegroup_add() and
** sqlite3changegroup_output() functions, also available are the streaming
** versions sqlite3changegroup_add_strm() and sqlite3changegroup_output_strm().
*/
SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
/*
+** CAPI3REF: Add a Schema to a Changegroup
+** METHOD: sqlite3_changegroup_schema
+**
+** This method may be used to optionally enforce the rule that the changesets
+** added to the changegroup handle must match the schema of database zDb
+** ("main", "temp", or the name of an attached database). If
+** sqlite3changegroup_add() is called to add a changeset that is not compatible
+** with the configured schema, SQLITE_SCHEMA is returned and the changegroup
+** object is left in an undefined state.
+**
+** A changeset schema is considered compatible with the database schema in
+** the same way as for sqlite3changeset_apply(). Specifically, for each
+** table in the changeset, there exists a database table with:
+**
+** <ul>
+** <li> The name identified by the changeset, and
+** <li> at least as many columns as recorded in the changeset, and
+** <li> the primary key columns in the same position as recorded in
+** the changeset.
+** </ul>
+**
+** The output of the changegroup object always has the same schema as the
+** database nominated using this function. In cases where changesets passed
+** to sqlite3changegroup_add() have fewer columns than the corresponding table
+** in the database schema, these are filled in using the default column
+** values from the database schema. This makes it possible to combined
+** changesets that have different numbers of columns for a single table
+** within a changegroup, provided that they are otherwise compatible.
+*/
+SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);
+
+/*
** CAPI3REF: Add A Changeset To A Changegroup
** METHOD: sqlite3_changegroup
**
** Add all changes within the changeset (or patchset) in buffer pData (size
-** nData bytes) to the changegroup.
+** nData bytes) to the changegroup.
**
** If the buffer contains a patchset, then all prior calls to this function
** on the same changegroup object must also have specified patchsets. Or, if
@@ -11375,7 +12241,7 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
** changeset was recorded immediately after the changesets already
** added to the changegroup.
** <tr><td>INSERT <td>UPDATE <td>
-** The INSERT change remains in the changegroup. The values in the
+** The INSERT change remains in the changegroup. The values in the
** INSERT change are modified as if the row was inserted by the
** existing change and then updated according to the new change.
** <tr><td>INSERT <td>DELETE <td>
@@ -11386,17 +12252,17 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
** changeset was recorded immediately after the changesets already
** added to the changegroup.
** <tr><td>UPDATE <td>UPDATE <td>
-** The existing UPDATE remains within the changegroup. It is amended
-** so that the accompanying values are as if the row was updated once
+** The existing UPDATE remains within the changegroup. It is amended
+** so that the accompanying values are as if the row was updated once
** by the existing change and then again by the new change.
** <tr><td>UPDATE <td>DELETE <td>
** The existing UPDATE is replaced by the new DELETE within the
** changegroup.
** <tr><td>DELETE <td>INSERT <td>
** If one or more of the column values in the row inserted by the
-** new change differ from those in the row deleted by the existing
+** new change differ from those in the row deleted by the existing
** change, the existing DELETE is replaced by an UPDATE within the
-** changegroup. Otherwise, if the inserted row is exactly the same
+** changegroup. Otherwise, if the inserted row is exactly the same
** as the deleted row, the existing DELETE is simply discarded.
** <tr><td>DELETE <td>UPDATE <td>
** The new change is ignored. This case does not occur if the new
@@ -11411,13 +12277,18 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
** If the new changeset contains changes to a table that is already present
** in the changegroup, then the number of columns and the position of the
** primary key columns for the table must be consistent. If this is not the
-** case, this function fails with SQLITE_SCHEMA. If the input changeset
-** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
-** returned. Or, if an out-of-memory condition occurs during processing, this
-** function returns SQLITE_NOMEM. In all cases, if an error occurs the
-** final contents of the changegroup is undefined.
+** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup
+** object has been configured with a database schema using the
+** sqlite3changegroup_schema() API, then it is possible to combine changesets
+** with different numbers of columns for a single table, provided that
+** they are otherwise compatible.
**
-** If no error occurs, SQLITE_OK is returned.
+** If the input changeset appears to be corrupt and the corruption is
+** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition
+** occurs during processing, this function returns SQLITE_NOMEM.
+**
+** In all cases, if an error occurs the state of the final contents of the
+** changegroup is undefined. If no error occurs, SQLITE_OK is returned.
*/
SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
@@ -11441,7 +12312,7 @@ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pDa
**
** If an error occurs, an SQLite error code is returned and the output
** variables (*pnData) and (*ppData) are set to 0. Otherwise, SQLITE_OK
-** is returned and the output variables are set to the size of and a
+** is returned and the output variables are set to the size of and a
** pointer to the output buffer, respectively. In this case it is the
** responsibility of the caller to eventually free the buffer using a
** call to sqlite3_free().
@@ -11463,7 +12334,7 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
**
** Apply a changeset or patchset to a database. These functions attempt to
** update the "main" database attached to handle db with the changes found in
-** the changeset passed via the second and third arguments.
+** the changeset passed via the second and third arguments.
**
** The fourth argument (xFilter) passed to these functions is the "filter
** callback". If it is not NULL, then for each table affected by at least one
@@ -11474,16 +12345,16 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
** Otherwise, if the return value is non-zero or the xFilter argument to
** is NULL, all changes related to the table are attempted.
**
-** For each table that is not excluded by the filter callback, this function
-** tests that the target database contains a compatible table. A table is
+** For each table that is not excluded by the filter callback, this function
+** tests that the target database contains a compatible table. A table is
** considered compatible if all of the following are true:
**
** <ul>
-** <li> The table has the same name as the name recorded in the
+** <li> The table has the same name as the name recorded in the
** changeset, and
-** <li> The table has at least as many columns as recorded in the
+** <li> The table has at least as many columns as recorded in the
** changeset, and
-** <li> The table has primary key columns in the same position as
+** <li> The table has primary key columns in the same position as
** recorded in the changeset.
** </ul>
**
@@ -11492,11 +12363,11 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
** via the sqlite3_log() mechanism with the error code SQLITE_SCHEMA. At most
** one such warning is issued for each table in the changeset.
**
-** For each change for which there is a compatible table, an attempt is made
-** to modify the table contents according to the UPDATE, INSERT or DELETE
-** change. If a change cannot be applied cleanly, the conflict handler
-** function passed as the fifth argument to sqlite3changeset_apply() may be
-** invoked. A description of exactly when the conflict handler is invoked for
+** For each change for which there is a compatible table, an attempt is made
+** to modify the table contents according to the UPDATE, INSERT or DELETE
+** change. If a change cannot be applied cleanly, the conflict handler
+** function passed as the fifth argument to sqlite3changeset_apply() may be
+** invoked. A description of exactly when the conflict handler is invoked for
** each type of change is below.
**
** Unlike the xFilter argument, xConflict may not be passed NULL. The results
@@ -11504,23 +12375,23 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
** argument are undefined.
**
** Each time the conflict handler function is invoked, it must return one
-** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or
+** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or
** [SQLITE_CHANGESET_REPLACE]. SQLITE_CHANGESET_REPLACE may only be returned
** if the second argument passed to the conflict handler is either
** SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If the conflict-handler
** returns an illegal value, any changes already made are rolled back and
-** the call to sqlite3changeset_apply() returns SQLITE_MISUSE. Different
+** the call to sqlite3changeset_apply() returns SQLITE_MISUSE. Different
** actions are taken by sqlite3changeset_apply() depending on the value
** returned by each invocation of the conflict-handler function. Refer to
-** the documentation for the three
+** the documentation for the three
** [SQLITE_CHANGESET_OMIT|available return values] for details.
**
** <dl>
** <dt>DELETE Changes<dd>
-** For each DELETE change, the function checks if the target database
-** contains a row with the same primary key value (or values) as the
-** original row values stored in the changeset. If it does, and the values
-** stored in all non-primary key columns also match the values stored in
+** For each DELETE change, the function checks if the target database
+** contains a row with the same primary key value (or values) as the
+** original row values stored in the changeset. If it does, and the values
+** stored in all non-primary key columns also match the values stored in
** the changeset the row is deleted from the target database.
**
** If a row with matching primary key values is found, but one or more of
@@ -11549,22 +12420,22 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
** database table, the trailing fields are populated with their default
** values.
**
-** If the attempt to insert the row fails because the database already
+** If the attempt to insert the row fails because the database already
** contains a row with the same primary key values, the conflict handler
-** function is invoked with the second argument set to
+** function is invoked with the second argument set to
** [SQLITE_CHANGESET_CONFLICT].
**
** If the attempt to insert the row fails because of some other constraint
-** violation (e.g. NOT NULL or UNIQUE), the conflict handler function is
+** violation (e.g. NOT NULL or UNIQUE), the conflict handler function is
** invoked with the second argument set to [SQLITE_CHANGESET_CONSTRAINT].
-** This includes the case where the INSERT operation is re-attempted because
-** an earlier call to the conflict handler function returned
+** This includes the case where the INSERT operation is re-attempted because
+** an earlier call to the conflict handler function returned
** [SQLITE_CHANGESET_REPLACE].
**
** <dt>UPDATE Changes<dd>
-** For each UPDATE change, the function checks if the target database
-** contains a row with the same primary key value (or values) as the
-** original row values stored in the changeset. If it does, and the values
+** For each UPDATE change, the function checks if the target database
+** contains a row with the same primary key value (or values) as the
+** original row values stored in the changeset. If it does, and the values
** stored in all modified non-primary key columns also match the values
** stored in the changeset the row is updated within the target database.
**
@@ -11580,28 +12451,28 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND]
** passed as the second argument.
**
-** If the UPDATE operation is attempted, but SQLite returns
-** SQLITE_CONSTRAINT, the conflict-handler function is invoked with
+** If the UPDATE operation is attempted, but SQLite returns
+** SQLITE_CONSTRAINT, the conflict-handler function is invoked with
** [SQLITE_CHANGESET_CONSTRAINT] passed as the second argument.
-** This includes the case where the UPDATE operation is attempted after
+** This includes the case where the UPDATE operation is attempted after
** an earlier call to the conflict handler function returned
-** [SQLITE_CHANGESET_REPLACE].
+** [SQLITE_CHANGESET_REPLACE].
** </dl>
**
** It is safe to execute SQL statements, including those that write to the
** table that the callback related to, from within the xConflict callback.
-** This can be used to further customize the applications conflict
+** This can be used to further customize the application's conflict
** resolution strategy.
**
** All changes made by these functions are enclosed in a savepoint transaction.
** If any other error (aside from a constraint failure when attempting to
** write to the target database) occurs, then the savepoint transaction is
-** rolled back, restoring the target database to its original state, and an
+** rolled back, restoring the target database to its original state, and an
** SQLite error code returned.
**
** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
-** may set (*ppRebase) to point to a "rebase" that may be used with the
+** may set (*ppRebase) to point to a "rebase" that may be used with the
** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
** is set to the size of the buffer in bytes. It is the responsibility of the
** caller to eventually free any such buffer using sqlite3_free(). The buffer
@@ -11662,18 +12533,39 @@ SQLITE_API int sqlite3changeset_apply_v2(
** SAVEPOINT is committed if the changeset or patchset is successfully
** applied, or rolled back if an error occurs. Specifying this flag
** causes the sessions module to omit this savepoint. In this case, if the
-** caller has an open transaction or savepoint when apply_v2() is called,
+** caller has an open transaction or savepoint when apply_v2() is called,
** it may revert the partially applied changeset by rolling it back.
**
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
** Invert the changeset before applying it. This is equivalent to inverting
** a changeset using sqlite3changeset_invert() before applying it. It is
** an error to specify this flag with a patchset.
+**
+** <dt>SQLITE_CHANGESETAPPLY_IGNORENOOP <dd>
+** Do not invoke the conflict handler callback for any changes that
+** would not actually modify the database even if they were applied.
+** Specifically, this means that the conflict handler is not invoked
+** for:
+** <ul>
+** <li>a delete change if the row being deleted cannot be found,
+** <li>an update change if the modified fields are already set to
+** their new values in the conflicting row, or
+** <li>an insert change if all fields of the conflicting row match
+** the row being inserted.
+** </ul>
+**
+** <dt>SQLITE_CHANGESETAPPLY_FKNOACTION <dd>
+** If this flag it set, then all foreign key constraints in the target
+** database behave as if they were declared with "ON UPDATE NO ACTION ON
+** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL
+** or SET DEFAULT.
*/
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
+#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
+#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
-/*
+/*
** CAPI3REF: Constants Passed To The Conflict Handler
**
** Values that may be passed as the second argument to a conflict-handler.
@@ -11682,32 +12574,32 @@ SQLITE_API int sqlite3changeset_apply_v2(
** <dt>SQLITE_CHANGESET_DATA<dd>
** The conflict handler is invoked with CHANGESET_DATA as the second argument
** when processing a DELETE or UPDATE change if a row with the required
-** PRIMARY KEY fields is present in the database, but one or more other
-** (non primary-key) fields modified by the update do not contain the
+** PRIMARY KEY fields is present in the database, but one or more other
+** (non primary-key) fields modified by the update do not contain the
** expected "before" values.
-**
+**
** The conflicting row, in this case, is the database row with the matching
** primary key.
-**
+**
** <dt>SQLITE_CHANGESET_NOTFOUND<dd>
** The conflict handler is invoked with CHANGESET_NOTFOUND as the second
** argument when processing a DELETE or UPDATE change if a row with the
** required PRIMARY KEY fields is not present in the database.
-**
+**
** There is no conflicting row in this case. The results of invoking the
** sqlite3changeset_conflict() API are undefined.
-**
+**
** <dt>SQLITE_CHANGESET_CONFLICT<dd>
** CHANGESET_CONFLICT is passed as the second argument to the conflict
-** handler while processing an INSERT change if the operation would result
+** handler while processing an INSERT change if the operation would result
** in duplicate primary key values.
-**
+**
** The conflicting row in this case is the database row with the matching
** primary key.
**
** <dt>SQLITE_CHANGESET_FOREIGN_KEY<dd>
** If foreign key handling is enabled, and applying a changeset leaves the
-** database in a state containing foreign key violations, the conflict
+** database in a state containing foreign key violations, the conflict
** handler is invoked with CHANGESET_FOREIGN_KEY as the second argument
** exactly once before the changeset is committed. If the conflict handler
** returns CHANGESET_OMIT, the changes, including those that caused the
@@ -11717,12 +12609,12 @@ SQLITE_API int sqlite3changeset_apply_v2(
** No current or conflicting row information is provided. The only function
** it is possible to call on the supplied sqlite3_changeset_iter handle
** is sqlite3changeset_fk_conflicts().
-**
+**
** <dt>SQLITE_CHANGESET_CONSTRAINT<dd>
-** If any other constraint violation occurs while applying a change (i.e.
-** a UNIQUE, CHECK or NOT NULL constraint), the conflict handler is
+** If any other constraint violation occurs while applying a change (i.e.
+** a UNIQUE, CHECK or NOT NULL constraint), the conflict handler is
** invoked with CHANGESET_CONSTRAINT as the second argument.
-**
+**
** There is no conflicting row in this case. The results of invoking the
** sqlite3changeset_conflict() API are undefined.
**
@@ -11734,7 +12626,7 @@ SQLITE_API int sqlite3changeset_apply_v2(
#define SQLITE_CHANGESET_CONSTRAINT 4
#define SQLITE_CHANGESET_FOREIGN_KEY 5
-/*
+/*
** CAPI3REF: Constants Returned By The Conflict Handler
**
** A conflict handler callback must return one of the following three values.
@@ -11742,13 +12634,13 @@ SQLITE_API int sqlite3changeset_apply_v2(
** <dl>
** <dt>SQLITE_CHANGESET_OMIT<dd>
** If a conflict handler returns this value no special action is taken. The
-** change that caused the conflict is not applied. The session module
+** change that caused the conflict is not applied. The session module
** continues to the next change in the changeset.
**
** <dt>SQLITE_CHANGESET_REPLACE<dd>
** This value may only be returned if the second argument to the conflict
** handler was SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If this
-** is not the case, any changes applied so far are rolled back and the
+** is not the case, any changes applied so far are rolled back and the
** call to sqlite3changeset_apply() returns SQLITE_MISUSE.
**
** If CHANGESET_REPLACE is returned by an SQLITE_CHANGESET_DATA conflict
@@ -11761,7 +12653,7 @@ SQLITE_API int sqlite3changeset_apply_v2(
** the original row is restored to the database before continuing.
**
** <dt>SQLITE_CHANGESET_ABORT<dd>
-** If this value is returned, any changes applied so far are rolled back
+** If this value is returned, any changes applied so far are rolled back
** and the call to sqlite3changeset_apply() returns SQLITE_ABORT.
** </dl>
*/
@@ -11769,20 +12661,20 @@ SQLITE_API int sqlite3changeset_apply_v2(
#define SQLITE_CHANGESET_REPLACE 1
#define SQLITE_CHANGESET_ABORT 2
-/*
+/*
** CAPI3REF: Rebasing changesets
** EXPERIMENTAL
**
** Suppose there is a site hosting a database in state S0. And that
** modifications are made that move that database to state S1 and a
** changeset recorded (the "local" changeset). Then, a changeset based
-** on S0 is received from another site (the "remote" changeset) and
-** applied to the database. The database is then in state
+** on S0 is received from another site (the "remote" changeset) and
+** applied to the database. The database is then in state
** (S1+"remote"), where the exact state depends on any conflict
** resolution decisions (OMIT or REPLACE) made while applying "remote".
-** Rebasing a changeset is to update it to take those conflict
+** Rebasing a changeset is to update it to take those conflict
** resolution decisions into account, so that the same conflicts
-** do not have to be resolved elsewhere in the network.
+** do not have to be resolved elsewhere in the network.
**
** For example, if both the local and remote changesets contain an
** INSERT of the same key on "CREATE TABLE t1(a PRIMARY KEY, b)":
@@ -11801,7 +12693,7 @@ SQLITE_API int sqlite3changeset_apply_v2(
**
** <dl>
** <dt>Local INSERT<dd>
-** This may only conflict with a remote INSERT. If the conflict
+** This may only conflict with a remote INSERT. If the conflict
** resolution was OMIT, then add an UPDATE change to the rebased
** changeset. Or, if the conflict resolution was REPLACE, add
** nothing to the rebased changeset.
@@ -11825,12 +12717,12 @@ SQLITE_API int sqlite3changeset_apply_v2(
** the old.* values are rebased using the new.* values in the remote
** change. Or, if the resolution is REPLACE, then the change is copied
** into the rebased changeset with updates to columns also updated by
-** the conflicting remote UPDATE removed. If this means no columns would
+** the conflicting remote UPDATE removed. If this means no columns would
** be updated, the change is omitted.
** </dl>
**
-** A local change may be rebased against multiple remote changes
-** simultaneously. If a single key is modified by multiple remote
+** A local change may be rebased against multiple remote changes
+** simultaneously. If a single key is modified by multiple remote
** changesets, they are combined as follows before the local changeset
** is rebased:
**
@@ -11843,10 +12735,10 @@ SQLITE_API int sqlite3changeset_apply_v2(
** of the OMIT resolutions.
** </ul>
**
-** Note that conflict resolutions from multiple remote changesets are
-** combined on a per-field basis, not per-row. This means that in the
-** case of multiple remote UPDATE operations, some fields of a single
-** local change may be rebased for REPLACE while others are rebased for
+** Note that conflict resolutions from multiple remote changesets are
+** combined on a per-field basis, not per-row. This means that in the
+** case of multiple remote UPDATE operations, some fields of a single
+** local change may be rebased for REPLACE while others are rebased for
** OMIT.
**
** In order to rebase a local changeset, the remote changeset must first
@@ -11854,7 +12746,7 @@ SQLITE_API int sqlite3changeset_apply_v2(
** the buffer of rebase information captured. Then:
**
** <ol>
-** <li> An sqlite3_rebaser object is created by calling
+** <li> An sqlite3_rebaser object is created by calling
** sqlite3rebaser_create().
** <li> The new object is configured with the rebase buffer obtained from
** sqlite3changeset_apply_v2() by calling sqlite3rebaser_configure().
@@ -11875,8 +12767,8 @@ typedef struct sqlite3_rebaser sqlite3_rebaser;
**
** Allocate a new changeset rebaser object. If successful, set (*ppNew) to
** point to the new object and return SQLITE_OK. Otherwise, if an error
-** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew)
-** to NULL.
+** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew)
+** to NULL.
*/
SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew);
@@ -11890,9 +12782,9 @@ SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew);
** sqlite3changeset_apply_v2().
*/
SQLITE_API int sqlite3rebaser_configure(
- sqlite3_rebaser*,
+ sqlite3_rebaser*,
int nRebase, const void *pRebase
-);
+);
/*
** CAPI3REF: Rebase a changeset
@@ -11900,9 +12792,9 @@ SQLITE_API int sqlite3rebaser_configure(
**
** Argument pIn must point to a buffer containing a changeset nIn bytes
** in size. This function allocates and populates a buffer with a copy
-** of the changeset rebased rebased according to the configuration of the
+** of the changeset rebased according to the configuration of the
** rebaser object passed as the first argument. If successful, (*ppOut)
-** is set to point to the new buffer containing the rebased changset and
+** is set to point to the new buffer containing the rebased changeset and
** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
** responsibility of the caller to eventually free the new buffer using
** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut)
@@ -11910,8 +12802,8 @@ SQLITE_API int sqlite3rebaser_configure(
*/
SQLITE_API int sqlite3rebaser_rebase(
sqlite3_rebaser*,
- int nIn, const void *pIn,
- int *pnOut, void **ppOut
+ int nIn, const void *pIn,
+ int *pnOut, void **ppOut
);
/*
@@ -11922,30 +12814,30 @@ SQLITE_API int sqlite3rebaser_rebase(
** should be one call to this function for each successful invocation
** of sqlite3rebaser_create().
*/
-SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
+SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
/*
** CAPI3REF: Streaming Versions of API functions.
**
-** The six streaming API xxx_strm() functions serve similar purposes to the
+** The six streaming API xxx_strm() functions serve similar purposes to the
** corresponding non-streaming API functions:
**
** <table border=1 style="margin-left:8ex;margin-right:8ex">
** <tr><th>Streaming function<th>Non-streaming equivalent</th>
-** <tr><td>sqlite3changeset_apply_strm<td>[sqlite3changeset_apply]
-** <tr><td>sqlite3changeset_apply_strm_v2<td>[sqlite3changeset_apply_v2]
-** <tr><td>sqlite3changeset_concat_strm<td>[sqlite3changeset_concat]
-** <tr><td>sqlite3changeset_invert_strm<td>[sqlite3changeset_invert]
-** <tr><td>sqlite3changeset_start_strm<td>[sqlite3changeset_start]
-** <tr><td>sqlite3session_changeset_strm<td>[sqlite3session_changeset]
-** <tr><td>sqlite3session_patchset_strm<td>[sqlite3session_patchset]
+** <tr><td>sqlite3changeset_apply_strm<td>[sqlite3changeset_apply]
+** <tr><td>sqlite3changeset_apply_strm_v2<td>[sqlite3changeset_apply_v2]
+** <tr><td>sqlite3changeset_concat_strm<td>[sqlite3changeset_concat]
+** <tr><td>sqlite3changeset_invert_strm<td>[sqlite3changeset_invert]
+** <tr><td>sqlite3changeset_start_strm<td>[sqlite3changeset_start]
+** <tr><td>sqlite3session_changeset_strm<td>[sqlite3session_changeset]
+** <tr><td>sqlite3session_patchset_strm<td>[sqlite3session_patchset]
** </table>
**
** Non-streaming functions that accept changesets (or patchsets) as input
-** require that the entire changeset be stored in a single buffer in memory.
-** Similarly, those that return a changeset or patchset do so by returning
-** a pointer to a single large buffer allocated using sqlite3_malloc().
-** Normally this is convenient. However, if an application running in a
+** require that the entire changeset be stored in a single buffer in memory.
+** Similarly, those that return a changeset or patchset do so by returning
+** a pointer to a single large buffer allocated using sqlite3_malloc().
+** Normally this is convenient. However, if an application running in a
** low-memory environment is required to handle very large changesets, the
** large contiguous memory allocations required can become onerous.
**
@@ -11967,12 +12859,12 @@ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
** </pre>
**
** Each time the xInput callback is invoked by the sessions module, the first
-** argument passed is a copy of the supplied pIn context pointer. The second
-** argument, pData, points to a buffer (*pnData) bytes in size. Assuming no
-** error occurs the xInput method should copy up to (*pnData) bytes of data
-** into the buffer and set (*pnData) to the actual number of bytes copied
-** before returning SQLITE_OK. If the input is completely exhausted, (*pnData)
-** should be set to zero to indicate this. Or, if an error occurs, an SQLite
+** argument passed is a copy of the supplied pIn context pointer. The second
+** argument, pData, points to a buffer (*pnData) bytes in size. Assuming no
+** error occurs the xInput method should copy up to (*pnData) bytes of data
+** into the buffer and set (*pnData) to the actual number of bytes copied
+** before returning SQLITE_OK. If the input is completely exhausted, (*pnData)
+** should be set to zero to indicate this. Or, if an error occurs, an SQLite
** error code should be returned. In all cases, if an xInput callback returns
** an error, all processing is abandoned and the streaming API function
** returns a copy of the error code to the caller.
@@ -11980,7 +12872,7 @@ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
** In the case of sqlite3changeset_start_strm(), the xInput callback may be
** invoked by the sessions module at any point during the lifetime of the
** iterator. If such an xInput callback returns an error, the iterator enters
-** an error state, whereby all subsequent calls to iterator functions
+** an error state, whereby all subsequent calls to iterator functions
** immediately fail with the same error code as returned by xInput.
**
** Similarly, streaming API functions that return changesets (or patchsets)
@@ -12010,7 +12902,7 @@ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
** is immediately abandoned and the streaming API function returns a copy
** of the xOutput error code to the application.
**
-** The sessions module never invokes an xOutput callback with the third
+** The sessions module never invokes an xOutput callback with the third
** parameter set to a value less than or equal to zero. Other than this,
** no guarantees are made as to the size of the chunks of data returned.
*/
@@ -12081,12 +12973,12 @@ SQLITE_API int sqlite3session_patchset_strm(
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
);
-SQLITE_API int sqlite3changegroup_add_strm(sqlite3_changegroup*,
+SQLITE_API int sqlite3changegroup_add_strm(sqlite3_changegroup*,
int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn
);
SQLITE_API int sqlite3changegroup_output_strm(sqlite3_changegroup*,
- int (*xOutput)(void *pOut, const void *pData, int nData),
+ int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
);
SQLITE_API int sqlite3rebaser_rebase_strm(
@@ -12101,16 +12993,16 @@ SQLITE_API int sqlite3rebaser_rebase_strm(
** CAPI3REF: Configure global parameters
**
** The sqlite3session_config() interface is used to make global configuration
-** changes to the sessions module in order to tune it to the specific needs
+** changes to the sessions module in order to tune it to the specific needs
** of the application.
**
** The sqlite3session_config() interface is not threadsafe. If it is invoked
** while any other thread is inside any other sessions method then the
** results are undefined. Furthermore, if it is invoked after any sessions
-** related objects have been created, the results are also undefined.
+** related objects have been created, the results are also undefined.
**
** The first argument to the sqlite3session_config() function must be one
-** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The
+** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The
** interpretation of the (void*) value passed as the second parameter and
** the effect of calling this function depends on the value of the first
** parameter.
@@ -12160,7 +13052,7 @@ SQLITE_API int sqlite3session_config(int op, void *pArg);
**
******************************************************************************
**
-** Interfaces to extend FTS5. Using the interfaces defined in this file,
+** Interfaces to extend FTS5. Using the interfaces defined in this file,
** FTS5 may be extended with:
**
** * custom tokenizers, and
@@ -12204,19 +13096,19 @@ struct Fts5PhraseIter {
** EXTENSION API FUNCTIONS
**
** xUserData(pFts):
-** Return a copy of the context pointer the extension function was
+** Return a copy of the context pointer the extension function was
** registered with.
**
** xColumnTotalSize(pFts, iCol, pnToken):
** If parameter iCol is less than zero, set output variable *pnToken
** to the total number of tokens in the FTS5 table. Or, if iCol is
** non-negative but less than the number of columns in the table, return
-** the total number of tokens in column iCol, considering all rows in
+** the total number of tokens in column iCol, considering all rows in
** the FTS5 table.
**
** If parameter iCol is greater than or equal to the number of columns
** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
-** an OOM condition or IO error), an appropriate SQLite error code is
+** an OOM condition or IO error), an appropriate SQLite error code is
** returned.
**
** xColumnCount(pFts):
@@ -12230,15 +13122,18 @@ struct Fts5PhraseIter {
**
** If parameter iCol is greater than or equal to the number of columns
** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
-** an OOM condition or IO error), an appropriate SQLite error code is
+** an OOM condition or IO error), an appropriate SQLite error code is
** returned.
**
** This function may be quite inefficient if used with an FTS5 table
** created with the "columnsize=0" option.
**
** xColumnText:
-** This function attempts to retrieve the text of column iCol of the
-** current document. If successful, (*pz) is set to point to a buffer
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of columns in the table, SQLITE_RANGE is returned.
+**
+** Otherwise, this function attempts to retrieve the text of column iCol of
+** the current document. If successful, (*pz) is set to point to a buffer
** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
** if an error occurs, an SQLite error code is returned and the final values
@@ -12248,8 +13143,10 @@ struct Fts5PhraseIter {
** Returns the number of phrases in the current query expression.
**
** xPhraseSize:
-** Returns the number of tokens in phrase iPhrase of the query. Phrases
-** are numbered starting from zero.
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of phrases in the current query, as returned by xPhraseCount,
+** 0 is returned. Otherwise, this function returns the number of tokens in
+** phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
** Set *pnInst to the total number of occurrences of all phrases within
@@ -12257,27 +13154,24 @@ struct Fts5PhraseIter {
** an error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" or "detail=column" option. If the FTS5 table is created
-** with either "detail=none" or "detail=column" and "content=" option
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
** (i.e. if it is a contentless table), then this API always returns 0.
**
** xInst:
** Query for the details of phrase match iIdx within the current row.
** Phrase matches are numbered starting from zero, so the iIdx argument
** should be greater than or equal to zero and smaller than the value
-** output by xInstCount().
+** output by xInstCount(). If iIdx is less than zero or greater than
+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
-** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the
-** first token of the phrase. The exception is if the table was created
-** with the offsets=0 option specified. In this case *piOff is always
-** set to -1.
-**
-** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
-** if an error occurs.
+** first token of the phrase. SQLITE_OK is returned if successful, or an
+** error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" or "detail=column" option.
+** "detail=none" or "detail=column" option.
**
** xRowid:
** Returns the rowid of the current row.
@@ -12293,13 +13187,17 @@ struct Fts5PhraseIter {
**
** with $p set to a phrase equivalent to the phrase iPhrase of the
** current query is executed. Any column filter that applies to
-** phrase iPhrase of the current query is included in $p. For each
-** row visited, the callback function passed as the fourth argument
-** is invoked. The context and API objects passed to the callback
+** phrase iPhrase of the current query is included in $p. For each
+** row visited, the callback function passed as the fourth argument
+** is invoked. The context and API objects passed to the callback
** function may be used to access the properties of each matched row.
-** Invoking Api.xUserData() returns a copy of the pointer passed as
+** Invoking Api.xUserData() returns a copy of the pointer passed as
** the third argument to pUserData.
**
+** If parameter iPhrase is less than zero, or greater than or equal to
+** the number of phrases in the query, as returned by xPhraseCount(),
+** this function returns SQLITE_RANGE.
+**
** If the callback function returns any value other than SQLITE_OK, the
** query is abandoned and the xQueryPhrase function returns immediately.
** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
@@ -12312,14 +13210,14 @@ struct Fts5PhraseIter {
**
** xSetAuxdata(pFts5, pAux, xDelete)
**
-** Save the pointer passed as the second argument as the extension functions
+** Save the pointer passed as the second argument as the extension function's
** "auxiliary data". The pointer may then be retrieved by the current or any
** future invocation of the same fts5 extension function made as part of
-** of the same MATCH query using the xGetAuxdata() API.
+** the same MATCH query using the xGetAuxdata() API.
**
** Each extension function is allocated a single auxiliary data slot for
-** each FTS query (MATCH expression). If the extension function is invoked
-** more than once for a single FTS query, then all invocations share a
+** each FTS query (MATCH expression). If the extension function is invoked
+** more than once for a single FTS query, then all invocations share a
** single auxiliary data context.
**
** If there is already an auxiliary data pointer when this function is
@@ -12330,7 +13228,7 @@ struct Fts5PhraseIter {
** The xDelete callback, if one is specified, is also invoked on the
** auxiliary data pointer after the FTS5 query has finished.
**
-** If an error (e.g. an OOM condition) occurs within this function, an
+** If an error (e.g. an OOM condition) occurs within this function,
** the auxiliary data is set to NULL and an error code returned. If the
** xDelete parameter was not NULL, it is invoked on the auxiliary data
** pointer before returning.
@@ -12338,7 +13236,7 @@ struct Fts5PhraseIter {
**
** xGetAuxdata(pFts5, bClear)
**
-** Returns the current auxiliary data pointer for the fts5 extension
+** Returns the current auxiliary data pointer for the fts5 extension
** function. See the xSetAuxdata() method for details.
**
** If the bClear argument is non-zero, then the auxiliary data is cleared
@@ -12358,7 +13256,7 @@ struct Fts5PhraseIter {
** method, to iterate through all instances of a single query phrase within
** the current row. This is the same information as is accessible via the
** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient
-** to use, this API may be faster under some circumstances. To iterate
+** to use, this API may be faster under some circumstances. To iterate
** through instances of phrase iPhrase, use the following code:
**
** Fts5PhraseIter iter;
@@ -12376,8 +13274,8 @@ struct Fts5PhraseIter {
** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below).
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" or "detail=column" option. If the FTS5 table is created
-** with either "detail=none" or "detail=column" and "content=" option
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
** (i.e. if it is a contentless table), then this API always iterates
** through an empty set (all calls to xPhraseFirst() set iCol to -1).
**
@@ -12401,19 +13299,52 @@ struct Fts5PhraseIter {
** }
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" option. If the FTS5 table is created with either
-** "detail=none" "content=" option (i.e. if it is a contentless table),
-** then this API always iterates through an empty set (all calls to
+** "detail=none" option. If the FTS5 table is created with either
+** "detail=none" "content=" option (i.e. if it is a contentless table),
+** then this API always iterates through an empty set (all calls to
** xPhraseFirstColumn() set iCol to -1).
**
** The information accessed using this API and its companion
** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
** (or xInst/xInstCount). The chief advantage of this API is that it is
** significantly more efficient than those alternatives when used with
-** "detail=column" tables.
+** "detail=column" tables.
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
+**
+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase iPhrase of the current
+** query. Before returning, output parameter *ppToken is set to point
+** to a buffer containing the requested token, and *pnToken to the
+** size of this buffer in bytes.
+**
+** If iPhrase or iToken are less than zero, or if iPhrase is greater than
+** or equal to the number of phrases in the query as reported by
+** xPhraseCount(), or if iToken is equal to or greater than the number of
+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
+ are both zeroed.
+**
+** The output text is not a copy of the query text that specified the
+** token. It is the output of the tokenizer module. For tokendata=1
+** tables, this includes any embedded 0x00 and trailing data.
+**
+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase hit iIdx within the
+** current row. If iIdx is less than zero or greater than or equal to the
+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
+** output variable (*ppToken) is set to point to a buffer containing the
+** matching document token, and (*pnToken) to the size of that buffer in
+** bytes. This API is not available if the specified token matches a
+** prefix query term. In that case both output variables are always set
+** to 0.
+**
+** The output text is not a copy of the document text that was tokenized.
+** It is the output of the tokenizer module. For tokendata=1 tables, this
+** includes any embedded 0x00 and trailing data.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
int iVersion; /* Currently always set to 3 */
@@ -12424,7 +13355,7 @@ struct Fts5ExtensionApi {
int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
- int (*xTokenize)(Fts5Context*,
+ int (*xTokenize)(Fts5Context*,
const char *pText, int nText, /* Text to tokenize */
void *pCtx, /* Context passed to xToken() */
int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
@@ -12451,17 +13382,24 @@ struct Fts5ExtensionApi {
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+
+ /* Below this point are iVersion>=3 only */
+ int (*xQueryToken)(Fts5Context*,
+ int iPhrase, int iToken,
+ const char **ppToken, int *pnToken
+ );
+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
-/*
+/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/
/*************************************************************************
** CUSTOM TOKENIZERS
**
-** Applications may also register custom tokenizer types. A tokenizer
-** is registered by providing fts5 with a populated instance of the
+** Applications may also register custom tokenizer types. A tokenizer
+** is registered by providing fts5 with a populated instance of the
** following structure. All structure methods must be defined, setting
** any member of the fts5_tokenizer struct to NULL leads to undefined
** behaviour. The structure methods are expected to function as follows:
@@ -12472,16 +13410,16 @@ struct Fts5ExtensionApi {
**
** The first argument passed to this function is a copy of the (void*)
** pointer provided by the application when the fts5_tokenizer object
-** was registered with FTS5 (the third argument to xCreateTokenizer()).
+** was registered with FTS5 (the third argument to xCreateTokenizer()).
** The second and third arguments are an array of nul-terminated strings
** containing the tokenizer arguments, if any, specified following the
** tokenizer name as part of the CREATE VIRTUAL TABLE statement used
** to create the FTS5 table.
**
-** The final argument is an output variable. If successful, (*ppOut)
+** The final argument is an output variable. If successful, (*ppOut)
** should be set to point to the new tokenizer handle and SQLITE_OK
** returned. If an error occurs, some value other than SQLITE_OK should
-** be returned. In this case, fts5 assumes that the final value of *ppOut
+** be returned. In this case, fts5 assumes that the final value of *ppOut
** is undefined.
**
** xDelete:
@@ -12490,7 +13428,7 @@ struct Fts5ExtensionApi {
** be invoked exactly once for each successful call to xCreate().
**
** xTokenize:
-** This function is expected to tokenize the nText byte string indicated
+** This function is expected to tokenize the nText byte string indicated
** by argument pText. pText may or may not be nul-terminated. The first
** argument passed to this function is a pointer to an Fts5Tokenizer object
** returned by an earlier call to xCreate().
@@ -12504,8 +13442,8 @@ struct Fts5ExtensionApi {
** determine the set of tokens to add to (or delete from) the
** FTS index.
**
-** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
-** against the FTS index. The tokenizer is being called to tokenize
+** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
+** against the FTS index. The tokenizer is being called to tokenize
** a bareword or quoted string specified as part of the query.
**
** <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as
@@ -12513,10 +13451,10 @@ struct Fts5ExtensionApi {
** followed by a "*" character, indicating that the last token
** returned by the tokenizer will be treated as a token prefix.
**
-** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
+** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
** satisfy an fts5_api.xTokenize() request made by an auxiliary
** function. Or an fts5_api.xColumnSize() request made by the same
-** on a columnsize=0 database.
+** on a columnsize=0 database.
** </ul>
**
** For each token in the input string, the supplied callback xToken() must
@@ -12528,10 +13466,10 @@ struct Fts5ExtensionApi {
** which the token is derived within the input.
**
** The second argument passed to the xToken() callback ("tflags") should
-** normally be set to 0. The exception is if the tokenizer supports
+** normally be set to 0. The exception is if the tokenizer supports
** synonyms. In this case see the discussion below for details.
**
-** FTS5 assumes the xToken() callback is invoked for each token in the
+** FTS5 assumes the xToken() callback is invoked for each token in the
** order that they occur within the input text.
**
** If an xToken() callback returns any value other than SQLITE_OK, then
@@ -12545,7 +13483,7 @@ struct Fts5ExtensionApi {
** SYNONYM SUPPORT
**
** Custom tokenizers may also support synonyms. Consider a case in which a
-** user wishes to query for a phrase such as "first place". Using the
+** user wishes to query for a phrase such as "first place". Using the
** built-in tokenizers, the FTS5 query 'first + place' will match instances
** of "first place" within the document set, but not alternative forms
** such as "1st place". In some applications, it would be better to match
@@ -12554,8 +13492,8 @@ struct Fts5ExtensionApi {
**
** There are several ways to approach this in FTS5:
**
-** <ol><li> By mapping all synonyms to a single token. In this case, the
-** In the above example, this means that the tokenizer returns the
+** <ol><li> By mapping all synonyms to a single token. In this case, using
+** the above example, this means that the tokenizer returns the
** same token for inputs "first" and "1st". Say that token is in
** fact "first", so that when the user inserts the document "I won
** 1st place" entries are added to the index for tokens "i", "won",
@@ -12563,36 +13501,36 @@ struct Fts5ExtensionApi {
** the tokenizer substitutes "first" for "1st" and the query works
** as expected.
**
-** <li> By adding multiple synonyms for a single term to the FTS index.
-** In this case, when tokenizing query text, the tokenizer may
-** provide multiple synonyms for a single term within the document.
-** FTS5 then queries the index for each synonym individually. For
-** example, faced with the query:
+** <li> By querying the index for all synonyms of each query term
+** separately. In this case, when tokenizing query text, the
+** tokenizer may provide multiple synonyms for a single term
+** within the document. FTS5 then queries the index for each
+** synonym individually. For example, faced with the query:
**
** <codeblock>
** ... MATCH 'first place'</codeblock>
**
** the tokenizer offers both "1st" and "first" as synonyms for the
-** first token in the MATCH query and FTS5 effectively runs a query
+** first token in the MATCH query and FTS5 effectively runs a query
** similar to:
**
** <codeblock>
** ... MATCH '(first OR 1st) place'</codeblock>
**
** except that, for the purposes of auxiliary functions, the query
-** still appears to contain just two phrases - "(first OR 1st)"
+** still appears to contain just two phrases - "(first OR 1st)"
** being treated as a single phrase.
**
** <li> By adding multiple synonyms for a single term to the FTS index.
** Using this method, when tokenizing document text, the tokenizer
-** provides multiple synonyms for each token. So that when a
+** provides multiple synonyms for each token. So that when a
** document such as "I won first place" is tokenized, entries are
** added to the FTS index for "i", "won", "first", "1st" and
** "place".
**
** This way, even if the tokenizer does not provide synonyms
-** when tokenizing query text (it should not - to do would be
-** inefficient), it doesn't matter if the user queries for
+** when tokenizing query text (it should not - to do so would be
+** inefficient), it doesn't matter if the user queries for
** 'first + place' or '1st + place', as there are entries in the
** FTS index corresponding to both forms of the first token.
** </ol>
@@ -12613,11 +13551,11 @@ struct Fts5ExtensionApi {
**
** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time
** xToken() is called. Multiple synonyms may be specified for a single token
-** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
+** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
** There is no limit to the number of synonyms that may be provided for a
** single token.
**
-** In many cases, method (1) above is the best approach. It does not add
+** In many cases, method (1) above is the best approach. It does not add
** extra data to the FTS index or require FTS5 to query for multiple terms,
** so it is efficient in terms of disk space and query speed. However, it
** does not support prefix queries very well. If, as suggested above, the
@@ -12629,24 +13567,24 @@ struct Fts5ExtensionApi {
** will not match documents that contain the token "1st" (as the tokenizer
** will probably not map "1s" to any prefix of "first").
**
-** For full prefix support, method (3) may be preferred. In this case,
+** For full prefix support, method (3) may be preferred. In this case,
** because the index contains entries for both "first" and "1st", prefix
** queries such as 'fi*' or '1s*' will match correctly. However, because
** extra entries are added to the FTS index, this method uses more space
** within the database.
**
** Method (2) offers a midpoint between (1) and (3). Using this method,
-** a query such as '1s*' will match documents that contain the literal
+** a query such as '1s*' will match documents that contain the literal
** token "1st", but not "first" (assuming the tokenizer is not able to
** provide synonyms for prefixes). However, a non-prefix query like '1st'
** will match against "1st" and "first". This method does not require
-** extra disk space, as no extra entries are added to the FTS index.
+** extra disk space, as no extra entries are added to the FTS index.
** On the other hand, it may require more CPU cycles to run MATCH queries,
** as separate queries of the FTS index are required for each synonym.
**
** When using methods (2) or (3), it is important that the tokenizer only
-** provide synonyms when tokenizing document text (method (2)) or query
-** text (method (3)), not both. Doing so will not cause any errors, but is
+** provide synonyms when tokenizing document text (method (3)) or query
+** text (method (2)), not both. Doing so will not cause any errors, but is
** inefficient.
*/
typedef struct Fts5Tokenizer Fts5Tokenizer;
@@ -12654,10 +13592,10 @@ typedef struct fts5_tokenizer fts5_tokenizer;
struct fts5_tokenizer {
int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
void (*xDelete)(Fts5Tokenizer*);
- int (*xTokenize)(Fts5Tokenizer*,
+ int (*xTokenize)(Fts5Tokenizer*,
void *pCtx,
int flags, /* Mask of FTS5_TOKENIZE_* flags */
- const char *pText, int nText,
+ const char *pText, int nText,
int (*xToken)(
void *pCtx, /* Copy of 2nd argument to xTokenize() */
int tflags, /* Mask of FTS5_TOKEN_* flags */
@@ -12694,7 +13632,7 @@ struct fts5_api {
int (*xCreateTokenizer)(
fts5_api *pApi,
const char *zName,
- void *pContext,
+ void *pUserData,
fts5_tokenizer *pTokenizer,
void (*xDestroy)(void*)
);
@@ -12703,7 +13641,7 @@ struct fts5_api {
int (*xFindTokenizer)(
fts5_api *pApi,
const char *zName,
- void **ppContext,
+ void **ppUserData,
fts5_tokenizer *pTokenizer
);
@@ -12711,7 +13649,7 @@ struct fts5_api {
int (*xCreateFunction)(
fts5_api *pApi,
const char *zName,
- void *pContext,
+ void *pUserData,
fts5_extension_function xFunction,
void (*xDestroy)(void*)
);
@@ -12733,11 +13671,16 @@ struct fts5_api {
/************** Continuing where we left off in sqliteInt.h ******************/
/*
+** Reuse the STATIC_LRU for mutex access to sqlite3_temp_directory.
+*/
+#define SQLITE_MUTEX_STATIC_TEMPDIR SQLITE_MUTEX_STATIC_VFS1
+
+/*
** Include the configuration header output by 'configure' if we're using the
** autoconf-based build
*/
#if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H)
-/* #include "config.h" */
+#include "sqlite_cfg.h"
#define SQLITECONFIG_H 1
#endif
@@ -12754,7 +13697,7 @@ struct fts5_api {
** May you share freely, never taking more than you give.
**
*************************************************************************
-**
+**
** This file defines various limits of what SQLite can process.
*/
@@ -12802,14 +13745,10 @@ struct fts5_api {
#endif
/*
-** The maximum depth of an expression tree. This is limited to
-** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
-** want to place more severe limits on the complexity of an
-** expression.
-**
-** A value of 0 used to mean that the limit was not enforced.
-** But that is no longer true. The limit is now strictly enforced
-** at all times.
+** The maximum depth of an expression tree. This is limited to
+** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
+** want to place more severe limits on the complexity of an
+** expression. A value of 0 means that there is no limit.
*/
#ifndef SQLITE_MAX_EXPR_DEPTH
# define SQLITE_MAX_EXPR_DEPTH 1000
@@ -12821,7 +13760,7 @@ struct fts5_api {
** level of recursion for each term. A stack overflow can result
** if the number of terms is too large. In practice, most SQL
** never has more than 3 or 4 terms. Use a value of 0 to disable
-** any limit on the number of terms in a compount SELECT.
+** any limit on the number of terms in a compound SELECT.
*/
#ifndef SQLITE_MAX_COMPOUND_SELECT
# define SQLITE_MAX_COMPOUND_SELECT 500
@@ -12876,9 +13815,12 @@ struct fts5_api {
/*
** The maximum value of a ?nnn wildcard that the parser will accept.
+** If the value exceeds 32767 then extra space is required for the Expr
+** structure. But otherwise, we believe that the number can be as large
+** as a signed 32-bit integer can hold.
*/
#ifndef SQLITE_MAX_VARIABLE_NUMBER
-# define SQLITE_MAX_VARIABLE_NUMBER 999
+# define SQLITE_MAX_VARIABLE_NUMBER 32766
#endif
/* Maximum page size. The upper bound on this value is 65536. This a limit
@@ -12886,10 +13828,10 @@ struct fts5_api {
**
** Earlier versions of SQLite allowed the user to change this value at
** compile time. This is no longer permitted, on the grounds that it creates
-** a library that is technically incompatible with an SQLite library
-** compiled with a different limit. If a process operating on a database
-** with a page-size of 65536 bytes crashes, then an instance of SQLite
-** compiled with the default page-size limit will not be able to rollback
+** a library that is technically incompatible with an SQLite library
+** compiled with a different limit. If a process operating on a database
+** with a page-size of 65536 bytes crashes, then an instance of SQLite
+** compiled with the default page-size limit will not be able to rollback
** the aborted transaction. This could lead to database corruption.
*/
#ifdef SQLITE_MAX_PAGE_SIZE
@@ -12933,7 +13875,7 @@ struct fts5_api {
** max_page_count macro.
*/
#ifndef SQLITE_MAX_PAGE_COUNT
-# define SQLITE_MAX_PAGE_COUNT 1073741823
+# define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */
#endif
/*
@@ -12948,7 +13890,7 @@ struct fts5_api {
** Maximum depth of recursion for triggers.
**
** A value of 1 means that a trigger program will not be able to itself
-** fire any triggers. A value of 0 means that no trigger programs at all
+** fire any triggers. A value of 0 means that no trigger programs at all
** may be executed.
*/
#ifndef SQLITE_MAX_TRIGGER_DEPTH
@@ -12968,6 +13910,23 @@ struct fts5_api {
#endif
/*
+** A few places in the code require atomic load/store of aligned
+** integer values.
+*/
+#ifndef __has_extension
+# define __has_extension(x) 0 /* compatibility with non-clang compilers */
+#endif
+#if GCC_VERSION>=4007000 || __has_extension(c_atomic)
+# define SQLITE_ATOMIC_INTRINSICS 1
+# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED)
+# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
+#else
+# define SQLITE_ATOMIC_INTRINSICS 0
+# define AtomicLoad(PTR) (*(PTR))
+# define AtomicStore(PTR,VAL) (*(PTR) = (VAL))
+#endif
+
+/*
** Include standard header files as necessary
*/
#ifdef HAVE_STDINT_H
@@ -12993,30 +13952,37 @@ struct fts5_api {
** So we have to define the macros in different ways depending on the
** compiler.
*/
-#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
+#if defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
+# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
+# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
+#elif defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
-#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
-# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
-# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
#else /* Generates a warning - but it always works */
# define SQLITE_INT_TO_PTR(X) ((void*)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(X))
#endif
/*
-** A macro to hint to the compiler that a function should not be
+** Macros to hint to the compiler that a function should or should not be
** inlined.
*/
#if defined(__GNUC__)
# define SQLITE_NOINLINE __attribute__((noinline))
+# define SQLITE_INLINE __attribute__((always_inline)) inline
#elif defined(_MSC_VER) && _MSC_VER>=1310
# define SQLITE_NOINLINE __declspec(noinline)
+# define SQLITE_INLINE __forceinline
#else
# define SQLITE_NOINLINE
+# define SQLITE_INLINE
+#endif
+#if defined(SQLITE_COVERAGE_TEST) || defined(__STRICT_ANSI__)
+# undef SQLITE_INLINE
+# define SQLITE_INLINE
#endif
/*
@@ -13039,6 +14005,29 @@ struct fts5_api {
#endif
/*
+** Enable SQLITE_USE_SEH by default on MSVC builds. Only omit
+** SEH support if the -DSQLITE_OMIT_SEH option is given.
+*/
+#if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH)
+# define SQLITE_USE_SEH 1
+#else
+# undef SQLITE_USE_SEH
+#endif
+
+/*
+** Enable SQLITE_DIRECT_OVERFLOW_READ, unless the build explicitly
+** disables it using -DSQLITE_DIRECT_OVERFLOW_READ=0
+*/
+#if defined(SQLITE_DIRECT_OVERFLOW_READ) && SQLITE_DIRECT_OVERFLOW_READ+1==1
+ /* Disable if -DSQLITE_DIRECT_OVERFLOW_READ=0 */
+# undef SQLITE_DIRECT_OVERFLOW_READ
+#else
+ /* In all other cases, enable */
+# define SQLITE_DIRECT_OVERFLOW_READ 1
+#endif
+
+
+/*
** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
** 0 means mutexes are permanently disable and the library is never
** threadsafe. 1 means the library is serialized which is the highest
@@ -13167,11 +14156,12 @@ struct fts5_api {
** is significant and used at least once. On switch statements
** where multiple cases go to the same block of code, testcase()
** can insure that all cases are evaluated.
-**
*/
-#ifdef SQLITE_COVERAGE_TEST
-SQLITE_PRIVATE void sqlite3Coverage(int);
-# define testcase(X) if( X ){ sqlite3Coverage(__LINE__); }
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG)
+# ifndef SQLITE_AMALGAMATION
+ extern unsigned int sqlite3CoverageCounter;
+# endif
+# define testcase(X) if( X ){ sqlite3CoverageCounter += (unsigned)__LINE__; }
#else
# define testcase(X)
#endif
@@ -13202,6 +14192,14 @@ SQLITE_PRIVATE void sqlite3Coverage(int);
#endif
/*
+** Disable ALWAYS() and NEVER() (make them pass-throughs) for coverage
+** and mutation testing
+*/
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
+# define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
+#endif
+
+/*
** The ALWAYS and NEVER macros surround boolean expressions which
** are intended to always be true or false, respectively. Such
** expressions could be omitted from the code completely. But they
@@ -13216,7 +14214,7 @@ SQLITE_PRIVATE void sqlite3Coverage(int);
** be true and false so that the unreachable code they specify will
** not be counted as untested code.
*/
-#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
+#if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
# define ALWAYS(X) (1)
# define NEVER(X) (0)
#elif !defined(NDEBUG)
@@ -13291,6 +14289,13 @@ SQLITE_PRIVATE void sqlite3Coverage(int);
#endif
/*
+** SQLITE_OMIT_VIRTUALTABLE implies SQLITE_OMIT_ALTERTABLE
+*/
+#if defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE)
+# define SQLITE_OMIT_ALTERTABLE
+#endif
+
+/*
** Return true (non-zero) if the input is an integer that is too large
** to fit in 32-bits. This macro is used inside of various testcase()
** macros to verify that we have tested SQLite for large-file support.
@@ -13346,7 +14351,7 @@ typedef struct HashElem HashElem;
** element pointed to plus the next _ht.count-1 elements in the list.
**
** Hash.htsize and Hash.ht may be zero. In that case lookup is done
-** by a linear search of the global list. For small tables, the
+** by a linear search of the global list. For small tables, the
** Hash.ht table is never allocated because if there are few elements
** in the table, it is faster to do a linear search than to manage
** the hash table.
@@ -13356,12 +14361,12 @@ struct Hash {
unsigned int count; /* Number of entries in this table */
HashElem *first; /* The first element of the array */
struct _ht { /* the hash table */
- int count; /* Number of entries with this hash */
+ unsigned 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
+/* 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
@@ -13402,7 +14407,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
/*
** Number of entries in a hash table
*/
-/* #define sqliteHashCount(H) ((H)->count) // NOT USED */
+#define sqliteHashCount(H) ((H)->count)
#endif /* SQLITE_HASH_H */
@@ -13434,8 +14439,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_LP 22
#define TK_RP 23
#define TK_AS 24
-#define TK_WITHOUT 25
-#define TK_COMMA 26
+#define TK_COMMA 25
+#define TK_WITHOUT 26
#define TK_ABORT 27
#define TK_ACTION 28
#define TK_AFTER 29
@@ -13491,105 +14496,109 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_VIEW 79
#define TK_VIRTUAL 80
#define TK_WITH 81
-#define TK_CURRENT 82
-#define TK_FOLLOWING 83
-#define TK_PARTITION 84
-#define TK_PRECEDING 85
-#define TK_RANGE 86
-#define TK_UNBOUNDED 87
-#define TK_REINDEX 88
-#define TK_RENAME 89
-#define TK_CTIME_KW 90
-#define TK_ANY 91
-#define TK_BITAND 92
-#define TK_BITOR 93
-#define TK_LSHIFT 94
-#define TK_RSHIFT 95
-#define TK_PLUS 96
-#define TK_MINUS 97
-#define TK_STAR 98
-#define TK_SLASH 99
-#define TK_REM 100
-#define TK_CONCAT 101
-#define TK_COLLATE 102
-#define TK_BITNOT 103
-#define TK_ON 104
-#define TK_INDEXED 105
-#define TK_STRING 106
-#define TK_JOIN_KW 107
-#define TK_CONSTRAINT 108
-#define TK_DEFAULT 109
-#define TK_NULL 110
-#define TK_PRIMARY 111
-#define TK_UNIQUE 112
-#define TK_CHECK 113
-#define TK_REFERENCES 114
-#define TK_AUTOINCR 115
-#define TK_INSERT 116
-#define TK_DELETE 117
-#define TK_UPDATE 118
-#define TK_SET 119
-#define TK_DEFERRABLE 120
-#define TK_FOREIGN 121
-#define TK_DROP 122
-#define TK_UNION 123
-#define TK_ALL 124
-#define TK_EXCEPT 125
-#define TK_INTERSECT 126
-#define TK_SELECT 127
-#define TK_VALUES 128
-#define TK_DISTINCT 129
-#define TK_DOT 130
-#define TK_FROM 131
-#define TK_JOIN 132
-#define TK_USING 133
-#define TK_ORDER 134
-#define TK_GROUP 135
-#define TK_HAVING 136
-#define TK_LIMIT 137
-#define TK_WHERE 138
-#define TK_INTO 139
-#define TK_NOTHING 140
-#define TK_FLOAT 141
-#define TK_BLOB 142
-#define TK_INTEGER 143
-#define TK_VARIABLE 144
-#define TK_CASE 145
-#define TK_WHEN 146
-#define TK_THEN 147
-#define TK_ELSE 148
-#define TK_INDEX 149
-#define TK_ALTER 150
-#define TK_ADD 151
-#define TK_WINDOW 152
-#define TK_OVER 153
-#define TK_FILTER 154
-#define TK_TRUEFALSE 155
-#define TK_ISNOT 156
-#define TK_FUNCTION 157
-#define TK_COLUMN 158
-#define TK_AGG_FUNCTION 159
-#define TK_AGG_COLUMN 160
-#define TK_UMINUS 161
-#define TK_UPLUS 162
-#define TK_TRUTH 163
-#define TK_REGISTER 164
-#define TK_VECTOR 165
-#define TK_SELECT_COLUMN 166
-#define TK_IF_NULL_ROW 167
-#define TK_ASTERISK 168
-#define TK_SPAN 169
-#define TK_END_OF_FILE 170
-#define TK_UNCLOSED_STRING 171
-#define TK_SPACE 172
-#define TK_ILLEGAL 173
-
-/* The token codes above must all fit in 8 bits */
-#define TKFLG_MASK 0xff
-
-/* Flags that can be added to a token code when it is not
-** being stored in a u8: */
-#define TKFLG_DONTFOLD 0x100 /* Omit constant folding optimizations */
+#define TK_NULLS 82
+#define TK_FIRST 83
+#define TK_LAST 84
+#define TK_CURRENT 85
+#define TK_FOLLOWING 86
+#define TK_PARTITION 87
+#define TK_PRECEDING 88
+#define TK_RANGE 89
+#define TK_UNBOUNDED 90
+#define TK_EXCLUDE 91
+#define TK_GROUPS 92
+#define TK_OTHERS 93
+#define TK_TIES 94
+#define TK_GENERATED 95
+#define TK_ALWAYS 96
+#define TK_MATERIALIZED 97
+#define TK_REINDEX 98
+#define TK_RENAME 99
+#define TK_CTIME_KW 100
+#define TK_ANY 101
+#define TK_BITAND 102
+#define TK_BITOR 103
+#define TK_LSHIFT 104
+#define TK_RSHIFT 105
+#define TK_PLUS 106
+#define TK_MINUS 107
+#define TK_STAR 108
+#define TK_SLASH 109
+#define TK_REM 110
+#define TK_CONCAT 111
+#define TK_PTR 112
+#define TK_COLLATE 113
+#define TK_BITNOT 114
+#define TK_ON 115
+#define TK_INDEXED 116
+#define TK_STRING 117
+#define TK_JOIN_KW 118
+#define TK_CONSTRAINT 119
+#define TK_DEFAULT 120
+#define TK_NULL 121
+#define TK_PRIMARY 122
+#define TK_UNIQUE 123
+#define TK_CHECK 124
+#define TK_REFERENCES 125
+#define TK_AUTOINCR 126
+#define TK_INSERT 127
+#define TK_DELETE 128
+#define TK_UPDATE 129
+#define TK_SET 130
+#define TK_DEFERRABLE 131
+#define TK_FOREIGN 132
+#define TK_DROP 133
+#define TK_UNION 134
+#define TK_ALL 135
+#define TK_EXCEPT 136
+#define TK_INTERSECT 137
+#define TK_SELECT 138
+#define TK_VALUES 139
+#define TK_DISTINCT 140
+#define TK_DOT 141
+#define TK_FROM 142
+#define TK_JOIN 143
+#define TK_USING 144
+#define TK_ORDER 145
+#define TK_GROUP 146
+#define TK_HAVING 147
+#define TK_LIMIT 148
+#define TK_WHERE 149
+#define TK_RETURNING 150
+#define TK_INTO 151
+#define TK_NOTHING 152
+#define TK_FLOAT 153
+#define TK_BLOB 154
+#define TK_INTEGER 155
+#define TK_VARIABLE 156
+#define TK_CASE 157
+#define TK_WHEN 158
+#define TK_THEN 159
+#define TK_ELSE 160
+#define TK_INDEX 161
+#define TK_ALTER 162
+#define TK_ADD 163
+#define TK_WINDOW 164
+#define TK_OVER 165
+#define TK_FILTER 166
+#define TK_COLUMN 167
+#define TK_AGG_FUNCTION 168
+#define TK_AGG_COLUMN 169
+#define TK_TRUEFALSE 170
+#define TK_ISNOT 171
+#define TK_FUNCTION 172
+#define TK_UMINUS 173
+#define TK_UPLUS 174
+#define TK_TRUTH 175
+#define TK_REGISTER 176
+#define TK_VECTOR 177
+#define TK_SELECT_COLUMN 178
+#define TK_IF_NULL_ROW 179
+#define TK_ASTERISK 180
+#define TK_SPAN 181
+#define TK_ERROR 182
+#define TK_SPACE 183
+#define TK_ILLEGAL 184
/************** End of parse.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -13695,7 +14704,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
** number of pages. A negative number N translations means that a buffer
** of -1024*N bytes is allocated and used for as many pages as it will hold.
**
-** The default value of "20" was choosen to minimize the run-time of the
+** The default value of "20" was chosen to minimize the run-time of the
** speedtest1 test program with options: --shrink-memory --reprepare
*/
#ifndef SQLITE_DEFAULT_PCACHE_INITSZ
@@ -13710,7 +14719,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#endif
/*
-** The compile-time options SQLITE_MMAP_READWRITE and
+** The compile-time options SQLITE_MMAP_READWRITE and
** SQLITE_ENABLE_BATCH_ATOMIC_WRITE are not compatible with one another.
** You must choose one or the other (or neither) but not both.
*/
@@ -13814,15 +14823,9 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
/*
** The datatype used to store estimates of the number of rows in a
-** table or index. This is an unsigned integer type. For 99.9% of
-** the world, a 32-bit integer is sufficient. But a 64-bit integer
-** can be used at compile-time if desired.
+** table or index.
*/
-#ifdef SQLITE_64BIT_STATS
- typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */
-#else
- typedef u32 tRowcnt; /* 32-bit is the default */
-#endif
+typedef u64 tRowcnt;
/*
** Estimated quantities used for query planning are stored as 16-bit
@@ -13857,6 +14860,7 @@ typedef INT16_TYPE LogEst;
# define SQLITE_PTRSIZE __SIZEOF_POINTER__
# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(_M_ARM) || defined(__arm__) || defined(__x86) || \
+ (defined(__APPLE__) && defined(__POWERPC__)) || \
(defined(__TOS_AIX__) && !defined(__64BIT__))
# define SQLITE_PTRSIZE 4
# else
@@ -13882,8 +14886,31 @@ typedef INT16_TYPE LogEst;
** the end of buffer S. This macro returns true if P points to something
** contained within the buffer S.
*/
-#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E)))
+#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E)))
+/*
+** P is one byte past the end of a large buffer. Return true if a span of bytes
+** between S..E crosses the end of that buffer. In other words, return true
+** if the sub-buffer S..E-1 overflows the buffer whose last byte is P-1.
+**
+** S is the start of the span. E is one byte past the end of end of span.
+**
+** P
+** |-----------------| FALSE
+** |-------|
+** S E
+**
+** P
+** |-----------------|
+** |-------| TRUE
+** S E
+**
+** P
+** |-----------------|
+** |-------| FALSE
+** S E
+*/
+#define SQLITE_OVERFLOW(P,S,E) (((uptr)(S)<(uptr)(P))&&((uptr)(E)>(uptr)(P)))
/*
** Macros to determine whether the machine is big or little endian,
@@ -13893,15 +14920,33 @@ typedef INT16_TYPE LogEst;
** using C-preprocessor macros. If that is unsuccessful, or if
** -DSQLITE_BYTEORDER=0 is set, then byte-order is determined
** at run-time.
+**
+** If you are building SQLite on some obscure platform for which the
+** following ifdef magic does not work, you can always include either:
+**
+** -DSQLITE_BYTEORDER=1234
+**
+** or
+**
+** -DSQLITE_BYTEORDER=4321
+**
+** to cause the build to work for little-endian or big-endian processors,
+** respectively.
*/
-#ifndef SQLITE_BYTEORDER
-# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \
- defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
- defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
- defined(__arm__) || defined(_M_ARM64)
-# define SQLITE_BYTEORDER 1234
-# elif defined(sparc) || defined(__ppc__)
-# define SQLITE_BYTEORDER 4321
+#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */
+# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
+# define SQLITE_BYTEORDER 4321
+# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
+# define SQLITE_BYTEORDER 1234
+# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1
+# define SQLITE_BYTEORDER 4321
+# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
+ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
+ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
+ defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64)
+# define SQLITE_BYTEORDER 1234
+# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__)
+# define SQLITE_BYTEORDER 4321
# else
# define SQLITE_BYTEORDER 0
# endif
@@ -13931,13 +14976,25 @@ typedef INT16_TYPE LogEst;
** compilers.
*/
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
+#define LARGEST_UINT64 (0xffffffff|(((u64)0xffffffff)<<32))
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
/*
** Round up a number to the next larger multiple of 8. This is used
** to force 8-byte alignment on 64-bit architectures.
+**
+** ROUND8() always does the rounding, for any argument.
+**
+** ROUND8P() assumes that the argument is already an integer number of
+** pointers in size, and so it is a no-op on systems where the pointer
+** size is 8.
*/
#define ROUND8(x) (((x)+7)&~7)
+#if SQLITE_PTRSIZE==8
+# define ROUND8P(x) (x)
+#else
+# define ROUND8P(x) (((x)+7)&~7)
+#endif
/*
** Round down to the nearest multiple of 8
@@ -13954,9 +15011,9 @@ typedef INT16_TYPE LogEst;
** pointers. In that case, only verify 4-byte alignment.
*/
#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
-# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&3)==0)
+# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0)
#else
-# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0)
+# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0)
#endif
/*
@@ -14000,30 +15057,92 @@ typedef INT16_TYPE LogEst;
#endif
/*
-** Only one of SQLITE_ENABLE_STAT3 or SQLITE_ENABLE_STAT4 can be defined.
-** Priority is given to SQLITE_ENABLE_STAT4. If either are defined, also
-** define SQLITE_ENABLE_STAT3_OR_STAT4
+** TREETRACE_ENABLED will be either 1 or 0 depending on whether or not
+** the Abstract Syntax Tree tracing logic is turned on.
*/
-#ifdef SQLITE_ENABLE_STAT4
-# undef SQLITE_ENABLE_STAT3
-# define SQLITE_ENABLE_STAT3_OR_STAT4 1
-#elif SQLITE_ENABLE_STAT3
-# define SQLITE_ENABLE_STAT3_OR_STAT4 1
-#elif SQLITE_ENABLE_STAT3_OR_STAT4
-# undef SQLITE_ENABLE_STAT3_OR_STAT4
+#if !defined(SQLITE_AMALGAMATION)
+SQLITE_PRIVATE u32 sqlite3TreeTrace;
+#endif
+#if defined(SQLITE_DEBUG) \
+ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE) \
+ || defined(SQLITE_ENABLE_TREETRACE))
+# define TREETRACE_ENABLED 1
+# define TREETRACE(K,P,S,X) \
+ if(sqlite3TreeTrace&(K)) \
+ sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\
+ sqlite3DebugPrintf X
+#else
+# define TREETRACE(K,P,S,X)
+# define TREETRACE_ENABLED 0
#endif
+/* TREETRACE flag meanings:
+**
+** 0x00000001 Beginning and end of SELECT processing
+** 0x00000002 WHERE clause processing
+** 0x00000004 Query flattener
+** 0x00000008 Result-set wildcard expansion
+** 0x00000010 Query name resolution
+** 0x00000020 Aggregate analysis
+** 0x00000040 Window functions
+** 0x00000080 Generated column names
+** 0x00000100 Move HAVING terms into WHERE
+** 0x00000200 Count-of-view optimization
+** 0x00000400 Compound SELECT processing
+** 0x00000800 Drop superfluous ORDER BY
+** 0x00001000 LEFT JOIN simplifies to JOIN
+** 0x00002000 Constant propagation
+** 0x00004000 Push-down optimization
+** 0x00008000 After all FROM-clause analysis
+** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing
+** 0x00020000 Transform DISTINCT into GROUP BY
+** 0x00040000 SELECT tree dump after all code has been generated
+*/
+
/*
-** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not
-** the Select query generator tracing logic is turned on.
+** Macros for "wheretrace"
*/
-#if defined(SQLITE_ENABLE_SELECTTRACE)
-# define SELECTTRACE_ENABLED 1
+SQLITE_PRIVATE u32 sqlite3WhereTrace;
+#if defined(SQLITE_DEBUG) \
+ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
+# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X
+# define WHERETRACE_ENABLED 1
#else
-# define SELECTTRACE_ENABLED 0
+# define WHERETRACE(K,X)
#endif
/*
+** Bits for the sqlite3WhereTrace mask:
+**
+** (---any--) Top-level block structure
+** 0x-------F High-level debug messages
+** 0x----FFF- More detail
+** 0xFFFF---- Low-level debug messages
+**
+** 0x00000001 Code generation
+** 0x00000002 Solver
+** 0x00000004 Solver costs
+** 0x00000008 WhereLoop inserts
+**
+** 0x00000010 Display sqlite3_index_info xBestIndex calls
+** 0x00000020 Range an equality scan metrics
+** 0x00000040 IN operator decisions
+** 0x00000080 WhereLoop cost adjustements
+** 0x00000100
+** 0x00000200 Covering index decisions
+** 0x00000400 OR optimization
+** 0x00000800 Index scanner
+** 0x00001000 More details associated with code generation
+** 0x00002000
+** 0x00004000 Show all WHERE terms at key points
+** 0x00008000 Show the full SELECT statement at key places
+**
+** 0x00010000 Show more detail when printing WHERE terms
+** 0x00020000 Show WHERE terms returned from whereScanNext()
+*/
+
+
+/*
** An instance of the following structure is used to store the busy-handler
** callback for a given sqlite handle.
**
@@ -14037,26 +15156,41 @@ struct BusyHandler {
int (*xBusyHandler)(void *,int); /* The busy callback */
void *pBusyArg; /* First arg to busy callback */
int nBusy; /* Incremented with each busy call */
- u8 bExtraFileArg; /* Include sqlite3_file as callback arg */
};
/*
-** Name of the master database table. The master database table
-** is a special table that holds the names and attributes of all
-** user tables and indices.
+** Name of table that holds the database schema.
+**
+** The PREFERRED names are used wherever possible. But LEGACY is also
+** used for backwards compatibility.
+**
+** 1. Queries can use either the PREFERRED or the LEGACY names
+** 2. The sqlite3_set_authorizer() callback uses the LEGACY name
+** 3. The PRAGMA table_list statement uses the PREFERRED name
+**
+** The LEGACY names are stored in the internal symbol hash table
+** in support of (2). Names are translated using sqlite3PreferredTableName()
+** for (3). The sqlite3FindTable() function takes care of translating
+** names for (1).
+**
+** Note that "sqlite_temp_schema" can also be called "temp.sqlite_schema".
*/
-#define MASTER_NAME "sqlite_master"
-#define TEMP_MASTER_NAME "sqlite_temp_master"
+#define LEGACY_SCHEMA_TABLE "sqlite_master"
+#define LEGACY_TEMP_SCHEMA_TABLE "sqlite_temp_master"
+#define PREFERRED_SCHEMA_TABLE "sqlite_schema"
+#define PREFERRED_TEMP_SCHEMA_TABLE "sqlite_temp_schema"
+
/*
-** The root-page of the master database table.
+** The root-page of the schema table.
*/
-#define MASTER_ROOT 1
+#define SCHEMA_ROOT 1
/*
-** The name of the schema table.
+** The name of the schema table. The name is different for TEMP.
*/
-#define SCHEMA_TABLE(x) ((!OMIT_TEMPDB)&&(x==1)?TEMP_MASTER_NAME:MASTER_NAME)
+#define SCHEMA_TABLE(x) \
+ ((!OMIT_TEMPDB)&&(x==1)?LEGACY_TEMP_SCHEMA_TABLE:LEGACY_SCHEMA_TABLE)
/*
** A convenience macro that returns the number of elements in
@@ -14077,7 +15211,7 @@ struct BusyHandler {
** pointer will work here as long as it is distinct from SQLITE_STATIC
** and SQLITE_TRANSIENT.
*/
-#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3MallocSize)
+#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3OomClear)
/*
** When SQLITE_OMIT_WSD is defined, it means that the target platform does
@@ -14133,16 +15267,22 @@ typedef struct AutoincInfo AutoincInfo;
typedef struct Bitvec Bitvec;
typedef struct CollSeq CollSeq;
typedef struct Column Column;
+typedef struct Cte Cte;
+typedef struct CteUse CteUse;
typedef struct Db Db;
+typedef struct DbClientData DbClientData;
+typedef struct DbFixer DbFixer;
typedef struct Schema Schema;
typedef struct Expr Expr;
typedef struct ExprList ExprList;
typedef struct FKey FKey;
+typedef struct FpDecode FpDecode;
typedef struct FuncDestructor FuncDestructor;
typedef struct FuncDef FuncDef;
typedef struct FuncDefHash FuncDefHash;
typedef struct IdList IdList;
typedef struct Index Index;
+typedef struct IndexedExpr IndexedExpr;
typedef struct IndexSample IndexSample;
typedef struct KeyClass KeyClass;
typedef struct KeyInfo KeyInfo;
@@ -14150,15 +15290,20 @@ typedef struct Lookaside Lookaside;
typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
+typedef struct OnOrUsing OnOrUsing;
typedef struct Parse Parse;
+typedef struct ParseCleanup ParseCleanup;
typedef struct PreUpdate PreUpdate;
typedef struct PrintfArguments PrintfArguments;
+typedef struct RCStr RCStr;
typedef struct RenameToken RenameToken;
+typedef struct Returning Returning;
typedef struct RowSet RowSet;
typedef struct Savepoint Savepoint;
typedef struct Select Select;
typedef struct SQLiteThread SQLiteThread;
typedef struct SelectDest SelectDest;
+typedef struct SrcItem SrcItem;
typedef struct SrcList SrcList;
typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */
typedef struct Table Table;
@@ -14199,9 +15344,12 @@ typedef struct With With;
/*
** A bit in a Bitmask
*/
-#define MASKBIT(n) (((Bitmask)1)<<(n))
-#define MASKBIT32(n) (((unsigned int)1)<<(n))
-#define ALLBITS ((Bitmask)-1)
+#define MASKBIT(n) (((Bitmask)1)<<(n))
+#define MASKBIT64(n) (((u64)1)<<(n))
+#define MASKBIT32(n) (((unsigned int)1)<<(n))
+#define SMASKBIT32(n) ((n)<=31?((unsigned int)1)<<(n):0)
+#define ALLBITS ((Bitmask)-1)
+#define TOPBIT (((Bitmask)1)<<(BMS-1))
/* A VList object records a mapping between parameters/variables/wildcards
** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer
@@ -14216,6 +15364,583 @@ typedef int VList;
** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque
** pointer types (i.e. FuncDef) defined above.
*/
+/************** Include os.h in the middle of sqliteInt.h ********************/
+/************** Begin file os.h **********************************************/
+/*
+** 2001 September 16
+**
+** 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 (together with is companion C source-code file
+** "os.c") attempt to abstract the underlying operating system so that
+** the SQLite library will work on both POSIX and windows systems.
+**
+** This header file is #include-ed by sqliteInt.h and thus ends up
+** being included by every source file.
+*/
+#ifndef _SQLITE_OS_H_
+#define _SQLITE_OS_H_
+
+/*
+** Attempt to automatically detect the operating system and setup the
+** necessary pre-processor macros for it.
+*/
+/************** Include os_setup.h in the middle of os.h *********************/
+/************** Begin file os_setup.h ****************************************/
+/*
+** 2013 November 25
+**
+** 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 contains pre-processor directives related to operating system
+** detection and/or setup.
+*/
+#ifndef SQLITE_OS_SETUP_H
+#define SQLITE_OS_SETUP_H
+
+/*
+** Figure out if we are dealing with Unix, Windows, or some other operating
+** system.
+**
+** After the following block of preprocess macros, all of
+**
+** SQLITE_OS_KV
+** SQLITE_OS_OTHER
+** SQLITE_OS_UNIX
+** SQLITE_OS_WIN
+**
+** will defined to either 1 or 0. One of them will be 1. The others will be 0.
+** If none of the macros are initially defined, then select either
+** SQLITE_OS_UNIX or SQLITE_OS_WIN depending on the target platform.
+**
+** If SQLITE_OS_OTHER=1 is specified at compile-time, then the application
+** must provide its own VFS implementation together with sqlite3_os_init()
+** and sqlite3_os_end() routines.
+*/
+#if !defined(SQLITE_OS_KV) && !defined(SQLITE_OS_OTHER) && \
+ !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_WIN)
+# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
+ defined(__MINGW32__) || defined(__BORLANDC__)
+# define SQLITE_OS_WIN 1
+# define SQLITE_OS_UNIX 0
+# else
+# define SQLITE_OS_WIN 0
+# define SQLITE_OS_UNIX 1
+# endif
+#endif
+#if SQLITE_OS_OTHER+1>1
+# undef SQLITE_OS_KV
+# define SQLITE_OS_KV 0
+# undef SQLITE_OS_UNIX
+# define SQLITE_OS_UNIX 0
+# undef SQLITE_OS_WIN
+# define SQLITE_OS_WIN 0
+#endif
+#if SQLITE_OS_KV+1>1
+# undef SQLITE_OS_OTHER
+# define SQLITE_OS_OTHER 0
+# undef SQLITE_OS_UNIX
+# define SQLITE_OS_UNIX 0
+# undef SQLITE_OS_WIN
+# define SQLITE_OS_WIN 0
+# define SQLITE_OMIT_LOAD_EXTENSION 1
+# define SQLITE_OMIT_WAL 1
+# define SQLITE_OMIT_DEPRECATED 1
+# undef SQLITE_TEMP_STORE
+# define SQLITE_TEMP_STORE 3 /* Always use memory for temporary storage */
+# define SQLITE_DQS 0
+# define SQLITE_OMIT_SHARED_CACHE 1
+# define SQLITE_OMIT_AUTOINIT 1
+#endif
+#if SQLITE_OS_UNIX+1>1
+# undef SQLITE_OS_KV
+# define SQLITE_OS_KV 0
+# undef SQLITE_OS_OTHER
+# define SQLITE_OS_OTHER 0
+# undef SQLITE_OS_WIN
+# define SQLITE_OS_WIN 0
+#endif
+#if SQLITE_OS_WIN+1>1
+# undef SQLITE_OS_KV
+# define SQLITE_OS_KV 0
+# undef SQLITE_OS_OTHER
+# define SQLITE_OS_OTHER 0
+# undef SQLITE_OS_UNIX
+# define SQLITE_OS_UNIX 0
+#endif
+
+
+#endif /* SQLITE_OS_SETUP_H */
+
+/************** End of os_setup.h ********************************************/
+/************** Continuing where we left off in os.h *************************/
+
+/* If the SET_FULLSYNC macro is not defined above, then make it
+** a no-op
+*/
+#ifndef SET_FULLSYNC
+# define SET_FULLSYNC(x,y)
+#endif
+
+/* Maximum pathname length. Note: FILENAME_MAX defined by stdio.h
+*/
+#ifndef SQLITE_MAX_PATHLEN
+# define SQLITE_MAX_PATHLEN FILENAME_MAX
+#endif
+
+/* Maximum number of symlinks that will be resolved while trying to
+** expand a filename in xFullPathname() in the VFS.
+*/
+#ifndef SQLITE_MAX_SYMLINK
+# define SQLITE_MAX_SYMLINK 200
+#endif
+
+/*
+** The default size of a disk sector
+*/
+#ifndef SQLITE_DEFAULT_SECTOR_SIZE
+# define SQLITE_DEFAULT_SECTOR_SIZE 4096
+#endif
+
+/*
+** Temporary files are named starting with this prefix followed by 16 random
+** alphanumeric characters, and no file extension. They are stored in the
+** OS's standard temporary file directory, and are deleted prior to exit.
+** If sqlite is being embedded in another program, you may wish to change the
+** prefix to reflect your program's name, so that if your program exits
+** prematurely, old temporary files can be easily identified. This can be done
+** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line.
+**
+** 2006-10-31: The default prefix used to be "sqlite_". But then
+** Mcafee started using SQLite in their anti-virus product and it
+** started putting files with the "sqlite" name in the c:/temp folder.
+** This annoyed many windows users. Those users would then do a
+** Google search for "sqlite", find the telephone numbers of the
+** developers and call to wake them up at night and complain.
+** For this reason, the default name prefix is changed to be "sqlite"
+** spelled backwards. So the temp files are still identified, but
+** anybody smart enough to figure out the code is also likely smart
+** enough to know that calling the developer will not help get rid
+** of the file.
+*/
+#ifndef SQLITE_TEMP_FILE_PREFIX
+# define SQLITE_TEMP_FILE_PREFIX "etilqs_"
+#endif
+
+/*
+** The following values may be passed as the second argument to
+** sqlite3OsLock(). The various locks exhibit the following semantics:
+**
+** SHARED: Any number of processes may hold a SHARED lock simultaneously.
+** RESERVED: A single process may hold a RESERVED lock on a file at
+** any time. Other processes may hold and obtain new SHARED locks.
+** PENDING: A single process may hold a PENDING lock on a file at
+** any one time. Existing SHARED locks may persist, but no new
+** SHARED locks may be obtained by other processes.
+** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
+**
+** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a
+** process that requests an EXCLUSIVE lock may actually obtain a PENDING
+** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
+** sqlite3OsLock().
+*/
+#define NO_LOCK 0
+#define SHARED_LOCK 1
+#define RESERVED_LOCK 2
+#define PENDING_LOCK 3
+#define EXCLUSIVE_LOCK 4
+
+/*
+** File Locking Notes: (Mostly about windows but also some info for Unix)
+**
+** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
+** those functions are not available. So we use only LockFile() and
+** UnlockFile().
+**
+** LockFile() prevents not just writing but also reading by other processes.
+** A SHARED_LOCK is obtained by locking a single randomly-chosen
+** byte out of a specific range of bytes. The lock byte is obtained at
+** random so two separate readers can probably access the file at the
+** same time, unless they are unlucky and choose the same lock byte.
+** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range.
+** There can only be one writer. A RESERVED_LOCK is obtained by locking
+** a single byte of the file that is designated as the reserved lock byte.
+** A PENDING_LOCK is obtained by locking a designated byte different from
+** the RESERVED_LOCK byte.
+**
+** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
+** which means we can use reader/writer locks. When reader/writer locks
+** are used, the lock is placed on the same range of bytes that is used
+** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
+** will support two or more Win95 readers or two or more WinNT readers.
+** But a single Win95 reader will lock out all WinNT readers and a single
+** WinNT reader will lock out all other Win95 readers.
+**
+** The following #defines specify the range of bytes used for locking.
+** SHARED_SIZE is the number of bytes available in the pool from which
+** a random byte is selected for a shared lock. The pool of bytes for
+** shared locks begins at SHARED_FIRST.
+**
+** The same locking strategy and
+** byte ranges are used for Unix. This leaves open the possibility of having
+** clients on win95, winNT, and unix all talking to the same shared file
+** and all locking correctly. To do so would require that samba (or whatever
+** tool is being used for file sharing) implements locks correctly between
+** windows and unix. I'm guessing that isn't likely to happen, but by
+** using the same locking range we are at least open to the possibility.
+**
+** Locking in windows is manditory. For this reason, we cannot store
+** actual data in the bytes used for locking. The pager never allocates
+** the pages involved in locking therefore. SHARED_SIZE is selected so
+** that all locks will fit on a single page even at the minimum page size.
+** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE
+** is set high so that we don't have to allocate an unused page except
+** for very large databases. But one should test the page skipping logic
+** by setting PENDING_BYTE low and running the entire regression suite.
+**
+** Changing the value of PENDING_BYTE results in a subtly incompatible
+** file format. Depending on how it is changed, you might not notice
+** the incompatibility right away, even running a full regression test.
+** The default location of PENDING_BYTE is the first byte past the
+** 1GB boundary.
+**
+*/
+#ifdef SQLITE_OMIT_WSD
+# define PENDING_BYTE (0x40000000)
+#else
+# define PENDING_BYTE sqlite3PendingByte
+#endif
+#define RESERVED_BYTE (PENDING_BYTE+1)
+#define SHARED_FIRST (PENDING_BYTE+2)
+#define SHARED_SIZE 510
+
+/*
+** Wrapper around OS specific sqlite3_os_init() function.
+*/
+SQLITE_PRIVATE int sqlite3OsInit(void);
+
+/*
+** Functions for accessing sqlite3_file methods
+*/
+SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file*);
+SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset);
+SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset);
+SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size);
+SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int);
+SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize);
+SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int);
+SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int);
+SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut);
+SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*);
+SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*);
+#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0
+SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id);
+SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id);
+#ifndef SQLITE_OMIT_WAL
+SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **);
+SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int);
+SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id);
+SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int);
+#endif /* SQLITE_OMIT_WAL */
+SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64, int, void **);
+SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *, i64, void *);
+
+
+/*
+** Functions for accessing sqlite3_vfs methods
+*/
+SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *);
+SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int);
+SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut);
+SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *);
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *);
+SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *);
+SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void);
+SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *);
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *);
+SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int);
+SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs*);
+SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*);
+
+/*
+** Convenience functions for opening and closing files using
+** sqlite3_malloc() to obtain space for the file-handle structure.
+*/
+SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*);
+SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
+
+#endif /* _SQLITE_OS_H_ */
+
+/************** End of os.h **************************************************/
+/************** Continuing where we left off in sqliteInt.h ******************/
+/************** Include pager.h in the middle of sqliteInt.h *****************/
+/************** Begin file pager.h *******************************************/
+/*
+** 2001 September 15
+**
+** 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 defines the interface that the sqlite page cache
+** subsystem. The page cache subsystem reads and writes a file a page
+** at a time and provides a journal for rollback.
+*/
+
+#ifndef SQLITE_PAGER_H
+#define SQLITE_PAGER_H
+
+/*
+** Default maximum size for persistent journal files. A negative
+** value means no limit. This value may be overridden using the
+** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit".
+*/
+#ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT
+ #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1
+#endif
+
+/*
+** The type used to represent a page number. The first page in a file
+** is called page 1. 0 is used to represent "not a page".
+*/
+typedef u32 Pgno;
+
+/*
+** Each open file is managed by a separate instance of the "Pager" structure.
+*/
+typedef struct Pager Pager;
+
+/*
+** Handle type for pages.
+*/
+typedef struct PgHdr DbPage;
+
+/*
+** Page number PAGER_SJ_PGNO is never used in an SQLite database (it is
+** reserved for working around a windows/posix incompatibility). It is
+** used in the journal to signify that the remainder of the journal file
+** is devoted to storing a super-journal name - there are no more pages to
+** roll back. See comments for function writeSuperJournal() in pager.c
+** for details.
+*/
+#define PAGER_SJ_PGNO_COMPUTED(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1))
+#define PAGER_SJ_PGNO(x) ((x)->lckPgno)
+
+/*
+** Allowed values for the flags parameter to sqlite3PagerOpen().
+**
+** NOTE: These values must match the corresponding BTREE_ values in btree.h.
+*/
+#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */
+#define PAGER_MEMORY 0x0002 /* In-memory database */
+
+/*
+** Valid values for the second argument to sqlite3PagerLockingMode().
+*/
+#define PAGER_LOCKINGMODE_QUERY -1
+#define PAGER_LOCKINGMODE_NORMAL 0
+#define PAGER_LOCKINGMODE_EXCLUSIVE 1
+
+/*
+** Numeric constants that encode the journalmode.
+**
+** The numeric values encoded here (other than PAGER_JOURNALMODE_QUERY)
+** are exposed in the API via the "PRAGMA journal_mode" command and
+** therefore cannot be changed without a compatibility break.
+*/
+#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */
+#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */
+#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */
+#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */
+#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */
+#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */
+#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
+
+/*
+** Flags that make up the mask passed to sqlite3PagerGet().
+*/
+#define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */
+#define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */
+
+/*
+** Flags for sqlite3PagerSetFlags()
+**
+** Value constraints (enforced via assert()):
+** PAGER_FULLFSYNC == SQLITE_FullFSync
+** PAGER_CKPT_FULLFSYNC == SQLITE_CkptFullFSync
+** PAGER_CACHE_SPILL == SQLITE_CacheSpill
+*/
+#define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */
+#define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */
+#define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */
+#define PAGER_SYNCHRONOUS_EXTRA 0x04 /* PRAGMA synchronous=EXTRA */
+#define PAGER_SYNCHRONOUS_MASK 0x07 /* Mask for four values above */
+#define PAGER_FULLFSYNC 0x08 /* PRAGMA fullfsync=ON */
+#define PAGER_CKPT_FULLFSYNC 0x10 /* PRAGMA checkpoint_fullfsync=ON */
+#define PAGER_CACHESPILL 0x20 /* PRAGMA cache_spill=ON */
+#define PAGER_FLAGS_MASK 0x38 /* All above except SYNCHRONOUS */
+
+/*
+** The remainder of this file contains the declarations of the functions
+** that make up the Pager sub-system API. See source code comments for
+** a detailed description of each routine.
+*/
+
+/* Open and close a Pager connection. */
+SQLITE_PRIVATE int sqlite3PagerOpen(
+ sqlite3_vfs*,
+ Pager **ppPager,
+ const char*,
+ int,
+ int,
+ int,
+ void(*)(DbPage*)
+);
+SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3*);
+SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
+
+/* Functions used to configure a Pager object. */
+SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *);
+SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int);
+SQLITE_PRIVATE Pgno sqlite3PagerMaxPageCount(Pager*, Pgno);
+SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int);
+SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int);
+SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64);
+SQLITE_PRIVATE void sqlite3PagerShrink(Pager*);
+SQLITE_PRIVATE void sqlite3PagerSetFlags(Pager*,unsigned);
+SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int);
+SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int);
+SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*);
+SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*);
+SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
+SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*);
+SQLITE_PRIVATE int sqlite3PagerFlush(Pager*);
+
+/* Functions used to obtain and release page references. */
+SQLITE_PRIVATE int sqlite3PagerGet(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
+SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
+SQLITE_PRIVATE void sqlite3PagerRef(DbPage*);
+SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*);
+SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage*);
+SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage*);
+
+/* Operations on page references. */
+SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*);
+SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*);
+SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int);
+SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*);
+SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *);
+SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *);
+
+/* Functions used to manage pager transactions and savepoints. */
+SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*);
+SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int);
+SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zSuper, int);
+SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*);
+SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zSuper);
+SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*);
+SQLITE_PRIVATE int sqlite3PagerRollback(Pager*);
+SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
+SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
+SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager);
+
+#ifndef SQLITE_OMIT_WAL
+SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
+SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager);
+SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager);
+SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
+SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
+# ifdef SQLITE_ENABLE_SNAPSHOT
+SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot);
+SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot);
+SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager);
+SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot);
+SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager);
+# endif
+#endif
+
+#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT)
+SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager*, int);
+SQLITE_PRIVATE void sqlite3PagerWalDb(Pager*, sqlite3*);
+#else
+# define sqlite3PagerWalWriteLock(y,z) SQLITE_OK
+# define sqlite3PagerWalDb(x,y)
+#endif
+
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno);
+#endif
+
+#ifdef SQLITE_ENABLE_ZIPVFS
+SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager);
+#endif
+
+/* Functions used to query pager state and configuration. */
+SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*);
+SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager*);
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*);
+#endif
+SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*);
+SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager*, int);
+SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*);
+SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*);
+SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*);
+SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
+SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
+SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
+SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, u64*);
+SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*);
+SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);
+
+/* Functions used to truncate the database file. */
+SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
+
+SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16);
+
+/* Functions to support testing and debugging. */
+#if !defined(NDEBUG) || defined(SQLITE_TEST)
+SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*);
+SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*);
+#endif
+#ifdef SQLITE_TEST
+SQLITE_PRIVATE int *sqlite3PagerStats(Pager*);
+SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*);
+ void disable_simulated_io_errors(void);
+ void enable_simulated_io_errors(void);
+#else
+# define disable_simulated_io_errors()
+# define enable_simulated_io_errors()
+#endif
+
+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
+SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager*);
+#endif
+
+#endif /* SQLITE_PAGER_H */
+
+/************** End of pager.h ***********************************************/
+/************** Continuing where we left off in sqliteInt.h ******************/
/************** Include btree.h in the middle of sqliteInt.h *****************/
/************** Begin file btree.h *******************************************/
/*
@@ -14291,30 +16016,38 @@ SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64);
SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(Btree*,unsigned);
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*);
-SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int);
-SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*);
+SQLITE_PRIVATE Pgno sqlite3BtreeMaxPageCount(Btree*,Pgno);
+SQLITE_PRIVATE Pgno sqlite3BtreeLastPage(Btree*);
SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int);
-SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree*);
+SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree*);
SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p);
SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int);
SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *);
SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int,int*);
-SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
+SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char*);
SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int);
SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*);
SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int,int);
SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int);
-SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags);
-SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*);
-SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*);
+SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, Pgno*, int flags);
+SQLITE_PRIVATE int sqlite3BtreeTxnState(Btree*);
SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*);
+
SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree);
#ifndef SQLITE_OMIT_SHARED_CACHE
SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock);
#endif
+
+/* Savepoints are named, nestable SQL transactions mostly implemented */
+/* in vdbe.c and pager.c See https://sqlite.org/lang_savepoint.html */
SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *, int, int);
+/* "Checkpoint" only refers to WAL. See https://sqlite.org/wal.html#ckpt */
+#ifndef SQLITE_OMIT_WAL
+SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
+#endif
+
SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *);
SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *);
SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *);
@@ -14335,7 +16068,7 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *);
#define BTREE_BLOBKEY 2 /* Table has keys only - no data */
SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*);
-SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*);
+SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, i64*);
SQLITE_PRIVATE int sqlite3BtreeClearTableOfCursor(BtCursor*);
SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree*, int, int);
@@ -14346,7 +16079,7 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p);
/*
** The second parameter to sqlite3BtreeGetMeta or sqlite3BtreeUpdateMeta
-** should be one of the following values. The integer values are assigned
+** should be one of the following values. The integer values are assigned
** to constants so that the offset of the corresponding field in an
** SQLite database header may be found using the following formula:
**
@@ -14395,7 +16128,7 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p);
** reduce network bandwidth.
**
** Note that BTREE_HINT_FLAGS with BTREE_BULKLOAD is the only hint used by
-** standard SQLite. The other hints are provided for extentions that use
+** standard SQLite. The other hints are provided for extensions that use
** the SQLite parser and code generator but substitute their own storage
** engine.
*/
@@ -14417,7 +16150,7 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p);
#define BTREE_BULKLOAD 0x00000001 /* Used to full index in sorted order */
#define BTREE_SEEK_EQ 0x00000002 /* EQ seeks only - no range seeks */
-/*
+/*
** Flags passed as the third argument to sqlite3BtreeCursor().
**
** For read-only cursors the wrFlag argument is always zero. For read-write
@@ -14445,7 +16178,7 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p);
SQLITE_PRIVATE int sqlite3BtreeCursor(
Btree*, /* BTree containing table to open */
- int iTable, /* Index of root page */
+ Pgno iTable, /* Index of root page */
int wrFlag, /* 1 for writing. 0 for read-only */
struct KeyInfo*, /* First argument to compare function */
BtCursor *pCursor /* Space to write cursor structure */
@@ -14459,13 +16192,17 @@ SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor*, int, ...);
#endif
SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*);
-SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
+SQLITE_PRIVATE int sqlite3BtreeTableMoveto(
BtCursor*,
- UnpackedRecord *pUnKey,
i64 intKey,
int bias,
int *pRes
);
+SQLITE_PRIVATE int sqlite3BtreeIndexMoveto(
+ BtCursor*,
+ UnpackedRecord *pUnKey,
+ int *pRes
+);
SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*);
SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor*, int*);
SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags);
@@ -14474,6 +16211,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags);
#define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
#define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
#define BTREE_APPEND 0x08 /* Insert is likely an append */
+#define BTREE_PREFORMAT 0x80 /* Inserted data is a preformated cell */
/* An instance of the BtreePayload object describes the content of a single
** entry in either an index or table btree.
@@ -14485,7 +16223,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags);
** The nMem field might be zero, indicating that no decomposition is available.
**
** Table btrees (used for rowid tables) contain an integer rowid used as
-** the key and passed in the nKey field. The pKey field is zero.
+** the key and passed in the nKey field. The pKey field is zero.
** pData,nData hold the content of the new entry. nZero extra zero bytes
** are appended to the end of the content when constructing the entry.
** The aMem,nMem fields are uninitialized for table btrees.
@@ -14504,7 +16242,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags);
**
** This object is used to pass information into sqlite3BtreeInsert(). The
** same information used to be passed as five separate parameters. But placing
-** the information into this object helps to keep the interface more
+** the information into this object helps to keep the interface more
** organized and understandable, and it also helps the resulting code to
** run a little faster by using fewer registers for parameter passing.
*/
@@ -14521,22 +16259,28 @@ struct BtreePayload {
SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
int flags, int seekResult);
SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes);
-#ifndef SQLITE_OMIT_WINDOWFUNC
-SQLITE_PRIVATE void sqlite3BtreeSkipNext(BtCursor*);
-#endif
SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes);
SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int flags);
SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*);
SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int flags);
SQLITE_PRIVATE i64 sqlite3BtreeIntegerKey(BtCursor*);
-#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
+SQLITE_PRIVATE void sqlite3BtreeCursorPin(BtCursor*);
+SQLITE_PRIVATE void sqlite3BtreeCursorUnpin(BtCursor*);
SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor*);
-#endif
SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*);
SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt);
SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*);
+SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*);
-SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
+SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
+ sqlite3 *db, /* Database connection that is running the check */
+ Btree *p, /* The btree to be checked */
+ Pgno *aRoot, /* An array of root pages numbers for individual trees */
+ int nRoot, /* Number of entries in aRoot[] */
+ int mxErr, /* Stop reporting errors after this many */
+ int *pnErr, /* OUT: Write number of errors seen to this variable */
+ char **pzOut /* OUT: Write the error message string here */
+);
SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*);
SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor*);
@@ -14551,14 +16295,18 @@ SQLITE_PRIVATE int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask);
SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *pBt);
SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void);
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree*);
+#else
+# define sqlite3BtreeSeekCount(X) 0
+#endif
+
#ifndef NDEBUG
SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*);
#endif
SQLITE_PRIVATE int sqlite3BtreeCursorIsValidNN(BtCursor*);
-#ifndef SQLITE_OMIT_BTREECOUNT
-SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *, i64 *);
-#endif
+SQLITE_PRIVATE int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*);
#ifdef SQLITE_TEST
SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
@@ -14569,6 +16317,10 @@ SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*);
SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
#endif
+SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor*, BtCursor*, i64);
+
+SQLITE_PRIVATE void sqlite3BtreeClearCache(Btree*);
+
/*
** If we are not using shared cache, then there is no need to
** use mutexes to access the BtShared structures. So make the
@@ -14581,7 +16333,7 @@ SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*);
SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*);
SQLITE_PRIVATE int sqlite3BtreeConnectionCount(Btree*);
#else
-# define sqlite3BtreeEnter(X)
+# define sqlite3BtreeEnter(X)
# define sqlite3BtreeEnterAll(X)
# define sqlite3BtreeSharable(X) 0
# define sqlite3BtreeEnterCursor(X)
@@ -14675,25 +16427,24 @@ struct VdbeOp {
Mem *pMem; /* Used when p4type is P4_MEM */
VTable *pVtab; /* Used when p4type is P4_VTAB */
KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
- int *ai; /* Used when p4type is P4_INTARRAY */
+ u32 *ai; /* Used when p4type is P4_INTARRAY */
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
Table *pTab; /* Used when p4type is P4_TABLE */
#ifdef SQLITE_ENABLE_CURSOR_HINTS
Expr *pExpr; /* Used when p4type is P4_EXPR */
#endif
- int (*xAdvance)(BtCursor *, int);
} p4;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
char *zComment; /* Comment to improve readability */
#endif
-#ifdef VDBE_PROFILE
- u32 cnt; /* Number of times this instruction was executed */
- u64 cycles; /* Total time spent executing this instruction */
-#endif
#ifdef SQLITE_VDBE_COVERAGE
u32 iSrcLine; /* Source-code line that generated this opcode
** with flags in the upper 8 bits */
#endif
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
+ u64 nExec;
+ u64 nCycle;
+#endif
};
typedef struct VdbeOp VdbeOp;
@@ -14732,21 +16483,20 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_COLLSEQ (-2) /* P4 is a pointer to a CollSeq structure */
#define P4_INT32 (-3) /* P4 is a 32-bit signed integer */
#define P4_SUBPROGRAM (-4) /* P4 is a pointer to a SubProgram structure */
-#define P4_ADVANCE (-5) /* P4 is a pointer to BtreeNext() or BtreePrev() */
-#define P4_TABLE (-6) /* P4 is a pointer to a Table structure */
+#define P4_TABLE (-5) /* P4 is a pointer to a Table structure */
/* Above do not own any resources. Must free those below */
-#define P4_FREE_IF_LE (-7)
-#define P4_DYNAMIC (-7) /* Pointer to memory from sqliteMalloc() */
-#define P4_FUNCDEF (-8) /* P4 is a pointer to a FuncDef structure */
-#define P4_KEYINFO (-9) /* P4 is a pointer to a KeyInfo structure */
-#define P4_EXPR (-10) /* P4 is a pointer to an Expr tree */
-#define P4_MEM (-11) /* P4 is a pointer to a Mem* structure */
-#define P4_VTAB (-12) /* P4 is a pointer to an sqlite3_vtab structure */
-#define P4_REAL (-13) /* P4 is a 64-bit floating point value */
-#define P4_INT64 (-14) /* P4 is a 64-bit signed integer */
-#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
-#define P4_FUNCCTX (-16) /* P4 is a pointer to an sqlite3_context object */
-#define P4_DYNBLOB (-17) /* Pointer to memory from sqliteMalloc() */
+#define P4_FREE_IF_LE (-6)
+#define P4_DYNAMIC (-6) /* Pointer to memory from sqliteMalloc() */
+#define P4_FUNCDEF (-7) /* P4 is a pointer to a FuncDef structure */
+#define P4_KEYINFO (-8) /* P4 is a pointer to a KeyInfo structure */
+#define P4_EXPR (-9) /* P4 is a pointer to an Expr tree */
+#define P4_MEM (-10) /* P4 is a pointer to a Mem* structure */
+#define P4_VTAB (-11) /* P4 is a pointer to an sqlite3_vtab structure */
+#define P4_REAL (-12) /* P4 is a 64-bit floating point value */
+#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */
+#define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */
+#define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */
+#define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */
/* Error message codes for OP_Halt */
#define P5_ConstraintNotNull 1
@@ -14755,7 +16505,7 @@ typedef struct VdbeOpList VdbeOpList;
#define P5_ConstraintFK 4
/*
-** The Vdbe.aColName array contains 5n Mem structures, where n is the
+** The Vdbe.aColName array contains 5n Mem structures, where n is the
** number of columns of data returned by the statement.
*/
#define COLNAME_NAME 0
@@ -14774,12 +16524,11 @@ typedef struct VdbeOpList VdbeOpList;
#endif
/*
-** The following macro converts a relative address in the p2 field
-** of a VdbeOp structure into a negative number so that
-** sqlite3VdbeAddOpList() knows that the address is relative. Calling
-** the macro again restores the address.
+** The following macro converts a label returned by sqlite3VdbeMakeLabel()
+** into an index into the Parse.aLabel[] array that contains the resolved
+** address of that label.
*/
-#define ADDR(X) (-1-(X))
+#define ADDR(X) (~(X))
/*
** The makefile scans the vdbe.c source file and creates the "opcodes.h"
@@ -14792,29 +16541,29 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Savepoint 0
#define OP_AutoCommit 1
#define OP_Transaction 2
-#define OP_SorterNext 3 /* jump */
-#define OP_Prev 4 /* jump */
-#define OP_Next 5 /* jump */
-#define OP_Checkpoint 6
-#define OP_JournalMode 7
-#define OP_Vacuum 8
-#define OP_VFilter 9 /* jump, synopsis: iplan=r[P3] zplan='P4' */
-#define OP_VUpdate 10 /* synopsis: data=r[P3@P2] */
-#define OP_Goto 11 /* jump */
-#define OP_Gosub 12 /* jump */
-#define OP_InitCoroutine 13 /* jump */
-#define OP_Yield 14 /* jump */
-#define OP_MustBeInt 15 /* jump */
-#define OP_Jump 16 /* jump */
-#define OP_Once 17 /* jump */
-#define OP_If 18 /* jump */
+#define OP_Checkpoint 3
+#define OP_JournalMode 4
+#define OP_Vacuum 5
+#define OP_VFilter 6 /* jump, synopsis: iplan=r[P3] zplan='P4' */
+#define OP_VUpdate 7 /* synopsis: data=r[P3@P2] */
+#define OP_Init 8 /* jump, synopsis: Start at P2 */
+#define OP_Goto 9 /* jump */
+#define OP_Gosub 10 /* jump */
+#define OP_InitCoroutine 11 /* jump */
+#define OP_Yield 12 /* jump */
+#define OP_MustBeInt 13 /* jump */
+#define OP_Jump 14 /* jump */
+#define OP_Once 15 /* jump */
+#define OP_If 16 /* jump */
+#define OP_IfNot 17 /* jump */
+#define OP_IsType 18 /* jump, synopsis: if typeof(P1.P3) in P5 goto P2 */
#define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */
-#define OP_IfNot 20 /* jump */
-#define OP_IfNullRow 21 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */
-#define OP_SeekLT 22 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekLE 23 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekGE 24 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekGT 25 /* jump, synopsis: key=r[P3@P4] */
+#define OP_IfNullRow 20 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */
+#define OP_SeekLT 21 /* jump, synopsis: key=r[P3@P4] */
+#define OP_SeekLE 22 /* jump, synopsis: key=r[P3@P4] */
+#define OP_SeekGE 23 /* jump, synopsis: key=r[P3@P4] */
+#define OP_SeekGT 24 /* jump, synopsis: key=r[P3@P4] */
+#define OP_IfNotOpen 25 /* jump, synopsis: if( !csr[P1] ) goto P2 */
#define OP_IfNoHope 26 /* jump, synopsis: key=r[P3@P4] */
#define OP_NoConflict 27 /* jump, synopsis: key=r[P3@P4] */
#define OP_NotFound 28 /* jump, synopsis: key=r[P3@P4] */
@@ -14826,19 +16575,19 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_SorterSort 34 /* jump */
#define OP_Sort 35 /* jump */
#define OP_Rewind 36 /* jump */
-#define OP_IdxLE 37 /* jump, synopsis: key=r[P3@P4] */
-#define OP_IdxGT 38 /* jump, synopsis: key=r[P3@P4] */
-#define OP_IdxLT 39 /* jump, synopsis: key=r[P3@P4] */
-#define OP_IdxGE 40 /* jump, synopsis: key=r[P3@P4] */
-#define OP_RowSetRead 41 /* jump, synopsis: r[P3]=rowset(P1) */
-#define OP_RowSetTest 42 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */
+#define OP_SorterNext 37 /* jump */
+#define OP_Prev 38 /* jump */
+#define OP_Next 39 /* jump */
+#define OP_IdxLE 40 /* jump, synopsis: key=r[P3@P4] */
+#define OP_IdxGT 41 /* jump, synopsis: key=r[P3@P4] */
+#define OP_IdxLT 42 /* jump, synopsis: key=r[P3@P4] */
#define OP_Or 43 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */
#define OP_And 44 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */
-#define OP_Program 45 /* jump */
-#define OP_FkIfZero 46 /* jump, synopsis: if fkctr[P1]==0 goto P2 */
-#define OP_IfPos 47 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
-#define OP_IfNotZero 48 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */
-#define OP_DecrJumpZero 49 /* jump, synopsis: if (--r[P1])==0 goto P2 */
+#define OP_IdxGE 45 /* jump, synopsis: key=r[P3@P4] */
+#define OP_RowSetRead 46 /* jump, synopsis: r[P3]=rowset(P1) */
+#define OP_RowSetTest 47 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */
+#define OP_Program 48 /* jump */
+#define OP_FkIfZero 49 /* jump, synopsis: if fkctr[P1]==0 goto P2 */
#define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */
#define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */
#define OP_Ne 52 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */
@@ -14847,122 +16596,138 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Le 55 /* jump, same as TK_LE, synopsis: IF r[P3]<=r[P1] */
#define OP_Lt 56 /* jump, same as TK_LT, synopsis: IF r[P3]<r[P1] */
#define OP_Ge 57 /* jump, same as TK_GE, synopsis: IF r[P3]>=r[P1] */
-#define OP_ElseNotEq 58 /* jump, same as TK_ESCAPE */
-#define OP_IncrVacuum 59 /* jump */
-#define OP_VNext 60 /* jump */
-#define OP_Init 61 /* jump, synopsis: Start at P2 */
-#define OP_PureFunc0 62
-#define OP_Function0 63 /* synopsis: r[P3]=func(r[P2@P5]) */
-#define OP_PureFunc 64
-#define OP_Function 65 /* synopsis: r[P3]=func(r[P2@P5]) */
-#define OP_Return 66
-#define OP_EndCoroutine 67
-#define OP_HaltIfNull 68 /* synopsis: if r[P3]=null halt */
-#define OP_Halt 69
-#define OP_Integer 70 /* synopsis: r[P2]=P1 */
-#define OP_Int64 71 /* synopsis: r[P2]=P4 */
-#define OP_String 72 /* synopsis: r[P2]='P4' (len=P1) */
-#define OP_Null 73 /* synopsis: r[P2..P3]=NULL */
-#define OP_SoftNull 74 /* synopsis: r[P1]=NULL */
-#define OP_Blob 75 /* synopsis: r[P2]=P4 (len=P1) */
-#define OP_Variable 76 /* synopsis: r[P2]=parameter(P1,P4) */
-#define OP_Move 77 /* synopsis: r[P2@P3]=r[P1@P3] */
-#define OP_Copy 78 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
-#define OP_SCopy 79 /* synopsis: r[P2]=r[P1] */
-#define OP_IntCopy 80 /* synopsis: r[P2]=r[P1] */
-#define OP_ResultRow 81 /* synopsis: output=r[P1@P2] */
-#define OP_CollSeq 82
-#define OP_AddImm 83 /* synopsis: r[P1]=r[P1]+P2 */
-#define OP_RealAffinity 84
-#define OP_Cast 85 /* synopsis: affinity(r[P1]) */
-#define OP_Permutation 86
-#define OP_Compare 87 /* synopsis: r[P1@P3] <-> r[P2@P3] */
-#define OP_IsTrue 88 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */
-#define OP_Offset 89 /* synopsis: r[P3] = sqlite_offset(P1) */
-#define OP_Column 90 /* synopsis: r[P3]=PX */
-#define OP_Affinity 91 /* synopsis: affinity(r[P1@P2]) */
-#define OP_BitAnd 92 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
-#define OP_BitOr 93 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
-#define OP_ShiftLeft 94 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
-#define OP_ShiftRight 95 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
-#define OP_Add 96 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
-#define OP_Subtract 97 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
-#define OP_Multiply 98 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
-#define OP_Divide 99 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
-#define OP_Remainder 100 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
-#define OP_Concat 101 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
-#define OP_MakeRecord 102 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
-#define OP_BitNot 103 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */
-#define OP_Count 104 /* synopsis: r[P2]=count() */
-#define OP_ReadCookie 105
-#define OP_String8 106 /* same as TK_STRING, synopsis: r[P2]='P4' */
-#define OP_SetCookie 107
-#define OP_ReopenIdx 108 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenRead 109 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenWrite 110 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenDup 111
-#define OP_OpenAutoindex 112 /* synopsis: nColumn=P2 */
-#define OP_OpenEphemeral 113 /* synopsis: nColumn=P2 */
-#define OP_SorterOpen 114
-#define OP_SequenceTest 115 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
-#define OP_OpenPseudo 116 /* synopsis: P3 columns in r[P2] */
-#define OP_Close 117
-#define OP_ColumnsUsed 118
-#define OP_SeekHit 119 /* synopsis: seekHit=P2 */
-#define OP_Sequence 120 /* synopsis: r[P2]=cursor[P1].ctr++ */
-#define OP_NewRowid 121 /* synopsis: r[P2]=rowid */
-#define OP_Insert 122 /* synopsis: intkey=r[P3] data=r[P2] */
-#define OP_InsertInt 123 /* synopsis: intkey=P3 data=r[P2] */
-#define OP_Delete 124
-#define OP_ResetCount 125
-#define OP_SorterCompare 126 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
-#define OP_SorterData 127 /* synopsis: r[P2]=data */
-#define OP_RowData 128 /* synopsis: r[P2]=data */
-#define OP_Rowid 129 /* synopsis: r[P2]=rowid */
-#define OP_NullRow 130
-#define OP_SeekEnd 131
-#define OP_SorterInsert 132 /* synopsis: key=r[P2] */
-#define OP_IdxInsert 133 /* synopsis: key=r[P2] */
-#define OP_IdxDelete 134 /* synopsis: key=r[P2@P3] */
-#define OP_DeferredSeek 135 /* synopsis: Move P3 to P1.rowid if needed */
-#define OP_IdxRowid 136 /* synopsis: r[P2]=rowid */
-#define OP_Destroy 137
-#define OP_Clear 138
-#define OP_ResetSorter 139
-#define OP_CreateBtree 140 /* synopsis: r[P2]=root iDb=P1 flags=P3 */
-#define OP_Real 141 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
-#define OP_SqlExec 142
-#define OP_ParseSchema 143
-#define OP_LoadAnalysis 144
-#define OP_DropTable 145
-#define OP_DropIndex 146
-#define OP_DropTrigger 147
-#define OP_IntegrityCk 148
-#define OP_RowSetAdd 149 /* synopsis: rowset(P1)=r[P2] */
-#define OP_Param 150
-#define OP_FkCounter 151 /* synopsis: fkctr[P1]+=P2 */
-#define OP_MemMax 152 /* synopsis: r[P1]=max(r[P1],r[P2]) */
-#define OP_OffsetLimit 153 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
-#define OP_AggInverse 154 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */
-#define OP_AggStep 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */
-#define OP_AggStep1 156 /* synopsis: accum=r[P3] step(r[P2@P5]) */
-#define OP_AggValue 157 /* synopsis: r[P3]=value N=P2 */
-#define OP_AggFinal 158 /* synopsis: accum=r[P1] N=P2 */
-#define OP_Expire 159
-#define OP_TableLock 160 /* synopsis: iDb=P1 root=P2 write=P3 */
-#define OP_VBegin 161
-#define OP_VCreate 162
-#define OP_VDestroy 163
-#define OP_VOpen 164
-#define OP_VColumn 165 /* synopsis: r[P3]=vcolumn(P2) */
-#define OP_VRename 166
-#define OP_Pagecount 167
-#define OP_MaxPgcnt 168
-#define OP_Trace 169
-#define OP_CursorHint 170
-#define OP_Noop 171
-#define OP_Explain 172
-#define OP_Abortable 173
+#define OP_ElseEq 58 /* jump, same as TK_ESCAPE */
+#define OP_IfPos 59 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
+#define OP_IfNotZero 60 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */
+#define OP_DecrJumpZero 61 /* jump, synopsis: if (--r[P1])==0 goto P2 */
+#define OP_IncrVacuum 62 /* jump */
+#define OP_VNext 63 /* jump */
+#define OP_Filter 64 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */
+#define OP_PureFunc 65 /* synopsis: r[P3]=func(r[P2@NP]) */
+#define OP_Function 66 /* synopsis: r[P3]=func(r[P2@NP]) */
+#define OP_Return 67
+#define OP_EndCoroutine 68
+#define OP_HaltIfNull 69 /* synopsis: if r[P3]=null halt */
+#define OP_Halt 70
+#define OP_Integer 71 /* synopsis: r[P2]=P1 */
+#define OP_Int64 72 /* synopsis: r[P2]=P4 */
+#define OP_String 73 /* synopsis: r[P2]='P4' (len=P1) */
+#define OP_BeginSubrtn 74 /* synopsis: r[P2]=NULL */
+#define OP_Null 75 /* synopsis: r[P2..P3]=NULL */
+#define OP_SoftNull 76 /* synopsis: r[P1]=NULL */
+#define OP_Blob 77 /* synopsis: r[P2]=P4 (len=P1) */
+#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1,P4) */
+#define OP_Move 79 /* synopsis: r[P2@P3]=r[P1@P3] */
+#define OP_Copy 80 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
+#define OP_SCopy 81 /* synopsis: r[P2]=r[P1] */
+#define OP_IntCopy 82 /* synopsis: r[P2]=r[P1] */
+#define OP_FkCheck 83
+#define OP_ResultRow 84 /* synopsis: output=r[P1@P2] */
+#define OP_CollSeq 85
+#define OP_AddImm 86 /* synopsis: r[P1]=r[P1]+P2 */
+#define OP_RealAffinity 87
+#define OP_Cast 88 /* synopsis: affinity(r[P1]) */
+#define OP_Permutation 89
+#define OP_Compare 90 /* synopsis: r[P1@P3] <-> r[P2@P3] */
+#define OP_IsTrue 91 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */
+#define OP_ZeroOrNull 92 /* synopsis: r[P2] = 0 OR NULL */
+#define OP_Offset 93 /* synopsis: r[P3] = sqlite_offset(P1) */
+#define OP_Column 94 /* synopsis: r[P3]=PX cursor P1 column P2 */
+#define OP_TypeCheck 95 /* synopsis: typecheck(r[P1@P2]) */
+#define OP_Affinity 96 /* synopsis: affinity(r[P1@P2]) */
+#define OP_MakeRecord 97 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
+#define OP_Count 98 /* synopsis: r[P2]=count() */
+#define OP_ReadCookie 99
+#define OP_SetCookie 100
+#define OP_ReopenIdx 101 /* synopsis: root=P2 iDb=P3 */
+#define OP_BitAnd 102 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
+#define OP_BitOr 103 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
+#define OP_ShiftLeft 104 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
+#define OP_ShiftRight 105 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
+#define OP_Add 106 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
+#define OP_Subtract 107 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
+#define OP_Multiply 108 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
+#define OP_Divide 109 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
+#define OP_Remainder 110 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
+#define OP_Concat 111 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
+#define OP_OpenRead 112 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenWrite 113 /* synopsis: root=P2 iDb=P3 */
+#define OP_BitNot 114 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */
+#define OP_OpenDup 115
+#define OP_OpenAutoindex 116 /* synopsis: nColumn=P2 */
+#define OP_String8 117 /* same as TK_STRING, synopsis: r[P2]='P4' */
+#define OP_OpenEphemeral 118 /* synopsis: nColumn=P2 */
+#define OP_SorterOpen 119
+#define OP_SequenceTest 120 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
+#define OP_OpenPseudo 121 /* synopsis: P3 columns in r[P2] */
+#define OP_Close 122
+#define OP_ColumnsUsed 123
+#define OP_SeekScan 124 /* synopsis: Scan-ahead up to P1 rows */
+#define OP_SeekHit 125 /* synopsis: set P2<=seekHit<=P3 */
+#define OP_Sequence 126 /* synopsis: r[P2]=cursor[P1].ctr++ */
+#define OP_NewRowid 127 /* synopsis: r[P2]=rowid */
+#define OP_Insert 128 /* synopsis: intkey=r[P3] data=r[P2] */
+#define OP_RowCell 129
+#define OP_Delete 130
+#define OP_ResetCount 131
+#define OP_SorterCompare 132 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
+#define OP_SorterData 133 /* synopsis: r[P2]=data */
+#define OP_RowData 134 /* synopsis: r[P2]=data */
+#define OP_Rowid 135 /* synopsis: r[P2]=PX rowid of P1 */
+#define OP_NullRow 136
+#define OP_SeekEnd 137
+#define OP_IdxInsert 138 /* synopsis: key=r[P2] */
+#define OP_SorterInsert 139 /* synopsis: key=r[P2] */
+#define OP_IdxDelete 140 /* synopsis: key=r[P2@P3] */
+#define OP_DeferredSeek 141 /* synopsis: Move P3 to P1.rowid if needed */
+#define OP_IdxRowid 142 /* synopsis: r[P2]=rowid */
+#define OP_FinishSeek 143
+#define OP_Destroy 144
+#define OP_Clear 145
+#define OP_ResetSorter 146
+#define OP_CreateBtree 147 /* synopsis: r[P2]=root iDb=P1 flags=P3 */
+#define OP_SqlExec 148
+#define OP_ParseSchema 149
+#define OP_LoadAnalysis 150
+#define OP_DropTable 151
+#define OP_DropIndex 152
+#define OP_Real 153 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
+#define OP_DropTrigger 154
+#define OP_IntegrityCk 155
+#define OP_RowSetAdd 156 /* synopsis: rowset(P1)=r[P2] */
+#define OP_Param 157
+#define OP_FkCounter 158 /* synopsis: fkctr[P1]+=P2 */
+#define OP_MemMax 159 /* synopsis: r[P1]=max(r[P1],r[P2]) */
+#define OP_OffsetLimit 160 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
+#define OP_AggInverse 161 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */
+#define OP_AggStep 162 /* synopsis: accum=r[P3] step(r[P2@P5]) */
+#define OP_AggStep1 163 /* synopsis: accum=r[P3] step(r[P2@P5]) */
+#define OP_AggValue 164 /* synopsis: r[P3]=value N=P2 */
+#define OP_AggFinal 165 /* synopsis: accum=r[P1] N=P2 */
+#define OP_Expire 166
+#define OP_CursorLock 167
+#define OP_CursorUnlock 168
+#define OP_TableLock 169 /* synopsis: iDb=P1 root=P2 write=P3 */
+#define OP_VBegin 170
+#define OP_VCreate 171
+#define OP_VDestroy 172
+#define OP_VOpen 173
+#define OP_VCheck 174
+#define OP_VInitIn 175 /* synopsis: r[P2]=ValueList(P1,P3) */
+#define OP_VColumn 176 /* synopsis: r[P3]=vcolumn(P2) */
+#define OP_VRename 177
+#define OP_Pagecount 178
+#define OP_MaxPgcnt 179
+#define OP_ClrSubtype 180 /* synopsis: r[P1].subtype = 0 */
+#define OP_GetSubtype 181 /* synopsis: r[P2] = r[P1].subtype */
+#define OP_SetSubtype 182 /* synopsis: r[P2].subtype = r[P1] */
+#define OP_FilterAdd 183 /* synopsis: filter(P1) += key(P3@P4) */
+#define OP_Trace 184
+#define OP_CursorHint 185
+#define OP_ReleaseReg 186 /* synopsis: release r[P1@P2] mask P3 */
+#define OP_Noop 187
+#define OP_Explain 188
+#define OP_Abortable 189
/* Properties such as "out2" or "jump" that are specified in
** comments following the "case" for each opcode in the vdbe.c
@@ -14974,37 +16739,40 @@ typedef struct VdbeOpList VdbeOpList;
#define OPFLG_IN3 0x08 /* in3: P3 is an input */
#define OPFLG_OUT2 0x10 /* out2: P2 is an output */
#define OPFLG_OUT3 0x20 /* out3: P3 is an output */
+#define OPFLG_NCYCLE 0x40 /* ncycle:Cycles count against P1 */
#define OPFLG_INITIALIZER {\
-/* 0 */ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x10,\
-/* 8 */ 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x03, 0x03,\
-/* 16 */ 0x01, 0x01, 0x03, 0x12, 0x03, 0x01, 0x09, 0x09,\
-/* 24 */ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\
-/* 32 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\
-/* 40 */ 0x01, 0x23, 0x0b, 0x26, 0x26, 0x01, 0x01, 0x03,\
-/* 48 */ 0x03, 0x03, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
-/* 56 */ 0x0b, 0x0b, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,\
-/* 64 */ 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10, 0x10,\
-/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,\
-/* 80 */ 0x10, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\
-/* 88 */ 0x12, 0x20, 0x00, 0x00, 0x26, 0x26, 0x26, 0x26,\
-/* 96 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x12,\
-/* 104 */ 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 112 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 128 */ 0x00, 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,\
-/* 136 */ 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
-/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00,\
-/* 152 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\
-/* 168 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,}
-
-/* The sqlite3P2Values() routine is able to run faster if it knows
+/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x41, 0x00,\
+/* 8 */ 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x01, 0x01,\
+/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x49, 0x49, 0x49,\
+/* 24 */ 0x49, 0x01, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,\
+/* 32 */ 0x41, 0x01, 0x41, 0x41, 0x41, 0x01, 0x41, 0x41,\
+/* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\
+/* 48 */ 0x01, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
+/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01, 0x41,\
+/* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\
+/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10, 0x00,\
+/* 80 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02,\
+/* 88 */ 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x40, 0x00,\
+/* 96 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x40, 0x26, 0x26,\
+/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\
+/* 112 */ 0x40, 0x00, 0x12, 0x40, 0x40, 0x10, 0x40, 0x00,\
+/* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\
+/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50,\
+/* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\
+/* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
+/* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\
+/* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\
+/* 176 */ 0x40, 0x00, 0x10, 0x10, 0x02, 0x12, 0x12, 0x00,\
+/* 184 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}
+
+/* The resolve3P2Values() routine is able to run faster if it knows
** the value of the largest JUMP opcode. The smaller the maximum
** JUMP opcode the better, so the mkopcodeh.tcl script that
** generated this include file strives to group all JUMP opcodes
** together near the beginning of the list.
*/
-#define SQLITE_MX_JUMP_OPCODE 61 /* Maximum JUMP opcode */
+#define SQLITE_MX_JUMP_OPCODE 64 /* Maximum JUMP opcode */
/************** End of opcodes.h *********************************************/
/************** Continuing where we left off in vdbe.h ***********************/
@@ -15020,6 +16788,7 @@ typedef struct VdbeOpList VdbeOpList;
** for a description of what each of these routines does.
*/
SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse*);
+SQLITE_PRIVATE Parse *sqlite3VdbeParser(Vdbe*);
SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int);
@@ -15030,6 +16799,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp4Dup8(Vdbe*,int,int,int,int,const u8*,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int);
+SQLITE_PRIVATE int sqlite3VdbeAddFunctionCall(Parse*,int,int,int,int,const FuncDef*,int);
SQLITE_PRIVATE void sqlite3VdbeEndCoroutine(Vdbe*,int);
#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
SQLITE_PRIVATE void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N);
@@ -15040,41 +16810,62 @@ SQLITE_PRIVATE void sqlite3VdbeVerifyNoResultRow(Vdbe *p);
#endif
#if defined(SQLITE_DEBUG)
SQLITE_PRIVATE void sqlite3VdbeVerifyAbortable(Vdbe *p, int);
+SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn(Vdbe*,int,int,int);
#else
# define sqlite3VdbeVerifyAbortable(A,B)
+# define sqlite3VdbeNoJumpsOutsideSubrtn(A,B,C,D)
#endif
SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
#ifndef SQLITE_OMIT_EXPLAIN
-SQLITE_PRIVATE void sqlite3VdbeExplain(Parse*,u8,const char*,...);
+SQLITE_PRIVATE int sqlite3VdbeExplain(Parse*,u8,const char*,...);
SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse*);
SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse*);
# define ExplainQueryPlan(P) sqlite3VdbeExplain P
+# ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+# define ExplainQueryPlan2(V,P) (V = sqlite3VdbeExplain P)
+# else
+# define ExplainQueryPlan2(V,P) ExplainQueryPlan(P)
+# endif
# define ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P)
# define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P)
#else
# define ExplainQueryPlan(P)
+# define ExplainQueryPlan2(V,P)
# define ExplainQueryPlanPop(P)
# define ExplainQueryPlanParent(P) 0
+# define sqlite3ExplainBreakpoint(A,B) /*no-op*/
+#endif
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN)
+SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char*,const char*);
+#else
+# define sqlite3ExplainBreakpoint(A,B) /*no-op*/
#endif
-SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
-SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
-SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
-SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
-SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
+SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*, int, char*, u16);
+SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, int addr, u8);
+SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
+SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
+SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3);
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u16 P5);
+SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe*, int);
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr);
+SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe*, int addr);
SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe*, int addr);
SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op);
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE void sqlite3VdbeReleaseRegisters(Parse*,int addr, int n, u32 mask, int);
+#else
+# define sqlite3VdbeReleaseRegisters(P,A,N,M,F)
+#endif
SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
SQLITE_PRIVATE void sqlite3VdbeAppendP4(Vdbe*, void *pP4, int p4type);
SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);
SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int);
SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
-SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*);
+SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe*);
+SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Parse*);
SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*);
-SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3*,Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,Parse*);
SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int);
@@ -15091,6 +16882,10 @@ SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe*);
SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*);
SQLITE_PRIVATE u8 sqlite3VdbePrepareFlags(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, u8);
+#ifdef SQLITE_ENABLE_NORMALIZE
+SQLITE_PRIVATE void sqlite3VdbeAddDblquoteStr(sqlite3*,Vdbe*,const char*);
+SQLITE_PRIVATE int sqlite3VdbeUsesDoubleQuotedString(Vdbe*,const char*);
+#endif
SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*);
SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*);
SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8);
@@ -15109,11 +16904,13 @@ SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*);
typedef int (*RecordCompare)(int,const void*,UnpackedRecord*);
SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);
-#ifndef SQLITE_OMIT_TRIGGER
SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
-#endif
+SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*);
SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
+#ifdef SQLITE_ENABLE_BYTECODE_VTAB
+SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
+#endif
/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
** each VDBE opcode.
@@ -15142,7 +16939,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
** The VdbeCoverage macros are used to set a coverage testing point
** for VDBE branch instructions. The coverage testing points are line
** numbers in the sqlite3.c source file. VDBE branch coverage testing
-** only works with an amalagmation build. That's ok since a VDBE branch
+** only works with an amalgamation build. That's ok since a VDBE branch
** coverage build designed for testing the test suite only. No application
** should ever ship with VDBE branch coverage measuring turned on.
**
@@ -15160,7 +16957,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
** // NULL option is not possible
**
** VdbeCoverageEqNe(v) // Previous OP_Jump is only interested
-** // in distingishing equal and not-equal.
+** // in distinguishing equal and not-equal.
**
** Every VDBE branch operation must be tagged with one of the macros above.
** If not, then when "make test" is run with -DSQLITE_VDBE_COVERAGE and
@@ -15170,7 +16967,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
** During testing, the test application will invoke
** sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE,...) to set a callback
** routine that is invoked as each bytecode branch is taken. The callback
-** contains the sqlite3.c source line number ov the VdbeCoverage macro and
+** contains the sqlite3.c source line number of the VdbeCoverage macro and
** flags to indicate whether or not the branch was taken. The test application
** is responsible for keeping track of this and reporting byte-code branches
** that are never taken.
@@ -15206,268 +17003,25 @@ SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe*,int);
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
SQLITE_PRIVATE void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*);
+SQLITE_PRIVATE void sqlite3VdbeScanStatusRange(Vdbe*, int, int, int);
+SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters(Vdbe*, int, int, int);
#else
-# define sqlite3VdbeScanStatus(a,b,c,d,e)
+# define sqlite3VdbeScanStatus(a,b,c,d,e,f)
+# define sqlite3VdbeScanStatusRange(a,b,c,d)
+# define sqlite3VdbeScanStatusCounters(a,b,c,d)
#endif
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, VdbeOp*);
#endif
-#endif /* SQLITE_VDBE_H */
-
-/************** End of vdbe.h ************************************************/
-/************** Continuing where we left off in sqliteInt.h ******************/
-/************** Include pager.h in the middle of sqliteInt.h *****************/
-/************** Begin file pager.h *******************************************/
-/*
-** 2001 September 15
-**
-** 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 defines the interface that the sqlite page cache
-** subsystem. The page cache subsystem reads and writes a file a page
-** at a time and provides a journal for rollback.
-*/
-
-#ifndef SQLITE_PAGER_H
-#define SQLITE_PAGER_H
-
-/*
-** Default maximum size for persistent journal files. A negative
-** value means no limit. This value may be overridden using the
-** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit".
-*/
-#ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT
- #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1
+#if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG)
+SQLITE_PRIVATE int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr);
#endif
-/*
-** The type used to represent a page number. The first page in a file
-** is called page 1. 0 is used to represent "not a page".
-*/
-typedef u32 Pgno;
-
-/*
-** Each open file is managed by a separate instance of the "Pager" structure.
-*/
-typedef struct Pager Pager;
-
-/*
-** Handle type for pages.
-*/
-typedef struct PgHdr DbPage;
-
-/*
-** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is
-** reserved for working around a windows/posix incompatibility). It is
-** used in the journal to signify that the remainder of the journal file
-** is devoted to storing a master journal name - there are no more pages to
-** roll back. See comments for function writeMasterJournal() in pager.c
-** for details.
-*/
-#define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1))
-
-/*
-** Allowed values for the flags parameter to sqlite3PagerOpen().
-**
-** NOTE: These values must match the corresponding BTREE_ values in btree.h.
-*/
-#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */
-#define PAGER_MEMORY 0x0002 /* In-memory database */
-
-/*
-** Valid values for the second argument to sqlite3PagerLockingMode().
-*/
-#define PAGER_LOCKINGMODE_QUERY -1
-#define PAGER_LOCKINGMODE_NORMAL 0
-#define PAGER_LOCKINGMODE_EXCLUSIVE 1
-
-/*
-** Numeric constants that encode the journalmode.
-**
-** The numeric values encoded here (other than PAGER_JOURNALMODE_QUERY)
-** are exposed in the API via the "PRAGMA journal_mode" command and
-** therefore cannot be changed without a compatibility break.
-*/
-#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */
-#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */
-#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */
-#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */
-#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */
-#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */
-#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
-
-/*
-** Flags that make up the mask passed to sqlite3PagerGet().
-*/
-#define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */
-#define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */
-
-/*
-** Flags for sqlite3PagerSetFlags()
-**
-** Value constraints (enforced via assert()):
-** PAGER_FULLFSYNC == SQLITE_FullFSync
-** PAGER_CKPT_FULLFSYNC == SQLITE_CkptFullFSync
-** PAGER_CACHE_SPILL == SQLITE_CacheSpill
-*/
-#define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */
-#define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */
-#define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */
-#define PAGER_SYNCHRONOUS_EXTRA 0x04 /* PRAGMA synchronous=EXTRA */
-#define PAGER_SYNCHRONOUS_MASK 0x07 /* Mask for four values above */
-#define PAGER_FULLFSYNC 0x08 /* PRAGMA fullfsync=ON */
-#define PAGER_CKPT_FULLFSYNC 0x10 /* PRAGMA checkpoint_fullfsync=ON */
-#define PAGER_CACHESPILL 0x20 /* PRAGMA cache_spill=ON */
-#define PAGER_FLAGS_MASK 0x38 /* All above except SYNCHRONOUS */
-
-/*
-** The remainder of this file contains the declarations of the functions
-** that make up the Pager sub-system API. See source code comments for
-** a detailed description of each routine.
-*/
-
-/* Open and close a Pager connection. */
-SQLITE_PRIVATE int sqlite3PagerOpen(
- sqlite3_vfs*,
- Pager **ppPager,
- const char*,
- int,
- int,
- int,
- void(*)(DbPage*)
-);
-SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3*);
-SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
-
-/* Functions used to configure a Pager object. */
-SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *);
-SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int);
-#ifdef SQLITE_HAS_CODEC
-SQLITE_PRIVATE void sqlite3PagerAlignReserve(Pager*,Pager*);
-#endif
-SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int);
-SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int);
-SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int);
-SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64);
-SQLITE_PRIVATE void sqlite3PagerShrink(Pager*);
-SQLITE_PRIVATE void sqlite3PagerSetFlags(Pager*,unsigned);
-SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int);
-SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int);
-SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*);
-SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*);
-SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
-SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*);
-SQLITE_PRIVATE int sqlite3PagerFlush(Pager*);
-
-/* Functions used to obtain and release page references. */
-SQLITE_PRIVATE int sqlite3PagerGet(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
-SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
-SQLITE_PRIVATE void sqlite3PagerRef(DbPage*);
-SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*);
-SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage*);
-SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage*);
-
-/* Operations on page references. */
-SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*);
-SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*);
-SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int);
-SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*);
-SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *);
-SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *);
-
-/* Functions used to manage pager transactions and savepoints. */
-SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*);
-SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int);
-SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
-SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*);
-SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zMaster);
-SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*);
-SQLITE_PRIVATE int sqlite3PagerRollback(Pager*);
-SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
-SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
-SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager);
-
-#ifndef SQLITE_OMIT_WAL
-SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
-SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager);
-SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager);
-SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
-SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
-# ifdef SQLITE_ENABLE_SNAPSHOT
-SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
-SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
-SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager);
-SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot);
-SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager);
-# endif
-#endif
-
-#ifdef SQLITE_DIRECT_OVERFLOW_READ
-SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno);
-#endif
-
-#ifdef SQLITE_ENABLE_ZIPVFS
-SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager);
-#endif
-
-/* Functions used to query pager state and configuration. */
-SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*);
-SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager*);
-#ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*);
-#endif
-SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*);
-SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int);
-SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*);
-SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*);
-SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*);
-SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
-SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
-SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
-SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
-SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*);
-SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
-SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager);
-#else
-# define sqlite3PagerResetLockTimeout(X)
-#endif
-
-/* Functions used to truncate the database file. */
-SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
-
-SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16);
-
-#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)
-SQLITE_PRIVATE void *sqlite3PagerCodec(DbPage *);
-#endif
-
-/* Functions to support testing and debugging. */
-#if !defined(NDEBUG) || defined(SQLITE_TEST)
-SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*);
-SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*);
-#endif
-#ifdef SQLITE_TEST
-SQLITE_PRIVATE int *sqlite3PagerStats(Pager*);
-SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*);
- void disable_simulated_io_errors(void);
- void enable_simulated_io_errors(void);
-#else
-# define disable_simulated_io_errors()
-# define enable_simulated_io_errors()
-#endif
-
-#endif /* SQLITE_PAGER_H */
+#endif /* SQLITE_VDBE_H */
-/************** End of pager.h ***********************************************/
+/************** End of vdbe.h ************************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
/************** Include pcache.h in the middle of sqliteInt.h ****************/
/************** Begin file pcache.h ******************************************/
@@ -15483,7 +17037,7 @@ SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*);
**
*************************************************************************
** This header file defines the interface that the sqlite page cache
-** subsystem.
+** subsystem.
*/
#ifndef _PCACHE_H_
@@ -15509,11 +17063,11 @@ struct PgHdr {
u16 flags; /* PGHDR flags defined below */
/**********************************************************************
- ** Elements above, except pCache, are public. All that follow are
+ ** Elements above, except pCache, are public. All that follow are
** private to pcache.c and should not be accessed by other modules.
** pCache is grouped with the public elements for efficiency.
*/
- i16 nRef; /* Number of users of this page */
+ i64 nRef; /* Number of users of this page */
PgHdr *pDirtyNext; /* Next element in list of dirty pages */
PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */
/* NB: pDirtyNext and pDirtyPrev are undefined if the
@@ -15562,7 +17116,7 @@ SQLITE_PRIVATE int sqlite3PcacheSetPageSize(PCache *, int);
SQLITE_PRIVATE int sqlite3PcacheSize(void);
/* One release per successful fetch. Page is pinned until released.
-** Reference counted.
+** Reference counted.
*/
SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch(PCache*, Pgno, int createFlag);
SQLITE_PRIVATE int sqlite3PcacheFetchStress(PCache*, Pgno, sqlite3_pcache_page**);
@@ -15594,19 +17148,19 @@ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *);
SQLITE_PRIVATE void sqlite3PcacheClear(PCache*);
/* Return the total number of outstanding page references */
-SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache*);
+SQLITE_PRIVATE i64 sqlite3PcacheRefCount(PCache*);
/* Increment the reference count of an existing page */
SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*);
-SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr*);
+SQLITE_PRIVATE i64 sqlite3PcachePageRefcount(PgHdr*);
/* Return the total number of pages stored in the cache */
SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*);
#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
/* Iterate through all dirty pages currently stored in the cache. This
-** interface is only available if SQLITE_CHECK_PAGES is defined when the
+** interface is only available if SQLITE_CHECK_PAGES is defined when the
** library is built.
*/
SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *));
@@ -15664,284 +17218,6 @@ SQLITE_PRIVATE int sqlite3PCacheIsDirty(PCache *pCache);
/************** End of pcache.h **********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
-/************** Include os.h in the middle of sqliteInt.h ********************/
-/************** Begin file os.h **********************************************/
-/*
-** 2001 September 16
-**
-** 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 (together with is companion C source-code file
-** "os.c") attempt to abstract the underlying operating system so that
-** the SQLite library will work on both POSIX and windows systems.
-**
-** This header file is #include-ed by sqliteInt.h and thus ends up
-** being included by every source file.
-*/
-#ifndef _SQLITE_OS_H_
-#define _SQLITE_OS_H_
-
-/*
-** Attempt to automatically detect the operating system and setup the
-** necessary pre-processor macros for it.
-*/
-/************** Include os_setup.h in the middle of os.h *********************/
-/************** Begin file os_setup.h ****************************************/
-/*
-** 2013 November 25
-**
-** 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 contains pre-processor directives related to operating system
-** detection and/or setup.
-*/
-#ifndef SQLITE_OS_SETUP_H
-#define SQLITE_OS_SETUP_H
-
-/*
-** Figure out if we are dealing with Unix, Windows, or some other operating
-** system.
-**
-** After the following block of preprocess macros, all of SQLITE_OS_UNIX,
-** SQLITE_OS_WIN, and SQLITE_OS_OTHER will defined to either 1 or 0. One of
-** the three will be 1. The other two will be 0.
-*/
-#if defined(SQLITE_OS_OTHER)
-# if SQLITE_OS_OTHER==1
-# undef SQLITE_OS_UNIX
-# define SQLITE_OS_UNIX 0
-# undef SQLITE_OS_WIN
-# define SQLITE_OS_WIN 0
-# else
-# undef SQLITE_OS_OTHER
-# endif
-#endif
-#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
-# define SQLITE_OS_OTHER 0
-# ifndef SQLITE_OS_WIN
-# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
- defined(__MINGW32__) || defined(__BORLANDC__)
-# define SQLITE_OS_WIN 1
-# define SQLITE_OS_UNIX 0
-# else
-# define SQLITE_OS_WIN 0
-# define SQLITE_OS_UNIX 1
-# endif
-# else
-# define SQLITE_OS_UNIX 0
-# endif
-#else
-# ifndef SQLITE_OS_WIN
-# define SQLITE_OS_WIN 0
-# endif
-#endif
-
-#endif /* SQLITE_OS_SETUP_H */
-
-/************** End of os_setup.h ********************************************/
-/************** Continuing where we left off in os.h *************************/
-
-/* If the SET_FULLSYNC macro is not defined above, then make it
-** a no-op
-*/
-#ifndef SET_FULLSYNC
-# define SET_FULLSYNC(x,y)
-#endif
-
-/*
-** The default size of a disk sector
-*/
-#ifndef SQLITE_DEFAULT_SECTOR_SIZE
-# define SQLITE_DEFAULT_SECTOR_SIZE 4096
-#endif
-
-/*
-** Temporary files are named starting with this prefix followed by 16 random
-** alphanumeric characters, and no file extension. They are stored in the
-** OS's standard temporary file directory, and are deleted prior to exit.
-** If sqlite is being embedded in another program, you may wish to change the
-** prefix to reflect your program's name, so that if your program exits
-** prematurely, old temporary files can be easily identified. This can be done
-** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line.
-**
-** 2006-10-31: The default prefix used to be "sqlite_". But then
-** Mcafee started using SQLite in their anti-virus product and it
-** started putting files with the "sqlite" name in the c:/temp folder.
-** This annoyed many windows users. Those users would then do a
-** Google search for "sqlite", find the telephone numbers of the
-** developers and call to wake them up at night and complain.
-** For this reason, the default name prefix is changed to be "sqlite"
-** spelled backwards. So the temp files are still identified, but
-** anybody smart enough to figure out the code is also likely smart
-** enough to know that calling the developer will not help get rid
-** of the file.
-*/
-#ifndef SQLITE_TEMP_FILE_PREFIX
-# define SQLITE_TEMP_FILE_PREFIX "etilqs_"
-#endif
-
-/*
-** The following values may be passed as the second argument to
-** sqlite3OsLock(). The various locks exhibit the following semantics:
-**
-** SHARED: Any number of processes may hold a SHARED lock simultaneously.
-** RESERVED: A single process may hold a RESERVED lock on a file at
-** any time. Other processes may hold and obtain new SHARED locks.
-** PENDING: A single process may hold a PENDING lock on a file at
-** any one time. Existing SHARED locks may persist, but no new
-** SHARED locks may be obtained by other processes.
-** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
-**
-** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a
-** process that requests an EXCLUSIVE lock may actually obtain a PENDING
-** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
-** sqlite3OsLock().
-*/
-#define NO_LOCK 0
-#define SHARED_LOCK 1
-#define RESERVED_LOCK 2
-#define PENDING_LOCK 3
-#define EXCLUSIVE_LOCK 4
-
-/*
-** File Locking Notes: (Mostly about windows but also some info for Unix)
-**
-** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
-** those functions are not available. So we use only LockFile() and
-** UnlockFile().
-**
-** LockFile() prevents not just writing but also reading by other processes.
-** A SHARED_LOCK is obtained by locking a single randomly-chosen
-** byte out of a specific range of bytes. The lock byte is obtained at
-** random so two separate readers can probably access the file at the
-** same time, unless they are unlucky and choose the same lock byte.
-** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range.
-** There can only be one writer. A RESERVED_LOCK is obtained by locking
-** a single byte of the file that is designated as the reserved lock byte.
-** A PENDING_LOCK is obtained by locking a designated byte different from
-** the RESERVED_LOCK byte.
-**
-** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
-** which means we can use reader/writer locks. When reader/writer locks
-** are used, the lock is placed on the same range of bytes that is used
-** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
-** will support two or more Win95 readers or two or more WinNT readers.
-** But a single Win95 reader will lock out all WinNT readers and a single
-** WinNT reader will lock out all other Win95 readers.
-**
-** The following #defines specify the range of bytes used for locking.
-** SHARED_SIZE is the number of bytes available in the pool from which
-** a random byte is selected for a shared lock. The pool of bytes for
-** shared locks begins at SHARED_FIRST.
-**
-** The same locking strategy and
-** byte ranges are used for Unix. This leaves open the possibility of having
-** clients on win95, winNT, and unix all talking to the same shared file
-** and all locking correctly. To do so would require that samba (or whatever
-** tool is being used for file sharing) implements locks correctly between
-** windows and unix. I'm guessing that isn't likely to happen, but by
-** using the same locking range we are at least open to the possibility.
-**
-** Locking in windows is manditory. For this reason, we cannot store
-** actual data in the bytes used for locking. The pager never allocates
-** the pages involved in locking therefore. SHARED_SIZE is selected so
-** that all locks will fit on a single page even at the minimum page size.
-** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE
-** is set high so that we don't have to allocate an unused page except
-** for very large databases. But one should test the page skipping logic
-** by setting PENDING_BYTE low and running the entire regression suite.
-**
-** Changing the value of PENDING_BYTE results in a subtly incompatible
-** file format. Depending on how it is changed, you might not notice
-** the incompatibility right away, even running a full regression test.
-** The default location of PENDING_BYTE is the first byte past the
-** 1GB boundary.
-**
-*/
-#ifdef SQLITE_OMIT_WSD
-# define PENDING_BYTE (0x40000000)
-#else
-# define PENDING_BYTE sqlite3PendingByte
-#endif
-#define RESERVED_BYTE (PENDING_BYTE+1)
-#define SHARED_FIRST (PENDING_BYTE+2)
-#define SHARED_SIZE 510
-
-/*
-** Wrapper around OS specific sqlite3_os_init() function.
-*/
-SQLITE_PRIVATE int sqlite3OsInit(void);
-
-/*
-** Functions for accessing sqlite3_file methods
-*/
-SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file*);
-SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset);
-SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset);
-SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size);
-SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int);
-SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize);
-SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int);
-SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int);
-SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut);
-SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*);
-SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*);
-#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0
-SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id);
-SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id);
-#ifndef SQLITE_OMIT_WAL
-SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **);
-SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int);
-SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id);
-SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int);
-#endif /* SQLITE_OMIT_WAL */
-SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64, int, void **);
-SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *, i64, void *);
-
-
-/*
-** Functions for accessing sqlite3_vfs methods
-*/
-SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *);
-SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int);
-SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut);
-SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *);
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
-SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *);
-SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *);
-SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void);
-SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *);
-#endif /* SQLITE_OMIT_LOAD_EXTENSION */
-SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *);
-SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int);
-SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs*);
-SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*);
-
-/*
-** Convenience functions for opening and closing files using
-** sqlite3_malloc() to obtain space for the file-handle structure.
-*/
-SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*);
-SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
-
-#endif /* _SQLITE_OS_H_ */
-
-/************** End of os.h **************************************************/
-/************** Continuing where we left off in sqliteInt.h ******************/
/************** Include mutex.h in the middle of sqliteInt.h *****************/
/************** Begin file mutex.h *******************************************/
/*
@@ -16002,9 +17278,9 @@ SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
*/
#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
#define sqlite3_mutex_free(X)
-#define sqlite3_mutex_enter(X)
+#define sqlite3_mutex_enter(X)
#define sqlite3_mutex_try(X) SQLITE_OK
-#define sqlite3_mutex_leave(X)
+#define sqlite3_mutex_leave(X)
#define sqlite3_mutex_held(X) ((void)(X),1)
#define sqlite3_mutex_notheld(X) ((void)(X),1)
#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8)
@@ -16013,6 +17289,7 @@ SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
#define MUTEX_LOGIC(X)
#else
#define MUTEX_LOGIC(X) X
+SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*);
#endif /* defined(SQLITE_MUTEX_OMIT) */
/************** End of mutex.h ***********************************************/
@@ -16029,7 +17306,7 @@ SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
/*
** Default synchronous levels.
**
-** Note that (for historcal reasons) the PAGER_SYNCHRONOUS_* macros differ
+** Note that (for historical reasons) the PAGER_SYNCHRONOUS_* macros differ
** from the SQLITE_DEFAULT_SYNCHRONOUS value by 1.
**
** PAGER_SYNCHRONOUS DEFAULT_SYNCHRONOUS
@@ -16068,7 +17345,7 @@ struct Db {
** An instance of the following structure stores a database schema.
**
** Most Schema objects are associated with a Btree. The exception is
-** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing.
+** the Schema for the TEMP database (sqlite3.aDb[1]) which is free-standing.
** In shared cache mode, a single Schema object can be shared by multiple
** Btrees that refer to the same underlying BtShared object.
**
@@ -16116,7 +17393,6 @@ struct Schema {
*/
#define DB_SchemaLoaded 0x0001 /* The schema has been loaded */
#define DB_UnresetViews 0x0002 /* Some views have defined column names */
-#define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */
#define DB_ResetWanted 0x0008 /* Reset the schema when nSchemaLock==0 */
/*
@@ -16144,22 +17420,66 @@ struct Schema {
** is shared by multiple database connections. Therefore, while parsing
** schema information, the Lookaside.bEnabled flag is cleared so that
** lookaside allocations are not used to construct the schema objects.
+**
+** New lookaside allocations are only allowed if bDisable==0. When
+** bDisable is greater than zero, sz is set to zero which effectively
+** disables lookaside without adding a new test for the bDisable flag
+** in a performance-critical path. sz should be set by to szTrue whenever
+** bDisable changes back to zero.
+**
+** Lookaside buffers are initially held on the pInit list. As they are
+** used and freed, they are added back to the pFree list. New allocations
+** come off of pFree first, then pInit as a fallback. This dual-list
+** allows use to compute a high-water mark - the maximum number of allocations
+** outstanding at any point in the past - by subtracting the number of
+** allocations on the pInit list from the total number of allocations.
+**
+** Enhancement on 2019-12-12: Two-size-lookaside
+** The default lookaside configuration is 100 slots of 1200 bytes each.
+** The larger slot sizes are important for performance, but they waste
+** a lot of space, as most lookaside allocations are less than 128 bytes.
+** The two-size-lookaside enhancement breaks up the lookaside allocation
+** into two pools: One of 128-byte slots and the other of the default size
+** (1200-byte) slots. Allocations are filled from the small-pool first,
+** failing over to the full-size pool if that does not work. Thus more
+** lookaside slots are available while also using less memory.
+** This enhancement can be omitted by compiling with
+** SQLITE_OMIT_TWOSIZE_LOOKASIDE.
*/
struct Lookaside {
u32 bDisable; /* Only operate the lookaside when zero */
u16 sz; /* Size of each buffer in bytes */
+ u16 szTrue; /* True value of sz, even if disabled */
u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */
u32 nSlot; /* Number of lookaside slots allocated */
u32 anStat[3]; /* 0: hits. 1: size misses. 2: full misses */
LookasideSlot *pInit; /* List of buffers not previously used */
LookasideSlot *pFree; /* List of available buffers */
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ LookasideSlot *pSmallInit; /* List of small buffers not previously used */
+ LookasideSlot *pSmallFree; /* List of available small buffers */
+ void *pMiddle; /* First byte past end of full-size buffers and
+ ** the first byte of LOOKASIDE_SMALL buffers */
+#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
void *pStart; /* First byte of available memory space */
void *pEnd; /* First byte past end of available space */
+ void *pTrueEnd; /* True value of pEnd, when db->pnBytesFreed!=0 */
};
struct LookasideSlot {
LookasideSlot *pNext; /* Next buffer in the list of free buffers */
};
+#define DisableLookaside db->lookaside.bDisable++;db->lookaside.sz=0
+#define EnableLookaside db->lookaside.bDisable--;\
+ db->lookaside.sz=db->lookaside.bDisable?0:db->lookaside.szTrue
+
+/* Size of the smaller allocations in two-size lookaside */
+#ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+# define LOOKASIDE_SMALL 0
+#else
+# define LOOKASIDE_SMALL 128
+#endif
+
/*
** A hash table for built-in function definitions. (Application-defined
** functions use a regular table table from hash.h.)
@@ -16216,11 +17536,19 @@ SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**);
/* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing
** in the style of sqlite3_trace()
*/
-#define SQLITE_TRACE_LEGACY 0x80
+#define SQLITE_TRACE_LEGACY 0x40 /* Use the legacy xTrace */
+#define SQLITE_TRACE_XPROFILE 0x80 /* Use the legacy xProfile */
#else
-#define SQLITE_TRACE_LEGACY 0
+#define SQLITE_TRACE_LEGACY 0
+#define SQLITE_TRACE_XPROFILE 0
#endif /* SQLITE_OMIT_DEPRECATED */
+#define SQLITE_TRACE_NONLEGACY_MASK 0x0f /* Normal flags */
+/*
+** Maximum number of sqlite3.aDb[] entries. This is the number of attached
+** databases plus 2 for "main" and "temp".
+*/
+#define SQLITE_MAX_DB (SQLITE_MAX_ATTACHED+2)
/*
** Each database connection is an instance of the following structure.
@@ -16228,7 +17556,7 @@ SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**);
struct sqlite3 {
sqlite3_vfs *pVfs; /* OS Interface */
struct Vdbe *pVdbe; /* List of active virtual machines */
- CollSeq *pDfltColl; /* The default collating sequence (BINARY) */
+ CollSeq *pDfltColl; /* BINARY collseq for the database encoding */
sqlite3_mutex *mutex; /* Connection mutex */
Db *aDb; /* All backends */
int nDb; /* Number of backends currently in use */
@@ -16239,9 +17567,10 @@ struct sqlite3 {
u32 nSchemaLock; /* Do not reset the schema when non-zero */
unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
int errCode; /* Most recent error code (SQLITE_*) */
+ int errByteOffset; /* Byte offset of error in SQL statement */
int errMask; /* & result codes with this before returning */
int iSysErrno; /* Errno value from last system error */
- u16 dbOptFlags; /* Flags to enable/disable optimizations */
+ u32 dbOptFlags; /* Flags to enable/disable optimizations */
u8 enc; /* Text encoding */
u8 autoCommit; /* The auto-commit flag. */
u8 temp_store; /* 1: file 2: memory 0: default */
@@ -16255,19 +17584,20 @@ struct sqlite3 {
u8 mTrace; /* zero or more SQLITE_TRACE flags */
u8 noSharedCache; /* True if no shared-cache backends */
u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */
+ u8 eOpenState; /* Current condition of the connection */
int nextPagesize; /* Pagesize after VACUUM if >0 */
- u32 magic; /* Magic number for detect library misuse */
- int nChange; /* Value returned by sqlite3_changes() */
- int nTotalChange; /* Value returned by sqlite3_total_changes() */
+ i64 nChange; /* Value returned by sqlite3_changes() */
+ i64 nTotalChange; /* Value returned by sqlite3_total_changes() */
int aLimit[SQLITE_N_LIMIT]; /* Limits */
int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */
struct sqlite3InitInfo { /* Information used during initialization */
- int newTnum; /* Rootpage of table being initialized */
+ Pgno newTnum; /* Rootpage of table being initialized */
u8 iDb; /* Which db file is being initialized */
u8 busy; /* TRUE if currently initializing */
unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
unsigned imposterTable : 1; /* Building an imposter table */
unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
+ const char **azInit; /* "type", "name", and "tbl_name" columns */
} init;
int nVdbeActive; /* Number of VDBEs currently running */
int nVdbeRead; /* Number of active VDBEs that read or write */
@@ -16276,16 +17606,25 @@ struct sqlite3 {
int nVDestroy; /* Number of active OP_VDestroy operations */
int nExtension; /* Number of loaded extensions */
void **aExtension; /* Array of shared library handles */
- int (*xTrace)(u32,void*,void*,void*); /* Trace function */
- void *pTraceArg; /* Argument to the trace function */
+ union {
+ void (*xLegacy)(void*,const char*); /* mTrace==SQLITE_TRACE_LEGACY */
+ int (*xV2)(u32,void*,void*,void*); /* All other mTrace values */
+ } trace;
+ void *pTraceArg; /* Argument to the trace function */
+#ifndef SQLITE_OMIT_DEPRECATED
void (*xProfile)(void*,const char*,u64); /* Profiling function */
void *pProfileArg; /* Argument to profile function */
+#endif
void *pCommitArg; /* Argument to xCommitCallback() */
int (*xCommitCallback)(void*); /* Invoked at every commit. */
void *pRollbackArg; /* Argument to xRollbackCallback() */
void (*xRollbackCallback)(void*); /* Invoked at every commit. */
void *pUpdateArg;
void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
+ void *pAutovacPagesArg; /* Client argument to autovac_pages */
+ void (*xAutovacDestr)(void*); /* Destructor for pAutovacPAgesArg */
+ unsigned int (*xAutovacPages)(void*,const char*,u32,u32,u32);
+ Parse *pParse; /* Current parse */
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
void *pPreUpdateArg; /* First argument to xPreUpdateCallback */
void (*xPreUpdateCallback)( /* Registered using sqlite3_preupdate_hook() */
@@ -16327,14 +17666,16 @@ struct sqlite3 {
BusyHandler busyHandler; /* Busy callback */
Db aDbStatic[2]; /* Static space for the 2 default backends */
Savepoint *pSavepoint; /* List of active savepoints */
+ int nAnalysisLimit; /* Number of index rows to ANALYZE */
int busyTimeout; /* Busy handler timeout, in msec */
int nSavepoint; /* Number of non-transaction savepoints */
int nStatement; /* Number of nested statement-transactions */
i64 nDeferredCons; /* Net deferred constraints this transaction. */
i64 nDeferredImmCons; /* Net deferred immediate constraints */
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
+ DbClientData *pDbData; /* sqlite3_set_clientdata() content */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
- /* The following variables are all protected by the STATIC_MASTER
+ /* The following variables are all protected by the STATIC_MAIN
** mutex, not by sqlite3.mutex. They are used by code in notify.c.
**
** When X.pUnlockConnection==Y, that means that X is waiting for Y to
@@ -16362,6 +17703,13 @@ struct sqlite3 {
#define ENC(db) ((db)->enc)
/*
+** A u64 constant where the lower 32 bits are all zeros. Only the
+** upper 32 bits are included in the argument. Necessary because some
+** C-compilers still do not accept LL integer literals.
+*/
+#define HI(X) ((u64)(X)<<32)
+
+/*
** Possible values for the sqlite3.flags.
**
** Value constraints (enforced via assert()):
@@ -16369,20 +17717,19 @@ struct sqlite3 {
** SQLITE_CkptFullFSync == PAGER_CKPT_FULLFSYNC
** SQLITE_CacheSpill == PAGER_CACHE_SPILL
*/
-#define SQLITE_WriteSchema 0x00000001 /* OK to update SQLITE_MASTER */
+#define SQLITE_WriteSchema 0x00000001 /* OK to update SQLITE_SCHEMA */
#define SQLITE_LegacyFileFmt 0x00000002 /* Create new databases in format 1 */
#define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */
#define SQLITE_FullFSync 0x00000008 /* Use full fsync on the backend */
#define SQLITE_CkptFullFSync 0x00000010 /* Use full fsync for checkpoint */
#define SQLITE_CacheSpill 0x00000020 /* OK to spill pager cache */
#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */
-#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */
- /* DELETE, or UPDATE and return */
- /* the count using a callback. */
+#define SQLITE_TrustedSchema 0x00000080 /* Allow unsafe functions and
+ ** vtabs in the schema definition */
#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */
/* result set is empty */
#define SQLITE_IgnoreChecks 0x00000200 /* Do not enforce check constraints */
-#define SQLITE_ReadUncommit 0x00000400 /* READ UNCOMMITTED in shared-cache */
+#define SQLITE_StmtScanStatus 0x00000400 /* Enable stmt_scanstats() counters */
#define SQLITE_NoCkptOnClose 0x00000800 /* No checkpoint on close()/DETACH */
#define SQLITE_ReverseOrder 0x00001000 /* Reverse unordered SELECTs */
#define SQLITE_RecTriggers 0x00002000 /* Enable recursive triggers */
@@ -16401,15 +17748,24 @@ struct sqlite3 {
#define SQLITE_LegacyAlter 0x04000000 /* Legacy ALTER TABLE behaviour */
#define SQLITE_NoSchemaError 0x08000000 /* Do not report schema parse errors*/
#define SQLITE_Defensive 0x10000000 /* Input SQL is likely hostile */
+#define SQLITE_DqsDDL 0x20000000 /* dbl-quoted strings allowed in DDL*/
+#define SQLITE_DqsDML 0x40000000 /* dbl-quoted strings allowed in DML*/
+#define SQLITE_EnableView 0x80000000 /* Enable the use of views */
+#define SQLITE_CountRows HI(0x00001) /* Count rows changed by INSERT, */
+ /* DELETE, or UPDATE and return */
+ /* the count using a callback. */
+#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */
+#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */
+#define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */
/* Flags used only if debugging */
-#define HI(X) ((u64)(X)<<32)
#ifdef SQLITE_DEBUG
-#define SQLITE_SqlTrace HI(0x0001) /* Debug print SQL as it executes */
-#define SQLITE_VdbeListing HI(0x0002) /* Debug listings of VDBE progs */
-#define SQLITE_VdbeTrace HI(0x0004) /* True to trace VDBE execution */
-#define SQLITE_VdbeAddopTrace HI(0x0008) /* Trace sqlite3VdbeAddOp() calls */
-#define SQLITE_VdbeEQP HI(0x0010) /* Debug EXPLAIN QUERY PLAN */
+#define SQLITE_SqlTrace HI(0x0100000) /* Debug print SQL as it executes */
+#define SQLITE_VdbeListing HI(0x0200000) /* Debug listings of VDBE progs */
+#define SQLITE_VdbeTrace HI(0x0400000) /* True to trace VDBE execution */
+#define SQLITE_VdbeAddopTrace HI(0x0800000) /* Trace sqlite3VdbeAddOp() calls */
+#define SQLITE_VdbeEQP HI(0x1000000) /* Debug EXPLAIN QUERY PLAN */
+#define SQLITE_ParserTrace HI(0x2000000) /* PRAGMA parser_trace=ON */
#endif
/*
@@ -16418,31 +17774,48 @@ struct sqlite3 {
#define DBFLAG_SchemaChange 0x0001 /* Uncommitted Hash table changes */
#define DBFLAG_PreferBuiltin 0x0002 /* Preference to built-in funcs */
#define DBFLAG_Vacuum 0x0004 /* Currently in a VACUUM */
-#define DBFLAG_SchemaKnownOk 0x0008 /* Schema is known to be valid */
+#define DBFLAG_VacuumInto 0x0008 /* Currently running VACUUM INTO */
+#define DBFLAG_SchemaKnownOk 0x0010 /* Schema is known to be valid */
+#define DBFLAG_InternalFunc 0x0020 /* Allow use of internal functions */
+#define DBFLAG_EncodingFixed 0x0040 /* No longer possible to change enc. */
/*
** Bits of the sqlite3.dbOptFlags field that are used by the
** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to
** selectively disable various optimizations.
*/
-#define SQLITE_QueryFlattener 0x0001 /* Query flattening */
- /* 0x0002 available for reuse */
-#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */
-#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */
-#define SQLITE_DistinctOpt 0x0010 /* DISTINCT using indexes */
-#define SQLITE_CoverIdxScan 0x0020 /* Covering index scans */
-#define SQLITE_OrderByIdxJoin 0x0040 /* ORDER BY of joins via index */
-#define SQLITE_Transitive 0x0080 /* Transitive constraints */
-#define SQLITE_OmitNoopJoin 0x0100 /* Omit unused tables in joins */
-#define SQLITE_CountOfView 0x0200 /* The count-of-view optimization */
-#define SQLITE_CursorHints 0x0400 /* Add OP_CursorHint opcodes */
-#define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */
- /* TH3 expects the Stat34 ^^^^^^ value to be 0x0800. Don't change it */
-#define SQLITE_PushDown 0x1000 /* The push-down optimization */
-#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */
-#define SQLITE_SkipScan 0x4000 /* Skip-scans */
-#define SQLITE_PropagateConst 0x8000 /* The constant propagation opt */
-#define SQLITE_AllOpts 0xffff /* All optimizations */
+#define SQLITE_QueryFlattener 0x00000001 /* Query flattening */
+#define SQLITE_WindowFunc 0x00000002 /* Use xInverse for window functions */
+#define SQLITE_GroupByOrder 0x00000004 /* GROUPBY cover of ORDERBY */
+#define SQLITE_FactorOutConst 0x00000008 /* Constant factoring */
+#define SQLITE_DistinctOpt 0x00000010 /* DISTINCT using indexes */
+#define SQLITE_CoverIdxScan 0x00000020 /* Covering index scans */
+#define SQLITE_OrderByIdxJoin 0x00000040 /* ORDER BY of joins via index */
+#define SQLITE_Transitive 0x00000080 /* Transitive constraints */
+#define SQLITE_OmitNoopJoin 0x00000100 /* Omit unused tables in joins */
+#define SQLITE_CountOfView 0x00000200 /* The count-of-view optimization */
+#define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */
+#define SQLITE_Stat4 0x00000800 /* Use STAT4 data */
+ /* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */
+#define SQLITE_PushDown 0x00001000 /* The push-down optimization */
+#define SQLITE_SimplifyJoin 0x00002000 /* Convert LEFT JOIN to JOIN */
+#define SQLITE_SkipScan 0x00004000 /* Skip-scans */
+#define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */
+#define SQLITE_MinMaxOpt 0x00010000 /* The min/max optimization */
+#define SQLITE_SeekScan 0x00020000 /* The OP_SeekScan optimization */
+#define SQLITE_OmitOrderBy 0x00040000 /* Omit pointless ORDER BY */
+ /* TH3 expects this value ^^^^^^^^^^ to be 0x40000. Coordinate any change */
+#define SQLITE_BloomFilter 0x00080000 /* Use a Bloom filter on searches */
+#define SQLITE_BloomPulldown 0x00100000 /* Run Bloom filters early */
+#define SQLITE_BalancedMerge 0x00200000 /* Balance multi-way merges */
+#define SQLITE_ReleaseReg 0x00400000 /* Use OP_ReleaseReg for testing */
+#define SQLITE_FlttnUnionAll 0x00800000 /* Disable the UNION ALL flattener */
+ /* TH3 expects this value ^^^^^^^^^^ See flatten04.test */
+#define SQLITE_IndexedExpr 0x01000000 /* Pull exprs from index when able */
+#define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */
+#define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */
+#define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */
+#define SQLITE_AllOpts 0xffffffff /* All optimizations */
/*
** Macros for testing whether or not optimizations are enabled or disabled.
@@ -16456,17 +17829,16 @@ struct sqlite3 {
*/
#define ConstFactorOk(P) ((P)->okConstFactor)
-/*
-** Possible values for the sqlite.magic field.
-** The numbers are obtained at random and have no special meaning, other
-** than being distinct from one another.
+/* Possible values for the sqlite3.eOpenState field.
+** The numbers are randomly selected such that a minimum of three bits must
+** change to convert any number to another or to zero
*/
-#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */
-#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */
-#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */
-#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */
-#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */
-#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */
+#define SQLITE_STATE_OPEN 0x76 /* Database is open */
+#define SQLITE_STATE_CLOSED 0xce /* Database is closed */
+#define SQLITE_STATE_SICK 0xba /* Error and awaiting close */
+#define SQLITE_STATE_BUSY 0x6d /* Database currently in use */
+#define SQLITE_STATE_ERROR 0xd5 /* An SQLITE_MISUSE error occurred */
+#define SQLITE_STATE_ZOMBIE 0xa7 /* Close with last statement close */
/*
** Each SQL function is defined by an instance of the following
@@ -16491,7 +17863,7 @@ struct FuncDef {
union {
FuncDef *pHash; /* Next with a different name but the same hash */
FuncDestructor *pDestructor; /* Reference counted destructor function */
- } u;
+ } u; /* pHash if SQLITE_FUNC_BUILTIN, pDestructor otherwise */
};
/*
@@ -16521,11 +17893,21 @@ struct FuncDestructor {
** are assert() statements in the code to verify this.
**
** Value constraints (enforced via assert()):
-** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg
-** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG
-** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG
-** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API
+** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg
+** SQLITE_FUNC_ANYORDER == NC_OrderAgg == SF_OrderByReqd
+** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG
+** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG
+** SQLITE_FUNC_BYTELEN == OPFLAG_BYTELENARG
+** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API
+** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API
+** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS -- opposite meanings!!!
** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API
+**
+** Note that even though SQLITE_FUNC_UNSAFE and SQLITE_INNOCUOUS have the
+** same bit value, their meanings are inverted. SQLITE_FUNC_UNSAFE is
+** used internally and if set means that the function has side effects.
+** SQLITE_INNOCUOUS is used by application code and means "not unsafe".
+** See multiple instances of tag-20230109-1.
*/
#define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */
#define SQLITE_FUNC_LIKE 0x0004 /* Candidate for the LIKE optimization */
@@ -16534,18 +17916,35 @@ struct FuncDestructor {
#define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/
#define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */
#define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */
+#define SQLITE_FUNC_BYTELEN 0x00c0 /* Built-in octet_length() function */
#define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */
-#define SQLITE_FUNC_COALESCE 0x0200 /* Built-in coalesce() or ifnull() */
+/* 0x0200 -- available for reuse */
#define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */
#define SQLITE_FUNC_CONSTANT 0x0800 /* Constant inputs give a constant output */
#define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */
#define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a
** single query - might change over time */
-#define SQLITE_FUNC_AFFINITY 0x4000 /* Built-in affinity() function */
-#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */
+#define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */
+#define SQLITE_FUNC_RUNONLY 0x8000 /* Cannot be used by valueFromFunction */
#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
-#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */
#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
+#define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */
+/* SQLITE_SUBTYPE 0x00100000 // Consumer of subtypes */
+#define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */
+#define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */
+#define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */
+/* SQLITE_RESULT_SUBTYPE 0x01000000 // Generator of subtypes */
+#define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */
+
+/* Identifier numbers for each in-line function */
+#define INLINEFUNC_coalesce 0
+#define INLINEFUNC_implies_nonnull_row 1
+#define INLINEFUNC_expr_implies_expr 2
+#define INLINEFUNC_expr_compare 3
+#define INLINEFUNC_affinity 4
+#define INLINEFUNC_iif 5
+#define INLINEFUNC_sqlite_offset 6
+#define INLINEFUNC_unlikely 99 /* Default case */
/*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
@@ -16561,6 +17960,22 @@ struct FuncDestructor {
** VFUNCTION(zName, nArg, iArg, bNC, xFunc)
** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag.
**
+** SFUNCTION(zName, nArg, iArg, bNC, xFunc)
+** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag and
+** adds the SQLITE_DIRECTONLY flag.
+**
+** INLINE_FUNC(zName, nArg, iFuncId, mFlags)
+** zName is the name of a function that is implemented by in-line
+** byte code rather than by the usual callbacks. The iFuncId
+** parameter determines the function id. The mFlags parameter is
+** optional SQLITE_FUNC_ flags for this function.
+**
+** TEST_FUNC(zName, nArg, iFuncId, mFlags)
+** zName is the name of a test-only function implemented by in-line
+** byte code rather than by the usual callbacks. The iFuncId
+** parameter determines the function id. The mFlags parameter is
+** optional SQLITE_FUNC_ flags for this function.
+**
** DFUNCTION(zName, nArg, iArg, bNC, xFunc)
** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag and
** adds the SQLITE_FUNC_SLOCHNG flag. Used for date & time functions
@@ -16568,10 +17983,13 @@ struct FuncDestructor {
** a single query. The iArg is ignored. The user-data is always set
** to a NULL pointer. The bNC parameter is not used.
**
+** MFUNCTION(zName, nArg, xPtr, xFunc)
+** For math-library functions. xPtr is an arbitrary pointer.
+**
** PURE_DATE(zName, nArg, iArg, bNC, xFunc)
** Used for "pure" date/time functions, this macro is like DFUNCTION
** except that it does set the SQLITE_FUNC_CONSTANT flags. iArg is
-** ignored and the user-data for these functions is set to an
+** ignored and the user-data for these functions is set to an
** arbitrary non-NULL pointer. The bNC parameter is not used.
**
** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal)
@@ -16580,7 +17998,7 @@ struct FuncDestructor {
** are interpreted in the same way as the first 4 parameters to
** FUNCTION().
**
-** WFUNCTION(zName, nArg, iArg, xStep, xFinal, xValue, xInverse)
+** WAGGREGATE(zName, nArg, iArg, xStep, xFinal, xValue, xInverse)
** Used to create an aggregate function definition implemented by
** the C functions xStep and xFinal. The first four parameters
** are interpreted in the same way as the first 4 parameters to
@@ -16595,37 +18013,56 @@ struct FuncDestructor {
** parameter.
*/
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
- {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ {nArg, SQLITE_FUNC_BUILTIN|\
+ SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \
- {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
+#define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \
+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
+#define MFUNCTION(zName, nArg, xPtr, xFunc) \
+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
+ xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
+#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, bJsonB, iArg, xFunc) \
+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\
+ SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\
+ ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \
+ SQLITE_INT_TO_PTR(iArg|((bJsonB)*JSON_BLOB)),0,xFunc,0, 0, 0, #zName, {0} }
+#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
+ {nArg, SQLITE_FUNC_BUILTIN|\
+ SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
+ SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} }
+#define TEST_FUNC(zName, nArg, iArg, mFlags) \
+ {nArg, SQLITE_FUNC_BUILTIN|\
+ SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \
+ SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
+ SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} }
#define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \
- {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \
+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_SLOCHNG|SQLITE_UTF8, \
0, 0, xFunc, 0, 0, 0, #zName, {0} }
#define PURE_DATE(zName, nArg, iArg, bNC, xFunc) \
- {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \
+ {nArg, SQLITE_FUNC_BUILTIN|\
+ SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \
(void*)&sqlite3Config, 0, xFunc, 0, 0, 0, #zName, {0} }
#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
- {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\
+ {nArg, SQLITE_FUNC_BUILTIN|\
+ SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
- {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ {nArg, SQLITE_FUNC_BUILTIN|\
+ SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
pArg, 0, xFunc, 0, 0, 0, #zName, }
#define LIKEFUNC(zName, nArg, arg, flags) \
- {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \
+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \
(void *)arg, 0, likeFunc, 0, 0, 0, #zName, {0} }
-#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal, xValue) \
- {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \
- SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,0,#zName, {0}}
-#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \
- {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
- SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xFinal,0,#zName, {0}}
#define WAGGREGATE(zName, nArg, arg, nc, xStep, xFinal, xValue, xInverse, f) \
- {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \
+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \
SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,xInverse,#zName, {0}}
#define INTERNAL_FUNCTION(zName, nArg, xFunc) \
- {nArg, SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \
+ {nArg, SQLITE_FUNC_BUILTIN|\
+ SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \
0, 0, xFunc, 0, 0, 0, #zName, {0} }
@@ -16659,32 +18096,84 @@ struct Savepoint {
struct Module {
const sqlite3_module *pModule; /* Callback pointers */
const char *zName; /* Name passed to create_module() */
+ int nRefModule; /* Number of pointers to this object */
void *pAux; /* pAux passed to create_module() */
void (*xDestroy)(void *); /* Module destructor function */
Table *pEpoTab; /* Eponymous table for this module */
};
/*
-** information about each column of an SQL table is held in an instance
-** of this structure.
+** Information about each column of an SQL table is held in an instance
+** of the Column structure, in the Table.aCol[] array.
+**
+** Definitions:
+**
+** "table column index" This is the index of the column in the
+** Table.aCol[] array, and also the index of
+** the column in the original CREATE TABLE stmt.
+**
+** "storage column index" This is the index of the column in the
+** record BLOB generated by the OP_MakeRecord
+** opcode. The storage column index is less than
+** or equal to the table column index. It is
+** equal if and only if there are no VIRTUAL
+** columns to the left.
+**
+** Notes on zCnName:
+** The zCnName field stores the name of the column, the datatype of the
+** column, and the collating sequence for the column, in that order, all in
+** a single allocation. Each string is 0x00 terminated. The datatype
+** is only included if the COLFLAG_HASTYPE bit of colFlags is set and the
+** collating sequence name is only included if the COLFLAG_HASCOLL bit is
+** set.
*/
struct Column {
- char *zName; /* Name of this column, \000, then the type */
- Expr *pDflt; /* Default value of this column */
- char *zColl; /* Collating sequence. If NULL, use the default */
- u8 notNull; /* An OE_ code for handling a NOT NULL constraint */
- char affinity; /* One of the SQLITE_AFF_... values */
- u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */
- u8 colFlags; /* Boolean properties. See COLFLAG_ defines below */
+ char *zCnName; /* Name of this column */
+ unsigned notNull :4; /* An OE_ code for handling a NOT NULL constraint */
+ unsigned eCType :4; /* One of the standard types */
+ char affinity; /* One of the SQLITE_AFF_... values */
+ u8 szEst; /* Est size of value in this column. sizeof(INT)==1 */
+ u8 hName; /* Column name hash for faster lookup */
+ u16 iDflt; /* 1-based index of DEFAULT. 0 means "none" */
+ u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */
};
-/* Allowed values for Column.colFlags:
-*/
-#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */
-#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */
-#define COLFLAG_HASTYPE 0x0004 /* Type name follows column name */
-#define COLFLAG_UNIQUE 0x0008 /* Column def contains "UNIQUE" or "PK" */
+/* Allowed values for Column.eCType.
+**
+** Values must match entries in the global constant arrays
+** sqlite3StdTypeLen[] and sqlite3StdType[]. Each value is one more
+** than the offset into these arrays for the corresponding name.
+** Adjust the SQLITE_N_STDTYPE value if adding or removing entries.
+*/
+#define COLTYPE_CUSTOM 0 /* Type appended to zName */
+#define COLTYPE_ANY 1
+#define COLTYPE_BLOB 2
+#define COLTYPE_INT 3
+#define COLTYPE_INTEGER 4
+#define COLTYPE_REAL 5
+#define COLTYPE_TEXT 6
+#define SQLITE_N_STDTYPE 6 /* Number of standard types */
+
+/* Allowed values for Column.colFlags.
+**
+** Constraints:
+** TF_HasVirtual == COLFLAG_VIRTUAL
+** TF_HasStored == COLFLAG_STORED
+** TF_HasHidden == COLFLAG_HIDDEN
+*/
+#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */
+#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */
+#define COLFLAG_HASTYPE 0x0004 /* Type name follows column name */
+#define COLFLAG_UNIQUE 0x0008 /* Column def contains "UNIQUE" or "PK" */
#define COLFLAG_SORTERREF 0x0010 /* Use sorter-refs with this column */
+#define COLFLAG_VIRTUAL 0x0020 /* GENERATED ALWAYS AS ... VIRTUAL */
+#define COLFLAG_STORED 0x0040 /* GENERATED ALWAYS AS ... STORED */
+#define COLFLAG_NOTAVAIL 0x0080 /* STORED column not yet calculated */
+#define COLFLAG_BUSY 0x0100 /* Blocks recursion on GENERATED columns */
+#define COLFLAG_HASCOLL 0x0200 /* Has collating sequence name in zCnName */
+#define COLFLAG_NOEXPAND 0x0400 /* Omit this column when expanding "*" */
+#define COLFLAG_GENERATED 0x0060 /* Combo: _STORED, _VIRTUAL */
+#define COLFLAG_NOINSERT 0x0062 /* Combo: _HIDDEN, _STORED, _VIRTUAL */
/*
** A "Collating Sequence" is defined by an instance of the following
@@ -16724,11 +18213,13 @@ struct CollSeq {
** Note also that the numeric types are grouped together so that testing
** for a numeric type is a single comparison. And the BLOB type is first.
*/
-#define SQLITE_AFF_BLOB 'A'
-#define SQLITE_AFF_TEXT 'B'
-#define SQLITE_AFF_NUMERIC 'C'
-#define SQLITE_AFF_INTEGER 'D'
-#define SQLITE_AFF_REAL 'E'
+#define SQLITE_AFF_NONE 0x40 /* '@' */
+#define SQLITE_AFF_BLOB 0x41 /* 'A' */
+#define SQLITE_AFF_TEXT 0x42 /* 'B' */
+#define SQLITE_AFF_NUMERIC 0x43 /* 'C' */
+#define SQLITE_AFF_INTEGER 0x44 /* 'D' */
+#define SQLITE_AFF_REAL 0x45 /* 'E' */
+#define SQLITE_AFF_FLEXNUM 0x46 /* 'F' */
#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC)
@@ -16747,9 +18238,7 @@ struct CollSeq {
** operator is NULL. It is added to certain comparison operators to
** prove that the operands are always NOT NULL.
*/
-#define SQLITE_KEEPNULL 0x08 /* Used by vector == or <> */
#define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */
-#define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */
#define SQLITE_NULLEQ 0x80 /* NULL=NULL */
#define SQLITE_NOTNULL 0x90 /* Assert that operands are never NULL */
@@ -16801,48 +18290,60 @@ struct VTable {
sqlite3_vtab *pVtab; /* Pointer to vtab instance */
int nRef; /* Number of pointers to this structure */
u8 bConstraint; /* True if constraints are supported */
+ u8 bAllSchemas; /* True if might use any attached schema */
+ u8 eVtabRisk; /* Riskiness of allowing hacker access */
int iSavepoint; /* Depth of the SAVEPOINT stack */
VTable *pNext; /* Next in linked list (see above) */
};
+/* Allowed values for VTable.eVtabRisk
+*/
+#define SQLITE_VTABRISK_Low 0
+#define SQLITE_VTABRISK_Normal 1
+#define SQLITE_VTABRISK_High 2
+
/*
-** The schema for each SQL table and view is represented in memory
-** by an instance of the following structure.
+** The schema for each SQL table, virtual table, and view is represented
+** in memory by an instance of the following structure.
*/
struct Table {
char *zName; /* Name of the table or view */
Column *aCol; /* Information about each column */
-#ifdef SQLITE_ENABLE_NORMALIZE
- Hash *pColHash; /* All columns indexed by name */
-#endif
Index *pIndex; /* List of SQL indexes on this table. */
- Select *pSelect; /* NULL for tables. Points to definition if a view. */
- FKey *pFKey; /* Linked list of all foreign keys in this table */
char *zColAff; /* String defining the affinity of each column */
ExprList *pCheck; /* All CHECK constraints */
/* ... also used as column name list in a VIEW */
- int tnum; /* Root BTree page for this table */
+ Pgno tnum; /* Root BTree page for this table */
u32 nTabRef; /* Number of pointers to this Table */
u32 tabFlags; /* Mask of TF_* values */
i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */
i16 nCol; /* Number of columns in this table */
+ i16 nNVCol; /* Number of columns that are not VIRTUAL */
LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */
LogEst szTabRow; /* Estimated size of each table row in bytes */
#ifdef SQLITE_ENABLE_COSTMULT
LogEst costMult; /* Cost multiplier for using this table */
#endif
u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
-#ifndef SQLITE_OMIT_ALTERTABLE
- int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */
-#endif
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- int nModuleArg; /* Number of arguments to the module */
- char **azModuleArg; /* 0: module 1: schema 2: vtab name 3...: args */
- VTable *pVTable; /* List of VTable objects. */
-#endif
- Trigger *pTrigger; /* List of triggers stored in pSchema */
+ u8 eTabType; /* 0: normal, 1: virtual, 2: view */
+ union {
+ struct { /* Used by ordinary tables: */
+ int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */
+ FKey *pFKey; /* Linked list of all foreign keys in this table */
+ ExprList *pDfltList; /* DEFAULT clauses on various columns.
+ ** Or the AS clause for generated columns. */
+ } tab;
+ struct { /* Used by views: */
+ Select *pSelect; /* View definition */
+ } view;
+ struct { /* Used by virtual tables only: */
+ int nArg; /* Number of arguments to the module */
+ char **azArg; /* 0: module 1: schema 2: vtab name 3...: args */
+ VTable *p; /* List of VTable objects. */
+ } vtab;
+ } u;
+ Trigger *pTrigger; /* List of triggers on this object */
Schema *pSchema; /* Schema that contains this table */
- Table *pNextZombie; /* Next on the Parse.pZombieTab list */
};
/*
@@ -16852,20 +18353,43 @@ struct Table {
** followed by non-hidden columns. Example: "CREATE VIRTUAL TABLE x USING
** vtab1(a HIDDEN, b);". Since "b" is a non-hidden column but "a" is hidden,
** the TF_OOOHidden attribute would apply in this case. Such tables require
-** special handling during INSERT processing.
-*/
-#define TF_Readonly 0x0001 /* Read-only system table */
-#define TF_Ephemeral 0x0002 /* An ephemeral table */
-#define TF_HasPrimaryKey 0x0004 /* Table has a primary key */
-#define TF_Autoincrement 0x0008 /* Integer primary key is autoincrement */
-#define TF_HasStat1 0x0010 /* nRowLogEst set from sqlite_stat1 */
-#define TF_WithoutRowid 0x0020 /* No rowid. PRIMARY KEY is the key */
-#define TF_NoVisibleRowid 0x0040 /* No user-visible "rowid" column */
-#define TF_OOOHidden 0x0080 /* Out-of-Order hidden columns */
-#define TF_StatsUsed 0x0100 /* Query planner decisions affected by
+** special handling during INSERT processing. The "OOO" means "Out Of Order".
+**
+** Constraints:
+**
+** TF_HasVirtual == COLFLAG_VIRTUAL
+** TF_HasStored == COLFLAG_STORED
+** TF_HasHidden == COLFLAG_HIDDEN
+*/
+#define TF_Readonly 0x00000001 /* Read-only system table */
+#define TF_HasHidden 0x00000002 /* Has one or more hidden columns */
+#define TF_HasPrimaryKey 0x00000004 /* Table has a primary key */
+#define TF_Autoincrement 0x00000008 /* Integer primary key is autoincrement */
+#define TF_HasStat1 0x00000010 /* nRowLogEst set from sqlite_stat1 */
+#define TF_HasVirtual 0x00000020 /* Has one or more VIRTUAL columns */
+#define TF_HasStored 0x00000040 /* Has one or more STORED columns */
+#define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */
+#define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */
+#define TF_StatsUsed 0x00000100 /* Query planner decisions affected by
** Index.aiRowLogEst[] values */
-#define TF_HasNotNull 0x0200 /* Contains NOT NULL constraints */
-#define TF_Shadow 0x0400 /* True for a shadow table */
+#define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */
+#define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */
+#define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */
+#define TF_Shadow 0x00001000 /* True for a shadow table */
+#define TF_HasStat4 0x00002000 /* STAT4 info available for this table */
+#define TF_Ephemeral 0x00004000 /* An ephemeral table */
+#define TF_Eponymous 0x00008000 /* An eponymous virtual table */
+#define TF_Strict 0x00010000 /* STRICT mode */
+
+/*
+** Allowed values for Table.eTabType
+*/
+#define TABTYP_NORM 0 /* Ordinary table */
+#define TABTYP_VTAB 1 /* Virtual table */
+#define TABTYP_VIEW 2 /* A view */
+
+#define IsView(X) ((X)->eTabType==TABTYP_VIEW)
+#define IsOrdinaryTable(X) ((X)->eTabType==TABTYP_NORM)
/*
** Test to see whether or not a table is a virtual table. This is
@@ -16873,9 +18397,12 @@ struct Table {
** table support is omitted from the build.
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
-# define IsVirtual(X) ((X)->nModuleArg)
+# define IsVirtual(X) ((X)->eTabType==TABTYP_VTAB)
+# define ExprIsVtab(X) \
+ ((X)->op==TK_COLUMN && (X)->y.pTab->eTabType==TABTYP_VTAB)
#else
# define IsVirtual(X) 0
+# define ExprIsVtab(X) 0
#endif
/*
@@ -16959,16 +18486,22 @@ struct FKey {
** is returned. REPLACE means that preexisting database rows that caused
** a UNIQUE constraint violation are removed so that the new insert or
** update can proceed. Processing continues and no error is reported.
+** UPDATE applies to insert operations only and means that the insert
+** is omitted and the DO UPDATE clause of an upsert is run instead.
**
-** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys.
+** RESTRICT, SETNULL, SETDFLT, and CASCADE actions apply only to foreign keys.
** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the
** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign
-** key is set to NULL. CASCADE means that a DELETE or UPDATE of the
+** key is set to NULL. SETDFLT means that the foreign key is set
+** to its default value. CASCADE means that a DELETE or UPDATE of the
** referenced table row is propagated into the row that holds the
** foreign key.
**
+** The OE_Default value is a place holder that means to use whatever
+** conflict resolution algorithm is required from context.
+**
** The following symbolic values are used to record which type
-** of action to take.
+** of conflict resolution action to take.
*/
#define OE_None 0 /* There is no constraint to check */
#define OE_Rollback 1 /* Fail the operation and rollback the transaction */
@@ -16999,11 +18532,17 @@ struct KeyInfo {
u16 nKeyField; /* Number of key columns in the index */
u16 nAllField; /* Total columns, including key plus others */
sqlite3 *db; /* The database connection */
- u8 *aSortOrder; /* Sort order for each column. */
+ u8 *aSortFlags; /* Sort order for each column. */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
};
/*
+** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
+*/
+#define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */
+#define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */
+
+/*
** This object holds a record which has been parsed out into individual
** fields, for the purposes of doing a comparison.
**
@@ -17041,6 +18580,11 @@ struct KeyInfo {
struct UnpackedRecord {
KeyInfo *pKeyInfo; /* Collation and sort-order information */
Mem *aMem; /* Values */
+ union {
+ char *z; /* Cache of aMem[0].z for vdbeRecordCompareString() */
+ i64 i; /* Cache of aMem[0].u.i for vdbeRecordCompareInt() */
+ } u;
+ int n; /* Cache of aMem[0].n used by vdbeRecordCompareString() */
u16 nField; /* Number of entries in apMem[] */
i8 default_rc; /* Comparison result if keys are equal */
u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */
@@ -17072,12 +18616,24 @@ struct UnpackedRecord {
** The Index.onError field determines whether or not the indexed columns
** must be unique and what to do if they are not. When Index.onError=OE_None,
** it means this is not a unique index. Otherwise it is a unique index
-** and the value of Index.onError indicate the which conflict resolution
-** algorithm to employ whenever an attempt is made to insert a non-unique
+** and the value of Index.onError indicates which conflict resolution
+** algorithm to employ when an attempt is made to insert a non-unique
** element.
**
+** The colNotIdxed bitmask is used in combination with SrcItem.colUsed
+** for a fast test to see if an index can serve as a covering index.
+** colNotIdxed has a 1 bit for every column of the original table that
+** is *not* available in the index. Thus the expression
+** "colUsed & colNotIdxed" will be non-zero if the index is not a
+** covering index. The most significant bit of of colNotIdxed will always
+** be true (note-20221022-a). If a column beyond the 63rd column of the
+** table is used, the "colUsed & colNotIdxed" test will always be non-zero
+** and we have to assume either that the index is not covering, or use
+** an alternative (slower) algorithm to determine whether or not
+** the index is covering.
+**
** While parsing a CREATE TABLE or CREATE INDEX statement in order to
-** generate VDBE code (as opposed to parsing one read from an sqlite_master
+** generate VDBE code (as opposed to parsing one read from an sqlite_schema
** table as part of parsing an existing database schema), transient instances
** of this structure may be created. In this case the Index.tnum variable is
** used to store the address of a VDBE instruction, not a database page
@@ -17096,28 +18652,34 @@ struct Index {
const char **azColl; /* Array of collation sequence names for index */
Expr *pPartIdxWhere; /* WHERE clause for partial indices */
ExprList *aColExpr; /* Column expressions */
- int tnum; /* DB Page containing root of this index */
+ Pgno tnum; /* DB Page containing root of this index */
LogEst szIdxRow; /* Estimated average row size in bytes */
u16 nKeyCol; /* Number of columns forming the key */
u16 nColumn; /* Number of columns stored in the index */
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
- unsigned idxType:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
+ unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */
unsigned bUnordered:1; /* Use this index for == or IN queries only */
unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
unsigned isResized:1; /* True if resizeIndexObject() has been called */
unsigned isCovering:1; /* True if this is a covering index */
unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
+ unsigned bLowQual:1; /* sqlite_stat1 says this is a low-quality index */
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
+ unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
+ unsigned bHasExpr:1; /* Index contains an expression, either a literal
+ ** expression, or a reference to a VIRTUAL column */
+#ifdef SQLITE_ENABLE_STAT4
int nSample; /* Number of elements in aSample[] */
+ int mxSample; /* Number of slots allocated to aSample[] */
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */
IndexSample *aSample; /* Samples of the left-most key */
tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */
tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */
#endif
- Bitmask colNotIdxed; /* 0 for unindexed columns in pTab */
+ Bitmask colNotIdxed; /* Unindexed columns in pTab */
};
/*
@@ -17126,6 +18688,7 @@ struct Index {
#define SQLITE_IDXTYPE_APPDEF 0 /* Created using CREATE INDEX */
#define SQLITE_IDXTYPE_UNIQUE 1 /* Implements a UNIQUE constraint */
#define SQLITE_IDXTYPE_PRIMARYKEY 2 /* Is the PRIMARY KEY for the table */
+#define SQLITE_IDXTYPE_IPK 3 /* INTEGER PRIMARY KEY index */
/* Return true if index X is a PRIMARY KEY index */
#define IsPrimaryKeyIndex(X) ((X)->idxType==SQLITE_IDXTYPE_PRIMARYKEY)
@@ -17140,7 +18703,7 @@ struct Index {
#define XN_EXPR (-2) /* Indexed column is an expression */
/*
-** Each sample stored in the sqlite_stat3 table is represented in memory
+** Each sample stored in the sqlite_stat4 table is represented in memory
** using a structure of this type. See documentation at the top of the
** analyze.c source file for additional information.
*/
@@ -17178,7 +18741,7 @@ struct Token {
** code for a SELECT that contains aggregate functions.
**
** If Expr.op==TK_AGG_COLUMN or TK_AGG_FUNCTION then Expr.pAggInfo is a
-** pointer to this structure. The Expr.iColumn field is the index in
+** pointer to this structure. The Expr.iAgg field is the index in
** AggInfo.aCol[] or AggInfo.aFunc[] of information needed to generate
** code for that node.
**
@@ -17191,43 +18754,61 @@ struct AggInfo {
** from source tables rather than from accumulators */
u8 useSortingIdx; /* In direct mode, reference the sorting index rather
** than the source table */
+ u16 nSortingColumn; /* Number of columns in the sorting index */
int sortingIdx; /* Cursor number of the sorting index */
int sortingIdxPTab; /* Cursor number of pseudo-table */
- int nSortingColumn; /* Number of columns in the sorting index */
- int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */
+ int iFirstReg; /* First register in range for aCol[] and aFunc[] */
ExprList *pGroupBy; /* The group by clause */
struct AggInfo_col { /* For each column used in source tables */
Table *pTab; /* Source table */
+ Expr *pCExpr; /* The original expression */
int iTable; /* Cursor number of the source table */
- int iColumn; /* Column number within the source table */
- int iSorterColumn; /* Column number in the sorting index */
- int iMem; /* Memory location that acts as accumulator */
- Expr *pExpr; /* The original expression */
+ i16 iColumn; /* Column number within the source table */
+ i16 iSorterColumn; /* Column number in the sorting index */
} *aCol;
int nColumn; /* Number of used entries in aCol[] */
int nAccumulator; /* Number of columns that show through to the output.
** Additional columns are used only as parameters to
** aggregate functions */
struct AggInfo_func { /* For each aggregate function */
- Expr *pExpr; /* Expression encoding the function */
+ Expr *pFExpr; /* Expression encoding the function */
FuncDef *pFunc; /* The aggregate function implementation */
- int iMem; /* Memory location that acts as accumulator */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
+ int iDistAddr; /* Address of OP_OpenEphemeral */
+ int iOBTab; /* Ephemeral table to implement ORDER BY */
+ u8 bOBPayload; /* iOBTab has payload columns separate from key */
+ u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */
+ u8 bUseSubtype; /* Transfer subtype info through sorter */
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
+ u32 selId; /* Select to which this AggInfo belongs */
+#ifdef SQLITE_DEBUG
+ Select *pSelect; /* SELECT statement that this AggInfo supports */
+#endif
};
/*
+** Macros to compute aCol[] and aFunc[] register numbers.
+**
+** These macros should not be used prior to the call to
+** assignAggregateRegisters() that computes the value of pAggInfo->iFirstReg.
+** The assert()s that are part of this macro verify that constraint.
+*/
+#define AggInfoColumnReg(A,I) (assert((A)->iFirstReg),(A)->iFirstReg+(I))
+#define AggInfoFuncReg(A,I) \
+ (assert((A)->iFirstReg),(A)->iFirstReg+(A)->nColumn+(I))
+
+/*
** The datatype ynVar is a signed integer, either 16-bit or 32-bit.
** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater
** than 32767 we have to make it 32-bit. 16-bit is preferred because
** it uses less memory in the Expr object, which is a big memory user
** in systems with lots of prepared statements. And few applications
** need more than about 10 or 20 variables. But some extreme users want
-** to have prepared statements with over 32767 variables, and for them
+** to have prepared statements with over 32766 variables, and for them
** the option is available (at compile-time).
*/
-#if SQLITE_MAX_VARIABLE_NUMBER<=32767
+#if SQLITE_MAX_VARIABLE_NUMBER<32767
typedef i16 ynVar;
#else
typedef int ynVar;
@@ -17244,10 +18825,10 @@ typedef int ynVar;
** tree.
**
** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB,
-** or TK_STRING), then Expr.token contains the text of the SQL literal. If
-** the expression is a variable (TK_VARIABLE), then Expr.token contains the
+** or TK_STRING), then Expr.u.zToken contains the text of the SQL literal. If
+** the expression is a variable (TK_VARIABLE), then Expr.u.zToken contains the
** variable name. Finally, if the expression is an SQL function (TK_FUNCTION),
-** then Expr.token contains the name of the function.
+** then Expr.u.zToken contains the name of the function.
**
** Expr.pRight and Expr.pLeft are the left and right subexpressions of a
** binary operator. Either or both may be NULL.
@@ -17287,7 +18868,7 @@ typedef int ynVar;
** help reduce memory requirements, sometimes an Expr object will be
** truncated. And to reduce the number of memory allocations, sometimes
** two or more Expr objects will be stored in a single memory allocation,
-** together with Expr.zToken strings.
+** together with Expr.u.zToken strings.
**
** If the EP_Reduced and EP_TokenOnly flags are set when
** an Expr object is truncated. When EP_Reduced is set, then all
@@ -17298,7 +18879,14 @@ typedef int ynVar;
*/
struct Expr {
u8 op; /* Operation performed by this node */
- char affinity; /* The affinity of the column or 0 if not a column */
+ char affExpr; /* affinity, or RAISE type */
+ u8 op2; /* TK_REGISTER/TK_TRUTH: original value of Expr.op
+ ** TK_COLUMN: the value of p5 for OP_Column
+ ** TK_AGG_FUNCTION: nesting depth
+ ** TK_FUNCTION: NC_SelfRef flag if needs OP_PureFunc */
+#ifdef SQLITE_DEBUG
+ u8 vvaFlags; /* Verification flags. */
+#endif
u32 flags; /* Various flags. EP_* See below */
union {
char *zToken; /* Token value. Zero terminated and dequoted */
@@ -17329,75 +18917,114 @@ struct Expr {
** TK_REGISTER: register number
** TK_TRIGGER: 1 -> new, 0 -> old
** EP_Unlikely: 134217728 times likelihood
+ ** TK_IN: ephemeral table holding RHS
+ ** TK_SELECT_COLUMN: Number of columns on the LHS
** TK_SELECT: 1st register of result vector */
ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid.
** TK_VARIABLE: variable number (always >= 1).
** TK_SELECT_COLUMN: column of the result vector */
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
- i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
- u8 op2; /* TK_REGISTER: original value of Expr.op
- ** TK_COLUMN: the value of p5 for OP_Column
- ** TK_AGG_FUNCTION: nesting depth */
+ union {
+ int iJoin; /* If EP_OuterON or EP_InnerON, the right table */
+ int iOfst; /* else: start of token from start of statement */
+ } w;
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
union {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
** for a column of an index on an expression */
- Window *pWin; /* TK_FUNCTION: Window definition for the func */
+ Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
+ struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
+ int iAddr; /* Subroutine entry address */
+ int regReturn; /* Register used to hold return address */
+ } sub;
} y;
};
-/*
-** The following are the meanings of bits in the Expr.flags field.
-*/
-#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */
-#define EP_Agg 0x000002 /* Contains one or more aggregate functions */
-#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */
-#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */
-#define EP_Distinct 0x000010 /* Aggregate function with DISTINCT keyword */
-#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
-#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
-#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */
-#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */
-#define EP_Generic 0x000200 /* Ignore COLLATE or affinity on this tree */
-#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */
-#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */
-#define EP_Skip 0x001000 /* COLLATE, AS, or UNLIKELY */
-#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
-#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
-#define EP_Static 0x008000 /* Held in memory not obtained from malloc() */
-#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
-#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
-#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
-#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
-#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
-#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
-#define EP_Alias 0x400000 /* Is an alias for a result set column */
-#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
-#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
-
-/*
-** The EP_Propagate mask is a set of properties that automatically propagate
+/* The following are the meanings of bits in the Expr.flags field.
+** Value restrictions:
+**
+** EP_Agg == NC_HasAgg == SF_HasAgg
+** EP_Win == NC_HasWin
+*/
+#define EP_OuterON 0x000001 /* Originates in ON/USING clause of outer join */
+#define EP_InnerON 0x000002 /* Originates in ON/USING of an inner join */
+#define EP_Distinct 0x000004 /* Aggregate function with DISTINCT keyword */
+#define EP_HasFunc 0x000008 /* Contains one or more functions of any kind */
+#define EP_Agg 0x000010 /* Contains one or more aggregate functions */
+#define EP_FixedCol 0x000020 /* TK_Column with a known fixed value */
+#define EP_VarSelect 0x000040 /* pSelect is correlated, not constant */
+#define EP_DblQuoted 0x000080 /* token.z was originally in "..." */
+#define EP_InfixFunc 0x000100 /* True for an infix function: LIKE, GLOB, etc */
+#define EP_Collate 0x000200 /* Tree contains a TK_COLLATE operator */
+#define EP_Commuted 0x000400 /* Comparison operator has been commuted */
+#define EP_IntValue 0x000800 /* Integer value contained in u.iValue */
+#define EP_xIsSelect 0x001000 /* x.pSelect is valid (otherwise x.pList is) */
+#define EP_Skip 0x002000 /* Operator does not contribute to affinity */
+#define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
+#define EP_Win 0x008000 /* Contains window functions */
+#define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
+#define EP_FullSize 0x020000 /* Expr structure must remain full sized */
+#define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */
+#define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */
+#define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
+#define EP_CanBeNull 0x200000 /* Can be null despite NOT NULL constraint */
+#define EP_Subquery 0x400000 /* Tree contains a TK_SELECT operator */
+#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
+#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
+#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
+#define EP_Quoted 0x4000000 /* TK_ID was originally quoted */
+#define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */
+#define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */
+#define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */
+#define EP_FromDDL 0x40000000 /* Originates from sqlite_schema */
+ /* 0x80000000 // Available */
+
+/* The EP_Propagate mask is a set of properties that automatically propagate
** upwards into parent nodes.
*/
#define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc)
-/*
-** These macros can be used to test, set, or clear bits in the
+/* Macros can be used to test, set, or clear bits in the
** Expr.flags field.
*/
#define ExprHasProperty(E,P) (((E)->flags&(P))!=0)
#define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P))
#define ExprSetProperty(E,P) (E)->flags|=(P)
#define ExprClearProperty(E,P) (E)->flags&=~(P)
+#define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue)
+#define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse)
+#define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0)
+
+/* Macros used to ensure that the correct members of unions are accessed
+** in Expr.
+*/
+#define ExprUseUToken(E) (((E)->flags&EP_IntValue)==0)
+#define ExprUseUValue(E) (((E)->flags&EP_IntValue)!=0)
+#define ExprUseWOfst(E) (((E)->flags&(EP_InnerON|EP_OuterON))==0)
+#define ExprUseWJoin(E) (((E)->flags&(EP_InnerON|EP_OuterON))!=0)
+#define ExprUseXList(E) (((E)->flags&EP_xIsSelect)==0)
+#define ExprUseXSelect(E) (((E)->flags&EP_xIsSelect)!=0)
+#define ExprUseYTab(E) (((E)->flags&(EP_WinFunc|EP_Subrtn))==0)
+#define ExprUseYWin(E) (((E)->flags&EP_WinFunc)!=0)
+#define ExprUseYSub(E) (((E)->flags&EP_Subrtn)!=0)
+
+/* Flags for use with Expr.vvaFlags
+*/
+#define EP_NoReduce 0x01 /* Cannot EXPRDUP_REDUCE this Expr */
+#define EP_Immutable 0x02 /* Do not change this Expr node */
/* The ExprSetVVAProperty() macro is used for Verification, Validation,
** and Accreditation only. It works like ExprSetProperty() during VVA
** processes but is a no-op for delivery.
*/
#ifdef SQLITE_DEBUG
-# define ExprSetVVAProperty(E,P) (E)->flags|=(P)
+# define ExprSetVVAProperty(E,P) (E)->vvaFlags|=(P)
+# define ExprHasVVAProperty(E,P) (((E)->vvaFlags&(P))!=0)
+# define ExprClearVVAProperties(E) (E)->vvaFlags = 0
#else
# define ExprSetVVAProperty(E,P)
+# define ExprHasVVAProperty(E,P) 0
+# define ExprClearVVAProperties(E)
#endif
/*
@@ -17416,6 +19043,18 @@ struct Expr {
#define EXPRDUP_REDUCE 0x0001 /* Used reduced-size Expr nodes */
/*
+** True if the expression passed as an argument was a function with
+** an OVER() clause (a window function).
+*/
+#ifdef SQLITE_OMIT_WINDOWFUNC
+# define IsWindowFunc(p) 0
+#else
+# define IsWindowFunc(p) ( \
+ ExprHasProperty((p), EP_WinFunc) && p->y.pWin->eFrmType!=TK_FILTER \
+ )
+#endif
+
+/*
** A list of expressions. Each expression may optionally have a
** name. An expr/name combination can be used in several ways, such
** as the list of "expr AS ID" fields following a "SELECT" or in the
@@ -17423,36 +19062,58 @@ struct Expr {
** also be used as the argument to a function, in which case the a.zName
** field is not used.
**
-** By default the Expr.zSpan field holds a human-readable description of
-** the expression that is used in the generation of error messages and
-** column labels. In this case, Expr.zSpan is typically the text of a
-** column expression as it exists in a SELECT statement. However, if
-** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name
-** of the result column in the form: DATABASE.TABLE.COLUMN. This later
-** form is used for name resolution with nested FROM clauses.
+** In order to try to keep memory usage down, the Expr.a.zEName field
+** is used for multiple purposes:
+**
+** eEName Usage
+** ---------- -------------------------
+** ENAME_NAME (1) the AS of result set column
+** (2) COLUMN= of an UPDATE
+**
+** ENAME_TAB DB.TABLE.NAME used to resolve names
+** of subqueries
+**
+** ENAME_SPAN Text of the original result set
+** expression.
*/
struct ExprList {
int nExpr; /* Number of expressions on the list */
+ int nAlloc; /* Number of a[] slots allocated */
struct ExprList_item { /* For each expression in the list */
Expr *pExpr; /* The parse tree for this expression */
- char *zName; /* Token associated with this expression */
- char *zSpan; /* Original text of the expression */
- u8 sortOrder; /* 1 for DESC or 0 for ASC */
- unsigned done :1; /* A flag to indicate when processing is finished */
- unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
- unsigned reusable :1; /* Constant expression is reusable */
- unsigned bSorterRef :1; /* Defer evaluation until after sorting */
+ char *zEName; /* Token associated with this expression */
+ struct {
+ u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */
+ unsigned eEName :2; /* Meaning of zEName */
+ unsigned done :1; /* Indicates when processing is finished */
+ unsigned reusable :1; /* Constant expression is reusable */
+ unsigned bSorterRef :1; /* Defer evaluation until after sorting */
+ unsigned bNulls :1; /* True if explicit "NULLS FIRST/LAST" */
+ unsigned bUsed :1; /* This column used in a SF_NestedFrom subquery */
+ unsigned bUsingTerm:1; /* Term from the USING clause of a NestedFrom */
+ unsigned bNoExpand: 1; /* Term is an auxiliary in NestedFrom and should
+ ** not be expanded by "*" in parent queries */
+ } fg;
union {
- struct {
+ struct { /* Used by any ExprList other than Parse.pConsExpr */
u16 iOrderByCol; /* For ORDER BY, column number in result set */
u16 iAlias; /* Index into Parse.aAlias[] for zName */
} x;
- int iConstExprReg; /* Register in which Expr value is cached */
+ int iConstExprReg; /* Register in which Expr value is cached. Used only
+ ** by Parse.pConstExpr */
} u;
} a[1]; /* One slot for each expression in the list */
};
/*
+** Allowed values for Expr.a.eEName
+*/
+#define ENAME_NAME 0 /* The AS clause of a result set */
+#define ENAME_SPAN 1 /* Complete text of the result set expression */
+#define ENAME_TAB 2 /* "DB.TABLE.NAME" for the result set */
+#define ENAME_ROWID 3 /* "DB.TABLE._rowid_" for * expansion of rowid */
+
+/*
** An instance of this structure can hold a simple list of identifiers,
** such as the list "a,b,c" in the following statements:
**
@@ -17468,23 +19129,28 @@ struct ExprList {
** If "a" is the k-th column of table "t", then IdList.a[0].idx==k.
*/
struct IdList {
+ int nId; /* Number of identifiers on the list */
+ u8 eU4; /* Which element of a.u4 is valid */
struct IdList_item {
char *zName; /* Name of the identifier */
- int idx; /* Index in some Table.aCol[] of a column named zName */
- } *a;
- int nId; /* Number of identifiers on the list */
+ union {
+ int idx; /* Index in some Table.aCol[] of a column named zName */
+ Expr *pExpr; /* Expr to implement a USING variable -- NOT USED */
+ } u4;
+ } a[1];
};
/*
-** The following structure describes the FROM clause of a SELECT statement.
-** Each table or subquery in the FROM clause is a separate element of
-** the SrcList.a[] array.
-**
-** With the addition of multiple database support, the following structure
-** can also be used to describe a particular table such as the table that
-** is modified by an INSERT, DELETE, or UPDATE statement. In standard SQL,
-** such a table must be a simple name: ID. But in SQLite, the table can
-** now be identified by a database name, a dot, then the table name: ID.ID.
+** Allowed values for IdList.eType, which determines which value of the a.u4
+** is valid.
+*/
+#define EU4_NONE 0 /* Does not use IdList.a.u4 */
+#define EU4_IDX 1 /* Uses IdList.a.u4.idx */
+#define EU4_EXPR 2 /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */
+
+/*
+** The SrcItem object represents a single term in the FROM clause of a query.
+** The SrcList object is mostly an array of SrcItems.
**
** The jointype starts out showing the join type between the current table
** and the next table on the list. The parser builds the list this way.
@@ -17493,52 +19159,91 @@ struct IdList {
**
** In the colUsed field, the high-order bit (bit 63) is set if the table
** contains more than 63 columns and the 64-th or later column is used.
+**
+** Union member validity:
+**
+** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc
+** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy
+** u2.pIBIndex fg.isIndexedBy && !fg.isCte
+** u2.pCteUse fg.isCte && !fg.isIndexedBy
+*/
+struct SrcItem {
+ Schema *pSchema; /* Schema to which this item is fixed */
+ char *zDatabase; /* Name of database holding this table */
+ char *zName; /* Name of the table */
+ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
+ Table *pTab; /* An SQL table corresponding to zName */
+ Select *pSelect; /* A SELECT statement used in place of a table name */
+ int addrFillSub; /* Address of subroutine to manifest a subquery */
+ int regReturn; /* Register holding return address of addrFillSub */
+ int regResult; /* Registers holding results of a co-routine */
+ struct {
+ u8 jointype; /* Type of join between this table and the previous */
+ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
+ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */
+ unsigned isTabFunc :1; /* True if table-valued-function syntax */
+ unsigned isCorrelated :1; /* True if sub-query is correlated */
+ unsigned isMaterialized:1; /* This is a materialized view */
+ unsigned viaCoroutine :1; /* Implemented as a co-routine */
+ unsigned isRecursive :1; /* True for recursive reference in WITH */
+ unsigned fromDDL :1; /* Comes from sqlite_schema */
+ unsigned isCte :1; /* This is a CTE */
+ unsigned notCte :1; /* This item may not match a CTE */
+ unsigned isUsing :1; /* u3.pUsing is valid */
+ unsigned isOn :1; /* u3.pOn was once valid and non-NULL */
+ unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */
+ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */
+ } fg;
+ int iCursor; /* The VDBE cursor number used to access this table */
+ union {
+ Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */
+ IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */
+ } u3;
+ Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */
+ union {
+ char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
+ ExprList *pFuncArg; /* Arguments to table-valued-function */
+ } u1;
+ union {
+ Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
+ CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */
+ } u2;
+};
+
+/*
+** The OnOrUsing object represents either an ON clause or a USING clause.
+** It can never be both at the same time, but it can be neither.
+*/
+struct OnOrUsing {
+ Expr *pOn; /* The ON clause of a join */
+ IdList *pUsing; /* The USING clause of a join */
+};
+
+/*
+** This object represents one or more tables that are the source of
+** content for an SQL statement. For example, a single SrcList object
+** is used to hold the FROM clause of a SELECT statement. SrcList also
+** represents the target tables for DELETE, INSERT, and UPDATE statements.
+**
*/
struct SrcList {
int nSrc; /* Number of tables or subqueries in the FROM clause */
u32 nAlloc; /* Number of entries allocated in a[] below */
- struct SrcList_item {
- Schema *pSchema; /* Schema to which this item is fixed */
- char *zDatabase; /* Name of database holding this table */
- char *zName; /* Name of the table */
- char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
- Table *pTab; /* An SQL table corresponding to zName */
- Select *pSelect; /* A SELECT statement used in place of a table name */
- int addrFillSub; /* Address of subroutine to manifest a subquery */
- int regReturn; /* Register holding return address of addrFillSub */
- int regResult; /* Registers holding results of a co-routine */
- struct {
- u8 jointype; /* Type of join between this table and the previous */
- unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
- unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */
- unsigned isTabFunc :1; /* True if table-valued-function syntax */
- unsigned isCorrelated :1; /* True if sub-query is correlated */
- unsigned viaCoroutine :1; /* Implemented as a co-routine */
- unsigned isRecursive :1; /* True for recursive reference in WITH */
- } fg;
- int iCursor; /* The VDBE cursor number used to access this table */
- Expr *pOn; /* The ON clause of a join */
- IdList *pUsing; /* The USING clause of a join */
- Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */
- union {
- char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
- ExprList *pFuncArg; /* Arguments to table-valued-function */
- } u1;
- Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
- } a[1]; /* One entry for each identifier on the list */
+ SrcItem a[1]; /* One entry for each identifier on the list */
};
/*
** Permitted values of the SrcList.a.jointype field
*/
-#define JT_INNER 0x0001 /* Any kind of inner or cross join */
-#define JT_CROSS 0x0002 /* Explicit use of the CROSS keyword */
-#define JT_NATURAL 0x0004 /* True for a "natural" join */
-#define JT_LEFT 0x0008 /* Left outer join */
-#define JT_RIGHT 0x0010 /* Right outer join */
-#define JT_OUTER 0x0020 /* The "OUTER" keyword is present */
-#define JT_ERROR 0x0040 /* unknown or unsupported join type */
-
+#define JT_INNER 0x01 /* Any kind of inner or cross join */
+#define JT_CROSS 0x02 /* Explicit use of the CROSS keyword */
+#define JT_NATURAL 0x04 /* True for a "natural" join */
+#define JT_LEFT 0x08 /* Left outer join */
+#define JT_RIGHT 0x10 /* Right outer join */
+#define JT_OUTER 0x20 /* The "OUTER" keyword is present */
+#define JT_LTORJ 0x40 /* One of the LEFT operands of a RIGHT JOIN
+ ** Mnemonic: Left Table Of Right Join */
+#define JT_ERROR 0x80 /* unknown or unsupported join type */
/*
** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin()
@@ -17559,9 +19264,9 @@ struct SrcList {
#define WHERE_DISTINCTBY 0x0080 /* pOrderby is really a DISTINCT clause */
#define WHERE_WANT_DISTINCT 0x0100 /* All output needs to be distinct */
#define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */
-#define WHERE_SEEK_TABLE 0x0400 /* Do not defer seeks on main table */
+#define WHERE_AGG_DISTINCT 0x0400 /* Query is "SELECT agg(DISTINCT ...)" */
#define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */
-#define WHERE_SEEK_UNIQ_TABLE 0x1000 /* Do not defer seeks if unique */
+#define WHERE_RIGHT_JOIN 0x1000 /* Processing a RIGHT JOIN */
/* 0x2000 not currently used */
#define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */
/* 0x8000 not currently used */
@@ -17601,11 +19306,13 @@ struct NameContext {
ExprList *pEList; /* Optional list of result-set columns */
AggInfo *pAggInfo; /* Information about aggregates at this level */
Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */
+ int iBaseReg; /* For TK_REGISTER when parsing RETURNING */
} uNC;
NameContext *pNext; /* Next outer name context. NULL for outermost */
int nRef; /* Number of names resolved by this context */
- int nErr; /* Number of errors encountered while resolving names */
- u16 ncFlags; /* Zero or more NC_* flags defined below */
+ int nNcErr; /* Number of errors encountered while resolving names */
+ int ncFlags; /* Zero or more NC_* flags defined below */
+ u32 nNestedSelect; /* Number of nested selects using this NC */
Select *pWinSelect; /* SELECT statement for any window functions */
};
@@ -17613,23 +19320,33 @@ struct NameContext {
** Allowed values for the NameContext, ncFlags field.
**
** Value constraints (all checked via assert()):
-** NC_HasAgg == SF_HasAgg
-** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX
-**
-*/
-#define NC_AllowAgg 0x0001 /* Aggregate functions are allowed here */
-#define NC_PartIdx 0x0002 /* True if resolving a partial index WHERE */
-#define NC_IsCheck 0x0004 /* True if resolving names in a CHECK constraint */
-#define NC_InAggFunc 0x0008 /* True if analyzing arguments to an agg func */
-#define NC_HasAgg 0x0010 /* One or more aggregate functions seen */
-#define NC_IdxExpr 0x0020 /* True if resolving columns of CREATE INDEX */
-#define NC_VarSelect 0x0040 /* A correlated subquery has been seen */
-#define NC_UEList 0x0080 /* True if uNC.pEList is used */
-#define NC_UAggInfo 0x0100 /* True if uNC.pAggInfo is used */
-#define NC_UUpsert 0x0200 /* True if uNC.pUpsert is used */
-#define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */
-#define NC_Complex 0x2000 /* True if a function or subquery seen */
-#define NC_AllowWin 0x4000 /* Window functions are allowed here */
+** NC_HasAgg == SF_HasAgg == EP_Agg
+** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX
+** NC_OrderAgg == SF_OrderByReqd == SQLITE_FUNC_ANYORDER
+** NC_HasWin == EP_Win
+**
+*/
+#define NC_AllowAgg 0x000001 /* Aggregate functions are allowed here */
+#define NC_PartIdx 0x000002 /* True if resolving a partial index WHERE */
+#define NC_IsCheck 0x000004 /* True if resolving a CHECK constraint */
+#define NC_GenCol 0x000008 /* True for a GENERATED ALWAYS AS clause */
+#define NC_HasAgg 0x000010 /* One or more aggregate functions seen */
+#define NC_IdxExpr 0x000020 /* True if resolving columns of CREATE INDEX */
+#define NC_SelfRef 0x00002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */
+#define NC_Subquery 0x000040 /* A subquery has been seen */
+#define NC_UEList 0x000080 /* True if uNC.pEList is used */
+#define NC_UAggInfo 0x000100 /* True if uNC.pAggInfo is used */
+#define NC_UUpsert 0x000200 /* True if uNC.pUpsert is used */
+#define NC_UBaseReg 0x000400 /* True if uNC.iBaseReg is used */
+#define NC_MinMaxAgg 0x001000 /* min/max aggregates seen. See note above */
+#define NC_Complex 0x002000 /* True if a function or subquery seen */
+#define NC_AllowWin 0x004000 /* Window functions are allowed here */
+#define NC_HasWin 0x008000 /* One or more window functions seen */
+#define NC_IsDDL 0x010000 /* Resolving names in a CREATE statement */
+#define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */
+#define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */
+#define NC_NoSelect 0x080000 /* Do not descend into sub-selects */
+#define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */
/*
** An instance of the following object describes a single ON CONFLICT
@@ -17640,21 +19357,27 @@ struct NameContext {
** conflict-target clause.) The pUpsertTargetWhere is the optional
** WHERE clause used to identify partial unique indexes.
**
-** pUpsertSet is the list of column=expr terms of the UPDATE statement.
+** pUpsertSet is the list of column=expr terms of the UPDATE statement.
** The pUpsertSet field is NULL for a ON CONFLICT DO NOTHING. The
** pUpsertWhere is the WHERE clause for the UPDATE and is NULL if the
** WHERE clause is omitted.
*/
struct Upsert {
- ExprList *pUpsertTarget; /* Optional description of conflicting index */
+ ExprList *pUpsertTarget; /* Optional description of conflict target */
Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */
ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */
Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */
- /* The fields above comprise the parse tree for the upsert clause.
- ** The fields below are used to transfer information from the INSERT
- ** processing down into the UPDATE processing while generating code.
- ** Upsert owns the memory allocated above, but not the memory below. */
- Index *pUpsertIdx; /* Constraint that pUpsertTarget identifies */
+ Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */
+ u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */
+ /* Above this point is the parse tree for the ON CONFLICT clauses.
+ ** The next group of fields stores intermediate data. */
+ void *pToFree; /* Free memory when deleting the Upsert object */
+ /* All fields above are owned by the Upsert object and must be freed
+ ** when the Upsert is destroyed. The fields below are used to transfer
+ ** information from the INSERT processing down into the UPDATE processing
+ ** while generating code. The fields below are owned by the INSERT
+ ** statement and will be freed by INSERT processing. */
+ Index *pUpsertIdx; /* UNIQUE constraint specified by pUpsertTarget */
SrcList *pUpsertSrc; /* Table to be updated */
int regData; /* First register holding array of VALUES */
int iDataCur; /* Index of the data cursor */
@@ -17679,13 +19402,13 @@ struct Upsert {
** sequences for the ORDER BY clause.
*/
struct Select {
- ExprList *pEList; /* The fields of the result */
u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
LogEst nSelectRow; /* Estimated number of result rows */
u32 selFlags; /* Various SF_* values */
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
u32 selId; /* Unique identifier number for this SELECT */
int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */
+ ExprList *pEList; /* The fields of the result */
SrcList *pSrc; /* The FROM clause */
Expr *pWhere; /* The WHERE clause */
ExprList *pGroupBy; /* The GROUP BY clause */
@@ -17706,29 +19429,43 @@ struct Select {
** "Select Flag".
**
** Value constraints (all checked via assert())
-** SF_HasAgg == NC_HasAgg
-** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX
-** SF_FixedLimit == WHERE_USE_LIMIT
-*/
-#define SF_Distinct 0x00001 /* Output should be DISTINCT */
-#define SF_All 0x00002 /* Includes the ALL keyword */
-#define SF_Resolved 0x00004 /* Identifiers have been resolved */
-#define SF_Aggregate 0x00008 /* Contains agg functions or a GROUP BY */
-#define SF_HasAgg 0x00010 /* Contains aggregate functions */
-#define SF_UsesEphemeral 0x00020 /* Uses the OpenEphemeral opcode */
-#define SF_Expanded 0x00040 /* sqlite3SelectExpand() called on this */
-#define SF_HasTypeInfo 0x00080 /* FROM subqueries have Table metadata */
-#define SF_Compound 0x00100 /* Part of a compound query */
-#define SF_Values 0x00200 /* Synthesized from VALUES clause */
-#define SF_MultiValue 0x00400 /* Single VALUES term with multiple rows */
-#define SF_NestedFrom 0x00800 /* Part of a parenthesized FROM clause */
-#define SF_MinMaxAgg 0x01000 /* Aggregate containing min() or max() */
-#define SF_Recursive 0x02000 /* The recursive part of a recursive CTE */
-#define SF_FixedLimit 0x04000 /* nSelectRow set by a constant LIMIT */
-#define SF_MaybeConvert 0x08000 /* Need convertCompoundSelectToSubquery() */
-#define SF_Converted 0x10000 /* By convertCompoundSelectToSubquery() */
-#define SF_IncludeHidden 0x20000 /* Include hidden columns in output */
-#define SF_ComplexResult 0x40000 /* Result contains subquery or function */
+** SF_HasAgg == NC_HasAgg
+** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX
+** SF_OrderByReqd == NC_OrderAgg == SQLITE_FUNC_ANYORDER
+** SF_FixedLimit == WHERE_USE_LIMIT
+*/
+#define SF_Distinct 0x0000001 /* Output should be DISTINCT */
+#define SF_All 0x0000002 /* Includes the ALL keyword */
+#define SF_Resolved 0x0000004 /* Identifiers have been resolved */
+#define SF_Aggregate 0x0000008 /* Contains agg functions or a GROUP BY */
+#define SF_HasAgg 0x0000010 /* Contains aggregate functions */
+#define SF_UsesEphemeral 0x0000020 /* Uses the OpenEphemeral opcode */
+#define SF_Expanded 0x0000040 /* sqlite3SelectExpand() called on this */
+#define SF_HasTypeInfo 0x0000080 /* FROM subqueries have Table metadata */
+#define SF_Compound 0x0000100 /* Part of a compound query */
+#define SF_Values 0x0000200 /* Synthesized from VALUES clause */
+#define SF_MultiValue 0x0000400 /* Single VALUES term with multiple rows */
+#define SF_NestedFrom 0x0000800 /* Part of a parenthesized FROM clause */
+#define SF_MinMaxAgg 0x0001000 /* Aggregate containing min() or max() */
+#define SF_Recursive 0x0002000 /* The recursive part of a recursive CTE */
+#define SF_FixedLimit 0x0004000 /* nSelectRow set by a constant LIMIT */
+#define SF_MaybeConvert 0x0008000 /* Need convertCompoundSelectToSubquery() */
+#define SF_Converted 0x0010000 /* By convertCompoundSelectToSubquery() */
+#define SF_IncludeHidden 0x0020000 /* Include hidden columns in output */
+#define SF_ComplexResult 0x0040000 /* Result contains subquery or function */
+#define SF_WhereBegin 0x0080000 /* Really a WhereBegin() call. Debug Only */
+#define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */
+#define SF_View 0x0200000 /* SELECT statement is a view */
+#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */
+#define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */
+#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */
+#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
+#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */
+#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */
+#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */
+
+/* True if S exists and has SF_NestedFrom */
+#define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0)
/*
** The results of a SELECT can be distributed in several ways, as defined
@@ -17747,9 +19484,6 @@ struct Select {
** statements within triggers whose only purpose is
** the side-effects of functions.
**
-** All of the above are free to ignore their ORDER BY clause. Those that
-** follow must honor the ORDER BY clause.
-**
** SRT_Output Generate a row of output (using the OP_ResultRow
** opcode) for each row in the result set.
**
@@ -17793,18 +19527,31 @@ struct Select {
** SRT_DistQueue Store results in priority queue pDest->iSDParm only if
** the same record has never been stored before. The
** index at pDest->iSDParm+1 hold all prior stores.
+**
+** SRT_Upfrom Store results in the temporary table already opened by
+** pDest->iSDParm. If (pDest->iSDParm<0), then the temp
+** table is an intkey table - in this case the first
+** column returned by the SELECT is used as the integer
+** key. If (pDest->iSDParm>0), then the table is an index
+** table. (pDest->iSDParm) is the number of key columns in
+** each index record in this case.
*/
#define SRT_Union 1 /* Store result as keys in an index */
#define SRT_Except 2 /* Remove result from a UNION index */
#define SRT_Exists 3 /* Store 1 if the result is not empty */
#define SRT_Discard 4 /* Do not save the results anywhere */
-#define SRT_Fifo 5 /* Store result as data with an automatic rowid */
-#define SRT_DistFifo 6 /* Like SRT_Fifo, but unique results only */
+#define SRT_DistFifo 5 /* Like SRT_Fifo, but unique results only */
+#define SRT_DistQueue 6 /* Like SRT_Queue, but unique results only */
+
+/* The DISTINCT clause is ignored for all of the above. Not that
+** IgnorableDistinct() implies IgnorableOrderby() */
+#define IgnorableDistinct(X) ((X->eDest)<=SRT_DistQueue)
+
#define SRT_Queue 7 /* Store result in an queue */
-#define SRT_DistQueue 8 /* Like SRT_Queue, but unique results only */
+#define SRT_Fifo 8 /* Store result as data with an automatic rowid */
/* The ORDER BY clause is ignored for all of the above */
-#define IgnorableOrderby(X) ((X->eDest)<=SRT_DistQueue)
+#define IgnorableOrderby(X) ((X->eDest)<=SRT_Fifo)
#define SRT_Output 9 /* Output each row of result */
#define SRT_Mem 10 /* Store result in a memory cell */
@@ -17812,17 +19559,19 @@ struct Select {
#define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */
#define SRT_Coroutine 13 /* Generate a single row of result */
#define SRT_Table 14 /* Store result as data with an automatic rowid */
+#define SRT_Upfrom 15 /* Store result as data with rowid */
/*
** An instance of this object describes where to put of the results of
** a SELECT statement.
*/
struct SelectDest {
- u8 eDest; /* How to dispose of the results. On of SRT_* above. */
+ u8 eDest; /* How to dispose of the results. One of SRT_* above. */
int iSDParm; /* A parameter used by the eDest disposal method */
+ int iSDParm2; /* A second parameter for the eDest disposal method */
int iSdst; /* Base register where results are written */
int nSdst; /* Number of registers allocated */
- char *zAffSdst; /* Affinity used when eDest==SRT_Set */
+ char *zAffSdst; /* Affinity used for SRT_Set */
ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
};
@@ -17881,11 +19630,45 @@ struct TriggerPrg {
#else
typedef unsigned int yDbMask;
# define DbMaskTest(M,I) (((M)&(((yDbMask)1)<<(I)))!=0)
-# define DbMaskZero(M) (M)=0
-# define DbMaskSet(M,I) (M)|=(((yDbMask)1)<<(I))
-# define DbMaskAllZero(M) (M)==0
-# define DbMaskNonZero(M) (M)!=0
+# define DbMaskZero(M) ((M)=0)
+# define DbMaskSet(M,I) ((M)|=(((yDbMask)1)<<(I)))
+# define DbMaskAllZero(M) ((M)==0)
+# define DbMaskNonZero(M) ((M)!=0)
+#endif
+
+/*
+** For each index X that has as one of its arguments either an expression
+** or the name of a virtual generated column, and if X is in scope such that
+** the value of the expression can simply be read from the index, then
+** there is an instance of this object on the Parse.pIdxExpr list.
+**
+** During code generation, while generating code to evaluate expressions,
+** this list is consulted and if a matching expression is found, the value
+** is read from the index rather than being recomputed.
+*/
+struct IndexedExpr {
+ Expr *pExpr; /* The expression contained in the index */
+ int iDataCur; /* The data cursor associated with the index */
+ int iIdxCur; /* The index cursor */
+ int iIdxCol; /* The index column that contains value of pExpr */
+ u8 bMaybeNullRow; /* True if we need an OP_IfNullRow check */
+ u8 aff; /* Affinity of the pExpr expression */
+ IndexedExpr *pIENext; /* Next in a list of all indexed expressions */
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ const char *zIdxName; /* Name of index, used only for bytecode comments */
#endif
+};
+
+/*
+** An instance of the ParseCleanup object specifies an operation that
+** should be performed after parsing to deallocation resources obtained
+** during the parse and which are no longer needed.
+*/
+struct ParseCleanup {
+ ParseCleanup *pNext; /* Next cleanup task */
+ void *pPtr; /* Pointer to object to deallocate */
+ void (*xCleanup)(sqlite3*,void*); /* Deallocation routine */
+};
/*
** An SQL parser context. A copy of this structure is passed through
@@ -17917,18 +19700,28 @@ struct Parse {
u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */
u8 okConstFactor; /* OK to factor out constants */
u8 disableLookaside; /* Number of times lookaside has been disabled */
+ u8 prepFlags; /* SQLITE_PREPARE_* flags */
+ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */
+#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
+ u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */
+#endif
+#ifdef SQLITE_DEBUG
+ u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */
+#endif
int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */
int nErr; /* Number of errors seen */
int nTab; /* Number of previously allocated VDBE cursors */
int nMem; /* Number of memory cells used so far */
- int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */
int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */
int iSelfTab; /* Table associated with an index on expr, or negative
** of the base register during check-constraint eval */
- int nLabel; /* Number of labels used */
+ int nLabel; /* The *negative* of the number of labels used */
+ int nLabelAlloc; /* Number of slots in aLabel */
int *aLabel; /* Space to hold the labels */
ExprList *pConstExpr;/* Constant expressions */
+ IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */
+ IndexedExpr *pIdxPartExpr; /* Exprs constrained by index WHERE clauses */
Token constraintName;/* Name of the constraint currently being parsed */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
@@ -17936,6 +19729,9 @@ struct Parse {
int regRoot; /* Register holding root page number for new objects */
int nMaxArg; /* Max args passed to user function by sub-program */
int nSelect; /* Number of SELECT stmts. Counter for Select.selId */
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ u32 nProgressSteps; /* xProgress steps taken during sqlite3_prepare() */
+#endif
#ifndef SQLITE_OMIT_SHARED_CACHE
int nTableLock; /* Number of locks in aTableLock */
TableLock *aTableLock; /* Required table locks for shared-cache mode */
@@ -17943,11 +19739,17 @@ struct Parse {
AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */
Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */
- int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */
- u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
+ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
+ ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */
+ union {
+ int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */
+ Returning *pReturning; /* The RETURNING clause */
+ } u1;
u32 oldmask; /* Mask of old.* columns referenced */
u32 newmask; /* Mask of new.* columns referenced */
+ LogEst nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
+ u8 bReturning; /* Coding a RETURNING trigger */
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
u8 disableTriggers; /* True to disable triggers */
@@ -17959,6 +19761,7 @@ struct Parse {
**************************************************************************/
int aTempReg[8]; /* Holding area for temporary registers */
+ Parse *pOuterParse; /* Outer Parse object when nested */
Token sNameToken; /* Token with unqualified schema object name */
/************************************************************************
@@ -17972,9 +19775,7 @@ struct Parse {
ynVar nVar; /* Number of '?' variables seen in the SQL so far */
u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */
u8 explain; /* True if the EXPLAIN flag is found on the query */
-#if !(defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE))
u8 eParseMode; /* PARSE_MODE_XXX constant */
-#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
int nVtabLock; /* Number of virtual tables to lock */
#endif
@@ -17986,31 +19787,33 @@ struct Parse {
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
const char *zTail; /* All SQL text past the last semicolon parsed */
Table *pNewTable; /* A table being constructed by CREATE TABLE */
- Index *pNewIndex; /* An index being constructed by CREATE INDEX */
+ Index *pNewIndex; /* An index being constructed by CREATE INDEX.
+ ** Also used to hold redundant UNIQUE constraints
+ ** during a RENAME COLUMN */
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
#ifndef SQLITE_OMIT_VIRTUALTABLE
Token sArg; /* Complete text of a module argument */
Table **apVtabLock; /* Pointer to virtual tables needing locking */
#endif
- Table *pZombieTab; /* List of Table objects to delete after code gen */
- TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
With *pWith; /* Current WITH clause, or NULL */
- With *pWithToFree; /* Free this WITH object at the end of the parse */
#ifndef SQLITE_OMIT_ALTERTABLE
RenameToken *pRename; /* Tokens subject to renaming by ALTER TABLE */
#endif
};
+/* Allowed values for Parse.eParseMode
+*/
#define PARSE_MODE_NORMAL 0
#define PARSE_MODE_DECLARE_VTAB 1
-#define PARSE_MODE_RENAME_COLUMN 2
-#define PARSE_MODE_RENAME_TABLE 3
+#define PARSE_MODE_RENAME 2
+#define PARSE_MODE_UNMAP 3
/*
** Sizes and pointers of various parts of the Parse object.
*/
-#define PARSE_HDR_SZ offsetof(Parse,aTempReg) /* Recursive part w/o aColCache*/
+#define PARSE_HDR(X) (((char*)(X))+offsetof(Parse,zErrMsg))
+#define PARSE_HDR_SZ (offsetof(Parse,aTempReg)-offsetof(Parse,zErrMsg)) /* Recursive part w/o aColCache*/
#define PARSE_RECURSE_SZ offsetof(Parse,sLastToken) /* Recursive part */
#define PARSE_TAIL_SZ (sizeof(Parse)-PARSE_RECURSE_SZ) /* Non-recursive part */
#define PARSE_TAIL(X) (((char*)(X))+PARSE_RECURSE_SZ) /* Pointer to tail */
@@ -18027,7 +19830,7 @@ struct Parse {
#if defined(SQLITE_OMIT_ALTERTABLE)
#define IN_RENAME_OBJECT 0
#else
- #define IN_RENAME_OBJECT (pParse->eParseMode>=PARSE_MODE_RENAME_COLUMN)
+ #define IN_RENAME_OBJECT (pParse->eParseMode>=PARSE_MODE_RENAME)
#endif
#if defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE)
@@ -18068,6 +19871,7 @@ struct AuthContext {
#define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */
#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */
#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */
+#define OPFLAG_BYTELENARG 0xc0 /* OP_Column only for octet_length() */
#define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */
#define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */
#define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */
@@ -18076,27 +19880,29 @@ struct AuthContext {
#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete/Insert: save cursor pos */
#define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */
#define OPFLAG_NOCHNG_MAGIC 0x6d /* OP_MakeRecord: serialtype 10 is ok */
+#define OPFLAG_PREFORMAT 0x80 /* OP_Insert uses preformatted cell */
/*
- * Each trigger present in the database schema is stored as an instance of
- * struct Trigger.
- *
- * Pointers to instances of struct Trigger are stored in two ways.
- * 1. In the "trigHash" hash table (part of the sqlite3* that represents the
- * database). This allows Trigger structures to be retrieved by name.
- * 2. All triggers associated with a single table form a linked list, using the
- * pNext member of struct Trigger. A pointer to the first element of the
- * linked list is stored as the "pTrigger" member of the associated
- * struct Table.
- *
- * The "step_list" member points to the first element of a linked list
- * containing the SQL statements specified as the trigger program.
- */
+** Each trigger present in the database schema is stored as an instance of
+** struct Trigger.
+**
+** Pointers to instances of struct Trigger are stored in two ways.
+** 1. In the "trigHash" hash table (part of the sqlite3* that represents the
+** database). This allows Trigger structures to be retrieved by name.
+** 2. All triggers associated with a single table form a linked list, using the
+** pNext member of struct Trigger. A pointer to the first element of the
+** linked list is stored as the "pTrigger" member of the associated
+** struct Table.
+**
+** The "step_list" member points to the first element of a linked list
+** containing the SQL statements specified as the trigger program.
+*/
struct Trigger {
char *zName; /* The name of the trigger */
char *table; /* The table or view to which the trigger applies */
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
+ u8 bReturning; /* This trigger implements a RETURNING clause */
Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */
IdList *pColumns; /* If this is an UPDATE OF <column-list> trigger,
the <column-list> is stored here */
@@ -18117,51 +19923,58 @@ struct Trigger {
#define TRIGGER_AFTER 2
/*
- * An instance of struct TriggerStep is used to store a single SQL statement
- * that is a part of a trigger-program.
- *
- * Instances of struct TriggerStep are stored in a singly linked list (linked
- * using the "pNext" member) referenced by the "step_list" member of the
- * associated struct Trigger instance. The first element of the linked list is
- * the first step of the trigger-program.
- *
- * The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or
- * "SELECT" statement. The meanings of the other members is determined by the
- * value of "op" as follows:
- *
- * (op == TK_INSERT)
- * orconf -> stores the ON CONFLICT algorithm
- * pSelect -> If this is an INSERT INTO ... SELECT ... statement, then
- * this stores a pointer to the SELECT statement. Otherwise NULL.
- * zTarget -> Dequoted name of the table to insert into.
- * pExprList -> If this is an INSERT INTO ... VALUES ... statement, then
- * this stores values to be inserted. Otherwise NULL.
- * pIdList -> If this is an INSERT INTO ... (<column-names>) VALUES ...
- * statement, then this stores the column-names to be
- * inserted into.
- *
- * (op == TK_DELETE)
- * zTarget -> Dequoted name of the table to delete from.
- * pWhere -> The WHERE clause of the DELETE statement if one is specified.
- * Otherwise NULL.
- *
- * (op == TK_UPDATE)
- * zTarget -> Dequoted name of the table to update.
- * pWhere -> The WHERE clause of the UPDATE statement if one is specified.
- * Otherwise NULL.
- * pExprList -> A list of the columns to update and the expressions to update
- * them to. See sqlite3Update() documentation of "pChanges"
- * argument.
- *
- */
+** An instance of struct TriggerStep is used to store a single SQL statement
+** that is a part of a trigger-program.
+**
+** Instances of struct TriggerStep are stored in a singly linked list (linked
+** using the "pNext" member) referenced by the "step_list" member of the
+** associated struct Trigger instance. The first element of the linked list is
+** the first step of the trigger-program.
+**
+** The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or
+** "SELECT" statement. The meanings of the other members is determined by the
+** value of "op" as follows:
+**
+** (op == TK_INSERT)
+** orconf -> stores the ON CONFLICT algorithm
+** pSelect -> The content to be inserted - either a SELECT statement or
+** a VALUES clause.
+** zTarget -> Dequoted name of the table to insert into.
+** pIdList -> If this is an INSERT INTO ... (<column-names>) VALUES ...
+** statement, then this stores the column-names to be
+** inserted into.
+** pUpsert -> The ON CONFLICT clauses for an Upsert
+**
+** (op == TK_DELETE)
+** zTarget -> Dequoted name of the table to delete from.
+** pWhere -> The WHERE clause of the DELETE statement if one is specified.
+** Otherwise NULL.
+**
+** (op == TK_UPDATE)
+** zTarget -> Dequoted name of the table to update.
+** pWhere -> The WHERE clause of the UPDATE statement if one is specified.
+** Otherwise NULL.
+** pExprList -> A list of the columns to update and the expressions to update
+** them to. See sqlite3Update() documentation of "pChanges"
+** argument.
+**
+** (op == TK_SELECT)
+** pSelect -> The SELECT statement
+**
+** (op == TK_RETURNING)
+** pExprList -> The list of expressions that follow the RETURNING keyword.
+**
+*/
struct TriggerStep {
- u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
+ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT,
+ ** or TK_RETURNING */
u8 orconf; /* OE_Rollback etc. */
Trigger *pTrig; /* The trigger that this step is a part of */
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
char *zTarget; /* Target table for DELETE, UPDATE, INSERT */
+ SrcList *pFrom; /* FROM clause for UPDATE statement (if any) */
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
- ExprList *pExprList; /* SET clause for UPDATE */
+ ExprList *pExprList; /* SET clause for UPDATE, or RETURNING clause */
IdList *pIdList; /* Column names for INSERT */
Upsert *pUpsert; /* Upsert clauses on an INSERT */
char *zSpan; /* Original SQL text of this command */
@@ -18170,18 +19983,17 @@ struct TriggerStep {
};
/*
-** The following structure contains information used by the sqliteFix...
-** routines as they walk the parse tree to make database references
-** explicit.
+** Information about a RETURNING clause
*/
-typedef struct DbFixer DbFixer;
-struct DbFixer {
- Parse *pParse; /* The parsing context. Error messages written here */
- Schema *pSchema; /* Fix items to this schema */
- int bVarOnly; /* Check for variable references only */
- const char *zDb; /* Make sure all objects are contained in this database */
- const char *zType; /* Type of the container - used for error messages */
- const Token *pName; /* Name of the container - used for error messages */
+struct Returning {
+ Parse *pParse; /* The parse that includes the RETURNING clause */
+ ExprList *pReturnEL; /* List of expressions to return */
+ Trigger retTrig; /* The transient trigger that implements RETURNING */
+ TriggerStep retTStep; /* The trigger step */
+ int iRetCur; /* Transient table holding RETURNING results */
+ int nRetCol; /* Number of in pReturnEL after expansion */
+ int iRetReg; /* Register array for holding a row of RETURNING */
+ char zName[40]; /* Name of trigger: "sqlite_returning_%p" */
};
/*
@@ -18203,6 +20015,28 @@ struct sqlite3_str {
#define isMalloced(X) (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0)
+/*
+** The following object is the header for an "RCStr" or "reference-counted
+** string". An RCStr is passed around and used like any other char*
+** that has been dynamically allocated. The important interface
+** differences:
+**
+** 1. RCStr strings are reference counted. They are deallocated
+** when the reference count reaches zero.
+**
+** 2. Use sqlite3RCStrUnref() to free an RCStr string rather than
+** sqlite3_free()
+**
+** 3. Make a (read-only) copy of a read-only RCStr string using
+** sqlite3RCStrRef().
+**
+** "String" is in the name, but an RCStr object can also be used to hold
+** binary data.
+*/
+struct RCStr {
+ u64 nRCRef; /* Number of references */
+ /* Total structure size should be a multiple of 8 bytes for alignment */
+};
/*
** A pointer to this structure is used to communicate information
@@ -18214,12 +20048,33 @@ typedef struct {
int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */
int rc; /* Result code stored here */
u32 mInitFlags; /* Flags controlling error messages */
+ u32 nInitRow; /* Number of rows processed */
+ Pgno mxPage; /* Maximum page number. 0 for no limit. */
} InitData;
/*
** Allowed values for mInitFlags
*/
-#define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */
+#define INITFLAG_AlterMask 0x0003 /* Types of ALTER */
+#define INITFLAG_AlterRename 0x0001 /* Reparse after a RENAME */
+#define INITFLAG_AlterDrop 0x0002 /* Reparse after a DROP COLUMN */
+#define INITFLAG_AlterAdd 0x0003 /* Reparse after an ADD COLUMN */
+
+/* Tuning parameters are set using SQLITE_TESTCTRL_TUNE and are controlled
+** on debug-builds of the CLI using ".testctrl tune ID VALUE". Tuning
+** parameters are for temporary use during development, to help find
+** optimal values for parameters in the query planner. The should not
+** be used on trunk check-ins. They are a temporary mechanism available
+** for transient development builds only.
+**
+** Tuning parameters are numbered starting with 1.
+*/
+#define SQLITE_NTUNE 6 /* Should be zero for all trunk check-ins */
+#ifdef SQLITE_DEBUG
+# define Tuning(X) (sqlite3Config.aTune[(X)-1])
+#else
+# define Tuning(X) 0
+#endif
/*
** Structure containing global configuration data for the SQLite library.
@@ -18228,11 +20083,16 @@ typedef struct {
*/
struct Sqlite3Config {
int bMemstat; /* True to enable memory status */
- int bCoreMutex; /* True to enable core mutexing */
- int bFullMutex; /* True to enable full mutexing */
- int bOpenUri; /* True to interpret filenames as URIs */
- int bUseCis; /* Use covering indices for full-scans */
- int bSmallMalloc; /* Avoid large memory allocations if true */
+ u8 bCoreMutex; /* True to enable core mutexing */
+ u8 bFullMutex; /* True to enable full mutexing */
+ u8 bOpenUri; /* True to interpret filenames as URIs */
+ u8 bUseCis; /* Use covering indices for full-scans */
+ u8 bSmallMalloc; /* Avoid large memory allocations if true */
+ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */
+ u8 bUseLongDouble; /* Make use of long double */
+#ifdef SQLITE_DEBUG
+ u8 bJsonSelfcheck; /* Double-check JSON parsing */
+#endif
int mxStrlen; /* Maximum string length */
int neverCorrupt; /* Database is always well-formed */
int szLookaside; /* Default lookaside buffer size */
@@ -18274,13 +20134,21 @@ struct Sqlite3Config {
void (*xVdbeBranch)(void*,unsigned iSrcLine,u8 eThis,u8 eMx); /* Callback */
void *pVdbeBranchArg; /* 1st argument */
#endif
+#ifndef SQLITE_OMIT_DESERIALIZE
+ sqlite3_int64 mxMemdbSize; /* Default max memdb size */
+#endif
#ifndef SQLITE_UNTESTABLE
int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
#endif
int bLocaltimeFault; /* True to fail localtime() calls */
- int bInternalFunctions; /* Internal SQL functions are visible */
+ int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */
int iOnceResetThreshold; /* When to reset OP_Once counters */
u32 szSorterRef; /* Min size in bytes to use sorter-refs */
+ unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */
+ /* vvvv--- must be last ---vvv */
+#ifdef SQLITE_DEBUG
+ sqlite3_int64 aTune[SQLITE_NTUNE]; /* Tuning parameters */
+#endif
};
/*
@@ -18310,27 +20178,48 @@ struct Walker {
int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */
void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */
int walkerDepth; /* Number of subqueries */
- u8 eCode; /* A small processing code */
+ u16 eCode; /* A small processing code */
+ u16 mWFlags; /* Use-dependent flags */
union { /* Extra data for callback */
NameContext *pNC; /* Naming context */
int n; /* A counter */
int iCur; /* A cursor number */
SrcList *pSrcList; /* FROM clause */
- struct SrcCount *pSrcCount; /* Counting column references */
struct CCurHint *pCCurHint; /* Used by codeCursorHint() */
+ struct RefSrcList *pRefSrcList; /* sqlite3ReferencesSrcList() */
int *aiCol; /* array of column indexes */
struct IdxCover *pIdxCover; /* Check for index coverage */
- struct IdxExprTrans *pIdxTrans; /* Convert idxed expr to column */
ExprList *pGroupBy; /* GROUP BY clause */
Select *pSelect; /* HAVING to WHERE clause ctx */
struct WindowRewrite *pRewrite; /* Window rewrite context */
struct WhereConst *pConst; /* WHERE clause constants */
struct RenameCtx *pRename; /* RENAME COLUMN context */
+ struct Table *pTab; /* Table of generated column */
+ struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */
+ SrcItem *pSrcItem; /* A single FROM clause item */
+ DbFixer *pFix; /* See sqlite3FixSelect() */
+ Mem *aMem; /* See sqlite3BtreeCursorHint() */
} u;
};
+/*
+** The following structure contains information used by the sqliteFix...
+** routines as they walk the parse tree to make database references
+** explicit.
+*/
+struct DbFixer {
+ Parse *pParse; /* The parsing context. Error messages written here */
+ Walker w; /* Walker object */
+ Schema *pSchema; /* Fix items to this schema */
+ u8 bTemp; /* True for TEMP schema entries */
+ const char *zDb; /* Make sure all objects are contained in this database */
+ const char *zType; /* Type of the container - used for error messages */
+ const Token *pName; /* Name of the container - used for error messages */
+};
+
/* Forward declarations */
SQLITE_PRIVATE int sqlite3WalkExpr(Walker*, Expr*);
+SQLITE_PRIVATE int sqlite3WalkExprNN(Walker*, Expr*);
SQLITE_PRIVATE int sqlite3WalkExprList(Walker*, ExprList*);
SQLITE_PRIVATE int sqlite3WalkSelect(Walker*, Select*);
SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker*, Select*);
@@ -18338,10 +20227,20 @@ SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker*, Select*);
SQLITE_PRIVATE int sqlite3ExprWalkNoop(Walker*, Expr*);
SQLITE_PRIVATE int sqlite3SelectWalkNoop(Walker*, Select*);
SQLITE_PRIVATE int sqlite3SelectWalkFail(Walker*, Select*);
+SQLITE_PRIVATE int sqlite3WalkerDepthIncrease(Walker*,Select*);
+SQLITE_PRIVATE void sqlite3WalkerDepthDecrease(Walker*,Select*);
+SQLITE_PRIVATE void sqlite3WalkWinDefnDummyCallback(Walker*,Select*);
+
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE void sqlite3SelectWalkAssert2(Walker*, Select*);
#endif
+#ifndef SQLITE_OMIT_CTE
+SQLITE_PRIVATE void sqlite3SelectPopWith(Walker*, Select*);
+#else
+# define sqlite3SelectPopWith 0
+#endif
+
/*
** Return code from the parse-tree walking primitives and their
** callbacks.
@@ -18351,18 +20250,64 @@ SQLITE_PRIVATE void sqlite3SelectWalkAssert2(Walker*, Select*);
#define WRC_Abort 2 /* Abandon the tree walk */
/*
-** An instance of this structure represents a set of one or more CTEs
-** (common table expressions) created by a single WITH clause.
+** A single common table expression
+*/
+struct Cte {
+ char *zName; /* Name of this CTE */
+ ExprList *pCols; /* List of explicit column names, or NULL */
+ Select *pSelect; /* The definition of this CTE */
+ const char *zCteErr; /* Error message for circular references */
+ CteUse *pUse; /* Usage information for this CTE */
+ u8 eM10d; /* The MATERIALIZED flag */
+};
+
+/*
+** Allowed values for the materialized flag (eM10d):
+*/
+#define M10d_Yes 0 /* AS MATERIALIZED */
+#define M10d_Any 1 /* Not specified. Query planner's choice */
+#define M10d_No 2 /* AS NOT MATERIALIZED */
+
+/*
+** An instance of the With object represents a WITH clause containing
+** one or more CTEs (common table expressions).
*/
struct With {
- int nCte; /* Number of CTEs in the WITH clause */
- With *pOuter; /* Containing WITH clause, or NULL */
- struct Cte { /* For each CTE in the WITH clause.... */
- char *zName; /* Name of this CTE */
- ExprList *pCols; /* List of explicit column names, or NULL */
- Select *pSelect; /* The definition of this CTE */
- const char *zCteErr; /* Error message for circular references */
- } a[1];
+ int nCte; /* Number of CTEs in the WITH clause */
+ int bView; /* Belongs to the outermost Select of a view */
+ With *pOuter; /* Containing WITH clause, or NULL */
+ Cte a[1]; /* For each CTE in the WITH clause.... */
+};
+
+/*
+** The Cte object is not guaranteed to persist for the entire duration
+** of code generation. (The query flattener or other parser tree
+** edits might delete it.) The following object records information
+** about each Common Table Expression that must be preserved for the
+** duration of the parse.
+**
+** The CteUse objects are freed using sqlite3ParserAddCleanup() rather
+** than sqlite3SelectDelete(), which is what enables them to persist
+** until the end of code generation.
+*/
+struct CteUse {
+ int nUse; /* Number of users of this CTE */
+ int addrM9e; /* Start of subroutine to compute materialization */
+ int regRtn; /* Return address register for addrM9e subroutine */
+ int iCur; /* Ephemeral table holding the materialization */
+ LogEst nRowEst; /* Estimated number of rows in the table */
+ u8 eM10d; /* The MATERIALIZED flag */
+};
+
+
+/* Client data associated with sqlite3_set_clientdata() and
+** sqlite3_get_clientdata().
+*/
+struct DbClientData {
+ DbClientData *pNext; /* Next in a linked list */
+ void *pData; /* The data */
+ void (*xDestructor)(void*); /* Destructor. Might be NULL */
+ char zName[1]; /* Name of this client data. MUST BE LAST */
};
#ifdef SQLITE_DEBUG
@@ -18377,10 +20322,11 @@ struct TreeView {
#endif /* SQLITE_DEBUG */
/*
-** This object is used in varioius ways, all related to window functions
+** This object is used in various ways, most (but not all) related to window
+** functions.
**
** (1) A single instance of this structure is attached to the
-** the Expr.pWin field for each window function in an expression tree.
+** the Expr.y.pWin field for each window function in an expression tree.
** This object holds the information contained in the OVER clause,
** plus additional fields used during code generation.
**
@@ -18391,47 +20337,62 @@ struct TreeView {
** (3) The terms of the WINDOW clause of a SELECT are instances of this
** object on a linked list attached to Select.pWinDefn.
**
+** (4) For an aggregate function with a FILTER clause, an instance
+** of this object is stored in Expr.y.pWin with eFrmType set to
+** TK_FILTER. In this case the only field used is Window.pFilter.
+**
** The uses (1) and (2) are really the same Window object that just happens
-** to be accessible in two different ways. Use (3) is are separate objects.
+** to be accessible in two different ways. Use case (3) are separate objects.
*/
struct Window {
char *zName; /* Name of window (may be NULL) */
+ char *zBase; /* Name of base window for chaining (may be NULL) */
ExprList *pPartition; /* PARTITION BY clause */
ExprList *pOrderBy; /* ORDER BY clause */
- u8 eType; /* TK_RANGE or TK_ROWS */
+ u8 eFrmType; /* TK_RANGE, TK_GROUPS, TK_ROWS, or 0 */
u8 eStart; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */
u8 eEnd; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */
+ u8 bImplicitFrame; /* True if frame was implicitly specified */
+ u8 eExclude; /* TK_NO, TK_CURRENT, TK_TIES, TK_GROUP, or 0 */
Expr *pStart; /* Expression for "<expr> PRECEDING" */
Expr *pEnd; /* Expression for "<expr> FOLLOWING" */
+ Window **ppThis; /* Pointer to this object in Select.pWin list */
Window *pNextWin; /* Next window function belonging to this SELECT */
Expr *pFilter; /* The FILTER expression */
- FuncDef *pFunc; /* The function */
+ FuncDef *pWFunc; /* The function */
int iEphCsr; /* Partition buffer or Peer buffer */
- int regAccum;
- int regResult;
+ int regAccum; /* Accumulator */
+ int regResult; /* Interim result */
int csrApp; /* Function cursor (used by min/max) */
int regApp; /* Function register (also used by min/max) */
- int regPart; /* First in a set of registers holding PARTITION BY
- ** and ORDER BY values for the window */
+ int regPart; /* Array of registers for PARTITION BY values */
Expr *pOwner; /* Expression object this window is attached to */
int nBufferCol; /* Number of columns in buffer table */
int iArgCol; /* Offset of first argument for this function */
+ int regOne; /* Register containing constant value 1 */
+ int regStartRowid;
+ int regEndRowid;
+ u8 bExprArgs; /* Defer evaluation of window function arguments
+ ** due to the SQLITE_SUBTYPE flag */
};
#ifndef SQLITE_OMIT_WINDOWFUNC
SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*);
+SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window*);
SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p);
-SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*);
+SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8);
SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*);
-SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*);
-SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Window*);
+SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin);
+SQLITE_PRIVATE int sqlite3WindowCompare(const Parse*, const Window*, const Window*, int);
+SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Select*);
SQLITE_PRIVATE void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
SQLITE_PRIVATE int sqlite3WindowRewrite(Parse*, Select*);
-SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse*, struct SrcList_item*);
SQLITE_PRIVATE void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*);
SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p);
SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p);
SQLITE_PRIVATE void sqlite3WindowFunctions(void);
+SQLITE_PRIVATE void sqlite3WindowChain(Parse*, Window*, Window*);
+SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprList*, Token*);
#else
# define sqlite3WindowDelete(a,b)
# define sqlite3WindowFunctions()
@@ -18465,13 +20426,16 @@ SQLITE_PRIVATE int sqlite3CantopenError(int);
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3NomemError(int);
SQLITE_PRIVATE int sqlite3IoerrnomemError(int);
-SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno);
# define SQLITE_NOMEM_BKPT sqlite3NomemError(__LINE__)
# define SQLITE_IOERR_NOMEM_BKPT sqlite3IoerrnomemError(__LINE__)
-# define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptPgnoError(__LINE__,(P))
#else
# define SQLITE_NOMEM_BKPT SQLITE_NOMEM
# define SQLITE_IOERR_NOMEM_BKPT SQLITE_IOERR_NOMEM
+#endif
+#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_CORRUPT_PGNO)
+SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno);
+# define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptPgnoError(__LINE__,(P))
+#else
# define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptError(__LINE__)
#endif
@@ -18515,6 +20479,8 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno);
# define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08)
# define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)])
# define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80)
+# define sqlite3JsonId1(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x42)
+# define sqlite3JsonId2(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x46)
#else
# define sqlite3Toupper(x) toupper((unsigned char)(x))
# define sqlite3Isspace(x) isspace((unsigned char)(x))
@@ -18524,6 +20490,8 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno);
# define sqlite3Isxdigit(x) isxdigit((unsigned char)(x))
# define sqlite3Tolower(x) tolower((unsigned char)(x))
# define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`')
+# define sqlite3JsonId1(x) (sqlite3IsIdChar(x)&&(x)<'0')
+# define sqlite3JsonId2(x) sqlite3IsIdChar(x)
#endif
SQLITE_PRIVATE int sqlite3IsIdChar(u8);
@@ -18551,8 +20519,9 @@ SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64);
SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, u64);
SQLITE_PRIVATE void sqlite3DbFree(sqlite3*, void*);
SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3*, void*);
-SQLITE_PRIVATE int sqlite3MallocSize(void*);
-SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, void*);
+SQLITE_PRIVATE void sqlite3DbNNFreeNN(sqlite3*, void*);
+SQLITE_PRIVATE int sqlite3MallocSize(const void*);
+SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, const void*);
SQLITE_PRIVATE void *sqlite3PageMalloc(int);
SQLITE_PRIVATE void sqlite3PageFree(void*);
SQLITE_PRIVATE void sqlite3MemSetDefault(void);
@@ -18571,12 +20540,14 @@ SQLITE_PRIVATE int sqlite3HeapNearlyFull(void);
*/
#ifdef SQLITE_USE_ALLOCA
# define sqlite3StackAllocRaw(D,N) alloca(N)
-# define sqlite3StackAllocZero(D,N) memset(alloca(N), 0, N)
+# define sqlite3StackAllocRawNN(D,N) alloca(N)
# define sqlite3StackFree(D,P)
+# define sqlite3StackFreeNN(D,P)
#else
# define sqlite3StackAllocRaw(D,N) sqlite3DbMallocRaw(D,N)
-# define sqlite3StackAllocZero(D,N) sqlite3DbMallocZero(D,N)
+# define sqlite3StackAllocRawNN(D,N) sqlite3DbMallocRawNN(D,N)
# define sqlite3StackFree(D,P) sqlite3DbFree(D,P)
+# define sqlite3StackFreeNN(D,P) sqlite3DbFreeNN(D,P)
#endif
/* Do not allow both MEMSYS5 and MEMSYS3 to be defined together. If they
@@ -18621,8 +20592,12 @@ SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex*);
#endif
#ifndef SQLITE_OMIT_FLOATING_POINT
+# define EXP754 (((u64)0x7ff)<<52)
+# define MAN754 ((((u64)1)<<52)-1)
+# define IsNaN(X) (((X)&EXP754)==EXP754 && ((X)&MAN754)!=0)
SQLITE_PRIVATE int sqlite3IsNaN(double);
#else
+# define IsNaN(X) 0
# define sqlite3IsNaN(X) 0
#endif
@@ -18636,6 +20611,20 @@ struct PrintfArguments {
sqlite3_value **apArg; /* The argument values */
};
+/*
+** An instance of this object receives the decoding of a floating point
+** value into an approximate decimal representation.
+*/
+struct FpDecode {
+ char sign; /* '+' or '-' */
+ char isSpecial; /* 1: Infinity 2: NaN */
+ int n; /* Significant digits in the decode */
+ int iDP; /* Location of the decimal point */
+ char *z; /* Start of significant digits */
+ char zBuf[24]; /* Storage for significant digits */
+};
+
+SQLITE_PRIVATE void sqlite3FpDecode(FpDecode*,double,int,int);
SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...);
SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
@@ -18646,31 +20635,74 @@ SQLITE_PRIVATE void *sqlite3TestTextToPtr(const char*);
#endif
#if defined(SQLITE_DEBUG)
+SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView*, const char *zFormat, ...);
SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
SQLITE_PRIVATE void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*);
SQLITE_PRIVATE void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
+SQLITE_PRIVATE void sqlite3TreeViewBareIdList(TreeView*, const IdList*, const char*);
+SQLITE_PRIVATE void sqlite3TreeViewIdList(TreeView*, const IdList*, u8, const char*);
+SQLITE_PRIVATE void sqlite3TreeViewColumnList(TreeView*, const Column*, int, u8);
SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView*, const SrcList*);
SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView*, const Select*, u8);
SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView*, const With*, u8);
+SQLITE_PRIVATE void sqlite3TreeViewUpsert(TreeView*, const Upsert*, u8);
+#if TREETRACE_ENABLED
+SQLITE_PRIVATE void sqlite3TreeViewDelete(const With*, const SrcList*, const Expr*,
+ const ExprList*,const Expr*, const Trigger*);
+SQLITE_PRIVATE void sqlite3TreeViewInsert(const With*, const SrcList*,
+ const IdList*, const Select*, const ExprList*,
+ int, const Upsert*, const Trigger*);
+SQLITE_PRIVATE void sqlite3TreeViewUpdate(const With*, const SrcList*, const ExprList*,
+ const Expr*, int, const ExprList*, const Expr*,
+ const Upsert*, const Trigger*);
+#endif
+#ifndef SQLITE_OMIT_TRIGGER
+SQLITE_PRIVATE void sqlite3TreeViewTriggerStep(TreeView*, const TriggerStep*, u8, u8);
+SQLITE_PRIVATE void sqlite3TreeViewTrigger(TreeView*, const Trigger*, u8, u8);
+#endif
#ifndef SQLITE_OMIT_WINDOWFUNC
SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView*, const Window*, u8);
SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView*, const Window*, u8);
#endif
+SQLITE_PRIVATE void sqlite3ShowExpr(const Expr*);
+SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList*);
+SQLITE_PRIVATE void sqlite3ShowIdList(const IdList*);
+SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList*);
+SQLITE_PRIVATE void sqlite3ShowSelect(const Select*);
+SQLITE_PRIVATE void sqlite3ShowWith(const With*);
+SQLITE_PRIVATE void sqlite3ShowUpsert(const Upsert*);
+#ifndef SQLITE_OMIT_TRIGGER
+SQLITE_PRIVATE void sqlite3ShowTriggerStep(const TriggerStep*);
+SQLITE_PRIVATE void sqlite3ShowTriggerStepList(const TriggerStep*);
+SQLITE_PRIVATE void sqlite3ShowTrigger(const Trigger*);
+SQLITE_PRIVATE void sqlite3ShowTriggerList(const Trigger*);
+#endif
+#ifndef SQLITE_OMIT_WINDOWFUNC
+SQLITE_PRIVATE void sqlite3ShowWindow(const Window*);
+SQLITE_PRIVATE void sqlite3ShowWinFunc(const Window*);
+#endif
#endif
-
SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*);
+SQLITE_PRIVATE void sqlite3ProgressCheck(Parse*);
SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...);
+SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int);
SQLITE_PRIVATE void sqlite3Dequote(char*);
+SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*);
+SQLITE_PRIVATE void sqlite3DequoteToken(Token*);
SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*);
SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int);
-SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*, char **);
+SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*);
SQLITE_PRIVATE void sqlite3FinishCoding(Parse*);
SQLITE_PRIVATE int sqlite3GetTempReg(Parse*);
SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int);
SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int);
SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int);
SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*);
+SQLITE_PRIVATE void sqlite3TouchRegister(Parse*,int);
+#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG)
+SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse*,int);
+#endif
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse*,int,int);
#endif
@@ -18679,17 +20711,27 @@ SQLITE_PRIVATE Expr *sqlite3Expr(sqlite3*,int,const char*);
SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*);
SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*);
SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*);
-SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3*,Expr*, Expr*);
-SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*, int);
+SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse*,Expr*, Expr*);
+SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*);
+SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int);
+SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*);
+SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse*,Expr*);
+SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*);
SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32);
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);
+SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3*,void*);
+SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse*, Expr*);
+SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
-SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int);
-SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
+SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse*, int, ExprList*);
+SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int);
+SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int);
SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*);
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);
+SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*);
+SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*);
SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**);
SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**);
SQLITE_PRIVATE int sqlite3InitOne(sqlite3*, int, char**, u32);
@@ -18701,28 +20743,43 @@ SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3*);
SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3*,int);
SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*);
SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*);
+SQLITE_PRIVATE void sqlite3ColumnSetExpr(Parse*,Table*,Column*,Expr*);
+SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table*,Column*);
+SQLITE_PRIVATE void sqlite3ColumnSetColl(sqlite3*,Column*,const char*zColl);
+SQLITE_PRIVATE const char *sqlite3ColumnColl(Column*);
SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*);
+SQLITE_PRIVATE void sqlite3GenerateColumnNames(Parse *pParse, Select *pSelect);
SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**);
-SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*);
-SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*);
-SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int);
+SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char);
+SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char);
+SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int);
SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*);
-SQLITE_PRIVATE i16 sqlite3ColumnOfIndex(Index*, i16);
+SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index*, i16);
+#ifdef SQLITE_OMIT_GENERATED_COLUMNS
+# define sqlite3TableColumnToStorage(T,X) (X) /* No-op pass-through */
+# define sqlite3StorageColumnToTable(T,X) (X) /* No-op pass-through */
+#else
+SQLITE_PRIVATE i16 sqlite3TableColumnToStorage(Table*, i16);
+SQLITE_PRIVATE i16 sqlite3StorageColumnToTable(Table*, i16);
+#endif
SQLITE_PRIVATE void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int);
#if SQLITE_ENABLE_HIDDEN_COLUMNS
SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table*, Column*);
#else
# define sqlite3ColumnPropertiesFromName(T,C) /* no-op */
#endif
-SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token*,Token*);
+SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token,Token);
SQLITE_PRIVATE void sqlite3AddNotNull(Parse*, int);
SQLITE_PRIVATE void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int);
-SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*);
+SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*, const char*, const char*);
SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*);
SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*);
-SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
+SQLITE_PRIVATE void sqlite3AddGenerated(Parse*,Expr*,Token*);
+SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u32,Select*);
+SQLITE_PRIVATE void sqlite3AddReturning(Parse*,ExprList*);
SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **);
+#define sqlite3CodecQueryParameters(A,B,C) 0
SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*);
#ifdef SQLITE_UNTESTABLE
@@ -18763,6 +20820,7 @@ SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask);
SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int);
SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int);
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*);
+SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3*, void*);
SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3*, Index*);
#ifndef SQLITE_OMIT_AUTOINCREMENT
SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse);
@@ -18772,19 +20830,24 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse);
# define sqlite3AutoincrementEnd(X)
#endif
SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int, Upsert*);
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+SQLITE_PRIVATE void sqlite3ComputeGeneratedColumns(Parse*, int, Table*);
+#endif
SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse*, IdList*, Token*);
SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*);
-SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int);
-SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*);
+SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int);
+SQLITE_PRIVATE SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2);
+SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*);
SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
- Token*, Select*, Expr*, IdList*);
+ Token*, Select*, OnOrUsing*);
SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *);
SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*);
-SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, struct SrcList_item *);
-SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList*);
+SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, SrcItem *);
+SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse*,SrcList*);
SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*);
SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*);
+SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*);
SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*);
SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**);
SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
@@ -18794,21 +20857,25 @@ SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
Expr*,ExprList*,u32,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
+SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
-SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int);
+SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*);
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*);
#endif
+SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe*,int,const char*);
SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*);
SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*,
Upsert*);
-SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
+SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,
+ ExprList*,Select*,u16,int);
SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*);
SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereOrderByLimitOptLabel(WhereInfo*);
+SQLITE_PRIVATE void sqlite3WhereMinMaxOptEarlyOut(Vdbe*,WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereIsSorted(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo*);
@@ -18816,17 +20883,20 @@ SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo*, int*);
#define ONEPASS_OFF 0 /* Use of ONEPASS not allowed */
#define ONEPASS_SINGLE 1 /* ONEPASS valid for a single row update */
#define ONEPASS_MULTI 2 /* ONEPASS is valid for multiple rows */
+SQLITE_PRIVATE int sqlite3WhereUsesDeferredSeek(WhereInfo*);
SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int);
SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCode(Parse*, Expr*, int);
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int);
+#endif
SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int);
SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
-SQLITE_PRIVATE int sqlite3ExprCodeAtInit(Parse*, Expr*, int);
+SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int);
SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int);
-SQLITE_PRIVATE void sqlite3ExprCodeAndCache(Parse*, Expr*, int);
SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
#define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
#define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
@@ -18839,22 +20909,24 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3*,const char*, const char*);
#define LOCATE_VIEW 0x01
#define LOCATE_NOERR 0x02
SQLITE_PRIVATE Table *sqlite3LocateTable(Parse*,u32 flags,const char*, const char*);
-SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *);
+SQLITE_PRIVATE const char *sqlite3PreferredTableName(const char*);
+SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,SrcItem *);
SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
-SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*);
-SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int);
-SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, Token*);
-SQLITE_PRIVATE int sqlite3ExprCompare(Parse*,Expr*, Expr*, int);
-SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*, Expr*, int);
-SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*, int);
-SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int);
-SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int);
+SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*,Expr*);
+SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*);
+SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, const Token*);
+SQLITE_PRIVATE int sqlite3ExprCompare(const Parse*,const Expr*,const Expr*, int);
+SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*,Expr*,int);
+SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList*,const ExprList*, int);
+SQLITE_PRIVATE int sqlite3ExprImpliesExpr(const Parse*,const Expr*,const Expr*, int);
+SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int,int);
+SQLITE_PRIVATE void sqlite3AggInfoPersistWalkerInit(Walker*,Parse*);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
SQLITE_PRIVATE int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx);
-SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*);
+SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse*, Expr*, SrcList*);
SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*);
#ifndef SQLITE_UNTESTABLE
SQLITE_PRIVATE void sqlite3PrngSaveState(void);
@@ -18868,6 +20940,7 @@ SQLITE_PRIVATE void sqlite3EndTransaction(Parse*,int);
SQLITE_PRIVATE void sqlite3Savepoint(Parse*, int, Token*);
SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *);
SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
+SQLITE_PRIVATE u32 sqlite3IsTrueOrFalse(const char*);
SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr*);
SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*);
@@ -18875,16 +20948,15 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8);
SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*);
SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int);
+SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int);
#ifdef SQLITE_ENABLE_CURSOR_HINTS
SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*);
#endif
-SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*);
+SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*);
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
SQLITE_PRIVATE int sqlite3IsRowid(const char*);
-#ifdef SQLITE_ENABLE_NORMALIZE
-SQLITE_PRIVATE int sqlite3IsRowidN(const char*, int);
-#endif
+SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab);
SQLITE_PRIVATE void sqlite3GenerateRowDelete(
Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
@@ -18906,22 +20978,26 @@ SQLITE_PRIVATE void sqlite3MayAbort(Parse*);
SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8);
SQLITE_PRIVATE void sqlite3UniqueConstraint(Parse*, int, Index*);
SQLITE_PRIVATE void sqlite3RowidConstraint(Parse*, int, Table*);
-SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
-SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int);
-SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
-SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,IdList*);
-SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,Select*,int);
-#ifdef SQLITE_ENABLE_NORMALIZE
-SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN(int,const char*,int);
-#endif
+SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,const Expr*,int);
+SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,const ExprList*,int);
+SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,const SrcList*,int);
+SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,const IdList*);
+SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,const Select*,int);
+SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*);
SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int);
SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8);
+SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*);
SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
+SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*);
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
+SQLITE_PRIVATE int sqlite3JsonTableFunctions(sqlite3*);
+#endif
SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*);
SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*);
SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int);
+SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p);
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
@@ -18945,13 +21021,14 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*,
SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(Parse*,Token*, IdList*,
Select*,u8,Upsert*,
const char*,const char*);
-SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(Parse*,Token*,ExprList*, Expr*, u8,
- const char*,const char*);
+SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(Parse*,Token*,SrcList*,ExprList*,
+ Expr*, u8, const char*,const char*);
SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(Parse*,Token*, Expr*,
const char*,const char*);
SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3*, Trigger*);
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
SQLITE_PRIVATE u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int);
+SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc(Parse*, TriggerStep*);
# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
# define sqlite3IsToplevel(p) ((p)->pToplevel==0)
#else
@@ -18965,9 +21042,13 @@ SQLITE_PRIVATE u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Tab
# define sqlite3ParseToplevel(p) p
# define sqlite3IsToplevel(p) 1
# define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0
+# define sqlite3TriggerStepSrc(A,B) 0
#endif
SQLITE_PRIVATE int sqlite3JoinType(Parse*, Token*, Token*, Token*);
+SQLITE_PRIVATE int sqlite3ColumnIndex(Table *pTab, const char *zCol);
+SQLITE_PRIVATE void sqlite3SrcItemColumnUsed(SrcItem*,int);
+SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr*,int,u32);
SQLITE_PRIVATE void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int);
SQLITE_PRIVATE void sqlite3DeferForeignKey(Parse*, int);
#ifndef SQLITE_OMIT_AUTHORIZATION
@@ -18982,32 +21063,32 @@ SQLITE_PRIVATE int sqlite3AuthReadCol(Parse*, const char *, const char *, int)
# define sqlite3AuthContextPush(a,b,c)
# define sqlite3AuthContextPop(a) ((void)(a))
#endif
+SQLITE_PRIVATE int sqlite3DbIsNamed(sqlite3 *db, int iDb, const char *zName);
SQLITE_PRIVATE void sqlite3Attach(Parse*, Expr*, Expr*, Expr*);
SQLITE_PRIVATE void sqlite3Detach(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*);
SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*);
SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*);
-SQLITE_PRIVATE int sqlite3FixExprList(DbFixer*, ExprList*);
SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
+
+SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64);
+SQLITE_PRIVATE i64 sqlite3RealToI64(double);
+SQLITE_PRIVATE int sqlite3Int64ToText(i64,char*);
SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8);
SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*);
+SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*);
SQLITE_PRIVATE int sqlite3Atoi(const char*);
#ifndef SQLITE_OMIT_UTF16
SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar);
#endif
SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte);
SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**);
+SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*);
SQLITE_PRIVATE LogEst sqlite3LogEst(u64);
SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst);
-#ifndef SQLITE_OMIT_VIRTUALTABLE
SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double);
-#endif
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
- defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
- defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst);
-#endif
SQLITE_PRIVATE VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int);
SQLITE_PRIVATE const char *sqlite3VListNumToName(VList*,int);
SQLITE_PRIVATE int sqlite3VListNameToNum(VList*,const char*,int);
@@ -19029,6 +21110,8 @@ SQLITE_PRIVATE int sqlite3VarintLen(u64 v);
*/
#define getVarint32(A,B) \
(u8)((*(A)<(u8)0x80)?((B)=(u32)*(A)),1:sqlite3GetVarint32((A),(u32 *)&(B)))
+#define getVarint32NR(A,B) \
+ B=(u32)*(A);if(B>=0x80)sqlite3GetVarint32((A),(u32*)&(B))
#define putVarint32(A,B) \
(u8)(((u32)(B)<(u32)0x80)?(*(A)=(unsigned char)(B)),1:\
sqlite3PutVarint((A),(B)))
@@ -19037,15 +21120,18 @@ SQLITE_PRIVATE int sqlite3VarintLen(u64 v);
SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3*, Index*);
+SQLITE_PRIVATE char *sqlite3TableAffinityStr(sqlite3*,const Table*);
SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe*, Table*, int);
-SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2);
-SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
-SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table*,int);
-SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr);
+SQLITE_PRIVATE char sqlite3CompareAffinity(const Expr *pExpr, char aff2);
+SQLITE_PRIVATE int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity);
+SQLITE_PRIVATE char sqlite3TableColumnAffinity(const Table*,int);
+SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr);
+SQLITE_PRIVATE int sqlite3ExprDataType(const Expr *pExpr);
SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8);
SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*);
SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
SQLITE_PRIVATE void sqlite3Error(sqlite3*,int);
+SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3*);
SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int);
SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
SQLITE_PRIVATE u8 sqlite3HexToInt(int h);
@@ -19055,8 +21141,11 @@ SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
SQLITE_PRIVATE const char *sqlite3ErrName(int);
#endif
-#ifdef SQLITE_ENABLE_DESERIALIZE
+#ifndef SQLITE_OMIT_DESERIALIZE
SQLITE_PRIVATE int sqlite3MemdbInit(void);
+SQLITE_PRIVATE int sqlite3IsMemdb(const sqlite3_vfs*);
+#else
+# define sqlite3IsMemdb(X) 0
#endif
SQLITE_PRIVATE const char *sqlite3ErrStr(int);
@@ -19064,16 +21153,18 @@ SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse);
SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int);
SQLITE_PRIVATE int sqlite3IsBinary(const CollSeq*);
SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName);
-SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
-SQLITE_PRIVATE CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, Expr *pExpr);
-SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse*,Expr*,Expr*);
-SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int);
-SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*);
+SQLITE_PRIVATE void sqlite3SetTextEncoding(sqlite3 *db, u8);
+SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr);
+SQLITE_PRIVATE CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr);
+SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse*,const Expr*,const Expr*);
+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(const Parse *pParse, Expr*, const Token*, int);
+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(const Parse*,Expr*,const char*);
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*);
+SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr*);
SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *);
SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3*);
-SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *, const char *);
-SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int);
+SQLITE_PRIVATE int sqlite3CheckObjectName(Parse*, const char*,const char*,const char*);
+SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, i64);
SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64);
SQLITE_PRIVATE int sqlite3SubInt64(i64*,i64);
SQLITE_PRIVATE int sqlite3MulInt64(i64*,i64);
@@ -19086,63 +21177,80 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(const char*, char*);
SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z,u8);
SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8);
+SQLITE_PRIVATE int sqlite3ValueIsOfClass(const sqlite3_value*, void(*)(void*));
SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8);
SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
void(*)(void*));
SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value*);
SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value*);
+#ifndef SQLITE_UNTESTABLE
+SQLITE_PRIVATE void sqlite3ResultIntReal(sqlite3_context*);
+#endif
SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *);
#ifndef SQLITE_OMIT_UTF16
SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8);
#endif
-SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **);
+SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, const Expr *, u8, u8, sqlite3_value **);
SQLITE_PRIVATE void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8);
#ifndef SQLITE_AMALGAMATION
SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[];
SQLITE_PRIVATE const char sqlite3StrBINARY[];
+SQLITE_PRIVATE const unsigned char sqlite3StdTypeLen[];
+SQLITE_PRIVATE const char sqlite3StdTypeAffinity[];
+SQLITE_PRIVATE const char *sqlite3StdType[];
SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[];
+SQLITE_PRIVATE const unsigned char *sqlite3aLTb;
+SQLITE_PRIVATE const unsigned char *sqlite3aEQb;
+SQLITE_PRIVATE const unsigned char *sqlite3aGTb;
SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[];
-SQLITE_PRIVATE const Token sqlite3IntTokens[];
SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config;
SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;
#ifndef SQLITE_OMIT_WSD
SQLITE_PRIVATE int sqlite3PendingByte;
#endif
-#endif
+#endif /* SQLITE_AMALGAMATION */
#ifdef VDBE_PROFILE
SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt;
#endif
-SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, int, int);
+SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, Pgno, Pgno);
SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*);
SQLITE_PRIVATE void sqlite3AlterFunctions(void);
SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
-#ifdef SQLITE_ENABLE_NORMALIZE
-SQLITE_PRIVATE int sqlite3GetTokenNormalized(const unsigned char *, int *, int *);
-#endif
SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int);
-SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr *, int, int);
+SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int);
+SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
+SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse*, SrcItem*);
SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
-SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
+SQLITE_PRIVATE int sqlite3MatchEName(
+ const struct ExprList_item*,
+ const char*,
+ const char*,
+ const char*,
+ int*
+);
+SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*);
+SQLITE_PRIVATE u8 sqlite3StrIHash(const char*);
SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*);
SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
-SQLITE_PRIVATE void sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
+SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *);
SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
-SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse*, void*, Token*);
-SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom);
+SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse*, SrcList*, const Token*);
+SQLITE_PRIVATE const void *sqlite3RenameTokenMap(Parse*, const void*, const Token*);
+SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse*, const void *pTo, const void *pFrom);
SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*);
SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*);
SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*);
-SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*);
+SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*);
SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*);
SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *);
SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB);
@@ -19158,28 +21266,40 @@ SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*);
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);
+SQLITE_PRIVATE const char *sqlite3SelectOpName(int);
+SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse*, ExprList*);
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*);
#endif
SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
void (*)(sqlite3_context*,int,sqlite3_value **),
- void (*)(sqlite3_context*,int,sqlite3_value **),
+ void (*)(sqlite3_context*,int,sqlite3_value **),
void (*)(sqlite3_context*),
void (*)(sqlite3_context*),
- void (*)(sqlite3_context*,int,sqlite3_value **),
+ void (*)(sqlite3_context*,int,sqlite3_value **),
FuncDestructor *pDestructor
);
SQLITE_PRIVATE void sqlite3NoopDestructor(void*);
-SQLITE_PRIVATE void sqlite3OomFault(sqlite3*);
+SQLITE_PRIVATE void *sqlite3OomFault(sqlite3*);
SQLITE_PRIVATE void sqlite3OomClear(sqlite3*);
SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int);
SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *);
+SQLITE_PRIVATE char *sqlite3RCStrRef(char*);
+SQLITE_PRIVATE void sqlite3RCStrUnref(void*);
+SQLITE_PRIVATE char *sqlite3RCStrNew(u64);
+SQLITE_PRIVATE char *sqlite3RCStrResize(char*,u64);
+
SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int);
+SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum*, i64);
SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*);
+SQLITE_PRIVATE void sqlite3StrAccumSetError(StrAccum*, u8);
+SQLITE_PRIVATE void sqlite3ResultStrAccum(sqlite3_context*,StrAccum*);
SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int);
SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
+SQLITE_PRIVATE void sqlite3RecordErrorByteOffset(sqlite3*,const char*);
+SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3*,const Expr*);
SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *);
SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
@@ -19190,8 +21310,7 @@ SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse*, Expr*);
# define sqlite3ExprCheckIN(x,y) SQLITE_OK
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void);
+#ifdef SQLITE_ENABLE_STAT4
SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(
Parse*,Index*,UnpackedRecord**,Expr*,int,int,int*);
SQLITE_PRIVATE int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
@@ -19221,7 +21340,7 @@ SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3*);
#endif
#ifndef SQLITE_OMIT_SHARED_CACHE
-SQLITE_PRIVATE void sqlite3TableLock(Parse *, int, int, u8, const char *);
+SQLITE_PRIVATE void sqlite3TableLock(Parse *, int, Pgno, u8, const char *);
#else
#define sqlite3TableLock(v,w,x,y,z)
#endif
@@ -19231,13 +21350,14 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*);
#endif
#ifdef SQLITE_OMIT_VIRTUALTABLE
-# define sqlite3VtabClear(Y)
+# define sqlite3VtabClear(D,T)
# define sqlite3VtabSync(X,Y) SQLITE_OK
# define sqlite3VtabRollback(X)
# define sqlite3VtabCommit(X)
# define sqlite3VtabInSync(db) 0
# define sqlite3VtabLock(X)
# define sqlite3VtabUnlock(X)
+# define sqlite3VtabModuleUnref(D,X)
# define sqlite3VtabUnlockList(X)
# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK
# define sqlite3GetVTable(X,Y) ((VTable*)0)
@@ -19249,6 +21369,7 @@ SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db);
SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db);
SQLITE_PRIVATE void sqlite3VtabLock(VTable *);
SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *);
+SQLITE_PRIVATE void sqlite3VtabModuleUnref(sqlite3*,Module*);
SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3*);
SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *, int, int);
SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*);
@@ -19262,6 +21383,16 @@ SQLITE_PRIVATE Module *sqlite3VtabCreateModule(
);
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
#endif
+SQLITE_PRIVATE int sqlite3ReadOnlyShadowTables(sqlite3 *db);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName);
+SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3*,Table*,const char*);
+SQLITE_PRIVATE void sqlite3MarkAllShadowTablesOf(sqlite3*, Table*);
+#else
+# define sqlite3ShadowTableName(A,B) 0
+# define sqlite3IsShadowTableOf(A,B,C) 0
+# define sqlite3MarkAllShadowTablesOf(A,B)
+#endif
SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse*,Module*);
SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3*,Module*);
SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*);
@@ -19273,17 +21404,22 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **);
SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse*, Table*);
SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *);
+
SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*);
+SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(Parse*);
SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*);
SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int);
SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
-SQLITE_PRIVATE void sqlite3ParserReset(Parse*);
+SQLITE_PRIVATE void sqlite3ParseObjectInit(Parse*,sqlite3*);
+SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse*);
+SQLITE_PRIVATE void *sqlite3ParserAddCleanup(Parse*,void(*)(sqlite3*,void*),void*);
#ifdef SQLITE_ENABLE_NORMALIZE
-SQLITE_PRIVATE void sqlite3Normalize(Vdbe*, const char*, int, u8);
+SQLITE_PRIVATE char *sqlite3Normalize(Vdbe*, const char*);
#endif
SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*);
SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
-SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
+SQLITE_PRIVATE CollSeq *sqlite3ExprCompareCollSeq(Parse*,const Expr*);
+SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, const Expr*, const Expr*);
SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3*);
SQLITE_PRIVATE const char *sqlite3JournalModename(int);
#ifndef SQLITE_OMIT_WAL
@@ -19291,23 +21427,33 @@ SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int, int, int*, int*);
SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
#endif
#ifndef SQLITE_OMIT_CTE
-SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*);
+SQLITE_PRIVATE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8);
+SQLITE_PRIVATE void sqlite3CteDelete(sqlite3*,Cte*);
+SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Cte*);
SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*);
-SQLITE_PRIVATE void sqlite3WithPush(Parse*, With*, u8);
+SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3*,void*);
+SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8);
#else
-#define sqlite3WithPush(x,y,z)
-#define sqlite3WithDelete(x,y)
+# define sqlite3CteNew(P,T,E,S) ((void*)0)
+# define sqlite3CteDelete(D,C)
+# define sqlite3CteWithAdd(P,W,C) ((void*)0)
+# define sqlite3WithDelete(x,y)
+# define sqlite3WithPush(x,y,z) ((void*)0)
#endif
#ifndef SQLITE_OMIT_UPSERT
-SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*);
+SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*);
SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3*,Upsert*);
SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
SQLITE_PRIVATE void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);
+SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert*,Index*);
+SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert*);
#else
-#define sqlite3UpsertNew(v,w,x,y,z) ((Upsert*)0)
+#define sqlite3UpsertNew(u,v,w,x,y,z) ((Upsert*)0)
#define sqlite3UpsertDelete(x,y)
-#define sqlite3UpsertDup(x,y) ((Upsert*)0)
+#define sqlite3UpsertDup(x,y) ((Upsert*)0)
+#define sqlite3UpsertOfIndex(x,y) ((Upsert*)0)
+#define sqlite3UpsertNextIsIPK(x) 0
#endif
@@ -19325,6 +21471,7 @@ SQLITE_PRIVATE void sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, int
SQLITE_PRIVATE int sqlite3FkRequired(Parse*, Table*, int*, int);
SQLITE_PRIVATE u32 sqlite3FkOldmask(Parse*, Table*);
SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *);
+SQLITE_PRIVATE void sqlite3FkClearTriggerCache(sqlite3*,int);
#else
#define sqlite3FkActions(a,b,c,d,e,f)
#define sqlite3FkCheck(a,b,c,d,e,f)
@@ -19332,6 +21479,7 @@ SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *);
#define sqlite3FkOldmask(a,b) 0
#define sqlite3FkRequired(a,b,c,d) 0
#define sqlite3FkReferences(a) 0
+ #define sqlite3FkClearTriggerCache(a,b)
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *, Table*);
@@ -19375,7 +21523,7 @@ SQLITE_PRIVATE void sqlite3EndBenignMalloc(void);
#define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */
#define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */
#define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */
-SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*);
+SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*, int*);
SQLITE_PRIVATE int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *);
@@ -19389,12 +21537,13 @@ SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *);
SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p);
#if SQLITE_MAX_EXPR_DEPTH>0
-SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *);
+SQLITE_PRIVATE int sqlite3SelectExprHeight(const Select *);
SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse*, int);
#else
#define sqlite3SelectExprHeight(x) 0
#define sqlite3ExprCheckHeight(x,y)
#endif
+SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr*,int);
SQLITE_PRIVATE u32 sqlite3Get4byte(const u8*);
SQLITE_PRIVATE void sqlite3Put4byte(u8*, u32);
@@ -19460,8 +21609,8 @@ SQLITE_API SQLITE_EXTERN void (SQLITE_CDECL *sqlite3IoTrace)(const char*,...);
*/
#ifdef SQLITE_MEMDEBUG
SQLITE_PRIVATE void sqlite3MemdebugSetType(void*,u8);
-SQLITE_PRIVATE int sqlite3MemdebugHasType(void*,u8);
-SQLITE_PRIVATE int sqlite3MemdebugNoType(void*,u8);
+SQLITE_PRIVATE int sqlite3MemdebugHasType(const void*,u8);
+SQLITE_PRIVATE int sqlite3MemdebugNoType(const void*,u8);
#else
# define sqlite3MemdebugSetType(X,Y) /* no-op */
# define sqlite3MemdebugHasType(X,Y) 1
@@ -19486,19 +21635,933 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3*);
SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3*);
#endif
-SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr);
-SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr);
+SQLITE_PRIVATE int sqlite3ExprVectorSize(const Expr *pExpr);
+SQLITE_PRIVATE int sqlite3ExprIsVector(const Expr *pExpr);
SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr*, int);
-SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(Parse*,Expr*,int);
+SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(Parse*,Expr*,int,int);
SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse*, Expr*);
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt);
#endif
+#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)
+SQLITE_PRIVATE int sqlite3KvvfsInit(void);
+#endif
+
+#if defined(VDBE_PROFILE) \
+ || defined(SQLITE_PERFORMANCE_TRACE) \
+ || defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+SQLITE_PRIVATE sqlite3_uint64 sqlite3Hwtime(void);
+#endif
+
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+# define IS_STMT_SCANSTATUS(db) (db->flags & SQLITE_StmtScanStatus)
+#else
+# define IS_STMT_SCANSTATUS(db) 0
+#endif
+
#endif /* SQLITEINT_H */
/************** End of sqliteInt.h *******************************************/
+/************** Begin file os_common.h ***************************************/
+/*
+** 2004 May 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 contains macros and a little bit of code that is common to
+** all of the platform-specific files (os_*.c) and is #included into those
+** files.
+**
+** This file should be #included by the os_*.c files only. It is not a
+** general purpose header file.
+*/
+#ifndef _OS_COMMON_H_
+#define _OS_COMMON_H_
+
+/*
+** At least two bugs have slipped in because we changed the MEMORY_DEBUG
+** macro to SQLITE_DEBUG and some older makefiles have not yet made the
+** switch. The following code should catch this problem at compile-time.
+*/
+#ifdef MEMORY_DEBUG
+# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
+#endif
+
+/*
+** Macros for performance tracing. Normally turned off. Only works
+** on i486 hardware.
+*/
+#ifdef SQLITE_PERFORMANCE_TRACE
+
+static sqlite_uint64 g_start;
+static sqlite_uint64 g_elapsed;
+#define TIMER_START g_start=sqlite3Hwtime()
+#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
+#define TIMER_ELAPSED g_elapsed
+#else
+#define TIMER_START
+#define TIMER_END
+#define TIMER_ELAPSED ((sqlite_uint64)0)
+#endif
+
+/*
+** If we compile with the SQLITE_TEST macro set, then the following block
+** of code will give us the ability to simulate a disk I/O error. This
+** is used for testing the I/O recovery logic.
+*/
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_io_error_hit;
+SQLITE_API extern int sqlite3_io_error_hardhit;
+SQLITE_API extern int sqlite3_io_error_pending;
+SQLITE_API extern int sqlite3_io_error_persist;
+SQLITE_API extern int sqlite3_io_error_benign;
+SQLITE_API extern int sqlite3_diskfull_pending;
+SQLITE_API extern int sqlite3_diskfull;
+#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
+#define SimulateIOError(CODE) \
+ if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
+ || sqlite3_io_error_pending-- == 1 ) \
+ { local_ioerr(); CODE; }
+static void local_ioerr(){
+ IOTRACE(("IOERR\n"));
+ sqlite3_io_error_hit++;
+ if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
+}
+#define SimulateDiskfullError(CODE) \
+ if( sqlite3_diskfull_pending ){ \
+ if( sqlite3_diskfull_pending == 1 ){ \
+ local_ioerr(); \
+ sqlite3_diskfull = 1; \
+ sqlite3_io_error_hit = 1; \
+ CODE; \
+ }else{ \
+ sqlite3_diskfull_pending--; \
+ } \
+ }
+#else
+#define SimulateIOErrorBenign(X)
+#define SimulateIOError(A)
+#define SimulateDiskfullError(A)
+#endif /* defined(SQLITE_TEST) */
+
+/*
+** When testing, keep a count of the number of open files.
+*/
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_open_file_count;
+#define OpenCounter(X) sqlite3_open_file_count+=(X)
+#else
+#define OpenCounter(X)
+#endif /* defined(SQLITE_TEST) */
+
+#endif /* !defined(_OS_COMMON_H_) */
+
+/************** End of os_common.h *******************************************/
+/************** Begin file ctime.c *******************************************/
+/* DO NOT EDIT!
+** This file is automatically generated by the script in the canonical
+** SQLite source tree at tool/mkctimec.tcl.
+**
+** To modify this header, edit any of the various lists in that script
+** which specify categories of generated conditionals in this file.
+*/
+
+/*
+** 2010 February 23
+**
+** 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 routines used to report what compile-time options
+** SQLite was built with.
+*/
+#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* IMP: R-16824-07538 */
+
+/*
+** Include the configuration header output by 'configure' if we're using the
+** autoconf-based build
+*/
+#if defined(_HAVE_SQLITE_CONFIG_H) && !defined(SQLITECONFIG_H)
+/* #include "sqlite_cfg.h" */
+#define SQLITECONFIG_H 1
+#endif
+
+/* These macros are provided to "stringify" the value of the define
+** for those options in which the value is meaningful. */
+#define CTIMEOPT_VAL_(opt) #opt
+#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt)
+
+/* Like CTIMEOPT_VAL, but especially for SQLITE_DEFAULT_LOOKASIDE. This
+** option requires a separate macro because legal values contain a single
+** comma. e.g. (-DSQLITE_DEFAULT_LOOKASIDE="100,100") */
+#define CTIMEOPT_VAL2_(opt1,opt2) #opt1 "," #opt2
+#define CTIMEOPT_VAL2(opt) CTIMEOPT_VAL2_(opt)
+/* #include "sqliteInt.h" */
+
+/*
+** An array of names of all compile-time options. This array should
+** be sorted A-Z.
+**
+** This array looks large, but in a typical installation actually uses
+** only a handful of compile-time options, so most times this array is usually
+** rather short and uses little memory space.
+*/
+static const char * const sqlite3azCompileOpt[] = {
+
+#ifdef SQLITE_32BIT_ROWID
+ "32BIT_ROWID",
+#endif
+#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
+ "4_BYTE_ALIGNED_MALLOC",
+#endif
+#ifdef SQLITE_ALLOW_COVERING_INDEX_SCAN
+# if SQLITE_ALLOW_COVERING_INDEX_SCAN != 1
+ "ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN),
+# endif
+#endif
+#ifdef SQLITE_ALLOW_URI_AUTHORITY
+ "ALLOW_URI_AUTHORITY",
+#endif
+#ifdef SQLITE_ATOMIC_INTRINSICS
+ "ATOMIC_INTRINSICS=" CTIMEOPT_VAL(SQLITE_ATOMIC_INTRINSICS),
+#endif
+#ifdef SQLITE_BITMASK_TYPE
+ "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE),
+#endif
+#ifdef SQLITE_BUG_COMPATIBLE_20160819
+ "BUG_COMPATIBLE_20160819",
+#endif
+#ifdef SQLITE_CASE_SENSITIVE_LIKE
+ "CASE_SENSITIVE_LIKE",
+#endif
+#ifdef SQLITE_CHECK_PAGES
+ "CHECK_PAGES",
+#endif
+#if defined(__clang__) && defined(__clang_major__)
+ "COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "."
+ CTIMEOPT_VAL(__clang_minor__) "."
+ CTIMEOPT_VAL(__clang_patchlevel__),
+#elif defined(_MSC_VER)
+ "COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER),
+#elif defined(__GNUC__) && defined(__VERSION__)
+ "COMPILER=gcc-" __VERSION__,
+#endif
+#ifdef SQLITE_COVERAGE_TEST
+ "COVERAGE_TEST",
+#endif
+#ifdef SQLITE_DEBUG
+ "DEBUG",
+#endif
+#ifdef SQLITE_DEFAULT_AUTOMATIC_INDEX
+ "DEFAULT_AUTOMATIC_INDEX",
+#endif
+#ifdef SQLITE_DEFAULT_AUTOVACUUM
+ "DEFAULT_AUTOVACUUM",
+#endif
+#ifdef SQLITE_DEFAULT_CACHE_SIZE
+ "DEFAULT_CACHE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_CACHE_SIZE),
+#endif
+#ifdef SQLITE_DEFAULT_CKPTFULLFSYNC
+ "DEFAULT_CKPTFULLFSYNC",
+#endif
+#ifdef SQLITE_DEFAULT_FILE_FORMAT
+ "DEFAULT_FILE_FORMAT=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_FORMAT),
+#endif
+#ifdef SQLITE_DEFAULT_FILE_PERMISSIONS
+ "DEFAULT_FILE_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_FILE_PERMISSIONS),
+#endif
+#ifdef SQLITE_DEFAULT_FOREIGN_KEYS
+ "DEFAULT_FOREIGN_KEYS",
+#endif
+#ifdef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT
+ "DEFAULT_JOURNAL_SIZE_LIMIT=" CTIMEOPT_VAL(SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT),
+#endif
+#ifdef SQLITE_DEFAULT_LOCKING_MODE
+ "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE),
+#endif
+#ifdef SQLITE_DEFAULT_LOOKASIDE
+ "DEFAULT_LOOKASIDE=" CTIMEOPT_VAL2(SQLITE_DEFAULT_LOOKASIDE),
+#endif
+#ifdef SQLITE_DEFAULT_MEMSTATUS
+# if SQLITE_DEFAULT_MEMSTATUS != 1
+ "DEFAULT_MEMSTATUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_MEMSTATUS),
+# endif
+#endif
+#ifdef SQLITE_DEFAULT_MMAP_SIZE
+ "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE),
+#endif
+#ifdef SQLITE_DEFAULT_PAGE_SIZE
+ "DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_PAGE_SIZE),
+#endif
+#ifdef SQLITE_DEFAULT_PCACHE_INITSZ
+ "DEFAULT_PCACHE_INITSZ=" CTIMEOPT_VAL(SQLITE_DEFAULT_PCACHE_INITSZ),
+#endif
+#ifdef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS
+ "DEFAULT_PROXYDIR_PERMISSIONS=" CTIMEOPT_VAL(SQLITE_DEFAULT_PROXYDIR_PERMISSIONS),
+#endif
+#ifdef SQLITE_DEFAULT_RECURSIVE_TRIGGERS
+ "DEFAULT_RECURSIVE_TRIGGERS",
+#endif
+#ifdef SQLITE_DEFAULT_ROWEST
+ "DEFAULT_ROWEST=" CTIMEOPT_VAL(SQLITE_DEFAULT_ROWEST),
+#endif
+#ifdef SQLITE_DEFAULT_SECTOR_SIZE
+ "DEFAULT_SECTOR_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_SECTOR_SIZE),
+#endif
+#ifdef SQLITE_DEFAULT_SYNCHRONOUS
+ "DEFAULT_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_SYNCHRONOUS),
+#endif
+#ifdef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT
+ "DEFAULT_WAL_AUTOCHECKPOINT=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_AUTOCHECKPOINT),
+#endif
+#ifdef SQLITE_DEFAULT_WAL_SYNCHRONOUS
+ "DEFAULT_WAL_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_SYNCHRONOUS),
+#endif
+#ifdef SQLITE_DEFAULT_WORKER_THREADS
+ "DEFAULT_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WORKER_THREADS),
+#endif
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ "DIRECT_OVERFLOW_READ",
+#endif
+#ifdef SQLITE_DISABLE_DIRSYNC
+ "DISABLE_DIRSYNC",
+#endif
+#ifdef SQLITE_DISABLE_FTS3_UNICODE
+ "DISABLE_FTS3_UNICODE",
+#endif
+#ifdef SQLITE_DISABLE_FTS4_DEFERRED
+ "DISABLE_FTS4_DEFERRED",
+#endif
+#ifdef SQLITE_DISABLE_INTRINSIC
+ "DISABLE_INTRINSIC",
+#endif
+#ifdef SQLITE_DISABLE_LFS
+ "DISABLE_LFS",
+#endif
+#ifdef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
+ "DISABLE_PAGECACHE_OVERFLOW_STATS",
+#endif
+#ifdef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
+ "DISABLE_SKIPAHEAD_DISTINCT",
+#endif
+#ifdef SQLITE_DQS
+ "DQS=" CTIMEOPT_VAL(SQLITE_DQS),
+#endif
+#ifdef SQLITE_ENABLE_8_3_NAMES
+ "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES),
+#endif
+#ifdef SQLITE_ENABLE_API_ARMOR
+ "ENABLE_API_ARMOR",
+#endif
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ "ENABLE_ATOMIC_WRITE",
+#endif
+#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
+ "ENABLE_BATCH_ATOMIC_WRITE",
+#endif
+#ifdef SQLITE_ENABLE_BYTECODE_VTAB
+ "ENABLE_BYTECODE_VTAB",
+#endif
+#ifdef SQLITE_ENABLE_CEROD
+ "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
+#endif
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+ "ENABLE_COLUMN_METADATA",
+#endif
+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
+ "ENABLE_COLUMN_USED_MASK",
+#endif
+#ifdef SQLITE_ENABLE_COSTMULT
+ "ENABLE_COSTMULT",
+#endif
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ "ENABLE_CURSOR_HINTS",
+#endif
+#ifdef SQLITE_ENABLE_DBPAGE_VTAB
+ "ENABLE_DBPAGE_VTAB",
+#endif
+#ifdef SQLITE_ENABLE_DBSTAT_VTAB
+ "ENABLE_DBSTAT_VTAB",
+#endif
+#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
+ "ENABLE_EXPENSIVE_ASSERT",
+#endif
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ "ENABLE_EXPLAIN_COMMENTS",
+#endif
+#ifdef SQLITE_ENABLE_FTS3
+ "ENABLE_FTS3",
+#endif
+#ifdef SQLITE_ENABLE_FTS3_PARENTHESIS
+ "ENABLE_FTS3_PARENTHESIS",
+#endif
+#ifdef SQLITE_ENABLE_FTS3_TOKENIZER
+ "ENABLE_FTS3_TOKENIZER",
+#endif
+#ifdef SQLITE_ENABLE_FTS4
+ "ENABLE_FTS4",
+#endif
+#ifdef SQLITE_ENABLE_FTS5
+ "ENABLE_FTS5",
+#endif
+#ifdef SQLITE_ENABLE_GEOPOLY
+ "ENABLE_GEOPOLY",
+#endif
+#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS
+ "ENABLE_HIDDEN_COLUMNS",
+#endif
+#ifdef SQLITE_ENABLE_ICU
+ "ENABLE_ICU",
+#endif
+#ifdef SQLITE_ENABLE_IOTRACE
+ "ENABLE_IOTRACE",
+#endif
+#ifdef SQLITE_ENABLE_LOAD_EXTENSION
+ "ENABLE_LOAD_EXTENSION",
+#endif
+#ifdef SQLITE_ENABLE_LOCKING_STYLE
+ "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE),
+#endif
+#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
+ "ENABLE_MATH_FUNCTIONS",
+#endif
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ "ENABLE_MEMORY_MANAGEMENT",
+#endif
+#ifdef SQLITE_ENABLE_MEMSYS3
+ "ENABLE_MEMSYS3",
+#endif
+#ifdef SQLITE_ENABLE_MEMSYS5
+ "ENABLE_MEMSYS5",
+#endif
+#ifdef SQLITE_ENABLE_MULTIPLEX
+ "ENABLE_MULTIPLEX",
+#endif
+#ifdef SQLITE_ENABLE_NORMALIZE
+ "ENABLE_NORMALIZE",
+#endif
+#ifdef SQLITE_ENABLE_NULL_TRIM
+ "ENABLE_NULL_TRIM",
+#endif
+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
+ "ENABLE_OFFSET_SQL_FUNC",
+#endif
+#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK
+ "ENABLE_OVERSIZE_CELL_CHECK",
+#endif
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ "ENABLE_PREUPDATE_HOOK",
+#endif
+#ifdef SQLITE_ENABLE_QPSG
+ "ENABLE_QPSG",
+#endif
+#ifdef SQLITE_ENABLE_RBU
+ "ENABLE_RBU",
+#endif
+#ifdef SQLITE_ENABLE_RTREE
+ "ENABLE_RTREE",
+#endif
+#ifdef SQLITE_ENABLE_SESSION
+ "ENABLE_SESSION",
+#endif
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ "ENABLE_SNAPSHOT",
+#endif
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ "ENABLE_SORTER_REFERENCES",
+#endif
+#ifdef SQLITE_ENABLE_SQLLOG
+ "ENABLE_SQLLOG",
+#endif
+#ifdef SQLITE_ENABLE_STAT4
+ "ENABLE_STAT4",
+#endif
+#ifdef SQLITE_ENABLE_STMTVTAB
+ "ENABLE_STMTVTAB",
+#endif
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ "ENABLE_STMT_SCANSTATUS",
+#endif
+#ifdef SQLITE_ENABLE_TREETRACE
+ "ENABLE_TREETRACE",
+#endif
+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+ "ENABLE_UNKNOWN_SQL_FUNCTION",
+#endif
+#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
+ "ENABLE_UNLOCK_NOTIFY",
+#endif
+#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
+ "ENABLE_UPDATE_DELETE_LIMIT",
+#endif
+#ifdef SQLITE_ENABLE_URI_00_ERROR
+ "ENABLE_URI_00_ERROR",
+#endif
+#ifdef SQLITE_ENABLE_VFSTRACE
+ "ENABLE_VFSTRACE",
+#endif
+#ifdef SQLITE_ENABLE_WHERETRACE
+ "ENABLE_WHERETRACE",
+#endif
+#ifdef SQLITE_ENABLE_ZIPVFS
+ "ENABLE_ZIPVFS",
+#endif
+#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
+ "EXPLAIN_ESTIMATED_ROWS",
+#endif
+#ifdef SQLITE_EXTRA_AUTOEXT
+ "EXTRA_AUTOEXT=" CTIMEOPT_VAL(SQLITE_EXTRA_AUTOEXT),
+#endif
+#ifdef SQLITE_EXTRA_IFNULLROW
+ "EXTRA_IFNULLROW",
+#endif
+#ifdef SQLITE_EXTRA_INIT
+ "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT),
+#endif
+#ifdef SQLITE_EXTRA_SHUTDOWN
+ "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN),
+#endif
+#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH
+ "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH),
+#endif
+#ifdef SQLITE_FTS5_ENABLE_TEST_MI
+ "FTS5_ENABLE_TEST_MI",
+#endif
+#ifdef SQLITE_FTS5_NO_WITHOUT_ROWID
+ "FTS5_NO_WITHOUT_ROWID",
+#endif
+#if HAVE_ISNAN || SQLITE_HAVE_ISNAN
+ "HAVE_ISNAN",
+#endif
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
+# if SQLITE_HOMEGROWN_RECURSIVE_MUTEX != 1
+ "HOMEGROWN_RECURSIVE_MUTEX=" CTIMEOPT_VAL(SQLITE_HOMEGROWN_RECURSIVE_MUTEX),
+# endif
+#endif
+#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS
+ "IGNORE_AFP_LOCK_ERRORS",
+#endif
+#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
+ "IGNORE_FLOCK_LOCK_ERRORS",
+#endif
+#ifdef SQLITE_INLINE_MEMCPY
+ "INLINE_MEMCPY",
+#endif
+#ifdef SQLITE_INT64_TYPE
+ "INT64_TYPE",
+#endif
+#ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX
+ "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX),
+#endif
+#ifdef SQLITE_LEGACY_JSON_VALID
+ "LEGACY_JSON_VALID",
+#endif
+#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ "LIKE_DOESNT_MATCH_BLOBS",
+#endif
+#ifdef SQLITE_LOCK_TRACE
+ "LOCK_TRACE",
+#endif
+#ifdef SQLITE_LOG_CACHE_SPILL
+ "LOG_CACHE_SPILL",
+#endif
+#ifdef SQLITE_MALLOC_SOFT_LIMIT
+ "MALLOC_SOFT_LIMIT=" CTIMEOPT_VAL(SQLITE_MALLOC_SOFT_LIMIT),
+#endif
+#ifdef SQLITE_MAX_ATTACHED
+ "MAX_ATTACHED=" CTIMEOPT_VAL(SQLITE_MAX_ATTACHED),
+#endif
+#ifdef SQLITE_MAX_COLUMN
+ "MAX_COLUMN=" CTIMEOPT_VAL(SQLITE_MAX_COLUMN),
+#endif
+#ifdef SQLITE_MAX_COMPOUND_SELECT
+ "MAX_COMPOUND_SELECT=" CTIMEOPT_VAL(SQLITE_MAX_COMPOUND_SELECT),
+#endif
+#ifdef SQLITE_MAX_DEFAULT_PAGE_SIZE
+ "MAX_DEFAULT_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_DEFAULT_PAGE_SIZE),
+#endif
+#ifdef SQLITE_MAX_EXPR_DEPTH
+ "MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_EXPR_DEPTH),
+#endif
+#ifdef SQLITE_MAX_FUNCTION_ARG
+ "MAX_FUNCTION_ARG=" CTIMEOPT_VAL(SQLITE_MAX_FUNCTION_ARG),
+#endif
+#ifdef SQLITE_MAX_LENGTH
+ "MAX_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LENGTH),
+#endif
+#ifdef SQLITE_MAX_LIKE_PATTERN_LENGTH
+ "MAX_LIKE_PATTERN_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_LIKE_PATTERN_LENGTH),
+#endif
+#ifdef SQLITE_MAX_MEMORY
+ "MAX_MEMORY=" CTIMEOPT_VAL(SQLITE_MAX_MEMORY),
+#endif
+#ifdef SQLITE_MAX_MMAP_SIZE
+ "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE),
+#endif
+#ifdef SQLITE_MAX_MMAP_SIZE_
+ "MAX_MMAP_SIZE_=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE_),
+#endif
+#ifdef SQLITE_MAX_PAGE_COUNT
+ "MAX_PAGE_COUNT=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_COUNT),
+#endif
+#ifdef SQLITE_MAX_PAGE_SIZE
+ "MAX_PAGE_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_PAGE_SIZE),
+#endif
+#ifdef SQLITE_MAX_SCHEMA_RETRY
+ "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY),
+#endif
+#ifdef SQLITE_MAX_SQL_LENGTH
+ "MAX_SQL_LENGTH=" CTIMEOPT_VAL(SQLITE_MAX_SQL_LENGTH),
+#endif
+#ifdef SQLITE_MAX_TRIGGER_DEPTH
+ "MAX_TRIGGER_DEPTH=" CTIMEOPT_VAL(SQLITE_MAX_TRIGGER_DEPTH),
+#endif
+#ifdef SQLITE_MAX_VARIABLE_NUMBER
+ "MAX_VARIABLE_NUMBER=" CTIMEOPT_VAL(SQLITE_MAX_VARIABLE_NUMBER),
+#endif
+#ifdef SQLITE_MAX_VDBE_OP
+ "MAX_VDBE_OP=" CTIMEOPT_VAL(SQLITE_MAX_VDBE_OP),
+#endif
+#ifdef SQLITE_MAX_WORKER_THREADS
+ "MAX_WORKER_THREADS=" CTIMEOPT_VAL(SQLITE_MAX_WORKER_THREADS),
+#endif
+#ifdef SQLITE_MEMDEBUG
+ "MEMDEBUG",
+#endif
+#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT
+ "MIXED_ENDIAN_64BIT_FLOAT",
+#endif
+#ifdef SQLITE_MMAP_READWRITE
+ "MMAP_READWRITE",
+#endif
+#ifdef SQLITE_MUTEX_NOOP
+ "MUTEX_NOOP",
+#endif
+#ifdef SQLITE_MUTEX_OMIT
+ "MUTEX_OMIT",
+#endif
+#ifdef SQLITE_MUTEX_PTHREADS
+ "MUTEX_PTHREADS",
+#endif
+#ifdef SQLITE_MUTEX_W32
+ "MUTEX_W32",
+#endif
+#ifdef SQLITE_NEED_ERR_NAME
+ "NEED_ERR_NAME",
+#endif
+#ifdef SQLITE_NO_SYNC
+ "NO_SYNC",
+#endif
+#ifdef SQLITE_OMIT_ALTERTABLE
+ "OMIT_ALTERTABLE",
+#endif
+#ifdef SQLITE_OMIT_ANALYZE
+ "OMIT_ANALYZE",
+#endif
+#ifdef SQLITE_OMIT_ATTACH
+ "OMIT_ATTACH",
+#endif
+#ifdef SQLITE_OMIT_AUTHORIZATION
+ "OMIT_AUTHORIZATION",
+#endif
+#ifdef SQLITE_OMIT_AUTOINCREMENT
+ "OMIT_AUTOINCREMENT",
+#endif
+#ifdef SQLITE_OMIT_AUTOINIT
+ "OMIT_AUTOINIT",
+#endif
+#ifdef SQLITE_OMIT_AUTOMATIC_INDEX
+ "OMIT_AUTOMATIC_INDEX",
+#endif
+#ifdef SQLITE_OMIT_AUTORESET
+ "OMIT_AUTORESET",
+#endif
+#ifdef SQLITE_OMIT_AUTOVACUUM
+ "OMIT_AUTOVACUUM",
+#endif
+#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION
+ "OMIT_BETWEEN_OPTIMIZATION",
+#endif
+#ifdef SQLITE_OMIT_BLOB_LITERAL
+ "OMIT_BLOB_LITERAL",
+#endif
+#ifdef SQLITE_OMIT_CAST
+ "OMIT_CAST",
+#endif
+#ifdef SQLITE_OMIT_CHECK
+ "OMIT_CHECK",
+#endif
+#ifdef SQLITE_OMIT_COMPLETE
+ "OMIT_COMPLETE",
+#endif
+#ifdef SQLITE_OMIT_COMPOUND_SELECT
+ "OMIT_COMPOUND_SELECT",
+#endif
+#ifdef SQLITE_OMIT_CONFLICT_CLAUSE
+ "OMIT_CONFLICT_CLAUSE",
+#endif
+#ifdef SQLITE_OMIT_CTE
+ "OMIT_CTE",
+#endif
+#if defined(SQLITE_OMIT_DATETIME_FUNCS) || defined(SQLITE_OMIT_FLOATING_POINT)
+ "OMIT_DATETIME_FUNCS",
+#endif
+#ifdef SQLITE_OMIT_DECLTYPE
+ "OMIT_DECLTYPE",
+#endif
+#ifdef SQLITE_OMIT_DEPRECATED
+ "OMIT_DEPRECATED",
+#endif
+#ifdef SQLITE_OMIT_DESERIALIZE
+ "OMIT_DESERIALIZE",
+#endif
+#ifdef SQLITE_OMIT_DISKIO
+ "OMIT_DISKIO",
+#endif
+#ifdef SQLITE_OMIT_EXPLAIN
+ "OMIT_EXPLAIN",
+#endif
+#ifdef SQLITE_OMIT_FLAG_PRAGMAS
+ "OMIT_FLAG_PRAGMAS",
+#endif
+#ifdef SQLITE_OMIT_FLOATING_POINT
+ "OMIT_FLOATING_POINT",
+#endif
+#ifdef SQLITE_OMIT_FOREIGN_KEY
+ "OMIT_FOREIGN_KEY",
+#endif
+#ifdef SQLITE_OMIT_GET_TABLE
+ "OMIT_GET_TABLE",
+#endif
+#ifdef SQLITE_OMIT_HEX_INTEGER
+ "OMIT_HEX_INTEGER",
+#endif
+#ifdef SQLITE_OMIT_INCRBLOB
+ "OMIT_INCRBLOB",
+#endif
+#ifdef SQLITE_OMIT_INTEGRITY_CHECK
+ "OMIT_INTEGRITY_CHECK",
+#endif
+#ifdef SQLITE_OMIT_INTROSPECTION_PRAGMAS
+ "OMIT_INTROSPECTION_PRAGMAS",
+#endif
+#ifdef SQLITE_OMIT_JSON
+ "OMIT_JSON",
+#endif
+#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION
+ "OMIT_LIKE_OPTIMIZATION",
+#endif
+#ifdef SQLITE_OMIT_LOAD_EXTENSION
+ "OMIT_LOAD_EXTENSION",
+#endif
+#ifdef SQLITE_OMIT_LOCALTIME
+ "OMIT_LOCALTIME",
+#endif
+#ifdef SQLITE_OMIT_LOOKASIDE
+ "OMIT_LOOKASIDE",
+#endif
+#ifdef SQLITE_OMIT_MEMORYDB
+ "OMIT_MEMORYDB",
+#endif
+#ifdef SQLITE_OMIT_OR_OPTIMIZATION
+ "OMIT_OR_OPTIMIZATION",
+#endif
+#ifdef SQLITE_OMIT_PAGER_PRAGMAS
+ "OMIT_PAGER_PRAGMAS",
+#endif
+#ifdef SQLITE_OMIT_PARSER_TRACE
+ "OMIT_PARSER_TRACE",
+#endif
+#ifdef SQLITE_OMIT_POPEN
+ "OMIT_POPEN",
+#endif
+#ifdef SQLITE_OMIT_PRAGMA
+ "OMIT_PRAGMA",
+#endif
+#ifdef SQLITE_OMIT_PROGRESS_CALLBACK
+ "OMIT_PROGRESS_CALLBACK",
+#endif
+#ifdef SQLITE_OMIT_QUICKBALANCE
+ "OMIT_QUICKBALANCE",
+#endif
+#ifdef SQLITE_OMIT_REINDEX
+ "OMIT_REINDEX",
+#endif
+#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS
+ "OMIT_SCHEMA_PRAGMAS",
+#endif
+#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
+ "OMIT_SCHEMA_VERSION_PRAGMAS",
+#endif
+#ifdef SQLITE_OMIT_SEH
+ "OMIT_SEH",
+#endif
+#ifdef SQLITE_OMIT_SHARED_CACHE
+ "OMIT_SHARED_CACHE",
+#endif
+#ifdef SQLITE_OMIT_SHUTDOWN_DIRECTORIES
+ "OMIT_SHUTDOWN_DIRECTORIES",
+#endif
+#ifdef SQLITE_OMIT_SUBQUERY
+ "OMIT_SUBQUERY",
+#endif
+#ifdef SQLITE_OMIT_TCL_VARIABLE
+ "OMIT_TCL_VARIABLE",
+#endif
+#ifdef SQLITE_OMIT_TEMPDB
+ "OMIT_TEMPDB",
+#endif
+#ifdef SQLITE_OMIT_TEST_CONTROL
+ "OMIT_TEST_CONTROL",
+#endif
+#ifdef SQLITE_OMIT_TRACE
+# if SQLITE_OMIT_TRACE != 1
+ "OMIT_TRACE=" CTIMEOPT_VAL(SQLITE_OMIT_TRACE),
+# endif
+#endif
+#ifdef SQLITE_OMIT_TRIGGER
+ "OMIT_TRIGGER",
+#endif
+#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION
+ "OMIT_TRUNCATE_OPTIMIZATION",
+#endif
+#ifdef SQLITE_OMIT_UTF16
+ "OMIT_UTF16",
+#endif
+#ifdef SQLITE_OMIT_VACUUM
+ "OMIT_VACUUM",
+#endif
+#ifdef SQLITE_OMIT_VIEW
+ "OMIT_VIEW",
+#endif
+#ifdef SQLITE_OMIT_VIRTUALTABLE
+ "OMIT_VIRTUALTABLE",
+#endif
+#ifdef SQLITE_OMIT_WAL
+ "OMIT_WAL",
+#endif
+#ifdef SQLITE_OMIT_WSD
+ "OMIT_WSD",
+#endif
+#ifdef SQLITE_OMIT_XFER_OPT
+ "OMIT_XFER_OPT",
+#endif
+#ifdef SQLITE_PERFORMANCE_TRACE
+ "PERFORMANCE_TRACE",
+#endif
+#ifdef SQLITE_POWERSAFE_OVERWRITE
+# if SQLITE_POWERSAFE_OVERWRITE != 1
+ "POWERSAFE_OVERWRITE=" CTIMEOPT_VAL(SQLITE_POWERSAFE_OVERWRITE),
+# endif
+#endif
+#ifdef SQLITE_PREFER_PROXY_LOCKING
+ "PREFER_PROXY_LOCKING",
+#endif
+#ifdef SQLITE_PROXY_DEBUG
+ "PROXY_DEBUG",
+#endif
+#ifdef SQLITE_REVERSE_UNORDERED_SELECTS
+ "REVERSE_UNORDERED_SELECTS",
+#endif
+#ifdef SQLITE_RTREE_INT_ONLY
+ "RTREE_INT_ONLY",
+#endif
+#ifdef SQLITE_SECURE_DELETE
+ "SECURE_DELETE",
+#endif
+#ifdef SQLITE_SMALL_STACK
+ "SMALL_STACK",
+#endif
+#ifdef SQLITE_SORTER_PMASZ
+ "SORTER_PMASZ=" CTIMEOPT_VAL(SQLITE_SORTER_PMASZ),
+#endif
+#ifdef SQLITE_SOUNDEX
+ "SOUNDEX",
+#endif
+#ifdef SQLITE_STAT4_SAMPLES
+ "STAT4_SAMPLES=" CTIMEOPT_VAL(SQLITE_STAT4_SAMPLES),
+#endif
+#ifdef SQLITE_STMTJRNL_SPILL
+ "STMTJRNL_SPILL=" CTIMEOPT_VAL(SQLITE_STMTJRNL_SPILL),
+#endif
+#ifdef SQLITE_SUBSTR_COMPATIBILITY
+ "SUBSTR_COMPATIBILITY",
+#endif
+#if (!defined(SQLITE_WIN32_MALLOC) \
+ && !defined(SQLITE_ZERO_MALLOC) \
+ && !defined(SQLITE_MEMDEBUG) \
+ ) || defined(SQLITE_SYSTEM_MALLOC)
+ "SYSTEM_MALLOC",
+#endif
+#ifdef SQLITE_TCL
+ "TCL",
+#endif
+#ifdef SQLITE_TEMP_STORE
+ "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE),
+#endif
+#ifdef SQLITE_TEST
+ "TEST",
+#endif
+#if defined(SQLITE_THREADSAFE)
+ "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE),
+#elif defined(THREADSAFE)
+ "THREADSAFE=" CTIMEOPT_VAL(THREADSAFE),
+#else
+ "THREADSAFE=1",
+#endif
+#ifdef SQLITE_UNLINK_AFTER_CLOSE
+ "UNLINK_AFTER_CLOSE",
+#endif
+#ifdef SQLITE_UNTESTABLE
+ "UNTESTABLE",
+#endif
+#ifdef SQLITE_USER_AUTHENTICATION
+ "USER_AUTHENTICATION",
+#endif
+#ifdef SQLITE_USE_ALLOCA
+ "USE_ALLOCA",
+#endif
+#ifdef SQLITE_USE_FCNTL_TRACE
+ "USE_FCNTL_TRACE",
+#endif
+#ifdef SQLITE_USE_URI
+ "USE_URI",
+#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ "VDBE_COVERAGE",
+#endif
+#ifdef SQLITE_WIN32_MALLOC
+ "WIN32_MALLOC",
+#endif
+#ifdef SQLITE_ZERO_MALLOC
+ "ZERO_MALLOC",
+#endif
+
+} ;
+
+SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt){
+ *pnOpt = sizeof(sqlite3azCompileOpt) / sizeof(sqlite3azCompileOpt[0]);
+ return (const char**)sqlite3azCompileOpt;
+}
+
+#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
+
+/************** End of ctime.c ***********************************************/
/************** Begin file global.c ******************************************/
/*
** 2008 June 13
@@ -19517,7 +22580,7 @@ SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt);
/* #include "sqliteInt.h" */
/* An array to map all upper-case characters into their corresponding
-** lower-case character.
+** lower-case character.
**
** SQLite only considers US-ASCII (or EBCDIC) characters. We do not
** handle case conversions for the UTF character set since the tables
@@ -19539,7 +22602,7 @@ SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = {
198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,
234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,
- 252,253,254,255
+ 252,253,254,255,
#endif
#ifdef SQLITE_EBCDIC
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */
@@ -19559,7 +22622,35 @@ SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = {
224,225,162,163,164,165,166,167,168,169,234,235,236,237,238,239, /* Ex */
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* Fx */
#endif
+/* All of the upper-to-lower conversion data is above. The following
+** 18 integers are completely unrelated. They are appended to the
+** sqlite3UpperToLower[] array to avoid UBSAN warnings. Here's what is
+** going on:
+**
+** The SQL comparison operators (<>, =, >, <=, <, and >=) are implemented
+** by invoking sqlite3MemCompare(A,B) which compares values A and B and
+** returns negative, zero, or positive if A is less then, equal to, or
+** greater than B, respectively. Then the true false results is found by
+** consulting sqlite3aLTb[opcode], sqlite3aEQb[opcode], or
+** sqlite3aGTb[opcode] depending on whether the result of compare(A,B)
+** is negative, zero, or positive, where opcode is the specific opcode.
+** The only works because the comparison opcodes are consecutive and in
+** this order: NE EQ GT LE LT GE. Various assert()s throughout the code
+** ensure that is the case.
+**
+** These elements must be appended to another array. Otherwise the
+** index (here shown as [256-OP_Ne]) would be out-of-bounds and thus
+** be undefined behavior. That's goofy, but the C-standards people thought
+** it was a good idea, so here we are.
+*/
+/* NE EQ GT LE LT GE */
+ 1, 0, 0, 1, 1, 0, /* aLTb[]: Use when compare(A,B) less than zero */
+ 0, 1, 0, 1, 0, 1, /* aEQb[]: Use when compare(A,B) equals zero */
+ 1, 0, 1, 0, 0, 1 /* aGTb[]: Use when compare(A,B) greater than zero*/
};
+SQLITE_PRIVATE const unsigned char *sqlite3aLTb = &sqlite3UpperToLower[256-OP_Ne];
+SQLITE_PRIVATE const unsigned char *sqlite3aEQb = &sqlite3UpperToLower[256+6-OP_Ne];
+SQLITE_PRIVATE const unsigned char *sqlite3aGTb = &sqlite3UpperToLower[256+12-OP_Ne];
/*
** The following 256 byte lookup table is used to support SQLites built-in
@@ -19571,7 +22662,7 @@ SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = {
** isalnum() 0x06
** isxdigit() 0x08
** toupper() 0x20
-** SQLite identifier character 0x40
+** SQLite identifier character 0x40 $, _, or non-ascii
** Quote character 0x80
**
** Bit 0x20 is set if the mapped character requires translation to upper
@@ -19584,12 +22675,11 @@ SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = {
** The equivalent of tolower() is implemented using the sqlite3UpperToLower[]
** array. tolower() is used more often than toupper() by SQLite.
**
-** Bit 0x40 is set if the character is non-alphanumeric and can be used in an
+** Bit 0x40 is set if the character is non-alphanumeric and can be used in an
** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any
** non-ASCII UTF character. Hence the test for whether or not a character is
** part of an identifier is 0x46.
*/
-#ifdef SQLITE_ASCII
SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */
@@ -19627,7 +22717,6 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */
};
-#endif
/* EVIDENCE-OF: R-02982-34736 In order to maintain full backwards
** compatibility for legacy applications, the URI filename capability is
@@ -19639,24 +22728,24 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally
** disabled. The default value may be changed by compiling with the
** SQLITE_USE_URI symbol defined.
-**
-** URI filenames are enabled by default if SQLITE_HAS_CODEC is
-** enabled.
*/
#ifndef SQLITE_USE_URI
-# ifdef SQLITE_HAS_CODEC
-# define SQLITE_USE_URI 1
-# else
-# define SQLITE_USE_URI 0
-# endif
+# define SQLITE_USE_URI 0
#endif
/* EVIDENCE-OF: R-38720-18127 The default setting is determined by the
** SQLITE_ALLOW_COVERING_INDEX_SCAN compile-time option, or is "on" if
** that compile-time option is omitted.
*/
-#ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN
+#if !defined(SQLITE_ALLOW_COVERING_INDEX_SCAN)
# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1
+#else
+# if !SQLITE_ALLOW_COVERING_INDEX_SCAN
+# error "Compile-time disabling of covering index scan using the\
+ -DSQLITE_ALLOW_COVERING_INDEX_SCAN=0 option is deprecated.\
+ Contact SQLite developers if this is a problem for you, and\
+ delete this #error macro to continue with your build."
+# endif
#endif
/* The minimum PMA size is set to this value multiplied by the database
@@ -19674,7 +22763,7 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this
** setting.)
*/
-#ifndef SQLITE_STMTJRNL_SPILL
+#ifndef SQLITE_STMTJRNL_SPILL
# define SQLITE_STMTJRNL_SPILL (64*1024)
#endif
@@ -19685,12 +22774,28 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
** changed as start-time using sqlite3_config(SQLITE_CONFIG_LOOKASIDE)
** or at run-time for an individual database connection using
** sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE);
+**
+** With the two-size-lookaside enhancement, less lookaside is required.
+** The default configuration of 1200,40 actually provides 30 1200-byte slots
+** and 93 128-byte slots, which is more lookaside than is available
+** using the older 1200,100 configuration without two-size-lookaside.
*/
#ifndef SQLITE_DEFAULT_LOOKASIDE
-# define SQLITE_DEFAULT_LOOKASIDE 1200,100
+# ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+# define SQLITE_DEFAULT_LOOKASIDE 1200,100 /* 120KB of memory */
+# else
+# define SQLITE_DEFAULT_LOOKASIDE 1200,40 /* 48KB of memory */
+# endif
#endif
+/* The default maximum size of an in-memory database created using
+** sqlite3_deserialize()
+*/
+#ifndef SQLITE_MEMDB_DEFAULT_MAXSIZE
+# define SQLITE_MEMDB_DEFAULT_MAXSIZE 1073741824
+#endif
+
/*
** The following singleton contains the global configuration for
** the SQLite library.
@@ -19702,6 +22807,11 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
SQLITE_USE_URI, /* bOpenUri */
SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
0, /* bSmallMalloc */
+ 1, /* bExtraSchemaChecks */
+ sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */
+#ifdef SQLITE_DEBUG
+ 0, /* bJsonSelfcheck */
+#endif
0x7ffffffe, /* mxStrlen */
0, /* neverCorrupt */
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
@@ -19738,13 +22848,20 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* xVdbeBranch */
0, /* pVbeBranchArg */
#endif
+#ifndef SQLITE_OMIT_DESERIALIZE
+ SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */
+#endif
#ifndef SQLITE_UNTESTABLE
0, /* xTestCallback */
#endif
0, /* bLocaltimeFault */
- 0, /* bInternalFunctions */
+ 0, /* xAltLocaltime */
0x7ffffffe, /* iOnceResetThreshold */
- SQLITE_DEFAULT_SORTERREF_SIZE /* szSorterRef */
+ SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */
+ 0, /* iPrngSeed */
+#ifdef SQLITE_DEBUG
+ {0,0,0,0,0,0}, /* aTune */
+#endif
};
/*
@@ -19754,13 +22871,17 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
*/
SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG)
/*
-** Constant tokens for values 0 and 1.
+** Counter used for coverage testing. Does not come into play for
+** release builds.
+**
+** Access to this global variable is not mutex protected. This might
+** result in TSAN warnings. But as the variable does not exist in
+** release builds, that should not be a concern.
*/
-SQLITE_PRIVATE const Token sqlite3IntTokens[] = {
- { "0", 1 },
- { "1", 1 }
-};
+SQLITE_PRIVATE unsigned int sqlite3CoverageCounter;
+#endif /* SQLITE_COVERAGE_TEST || SQLITE_DEBUG */
#ifdef VDBE_PROFILE
/*
@@ -19792,12 +22913,18 @@ SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt = 0;
SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000;
#endif
+/*
+** Tracing flags set by SQLITE_TESTCTRL_TRACEFLAGS.
+*/
+SQLITE_PRIVATE u32 sqlite3TreeTrace = 0;
+SQLITE_PRIVATE u32 sqlite3WhereTrace = 0;
+
/* #include "opcodes.h" */
/*
** Properties of opcodes. The OPFLG_INITIALIZER macro is
** created by mkopcodeh.awk during compilation. Data is obtained
** from the comments following the "case OP_xxxx:" statements in
-** the vdbe.c file.
+** the vdbe.c file.
*/
SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER;
@@ -19806,6 +22933,36 @@ SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER;
*/
SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY";
+/*
+** Standard typenames. These names must match the COLTYPE_* definitions.
+** Adjust the SQLITE_N_STDTYPE value if adding or removing entries.
+**
+** sqlite3StdType[] The actual names of the datatypes.
+**
+** sqlite3StdTypeLen[] The length (in bytes) of each entry
+** in sqlite3StdType[].
+**
+** sqlite3StdTypeAffinity[] The affinity associated with each entry
+** in sqlite3StdType[].
+*/
+SQLITE_PRIVATE const unsigned char sqlite3StdTypeLen[] = { 3, 4, 3, 7, 4, 4 };
+SQLITE_PRIVATE const char sqlite3StdTypeAffinity[] = {
+ SQLITE_AFF_NUMERIC,
+ SQLITE_AFF_BLOB,
+ SQLITE_AFF_INTEGER,
+ SQLITE_AFF_INTEGER,
+ SQLITE_AFF_REAL,
+ SQLITE_AFF_TEXT
+};
+SQLITE_PRIVATE const char *sqlite3StdType[] = {
+ "ANY",
+ "BLOB",
+ "INT",
+ "INTEGER",
+ "REAL",
+ "TEXT"
+};
+
/************** End of global.c **********************************************/
/************** Begin file status.c ******************************************/
/*
@@ -19859,7 +23016,8 @@ SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY";
** "explain" P4 display logic is enabled.
*/
#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
- || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+ || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \
+ || defined(SQLITE_ENABLE_BYTECODE_VTAB)
# define VDBE_DISPLAY_P4 1
#else
# define VDBE_DISPLAY_P4 0
@@ -19883,6 +23041,9 @@ typedef struct VdbeSorter VdbeSorter;
/* Elements of the linked list at Vdbe.pAuxData */
typedef struct AuxData AuxData;
+/* A cache of large TEXT or BLOB values in a VdbeCursor */
+typedef struct VdbeTxtBlbCache VdbeTxtBlbCache;
+
/* Types of VDBE cursors */
#define CURTYPE_BTREE 0
#define CURTYPE_SORTER 1
@@ -19902,7 +23063,7 @@ typedef struct AuxData AuxData;
typedef struct VdbeCursor VdbeCursor;
struct VdbeCursor {
u8 eCurType; /* One of the CURTYPE_* values above */
- i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */
+ i8 iDb; /* Index of cursor database in db->aDb[] */
u8 nullRow; /* True if pointing to a row with no data */
u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
u8 isTable; /* True for rowid tables. False for indexes */
@@ -19913,10 +23074,14 @@ struct VdbeCursor {
Bool isEphemeral:1; /* True for an ephemeral table */
Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */
Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */
- Bool seekHit:1; /* See the OP_SeekHit and OP_IfNoHope opcodes */
- Btree *pBtx; /* Separate file holding temporary table */
+ Bool noReuse:1; /* OpenEphemeral may not reuse this cursor */
+ Bool colCache:1; /* pCache pointer is initialized and non-NULL */
+ u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */
+ union { /* pBtx for isEphermeral. pAltMap otherwise */
+ Btree *pBtx; /* Separate file holding temporary table */
+ u32 *aAltMap; /* Mapping from table to index column numbers */
+ } ub;
i64 seqCount; /* Sequence counter */
- int *aAltMap; /* Mapping from table to index column numbers */
/* Cached OP_Column parse information is only valid if cacheStatus matches
** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of
@@ -19951,6 +23116,7 @@ struct VdbeCursor {
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
u64 maskUsed; /* Mask of columns used by this cursor */
#endif
+ VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */
/* 2*nField extra array elements allocated for aType[], beyond the one
** static element declared in the structure. nField total array slots for
@@ -19958,6 +23124,10 @@ struct VdbeCursor {
u32 aType[1]; /* Type values record decode. MUST BE LAST */
};
+/* Return true if P is a null-only cursor
+*/
+#define IsNullCursor(P) \
+ ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0)
/*
** A value for VdbeCursor.cacheStatus that means the cache is always invalid.
@@ -19965,10 +23135,24 @@ struct VdbeCursor {
#define CACHE_STALE 0
/*
+** Large TEXT or BLOB values can be slow to load, so we want to avoid
+** loading them more than once. For that reason, large TEXT and BLOB values
+** can be stored in a cache defined by this object, and attached to the
+** VdbeCursor using the pCache field.
+*/
+struct VdbeTxtBlbCache {
+ char *pCValue; /* A RCStr buffer to hold the value */
+ i64 iOffset; /* File offset of the row being cached */
+ int iCol; /* Column for which the cache is valid */
+ u32 cacheStatus; /* Vdbe.cacheCtr value */
+ u32 colCacheCtr; /* Column cache counter */
+};
+
+/*
** When a sub-program is executed (OP_Program), a structure of this type
** is allocated to store the current value of the program counter, as
** well as the current memory cell array and various other frame specific
-** values stored in the Vdbe struct. When the sub-program is finished,
+** values stored in the Vdbe struct. When the sub-program is finished,
** these values are copied back to the Vdbe from the VdbeFrame structure,
** restoring the state of the VM to as it was before the sub-program
** began executing.
@@ -19990,7 +23174,6 @@ struct VdbeFrame {
Vdbe *v; /* VM this frame belongs to */
VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */
Op *aOp; /* Program instructions for parent frame */
- i64 *anExec; /* Event counters from parent frame */
Mem *aMem; /* Array of memory cells for parent frame */
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
u8 *aOnce; /* Bitmask used by OP_Once */
@@ -20006,8 +23189,8 @@ struct VdbeFrame {
int nMem; /* Number of entries in aMem */
int nChildMem; /* Number of memory cells for child frame */
int nChildCsr; /* Number of cursors for child frame */
- int nChange; /* Statement changes (Vdbe.nChange) */
- int nDbChange; /* Value of db->nChange */
+ i64 nChange; /* Statement changes (Vdbe.nChange) */
+ i64 nDbChange; /* Value of db->nChange */
};
/* Magic number for sanity checking on VdbeFrame objects */
@@ -20032,16 +23215,16 @@ struct sqlite3_value {
const char *zPType; /* Pointer type when MEM_Term|MEM_Subtype|MEM_Null */
FuncDef *pDef; /* Used only when flags==MEM_Agg */
} u;
+ char *z; /* String or BLOB value */
+ int n; /* Number of characters in string value, excluding '\0' */
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
u8 eSubtype; /* Subtype for this value */
- int n; /* Number of characters in string value, excluding '\0' */
- char *z; /* String or BLOB value */
/* ShallowCopy only needs to copy the information above */
- char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */
+ sqlite3 *db; /* The associated database connection */
int szMalloc; /* Size of the zMalloc allocation */
u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */
- sqlite3 *db; /* The associated database connection */
+ char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */
void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */
#ifdef SQLITE_DEBUG
Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */
@@ -20053,11 +23236,43 @@ struct sqlite3_value {
** Size of struct Mem not including the Mem.zMalloc member or anything that
** follows.
*/
-#define MEMCELLSIZE offsetof(Mem,zMalloc)
+#define MEMCELLSIZE offsetof(Mem,db)
-/* One or more of the following flags are set to indicate the validOK
+/* One or more of the following flags are set to indicate the
** representations of the value stored in the Mem struct.
**
+** * MEM_Null An SQL NULL value
+**
+** * MEM_Null|MEM_Zero An SQL NULL with the virtual table
+** UPDATE no-change flag set
+**
+** * MEM_Null|MEM_Term| An SQL NULL, but also contains a
+** MEM_Subtype pointer accessible using
+** sqlite3_value_pointer().
+**
+** * MEM_Null|MEM_Cleared Special SQL NULL that compares non-equal
+** to other NULLs even using the IS operator.
+**
+** * MEM_Str A string, stored in Mem.z with
+** length Mem.n. Zero-terminated if
+** MEM_Term is set. This flag is
+** incompatible with MEM_Blob and
+** MEM_Null, but can appear with MEM_Int,
+** MEM_Real, and MEM_IntReal.
+**
+** * MEM_Blob A blob, stored in Mem.z length Mem.n.
+** Incompatible with MEM_Str, MEM_Null,
+** MEM_Int, MEM_Real, and MEM_IntReal.
+**
+** * MEM_Blob|MEM_Zero A blob in Mem.z of length Mem.n plus
+** MEM.u.i extra 0x00 bytes at the end.
+**
+** * MEM_Int Integer stored in Mem.u.i.
+**
+** * MEM_Real Real stored in Mem.u.r.
+**
+** * MEM_IntReal Real stored as an integer in Mem.u.i.
+**
** If the MEM_Null flag is set, then the value is an SQL NULL value.
** For a pointer type created using sqlite3_bind_pointer() or
** sqlite3_result_pointer() the MEM_Term and MEM_Subtype flags are also set.
@@ -20065,38 +23280,35 @@ struct sqlite3_value {
** If the MEM_Str flag is set then Mem.z points at a string representation.
** Usually this is encoded in the same unicode encoding as the main
** database (see below for exceptions). If the MEM_Term flag is also
-** set, then the string is nul terminated. The MEM_Int and MEM_Real
+** set, then the string is nul terminated. The MEM_Int and MEM_Real
** flags may coexist with the MEM_Str flag.
*/
+#define MEM_Undefined 0x0000 /* Value is undefined */
#define MEM_Null 0x0001 /* Value is NULL (or a pointer) */
#define MEM_Str 0x0002 /* Value is a string */
#define MEM_Int 0x0004 /* Value is an integer */
#define MEM_Real 0x0008 /* Value is a real number */
#define MEM_Blob 0x0010 /* Value is a BLOB */
-#define MEM_AffMask 0x001f /* Mask of affinity bits */
-/* Available 0x0020 */
-/* Available 0x0040 */
-#define MEM_Undefined 0x0080 /* Value is undefined */
-#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
-#define MEM_TypeMask 0xc1ff /* Mask of type bits */
-
+#define MEM_IntReal 0x0020 /* MEM_Int that stringifies like MEM_Real */
+#define MEM_AffMask 0x003f /* Mask of affinity bits */
-/* Whenever Mem contains a valid string or blob representation, one of
-** the following flags must be set to determine the memory management
-** policy for Mem.z. The MEM_Term flag tells us whether or not the
-** string is \000 or \u0000 terminated
+/* Extra bits that modify the meanings of the core datatypes above
*/
+#define MEM_FromBind 0x0040 /* Value originates from sqlite3_bind() */
+ /* 0x0080 // Available */
+#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
#define MEM_Term 0x0200 /* String in Mem.z is zero terminated */
-#define MEM_Dyn 0x0400 /* Need to call Mem.xDel() on Mem.z */
-#define MEM_Static 0x0800 /* Mem.z points to a static string */
-#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */
-#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */
-#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */
-#define MEM_Subtype 0x8000 /* Mem.eSubtype is valid */
-#ifdef SQLITE_OMIT_INCRBLOB
- #undef MEM_Zero
- #define MEM_Zero 0x0000
-#endif
+#define MEM_Zero 0x0400 /* Mem.i contains count of 0s appended to blob */
+#define MEM_Subtype 0x0800 /* Mem.eSubtype is valid */
+#define MEM_TypeMask 0x0dbf /* Mask of type bits */
+
+/* Bits that determine the storage for Mem.z for a string or blob or
+** aggregate accumulator.
+*/
+#define MEM_Dyn 0x1000 /* Need to call Mem.xDel() on Mem.z */
+#define MEM_Static 0x2000 /* Mem.z points to a static string */
+#define MEM_Ephem 0x4000 /* Mem.z points to an ephemeral string */
+#define MEM_Agg 0x8000 /* Mem.z points to an agg function context */
/* Return TRUE if Mem X contains dynamically allocated content - anything
** that needs to be deallocated to avoid a leak.
@@ -20111,15 +23323,26 @@ struct sqlite3_value {
((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f)
/*
-** Return true if a memory cell is not marked as invalid. This macro
+** True if Mem X is a NULL-nochng type.
+*/
+#define MemNullNochng(X) \
+ (((X)->flags&MEM_TypeMask)==(MEM_Null|MEM_Zero) \
+ && (X)->n==0 && (X)->u.nZero==0)
+
+/*
+** Return true if a memory cell has been initialized and is valid.
** is for use inside assert() statements only.
+**
+** A Memory cell is initialized if at least one of the
+** MEM_Null, MEM_Str, MEM_Int, MEM_Real, MEM_Blob, or MEM_IntReal bits
+** is set. It is "undefined" if all those bits are zero.
*/
#ifdef SQLITE_DEBUG
-#define memIsValid(M) ((M)->flags & MEM_Undefined)==0
+#define memIsValid(M) ((M)->flags & MEM_AffMask)!=0
#endif
/*
-** Each auxiliary data pointer stored by a user defined function
+** Each auxiliary data pointer stored by a user defined function
** implementation calling sqlite3_set_auxdata() is stored in an instance
** of this structure. All such structures associated with a single VM
** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed
@@ -20153,6 +23376,7 @@ struct sqlite3_context {
Vdbe *pVdbe; /* The VM that owns this context */
int iOp; /* Instruction number of OP_Function */
int isError; /* Error code returned by the function. */
+ u8 enc; /* Encoding to use for results */
u8 skipFlag; /* Skip accumulator loading if true */
u8 argc; /* Number of arguments */
sqlite3_value *argv[1]; /* Argument set */
@@ -20163,9 +23387,21 @@ struct sqlite3_context {
*/
typedef unsigned bft; /* Bit Field Type */
+/* The ScanStatus object holds a single value for the
+** sqlite3_stmt_scanstatus() interface.
+**
+** aAddrRange[]:
+** This array is used by ScanStatus elements associated with EQP
+** notes that make an SQLITE_SCANSTAT_NCYCLE value available. It is
+** an array of up to 3 ranges of VM addresses for which the Vdbe.anCycle[]
+** values should be summed to calculate the NCYCLE value. Each pair of
+** integer addresses is a start and end address (both inclusive) for a range
+** instructions. A start value of 0 indicates an empty range.
+*/
typedef struct ScanStatus ScanStatus;
struct ScanStatus {
int addrExplain; /* OP_Explain for loop */
+ int aAddrRange[6];
int addrLoop; /* Address of "loops" counter */
int addrVisit; /* Address of "rows visited" counter */
int iSelectID; /* The "Select-ID" for this loop */
@@ -20173,6 +23409,19 @@ struct ScanStatus {
char *zName; /* Name of table or index */
};
+/* The DblquoteStr object holds the text of a double-quoted
+** string for a prepared statement. A linked list of these objects
+** is constructed during statement parsing and is held on Vdbe.pDblStr.
+** When computing a normalized SQL statement for an SQL statement, that
+** list is consulted for each double-quoted identifier to see if the
+** identifier should really be a string literal.
+*/
+typedef struct DblquoteStr DblquoteStr;
+struct DblquoteStr {
+ DblquoteStr *pNextStr; /* Next string literal in the list */
+ char z[8]; /* Dequoted value for the string */
+};
+
/*
** An instance of the virtual machine. This structure contains the complete
** state of the virtual machine.
@@ -20182,60 +23431,62 @@ struct ScanStatus {
*/
struct Vdbe {
sqlite3 *db; /* The database connection that owns this statement */
- Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
+ Vdbe **ppVPrev,*pVNext; /* Linked list of VDBEs with the same Vdbe.db */
Parse *pParse; /* Parsing context used to create this Vdbe */
ynVar nVar; /* Number of entries in aVar[] */
- u32 magic; /* Magic number for sanity checking */
int nMem; /* Number of memory locations currently allocated */
int nCursor; /* Number of slots in apCsr[] */
u32 cacheCtr; /* VdbeCursor row cache generation counter */
int pc; /* The program counter */
int rc; /* Value to return */
- int nChange; /* Number of db changes made since last reset */
- int iStatement; /* Statement number (or 0 if has not opened stmt) */
+ i64 nChange; /* Number of db changes made since last reset */
+ int iStatement; /* Statement number (or 0 if has no opened stmt) */
i64 iCurrentTime; /* Value of julianday('now') for this statement */
i64 nFkConstraint; /* Number of imm. FK constraints this VM */
i64 nStmtDefCons; /* Number of def. constraints when stmt started */
i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */
+ Mem *aMem; /* The memory locations */
+ Mem **apArg; /* Arguments to currently executing user function */
+ VdbeCursor **apCsr; /* One element of this array for each open cursor */
+ Mem *aVar; /* Values for the OP_Variable opcode. */
/* When allocating a new Vdbe object, all of the fields below should be
** initialized to zero or NULL */
Op *aOp; /* Space to hold the virtual machine's program */
- Mem *aMem; /* The memory locations */
- Mem **apArg; /* Arguments to currently executing user function */
+ int nOp; /* Number of instructions in the program */
+ int nOpAlloc; /* Slots allocated for aOp[] */
Mem *aColName; /* Column names to return */
- Mem *pResultSet; /* Pointer to an array of results */
+ Mem *pResultRow; /* Current output row */
char *zErrMsg; /* Error message written here */
- VdbeCursor **apCsr; /* One element of this array for each open cursor */
- Mem *aVar; /* Values for the OP_Variable opcode. */
VList *pVList; /* Name of variables */
#ifndef SQLITE_OMIT_TRACE
i64 startTime; /* Time when query started - used for profiling */
#endif
- int nOp; /* Number of instructions in the program */
#ifdef SQLITE_DEBUG
int rcApp; /* errcode set by sqlite3_result_error_code() */
u32 nWrite; /* Number of write operations that have occurred */
#endif
u16 nResColumn; /* Number of columns in one row of the result set */
+ u16 nResAlloc; /* Column slots allocated to aColName[] */
u8 errorAction; /* Recovery action to do in case of an error */
u8 minWriteFileFormat; /* Minimum file format for writable database files */
u8 prepFlags; /* SQLITE_PREPARE_* flags */
+ u8 eVdbeState; /* On of the VDBE_*_STATE values */
bft expired:2; /* 1: recompile VM immediately 2: when convenient */
- bft explain:2; /* True if EXPLAIN present on SQL command */
- bft doingRerun:1; /* True if rerunning after an auto-reprepare */
+ bft explain:2; /* 0: normal, 1: EXPLAIN, 2: EXPLAIN QUERY PLAN */
bft changeCntOn:1; /* True to update the change-counter */
- bft runOnlyOnce:1; /* Automatically expire on reset */
bft usesStmtJournal:1; /* True if uses a statement journal */
bft readOnly:1; /* True for statements that do not write */
bft bIsReader:1; /* True for statements that read */
+ bft haveEqpOps:1; /* Bytecode supports EXPLAIN QUERY PLAN */
yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
- u32 aCounter[7]; /* Counters used by sqlite3_stmt_status() */
+ u32 aCounter[9]; /* Counters used by sqlite3_stmt_status() */
char *zSql; /* Text of the SQL statement that generated this */
#ifdef SQLITE_ENABLE_NORMALIZE
char *zNormSql; /* Normalization of the associated SQL statement */
+ DblquoteStr *pDblStr; /* List of double-quoted string literals */
#endif
void *pFree; /* Free this when deleting the vdbe */
VdbeFrame *pFrame; /* Parent frame */
@@ -20245,23 +23496,21 @@ struct Vdbe {
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
AuxData *pAuxData; /* Linked list of auxdata allocations */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- i64 *anExec; /* Number of times each op has been executed */
int nScan; /* Entries in aScan[] */
ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */
#endif
};
/*
-** The following are allowed values for Vdbe.magic
+** The following are allowed values for Vdbe.eVdbeState
*/
-#define VDBE_MAGIC_INIT 0x16bceaa5 /* Building a VDBE program */
-#define VDBE_MAGIC_RUN 0x2df20da3 /* VDBE is ready to execute */
-#define VDBE_MAGIC_HALT 0x319c2973 /* VDBE has completed execution */
-#define VDBE_MAGIC_RESET 0x48fa9f76 /* Reset and ready to run again */
-#define VDBE_MAGIC_DEAD 0x5606c3c8 /* The VDBE has been deallocated */
+#define VDBE_INIT_STATE 0 /* Prepared statement under construction */
+#define VDBE_READY_STATE 1 /* Ready to run but not yet started */
+#define VDBE_RUN_STATE 2 /* Run in progress */
+#define VDBE_HALT_STATE 3 /* Finished. Need reset() or finalize() */
/*
-** Structure used to store the context required by the
+** Structure used to store the context required by the
** sqlite3_preupdate_*() API functions.
*/
struct PreUpdate {
@@ -20273,33 +23522,72 @@ struct PreUpdate {
UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */
UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */
int iNewReg; /* Register for new.* values */
+ int iBlobWrite; /* Value returned by preupdate_blobwrite() */
i64 iKey1; /* First key value passed to hook */
i64 iKey2; /* Second key value passed to hook */
Mem *aNew; /* Array of new.* values */
- Table *pTab; /* Schema object being upated */
+ Table *pTab; /* Schema object being updated */
Index *pPk; /* PK index if pTab is WITHOUT ROWID */
};
/*
+** An instance of this object is used to pass an vector of values into
+** OP_VFilter, the xFilter method of a virtual table. The vector is the
+** set of values on the right-hand side of an IN constraint.
+**
+** The value as passed into xFilter is an sqlite3_value with a "pointer"
+** type, such as is generated by sqlite3_result_pointer() and read by
+** sqlite3_value_pointer. Such values have MEM_Term|MEM_Subtype|MEM_Null
+** and a subtype of 'p'. The sqlite3_vtab_in_first() and _next() interfaces
+** know how to use this object to step through all the values in the
+** right operand of the IN constraint.
+*/
+typedef struct ValueList ValueList;
+struct ValueList {
+ BtCursor *pCsr; /* An ephemeral table holding all values */
+ sqlite3_value *pOut; /* Register to hold each decoded output value */
+};
+
+/* Size of content associated with serial types that fit into a
+** single-byte varint.
+*/
+#ifndef SQLITE_AMALGAMATION
+SQLITE_PRIVATE const u8 sqlite3SmallTypeSizes[];
+#endif
+
+/*
** Function prototypes
*/
SQLITE_PRIVATE void sqlite3VdbeError(Vdbe*, const char *, ...);
SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
+SQLITE_PRIVATE void sqlite3VdbeFreeCursorNN(Vdbe*,VdbeCursor*);
void sqliteVdbePopStack(Vdbe*,int);
-SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
+SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeHandleMovedCursor(VdbeCursor *p);
+SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor*);
SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*);
SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32);
SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8);
-SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int, u32*);
-SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32);
-SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
+#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT
+SQLITE_PRIVATE u64 sqlite3FloatSwap(u64 in);
+# define swapMixedEndianFloat(X) X = sqlite3FloatSwap(X)
+#else
+# define swapMixedEndianFloat(X)
+#endif
+SQLITE_PRIVATE void sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3*, AuxData**, int, int);
int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*);
SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*);
SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*);
-#ifndef SQLITE_OMIT_EXPLAIN
+#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB)
+SQLITE_PRIVATE int sqlite3VdbeNextOpcode(Vdbe*,Mem*,int,int*,int*,Op**);
+SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3*,Op*);
+#endif
+#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
+SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(sqlite3*,const Op*,const char*);
+#endif
+#if !defined(SQLITE_OMIT_EXPLAIN)
SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*);
#endif
SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*);
@@ -20309,7 +23597,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem*, const Mem*);
SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int);
SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*);
-SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*));
+SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(*)(void*));
SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64);
#ifdef SQLITE_OMIT_FLOATING_POINT
# define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64
@@ -20319,28 +23607,36 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double);
SQLITE_PRIVATE void sqlite3VdbeMemSetPointer(Mem*, void*, const char*, void(*)(void*));
SQLITE_PRIVATE void sqlite3VdbeMemInit(Mem*,sqlite3*,u16);
SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem*);
+#ifndef SQLITE_OMIT_INCRBLOB
SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem*,int);
+#else
+SQLITE_PRIVATE int sqlite3VdbeMemSetZeroBlob(Mem*,int);
+#endif
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3VdbeMemIsRowSet(const Mem*);
#endif
SQLITE_PRIVATE int sqlite3VdbeMemSetRowSet(Mem*);
+SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, u8, u8);
-SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem*);
+SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double);
+SQLITE_PRIVATE i64 sqlite3VdbeIntValue(const Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem*);
SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem*);
SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem*, int ifNull);
SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem*);
-SQLITE_PRIVATE void sqlite3VdbeMemCast(Mem*,u8,u8);
+SQLITE_PRIVATE int sqlite3VdbeMemCast(Mem*,u8,u8);
SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*);
+SQLITE_PRIVATE int sqlite3VdbeMemFromBtreeZeroOffset(BtCursor*,u32,Mem*);
SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p);
+SQLITE_PRIVATE void sqlite3VdbeMemReleaseMalloc(Mem*p);
SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
#ifndef SQLITE_OMIT_WINDOWFUNC
SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*);
#endif
-#ifndef SQLITE_OMIT_EXPLAIN
+#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB)
SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
#endif
SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
@@ -20353,7 +23649,8 @@ SQLITE_PRIVATE void sqlite3VdbeFrameMemDel(void*); /* Destructor on Mem */
SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); /* Actually deletes the Frame */
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *);
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
-SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int);
+SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
+ Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int,int);
#endif
SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p);
@@ -20366,6 +23663,8 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *);
SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *);
SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
+SQLITE_PRIVATE void sqlite3VdbeValueListFree(void*);
+
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*);
SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe*);
@@ -20374,7 +23673,7 @@ SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe*);
# define sqlite3VdbeAssertAbortable(V)
#endif
-#if !defined(SQLITE_OMIT_SHARED_CACHE)
+#if !defined(SQLITE_OMIT_SHARED_CACHE)
SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*);
#else
# define sqlite3VdbeEnter(X)
@@ -20399,7 +23698,7 @@ SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *, int);
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe*);
-SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf);
+SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr);
#endif
#ifndef SQLITE_OMIT_UTF16
SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem*, u8);
@@ -20591,6 +23890,10 @@ static u32 countLookasideSlots(LookasideSlot *p){
SQLITE_PRIVATE int sqlite3LookasideUsed(sqlite3 *db, int *pHighwater){
u32 nInit = countLookasideSlots(db->lookaside.pInit);
u32 nFree = countLookasideSlots(db->lookaside.pFree);
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ nInit += countLookasideSlots(db->lookaside.pSmallInit);
+ nFree += countLookasideSlots(db->lookaside.pSmallFree);
+#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit;
return db->lookaside.nSlot - (nInit+nFree);
}
@@ -20623,6 +23926,15 @@ SQLITE_API int sqlite3_db_status(
db->lookaside.pInit = db->lookaside.pFree;
db->lookaside.pFree = 0;
}
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ p = db->lookaside.pSmallFree;
+ if( p ){
+ while( p->pNext ) p = p->pNext;
+ p->pNext = db->lookaside.pSmallInit;
+ db->lookaside.pSmallInit = db->lookaside.pSmallFree;
+ db->lookaside.pSmallFree = 0;
+ }
+#endif
}
break;
}
@@ -20643,7 +23955,7 @@ SQLITE_API int sqlite3_db_status(
break;
}
- /*
+ /*
** Return an approximation for the amount of memory currently used
** by all pagers associated with the given database connection. The
** highwater mark is meaningless and is returned as zero.
@@ -20681,13 +23993,15 @@ SQLITE_API int sqlite3_db_status(
sqlite3BtreeEnterAll(db);
db->pnBytesFreed = &nByte;
+ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd );
+ db->lookaside.pEnd = db->lookaside.pStart;
for(i=0; i<db->nDb; i++){
Schema *pSchema = db->aDb[i].pSchema;
if( ALWAYS(pSchema!=0) ){
HashElem *p;
nByte += sqlite3GlobalConfig.m.xRoundup(sizeof(HashElem)) * (
- pSchema->tblHash.count
+ pSchema->tblHash.count
+ pSchema->trigHash.count
+ pSchema->idxHash.count
+ pSchema->fkeyHash.count
@@ -20706,6 +24020,7 @@ SQLITE_API int sqlite3_db_status(
}
}
db->pnBytesFreed = 0;
+ db->lookaside.pEnd = db->lookaside.pTrueEnd;
sqlite3BtreeLeaveAll(db);
*pHighwater = 0;
@@ -20723,10 +24038,12 @@ SQLITE_API int sqlite3_db_status(
int nByte = 0; /* Used to accumulate return value */
db->pnBytesFreed = &nByte;
- for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){
- sqlite3VdbeClearObject(db, pVdbe);
- sqlite3DbFree(db, pVdbe);
+ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd );
+ db->lookaside.pEnd = db->lookaside.pStart;
+ for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pVNext){
+ sqlite3VdbeDelete(pVdbe);
}
+ db->lookaside.pEnd = db->lookaside.pTrueEnd;
db->pnBytesFreed = 0;
*pHighwater = 0; /* IMP: R-64479-57858 */
@@ -20737,17 +24054,17 @@ SQLITE_API int sqlite3_db_status(
/*
** Set *pCurrent to the total cache hits or misses encountered by all
- ** pagers the database handle is connected to. *pHighwater is always set
+ ** pagers the database handle is connected to. *pHighwater is always set
** to zero.
*/
case SQLITE_DBSTATUS_CACHE_SPILL:
op = SQLITE_DBSTATUS_CACHE_WRITE+1;
- /* Fall through into the next case */
+ /* no break */ deliberate_fall_through
case SQLITE_DBSTATUS_CACHE_HIT:
case SQLITE_DBSTATUS_CACHE_MISS:
case SQLITE_DBSTATUS_CACHE_WRITE:{
int i;
- int nRet = 0;
+ u64 nRet = 0;
assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );
@@ -20760,7 +24077,7 @@ SQLITE_API int sqlite3_db_status(
*pHighwater = 0; /* IMP: R-42420-56072 */
/* IMP: R-54100-20147 */
/* IMP: R-29431-39229 */
- *pCurrent = nRet;
+ *pCurrent = (int)nRet & 0x7fffffff;
break;
}
@@ -20796,7 +24113,7 @@ SQLITE_API int sqlite3_db_status(
**
*************************************************************************
** This file contains the C functions that implement date and time
-** functions for SQLite.
+** functions for SQLite.
**
** There is only one exported symbol in this file - the function
** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
@@ -20805,7 +24122,7 @@ SQLITE_API int sqlite3_db_status(
** SQLite processes all times and dates as julian day numbers. The
** dates and times are stored as the number of days since noon
** in Greenwich on November 24, 4714 B.C. according to the Gregorian
-** calendar system.
+** calendar system.
**
** 1970-01-01 00:00:00 is JD 2440587.5
** 2000-01-01 00:00:00 is JD 2451544.5
@@ -20863,6 +24180,7 @@ struct DateTime {
char validTZ; /* True (1) if tz is valid */
char tzSet; /* Timezone was set explicitly */
char isError; /* An overflow has occurred */
+ char useSubsec; /* Display subsecond precision */
};
@@ -20895,8 +24213,8 @@ struct DateTime {
*/
static int getDigits(const char *zDate, const char *zFormat, ...){
/* The aMx[] array translates the 3rd character of each format
- ** spec into a max size: a b c d e f */
- static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 };
+ ** spec into a max size: a b c d e f */
+ static const u16 aMx[] = { 12, 14, 24, 31, 59, 14712 };
va_list ap;
int cnt = 0;
char nextC;
@@ -21062,7 +24380,7 @@ static void computeJD(DateTime *p){
p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000);
p->validJD = 1;
if( p->validHMS ){
- p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000);
+ p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5);
if( p->validTZ ){
p->iJD -= p->tz*60000;
p->validYMD = 0;
@@ -21153,7 +24471,7 @@ static void setRawDateNumber(DateTime *p, double r){
** The following are acceptable forms for the input string:
**
** YYYY-MM-DD HH:MM:SS.FFF +/-HH:MM
-** DDDD.DD
+** DDDD.DD
** now
**
** In the first form, the +/-HH:MM is always optional. The fractional
@@ -21163,8 +24481,8 @@ static void setRawDateNumber(DateTime *p, double r){
** as there is a year and date.
*/
static int parseDateOrTime(
- sqlite3_context *context,
- const char *zDate,
+ sqlite3_context *context,
+ const char *zDate,
DateTime *p
){
double r;
@@ -21174,9 +24492,14 @@ static int parseDateOrTime(
return 0;
}else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){
return setDateTimeToCurrent(context, p);
- }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){
+ }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){
setRawDateNumber(p, r);
return 0;
+ }else if( (sqlite3StrICmp(zDate,"subsec")==0
+ || sqlite3StrICmp(zDate,"subsecond")==0)
+ && sqlite3NotPureFunc(context) ){
+ p->useSubsec = 1;
+ return setDateTimeToCurrent(context, p);
}
return 1;
}
@@ -21185,7 +24508,7 @@ static int parseDateOrTime(
** Multiplying this by 86400000 gives 464269060799999 as the maximum value
** for DateTime.iJD.
**
-** But some older compilers (ex: gcc 4.2.1 on older Macs) cannot deal with
+** But some older compilers (ex: gcc 4.2.1 on older Macs) cannot deal with
** such a large integer literal, so we have to encode it.
*/
#define INT_464269060799999 ((((i64)0x1a640)<<32)|0x1072fdff)
@@ -21232,17 +24555,14 @@ static void computeYMD(DateTime *p){
** Compute the Hour, Minute, and Seconds from the julian day number.
*/
static void computeHMS(DateTime *p){
- int s;
+ int day_ms, day_min; /* milliseconds, minutes into the day */
if( p->validHMS ) return;
computeJD(p);
- s = (int)((p->iJD + 43200000) % 86400000);
- p->s = s/1000.0;
- s = (int)p->s;
- p->s -= s;
- p->h = s/3600;
- s -= p->h*3600;
- p->m = s/60;
- p->s += s - p->m*60;
+ day_ms = (int)((p->iJD + 43200000) % 86400000);
+ p->s = (day_ms % 60000)/1000.0;
+ day_min = day_ms/60000;
+ p->m = day_min % 60;
+ p->h = day_min / 60;
p->rawS = 0;
p->validHMS = 1;
}
@@ -21267,14 +24587,14 @@ static void clearYMD_HMS_TZ(DateTime *p){
#ifndef SQLITE_OMIT_LOCALTIME
/*
** On recent Windows platforms, the localtime_s() function is available
-** as part of the "Secure CRT". It is essentially equivalent to
-** localtime_r() available under most POSIX platforms, except that the
+** as part of the "Secure CRT". It is essentially equivalent to
+** localtime_r() available under most POSIX platforms, except that the
** order of the parameters is reversed.
**
** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx.
**
** If the user has not indicated to use localtime_r() or localtime_s()
-** already, check for an MSVC build environment that provides
+** already, check for an MSVC build environment that provides
** localtime_s().
*/
#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S \
@@ -21289,8 +24609,10 @@ static void clearYMD_HMS_TZ(DateTime *p){
** is available. This routine returns 0 on success and
** non-zero on any kind of error.
**
-** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this
-** routine will always fail.
+** If the sqlite3GlobalConfig.bLocaltimeFault variable is non-zero then this
+** routine will always fail. If bLocaltimeFault is nonzero and
+** sqlite3GlobalConfig.xAltLocaltime is not NULL, then xAltLocaltime() is
+** invoked in place of the OS-defined localtime() function.
**
** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C
** library function localtime_r() is used to assist in the calculation of
@@ -21301,19 +24623,35 @@ static int osLocaltime(time_t *t, struct tm *pTm){
#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S
struct tm *pX;
#if SQLITE_THREADSAFE>0
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
#endif
sqlite3_mutex_enter(mutex);
pX = localtime(t);
#ifndef SQLITE_UNTESTABLE
- if( sqlite3GlobalConfig.bLocaltimeFault ) pX = 0;
+ if( sqlite3GlobalConfig.bLocaltimeFault ){
+ if( sqlite3GlobalConfig.xAltLocaltime!=0
+ && 0==sqlite3GlobalConfig.xAltLocaltime((const void*)t,(void*)pTm)
+ ){
+ pX = pTm;
+ }else{
+ pX = 0;
+ }
+ }
#endif
if( pX ) *pTm = *pX;
+#if SQLITE_THREADSAFE>0
sqlite3_mutex_leave(mutex);
+#endif
rc = pX==0;
#else
#ifndef SQLITE_UNTESTABLE
- if( sqlite3GlobalConfig.bLocaltimeFault ) return 1;
+ if( sqlite3GlobalConfig.bLocaltimeFault ){
+ if( sqlite3GlobalConfig.xAltLocaltime!=0 ){
+ return sqlite3GlobalConfig.xAltLocaltime((const void*)t,(void*)pTm);
+ }else{
+ return 1;
+ }
+ }
#endif
#if HAVE_LOCALTIME_R
rc = localtime_r(t, pTm)==0;
@@ -21328,67 +24666,56 @@ static int osLocaltime(time_t *t, struct tm *pTm){
#ifndef SQLITE_OMIT_LOCALTIME
/*
-** Compute the difference (in milliseconds) between localtime and UTC
-** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs,
-** return this value and set *pRc to SQLITE_OK.
-**
-** Or, if an error does occur, set *pRc to SQLITE_ERROR. The returned value
-** is undefined in this case.
+** Assuming the input DateTime is UTC, move it to its localtime equivalent.
*/
-static sqlite3_int64 localtimeOffset(
- DateTime *p, /* Date at which to calculate offset */
- sqlite3_context *pCtx, /* Write error here if one occurs */
- int *pRc /* OUT: Error code. SQLITE_OK or ERROR */
+static int toLocaltime(
+ DateTime *p, /* Date at which to calculate offset */
+ sqlite3_context *pCtx /* Write error here if one occurs */
){
- DateTime x, y;
time_t t;
struct tm sLocal;
+ int iYearDiff;
/* Initialize the contents of sLocal to avoid a compiler warning. */
memset(&sLocal, 0, sizeof(sLocal));
- x = *p;
- computeYMD_HMS(&x);
- if( x.Y<1971 || x.Y>=2038 ){
+ computeJD(p);
+ if( p->iJD<2108667600*(i64)100000 /* 1970-01-01 */
+ || p->iJD>2130141456*(i64)100000 /* 2038-01-18 */
+ ){
/* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only
** works for years between 1970 and 2037. For dates outside this range,
** SQLite attempts to map the year into an equivalent year within this
** range, do the calculation, then map the year back.
*/
- x.Y = 2000;
- x.M = 1;
- x.D = 1;
- x.h = 0;
- x.m = 0;
- x.s = 0.0;
- } else {
- int s = (int)(x.s + 0.5);
- x.s = s;
+ DateTime x = *p;
+ computeYMD_HMS(&x);
+ iYearDiff = (2000 + x.Y%4) - x.Y;
+ x.Y += iYearDiff;
+ x.validJD = 0;
+ computeJD(&x);
+ t = (time_t)(x.iJD/1000 - 21086676*(i64)10000);
+ }else{
+ iYearDiff = 0;
+ t = (time_t)(p->iJD/1000 - 21086676*(i64)10000);
}
- x.tz = 0;
- x.validJD = 0;
- computeJD(&x);
- t = (time_t)(x.iJD/1000 - 21086676*(i64)10000);
if( osLocaltime(&t, &sLocal) ){
sqlite3_result_error(pCtx, "local time unavailable", -1);
- *pRc = SQLITE_ERROR;
- return 0;
+ return SQLITE_ERROR;
}
- y.Y = sLocal.tm_year + 1900;
- y.M = sLocal.tm_mon + 1;
- y.D = sLocal.tm_mday;
- y.h = sLocal.tm_hour;
- y.m = sLocal.tm_min;
- y.s = sLocal.tm_sec;
- y.validYMD = 1;
- y.validHMS = 1;
- y.validJD = 0;
- y.rawS = 0;
- y.validTZ = 0;
- y.isError = 0;
- computeJD(&y);
- *pRc = SQLITE_OK;
- return y.iJD - x.iJD;
+ p->Y = sLocal.tm_year + 1900 - iYearDiff;
+ p->M = sLocal.tm_mon + 1;
+ p->D = sLocal.tm_mday;
+ p->h = sLocal.tm_hour;
+ p->m = sLocal.tm_min;
+ p->s = sLocal.tm_sec + (p->iJD%1000)*0.001;
+ p->validYMD = 1;
+ p->validHMS = 1;
+ p->validJD = 0;
+ p->rawS = 0;
+ p->validTZ = 0;
+ p->isError = 0;
+ return SQLITE_OK;
}
#endif /* SQLITE_OMIT_LOCALTIME */
@@ -21401,21 +24728,39 @@ static sqlite3_int64 localtimeOffset(
** of several units of time.
*/
static const struct {
- u8 eType; /* Transformation type code */
- u8 nName; /* Length of th name */
- char *zName; /* Name of the transformation */
- double rLimit; /* Maximum NNN value for this transform */
- double rXform; /* Constant used for this transform */
+ u8 nName; /* Length of the name */
+ char zName[7]; /* Name of the transformation */
+ float rLimit; /* Maximum NNN value for this transform */
+ float rXform; /* Constant used for this transform */
} aXformType[] = {
- { 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) },
- { 0, 6, "minute", 7737817680.0, 86400000.0/(24.0*60.0) },
- { 0, 4, "hour", 128963628.0, 86400000.0/24.0 },
- { 0, 3, "day", 5373485.0, 86400000.0 },
- { 1, 5, "month", 176546.0, 30.0*86400000.0 },
- { 2, 4, "year", 14713.0, 365.0*86400000.0 },
+ { 6, "second", 4.6427e+14, 1.0 },
+ { 6, "minute", 7.7379e+12, 60.0 },
+ { 4, "hour", 1.2897e+11, 3600.0 },
+ { 3, "day", 5373485.0, 86400.0 },
+ { 5, "month", 176546.0, 2592000.0 },
+ { 4, "year", 14713.0, 31536000.0 },
};
/*
+** If the DateTime p is raw number, try to figure out if it is
+** a julian day number of a unix timestamp. Set the p value
+** appropriately.
+*/
+static void autoAdjustDate(DateTime *p){
+ if( !p->rawS || p->validJD ){
+ p->rawS = 0;
+ }else if( p->s>=-21086676*(i64)10000 /* -4713-11-24 12:00:00 */
+ && p->s<=(25340230*(i64)10000)+799 /* 9999-12-31 23:59:59 */
+ ){
+ double r = p->s*1000.0 + 210866760000000.0;
+ clearYMD_HMS_TZ(p);
+ p->iJD = (sqlite3_int64)(r + 0.5);
+ p->validJD = 1;
+ p->rawS = 0;
+ }
+}
+
+/*
** Process a modifier to a date-time stamp. The modifiers are
** as follows:
**
@@ -21443,11 +24788,44 @@ static int parseModifier(
sqlite3_context *pCtx, /* Function context */
const char *z, /* The text of the modifier */
int n, /* Length of zMod in bytes */
- DateTime *p /* The date/time value to be modified */
+ DateTime *p, /* The date/time value to be modified */
+ int idx /* Parameter index of the modifier */
){
int rc = 1;
double r;
switch(sqlite3UpperToLower[(u8)z[0]] ){
+ case 'a': {
+ /*
+ ** auto
+ **
+ ** If rawS is available, then interpret as a julian day number, or
+ ** a unix timestamp, depending on its magnitude.
+ */
+ if( sqlite3_stricmp(z, "auto")==0 ){
+ if( idx>1 ) return 1; /* IMP: R-33611-57934 */
+ autoAdjustDate(p);
+ rc = 0;
+ }
+ break;
+ }
+ case 'j': {
+ /*
+ ** julianday
+ **
+ ** Always interpret the prior number as a julian-day value. If this
+ ** is not the first modifier, or if the prior argument is not a numeric
+ ** value in the allowed range of julian day numbers understood by
+ ** SQLite (0..5373484.5) then the result will be NULL.
+ */
+ if( sqlite3_stricmp(z, "julianday")==0 ){
+ if( idx>1 ) return 1; /* IMP: R-31176-64601 */
+ if( p->validJD && p->rawS ){
+ rc = 0;
+ p->rawS = 0;
+ }
+ }
+ break;
+ }
#ifndef SQLITE_OMIT_LOCALTIME
case 'l': {
/* localtime
@@ -21456,9 +24834,7 @@ static int parseModifier(
** show local time.
*/
if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){
- computeJD(p);
- p->iJD += localtimeOffset(p, pCtx, &rc);
- clearYMD_HMS_TZ(p);
+ rc = toLocaltime(p, pCtx);
}
break;
}
@@ -21471,10 +24847,11 @@ static int parseModifier(
** seconds since 1970. Convert to a real julian day number.
*/
if( sqlite3_stricmp(z, "unixepoch")==0 && p->rawS ){
+ if( idx>1 ) return 1; /* IMP: R-49255-55373 */
r = p->s*1000.0 + 210866760000000.0;
if( r>=0.0 && r<464269060800000.0 ){
clearYMD_HMS_TZ(p);
- p->iJD = (sqlite3_int64)r;
+ p->iJD = (sqlite3_int64)(r + 0.5);
p->validJD = 1;
p->rawS = 0;
rc = 0;
@@ -21483,18 +24860,31 @@ static int parseModifier(
#ifndef SQLITE_OMIT_LOCALTIME
else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){
if( p->tzSet==0 ){
- sqlite3_int64 c1;
+ i64 iOrigJD; /* Original localtime */
+ i64 iGuess; /* Guess at the corresponding utc time */
+ int cnt = 0; /* Safety to prevent infinite loop */
+ i64 iErr; /* Guess is off by this much */
+
computeJD(p);
- c1 = localtimeOffset(p, pCtx, &rc);
- if( rc==SQLITE_OK ){
- p->iJD -= c1;
- clearYMD_HMS_TZ(p);
- p->iJD += c1 - localtimeOffset(p, pCtx, &rc);
- }
+ iGuess = iOrigJD = p->iJD;
+ iErr = 0;
+ do{
+ DateTime new;
+ memset(&new, 0, sizeof(new));
+ iGuess -= iErr;
+ new.iJD = iGuess;
+ new.validJD = 1;
+ rc = toLocaltime(&new, pCtx);
+ if( rc ) return rc;
+ computeJD(&new);
+ iErr = new.iJD - iOrigJD;
+ }while( iErr && cnt++<3 );
+ memset(p, 0, sizeof(*p));
+ p->iJD = iGuess;
+ p->validJD = 1;
p->tzSet = 1;
- }else{
- rc = SQLITE_OK;
}
+ rc = SQLITE_OK;
}
#endif
break;
@@ -21508,8 +24898,8 @@ static int parseModifier(
** date is already on the appropriate weekday, this is a no-op.
*/
if( sqlite3_strnicmp(z, "weekday ", 8)==0
- && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)
- && (n=(int)r)==r && n>=0 && r<7 ){
+ && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)>0
+ && r>=0.0 && r<7.0 && (n=(int)r)==r ){
sqlite3_int64 Z;
computeYMD_HMS(p);
p->validTZ = 0;
@@ -21529,8 +24919,22 @@ static int parseModifier(
**
** Move the date backwards to the beginning of the current day,
** or month or year.
+ **
+ ** subsecond
+ ** subsec
+ **
+ ** Show subsecond precision in the output of datetime() and
+ ** unixepoch() and strftime('%s').
*/
- if( sqlite3_strnicmp(z, "start of ", 9)!=0 ) break;
+ if( sqlite3_strnicmp(z, "start of ", 9)!=0 ){
+ if( sqlite3_stricmp(z, "subsec")==0
+ || sqlite3_stricmp(z, "subsecond")==0
+ ){
+ p->useSubsec = 1;
+ rc = 0;
+ }
+ break;
+ }
if( !p->validJD && !p->validYMD && !p->validHMS ) break;
z += 9;
computeYMD(p);
@@ -21566,18 +24970,73 @@ static int parseModifier(
case '9': {
double rRounder;
int i;
- for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){}
- if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){
- rc = 1;
+ int Y,M,D,h,m,x;
+ const char *z2 = z;
+ char z0 = z[0];
+ for(n=1; z[n]; n++){
+ if( z[n]==':' ) break;
+ if( sqlite3Isspace(z[n]) ) break;
+ if( z[n]=='-' ){
+ if( n==5 && getDigits(&z[1], "40f", &Y)==1 ) break;
+ if( n==6 && getDigits(&z[1], "50f", &Y)==1 ) break;
+ }
+ }
+ if( sqlite3AtoF(z, &r, n, SQLITE_UTF8)<=0 ){
+ assert( rc==1 );
break;
}
- if( z[n]==':' ){
+ if( z[n]=='-' ){
+ /* A modifier of the form (+|-)YYYY-MM-DD adds or subtracts the
+ ** specified number of years, months, and days. MM is limited to
+ ** the range 0-11 and DD is limited to 0-30.
+ */
+ if( z0!='+' && z0!='-' ) break; /* Must start with +/- */
+ if( n==5 ){
+ if( getDigits(&z[1], "40f-20a-20d", &Y, &M, &D)!=3 ) break;
+ }else{
+ assert( n==6 );
+ if( getDigits(&z[1], "50f-20a-20d", &Y, &M, &D)!=3 ) break;
+ z++;
+ }
+ if( M>=12 ) break; /* M range 0..11 */
+ if( D>=31 ) break; /* D range 0..30 */
+ computeYMD_HMS(p);
+ p->validJD = 0;
+ if( z0=='-' ){
+ p->Y -= Y;
+ p->M -= M;
+ D = -D;
+ }else{
+ p->Y += Y;
+ p->M += M;
+ }
+ x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
+ p->Y += x;
+ p->M -= x*12;
+ computeJD(p);
+ p->validHMS = 0;
+ p->validYMD = 0;
+ p->iJD += (i64)D*86400000;
+ if( z[11]==0 ){
+ rc = 0;
+ break;
+ }
+ if( sqlite3Isspace(z[11])
+ && getDigits(&z[12], "20c:20e", &h, &m)==2
+ ){
+ z2 = &z[12];
+ n = 2;
+ }else{
+ break;
+ }
+ }
+ if( z2[n]==':' ){
/* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the
** specified number of hours, minutes, seconds, and fractional seconds
** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be
** omitted.
*/
- const char *z2 = z;
+
DateTime tx;
sqlite3_int64 day;
if( !sqlite3Isdigit(*z2) ) z2++;
@@ -21587,7 +25046,7 @@ static int parseModifier(
tx.iJD -= 43200000;
day = tx.iJD/86400000;
tx.iJD -= day*86400000;
- if( z[0]=='-' ) tx.iJD = -tx.iJD;
+ if( z0=='-' ) tx.iJD = -tx.iJD;
computeJD(p);
clearYMD_HMS_TZ(p);
p->iJD += tx.iJD;
@@ -21603,16 +25062,16 @@ static int parseModifier(
if( n>10 || n<3 ) break;
if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--;
computeJD(p);
- rc = 1;
+ assert( rc==1 );
rRounder = r<0 ? -0.5 : +0.5;
for(i=0; i<ArraySize(aXformType); i++){
if( aXformType[i].nName==n
&& sqlite3_strnicmp(aXformType[i].zName, z, n)==0
&& r>-aXformType[i].rLimit && r<aXformType[i].rLimit
){
- switch( aXformType[i].eType ){
- case 1: { /* Special processing to add months */
- int x;
+ switch( i ){
+ case 4: { /* Special processing to add months */
+ assert( strcmp(aXformType[i].zName,"month")==0 );
computeYMD_HMS(p);
p->M += (int)r;
x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
@@ -21622,8 +25081,9 @@ static int parseModifier(
r -= (int)r;
break;
}
- case 2: { /* Special processing to add years */
+ case 5: { /* Special processing to add years */
int y = (int)r;
+ assert( strcmp(aXformType[i].zName,"year")==0 );
computeYMD_HMS(p);
p->Y += y;
p->validJD = 0;
@@ -21632,7 +25092,7 @@ static int parseModifier(
}
}
computeJD(p);
- p->iJD += (sqlite3_int64)(r*aXformType[i].rXform + rRounder);
+ p->iJD += (sqlite3_int64)(r*1000.0*aXformType[i].rXform + rRounder);
rc = 0;
break;
}
@@ -21657,9 +25117,9 @@ static int parseModifier(
** then assume a default value of "now" for argv[0].
*/
static int isDate(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv,
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv,
DateTime *p
){
int i, n;
@@ -21667,6 +25127,7 @@ static int isDate(
int eType;
memset(p, 0, sizeof(*p));
if( argc==0 ){
+ if( !sqlite3NotPureFunc(context) ) return 1;
return setDateTimeToCurrent(context, p);
}
if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
@@ -21681,10 +25142,16 @@ static int isDate(
for(i=1; i<argc; i++){
z = sqlite3_value_text(argv[i]);
n = sqlite3_value_bytes(argv[i]);
- if( z==0 || parseModifier(context, (char*)z, n, p) ) return 1;
+ if( z==0 || parseModifier(context, (char*)z, n, p, i) ) return 1;
}
computeJD(p);
if( p->isError || !validJulianDay(p->iJD) ) return 1;
+ if( argc==1 && p->validYMD && p->D>28 ){
+ /* Make sure a YYYY-MM-DD is normalized.
+ ** Example: 2023-02-31 -> 2023-03-03 */
+ assert( p->validJD );
+ p->validYMD = 0;
+ }
return 0;
}
@@ -21712,6 +25179,28 @@ static void juliandayFunc(
}
/*
+** unixepoch( TIMESTRING, MOD, MOD, ...)
+**
+** Return the number of seconds (including fractional seconds) since
+** the unix epoch of 1970-01-01 00:00:00 GMT.
+*/
+static void unixepochFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ DateTime x;
+ if( isDate(context, argc, argv, &x)==0 ){
+ computeJD(&x);
+ if( x.useSubsec ){
+ sqlite3_result_double(context, (x.iJD - 21086676*(i64)10000000)/1000.0);
+ }else{
+ sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000);
+ }
+ }
+}
+
+/*
** datetime( TIMESTRING, MOD, MOD, ...)
**
** Return YYYY-MM-DD HH:MM:SS
@@ -21723,11 +25212,51 @@ static void datetimeFunc(
){
DateTime x;
if( isDate(context, argc, argv, &x)==0 ){
- char zBuf[100];
+ int Y, s, n;
+ char zBuf[32];
computeYMD_HMS(&x);
- sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d %02d:%02d:%02d",
- x.Y, x.M, x.D, x.h, x.m, (int)(x.s));
- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ Y = x.Y;
+ if( Y<0 ) Y = -Y;
+ zBuf[1] = '0' + (Y/1000)%10;
+ zBuf[2] = '0' + (Y/100)%10;
+ zBuf[3] = '0' + (Y/10)%10;
+ zBuf[4] = '0' + (Y)%10;
+ zBuf[5] = '-';
+ zBuf[6] = '0' + (x.M/10)%10;
+ zBuf[7] = '0' + (x.M)%10;
+ zBuf[8] = '-';
+ zBuf[9] = '0' + (x.D/10)%10;
+ zBuf[10] = '0' + (x.D)%10;
+ zBuf[11] = ' ';
+ zBuf[12] = '0' + (x.h/10)%10;
+ zBuf[13] = '0' + (x.h)%10;
+ zBuf[14] = ':';
+ zBuf[15] = '0' + (x.m/10)%10;
+ zBuf[16] = '0' + (x.m)%10;
+ zBuf[17] = ':';
+ if( x.useSubsec ){
+ s = (int)(1000.0*x.s + 0.5);
+ zBuf[18] = '0' + (s/10000)%10;
+ zBuf[19] = '0' + (s/1000)%10;
+ zBuf[20] = '.';
+ zBuf[21] = '0' + (s/100)%10;
+ zBuf[22] = '0' + (s/10)%10;
+ zBuf[23] = '0' + (s)%10;
+ zBuf[24] = 0;
+ n = 24;
+ }else{
+ s = (int)x.s;
+ zBuf[18] = '0' + (s/10)%10;
+ zBuf[19] = '0' + (s)%10;
+ zBuf[20] = 0;
+ n = 20;
+ }
+ if( x.Y<0 ){
+ zBuf[0] = '-';
+ sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT);
+ }else{
+ sqlite3_result_text(context, &zBuf[1], n-1, SQLITE_TRANSIENT);
+ }
}
}
@@ -21743,10 +25272,33 @@ static void timeFunc(
){
DateTime x;
if( isDate(context, argc, argv, &x)==0 ){
- char zBuf[100];
+ int s, n;
+ char zBuf[16];
computeHMS(&x);
- sqlite3_snprintf(sizeof(zBuf), zBuf, "%02d:%02d:%02d", x.h, x.m, (int)x.s);
- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ zBuf[0] = '0' + (x.h/10)%10;
+ zBuf[1] = '0' + (x.h)%10;
+ zBuf[2] = ':';
+ zBuf[3] = '0' + (x.m/10)%10;
+ zBuf[4] = '0' + (x.m)%10;
+ zBuf[5] = ':';
+ if( x.useSubsec ){
+ s = (int)(1000.0*x.s + 0.5);
+ zBuf[6] = '0' + (s/10000)%10;
+ zBuf[7] = '0' + (s/1000)%10;
+ zBuf[8] = '.';
+ zBuf[9] = '0' + (s/100)%10;
+ zBuf[10] = '0' + (s/10)%10;
+ zBuf[11] = '0' + (s)%10;
+ zBuf[12] = 0;
+ n = 12;
+ }else{
+ s = (int)x.s;
+ zBuf[6] = '0' + (s/10)%10;
+ zBuf[7] = '0' + (s)%10;
+ zBuf[8] = 0;
+ n = 8;
+ }
+ sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT);
}
}
@@ -21762,10 +25314,28 @@ static void dateFunc(
){
DateTime x;
if( isDate(context, argc, argv, &x)==0 ){
- char zBuf[100];
+ int Y;
+ char zBuf[16];
computeYMD(&x);
- sqlite3_snprintf(sizeof(zBuf), zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D);
- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ Y = x.Y;
+ if( Y<0 ) Y = -Y;
+ zBuf[1] = '0' + (Y/1000)%10;
+ zBuf[2] = '0' + (Y/100)%10;
+ zBuf[3] = '0' + (Y/10)%10;
+ zBuf[4] = '0' + (Y)%10;
+ zBuf[5] = '-';
+ zBuf[6] = '0' + (x.M/10)%10;
+ zBuf[7] = '0' + (x.M)%10;
+ zBuf[8] = '-';
+ zBuf[9] = '0' + (x.D/10)%10;
+ zBuf[10] = '0' + (x.D)%10;
+ zBuf[11] = 0;
+ if( x.Y<0 ){
+ zBuf[0] = '-';
+ sqlite3_result_text(context, zBuf, 11, SQLITE_TRANSIENT);
+ }else{
+ sqlite3_result_text(context, &zBuf[1], 10, SQLITE_TRANSIENT);
+ }
}
}
@@ -21783,7 +25353,7 @@ static void dateFunc(
** %M minute 00-59
** %s seconds since 1970-01-01
** %S seconds 00-59
-** %w day of week 0-6 sunday==0
+** %w day of week 0-6 Sunday==0
** %W week of year 00-53
** %Y year 0000-9999
** %% %
@@ -21794,131 +25364,140 @@ static void strftimeFunc(
sqlite3_value **argv
){
DateTime x;
- u64 n;
size_t i,j;
- char *z;
sqlite3 *db;
const char *zFmt;
- char zBuf[100];
+ sqlite3_str sRes;
+
+
if( argc==0 ) return;
zFmt = (const char*)sqlite3_value_text(argv[0]);
if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return;
db = sqlite3_context_db_handle(context);
- for(i=0, n=1; zFmt[i]; i++, n++){
- if( zFmt[i]=='%' ){
- switch( zFmt[i+1] ){
- case 'd':
- case 'H':
- case 'm':
- case 'M':
- case 'S':
- case 'W':
- n++;
- /* fall thru */
- case 'w':
- case '%':
- break;
- case 'f':
- n += 8;
- break;
- case 'j':
- n += 3;
- break;
- case 'Y':
- n += 8;
- break;
- case 's':
- case 'J':
- n += 50;
- break;
- default:
- return; /* ERROR. return a NULL */
- }
- i++;
- }
- }
- testcase( n==sizeof(zBuf)-1 );
- testcase( n==sizeof(zBuf) );
- testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH]+1 );
- testcase( n==(u64)db->aLimit[SQLITE_LIMIT_LENGTH] );
- if( n<sizeof(zBuf) ){
- z = zBuf;
- }else if( n>(u64)db->aLimit[SQLITE_LIMIT_LENGTH] ){
- sqlite3_result_error_toobig(context);
- return;
- }else{
- z = sqlite3DbMallocRawNN(db, (int)n);
- if( z==0 ){
- sqlite3_result_error_nomem(context);
- return;
- }
- }
+ sqlite3StrAccumInit(&sRes, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
+
computeJD(&x);
computeYMD_HMS(&x);
for(i=j=0; zFmt[i]; i++){
- if( zFmt[i]!='%' ){
- z[j++] = zFmt[i];
- }else{
- i++;
- switch( zFmt[i] ){
- case 'd': sqlite3_snprintf(3, &z[j],"%02d",x.D); j+=2; break;
- case 'f': {
- double s = x.s;
- if( s>59.999 ) s = 59.999;
- sqlite3_snprintf(7, &z[j],"%06.3f", s);
- j += sqlite3Strlen30(&z[j]);
- break;
- }
- case 'H': sqlite3_snprintf(3, &z[j],"%02d",x.h); j+=2; break;
- case 'W': /* Fall thru */
- case 'j': {
- int nDay; /* Number of days since 1st day of year */
- DateTime y = x;
- y.validJD = 0;
- y.M = 1;
- y.D = 1;
- computeJD(&y);
- nDay = (int)((x.iJD-y.iJD+43200000)/86400000);
- if( zFmt[i]=='W' ){
- int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
- wd = (int)(((x.iJD+43200000)/86400000)%7);
- sqlite3_snprintf(3, &z[j],"%02d",(nDay+7-wd)/7);
- j += 2;
- }else{
- sqlite3_snprintf(4, &z[j],"%03d",nDay+1);
- j += 3;
- }
- break;
- }
- case 'J': {
- sqlite3_snprintf(20, &z[j],"%.16g",x.iJD/86400000.0);
- j+=sqlite3Strlen30(&z[j]);
- break;
- }
- case 'm': sqlite3_snprintf(3, &z[j],"%02d",x.M); j+=2; break;
- case 'M': sqlite3_snprintf(3, &z[j],"%02d",x.m); j+=2; break;
- case 's': {
- sqlite3_snprintf(30,&z[j],"%lld",
- (i64)(x.iJD/1000 - 21086676*(i64)10000));
- j += sqlite3Strlen30(&z[j]);
- break;
+ char cf;
+ if( zFmt[i]!='%' ) continue;
+ if( j<i ) sqlite3_str_append(&sRes, zFmt+j, (int)(i-j));
+ i++;
+ j = i + 1;
+ cf = zFmt[i];
+ switch( cf ){
+ case 'd': /* Fall thru */
+ case 'e': {
+ sqlite3_str_appendf(&sRes, cf=='d' ? "%02d" : "%2d", x.D);
+ break;
+ }
+ case 'f': {
+ double s = x.s;
+ if( s>59.999 ) s = 59.999;
+ sqlite3_str_appendf(&sRes, "%06.3f", s);
+ break;
+ }
+ case 'F': {
+ sqlite3_str_appendf(&sRes, "%04d-%02d-%02d", x.Y, x.M, x.D);
+ break;
+ }
+ case 'H':
+ case 'k': {
+ sqlite3_str_appendf(&sRes, cf=='H' ? "%02d" : "%2d", x.h);
+ break;
+ }
+ case 'I': /* Fall thru */
+ case 'l': {
+ int h = x.h;
+ if( h>12 ) h -= 12;
+ if( h==0 ) h = 12;
+ sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h);
+ break;
+ }
+ case 'W': /* Fall thru */
+ case 'j': {
+ int nDay; /* Number of days since 1st day of year */
+ DateTime y = x;
+ y.validJD = 0;
+ y.M = 1;
+ y.D = 1;
+ computeJD(&y);
+ nDay = (int)((x.iJD-y.iJD+43200000)/86400000);
+ if( cf=='W' ){
+ int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
+ wd = (int)(((x.iJD+43200000)/86400000)%7);
+ sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7);
+ }else{
+ sqlite3_str_appendf(&sRes,"%03d",nDay+1);
}
- case 'S': sqlite3_snprintf(3,&z[j],"%02d",(int)x.s); j+=2; break;
- case 'w': {
- z[j++] = (char)(((x.iJD+129600000)/86400000) % 7) + '0';
- break;
+ break;
+ }
+ case 'J': {
+ sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0);
+ break;
+ }
+ case 'm': {
+ sqlite3_str_appendf(&sRes,"%02d",x.M);
+ break;
+ }
+ case 'M': {
+ sqlite3_str_appendf(&sRes,"%02d",x.m);
+ break;
+ }
+ case 'p': /* Fall thru */
+ case 'P': {
+ if( x.h>=12 ){
+ sqlite3_str_append(&sRes, cf=='p' ? "PM" : "pm", 2);
+ }else{
+ sqlite3_str_append(&sRes, cf=='p' ? "AM" : "am", 2);
}
- case 'Y': {
- sqlite3_snprintf(5,&z[j],"%04d",x.Y); j+=sqlite3Strlen30(&z[j]);
- break;
+ break;
+ }
+ case 'R': {
+ sqlite3_str_appendf(&sRes, "%02d:%02d", x.h, x.m);
+ break;
+ }
+ case 's': {
+ if( x.useSubsec ){
+ sqlite3_str_appendf(&sRes,"%.3f",
+ (x.iJD - 21086676*(i64)10000000)/1000.0);
+ }else{
+ i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000);
+ sqlite3_str_appendf(&sRes,"%lld",iS);
}
- default: z[j++] = '%'; break;
+ break;
+ }
+ case 'S': {
+ sqlite3_str_appendf(&sRes,"%02d",(int)x.s);
+ break;
+ }
+ case 'T': {
+ sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s);
+ break;
+ }
+ case 'u': /* Fall thru */
+ case 'w': {
+ char c = (char)(((x.iJD+129600000)/86400000) % 7) + '0';
+ if( c=='0' && cf=='u' ) c = '7';
+ sqlite3_str_appendchar(&sRes, 1, c);
+ break;
+ }
+ case 'Y': {
+ sqlite3_str_appendf(&sRes,"%04d",x.Y);
+ break;
+ }
+ case '%': {
+ sqlite3_str_appendchar(&sRes, 1, '%');
+ break;
+ }
+ default: {
+ sqlite3_str_reset(&sRes);
+ return;
}
}
}
- z[j] = 0;
- sqlite3_result_text(context, z, -1,
- z==zBuf ? SQLITE_TRANSIENT : SQLITE_DYNAMIC);
+ if( j<i ) sqlite3_str_append(&sRes, zFmt+j, (int)(i-j));
+ sqlite3ResultStrAccum(context, &sRes);
}
/*
@@ -21950,6 +25529,117 @@ static void cdateFunc(
}
/*
+** timediff(DATE1, DATE2)
+**
+** Return the amount of time that must be added to DATE2 in order to
+** convert it into DATE2. The time difference format is:
+**
+** +YYYY-MM-DD HH:MM:SS.SSS
+**
+** The initial "+" becomes "-" if DATE1 occurs before DATE2. For
+** date/time values A and B, the following invariant should hold:
+**
+** datetime(A) == (datetime(B, timediff(A,B))
+**
+** Both DATE arguments must be either a julian day number, or an
+** ISO-8601 string. The unix timestamps are not supported by this
+** routine.
+*/
+static void timediffFunc(
+ sqlite3_context *context,
+ int NotUsed1,
+ sqlite3_value **argv
+){
+ char sign;
+ int Y, M;
+ DateTime d1, d2;
+ sqlite3_str sRes;
+ UNUSED_PARAMETER(NotUsed1);
+ if( isDate(context, 1, &argv[0], &d1) ) return;
+ if( isDate(context, 1, &argv[1], &d2) ) return;
+ computeYMD_HMS(&d1);
+ computeYMD_HMS(&d2);
+ if( d1.iJD>=d2.iJD ){
+ sign = '+';
+ Y = d1.Y - d2.Y;
+ if( Y ){
+ d2.Y = d1.Y;
+ d2.validJD = 0;
+ computeJD(&d2);
+ }
+ M = d1.M - d2.M;
+ if( M<0 ){
+ Y--;
+ M += 12;
+ }
+ if( M!=0 ){
+ d2.M = d1.M;
+ d2.validJD = 0;
+ computeJD(&d2);
+ }
+ while( d1.iJD<d2.iJD ){
+ M--;
+ if( M<0 ){
+ M = 11;
+ Y--;
+ }
+ d2.M--;
+ if( d2.M<1 ){
+ d2.M = 12;
+ d2.Y--;
+ }
+ d2.validJD = 0;
+ computeJD(&d2);
+ }
+ d1.iJD -= d2.iJD;
+ d1.iJD += (u64)1486995408 * (u64)100000;
+ }else /* d1<d2 */{
+ sign = '-';
+ Y = d2.Y - d1.Y;
+ if( Y ){
+ d2.Y = d1.Y;
+ d2.validJD = 0;
+ computeJD(&d2);
+ }
+ M = d2.M - d1.M;
+ if( M<0 ){
+ Y--;
+ M += 12;
+ }
+ if( M!=0 ){
+ d2.M = d1.M;
+ d2.validJD = 0;
+ computeJD(&d2);
+ }
+ while( d1.iJD>d2.iJD ){
+ M--;
+ if( M<0 ){
+ M = 11;
+ Y--;
+ }
+ d2.M++;
+ if( d2.M>12 ){
+ d2.M = 1;
+ d2.Y++;
+ }
+ d2.validJD = 0;
+ computeJD(&d2);
+ }
+ d1.iJD = d2.iJD - d1.iJD;
+ d1.iJD += (u64)1486995408 * (u64)100000;
+ }
+ d1.validYMD = 0;
+ d1.validHMS = 0;
+ d1.validTZ = 0;
+ computeYMD_HMS(&d1);
+ sqlite3StrAccumInit(&sRes, 0, 0, 0, 100);
+ sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f",
+ sign, Y, M, d1.D-1, d1.h, d1.m, d1.s);
+ sqlite3ResultStrAccum(context, &sRes);
+}
+
+
+/*
** current_timestamp()
**
** This function returns the same value as datetime('now').
@@ -21997,10 +25687,10 @@ static void currentTimeFunc(
#if HAVE_GMTIME_R
pTm = gmtime_r(&t, &sNow);
#else
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
pTm = gmtime(&t);
if( pTm ) memcpy(&sNow, pTm, sizeof(sNow));
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
#endif
if( pTm ){
strftime(zBuf, 20, zFormat, &sNow);
@@ -22018,10 +25708,12 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
static FuncDef aDateTimeFuncs[] = {
#ifndef SQLITE_OMIT_DATETIME_FUNCS
PURE_DATE(julianday, -1, 0, 0, juliandayFunc ),
+ PURE_DATE(unixepoch, -1, 0, 0, unixepochFunc ),
PURE_DATE(date, -1, 0, 0, dateFunc ),
PURE_DATE(time, -1, 0, 0, timeFunc ),
PURE_DATE(datetime, -1, 0, 0, datetimeFunc ),
PURE_DATE(strftime, -1, 0, 0, strftimeFunc ),
+ PURE_DATE(timediff, 2, 0, 0, timediffFunc ),
DFUNCTION(current_time, 0, 0, 0, ctimeFunc ),
DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
DFUNCTION(current_date, 0, 0, 0, cdateFunc ),
@@ -22144,9 +25836,11 @@ SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){
}
SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file *id, int lockType){
DO_OS_MALLOC_TEST(id);
+ assert( lockType>=SQLITE_LOCK_SHARED && lockType<=SQLITE_LOCK_EXCLUSIVE );
return id->pMethods->xLock(id, lockType);
}
SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file *id, int lockType){
+ assert( lockType==SQLITE_LOCK_NONE || lockType==SQLITE_LOCK_SHARED );
return id->pMethods->xUnlock(id, lockType);
}
SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
@@ -22167,17 +25861,24 @@ SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
#ifdef SQLITE_TEST
if( op!=SQLITE_FCNTL_COMMIT_PHASETWO
&& op!=SQLITE_FCNTL_LOCK_TIMEOUT
+ && op!=SQLITE_FCNTL_CKPT_DONE
+ && op!=SQLITE_FCNTL_CKPT_START
){
/* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
** is using a regular VFS, it is called after the corresponding
** transaction has been committed. Injecting a fault at this point
- ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM
+ ** confuses the test scripts - the COMMIT command returns SQLITE_NOMEM
** but the transaction is committed anyway.
**
** The core must call OsFileControl() though, not OsFileControlHint(),
** as if a custom VFS (e.g. zipvfs) returns an error here, it probably
** means the commit really has failed and an error should be returned
- ** to the user. */
+ ** to the user.
+ **
+ ** The CKPT_DONE and CKPT_START file-controls are write-only signals
+ ** to the cksumvfs. Their return code is meaningless and is ignored
+ ** by the SQLite core, so there is no point in simulating OOMs for them.
+ */
DO_OS_MALLOC_TEST(id);
}
#endif
@@ -22192,6 +25893,7 @@ SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){
return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE);
}
SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id){
+ if( NEVER(id->pMethods==0) ) return 0;
return id->pMethods->xDeviceCharacteristics(id);
}
#ifndef SQLITE_OMIT_WAL
@@ -22253,14 +25955,15 @@ SQLITE_PRIVATE int sqlite3OsOpen(
** down into the VFS layer. Some SQLITE_OPEN_ flags (for example,
** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before
** reaching the VFS. */
- rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f7f, pFlagsOut);
+ assert( zPath || (flags & SQLITE_OPEN_EXCLUSIVE) );
+ rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x1087f7f, pFlagsOut);
assert( rc==SQLITE_OK || pFile->pMethods==0 );
return rc;
}
SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
DO_OS_MALLOC_TEST(0);
assert( dirSync==0 || dirSync==1 );
- return pVfs->xDelete(pVfs, zPath, dirSync);
+ return pVfs->xDelete!=0 ? pVfs->xDelete(pVfs, zPath, dirSync) : SQLITE_OK;
}
SQLITE_PRIVATE int sqlite3OsAccess(
sqlite3_vfs *pVfs,
@@ -22283,6 +25986,8 @@ SQLITE_PRIVATE int sqlite3OsFullPathname(
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ assert( zPath!=0 );
+ assert( strlen(zPath)<=SQLITE_MAX_PATHLEN ); /* tag-20210611-1 */
return pVfs->xDlOpen(pVfs, zPath);
}
SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
@@ -22296,7 +26001,15 @@ SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *pVfs, void *pHandle){
}
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
- return pVfs->xRandomness(pVfs, nByte, zBufOut);
+ if( sqlite3Config.iPrngSeed ){
+ memset(zBufOut, 0, nByte);
+ if( ALWAYS(nByte>(signed)sizeof(unsigned)) ) nByte = sizeof(unsigned int);
+ memcpy(zBufOut, &sqlite3Config.iPrngSeed, nByte);
+ return SQLITE_OK;
+ }else{
+ return pVfs->xRandomness(pVfs, nByte, zBufOut);
+ }
+
}
SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){
return pVfs->xSleep(pVfs, nMicro);
@@ -22336,12 +26049,15 @@ SQLITE_PRIVATE int sqlite3OsOpenMalloc(
rc = sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags);
if( rc!=SQLITE_OK ){
sqlite3_free(pFile);
+ *ppFile = 0;
}else{
*ppFile = pFile;
}
}else{
+ *ppFile = 0;
rc = SQLITE_NOMEM_BKPT;
}
+ assert( *ppFile!=0 || rc!=SQLITE_OK );
return rc;
}
SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *pFile){
@@ -22383,7 +26099,7 @@ SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){
if( rc ) return 0;
#endif
#if SQLITE_THREADSAFE
- mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
#endif
sqlite3_mutex_enter(mutex);
for(pVfs = vfsList; pVfs; pVfs=pVfs->pNext){
@@ -22398,7 +26114,7 @@ SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){
** Unlink a VFS from the linked list
*/
static void vfsUnlink(sqlite3_vfs *pVfs){
- assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) );
+ assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)) );
if( pVfs==0 ){
/* No-op */
}else if( vfsList==pVfs ){
@@ -22429,7 +26145,7 @@ SQLITE_API int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
if( pVfs==0 ) return SQLITE_MISUSE_BKPT;
#endif
- MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
+ MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); )
sqlite3_mutex_enter(mutex);
vfsUnlink(pVfs);
if( makeDflt || vfsList==0 ){
@@ -22453,7 +26169,7 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){
int rc = sqlite3_initialize();
if( rc ) return rc;
#endif
- MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
+ MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); )
sqlite3_mutex_enter(mutex);
vfsUnlink(pVfs);
sqlite3_mutex_leave(mutex);
@@ -22474,17 +26190,17 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){
**
*************************************************************************
**
-** This file contains code to support the concept of "benign"
+** This file contains code to support the concept of "benign"
** malloc failures (when the xMalloc() or xRealloc() method of the
** sqlite3_mem_methods structure fails to allocate a block of memory
-** and returns 0).
+** and returns 0).
**
** Most malloc failures are non-benign. After they occur, SQLite
** abandons the current operation and returns an error code (usually
** SQLITE_NOMEM) to the user. However, sometimes a fault is not necessarily
-** fatal. For example, if a malloc fails while resizing a hash table, this
-** is completely recoverable simply by not carrying out the resize. The
-** hash table will continue to function normally. So a malloc failure
+** fatal. For example, if a malloc fails while resizing a hash table, this
+** is completely recoverable simply by not carrying out the resize. The
+** hash table will continue to function normally. So a malloc failure
** during a hash table resize is a benign fault.
*/
@@ -22686,7 +26402,7 @@ static malloc_zone_t* _sqliteZone_;
#else /* if not __APPLE__ */
/*
-** Use standard C library malloc and free on non-Apple systems.
+** Use standard C library malloc and free on non-Apple systems.
** Also used by Apple systems if SQLITE_WITHOUT_ZONEMALLOC is defined.
*/
#define SQLITE_MALLOC(x) malloc(x)
@@ -22772,7 +26488,7 @@ static void *sqlite3MemMalloc(int nByte){
** or sqlite3MemRealloc().
**
** For this low-level routine, we already know that pPrior!=0 since
-** cases where pPrior==0 will have been intecepted and dealt with
+** cases where pPrior==0 will have been intercepted and dealt with
** by higher-level routines.
*/
static void sqlite3MemFree(void *pPrior){
@@ -22860,13 +26576,13 @@ static int sqlite3MemInit(void *NotUsed){
return SQLITE_OK;
}
len = sizeof(cpuCount);
- /* One usually wants to use hw.acctivecpu for MT decisions, but not here */
+ /* One usually wants to use hw.activecpu for MT decisions, but not here */
sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0);
if( cpuCount>1 ){
/* defer MT decisions to system malloc */
_sqliteZone_ = malloc_default_zone();
}else{
- /* only 1 core, use our own zone to contention over global locks,
+ /* only 1 core, use our own zone to contention over global locks,
** e.g. we have our own dedicated locks */
_sqliteZone_ = malloc_create_zone(4096, 0);
malloc_set_zone_name(_sqliteZone_, "Sqlite_Heap");
@@ -22990,7 +26706,7 @@ struct MemBlockHdr {
** when this module is combined with other in the amalgamation.
*/
static struct {
-
+
/*
** Mutex to control access to the memory allocation subsystem.
*/
@@ -23001,7 +26717,7 @@ static struct {
*/
struct MemBlockHdr *pFirst;
struct MemBlockHdr *pLast;
-
+
/*
** The number of levels of backtrace to save in new allocations.
*/
@@ -23014,7 +26730,7 @@ static struct {
int nTitle; /* Bytes of zTitle to save. Includes '\0' and padding */
char zTitle[100]; /* The title text */
- /*
+ /*
** sqlite3MallocDisallow() increments the following counter.
** sqlite3MallocAllow() decrements it.
*/
@@ -23059,7 +26775,7 @@ static void adjustStats(int iSize, int increment){
** This routine checks the guards at either end of the allocation and
** if they are incorrect it asserts.
*/
-static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){
+static struct MemBlockHdr *sqlite3MemsysGetHeader(const void *pAllocation){
struct MemBlockHdr *p;
int *pInt;
u8 *pU8;
@@ -23073,7 +26789,7 @@ static struct MemBlockHdr *sqlite3MemsysGetHeader(void *pAllocation){
pU8 = (u8*)pAllocation;
assert( pInt[nReserve/sizeof(int)]==(int)REARGUARD );
/* This checks any of the "extra" bytes allocated due
- ** to rounding up to an 8 byte boundary to ensure
+ ** to rounding up to an 8 byte boundary to ensure
** they haven't been overwritten.
*/
while( nReserve-- > p->iSize ) assert( pU8[nReserve]==0x65 );
@@ -23202,7 +26918,7 @@ static void *sqlite3MemMalloc(int nByte){
p = (void*)pInt;
}
sqlite3_mutex_leave(mem.mutex);
- return p;
+ return p;
}
/*
@@ -23212,7 +26928,7 @@ static void sqlite3MemFree(void *pPrior){
struct MemBlockHdr *pHdr;
void **pBt;
char *z;
- assert( sqlite3GlobalConfig.bMemstat || sqlite3GlobalConfig.bCoreMutex==0
+ assert( sqlite3GlobalConfig.bMemstat || sqlite3GlobalConfig.bCoreMutex==0
|| mem.mutex!=0 );
pHdr = sqlite3MemsysGetHeader(pPrior);
pBt = (void**)pHdr;
@@ -23238,15 +26954,15 @@ static void sqlite3MemFree(void *pPrior){
randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) +
(int)pHdr->iSize + sizeof(int) + pHdr->nTitle);
free(z);
- sqlite3_mutex_leave(mem.mutex);
+ sqlite3_mutex_leave(mem.mutex);
}
/*
** Change the size of an existing memory allocation.
**
** For this debugging implementation, we *always* make a copy of the
-** allocation into a new place in memory. In this way, if the
-** higher level code is using pointer to the old allocation, it is
+** allocation into a new place in memory. In this way, if the
+** higher level code is using pointer to the old allocation, it is
** much more likely to break and we are much more liking to find
** the error.
*/
@@ -23289,7 +27005,7 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
** Set the "type" of an allocation.
*/
SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){
- if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
+ if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){
struct MemBlockHdr *pHdr;
pHdr = sqlite3MemsysGetHeader(p);
assert( pHdr->iForeGuard==FOREGUARD );
@@ -23306,9 +27022,9 @@ SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){
**
** assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
*/
-SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
+SQLITE_PRIVATE int sqlite3MemdebugHasType(const void *p, u8 eType){
int rc = 1;
- if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
+ if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){
struct MemBlockHdr *pHdr;
pHdr = sqlite3MemsysGetHeader(p);
assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
@@ -23328,9 +27044,9 @@ SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
**
** assert( sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
*/
-SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){
+SQLITE_PRIVATE int sqlite3MemdebugNoType(const void *p, u8 eType){
int rc = 1;
- if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){
+ if( p && sqlite3GlobalConfig.m.xFree==sqlite3MemFree ){
struct MemBlockHdr *pHdr;
pHdr = sqlite3MemsysGetHeader(p);
assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
@@ -23380,7 +27096,7 @@ SQLITE_PRIVATE void sqlite3MemdebugSync(){
}
/*
-** Open the file indicated and write a log of all unfreed memory
+** Open the file indicated and write a log of all unfreed memory
** allocations into that log.
*/
SQLITE_PRIVATE void sqlite3MemdebugDump(const char *zFilename){
@@ -23397,7 +27113,7 @@ SQLITE_PRIVATE void sqlite3MemdebugDump(const char *zFilename){
for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){
char *z = (char*)pHdr;
z -= pHdr->nBacktraceSlots*sizeof(void*) + pHdr->nTitle;
- fprintf(out, "**** %lld bytes at %p from %s ****\n",
+ fprintf(out, "**** %lld bytes at %p from %s ****\n",
pHdr->iSize, &pHdr[1], pHdr->nTitle ? z : "???");
if( pHdr->nBacktrace ){
fflush(out);
@@ -23410,7 +27126,7 @@ SQLITE_PRIVATE void sqlite3MemdebugDump(const char *zFilename){
fprintf(out, "COUNTS:\n");
for(i=0; i<NCSIZE-1; i++){
if( mem.nAlloc[i] ){
- fprintf(out, " %5d: %10d %10d %10d\n",
+ fprintf(out, " %5d: %10d %10d %10d\n",
i*8, mem.nAlloc[i], mem.nCurrent[i], mem.mxCurrent[i]);
}
}
@@ -23451,12 +27167,12 @@ SQLITE_PRIVATE int sqlite3MemdebugMallocCount(){
**
*************************************************************************
** This file contains the C functions that implement a memory
-** allocation subsystem for use by SQLite.
+** allocation subsystem for use by SQLite.
**
** This version of the memory allocation subsystem omits all
** use of malloc(). The SQLite user supplies a block of memory
** before calling sqlite3_initialize() from which allocations
-** are made and returned by the xMalloc() and xRealloc()
+** are made and returned by the xMalloc() and xRealloc()
** implementations. Once sqlite3_initialize() has been called,
** the amount of memory available to SQLite is fixed and cannot
** be changed.
@@ -23487,8 +27203,8 @@ SQLITE_PRIVATE int sqlite3MemdebugMallocCount(){
#define N_HASH 61
/*
-** A memory allocation (also called a "chunk") consists of two or
-** more blocks where each block is 8 bytes. The first 8 bytes are
+** A memory allocation (also called a "chunk") consists of two or
+** more blocks where each block is 8 bytes. The first 8 bytes are
** a header that is not returned to the user.
**
** A chunk is two or more blocks that is either checked out or
@@ -23511,10 +27227,10 @@ SQLITE_PRIVATE int sqlite3MemdebugMallocCount(){
**
** The second block of free chunks is of the form u.list. The
** two fields form a double-linked list of chunks of related sizes.
-** Pointers to the head of the list are stored in mem3.aiSmall[]
+** Pointers to the head of the list are stored in mem3.aiSmall[]
** for smaller chunks and mem3.aiHash[] for larger chunks.
**
-** The second block of a chunk is user data if the chunk is checked
+** The second block of a chunk is user data if the chunk is checked
** out. If a chunk is checked out, the user data may extend into
** the u.hdr.prevSize value of the following chunk.
*/
@@ -23550,28 +27266,28 @@ static SQLITE_WSD struct Mem3Global {
** True if we are evaluating an out-of-memory callback.
*/
int alarmBusy;
-
+
/*
** Mutex to control access to the memory allocation subsystem.
*/
sqlite3_mutex *mutex;
-
+
/*
** The minimum amount of free space that we have seen.
*/
- u32 mnMaster;
+ u32 mnKeyBlk;
/*
- ** iMaster is the index of the master chunk. Most new allocations
- ** occur off of this chunk. szMaster is the size (in Mem3Blocks)
- ** of the current master. iMaster is 0 if there is not master chunk.
- ** The master chunk is not in either the aiHash[] or aiSmall[].
+ ** iKeyBlk is the index of the key chunk. Most new allocations
+ ** occur off of this chunk. szKeyBlk is the size (in Mem3Blocks)
+ ** of the current key chunk. iKeyBlk is 0 if there is no key chunk.
+ ** The key chunk is not in either the aiHash[] or aiSmall[].
*/
- u32 iMaster;
- u32 szMaster;
+ u32 iKeyBlk;
+ u32 szKeyBlk;
/*
- ** Array of lists of free blocks according to the block size
+ ** Array of lists of free blocks according to the block size
** for smaller chunks, or a hash on the block size for larger
** chunks.
*/
@@ -23602,7 +27318,7 @@ static void memsys3UnlinkFromList(u32 i, u32 *pRoot){
}
/*
-** Unlink the chunk at index i from
+** Unlink the chunk at index i from
** whatever list is currently a member of.
*/
static void memsys3Unlink(u32 i){
@@ -23686,8 +27402,8 @@ static void memsys3OutOfMemory(int nByte){
/*
-** Chunk i is a free chunk that has been unlinked. Adjust its
-** size parameters for check-out and return a pointer to the
+** Chunk i is a free chunk that has been unlinked. Adjust its
+** size parameters for check-out and return a pointer to the
** user portion of the chunk.
*/
static void *memsys3Checkout(u32 i, u32 nBlock){
@@ -23704,34 +27420,34 @@ static void *memsys3Checkout(u32 i, u32 nBlock){
}
/*
-** Carve a piece off of the end of the mem3.iMaster free chunk.
-** Return a pointer to the new allocation. Or, if the master chunk
+** Carve a piece off of the end of the mem3.iKeyBlk free chunk.
+** Return a pointer to the new allocation. Or, if the key chunk
** is not large enough, return 0.
*/
-static void *memsys3FromMaster(u32 nBlock){
+static void *memsys3FromKeyBlk(u32 nBlock){
assert( sqlite3_mutex_held(mem3.mutex) );
- assert( mem3.szMaster>=nBlock );
- if( nBlock>=mem3.szMaster-1 ){
- /* Use the entire master */
- void *p = memsys3Checkout(mem3.iMaster, mem3.szMaster);
- mem3.iMaster = 0;
- mem3.szMaster = 0;
- mem3.mnMaster = 0;
+ assert( mem3.szKeyBlk>=nBlock );
+ if( nBlock>=mem3.szKeyBlk-1 ){
+ /* Use the entire key chunk */
+ void *p = memsys3Checkout(mem3.iKeyBlk, mem3.szKeyBlk);
+ mem3.iKeyBlk = 0;
+ mem3.szKeyBlk = 0;
+ mem3.mnKeyBlk = 0;
return p;
}else{
- /* Split the master block. Return the tail. */
+ /* Split the key block. Return the tail. */
u32 newi, x;
- newi = mem3.iMaster + mem3.szMaster - nBlock;
- assert( newi > mem3.iMaster+1 );
- mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = nBlock;
- mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x |= 2;
+ newi = mem3.iKeyBlk + mem3.szKeyBlk - nBlock;
+ assert( newi > mem3.iKeyBlk+1 );
+ mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = nBlock;
+ mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x |= 2;
mem3.aPool[newi-1].u.hdr.size4x = nBlock*4 + 1;
- mem3.szMaster -= nBlock;
- mem3.aPool[newi-1].u.hdr.prevSize = mem3.szMaster;
- x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
- mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
- if( mem3.szMaster < mem3.mnMaster ){
- mem3.mnMaster = mem3.szMaster;
+ mem3.szKeyBlk -= nBlock;
+ mem3.aPool[newi-1].u.hdr.prevSize = mem3.szKeyBlk;
+ x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2;
+ mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x;
+ if( mem3.szKeyBlk < mem3.mnKeyBlk ){
+ mem3.mnKeyBlk = mem3.szKeyBlk;
}
return (void*)&mem3.aPool[newi];
}
@@ -23740,18 +27456,18 @@ static void *memsys3FromMaster(u32 nBlock){
/*
** *pRoot is the head of a list of free chunks of the same size
** or same size hash. In other words, *pRoot is an entry in either
-** mem3.aiSmall[] or mem3.aiHash[].
+** mem3.aiSmall[] or mem3.aiHash[].
**
** This routine examines all entries on the given list and tries
-** to coalesce each entries with adjacent free chunks.
+** to coalesce each entries with adjacent free chunks.
**
-** If it sees a chunk that is larger than mem3.iMaster, it replaces
-** the current mem3.iMaster with the new larger chunk. In order for
-** this mem3.iMaster replacement to work, the master chunk must be
+** If it sees a chunk that is larger than mem3.iKeyBlk, it replaces
+** the current mem3.iKeyBlk with the new larger chunk. In order for
+** this mem3.iKeyBlk replacement to work, the key chunk must be
** linked into the hash tables. That is not the normal state of
-** affairs, of course. The calling routine must link the master
+** affairs, of course. The calling routine must link the key
** chunk before invoking this routine, then must unlink the (possibly
-** changed) master chunk once this routine has finished.
+** changed) key chunk once this routine has finished.
*/
static void memsys3Merge(u32 *pRoot){
u32 iNext, prev, size, i, x;
@@ -23778,9 +27494,9 @@ static void memsys3Merge(u32 *pRoot){
}else{
size /= 4;
}
- if( size>mem3.szMaster ){
- mem3.iMaster = i;
- mem3.szMaster = size;
+ if( size>mem3.szKeyBlk ){
+ mem3.iKeyBlk = i;
+ mem3.szKeyBlk = size;
}
}
}
@@ -23829,26 +27545,26 @@ static void *memsys3MallocUnsafe(int nByte){
/* STEP 2:
** Try to satisfy the allocation by carving a piece off of the end
- ** of the master chunk. This step usually works if step 1 fails.
+ ** of the key chunk. This step usually works if step 1 fails.
*/
- if( mem3.szMaster>=nBlock ){
- return memsys3FromMaster(nBlock);
+ if( mem3.szKeyBlk>=nBlock ){
+ return memsys3FromKeyBlk(nBlock);
}
- /* STEP 3:
+ /* STEP 3:
** Loop through the entire memory pool. Coalesce adjacent free
- ** chunks. Recompute the master chunk as the largest free chunk.
+ ** chunks. Recompute the key chunk as the largest free chunk.
** Then try again to satisfy the allocation by carving a piece off
- ** of the end of the master chunk. This step happens very
+ ** of the end of the key chunk. This step happens very
** rarely (we hope!)
*/
for(toFree=nBlock*16; toFree<(mem3.nPool*16); toFree *= 2){
memsys3OutOfMemory(toFree);
- if( mem3.iMaster ){
- memsys3Link(mem3.iMaster);
- mem3.iMaster = 0;
- mem3.szMaster = 0;
+ if( mem3.iKeyBlk ){
+ memsys3Link(mem3.iKeyBlk);
+ mem3.iKeyBlk = 0;
+ mem3.szKeyBlk = 0;
}
for(i=0; i<N_HASH; i++){
memsys3Merge(&mem3.aiHash[i]);
@@ -23856,10 +27572,10 @@ static void *memsys3MallocUnsafe(int nByte){
for(i=0; i<MX_SMALL-1; i++){
memsys3Merge(&mem3.aiSmall[i]);
}
- if( mem3.szMaster ){
- memsys3Unlink(mem3.iMaster);
- if( mem3.szMaster>=nBlock ){
- return memsys3FromMaster(nBlock);
+ if( mem3.szKeyBlk ){
+ memsys3Unlink(mem3.iKeyBlk);
+ if( mem3.szKeyBlk>=nBlock ){
+ return memsys3FromKeyBlk(nBlock);
}
}
}
@@ -23889,23 +27605,23 @@ static void memsys3FreeUnsafe(void *pOld){
mem3.aPool[i+size-1].u.hdr.size4x &= ~2;
memsys3Link(i);
- /* Try to expand the master using the newly freed chunk */
- if( mem3.iMaster ){
- while( (mem3.aPool[mem3.iMaster-1].u.hdr.size4x&2)==0 ){
- size = mem3.aPool[mem3.iMaster-1].u.hdr.prevSize;
- mem3.iMaster -= size;
- mem3.szMaster += size;
- memsys3Unlink(mem3.iMaster);
- x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
- mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
- mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster;
+ /* Try to expand the key using the newly freed chunk */
+ if( mem3.iKeyBlk ){
+ while( (mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x&2)==0 ){
+ size = mem3.aPool[mem3.iKeyBlk-1].u.hdr.prevSize;
+ mem3.iKeyBlk -= size;
+ mem3.szKeyBlk += size;
+ memsys3Unlink(mem3.iKeyBlk);
+ x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2;
+ mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x;
+ mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = mem3.szKeyBlk;
}
- x = mem3.aPool[mem3.iMaster-1].u.hdr.size4x & 2;
- while( (mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x&1)==0 ){
- memsys3Unlink(mem3.iMaster+mem3.szMaster);
- mem3.szMaster += mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.size4x/4;
- mem3.aPool[mem3.iMaster-1].u.hdr.size4x = mem3.szMaster*4 | x;
- mem3.aPool[mem3.iMaster+mem3.szMaster-1].u.hdr.prevSize = mem3.szMaster;
+ x = mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x & 2;
+ while( (mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x&1)==0 ){
+ memsys3Unlink(mem3.iKeyBlk+mem3.szKeyBlk);
+ mem3.szKeyBlk += mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.size4x/4;
+ mem3.aPool[mem3.iKeyBlk-1].u.hdr.size4x = mem3.szKeyBlk*4 | x;
+ mem3.aPool[mem3.iKeyBlk+mem3.szKeyBlk-1].u.hdr.prevSize = mem3.szKeyBlk;
}
}
}
@@ -23943,7 +27659,7 @@ static void *memsys3Malloc(int nBytes){
memsys3Enter();
p = memsys3MallocUnsafe(nBytes);
memsys3Leave();
- return (void*)p;
+ return (void*)p;
}
/*
@@ -24001,11 +27717,11 @@ static int memsys3Init(void *NotUsed){
mem3.aPool = (Mem3Block *)sqlite3GlobalConfig.pHeap;
mem3.nPool = (sqlite3GlobalConfig.nHeap / sizeof(Mem3Block)) - 2;
- /* Initialize the master block. */
- mem3.szMaster = mem3.nPool;
- mem3.mnMaster = mem3.szMaster;
- mem3.iMaster = 1;
- mem3.aPool[0].u.hdr.size4x = (mem3.szMaster<<2) + 2;
+ /* Initialize the key block. */
+ mem3.szKeyBlk = mem3.nPool;
+ mem3.mnKeyBlk = mem3.szKeyBlk;
+ mem3.iKeyBlk = 1;
+ mem3.aPool[0].u.hdr.size4x = (mem3.szKeyBlk<<2) + 2;
mem3.aPool[mem3.nPool].u.hdr.prevSize = mem3.nPool;
mem3.aPool[mem3.nPool].u.hdr.size4x = 1;
@@ -24024,7 +27740,7 @@ static void memsys3Shutdown(void *NotUsed){
/*
-** Open the file indicated and write a log of all unfreed memory
+** Open the file indicated and write a log of all unfreed memory
** allocations into that log.
*/
SQLITE_PRIVATE void sqlite3Memsys3Dump(const char *zFilename){
@@ -24065,7 +27781,7 @@ SQLITE_PRIVATE void sqlite3Memsys3Dump(const char *zFilename){
fprintf(out, "%p %6d bytes checked out\n", &mem3.aPool[i], (size/4)*8-8);
}else{
fprintf(out, "%p %6d bytes free%s\n", &mem3.aPool[i], (size/4)*8-8,
- i==mem3.iMaster ? " **master**" : "");
+ i==mem3.iKeyBlk ? " **key**" : "");
}
}
for(i=0; i<MX_SMALL-1; i++){
@@ -24075,7 +27791,7 @@ SQLITE_PRIVATE void sqlite3Memsys3Dump(const char *zFilename){
fprintf(out, " %p(%d)", &mem3.aPool[j],
(mem3.aPool[j-1].u.hdr.size4x/4)*8-8);
}
- fprintf(out, "\n");
+ fprintf(out, "\n");
}
for(i=0; i<N_HASH; i++){
if( mem3.aiHash[i]==0 ) continue;
@@ -24084,11 +27800,11 @@ SQLITE_PRIVATE void sqlite3Memsys3Dump(const char *zFilename){
fprintf(out, " %p(%d)", &mem3.aPool[j],
(mem3.aPool[j-1].u.hdr.size4x/4)*8-8);
}
- fprintf(out, "\n");
+ fprintf(out, "\n");
}
- fprintf(out, "master=%d\n", mem3.iMaster);
- fprintf(out, "nowUsed=%d\n", mem3.nPool*8 - mem3.szMaster*8);
- fprintf(out, "mxUsed=%d\n", mem3.nPool*8 - mem3.mnMaster*8);
+ fprintf(out, "key=%d\n", mem3.iKeyBlk);
+ fprintf(out, "nowUsed=%d\n", mem3.nPool*8 - mem3.szKeyBlk*8);
+ fprintf(out, "mxUsed=%d\n", mem3.nPool*8 - mem3.mnKeyBlk*8);
sqlite3_mutex_leave(mem3.mutex);
if( out==stdout ){
fflush(stdout);
@@ -24101,7 +27817,7 @@ SQLITE_PRIVATE void sqlite3Memsys3Dump(const char *zFilename){
}
/*
-** This routine is the only routine in this file with external
+** This routine is the only routine in this file with external
** linkage.
**
** Populate the low-level memory allocation function pointers in
@@ -24141,12 +27857,12 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){
**
*************************************************************************
** This file contains the C functions that implement a memory
-** allocation subsystem for use by SQLite.
+** allocation subsystem for use by SQLite.
**
** This version of the memory allocation subsystem omits all
** use of malloc(). The application gives SQLite a block of memory
** before calling sqlite3_initialize() from which allocations
-** are made and returned by the xMalloc() and xRealloc()
+** are made and returned by the xMalloc() and xRealloc()
** implementations. Once sqlite3_initialize() has been called,
** the amount of memory available to SQLite is fixed and cannot
** be changed.
@@ -24166,12 +27882,12 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){
** This algorithm is described in: J. M. Robson. "Bounds for Some Functions
** Concerning Dynamic Storage Allocation". Journal of the Association for
** Computing Machinery, Volume 21, Number 8, July 1974, pages 491-499.
-**
+**
** Let n be the size of the largest allocation divided by the minimum
** allocation size (after rounding all sizes up to a power of 2.) Let M
** be the maximum amount of memory ever outstanding at one time. Let
** N be the total amount of memory available for allocation. Robson
-** proved that this memory allocator will never breakdown due to
+** proved that this memory allocator will never breakdown due to
** fragmentation as long as the following constraint holds:
**
** N >= M*(1 + log2(n)/2) - n + 1
@@ -24182,7 +27898,7 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){
/* #include "sqliteInt.h" */
/*
-** This version of the memory allocator is used only when
+** This version of the memory allocator is used only when
** SQLITE_ENABLE_MEMSYS5 is defined.
*/
#ifdef SQLITE_ENABLE_MEMSYS5
@@ -24227,7 +27943,7 @@ static SQLITE_WSD struct Mem5Global {
int szAtom; /* Smallest possible allocation in bytes */
int nBlock; /* Number of szAtom sized blocks in zPool */
u8 *zPool; /* Memory available to be allocated */
-
+
/*
** Mutex to control access to the memory allocation subsystem.
*/
@@ -24246,7 +27962,7 @@ static SQLITE_WSD struct Mem5Global {
u32 maxCount; /* Maximum instantaneous currentCount */
u32 maxRequest; /* Largest allocation (exclusive of internal frag) */
#endif
-
+
/*
** Lists of free blocks. aiFreelist[0] is a list of free blocks of
** size mem5.szAtom. aiFreelist[1] holds blocks of size szAtom*2.
@@ -24422,7 +28138,7 @@ static void memsys5FreeUnsafe(void *pOld){
u32 size, iLogsize;
int iBlock;
- /* Set iBlock to the index of the block pointed to by pOld in
+ /* Set iBlock to the index of the block pointed to by pOld in
** the array of mem5.szAtom byte blocks pointed to by mem5.zPool.
*/
iBlock = (int)(((u8 *)pOld-mem5.zPool)/mem5.szAtom);
@@ -24491,7 +28207,7 @@ static void *memsys5Malloc(int nBytes){
p = memsys5MallocUnsafe(nBytes);
memsys5Leave();
}
- return (void*)p;
+ return (void*)p;
}
/*
@@ -24504,14 +28220,14 @@ static void memsys5Free(void *pPrior){
assert( pPrior!=0 );
memsys5Enter();
memsys5FreeUnsafe(pPrior);
- memsys5Leave();
+ memsys5Leave();
}
/*
** Change the size of an existing memory allocation.
**
** The outer layer memory allocator prevents this routine from
-** being called with pPrior==0.
+** being called with pPrior==0.
**
** nBytes is always a value obtained from a prior call to
** memsys5Round(). Hence nBytes is always a non-negative power
@@ -24551,8 +28267,17 @@ static void *memsys5Realloc(void *pPrior, int nBytes){
*/
static int memsys5Roundup(int n){
int iFullSz;
- if( n > 0x40000000 ) return 0;
- for(iFullSz=mem5.szAtom; iFullSz<n; iFullSz *= 2);
+ if( n<=mem5.szAtom*2 ){
+ if( n<=mem5.szAtom ) return mem5.szAtom;
+ return mem5.szAtom*2;
+ }
+ if( n>0x10000000 ){
+ if( n>0x40000000 ) return 0;
+ if( n>0x20000000 ) return 0x40000000;
+ return 0x20000000;
+ }
+ for(iFullSz=mem5.szAtom*8; iFullSz<n; iFullSz *= 4);
+ if( (iFullSz/2)>=(i64)n ) return iFullSz/2;
return iFullSz;
}
@@ -24644,7 +28369,7 @@ static void memsys5Shutdown(void *NotUsed){
#ifdef SQLITE_TEST
/*
-** Open the file indicated and write a log of all unfreed memory
+** Open the file indicated and write a log of all unfreed memory
** allocations into that log.
*/
SQLITE_PRIVATE void sqlite3Memsys5Dump(const char *zFilename){
@@ -24686,7 +28411,7 @@ SQLITE_PRIVATE void sqlite3Memsys5Dump(const char *zFilename){
#endif
/*
-** This routine is the only routine in this file with external
+** This routine is the only routine in this file with external
** linkage. It returns a pointer to a static sqlite3_mem_methods
** struct populated with the memsys5 methods.
*/
@@ -24741,7 +28466,7 @@ static SQLITE_WSD int mutexIsInit = 0;
/*
** This block (enclosed by SQLITE_ENABLE_MULTITHREADED_CHECKS) contains
** the implementation of a wrapper around the system default mutex
-** implementation (sqlite3DefaultMutex()).
+** implementation (sqlite3DefaultMutex()).
**
** Most calls are passed directly through to the underlying default
** mutex implementation. Except, if a mutex is configured by calling
@@ -24752,7 +28477,7 @@ static SQLITE_WSD int mutexIsInit = 0;
** apps that usually use SQLITE_CONFIG_MULTITHREAD mode.
*/
-/*
+/*
** Type for all mutexes used when SQLITE_ENABLE_MULTITHREADED_CHECKS
** is defined. Variable CheckMutex.mutex is a pointer to the real mutex
** allocated by the system mutex implementation. Variable iType is usually set
@@ -24769,9 +28494,9 @@ struct CheckMutex {
#define SQLITE_MUTEX_WARNONCONTENTION (-1)
-/*
+/*
** Pointer to real mutex methods object used by the CheckMutex
-** implementation. Set by checkMutexInit().
+** implementation. Set by checkMutexInit().
*/
static SQLITE_WSD const sqlite3_mutex_methods *pGlobalMutexMethods;
@@ -24787,13 +28512,13 @@ static int checkMutexNotheld(sqlite3_mutex *p){
/*
** Initialize and deinitialize the mutex subsystem.
*/
-static int checkMutexInit(void){
+static int checkMutexInit(void){
pGlobalMutexMethods = sqlite3DefaultMutex();
- return SQLITE_OK;
+ return SQLITE_OK;
}
-static int checkMutexEnd(void){
+static int checkMutexEnd(void){
pGlobalMutexMethods = 0;
- return SQLITE_OK;
+ return SQLITE_OK;
}
/*
@@ -24843,7 +28568,7 @@ static void checkMutexFree(sqlite3_mutex *p){
assert( SQLITE_MUTEX_FAST<2 );
assert( SQLITE_MUTEX_WARNONCONTENTION<2 );
-#if SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( ((CheckMutex*)p)->iType<2 )
#endif
{
@@ -24867,7 +28592,7 @@ static void checkMutexEnter(sqlite3_mutex *p){
if( SQLITE_OK==pGlobalMutexMethods->xMutexTry(pCheck->mutex) ){
return;
}
- sqlite3_log(SQLITE_MISUSE,
+ sqlite3_log(SQLITE_MISUSE,
"illegal multi-threaded access to database connection"
);
}
@@ -24926,11 +28651,11 @@ SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex *p){
/*
** Initialize the mutex system.
*/
-SQLITE_PRIVATE int sqlite3MutexInit(void){
+SQLITE_PRIVATE int sqlite3MutexInit(void){
int rc = SQLITE_OK;
if( !sqlite3GlobalConfig.mutex.xMutexAlloc ){
/* If the xMutexAlloc method has not been set, then the user did not
- ** install a mutex implementation via sqlite3_config() prior to
+ ** install a mutex implementation via sqlite3_config() prior to
** sqlite3_initialize() being called. This block copies pointers to
** the default implementation into the sqlite3GlobalConfig structure.
*/
@@ -24964,6 +28689,7 @@ SQLITE_PRIVATE int sqlite3MutexInit(void){
GLOBAL(int, mutexIsInit) = 1;
#endif
+ sqlite3MemoryBarrier();
return rc;
}
@@ -25041,7 +28767,7 @@ SQLITE_API int sqlite3_mutex_try(sqlite3_mutex *p){
/*
** The sqlite3_mutex_leave() routine exits a mutex that was previously
-** entered by the same thread. The behavior is undefined if the mutex
+** entered by the same thread. The behavior is undefined if the mutex
** is not currently entered. If a NULL pointer is passed as an argument
** this function is a no-op.
*/
@@ -25110,9 +28836,9 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){
*/
static int noopMutexInit(void){ return SQLITE_OK; }
static int noopMutexEnd(void){ return SQLITE_OK; }
-static sqlite3_mutex *noopMutexAlloc(int id){
+static sqlite3_mutex *noopMutexAlloc(int id){
UNUSED_PARAMETER(id);
- return (sqlite3_mutex*)8;
+ return (sqlite3_mutex*)8;
}
static void noopMutexFree(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; }
static void noopMutexEnter(sqlite3_mutex *p){ UNUSED_PARAMETER(p); return; }
@@ -25177,7 +28903,7 @@ static int debugMutexEnd(void){ return SQLITE_OK; }
/*
** The sqlite3_mutex_alloc() routine allocates a new
** mutex and returns a pointer to it. If it returns NULL
-** that means that a mutex could not be allocated.
+** that means that a mutex could not be allocated.
*/
static sqlite3_mutex *debugMutexAlloc(int id){
static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_VFS3 - 1];
@@ -25317,7 +29043,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
/*
** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields
-** are necessary under two condidtions: (1) Debug builds and (2) using
+** are necessary under two conditions: (1) Debug builds and (2) using
** home-grown mutexes. Encapsulate these conditions into a single #define.
*/
#if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX)
@@ -25355,7 +29081,7 @@ struct sqlite3_mutex {
** there might be race conditions that can cause these routines to
** deliver incorrect results. In particular, if pthread_equal() is
** not an atomic operation, then these routines might delivery
-** incorrect results. On most platforms, pthread_equal() is a
+** incorrect results. On most platforms, pthread_equal() is a
** comparison of two integers and is therefore atomic. But we are
** told that HPUX is not such a platform. If so, then these routines
** will not always work correctly on HPUX.
@@ -25403,7 +29129,7 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
** <ul>
** <li> SQLITE_MUTEX_FAST
** <li> SQLITE_MUTEX_RECURSIVE
-** <li> SQLITE_MUTEX_STATIC_MASTER
+** <li> SQLITE_MUTEX_STATIC_MAIN
** <li> SQLITE_MUTEX_STATIC_MEM
** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
@@ -25437,7 +29163,7 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
**
** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
-** returns a different mutex on every call. But for the static
+** returns a different mutex on every call. But for the static
** mutex types, the same mutex is returned on every call that has
** the same type number.
*/
@@ -25514,7 +29240,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
*/
static void pthreadMutexFree(sqlite3_mutex *p){
assert( p->nRef==0 );
-#if SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE )
#endif
{
@@ -25548,7 +29274,7 @@ static void pthreadMutexEnter(sqlite3_mutex *p){
** is atomic - that it cannot be deceived into thinking self
** and p->owner are equal if p->owner changes between two values
** that are not equal to self while the comparison is taking place.
- ** This implementation also assumes a coherent cache - that
+ ** This implementation also assumes a coherent cache - that
** separate processes cannot read different values from the same
** address at the same time. If either of these two conditions
** are not met, then the mutexes will fail and problems will result.
@@ -25591,7 +29317,7 @@ static int pthreadMutexTry(sqlite3_mutex *p){
** is atomic - that it cannot be deceived into thinking self
** and p->owner are equal if p->owner changes between two values
** that are not equal to self while the comparison is taking place.
- ** This implementation also assumes a coherent cache - that
+ ** This implementation also assumes a coherent cache - that
** separate processes cannot read different values from the same
** address at the same time. If either of these two conditions
** are not met, then the mutexes will fail and problems will result.
@@ -25705,205 +29431,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
/*
** Include code that is common to all os_*.c files
*/
-/************** Include os_common.h in the middle of mutex_w32.c *************/
-/************** Begin file os_common.h ***************************************/
-/*
-** 2004 May 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 contains macros and a little bit of code that is common to
-** all of the platform-specific files (os_*.c) and is #included into those
-** files.
-**
-** This file should be #included by the os_*.c files only. It is not a
-** general purpose header file.
-*/
-#ifndef _OS_COMMON_H_
-#define _OS_COMMON_H_
-
-/*
-** At least two bugs have slipped in because we changed the MEMORY_DEBUG
-** macro to SQLITE_DEBUG and some older makefiles have not yet made the
-** switch. The following code should catch this problem at compile-time.
-*/
-#ifdef MEMORY_DEBUG
-# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
-#endif
-
-/*
-** Macros for performance tracing. Normally turned off. Only works
-** on i486 hardware.
-*/
-#ifdef SQLITE_PERFORMANCE_TRACE
-
-/*
-** hwtime.h contains inline assembler code for implementing
-** high-performance timing routines.
-*/
-/************** Include hwtime.h in the middle of os_common.h ****************/
-/************** Begin file hwtime.h ******************************************/
-/*
-** 2008 May 27
-**
-** 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 contains inline asm code for retrieving "high-performance"
-** counters for x86 class CPUs.
-*/
-#ifndef SQLITE_HWTIME_H
-#define SQLITE_HWTIME_H
-
-/*
-** The following routine only works on pentium-class (or newer) processors.
-** It uses the RDTSC opcode to read the cycle count value out of the
-** processor and returns that value. This can be used for high-res
-** profiling.
-*/
-#if (defined(__GNUC__) || defined(_MSC_VER)) && \
- (defined(i386) || defined(__i386__) || defined(_M_IX86))
-
- #if defined(__GNUC__)
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned int lo, hi;
- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
- return (sqlite_uint64)hi << 32 | lo;
- }
-
- #elif defined(_MSC_VER)
-
- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
- __asm {
- rdtsc
- ret ; return value at EDX:EAX
- }
- }
-
- #endif
-
-#elif (defined(__GNUC__) && defined(__x86_64__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long val;
- __asm__ __volatile__ ("rdtsc" : "=A" (val));
- return val;
- }
-
-#elif (defined(__GNUC__) && defined(__ppc__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long long retval;
- unsigned long junk;
- __asm__ __volatile__ ("\n\
- 1: mftbu %1\n\
- mftb %L0\n\
- mftbu %0\n\
- cmpw %0,%1\n\
- bne 1b"
- : "=r" (retval), "=r" (junk));
- return retval;
- }
-
-#else
-
- #error Need implementation of sqlite3Hwtime() for your platform.
-
- /*
- ** To compile without implementing sqlite3Hwtime() for your platform,
- ** you can remove the above #error and use the following
- ** stub function. You will lose timing support for many
- ** of the debugging and testing utilities, but it should at
- ** least compile and run.
- */
-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
-
-#endif
-
-#endif /* !defined(SQLITE_HWTIME_H) */
-
-/************** End of hwtime.h **********************************************/
-/************** Continuing where we left off in os_common.h ******************/
-
-static sqlite_uint64 g_start;
-static sqlite_uint64 g_elapsed;
-#define TIMER_START g_start=sqlite3Hwtime()
-#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
-#define TIMER_ELAPSED g_elapsed
-#else
-#define TIMER_START
-#define TIMER_END
-#define TIMER_ELAPSED ((sqlite_uint64)0)
-#endif
-
-/*
-** If we compile with the SQLITE_TEST macro set, then the following block
-** of code will give us the ability to simulate a disk I/O error. This
-** is used for testing the I/O recovery logic.
-*/
-#if defined(SQLITE_TEST)
-SQLITE_API extern int sqlite3_io_error_hit;
-SQLITE_API extern int sqlite3_io_error_hardhit;
-SQLITE_API extern int sqlite3_io_error_pending;
-SQLITE_API extern int sqlite3_io_error_persist;
-SQLITE_API extern int sqlite3_io_error_benign;
-SQLITE_API extern int sqlite3_diskfull_pending;
-SQLITE_API extern int sqlite3_diskfull;
-#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
-#define SimulateIOError(CODE) \
- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
- || sqlite3_io_error_pending-- == 1 ) \
- { local_ioerr(); CODE; }
-static void local_ioerr(){
- IOTRACE(("IOERR\n"));
- sqlite3_io_error_hit++;
- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
-}
-#define SimulateDiskfullError(CODE) \
- if( sqlite3_diskfull_pending ){ \
- if( sqlite3_diskfull_pending == 1 ){ \
- local_ioerr(); \
- sqlite3_diskfull = 1; \
- sqlite3_io_error_hit = 1; \
- CODE; \
- }else{ \
- sqlite3_diskfull_pending--; \
- } \
- }
-#else
-#define SimulateIOErrorBenign(X)
-#define SimulateIOError(A)
-#define SimulateDiskfullError(A)
-#endif /* defined(SQLITE_TEST) */
-
-/*
-** When testing, keep a count of the number of open files.
-*/
-#if defined(SQLITE_TEST)
-SQLITE_API extern int sqlite3_open_file_count;
-#define OpenCounter(X) sqlite3_open_file_count+=(X)
-#else
-#define OpenCounter(X)
-#endif /* defined(SQLITE_TEST) */
-
-#endif /* !defined(_OS_COMMON_H_) */
-
-/************** End of os_common.h *******************************************/
-/************** Continuing where we left off in mutex_w32.c ******************/
+/* #include "os_common.h" */
/*
** Include the header file for the Windows VFS.
@@ -26016,7 +29544,7 @@ struct sqlite3_mutex {
CRITICAL_SECTION mutex; /* Mutex controlling the lock */
int id; /* Mutex type */
#ifdef SQLITE_DEBUG
- volatile int nRef; /* Number of enterances */
+ volatile int nRef; /* Number of entrances */
volatile DWORD owner; /* Thread holding this mutex */
volatile LONG trace; /* True to trace changes */
#endif
@@ -26065,7 +29593,7 @@ SQLITE_PRIVATE void sqlite3MemoryBarrier(void){
SQLITE_MEMORY_BARRIER;
#elif defined(__GNUC__)
__sync_synchronize();
-#elif MSVC_VERSION>=1300
+#elif MSVC_VERSION>=1400
_ReadWriteBarrier();
#elif defined(MemoryBarrier)
MemoryBarrier();
@@ -26149,7 +29677,7 @@ static int winMutexEnd(void){
** <ul>
** <li> SQLITE_MUTEX_FAST
** <li> SQLITE_MUTEX_RECURSIVE
-** <li> SQLITE_MUTEX_STATIC_MASTER
+** <li> SQLITE_MUTEX_STATIC_MAIN
** <li> SQLITE_MUTEX_STATIC_MEM
** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
@@ -26413,18 +29941,26 @@ SQLITE_API int sqlite3_release_memory(int n){
}
/*
+** Default value of the hard heap limit. 0 means "no limit".
+*/
+#ifndef SQLITE_MAX_MEMORY
+# define SQLITE_MAX_MEMORY 0
+#endif
+
+/*
** State information local to the memory allocation subsystem.
*/
static SQLITE_WSD struct Mem0Global {
sqlite3_mutex *mutex; /* Mutex to serialize access */
sqlite3_int64 alarmThreshold; /* The soft heap limit */
+ sqlite3_int64 hardLimit; /* The hard upper bound on memory */
/*
** True if heap is nearly "full" where "full" is defined by the
** sqlite3_soft_heap_limit() setting.
*/
int nearlyFull;
-} mem0 = { 0, 0, 0 };
+} mem0 = { 0, SQLITE_MAX_MEMORY, SQLITE_MAX_MEMORY, 0 };
#define mem0 GLOBAL(struct Mem0Global, mem0)
@@ -26454,8 +29990,15 @@ SQLITE_API int sqlite3_memory_alarm(
#endif
/*
-** Set the soft heap-size limit for the library. Passing a zero or
-** negative value indicates no limit.
+** Set the soft heap-size limit for the library. An argument of
+** zero disables the limit. A negative argument is a no-op used to
+** obtain the return value.
+**
+** The return value is the value of the heap limit just before this
+** interface was called.
+**
+** If the hard heap limit is enabled, then the soft heap limit cannot
+** be disabled nor raised above the hard heap limit.
*/
SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
sqlite3_int64 priorLimit;
@@ -26471,9 +30014,12 @@ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
sqlite3_mutex_leave(mem0.mutex);
return priorLimit;
}
+ if( mem0.hardLimit>0 && (n>mem0.hardLimit || n==0) ){
+ n = mem0.hardLimit;
+ }
mem0.alarmThreshold = n;
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
- mem0.nearlyFull = (n>0 && n<=nUsed);
+ AtomicStore(&mem0.nearlyFull, n>0 && n<=nUsed);
sqlite3_mutex_leave(mem0.mutex);
excess = sqlite3_memory_used() - n;
if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
@@ -26485,6 +30031,37 @@ SQLITE_API void sqlite3_soft_heap_limit(int n){
}
/*
+** Set the hard heap-size limit for the library. An argument of zero
+** disables the hard heap limit. A negative argument is a no-op used
+** to obtain the return value without affecting the hard heap limit.
+**
+** The return value is the value of the hard heap limit just prior to
+** calling this interface.
+**
+** Setting the hard heap limit will also activate the soft heap limit
+** and constrain the soft heap limit to be no more than the hard heap
+** limit.
+*/
+SQLITE_API sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 n){
+ sqlite3_int64 priorLimit;
+#ifndef SQLITE_OMIT_AUTOINIT
+ int rc = sqlite3_initialize();
+ if( rc ) return -1;
+#endif
+ sqlite3_mutex_enter(mem0.mutex);
+ priorLimit = mem0.hardLimit;
+ if( n>=0 ){
+ mem0.hardLimit = n;
+ if( n<mem0.alarmThreshold || mem0.alarmThreshold==0 ){
+ mem0.alarmThreshold = n;
+ }
+ }
+ sqlite3_mutex_leave(mem0.mutex);
+ return priorLimit;
+}
+
+
+/*
** Initialize the memory allocation subsystem.
*/
SQLITE_PRIVATE int sqlite3MallocInit(void){
@@ -26492,7 +30069,6 @@ SQLITE_PRIVATE int sqlite3MallocInit(void){
if( sqlite3GlobalConfig.m.xMalloc==0 ){
sqlite3MemSetDefault();
}
- memset(&mem0, 0, sizeof(mem0));
mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512
|| sqlite3GlobalConfig.nPage<=0 ){
@@ -26510,7 +30086,7 @@ SQLITE_PRIVATE int sqlite3MallocInit(void){
** sqlite3_soft_heap_limit().
*/
SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){
- return mem0.nearlyFull;
+ return AtomicLoad(&mem0.nearlyFull);
}
/*
@@ -26544,7 +30120,7 @@ SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
}
/*
-** Trigger the alarm
+** Trigger the alarm
*/
static void sqlite3MallocAlarm(int nByte){
if( mem0.alarmThreshold<=0 ) return;
@@ -26570,21 +30146,21 @@ static void mallocWithAlarm(int n, void **pp){
** following xRoundup() call. */
nFull = sqlite3GlobalConfig.m.xRoundup(n);
-#ifdef SQLITE_MAX_MEMORY
- if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nFull>SQLITE_MAX_MEMORY ){
- *pp = 0;
- return;
- }
-#endif
-
sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
if( mem0.alarmThreshold>0 ){
sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
if( nUsed >= mem0.alarmThreshold - nFull ){
- mem0.nearlyFull = 1;
+ AtomicStore(&mem0.nearlyFull, 1);
sqlite3MallocAlarm(nFull);
+ if( mem0.hardLimit ){
+ nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
+ if( nUsed >= mem0.hardLimit - nFull ){
+ *pp = 0;
+ return;
+ }
+ }
}else{
- mem0.nearlyFull = 0;
+ AtomicStore(&mem0.nearlyFull, 0);
}
}
p = sqlite3GlobalConfig.m.xMalloc(nFull);
@@ -26603,17 +30179,33 @@ static void mallocWithAlarm(int n, void **pp){
}
/*
+** Maximum size of any single memory allocation.
+**
+** This is not a limit on the total amount of memory used. This is
+** a limit on the size parameter to sqlite3_malloc() and sqlite3_realloc().
+**
+** The upper bound is slightly less than 2GiB: 0x7ffffeff == 2,147,483,391
+** This provides a 256-byte safety margin for defense against 32-bit
+** signed integer overflow bugs when computing memory allocation sizes.
+** Paranoid applications might want to reduce the maximum allocation size
+** further for an even larger safety margin. 0x3fffffff or 0x0fffffff
+** or even smaller would be reasonable upper bounds on the size of a memory
+** allocations for most applications.
+*/
+#ifndef SQLITE_MAX_ALLOCATION_SIZE
+# define SQLITE_MAX_ALLOCATION_SIZE 2147483391
+#endif
+#if SQLITE_MAX_ALLOCATION_SIZE>2147483391
+# error Maximum size for SQLITE_MAX_ALLOCATION_SIZE is 2147483391
+#endif
+
+/*
** Allocate memory. This routine is like sqlite3_malloc() except that it
** assumes the memory subsystem has already been initialized.
*/
SQLITE_PRIVATE void *sqlite3Malloc(u64 n){
void *p;
- if( n==0 || n>=0x7fffff00 ){
- /* A memory allocation of a number of bytes which is near the maximum
- ** signed integer value might cause an integer overflow inside of the
- ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving
- ** 255 bytes of overhead. SQLite itself will never use anything near
- ** this amount. The only way to reach the limit is with sqlite3_malloc() */
+ if( n==0 || n>SQLITE_MAX_ALLOCATION_SIZE ){
p = 0;
}else if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
@@ -26648,8 +30240,8 @@ SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){
** TRUE if p is a lookaside memory allocation from db
*/
#ifndef SQLITE_OMIT_LOOKASIDE
-static int isLookaside(sqlite3 *db, void *p){
- return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd);
+static int isLookaside(sqlite3 *db, const void *p){
+ return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pTrueEnd);
}
#else
#define isLookaside(A,B) 0
@@ -26659,27 +30251,43 @@ static int isLookaside(sqlite3 *db, void *p){
** Return the size of a memory allocation previously obtained from
** sqlite3Malloc() or sqlite3_malloc().
*/
-SQLITE_PRIVATE int sqlite3MallocSize(void *p){
+SQLITE_PRIVATE int sqlite3MallocSize(const void *p){
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
- return sqlite3GlobalConfig.m.xSize(p);
+ return sqlite3GlobalConfig.m.xSize((void*)p);
+}
+static int lookasideMallocSize(sqlite3 *db, const void *p){
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ return p<db->lookaside.pMiddle ? db->lookaside.szTrue : LOOKASIDE_SMALL;
+#else
+ return db->lookaside.szTrue;
+#endif
}
-SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){
+SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, const void *p){
assert( p!=0 );
- if( db==0 || !isLookaside(db,p) ){
#ifdef SQLITE_DEBUG
- if( db==0 ){
- assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
- assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
- }else{
- assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
- assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
- }
+ if( db==0 ){
+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
+ }else if( !isLookaside(db,p) ){
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ }
#endif
- return sqlite3GlobalConfig.m.xSize(p);
- }else{
- assert( sqlite3_mutex_held(db->mutex) );
- return db->lookaside.sz;
+ if( db ){
+ if( ((uptr)p)<(uptr)(db->lookaside.pTrueEnd) ){
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){
+ assert( sqlite3_mutex_held(db->mutex) );
+ return LOOKASIDE_SMALL;
+ }
+#endif
+ if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){
+ assert( sqlite3_mutex_held(db->mutex) );
+ return db->lookaside.szTrue;
+ }
+ }
}
+ return sqlite3GlobalConfig.m.xSize((void*)p);
}
SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){
assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
@@ -26722,24 +30330,75 @@ SQLITE_PRIVATE void sqlite3DbFreeNN(sqlite3 *db, void *p){
assert( db==0 || sqlite3_mutex_held(db->mutex) );
assert( p!=0 );
if( db ){
+ if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){
+ LookasideSlot *pBuf = (LookasideSlot*)p;
+ assert( db->pnBytesFreed==0 );
+#ifdef SQLITE_DEBUG
+ memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */
+#endif
+ pBuf->pNext = db->lookaside.pSmallFree;
+ db->lookaside.pSmallFree = pBuf;
+ return;
+ }
+#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
+ if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){
+ LookasideSlot *pBuf = (LookasideSlot*)p;
+ assert( db->pnBytesFreed==0 );
+#ifdef SQLITE_DEBUG
+ memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */
+#endif
+ pBuf->pNext = db->lookaside.pFree;
+ db->lookaside.pFree = pBuf;
+ return;
+ }
+ }
if( db->pnBytesFreed ){
measureAllocationSize(db, p);
return;
}
- if( isLookaside(db, p) ){
+ }
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
+ sqlite3_free(p);
+}
+SQLITE_PRIVATE void sqlite3DbNNFreeNN(sqlite3 *db, void *p){
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
+ assert( p!=0 );
+ if( ((uptr)p)<(uptr)(db->lookaside.pEnd) ){
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ if( ((uptr)p)>=(uptr)(db->lookaside.pMiddle) ){
LookasideSlot *pBuf = (LookasideSlot*)p;
+ assert( db->pnBytesFreed==0 );
#ifdef SQLITE_DEBUG
- /* Trash all content in the buffer being freed */
- memset(p, 0xaa, db->lookaside.sz);
+ memset(p, 0xaa, LOOKASIDE_SMALL); /* Trash freed content */
+#endif
+ pBuf->pNext = db->lookaside.pSmallFree;
+ db->lookaside.pSmallFree = pBuf;
+ return;
+ }
+#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
+ if( ((uptr)p)>=(uptr)(db->lookaside.pStart) ){
+ LookasideSlot *pBuf = (LookasideSlot*)p;
+ assert( db->pnBytesFreed==0 );
+#ifdef SQLITE_DEBUG
+ memset(p, 0xaa, db->lookaside.szTrue); /* Trash freed content */
#endif
pBuf->pNext = db->lookaside.pFree;
db->lookaside.pFree = pBuf;
return;
}
}
+ if( db->pnBytesFreed ){
+ measureAllocationSize(db, p);
+ return;
+ }
assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
- assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
sqlite3_free(p);
}
@@ -26775,18 +30434,25 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){
if( nOld==nNew ){
pNew = pOld;
}else if( sqlite3GlobalConfig.bMemstat ){
+ sqlite3_int64 nUsed;
sqlite3_mutex_enter(mem0.mutex);
sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
nDiff = nNew - nOld;
- if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
+ if( nDiff>0 && (nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)) >=
mem0.alarmThreshold-nDiff ){
sqlite3MallocAlarm(nDiff);
+ if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){
+ sqlite3_mutex_leave(mem0.mutex);
+ return 0;
+ }
}
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
if( pNew==0 && mem0.alarmThreshold>0 ){
sqlite3MallocAlarm((int)nBytes);
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
}
+#endif
if( pNew ){
nNew = sqlite3MallocSize(pNew);
sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
@@ -26820,7 +30486,7 @@ SQLITE_API void *sqlite3_realloc64(void *pOld, sqlite3_uint64 n){
/*
** Allocate and zero memory.
-*/
+*/
SQLITE_PRIVATE void *sqlite3MallocZero(u64 n){
void *p = sqlite3Malloc(n);
if( p ){
@@ -26850,13 +30516,13 @@ static SQLITE_NOINLINE void *dbMallocRawFinish(sqlite3 *db, u64 n){
assert( db!=0 );
p = sqlite3Malloc(n);
if( !p ) sqlite3OomFault(db);
- sqlite3MemdebugSetType(p,
+ sqlite3MemdebugSetType(p,
(db->lookaside.bDisable==0) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP);
return p;
}
/*
-** Allocate memory, either lookaside (if possible) or heap.
+** Allocate memory, either lookaside (if possible) or heap.
** If the allocation fails, set the mallocFailed flag in
** the connection pointer.
**
@@ -26890,23 +30556,37 @@ SQLITE_PRIVATE void *sqlite3DbMallocRawNN(sqlite3 *db, u64 n){
assert( db!=0 );
assert( sqlite3_mutex_held(db->mutex) );
assert( db->pnBytesFreed==0 );
- if( db->lookaside.bDisable==0 ){
- assert( db->mallocFailed==0 );
- if( n>db->lookaside.sz ){
+ if( n>db->lookaside.sz ){
+ if( !db->lookaside.bDisable ){
db->lookaside.anStat[1]++;
- }else if( (pBuf = db->lookaside.pFree)!=0 ){
- db->lookaside.pFree = pBuf->pNext;
+ }else if( db->mallocFailed ){
+ return 0;
+ }
+ return dbMallocRawFinish(db, n);
+ }
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ if( n<=LOOKASIDE_SMALL ){
+ if( (pBuf = db->lookaside.pSmallFree)!=0 ){
+ db->lookaside.pSmallFree = pBuf->pNext;
db->lookaside.anStat[0]++;
return (void*)pBuf;
- }else if( (pBuf = db->lookaside.pInit)!=0 ){
- db->lookaside.pInit = pBuf->pNext;
+ }else if( (pBuf = db->lookaside.pSmallInit)!=0 ){
+ db->lookaside.pSmallInit = pBuf->pNext;
db->lookaside.anStat[0]++;
return (void*)pBuf;
- }else{
- db->lookaside.anStat[2]++;
}
- }else if( db->mallocFailed ){
- return 0;
+ }
+#endif
+ if( (pBuf = db->lookaside.pFree)!=0 ){
+ db->lookaside.pFree = pBuf->pNext;
+ db->lookaside.anStat[0]++;
+ return (void*)pBuf;
+ }else if( (pBuf = db->lookaside.pInit)!=0 ){
+ db->lookaside.pInit = pBuf->pNext;
+ db->lookaside.anStat[0]++;
+ return (void*)pBuf;
+ }else{
+ db->lookaside.anStat[2]++;
}
#else
assert( db!=0 );
@@ -26930,7 +30610,16 @@ SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){
assert( db!=0 );
if( p==0 ) return sqlite3DbMallocRawNN(db, n);
assert( sqlite3_mutex_held(db->mutex) );
- if( isLookaside(db,p) && n<=db->lookaside.sz ) return p;
+ if( ((uptr)p)<(uptr)db->lookaside.pEnd ){
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ if( ((uptr)p)>=(uptr)db->lookaside.pMiddle ){
+ if( n<=LOOKASIDE_SMALL ) return p;
+ }else
+#endif
+ if( ((uptr)p)>=(uptr)db->lookaside.pStart ){
+ if( n<=db->lookaside.szTrue ) return p;
+ }
+ }
return dbReallocFinish(db, p, n);
}
static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n){
@@ -26941,14 +30630,14 @@ static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n){
if( isLookaside(db, p) ){
pNew = sqlite3DbMallocRawNN(db, n);
if( pNew ){
- memcpy(pNew, p, db->lookaside.sz);
+ memcpy(pNew, p, lookasideMallocSize(db, p));
sqlite3DbFree(db, p);
}
}else{
assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
- pNew = sqlite3_realloc64(p, n);
+ pNew = sqlite3Realloc(p, n);
if( !pNew ){
sqlite3OomFault(db);
}
@@ -26973,9 +30662,9 @@ SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, u64 n){
}
/*
-** Make a copy of a string in memory obtained from sqliteMalloc(). These
+** Make a copy of a string in memory obtained from sqliteMalloc(). These
** functions call sqlite3MallocRaw() directly instead of sqliteMalloc(). This
-** is because when memory debugging is turned on, these two functions are
+** is because when memory debugging is turned on, these two functions are
** called via macros that record the current file and line number in the
** ThreadData structure.
*/
@@ -26995,11 +30684,9 @@ SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3 *db, const char *z){
SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
char *zNew;
assert( db!=0 );
- if( z==0 ){
- return 0;
- }
+ assert( z!=0 || n==0 );
assert( (n&0x7fffffff)==n );
- zNew = sqlite3DbMallocRawNN(db, n+1);
+ zNew = z ? sqlite3DbMallocRawNN(db, n+1) : 0;
if( zNew ){
memcpy(zNew, z, (size_t)n);
zNew[n] = 0;
@@ -27014,9 +30701,14 @@ SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
*/
SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){
int n;
+#ifdef SQLITE_DEBUG
+ /* Because of the way the parser works, the span is guaranteed to contain
+ ** at least one non-space character */
+ for(n=0; sqlite3Isspace(zStart[n]); n++){ assert( &zStart[n]<zEnd ); }
+#endif
while( sqlite3Isspace(zStart[0]) ) zStart++;
n = (int)(zEnd - zStart);
- while( ALWAYS(n>0) && sqlite3Isspace(zStart[n-1]) ) n--;
+ while( sqlite3Isspace(zStart[n-1]) ) n--;
return sqlite3DbStrNDup(db, zStart, n);
}
@@ -27024,8 +30716,9 @@ SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const cha
** Free any prior content in *pz and replace it with a copy of zNew.
*/
SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){
+ char *z = sqlite3DbStrDup(db, zNew);
sqlite3DbFree(db, *pz);
- *pz = sqlite3DbStrDup(db, zNew);
+ *pz = z;
}
/*
@@ -27033,15 +30726,32 @@ SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){
** has happened. This routine will set db->mallocFailed, and also
** temporarily disable the lookaside memory allocator and interrupt
** any running VDBEs.
+**
+** Always return a NULL pointer so that this routine can be invoked using
+**
+** return sqlite3OomFault(db);
+**
+** and thereby avoid unnecessary stack frame allocations for the overwhelmingly
+** common case where no OOM occurs.
*/
-SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){
+SQLITE_PRIVATE void *sqlite3OomFault(sqlite3 *db){
if( db->mallocFailed==0 && db->bBenignMalloc==0 ){
db->mallocFailed = 1;
if( db->nVdbeExec>0 ){
- db->u1.isInterrupted = 1;
+ AtomicStore(&db->u1.isInterrupted, 1);
+ }
+ DisableLookaside;
+ if( db->pParse ){
+ Parse *pParse;
+ sqlite3ErrorMsg(db->pParse, "out of memory");
+ db->pParse->rc = SQLITE_NOMEM_BKPT;
+ for(pParse=db->pParse->pOuterParse; pParse; pParse = pParse->pOuterParse){
+ pParse->nErr++;
+ pParse->rc = SQLITE_NOMEM;
+ }
}
- db->lookaside.bDisable++;
}
+ return 0;
}
/*
@@ -27054,51 +30764,54 @@ SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){
SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){
if( db->mallocFailed && db->nVdbeExec==0 ){
db->mallocFailed = 0;
- db->u1.isInterrupted = 0;
+ AtomicStore(&db->u1.isInterrupted, 0);
assert( db->lookaside.bDisable>0 );
- db->lookaside.bDisable--;
+ EnableLookaside;
}
}
/*
-** Take actions at the end of an API call to indicate an OOM error
+** Take actions at the end of an API call to deal with error codes.
*/
-static SQLITE_NOINLINE int apiOomError(sqlite3 *db){
- sqlite3OomClear(db);
- sqlite3Error(db, SQLITE_NOMEM);
- return SQLITE_NOMEM_BKPT;
+static SQLITE_NOINLINE int apiHandleError(sqlite3 *db, int rc){
+ if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
+ sqlite3OomClear(db);
+ sqlite3Error(db, SQLITE_NOMEM);
+ return SQLITE_NOMEM_BKPT;
+ }
+ return rc & db->errMask;
}
/*
-** This function must be called before exiting any API function (i.e.
+** This function must be called before exiting any API function (i.e.
** returning control to the user) that has called sqlite3_malloc or
** sqlite3_realloc.
**
** The returned value is normally a copy of the second argument to this
** function. However, if a malloc() failure has occurred since the previous
-** invocation SQLITE_NOMEM is returned instead.
+** invocation SQLITE_NOMEM is returned instead.
**
** If an OOM as occurred, then the connection error-code (the value
** returned by sqlite3_errcode()) is set to SQLITE_NOMEM.
*/
SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
/* If the db handle must hold the connection handle mutex here.
- ** Otherwise the read (and possible write) of db->mallocFailed
+ ** Otherwise the read (and possible write) of db->mallocFailed
** is unsafe, as is the call to sqlite3Error().
*/
assert( db!=0 );
assert( sqlite3_mutex_held(db->mutex) );
- if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
- return apiOomError(db);
+ if( db->mallocFailed || rc ){
+ return apiHandleError(db, rc);
}
- return rc & db->errMask;
+ return 0;
}
/************** End of malloc.c **********************************************/
/************** Begin file printf.c ******************************************/
/*
** The "printf" code that follows dates from the 1980's. It is in
-** the public domain.
+** the public domain.
**
**************************************************************************
**
@@ -27127,7 +30840,7 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '',
NULL pointers replaced by SQL NULL. %Q */
#define etTOKEN 11 /* a pointer to a Token structure */
-#define etSRCLIST 12 /* a pointer to a SrcList */
+#define etSRCITEM 12 /* a pointer to a SrcItem */
#define etPOINTER 13 /* The %p conversion */
#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */
#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
@@ -27193,48 +30906,24 @@ static const et_info fmtinfo[] = {
/* All the rest are undocumented and are for internal use only */
{ 'T', 0, 0, etTOKEN, 0, 0 },
- { 'S', 0, 0, etSRCLIST, 0, 0 },
+ { 'S', 0, 0, etSRCITEM, 0, 0 },
{ 'r', 10, 1, etORDINAL, 0, 0 },
};
-/*
-** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point
-** conversions will work.
-*/
-#ifndef SQLITE_OMIT_FLOATING_POINT
-/*
-** "*val" is a double such that 0.1 <= *val < 10.0
-** Return the ascii code for the leading digit of *val, then
-** multiply "*val" by 10.0 to renormalize.
+/* Notes:
**
-** Example:
-** input: *val = 3.14159
-** output: *val = 1.4159 function return = '3'
-**
-** The counter *cnt is incremented each time. After counter exceeds
-** 16 (the number of significant digits in a 64-bit float) '0' is
-** always returned.
-*/
-static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
- int digit;
- LONGDOUBLE_TYPE d;
- if( (*cnt)<=0 ) return '0';
- (*cnt)--;
- digit = (int)*val;
- d = digit;
- digit += '0';
- *val = (*val - d)*10.0;
- return (char)digit;
-}
-#endif /* SQLITE_OMIT_FLOATING_POINT */
+** %S Takes a pointer to SrcItem. Shows name or database.name
+** %!S Like %S but prefer the zName over the zAlias
+*/
/*
** Set the StrAccum object to an error mode.
*/
-static void setStrAccumError(StrAccum *p, u8 eError){
+SQLITE_PRIVATE void sqlite3StrAccumSetError(StrAccum *p, u8 eError){
assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG );
p->accError = eError;
- p->nAlloc = 0;
+ if( p->mxAlloc ) sqlite3_str_reset(p);
+ if( eError==SQLITE_TOOBIG ) sqlite3ErrorToParser(p->db, eError);
}
/*
@@ -27253,6 +30942,28 @@ static char *getTextArg(PrintfArguments *p){
return (char*)sqlite3_value_text(p->apArg[p->nUsed++]);
}
+/*
+** Allocate memory for a temporary buffer needed for printf rendering.
+**
+** If the requested size of the temp buffer is larger than the size
+** of the output buffer in pAccum, then cause an SQLITE_TOOBIG error.
+** Do the size check before the memory allocation to prevent rogue
+** SQL from requesting large allocations using the precision or width
+** field of the printf() function.
+*/
+static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){
+ char *z;
+ if( pAccum->accError ) return 0;
+ if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){
+ sqlite3StrAccumSetError(pAccum, SQLITE_TOOBIG);
+ return 0;
+ }
+ z = sqlite3DbMallocRaw(pAccum->db, n);
+ if( z==0 ){
+ sqlite3StrAccumSetError(pAccum, SQLITE_NOMEM);
+ }
+ return z;
+}
/*
** On machines with a small stack size, you can redefine the
@@ -27264,6 +30975,13 @@ static char *getTextArg(PrintfArguments *p){
#define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */
/*
+** Hard limit on the precision of floating-point conversions.
+*/
+#ifndef SQLITE_PRINTF_PRECISION_LIMIT
+# define SQLITE_FP_PRECISION_LIMIT 100000000
+#endif
+
+/*
** Render a string given by "fmt" into the StrAccum object.
*/
SQLITE_API void sqlite3_str_vappendf(
@@ -27289,22 +31007,19 @@ SQLITE_API void sqlite3_str_vappendf(
u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
sqlite_uint64 longvalue; /* Value for integer types */
- LONGDOUBLE_TYPE realvalue; /* Value for real types */
+ double realvalue; /* Value for real types */
const et_info *infop; /* Pointer to the appropriate info structure */
char *zOut; /* Rendering buffer */
int nOut; /* Size of the rendering buffer */
char *zExtra = 0; /* Malloced memory used by some conversion */
-#ifndef SQLITE_OMIT_FLOATING_POINT
- int exp, e2; /* exponent of real numbers */
- int nsd; /* Number of significant digits returned */
- double rounder; /* Used for rounding floating point values */
+ int exp, e2; /* exponent of real numbers */
etByte flag_dp; /* True if decimal point should be shown */
etByte flag_rtz; /* True if trailing zeros should be removed */
-#endif
+
PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
char buf[etBUFSIZE]; /* Conversion buffer */
- /* pAccum never starts out with an empty buffer that was obtained from
+ /* pAccum never starts out with an empty buffer that was obtained from
** malloc(). This precondition is required by the mprintf("%z...")
** optimization. */
assert( pAccum->nChar>0 || (pAccum->printfFlags&SQLITE_PRINTF_MALLOCED)==0 );
@@ -27335,6 +31050,9 @@ SQLITE_API void sqlite3_str_vappendf(
flag_leftjustify = flag_prefix = cThousand =
flag_alternateform = flag_altform2 = flag_zeropad = 0;
done = 0;
+ width = 0;
+ flag_long = 0;
+ precision = -1;
do{
switch( c ){
case '-': flag_leftjustify = 1; break;
@@ -27345,80 +31063,93 @@ SQLITE_API void sqlite3_str_vappendf(
case '0': flag_zeropad = 1; break;
case ',': cThousand = ','; break;
default: done = 1; break;
- }
- }while( !done && (c=(*++fmt))!=0 );
- /* Get the field width */
- if( c=='*' ){
- if( bArgList ){
- width = (int)getIntArg(pArgList);
- }else{
- width = va_arg(ap,int);
- }
- if( width<0 ){
- flag_leftjustify = 1;
- width = width >= -2147483647 ? -width : 0;
- }
- c = *++fmt;
- }else{
- unsigned wx = 0;
- while( c>='0' && c<='9' ){
- wx = wx*10 + c - '0';
- c = *++fmt;
- }
- testcase( wx>0x7fffffff );
- width = wx & 0x7fffffff;
- }
- assert( width>=0 );
+ case 'l': {
+ flag_long = 1;
+ c = *++fmt;
+ if( c=='l' ){
+ c = *++fmt;
+ flag_long = 2;
+ }
+ done = 1;
+ break;
+ }
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': {
+ unsigned wx = c - '0';
+ while( (c = *++fmt)>='0' && c<='9' ){
+ wx = wx*10 + c - '0';
+ }
+ testcase( wx>0x7fffffff );
+ width = wx & 0x7fffffff;
#ifdef SQLITE_PRINTF_PRECISION_LIMIT
- if( width>SQLITE_PRINTF_PRECISION_LIMIT ){
- width = SQLITE_PRINTF_PRECISION_LIMIT;
- }
+ if( width>SQLITE_PRINTF_PRECISION_LIMIT ){
+ width = SQLITE_PRINTF_PRECISION_LIMIT;
+ }
#endif
-
- /* Get the precision */
- if( c=='.' ){
- c = *++fmt;
- if( c=='*' ){
- if( bArgList ){
- precision = (int)getIntArg(pArgList);
- }else{
- precision = va_arg(ap,int);
+ if( c!='.' && c!='l' ){
+ done = 1;
+ }else{
+ fmt--;
+ }
+ break;
}
- c = *++fmt;
- if( precision<0 ){
- precision = precision >= -2147483647 ? -precision : -1;
+ case '*': {
+ if( bArgList ){
+ width = (int)getIntArg(pArgList);
+ }else{
+ width = va_arg(ap,int);
+ }
+ if( width<0 ){
+ flag_leftjustify = 1;
+ width = width >= -2147483647 ? -width : 0;
+ }
+#ifdef SQLITE_PRINTF_PRECISION_LIMIT
+ if( width>SQLITE_PRINTF_PRECISION_LIMIT ){
+ width = SQLITE_PRINTF_PRECISION_LIMIT;
+ }
+#endif
+ if( (c = fmt[1])!='.' && c!='l' ){
+ c = *++fmt;
+ done = 1;
+ }
+ break;
}
- }else{
- unsigned px = 0;
- while( c>='0' && c<='9' ){
- px = px*10 + c - '0';
+ case '.': {
c = *++fmt;
- }
- testcase( px>0x7fffffff );
- precision = px & 0x7fffffff;
- }
- }else{
- precision = -1;
- }
- assert( precision>=(-1) );
+ if( c=='*' ){
+ if( bArgList ){
+ precision = (int)getIntArg(pArgList);
+ }else{
+ precision = va_arg(ap,int);
+ }
+ if( precision<0 ){
+ precision = precision >= -2147483647 ? -precision : -1;
+ }
+ c = *++fmt;
+ }else{
+ unsigned px = 0;
+ while( c>='0' && c<='9' ){
+ px = px*10 + c - '0';
+ c = *++fmt;
+ }
+ testcase( px>0x7fffffff );
+ precision = px & 0x7fffffff;
+ }
#ifdef SQLITE_PRINTF_PRECISION_LIMIT
- if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){
- precision = SQLITE_PRINTF_PRECISION_LIMIT;
- }
+ if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){
+ precision = SQLITE_PRINTF_PRECISION_LIMIT;
+ }
#endif
-
-
- /* Get the conversion type modifier */
- if( c=='l' ){
- flag_long = 1;
- c = *++fmt;
- if( c=='l' ){
- flag_long = 2;
- c = *++fmt;
+ if( c=='l' ){
+ --fmt;
+ }else{
+ done = 1;
+ }
+ break;
+ }
}
- }else{
- flag_long = 0;
- }
+ }while( !done && (c=(*++fmt))!=0 );
+
/* Fetch the info entry for the field */
infop = &fmtinfo[0];
xtype = etINVALID;
@@ -27447,15 +31178,17 @@ SQLITE_API void sqlite3_str_vappendf(
** xtype The class of the conversion.
** infop Pointer to the appropriate info struct.
*/
+ assert( width>=0 );
+ assert( precision>=(-1) );
switch( xtype ){
case etPOINTER:
flag_long = sizeof(char*)==sizeof(i64) ? 2 :
sizeof(char*)==sizeof(long int) ? 1 : 0;
- /* Fall through into the next case */
+ /* no break */ deliberate_fall_through
case etORDINAL:
- case etRADIX:
+ case etRADIX:
cThousand = 0;
- /* Fall through into the next case */
+ /* no break */ deliberate_fall_through
case etDECIMAL:
if( infop->flags & FLAG_SIGNED ){
i64 v;
@@ -27471,11 +31204,10 @@ SQLITE_API void sqlite3_str_vappendf(
v = va_arg(ap,int);
}
if( v<0 ){
- if( v==SMALLEST_INT64 ){
- longvalue = ((u64)1)<<63;
- }else{
- longvalue = -v;
- }
+ testcase( v==SMALLEST_INT64 );
+ testcase( v==(-1) );
+ longvalue = ~v;
+ longvalue++;
prefix = '-';
}else{
longvalue = v;
@@ -27503,12 +31235,11 @@ SQLITE_API void sqlite3_str_vappendf(
nOut = etBUFSIZE;
zOut = buf;
}else{
- u64 n = (u64)precision + 10 + precision/3;
- zOut = zExtra = sqlite3Malloc( n );
- if( zOut==0 ){
- setStrAccumError(pAccum, SQLITE_NOMEM);
- return;
- }
+ u64 n;
+ n = (u64)precision + 10;
+ if( cThousand ) n += precision/3;
+ zOut = zExtra = printfTempBuf(pAccum, n);
+ if( zOut==0 ) return;
nOut = (int)n;
}
bufpt = &zOut[nOut-1];
@@ -27559,58 +31290,66 @@ SQLITE_API void sqlite3_str_vappendf(
break;
case etFLOAT:
case etEXP:
- case etGENERIC:
+ case etGENERIC: {
+ FpDecode s;
+ int iRound;
+ int j;
+
if( bArgList ){
realvalue = getDoubleArg(pArgList);
}else{
realvalue = va_arg(ap,double);
}
-#ifdef SQLITE_OMIT_FLOATING_POINT
- length = 0;
-#else
if( precision<0 ) precision = 6; /* Set default precision */
- if( realvalue<0.0 ){
- realvalue = -realvalue;
- prefix = '-';
- }else{
- prefix = flag_prefix;
+#ifdef SQLITE_FP_PRECISION_LIMIT
+ if( precision>SQLITE_FP_PRECISION_LIMIT ){
+ precision = SQLITE_FP_PRECISION_LIMIT;
}
- if( xtype==etGENERIC && precision>0 ) precision--;
- testcase( precision>0xfff );
- for(idx=precision&0xfff, rounder=0.5; idx>0; idx--, rounder*=0.1){}
- if( xtype==etFLOAT ) realvalue += rounder;
- /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
- exp = 0;
- if( sqlite3IsNaN((double)realvalue) ){
- bufpt = "NaN";
- length = 3;
- break;
+#endif
+ if( xtype==etFLOAT ){
+ iRound = -precision;
+ }else if( xtype==etGENERIC ){
+ iRound = precision;
+ }else{
+ iRound = precision+1;
}
- if( realvalue>0.0 ){
- LONGDOUBLE_TYPE scale = 1.0;
- while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;}
- while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; }
- while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; }
- realvalue /= scale;
- while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
- while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
- if( exp>350 ){
+ sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16);
+ if( s.isSpecial ){
+ if( s.isSpecial==2 ){
+ bufpt = flag_zeropad ? "null" : "NaN";
+ length = sqlite3Strlen30(bufpt);
+ break;
+ }else if( flag_zeropad ){
+ s.z[0] = '9';
+ s.iDP = 1000;
+ s.n = 1;
+ }else{
+ memcpy(buf, "-Inf", 5);
bufpt = buf;
- buf[0] = prefix;
- memcpy(buf+(prefix!=0),"Inf",4);
- length = 3+(prefix!=0);
+ if( s.sign=='-' ){
+ /* no-op */
+ }else if( flag_prefix ){
+ buf[0] = flag_prefix;
+ }else{
+ bufpt++;
+ }
+ length = sqlite3Strlen30(bufpt);
break;
}
}
- bufpt = buf;
+ if( s.sign=='-' ){
+ prefix = '-';
+ }else{
+ prefix = flag_prefix;
+ }
+
+ exp = s.iDP-1;
+ if( xtype==etGENERIC && precision>0 ) precision--;
+
/*
** If the field type is etGENERIC, then convert to either etEXP
** or etFLOAT, as appropriate.
*/
- if( xtype!=etFLOAT ){
- realvalue += rounder;
- if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
- }
if( xtype==etGENERIC ){
flag_rtz = !flag_alternateform;
if( exp<-4 || exp>precision ){
@@ -27625,29 +31364,32 @@ SQLITE_API void sqlite3_str_vappendf(
if( xtype==etEXP ){
e2 = 0;
}else{
- e2 = exp;
+ e2 = s.iDP - 1;
}
- if( MAX(e2,0)+(i64)precision+(i64)width > etBUFSIZE - 15 ){
- bufpt = zExtra
- = sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 );
- if( bufpt==0 ){
- setStrAccumError(pAccum, SQLITE_NOMEM);
- return;
+ bufpt = buf;
+ {
+ i64 szBufNeeded; /* Size of a temporary buffer needed */
+ szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15;
+ if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3;
+ if( szBufNeeded > etBUFSIZE ){
+ bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded);
+ if( bufpt==0 ) return;
}
}
zOut = bufpt;
- nsd = 16 + flag_altform2*10;
flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
/* The sign in front of the number */
if( prefix ){
*(bufpt++) = prefix;
}
/* Digits prior to the decimal point */
+ j = 0;
if( e2<0 ){
*(bufpt++) = '0';
}else{
for(; e2>=0; e2--){
- *(bufpt++) = et_getdigit(&realvalue,&nsd);
+ *(bufpt++) = j<s.n ? s.z[j++] : '0';
+ if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ',';
}
}
/* The decimal point */
@@ -27656,13 +31398,12 @@ SQLITE_API void sqlite3_str_vappendf(
}
/* "0" digits after the decimal point but before the first
** significant digit of the number */
- for(e2++; e2<0; precision--, e2++){
- assert( precision>0 );
+ for(e2++; e2<0 && precision>0; precision--, e2++){
*(bufpt++) = '0';
}
/* Significant digits after the decimal point */
while( (precision--)>0 ){
- *(bufpt++) = et_getdigit(&realvalue,&nsd);
+ *(bufpt++) = j<s.n ? s.z[j++] : '0';
}
/* Remove trailing zeros and the "." if no digits follow the "." */
if( flag_rtz && flag_dp ){
@@ -27678,6 +31419,7 @@ SQLITE_API void sqlite3_str_vappendf(
}
/* Add the "eNNN" suffix */
if( xtype==etEXP ){
+ exp = s.iDP - 1;
*(bufpt++) = aDigits[infop->charset];
if( exp<0 ){
*(bufpt++) = '-'; exp = -exp;
@@ -27711,8 +31453,8 @@ SQLITE_API void sqlite3_str_vappendf(
while( nPad-- ) bufpt[i++] = '0';
length = width;
}
-#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */
break;
+ }
case etSIZE:
if( !bArgList ){
*(va_arg(ap,int*)) = pAccum->nChar;
@@ -27761,13 +31503,26 @@ SQLITE_API void sqlite3_str_vappendf(
}
}
if( precision>1 ){
+ i64 nPrior = 1;
width -= precision-1;
if( width>1 && !flag_leftjustify ){
sqlite3_str_appendchar(pAccum, width-1, ' ');
width = 0;
}
- while( precision-- > 1 ){
- sqlite3_str_append(pAccum, buf, length);
+ sqlite3_str_append(pAccum, buf, length);
+ precision--;
+ while( precision > 1 ){
+ i64 nCopyBytes;
+ if( nPrior > precision-1 ) nPrior = precision - 1;
+ nCopyBytes = length*nPrior;
+ if( nCopyBytes + pAccum->nChar >= pAccum->nAlloc ){
+ sqlite3StrAccumEnlarge(pAccum, nCopyBytes);
+ }
+ if( pAccum->accError ) break;
+ sqlite3_str_append(pAccum,
+ &pAccum->zText[pAccum->nChar-nCopyBytes], nCopyBytes);
+ precision -= nPrior;
+ nPrior *= 2;
}
}
bufpt = buf;
@@ -27828,8 +31583,8 @@ SQLITE_API void sqlite3_str_vappendf(
case etSQLESCAPE: /* %q: Escape ' characters */
case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */
case etSQLESCAPE3: { /* %w: Escape " characters */
- int i, j, k, n, isnull;
- int needQuote;
+ i64 i, j, k, n;
+ int needQuote, isnull;
char ch;
char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
char *escarg;
@@ -27841,7 +31596,7 @@ SQLITE_API void sqlite3_str_vappendf(
}
isnull = escarg==0;
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
- /* For %q, %Q, and %w, the precision is the number of byte (or
+ /* For %q, %Q, and %w, the precision is the number of bytes (or
** characters if the ! flags is present) to use from the input.
** Because of the extra quoting characters inserted, the number
** of output characters may be larger than the precision.
@@ -27856,11 +31611,8 @@ SQLITE_API void sqlite3_str_vappendf(
needQuote = !isnull && xtype==etSQLESCAPE2;
n += i + 3;
if( n>etBUFSIZE ){
- bufpt = zExtra = sqlite3Malloc( n );
- if( bufpt==0 ){
- setStrAccumError(pAccum, SQLITE_NOMEM);
- return;
- }
+ bufpt = zExtra = printfTempBuf(pAccum, n);
+ if( bufpt==0 ) return;
}else{
bufpt = buf;
}
@@ -27877,31 +31629,50 @@ SQLITE_API void sqlite3_str_vappendf(
goto adjust_width_for_utf8;
}
case etTOKEN: {
- Token *pToken;
if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return;
- pToken = va_arg(ap, Token*);
- assert( bArgList==0 );
- if( pToken && pToken->n ){
- sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n);
+ if( flag_alternateform ){
+ /* %#T means an Expr pointer that uses Expr.u.zToken */
+ Expr *pExpr = va_arg(ap,Expr*);
+ if( ALWAYS(pExpr) && ALWAYS(!ExprHasProperty(pExpr,EP_IntValue)) ){
+ sqlite3_str_appendall(pAccum, (const char*)pExpr->u.zToken);
+ sqlite3RecordErrorOffsetOfExpr(pAccum->db, pExpr);
+ }
+ }else{
+ /* %T means a Token pointer */
+ Token *pToken = va_arg(ap, Token*);
+ assert( bArgList==0 );
+ if( pToken && pToken->n ){
+ sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n);
+ sqlite3RecordErrorByteOffset(pAccum->db, pToken->z);
+ }
}
length = width = 0;
break;
}
- case etSRCLIST: {
- SrcList *pSrc;
- int k;
- struct SrcList_item *pItem;
+ case etSRCITEM: {
+ SrcItem *pItem;
if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return;
- pSrc = va_arg(ap, SrcList*);
- k = va_arg(ap, int);
- pItem = &pSrc->a[k];
+ pItem = va_arg(ap, SrcItem*);
assert( bArgList==0 );
- assert( k>=0 && k<pSrc->nSrc );
- if( pItem->zDatabase ){
- sqlite3_str_appendall(pAccum, pItem->zDatabase);
- sqlite3_str_append(pAccum, ".", 1);
+ if( pItem->zAlias && !flag_altform2 ){
+ sqlite3_str_appendall(pAccum, pItem->zAlias);
+ }else if( pItem->zName ){
+ if( pItem->zDatabase ){
+ sqlite3_str_appendall(pAccum, pItem->zDatabase);
+ sqlite3_str_append(pAccum, ".", 1);
+ }
+ sqlite3_str_appendall(pAccum, pItem->zName);
+ }else if( pItem->zAlias ){
+ sqlite3_str_appendall(pAccum, pItem->zAlias);
+ }else{
+ Select *pSel = pItem->pSelect;
+ assert( pSel!=0 );
+ if( pSel->selFlags & SF_NestedFrom ){
+ sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId);
+ }else{
+ sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId);
+ }
}
- sqlite3_str_appendall(pAccum, pItem->zName);
length = width = 0;
break;
}
@@ -27934,6 +31705,44 @@ SQLITE_API void sqlite3_str_vappendf(
}/* End for loop over the format string */
} /* End of function */
+
+/*
+** The z string points to the first character of a token that is
+** associated with an error. If db does not already have an error
+** byte offset recorded, try to compute the error byte offset for
+** z and set the error byte offset in db.
+*/
+SQLITE_PRIVATE void sqlite3RecordErrorByteOffset(sqlite3 *db, const char *z){
+ const Parse *pParse;
+ const char *zText;
+ const char *zEnd;
+ assert( z!=0 );
+ if( NEVER(db==0) ) return;
+ if( db->errByteOffset!=(-2) ) return;
+ pParse = db->pParse;
+ if( NEVER(pParse==0) ) return;
+ zText =pParse->zTail;
+ if( NEVER(zText==0) ) return;
+ zEnd = &zText[strlen(zText)];
+ if( SQLITE_WITHIN(z,zText,zEnd) ){
+ db->errByteOffset = (int)(z-zText);
+ }
+}
+
+/*
+** If pExpr has a byte offset for the start of a token, record that as
+** as the error offset.
+*/
+SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExpr){
+ while( pExpr
+ && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0)
+ ){
+ pExpr = pExpr->pLeft;
+ }
+ if( pExpr==0 ) return;
+ db->errByteOffset = pExpr->w.iOfst;
+}
+
/*
** Enlarge the memory allocation on a StrAccum object so that it is
** able to accept at least N more bytes of text.
@@ -27941,22 +31750,20 @@ SQLITE_API void sqlite3_str_vappendf(
** Return the number of bytes of text that StrAccum is able to accept
** after the attempted enlargement. The value returned might be zero.
*/
-static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
+SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum *p, i64 N){
char *zNew;
- assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */
+ assert( p->nChar+N >= p->nAlloc ); /* Only called if really needed */
if( p->accError ){
testcase(p->accError==SQLITE_TOOBIG);
testcase(p->accError==SQLITE_NOMEM);
return 0;
}
if( p->mxAlloc==0 ){
- N = p->nAlloc - p->nChar - 1;
- setStrAccumError(p, SQLITE_TOOBIG);
- return N;
+ sqlite3StrAccumSetError(p, SQLITE_TOOBIG);
+ return p->nAlloc - p->nChar - 1;
}else{
char *zOld = isMalloced(p) ? p->zText : 0;
- i64 szNew = p->nChar;
- szNew += N + 1;
+ i64 szNew = p->nChar + N + 1;
if( szNew+p->nChar<=p->mxAlloc ){
/* Force exponential buffer size growth as long as it does not overflow,
** to avoid having to call this routine too often */
@@ -27964,7 +31771,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
}
if( szNew > p->mxAlloc ){
sqlite3_str_reset(p);
- setStrAccumError(p, SQLITE_TOOBIG);
+ sqlite3StrAccumSetError(p, SQLITE_TOOBIG);
return 0;
}else{
p->nAlloc = (int)szNew;
@@ -27972,7 +31779,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
if( p->db ){
zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
}else{
- zNew = sqlite3_realloc64(zOld, p->nAlloc);
+ zNew = sqlite3Realloc(zOld, p->nAlloc);
}
if( zNew ){
assert( p->zText!=0 || p->nChar==0 );
@@ -27982,11 +31789,12 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
sqlite3_str_reset(p);
- setStrAccumError(p, SQLITE_NOMEM);
+ sqlite3StrAccumSetError(p, SQLITE_NOMEM);
return 0;
}
}
- return N;
+ assert( N>=0 && N<=0x7fffffff );
+ return (int)N;
}
/*
@@ -28024,7 +31832,7 @@ SQLITE_API void sqlite3_str_append(sqlite3_str *p, const char *z, int N){
assert( z!=0 || N==0 );
assert( p->zText!=0 || p->nChar==0 || p->accError );
assert( N>=0 );
- assert( p->accError==0 || p->nAlloc==0 );
+ assert( p->accError==0 || p->nAlloc==0 || p->mxAlloc==0 );
if( p->nChar+N >= p->nAlloc ){
enlargeAndAppend(p,z,N);
}else if( N ){
@@ -28055,7 +31863,7 @@ static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){
memcpy(zText, p->zText, p->nChar+1);
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
- setStrAccumError(p, SQLITE_NOMEM);
+ sqlite3StrAccumSetError(p, SQLITE_NOMEM);
}
p->zText = zText;
return zText;
@@ -28071,6 +31879,22 @@ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){
}
/*
+** Use the content of the StrAccum passed as the second argument
+** as the result of an SQL function.
+*/
+SQLITE_PRIVATE void sqlite3ResultStrAccum(sqlite3_context *pCtx, StrAccum *p){
+ if( p->accError ){
+ sqlite3_result_error_code(pCtx, p->accError);
+ sqlite3_str_reset(p);
+ }else if( isMalloced(p) ){
+ sqlite3_result_text(pCtx, p->zText, p->nChar, SQLITE_DYNAMIC);
+ }else{
+ sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC);
+ sqlite3_str_reset(p);
+ }
+}
+
+/*
** This singleton is an sqlite3_str object that is returned if
** sqlite3_malloc() fails to provide space for a real one. This
** sqlite3_str object accepts no new text and always returns
@@ -28201,7 +32025,7 @@ SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){
char zBase[SQLITE_PRINT_BUF_SIZE];
StrAccum acc;
-#ifdef SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( zFormat==0 ){
(void)SQLITE_MISUSE_BKPT;
return 0;
@@ -28261,12 +32085,22 @@ SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_li
return zBuf;
}
SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
- char *z;
+ StrAccum acc;
va_list ap;
+ if( n<=0 ) return zBuf;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( zBuf==0 || zFormat==0 ) {
+ (void)SQLITE_MISUSE_BKPT;
+ if( zBuf ) zBuf[0] = 0;
+ return zBuf;
+ }
+#endif
+ sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
va_start(ap,zFormat);
- z = sqlite3_vsnprintf(n, zBuf, zFormat, ap);
+ sqlite3_str_vappendf(&acc, zFormat, ap);
va_end(ap);
- return z;
+ zBuf[acc.nChar] = 0;
+ return zBuf;
}
/*
@@ -28314,7 +32148,7 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...){
SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){
va_list ap;
StrAccum acc;
- char zBuf[500];
+ char zBuf[SQLITE_PRINT_BUF_SIZE*10];
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
va_start(ap,zFormat);
sqlite3_str_vappendf(&acc, zFormat, ap);
@@ -28344,6 +32178,75 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){
va_end(ap);
}
+
+/*****************************************************************************
+** Reference counted string/blob storage
+*****************************************************************************/
+
+/*
+** Increase the reference count of the string by one.
+**
+** The input parameter is returned.
+*/
+SQLITE_PRIVATE char *sqlite3RCStrRef(char *z){
+ RCStr *p = (RCStr*)z;
+ assert( p!=0 );
+ p--;
+ p->nRCRef++;
+ return z;
+}
+
+/*
+** Decrease the reference count by one. Free the string when the
+** reference count reaches zero.
+*/
+SQLITE_PRIVATE void sqlite3RCStrUnref(void *z){
+ RCStr *p = (RCStr*)z;
+ assert( p!=0 );
+ p--;
+ assert( p->nRCRef>0 );
+ if( p->nRCRef>=2 ){
+ p->nRCRef--;
+ }else{
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Create a new string that is capable of holding N bytes of text, not counting
+** the zero byte at the end. The string is uninitialized.
+**
+** The reference count is initially 1. Call sqlite3RCStrUnref() to free the
+** newly allocated string.
+**
+** This routine returns 0 on an OOM.
+*/
+SQLITE_PRIVATE char *sqlite3RCStrNew(u64 N){
+ RCStr *p = sqlite3_malloc64( N + sizeof(*p) + 1 );
+ if( p==0 ) return 0;
+ p->nRCRef = 1;
+ return (char*)&p[1];
+}
+
+/*
+** Change the size of the string so that it is able to hold N bytes.
+** The string might be reallocated, so return the new allocation.
+*/
+SQLITE_PRIVATE char *sqlite3RCStrResize(char *z, u64 N){
+ RCStr *p = (RCStr*)z;
+ RCStr *pNew;
+ assert( p!=0 );
+ p--;
+ assert( p->nRCRef==1 );
+ pNew = sqlite3_realloc64(p, N+sizeof(RCStr)+1);
+ if( pNew==0 ){
+ sqlite3_free(p);
+ return 0;
+ }else{
+ return (char*)&pNew[1];
+ }
+}
+
/************** End of printf.c **********************************************/
/************** Begin file treeview.c ****************************************/
/*
@@ -28360,7 +32263,7 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){
**
** This file contains C code to implement the TreeView debugging routines.
** These routines print a parse tree to standard output for debugging and
-** analysis.
+** analysis.
**
** The interfaces in this file is only available when compiling
** with SQLITE_DEBUG.
@@ -28372,40 +32275,44 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){
** Add a new subitem to the tree. The moreToFollow flag indicates that this
** is not the last item in the tree.
*/
-static TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){
+static void sqlite3TreeViewPush(TreeView **pp, u8 moreToFollow){
+ TreeView *p = *pp;
if( p==0 ){
- p = sqlite3_malloc64( sizeof(*p) );
- if( p==0 ) return 0;
+ *pp = p = sqlite3_malloc64( sizeof(*p) );
+ if( p==0 ) return;
memset(p, 0, sizeof(*p));
}else{
p->iLevel++;
}
assert( moreToFollow==0 || moreToFollow==1 );
- if( p->iLevel<sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow;
- return p;
+ if( p->iLevel<(int)sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow;
}
/*
** Finished with one layer of the tree
*/
-static void sqlite3TreeViewPop(TreeView *p){
+static void sqlite3TreeViewPop(TreeView **pp){
+ TreeView *p = *pp;
if( p==0 ) return;
p->iLevel--;
- if( p->iLevel<0 ) sqlite3_free(p);
+ if( p->iLevel<0 ){
+ sqlite3_free(p);
+ *pp = 0;
+ }
}
/*
** Generate a single line of output for the tree, with a prefix that contains
** all the appropriate tree lines
*/
-static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
+SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
va_list ap;
int i;
StrAccum acc;
- char zBuf[500];
+ char zBuf[1000];
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
if( p ){
- for(i=0; i<p->iLevel && i<sizeof(p->bLine)-1; i++){
+ for(i=0; i<p->iLevel && i<(int)sizeof(p->bLine)-1; i++){
sqlite3_str_append(&acc, p->bLine[i] ? "| " : " ", 4);
}
sqlite3_str_append(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
@@ -28414,7 +32321,7 @@ static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
va_start(ap, zFormat);
sqlite3_str_vappendf(&acc, zFormat, ap);
va_end(ap);
- assert( acc.nChar>0 );
+ assert( acc.nChar>0 || acc.accError );
sqlite3_str_append(&acc, "\n", 1);
}
sqlite3StrAccumFinish(&acc);
@@ -28426,11 +32333,58 @@ static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
** Shorthand for starting a new tree item that consists of a single label
*/
static void sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){
- p = sqlite3TreeViewPush(p, moreFollows);
+ sqlite3TreeViewPush(&p, moreFollows);
sqlite3TreeViewLine(p, "%s", zLabel);
}
/*
+** Show a list of Column objects in tree format.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewColumnList(
+ TreeView *pView,
+ const Column *aCol,
+ int nCol,
+ u8 moreToFollow
+){
+ int i;
+ sqlite3TreeViewPush(&pView, moreToFollow);
+ sqlite3TreeViewLine(pView, "COLUMNS");
+ for(i=0; i<nCol; i++){
+ u16 flg = aCol[i].colFlags;
+ int colMoreToFollow = i<(nCol - 1);
+ sqlite3TreeViewPush(&pView, colMoreToFollow);
+ sqlite3TreeViewLine(pView, 0);
+ printf(" %s", aCol[i].zCnName);
+ switch( aCol[i].eCType ){
+ case COLTYPE_ANY: printf(" ANY"); break;
+ case COLTYPE_BLOB: printf(" BLOB"); break;
+ case COLTYPE_INT: printf(" INT"); break;
+ case COLTYPE_INTEGER: printf(" INTEGER"); break;
+ case COLTYPE_REAL: printf(" REAL"); break;
+ case COLTYPE_TEXT: printf(" TEXT"); break;
+ case COLTYPE_CUSTOM: {
+ if( flg & COLFLAG_HASTYPE ){
+ const char *z = aCol[i].zCnName;
+ z += strlen(z)+1;
+ printf(" X-%s", z);
+ break;
+ }
+ }
+ }
+ if( flg & COLFLAG_PRIMKEY ) printf(" PRIMARY KEY");
+ if( flg & COLFLAG_HIDDEN ) printf(" HIDDEN");
+#ifdef COLFLAG_NOEXPAND
+ if( flg & COLFLAG_NOEXPAND ) printf(" NO-EXPAND");
+#endif
+ if( flg ) printf(" flags=%04x", flg);
+ printf("\n");
+ fflush(stdout);
+ sqlite3TreeViewPop(&pView);
+ }
+ sqlite3TreeViewPop(&pView);
+}
+
+/*
** Generate a human-readable description of a WITH clause.
*/
SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
@@ -28443,7 +32397,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 m
sqlite3TreeViewLine(pView, "WITH (0x%p)", pWith);
}
if( pWith->nCte>0 ){
- pView = sqlite3TreeViewPush(pView, 1);
+ sqlite3TreeViewPush(&pView, moreToFollow);
for(i=0; i<pWith->nCte; i++){
StrAccum x;
char zLine[1000];
@@ -28454,18 +32408,25 @@ SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 m
char cSep = '(';
int j;
for(j=0; j<pCte->pCols->nExpr; j++){
- sqlite3_str_appendf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
+ sqlite3_str_appendf(&x, "%c%s", cSep, pCte->pCols->a[j].zEName);
cSep = ',';
}
sqlite3_str_appendf(&x, ")");
}
- sqlite3_str_appendf(&x, " AS");
+ if( pCte->eM10d!=M10d_Any ){
+ sqlite3_str_appendf(&x, " %sMATERIALIZED",
+ pCte->eM10d==M10d_No ? "NOT " : "");
+ }
+ if( pCte->pUse ){
+ sqlite3_str_appendf(&x, " (pUse=0x%p, nUse=%d)", pCte->pUse,
+ pCte->pUse->nUse);
+ }
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<pWith->nCte-1);
sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
}
@@ -28474,35 +32435,68 @@ SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 m
*/
SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
int i;
+ if( pSrc==0 ) return;
for(i=0; i<pSrc->nSrc; i++){
- const struct SrcList_item *pItem = &pSrc->a[i];
+ const SrcItem *pItem = &pSrc->a[i];
StrAccum x;
- char zLine[100];
+ int n = 0;
+ char zLine[1000];
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
- sqlite3_str_appendf(&x, "{%d,*}", pItem->iCursor);
- if( pItem->zDatabase ){
- sqlite3_str_appendf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
- }else if( pItem->zName ){
- sqlite3_str_appendf(&x, " %s", pItem->zName);
- }
+ x.printfFlags |= SQLITE_PRINTF_INTERNAL;
+ sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem);
if( pItem->pTab ){
- sqlite3_str_appendf(&x, " tabname=%Q", pItem->pTab->zName);
- }
- if( pItem->zAlias ){
- sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias);
+ sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx",
+ pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed);
}
- if( pItem->fg.jointype & JT_LEFT ){
+ if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){
+ sqlite3_str_appendf(&x, " FULL-OUTER-JOIN");
+ }else if( pItem->fg.jointype & JT_LEFT ){
sqlite3_str_appendf(&x, " LEFT-JOIN");
+ }else if( pItem->fg.jointype & JT_RIGHT ){
+ sqlite3_str_appendf(&x, " RIGHT-JOIN");
+ }else if( pItem->fg.jointype & JT_CROSS ){
+ sqlite3_str_appendf(&x, " CROSS-JOIN");
+ }
+ if( pItem->fg.jointype & JT_LTORJ ){
+ sqlite3_str_appendf(&x, " LTORJ");
+ }
+ if( pItem->fg.fromDDL ){
+ sqlite3_str_appendf(&x, " DDL");
}
+ if( pItem->fg.isCte ){
+ sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
+ }
+ if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
+ sqlite3_str_appendf(&x, " ON");
+ }
+ if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc");
+ if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated");
+ if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized");
+ if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine");
+ if( pItem->fg.notCte ) sqlite3_str_appendf(&x, " notCte");
+ if( pItem->fg.isNestedFrom ) sqlite3_str_appendf(&x, " isNestedFrom");
+
sqlite3StrAccumFinish(&x);
- sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
+ sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
+ n = 0;
+ if( pItem->pSelect ) n++;
+ if( pItem->fg.isTabFunc ) n++;
+ if( pItem->fg.isUsing ) n++;
+ if( pItem->fg.isUsing ){
+ sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING");
+ }
if( pItem->pSelect ){
- sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
+ if( pItem->pTab ){
+ Table *pTab = pItem->pTab;
+ sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1);
+ }
+ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) );
+ sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0);
}
if( pItem->fg.isTabFunc ){
sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
}
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
}
@@ -28515,22 +32509,26 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
if( p==0 ){
sqlite3TreeViewLine(pView, "nil-SELECT");
return;
- }
- pView = sqlite3TreeViewPush(pView, moreToFollow);
+ }
+ sqlite3TreeViewPush(&pView, moreToFollow);
if( p->pWith ){
sqlite3TreeViewWith(pView, p->pWith, 1);
cnt = 1;
- sqlite3TreeViewPush(pView, 1);
+ sqlite3TreeViewPush(&pView, 1);
}
do{
- sqlite3TreeViewLine(pView,
- "SELECT%s%s (%u/%p) selFlags=0x%x nSelectRow=%d",
- ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
- ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""),
- p->selId, p, p->selFlags,
- (int)p->nSelectRow
- );
- if( cnt++ ) sqlite3TreeViewPop(pView);
+ if( p->selFlags & SF_WhereBegin ){
+ sqlite3TreeViewLine(pView, "sqlite3WhereBegin()");
+ }else{
+ sqlite3TreeViewLine(pView,
+ "SELECT%s%s (%u/%p) selFlags=0x%x nSelectRow=%d",
+ ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
+ ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""),
+ p->selId, p, p->selFlags,
+ (int)p->nSelectRow
+ );
+ }
+ if( cnt++ ) sqlite3TreeViewPop(&pView);
if( p->pPrior ){
n = 1000;
}else{
@@ -28546,28 +32544,31 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
if( p->pWinDefn ) n++;
#endif
}
- sqlite3TreeViewExprList(pView, p->pEList, (n--)>0, "result-set");
+ if( p->pEList ){
+ sqlite3TreeViewExprList(pView, p->pEList, n>0, "result-set");
+ }
+ n--;
#ifndef SQLITE_OMIT_WINDOWFUNC
if( p->pWin ){
Window *pX;
- pView = sqlite3TreeViewPush(pView, (n--)>0);
+ sqlite3TreeViewPush(&pView, (n--)>0);
sqlite3TreeViewLine(pView, "window-functions");
for(pX=p->pWin; pX; pX=pX->pNextWin){
sqlite3TreeViewWinFunc(pView, pX, pX->pNextWin!=0);
}
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
#endif
if( p->pSrc && p->pSrc->nSrc ){
- pView = sqlite3TreeViewPush(pView, (n--)>0);
+ sqlite3TreeViewPush(&pView, (n--)>0);
sqlite3TreeViewLine(pView, "FROM");
sqlite3TreeViewSrcList(pView, p->pSrc);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
if( p->pWhere ){
sqlite3TreeViewItem(pView, "WHERE", (n--)>0);
sqlite3TreeViewExpr(pView, p->pWhere, 0);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
if( p->pGroupBy ){
sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY");
@@ -28575,7 +32576,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
if( p->pHaving ){
sqlite3TreeViewItem(pView, "HAVING", (n--)>0);
sqlite3TreeViewExpr(pView, p->pHaving, 0);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( p->pWinDefn ){
@@ -28584,7 +32585,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
for(pX=p->pWinDefn; pX; pX=pX->pNextWin){
sqlite3TreeViewWindow(pView, pX, pX->pNextWin!=0);
}
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
#endif
if( p->pOrderBy ){
@@ -28596,9 +32597,9 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
if( p->pLimit->pRight ){
sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
if( p->pPrior ){
const char *zOp = "UNION";
@@ -28611,7 +32612,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
}
p = p->pPrior;
}while( p!=0 );
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
#ifndef SQLITE_OMIT_WINDOWFUNC
@@ -28627,24 +32628,24 @@ SQLITE_PRIVATE void sqlite3TreeViewBound(
switch( eBound ){
case TK_UNBOUNDED: {
sqlite3TreeViewItem(pView, "UNBOUNDED", moreToFollow);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
break;
}
case TK_CURRENT: {
sqlite3TreeViewItem(pView, "CURRENT", moreToFollow);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
break;
}
case TK_PRECEDING: {
sqlite3TreeViewItem(pView, "PRECEDING", moreToFollow);
sqlite3TreeViewExpr(pView, pExpr, 0);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
break;
}
case TK_FOLLOWING: {
sqlite3TreeViewItem(pView, "FOLLOWING", moreToFollow);
sqlite3TreeViewExpr(pView, pExpr, 0);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
break;
}
}
@@ -28656,25 +32657,65 @@ SQLITE_PRIVATE void sqlite3TreeViewBound(
** Generate a human-readable explanation for a Window object
*/
SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){
- pView = sqlite3TreeViewPush(pView, more);
+ int nElement = 0;
+ if( pWin==0 ) return;
+ if( pWin->pFilter ){
+ sqlite3TreeViewItem(pView, "FILTER", 1);
+ sqlite3TreeViewExpr(pView, pWin->pFilter, 0);
+ sqlite3TreeViewPop(&pView);
+ if( pWin->eFrmType==TK_FILTER ) return;
+ }
+ sqlite3TreeViewPush(&pView, more);
if( pWin->zName ){
- sqlite3TreeViewLine(pView, "OVER %s", pWin->zName);
+ sqlite3TreeViewLine(pView, "OVER %s (%p)", pWin->zName, pWin);
}else{
- sqlite3TreeViewLine(pView, "OVER");
+ sqlite3TreeViewLine(pView, "OVER (%p)", pWin);
+ }
+ if( pWin->zBase ) nElement++;
+ if( pWin->pOrderBy ) nElement++;
+ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ) nElement++;
+ if( pWin->eExclude ) nElement++;
+ if( pWin->zBase ){
+ sqlite3TreeViewPush(&pView, (--nElement)>0);
+ sqlite3TreeViewLine(pView, "window: %s", pWin->zBase);
+ sqlite3TreeViewPop(&pView);
}
if( pWin->pPartition ){
- sqlite3TreeViewExprList(pView, pWin->pPartition, 1, "PARTITION-BY");
+ sqlite3TreeViewExprList(pView, pWin->pPartition, nElement>0,"PARTITION-BY");
}
if( pWin->pOrderBy ){
- sqlite3TreeViewExprList(pView, pWin->pOrderBy, 1, "ORDER-BY");
- }
- if( pWin->eType ){
- sqlite3TreeViewItem(pView, pWin->eType==TK_RANGE ? "RANGE" : "ROWS", 0);
+ sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY");
+ }
+ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ){
+ char zBuf[30];
+ const char *zFrmType = "ROWS";
+ if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE";
+ if( pWin->eFrmType==TK_GROUPS ) zFrmType = "GROUPS";
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%s%s",zFrmType,
+ pWin->bImplicitFrame ? " (implied)" : "");
+ sqlite3TreeViewItem(pView, zBuf, (--nElement)>0);
sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1);
sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pWin->eExclude ){
+ char zBuf[30];
+ const char *zExclude;
+ switch( pWin->eExclude ){
+ case TK_NO: zExclude = "NO OTHERS"; break;
+ case TK_CURRENT: zExclude = "CURRENT ROW"; break;
+ case TK_GROUP: zExclude = "GROUP"; break;
+ case TK_TIES: zExclude = "TIES"; break;
+ default:
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"invalid(%d)", pWin->eExclude);
+ zExclude = zBuf;
+ break;
+ }
+ sqlite3TreeViewPush(&pView, 0);
+ sqlite3TreeViewLine(pView, "EXCLUDE %s", zExclude);
+ sqlite3TreeViewPop(&pView);
}
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
#endif /* SQLITE_OMIT_WINDOWFUNC */
@@ -28683,11 +32724,12 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u
** Generate a human-readable explanation for a Window Function object
*/
SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin, u8 more){
- pView = sqlite3TreeViewPush(pView, more);
+ if( pWin==0 ) return;
+ sqlite3TreeViewPush(&pView, more);
sqlite3TreeViewLine(pView, "WINFUNC %s(%d)",
- pWin->pFunc->zName, pWin->pFunc->nArg);
+ pWin->pWFunc->zName, pWin->pWFunc->nArg);
sqlite3TreeViewWindow(pView, pWin, 0);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
#endif /* SQLITE_OMIT_WINDOWFUNC */
@@ -28697,20 +32739,34 @@ SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView *pView, const Window *pWin,
SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
const char *zBinOp = 0; /* Binary operator */
const char *zUniOp = 0; /* Unary operator */
- char zFlgs[60];
- pView = sqlite3TreeViewPush(pView, moreToFollow);
+ char zFlgs[200];
+ sqlite3TreeViewPush(&pView, moreToFollow);
if( pExpr==0 ){
sqlite3TreeViewLine(pView, "nil");
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
return;
}
- if( pExpr->flags ){
- if( ExprHasProperty(pExpr, EP_FromJoin) ){
- sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x iRJT=%d",
- pExpr->flags, pExpr->iRightJoinTable);
- }else{
- sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x",pExpr->flags);
+ if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags || pExpr->pAggInfo ){
+ StrAccum x;
+ sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0);
+ sqlite3_str_appendf(&x, " fg.af=%x.%c",
+ pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n');
+ if( ExprHasProperty(pExpr, EP_OuterON) ){
+ sqlite3_str_appendf(&x, " outer.iJoin=%d", pExpr->w.iJoin);
+ }
+ if( ExprHasProperty(pExpr, EP_InnerON) ){
+ sqlite3_str_appendf(&x, " inner.iJoin=%d", pExpr->w.iJoin);
+ }
+ if( ExprHasProperty(pExpr, EP_FromDDL) ){
+ sqlite3_str_appendf(&x, " DDL");
}
+ if( ExprHasVVAProperty(pExpr, EP_Immutable) ){
+ sqlite3_str_appendf(&x, " IMMUTABLE");
+ }
+ if( pExpr->pAggInfo!=0 ){
+ sqlite3_str_appendf(&x, " agg-column[%d]", pExpr->iAgg);
+ }
+ sqlite3StrAccumFinish(&x);
}else{
zFlgs[0] = 0;
}
@@ -28723,10 +32779,19 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
case TK_COLUMN: {
if( pExpr->iTable<0 ){
/* This only happens when coding check constraints */
- sqlite3TreeViewLine(pView, "COLUMN(%d)%s", pExpr->iColumn, zFlgs);
+ char zOp2[16];
+ if( pExpr->op2 ){
+ sqlite3_snprintf(sizeof(zOp2),zOp2," op2=0x%02x",pExpr->op2);
+ }else{
+ zOp2[0] = 0;
+ }
+ sqlite3TreeViewLine(pView, "COLUMN(%d)%s%s",
+ pExpr->iColumn, zFlgs, zOp2);
}else{
- sqlite3TreeViewLine(pView, "{%d:%d}%s",
- pExpr->iTable, pExpr->iColumn, zFlgs);
+ assert( ExprUseYTab(pExpr) );
+ sqlite3TreeViewLine(pView, "{%d:%d} pTab=%p%s",
+ pExpr->iTable, pExpr->iColumn,
+ pExpr->y.pTab, zFlgs);
}
if( ExprHasProperty(pExpr, EP_FixedCol) ){
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
@@ -28743,11 +32808,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
}
#ifndef SQLITE_OMIT_FLOATING_POINT
case TK_FLOAT: {
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
break;
}
#endif
case TK_STRING: {
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3TreeViewLine(pView,"%Q", pExpr->u.zToken);
break;
}
@@ -28756,17 +32823,19 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
break;
}
case TK_TRUEFALSE: {
- sqlite3TreeViewLine(pView,
- sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE");
+ sqlite3TreeViewLine(pView,"%s%s",
+ sqlite3ExprTruthValue(pExpr) ? "TRUE" : "FALSE", zFlgs);
break;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
case TK_BLOB: {
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
break;
}
#endif
case TK_VARIABLE: {
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3TreeViewLine(pView,"VARIABLE(%s,%d)",
pExpr->u.zToken, pExpr->iColumn);
break;
@@ -28776,12 +32845,14 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
break;
}
case TK_ID: {
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken);
break;
}
#ifndef SQLITE_OMIT_CAST
case TK_CAST: {
/* Expressions of the form: CAST(pLeft AS token) */
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3TreeViewLine(pView,"CAST %Q", pExpr->u.zToken);
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
break;
@@ -28808,6 +32879,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
case TK_RSHIFT: zBinOp = "RSHIFT"; break;
case TK_CONCAT: zBinOp = "CONCAT"; break;
case TK_DOT: zBinOp = "DOT"; break;
+ case TK_LIMIT: zBinOp = "LIMIT"; break;
case TK_UMINUS: zUniOp = "UMINUS"; break;
case TK_UPLUS: zUniOp = "UPLUS"; break;
@@ -28823,20 +32895,30 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
};
assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT );
assert( pExpr->pRight );
- assert( pExpr->pRight->op==TK_TRUEFALSE );
+ assert( sqlite3ExprSkipCollateAndLikely(pExpr->pRight)->op
+ == TK_TRUEFALSE );
x = (pExpr->op2==TK_ISNOT)*2 + sqlite3ExprTruthValue(pExpr->pRight);
zUniOp = azOp[x];
break;
}
case TK_SPAN: {
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken);
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
break;
}
case TK_COLLATE: {
- sqlite3TreeViewLine(pView, "COLLATE %Q", pExpr->u.zToken);
+ /* COLLATE operators without the EP_Collate flag are intended to
+ ** emulate collation associated with a table column. These show
+ ** up in the treeview output as "SOFT-COLLATE". Explicit COLLATE
+ ** operators that appear in the original SQL always have the
+ ** EP_Collate bit set and appear in treeview output as just "COLLATE" */
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+ sqlite3TreeViewLine(pView, "%sCOLLATE %Q%s",
+ !ExprHasProperty(pExpr, EP_Collate) ? "SOFT-" : "",
+ pExpr->u.zToken, zFlgs);
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
break;
}
@@ -28849,21 +32931,42 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
pFarg = 0;
pWin = 0;
}else{
+ assert( ExprUseXList(pExpr) );
pFarg = pExpr->x.pList;
#ifndef SQLITE_OMIT_WINDOWFUNC
- pWin = pExpr->y.pWin;
+ pWin = IsWindowFunc(pExpr) ? pExpr->y.pWin : 0;
#else
pWin = 0;
-#endif
+#endif
}
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
if( pExpr->op==TK_AGG_FUNCTION ){
- sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q",
- pExpr->op2, pExpr->u.zToken);
+ sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s agg=%d[%d]/%p",
+ pExpr->op2, pExpr->u.zToken, zFlgs,
+ pExpr->pAggInfo ? pExpr->pAggInfo->selId : 0,
+ pExpr->iAgg, pExpr->pAggInfo);
+ }else if( pExpr->op2!=0 ){
+ const char *zOp2;
+ char zBuf[8];
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"0x%02x",pExpr->op2);
+ zOp2 = zBuf;
+ if( pExpr->op2==NC_IsCheck ) zOp2 = "NC_IsCheck";
+ if( pExpr->op2==NC_IdxExpr ) zOp2 = "NC_IdxExpr";
+ if( pExpr->op2==NC_PartIdx ) zOp2 = "NC_PartIdx";
+ if( pExpr->op2==NC_GenCol ) zOp2 = "NC_GenCol";
+ sqlite3TreeViewLine(pView, "FUNCTION %Q%s op2=%s",
+ pExpr->u.zToken, zFlgs, zOp2);
}else{
- sqlite3TreeViewLine(pView, "FUNCTION %Q", pExpr->u.zToken);
+ sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs);
}
if( pFarg ){
- sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0);
+ sqlite3TreeViewExprList(pView, pFarg, pWin!=0 || pExpr->pLeft, 0);
+ if( pExpr->pLeft ){
+ Expr *pOB = pExpr->pLeft;
+ assert( pOB->op==TK_ORDER );
+ assert( ExprUseXList(pOB) );
+ sqlite3TreeViewExprList(pView, pOB->x.pList, pWin!=0, "ORDERBY");
+ }
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pWin ){
@@ -28872,21 +32975,37 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
#endif
break;
}
+ case TK_ORDER: {
+ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, "ORDERBY");
+ break;
+ }
#ifndef SQLITE_OMIT_SUBQUERY
case TK_EXISTS: {
+ assert( ExprUseXSelect(pExpr) );
sqlite3TreeViewLine(pView, "EXISTS-expr flags=0x%x", pExpr->flags);
sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
break;
}
case TK_SELECT: {
- sqlite3TreeViewLine(pView, "SELECT-expr flags=0x%x", pExpr->flags);
+ assert( ExprUseXSelect(pExpr) );
+ sqlite3TreeViewLine(pView, "subquery-expr flags=0x%x", pExpr->flags);
sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
break;
}
case TK_IN: {
- sqlite3TreeViewLine(pView, "IN flags=0x%x", pExpr->flags);
+ sqlite3_str *pStr = sqlite3_str_new(0);
+ char *z;
+ sqlite3_str_appendf(pStr, "IN flags=0x%x", pExpr->flags);
+ if( pExpr->iTable ) sqlite3_str_appendf(pStr, " iTable=%d",pExpr->iTable);
+ if( ExprHasProperty(pExpr, EP_Subrtn) ){
+ sqlite3_str_appendf(pStr, " subrtn(%d,%d)",
+ pExpr->y.sub.regReturn, pExpr->y.sub.iAddr);
+ }
+ z = sqlite3_str_finish(pStr);
+ sqlite3TreeViewLine(pView, z);
+ sqlite3_free(z);
sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ if( ExprUseXSelect(pExpr) ){
sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
}else{
sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0);
@@ -28907,10 +33026,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
** Z is stored in pExpr->pList->a[1].pExpr.
*/
case TK_BETWEEN: {
- Expr *pX = pExpr->pLeft;
- Expr *pY = pExpr->x.pList->a[0].pExpr;
- Expr *pZ = pExpr->x.pList->a[1].pExpr;
- sqlite3TreeViewLine(pView, "BETWEEN");
+ const Expr *pX, *pY, *pZ;
+ pX = pExpr->pLeft;
+ assert( ExprUseXList(pExpr) );
+ assert( pExpr->x.pList->nExpr==2 );
+ pY = pExpr->x.pList->a[0].pExpr;
+ pZ = pExpr->x.pList->a[1].pExpr;
+ sqlite3TreeViewLine(pView, "BETWEEN%s", zFlgs);
sqlite3TreeViewExpr(pView, pX, 1);
sqlite3TreeViewExpr(pView, pY, 1);
sqlite3TreeViewExpr(pView, pZ, 0);
@@ -28924,25 +33046,27 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
** is set to the column of the pseudo-table to read, or to -1 to
** read the rowid field.
*/
- sqlite3TreeViewLine(pView, "%s(%d)",
+ sqlite3TreeViewLine(pView, "%s(%d)",
pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn);
break;
}
case TK_CASE: {
sqlite3TreeViewLine(pView, "CASE");
sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
+ assert( ExprUseXList(pExpr) );
sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0);
break;
}
#ifndef SQLITE_OMIT_TRIGGER
case TK_RAISE: {
const char *zType = "unk";
- switch( pExpr->affinity ){
+ switch( pExpr->affExpr ){
case OE_Rollback: zType = "rollback"; break;
case OE_Abort: zType = "abort"; break;
case OE_Fail: zType = "fail"; break;
case OE_Ignore: zType = "ignore"; break;
}
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken);
break;
}
@@ -28954,11 +33078,17 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
break;
}
case TK_VECTOR: {
- sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR");
+ char *z = sqlite3_mprintf("VECTOR%s",zFlgs);
+ assert( ExprUseXList(pExpr) );
+ sqlite3TreeViewBareExprList(pView, pExpr->x.pList, z);
+ sqlite3_free(z);
break;
}
case TK_SELECT_COLUMN: {
- sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn);
+ sqlite3TreeViewLine(pView, "SELECT-COLUMN %d of [0..%d]%s",
+ pExpr->iColumn, pExpr->iTable-1,
+ pExpr->pRight==pExpr->pLeft ? " (SELECT-owner)" : "");
+ assert( ExprUseXSelect(pExpr->pLeft) );
sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0);
break;
}
@@ -28967,6 +33097,23 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
break;
}
+ case TK_ERROR: {
+ Expr tmp;
+ sqlite3TreeViewLine(pView, "ERROR");
+ tmp = *pExpr;
+ tmp.op = pExpr->op2;
+ sqlite3TreeViewExpr(pView, &tmp, 0);
+ break;
+ }
+ case TK_ROW: {
+ if( pExpr->iColumn<=0 ){
+ sqlite3TreeViewLine(pView, "First FROM table rowid");
+ }else{
+ sqlite3TreeViewLine(pView, "First FROM table column %d",
+ pExpr->iColumn-1);
+ }
+ break;
+ }
default: {
sqlite3TreeViewLine(pView, "op=%d", pExpr->op);
break;
@@ -28978,9 +33125,9 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
}else if( zUniOp ){
sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs);
- sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
}
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
@@ -29000,14 +33147,27 @@ SQLITE_PRIVATE void sqlite3TreeViewBareExprList(
sqlite3TreeViewLine(pView, "%s", zLabel);
for(i=0; i<pList->nExpr; i++){
int j = pList->a[i].u.x.iOrderByCol;
- char *zName = pList->a[i].zName;
+ char *zName = pList->a[i].zEName;
int moreToFollow = i<pList->nExpr - 1;
if( j || zName ){
- sqlite3TreeViewPush(pView, moreToFollow);
+ sqlite3TreeViewPush(&pView, moreToFollow);
moreToFollow = 0;
sqlite3TreeViewLine(pView, 0);
if( zName ){
- fprintf(stdout, "AS %s ", zName);
+ switch( pList->a[i].fg.eEName ){
+ default:
+ fprintf(stdout, "AS %s ", zName);
+ break;
+ case ENAME_TAB:
+ fprintf(stdout, "TABLE-ALIAS-NAME(\"%s\") ", zName);
+ if( pList->a[i].fg.bUsed ) fprintf(stdout, "(used) ");
+ if( pList->a[i].fg.bUsingTerm ) fprintf(stdout, "(USING-term) ");
+ if( pList->a[i].fg.bNoExpand ) fprintf(stdout, "(NoExpand) ");
+ break;
+ case ENAME_SPAN:
+ fprintf(stdout, "SPAN(\"%s\") ", zName);
+ break;
+ }
}
if( j ){
fprintf(stdout, "iOrderByCol=%d", j);
@@ -29017,7 +33177,7 @@ SQLITE_PRIVATE void sqlite3TreeViewBareExprList(
}
sqlite3TreeViewExpr(pView, pList->a[i].pExpr, moreToFollow);
if( j || zName ){
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
}
}
}
@@ -29028,10 +33188,377 @@ SQLITE_PRIVATE void sqlite3TreeViewExprList(
u8 moreToFollow,
const char *zLabel
){
- pView = sqlite3TreeViewPush(pView, moreToFollow);
+ sqlite3TreeViewPush(&pView, moreToFollow);
sqlite3TreeViewBareExprList(pView, pList, zLabel);
- sqlite3TreeViewPop(pView);
+ sqlite3TreeViewPop(&pView);
+}
+
+/*
+** Generate a human-readable explanation of an id-list.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewBareIdList(
+ TreeView *pView,
+ const IdList *pList,
+ const char *zLabel
+){
+ if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST";
+ if( pList==0 ){
+ sqlite3TreeViewLine(pView, "%s (empty)", zLabel);
+ }else{
+ int i;
+ sqlite3TreeViewLine(pView, "%s", zLabel);
+ for(i=0; i<pList->nId; i++){
+ char *zName = pList->a[i].zName;
+ int moreToFollow = i<pList->nId - 1;
+ if( zName==0 ) zName = "(null)";
+ sqlite3TreeViewPush(&pView, moreToFollow);
+ sqlite3TreeViewLine(pView, 0);
+ if( pList->eU4==EU4_NONE ){
+ fprintf(stdout, "%s\n", zName);
+ }else if( pList->eU4==EU4_IDX ){
+ fprintf(stdout, "%s (%d)\n", zName, pList->a[i].u4.idx);
+ }else{
+ assert( pList->eU4==EU4_EXPR );
+ if( pList->a[i].u4.pExpr==0 ){
+ fprintf(stdout, "%s (pExpr=NULL)\n", zName);
+ }else{
+ fprintf(stdout, "%s\n", zName);
+ sqlite3TreeViewPush(&pView, i<pList->nId-1);
+ sqlite3TreeViewExpr(pView, pList->a[i].u4.pExpr, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ }
+ sqlite3TreeViewPop(&pView);
+ }
+ }
+}
+SQLITE_PRIVATE void sqlite3TreeViewIdList(
+ TreeView *pView,
+ const IdList *pList,
+ u8 moreToFollow,
+ const char *zLabel
+){
+ sqlite3TreeViewPush(&pView, moreToFollow);
+ sqlite3TreeViewBareIdList(pView, pList, zLabel);
+ sqlite3TreeViewPop(&pView);
+}
+
+/*
+** Generate a human-readable explanation of a list of Upsert objects
+*/
+SQLITE_PRIVATE void sqlite3TreeViewUpsert(
+ TreeView *pView,
+ const Upsert *pUpsert,
+ u8 moreToFollow
+){
+ if( pUpsert==0 ) return;
+ sqlite3TreeViewPush(&pView, moreToFollow);
+ while( pUpsert ){
+ int n;
+ sqlite3TreeViewPush(&pView, pUpsert->pNextUpsert!=0 || moreToFollow);
+ sqlite3TreeViewLine(pView, "ON CONFLICT DO %s",
+ pUpsert->isDoUpdate ? "UPDATE" : "NOTHING");
+ n = (pUpsert->pUpsertSet!=0) + (pUpsert->pUpsertWhere!=0);
+ sqlite3TreeViewExprList(pView, pUpsert->pUpsertTarget, (n--)>0, "TARGET");
+ sqlite3TreeViewExprList(pView, pUpsert->pUpsertSet, (n--)>0, "SET");
+ if( pUpsert->pUpsertWhere ){
+ sqlite3TreeViewItem(pView, "WHERE", (n--)>0);
+ sqlite3TreeViewExpr(pView, pUpsert->pUpsertWhere, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ sqlite3TreeViewPop(&pView);
+ pUpsert = pUpsert->pNextUpsert;
+ }
+ sqlite3TreeViewPop(&pView);
+}
+
+#if TREETRACE_ENABLED
+/*
+** Generate a human-readable diagram of the data structure that go
+** into generating an DELETE statement.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewDelete(
+ const With *pWith,
+ const SrcList *pTabList,
+ const Expr *pWhere,
+ const ExprList *pOrderBy,
+ const Expr *pLimit,
+ const Trigger *pTrigger
+){
+ int n = 0;
+ TreeView *pView = 0;
+ sqlite3TreeViewPush(&pView, 0);
+ sqlite3TreeViewLine(pView, "DELETE");
+ if( pWith ) n++;
+ if( pTabList ) n++;
+ if( pWhere ) n++;
+ if( pOrderBy ) n++;
+ if( pLimit ) n++;
+ if( pTrigger ) n++;
+ if( pWith ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewWith(pView, pWith, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pTabList ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewLine(pView, "FROM");
+ sqlite3TreeViewSrcList(pView, pTabList);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pWhere ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewLine(pView, "WHERE");
+ sqlite3TreeViewExpr(pView, pWhere, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pOrderBy ){
+ sqlite3TreeViewExprList(pView, pOrderBy, (--n)>0, "ORDER-BY");
+ }
+ if( pLimit ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewLine(pView, "LIMIT");
+ sqlite3TreeViewExpr(pView, pLimit, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pTrigger ){
+ sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1);
+ }
+ sqlite3TreeViewPop(&pView);
+}
+#endif /* TREETRACE_ENABLED */
+
+#if TREETRACE_ENABLED
+/*
+** Generate a human-readable diagram of the data structure that go
+** into generating an INSERT statement.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewInsert(
+ const With *pWith,
+ const SrcList *pTabList,
+ const IdList *pColumnList,
+ const Select *pSelect,
+ const ExprList *pExprList,
+ int onError,
+ const Upsert *pUpsert,
+ const Trigger *pTrigger
+){
+ TreeView *pView = 0;
+ int n = 0;
+ const char *zLabel = "INSERT";
+ switch( onError ){
+ case OE_Replace: zLabel = "REPLACE"; break;
+ case OE_Ignore: zLabel = "INSERT OR IGNORE"; break;
+ case OE_Rollback: zLabel = "INSERT OR ROLLBACK"; break;
+ case OE_Abort: zLabel = "INSERT OR ABORT"; break;
+ case OE_Fail: zLabel = "INSERT OR FAIL"; break;
+ }
+ sqlite3TreeViewPush(&pView, 0);
+ sqlite3TreeViewLine(pView, zLabel);
+ if( pWith ) n++;
+ if( pTabList ) n++;
+ if( pColumnList ) n++;
+ if( pSelect ) n++;
+ if( pExprList ) n++;
+ if( pUpsert ) n++;
+ if( pTrigger ) n++;
+ if( pWith ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewWith(pView, pWith, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pTabList ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewLine(pView, "INTO");
+ sqlite3TreeViewSrcList(pView, pTabList);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pColumnList ){
+ sqlite3TreeViewIdList(pView, pColumnList, (--n)>0, "COLUMNS");
+ }
+ if( pSelect ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewLine(pView, "DATA-SOURCE");
+ sqlite3TreeViewSelect(pView, pSelect, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pExprList ){
+ sqlite3TreeViewExprList(pView, pExprList, (--n)>0, "VALUES");
+ }
+ if( pUpsert ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewLine(pView, "UPSERT");
+ sqlite3TreeViewUpsert(pView, pUpsert, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pTrigger ){
+ sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1);
+ }
+ sqlite3TreeViewPop(&pView);
}
+#endif /* TREETRACE_ENABLED */
+
+#if TREETRACE_ENABLED
+/*
+** Generate a human-readable diagram of the data structure that go
+** into generating an UPDATE statement.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewUpdate(
+ const With *pWith,
+ const SrcList *pTabList,
+ const ExprList *pChanges,
+ const Expr *pWhere,
+ int onError,
+ const ExprList *pOrderBy,
+ const Expr *pLimit,
+ const Upsert *pUpsert,
+ const Trigger *pTrigger
+){
+ int n = 0;
+ TreeView *pView = 0;
+ const char *zLabel = "UPDATE";
+ switch( onError ){
+ case OE_Replace: zLabel = "UPDATE OR REPLACE"; break;
+ case OE_Ignore: zLabel = "UPDATE OR IGNORE"; break;
+ case OE_Rollback: zLabel = "UPDATE OR ROLLBACK"; break;
+ case OE_Abort: zLabel = "UPDATE OR ABORT"; break;
+ case OE_Fail: zLabel = "UPDATE OR FAIL"; break;
+ }
+ sqlite3TreeViewPush(&pView, 0);
+ sqlite3TreeViewLine(pView, zLabel);
+ if( pWith ) n++;
+ if( pTabList ) n++;
+ if( pChanges ) n++;
+ if( pWhere ) n++;
+ if( pOrderBy ) n++;
+ if( pLimit ) n++;
+ if( pUpsert ) n++;
+ if( pTrigger ) n++;
+ if( pWith ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewWith(pView, pWith, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pTabList ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewLine(pView, "FROM");
+ sqlite3TreeViewSrcList(pView, pTabList);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pChanges ){
+ sqlite3TreeViewExprList(pView, pChanges, (--n)>0, "SET");
+ }
+ if( pWhere ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewLine(pView, "WHERE");
+ sqlite3TreeViewExpr(pView, pWhere, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pOrderBy ){
+ sqlite3TreeViewExprList(pView, pOrderBy, (--n)>0, "ORDER-BY");
+ }
+ if( pLimit ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewLine(pView, "LIMIT");
+ sqlite3TreeViewExpr(pView, pLimit, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pUpsert ){
+ sqlite3TreeViewPush(&pView, (--n)>0);
+ sqlite3TreeViewLine(pView, "UPSERT");
+ sqlite3TreeViewUpsert(pView, pUpsert, 0);
+ sqlite3TreeViewPop(&pView);
+ }
+ if( pTrigger ){
+ sqlite3TreeViewTrigger(pView, pTrigger, (--n)>0, 1);
+ }
+ sqlite3TreeViewPop(&pView);
+}
+#endif /* TREETRACE_ENABLED */
+
+#ifndef SQLITE_OMIT_TRIGGER
+/*
+** Show a human-readable graph of a TriggerStep
+*/
+SQLITE_PRIVATE void sqlite3TreeViewTriggerStep(
+ TreeView *pView,
+ const TriggerStep *pStep,
+ u8 moreToFollow,
+ u8 showFullList
+){
+ int cnt = 0;
+ if( pStep==0 ) return;
+ sqlite3TreeViewPush(&pView,
+ moreToFollow || (showFullList && pStep->pNext!=0));
+ do{
+ if( cnt++ && pStep->pNext==0 ){
+ sqlite3TreeViewPop(&pView);
+ sqlite3TreeViewPush(&pView, 0);
+ }
+ sqlite3TreeViewLine(pView, "%s", pStep->zSpan ? pStep->zSpan : "RETURNING");
+ }while( showFullList && (pStep = pStep->pNext)!=0 );
+ sqlite3TreeViewPop(&pView);
+}
+
+/*
+** Show a human-readable graph of a Trigger
+*/
+SQLITE_PRIVATE void sqlite3TreeViewTrigger(
+ TreeView *pView,
+ const Trigger *pTrigger,
+ u8 moreToFollow,
+ u8 showFullList
+){
+ int cnt = 0;
+ if( pTrigger==0 ) return;
+ sqlite3TreeViewPush(&pView,
+ moreToFollow || (showFullList && pTrigger->pNext!=0));
+ do{
+ if( cnt++ && pTrigger->pNext==0 ){
+ sqlite3TreeViewPop(&pView);
+ sqlite3TreeViewPush(&pView, 0);
+ }
+ sqlite3TreeViewLine(pView, "TRIGGER %s", pTrigger->zName);
+ sqlite3TreeViewPush(&pView, 0);
+ sqlite3TreeViewTriggerStep(pView, pTrigger->step_list, 0, 1);
+ sqlite3TreeViewPop(&pView);
+ }while( showFullList && (pTrigger = pTrigger->pNext)!=0 );
+ sqlite3TreeViewPop(&pView);
+}
+#endif /* SQLITE_OMIT_TRIGGER */
+
+
+/*
+** These simplified versions of the tree-view routines omit unnecessary
+** parameters. These variants are intended to be used from a symbolic
+** debugger, such as "gdb", during interactive debugging sessions.
+**
+** This routines are given external linkage so that they will always be
+** accessible to the debugging, and to avoid warnings about unused
+** functions. But these routines only exist in debugging builds, so they
+** do not contaminate the interface.
+*/
+SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); }
+SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);}
+SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); }
+SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); }
+SQLITE_PRIVATE void sqlite3ShowSelect(const Select *p){ sqlite3TreeViewSelect(0,p,0); }
+SQLITE_PRIVATE void sqlite3ShowWith(const With *p){ sqlite3TreeViewWith(0,p,0); }
+SQLITE_PRIVATE void sqlite3ShowUpsert(const Upsert *p){ sqlite3TreeViewUpsert(0,p,0); }
+#ifndef SQLITE_OMIT_TRIGGER
+SQLITE_PRIVATE void sqlite3ShowTriggerStep(const TriggerStep *p){
+ sqlite3TreeViewTriggerStep(0,p,0,0);
+}
+SQLITE_PRIVATE void sqlite3ShowTriggerStepList(const TriggerStep *p){
+ sqlite3TreeViewTriggerStep(0,p,0,1);
+}
+SQLITE_PRIVATE void sqlite3ShowTrigger(const Trigger *p){ sqlite3TreeViewTrigger(0,p,0,0); }
+SQLITE_PRIVATE void sqlite3ShowTriggerList(const Trigger *p){ sqlite3TreeViewTrigger(0,p,0,1);}
+#endif
+#ifndef SQLITE_OMIT_WINDOWFUNC
+SQLITE_PRIVATE void sqlite3ShowWindow(const Window *p){ sqlite3TreeViewWindow(0,p,0); }
+SQLITE_PRIVATE void sqlite3ShowWinFunc(const Window *p){ sqlite3TreeViewWinFunc(0,p,0); }
+#endif
#endif /* SQLITE_DEBUG */
@@ -29061,16 +33588,41 @@ SQLITE_PRIVATE void sqlite3TreeViewExprList(
** This structure is the current state of the generator.
*/
static SQLITE_WSD struct sqlite3PrngType {
- unsigned char isInit; /* True if initialized */
- unsigned char i, j; /* State variables */
- unsigned char s[256]; /* State variables */
+ u32 s[16]; /* 64 bytes of chacha20 state */
+ u8 out[64]; /* Output bytes */
+ u8 n; /* Output bytes remaining */
} sqlite3Prng;
+
+/* The RFC-7539 ChaCha20 block function
+*/
+#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
+#define QR(a, b, c, d) ( \
+ a += b, d ^= a, d = ROTL(d,16), \
+ c += d, b ^= c, b = ROTL(b,12), \
+ a += b, d ^= a, d = ROTL(d, 8), \
+ c += d, b ^= c, b = ROTL(b, 7))
+static void chacha_block(u32 *out, const u32 *in){
+ int i;
+ u32 x[16];
+ memcpy(x, in, 64);
+ for(i=0; i<10; i++){
+ QR(x[0], x[4], x[ 8], x[12]);
+ QR(x[1], x[5], x[ 9], x[13]);
+ QR(x[2], x[6], x[10], x[14]);
+ QR(x[3], x[7], x[11], x[15]);
+ QR(x[0], x[5], x[10], x[15]);
+ QR(x[1], x[6], x[11], x[12]);
+ QR(x[2], x[7], x[ 8], x[13]);
+ QR(x[3], x[4], x[ 9], x[14]);
+ }
+ for(i=0; i<16; i++) out[i] = x[i]+in[i];
+}
+
/*
** Return N random bytes.
*/
SQLITE_API void sqlite3_randomness(int N, void *pBuf){
- unsigned char t;
unsigned char *zBuf = pBuf;
/* The "wsdPrng" macro will resolve to the pseudo-random number generator
@@ -29100,48 +33652,46 @@ SQLITE_API void sqlite3_randomness(int N, void *pBuf){
sqlite3_mutex_enter(mutex);
if( N<=0 || pBuf==0 ){
- wsdPrng.isInit = 0;
+ wsdPrng.s[0] = 0;
sqlite3_mutex_leave(mutex);
return;
}
/* Initialize the state of the random number generator once,
- ** the first time this routine is called. The seed value does
- ** not need to contain a lot of randomness since we are not
- ** trying to do secure encryption or anything like that...
- **
- ** Nothing in this file or anywhere else in SQLite does any kind of
- ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
- ** number generator) not as an encryption device.
+ ** the first time this routine is called.
*/
- if( !wsdPrng.isInit ){
- int i;
- char k[256];
- wsdPrng.j = 0;
- wsdPrng.i = 0;
- sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k);
- for(i=0; i<256; i++){
- wsdPrng.s[i] = (u8)i;
- }
- for(i=0; i<256; i++){
- wsdPrng.j += wsdPrng.s[i] + k[i];
- t = wsdPrng.s[wsdPrng.j];
- wsdPrng.s[wsdPrng.j] = wsdPrng.s[i];
- wsdPrng.s[i] = t;
+ if( wsdPrng.s[0]==0 ){
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
+ static const u32 chacha20_init[] = {
+ 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
+ };
+ memcpy(&wsdPrng.s[0], chacha20_init, 16);
+ if( NEVER(pVfs==0) ){
+ memset(&wsdPrng.s[4], 0, 44);
+ }else{
+ sqlite3OsRandomness(pVfs, 44, (char*)&wsdPrng.s[4]);
}
- wsdPrng.isInit = 1;
+ wsdPrng.s[15] = wsdPrng.s[12];
+ wsdPrng.s[12] = 0;
+ wsdPrng.n = 0;
}
assert( N>0 );
- do{
- wsdPrng.i++;
- t = wsdPrng.s[wsdPrng.i];
- wsdPrng.j += t;
- wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
- wsdPrng.s[wsdPrng.j] = t;
- t += wsdPrng.s[wsdPrng.i];
- *(zBuf++) = wsdPrng.s[t];
- }while( --N );
+ while( 1 /* exit by break */ ){
+ if( N<=wsdPrng.n ){
+ memcpy(zBuf, &wsdPrng.out[wsdPrng.n-N], N);
+ wsdPrng.n -= N;
+ break;
+ }
+ if( wsdPrng.n>0 ){
+ memcpy(zBuf, wsdPrng.out, wsdPrng.n);
+ N -= wsdPrng.n;
+ zBuf += wsdPrng.n;
+ }
+ wsdPrng.s[12]++;
+ chacha_block((u32*)wsdPrng.out, wsdPrng.s);
+ wsdPrng.n = 64;
+ }
sqlite3_mutex_leave(mutex);
}
@@ -29243,13 +33793,13 @@ SQLITE_PRIVATE int sqlite3ThreadCreate(
memset(p, 0, sizeof(*p));
p->xTask = xTask;
p->pIn = pIn;
- /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
+ /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
** function that returns SQLITE_ERROR when passed the argument 200, that
- ** forces worker threads to run sequentially and deterministically
+ ** forces worker threads to run sequentially and deterministically
** for testing purposes. */
if( sqlite3FaultSim(200) ){
rc = 1;
- }else{
+ }else{
rc = pthread_create(&p->tid, 0, xTask, pIn);
}
if( rc ){
@@ -29331,9 +33881,9 @@ SQLITE_PRIVATE int sqlite3ThreadCreate(
*ppThread = 0;
p = sqlite3Malloc(sizeof(*p));
if( p==0 ) return SQLITE_NOMEM_BKPT;
- /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
+ /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
** function that returns SQLITE_ERROR when passed the argument 200, that
- ** forces worker threads to run sequentially and deterministically
+ ** forces worker threads to run sequentially and deterministically
** (via the sqlite3FaultSim() term of the conditional) for testing
** purposes. */
if( sqlite3GlobalConfig.bCoreMutex==0 || sqlite3FaultSim(200) ){
@@ -29462,7 +34012,7 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file contains routines used to translate between UTF-8,
+** This file contains routines used to translate between UTF-8,
** UTF-16, UTF-16BE, and UTF-16LE.
**
** Notes on UTF-8:
@@ -29558,26 +34108,6 @@ static const unsigned char sqlite3Utf8Trans1[] = {
} \
}
-#define READ_UTF16LE(zIn, TERM, c){ \
- c = (*zIn++); \
- c += ((*zIn++)<<8); \
- if( c>=0xD800 && c<0xE000 && TERM ){ \
- int c2 = (*zIn++); \
- c2 += ((*zIn++)<<8); \
- c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
- } \
-}
-
-#define READ_UTF16BE(zIn, TERM, c){ \
- c = ((*zIn++)<<8); \
- c += (*zIn++); \
- if( c>=0xD800 && c<0xE000 && TERM ){ \
- int c2 = ((*zIn++)<<8); \
- c2 += (*zIn++); \
- c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
- } \
-}
-
/*
** Translate a single UTF-8 character. Return the unicode value.
**
@@ -29637,13 +34167,44 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read(
return c;
}
-
+/*
+** Read a single UTF8 character out of buffer z[], but reading no
+** more than n characters from the buffer. z[] is not zero-terminated.
+**
+** Return the number of bytes used to construct the character.
+**
+** Invalid UTF8 might generate a strange result. No effort is made
+** to detect invalid UTF8.
+**
+** At most 4 bytes will be read out of z[]. The return value will always
+** be between 1 and 4.
+*/
+SQLITE_PRIVATE int sqlite3Utf8ReadLimited(
+ const u8 *z,
+ int n,
+ u32 *piOut
+){
+ u32 c;
+ int i = 1;
+ assert( n>0 );
+ c = z[0];
+ if( c>=0xc0 ){
+ c = sqlite3Utf8Trans1[c-0xc0];
+ if( n>4 ) n = 4;
+ while( i<n && (z[i] & 0xc0)==0x80 ){
+ c = (c<<6) + (0x3f & z[i]);
+ i++;
+ }
+ }
+ *piOut = c;
+ return i;
+}
/*
** If the TRANSLATE_TRACE macro is defined, the value of each Mem is
** printed on stderr on the way into and out of sqlite3VdbeMemTranslate().
-*/
+*/
/* #define TRANSLATE_TRACE 1 */
#ifndef SQLITE_OMIT_UTF16
@@ -29653,11 +34214,11 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read(
** encoding, or if *pMem does not contain a string value.
*/
SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
- int len; /* Maximum length of output string in bytes */
- unsigned char *zOut; /* Output buffer */
- unsigned char *zIn; /* Input iterator */
- unsigned char *zTerm; /* End of input */
- unsigned char *z; /* Output iterator */
+ sqlite3_int64 len; /* Maximum length of output string in bytes */
+ unsigned char *zOut; /* Output buffer */
+ unsigned char *zIn; /* Input iterator */
+ unsigned char *zTerm; /* End of input */
+ unsigned char *z; /* Output iterator */
unsigned int c;
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
@@ -29668,13 +34229,15 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desired
#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG)
{
- char zBuf[100];
- sqlite3VdbeMemPrettyPrint(pMem, zBuf);
- fprintf(stderr, "INPUT: %s\n", zBuf);
+ StrAccum acc;
+ char zBuf[1000];
+ sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
+ sqlite3VdbeMemPrettyPrint(pMem, &acc);
+ fprintf(stderr, "INPUT: %s\n", sqlite3StrAccumFinish(&acc));
}
#endif
- /* If the translation is between UTF-16 little and big endian, then
+ /* If the translation is between UTF-16 little and big endian, then
** all that is required is to swap the byte order. This case is handled
** differently from the others.
*/
@@ -29706,14 +34269,14 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desired
** nul-terminator.
*/
pMem->n &= ~1;
- len = pMem->n * 2 + 1;
+ len = 2 * (sqlite3_int64)pMem->n + 1;
}else{
/* When converting from UTF-8 to UTF-16 the maximum growth is caused
** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16
** character. Two bytes are required in the output buffer for the
** nul-terminator.
*/
- len = pMem->n * 2 + 2;
+ len = 2 * (sqlite3_int64)pMem->n + 2;
}
/* Set zIn to point at the start of the input buffer and zTerm to point 1
@@ -29752,13 +34315,59 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desired
if( pMem->enc==SQLITE_UTF16LE ){
/* UTF-16 Little-endian -> UTF-8 */
while( zIn<zTerm ){
- READ_UTF16LE(zIn, zIn<zTerm, c);
+ c = *(zIn++);
+ c += (*(zIn++))<<8;
+ if( c>=0xd800 && c<0xe000 ){
+#ifdef SQLITE_REPLACE_INVALID_UTF
+ if( c>=0xdc00 || zIn>=zTerm ){
+ c = 0xfffd;
+ }else{
+ int c2 = *(zIn++);
+ c2 += (*(zIn++))<<8;
+ if( c2<0xdc00 || c2>=0xe000 ){
+ zIn -= 2;
+ c = 0xfffd;
+ }else{
+ c = ((c&0x3ff)<<10) + (c2&0x3ff) + 0x10000;
+ }
+ }
+#else
+ if( zIn<zTerm ){
+ int c2 = (*zIn++);
+ c2 += ((*zIn++)<<8);
+ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10);
+ }
+#endif
+ }
WRITE_UTF8(z, c);
}
}else{
/* UTF-16 Big-endian -> UTF-8 */
while( zIn<zTerm ){
- READ_UTF16BE(zIn, zIn<zTerm, c);
+ c = (*(zIn++))<<8;
+ c += *(zIn++);
+ if( c>=0xd800 && c<0xe000 ){
+#ifdef SQLITE_REPLACE_INVALID_UTF
+ if( c>=0xdc00 || zIn>=zTerm ){
+ c = 0xfffd;
+ }else{
+ int c2 = (*(zIn++))<<8;
+ c2 += *(zIn++);
+ if( c2<0xdc00 || c2>=0xe000 ){
+ zIn -= 2;
+ c = 0xfffd;
+ }else{
+ c = ((c&0x3ff)<<10) + (c2&0x3ff) + 0x10000;
+ }
+ }
+#else
+ if( zIn<zTerm ){
+ int c2 = ((*zIn++)<<8);
+ c2 += (*zIn++);
+ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10);
+ }
+#endif
+ }
WRITE_UTF8(z, c);
}
}
@@ -29767,9 +34376,9 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desired
*z = 0;
assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len );
- c = pMem->flags;
+ c = MEM_Str|MEM_Term|(pMem->flags&(MEM_AffMask|MEM_Subtype));
sqlite3VdbeMemRelease(pMem);
- pMem->flags = MEM_Str|MEM_Term|(c&(MEM_AffMask|MEM_Subtype));
+ pMem->flags = c;
pMem->enc = desiredEnc;
pMem->z = (char*)zOut;
pMem->zMalloc = pMem->z;
@@ -29778,9 +34387,11 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desired
translate_out:
#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG)
{
- char zBuf[100];
- sqlite3VdbeMemPrettyPrint(pMem, zBuf);
- fprintf(stderr, "OUTPUT: %s\n", zBuf);
+ StrAccum acc;
+ char zBuf[1000];
+ sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
+ sqlite3VdbeMemPrettyPrint(pMem, &acc);
+ fprintf(stderr, "OUTPUT: %s\n", sqlite3StrAccumFinish(&acc));
}
#endif
return SQLITE_OK;
@@ -29789,7 +34400,7 @@ translate_out:
#ifndef SQLITE_OMIT_UTF16
/*
-** This routine checks for a byte-order mark at the beginning of the
+** This routine checks for a byte-order mark at the beginning of the
** UTF-16 string stored in *pMem. If one is present, it is removed and
** the encoding of the Mem adjusted. This routine does not do any
** byte-swapping, it just sets Mem.enc appropriately.
@@ -29812,7 +34423,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemHandleBom(Mem *pMem){
bom = SQLITE_UTF16LE;
}
}
-
+
if( bom ){
rc = sqlite3VdbeMemMakeWriteable(pMem);
if( rc==SQLITE_OK ){
@@ -29832,7 +34443,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemHandleBom(Mem *pMem){
** pZ is a UTF-8 encoded unicode string. If nByte is less than zero,
** return the number of unicode characters in pZ up to (but not including)
** the first 0x00 byte. If nByte is not less than zero, return the
-** number of unicode characters in the first nByte of pZ (or up to
+** number of unicode characters in the first nByte of pZ (or up to
** the first 0x00, whichever comes first).
*/
SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *zIn, int nByte){
@@ -29852,7 +34463,7 @@ SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *zIn, int nByte){
return r;
}
-/* This test function is not currently used by the automated test-suite.
+/* This test function is not currently used by the automated test-suite.
** Hence it is only available in debug builds.
*/
#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
@@ -29914,19 +34525,16 @@ SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *zIn, int nChar){
int c;
unsigned char const *z = zIn;
int n = 0;
-
- if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){
- while( n<nChar ){
- READ_UTF16BE(z, 1, c);
- n++;
- }
- }else{
- while( n<nChar ){
- READ_UTF16LE(z, 1, c);
- n++;
- }
+
+ if( SQLITE_UTF16NATIVE==SQLITE_UTF16LE ) z++;
+ while( n<nChar ){
+ c = z[0];
+ z += 2;
+ if( c>=0xd8 && c<0xdc && z[0]>=0xdc && z[0]<0xe0 ) z += 2;
+ n++;
}
- return (int)(z-(unsigned char const *)zIn);
+ return (int)(z-(unsigned char const *)zIn)
+ - (SQLITE_UTF16NATIVE==SQLITE_UTF16LE);
}
#if defined(SQLITE_TEST)
@@ -29956,30 +34564,6 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){
assert( c==t );
assert( (z-zBuf)==n );
}
- for(i=0; i<0x00110000; i++){
- if( i>=0xD800 && i<0xE000 ) continue;
- z = zBuf;
- WRITE_UTF16LE(z, i);
- n = (int)(z-zBuf);
- assert( n>0 && n<=4 );
- z[0] = 0;
- z = zBuf;
- READ_UTF16LE(z, 1, c);
- assert( c==i );
- assert( (z-zBuf)==n );
- }
- for(i=0; i<0x00110000; i++){
- if( i>=0xD800 && i<0xE000 ) continue;
- z = zBuf;
- WRITE_UTF16BE(z, i);
- n = (int)(z-zBuf);
- assert( n>0 && n<=4 );
- z[0] = 0;
- z = zBuf;
- READ_UTF16BE(z, 1, c);
- assert( c==i );
- assert( (z-zBuf)==n );
- }
}
#endif /* SQLITE_TEST */
#endif /* SQLITE_OMIT_UTF16 */
@@ -30005,30 +34589,28 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){
*/
/* #include "sqliteInt.h" */
/* #include <stdarg.h> */
-#if HAVE_ISNAN || SQLITE_HAVE_ISNAN
-# include <math.h>
-#endif
-
-/*
-** Routine needed to support the testcase() macro.
-*/
-#ifdef SQLITE_COVERAGE_TEST
-SQLITE_PRIVATE void sqlite3Coverage(int x){
- static unsigned dummy = 0;
- dummy += (unsigned)x;
-}
+#ifndef SQLITE_OMIT_FLOATING_POINT
+#include <math.h>
#endif
/*
-** Give a callback to the test harness that can be used to simulate faults
-** in places where it is difficult or expensive to do so purely by means
-** of inputs.
+** Calls to sqlite3FaultSim() are used to simulate a failure during testing,
+** or to bypass normal error detection during testing in order to let
+** execute proceed further downstream.
**
-** The intent of the integer argument is to let the fault simulator know
-** which of multiple sqlite3FaultSim() calls has been hit.
+** In deployment, sqlite3FaultSim() *always* return SQLITE_OK (0). The
+** sqlite3FaultSim() function only returns non-zero during testing.
**
-** Return whatever integer value the test callback returns, or return
-** SQLITE_OK if no test callback is installed.
+** During testing, if the test harness has set a fault-sim callback using
+** a call to sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL), then
+** each call to sqlite3FaultSim() is relayed to that application-supplied
+** callback and the integer return value form the application-supplied
+** callback is returned by sqlite3FaultSim().
+**
+** The integer argument to sqlite3FaultSim() is a code to identify which
+** sqlite3FaultSim() instance is being invoked. Each call to sqlite3FaultSim()
+** should have a unique code. To prevent legacy testing applications from
+** breaking, the codes should not be changed or reused.
*/
#ifndef SQLITE_UNTESTABLE
SQLITE_PRIVATE int sqlite3FaultSim(int iTest){
@@ -30047,36 +34629,10 @@ SQLITE_PRIVATE int sqlite3FaultSim(int iTest){
SQLITE_PRIVATE int sqlite3IsNaN(double x){
int rc; /* The value return */
#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN
- /*
- ** Systems that support the isnan() library function should probably
- ** make use of it by compiling with -DSQLITE_HAVE_ISNAN. But we have
- ** found that many systems do not have a working isnan() function so
- ** this implementation is provided as an alternative.
- **
- ** This NaN test sometimes fails if compiled on GCC with -ffast-math.
- ** On the other hand, the use of -ffast-math comes with the following
- ** warning:
- **
- ** This option [-ffast-math] should never be turned on by any
- ** -O option since it can result in incorrect output for programs
- ** which depend on an exact implementation of IEEE or ISO
- ** rules/specifications for math functions.
- **
- ** Under MSVC, this NaN test may fail if compiled with a floating-
- ** point precision mode other than /fp:precise. From the MSDN
- ** documentation:
- **
- ** The compiler [with /fp:precise] will properly handle comparisons
- ** involving NaN. For example, x != x evaluates to true if x is NaN
- ** ...
- */
-#ifdef __FAST_MATH__
-# error SQLite will not work correctly with the -ffast-math option of GCC.
-#endif
- volatile double y = x;
- volatile double z = y;
- rc = (y!=z);
-#else /* if HAVE_ISNAN */
+ u64 y;
+ memcpy(&y,&x,sizeof(y));
+ rc = IsNaN(y);
+#else
rc = isnan(x);
#endif /* HAVE_ISNAN */
testcase( rc );
@@ -30098,15 +34654,21 @@ SQLITE_PRIVATE int sqlite3Strlen30(const char *z){
}
/*
-** Return the declared type of a column. Or return zDflt if the column
+** Return the declared type of a column. Or return zDflt if the column
** has no declared type.
**
** The column type is an extra string stored after the zero-terminator on
** the column name if and only if the COLFLAG_HASTYPE flag is set.
*/
SQLITE_PRIVATE char *sqlite3ColumnType(Column *pCol, char *zDflt){
- if( (pCol->colFlags & COLFLAG_HASTYPE)==0 ) return zDflt;
- return pCol->zName + strlen(pCol->zName) + 1;
+ if( pCol->colFlags & COLFLAG_HASTYPE ){
+ return pCol->zCnName + strlen(pCol->zCnName) + 1;
+ }else if( pCol->eCType ){
+ assert( pCol->eCType<=SQLITE_N_STDTYPE );
+ return (char*)sqlite3StdType[pCol->eCType-1];
+ }else{
+ return zDflt;
+ }
}
/*
@@ -30127,7 +34689,22 @@ static SQLITE_NOINLINE void sqlite3ErrorFinish(sqlite3 *db, int err_code){
SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){
assert( db!=0 );
db->errCode = err_code;
- if( err_code || db->pErr ) sqlite3ErrorFinish(db, err_code);
+ if( err_code || db->pErr ){
+ sqlite3ErrorFinish(db, err_code);
+ }else{
+ db->errByteOffset = -1;
+ }
+}
+
+/*
+** The equivalent of sqlite3Error(db, SQLITE_OK). Clear the error state
+** and error message.
+*/
+SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3 *db){
+ assert( db!=0 );
+ db->errCode = SQLITE_OK;
+ db->errByteOffset = -1;
+ if( db->pErr ) sqlite3ValueSetNull(db->pErr);
}
/*
@@ -30136,6 +34713,23 @@ SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){
*/
SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
if( rc==SQLITE_IOERR_NOMEM ) return;
+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
+ if( rc==SQLITE_IOERR_IN_PAGE ){
+ int ii;
+ int iErr;
+ sqlite3BtreeEnterAll(db);
+ for(ii=0; ii<db->nDb; ii++){
+ if( db->aDb[ii].pBt ){
+ iErr = sqlite3PagerWalSystemErrno(sqlite3BtreePager(db->aDb[ii].pBt));
+ if( iErr ){
+ db->iSysErrno = iErr;
+ }
+ }
+ }
+ sqlite3BtreeLeaveAll(db);
+ return;
+ }
+#endif
rc &= 0xff;
if( rc==SQLITE_CANTOPEN || rc==SQLITE_IOERR ){
db->iSysErrno = sqlite3OsGetLastError(db->pVfs);
@@ -30147,17 +34741,8 @@ SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
** handle "db". The error code is set to "err_code".
**
** If it is not NULL, string zFormat specifies the format of the
-** error string in the style of the printf functions: The following
-** format characters are allowed:
-**
-** %s Insert a string
-** %z A string that should be freed after use
-** %d Insert an integer
-** %T Insert a token
-** %S Insert the first element of a SrcList
-**
-** zFormat and any string tokens that follow it are assumed to be
-** encoded in UTF-8.
+** error string. zFormat and any string tokens that follow it are
+** assumed to be encoded in UTF-8.
**
** To clear the most recent error for sqlite handle "db", sqlite3Error
** should be called with err_code set to SQLITE_OK and zFormat set
@@ -30180,14 +34765,31 @@ SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3 *db, int err_code, const char *z
}
/*
+** Check for interrupts and invoke progress callback.
+*/
+SQLITE_PRIVATE void sqlite3ProgressCheck(Parse *p){
+ sqlite3 *db = p->db;
+ if( AtomicLoad(&db->u1.isInterrupted) ){
+ p->nErr++;
+ p->rc = SQLITE_INTERRUPT;
+ }
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ if( db->xProgress ){
+ if( p->rc==SQLITE_INTERRUPT ){
+ p->nProgressSteps = 0;
+ }else if( (++p->nProgressSteps)>=db->nProgressOps ){
+ if( db->xProgress(db->pProgressArg) ){
+ p->nErr++;
+ p->rc = SQLITE_INTERRUPT;
+ }
+ p->nProgressSteps = 0;
+ }
+ }
+#endif
+}
+
+/*
** Add an error message to pParse->zErrMsg and increment pParse->nErr.
-** The following formatting characters are allowed:
-**
-** %s Insert a string
-** %z A string that should be freed after use
-** %d Insert an integer
-** %T Insert a token
-** %S Insert the first element of a SrcList
**
** This function should be used to report any error that occurs while
** compiling an SQL statement (i.e. within sqlite3_prepare()). The
@@ -30200,20 +34802,42 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
char *zMsg;
va_list ap;
sqlite3 *db = pParse->db;
+ assert( db!=0 );
+ assert( db->pParse==pParse || db->pParse->pToplevel==pParse );
+ db->errByteOffset = -2;
va_start(ap, zFormat);
zMsg = sqlite3VMPrintf(db, zFormat, ap);
va_end(ap);
+ if( db->errByteOffset<-1 ) db->errByteOffset = -1;
if( db->suppressErr ){
sqlite3DbFree(db, zMsg);
+ if( db->mallocFailed ){
+ pParse->nErr++;
+ pParse->rc = SQLITE_NOMEM;
+ }
}else{
pParse->nErr++;
sqlite3DbFree(db, pParse->zErrMsg);
pParse->zErrMsg = zMsg;
pParse->rc = SQLITE_ERROR;
+ pParse->pWith = 0;
}
}
/*
+** If database connection db is currently parsing SQL, then transfer
+** error code errCode to that parser if the parser has not already
+** encountered some other kind of error.
+*/
+SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3 *db, int errCode){
+ Parse *pParse;
+ if( db==0 || (pParse = db->pParse)==0 ) return errCode;
+ pParse->rc = errCode;
+ pParse->nErr++;
+ return errCode;
+}
+
+/*
** Convert an SQL-style quoted string into a normal string by removing
** the quote characters. The conversion is done in-place. If the
** input does not begin with a quote character, then this routine
@@ -30226,7 +34850,7 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
** dequoted string, exclusive of the zero terminator, if dequoting does
** occur.
**
-** 2002-Feb-14: This routine is extended to remove MS-Access style
+** 2002-02-14: This routine is extended to remove MS-Access style
** brackets from around identifiers. For example: "[a-b-c]" becomes
** "a-b-c".
*/
@@ -30252,6 +34876,34 @@ SQLITE_PRIVATE void sqlite3Dequote(char *z){
}
z[j] = 0;
}
+SQLITE_PRIVATE void sqlite3DequoteExpr(Expr *p){
+ assert( !ExprHasProperty(p, EP_IntValue) );
+ assert( sqlite3Isquote(p->u.zToken[0]) );
+ p->flags |= p->u.zToken[0]=='"' ? EP_Quoted|EP_DblQuoted : EP_Quoted;
+ sqlite3Dequote(p->u.zToken);
+}
+
+/*
+** If the input token p is quoted, try to adjust the token to remove
+** the quotes. This is not always possible:
+**
+** "abc" -> abc
+** "ab""cd" -> (not possible because of the interior "")
+**
+** Remove the quotes if possible. This is a optimization. The overall
+** system should still return the correct answer even if this routine
+** is always a no-op.
+*/
+SQLITE_PRIVATE void sqlite3DequoteToken(Token *p){
+ unsigned int i;
+ if( p->n<2 ) return;
+ if( !sqlite3Isquote(p->z[0]) ) return;
+ for(i=1; i<p->n-1; i++){
+ if( sqlite3Isquote(p->z[i]) ) return;
+ }
+ p->n -= 2;
+ p->z++;
+}
/*
** Generate a Token object from a string
@@ -30284,12 +34936,18 @@ SQLITE_API int sqlite3_stricmp(const char *zLeft, const char *zRight){
}
SQLITE_PRIVATE int sqlite3StrICmp(const char *zLeft, const char *zRight){
unsigned char *a, *b;
- int c;
+ int c, x;
a = (unsigned char *)zLeft;
b = (unsigned char *)zRight;
for(;;){
- c = (int)UpperToLower[*a] - (int)UpperToLower[*b];
- if( c || *a==0 ) break;
+ c = *a;
+ x = *b;
+ if( c==x ){
+ if( c==0 ) break;
+ }else{
+ c = (int)UpperToLower[c] - (int)UpperToLower[x];
+ if( c ) break;
+ }
a++;
b++;
}
@@ -30309,42 +34967,52 @@ SQLITE_API int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
}
/*
-** Compute 10 to the E-th power. Examples: E==1 results in 10.
-** E==2 results in 100. E==50 results in 1.0e50.
-**
-** This routine only works for values of E between 1 and 341.
+** Compute an 8-bit hash on a string that is insensitive to case differences
*/
-static LONGDOUBLE_TYPE sqlite3Pow10(int E){
-#if defined(_MSC_VER)
- static const LONGDOUBLE_TYPE x[] = {
- 1.0e+001,
- 1.0e+002,
- 1.0e+004,
- 1.0e+008,
- 1.0e+016,
- 1.0e+032,
- 1.0e+064,
- 1.0e+128,
- 1.0e+256
- };
- LONGDOUBLE_TYPE r = 1.0;
- int i;
- assert( E>=0 && E<=307 );
- for(i=0; E!=0; i++, E >>=1){
- if( E & 1 ) r *= x[i];
- }
- return r;
-#else
- LONGDOUBLE_TYPE x = 10.0;
- LONGDOUBLE_TYPE r = 1.0;
- while(1){
- if( E & 1 ) r *= x;
- E >>= 1;
- if( E==0 ) break;
- x *= x;
+SQLITE_PRIVATE u8 sqlite3StrIHash(const char *z){
+ u8 h = 0;
+ if( z==0 ) return 0;
+ while( z[0] ){
+ h += UpperToLower[(unsigned char)z[0]];
+ z++;
}
- return r;
-#endif
+ return h;
+}
+
+/* Double-Double multiplication. (x[0],x[1]) *= (y,yy)
+**
+** Reference:
+** T. J. Dekker, "A Floating-Point Technique for Extending the
+** Available Precision". 1971-07-26.
+*/
+static void dekkerMul2(volatile double *x, double y, double yy){
+ /*
+ ** The "volatile" keywords on parameter x[] and on local variables
+ ** below are needed force intermediate results to be truncated to
+ ** binary64 rather than be carried around in an extended-precision
+ ** format. The truncation is necessary for the Dekker algorithm to
+ ** work. Intel x86 floating point might omit the truncation without
+ ** the use of volatile.
+ */
+ volatile double tx, ty, p, q, c, cc;
+ double hx, hy;
+ u64 m;
+ memcpy(&m, (void*)&x[0], 8);
+ m &= 0xfffffffffc000000LL;
+ memcpy(&hx, &m, 8);
+ tx = x[0] - hx;
+ memcpy(&m, &y, 8);
+ m &= 0xfffffffffc000000LL;
+ memcpy(&hy, &m, 8);
+ ty = y - hy;
+ p = hx*hy;
+ q = hx*ty + tx*hy;
+ c = p+q;
+ cc = p - c + q + tx*ty;
+ cc = x[0]*yy + x[1]*y + cc;
+ x[0] = c + cc;
+ x[1] = c - x[0];
+ x[1] += cc;
}
/*
@@ -30355,8 +35023,15 @@ static LONGDOUBLE_TYPE sqlite3Pow10(int E){
** uses the encoding enc. The string is not necessarily zero-terminated.
**
** Return TRUE if the result is a valid real number (or integer) and FALSE
-** if the string is empty or contains extraneous text. Valid numbers
-** are in one of these formats:
+** if the string is empty or contains extraneous text. More specifically
+** return
+** 1 => The input string is a pure integer
+** 2 or more => The input has a decimal point or eNNN clause
+** 0 or less => The input string is not a valid number
+** -1 => Not a valid number, but has a valid prefix which
+** includes a decimal point and/or an eNNN clause
+**
+** Valid numbers are in one of these formats:
**
** [+-]digits[E[+-]digits]
** [+-]digits.[digits][E[+-]digits]
@@ -30369,32 +35044,39 @@ static LONGDOUBLE_TYPE sqlite3Pow10(int E){
** returns FALSE but it still converts the prefix and writes the result
** into *pResult.
*/
+#if defined(_MSC_VER)
+#pragma warning(disable : 4756)
+#endif
SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
#ifndef SQLITE_OMIT_FLOATING_POINT
int incr;
- const char *zEnd = z + length;
+ const char *zEnd;
/* sign * significand * (10 ^ (esign * exponent)) */
int sign = 1; /* sign of significand */
- i64 s = 0; /* significand */
+ u64 s = 0; /* significand */
int d = 0; /* adjust exponent for shifting decimal point */
int esign = 1; /* sign of exponent */
int e = 0; /* exponent */
int eValid = 1; /* True exponent is either not used or is well-formed */
- double result;
- int nDigits = 0;
- int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */
+ int nDigit = 0; /* Number of digits processed */
+ int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
*pResult = 0.0; /* Default return value, in case of an error */
+ if( length==0 ) return 0;
if( enc==SQLITE_UTF8 ){
incr = 1;
+ zEnd = z + length;
}else{
int i;
incr = 2;
+ length &= ~1;
assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
+ testcase( enc==SQLITE_UTF16LE );
+ testcase( enc==SQLITE_UTF16BE );
for(i=3-enc; i<length && z[i]==0; i+=2){}
- nonNum = i<length;
+ if( i<length ) eType = -100;
zEnd = &z[i^1];
z += (enc&1);
}
@@ -30412,27 +35094,30 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
}
/* copy max significant digits to significand */
- while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
+ while( z<zEnd && sqlite3Isdigit(*z) ){
s = s*10 + (*z - '0');
- z+=incr; nDigits++;
+ z+=incr; nDigit++;
+ if( s>=((LARGEST_UINT64-9)/10) ){
+ /* skip non-significant significand digits
+ ** (increase exponent by d to shift decimal left) */
+ while( z<zEnd && sqlite3Isdigit(*z) ){ z+=incr; d++; }
+ }
}
-
- /* skip non-significant significand digits
- ** (increase exponent by d to shift decimal left) */
- while( z<zEnd && sqlite3Isdigit(*z) ){ z+=incr; nDigits++; d++; }
if( z>=zEnd ) goto do_atof_calc;
/* if decimal point is present */
if( *z=='.' ){
z+=incr;
+ eType++;
/* copy digits from after decimal to significand
** (decrease exponent by d to shift decimal right) */
while( z<zEnd && sqlite3Isdigit(*z) ){
- if( s<((LARGEST_INT64-9)/10) ){
+ if( s<((LARGEST_UINT64-9)/10) ){
s = s*10 + (*z - '0');
d--;
+ nDigit++;
}
- z+=incr; nDigits++;
+ z+=incr;
}
}
if( z>=zEnd ) goto do_atof_calc;
@@ -30441,8 +35126,9 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
if( *z=='e' || *z=='E' ){
z+=incr;
eValid = 0;
+ eType++;
- /* This branch is needed to avoid a (harmless) buffer overread. The
+ /* This branch is needed to avoid a (harmless) buffer overread. The
** special comment alerts the mutation tester that the correct answer
** is obtained even if the branch is omitted */
if( z>=zEnd ) goto do_atof_calc; /*PREVENTS-HARMLESS-OVERREAD*/
@@ -30466,84 +35152,133 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
do_atof_calc:
- /* adjust exponent by d, and update sign */
- e = (e*esign) + d;
- if( e<0 ) {
- esign = -1;
- e *= -1;
- } else {
- esign = 1;
+ /* Zero is a special case */
+ if( s==0 ){
+ *pResult = sign<0 ? -0.0 : +0.0;
+ goto atof_return;
}
- if( s==0 ) {
- /* In the IEEE 754 standard, zero is signed. */
- result = sign<0 ? -(double)0 : (double)0;
- } else {
- /* Attempt to reduce exponent.
- **
- ** Branches that are not required for the correct answer but which only
- ** help to obtain the correct answer faster are marked with special
- ** comments, as a hint to the mutation tester.
- */
- while( e>0 ){ /*OPTIMIZATION-IF-TRUE*/
- if( esign>0 ){
- if( s>=(LARGEST_INT64/10) ) break; /*OPTIMIZATION-IF-FALSE*/
- s *= 10;
- }else{
- if( s%10!=0 ) break; /*OPTIMIZATION-IF-FALSE*/
- s /= 10;
- }
- e--;
- }
+ /* adjust exponent by d, and update sign */
+ e = (e*esign) + d;
- /* adjust the sign of significand */
- s = sign<0 ? -s : s;
+ /* Try to adjust the exponent to make it smaller */
+ while( e>0 && s<(LARGEST_UINT64/10) ){
+ s *= 10;
+ e--;
+ }
+ while( e<0 && (s%10)==0 ){
+ s /= 10;
+ e++;
+ }
- if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/
- result = (double)s;
+ if( e==0 ){
+ *pResult = s;
+ }else if( sqlite3Config.bUseLongDouble ){
+ LONGDOUBLE_TYPE r = (LONGDOUBLE_TYPE)s;
+ if( e>0 ){
+ while( e>=100 ){ e-=100; r *= 1.0e+100L; }
+ while( e>=10 ){ e-=10; r *= 1.0e+10L; }
+ while( e>=1 ){ e-=1; r *= 1.0e+01L; }
}else{
- /* attempt to handle extremely small/large numbers better */
- if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/
- if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/
- LONGDOUBLE_TYPE scale = sqlite3Pow10(e-308);
- if( esign<0 ){
- result = s / scale;
- result /= 1.0e+308;
- }else{
- result = s * scale;
- result *= 1.0e+308;
- }
- }else{ assert( e>=342 );
- if( esign<0 ){
- result = 0.0*s;
- }else{
+ while( e<=-100 ){ e+=100; r *= 1.0e-100L; }
+ while( e<=-10 ){ e+=10; r *= 1.0e-10L; }
+ while( e<=-1 ){ e+=1; r *= 1.0e-01L; }
+ }
+ assert( r>=0.0 );
+ if( r>+1.7976931348623157081452742373e+308L ){
#ifdef INFINITY
- result = INFINITY*s;
+ *pResult = +INFINITY;
#else
- result = 1e308*1e308*s; /* Infinity */
+ *pResult = 1.0e308*10.0;
#endif
- }
- }
- }else{
- LONGDOUBLE_TYPE scale = sqlite3Pow10(e);
- if( esign<0 ){
- result = s / scale;
- }else{
- result = s * scale;
- }
+ }else{
+ *pResult = (double)r;
+ }
+ }else{
+ double rr[2];
+ u64 s2;
+ rr[0] = (double)s;
+ s2 = (u64)rr[0];
+ rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
+ if( e>0 ){
+ while( e>=100 ){
+ e -= 100;
+ dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
+ }
+ while( e>=10 ){
+ e -= 10;
+ dekkerMul2(rr, 1.0e+10, 0.0);
+ }
+ while( e>=1 ){
+ e -= 1;
+ dekkerMul2(rr, 1.0e+01, 0.0);
+ }
+ }else{
+ while( e<=-100 ){
+ e += 100;
+ dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117);
+ }
+ while( e<=-10 ){
+ e += 10;
+ dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27);
+ }
+ while( e<=-1 ){
+ e += 1;
+ dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18);
}
}
+ *pResult = rr[0]+rr[1];
+ if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300;
}
+ if( sign<0 ) *pResult = -*pResult;
+ assert( !sqlite3IsNaN(*pResult) );
- /* store the result */
- *pResult = result;
-
- /* return true if number and no extra non-whitespace chracters after */
- return z==zEnd && nDigits>0 && eValid && nonNum==0;
+atof_return:
+ /* return true if number and no extra non-whitespace characters after */
+ if( z==zEnd && nDigit>0 && eValid && eType>0 ){
+ return eType;
+ }else if( eType>=2 && (eType==3 || eValid) && nDigit>0 ){
+ return -1;
+ }else{
+ return 0;
+ }
#else
return !sqlite3Atoi64(z, pResult, length, enc);
#endif /* SQLITE_OMIT_FLOATING_POINT */
}
+#if defined(_MSC_VER)
+#pragma warning(default : 4756)
+#endif
+
+/*
+** Render an signed 64-bit integer as text. Store the result in zOut[] and
+** return the length of the string that was stored, in bytes. The value
+** returned does not include the zero terminator at the end of the output
+** string.
+**
+** The caller must ensure that zOut[] is at least 21 bytes in size.
+*/
+SQLITE_PRIVATE int sqlite3Int64ToText(i64 v, char *zOut){
+ int i;
+ u64 x;
+ char zTemp[22];
+ if( v<0 ){
+ x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v;
+ }else{
+ x = v;
+ }
+ i = sizeof(zTemp)-2;
+ zTemp[sizeof(zTemp)-1] = 0;
+ while( 1 /*exit-by-break*/ ){
+ zTemp[i] = (x%10) + '0';
+ x = x/10;
+ if( x==0 ) break;
+ i--;
+ };
+ if( v<0 ) zTemp[--i] = '-';
+ memcpy(zOut, &zTemp[i], sizeof(zTemp)-i);
+ return sizeof(zTemp)-1-i;
+}
/*
** Compare the 19-character string zNum against the text representation
@@ -30582,6 +35317,7 @@ static int compare2pow63(const char *zNum, int incr){
**
** Returns:
**
+** -1 Not even a prefix of the input text looks like an integer
** 0 Successful transformation. Fits in a 64-bit signed integer.
** 1 Excess non-space text after the integer value
** 2 Integer too large for a 64-bit signed integer or is malformed
@@ -30606,6 +35342,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
incr = 1;
}else{
incr = 2;
+ length &= ~1;
assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
for(i=3-enc; i<length && zNum[i]==0; i+=2){}
nonNum = i<length;
@@ -30633,7 +35370,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
/* This test and assignment is needed only to suppress UB warnings
** from clang and -fsanitize=undefined. This test and assignment make
** the code a little larger and slower, and no harm comes from omitting
- ** them, but we must appaise the undefined-behavior pharisees. */
+ ** them, but we must appease the undefined-behavior pharisees. */
*pNum = neg ? SMALLEST_INT64 : LARGEST_INT64;
}else if( neg ){
*pNum = -(i64)u;
@@ -30641,9 +35378,9 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
*pNum = (i64)u;
}
rc = 0;
- if( (i==0 && zStart==zNum) /* No digits */
- || nonNum /* UTF16 with high-order bytes non-zero */
- ){
+ if( i==0 && zStart==zNum ){ /* No digits */
+ rc = -1;
+ }else if( nonNum ){ /* UTF16 with high-order bytes non-zero */
rc = 1;
}else if( &zNum[i]<zEnd ){ /* Extra bytes at the end */
int jj = i;
@@ -30705,11 +35442,15 @@ SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char *z, i64 *pOut){
u = u*16 + sqlite3HexToInt(z[k]);
}
memcpy(pOut, &u, 8);
- return (z[k]==0 && k-i<=16) ? 0 : 2;
+ if( k-i>16 ) return 2;
+ if( z[k]!=0 ) return 1;
+ return 0;
}else
#endif /* SQLITE_OMIT_HEX_INTEGER */
{
- return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8);
+ int n = (int)(0x3fffffff&strspn(z,"+- \n\t0123456789"));
+ if( z[n] ) n++;
+ return sqlite3Atoi64(z, pOut, n, SQLITE_UTF8);
}
}
@@ -30741,7 +35482,7 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){
u32 u = 0;
zNum += 2;
while( zNum[0]=='0' ) zNum++;
- for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){
+ for(i=0; i<8 && sqlite3Isxdigit(zNum[i]); i++){
u = u*16 + sqlite3HexToInt(zNum[i]);
}
if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){
@@ -30784,11 +35525,176 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){
*/
SQLITE_PRIVATE int sqlite3Atoi(const char *z){
int x = 0;
- if( z ) sqlite3GetInt32(z, &x);
+ sqlite3GetInt32(z, &x);
return x;
}
/*
+** Decode a floating-point value into an approximate decimal
+** representation.
+**
+** Round the decimal representation to n significant digits if
+** n is positive. Or round to -n signficant digits after the
+** decimal point if n is negative. No rounding is performed if
+** n is zero.
+**
+** The significant digits of the decimal representation are
+** stored in p->z[] which is a often (but not always) a pointer
+** into the middle of p->zBuf[]. There are p->n significant digits.
+** The p->z[] array is *not* zero-terminated.
+*/
+SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){
+ int i;
+ u64 v;
+ int e, exp = 0;
+ p->isSpecial = 0;
+ p->z = p->zBuf;
+
+ /* Convert negative numbers to positive. Deal with Infinity, 0.0, and
+ ** NaN. */
+ if( r<0.0 ){
+ p->sign = '-';
+ r = -r;
+ }else if( r==0.0 ){
+ p->sign = '+';
+ p->n = 1;
+ p->iDP = 1;
+ p->z = "0";
+ return;
+ }else{
+ p->sign = '+';
+ }
+ memcpy(&v,&r,8);
+ e = v>>52;
+ if( (e&0x7ff)==0x7ff ){
+ p->isSpecial = 1 + (v!=0x7ff0000000000000LL);
+ p->n = 0;
+ p->iDP = 0;
+ return;
+ }
+
+ /* Multiply r by powers of ten until it lands somewhere in between
+ ** 1.0e+19 and 1.0e+17.
+ */
+ if( sqlite3Config.bUseLongDouble ){
+ LONGDOUBLE_TYPE rr = r;
+ if( rr>=1.0e+19 ){
+ while( rr>=1.0e+119L ){ exp+=100; rr *= 1.0e-100L; }
+ while( rr>=1.0e+29L ){ exp+=10; rr *= 1.0e-10L; }
+ while( rr>=1.0e+19L ){ exp++; rr *= 1.0e-1L; }
+ }else{
+ while( rr<1.0e-97L ){ exp-=100; rr *= 1.0e+100L; }
+ while( rr<1.0e+07L ){ exp-=10; rr *= 1.0e+10L; }
+ while( rr<1.0e+17L ){ exp--; rr *= 1.0e+1L; }
+ }
+ v = (u64)rr;
+ }else{
+ /* If high-precision floating point is not available using "long double",
+ ** then use Dekker-style double-double computation to increase the
+ ** precision.
+ **
+ ** The error terms on constants like 1.0e+100 computed using the
+ ** decimal extension, for example as follows:
+ **
+ ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100)));
+ */
+ double rr[2];
+ rr[0] = r;
+ rr[1] = 0.0;
+ if( rr[0]>9.223372036854774784e+18 ){
+ while( rr[0]>9.223372036854774784e+118 ){
+ exp += 100;
+ dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117);
+ }
+ while( rr[0]>9.223372036854774784e+28 ){
+ exp += 10;
+ dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27);
+ }
+ while( rr[0]>9.223372036854774784e+18 ){
+ exp += 1;
+ dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18);
+ }
+ }else{
+ while( rr[0]<9.223372036854774784e-83 ){
+ exp -= 100;
+ dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
+ }
+ while( rr[0]<9.223372036854774784e+07 ){
+ exp -= 10;
+ dekkerMul2(rr, 1.0e+10, 0.0);
+ }
+ while( rr[0]<9.22337203685477478e+17 ){
+ exp -= 1;
+ dekkerMul2(rr, 1.0e+01, 0.0);
+ }
+ }
+ v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1];
+ }
+
+
+ /* Extract significant digits. */
+ i = sizeof(p->zBuf)-1;
+ assert( v>0 );
+ while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; }
+ assert( i>=0 && i<sizeof(p->zBuf)-1 );
+ p->n = sizeof(p->zBuf) - 1 - i;
+ assert( p->n>0 );
+ assert( p->n<sizeof(p->zBuf) );
+ p->iDP = p->n + exp;
+ if( iRound<0 ){
+ iRound = p->iDP - iRound;
+ if( iRound==0 && p->zBuf[i+1]>='5' ){
+ iRound = 1;
+ p->zBuf[i--] = '0';
+ p->n++;
+ p->iDP++;
+ }
+ }
+ if( iRound>0 && (iRound<p->n || p->n>mxRound) ){
+ char *z = &p->zBuf[i+1];
+ if( iRound>mxRound ) iRound = mxRound;
+ p->n = iRound;
+ if( z[iRound]>='5' ){
+ int j = iRound-1;
+ while( 1 /*exit-by-break*/ ){
+ z[j]++;
+ if( z[j]<='9' ) break;
+ z[j] = '0';
+ if( j==0 ){
+ p->z[i--] = '1';
+ p->n++;
+ p->iDP++;
+ break;
+ }else{
+ j--;
+ }
+ }
+ }
+ }
+ p->z = &p->zBuf[i+1];
+ assert( i+p->n < sizeof(p->zBuf) );
+ while( ALWAYS(p->n>0) && p->z[p->n-1]=='0' ){ p->n--; }
+}
+
+/*
+** Try to convert z into an unsigned 32-bit integer. Return true on
+** success and false if there is an error.
+**
+** Only decimal notation is accepted.
+*/
+SQLITE_PRIVATE int sqlite3GetUInt32(const char *z, u32 *pI){
+ u64 v = 0;
+ int i;
+ for(i=0; sqlite3Isdigit(z[i]); i++){
+ v = v*10 + z[i] - '0';
+ if( v>4294967296LL ){ *pI = 0; return 0; }
+ }
+ if( i==0 || z[i]!=0 ){ *pI = 0; return 0; }
+ *pI = (u32)v;
+ return 1;
+}
+
+/*
** The variable-length integer encoding is as follows:
**
** KEY:
@@ -30828,7 +35734,7 @@ static int SQLITE_NOINLINE putVarint64(unsigned char *p, u64 v){
v >>= 7;
}
return 9;
- }
+ }
n = 0;
do{
buf[n++] = (u8)((v & 0x7f) | 0x80);
@@ -30874,23 +35780,12 @@ SQLITE_PRIVATE int sqlite3PutVarint(unsigned char *p, u64 v){
SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
u32 a,b,s;
- a = *p;
- /* a: p0 (unmasked) */
- if (!(a&0x80))
- {
- *v = a;
+ if( ((signed char*)p)[0]>=0 ){
+ *v = *p;
return 1;
}
-
- p++;
- b = *p;
- /* b: p1 (unmasked) */
- if (!(b&0x80))
- {
- a &= 0x7f;
- a = a<<7;
- a |= b;
- *v = a;
+ if( ((signed char*)p)[1]>=0 ){
+ *v = ((u32)(p[0]&0x7f)<<7) | p[1];
return 2;
}
@@ -30898,8 +35793,9 @@ SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) );
assert( SLOT_4_2_0 == ((0xfU<<28) | (0x7f<<14) | (0x7f)) );
- p++;
- a = a<<14;
+ a = ((u32)p[0])<<14;
+ b = p[1];
+ p += 2;
a |= *p;
/* a: p0<<14 | p2 (unmasked) */
if (!(a&0x80))
@@ -31038,127 +35934,37 @@ SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
** If the varint stored in p[0] is larger than can fit in a 32-bit unsigned
** integer, then set *v to 0xffffffff.
**
-** A MACRO version, getVarint32, is provided which inlines the
-** single-byte case. All code should use the MACRO version as
+** A MACRO version, getVarint32, is provided which inlines the
+** single-byte case. All code should use the MACRO version as
** this function assumes the single-byte case has already been handled.
*/
SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
- u32 a,b;
+ u64 v64;
+ u8 n;
- /* The 1-byte case. Overwhelmingly the most common. Handled inline
- ** by the getVarin32() macro */
- a = *p;
- /* a: p0 (unmasked) */
-#ifndef getVarint32
- if (!(a&0x80))
- {
- /* Values between 0 and 127 */
- *v = a;
- return 1;
- }
-#endif
+ /* Assume that the single-byte case has already been handled by
+ ** the getVarint32() macro */
+ assert( (p[0] & 0x80)!=0 );
- /* The 2-byte case */
- p++;
- b = *p;
- /* b: p1 (unmasked) */
- if (!(b&0x80))
- {
- /* Values between 128 and 16383 */
- a &= 0x7f;
- a = a<<7;
- *v = a | b;
+ if( (p[1] & 0x80)==0 ){
+ /* This is the two-byte case */
+ *v = ((p[0]&0x7f)<<7) | p[1];
return 2;
}
-
- /* The 3-byte case */
- p++;
- a = a<<14;
- a |= *p;
- /* a: p0<<14 | p2 (unmasked) */
- if (!(a&0x80))
- {
- /* Values between 16384 and 2097151 */
- a &= (0x7f<<14)|(0x7f);
- b &= 0x7f;
- b = b<<7;
- *v = a | b;
+ if( (p[2] & 0x80)==0 ){
+ /* This is the three-byte case */
+ *v = ((p[0]&0x7f)<<14) | ((p[1]&0x7f)<<7) | p[2];
return 3;
}
-
- /* A 32-bit varint is used to store size information in btrees.
- ** Objects are rarely larger than 2MiB limit of a 3-byte varint.
- ** A 3-byte varint is sufficient, for example, to record the size
- ** of a 1048569-byte BLOB or string.
- **
- ** We only unroll the first 1-, 2-, and 3- byte cases. The very
- ** rare larger cases can be handled by the slower 64-bit varint
- ** routine.
- */
-#if 1
- {
- u64 v64;
- u8 n;
-
- p -= 2;
- n = sqlite3GetVarint(p, &v64);
- assert( n>3 && n<=9 );
- if( (v64 & SQLITE_MAX_U32)!=v64 ){
- *v = 0xffffffff;
- }else{
- *v = (u32)v64;
- }
- return n;
- }
-
-#else
- /* For following code (kept for historical record only) shows an
- ** unrolling for the 3- and 4-byte varint cases. This code is
- ** slightly faster, but it is also larger and much harder to test.
- */
- p++;
- b = b<<14;
- b |= *p;
- /* b: p1<<14 | p3 (unmasked) */
- if (!(b&0x80))
- {
- /* Values between 2097152 and 268435455 */
- b &= (0x7f<<14)|(0x7f);
- a &= (0x7f<<14)|(0x7f);
- a = a<<7;
- *v = a | b;
- return 4;
- }
-
- p++;
- a = a<<14;
- a |= *p;
- /* a: p0<<28 | p2<<14 | p4 (unmasked) */
- if (!(a&0x80))
- {
- /* Values between 268435456 and 34359738367 */
- a &= SLOT_4_2_0;
- b &= SLOT_4_2_0;
- b = b<<7;
- *v = a | b;
- return 5;
- }
-
- /* We can only reach this point when reading a corrupt database
- ** file. In that case we are not in any hurry. Use the (relatively
- ** slow) general-purpose sqlite3GetVarint() routine to extract the
- ** value. */
- {
- u64 v64;
- u8 n;
-
- p -= 4;
- n = sqlite3GetVarint(p, &v64);
- assert( n>5 && n<=9 );
+ /* four or more bytes */
+ n = sqlite3GetVarint(p, &v64);
+ assert( n>3 && n<=9 );
+ if( (v64 & SQLITE_MAX_U32)!=v64 ){
+ *v = 0xffffffff;
+ }else{
*v = (u32)v64;
- return n;
}
-#endif
+ return n;
}
/*
@@ -31228,7 +36034,7 @@ SQLITE_PRIVATE u8 sqlite3HexToInt(int h){
return (u8)(h & 0xf);
}
-#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC)
+#if !defined(SQLITE_OMIT_BLOB_LITERAL)
/*
** Convert a BLOB literal of the form "x'hhhhhh'" into its binary
** value. Return a pointer to its binary value. Space to hold the
@@ -31249,7 +36055,7 @@ SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){
}
return zBlob;
}
-#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */
+#endif /* !SQLITE_OMIT_BLOB_LITERAL */
/*
** Log an error that is an API call on a connection pointer that should
@@ -31257,7 +36063,7 @@ SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){
** argument. The zType is a word like "NULL" or "closed" or "invalid".
*/
static void logBadConnection(const char *zType){
- sqlite3_log(SQLITE_MISUSE,
+ sqlite3_log(SQLITE_MISUSE,
"API call with %s database connection pointer",
zType
);
@@ -31278,13 +36084,13 @@ static void logBadConnection(const char *zType){
** used as an argument to sqlite3_errmsg() or sqlite3_close().
*/
SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3 *db){
- u32 magic;
+ u8 eOpenState;
if( db==0 ){
logBadConnection("NULL");
return 0;
}
- magic = db->magic;
- if( magic!=SQLITE_MAGIC_OPEN ){
+ eOpenState = db->eOpenState;
+ if( eOpenState!=SQLITE_STATE_OPEN ){
if( sqlite3SafetyCheckSickOrOk(db) ){
testcase( sqlite3GlobalConfig.xLog!=0 );
logBadConnection("unopened");
@@ -31295,11 +36101,11 @@ SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3 *db){
}
}
SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){
- u32 magic;
- magic = db->magic;
- if( magic!=SQLITE_MAGIC_SICK &&
- magic!=SQLITE_MAGIC_OPEN &&
- magic!=SQLITE_MAGIC_BUSY ){
+ u8 eOpenState;
+ eOpenState = db->eOpenState;
+ if( eOpenState!=SQLITE_STATE_SICK &&
+ eOpenState!=SQLITE_STATE_OPEN &&
+ eOpenState!=SQLITE_STATE_BUSY ){
testcase( sqlite3GlobalConfig.xLog!=0 );
logBadConnection("invalid");
return 0;
@@ -31309,7 +36115,7 @@ SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){
}
/*
-** Attempt to add, substract, or multiply the 64-bit signed value iB against
+** Attempt to add, subtract, or multiply the 64-bit signed value iB against
** the other 64-bit signed integer at *pA and store the result in *pA.
** Return 0 on success. Or if the operation would have resulted in an
** overflow, leave *pA unchanged and return 1.
@@ -31331,7 +36137,7 @@ SQLITE_PRIVATE int sqlite3AddInt64(i64 *pA, i64 iB){
if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1;
}
*pA += iB;
- return 0;
+ return 0;
#endif
}
SQLITE_PRIVATE int sqlite3SubInt64(i64 *pA, i64 iB){
@@ -31372,7 +36178,7 @@ SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){
}
/*
-** Compute the absolute value of a 32-bit signed integer, of possible. Or
+** Compute the absolute value of a 32-bit signed integer, of possible. Or
** if the integer has a value of -2147483648, return +2147483647
*/
SQLITE_PRIVATE int sqlite3AbsInt32(int x){
@@ -31412,11 +36218,11 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
}
#endif
-/*
+/*
** Find (an approximate) sum of two LogEst values. This computation is
** not a simple "+" operator because LogEst is stored as a logarithmic
** value.
-**
+**
*/
SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst a, LogEst b){
static const unsigned char x[] = {
@@ -31464,7 +36270,6 @@ SQLITE_PRIVATE LogEst sqlite3LogEst(u64 x){
return a[x&7] + y - 10;
}
-#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Convert a double into a LogEst
** In other words, compute an approximation for 10*log2(x).
@@ -31479,16 +36284,9 @@ SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double x){
e = (a>>52) - 1022;
return e*10;
}
-#endif /* SQLITE_OMIT_VIRTUALTABLE */
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
- defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
- defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
/*
** Convert a LogEst into an integer.
-**
-** Note that this routine is only used when one or more of various
-** non-standard compile-time options is enabled.
*/
SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
u64 n;
@@ -31496,17 +36294,9 @@ SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
x /= 10;
if( n>=5 ) n -= 2;
else if( n>=1 ) n -= 1;
-#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
- defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
if( x>60 ) return (u64)LARGEST_INT64;
-#else
- /* If only SQLITE_ENABLE_STAT3_OR_STAT4 is on, then the largest input
- ** possible to this routine is 310, resulting in a maximum x of 31 */
- assert( x<=60 );
-#endif
return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x);
}
-#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */
/*
** Add a new name/number pair to a VList. This might require that the
@@ -31530,8 +36320,8 @@ SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
** Conceptually:
**
** struct VList {
-** int nAlloc; // Number of allocated slots
-** int nUsed; // Number of used slots
+** int nAlloc; // Number of allocated slots
+** int nUsed; // Number of used slots
** struct VListEntry {
** int iValue; // Value for this entry
** int nSlot; // Slots used by this entry
@@ -31540,7 +36330,7 @@ SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
** }
**
** During code generation, pointers to the variable names within the
-** VList are taken. When that happens, nAlloc is set to zero as an
+** VList are taken. When that happens, nAlloc is set to zero as an
** indication that the VList may never again be enlarged, since the
** accompanying realloc() would invalidate the pointers.
*/
@@ -31559,7 +36349,7 @@ SQLITE_PRIVATE VList *sqlite3VListAdd(
assert( pIn==0 || pIn[0]>=3 ); /* Verify ok to add new elements */
if( pIn==0 || pIn[1]+nInt > pIn[0] ){
/* Enlarge the allocation */
- int nAlloc = (pIn ? pIn[0]*2 : 10) + nInt;
+ sqlite3_int64 nAlloc = (pIn ? 2*(sqlite3_int64)pIn[0] : 10) + nInt;
VList *pOut = sqlite3DbRealloc(db, pIn, nAlloc*sizeof(int));
if( pOut==0 ) return pIn;
if( pIn==0 ) pOut[1] = 2;
@@ -31611,6 +36401,104 @@ SQLITE_PRIVATE int sqlite3VListNameToNum(VList *pIn, const char *zName, int nNam
return 0;
}
+/*
+** High-resolution hardware timer used for debugging and testing only.
+*/
+#if defined(VDBE_PROFILE) \
+ || defined(SQLITE_PERFORMANCE_TRACE) \
+ || defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+/************** Include hwtime.h in the middle of util.c *********************/
+/************** Begin file hwtime.h ******************************************/
+/*
+** 2008 May 27
+**
+** 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 contains inline asm code for retrieving "high-performance"
+** counters for x86 and x86_64 class CPUs.
+*/
+#ifndef SQLITE_HWTIME_H
+#define SQLITE_HWTIME_H
+
+/*
+** The following routine only works on Pentium-class (or newer) processors.
+** It uses the RDTSC opcode to read the cycle count value out of the
+** processor and returns that value. This can be used for high-res
+** profiling.
+*/
+#if !defined(__STRICT_ANSI__) && \
+ (defined(__GNUC__) || defined(_MSC_VER)) && \
+ (defined(i386) || defined(__i386__) || defined(_M_IX86))
+
+ #if defined(__GNUC__)
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned int lo, hi;
+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
+ return (sqlite_uint64)hi << 32 | lo;
+ }
+
+ #elif defined(_MSC_VER)
+
+ __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
+ __asm {
+ rdtsc
+ ret ; return value at EDX:EAX
+ }
+ }
+
+ #endif
+
+#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__))
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned int lo, hi;
+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
+ return (sqlite_uint64)hi << 32 | lo;
+ }
+
+#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__))
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned long long retval;
+ unsigned long junk;
+ __asm__ __volatile__ ("\n\
+ 1: mftbu %1\n\
+ mftb %L0\n\
+ mftbu %0\n\
+ cmpw %0,%1\n\
+ bne 1b"
+ : "=r" (retval), "=r" (junk));
+ return retval;
+ }
+
+#else
+
+ /*
+ ** asm() is needed for hardware timing support. Without asm(),
+ ** disable the sqlite3Hwtime() routine.
+ **
+ ** sqlite3Hwtime() is only used for some obscure debugging
+ ** and analysis configurations, not in any deliverable, so this
+ ** should not be a great loss.
+ */
+SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
+
+#endif
+
+#endif /* !defined(SQLITE_HWTIME_H) */
+
+/************** End of hwtime.h **********************************************/
+/************** Continuing where we left off in util.c ***********************/
+#endif
+
/************** End of util.c ************************************************/
/************** Begin file hash.c ********************************************/
/*
@@ -31679,20 +36567,6 @@ static unsigned int strHash(const char *z){
}
return h;
}
-#ifdef SQLITE_ENABLE_NORMALIZE
-static unsigned int strHashN(const char *z, int n){
- unsigned int h = 0;
- int i;
- for(i=0; i<n; i++){
- /* Knuth multiplicative hashing. (Sorting & Searching, p. 510).
- ** 0x9e3779b1 is 2654435761 which is the closest prime number to
- ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */
- h += sqlite3UpperToLower[z[i]];
- h *= 0x9e3779b1;
- }
- return h;
-}
-#endif /* SQLITE_ENABLE_NORMALIZE */
/* Link pNew element into the hash table pH. If pEntry!=0 then also
@@ -31726,7 +36600,7 @@ static void insertElement(
}
-/* Resize the hash table so that it cantains "new_size" buckets.
+/* Resize the hash table so that it contains "new_size" buckets.
**
** The hash table might fail to resize if sqlite3_malloc() fails or
** if the new size is the same as the prior size.
@@ -31745,7 +36619,7 @@ static int rehash(Hash *pH, unsigned int new_size){
/* The inability to allocates space for a larger hash table is
** a performance hit but it is not a fatal error. So mark the
- ** allocation as a benign. Use sqlite3Malloc()/memset(0) instead of
+ ** allocation as a benign. Use sqlite3Malloc()/memset(0) instead of
** sqlite3MallocZero() to make the allocation, as sqlite3MallocZero()
** only zeroes the requested number of bytes whereas this module will
** use the actual amount of space allocated for the hash table (which
@@ -31779,7 +36653,7 @@ static HashElem *findElementWithHash(
unsigned int *pHash /* Write the hash value here */
){
HashElem *elem; /* Used to loop thru the element list */
- int count; /* Number of elements left to test */
+ unsigned int count; /* Number of elements left to test */
unsigned int h; /* The computed hash */
static HashElem nullElement = { 0, 0, 0, 0 };
@@ -31795,49 +36669,16 @@ static HashElem *findElementWithHash(
count = pH->count;
}
if( pHash ) *pHash = h;
- while( count-- ){
- assert( elem!=0 );
- if( sqlite3StrICmp(elem->pKey,pKey)==0 ){
- return elem;
- }
- elem = elem->next;
- }
- return &nullElement;
-}
-#ifdef SQLITE_ENABLE_NORMALIZE
-static HashElem *findElementWithHashN(
- const Hash *pH, /* The pH to be searched */
- const char *pKey, /* The key we are searching for */
- int nKey, /* Number of key bytes to use */
- unsigned int *pHash /* Write the hash value here */
-){
- HashElem *elem; /* Used to loop thru the element list */
- int count; /* Number of elements left to test */
- unsigned int h; /* The computed hash */
- static HashElem nullElement = { 0, 0, 0, 0 };
-
- if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/
- struct _ht *pEntry;
- h = strHashN(pKey, nKey) % pH->htsize;
- pEntry = &pH->ht[h];
- elem = pEntry->chain;
- count = pEntry->count;
- }else{
- h = 0;
- elem = pH->first;
- count = pH->count;
- }
- if( pHash ) *pHash = h;
- while( count-- ){
+ while( count ){
assert( elem!=0 );
- if( sqlite3StrNICmp(elem->pKey,pKey,nKey)==0 ){
+ if( sqlite3StrICmp(elem->pKey,pKey)==0 ){
return elem;
}
elem = elem->next;
+ count--;
}
return &nullElement;
}
-#endif /* SQLITE_ENABLE_NORMALIZE */
/* Remove a single entry from the hash table given a pointer to that
** element and a hash on the element's key.
@@ -31849,7 +36690,7 @@ static void removeElementGivenHash(
){
struct _ht *pEntry;
if( elem->prev ){
- elem->prev->next = elem->next;
+ elem->prev->next = elem->next;
}else{
pH->first = elem->next;
}
@@ -31861,8 +36702,8 @@ static void removeElementGivenHash(
if( pEntry->chain==elem ){
pEntry->chain = elem->next;
}
+ assert( pEntry->count>0 );
pEntry->count--;
- assert( pEntry->count>=0 );
}
sqlite3_free( elem );
pH->count--;
@@ -31882,14 +36723,6 @@ SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey){
assert( pKey!=0 );
return findElementWithHash(pH, pKey, 0)->data;
}
-#ifdef SQLITE_ENABLE_NORMALIZE
-SQLITE_PRIVATE void *sqlite3HashFindN(const Hash *pH, const char *pKey, int nKey){
- assert( pH!=0 );
- assert( pKey!=0 );
- assert( nKey>=0 );
- return findElementWithHashN(pH, pKey, nKey, 0)->data;
-}
-#endif /* SQLITE_ENABLE_NORMALIZE */
/* Insert an element into the hash table pH. The key is pKey
** and the data is "data".
@@ -31956,29 +36789,29 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 0 */ "Savepoint" OpHelp(""),
/* 1 */ "AutoCommit" OpHelp(""),
/* 2 */ "Transaction" OpHelp(""),
- /* 3 */ "SorterNext" OpHelp(""),
- /* 4 */ "Prev" OpHelp(""),
- /* 5 */ "Next" OpHelp(""),
- /* 6 */ "Checkpoint" OpHelp(""),
- /* 7 */ "JournalMode" OpHelp(""),
- /* 8 */ "Vacuum" OpHelp(""),
- /* 9 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"),
- /* 10 */ "VUpdate" OpHelp("data=r[P3@P2]"),
- /* 11 */ "Goto" OpHelp(""),
- /* 12 */ "Gosub" OpHelp(""),
- /* 13 */ "InitCoroutine" OpHelp(""),
- /* 14 */ "Yield" OpHelp(""),
- /* 15 */ "MustBeInt" OpHelp(""),
- /* 16 */ "Jump" OpHelp(""),
- /* 17 */ "Once" OpHelp(""),
- /* 18 */ "If" OpHelp(""),
+ /* 3 */ "Checkpoint" OpHelp(""),
+ /* 4 */ "JournalMode" OpHelp(""),
+ /* 5 */ "Vacuum" OpHelp(""),
+ /* 6 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"),
+ /* 7 */ "VUpdate" OpHelp("data=r[P3@P2]"),
+ /* 8 */ "Init" OpHelp("Start at P2"),
+ /* 9 */ "Goto" OpHelp(""),
+ /* 10 */ "Gosub" OpHelp(""),
+ /* 11 */ "InitCoroutine" OpHelp(""),
+ /* 12 */ "Yield" OpHelp(""),
+ /* 13 */ "MustBeInt" OpHelp(""),
+ /* 14 */ "Jump" OpHelp(""),
+ /* 15 */ "Once" OpHelp(""),
+ /* 16 */ "If" OpHelp(""),
+ /* 17 */ "IfNot" OpHelp(""),
+ /* 18 */ "IsType" OpHelp("if typeof(P1.P3) in P5 goto P2"),
/* 19 */ "Not" OpHelp("r[P2]= !r[P1]"),
- /* 20 */ "IfNot" OpHelp(""),
- /* 21 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"),
- /* 22 */ "SeekLT" OpHelp("key=r[P3@P4]"),
- /* 23 */ "SeekLE" OpHelp("key=r[P3@P4]"),
- /* 24 */ "SeekGE" OpHelp("key=r[P3@P4]"),
- /* 25 */ "SeekGT" OpHelp("key=r[P3@P4]"),
+ /* 20 */ "IfNullRow" OpHelp("if P1.nullRow then r[P3]=NULL, goto P2"),
+ /* 21 */ "SeekLT" OpHelp("key=r[P3@P4]"),
+ /* 22 */ "SeekLE" OpHelp("key=r[P3@P4]"),
+ /* 23 */ "SeekGE" OpHelp("key=r[P3@P4]"),
+ /* 24 */ "SeekGT" OpHelp("key=r[P3@P4]"),
+ /* 25 */ "IfNotOpen" OpHelp("if( !csr[P1] ) goto P2"),
/* 26 */ "IfNoHope" OpHelp("key=r[P3@P4]"),
/* 27 */ "NoConflict" OpHelp("key=r[P3@P4]"),
/* 28 */ "NotFound" OpHelp("key=r[P3@P4]"),
@@ -31990,19 +36823,19 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 34 */ "SorterSort" OpHelp(""),
/* 35 */ "Sort" OpHelp(""),
/* 36 */ "Rewind" OpHelp(""),
- /* 37 */ "IdxLE" OpHelp("key=r[P3@P4]"),
- /* 38 */ "IdxGT" OpHelp("key=r[P3@P4]"),
- /* 39 */ "IdxLT" OpHelp("key=r[P3@P4]"),
- /* 40 */ "IdxGE" OpHelp("key=r[P3@P4]"),
- /* 41 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
- /* 42 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
+ /* 37 */ "SorterNext" OpHelp(""),
+ /* 38 */ "Prev" OpHelp(""),
+ /* 39 */ "Next" OpHelp(""),
+ /* 40 */ "IdxLE" OpHelp("key=r[P3@P4]"),
+ /* 41 */ "IdxGT" OpHelp("key=r[P3@P4]"),
+ /* 42 */ "IdxLT" OpHelp("key=r[P3@P4]"),
/* 43 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"),
/* 44 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"),
- /* 45 */ "Program" OpHelp(""),
- /* 46 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
- /* 47 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
- /* 48 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"),
- /* 49 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
+ /* 45 */ "IdxGE" OpHelp("key=r[P3@P4]"),
+ /* 46 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
+ /* 47 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
+ /* 48 */ "Program" OpHelp(""),
+ /* 49 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
/* 50 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"),
/* 51 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"),
/* 52 */ "Ne" OpHelp("IF r[P3]!=r[P1]"),
@@ -32011,128 +36844,1126 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 55 */ "Le" OpHelp("IF r[P3]<=r[P1]"),
/* 56 */ "Lt" OpHelp("IF r[P3]<r[P1]"),
/* 57 */ "Ge" OpHelp("IF r[P3]>=r[P1]"),
- /* 58 */ "ElseNotEq" OpHelp(""),
- /* 59 */ "IncrVacuum" OpHelp(""),
- /* 60 */ "VNext" OpHelp(""),
- /* 61 */ "Init" OpHelp("Start at P2"),
- /* 62 */ "PureFunc0" OpHelp(""),
- /* 63 */ "Function0" OpHelp("r[P3]=func(r[P2@P5])"),
- /* 64 */ "PureFunc" OpHelp(""),
- /* 65 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"),
- /* 66 */ "Return" OpHelp(""),
- /* 67 */ "EndCoroutine" OpHelp(""),
- /* 68 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
- /* 69 */ "Halt" OpHelp(""),
- /* 70 */ "Integer" OpHelp("r[P2]=P1"),
- /* 71 */ "Int64" OpHelp("r[P2]=P4"),
- /* 72 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
- /* 73 */ "Null" OpHelp("r[P2..P3]=NULL"),
- /* 74 */ "SoftNull" OpHelp("r[P1]=NULL"),
- /* 75 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
- /* 76 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
- /* 77 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
- /* 78 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
- /* 79 */ "SCopy" OpHelp("r[P2]=r[P1]"),
- /* 80 */ "IntCopy" OpHelp("r[P2]=r[P1]"),
- /* 81 */ "ResultRow" OpHelp("output=r[P1@P2]"),
- /* 82 */ "CollSeq" OpHelp(""),
- /* 83 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
- /* 84 */ "RealAffinity" OpHelp(""),
- /* 85 */ "Cast" OpHelp("affinity(r[P1])"),
- /* 86 */ "Permutation" OpHelp(""),
- /* 87 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
- /* 88 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"),
- /* 89 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"),
- /* 90 */ "Column" OpHelp("r[P3]=PX"),
- /* 91 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
- /* 92 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
- /* 93 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
- /* 94 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
- /* 95 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
- /* 96 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
- /* 97 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
- /* 98 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
- /* 99 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
- /* 100 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
- /* 101 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
- /* 102 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
- /* 103 */ "BitNot" OpHelp("r[P2]= ~r[P1]"),
- /* 104 */ "Count" OpHelp("r[P2]=count()"),
- /* 105 */ "ReadCookie" OpHelp(""),
- /* 106 */ "String8" OpHelp("r[P2]='P4'"),
- /* 107 */ "SetCookie" OpHelp(""),
- /* 108 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
- /* 109 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
- /* 110 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
- /* 111 */ "OpenDup" OpHelp(""),
- /* 112 */ "OpenAutoindex" OpHelp("nColumn=P2"),
- /* 113 */ "OpenEphemeral" OpHelp("nColumn=P2"),
- /* 114 */ "SorterOpen" OpHelp(""),
- /* 115 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
- /* 116 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
- /* 117 */ "Close" OpHelp(""),
- /* 118 */ "ColumnsUsed" OpHelp(""),
- /* 119 */ "SeekHit" OpHelp("seekHit=P2"),
- /* 120 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
- /* 121 */ "NewRowid" OpHelp("r[P2]=rowid"),
- /* 122 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
- /* 123 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"),
- /* 124 */ "Delete" OpHelp(""),
- /* 125 */ "ResetCount" OpHelp(""),
- /* 126 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
- /* 127 */ "SorterData" OpHelp("r[P2]=data"),
- /* 128 */ "RowData" OpHelp("r[P2]=data"),
- /* 129 */ "Rowid" OpHelp("r[P2]=rowid"),
- /* 130 */ "NullRow" OpHelp(""),
- /* 131 */ "SeekEnd" OpHelp(""),
- /* 132 */ "SorterInsert" OpHelp("key=r[P2]"),
- /* 133 */ "IdxInsert" OpHelp("key=r[P2]"),
- /* 134 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
- /* 135 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"),
- /* 136 */ "IdxRowid" OpHelp("r[P2]=rowid"),
- /* 137 */ "Destroy" OpHelp(""),
- /* 138 */ "Clear" OpHelp(""),
- /* 139 */ "ResetSorter" OpHelp(""),
- /* 140 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"),
- /* 141 */ "Real" OpHelp("r[P2]=P4"),
- /* 142 */ "SqlExec" OpHelp(""),
- /* 143 */ "ParseSchema" OpHelp(""),
- /* 144 */ "LoadAnalysis" OpHelp(""),
- /* 145 */ "DropTable" OpHelp(""),
- /* 146 */ "DropIndex" OpHelp(""),
- /* 147 */ "DropTrigger" OpHelp(""),
- /* 148 */ "IntegrityCk" OpHelp(""),
- /* 149 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
- /* 150 */ "Param" OpHelp(""),
- /* 151 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
- /* 152 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
- /* 153 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
- /* 154 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"),
- /* 155 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
- /* 156 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"),
- /* 157 */ "AggValue" OpHelp("r[P3]=value N=P2"),
- /* 158 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
- /* 159 */ "Expire" OpHelp(""),
- /* 160 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
- /* 161 */ "VBegin" OpHelp(""),
- /* 162 */ "VCreate" OpHelp(""),
- /* 163 */ "VDestroy" OpHelp(""),
- /* 164 */ "VOpen" OpHelp(""),
- /* 165 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
- /* 166 */ "VRename" OpHelp(""),
- /* 167 */ "Pagecount" OpHelp(""),
- /* 168 */ "MaxPgcnt" OpHelp(""),
- /* 169 */ "Trace" OpHelp(""),
- /* 170 */ "CursorHint" OpHelp(""),
- /* 171 */ "Noop" OpHelp(""),
- /* 172 */ "Explain" OpHelp(""),
- /* 173 */ "Abortable" OpHelp(""),
+ /* 58 */ "ElseEq" OpHelp(""),
+ /* 59 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
+ /* 60 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"),
+ /* 61 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
+ /* 62 */ "IncrVacuum" OpHelp(""),
+ /* 63 */ "VNext" OpHelp(""),
+ /* 64 */ "Filter" OpHelp("if key(P3@P4) not in filter(P1) goto P2"),
+ /* 65 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"),
+ /* 66 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"),
+ /* 67 */ "Return" OpHelp(""),
+ /* 68 */ "EndCoroutine" OpHelp(""),
+ /* 69 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
+ /* 70 */ "Halt" OpHelp(""),
+ /* 71 */ "Integer" OpHelp("r[P2]=P1"),
+ /* 72 */ "Int64" OpHelp("r[P2]=P4"),
+ /* 73 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
+ /* 74 */ "BeginSubrtn" OpHelp("r[P2]=NULL"),
+ /* 75 */ "Null" OpHelp("r[P2..P3]=NULL"),
+ /* 76 */ "SoftNull" OpHelp("r[P1]=NULL"),
+ /* 77 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
+ /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
+ /* 79 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
+ /* 80 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
+ /* 81 */ "SCopy" OpHelp("r[P2]=r[P1]"),
+ /* 82 */ "IntCopy" OpHelp("r[P2]=r[P1]"),
+ /* 83 */ "FkCheck" OpHelp(""),
+ /* 84 */ "ResultRow" OpHelp("output=r[P1@P2]"),
+ /* 85 */ "CollSeq" OpHelp(""),
+ /* 86 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
+ /* 87 */ "RealAffinity" OpHelp(""),
+ /* 88 */ "Cast" OpHelp("affinity(r[P1])"),
+ /* 89 */ "Permutation" OpHelp(""),
+ /* 90 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
+ /* 91 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"),
+ /* 92 */ "ZeroOrNull" OpHelp("r[P2] = 0 OR NULL"),
+ /* 93 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"),
+ /* 94 */ "Column" OpHelp("r[P3]=PX cursor P1 column P2"),
+ /* 95 */ "TypeCheck" OpHelp("typecheck(r[P1@P2])"),
+ /* 96 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
+ /* 97 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
+ /* 98 */ "Count" OpHelp("r[P2]=count()"),
+ /* 99 */ "ReadCookie" OpHelp(""),
+ /* 100 */ "SetCookie" OpHelp(""),
+ /* 101 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
+ /* 102 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
+ /* 103 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
+ /* 104 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
+ /* 105 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
+ /* 106 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
+ /* 107 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
+ /* 108 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
+ /* 109 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
+ /* 110 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
+ /* 111 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
+ /* 112 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
+ /* 113 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
+ /* 114 */ "BitNot" OpHelp("r[P2]= ~r[P1]"),
+ /* 115 */ "OpenDup" OpHelp(""),
+ /* 116 */ "OpenAutoindex" OpHelp("nColumn=P2"),
+ /* 117 */ "String8" OpHelp("r[P2]='P4'"),
+ /* 118 */ "OpenEphemeral" OpHelp("nColumn=P2"),
+ /* 119 */ "SorterOpen" OpHelp(""),
+ /* 120 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
+ /* 121 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
+ /* 122 */ "Close" OpHelp(""),
+ /* 123 */ "ColumnsUsed" OpHelp(""),
+ /* 124 */ "SeekScan" OpHelp("Scan-ahead up to P1 rows"),
+ /* 125 */ "SeekHit" OpHelp("set P2<=seekHit<=P3"),
+ /* 126 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
+ /* 127 */ "NewRowid" OpHelp("r[P2]=rowid"),
+ /* 128 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
+ /* 129 */ "RowCell" OpHelp(""),
+ /* 130 */ "Delete" OpHelp(""),
+ /* 131 */ "ResetCount" OpHelp(""),
+ /* 132 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
+ /* 133 */ "SorterData" OpHelp("r[P2]=data"),
+ /* 134 */ "RowData" OpHelp("r[P2]=data"),
+ /* 135 */ "Rowid" OpHelp("r[P2]=PX rowid of P1"),
+ /* 136 */ "NullRow" OpHelp(""),
+ /* 137 */ "SeekEnd" OpHelp(""),
+ /* 138 */ "IdxInsert" OpHelp("key=r[P2]"),
+ /* 139 */ "SorterInsert" OpHelp("key=r[P2]"),
+ /* 140 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
+ /* 141 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"),
+ /* 142 */ "IdxRowid" OpHelp("r[P2]=rowid"),
+ /* 143 */ "FinishSeek" OpHelp(""),
+ /* 144 */ "Destroy" OpHelp(""),
+ /* 145 */ "Clear" OpHelp(""),
+ /* 146 */ "ResetSorter" OpHelp(""),
+ /* 147 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"),
+ /* 148 */ "SqlExec" OpHelp(""),
+ /* 149 */ "ParseSchema" OpHelp(""),
+ /* 150 */ "LoadAnalysis" OpHelp(""),
+ /* 151 */ "DropTable" OpHelp(""),
+ /* 152 */ "DropIndex" OpHelp(""),
+ /* 153 */ "Real" OpHelp("r[P2]=P4"),
+ /* 154 */ "DropTrigger" OpHelp(""),
+ /* 155 */ "IntegrityCk" OpHelp(""),
+ /* 156 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
+ /* 157 */ "Param" OpHelp(""),
+ /* 158 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
+ /* 159 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
+ /* 160 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
+ /* 161 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"),
+ /* 162 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
+ /* 163 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"),
+ /* 164 */ "AggValue" OpHelp("r[P3]=value N=P2"),
+ /* 165 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
+ /* 166 */ "Expire" OpHelp(""),
+ /* 167 */ "CursorLock" OpHelp(""),
+ /* 168 */ "CursorUnlock" OpHelp(""),
+ /* 169 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
+ /* 170 */ "VBegin" OpHelp(""),
+ /* 171 */ "VCreate" OpHelp(""),
+ /* 172 */ "VDestroy" OpHelp(""),
+ /* 173 */ "VOpen" OpHelp(""),
+ /* 174 */ "VCheck" OpHelp(""),
+ /* 175 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"),
+ /* 176 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
+ /* 177 */ "VRename" OpHelp(""),
+ /* 178 */ "Pagecount" OpHelp(""),
+ /* 179 */ "MaxPgcnt" OpHelp(""),
+ /* 180 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"),
+ /* 181 */ "GetSubtype" OpHelp("r[P2] = r[P1].subtype"),
+ /* 182 */ "SetSubtype" OpHelp("r[P2].subtype = r[P1]"),
+ /* 183 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"),
+ /* 184 */ "Trace" OpHelp(""),
+ /* 185 */ "CursorHint" OpHelp(""),
+ /* 186 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"),
+ /* 187 */ "Noop" OpHelp(""),
+ /* 188 */ "Explain" OpHelp(""),
+ /* 189 */ "Abortable" OpHelp(""),
};
return azName[i];
}
#endif
/************** End of opcodes.c *********************************************/
+/************** Begin file os_kv.c *******************************************/
+/*
+** 2022-09-06
+**
+** 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 contains an experimental VFS layer that operates on a
+** Key/Value storage engine where both keys and values must be pure
+** text.
+*/
+/* #include <sqliteInt.h> */
+#if SQLITE_OS_KV || (SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL))
+
+/*****************************************************************************
+** Debugging logic
+*/
+
+/* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */
+#if 0
+#define SQLITE_KV_TRACE(X) printf X
+#else
+#define SQLITE_KV_TRACE(X)
+#endif
+
+/* SQLITE_KV_LOG() is used for tracing calls to the VFS interface */
+#if 0
+#define SQLITE_KV_LOG(X) printf X
+#else
+#define SQLITE_KV_LOG(X)
+#endif
+
+
+/*
+** Forward declaration of objects used by this VFS implementation
+*/
+typedef struct KVVfsFile KVVfsFile;
+
+/* A single open file. There are only two files represented by this
+** VFS - the database and the rollback journal.
+*/
+struct KVVfsFile {
+ sqlite3_file base; /* IO methods */
+ const char *zClass; /* Storage class */
+ int isJournal; /* True if this is a journal file */
+ unsigned int nJrnl; /* Space allocated for aJrnl[] */
+ char *aJrnl; /* Journal content */
+ int szPage; /* Last known page size */
+ sqlite3_int64 szDb; /* Database file size. -1 means unknown */
+ char *aData; /* Buffer to hold page data */
+};
+#define SQLITE_KVOS_SZ 133073
+
+/*
+** Methods for KVVfsFile
+*/
+static int kvvfsClose(sqlite3_file*);
+static int kvvfsReadDb(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int kvvfsReadJrnl(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int kvvfsWriteDb(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
+static int kvvfsWriteJrnl(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
+static int kvvfsTruncateDb(sqlite3_file*, sqlite3_int64 size);
+static int kvvfsTruncateJrnl(sqlite3_file*, sqlite3_int64 size);
+static int kvvfsSyncDb(sqlite3_file*, int flags);
+static int kvvfsSyncJrnl(sqlite3_file*, int flags);
+static int kvvfsFileSizeDb(sqlite3_file*, sqlite3_int64 *pSize);
+static int kvvfsFileSizeJrnl(sqlite3_file*, sqlite3_int64 *pSize);
+static int kvvfsLock(sqlite3_file*, int);
+static int kvvfsUnlock(sqlite3_file*, int);
+static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut);
+static int kvvfsFileControlDb(sqlite3_file*, int op, void *pArg);
+static int kvvfsFileControlJrnl(sqlite3_file*, int op, void *pArg);
+static int kvvfsSectorSize(sqlite3_file*);
+static int kvvfsDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Methods for sqlite3_vfs
+*/
+static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
+static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename);
+static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int kvvfsSleep(sqlite3_vfs*, int microseconds);
+static int kvvfsCurrentTime(sqlite3_vfs*, double*);
+static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
+
+static sqlite3_vfs sqlite3OsKvvfsObject = {
+ 1, /* iVersion */
+ sizeof(KVVfsFile), /* szOsFile */
+ 1024, /* mxPathname */
+ 0, /* pNext */
+ "kvvfs", /* zName */
+ 0, /* pAppData */
+ kvvfsOpen, /* xOpen */
+ kvvfsDelete, /* xDelete */
+ kvvfsAccess, /* xAccess */
+ kvvfsFullPathname, /* xFullPathname */
+ kvvfsDlOpen, /* xDlOpen */
+ 0, /* xDlError */
+ 0, /* xDlSym */
+ 0, /* xDlClose */
+ kvvfsRandomness, /* xRandomness */
+ kvvfsSleep, /* xSleep */
+ kvvfsCurrentTime, /* xCurrentTime */
+ 0, /* xGetLastError */
+ kvvfsCurrentTimeInt64 /* xCurrentTimeInt64 */
+};
+
+/* Methods for sqlite3_file objects referencing a database file
+*/
+static sqlite3_io_methods kvvfs_db_io_methods = {
+ 1, /* iVersion */
+ kvvfsClose, /* xClose */
+ kvvfsReadDb, /* xRead */
+ kvvfsWriteDb, /* xWrite */
+ kvvfsTruncateDb, /* xTruncate */
+ kvvfsSyncDb, /* xSync */
+ kvvfsFileSizeDb, /* xFileSize */
+ kvvfsLock, /* xLock */
+ kvvfsUnlock, /* xUnlock */
+ kvvfsCheckReservedLock, /* xCheckReservedLock */
+ kvvfsFileControlDb, /* xFileControl */
+ kvvfsSectorSize, /* xSectorSize */
+ kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */
+ 0, /* xShmMap */
+ 0, /* xShmLock */
+ 0, /* xShmBarrier */
+ 0, /* xShmUnmap */
+ 0, /* xFetch */
+ 0 /* xUnfetch */
+};
+
+/* Methods for sqlite3_file objects referencing a rollback journal
+*/
+static sqlite3_io_methods kvvfs_jrnl_io_methods = {
+ 1, /* iVersion */
+ kvvfsClose, /* xClose */
+ kvvfsReadJrnl, /* xRead */
+ kvvfsWriteJrnl, /* xWrite */
+ kvvfsTruncateJrnl, /* xTruncate */
+ kvvfsSyncJrnl, /* xSync */
+ kvvfsFileSizeJrnl, /* xFileSize */
+ kvvfsLock, /* xLock */
+ kvvfsUnlock, /* xUnlock */
+ kvvfsCheckReservedLock, /* xCheckReservedLock */
+ kvvfsFileControlJrnl, /* xFileControl */
+ kvvfsSectorSize, /* xSectorSize */
+ kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */
+ 0, /* xShmMap */
+ 0, /* xShmLock */
+ 0, /* xShmBarrier */
+ 0, /* xShmUnmap */
+ 0, /* xFetch */
+ 0 /* xUnfetch */
+};
+
+/****** Storage subsystem **************************************************/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* Forward declarations for the low-level storage engine
+*/
+static int kvstorageWrite(const char*, const char *zKey, const char *zData);
+static int kvstorageDelete(const char*, const char *zKey);
+static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf);
+#define KVSTORAGE_KEY_SZ 32
+
+/* Expand the key name with an appropriate prefix and put the result
+** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least
+** KVSTORAGE_KEY_SZ bytes.
+*/
+static void kvstorageMakeKey(
+ const char *zClass,
+ const char *zKeyIn,
+ char *zKeyOut
+){
+ sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn);
+}
+
+/* Write content into a key. zClass is the particular namespace of the
+** underlying key/value store to use - either "local" or "session".
+**
+** Both zKey and zData are zero-terminated pure text strings.
+**
+** Return the number of errors.
+*/
+static int kvstorageWrite(
+ const char *zClass,
+ const char *zKey,
+ const char *zData
+){
+ FILE *fd;
+ char zXKey[KVSTORAGE_KEY_SZ];
+ kvstorageMakeKey(zClass, zKey, zXKey);
+ fd = fopen(zXKey, "wb");
+ if( fd ){
+ SQLITE_KV_TRACE(("KVVFS-WRITE %-15s (%d) %.50s%s\n", zXKey,
+ (int)strlen(zData), zData,
+ strlen(zData)>50 ? "..." : ""));
+ fputs(zData, fd);
+ fclose(fd);
+ return 0;
+ }else{
+ return 1;
+ }
+}
+
+/* Delete a key (with its corresponding data) from the key/value
+** namespace given by zClass. If the key does not previously exist,
+** this routine is a no-op.
+*/
+static int kvstorageDelete(const char *zClass, const char *zKey){
+ char zXKey[KVSTORAGE_KEY_SZ];
+ kvstorageMakeKey(zClass, zKey, zXKey);
+ unlink(zXKey);
+ SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey));
+ return 0;
+}
+
+/* Read the value associated with a zKey from the key/value namespace given
+** by zClass and put the text data associated with that key in the first
+** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large
+** enough to hold it all. The value put into zBuf must always be zero
+** terminated, even if it gets truncated because nBuf is not large enough.
+**
+** Return the total number of bytes in the data, without truncation, and
+** not counting the final zero terminator. Return -1 if the key does
+** not exist.
+**
+** If nBuf<=0 then this routine simply returns the size of the data without
+** actually reading it.
+*/
+static int kvstorageRead(
+ const char *zClass,
+ const char *zKey,
+ char *zBuf,
+ int nBuf
+){
+ FILE *fd;
+ struct stat buf;
+ char zXKey[KVSTORAGE_KEY_SZ];
+ kvstorageMakeKey(zClass, zKey, zXKey);
+ if( access(zXKey, R_OK)!=0
+ || stat(zXKey, &buf)!=0
+ || !S_ISREG(buf.st_mode)
+ ){
+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey));
+ return -1;
+ }
+ if( nBuf<=0 ){
+ return (int)buf.st_size;
+ }else if( nBuf==1 ){
+ zBuf[0] = 0;
+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (%d)\n", zXKey,
+ (int)buf.st_size));
+ return (int)buf.st_size;
+ }
+ if( nBuf > buf.st_size + 1 ){
+ nBuf = buf.st_size + 1;
+ }
+ fd = fopen(zXKey, "rb");
+ if( fd==0 ){
+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey));
+ return -1;
+ }else{
+ sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd);
+ fclose(fd);
+ zBuf[n] = 0;
+ SQLITE_KV_TRACE(("KVVFS-READ %-15s (%lld) %.50s%s\n", zXKey,
+ n, zBuf, n>50 ? "..." : ""));
+ return (int)n;
+ }
+}
+
+/*
+** An internal level of indirection which enables us to replace the
+** kvvfs i/o methods with JavaScript implementations in WASM builds.
+** Maintenance reminder: if this struct changes in any way, the JSON
+** rendering of its structure must be updated in
+** sqlite3_wasm_enum_json(). There are no binary compatibility
+** concerns, so it does not need an iVersion member. This file is
+** necessarily always compiled together with sqlite3_wasm_enum_json(),
+** and JS code dynamically creates the mapping of members based on
+** that JSON description.
+*/
+typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods;
+struct sqlite3_kvvfs_methods {
+ int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf);
+ int (*xWrite)(const char *zClass, const char *zKey, const char *zData);
+ int (*xDelete)(const char *zClass, const char *zKey);
+ const int nKeySize;
+};
+
+/*
+** This object holds the kvvfs I/O methods which may be swapped out
+** for JavaScript-side implementations in WASM builds. In such builds
+** it cannot be const, but in native builds it should be so that
+** the compiler can hopefully optimize this level of indirection out.
+** That said, kvvfs is intended primarily for use in WASM builds.
+**
+** Note that this is not explicitly flagged as static because the
+** amalgamation build will tag it with SQLITE_PRIVATE.
+*/
+#ifndef SQLITE_WASM
+const
+#endif
+SQLITE_PRIVATE sqlite3_kvvfs_methods sqlite3KvvfsMethods = {
+kvstorageRead,
+kvstorageWrite,
+kvstorageDelete,
+KVSTORAGE_KEY_SZ
+};
+
+/****** Utility subroutines ************************************************/
+
+/*
+** Encode binary into the text encoded used to persist on disk.
+** The output text is stored in aOut[], which must be at least
+** nData+1 bytes in length.
+**
+** Return the actual length of the encoded text, not counting the
+** zero terminator at the end.
+**
+** Encoding format
+** ---------------
+**
+** * Non-zero bytes are encoded as upper-case hexadecimal
+**
+** * A sequence of one or more zero-bytes that are not at the
+** beginning of the buffer are encoded as a little-endian
+** base-26 number using a..z. "a" means 0. "b" means 1,
+** "z" means 25. "ab" means 26. "ac" means 52. And so forth.
+**
+** * Because there is no overlap between the encoding characters
+** of hexadecimal and base-26 numbers, it is always clear where
+** one stops and the next begins.
+*/
+static int kvvfsEncode(const char *aData, int nData, char *aOut){
+ int i, j;
+ const unsigned char *a = (const unsigned char*)aData;
+ for(i=j=0; i<nData; i++){
+ unsigned char c = a[i];
+ if( c!=0 ){
+ aOut[j++] = "0123456789ABCDEF"[c>>4];
+ aOut[j++] = "0123456789ABCDEF"[c&0xf];
+ }else{
+ /* A sequence of 1 or more zeros is stored as a little-endian
+ ** base-26 number using a..z as the digits. So one zero is "b".
+ ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb",
+ ** and so forth.
+ */
+ int k;
+ for(k=1; i+k<nData && a[i+k]==0; k++){}
+ i += k-1;
+ while( k>0 ){
+ aOut[j++] = 'a'+(k%26);
+ k /= 26;
+ }
+ }
+ }
+ aOut[j] = 0;
+ return j;
+}
+
+static const signed char kvvfsHexValue[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+/*
+** Decode the text encoding back to binary. The binary content is
+** written into pOut, which must be at least nOut bytes in length.
+**
+** The return value is the number of bytes actually written into aOut[].
+*/
+static int kvvfsDecode(const char *a, char *aOut, int nOut){
+ int i, j;
+ int c;
+ const unsigned char *aIn = (const unsigned char*)a;
+ i = 0;
+ j = 0;
+ while( 1 ){
+ c = kvvfsHexValue[aIn[i]];
+ if( c<0 ){
+ int n = 0;
+ int mult = 1;
+ c = aIn[i];
+ if( c==0 ) break;
+ while( c>='a' && c<='z' ){
+ n += (c - 'a')*mult;
+ mult *= 26;
+ c = aIn[++i];
+ }
+ if( j+n>nOut ) return -1;
+ memset(&aOut[j], 0, n);
+ j += n;
+ if( c==0 || mult==1 ) break; /* progress stalled if mult==1 */
+ }else{
+ aOut[j] = c<<4;
+ c = kvvfsHexValue[aIn[++i]];
+ if( c<0 ) break;
+ aOut[j++] += c;
+ i++;
+ }
+ }
+ return j;
+}
+
+/*
+** Decode a complete journal file. Allocate space in pFile->aJrnl
+** and store the decoding there. Or leave pFile->aJrnl set to NULL
+** if an error is encountered.
+**
+** The first few characters of the text encoding will be a little-endian
+** base-26 number (digits a..z) that is the total number of bytes
+** in the decoded journal file image. This base-26 number is followed
+** by a single space, then the encoding of the journal. The space
+** separator is required to act as a terminator for the base-26 number.
+*/
+static void kvvfsDecodeJournal(
+ KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */
+ const char *zTxt, /* Text encoding. Zero-terminated */
+ int nTxt /* Bytes in zTxt, excluding zero terminator */
+){
+ unsigned int n = 0;
+ int c, i, mult;
+ i = 0;
+ mult = 1;
+ while( (c = zTxt[i++])>='a' && c<='z' ){
+ n += (zTxt[i] - 'a')*mult;
+ mult *= 26;
+ }
+ sqlite3_free(pFile->aJrnl);
+ pFile->aJrnl = sqlite3_malloc64( n );
+ if( pFile->aJrnl==0 ){
+ pFile->nJrnl = 0;
+ return;
+ }
+ pFile->nJrnl = n;
+ n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl);
+ if( n<pFile->nJrnl ){
+ sqlite3_free(pFile->aJrnl);
+ pFile->aJrnl = 0;
+ pFile->nJrnl = 0;
+ }
+}
+
+/*
+** Read or write the "sz" element, containing the database file size.
+*/
+static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){
+ char zData[50];
+ zData[0] = 0;
+ sqlite3KvvfsMethods.xRead(pFile->zClass, "sz", zData, sizeof(zData)-1);
+ return strtoll(zData, 0, 0);
+}
+static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){
+ char zData[50];
+ sqlite3_snprintf(sizeof(zData), zData, "%lld", sz);
+ return sqlite3KvvfsMethods.xWrite(pFile->zClass, "sz", zData);
+}
+
+/****** sqlite3_io_methods methods ******************************************/
+
+/*
+** Close an kvvfs-file.
+*/
+static int kvvfsClose(sqlite3_file *pProtoFile){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+
+ SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass,
+ pFile->isJournal ? "journal" : "db"));
+ sqlite3_free(pFile->aJrnl);
+ sqlite3_free(pFile->aData);
+ return SQLITE_OK;
+}
+
+/*
+** Read from the -journal file.
+*/
+static int kvvfsReadJrnl(
+ sqlite3_file *pProtoFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
+ assert( pFile->isJournal );
+ SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
+ if( pFile->aJrnl==0 ){
+ int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0);
+ char *aTxt;
+ if( szTxt<=4 ){
+ return SQLITE_IOERR;
+ }
+ aTxt = sqlite3_malloc64( szTxt+1 );
+ if( aTxt==0 ) return SQLITE_NOMEM;
+ kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1);
+ kvvfsDecodeJournal(pFile, aTxt, szTxt);
+ sqlite3_free(aTxt);
+ if( pFile->aJrnl==0 ) return SQLITE_IOERR;
+ }
+ if( iOfst+iAmt>pFile->nJrnl ){
+ return SQLITE_IOERR_SHORT_READ;
+ }
+ memcpy(zBuf, pFile->aJrnl+iOfst, iAmt);
+ return SQLITE_OK;
+}
+
+/*
+** Read from the database file.
+*/
+static int kvvfsReadDb(
+ sqlite3_file *pProtoFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
+ unsigned int pgno;
+ int got, n;
+ char zKey[30];
+ char *aData = pFile->aData;
+ assert( iOfst>=0 );
+ assert( iAmt>=0 );
+ SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
+ if( iOfst+iAmt>=512 ){
+ if( (iOfst % iAmt)!=0 ){
+ return SQLITE_IOERR_READ;
+ }
+ if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){
+ return SQLITE_IOERR_READ;
+ }
+ pFile->szPage = iAmt;
+ pgno = 1 + iOfst/iAmt;
+ }else{
+ pgno = 1;
+ }
+ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
+ got = sqlite3KvvfsMethods.xRead(pFile->zClass, zKey,
+ aData, SQLITE_KVOS_SZ-1);
+ if( got<0 ){
+ n = 0;
+ }else{
+ aData[got] = 0;
+ if( iOfst+iAmt<512 ){
+ int k = iOfst+iAmt;
+ aData[k*2] = 0;
+ n = kvvfsDecode(aData, &aData[2000], SQLITE_KVOS_SZ-2000);
+ if( n>=iOfst+iAmt ){
+ memcpy(zBuf, &aData[2000+iOfst], iAmt);
+ n = iAmt;
+ }else{
+ n = 0;
+ }
+ }else{
+ n = kvvfsDecode(aData, zBuf, iAmt);
+ }
+ }
+ if( n<iAmt ){
+ memset(zBuf+n, 0, iAmt-n);
+ return SQLITE_IOERR_SHORT_READ;
+ }
+ return SQLITE_OK;
+}
+
+
+/*
+** Write into the -journal file.
+*/
+static int kvvfsWriteJrnl(
+ sqlite3_file *pProtoFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
+ sqlite3_int64 iEnd = iOfst+iAmt;
+ SQLITE_KV_LOG(("xWrite('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
+ if( iEnd>=0x10000000 ) return SQLITE_FULL;
+ if( pFile->aJrnl==0 || pFile->nJrnl<iEnd ){
+ char *aNew = sqlite3_realloc(pFile->aJrnl, iEnd);
+ if( aNew==0 ){
+ return SQLITE_IOERR_NOMEM;
+ }
+ pFile->aJrnl = aNew;
+ if( pFile->nJrnl<iOfst ){
+ memset(pFile->aJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl);
+ }
+ pFile->nJrnl = iEnd;
+ }
+ memcpy(pFile->aJrnl+iOfst, zBuf, iAmt);
+ return SQLITE_OK;
+}
+
+/*
+** Write into the database file.
+*/
+static int kvvfsWriteDb(
+ sqlite3_file *pProtoFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
+ unsigned int pgno;
+ char zKey[30];
+ char *aData = pFile->aData;
+ SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
+ assert( iAmt>=512 && iAmt<=65536 );
+ assert( (iAmt & (iAmt-1))==0 );
+ assert( pFile->szPage<0 || pFile->szPage==iAmt );
+ pFile->szPage = iAmt;
+ pgno = 1 + iOfst/iAmt;
+ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
+ kvvfsEncode(zBuf, iAmt, aData);
+ if( sqlite3KvvfsMethods.xWrite(pFile->zClass, zKey, aData) ){
+ return SQLITE_IOERR;
+ }
+ if( iOfst+iAmt > pFile->szDb ){
+ pFile->szDb = iOfst + iAmt;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Truncate an kvvfs-file.
+*/
+static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size));
+ assert( size==0 );
+ sqlite3KvvfsMethods.xDelete(pFile->zClass, "jrnl");
+ sqlite3_free(pFile->aJrnl);
+ pFile->aJrnl = 0;
+ pFile->nJrnl = 0;
+ return SQLITE_OK;
+}
+static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ if( pFile->szDb>size
+ && pFile->szPage>0
+ && (size % pFile->szPage)==0
+ ){
+ char zKey[50];
+ unsigned int pgno, pgnoMax;
+ SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size));
+ pgno = 1 + size/pFile->szPage;
+ pgnoMax = 2 + pFile->szDb/pFile->szPage;
+ while( pgno<=pgnoMax ){
+ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
+ sqlite3KvvfsMethods.xDelete(pFile->zClass, zKey);
+ pgno++;
+ }
+ pFile->szDb = size;
+ return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK;
+ }
+ return SQLITE_IOERR;
+}
+
+/*
+** Sync an kvvfs-file.
+*/
+static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){
+ int i, n;
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ char *zOut;
+ SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass));
+ if( pFile->nJrnl<=0 ){
+ return kvvfsTruncateJrnl(pProtoFile, 0);
+ }
+ zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 );
+ if( zOut==0 ){
+ return SQLITE_IOERR_NOMEM;
+ }
+ n = pFile->nJrnl;
+ i = 0;
+ do{
+ zOut[i++] = 'a' + (n%26);
+ n /= 26;
+ }while( n>0 );
+ zOut[i++] = ' ';
+ kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]);
+ i = sqlite3KvvfsMethods.xWrite(pFile->zClass, "jrnl", zOut);
+ sqlite3_free(zOut);
+ return i ? SQLITE_IOERR : SQLITE_OK;
+}
+static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){
+ return SQLITE_OK;
+}
+
+/*
+** Return the current file-size of an kvvfs-file.
+*/
+static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass));
+ *pSize = pFile->nJrnl;
+ return SQLITE_OK;
+}
+static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass));
+ if( pFile->szDb>=0 ){
+ *pSize = pFile->szDb;
+ }else{
+ *pSize = kvvfsReadFileSize(pFile);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Lock an kvvfs-file.
+*/
+static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ assert( !pFile->isJournal );
+ SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock));
+
+ if( eLock!=SQLITE_LOCK_NONE ){
+ pFile->szDb = kvvfsReadFileSize(pFile);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Unlock an kvvfs-file.
+*/
+static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ assert( !pFile->isJournal );
+ SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock));
+ if( eLock==SQLITE_LOCK_NONE ){
+ pFile->szDb = -1;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an kvvfs-file.
+*/
+static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){
+ SQLITE_KV_LOG(("xCheckReservedLock\n"));
+ *pResOut = 0;
+ return SQLITE_OK;
+}
+
+/*
+** File control method. For custom operations on an kvvfs-file.
+*/
+static int kvvfsFileControlJrnl(sqlite3_file *pProtoFile, int op, void *pArg){
+ SQLITE_KV_LOG(("xFileControl(%d) on journal\n", op));
+ return SQLITE_NOTFOUND;
+}
+static int kvvfsFileControlDb(sqlite3_file *pProtoFile, int op, void *pArg){
+ SQLITE_KV_LOG(("xFileControl(%d) on database\n", op));
+ if( op==SQLITE_FCNTL_SYNC ){
+ KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
+ int rc = SQLITE_OK;
+ SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass));
+ if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){
+ rc = SQLITE_IOERR;
+ }
+ return rc;
+ }
+ return SQLITE_NOTFOUND;
+}
+
+/*
+** Return the sector-size in bytes for an kvvfs-file.
+*/
+static int kvvfsSectorSize(sqlite3_file *pFile){
+ return 512;
+}
+
+/*
+** Return the device characteristic flags supported by an kvvfs-file.
+*/
+static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){
+ return 0;
+}
+
+/****** sqlite3_vfs methods *************************************************/
+
+/*
+** Open an kvvfs file handle.
+*/
+static int kvvfsOpen(
+ sqlite3_vfs *pProtoVfs,
+ const char *zName,
+ sqlite3_file *pProtoFile,
+ int flags,
+ int *pOutFlags
+){
+ KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
+ if( zName==0 ) zName = "";
+ SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName));
+ if( strcmp(zName, "local")==0
+ || strcmp(zName, "session")==0
+ ){
+ pFile->isJournal = 0;
+ pFile->base.pMethods = &kvvfs_db_io_methods;
+ }else
+ if( strcmp(zName, "local-journal")==0
+ || strcmp(zName, "session-journal")==0
+ ){
+ pFile->isJournal = 1;
+ pFile->base.pMethods = &kvvfs_jrnl_io_methods;
+ }else{
+ return SQLITE_CANTOPEN;
+ }
+ if( zName[0]=='s' ){
+ pFile->zClass = "session";
+ }else{
+ pFile->zClass = "local";
+ }
+ pFile->aData = sqlite3_malloc64(SQLITE_KVOS_SZ);
+ if( pFile->aData==0 ){
+ return SQLITE_NOMEM;
+ }
+ pFile->aJrnl = 0;
+ pFile->nJrnl = 0;
+ pFile->szPage = -1;
+ pFile->szDb = -1;
+ return SQLITE_OK;
+}
+
+/*
+** Delete the file located at zPath. If the dirSync argument is true,
+** ensure the file-system modifications are synced to disk before
+** returning.
+*/
+static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ if( strcmp(zPath, "local-journal")==0 ){
+ sqlite3KvvfsMethods.xDelete("local", "jrnl");
+ }else
+ if( strcmp(zPath, "session-journal")==0 ){
+ sqlite3KvvfsMethods.xDelete("session", "jrnl");
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int kvvfsAccess(
+ sqlite3_vfs *pProtoVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath));
+ if( strcmp(zPath, "local-journal")==0 ){
+ *pResOut = sqlite3KvvfsMethods.xRead("local", "jrnl", 0, 0)>0;
+ }else
+ if( strcmp(zPath, "session-journal")==0 ){
+ *pResOut = sqlite3KvvfsMethods.xRead("session", "jrnl", 0, 0)>0;
+ }else
+ if( strcmp(zPath, "local")==0 ){
+ *pResOut = sqlite3KvvfsMethods.xRead("local", "sz", 0, 0)>0;
+ }else
+ if( strcmp(zPath, "session")==0 ){
+ *pResOut = sqlite3KvvfsMethods.xRead("session", "sz", 0, 0)>0;
+ }else
+ {
+ *pResOut = 0;
+ }
+ SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut));
+ return SQLITE_OK;
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (INST_MAX_PATHNAME+1) bytes.
+*/
+static int kvvfsFullPathname(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
+ char *zOut
+){
+ size_t nPath;
+#ifdef SQLITE_OS_KV_ALWAYS_LOCAL
+ zPath = "local";
+#endif
+ nPath = strlen(zPath);
+ SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath));
+ if( nOut<nPath+1 ) nPath = nOut - 1;
+ memcpy(zOut, zPath, nPath);
+ zOut[nPath] = 0;
+ return SQLITE_OK;
+}
+
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *kvvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ return 0;
+}
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of
+** random data.
+*/
+static int kvvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ memset(zBufOut, 0, nByte);
+ return nByte;
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){
+ return SQLITE_OK;
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ sqlite3_int64 i = 0;
+ int rc;
+ rc = kvvfsCurrentTimeInt64(0, &i);
+ *pTimeOut = i/86400000.0;
+ return rc;
+}
+#include <sys/time.h>
+static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
+ static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
+ struct timeval sNow;
+ (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */
+ *pTimeOut = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
+ return SQLITE_OK;
+}
+#endif /* SQLITE_OS_KV || SQLITE_OS_UNIX */
+
+#if SQLITE_OS_KV
+/*
+** This routine is called initialize the KV-vfs as the default VFS.
+*/
+SQLITE_API int sqlite3_os_init(void){
+ return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 1);
+}
+SQLITE_API int sqlite3_os_end(void){
+ return SQLITE_OK;
+}
+#endif /* SQLITE_OS_KV */
+
+#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)
+SQLITE_PRIVATE int sqlite3KvvfsInit(void){
+ return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 0);
+}
+#endif
+
+/************** End of os_kv.c ***********************************************/
/************** Begin file os_unix.c *****************************************/
/*
** 2004 May 22
@@ -32158,7 +37989,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
** This source file is organized into divisions where the logic for various
** subfunctions is contained within the appropriate division. PLEASE
** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed
-** in the correct division and should be clearly labeled.
+** in the correct division and should be clearly labelled.
**
** The layout of divisions is as follows:
**
@@ -32197,7 +38028,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
** Styles 4, 5, and 7 are only available of SQLITE_ENABLE_LOCKING_STYLE
** is defined to 1. The SQLITE_ENABLE_LOCKING_STYLE also enables automatic
** selection of the appropriate locking style based on the filesystem
-** where the database is located.
+** where the database is located.
*/
#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
# if defined(__APPLE__)
@@ -32208,7 +38039,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
#endif
/* Use pread() and pwrite() if they are available */
-#if defined(__APPLE__)
+#if defined(__APPLE__) || defined(__linux__)
# define HAVE_PREAD 1
# define HAVE_PWRITE 1
#endif
@@ -32223,15 +38054,16 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/*
** standard include files.
*/
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <sys/types.h> /* amalgamator: keep */
+#include <sys/stat.h> /* amalgamator: keep */
#include <fcntl.h>
#include <sys/ioctl.h>
-#include <unistd.h>
+#include <unistd.h> /* amalgamator: keep */
/* #include <time.h> */
-#include <sys/time.h>
+#include <sys/time.h> /* amalgamator: keep */
#include <errno.h>
-#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \
+ && !defined(SQLITE_WASI)
# include <sys/mman.h>
#endif
@@ -32241,13 +38073,30 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
# include <sys/param.h>
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
-#if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
- (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
-# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \
- && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))
-# define HAVE_GETHOSTUUID 1
-# else
-# warning "gethostuuid() is disabled."
+/*
+** Try to determine if gethostuuid() is available based on standard
+** macros. This might sometimes compute the wrong value for some
+** obscure platforms. For those cases, simply compile with one of
+** the following:
+**
+** -DHAVE_GETHOSTUUID=0
+** -DHAVE_GETHOSTUUID=1
+**
+** None if this matters except when building on Apple products with
+** -DSQLITE_ENABLE_LOCKING_STYLE.
+*/
+#ifndef HAVE_GETHOSTUUID
+# define HAVE_GETHOSTUUID 0
+# if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
+ (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
+# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \
+ && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))\
+ && (!defined(TARGET_OS_MACCATALYST) || (TARGET_OS_MACCATALYST==0))
+# undef HAVE_GETHOSTUUID
+# define HAVE_GETHOSTUUID 1
+# else
+# warning "gethostuuid() is disabled."
+# endif
# endif
#endif
@@ -32302,12 +38151,49 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
*/
#define SQLITE_MAX_SYMLINKS 100
+/*
+** Remove and stub certain info for WASI (WebAssembly System
+** Interface) builds.
+*/
+#ifdef SQLITE_WASI
+# undef HAVE_FCHMOD
+# undef HAVE_FCHOWN
+# undef HAVE_MREMAP
+# define HAVE_MREMAP 0
+# ifndef SQLITE_DEFAULT_UNIX_VFS
+# define SQLITE_DEFAULT_UNIX_VFS "unix-dotfile"
+ /* ^^^ should SQLITE_DEFAULT_UNIX_VFS be "unix-none"? */
+# endif
+# ifndef F_RDLCK
+# define F_RDLCK 0
+# define F_WRLCK 1
+# define F_UNLCK 2
+# if __LONG_MAX == 0x7fffffffL
+# define F_GETLK 12
+# define F_SETLK 13
+# define F_SETLKW 14
+# else
+# define F_GETLK 5
+# define F_SETLK 6
+# define F_SETLKW 7
+# endif
+# endif
+#else /* !SQLITE_WASI */
+# ifndef HAVE_FCHMOD
+# define HAVE_FCHMOD
+# endif
+#endif /* SQLITE_WASI */
+
+#ifdef SQLITE_WASI
+# define osGetpid(X) (pid_t)1
+#else
/* Always cast the getpid() return type for compatibility with
** kernel modules in VxWorks. */
-#define osGetpid(X) (pid_t)getpid()
+# define osGetpid(X) (pid_t)getpid()
+#endif
/*
-** Only set the lastErrno if the error code is a real error and not
+** Only set the lastErrno if the error code is a real error and not
** a normal expected return code of SQLITE_BUSY or SQLITE_OK
*/
#define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY))
@@ -32375,7 +38261,7 @@ struct unixFile {
** whenever any part of the database changes. An assertion fault will
** occur if a file is updated without also updating the transaction
** counter. This test is made to avoid new problems similar to the
- ** one described by ticket #3584.
+ ** one described by ticket #3584.
*/
unsigned char transCntrChng; /* True if the transaction counter changed */
unsigned char dbUpdate; /* True if any part of database file changed */
@@ -32384,7 +38270,7 @@ struct unixFile {
#endif
#ifdef SQLITE_TEST
- /* In test mode, increase the size of this structure a bit so that
+ /* In test mode, increase the size of this structure a bit so that
** it is larger than the struct CrashFile defined in test6.c.
*/
char aPadding[32];
@@ -32416,205 +38302,7 @@ static pid_t randomnessPid = 0;
/*
** Include code that is common to all os_*.c files
*/
-/************** Include os_common.h in the middle of os_unix.c ***************/
-/************** Begin file os_common.h ***************************************/
-/*
-** 2004 May 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 contains macros and a little bit of code that is common to
-** all of the platform-specific files (os_*.c) and is #included into those
-** files.
-**
-** This file should be #included by the os_*.c files only. It is not a
-** general purpose header file.
-*/
-#ifndef _OS_COMMON_H_
-#define _OS_COMMON_H_
-
-/*
-** At least two bugs have slipped in because we changed the MEMORY_DEBUG
-** macro to SQLITE_DEBUG and some older makefiles have not yet made the
-** switch. The following code should catch this problem at compile-time.
-*/
-#ifdef MEMORY_DEBUG
-# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
-#endif
-
-/*
-** Macros for performance tracing. Normally turned off. Only works
-** on i486 hardware.
-*/
-#ifdef SQLITE_PERFORMANCE_TRACE
-
-/*
-** hwtime.h contains inline assembler code for implementing
-** high-performance timing routines.
-*/
-/************** Include hwtime.h in the middle of os_common.h ****************/
-/************** Begin file hwtime.h ******************************************/
-/*
-** 2008 May 27
-**
-** 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 contains inline asm code for retrieving "high-performance"
-** counters for x86 class CPUs.
-*/
-#ifndef SQLITE_HWTIME_H
-#define SQLITE_HWTIME_H
-
-/*
-** The following routine only works on pentium-class (or newer) processors.
-** It uses the RDTSC opcode to read the cycle count value out of the
-** processor and returns that value. This can be used for high-res
-** profiling.
-*/
-#if (defined(__GNUC__) || defined(_MSC_VER)) && \
- (defined(i386) || defined(__i386__) || defined(_M_IX86))
-
- #if defined(__GNUC__)
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned int lo, hi;
- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
- return (sqlite_uint64)hi << 32 | lo;
- }
-
- #elif defined(_MSC_VER)
-
- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
- __asm {
- rdtsc
- ret ; return value at EDX:EAX
- }
- }
-
- #endif
-
-#elif (defined(__GNUC__) && defined(__x86_64__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long val;
- __asm__ __volatile__ ("rdtsc" : "=A" (val));
- return val;
- }
-
-#elif (defined(__GNUC__) && defined(__ppc__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long long retval;
- unsigned long junk;
- __asm__ __volatile__ ("\n\
- 1: mftbu %1\n\
- mftb %L0\n\
- mftbu %0\n\
- cmpw %0,%1\n\
- bne 1b"
- : "=r" (retval), "=r" (junk));
- return retval;
- }
-
-#else
-
- #error Need implementation of sqlite3Hwtime() for your platform.
-
- /*
- ** To compile without implementing sqlite3Hwtime() for your platform,
- ** you can remove the above #error and use the following
- ** stub function. You will lose timing support for many
- ** of the debugging and testing utilities, but it should at
- ** least compile and run.
- */
-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
-
-#endif
-
-#endif /* !defined(SQLITE_HWTIME_H) */
-
-/************** End of hwtime.h **********************************************/
-/************** Continuing where we left off in os_common.h ******************/
-
-static sqlite_uint64 g_start;
-static sqlite_uint64 g_elapsed;
-#define TIMER_START g_start=sqlite3Hwtime()
-#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
-#define TIMER_ELAPSED g_elapsed
-#else
-#define TIMER_START
-#define TIMER_END
-#define TIMER_ELAPSED ((sqlite_uint64)0)
-#endif
-
-/*
-** If we compile with the SQLITE_TEST macro set, then the following block
-** of code will give us the ability to simulate a disk I/O error. This
-** is used for testing the I/O recovery logic.
-*/
-#if defined(SQLITE_TEST)
-SQLITE_API extern int sqlite3_io_error_hit;
-SQLITE_API extern int sqlite3_io_error_hardhit;
-SQLITE_API extern int sqlite3_io_error_pending;
-SQLITE_API extern int sqlite3_io_error_persist;
-SQLITE_API extern int sqlite3_io_error_benign;
-SQLITE_API extern int sqlite3_diskfull_pending;
-SQLITE_API extern int sqlite3_diskfull;
-#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
-#define SimulateIOError(CODE) \
- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
- || sqlite3_io_error_pending-- == 1 ) \
- { local_ioerr(); CODE; }
-static void local_ioerr(){
- IOTRACE(("IOERR\n"));
- sqlite3_io_error_hit++;
- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
-}
-#define SimulateDiskfullError(CODE) \
- if( sqlite3_diskfull_pending ){ \
- if( sqlite3_diskfull_pending == 1 ){ \
- local_ioerr(); \
- sqlite3_diskfull = 1; \
- sqlite3_io_error_hit = 1; \
- CODE; \
- }else{ \
- sqlite3_diskfull_pending--; \
- } \
- }
-#else
-#define SimulateIOErrorBenign(X)
-#define SimulateIOError(A)
-#define SimulateDiskfullError(A)
-#endif /* defined(SQLITE_TEST) */
-
-/*
-** When testing, keep a count of the number of open files.
-*/
-#if defined(SQLITE_TEST)
-SQLITE_API extern int sqlite3_open_file_count;
-#define OpenCounter(X) sqlite3_open_file_count+=(X)
-#else
-#define OpenCounter(X)
-#endif /* defined(SQLITE_TEST) */
-
-#endif /* !defined(_OS_COMMON_H_) */
-
-/************** End of os_common.h *******************************************/
-/************** Continuing where we left off in os_unix.c ********************/
+/* #include "os_common.h" */
/*
** Define various macros that are missing from some systems.
@@ -32727,7 +38415,7 @@ static struct unix_syscall {
#ifdef __DJGPP__
{ "fstat", 0, 0 },
#define osFstat(a,b,c) 0
-#else
+#else
{ "fstat", (sqlite3_syscall_ptr)fstat, 0 },
#define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent)
#endif
@@ -32774,7 +38462,11 @@ static struct unix_syscall {
#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
aSyscall[13].pCurrent)
+#if defined(HAVE_FCHMOD)
{ "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
+#else
+ { "fchmod", (sqlite3_syscall_ptr)0, 0 },
+#endif
#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
@@ -32810,14 +38502,16 @@ static struct unix_syscall {
#endif
#define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent)
-#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \
+ && !defined(SQLITE_WASI)
{ "mmap", (sqlite3_syscall_ptr)mmap, 0 },
#else
{ "mmap", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent)
-#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \
+ && !defined(SQLITE_WASI)
{ "munmap", (sqlite3_syscall_ptr)munmap, 0 },
#else
{ "munmap", (sqlite3_syscall_ptr)0, 0 },
@@ -32855,13 +38549,14 @@ static struct unix_syscall {
#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
# ifdef __ANDROID__
{ "ioctl", (sqlite3_syscall_ptr)(int(*)(int, int, ...))ioctl, 0 },
+#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent)
# else
{ "ioctl", (sqlite3_syscall_ptr)ioctl, 0 },
+#define osIoctl ((int(*)(int,unsigned long,...))aSyscall[28].pCurrent)
# endif
#else
{ "ioctl", (sqlite3_syscall_ptr)0, 0 },
#endif
-#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent)
}; /* End of the overrideable system calls */
@@ -32881,7 +38576,7 @@ static int robustFchown(int fd, uid_t uid, gid_t gid){
/*
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
-** "unix" VFSes. Return SQLITE_OK opon successfully updating the
+** "unix" VFSes. Return SQLITE_OK upon successfully updating the
** system call pointer, or SQLITE_NOTFOUND if there is no configurable
** system call named zName.
*/
@@ -32964,7 +38659,7 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
/*
** Do not accept any file descriptor less than this value, in order to avoid
-** opening database file using file descriptors that are commonly used for
+** opening database file using file descriptors that are commonly used for
** standard input, output, and error.
*/
#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR
@@ -33002,18 +38697,21 @@ static int robust_open(const char *z, int f, mode_t m){
break;
}
if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break;
+ if( (f & (O_EXCL|O_CREAT))==(O_EXCL|O_CREAT) ){
+ (void)osUnlink(z);
+ }
osClose(fd);
- sqlite3_log(SQLITE_WARNING,
+ sqlite3_log(SQLITE_WARNING,
"attempt to open \"%s\" as file descriptor %d", z, fd);
fd = -1;
- if( osOpen("/dev/null", f, m)<0 ) break;
+ if( osOpen("/dev/null", O_RDONLY, m)<0 ) break;
}
if( fd>=0 ){
if( m!=0 ){
struct stat statbuf;
- if( osFstat(fd, &statbuf)==0
+ if( osFstat(fd, &statbuf)==0
&& statbuf.st_size==0
- && (statbuf.st_mode&0777)!=m
+ && (statbuf.st_mode&0777)!=m
){
osFchmod(fd, m);
}
@@ -33028,11 +38726,11 @@ static int robust_open(const char *z, int f, mode_t m){
/*
** Helper functions to obtain and relinquish the global mutex. The
** global mutex is used to protect the unixInodeInfo and
-** vxworksFileId objects used by this file, all of which may be
+** vxworksFileId objects used by this file, all of which may be
** shared by multiple threads.
**
-** Function unixMutexHeld() is used to assert() that the global mutex
-** is held when required. This function is only used as part of assert()
+** Function unixMutexHeld() is used to assert() that the global mutex
+** is held when required. This function is only used as part of assert()
** statements. e.g.
**
** unixEnterMutex()
@@ -33154,7 +38852,7 @@ static int lockTrace(int fd, int op, struct flock *p){
static int robust_ftruncate(int h, sqlite3_int64 sz){
int rc;
#ifdef __ANDROID__
- /* On Android, ftruncate() always uses 32-bit offsets, even if
+ /* On Android, ftruncate() always uses 32-bit offsets, even if
** _FILE_OFFSET_BITS=64 is defined. This means it is unsafe to attempt to
** truncate a file to any size larger than 2GiB. Silently ignore any
** such attempts. */
@@ -33170,32 +38868,32 @@ static int robust_ftruncate(int h, sqlite3_int64 sz){
** This routine translates a standard POSIX errno code into something
** useful to the clients of the sqlite3 functions. Specifically, it is
** intended to translate a variety of "try again" errors into SQLITE_BUSY
-** and a variety of "please close the file descriptor NOW" errors into
+** and a variety of "please close the file descriptor NOW" errors into
** SQLITE_IOERR
-**
+**
** Errors during initialization of locks, or file system support for locks,
** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately.
*/
static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
- assert( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
- (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
+ assert( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
(sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
(sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) );
switch (posixError) {
- case EACCES:
+ case EACCES:
case EAGAIN:
case ETIMEDOUT:
case EBUSY:
case EINTR:
- case ENOLCK:
- /* random NFS retry error, unless during file system support
+ case ENOLCK:
+ /* random NFS retry error, unless during file system support
* introspection, in which it actually means what it says */
return SQLITE_BUSY;
-
- case EPERM:
+
+ case EPERM:
return SQLITE_PERM;
-
- default:
+
+ default:
return sqliteIOErr;
}
}
@@ -33210,7 +38908,7 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
**
** A pointer to an instance of the following structure can be used as a
** unique file ID in VxWorks. Each instance of this structure contains
-** a copy of the canonical filename. There is also a reference count.
+** a copy of the canonical filename. There is also a reference count.
** The structure is reclaimed when the number of pointers to it drops to
** zero.
**
@@ -33226,7 +38924,7 @@ struct vxworksFileId {
};
#if OS_VXWORKS
-/*
+/*
** All unique filenames are held on a linked list headed by this
** variable:
*/
@@ -33298,7 +38996,7 @@ static struct vxworksFileId *vxworksFindFileId(const char *zAbsoluteName){
*/
unixEnterMutex();
for(pCandidate=vxworksFileList; pCandidate; pCandidate=pCandidate->pNext){
- if( pCandidate->nName==n
+ if( pCandidate->nName==n
&& memcmp(pCandidate->zCanonicalName, pNew->zCanonicalName, n)==0
){
sqlite3_free(pNew);
@@ -33391,7 +39089,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){
** cnt>0 means there are cnt shared locks on the file.
**
** Any attempt to lock or unlock a file first checks the locking
-** structure. The fcntl() system call is only invoked to set a
+** structure. The fcntl() system call is only invoked to set a
** POSIX lock if the internal lock structure transitions between
** a locked and an unlocked state.
**
@@ -33400,7 +39098,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){
** If you close a file descriptor that points to a file that has locks,
** all locks on that file that are owned by the current process are
** released. To work around this problem, each unixInodeInfo object
-** maintains a count of the number of pending locks on tha inode.
+** maintains a count of the number of pending locks on the inode.
** When an attempt is made to close an unixFile, if there are
** other unixFile open on the same inode that are holding locks, the call
** to close() the file descriptor is deferred until all of the locks clear.
@@ -33414,7 +39112,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){
** not posix compliant. Under LinuxThreads, a lock created by thread
** A cannot be modified or overridden by a different thread B.
** Only thread A can modify the lock. Locking behavior is correct
-** if the appliation uses the newer Native Posix Thread Library (NPTL)
+** if the application uses the newer Native Posix Thread Library (NPTL)
** on linux - with NPTL a lock created by thread A can override locks
** in thread B. But there is no way to know at compile-time which
** threading library is being used. So there is no way to know at
@@ -33424,7 +39122,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){
**
** SQLite used to support LinuxThreads. But support for LinuxThreads
** was dropped beginning with version 3.7.0. SQLite will still work with
-** LinuxThreads provided that (1) there is no more than one connection
+** LinuxThreads provided that (1) there is no more than one connection
** per database file in the same process and (2) database connections
** do not move across threads.
*/
@@ -33441,7 +39139,7 @@ struct unixFileId {
/* We are told that some versions of Android contain a bug that
** sizes ino_t at only 32-bits instead of 64-bits. (See
** https://android-review.googlesource.com/#/c/115351/3/dist/sqlite3.c)
- ** To work around this, always allocate 64-bits for the inode number.
+ ** To work around this, always allocate 64-bits for the inode number.
** On small machines that only have 32-bit inodes, this wastes 4 bytes,
** but that should not be a big deal. */
/* WAS: ino_t ino; */
@@ -33529,7 +39227,7 @@ int unixFileMutexNotheld(unixFile *pFile){
** strerror_r().
**
** The first argument passed to the macro should be the error code that
-** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
+** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
** The two subsequent arguments should be the name of the OS function that
** failed (e.g. "unlink", "open") and the associated file-system path,
** if any.
@@ -33547,7 +39245,7 @@ static int unixLogErrorAtLine(
/* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use
** the strerror() function to obtain the human-readable error message
** equivalent to errno. Otherwise, use strerror_r().
- */
+ */
#if SQLITE_THREADSAFE && defined(HAVE_STRERROR_R)
char aErr[80];
memset(aErr, 0, sizeof(aErr));
@@ -33555,18 +39253,18 @@ static int unixLogErrorAtLine(
/* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined,
** assume that the system provides the GNU version of strerror_r() that
- ** returns a pointer to a buffer containing the error message. That pointer
- ** may point to aErr[], or it may point to some static storage somewhere.
- ** Otherwise, assume that the system provides the POSIX version of
+ ** returns a pointer to a buffer containing the error message. That pointer
+ ** may point to aErr[], or it may point to some static storage somewhere.
+ ** Otherwise, assume that the system provides the POSIX version of
** strerror_r(), which always writes an error message into aErr[].
**
** If the code incorrectly assumes that it is the POSIX version that is
** available, the error message will often be an empty string. Not a
- ** huge problem. Incorrectly concluding that the GNU version is available
+ ** huge problem. Incorrectly concluding that the GNU version is available
** could lead to a segfault though.
*/
#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)
- zErr =
+ zErr =
# endif
strerror_r(iErrno, aErr, sizeof(aErr)-1);
@@ -33616,8 +39314,8 @@ static void storeLastErrno(unixFile *pFile, int error){
}
/*
-** Close all file descriptors accumuated in the unixInodeInfo->pUnused list.
-*/
+** Close all file descriptors accumulated in the unixInodeInfo->pUnused list.
+*/
static void closePendingFds(unixFile *pFile){
unixInodeInfo *pInode = pFile->pInode;
UnixUnusedFd *p;
@@ -33772,7 +39470,7 @@ static int fileHasMoved(unixFile *pFile){
#else
struct stat buf;
return pFile->pInode!=0 &&
- (osStat(pFile->zPath, &buf)!=0
+ (osStat(pFile->zPath, &buf)!=0
|| (u64)buf.st_ino!=pFile->pInode->fileId.ino);
#endif
}
@@ -33853,7 +39551,7 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
}
}
#endif
-
+
sqlite3_mutex_leave(pFile->pInode->pLockMutex);
OSTRACE(("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved));
@@ -33861,6 +39559,9 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
return rc;
}
+/* Forward declaration*/
+static int unixSleep(sqlite3_vfs*,int);
+
/*
** Set a posix-advisory-lock.
**
@@ -33882,16 +39583,17 @@ static int osSetPosixAdvisoryLock(
struct flock *pLock, /* The description of the lock */
unixFile *pFile /* Structure holding timeout value */
){
+ int tm = pFile->iBusyTimeout;
int rc = osFcntl(h,F_SETLK,pLock);
- while( rc<0 && pFile->iBusyTimeout>0 ){
+ while( rc<0 && tm>0 ){
/* On systems that support some kind of blocking file lock with a timeout,
** make appropriate changes here to invoke that blocking file lock. On
** generic posix, however, there is no such API. So we simply try the
** lock once every millisecond until either the timeout expires, or until
** the lock is obtained. */
- usleep(1000);
+ unixSleep(0,1000);
rc = osFcntl(h,F_SETLK,pLock);
- pFile->iBusyTimeout--;
+ tm--;
}
return rc;
}
@@ -33899,7 +39601,7 @@ static int osSetPosixAdvisoryLock(
/*
-** Attempt to set a system-lock on the file pFile. The lock is
+** Attempt to set a system-lock on the file pFile. The lock is
** described by pLock.
**
** If the pFile was opened read/write from unix-excl, then the only lock
@@ -33960,7 +39662,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){
**
** UNLOCKED -> SHARED
** SHARED -> RESERVED
-** SHARED -> (PENDING) -> EXCLUSIVE
+** SHARED -> EXCLUSIVE
** RESERVED -> (PENDING) -> EXCLUSIVE
** PENDING -> EXCLUSIVE
**
@@ -33975,7 +39677,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
** slightly in order to be compatible with Windows95 systems simultaneously
** accessing the same database file, in case that is ever required.
**
- ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved
+ ** Symbols defined in os.h identify the 'pending byte' and the 'reserved
** byte', each single bytes at well known offsets, and the 'shared byte
** range', a range of 510 bytes at a well known offset.
**
@@ -33983,7 +39685,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
** byte'. If this is successful, 'shared byte range' is read-locked
** and the lock on the 'pending byte' released. (Legacy note: When
** SQLite was first developed, Windows95 systems were still very common,
- ** and Widnows95 lacks a shared-lock capability. So on Windows95, a
+ ** and Windows95 lacks a shared-lock capability. So on Windows95, a
** single randomly selected by from the 'shared byte range' is locked.
** Windows95 is now pretty much extinct, but this work-around for the
** lack of shared-locks on Windows95 lives on, for backwards
@@ -33991,21 +39693,22 @@ static int unixLock(sqlite3_file *id, int eFileLock){
**
** A process may only obtain a RESERVED lock after it has a SHARED lock.
** A RESERVED lock is implemented by grabbing a write-lock on the
- ** 'reserved byte'.
+ ** 'reserved byte'.
**
- ** A process may only obtain a PENDING lock after it has obtained a
- ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock
- ** on the 'pending byte'. This ensures that no new SHARED locks can be
- ** obtained, but existing SHARED locks are allowed to persist. A process
- ** does not have to obtain a RESERVED lock on the way to a PENDING lock.
- ** This property is used by the algorithm for rolling back a journal file
- ** after a crash.
+ ** An EXCLUSIVE lock may only be requested after either a SHARED or
+ ** RESERVED lock is held. An EXCLUSIVE lock is implemented by obtaining
+ ** a write-lock on the entire 'shared byte range'. Since all other locks
+ ** require a read-lock on one of the bytes within this range, this ensures
+ ** that no other locks are held on the database.
**
- ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is
- ** implemented by obtaining a write-lock on the entire 'shared byte
- ** range'. Since all other locks require a read-lock on one of the bytes
- ** within this range, this ensures that no other locks are held on the
- ** database.
+ ** If a process that holds a RESERVED lock requests an EXCLUSIVE, then
+ ** a PENDING lock is obtained first. A PENDING lock is implemented by
+ ** obtaining a write-lock on the 'pending byte'. This ensures that no new
+ ** SHARED locks can be obtained, but existing SHARED locks are allowed to
+ ** persist. If the call to this function fails to obtain the EXCLUSIVE
+ ** lock in this case, it holds the PENDING lock instead. The client may
+ ** then re-attempt the EXCLUSIVE lock later on, after existing SHARED
+ ** locks have cleared.
*/
int rc = SQLITE_OK;
unixFile *pFile = (unixFile*)id;
@@ -34031,7 +39734,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
/* Make sure the locking sequence is correct.
** (1) We never move from unlocked to anything higher than shared lock.
- ** (2) SQLite never explicitly requests a pendig lock.
+ ** (2) SQLite never explicitly requests a pending lock.
** (3) A shared lock is always held when a reserve lock is requested.
*/
assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK );
@@ -34046,7 +39749,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
/* If some thread using this PID has a lock via a different unixFile*
** handle that precludes the requested lock, return BUSY.
*/
- if( (pFile->eFileLock!=pInode->eFileLock &&
+ if( (pFile->eFileLock!=pInode->eFileLock &&
(pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
){
rc = SQLITE_BUSY;
@@ -34057,7 +39760,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
** has a SHARED or RESERVED lock, then increment reference counts and
** return SQLITE_OK.
*/
- if( eFileLock==SHARED_LOCK &&
+ if( eFileLock==SHARED_LOCK &&
(pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){
assert( eFileLock==SHARED_LOCK );
assert( pFile->eFileLock==0 );
@@ -34075,8 +39778,8 @@ static int unixLock(sqlite3_file *id, int eFileLock){
*/
lock.l_len = 1L;
lock.l_whence = SEEK_SET;
- if( eFileLock==SHARED_LOCK
- || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK)
+ if( eFileLock==SHARED_LOCK
+ || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock==RESERVED_LOCK)
){
lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK);
lock.l_start = PENDING_BYTE;
@@ -34087,6 +39790,9 @@ static int unixLock(sqlite3_file *id, int eFileLock){
storeLastErrno(pFile, tErrno);
}
goto end_lock;
+ }else if( eFileLock==EXCLUSIVE_LOCK ){
+ pFile->eFileLock = PENDING_LOCK;
+ pInode->eFileLock = PENDING_LOCK;
}
}
@@ -34114,7 +39820,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){
/* This could happen with a network mount */
tErrno = errno;
- rc = SQLITE_IOERR_UNLOCK;
+ rc = SQLITE_IOERR_UNLOCK;
}
if( rc ){
@@ -34156,7 +39862,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
}
}
}
-
+
#ifdef SQLITE_DEBUG
/* Set up the transaction-counter change checking flags when
@@ -34174,18 +39880,14 @@ static int unixLock(sqlite3_file *id, int eFileLock){
}
#endif
-
if( rc==SQLITE_OK ){
pFile->eFileLock = eFileLock;
pInode->eFileLock = eFileLock;
- }else if( eFileLock==EXCLUSIVE_LOCK ){
- pFile->eFileLock = PENDING_LOCK;
- pInode->eFileLock = PENDING_LOCK;
}
end_lock:
sqlite3_mutex_leave(pInode->pLockMutex);
- OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock),
+ OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock),
rc==SQLITE_OK ? "ok" : "failed"));
return rc;
}
@@ -34210,11 +39912,11 @@ static void setPendingFd(unixFile *pFile){
**
** If the locking level of the file descriptor is already at or below
** the requested locking level, this routine is a no-op.
-**
+**
** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED
** the byte range is divided into 2 parts and the first part is unlocked then
-** set to a read lock, then the other part is simply unlocked. This works
-** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to
+** set to a read lock, then the other part is simply unlocked. This works
+** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to
** remove the write lock on a region when a read lock is set.
*/
static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
@@ -34252,7 +39954,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
/* downgrading to a shared lock on NFS involves clearing the write lock
** before establishing the readlock - to avoid a race condition we downgrade
- ** the lock in 2 blocks, so that part of the range will be covered by a
+ ** the lock in 2 blocks, so that part of the range will be covered by a
** write lock until the rest is covered by a read lock:
** 1: [WWWWW]
** 2: [....W]
@@ -34268,7 +39970,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
if( handleNFSUnlock ){
int tErrno; /* Error code from system call errors */
off_t divSize = SHARED_SIZE - 1;
-
+
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = SHARED_FIRST;
@@ -34310,11 +40012,11 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
lock.l_len = SHARED_SIZE;
if( unixFileLock(pFile, &lock) ){
/* In theory, the call to unixFileLock() cannot fail because another
- ** process is holding an incompatible lock. If it does, this
+ ** process is holding an incompatible lock. If it does, this
** indicates that the other process is not following the locking
** protocol. If this happens, return SQLITE_IOERR_RDLOCK. Returning
- ** SQLITE_BUSY would confuse the upper layer (in practice it causes
- ** an assert to fail). */
+ ** SQLITE_BUSY would confuse the upper layer (in practice it causes
+ ** an assert to fail). */
rc = SQLITE_IOERR_RDLOCK;
storeLastErrno(pFile, errno);
goto end_unlock;
@@ -34390,7 +40092,7 @@ static void unixUnmapfile(unixFile *pFd);
#endif
/*
-** This function performs the parts of the "close file" operation
+** This function performs the parts of the "close file" operation
** common to all locking schemes. It closes the directory and file
** handles, if they are valid, and sets all fields of the unixFile
** structure to 0.
@@ -34453,13 +40155,14 @@ static int unixClose(sqlite3_file *id){
if( pInode->nLock ){
/* If there are outstanding locks, do not actually close the file just
** yet because that would clear those locks. Instead, add the file
- ** descriptor to pInode->pUnused list. It will be automatically closed
+ ** descriptor to pInode->pUnused list. It will be automatically closed
** when the last lock is cleared.
*/
setPendingFd(pFile);
}
sqlite3_mutex_leave(pInode->pLockMutex);
releaseInodeInfo(pFile);
+ assert( pFile->pShm==0 );
rc = closeUnixFile(id);
unixLeaveMutex();
return rc;
@@ -34553,7 +40256,7 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
unixFile *pFile = (unixFile*)id;
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
-
+
assert( pFile );
reserved = osAccess((const char*)pFile->lockingContext, 0)==0;
OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved));
@@ -34607,7 +40310,7 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
#endif
return SQLITE_OK;
}
-
+
/* grab an exclusive lock */
rc = osMkdir(zLockFile, 0777);
if( rc<0 ){
@@ -34622,8 +40325,8 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
}
}
return rc;
- }
-
+ }
+
/* got it, set the type and return ok */
pFile->eFileLock = eFileLock;
return rc;
@@ -34647,7 +40350,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock,
pFile->eFileLock, osGetpid(0)));
assert( eFileLock<=SHARED_LOCK );
-
+
/* no-op if possible */
if( pFile->eFileLock==eFileLock ){
return SQLITE_OK;
@@ -34660,7 +40363,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
pFile->eFileLock = SHARED_LOCK;
return SQLITE_OK;
}
-
+
/* To fully unlock the database, delete the lock file */
assert( eFileLock==NO_LOCK );
rc = osRmdir(zLockFile);
@@ -34672,7 +40375,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
rc = SQLITE_IOERR_UNLOCK;
storeLastErrno(pFile, tErrno);
}
- return rc;
+ return rc;
}
pFile->eFileLock = NO_LOCK;
return SQLITE_OK;
@@ -34719,7 +40422,7 @@ static int robust_flock(int fd, int op){
#else
# define robust_flock(a,b) flock(a,b)
#endif
-
+
/*
** This routine checks if there is a RESERVED lock held on the specified
@@ -34731,16 +40434,16 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
int rc = SQLITE_OK;
int reserved = 0;
unixFile *pFile = (unixFile*)id;
-
+
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
-
+
assert( pFile );
-
+
/* Check if a thread in this process holds such a lock */
if( pFile->eFileLock>SHARED_LOCK ){
reserved = 1;
}
-
+
/* Otherwise see if some other process holds it. */
if( !reserved ){
/* attempt to get the lock */
@@ -34751,7 +40454,7 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
if ( lrc ) {
int tErrno = errno;
/* unlock failed with an error */
- lrc = SQLITE_IOERR_UNLOCK;
+ lrc = SQLITE_IOERR_UNLOCK;
storeLastErrno(pFile, tErrno);
rc = lrc;
}
@@ -34759,7 +40462,7 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
int tErrno = errno;
reserved = 1;
/* someone else might have it reserved */
- lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
+ lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( IS_LOCK_ERROR(lrc) ){
storeLastErrno(pFile, tErrno);
rc = lrc;
@@ -34813,15 +40516,15 @@ static int flockLock(sqlite3_file *id, int eFileLock) {
assert( pFile );
- /* if we already have a lock, it is exclusive.
+ /* if we already have a lock, it is exclusive.
** Just adjust level and punt on outta here. */
if (pFile->eFileLock > NO_LOCK) {
pFile->eFileLock = eFileLock;
return SQLITE_OK;
}
-
+
/* grab an exclusive lock */
-
+
if (robust_flock(pFile->h, LOCK_EX | LOCK_NB)) {
int tErrno = errno;
/* didn't get, must be busy */
@@ -34833,7 +40536,7 @@ static int flockLock(sqlite3_file *id, int eFileLock) {
/* got it, set the type and return ok */
pFile->eFileLock = eFileLock;
}
- OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock),
+ OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock),
rc==SQLITE_OK ? "ok" : "failed"));
#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
if( (rc & 0xff) == SQLITE_IOERR ){
@@ -34853,23 +40556,23 @@ static int flockLock(sqlite3_file *id, int eFileLock) {
*/
static int flockUnlock(sqlite3_file *id, int eFileLock) {
unixFile *pFile = (unixFile*)id;
-
+
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, eFileLock,
pFile->eFileLock, osGetpid(0)));
assert( eFileLock<=SHARED_LOCK );
-
+
/* no-op if possible */
if( pFile->eFileLock==eFileLock ){
return SQLITE_OK;
}
-
+
/* shared can just be set because we always have an exclusive */
if (eFileLock==SHARED_LOCK) {
pFile->eFileLock = eFileLock;
return SQLITE_OK;
}
-
+
/* no, really, unlock. */
if( robust_flock(pFile->h, LOCK_UN) ){
#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
@@ -34920,14 +40623,14 @@ static int semXCheckReservedLock(sqlite3_file *id, int *pResOut) {
unixFile *pFile = (unixFile*)id;
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
-
+
assert( pFile );
/* Check if a thread in this process holds such a lock */
if( pFile->eFileLock>SHARED_LOCK ){
reserved = 1;
}
-
+
/* Otherwise see if some other process holds it. */
if( !reserved ){
sem_t *pSem = pFile->pInode->pSem;
@@ -34986,14 +40689,14 @@ static int semXLock(sqlite3_file *id, int eFileLock) {
sem_t *pSem = pFile->pInode->pSem;
int rc = SQLITE_OK;
- /* if we already have a lock, it is exclusive.
+ /* if we already have a lock, it is exclusive.
** Just adjust level and punt on outta here. */
if (pFile->eFileLock > NO_LOCK) {
pFile->eFileLock = eFileLock;
rc = SQLITE_OK;
goto sem_end_lock;
}
-
+
/* lock semaphore now but bail out when already locked. */
if( sem_trywait(pSem)==-1 ){
rc = SQLITE_BUSY;
@@ -35023,18 +40726,18 @@ static int semXUnlock(sqlite3_file *id, int eFileLock) {
OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock,
pFile->eFileLock, osGetpid(0)));
assert( eFileLock<=SHARED_LOCK );
-
+
/* no-op if possible */
if( pFile->eFileLock==eFileLock ){
return SQLITE_OK;
}
-
+
/* shared can just be set because we always have an exclusive */
if (eFileLock==SHARED_LOCK) {
pFile->eFileLock = eFileLock;
return SQLITE_OK;
}
-
+
/* no, really unlock. */
if ( sem_post(pSem)==-1 ) {
int rc, tErrno = errno;
@@ -35042,7 +40745,7 @@ static int semXUnlock(sqlite3_file *id, int eFileLock) {
if( IS_LOCK_ERROR(rc) ){
storeLastErrno(pFile, tErrno);
}
- return rc;
+ return rc;
}
pFile->eFileLock = NO_LOCK;
return SQLITE_OK;
@@ -35108,7 +40811,7 @@ struct ByteRangeLockPB2
/*
** This is a utility for setting or clearing a bit-range lock on an
** AFP filesystem.
-**
+**
** Return SQLITE_OK on success, SQLITE_BUSY on failure.
*/
static int afpSetLock(
@@ -35120,14 +40823,14 @@ static int afpSetLock(
){
struct ByteRangeLockPB2 pb;
int err;
-
+
pb.unLockFlag = setLockFlag ? 0 : 1;
pb.startEndFlag = 0;
pb.offset = offset;
- pb.length = length;
+ pb.length = length;
pb.fd = pFile->h;
-
- OSTRACE(("AFPSETLOCK [%s] for %d%s in range %llx:%llx\n",
+
+ OSTRACE(("AFPSETLOCK [%s] for %d%s in range %llx:%llx\n",
(setLockFlag?"ON":"OFF"), pFile->h, (pb.fd==-1?"[testval-1]":""),
offset, length));
err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0);
@@ -35162,9 +40865,9 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
int reserved = 0;
unixFile *pFile = (unixFile*)id;
afpLockingContext *context;
-
+
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
-
+
assert( pFile );
context = (afpLockingContext *) pFile->lockingContext;
if( context->reserved ){
@@ -35176,12 +40879,12 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
if( pFile->pInode->eFileLock>SHARED_LOCK ){
reserved = 1;
}
-
+
/* Otherwise see if some other process holds it.
*/
if( !reserved ){
/* lock the RESERVED byte */
- int lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1);
+ int lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1);
if( SQLITE_OK==lrc ){
/* if we succeeded in taking the reserved lock, unlock it to restore
** the original state */
@@ -35194,10 +40897,10 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
rc=lrc;
}
}
-
+
sqlite3_mutex_leave(pFile->pInode->pLockMutex);
OSTRACE(("TEST WR-LOCK %d %d %d (afp)\n", pFile->h, rc, reserved));
-
+
*pResOut = reserved;
return rc;
}
@@ -35231,7 +40934,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){
unixFile *pFile = (unixFile*)id;
unixInodeInfo *pInode = pFile->pInode;
afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
-
+
assert( pFile );
OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (afp)\n", pFile->h,
azFileLock(eFileLock), azFileLock(pFile->eFileLock),
@@ -35249,13 +40952,13 @@ static int afpLock(sqlite3_file *id, int eFileLock){
/* Make sure the locking sequence is correct
** (1) We never move from unlocked to anything higher than shared lock.
- ** (2) SQLite never explicitly requests a pendig lock.
+ ** (2) SQLite never explicitly requests a pending lock.
** (3) A shared lock is always held when a reserve lock is requested.
*/
assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK );
assert( eFileLock!=PENDING_LOCK );
assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK );
-
+
/* This mutex is needed because pFile->pInode is shared across threads
*/
pInode = pFile->pInode;
@@ -35264,18 +40967,18 @@ static int afpLock(sqlite3_file *id, int eFileLock){
/* If some thread using this PID has a lock via a different unixFile*
** handle that precludes the requested lock, return BUSY.
*/
- if( (pFile->eFileLock!=pInode->eFileLock &&
+ if( (pFile->eFileLock!=pInode->eFileLock &&
(pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK))
){
rc = SQLITE_BUSY;
goto afp_end_lock;
}
-
+
/* If a SHARED lock is requested, and some thread using this PID already
** has a SHARED or RESERVED lock, then increment reference counts and
** return SQLITE_OK.
*/
- if( eFileLock==SHARED_LOCK &&
+ if( eFileLock==SHARED_LOCK &&
(pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){
assert( eFileLock==SHARED_LOCK );
assert( pFile->eFileLock==0 );
@@ -35285,12 +40988,12 @@ static int afpLock(sqlite3_file *id, int eFileLock){
pInode->nLock++;
goto afp_end_lock;
}
-
+
/* A PENDING lock is needed before acquiring a SHARED lock and before
** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will
** be released.
*/
- if( eFileLock==SHARED_LOCK
+ if( eFileLock==SHARED_LOCK
|| (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK)
){
int failed;
@@ -35300,30 +41003,30 @@ static int afpLock(sqlite3_file *id, int eFileLock){
goto afp_end_lock;
}
}
-
+
/* If control gets to this point, then actually go ahead and make
** operating system calls for the specified lock.
*/
if( eFileLock==SHARED_LOCK ){
int lrc1, lrc2, lrc1Errno = 0;
long lk, mask;
-
+
assert( pInode->nShared==0 );
assert( pInode->eFileLock==0 );
-
+
mask = (sizeof(long)==8) ? LARGEST_INT64 : 0x7fffffff;
/* Now get the read-lock SHARED_LOCK */
/* note that the quality of the randomness doesn't matter that much */
- lk = random();
+ lk = random();
pInode->sharedByte = (lk & mask)%(SHARED_SIZE - 1);
- lrc1 = afpSetLock(context->dbPath, pFile,
+ lrc1 = afpSetLock(context->dbPath, pFile,
SHARED_FIRST+pInode->sharedByte, 1, 1);
if( IS_LOCK_ERROR(lrc1) ){
lrc1Errno = pFile->lastErrno;
}
/* Drop the temporary PENDING lock */
lrc2 = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0);
-
+
if( IS_LOCK_ERROR(lrc1) ) {
storeLastErrno(pFile, lrc1Errno);
rc = lrc1;
@@ -35358,34 +41061,34 @@ static int afpLock(sqlite3_file *id, int eFileLock){
}
if (!failed && eFileLock == EXCLUSIVE_LOCK) {
/* Acquire an EXCLUSIVE lock */
-
- /* Remove the shared lock before trying the range. we'll need to
+
+ /* Remove the shared lock before trying the range. we'll need to
** reestablish the shared lock if we can't get the afpUnlock
*/
if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST +
pInode->sharedByte, 1, 0)) ){
int failed2 = SQLITE_OK;
- /* now attemmpt to get the exclusive lock range */
- failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST,
+ /* now attempt to get the exclusive lock range */
+ failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST,
SHARED_SIZE, 1);
- if( failed && (failed2 = afpSetLock(context->dbPath, pFile,
+ if( failed && (failed2 = afpSetLock(context->dbPath, pFile,
SHARED_FIRST + pInode->sharedByte, 1, 1)) ){
/* Can't reestablish the shared lock. Sqlite can't deal, this is
** a critical I/O error
*/
- rc = ((failed & 0xff) == SQLITE_IOERR) ? failed2 :
+ rc = ((failed & 0xff) == SQLITE_IOERR) ? failed2 :
SQLITE_IOERR_LOCK;
goto afp_end_lock;
- }
+ }
}else{
- rc = failed;
+ rc = failed;
}
}
if( failed ){
rc = failed;
}
}
-
+
if( rc==SQLITE_OK ){
pFile->eFileLock = eFileLock;
pInode->eFileLock = eFileLock;
@@ -35393,10 +41096,10 @@ static int afpLock(sqlite3_file *id, int eFileLock){
pFile->eFileLock = PENDING_LOCK;
pInode->eFileLock = PENDING_LOCK;
}
-
+
afp_end_lock:
sqlite3_mutex_leave(pInode->pLockMutex);
- OSTRACE(("LOCK %d %s %s (afp)\n", pFile->h, azFileLock(eFileLock),
+ OSTRACE(("LOCK %d %s %s (afp)\n", pFile->h, azFileLock(eFileLock),
rc==SQLITE_OK ? "ok" : "failed"));
return rc;
}
@@ -35414,9 +41117,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
unixInodeInfo *pInode;
afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
int skipShared = 0;
-#ifdef SQLITE_TEST
- int h = pFile->h;
-#endif
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock,
@@ -35432,10 +41132,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
assert( pInode->nShared!=0 );
if( pFile->eFileLock>SHARED_LOCK ){
assert( pInode->eFileLock==pFile->eFileLock );
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
-
+
#ifdef SQLITE_DEBUG
/* When reducing a lock such that other processes can start
** reading the database file again, make sure that the
@@ -35450,7 +41147,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
|| pFile->transCntrChng==1 );
pFile->inNormalWrite = 0;
#endif
-
+
if( pFile->eFileLock==EXCLUSIVE_LOCK ){
rc = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0);
if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1) ){
@@ -35463,11 +41160,11 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
}
if( rc==SQLITE_OK && pFile->eFileLock>=PENDING_LOCK ){
rc = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0);
- }
+ }
if( rc==SQLITE_OK && pFile->eFileLock>=RESERVED_LOCK && context->reserved ){
rc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0);
- if( !rc ){
- context->reserved = 0;
+ if( !rc ){
+ context->reserved = 0;
}
}
if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1)){
@@ -35483,9 +41180,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte;
pInode->nShared--;
if( pInode->nShared==0 ){
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
if( !skipShared ){
rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0);
}
@@ -35500,7 +41194,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
if( pInode->nLock==0 ) closePendingFds(pFile);
}
}
-
+
sqlite3_mutex_leave(pInode->pLockMutex);
if( rc==SQLITE_OK ){
pFile->eFileLock = eFileLock;
@@ -35509,7 +41203,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
}
/*
-** Close a file & cleanup AFP specific locking context
+** Close a file & cleanup AFP specific locking context
*/
static int afpClose(sqlite3_file *id) {
int rc = SQLITE_OK;
@@ -35567,7 +41261,7 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){
/*
** The code above is the NFS lock implementation. The code is specific
** to MacOSX and does not work on other unix platforms. No alternative
-** is available.
+** is available.
**
********************* End of the NFS lock implementation **********************
******************************************************************************/
@@ -35575,7 +41269,7 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){
/******************************************************************************
**************** Non-locking sqlite3_file methods *****************************
**
-** The next division contains implementations for all methods of the
+** The next division contains implementations for all methods of the
** sqlite3_file object other than the locking methods. The locking
** methods were defined in divisions above (one locking method per
** division). Those methods that are common to all locking modes
@@ -35583,15 +41277,9 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){
*/
/*
-** Seek to the offset passed as the second argument, then read cnt
+** Seek to the offset passed as the second argument, then read cnt
** bytes into pBuf. Return the number of bytes actually read.
**
-** NB: If you define USE_PREAD or USE_PREAD64, then it might also
-** be necessary to define _XOPEN_SOURCE to be 500. This varies from
-** one system to another. Since SQLite does not define USE_PREAD
-** in any form by default, we will not attempt to define _XOPEN_SOURCE.
-** See tickets #2741 and #2681.
-**
** To avoid stomping the errno value on a failed read the lastErrno value
** is set before returning.
*/
@@ -35645,8 +41333,8 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
** wrong.
*/
static int unixRead(
- sqlite3_file *id,
- void *pBuf,
+ sqlite3_file *id,
+ void *pBuf,
int amt,
sqlite3_int64 offset
){
@@ -35656,17 +41344,17 @@ static int unixRead(
assert( offset>=0 );
assert( amt>0 );
- /* If this is a database file (not a journal, master-journal or temp
+ /* If this is a database file (not a journal, super-journal or temp
** file), the bytes in the locking range should never be read or written. */
#if 0
assert( pFile->pPreallocatedUnused==0
|| offset>=PENDING_BYTE+512
- || offset+amt<=PENDING_BYTE
+ || offset+amt<=PENDING_BYTE
);
#endif
#if SQLITE_MAX_MMAP_SIZE>0
- /* Deal with as much of this read request as possible by transfering
+ /* Deal with as much of this read request as possible by transferring
** data from the memory mapping using memcpy(). */
if( offset<pFile->mmapSize ){
if( offset+amt <= pFile->mmapSize ){
@@ -35686,7 +41374,24 @@ static int unixRead(
if( got==amt ){
return SQLITE_OK;
}else if( got<0 ){
- /* lastErrno set by seekAndRead */
+ /* pFile->lastErrno has been set by seekAndRead().
+ ** Usually we return SQLITE_IOERR_READ here, though for some
+ ** kinds of errors we return SQLITE_IOERR_CORRUPTFS. The
+ ** SQLITE_IOERR_CORRUPTFS will be converted into SQLITE_CORRUPT
+ ** prior to returning to the application by the sqlite3ApiExit()
+ ** routine.
+ */
+ switch( pFile->lastErrno ){
+ case ERANGE:
+ case EIO:
+#ifdef ENXIO
+ case ENXIO:
+#endif
+#ifdef EDEVERR
+ case EDEVERR:
+#endif
+ return SQLITE_IOERR_CORRUPTFS;
+ }
return SQLITE_IOERR_READ;
}else{
storeLastErrno(pFile, 0); /* not a system error */
@@ -35699,7 +41404,7 @@ static int unixRead(
/*
** Attempt to seek the file-descriptor passed as the first argument to
** absolute offset iOff, then attempt to write nBuf bytes of data from
-** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise,
+** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise,
** return the actual number of bytes written (which may be less than
** nBuf).
*/
@@ -35759,22 +41464,22 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
** or some other error code on failure.
*/
static int unixWrite(
- sqlite3_file *id,
- const void *pBuf,
+ sqlite3_file *id,
+ const void *pBuf,
int amt,
- sqlite3_int64 offset
+ sqlite3_int64 offset
){
unixFile *pFile = (unixFile*)id;
int wrote = 0;
assert( id );
assert( amt>0 );
- /* If this is a database file (not a journal, master-journal or temp
+ /* If this is a database file (not a journal, super-journal or temp
** file), the bytes in the locking range should never be read or written. */
#if 0
assert( pFile->pPreallocatedUnused==0
|| offset>=PENDING_BYTE+512
- || offset+amt<=PENDING_BYTE
+ || offset+amt<=PENDING_BYTE
);
#endif
@@ -35801,7 +41506,7 @@ static int unixWrite(
#endif
#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0
- /* Deal with as much of this write request as possible by transfering
+ /* Deal with as much of this write request as possible by transferring
** data from the memory mapping using memcpy(). */
if( offset<pFile->mmapSize ){
if( offset+amt <= pFile->mmapSize ){
@@ -35816,7 +41521,7 @@ static int unixWrite(
}
}
#endif
-
+
while( (wrote = seekAndWrite(pFile, offset, pBuf, amt))<amt && wrote>0 ){
amt -= wrote;
offset += wrote;
@@ -35882,8 +41587,8 @@ SQLITE_API int sqlite3_fullsync_count = 0;
**
** SQLite sets the dataOnly flag if the size of the file is unchanged.
** The idea behind dataOnly is that it should only write the file content
-** to disk, not the inode. We only set dataOnly if the file size is
-** unchanged since the file size is part of the inode. However,
+** to disk, not the inode. We only set dataOnly if the file size is
+** unchanged since the file size is part of the inode. However,
** Ted Ts'o tells us that fdatasync() will also write the inode if the
** file size has changed. The only real difference between fdatasync()
** and fsync(), Ted tells us, is that fdatasync() will not flush the
@@ -35897,7 +41602,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
int rc;
/* The following "ifdef/elif/else/" block has the same structure as
- ** the one below. It is replicated here solely to avoid cluttering
+ ** the one below. It is replicated here solely to avoid cluttering
** up the real code with the UNUSED_PARAMETER() macros.
*/
#ifdef SQLITE_NO_SYNC
@@ -35911,7 +41616,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
UNUSED_PARAMETER(dataOnly);
#endif
- /* Record the number of times that we do a normal fsync() and
+ /* Record the number of times that we do a normal fsync() and
** FULLSYNC. This is used during testing to verify that this procedure
** gets called with the correct arguments.
*/
@@ -35923,7 +41628,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
/* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
** no-op. But go ahead and call fstat() to validate the file
** descriptor as we need a method to provoke a failure during
- ** coverate testing.
+ ** coverage testing.
*/
#ifdef SQLITE_NO_SYNC
{
@@ -35937,11 +41642,11 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
rc = 1;
}
/* If the FULLFSYNC failed, fall back to attempting an fsync().
- ** It shouldn't be possible for fullfsync to fail on the local
+ ** It shouldn't be possible for fullfsync to fail on the local
** file system (on OSX), so failure indicates that FULLFSYNC
- ** isn't supported for this file system. So, attempt an fsync
- ** and (for now) ignore the overhead of a superfluous fcntl call.
- ** It'd be better to detect fullfsync support once and avoid
+ ** isn't supported for this file system. So, attempt an fsync
+ ** and (for now) ignore the overhead of a superfluous fcntl call.
+ ** It'd be better to detect fullfsync support once and avoid
** the fcntl call every time sync is called.
*/
if( rc ) rc = fsync(fd);
@@ -35951,7 +41656,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
** so currently we default to the macro that redefines fdatasync to fsync
*/
rc = fsync(fd);
-#else
+#else
rc = fdatasync(fd);
#if OS_VXWORKS
if( rc==-1 && errno==ENOTSUP ){
@@ -36112,7 +41817,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
#if SQLITE_MAX_MMAP_SIZE>0
/* If the file was just truncated to a size smaller than the currently
** mapped region, reduce the effective mapping size as well. SQLite will
- ** use read() and write() to access data beyond this point from now on.
+ ** use read() and write() to access data beyond this point from now on.
*/
if( nByte<pFile->mmapSize ){
pFile->mmapSize = nByte;
@@ -36158,8 +41863,8 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){
static int proxyFileControl(sqlite3_file*,int,void*);
#endif
-/*
-** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
+/*
+** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
** file-control operation. Enlarge the database to nBytes in size
** (rounded up to the next chunk-size). If the database is already
** nBytes or larger, this routine is a no-op.
@@ -36168,7 +41873,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
if( pFile->szChunk>0 ){
i64 nSize; /* Required file size */
struct stat buf; /* Used to hold return values of fstat() */
-
+
if( osFstat(pFile->h, &buf) ){
return SQLITE_IOERR_FSTAT;
}
@@ -36177,8 +41882,8 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
if( nSize>(i64)buf.st_size ){
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
- /* The code below is handling the return value of osFallocate()
- ** correctly. posix_fallocate() is defined to "returns zero on success,
+ /* The code below is handling the return value of osFallocate()
+ ** correctly. posix_fallocate() is defined to "returns zero on success,
** or an error number on failure". See the manpage for details. */
int err;
do{
@@ -36186,7 +41891,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
}while( err==EINTR );
if( err && err!=EINVAL ) return SQLITE_IOERR_WRITE;
#else
- /* If the OS does not have posix_fallocate(), fake it. Write a
+ /* If the OS does not have posix_fallocate(), fake it. Write a
** single byte to the last byte in each block that falls entirely
** within the extended region. Then, if required, a single byte
** at offset (nSize-1), to set the size of the file correctly.
@@ -36245,6 +41950,9 @@ static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){
/* Forward declaration */
static int unixGetTempname(int nBuf, char *zBuf);
+#ifndef SQLITE_OMIT_WAL
+ static int unixFcntlExternalReader(unixFile*, int*);
+#endif
/*
** Information and control of an open file handle.
@@ -36312,7 +42020,15 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
case SQLITE_FCNTL_LOCK_TIMEOUT: {
+ int iOld = pFile->iBusyTimeout;
+#if SQLITE_ENABLE_SETLK_TIMEOUT==1
pFile->iBusyTimeout = *(int*)pArg;
+#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
+ pFile->iBusyTimeout = !!(*(int*)pArg);
+#else
+# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
+#endif
+ *(int*)pArg = iOld;
return SQLITE_OK;
}
#endif
@@ -36359,15 +42075,24 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
return proxyFileControl(id,op,pArg);
}
#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
+
+ case SQLITE_FCNTL_EXTERNAL_READER: {
+#ifndef SQLITE_OMIT_WAL
+ return unixFcntlExternalReader((unixFile*)id, (int*)pArg);
+#else
+ *(int*)pArg = 0;
+ return SQLITE_OK;
+#endif
+ }
}
return SQLITE_NOTFOUND;
}
/*
** If pFd->sectorSize is non-zero when this function is called, it is a
-** no-op. Otherwise, the values of pFd->sectorSize and
-** pFd->deviceCharacteristics are set according to the file-system
-** characteristics.
+** no-op. Otherwise, the values of pFd->sectorSize and
+** pFd->deviceCharacteristics are set according to the file-system
+** characteristics.
**
** There are two versions of this function. One for QNX and one for all
** other systems.
@@ -36401,7 +42126,7 @@ static void setDeviceCharacteristics(unixFile *pFd){
static void setDeviceCharacteristics(unixFile *pFile){
if( pFile->sectorSize == 0 ){
struct statvfs fsInfo;
-
+
/* Set defaults for non-supported filesystems */
pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
pFile->deviceCharacteristics = 0;
@@ -36510,7 +42235,7 @@ static int unixDeviceCharacteristics(sqlite3_file *id){
/*
** Return the system page size.
**
-** This function should not be called directly by other code in this file.
+** This function should not be called directly by other code in this file.
** Instead, it should be called via macro osGetpagesize().
*/
static int unixGetpagesize(void){
@@ -36528,7 +42253,7 @@ static int unixGetpagesize(void){
#ifndef SQLITE_OMIT_WAL
/*
-** Object used to represent an shared memory buffer.
+** Object used to represent an shared memory buffer.
**
** When multiple threads all reference the same wal-index, each thread
** has its own unixShm object, but they all point to a single instance
@@ -36548,13 +42273,32 @@ static int unixGetpagesize(void){
** nRef
**
** The following fields are read-only after the object is created:
-**
+**
** hShm
** zFilename
**
** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and
** unixMutexHeld() is true when reading or writing any other field
** in this structure.
+**
+** aLock[SQLITE_SHM_NLOCK]:
+** This array records the various locks held by clients on each of the
+** SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no
+** locks are held by the process on this slot. If it is set to -1, then
+** some client holds an EXCLUSIVE lock on the locking slot. If the aLock[]
+** value is set to a positive value, then it is the number of shared
+** locks currently held on the slot.
+**
+** aMutex[SQLITE_SHM_NLOCK]:
+** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex
+** pShmMutex is used to protect the aLock[] array and the right to
+** call fcntl() on unixShmNode.hShm to obtain or release locks.
+**
+** If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array
+** of mutexes - one for each locking slot. To read or write locking
+** slot aLock[iSlot], the caller must hold the corresponding mutex
+** aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a
+** lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held.
*/
struct unixShmNode {
unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */
@@ -36568,9 +42312,11 @@ struct unixShmNode {
char **apRegion; /* Array of mapped shared-memory regions */
int nRef; /* Number of unixShm objects pointing to this */
unixShm *pFirst; /* All unixShm objects pointing to this */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK];
+#endif
+ int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */
#ifdef SQLITE_DEBUG
- u8 exclMask; /* Mask of exclusive locks held */
- u8 sharedMask; /* Mask of shared locks held */
u8 nextShmId; /* Next available unixShm.id value */
#endif
};
@@ -36604,6 +42350,40 @@ struct unixShm {
#define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
/*
+** Use F_GETLK to check whether or not there are any readers with open
+** wal-mode transactions in other processes on database file pFile. If
+** no error occurs, return SQLITE_OK and set (*piOut) to 1 if there are
+** such transactions, or 0 otherwise. If an error occurs, return an
+** SQLite error code. The final value of *piOut is undefined in this
+** case.
+*/
+static int unixFcntlExternalReader(unixFile *pFile, int *piOut){
+ int rc = SQLITE_OK;
+ *piOut = 0;
+ if( pFile->pShm){
+ unixShmNode *pShmNode = pFile->pShm->pShmNode;
+ struct flock f;
+
+ memset(&f, 0, sizeof(f));
+ f.l_type = F_WRLCK;
+ f.l_whence = SEEK_SET;
+ f.l_start = UNIX_SHM_BASE + 3;
+ f.l_len = SQLITE_SHM_NLOCK - 3;
+
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
+ if( osFcntl(pShmNode->hShm, F_GETLK, &f)<0 ){
+ rc = SQLITE_IOERR_LOCK;
+ }else{
+ *piOut = (f.l_type!=F_UNLCK);
+ }
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
+ }
+
+ return rc;
+}
+
+
+/*
** Apply posix advisory locks for all bytes from ofst through ofst+n-1.
**
** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking
@@ -36619,63 +42399,78 @@ static int unixShmSystemLock(
struct flock f; /* The posix advisory locking structure */
int rc = SQLITE_OK; /* Result code form fcntl() */
- /* Access to the unixShmNode object is serialized by the caller */
pShmNode = pFile->pInode->pShmNode;
- assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
- assert( pShmNode->nRef>0 || unixMutexHeld() );
+
+ /* Assert that the parameters are within expected range and that the
+ ** correct mutex or mutexes are held. */
+ assert( pShmNode->nRef>=0 );
+ assert( (ofst==UNIX_SHM_DMS && n==1)
+ || (ofst>=UNIX_SHM_BASE && ofst+n<=(UNIX_SHM_BASE+SQLITE_SHM_NLOCK))
+ );
+ if( ofst==UNIX_SHM_DMS ){
+ assert( pShmNode->nRef>0 || unixMutexHeld() );
+ assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
+ }else{
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int ii;
+ for(ii=ofst-UNIX_SHM_BASE; ii<ofst-UNIX_SHM_BASE+n; ii++){
+ assert( sqlite3_mutex_held(pShmNode->aMutex[ii]) );
+ }
+#else
+ assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
+ assert( pShmNode->nRef>0 );
+#endif
+ }
/* Shared locks never span more than one byte */
assert( n==1 || lockType!=F_RDLCK );
/* Locks are within range */
assert( n>=1 && n<=SQLITE_SHM_NLOCK );
+ assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) );
if( pShmNode->hShm>=0 ){
+ int res;
/* Initialize the locking parameters */
f.l_type = lockType;
f.l_whence = SEEK_SET;
f.l_start = ofst;
f.l_len = n;
- rc = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
- rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
+ res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
+ if( res==-1 ){
+#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && SQLITE_ENABLE_SETLK_TIMEOUT==1
+ rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY);
+#else
+ rc = SQLITE_BUSY;
+#endif
+ }
}
- /* Update the global lock state and do debug tracing */
+ /* Do debug tracing */
#ifdef SQLITE_DEBUG
- { u16 mask;
OSTRACE(("SHM-LOCK "));
- mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
if( rc==SQLITE_OK ){
if( lockType==F_UNLCK ){
- OSTRACE(("unlock %d ok", ofst));
- pShmNode->exclMask &= ~mask;
- pShmNode->sharedMask &= ~mask;
+ OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1));
}else if( lockType==F_RDLCK ){
- OSTRACE(("read-lock %d ok", ofst));
- pShmNode->exclMask &= ~mask;
- pShmNode->sharedMask |= mask;
+ OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1));
}else{
assert( lockType==F_WRLCK );
- OSTRACE(("write-lock %d ok", ofst));
- pShmNode->exclMask |= mask;
- pShmNode->sharedMask &= ~mask;
+ OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1));
}
}else{
if( lockType==F_UNLCK ){
- OSTRACE(("unlock %d failed", ofst));
+ OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1));
}else if( lockType==F_RDLCK ){
- OSTRACE(("read-lock failed"));
+ OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1));
}else{
assert( lockType==F_WRLCK );
- OSTRACE(("write-lock %d failed", ofst));
+ OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1));
}
}
- OSTRACE((" - afterwards %03x,%03x\n",
- pShmNode->sharedMask, pShmNode->exclMask));
- }
#endif
- return rc;
+ return rc;
}
/*
@@ -36709,6 +42504,11 @@ static void unixShmPurge(unixFile *pFd){
int i;
assert( p->pInode==pFd->pInode );
sqlite3_mutex_free(p->pShmMutex);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ for(i=0; i<SQLITE_SHM_NLOCK; i++){
+ sqlite3_mutex_free(p->aMutex[i]);
+ }
+#endif
for(i=0; i<p->nRegion; i+=nShmPerMap){
if( p->hShm>=0 ){
osMunmap(p->apRegion[i], p->szRegion);
@@ -36731,7 +42531,7 @@ static void unixShmPurge(unixFile *pFd){
** take it now. Return SQLITE_OK if successful, or an SQLite error
** code otherwise.
**
-** If the DMS cannot be locked because this is a readonly_shm=1
+** If the DMS cannot be locked because this is a readonly_shm=1
** connection and no other process already holds a lock, return
** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1.
*/
@@ -36742,7 +42542,7 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
/* Use F_GETLK to determine the locks other processes are holding
** on the DMS byte. If it indicates that another process is holding
** a SHARED lock, then this process may also take a SHARED lock
- ** and proceed with opening the *-shm file.
+ ** and proceed with opening the *-shm file.
**
** Or, if no other process is holding any lock, then this process
** is the first to open it. In this case take an EXCLUSIVE lock on the
@@ -36768,7 +42568,20 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
pShmNode->isUnlocked = 1;
rc = SQLITE_READONLY_CANTINIT;
}else{
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* Do not use a blocking lock here. If the lock cannot be obtained
+ ** immediately, it means some other connection is truncating the
+ ** *-shm file. And after it has done so, it will not release its
+ ** lock, but only downgrade it to a shared lock. So no point in
+ ** blocking here. The call below to obtain the shared DMS lock may
+ ** use a blocking lock. */
+ int iSaveTimeout = pDbFd->iBusyTimeout;
+ pDbFd->iBusyTimeout = 0;
+#endif
rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ pDbFd->iBusyTimeout = iSaveTimeout;
+#endif
/* The first connection to attach must truncate the -shm file. We
** truncate to 3 bytes (an arbitrary small number, less than the
** -shm header size) rather than 0 as a system debugging aid, to
@@ -36790,20 +42603,20 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
}
/*
-** Open a shared-memory area associated with open database file pDbFd.
+** Open a shared-memory area associated with open database file pDbFd.
** This particular implementation uses mmapped files.
**
** The file used to implement shared-memory is in the same directory
** as the open database file and has the same name as the open database
** file with the "-shm" suffix added. For example, if the database file
** is "/home/user1/config.db" then the file that is created and mmapped
-** for shared memory will be called "/home/user1/config.db-shm".
+** for shared memory will be called "/home/user1/config.db-shm".
**
** Another approach to is to use files in /dev/shm or /dev/tmp or an
** some other tmpfs mount. But if a file in a different directory
** from the database file is used, then differing access permissions
** or a chroot() might cause two different processes on the same
-** database to end up using different files for shared memory -
+** database to end up using different files for shared memory -
** meaning that their memory would not really be shared - resulting
** in database corruption. Nevertheless, this tmpfs file usage
** can be enabled at compile-time using -DSQLITE_SHM_DIRECTORY="/dev/shm"
@@ -36873,7 +42686,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename);
zShm = pShmNode->zFilename = (char*)&pShmNode[1];
#ifdef SQLITE_SHM_DIRECTORY
- sqlite3_snprintf(nShmFilename, zShm,
+ sqlite3_snprintf(nShmFilename, zShm,
SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x",
(u32)sStat.st_ino, (u32)sStat.st_dev);
#else
@@ -36889,14 +42702,28 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
rc = SQLITE_NOMEM_BKPT;
goto shm_open_err;
}
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ {
+ int ii;
+ for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
+ pShmNode->aMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ if( pShmNode->aMutex[ii]==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ goto shm_open_err;
+ }
+ }
+ }
+#endif
}
if( pInode->bProcessLock==0 ){
if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
- pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT,(sStat.st_mode&0777));
+ pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT|O_NOFOLLOW,
+ (sStat.st_mode&0777));
}
if( pShmNode->hShm<0 ){
- pShmNode->hShm = robust_open(zShm, O_RDONLY, (sStat.st_mode&0777));
+ pShmNode->hShm = robust_open(zShm, O_RDONLY|O_NOFOLLOW,
+ (sStat.st_mode&0777));
if( pShmNode->hShm<0 ){
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShm);
goto shm_open_err;
@@ -36946,22 +42773,22 @@ shm_open_err:
}
/*
-** This function is called to obtain a pointer to region iRegion of the
-** shared-memory associated with the database file fd. Shared-memory regions
-** are numbered starting from zero. Each shared-memory region is szRegion
+** This function is called to obtain a pointer to region iRegion of the
+** shared-memory associated with the database file fd. Shared-memory regions
+** are numbered starting from zero. Each shared-memory region is szRegion
** bytes in size.
**
** If an error occurs, an error code is returned and *pp is set to NULL.
**
** Otherwise, if the bExtend parameter is 0 and the requested shared-memory
** region has not been allocated (by any client, including one running in a
-** separate process), then *pp is set to NULL and SQLITE_OK returned. If
-** bExtend is non-zero and the requested shared-memory region has not yet
+** separate process), then *pp is set to NULL and SQLITE_OK returned. If
+** bExtend is non-zero and the requested shared-memory region has not yet
** been allocated, it is allocated by this function.
**
** If the shared-memory region has already been allocated or is allocated by
-** this call as described above, then it is mapped into this processes
-** address space (if it is not already), *pp is set to point to the mapped
+** this call as described above, then it is mapped into this processes
+** address space (if it is not already), *pp is set to point to the mapped
** memory and SQLITE_OK returned.
*/
static int unixShmMap(
@@ -37016,7 +42843,7 @@ static int unixShmMap(
rc = SQLITE_IOERR_SHMSIZE;
goto shmpage_out;
}
-
+
if( sStat.st_size<nByte ){
/* The requested memory region does not exist. If bExtend is set to
** false, exit early. *pp will be set to NULL and SQLITE_OK returned.
@@ -37065,7 +42892,7 @@ static int unixShmMap(
void *pMem;
if( pShmNode->hShm>=0 ){
pMem = osMmap(0, nMap,
- pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE,
+ pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE,
MAP_SHARED, pShmNode->hShm, szRegion*(i64)pShmNode->nRegion
);
if( pMem==MAP_FAILED ){
@@ -37100,9 +42927,44 @@ shmpage_out:
}
/*
+** Check that the pShmNode->aLock[] array comports with the locking bitmasks
+** held by each client. Return true if it does, or false otherwise. This
+** is to be used in an assert(). e.g.
+**
+** assert( assertLockingArrayOk(pShmNode) );
+*/
+#ifdef SQLITE_DEBUG
+static int assertLockingArrayOk(unixShmNode *pShmNode){
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ return 1;
+#else
+ unixShm *pX;
+ int aLock[SQLITE_SHM_NLOCK];
+
+ memset(aLock, 0, sizeof(aLock));
+ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
+ int i;
+ for(i=0; i<SQLITE_SHM_NLOCK; i++){
+ if( pX->exclMask & (1<<i) ){
+ assert( aLock[i]==0 );
+ aLock[i] = -1;
+ }else if( pX->sharedMask & (1<<i) ){
+ assert( aLock[i]>=0 );
+ aLock[i]++;
+ }
+ }
+ }
+
+ assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) );
+ return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0);
+#endif
+}
+#endif
+
+/*
** Change the lock state for a shared-memory segment.
**
-** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
+** Note that the relationship between SHARED and EXCLUSIVE locks is a little
** different here than in posix. In xShmLock(), one can go from unlocked
** to shared and back or from unlocked to exclusive and back. But one may
** not go from shared to exclusive or from exclusive to shared.
@@ -37114,11 +42976,17 @@ static int unixShmLock(
int flags /* What to do with the lock */
){
unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */
- unixShm *p = pDbFd->pShm; /* The shared memory being locked */
- unixShm *pX; /* For looping over all siblings */
- unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */
+ unixShm *p; /* The shared memory being locked */
+ unixShmNode *pShmNode; /* The underlying file iNode */
int rc = SQLITE_OK; /* Result code */
- u16 mask; /* Mask of locks to take or release */
+ u16 mask = (1<<(ofst+n)) - (1<<ofst); /* Mask of locks to take or release */
+ int *aLock;
+
+ p = pDbFd->pShm;
+ if( p==0 ) return SQLITE_IOERR_SHMLOCK;
+ pShmNode = p->pShmNode;
+ if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK;
+ aLock = pShmNode->aLock;
assert( pShmNode==pDbFd->pInode->pShmNode );
assert( pShmNode->pInode==pDbFd->pInode );
@@ -37132,89 +43000,172 @@ static int unixShmLock(
assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
- mask = (1<<(ofst+n)) - (1<<ofst);
- assert( n>1 || mask==(1<<ofst) );
- sqlite3_mutex_enter(pShmNode->pShmMutex);
- if( flags & SQLITE_SHM_UNLOCK ){
- u16 allMask = 0; /* Mask of locks held by siblings */
+ /* Check that, if this to be a blocking lock, no locks that occur later
+ ** in the following list than the lock being obtained are already held:
+ **
+ ** 1. Checkpointer lock (ofst==1).
+ ** 2. Write lock (ofst==0).
+ ** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
+ **
+ ** In other words, if this is a blocking lock, none of the locks that
+ ** occur later in the above list than the lock being obtained may be
+ ** held.
+ **
+ ** It is not permitted to block on the RECOVER lock.
+ */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ {
+ u16 lockMask = (p->exclMask|p->sharedMask);
+ assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
+ (ofst!=2) /* not RECOVER */
+ && (ofst!=1 || lockMask==0 || lockMask==2)
+ && (ofst!=0 || lockMask<3)
+ && (ofst<3 || lockMask<(1<<ofst))
+ ));
+ }
+#endif
- /* See if any siblings hold this same lock */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( pX==p ) continue;
- assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
- allMask |= pX->sharedMask;
- }
+ /* Check if there is any work to do. There are three cases:
+ **
+ ** a) An unlock operation where there are locks to unlock,
+ ** b) An shared lock where the requested lock is not already held
+ ** c) An exclusive lock where the requested lock is not already held
+ **
+ ** The SQLite core never requests an exclusive lock that it already holds.
+ ** This is assert()ed below.
+ */
+ assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
+ || 0==(p->exclMask & mask)
+ );
+ if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
+ || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
+ || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
+ ){
- /* Unlock the system-level locks */
- if( (mask & allMask)==0 ){
- rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
- }else{
- rc = SQLITE_OK;
+ /* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if
+ ** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any
+ ** other thread is holding this mutex, then it is either holding or about
+ ** to hold a lock exclusive to the one being requested, and we may
+ ** therefore return SQLITE_BUSY to the caller.
+ **
+ ** Doing this prevents some deadlock scenarios. For example, thread 1 may
+ ** be a checkpointer blocked waiting on the WRITER lock. And thread 2
+ ** may be a normal SQL client upgrading to a write transaction. In this
+ ** case thread 2 does a non-blocking request for the WRITER lock. But -
+ ** if it were to use sqlite3_mutex_enter() then it would effectively
+ ** become a (doomed) blocking request, as thread 2 would block until thread
+ ** 1 obtained WRITER and released the mutex. Since thread 2 already holds
+ ** a lock on a read-locking slot at this point, this breaks the
+ ** anti-deadlock rules (see above). */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int iMutex;
+ for(iMutex=ofst; iMutex<ofst+n; iMutex++){
+ if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){
+ rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]);
+ if( rc!=SQLITE_OK ) goto leave_shmnode_mutexes;
+ }else{
+ sqlite3_mutex_enter(pShmNode->aMutex[iMutex]);
+ }
}
+#else
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
+#endif
- /* Undo the local locks */
- if( rc==SQLITE_OK ){
- p->exclMask &= ~mask;
- p->sharedMask &= ~mask;
- }
- }else if( flags & SQLITE_SHM_SHARED ){
- u16 allShared = 0; /* Union of locks held by connections other than "p" */
+ if( ALWAYS(rc==SQLITE_OK) ){
+ if( flags & SQLITE_SHM_UNLOCK ){
+ /* Case (a) - unlock. */
+ int bUnlock = 1;
+ assert( (p->exclMask & p->sharedMask)==0 );
+ assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
+ assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
- /* Find out which shared locks are already held by sibling connections.
- ** If any sibling already holds an exclusive lock, go ahead and return
- ** SQLITE_BUSY.
- */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( (pX->exclMask & mask)!=0 ){
- rc = SQLITE_BUSY;
- break;
- }
- allShared |= pX->sharedMask;
- }
+ /* If this is a SHARED lock being unlocked, it is possible that other
+ ** clients within this process are holding the same SHARED lock. In
+ ** this case, set bUnlock to 0 so that the posix lock is not removed
+ ** from the file-descriptor below. */
+ if( flags & SQLITE_SHM_SHARED ){
+ assert( n==1 );
+ assert( aLock[ofst]>=1 );
+ if( aLock[ofst]>1 ){
+ bUnlock = 0;
+ aLock[ofst]--;
+ p->sharedMask &= ~mask;
+ }
+ }
- /* Get shared locks at the system level, if necessary */
- if( rc==SQLITE_OK ){
- if( (allShared & mask)==0 ){
- rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
+ if( bUnlock ){
+ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
+ if( rc==SQLITE_OK ){
+ memset(&aLock[ofst], 0, sizeof(int)*n);
+ p->sharedMask &= ~mask;
+ p->exclMask &= ~mask;
+ }
+ }
+ }else if( flags & SQLITE_SHM_SHARED ){
+ /* Case (b) - a shared lock. */
+
+ if( aLock[ofst]<0 ){
+ /* An exclusive lock is held by some other connection. BUSY. */
+ rc = SQLITE_BUSY;
+ }else if( aLock[ofst]==0 ){
+ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
+ }
+
+ /* Get the local shared locks */
+ if( rc==SQLITE_OK ){
+ p->sharedMask |= mask;
+ aLock[ofst]++;
+ }
}else{
- rc = SQLITE_OK;
- }
- }
+ /* Case (c) - an exclusive lock. */
+ int ii;
- /* Get the local shared locks */
- if( rc==SQLITE_OK ){
- p->sharedMask |= mask;
- }
- }else{
- /* Make sure no sibling connections hold locks that will block this
- ** lock. If any do, return SQLITE_BUSY right away.
- */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
- rc = SQLITE_BUSY;
- break;
- }
- }
-
- /* Get the exclusive locks at the system level. Then if successful
- ** also mark the local connection as being locked.
- */
- if( rc==SQLITE_OK ){
- rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
- if( rc==SQLITE_OK ){
+ assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) );
assert( (p->sharedMask & mask)==0 );
- p->exclMask |= mask;
+ assert( (p->exclMask & mask)==0 );
+
+ /* Make sure no sibling connections hold locks that will block this
+ ** lock. If any do, return SQLITE_BUSY right away. */
+ for(ii=ofst; ii<ofst+n; ii++){
+ if( aLock[ii] ){
+ rc = SQLITE_BUSY;
+ break;
+ }
+ }
+
+ /* Get the exclusive locks at the system level. Then if successful
+ ** also update the in-memory values. */
+ if( rc==SQLITE_OK ){
+ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
+ if( rc==SQLITE_OK ){
+ p->exclMask |= mask;
+ for(ii=ofst; ii<ofst+n; ii++){
+ aLock[ii] = -1;
+ }
+ }
+ }
}
+ assert( assertLockingArrayOk(pShmNode) );
+ }
+
+ /* Drop the mutexes acquired above. */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ leave_shmnode_mutexes:
+ for(iMutex--; iMutex>=ofst; iMutex--){
+ sqlite3_mutex_leave(pShmNode->aMutex[iMutex]);
}
+#else
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
+#endif
}
- sqlite3_mutex_leave(pShmNode->pShmMutex);
+
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
p->id, osGetpid(0), p->sharedMask, p->exclMask));
return rc;
}
/*
-** Implement a memory barrier or memory fence on shared memory.
+** Implement a memory barrier or memory fence on shared memory.
**
** All loads and stores begun before the barrier must complete before
** any load or store begun after the barrier.
@@ -37224,15 +43175,15 @@ static void unixShmBarrier(
){
UNUSED_PARAMETER(fd);
sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
- assert( fd->pMethods->xLock==nolockLock
- || unixFileMutexNotheld((unixFile*)fd)
+ assert( fd->pMethods->xLock==nolockLock
+ || unixFileMutexNotheld((unixFile*)fd)
);
unixEnterMutex(); /* Also mutex, for redundancy */
unixLeaveMutex();
}
/*
-** Close a connection to shared-memory. Delete the underlying
+** Close a connection to shared-memory. Delete the underlying
** storage if deleteFlag is true.
**
** If there is no shared memory associated with the connection then this
@@ -37306,7 +43257,7 @@ static void unixUnmapfile(unixFile *pFd){
}
/*
-** Attempt to set the size of the memory mapping maintained by file
+** Attempt to set the size of the memory mapping maintained by file
** descriptor pFd to nNew bytes. Any existing mapping is discarded.
**
** If successful, this function sets the following variables:
@@ -37398,14 +43349,14 @@ static void unixRemapfile(
/*
** Memory map or remap the file opened by file-descriptor pFd (if the file
-** is already mapped, the existing mapping is replaced by the new). Or, if
-** there already exists a mapping for this file, and there are still
+** is already mapped, the existing mapping is replaced by the new). Or, if
+** there already exists a mapping for this file, and there are still
** outstanding xFetch() references to it, this function is a no-op.
**
-** If parameter nByte is non-negative, then it is the requested size of
-** the mapping to create. Otherwise, if nByte is less than zero, then the
+** If parameter nByte is non-negative, then it is the requested size of
+** the mapping to create. Otherwise, if nByte is less than zero, then the
** requested size is the size of the file on disk. The actual size of the
-** created mapping is either the requested size or the value configured
+** created mapping is either the requested size or the value configured
** using SQLITE_FCNTL_MMAP_LIMIT, whichever is smaller.
**
** SQLITE_OK is returned if no error occurs (even if the mapping is not
@@ -37446,7 +43397,7 @@ static int unixMapfile(unixFile *pFd, i64 nMap){
** Finally, if an error does occur, return an SQLite error code. The final
** value of *pp is undefined in this case.
**
-** If this function does return a pointer, the caller must eventually
+** If this function does return a pointer, the caller must eventually
** release the reference by calling unixUnfetch().
*/
static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
@@ -37457,11 +43408,16 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
+ /* Ensure that there is always at least a 256 byte buffer of addressable
+ ** memory following the returned page. If the database is corrupt,
+ ** SQLite may overread the page slightly (in practice only a few bytes,
+ ** but 256 is safe, round, number). */
+ const int nEofBuffer = 256;
if( pFd->pMapRegion==0 ){
int rc = unixMapfile(pFd, -1);
if( rc!=SQLITE_OK ) return rc;
}
- if( pFd->mmapSize >= iOff+nAmt ){
+ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
}
@@ -37471,13 +43427,13 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
}
/*
-** If the third argument is non-NULL, then this function releases a
+** If the third argument is non-NULL, then this function releases a
** reference obtained by an earlier call to unixFetch(). The second
** argument passed to this function must be the same as the corresponding
-** argument that was passed to the unixFetch() invocation.
+** argument that was passed to the unixFetch() invocation.
**
-** Or, if the third argument is NULL, then this function is being called
-** to inform the VFS layer that, according to POSIX, any existing mapping
+** Or, if the third argument is NULL, then this function is being called
+** to inform the VFS layer that, according to POSIX, any existing mapping
** may now be invalid and should be unmapped.
*/
static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
@@ -37485,7 +43441,7 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
unixFile *pFd = (unixFile *)fd; /* The underlying database file */
UNUSED_PARAMETER(iOff);
- /* If p==0 (unmap the entire file) then there must be no outstanding
+ /* If p==0 (unmap the entire file) then there must be no outstanding
** xFetch references. Or, if p!=0 (meaning it is an xFetch reference),
** then there must be at least one outstanding. */
assert( (p==0)==(pFd->nFetchOut==0) );
@@ -37693,8 +43649,8 @@ IOMETHODS(
#endif
#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
-/*
-** This "finder" function attempts to determine the best locking strategy
+/*
+** This "finder" function attempts to determine the best locking strategy
** for the database file "filePath". It then returns the sqlite3_io_methods
** object that implements that strategy.
**
@@ -37736,8 +43692,8 @@ static const sqlite3_io_methods *autolockIoFinderImpl(
}
/* Default case. Handles, amongst others, "nfs".
- ** Test byte-range lock using fcntl(). If the call succeeds,
- ** assume that the file-system supports POSIX style locks.
+ ** Test byte-range lock using fcntl(). If the call succeeds,
+ ** assume that the file-system supports POSIX style locks.
*/
lockInfo.l_len = 1;
lockInfo.l_start = 0;
@@ -37753,7 +43709,7 @@ static const sqlite3_io_methods *autolockIoFinderImpl(
return &dotlockIoMethods;
}
}
-static const sqlite3_io_methods
+static const sqlite3_io_methods
*(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl;
#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
@@ -37789,7 +43745,7 @@ static const sqlite3_io_methods *vxworksIoFinderImpl(
return &semIoMethods;
}
}
-static const sqlite3_io_methods
+static const sqlite3_io_methods
*(*const vxworksIoFinder)(const char*,unixFile*) = vxworksIoFinderImpl;
#endif /* OS_VXWORKS */
@@ -37917,14 +43873,14 @@ static int fillInUnixFile(
robust_close(pNew, h, __LINE__);
h = -1;
}
- unixLeaveMutex();
+ unixLeaveMutex();
}
}
#endif
else if( pLockingStyle == &dotlockIoMethods ){
/* Dotfile locking uses the file path so it needs to be included in
- ** the dotlockLockingContext
+ ** the dotlockLockingContext
*/
char *zLockFile;
int nFilename;
@@ -37962,7 +43918,7 @@ static int fillInUnixFile(
unixLeaveMutex();
}
#endif
-
+
storeLastErrno(pNew, 0);
#if OS_VXWORKS
if( rc!=SQLITE_OK ){
@@ -37975,7 +43931,7 @@ static int fillInUnixFile(
if( rc!=SQLITE_OK ){
if( h>=0 ) robust_close(pNew, h, __LINE__);
}else{
- pNew->pMethod = pLockingStyle;
+ pId->pMethods = pLockingStyle;
OpenCounter(+1);
verifyDbFile(pNew);
}
@@ -37983,24 +43939,34 @@ static int fillInUnixFile(
}
/*
+** Directories to consider for temp files.
+*/
+static const char *azTempDirs[] = {
+ 0,
+ 0,
+ "/var/tmp",
+ "/usr/tmp",
+ "/tmp",
+ "."
+};
+
+/*
+** Initialize first two members of azTempDirs[] array.
+*/
+static void unixTempFileInit(void){
+ azTempDirs[0] = getenv("SQLITE_TMPDIR");
+ azTempDirs[1] = getenv("TMPDIR");
+}
+
+/*
** Return the name of a directory in which to put temporary files.
** If no suitable temporary file directory can be found, return NULL.
*/
static const char *unixTempFileDir(void){
- static const char *azDirs[] = {
- 0,
- 0,
- "/var/tmp",
- "/usr/tmp",
- "/tmp",
- "."
- };
unsigned int i = 0;
struct stat buf;
const char *zDir = sqlite3_temp_directory;
- if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
- if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
while(1){
if( zDir!=0
&& osStat(zDir, &buf)==0
@@ -38009,8 +43975,8 @@ static const char *unixTempFileDir(void){
){
return zDir;
}
- if( i>=sizeof(azDirs)/sizeof(azDirs[0]) ) break;
- zDir = azDirs[i++];
+ if( i>=sizeof(azTempDirs)/sizeof(azTempDirs[0]) ) break;
+ zDir = azTempDirs[i++];
}
return 0;
}
@@ -38023,26 +43989,35 @@ static const char *unixTempFileDir(void){
static int unixGetTempname(int nBuf, char *zBuf){
const char *zDir;
int iLimit = 0;
+ int rc = SQLITE_OK;
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
- ** function failing.
+ ** function failing.
*/
zBuf[0] = 0;
SimulateIOError( return SQLITE_IOERR );
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
zDir = unixTempFileDir();
- if( zDir==0 ) return SQLITE_IOERR_GETTEMPPATH;
- do{
- u64 r;
- sqlite3_randomness(sizeof(r), &r);
- assert( nBuf>2 );
- zBuf[nBuf-2] = 0;
- sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c",
- zDir, r, 0);
- if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR;
- }while( osAccess(zBuf,0)==0 );
- return SQLITE_OK;
+ if( zDir==0 ){
+ rc = SQLITE_IOERR_GETTEMPPATH;
+ }else{
+ do{
+ u64 r;
+ sqlite3_randomness(sizeof(r), &r);
+ assert( nBuf>2 );
+ zBuf[nBuf-2] = 0;
+ sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c",
+ zDir, r, 0);
+ if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ){
+ rc = SQLITE_ERROR;
+ break;
+ }
+ }while( osAccess(zBuf,0)==0 );
+ }
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
+ return rc;
}
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
@@ -38055,8 +44030,8 @@ static int proxyTransformUnixFile(unixFile*, const char*);
#endif
/*
-** Search for an unused file descriptor that was opened on the database
-** file (not a journal or master-journal file) identified by pathname
+** Search for an unused file descriptor that was opened on the database
+** file (not a journal or super-journal file) identified by pathname
** zPath with SQLITE_OPEN_XXX flags matching those passed as the second
** argument to this function.
**
@@ -38064,7 +44039,7 @@ static int proxyTransformUnixFile(unixFile*, const char*);
** but the associated file descriptor could not be closed because some
** other file descriptor open on the same file is holding a file-lock.
** Refer to comments in the unixClose() function and the lengthy comment
-** describing "Posix Advisory Locking" at the start of this file for
+** describing "Posix Advisory Locking" at the start of this file for
** further details. Also, ticket #4018.
**
** If a suitable file descriptor is found, then it is returned. If no
@@ -38075,8 +44050,8 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
/* Do not search for an unused file descriptor on vxworks. Not because
** vxworks would not benefit from the change (it might, we're not sure),
- ** but because no way to test it is currently available. It is better
- ** not to risk breaking vxworks support for the sake of such an obscure
+ ** but because no way to test it is currently available. It is better
+ ** not to risk breaking vxworks support for the sake of such an obscure
** feature. */
#if !OS_VXWORKS
struct stat sStat; /* Results of stat() call */
@@ -38103,6 +44078,7 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
UnixUnusedFd **pp;
assert( sqlite3_mutex_notheld(pInode->pLockMutex) );
sqlite3_mutex_enter(pInode->pLockMutex);
+ flags &= (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE);
for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
pUnused = *pp;
if( pUnused ){
@@ -38117,7 +44093,7 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
}
/*
-** Find the mode, uid and gid of file zFile.
+** Find the mode, uid and gid of file zFile.
*/
static int getFileMode(
const char *zFile, /* File name */
@@ -38141,22 +44117,22 @@ static int getFileMode(
** This function is called by unixOpen() to determine the unix permissions
** to create new files with. If no error occurs, then SQLITE_OK is returned
** and a value suitable for passing as the third argument to open(2) is
-** written to *pMode. If an IO error occurs, an SQLite error code is
+** written to *pMode. If an IO error occurs, an SQLite error code is
** returned and the value of *pMode is not modified.
**
** In most cases, this routine sets *pMode to 0, which will become
** an indication to robust_open() to create the file using
** SQLITE_DEFAULT_FILE_PERMISSIONS adjusted by the umask.
-** But if the file being opened is a WAL or regular journal file, then
-** this function queries the file-system for the permissions on the
-** corresponding database file and sets *pMode to this value. Whenever
-** possible, WAL and journal files are created using the same permissions
+** But if the file being opened is a WAL or regular journal file, then
+** this function queries the file-system for the permissions on the
+** corresponding database file and sets *pMode to this value. Whenever
+** possible, WAL and journal files are created using the same permissions
** as the associated database file.
**
** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the
** original filename is unavailable. But 8_3_NAMES is only used for
** FAT filesystems and permissions do not matter there, so just use
-** the default permissions.
+** the default permissions. In 8_3_NAMES mode, leave *pMode set to zero.
*/
static int findCreateFileMode(
const char *zPath, /* Path of file (possibly) being created */
@@ -38182,22 +44158,25 @@ static int findCreateFileMode(
** "<path to db>-journalNN"
** "<path to db>-walNN"
**
- ** where NN is a decimal number. The NN naming schemes are
+ ** where NN is a decimal number. The NN naming schemes are
** used by the test_multiplex.c module.
+ **
+ ** In normal operation, the journal file name will always contain
+ ** a '-' character. However in 8+3 filename mode, or if a corrupt
+ ** rollback journal specifies a super-journal with a goofy name, then
+ ** the '-' might be missing or the '-' might be the first character in
+ ** the filename. In that case, just return SQLITE_OK with *pMode==0.
*/
- nDb = sqlite3Strlen30(zPath) - 1;
- while( zPath[nDb]!='-' ){
- /* In normal operation, the journal file name will always contain
- ** a '-' character. However in 8+3 filename mode, or if a corrupt
- ** rollback journal specifies a master journal with a goofy name, then
- ** the '-' might be missing. */
- if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK;
+ nDb = sqlite3Strlen30(zPath) - 1;
+ while( nDb>0 && zPath[nDb]!='.' ){
+ if( zPath[nDb]=='-' ){
+ memcpy(zDb, zPath, nDb);
+ zDb[nDb] = '\0';
+ rc = getFileMode(zDb, pMode, pUid, pGid);
+ break;
+ }
nDb--;
}
- memcpy(zDb, zPath, nDb);
- zDb[nDb] = '\0';
-
- rc = getFileMode(zDb, pMode, pUid, pGid);
}else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
*pMode = 0600;
}else if( flags & SQLITE_OPEN_URI ){
@@ -38215,7 +44194,7 @@ static int findCreateFileMode(
/*
** Open the file zPath.
-**
+**
** Previously, the SQLite OS layer used three functions in place of this
** one:
**
@@ -38226,13 +44205,13 @@ static int findCreateFileMode(
** These calls correspond to the following combinations of flags:
**
** ReadWrite() -> (READWRITE | CREATE)
-** ReadOnly() -> (READONLY)
+** ReadOnly() -> (READONLY)
** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE)
**
** The old OpenExclusive() accepted a boolean argument - "delFlag". If
** true, the file was configured to be automatically deleted when the
-** file handle closed. To achieve the same effect using this new
-** interface, add the DELETEONCLOSE flag to those specified above for
+** file handle closed. To achieve the same effect using this new
+** interface, add the DELETEONCLOSE flag to those specified above for
** OpenExclusive().
*/
static int unixOpen(
@@ -38245,7 +44224,7 @@ static int unixOpen(
unixFile *p = (unixFile *)pFile;
int fd = -1; /* File descriptor returned by open() */
int openFlags = 0; /* Flags to pass to open() */
- int eType = flags&0xFFFFFF00; /* Type of file to open */
+ int eType = flags&0x0FFF00; /* Type of file to open */
int noLock; /* True to omit locking primitives */
int rc = SQLITE_OK; /* Function Return Code */
int ctrlFlags = 0; /* UNIXFILE_* flags */
@@ -38262,13 +44241,13 @@ static int unixOpen(
struct statfs fsInfo;
#endif
- /* If creating a master or main-file journal, this function will open
+ /* If creating a super- or main-file journal, this function will open
** a file-descriptor on the directory too. The first time unixSync()
** is called the directory file descriptor will be fsync()ed and close()d.
*/
int isNewJrnl = (isCreate && (
- eType==SQLITE_OPEN_MASTER_JOURNAL
- || eType==SQLITE_OPEN_MAIN_JOURNAL
+ eType==SQLITE_OPEN_SUPER_JOURNAL
+ || eType==SQLITE_OPEN_MAIN_JOURNAL
|| eType==SQLITE_OPEN_WAL
));
@@ -38278,9 +44257,9 @@ static int unixOpen(
char zTmpname[MAX_PATHNAME+2];
const char *zName = zPath;
- /* Check the following statements are true:
+ /* Check the following statements are true:
**
- ** (a) Exactly one of the READWRITE and READONLY flags must be set, and
+ ** (a) Exactly one of the READWRITE and READONLY flags must be set, and
** (b) if CREATE is set, then READWRITE must also be set, and
** (c) if EXCLUSIVE is set, then CREATE must also be set.
** (d) if DELETEONCLOSE is set, then CREATE must also be set.
@@ -38290,17 +44269,17 @@ static int unixOpen(
assert(isExclusive==0 || isCreate);
assert(isDelete==0 || isCreate);
- /* The main DB, main journal, WAL file and master journal are never
+ /* The main DB, main journal, WAL file and super-journal are never
** automatically deleted. Nor are they ever temporary files. */
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
+ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_SUPER_JOURNAL );
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
/* Assert that the upper layer has set one of the "file-type" flags. */
- assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
- || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
- || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
+ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
+ || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
+ || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
);
@@ -38315,6 +44294,11 @@ static int unixOpen(
}
memset(p, 0, sizeof(unixFile));
+#ifdef SQLITE_ASSERT_NO_FILES
+ /* Applications that never read or write a persistent disk files */
+ assert( zName==0 );
+#endif
+
if( eType==SQLITE_OPEN_MAIN_DB ){
UnixUnusedFd *pUnused;
pUnused = findReusableFd(zName, flags);
@@ -38349,13 +44333,13 @@ static int unixOpen(
/* Determine the value of the flags parameter passed to POSIX function
** open(). These must be calculated even if open() is not called, as
- ** they may be stored as part of the file handle and used by the
+ ** they may be stored as part of the file handle and used by the
** 'conch file' locking functions later on. */
if( isReadonly ) openFlags |= O_RDONLY;
if( isReadWrite ) openFlags |= O_RDWR;
if( isCreate ) openFlags |= O_CREAT;
if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW);
- openFlags |= (O_LARGEFILE|O_BINARY);
+ openFlags |= (O_LARGEFILE|O_BINARY|O_NOFOLLOW);
if( fd<0 ){
mode_t openMode; /* Permissions to create file with */
@@ -38391,11 +44375,19 @@ static int unixOpen(
goto open_finished;
}
- /* If this process is running as root and if creating a new rollback
- ** journal or WAL file, set the ownership of the journal or WAL to be
- ** the same as the original database.
+ /* The owner of the rollback journal or WAL file should always be the
+ ** same as the owner of the database file. Try to ensure that this is
+ ** the case. The chown() system call will be a no-op if the current
+ ** process lacks root privileges, be we should at least try. Without
+ ** this step, if a root process opens a database file, it can leave
+ ** behinds a journal/WAL that is owned by root and hence make the
+ ** database inaccessible to unprivileged processes.
+ **
+ ** If openMode==0, then that means uid and gid are not set correctly
+ ** (probably because SQLite is configured to use 8+3 filename mode) and
+ ** in that case we do not want to attempt the chown().
*/
- if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
+ if( openMode && (flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){
robustFchown(fd, uid, gid);
}
}
@@ -38406,7 +44398,8 @@ static int unixOpen(
if( p->pPreallocatedUnused ){
p->pPreallocatedUnused->fd = fd;
- p->pPreallocatedUnused->flags = flags;
+ p->pPreallocatedUnused->flags =
+ flags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE);
}
if( isDelete ){
@@ -38427,7 +44420,7 @@ static int unixOpen(
p->openFlags = openFlags;
}
#endif
-
+
#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
if( fstatfs(fd, &fsInfo) == -1 ){
storeLastErrno(p, errno);
@@ -38458,7 +44451,7 @@ static int unixOpen(
char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING");
int useProxy = 0;
- /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means
+ /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means
** never use proxy, NULL means use proxy for non-local files only. */
if( envforce!=NULL ){
useProxy = atoi(envforce)>0;
@@ -38470,9 +44463,9 @@ static int unixOpen(
if( rc==SQLITE_OK ){
rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:");
if( rc!=SQLITE_OK ){
- /* Use unixClose to clean up the resources added in fillInUnixFile
- ** and clear all the structure's references. Specifically,
- ** pFile->pMethods will be NULL so sqlite3OsClose will be a no-op
+ /* Use unixClose to clean up the resources added in fillInUnixFile
+ ** and clear all the structure's references. Specifically,
+ ** pFile->pMethods will be NULL so sqlite3OsClose will be a no-op
*/
unixClose(pFile);
return rc;
@@ -38482,9 +44475,9 @@ static int unixOpen(
}
}
#endif
-
- assert( zPath==0 || zPath[0]=='/'
- || eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL
+
+ assert( zPath==0 || zPath[0]=='/'
+ || eType==SQLITE_OPEN_SUPER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL
);
rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
@@ -38564,7 +44557,8 @@ static int unixAccess(
if( flags==SQLITE_ACCESS_EXISTS ){
struct stat buf;
- *pResOut = (0==osStat(zPath, &buf) && buf.st_size>0);
+ *pResOut = 0==osStat(zPath, &buf) &&
+ (!S_ISREG(buf.st_mode) || buf.st_size>0);
}else{
*pResOut = osAccess(zPath, W_OK|R_OK)==0;
}
@@ -38572,38 +44566,105 @@ static int unixAccess(
}
/*
-**
+** A pathname under construction
+*/
+typedef struct DbPath DbPath;
+struct DbPath {
+ int rc; /* Non-zero following any error */
+ int nSymlink; /* Number of symlinks resolved */
+ char *zOut; /* Write the pathname here */
+ int nOut; /* Bytes of space available to zOut[] */
+ int nUsed; /* Bytes of zOut[] currently being used */
+};
+
+/* Forward reference */
+static void appendAllPathElements(DbPath*,const char*);
+
+/*
+** Append a single path element to the DbPath under construction
*/
-static int mkFullPathname(
- const char *zPath, /* Input path */
- char *zOut, /* Output buffer */
- int nOut /* Allocated size of buffer zOut */
+static void appendOnePathElement(
+ DbPath *pPath, /* Path under construction, to which to append zName */
+ const char *zName, /* Name to append to pPath. Not zero-terminated */
+ int nName /* Number of significant bytes in zName */
){
- int nPath = sqlite3Strlen30(zPath);
- int iOff = 0;
- if( zPath[0]!='/' ){
- if( osGetcwd(zOut, nOut-2)==0 ){
- return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
+ assert( nName>0 );
+ assert( zName!=0 );
+ if( zName[0]=='.' ){
+ if( nName==1 ) return;
+ if( zName[1]=='.' && nName==2 ){
+ if( pPath->nUsed>1 ){
+ assert( pPath->zOut[0]=='/' );
+ while( pPath->zOut[--pPath->nUsed]!='/' ){}
+ }
+ return;
}
- iOff = sqlite3Strlen30(zOut);
- zOut[iOff++] = '/';
}
- if( (iOff+nPath+1)>nOut ){
- /* SQLite assumes that xFullPathname() nul-terminates the output buffer
- ** even if it returns an error. */
- zOut[iOff] = '\0';
- return SQLITE_CANTOPEN_BKPT;
+ if( pPath->nUsed + nName + 2 >= pPath->nOut ){
+ pPath->rc = SQLITE_ERROR;
+ return;
}
- sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
- return SQLITE_OK;
+ pPath->zOut[pPath->nUsed++] = '/';
+ memcpy(&pPath->zOut[pPath->nUsed], zName, nName);
+ pPath->nUsed += nName;
+#if defined(HAVE_READLINK) && defined(HAVE_LSTAT)
+ if( pPath->rc==SQLITE_OK ){
+ const char *zIn;
+ struct stat buf;
+ pPath->zOut[pPath->nUsed] = 0;
+ zIn = pPath->zOut;
+ if( osLstat(zIn, &buf)!=0 ){
+ if( errno!=ENOENT ){
+ pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn);
+ }
+ }else if( S_ISLNK(buf.st_mode) ){
+ ssize_t got;
+ char zLnk[SQLITE_MAX_PATHLEN+2];
+ if( pPath->nSymlink++ > SQLITE_MAX_SYMLINK ){
+ pPath->rc = SQLITE_CANTOPEN_BKPT;
+ return;
+ }
+ got = osReadlink(zIn, zLnk, sizeof(zLnk)-2);
+ if( got<=0 || got>=(ssize_t)sizeof(zLnk)-2 ){
+ pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn);
+ return;
+ }
+ zLnk[got] = 0;
+ if( zLnk[0]=='/' ){
+ pPath->nUsed = 0;
+ }else{
+ pPath->nUsed -= nName + 1;
+ }
+ appendAllPathElements(pPath, zLnk);
+ }
+ }
+#endif
+}
+
+/*
+** Append all path elements in zPath to the DbPath under construction.
+*/
+static void appendAllPathElements(
+ DbPath *pPath, /* Path under construction, to which to append zName */
+ const char *zPath /* Path to append to pPath. Is zero-terminated */
+){
+ int i = 0;
+ int j = 0;
+ do{
+ while( zPath[i] && zPath[i]!='/' ){ i++; }
+ if( i>j ){
+ appendOnePathElement(pPath, &zPath[j], i-j);
+ }
+ j = i+1;
+ }while( zPath[i++] );
}
/*
** Turn a relative pathname into a full pathname. The relative path
** is stored as a nul-terminated string in the buffer pointed to by
-** zPath.
+** zPath.
**
-** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes
+** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes
** (in this case, MAX_PATHNAME bytes). The full-path is written to
** this buffer before returning.
*/
@@ -38613,84 +44674,27 @@ static int unixFullPathname(
int nOut, /* Size of output buffer in bytes */
char *zOut /* Output buffer */
){
-#if !defined(HAVE_READLINK) || !defined(HAVE_LSTAT)
- return mkFullPathname(zPath, zOut, nOut);
-#else
- int rc = SQLITE_OK;
- int nByte;
- int nLink = 1; /* Number of symbolic links followed so far */
- const char *zIn = zPath; /* Input path for each iteration of loop */
- char *zDel = 0;
-
- assert( pVfs->mxPathname==MAX_PATHNAME );
+ DbPath path;
UNUSED_PARAMETER(pVfs);
-
- /* It's odd to simulate an io-error here, but really this is just
- ** using the io-error infrastructure to test that SQLite handles this
- ** function failing. This function could fail if, for example, the
- ** current working directory has been unlinked.
- */
- SimulateIOError( return SQLITE_ERROR );
-
- do {
-
- /* Call stat() on path zIn. Set bLink to true if the path is a symbolic
- ** link, or false otherwise. */
- int bLink = 0;
- struct stat buf;
- if( osLstat(zIn, &buf)!=0 ){
- if( errno!=ENOENT ){
- rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn);
- }
- }else{
- bLink = S_ISLNK(buf.st_mode);
- }
-
- if( bLink ){
- if( zDel==0 ){
- zDel = sqlite3_malloc(nOut);
- if( zDel==0 ) rc = SQLITE_NOMEM_BKPT;
- }else if( ++nLink>SQLITE_MAX_SYMLINKS ){
- rc = SQLITE_CANTOPEN_BKPT;
- }
-
- if( rc==SQLITE_OK ){
- nByte = osReadlink(zIn, zDel, nOut-1);
- if( nByte<0 ){
- rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn);
- }else{
- if( zDel[0]!='/' ){
- int n;
- for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--);
- if( nByte+n+1>nOut ){
- rc = SQLITE_CANTOPEN_BKPT;
- }else{
- memmove(&zDel[n], zDel, nByte+1);
- memcpy(zDel, zIn, n);
- nByte += n;
- }
- }
- zDel[nByte] = '\0';
- }
- }
-
- zIn = zDel;
- }
-
- assert( rc!=SQLITE_OK || zIn!=zOut || zIn[0]=='/' );
- if( rc==SQLITE_OK && zIn!=zOut ){
- rc = mkFullPathname(zIn, zOut, nOut);
+ path.rc = 0;
+ path.nUsed = 0;
+ path.nSymlink = 0;
+ path.nOut = nOut;
+ path.zOut = zOut;
+ if( zPath[0]!='/' ){
+ char zPwd[SQLITE_MAX_PATHLEN+2];
+ if( osGetcwd(zPwd, sizeof(zPwd)-2)==0 ){
+ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
}
- if( bLink==0 ) break;
- zIn = zOut;
- }while( rc==SQLITE_OK );
-
- sqlite3_free(zDel);
- return rc;
-#endif /* HAVE_READLINK && HAVE_LSTAT */
+ appendAllPathElements(&path, zPwd);
+ }
+ appendAllPathElements(&path, zPath);
+ zOut[path.nUsed] = 0;
+ if( path.rc || path.nUsed<2 ) return SQLITE_CANTOPEN_BKPT;
+ if( path.nSymlink ) return SQLITE_OK_SYMLINK;
+ return SQLITE_OK;
}
-
#ifndef SQLITE_OMIT_LOAD_EXTENSION
/*
** Interfaces for opening a shared library, finding entry points
@@ -38720,7 +44724,7 @@ static void unixDlError(sqlite3_vfs *NotUsed, int nBuf, char *zBufOut){
unixLeaveMutex();
}
static void (*unixDlSym(sqlite3_vfs *NotUsed, void *p, const char*zSym))(void){
- /*
+ /*
** GCC with -pedantic-errors says that C90 does not allow a void* to be
** cast into a pointer to a function. And yet the library dlsym() routine
** returns a void* which is really a pointer to a function. So how do we
@@ -38730,7 +44734,7 @@ static void (*unixDlSym(sqlite3_vfs *NotUsed, void *p, const char*zSym))(void){
** parameters void* and const char* and returning a pointer to a function.
** We initialize x by assigning it a pointer to the dlsym() function.
** (That assignment requires a cast.) Then we call the function that
- ** x points to.
+ ** x points to.
**
** This work-around is unlikely to work correctly on any system where
** you really cannot cast a function pointer into void*. But then, on the
@@ -38773,7 +44777,7 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
** tests repeatable.
*/
memset(zBuf, 0, nBuf);
- randomnessPid = osGetpid(0);
+ randomnessPid = osGetpid(0);
#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS)
{
int fd, got;
@@ -38804,16 +44808,22 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
** than the argument.
*/
static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){
-#if OS_VXWORKS
+#if !defined(HAVE_NANOSLEEP) || HAVE_NANOSLEEP+0
struct timespec sp;
-
sp.tv_sec = microseconds / 1000000;
sp.tv_nsec = (microseconds % 1000000) * 1000;
+
+ /* Almost all modern unix systems support nanosleep(). But if you are
+ ** compiling for one of the rare exceptions, you can use
+ ** -DHAVE_NANOSLEEP=0 (perhaps in conjuction with -DHAVE_USLEEP if
+ ** usleep() is available) in order to bypass the use of nanosleep() */
nanosleep(&sp, NULL);
+
UNUSED_PARAMETER(NotUsed);
return microseconds;
#elif defined(HAVE_USLEEP) && HAVE_USLEEP
- usleep(microseconds);
+ if( microseconds>=1000000 ) sleep(microseconds/1000000);
+ if( microseconds%1000000 ) usleep(microseconds%1000000);
UNUSED_PARAMETER(NotUsed);
return microseconds;
#else
@@ -38840,7 +44850,7 @@ SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1
** epoch of noon in Greenwich on November 24, 4714 B.C according to the
** proleptic Gregorian calendar.
**
-** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
+** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
** cannot be found.
*/
static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
@@ -38947,7 +44957,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
** To address the performance and cache coherency issues, proxy file locking
** changes the way database access is controlled by limiting access to a
** single host at a time and moving file locks off of the database file
-** and onto a proxy file on the local file system.
+** and onto a proxy file on the local file system.
**
**
** Using proxy locks
@@ -38973,19 +44983,19 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
** actual proxy file name is generated from the name and path of the
** database file. For example:
**
-** For database path "/Users/me/foo.db"
+** For database path "/Users/me/foo.db"
** The lock path will be "<tmpdir>/sqliteplocks/_Users_me_foo.db:auto:")
**
** Once a lock proxy is configured for a database connection, it can not
** be removed, however it may be switched to a different proxy path via
** the above APIs (assuming the conch file is not being held by another
-** connection or process).
+** connection or process).
**
**
** How proxy locking works
** -----------------------
**
-** Proxy file locking relies primarily on two new supporting files:
+** Proxy file locking relies primarily on two new supporting files:
**
** * conch file to limit access to the database file to a single host
** at a time
@@ -39012,11 +45022,11 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
** host (the conch ensures that they all use the same local lock file).
**
** Requesting the lock proxy does not immediately take the conch, it is
-** only taken when the first request to lock database file is made.
+** only taken when the first request to lock database file is made.
** This matches the semantics of the traditional locking behavior, where
** opening a connection to a database file does not take a lock on it.
-** The shared lock and an open file descriptor are maintained until
-** the connection to the database is closed.
+** The shared lock and an open file descriptor are maintained until
+** the connection to the database is closed.
**
** The proxy file and the lock file are never deleted so they only need
** to be created the first time they are used.
@@ -39030,7 +45040,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
** automatically configured for proxy locking, lock files are
** named automatically using the same logic as
** PRAGMA lock_proxy_file=":auto:"
-**
+**
** SQLITE_PROXY_DEBUG
**
** Enables the logging of error messages during host id file
@@ -39045,8 +45055,8 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
**
** Permissions to use when creating a directory for storing the
** lock proxy files, only used when LOCKPROXYDIR is not set.
-**
-**
+**
+**
** As mentioned above, when compiled with SQLITE_PREFER_PROXY_LOCKING,
** setting the environment variable SQLITE_FORCE_PROXY_LOCKING to 1 will
** force proxy locking to be used for every database file opened, and 0
@@ -39056,12 +45066,12 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
*/
/*
-** Proxy locking is only available on MacOSX
+** Proxy locking is only available on MacOSX
*/
#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
/*
-** The proxyLockingContext has the path and file structures for the remote
+** The proxyLockingContext has the path and file structures for the remote
** and local proxy files in it
*/
typedef struct proxyLockingContext proxyLockingContext;
@@ -39077,10 +45087,10 @@ struct proxyLockingContext {
sqlite3_io_methods const *pOldMethod; /* Original I/O methods for close */
};
-/*
-** The proxy lock file path for the database at dbPath is written into lPath,
+/*
+** The proxy lock file path for the database at dbPath is written into lPath,
** which must point to valid, writable memory large enough for a maxLen length
-** file path.
+** file path.
*/
static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
int len;
@@ -39097,7 +45107,7 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
lPath, errno, osGetpid(0)));
return SQLITE_IOERR_LOCK;
}
- len = strlcat(lPath, "sqliteplocks", maxLen);
+ len = strlcat(lPath, "sqliteplocks", maxLen);
}
# else
len = strlcpy(lPath, "/tmp/", maxLen);
@@ -39107,7 +45117,7 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
if( lPath[len-1]!='/' ){
len = strlcat(lPath, "/", maxLen);
}
-
+
/* transform the db path to a unique cache name */
dbLen = (int)strlen(dbPath);
for( i=0; i<dbLen && (i+len+7)<(int)maxLen; i++){
@@ -39120,14 +45130,14 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
return SQLITE_OK;
}
-/*
+/*
** Creates the lock file and any missing directories in lockPath
*/
static int proxyCreateLockPath(const char *lockPath){
int i, len;
char buf[MAXPATHLEN];
int start = 0;
-
+
assert(lockPath!=NULL);
/* try to create all the intermediate directories */
len = (int)strlen(lockPath);
@@ -39135,7 +45145,7 @@ static int proxyCreateLockPath(const char *lockPath){
for( i=1; i<len; i++ ){
if( lockPath[i] == '/' && (i - start > 0) ){
/* only mkdir if leaf dir != "." or "/" or ".." */
- if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/')
+ if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/')
|| (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){
buf[i]='\0';
if( osMkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
@@ -39171,7 +45181,7 @@ static int proxyCreateUnixFile(
int fd = -1;
unixFile *pNew;
int rc = SQLITE_OK;
- int openFlags = O_RDWR | O_CREAT;
+ int openFlags = O_RDWR | O_CREAT | O_NOFOLLOW;
sqlite3_vfs dummyVfs;
int terrno = 0;
UnixUnusedFd *pUnused = NULL;
@@ -39201,7 +45211,7 @@ static int proxyCreateUnixFile(
}
}
if( fd<0 ){
- openFlags = O_RDONLY;
+ openFlags = O_RDONLY | O_NOFOLLOW;
fd = robust_open(path, openFlags, 0);
terrno = errno;
}
@@ -39212,13 +45222,13 @@ static int proxyCreateUnixFile(
switch (terrno) {
case EACCES:
return SQLITE_PERM;
- case EIO:
+ case EIO:
return SQLITE_IOERR_LOCK; /* even though it is the conch */
default:
return SQLITE_CANTOPEN_BKPT;
}
}
-
+
pNew = (unixFile *)sqlite3_malloc64(sizeof(*pNew));
if( pNew==NULL ){
rc = SQLITE_NOMEM_BKPT;
@@ -39232,13 +45242,13 @@ static int proxyCreateUnixFile(
pUnused->fd = fd;
pUnused->flags = openFlags;
pNew->pPreallocatedUnused = pUnused;
-
+
rc = fillInUnixFile(&dummyVfs, fd, (sqlite3_file*)pNew, path, 0);
if( rc==SQLITE_OK ){
*ppFile = pNew;
return SQLITE_OK;
}
-end_create_proxy:
+end_create_proxy:
robust_close(pNew, fd, __LINE__);
sqlite3_free(pNew);
sqlite3_free(pUnused);
@@ -39252,18 +45262,18 @@ SQLITE_API int sqlite3_hostid_num = 0;
#define PROXY_HOSTIDLEN 16 /* conch file host id length */
-#ifdef HAVE_GETHOSTUUID
+#if HAVE_GETHOSTUUID
/* Not always defined in the headers as it ought to be */
extern int gethostuuid(uuid_t id, const struct timespec *wait);
#endif
-/* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN
+/* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN
** bytes of writable memory.
*/
static int proxyGetHostID(unsigned char *pHostID, int *pError){
assert(PROXY_HOSTIDLEN == sizeof(uuid_t));
memset(pHostID, 0, PROXY_HOSTIDLEN);
-#ifdef HAVE_GETHOSTUUID
+#if HAVE_GETHOSTUUID
{
struct timespec timeout = {1, 0}; /* 1 sec timeout */
if( gethostuuid(pHostID, &timeout) ){
@@ -39283,7 +45293,7 @@ static int proxyGetHostID(unsigned char *pHostID, int *pError){
pHostID[0] = (char)(pHostID[0] + (char)(sqlite3_hostid_num & 0xFF));
}
#endif
-
+
return SQLITE_OK;
}
@@ -39294,14 +45304,14 @@ static int proxyGetHostID(unsigned char *pHostID, int *pError){
#define PROXY_PATHINDEX (PROXY_HEADERLEN+PROXY_HOSTIDLEN)
#define PROXY_MAXCONCHLEN (PROXY_HEADERLEN+PROXY_HOSTIDLEN+MAXPATHLEN)
-/*
-** Takes an open conch file, copies the contents to a new path and then moves
+/*
+** Takes an open conch file, copies the contents to a new path and then moves
** it back. The newly created file's file descriptor is assigned to the
-** conch file structure and finally the original conch file descriptor is
+** conch file structure and finally the original conch file descriptor is
** closed. Returns zero if successful.
*/
static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
- proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
+ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
unixFile *conchFile = pCtx->conchFile;
char tPath[MAXPATHLEN];
char buf[PROXY_MAXCONCHLEN];
@@ -39315,7 +45325,7 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
/* create a new path by replace the trailing '-conch' with '-break' */
pathLen = strlcpy(tPath, cPath, MAXPATHLEN);
- if( pathLen>MAXPATHLEN || pathLen<6 ||
+ if( pathLen>MAXPATHLEN || pathLen<6 ||
(strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){
sqlite3_snprintf(sizeof(errmsg),errmsg,"path error (len %d)",(int)pathLen);
goto end_breaklock;
@@ -39327,7 +45337,7 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
goto end_breaklock;
}
/* write it out to the temporary break file */
- fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), 0);
+ fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW), 0);
if( fd<0 ){
sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno);
goto end_breaklock;
@@ -39357,24 +45367,24 @@ end_breaklock:
return rc;
}
-/* Take the requested lock on the conch file and break a stale lock if the
+/* Take the requested lock on the conch file and break a stale lock if the
** host id matches.
*/
static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
- proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
+ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
unixFile *conchFile = pCtx->conchFile;
int rc = SQLITE_OK;
int nTries = 0;
struct timespec conchModTime;
-
+
memset(&conchModTime, 0, sizeof(conchModTime));
do {
rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
nTries ++;
if( rc==SQLITE_BUSY ){
/* If the lock failed (busy):
- * 1st try: get the mod time of the conch, wait 0.5s and try again.
- * 2nd try: fail if the mod time changed or host id is different, wait
+ * 1st try: get the mod time of the conch, wait 0.5s and try again.
+ * 2nd try: fail if the mod time changed or host id is different, wait
* 10 sec and try again
* 3rd try: break the lock unless the mod time has changed.
*/
@@ -39383,20 +45393,20 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
storeLastErrno(pFile, errno);
return SQLITE_IOERR_LOCK;
}
-
+
if( nTries==1 ){
conchModTime = buf.st_mtimespec;
- usleep(500000); /* wait 0.5 sec and try the lock again*/
- continue;
+ unixSleep(0,500000); /* wait 0.5 sec and try the lock again*/
+ continue;
}
assert( nTries>1 );
- if( conchModTime.tv_sec != buf.st_mtimespec.tv_sec ||
+ if( conchModTime.tv_sec != buf.st_mtimespec.tv_sec ||
conchModTime.tv_nsec != buf.st_mtimespec.tv_nsec ){
return SQLITE_BUSY;
}
-
- if( nTries==2 ){
+
+ if( nTries==2 ){
char tBuf[PROXY_MAXCONCHLEN];
int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0);
if( len<0 ){
@@ -39412,10 +45422,10 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
/* don't break the lock on short read or a version mismatch */
return SQLITE_BUSY;
}
- usleep(10000000); /* wait 10 sec and try the lock again */
- continue;
+ unixSleep(0,10000000); /* wait 10 sec and try the lock again */
+ continue;
}
-
+
assert( nTries==3 );
if( 0==proxyBreakConchLock(pFile, myHostID) ){
rc = SQLITE_OK;
@@ -39428,19 +45438,19 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
}
}
} while( rc==SQLITE_BUSY && nTries<3 );
-
+
return rc;
}
-/* Takes the conch by taking a shared lock and read the contents conch, if
-** lockPath is non-NULL, the host ID and lock file path must match. A NULL
-** lockPath means that the lockPath in the conch file will be used if the
-** host IDs match, or a new lock path will be generated automatically
+/* Takes the conch by taking a shared lock and read the contents conch, if
+** lockPath is non-NULL, the host ID and lock file path must match. A NULL
+** lockPath means that the lockPath in the conch file will be used if the
+** host IDs match, or a new lock path will be generated automatically
** and written to the conch file.
*/
static int proxyTakeConch(unixFile *pFile){
- proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
-
+ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
+
if( pCtx->conchHeld!=0 ){
return SQLITE_OK;
}else{
@@ -39456,7 +45466,7 @@ static int proxyTakeConch(unixFile *pFile){
int readLen = 0;
int tryOldLockPath = 0;
int forceNewLockPath = 0;
-
+
OSTRACE(("TAKECONCH %d for %s pid=%d\n", conchFile->h,
(pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"),
osGetpid(0)));
@@ -39477,21 +45487,21 @@ static int proxyTakeConch(unixFile *pFile){
storeLastErrno(pFile, conchFile->lastErrno);
rc = SQLITE_IOERR_READ;
goto end_takeconch;
- }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) ||
+ }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) ||
readBuf[0]!=(char)PROXY_CONCHVERSION ){
- /* a short read or version format mismatch means we need to create a new
- ** conch file.
+ /* a short read or version format mismatch means we need to create a new
+ ** conch file.
*/
createConch = 1;
}
/* if the host id matches and the lock path already exists in the conch
- ** we'll try to use the path there, if we can't open that path, we'll
- ** retry with a new auto-generated path
+ ** we'll try to use the path there, if we can't open that path, we'll
+ ** retry with a new auto-generated path
*/
do { /* in case we need to try again for an :auto: named lock file */
if( !createConch && !forceNewLockPath ){
- hostIdMatch = !memcmp(&readBuf[PROXY_HEADERLEN], myHostID,
+ hostIdMatch = !memcmp(&readBuf[PROXY_HEADERLEN], myHostID,
PROXY_HOSTIDLEN);
/* if the conch has data compare the contents */
if( !pCtx->lockProxyPath ){
@@ -39500,7 +45510,7 @@ static int proxyTakeConch(unixFile *pFile){
*/
if( hostIdMatch ){
size_t pathLen = (readLen - PROXY_PATHINDEX);
-
+
if( pathLen>=MAXPATHLEN ){
pathLen=MAXPATHLEN-1;
}
@@ -39516,23 +45526,23 @@ static int proxyTakeConch(unixFile *pFile){
readLen-PROXY_PATHINDEX)
){
/* conch host and lock path match */
- goto end_takeconch;
+ goto end_takeconch;
}
}
-
+
/* if the conch isn't writable and doesn't match, we can't take it */
if( (conchFile->openFlags&O_RDWR) == 0 ){
rc = SQLITE_BUSY;
goto end_takeconch;
}
-
+
/* either the conch didn't match or we need to create a new one */
if( !pCtx->lockProxyPath ){
proxyGetLockPath(pCtx->dbPath, lockPath, MAXPATHLEN);
tempLockPath = lockPath;
/* create a copy of the lock path _only_ if the conch is taken */
}
-
+
/* update conch with host and path (this will fail if other process
** has a shared lock already), if the host id matches, use the big
** stick.
@@ -39543,7 +45553,7 @@ static int proxyTakeConch(unixFile *pFile){
/* We are trying for an exclusive lock but another thread in this
** same process is still holding a shared lock. */
rc = SQLITE_BUSY;
- } else {
+ } else {
rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK);
}
}else{
@@ -39552,7 +45562,7 @@ static int proxyTakeConch(unixFile *pFile){
if( rc==SQLITE_OK ){
char writeBuffer[PROXY_MAXCONCHLEN];
int writeSize = 0;
-
+
writeBuffer[0] = (char)PROXY_CONCHVERSION;
memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN);
if( pCtx->lockProxyPath!=NULL ){
@@ -39565,8 +45575,8 @@ static int proxyTakeConch(unixFile *pFile){
robust_ftruncate(conchFile->h, writeSize);
rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0);
full_fsync(conchFile->h,0,0);
- /* If we created a new conch file (not just updated the contents of a
- ** valid conch file), try to match the permissions of the database
+ /* If we created a new conch file (not just updated the contents of a
+ ** valid conch file), try to match the permissions of the database
*/
if( rc==SQLITE_OK && createConch ){
struct stat buf;
@@ -39590,14 +45600,14 @@ static int proxyTakeConch(unixFile *pFile){
}
}else{
int code = errno;
- fprintf(stderr, "STAT FAILED[%d] with %d %s\n",
+ fprintf(stderr, "STAT FAILED[%d] with %d %s\n",
err, code, strerror(code));
#endif
}
}
}
conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK);
-
+
end_takeconch:
OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h));
if( rc==SQLITE_OK && pFile->openFlags ){
@@ -39620,7 +45630,7 @@ static int proxyTakeConch(unixFile *pFile){
rc = proxyCreateUnixFile(path, &pCtx->lockProxy, 1);
if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && tryOldLockPath ){
/* we couldn't create the proxy lock file with the old lock file path
- ** so try again via auto-naming
+ ** so try again via auto-naming
*/
forceNewLockPath = 1;
tryOldLockPath = 0;
@@ -39640,7 +45650,7 @@ static int proxyTakeConch(unixFile *pFile){
}
if( rc==SQLITE_OK ){
pCtx->conchHeld = 1;
-
+
if( pCtx->lockProxy->pMethod == &afpIoMethods ){
afpLockingContext *afpCtx;
afpCtx = (afpLockingContext *)pCtx->lockProxy->lockingContext;
@@ -39652,7 +45662,7 @@ static int proxyTakeConch(unixFile *pFile){
OSTRACE(("TAKECONCH %d %s\n", conchFile->h,
rc==SQLITE_OK?"ok":"failed"));
return rc;
- } while (1); /* in case we need to retry the :auto: lock file -
+ } while (1); /* in case we need to retry the :auto: lock file -
** we should never get here except via the 'continue' call. */
}
}
@@ -39668,7 +45678,7 @@ static int proxyReleaseConch(unixFile *pFile){
pCtx = (proxyLockingContext *)pFile->lockingContext;
conchFile = pCtx->conchFile;
OSTRACE(("RELEASECONCH %d for %s pid=%d\n", conchFile->h,
- (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"),
+ (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"),
osGetpid(0)));
if( pCtx->conchHeld>0 ){
rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
@@ -39696,13 +45706,13 @@ static int proxyCreateConchPathname(char *dbPath, char **pConchPath){
char *conchPath; /* buffer in which to construct conch name */
/* Allocate space for the conch filename and initialize the name to
- ** the name of the original database file. */
+ ** the name of the original database file. */
*pConchPath = conchPath = (char *)sqlite3_malloc64(len + 8);
if( conchPath==0 ){
return SQLITE_NOMEM_BKPT;
}
memcpy(conchPath, dbPath, len+1);
-
+
/* now insert a "." before the last / character */
for( i=(len-1); i>=0; i-- ){
if( conchPath[i]=='/' ){
@@ -39725,7 +45735,7 @@ static int proxyCreateConchPathname(char *dbPath, char **pConchPath){
/* Takes a fully configured proxy locking-style unix file and switches
-** the local lock file path
+** the local lock file path
*/
static int switchLockProxyPath(unixFile *pFile, const char *path) {
proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext;
@@ -39734,7 +45744,7 @@ static int switchLockProxyPath(unixFile *pFile, const char *path) {
if( pFile->eFileLock!=NO_LOCK ){
return SQLITE_BUSY;
- }
+ }
/* nothing to do if the path is NULL, :auto: or matches the existing path */
if( !path || path[0]=='\0' || !strcmp(path, ":auto:") ||
@@ -39752,7 +45762,7 @@ static int switchLockProxyPath(unixFile *pFile, const char *path) {
sqlite3_free(oldPath);
pCtx->lockProxyPath = sqlite3DbStrDup(0, path);
}
-
+
return rc;
}
@@ -39766,7 +45776,7 @@ static int switchLockProxyPath(unixFile *pFile, const char *path) {
static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){
#if defined(__APPLE__)
if( pFile->pMethod == &afpIoMethods ){
- /* afp style keeps a reference to the db path in the filePath field
+ /* afp style keeps a reference to the db path in the filePath field
** of the struct */
assert( (int)strlen((char*)pFile->lockingContext)<=MAXPATHLEN );
strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath,
@@ -39787,9 +45797,9 @@ static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){
}
/*
-** Takes an already filled in unix file and alters it so all file locking
+** Takes an already filled in unix file and alters it so all file locking
** will be performed on the local proxy lock file. The following fields
-** are preserved in the locking context so that they can be restored and
+** are preserved in the locking context so that they can be restored and
** the unix structure properly cleaned up at close time:
** ->lockingContext
** ->pMethod
@@ -39799,7 +45809,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
char dbPath[MAXPATHLEN+1]; /* Name of the database file */
char *lockPath=NULL;
int rc = SQLITE_OK;
-
+
if( pFile->eFileLock!=NO_LOCK ){
return SQLITE_BUSY;
}
@@ -39809,7 +45819,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
}else{
lockPath=(char *)path;
}
-
+
OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h,
(lockPath ? lockPath : ":auto:"), osGetpid(0)));
@@ -39843,7 +45853,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
rc = SQLITE_OK;
}
}
- }
+ }
if( rc==SQLITE_OK && lockPath ){
pCtx->lockProxyPath = sqlite3DbStrDup(0, lockPath);
}
@@ -39855,7 +45865,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
}
}
if( rc==SQLITE_OK ){
- /* all memory is allocated, proxys are created and assigned,
+ /* all memory is allocated, proxys are created and assigned,
** switch the locking context and pMethod then return.
*/
pCtx->oldLockingContext = pFile->lockingContext;
@@ -39863,12 +45873,12 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
pCtx->pOldMethod = pFile->pMethod;
pFile->pMethod = &proxyIoMethods;
}else{
- if( pCtx->conchFile ){
+ if( pCtx->conchFile ){
pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile);
sqlite3_free(pCtx->conchFile);
}
sqlite3DbFree(0, pCtx->lockProxyPath);
- sqlite3_free(pCtx->conchFilePath);
+ sqlite3_free(pCtx->conchFilePath);
sqlite3_free(pCtx);
}
OSTRACE(("TRANSPROXY %d %s\n", pFile->h,
@@ -39906,7 +45916,7 @@ static int proxyFileControl(sqlite3_file *id, int op, void *pArg){
if( isProxyStyle ){
/* turn off proxy locking - not supported. If support is added for
** switching proxy locking mode off then it will need to fail if
- ** the journal mode is WAL mode.
+ ** the journal mode is WAL mode.
*/
rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/;
}else{
@@ -39916,9 +45926,9 @@ static int proxyFileControl(sqlite3_file *id, int op, void *pArg){
}else{
const char *proxyPath = (const char *)pArg;
if( isProxyStyle ){
- proxyLockingContext *pCtx =
+ proxyLockingContext *pCtx =
(proxyLockingContext*)pFile->lockingContext;
- if( !strcmp(pArg, ":auto:")
+ if( !strcmp(pArg, ":auto:")
|| (pCtx->lockProxyPath &&
!strncmp(pCtx->lockProxyPath, proxyPath, MAXPATHLEN))
){
@@ -39937,7 +45947,7 @@ static int proxyFileControl(sqlite3_file *id, int op, void *pArg){
assert( 0 ); /* The call assures that only valid opcodes are sent */
}
}
- /*NOTREACHED*/
+ /*NOTREACHED*/ assert(0);
return SQLITE_ERROR;
}
@@ -40043,7 +46053,7 @@ static int proxyClose(sqlite3_file *id) {
unixFile *lockProxy = pCtx->lockProxy;
unixFile *conchFile = pCtx->conchFile;
int rc = SQLITE_OK;
-
+
if( lockProxy ){
rc = lockProxy->pMethod->xUnlock((sqlite3_file*)lockProxy, NO_LOCK);
if( rc ) return rc;
@@ -40080,7 +46090,7 @@ static int proxyClose(sqlite3_file *id) {
** The proxy locking style is intended for use with AFP filesystems.
** And since AFP is only supported on MacOSX, the proxy locking is also
** restricted to MacOSX.
-**
+**
**
******************* End of the proxy lock implementation **********************
******************************************************************************/
@@ -40098,8 +46108,8 @@ static int proxyClose(sqlite3_file *id) {
** necessarily been initialized when this routine is called, and so they
** should not be used.
*/
-SQLITE_API int sqlite3_os_init(void){
- /*
+SQLITE_API int sqlite3_os_init(void){
+ /*
** The following macro defines an initializer for an sqlite3_vfs object.
** The name of the VFS is NAME. The pAppData is a pointer to a pointer
** to the "finder" function. (pAppData is a pointer to a pointer because
@@ -40115,7 +46125,7 @@ SQLITE_API int sqlite3_os_init(void){
**
** Most finders simply return a pointer to a fixed sqlite3_io_methods
** object. But the "autolockIoFinder" available on MacOSX does a little
- ** more than that; it looks at the filesystem type that hosts the
+ ** more than that; it looks at the filesystem type that hosts the
** database file and tries to choose an locking method appropriate for
** that filesystem time.
*/
@@ -40185,10 +46195,40 @@ SQLITE_API int sqlite3_os_init(void){
/* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
+#ifdef SQLITE_DEFAULT_UNIX_VFS
+ sqlite3_vfs_register(&aVfs[i],
+ 0==strcmp(aVfs[i].zName,SQLITE_DEFAULT_UNIX_VFS));
+#else
sqlite3_vfs_register(&aVfs[i], i==0);
+#endif
}
+#ifdef SQLITE_OS_KV_OPTIONAL
+ sqlite3KvvfsInit();
+#endif
unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
- return SQLITE_OK;
+
+#ifndef SQLITE_OMIT_WAL
+ /* Validate lock assumptions */
+ assert( SQLITE_SHM_NLOCK==8 ); /* Number of available locks */
+ assert( UNIX_SHM_BASE==120 ); /* Start of locking area */
+ /* Locks:
+ ** WRITE UNIX_SHM_BASE 120
+ ** CKPT UNIX_SHM_BASE+1 121
+ ** RECOVER UNIX_SHM_BASE+2 122
+ ** READ-0 UNIX_SHM_BASE+3 123
+ ** READ-1 UNIX_SHM_BASE+4 124
+ ** READ-2 UNIX_SHM_BASE+5 125
+ ** READ-3 UNIX_SHM_BASE+6 126
+ ** READ-4 UNIX_SHM_BASE+7 127
+ ** DMS UNIX_SHM_BASE+8 128
+ */
+ assert( UNIX_SHM_DMS==128 ); /* Byte offset of the deadman-switch */
+#endif
+
+ /* Initialize temp file dir array. */
+ unixTempFileInit();
+
+ return SQLITE_OK;
}
/*
@@ -40198,11 +46238,11 @@ SQLITE_API int sqlite3_os_init(void){
** to release dynamically allocated objects. But not on unix.
** This routine is a no-op for unix.
*/
-SQLITE_API int sqlite3_os_end(void){
+SQLITE_API int sqlite3_os_end(void){
unixBigLock = 0;
- return SQLITE_OK;
+ return SQLITE_OK;
}
-
+
#endif /* SQLITE_OS_UNIX */
/************** End of os_unix.c *********************************************/
@@ -40227,205 +46267,7 @@ SQLITE_API int sqlite3_os_end(void){
/*
** Include code that is common to all os_*.c files
*/
-/************** Include os_common.h in the middle of os_win.c ****************/
-/************** Begin file os_common.h ***************************************/
-/*
-** 2004 May 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 contains macros and a little bit of code that is common to
-** all of the platform-specific files (os_*.c) and is #included into those
-** files.
-**
-** This file should be #included by the os_*.c files only. It is not a
-** general purpose header file.
-*/
-#ifndef _OS_COMMON_H_
-#define _OS_COMMON_H_
-
-/*
-** At least two bugs have slipped in because we changed the MEMORY_DEBUG
-** macro to SQLITE_DEBUG and some older makefiles have not yet made the
-** switch. The following code should catch this problem at compile-time.
-*/
-#ifdef MEMORY_DEBUG
-# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
-#endif
-
-/*
-** Macros for performance tracing. Normally turned off. Only works
-** on i486 hardware.
-*/
-#ifdef SQLITE_PERFORMANCE_TRACE
-
-/*
-** hwtime.h contains inline assembler code for implementing
-** high-performance timing routines.
-*/
-/************** Include hwtime.h in the middle of os_common.h ****************/
-/************** Begin file hwtime.h ******************************************/
-/*
-** 2008 May 27
-**
-** 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 contains inline asm code for retrieving "high-performance"
-** counters for x86 class CPUs.
-*/
-#ifndef SQLITE_HWTIME_H
-#define SQLITE_HWTIME_H
-
-/*
-** The following routine only works on pentium-class (or newer) processors.
-** It uses the RDTSC opcode to read the cycle count value out of the
-** processor and returns that value. This can be used for high-res
-** profiling.
-*/
-#if (defined(__GNUC__) || defined(_MSC_VER)) && \
- (defined(i386) || defined(__i386__) || defined(_M_IX86))
-
- #if defined(__GNUC__)
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned int lo, hi;
- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
- return (sqlite_uint64)hi << 32 | lo;
- }
-
- #elif defined(_MSC_VER)
-
- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
- __asm {
- rdtsc
- ret ; return value at EDX:EAX
- }
- }
-
- #endif
-
-#elif (defined(__GNUC__) && defined(__x86_64__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long val;
- __asm__ __volatile__ ("rdtsc" : "=A" (val));
- return val;
- }
-
-#elif (defined(__GNUC__) && defined(__ppc__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long long retval;
- unsigned long junk;
- __asm__ __volatile__ ("\n\
- 1: mftbu %1\n\
- mftb %L0\n\
- mftbu %0\n\
- cmpw %0,%1\n\
- bne 1b"
- : "=r" (retval), "=r" (junk));
- return retval;
- }
-
-#else
-
- #error Need implementation of sqlite3Hwtime() for your platform.
-
- /*
- ** To compile without implementing sqlite3Hwtime() for your platform,
- ** you can remove the above #error and use the following
- ** stub function. You will lose timing support for many
- ** of the debugging and testing utilities, but it should at
- ** least compile and run.
- */
-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
-
-#endif
-
-#endif /* !defined(SQLITE_HWTIME_H) */
-
-/************** End of hwtime.h **********************************************/
-/************** Continuing where we left off in os_common.h ******************/
-
-static sqlite_uint64 g_start;
-static sqlite_uint64 g_elapsed;
-#define TIMER_START g_start=sqlite3Hwtime()
-#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
-#define TIMER_ELAPSED g_elapsed
-#else
-#define TIMER_START
-#define TIMER_END
-#define TIMER_ELAPSED ((sqlite_uint64)0)
-#endif
-
-/*
-** If we compile with the SQLITE_TEST macro set, then the following block
-** of code will give us the ability to simulate a disk I/O error. This
-** is used for testing the I/O recovery logic.
-*/
-#if defined(SQLITE_TEST)
-SQLITE_API extern int sqlite3_io_error_hit;
-SQLITE_API extern int sqlite3_io_error_hardhit;
-SQLITE_API extern int sqlite3_io_error_pending;
-SQLITE_API extern int sqlite3_io_error_persist;
-SQLITE_API extern int sqlite3_io_error_benign;
-SQLITE_API extern int sqlite3_diskfull_pending;
-SQLITE_API extern int sqlite3_diskfull;
-#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
-#define SimulateIOError(CODE) \
- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
- || sqlite3_io_error_pending-- == 1 ) \
- { local_ioerr(); CODE; }
-static void local_ioerr(){
- IOTRACE(("IOERR\n"));
- sqlite3_io_error_hit++;
- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
-}
-#define SimulateDiskfullError(CODE) \
- if( sqlite3_diskfull_pending ){ \
- if( sqlite3_diskfull_pending == 1 ){ \
- local_ioerr(); \
- sqlite3_diskfull = 1; \
- sqlite3_io_error_hit = 1; \
- CODE; \
- }else{ \
- sqlite3_diskfull_pending--; \
- } \
- }
-#else
-#define SimulateIOErrorBenign(X)
-#define SimulateIOError(A)
-#define SimulateDiskfullError(A)
-#endif /* defined(SQLITE_TEST) */
-
-/*
-** When testing, keep a count of the number of open files.
-*/
-#if defined(SQLITE_TEST)
-SQLITE_API extern int sqlite3_open_file_count;
-#define OpenCounter(X) sqlite3_open_file_count+=(X)
-#else
-#define OpenCounter(X)
-#endif /* defined(SQLITE_TEST) */
-
-#endif /* !defined(_OS_COMMON_H_) */
-
-/************** End of os_common.h *******************************************/
-/************** Continuing where we left off in os_win.c *********************/
+/* #include "os_common.h" */
/*
** Include the header file for the Windows VFS.
@@ -41566,7 +47408,7 @@ static struct win_syscall {
/*
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
-** "win32" VFSes. Return SQLITE_OK opon successfully updating the
+** "win32" VFSes. Return SQLITE_OK upon successfully updating the
** system call pointer, or SQLITE_NOTFOUND if there is no configurable
** system call named zName.
*/
@@ -41697,17 +47539,17 @@ SQLITE_API int sqlite3_win32_compact_heap(LPUINT pnLargest){
*/
SQLITE_API int sqlite3_win32_reset_heap(){
int rc;
- MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
+ MUTEX_LOGIC( sqlite3_mutex *pMainMtx; ) /* The main static mutex */
MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */
- MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
+ MUTEX_LOGIC( pMainMtx = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); )
MUTEX_LOGIC( pMem = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); )
- sqlite3_mutex_enter(pMaster);
+ sqlite3_mutex_enter(pMainMtx);
sqlite3_mutex_enter(pMem);
winMemAssertMagic();
if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlite3_memory_used()==0 ){
/*
** At this point, there should be no outstanding memory allocations on
- ** the heap. Also, since both the master and memsys locks are currently
+ ** the heap. Also, since both the main and memsys locks are currently
** being held by us, no other function (i.e. from another thread) should
** be able to even access the heap. Attempt to destroy and recreate our
** isolated Win32 native heap now.
@@ -41730,7 +47572,7 @@ SQLITE_API int sqlite3_win32_reset_heap(){
rc = SQLITE_BUSY;
}
sqlite3_mutex_leave(pMem);
- sqlite3_mutex_leave(pMaster);
+ sqlite3_mutex_leave(pMainMtx);
return rc;
}
#endif /* SQLITE_WIN32_MALLOC */
@@ -42325,10 +48167,12 @@ SQLITE_API int sqlite3_win32_set_directory8(
const char *zValue /* New value for directory being set or reset */
){
char **ppDirectory = 0;
+ int rc;
#ifndef SQLITE_OMIT_AUTOINIT
- int rc = sqlite3_initialize();
+ rc = sqlite3_initialize();
if( rc ) return rc;
#endif
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){
ppDirectory = &sqlite3_data_directory;
}else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){
@@ -42343,14 +48187,19 @@ SQLITE_API int sqlite3_win32_set_directory8(
if( zValue && zValue[0] ){
zCopy = sqlite3_mprintf("%s", zValue);
if ( zCopy==0 ){
- return SQLITE_NOMEM_BKPT;
+ rc = SQLITE_NOMEM_BKPT;
+ goto set_directory8_done;
}
}
sqlite3_free(*ppDirectory);
*ppDirectory = zCopy;
- return SQLITE_OK;
+ rc = SQLITE_OK;
+ }else{
+ rc = SQLITE_ERROR;
}
- return SQLITE_ERROR;
+set_directory8_done:
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
+ return rc;
}
/*
@@ -43139,7 +48988,7 @@ static int winRead(
pFile->h, pBuf, amt, offset, pFile->locktype));
#if SQLITE_MAX_MMAP_SIZE>0
- /* Deal with as much of this read request as possible by transfering
+ /* Deal with as much of this read request as possible by transferring
** data from the memory mapping using memcpy(). */
if( offset<pFile->mmapSize ){
if( offset+amt <= pFile->mmapSize ){
@@ -43217,7 +49066,7 @@ static int winWrite(
pFile->h, pBuf, amt, offset, pFile->locktype));
#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0
- /* Deal with as much of this write request as possible by transfering
+ /* Deal with as much of this write request as possible by transferring
** data from the memory mapping using memcpy(). */
if( offset<pFile->mmapSize ){
if( offset+amt <= pFile->mmapSize ){
@@ -43327,7 +49176,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
** all references to memory-mapped content are closed. That is doable,
** but involves adding a few branches in the common write code path which
** could slow down normal operations slightly. Hence, we have decided for
- ** now to simply make trancations a no-op if there are pending reads. We
+ ** now to simply make transactions a no-op if there are pending reads. We
** can maybe revisit this decision in the future.
*/
return SQLITE_OK;
@@ -43386,7 +49235,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
#ifdef SQLITE_TEST
/*
** Count the number of fullsyncs and normal syncs. This is used to test
-** that syncs and fullsyncs are occuring at the right times.
+** that syncs and fullsyncs are occurring at the right times.
*/
SQLITE_API int sqlite3_sync_count = 0;
SQLITE_API int sqlite3_fullsync_count = 0;
@@ -43743,7 +49592,7 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==EXCLUSIVE_LOCK && res ){
assert( pFile->locktype>=SHARED_LOCK );
- res = winUnlockReadLock(pFile);
+ (void)winUnlockReadLock(pFile);
res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0,
SHARED_SIZE, 0);
if( res ){
@@ -43909,6 +49758,7 @@ static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){
/* Forward references to VFS helper methods used for temporary files */
static int winGetTempname(sqlite3_vfs *, char **);
static int winIsDir(const void *);
+static BOOL winIsLongPathPrefix(const char *);
static BOOL winIsDriveLetterAndColon(const char *);
/*
@@ -44476,10 +50326,14 @@ static int winShmLock(
winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */
winShm *p = pDbFd->pShm; /* The shared memory being locked */
winShm *pX; /* For looping over all siblings */
- winShmNode *pShmNode = p->pShmNode;
+ winShmNode *pShmNode;
int rc = SQLITE_OK; /* Result code */
u16 mask; /* Mask of locks to take or release */
+ if( p==0 ) return SQLITE_IOERR_SHMLOCK;
+ pShmNode = p->pShmNode;
+ if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK;
+
assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
assert( n>=1 );
assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
@@ -44622,6 +50476,7 @@ static int winShmMap(
rc = winOpenSharedMemory(pDbFd);
if( rc!=SQLITE_OK ) return rc;
pShm = pDbFd->pShm;
+ assert( pShm!=0 );
}
pShmNode = pShm->pShmNode;
@@ -44915,6 +50770,11 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
+ /* Ensure that there is always at least a 256 byte buffer of addressable
+ ** memory following the returned page. If the database is corrupt,
+ ** SQLite may overread the page slightly (in practice only a few bytes,
+ ** but 256 is safe, round, number). */
+ const int nEofBuffer = 256;
if( pFd->pMapRegion==0 ){
int rc = winMapfile(pFd, -1);
if( rc!=SQLITE_OK ){
@@ -44923,7 +50783,8 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
return rc;
}
}
- if( pFd->mmapSize >= iOff+nAmt ){
+ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
+ assert( pFd->pMapRegion!=0 );
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
}
@@ -45118,6 +50979,19 @@ static int winMakeEndInDirSep(int nBuf, char *zBuf){
}
/*
+** If sqlite3_temp_directory is defined, take the mutex and return true.
+**
+** If sqlite3_temp_directory is NULL (undefined), omit the mutex and
+** return false.
+*/
+static int winTempDirDefined(void){
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
+ if( sqlite3_temp_directory!=0 ) return 1;
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
+ return 0;
+}
+
+/*
** Create a temporary file name and store the resulting pointer into pzBuf.
** The pointer returned in pzBuf must be freed via sqlite3_free().
*/
@@ -45127,6 +51001,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
size_t i, j;
+ DWORD pid;
int nPre = sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX);
int nMax, nBuf, nDir, nLen;
char *zBuf;
@@ -45153,20 +51028,23 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
*/
nDir = nMax - (nPre + 15);
assert( nDir>0 );
- if( sqlite3_temp_directory ){
+ if( winTempDirDefined() ){
int nDirLen = sqlite3Strlen30(sqlite3_temp_directory);
if( nDirLen>0 ){
if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){
nDirLen++;
}
if( nDirLen>nDir ){
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0);
}
sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory);
}
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
}
+
#if defined(__CYGWIN__)
else{
static const char *azDirs[] = {
@@ -45336,7 +51214,10 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
j = sqlite3Strlen30(zBuf);
sqlite3_randomness(15, &zBuf[j]);
+ pid = osGetCurrentProcessId();
for(i=0; i<15; i++, j++){
+ zBuf[j] += pid & 0xff;
+ pid >>= 8;
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
}
zBuf[j] = 0;
@@ -45427,7 +51308,7 @@ static int winOpen(
#ifndef NDEBUG
int isOpenJournal = (isCreate && (
- eType==SQLITE_OPEN_MASTER_JOURNAL
+ eType==SQLITE_OPEN_SUPER_JOURNAL
|| eType==SQLITE_OPEN_MAIN_JOURNAL
|| eType==SQLITE_OPEN_WAL
));
@@ -45448,17 +51329,17 @@ static int winOpen(
assert(isExclusive==0 || isCreate);
assert(isDelete==0 || isCreate);
- /* The main DB, main journal, WAL file and master journal are never
+ /* The main DB, main journal, WAL file and super-journal are never
** automatically deleted. Nor are they ever temporary files. */
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
+ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_SUPER_JOURNAL );
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
/* Assert that the upper layer has set one of the "file-type" flags. */
assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
|| eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
- || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
+ || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
);
@@ -45530,7 +51411,11 @@ static int winOpen(
dwCreationDisposition = OPEN_EXISTING;
}
- dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ if( 0==sqlite3_uri_boolean(zName, "exclusive", 0) ){
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ }else{
+ dwShareMode = 0;
+ }
if( isDelete ){
#if SQLITE_OS_WINCE
@@ -45570,7 +51455,7 @@ static int winOpen(
if( isReadWrite ){
int rc2, isRO = 0;
sqlite3BeginBenignMalloc();
- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
sqlite3EndBenignMalloc();
if( rc2==SQLITE_OK && isRO ) break;
}
@@ -45587,7 +51472,7 @@ static int winOpen(
if( isReadWrite ){
int rc2, isRO = 0;
sqlite3BeginBenignMalloc();
- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
sqlite3EndBenignMalloc();
if( rc2==SQLITE_OK && isRO ) break;
}
@@ -45607,7 +51492,7 @@ static int winOpen(
if( isReadWrite ){
int rc2, isRO = 0;
sqlite3BeginBenignMalloc();
- rc2 = winAccess(pVfs, zName, SQLITE_ACCESS_READ, &isRO);
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
sqlite3EndBenignMalloc();
if( rc2==SQLITE_OK && isRO ) break;
}
@@ -45670,13 +51555,15 @@ static int winOpen(
}
sqlite3_free(zTmpname);
- pFile->pMethod = pAppData ? pAppData->pMethod : &winIoMethod;
+ id->pMethods = pAppData ? pAppData->pMethod : &winIoMethod;
pFile->pVfs = pVfs;
pFile->h = h;
if( isReadonly ){
pFile->ctrlFlags |= WINFILE_RDONLY;
}
- if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){
+ if( (flags & SQLITE_OPEN_MAIN_DB)
+ && sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE)
+ ){
pFile->ctrlFlags |= WINFILE_PSOW;
}
pFile->lastErrno = NO_ERROR;
@@ -45828,6 +51715,13 @@ static int winAccess(
OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
zFilename, flags, pResOut));
+ if( zFilename==0 ){
+ *pResOut = 0;
+ OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
+ zFilename, pResOut, *pResOut));
+ return SQLITE_OK;
+ }
+
zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
@@ -45887,6 +51781,17 @@ static int winAccess(
}
/*
+** Returns non-zero if the specified path name starts with the "long path"
+** prefix.
+*/
+static BOOL winIsLongPathPrefix(
+ const char *zPathname
+){
+ return ( zPathname[0]=='\\' && zPathname[1]=='\\'
+ && zPathname[2]=='?' && zPathname[3]=='\\' );
+}
+
+/*
** Returns non-zero if the specified path name starts with a drive letter
** followed by a colon character.
*/
@@ -45938,7 +51843,7 @@ static BOOL winIsVerbatimPathname(
** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
** bytes in size.
*/
-static int winFullPathname(
+static int winFullPathnameNoMutex(
sqlite3_vfs *pVfs, /* Pointer to vfs object */
const char *zRelative, /* Possibly relative input path */
int nFull, /* Size of output buffer in bytes */
@@ -45950,10 +51855,11 @@ static int winFullPathname(
char *zOut;
#endif
- /* If this path name begins with "/X:", where "X" is any alphabetic
- ** character, discard the initial "/" from the pathname.
+ /* If this path name begins with "/X:" or "\\?\", where "X" is any
+ ** alphabetic character, discard the initial "/" from the pathname.
*/
- if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){
+ if( zRelative[0]=='/' && (winIsDriveLetterAndColon(zRelative+1)
+ || winIsLongPathPrefix(zRelative+1)) ){
zRelative++;
}
@@ -46116,6 +52022,20 @@ static int winFullPathname(
}
#endif
}
+static int winFullPathname(
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */
+ const char *zRelative, /* Possibly relative input path */
+ int nFull, /* Size of output buffer in bytes */
+ char *zFull /* Output buffer */
+){
+ int rc;
+ MUTEX_LOGIC( sqlite3_mutex *pMutex; )
+ MUTEX_LOGIC( pMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR); )
+ sqlite3_mutex_enter(pMutex);
+ rc = winFullPathnameNoMutex(pVfs, zRelative, nFull, zFull);
+ sqlite3_mutex_leave(pMutex);
+ return rc;
+}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
/*
@@ -46560,31 +52480,89 @@ SQLITE_API int sqlite3_os_end(void){
** sqlite3_deserialize().
*/
/* #include "sqliteInt.h" */
-#ifdef SQLITE_ENABLE_DESERIALIZE
+#ifndef SQLITE_OMIT_DESERIALIZE
/*
** Forward declaration of objects used by this utility
*/
typedef struct sqlite3_vfs MemVfs;
typedef struct MemFile MemFile;
+typedef struct MemStore MemStore;
/* Access to a lower-level VFS that (might) implement dynamic loading,
** access to randomness, etc.
*/
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
-/* An open file */
-struct MemFile {
- sqlite3_file base; /* IO methods */
+/* Storage for a memdb file.
+**
+** An memdb object can be shared or separate. Shared memdb objects can be
+** used by more than one database connection. Mutexes are used by shared
+** memdb objects to coordinate access. Separate memdb objects are only
+** connected to a single database connection and do not require additional
+** mutexes.
+**
+** Shared memdb objects have .zFName!=0 and .pMutex!=0. They are created
+** using "file:/name?vfs=memdb". The first character of the name must be
+** "/" or else the object will be a separate memdb object. All shared
+** memdb objects are stored in memdb_g.apMemStore[] in an arbitrary order.
+**
+** Separate memdb objects are created using a name that does not begin
+** with "/" or using sqlite3_deserialize().
+**
+** Access rules for shared MemStore objects:
+**
+** * .zFName is initialized when the object is created and afterwards
+** is unchanged until the object is destroyed. So it can be accessed
+** at any time as long as we know the object is not being destroyed,
+** which means while either the SQLITE_MUTEX_STATIC_VFS1 or
+** .pMutex is held or the object is not part of memdb_g.apMemStore[].
+**
+** * Can .pMutex can only be changed while holding the
+** SQLITE_MUTEX_STATIC_VFS1 mutex or while the object is not part
+** of memdb_g.apMemStore[].
+**
+** * Other fields can only be changed while holding the .pMutex mutex
+** or when the .nRef is less than zero and the object is not part of
+** memdb_g.apMemStore[].
+**
+** * The .aData pointer has the added requirement that it can can only
+** be changed (for resizing) when nMmap is zero.
+**
+*/
+struct MemStore {
sqlite3_int64 sz; /* Size of the file */
- sqlite3_int64 szMax; /* Space allocated to aData */
+ sqlite3_int64 szAlloc; /* Space allocated to aData */
+ sqlite3_int64 szMax; /* Maximum allowed size of the file */
unsigned char *aData; /* content of the file */
+ sqlite3_mutex *pMutex; /* Used by shared stores only */
int nMmap; /* Number of memory mapped pages */
unsigned mFlags; /* Flags */
+ int nRdLock; /* Number of readers */
+ int nWrLock; /* Number of writers. (Always 0 or 1) */
+ int nRef; /* Number of users of this MemStore */
+ char *zFName; /* The filename for shared stores */
+};
+
+/* An open file */
+struct MemFile {
+ sqlite3_file base; /* IO methods */
+ MemStore *pStore; /* The storage */
int eLock; /* Most recent lock against this file */
};
/*
+** File-scope variables for holding the memdb files that are accessible
+** to multiple database connections in separate threads.
+**
+** Must hold SQLITE_MUTEX_STATIC_VFS1 to access any part of this object.
+*/
+static struct MemFS {
+ int nMemStore; /* Number of shared MemStore objects */
+ MemStore **apMemStore; /* Array of all shared MemStore objects */
+} memdb_g;
+
+/*
** Methods for MemFile
*/
static int memdbClose(sqlite3_file*);
@@ -46594,6 +52572,7 @@ static int memdbTruncate(sqlite3_file*, sqlite3_int64 size);
static int memdbSync(sqlite3_file*, int flags);
static int memdbFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int memdbLock(sqlite3_file*, int);
+static int memdbUnlock(sqlite3_file*, int);
/* static int memdbCheckReservedLock(sqlite3_file*, int *pResOut);// not used */
static int memdbFileControl(sqlite3_file*, int op, void *pArg);
/* static int memdbSectorSize(sqlite3_file*); // not used */
@@ -46624,7 +52603,7 @@ static sqlite3_vfs memdb_vfs = {
1024, /* mxPathname */
0, /* pNext */
"memdb", /* zName */
- 0, /* pAppData (set when registered) */
+ 0, /* pAppData (set when registered) */
memdbOpen, /* xOpen */
0, /* memdbDelete, */ /* xDelete */
memdbAccess, /* xAccess */
@@ -46637,7 +52616,10 @@ static sqlite3_vfs memdb_vfs = {
memdbSleep, /* xSleep */
0, /* memdbCurrentTime, */ /* xCurrentTime */
memdbGetLastError, /* xGetLastError */
- memdbCurrentTimeInt64 /* xCurrentTimeInt64 */
+ memdbCurrentTimeInt64, /* xCurrentTimeInt64 */
+ 0, /* xSetSystemCall */
+ 0, /* xGetSystemCall */
+ 0, /* xNextSystemCall */
};
static const sqlite3_io_methods memdb_io_methods = {
@@ -46649,7 +52631,7 @@ static const sqlite3_io_methods memdb_io_methods = {
memdbSync, /* xSync */
memdbFileSize, /* xFileSize */
memdbLock, /* xLock */
- memdbLock, /* xUnlock - same as xLock in this case */
+ memdbUnlock, /* xUnlock */
0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */
memdbFileControl, /* xFileControl */
0, /* memdbSectorSize,*/ /* xSectorSize */
@@ -46662,17 +52644,68 @@ static const sqlite3_io_methods memdb_io_methods = {
memdbUnfetch /* xUnfetch */
};
+/*
+** Enter/leave the mutex on a MemStore
+*/
+#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
+static void memdbEnter(MemStore *p){
+ UNUSED_PARAMETER(p);
+}
+static void memdbLeave(MemStore *p){
+ UNUSED_PARAMETER(p);
+}
+#else
+static void memdbEnter(MemStore *p){
+ sqlite3_mutex_enter(p->pMutex);
+}
+static void memdbLeave(MemStore *p){
+ sqlite3_mutex_leave(p->pMutex);
+}
+#endif
+
/*
** Close an memdb-file.
-**
-** The pData pointer is owned by the application, so there is nothing
-** to free.
+** Free the underlying MemStore object when its refcount drops to zero
+** or less.
*/
static int memdbClose(sqlite3_file *pFile){
- MemFile *p = (MemFile *)pFile;
- if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ) sqlite3_free(p->aData);
+ MemStore *p = ((MemFile*)pFile)->pStore;
+ if( p->zFName ){
+ int i;
+#ifndef SQLITE_MUTEX_OMIT
+ sqlite3_mutex *pVfsMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
+#endif
+ sqlite3_mutex_enter(pVfsMutex);
+ for(i=0; ALWAYS(i<memdb_g.nMemStore); i++){
+ if( memdb_g.apMemStore[i]==p ){
+ memdbEnter(p);
+ if( p->nRef==1 ){
+ memdb_g.apMemStore[i] = memdb_g.apMemStore[--memdb_g.nMemStore];
+ if( memdb_g.nMemStore==0 ){
+ sqlite3_free(memdb_g.apMemStore);
+ memdb_g.apMemStore = 0;
+ }
+ }
+ break;
+ }
+ }
+ sqlite3_mutex_leave(pVfsMutex);
+ }else{
+ memdbEnter(p);
+ }
+ p->nRef--;
+ if( p->nRef<=0 ){
+ if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ){
+ sqlite3_free(p->aData);
+ }
+ memdbLeave(p);
+ sqlite3_mutex_free(p->pMutex);
+ sqlite3_free(p);
+ }else{
+ memdbLeave(p);
+ }
return SQLITE_OK;
}
@@ -46680,33 +52713,41 @@ static int memdbClose(sqlite3_file *pFile){
** Read data from an memdb-file.
*/
static int memdbRead(
- sqlite3_file *pFile,
- void *zBuf,
- int iAmt,
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
sqlite_int64 iOfst
){
- MemFile *p = (MemFile *)pFile;
+ MemStore *p = ((MemFile*)pFile)->pStore;
+ memdbEnter(p);
if( iOfst+iAmt>p->sz ){
memset(zBuf, 0, iAmt);
if( iOfst<p->sz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst);
+ memdbLeave(p);
return SQLITE_IOERR_SHORT_READ;
}
memcpy(zBuf, p->aData+iOfst, iAmt);
+ memdbLeave(p);
return SQLITE_OK;
}
/*
** Try to enlarge the memory allocation to hold at least sz bytes
*/
-static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){
+static int memdbEnlarge(MemStore *p, sqlite3_int64 newSz){
unsigned char *pNew;
- if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || p->nMmap>0 ){
+ if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || NEVER(p->nMmap>0) ){
return SQLITE_FULL;
}
- pNew = sqlite3_realloc64(p->aData, newSz);
- if( pNew==0 ) return SQLITE_NOMEM;
+ if( newSz>p->szMax ){
+ return SQLITE_FULL;
+ }
+ newSz *= 2;
+ if( newSz>p->szMax ) newSz = p->szMax;
+ pNew = sqlite3Realloc(p->aData, newSz);
+ if( pNew==0 ) return SQLITE_IOERR_NOMEM;
p->aData = pNew;
- p->szMax = newSz;
+ p->szAlloc = newSz;
return SQLITE_OK;
}
@@ -46719,18 +52760,27 @@ static int memdbWrite(
int iAmt,
sqlite_int64 iOfst
){
- MemFile *p = (MemFile *)pFile;
+ MemStore *p = ((MemFile*)pFile)->pStore;
+ memdbEnter(p);
+ if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ){
+ /* Can't happen: memdbLock() will return SQLITE_READONLY before
+ ** reaching this point */
+ memdbLeave(p);
+ return SQLITE_IOERR_WRITE;
+ }
if( iOfst+iAmt>p->sz ){
int rc;
- if( iOfst+iAmt>p->szMax
- && (rc = memdbEnlarge(p, (iOfst+iAmt)*2))!=SQLITE_OK
+ if( iOfst+iAmt>p->szAlloc
+ && (rc = memdbEnlarge(p, iOfst+iAmt))!=SQLITE_OK
){
+ memdbLeave(p);
return rc;
}
if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz);
p->sz = iOfst+iAmt;
}
memcpy(p->aData+iOfst, z, iAmt);
+ memdbLeave(p);
return SQLITE_OK;
}
@@ -46742,16 +52792,25 @@ static int memdbWrite(
** the size of a file, never to increase the size.
*/
static int memdbTruncate(sqlite3_file *pFile, sqlite_int64 size){
- MemFile *p = (MemFile *)pFile;
- if( NEVER(size>p->sz) ) return SQLITE_FULL;
- p->sz = size;
- return SQLITE_OK;
+ MemStore *p = ((MemFile*)pFile)->pStore;
+ int rc = SQLITE_OK;
+ memdbEnter(p);
+ if( size>p->sz ){
+ /* This can only happen with a corrupt wal mode db */
+ rc = SQLITE_CORRUPT;
+ }else{
+ p->sz = size;
+ }
+ memdbLeave(p);
+ return rc;
}
/*
** Sync an memdb-file.
*/
static int memdbSync(sqlite3_file *pFile, int flags){
+ UNUSED_PARAMETER(pFile);
+ UNUSED_PARAMETER(flags);
return SQLITE_OK;
}
@@ -46759,8 +52818,10 @@ static int memdbSync(sqlite3_file *pFile, int flags){
** Return the current file-size of an memdb-file.
*/
static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
- MemFile *p = (MemFile *)pFile;
+ MemStore *p = ((MemFile*)pFile)->pStore;
+ memdbEnter(p);
*pSize = p->sz;
+ memdbLeave(p);
return SQLITE_OK;
}
@@ -46768,14 +52829,90 @@ static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
** Lock an memdb-file.
*/
static int memdbLock(sqlite3_file *pFile, int eLock){
- MemFile *p = (MemFile *)pFile;
- p->eLock = eLock;
+ MemFile *pThis = (MemFile*)pFile;
+ MemStore *p = pThis->pStore;
+ int rc = SQLITE_OK;
+ if( eLock<=pThis->eLock ) return SQLITE_OK;
+ memdbEnter(p);
+
+ assert( p->nWrLock==0 || p->nWrLock==1 );
+ assert( pThis->eLock<=SQLITE_LOCK_SHARED || p->nWrLock==1 );
+ assert( pThis->eLock==SQLITE_LOCK_NONE || p->nRdLock>=1 );
+
+ if( eLock>SQLITE_LOCK_SHARED && (p->mFlags & SQLITE_DESERIALIZE_READONLY) ){
+ rc = SQLITE_READONLY;
+ }else{
+ switch( eLock ){
+ case SQLITE_LOCK_SHARED: {
+ assert( pThis->eLock==SQLITE_LOCK_NONE );
+ if( p->nWrLock>0 ){
+ rc = SQLITE_BUSY;
+ }else{
+ p->nRdLock++;
+ }
+ break;
+ };
+
+ case SQLITE_LOCK_RESERVED:
+ case SQLITE_LOCK_PENDING: {
+ assert( pThis->eLock>=SQLITE_LOCK_SHARED );
+ if( ALWAYS(pThis->eLock==SQLITE_LOCK_SHARED) ){
+ if( p->nWrLock>0 ){
+ rc = SQLITE_BUSY;
+ }else{
+ p->nWrLock = 1;
+ }
+ }
+ break;
+ }
+
+ default: {
+ assert( eLock==SQLITE_LOCK_EXCLUSIVE );
+ assert( pThis->eLock>=SQLITE_LOCK_SHARED );
+ if( p->nRdLock>1 ){
+ rc = SQLITE_BUSY;
+ }else if( pThis->eLock==SQLITE_LOCK_SHARED ){
+ p->nWrLock = 1;
+ }
+ break;
+ }
+ }
+ }
+ if( rc==SQLITE_OK ) pThis->eLock = eLock;
+ memdbLeave(p);
+ return rc;
+}
+
+/*
+** Unlock an memdb-file.
+*/
+static int memdbUnlock(sqlite3_file *pFile, int eLock){
+ MemFile *pThis = (MemFile*)pFile;
+ MemStore *p = pThis->pStore;
+ if( eLock>=pThis->eLock ) return SQLITE_OK;
+ memdbEnter(p);
+
+ assert( eLock==SQLITE_LOCK_SHARED || eLock==SQLITE_LOCK_NONE );
+ if( eLock==SQLITE_LOCK_SHARED ){
+ if( ALWAYS(pThis->eLock>SQLITE_LOCK_SHARED) ){
+ p->nWrLock--;
+ }
+ }else{
+ if( pThis->eLock>SQLITE_LOCK_SHARED ){
+ p->nWrLock--;
+ }
+ p->nRdLock--;
+ }
+
+ pThis->eLock = eLock;
+ memdbLeave(p);
return SQLITE_OK;
}
-#if 0 /* Never used because memdbAccess() always returns false */
+#if 0
/*
-** Check if another file-handle holds a RESERVED lock on an memdb-file.
+** This interface is only used for crash recovery, which does not
+** occur on an in-memory database.
*/
static int memdbCheckReservedLock(sqlite3_file *pFile, int *pResOut){
*pResOut = 0;
@@ -46783,16 +52920,32 @@ static int memdbCheckReservedLock(sqlite3_file *pFile, int *pResOut){
}
#endif
+
/*
** File control method. For custom operations on an memdb-file.
*/
static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){
- MemFile *p = (MemFile *)pFile;
+ MemStore *p = ((MemFile*)pFile)->pStore;
int rc = SQLITE_NOTFOUND;
+ memdbEnter(p);
if( op==SQLITE_FCNTL_VFSNAME ){
*(char**)pArg = sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz);
rc = SQLITE_OK;
}
+ if( op==SQLITE_FCNTL_SIZE_LIMIT ){
+ sqlite3_int64 iLimit = *(sqlite3_int64*)pArg;
+ if( iLimit<p->sz ){
+ if( iLimit<0 ){
+ iLimit = p->szMax;
+ }else{
+ iLimit = p->sz;
+ }
+ }
+ p->szMax = iLimit;
+ *(sqlite3_int64*)pArg = iLimit;
+ rc = SQLITE_OK;
+ }
+ memdbLeave(p);
return rc;
}
@@ -46809,7 +52962,8 @@ static int memdbSectorSize(sqlite3_file *pFile){
** Return the device characteristic flags supported by an memdb-file.
*/
static int memdbDeviceCharacteristics(sqlite3_file *pFile){
- return SQLITE_IOCAP_ATOMIC |
+ UNUSED_PARAMETER(pFile);
+ return SQLITE_IOCAP_ATOMIC |
SQLITE_IOCAP_POWERSAFE_OVERWRITE |
SQLITE_IOCAP_SAFE_APPEND |
SQLITE_IOCAP_SEQUENTIAL;
@@ -46822,16 +52976,26 @@ static int memdbFetch(
int iAmt,
void **pp
){
- MemFile *p = (MemFile *)pFile;
- p->nMmap++;
- *pp = (void*)(p->aData + iOfst);
+ MemStore *p = ((MemFile*)pFile)->pStore;
+ memdbEnter(p);
+ if( iOfst+iAmt>p->sz || (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)!=0 ){
+ *pp = 0;
+ }else{
+ p->nMmap++;
+ *pp = (void*)(p->aData + iOfst);
+ }
+ memdbLeave(p);
return SQLITE_OK;
}
/* Release a memory-mapped page */
static int memdbUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
- MemFile *p = (MemFile *)pFile;
+ MemStore *p = ((MemFile*)pFile)->pStore;
+ UNUSED_PARAMETER(iOfst);
+ UNUSED_PARAMETER(pPage);
+ memdbEnter(p);
p->nMmap--;
+ memdbLeave(p);
return SQLITE_OK;
}
@@ -46841,23 +53005,83 @@ static int memdbUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
static int memdbOpen(
sqlite3_vfs *pVfs,
const char *zName,
- sqlite3_file *pFile,
+ sqlite3_file *pFd,
int flags,
int *pOutFlags
){
- MemFile *p = (MemFile*)pFile;
- if( (flags & SQLITE_OPEN_MAIN_DB)==0 ){
- return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags);
+ MemFile *pFile = (MemFile*)pFd;
+ MemStore *p = 0;
+ int szName;
+ UNUSED_PARAMETER(pVfs);
+
+ memset(pFile, 0, sizeof(*pFile));
+ szName = sqlite3Strlen30(zName);
+ if( szName>1 && (zName[0]=='/' || zName[0]=='\\') ){
+ int i;
+#ifndef SQLITE_MUTEX_OMIT
+ sqlite3_mutex *pVfsMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
+#endif
+ sqlite3_mutex_enter(pVfsMutex);
+ for(i=0; i<memdb_g.nMemStore; i++){
+ if( strcmp(memdb_g.apMemStore[i]->zFName,zName)==0 ){
+ p = memdb_g.apMemStore[i];
+ break;
+ }
+ }
+ if( p==0 ){
+ MemStore **apNew;
+ p = sqlite3Malloc( sizeof(*p) + szName + 3 );
+ if( p==0 ){
+ sqlite3_mutex_leave(pVfsMutex);
+ return SQLITE_NOMEM;
+ }
+ apNew = sqlite3Realloc(memdb_g.apMemStore,
+ sizeof(apNew[0])*(memdb_g.nMemStore+1) );
+ if( apNew==0 ){
+ sqlite3_free(p);
+ sqlite3_mutex_leave(pVfsMutex);
+ return SQLITE_NOMEM;
+ }
+ apNew[memdb_g.nMemStore++] = p;
+ memdb_g.apMemStore = apNew;
+ memset(p, 0, sizeof(*p));
+ p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE|SQLITE_DESERIALIZE_FREEONCLOSE;
+ p->szMax = sqlite3GlobalConfig.mxMemdbSize;
+ p->zFName = (char*)&p[1];
+ memcpy(p->zFName, zName, szName+1);
+ p->pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ if( p->pMutex==0 ){
+ memdb_g.nMemStore--;
+ sqlite3_free(p);
+ sqlite3_mutex_leave(pVfsMutex);
+ return SQLITE_NOMEM;
+ }
+ p->nRef = 1;
+ memdbEnter(p);
+ }else{
+ memdbEnter(p);
+ p->nRef++;
+ }
+ sqlite3_mutex_leave(pVfsMutex);
+ }else{
+ p = sqlite3Malloc( sizeof(*p) );
+ if( p==0 ){
+ return SQLITE_NOMEM;
+ }
+ memset(p, 0, sizeof(*p));
+ p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE;
+ p->szMax = sqlite3GlobalConfig.mxMemdbSize;
}
- memset(p, 0, sizeof(*p));
- p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE;
- assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */
- *pOutFlags = flags | SQLITE_OPEN_MEMORY;
- p->base.pMethods = &memdb_io_methods;
+ pFile->pStore = p;
+ if( pOutFlags!=0 ){
+ *pOutFlags = flags | SQLITE_OPEN_MEMORY;
+ }
+ pFd->pMethods = &memdb_io_methods;
+ memdbLeave(p);
return SQLITE_OK;
}
-#if 0 /* Only used to delete rollback journals, master journals, and WAL
+#if 0 /* Only used to delete rollback journals, super-journals, and WAL
** files, none of which exist in memdb. So this routine is never used */
/*
** Delete the file located at zPath. If the dirSync argument is true,
@@ -46876,11 +53100,14 @@ static int memdbDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
** With memdb, no files ever exist on disk. So always return false.
*/
static int memdbAccess(
- sqlite3_vfs *pVfs,
- const char *zPath,
- int flags,
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
int *pResOut
){
+ UNUSED_PARAMETER(pVfs);
+ UNUSED_PARAMETER(zPath);
+ UNUSED_PARAMETER(flags);
*pResOut = 0;
return SQLITE_OK;
}
@@ -46891,11 +53118,12 @@ static int memdbAccess(
** of at least (INST_MAX_PATHNAME+1) bytes.
*/
static int memdbFullPathname(
- sqlite3_vfs *pVfs,
- const char *zPath,
- int nOut,
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
char *zOut
){
+ UNUSED_PARAMETER(pVfs);
sqlite3_snprintf(nOut, zOut, "%s", zPath);
return SQLITE_OK;
}
@@ -46909,7 +53137,7 @@ static void *memdbDlOpen(sqlite3_vfs *pVfs, const char *zPath){
/*
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
-** utf-8 string describing the most recent error encountered associated
+** utf-8 string describing the most recent error encountered associated
** with dynamic libraries.
*/
static void memdbDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
@@ -46931,7 +53159,7 @@ static void memdbDlClose(sqlite3_vfs *pVfs, void *pHandle){
}
/*
-** Populate the buffer pointed to by zBufOut with nByte bytes of
+** Populate the buffer pointed to by zBufOut with nByte bytes of
** random data.
*/
static int memdbRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
@@ -46939,7 +53167,7 @@ static int memdbRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
}
/*
-** Sleep for nMicro microseconds. Return the number of microseconds
+** Sleep for nMicro microseconds. Return the number of microseconds
** actually slept.
*/
static int memdbSleep(sqlite3_vfs *pVfs, int nMicro){
@@ -46968,9 +53196,14 @@ static int memdbCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
*/
static MemFile *memdbFromDbSchema(sqlite3 *db, const char *zSchema){
MemFile *p = 0;
+ MemStore *pStore;
int rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p);
if( rc ) return 0;
if( p->base.pMethods!=&memdb_io_methods ) return 0;
+ pStore = p->pStore;
+ memdbEnter(pStore);
+ if( pStore->zFName!=0 ) p = 0;
+ memdbLeave(pStore);
return p;
}
@@ -47006,12 +53239,14 @@ SQLITE_API unsigned char *sqlite3_serialize(
if( piSize ) *piSize = -1;
if( iDb<0 ) return 0;
if( p ){
- if( piSize ) *piSize = p->sz;
+ MemStore *pStore = p->pStore;
+ assert( pStore->pMutex==0 );
+ if( piSize ) *piSize = pStore->sz;
if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
- pOut = p->aData;
+ pOut = pStore->aData;
}else{
- pOut = sqlite3_malloc64( p->sz );
- if( pOut ) memcpy(pOut, p->aData, p->sz);
+ pOut = sqlite3_malloc64( pStore->sz );
+ if( pOut ) memcpy(pOut, pStore->aData, pStore->sz);
}
return pOut;
}
@@ -47045,7 +53280,7 @@ SQLITE_API unsigned char *sqlite3_serialize(
}else{
memset(pTo, 0, szPage);
}
- sqlite3PagerUnref(pPage);
+ sqlite3PagerUnref(pPage);
}
}
}
@@ -47081,13 +53316,18 @@ SQLITE_API int sqlite3_deserialize(
sqlite3_mutex_enter(db->mutex);
if( zSchema==0 ) zSchema = db->aDb[0].zDbSName;
iDb = sqlite3FindDbName(db, zSchema);
- if( iDb<0 ){
+ testcase( iDb==1 );
+ if( iDb<2 && iDb!=0 ){
rc = SQLITE_ERROR;
goto end_deserialize;
- }
+ }
zSql = sqlite3_mprintf("ATTACH x AS %Q", zSchema);
- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
- sqlite3_free(zSql);
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ }
if( rc ) goto end_deserialize;
db->init.iDb = (u8)iDb;
db->init.reopenMemdb = 1;
@@ -47101,35 +53341,54 @@ SQLITE_API int sqlite3_deserialize(
if( p==0 ){
rc = SQLITE_ERROR;
}else{
- p->aData = pData;
- p->sz = szDb;
- p->szMax = szBuf;
- p->mFlags = mFlags;
+ MemStore *pStore = p->pStore;
+ pStore->aData = pData;
+ pData = 0;
+ pStore->sz = szDb;
+ pStore->szAlloc = szBuf;
+ pStore->szMax = szBuf;
+ if( pStore->szMax<sqlite3GlobalConfig.mxMemdbSize ){
+ pStore->szMax = sqlite3GlobalConfig.mxMemdbSize;
+ }
+ pStore->mFlags = mFlags;
rc = SQLITE_OK;
}
end_deserialize:
sqlite3_finalize(pStmt);
+ if( pData && (mFlags & SQLITE_DESERIALIZE_FREEONCLOSE)!=0 ){
+ sqlite3_free(pData);
+ }
sqlite3_mutex_leave(db->mutex);
return rc;
}
-/*
+/*
+** Return true if the VFS is the memvfs.
+*/
+SQLITE_PRIVATE int sqlite3IsMemdb(const sqlite3_vfs *pVfs){
+ return pVfs==&memdb_vfs;
+}
+
+/*
** This routine is called when the extension is loaded.
** Register the new VFS.
*/
SQLITE_PRIVATE int sqlite3MemdbInit(void){
sqlite3_vfs *pLower = sqlite3_vfs_find(0);
- int sz = pLower->szOsFile;
+ unsigned int sz;
+ if( NEVER(pLower==0) ) return SQLITE_ERROR;
+ sz = pLower->szOsFile;
memdb_vfs.pAppData = pLower;
- /* In all known configurations of SQLite, the size of a default
- ** sqlite3_file is greater than the size of a memdb sqlite3_file.
- ** Should that ever change, remove the following NEVER() */
- if( NEVER(sz<sizeof(MemFile)) ) sz = sizeof(MemFile);
+ /* The following conditional can only be true when compiled for
+ ** Windows x86 and SQLITE_MAX_MMAP_SIZE=0. We always leave
+ ** it in, to be safe, but it is marked as NO_TEST since there
+ ** is no way to reach it under most builds. */
+ if( sz<sizeof(MemFile) ) sz = sizeof(MemFile); /*NO_TEST*/
memdb_vfs.szOsFile = sz;
return sqlite3_vfs_register(&memdb_vfs, 0);
}
-#endif /* SQLITE_ENABLE_DESERIALIZE */
+#endif /* SQLITE_OMIT_DESERIALIZE */
/************** End of memdb.c ***********************************************/
/************** Begin file bitvec.c ******************************************/
@@ -47152,8 +53411,8 @@ SQLITE_PRIVATE int sqlite3MemdbInit(void){
** property. Usually only a few pages are meet either condition.
** So the bitmap is usually sparse and has low cardinality.
** But sometimes (for example when during a DROP of a large table) most
-** or all of the pages in a database can get journalled. In those cases,
-** the bitmap becomes dense with high cardinality. The algorithm needs
+** or all of the pages in a database can get journalled. In those cases,
+** the bitmap becomes dense with high cardinality. The algorithm needs
** to handle both cases well.
**
** The size of the bitmap is fixed when the object is created.
@@ -47174,13 +53433,13 @@ SQLITE_PRIVATE int sqlite3MemdbInit(void){
/* Size of the Bitvec structure in bytes. */
#define BITVEC_SZ 512
-/* Round the union size down to the nearest pointer boundary, since that's how
+/* Round the union size down to the nearest pointer boundary, since that's how
** it will be aligned within the Bitvec struct. */
#define BITVEC_USIZE \
(((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
-/* Type of the array "element" for the bitmap representation.
-** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE.
+/* Type of the array "element" for the bitmap representation.
+** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE.
** Setting this to the "natural word" size of your CPU may improve
** performance. */
#define BITVEC_TELEM u8
@@ -47193,12 +53452,12 @@ SQLITE_PRIVATE int sqlite3MemdbInit(void){
/* Number of u32 values in hash table. */
#define BITVEC_NINT (BITVEC_USIZE/sizeof(u32))
-/* Maximum number of entries in hash table before
+/* Maximum number of entries in hash table before
** sub-dividing and re-hashing. */
#define BITVEC_MXHASH (BITVEC_NINT/2)
/* Hashing function for the aHash representation.
-** Empirical testing showed that the *37 multiplier
-** (an arbitrary prime)in the hash function provided
+** Empirical testing showed that the *37 multiplier
+** (an arbitrary prime)in the hash function provided
** no fewer collisions than the no-op *1. */
#define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT)
@@ -47244,7 +53503,7 @@ struct Bitvec {
/*
** Create a new bitmap object able to handle bits between 0 and iSize,
-** inclusive. Return a pointer to the new object. Return NULL if
+** inclusive. Return a pointer to the new object. Return NULL if
** malloc fails.
*/
SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32 iSize){
@@ -47323,7 +53582,7 @@ SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec *p, u32 i){
h = BITVEC_HASH(i++);
/* if there wasn't a hash collision, and this doesn't */
/* completely fill the hash, then just add it without */
- /* worring about sub-dividing and re-hashing. */
+ /* worrying about sub-dividing and re-hashing. */
if( !p->u.aHash[h] ){
if (p->nSet<(BITVEC_NINT-1)) {
goto bitvec_set_end;
@@ -47488,7 +53747,7 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){
sqlite3BitvecClear(0, 1, pTmpSpace);
/* Run the program */
- pc = 0;
+ pc = i = 0;
while( (op = aOp[pc])!=0 ){
switch( op ){
case 1:
@@ -47500,7 +53759,7 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){
break;
}
case 3:
- case 4:
+ case 4:
default: {
nx = 2;
sqlite3_randomness(sizeof(i), &i);
@@ -47580,7 +53839,7 @@ bitvec_end:
**
** The PCache.pSynced variable is used to optimize searching for a dirty
** page to eject from the cache mid-transaction. It is better to eject
-** a page that does not require a journal sync than one that does.
+** a page that does not require a journal sync than one that does.
** Therefore, pSynced is maintained so that it *almost* always points
** to either the oldest page in the pDirty/pDirtyTail list that has a
** clear PGHDR_NEED_SYNC flag or to a page that is older than this one
@@ -47590,7 +53849,7 @@ bitvec_end:
struct PCache {
PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
PgHdr *pSynced; /* Last synced page in dirty page list */
- int nRefSum; /* Sum of ref counts over all pages */
+ i64 nRefSum; /* Sum of ref counts over all pages */
int szCache; /* Configured cache size */
int szSpill; /* Size before spilling occurs */
int szPage; /* Size of every page in this cache */
@@ -47615,36 +53874,68 @@ struct PCache {
int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */
int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */
# define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;}
- void pcacheDump(PCache *pCache){
- int N;
- int i, j;
- sqlite3_pcache_page *pLower;
+ static void pcachePageTrace(int i, sqlite3_pcache_page *pLower){
PgHdr *pPg;
unsigned char *a;
-
+ int j;
+ if( pLower==0 ){
+ printf("%3d: NULL\n", i);
+ }else{
+ pPg = (PgHdr*)pLower->pExtra;
+ printf("%3d: nRef %2lld flgs %02x data ", i, pPg->nRef, pPg->flags);
+ a = (unsigned char *)pLower->pBuf;
+ for(j=0; j<12; j++) printf("%02x", a[j]);
+ printf(" ptr %p\n", pPg);
+ }
+ }
+ static void pcacheDump(PCache *pCache){
+ int N;
+ int i;
+ sqlite3_pcache_page *pLower;
+
if( sqlite3PcacheTrace<2 ) return;
if( pCache->pCache==0 ) return;
N = sqlite3PcachePagecount(pCache);
if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump;
for(i=1; i<=N; i++){
pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0);
- if( pLower==0 ) continue;
- pPg = (PgHdr*)pLower->pExtra;
- printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags);
- a = (unsigned char *)pLower->pBuf;
- for(j=0; j<12; j++) printf("%02x", a[j]);
- printf("\n");
- if( pPg->pPage==0 ){
+ pcachePageTrace(i, pLower);
+ if( pLower && ((PgHdr*)pLower)->pPage==0 ){
sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0);
}
}
}
- #else
+#else
# define pcacheTrace(X)
+# define pcachePageTrace(PGNO, X)
# define pcacheDump(X)
#endif
/*
+** Return 1 if pPg is on the dirty list for pCache. Return 0 if not.
+** This routine runs inside of assert() statements only.
+*/
+#if defined(SQLITE_ENABLE_EXPENSIVE_ASSERT)
+static int pageOnDirtyList(PCache *pCache, PgHdr *pPg){
+ PgHdr *p;
+ for(p=pCache->pDirty; p; p=p->pDirtyNext){
+ if( p==pPg ) return 1;
+ }
+ return 0;
+}
+static int pageNotOnDirtyList(PCache *pCache, PgHdr *pPg){
+ PgHdr *p;
+ for(p=pCache->pDirty; p; p=p->pDirtyNext){
+ if( p==pPg ) return 0;
+ }
+ return 1;
+}
+#else
+# define pageOnDirtyList(A,B) 1
+# define pageNotOnDirtyList(A,B) 1
+#endif
+
+/*
** Check invariants on a PgHdr entry. Return true if everything is OK.
** Return false if any invariant is violated.
**
@@ -47662,8 +53953,13 @@ SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr *pPg){
assert( pCache!=0 ); /* Every page has an associated PCache */
if( pPg->flags & PGHDR_CLEAN ){
assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */
- assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */
- assert( pCache->pDirtyTail!=pPg );
+ assert( pageNotOnDirtyList(pCache, pPg) );/* CLEAN pages not on dirtylist */
+ }else{
+ assert( (pPg->flags & PGHDR_DIRTY)!=0 );/* If not CLEAN must be DIRTY */
+ assert( pPg->pDirtyNext==0 || pPg->pDirtyNext->pDirtyPrev==pPg );
+ assert( pPg->pDirtyPrev==0 || pPg->pDirtyPrev->pDirtyNext==pPg );
+ assert( pPg->pDirtyPrev!=0 || pCache->pDirty==pPg );
+ assert( pageOnDirtyList(pCache, pPg) );
}
/* WRITEABLE pages must also be DIRTY */
if( pPg->flags & PGHDR_WRITEABLE ){
@@ -47713,12 +54009,12 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
if( addRemove & PCACHE_DIRTYLIST_REMOVE ){
assert( pPage->pDirtyNext || pPage==p->pDirtyTail );
assert( pPage->pDirtyPrev || pPage==p->pDirty );
-
+
/* Update the PCache1.pSynced variable if necessary. */
if( p->pSynced==pPage ){
p->pSynced = pPage->pDirtyPrev;
}
-
+
if( pPage->pDirtyNext ){
pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev;
}else{
@@ -47728,7 +54024,7 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
if( pPage->pDirtyPrev ){
pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext;
}else{
- /* If there are now no dirty pages in the cache, set eCreate to 2.
+ /* If there are now no dirty pages in the cache, set eCreate to 2.
** This is an optimization that allows sqlite3PcacheFetch() to skip
** searching for a dirty page to eject from the cache when it might
** otherwise have to. */
@@ -47757,11 +54053,11 @@ static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
p->pDirty = pPage;
/* If pSynced is NULL and this page has a clear NEED_SYNC flag, set
- ** pSynced to point to it. Checking the NEED_SYNC flag is an
+ ** pSynced to point to it. Checking the NEED_SYNC flag is an
** optimization, as if pSynced points to a page with the NEED_SYNC
- ** flag set sqlite3PcacheFetchStress() searches through all newer
+ ** flag set sqlite3PcacheFetchStress() searches through all newer
** entries of the dirty-list for a page with NEED_SYNC clear anyway. */
- if( !p->pSynced
+ if( !p->pSynced
&& 0==(pPage->flags&PGHDR_NEED_SYNC) /*OPTIMIZATION-IF-FALSE*/
){
p->pSynced = pPage;
@@ -47792,16 +54088,20 @@ static int numberOfCachePages(PCache *p){
** suggested cache size is set to N. */
return p->szCache;
}else{
- /* IMPLEMENTATION-OF: R-61436-13639 If the argument N is negative, then
- ** the number of cache pages is adjusted to use approximately abs(N*1024)
- ** bytes of memory. */
- return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra));
+ i64 n;
+ /* IMPLEMENTATION-OF: R-59858-46238 If the argument N is negative, then the
+ ** number of cache pages is adjusted to be a number of pages that would
+ ** use approximately abs(N*1024) bytes of memory based on the current
+ ** page size. */
+ n = ((-1024*(i64)p->szCache)/(p->szPage+p->szExtra));
+ if( n>1000000000 ) n = 1000000000;
+ return (int)n;
}
}
/*************************************************** General Interfaces ******
**
-** Initialize and shutdown the page cache subsystem. Neither of these
+** Initialize and shutdown the page cache subsystem. Neither of these
** functions are threadsafe.
*/
SQLITE_PRIVATE int sqlite3PcacheInitialize(void){
@@ -47810,6 +54110,7 @@ SQLITE_PRIVATE int sqlite3PcacheInitialize(void){
** built-in default page cache is used instead of the application defined
** page cache. */
sqlite3PCacheSetDefault();
+ assert( sqlite3GlobalConfig.pcache2.xInit!=0 );
}
return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg);
}
@@ -47827,8 +54128,8 @@ SQLITE_PRIVATE int sqlite3PcacheSize(void){ return sizeof(PCache); }
/*
** Create a new PCache object. Storage space to hold the object
-** has already been allocated and is passed in as the p pointer.
-** The caller discovers how much space needs to be allocated by
+** has already been allocated and is passed in as the p pointer.
+** The caller discovers how much space needs to be allocated by
** calling sqlite3PcacheSize().
**
** szExtra is some extra space allocated for each page. The first
@@ -47932,15 +54233,16 @@ SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch(
assert( createFlag==0 || pCache->eCreate==eCreate );
assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) );
pRes = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
- pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno,
+ pcacheTrace(("%p.FETCH %d%s (result: %p) ",pCache,pgno,
createFlag?" create":"",pRes));
+ pcachePageTrace(pgno, pRes);
return pRes;
}
/*
** If the sqlite3PcacheFetch() routine is unable to allocate a new
** page because no clean pages are available for reuse and the cache
-** size limit has been reached, then this routine can be invoked to
+** size limit has been reached, then this routine can be invoked to
** try harder to allocate a page. This routine might invoke the stress
** callback to spill dirty pages to the journal. It will then try to
** allocate the new page and will only fail to allocate a new page on
@@ -47957,17 +54259,17 @@ SQLITE_PRIVATE int sqlite3PcacheFetchStress(
if( pCache->eCreate==2 ) return 0;
if( sqlite3PcachePagecount(pCache)>pCache->szSpill ){
- /* Find a dirty page to write-out and recycle. First try to find a
+ /* Find a dirty page to write-out and recycle. First try to find a
** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
- ** cleared), but if that is not possible settle for any other
+ ** cleared), but if that is not possible settle for any other
** unreferenced dirty page.
**
** If the LRU page in the dirty list that has a clear PGHDR_NEED_SYNC
** flag is currently referenced, then the following may leave pSynced
** set incorrectly (pointing to other than the LRU page with NEED_SYNC
** cleared). This is Ok, as pSynced is just an optimization. */
- for(pPg=pCache->pSynced;
- pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
+ for(pPg=pCache->pSynced;
+ pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
pPg=pPg->pDirtyPrev
);
pCache->pSynced = pPg;
@@ -47977,7 +54279,7 @@ SQLITE_PRIVATE int sqlite3PcacheFetchStress(
if( pPg ){
int rc;
#ifdef SQLITE_LOG_CACHE_SPILL
- sqlite3_log(SQLITE_FULL,
+ sqlite3_log(SQLITE_FULL,
"spill page %d making room for %d - cache used: %d/%d",
pPg->pgno, pgno,
sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache),
@@ -48061,6 +54363,7 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
pcacheUnpin(p);
}else{
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
+ assert( sqlite3PcachePageSanity(p) );
}
}
}
@@ -48104,6 +54407,7 @@ SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){
pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno));
assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY );
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD);
+ assert( sqlite3PcachePageSanity(p) );
}
assert( sqlite3PcachePageSanity(p) );
}
@@ -48162,18 +54466,28 @@ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *pCache){
}
/*
-** Change the page number of page p to newPgno.
+** Change the page number of page p to newPgno.
*/
SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
PCache *pCache = p->pCache;
+ sqlite3_pcache_page *pOther;
assert( p->nRef>0 );
assert( newPgno>0 );
assert( sqlite3PcachePageSanity(p) );
pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno));
+ pOther = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, newPgno, 0);
+ if( pOther ){
+ PgHdr *pXPage = (PgHdr*)pOther->pExtra;
+ assert( pXPage->nRef==0 );
+ pXPage->nRef++;
+ pCache->nRefSum++;
+ sqlite3PcacheDrop(pXPage);
+ }
sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
p->pgno = newPgno;
if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
+ assert( sqlite3PcachePageSanity(p) );
}
}
@@ -48225,7 +54539,7 @@ SQLITE_PRIVATE void sqlite3PcacheClose(PCache *pCache){
sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
}
-/*
+/*
** Discard the contents of the cache.
*/
SQLITE_PRIVATE void sqlite3PcacheClear(PCache *pCache){
@@ -48263,7 +54577,7 @@ static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){
}
/*
-** Sort the list of pages in accending order by pgno. Pages are
+** Sort the list of pages in ascending order by pgno. Pages are
** connected by pDirty pointers. The pDirtyPrev pointers are
** corrupted by this sort.
**
@@ -48316,24 +54630,24 @@ SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache *pCache){
return pcacheSortDirtyList(pCache->pDirty);
}
-/*
+/*
** Return the total number of references to all pages held by the cache.
**
** This is not the total number of pages referenced, but the sum of the
** reference count for all pages.
*/
-SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache *pCache){
+SQLITE_PRIVATE i64 sqlite3PcacheRefCount(PCache *pCache){
return pCache->nRefSum;
}
/*
** Return the number of references to the page supplied as an argument.
*/
-SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr *p){
+SQLITE_PRIVATE i64 sqlite3PcachePageRefcount(PgHdr *p){
return p->nRef;
}
-/*
+/*
** Return the total number of pages in the cache.
*/
SQLITE_PRIVATE int sqlite3PcachePagecount(PCache *pCache){
@@ -48375,7 +54689,7 @@ SQLITE_PRIVATE int sqlite3PcacheSetSpillsize(PCache *p, int mxPage){
p->szSpill = mxPage;
}
res = numberOfCachePages(p);
- if( res<p->szSpill ) res = p->szSpill;
+ if( res<p->szSpill ) res = p->szSpill;
return res;
}
@@ -48406,7 +54720,7 @@ SQLITE_PRIVATE int sqlite3PCachePercentDirty(PCache *pCache){
}
#ifdef SQLITE_DIRECT_OVERFLOW_READ
-/*
+/*
** Return true if there are one or more dirty pages in the cache. Else false.
*/
SQLITE_PRIVATE int sqlite3PCacheIsDirty(PCache *pCache){
@@ -48471,12 +54785,13 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd
** size can vary according to architecture, compile-time options, and
** SQLite library version number.
**
-** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained
-** using a separate memory allocation from the database page content. This
-** seeks to overcome the "clownshoe" problem (also called "internal
-** fragmentation" in academic literature) of allocating a few bytes more
-** than a power of two with the memory allocator rounding up to the next
-** power of two, and leaving the rounded-up space unused.
+** Historical note: It used to be that if the SQLITE_PCACHE_SEPARATE_HEADER
+** was defined, then the page content would be held in a separate memory
+** allocation from the PgHdr1. This was intended to avoid clownshoe memory
+** allocations. However, the btree layer needs a small (16-byte) overrun
+** area after the page content buffer. The header serves as that overrun
+** area. Therefore SQLITE_PCACHE_SEPARATE_HEADER was discontinued to avoid
+** any possibility of a memory error.
**
** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates
** with this module. Information is passed back and forth as PgHdr1 pointers.
@@ -48495,14 +54810,14 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd
**
** The third case is a chunk of heap memory (defaulting to 100 pages worth)
** that is allocated when the page cache is created. The size of the local
-** bulk allocation can be adjusted using
+** bulk allocation can be adjusted using
**
** sqlite3_config(SQLITE_CONFIG_PAGECACHE, (void*)0, 0, N).
**
** If N is positive, then N pages worth of memory are allocated using a single
** sqlite3Malloc() call and that memory is used for the first N pages allocated.
** Or if N is negative, then -1024*N bytes of memory are allocated and used
-** for as many pages as can be accomodated.
+** for as many pages as can be accommodated.
**
** Only one of (2) or (3) can be used. Once the memory available to (2) or
** (3) is exhausted, subsequent allocations fail over to the general-purpose
@@ -48520,20 +54835,41 @@ typedef struct PgFreeslot PgFreeslot;
typedef struct PGroup PGroup;
/*
-** Each cache entry is represented by an instance of the following
-** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of
-** PgHdr1.pCache->szPage bytes is allocated directly before this structure
-** in memory.
+** Each cache entry is represented by an instance of the following
+** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated
+** directly before this structure and is used to cache the page content.
+**
+** When reading a corrupt database file, it is possible that SQLite might
+** read a few bytes (no more than 16 bytes) past the end of the page buffer.
+** It will only read past the end of the page buffer, never write. This
+** object is positioned immediately after the page buffer to serve as an
+** overrun area, so that overreads are harmless.
+**
+** Variables isBulkLocal and isAnchor were once type "u8". That works,
+** but causes a 2-byte gap in the structure for most architectures (since
+** pointers must be either 4 or 8-byte aligned). As this structure is located
+** in memory directly after the associated page data, if the database is
+** corrupt, code at the b-tree layer may overread the page buffer and
+** read part of this structure before the corruption is detected. This
+** can cause a valgrind error if the uninitialized gap is accessed. Using u16
+** ensures there is no such gap, and therefore no bytes of uninitialized
+** memory in the structure.
+**
+** The pLruNext and pLruPrev pointers form a double-linked circular list
+** of all pages that are unpinned. The PGroup.lru element (which should be
+** the only element on the list with PgHdr1.isAnchor set to 1) forms the
+** beginning and the end of the list.
*/
struct PgHdr1 {
- sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */
- unsigned int iKey; /* Key value (page number) */
- u8 isBulkLocal; /* This page from bulk local storage */
- u8 isAnchor; /* This is the PGroup.lru element */
- PgHdr1 *pNext; /* Next in hash table chain */
- PCache1 *pCache; /* Cache that currently owns this page */
- PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
- PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
+ sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */
+ unsigned int iKey; /* Key value (page number) */
+ u16 isBulkLocal; /* This page from bulk local storage */
+ u16 isAnchor; /* This is the PGroup.lru element */
+ PgHdr1 *pNext; /* Next in hash table chain */
+ PCache1 *pCache; /* Cache that currently owns this page */
+ PgHdr1 *pLruNext; /* Next in circular LRU list of unpinned pages */
+ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
+ /* NB: pLruPrev is only valid if pLruNext!=0 */
};
/*
@@ -48543,7 +54879,7 @@ struct PgHdr1 {
#define PAGE_IS_PINNED(p) ((p)->pLruNext==0)
#define PAGE_IS_UNPINNED(p) ((p)->pLruNext!=0)
-/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
+/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
** of one or more PCaches that are able to recycle each other's unpinned
** pages when they are under memory pressure. A PGroup is an instance of
** the following object.
@@ -48579,13 +54915,13 @@ struct PGroup {
** temporary or transient database) has a single page cache which
** is an instance of this object.
**
-** Pointers to structures of this type are cast and returned as
+** Pointers to structures of this type are cast and returned as
** opaque sqlite3_pcache* handles.
*/
struct PCache1 {
/* Cache configuration parameters. Page size (szPage) and the purgeable
** flag (bPurgeable) and the pnPurgeable pointer are all set when the
- ** cache is created and are never changed thereafter. nMax may be
+ ** cache is created and are never changed thereafter. nMax may be
** modified at any time by a call to the pcache1Cachesize() method.
** The PGroup mutex must be held when accessing nMax.
*/
@@ -48599,6 +54935,7 @@ struct PCache1 {
unsigned int nMax; /* Configured "cache_size" value */
unsigned int n90pct; /* nMax*9/10 */
unsigned int iMaxKey; /* Largest key seen since xTruncate() */
+ unsigned int nPurgeableDummy; /* pnPurgeable points here when not used*/
/* Hash table of all pages. The following variables may only be accessed
** when the accessor is holding the PGroup mutex.
@@ -48632,7 +54969,7 @@ static SQLITE_WSD struct PCacheGlobal {
*/
int isInit; /* True if initialized */
int separateCache; /* Use a new PGroup for each PCache */
- int nInitPage; /* Initial bulk allocation size */
+ int nInitPage; /* Initial bulk allocation size */
int szSlot; /* Size of each free slot */
int nSlot; /* The number of pcache slots */
int nReserve; /* Try to keep nFreeSlot above this */
@@ -48673,7 +55010,7 @@ static SQLITE_WSD struct PCacheGlobal {
/*
-** This function is called during initialization if a static buffer is
+** This function is called during initialization if a static buffer is
** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE
** verb to sqlite3_config(). Parameter pBuf points to an allocation large
** enough to contain 'n' buffers of 'sz' bytes each.
@@ -48733,6 +55070,7 @@ static int pcache1InitBulk(PCache1 *pCache){
pX->isBulkLocal = 1;
pX->isAnchor = 0;
pX->pNext = pCache->pFree;
+ pX->pLruPrev = 0; /* Initializing this saves a valgrind error */
pCache->pFree = pX;
zBulk += pCache->szAlloc;
}while( --nBulk );
@@ -48742,8 +55080,8 @@ static int pcache1InitBulk(PCache1 *pCache){
/*
** Malloc function used within this file to allocate space from the buffer
-** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no
-** such buffer exists or there is no space left in it, this function falls
+** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no
+** such buffer exists or there is no space left in it, this function falls
** back to sqlite3Malloc().
**
** Multiple threads can run this routine at the same time. Global variables
@@ -48843,40 +55181,32 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){
+ assert( pCache->pFree!=0 );
p = pCache->pFree;
pCache->pFree = p->pNext;
p->pNext = 0;
}else{
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/* The group mutex must be released before pcache1Alloc() is called. This
- ** is because it might call sqlite3_release_memory(), which assumes that
+ ** is because it might call sqlite3_release_memory(), which assumes that
** this mutex is not held. */
assert( pcache1.separateCache==0 );
assert( pCache->pGroup==&pcache1.grp );
pcache1LeaveMutex(pCache->pGroup);
#endif
if( benignMalloc ){ sqlite3BeginBenignMalloc(); }
-#ifdef SQLITE_PCACHE_SEPARATE_HEADER
- pPg = pcache1Alloc(pCache->szPage);
- p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
- if( !pPg || !p ){
- pcache1Free(pPg);
- sqlite3_free(p);
- pPg = 0;
- }
-#else
pPg = pcache1Alloc(pCache->szAlloc);
- p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
-#endif
if( benignMalloc ){ sqlite3EndBenignMalloc(); }
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
pcache1EnterMutex(pCache->pGroup);
#endif
if( pPg==0 ) return 0;
+ p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
p->page.pBuf = pPg;
p->page.pExtra = &p[1];
p->isBulkLocal = 0;
p->isAnchor = 0;
+ p->pLruPrev = 0; /* Initializing this saves a valgrind error */
}
(*pCache->pnPurgeable)++;
return p;
@@ -48895,9 +55225,6 @@ static void pcache1FreePage(PgHdr1 *p){
pCache->pFree = p;
}else{
pcache1Free(p->page.pBuf);
-#ifdef SQLITE_PCACHE_SEPARATE_HEADER
- sqlite3_free(p);
-#endif
}
(*pCache->pnPurgeable)--;
}
@@ -48908,6 +55235,7 @@ static void pcache1FreePage(PgHdr1 *p){
** exists, this function falls back to sqlite3Malloc().
*/
SQLITE_PRIVATE void *sqlite3PageMalloc(int sz){
+ assert( sz<=65536+8 ); /* These allocations are never very large */
return pcache1Alloc(sz);
}
@@ -48987,7 +55315,7 @@ static void pcache1ResizeHash(PCache1 *p){
}
/*
-** This function is used internally to remove the page pPage from the
+** This function is used internally to remove the page pPage from the
** PGroup LRU list, if is part of it. If pPage is not part of the PGroup
** LRU list, then this function is a no-op.
**
@@ -49002,7 +55330,8 @@ static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){
pPage->pLruPrev->pLruNext = pPage->pLruNext;
pPage->pLruNext->pLruPrev = pPage->pLruPrev;
pPage->pLruNext = 0;
- pPage->pLruPrev = 0;
+ /* pPage->pLruPrev = 0;
+ ** No need to clear pLruPrev as it is never accessed if pLruNext is 0 */
assert( pPage->isAnchor==0 );
assert( pPage->pCache->pGroup->lru.isAnchor==1 );
pPage->pCache->nRecyclable--;
@@ -49011,7 +55340,7 @@ static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){
/*
-** Remove the page supplied as an argument from the hash table
+** Remove the page supplied as an argument from the hash table
** (PCache1.apHash structure) that it is currently stored in.
** Also free the page if freePage is true.
**
@@ -49054,8 +55383,8 @@ static void pcache1EnforceMaxPage(PCache1 *pCache){
}
/*
-** Discard all pages from cache pCache with a page number (key value)
-** greater than or equal to iLimit. Any pinned pages that meet this
+** Discard all pages from cache pCache with a page number (key value)
+** greater than or equal to iLimit. Any pinned pages that meet this
** criteria are unpinned before they are discarded.
**
** The PCache mutex must be held when this function is called.
@@ -49087,7 +55416,7 @@ static void pcache1TruncateUnsafe(
PgHdr1 **pp;
PgHdr1 *pPage;
assert( h<pCache->nHash );
- pp = &pCache->apHash[h];
+ pp = &pCache->apHash[h];
while( (pPage = *pp)!=0 ){
if( pPage->iKey>=iLimit ){
pCache->nPage--;
@@ -49126,7 +55455,7 @@ static int pcache1Init(void *NotUsed){
**
** * Use a unified cache in single-threaded applications that have
** configured a start-time buffer for use as page-cache memory using
- ** sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL
+ ** sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL
** pBuf argument.
**
** * Otherwise use separate caches (mode-1)
@@ -49161,7 +55490,7 @@ static int pcache1Init(void *NotUsed){
/*
** Implementation of the sqlite3_pcache.xShutdown method.
-** Note that the static mutex allocated in xInit does
+** Note that the static mutex allocated in xInit does
** not need to be freed.
*/
static void pcache1Shutdown(void *NotUsed){
@@ -49195,6 +55524,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
}else{
pGroup = &pcache1.grp;
}
+ pcache1EnterMutex(pGroup);
if( pGroup->lru.isAnchor==0 ){
pGroup->lru.isAnchor = 1;
pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru;
@@ -49204,7 +55534,6 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
pCache->szExtra = szExtra;
pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1));
pCache->bPurgeable = (bPurgeable ? 1 : 0);
- pcache1EnterMutex(pGroup);
pcache1ResizeHash(pCache);
if( bPurgeable ){
pCache->nMin = 10;
@@ -49212,8 +55541,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pCache->pnPurgeable = &pGroup->nPurgeable;
}else{
- static unsigned int dummyCurrentPage;
- pCache->pnPurgeable = &dummyCurrentPage;
+ pCache->pnPurgeable = &pCache->nPurgeableDummy;
}
pcache1LeaveMutex(pGroup);
if( pCache->nHash==0 ){
@@ -49225,18 +55553,24 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
}
/*
-** Implementation of the sqlite3_pcache.xCachesize method.
+** Implementation of the sqlite3_pcache.xCachesize method.
**
** Configure the cache_size limit for a cache.
*/
static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
PCache1 *pCache = (PCache1 *)p;
+ u32 n;
+ assert( nMax>=0 );
if( pCache->bPurgeable ){
PGroup *pGroup = pCache->pGroup;
pcache1EnterMutex(pGroup);
- pGroup->nMaxPage += (nMax - pCache->nMax);
+ n = (u32)nMax;
+ if( n > 0x7fff0000 - pGroup->nMaxPage + pCache->nMax ){
+ n = 0x7fff0000 - pGroup->nMaxPage + pCache->nMax;
+ }
+ pGroup->nMaxPage += (n - pCache->nMax);
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
- pCache->nMax = nMax;
+ pCache->nMax = n;
pCache->n90pct = pCache->nMax*9/10;
pcache1EnforceMaxPage(pCache);
pcache1LeaveMutex(pGroup);
@@ -49244,7 +55578,7 @@ static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
}
/*
-** Implementation of the sqlite3_pcache.xShrink method.
+** Implementation of the sqlite3_pcache.xShrink method.
**
** Free up as much memory as possible.
*/
@@ -49252,7 +55586,7 @@ static void pcache1Shrink(sqlite3_pcache *p){
PCache1 *pCache = (PCache1*)p;
if( pCache->bPurgeable ){
PGroup *pGroup = pCache->pGroup;
- int savedMaxPage;
+ unsigned int savedMaxPage;
pcache1EnterMutex(pGroup);
savedMaxPage = pGroup->nMaxPage;
pGroup->nMaxPage = 0;
@@ -49263,7 +55597,7 @@ static void pcache1Shrink(sqlite3_pcache *p){
}
/*
-** Implementation of the sqlite3_pcache.xPagecount method.
+** Implementation of the sqlite3_pcache.xPagecount method.
*/
static int pcache1Pagecount(sqlite3_pcache *p){
int n;
@@ -49284,8 +55618,8 @@ static int pcache1Pagecount(sqlite3_pcache *p){
** for these steps, the main pcache1Fetch() procedure can run faster.
*/
static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
- PCache1 *pCache,
- unsigned int iKey,
+ PCache1 *pCache,
+ unsigned int iKey,
int createFlag
){
unsigned int nPinned;
@@ -49327,8 +55661,8 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
}
}
- /* Step 5. If a usable page buffer has still not been found,
- ** attempt to allocate a new one.
+ /* Step 5. If a usable page buffer has still not been found,
+ ** attempt to allocate a new one.
*/
if( !pPage ){
pPage = pcache1AllocPage(pCache, createFlag==1);
@@ -49340,8 +55674,9 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
pPage->iKey = iKey;
pPage->pNext = pCache->apHash[h];
pPage->pCache = pCache;
- pPage->pLruPrev = 0;
pPage->pLruNext = 0;
+ /* pPage->pLruPrev = 0;
+ ** No need to clear pLruPrev since it is not accessed when pLruNext==0 */
*(void **)pPage->page.pExtra = 0;
pCache->apHash[h] = pPage;
if( iKey>pCache->iMaxKey ){
@@ -49352,13 +55687,13 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
}
/*
-** Implementation of the sqlite3_pcache.xFetch method.
+** Implementation of the sqlite3_pcache.xFetch method.
**
** Fetch a page by key value.
**
** Whether or not a new page may be allocated by this function depends on
** the value of the createFlag argument. 0 means do not allocate a new
-** page. 1 means allocate a new page if space is easily available. 2
+** page. 1 means allocate a new page if space is easily available. 2
** means to try really hard to allocate a new page.
**
** For a non-purgeable cache (a cache used as the storage for an in-memory
@@ -49369,7 +55704,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
** There are three different approaches to obtaining space for a page,
** depending on the value of parameter createFlag (which may be 0, 1 or 2).
**
-** 1. Regardless of the value of createFlag, the cache is searched for a
+** 1. Regardless of the value of createFlag, the cache is searched for a
** copy of the requested page. If one is found, it is returned.
**
** 2. If createFlag==0 and the page is not already in the cache, NULL is
@@ -49383,13 +55718,13 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
** PCache1.nMax, or
**
** (b) the number of pages pinned by the cache is greater than
-** the sum of nMax for all purgeable caches, less the sum of
+** the sum of nMax for all purgeable caches, less the sum of
** nMin for all other purgeable caches, or
**
** 4. If none of the first three conditions apply and the cache is marked
** as purgeable, and if one of the following is true:
**
-** (a) The number of pages allocated for the cache is already
+** (a) The number of pages allocated for the cache is already
** PCache1.nMax, or
**
** (b) The number of pages allocated for all purgeable caches is
@@ -49401,7 +55736,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
**
** then attempt to recycle a page from the LRU list. If it is the right
** size, return the recycled buffer. Otherwise, free the buffer and
-** proceed to step 5.
+** proceed to step 5.
**
** 5. Otherwise, allocate and return a new page buffer.
**
@@ -49411,8 +55746,8 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
** invokes the appropriate routine.
*/
static PgHdr1 *pcache1FetchNoMutex(
- sqlite3_pcache *p,
- unsigned int iKey,
+ sqlite3_pcache *p,
+ unsigned int iKey,
int createFlag
){
PCache1 *pCache = (PCache1 *)p;
@@ -49441,8 +55776,8 @@ static PgHdr1 *pcache1FetchNoMutex(
}
#if PCACHE1_MIGHT_USE_GROUP_MUTEX
static PgHdr1 *pcache1FetchWithMutex(
- sqlite3_pcache *p,
- unsigned int iKey,
+ sqlite3_pcache *p,
+ unsigned int iKey,
int createFlag
){
PCache1 *pCache = (PCache1 *)p;
@@ -49456,8 +55791,8 @@ static PgHdr1 *pcache1FetchWithMutex(
}
#endif
static sqlite3_pcache_page *pcache1Fetch(
- sqlite3_pcache *p,
- unsigned int iKey,
+ sqlite3_pcache *p,
+ unsigned int iKey,
int createFlag
){
#if PCACHE1_MIGHT_USE_GROUP_MUTEX || defined(SQLITE_DEBUG)
@@ -49487,21 +55822,21 @@ static sqlite3_pcache_page *pcache1Fetch(
** Mark a page as unpinned (eligible for asynchronous recycling).
*/
static void pcache1Unpin(
- sqlite3_pcache *p,
- sqlite3_pcache_page *pPg,
+ sqlite3_pcache *p,
+ sqlite3_pcache_page *pPg,
int reuseUnlikely
){
PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage = (PgHdr1 *)pPg;
PGroup *pGroup = pCache->pGroup;
-
+
assert( pPage->pCache==pCache );
pcache1EnterMutex(pGroup);
- /* It is an error to call this function if the page is already
+ /* It is an error to call this function if the page is already
** part of the PGroup LRU list.
*/
- assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
+ assert( pPage->pLruNext==0 );
assert( PAGE_IS_PINNED(pPage) );
if( reuseUnlikely || pGroup->nPurgeable>pGroup->nMaxPage ){
@@ -49519,7 +55854,7 @@ static void pcache1Unpin(
}
/*
-** Implementation of the sqlite3_pcache.xRekey method.
+** Implementation of the sqlite3_pcache.xRekey method.
*/
static void pcache1Rekey(
sqlite3_pcache *p,
@@ -49530,23 +55865,26 @@ static void pcache1Rekey(
PCache1 *pCache = (PCache1 *)p;
PgHdr1 *pPage = (PgHdr1 *)pPg;
PgHdr1 **pp;
- unsigned int h;
+ unsigned int hOld, hNew;
assert( pPage->iKey==iOld );
assert( pPage->pCache==pCache );
+ assert( iOld!=iNew ); /* The page number really is changing */
pcache1EnterMutex(pCache->pGroup);
- h = iOld%pCache->nHash;
- pp = &pCache->apHash[h];
+ assert( pcache1FetchNoMutex(p, iOld, 0)==pPage ); /* pPg really is iOld */
+ hOld = iOld%pCache->nHash;
+ pp = &pCache->apHash[hOld];
while( (*pp)!=pPage ){
pp = &(*pp)->pNext;
}
*pp = pPage->pNext;
- h = iNew%pCache->nHash;
+ assert( pcache1FetchNoMutex(p, iNew, 0)==0 ); /* iNew not in cache */
+ hNew = iNew%pCache->nHash;
pPage->iKey = iNew;
- pPage->pNext = pCache->apHash[h];
- pCache->apHash[h] = pPage;
+ pPage->pNext = pCache->apHash[hNew];
+ pCache->apHash[hNew] = pPage;
if( iNew>pCache->iMaxKey ){
pCache->iMaxKey = iNew;
}
@@ -49555,7 +55893,7 @@ static void pcache1Rekey(
}
/*
-** Implementation of the sqlite3_pcache.xTruncate method.
+** Implementation of the sqlite3_pcache.xTruncate method.
**
** Discard all unpinned pages in the cache with a page number equal to
** or greater than parameter iLimit. Any pinned pages with a page number
@@ -49572,7 +55910,7 @@ static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){
}
/*
-** Implementation of the sqlite3_pcache.xDestroy method.
+** Implementation of the sqlite3_pcache.xDestroy method.
**
** Destroy a cache allocated using pcache1Create().
*/
@@ -49638,7 +55976,7 @@ SQLITE_PRIVATE sqlite3_mutex *sqlite3Pcache1Mutex(void){
** by the current thread may be sqlite3_free()ed.
**
** nReq is the number of bytes of memory required. Once this much has
-** been released, the function returns. The return value is the total number
+** been released, the function returns. The return value is the total number
** of bytes of memory released.
*/
SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){
@@ -49653,9 +55991,6 @@ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){
&& p->isAnchor==0
){
nFree += pcache1MemSize(p->page.pBuf);
-#ifdef SQLITE_PCACHE_SEPARATE_HEADER
- nFree += sqlite3MemSize(p);
-#endif
assert( PAGE_IS_UNPINNED(p) );
pcache1PinPage(p);
pcache1RemoveFromHash(p, 1);
@@ -49729,14 +56064,14 @@ SQLITE_PRIVATE void sqlite3PcacheStats(
** extracts the least value from the RowSet.
**
** The INSERT primitive might allocate additional memory. Memory is
-** allocated in chunks so most INSERTs do no allocation. There is an
+** allocated in chunks so most INSERTs do no allocation. There is an
** upper bound on the size of allocated memory. No memory is freed
** until DESTROY.
**
** The TEST primitive includes a "batch" number. The TEST primitive
** will only see elements that were inserted before the last change
** in the batch number. In other words, if an INSERT occurs between
-** two TESTs where the TESTs have the same batch nubmer, then the
+** two TESTs where the TESTs have the same batch number, then the
** value added by the INSERT will not be visible to the second TEST.
** The initial batch number is zero, so if the very first TEST contains
** a non-zero batch number, it will see all prior INSERTs.
@@ -49777,7 +56112,7 @@ SQLITE_PRIVATE void sqlite3PcacheStats(
** in the list, pLeft points to the tree, and v is unused. The
** RowSet.pForest value points to the head of this forest list.
*/
-struct RowSetEntry {
+struct RowSetEntry {
i64 v; /* ROWID value for this entry */
struct RowSetEntry *pRight; /* Right subtree (larger entries) or list */
struct RowSetEntry *pLeft; /* Left subtree (smaller entries) */
@@ -49871,7 +56206,7 @@ SQLITE_PRIVATE void sqlite3RowSetDelete(void *pArg){
/*
** Allocate a new RowSetEntry object that is associated with the
** given RowSet. Return a pointer to the new and completely uninitialized
-** objected.
+** object.
**
** In an OOM situation, the RowSet.db->mallocFailed flag is set and this
** routine returns NULL.
@@ -49929,7 +56264,7 @@ SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet *p, i64 rowid){
/*
** Merge two lists of RowSetEntry objects. Remove duplicates.
**
-** The input lists are connected via pRight pointers and are
+** The input lists are connected via pRight pointers and are
** assumed to each already be in sorted order.
*/
static struct RowSetEntry *rowSetEntryMerge(
@@ -49966,7 +56301,7 @@ static struct RowSetEntry *rowSetEntryMerge(
/*
** Sort all elements on the list of RowSetEntry objects into order of
** increasing v.
-*/
+*/
static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){
unsigned int i;
struct RowSetEntry *pNext, *aBucket[40];
@@ -50039,7 +56374,7 @@ static struct RowSetEntry *rowSetNDeepTree(
struct RowSetEntry *pLeft; /* Left subtree */
if( *ppList==0 ){ /*OPTIMIZATION-IF-TRUE*/
/* Prevent unnecessary deep recursion when we run out of entries */
- return 0;
+ return 0;
}
if( iDepth>1 ){ /*OPTIMIZATION-IF-TRUE*/
/* This branch causes a *balanced* tree to be generated. A valid tree
@@ -50147,7 +56482,7 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64
if( p ){
struct RowSetEntry **ppPrevTree = &pRowSet->pForest;
if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/
- /* Only sort the current set of entiries if they need it */
+ /* Only sort the current set of entries if they need it */
p = rowSetEntrySort(p);
}
for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
@@ -50209,7 +56544,7 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64
**
*************************************************************************
** This is the implementation of the page cache subsystem or "pager".
-**
+**
** The pager is used to access a database disk file. It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file. The pager also implements file
@@ -50232,8 +56567,8 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This header file defines the interface to the write-ahead logging
-** system. Refer to the comments below and the header comment attached to
+** This header file defines the interface to the write-ahead logging
+** system. Refer to the comments below and the header comment attached to
** the implementation of each function in log.c for further details.
*/
@@ -50268,12 +56603,13 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64
# define sqlite3WalFramesize(z) 0
# define sqlite3WalFindFrame(x,y,z) 0
# define sqlite3WalFile(x) 0
+# undef SQLITE_USE_SEH
#else
#define WAL_SAVEPOINT_NDATA 4
-/* Connection to a write-ahead log (WAL) file.
-** There is one object of this type for each pager.
+/* Connection to a write-ahead log (WAL) file.
+** There is one object of this type for each pager.
*/
typedef struct Wal Wal;
@@ -50284,7 +56620,7 @@ SQLITE_PRIVATE int sqlite3WalClose(Wal *pWal, sqlite3*, int sync_flags, int, u8
/* Set the limiting size of a WAL file. */
SQLITE_PRIVATE void sqlite3WalLimit(Wal*, i64);
-/* Used by readers to open (lock) and close (unlock) a snapshot. A
+/* Used by readers to open (lock) and close (unlock) a snapshot. A
** snapshot is like a read-transaction. It is the state of the database
** at an instant in time. sqlite3WalOpenSnapshot gets a read lock and
** preserves the current state even if the other threads or processes
@@ -50319,7 +56655,7 @@ SQLITE_PRIVATE int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData);
/* Write a frame or frames to the log. */
SQLITE_PRIVATE int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int);
-/* Copy pages from the log to the database file */
+/* Copy pages from the log to the database file */
SQLITE_PRIVATE int sqlite3WalCheckpoint(
Wal *pWal, /* Write-ahead log connection */
sqlite3 *db, /* Check this handle's interrupt flag */
@@ -50347,7 +56683,7 @@ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op);
/* Return true if the argument is non-NULL and the WAL module is using
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
-** WAL module is using shared-memory, return false.
+** WAL module is using shared-memory, return false.
*/
SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal);
@@ -50369,6 +56705,15 @@ SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal);
/* Return the sqlite3_file object for the WAL file */
SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock);
+SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db);
+#endif
+
+#ifdef SQLITE_USE_SEH
+SQLITE_PRIVATE int sqlite3WalSystemErrno(Wal*);
+#endif
+
#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* SQLITE_WAL_H */
@@ -50389,60 +56734,60 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal);
**
** Definition: A page of the database file is said to be "overwriteable" if
** one or more of the following are true about the page:
-**
+**
** (a) The original content of the page as it was at the beginning of
** the transaction has been written into the rollback journal and
** synced.
-**
+**
** (b) The page was a freelist leaf page at the start of the transaction.
-**
+**
** (c) The page number is greater than the largest page that existed in
** the database file at the start of the transaction.
-**
+**
** (1) A page of the database file is never overwritten unless one of the
** following are true:
-**
+**
** (a) The page and all other pages on the same sector are overwriteable.
-**
+**
** (b) The atomic page write optimization is enabled, and the entire
** transaction other than the update of the transaction sequence
** number consists of a single page change.
-**
+**
** (2) The content of a page written into the rollback journal exactly matches
** both the content in the database when the rollback journal was written
** and the content in the database at the beginning of the current
** transaction.
-**
+**
** (3) Writes to the database file are an integer multiple of the page size
** in length and are aligned on a page boundary.
-**
+**
** (4) Reads from the database file are either aligned on a page boundary and
** an integer multiple of the page size in length or are taken from the
** first 100 bytes of the database file.
-**
+**
** (5) All writes to the database file are synced prior to the rollback journal
** being deleted, truncated, or zeroed.
-**
-** (6) If a master journal file is used, then all writes to the database file
-** are synced prior to the master journal being deleted.
-**
+**
+** (6) If a super-journal file is used, then all writes to the database file
+** are synced prior to the super-journal being deleted.
+**
** Definition: Two databases (or the same database at two points it time)
** are said to be "logically equivalent" if they give the same answer to
** all queries. Note in particular the content of freelist leaf
** pages can be changed arbitrarily without affecting the logical equivalence
** of the database.
-**
+**
** (7) At any time, if any subset, including the empty set and the total set,
-** of the unsynced changes to a rollback journal are removed and the
+** of the unsynced changes to a rollback journal are removed and the
** journal is rolled back, the resulting database file will be logically
** equivalent to the database file at the beginning of the transaction.
-**
+**
** (8) When a transaction is rolled back, the xTruncate method of the VFS
** is called to restore the database file to the same size it was at
** the beginning of the transaction. (In some VFSes, the xTruncate
** method is a no-op, but that does not change the fact the SQLite will
** invoke it.)
-**
+**
** (9) Whenever the database file is modified, at least one bit in the range
** of bytes from 24 through 39 inclusive will be changed prior to releasing
** the EXCLUSIVE lock, thus signaling other connections on the same
@@ -50475,7 +56820,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
/*
** The following two macros are used within the PAGERTRACE() macros above
-** to print out file-descriptors.
+** to print out file-descriptors.
**
** PAGERID() takes a pointer to a Pager struct as its argument. The
** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file
@@ -50496,7 +56841,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** | | |
** | V |
** |<-------WRITER_LOCKED------> ERROR
-** | | ^
+** | | ^
** | V |
** |<------WRITER_CACHEMOD-------->|
** | | |
@@ -50508,7 +56853,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
**
**
** List of state transitions and the C [function] that performs each:
-**
+**
** OPEN -> READER [sqlite3PagerSharedLock]
** READER -> OPEN [pager_unlock]
**
@@ -50520,7 +56865,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
**
** WRITER_*** -> ERROR [pager_error]
** ERROR -> OPEN [pager_unlock]
-**
+**
**
** OPEN:
**
@@ -50534,9 +56879,9 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
**
** READER:
**
-** In this state all the requirements for reading the database in
+** In this state all the requirements for reading the database in
** rollback (non-WAL) mode are met. Unless the pager is (or recently
-** was) in exclusive-locking mode, a user-level read transaction is
+** was) in exclusive-locking mode, a user-level read transaction is
** open. The database size is known in this state.
**
** A connection running with locking_mode=normal enters this state when
@@ -50546,28 +56891,28 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** this state even after the read-transaction is closed. The only way
** a locking_mode=exclusive connection can transition from READER to OPEN
** is via the ERROR state (see below).
-**
+**
** * A read transaction may be active (but a write-transaction cannot).
** * A SHARED or greater lock is held on the database file.
-** * The dbSize variable may be trusted (even if a user-level read
+** * The dbSize variable may be trusted (even if a user-level read
** transaction is not active). The dbOrigSize and dbFileSize variables
** may not be trusted at this point.
** * If the database is a WAL database, then the WAL connection is open.
-** * Even if a read-transaction is not open, it is guaranteed that
+** * Even if a read-transaction is not open, it is guaranteed that
** there is no hot-journal in the file-system.
**
** WRITER_LOCKED:
**
** The pager moves to this state from READER when a write-transaction
-** is first opened on the database. In WRITER_LOCKED state, all locks
-** required to start a write-transaction are held, but no actual
+** is first opened on the database. In WRITER_LOCKED state, all locks
+** required to start a write-transaction are held, but no actual
** modifications to the cache or database have taken place.
**
-** In rollback mode, a RESERVED or (if the transaction was opened with
+** In rollback mode, a RESERVED or (if the transaction was opened with
** BEGIN EXCLUSIVE) EXCLUSIVE lock is obtained on the database file when
-** moving to this state, but the journal file is not written to or opened
-** to in this state. If the transaction is committed or rolled back while
-** in WRITER_LOCKED state, all that is required is to unlock the database
+** moving to this state, but the journal file is not written to or opened
+** to in this state. If the transaction is committed or rolled back while
+** in WRITER_LOCKED state, all that is required is to unlock the database
** file.
**
** IN WAL mode, WalBeginWriteTransaction() is called to lock the log file.
@@ -50575,7 +56920,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** is made to obtain an EXCLUSIVE lock on the database file.
**
** * A write transaction is active.
-** * If the connection is open in rollback-mode, a RESERVED or greater
+** * If the connection is open in rollback-mode, a RESERVED or greater
** lock is held on the database file.
** * If the connection is open in WAL-mode, a WAL write transaction
** is open (i.e. sqlite3WalBeginWriteTransaction() has been successfully
@@ -50594,7 +56939,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
**
** * A write transaction is active.
** * A RESERVED or greater lock is held on the database file.
-** * The journal file is open and the first header has been written
+** * The journal file is open and the first header has been written
** to it, but the header has not been synced to disk.
** * The contents of the page cache have been modified.
**
@@ -50607,7 +56952,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
**
** * A write transaction is active.
** * An EXCLUSIVE or greater lock is held on the database file.
-** * The journal file is open and the first header has been written
+** * The journal file is open and the first header has been written
** and synced to disk.
** * The contents of the page cache have been modified (and possibly
** written to disk).
@@ -50619,8 +56964,8 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** A rollback-mode pager changes to WRITER_FINISHED state from WRITER_DBMOD
** state after the entire transaction has been successfully written into the
** database file. In this state the transaction may be committed simply
-** by finalizing the journal file. Once in WRITER_FINISHED state, it is
-** not possible to modify the database further. At this point, the upper
+** by finalizing the journal file. Once in WRITER_FINISHED state, it is
+** not possible to modify the database further. At this point, the upper
** layer must either commit or rollback the transaction.
**
** * A write transaction is active.
@@ -50628,19 +56973,19 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** * All writing and syncing of journal and database data has finished.
** If no error occurred, all that remains is to finalize the journal to
** commit the transaction. If an error did occur, the caller will need
-** to rollback the transaction.
+** to rollback the transaction.
**
** ERROR:
**
** The ERROR state is entered when an IO or disk-full error (including
-** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it
-** difficult to be sure that the in-memory pager state (cache contents,
+** SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it
+** difficult to be sure that the in-memory pager state (cache contents,
** db size etc.) are consistent with the contents of the file-system.
**
** Temporary pager files may enter the ERROR state, but in-memory pagers
** cannot.
**
-** For example, if an IO error occurs while performing a rollback,
+** For example, if an IO error occurs while performing a rollback,
** the contents of the page-cache may be left in an inconsistent state.
** At this point it would be dangerous to change back to READER state
** (as usually happens after a rollback). Any subsequent readers might
@@ -50650,13 +56995,13 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** instead of READER following such an error.
**
** Once it has entered the ERROR state, any attempt to use the pager
-** to read or write data returns an error. Eventually, once all
+** to read or write data returns an error. Eventually, once all
** outstanding transactions have been abandoned, the pager is able to
-** transition back to OPEN state, discarding the contents of the
+** transition back to OPEN state, discarding the contents of the
** page-cache and any other in-memory state at the same time. Everything
-** is reloaded from disk (and, if necessary, hot-journal rollback peformed)
+** is reloaded from disk (and, if necessary, hot-journal rollback performed)
** when a read-transaction is next opened on the pager (transitioning
-** the pager into READER state). At that point the system has recovered
+** the pager into READER state). At that point the system has recovered
** from the error.
**
** Specifically, the pager jumps into the ERROR state if:
@@ -50672,21 +57017,21 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** memory.
**
** In other cases, the error is returned to the b-tree layer. The b-tree
-** layer then attempts a rollback operation. If the error condition
+** layer then attempts a rollback operation. If the error condition
** persists, the pager enters the ERROR state via condition (1) above.
**
** Condition (3) is necessary because it can be triggered by a read-only
** statement executed within a transaction. In this case, if the error
** code were simply returned to the user, the b-tree layer would not
** automatically attempt a rollback, as it assumes that an error in a
-** read-only statement cannot leave the pager in an internally inconsistent
+** read-only statement cannot leave the pager in an internally inconsistent
** state.
**
** * The Pager.errCode variable is set to something other than SQLITE_OK.
** * There are one or more outstanding references to pages (after the
** last reference is dropped the pager should move back to OPEN state).
** * The pager is not an in-memory pager.
-**
+**
**
** Notes:
**
@@ -50696,7 +57041,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
**
** * Normally, a connection open in exclusive mode is never in PAGER_OPEN
** state. There are two exceptions: immediately after exclusive-mode has
-** been turned on (and before any read or write transactions are
+** been turned on (and before any read or write transactions are
** executed), and when the pager is leaving the "error state".
**
** * See also: assert_pager_state().
@@ -50710,7 +57055,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
#define PAGER_ERROR 6
/*
-** The Pager.eLock variable is almost always set to one of the
+** The Pager.eLock variable is almost always set to one of the
** following locking-states, according to the lock currently held on
** the database file: NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK.
** This variable is kept up to date as locks are taken and released by
@@ -50725,20 +57070,20 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** to a less exclusive (lower) value than the lock that is actually held
** at the system level, but it is never set to a more exclusive value.
**
-** This is usually safe. If an xUnlock fails or appears to fail, there may
+** This is usually safe. If an xUnlock fails or appears to fail, there may
** be a few redundant xLock() calls or a lock may be held for longer than
** required, but nothing really goes wrong.
**
** The exception is when the database file is unlocked as the pager moves
-** from ERROR to OPEN state. At this point there may be a hot-journal file
+** from ERROR to OPEN state. At this point there may be a hot-journal file
** in the file-system that needs to be rolled back (as part of an OPEN->SHARED
** transition, by the same pager or any other). If the call to xUnlock()
** fails at this point and the pager is left holding an EXCLUSIVE lock, this
** can confuse the call to xCheckReservedLock() call made later as part
** of hot-journal detection.
**
-** xCheckReservedLock() is defined as returning true "if there is a RESERVED
-** lock held by this process or any others". So xCheckReservedLock may
+** xCheckReservedLock() is defined as returning true "if there is a RESERVED
+** lock held by this process or any others". So xCheckReservedLock may
** return true because the caller itself is holding an EXCLUSIVE lock (but
** doesn't know it because of a previous error in xUnlock). If this happens
** a hot-journal may be mistaken for a journal being created by an active
@@ -50749,32 +57094,18 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** database in the ERROR state, Pager.eLock is set to UNKNOWN_LOCK. It
** is only changed back to a real locking state after a successful call
** to xLock(EXCLUSIVE). Also, the code to do the OPEN->SHARED state transition
-** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK
+** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK
** lock. Instead, it assumes a hot-journal exists and obtains an EXCLUSIVE
** lock on the database file before attempting to roll it back. See function
** PagerSharedLock() for more detail.
**
-** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in
+** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in
** PAGER_OPEN state.
*/
#define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1)
/*
-** A macro used for invoking the codec if there is one
-*/
-#ifdef SQLITE_HAS_CODEC
-# define CODEC1(P,D,N,X,E) \
- if( P->xCodec && P->xCodec(P->pCodec,D,N,X)==0 ){ E; }
-# define CODEC2(P,D,N,X,E,O) \
- if( P->xCodec==0 ){ O=(char*)D; }else \
- if( (O=(char*)(P->xCodec(P->pCodec,D,N,X)))==0 ){ E; }
-#else
-# define CODEC1(P,D,N,X,E) /* NO-OP */
-# define CODEC2(P,D,N,X,E,O) O=(char*)D
-#endif
-
-/*
-** The maximum allowed sector size. 64KiB. If the xSectorsize() method
+** The maximum allowed sector size. 64KiB. If the xSectorsize() method
** returns a value larger than this, then MAX_SECTOR_SIZE is used instead.
** This could conceivably cause corruption following a power failure on
** such a system. This is currently an undocumented limit.
@@ -50790,7 +57121,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
**
** When a savepoint is created, the PagerSavepoint.iHdrOffset field is
** set to 0. If a journal-header is written into the main journal while
-** the savepoint is active, then iHdrOffset is set to the byte offset
+** the savepoint is active, then iHdrOffset is set to the byte offset
** immediately following the last journal record written into the main
** journal before the journal-header. This is required during savepoint
** rollback (see pagerPlaybackSavepoint()).
@@ -50802,6 +57133,7 @@ struct PagerSavepoint {
Bitvec *pInSavepoint; /* Set of pages in this savepoint */
Pgno nOrig; /* Original number of pages in file */
Pgno iSubRec; /* Index of first record in sub-journal */
+ int bTruncateOnRelease; /* If stmt journal may be truncated on RELEASE */
#ifndef SQLITE_OMIT_WAL
u32 aWalData[WAL_SAVEPOINT_NDATA]; /* WAL savepoint context */
#endif
@@ -50840,44 +57172,44 @@ struct PagerSavepoint {
**
** changeCountDone
**
-** This boolean variable is used to make sure that the change-counter
-** (the 4-byte header field at byte offset 24 of the database file) is
-** not updated more often than necessary.
+** This boolean variable is used to make sure that the change-counter
+** (the 4-byte header field at byte offset 24 of the database file) is
+** not updated more often than necessary.
**
-** It is set to true when the change-counter field is updated, which
+** It is set to true when the change-counter field is updated, which
** can only happen if an exclusive lock is held on the database file.
-** It is cleared (set to false) whenever an exclusive lock is
+** It is cleared (set to false) whenever an exclusive lock is
** relinquished on the database file. Each time a transaction is committed,
** The changeCountDone flag is inspected. If it is true, the work of
** updating the change-counter is omitted for the current transaction.
**
-** This mechanism means that when running in exclusive mode, a connection
+** This mechanism means that when running in exclusive mode, a connection
** need only update the change-counter once, for the first transaction
** committed.
**
-** setMaster
+** setSuper
**
** When PagerCommitPhaseOne() is called to commit a transaction, it may
-** (or may not) specify a master-journal name to be written into the
+** (or may not) specify a super-journal name to be written into the
** journal file before it is synced to disk.
**
-** Whether or not a journal file contains a master-journal pointer affects
-** the way in which the journal file is finalized after the transaction is
+** Whether or not a journal file contains a super-journal pointer affects
+** the way in which the journal file is finalized after the transaction is
** committed or rolled back when running in "journal_mode=PERSIST" mode.
-** If a journal file does not contain a master-journal pointer, it is
+** If a journal file does not contain a super-journal pointer, it is
** finalized by overwriting the first journal header with zeroes. If
-** it does contain a master-journal pointer the journal file is finalized
-** by truncating it to zero bytes, just as if the connection were
+** it does contain a super-journal pointer the journal file is finalized
+** by truncating it to zero bytes, just as if the connection were
** running in "journal_mode=truncate" mode.
**
-** Journal files that contain master journal pointers cannot be finalized
+** Journal files that contain super-journal pointers cannot be finalized
** simply by overwriting the first journal-header with zeroes, as the
-** master journal pointer could interfere with hot-journal rollback of any
+** super-journal pointer could interfere with hot-journal rollback of any
** subsequently interrupted transaction that reuses the journal file.
**
** The flag is cleared as soon as the journal file is finalized (either
** by PagerCommitPhaseTwo or PagerRollback). If an IO error prevents the
-** journal file from being successfully finalized, the setMaster flag
+** journal file from being successfully finalized, the setSuper flag
** is cleared anyway (and the pager will move to ERROR state).
**
** doNotSpill
@@ -50893,12 +57225,12 @@ struct PagerSavepoint {
** to allocate a new page to prevent the journal file from being written
** while it is being traversed by code in pager_playback(). The SPILLFLAG_OFF
** case is a user preference.
-**
+**
** If the SPILLFLAG_NOSYNC bit is set, writing to the database from
** pagerStress() is permitted, but syncing the journal file is not.
** This flag is set by sqlite3PagerWrite() when the file-system sector-size
** is larger than the database page-size in order to prevent a journal sync
-** from happening in between the journalling of two pages on the same sector.
+** from happening in between the journalling of two pages on the same sector.
**
** subjInMemory
**
@@ -50906,16 +57238,16 @@ struct PagerSavepoint {
** is opened as an in-memory journal file. If false, then in-memory
** sub-journals are only used for in-memory pager files.
**
-** This variable is updated by the upper layer each time a new
+** This variable is updated by the upper layer each time a new
** write-transaction is opened.
**
** dbSize, dbOrigSize, dbFileSize
**
** Variable dbSize is set to the number of pages in the database file.
** It is valid in PAGER_READER and higher states (all states except for
-** OPEN and ERROR).
+** OPEN and ERROR).
**
-** dbSize is set based on the size of the database file, which may be
+** dbSize is set based on the size of the database file, which may be
** larger than the size of the database (the value stored at offset
** 28 of the database header by the btree). If the size of the file
** is not an integer multiple of the page-size, the value stored in
@@ -50926,10 +57258,10 @@ struct PagerSavepoint {
**
** During a write-transaction, if pages with page-numbers greater than
** dbSize are modified in the cache, dbSize is updated accordingly.
-** Similarly, if the database is truncated using PagerTruncateImage(),
+** Similarly, if the database is truncated using PagerTruncateImage(),
** dbSize is updated.
**
-** Variables dbOrigSize and dbFileSize are valid in states
+** Variables dbOrigSize and dbFileSize are valid in states
** PAGER_WRITER_LOCKED and higher. dbOrigSize is a copy of the dbSize
** variable at the start of the transaction. It is used during rollback,
** and to determine whether or not pages need to be journalled before
@@ -50938,12 +57270,12 @@ struct PagerSavepoint {
** Throughout a write-transaction, dbFileSize contains the size of
** the file on disk in pages. It is set to a copy of dbSize when the
** write-transaction is first opened, and updated when VFS calls are made
-** to write or truncate the database file on disk.
+** to write or truncate the database file on disk.
**
-** The only reason the dbFileSize variable is required is to suppress
-** unnecessary calls to xTruncate() after committing a transaction. If,
-** when a transaction is committed, the dbFileSize variable indicates
-** that the database file is larger than the database image (Pager.dbSize),
+** The only reason the dbFileSize variable is required is to suppress
+** unnecessary calls to xTruncate() after committing a transaction. If,
+** when a transaction is committed, the dbFileSize variable indicates
+** that the database file is larger than the database image (Pager.dbSize),
** pager_truncate() is called. The pager_truncate() call uses xFilesize()
** to measure the database file on disk, and then truncates it if required.
** dbFileSize is not used when rolling back a transaction. In this case
@@ -50954,20 +57286,20 @@ struct PagerSavepoint {
** dbHintSize
**
** The dbHintSize variable is used to limit the number of calls made to
-** the VFS xFileControl(FCNTL_SIZE_HINT) method.
+** the VFS xFileControl(FCNTL_SIZE_HINT) method.
**
** dbHintSize is set to a copy of the dbSize variable when a
** write-transaction is opened (at the same time as dbFileSize and
** dbOrigSize). If the xFileControl(FCNTL_SIZE_HINT) method is called,
** dbHintSize is increased to the number of pages that correspond to the
-** size-hint passed to the method call. See pager_write_pagelist() for
+** size-hint passed to the method call. See pager_write_pagelist() for
** details.
**
** errCode
**
** The Pager.errCode variable is only ever used in PAGER_ERROR state. It
-** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode
-** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX
+** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode
+** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX
** sub-codes.
**
** syncFlags, walSyncFlags
@@ -50996,6 +57328,7 @@ struct Pager {
u8 noLock; /* Do not lock (except in WAL mode) */
u8 readOnly; /* True for a read-only database */
u8 memDb; /* True to inhibit all file I/O */
+ u8 memVfs; /* VFS-implemented memory database */
/**************************************************************************
** The following block contains those class members that change during
@@ -51009,7 +57342,7 @@ struct Pager {
u8 eState; /* Pager state (OPEN, READER, WRITER_LOCKED..) */
u8 eLock; /* Current lock held on database file */
u8 changeCountDone; /* Set after incrementing the change-counter */
- u8 setMaster; /* True if a m-j name has been written to jrnl */
+ u8 setSuper; /* Super-jrnl name is written into jrnl */
u8 doNotSpill; /* Do not spill the cache when non-zero */
u8 subjInMemory; /* True to use in-memory sub-journals */
u8 bUseFetch; /* True to use xFetch() */
@@ -51045,25 +57378,20 @@ struct Pager {
i16 nReserve; /* Number of unused bytes at end of each page */
u32 vfsFlags; /* Flags for sqlite3_vfs.xOpen() */
u32 sectorSize; /* Assumed sector size during rollback */
- int pageSize; /* Number of bytes in a page */
Pgno mxPgno; /* Maximum allowed size of the database */
+ Pgno lckPgno; /* Page number for the locking page */
+ i64 pageSize; /* Number of bytes in a page */
i64 journalSizeLimit; /* Size limit for persistent journal files */
char *zFilename; /* Name of the database file */
char *zJournal; /* Name of the journal file */
int (*xBusyHandler)(void*); /* Function to call when busy */
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
- int aStat[4]; /* Total cache hits, misses, writes, spills */
+ u32 aStat[4]; /* Total cache hits, misses, writes, spills */
#ifdef SQLITE_TEST
int nRead; /* Database pages read */
#endif
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */
-#ifdef SQLITE_HAS_CODEC
- void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */
- void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */
- void (*xCodecFree)(void*); /* Destructor for the codec */
- void *pCodec; /* First argument to xCodec... methods */
-#endif
char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */
PCache *pPCache; /* Pointer to page cache object */
#ifndef SQLITE_OMIT_WAL
@@ -51074,7 +57402,7 @@ struct Pager {
/*
** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains
-** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS
+** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS
** or CACHE_WRITE to sqlite3_db_status().
*/
#define PAGER_STAT_HIT 0
@@ -51132,7 +57460,7 @@ static const unsigned char aJournalMagic[] = {
#define JOURNAL_PG_SZ(pPager) ((pPager->pageSize) + 8)
/*
-** The journal header size for this pager. This is usually the same
+** The journal header size for this pager. This is usually the same
** size as a single disk sector. See also setSectorSize().
*/
#define JOURNAL_HDR_SZ(pPager) (pPager->sectorSize)
@@ -51160,11 +57488,6 @@ static const unsigned char aJournalMagic[] = {
#endif
/*
-** The maximum legal page number is (2^31 - 1).
-*/
-#define PAGER_MAX_PGNO 2147483647
-
-/*
** The argument to this macro is a file descriptor (type sqlite3_file*).
** Return 0 if it is not open, or non-zero (but not 1) if it is.
**
@@ -51193,9 +57516,8 @@ SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
#ifndef SQLITE_OMIT_WAL
if( pPager->pWal ){
u32 iRead = 0;
- int rc;
- rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
- return (rc==SQLITE_OK && iRead==0);
+ (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
+ return iRead==0;
}
#endif
return 1;
@@ -51212,7 +57534,7 @@ SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
# define pagerBeginReadTransaction(z) SQLITE_OK
#endif
-#ifndef NDEBUG
+#ifndef NDEBUG
/*
** Usage:
**
@@ -51241,25 +57563,25 @@ static int assert_pager_state(Pager *p){
assert( p->tempFile==0 || p->eLock==EXCLUSIVE_LOCK );
assert( p->tempFile==0 || pPager->changeCountDone );
- /* If the useJournal flag is clear, the journal-mode must be "OFF".
+ /* If the useJournal flag is clear, the journal-mode must be "OFF".
** And if the journal-mode is "OFF", the journal file must not be open.
*/
assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal );
assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) );
- /* Check that MEMDB implies noSync. And an in-memory journal. Since
- ** this means an in-memory pager performs no IO at all, it cannot encounter
- ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing
- ** a journal file. (although the in-memory journal implementation may
- ** return SQLITE_IOERR_NOMEM while the journal file is being written). It
- ** is therefore not possible for an in-memory pager to enter the ERROR
+ /* Check that MEMDB implies noSync. And an in-memory journal. Since
+ ** this means an in-memory pager performs no IO at all, it cannot encounter
+ ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing
+ ** a journal file. (although the in-memory journal implementation may
+ ** return SQLITE_IOERR_NOMEM while the journal file is being written). It
+ ** is therefore not possible for an in-memory pager to enter the ERROR
** state.
*/
if( MEMDB ){
assert( !isOpen(p->fd) );
assert( p->noSync );
- assert( p->journalMode==PAGER_JOURNALMODE_OFF
- || p->journalMode==PAGER_JOURNALMODE_MEMORY
+ assert( p->journalMode==PAGER_JOURNALMODE_OFF
+ || p->journalMode==PAGER_JOURNALMODE_MEMORY
);
assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN );
assert( pagerUseWal(p)==0 );
@@ -51293,7 +57615,7 @@ static int assert_pager_state(Pager *p){
assert( pPager->dbSize==pPager->dbOrigSize );
assert( pPager->dbOrigSize==pPager->dbFileSize );
assert( pPager->dbOrigSize==pPager->dbHintSize );
- assert( pPager->setMaster==0 );
+ assert( pPager->setSuper==0 );
break;
case PAGER_WRITER_CACHEMOD:
@@ -51306,9 +57628,9 @@ static int assert_pager_state(Pager *p){
** to journal_mode=wal.
*/
assert( p->eLock>=RESERVED_LOCK );
- assert( isOpen(p->jfd)
- || p->journalMode==PAGER_JOURNALMODE_OFF
- || p->journalMode==PAGER_JOURNALMODE_WAL
+ assert( isOpen(p->jfd)
+ || p->journalMode==PAGER_JOURNALMODE_OFF
+ || p->journalMode==PAGER_JOURNALMODE_WAL
);
}
assert( pPager->dbOrigSize==pPager->dbFileSize );
@@ -51320,9 +57642,9 @@ static int assert_pager_state(Pager *p){
assert( pPager->errCode==SQLITE_OK );
assert( !pagerUseWal(pPager) );
assert( p->eLock>=EXCLUSIVE_LOCK );
- assert( isOpen(p->jfd)
- || p->journalMode==PAGER_JOURNALMODE_OFF
- || p->journalMode==PAGER_JOURNALMODE_WAL
+ assert( isOpen(p->jfd)
+ || p->journalMode==PAGER_JOURNALMODE_OFF
+ || p->journalMode==PAGER_JOURNALMODE_WAL
|| (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
);
assert( pPager->dbOrigSize<=pPager->dbHintSize );
@@ -51332,9 +57654,9 @@ static int assert_pager_state(Pager *p){
assert( p->eLock==EXCLUSIVE_LOCK );
assert( pPager->errCode==SQLITE_OK );
assert( !pagerUseWal(pPager) );
- assert( isOpen(p->jfd)
- || p->journalMode==PAGER_JOURNALMODE_OFF
- || p->journalMode==PAGER_JOURNALMODE_WAL
+ assert( isOpen(p->jfd)
+ || p->journalMode==PAGER_JOURNALMODE_OFF
+ || p->journalMode==PAGER_JOURNALMODE_WAL
|| (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
);
break;
@@ -51353,7 +57675,7 @@ static int assert_pager_state(Pager *p){
}
#endif /* ifndef NDEBUG */
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_DEBUG
/*
** Return a pointer to a human readable string in a static buffer
** containing the state of the Pager object passed as an argument. This
@@ -51423,11 +57745,7 @@ static void setGetterMethod(Pager *pPager){
if( pPager->errCode ){
pPager->xGet = getPageError;
#if SQLITE_MAX_MMAP_SIZE>0
- }else if( USEFETCH(pPager)
-#ifdef SQLITE_HAS_CODEC
- && pPager->xCodec==0
-#endif
- ){
+ }else if( USEFETCH(pPager) ){
pPager->xGet = getPageMMap;
#endif /* SQLITE_MAX_MMAP_SIZE>0 */
}else{
@@ -51452,6 +57770,9 @@ static int subjRequiresPage(PgHdr *pPg){
for(i=0; i<pPager->nSavepoint; i++){
p = &pPager->aSavepoint[i];
if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){
+ for(i=i+1; i<pPager->nSavepoint; i++){
+ pPager->aSavepoint[i].bTruncateOnRelease = 0;
+ }
return 1;
}
}
@@ -51505,7 +57826,7 @@ static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
** succeeds, set the Pager.eLock variable to match the (attempted) new lock.
**
** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is
-** called, do not modify it. See the comment above the #define of
+** called, do not modify it. See the comment above the #define of
** UNKNOWN_LOCK for an explanation of this.
*/
static int pagerUnlockDb(Pager *pPager, int eLock){
@@ -51522,17 +57843,18 @@ static int pagerUnlockDb(Pager *pPager, int eLock){
}
IOTRACE(("UNLOCK %p %d\n", pPager, eLock))
}
+ pPager->changeCountDone = pPager->tempFile; /* ticket fb3b3024ea238d5c */
return rc;
}
/*
** Lock the database file to level eLock, which must be either SHARED_LOCK,
** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the
-** Pager.eLock variable to the new locking state.
+** Pager.eLock variable to the new locking state.
**
-** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is
-** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK.
-** See the comment above the #define of UNKNOWN_LOCK for an explanation
+** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is
+** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK.
+** See the comment above the #define of UNKNOWN_LOCK for an explanation
** of this.
*/
static int pagerLockDb(Pager *pPager, int eLock){
@@ -51559,7 +57881,7 @@ static int pagerLockDb(Pager *pPager, int eLock){
** (b) the value returned by OsSectorSize() is less than or equal
** to the page size.
**
-** If it can be used, then the value returned is the size of the journal
+** If it can be used, then the value returned is the size of the journal
** file when it contains rollback data for exactly one page.
**
** The atomic-batch-write optimization can be used if OsDeviceCharacteristics()
@@ -51650,72 +57972,73 @@ static void checkPage(PgHdr *pPg){
/*
** When this is called the journal file for pager pPager must be open.
-** This function attempts to read a master journal file name from the
-** end of the file and, if successful, copies it into memory supplied
-** by the caller. See comments above writeMasterJournal() for the format
-** used to store a master journal file name at the end of a journal file.
+** This function attempts to read a super-journal file name from the
+** end of the file and, if successful, copies it into memory supplied
+** by the caller. See comments above writeSuperJournal() for the format
+** used to store a super-journal file name at the end of a journal file.
**
-** zMaster must point to a buffer of at least nMaster bytes allocated by
+** zSuper must point to a buffer of at least nSuper bytes allocated by
** the caller. This should be sqlite3_vfs.mxPathname+1 (to ensure there is
-** enough space to write the master journal name). If the master journal
-** name in the journal is longer than nMaster bytes (including a
-** nul-terminator), then this is handled as if no master journal name
+** enough space to write the super-journal name). If the super-journal
+** name in the journal is longer than nSuper bytes (including a
+** nul-terminator), then this is handled as if no super-journal name
** were present in the journal.
**
-** If a master journal file name is present at the end of the journal
-** file, then it is copied into the buffer pointed to by zMaster. A
-** nul-terminator byte is appended to the buffer following the master
-** journal file name.
+** If a super-journal file name is present at the end of the journal
+** file, then it is copied into the buffer pointed to by zSuper. A
+** nul-terminator byte is appended to the buffer following the
+** super-journal file name.
**
-** If it is determined that no master journal file name is present
-** zMaster[0] is set to 0 and SQLITE_OK returned.
+** If it is determined that no super-journal file name is present
+** zSuper[0] is set to 0 and SQLITE_OK returned.
**
** If an error occurs while reading from the journal file, an SQLite
** error code is returned.
*/
-static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){
+static int readSuperJournal(sqlite3_file *pJrnl, char *zSuper, u32 nSuper){
int rc; /* Return code */
- u32 len; /* Length in bytes of master journal name */
+ u32 len; /* Length in bytes of super-journal name */
i64 szJ; /* Total size in bytes of journal file pJrnl */
u32 cksum; /* MJ checksum value read from journal */
u32 u; /* Unsigned loop counter */
unsigned char aMagic[8]; /* A buffer to hold the magic header */
- zMaster[0] = '\0';
+ zSuper[0] = '\0';
if( SQLITE_OK!=(rc = sqlite3OsFileSize(pJrnl, &szJ))
|| szJ<16
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len))
- || len>=nMaster
+ || len>=nSuper
|| len>szJ-16
- || len==0
+ || len==0
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum))
|| SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8))
|| memcmp(aMagic, aJournalMagic, 8)
- || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, zMaster, len, szJ-16-len))
+ || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, zSuper, len, szJ-16-len))
){
return rc;
}
- /* See if the checksum matches the master journal name */
+ /* See if the checksum matches the super-journal name */
for(u=0; u<len; u++){
- cksum -= zMaster[u];
+ cksum -= zSuper[u];
}
if( cksum ){
/* If the checksum doesn't add up, then one or more of the disk sectors
- ** containing the master journal filename is corrupted. This means
+ ** containing the super-journal filename is corrupted. This means
** definitely roll back, so just return SQLITE_OK and report a (nul)
- ** master-journal filename.
+ ** super-journal filename.
*/
len = 0;
}
- zMaster[len] = '\0';
-
+ zSuper[len] = '\0';
+ zSuper[len+1] = '\0';
+
return SQLITE_OK;
}
/*
-** Return the offset of the sector boundary at or immediately
-** following the value in pPager->journalOff, assuming a sector
+** Return the offset of the sector boundary at or immediately
+** following the value in pPager->journalOff, assuming a sector
** size of pPager->sectorSize bytes.
**
** i.e for a sector size of 512:
@@ -51726,7 +58049,7 @@ static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){
** 512 512
** 100 512
** 2000 2048
-**
+**
*/
static i64 journalHdrOffset(Pager *pPager){
i64 offset = 0;
@@ -51748,12 +58071,12 @@ static i64 journalHdrOffset(Pager *pPager){
**
** If doTruncate is non-zero or the Pager.journalSizeLimit variable is
** set to 0, then truncate the journal file to zero bytes in size. Otherwise,
-** zero the 28-byte header at the start of the journal file. In either case,
-** if the pager is not in no-sync mode, sync the journal file immediately
+** zero the 28-byte header at the start of the journal file. In either case,
+** if the pager is not in no-sync mode, sync the journal file immediately
** after writing or truncating it.
**
** If Pager.journalSizeLimit is set to a positive, non-zero value, and
-** following the truncation or zeroing described above the size of the
+** following the truncation or zeroing described above the size of the
** journal file in bytes is larger than this value, then truncate the
** journal file to Pager.journalSizeLimit bytes. The journal file does
** not need to be synced following this operation.
@@ -51779,8 +58102,8 @@ static int zeroJournalHdr(Pager *pPager, int doTruncate){
rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags);
}
- /* At this point the transaction is committed but the write lock
- ** is still held on the file. If there is a size limit configured for
+ /* At this point the transaction is committed but the write lock
+ ** is still held on the file. If there is a size limit configured for
** the persistent journal and the journal file currently consumes more
** space than that limit allows for, truncate it now. There is no need
** to sync the file following this operation.
@@ -51808,7 +58131,7 @@ static int zeroJournalHdr(Pager *pPager, int doTruncate){
** - 4 bytes: Initial database page count.
** - 4 bytes: Sector size used by the process that wrote this journal.
** - 4 bytes: Database page size.
-**
+**
** Followed by (JOURNAL_HDR_SZ - 28) bytes of unused space.
*/
static int writeJournalHdr(Pager *pPager){
@@ -51824,8 +58147,8 @@ static int writeJournalHdr(Pager *pPager){
nHeader = JOURNAL_HDR_SZ(pPager);
}
- /* If there are active savepoints and any of them were created
- ** since the most recent journal header was written, update the
+ /* If there are active savepoints and any of them were created
+ ** since the most recent journal header was written, update the
** PagerSavepoint.iHdrOffset fields now.
*/
for(ii=0; ii<pPager->nSavepoint; ii++){
@@ -51836,10 +58159,10 @@ static int writeJournalHdr(Pager *pPager){
pPager->journalHdr = pPager->journalOff = journalHdrOffset(pPager);
- /*
+ /*
** Write the nRec Field - the number of page records that follow this
** journal header. Normally, zero is written to this value at this time.
- ** After the records are added to the journal (and the journal synced,
+ ** After the records are added to the journal (and the journal synced,
** if in full-sync mode), the zero is overwritten with the true number
** of records (see syncJournal()).
**
@@ -51858,7 +58181,7 @@ static int writeJournalHdr(Pager *pPager){
*/
assert( isOpen(pPager->fd) || pPager->noSync );
if( pPager->noSync || (pPager->journalMode==PAGER_JOURNALMODE_MEMORY)
- || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
+ || (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_SAFE_APPEND)
){
memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff);
@@ -51866,9 +58189,32 @@ static int writeJournalHdr(Pager *pPager){
memset(zHeader, 0, sizeof(aJournalMagic)+4);
}
- /* The random check-hash initializer */
- sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
+
+
+ /* The random check-hash initializer */
+ if( pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){
+ sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
+ }
+#ifdef SQLITE_DEBUG
+ else{
+ /* The Pager.cksumInit variable is usually randomized above to protect
+ ** against there being existing records in the journal file. This is
+ ** dangerous, as following a crash they may be mistaken for records
+ ** written by the current transaction and rolled back into the database
+ ** file, causing corruption. The following assert statements verify
+ ** that this is not required in "journal_mode=memory" mode, as in that
+ ** case the journal file is always 0 bytes in size at this point.
+ ** It is advantageous to avoid the sqlite3_randomness() call if possible
+ ** as it takes the global PRNG mutex. */
+ i64 sz = 0;
+ sqlite3OsFileSize(pPager->jfd, &sz);
+ assert( sz==0 );
+ assert( pPager->journalOff==journalHdrOffset(pPager) );
+ assert( sqlite3JournalIsInMemory(pPager->jfd) );
+ }
+#endif
put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit);
+
/* The initial database size */
put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbOrigSize);
/* The assumed sector size for this process */
@@ -51885,23 +58231,23 @@ static int writeJournalHdr(Pager *pPager){
memset(&zHeader[sizeof(aJournalMagic)+20], 0,
nHeader-(sizeof(aJournalMagic)+20));
- /* In theory, it is only necessary to write the 28 bytes that the
- ** journal header consumes to the journal file here. Then increment the
- ** Pager.journalOff variable by JOURNAL_HDR_SZ so that the next
+ /* In theory, it is only necessary to write the 28 bytes that the
+ ** journal header consumes to the journal file here. Then increment the
+ ** Pager.journalOff variable by JOURNAL_HDR_SZ so that the next
** record is written to the following sector (leaving a gap in the file
** that will be implicitly filled in by the OS).
**
- ** However it has been discovered that on some systems this pattern can
+ ** However it has been discovered that on some systems this pattern can
** be significantly slower than contiguously writing data to the file,
- ** even if that means explicitly writing data to the block of
+ ** even if that means explicitly writing data to the block of
** (JOURNAL_HDR_SZ - 28) bytes that will not be used. So that is what
- ** is done.
+ ** is done.
**
- ** The loop is required here in case the sector-size is larger than the
+ ** The loop is required here in case the sector-size is larger than the
** database page size. Since the zHeader buffer is only Pager.pageSize
** bytes in size, more than one call to sqlite3OsWrite() may be required
** to populate the entire journal header sector.
- */
+ */
for(nWrite=0; rc==SQLITE_OK&&nWrite<JOURNAL_HDR_SZ(pPager); nWrite+=nHeader){
IOTRACE(("JHDR %p %lld %d\n", pPager, pPager->journalHdr, nHeader))
rc = sqlite3OsWrite(pPager->jfd, zHeader, nHeader, pPager->journalOff);
@@ -51999,29 +58345,29 @@ static int readJournalHdr(
/* Check that the values read from the page-size and sector-size fields
** are within range. To be 'in range', both values need to be a power
- ** of two greater than or equal to 512 or 32, and not greater than their
+ ** of two greater than or equal to 512 or 32, and not greater than their
** respective compile time maximum limits.
*/
if( iPageSize<512 || iSectorSize<32
|| iPageSize>SQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE
- || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0
+ || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0
){
- /* If the either the page-size or sector-size in the journal-header is
- ** invalid, then the process that wrote the journal-header must have
- ** crashed before the header was synced. In this case stop reading
+ /* If the either the page-size or sector-size in the journal-header is
+ ** invalid, then the process that wrote the journal-header must have
+ ** crashed before the header was synced. In this case stop reading
** the journal file here.
*/
return SQLITE_DONE;
}
- /* Update the page-size to match the value read from the journal.
- ** Use a testcase() macro to make sure that malloc failure within
+ /* Update the page-size to match the value read from the journal.
+ ** Use a testcase() macro to make sure that malloc failure within
** PagerSetPagesize() is tested.
*/
rc = sqlite3PagerSetPagesize(pPager, &iPageSize, -1);
testcase( rc!=SQLITE_OK );
- /* Update the assumed sector-size to match the value used by
+ /* Update the assumed sector-size to match the value used by
** the process that created this journal. If this journal was
** created by a process other than this one, then this routine
** is being called from within pager_playback(). The local value
@@ -52036,50 +58382,50 @@ static int readJournalHdr(
/*
-** Write the supplied master journal name into the journal file for pager
-** pPager at the current location. The master journal name must be the last
+** Write the supplied super-journal name into the journal file for pager
+** pPager at the current location. The super-journal name must be the last
** thing written to a journal file. If the pager is in full-sync mode, the
** journal file descriptor is advanced to the next sector boundary before
** anything is written. The format is:
**
-** + 4 bytes: PAGER_MJ_PGNO.
-** + N bytes: Master journal filename in utf-8.
-** + 4 bytes: N (length of master journal name in bytes, no nul-terminator).
-** + 4 bytes: Master journal name checksum.
+** + 4 bytes: PAGER_SJ_PGNO.
+** + N bytes: super-journal filename in utf-8.
+** + 4 bytes: N (length of super-journal name in bytes, no nul-terminator).
+** + 4 bytes: super-journal name checksum.
** + 8 bytes: aJournalMagic[].
**
-** The master journal page checksum is the sum of the bytes in the master
-** journal name, where each byte is interpreted as a signed 8-bit integer.
+** The super-journal page checksum is the sum of the bytes in the super-journal
+** name, where each byte is interpreted as a signed 8-bit integer.
**
-** If zMaster is a NULL pointer (occurs for a single database transaction),
+** If zSuper is a NULL pointer (occurs for a single database transaction),
** this call is a no-op.
*/
-static int writeMasterJournal(Pager *pPager, const char *zMaster){
+static int writeSuperJournal(Pager *pPager, const char *zSuper){
int rc; /* Return code */
- int nMaster; /* Length of string zMaster */
+ int nSuper; /* Length of string zSuper */
i64 iHdrOff; /* Offset of header in journal file */
i64 jrnlSize; /* Size of journal file on disk */
- u32 cksum = 0; /* Checksum of string zMaster */
+ u32 cksum = 0; /* Checksum of string zSuper */
- assert( pPager->setMaster==0 );
+ assert( pPager->setSuper==0 );
assert( !pagerUseWal(pPager) );
- if( !zMaster
- || pPager->journalMode==PAGER_JOURNALMODE_MEMORY
+ if( !zSuper
+ || pPager->journalMode==PAGER_JOURNALMODE_MEMORY
|| !isOpen(pPager->jfd)
){
return SQLITE_OK;
}
- pPager->setMaster = 1;
+ pPager->setSuper = 1;
assert( pPager->journalHdr <= pPager->journalOff );
- /* Calculate the length in bytes and the checksum of zMaster */
- for(nMaster=0; zMaster[nMaster]; nMaster++){
- cksum += zMaster[nMaster];
+ /* Calculate the length in bytes and the checksum of zSuper */
+ for(nSuper=0; zSuper[nSuper]; nSuper++){
+ cksum += zSuper[nSuper];
}
/* If in full-sync mode, advance to the next disk sector before writing
- ** the master journal name. This is in case the previous page written to
+ ** the super-journal name. This is in case the previous page written to
** the journal has already been synced.
*/
if( pPager->fullSync ){
@@ -52087,30 +58433,30 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
}
iHdrOff = pPager->journalOff;
- /* Write the master journal data to the end of the journal file. If
+ /* Write the super-journal data to the end of the journal file. If
** an error occurs, return the error code to the caller.
*/
- if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_MJ_PGNO(pPager))))
- || (0 != (rc = sqlite3OsWrite(pPager->jfd, zMaster, nMaster, iHdrOff+4)))
- || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster)))
- || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum)))
+ if( (0 != (rc = write32bits(pPager->jfd, iHdrOff, PAGER_SJ_PGNO(pPager))))
+ || (0 != (rc = sqlite3OsWrite(pPager->jfd, zSuper, nSuper, iHdrOff+4)))
+ || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper, nSuper)))
+ || (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nSuper+4, cksum)))
|| (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8,
- iHdrOff+4+nMaster+8)))
+ iHdrOff+4+nSuper+8)))
){
return rc;
}
- pPager->journalOff += (nMaster+20);
+ pPager->journalOff += (nSuper+20);
- /* If the pager is in peristent-journal mode, then the physical
- ** journal-file may extend past the end of the master-journal name
- ** and 8 bytes of magic data just written to the file. This is
+ /* If the pager is in persistent-journal mode, then the physical
+ ** journal-file may extend past the end of the super-journal name
+ ** and 8 bytes of magic data just written to the file. This is
** dangerous because the code to rollback a hot-journal file
- ** will not be able to find the master-journal name to determine
- ** whether or not the journal is hot.
+ ** will not be able to find the super-journal name to determine
+ ** whether or not the journal is hot.
**
- ** Easiest thing to do in this scenario is to truncate the journal
+ ** Easiest thing to do in this scenario is to truncate the journal
** file to the required size.
- */
+ */
if( SQLITE_OK==(rc = sqlite3OsFileSize(pPager->jfd, &jrnlSize))
&& jrnlSize>pPager->journalOff
){
@@ -52155,7 +58501,7 @@ static void releaseAllSavepoints(Pager *pPager){
}
/*
-** Set the bit number pgno in the PagerSavepoint.pInSavepoint
+** Set the bit number pgno in the PagerSavepoint.pInSavepoint
** bitvecs of all open savepoints. Return SQLITE_OK if successful
** or SQLITE_NOMEM if a malloc failure occurs.
*/
@@ -52184,8 +58530,8 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){
** not exhibit the UNDELETABLE_WHEN_OPEN property, the journal file is
** closed (if it is open).
**
-** If the pager is in ERROR state when this function is called, the
-** contents of the pager cache are discarded before switching back to
+** If the pager is in ERROR state when this function is called, the
+** contents of the pager cache are discarded before switching back to
** the OPEN state. Regardless of whether the pager is in exclusive-mode
** or not, any journal file left in the file-system will be treated
** as a hot-journal and rolled back the next time a read-transaction
@@ -52193,9 +58539,9 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){
*/
static void pager_unlock(Pager *pPager){
- assert( pPager->eState==PAGER_READER
- || pPager->eState==PAGER_OPEN
- || pPager->eState==PAGER_ERROR
+ assert( pPager->eState==PAGER_READER
+ || pPager->eState==PAGER_OPEN
+ || pPager->eState==PAGER_ERROR
);
sqlite3BitvecDestroy(pPager->pInJournal);
@@ -52242,7 +58588,6 @@ static void pager_unlock(Pager *pPager){
** code is cleared and the cache reset in the block below.
*/
assert( pPager->errCode || pPager->eState!=PAGER_ERROR );
- pPager->changeCountDone = 0;
pPager->eState = PAGER_OPEN;
}
@@ -52267,23 +58612,23 @@ static void pager_unlock(Pager *pPager){
pPager->journalOff = 0;
pPager->journalHdr = 0;
- pPager->setMaster = 0;
+ pPager->setSuper = 0;
}
/*
** This function is called whenever an IOERR or FULL error that requires
-** the pager to transition into the ERROR state may ahve occurred.
-** The first argument is a pointer to the pager structure, the second
-** the error-code about to be returned by a pager API function. The
-** value returned is a copy of the second argument to this function.
+** the pager to transition into the ERROR state may have occurred.
+** The first argument is a pointer to the pager structure, the second
+** the error-code about to be returned by a pager API function. The
+** value returned is a copy of the second argument to this function.
**
** If the second argument is SQLITE_FULL, SQLITE_IOERR or one of the
** IOERR sub-codes, the pager enters the ERROR state and the error code
** is stored in Pager.errCode. While the pager remains in the ERROR state,
** all major API calls on the Pager will immediately return Pager.errCode.
**
-** The ERROR state indicates that the contents of the pager-cache
-** cannot be trusted. This state can be cleared by completely discarding
+** The ERROR state indicates that the contents of the pager-cache
+** cannot be trusted. This state can be cleared by completely discarding
** the contents of the pager-cache. If a transaction was active when
** the persistent error occurred, then the rollback journal may need
** to be replayed to restore the contents of the database file (as if
@@ -52331,27 +58676,27 @@ static int pagerFlushOnCommit(Pager *pPager, int bCommit){
}
/*
-** This routine ends a transaction. A transaction is usually ended by
-** either a COMMIT or a ROLLBACK operation. This routine may be called
+** This routine ends a transaction. A transaction is usually ended by
+** either a COMMIT or a ROLLBACK operation. This routine may be called
** after rollback of a hot-journal, or if an error occurs while opening
** the journal file or writing the very first journal-header of a
** database transaction.
-**
+**
** This routine is never called in PAGER_ERROR state. If it is called
** in PAGER_NONE or PAGER_SHARED state and the lock held is less
** exclusive than a RESERVED lock, it is a no-op.
**
** Otherwise, any active savepoints are released.
**
-** If the journal file is open, then it is "finalized". Once a journal
-** file has been finalized it is not possible to use it to roll back a
+** If the journal file is open, then it is "finalized". Once a journal
+** file has been finalized it is not possible to use it to roll back a
** transaction. Nor will it be considered to be a hot-journal by this
** or any other database connection. Exactly how a journal is finalized
** depends on whether or not the pager is running in exclusive mode and
** the current journal-mode (Pager.journalMode value), as follows:
**
** journalMode==MEMORY
-** Journal file descriptor is simply closed. This destroys an
+** Journal file descriptor is simply closed. This destroys an
** in-memory journal.
**
** journalMode==TRUNCATE
@@ -52371,19 +58716,19 @@ static int pagerFlushOnCommit(Pager *pPager, int bCommit){
** journalMode==PERSIST is used instead.
**
** After the journal is finalized, the pager moves to PAGER_READER state.
-** If running in non-exclusive rollback mode, the lock on the file is
+** If running in non-exclusive rollback mode, the lock on the file is
** downgraded to a SHARED_LOCK.
**
** SQLITE_OK is returned if no error occurs. If an error occurs during
** any of the IO operations to finalize the journal file or unlock the
-** database then the IO error code is returned to the user. If the
+** database then the IO error code is returned to the user. If the
** operation to finalize the journal file fails, then the code still
** tries to unlock the database file if not in exclusive mode. If the
** unlock operation fails as well, then the first error code related
** to the first error encountered (the journal finalization one) is
** returned.
*/
-static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
+static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
int rc = SQLITE_OK; /* Error code from journal finalization operation */
int rc2 = SQLITE_OK; /* Error code from db file unlock operation */
@@ -52395,9 +58740,9 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
** 1. After a successful hot-journal rollback, it is called with
** eState==PAGER_NONE and eLock==EXCLUSIVE_LOCK.
**
- ** 2. If a connection with locking_mode=exclusive holding an EXCLUSIVE
+ ** 2. If a connection with locking_mode=exclusive holding an EXCLUSIVE
** lock switches back to locking_mode=normal and then executes a
- ** read-transaction, this function is called with eState==PAGER_READER
+ ** read-transaction, this function is called with eState==PAGER_READER
** and eLock==EXCLUSIVE_LOCK when the read-transaction is closed.
*/
assert( assert_pager_state(pPager) );
@@ -52407,7 +58752,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
}
releaseAllSavepoints(pPager);
- assert( isOpen(pPager->jfd) || pPager->pInJournal==0
+ assert( isOpen(pPager->jfd) || pPager->pInJournal==0
|| (sqlite3OsDeviceCharacteristics(pPager->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
);
if( isOpen(pPager->jfd) ){
@@ -52435,7 +58780,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
|| (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
){
- rc = zeroJournalHdr(pPager, hasMaster||pPager->tempFile);
+ rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile);
pPager->journalOff = 0;
}else{
/* This branch may be executed with Pager.journalMode==MEMORY if
@@ -52445,9 +58790,9 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
*/
int bDelete = !pPager->tempFile;
assert( sqlite3JournalIsInMemory(pPager->jfd)==0 );
- assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE
- || pPager->journalMode==PAGER_JOURNALMODE_MEMORY
- || pPager->journalMode==PAGER_JOURNALMODE_WAL
+ assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE
+ || pPager->journalMode==PAGER_JOURNALMODE_MEMORY
+ || pPager->journalMode==PAGER_JOURNALMODE_WAL
);
sqlite3OsClose(pPager->jfd);
if( bDelete ){
@@ -52480,8 +58825,8 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
}
if( pagerUseWal(pPager) ){
- /* Drop the WAL write-lock, if any. Also, if the connection was in
- ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE
+ /* Drop the WAL write-lock, if any. Also, if the connection was in
+ ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE
** lock held on the database file.
*/
rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
@@ -52489,7 +58834,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
}else if( rc==SQLITE_OK && bCommit && pPager->dbFileSize>pPager->dbSize ){
/* This branch is taken when committing a transaction in rollback-journal
** mode if the database file on disk is larger than the database image.
- ** At this point the journal has been finalized and the transaction
+ ** At this point the journal has been finalized and the transaction
** successfully committed, but the EXCLUSIVE lock is still held on the
** file. So it is safe to truncate the database file to its minimum
** required size. */
@@ -52502,32 +58847,34 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
}
- if( !pPager->exclusiveMode
+ if( !pPager->exclusiveMode
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
){
rc2 = pagerUnlockDb(pPager, SHARED_LOCK);
- pPager->changeCountDone = 0;
}
pPager->eState = PAGER_READER;
- pPager->setMaster = 0;
+ pPager->setSuper = 0;
return (rc==SQLITE_OK?rc2:rc);
}
+/* Forward reference */
+static int pager_playback(Pager *pPager, int isHot);
+
/*
-** Execute a rollback if a transaction is active and unlock the
-** database file.
+** Execute a rollback if a transaction is active and unlock the
+** database file.
**
-** If the pager has already entered the ERROR state, do not attempt
+** If the pager has already entered the ERROR state, do not attempt
** the rollback at this time. Instead, pager_unlock() is called. The
** call to pager_unlock() will discard all in-memory pages, unlock
-** the database file and move the pager back to OPEN state. If this
-** means that there is a hot-journal left in the file-system, the next
-** connection to obtain a shared lock on the pager (which may be this one)
+** the database file and move the pager back to OPEN state. If this
+** means that there is a hot-journal left in the file-system, the next
+** connection to obtain a shared lock on the pager (which may be this one)
** will roll it back.
**
** If the pager has not already entered the ERROR state, but an IO or
-** malloc error occurs during a rollback, then this will itself cause
+** malloc error occurs during a rollback, then this will itself cause
** the pager to enter the ERROR state. Which will be cleared by the
** call to pager_unlock(), as described above.
*/
@@ -52542,16 +58889,31 @@ static void pagerUnlockAndRollback(Pager *pPager){
assert( pPager->eState==PAGER_READER );
pager_end_transaction(pPager, 0, 0);
}
+ }else if( pPager->eState==PAGER_ERROR
+ && pPager->journalMode==PAGER_JOURNALMODE_MEMORY
+ && isOpen(pPager->jfd)
+ ){
+ /* Special case for a ROLLBACK due to I/O error with an in-memory
+ ** journal: We have to rollback immediately, before the journal is
+ ** closed, because once it is closed, all content is forgotten. */
+ int errCode = pPager->errCode;
+ u8 eLock = pPager->eLock;
+ pPager->eState = PAGER_OPEN;
+ pPager->errCode = SQLITE_OK;
+ pPager->eLock = EXCLUSIVE_LOCK;
+ pager_playback(pPager, 1);
+ pPager->errCode = errCode;
+ pPager->eLock = eLock;
}
pager_unlock(pPager);
}
/*
** Parameter aData must point to a buffer of pPager->pageSize bytes
-** of data. Compute and return a checksum based ont the contents of the
+** of data. Compute and return a checksum based on the contents of the
** page of data and the current value of pPager->cksumInit.
**
-** This is not a real checksum. It is really just the sum of the
+** This is not a real checksum. It is really just the sum of the
** random initial value (pPager->cksumInit) and every 200th byte
** of the page data, starting with byte offset (pPager->pageSize%200).
** Each byte is interpreted as an 8-bit unsigned integer.
@@ -52559,8 +58921,8 @@ static void pagerUnlockAndRollback(Pager *pPager){
** Changing the formula used to compute this checksum results in an
** incompatible journal file format.
**
-** If journal corruption occurs due to a power failure, the most likely
-** scenario is that one end or the other of the record will be changed.
+** If journal corruption occurs due to a power failure, the most likely
+** scenario is that one end or the other of the record will be changed.
** It is much less likely that the two ends of the journal record will be
** correct and the middle be corrupt. Thus, this "checksum" scheme,
** though fast and simple, catches the mostly likely kind of corruption.
@@ -52576,41 +58938,12 @@ static u32 pager_cksum(Pager *pPager, const u8 *aData){
}
/*
-** Report the current page size and number of reserved bytes back
-** to the codec.
-*/
-#ifdef SQLITE_HAS_CODEC
-static void pagerReportSize(Pager *pPager){
- if( pPager->xCodecSizeChng ){
- pPager->xCodecSizeChng(pPager->pCodec, pPager->pageSize,
- (int)pPager->nReserve);
- }
-}
-#else
-# define pagerReportSize(X) /* No-op if we do not support a codec */
-#endif
-
-#ifdef SQLITE_HAS_CODEC
-/*
-** Make sure the number of reserved bits is the same in the destination
-** pager as it is in the source. This comes up when a VACUUM changes the
-** number of reserved bits to the "optimal" amount.
-*/
-SQLITE_PRIVATE void sqlite3PagerAlignReserve(Pager *pDest, Pager *pSrc){
- if( pDest->nReserve!=pSrc->nReserve ){
- pDest->nReserve = pSrc->nReserve;
- pagerReportSize(pDest);
- }
-}
-#endif
-
-/*
** Read a single page from either the journal file (if isMainJrnl==1) or
** from the sub-journal (if isMainJrnl==0) and playback that page.
** The page begins at offset *pOffset into the file. The *pOffset
** value is increased to the start of the next page in the journal.
**
-** The main rollback journal uses checksums - the statement journal does
+** The main rollback journal uses checksums - the statement journal does
** not.
**
** If the page number of the page record read from the (sub-)journal file
@@ -52630,8 +58963,8 @@ SQLITE_PRIVATE void sqlite3PagerAlignReserve(Pager *pDest, Pager *pSrc){
** is successfully read from the (sub-)journal file but appears to be
** corrupted, SQLITE_DONE is returned. Data is considered corrupted in
** two circumstances:
-**
-** * If the record page-number is illegal (0 or PAGER_MJ_PGNO), or
+**
+** * If the record page-number is illegal (0 or PAGER_SJ_PGNO), or
** * If the record is being rolled back from the main journal file
** and the checksum field does not match the record content.
**
@@ -52655,11 +58988,6 @@ static int pager_playback_one_page(
char *aData; /* Temporary storage for the page */
sqlite3_file *jfd; /* The file descriptor for the journal file */
int isSynced; /* True if journal page is synced */
-#ifdef SQLITE_HAS_CODEC
- /* The jrnlEnc flag is true if Journal pages should be passed through
- ** the codec. It is false for pure in-memory journals. */
- const int jrnlEnc = (isMainJrnl || pPager->subjInMemory==0);
-#endif
assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */
assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */
@@ -52670,7 +58998,7 @@ static int pager_playback_one_page(
assert( aData ); /* Temp storage must have already been allocated */
assert( pagerUseWal(pPager)==0 || (!isMainJrnl && isSavepnt) );
- /* Either the state is greater than PAGER_WRITER_CACHEMOD (a transaction
+ /* Either the state is greater than PAGER_WRITER_CACHEMOD (a transaction
** or savepoint rollback done at the request of the caller) or this is
** a hot-journal rollback. If it is a hot-journal rollback, the pager
** is in state OPEN and holds an EXCLUSIVE lock. Hot-journal rollback
@@ -52696,7 +59024,7 @@ static int pager_playback_one_page(
** it could cause invalid data to be written into the journal. We need to
** detect this invalid data (with high probability) and ignore it.
*/
- if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){
+ if( pgno==0 || pgno==PAGER_SJ_PGNO(pPager) ){
assert( !isSavepnt );
return SQLITE_DONE;
}
@@ -52722,7 +59050,6 @@ static int pager_playback_one_page(
*/
if( pgno==1 && pPager->nReserve!=((u8*)aData)[20] ){
pPager->nReserve = ((u8*)aData)[20];
- pagerReportSize(pPager);
}
/* If the pager is in CACHEMOD state, then there must be a copy of this
@@ -52737,7 +59064,7 @@ static int pager_playback_one_page(
** assert()able.
**
** If in WRITER_DBMOD, WRITER_FINISHED or OPEN state, then we update the
- ** pager cache if it exists and the main file. The page is then marked
+ ** pager cache if it exists and the main file. The page is then marked
** not dirty. Since this code is only executed in PAGER_OPEN state for
** a hot-journal rollback, it is guaranteed that the page-cache is empty
** if the pager is in OPEN state.
@@ -52790,43 +59117,29 @@ static int pager_playback_one_page(
** is if the data was just read from an in-memory sub-journal. In that
** case it must be encrypted here before it is copied into the database
** file. */
-#ifdef SQLITE_HAS_CODEC
- if( !jrnlEnc ){
- CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData);
- rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst);
- CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT);
- }else
-#endif
rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst);
if( pgno>pPager->dbFileSize ){
pPager->dbFileSize = pgno;
}
if( pPager->pBackup ){
-#ifdef SQLITE_HAS_CODEC
- if( jrnlEnc ){
- CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT);
- sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData);
- CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT,aData);
- }else
-#endif
sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData);
}
}else if( !isMainJrnl && pPg==0 ){
/* If this is a rollback of a savepoint and data was not written to
** the database and the page is not in-memory, there is a potential
- ** problem. When the page is next fetched by the b-tree layer, it
- ** will be read from the database file, which may or may not be
- ** current.
+ ** problem. When the page is next fetched by the b-tree layer, it
+ ** will be read from the database file, which may or may not be
+ ** current.
**
** There are a couple of different ways this can happen. All are quite
- ** obscure. When running in synchronous mode, this can only happen
+ ** obscure. When running in synchronous mode, this can only happen
** if the page is on the free-list at the start of the transaction, then
** populated, then moved using sqlite3PagerMovepage().
**
** The solution is to add an in-memory page to the cache containing
- ** the data just read from the sub-journal. Mark the page as dirty
- ** and if the pager requires a journal-sync, then mark the page as
+ ** the data just read from the sub-journal. Mark the page as dirty
+ ** and if the pager requires a journal-sync, then mark the page as
** requiring a journal-sync before it is written.
*/
assert( isSavepnt );
@@ -52860,164 +59173,167 @@ static int pager_playback_one_page(
if( pgno==1 ){
memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers));
}
-
- /* Decode the page just read from disk */
-#if SQLITE_HAS_CODEC
- if( jrnlEnc ){ CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT); }
-#endif
sqlite3PcacheRelease(pPg);
}
return rc;
}
/*
-** Parameter zMaster is the name of a master journal file. A single journal
-** file that referred to the master journal file has just been rolled back.
-** This routine checks if it is possible to delete the master journal file,
+** Parameter zSuper is the name of a super-journal file. A single journal
+** file that referred to the super-journal file has just been rolled back.
+** This routine checks if it is possible to delete the super-journal file,
** and does so if it is.
**
-** Argument zMaster may point to Pager.pTmpSpace. So that buffer is not
+** Argument zSuper may point to Pager.pTmpSpace. So that buffer is not
** available for use within this function.
**
-** When a master journal file is created, it is populated with the names
-** of all of its child journals, one after another, formatted as utf-8
-** encoded text. The end of each child journal file is marked with a
-** nul-terminator byte (0x00). i.e. the entire contents of a master journal
+** When a super-journal file is created, it is populated with the names
+** of all of its child journals, one after another, formatted as utf-8
+** encoded text. The end of each child journal file is marked with a
+** nul-terminator byte (0x00). i.e. the entire contents of a super-journal
** file for a transaction involving two databases might be:
**
** "/home/bill/a.db-journal\x00/home/bill/b.db-journal\x00"
**
-** A master journal file may only be deleted once all of its child
+** A super-journal file may only be deleted once all of its child
** journals have been rolled back.
**
-** This function reads the contents of the master-journal file into
+** This function reads the contents of the super-journal file into
** memory and loops through each of the child journal names. For
** each child journal, it checks if:
**
** * if the child journal exists, and if so
-** * if the child journal contains a reference to master journal
-** file zMaster
+** * if the child journal contains a reference to super-journal
+** file zSuper
**
** If a child journal can be found that matches both of the criteria
** above, this function returns without doing anything. Otherwise, if
-** no such child journal can be found, file zMaster is deleted from
+** no such child journal can be found, file zSuper is deleted from
** the file-system using sqlite3OsDelete().
**
** If an IO error within this function, an error code is returned. This
** function allocates memory by calling sqlite3Malloc(). If an allocation
-** fails, SQLITE_NOMEM is returned. Otherwise, if no IO or malloc errors
+** fails, SQLITE_NOMEM is returned. Otherwise, if no IO or malloc errors
** occur, SQLITE_OK is returned.
**
** TODO: This function allocates a single block of memory to load
-** the entire contents of the master journal file. This could be
-** a couple of kilobytes or so - potentially larger than the page
+** the entire contents of the super-journal file. This could be
+** a couple of kilobytes or so - potentially larger than the page
** size.
*/
-static int pager_delmaster(Pager *pPager, const char *zMaster){
+static int pager_delsuper(Pager *pPager, const char *zSuper){
sqlite3_vfs *pVfs = pPager->pVfs;
int rc; /* Return code */
- sqlite3_file *pMaster; /* Malloc'd master-journal file descriptor */
+ sqlite3_file *pSuper; /* Malloc'd super-journal file descriptor */
sqlite3_file *pJournal; /* Malloc'd child-journal file descriptor */
- char *zMasterJournal = 0; /* Contents of master journal file */
- i64 nMasterJournal; /* Size of master journal file */
+ char *zSuperJournal = 0; /* Contents of super-journal file */
+ i64 nSuperJournal; /* Size of super-journal file */
char *zJournal; /* Pointer to one journal within MJ file */
- char *zMasterPtr; /* Space to hold MJ filename from a journal file */
- int nMasterPtr; /* Amount of space allocated to zMasterPtr[] */
+ char *zSuperPtr; /* Space to hold super-journal filename */
+ char *zFree = 0; /* Free this buffer */
+ int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */
- /* Allocate space for both the pJournal and pMaster file descriptors.
- ** If successful, open the master journal file for reading.
+ /* Allocate space for both the pJournal and pSuper file descriptors.
+ ** If successful, open the super-journal file for reading.
*/
- pMaster = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2);
- pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile);
- if( !pMaster ){
+ pSuper = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2);
+ if( !pSuper ){
rc = SQLITE_NOMEM_BKPT;
+ pJournal = 0;
}else{
- const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL);
- rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0);
+ const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL);
+ rc = sqlite3OsOpen(pVfs, zSuper, pSuper, flags, 0);
+ pJournal = (sqlite3_file *)(((u8 *)pSuper) + pVfs->szOsFile);
}
- if( rc!=SQLITE_OK ) goto delmaster_out;
+ if( rc!=SQLITE_OK ) goto delsuper_out;
- /* Load the entire master journal file into space obtained from
- ** sqlite3_malloc() and pointed to by zMasterJournal. Also obtain
- ** sufficient space (in zMasterPtr) to hold the names of master
- ** journal files extracted from regular rollback-journals.
+ /* Load the entire super-journal file into space obtained from
+ ** sqlite3_malloc() and pointed to by zSuperJournal. Also obtain
+ ** sufficient space (in zSuperPtr) to hold the names of super-journal
+ ** files extracted from regular rollback-journals.
*/
- rc = sqlite3OsFileSize(pMaster, &nMasterJournal);
- if( rc!=SQLITE_OK ) goto delmaster_out;
- nMasterPtr = pVfs->mxPathname+1;
- zMasterJournal = sqlite3Malloc(nMasterJournal + nMasterPtr + 1);
- if( !zMasterJournal ){
+ rc = sqlite3OsFileSize(pSuper, &nSuperJournal);
+ if( rc!=SQLITE_OK ) goto delsuper_out;
+ nSuperPtr = pVfs->mxPathname+1;
+ zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2);
+ if( !zFree ){
rc = SQLITE_NOMEM_BKPT;
- goto delmaster_out;
- }
- zMasterPtr = &zMasterJournal[nMasterJournal+1];
- rc = sqlite3OsRead(pMaster, zMasterJournal, (int)nMasterJournal, 0);
- if( rc!=SQLITE_OK ) goto delmaster_out;
- zMasterJournal[nMasterJournal] = 0;
-
- zJournal = zMasterJournal;
- while( (zJournal-zMasterJournal)<nMasterJournal ){
+ goto delsuper_out;
+ }
+ zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0;
+ zSuperJournal = &zFree[4];
+ zSuperPtr = &zSuperJournal[nSuperJournal+2];
+ rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0);
+ if( rc!=SQLITE_OK ) goto delsuper_out;
+ zSuperJournal[nSuperJournal] = 0;
+ zSuperJournal[nSuperJournal+1] = 0;
+
+ zJournal = zSuperJournal;
+ while( (zJournal-zSuperJournal)<nSuperJournal ){
int exists;
rc = sqlite3OsAccess(pVfs, zJournal, SQLITE_ACCESS_EXISTS, &exists);
if( rc!=SQLITE_OK ){
- goto delmaster_out;
+ goto delsuper_out;
}
if( exists ){
- /* One of the journals pointed to by the master journal exists.
- ** Open it and check if it points at the master journal. If
- ** so, return without deleting the master journal file.
+ /* One of the journals pointed to by the super-journal exists.
+ ** Open it and check if it points at the super-journal. If
+ ** so, return without deleting the super-journal file.
+ ** NB: zJournal is really a MAIN_JOURNAL. But call it a
+ ** SUPER_JOURNAL here so that the VFS will not send the zJournal
+ ** name into sqlite3_database_file_object().
*/
int c;
- int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL);
+ int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL);
rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
if( rc!=SQLITE_OK ){
- goto delmaster_out;
+ goto delsuper_out;
}
- rc = readMasterJournal(pJournal, zMasterPtr, nMasterPtr);
+ rc = readSuperJournal(pJournal, zSuperPtr, nSuperPtr);
sqlite3OsClose(pJournal);
if( rc!=SQLITE_OK ){
- goto delmaster_out;
+ goto delsuper_out;
}
- c = zMasterPtr[0]!=0 && strcmp(zMasterPtr, zMaster)==0;
+ c = zSuperPtr[0]!=0 && strcmp(zSuperPtr, zSuper)==0;
if( c ){
- /* We have a match. Do not delete the master journal file. */
- goto delmaster_out;
+ /* We have a match. Do not delete the super-journal file. */
+ goto delsuper_out;
}
}
zJournal += (sqlite3Strlen30(zJournal)+1);
}
-
- sqlite3OsClose(pMaster);
- rc = sqlite3OsDelete(pVfs, zMaster, 0);
-delmaster_out:
- sqlite3_free(zMasterJournal);
- if( pMaster ){
- sqlite3OsClose(pMaster);
+ sqlite3OsClose(pSuper);
+ rc = sqlite3OsDelete(pVfs, zSuper, 0);
+
+delsuper_out:
+ sqlite3_free(zFree);
+ if( pSuper ){
+ sqlite3OsClose(pSuper);
assert( !isOpen(pJournal) );
- sqlite3_free(pMaster);
+ sqlite3_free(pSuper);
}
return rc;
}
/*
-** This function is used to change the actual size of the database
+** This function is used to change the actual size of the database
** file in the file-system. This only happens when committing a transaction,
** or rolling back a transaction (including rolling back a hot-journal).
**
** If the main database file is not open, or the pager is not in either
-** DBMOD or OPEN state, this function is a no-op. Otherwise, the size
-** of the file is changed to nPage pages (nPage*pPager->pageSize bytes).
+** DBMOD or OPEN state, this function is a no-op. Otherwise, the size
+** of the file is changed to nPage pages (nPage*pPager->pageSize bytes).
** If the file on disk is currently larger than nPage pages, then use the VFS
** xTruncate() method to truncate it.
**
-** Or, it might be the case that the file on disk is smaller than
-** nPage pages. Some operating system implementations can get confused if
-** you try to truncate a file to some size that is larger than it
-** currently is, so detect this case and write a single zero byte to
+** Or, it might be the case that the file on disk is smaller than
+** nPage pages. Some operating system implementations can get confused if
+** you try to truncate a file to some size that is larger than it
+** currently is, so detect this case and write a single zero byte to
** the end of the new file instead.
**
** If successful, return SQLITE_OK. If an IO error occurs while modifying
@@ -53027,9 +59343,11 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
int rc = SQLITE_OK;
assert( pPager->eState!=PAGER_ERROR );
assert( pPager->eState!=PAGER_READER );
-
- if( isOpen(pPager->fd)
- && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
+ PAGERTRACE(("Truncate %d npage %u\n", PAGERID(pPager), nPage));
+
+
+ if( isOpen(pPager->fd)
+ && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
i64 currentSize, newSize;
int szPage = pPager->pageSize;
@@ -53045,6 +59363,7 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
memset(pTmp, 0, szPage);
testcase( (newSize-szPage) == currentSize );
testcase( (newSize-szPage) > currentSize );
+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &newSize);
rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage);
}
if( rc==SQLITE_OK ){
@@ -53073,9 +59392,9 @@ SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *pFile){
/*
** Set the value of the Pager.sectorSize variable for the given
** pager based on the value returned by the xSectorSize method
-** of the open database file. The sector size will be used
-** to determine the size and alignment of journal header and
-** master journal pointers within created journal files.
+** of the open database file. The sector size will be used
+** to determine the size and alignment of journal header and
+** super-journal pointers within created journal files.
**
** For temporary files the effective sector size is always 512 bytes.
**
@@ -53097,7 +59416,7 @@ static void setSectorSize(Pager *pPager){
assert( isOpen(pPager->fd) || pPager->tempFile );
if( pPager->tempFile
- || (sqlite3OsDeviceCharacteristics(pPager->fd) &
+ || (sqlite3OsDeviceCharacteristics(pPager->fd) &
SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0
){
/* Sector size doesn't matter for temporary files. Also, the file
@@ -53111,15 +59430,15 @@ static void setSectorSize(Pager *pPager){
/*
** Playback the journal and thus restore the database file to
-** the state it was in before we started making changes.
+** the state it was in before we started making changes.
**
-** The journal file format is as follows:
+** The journal file format is as follows:
**
** (1) 8 byte prefix. A copy of aJournalMagic[].
** (2) 4 byte big-endian integer which is the number of valid page records
** in the journal. If this value is 0xffffffff, then compute the
** number of page records from the journal size.
-** (3) 4 byte big-endian integer which is the initial value for the
+** (3) 4 byte big-endian integer which is the initial value for the
** sanity checksum.
** (4) 4 byte integer which is the number of pages to truncate the
** database to during a rollback.
@@ -53148,7 +59467,7 @@ static void setSectorSize(Pager *pPager){
** from the file size. This value is used when the user selects the
** no-sync option for the journal. A power failure could lead to corruption
** in this case. But for things like temporary table (which will be
-** deleted when the power is restored) we don't care.
+** deleted when the power is restored) we don't care.
**
** If the file opened as the journal file is not a well-formed
** journal file then all pages up to the first corrupted page are rolled
@@ -53160,7 +59479,7 @@ static void setSectorSize(Pager *pPager){
** and an error code is returned.
**
** The isHot parameter indicates that we are trying to rollback a journal
-** that might be a hot journal. Or, it could be that the journal is
+** that might be a hot journal. Or, it could be that the journal is
** preserved because of JOURNALMODE_PERSIST or JOURNALMODE_TRUNCATE.
** If the journal really is hot, reset the pager cache prior rolling
** back any content. If the journal is merely persistent, no reset is
@@ -53174,7 +59493,7 @@ static int pager_playback(Pager *pPager, int isHot){
Pgno mxPg = 0; /* Size of the original file in pages */
int rc; /* Result code of a subroutine */
int res = 1; /* Value returned by sqlite3OsAccess() */
- char *zMaster = 0; /* Name of master journal file if any */
+ char *zSuper = 0; /* Name of super-journal file if any */
int needPagerReset; /* True to reset page prior to first page rollback */
int nPlayback = 0; /* Total number of pages restored from journal */
u32 savedPageSize = pPager->pageSize;
@@ -53188,8 +59507,8 @@ static int pager_playback(Pager *pPager, int isHot){
goto end_playback;
}
- /* Read the master journal name from the journal, if it is present.
- ** If a master journal file name is specified, but the file is not
+ /* Read the super-journal name from the journal, if it is present.
+ ** If a super-journal file name is specified, but the file is not
** present on disk, then the journal is not hot and does not need to be
** played back.
**
@@ -53199,21 +59518,21 @@ static int pager_playback(Pager *pPager, int isHot){
** mxPathname is 512, which is the same as the minimum allowable value
** for pageSize.
*/
- zMaster = pPager->pTmpSpace;
- rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
- if( rc==SQLITE_OK && zMaster[0] ){
- rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
+ zSuper = pPager->pTmpSpace;
+ rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1);
+ if( rc==SQLITE_OK && zSuper[0] ){
+ rc = sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res);
}
- zMaster = 0;
+ zSuper = 0;
if( rc!=SQLITE_OK || !res ){
goto end_playback;
}
pPager->journalOff = 0;
needPagerReset = isHot;
- /* This loop terminates either when a readJournalHdr() or
- ** pager_playback_one_page() call returns SQLITE_DONE or an IO error
- ** occurs.
+ /* This loop terminates either when a readJournalHdr() or
+ ** pager_playback_one_page() call returns SQLITE_DONE or an IO error
+ ** occurs.
*/
while( 1 ){
/* Read the next journal header from the journal file. If there are
@@ -53222,7 +59541,7 @@ static int pager_playback(Pager *pPager, int isHot){
** This indicates nothing more needs to be rolled back.
*/
rc = readJournalHdr(pPager, isHot, szJ, &nRec, &mxPg);
- if( rc!=SQLITE_OK ){
+ if( rc!=SQLITE_OK ){
if( rc==SQLITE_DONE ){
rc = SQLITE_OK;
}
@@ -53250,7 +59569,7 @@ static int pager_playback(Pager *pPager, int isHot){
** chunk of the journal contains zero pages to be rolled back. But
** when doing a ROLLBACK and the nRec==0 chunk is the last chunk in
** the journal, it means that the journal might contain additional
- ** pages that need to be rolled back and that the number of pages
+ ** pages that need to be rolled back and that the number of pages
** should be computed based on the journal file size.
*/
if( nRec==0 && !isHot &&
@@ -53267,9 +59586,12 @@ static int pager_playback(Pager *pPager, int isHot){
goto end_playback;
}
pPager->dbSize = mxPg;
+ if( pPager->mxPgno<mxPg ){
+ pPager->mxPgno = mxPg;
+ }
}
- /* Copy original pages out of the journal and back into the
+ /* Copy original pages out of the journal and back into the
** database file and/or page cache.
*/
for(u=0; u<nRec; u++){
@@ -53319,10 +59641,10 @@ end_playback:
sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0);
#endif
- /* If this playback is happening automatically as a result of an IO or
- ** malloc error that occurred after the change-counter was updated but
- ** before the transaction was committed, then the change-counter
- ** modification may just have been reverted. If this happens in exclusive
+ /* If this playback is happening automatically as a result of an IO or
+ ** malloc error that occurred after the change-counter was updated but
+ ** before the transaction was committed, then the change-counter
+ ** modification may just have been reverted. If this happens in exclusive
** mode, then subsequent transactions performed by the connection will not
** update the change-counter at all. This may lead to cache inconsistency
** problems for other processes at some point in the future. So, just
@@ -53331,8 +59653,12 @@ end_playback:
pPager->changeCountDone = pPager->tempFile;
if( rc==SQLITE_OK ){
- zMaster = pPager->pTmpSpace;
- rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
+ /* Leave 4 bytes of space before the super-journal filename in memory.
+ ** This is because it may end up being passed to sqlite3OsOpen(), in
+ ** which case it requires 4 0x00 bytes in memory immediately before
+ ** the filename. */
+ zSuper = &pPager->pTmpSpace[4];
+ rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1);
testcase( rc!=SQLITE_OK );
}
if( rc==SQLITE_OK
@@ -53341,14 +59667,16 @@ end_playback:
rc = sqlite3PagerSync(pPager, 0);
}
if( rc==SQLITE_OK ){
- rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0);
+ rc = pager_end_transaction(pPager, zSuper[0]!='\0', 0);
testcase( rc!=SQLITE_OK );
}
- if( rc==SQLITE_OK && zMaster[0] && res ){
- /* If there was a master journal and this routine will return success,
- ** see if it is possible to delete the master journal.
+ if( rc==SQLITE_OK && zSuper[0] && res ){
+ /* If there was a super-journal and this routine will return success,
+ ** see if it is possible to delete the super-journal.
*/
- rc = pager_delmaster(pPager, zMaster);
+ assert( zSuper==&pPager->pTmpSpace[4] );
+ memset(pPager->pTmpSpace, 0, 4);
+ rc = pager_delsuper(pPager, zSuper);
testcase( rc!=SQLITE_OK );
}
if( isHot && nPlayback ){
@@ -53367,7 +59695,7 @@ end_playback:
/*
** Read the content for page pPg out of the database file (or out of
-** the WAL if that is where the most recent copy if found) into
+** the WAL if that is where the most recent copy if found) into
** pPg->pData. A shared lock or greater must be held on the database
** file before this function is called.
**
@@ -53423,8 +59751,6 @@ static int readDbPage(PgHdr *pPg){
memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers));
}
}
- CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = SQLITE_NOMEM_BKPT);
-
PAGER_INCR(sqlite3_pager_readdb_count);
PAGER_INCR(pPager->nRead);
IOTRACE(("PGIN %p %d\n", pPager, pPg->pgno));
@@ -53444,6 +59770,7 @@ static int readDbPage(PgHdr *pPg){
*/
static void pager_write_changecounter(PgHdr *pPg){
u32 change_counter;
+ if( NEVER(pPg==0) ) return;
/* Increment the value just read and write it back to byte 24. */
change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1;
@@ -53458,15 +59785,15 @@ static void pager_write_changecounter(PgHdr *pPg){
#ifndef SQLITE_OMIT_WAL
/*
-** This function is invoked once for each page that has already been
+** This function is invoked once for each page that has already been
** written into the log file when a WAL transaction is rolled back.
-** Parameter iPg is the page number of said page. The pCtx argument
+** Parameter iPg is the page number of said page. The pCtx argument
** is actually a pointer to the Pager structure.
**
** If page iPg is present in the cache, and has no outstanding references,
** it is discarded. Otherwise, if there are one or more outstanding
** references, the page content is reloaded from the database. If the
-** attempt to reload content from the database is required and fails,
+** attempt to reload content from the database is required and fails,
** return an SQLite error code. Otherwise, SQLITE_OK.
*/
static int pagerUndoCallback(void *pCtx, Pgno iPg){
@@ -53492,7 +59819,7 @@ static int pagerUndoCallback(void *pCtx, Pgno iPg){
** updated as data is copied out of the rollback journal and into the
** database. This is not generally possible with a WAL database, as
** rollback involves simply truncating the log file. Therefore, if one
- ** or more frames have already been written to the log (and therefore
+ ** or more frames have already been written to the log (and therefore
** also copied into the backup databases) as part of this transaction,
** the backups must be restarted.
*/
@@ -53509,7 +59836,7 @@ static int pagerRollbackWal(Pager *pPager){
PgHdr *pList; /* List of dirty pages to revert */
/* For all pages in the cache that are currently dirty or have already
- ** been written (but not committed) to the log file, do one of the
+ ** been written (but not committed) to the log file, do one of the
** following:
**
** + Discard the cached page (if refcount==0), or
@@ -53531,11 +59858,11 @@ static int pagerRollbackWal(Pager *pPager){
** This function is a wrapper around sqlite3WalFrames(). As well as logging
** the contents of the list of pages headed by pList (connected by pDirty),
** this function notifies any active backup processes that the pages have
-** changed.
+** changed.
**
** The list of pages passed into this routine is always sorted by page number.
** Hence, if page 1 appears anywhere on the list, it will be the first page.
-*/
+*/
static int pagerWalFrames(
Pager *pPager, /* Pager object */
PgHdr *pList, /* List of frames to log */
@@ -53549,7 +59876,7 @@ static int pagerWalFrames(
assert( pPager->pWal );
assert( pList );
#ifdef SQLITE_DEBUG
- /* Verify that the page list is in accending order */
+ /* Verify that the page list is in ascending order */
for(p=pList; p && p->pDirty; p=p->pDirty){
assert( p->pgno < p->pDirty->pgno );
}
@@ -53576,7 +59903,7 @@ static int pagerWalFrames(
pPager->aStat[PAGER_STAT_WRITE] += nList;
if( pList->pgno==1 ) pager_write_changecounter(pList);
- rc = sqlite3WalFrames(pPager->pWal,
+ rc = sqlite3WalFrames(pPager->pWal,
pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags
);
if( rc==SQLITE_OK && pPager->pBackup ){
@@ -53680,7 +60007,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
#ifndef SQLITE_OMIT_WAL
/*
** Check if the *-wal file that corresponds to the database opened by pPager
-** exists if the database is not empy, or verify that the *-wal file does
+** exists if the database is not empty, or verify that the *-wal file does
** not exist (by deleting it) if the database file is empty.
**
** If the database is not empty and the *-wal file exists, open the pager
@@ -53691,9 +60018,9 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
** Return SQLITE_OK or an error code.
**
** The caller must hold a SHARED lock on the database file to call this
-** function. Because an EXCLUSIVE lock on the db file is required to delete
-** a WAL on a none-empty database, this ensures there is no race condition
-** between the xAccess() below and an xDelete() being executed by some
+** function. Because an EXCLUSIVE lock on the db file is required to delete
+** a WAL on a none-empty database, this ensures there is no race condition
+** between the xAccess() below and an xDelete() being executed by some
** other connection.
*/
static int pagerOpenWalIfPresent(Pager *pPager){
@@ -53729,21 +60056,21 @@ static int pagerOpenWalIfPresent(Pager *pPager){
/*
** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback
-** the entire master journal file. The case pSavepoint==NULL occurs when
-** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction
+** the entire super-journal file. The case pSavepoint==NULL occurs when
+** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction
** savepoint.
**
-** When pSavepoint is not NULL (meaning a non-transaction savepoint is
+** When pSavepoint is not NULL (meaning a non-transaction savepoint is
** being rolled back), then the rollback consists of up to three stages,
** performed in the order specified:
**
** * Pages are played back from the main journal starting at byte
-** offset PagerSavepoint.iOffset and continuing to
+** offset PagerSavepoint.iOffset and continuing to
** PagerSavepoint.iHdrOffset, or to the end of the main journal
** file if PagerSavepoint.iHdrOffset is zero.
**
** * If PagerSavepoint.iHdrOffset is not zero, then pages are played
-** back starting from the journal header immediately following
+** back starting from the journal header immediately following
** PagerSavepoint.iHdrOffset to the end of the main journal file.
**
** * Pages are then played back from the sub-journal file, starting
@@ -53759,7 +60086,7 @@ static int pagerOpenWalIfPresent(Pager *pPager){
** journal file. There is no need for a bitvec in this case.
**
** In either case, before playback commences the Pager.dbSize variable
-** is reset to the value that it held at the start of the savepoint
+** is reset to the value that it held at the start of the savepoint
** (or transaction). No page with a page-number greater than this value
** is played back. If one is encountered it is simply skipped.
*/
@@ -53780,7 +60107,7 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
}
}
- /* Set the database size back to the value it was before the savepoint
+ /* Set the database size back to the value it was before the savepoint
** being reverted was opened.
*/
pPager->dbSize = pSavepoint ? pSavepoint->nOrig : pPager->dbOrigSize;
@@ -53833,7 +60160,7 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
** test is related to ticket #2565. See the discussion in the
** pager_playback() function for additional information.
*/
- if( nJRec==0
+ if( nJRec==0
&& pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff
){
nJRec = (u32)((szJ - pPager->journalOff)/JOURNAL_PG_SZ(pPager));
@@ -53969,7 +60296,6 @@ SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){
** Numeric values associated with these states are OFF==1, NORMAL=2,
** and FULL=3.
*/
-#ifndef SQLITE_OMIT_PAGER_PRAGMAS
SQLITE_PRIVATE void sqlite3PagerSetFlags(
Pager *pPager, /* The pager to set safety level for */
unsigned pgFlags /* Various flags */
@@ -54004,12 +60330,11 @@ SQLITE_PRIVATE void sqlite3PagerSetFlags(
pPager->doNotSpill |= SPILLFLAG_OFF;
}
}
-#endif
/*
** The following global variable is incremented whenever the library
** attempts to open a temporary file. This information is used for
-** testing and analysis only.
+** testing and analysis only.
*/
#ifdef SQLITE_TEST
SQLITE_API int sqlite3_opentemp_count = 0;
@@ -54018,8 +60343,8 @@ SQLITE_API int sqlite3_opentemp_count = 0;
/*
** Open a temporary file.
**
-** Write the file descriptor into *pFile. Return SQLITE_OK on success
-** or some other error code if we fail. The OS will automatically
+** Write the file descriptor into *pFile. Return SQLITE_OK on success
+** or some other error code if we fail. The OS will automatically
** delete the temporary file when it is closed.
**
** The flags passed to the VFS layer xOpen() call are those specified
@@ -54051,9 +60376,9 @@ static int pagerOpentemp(
/*
** Set the busy handler function.
**
-** The pager invokes the busy-handler if sqlite3OsLock() returns
+** The pager invokes the busy-handler if sqlite3OsLock() returns
** SQLITE_BUSY when trying to upgrade from no-lock to a SHARED lock,
-** or when trying to upgrade from a RESERVED lock to an EXCLUSIVE
+** or when trying to upgrade from a RESERVED lock to an EXCLUSIVE
** lock. It does *not* invoke the busy handler when upgrading from
** SHARED to RESERVED, or when upgrading from SHARED to EXCLUSIVE
** (which occurs during hot-journal rollback). Summary:
@@ -54065,7 +60390,7 @@ static int pagerOpentemp(
** SHARED_LOCK -> EXCLUSIVE_LOCK | No
** RESERVED_LOCK -> EXCLUSIVE_LOCK | Yes
**
-** If the busy-handler callback returns non-zero, the lock is
+** If the busy-handler callback returns non-zero, the lock is
** retried. If it returns zero, then the SQLITE_BUSY error is
** returned to the caller of the pager API function.
*/
@@ -54084,16 +60409,16 @@ SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(
}
/*
-** Change the page size used by the Pager object. The new page size
+** Change the page size used by the Pager object. The new page size
** is passed in *pPageSize.
**
** If the pager is in the error state when this function is called, it
-** is a no-op. The value returned is the error state error code (i.e.
+** is a no-op. The value returned is the error state error code (i.e.
** one of SQLITE_IOERR, an SQLITE_IOERR_xxx sub-code or SQLITE_FULL).
**
** Otherwise, if all of the following are true:
**
-** * the new page size (value of *pPageSize) is valid (a power
+** * the new page size (value of *pPageSize) is valid (a power
** of two between 512 and SQLITE_MAX_PAGE_SIZE, inclusive), and
**
** * there are no outstanding page references, and
@@ -54103,14 +60428,14 @@ SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(
**
** then the pager object page size is set to *pPageSize.
**
-** If the page size is changed, then this function uses sqlite3PagerMalloc()
-** to obtain a new Pager.pTmpSpace buffer. If this allocation attempt
-** fails, SQLITE_NOMEM is returned and the page size remains unchanged.
+** If the page size is changed, then this function uses sqlite3PagerMalloc()
+** to obtain a new Pager.pTmpSpace buffer. If this allocation attempt
+** fails, SQLITE_NOMEM is returned and the page size remains unchanged.
** In all other cases, SQLITE_OK is returned.
**
** If the page size is not changed, either because one of the enumerated
** conditions above is not true, the pager was in error state when this
-** function was called, or because the memory allocation attempt failed,
+** function was called, or because the memory allocation attempt failed,
** then *pPageSize is set to the old, retained page size before returning.
*/
SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){
@@ -54120,7 +60445,7 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR
** function may be called from within PagerOpen(), before the state
** of the Pager object is internally consistent.
**
- ** At one point this function returned an error if the pager was in
+ ** At one point this function returned an error if the pager was in
** PAGER_ERROR state. But since PAGER_ERROR state guarantees that
** there is at least one outstanding page reference, this function
** is a no-op for that case anyhow.
@@ -54129,8 +60454,8 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR
u32 pageSize = *pPageSize;
assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
if( (pPager->memDb==0 || pPager->dbSize==0)
- && sqlite3PcacheRefCount(pPager->pPCache)==0
- && pageSize && pageSize!=(u32)pPager->pageSize
+ && sqlite3PcacheRefCount(pPager->pPCache)==0
+ && pageSize && pageSize!=(u32)pPager->pageSize
){
char *pNew = NULL; /* New temp space */
i64 nByte = 0;
@@ -54139,8 +60464,14 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR
rc = sqlite3OsFileSize(pPager->fd, &nByte);
}
if( rc==SQLITE_OK ){
- pNew = (char *)sqlite3PageMalloc(pageSize);
- if( !pNew ) rc = SQLITE_NOMEM_BKPT;
+ /* 8 bytes of zeroed overrun space is sufficient so that the b-tree
+ * cell header parser will never run off the end of the allocation */
+ pNew = (char *)sqlite3PageMalloc(pageSize+8);
+ if( !pNew ){
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ memset(pNew+pageSize, 0, 8);
+ }
}
if( rc==SQLITE_OK ){
@@ -54152,6 +60483,7 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR
pPager->pTmpSpace = pNew;
pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize);
pPager->pageSize = pageSize;
+ pPager->lckPgno = (Pgno)(PENDING_BYTE/pageSize) + 1;
}else{
sqlite3PageFree(pNew);
}
@@ -54162,7 +60494,6 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR
if( nReserve<0 ) nReserve = pPager->nReserve;
assert( nReserve>=0 && nReserve<1000 );
pPager->nReserve = (i16)nReserve;
- pagerReportSize(pPager);
pagerFixMaplimit(pPager);
}
return rc;
@@ -54181,18 +60512,21 @@ SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager *pPager){
}
/*
-** Attempt to set the maximum database page count if mxPage is positive.
+** Attempt to set the maximum database page count if mxPage is positive.
** Make no changes if mxPage is zero or negative. And never reduce the
** maximum page count below the current size of the database.
**
** Regardless of mxPage, return the current maximum page count.
*/
-SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){
+SQLITE_PRIVATE Pgno sqlite3PagerMaxPageCount(Pager *pPager, Pgno mxPage){
if( mxPage>0 ){
pPager->mxPgno = mxPage;
}
assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */
- assert( pPager->mxPgno>=pPager->dbSize ); /* OP_MaxPgcnt enforces this */
+ /* assert( pPager->mxPgno>=pPager->dbSize ); */
+ /* OP_MaxPgcnt ensures that the parameter passed to this function is not
+ ** less than the total number of valid pages in the database. But this
+ ** may be less than Pager.dbSize, and so the assert() above is not valid */
return pPager->mxPgno;
}
@@ -54222,11 +60556,11 @@ void enable_simulated_io_errors(void){
/*
** Read the first N bytes from the beginning of the file into memory
-** that pDest points to.
+** that pDest points to.
**
** If the pager was opened on a transient file (zFilename==""), or
** opened on a file less than N bytes in size, the output buffer is
-** zeroed and SQLITE_OK returned. The rationale for this is that this
+** zeroed and SQLITE_OK returned. The rationale for this is that this
** function is used to read database headers, and a new transient or
** zero sized database has a header than consists entirely of zeroes.
**
@@ -54259,7 +60593,7 @@ SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned cha
** This function may only be called when a read-transaction is open on
** the pager. It returns the total number of pages in the database.
**
-** However, if the file is between 1 and <page-size> bytes in size, then
+** However, if the file is between 1 and <page-size> bytes in size, then
** this is considered a 1 page file.
*/
SQLITE_PRIVATE void sqlite3PagerPagecount(Pager *pPager, int *pnPage){
@@ -54274,19 +60608,19 @@ SQLITE_PRIVATE void sqlite3PagerPagecount(Pager *pPager, int *pnPage){
** a similar or greater lock is already held, this function is a no-op
** (returning SQLITE_OK immediately).
**
-** Otherwise, attempt to obtain the lock using sqlite3OsLock(). Invoke
-** the busy callback if the lock is currently not available. Repeat
-** until the busy callback returns false or until the attempt to
+** Otherwise, attempt to obtain the lock using sqlite3OsLock(). Invoke
+** the busy callback if the lock is currently not available. Repeat
+** until the busy callback returns false or until the attempt to
** obtain the lock succeeds.
**
** Return SQLITE_OK on success and an error code if we cannot obtain
-** the lock. If the lock is obtained successfully, set the Pager.state
+** the lock. If the lock is obtained successfully, set the Pager.state
** variable to locktype before returning.
*/
static int pager_wait_on_lock(Pager *pPager, int locktype){
int rc; /* Return code */
- /* Check that this is either a no-op (because the requested lock is
+ /* Check that this is either a no-op (because the requested lock is
** already held), or one of the transitions that the busy-handler
** may be invoked during, according to the comment above
** sqlite3PagerSetBusyhandler().
@@ -54303,15 +60637,14 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
}
/*
-** Function assertTruncateConstraint(pPager) checks that one of the
+** Function assertTruncateConstraint(pPager) checks that one of the
** following is true for all dirty pages currently in the page-cache:
**
-** a) The page number is less than or equal to the size of the
+** a) The page number is less than or equal to the size of the
** current database image, in pages, OR
**
** b) if the page content were written at this time, it would not
-** be necessary to write the current content out to the sub-journal
-** (as determined by function subjRequiresPage()).
+** be necessary to write the current content out to the sub-journal.
**
** If the condition asserted by this function were not true, and the
** dirty page were to be discarded from the cache via the pagerStress()
@@ -54319,15 +60652,23 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
** the database file. If a savepoint transaction were rolled back after
** this happened, the correct behavior would be to restore the current
** content of the page. However, since this content is not present in either
-** the database file or the portion of the rollback journal and
+** the database file or the portion of the rollback journal and
** sub-journal rolled back the content could not be restored and the
-** database image would become corrupt. It is therefore fortunate that
+** database image would become corrupt. It is therefore fortunate that
** this circumstance cannot arise.
*/
#if defined(SQLITE_DEBUG)
static void assertTruncateConstraintCb(PgHdr *pPg){
+ Pager *pPager = pPg->pPager;
assert( pPg->flags&PGHDR_DIRTY );
- assert( !subjRequiresPage(pPg) || pPg->pgno<=pPg->pPager->dbSize );
+ if( pPg->pgno>pPager->dbSize ){ /* if (a) is false */
+ Pgno pgno = pPg->pgno;
+ int i;
+ for(i=0; i<pPg->pPager->nSavepoint; i++){
+ PagerSavepoint *p = &pPager->aSavepoint[i];
+ assert( p->nOrig<pgno || sqlite3BitvecTestNotNull(p->pInSavepoint,pgno) );
+ }
+ }
}
static void assertTruncateConstraint(Pager *pPager){
sqlite3PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb);
@@ -54337,9 +60678,9 @@ static void assertTruncateConstraint(Pager *pPager){
#endif
/*
-** Truncate the in-memory database file image to nPage pages. This
-** function does not actually modify the database file on disk. It
-** just sets the internal state of the pager object so that the
+** Truncate the in-memory database file image to nPage pages. This
+** function does not actually modify the database file on disk. It
+** just sets the internal state of the pager object so that the
** truncation will be done when the current transaction is committed.
**
** This function is only called right before committing a transaction.
@@ -54348,17 +60689,17 @@ static void assertTruncateConstraint(Pager *pPager){
** then continue writing to the database.
*/
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
- assert( pPager->dbSize>=nPage );
+ assert( pPager->dbSize>=nPage || CORRUPT_DB );
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
pPager->dbSize = nPage;
/* At one point the code here called assertTruncateConstraint() to
** ensure that all pages being truncated away by this operation are,
- ** if one or more savepoints are open, present in the savepoint
+ ** if one or more savepoints are open, present in the savepoint
** journal so that they can be restored if the savepoint is rolled
** back. This is no longer necessary as this function is now only
- ** called right before committing a transaction. So although the
- ** Pager object may still have open savepoints (Pager.nSavepoint!=0),
+ ** called right before committing a transaction. So although the
+ ** Pager object may still have open savepoints (Pager.nSavepoint!=0),
** they cannot be rolled back. So the assertTruncateConstraint() call
** is no longer correct. */
}
@@ -54370,12 +60711,12 @@ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
** size of the journal file so that the pager_playback() routine knows
** that the entire journal file has been synced.
**
-** Syncing a hot-journal to disk before attempting to roll it back ensures
+** Syncing a hot-journal to disk before attempting to roll it back ensures
** that if a power-failure occurs during the rollback, the process that
** attempts rollback following system recovery sees the same journal
** content as this process.
**
-** If everything goes as planned, SQLITE_OK is returned. Otherwise,
+** If everything goes as planned, SQLITE_OK is returned. Otherwise,
** an SQLite error code.
*/
static int pagerSyncHotJournal(Pager *pPager){
@@ -54391,7 +60732,7 @@ static int pagerSyncHotJournal(Pager *pPager){
#if SQLITE_MAX_MMAP_SIZE>0
/*
-** Obtain a reference to a memory mapped page object for page number pgno.
+** Obtain a reference to a memory mapped page object for page number pgno.
** The new object will use the pointer pData, obtained from xFetch().
** If successful, set *ppPage to point to the new page reference
** and return SQLITE_OK. Otherwise, return an SQLite error code and set
@@ -54407,7 +60748,7 @@ static int pagerAcquireMapPage(
PgHdr **ppPage /* OUT: Acquired page object */
){
PgHdr *p; /* Memory mapped page to return */
-
+
if( pPager->pMmapFreelist ){
*ppPage = p = pPager->pMmapFreelist;
pPager->pMmapFreelist = p->pDirty;
@@ -54441,7 +60782,7 @@ static int pagerAcquireMapPage(
#endif
/*
-** Release a reference to page pPg. pPg must have been returned by an
+** Release a reference to page pPg. pPg must have been returned by an
** earlier call to pagerAcquireMapPage().
*/
static void pagerReleaseMapPage(PgHdr *pPg){
@@ -54501,7 +60842,7 @@ static int databaseIsUnmoved(Pager *pPager){
** result in a coredump.
**
** This function always succeeds. If a transaction is active an attempt
-** is made to roll it back. If an error occurs during the rollback
+** is made to roll it back. If an error occurs during the rollback
** a hot journal may be left in the filesystem but no error is returned
** to the caller.
*/
@@ -54518,7 +60859,7 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
{
u8 *a = 0;
assert( db || pPager->pWal==0 );
- if( db && 0==(db->flags & SQLITE_NoCkptOnClose)
+ if( db && 0==(db->flags & SQLITE_NoCkptOnClose)
&& SQLITE_OK==databaseIsUnmoved(pPager)
){
a = pTmp;
@@ -54532,8 +60873,8 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
pager_unlock(pPager);
}else{
/* If it is open, sync the journal file before calling UnlockAndRollback.
- ** If this is not done, then an unsynced portion of the open journal
- ** file may be played back into the database. If a power failure occurs
+ ** If this is not done, then an unsynced portion of the open journal
+ ** file may be played back into the database. If a power failure occurs
** while this is happening, the database could become corrupt.
**
** If an error occurs while trying to sync the journal, shift the pager
@@ -54555,11 +60896,6 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
sqlite3OsClose(pPager->fd);
sqlite3PageFree(pTmp);
sqlite3PcacheClose(pPager->pPCache);
-
-#ifdef SQLITE_HAS_CODEC
- if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec);
-#endif
-
assert( !pPager->aSavepoint && !pPager->pInJournal );
assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) );
@@ -54589,7 +60925,7 @@ SQLITE_PRIVATE void sqlite3PagerRef(DbPage *pPg){
** disk and can be restored in the event of a hot-journal rollback.
**
** If the Pager.noSync flag is set, then this function is a no-op.
-** Otherwise, the actions required depend on the journal-mode and the
+** Otherwise, the actions required depend on the journal-mode and the
** device characteristics of the file-system, as follows:
**
** * If the journal file is an in-memory journal file, no action need
@@ -54601,7 +60937,7 @@ SQLITE_PRIVATE void sqlite3PagerRef(DbPage *pPg){
** been written following it. If the pager is operating in full-sync
** mode, then the journal file is synced before this field is updated.
**
-** * If the device does not support the SEQUENTIAL property, then
+** * If the device does not support the SEQUENTIAL property, then
** journal file is synced.
**
** Or, in pseudo-code:
@@ -54610,11 +60946,11 @@ SQLITE_PRIVATE void sqlite3PagerRef(DbPage *pPg){
** if( NOT SAFE_APPEND ){
** if( <full-sync mode> ) xSync(<journal file>);
** <update nRec field>
-** }
+** }
** if( NOT SEQUENTIAL ) xSync(<journal file>);
** }
**
-** If successful, this routine clears the PGHDR_NEED_SYNC flag of every
+** If successful, this routine clears the PGHDR_NEED_SYNC flag of every
** page currently held in memory before returning SQLITE_OK. If an IO
** error is encountered, then the IO error code is returned to the caller.
*/
@@ -54642,10 +60978,10 @@ static int syncJournal(Pager *pPager, int newHdr){
** mode, then the journal file may at this point actually be larger
** than Pager.journalOff bytes. If the next thing in the journal
** file happens to be a journal-header (written as part of the
- ** previous connection's transaction), and a crash or power-failure
- ** occurs after nRec is updated but before this connection writes
- ** anything else to the journal file (or commits/rolls back its
- ** transaction), then SQLite may become confused when doing the
+ ** previous connection's transaction), and a crash or power-failure
+ ** occurs after nRec is updated but before this connection writes
+ ** anything else to the journal file (or commits/rolls back its
+ ** transaction), then SQLite may become confused when doing the
** hot-journal rollback following recovery. It may roll back all
** of this connections data, then proceed to rolling back the old,
** out-of-date data that follows it. Database corruption.
@@ -54655,7 +60991,7 @@ static int syncJournal(Pager *pPager, int newHdr){
** byte to the start of it to prevent it from being recognized.
**
** Variable iNextHdrOffset is set to the offset at which this
- ** problematic header will occur, if it exists. aMagic is used
+ ** problematic header will occur, if it exists. aMagic is used
** as a temporary buffer to inspect the first couple of bytes of
** the potential journal header.
*/
@@ -54682,7 +61018,7 @@ static int syncJournal(Pager *pPager, int newHdr){
** it as a candidate for rollback.
**
** This is not required if the persistent media supports the
- ** SAFE_APPEND property. Because in this case it is not possible
+ ** SAFE_APPEND property. Because in this case it is not possible
** for garbage data to be appended to the file, the nRec field
** is populated with 0xFFFFFFFF when the journal header is written
** and never needs to be updated.
@@ -54702,7 +61038,7 @@ static int syncJournal(Pager *pPager, int newHdr){
if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
IOTRACE(("JSYNC %p\n", pPager))
- rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags|
+ rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags|
(pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
);
if( rc!=SQLITE_OK ) return rc;
@@ -54719,8 +61055,8 @@ static int syncJournal(Pager *pPager, int newHdr){
}
}
- /* Unless the pager is in noSync mode, the journal file was just
- ** successfully synced. Either way, clear the PGHDR_NEED_SYNC flag on
+ /* Unless the pager is in noSync mode, the journal file was just
+ ** successfully synced. Either way, clear the PGHDR_NEED_SYNC flag on
** all pages.
*/
sqlite3PcacheClearSyncFlags(pPager->pPCache);
@@ -54740,9 +61076,9 @@ static int syncJournal(Pager *pPager, int newHdr){
** is called. Before writing anything to the database file, this lock
** is upgraded to an EXCLUSIVE lock. If the lock cannot be obtained,
** SQLITE_BUSY is returned and no data is written to the database file.
-**
+**
** If the pager is a temp-file pager and the actual file-system file
-** is not yet open, it is created and opened before any data is
+** is not yet open, it is created and opened before any data is
** written out.
**
** Once the lock has been upgraded and, if necessary, the file opened,
@@ -54757,7 +61093,7 @@ static int syncJournal(Pager *pPager, int newHdr){
** in Pager.dbFileVers[] is updated to match the new value stored in
** the database file.
**
-** If everything is successful, SQLITE_OK is returned. If an IO error
+** If everything is successful, SQLITE_OK is returned. If an IO error
** occurs, an IO error code is returned. Or, if the EXCLUSIVE lock cannot
** be obtained, SQLITE_BUSY is returned.
*/
@@ -54783,7 +61119,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
** file size will be.
*/
assert( rc!=SQLITE_OK || isOpen(pPager->fd) );
- if( rc==SQLITE_OK
+ if( rc==SQLITE_OK
&& pPager->dbHintSize<pPager->dbSize
&& (pList->pDirty || pList->pgno>pPager->dbHintSize)
){
@@ -54805,20 +61141,19 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
*/
if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){
i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */
- char *pData; /* Data to write */
+ char *pData; /* Data to write */
assert( (pList->flags&PGHDR_NEED_SYNC)==0 );
if( pList->pgno==1 ) pager_write_changecounter(pList);
- /* Encode the database */
- CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM_BKPT, pData);
+ pData = pList->pData;
/* Write out the page data. */
rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset);
/* If page 1 was just written, update Pager.dbFileVers to match
- ** the value now stored in the database file. If writing this
- ** page caused the database file to grow, update dbFileSize.
+ ** the value now stored in the database file. If writing this
+ ** page caused the database file to grow, update dbFileSize.
*/
if( pgno==1 ){
memcpy(&pPager->dbFileVers, &pData[24], sizeof(pPager->dbFileVers));
@@ -54846,18 +61181,18 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
}
/*
-** Ensure that the sub-journal file is open. If it is already open, this
+** Ensure that the sub-journal file is open. If it is already open, this
** function is a no-op.
**
-** SQLITE_OK is returned if everything goes according to plan. An
-** SQLITE_IOERR_XXX error code is returned if a call to sqlite3OsOpen()
+** SQLITE_OK is returned if everything goes according to plan. An
+** SQLITE_IOERR_XXX error code is returned if a call to sqlite3OsOpen()
** fails.
*/
static int openSubJournal(Pager *pPager){
int rc = SQLITE_OK;
if( !isOpen(pPager->sjfd) ){
- const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE
- | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE
+ const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE
+ | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE
| SQLITE_OPEN_DELETEONCLOSE;
int nStmtSpill = sqlite3Config.nStmtSpill;
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){
@@ -54869,13 +61204,13 @@ static int openSubJournal(Pager *pPager){
}
/*
-** Append a record of the current state of page pPg to the sub-journal.
+** Append a record of the current state of page pPg to the sub-journal.
**
** If successful, set the bit corresponding to pPg->pgno in the bitvecs
** for all open savepoints before returning.
**
** This function returns SQLITE_OK if everything is successful, an IO
-** error code if the attempt to write to the sub-journal fails, or
+** error code if the attempt to write to the sub-journal fails, or
** SQLITE_NOMEM if a malloc fails while setting a bit in a savepoint
** bitvec.
*/
@@ -54888,9 +61223,9 @@ static int subjournalPage(PgHdr *pPg){
assert( pPager->useJournal );
assert( isOpen(pPager->jfd) || pagerUseWal(pPager) );
assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 );
- assert( pagerUseWal(pPager)
- || pageInJournal(pPager, pPg)
- || pPg->pgno>pPager->dbOrigSize
+ assert( pagerUseWal(pPager)
+ || pageInJournal(pPager, pPg)
+ || pPg->pgno>pPager->dbOrigSize
);
rc = openSubJournal(pPager);
@@ -54900,12 +61235,6 @@ static int subjournalPage(PgHdr *pPg){
void *pData = pPg->pData;
i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize);
char *pData2;
-
-#if SQLITE_HAS_CODEC
- if( !pPager->subjInMemory ){
- CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2);
- }else
-#endif
pData2 = pData;
PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno));
rc = write32bits(pPager->sjfd, offset, pPg->pgno);
@@ -54933,14 +61262,14 @@ static int subjournalPageIfRequired(PgHdr *pPg){
** This function is called by the pcache layer when it has reached some
** soft memory limit. The first argument is a pointer to a Pager object
** (cast as a void*). The pager is always 'purgeable' (not an in-memory
-** database). The second argument is a reference to a page that is
+** database). The second argument is a reference to a page that is
** currently dirty but has no outstanding references. The page
-** is always associated with the Pager object passed as the first
+** is always associated with the Pager object passed as the first
** argument.
**
** The job of this function is to make pPg clean by writing its contents
** out to the database file, if possible. This may involve syncing the
-** journal file.
+** journal file.
**
** If successful, sqlite3PcacheMakeClean() is called on the page and
** SQLITE_OK returned. If an IO error occurs while trying to make the
@@ -54965,7 +61294,7 @@ static int pagerStress(void *p, PgHdr *pPg){
** a rollback or by user request, respectively.
**
** Spilling is also prohibited when in an error state since that could
- ** lead to database corruption. In the current implementation it
+ ** lead to database corruption. In the current implementation it
** is impossible for sqlite3PcacheFetch() to be called with createFlag==3
** while in the error state, hence it is impossible for this routine to
** be called in the error state. Nevertheless, we include a NEVER()
@@ -54986,26 +61315,26 @@ static int pagerStress(void *p, PgHdr *pPg){
pPg->pDirty = 0;
if( pagerUseWal(pPager) ){
/* Write a single frame for this page to the log. */
- rc = subjournalPageIfRequired(pPg);
+ rc = subjournalPageIfRequired(pPg);
if( rc==SQLITE_OK ){
rc = pagerWalFrames(pPager, pPg, 0, 0);
}
}else{
-
+
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
if( pPager->tempFile==0 ){
rc = sqlite3JournalCreate(pPager->jfd);
if( rc!=SQLITE_OK ) return pager_error(pPager, rc);
}
#endif
-
+
/* Sync the journal file if required. */
- if( pPg->flags&PGHDR_NEED_SYNC
+ if( pPg->flags&PGHDR_NEED_SYNC
|| pPager->eState==PAGER_WRITER_CACHEMOD
){
rc = syncJournal(pPager, 1);
}
-
+
/* Write the contents of the page out to the database file. */
if( rc==SQLITE_OK ){
assert( (pPg->flags&PGHDR_NEED_SYNC)==0 );
@@ -55019,7 +61348,7 @@ static int pagerStress(void *p, PgHdr *pPg){
sqlite3PcacheMakeClean(pPg);
}
- return pager_error(pPager, rc);
+ return pager_error(pPager, rc);
}
/*
@@ -55050,8 +61379,8 @@ SQLITE_PRIVATE int sqlite3PagerFlush(Pager *pPager){
** The zFilename argument is the path to the database file to open.
** If zFilename is NULL then a randomly-named temporary file is created
** and used as the file to be cached. Temporary files are be deleted
-** automatically when they are closed. If zFilename is ":memory:" then
-** all information is held in cache. It is never written to disk.
+** automatically when they are closed. If zFilename is ":memory:" then
+** all information is held in cache. It is never written to disk.
** This can be used to implement an in-memory database.
**
** The nExtra parameter specifies the number of bytes of space allocated
@@ -55065,13 +61394,13 @@ SQLITE_PRIVATE int sqlite3PagerFlush(Pager *pPager){
** of the PAGER_* flags.
**
** The vfsFlags parameter is a bitmask to pass to the flags parameter
-** of the xOpen() method of the supplied VFS when opening files.
+** of the xOpen() method of the supplied VFS when opening files.
**
-** If the pager object is allocated and the specified file opened
+** If the pager object is allocated and the specified file opened
** successfully, SQLITE_OK is returned and *ppPager set to point to
** the new pager object. If an error occurs, *ppPager is set to NULL
** and error code returned. This function may return SQLITE_NOMEM
-** (sqlite3Malloc() is used to allocate memory), SQLITE_CANTOPEN or
+** (sqlite3Malloc() is used to allocate memory), SQLITE_CANTOPEN or
** various SQLITE_IO_XXX errors.
*/
SQLITE_PRIVATE int sqlite3PagerOpen(
@@ -55088,11 +61417,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
int rc = SQLITE_OK; /* Return code */
int tempFile = 0; /* True for temp files (incl. in-memory files) */
int memDb = 0; /* True if this is an in-memory file */
-#ifdef SQLITE_ENABLE_DESERIALIZE
int memJM = 0; /* Memory journal mode */
-#else
-# define memJM 0
-#endif
int readOnly = 0; /* True if this is a read-only file */
int journalFileSize; /* Bytes to allocate for each journal fd */
char *zPathname = 0; /* Full path to database file */
@@ -55101,7 +61426,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */
u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
const char *zUri = 0; /* URI args to copy */
- int nUri = 0; /* Number of bytes of URI args at *zUri */
+ int nUriByte = 1; /* Number of bytes of URI args at *zUri */
/* Figure out how much space is required for each journal file-handle
** (there are two of them, the main journal and the sub-journal). */
@@ -55135,14 +61460,23 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
}
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_OK_SYMLINK ){
+ if( vfsFlags & SQLITE_OPEN_NOFOLLOW ){
+ rc = SQLITE_CANTOPEN_SYMLINK;
+ }else{
+ rc = SQLITE_OK;
+ }
+ }
+ }
nPathname = sqlite3Strlen30(zPathname);
z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1];
while( *z ){
- z += sqlite3Strlen30(z)+1;
- z += sqlite3Strlen30(z)+1;
+ z += strlen(z)+1;
+ z += strlen(z)+1;
}
- nUri = (int)(&z[1] - zUri);
- assert( nUri>=0 );
+ nUriByte = (int)(&z[1] - zUri);
+ assert( nUriByte>=1 );
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){
/* This branch is taken when the journal path required by
** the database being opened will be more than pVfs->mxPathname
@@ -55159,7 +61493,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
}
/* Allocate memory for the Pager structure, PCache object, the
- ** three file descriptors, the database file name and the journal
+ ** three file descriptors, the database file name and the journal
** file name. The layout in memory is as follows:
**
** Pager object (sizeof(Pager) bytes)
@@ -55167,50 +61501,113 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
** Database file handle (pVfs->szOsFile bytes)
** Sub-journal file handle (journalFileSize bytes)
** Main journal file handle (journalFileSize bytes)
+ ** Ptr back to the Pager (sizeof(Pager*) bytes)
+ ** \0\0\0\0 database prefix (4 bytes)
** Database file name (nPathname+1 bytes)
- ** Journal file name (nPathname+8+1 bytes)
+ ** URI query parameters (nUriByte bytes)
+ ** Journal filename (nPathname+8+1 bytes)
+ ** WAL filename (nPathname+4+1 bytes)
+ ** \0\0\0 terminator (3 bytes)
+ **
+ ** Some 3rd-party software, over which we have no control, depends on
+ ** the specific order of the filenames and the \0 separators between them
+ ** so that it can (for example) find the database filename given the WAL
+ ** filename without using the sqlite3_filename_database() API. This is a
+ ** misuse of SQLite and a bug in the 3rd-party software, but the 3rd-party
+ ** software is in widespread use, so we try to avoid changing the filename
+ ** order and formatting if possible. In particular, the details of the
+ ** filename format expected by 3rd-party software should be as follows:
+ **
+ ** - Main Database Path
+ ** - \0
+ ** - Multiple URI components consisting of:
+ ** - Key
+ ** - \0
+ ** - Value
+ ** - \0
+ ** - \0
+ ** - Journal Path
+ ** - \0
+ ** - WAL Path (zWALName)
+ ** - \0
+ **
+ ** The sqlite3_create_filename() interface and the databaseFilename() utility
+ ** that is used by sqlite3_filename_database() and kin also depend on the
+ ** specific formatting and order of the various filenames, so if the format
+ ** changes here, be sure to change it there as well.
*/
+ assert( SQLITE_PTRSIZE==sizeof(Pager*) );
pPtr = (u8 *)sqlite3MallocZero(
- ROUND8(sizeof(*pPager)) + /* Pager structure */
- ROUND8(pcacheSize) + /* PCache object */
- ROUND8(pVfs->szOsFile) + /* The main db file */
- journalFileSize * 2 + /* The two journal files */
- nPathname + 1 + nUri + /* zFilename */
- nPathname + 8 + 2 /* zJournal */
+ ROUND8(sizeof(*pPager)) + /* Pager structure */
+ ROUND8(pcacheSize) + /* PCache object */
+ ROUND8(pVfs->szOsFile) + /* The main db file */
+ journalFileSize * 2 + /* The two journal files */
+ SQLITE_PTRSIZE + /* Space to hold a pointer */
+ 4 + /* Database prefix */
+ nPathname + 1 + /* database filename */
+ nUriByte + /* query parameters */
+ nPathname + 8 + 1 + /* Journal filename */
#ifndef SQLITE_OMIT_WAL
- + nPathname + 4 + 2 /* zWal */
+ nPathname + 4 + 1 + /* WAL filename */
#endif
+ 3 /* Terminator */
);
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
if( !pPtr ){
sqlite3DbFree(0, zPathname);
return SQLITE_NOMEM_BKPT;
}
- pPager = (Pager*)(pPtr);
- pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager)));
- pPager->fd = (sqlite3_file*)(pPtr += ROUND8(pcacheSize));
- pPager->sjfd = (sqlite3_file*)(pPtr += ROUND8(pVfs->szOsFile));
- pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize);
- pPager->zFilename = (char*)(pPtr += journalFileSize);
+ pPager = (Pager*)pPtr; pPtr += ROUND8(sizeof(*pPager));
+ pPager->pPCache = (PCache*)pPtr; pPtr += ROUND8(pcacheSize);
+ pPager->fd = (sqlite3_file*)pPtr; pPtr += ROUND8(pVfs->szOsFile);
+ pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
+ pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) );
+ memcpy(pPtr, &pPager, SQLITE_PTRSIZE); pPtr += SQLITE_PTRSIZE;
+
+ /* Fill in the Pager.zFilename and pPager.zQueryParam fields */
+ pPtr += 4; /* Skip zero prefix */
+ pPager->zFilename = (char*)pPtr;
+ if( nPathname>0 ){
+ memcpy(pPtr, zPathname, nPathname); pPtr += nPathname + 1;
+ if( zUri ){
+ memcpy(pPtr, zUri, nUriByte); pPtr += nUriByte;
+ }else{
+ pPtr++;
+ }
+ }
+
+
+ /* Fill in Pager.zJournal */
+ if( nPathname>0 ){
+ pPager->zJournal = (char*)pPtr;
+ memcpy(pPtr, zPathname, nPathname); pPtr += nPathname;
+ memcpy(pPtr, "-journal",8); pPtr += 8 + 1;
+#ifdef SQLITE_ENABLE_8_3_NAMES
+ sqlite3FileSuffix3(zFilename,pPager->zJournal);
+ pPtr = (u8*)(pPager->zJournal + sqlite3Strlen30(pPager->zJournal)+1);
+#endif
+ }else{
+ pPager->zJournal = 0;
+ }
- /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */
- if( zPathname ){
- assert( nPathname>0 );
- pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri);
- memcpy(pPager->zFilename, zPathname, nPathname);
- if( nUri ) memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
- memcpy(pPager->zJournal, zPathname, nPathname);
- memcpy(&pPager->zJournal[nPathname], "-journal\000", 8+2);
- sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal);
#ifndef SQLITE_OMIT_WAL
- pPager->zWal = &pPager->zJournal[nPathname+8+1];
- memcpy(pPager->zWal, zPathname, nPathname);
- memcpy(&pPager->zWal[nPathname], "-wal\000", 4+1);
- sqlite3FileSuffix3(pPager->zFilename, pPager->zWal);
+ /* Fill in Pager.zWal */
+ if( nPathname>0 ){
+ pPager->zWal = (char*)pPtr;
+ memcpy(pPtr, zPathname, nPathname); pPtr += nPathname;
+ memcpy(pPtr, "-wal", 4); pPtr += 4 + 1;
+#ifdef SQLITE_ENABLE_8_3_NAMES
+ sqlite3FileSuffix3(zFilename, pPager->zWal);
+ pPtr = (u8*)(pPager->zWal + sqlite3Strlen30(pPager->zWal)+1);
#endif
- sqlite3DbFree(0, zPathname);
+ }else{
+ pPager->zWal = 0;
}
+#endif
+ (void)pPtr; /* Suppress warning about unused pPtr value */
+
+ if( nPathname ) sqlite3DbFree(0, zPathname);
pPager->pVfs = pVfs;
pPager->vfsFlags = vfsFlags;
@@ -55220,9 +61617,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
int fout = 0; /* VFS flags returned by xOpen() */
rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout);
assert( !memDb );
-#ifdef SQLITE_ENABLE_DESERIALIZE
- memJM = (fout&SQLITE_OPEN_MEMORY)!=0;
-#endif
+ pPager->memVfs = memJM = (fout&SQLITE_OPEN_MEMORY)!=0;
readOnly = (fout&SQLITE_OPEN_READONLY)!=0;
/* If the file was successfully opened for read/write access,
@@ -55259,9 +61654,9 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
}
#endif
}
- pPager->noLock = sqlite3_uri_boolean(zFilename, "nolock", 0);
+ pPager->noLock = sqlite3_uri_boolean(pPager->zFilename, "nolock", 0);
if( (iDc & SQLITE_IOCAP_IMMUTABLE)!=0
- || sqlite3_uri_boolean(zFilename, "immutable", 0) ){
+ || sqlite3_uri_boolean(pPager->zFilename, "immutable", 0) ){
vfsFlags |= SQLITE_OPEN_READONLY;
goto act_like_temp_file;
}
@@ -55276,7 +61671,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
** disk and uses an in-memory rollback journal.
**
** This branch also runs for files marked as immutable.
- */
+ */
act_like_temp_file:
tempFile = 1;
pPager->eState = PAGER_READER; /* Pretend we already have a lock */
@@ -55285,7 +61680,7 @@ act_like_temp_file:
readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
}
- /* The following call to PagerSetPagesize() serves to set the value of
+ /* The following call to PagerSetPagesize() serves to set the value of
** Pager.pageSize and to allocate the Pager.pTmpSpace buffer.
*/
if( rc==SQLITE_OK ){
@@ -55325,26 +61720,15 @@ act_like_temp_file:
/* pPager->state = PAGER_UNLOCK; */
/* pPager->errMask = 0; */
pPager->tempFile = (u8)tempFile;
- assert( tempFile==PAGER_LOCKINGMODE_NORMAL
+ assert( tempFile==PAGER_LOCKINGMODE_NORMAL
|| tempFile==PAGER_LOCKINGMODE_EXCLUSIVE );
assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 );
- pPager->exclusiveMode = (u8)tempFile;
+ pPager->exclusiveMode = (u8)tempFile;
pPager->changeCountDone = pPager->tempFile;
pPager->memDb = (u8)memDb;
pPager->readOnly = (u8)readOnly;
assert( useJournal || pPager->tempFile );
- pPager->noSync = pPager->tempFile;
- if( pPager->noSync ){
- assert( pPager->fullSync==0 );
- assert( pPager->extraSync==0 );
- assert( pPager->syncFlags==0 );
- assert( pPager->walSyncFlags==0 );
- }else{
- pPager->fullSync = 1;
- pPager->extraSync = 0;
- pPager->syncFlags = SQLITE_SYNC_NORMAL;
- pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2);
- }
+ sqlite3PagerSetFlags(pPager, (SQLITE_DEFAULT_SYNCHRONOUS+1)|PAGER_CACHESPILL);
/* pPager->pFirst = 0; */
/* pPager->pFirstSynced = 0; */
/* pPager->pLast = 0; */
@@ -55368,12 +61752,28 @@ act_like_temp_file:
return SQLITE_OK;
}
+/*
+** Return the sqlite3_file for the main database given the name
+** of the corresponding WAL or Journal name as passed into
+** xOpen.
+*/
+SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){
+ Pager *pPager;
+ const char *p;
+ while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){
+ zName--;
+ }
+ p = zName - 4 - sizeof(Pager*);
+ assert( EIGHT_BYTE_ALIGNMENT(p) );
+ pPager = *(Pager**)p;
+ return pPager->fd;
+}
/*
** This function is called after transitioning from PAGER_UNLOCK to
** PAGER_SHARED state. It tests if there is a hot journal present in
-** the file-system for the given pager. A hot journal is one that
+** the file-system for the given pager. A hot journal is one that
** needs to be played back. According to this function, a hot-journal
** file exists if the following criteria are met:
**
@@ -55388,14 +61788,14 @@ act_like_temp_file:
** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK
** is returned.
**
-** This routine does not check if there is a master journal filename
-** at the end of the file. If there is, and that master journal file
+** This routine does not check if there is a super-journal filename
+** at the end of the file. If there is, and that super-journal file
** does not exist, then the journal file is not really hot. In this
** case this routine will return a false-positive. The pager_playback()
-** routine will discover that the journal file is not really hot and
-** will not roll it back.
+** routine will discover that the journal file is not really hot and
+** will not roll it back.
**
-** If a hot-journal file is found to exist, *pExists is set to 1 and
+** If a hot-journal file is found to exist, *pExists is set to 1 and
** SQLITE_OK returned. If no hot-journal file is present, *pExists is
** set to 0 and SQLITE_OK returned. If an IO error occurs while trying
** to determine whether or not a hot-journal file exists, the IO error
@@ -55423,7 +61823,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
int locked = 0; /* True if some process holds a RESERVED lock */
/* Race condition here: Another process might have been holding the
- ** the RESERVED lock and have a journal open at the sqlite3OsAccess()
+ ** the RESERVED lock and have a journal open at the sqlite3OsAccess()
** call above, but then delete the journal and drop the lock before
** we get to the following sqlite3OsCheckReservedLock() call. If that
** is the case, this routine might think there is a hot journal when
@@ -55456,7 +61856,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
/* The journal file exists and no other connection has a reserved
** or greater lock on the database file. Now check that there is
** at least one non-zero bytes at the start of the journal file.
- ** If there is, then we consider this journal to be hot. If not,
+ ** If there is, then we consider this journal to be hot. If not,
** it can be ignored.
*/
if( !jrnlOpen ){
@@ -55506,7 +61906,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
** on the database file), then an attempt is made to obtain a
** SHARED lock on the database file. Immediately after obtaining
** the SHARED lock, the file-system is checked for a hot-journal,
-** which is played back if present. Following any hot-journal
+** which is played back if present. Following any hot-journal
** rollback, the contents of the cache are validated by checking
** the 'change-counter' field of the database file header and
** discarded if they are found to be invalid.
@@ -55517,8 +61917,8 @@ static int hasHotJournal(Pager *pPager, int *pExists){
** the contents of the page cache and rolling back any open journal
** file.
**
-** If everything is successful, SQLITE_OK is returned. If an IO error
-** occurs while locking the database, checking for a hot-journal file or
+** If everything is successful, SQLITE_OK is returned. If an IO error
+** occurs while locking the database, checking for a hot-journal file or
** rolling back a journal file, the IO error code is returned.
*/
SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
@@ -55526,7 +61926,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
/* This routine is only called from b-tree and only when there are no
** outstanding pages. This implies that the pager state should either
- ** be OPEN or READER. READER is only possible if the pager is or was in
+ ** be OPEN or READER. READER is only possible if the pager is or was in
** exclusive access mode. */
assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
assert( assert_pager_state(pPager) );
@@ -55564,12 +61964,12 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
** important that a RESERVED lock is not obtained on the way to the
** EXCLUSIVE lock. If it were, another process might open the
** database file, detect the RESERVED lock, and conclude that the
- ** database is safe to read while this process is still rolling the
+ ** database is safe to read while this process is still rolling the
** hot-journal back.
- **
+ **
** Because the intermediate RESERVED lock is not requested, any
- ** other process attempting to access the database file will get to
- ** this point in the code and fail to obtain its own EXCLUSIVE lock
+ ** other process attempting to access the database file will get to
+ ** this point in the code and fail to obtain its own EXCLUSIVE lock
** on the database file.
**
** Unless the pager is in locking_mode=exclusive mode, the lock is
@@ -55579,21 +61979,21 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
if( rc!=SQLITE_OK ){
goto failed;
}
-
- /* If it is not already open and the file exists on disk, open the
- ** journal for read/write access. Write access is required because
- ** in exclusive-access mode the file descriptor will be kept open
- ** and possibly used for a transaction later on. Also, write-access
- ** is usually required to finalize the journal in journal_mode=persist
+
+ /* If it is not already open and the file exists on disk, open the
+ ** journal for read/write access. Write access is required because
+ ** in exclusive-access mode the file descriptor will be kept open
+ ** and possibly used for a transaction later on. Also, write-access
+ ** is usually required to finalize the journal in journal_mode=persist
** mode (and also for journal_mode=truncate on some systems).
**
- ** If the journal does not exist, it usually means that some
- ** other connection managed to get in and roll it back before
- ** this connection obtained the exclusive lock above. Or, it
+ ** If the journal does not exist, it usually means that some
+ ** other connection managed to get in and roll it back before
+ ** this connection obtained the exclusive lock above. Or, it
** may mean that the pager was in the error-state when this
** function was called and the journal file does not exist.
*/
- if( !isOpen(pPager->jfd) ){
+ if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
sqlite3_vfs * const pVfs = pPager->pVfs;
int bExists; /* True if journal file exists */
rc = sqlite3OsAccess(
@@ -55610,7 +62010,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
}
}
}
-
+
/* Playback and delete the journal. Drop the database write
** lock and reacquire the read lock. Purge the cache before
** playing back the hot-journal so that we don't end up with
@@ -55635,8 +62035,8 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
** or roll back a hot-journal while holding an EXCLUSIVE lock. The
** pager_unlock() routine will be called before returning to unlock
** the file. If the unlock attempt fails, then Pager.eLock must be
- ** set to UNKNOWN_LOCK (see the comment above the #define for
- ** UNKNOWN_LOCK above for an explanation).
+ ** set to UNKNOWN_LOCK (see the comment above the #define for
+ ** UNKNOWN_LOCK above for an explanation).
**
** In order to get pager_unlock() to do this, set Pager.eState to
** PAGER_ERROR now. This is not actually counted as a transition
@@ -55644,7 +62044,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
** since we know that the same call to pager_unlock() will very
** shortly transition the pager object to the OPEN state. Calling
** assert_pager_state() would fail now, as it should not be possible
- ** to be in ERROR state when there are zero outstanding page
+ ** to be in ERROR state when there are zero outstanding page
** references.
*/
pager_error(pPager, rc);
@@ -55669,8 +62069,8 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
** a 32-bit counter that is incremented with each change. The
** other bytes change randomly with each file change when
** a codec is in use.
- **
- ** There is a vanishingly small chance that a change will not be
+ **
+ ** There is a vanishingly small chance that a change will not be
** detected. The chance of an undetected change is so small that
** it can be neglected.
*/
@@ -55737,7 +62137,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
** Except, in locking_mode=EXCLUSIVE when there is nothing to in
** the rollback journal, the unlock is not performed and there is
** nothing to rollback, so this routine is a no-op.
-*/
+*/
static void pagerUnlockIfUnused(Pager *pPager){
if( sqlite3PcacheRefCount(pPager->pPCache)==0 ){
assert( pPager->nMmapOut==0 ); /* because page1 is never memory mapped */
@@ -55747,7 +62147,7 @@ static void pagerUnlockIfUnused(Pager *pPager){
/*
** The page getter methods each try to acquire a reference to a
-** page with page number pgno. If the requested reference is
+** page with page number pgno. If the requested reference is
** successfully obtained, it is copied to *ppPage and SQLITE_OK returned.
**
** There are different implementations of the getter method depending
@@ -55757,22 +62157,22 @@ static void pagerUnlockIfUnused(Pager *pPager){
** getPageError() -- Used if the pager is in an error state
** getPageMmap() -- Used if memory-mapped I/O is enabled
**
-** If the requested page is already in the cache, it is returned.
+** If the requested page is already in the cache, it is returned.
** Otherwise, a new page object is allocated and populated with data
** read from the database file. In some cases, the pcache module may
** choose not to allocate a new page object and may reuse an existing
** object with no outstanding references.
**
-** The extra data appended to a page is always initialized to zeros the
-** first time a page is loaded into memory. If the page requested is
+** The extra data appended to a page is always initialized to zeros the
+** first time a page is loaded into memory. If the page requested is
** already in the cache when this function is called, then the extra
** data is left as it was when the page object was last used.
**
-** If the database image is smaller than the requested page or if
-** the flags parameter contains the PAGER_GET_NOCONTENT bit and the
-** requested page is not already stored in the cache, then no
-** actual disk read occurs. In this case the memory image of the
-** page is initialized to all zeros.
+** If the database image is smaller than the requested page or if
+** the flags parameter contains the PAGER_GET_NOCONTENT bit and the
+** requested page is not already stored in the cache, then no
+** actual disk read occurs. In this case the memory image of the
+** page is initialized to all zeros.
**
** If PAGER_GET_NOCONTENT is true, it means that we do not care about
** the contents of the page. This occurs in two scenarios:
@@ -55838,18 +62238,18 @@ static int getPageNormal(
if( pPg->pPager && !noContent ){
/* In this case the pcache already contains an initialized copy of
** the page. Return without further ado. */
- assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
+ assert( pgno!=PAGER_SJ_PGNO(pPager) );
pPager->aStat[PAGER_STAT_HIT]++;
return SQLITE_OK;
}else{
- /* The pager cache has created a new page. Its content needs to
+ /* The pager cache has created a new page. Its content needs to
** be initialized. But first some error checks:
**
- ** (1) The maximum page number is 2^31
+ ** (*) obsolete. Was: maximum page number is 2^31
** (2) Never try to fetch the locking page
*/
- if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){
+ if( pgno==PAGER_SJ_PGNO(pPager) ){
rc = SQLITE_CORRUPT_BKPT;
goto pager_acquire_err;
}
@@ -55860,13 +62260,17 @@ static int getPageNormal(
if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){
if( pgno>pPager->mxPgno ){
rc = SQLITE_FULL;
+ if( pgno<=pPager->dbSize ){
+ sqlite3PcacheRelease(pPg);
+ pPg = 0;
+ }
goto pager_acquire_err;
}
if( noContent ){
/* Failure to set the bits in the InJournal bit-vectors is benign.
- ** It merely means that we might do some extra work to journal a
- ** page that does not need to be journaled. Nevertheless, be sure
- ** to test the case where a malloc error occurs while trying to set
+ ** It merely means that we might do some extra work to journal a
+ ** page that does not need to be journaled. Nevertheless, be sure
+ ** to test the case where a malloc error occurs while trying to set
** a bit in a bit vector.
*/
sqlite3BeginBenignMalloc();
@@ -55916,16 +62320,13 @@ static int getPageMMap(
/* It is acceptable to use a read-only (mmap) page for any page except
** page 1 if there is no write-transaction open or the ACQUIRE_READONLY
- ** flag was specified by the caller. And so long as the db is not a
+ ** flag was specified by the caller. And so long as the db is not a
** temporary or in-memory database. */
const int bMmapOk = (pgno>1
&& (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY))
);
assert( USEFETCH(pPager) );
-#ifdef SQLITE_HAS_CODEC
- assert( pPager->xCodec==0 );
-#endif
/* Optimization note: Adding the "pgno<=1" term before "pgno==0" here
** allows the compiler optimizer to reuse the results of the "pgno>1"
@@ -55948,7 +62349,7 @@ static int getPageMMap(
}
if( bMmapOk && iFrame==0 ){
void *pData = 0;
- rc = sqlite3OsFetch(pPager->fd,
+ rc = sqlite3OsFetch(pPager->fd,
(i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData
);
if( rc==SQLITE_OK && pData ){
@@ -55998,18 +62399,31 @@ SQLITE_PRIVATE int sqlite3PagerGet(
DbPage **ppPage, /* Write a pointer to the page here */
int flags /* PAGER_GET_XXX flags */
){
+#if 0 /* Trace page fetch by setting to 1 */
+ int rc;
+ printf("PAGE %u\n", pgno);
+ fflush(stdout);
+ rc = pPager->xGet(pPager, pgno, ppPage, flags);
+ if( rc ){
+ printf("PAGE %u failed with 0x%02x\n", pgno, rc);
+ fflush(stdout);
+ }
+ return rc;
+#else
+ /* Normal, high-speed version of sqlite3PagerGet() */
return pPager->xGet(pPager, pgno, ppPage, flags);
+#endif
}
/*
** Acquire a page if it is already in the in-memory cache. Do
** not read the page from disk. Return a pointer to the page,
-** or 0 if the page is not in cache.
+** or 0 if the page is not in cache.
**
** See also sqlite3PagerGet(). The difference between this routine
** and sqlite3PagerGet() is that _get() will go to the disk and read
** in the page if the page is not already in cache. This routine
-** returns NULL if the page is not in cache or if a disk I/O error
+** returns NULL if the page is not in cache or if a disk I/O error
** has ever happened.
*/
SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
@@ -56026,10 +62440,12 @@ SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
/*
** Release a page reference.
**
-** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be
-** used if we know that the page being released is not the last page.
+** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be used
+** if we know that the page being released is not the last reference to page1.
** The btree layer always holds page1 open until the end, so these first
-** to routines can be used to release any page other than BtShared.pPage1.
+** two routines can be used to release any page other than BtShared.pPage1.
+** The assert() at tag-20230419-2 proves that this constraint is always
+** honored.
**
** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine
** checks the total number of outstanding pages and if the number of
@@ -56045,7 +62461,7 @@ SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage *pPg){
sqlite3PcacheRelease(pPg);
}
/* Do not use this routine to release the last reference to page1 */
- assert( sqlite3PcacheRefCount(pPager->pPCache)>0 );
+ assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); /* tag-20230419-2 */
}
SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){
if( pPg ) sqlite3PagerUnrefNotNull(pPg);
@@ -56056,31 +62472,30 @@ SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage *pPg){
assert( pPg->pgno==1 );
assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */
pPager = pPg->pPager;
- sqlite3PagerResetLockTimeout(pPager);
sqlite3PcacheRelease(pPg);
pagerUnlockIfUnused(pPager);
}
/*
** This function is called at the start of every write transaction.
-** There must already be a RESERVED or EXCLUSIVE lock on the database
+** There must already be a RESERVED or EXCLUSIVE lock on the database
** file when this routine is called.
**
** Open the journal file for pager pPager and write a journal header
** to the start of it. If there are active savepoints, open the sub-journal
-** as well. This function is only used when the journal file is being
-** opened to write a rollback log for a transaction. It is not used
+** as well. This function is only used when the journal file is being
+** opened to write a rollback log for a transaction. It is not used
** when opening a hot journal file to roll it back.
**
** If the journal file is already open (as it may be in exclusive mode),
** then this function just writes a journal header to the start of the
-** already open file.
+** already open file.
**
** Whether or not the journal file is opened by this function, the
** Pager.pInJournal bitvec structure is allocated.
**
-** Return SQLITE_OK if everything is successful. Otherwise, return
-** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or
+** Return SQLITE_OK if everything is successful. Otherwise, return
+** SQLITE_NOMEM if the attempt to allocate Pager.pInJournal fails, or
** an IO error code if opening or writing the journal file fails.
*/
static int pager_open_journal(Pager *pPager){
@@ -56090,7 +62505,7 @@ static int pager_open_journal(Pager *pPager){
assert( pPager->eState==PAGER_WRITER_LOCKED );
assert( assert_pager_state(pPager) );
assert( pPager->pInJournal==0 );
-
+
/* If already in the error state, this function is a no-op. But on
** the other hand, this routine is never called if we are already in
** an error state. */
@@ -56101,7 +62516,7 @@ static int pager_open_journal(Pager *pPager){
if( pPager->pInJournal==0 ){
return SQLITE_NOMEM_BKPT;
}
-
+
/* Open the journal file if it is not already open. */
if( !isOpen(pPager->jfd) ){
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
@@ -56112,12 +62527,13 @@ static int pager_open_journal(Pager *pPager){
if( pPager->tempFile ){
flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL);
+ flags |= SQLITE_OPEN_EXCLUSIVE;
nSpill = sqlite3Config.nStmtSpill;
}else{
flags |= SQLITE_OPEN_MAIN_JOURNAL;
nSpill = jrnlBufferSize(pPager);
}
-
+
/* Verify that the database still has the same name as it did when
** it was originally opened. */
rc = databaseIsUnmoved(pPager);
@@ -56129,16 +62545,16 @@ static int pager_open_journal(Pager *pPager){
}
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
}
-
-
- /* Write the first journal header to the journal file and open
+
+
+ /* Write the first journal header to the journal file and open
** the sub-journal if necessary.
*/
if( rc==SQLITE_OK ){
/* TODO: Check if all of these are really required. */
pPager->nRec = 0;
pPager->journalOff = 0;
- pPager->setMaster = 0;
+ pPager->setSuper = 0;
pPager->journalHdr = 0;
rc = writeJournalHdr(pPager);
}
@@ -56147,6 +62563,7 @@ static int pager_open_journal(Pager *pPager){
if( rc!=SQLITE_OK ){
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
+ pPager->journalOff = 0;
}else{
assert( pPager->eState==PAGER_WRITER_LOCKED );
pPager->eState = PAGER_WRITER_CACHEMOD;
@@ -56156,12 +62573,12 @@ static int pager_open_journal(Pager *pPager){
}
/*
-** Begin a write-transaction on the specified pager object. If a
+** Begin a write-transaction on the specified pager object. If a
** write-transaction has already been opened, this function is a no-op.
**
** If the exFlag argument is false, then acquire at least a RESERVED
** lock on the database file. If exFlag is true, then acquire at least
-** an EXCLUSIVE lock. If such a lock is already held, no locking
+** an EXCLUSIVE lock. If such a lock is already held, no locking
** functions need be called.
**
** If the subjInMemory argument is non-zero, then any sub-journal opened
@@ -56169,7 +62586,7 @@ static int pager_open_journal(Pager *pPager){
** has no effect if the sub-journal is already opened (as it may be when
** running in exclusive mode) or if the transaction does not require a
** sub-journal. If the subjInMemory argument is zero, then any required
-** sub-journal is implemented in-memory if pPager is an in-memory database,
+** sub-journal is implemented in-memory if pPager is an in-memory database,
** or using a temporary file otherwise.
*/
SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
@@ -56179,7 +62596,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR );
pPager->subjInMemory = (u8)subjInMemory;
- if( ALWAYS(pPager->eState==PAGER_READER) ){
+ if( pPager->eState==PAGER_READER ){
assert( pPager->pInJournal==0 );
if( pagerUseWal(pPager) ){
@@ -56217,9 +62634,9 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
**
** WAL mode sets Pager.eState to PAGER_WRITER_LOCKED or CACHEMOD
** when it has an open transaction, but never to DBMOD or FINISHED.
- ** This is because in those states the code to roll back savepoint
- ** transactions may copy data from the sub-journal into the database
- ** file as well as into the page cache. Which would be incorrect in
+ ** This is because in those states the code to roll back savepoint
+ ** transactions may copy data from the sub-journal into the database
+ ** file as well as into the page cache. Which would be incorrect in
** WAL mode.
*/
pPager->eState = PAGER_WRITER_LOCKED;
@@ -56251,10 +62668,10 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){
/* We should never write to the journal file the page that
** contains the database locks. The following assert verifies
** that we do not. */
- assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
+ assert( pPg->pgno!=PAGER_SJ_PGNO(pPager) );
assert( pPager->journalHdr<=pPager->journalOff );
- CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2);
+ pData2 = pPg->pData;
cksum = pager_cksum(pPager, (u8*)pData2);
/* Even if an IO or diskfull error occurs while journalling the
@@ -56273,11 +62690,11 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){
rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
if( rc!=SQLITE_OK ) return rc;
- IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
+ IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
pPager->journalOff, pPager->pageSize));
PAGER_INCR(sqlite3_pager_writej_count);
PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
- PAGERID(pPager), pPg->pgno,
+ PAGERID(pPager), pPg->pgno,
((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
pPager->journalOff += 8 + pPager->pageSize;
@@ -56292,9 +62709,9 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){
}
/*
-** Mark a single data page as writeable. The page is written into the
+** Mark a single data page as writeable. The page is written into the
** main journal or sub-journal as required. If the page is written into
-** one of the journals, the corresponding bit is set in the
+** one of the journals, the corresponding bit is set in the
** Pager.pInJournal bitvec and the PagerSavepoint.pInSavepoint bitvecs
** of any open savepoints as appropriate.
*/
@@ -56302,7 +62719,7 @@ static int pager_write(PgHdr *pPg){
Pager *pPager = pPg->pPager;
int rc = SQLITE_OK;
- /* This routine is not called unless a write-transaction has already
+ /* This routine is not called unless a write-transaction has already
** been started. The journal file may or may not be open at this point.
** It is never called in the ERROR state.
*/
@@ -56319,7 +62736,7 @@ static int pager_write(PgHdr *pPg){
** obtained the necessary locks to begin the write-transaction, but the
** rollback journal might not yet be open. Open it now if this is the case.
**
- ** This is done before calling sqlite3PcacheMakeDirty() on the page.
+ ** This is done before calling sqlite3PcacheMakeDirty() on the page.
** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then
** an error might occur and the pager would end up in WRITER_LOCKED state
** with pages marked as dirty in the cache.
@@ -56364,7 +62781,7 @@ static int pager_write(PgHdr *pPg){
** PGHDR_WRITEABLE bit that indicates that the page can be safely modified.
*/
pPg->flags |= PGHDR_WRITEABLE;
-
+
/* If the statement journal is open and the page is not in it,
** then write the page into the statement journal.
*/
@@ -56430,7 +62847,7 @@ static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){
Pgno pg = pg1+ii;
PgHdr *pPage;
if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
- if( pg!=PAGER_MJ_PGNO(pPager) ){
+ if( pg!=PAGER_SJ_PGNO(pPager) ){
rc = sqlite3PagerGet(pPager, pg, &pPage, 0);
if( rc==SQLITE_OK ){
rc = pager_write(pPage);
@@ -56448,7 +62865,7 @@ static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){
}
}
- /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages
+ /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages
** starting at pg1, then it needs to be set for all of them. Because
** writing to any of these nPage pages may damage the others, the
** journal file must contain sync()ed copies of all of them
@@ -56471,9 +62888,9 @@ static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){
}
/*
-** Mark a data page as writeable. This routine must be called before
-** making changes to a page. The caller must check the return value
-** of this function and be careful not to change any page data unless
+** Mark a data page as writeable. This routine must be called before
+** making changes to a page. The caller must check the return value
+** of this function and be careful not to change any page data unless
** this routine returns SQLITE_OK.
**
** The difference between this function and pager_write() is that this
@@ -56524,13 +62941,13 @@ SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage *pPg){
** on the given page is unused. The pager marks the page as clean so
** that it does not get written to disk.
**
-** Tests show that this optimization can quadruple the speed of large
+** Tests show that this optimization can quadruple the speed of large
** DELETE operations.
**
** This optimization cannot be used with a temp-file, as the page may
** have been dirty at the start of the transaction. In that case, if
-** memory pressure forces page pPg out of the cache, the data does need
-** to be written out to disk so that it may be read back in if the
+** memory pressure forces page pPg out of the cache, the data does need
+** to be written out to disk so that it may be read back in if the
** current transaction is rolled back.
*/
SQLITE_PRIVATE void sqlite3PagerDontWrite(PgHdr *pPg){
@@ -56546,17 +62963,17 @@ SQLITE_PRIVATE void sqlite3PagerDontWrite(PgHdr *pPg){
}
/*
-** This routine is called to increment the value of the database file
-** change-counter, stored as a 4-byte big-endian integer starting at
+** This routine is called to increment the value of the database file
+** change-counter, stored as a 4-byte big-endian integer starting at
** byte offset 24 of the pager file. The secondary change counter at
** 92 is also updated, as is the SQLite version number at offset 96.
**
** But this only happens if the pPager->changeCountDone flag is false.
** To avoid excess churning of page 1, the update only happens once.
-** See also the pager_write_changecounter() routine that does an
+** See also the pager_write_changecounter() routine that does an
** unconditional update of the change counters.
**
-** If the isDirectMode flag is zero, then this is done by calling
+** If the isDirectMode flag is zero, then this is done by calling
** sqlite3PagerWrite() on page 1, then modifying the contents of the
** page data. In this case the file will be updated when the current
** transaction is committed.
@@ -56564,7 +62981,7 @@ SQLITE_PRIVATE void sqlite3PagerDontWrite(PgHdr *pPg){
** The isDirectMode flag may only be non-zero if the library was compiled
** with the SQLITE_ENABLE_ATOMIC_WRITE macro defined. In this case,
** if isDirect is non-zero, then the database file is updated directly
-** by writing an updated version of page 1 using a call to the
+** by writing an updated version of page 1 using a call to the
** sqlite3OsWrite() function.
*/
static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
@@ -56593,7 +63010,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
# define DIRECT_MODE isDirectMode
#endif
- if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){
+ if( !pPager->changeCountDone && pPager->dbSize>0 ){
PgHdr *pPgHdr; /* Reference to page 1 */
assert( !pPager->tempFile && isOpen(pPager->fd) );
@@ -56603,7 +63020,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
assert( pPgHdr==0 || rc==SQLITE_OK );
/* If page one was fetched successfully, and this function is not
- ** operating in direct-mode, make page 1 writable. When not in
+ ** operating in direct-mode, make page 1 writable. When not in
** direct mode, page 1 is always held in cache and hence the PagerGet()
** above is always successful - hence the ALWAYS on rc==SQLITE_OK.
*/
@@ -56619,7 +63036,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
if( DIRECT_MODE ){
const void *zBuf;
assert( pPager->dbFileSize>0 );
- CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM_BKPT, zBuf);
+ zBuf = pPgHdr->pData;
if( rc==SQLITE_OK ){
rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0);
pPager->aStat[PAGER_STAT_WRITE]++;
@@ -56650,9 +63067,9 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
** If successful, or if called on a pager for which it is a no-op, this
** function returns SQLITE_OK. Otherwise, an IO error code is returned.
*/
-SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zMaster){
+SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zSuper){
int rc = SQLITE_OK;
- void *pArg = (void*)zMaster;
+ void *pArg = (void*)zSuper;
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg);
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
if( rc==SQLITE_OK && !pPager->noSync ){
@@ -56664,22 +63081,22 @@ SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zMaster){
/*
** This function may only be called while a write-transaction is active in
-** rollback. If the connection is in WAL mode, this call is a no-op.
-** Otherwise, if the connection does not already have an EXCLUSIVE lock on
+** rollback. If the connection is in WAL mode, this call is a no-op.
+** Otherwise, if the connection does not already have an EXCLUSIVE lock on
** the database file, an attempt is made to obtain one.
**
** If the EXCLUSIVE lock is already held or the attempt to obtain it is
** successful, or the connection is in WAL mode, SQLITE_OK is returned.
-** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is
+** Otherwise, either SQLITE_BUSY or an SQLITE_IOERR_XXX error code is
** returned.
*/
SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){
int rc = pPager->errCode;
assert( assert_pager_state(pPager) );
if( rc==SQLITE_OK ){
- assert( pPager->eState==PAGER_WRITER_CACHEMOD
- || pPager->eState==PAGER_WRITER_DBMOD
- || pPager->eState==PAGER_WRITER_LOCKED
+ assert( pPager->eState==PAGER_WRITER_CACHEMOD
+ || pPager->eState==PAGER_WRITER_DBMOD
+ || pPager->eState==PAGER_WRITER_LOCKED
);
assert( assert_pager_state(pPager) );
if( 0==pagerUseWal(pPager) ){
@@ -56690,24 +63107,24 @@ SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){
}
/*
-** Sync the database file for the pager pPager. zMaster points to the name
-** of a master journal file that should be written into the individual
-** journal file. zMaster may be NULL, which is interpreted as no master
-** journal (a single database transaction).
+** Sync the database file for the pager pPager. zSuper points to the name
+** of a super-journal file that should be written into the individual
+** journal file. zSuper may be NULL, which is interpreted as no
+** super-journal (a single database transaction).
**
** This routine ensures that:
**
** * The database file change-counter is updated,
** * the journal is synced (unless the atomic-write optimization is used),
-** * all dirty pages are written to the database file,
+** * all dirty pages are written to the database file,
** * the database file is truncated (if required), and
-** * the database file synced.
+** * the database file synced.
**
-** The only thing that remains to commit the transaction is to finalize
-** (delete, truncate or zero the first part of) the journal file (or
-** delete the master journal file if specified).
+** The only thing that remains to commit the transaction is to finalize
+** (delete, truncate or zero the first part of) the journal file (or
+** delete the super-journal file if specified).
**
-** Note that if zMaster==NULL, this does not overwrite a previous value
+** Note that if zSuper==NULL, this does not overwrite a previous value
** passed to an sqlite3PagerCommitPhaseOne() call.
**
** If the final parameter - noSync - is true, then the database file itself
@@ -56717,7 +63134,7 @@ SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){
*/
SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
Pager *pPager, /* Pager object */
- const char *zMaster, /* If not NULL, the master journal name */
+ const char *zSuper, /* If not NULL, the super-journal name */
int noSync /* True to omit the xSync on the db file */
){
int rc = SQLITE_OK; /* Return code */
@@ -56735,8 +63152,8 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
/* Provide the ability to easily simulate an I/O error during testing */
if( sqlite3FaultSim(400) ) return SQLITE_IOERR;
- PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n",
- pPager->zFilename, zMaster, pPager->dbSize));
+ PAGERTRACE(("DATABASE SYNC: File=%s zSuper=%s nSize=%d\n",
+ pPager->zFilename, zSuper, pPager->dbSize));
/* If no database changes have been made, return early. */
if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK;
@@ -56775,7 +63192,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
*/
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
sqlite3_file *fd = pPager->fd;
- int bBatch = zMaster==0 /* An SQLITE_IOCAP_BATCH_ATOMIC commit */
+ int bBatch = zSuper==0 /* An SQLITE_IOCAP_BATCH_ATOMIC commit */
&& (sqlite3OsDeviceCharacteristics(fd) & SQLITE_IOCAP_BATCH_ATOMIC)
&& !pPager->noSync
&& sqlite3JournalIsInMemory(pPager->jfd);
@@ -56786,11 +63203,11 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
/* The following block updates the change-counter. Exactly how it
** does this depends on whether or not the atomic-update optimization
- ** was enabled at compile time, and if this transaction meets the
- ** runtime criteria to use the operation:
+ ** was enabled at compile time, and if this transaction meets the
+ ** runtime criteria to use the operation:
**
** * The file-system supports the atomic-write property for
- ** blocks of size page-size, and
+ ** blocks of size page-size, and
** * This commit is not part of a multi-file transaction, and
** * Exactly one page has been modified and store in the journal file.
**
@@ -56800,7 +63217,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
** is not applicable to this transaction, call sqlite3JournalCreate()
** to make sure the journal file has actually been created, then call
** pager_incr_changecounter() to update the change-counter in indirect
- ** mode.
+ ** mode.
**
** Otherwise, if the optimization is both enabled and applicable,
** then call pager_incr_changecounter() to update the change-counter
@@ -56809,19 +63226,19 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
*/
if( bBatch==0 ){
PgHdr *pPg;
- assert( isOpen(pPager->jfd)
- || pPager->journalMode==PAGER_JOURNALMODE_OFF
- || pPager->journalMode==PAGER_JOURNALMODE_WAL
+ assert( isOpen(pPager->jfd)
+ || pPager->journalMode==PAGER_JOURNALMODE_OFF
+ || pPager->journalMode==PAGER_JOURNALMODE_WAL
);
- if( !zMaster && isOpen(pPager->jfd)
- && pPager->journalOff==jrnlBufferSize(pPager)
+ if( !zSuper && isOpen(pPager->jfd)
+ && pPager->journalOff==jrnlBufferSize(pPager)
&& pPager->dbSize>=pPager->dbOrigSize
&& (!(pPg = sqlite3PcacheDirtyList(pPager->pPCache)) || 0==pPg->pDirty)
){
- /* Update the db file change counter via the direct-write method. The
- ** following call will modify the in-memory representation of page 1
- ** to include the updated change counter and then write page 1
- ** directly to the database file. Because of the atomic-write
+ /* Update the db file change counter via the direct-write method. The
+ ** following call will modify the in-memory representation of page 1
+ ** to include the updated change counter and then write page 1
+ ** directly to the database file. Because of the atomic-write
** property of the host file-system, this is safe.
*/
rc = pager_incr_changecounter(pPager, 1);
@@ -56834,7 +63251,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
}
#else /* SQLITE_ENABLE_ATOMIC_WRITE */
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
- if( zMaster ){
+ if( zSuper ){
rc = sqlite3JournalCreate(pPager->jfd);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
assert( bBatch==0 );
@@ -56843,24 +63260,24 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
rc = pager_incr_changecounter(pPager, 0);
#endif /* !SQLITE_ENABLE_ATOMIC_WRITE */
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
-
- /* Write the master journal name into the journal file. If a master
- ** journal file name has already been written to the journal file,
- ** or if zMaster is NULL (no master journal), then this call is a no-op.
+
+ /* Write the super-journal name into the journal file. If a
+ ** super-journal file name has already been written to the journal file,
+ ** or if zSuper is NULL (no super-journal), then this call is a no-op.
*/
- rc = writeMasterJournal(pPager, zMaster);
+ rc = writeSuperJournal(pPager, zSuper);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
-
+
/* Sync the journal file and write all dirty pages to the database.
- ** If the atomic-update optimization is being used, this sync will not
+ ** If the atomic-update optimization is being used, this sync will not
** create the journal file or perform any real IO.
**
** Because the change-counter page was just modified, unless the
** atomic-update optimization is used it is almost certain that the
** journal requires a sync here. However, in locking_mode=exclusive
- ** on a system under memory pressure it is just possible that this is
+ ** on a system under memory pressure it is just possible that this is
** not the case. In this case it is likely enough that the redundant
- ** xSync() call will be changed to a no-op by the OS anyhow.
+ ** xSync() call will be changed to a no-op by the OS anyhow.
*/
rc = syncJournal(pPager, 0);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
@@ -56871,6 +63288,13 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0);
if( rc==SQLITE_OK ){
rc = pager_write_pagelist(pPager, pList);
+ if( rc==SQLITE_OK && pPager->dbSize>pPager->dbFileSize ){
+ char *pTmp = pPager->pTmpSpace;
+ int szPage = (int)pPager->pageSize;
+ memset(pTmp, 0, szPage);
+ rc = sqlite3OsWrite(pPager->fd, pTmp, szPage,
+ ((i64)pPager->dbSize*pPager->pageSize)-szPage);
+ }
if( rc==SQLITE_OK ){
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0);
}
@@ -56901,22 +63325,22 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
}
sqlite3PcacheCleanAll(pPager->pPCache);
- /* If the file on disk is smaller than the database image, use
+ /* If the file on disk is smaller than the database image, use
** pager_truncate to grow the file here. This can happen if the database
** image was extended as part of the current transaction and then the
** last page in the db image moved to the free-list. In this case the
** last page is never written out to disk, leaving the database file
** undersized. Fix this now if it is the case. */
if( pPager->dbSize>pPager->dbFileSize ){
- Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager));
+ Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_SJ_PGNO(pPager));
assert( pPager->eState==PAGER_WRITER_DBMOD );
rc = pager_truncate(pPager, nNew);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
}
-
+
/* Finally, sync the database file. */
if( !noSync ){
- rc = sqlite3PagerSync(pPager, zMaster);
+ rc = sqlite3PagerSync(pPager, zSuper);
}
IOTRACE(("DBSYNC %p\n", pPager))
}
@@ -56933,12 +63357,12 @@ commit_phase_one_exit:
/*
** When this function is called, the database file has been completely
** updated to reflect the changes made by the current transaction and
-** synced to disk. The journal file still exists in the file-system
+** synced to disk. The journal file still exists in the file-system
** though, and if a failure occurs at this point it will eventually
** be used as a hot-journal and the current transaction rolled back.
**
-** This function finalizes the journal file, either by deleting,
-** truncating or partially zeroing it, so that it cannot be used
+** This function finalizes the journal file, either by deleting,
+** truncating or partially zeroing it, so that it cannot be used
** for hot-journal rollback. Once this is done the transaction is
** irrevocably committed.
**
@@ -56952,6 +63376,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager *pPager){
** But if (due to a coding error elsewhere in the system) it does get
** called, just return the same error code without doing anything. */
if( NEVER(pPager->errCode) ) return pPager->errCode;
+ pPager->iDataVersion++;
assert( pPager->eState==PAGER_WRITER_LOCKED
|| pPager->eState==PAGER_WRITER_FINISHED
@@ -56963,15 +63388,15 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager *pPager){
** this transaction, the pager is running in exclusive-mode and is
** using persistent journals, then this function is a no-op.
**
- ** The start of the journal file currently contains a single journal
+ ** The start of the journal file currently contains a single journal
** header with the nRec field set to 0. If such a journal is used as
** a hot-journal during hot-journal rollback, 0 changes will be made
- ** to the database file. So there is no need to zero the journal
+ ** to the database file. So there is no need to zero the journal
** header. Since the pager is in exclusive mode, there is no need
** to drop any locks either.
*/
- if( pPager->eState==PAGER_WRITER_LOCKED
- && pPager->exclusiveMode
+ if( pPager->eState==PAGER_WRITER_LOCKED
+ && pPager->exclusiveMode
&& pPager->journalMode==PAGER_JOURNALMODE_PERSIST
){
assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff );
@@ -56980,13 +63405,12 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager *pPager){
}
PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
- pPager->iDataVersion++;
- rc = pager_end_transaction(pPager, pPager->setMaster, 1);
+ rc = pager_end_transaction(pPager, pPager->setSuper, 1);
return pager_error(pPager, rc);
}
/*
-** If a write transaction is open, then all changes made within the
+** If a write transaction is open, then all changes made within the
** transaction are reverted and the current write-transaction is closed.
** The pager falls back to PAGER_READER state if successful, or PAGER_ERROR
** state if an error occurs.
@@ -56996,14 +63420,14 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager *pPager){
**
** Otherwise, in rollback mode, this function performs two functions:
**
-** 1) It rolls back the journal file, restoring all database file and
+** 1) It rolls back the journal file, restoring all database file and
** in-memory cache pages to the state they were in when the transaction
** was opened, and
**
** 2) It finalizes the journal file, so that it is not used for hot
** rollback at any point in the future.
**
-** Finalization of the journal file (task 2) is only performed if the
+** Finalization of the journal file (task 2) is only performed if the
** rollback is successful.
**
** In WAL mode, all cache-entries containing data modified within the
@@ -57016,7 +63440,7 @@ SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
PAGERTRACE(("ROLLBACK %d\n", PAGERID(pPager)));
/* PagerRollback() is a no-op if called in READER or OPEN state. If
- ** the pager is already in the ERROR state, the rollback is not
+ ** the pager is already in the ERROR state, the rollback is not
** attempted here. Instead, the error code is returned to the caller.
*/
assert( assert_pager_state(pPager) );
@@ -57026,13 +63450,13 @@ SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
if( pagerUseWal(pPager) ){
int rc2;
rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1);
- rc2 = pager_end_transaction(pPager, pPager->setMaster, 0);
+ rc2 = pager_end_transaction(pPager, pPager->setSuper, 0);
if( rc==SQLITE_OK ) rc = rc2;
}else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){
int eState = pPager->eState;
rc = pager_end_transaction(pPager, 0, 0);
if( !MEMDB && eState>PAGER_WRITER_LOCKED ){
- /* This can happen using journal_mode=off. Move the pager to the error
+ /* This can happen using journal_mode=off. Move the pager to the error
** state to indicate that the contents of the cache may not be trusted.
** Any active readers will get SQLITE_ABORT.
*/
@@ -57047,7 +63471,7 @@ SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK );
assert( rc==SQLITE_OK || rc==SQLITE_FULL || rc==SQLITE_CORRUPT
- || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR
+ || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR
|| rc==SQLITE_CANTOPEN
);
@@ -57079,8 +63503,8 @@ SQLITE_PRIVATE int sqlite3PagerRefcount(Pager *pPager){
** used by the pager and its associated cache.
*/
SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager *pPager){
- int perPageSize = pPager->pageSize + pPager->nExtra + sizeof(PgHdr)
- + 5*sizeof(void*);
+ int perPageSize = pPager->pageSize + pPager->nExtra
+ + (int)(sizeof(PgHdr) + 5*sizeof(void*));
return perPageSize*sqlite3PcachePagecount(pPager->pPCache)
+ sqlite3MallocSize(pPager)
+ pPager->pageSize;
@@ -57105,11 +63529,11 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
a[4] = pPager->eState;
a[5] = pPager->errCode;
- a[6] = pPager->aStat[PAGER_STAT_HIT];
- a[7] = pPager->aStat[PAGER_STAT_MISS];
+ a[6] = (int)pPager->aStat[PAGER_STAT_HIT] & 0x7fffffff;
+ a[7] = (int)pPager->aStat[PAGER_STAT_MISS] & 0x7fffffff;
a[8] = 0; /* Used to be pPager->nOvfl */
a[9] = pPager->nRead;
- a[10] = pPager->aStat[PAGER_STAT_WRITE];
+ a[10] = (int)pPager->aStat[PAGER_STAT_WRITE] & 0x7fffffff;
return a;
}
#endif
@@ -57121,11 +63545,11 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
** it was added later.
**
** Before returning, *pnVal is incremented by the
-** current cache hit or miss count, according to the value of eStat. If the
-** reset parameter is non-zero, the cache hit or miss count is zeroed before
+** current cache hit or miss count, according to the value of eStat. If the
+** reset parameter is non-zero, the cache hit or miss count is zeroed before
** returning.
*/
-SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
+SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, u64 *pnVal){
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|| eStat==SQLITE_DBSTATUS_CACHE_MISS
@@ -57149,7 +63573,7 @@ SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, i
** Return true if this is an in-memory or temp-file backed pager.
*/
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager *pPager){
- return pPager->tempFile;
+ return pPager->tempFile || pPager->memVfs;
}
/*
@@ -57158,7 +63582,7 @@ SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager *pPager){
** to make up the difference. If the number of savepoints is already
** equal to nSavepoint, then this function is a no-op.
**
-** If a memory allocation fails, SQLITE_NOMEM is returned. If an error
+** If a memory allocation fails, SQLITE_NOMEM is returned. If an error
** occurs while opening the sub-journal file, then an IO error code is
** returned. Otherwise, SQLITE_OK.
*/
@@ -57173,7 +63597,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){
assert( nSavepoint>nCurrent && pPager->useJournal );
/* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
- ** if the allocation fails. Otherwise, zero the new portion in case a
+ ** if the allocation fails. Otherwise, zero the new portion in case a
** malloc failure occurs while populating it in the for(...) loop below.
*/
aNew = (PagerSavepoint *)sqlite3Realloc(
@@ -57195,6 +63619,7 @@ static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){
}
aNew[ii].iSubRec = pPager->nSubRec;
aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
+ aNew[ii].bTruncateOnRelease = 1;
if( !aNew[ii].pInSavepoint ){
return SQLITE_NOMEM_BKPT;
}
@@ -57221,7 +63646,7 @@ SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
/*
** This function is called to rollback or release (commit) a savepoint.
-** The savepoint to release or rollback need not be the most recently
+** The savepoint to release or rollback need not be the most recently
** created savepoint.
**
** Parameter op is always either SAVEPOINT_ROLLBACK or SAVEPOINT_RELEASE.
@@ -57229,29 +63654,29 @@ SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
** index iSavepoint. If it is SAVEPOINT_ROLLBACK, then rollback all changes
** that have occurred since the specified savepoint was created.
**
-** The savepoint to rollback or release is identified by parameter
+** The savepoint to rollback or release is identified by parameter
** iSavepoint. A value of 0 means to operate on the outermost savepoint
** (the first created). A value of (Pager.nSavepoint-1) means operate
** on the most recently created savepoint. If iSavepoint is greater than
** (Pager.nSavepoint-1), then this function is a no-op.
**
** If a negative value is passed to this function, then the current
-** transaction is rolled back. This is different to calling
+** transaction is rolled back. This is different to calling
** sqlite3PagerRollback() because this function does not terminate
-** the transaction or unlock the database, it just restores the
-** contents of the database to its original state.
+** the transaction or unlock the database, it just restores the
+** contents of the database to its original state.
**
-** In any case, all savepoints with an index greater than iSavepoint
+** In any case, all savepoints with an index greater than iSavepoint
** are destroyed. If this is a release operation (op==SAVEPOINT_RELEASE),
** then savepoint iSavepoint is also destroyed.
**
** This function may return SQLITE_NOMEM if a memory allocation fails,
-** or an IO error code if an IO error occurs while rolling back a
+** or an IO error code if an IO error occurs while rolling back a
** savepoint. If no errors occur, SQLITE_OK is returned.
-*/
+*/
SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
int rc = pPager->errCode;
-
+
#ifdef SQLITE_ENABLE_ZIPVFS
if( op==SAVEPOINT_RELEASE ) rc = SQLITE_OK;
#endif
@@ -57264,7 +63689,7 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
int nNew; /* Number of remaining savepoints after this op. */
/* Figure out how many savepoints will still be active after this
- ** operation. Store this value in nNew. Then free resources associated
+ ** operation. Store this value in nNew. Then free resources associated
** with any savepoints that are destroyed by this operation.
*/
nNew = iSavepoint + (( op==SAVEPOINT_RELEASE ) ? 0 : 1);
@@ -57273,16 +63698,18 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
}
pPager->nSavepoint = nNew;
- /* If this is a release of the outermost savepoint, truncate
- ** the sub-journal to zero bytes in size. */
+ /* Truncate the sub-journal so that it only includes the parts
+ ** that are still in use. */
if( op==SAVEPOINT_RELEASE ){
- if( nNew==0 && isOpen(pPager->sjfd) ){
+ PagerSavepoint *pRel = &pPager->aSavepoint[nNew];
+ if( pRel->bTruncateOnRelease && isOpen(pPager->sjfd) ){
/* Only truncate if it is an in-memory sub-journal. */
if( sqlite3JournalIsInMemory(pPager->sjfd) ){
- rc = sqlite3OsTruncate(pPager->sjfd, 0);
+ i64 sz = (pPager->pageSize+4)*(i64)pRel->iSubRec;
+ rc = sqlite3OsTruncate(pPager->sjfd, sz);
assert( rc==SQLITE_OK );
}
- pPager->nSubRec = 0;
+ pPager->nSubRec = pRel->iSubRec;
}
}
/* Else this is a rollback operation, playback the specified savepoint.
@@ -57295,14 +63722,14 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
rc = pagerPlaybackSavepoint(pPager, pSavepoint);
assert(rc!=SQLITE_DONE);
}
-
+
#ifdef SQLITE_ENABLE_ZIPVFS
- /* If the cache has been modified but the savepoint cannot be rolled
+ /* If the cache has been modified but the savepoint cannot be rolled
** back journal_mode=off, put the pager in the error state. This way,
** if the VFS used by this pager includes ZipVFS, the entire transaction
** can be rolled back at the ZipVFS level. */
- else if(
- pPager->journalMode==PAGER_JOURNALMODE_OFF
+ else if(
+ pPager->journalMode==PAGER_JOURNALMODE_OFF
&& pPager->eState>=PAGER_WRITER_CACHEMOD
){
pPager->errCode = SQLITE_ABORT;
@@ -57324,9 +63751,17 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
** behavior. But when the Btree needs to know the filename for matching to
** shared cache, it uses nullIfMemDb==0 so that in-memory databases can
** participate in shared-cache.
+**
+** The return value to this routine is always safe to use with
+** sqlite3_uri_parameter() and sqlite3_filename_database() and friends.
*/
-SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager *pPager, int nullIfMemDb){
- return (nullIfMemDb && pPager->memDb) ? "" : pPager->zFilename;
+SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager *pPager, int nullIfMemDb){
+ static const char zFake[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ if( nullIfMemDb && (pPager->memDb || sqlite3IsMemdb(pPager->pVfs)) ){
+ return &zFake[4];
+ }else{
+ return pPager->zFilename;
+ }
}
/*
@@ -57345,16 +63780,6 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){
return pPager->fd;
}
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
-/*
-** Reset the lock timeout for pager.
-*/
-SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager){
- int x = 0;
- sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x);
-}
-#endif
-
/*
** Return the file handle for the journal file (if it exists).
** This will be either the rollback journal or the WAL file.
@@ -57374,54 +63799,6 @@ SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager *pPager){
return pPager->zJournal;
}
-#ifdef SQLITE_HAS_CODEC
-/*
-** Set or retrieve the codec for this pager
-*/
-SQLITE_PRIVATE void sqlite3PagerSetCodec(
- Pager *pPager,
- void *(*xCodec)(void*,void*,Pgno,int),
- void (*xCodecSizeChng)(void*,int,int),
- void (*xCodecFree)(void*),
- void *pCodec
-){
- if( pPager->xCodecFree ){
- pPager->xCodecFree(pPager->pCodec);
- }else{
- pager_reset(pPager);
- }
- pPager->xCodec = pPager->memDb ? 0 : xCodec;
- pPager->xCodecSizeChng = xCodecSizeChng;
- pPager->xCodecFree = xCodecFree;
- pPager->pCodec = pCodec;
- setGetterMethod(pPager);
- pagerReportSize(pPager);
-}
-SQLITE_PRIVATE void *sqlite3PagerGetCodec(Pager *pPager){
- return pPager->pCodec;
-}
-
-/*
-** This function is called by the wal module when writing page content
-** into the log file.
-**
-** This function returns a pointer to a buffer containing the encrypted
-** page content. If a malloc fails, this function may return NULL.
-*/
-SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
- void *aData = 0;
- CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
- return aData;
-}
-
-/*
-** Return the current pager state
-*/
-SQLITE_PRIVATE int sqlite3PagerState(Pager *pPager){
- return pPager->eState;
-}
-#endif /* SQLITE_HAS_CODEC */
-
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Move the page pPg to location pgno in the file.
@@ -57441,8 +63818,8 @@ SQLITE_PRIVATE int sqlite3PagerState(Pager *pPager){
** transaction is active).
**
** If the fourth argument, isCommit, is non-zero, then this page is being
-** moved as part of a database reorganization just before the transaction
-** is being committed. In this case, it is guaranteed that the database page
+** moved as part of a database reorganization just before the transaction
+** is being committed. In this case, it is guaranteed that the database page
** pPg refers to will not be written to again within this transaction.
**
** This function may return SQLITE_NOMEM or an IO error code if an error
@@ -57470,7 +63847,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
}
/* If the page being moved is dirty and has not been saved by the latest
- ** savepoint, then save the current contents of the page into the
+ ** savepoint, then save the current contents of the page into the
** sub-journal now. This is required to handle the following scenario:
**
** BEGIN;
@@ -57493,7 +63870,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
return rc;
}
- PAGERTRACE(("MOVE %d page %d (needSync=%d) moves to %d\n",
+ PAGERTRACE(("MOVE %d page %d (needSync=%d) moves to %d\n",
PAGERID(pPager), pPg->pgno, (pPg->flags&PGHDR_NEED_SYNC)?1:0, pgno));
IOTRACE(("MOVE %p %d %d\n", pPager, pPg->pgno, pgno))
@@ -57501,7 +63878,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
** be written to, store pPg->pgno in local variable needSyncPgno.
**
** If the isCommit flag is set, there is no need to remember that
- ** the journal needs to be sync()ed before database page pPg->pgno
+ ** the journal needs to be sync()ed before database page pPg->pgno
** can be written to. The caller has already promised not to write to it.
*/
if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
@@ -57512,14 +63889,18 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
}
/* If the cache contains a page with page-number pgno, remove it
- ** from its hash chain. Also, if the PGHDR_NEED_SYNC flag was set for
- ** page pgno before the 'move' operation, it needs to be retained
+ ** from its hash chain. Also, if the PGHDR_NEED_SYNC flag was set for
+ ** page pgno before the 'move' operation, it needs to be retained
** for the page moved there.
*/
pPg->flags &= ~PGHDR_NEED_SYNC;
pPgOld = sqlite3PagerLookup(pPager, pgno);
- assert( !pPgOld || pPgOld->nRef==1 );
+ assert( !pPgOld || pPgOld->nRef==1 || CORRUPT_DB );
if( pPgOld ){
+ if( NEVER(pPgOld->nRef>1) ){
+ sqlite3PagerUnrefNotNull(pPgOld);
+ return SQLITE_CORRUPT_BKPT;
+ }
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
if( pPager->tempFile ){
/* Do not discard pages from an in-memory database since we might
@@ -57544,9 +63925,9 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
}
if( needSyncPgno ){
- /* If needSyncPgno is non-zero, then the journal file needs to be
+ /* If needSyncPgno is non-zero, then the journal file needs to be
** sync()ed before any data is written to database file page needSyncPgno.
- ** Currently, no such page exists in the page-cache and the
+ ** Currently, no such page exists in the page-cache and the
** "is journaled" bitvec flag has been set. This needs to be remedied by
** loading the page into the pager-cache and setting the PGHDR_NEED_SYNC
** flag.
@@ -57577,9 +63958,9 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
#endif
/*
-** The page handle passed as the first argument refers to a dirty page
-** with a page number other than iNew. This function changes the page's
-** page number to iNew and sets the value of the PgHdr.flags field to
+** The page handle passed as the first argument refers to a dirty page
+** with a page number other than iNew. This function changes the page's
+** page number to iNew and sets the value of the PgHdr.flags field to
** the value passed as the third parameter.
*/
SQLITE_PRIVATE void sqlite3PagerRekey(DbPage *pPg, Pgno iNew, u16 flags){
@@ -57597,7 +63978,7 @@ SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *pPg){
}
/*
-** Return a pointer to the Pager.nExtra bytes of "extra" space
+** Return a pointer to the Pager.nExtra bytes of "extra" space
** allocated along with the specified page.
*/
SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *pPg){
@@ -57606,7 +63987,7 @@ SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *pPg){
/*
** Get/set the locking-mode for this pager. Parameter eMode must be one
-** of PAGER_LOCKINGMODE_QUERY, PAGER_LOCKINGMODE_NORMAL or
+** of PAGER_LOCKINGMODE_QUERY, PAGER_LOCKINGMODE_NORMAL or
** PAGER_LOCKINGMODE_EXCLUSIVE. If the parameter is not _QUERY, then
** the locking-mode is set to the value specified.
**
@@ -57651,12 +64032,12 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
u8 eOld = pPager->journalMode; /* Prior journalmode */
/* The eMode parameter is always valid */
- assert( eMode==PAGER_JOURNALMODE_DELETE
- || eMode==PAGER_JOURNALMODE_TRUNCATE
- || eMode==PAGER_JOURNALMODE_PERSIST
- || eMode==PAGER_JOURNALMODE_OFF
- || eMode==PAGER_JOURNALMODE_WAL
- || eMode==PAGER_JOURNALMODE_MEMORY );
+ assert( eMode==PAGER_JOURNALMODE_DELETE /* 0 */
+ || eMode==PAGER_JOURNALMODE_PERSIST /* 1 */
+ || eMode==PAGER_JOURNALMODE_OFF /* 2 */
+ || eMode==PAGER_JOURNALMODE_TRUNCATE /* 3 */
+ || eMode==PAGER_JOURNALMODE_MEMORY /* 4 */
+ || eMode==PAGER_JOURNALMODE_WAL /* 5 */ );
/* This routine is only called from the OP_JournalMode opcode, and
** the logic there will never allow a temporary file to be changed
@@ -57680,7 +64061,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
assert( pPager->eState!=PAGER_ERROR );
pPager->journalMode = (u8)eMode;
- /* When transistioning from TRUNCATE or PERSIST to any other journal
+ /* When transitioning from TRUNCATE or PERSIST to any other journal
** mode except WAL, unless the pager is in locking_mode=exclusive mode,
** delete the journal file.
*/
@@ -57693,7 +64074,6 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
assert( isOpen(pPager->fd) || pPager->exclusiveMode );
if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){
-
/* In this case we would like to delete the journal file. If it is
** not possible, then that is not a problem. Deleting the journal file
** here is an optimization only.
@@ -57726,7 +64106,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
}
assert( state==pPager->eState );
}
- }else if( eMode==PAGER_JOURNALMODE_OFF ){
+ }else if( eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_MEMORY ){
sqlite3OsClose(pPager->jfd);
}
}
@@ -57805,6 +64185,18 @@ SQLITE_PRIVATE int sqlite3PagerCheckpoint(
int *pnCkpt /* OUT: Final number of checkpointed frames */
){
int rc = SQLITE_OK;
+ if( pPager->pWal==0 && pPager->journalMode==PAGER_JOURNALMODE_WAL ){
+ /* This only happens when a database file is zero bytes in size opened and
+ ** then "PRAGMA journal_mode=WAL" is run and then sqlite3_wal_checkpoint()
+ ** is invoked without any intervening transactions. We need to start
+ ** a transaction to initialize pWal. The PRAGMA table_list statement is
+ ** used for this since it starts transactions on every database file,
+ ** including all ATTACHed databases. This seems expensive for a single
+ ** sqlite3_wal_checkpoint() call, but it happens very rarely.
+ ** https://sqlite.org/forum/forumpost/fd0f19d229156939
+ */
+ sqlite3_exec(db, "PRAGMA table_list",0,0,0);
+ }
if( pPager->pWal ){
rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
(eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
@@ -57812,7 +64204,6 @@ SQLITE_PRIVATE int sqlite3PagerCheckpoint(
pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
pnLog, pnCkpt
);
- sqlite3PagerResetLockTimeout(pPager);
}
return rc;
}
@@ -57837,20 +64228,22 @@ SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager){
*/
static int pagerExclusiveLock(Pager *pPager){
int rc; /* Return code */
+ u8 eOrigLock; /* Original lock */
- assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK );
+ assert( pPager->eLock>=SHARED_LOCK );
+ eOrigLock = pPager->eLock;
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
if( rc!=SQLITE_OK ){
- /* If the attempt to grab the exclusive lock failed, release the
+ /* If the attempt to grab the exclusive lock failed, release the
** pending lock that may have been obtained instead. */
- pagerUnlockDb(pPager, SHARED_LOCK);
+ pagerUnlockDb(pPager, eOrigLock);
}
return rc;
}
/*
-** Call sqlite3WalOpen() to open the WAL handle. If the pager is in
+** Call sqlite3WalOpen() to open the WAL handle. If the pager is in
** exclusive-locking mode when this function is called, take an EXCLUSIVE
** lock on the database file and use heap-memory to store the wal-index
** in. Otherwise, use the normal shared-memory.
@@ -57861,8 +64254,8 @@ static int pagerOpenWal(Pager *pPager){
assert( pPager->pWal==0 && pPager->tempFile==0 );
assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK );
- /* If the pager is already in exclusive-mode, the WAL module will use
- ** heap-memory for the wal-index instead of the VFS shared-memory
+ /* If the pager is already in exclusive-mode, the WAL module will use
+ ** heap-memory for the wal-index instead of the VFS shared-memory
** implementation. Take the exclusive lock now, before opening the WAL
** file, to make sure this is safe.
*/
@@ -57870,7 +64263,7 @@ static int pagerOpenWal(Pager *pPager){
rc = pagerExclusiveLock(pPager);
}
- /* Open the connection to the log file. If this operation fails,
+ /* Open the connection to the log file. If this operation fails,
** (e.g. due to malloc() failure), return an error code.
*/
if( rc==SQLITE_OK ){
@@ -57892,7 +64285,7 @@ static int pagerOpenWal(Pager *pPager){
** If the pager passed as the first argument is open on a real database
** file (not a temp file or an in-memory database), and the WAL file
** is not already open, make an attempt to open it now. If successful,
-** return SQLITE_OK. If an error occurs or the VFS used by the pager does
+** return SQLITE_OK. If an error occurs or the VFS used by the pager does
** not support the xShmXXX() methods, return an error code. *pbOpen is
** not modified in either case.
**
@@ -57934,7 +64327,7 @@ SQLITE_PRIVATE int sqlite3PagerOpenWal(
** This function is called to close the connection to the log file prior
** to switching from WAL to rollback mode.
**
-** Before closing the log file, this function attempts to take an
+** Before closing the log file, this function attempts to take an
** EXCLUSIVE lock on the database file. If this cannot be obtained, an
** error (SQLITE_BUSY) is returned and the log connection is not closed.
** If successful, the EXCLUSIVE lock is not released before returning.
@@ -57960,7 +64353,7 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){
rc = pagerOpenWal(pPager);
}
}
-
+
/* Checkpoint and close the log. Because an EXCLUSIVE lock is held on
** the database file, the log and log-summary files will be deleted.
*/
@@ -57977,6 +64370,32 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){
return rc;
}
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+/*
+** If pager pPager is a wal-mode database not in exclusive locking mode,
+** invoke the sqlite3WalWriteLock() function on the associated Wal object
+** with the same db and bLock parameters as were passed to this function.
+** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise.
+*/
+SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager *pPager, int bLock){
+ int rc = SQLITE_OK;
+ if( pagerUseWal(pPager) && pPager->exclusiveMode==0 ){
+ rc = sqlite3WalWriteLock(pPager->pWal, bLock);
+ }
+ return rc;
+}
+
+/*
+** Set the database handle used by the wal layer to determine if
+** blocking locks are required.
+*/
+SQLITE_PRIVATE void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){
+ if( pagerUseWal(pPager) ){
+ sqlite3WalDb(pPager->pWal, db);
+ }
+}
+#endif
+
#ifdef SQLITE_ENABLE_SNAPSHOT
/*
** If this is a WAL database, obtain a snapshot handle for the snapshot
@@ -57992,10 +64411,13 @@ SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppS
/*
** If this is a WAL database, store a pointer to pSnapshot. Next time a
-** read transaction is opened, attempt to read from the snapshot it
+** read transaction is opened, attempt to read from the snapshot it
** identifies. If this is not a WAL database, return an error.
*/
-SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){
+SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(
+ Pager *pPager,
+ sqlite3_snapshot *pSnapshot
+){
int rc = SQLITE_OK;
if( pPager->pWal ){
sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot);
@@ -58006,7 +64428,7 @@ SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSn
}
/*
-** If this is a WAL database, call sqlite3WalSnapshotRecover(). If this
+** If this is a WAL database, call sqlite3WalSnapshotRecover(). If this
** is not a WAL database, return an error.
*/
SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager){
@@ -58023,7 +64445,7 @@ SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager){
** The caller currently has a read transaction open on the database.
** If this is not a WAL database, SQLITE_ERROR is returned. Otherwise,
** this function takes a SHARED lock on the CHECKPOINTER slot and then
-** checks if the snapshot passed as the second argument is still
+** checks if the snapshot passed as the second argument is still
** available. If so, SQLITE_OK is returned.
**
** If the snapshot is not available, SQLITE_ERROR is returned. Or, if
@@ -58047,7 +64469,7 @@ SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pS
*/
SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager){
assert( pPager->pWal );
- return sqlite3WalSnapshotUnlock(pPager->pWal);
+ sqlite3WalSnapshotUnlock(pPager->pWal);
}
#endif /* SQLITE_ENABLE_SNAPSHOT */
@@ -58067,6 +64489,12 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
}
#endif
+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
+SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){
+ return sqlite3WalSystemErrno(pPager->pWal);
+}
+#endif
+
#endif /* SQLITE_OMIT_DISKIO */
/************** End of pager.c ***********************************************/
@@ -58083,7 +64511,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
**
*************************************************************************
**
-** This file contains the implementation of a write-ahead log (WAL) used in
+** This file contains the implementation of a write-ahead log (WAL) used in
** "journal_mode=WAL" mode.
**
** WRITE-AHEAD LOG (WAL) FILE FORMAT
@@ -58092,7 +64520,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
** Each frame records the revised content of a single page from the
** database file. All changes to the database are recorded by writing
** frames into the WAL. Transactions commit when a frame is written that
-** contains a commit marker. A single WAL can and usually does record
+** contains a commit marker. A single WAL can and usually does record
** multiple transactions. Periodically, the content of the WAL is
** transferred back into the database file in an operation called a
** "checkpoint".
@@ -58118,11 +64546,11 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
**
** Immediately following the wal-header are zero or more frames. Each
** frame consists of a 24-byte frame-header followed by a <page-size> bytes
-** of page data. The frame-header is six big-endian 32-bit unsigned
+** of page data. The frame-header is six big-endian 32-bit unsigned
** integer values, as follows:
**
** 0: Page number.
-** 4: For commit records, the size of the database image in pages
+** 4: For commit records, the size of the database image in pages
** after the commit. For all other records, zero.
** 8: Salt-1 (copied from the header)
** 12: Salt-2 (copied from the header)
@@ -58148,7 +64576,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
** the checksum. The checksum is computed by interpreting the input as
** an even number of unsigned 32-bit integers: x[0] through x[N]. The
** algorithm used for the checksum is as follows:
-**
+**
** for i from 0 to n-1 step 2:
** s0 += x[i] + s1;
** s1 += x[i+1] + s0;
@@ -58156,7 +64584,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
**
** Note that s0 and s1 are both weighted checksums using fibonacci weights
** in reverse order (the largest fibonacci weight occurs on the first element
-** of the sequence being summed.) The s1 value spans all 32-bit
+** of the sequence being summed.) The s1 value spans all 32-bit
** terms of the sequence whereas s0 omits the final term.
**
** On a checkpoint, the WAL is first VFS.xSync-ed, then valid content of the
@@ -58189,19 +64617,19 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
** multiple concurrent readers to view different versions of the database
** content simultaneously.
**
-** The reader algorithm in the previous paragraphs works correctly, but
+** The reader algorithm in the previous paragraphs works correctly, but
** because frames for page P can appear anywhere within the WAL, the
** reader has to scan the entire WAL looking for page P frames. If the
** WAL is large (multiple megabytes is typical) that scan can be slow,
** and read performance suffers. To overcome this problem, a separate
** data structure called the wal-index is maintained to expedite the
** search for frames of a particular page.
-**
+**
** WAL-INDEX FORMAT
**
** Conceptually, the wal-index is shared memory, though VFS implementations
** might choose to implement the wal-index using a mmapped file. Because
-** the wal-index is shared memory, SQLite does not support journal_mode=WAL
+** the wal-index is shared memory, SQLite does not support journal_mode=WAL
** on a network filesystem. All users of the database must be able to
** share memory.
**
@@ -58219,28 +64647,31 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
** byte order of the host computer.
**
** The purpose of the wal-index is to answer this question quickly: Given
-** a page number P and a maximum frame index M, return the index of the
+** a page number P and a maximum frame index M, return the index of the
** last frame in the wal before frame M for page P in the WAL, or return
** NULL if there are no frames for page P in the WAL prior to M.
**
** The wal-index consists of a header region, followed by an one or
-** more index blocks.
+** more index blocks.
**
** The wal-index header contains the total number of frames within the WAL
** in the mxFrame field.
**
-** Each index block except for the first contains information on
+** Each index block except for the first contains information on
** HASHTABLE_NPAGE frames. The first index block contains information on
-** HASHTABLE_NPAGE_ONE frames. The values of HASHTABLE_NPAGE_ONE and
+** HASHTABLE_NPAGE_ONE frames. The values of HASHTABLE_NPAGE_ONE and
** HASHTABLE_NPAGE are selected so that together the wal-index header and
** first index block are the same size as all other index blocks in the
-** wal-index.
+** wal-index. The values are:
+**
+** HASHTABLE_NPAGE 4096
+** HASHTABLE_NPAGE_ONE 4062
**
** Each index block contains two sections, a page-mapping that contains the
-** database page number associated with each wal frame, and a hash-table
+** database page number associated with each wal frame, and a hash-table
** that allows readers to query an index block for a specific page number.
** The page-mapping is an array of HASHTABLE_NPAGE (or HASHTABLE_NPAGE_ONE
-** for the first index block) 32-bit page numbers. The first entry in the
+** for the first index block) 32-bit page numbers. The first entry in the
** first index-block contains the database page number corresponding to the
** first frame in the WAL file. The first entry in the second index block
** in the WAL file corresponds to the (HASHTABLE_NPAGE_ONE+1)th frame in
@@ -58261,8 +64692,8 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
**
** The hash table consists of HASHTABLE_NSLOT 16-bit unsigned integers.
** HASHTABLE_NSLOT = 2*HASHTABLE_NPAGE, and there is one entry in the
-** hash table for each page number in the mapping section, so the hash
-** table is never more than half full. The expected number of collisions
+** hash table for each page number in the mapping section, so the hash
+** table is never more than half full. The expected number of collisions
** prior to finding a match is 1. Each entry of the hash table is an
** 1-based index of an entry in the mapping section of the same
** index block. Let K be the 1-based index of the largest entry in
@@ -58281,12 +64712,12 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
** reached) until an unused hash slot is found. Let the first unused slot
** be at index iUnused. (iUnused might be less than iKey if there was
** wrap-around.) Because the hash table is never more than half full,
-** the search is guaranteed to eventually hit an unused entry. Let
+** the search is guaranteed to eventually hit an unused entry. Let
** iMax be the value between iKey and iUnused, closest to iUnused,
** where aHash[iMax]==P. If there is no iMax entry (if there exists
** no hash slot such that aHash[i]==p) then page P is not in the
** current index block. Otherwise the iMax-th mapping entry of the
-** current index block corresponds to the last entry that references
+** current index block corresponds to the last entry that references
** page P.
**
** A hash search begins with the last index block and moves toward the
@@ -58311,7 +64742,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
** if no values greater than K0 had ever been inserted into the hash table
** in the first place - which is what reader one wants. Meanwhile, the
** second reader using K1 will see additional values that were inserted
-** later, which is exactly what reader two wants.
+** later, which is exactly what reader two wants.
**
** When a rollback occurs, the value of K is decreased. Hash table entries
** that correspond to frames greater than the new K value are removed
@@ -58332,18 +64763,6 @@ SQLITE_PRIVATE int sqlite3WalTrace = 0;
#endif
/*
-** WAL mode depends on atomic aligned 32-bit loads and stores in a few
-** places. The following macros try to make this explicit.
-*/
-#if GCC_VESRION>=5004000
-# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED)
-# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
-#else
-# define AtomicLoad(PTR) (*(PTR))
-# define AtomicStore(PTR,VAL) (*(PTR) = (VAL))
-#endif
-
-/*
** The maximum (and only) versions of the wal and wal-index formats
** that may be interpreted by this version of SQLite.
**
@@ -58351,7 +64770,7 @@ SQLITE_PRIVATE int sqlite3WalTrace = 0;
** values in the wal-header are correct and (b) the version field is not
** WAL_MAX_VERSION, recovery fails and SQLite returns SQLITE_CANTOPEN.
**
-** Similarly, if a client successfully reads a wal-index header (i.e. the
+** Similarly, if a client successfully reads a wal-index header (i.e. the
** checksum test is successful) and finds that the version field is not
** WALINDEX_MAX_VERSION, then no read-transaction is opened and SQLite
** returns SQLITE_CANTOPEN.
@@ -58366,7 +64785,7 @@ SQLITE_PRIVATE int sqlite3WalTrace = 0;
**
** Technically, the various VFSes are free to implement these locks however
** they see fit. However, compatibility is encouraged so that VFSes can
-** interoperate. The standard implemention used on both unix and windows
+** interoperate. The standard implementation used on both unix and windows
** is for the index number to indicate a byte offset into the
** WalCkptInfo.aLock[] array in the wal-index header. In other words, all
** locks are on the shm file. The WALINDEX_LOCK_OFFSET constant (which
@@ -58398,7 +64817,7 @@ typedef struct WalCkptInfo WalCkptInfo;
**
** The szPage value can be any power of 2 between 512 and 32768, inclusive.
** Or it can be 1 to represent a 65536-byte page. The latter case was
-** added in 3.7.1 when support for 64K pages was added.
+** added in 3.7.1 when support for 64K pages was added.
*/
struct WalIndexHdr {
u32 iVersion; /* Wal-index version */
@@ -58440,9 +64859,9 @@ struct WalIndexHdr {
** There is one entry in aReadMark[] for each reader lock. If a reader
** holds read-lock K, then the value in aReadMark[K] is no greater than
** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff)
-** for any aReadMark[] means that entry is unused. aReadMark[0] is
+** for any aReadMark[] means that entry is unused. aReadMark[0] is
** a special case; its value is never used and it exists as a place-holder
-** to avoid having to offset aReadMark[] indexs by one. Readers holding
+** to avoid having to offset aReadMark[] indexes by one. Readers holding
** WAL_READ_LOCK(0) always ignore the entire WAL and read all content
** directly from the database.
**
@@ -58460,7 +64879,7 @@ struct WalIndexHdr {
** previous sentence is when nBackfill equals mxFrame (meaning that everything
** in the WAL has been backfilled into the database) then new readers
** will choose aReadMark[0] which has value 0 and hence such reader will
-** get all their all content directly from the database file and ignore
+** get all their all content directly from the database file and ignore
** the WAL.
**
** Writers normally append new frames to the end of the WAL. However,
@@ -58482,6 +64901,70 @@ struct WalCkptInfo {
};
#define READMARK_NOT_USED 0xffffffff
+/*
+** This is a schematic view of the complete 136-byte header of the
+** wal-index file (also known as the -shm file):
+**
+** +-----------------------------+
+** 0: | iVersion | \
+** +-----------------------------+ |
+** 4: | (unused padding) | |
+** +-----------------------------+ |
+** 8: | iChange | |
+** +-------+-------+-------------+ |
+** 12: | bInit | bBig | szPage | |
+** +-------+-------+-------------+ |
+** 16: | mxFrame | | First copy of the
+** +-----------------------------+ | WalIndexHdr object
+** 20: | nPage | |
+** +-----------------------------+ |
+** 24: | aFrameCksum | |
+** | | |
+** +-----------------------------+ |
+** 32: | aSalt | |
+** | | |
+** +-----------------------------+ |
+** 40: | aCksum | |
+** | | /
+** +-----------------------------+
+** 48: | iVersion | \
+** +-----------------------------+ |
+** 52: | (unused padding) | |
+** +-----------------------------+ |
+** 56: | iChange | |
+** +-------+-------+-------------+ |
+** 60: | bInit | bBig | szPage | |
+** +-------+-------+-------------+ | Second copy of the
+** 64: | mxFrame | | WalIndexHdr
+** +-----------------------------+ |
+** 68: | nPage | |
+** +-----------------------------+ |
+** 72: | aFrameCksum | |
+** | | |
+** +-----------------------------+ |
+** 80: | aSalt | |
+** | | |
+** +-----------------------------+ |
+** 88: | aCksum | |
+** | | /
+** +-----------------------------+
+** 96: | nBackfill |
+** +-----------------------------+
+** 100: | 5 read marks |
+** | |
+** | |
+** | |
+** | |
+** +-------+-------+------+------+
+** 120: | Write | Ckpt | Rcvr | Rd0 | \
+** +-------+-------+------+------+ ) 8 lock bytes
+** | Read1 | Read2 | Rd3 | Rd4 | /
+** +-------+-------+------+------+
+** 128: | nBackfillAttempted |
+** +-----------------------------+
+** 132: | (unused padding) |
+** +-----------------------------+
+*/
/* A block of WALINDEX_LOCK_RESERVED bytes beginning at
** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems
@@ -58502,14 +64985,14 @@ struct WalCkptInfo {
** big-endian format in the first 4 bytes of a WAL file.
**
** If the LSB is set, then the checksums for each frame within the WAL
-** file are calculated by treating all data as an array of 32-bit
-** big-endian words. Otherwise, they are calculated by interpreting
+** file are calculated by treating all data as an array of 32-bit
+** big-endian words. Otherwise, they are calculated by interpreting
** all data as 32-bit little-endian words.
*/
#define WAL_MAGIC 0x377f0682
/*
-** Return the offset of frame iFrame in the write-ahead log file,
+** Return the offset of frame iFrame in the write-ahead log file,
** assuming a database page size of szPage bytes. The offset returned
** is to the start of the write-ahead log frame-header.
*/
@@ -58546,19 +65029,30 @@ struct Wal {
u32 iReCksum; /* On commit, recalculate checksums from here */
const char *zWalName; /* Name of WAL file */
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
+#ifdef SQLITE_USE_SEH
+ u32 lockMask; /* Mask of locks held */
+ void *pFree; /* Pointer to sqlite3_free() if exception thrown */
+ u32 *pWiValue; /* Value to write into apWiData[iWiPg] */
+ int iWiPg; /* Write pWiValue into apWiData[iWiPg] */
+ int iSysErrno; /* System error code following exception */
+#endif
#ifdef SQLITE_DEBUG
+ int nSehTry; /* Number of nested SEH_TRY{} blocks */
u8 lockError; /* True if a locking error has occurred */
#endif
#ifdef SQLITE_ENABLE_SNAPSHOT
WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */
#endif
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ sqlite3 *db;
+#endif
};
/*
** Candidate values for Wal.exclusiveMode.
*/
#define WAL_NORMAL_MODE 0
-#define WAL_EXCLUSIVE_MODE 1
+#define WAL_EXCLUSIVE_MODE 1
#define WAL_HEAPMEMORY_MODE 2
/*
@@ -58577,7 +65071,7 @@ typedef u16 ht_slot;
/*
** This structure is used to implement an iterator that loops through
** all frames in the WAL in database page order. Where two or more frames
-** correspond to the same database page, the iterator visits only the
+** correspond to the same database page, the iterator visits only the
** frame most recently written to the WAL (in other words, the frame with
** the largest index).
**
@@ -58590,7 +65084,7 @@ typedef u16 ht_slot;
** This functionality is used by the checkpoint code (see walCheckpoint()).
*/
struct WalIterator {
- int iPrior; /* Last result returned from the iterator */
+ u32 iPrior; /* Last result returned from the iterator */
int nSegment; /* Number of entries in aSegment[] */
struct WalSegment {
int iNext; /* Next slot in aIndex[] not yet returned */
@@ -58613,7 +65107,7 @@ struct WalIterator {
#define HASHTABLE_HASH_1 383 /* Should be prime */
#define HASHTABLE_NSLOT (HASHTABLE_NPAGE*2) /* Must be a power of 2 */
-/*
+/*
** The block of page numbers associated with the first hash-table in a
** wal-index is smaller than usual. This is so that there is a complete
** hash-table on each aligned 32KB page of the wal-index.
@@ -58626,6 +65120,113 @@ struct WalIterator {
)
/*
+** Structured Exception Handling (SEH) is a Windows-specific technique
+** for catching exceptions raised while accessing memory-mapped files.
+**
+** The -DSQLITE_USE_SEH compile-time option means to use SEH to catch and
+** deal with system-level errors that arise during WAL -shm file processing.
+** Without this compile-time option, any system-level faults that appear
+** while accessing the memory-mapped -shm file will cause a process-wide
+** signal to be deliver, which will more than likely cause the entire
+** process to exit.
+*/
+#ifdef SQLITE_USE_SEH
+#include <Windows.h>
+
+/* Beginning of a block of code in which an exception might occur */
+# define SEH_TRY __try { \
+ assert( walAssertLockmask(pWal) && pWal->nSehTry==0 ); \
+ VVA_ONLY(pWal->nSehTry++);
+
+/* The end of a block of code in which an exception might occur */
+# define SEH_EXCEPT(X) \
+ VVA_ONLY(pWal->nSehTry--); \
+ assert( pWal->nSehTry==0 ); \
+ } __except( sehExceptionFilter(pWal, GetExceptionCode(), GetExceptionInformation() ) ){ X }
+
+/* Simulate a memory-mapping fault in the -shm file for testing purposes */
+# define SEH_INJECT_FAULT sehInjectFault(pWal)
+
+/*
+** The second argument is the return value of GetExceptionCode() for the
+** current exception. Return EXCEPTION_EXECUTE_HANDLER if the exception code
+** indicates that the exception may have been caused by accessing the *-shm
+** file mapping. Or EXCEPTION_CONTINUE_SEARCH otherwise.
+*/
+static int sehExceptionFilter(Wal *pWal, int eCode, EXCEPTION_POINTERS *p){
+ VVA_ONLY(pWal->nSehTry--);
+ if( eCode==EXCEPTION_IN_PAGE_ERROR ){
+ if( p && p->ExceptionRecord && p->ExceptionRecord->NumberParameters>=3 ){
+ /* From MSDN: For this type of exception, the first element of the
+ ** ExceptionInformation[] array is a read-write flag - 0 if the exception
+ ** was thrown while reading, 1 if while writing. The second element is
+ ** the virtual address being accessed. The "third array element specifies
+ ** the underlying NTSTATUS code that resulted in the exception". */
+ pWal->iSysErrno = (int)p->ExceptionRecord->ExceptionInformation[2];
+ }
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+/*
+** If one is configured, invoke the xTestCallback callback with 650 as
+** the argument. If it returns true, throw the same exception that is
+** thrown by the system if the *-shm file mapping is accessed after it
+** has been invalidated.
+*/
+static void sehInjectFault(Wal *pWal){
+ int res;
+ assert( pWal->nSehTry>0 );
+
+ res = sqlite3FaultSim(650);
+ if( res!=0 ){
+ ULONG_PTR aArg[3];
+ aArg[0] = 0;
+ aArg[1] = 0;
+ aArg[2] = (ULONG_PTR)res;
+ RaiseException(EXCEPTION_IN_PAGE_ERROR, 0, 3, (const ULONG_PTR*)aArg);
+ }
+}
+
+/*
+** There are two ways to use this macro. To set a pointer to be freed
+** if an exception is thrown:
+**
+** SEH_FREE_ON_ERROR(0, pPtr);
+**
+** and to cancel the same:
+**
+** SEH_FREE_ON_ERROR(pPtr, 0);
+**
+** In the first case, there must not already be a pointer registered to
+** be freed. In the second case, pPtr must be the registered pointer.
+*/
+#define SEH_FREE_ON_ERROR(X,Y) \
+ assert( (X==0 || Y==0) && pWal->pFree==X ); pWal->pFree = Y
+
+/*
+** There are two ways to use this macro. To arrange for pWal->apWiData[iPg]
+** to be set to pValue if an exception is thrown:
+**
+** SEH_SET_ON_ERROR(iPg, pValue);
+**
+** and to cancel the same:
+**
+** SEH_SET_ON_ERROR(0, 0);
+*/
+#define SEH_SET_ON_ERROR(X,Y) pWal->iWiPg = X; pWal->pWiValue = Y
+
+#else
+# define SEH_TRY VVA_ONLY(pWal->nSehTry++);
+# define SEH_EXCEPT(X) VVA_ONLY(pWal->nSehTry--); assert( pWal->nSehTry==0 );
+# define SEH_INJECT_FAULT assert( pWal->nSehTry>0 );
+# define SEH_FREE_ON_ERROR(X,Y)
+# define SEH_SET_ON_ERROR(X,Y)
+#endif /* ifdef SQLITE_USE_SEH */
+
+
+/*
** Obtain a pointer to the iPage'th page of the wal-index. The wal-index
** is broken into pages of WALINDEX_PGSZ bytes. Wal-index pages are
** numbered from zero.
@@ -58635,9 +65236,13 @@ struct WalIterator {
** so. It is safe to enlarge the wal-index if pWal->writeLock is true
** or pWal->exclusiveMode==WAL_HEAPMEMORY_MODE.
**
-** If this call is successful, *ppPage is set to point to the wal-index
-** page and SQLITE_OK is returned. If an error (an OOM or VFS error) occurs,
-** then an SQLite error code is returned and *ppPage is set to 0.
+** Three possible result scenarios:
+**
+** (1) rc==SQLITE_OK and *ppPage==Requested-Wal-Index-Page
+** (2) rc>=SQLITE_ERROR and *ppPage==NULL
+** (3) rc==SQLITE_OK and *ppPage==NULL // only if iPage==0
+**
+** Scenario (3) can only occur when pWal->writeLock is false and iPage==0
*/
static SQLITE_NOINLINE int walIndexPageRealloc(
Wal *pWal, /* The WAL context */
@@ -58648,9 +65253,9 @@ static SQLITE_NOINLINE int walIndexPageRealloc(
/* Enlarge the pWal->apWiData[] array if required */
if( pWal->nWiData<=iPage ){
- int nByte = sizeof(u32*)*(iPage+1);
+ sqlite3_int64 nByte = sizeof(u32*)*(iPage+1);
volatile u32 **apNew;
- apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte);
+ apNew = (volatile u32 **)sqlite3Realloc((void *)pWal->apWiData, nByte);
if( !apNew ){
*ppPage = 0;
return SQLITE_NOMEM_BKPT;
@@ -58667,12 +65272,16 @@ static SQLITE_NOINLINE int walIndexPageRealloc(
pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT;
}else{
- rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
+ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
);
- assert( pWal->apWiData[iPage]!=0 || rc!=SQLITE_OK || pWal->writeLock==0 );
+ assert( pWal->apWiData[iPage]!=0
+ || rc!=SQLITE_OK
+ || (pWal->writeLock==0 && iPage==0) );
testcase( pWal->apWiData[iPage]==0 && rc==SQLITE_OK );
- if( (rc&0xff)==SQLITE_READONLY ){
+ if( rc==SQLITE_OK ){
+ if( iPage>0 && sqlite3FaultSim(600) ) rc = SQLITE_NOMEM;
+ }else if( (rc&0xff)==SQLITE_READONLY ){
pWal->readOnly |= WAL_SHM_RDONLY;
if( rc==SQLITE_READONLY ){
rc = SQLITE_OK;
@@ -58689,6 +65298,7 @@ static int walIndexPage(
int iPage, /* The page we seek */
volatile u32 **ppPage /* Write the page pointer here */
){
+ SEH_INJECT_FAULT;
if( pWal->nWiData<=iPage || (*ppPage = pWal->apWiData[iPage])==0 ){
return walIndexPageRealloc(pWal, iPage, ppPage);
}
@@ -58700,6 +65310,7 @@ static int walIndexPage(
*/
static volatile WalCkptInfo *walCkptInfo(Wal *pWal){
assert( pWal->nWiData>0 && pWal->apWiData[0] );
+ SEH_INJECT_FAULT;
return (volatile WalCkptInfo*)&(pWal->apWiData[0][sizeof(WalIndexHdr)/2]);
}
@@ -58708,6 +65319,7 @@ static volatile WalCkptInfo *walCkptInfo(Wal *pWal){
*/
static volatile WalIndexHdr *walIndexHdr(Wal *pWal){
assert( pWal->nWiData>0 && pWal->apWiData[0] );
+ SEH_INJECT_FAULT;
return (volatile WalIndexHdr*)pWal->apWiData[0];
}
@@ -58724,7 +65336,7 @@ static volatile WalIndexHdr *walIndexHdr(Wal *pWal){
)
/*
-** Generate or extend an 8 byte checksum based on the data in
+** Generate or extend an 8 byte checksum based on the data in
** array aByte[] and the initial values of aIn[0] and aIn[1] (or
** initial values of 0 and 0 if aIn==NULL).
**
@@ -58752,24 +65364,50 @@ static void walChecksumBytes(
assert( nByte>=8 );
assert( (nByte&0x00000007)==0 );
+ assert( nByte<=65536 );
+ assert( nByte%4==0 );
- if( nativeCksum ){
+ if( !nativeCksum ){
+ do {
+ s1 += BYTESWAP32(aData[0]) + s2;
+ s2 += BYTESWAP32(aData[1]) + s1;
+ aData += 2;
+ }while( aData<aEnd );
+ }else if( nByte%64==0 ){
do {
s1 += *aData++ + s2;
s2 += *aData++ + s1;
+ s1 += *aData++ + s2;
+ s2 += *aData++ + s1;
+ s1 += *aData++ + s2;
+ s2 += *aData++ + s1;
+ s1 += *aData++ + s2;
+ s2 += *aData++ + s1;
+ s1 += *aData++ + s2;
+ s2 += *aData++ + s1;
+ s1 += *aData++ + s2;
+ s2 += *aData++ + s1;
+ s1 += *aData++ + s2;
+ s2 += *aData++ + s1;
+ s1 += *aData++ + s2;
+ s2 += *aData++ + s1;
}while( aData<aEnd );
}else{
do {
- s1 += BYTESWAP32(aData[0]) + s2;
- s2 += BYTESWAP32(aData[1]) + s1;
- aData += 2;
+ s1 += *aData++ + s2;
+ s2 += *aData++ + s1;
}while( aData<aEnd );
}
+ assert( aData==aEnd );
aOut[0] = s1;
aOut[1] = s2;
}
+/*
+** If there is the possibility of concurrent access to the SHM file
+** from multiple threads and/or processes, then do a memory barrier.
+*/
static void walShmBarrier(Wal *pWal){
if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){
sqlite3OsShmBarrier(pWal->pDbFd);
@@ -58777,11 +65415,24 @@ static void walShmBarrier(Wal *pWal){
}
/*
+** Add the SQLITE_NO_TSAN as part of the return-type of a function
+** definition as a hint that the function contains constructs that
+** might give false-positive TSAN warnings.
+**
+** See tag-20200519-1.
+*/
+#if defined(__clang__) && !defined(SQLITE_NO_TSAN)
+# define SQLITE_NO_TSAN __attribute__((no_sanitize_thread))
+#else
+# define SQLITE_NO_TSAN
+#endif
+
+/*
** Write the header information in pWal->hdr into the wal-index.
**
** The checksum on pWal->hdr is updated before it is written.
*/
-static void walIndexWriteHdr(Wal *pWal){
+static SQLITE_NO_TSAN void walIndexWriteHdr(Wal *pWal){
volatile WalIndexHdr *aHdr = walIndexHdr(pWal);
const int nCksum = offsetof(WalIndexHdr, aCksum);
@@ -58789,6 +65440,7 @@ static void walIndexWriteHdr(Wal *pWal){
pWal->hdr.isInit = 1;
pWal->hdr.iVersion = WALINDEX_MAX_VERSION;
walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum);
+ /* Possible TSAN false-positive. See tag-20200519-1 */
memcpy((void*)&aHdr[1], (const void*)&pWal->hdr, sizeof(WalIndexHdr));
walShmBarrier(pWal);
memcpy((void*)&aHdr[0], (const void*)&pWal->hdr, sizeof(WalIndexHdr));
@@ -58796,11 +65448,11 @@ static void walIndexWriteHdr(Wal *pWal){
/*
** This function encodes a single frame header and writes it to a buffer
-** supplied by the caller. A frame-header is made up of a series of
+** supplied by the caller. A frame-header is made up of a series of
** 4-byte big-endian integers, as follows:
**
** 0: Page number.
-** 4: For commit records, the size of the database image in pages
+** 4: For commit records, the size of the database image in pages
** after the commit. For all other records, zero.
** 8: Salt-1 (copied from the wal-header)
** 12: Salt-2 (copied from the wal-header)
@@ -58851,13 +65503,13 @@ static int walDecodeFrame(
assert( WAL_FRAME_HDRSIZE==24 );
/* A frame is only valid if the salt values in the frame-header
- ** match the salt values in the wal-header.
+ ** match the salt values in the wal-header.
*/
if( memcmp(&pWal->hdr.aSalt, &aFrame[8], 8)!=0 ){
return 0;
}
- /* A frame is only valid if the page number is creater than zero.
+ /* A frame is only valid if the page number is greater than zero.
*/
pgno = sqlite3Get4byte(&aFrame[0]);
if( pgno==0 ){
@@ -58865,15 +65517,15 @@ static int walDecodeFrame(
}
/* A frame is only valid if a checksum of the WAL header,
- ** all prior frams, the first 16 bytes of this frame-header,
- ** and the frame-data matches the checksum in the last 8
+ ** all prior frames, the first 16 bytes of this frame-header,
+ ** and the frame-data matches the checksum in the last 8
** bytes of this frame-header.
*/
nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN);
walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum);
walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum);
- if( aCksum[0]!=sqlite3Get4byte(&aFrame[16])
- || aCksum[1]!=sqlite3Get4byte(&aFrame[20])
+ if( aCksum[0]!=sqlite3Get4byte(&aFrame[16])
+ || aCksum[1]!=sqlite3Get4byte(&aFrame[20])
){
/* Checksum failed. */
return 0;
@@ -58908,7 +65560,7 @@ static const char *walLockName(int lockIdx){
}
}
#endif /*defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
-
+
/*
** Set or release locks on the WAL. Locks are either shared or exclusive.
@@ -58924,13 +65576,19 @@ static int walLockShared(Wal *pWal, int lockIdx){
SQLITE_SHM_LOCK | SQLITE_SHM_SHARED);
WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal,
walLockName(lockIdx), rc ? "failed" : "ok"));
- VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); )
+ VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); )
+#ifdef SQLITE_USE_SEH
+ if( rc==SQLITE_OK ) pWal->lockMask |= (1 << lockIdx);
+#endif
return rc;
}
static void walUnlockShared(Wal *pWal, int lockIdx){
if( pWal->exclusiveMode ) return;
(void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED);
+#ifdef SQLITE_USE_SEH
+ pWal->lockMask &= ~(1 << lockIdx);
+#endif
WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx)));
}
static int walLockExclusive(Wal *pWal, int lockIdx, int n){
@@ -58940,13 +65598,21 @@ static int walLockExclusive(Wal *pWal, int lockIdx, int n){
SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE);
WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal,
walLockName(lockIdx), n, rc ? "failed" : "ok"));
- VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); )
+ VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); )
+#ifdef SQLITE_USE_SEH
+ if( rc==SQLITE_OK ){
+ pWal->lockMask |= (((1<<n)-1) << (SQLITE_SHM_NLOCK+lockIdx));
+ }
+#endif
return rc;
}
static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){
if( pWal->exclusiveMode ) return;
(void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n,
SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE);
+#ifdef SQLITE_USE_SEH
+ pWal->lockMask &= ~(((1<<n)-1) << (SQLITE_SHM_NLOCK+lockIdx));
+#endif
WALTRACE(("WAL%p: release EXCLUSIVE-%s cnt=%d\n", pWal,
walLockName(lockIdx), n));
}
@@ -58977,19 +65643,19 @@ struct WalHashLoc {
u32 iZero; /* One less than the frame number of first indexed*/
};
-/*
+/*
** Return pointers to the hash table and page number array stored on
** page iHash of the wal-index. The wal-index is broken into 32KB pages
** numbered starting from 0.
**
** Set output variable pLoc->aHash to point to the start of the hash table
-** in the wal-index file. Set pLoc->iZero to one less than the frame
+** in the wal-index file. Set pLoc->iZero to one less than the frame
** number of the first frame indexed by this hash table. If a
-** slot in the hash table is set to N, it refers to frame number
+** slot in the hash table is set to N, it refers to frame number
** (pLoc->iZero+N) in the log.
**
-** Finally, set pLoc->aPgno so that pLoc->aPgno[1] is the page number of the
-** first frame indexed by the hash table, frame (pLoc->iZero+1).
+** Finally, set pLoc->aPgno so that pLoc->aPgno[0] is the page number of the
+** first frame indexed by the hash table, frame (pLoc->iZero).
*/
static int walHashGet(
Wal *pWal, /* WAL handle */
@@ -59001,7 +65667,7 @@ static int walHashGet(
rc = walIndexPage(pWal, iHash, &pLoc->aPgno);
assert( rc==SQLITE_OK || iHash>0 );
- if( rc==SQLITE_OK ){
+ if( pLoc->aPgno ){
pLoc->aHash = (volatile ht_slot *)&pLoc->aPgno[HASHTABLE_NPAGE];
if( iHash==0 ){
pLoc->aPgno = &pLoc->aPgno[WALINDEX_HDR_SIZE/sizeof(u32)];
@@ -59009,7 +65675,8 @@ static int walHashGet(
}else{
pLoc->iZero = HASHTABLE_NPAGE_ONE + (iHash-1)*HASHTABLE_NPAGE;
}
- pLoc->aPgno = &pLoc->aPgno[-1];
+ }else if( NEVER(rc==SQLITE_OK) ){
+ rc = SQLITE_ERROR;
}
return rc;
}
@@ -59017,7 +65684,7 @@ static int walHashGet(
/*
** Return the number of the wal-index page that contains the hash-table
** and page-number array that contain entries corresponding to WAL frame
-** iFrame. The wal-index is broken up into 32KB pages. Wal-index pages
+** iFrame. The wal-index is broken up into 32KB pages. Wal-index pages
** are numbered starting from 0.
*/
static int walFramePage(u32 iFrame){
@@ -59028,6 +65695,7 @@ static int walFramePage(u32 iFrame){
&& (iHash>=2 || iFrame<=HASHTABLE_NPAGE_ONE+HASHTABLE_NPAGE)
&& (iHash<=2 || iFrame>(HASHTABLE_NPAGE_ONE+2*HASHTABLE_NPAGE))
);
+ assert( iHash>=0 );
return iHash;
}
@@ -59036,6 +65704,7 @@ static int walFramePage(u32 iFrame){
*/
static u32 walFramePgno(Wal *pWal, u32 iFrame){
int iHash = walFramePage(iFrame);
+ SEH_INJECT_FAULT;
if( iHash==0 ){
return pWal->apWiData[0][WALINDEX_HDR_SIZE/sizeof(u32) + iFrame - 1];
}
@@ -59067,13 +65736,14 @@ static void walCleanupHash(Wal *pWal){
if( pWal->hdr.mxFrame==0 ) return;
- /* Obtain pointers to the hash-table and page-number array containing
+ /* Obtain pointers to the hash-table and page-number array containing
** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed
- ** that the page said hash-table and array reside on is already mapped.
+ ** that the page said hash-table and array reside on is already mapped.(1)
*/
assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) );
assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] );
- walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc);
+ i = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc);
+ if( NEVER(i) ) return; /* Defense-in-depth, in case (1) above is wrong */
/* Zero all hash-table entries that correspond to frame numbers greater
** than pWal->hdr.mxFrame.
@@ -59085,12 +65755,13 @@ static void walCleanupHash(Wal *pWal){
sLoc.aHash[i] = 0;
}
}
-
+
/* Zero the entries in the aPgno array that correspond to frames with
- ** frame numbers greater than pWal->hdr.mxFrame.
+ ** frame numbers greater than pWal->hdr.mxFrame.
*/
- nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit+1]);
- memset((void *)&sLoc.aPgno[iLimit+1], 0, nByte);
+ nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit]);
+ assert( nByte>=0 );
+ memset((void *)&sLoc.aPgno[iLimit], 0, nByte);
#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
/* Verify that the every entry in the mapping region is still reachable
@@ -59099,11 +65770,11 @@ static void walCleanupHash(Wal *pWal){
if( iLimit ){
int j; /* Loop counter */
int iKey; /* Hash key */
- for(j=1; j<=iLimit; j++){
+ for(j=0; j<iLimit; j++){
for(iKey=walHash(sLoc.aPgno[j]);sLoc.aHash[iKey];iKey=walNextHash(iKey)){
- if( sLoc.aHash[iKey]==j ) break;
+ if( sLoc.aHash[iKey]==j+1 ) break;
}
- assert( sLoc.aHash[iKey]==j );
+ assert( sLoc.aHash[iKey]==j+1 );
}
}
#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */
@@ -59130,25 +65801,25 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
idx = iFrame - sLoc.iZero;
assert( idx <= HASHTABLE_NSLOT/2 + 1 );
-
+
/* If this is the first entry to be added to this hash-table, zero the
- ** entire hash table and aPgno[] array before proceeding.
+ ** entire hash table and aPgno[] array before proceeding.
*/
if( idx==1 ){
- int nByte = (int)((u8 *)&sLoc.aHash[HASHTABLE_NSLOT]
- - (u8 *)&sLoc.aPgno[1]);
- memset((void*)&sLoc.aPgno[1], 0, nByte);
+ int nByte = (int)((u8*)&sLoc.aHash[HASHTABLE_NSLOT] - (u8*)sLoc.aPgno);
+ assert( nByte>=0 );
+ memset((void*)sLoc.aPgno, 0, nByte);
}
/* If the entry in aPgno[] is already set, then the previous writer
** must have exited unexpectedly in the middle of a transaction (after
- ** writing one or more dirty pages to the WAL to free up memory).
- ** Remove the remnants of that writers uncommitted transaction from
+ ** writing one or more dirty pages to the WAL to free up memory).
+ ** Remove the remnants of that writers uncommitted transaction from
** the hash-table before writing any new entries.
*/
- if( sLoc.aPgno[idx] ){
+ if( sLoc.aPgno[idx-1] ){
walCleanupHash(pWal);
- assert( !sLoc.aPgno[idx] );
+ assert( !sLoc.aPgno[idx-1] );
}
/* Write the aPgno[] array entry and the hash-table slot. */
@@ -59156,8 +65827,8 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
for(iKey=walHash(iPage); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){
if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT;
}
- sLoc.aPgno[idx] = iPage;
- sLoc.aHash[iKey] = (ht_slot)idx;
+ sLoc.aPgno[idx-1] = iPage;
+ AtomicStore(&sLoc.aHash[iKey], (ht_slot)idx);
#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
/* Verify that the number of entries in the hash table exactly equals
@@ -59177,25 +65848,24 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
*/
if( (idx&0x3ff)==0 ){
int i; /* Loop counter */
- for(i=1; i<=idx; i++){
+ for(i=0; i<idx; i++){
for(iKey=walHash(sLoc.aPgno[i]);
sLoc.aHash[iKey];
iKey=walNextHash(iKey)){
- if( sLoc.aHash[iKey]==i ) break;
+ if( sLoc.aHash[iKey]==i+1 ) break;
}
- assert( sLoc.aHash[iKey]==i );
+ assert( sLoc.aHash[iKey]==i+1 );
}
}
#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */
}
-
return rc;
}
/*
-** Recover the wal-index by reading the write-ahead log file.
+** Recover the wal-index by reading the write-ahead log file.
**
** This routine first tries to establish an exclusive lock on the
** wal-index to prevent other threads/processes from doing anything
@@ -59222,12 +65892,6 @@ static int walIndexRecover(Wal *pWal){
assert( pWal->writeLock );
iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock;
rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock);
- if( rc==SQLITE_OK ){
- rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
- if( rc!=SQLITE_OK ){
- walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock);
- }
- }
if( rc ){
return rc;
}
@@ -59243,15 +65907,16 @@ static int walIndexRecover(Wal *pWal){
if( nSize>WAL_HDRSIZE ){
u8 aBuf[WAL_HDRSIZE]; /* Buffer to load WAL header into */
+ u32 *aPrivate = 0; /* Heap copy of *-shm hash being populated */
u8 *aFrame = 0; /* Malloc'd buffer to load entire frame */
int szFrame; /* Number of bytes in buffer aFrame[] */
u8 *aData; /* Pointer to data part of aFrame buffer */
- int iFrame; /* Index of last frame read */
- i64 iOffset; /* Next offset to read from log file */
int szPage; /* Page size according to the log */
u32 magic; /* Magic value read from WAL header */
u32 version; /* Magic value read from WAL header */
int isValid; /* True if this frame is valid */
+ u32 iPg; /* Current 32KB wal-index page */
+ u32 iLastFrame; /* Last frame in wal, based on nSize alone */
/* Read in the WAL header. */
rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
@@ -59260,16 +65925,16 @@ static int walIndexRecover(Wal *pWal){
}
/* If the database page size is not a power of two, or is greater than
- ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid
+ ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid
** data. Similarly, if the 'magic' value is invalid, ignore the whole
** WAL file.
*/
magic = sqlite3Get4byte(&aBuf[0]);
szPage = sqlite3Get4byte(&aBuf[8]);
- if( (magic&0xFFFFFFFE)!=WAL_MAGIC
- || szPage&(szPage-1)
- || szPage>SQLITE_MAX_PAGE_SIZE
- || szPage<512
+ if( (magic&0xFFFFFFFE)!=WAL_MAGIC
+ || szPage&(szPage-1)
+ || szPage>SQLITE_MAX_PAGE_SIZE
+ || szPage<512
){
goto finished;
}
@@ -59279,7 +65944,7 @@ static int walIndexRecover(Wal *pWal){
memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
/* Verify that the WAL header checksum is correct */
- walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN,
+ walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN,
aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum
);
if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[24])
@@ -59298,40 +65963,90 @@ static int walIndexRecover(Wal *pWal){
/* Malloc a buffer to read frames into. */
szFrame = szPage + WAL_FRAME_HDRSIZE;
- aFrame = (u8 *)sqlite3_malloc64(szFrame);
+ aFrame = (u8 *)sqlite3_malloc64(szFrame + WALINDEX_PGSZ);
+ SEH_FREE_ON_ERROR(0, aFrame);
if( !aFrame ){
rc = SQLITE_NOMEM_BKPT;
goto recovery_error;
}
aData = &aFrame[WAL_FRAME_HDRSIZE];
+ aPrivate = (u32*)&aData[szPage];
/* Read all frames from the log file. */
- iFrame = 0;
- for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){
- u32 pgno; /* Database page number for frame */
- u32 nTruncate; /* dbsize field from frame header */
-
- /* Read and decode the next log frame. */
- iFrame++;
- rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
- if( rc!=SQLITE_OK ) break;
- isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame);
- if( !isValid ) break;
- rc = walIndexAppend(pWal, iFrame, pgno);
- if( rc!=SQLITE_OK ) break;
-
- /* If nTruncate is non-zero, this is a commit record. */
- if( nTruncate ){
- pWal->hdr.mxFrame = iFrame;
- pWal->hdr.nPage = nTruncate;
- pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
- testcase( szPage<=32768 );
- testcase( szPage>=65536 );
- aFrameCksum[0] = pWal->hdr.aFrameCksum[0];
- aFrameCksum[1] = pWal->hdr.aFrameCksum[1];
+ iLastFrame = (nSize - WAL_HDRSIZE) / szFrame;
+ for(iPg=0; iPg<=(u32)walFramePage(iLastFrame); iPg++){
+ u32 *aShare;
+ u32 iFrame; /* Index of last frame read */
+ u32 iLast = MIN(iLastFrame, HASHTABLE_NPAGE_ONE+iPg*HASHTABLE_NPAGE);
+ u32 iFirst = 1 + (iPg==0?0:HASHTABLE_NPAGE_ONE+(iPg-1)*HASHTABLE_NPAGE);
+ u32 nHdr, nHdr32;
+ rc = walIndexPage(pWal, iPg, (volatile u32**)&aShare);
+ assert( aShare!=0 || rc!=SQLITE_OK );
+ if( aShare==0 ) break;
+ SEH_SET_ON_ERROR(iPg, aShare);
+ pWal->apWiData[iPg] = aPrivate;
+
+ for(iFrame=iFirst; iFrame<=iLast; iFrame++){
+ i64 iOffset = walFrameOffset(iFrame, szPage);
+ u32 pgno; /* Database page number for frame */
+ u32 nTruncate; /* dbsize field from frame header */
+
+ /* Read and decode the next log frame. */
+ rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
+ if( rc!=SQLITE_OK ) break;
+ isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame);
+ if( !isValid ) break;
+ rc = walIndexAppend(pWal, iFrame, pgno);
+ if( NEVER(rc!=SQLITE_OK) ) break;
+
+ /* If nTruncate is non-zero, this is a commit record. */
+ if( nTruncate ){
+ pWal->hdr.mxFrame = iFrame;
+ pWal->hdr.nPage = nTruncate;
+ pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
+ testcase( szPage<=32768 );
+ testcase( szPage>=65536 );
+ aFrameCksum[0] = pWal->hdr.aFrameCksum[0];
+ aFrameCksum[1] = pWal->hdr.aFrameCksum[1];
+ }
+ }
+ pWal->apWiData[iPg] = aShare;
+ SEH_SET_ON_ERROR(0,0);
+ nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0);
+ nHdr32 = nHdr / sizeof(u32);
+#ifndef SQLITE_SAFER_WALINDEX_RECOVERY
+ /* Memcpy() should work fine here, on all reasonable implementations.
+ ** Technically, memcpy() might change the destination to some
+ ** intermediate value before setting to the final value, and that might
+ ** cause a concurrent reader to malfunction. Memcpy() is allowed to
+ ** do that, according to the spec, but no memcpy() implementation that
+ ** we know of actually does that, which is why we say that memcpy()
+ ** is safe for this. Memcpy() is certainly a lot faster.
+ */
+ memcpy(&aShare[nHdr32], &aPrivate[nHdr32], WALINDEX_PGSZ-nHdr);
+#else
+ /* In the event that some platform is found for which memcpy()
+ ** changes the destination to some intermediate value before
+ ** setting the final value, this alternative copy routine is
+ ** provided.
+ */
+ {
+ int i;
+ for(i=nHdr32; i<WALINDEX_PGSZ/sizeof(u32); i++){
+ if( aShare[i]!=aPrivate[i] ){
+ /* Atomic memory operations are not required here because if
+ ** the value needs to be changed, that means it is not being
+ ** accessed concurrently. */
+ aShare[i] = aPrivate[i];
+ }
+ }
}
+#endif
+ SEH_INJECT_FAULT;
+ if( iFrame<=iLast ) break;
}
+ SEH_FREE_ON_ERROR(aFrame, 0);
sqlite3_free(aFrame);
}
@@ -59343,16 +66058,28 @@ finished:
pWal->hdr.aFrameCksum[1] = aFrameCksum[1];
walIndexWriteHdr(pWal);
- /* Reset the checkpoint-header. This is safe because this thread is
- ** currently holding locks that exclude all other readers, writers and
- ** checkpointers.
+ /* Reset the checkpoint-header. This is safe because this thread is
+ ** currently holding locks that exclude all other writers and
+ ** checkpointers. Then set the values of read-mark slots 1 through N.
*/
pInfo = walCkptInfo(pWal);
pInfo->nBackfill = 0;
pInfo->nBackfillAttempted = pWal->hdr.mxFrame;
pInfo->aReadMark[0] = 0;
- for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
- if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame;
+ for(i=1; i<WAL_NREADER; i++){
+ rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
+ if( rc==SQLITE_OK ){
+ if( i==1 && pWal->hdr.mxFrame ){
+ pInfo->aReadMark[i] = pWal->hdr.mxFrame;
+ }else{
+ pInfo->aReadMark[i] = READMARK_NOT_USED;
+ }
+ SEH_INJECT_FAULT;
+ walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
+ }else if( rc!=SQLITE_BUSY ){
+ goto recovery_error;
+ }
+ }
/* If more than one frame was recovered from the log file, report an
** event via sqlite3_log(). This is to help with identifying performance
@@ -59370,7 +66097,6 @@ finished:
recovery_error:
WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok"));
walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock);
- walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
return rc;
}
@@ -59390,8 +66116,8 @@ static void walIndexClose(Wal *pWal, int isDelete){
}
}
-/*
-** Open a connection to the WAL file zWalName. The database file must
+/*
+** Open a connection to the WAL file zWalName. The database file must
** already be opened on connection pDbFd. The buffer that zWalName points
** to must remain valid for the lifetime of the returned Wal* handle.
**
@@ -59401,7 +66127,7 @@ static void walIndexClose(Wal *pWal, int isDelete){
** were to do this just after this client opened one of these files, the
** system would be badly broken.
**
-** If the log file is successfully opened, SQLITE_OK is returned and
+** If the log file is successfully opened, SQLITE_OK is returned and
** *ppWal is set to point to a new WAL handle. If an error occurs,
** an SQLite error code is returned and *ppWal is left unmodified.
*/
@@ -59420,14 +66146,43 @@ SQLITE_PRIVATE int sqlite3WalOpen(
assert( zWalName && zWalName[0] );
assert( pDbFd );
+ /* Verify the values of various constants. Any changes to the values
+ ** of these constants would result in an incompatible on-disk format
+ ** for the -shm file. Any change that causes one of these asserts to
+ ** fail is a backward compatibility problem, even if the change otherwise
+ ** works.
+ **
+ ** This table also serves as a helpful cross-reference when trying to
+ ** interpret hex dumps of the -shm file.
+ */
+ assert( 48 == sizeof(WalIndexHdr) );
+ assert( 40 == sizeof(WalCkptInfo) );
+ assert( 120 == WALINDEX_LOCK_OFFSET );
+ assert( 136 == WALINDEX_HDR_SIZE );
+ assert( 4096 == HASHTABLE_NPAGE );
+ assert( 4062 == HASHTABLE_NPAGE_ONE );
+ assert( 8192 == HASHTABLE_NSLOT );
+ assert( 383 == HASHTABLE_HASH_1 );
+ assert( 32768 == WALINDEX_PGSZ );
+ assert( 8 == SQLITE_SHM_NLOCK );
+ assert( 5 == WAL_NREADER );
+ assert( 24 == WAL_FRAME_HDRSIZE );
+ assert( 32 == WAL_HDRSIZE );
+ assert( 120 == WALINDEX_LOCK_OFFSET + WAL_WRITE_LOCK );
+ assert( 121 == WALINDEX_LOCK_OFFSET + WAL_CKPT_LOCK );
+ assert( 122 == WALINDEX_LOCK_OFFSET + WAL_RECOVER_LOCK );
+ assert( 123 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(0) );
+ assert( 124 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(1) );
+ assert( 125 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(2) );
+ assert( 126 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(3) );
+ assert( 127 == WALINDEX_LOCK_OFFSET + WAL_READ_LOCK(4) );
+
/* In the amalgamation, the os_unix.c and os_win.c source files come before
** this source file. Verify that the #defines of the locking byte offsets
** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value.
** For that matter, if the lock offset ever changes from its initial design
** value of 120, we need to know that so there is an assert() to check it.
*/
- assert( 120==WALINDEX_LOCK_OFFSET );
- assert( 136==WALINDEX_HDR_SIZE );
#ifdef WIN_SHM_BASE
assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET );
#endif
@@ -59477,7 +66232,7 @@ SQLITE_PRIVATE int sqlite3WalOpen(
}
/*
-** Change the size to which the WAL file is trucated on each reset.
+** Change the size to which the WAL file is truncated on each reset.
*/
SQLITE_PRIVATE void sqlite3WalLimit(Wal *pWal, i64 iLimit){
if( pWal ) pWal->mxWalSize = iLimit;
@@ -59565,7 +66320,7 @@ static void walMerge(
ht_slot logpage;
Pgno dbpage;
- if( (iLeft<nLeft)
+ if( (iLeft<nLeft)
&& (iRight>=nRight || aContent[aLeft[iLeft]]<aContent[aRight[iRight]])
){
logpage = aLeft[iLeft++];
@@ -59663,7 +66418,7 @@ static void walMergesort(
#endif
}
-/*
+/*
** Free an iterator allocated by walIteratorInit().
*/
static void walIteratorFree(WalIterator *p){
@@ -59671,7 +66426,7 @@ static void walIteratorFree(WalIterator *p){
}
/*
-** Construct a WalInterator object that can be used to loop over all
+** Construct a WalInterator object that can be used to loop over all
** pages in the WAL following frame nBackfill in ascending order. Frames
** nBackfill or earlier may be included - excluding them is an optimization
** only. The caller must hold the checkpoint lock.
@@ -59687,7 +66442,7 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
WalIterator *p; /* Return value */
int nSegment; /* Number of segments to merge */
u32 iLast; /* Last frame in log */
- int nByte; /* Number of bytes to allocate */
+ sqlite3_int64 nByte; /* Number of bytes to allocate */
int i; /* Iterator variable */
ht_slot *aTmp; /* Temp space used by merge-sort */
int rc = SQLITE_OK; /* Return Code */
@@ -59700,26 +66455,19 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
/* Allocate space for the WalIterator object. */
nSegment = walFramePage(iLast) + 1;
- nByte = sizeof(WalIterator)
+ nByte = sizeof(WalIterator)
+ (nSegment-1)*sizeof(struct WalSegment)
+ iLast*sizeof(ht_slot);
- p = (WalIterator *)sqlite3_malloc64(nByte);
+ p = (WalIterator *)sqlite3_malloc64(nByte
+ + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
+ );
if( !p ){
return SQLITE_NOMEM_BKPT;
}
memset(p, 0, nByte);
p->nSegment = nSegment;
-
- /* Allocate temporary space used by the merge-sort routine. This block
- ** of memory will be freed before this function returns.
- */
- aTmp = (ht_slot *)sqlite3_malloc64(
- sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
- );
- if( !aTmp ){
- rc = SQLITE_NOMEM_BKPT;
- }
-
+ aTmp = (ht_slot*)&(((u8*)p)[nByte]);
+ SEH_FREE_ON_ERROR(0, p);
for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && i<nSegment; i++){
WalHashLoc sLoc;
@@ -59729,7 +66477,6 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
int nEntry; /* Number of entries in this segment */
ht_slot *aIndex; /* Sorted index for this segment */
- sLoc.aPgno++;
if( (i+1)==nSegment ){
nEntry = (int)(iLast - sLoc.iZero);
}else{
@@ -59737,7 +66484,7 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
}
aIndex = &((ht_slot *)&p->aSegment[p->nSegment])[sLoc.iZero];
sLoc.iZero++;
-
+
for(j=0; j<nEntry; j++){
aIndex[j] = (ht_slot)j;
}
@@ -59748,9 +66495,8 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
p->aSegment[i].aPgno = (u32 *)sLoc.aPgno;
}
}
- sqlite3_free(aTmp);
-
if( rc!=SQLITE_OK ){
+ SEH_FREE_ON_ERROR(p, 0);
walIteratorFree(p);
p = 0;
}
@@ -59758,6 +66504,88 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
return rc;
}
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+
+
+/*
+** Attempt to enable blocking locks that block for nMs ms. Return 1 if
+** blocking locks are successfully enabled, or 0 otherwise.
+*/
+static int walEnableBlockingMs(Wal *pWal, int nMs){
+ int rc = sqlite3OsFileControl(
+ pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&nMs
+ );
+ return (rc==SQLITE_OK);
+}
+
+/*
+** Attempt to enable blocking locks. Blocking locks are enabled only if (a)
+** they are supported by the VFS, and (b) the database handle is configured
+** with a busy-timeout. Return 1 if blocking locks are successfully enabled,
+** or 0 otherwise.
+*/
+static int walEnableBlocking(Wal *pWal){
+ int res = 0;
+ if( pWal->db ){
+ int tmout = pWal->db->busyTimeout;
+ if( tmout ){
+ res = walEnableBlockingMs(pWal, tmout);
+ }
+ }
+ return res;
+}
+
+/*
+** Disable blocking locks.
+*/
+static void walDisableBlocking(Wal *pWal){
+ int tmout = 0;
+ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout);
+}
+
+/*
+** If parameter bLock is true, attempt to enable blocking locks, take
+** the WRITER lock, and then disable blocking locks. If blocking locks
+** cannot be enabled, no attempt to obtain the WRITER lock is made. Return
+** an SQLite error code if an error occurs, or SQLITE_OK otherwise. It is not
+** an error if blocking locks can not be enabled.
+**
+** If the bLock parameter is false and the WRITER lock is held, release it.
+*/
+SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock){
+ int rc = SQLITE_OK;
+ assert( pWal->readLock<0 || bLock==0 );
+ if( bLock ){
+ assert( pWal->db );
+ if( walEnableBlocking(pWal) ){
+ rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
+ if( rc==SQLITE_OK ){
+ pWal->writeLock = 1;
+ }
+ walDisableBlocking(pWal);
+ }
+ }else if( pWal->writeLock ){
+ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
+ pWal->writeLock = 0;
+ }
+ return rc;
+}
+
+/*
+** Set the database handle used to determine if blocking locks are required.
+*/
+SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){
+ pWal->db = db;
+}
+
+#else
+# define walEnableBlocking(x) 0
+# define walDisableBlocking(x)
+# define walEnableBlockingMs(pWal, ms) 0
+# define sqlite3WalDb(pWal, db)
+#endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */
+
+
/*
** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and
** n. If the attempt fails and parameter xBusy is not NULL, then it is a
@@ -59775,6 +66603,12 @@ static int walBusyLock(
do {
rc = walLockExclusive(pWal, lockIdx, n);
}while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) );
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ if( rc==SQLITE_BUSY_TIMEOUT ){
+ walDisableBlocking(pWal);
+ rc = SQLITE_BUSY;
+ }
+#endif
return rc;
}
@@ -59799,8 +66633,8 @@ static int walPagesize(Wal *pWal){
** client to write to the database (which may be this one) does so by
** writing frames into the start of the log file.
**
-** The value of parameter salt1 is used as the aSalt[1] value in the
-** new wal-index header. It should be passed a pseudo-random value (i.e.
+** The value of parameter salt1 is used as the aSalt[1] value in the
+** new wal-index header. It should be passed a pseudo-random value (i.e.
** one obtained from sqlite3_randomness()).
*/
static void walRestartHdr(Wal *pWal, u32 salt1){
@@ -59812,7 +66646,7 @@ static void walRestartHdr(Wal *pWal, u32 salt1){
sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
memcpy(&pWal->hdr.aSalt[1], &salt1, 4);
walIndexWriteHdr(pWal);
- pInfo->nBackfill = 0;
+ AtomicStore(&pInfo->nBackfill, 0);
pInfo->nBackfillAttempted = 0;
pInfo->aReadMark[1] = 0;
for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
@@ -59828,8 +66662,8 @@ static void walRestartHdr(Wal *pWal, u32 salt1){
** that a concurrent reader might be using.
**
** All I/O barrier operations (a.k.a fsyncs) occur in this routine when
-** SQLite is in WAL-mode in synchronous=NORMAL. That means that if
-** checkpoints are always run by a background thread or background
+** SQLite is in WAL-mode in synchronous=NORMAL. That means that if
+** checkpoints are always run by a background thread or background
** process, foreground threads will never block on a lengthy fsync call.
**
** Fsync is called on the WAL before writing content out of the WAL and
@@ -59842,7 +66676,7 @@ static void walRestartHdr(Wal *pWal, u32 salt1){
** database file.
**
** This routine uses and updates the nBackfill field of the wal-index header.
-** This is the only routine that will increase the value of nBackfill.
+** This is the only routine that will increase the value of nBackfill.
** (A WAL reset or recovery will revert nBackfill to zero, but not increase
** its value.)
**
@@ -59887,20 +66721,13 @@ static int walCheckpoint(
mxSafeFrame = pWal->hdr.mxFrame;
mxPage = pWal->hdr.nPage;
for(i=1; i<WAL_NREADER; i++){
- /* Thread-sanitizer reports that the following is an unsafe read,
- ** as some other thread may be in the process of updating the value
- ** of the aReadMark[] slot. The assumption here is that if that is
- ** happening, the other client may only be increasing the value,
- ** not decreasing it. So assuming either that either the "old" or
- ** "new" version of the value is read, and not some arbitrary value
- ** that would never be written by a real client, things are still
- ** safe. */
- u32 y = pInfo->aReadMark[i];
+ u32 y = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
if( mxSafeFrame>y ){
assert( y<=pWal->hdr.mxFrame );
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){
- pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
+ u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
+ AtomicStore(pInfo->aReadMark+i, iMark); SEH_INJECT_FAULT;
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
}else if( rc==SQLITE_BUSY ){
mxSafeFrame = y;
@@ -59918,11 +66745,10 @@ static int walCheckpoint(
}
if( pIter
- && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK
+ && (rc = walBusyLock(pWal,xBusy,pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK
){
u32 nBackfill = pInfo->nBackfill;
-
- pInfo->nBackfillAttempted = mxSafeFrame;
+ pInfo->nBackfillAttempted = mxSafeFrame; SEH_INJECT_FAULT;
/* Sync the WAL to disk */
rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags));
@@ -59933,18 +66759,28 @@ static int walCheckpoint(
if( rc==SQLITE_OK ){
i64 nReq = ((i64)mxPage * szPage);
i64 nSize; /* Current size of database file */
+ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0);
rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
if( rc==SQLITE_OK && nSize<nReq ){
- sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
+ if( (nSize+65536+(i64)pWal->hdr.mxFrame*szPage)<nReq ){
+ /* If the size of the final database is larger than the current
+ ** database plus the amount of data in the wal file, plus the
+ ** maximum size of the pending-byte page (65536 bytes), then
+ ** must be corruption somewhere. */
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq);
+ }
}
- }
+ }
/* Iterate through the contents of the WAL, copying data to the db file */
while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
i64 iOffset;
assert( walFramePgno(pWal, iFrame)==iDbpage );
- if( db->u1.isInterrupted ){
+ SEH_INJECT_FAULT;
+ if( AtomicLoad(&db->u1.isInterrupted) ){
rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
break;
}
@@ -59960,6 +66796,7 @@ static int walCheckpoint(
rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
if( rc!=SQLITE_OK ) break;
}
+ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0);
/* If work was actually accomplished... */
if( rc==SQLITE_OK ){
@@ -59972,7 +66809,7 @@ static int walCheckpoint(
}
}
if( rc==SQLITE_OK ){
- pInfo->nBackfill = mxSafeFrame;
+ AtomicStore(&pInfo->nBackfill, mxSafeFrame); SEH_INJECT_FAULT;
}
}
@@ -59988,12 +66825,13 @@ static int walCheckpoint(
}
/* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the
- ** entire wal file has been copied into the database file, then block
- ** until all readers have finished using the wal file. This ensures that
+ ** entire wal file has been copied into the database file, then block
+ ** until all readers have finished using the wal file. This ensures that
** the next process to write to the database restarts the wal file.
*/
if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){
assert( pWal->writeLock );
+ SEH_INJECT_FAULT;
if( pInfo->nBackfill<pWal->hdr.mxFrame ){
rc = SQLITE_BUSY;
}else if( eMode>=SQLITE_CHECKPOINT_RESTART ){
@@ -60013,7 +66851,7 @@ static int walCheckpoint(
** writer clients should see that the entire log file has been
** checkpointed and behave accordingly. This seems unsafe though,
** as it would leave the system in a state where the contents of
- ** the wal-index header do not match the contents of the
+ ** the wal-index header do not match the contents of the
** file-system. To avoid this, update the wal-index header to
** indicate that the log file contains zero valid frames. */
walRestartHdr(pWal, salt1);
@@ -60025,6 +66863,7 @@ static int walCheckpoint(
}
walcheckpoint_out:
+ SEH_FREE_ON_ERROR(pIter, 0);
walIteratorFree(pIter);
return rc;
}
@@ -60047,6 +66886,93 @@ static void walLimitSize(Wal *pWal, i64 nMax){
}
}
+#ifdef SQLITE_USE_SEH
+/*
+** This is the "standard" exception handler used in a few places to handle
+** an exception thrown by reading from the *-shm mapping after it has become
+** invalid in SQLITE_USE_SEH builds. It is used as follows:
+**
+** SEH_TRY { ... }
+** SEH_EXCEPT( rc = walHandleException(pWal); )
+**
+** This function does three things:
+**
+** 1) Determines the locks that should be held, based on the contents of
+** the Wal.readLock, Wal.writeLock and Wal.ckptLock variables. All other
+** held locks are assumed to be transient locks that would have been
+** released had the exception not been thrown and are dropped.
+**
+** 2) Frees the pointer at Wal.pFree, if any, using sqlite3_free().
+**
+** 3) Set pWal->apWiData[pWal->iWiPg] to pWal->pWiValue if not NULL
+**
+** 4) Returns SQLITE_IOERR.
+*/
+static int walHandleException(Wal *pWal){
+ if( pWal->exclusiveMode==0 ){
+ static const int S = 1;
+ static const int E = (1<<SQLITE_SHM_NLOCK);
+ int ii;
+ u32 mUnlock = pWal->lockMask & ~(
+ (pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock)))
+ | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0)
+ | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0)
+ );
+ for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
+ if( (S<<ii) & mUnlock ) walUnlockShared(pWal, ii);
+ if( (E<<ii) & mUnlock ) walUnlockExclusive(pWal, ii, 1);
+ }
+ }
+ sqlite3_free(pWal->pFree);
+ pWal->pFree = 0;
+ if( pWal->pWiValue ){
+ pWal->apWiData[pWal->iWiPg] = pWal->pWiValue;
+ pWal->pWiValue = 0;
+ }
+ return SQLITE_IOERR_IN_PAGE;
+}
+
+/*
+** Assert that the Wal.lockMask mask, which indicates the locks held
+** by the connenction, is consistent with the Wal.readLock, Wal.writeLock
+** and Wal.ckptLock variables. To be used as:
+**
+** assert( walAssertLockmask(pWal) );
+*/
+static int walAssertLockmask(Wal *pWal){
+ if( pWal->exclusiveMode==0 ){
+ static const int S = 1;
+ static const int E = (1<<SQLITE_SHM_NLOCK);
+ u32 mExpect = (
+ (pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock)))
+ | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0)
+ | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0)
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ | (pWal->pSnapshot ? (pWal->lockMask & (1 << WAL_CKPT_LOCK)) : 0)
+#endif
+ );
+ assert( mExpect==pWal->lockMask );
+ }
+ return 1;
+}
+
+/*
+** Return and zero the "system error" field set when an
+** EXCEPTION_IN_PAGE_ERROR exception is caught.
+*/
+SQLITE_PRIVATE int sqlite3WalSystemErrno(Wal *pWal){
+ int iRet = 0;
+ if( pWal ){
+ iRet = pWal->iSysErrno;
+ pWal->iSysErrno = 0;
+ }
+ return iRet;
+}
+
+#else
+# define walAssertLockmask(x) 1
+#endif /* ifdef SQLITE_USE_SEH */
+
/*
** Close a connection to a log file.
*/
@@ -60061,6 +66987,8 @@ SQLITE_PRIVATE int sqlite3WalClose(
if( pWal ){
int isDelete = 0; /* True to unlink wal and wal-index files */
+ assert( walAssertLockmask(pWal) );
+
/* If an EXCLUSIVE lock can be obtained on the database file (using the
** ordinary, rollback-mode locking methods, this guarantees that the
** connection associated with this log file is the only connection to
@@ -60075,7 +67003,7 @@ SQLITE_PRIVATE int sqlite3WalClose(
if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
}
- rc = sqlite3WalCheckpoint(pWal, db,
+ rc = sqlite3WalCheckpoint(pWal, db,
SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
);
if( rc==SQLITE_OK ){
@@ -60085,7 +67013,7 @@ SQLITE_PRIVATE int sqlite3WalClose(
);
if( bPersist!=1 ){
/* Try to delete the WAL file if the checkpoint completed and
- ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal
+ ** fsynced (rc==SQLITE_OK) and if we are not in persistent-wal
** mode (!bPersist) */
isDelete = 1;
}else if( pWal->mxWalSize>=0 ){
@@ -60131,7 +67059,7 @@ SQLITE_PRIVATE int sqlite3WalClose(
** If the checksum cannot be verified return non-zero. If the header
** is read successfully and the checksum verified, return zero.
*/
-static int walIndexTryHdr(Wal *pWal, int *pChanged){
+static SQLITE_NO_TSAN int walIndexTryHdr(Wal *pWal, int *pChanged){
u32 aCksum[2]; /* Checksum on the header content */
WalIndexHdr h1, h2; /* Two copies of the header content */
WalIndexHdr volatile *aHdr; /* Header in shared memory */
@@ -60144,19 +67072,25 @@ static int walIndexTryHdr(Wal *pWal, int *pChanged){
** meaning it is possible that an inconsistent snapshot is read
** from the file. If this happens, return non-zero.
**
+ ** tag-20200519-1:
** There are two copies of the header at the beginning of the wal-index.
** When reading, read [0] first then [1]. Writes are in the reverse order.
** Memory barriers are used to prevent the compiler or the hardware from
- ** reordering the reads and writes.
+ ** reordering the reads and writes. TSAN and similar tools can sometimes
+ ** give false-positive warnings about these accesses because the tools do not
+ ** account for the double-read and the memory barrier. The use of mutexes
+ ** here would be problematic as the memory being accessed is potentially
+ ** shared among multiple processes and not all mutex implementations work
+ ** reliably in that environment.
*/
aHdr = walIndexHdr(pWal);
- memcpy(&h1, (void *)&aHdr[0], sizeof(h1));
+ memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); /* Possible TSAN false-positive */
walShmBarrier(pWal);
memcpy(&h2, (void *)&aHdr[1], sizeof(h2));
if( memcmp(&h1, &h2, sizeof(h1))!=0 ){
return 1; /* Dirty read */
- }
+ }
if( h1.isInit==0 ){
return 1; /* Malformed header - probably all zeros */
}
@@ -60192,7 +67126,7 @@ static int walIndexTryHdr(Wal *pWal, int *pChanged){
** changed by this operation. If pWal->hdr is unchanged, set *pChanged
** to 0.
**
-** If the wal-index header is successfully read, return SQLITE_OK.
+** If the wal-index header is successfully read, return SQLITE_OK.
** Otherwise an SQLite error code.
*/
static int walIndexReadHdr(Wal *pWal, int *pChanged){
@@ -60200,7 +67134,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
int badHdr; /* True if a header read failed */
volatile u32 *page0; /* Chunk of wal-index containing header */
- /* Ensure that page 0 of the wal-index (the page that contains the
+ /* Ensure that page 0 of the wal-index (the page that contains the
** wal-index header) is mapped. Return early if an error occurs here.
*/
assert( pChanged );
@@ -60232,7 +67166,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
/* If the first page of the wal-index has been mapped, try to read the
** wal-index header immediately, without holding any lock. This usually
- ** works, but may fail if the wal-index header is corrupt or currently
+ ** works, but may fail if the wal-index header is corrupt or currently
** being modified by another thread or process.
*/
badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1);
@@ -60240,28 +67174,35 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
/* If the first attempt failed, it might have been due to a race
** with a writer. So get a WRITE lock and try again.
*/
- assert( badHdr==0 || pWal->writeLock==0 );
if( badHdr ){
if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){
if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
walUnlockShared(pWal, WAL_WRITE_LOCK);
rc = SQLITE_READONLY_RECOVERY;
}
- }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){
- pWal->writeLock = 1;
- if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
- badHdr = walIndexTryHdr(pWal, pChanged);
- if( badHdr ){
- /* If the wal-index header is still malformed even while holding
- ** a WRITE lock, it can only mean that the header is corrupted and
- ** needs to be reconstructed. So run recovery to do exactly that.
- */
- rc = walIndexRecover(pWal);
- *pChanged = 1;
+ }else{
+ int bWriteLock = pWal->writeLock;
+ if( bWriteLock
+ || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1))
+ ){
+ pWal->writeLock = 1;
+ if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
+ badHdr = walIndexTryHdr(pWal, pChanged);
+ if( badHdr ){
+ /* If the wal-index header is still malformed even while holding
+ ** a WRITE lock, it can only mean that the header is corrupted and
+ ** needs to be reconstructed. So run recovery to do exactly that.
+ ** Disable blocking locks first. */
+ walDisableBlocking(pWal);
+ rc = walIndexRecover(pWal);
+ *pChanged = 1;
+ }
+ }
+ if( bWriteLock==0 ){
+ pWal->writeLock = 0;
+ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
}
}
- pWal->writeLock = 0;
- walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
}
}
@@ -60303,15 +67244,15 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
**
** The *-wal file has been read and an appropriate wal-index has been
** constructed in pWal->apWiData[] using heap memory instead of shared
-** memory.
+** memory.
**
** If this function returns SQLITE_OK, then the read transaction has
-** been successfully opened. In this case output variable (*pChanged)
+** been successfully opened. In this case output variable (*pChanged)
** is set to true before returning if the caller should discard the
-** contents of the page cache before proceeding. Or, if it returns
-** WAL_RETRY, then the heap memory wal-index has been discarded and
-** the caller should retry opening the read transaction from the
-** beginning (including attempting to map the *-shm file).
+** contents of the page cache before proceeding. Or, if it returns
+** WAL_RETRY, then the heap memory wal-index has been discarded and
+** the caller should retry opening the read transaction from the
+** beginning (including attempting to map the *-shm file).
**
** If an error occurs, an SQLite error code is returned.
*/
@@ -60408,7 +67349,9 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
}
/* Allocate a buffer to read frames into */
- szFrame = pWal->hdr.szPage + WAL_FRAME_HDRSIZE;
+ assert( (pWal->szPage & (pWal->szPage-1))==0 );
+ assert( pWal->szPage>=512 && pWal->szPage<=65536 );
+ szFrame = pWal->szPage + WAL_FRAME_HDRSIZE;
aFrame = (u8 *)sqlite3_malloc64(szFrame);
if( aFrame==0 ){
rc = SQLITE_NOMEM_BKPT;
@@ -60422,8 +67365,8 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
** the caller. */
aSaveCksum[0] = pWal->hdr.aFrameCksum[0];
aSaveCksum[1] = pWal->hdr.aFrameCksum[1];
- for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->hdr.szPage);
- iOffset+szFrame<=szWal;
+ for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->szPage);
+ iOffset+szFrame<=szWal;
iOffset+=szFrame
){
u32 pgno; /* Database page number for frame */
@@ -60461,6 +67404,37 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
}
/*
+** The final argument passed to walTryBeginRead() is of type (int*). The
+** caller should invoke walTryBeginRead as follows:
+**
+** int cnt = 0;
+** do {
+** rc = walTryBeginRead(..., &cnt);
+** }while( rc==WAL_RETRY );
+**
+** The final value of "cnt" is of no use to the caller. It is used by
+** the implementation of walTryBeginRead() as follows:
+**
+** + Each time walTryBeginRead() is called, it is incremented. Once
+** it reaches WAL_RETRY_PROTOCOL_LIMIT - indicating that walTryBeginRead()
+** has many times been invoked and failed with WAL_RETRY - walTryBeginRead()
+** returns SQLITE_PROTOCOL.
+**
+** + If SQLITE_ENABLE_SETLK_TIMEOUT is defined and walTryBeginRead() failed
+** because a blocking lock timed out (SQLITE_BUSY_TIMEOUT from the OS
+** layer), the WAL_RETRY_BLOCKED_MASK bit is set in "cnt". In this case
+** the next invocation of walTryBeginRead() may omit an expected call to
+** sqlite3OsSleep(). There has already been a delay when the previous call
+** waited on a lock.
+*/
+#define WAL_RETRY_PROTOCOL_LIMIT 100
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+# define WAL_RETRY_BLOCKED_MASK 0x10000000
+#else
+# define WAL_RETRY_BLOCKED_MASK 0
+#endif
+
+/*
** Attempt to start a read transaction. This might fail due to a race or
** other transient condition. When that happens, it returns WAL_RETRY to
** indicate to the caller that it is safe to retry immediately.
@@ -60471,10 +67445,10 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
**
** The useWal parameter is true to force the use of the WAL and disable
** the case where the WAL is bypassed because it has been completely
-** checkpointed. If useWal==0 then this routine calls walIndexReadHdr()
-** to make a copy of the wal-index header into pWal->hdr. If the
-** wal-index header has changed, *pChanged is set to 1 (as an indication
-** to the caller that the local page cache is obsolete and needs to be
+** checkpointed. If useWal==0 then this routine calls walIndexReadHdr()
+** to make a copy of the wal-index header into pWal->hdr. If the
+** wal-index header has changed, *pChanged is set to 1 (as an indication
+** to the caller that the local page cache is obsolete and needs to be
** flushed.) When useWal==1, the wal-index header is assumed to already
** be loaded and the pChanged parameter is unused.
**
@@ -60489,7 +67463,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
** bad luck when there is lots of contention for the wal-index, but that
** possibility is so small that it can be safely neglected, we believe.
**
-** On success, this routine obtains a read lock on
+** On success, this routine obtains a read lock on
** WAL_READ_LOCK(pWal->readLock). The pWal->readLock integer is
** in the range 0 <= pWal->readLock < WAL_NREADER. If pWal->readLock==(-1)
** that means the Wal does not hold any read lock. The reader must not
@@ -60510,13 +67484,16 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
** so it takes care to hold an exclusive lock on the corresponding
** WAL_READ_LOCK() while changing values.
*/
-static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
+static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
u32 mxReadMark; /* Largest aReadMark[] value */
int mxI; /* Index of largest aReadMark[] value */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
u32 mxFrame; /* Wal frame to lock to */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int nBlockTmout = 0;
+#endif
assert( pWal->readLock<0 ); /* Not currently locked */
@@ -60527,27 +67504,47 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
**
** Circumstances that cause a RETRY should only last for the briefest
** instances of time. No I/O or other system calls are done while the
- ** locks are held, so the locks should not be held for very long. But
+ ** locks are held, so the locks should not be held for very long. But
** if we are unlucky, another process that is holding a lock might get
- ** paged out or take a page-fault that is time-consuming to resolve,
+ ** paged out or take a page-fault that is time-consuming to resolve,
** during the few nanoseconds that it is holding the lock. In that case,
** it might take longer than normal for the lock to free.
**
** After 5 RETRYs, we begin calling sqlite3OsSleep(). The first few
** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this
** is more of a scheduler yield than an actual delay. But on the 10th
- ** an subsequent retries, the delays start becoming longer and longer,
+ ** an subsequent retries, the delays start becoming longer and longer,
** so that on the 100th (and last) RETRY we delay for 323 milliseconds.
** The total delay time before giving up is less than 10 seconds.
*/
- if( cnt>5 ){
+ (*pCnt)++;
+ if( *pCnt>5 ){
int nDelay = 1; /* Pause time in microseconds */
- if( cnt>100 ){
+ int cnt = (*pCnt & ~WAL_RETRY_BLOCKED_MASK);
+ if( cnt>WAL_RETRY_PROTOCOL_LIMIT ){
VVA_ONLY( pWal->lockError = 1; )
return SQLITE_PROTOCOL;
}
- if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
+ if( *pCnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor
+ ** to block for locks for approximately nDelay us. This affects three
+ ** locks: (a) the shared lock taken on the DMS slot in os_unix.c (if
+ ** using os_unix.c), (b) the WRITER lock taken in walIndexReadHdr() if the
+ ** first attempted read fails, and (c) the shared lock taken on the
+ ** read-mark.
+ **
+ ** If the previous call failed due to an SQLITE_BUSY_TIMEOUT error,
+ ** then sleep for the minimum of 1us. The previous call already provided
+ ** an extra delay while it was blocking on the lock.
+ */
+ nBlockTmout = (nDelay+998) / 1000;
+ if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){
+ if( *pCnt & WAL_RETRY_BLOCKED_MASK ) nDelay = 1;
+ }
+#endif
sqlite3OsSleep(pWal->pVfs, nDelay);
+ *pCnt &= ~WAL_RETRY_BLOCKED_MASK;
}
if( !useWal ){
@@ -60555,6 +67552,13 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
if( pWal->bShmUnreliable==0 ){
rc = walIndexReadHdr(pWal, pChanged);
}
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ walDisableBlocking(pWal);
+ if( rc==SQLITE_BUSY_TIMEOUT ){
+ rc = SQLITE_BUSY;
+ *pCnt |= WAL_RETRY_BLOCKED_MASK;
+ }
+#endif
if( rc==SQLITE_BUSY ){
/* If there is not a recovery running in another thread or process
** then convert BUSY errors to WAL_RETRY. If recovery is known to
@@ -60567,9 +67571,9 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
if( pWal->apWiData[0]==0 ){
/* This branch is taken when the xShmMap() method returns SQLITE_BUSY.
** We assume this is a transient condition, so return WAL_RETRY. The
- ** xShmMap() implementation used by the default unix and win32 VFS
- ** modules may return SQLITE_BUSY due to a race condition in the
- ** code that determines whether or not the shared-memory region
+ ** xShmMap() implementation used by the default unix and win32 VFS
+ ** modules may return SQLITE_BUSY due to a race condition in the
+ ** code that determines whether or not the shared-memory region
** must be zeroed before the requested page is returned.
*/
rc = WAL_RETRY;
@@ -60591,7 +67595,8 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
assert( pWal->nWiData>0 );
assert( pWal->apWiData[0]!=0 );
pInfo = walCkptInfo(pWal);
- if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame
+ SEH_INJECT_FAULT;
+ if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame
#ifdef SQLITE_ENABLE_SNAPSHOT
&& (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0)
#endif
@@ -60610,7 +67615,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
** happening, this is usually correct.
**
- ** However, if frames have been appended to the log (or if the log
+ ** However, if frames have been appended to the log (or if the log
** is wrapped and written for that matter) before the READ_LOCK(0)
** is obtained, that is not necessarily true. A checkpointer may
** have started to backfill the appended frames but crashed before
@@ -60640,7 +67645,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
}
#endif
for(i=1; i<WAL_NREADER; i++){
- u32 thisMark = AtomicLoad(pInfo->aReadMark+i);
+ u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
if( mxReadMark<=thisMark && thisMark<=mxFrame ){
assert( thisMark!=READMARK_NOT_USED );
mxReadMark = thisMark;
@@ -60653,7 +67658,8 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
for(i=1; i<WAL_NREADER; i++){
rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){
- mxReadMark = AtomicStore(pInfo->aReadMark+i,mxFrame);
+ AtomicStore(pInfo->aReadMark+i,mxFrame);
+ mxReadMark = mxFrame;
mxI = i;
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
break;
@@ -60667,9 +67673,19 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
}
+ (void)walEnableBlockingMs(pWal, nBlockTmout);
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
+ walDisableBlocking(pWal);
if( rc ){
- return rc==SQLITE_BUSY ? WAL_RETRY : rc;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ if( rc==SQLITE_BUSY_TIMEOUT ){
+ *pCnt |= WAL_RETRY_BLOCKED_MASK;
+ }
+#else
+ assert( rc!=SQLITE_BUSY_TIMEOUT );
+#endif
+ assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT );
+ return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
}
/* Now that the read-lock has been obtained, check that neither the
** value in the aReadMark[] array or the contents of the wal-index
@@ -60691,9 +67707,9 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** to read any frames earlier than minFrame from the wal file - they
** can be safely read directly from the database file.
**
- ** Because a ShmBarrier() call is made between taking the copy of
+ ** Because a ShmBarrier() call is made between taking the copy of
** nBackfill and checking that the wal-header in shared-memory still
- ** matches the one cached in pWal->hdr, it is guaranteed that the
+ ** matches the one cached in pWal->hdr, it is guaranteed that the
** checkpointer that set nBackfill was not working with a wal-index
** header newer than that cached in pWal->hdr. If it were, that could
** cause a problem. The checkpointer could omit to checkpoint
@@ -60705,7 +67721,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** we can guarantee that the checkpointer that set nBackfill could not
** see any pages past pWal->hdr.mxFrame, this problem does not come up.
*/
- pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1;
+ pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT;
walShmBarrier(pWal);
if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
|| memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
@@ -60721,15 +67737,63 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
#ifdef SQLITE_ENABLE_SNAPSHOT
/*
-** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted
+** This function does the work of sqlite3WalSnapshotRecover().
+*/
+static int walSnapshotRecover(
+ Wal *pWal, /* WAL handle */
+ void *pBuf1, /* Temp buffer pWal->szPage bytes in size */
+ void *pBuf2 /* Temp buffer pWal->szPage bytes in size */
+){
+ int szPage = (int)pWal->szPage;
+ int rc;
+ i64 szDb; /* Size of db file in bytes */
+
+ rc = sqlite3OsFileSize(pWal->pDbFd, &szDb);
+ if( rc==SQLITE_OK ){
+ volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
+ u32 i = pInfo->nBackfillAttempted;
+ for(i=pInfo->nBackfillAttempted; i>AtomicLoad(&pInfo->nBackfill); i--){
+ WalHashLoc sLoc; /* Hash table location */
+ u32 pgno; /* Page number in db file */
+ i64 iDbOff; /* Offset of db file entry */
+ i64 iWalOff; /* Offset of wal file entry */
+
+ rc = walHashGet(pWal, walFramePage(i), &sLoc);
+ if( rc!=SQLITE_OK ) break;
+ assert( i - sLoc.iZero - 1 >=0 );
+ pgno = sLoc.aPgno[i-sLoc.iZero-1];
+ iDbOff = (i64)(pgno-1) * szPage;
+
+ if( iDbOff+szPage<=szDb ){
+ iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
+ rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
+ }
+
+ if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){
+ break;
+ }
+ }
+
+ pInfo->nBackfillAttempted = i-1;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted
** variable so that older snapshots can be accessed. To do this, loop
-** through all wal frames from nBackfillAttempted to (nBackfill+1),
+** through all wal frames from nBackfillAttempted to (nBackfill+1),
** comparing their content to the corresponding page with the database
** file, if any. Set nBackfillAttempted to the frame number of the
** first frame for which the wal file content matches the db file.
**
-** This is only really safe if the file-system is such that any page
-** writes made by earlier checkpointers were atomic operations, which
+** This is only really safe if the file-system is such that any page
+** writes made by earlier checkpointers were atomic operations, which
** is not always true. It is also possible that nBackfillAttempted
** may be left set to a value larger than expected, if a wal frame
** contains content that duplicate of an earlier version of the same
@@ -60745,49 +67809,21 @@ SQLITE_PRIVATE int sqlite3WalSnapshotRecover(Wal *pWal){
assert( pWal->readLock>=0 );
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
if( rc==SQLITE_OK ){
- volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
- int szPage = (int)pWal->szPage;
- i64 szDb; /* Size of db file in bytes */
-
- rc = sqlite3OsFileSize(pWal->pDbFd, &szDb);
- if( rc==SQLITE_OK ){
- void *pBuf1 = sqlite3_malloc(szPage);
- void *pBuf2 = sqlite3_malloc(szPage);
- if( pBuf1==0 || pBuf2==0 ){
- rc = SQLITE_NOMEM;
- }else{
- u32 i = pInfo->nBackfillAttempted;
- for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){
- WalHashLoc sLoc; /* Hash table location */
- u32 pgno; /* Page number in db file */
- i64 iDbOff; /* Offset of db file entry */
- i64 iWalOff; /* Offset of wal file entry */
-
- rc = walHashGet(pWal, walFramePage(i), &sLoc);
- if( rc!=SQLITE_OK ) break;
- pgno = sLoc.aPgno[i-sLoc.iZero];
- iDbOff = (i64)(pgno-1) * szPage;
-
- if( iDbOff+szPage<=szDb ){
- iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
- rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);
-
- if( rc==SQLITE_OK ){
- rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
- }
-
- if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){
- break;
- }
- }
-
- pInfo->nBackfillAttempted = i-1;
- }
+ void *pBuf1 = sqlite3_malloc(pWal->szPage);
+ void *pBuf2 = sqlite3_malloc(pWal->szPage);
+ if( pBuf1==0 || pBuf2==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pWal->ckptLock = 1;
+ SEH_TRY {
+ rc = walSnapshotRecover(pWal, pBuf1, pBuf2);
}
-
- sqlite3_free(pBuf1);
- sqlite3_free(pBuf2);
+ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
+ pWal->ckptLock = 0;
}
+
+ sqlite3_free(pBuf1);
+ sqlite3_free(pBuf2);
walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
}
@@ -60796,33 +67832,48 @@ SQLITE_PRIVATE int sqlite3WalSnapshotRecover(Wal *pWal){
#endif /* SQLITE_ENABLE_SNAPSHOT */
/*
-** Begin a read transaction on the database.
-**
-** This routine used to be called sqlite3OpenSnapshot() and with good reason:
-** it takes a snapshot of the state of the WAL and wal-index for the current
-** instant in time. The current thread will continue to use this snapshot.
-** Other threads might append new content to the WAL and wal-index but
-** that extra content is ignored by the current thread.
-**
-** If the database contents have changes since the previous read
-** transaction, then *pChanged is set to 1 before returning. The
-** Pager layer will use this to know that its cache is stale and
-** needs to be flushed.
+** This function does the work of sqlite3WalBeginReadTransaction() (see
+** below). That function simply calls this one inside an SEH_TRY{...} block.
*/
-SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
+static int walBeginReadTransaction(Wal *pWal, int *pChanged){
int rc; /* Return code */
int cnt = 0; /* Number of TryBeginRead attempts */
-
#ifdef SQLITE_ENABLE_SNAPSHOT
+ int ckptLock = 0;
int bChanged = 0;
WalIndexHdr *pSnapshot = pWal->pSnapshot;
- if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
- bChanged = 1;
+#endif
+
+ assert( pWal->ckptLock==0 );
+ assert( pWal->nSehTry>0 );
+
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ if( pSnapshot ){
+ if( memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
+ bChanged = 1;
+ }
+
+ /* It is possible that there is a checkpointer thread running
+ ** concurrent with this code. If this is the case, it may be that the
+ ** checkpointer has already determined that it will checkpoint
+ ** snapshot X, where X is later in the wal file than pSnapshot, but
+ ** has not yet set the pInfo->nBackfillAttempted variable to indicate
+ ** its intent. To avoid the race condition this leads to, ensure that
+ ** there is no checkpointer process by taking a shared CKPT lock
+ ** before checking pInfo->nBackfillAttempted. */
+ (void)walEnableBlocking(pWal);
+ rc = walLockShared(pWal, WAL_CKPT_LOCK);
+ walDisableBlocking(pWal);
+
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ ckptLock = 1;
}
#endif
do{
- rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
+ rc = walTryBeginRead(pWal, pChanged, 0, &cnt);
}while( rc==WAL_RETRY );
testcase( (rc&0xff)==SQLITE_BUSY );
testcase( (rc&0xff)==SQLITE_IOERR );
@@ -60850,53 +67901,69 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 );
assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );
- /* It is possible that there is a checkpointer thread running
- ** concurrent with this code. If this is the case, it may be that the
- ** checkpointer has already determined that it will checkpoint
- ** snapshot X, where X is later in the wal file than pSnapshot, but
- ** has not yet set the pInfo->nBackfillAttempted variable to indicate
- ** its intent. To avoid the race condition this leads to, ensure that
- ** there is no checkpointer process by taking a shared CKPT lock
- ** before checking pInfo->nBackfillAttempted.
- **
- ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing
- ** this already?
- */
- rc = walLockShared(pWal, WAL_CKPT_LOCK);
-
- if( rc==SQLITE_OK ){
- /* Check that the wal file has not been wrapped. Assuming that it has
- ** not, also check that no checkpointer has attempted to checkpoint any
- ** frames beyond pSnapshot->mxFrame. If either of these conditions are
- ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
- ** with *pSnapshot and set *pChanged as appropriate for opening the
- ** snapshot. */
- if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
- && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
- ){
- assert( pWal->readLock>0 );
- memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
- *pChanged = bChanged;
- }else{
- rc = SQLITE_ERROR_SNAPSHOT;
- }
-
- /* Release the shared CKPT lock obtained above. */
- walUnlockShared(pWal, WAL_CKPT_LOCK);
- pWal->minFrame = 1;
+ /* Check that the wal file has not been wrapped. Assuming that it has
+ ** not, also check that no checkpointer has attempted to checkpoint any
+ ** frames beyond pSnapshot->mxFrame. If either of these conditions are
+ ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
+ ** with *pSnapshot and set *pChanged as appropriate for opening the
+ ** snapshot. */
+ if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
+ && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
+ ){
+ assert( pWal->readLock>0 );
+ memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
+ *pChanged = bChanged;
+ }else{
+ rc = SQLITE_ERROR_SNAPSHOT;
}
+ /* A client using a non-current snapshot may not ignore any frames
+ ** from the start of the wal file. This is because, for a system
+ ** where (minFrame < iSnapshot < maxFrame), a checkpointer may
+ ** have omitted to checkpoint a frame earlier than minFrame in
+ ** the file because there exists a frame after iSnapshot that
+ ** is the same database page. */
+ pWal->minFrame = 1;
if( rc!=SQLITE_OK ){
sqlite3WalEndReadTransaction(pWal);
}
}
}
+
+ /* Release the shared CKPT lock obtained above. */
+ if( ckptLock ){
+ assert( pSnapshot );
+ walUnlockShared(pWal, WAL_CKPT_LOCK);
+ }
#endif
return rc;
}
/*
+** Begin a read transaction on the database.
+**
+** This routine used to be called sqlite3OpenSnapshot() and with good reason:
+** it takes a snapshot of the state of the WAL and wal-index for the current
+** instant in time. The current thread will continue to use this snapshot.
+** Other threads might append new content to the WAL and wal-index but
+** that extra content is ignored by the current thread.
+**
+** If the database contents have changes since the previous read
+** transaction, then *pChanged is set to 1 before returning. The
+** Pager layer will use this to know that its cache is stale and
+** needs to be flushed.
+*/
+SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
+ int rc;
+ SEH_TRY {
+ rc = walBeginReadTransaction(pWal, pChanged);
+ }
+ SEH_EXCEPT( rc = walHandleException(pWal); )
+ return rc;
+}
+
+/*
** Finish with a read transaction. All this does is release the
** read-lock.
*/
@@ -60916,7 +67983,7 @@ SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){
** Return SQLITE_OK if successful, or an error code if an error occurs. If an
** error does occur, the final value of *piRead is undefined.
*/
-SQLITE_PRIVATE int sqlite3WalFindFrame(
+static int walFindFrame(
Wal *pWal, /* WAL handle */
Pgno pgno, /* Database page number to read data for */
u32 *piRead /* OUT: Frame number (or zero) */
@@ -60931,8 +67998,8 @@ SQLITE_PRIVATE int sqlite3WalFindFrame(
/* If the "last page" field of the wal-index header snapshot is 0, then
** no data will be read from the wal under any circumstances. Return early
- ** in this case as an optimization. Likewise, if pWal->readLock==0,
- ** then the WAL is ignored by the reader so return early, as if the
+ ** in this case as an optimization. Likewise, if pWal->readLock==0,
+ ** then the WAL is ignored by the reader so return early, as if the
** WAL were empty.
*/
if( iLast==0 || (pWal->readLock==0 && pWal->bShmUnreliable==0) ){
@@ -60945,9 +68012,9 @@ SQLITE_PRIVATE int sqlite3WalFindFrame(
** hash table (each hash table indexes up to HASHTABLE_NPAGE frames).
**
** This code might run concurrently to the code in walIndexAppend()
- ** that adds entries to the wal-index (and possibly to this hash
- ** table). This means the value just read from the hash
- ** slot (aHash[iKey]) may have been added before or after the
+ ** that adds entries to the wal-index (and possibly to this hash
+ ** table). This means the value just read from the hash
+ ** slot (aHash[iKey]) may have been added before or after the
** current read transaction was opened. Values added after the
** read transaction was opened may have been written incorrectly -
** i.e. these slots may contain garbage data. However, we assume
@@ -60955,13 +68022,13 @@ SQLITE_PRIVATE int sqlite3WalFindFrame(
** opened remain unmodified.
**
** For the reasons above, the if(...) condition featured in the inner
- ** loop of the following block is more stringent that would be required
+ ** loop of the following block is more stringent that would be required
** if we had exclusive access to the hash-table:
**
- ** (aPgno[iFrame]==pgno):
+ ** (aPgno[iFrame]==pgno):
** This condition filters out normal hash-table collisions.
**
- ** (iFrame<=iLast):
+ ** (iFrame<=iLast):
** This condition filters out entries that were added to the hash
** table after the current read-transaction had started.
*/
@@ -60971,22 +68038,26 @@ SQLITE_PRIVATE int sqlite3WalFindFrame(
int iKey; /* Hash slot index */
int nCollide; /* Number of hash collisions remaining */
int rc; /* Error code */
+ u32 iH;
rc = walHashGet(pWal, iHash, &sLoc);
if( rc!=SQLITE_OK ){
return rc;
}
nCollide = HASHTABLE_NSLOT;
- for(iKey=walHash(pgno); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){
- u32 iFrame = sLoc.aHash[iKey] + sLoc.iZero;
- if( iFrame<=iLast && iFrame>=pWal->minFrame
- && sLoc.aPgno[sLoc.aHash[iKey]]==pgno ){
+ iKey = walHash(pgno);
+ SEH_INJECT_FAULT;
+ while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){
+ u32 iFrame = iH + sLoc.iZero;
+ if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){
assert( iFrame>iRead || CORRUPT_DB );
iRead = iFrame;
}
if( (nCollide--)==0 ){
+ *piRead = 0;
return SQLITE_CORRUPT_BKPT;
}
+ iKey = walNextHash(iKey);
}
if( iRead ) break;
}
@@ -61014,6 +68085,30 @@ SQLITE_PRIVATE int sqlite3WalFindFrame(
}
/*
+** Search the wal file for page pgno. If found, set *piRead to the frame that
+** contains the page. Otherwise, if pgno is not in the wal file, set *piRead
+** to zero.
+**
+** Return SQLITE_OK if successful, or an error code if an error occurs. If an
+** error does occur, the final value of *piRead is undefined.
+**
+** The difference between this function and walFindFrame() is that this
+** function wraps walFindFrame() in an SEH_TRY{...} block.
+*/
+SQLITE_PRIVATE int sqlite3WalFindFrame(
+ Wal *pWal, /* WAL handle */
+ Pgno pgno, /* Database page number to read data for */
+ u32 *piRead /* OUT: Frame number (or zero) */
+){
+ int rc;
+ SEH_TRY {
+ rc = walFindFrame(pWal, pgno, piRead);
+ }
+ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
+ return rc;
+}
+
+/*
** Read the contents of frame iRead from the wal file into buffer pOut
** (which is nOut bytes in size). Return SQLITE_OK if successful, or an
** error code otherwise.
@@ -61035,7 +68130,7 @@ SQLITE_PRIVATE int sqlite3WalReadFrame(
return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset);
}
-/*
+/*
** Return the size of the database in pages (or zero, if unknown).
*/
SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal){
@@ -61046,7 +68141,7 @@ SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal){
}
-/*
+/*
** This function starts a write transaction on the WAL.
**
** A read transaction must have already been started by a prior call
@@ -61062,6 +68157,16 @@ SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal){
SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
int rc;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* If the write-lock is already held, then it was obtained before the
+ ** read-transaction was even opened, making this call a no-op.
+ ** Return early. */
+ if( pWal->writeLock ){
+ assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) );
+ return SQLITE_OK;
+ }
+#endif
+
/* Cannot start a write transaction without first holding a read
** transaction. */
assert( pWal->readLock>=0 );
@@ -61084,12 +68189,17 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
** time the read transaction on this connection was started, then
** the write is disallowed.
*/
- if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
+ SEH_TRY {
+ if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
+ rc = SQLITE_BUSY_SNAPSHOT;
+ }
+ }
+ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
+
+ if( rc!=SQLITE_OK ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
- rc = SQLITE_BUSY_SNAPSHOT;
}
-
return rc;
}
@@ -61124,39 +68234,42 @@ SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *p
if( ALWAYS(pWal->writeLock) ){
Pgno iMax = pWal->hdr.mxFrame;
Pgno iFrame;
-
- /* Restore the clients cache of the wal-index header to the state it
- ** was in before the client began writing to the database.
- */
- memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
- for(iFrame=pWal->hdr.mxFrame+1;
- ALWAYS(rc==SQLITE_OK) && iFrame<=iMax;
- iFrame++
- ){
- /* This call cannot fail. Unless the page for which the page number
- ** is passed as the second argument is (a) in the cache and
- ** (b) has an outstanding reference, then xUndo is either a no-op
- ** (if (a) is false) or simply expels the page from the cache (if (b)
- ** is false).
- **
- ** If the upper layer is doing a rollback, it is guaranteed that there
- ** are no outstanding references to any page other than page 1. And
- ** page 1 is never written to the log until the transaction is
- ** committed. As a result, the call to xUndo may not fail.
+ SEH_TRY {
+ /* Restore the clients cache of the wal-index header to the state it
+ ** was in before the client began writing to the database.
*/
- assert( walFramePgno(pWal, iFrame)!=1 );
- rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
+ memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
+
+ for(iFrame=pWal->hdr.mxFrame+1;
+ ALWAYS(rc==SQLITE_OK) && iFrame<=iMax;
+ iFrame++
+ ){
+ /* This call cannot fail. Unless the page for which the page number
+ ** is passed as the second argument is (a) in the cache and
+ ** (b) has an outstanding reference, then xUndo is either a no-op
+ ** (if (a) is false) or simply expels the page from the cache (if (b)
+ ** is false).
+ **
+ ** If the upper layer is doing a rollback, it is guaranteed that there
+ ** are no outstanding references to any page other than page 1. And
+ ** page 1 is never written to the log until the transaction is
+ ** committed. As a result, the call to xUndo may not fail.
+ */
+ assert( walFramePgno(pWal, iFrame)!=1 );
+ rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
+ }
+ if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal);
}
- if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal);
+ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
}
return rc;
}
-/*
-** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32
-** values. This function populates the array with values required to
-** "rollback" the write position of the WAL handle back to the current
+/*
+** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32
+** values. This function populates the array with values required to
+** "rollback" the write position of the WAL handle back to the current
** point in the event of a savepoint rollback (via WalSavepointUndo()).
*/
SQLITE_PRIVATE void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){
@@ -61167,7 +68280,7 @@ SQLITE_PRIVATE void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){
aWalData[3] = pWal->nCkpt;
}
-/*
+/*
** Move the write position of the WAL back to the point identified by
** the values in the aWalData[] array. aWalData must point to an array
** of WAL_SAVEPOINT_NDATA u32 values that has been previously populated
@@ -61192,7 +68305,10 @@ SQLITE_PRIVATE int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
pWal->hdr.mxFrame = aWalData[0];
pWal->hdr.aFrameCksum[0] = aWalData[1];
pWal->hdr.aFrameCksum[1] = aWalData[2];
- walCleanupHash(pWal);
+ SEH_TRY {
+ walCleanupHash(pWal);
+ }
+ SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
}
return rc;
@@ -61242,7 +68358,7 @@ static int walRestartLog(Wal *pWal){
cnt = 0;
do{
int notUsed;
- rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
+ rc = walTryBeginRead(pWal, &notUsed, 1, &cnt);
}while( rc==WAL_RETRY );
assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
testcase( (rc&0xff)==SQLITE_IOERR );
@@ -61307,11 +68423,7 @@ static int walWriteOneFrame(
int rc; /* Result code from subfunctions */
void *pData; /* Data actually written */
u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
-#if defined(SQLITE_HAS_CODEC)
- if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT;
-#else
pData = pPage->pData;
-#endif
walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame);
rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset);
if( rc ) return rc;
@@ -61373,11 +68485,11 @@ static int walRewriteChecksums(Wal *pWal, u32 iLast){
return rc;
}
-/*
+/*
** Write a set of frames to the log. The caller must hold the write-lock
** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
*/
-SQLITE_PRIVATE int sqlite3WalFrames(
+static int walFrames(
Wal *pWal, /* Wal handle to write to */
int szPage, /* Database page-size in bytes */
PgHdr *pList, /* List of dirty pages to write */
@@ -61440,7 +68552,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
sqlite3Put4byte(&aWalHdr[28], aCksum[1]);
-
+
pWal->szPage = szPage;
pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
pWal->hdr.aFrameCksum[0] = aCksum[0];
@@ -61465,7 +68577,9 @@ SQLITE_PRIVATE int sqlite3WalFrames(
if( rc ) return rc;
}
}
- assert( (int)pWal->szPage==szPage );
+ if( (int)pWal->szPage!=szPage ){
+ return SQLITE_CORRUPT_BKPT; /* TH3 test case: cov1/corrupt155.test */
+ }
/* Setup information needed to write frames into the WAL */
w.pWal = pWal;
@@ -61482,11 +68596,11 @@ SQLITE_PRIVATE int sqlite3WalFrames(
/* Check if this page has already been written into the wal file by
** the current transaction. If so, overwrite the existing frame and
- ** set Wal.writeLock to WAL_WRITELOCK_RECKSUM - indicating that
+ ** set Wal.writeLock to WAL_WRITELOCK_RECKSUM - indicating that
** checksums must be recomputed when the transaction is committed. */
if( iFirst && (p->pDirty || isCommit==0) ){
u32 iWrite = 0;
- VVA_ONLY(rc =) sqlite3WalFindFrame(pWal, p->pgno, &iWrite);
+ VVA_ONLY(rc =) walFindFrame(pWal, p->pgno, &iWrite);
assert( rc==SQLITE_OK || iWrite==0 );
if( iWrite>=iFirst ){
i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE;
@@ -61494,11 +68608,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
if( pWal->iReCksum==0 || iWrite<pWal->iReCksum ){
pWal->iReCksum = iWrite;
}
-#if defined(SQLITE_HAS_CODEC)
- if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM;
-#else
pData = p->pData;
-#endif
rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOff);
if( rc ) return rc;
p->flags &= ~PGHDR_WAL_APPEND;
@@ -61548,6 +68658,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
if( rc ) return rc;
iOffset += szFrame;
nExtra++;
+ assert( pLast!=0 );
}
}
if( bSync ){
@@ -61569,7 +68680,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
pWal->truncateOnCommit = 0;
}
- /* Append data to the wal-index. It is not necessary to lock the
+ /* Append data to the wal-index. It is not necessary to lock the
** wal-index to do this as the SQLITE_SHM_WRITE lock held on the wal-index
** guarantees that there are no other writers, and no data that may
** be in use by existing readers is being overwritten.
@@ -61580,6 +68691,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
iFrame++;
rc = walIndexAppend(pWal, iFrame, p->pgno);
}
+ assert( pLast!=0 || nExtra==0 );
while( rc==SQLITE_OK && nExtra>0 ){
iFrame++;
nExtra--;
@@ -61607,7 +68719,30 @@ SQLITE_PRIVATE int sqlite3WalFrames(
return rc;
}
-/*
+/*
+** Write a set of frames to the log. The caller must hold the write-lock
+** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
+**
+** The difference between this function and walFrames() is that this
+** function wraps walFrames() in an SEH_TRY{...} block.
+*/
+SQLITE_PRIVATE int sqlite3WalFrames(
+ Wal *pWal, /* Wal handle to write to */
+ int szPage, /* Database page-size in bytes */
+ PgHdr *pList, /* List of dirty pages to write */
+ Pgno nTruncate, /* Database size after this commit */
+ int isCommit, /* True if this is a commit */
+ int sync_flags /* Flags to pass to OsSync() (or 0) */
+){
+ int rc;
+ SEH_TRY {
+ rc = walFrames(pWal, szPage, pList, nTruncate, isCommit, sync_flags);
+ }
+ SEH_EXCEPT( rc = walHandleException(pWal); )
+ return rc;
+}
+
+/*
** This routine is called to implement sqlite3_wal_checkpoint() and
** related interfaces.
**
@@ -61644,68 +68779,82 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
if( pWal->readOnly ) return SQLITE_READONLY;
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
- /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
- ** "checkpoint" lock on the database file. */
- rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
- if( rc ){
- /* EVIDENCE-OF: R-10421-19736 If any other process is running a
- ** checkpoint operation at the same time, the lock cannot be obtained and
- ** SQLITE_BUSY is returned.
- ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
- ** it will not be invoked in this case.
- */
- testcase( rc==SQLITE_BUSY );
- testcase( xBusy!=0 );
- return rc;
- }
- pWal->ckptLock = 1;
+ /* Enable blocking locks, if possible. */
+ sqlite3WalDb(pWal, db);
+ if( xBusy2 ) (void)walEnableBlocking(pWal);
- /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
- ** TRUNCATE modes also obtain the exclusive "writer" lock on the database
- ** file.
- **
- ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
- ** immediately, and a busy-handler is configured, it is invoked and the
- ** writer lock retried until either the busy-handler returns 0 or the
- ** lock is successfully obtained.
+ /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
+ ** "checkpoint" lock on the database file.
+ ** EVIDENCE-OF: R-10421-19736 If any other process is running a
+ ** checkpoint operation at the same time, the lock cannot be obtained and
+ ** SQLITE_BUSY is returned.
+ ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
+ ** it will not be invoked in this case.
*/
- if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
- rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
- if( rc==SQLITE_OK ){
- pWal->writeLock = 1;
- }else if( rc==SQLITE_BUSY ){
- eMode2 = SQLITE_CHECKPOINT_PASSIVE;
- xBusy2 = 0;
- rc = SQLITE_OK;
- }
- }
-
- /* Read the wal-index header. */
+ rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
+ testcase( rc==SQLITE_BUSY );
+ testcase( rc!=SQLITE_OK && xBusy2!=0 );
if( rc==SQLITE_OK ){
- rc = walIndexReadHdr(pWal, &isChanged);
- if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
- sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
+ pWal->ckptLock = 1;
+
+ /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
+ ** TRUNCATE modes also obtain the exclusive "writer" lock on the database
+ ** file.
+ **
+ ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
+ ** immediately, and a busy-handler is configured, it is invoked and the
+ ** writer lock retried until either the busy-handler returns 0 or the
+ ** lock is successfully obtained.
+ */
+ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
+ rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1);
+ if( rc==SQLITE_OK ){
+ pWal->writeLock = 1;
+ }else if( rc==SQLITE_BUSY ){
+ eMode2 = SQLITE_CHECKPOINT_PASSIVE;
+ xBusy2 = 0;
+ rc = SQLITE_OK;
+ }
}
}
- /* Copy data from the log to the database file. */
- if( rc==SQLITE_OK ){
- if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
- rc = SQLITE_CORRUPT_BKPT;
- }else{
- rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf);
+ /* Read the wal-index header. */
+ SEH_TRY {
+ if( rc==SQLITE_OK ){
+ /* For a passive checkpoint, do not re-enable blocking locks after
+ ** reading the wal-index header. A passive checkpoint should not block
+ ** or invoke the busy handler. The only lock such a checkpoint may
+ ** attempt to obtain is a lock on a read-slot, and it should give up
+ ** immediately and do a partial checkpoint if it cannot obtain it. */
+ walDisableBlocking(pWal);
+ rc = walIndexReadHdr(pWal, &isChanged);
+ if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal);
+ if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
+ sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
+ }
}
- /* If no error occurred, set the output variables. */
- if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
- if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame;
- if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill);
+ /* Copy data from the log to the database file. */
+ if( rc==SQLITE_OK ){
+ if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf);
+ }
+
+ /* If no error occurred, set the output variables. */
+ if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
+ if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame;
+ SEH_INJECT_FAULT;
+ if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill);
+ }
}
}
+ SEH_EXCEPT( rc = walHandleException(pWal); )
if( isChanged ){
- /* If a new wal-index header was loaded before the checkpoint was
+ /* If a new wal-index header was loaded before the checkpoint was
** performed, then the pager-cache associated with pWal is now
** out of date. So zero the cached wal-index header to ensure that
** next time the pager opens a snapshot on this database it knows that
@@ -61714,11 +68863,19 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
}
+ walDisableBlocking(pWal);
+ sqlite3WalDb(pWal, 0);
+
/* Release the locks. */
sqlite3WalEndWriteTransaction(pWal);
- walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
- pWal->ckptLock = 0;
+ if( pWal->ckptLock ){
+ walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
+ pWal->ckptLock = 0;
+ }
WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
+#endif
return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
}
@@ -61748,7 +68905,7 @@ SQLITE_PRIVATE int sqlite3WalCallback(Wal *pWal){
** operation must occur while the pager is still holding the exclusive
** lock on the main database file.
**
-** If op is one, then change from locking_mode=NORMAL into
+** If op is one, then change from locking_mode=NORMAL into
** locking_mode=EXCLUSIVE. This means that the pWal->readLock must
** be released. Return 1 if the transition is made and 0 if the
** WAL is already in exclusive-locking mode - meaning that this
@@ -61765,13 +68922,15 @@ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op){
assert( pWal->writeLock==0 );
assert( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE || op==-1 );
- /* pWal->readLock is usually set, but might be -1 if there was a
- ** prior error while attempting to acquire are read-lock. This cannot
+ /* pWal->readLock is usually set, but might be -1 if there was a
+ ** prior error while attempting to acquire are read-lock. This cannot
** happen if the connection is actually in exclusive mode (as no xShmLock
** locks are taken in this case). Nor should the pager attempt to
** upgrade to exclusive-mode following such an error.
*/
+#ifndef SQLITE_USE_SEH
assert( pWal->readLock>=0 || pWal->lockError );
+#endif
assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) );
if( op==0 ){
@@ -61797,10 +68956,10 @@ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op){
return rc;
}
-/*
+/*
** Return true if the argument is non-NULL and the WAL module is using
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
-** WAL module is using shared-memory, return false.
+** WAL module is using shared-memory, return false.
*/
SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE );
@@ -61835,11 +68994,14 @@ SQLITE_PRIVATE int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapsho
/* Try to open on pSnapshot when the next read-transaction starts
*/
-SQLITE_PRIVATE void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){
+SQLITE_PRIVATE void sqlite3WalSnapshotOpen(
+ Wal *pWal,
+ sqlite3_snapshot *pSnapshot
+){
pWal->pSnapshot = (WalIndexHdr*)pSnapshot;
}
-/*
+/*
** Return a +ve value if snapshot p1 is newer than p2. A -ve value if
** p1 is older than p2 and zero if p1 and p2 are the same snapshot.
*/
@@ -61859,7 +69021,7 @@ SQLITE_API int sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){
/*
** The caller currently has a read transaction open on the database.
** This function takes a SHARED lock on the CHECKPOINTER slot and then
-** checks if the snapshot passed as the second argument is still
+** checks if the snapshot passed as the second argument is still
** available. If so, SQLITE_OK is returned.
**
** If the snapshot is not available, SQLITE_ERROR is returned. Or, if
@@ -61869,16 +69031,19 @@ SQLITE_API int sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){
*/
SQLITE_PRIVATE int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot){
int rc;
- rc = walLockShared(pWal, WAL_CKPT_LOCK);
- if( rc==SQLITE_OK ){
- WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot;
- if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
- || pNew->mxFrame<walCkptInfo(pWal)->nBackfillAttempted
- ){
- rc = SQLITE_ERROR_SNAPSHOT;
- walUnlockShared(pWal, WAL_CKPT_LOCK);
+ SEH_TRY {
+ rc = walLockShared(pWal, WAL_CKPT_LOCK);
+ if( rc==SQLITE_OK ){
+ WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot;
+ if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
+ || pNew->mxFrame<walCkptInfo(pWal)->nBackfillAttempted
+ ){
+ rc = SQLITE_ERROR_SNAPSHOT;
+ walUnlockShared(pWal, WAL_CKPT_LOCK);
+ }
}
}
+ SEH_EXCEPT( rc = walHandleException(pWal); )
return rc;
}
@@ -61966,16 +69131,16 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){
** on Ptr(N) and its subpages have values greater than Key(N-1). And
** so forth.
**
-** Finding a particular key requires reading O(log(M)) pages from the
+** Finding a particular key requires reading O(log(M)) pages from the
** disk where M is the number of entries in the tree.
**
-** In this implementation, a single file can hold one or more separate
+** In this implementation, a single file can hold one or more separate
** BTrees. Each BTree is identified by the index of its root page. The
** key and data for any entry are combined to form the "payload". A
** fixed amount of payload can be carried directly on the database
** page. If the payload is larger than the preset amount then surplus
** bytes are stored on overflow pages. The payload for an entry
-** and the preceding pointer are combined to form a "Cell". Each
+** and the preceding pointer are combined to form a "Cell". Each
** page has a small header which contains the Ptr(N) pointer and other
** information such as the size of key and data.
**
@@ -62001,7 +69166,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){
** 22 1 Min embedded payload fraction (must be 32)
** 23 1 Min leaf payload fraction (must be 32)
** 24 4 File change counter
-** 28 4 Reserved for future use
+** 28 4 The size of the database in pages
** 32 4 First freelist page
** 36 4 Number of freelist pages in the file
** 40 60 15 4-byte meta values passed to higher layers
@@ -62105,11 +69270,11 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){
** contiguous or in order, but cell pointers are contiguous and in order.
**
** Cell content makes use of variable length integers. A variable
-** length integer is 1 to 9 bytes where the lower 7 bits of each
+** length integer is 1 to 9 bytes where the lower 7 bits of each
** byte are used. The integer consists of all bytes that have bit 8 set and
** the first byte with bit 8 clear. The most significant byte of the integer
** appears first. A variable-length integer may not be more than 9 bytes long.
-** As a special case, all 8 bytes of the 9th byte are used as data. This
+** As a special case, all 8 bits of the 9th byte are used as data. This
** allows a 64-bit integer to be encoded in 9 bytes.
**
** 0x00 becomes 0x00000000
@@ -62117,7 +69282,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){
** 0x81 0x00 becomes 0x00000080
** 0x82 0x00 becomes 0x00000100
** 0x80 0x7f becomes 0x0000007f
-** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678
+** 0x81 0x91 0xd1 0xac 0x78 becomes 0x12345678
** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081
**
** Variable length integers are used for rowids and to hold the number of
@@ -62178,7 +69343,7 @@ typedef struct CellInfo CellInfo;
** -DSQLITE_FILE_HEADER="..." on the compiler command-line. The
** header must be exactly 16 bytes including the zero-terminator so
** the string itself should be 15 characters long. If you change
-** the header, then your custom library will not be able to read
+** the header, then your custom library will not be able to read
** databases generated by the standard tools and the standard tools
** will not be able to read databases created by your custom library.
*/
@@ -62200,7 +69365,7 @@ typedef struct CellInfo CellInfo;
** page that has been loaded into memory. The information in this object
** is derived from the raw on-disk page content.
**
-** As each database page is loaded into memory, the pager allocats an
+** As each database page is loaded into memory, the pager allocates an
** instance of this object and zeros the first 8 bytes. (This is the
** "extra" information associated with each page of the pager.)
**
@@ -62209,7 +69374,6 @@ typedef struct CellInfo CellInfo;
*/
struct MemPage {
u8 isInit; /* True if previously initialized. MUST BE FIRST! */
- u8 bBusy; /* Prevent endless loops on corrupt database files */
u8 intKey; /* True if table b-trees. False for index b-trees */
u8 intKeyLeaf; /* True if the leaf of an intKey table */
Pgno pgno; /* Page number for this page */
@@ -62223,7 +69387,7 @@ struct MemPage {
u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */
u16 cellOffset; /* Index in aData of first cell pointer */
- u16 nFree; /* Number of free bytes on the page */
+ int nFree; /* Number of free bytes on the page. -1 for unknown */
u16 nCell; /* Number of cells on this page, local and ovfl */
u16 maskPage; /* Mask for page offset */
u16 aiOvfl[4]; /* Insert the i-th overflow cell before the aiOvfl-th
@@ -62231,7 +69395,9 @@ struct MemPage {
u8 *apOvfl[4]; /* Pointers to the body of overflow cells */
BtShared *pBt; /* Pointer to BtShared that this page is part of */
u8 *aData; /* Pointer to disk image of the page data */
- u8 *aDataEnd; /* One byte past the end of usable data */
+ u8 *aDataEnd; /* One byte past the end of the entire page - not just
+ ** the usable space, the entire page. Used to prevent
+ ** corruption-induced buffer overflow. */
u8 *aCellIdx; /* The cell index area */
u8 *aDataOfst; /* Same as aData for leaves. aData+4 for interior */
DbPage *pDbPage; /* Pager page handle */
@@ -62241,7 +69407,7 @@ struct MemPage {
/*
** A linked list of the following structures is stored at BtShared.pLock.
-** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor
+** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor
** is opened on the table with root page BtShared.iTable. Locks are removed
** from this list when a transaction is committed or rolled back, or when
** a btree handle is closed.
@@ -62265,7 +69431,7 @@ struct BtLock {
** see the internals of this structure and only deals with pointers to
** this structure.
**
-** For some database files, the same underlying database cache might be
+** For some database files, the same underlying database cache might be
** shared between multiple connections. In that case, each connection
** has it own instance of this object. But each instance of this object
** points to the same BtShared object. The database cache and the
@@ -62273,7 +69439,7 @@ struct BtLock {
** the BtShared object.
**
** All fields in this structure are accessed under sqlite3.mutex.
-** The pBt pointer itself may not be changed while there exists cursors
+** The pBt pointer itself may not be changed while there exists cursors
** in the referenced BtShared that point back to this Btree since those
** cursors have to go through this Btree to find their BtShared and
** they often do so without holding sqlite3.mutex.
@@ -62287,9 +69453,12 @@ struct Btree {
u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */
int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
int nBackup; /* Number of backup operations reading this btree */
- u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */
+ u32 iBDataVersion; /* Combines with pBt->pPager->iDataVersion */
Btree *pNext; /* List of other sharable Btrees from the same db */
Btree *pPrev; /* Back pointer of the same list */
+#ifdef SQLITE_DEBUG
+ u64 nSeek; /* Calls to sqlite3BtreeMovetoUnpacked() */
+#endif
#ifndef SQLITE_OMIT_SHARED_CACHE
BtLock lock; /* Object used to lock page 1 */
#endif
@@ -62301,14 +69470,28 @@ struct Btree {
** If the shared-data extension is enabled, there may be multiple users
** of the Btree structure. At most one of these may open a write transaction,
** but any number may have active read transactions.
+**
+** These values must match SQLITE_TXN_NONE, SQLITE_TXN_READ, and
+** SQLITE_TXN_WRITE
*/
#define TRANS_NONE 0
#define TRANS_READ 1
#define TRANS_WRITE 2
+#if TRANS_NONE!=SQLITE_TXN_NONE
+# error wrong numeric code for no-transaction
+#endif
+#if TRANS_READ!=SQLITE_TXN_READ
+# error wrong numeric code for read-transaction
+#endif
+#if TRANS_WRITE!=SQLITE_TXN_WRITE
+# error wrong numeric code for write-transaction
+#endif
+
+
/*
** An instance of this object represents a single database file.
-**
+**
** A single database file can be in use at the same time by two
** or more database connections. When two or more connections are
** sharing the same database file, each connection has it own
@@ -62318,7 +69501,7 @@ struct Btree {
**
** Fields in this structure are accessed under the BtShared.mutex
** mutex, except for nRef and pNext which are accessed under the
-** global SQLITE_MUTEX_STATIC_MASTER mutex. The pPager field
+** global SQLITE_MUTEX_STATIC_MAIN mutex. The pPager field
** may not be modified once it is initially set as long as nRef>0.
** The pSchema field may be set once under BtShared.mutex and
** thereafter is unchanged as long as nRef>0.
@@ -62354,9 +69537,7 @@ struct BtShared {
#endif
u8 inTransaction; /* Transaction state */
u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */
-#ifdef SQLITE_HAS_CODEC
- u8 optimalReserve; /* Desired amount of reserved space per page */
-#endif
+ u8 nReserveWanted; /* Desired number of extra bytes per page */
u16 btsFlags; /* Boolean parameters. See BTS_* macros below */
u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
@@ -62377,6 +69558,7 @@ struct BtShared {
Btree *pWriter; /* Btree with currently open write transaction */
#endif
u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */
+ int nPreformatSize; /* Size of last cell written by TransferRow() */
};
/*
@@ -62428,12 +69610,19 @@ struct CellInfo {
** particular database connection identified BtCursor.pBtree.db.
**
** Fields in this structure are accessed under the BtShared.mutex
-** found at self->pBt->mutex.
+** found at self->pBt->mutex.
**
** skipNext meaning:
-** eState==SKIPNEXT && skipNext>0: Next sqlite3BtreeNext() is no-op.
-** eState==SKIPNEXT && skipNext<0: Next sqlite3BtreePrevious() is no-op.
-** eState==FAULT: Cursor fault with skipNext as error code.
+** The meaning of skipNext depends on the value of eState:
+**
+** eState Meaning of skipNext
+** VALID skipNext is meaningless and is ignored
+** INVALID skipNext is meaningless and is ignored
+** SKIPNEXT sqlite3BtreeNext() is a no-op if skipNext>0 and
+** sqlite3BtreePrevious() is no-op if skipNext<0.
+** REQUIRESEEK restoreCursorPosition() restores the cursor to
+** eState=SKIPNEXT if skipNext!=0
+** FAULT skipNext holds the cursor fault error code.
*/
struct BtCursor {
u8 eState; /* One of the CURSOR_XXX constants (see below) */
@@ -62469,15 +69658,16 @@ struct BtCursor {
#define BTCF_WriteFlag 0x01 /* True if a write cursor */
#define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */
#define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */
-#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */
+#define BTCF_AtLast 0x08 /* Cursor is pointing to the last entry */
#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */
#define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */
+#define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */
/*
** Potential values for BtCursor.eState.
**
** CURSOR_INVALID:
-** Cursor does not point to a valid entry. This can happen (for example)
+** Cursor does not point to a valid entry. This can happen (for example)
** because the table is empty or because BtreeCursorFirst() has not been
** called.
**
@@ -62490,9 +69680,9 @@ struct BtCursor {
** operation should be a no-op.
**
** CURSOR_REQUIRESEEK:
-** The table that this cursor was opened on still exists, but has been
+** The table that this cursor was opened on still exists, but has been
** modified since the cursor was last used. The cursor position is saved
-** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in
+** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in
** this state, restoreCursorPosition() can be called to attempt to
** seek the cursor to the saved position.
**
@@ -62509,13 +69699,13 @@ struct BtCursor {
#define CURSOR_REQUIRESEEK 3
#define CURSOR_FAULT 4
-/*
+/*
** The database page the PENDING_BYTE occupies. This page is never used.
*/
-# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt)
+#define PENDING_BYTE_PAGE(pBt) ((Pgno)((PENDING_BYTE/((pBt)->pageSize))+1))
/*
-** These macros define the location of the pointer-map entry for a
+** These macros define the location of the pointer-map entry for a
** database page. The first argument to each is the number of usable
** bytes on each page of the database (often 1024). The second is the
** page number to look up in the pointer map.
@@ -62550,10 +69740,10 @@ struct BtCursor {
** PTRMAP_ROOTPAGE: The database page is a root-page. The page-number is not
** used in this case.
**
-** PTRMAP_FREEPAGE: The database page is an unused (free) page. The page-number
+** PTRMAP_FREEPAGE: The database page is an unused (free) page. The page-number
** is not used in this case.
**
-** PTRMAP_OVERFLOW1: The database page is the first page in a list of
+** PTRMAP_OVERFLOW1: The database page is the first page in a list of
** overflow pages. The page number identifies the page that
** contains the cell with a pointer to this overflow page.
**
@@ -62575,31 +69765,31 @@ struct BtCursor {
*/
#define btreeIntegrity(p) \
assert( p->pBt->inTransaction!=TRANS_NONE || p->pBt->nTransaction==0 ); \
- assert( p->pBt->inTransaction>=p->inTrans );
+ assert( p->pBt->inTransaction>=p->inTrans );
/*
** The ISAUTOVACUUM macro is used within balance_nonroot() to determine
** if the database supports auto-vacuum or not. Because it is used
-** within an expression that is an argument to another macro
+** within an expression that is an argument to another macro
** (sqliteMallocRaw), it is not possible to use conditional compilation.
** So, this macro is defined instead.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
-#define ISAUTOVACUUM (pBt->autoVacuum)
+#define ISAUTOVACUUM(pBt) (pBt->autoVacuum)
#else
-#define ISAUTOVACUUM 0
+#define ISAUTOVACUUM(pBt) 0
#endif
/*
-** This structure is passed around through all the sanity checking routines
-** in order to keep track of some global state information.
+** This structure is passed around through all the PRAGMA integrity_check
+** checking routines in order to keep track of some global state information.
**
** The aRef[] array is allocated so that there is 1 bit for each page in
** the database. As the integrity-check proceeds, for each page used in
-** the database the corresponding bit is set. This allows integrity-check to
-** detect pages that are used twice and orphaned pages (both of which
+** the database the corresponding bit is set. This allows integrity-check to
+** detect pages that are used twice and orphaned pages (both of which
** indicate corruption).
*/
typedef struct IntegrityCk IntegrityCk;
@@ -62607,14 +69797,18 @@ struct IntegrityCk {
BtShared *pBt; /* The tree being checked out */
Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
u8 *aPgRef; /* 1 bit per page in the db (see above) */
- Pgno nPage; /* Number of pages in the database */
+ Pgno nCkPage; /* Pages in the database. 0 for partial check */
int mxErr; /* Stop accumulating errors when this reaches zero */
int nErr; /* Number of messages written to zErrMsg so far */
- int mallocFailed; /* A memory allocation error has occurred */
+ int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */
+ u32 nStep; /* Number of steps into the integrity_check process */
const char *zPfx; /* Error message prefix */
- int v1, v2; /* Values for up to two %d fields in zPfx */
+ Pgno v0; /* Value for first %u substitution in zPfx (root page) */
+ Pgno v1; /* Value for second %u substitution in zPfx (current pg) */
+ int v2; /* Value for third %d substitution in zPfx */
StrAccum errMsg; /* Accumulate the error message text here */
u32 *heap; /* Min-heap used for analyzing cell coverage */
+ sqlite3 *db; /* Database connection running the check */
};
/*
@@ -62627,7 +69821,7 @@ struct IntegrityCk {
/*
** get2byteAligned(), unlike get2byte(), requires that its argument point to a
-** two-byte aligned address. get2bytea() is only used for accessing the
+** two-byte aligned address. get2byteAligned() is only used for accessing the
** cell addresses in a btree header.
*/
#if SQLITE_BYTEORDER==4321
@@ -62804,7 +69998,7 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree *p){
**
** There is a corresponding leave-all procedures.
**
-** Enter the mutexes in accending order by BtShared pointer address
+** Enter the mutexes in ascending order by BtShared pointer address
** to avoid the possibility of deadlock when two threads with
** two or more btrees in common both try to lock all their btrees
** at the same instant.
@@ -62878,6 +70072,7 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){
SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3 *db, int iDb, Schema *pSchema){
Btree *p;
assert( db!=0 );
+ if( db->pVfs==0 && db->nDb==0 ) return 1;
if( pSchema ) iDb = sqlite3SchemaToIndex(db, pSchema);
assert( iDb>=0 && iDb<db->nDb );
if( !sqlite3_mutex_held(db->mutex) ) return 0;
@@ -62915,10 +70110,10 @@ SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3 *db){
#ifndef SQLITE_OMIT_INCRBLOB
/*
-** Enter a mutex on a Btree given a cursor owned by that Btree.
+** Enter a mutex on a Btree given a cursor owned by that Btree.
**
-** These entry points are used by incremental I/O only. Enter() is required
-** any time OMIT_SHARED_CACHE is not defined, regardless of whether or not
+** These entry points are used by incremental I/O only. Enter() is required
+** any time OMIT_SHARED_CACHE is not defined, regardless of whether or not
** the build is threadsafe. Leave() is only required by threadsafe builds.
*/
SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor *pCur){
@@ -62988,7 +70183,7 @@ int sqlite3BtreeTrace=1; /* True to enable tracing */
#define BTALLOC_LE 2 /* Allocate any page <= the parameter */
/*
-** Macro IfNotOmitAV(x) returns (x) if SQLITE_OMIT_AUTOVACUUM is not
+** Macro IfNotOmitAV(x) returns (x) if SQLITE_OMIT_AUTOVACUUM is not
** defined, or 0 if it is. For example:
**
** bIncrVacuum = IfNotOmitAV(pBtShared->incrVacuum);
@@ -63003,10 +70198,10 @@ int sqlite3BtreeTrace=1; /* True to enable tracing */
/*
** A list of BtShared objects that are eligible for participation
** in shared cache. This variable has file scope during normal builds,
-** but the test harness needs to access it so we make it global for
+** but the test harness needs to access it so we make it global for
** test builds.
**
-** Access to this variable is protected by SQLITE_MUTEX_STATIC_MASTER.
+** Access to this variable is protected by SQLITE_MUTEX_STATIC_MAIN.
*/
#ifdef SQLITE_TEST
SQLITE_PRIVATE BtShared *SQLITE_WSD sqlite3SharedCacheList = 0;
@@ -63038,7 +70233,7 @@ SQLITE_API int sqlite3_enable_shared_cache(int enable){
** manipulate entries in the BtShared.pLock linked list used to store
** shared-cache table level locks. If the library is compiled with the
** shared-cache feature disabled, then there is only ever one user
- ** of each BtShared structure and so this locking is not necessary.
+ ** of each BtShared structure and so this locking is not necessary.
** So define the lock related functions as no-ops.
*/
#define querySharedCacheTableLock(a,b,c) SQLITE_OK
@@ -63049,6 +70244,17 @@ SQLITE_API int sqlite3_enable_shared_cache(int enable){
#define hasReadConflicts(a, b) 0
#endif
+#ifdef SQLITE_DEBUG
+/*
+** Return and reset the seek counter for a Btree object.
+*/
+SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){
+ u64 n = pBt->nSeek;
+ pBt->nSeek = 0;
+ return n;
+}
+#endif
+
/*
** Implementation of the SQLITE_CORRUPT_PAGE() macro. Takes a single
** (MemPage*) as an argument. The (MemPage*) must not be NULL.
@@ -63062,8 +70268,8 @@ SQLITE_API int sqlite3_enable_shared_cache(int enable){
int corruptPageError(int lineno, MemPage *p){
char *zMsg;
sqlite3BeginBenignMalloc();
- zMsg = sqlite3_mprintf("database corruption page %d of %s",
- (int)p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0)
+ zMsg = sqlite3_mprintf("database corruption page %u of %s",
+ p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0)
);
sqlite3EndBenignMalloc();
if( zMsg ){
@@ -63083,15 +70289,15 @@ int corruptPageError(int lineno, MemPage *p){
/*
**** This function is only used as part of an assert() statement. ***
**
-** Check to see if pBtree holds the required locks to read or write to the
+** Check to see if pBtree holds the required locks to read or write to the
** table with root page iRoot. Return 1 if it does and 0 if not.
**
-** For example, when writing to a table with root-page iRoot via
+** For example, when writing to a table with root-page iRoot via
** Btree connection pBtree:
**
** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) );
**
-** When writing to an index that resides in a sharable database, the
+** When writing to an index that resides in a sharable database, the
** caller should have first obtained a lock specifying the root page of
** the corresponding table. This makes things a bit more complicated,
** as this module treats each table as a separate structure. To determine
@@ -63113,7 +70319,7 @@ static int hasSharedCacheTableLock(
BtLock *pLock;
/* If this database is not shareable, or if the client is reading
- ** and has the read-uncommitted flag set, then no lock is required.
+ ** and has the read-uncommitted flag set, then no lock is required.
** Return true immediately.
*/
if( (pBtree->sharable==0)
@@ -63137,29 +70343,31 @@ static int hasSharedCacheTableLock(
** table. */
if( isIndex ){
HashElem *p;
+ int bSeen = 0;
for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){
Index *pIdx = (Index *)sqliteHashData(p);
- if( pIdx->tnum==(int)iRoot ){
- if( iTab ){
+ if( pIdx->tnum==iRoot ){
+ if( bSeen ){
/* Two or more indexes share the same root page. There must
** be imposter tables. So just return true. The assert is not
** useful in that case. */
return 1;
}
iTab = pIdx->pTable->tnum;
+ bSeen = 1;
}
}
}else{
iTab = iRoot;
}
- /* Search for the required lock. Either a write-lock on root-page iTab, a
+ /* Search for the required lock. Either a write-lock on root-page iTab, a
** write-lock on the schema table, or (if the client is reading) a
** read-lock on iTab will suffice. Return 1 if any of these are found. */
for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){
- if( pLock->pBtree==pBtree
+ if( pLock->pBtree==pBtree
&& (pLock->iTable==iTab || (pLock->eLock==WRITE_LOCK && pLock->iTable==1))
- && pLock->eLock>=eLockType
+ && pLock->eLock>=eLockType
){
return 1;
}
@@ -63192,7 +70400,7 @@ static int hasSharedCacheTableLock(
static int hasReadConflicts(Btree *pBtree, Pgno iRoot){
BtCursor *p;
for(p=pBtree->pBt->pCursor; p; p=p->pNext){
- if( p->pgnoRoot==iRoot
+ if( p->pgnoRoot==iRoot
&& p->pBtree!=pBtree
&& 0==(p->pBtree->db->flags & SQLITE_ReadUncommit)
){
@@ -63204,7 +70412,7 @@ static int hasReadConflicts(Btree *pBtree, Pgno iRoot){
#endif /* #ifdef SQLITE_DEBUG */
/*
-** Query to see if Btree handle p may obtain a lock of type eLock
+** Query to see if Btree handle p may obtain a lock of type eLock
** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return
** SQLITE_OK if the lock may be obtained (by calling
** setSharedCacheTableLock()), or SQLITE_LOCKED if not.
@@ -63217,14 +70425,14 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
assert( eLock==READ_LOCK || eLock==WRITE_LOCK );
assert( p->db!=0 );
assert( !(p->db->flags&SQLITE_ReadUncommit)||eLock==WRITE_LOCK||iTab==1 );
-
+
/* If requesting a write-lock, then the Btree must have an open write
- ** transaction on this file. And, obviously, for this to be so there
+ ** transaction on this file. And, obviously, for this to be so there
** must be an open write transaction on the file itself.
*/
assert( eLock==READ_LOCK || (p==pBt->pWriter && p->inTrans==TRANS_WRITE) );
assert( eLock==READ_LOCK || pBt->inTransaction==TRANS_WRITE );
-
+
/* This routine is a no-op if the shared-cache is not enabled */
if( !p->sharable ){
return SQLITE_OK;
@@ -63239,7 +70447,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
}
for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
- /* The condition (pIter->eLock!=eLock) in the following if(...)
+ /* The condition (pIter->eLock!=eLock) in the following if(...)
** statement is a simplification of:
**
** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK)
@@ -63266,7 +70474,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
#ifndef SQLITE_OMIT_SHARED_CACHE
/*
** Add a lock on the table with root-page iTable to the shared-btree used
-** by Btree handle p. Parameter eLock must be either READ_LOCK or
+** by Btree handle p. Parameter eLock must be either READ_LOCK or
** WRITE_LOCK.
**
** This function assumes the following:
@@ -63278,7 +70486,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
** with the requested lock (i.e. querySharedCacheTableLock() has
** already been called and returned SQLITE_OK).
**
-** SQLITE_OK is returned if the lock is added successfully. SQLITE_NOMEM
+** SQLITE_OK is returned if the lock is added successfully. SQLITE_NOMEM
** is returned if a malloc attempt fails.
*/
static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
@@ -63292,11 +70500,11 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
/* A connection with the read-uncommitted flag set will never try to
** obtain a read-lock using this function. The only read-lock obtained
- ** by a connection in read-uncommitted mode is on the sqlite_master
+ ** by a connection in read-uncommitted mode is on the sqlite_schema
** table, and that lock is obtained in BtreeBeginTrans(). */
assert( 0==(p->db->flags&SQLITE_ReadUncommit) || eLock==WRITE_LOCK );
- /* This function should only be called on a sharable b-tree after it
+ /* This function should only be called on a sharable b-tree after it
** has been determined that no other b-tree holds a conflicting lock. */
assert( p->sharable );
assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) );
@@ -63341,7 +70549,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
** Release all the table locks (locks obtained via calls to
** the setSharedCacheTableLock() procedure) held by Btree object p.
**
-** This function assumes that Btree p has an open read or write
+** This function assumes that Btree p has an open read or write
** transaction. If it does not, then the BTS_PENDING flag
** may be incorrectly cleared.
*/
@@ -63373,7 +70581,7 @@ static void clearAllSharedCacheTableLocks(Btree *p){
pBt->pWriter = 0;
pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING);
}else if( pBt->nTransaction==2 ){
- /* This function is called when Btree p is concluding its
+ /* This function is called when Btree p is concluding its
** transaction. If there currently exists a writer, and p is not
** that writer, then the number of locks held by connections other
** than the writer must be about to drop to zero. In this case
@@ -63419,7 +70627,7 @@ static int cursorHoldsMutex(BtCursor *p){
}
/* Verify that the cursor and the BtShared agree about what is the current
-** database connetion. This is important in shared-cache mode. If the database
+** database connetion. This is important in shared-cache mode. If the database
** connection pointers get out-of-sync, it is possible for routines like
** btreeInitPage() to reference an stale connection pointer that references a
** a connection that has already closed. This routine is used inside assert()
@@ -63471,7 +70679,7 @@ static void invalidateIncrblobCursors(
int isClearTable /* True if all rows are being deleted */
){
BtCursor *p;
- if( pBtree->hasIncrblobCur==0 ) return;
+ assert( pBtree->hasIncrblobCur );
assert( sqlite3BtreeHoldsMutex(pBtree) );
pBtree->hasIncrblobCur = 0;
for(p=pBtree->pBt->pCursor; p; p=p->pNext){
@@ -63490,8 +70698,8 @@ static void invalidateIncrblobCursors(
#endif /* SQLITE_OMIT_INCRBLOB */
/*
-** Set bit pgno of the BtShared.pHasContent bitvec. This is called
-** when a page that previously contained data becomes a free-list leaf
+** Set bit pgno of the BtShared.pHasContent bitvec. This is called
+** when a page that previously contained data becomes a free-list leaf
** page.
**
** The BtShared.pHasContent bitvec exists to work around an obscure
@@ -63517,7 +70725,7 @@ static void invalidateIncrblobCursors(
** may be lost. In the event of a rollback, it may not be possible
** to restore the database to its original configuration.
**
-** The solution is the BtShared.pHasContent bitvec. Whenever a page is
+** The solution is the BtShared.pHasContent bitvec. Whenever a page is
** moved to become a free-list leaf page, the corresponding bit is
** set in the bitvec. Whenever a leaf page is extracted from the free-list,
** optimization 2 above is omitted if the corresponding bit is already
@@ -63548,7 +70756,7 @@ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){
*/
static int btreeGetHasContent(BtShared *pBt, Pgno pgno){
Bitvec *p = pBt->pHasContent;
- return (p && (pgno>sqlite3BitvecSize(p) || sqlite3BitvecTest(p, pgno)));
+ return p && (pgno>sqlite3BitvecSize(p) || sqlite3BitvecTestNotNull(p, pgno));
}
/*
@@ -63578,13 +70786,13 @@ static void btreeReleaseAllCursorPages(BtCursor *pCur){
** The cursor passed as the only argument must point to a valid entry
** when this function is called (i.e. have eState==CURSOR_VALID). This
** function saves the current cursor key in variables pCur->nKey and
-** pCur->pKey. SQLITE_OK is returned if successful or an SQLite error
+** pCur->pKey. SQLITE_OK is returned if successful or an SQLite error
** code otherwise.
**
** If the cursor is open on an intkey table, then the integer key
** (the rowid) is stored in pCur->nKey and pCur->pKey is left set to
-** NULL. If the cursor is open on a non-intkey table, then pCur->pKey is
-** set to point to a malloced buffer pCur->nKey bytes in size containing
+** NULL. If the cursor is open on a non-intkey table, then pCur->pKey is
+** set to point to a malloced buffer pCur->nKey bytes in size containing
** the key.
*/
static int saveCursorKey(BtCursor *pCur){
@@ -63597,13 +70805,19 @@ static int saveCursorKey(BtCursor *pCur){
/* Only the rowid is required for a table btree */
pCur->nKey = sqlite3BtreeIntegerKey(pCur);
}else{
- /* For an index btree, save the complete key content */
+ /* For an index btree, save the complete key content. It is possible
+ ** that the current key is corrupt. In that case, it is possible that
+ ** the sqlite3VdbeRecordUnpack() function may overread the buffer by
+ ** up to the size of 1 varint plus 1 8-byte value when the cursor
+ ** position is restored. Hence the 17 bytes of padding allocated
+ ** below. */
void *pKey;
pCur->nKey = sqlite3BtreePayloadSize(pCur);
- pKey = sqlite3Malloc( pCur->nKey );
+ pKey = sqlite3Malloc( pCur->nKey + 9 + 8 );
if( pKey ){
rc = sqlite3BtreePayload(pCur, 0, (int)pCur->nKey, pKey);
if( rc==SQLITE_OK ){
+ memset(((u8*)pKey)+pCur->nKey, 0, 9+8);
pCur->pKey = pKey;
}else{
sqlite3_free(pKey);
@@ -63617,11 +70831,11 @@ static int saveCursorKey(BtCursor *pCur){
}
/*
-** Save the current cursor position in the variables BtCursor.nKey
+** Save the current cursor position in the variables BtCursor.nKey
** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
**
** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID)
-** prior to calling this routine.
+** prior to calling this routine.
*/
static int saveCursorPosition(BtCursor *pCur){
int rc;
@@ -63630,6 +70844,9 @@ static int saveCursorPosition(BtCursor *pCur){
assert( 0==pCur->pKey );
assert( cursorHoldsMutex(pCur) );
+ if( pCur->curFlags & BTCF_Pinned ){
+ return SQLITE_CONSTRAINT_PINNED;
+ }
if( pCur->eState==CURSOR_SKIPNEXT ){
pCur->eState = CURSOR_VALID;
}else{
@@ -63657,7 +70874,7 @@ static int SQLITE_NOINLINE saveCursorsOnList(BtCursor*,Pgno,BtCursor*);
** routine is called just before cursor pExcept is used to modify the
** table, for example in BtreeDelete() or BtreeInsert().
**
-** If there are two or more cursors on the same btree, then all such
+** If there are two or more cursors on the same btree, then all such
** cursors should have their BTCF_Multiple flag set. The btreeCursor()
** routine enforces that rule. This routine only needs to be called in
** the uncommon case when pExpect has the BTCF_Multiple flag set.
@@ -63722,7 +70939,7 @@ SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *pCur){
/*
** In this version of BtreeMoveto, pKey is a packed index record
** such as is generated by the OP_MakeRecord opcode. Unpack the
-** record and then call BtreeMovetoUnpacked() to do the work.
+** record and then call sqlite3BtreeIndexMoveto() to do the work.
*/
static int btreeMoveto(
BtCursor *pCur, /* Cursor open on the btree to be searched */
@@ -63735,47 +70952,50 @@ static int btreeMoveto(
UnpackedRecord *pIdxKey; /* Unpacked index key */
if( pKey ){
+ KeyInfo *pKeyInfo = pCur->pKeyInfo;
assert( nKey==(i64)(int)nKey );
- pIdxKey = sqlite3VdbeAllocUnpackedRecord(pCur->pKeyInfo);
+ pIdxKey = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT;
- sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey);
- if( pIdxKey->nField==0 ){
+ sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey);
+ if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){
rc = SQLITE_CORRUPT_BKPT;
- goto moveto_done;
+ }else{
+ rc = sqlite3BtreeIndexMoveto(pCur, pIdxKey, pRes);
}
+ sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey);
}else{
pIdxKey = 0;
- }
- rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes);
-moveto_done:
- if( pIdxKey ){
- sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey);
+ rc = sqlite3BtreeTableMoveto(pCur, nKey, bias, pRes);
}
return rc;
}
/*
** Restore the cursor to the position it was in (or as close to as possible)
-** when saveCursorPosition() was called. Note that this call deletes the
+** when saveCursorPosition() was called. Note that this call deletes the
** saved position info stored by saveCursorPosition(), so there can be
-** at most one effective restoreCursorPosition() call after each
+** at most one effective restoreCursorPosition() call after each
** saveCursorPosition().
*/
static int btreeRestoreCursorPosition(BtCursor *pCur){
int rc;
- int skipNext;
+ int skipNext = 0;
assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState>=CURSOR_REQUIRESEEK );
if( pCur->eState==CURSOR_FAULT ){
return pCur->skipNext;
}
pCur->eState = CURSOR_INVALID;
- rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext);
+ if( sqlite3FaultSim(410) ){
+ rc = SQLITE_IOERR;
+ }else{
+ rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext);
+ }
if( rc==SQLITE_OK ){
sqlite3_free(pCur->pKey);
pCur->pKey = 0;
assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID );
- pCur->skipNext |= skipNext;
+ if( skipNext ) pCur->skipNext = skipNext;
if( pCur->skipNext && pCur->eState==CURSOR_VALID ){
pCur->eState = CURSOR_SKIPNEXT;
}
@@ -63822,7 +71042,7 @@ SQLITE_PRIVATE BtCursor *sqlite3BtreeFakeValidCursor(void){
/*
** This routine restores a cursor back to its original position after it
** has been moved by some outside activity (such as a btree rebalance or
-** a row having been deleted out from under the cursor).
+** a row having been deleted out from under the cursor).
**
** On success, the *pDifferentRow parameter is false if the cursor is left
** pointing at exactly the same row. *pDifferntRow is the row the cursor
@@ -63845,7 +71065,6 @@ SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow)
if( pCur->eState!=CURSOR_VALID ){
*pDifferentRow = 1;
}else{
- assert( pCur->skipNext==0 );
*pDifferentRow = 0;
}
return SQLITE_OK;
@@ -63859,8 +71078,25 @@ SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow)
*/
SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){
/* Used only by system that substitute their own storage engine */
+#ifdef SQLITE_DEBUG
+ if( ALWAYS(eHintType==BTREE_HINT_RANGE) ){
+ va_list ap;
+ Expr *pExpr;
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = sqlite3CursorRangeHintExprCheck;
+ va_start(ap, eHintType);
+ pExpr = va_arg(ap, Expr*);
+ w.u.aMem = va_arg(ap, Mem*);
+ va_end(ap);
+ assert( pExpr!=0 );
+ assert( w.u.aMem!=0 );
+ sqlite3WalkExpr(&w, pExpr);
+ }
+#endif /* SQLITE_DEBUG */
}
-#endif
+#endif /* SQLITE_ENABLE_CURSOR_HINTS */
+
/*
** Provide flag hints to the cursor.
@@ -63888,7 +71124,7 @@ static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
if( pgno<2 ) return 0;
nPagesPerMapPage = (pBt->usableSize/5)+1;
iPtrMap = (pgno-2)/nPagesPerMapPage;
- ret = (iPtrMap*nPagesPerMapPage) + 2;
+ ret = (iPtrMap*nPagesPerMapPage) + 2;
if( ret==PENDING_BYTE_PAGE(pBt) ){
ret++;
}
@@ -63915,7 +71151,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){
if( *pRC ) return;
assert( sqlite3_mutex_held(pBt->mutex) );
- /* The master-journal page number must never be used as a pointer map page */
+ /* The super-journal page number must never be used as a pointer map page */
assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) );
assert( pBt->autoVacuum );
@@ -63929,6 +71165,13 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){
*pRC = rc;
return;
}
+ if( ((char*)sqlite3PagerGetExtra(pDbPage))[0]!=0 ){
+ /* The first byte of the extra data is the MemPage.isInit byte.
+ ** If that byte is set, it means this page is also being used
+ ** as a btree page. */
+ *pRC = SQLITE_CORRUPT_BKPT;
+ goto ptrmap_exit;
+ }
offset = PTRMAP_PTROFFSET(iPtrmap, key);
if( offset<0 ){
*pRC = SQLITE_CORRUPT_BKPT;
@@ -63938,7 +71181,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){
pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage);
if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){
- TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent));
+ TRACE(("PTRMAP_UPDATE: %u->(%u,%u)\n", key, eType, parent));
*pRC= rc = sqlite3PagerWrite(pDbPage);
if( rc==SQLITE_OK ){
pPtrmap[offset] = eType;
@@ -63991,7 +71234,7 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
#else /* if defined SQLITE_OMIT_AUTOVACUUM */
#define ptrmapPut(w,x,y,z,rc)
#define ptrmapGet(w,x,y,z) SQLITE_OK
- #define ptrmapPutOvflPtr(x, y, rc)
+ #define ptrmapPutOvflPtr(x, y, z, rc)
#endif
/*
@@ -64048,6 +71291,24 @@ static SQLITE_NOINLINE void btreeParseCellAdjustSizeForOverflow(
}
/*
+** Given a record with nPayload bytes of payload stored within btree
+** page pPage, return the number of bytes of payload stored locally.
+*/
+static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){
+ int maxLocal; /* Maximum amount of payload held locally */
+ maxLocal = pPage->maxLocal;
+ if( nPayload<=maxLocal ){
+ return nPayload;
+ }else{
+ int minLocal; /* Minimum amount of payload held locally */
+ int surplus; /* Overflow payload available for local storage */
+ minLocal = pPage->minLocal;
+ surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize-4);
+ return ( surplus <= maxLocal ) ? surplus : minLocal;
+ }
+}
+
+/*
** The following routines are implementations of the MemPage.xParseCell()
** method.
**
@@ -64113,19 +71374,37 @@ static void btreeParseCellPtr(
**
** pIter += getVarint(pIter, (u64*)&pInfo->nKey);
**
- ** The code is inlined to avoid a function call.
+ ** The code is inlined and the loop is unrolled for performance.
+ ** This routine is a high-runner.
*/
iKey = *pIter;
if( iKey>=0x80 ){
- u8 *pEnd = &pIter[7];
- iKey &= 0x7f;
- while(1){
- iKey = (iKey<<7) | (*++pIter & 0x7f);
- if( (*pIter)<0x80 ) break;
- if( pIter>=pEnd ){
- iKey = (iKey<<8) | *++pIter;
- break;
+ u8 x;
+ iKey = (iKey<<7) ^ (x = *++pIter);
+ if( x>=0x80 ){
+ iKey = (iKey<<7) ^ (x = *++pIter);
+ if( x>=0x80 ){
+ iKey = (iKey<<7) ^ 0x10204000 ^ (x = *++pIter);
+ if( x>=0x80 ){
+ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter);
+ if( x>=0x80 ){
+ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter);
+ if( x>=0x80 ){
+ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter);
+ if( x>=0x80 ){
+ iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter);
+ if( x>=0x80 ){
+ iKey = (iKey<<8) ^ 0x8000 ^ (*++pIter);
+ }
+ }
+ }
+ }
+ }
+ }else{
+ iKey ^= 0x204000;
}
+ }else{
+ iKey ^= 0x4000;
}
}
pIter++;
@@ -64134,7 +71413,7 @@ static void btreeParseCellPtr(
pInfo->nPayload = nPayload;
pInfo->pPayload = pIter;
testcase( nPayload==pPage->maxLocal );
- testcase( nPayload==pPage->maxLocal+1 );
+ testcase( nPayload==(u32)pPage->maxLocal+1 );
if( nPayload<=pPage->maxLocal ){
/* This is the (easy) common case where the entire payload fits
** on the local page. No overflow is required.
@@ -64171,7 +71450,7 @@ static void btreeParseCellPtrIndex(
pInfo->nPayload = nPayload;
pInfo->pPayload = pIter;
testcase( nPayload==pPage->maxLocal );
- testcase( nPayload==pPage->maxLocal+1 );
+ testcase( nPayload==(u32)pPage->maxLocal+1 );
if( nPayload<=pPage->maxLocal ){
/* This is the (easy) common case where the entire payload fits
** on the local page. No overflow is required.
@@ -64201,10 +71480,12 @@ static void btreeParseCell(
** the space used by the cell pointer.
**
** cellSizePtrNoPayload() => table internal nodes
-** cellSizePtr() => all index nodes & table leaf nodes
+** cellSizePtrTableLeaf() => table leaf nodes
+** cellSizePtr() => index internal nodes
+** cellSizeIdxLeaf() => index leaf nodes
*/
static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
- u8 *pIter = pCell + pPage->childPtrSize; /* For looping over bytes of pCell */
+ u8 *pIter = pCell + 4; /* For looping over bytes of pCell */
u8 *pEnd; /* End mark for a varint */
u32 nSize; /* Size value to return */
@@ -64217,6 +71498,7 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
pPage->xParseCell(pPage, pCell, &debuginfo);
#endif
+ assert( pPage->childPtrSize==4 );
nSize = *pIter;
if( nSize>=0x80 ){
pEnd = &pIter[8];
@@ -64226,15 +71508,50 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
}while( *(pIter)>=0x80 && pIter<pEnd );
}
pIter++;
- if( pPage->intKey ){
- /* pIter now points at the 64-bit integer key value, a variable length
- ** integer. The following block moves pIter to point at the first byte
- ** past the end of the key value. */
- pEnd = &pIter[9];
- while( (*pIter++)&0x80 && pIter<pEnd );
+ testcase( nSize==pPage->maxLocal );
+ testcase( nSize==(u32)pPage->maxLocal+1 );
+ if( nSize<=pPage->maxLocal ){
+ nSize += (u32)(pIter - pCell);
+ assert( nSize>4 );
+ }else{
+ int minLocal = pPage->minLocal;
+ nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4);
+ testcase( nSize==pPage->maxLocal );
+ testcase( nSize==(u32)pPage->maxLocal+1 );
+ if( nSize>pPage->maxLocal ){
+ nSize = minLocal;
+ }
+ nSize += 4 + (u16)(pIter - pCell);
+ }
+ assert( nSize==debuginfo.nSize || CORRUPT_DB );
+ return (u16)nSize;
+}
+static u16 cellSizePtrIdxLeaf(MemPage *pPage, u8 *pCell){
+ u8 *pIter = pCell; /* For looping over bytes of pCell */
+ u8 *pEnd; /* End mark for a varint */
+ u32 nSize; /* Size value to return */
+
+#ifdef SQLITE_DEBUG
+ /* The value returned by this function should always be the same as
+ ** the (CellInfo.nSize) value found by doing a full parse of the
+ ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of
+ ** this function verifies that this invariant is not violated. */
+ CellInfo debuginfo;
+ pPage->xParseCell(pPage, pCell, &debuginfo);
+#endif
+
+ assert( pPage->childPtrSize==0 );
+ nSize = *pIter;
+ if( nSize>=0x80 ){
+ pEnd = &pIter[8];
+ nSize &= 0x7f;
+ do{
+ nSize = (nSize<<7) | (*++pIter & 0x7f);
+ }while( *(pIter)>=0x80 && pIter<pEnd );
}
+ pIter++;
testcase( nSize==pPage->maxLocal );
- testcase( nSize==pPage->maxLocal+1 );
+ testcase( nSize==(u32)pPage->maxLocal+1 );
if( nSize<=pPage->maxLocal ){
nSize += (u32)(pIter - pCell);
if( nSize<4 ) nSize = 4;
@@ -64242,7 +71559,7 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
int minLocal = pPage->minLocal;
nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4);
testcase( nSize==pPage->maxLocal );
- testcase( nSize==pPage->maxLocal+1 );
+ testcase( nSize==(u32)pPage->maxLocal+1 );
if( nSize>pPage->maxLocal ){
nSize = minLocal;
}
@@ -64272,6 +71589,58 @@ static u16 cellSizePtrNoPayload(MemPage *pPage, u8 *pCell){
assert( debuginfo.nSize==(u16)(pIter - pCell) || CORRUPT_DB );
return (u16)(pIter - pCell);
}
+static u16 cellSizePtrTableLeaf(MemPage *pPage, u8 *pCell){
+ u8 *pIter = pCell; /* For looping over bytes of pCell */
+ u8 *pEnd; /* End mark for a varint */
+ u32 nSize; /* Size value to return */
+
+#ifdef SQLITE_DEBUG
+ /* The value returned by this function should always be the same as
+ ** the (CellInfo.nSize) value found by doing a full parse of the
+ ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of
+ ** this function verifies that this invariant is not violated. */
+ CellInfo debuginfo;
+ pPage->xParseCell(pPage, pCell, &debuginfo);
+#endif
+
+ nSize = *pIter;
+ if( nSize>=0x80 ){
+ pEnd = &pIter[8];
+ nSize &= 0x7f;
+ do{
+ nSize = (nSize<<7) | (*++pIter & 0x7f);
+ }while( *(pIter)>=0x80 && pIter<pEnd );
+ }
+ pIter++;
+ /* pIter now points at the 64-bit integer key value, a variable length
+ ** integer. The following block moves pIter to point at the first byte
+ ** past the end of the key value. */
+ if( (*pIter++)&0x80
+ && (*pIter++)&0x80
+ && (*pIter++)&0x80
+ && (*pIter++)&0x80
+ && (*pIter++)&0x80
+ && (*pIter++)&0x80
+ && (*pIter++)&0x80
+ && (*pIter++)&0x80 ){ pIter++; }
+ testcase( nSize==pPage->maxLocal );
+ testcase( nSize==(u32)pPage->maxLocal+1 );
+ if( nSize<=pPage->maxLocal ){
+ nSize += (u32)(pIter - pCell);
+ if( nSize<4 ) nSize = 4;
+ }else{
+ int minLocal = pPage->minLocal;
+ nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4);
+ testcase( nSize==pPage->maxLocal );
+ testcase( nSize==(u32)pPage->maxLocal+1 );
+ if( nSize>pPage->maxLocal ){
+ nSize = minLocal;
+ }
+ nSize += 4 + (u16)(pIter - pCell);
+ }
+ assert( nSize==debuginfo.nSize || CORRUPT_DB );
+ return (u16)nSize;
+}
#ifdef SQLITE_DEBUG
@@ -64284,17 +71653,24 @@ static u16 cellSize(MemPage *pPage, int iCell){
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
-** If the cell pCell, part of page pPage contains a pointer
-** to an overflow page, insert an entry into the pointer-map
-** for the overflow page.
+** The cell pCell is currently part of page pSrc but will ultimately be part
+** of pPage. (pSrc and pPage are often the same.) If pCell contains a
+** pointer to an overflow page, insert an entry into the pointer-map for
+** the overflow page that will be valid after pCell has been moved to pPage.
*/
-static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){
+static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){
CellInfo info;
if( *pRC ) return;
assert( pCell!=0 );
pPage->xParseCell(pPage, pCell, &info);
if( info.nLocal<info.nPayload ){
- Pgno ovfl = get4byte(&pCell[info.nSize-4]);
+ Pgno ovfl;
+ if( SQLITE_OVERFLOW(pSrc->aDataEnd, pCell, pCell+info.nLocal) ){
+ testcase( pSrc!=pPage );
+ *pRC = SQLITE_CORRUPT_BKPT;
+ return;
+ }
+ ovfl = get4byte(&pCell[info.nSize-4]);
ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC);
}
}
@@ -64327,41 +71703,32 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
unsigned char *src; /* Source of content */
int iCellFirst; /* First allowable cell index */
int iCellLast; /* Last possible cell index */
+ int iCellStart; /* First cell offset in input */
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( pPage->pBt!=0 );
assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE );
assert( pPage->nOverflow==0 );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- temp = 0;
- src = data = pPage->aData;
+ data = pPage->aData;
hdr = pPage->hdrOffset;
cellOffset = pPage->cellOffset;
nCell = pPage->nCell;
- assert( nCell==get2byte(&data[hdr+3]) );
+ assert( nCell==get2byte(&data[hdr+3]) || CORRUPT_DB );
iCellFirst = cellOffset + 2*nCell;
usableSize = pPage->pBt->usableSize;
/* This block handles pages with two or fewer free blocks and nMaxFrag
** or fewer fragmented bytes. In this case it is faster to move the
** two (or one) blocks of cells using memmove() and add the required
- ** offsets to each pointer in the cell-pointer array than it is to
+ ** offsets to each pointer in the cell-pointer array than it is to
** reconstruct the entire page. */
if( (int)data[hdr+7]<=nMaxFrag ){
int iFree = get2byte(&data[hdr+1]);
+ if( iFree>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage);
if( iFree ){
int iFree2 = get2byte(&data[iFree]);
-
- /* pageFindSlot() has already verified that free blocks are sorted
- ** in order of offset within the page, and that no block extends
- ** past the end of the page. Provided the two free slots do not
- ** overlap, this guarantees that the memmove() calls below will not
- ** overwrite the usableSize byte buffer, even if the database page
- ** is corrupt. */
- assert( iFree2==0 || iFree2>iFree );
- assert( iFree+get2byte(&data[iFree+2]) <= usableSize );
- assert( iFree2==0 || iFree2+get2byte(&data[iFree2+2]) <= usableSize );
-
+ if( iFree2>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage);
if( 0==iFree2 || (data[iFree2]==0 && data[iFree2+1]==0) ){
u8 *pEnd = &data[cellOffset + nCell*2];
u8 *pAddr;
@@ -64372,12 +71739,15 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
return SQLITE_CORRUPT_PAGE(pPage);
}
if( iFree2 ){
- assert( iFree+sz<=iFree2 ); /* Verified by pageFindSlot() */
+ if( iFree+sz>iFree2 ) return SQLITE_CORRUPT_PAGE(pPage);
sz2 = get2byte(&data[iFree2+2]);
- assert( iFree+sz+sz2+iFree2-(iFree+sz) <= usableSize );
+ if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage);
memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz));
sz += sz2;
+ }else if( iFree+sz>usableSize ){
+ return SQLITE_CORRUPT_PAGE(pPage);
}
+
cbrk = top+sz;
assert( cbrk+(iFree-top) <= usableSize );
memmove(&data[cbrk], &data[top], iFree-top);
@@ -64393,41 +71763,40 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
cbrk = usableSize;
iCellLast = usableSize - 4;
- for(i=0; i<nCell; i++){
- u8 *pAddr; /* The i-th cell pointer */
- pAddr = &data[cellOffset + i*2];
- pc = get2byte(pAddr);
- testcase( pc==iCellFirst );
- testcase( pc==iCellLast );
- /* These conditions have already been verified in btreeInitPage()
- ** if PRAGMA cell_size_check=ON.
- */
- if( pc<iCellFirst || pc>iCellLast ){
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- assert( pc>=iCellFirst && pc<=iCellLast );
- size = pPage->xCellSize(pPage, &src[pc]);
- cbrk -= size;
- if( cbrk<iCellFirst || pc+size>usableSize ){
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- assert( cbrk+size<=usableSize && cbrk>=iCellFirst );
- testcase( cbrk+size==usableSize );
- testcase( pc+size==usableSize );
- put2byte(pAddr, cbrk);
- if( temp==0 ){
- int x;
- if( cbrk==pc ) continue;
- temp = sqlite3PagerTempSpace(pPage->pBt->pPager);
- x = get2byte(&data[hdr+5]);
- memcpy(&temp[x], &data[x], (cbrk+size) - x);
- src = temp;
+ iCellStart = get2byte(&data[hdr+5]);
+ if( nCell>0 ){
+ temp = sqlite3PagerTempSpace(pPage->pBt->pPager);
+ memcpy(temp, data, usableSize);
+ src = temp;
+ for(i=0; i<nCell; i++){
+ u8 *pAddr; /* The i-th cell pointer */
+ pAddr = &data[cellOffset + i*2];
+ pc = get2byte(pAddr);
+ testcase( pc==iCellFirst );
+ testcase( pc==iCellLast );
+ /* These conditions have already been verified in btreeInitPage()
+ ** if PRAGMA cell_size_check=ON.
+ */
+ if( pc>iCellLast ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ assert( pc>=0 && pc<=iCellLast );
+ size = pPage->xCellSize(pPage, &src[pc]);
+ cbrk -= size;
+ if( cbrk<iCellStart || pc+size>usableSize ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ assert( cbrk+size<=usableSize && cbrk>=iCellStart );
+ testcase( cbrk+size==usableSize );
+ testcase( pc+size==usableSize );
+ put2byte(pAddr, cbrk);
+ memcpy(&data[cbrk], &src[pc], size);
}
- memcpy(&data[cbrk], &src[pc], size);
}
data[hdr+7] = 0;
- defragment_out:
+defragment_out:
+ assert( pPage->nFree>=0 );
if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){
return SQLITE_CORRUPT_PAGE(pPage);
}
@@ -64455,27 +71824,26 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
** causes the fragmentation count to exceed 60.
*/
static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
- const int hdr = pPg->hdrOffset;
- u8 * const aData = pPg->aData;
- int iAddr = hdr + 1;
- int pc = get2byte(&aData[iAddr]);
- int x;
- int usableSize = pPg->pBt->usableSize;
- int size; /* Size of the free slot */
+ const int hdr = pPg->hdrOffset; /* Offset to page header */
+ u8 * const aData = pPg->aData; /* Page data */
+ int iAddr = hdr + 1; /* Address of ptr to pc */
+ u8 *pTmp = &aData[iAddr]; /* Temporary ptr into aData[] */
+ int pc = get2byte(pTmp); /* Address of a free slot */
+ int x; /* Excess size of the slot */
+ int maxPC = pPg->pBt->usableSize - nByte; /* Max address for a usable slot */
+ int size; /* Size of the free slot */
assert( pc>0 );
- while( pc<=usableSize-4 ){
+ while( pc<=maxPC ){
/* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each
** freeblock form a big-endian integer which is the size of the freeblock
** in bytes, including the 4-byte header. */
- size = get2byte(&aData[pc+2]);
+ pTmp = &aData[pc+2];
+ size = get2byte(pTmp);
if( (x = size - nByte)>=0 ){
testcase( x==4 );
testcase( x==3 );
- if( size+pc > usableSize ){
- *pRc = SQLITE_CORRUPT_PAGE(pPg);
- return 0;
- }else if( x<4 ){
+ if( x<4 ){
/* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total
** number of bytes in fragments may not exceed 60. */
if( aData[hdr+7]>57 ) return 0;
@@ -64484,21 +71852,33 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
** fragmented bytes within the page. */
memcpy(&aData[iAddr], &aData[pc], 2);
aData[hdr+7] += (u8)x;
+ return &aData[pc];
+ }else if( x+pc > maxPC ){
+ /* This slot extends off the end of the usable part of the page */
+ *pRc = SQLITE_CORRUPT_PAGE(pPg);
+ return 0;
}else{
/* The slot remains on the free-list. Reduce its size to account
- ** for the portion used by the new allocation. */
+ ** for the portion used by the new allocation. */
put2byte(&aData[pc+2], x);
}
return &aData[pc + x];
}
iAddr = pc;
- pc = get2byte(&aData[pc]);
- if( pc<iAddr+size ) break;
+ pTmp = &aData[pc];
+ pc = get2byte(pTmp);
+ if( pc<=iAddr ){
+ if( pc ){
+ /* The next slot in the chain comes before the current slot */
+ *pRc = SQLITE_CORRUPT_PAGE(pPg);
+ }
+ return 0;
+ }
}
- if( pc ){
+ if( pc>maxPC+nByte-4 ){
+ /* The free slot chain extends off the end of the page */
*pRc = SQLITE_CORRUPT_PAGE(pPg);
}
-
return 0;
}
@@ -64515,13 +71895,14 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
** allocation is being made in order to insert a new cell, so we will
** also end up needing a new cell pointer.
*/
-static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
+static SQLITE_INLINE int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */
u8 * const data = pPage->aData; /* Local cache of pPage->aData */
int top; /* First byte of cell content area */
int rc = SQLITE_OK; /* Integer return code */
+ u8 *pTmp; /* Temp ptr into data[] */
int gap; /* First byte of gap between cell pointers and cell content */
-
+
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( pPage->pBt );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
@@ -64538,19 +71919,21 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
** then the cell content offset of an empty page wants to be 65536.
** However, that integer is too large to be stored in a 2-byte unsigned
** integer, so a value of 0 is used in its place. */
- top = get2byte(&data[hdr+5]);
- assert( top<=(int)pPage->pBt->usableSize ); /* Prevent by getAndInitPage() */
+ pTmp = &data[hdr+5];
+ top = get2byte(pTmp);
if( gap>top ){
if( top==0 && pPage->pBt->usableSize==65536 ){
top = 65536;
}else{
return SQLITE_CORRUPT_PAGE(pPage);
}
+ }else if( top>(int)pPage->pBt->usableSize ){
+ return SQLITE_CORRUPT_PAGE(pPage);
}
- /* If there is enough space between gap and top for one more cell pointer
- ** array entry offset, and if the freelist is not empty, then search the
- ** freelist looking for a free slot big enough to satisfy the request.
+ /* If there is enough space between gap and top for one more cell pointer,
+ ** and if the freelist is not empty, then search the
+ ** freelist looking for a slot big enough to satisfy the request.
*/
testcase( gap+2==top );
testcase( gap+1==top );
@@ -64558,9 +71941,14 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
if( (data[hdr+2] || data[hdr+1]) && gap+2<=top ){
u8 *pSpace = pageFindSlot(pPage, nByte, &rc);
if( pSpace ){
- assert( pSpace>=data && (pSpace - data)<65536 );
- *pIdx = (int)(pSpace - data);
- return SQLITE_OK;
+ int g2;
+ assert( pSpace+nByte<=data+pPage->pBt->usableSize );
+ *pIdx = g2 = (int)(pSpace-data);
+ if( g2<=gap ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }else{
+ return SQLITE_OK;
+ }
}else if( rc ){
return rc;
}
@@ -64572,6 +71960,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
testcase( gap+2+nByte==top );
if( gap+2+nByte>top ){
assert( pPage->nCell>0 || CORRUPT_DB );
+ assert( pPage->nFree>=0 );
rc = defragmentPage(pPage, MIN(4, pPage->nFree - (2+nByte)));
if( rc ) return rc;
top = get2byteNotZero(&data[hdr+5]);
@@ -64580,7 +71969,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
/* Allocate memory from the gap in between the cell pointer array
- ** and the cell content area. The btreeInitPage() call has already
+ ** and the cell content area. The btreeComputeFreeSpace() call has already
** validated the freelist. Given that the freelist is valid, there
** is no way that the allocation can extend off the end of the page.
** The assert() below verifies the previous sentence.
@@ -64599,9 +71988,9 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
**
** Adjacent freeblocks are coalesced.
**
-** Note that even though the freeblock list was checked by btreeInitPage(),
+** Even though the freeblock list was checked by btreeComputeFreeSpace(),
** that routine will not detect overlap between cells or freeblocks. Nor
-** does it detect cells or freeblocks that encrouch into the reserved bytes
+** does it detect cells or freeblocks that encroach into the reserved bytes
** at the end of the page. So do additional corruption checks inside this
** routine and return SQLITE_CORRUPT if any problems are found.
*/
@@ -64614,6 +72003,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
u16 x; /* Offset to cell content area */
u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */
unsigned char *data = pPage->aData; /* Page content */
+ u8 *pTmp; /* Temporary ptr into data[] */
assert( pPage->pBt!=0 );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
@@ -64621,9 +72011,9 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( iSize>=4 ); /* Minimum cell size is 4 */
- assert( iStart<=pPage->pBt->usableSize-4 );
+ assert( CORRUPT_DB || iStart<=pPage->pBt->usableSize-4 );
- /* The list of freeblocks must be in ascending order. Find the
+ /* The list of freeblocks must be in ascending order. Find the
** spot on the list where iStart should be inserted.
*/
hdr = pPage->hdrOffset;
@@ -64632,17 +72022,17 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */
}else{
while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){
- if( iFreeBlk<iPtr+4 ){
- if( iFreeBlk==0 ) break;
+ if( iFreeBlk<=iPtr ){
+ if( iFreeBlk==0 ) break; /* TH3: corrupt082.100 */
return SQLITE_CORRUPT_PAGE(pPage);
}
iPtr = iFreeBlk;
}
- if( iFreeBlk>pPage->pBt->usableSize-4 ){
+ if( iFreeBlk>pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */
return SQLITE_CORRUPT_PAGE(pPage);
}
- assert( iFreeBlk>iPtr || iFreeBlk==0 );
-
+ assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB );
+
/* At this point:
** iFreeBlk: First freeblock after iStart, or zero if none
** iPtr: The address of a pointer to iFreeBlk
@@ -64659,7 +72049,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
iSize = iEnd - iStart;
iFreeBlk = get2byte(&data[iFreeBlk]);
}
-
+
/* If iPtr is another freeblock (that is, if iPtr is not the freelist
** pointer in the page header) then check to see if iStart should be
** coalesced onto the end of iPtr.
@@ -64676,25 +72066,27 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage);
data[hdr+7] -= nFrag;
}
- x = get2byte(&data[hdr+5]);
+ pTmp = &data[hdr+5];
+ x = get2byte(pTmp);
+ if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){
+ /* Overwrite deleted information with zeros when the secure_delete
+ ** option is enabled */
+ memset(&data[iStart], 0, iSize);
+ }
if( iStart<=x ){
/* The new freeblock is at the beginning of the cell content area,
** so just extend the cell content area rather than create another
** freelist entry */
- if( iStart<x || iPtr!=hdr+1 ) return SQLITE_CORRUPT_PAGE(pPage);
+ if( iStart<x ) return SQLITE_CORRUPT_PAGE(pPage);
+ if( iPtr!=hdr+1 ) return SQLITE_CORRUPT_PAGE(pPage);
put2byte(&data[hdr+1], iFreeBlk);
put2byte(&data[hdr+5], iEnd);
}else{
/* Insert the new freeblock into the freelist */
put2byte(&data[iPtr], iStart);
+ put2byte(&data[iStart], iFreeBlk);
+ put2byte(&data[iStart+2], iSize);
}
- if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){
- /* Overwrite deleted information with zeros when the secure_delete
- ** option is enabled */
- memset(&data[iStart], 0, iSize);
- }
- put2byte(&data[iStart], iFreeBlk);
- put2byte(&data[iStart+2], iSize);
pPage->nFree += iOrigSize;
return SQLITE_OK;
}
@@ -64706,76 +72098,79 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
** Only the following combinations are supported. Anything different
** indicates a corrupt database files:
**
-** PTF_ZERODATA
-** PTF_ZERODATA | PTF_LEAF
-** PTF_LEAFDATA | PTF_INTKEY
-** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF
+** PTF_ZERODATA (0x02, 2)
+** PTF_LEAFDATA | PTF_INTKEY (0x05, 5)
+** PTF_ZERODATA | PTF_LEAF (0x0a, 10)
+** PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF (0x0d, 13)
*/
static int decodeFlags(MemPage *pPage, int flagByte){
BtShared *pBt; /* A copy of pPage->pBt */
assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 );
- flagByte &= ~PTF_LEAF;
- pPage->childPtrSize = 4-4*pPage->leaf;
- pPage->xCellSize = cellSizePtr;
pBt = pPage->pBt;
- if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
- /* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an
- ** interior table b-tree page. */
- assert( (PTF_LEAFDATA|PTF_INTKEY)==5 );
- /* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a
- ** leaf table b-tree page. */
- assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 );
- pPage->intKey = 1;
- if( pPage->leaf ){
+ pPage->max1bytePayload = pBt->max1bytePayload;
+ if( flagByte>=(PTF_ZERODATA | PTF_LEAF) ){
+ pPage->childPtrSize = 0;
+ pPage->leaf = 1;
+ if( flagByte==(PTF_LEAFDATA | PTF_INTKEY | PTF_LEAF) ){
pPage->intKeyLeaf = 1;
+ pPage->xCellSize = cellSizePtrTableLeaf;
pPage->xParseCell = btreeParseCellPtr;
+ pPage->intKey = 1;
+ pPage->maxLocal = pBt->maxLeaf;
+ pPage->minLocal = pBt->minLeaf;
+ }else if( flagByte==(PTF_ZERODATA | PTF_LEAF) ){
+ pPage->intKey = 0;
+ pPage->intKeyLeaf = 0;
+ pPage->xCellSize = cellSizePtrIdxLeaf;
+ pPage->xParseCell = btreeParseCellPtrIndex;
+ pPage->maxLocal = pBt->maxLocal;
+ pPage->minLocal = pBt->minLocal;
}else{
+ pPage->intKey = 0;
+ pPage->intKeyLeaf = 0;
+ pPage->xCellSize = cellSizePtrIdxLeaf;
+ pPage->xParseCell = btreeParseCellPtrIndex;
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ }else{
+ pPage->childPtrSize = 4;
+ pPage->leaf = 0;
+ if( flagByte==(PTF_ZERODATA) ){
+ pPage->intKey = 0;
+ pPage->intKeyLeaf = 0;
+ pPage->xCellSize = cellSizePtr;
+ pPage->xParseCell = btreeParseCellPtrIndex;
+ pPage->maxLocal = pBt->maxLocal;
+ pPage->minLocal = pBt->minLocal;
+ }else if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
pPage->intKeyLeaf = 0;
pPage->xCellSize = cellSizePtrNoPayload;
pPage->xParseCell = btreeParseCellPtrNoPayload;
+ pPage->intKey = 1;
+ pPage->maxLocal = pBt->maxLeaf;
+ pPage->minLocal = pBt->minLeaf;
+ }else{
+ pPage->intKey = 0;
+ pPage->intKeyLeaf = 0;
+ pPage->xCellSize = cellSizePtr;
+ pPage->xParseCell = btreeParseCellPtrIndex;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
- pPage->maxLocal = pBt->maxLeaf;
- pPage->minLocal = pBt->minLeaf;
- }else if( flagByte==PTF_ZERODATA ){
- /* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an
- ** interior index b-tree page. */
- assert( (PTF_ZERODATA)==2 );
- /* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a
- ** leaf index b-tree page. */
- assert( (PTF_ZERODATA|PTF_LEAF)==10 );
- pPage->intKey = 0;
- pPage->intKeyLeaf = 0;
- pPage->xParseCell = btreeParseCellPtrIndex;
- pPage->maxLocal = pBt->maxLocal;
- pPage->minLocal = pBt->minLocal;
- }else{
- /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is
- ** an error. */
- return SQLITE_CORRUPT_PAGE(pPage);
}
- pPage->max1bytePayload = pBt->max1bytePayload;
return SQLITE_OK;
}
/*
-** Initialize the auxiliary information for a disk block.
-**
-** Return SQLITE_OK on success. If we see that the page does
-** not contain a well-formed database page, then return
-** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not
-** guarantee that the page is well-formed. It only shows that
-** we failed to detect any corruption.
+** Compute the amount of freespace on the page. In other words, fill
+** in the pPage->nFree field.
*/
-static int btreeInitPage(MemPage *pPage){
+static int btreeComputeFreeSpace(MemPage *pPage){
int pc; /* Address of a freeblock within pPage->aData[] */
u8 hdr; /* Offset to beginning of page header */
u8 *data; /* Equal to pPage->aData */
- BtShared *pBt; /* The main btree structure */
int usableSize; /* Amount of usable space on each page */
- u16 cellOffset; /* Offset from start of page to first cell pointer */
int nFree; /* Number of unused bytes on the page */
int top; /* First byte of the cell content area */
int iCellFirst; /* First allowable cell or freeblock offset */
@@ -64787,71 +72182,18 @@ static int btreeInitPage(MemPage *pPage){
assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) );
assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) );
- assert( pPage->isInit==0 );
+ assert( pPage->isInit==1 );
+ assert( pPage->nFree<0 );
- pBt = pPage->pBt;
+ usableSize = pPage->pBt->usableSize;
hdr = pPage->hdrOffset;
data = pPage->aData;
- /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating
- ** the b-tree page type. */
- if( decodeFlags(pPage, data[hdr]) ){
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
- pPage->maskPage = (u16)(pBt->pageSize - 1);
- pPage->nOverflow = 0;
- usableSize = pBt->usableSize;
- pPage->cellOffset = cellOffset = hdr + 8 + pPage->childPtrSize;
- pPage->aDataEnd = &data[usableSize];
- pPage->aCellIdx = &data[cellOffset];
- pPage->aDataOfst = &data[pPage->childPtrSize];
/* EVIDENCE-OF: R-58015-48175 The two-byte integer at offset 5 designates
** the start of the cell content area. A zero value for this integer is
** interpreted as 65536. */
top = get2byteNotZero(&data[hdr+5]);
- /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
- ** number of cells on the page. */
- pPage->nCell = get2byte(&data[hdr+3]);
- if( pPage->nCell>MX_CELL(pBt) ){
- /* To many cells for a single page. The page must be corrupt */
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- testcase( pPage->nCell==MX_CELL(pBt) );
- /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only
- ** possible for a root page of a table that contains no rows) then the
- ** offset to the cell content area will equal the page size minus the
- ** bytes of reserved space. */
- assert( pPage->nCell>0 || top==usableSize || CORRUPT_DB );
-
- /* A malformed database page might cause us to read past the end
- ** of page when parsing a cell.
- **
- ** The following block of code checks early to see if a cell extends
- ** past the end of a page boundary and causes SQLITE_CORRUPT to be
- ** returned if it does.
- */
- iCellFirst = cellOffset + 2*pPage->nCell;
+ iCellFirst = hdr + 8 + pPage->childPtrSize + 2*pPage->nCell;
iCellLast = usableSize - 4;
- if( pBt->db->flags & SQLITE_CellSizeCk ){
- int i; /* Index into the cell pointer array */
- int sz; /* Size of a cell */
-
- if( !pPage->leaf ) iCellLast--;
- for(i=0; i<pPage->nCell; i++){
- pc = get2byteAligned(&data[cellOffset+i*2]);
- testcase( pc==iCellFirst );
- testcase( pc==iCellLast );
- if( pc<iCellFirst || pc>iCellLast ){
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- sz = pPage->xCellSize(pPage, &data[pc]);
- testcase( pc+sz==usableSize );
- if( pc+sz>usableSize ){
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- }
- if( !pPage->leaf ) iCellLast++;
- }
/* Compute the total free space on the page
** EVIDENCE-OF: R-23588-34450 The two-byte integer at offset 1 gives the
@@ -64861,11 +72203,11 @@ static int btreeInitPage(MemPage *pPage){
nFree = data[hdr+7] + top; /* Init nFree to non-freeblock free space */
if( pc>0 ){
u32 next, size;
- if( pc<iCellFirst ){
+ if( pc<top ){
/* EVIDENCE-OF: R-55530-52930 In a well-formed b-tree page, there will
** always be at least one cell before the first freeblock.
*/
- return SQLITE_CORRUPT_PAGE(pPage);
+ return SQLITE_CORRUPT_PAGE(pPage);
}
while( 1 ){
if( pc>iCellLast ){
@@ -64895,11 +72237,104 @@ static int btreeInitPage(MemPage *pPage){
** serves to verify that the offset to the start of the cell-content
** area, according to the page header, lies within the page.
*/
- if( nFree>usableSize ){
+ if( nFree>usableSize || nFree<iCellFirst ){
return SQLITE_CORRUPT_PAGE(pPage);
}
pPage->nFree = (u16)(nFree - iCellFirst);
+ return SQLITE_OK;
+}
+
+/*
+** Do additional sanity check after btreeInitPage() if
+** PRAGMA cell_size_check=ON
+*/
+static SQLITE_NOINLINE int btreeCellSizeCheck(MemPage *pPage){
+ int iCellFirst; /* First allowable cell or freeblock offset */
+ int iCellLast; /* Last possible cell or freeblock offset */
+ int i; /* Index into the cell pointer array */
+ int sz; /* Size of a cell */
+ int pc; /* Address of a freeblock within pPage->aData[] */
+ u8 *data; /* Equal to pPage->aData */
+ int usableSize; /* Maximum usable space on the page */
+ int cellOffset; /* Start of cell content area */
+
+ iCellFirst = pPage->cellOffset + 2*pPage->nCell;
+ usableSize = pPage->pBt->usableSize;
+ iCellLast = usableSize - 4;
+ data = pPage->aData;
+ cellOffset = pPage->cellOffset;
+ if( !pPage->leaf ) iCellLast--;
+ for(i=0; i<pPage->nCell; i++){
+ pc = get2byteAligned(&data[cellOffset+i*2]);
+ testcase( pc==iCellFirst );
+ testcase( pc==iCellLast );
+ if( pc<iCellFirst || pc>iCellLast ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ sz = pPage->xCellSize(pPage, &data[pc]);
+ testcase( pc+sz==usableSize );
+ if( pc+sz>usableSize ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Initialize the auxiliary information for a disk block.
+**
+** Return SQLITE_OK on success. If we see that the page does
+** not contain a well-formed database page, then return
+** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not
+** guarantee that the page is well-formed. It only shows that
+** we failed to detect any corruption.
+*/
+static int btreeInitPage(MemPage *pPage){
+ u8 *data; /* Equal to pPage->aData */
+ BtShared *pBt; /* The main btree structure */
+
+ assert( pPage->pBt!=0 );
+ assert( pPage->pBt->db!=0 );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
+ assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) );
+ assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) );
+ assert( pPage->isInit==0 );
+
+ pBt = pPage->pBt;
+ data = pPage->aData + pPage->hdrOffset;
+ /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating
+ ** the b-tree page type. */
+ if( decodeFlags(pPage, data[0]) ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
+ pPage->maskPage = (u16)(pBt->pageSize - 1);
+ pPage->nOverflow = 0;
+ pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize;
+ pPage->aCellIdx = data + pPage->childPtrSize + 8;
+ pPage->aDataEnd = pPage->aData + pBt->pageSize;
+ pPage->aDataOfst = pPage->aData + pPage->childPtrSize;
+ /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
+ ** number of cells on the page. */
+ pPage->nCell = get2byte(&data[3]);
+ if( pPage->nCell>MX_CELL(pBt) ){
+ /* To many cells for a single page. The page must be corrupt */
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ testcase( pPage->nCell==MX_CELL(pBt) );
+ /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only
+ ** possible for a root page of a table that contains no rows) then the
+ ** offset to the cell content area will equal the page size minus the
+ ** bytes of reserved space. */
+ assert( pPage->nCell>0
+ || get2byteNotZero(&data[5])==(int)pBt->usableSize
+ || CORRUPT_DB );
+ pPage->nFree = -1; /* Indicate that this value is yet uncomputed */
pPage->isInit = 1;
+ if( pBt->db->flags & SQLITE_CellSizeCk ){
+ return btreeCellSizeCheck(pPage);
+ }
return SQLITE_OK;
}
@@ -64913,7 +72348,7 @@ static void zeroPage(MemPage *pPage, int flags){
u8 hdr = pPage->hdrOffset;
u16 first;
- assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno );
+ assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB );
assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
assert( sqlite3PagerGetData(pPage->pDbPage) == data );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
@@ -64929,7 +72364,7 @@ static void zeroPage(MemPage *pPage, int flags){
pPage->nFree = (u16)(pBt->usableSize - first);
decodeFlags(pPage, flags);
pPage->cellOffset = first;
- pPage->aDataEnd = &data[pBt->usableSize];
+ pPage->aDataEnd = &data[pBt->pageSize];
pPage->aCellIdx = &data[first];
pPage->aDataOfst = &data[pPage->childPtrSize];
pPage->nOverflow = 0;
@@ -64954,7 +72389,7 @@ static MemPage *btreePageFromDbPage(DbPage *pDbPage, Pgno pgno, BtShared *pBt){
pPage->hdrOffset = pgno==1 ? 100 : 0;
}
assert( pPage->aData==sqlite3PagerGetData(pDbPage) );
- return pPage;
+ return pPage;
}
/*
@@ -65007,76 +72442,48 @@ static MemPage *btreePageLookup(BtShared *pBt, Pgno pgno){
static Pgno btreePagecount(BtShared *pBt){
return pBt->nPage;
}
-SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree *p){
+SQLITE_PRIVATE Pgno sqlite3BtreeLastPage(Btree *p){
assert( sqlite3BtreeHoldsMutex(p) );
- assert( ((p->pBt->nPage)&0x80000000)==0 );
return btreePagecount(p->pBt);
}
/*
** Get a page from the pager and initialize it.
-**
-** If pCur!=0 then the page is being fetched as part of a moveToChild()
-** call. Do additional sanity checking on the page in this case.
-** And if the fetch fails, this routine must decrement pCur->iPage.
-**
-** The page is fetched as read-write unless pCur is not NULL and is
-** a read-only cursor.
-**
-** If an error occurs, then *ppPage is undefined. It
-** may remain unchanged, or it may be set to an invalid value.
*/
static int getAndInitPage(
BtShared *pBt, /* The database file */
Pgno pgno, /* Number of the page to get */
MemPage **ppPage, /* Write the page pointer here */
- BtCursor *pCur, /* Cursor to receive the page, or NULL */
int bReadOnly /* True for a read-only page */
){
int rc;
DbPage *pDbPage;
+ MemPage *pPage;
assert( sqlite3_mutex_held(pBt->mutex) );
- assert( pCur==0 || ppPage==&pCur->pPage );
- assert( pCur==0 || bReadOnly==pCur->curPagerFlags );
- assert( pCur==0 || pCur->iPage>0 );
if( pgno>btreePagecount(pBt) ){
- rc = SQLITE_CORRUPT_BKPT;
- goto getAndInitPage_error;
+ *ppPage = 0;
+ return SQLITE_CORRUPT_BKPT;
}
rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly);
if( rc ){
- goto getAndInitPage_error;
+ *ppPage = 0;
+ return rc;
}
- *ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage);
- if( (*ppPage)->isInit==0 ){
+ pPage = (MemPage*)sqlite3PagerGetExtra(pDbPage);
+ if( pPage->isInit==0 ){
btreePageFromDbPage(pDbPage, pgno, pBt);
- rc = btreeInitPage(*ppPage);
+ rc = btreeInitPage(pPage);
if( rc!=SQLITE_OK ){
- releasePage(*ppPage);
- goto getAndInitPage_error;
+ releasePage(pPage);
+ *ppPage = 0;
+ return rc;
}
}
- assert( (*ppPage)->pgno==pgno );
- assert( (*ppPage)->aData==sqlite3PagerGetData(pDbPage) );
-
- /* If obtaining a child page for a cursor, we must verify that the page is
- ** compatible with the root page. */
- if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){
- rc = SQLITE_CORRUPT_PGNO(pgno);
- releasePage(*ppPage);
- goto getAndInitPage_error;
- }
+ assert( pPage->pgno==pgno || CORRUPT_DB );
+ assert( pPage->aData==sqlite3PagerGetData(pDbPage) );
+ *ppPage = pPage;
return SQLITE_OK;
-
-getAndInitPage_error:
- if( pCur ){
- pCur->iPage--;
- pCur->pPage = pCur->apPage[pCur->iPage];
- }
- testcase( pgno==0 );
- assert( pgno!=0 || rc==SQLITE_CORRUPT );
- return rc;
}
/*
@@ -65159,7 +72566,7 @@ static void pageReinit(DbPage *pData){
** call to btreeInitPage() will likely return SQLITE_CORRUPT.
** But no harm is done by this. And it is very important that
** btreeInitPage() be called on every btree page so we make
- ** the call for every page that comes in for re-initing. */
+ ** the call for every page that comes in for re-initializing. */
btreeInitPage(pPage);
}
}
@@ -65172,17 +72579,16 @@ static int btreeInvokeBusyHandler(void *pArg){
BtShared *pBt = (BtShared*)pArg;
assert( pBt->db );
assert( sqlite3_mutex_held(pBt->db->mutex) );
- return sqlite3InvokeBusyHandler(&pBt->db->busyHandler,
- sqlite3PagerFile(pBt->pPager));
+ return sqlite3InvokeBusyHandler(&pBt->db->busyHandler);
}
/*
** Open a database file.
-**
+**
** zFilename is the name of the database file. If zFilename is NULL
** then an ephemeral database is created. The ephemeral database might
** be exclusively in memory, or it might use a disk-based memory cache.
-** Either way, the ephemeral database will be automatically deleted
+** Either way, the ephemeral database will be automatically deleted
** when sqlite3BtreeClose() is called.
**
** If zFilename is ":memory:" then an in-memory database is created
@@ -65215,7 +72621,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
/* True if opening an ephemeral, temporary database */
const int isTempDb = zFilename==0 || zFilename[0]==0;
- /* Set the variable isMemdb to true for an in-memory database, or
+ /* Set the variable isMemdb to true for an in-memory database, or
** false for a file-based database.
*/
#ifdef SQLITE_OMIT_MEMORYDB
@@ -65277,15 +72683,19 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
rc = sqlite3OsFullPathname(pVfs, zFilename,
nFullPathname, zFullPathname);
if( rc ){
- sqlite3_free(zFullPathname);
- sqlite3_free(p);
- return rc;
+ if( rc==SQLITE_OK_SYMLINK ){
+ rc = SQLITE_OK;
+ }else{
+ sqlite3_free(zFullPathname);
+ sqlite3_free(p);
+ return rc;
+ }
}
}
#if SQLITE_THREADSAFE
mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
sqlite3_mutex_enter(mutexOpen);
- mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
sqlite3_mutex_enter(mutexShared);
#endif
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
@@ -65334,7 +72744,10 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
assert( sizeof(u32)==4 );
assert( sizeof(u16)==2 );
assert( sizeof(Pgno)==4 );
-
+
+ /* Suppress false-positive compiler warning from PVS-Studio */
+ memset(&zDbHeader[16], 0, 8);
+
pBt = sqlite3MallocZero( sizeof(*pBt) );
if( pBt==0 ){
rc = SQLITE_NOMEM_BKPT;
@@ -65353,7 +72766,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
pBt->db = db;
sqlite3PagerSetBusyHandler(pBt->pPager, btreeInvokeBusyHandler, pBt);
p->pBt = pBt;
-
+
pBt->pCursor = 0;
pBt->pPage1 = 0;
if( sqlite3PagerIsreadonly(pBt->pPager) ) pBt->btsFlags |= BTS_READ_ONLY;
@@ -65397,14 +72810,14 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
if( rc ) goto btree_open_out;
pBt->usableSize = pBt->pageSize - nReserve;
assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */
-
+
#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
/* Add the new BtShared object to the linked list sharable BtShareds.
*/
pBt->nRef = 1;
if( p->sharable ){
MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
- MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);)
+ MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);)
if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){
pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);
if( pBt->mutex==0 ){
@@ -65469,7 +72882,7 @@ btree_open_out:
** do not change the pager-cache size.
*/
if( sqlite3BtreeSchema(p, 0, 0)==0 ){
- sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE);
+ sqlite3BtreeSetCacheSize(p, SQLITE_DEFAULT_CACHE_SIZE);
}
pFile = sqlite3PagerFile(pBt->pPager);
@@ -65493,13 +72906,13 @@ btree_open_out:
*/
static int removeFromSharingList(BtShared *pBt){
#ifndef SQLITE_OMIT_SHARED_CACHE
- MUTEX_LOGIC( sqlite3_mutex *pMaster; )
+ MUTEX_LOGIC( sqlite3_mutex *pMainMtx; )
BtShared *pList;
int removed = 0;
assert( sqlite3_mutex_notheld(pBt->mutex) );
- MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
- sqlite3_mutex_enter(pMaster);
+ MUTEX_LOGIC( pMainMtx = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); )
+ sqlite3_mutex_enter(pMainMtx);
pBt->nRef--;
if( pBt->nRef<=0 ){
if( GLOBAL(BtShared*,sqlite3SharedCacheList)==pBt ){
@@ -65518,7 +72931,7 @@ static int removeFromSharingList(BtShared *pBt){
}
removed = 1;
}
- sqlite3_mutex_leave(pMaster);
+ sqlite3_mutex_leave(pMainMtx);
return removed;
#else
return 1;
@@ -65526,34 +72939,42 @@ static int removeFromSharingList(BtShared *pBt){
}
/*
-** Make sure pBt->pTmpSpace points to an allocation of
+** Make sure pBt->pTmpSpace points to an allocation of
** MX_CELL_SIZE(pBt) bytes with a 4-byte prefix for a left-child
** pointer.
*/
-static void allocateTempSpace(BtShared *pBt){
- if( !pBt->pTmpSpace ){
- pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize );
-
- /* One of the uses of pBt->pTmpSpace is to format cells before
- ** inserting them into a leaf page (function fillInCell()). If
- ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes
- ** by the various routines that manipulate binary cells. Which
- ** can mean that fillInCell() only initializes the first 2 or 3
- ** bytes of pTmpSpace, but that the first 4 bytes are copied from
- ** it into a database page. This is not actually a problem, but it
- ** does cause a valgrind error when the 1 or 2 bytes of unitialized
- ** data is passed to system call write(). So to avoid this error,
- ** zero the first 4 bytes of temp space here.
- **
- ** Also: Provide four bytes of initialized space before the
- ** beginning of pTmpSpace as an area available to prepend the
- ** left-child pointer to the beginning of a cell.
- */
- if( pBt->pTmpSpace ){
- memset(pBt->pTmpSpace, 0, 8);
- pBt->pTmpSpace += 4;
- }
+static SQLITE_NOINLINE int allocateTempSpace(BtShared *pBt){
+ assert( pBt!=0 );
+ assert( pBt->pTmpSpace==0 );
+ /* This routine is called only by btreeCursor() when allocating the
+ ** first write cursor for the BtShared object */
+ assert( pBt->pCursor!=0 && (pBt->pCursor->curFlags & BTCF_WriteFlag)!=0 );
+ pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize );
+ if( pBt->pTmpSpace==0 ){
+ BtCursor *pCur = pBt->pCursor;
+ pBt->pCursor = pCur->pNext; /* Unlink the cursor */
+ memset(pCur, 0, sizeof(*pCur));
+ return SQLITE_NOMEM_BKPT;
}
+
+ /* One of the uses of pBt->pTmpSpace is to format cells before
+ ** inserting them into a leaf page (function fillInCell()). If
+ ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes
+ ** by the various routines that manipulate binary cells. Which
+ ** can mean that fillInCell() only initializes the first 2 or 3
+ ** bytes of pTmpSpace, but that the first 4 bytes are copied from
+ ** it into a database page. This is not actually a problem, but it
+ ** does cause a valgrind error when the 1 or 2 bytes of uninitialized
+ ** data is passed to system call write(). So to avoid this error,
+ ** zero the first 4 bytes of temp space here.
+ **
+ ** Also: Provide four bytes of initialized space before the
+ ** beginning of pTmpSpace as an area available to prepend the
+ ** left-child pointer to the beginning of a cell.
+ */
+ memset(pBt->pTmpSpace, 0, 8);
+ pBt->pTmpSpace += 4;
+ return SQLITE_OK;
}
/*
@@ -65572,19 +72993,23 @@ static void freeTempSpace(BtShared *pBt){
*/
SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){
BtShared *pBt = p->pBt;
- BtCursor *pCur;
/* Close all cursors opened via this handle. */
assert( sqlite3_mutex_held(p->db->mutex) );
sqlite3BtreeEnter(p);
- pCur = pBt->pCursor;
- while( pCur ){
- BtCursor *pTmp = pCur;
- pCur = pCur->pNext;
- if( pTmp->pBtree==p ){
- sqlite3BtreeCloseCursor(pTmp);
+
+ /* Verify that no other cursors have this Btree open */
+#ifdef SQLITE_DEBUG
+ {
+ BtCursor *pCur = pBt->pCursor;
+ while( pCur ){
+ BtCursor *pTmp = pCur;
+ pCur = pCur->pNext;
+ assert( pTmp->pBtree!=p );
+
}
}
+#endif
/* Rollback any active transaction and free the handle structure.
** The call to sqlite3BtreeRollback() drops any table-locks held by
@@ -65594,7 +73019,7 @@ SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){
sqlite3BtreeLeave(p);
/* If there are still other outstanding references to the shared-btree
- ** structure, return now. The remainder of this procedure cleans
+ ** structure, return now. The remainder of this procedure cleans
** up the shared-btree.
*/
assert( p->wantToLock==0 && p->locked==0 );
@@ -65700,7 +73125,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(
/*
** Change the default pages size and the number of reserved bytes per page.
-** Or, if the page size has already been fixed, return SQLITE_READONLY
+** Or, if the page size has already been fixed, return SQLITE_READONLY
** without changing anything.
**
** The page size must be a power of 2 between 512 and 65536. If the page
@@ -65720,24 +73145,23 @@ SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(
*/
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
int rc = SQLITE_OK;
+ int x;
BtShared *pBt = p->pBt;
- assert( nReserve>=-1 && nReserve<=255 );
+ assert( nReserve>=0 && nReserve<=255 );
sqlite3BtreeEnter(p);
-#if SQLITE_HAS_CODEC
- if( nReserve>pBt->optimalReserve ) pBt->optimalReserve = (u8)nReserve;
-#endif
+ pBt->nReserveWanted = nReserve;
+ x = pBt->pageSize - pBt->usableSize;
+ if( nReserve<x ) nReserve = x;
if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
sqlite3BtreeLeave(p);
return SQLITE_READONLY;
}
- if( nReserve<0 ){
- nReserve = pBt->pageSize - pBt->usableSize;
- }
assert( nReserve>=0 && nReserve<=255 );
if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
((pageSize-1)&pageSize)==0 ){
assert( (pageSize & 7)==0 );
assert( !pBt->pCursor );
+ if( nReserve>32 && pageSize==512 ) pageSize = 1024;
pBt->pageSize = (u32)pageSize;
freeTempSpace(pBt);
}
@@ -65761,7 +73185,7 @@ SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){
** held.
**
** This is useful in one special case in the backup API code where it is
-** known that the shared b-tree mutex is held, but the mutex on the
+** known that the shared b-tree mutex is held, but the mutex on the
** database handle that owns *p is not. In this case if sqlite3BtreeEnter()
** were to be called, it might collide with some other operation on the
** database handle that owns *p, causing undefined behavior.
@@ -65775,22 +73199,20 @@ SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){
/*
** Return the number of bytes of space at the end of every page that
-** are intentually left unused. This is the "reserved" space that is
+** are intentionally left unused. This is the "reserved" space that is
** sometimes used by extensions.
**
-** If SQLITE_HAS_MUTEX is defined then the number returned is the
-** greater of the current reserved space and the maximum requested
-** reserve space.
+** The value returned is the larger of the current reserve size and
+** the latest reserve size requested by SQLITE_FILECTRL_RESERVE_BYTES.
+** The amount of reserve can only grow - never shrink.
*/
-SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree *p){
- int n;
+SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree *p){
+ int n1, n2;
sqlite3BtreeEnter(p);
- n = sqlite3BtreeGetReserveNoMutex(p);
-#ifdef SQLITE_HAS_CODEC
- if( n<p->pBt->optimalReserve ) n = p->pBt->optimalReserve;
-#endif
+ n1 = (int)p->pBt->nReserveWanted;
+ n2 = sqlite3BtreeGetReserveNoMutex(p);
sqlite3BtreeLeave(p);
- return n;
+ return n1>n2 ? n1 : n2;
}
@@ -65799,8 +73221,8 @@ SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree *p){
** No changes are made if mxPage is 0 or negative.
** Regardless of the value of mxPage, return the maximum page count.
*/
-SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){
- int n;
+SQLITE_PRIVATE Pgno sqlite3BtreeMaxPageCount(Btree *p, Pgno mxPage){
+ Pgno n;
sqlite3BtreeEnter(p);
n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage);
sqlite3BtreeLeave(p);
@@ -65843,7 +73265,7 @@ SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){
/*
** Change the 'auto-vacuum' property of the database. If the 'autoVacuum'
** parameter is non-zero, then auto-vacuum mode is enabled. If zero, it
-** is disabled. The default value for the auto-vacuum property is
+** is disabled. The default value for the auto-vacuum property is
** determined by the SQLITE_DEFAULT_AUTOVACUUM macro.
*/
SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){
@@ -65867,7 +73289,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){
}
/*
-** Return the value of the 'auto-vacuum' property. If auto-vacuum is
+** Return the value of the 'auto-vacuum' property. If auto-vacuum is
** enabled 1 is returned. Otherwise 0.
*/
SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *p){
@@ -65899,9 +73321,9 @@ static void setDefaultSyncFlag(BtShared *pBt, u8 safety_level){
Db *pDb;
if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){
while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; }
- if( pDb->bSyncSet==0
- && pDb->safety_level!=safety_level
- && pDb!=&db->aDb[1]
+ if( pDb->bSyncSet==0
+ && pDb->safety_level!=safety_level
+ && pDb!=&db->aDb[1]
){
pDb->safety_level = safety_level;
sqlite3PagerSetFlags(pBt->pPager,
@@ -65924,14 +73346,13 @@ static int newDatabase(BtShared*);
** SQLITE_OK is returned on success. If the file is not a
** well-formed database file, then SQLITE_CORRUPT is returned.
** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM
-** is returned if we run out of memory.
+** is returned if we run out of memory.
*/
static int lockBtree(BtShared *pBt){
int rc; /* Result code from subfunctions */
MemPage *pPage1; /* Page 1 of the database file */
- int nPage; /* Number of pages in the database */
- int nPageFile = 0; /* Number of pages in the database file */
- int nPageHeader; /* Number of pages in the database according to hdr */
+ u32 nPage; /* Number of pages in the database */
+ u32 nPageFile = 0; /* Number of pages in the database file */
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pBt->pPage1==0 );
@@ -65941,10 +73362,10 @@ static int lockBtree(BtShared *pBt){
if( rc!=SQLITE_OK ) return rc;
/* Do some checking to help insure the file we opened really is
- ** a valid database file.
+ ** a valid database file.
*/
- nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData);
- sqlite3PagerPagecount(pBt->pPager, &nPageFile);
+ nPage = get4byte(28+(u8*)pPage1->aData);
+ sqlite3PagerPagecount(pBt->pPager, (int*)&nPageFile);
if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){
nPage = nPageFile;
}
@@ -65978,8 +73399,8 @@ static int lockBtree(BtShared *pBt){
goto page1_init_failed;
}
- /* If the write version is set to 2, this database should be accessed
- ** in WAL mode. If the log is not already open, open it now. Then
+ /* If the read version is set to 2, this database should be accessed
+ ** in WAL mode. If the log is not already open, open it now. Then
** return SQLITE_OK and return without populating BtShared.pPage1.
** The caller detects this and calls this function again. This is
** required as the version of page 1 currently in the page1 buffer
@@ -66020,15 +73441,15 @@ static int lockBtree(BtShared *pBt){
/* EVIDENCE-OF: R-25008-21688 The size of a page is a power of two
** between 512 and 65536 inclusive. */
if( ((pageSize-1)&pageSize)!=0
- || pageSize>SQLITE_MAX_PAGE_SIZE
- || pageSize<=256
+ || pageSize>SQLITE_MAX_PAGE_SIZE
+ || pageSize<=256
){
goto page1_init_failed;
}
assert( (pageSize & 7)==0 );
/* EVIDENCE-OF: R-59310-51205 The "reserved space" size in the 1-byte
** integer at offset 20 is the number of bytes of space at the end of
- ** each page to reserve for extensions.
+ ** each page to reserve for extensions.
**
** EVIDENCE-OF: R-37497-42412 The size of the reserved region is
** determined by the one-byte unsigned integer found at an offset of 20
@@ -66044,14 +73465,19 @@ static int lockBtree(BtShared *pBt){
releasePageOne(pPage1);
pBt->usableSize = usableSize;
pBt->pageSize = pageSize;
+ pBt->btsFlags |= BTS_PAGESIZE_FIXED;
freeTempSpace(pBt);
rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize,
pageSize-usableSize);
return rc;
}
- if( sqlite3WritableSchema(pBt->db)==0 && nPage>nPageFile ){
- rc = SQLITE_CORRUPT_BKPT;
- goto page1_init_failed;
+ if( nPage>nPageFile ){
+ if( sqlite3WritableSchema(pBt->db)==0 ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto page1_init_failed;
+ }else{
+ nPage = nPageFile;
+ }
}
/* EVIDENCE-OF: R-28312-64704 However, the usable size is not allowed to
** be less than 480. In other words, if the page size is 512, then the
@@ -66059,6 +73485,7 @@ static int lockBtree(BtShared *pBt){
if( usableSize<480 ){
goto page1_init_failed;
}
+ pBt->btsFlags |= BTS_PAGESIZE_FIXED;
pBt->pageSize = pageSize;
pBt->usableSize = usableSize;
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -66118,7 +73545,7 @@ static int countValidCursors(BtShared *pBt, int wrOnly){
int r = 0;
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
if( (wrOnly==0 || (pCur->curFlags & BTCF_WriteFlag)!=0)
- && pCur->eState!=CURSOR_FAULT ) r++;
+ && pCur->eState!=CURSOR_FAULT ) r++;
}
return r;
}
@@ -66127,7 +73554,7 @@ static int countValidCursors(BtShared *pBt, int wrOnly){
/*
** If there are no outstanding cursors and we are not in the middle
** of a transaction but there is a read lock on the database, then
-** this routine unrefs the first page of the database file which
+** this routine unrefs the first page of the database file which
** has the effect of releasing the read lock.
**
** If there is a transaction in progress, this routine is a no-op.
@@ -66211,8 +73638,8 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p){
** upgraded to exclusive by calling this routine a second time - the
** exclusivity flag only works for a new transaction.
**
-** A write-transaction must be started before attempting any
-** changes to the database. None of the following routines
+** A write-transaction must be started before attempting any
+** changes to the database. None of the following routines
** will work unless a transaction is started first:
**
** sqlite3BtreeCreateTable()
@@ -66226,7 +73653,7 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p){
** If an initial attempt to acquire the lock fails because of lock contention
** and the database was previously unlocked, then invoke the busy handler
** if there is one. But if there was previously a read-lock, do not
-** invoke the busy handler - just return SQLITE_BUSY. SQLITE_BUSY is
+** invoke the busy handler - just return SQLITE_BUSY. SQLITE_BUSY is
** returned when there is already a read-lock in order to avoid a deadlock.
**
** Suppose there are two processes A and B. A has a read lock and B has
@@ -66237,8 +73664,13 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p){
** when A already has a read lock, we encourage A to give up and let B
** proceed.
*/
-SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
+static SQLITE_NOINLINE int btreeBeginTrans(
+ Btree *p, /* The btree in which to start the transaction */
+ int wrflag, /* True to start a write transaction */
+ int *pSchemaVersion /* Put schema version number here, if not NULL */
+){
BtShared *pBt = p->pBt;
+ Pager *pPager = pBt->pPager;
int rc = SQLITE_OK;
sqlite3BtreeEnter(p);
@@ -66253,8 +73685,8 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVers
}
assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
- if( (p->db->flags & SQLITE_ResetDatabase)
- && sqlite3PagerIsreadonly(pBt->pPager)==0
+ if( (p->db->flags & SQLITE_ResetDatabase)
+ && sqlite3PagerIsreadonly(pPager)==0
){
pBt->btsFlags &= ~BTS_READ_ONLY;
}
@@ -66268,7 +73700,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVers
#ifndef SQLITE_OMIT_SHARED_CACHE
{
sqlite3 *pBlock = 0;
- /* If another database handle has already opened a write transaction
+ /* If another database handle has already opened a write transaction
** on this shared-btree structure and a second write transaction is
** requested, return SQLITE_LOCKED.
*/
@@ -66293,19 +73725,31 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVers
}
#endif
- /* Any read-only or read-write transaction implies a read-lock on
- ** page 1. So if some other shared-cache client already has a write-lock
+ /* Any read-only or read-write transaction implies a read-lock on
+ ** page 1. So if some other shared-cache client already has a write-lock
** on page 1, the transaction cannot be opened. */
- rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);
+ rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK);
if( SQLITE_OK!=rc ) goto trans_begun;
pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
do {
+ sqlite3PagerWalDb(pPager, p->db);
+
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* If transitioning from no transaction directly to a write transaction,
+ ** block for the WRITER lock first if possible. */
+ if( pBt->pPage1==0 && wrflag ){
+ assert( pBt->inTransaction==TRANS_NONE );
+ rc = sqlite3PagerWalWriteLock(pPager, 1);
+ if( rc!=SQLITE_BUSY && rc!=SQLITE_OK ) break;
+ }
+#endif
+
/* Call lockBtree() until either pBt->pPage1 is populated or
** lockBtree() returns something other than SQLITE_OK. lockBtree()
** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after
- ** reading page 1 it discovers that the page-size of the database
+ ** reading page 1 it discovers that the page-size of the database
** file is not pBt->pageSize. In this case lockBtree() will update
** pBt->pageSize to the page-size of the file on disk.
*/
@@ -66315,7 +73759,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVers
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
rc = SQLITE_READONLY;
}else{
- rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
+ rc = sqlite3PagerBegin(pPager, wrflag>1, sqlite3TempInMemory(p->db));
if( rc==SQLITE_OK ){
rc = newDatabase(pBt);
}else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){
@@ -66326,13 +73770,17 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVers
}
}
}
-
+
if( rc!=SQLITE_OK ){
+ (void)sqlite3PagerWalWriteLock(pPager, 0);
unlockBtreeIfUnused(pBt);
}
}while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
btreeInvokeBusyHandler(pBt) );
- sqlite3PagerResetLockTimeout(pBt->pPager);
+ sqlite3PagerWalDb(pPager, 0);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
+#endif
if( rc==SQLITE_OK ){
if( p->inTrans==TRANS_NONE ){
@@ -66361,7 +73809,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVers
/* If the db-size header field is incorrect (as it may be if an old
** client has been writing the database file), update it now. Doing
- ** this sooner rather than later means the database size can safely
+ ** this sooner rather than later means the database size can safely
** re-read the database size from page 1 if a savepoint or transaction
** rollback occurs within the transaction.
*/
@@ -66384,7 +73832,7 @@ trans_begun:
** open savepoints. If the second parameter is greater than 0 and
** the sub-journal is not already open, then it will be opened here.
*/
- rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
+ rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint);
}
}
@@ -66392,6 +73840,28 @@ trans_begun:
sqlite3BtreeLeave(p);
return rc;
}
+SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
+ BtShared *pBt;
+ if( p->sharable
+ || p->inTrans==TRANS_NONE
+ || (p->inTrans==TRANS_READ && wrflag!=0)
+ ){
+ return btreeBeginTrans(p,wrflag,pSchemaVersion);
+ }
+ pBt = p->pBt;
+ if( pSchemaVersion ){
+ *pSchemaVersion = get4byte(&pBt->pPage1->aData[40]);
+ }
+ if( wrflag ){
+ /* This call makes sure that the pager has the correct number of
+ ** open savepoints. If the second parameter is greater than 0 and
+ ** the sub-journal is not already open, then it will be opened here.
+ */
+ return sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
+ }else{
+ return SQLITE_OK;
+ }
+}
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -66415,7 +73885,7 @@ static int setChildPtrmaps(MemPage *pPage){
for(i=0; i<nCell; i++){
u8 *pCell = findCell(pPage, i);
- ptrmapPutOvflPtr(pPage, pCell, &rc);
+ ptrmapPutOvflPtr(pPage, pPage, pCell, &rc);
if( !pPage->leaf ){
Pgno childPgno = get4byte(pCell);
@@ -66436,7 +73906,7 @@ static int setChildPtrmaps(MemPage *pPage){
** that it points to iTo. Parameter eType describes the type of pointer to
** be modified, as follows:
**
-** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child
+** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child
** page of pPage.
**
** PTRMAP_OVERFLOW1: pPage is a btree-page. The pointer points at an overflow
@@ -66478,15 +73948,18 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
}
}
}else{
+ if( pCell+4 > pPage->aData+pPage->pBt->usableSize ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
if( get4byte(pCell)==iFrom ){
put4byte(pCell, iTo);
break;
}
}
}
-
+
if( i==nCell ){
- if( eType!=PTRMAP_BTREE ||
+ if( eType!=PTRMAP_BTREE ||
get4byte(&pPage->aData[pPage->hdrOffset+8])!=iFrom ){
return SQLITE_CORRUPT_PAGE(pPage);
}
@@ -66498,11 +73971,11 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
/*
-** Move the open database page pDbPage to location iFreePage in the
+** Move the open database page pDbPage to location iFreePage in the
** database. The pDbPage reference remains valid.
**
** The isCommit flag indicates that there is no need to remember that
-** the journal needs to be sync()ed before database page pDbPage->pgno
+** the journal needs to be sync()ed before database page pDbPage->pgno
** can be written to. The caller has already promised not to write to that
** page.
*/
@@ -66519,14 +73992,14 @@ static int relocatePage(
Pager *pPager = pBt->pPager;
int rc;
- assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 ||
+ assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 ||
eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE );
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pDbPage->pBt==pBt );
if( iDbPage<3 ) return SQLITE_CORRUPT_BKPT;
/* Move page iDbPage from its current location to page number iFreePage */
- TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n",
+ TRACE(("AUTOVACUUM: Moving %u to free page %u (ptr page %u type %u)\n",
iDbPage, iFreePage, iPtrPage, eType));
rc = sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage, isCommit);
if( rc!=SQLITE_OK ){
@@ -66585,19 +74058,19 @@ static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8);
/*
** Perform a single step of an incremental-vacuum. If successful, return
-** SQLITE_OK. If there is no work to do (and therefore no point in
-** calling this function again), return SQLITE_DONE. Or, if an error
+** SQLITE_OK. If there is no work to do (and therefore no point in
+** calling this function again), return SQLITE_DONE. Or, if an error
** occurs, return some other error code.
**
-** More specifically, this function attempts to re-organize the database so
+** More specifically, this function attempts to re-organize the database so
** that the last page of the file currently in use is no longer in use.
**
** Parameter nFin is the number of pages that this database would contain
** were this function called until it returns SQLITE_DONE.
**
-** If the bCommit parameter is non-zero, this function assumes that the
-** caller will keep calling incrVacuumStep() until it returns SQLITE_DONE
-** or an error. bCommit is passed true for an auto-vacuum-on-commit
+** If the bCommit parameter is non-zero, this function assumes that the
+** caller will keep calling incrVacuumStep() until it returns SQLITE_DONE
+** or an error. bCommit is passed true for an auto-vacuum-on-commit
** operation, or false for an incremental vacuum.
*/
static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
@@ -66628,7 +74101,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
if( bCommit==0 ){
/* Remove the page from the files free-list. This is not required
** if bCommit is non-zero. In that case, the free-list will be
- ** truncated to zero after this function returns, so it doesn't
+ ** truncated to zero after this function returns, so it doesn't
** matter if it still contains some garbage entries.
*/
Pgno iFreePg;
@@ -66664,15 +74137,20 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
}
do {
MemPage *pFreePg;
+ Pgno dbSize = btreePagecount(pBt);
rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode);
if( rc!=SQLITE_OK ){
releasePage(pLastPg);
return rc;
}
releasePage(pFreePg);
+ if( iFreePg>dbSize ){
+ releasePage(pLastPg);
+ return SQLITE_CORRUPT_BKPT;
+ }
}while( bCommit && iFreePg>nFin );
assert( iFreePg<iLastPg );
-
+
rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, bCommit);
releasePage(pLastPg);
if( rc!=SQLITE_OK ){
@@ -66693,7 +74171,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
/*
** The database opened by the first argument is an auto-vacuum database
-** nOrig pages in size containing nFree free pages. Return the expected
+** nOrig pages in size containing nFree free pages. Return the expected
** size of the database in pages following an auto-vacuum operation.
*/
static Pgno finalDbSize(BtShared *pBt, Pgno nOrig, Pgno nFree){
@@ -66720,7 +74198,7 @@ static Pgno finalDbSize(BtShared *pBt, Pgno nOrig, Pgno nFree){
**
** If the incremental vacuum is finished after this function has run,
** SQLITE_DONE is returned. If it is not finished, but no error occurred,
-** SQLITE_OK is returned. Otherwise an SQLite error code.
+** SQLITE_OK is returned. Otherwise an SQLite error code.
*/
SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
int rc;
@@ -66735,7 +74213,7 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
Pgno nFree = get4byte(&pBt->pPage1->aData[36]);
Pgno nFin = finalDbSize(pBt, nOrig, nFree);
- if( nOrig<nFin ){
+ if( nOrig<nFin || nFree>=nOrig ){
rc = SQLITE_CORRUPT_BKPT;
}else if( nFree>0 ){
rc = saveAllCursors(pBt, 0, 0);
@@ -66758,16 +74236,18 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
/*
** This routine is called prior to sqlite3PagerCommit when a transaction
** is committed for an auto-vacuum database.
-**
-** If SQLITE_OK is returned, then *pnTrunc is set to the number of pages
-** the database file should be truncated to during the commit process.
-** i.e. the database has been reorganized so that only the first *pnTrunc
-** pages are in use.
*/
-static int autoVacuumCommit(BtShared *pBt){
+static int autoVacuumCommit(Btree *p){
int rc = SQLITE_OK;
- Pager *pPager = pBt->pPager;
- VVA_ONLY( int nRef = sqlite3PagerRefcount(pPager); )
+ Pager *pPager;
+ BtShared *pBt;
+ sqlite3 *db;
+ VVA_ONLY( int nRef );
+
+ assert( p!=0 );
+ pBt = p->pBt;
+ pPager = pBt->pPager;
+ VVA_ONLY( nRef = sqlite3PagerRefcount(pPager); )
assert( sqlite3_mutex_held(pBt->mutex) );
invalidateAllOverflowCache(pBt);
@@ -66775,6 +74255,7 @@ static int autoVacuumCommit(BtShared *pBt){
if( !pBt->incrVacuum ){
Pgno nFin; /* Number of pages in database after autovacuuming */
Pgno nFree; /* Number of pages on the freelist initially */
+ Pgno nVac; /* Number of pages to vacuum */
Pgno iFree; /* The next page to be freed */
Pgno nOrig; /* Database size before freeing */
@@ -66788,18 +74269,42 @@ static int autoVacuumCommit(BtShared *pBt){
}
nFree = get4byte(&pBt->pPage1->aData[36]);
- nFin = finalDbSize(pBt, nOrig, nFree);
+ db = p->db;
+ if( db->xAutovacPages ){
+ int iDb;
+ for(iDb=0; ALWAYS(iDb<db->nDb); iDb++){
+ if( db->aDb[iDb].pBt==p ) break;
+ }
+ nVac = db->xAutovacPages(
+ db->pAutovacPagesArg,
+ db->aDb[iDb].zDbSName,
+ nOrig,
+ nFree,
+ pBt->pageSize
+ );
+ if( nVac>nFree ){
+ nVac = nFree;
+ }
+ if( nVac==0 ){
+ return SQLITE_OK;
+ }
+ }else{
+ nVac = nFree;
+ }
+ nFin = finalDbSize(pBt, nOrig, nVac);
if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT;
if( nFin<nOrig ){
rc = saveAllCursors(pBt, 0, 0);
}
for(iFree=nOrig; iFree>nFin && rc==SQLITE_OK; iFree--){
- rc = incrVacuumStep(pBt, nFin, iFree, 1);
+ rc = incrVacuumStep(pBt, nFin, iFree, nVac==nFree);
}
if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
- put4byte(&pBt->pPage1->aData[32], 0);
- put4byte(&pBt->pPage1->aData[36], 0);
+ if( nVac==nFree ){
+ put4byte(&pBt->pPage1->aData[32], 0);
+ put4byte(&pBt->pPage1->aData[36], 0);
+ }
put4byte(&pBt->pPage1->aData[28], nFin);
pBt->bDoTruncate = 1;
pBt->nPage = nFin;
@@ -66832,25 +74337,25 @@ static int autoVacuumCommit(BtShared *pBt){
**
** This call is a no-op if no write-transaction is currently active on pBt.
**
-** Otherwise, sync the database file for the btree pBt. zMaster points to
-** the name of a master journal file that should be written into the
-** individual journal file, or is NULL, indicating no master journal file
+** Otherwise, sync the database file for the btree pBt. zSuperJrnl points to
+** the name of a super-journal file that should be written into the
+** individual journal file, or is NULL, indicating no super-journal file
** (single database transaction).
**
-** When this is called, the master journal should already have been
+** When this is called, the super-journal should already have been
** created, populated with this journal pointer and synced to disk.
**
** Once this is routine has returned, the only thing required to commit
** the write-transaction for this database file is to delete the journal.
*/
-SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
+SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){
int rc = SQLITE_OK;
if( p->inTrans==TRANS_WRITE ){
BtShared *pBt = p->pBt;
sqlite3BtreeEnter(p);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
- rc = autoVacuumCommit(pBt);
+ rc = autoVacuumCommit(p);
if( rc!=SQLITE_OK ){
sqlite3BtreeLeave(p);
return rc;
@@ -66860,7 +74365,7 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
}
#endif
- rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
+ rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zSuperJrnl, 0);
sqlite3BtreeLeave(p);
}
return rc;
@@ -66885,8 +74390,8 @@ static void btreeEndTransaction(Btree *p){
downgradeAllSharedCacheTableLocks(p);
p->inTrans = TRANS_READ;
}else{
- /* If the handle had any kind of transaction open, decrement the
- ** transaction count of the shared btree. If the transaction count
+ /* If the handle had any kind of transaction open, decrement the
+ ** transaction count of the shared btree. If the transaction count
** reaches 0, set the shared state to TRANS_NONE. The unlockBtreeIfUnused()
** call below will unlock the pager. */
if( p->inTrans!=TRANS_NONE ){
@@ -66897,7 +74402,7 @@ static void btreeEndTransaction(Btree *p){
}
}
- /* Set the current transaction state to TRANS_NONE and unlock the
+ /* Set the current transaction state to TRANS_NONE and unlock the
** pager if this call closed the only read or write transaction. */
p->inTrans = TRANS_NONE;
unlockBtreeIfUnused(pBt);
@@ -66918,12 +74423,12 @@ static void btreeEndTransaction(Btree *p){
** the rollback journal (which causes the transaction to commit) and
** drop locks.
**
-** Normally, if an error occurs while the pager layer is attempting to
+** Normally, if an error occurs while the pager layer is attempting to
** finalize the underlying journal file, this function returns an error and
** the upper layer will attempt a rollback. However, if the second argument
-** is non-zero then this b-tree transaction is part of a multi-file
-** transaction. In this case, the transaction has already been committed
-** (by deleting a master journal file) and the caller will ignore this
+** is non-zero then this b-tree transaction is part of a multi-file
+** transaction. In this case, the transaction has already been committed
+** (by deleting a super-journal file) and the caller will ignore this
** functions return code. So, even if an error occurs in the pager layer,
** reset the b-tree objects internal state to indicate that the write
** transaction has been closed. This is quite safe, as the pager will have
@@ -66938,7 +74443,7 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){
sqlite3BtreeEnter(p);
btreeIntegrity(p);
- /* If the handle has a write-transaction open, commit the shared-btrees
+ /* If the handle has a write-transaction open, commit the shared-btrees
** transaction and set the shared state to TRANS_READ.
*/
if( p->inTrans==TRANS_WRITE ){
@@ -66951,7 +74456,7 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){
sqlite3BtreeLeave(p);
return rc;
}
- p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */
+ p->iBDataVersion--; /* Compensate for pPager->iDataVersion++; */
pBt->inTransaction = TRANS_READ;
btreeClearHasContent(pBt);
}
@@ -66987,15 +74492,15 @@ SQLITE_PRIVATE int sqlite3BtreeCommit(Btree *p){
**
** This routine gets called when a rollback occurs. If the writeOnly
** flag is true, then only write-cursors need be tripped - read-only
-** cursors save their current positions so that they may continue
-** following the rollback. Or, if writeOnly is false, all cursors are
+** cursors save their current positions so that they may continue
+** following the rollback. Or, if writeOnly is false, all cursors are
** tripped. In general, writeOnly is false if the transaction being
** rolled back modified the database schema. In this case b-tree root
** pages may be moved or deleted from the database altogether, making
** it unsafe for read cursors to continue.
**
-** If the writeOnly flag is true and an error is encountered while
-** saving the current position of a read-only cursor, all cursors,
+** If the writeOnly flag is true and an error is encountered while
+** saving the current position of a read-only cursor, all cursors,
** including all read-cursors are tripped.
**
** SQLITE_OK is returned if successful, or if an error occurs while
@@ -67030,6 +74535,18 @@ SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int wr
}
/*
+** Set the pBt->nPage field correctly, according to the current
+** state of the database. Assume pBt->pPage1 is valid.
+*/
+static void btreeSetNPage(BtShared *pBt, MemPage *pPage1){
+ int nPage = get4byte(&pPage1->aData[28]);
+ testcase( nPage==0 );
+ if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage);
+ testcase( pBt->nPage!=(u32)nPage );
+ pBt->nPage = nPage;
+}
+
+/*
** Rollback the transaction in progress.
**
** If tripCode is not SQLITE_OK then cursors will be invalidated (tripped).
@@ -67074,11 +74591,7 @@ SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly){
** call btreeGetPage() on page 1 again to make
** sure pPage1->aData is set correctly. */
if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){
- int nPage = get4byte(28+(u8*)pPage1->aData);
- testcase( nPage==0 );
- if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage);
- testcase( pBt->nPage!=nPage );
- pBt->nPage = nPage;
+ btreeSetNPage(pBt, pPage1);
releasePageOne(pPage1);
}
assert( countValidCursors(pBt, 1)==0 );
@@ -67093,8 +74606,8 @@ SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly){
/*
** Start a statement subtransaction. The subtransaction can be rolled
-** back independently of the main transaction. You must start a transaction
-** before starting a subtransaction. The subtransaction is ended automatically
+** back independently of the main transaction. You must start a transaction
+** before starting a subtransaction. The subtransaction is ended automatically
** if the main transaction commits or rolls back.
**
** Statement subtransactions are used around individual SQL statements
@@ -67131,11 +74644,11 @@ SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree *p, int iStatement){
/*
** The second argument to this function, op, is always SAVEPOINT_ROLLBACK
** or SAVEPOINT_RELEASE. This function either releases or rolls back the
-** savepoint identified by parameter iSavepoint, depending on the value
+** savepoint identified by parameter iSavepoint, depending on the value
** of op.
**
** Normally, iSavepoint is greater than or equal to zero. However, if op is
-** SAVEPOINT_ROLLBACK, then iSavepoint may also be -1. In this case the
+** SAVEPOINT_ROLLBACK, then iSavepoint may also be -1. In this case the
** contents of the entire transaction are rolled back. This is different
** from a normal transaction rollback, as no locks are released and the
** transaction remains open.
@@ -67158,12 +74671,11 @@ SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
pBt->nPage = 0;
}
rc = newDatabase(pBt);
- pBt->nPage = get4byte(28 + pBt->pPage1->aData);
+ btreeSetNPage(pBt, pBt->pPage1);
- /* The database size was written into the offset 28 of the header
- ** when the transaction started, so we know that the value at offset
- ** 28 is nonzero. */
- assert( pBt->nPage>0 );
+ /* pBt->nPage might be zero if the database was corrupt when
+ ** the transaction was started. Otherwise, it must be at least 1. */
+ assert( CORRUPT_DB || pBt->nPage>0 );
}
sqlite3BtreeLeave(p);
}
@@ -67199,10 +74711,10 @@ SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
** is set. If FORDELETE is set, that is a hint to the implementation that
** this cursor will only be used to seek to and delete entries of an index
** as part of a larger DELETE statement. The FORDELETE hint is not used by
-** this implementation. But in a hypothetical alternative storage engine
+** this implementation. But in a hypothetical alternative storage engine
** in which index entries are automatically deleted when corresponding table
** rows are deleted, the FORDELETE flag is a hint that all SEEK and DELETE
-** operations on this cursor can be no-ops and all READ operations can
+** operations on this cursor can be no-ops and all READ operations can
** return a null row (2-bytes: 0x01 0x00).
**
** No checking is done to make sure that page iTable really is the
@@ -67214,7 +74726,7 @@ SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
*/
static int btreeCursor(
Btree *p, /* The btree */
- int iTable, /* Root page of table to open */
+ Pgno iTable, /* Root page of table to open */
int wrFlag, /* 1 to write. 0 read-only */
struct KeyInfo *pKeyInfo, /* First arg to comparison function */
BtCursor *pCur /* Space for new cursor */
@@ -67223,16 +74735,17 @@ static int btreeCursor(
BtCursor *pX; /* Looping over other all cursors */
assert( sqlite3BtreeHoldsMutex(p) );
- assert( wrFlag==0
- || wrFlag==BTREE_WRCSR
- || wrFlag==(BTREE_WRCSR|BTREE_FORDELETE)
+ assert( wrFlag==0
+ || wrFlag==BTREE_WRCSR
+ || wrFlag==(BTREE_WRCSR|BTREE_FORDELETE)
);
- /* The following assert statements verify that if this is a sharable
- ** b-tree database, the connection is holding the required table locks,
- ** and that no other connection has any open cursor that conflicts with
- ** this lock. */
- assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, (wrFlag?2:1)) );
+ /* The following assert statements verify that if this is a sharable
+ ** b-tree database, the connection is holding the required table locks,
+ ** and that no other connection has any open cursor that conflicts with
+ ** this lock. The iTable<1 term disables the check for corrupt schemas. */
+ assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, (wrFlag?2:1))
+ || iTable<1 );
assert( wrFlag==0 || !hasReadConflicts(p, iTable) );
/* Assert that the caller has opened the required transaction. */
@@ -67241,53 +74754,68 @@ static int btreeCursor(
assert( pBt->pPage1 && pBt->pPage1->aData );
assert( wrFlag==0 || (pBt->btsFlags & BTS_READ_ONLY)==0 );
- if( wrFlag ){
- allocateTempSpace(pBt);
- if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM_BKPT;
- }
- if( iTable==1 && btreePagecount(pBt)==0 ){
- assert( wrFlag==0 );
- iTable = 0;
+ if( iTable<=1 ){
+ if( iTable<1 ){
+ return SQLITE_CORRUPT_BKPT;
+ }else if( btreePagecount(pBt)==0 ){
+ assert( wrFlag==0 );
+ iTable = 0;
+ }
}
/* Now that no other errors can occur, finish filling in the BtCursor
** variables and link the cursor into the BtShared list. */
- pCur->pgnoRoot = (Pgno)iTable;
+ pCur->pgnoRoot = iTable;
pCur->iPage = -1;
pCur->pKeyInfo = pKeyInfo;
pCur->pBtree = p;
pCur->pBt = pBt;
- pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0;
- pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY;
+ pCur->curFlags = 0;
/* If there are two or more cursors on the same btree, then all such
** cursors *must* have the BTCF_Multiple flag set. */
for(pX=pBt->pCursor; pX; pX=pX->pNext){
- if( pX->pgnoRoot==(Pgno)iTable ){
+ if( pX->pgnoRoot==iTable ){
pX->curFlags |= BTCF_Multiple;
- pCur->curFlags |= BTCF_Multiple;
+ pCur->curFlags = BTCF_Multiple;
}
}
+ pCur->eState = CURSOR_INVALID;
pCur->pNext = pBt->pCursor;
pBt->pCursor = pCur;
- pCur->eState = CURSOR_INVALID;
+ if( wrFlag ){
+ pCur->curFlags |= BTCF_WriteFlag;
+ pCur->curPagerFlags = 0;
+ if( pBt->pTmpSpace==0 ) return allocateTempSpace(pBt);
+ }else{
+ pCur->curPagerFlags = PAGER_GET_READONLY;
+ }
return SQLITE_OK;
}
+static int btreeCursorWithLock(
+ Btree *p, /* The btree */
+ Pgno iTable, /* Root page of table to open */
+ int wrFlag, /* 1 to write. 0 read-only */
+ struct KeyInfo *pKeyInfo, /* First arg to comparison function */
+ BtCursor *pCur /* Space for new cursor */
+){
+ int rc;
+ sqlite3BtreeEnter(p);
+ rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
+ sqlite3BtreeLeave(p);
+ return rc;
+}
SQLITE_PRIVATE int sqlite3BtreeCursor(
Btree *p, /* The btree */
- int iTable, /* Root page of table to open */
+ Pgno iTable, /* Root page of table to open */
int wrFlag, /* 1 to write. 0 read-only */
struct KeyInfo *pKeyInfo, /* First arg to xCompare() */
BtCursor *pCur /* Write new cursor here */
){
- int rc;
- if( iTable<1 ){
- rc = SQLITE_CORRUPT_BKPT;
+ if( p->sharable ){
+ return btreeCursorWithLock(p, iTable, wrFlag, pKeyInfo, pCur);
}else{
- sqlite3BtreeEnter(p);
- rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
- sqlite3BtreeLeave(p);
+ return btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
}
- return rc;
}
/*
@@ -67340,7 +74868,15 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
unlockBtreeIfUnused(pBt);
sqlite3_free(pCur->aOverflow);
sqlite3_free(pCur->pKey);
- sqlite3BtreeLeave(pBtree);
+ if( (pBt->openFlags & BTREE_SINGLE) && pBt->pCursor==0 ){
+ /* Since the BtShared is not sharable, there is no need to
+ ** worry about the missing sqlite3BtreeLeave() call here. */
+ assert( pBtree->sharable==0 );
+ sqlite3BtreeClose(pBtree);
+ }else{
+ sqlite3BtreeLeave(pBtree);
+ }
+ pCur->pBtree = 0;
}
return SQLITE_OK;
}
@@ -67409,7 +74945,18 @@ SQLITE_PRIVATE i64 sqlite3BtreeIntegerKey(BtCursor *pCur){
return pCur->info.nKey;
}
-#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
+/*
+** Pin or unpin a cursor.
+*/
+SQLITE_PRIVATE void sqlite3BtreeCursorPin(BtCursor *pCur){
+ assert( (pCur->curFlags & BTCF_Pinned)==0 );
+ pCur->curFlags |= BTCF_Pinned;
+}
+SQLITE_PRIVATE void sqlite3BtreeCursorUnpin(BtCursor *pCur){
+ assert( (pCur->curFlags & BTCF_Pinned)!=0 );
+ pCur->curFlags &= ~BTCF_Pinned;
+}
+
/*
** Return the offset into the database file for the start of the
** payload to which the cursor is pointing.
@@ -67421,7 +74968,6 @@ SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor *pCur){
return (i64)pCur->pBt->pageSize*((i64)pCur->pPage->pgno - 1) +
(i64)(pCur->info.pPayload - pCur->pPage->aData);
}
-#endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */
/*
** Return the number of bytes of payload for the entry that pCur is
@@ -67440,16 +74986,35 @@ SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor *pCur){
}
/*
+** Return an upper bound on the size of any record for the table
+** that the cursor is pointing into.
+**
+** This is an optimization. Everything will still work if this
+** routine always returns 2147483647 (which is the largest record
+** that SQLite can handle) or more. But returning a smaller value might
+** prevent large memory allocations when trying to interpret a
+** corrupt database.
+**
+** The current implementation merely returns the size of the underlying
+** database file.
+*/
+SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor *pCur){
+ assert( cursorHoldsMutex(pCur) );
+ assert( pCur->eState==CURSOR_VALID );
+ return pCur->pBt->pageSize * (sqlite3_int64)pCur->pBt->nPage;
+}
+
+/*
** Given the page number of an overflow page in the database (parameter
-** ovfl), this function finds the page number of the next page in the
+** ovfl), this function finds the page number of the next page in the
** linked list of overflow pages. If possible, it uses the auto-vacuum
-** pointer-map data instead of reading the content of page ovfl to do so.
+** pointer-map data instead of reading the content of page ovfl to do so.
**
** If an error occurs an SQLite error code is returned. Otherwise:
**
-** The page number of the next overflow page in the linked list is
-** written to *pPgnoNext. If page ovfl is the last page in its linked
-** list, *pPgnoNext is set to zero.
+** The page number of the next overflow page in the linked list is
+** written to *pPgnoNext. If page ovfl is the last page in its linked
+** list, *pPgnoNext is set to zero.
**
** If ppPage is not NULL, and a reference to the MemPage object corresponding
** to page number pOvfl was obtained, then *ppPage is set to point to that
@@ -67473,9 +75038,9 @@ static int getOverflowPage(
#ifndef SQLITE_OMIT_AUTOVACUUM
/* Try to find the next page in the overflow list using the
- ** autovacuum pointer-map pages. Guess that the next page in
- ** the overflow list is page number (ovfl+1). If that guess turns
- ** out to be wrong, fall back to loading the data of page
+ ** autovacuum pointer-map pages. Guess that the next page in
+ ** the overflow list is page number (ovfl+1). If that guess turns
+ ** out to be wrong, fall back to loading the data of page
** number ovfl to determine the next page number.
*/
if( pBt->autoVacuum ){
@@ -67563,8 +75128,8 @@ static int copyPayload(
**
** If the current cursor entry uses one or more overflow pages
** this function may allocate space for and lazily populate
-** the overflow page-list cache array (BtCursor.aOverflow).
-** Subsequent calls use this cache to make seeking to the supplied offset
+** the overflow page-list cache array (BtCursor.aOverflow).
+** Subsequent calls use this cache to make seeking to the supplied offset
** more efficient.
**
** Once an overflow page-list cache has been allocated, it must be
@@ -67580,7 +75145,7 @@ static int accessPayload(
BtCursor *pCur, /* Cursor pointing to entry to read from */
u32 offset, /* Begin reading this far into payload */
u32 amt, /* Read this many bytes */
- unsigned char *pBuf, /* Write the bytes into this buffer */
+ unsigned char *pBuf, /* Write the bytes into this buffer */
int eOp /* zero to read. non-zero to write. */
){
unsigned char *aPayload;
@@ -67595,7 +75160,9 @@ static int accessPayload(
assert( pPage );
assert( eOp==0 || eOp==1 );
assert( pCur->eState==CURSOR_VALID );
- assert( pCur->ix<pPage->nCell );
+ if( pCur->ix>=pPage->nCell ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
assert( cursorHoldsMutex(pCur) );
getCellInfo(pCur);
@@ -67671,6 +75238,7 @@ static int accessPayload(
assert( rc==SQLITE_OK && amt>0 );
while( nextPage ){
/* If required, populate the overflow page-list cache. */
+ if( nextPage > pBt->nPage ) return SQLITE_CORRUPT_BKPT;
assert( pCur->aOverflow[iIdx]==0
|| pCur->aOverflow[iIdx]==nextPage
|| CORRUPT_DB );
@@ -67703,12 +75271,12 @@ static int accessPayload(
#ifdef SQLITE_DIRECT_OVERFLOW_READ
/* If all the following are true:
**
- ** 1) this is a read operation, and
+ ** 1) this is a read operation, and
** 2) data is required from the start of this overflow page, and
** 3) there are no dirty pages in the page-cache
** 4) the database is file-backed, and
** 5) the page is not in the WAL file
- ** 6) at least 4 bytes have already been read into the output buffer
+ ** 6) at least 4 bytes have already been read into the output buffer
**
** then data can be read directly from the database file into the
** output buffer, bypassing the page-cache altogether. This speeds
@@ -67780,7 +75348,6 @@ SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor *pCur, u32 offset, u32 amt, void
assert( cursorHoldsMutex(pCur) );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->iPage>=0 && pCur->pPage );
- assert( pCur->ix<pCur->pPage->nCell );
return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
}
@@ -67815,7 +75382,7 @@ SQLITE_PRIVATE int sqlite3BtreePayloadChecked(BtCursor *pCur, u32 offset, u32 am
#endif /* SQLITE_OMIT_INCRBLOB */
/*
-** Return a pointer to payload information from the entry that the
+** Return a pointer to payload information from the entry that the
** pCur cursor is pointing to. The pointer is to the beginning of
** the key if index btrees (pPage->intKey==0) and is the data for
** table btrees (pPage->intKey==1). The number of bytes of available
@@ -67842,7 +75409,7 @@ static const void *fetchPayload(
assert( pCur->eState==CURSOR_VALID );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert( cursorOwnsBtShared(pCur) );
- assert( pCur->ix<pCur->pPage->nCell );
+ assert( pCur->ix<pCur->pPage->nCell || CORRUPT_DB );
assert( pCur->info.nSize>0 );
assert( pCur->info.pPayload>pCur->pPage->aData || CORRUPT_DB );
assert( pCur->info.pPayload<pCur->pPage->aDataEnd ||CORRUPT_DB);
@@ -67887,8 +75454,7 @@ SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){
** vice-versa).
*/
static int moveToChild(BtCursor *pCur, u32 newPgno){
- BtShared *pBt = pCur->pBt;
-
+ int rc;
assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->iPage<BTCURSOR_MAX_DEPTH );
@@ -67902,12 +75468,23 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
pCur->apPage[pCur->iPage] = pCur->pPage;
pCur->ix = 0;
pCur->iPage++;
- return getAndInitPage(pBt, newPgno, &pCur->pPage, pCur, pCur->curPagerFlags);
+ rc = getAndInitPage(pCur->pBt, newPgno, &pCur->pPage, pCur->curPagerFlags);
+ assert( pCur->pPage!=0 || rc!=SQLITE_OK );
+ if( rc==SQLITE_OK
+ && (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey)
+ ){
+ releasePage(pCur->pPage);
+ rc = SQLITE_CORRUPT_PGNO(newPgno);
+ }
+ if( rc ){
+ pCur->pPage = pCur->apPage[--pCur->iPage];
+ }
+ return rc;
}
#ifdef SQLITE_DEBUG
/*
-** Page pParent is an internal (non-leaf) tree page. This function
+** Page pParent is an internal (non-leaf) tree page. This function
** asserts that page number iChild is the left-child if the iIdx'th
** cell in page pParent. Or, if iIdx is equal to the total number of
** cells in pParent, that page number iChild is the right-child of
@@ -67924,7 +75501,7 @@ static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){
}
}
#else
-# define assertParentIndex(x,y,z)
+# define assertParentIndex(x,y,z)
#endif
/*
@@ -67942,8 +75519,8 @@ static void moveToParent(BtCursor *pCur){
assert( pCur->iPage>0 );
assert( pCur->pPage );
assertParentIndex(
- pCur->apPage[pCur->iPage-1],
- pCur->aiIdx[pCur->iPage-1],
+ pCur->apPage[pCur->iPage-1],
+ pCur->aiIdx[pCur->iPage-1],
pCur->pPage->pgno
);
testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell );
@@ -67960,19 +75537,19 @@ static void moveToParent(BtCursor *pCur){
**
** If the table has a virtual root page, then the cursor is moved to point
** to the virtual root page instead of the actual root page. A table has a
-** virtual root page when the actual root page contains no cells and a
+** virtual root page when the actual root page contains no cells and a
** single child page. This can only happen with the table rooted at page 1.
**
-** If the b-tree structure is empty, the cursor state is set to
+** If the b-tree structure is empty, the cursor state is set to
** CURSOR_INVALID and this routine returns SQLITE_EMPTY. Otherwise,
** the cursor is set to point to the first cell located on the root
** (or virtual root) page and the cursor state is set to CURSOR_VALID.
**
** If this function returns successfully, it may be assumed that the
-** page-header flags indicate that the [virtual] root-page is the expected
+** page-header flags indicate that the [virtual] root-page is the expected
** kind of b-tree page (i.e. if when opening the cursor the caller did not
** specify a KeyInfo structure the flags byte is set to 0x05 or 0x0D,
-** indicating a table b-tree, or if the caller did specify a KeyInfo
+** indicating a table b-tree, or if the caller did specify a KeyInfo
** structure the flags byte is set to 0x02 or 0x0A, indicating an index
** b-tree).
*/
@@ -67993,7 +75570,7 @@ static int moveToRoot(BtCursor *pCur){
while( --pCur->iPage ){
releasePageNotNull(pCur->apPage[pCur->iPage]);
}
- pCur->pPage = pCur->apPage[0];
+ pRoot = pCur->pPage = pCur->apPage[0];
goto skip_init;
}
}else if( pCur->pgnoRoot==0 ){
@@ -68008,8 +75585,8 @@ static int moveToRoot(BtCursor *pCur){
}
sqlite3BtreeClearCursor(pCur);
}
- rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->pPage,
- 0, pCur->curPagerFlags);
+ rc = getAndInitPage(pCur->pBt, pCur->pgnoRoot, &pCur->pPage,
+ pCur->curPagerFlags);
if( rc!=SQLITE_OK ){
pCur->eState = CURSOR_INVALID;
return rc;
@@ -68018,29 +75595,28 @@ static int moveToRoot(BtCursor *pCur){
pCur->curIntKey = pCur->pPage->intKey;
}
pRoot = pCur->pPage;
- assert( pRoot->pgno==pCur->pgnoRoot );
+ assert( pRoot->pgno==pCur->pgnoRoot || CORRUPT_DB );
/* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
** NULL, the caller expects a table b-tree. If this is not the case,
- ** return an SQLITE_CORRUPT error.
+ ** return an SQLITE_CORRUPT error.
**
** Earlier versions of SQLite assumed that this test could not fail
** if the root page was already loaded when this function was called (i.e.
- ** if pCur->iPage>=0). But this is not so if the database is corrupted
- ** in such a way that page pRoot is linked into a second b-tree table
+ ** if pCur->iPage>=0). But this is not so if the database is corrupted
+ ** in such a way that page pRoot is linked into a second b-tree table
** (or the freelist). */
assert( pRoot->intKey==1 || pRoot->intKey==0 );
if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){
return SQLITE_CORRUPT_PAGE(pCur->pPage);
}
-skip_init:
+skip_init:
pCur->ix = 0;
pCur->info.nSize = 0;
pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl);
- pRoot = pCur->pPage;
if( pRoot->nCell>0 ){
pCur->eState = CURSOR_VALID;
}else if( !pRoot->leaf ){
@@ -68122,79 +75698,61 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
*pRes = 0;
rc = moveToLeftmost(pCur);
}else if( rc==SQLITE_EMPTY ){
- assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
+ assert( pCur->pgnoRoot==0 || (pCur->pPage!=0 && pCur->pPage->nCell==0) );
*pRes = 1;
rc = SQLITE_OK;
}
return rc;
}
-/*
-** This function is a no-op if cursor pCur does not point to a valid row.
-** Otherwise, if pCur is valid, configure it so that the next call to
-** sqlite3BtreeNext() is a no-op.
-*/
-#ifndef SQLITE_OMIT_WINDOWFUNC
-SQLITE_PRIVATE void sqlite3BtreeSkipNext(BtCursor *pCur){
- /* We believe that the cursor must always be in the valid state when
- ** this routine is called, but the proof is difficult, so we add an
- ** ALWaYS() test just in case we are wrong. */
- if( ALWAYS(pCur->eState==CURSOR_VALID) ){
- pCur->eState = CURSOR_SKIPNEXT;
- pCur->skipNext = 1;
- }
-}
-#endif /* SQLITE_OMIT_WINDOWFUNC */
-
/* Move the cursor to the last entry in the table. Return SQLITE_OK
** on success. Set *pRes to 0 if the cursor actually points to something
** or set *pRes to 1 if the table is empty.
*/
+static SQLITE_NOINLINE int btreeLast(BtCursor *pCur, int *pRes){
+ int rc = moveToRoot(pCur);
+ if( rc==SQLITE_OK ){
+ assert( pCur->eState==CURSOR_VALID );
+ *pRes = 0;
+ rc = moveToRightmost(pCur);
+ if( rc==SQLITE_OK ){
+ pCur->curFlags |= BTCF_AtLast;
+ }else{
+ pCur->curFlags &= ~BTCF_AtLast;
+ }
+ }else if( rc==SQLITE_EMPTY ){
+ assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
+ *pRes = 1;
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
- int rc;
-
assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
/* If the cursor already points to the last entry, this is a no-op. */
if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){
#ifdef SQLITE_DEBUG
- /* This block serves to assert() that the cursor really does point
+ /* This block serves to assert() that the cursor really does point
** to the last entry in the b-tree. */
int ii;
for(ii=0; ii<pCur->iPage; ii++){
assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell );
}
- assert( pCur->ix==pCur->pPage->nCell-1 );
+ assert( pCur->ix==pCur->pPage->nCell-1 || CORRUPT_DB );
+ testcase( pCur->ix!=pCur->pPage->nCell-1 );
+ /* ^-- dbsqlfuzz b92b72e4de80b5140c30ab71372ca719b8feb618 */
assert( pCur->pPage->leaf );
#endif
- return SQLITE_OK;
- }
-
- rc = moveToRoot(pCur);
- if( rc==SQLITE_OK ){
- assert( pCur->eState==CURSOR_VALID );
*pRes = 0;
- rc = moveToRightmost(pCur);
- if( rc==SQLITE_OK ){
- pCur->curFlags |= BTCF_AtLast;
- }else{
- pCur->curFlags &= ~BTCF_AtLast;
- }
- }else if( rc==SQLITE_EMPTY ){
- assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
- *pRes = 1;
- rc = SQLITE_OK;
+ return SQLITE_OK;
}
- return rc;
+ return btreeLast(pCur, pRes);
}
-/* Move the cursor so that it points to an entry near the key
-** specified by pIdxKey or intKey. Return a success code.
-**
-** For INTKEY tables, the intKey parameter is used. pIdxKey
-** must be NULL. For index tables, pIdxKey is used and intKey
-** is ignored.
+/* Move the cursor so that it points to an entry in a table (a.k.a INTKEY)
+** table near the key intKey. Return a success code.
**
** If an exact match is not found, then the cursor is always
** left pointing at a leaf page which would hold the entry if it
@@ -68202,44 +75760,37 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
** before or after the key.
**
** An integer is written into *pRes which is the result of
-** comparing the key with the entry to which the cursor is
+** comparing the key with the entry to which the cursor is
** pointing. The meaning of the integer written into
** *pRes is as follows:
**
** *pRes<0 The cursor is left pointing at an entry that
-** is smaller than intKey/pIdxKey or if the table is empty
+** is smaller than intKey or if the table is empty
** and the cursor is therefore left point to nothing.
**
** *pRes==0 The cursor is left pointing at an entry that
-** exactly matches intKey/pIdxKey.
+** exactly matches intKey.
**
** *pRes>0 The cursor is left pointing at an entry that
-** is larger than intKey/pIdxKey.
-**
-** For index tables, the pIdxKey->eqSeen field is set to 1 if there
-** exists an entry in the table that exactly matches pIdxKey.
+** is larger than intKey.
*/
-SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
+SQLITE_PRIVATE int sqlite3BtreeTableMoveto(
BtCursor *pCur, /* The cursor to be moved */
- UnpackedRecord *pIdxKey, /* Unpacked index key */
i64 intKey, /* The table key */
int biasRight, /* If true, bias the search to the high end */
int *pRes /* Write search results here */
){
int rc;
- RecordCompare xRecordCompare;
assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert( pRes );
- assert( (pIdxKey==0)==(pCur->pKeyInfo==0) );
- assert( pCur->eState!=CURSOR_VALID || (pIdxKey==0)==(pCur->curIntKey!=0) );
+ assert( pCur->pKeyInfo==0 );
+ assert( pCur->eState!=CURSOR_VALID || pCur->curIntKey!=0 );
/* If the cursor is already positioned at the point we are trying
** to move to, then just return without doing any work */
- if( pIdxKey==0
- && pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0
- ){
+ if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){
if( pCur->info.nKey==intKey ){
*pRes = 0;
return SQLITE_OK;
@@ -68252,8 +75803,8 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
/* If the requested key is one more than the previous key, then
** try to get there using sqlite3BtreeNext() rather than a full
** binary search. This is an optimization only. The correct answer
- ** is still obtained without this case, only a little more slowely */
- if( pCur->info.nKey+1==intKey && !pCur->skipNext ){
+ ** is still obtained without this case, only a little more slowly. */
+ if( pCur->info.nKey+1==intKey ){
*pRes = 0;
rc = sqlite3BtreeNext(pCur, 0);
if( rc==SQLITE_OK ){
@@ -68261,25 +75812,16 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
if( pCur->info.nKey==intKey ){
return SQLITE_OK;
}
- }else if( rc==SQLITE_DONE ){
- rc = SQLITE_OK;
- }else{
+ }else if( rc!=SQLITE_DONE ){
return rc;
}
}
}
}
- if( pIdxKey ){
- xRecordCompare = sqlite3VdbeFindCompare(pIdxKey);
- pIdxKey->errCode = 0;
- assert( pIdxKey->default_rc==1
- || pIdxKey->default_rc==0
- || pIdxKey->default_rc==-1
- );
- }else{
- xRecordCompare = 0; /* All keys are integers */
- }
+#ifdef SQLITE_DEBUG
+ pCur->pBtree->nSeek++; /* Performance measurement during testing */
+#endif
rc = moveToRoot(pCur);
if( rc ){
@@ -68295,7 +75837,8 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
assert( pCur->eState==CURSOR_VALID );
assert( pCur->pPage->nCell > 0 );
assert( pCur->iPage==0 || pCur->apPage[0]->intKey==pCur->curIntKey );
- assert( pCur->curIntKey || pIdxKey );
+ assert( pCur->curIntKey );
+
for(;;){
int lwr, upr, idx, c;
Pgno chldPg;
@@ -68309,142 +75852,55 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
** be the right kind (index or table) of b-tree page. Otherwise
** a moveToChild() or moveToRoot() call would have detected corruption. */
assert( pPage->nCell>0 );
- assert( pPage->intKey==(pIdxKey==0) );
+ assert( pPage->intKey );
lwr = 0;
upr = pPage->nCell-1;
assert( biasRight==0 || biasRight==1 );
idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */
- pCur->ix = (u16)idx;
- if( xRecordCompare==0 ){
- for(;;){
- i64 nCellKey;
- pCell = findCellPastPtr(pPage, idx);
- if( pPage->intKeyLeaf ){
- while( 0x80 <= *(pCell++) ){
- if( pCell>=pPage->aDataEnd ){
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- }
- }
- getVarint(pCell, (u64*)&nCellKey);
- if( nCellKey<intKey ){
- lwr = idx+1;
- if( lwr>upr ){ c = -1; break; }
- }else if( nCellKey>intKey ){
- upr = idx-1;
- if( lwr>upr ){ c = +1; break; }
- }else{
- assert( nCellKey==intKey );
- pCur->ix = (u16)idx;
- if( !pPage->leaf ){
- lwr = idx;
- goto moveto_next_layer;
- }else{
- pCur->curFlags |= BTCF_ValidNKey;
- pCur->info.nKey = nCellKey;
- pCur->info.nSize = 0;
- *pRes = 0;
- return SQLITE_OK;
+ for(;;){
+ i64 nCellKey;
+ pCell = findCellPastPtr(pPage, idx);
+ if( pPage->intKeyLeaf ){
+ while( 0x80 <= *(pCell++) ){
+ if( pCell>=pPage->aDataEnd ){
+ return SQLITE_CORRUPT_PAGE(pPage);
}
}
- assert( lwr+upr>=0 );
- idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */
}
- }else{
- for(;;){
- int nCell; /* Size of the pCell cell in bytes */
- pCell = findCellPastPtr(pPage, idx);
-
- /* The maximum supported page-size is 65536 bytes. This means that
- ** the maximum number of record bytes stored on an index B-Tree
- ** page is less than 16384 bytes and may be stored as a 2-byte
- ** varint. This information is used to attempt to avoid parsing
- ** the entire cell by checking for the cases where the record is
- ** stored entirely within the b-tree page by inspecting the first
- ** 2 bytes of the cell.
- */
- nCell = pCell[0];
- if( nCell<=pPage->max1bytePayload ){
- /* This branch runs if the record-size field of the cell is a
- ** single byte varint and the record fits entirely on the main
- ** b-tree page. */
- testcase( pCell+nCell+1==pPage->aDataEnd );
- c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
- }else if( !(pCell[1] & 0x80)
- && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
- ){
- /* The record-size field is a 2 byte varint and the record
- ** fits entirely on the main b-tree page. */
- testcase( pCell+nCell+2==pPage->aDataEnd );
- c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
- }else{
- /* The record flows over onto one or more overflow pages. In
- ** this case the whole cell needs to be parsed, a buffer allocated
- ** and accessPayload() used to retrieve the record into the
- ** buffer before VdbeRecordCompare() can be called.
- **
- ** If the record is corrupt, the xRecordCompare routine may read
- ** up to two varints past the end of the buffer. An extra 18
- ** bytes of padding is allocated at the end of the buffer in
- ** case this happens. */
- void *pCellKey;
- u8 * const pCellBody = pCell - pPage->childPtrSize;
- pPage->xParseCell(pPage, pCellBody, &pCur->info);
- nCell = (int)pCur->info.nKey;
- testcase( nCell<0 ); /* True if key size is 2^32 or more */
- testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */
- testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */
- testcase( nCell==2 ); /* Minimum legal index key size */
- if( nCell<2 ){
- rc = SQLITE_CORRUPT_PAGE(pPage);
- goto moveto_finish;
- }
- pCellKey = sqlite3Malloc( nCell+18 );
- if( pCellKey==0 ){
- rc = SQLITE_NOMEM_BKPT;
- goto moveto_finish;
- }
- pCur->ix = (u16)idx;
- rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0);
- pCur->curFlags &= ~BTCF_ValidOvfl;
- if( rc ){
- sqlite3_free(pCellKey);
- goto moveto_finish;
- }
- c = xRecordCompare(nCell, pCellKey, pIdxKey);
- sqlite3_free(pCellKey);
- }
- assert(
- (pIdxKey->errCode!=SQLITE_CORRUPT || c==0)
- && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed)
- );
- if( c<0 ){
- lwr = idx+1;
- }else if( c>0 ){
- upr = idx-1;
+ getVarint(pCell, (u64*)&nCellKey);
+ if( nCellKey<intKey ){
+ lwr = idx+1;
+ if( lwr>upr ){ c = -1; break; }
+ }else if( nCellKey>intKey ){
+ upr = idx-1;
+ if( lwr>upr ){ c = +1; break; }
+ }else{
+ assert( nCellKey==intKey );
+ pCur->ix = (u16)idx;
+ if( !pPage->leaf ){
+ lwr = idx;
+ goto moveto_table_next_layer;
}else{
- assert( c==0 );
+ pCur->curFlags |= BTCF_ValidNKey;
+ pCur->info.nKey = nCellKey;
+ pCur->info.nSize = 0;
*pRes = 0;
- rc = SQLITE_OK;
- pCur->ix = (u16)idx;
- if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT;
- goto moveto_finish;
+ return SQLITE_OK;
}
- if( lwr>upr ) break;
- assert( lwr+upr>=0 );
- idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */
}
+ assert( lwr+upr>=0 );
+ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */
}
- assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) );
+ assert( lwr==upr+1 || !pPage->leaf );
assert( pPage->isInit );
if( pPage->leaf ){
assert( pCur->ix<pCur->pPage->nCell );
pCur->ix = (u16)idx;
*pRes = c;
rc = SQLITE_OK;
- goto moveto_finish;
+ goto moveto_table_finish;
}
-moveto_next_layer:
+moveto_table_next_layer:
if( lwr>=pPage->nCell ){
chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
}else{
@@ -68454,7 +75910,326 @@ moveto_next_layer:
rc = moveToChild(pCur, chldPg);
if( rc ) break;
}
-moveto_finish:
+moveto_table_finish:
+ pCur->info.nSize = 0;
+ assert( (pCur->curFlags & BTCF_ValidOvfl)==0 );
+ return rc;
+}
+
+/*
+** Compare the "idx"-th cell on the page the cursor pCur is currently
+** pointing to to pIdxKey using xRecordCompare. Return negative or
+** zero if the cell is less than or equal pIdxKey. Return positive
+** if unknown.
+**
+** Return value negative: Cell at pCur[idx] less than pIdxKey
+**
+** Return value is zero: Cell at pCur[idx] equals pIdxKey
+**
+** Return value positive: Nothing is known about the relationship
+** of the cell at pCur[idx] and pIdxKey.
+**
+** This routine is part of an optimization. It is always safe to return
+** a positive value as that will cause the optimization to be skipped.
+*/
+static int indexCellCompare(
+ BtCursor *pCur,
+ int idx,
+ UnpackedRecord *pIdxKey,
+ RecordCompare xRecordCompare
+){
+ MemPage *pPage = pCur->pPage;
+ int c;
+ int nCell; /* Size of the pCell cell in bytes */
+ u8 *pCell = findCellPastPtr(pPage, idx);
+
+ nCell = pCell[0];
+ if( nCell<=pPage->max1bytePayload ){
+ /* This branch runs if the record-size field of the cell is a
+ ** single byte varint and the record fits entirely on the main
+ ** b-tree page. */
+ testcase( pCell+nCell+1==pPage->aDataEnd );
+ c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
+ }else if( !(pCell[1] & 0x80)
+ && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
+ ){
+ /* The record-size field is a 2 byte varint and the record
+ ** fits entirely on the main b-tree page. */
+ testcase( pCell+nCell+2==pPage->aDataEnd );
+ c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
+ }else{
+ /* If the record extends into overflow pages, do not attempt
+ ** the optimization. */
+ c = 99;
+ }
+ return c;
+}
+
+/*
+** Return true (non-zero) if pCur is current pointing to the last
+** page of a table.
+*/
+static int cursorOnLastPage(BtCursor *pCur){
+ int i;
+ assert( pCur->eState==CURSOR_VALID );
+ for(i=0; i<pCur->iPage; i++){
+ MemPage *pPage = pCur->apPage[i];
+ if( pCur->aiIdx[i]<pPage->nCell ) return 0;
+ }
+ return 1;
+}
+
+/* Move the cursor so that it points to an entry in an index table
+** near the key pIdxKey. Return a success code.
+**
+** If an exact match is not found, then the cursor is always
+** left pointing at a leaf page which would hold the entry if it
+** were present. The cursor might point to an entry that comes
+** before or after the key.
+**
+** An integer is written into *pRes which is the result of
+** comparing the key with the entry to which the cursor is
+** pointing. The meaning of the integer written into
+** *pRes is as follows:
+**
+** *pRes<0 The cursor is left pointing at an entry that
+** is smaller than pIdxKey or if the table is empty
+** and the cursor is therefore left point to nothing.
+**
+** *pRes==0 The cursor is left pointing at an entry that
+** exactly matches pIdxKey.
+**
+** *pRes>0 The cursor is left pointing at an entry that
+** is larger than pIdxKey.
+**
+** The pIdxKey->eqSeen field is set to 1 if there
+** exists an entry in the table that exactly matches pIdxKey.
+*/
+SQLITE_PRIVATE int sqlite3BtreeIndexMoveto(
+ BtCursor *pCur, /* The cursor to be moved */
+ UnpackedRecord *pIdxKey, /* Unpacked index key */
+ int *pRes /* Write search results here */
+){
+ int rc;
+ RecordCompare xRecordCompare;
+
+ assert( cursorOwnsBtShared(pCur) );
+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
+ assert( pRes );
+ assert( pCur->pKeyInfo!=0 );
+
+#ifdef SQLITE_DEBUG
+ pCur->pBtree->nSeek++; /* Performance measurement during testing */
+#endif
+
+ xRecordCompare = sqlite3VdbeFindCompare(pIdxKey);
+ pIdxKey->errCode = 0;
+ assert( pIdxKey->default_rc==1
+ || pIdxKey->default_rc==0
+ || pIdxKey->default_rc==-1
+ );
+
+
+ /* Check to see if we can skip a lot of work. Two cases:
+ **
+ ** (1) If the cursor is already pointing to the very last cell
+ ** in the table and the pIdxKey search key is greater than or
+ ** equal to that last cell, then no movement is required.
+ **
+ ** (2) If the cursor is on the last page of the table and the first
+ ** cell on that last page is less than or equal to the pIdxKey
+ ** search key, then we can start the search on the current page
+ ** without needing to go back to root.
+ */
+ if( pCur->eState==CURSOR_VALID
+ && pCur->pPage->leaf
+ && cursorOnLastPage(pCur)
+ ){
+ int c;
+ if( pCur->ix==pCur->pPage->nCell-1
+ && (c = indexCellCompare(pCur, pCur->ix, pIdxKey, xRecordCompare))<=0
+ && pIdxKey->errCode==SQLITE_OK
+ ){
+ *pRes = c;
+ return SQLITE_OK; /* Cursor already pointing at the correct spot */
+ }
+ if( pCur->iPage>0
+ && indexCellCompare(pCur, 0, pIdxKey, xRecordCompare)<=0
+ && pIdxKey->errCode==SQLITE_OK
+ ){
+ pCur->curFlags &= ~BTCF_ValidOvfl;
+ if( !pCur->pPage->isInit ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ goto bypass_moveto_root; /* Start search on the current page */
+ }
+ pIdxKey->errCode = SQLITE_OK;
+ }
+
+ rc = moveToRoot(pCur);
+ if( rc ){
+ if( rc==SQLITE_EMPTY ){
+ assert( pCur->pgnoRoot==0 || pCur->pPage->nCell==0 );
+ *pRes = -1;
+ return SQLITE_OK;
+ }
+ return rc;
+ }
+
+bypass_moveto_root:
+ assert( pCur->pPage );
+ assert( pCur->pPage->isInit );
+ assert( pCur->eState==CURSOR_VALID );
+ assert( pCur->pPage->nCell > 0 );
+ assert( pCur->curIntKey==0 );
+ assert( pIdxKey!=0 );
+ for(;;){
+ int lwr, upr, idx, c;
+ Pgno chldPg;
+ MemPage *pPage = pCur->pPage;
+ u8 *pCell; /* Pointer to current cell in pPage */
+
+ /* pPage->nCell must be greater than zero. If this is the root-page
+ ** the cursor would have been INVALID above and this for(;;) loop
+ ** not run. If this is not the root-page, then the moveToChild() routine
+ ** would have already detected db corruption. Similarly, pPage must
+ ** be the right kind (index or table) of b-tree page. Otherwise
+ ** a moveToChild() or moveToRoot() call would have detected corruption. */
+ assert( pPage->nCell>0 );
+ assert( pPage->intKey==0 );
+ lwr = 0;
+ upr = pPage->nCell-1;
+ idx = upr>>1; /* idx = (lwr+upr)/2; */
+ for(;;){
+ int nCell; /* Size of the pCell cell in bytes */
+ pCell = findCellPastPtr(pPage, idx);
+
+ /* The maximum supported page-size is 65536 bytes. This means that
+ ** the maximum number of record bytes stored on an index B-Tree
+ ** page is less than 16384 bytes and may be stored as a 2-byte
+ ** varint. This information is used to attempt to avoid parsing
+ ** the entire cell by checking for the cases where the record is
+ ** stored entirely within the b-tree page by inspecting the first
+ ** 2 bytes of the cell.
+ */
+ nCell = pCell[0];
+ if( nCell<=pPage->max1bytePayload ){
+ /* This branch runs if the record-size field of the cell is a
+ ** single byte varint and the record fits entirely on the main
+ ** b-tree page. */
+ testcase( pCell+nCell+1==pPage->aDataEnd );
+ c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
+ }else if( !(pCell[1] & 0x80)
+ && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
+ ){
+ /* The record-size field is a 2 byte varint and the record
+ ** fits entirely on the main b-tree page. */
+ testcase( pCell+nCell+2==pPage->aDataEnd );
+ c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
+ }else{
+ /* The record flows over onto one or more overflow pages. In
+ ** this case the whole cell needs to be parsed, a buffer allocated
+ ** and accessPayload() used to retrieve the record into the
+ ** buffer before VdbeRecordCompare() can be called.
+ **
+ ** If the record is corrupt, the xRecordCompare routine may read
+ ** up to two varints past the end of the buffer. An extra 18
+ ** bytes of padding is allocated at the end of the buffer in
+ ** case this happens. */
+ void *pCellKey;
+ u8 * const pCellBody = pCell - pPage->childPtrSize;
+ const int nOverrun = 18; /* Size of the overrun padding */
+ pPage->xParseCell(pPage, pCellBody, &pCur->info);
+ nCell = (int)pCur->info.nKey;
+ testcase( nCell<0 ); /* True if key size is 2^32 or more */
+ testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */
+ testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */
+ testcase( nCell==2 ); /* Minimum legal index key size */
+ if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){
+ rc = SQLITE_CORRUPT_PAGE(pPage);
+ goto moveto_index_finish;
+ }
+ pCellKey = sqlite3Malloc( nCell+nOverrun );
+ if( pCellKey==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ goto moveto_index_finish;
+ }
+ pCur->ix = (u16)idx;
+ rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0);
+ memset(((u8*)pCellKey)+nCell,0,nOverrun); /* Fix uninit warnings */
+ pCur->curFlags &= ~BTCF_ValidOvfl;
+ if( rc ){
+ sqlite3_free(pCellKey);
+ goto moveto_index_finish;
+ }
+ c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey);
+ sqlite3_free(pCellKey);
+ }
+ assert(
+ (pIdxKey->errCode!=SQLITE_CORRUPT || c==0)
+ && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed)
+ );
+ if( c<0 ){
+ lwr = idx+1;
+ }else if( c>0 ){
+ upr = idx-1;
+ }else{
+ assert( c==0 );
+ *pRes = 0;
+ rc = SQLITE_OK;
+ pCur->ix = (u16)idx;
+ if( pIdxKey->errCode ) rc = SQLITE_CORRUPT_BKPT;
+ goto moveto_index_finish;
+ }
+ if( lwr>upr ) break;
+ assert( lwr+upr>=0 );
+ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */
+ }
+ assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) );
+ assert( pPage->isInit );
+ if( pPage->leaf ){
+ assert( pCur->ix<pCur->pPage->nCell || CORRUPT_DB );
+ pCur->ix = (u16)idx;
+ *pRes = c;
+ rc = SQLITE_OK;
+ goto moveto_index_finish;
+ }
+ if( lwr>=pPage->nCell ){
+ chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
+ }else{
+ chldPg = get4byte(findCell(pPage, lwr));
+ }
+
+ /* This block is similar to an in-lined version of:
+ **
+ ** pCur->ix = (u16)lwr;
+ ** rc = moveToChild(pCur, chldPg);
+ ** if( rc ) break;
+ */
+ pCur->info.nSize = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
+ if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ pCur->aiIdx[pCur->iPage] = (u16)lwr;
+ pCur->apPage[pCur->iPage] = pCur->pPage;
+ pCur->ix = 0;
+ pCur->iPage++;
+ rc = getAndInitPage(pCur->pBt, chldPg, &pCur->pPage, pCur->curPagerFlags);
+ if( rc==SQLITE_OK
+ && (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey)
+ ){
+ releasePage(pCur->pPage);
+ rc = SQLITE_CORRUPT_PGNO(chldPg);
+ }
+ if( rc ){
+ pCur->pPage = pCur->apPage[--pCur->iPage];
+ break;
+ }
+ /*
+ ***** End of in-lined moveToChild() call */
+ }
+moveto_index_finish:
pCur->info.nSize = 0;
assert( (pCur->curFlags & BTCF_ValidOvfl)==0 );
return rc;
@@ -68478,7 +76253,7 @@ SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor *pCur){
/*
** Return an estimate for the number of rows in the table that pCur is
-** pointing to. Return a negative number if no estimate is currently
+** pointing to. Return a negative number if no estimate is currently
** available.
*/
SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor *pCur){
@@ -68502,7 +76277,7 @@ SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor *pCur){
}
/*
-** Advance the cursor to the next entry in the database.
+** Advance the cursor to the next entry in the database.
** Return value:
**
** SQLITE_OK success
@@ -68527,7 +76302,6 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){
MemPage *pPage;
assert( cursorOwnsBtShared(pCur) );
- assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
if( pCur->eState!=CURSOR_VALID ){
assert( (pCur->curFlags & BTCF_ValidOvfl)==0 );
rc = restoreCursorPosition(pCur);
@@ -68537,37 +76311,19 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){
if( CURSOR_INVALID==pCur->eState ){
return SQLITE_DONE;
}
- if( pCur->skipNext ){
- assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT );
+ if( pCur->eState==CURSOR_SKIPNEXT ){
pCur->eState = CURSOR_VALID;
- if( pCur->skipNext>0 ){
- pCur->skipNext = 0;
- return SQLITE_OK;
- }
- pCur->skipNext = 0;
+ if( pCur->skipNext>0 ) return SQLITE_OK;
}
}
pPage = pCur->pPage;
idx = ++pCur->ix;
+ if( sqlite3FaultSim(412) ) pPage->isInit = 0;
if( !pPage->isInit ){
- /* The only known way for this to happen is for there to be a
- ** recursive SQL function that does a DELETE operation as part of a
- ** SELECT which deletes content out from under an active cursor
- ** in a corrupt database file where the table being DELETE-ed from
- ** has pages in common with the table being queried. See TH3
- ** module cov1/btree78.test testcase 220 (2018-06-08) for an
- ** example. */
return SQLITE_CORRUPT_BKPT;
}
- /* If the database file is corrupt, it is possible for the value of idx
- ** to be invalid here. This can only occur if a second cursor modifies
- ** the page while cursor pCur is holding a reference to it. Which can
- ** only happen if the database is corrupt in such a way as to link the
- ** page into more than one b-tree structure. */
- testcase( idx>pPage->nCell );
-
if( idx>=pPage->nCell ){
if( !pPage->leaf ){
rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
@@ -68599,7 +76355,6 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int flags){
UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */
assert( cursorOwnsBtShared(pCur) );
assert( flags==0 || flags==1 );
- assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
pCur->info.nSize = 0;
pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
if( pCur->eState!=CURSOR_VALID ) return btreeNext(pCur);
@@ -68640,7 +76395,6 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){
MemPage *pPage;
assert( cursorOwnsBtShared(pCur) );
- assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 );
assert( pCur->info.nSize==0 );
if( pCur->eState!=CURSOR_VALID ){
@@ -68651,19 +76405,17 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){
if( CURSOR_INVALID==pCur->eState ){
return SQLITE_DONE;
}
- if( pCur->skipNext ){
- assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT );
+ if( CURSOR_SKIPNEXT==pCur->eState ){
pCur->eState = CURSOR_VALID;
- if( pCur->skipNext<0 ){
- pCur->skipNext = 0;
- return SQLITE_OK;
- }
- pCur->skipNext = 0;
+ if( pCur->skipNext<0 ) return SQLITE_OK;
}
}
pPage = pCur->pPage;
- assert( pPage->isInit );
+ if( sqlite3FaultSim(412) ) pPage->isInit = 0;
+ if( !pPage->isInit ){
+ return SQLITE_CORRUPT_BKPT;
+ }
if( !pPage->leaf ){
int idx = pCur->ix;
rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
@@ -68693,7 +76445,6 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){
SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int flags){
assert( cursorOwnsBtShared(pCur) );
assert( flags==0 || flags==1 );
- assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */
pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey);
pCur->info.nSize = 0;
@@ -68718,7 +76469,7 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int flags){
** SQLITE_OK is returned on success. Any other return value indicates
** an error. *ppPage is set to NULL in the event of an error.
**
-** If the "nearby" parameter is not 0, then an effort is made to
+** If the "nearby" parameter is not 0, then an effort is made to
** locate a page close to the page number "nearby". This can be used in an
** attempt to keep related pages close to each other in the database file,
** which in turn can make database access faster.
@@ -68748,8 +76499,8 @@ static int allocateBtreePage(
assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) );
pPage1 = pBt->pPage1;
mxPage = btreePagecount(pBt);
- /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36
- ** stores stores the total number of pages on the freelist. */
+ /* EVIDENCE-OF: R-21003-45125 The 4-byte big-endian integer at offset 36
+ ** stores the total number of pages on the freelist. */
n = get4byte(&pPage1->aData[36]);
testcase( n==mxPage-1 );
if( n>=mxPage ){
@@ -68760,7 +76511,7 @@ static int allocateBtreePage(
Pgno iTrunk;
u8 searchList = 0; /* If the free-list must be searched for 'nearby' */
u32 nSearch = 0; /* Count of the number of search attempts */
-
+
/* If eMode==BTALLOC_EXACT and a query of the pointer-map
** shows that the page 'nearby' is somewhere on the free-list, then
** the entire-list will be searched for that page.
@@ -68823,8 +76574,8 @@ static int allocateBtreePage(
** is the number of leaf page pointers to follow. */
k = get4byte(&pTrunk->aData[4]);
if( k==0 && !searchList ){
- /* The trunk has no leaves and the list is not being searched.
- ** So extract the trunk page itself and use it as the newly
+ /* The trunk has no leaves and the list is not being searched.
+ ** So extract the trunk page itself and use it as the newly
** allocated page */
assert( pPrevTrunk==0 );
rc = sqlite3PagerWrite(pTrunk->pDbPage);
@@ -68835,14 +76586,14 @@ static int allocateBtreePage(
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
*ppPage = pTrunk;
pTrunk = 0;
- TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
+ TRACE(("ALLOCATE: %u trunk - %u free pages left\n", *pPgno, n-1));
}else if( k>(u32)(pBt->usableSize/4 - 2) ){
/* Value of k is out of range. Database corruption */
rc = SQLITE_CORRUPT_PGNO(iTrunk);
goto end_allocate_page;
#ifndef SQLITE_OMIT_AUTOVACUUM
- }else if( searchList
- && (nearby==iTrunk || (iTrunk<nearby && eMode==BTALLOC_LE))
+ }else if( searchList
+ && (nearby==iTrunk || (iTrunk<nearby && eMode==BTALLOC_LE))
){
/* The list is being searched and this trunk page is the page
** to allocate, regardless of whether it has leaves.
@@ -68865,13 +76616,13 @@ static int allocateBtreePage(
memcpy(&pPrevTrunk->aData[0], &pTrunk->aData[0], 4);
}
}else{
- /* The trunk page is required by the caller but it contains
+ /* The trunk page is required by the caller but it contains
** pointers to free-list leaves. The first leaf becomes a trunk
** page in this case.
*/
MemPage *pNewTrunk;
Pgno iNewTrunk = get4byte(&pTrunk->aData[8]);
- if( iNewTrunk>mxPage ){
+ if( iNewTrunk>mxPage ){
rc = SQLITE_CORRUPT_PGNO(iTrunk);
goto end_allocate_page;
}
@@ -68901,7 +76652,7 @@ static int allocateBtreePage(
}
}
pTrunk = 0;
- TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
+ TRACE(("ALLOCATE: %u trunk - %u free pages left\n", *pPgno, n-1));
#endif
}else if( k>0 ){
/* Extract a leaf from the trunk */
@@ -68936,18 +76687,18 @@ static int allocateBtreePage(
iPage = get4byte(&aData[8+closest*4]);
testcase( iPage==mxPage );
- if( iPage>mxPage ){
+ if( iPage>mxPage || iPage<2 ){
rc = SQLITE_CORRUPT_PGNO(iTrunk);
goto end_allocate_page;
}
testcase( iPage==mxPage );
- if( !searchList
- || (iPage==nearby || (iPage<nearby && eMode==BTALLOC_LE))
+ if( !searchList
+ || (iPage==nearby || (iPage<nearby && eMode==BTALLOC_LE))
){
int noContent;
*pPgno = iPage;
- TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d"
- ": %d more free pages\n",
+ TRACE(("ALLOCATE: %u was leaf %u of %u on trunk %u"
+ ": %u more free pages\n",
*pPgno, closest+1, k, pTrunk->pgno, n-1));
rc = sqlite3PagerWrite(pTrunk->pDbPage);
if( rc ) goto end_allocate_page;
@@ -68983,7 +76734,7 @@ static int allocateBtreePage(
** not set the no-content flag. This causes the pager to load and journal
** the current page content before overwriting it.
**
- ** Note that the pager will not actually attempt to load or journal
+ ** Note that the pager will not actually attempt to load or journal
** content for any page that really does lie past the end of the database
** file on disk. So the effects of disabling the no-content optimization
** here are confined to those pages that lie between the end of the
@@ -69003,7 +76754,7 @@ static int allocateBtreePage(
** becomes a new pointer-map page, the second is used by the caller.
*/
MemPage *pPg = 0;
- TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage));
+ TRACE(("ALLOCATE: %u from end of file (pointer-map page)\n", pBt->nPage));
assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) );
rc = btreeGetUnusedPage(pBt, pBt->nPage, &pPg, bNoContent);
if( rc==SQLITE_OK ){
@@ -69026,10 +76777,10 @@ static int allocateBtreePage(
releasePage(*ppPage);
*ppPage = 0;
}
- TRACE(("ALLOCATE: %d from end of file\n", *pPgno));
+ TRACE(("ALLOCATE: %u from end of file\n", *pPgno));
}
- assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
+ assert( CORRUPT_DB || *pPgno!=PENDING_BYTE_PAGE(pBt) );
end_allocate_page:
releasePage(pTrunk);
@@ -69040,12 +76791,12 @@ end_allocate_page:
}
/*
-** This function is used to add page iPage to the database file free-list.
+** This function is used to add page iPage to the database file free-list.
** It is assumed that the page is not already a part of the free-list.
**
** The value passed as the second argument to this function is optional.
-** If the caller happens to have a pointer to the MemPage object
-** corresponding to page iPage handy, it may pass it as the second value.
+** If the caller happens to have a pointer to the MemPage object
+** corresponding to page iPage handy, it may pass it as the second value.
** Otherwise, it may pass NULL.
**
** If a pointer to a MemPage object is passed as the second argument,
@@ -69053,17 +76804,19 @@ end_allocate_page:
*/
static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
MemPage *pTrunk = 0; /* Free-list trunk page */
- Pgno iTrunk = 0; /* Page number of free-list trunk page */
+ Pgno iTrunk = 0; /* Page number of free-list trunk page */
MemPage *pPage1 = pBt->pPage1; /* Local reference to page 1 */
MemPage *pPage; /* Page being freed. May be NULL. */
int rc; /* Return Code */
- int nFree; /* Initial number of pages on free-list */
+ u32 nFree; /* Initial number of pages on free-list */
assert( sqlite3_mutex_held(pBt->mutex) );
assert( CORRUPT_DB || iPage>1 );
assert( !pMemPage || pMemPage->pgno==iPage );
- if( iPage<2 ) return SQLITE_CORRUPT_BKPT;
+ if( iPage<2 || iPage>pBt->nPage ){
+ return SQLITE_CORRUPT_BKPT;
+ }
if( pMemPage ){
pPage = pMemPage;
sqlite3PagerRef(pPage->pDbPage);
@@ -69092,7 +76845,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
/* If the database supports auto-vacuum, write an entry in the pointer-map
** to indicate that the page is free.
*/
- if( ISAUTOVACUUM ){
+ if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc);
if( rc ) goto freepage_out;
}
@@ -69108,6 +76861,10 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
u32 nLeaf; /* Initial number of leaf cells on trunk page */
iTrunk = get4byte(&pPage1->aData[32]);
+ if( iTrunk>btreePagecount(pBt) ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto freepage_out;
+ }
rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0);
if( rc!=SQLITE_OK ){
goto freepage_out;
@@ -69148,14 +76905,14 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
}
rc = btreeSetHasContent(pBt, iPage);
}
- TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno));
+ TRACE(("FREE-PAGE: %u leaf on trunk page %u\n",pPage->pgno,pTrunk->pgno));
goto freepage_out;
}
}
/* If control flows to this point, then it was not possible to add the
** the page being freed as a leaf page of the first trunk in the free-list.
- ** Possibly because the free-list is empty, or possibly because the
+ ** Possibly because the free-list is empty, or possibly because the
** first trunk in the free-list is full. Either way, the page being freed
** will become the new first trunk page in the free-list.
*/
@@ -69169,7 +76926,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
put4byte(pPage->aData, iTrunk);
put4byte(&pPage->aData[4], 0);
put4byte(&pPage1->aData[32], iPage);
- TRACE(("FREE-PAGE: %d new trunk page replacing %d\n", pPage->pgno, iTrunk));
+ TRACE(("FREE-PAGE: %u new trunk page replacing %u\n", pPage->pgno, iTrunk));
freepage_out:
if( pPage ){
@@ -69186,10 +76943,9 @@ static void freePage(MemPage *pPage, int *pRC){
}
/*
-** Free any overflow pages associated with the given Cell. Store
-** size information about the cell in pInfo.
+** Free the overflow pages associated with the given Cell.
*/
-static int clearCell(
+static SQLITE_NOINLINE int clearCellOverflow(
MemPage *pPage, /* The page that contains the Cell */
unsigned char *pCell, /* First byte of the Cell */
CellInfo *pInfo /* Size information about the cell */
@@ -69201,10 +76957,7 @@ static int clearCell(
u32 ovflPageSize;
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- pPage->xParseCell(pPage, pCell, pInfo);
- if( pInfo->nLocal==pInfo->nPayload ){
- return SQLITE_OK; /* No overflow pages. Return without doing anything */
- }
+ assert( pInfo->nLocal!=pInfo->nPayload );
testcase( pCell + pInfo->nSize == pPage->aDataEnd );
testcase( pCell + (pInfo->nSize-1) == pPage->aDataEnd );
if( pCell + pInfo->nSize > pPage->aDataEnd ){
@@ -69216,15 +76969,15 @@ static int clearCell(
assert( pBt->usableSize > 4 );
ovflPageSize = pBt->usableSize - 4;
nOvfl = (pInfo->nPayload - pInfo->nLocal + ovflPageSize - 1)/ovflPageSize;
- assert( nOvfl>0 ||
+ assert( nOvfl>0 ||
(CORRUPT_DB && (pInfo->nPayload + ovflPageSize)<ovflPageSize)
);
while( nOvfl-- ){
Pgno iNext = 0;
MemPage *pOvfl = 0;
if( ovflPgno<2 || ovflPgno>btreePagecount(pBt) ){
- /* 0 is not a legal page number and page 1 cannot be an
- ** overflow page. Therefore if ovflPgno<2 or past the end of the
+ /* 0 is not a legal page number and page 1 cannot be an
+ ** overflow page. Therefore if ovflPgno<2 or past the end of the
** file the database must be corrupt. */
return SQLITE_CORRUPT_BKPT;
}
@@ -69236,11 +76989,11 @@ static int clearCell(
if( ( pOvfl || ((pOvfl = btreePageLookup(pBt, ovflPgno))!=0) )
&& sqlite3PagerPageRefcount(pOvfl->pDbPage)!=1
){
- /* There is no reason any cursor should have an outstanding reference
+ /* There is no reason any cursor should have an outstanding reference
** to an overflow page belonging to a cell that is being deleted/updated.
- ** So if there exists more than one reference to this page, then it
- ** must not really be an overflow page and the database must be corrupt.
- ** It is helpful to detect this before calling freePage2(), as
+ ** So if there exists more than one reference to this page, then it
+ ** must not really be an overflow page and the database must be corrupt.
+ ** It is helpful to detect this before calling freePage2(), as
** freePage2() may zero the page contents if secure-delete mode is
** enabled. If this 'overflow' page happens to be a page that the
** caller is iterating through or using in some other way, this
@@ -69260,6 +77013,21 @@ static int clearCell(
return SQLITE_OK;
}
+/* Call xParseCell to compute the size of a cell. If the cell contains
+** overflow, then invoke cellClearOverflow to clear out that overflow.
+** Store the result code (SQLITE_OK or some error code) in rc.
+**
+** Implemented as macro to force inlining for performance.
+*/
+#define BTREE_CLEAR_CELL(rc, pPage, pCell, sInfo) \
+ pPage->xParseCell(pPage, pCell, &sInfo); \
+ if( sInfo.nLocal!=sInfo.nPayload ){ \
+ rc = clearCellOverflow(pPage, pCell, &sInfo); \
+ }else{ \
+ rc = SQLITE_OK; \
+ }
+
+
/*
** Create the byte sequence used to represent a cell on page pPage
** and write that byte sequence into pCell[]. Overflow pages are
@@ -69311,7 +77079,7 @@ static int fillInCell(
pSrc = pX->pKey;
nHeader += putVarint32(&pCell[nHeader], nPayload);
}
-
+
/* Fill in the payload */
pPayload = &pCell[nHeader];
if( nPayload<=pPage->maxLocal ){
@@ -69402,8 +77170,8 @@ static int fillInCell(
if( pBt->autoVacuum ){
do{
pgnoOvfl++;
- } while(
- PTRMAP_ISPAGE(pBt, pgnoOvfl) || pgnoOvfl==PENDING_BYTE_PAGE(pBt)
+ } while(
+ PTRMAP_ISPAGE(pBt, pgnoOvfl) || pgnoOvfl==PENDING_BYTE_PAGE(pBt)
);
}
#endif
@@ -69411,9 +77179,9 @@ static int fillInCell(
#ifndef SQLITE_OMIT_AUTOVACUUM
/* If the database supports auto-vacuum, and the second or subsequent
** overflow page is being allocated, add an entry to the pointer-map
- ** for that page now.
+ ** for that page now.
**
- ** If this is the first overflow page, then write a partial entry
+ ** If this is the first overflow page, then write a partial entry
** to the pointer-map. If we write nothing to this pointer-map slot,
** then the optimistic overflow chain processing in clearCell()
** may misinterpret the uninitialized values and delete the
@@ -69470,15 +77238,18 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */
if( *pRC ) return;
- assert( idx>=0 && idx<pPage->nCell );
+ assert( idx>=0 );
+ assert( idx<pPage->nCell );
assert( CORRUPT_DB || sz==cellSize(pPage, idx) );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ assert( pPage->nFree>=0 );
data = pPage->aData;
ptr = &pPage->aCellIdx[2*idx];
+ assert( pPage->pBt->usableSize > (u32)(ptr-data) );
pc = get2byte(ptr);
hdr = pPage->hdrOffset;
- testcase( pc==get2byte(&data[hdr+5]) );
+ testcase( pc==(u32)get2byte(&data[hdr+5]) );
testcase( pc+sz==pPage->pBt->usableSize );
if( pc+sz > pPage->pBt->usableSize ){
*pRC = SQLITE_CORRUPT_BKPT;
@@ -69511,47 +77282,136 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
** will not fit, then make a copy of the cell content into pTemp if
** pTemp is not null. Regardless of pTemp, allocate a new entry
** in pPage->apOvfl[] and make it point to the cell content (either
-** in pTemp or the original pCell) and also record its index.
-** Allocating a new entry in pPage->aCell[] implies that
+** in pTemp or the original pCell) and also record its index.
+** Allocating a new entry in pPage->aCell[] implies that
** pPage->nOverflow is incremented.
**
-** *pRC must be SQLITE_OK when this routine is called.
+** The insertCellFast() routine below works exactly the same as
+** insertCell() except that it lacks the pTemp and iChild parameters
+** which are assumed zero. Other than that, the two routines are the
+** same.
+**
+** Fixes or enhancements to this routine should be reflected in
+** insertCellFast()!
*/
-static void insertCell(
+static int insertCell(
MemPage *pPage, /* Page into which we are copying */
int i, /* New cell becomes the i-th cell of the page */
u8 *pCell, /* Content of the new cell */
int sz, /* Bytes of content in pCell */
u8 *pTemp, /* Temp storage space for pCell, if needed */
- Pgno iChild, /* If non-zero, replace first 4 bytes with this value */
- int *pRC /* Read and write return code from here */
+ Pgno iChild /* If non-zero, replace first 4 bytes with this value */
){
int idx = 0; /* Where to write new cell content in data[] */
int j; /* Loop counter */
u8 *data; /* The content of the whole page */
u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */
- assert( *pRC==SQLITE_OK );
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
assert( MX_CELL(pPage->pBt)<=10921 );
assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- /* The cell should normally be sized correctly. However, when moving a
- ** malformed cell from a leaf page to an interior page, if the cell size
- ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
- ** might be less than 8 (leaf-size + pointer) on the interior node. Hence
- ** the term after the || in the following assert(). */
- assert( sz==pPage->xCellSize(pPage, pCell) || (sz==8 && iChild>0) );
+ assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB );
+ assert( pPage->nFree>=0 );
+ assert( iChild>0 );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
memcpy(pTemp, pCell, sz);
pCell = pTemp;
}
- if( iChild ){
- put4byte(pCell, iChild);
+ put4byte(pCell, iChild);
+ j = pPage->nOverflow++;
+ /* Comparison against ArraySize-1 since we hold back one extra slot
+ ** as a contingency. In other words, never need more than 3 overflow
+ ** slots but 4 are allocated, just to be safe. */
+ assert( j < ArraySize(pPage->apOvfl)-1 );
+ pPage->apOvfl[j] = pCell;
+ pPage->aiOvfl[j] = (u16)i;
+
+ /* When multiple overflows occur, they are always sequential and in
+ ** sorted order. This invariants arise because multiple overflows can
+ ** only occur when inserting divider cells into the parent page during
+ ** balancing, and the dividers are adjacent and sorted.
+ */
+ assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */
+ assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */
+ }else{
+ int rc = sqlite3PagerWrite(pPage->pDbPage);
+ if( NEVER(rc!=SQLITE_OK) ){
+ return rc;
+ }
+ assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+ data = pPage->aData;
+ assert( &data[pPage->cellOffset]==pPage->aCellIdx );
+ rc = allocateSpace(pPage, sz, &idx);
+ if( rc ){ return rc; }
+ /* The allocateSpace() routine guarantees the following properties
+ ** if it returns successfully */
+ assert( idx >= 0 );
+ assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB );
+ assert( idx+sz <= (int)pPage->pBt->usableSize );
+ pPage->nFree -= (u16)(2 + sz);
+ /* In a corrupt database where an entry in the cell index section of
+ ** a btree page has a value of 3 or less, the pCell value might point
+ ** as many as 4 bytes in front of the start of the aData buffer for
+ ** the source page. Make sure this does not cause problems by not
+ ** reading the first 4 bytes */
+ memcpy(&data[idx+4], pCell+4, sz-4);
+ put4byte(&data[idx], iChild);
+ pIns = pPage->aCellIdx + i*2;
+ memmove(pIns+2, pIns, 2*(pPage->nCell - i));
+ put2byte(pIns, idx);
+ pPage->nCell++;
+ /* increment the cell count */
+ if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++;
+ assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB );
+#ifndef SQLITE_OMIT_AUTOVACUUM
+ if( pPage->pBt->autoVacuum ){
+ int rc2 = SQLITE_OK;
+ /* The cell may contain a pointer to an overflow page. If so, write
+ ** the entry for the overflow page into the pointer map.
+ */
+ ptrmapPutOvflPtr(pPage, pPage, pCell, &rc2);
+ if( rc2 ) return rc2;
}
+#endif
+ }
+ return SQLITE_OK;
+}
+
+/*
+** This variant of insertCell() assumes that the pTemp and iChild
+** parameters are both zero. Use this variant in sqlite3BtreeInsert()
+** for performance improvement, and also so that this variant is only
+** called from that one place, and is thus inlined, and thus runs must
+** faster.
+**
+** Fixes or enhancements to this routine should be reflected into
+** the insertCell() routine.
+*/
+static int insertCellFast(
+ MemPage *pPage, /* Page into which we are copying */
+ int i, /* New cell becomes the i-th cell of the page */
+ u8 *pCell, /* Content of the new cell */
+ int sz /* Bytes of content in pCell */
+){
+ int idx = 0; /* Where to write new cell content in data[] */
+ int j; /* Loop counter */
+ u8 *data; /* The content of the whole page */
+ u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */
+
+ assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
+ assert( MX_CELL(pPage->pBt)<=10921 );
+ assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
+ assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
+ assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB );
+ assert( pPage->nFree>=0 );
+ assert( pPage->nOverflow==0 );
+ if( sz+2>pPage->nFree ){
j = pPage->nOverflow++;
/* Comparison against ArraySize-1 since we hold back one extra slot
** as a contingency. In other words, never need more than 3 overflow
@@ -69570,14 +77430,13 @@ static void insertCell(
}else{
int rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc!=SQLITE_OK ){
- *pRC = rc;
- return;
+ return rc;
}
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
data = pPage->aData;
assert( &data[pPage->cellOffset]==pPage->aCellIdx );
rc = allocateSpace(pPage, sz, &idx);
- if( rc ){ *pRC = rc; return; }
+ if( rc ){ return rc; }
/* The allocateSpace() routine guarantees the following properties
** if it returns successfully */
assert( idx >= 0 );
@@ -69585,30 +77444,109 @@ static void insertCell(
assert( idx+sz <= (int)pPage->pBt->usableSize );
pPage->nFree -= (u16)(2 + sz);
memcpy(&data[idx], pCell, sz);
- if( iChild ){
- put4byte(&data[idx], iChild);
- }
pIns = pPage->aCellIdx + i*2;
memmove(pIns+2, pIns, 2*(pPage->nCell - i));
put2byte(pIns, idx);
pPage->nCell++;
/* increment the cell count */
if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++;
- assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell );
+ assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB );
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pPage->pBt->autoVacuum ){
+ int rc2 = SQLITE_OK;
/* The cell may contain a pointer to an overflow page. If so, write
** the entry for the overflow page into the pointer map.
*/
- ptrmapPutOvflPtr(pPage, pCell, pRC);
+ ptrmapPutOvflPtr(pPage, pPage, pCell, &rc2);
+ if( rc2 ) return rc2;
}
#endif
}
+ return SQLITE_OK;
}
/*
+** The following parameters determine how many adjacent pages get involved
+** in a balancing operation. NN is the number of neighbors on either side
+** of the page that participate in the balancing operation. NB is the
+** total number of pages that participate, including the target page and
+** NN neighbors on either side.
+**
+** The minimum value of NN is 1 (of course). Increasing NN above 1
+** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance
+** in exchange for a larger degradation in INSERT and UPDATE performance.
+** The value of NN appears to give the best results overall.
+**
+** (Later:) The description above makes it seem as if these values are
+** tunable - as if you could change them and recompile and it would all work.
+** But that is unlikely. NB has been 3 since the inception of SQLite and
+** we have never tested any other value.
+*/
+#define NN 1 /* Number of neighbors on either side of pPage */
+#define NB 3 /* (NN*2+1): Total pages involved in the balance */
+
+/*
** A CellArray object contains a cache of pointers and sizes for a
** consecutive sequence of cells that might be held on multiple pages.
+**
+** The cells in this array are the divider cell or cells from the pParent
+** page plus up to three child pages. There are a total of nCell cells.
+**
+** pRef is a pointer to one of the pages that contributes cells. This is
+** used to access information such as MemPage.intKey and MemPage.pBt->pageSize
+** which should be common to all pages that contribute cells to this array.
+**
+** apCell[] and szCell[] hold, respectively, pointers to the start of each
+** cell and the size of each cell. Some of the apCell[] pointers might refer
+** to overflow cells. In other words, some apCel[] pointers might not point
+** to content area of the pages.
+**
+** A szCell[] of zero means the size of that cell has not yet been computed.
+**
+** The cells come from as many as four different pages:
+**
+** -----------
+** | Parent |
+** -----------
+** / | \
+** / | \
+** --------- --------- ---------
+** |Child-1| |Child-2| |Child-3|
+** --------- --------- ---------
+**
+** The order of cells is in the array is for an index btree is:
+**
+** 1. All cells from Child-1 in order
+** 2. The first divider cell from Parent
+** 3. All cells from Child-2 in order
+** 4. The second divider cell from Parent
+** 5. All cells from Child-3 in order
+**
+** For a table-btree (with rowids) the items 2 and 4 are empty because
+** content exists only in leaves and there are no divider cells.
+**
+** For an index btree, the apEnd[] array holds pointer to the end of page
+** for Child-1, the Parent, Child-2, the Parent (again), and Child-3,
+** respectively. The ixNx[] array holds the number of cells contained in
+** each of these 5 stages, and all stages to the left. Hence:
+**
+** ixNx[0] = Number of cells in Child-1.
+** ixNx[1] = Number of cells in Child-1 plus 1 for first divider.
+** ixNx[2] = Number of cells in Child-1 and Child-2 + 1 for 1st divider.
+** ixNx[3] = Number of cells in Child-1 and Child-2 + both divider cells
+** ixNx[4] = Total number of cells.
+**
+** For a table-btree, the concept is similar, except only apEnd[0]..apEnd[2]
+** are used and they point to the leaf pages only, and the ixNx value are:
+**
+** ixNx[0] = Number of cells in Child-1.
+** ixNx[1] = Number of cells in Child-1 and Child-2.
+** ixNx[2] = Total number of cells.
+**
+** Sometimes when deleting, a child page can have zero cells. In those
+** cases, ixNx[] entries with higher indexes, and the corresponding apEnd[]
+** entries, shift down. The end result is that each ixNx[] entry should
+** be larger than the previous
*/
typedef struct CellArray CellArray;
struct CellArray {
@@ -69616,6 +77554,8 @@ struct CellArray {
MemPage *pRef; /* Reference page */
u8 **apCell; /* All cells begin balanced */
u16 *szCell; /* Local size of all cells in apCell[] */
+ u8 *apEnd[NB*2]; /* MemPage.aDataEnd values */
+ int ixNx[NB*2]; /* Index of at which we move to the next apEnd[] */
};
/*
@@ -69623,14 +77563,16 @@ struct CellArray {
** computed.
*/
static void populateCellCache(CellArray *p, int idx, int N){
+ MemPage *pRef = p->pRef;
+ u16 *szCell = p->szCell;
assert( idx>=0 && idx+N<=p->nCell );
while( N>0 ){
assert( p->apCell[idx]!=0 );
- if( p->szCell[idx]==0 ){
- p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]);
+ if( szCell[idx]==0 ){
+ szCell[idx] = pRef->xCellSize(pRef, p->apCell[idx]);
}else{
assert( CORRUPT_DB ||
- p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) );
+ szCell[idx]==pRef->xCellSize(pRef, p->apCell[idx]) );
}
idx++;
N--;
@@ -69653,49 +77595,72 @@ static u16 cachedCellSize(CellArray *p, int N){
}
/*
-** Array apCell[] contains pointers to nCell b-tree page cells. The
+** Array apCell[] contains pointers to nCell b-tree page cells. The
** szCell[] array contains the size in bytes of each cell. This function
** replaces the current contents of page pPg with the contents of the cell
** array.
**
** Some of the cells in apCell[] may currently be stored in pPg. This
-** function works around problems caused by this by making a copy of any
+** function works around problems caused by this by making a copy of any
** such cells before overwriting the page data.
**
-** The MemPage.nFree field is invalidated by this function. It is the
+** The MemPage.nFree field is invalidated by this function. It is the
** responsibility of the caller to set it correctly.
*/
static int rebuildPage(
- MemPage *pPg, /* Edit this page */
+ CellArray *pCArray, /* Content to be added to page pPg */
+ int iFirst, /* First cell in pCArray to use */
int nCell, /* Final number of cells on page */
- u8 **apCell, /* Array of cells */
- u16 *szCell /* Array of cell sizes */
+ MemPage *pPg /* The page to be reconstructed */
){
const int hdr = pPg->hdrOffset; /* Offset of header on pPg */
u8 * const aData = pPg->aData; /* Pointer to data for pPg */
const int usableSize = pPg->pBt->usableSize;
u8 * const pEnd = &aData[usableSize];
- int i;
+ int i = iFirst; /* Which cell to copy from pCArray*/
+ u32 j; /* Start of cell content area */
+ int iEnd = i+nCell; /* Loop terminator */
u8 *pCellptr = pPg->aCellIdx;
u8 *pTmp = sqlite3PagerTempSpace(pPg->pBt->pPager);
u8 *pData;
+ int k; /* Current slot in pCArray->apEnd[] */
+ u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */
+
+ assert( nCell>0 );
+ assert( i<iEnd );
+ j = get2byte(&aData[hdr+5]);
+ if( j>(u32)usableSize ){ j = 0; }
+ memcpy(&pTmp[j], &aData[j], usableSize - j);
- i = get2byte(&aData[hdr+5]);
- memcpy(&pTmp[i], &aData[i], usableSize - i);
+ for(k=0; ALWAYS(k<NB*2) && pCArray->ixNx[k]<=i; k++){}
+ pSrcEnd = pCArray->apEnd[k];
pData = pEnd;
- for(i=0; i<nCell; i++){
- u8 *pCell = apCell[i];
- if( SQLITE_WITHIN(pCell,aData,pEnd) ){
+ while( 1/*exit by break*/ ){
+ u8 *pCell = pCArray->apCell[i];
+ u16 sz = pCArray->szCell[i];
+ assert( sz>0 );
+ if( SQLITE_WITHIN(pCell,aData+j,pEnd) ){
+ if( ((uptr)(pCell+sz))>(uptr)pEnd ) return SQLITE_CORRUPT_BKPT;
pCell = &pTmp[pCell - aData];
+ }else if( (uptr)(pCell+sz)>(uptr)pSrcEnd
+ && (uptr)(pCell)<(uptr)pSrcEnd
+ ){
+ return SQLITE_CORRUPT_BKPT;
}
- pData -= szCell[i];
+
+ pData -= sz;
put2byte(pCellptr, (pData - aData));
pCellptr += 2;
if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT;
- memcpy(pData, pCell, szCell[i]);
- assert( szCell[i]==pPg->xCellSize(pPg, pCell) || CORRUPT_DB );
- testcase( szCell[i]!=pPg->xCellSize(pPg,pCell) );
+ memmove(pData, pCell, sz);
+ assert( sz==pPg->xCellSize(pPg, pCell) || CORRUPT_DB );
+ i++;
+ if( i>=iEnd ) break;
+ if( pCArray->ixNx[k]<=i ){
+ k++;
+ pSrcEnd = pCArray->apEnd[k];
+ }
}
/* The pPg->nFree field is now set incorrectly. The caller will fix it. */
@@ -69710,12 +77675,11 @@ static int rebuildPage(
}
/*
-** Array apCell[] contains nCell pointers to b-tree cells. Array szCell
-** contains the size in bytes of each such cell. This function attempts to
-** add the cells stored in the array to page pPg. If it cannot (because
-** the page needs to be defragmented before the cells will fit), non-zero
-** is returned. Otherwise, if the cells are added successfully, zero is
-** returned.
+** The pCArray objects contains pointers to b-tree cells and the cell sizes.
+** This function attempts to add the cells stored in the array to page pPg.
+** If it cannot (because the page needs to be defragmented before the cells
+** will fit), non-zero is returned. Otherwise, if the cells are added
+** successfully, zero is returned.
**
** Argument pCellptr points to the first entry in the cell-pointer array
** (part of page pPg) to populate. After cell apCell[0] is written to the
@@ -69723,7 +77687,7 @@ static int rebuildPage(
** cell in the array. It is the responsibility of the caller to ensure
** that it is safe to overwrite this part of the cell-pointer array.
**
-** When this function is called, *ppData points to the start of the
+** When this function is called, *ppData points to the start of the
** content area on page pPg. If the size of the content area is extended,
** *ppData is updated to point to the new start of the content area
** before returning.
@@ -69731,27 +77695,33 @@ static int rebuildPage(
** Finally, argument pBegin points to the byte immediately following the
** end of the space required by this page for the cell-pointer area (for
** all cells - not just those inserted by the current call). If the content
-** area must be extended to before this point in order to accomodate all
+** area must be extended to before this point in order to accommodate all
** cells in apCell[], then the cells do not fit and non-zero is returned.
*/
static int pageInsertArray(
MemPage *pPg, /* Page to add cells to */
u8 *pBegin, /* End of cell-pointer array */
- u8 **ppData, /* IN/OUT: Page content -area pointer */
+ u8 **ppData, /* IN/OUT: Page content-area pointer */
u8 *pCellptr, /* Pointer to cell-pointer area */
int iFirst, /* Index of first cell to add */
int nCell, /* Number of cells to add to pPg */
CellArray *pCArray /* Array of cells */
){
- int i;
- u8 *aData = pPg->aData;
- u8 *pData = *ppData;
- int iEnd = iFirst + nCell;
+ int i = iFirst; /* Loop counter - cell index to insert */
+ u8 *aData = pPg->aData; /* Complete page */
+ u8 *pData = *ppData; /* Content area. A subset of aData[] */
+ int iEnd = iFirst + nCell; /* End of loop. One past last cell to ins */
+ int k; /* Current slot in pCArray->apEnd[] */
+ u8 *pEnd; /* Maximum extent of cell data */
assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */
- for(i=iFirst; i<iEnd; i++){
+ if( iEnd<=iFirst ) return 0;
+ for(k=0; ALWAYS(k<NB*2) && pCArray->ixNx[k]<=i ; k++){}
+ pEnd = pCArray->apEnd[k];
+ while( 1 /*Exit by break*/ ){
int sz, rc;
u8 *pSlot;
- sz = cachedCellSize(pCArray, i);
+ assert( pCArray->szCell[i]!=0 );
+ sz = pCArray->szCell[i];
if( (aData[1]==0 && aData[2]==0) || (pSlot = pageFindSlot(pPg,sz,&rc))==0 ){
if( (pData - pBegin)<sz ) return 1;
pData -= sz;
@@ -69763,20 +77733,33 @@ static int pageInsertArray(
assert( (pSlot+sz)<=pCArray->apCell[i]
|| pSlot>=(pCArray->apCell[i]+sz)
|| CORRUPT_DB );
+ if( (uptr)(pCArray->apCell[i]+sz)>(uptr)pEnd
+ && (uptr)(pCArray->apCell[i])<(uptr)pEnd
+ ){
+ assert( CORRUPT_DB );
+ (void)SQLITE_CORRUPT_BKPT;
+ return 1;
+ }
memmove(pSlot, pCArray->apCell[i], sz);
put2byte(pCellptr, (pSlot - aData));
pCellptr += 2;
+ i++;
+ if( i>=iEnd ) break;
+ if( pCArray->ixNx[k]<=i ){
+ k++;
+ pEnd = pCArray->apEnd[k];
+ }
}
*ppData = pData;
return 0;
}
/*
-** Array apCell[] contains nCell pointers to b-tree cells. Array szCell
-** contains the size in bytes of each such cell. This function adds the
-** space associated with each cell in the array that is currently stored
-** within the body of pPg to the pPg free-list. The cell-pointers and other
-** fields of the page are not updated.
+** The pCArray object contains pointers to b-tree cells and their sizes.
+**
+** This function adds the space associated with each cell in the array
+** that is currently stored within the body of pPg to the pPg free-list.
+** The cell-pointers and other fields of the page are not updated.
**
** This function returns the total number of cells added to the free-list.
*/
@@ -69790,45 +77773,58 @@ static int pageFreeArray(
u8 * const pEnd = &aData[pPg->pBt->usableSize];
u8 * const pStart = &aData[pPg->hdrOffset + 8 + pPg->childPtrSize];
int nRet = 0;
- int i;
+ int i, j;
int iEnd = iFirst + nCell;
- u8 *pFree = 0;
- int szFree = 0;
+ int nFree = 0;
+ int aOfst[10];
+ int aAfter[10];
for(i=iFirst; i<iEnd; i++){
u8 *pCell = pCArray->apCell[i];
if( SQLITE_WITHIN(pCell, pStart, pEnd) ){
int sz;
+ int iAfter;
+ int iOfst;
/* No need to use cachedCellSize() here. The sizes of all cells that
** are to be freed have already been computing while deciding which
** cells need freeing */
sz = pCArray->szCell[i]; assert( sz>0 );
- if( pFree!=(pCell + sz) ){
- if( pFree ){
- assert( pFree>aData && (pFree - aData)<65536 );
- freeSpace(pPg, (u16)(pFree - aData), szFree);
+ iOfst = (u16)(pCell - aData);
+ iAfter = iOfst+sz;
+ for(j=0; j<nFree; j++){
+ if( aOfst[j]==iAfter ){
+ aOfst[j] = iOfst;
+ break;
+ }else if( aAfter[j]==iOfst ){
+ aAfter[j] = iAfter;
+ break;
}
- pFree = pCell;
- szFree = sz;
- if( pFree+sz>pEnd ) return 0;
- }else{
- pFree = pCell;
- szFree += sz;
+ }
+ if( j>=nFree ){
+ if( nFree>=(int)(sizeof(aOfst)/sizeof(aOfst[0])) ){
+ for(j=0; j<nFree; j++){
+ freeSpace(pPg, aOfst[j], aAfter[j]-aOfst[j]);
+ }
+ nFree = 0;
+ }
+ aOfst[nFree] = iOfst;
+ aAfter[nFree] = iAfter;
+ if( &aData[iAfter]>pEnd ) return 0;
+ nFree++;
}
nRet++;
}
}
- if( pFree ){
- assert( pFree>aData && (pFree - aData)<65536 );
- freeSpace(pPg, (u16)(pFree - aData), szFree);
+ for(j=0; j<nFree; j++){
+ freeSpace(pPg, aOfst[j], aAfter[j]-aOfst[j]);
}
return nRet;
}
/*
-** apCell[] and szCell[] contains pointers to and sizes of all cells in the
-** pages being balanced. The current page, pPg, has pPg->nCell cells starting
-** with apCell[iOld]. After balancing, this page should hold nNew cells
+** pCArray contains pointers to and sizes of all cells in the page being
+** balanced. The current page, pPg, has pPg->nCell cells starting with
+** pCArray->apCell[iOld]. After balancing, this page should hold nNew cells
** starting at apCell[iNew].
**
** This routine makes the necessary adjustments to pPg so that it contains
@@ -69860,22 +77856,28 @@ static int editPage(
#endif
/* Remove cells from the start and end of the page */
+ assert( nCell>=0 );
if( iOld<iNew ){
int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray);
+ if( NEVER(nShift>nCell) ) return SQLITE_CORRUPT_BKPT;
memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2);
nCell -= nShift;
}
if( iNewEnd < iOldEnd ){
- nCell -= pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
+ int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
+ assert( nCell>=nTail );
+ nCell -= nTail;
}
- pData = &aData[get2byteNotZero(&aData[hdr+5])];
+ pData = &aData[get2byte(&aData[hdr+5])];
if( pData<pBegin ) goto editpage_fail;
+ if( NEVER(pData>pPg->aDataEnd) ) goto editpage_fail;
/* Add cells to the start of the page */
if( iNew<iOld ){
int nAdd = MIN(nNew,iOld-iNew);
assert( (iOld-iNew)<nNew || nCell==0 || CORRUPT_DB );
+ assert( nAdd>=0 );
pCellptr = pPg->aCellIdx;
memmove(&pCellptr[nAdd*2], pCellptr, nCell*2);
if( pageInsertArray(
@@ -69890,8 +77892,11 @@ static int editPage(
int iCell = (iOld + pPg->aiOvfl[i]) - iNew;
if( iCell>=0 && iCell<nNew ){
pCellptr = &pPg->aCellIdx[iCell * 2];
- memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2);
+ if( nCell>iCell ){
+ memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2);
+ }
nCell++;
+ cachedCellSize(pCArray, iCell+iNew);
if( pageInsertArray(
pPg, pBegin, &pData, pCellptr,
iCell+iNew, 1, pCArray
@@ -69900,6 +77905,7 @@ static int editPage(
}
/* Append cells to the end of the page */
+ assert( nCell>=0 );
pCellptr = &pPg->aCellIdx[nCell*2];
if( pageInsertArray(
pPg, pBegin, &pData, pCellptr,
@@ -69927,25 +77933,11 @@ static int editPage(
return SQLITE_OK;
editpage_fail:
/* Unable to edit this page. Rebuild it from scratch instead. */
+ if( nNew<1 ) return SQLITE_CORRUPT_BKPT;
populateCellCache(pCArray, iNew, nNew);
- return rebuildPage(pPg, nNew, &pCArray->apCell[iNew], &pCArray->szCell[iNew]);
+ return rebuildPage(pCArray, iNew, nNew, pPg);
}
-/*
-** The following parameters determine how many adjacent pages get involved
-** in a balancing operation. NN is the number of neighbors on either side
-** of the page that participate in the balancing operation. NB is the
-** total number of pages that participate, including the target page and
-** NN neighbors on either side.
-**
-** The minimum value of NN is 1 (of course). Increasing NN above 1
-** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance
-** in exchange for a larger degradation in INSERT and UPDATE performance.
-** The value of NN appears to give the best results overall.
-*/
-#define NN 1 /* Number of neighbors on either side of pPage */
-#define NB (NN*2+1) /* Total pages involved in the balance */
-
#ifndef SQLITE_OMIT_QUICKBALANCE
/*
@@ -69981,10 +77973,11 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
assert( pPage->nOverflow==1 );
- /* This error condition is now caught prior to reaching this function */
- if( NEVER(pPage->nCell==0) ) return SQLITE_CORRUPT_BKPT;
+ if( pPage->nCell==0 ) return SQLITE_CORRUPT_BKPT; /* dbfuzz001.test */
+ assert( pPage->nFree>=0 );
+ assert( pParent->nFree>=0 );
- /* Allocate a new page. This page will become the right-sibling of
+ /* Allocate a new page. This page will become the right-sibling of
** pPage. Make the parent page writable, so that the new divider cell
** may be inserted. If both these operations are successful, proceed.
*/
@@ -69996,37 +77989,47 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
u8 *pCell = pPage->apOvfl[0];
u16 szCell = pPage->xCellSize(pPage, pCell);
u8 *pStop;
+ CellArray b;
assert( sqlite3PagerIswriteable(pNew->pDbPage) );
- assert( pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) );
+ assert( CORRUPT_DB || pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) );
zeroPage(pNew, PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF);
- rc = rebuildPage(pNew, 1, &pCell, &szCell);
- if( NEVER(rc) ) return rc;
+ b.nCell = 1;
+ b.pRef = pPage;
+ b.apCell = &pCell;
+ b.szCell = &szCell;
+ b.apEnd[0] = pPage->aDataEnd;
+ b.ixNx[0] = 2;
+ rc = rebuildPage(&b, 0, 1, pNew);
+ if( NEVER(rc) ){
+ releasePage(pNew);
+ return rc;
+ }
pNew->nFree = pBt->usableSize - pNew->cellOffset - 2 - szCell;
/* If this is an auto-vacuum database, update the pointer map
- ** with entries for the new page, and any pointer from the
+ ** with entries for the new page, and any pointer from the
** cell on the page to an overflow page. If either of these
** operations fails, the return code is set, but the contents
- ** of the parent page are still manipulated by thh code below.
+ ** of the parent page are still manipulated by the code below.
** That is Ok, at this point the parent page is guaranteed to
** be marked as dirty. Returning an error code will cause a
** rollback, undoing any changes made to the parent page.
*/
- if( ISAUTOVACUUM ){
+ if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc);
if( szCell>pNew->minLocal ){
- ptrmapPutOvflPtr(pNew, pCell, &rc);
+ ptrmapPutOvflPtr(pNew, pNew, pCell, &rc);
}
}
-
+
/* Create a divider cell to insert into pParent. The divider cell
** consists of a 4-byte page number (the page number of pPage) and
** a variable length key value (which must be the same value as the
** largest key on pPage).
**
- ** To find the largest key value on pPage, first find the right-most
- ** cell on pPage. The first two fields of this cell are the
+ ** To find the largest key value on pPage, first find the right-most
+ ** cell on pPage. The first two fields of this cell are the
** record-length (a variable length integer at most 32-bits in size)
** and the key value (a variable length integer, may have any value).
** The first of the while(...) loops below skips over the record-length
@@ -70041,13 +78044,13 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
/* Insert the new divider cell into pParent. */
if( rc==SQLITE_OK ){
- insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
- 0, pPage->pgno, &rc);
+ rc = insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
+ 0, pPage->pgno);
}
/* Set the right-child pointer of pParent to point to the new page. */
put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew);
-
+
/* Release the reference to the new page. */
releasePage(pNew);
}
@@ -70059,7 +78062,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
#if 0
/*
** This function does not contribute anything to the operation of SQLite.
-** it is sometimes activated temporarily while debugging code responsible
+** it is sometimes activated temporarily while debugging code responsible
** for setting pointer-map entries.
*/
static int ptrmapCheckPages(MemPage **apPage, int nPage){
@@ -70074,7 +78077,7 @@ static int ptrmapCheckPages(MemPage **apPage, int nPage){
for(j=0; j<pPage->nCell; j++){
CellInfo info;
u8 *z;
-
+
z = findCell(pPage, j);
pPage->xParseCell(pPage, z, &info);
if( info.nLocal<info.nPayload ){
@@ -70099,7 +78102,7 @@ static int ptrmapCheckPages(MemPage **apPage, int nPage){
#endif
/*
-** This function is used to copy the contents of the b-tree node stored
+** This function is used to copy the contents of the b-tree node stored
** on page pFrom to page pTo. If page pFrom was not a leaf page, then
** the pointer-map entries for each child page are updated so that the
** parent page stored in the pointer map is page pTo. If pFrom contained
@@ -70107,11 +78110,11 @@ static int ptrmapCheckPages(MemPage **apPage, int nPage){
** map entries are also updated so that the parent page is page pTo.
**
** If pFrom is currently carrying any overflow cells (entries in the
-** MemPage.apOvfl[] array), they are not copied to pTo.
+** MemPage.apOvfl[] array), they are not copied to pTo.
**
** Before returning, page pTo is reinitialized using btreeInitPage().
**
-** The performance of this function is not critical. It is only used by
+** The performance of this function is not critical. It is only used by
** the balance_shallower() and balance_deeper() procedures, neither of
** which are called often under normal circumstances.
*/
@@ -70124,33 +78127,34 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
int const iToHdr = ((pTo->pgno==1) ? 100 : 0);
int rc;
int iData;
-
-
+
+
assert( pFrom->isInit );
assert( pFrom->nFree>=iToHdr );
assert( get2byte(&aFrom[iFromHdr+5]) <= (int)pBt->usableSize );
-
+
/* Copy the b-tree node content from page pFrom to page pTo. */
iData = get2byte(&aFrom[iFromHdr+5]);
memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData);
memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell);
-
+
/* Reinitialize page pTo so that the contents of the MemPage structure
** match the new data. The initialization of pTo can actually fail under
- ** fairly obscure circumstances, even though it is a copy of initialized
+ ** fairly obscure circumstances, even though it is a copy of initialized
** page pFrom.
*/
pTo->isInit = 0;
rc = btreeInitPage(pTo);
+ if( rc==SQLITE_OK ) rc = btreeComputeFreeSpace(pTo);
if( rc!=SQLITE_OK ){
*pRC = rc;
return;
}
-
+
/* If this is an auto-vacuum database, update the pointer-map entries
** for any b-tree or overflow pages that pTo now contains the pointers to.
*/
- if( ISAUTOVACUUM ){
+ if( ISAUTOVACUUM(pBt) ){
*pRC = setChildPtrmaps(pTo);
}
}
@@ -70161,13 +78165,13 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
** (hereafter "the page") and up to 2 siblings so that all pages have about the
** same amount of free space. Usually a single sibling on either side of the
** page are used in the balancing, though both siblings might come from one
-** side if the page is the first or last child of its parent. If the page
+** side if the page is the first or last child of its parent. If the page
** has fewer than 2 siblings (something which can only happen if the page
** is a root page or a child of a root page) then all available siblings
** participate in the balancing.
**
-** The number of siblings of the page might be increased or decreased by
-** one or two in an effort to keep pages nearly full but not over full.
+** The number of siblings of the page might be increased or decreased by
+** one or two in an effort to keep pages nearly full but not over full.
**
** Note that when this routine is called, some of the cells on the page
** might not actually be stored in MemPage.aData[]. This can happen
@@ -70178,7 +78182,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
** inserted into or removed from the parent page (pParent). Doing so
** may cause the parent page to become overfull or underfull. If this
** happens, it is the responsibility of the caller to invoke the correct
-** balancing routine to fix this problem (see the balance() routine).
+** balancing routine to fix this problem (see the balance() routine).
**
** If this routine fails for any reason, it might leave the database
** in a corrupted state. So if this routine fails, the database should
@@ -70193,7 +78197,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
** of the page-size, the aOvflSpace[] buffer is guaranteed to be large
** enough for all overflow cells.
**
-** If aOvflSpace is set to a null pointer, this function returns
+** If aOvflSpace is set to a null pointer, this function returns
** SQLITE_NOMEM.
*/
static int balance_nonroot(
@@ -70228,23 +78232,16 @@ static int balance_nonroot(
Pgno pgno; /* Temp var to store a page number in */
u8 abDone[NB+2]; /* True after i'th new page is populated */
Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */
- Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */
- u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */
- CellArray b; /* Parsed information on cells being balanced */
+ CellArray b; /* Parsed information on cells being balanced */
memset(abDone, 0, sizeof(abDone));
- b.nCell = 0;
- b.apCell = 0;
+ memset(&b, 0, sizeof(b));
pBt = pParent->pBt;
assert( sqlite3_mutex_held(pBt->mutex) );
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
-#if 0
- TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno));
-#endif
-
/* At this point pParent may have at most one overflow cell. And if
- ** this overflow cell is present, it must be the cell with
+ ** this overflow cell is present, it must be the cell with
** index iParentIdx. This scenario comes about when this function
** is called (indirectly) from sqlite3BtreeDelete().
*/
@@ -70254,12 +78251,13 @@ static int balance_nonroot(
if( !aOvflSpace ){
return SQLITE_NOMEM_BKPT;
}
+ assert( pParent->nFree>=0 );
- /* Find the sibling pages to balance. Also locate the cells in pParent
- ** that divide the siblings. An attempt is made to find NN siblings on
- ** either side of pPage. More siblings are taken from one side, however,
+ /* Find the sibling pages to balance. Also locate the cells in pParent
+ ** that divide the siblings. An attempt is made to find NN siblings on
+ ** either side of pPage. More siblings are taken from one side, however,
** if there are fewer than NN siblings on the other side. If pParent
- ** has NB or fewer children then all children of pParent are taken.
+ ** has NB or fewer children then all children of pParent are taken.
**
** This loop also drops the divider cells from the parent page. This
** way, the remainder of the function does not have to deal with any
@@ -70271,7 +78269,7 @@ static int balance_nonroot(
nxDiv = 0;
}else{
assert( bBulk==0 || bBulk==1 );
- if( iParentIdx==0 ){
+ if( iParentIdx==0 ){
nxDiv = 0;
}else if( iParentIdx==i ){
nxDiv = i-2+bBulk;
@@ -70288,12 +78286,21 @@ static int balance_nonroot(
}
pgno = get4byte(pRight);
while( 1 ){
- rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = getAndInitPage(pBt, pgno, &apOld[i], 0);
+ }
if( rc ){
memset(apOld, 0, (i+1)*sizeof(MemPage*));
goto balance_cleanup;
}
- nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow;
+ if( apOld[i]->nFree<0 ){
+ rc = btreeComputeFreeSpace(apOld[i]);
+ if( rc ){
+ memset(apOld, 0, (i)*sizeof(MemPage*));
+ goto balance_cleanup;
+ }
+ }
+ nMaxCells += apOld[i]->nCell + ArraySize(pParent->apOvfl);
if( (i--)==0 ) break;
if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){
@@ -70311,7 +78318,7 @@ static int balance_nonroot(
** This is safe because dropping a cell only overwrites the first
** four bytes of it, and this function does not need the first
** four bytes of the divider cell. So the pointer is safe to use
- ** later on.
+ ** later on.
**
** But not if we are in secure-delete mode. In secure-delete mode,
** the dropCell() routine will overwrite the entire cell with zeroes.
@@ -70321,12 +78328,10 @@ static int balance_nonroot(
if( pBt->btsFlags & BTS_FAST_SECURE ){
int iOff;
+ /* If the following if() condition is not true, the db is corrupted.
+ ** The call to dropCell() below will detect this. */
iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
- if( (iOff+szNew[i])>(int)pBt->usableSize ){
- rc = SQLITE_CORRUPT_BKPT;
- memset(apOld, 0, (i+1)*sizeof(MemPage*));
- goto balance_cleanup;
- }else{
+ if( (iOff+szNew[i])<=(int)pBt->usableSize ){
memcpy(&aOvflSpace[iOff], apDiv[i], szNew[i]);
apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData];
}
@@ -70347,7 +78352,7 @@ static int balance_nonroot(
+ nMaxCells*sizeof(u16) /* b.szCell */
+ pBt->pageSize; /* aSpace1 */
- assert( szScratch<=6*(int)pBt->pageSize );
+ assert( szScratch<=7*(int)pBt->pageSize );
b.apCell = sqlite3StackAllocRaw(0, szScratch );
if( b.apCell==0 ){
rc = SQLITE_NOMEM_BKPT;
@@ -70383,6 +78388,7 @@ static int balance_nonroot(
u16 maskPage = pOld->maskPage;
u8 *piCell = aData + pOld->cellOffset;
u8 *piEnd;
+ VVA_ONLY( int nCellAtStart = b.nCell; )
/* Verify that all sibling pages are of the same "type" (table-leaf,
** table-interior, index-leaf, or index-interior).
@@ -70411,6 +78417,10 @@ static int balance_nonroot(
*/
memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow));
if( pOld->nOverflow>0 ){
+ if( NEVER(limit<pOld->aiOvfl[0]) ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto balance_cleanup;
+ }
limit = pOld->aiOvfl[0];
for(j=0; j<limit; j++){
b.apCell[b.nCell] = aData + (maskPage & get2byteAligned(piCell));
@@ -70430,6 +78440,7 @@ static int balance_nonroot(
piCell += 2;
b.nCell++;
}
+ assert( (b.nCell-nCellAtStart)==(pOld->nCell+pOld->nOverflow) );
cntOld[i] = b.nCell;
if( i<nOld-1 && !leafData){
@@ -70447,7 +78458,7 @@ static int balance_nonroot(
b.szCell[b.nCell] = b.szCell[b.nCell] - leafCorrection;
if( !pOld->leaf ){
assert( leafCorrection==0 );
- assert( pOld->hdrOffset==0 );
+ assert( pOld->hdrOffset==0 || CORRUPT_DB );
/* The right pointer of the child page pOld becomes the left
** pointer of the divider cell */
memcpy(b.apCell[b.nCell], &pOld->aData[8], 4);
@@ -70470,7 +78481,7 @@ static int balance_nonroot(
** Figure out the number of pages needed to hold all b.nCell cells.
** Store this number in "k". Also compute szNew[] which is the total
** size of all cells on the i-th page and cntNew[] which is the index
- ** in b.apCell[] of the cell that divides page i from page i+1.
+ ** in b.apCell[] of the cell that divides page i from page i+1.
** cntNew[k] should equal b.nCell.
**
** Values computed by this block:
@@ -70480,11 +78491,22 @@ static int balance_nonroot(
** cntNew[i]: Index in b.apCell[] and b.szCell[] for the first cell to
** the right of the i-th sibling page.
** usableSpace: Number of bytes of space available on each sibling.
- **
+ **
*/
usableSpace = pBt->usableSize - 12 + leafCorrection;
- for(i=0; i<nOld; i++){
+ for(i=k=0; i<nOld; i++, k++){
MemPage *p = apOld[i];
+ b.apEnd[k] = p->aDataEnd;
+ b.ixNx[k] = cntOld[i];
+ if( k && b.ixNx[k]==b.ixNx[k-1] ){
+ k--; /* Omit b.ixNx[] entry for child pages with no cells */
+ }
+ if( !leafData ){
+ k++;
+ b.apEnd[k] = pParent->aDataEnd;
+ b.ixNx[k] = cntOld[i]+1;
+ }
+ assert( p->nFree>=0 );
szNew[i] = usableSpace - p->nFree;
for(j=0; j<p->nOverflow; j++){
szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]);
@@ -70556,15 +78578,17 @@ static int balance_nonroot(
d = r + 1 - leafData;
(void)cachedCellSize(&b, d);
do{
+ int szR, szD;
assert( d<nMaxCells );
assert( r<nMaxCells );
- (void)cachedCellSize(&b, r);
+ szR = cachedCellSize(&b, r);
+ szD = b.szCell[d];
if( szRight!=0
- && (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+(i==k-1?0:2)))){
+ && (bBulk || szRight+szD+2 > szLeft-(szR+(i==k-1?0:2)))){
break;
}
- szRight += b.szCell[d] + 2;
- szLeft -= b.szCell[r] + 2;
+ szRight += szD + 2;
+ szLeft -= szR + 2;
cntNew[i-1] = r;
r--;
d--;
@@ -70577,7 +78601,7 @@ static int balance_nonroot(
}
}
- /* Sanity check: For a non-corrupt database file one of the follwing
+ /* Sanity check: For a non-corrupt database file one of the following
** must be true:
** (1) We found one or more cells (cntNew[0])>0), or
** (2) pPage is a virtual root page. A virtual root page is when
@@ -70585,7 +78609,7 @@ static int balance_nonroot(
** that page.
*/
assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) || CORRUPT_DB);
- TRACE(("BALANCE: old: %d(nc=%d) %d(nc=%d) %d(nc=%d)\n",
+ TRACE(("BALANCE: old: %u(nc=%u) %u(nc=%u) %u(nc=%u)\n",
apOld[0]->pgno, apOld[0]->nCell,
nOld>=2 ? apOld[1]->pgno : 0, nOld>=2 ? apOld[1]->nCell : 0,
nOld>=3 ? apOld[2]->pgno : 0, nOld>=3 ? apOld[2]->nCell : 0
@@ -70602,6 +78626,11 @@ static int balance_nonroot(
apOld[i] = 0;
rc = sqlite3PagerWrite(pNew->pDbPage);
nNew++;
+ if( sqlite3PagerPageRefcount(pNew->pDbPage)!=1+(i==(iParentIdx-nxDiv))
+ && rc==SQLITE_OK
+ ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }
if( rc ) goto balance_cleanup;
}else{
assert( i>0 );
@@ -70613,7 +78642,7 @@ static int balance_nonroot(
cntOld[i] = b.nCell;
/* Set the pointer-map entry for the new sibling page. */
- if( ISAUTOVACUUM ){
+ if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc);
if( rc!=SQLITE_OK ){
goto balance_cleanup;
@@ -70623,52 +78652,49 @@ static int balance_nonroot(
}
/*
- ** Reassign page numbers so that the new pages are in ascending order.
+ ** Reassign page numbers so that the new pages are in ascending order.
** This helps to keep entries in the disk file in order so that a scan
- ** of the table is closer to a linear scan through the file. That in turn
+ ** of the table is closer to a linear scan through the file. That in turn
** helps the operating system to deliver pages from the disk more rapidly.
**
- ** An O(n^2) insertion sort algorithm is used, but since n is never more
- ** than (NB+2) (a small constant), that should not be a problem.
+ ** An O(N*N) sort algorithm is used, but since N is never more than NB+2
+ ** (5), that is not a performance concern.
**
- ** When NB==3, this one optimization makes the database about 25% faster
+ ** When NB==3, this one optimization makes the database about 25% faster
** for large insertions and deletions.
*/
for(i=0; i<nNew; i++){
- aPgOrder[i] = aPgno[i] = apNew[i]->pgno;
- aPgFlags[i] = apNew[i]->pDbPage->flags;
- for(j=0; j<i; j++){
- if( aPgno[j]==aPgno[i] ){
- /* This branch is taken if the set of sibling pages somehow contains
- ** duplicate entries. This can happen if the database is corrupt.
- ** It would be simpler to detect this as part of the loop below, but
- ** we do the detection here in order to avoid populating the pager
- ** cache with two separate objects associated with the same
- ** page number. */
- assert( CORRUPT_DB );
- rc = SQLITE_CORRUPT_BKPT;
- goto balance_cleanup;
- }
- }
+ aPgno[i] = apNew[i]->pgno;
+ assert( apNew[i]->pDbPage->flags & PGHDR_WRITEABLE );
+ assert( apNew[i]->pDbPage->flags & PGHDR_DIRTY );
}
- for(i=0; i<nNew; i++){
- int iBest = 0; /* aPgno[] index of page number to use */
- for(j=1; j<nNew; j++){
- if( aPgOrder[j]<aPgOrder[iBest] ) iBest = j;
- }
- pgno = aPgOrder[iBest];
- aPgOrder[iBest] = 0xffffffff;
- if( iBest!=i ){
- if( iBest>i ){
- sqlite3PagerRekey(apNew[iBest]->pDbPage, pBt->nPage+iBest+1, 0);
- }
- sqlite3PagerRekey(apNew[i]->pDbPage, pgno, aPgFlags[iBest]);
- apNew[i]->pgno = pgno;
+ for(i=0; i<nNew-1; i++){
+ int iB = i;
+ for(j=i+1; j<nNew; j++){
+ if( apNew[j]->pgno < apNew[iB]->pgno ) iB = j;
}
- }
- TRACE(("BALANCE: new: %d(%d nc=%d) %d(%d nc=%d) %d(%d nc=%d) "
- "%d(%d nc=%d) %d(%d nc=%d)\n",
+ /* If apNew[i] has a page number that is bigger than any of the
+ ** subsequence apNew[i] entries, then swap apNew[i] with the subsequent
+ ** entry that has the smallest page number (which we know to be
+ ** entry apNew[iB]).
+ */
+ if( iB!=i ){
+ Pgno pgnoA = apNew[i]->pgno;
+ Pgno pgnoB = apNew[iB]->pgno;
+ Pgno pgnoTemp = (PENDING_BYTE/pBt->pageSize)+1;
+ u16 fgA = apNew[i]->pDbPage->flags;
+ u16 fgB = apNew[iB]->pDbPage->flags;
+ sqlite3PagerRekey(apNew[i]->pDbPage, pgnoTemp, fgB);
+ sqlite3PagerRekey(apNew[iB]->pDbPage, pgnoA, fgA);
+ sqlite3PagerRekey(apNew[i]->pDbPage, pgnoB, fgB);
+ apNew[i]->pgno = pgnoB;
+ apNew[iB]->pgno = pgnoA;
+ }
+ }
+
+ TRACE(("BALANCE: new: %u(%u nc=%u) %u(%u nc=%u) %u(%u nc=%u) "
+ "%u(%u nc=%u) %u(%u nc=%u)\n",
apNew[0]->pgno, szNew[0], cntNew[0],
nNew>=2 ? apNew[1]->pgno : 0, nNew>=2 ? szNew[1] : 0,
nNew>=2 ? cntNew[1] - cntNew[0] - !leafData : 0,
@@ -70681,17 +78707,19 @@ static int balance_nonroot(
));
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
+ assert( nNew>=1 && nNew<=ArraySize(apNew) );
+ assert( apNew[nNew-1]!=0 );
put4byte(pRight, apNew[nNew-1]->pgno);
/* If the sibling pages are not leaves, ensure that the right-child pointer
- ** of the right-most new sibling page is set to the value that was
+ ** of the right-most new sibling page is set to the value that was
** originally in the same field of the right-most old sibling page. */
if( (pageFlags & PTF_LEAF)==0 && nOld!=nNew ){
MemPage *pOld = (nNew>nOld ? apNew : apOld)[nOld-1];
memcpy(&apNew[nNew-1]->aData[8], &pOld->aData[8], 4);
}
- /* Make any required updates to pointer map entries associated with
+ /* Make any required updates to pointer map entries associated with
** cells stored on sibling pages following the balance operation. Pointer
** map entries associated with divider cells are set by the insertCell()
** routine. The associated pointer map entries are:
@@ -70702,25 +78730,26 @@ static int balance_nonroot(
** b) if the sibling pages are not leaves, the child page associated
** with the cell.
**
- ** If the sibling pages are not leaves, then the pointer map entry
- ** associated with the right-child of each sibling may also need to be
- ** updated. This happens below, after the sibling pages have been
+ ** If the sibling pages are not leaves, then the pointer map entry
+ ** associated with the right-child of each sibling may also need to be
+ ** updated. This happens below, after the sibling pages have been
** populated, not here.
*/
- if( ISAUTOVACUUM ){
- MemPage *pNew = apNew[0];
- u8 *aOld = pNew->aData;
+ if( ISAUTOVACUUM(pBt) ){
+ MemPage *pOld;
+ MemPage *pNew = pOld = apNew[0];
int cntOldNext = pNew->nCell + pNew->nOverflow;
- int usableSize = pBt->usableSize;
int iNew = 0;
int iOld = 0;
for(i=0; i<b.nCell; i++){
u8 *pCell = b.apCell[i];
- if( i==cntOldNext ){
- MemPage *pOld = (++iOld)<nNew ? apNew[iOld] : apOld[iOld];
+ while( i==cntOldNext ){
+ iOld++;
+ assert( iOld<nNew || iOld<nOld );
+ assert( iOld>=0 && iOld<NB );
+ pOld = iOld<nNew ? apNew[iOld] : apOld[iOld];
cntOldNext += pOld->nCell + pOld->nOverflow + !leafData;
- aOld = pOld->aData;
}
if( i==cntNew[iNew] ){
pNew = apNew[++iNew];
@@ -70728,20 +78757,20 @@ static int balance_nonroot(
}
/* Cell pCell is destined for new sibling page pNew. Originally, it
- ** was either part of sibling page iOld (possibly an overflow cell),
+ ** was either part of sibling page iOld (possibly an overflow cell),
** or else the divider cell to the left of sibling page iOld. So,
** if sibling page iOld had the same page number as pNew, and if
** pCell really was a part of sibling page iOld (not a divider or
** overflow cell), we can skip updating the pointer map entries. */
if( iOld>=nNew
|| pNew->pgno!=aPgno[iOld]
- || !SQLITE_WITHIN(pCell,aOld,&aOld[usableSize])
+ || !SQLITE_WITHIN(pCell,pOld->aData,pOld->aDataEnd)
){
if( !leafCorrection ){
ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc);
}
if( cachedCellSize(&b,i)>pNew->minLocal ){
- ptrmapPutOvflPtr(pNew, pCell, &rc);
+ ptrmapPutOvflPtr(pNew, pOld, pCell, &rc);
}
if( rc ) goto balance_cleanup;
}
@@ -70753,6 +78782,7 @@ static int balance_nonroot(
u8 *pCell;
u8 *pTemp;
int sz;
+ u8 *pSrcEnd;
MemPage *pNew = apNew[i];
j = cntNew[i];
@@ -70764,9 +78794,9 @@ static int balance_nonroot(
if( !pNew->leaf ){
memcpy(&pNew->aData[8], pCell, 4);
}else if( leafData ){
- /* If the tree is a leaf-data tree, and the siblings are leaves,
- ** then there is no divider cell in b.apCell[]. Instead, the divider
- ** cell consists of the integer key for the right-most cell of
+ /* If the tree is a leaf-data tree, and the siblings are leaves,
+ ** then there is no divider cell in b.apCell[]. Instead, the divider
+ ** cell consists of the integer key for the right-most cell of
** the sibling-page assembled above only.
*/
CellInfo info;
@@ -70779,9 +78809,9 @@ static int balance_nonroot(
pCell -= 4;
/* Obscure case for non-leaf-data trees: If the cell at pCell was
** previously stored on a leaf node, and its reported size was 4
- ** bytes, then it may actually be smaller than this
+ ** bytes, then it may actually be smaller than this
** (see btreeParseCellPtr(), 4 bytes is the minimum size of
- ** any cell). But it is important to pass the correct size to
+ ** any cell). But it is important to pass the correct size to
** insertCell(), so reparse the cell now.
**
** This can only happen for b-trees used to evaluate "IN (SELECT ...)"
@@ -70796,7 +78826,13 @@ static int balance_nonroot(
iOvflSpace += sz;
assert( sz<=pBt->maxLocal+23 );
assert( iOvflSpace <= (int)pBt->pageSize );
- insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno, &rc);
+ for(k=0; ALWAYS(k<NB*2) && b.ixNx[k]<=j; k++){}
+ pSrcEnd = b.apEnd[k];
+ if( SQLITE_OVERFLOW(pSrcEnd, pCell, pCell+sz) ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto balance_cleanup;
+ }
+ rc = insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno);
if( rc!=SQLITE_OK ) goto balance_cleanup;
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
}
@@ -70826,6 +78862,8 @@ static int balance_nonroot(
for(i=1-nNew; i<nNew; i++){
int iPg = i<0 ? -i : i;
assert( iPg>=0 && iPg<nNew );
+ assert( iPg>=1 || i>=0 );
+ assert( iPg<ArraySize(cntOld) );
if( abDone[iPg] ) continue; /* Skip pages already processed */
if( i>=0 /* On the upwards pass, or... */
|| cntOld[iPg-1]>=cntNew[iPg-1] /* Condition (1) is true */
@@ -70873,8 +78911,8 @@ static int balance_nonroot(
** b-tree structure by one. This is described as the "balance-shallower"
** sub-algorithm in some documentation.
**
- ** If this is an auto-vacuum database, the call to copyNodeContent()
- ** sets all pointer-map entries corresponding to database image pages
+ ** If this is an auto-vacuum database, the call to copyNodeContent()
+ ** sets all pointer-map entries corresponding to database image pages
** for which the pointer is stored within the content being copied.
**
** It is critical that the child page be defragmented before being
@@ -70885,13 +78923,14 @@ static int balance_nonroot(
assert( nNew==1 || CORRUPT_DB );
rc = defragmentPage(apNew[0], -1);
testcase( rc!=SQLITE_OK );
- assert( apNew[0]->nFree ==
- (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2)
+ assert( apNew[0]->nFree ==
+ (get2byteNotZero(&apNew[0]->aData[5]) - apNew[0]->cellOffset
+ - apNew[0]->nCell*2)
|| rc!=SQLITE_OK
);
copyNodeContent(apNew[0], pParent, &rc);
freePage(apNew[0], &rc);
- }else if( ISAUTOVACUUM && !leafCorrection ){
+ }else if( ISAUTOVACUUM(pBt) && !leafCorrection ){
/* Fix the pointer map entries associated with the right-child of each
** sibling page. All other pointer map entries have already been taken
** care of. */
@@ -70902,7 +78941,7 @@ static int balance_nonroot(
}
assert( pParent->isInit );
- TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n",
+ TRACE(("BALANCE: finished: old=%u new=%u cells=%u\n",
nOld, nNew, b.nCell));
/* Free any old pages that were not reused as new pages.
@@ -70912,9 +78951,9 @@ static int balance_nonroot(
}
#if 0
- if( ISAUTOVACUUM && rc==SQLITE_OK && apNew[0]->isInit ){
+ if( ISAUTOVACUUM(pBt) && rc==SQLITE_OK && apNew[0]->isInit ){
/* The ptrmapCheckPages() contains assert() statements that verify that
- ** all pointer map pages are set correctly. This is helpful while
+ ** all pointer map pages are set correctly. This is helpful while
** debugging. This is usually disabled because a corrupt database may
** cause an assert() statement to fail. */
ptrmapCheckPages(apNew, nNew);
@@ -70944,15 +78983,15 @@ balance_cleanup:
**
** A new child page is allocated and the contents of the current root
** page, including overflow cells, are copied into the child. The root
-** page is then overwritten to make it an empty page with the right-child
+** page is then overwritten to make it an empty page with the right-child
** pointer pointing to the new page.
**
-** Before returning, all pointer-map entries corresponding to pages
+** Before returning, all pointer-map entries corresponding to pages
** that the new child-page now contains pointers to are updated. The
** entry corresponding to the new right-child pointer of the root
** page is also updated.
**
-** If successful, *ppChild is set to contain a reference to the child
+** If successful, *ppChild is set to contain a reference to the child
** page and SQLITE_OK is returned. In this case the caller is required
** to call releasePage() on *ppChild exactly once. If an error occurs,
** an error code is returned and *ppChild is set to 0.
@@ -70966,7 +79005,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
assert( pRoot->nOverflow>0 );
assert( sqlite3_mutex_held(pBt->mutex) );
- /* Make pRoot, the root page of the b-tree, writable. Allocate a new
+ /* Make pRoot, the root page of the b-tree, writable. Allocate a new
** page that will become the new right-child of pPage. Copy the contents
** of the node stored on pRoot into the new child page.
*/
@@ -70974,7 +79013,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
if( rc==SQLITE_OK ){
rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0);
copyNodeContent(pRoot, pChild, &rc);
- if( ISAUTOVACUUM ){
+ if( ISAUTOVACUUM(pBt) ){
ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc);
}
}
@@ -70985,9 +79024,9 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
}
assert( sqlite3PagerIswriteable(pChild->pDbPage) );
assert( sqlite3PagerIswriteable(pRoot->pDbPage) );
- assert( pChild->nCell==pRoot->nCell );
+ assert( pChild->nCell==pRoot->nCell || CORRUPT_DB );
- TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno));
+ TRACE(("BALANCE: copy root %u into %u\n", pRoot->pgno, pChild->pgno));
/* Copy the overflow cells from pRoot to pChild */
memcpy(pChild->aiOvfl, pRoot->aiOvfl,
@@ -71005,9 +79044,33 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
}
/*
+** Return SQLITE_CORRUPT if any cursor other than pCur is currently valid
+** on the same B-tree as pCur.
+**
+** This can occur if a database is corrupt with two or more SQL tables
+** pointing to the same b-tree. If an insert occurs on one SQL table
+** and causes a BEFORE TRIGGER to do a secondary insert on the other SQL
+** table linked to the same b-tree. If the secondary insert causes a
+** rebalance, that can change content out from under the cursor on the
+** first SQL table, violating invariants on the first insert.
+*/
+static int anotherValidCursor(BtCursor *pCur){
+ BtCursor *pOther;
+ for(pOther=pCur->pBt->pCursor; pOther; pOther=pOther->pNext){
+ if( pOther!=pCur
+ && pOther->eState==CURSOR_VALID
+ && pOther->pPage==pCur->pPage
+ ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
** The page that pCur currently points to has just been modified in
** some way. This function figures out if this modification means the
-** tree needs to be balanced, and if so calls the appropriate balancing
+** tree needs to be balanced, and if so calls the appropriate balancing
** routine. Balancing routines are:
**
** balance_quick()
@@ -71016,7 +79079,6 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
*/
static int balance(BtCursor *pCur){
int rc = SQLITE_OK;
- const int nMin = pCur->pBt->usableSize * 2 / 3;
u8 aBalanceQuickSpace[13];
u8 *pFree = 0;
@@ -71024,16 +79086,23 @@ static int balance(BtCursor *pCur){
VVA_ONLY( int balance_deeper_called = 0 );
do {
- int iPage = pCur->iPage;
+ int iPage;
MemPage *pPage = pCur->pPage;
- if( iPage==0 ){
- if( pPage->nOverflow ){
+ if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break;
+ if( pPage->nOverflow==0 && pPage->nFree*3<=(int)pCur->pBt->usableSize*2 ){
+ /* No rebalance required as long as:
+ ** (1) There are no overflow cells
+ ** (2) The amount of free space on the page is less than 2/3rds of
+ ** the total usable space on the page. */
+ break;
+ }else if( (iPage = pCur->iPage)==0 ){
+ if( pPage->nOverflow && (rc = anotherValidCursor(pCur))==SQLITE_OK ){
/* The root page of the b-tree is overfull. In this case call the
** balance_deeper() function to create a new child for the root-page
** and copy the current contents of the root-page to it. The
** next iteration of the do-loop will balance the child page.
- */
+ */
assert( balance_deeper_called==0 );
VVA_ONLY( balance_deeper_called++ );
rc = balance_deeper(pPage, &pCur->apPage[1]);
@@ -71048,13 +79117,19 @@ static int balance(BtCursor *pCur){
}else{
break;
}
- }else if( pPage->nOverflow==0 && pPage->nFree<=nMin ){
- break;
+ }else if( sqlite3PagerPageRefcount(pPage->pDbPage)>1 ){
+ /* The page being written is not a root page, and there is currently
+ ** more than one reference to it. This only happens if the page is one
+ ** of its own ancestor pages. Corruption. */
+ rc = SQLITE_CORRUPT_BKPT;
}else{
MemPage * const pParent = pCur->apPage[iPage-1];
int const iIdx = pCur->aiIdx[iPage-1];
rc = sqlite3PagerWrite(pParent->pDbPage);
+ if( rc==SQLITE_OK && pParent->nFree<0 ){
+ rc = btreeComputeFreeSpace(pParent);
+ }
if( rc==SQLITE_OK ){
#ifndef SQLITE_OMIT_QUICKBALANCE
if( pPage->intKeyLeaf
@@ -71066,17 +79141,17 @@ static int balance(BtCursor *pCur){
/* Call balance_quick() to create a new sibling of pPage on which
** to store the overflow cell. balance_quick() inserts a new cell
** into pParent, which may cause pParent overflow. If this
- ** happens, the next iteration of the do-loop will balance pParent
+ ** happens, the next iteration of the do-loop will balance pParent
** use either balance_nonroot() or balance_deeper(). Until this
** happens, the overflow cell is stored in the aBalanceQuickSpace[]
- ** buffer.
+ ** buffer.
**
** The purpose of the following assert() is to check that only a
** single call to balance_quick() is made for each call to this
** function. If this were not verified, a subtle bug involving reuse
** of the aBalanceQuickSpace[] might sneak in.
*/
- assert( balance_quick_called==0 );
+ assert( balance_quick_called==0 );
VVA_ONLY( balance_quick_called++ );
rc = balance_quick(pParent, pPage, aBalanceQuickSpace);
}else
@@ -71087,15 +79162,15 @@ static int balance(BtCursor *pCur){
** modifying the contents of pParent, which may cause pParent to
** become overfull or underfull. The next iteration of the do-loop
** will balance the parent page to correct this.
- **
+ **
** If the parent page becomes overfull, the overflow cell or cells
- ** are stored in the pSpace buffer allocated immediately below.
+ ** are stored in the pSpace buffer allocated immediately below.
** A subsequent iteration of the do-loop will deal with this by
** calling balance_nonroot() (balance_deeper() may be called first,
** but it doesn't deal with overflow cells - just moves them to a
- ** different page). Once this subsequent call to balance_nonroot()
+ ** different page). Once this subsequent call to balance_nonroot()
** has completed, it is safe to release the pSpace buffer used by
- ** the previous call, as the overflow cell data will have been
+ ** the previous call, as the overflow cell data will have been
** copied either into the body of a database page or into the new
** pSpace buffer passed to the latter call to balance_nonroot().
*/
@@ -71103,9 +79178,9 @@ static int balance(BtCursor *pCur){
rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1,
pCur->hints&BTREE_BULKLOAD);
if( pFree ){
- /* If pFree is not NULL, it points to the pSpace buffer used
+ /* If pFree is not NULL, it points to the pSpace buffer used
** by a previous call to balance_nonroot(). Its contents are
- ** now stored either on real database pages or within the
+ ** now stored either on real database pages or within the
** new pSpace buffer, so it may be safely freed here. */
sqlite3PageFree(pFree);
}
@@ -71145,7 +79220,7 @@ static int btreeOverwriteContent(
){
int nData = pX->nData - iOffset;
if( nData<=0 ){
- /* Overwritting with zeros */
+ /* Overwriting with zeros */
int i;
for(i=0; i<iAmt && pDest[i]==0; i++){}
if( i<iAmt ){
@@ -71165,7 +79240,11 @@ static int btreeOverwriteContent(
if( memcmp(pDest, ((u8*)pX->pData) + iOffset, iAmt)!=0 ){
int rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
- memcpy(pDest, ((u8*)pX->pData) + iOffset, iAmt);
+ /* In a corrupt database, it is possible for the source and destination
+ ** buffers to overlap. This is harmless since the database is already
+ ** corrupt but it does cause valgrind and ASAN warnings. So use
+ ** memmove(). */
+ memmove(pDest, ((u8*)pX->pData) + iOffset, iAmt);
}
}
return SQLITE_OK;
@@ -71173,9 +79252,13 @@ static int btreeOverwriteContent(
/*
** Overwrite the cell that cursor pCur is pointing to with fresh content
-** contained in pX.
+** contained in pX. In this variant, pCur is pointing to an overflow
+** cell.
*/
-static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
+static SQLITE_NOINLINE int btreeOverwriteOverflowCell(
+ BtCursor *pCur, /* Cursor pointing to cell to overwrite */
+ const BtreePayload *pX /* Content to write into the cell */
+){
int iOffset; /* Next byte of pX->pData to write */
int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */
int rc; /* Return code */
@@ -71184,14 +79267,12 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
Pgno ovflPgno; /* Next overflow page to write */
u32 ovflPageSize; /* Size to write on overflow page */
- if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd ){
- return SQLITE_CORRUPT_BKPT;
- }
+ assert( pCur->info.nLocal<nTotal ); /* pCur is an overflow cell */
+
/* Overwrite the local portion first */
rc = btreeOverwriteContent(pPage, pCur->info.pPayload, pX,
0, pCur->info.nLocal);
if( rc ) return rc;
- if( pCur->info.nLocal==nTotal ) return SQLITE_OK;
/* Now overwrite the overflow pages */
iOffset = pCur->info.nLocal;
@@ -71203,7 +79284,7 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
do{
rc = btreeGetPage(pBt, ovflPgno, &pPage, 0);
if( rc ) return rc;
- if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 ){
+ if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){
rc = SQLITE_CORRUPT_BKPT;
}else{
if( iOffset+ovflPageSize<(u32)nTotal ){
@@ -71218,7 +79299,30 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
if( rc ) return rc;
iOffset += ovflPageSize;
}while( iOffset<nTotal );
- return SQLITE_OK;
+ return SQLITE_OK;
+}
+
+/*
+** Overwrite the cell that cursor pCur is pointing to with fresh content
+** contained in pX.
+*/
+static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
+ int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */
+ MemPage *pPage = pCur->pPage; /* Page being written */
+
+ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd
+ || pCur->info.pPayload < pPage->aData + pPage->cellOffset
+ ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ if( pCur->info.nLocal==nTotal ){
+ /* The entire cell is local */
+ return btreeOverwriteContent(pPage, pCur->info.pPayload, pX,
+ 0, pCur->info.nLocal);
+ }else{
+ /* The cell contains overflow content */
+ return btreeOverwriteOverflowCell(pCur, pX);
+ }
}
@@ -71234,11 +79338,11 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
** hold the content of the row.
**
** For an index btree (used for indexes and WITHOUT ROWID tables), the
-** key is an arbitrary byte sequence stored in pX.pKey,nKey. The
+** key is an arbitrary byte sequence stored in pX.pKey,nKey. The
** pX.pData,nData,nZero fields must be zero.
**
** If the seekResult parameter is non-zero, then a successful call to
-** MovetoUnpacked() to seek cursor pCur to (pKey,nKey) has already
+** sqlite3BtreeIndexMoveto() to seek cursor pCur to (pKey,nKey) has already
** been performed. In other words, if seekResult!=0 then the cursor
** is currently pointing to a cell that will be adjacent to the cell
** to be inserted. If seekResult<0 then pCur points to a cell that is
@@ -71256,7 +79360,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
BtCursor *pCur, /* Insert data into the table of this cursor */
const BtreePayload *pX, /* Content of the row to be inserted */
int flags, /* True if this is likely an append */
- int seekResult /* Result of prior MovetoUnpacked() call */
+ int seekResult /* Result of prior IndexMoveto() call */
){
int rc;
int loc = seekResult; /* -1: before desired location +1: after */
@@ -71264,60 +79368,74 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
int idx;
MemPage *pPage;
Btree *p = pCur->pBtree;
- BtShared *pBt = p->pBt;
unsigned char *oldCell;
unsigned char *newCell = 0;
- assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND))==flags );
-
- if( pCur->eState==CURSOR_FAULT ){
- assert( pCur->skipNext!=SQLITE_OK );
- return pCur->skipNext;
- }
-
- assert( cursorOwnsBtShared(pCur) );
- assert( (pCur->curFlags & BTCF_WriteFlag)!=0
- && pBt->inTransaction==TRANS_WRITE
- && (pBt->btsFlags & BTS_READ_ONLY)==0 );
- assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
-
- /* Assert that the caller has been consistent. If this cursor was opened
- ** expecting an index b-tree, then the caller should be inserting blob
- ** keys with no associated data. If the cursor was opened expecting an
- ** intkey table, the caller should be inserting integer keys with a
- ** blob of associated data. */
- assert( (pX->pKey==0)==(pCur->pKeyInfo==0) );
+ assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND|BTREE_PREFORMAT))==flags );
+ assert( (flags & BTREE_PREFORMAT)==0 || seekResult || pCur->pKeyInfo==0 );
/* Save the positions of any other cursors open on this table.
**
** In some cases, the call to btreeMoveto() below is a no-op. For
** example, when inserting data into a table with auto-generated integer
- ** keys, the VDBE layer invokes sqlite3BtreeLast() to figure out the
- ** integer key to use. It then calls this function to actually insert the
+ ** keys, the VDBE layer invokes sqlite3BtreeLast() to figure out the
+ ** integer key to use. It then calls this function to actually insert the
** data into the intkey B-Tree. In this case btreeMoveto() recognizes
** that the cursor is already where it needs to be and returns without
** doing any work. To avoid thwarting these optimizations, it is important
** not to clear the cursor here.
*/
if( pCur->curFlags & BTCF_Multiple ){
- rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
+ rc = saveAllCursors(p->pBt, pCur->pgnoRoot, pCur);
if( rc ) return rc;
+ if( loc && pCur->iPage<0 ){
+ /* This can only happen if the schema is corrupt such that there is more
+ ** than one table or index with the same root page as used by the cursor.
+ ** Which can only happen if the SQLITE_NoSchemaError flag was set when
+ ** the schema was loaded. This cannot be asserted though, as a user might
+ ** set the flag, load the schema, and then unset the flag. */
+ return SQLITE_CORRUPT_BKPT;
+ }
}
+ /* Ensure that the cursor is not in the CURSOR_FAULT state and that it
+ ** points to a valid cell.
+ */
+ if( pCur->eState>=CURSOR_REQUIRESEEK ){
+ testcase( pCur->eState==CURSOR_REQUIRESEEK );
+ testcase( pCur->eState==CURSOR_FAULT );
+ rc = moveToRoot(pCur);
+ if( rc && rc!=SQLITE_EMPTY ) return rc;
+ }
+
+ assert( cursorOwnsBtShared(pCur) );
+ assert( (pCur->curFlags & BTCF_WriteFlag)!=0
+ && p->pBt->inTransaction==TRANS_WRITE
+ && (p->pBt->btsFlags & BTS_READ_ONLY)==0 );
+ assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
+
+ /* Assert that the caller has been consistent. If this cursor was opened
+ ** expecting an index b-tree, then the caller should be inserting blob
+ ** keys with no associated data. If the cursor was opened expecting an
+ ** intkey table, the caller should be inserting integer keys with a
+ ** blob of associated data. */
+ assert( (flags & BTREE_PREFORMAT) || (pX->pKey==0)==(pCur->pKeyInfo==0) );
+
if( pCur->pKeyInfo==0 ){
assert( pX->pKey==0 );
- /* If this is an insert into a table b-tree, invalidate any incrblob
+ /* If this is an insert into a table b-tree, invalidate any incrblob
** cursors open on the row being replaced */
- invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0);
+ if( p->hasIncrblobCur ){
+ invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0);
+ }
- /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
+ /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
** to a row with the same key as the new entry being inserted.
*/
#ifdef SQLITE_DEBUG
if( flags & BTREE_SAVEPOSITION ){
assert( pCur->curFlags & BTCF_ValidNKey );
assert( pX->nKey==pCur->info.nKey );
- assert( pCur->info.nSize!=0 );
assert( loc==0 );
}
#endif
@@ -71342,13 +79460,14 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** to an adjacent cell. Move the cursor so that it is pointing either
** to the cell to be overwritten or an adjacent cell.
*/
- rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc);
+ rc = sqlite3BtreeTableMoveto(pCur, pX->nKey,
+ (flags & BTREE_APPEND)!=0, &loc);
if( rc ) return rc;
}
}else{
/* This is an index or a WITHOUT ROWID table */
- /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
+ /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
** to a row with the same key as the new entry being inserted.
*/
assert( (flags & BTREE_SAVEPOSITION)==0 || loc==0 );
@@ -71365,13 +79484,11 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
r.aMem = pX->aMem;
r.nField = pX->nMem;
r.default_rc = 0;
- r.errCode = 0;
- r.r1 = 0;
- r.r2 = 0;
r.eqSeen = 0;
- rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc);
+ rc = sqlite3BtreeIndexMoveto(pCur, &r, &loc);
}else{
- rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc);
+ rc = btreeMoveto(pCur, pX->pKey, pX->nKey,
+ (flags & BTREE_APPEND)!=0, &loc);
}
if( rc ) return rc;
}
@@ -71390,28 +79507,57 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
return btreeOverwriteCell(pCur, &x2);
}
}
-
}
- assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) );
+ assert( pCur->eState==CURSOR_VALID
+ || (pCur->eState==CURSOR_INVALID && loc) || CORRUPT_DB );
pPage = pCur->pPage;
- assert( pPage->intKey || pX->nKey>=0 );
+ assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) );
assert( pPage->leaf || !pPage->intKey );
+ if( pPage->nFree<0 ){
+ if( NEVER(pCur->eState>CURSOR_INVALID) ){
+ /* ^^^^^--- due to the moveToRoot() call above */
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ rc = btreeComputeFreeSpace(pPage);
+ }
+ if( rc ) return rc;
+ }
- TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
+ TRACE(("INSERT: table=%u nkey=%lld ndata=%u page=%u %s\n",
pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
loc==0 ? "overwrite" : "new entry"));
- assert( pPage->isInit );
- newCell = pBt->pTmpSpace;
+ assert( pPage->isInit || CORRUPT_DB );
+ newCell = p->pBt->pTmpSpace;
assert( newCell!=0 );
- rc = fillInCell(pPage, newCell, pX, &szNew);
- if( rc ) goto end_insert;
+ assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT );
+ if( flags & BTREE_PREFORMAT ){
+ rc = SQLITE_OK;
+ szNew = p->pBt->nPreformatSize;
+ if( szNew<4 ) szNew = 4;
+ if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){
+ CellInfo info;
+ pPage->xParseCell(pPage, newCell, &info);
+ if( info.nPayload!=info.nLocal ){
+ Pgno ovfl = get4byte(&newCell[szNew-4]);
+ ptrmapPut(p->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc);
+ if( NEVER(rc) ) goto end_insert;
+ }
+ }
+ }else{
+ rc = fillInCell(pPage, newCell, pX, &szNew);
+ if( rc ) goto end_insert;
+ }
assert( szNew==pPage->xCellSize(pPage, newCell) );
- assert( szNew <= MX_CELL_SIZE(pBt) );
+ assert( szNew <= MX_CELL_SIZE(p->pBt) );
idx = pCur->ix;
+ pCur->info.nSize = 0;
if( loc==0 ){
CellInfo info;
- assert( idx<pPage->nCell );
+ assert( idx>=0 );
+ if( idx>=pPage->nCell ){
+ return SQLITE_CORRUPT_BKPT;
+ }
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ){
goto end_insert;
@@ -71420,21 +79566,28 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
if( !pPage->leaf ){
memcpy(newCell, oldCell, 4);
}
- rc = clearCell(pPage, oldCell, &info);
- if( info.nSize==szNew && info.nLocal==info.nPayload
- && (!ISAUTOVACUUM || szNew<pPage->minLocal)
+ BTREE_CLEAR_CELL(rc, pPage, oldCell, info);
+ testcase( pCur->curFlags & BTCF_ValidOvfl );
+ invalidateOverflowCache(pCur);
+ if( info.nSize==szNew && info.nLocal==info.nPayload
+ && (!ISAUTOVACUUM(p->pBt) || szNew<pPage->minLocal)
){
/* Overwrite the old cell with the new if they are the same size.
** We could also try to do this if the old cell is smaller, then add
** the leftover space to the free list. But experiments show that
** doing that is no faster then skipping this optimization and just
- ** calling dropCell() and insertCell().
+ ** calling dropCell() and insertCell().
**
** This optimization cannot be used on an autovacuum database if the
** new entry uses overflow pages, as the insertCell() call below is
** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */
assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */
- if( oldCell+szNew > pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT;
+ if( oldCell < pPage->aData+pPage->hdrOffset+10 ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ if( oldCell+szNew > pPage->aDataEnd ){
+ return SQLITE_CORRUPT_BKPT;
+ }
memcpy(oldCell, newCell, szNew);
return SQLITE_OK;
}
@@ -71447,11 +79600,11 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
}else{
assert( pPage->leaf );
}
- insertCell(pPage, idx, newCell, szNew, 0, 0, &rc);
+ rc = insertCellFast(pPage, idx, newCell, szNew);
assert( pPage->nOverflow==0 || rc==SQLITE_OK );
assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 );
- /* If no error has occurred and pPage has an overflow cell, call balance()
+ /* If no error has occurred and pPage has an overflow cell, call balance()
** to redistribute the cells within the tree. Since balance() may move
** the cursor, zero the BtCursor.info.nSize and BTCF_ValidNKey
** variables.
@@ -71471,14 +79624,13 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** larger than the largest existing key, it is possible to insert the
** row without seeking the cursor. This can be a big performance boost.
*/
- pCur->info.nSize = 0;
if( pPage->nOverflow ){
assert( rc==SQLITE_OK );
pCur->curFlags &= ~(BTCF_ValidNKey);
rc = balance(pCur);
/* Must make sure nOverflow is reset to zero even if the balance()
- ** fails. Internal data structure corruption will result otherwise.
+ ** fails. Internal data structure corruption will result otherwise.
** Also, set the cursor state to invalid. This stops saveCursorPosition()
** from trying to save the current position of the cursor. */
pCur->pPage->nOverflow = 0;
@@ -71505,7 +79657,119 @@ end_insert:
}
/*
-** Delete the entry that the cursor is pointing to.
+** This function is used as part of copying the current row from cursor
+** pSrc into cursor pDest. If the cursors are open on intkey tables, then
+** parameter iKey is used as the rowid value when the record is copied
+** into pDest. Otherwise, the record is copied verbatim.
+**
+** This function does not actually write the new value to cursor pDest.
+** Instead, it creates and populates any required overflow pages and
+** writes the data for the new cell into the BtShared.pTmpSpace buffer
+** for the destination database. The size of the cell, in bytes, is left
+** in BtShared.nPreformatSize. The caller completes the insertion by
+** calling sqlite3BtreeInsert() with the BTREE_PREFORMAT flag specified.
+**
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
+*/
+SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
+ BtShared *pBt = pDest->pBt;
+ u8 *aOut = pBt->pTmpSpace; /* Pointer to next output buffer */
+ const u8 *aIn; /* Pointer to next input buffer */
+ u32 nIn; /* Size of input buffer aIn[] */
+ u32 nRem; /* Bytes of data still to copy */
+
+ getCellInfo(pSrc);
+ if( pSrc->info.nPayload<0x80 ){
+ *(aOut++) = pSrc->info.nPayload;
+ }else{
+ aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload);
+ }
+ if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey);
+ nIn = pSrc->info.nLocal;
+ aIn = pSrc->info.pPayload;
+ if( aIn+nIn>pSrc->pPage->aDataEnd ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ nRem = pSrc->info.nPayload;
+ if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
+ memcpy(aOut, aIn, nIn);
+ pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace);
+ return SQLITE_OK;
+ }else{
+ int rc = SQLITE_OK;
+ Pager *pSrcPager = pSrc->pBt->pPager;
+ u8 *pPgnoOut = 0;
+ Pgno ovflIn = 0;
+ DbPage *pPageIn = 0;
+ MemPage *pPageOut = 0;
+ u32 nOut; /* Size of output buffer aOut[] */
+
+ nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload);
+ pBt->nPreformatSize = nOut + (aOut - pBt->pTmpSpace);
+ if( nOut<pSrc->info.nPayload ){
+ pPgnoOut = &aOut[nOut];
+ pBt->nPreformatSize += 4;
+ }
+
+ if( nRem>nIn ){
+ if( aIn+nIn+4>pSrc->pPage->aDataEnd ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ ovflIn = get4byte(&pSrc->info.pPayload[nIn]);
+ }
+
+ do {
+ nRem -= nOut;
+ do{
+ assert( nOut>0 );
+ if( nIn>0 ){
+ int nCopy = MIN(nOut, nIn);
+ memcpy(aOut, aIn, nCopy);
+ nOut -= nCopy;
+ nIn -= nCopy;
+ aOut += nCopy;
+ aIn += nCopy;
+ }
+ if( nOut>0 ){
+ sqlite3PagerUnref(pPageIn);
+ pPageIn = 0;
+ rc = sqlite3PagerGet(pSrcPager, ovflIn, &pPageIn, PAGER_GET_READONLY);
+ if( rc==SQLITE_OK ){
+ aIn = (const u8*)sqlite3PagerGetData(pPageIn);
+ ovflIn = get4byte(aIn);
+ aIn += 4;
+ nIn = pSrc->pBt->usableSize - 4;
+ }
+ }
+ }while( rc==SQLITE_OK && nOut>0 );
+
+ if( rc==SQLITE_OK && nRem>0 && ALWAYS(pPgnoOut) ){
+ Pgno pgnoNew;
+ MemPage *pNew = 0;
+ rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0);
+ put4byte(pPgnoOut, pgnoNew);
+ if( ISAUTOVACUUM(pBt) && pPageOut ){
+ ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc);
+ }
+ releasePage(pPageOut);
+ pPageOut = pNew;
+ if( pPageOut ){
+ pPgnoOut = pPageOut->aData;
+ put4byte(pPgnoOut, 0);
+ aOut = &pPgnoOut[4];
+ nOut = MIN(pBt->usableSize - 4, nRem);
+ }
+ }
+ }while( nRem>0 && rc==SQLITE_OK );
+
+ releasePage(pPageOut);
+ sqlite3PagerUnref(pPageIn);
+ return rc;
+ }
+}
+
+/*
+** Delete the entry that the cursor is pointing to.
**
** If the BTREE_SAVEPOSITION bit of the flags parameter is zero, then
** the cursor is left pointing at an arbitrary location after the delete.
@@ -71523,15 +79787,14 @@ end_insert:
*/
SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
Btree *p = pCur->pBtree;
- BtShared *pBt = p->pBt;
- int rc; /* Return code */
- MemPage *pPage; /* Page to delete cell from */
- unsigned char *pCell; /* Pointer to cell to delete */
- int iCellIdx; /* Index of cell to delete */
- int iCellDepth; /* Depth of node containing pCell */
- CellInfo info; /* Size of the cell being deleted */
- int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */
- u8 bPreserve = flags & BTREE_SAVEPOSITION; /* Keep cursor valid */
+ BtShared *pBt = p->pBt;
+ int rc; /* Return code */
+ MemPage *pPage; /* Page to delete cell from */
+ unsigned char *pCell; /* Pointer to cell to delete */
+ int iCellIdx; /* Index of cell to delete */
+ int iCellDepth; /* Depth of node containing pCell */
+ CellInfo info; /* Size of the cell being deleted */
+ u8 bPreserve; /* Keep cursor valid. 2 for CURSOR_SKIPNEXT */
assert( cursorOwnsBtShared(pCur) );
assert( pBt->inTransaction==TRANS_WRITE );
@@ -71539,34 +79802,61 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
assert( pCur->curFlags & BTCF_WriteFlag );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
assert( !hasReadConflicts(p, pCur->pgnoRoot) );
- assert( pCur->ix<pCur->pPage->nCell );
- assert( pCur->eState==CURSOR_VALID );
assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 );
+ if( pCur->eState!=CURSOR_VALID ){
+ if( pCur->eState>=CURSOR_REQUIRESEEK ){
+ rc = btreeRestoreCursorPosition(pCur);
+ assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID );
+ if( rc || pCur->eState!=CURSOR_VALID ) return rc;
+ }else{
+ return SQLITE_CORRUPT_BKPT;
+ }
+ }
+ assert( pCur->eState==CURSOR_VALID );
iCellDepth = pCur->iPage;
iCellIdx = pCur->ix;
pPage = pCur->pPage;
+ if( pPage->nCell<=iCellIdx ){
+ return SQLITE_CORRUPT_BKPT;
+ }
pCell = findCell(pPage, iCellIdx);
+ if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ if( pCell<&pPage->aCellIdx[pPage->nCell] ){
+ return SQLITE_CORRUPT_BKPT;
+ }
- /* If the bPreserve flag is set to true, then the cursor position must
+ /* If the BTREE_SAVEPOSITION bit is on, then the cursor position must
** be preserved following this delete operation. If the current delete
** will cause a b-tree rebalance, then this is done by saving the cursor
- ** key and leaving the cursor in CURSOR_REQUIRESEEK state before
- ** returning.
+ ** key and leaving the cursor in CURSOR_REQUIRESEEK state before
+ ** returning.
**
- ** Or, if the current delete will not cause a rebalance, then the cursor
+ ** If the current delete will not cause a rebalance, then the cursor
** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately
- ** before or after the deleted entry. In this case set bSkipnext to true. */
+ ** before or after the deleted entry.
+ **
+ ** The bPreserve value records which path is required:
+ **
+ ** bPreserve==0 Not necessary to save the cursor position
+ ** bPreserve==1 Use CURSOR_REQUIRESEEK to save the cursor position
+ ** bPreserve==2 Cursor won't move. Set CURSOR_SKIPNEXT.
+ */
+ bPreserve = (flags & BTREE_SAVEPOSITION)!=0;
if( bPreserve ){
- if( !pPage->leaf
- || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
+ if( !pPage->leaf
+ || (pPage->nFree+pPage->xCellSize(pPage,pCell)+2) >
+ (int)(pBt->usableSize*2/3)
+ || pPage->nCell==1 /* See dbfuzz001.test for a test case */
){
/* A b-tree rebalance will be required after deleting this entry.
** Save the cursor key. */
rc = saveCursorKey(pCur);
if( rc ) return rc;
}else{
- bSkipnext = 1;
+ bPreserve = 2;
}
}
@@ -71592,7 +79882,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
/* If this is a delete operation to remove a row from a table b-tree,
** invalidate any incrblob cursors open on the row being deleted. */
- if( pCur->pKeyInfo==0 ){
+ if( pCur->pKeyInfo==0 && p->hasIncrblobCur ){
invalidateIncrblobCursors(p, pCur->pgnoRoot, pCur->info.nKey, 0);
}
@@ -71601,7 +79891,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
** itself from within the page. */
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
- rc = clearCell(pPage, pCell, &info);
+ BTREE_CLEAR_CELL(rc, pPage, pCell, info);
dropCell(pPage, iCellIdx, info.nSize, &rc);
if( rc ) return rc;
@@ -71616,6 +79906,10 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
Pgno n;
unsigned char *pTmp;
+ if( pLeaf->nFree<0 ){
+ rc = btreeComputeFreeSpace(pLeaf);
+ if( rc ) return rc;
+ }
if( iCellDepth<pCur->iPage-1 ){
n = pCur->apPage[iCellDepth+1]->pgno;
}else{
@@ -71629,7 +79923,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
assert( pTmp!=0 );
rc = sqlite3PagerWrite(pLeaf->pDbPage);
if( rc==SQLITE_OK ){
- insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
+ rc = insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n);
}
dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc);
if( rc ) return rc;
@@ -71648,9 +79942,17 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
** on the leaf node first. If the balance proceeds far enough up the
** tree that we can be sure that any problem in the internal node has
** been corrected, so be it. Otherwise, after balancing the leaf node,
- ** walk the cursor up the tree to the internal node and balance it as
+ ** walk the cursor up the tree to the internal node and balance it as
** well. */
- rc = balance(pCur);
+ assert( pCur->pPage->nOverflow==0 );
+ assert( pCur->pPage->nFree>=0 );
+ if( pCur->pPage->nFree*3<=(int)pCur->pBt->usableSize*2 ){
+ /* Optimization: If the free space is less than 2/3rds of the page,
+ ** then balance() will always be a no-op. No need to invoke it. */
+ rc = SQLITE_OK;
+ }else{
+ rc = balance(pCur);
+ }
if( rc==SQLITE_OK && pCur->iPage>iCellDepth ){
releasePageNotNull(pCur->pPage);
pCur->iPage--;
@@ -71662,8 +79964,8 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
}
if( rc==SQLITE_OK ){
- if( bSkipnext ){
- assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) );
+ if( bPreserve>1 ){
+ assert( (pCur->iPage==iCellDepth || CORRUPT_DB) );
assert( pPage==pCur->pPage || CORRUPT_DB );
assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell );
pCur->eState = CURSOR_SKIPNEXT;
@@ -71696,12 +79998,12 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys
** BTREE_ZERODATA Used for SQL indices
*/
-static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
+static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){
BtShared *pBt = p->pBt;
MemPage *pRoot;
Pgno pgnoRoot;
int rc;
- int ptfFlags; /* Page-type flage for the root page of new table */
+ int ptfFlags; /* Page-type flags for the root page of new table */
assert( sqlite3BtreeHoldsMutex(p) );
assert( pBt->inTransaction==TRANS_WRITE );
@@ -71729,6 +80031,9 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
** created so far, so the new root-page is (meta[3]+1).
*/
sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot);
+ if( pgnoRoot>btreePagecount(pBt) ){
+ return SQLITE_CORRUPT_BKPT;
+ }
pgnoRoot++;
/* The new root-page may not be allocated on a pointer-map page, or the
@@ -71738,8 +80043,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
pgnoRoot==PENDING_BYTE_PAGE(pBt) ){
pgnoRoot++;
}
- assert( pgnoRoot>=3 || CORRUPT_DB );
- testcase( pgnoRoot<3 );
+ assert( pgnoRoot>=3 );
/* Allocate a page. The page that currently resides at pgnoRoot will
** be moved to the allocated page (unless the allocated page happens
@@ -71802,7 +80106,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
}
}else{
pRoot = pPageMove;
- }
+ }
/* Update the pointer-map and meta-data with the new root-page number. */
ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0, &rc);
@@ -71836,10 +80140,10 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
zeroPage(pRoot, ptfFlags);
sqlite3PagerUnref(pRoot->pDbPage);
assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 );
- *piTable = (int)pgnoRoot;
+ *piTable = pgnoRoot;
return SQLITE_OK;
}
-SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
+SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree *p, Pgno *piTable, int flags){
int rc;
sqlite3BtreeEnter(p);
rc = btreeCreateTable(p, piTable, flags);
@@ -71855,7 +80159,7 @@ static int clearDatabasePage(
BtShared *pBt, /* The BTree that contains the table */
Pgno pgno, /* Page number to clear */
int freePageFlag, /* Deallocate page if true */
- int *pnChange /* Add number of Cells freed to this counter */
+ i64 *pnChange /* Add number of Cells freed to this counter */
){
MemPage *pPage;
int rc;
@@ -71868,13 +80172,14 @@ static int clearDatabasePage(
if( pgno>btreePagecount(pBt) ){
return SQLITE_CORRUPT_BKPT;
}
- rc = getAndInitPage(pBt, pgno, &pPage, 0, 0);
+ rc = getAndInitPage(pBt, pgno, &pPage, 0);
if( rc ) return rc;
- if( pPage->bBusy ){
+ if( (pBt->openFlags & BTREE_SINGLE)==0
+ && sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1))
+ ){
rc = SQLITE_CORRUPT_BKPT;
goto cleardatabasepage_out;
}
- pPage->bBusy = 1;
hdr = pPage->hdrOffset;
for(i=0; i<pPage->nCell; i++){
pCell = findCell(pPage, i);
@@ -71882,14 +80187,15 @@ static int clearDatabasePage(
rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange);
if( rc ) goto cleardatabasepage_out;
}
- rc = clearCell(pPage, pCell, &info);
+ BTREE_CLEAR_CELL(rc, pPage, pCell, info);
if( rc ) goto cleardatabasepage_out;
}
if( !pPage->leaf ){
rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange);
if( rc ) goto cleardatabasepage_out;
- }else if( pnChange ){
- assert( pPage->intKey || CORRUPT_DB );
+ if( pPage->intKey ) pnChange = 0;
+ }
+ if( pnChange ){
testcase( !pPage->intKey );
*pnChange += pPage->nCell;
}
@@ -71900,7 +80206,6 @@ static int clearDatabasePage(
}
cleardatabasepage_out:
- pPage->bBusy = 0;
releasePage(pPage);
return rc;
}
@@ -71914,11 +80219,10 @@ cleardatabasepage_out:
** read cursors on the table. Open write cursors are moved to the
** root of the table.
**
-** If pnChange is not NULL, then table iTable must be an intkey table. The
-** integer value pointed to by pnChange is incremented by the number of
-** entries in the table.
+** If pnChange is not NULL, then the integer value pointed to by pnChange
+** is incremented by the number of entries in the table.
*/
-SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
+SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, i64 *pnChange){
int rc;
BtShared *pBt = p->pBt;
sqlite3BtreeEnter(p);
@@ -71930,7 +80234,9 @@ SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
/* Invalidate all incrblob cursors open on table iTable (assuming iTable
** is the root of a table b-tree - if it is not, the following call is
** a no-op). */
- invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1);
+ if( p->hasIncrblobCur ){
+ invalidateIncrblobCursors(p, (Pgno)iTable, 0, 1);
+ }
rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);
}
sqlite3BtreeLeave(p);
@@ -71955,12 +80261,12 @@ SQLITE_PRIVATE int sqlite3BtreeClearTableOfCursor(BtCursor *pCur){
** cursors on the table.
**
** If AUTOVACUUM is enabled and the page at iTable is not the last
-** root page in the database file, then the last root page
+** root page in the database file, then the last root page
** in the database file is moved into the slot formerly occupied by
** iTable and that last slot formerly occupied by the last root page
** is added to the freelist instead of iTable. In this say, all
** root pages are kept at the beginning of the database file, which
-** is necessary for AUTOVACUUM to work right. *piMoved is set to the
+** is necessary for AUTOVACUUM to work right. *piMoved is set to the
** page number that used to be the last root page in the file before
** the move. If no page gets moved, *piMoved is set to 0.
** The last root page is recorded in meta[3] and the value of
@@ -71974,11 +80280,14 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
assert( sqlite3BtreeHoldsMutex(p) );
assert( p->inTrans==TRANS_WRITE );
assert( iTable>=2 );
+ if( iTable>btreePagecount(pBt) ){
+ return SQLITE_CORRUPT_BKPT;
+ }
- rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0);
- if( rc ) return rc;
rc = sqlite3BtreeClearTable(p, iTable, 0);
- if( rc ){
+ if( rc ) return rc;
+ rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0);
+ if( NEVER(rc) ){
releasePage(pPage);
return rc;
}
@@ -71995,7 +80304,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
if( iTable==maxRootPgno ){
/* If the table being dropped is the table with the largest root-page
- ** number in the database, put the root page on the free list.
+ ** number in the database, put the root page on the free list.
*/
freePage(pPage, &rc);
releasePage(pPage);
@@ -72004,7 +80313,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
}
}else{
/* The table being dropped does not have the largest root-page
- ** number in the database. So move the page that does into the
+ ** number in the database. So move the page that does into the
** gap left by the deleted root-page.
*/
MemPage *pMove;
@@ -72046,7 +80355,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
releasePage(pPage);
}
#endif
- return rc;
+ return rc;
}
SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
int rc;
@@ -72065,7 +80374,7 @@ SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
** is the number of free pages currently in the database. Meta[1]
** through meta[15] are available for use by higher layers. Meta[0]
** is read-only, the others are read/write.
-**
+**
** The schema layer numbers meta values differently. At the schema
** layer (and the SetCookie and ReadCookie opcodes) the number of
** free pages is not visible. So Cookie[0] is the same as Meta[1].
@@ -72082,12 +80391,12 @@ SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
sqlite3BtreeEnter(p);
assert( p->inTrans>TRANS_NONE );
- assert( SQLITE_OK==querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK) );
+ assert( SQLITE_OK==querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK) );
assert( pBt->pPage1 );
assert( idx>=0 && idx<=15 );
if( idx==BTREE_DATA_VERSION ){
- *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion;
+ *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iBDataVersion;
}else{
*pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]);
}
@@ -72131,16 +80440,15 @@ SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
return rc;
}
-#ifndef SQLITE_OMIT_BTREECOUNT
/*
** The first argument, pCur, is a cursor opened on some b-tree. Count the
** number of entries in the b-tree and write the result to *pnEntry.
**
-** SQLITE_OK is returned if the operation is successfully executed.
+** SQLITE_OK is returned if the operation is successfully executed.
** Otherwise, if an error is encountered (i.e. an IO error or database
** corruption) an SQLite error code is returned.
*/
-SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
+SQLITE_PRIVATE int sqlite3BtreeCount(sqlite3 *db, BtCursor *pCur, i64 *pnEntry){
i64 nEntry = 0; /* Value to return in *pnEntry */
int rc; /* Return code */
@@ -72151,13 +80459,13 @@ SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
}
/* Unless an error occurs, the following loop runs one iteration for each
- ** page in the B-Tree structure (not including overflow pages).
+ ** page in the B-Tree structure (not including overflow pages).
*/
- while( rc==SQLITE_OK ){
+ while( rc==SQLITE_OK && !AtomicLoad(&db->u1.isInterrupted) ){
int iIdx; /* Index of child node in parent */
MemPage *pPage; /* Current page of the b-tree */
- /* If this is a leaf page or the tree is not an int-key tree, then
+ /* If this is a leaf page or the tree is not an int-key tree, then
** this page contains countable entries. Increment the entry counter
** accordingly.
*/
@@ -72166,7 +80474,7 @@ SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
nEntry += pPage->nCell;
}
- /* pPage is a leaf node. This loop navigates the cursor so that it
+ /* pPage is a leaf node. This loop navigates the cursor so that it
** points to the first interior cell that it points to the parent of
** the next page in the tree that has not yet been visited. The
** pCur->aiIdx[pCur->iPage] value is set to the index of the parent cell
@@ -72190,7 +80498,7 @@ SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
pPage = pCur->pPage;
}
- /* Descend to the child node of the cell that the cursor currently
+ /* Descend to the child node of the cell that the cursor currently
** points at. This is the right-child if (iIdx==pPage->nCell).
*/
iIdx = pCur->ix;
@@ -72204,7 +80512,6 @@ SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
/* An error has occurred. Return an error code. */
return rc;
}
-#endif
/*
** Return the pager associated with a BTree. This routine is used for
@@ -72216,6 +80523,41 @@ SQLITE_PRIVATE Pager *sqlite3BtreePager(Btree *p){
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
/*
+** Record an OOM error during integrity_check
+*/
+static void checkOom(IntegrityCk *pCheck){
+ pCheck->rc = SQLITE_NOMEM;
+ pCheck->mxErr = 0; /* Causes integrity_check processing to stop */
+ if( pCheck->nErr==0 ) pCheck->nErr++;
+}
+
+/*
+** Invoke the progress handler, if appropriate. Also check for an
+** interrupt.
+*/
+static void checkProgress(IntegrityCk *pCheck){
+ sqlite3 *db = pCheck->db;
+ if( AtomicLoad(&db->u1.isInterrupted) ){
+ pCheck->rc = SQLITE_INTERRUPT;
+ pCheck->nErr++;
+ pCheck->mxErr = 0;
+ }
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ if( db->xProgress ){
+ assert( db->nProgressOps>0 );
+ pCheck->nStep++;
+ if( (pCheck->nStep % db->nProgressOps)==0
+ && db->xProgress(db->pProgressArg)
+ ){
+ pCheck->rc = SQLITE_INTERRUPT;
+ pCheck->nErr++;
+ pCheck->mxErr = 0;
+ }
+ }
+#endif
+}
+
+/*
** Append a message to the error message string.
*/
static void checkAppendMsg(
@@ -72224,6 +80566,7 @@ static void checkAppendMsg(
...
){
va_list ap;
+ checkProgress(pCheck);
if( !pCheck->mxErr ) return;
pCheck->mxErr--;
pCheck->nErr++;
@@ -72232,12 +80575,13 @@ static void checkAppendMsg(
sqlite3_str_append(&pCheck->errMsg, "\n", 1);
}
if( pCheck->zPfx ){
- sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2);
+ sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx,
+ pCheck->v0, pCheck->v1, pCheck->v2);
}
sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap);
va_end(ap);
if( pCheck->errMsg.accError==SQLITE_NOMEM ){
- pCheck->mallocFailed = 1;
+ checkOom(pCheck);
}
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -72249,7 +80593,8 @@ static void checkAppendMsg(
** corresponds to page iPg is already set.
*/
static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){
- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ assert( pCheck->aPgRef!=0 );
+ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 );
return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07)));
}
@@ -72257,7 +80602,8 @@ static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){
** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg.
*/
static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ assert( pCheck->aPgRef!=0 );
+ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 );
pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07));
}
@@ -72271,12 +80617,12 @@ static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
** Also check that the page number is in bounds.
*/
static int checkRef(IntegrityCk *pCheck, Pgno iPage){
- if( iPage>pCheck->nPage || iPage==0 ){
- checkAppendMsg(pCheck, "invalid page number %d", iPage);
+ if( iPage>pCheck->nCkPage || iPage==0 ){
+ checkAppendMsg(pCheck, "invalid page number %u", iPage);
return 1;
}
if( getPageReferenced(pCheck, iPage) ){
- checkAppendMsg(pCheck, "2nd reference to page %d", iPage);
+ checkAppendMsg(pCheck, "2nd reference to page %u", iPage);
return 1;
}
setPageReferenced(pCheck, iPage);
@@ -72285,7 +80631,7 @@ static int checkRef(IntegrityCk *pCheck, Pgno iPage){
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
-** Check that the entry in the pointer-map for page iChild maps to
+** Check that the entry in the pointer-map for page iChild maps to
** page iParent, pointer type ptrType. If not, append an error message
** to pCheck.
*/
@@ -72301,14 +80647,14 @@ static void checkPtrmap(
rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent);
if( rc!=SQLITE_OK ){
- if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1;
- checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild);
+ if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) checkOom(pCheck);
+ checkAppendMsg(pCheck, "Failed to read ptrmap key=%u", iChild);
return;
}
if( ePtrmapType!=eType || iPtrmapParent!=iParent ){
checkAppendMsg(pCheck,
- "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)",
+ "Bad ptr map entry key=%u expected=(%u,%u) got=(%u,%u)",
iChild, eType, iParent, ePtrmapType, iPtrmapParent);
}
}
@@ -72321,11 +80667,11 @@ static void checkPtrmap(
static void checkList(
IntegrityCk *pCheck, /* Integrity checking context */
int isFreeList, /* True for a freelist. False for overflow page list */
- int iPage, /* Page number for first page in the list */
- int N /* Expected number of pages in the list */
+ Pgno iPage, /* Page number for first page in the list */
+ u32 N /* Expected number of pages in the list */
){
int i;
- int expected = N;
+ u32 expected = N;
int nErrAtStart = pCheck->nErr;
while( iPage!=0 && pCheck->mxErr ){
DbPage *pOvflPage;
@@ -72333,23 +80679,23 @@ static void checkList(
if( checkRef(pCheck, iPage) ) break;
N--;
if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage, 0) ){
- checkAppendMsg(pCheck, "failed to get page %d", iPage);
+ checkAppendMsg(pCheck, "failed to get page %u", iPage);
break;
}
pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage);
if( isFreeList ){
- int n = get4byte(&pOvflData[4]);
+ u32 n = (u32)get4byte(&pOvflData[4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0);
}
#endif
- if( n>(int)pCheck->pBt->usableSize/4-2 ){
+ if( n>pCheck->pBt->usableSize/4-2 ){
checkAppendMsg(pCheck,
- "freelist leaf count too big on page %d", iPage);
+ "freelist leaf count too big on page %u", iPage);
N--;
}else{
- for(i=0; i<n; i++){
+ for(i=0; i<(int)n; i++){
Pgno iFreePage = get4byte(&pOvflData[8+i*4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
@@ -72378,7 +80724,7 @@ static void checkList(
}
if( N && nErrAtStart==pCheck->nErr ){
checkAppendMsg(pCheck,
- "%s is %d but should be %d",
+ "%s is %u but should be %u",
isFreeList ? "size" : "overflow list length",
expected-N, expected);
}
@@ -72403,12 +80749,14 @@ static void checkList(
** property.
**
** This heap is used for cell overlap and coverage testing. Each u32
-** entry represents the span of a cell or freeblock on a btree page.
+** entry represents the span of a cell or freeblock on a btree page.
** The upper 16 bits are the index of the first byte of a range and the
** lower 16 bits are the index of the last byte of that range.
*/
static void btreeHeapInsert(u32 *aHeap, u32 x){
- u32 j, i = ++aHeap[0];
+ u32 j, i;
+ assert( aHeap!=0 );
+ i = ++aHeap[0];
aHeap[i] = x;
while( (j = i/2)>0 && aHeap[j]>aHeap[i] ){
x = aHeap[j];
@@ -72433,7 +80781,7 @@ static int btreeHeapPull(u32 *aHeap, u32 *pOut){
aHeap[j] = x;
i = j;
}
- return 1;
+ return 1;
}
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
@@ -72441,7 +80789,7 @@ static int btreeHeapPull(u32 *aHeap, u32 *pOut){
** Do various sanity checks on a single page of a tree. Return
** the tree depth. Root pages return 0. Parents of root pages
** return 1, and so forth.
-**
+**
** These checks are done:
**
** 1. Make sure that cells and freeblocks do not overlap
@@ -72453,7 +80801,7 @@ static int btreeHeapPull(u32 *aHeap, u32 *pOut){
*/
static int checkTreePage(
IntegrityCk *pCheck, /* Context for the sanity check */
- int iPage, /* Page number of the page to check */
+ Pgno iPage, /* Page number of the page to check */
i64 *piMinKey, /* Write minimum integer primary key here */
i64 maxKey /* Error if integer primary key greater than this */
){
@@ -72485,15 +80833,18 @@ static int checkTreePage(
/* Check that the page exists
*/
+ checkProgress(pCheck);
+ if( pCheck->mxErr==0 ) goto end_of_check;
pBt = pCheck->pBt;
usableSize = pBt->usableSize;
if( iPage==0 ) return 0;
if( checkRef(pCheck, iPage) ) return 0;
- pCheck->zPfx = "Page %d: ";
+ pCheck->zPfx = "Tree %u page %u: ";
pCheck->v1 = iPage;
- if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
+ if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){
checkAppendMsg(pCheck,
"unable to get the page. error code=%d", rc);
+ if( rc==SQLITE_IOERR_NOMEM ) pCheck->rc = SQLITE_NOMEM;
goto end_of_check;
}
@@ -72507,11 +80858,16 @@ static int checkTreePage(
"btreeInitPage() returns error code %d", rc);
goto end_of_check;
}
+ if( (rc = btreeComputeFreeSpace(pPage))!=0 ){
+ assert( rc==SQLITE_CORRUPT );
+ checkAppendMsg(pCheck, "free space corruption", rc);
+ goto end_of_check;
+ }
data = pPage->aData;
hdr = pPage->hdrOffset;
/* Set up for cell analysis */
- pCheck->zPfx = "On tree page %d cell %d: ";
+ pCheck->zPfx = "Tree %u page %u cell %u: ";
contentOffset = get2byteNotZero(&data[hdr+5]);
assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
@@ -72531,7 +80887,7 @@ static int checkTreePage(
pgno = get4byte(&data[hdr+8]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
- pCheck->zPfx = "On page %d at right child: ";
+ pCheck->zPfx = "Tree %u page %u right child: ";
checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
}
#endif
@@ -72555,7 +80911,7 @@ static int checkTreePage(
pc = get2byteAligned(pCellIdx);
pCellIdx -= 2;
if( pc<contentOffset || pc>usableSize-4 ){
- checkAppendMsg(pCheck, "Offset %d out of range %d..%d",
+ checkAppendMsg(pCheck, "Offset %u out of range %u..%u",
pc, contentOffset, usableSize-4);
doCoverageCheck = 0;
continue;
@@ -72579,7 +80935,7 @@ static int checkTreePage(
/* Check the content overflow list */
if( info.nPayload>info.nLocal ){
- int nPage; /* Number of pages on the overflow chain */
+ u32 nPage; /* Number of pages on the overflow chain */
Pgno pgnoOvfl; /* First page of the overflow chain */
assert( pc + info.nSize - 4 <= usableSize );
nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4);
@@ -72634,14 +80990,14 @@ static int checkTreePage(
**
** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
** is the offset of the first freeblock, or zero if there are no
- ** freeblocks on the page.
+ ** freeblocks on the page.
*/
i = get2byte(&data[hdr+1]);
while( i>0 ){
int size, j;
- assert( (u32)i<=usableSize-4 ); /* Enforced by btreeInitPage() */
+ assert( (u32)i<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */
size = get2byte(&data[i+2]);
- assert( (u32)(i+size)<=usableSize ); /* Enforced by btreeInitPage() */
+ assert( (u32)(i+size)<=usableSize ); /* due to btreeComputeFreeSpace() */
btreeHeapInsert(heap, (((u32)i)<<16)|(i+size-1));
/* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a
** big-endian integer which is the offset in the b-tree page of the next
@@ -72650,17 +81006,17 @@ static int checkTreePage(
j = get2byte(&data[i]);
/* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
** increasing offset. */
- assert( j==0 || j>i+size ); /* Enforced by btreeInitPage() */
- assert( (u32)j<=usableSize-4 ); /* Enforced by btreeInitPage() */
+ assert( j==0 || j>i+size ); /* Enforced by btreeComputeFreeSpace() */
+ assert( (u32)j<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */
i = j;
}
- /* Analyze the min-heap looking for overlap between cells and/or
+ /* Analyze the min-heap looking for overlap between cells and/or
** freeblocks, and counting the number of untracked bytes in nFrag.
- **
+ **
** Each min-heap entry is of the form: (start_address<<16)|end_address.
** There is an implied first entry the covers the page header, the cell
** pointer index, and the gap between the cell pointer index and the start
- ** of cell content.
+ ** of cell content.
**
** The loop below pulls entries from the min-heap in order and compares
** the start_address against the previous end_address. If there is an
@@ -72672,7 +81028,7 @@ static int checkTreePage(
while( btreeHeapPull(heap,&x) ){
if( (prev&0xffff)>=(x>>16) ){
checkAppendMsg(pCheck,
- "Multiple uses for byte %u of page %d", x>>16, iPage);
+ "Multiple uses for byte %u of page %u", x>>16, iPage);
break;
}else{
nFrag += (x>>16) - (prev&0xffff) - 1;
@@ -72687,7 +81043,7 @@ static int checkTreePage(
*/
if( heap[0]==0 && nFrag!=data[hdr+7] ){
checkAppendMsg(pCheck,
- "Fragmentation of %d bytes reported as %d on page %d",
+ "Fragmentation of %u bytes reported as %u on page %u",
nFrag, data[hdr+7], iPage);
}
}
@@ -72715,117 +81071,140 @@ end_of_check:
** allocation errors, an error message held in memory obtained from
** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is
** returned. If a memory allocation error occurs, NULL is returned.
-*/
-SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
+**
+** If the first entry in aRoot[] is 0, that indicates that the list of
+** root pages is incomplete. This is a "partial integrity-check". This
+** happens when performing an integrity check on a single table. The
+** zero is skipped, of course. But in addition, the freelist checks
+** and the checks to make sure every page is referenced are also skipped,
+** since obviously it is not possible to know which pages are covered by
+** the unverified btrees. Except, if aRoot[1] is 1, then the freelist
+** checks are still performed.
+*/
+SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
+ sqlite3 *db, /* Database connection that is running the check */
Btree *p, /* The btree to be checked */
- int *aRoot, /* An array of root pages numbers for individual trees */
+ Pgno *aRoot, /* An array of root pages numbers for individual trees */
int nRoot, /* Number of entries in aRoot[] */
int mxErr, /* Stop reporting errors after this many */
- int *pnErr /* Write number of errors seen to this variable */
+ int *pnErr, /* OUT: Write number of errors seen to this variable */
+ char **pzOut /* OUT: Write the error message string here */
){
Pgno i;
IntegrityCk sCheck;
BtShared *pBt = p->pBt;
- int savedDbFlags = pBt->db->flags;
+ u64 savedDbFlags = pBt->db->flags;
char zErr[100];
+ int bPartial = 0; /* True if not checking all btrees */
+ int bCkFreelist = 1; /* True to scan the freelist */
VVA_ONLY( int nRef );
+ assert( nRoot>0 );
+
+ /* aRoot[0]==0 means this is a partial check */
+ if( aRoot[0]==0 ){
+ assert( nRoot>1 );
+ bPartial = 1;
+ if( aRoot[1]!=1 ) bCkFreelist = 0;
+ }
sqlite3BtreeEnter(p);
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) );
assert( nRef>=0 );
+ memset(&sCheck, 0, sizeof(sCheck));
+ sCheck.db = db;
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
- sCheck.nPage = btreePagecount(sCheck.pBt);
+ sCheck.nCkPage = btreePagecount(sCheck.pBt);
sCheck.mxErr = mxErr;
- sCheck.nErr = 0;
- sCheck.mallocFailed = 0;
- sCheck.zPfx = 0;
- sCheck.v1 = 0;
- sCheck.v2 = 0;
- sCheck.aPgRef = 0;
- sCheck.heap = 0;
sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL;
- if( sCheck.nPage==0 ){
+ if( sCheck.nCkPage==0 ){
goto integrity_ck_cleanup;
}
- sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1);
+ sCheck.aPgRef = sqlite3MallocZero((sCheck.nCkPage / 8)+ 1);
if( !sCheck.aPgRef ){
- sCheck.mallocFailed = 1;
+ checkOom(&sCheck);
goto integrity_ck_cleanup;
}
sCheck.heap = (u32*)sqlite3PageMalloc( pBt->pageSize );
if( sCheck.heap==0 ){
- sCheck.mallocFailed = 1;
+ checkOom(&sCheck);
goto integrity_ck_cleanup;
}
i = PENDING_BYTE_PAGE(pBt);
- if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
+ if( i<=sCheck.nCkPage ) setPageReferenced(&sCheck, i);
/* Check the integrity of the freelist
*/
- sCheck.zPfx = "Main freelist: ";
- checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
- get4byte(&pBt->pPage1->aData[36]));
- sCheck.zPfx = 0;
+ if( bCkFreelist ){
+ sCheck.zPfx = "Freelist: ";
+ checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
+ get4byte(&pBt->pPage1->aData[36]));
+ sCheck.zPfx = 0;
+ }
/* Check all the tables.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum ){
- int mx = 0;
- int mxInHdr;
- for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i];
- mxInHdr = get4byte(&pBt->pPage1->aData[52]);
- if( mx!=mxInHdr ){
+ if( !bPartial ){
+ if( pBt->autoVacuum ){
+ Pgno mx = 0;
+ Pgno mxInHdr;
+ for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i];
+ mxInHdr = get4byte(&pBt->pPage1->aData[52]);
+ if( mx!=mxInHdr ){
+ checkAppendMsg(&sCheck,
+ "max rootpage (%u) disagrees with header (%u)",
+ mx, mxInHdr
+ );
+ }
+ }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){
checkAppendMsg(&sCheck,
- "max rootpage (%d) disagrees with header (%d)",
- mx, mxInHdr
+ "incremental_vacuum enabled with a max rootpage of zero"
);
}
- }else if( get4byte(&pBt->pPage1->aData[64])!=0 ){
- checkAppendMsg(&sCheck,
- "incremental_vacuum enabled with a max rootpage of zero"
- );
}
#endif
testcase( pBt->db->flags & SQLITE_CellSizeCk );
- pBt->db->flags &= ~SQLITE_CellSizeCk;
+ pBt->db->flags &= ~(u64)SQLITE_CellSizeCk;
for(i=0; (int)i<nRoot && sCheck.mxErr; i++){
i64 notUsed;
if( aRoot[i]==0 ) continue;
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum && aRoot[i]>1 ){
+ if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
}
#endif
+ sCheck.v0 = aRoot[i];
checkTreePage(&sCheck, aRoot[i], &notUsed, LARGEST_INT64);
}
pBt->db->flags = savedDbFlags;
/* Make sure every page in the file is referenced
*/
- for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
+ if( !bPartial ){
+ for(i=1; i<=sCheck.nCkPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
- if( getPageReferenced(&sCheck, i)==0 ){
- checkAppendMsg(&sCheck, "Page %d is never used", i);
- }
+ if( getPageReferenced(&sCheck, i)==0 ){
+ checkAppendMsg(&sCheck, "Page %u: never used", i);
+ }
#else
- /* If the database supports auto-vacuum, make sure no tables contain
- ** references to pointer-map pages.
- */
- if( getPageReferenced(&sCheck, i)==0 &&
- (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
- checkAppendMsg(&sCheck, "Page %d is never used", i);
- }
- if( getPageReferenced(&sCheck, i)!=0 &&
- (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
- checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i);
- }
+ /* If the database supports auto-vacuum, make sure no tables contain
+ ** references to pointer-map pages.
+ */
+ if( getPageReferenced(&sCheck, i)==0 &&
+ (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
+ checkAppendMsg(&sCheck, "Page %u: never used", i);
+ }
+ if( getPageReferenced(&sCheck, i)!=0 &&
+ (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
+ checkAppendMsg(&sCheck, "Page %u: pointer map referenced", i);
+ }
#endif
+ }
}
/* Clean up and report errors.
@@ -72833,16 +81212,17 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
integrity_ck_cleanup:
sqlite3PageFree(sCheck.heap);
sqlite3_free(sCheck.aPgRef);
- if( sCheck.mallocFailed ){
+ *pnErr = sCheck.nErr;
+ if( sCheck.nErr==0 ){
sqlite3_str_reset(&sCheck.errMsg);
- sCheck.nErr++;
+ *pzOut = 0;
+ }else{
+ *pzOut = sqlite3StrAccumFinish(&sCheck.errMsg);
}
- *pnErr = sCheck.nErr;
- if( sCheck.nErr==0 ) sqlite3_str_reset(&sCheck.errMsg);
/* Make sure this analysis did not leave any unref() pages. */
assert( nRef==sqlite3PagerRefcount(pBt->pPager) );
sqlite3BtreeLeave(p);
- return sqlite3StrAccumFinish(&sCheck.errMsg);
+ return sCheck.rc;
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -72872,18 +81252,19 @@ SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *p){
}
/*
-** Return non-zero if a transaction is active.
+** Return one of SQLITE_TXN_NONE, SQLITE_TXN_READ, or SQLITE_TXN_WRITE
+** to describe the current transaction state of Btree p.
*/
-SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree *p){
+SQLITE_PRIVATE int sqlite3BtreeTxnState(Btree *p){
assert( p==0 || sqlite3_mutex_held(p->db->mutex) );
- return (p && (p->inTrans==TRANS_WRITE));
+ return p ? p->inTrans : 0;
}
#ifndef SQLITE_OMIT_WAL
/*
** Run a checkpoint on the Btree passed as the first argument.
**
-** Return SQLITE_LOCKED if this or any other connection has an open
+** Return SQLITE_LOCKED if this or any other connection has an open
** transaction on the shared-cache the argument Btree is connected to.
**
** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART.
@@ -72905,14 +81286,8 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *
#endif
/*
-** Return non-zero if a read (or write) transaction is active.
+** Return true if there is currently a backup running on Btree p.
*/
-SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree *p){
- assert( p );
- assert( sqlite3_mutex_held(p->db->mutex) );
- return p->inTrans!=TRANS_NONE;
-}
-
SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree *p){
assert( p );
assert( sqlite3_mutex_held(p->db->mutex) );
@@ -72922,20 +81297,20 @@ SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree *p){
/*
** This function returns a pointer to a blob of memory associated with
** a single shared-btree. The memory is used by client code for its own
-** purposes (for example, to store a high-level schema associated with
+** purposes (for example, to store a high-level schema associated with
** the shared-btree). The btree layer manages reference counting issues.
**
** The first time this is called on a shared-btree, nBytes bytes of memory
-** are allocated, zeroed, and returned to the caller. For each subsequent
+** are allocated, zeroed, and returned to the caller. For each subsequent
** call the nBytes parameter is ignored and a pointer to the same blob
-** of memory returned.
+** of memory returned.
**
** If the nBytes parameter is 0 and the blob of memory has not yet been
** allocated, a null pointer is returned. If the blob has already been
** allocated, it is returned as normal.
**
-** Just before the shared-btree is closed, the function passed as the
-** xFree argument when the memory allocation was made is invoked on the
+** Just before the shared-btree is closed, the function passed as the
+** xFree argument when the memory allocation was made is invoked on the
** blob of allocated memory. The xFree function should not call sqlite3_free()
** on the memory, the btree layer does that.
*/
@@ -72951,15 +81326,15 @@ SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void
}
/*
-** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared
-** btree as the argument handle holds an exclusive lock on the
-** sqlite_master table. Otherwise SQLITE_OK.
+** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared
+** btree as the argument handle holds an exclusive lock on the
+** sqlite_schema table. Otherwise SQLITE_OK.
*/
SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *p){
int rc;
assert( sqlite3_mutex_held(p->db->mutex) );
sqlite3BtreeEnter(p);
- rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);
+ rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK);
assert( rc==SQLITE_OK || rc==SQLITE_LOCKED_SHAREDCACHE );
sqlite3BtreeLeave(p);
return rc;
@@ -72993,11 +81368,11 @@ SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
#ifndef SQLITE_OMIT_INCRBLOB
/*
-** Argument pCsr must be a cursor opened for writing on an
-** INTKEY table currently pointing at a valid table entry.
+** Argument pCsr must be a cursor opened for writing on an
+** INTKEY table currently pointing at a valid table entry.
** This function modifies the data stored as part of that entry.
**
-** Only the data content may only be modified, it is not possible to
+** Only the data content may only be modified, it is not possible to
** change the length of the data stored. If this function is called with
** parameters that attempt to write past the end of the existing data,
** no modifications are made and SQLITE_CORRUPT is returned.
@@ -73028,7 +81403,7 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void
VVA_ONLY(rc =) saveAllCursors(pCsr->pBt, pCsr->pgnoRoot, pCsr);
assert( rc==SQLITE_OK );
- /* Check some assumptions:
+ /* Check some assumptions:
** (a) the cursor is open for writing,
** (b) there is a read/write transaction open,
** (c) the connection holds a write-lock on the table (if required),
@@ -73047,7 +81422,7 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void
return accessPayload(pCsr, offset, amt, (unsigned char *)z, 1);
}
-/*
+/*
** Mark this cursor as an incremental blob cursor.
*/
SQLITE_PRIVATE void sqlite3BtreeIncrblobCursor(BtCursor *pCur){
@@ -73057,14 +81432,14 @@ SQLITE_PRIVATE void sqlite3BtreeIncrblobCursor(BtCursor *pCur){
#endif
/*
-** Set both the "read version" (single byte at byte offset 18) and
+** Set both the "read version" (single byte at byte offset 18) and
** "write version" (single byte at byte offset 19) fields in the database
** header to iVersion.
*/
SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
BtShared *pBt = pBtree->pBt;
int rc; /* Return code */
-
+
assert( iVersion==1 || iVersion==2 );
/* If setting the version fields to 1, do not automatically open the
@@ -73112,6 +81487,17 @@ SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *p){
*/
SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }
+/*
+** If no transaction is active and the database is not a temp-db, clear
+** the in-memory pager cache.
+*/
+SQLITE_PRIVATE void sqlite3BtreeClearCache(Btree *p){
+ BtShared *pBt = p->pBt;
+ if( pBt->inTransaction==TRANS_NONE ){
+ sqlite3PagerClearCache(pBt->pPager);
+ }
+}
+
#if !defined(SQLITE_OMIT_SHARED_CACHE)
/*
** Return true if the Btree passed as the only argument is sharable.
@@ -73122,7 +81508,7 @@ SQLITE_PRIVATE int sqlite3BtreeSharable(Btree *p){
/*
** Return the number of connections to the BtShared object accessed by
-** the Btree handle passed as the only argument. For private caches
+** the Btree handle passed as the only argument. For private caches
** this is always 1. For shared caches it may be 1 or greater.
*/
SQLITE_PRIVATE int sqlite3BtreeConnectionCount(Btree *p){
@@ -73144,7 +81530,7 @@ SQLITE_PRIVATE int sqlite3BtreeConnectionCount(Btree *p){
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file contains the implementation of the sqlite3_backup_XXX()
+** This file contains the implementation of the sqlite3_backup_XXX()
** API functions and the related features.
*/
/* #include "sqliteInt.h" */
@@ -73181,15 +81567,15 @@ struct sqlite3_backup {
** Once it has been created using backup_init(), a single sqlite3_backup
** structure may be accessed via two groups of thread-safe entry points:
**
-** * Via the sqlite3_backup_XXX() API function backup_step() and
+** * Via the sqlite3_backup_XXX() API function backup_step() and
** backup_finish(). Both these functions obtain the source database
-** handle mutex and the mutex associated with the source BtShared
+** handle mutex and the mutex associated with the source BtShared
** structure, in that order.
**
** * Via the BackupUpdate() and BackupRestart() functions, which are
** invoked by the pager layer to report various state changes in
** the page cache associated with the source database. The mutex
-** associated with the source database BtShared structure will always
+** associated with the source database BtShared structure will always
** be held when either of these functions are invoked.
**
** The other sqlite3_backup_XXX() API functions, backup_remaining() and
@@ -73210,8 +81596,8 @@ struct sqlite3_backup {
** in connection handle pDb. If such a database cannot be found, return
** a NULL pointer and write an error message to pErrorDb.
**
-** If the "temp" database is requested, it may need to be opened by this
-** function. If an error occurs while doing so, return 0 and write an
+** If the "temp" database is requested, it may need to be opened by this
+** function. If an error occurs while doing so, return 0 and write an
** error message to pErrorDb.
*/
static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
@@ -73220,14 +81606,13 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
if( i==1 ){
Parse sParse;
int rc = 0;
- memset(&sParse, 0, sizeof(sParse));
- sParse.db = pDb;
+ sqlite3ParseObjectInit(&sParse,pDb);
if( sqlite3OpenTempDatabase(&sParse) ){
sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg);
rc = SQLITE_ERROR;
}
sqlite3DbFree(pErrorDb, sParse.zErrMsg);
- sqlite3ParserReset(&sParse);
+ sqlite3ParseObjectReset(&sParse);
if( rc ){
return 0;
}
@@ -73247,18 +81632,18 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
*/
static int setDestPgsz(sqlite3_backup *p){
int rc;
- rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0);
+ rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),0,0);
return rc;
}
/*
** Check that there is no open read-transaction on the b-tree passed as the
** second argument. If there is not, return SQLITE_OK. Otherwise, if there
-** is an open read-transaction, return SQLITE_ERROR and leave an error
+** is an open read-transaction, return SQLITE_ERROR and leave an error
** message in database handle db.
*/
static int checkReadTransaction(sqlite3 *db, Btree *p){
- if( sqlite3BtreeIsInReadTrans(p) ){
+ if( sqlite3BtreeTxnState(p)!=SQLITE_TXN_NONE ){
sqlite3ErrorWithMsg(db, SQLITE_ERROR, "destination database is in use");
return SQLITE_ERROR;
}
@@ -73324,13 +81709,13 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init(
p->iNext = 1;
p->isAttached = 0;
- if( 0==p->pSrc || 0==p->pDest
- || checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK
+ if( 0==p->pSrc || 0==p->pDest
+ || checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK
){
/* One (or both) of the named databases did not exist or an OOM
** error was hit. Or there is a transaction open on the destination
- ** database. The error has already been written into the pDestDb
- ** handle. All that is left to do here is free the sqlite3_backup
+ ** database. The error has already been written into the pDestDb
+ ** handle. All that is left to do here is free the sqlite3_backup
** structure. */
sqlite3_free(p);
p = 0;
@@ -73346,7 +81731,7 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init(
}
/*
-** Argument rc is an SQLite error code. Return true if this error is
+** Argument rc is an SQLite error code. Return true if this error is
** considered fatal if encountered during a backup operation. All errors
** are considered fatal except for SQLITE_BUSY and SQLITE_LOCKED.
*/
@@ -73355,8 +81740,8 @@ static int isFatalError(int rc){
}
/*
-** Parameter zSrcData points to a buffer containing the data for
-** page iSrcPg from the source database. Copy this data into the
+** Parameter zSrcData points to a buffer containing the data for
+** page iSrcPg from the source database. Copy this data into the
** destination database.
*/
static int backupOnePage(
@@ -73370,13 +81755,6 @@ static int backupOnePage(
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
-#ifdef SQLITE_HAS_CODEC
- /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is
- ** guaranteed that the shared-mutex is held by this thread, handle
- ** p->pSrc may not actually be the owner. */
- int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc);
- int nDestReserve = sqlite3BtreeGetOptimalReserve(p->pDest);
-#endif
int rc = SQLITE_OK;
i64 iOff;
@@ -73385,35 +81763,9 @@ static int backupOnePage(
assert( !isFatalError(p->rc) );
assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
assert( zSrcData );
+ assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 );
- /* Catch the case where the destination is an in-memory database and the
- ** page sizes of the source and destination differ.
- */
- if( nSrcPgsz!=nDestPgsz && sqlite3PagerIsMemdb(pDestPager) ){
- rc = SQLITE_READONLY;
- }
-
-#ifdef SQLITE_HAS_CODEC
- /* Backup is not possible if the page size of the destination is changing
- ** and a codec is in use.
- */
- if( nSrcPgsz!=nDestPgsz && sqlite3PagerGetCodec(pDestPager)!=0 ){
- rc = SQLITE_READONLY;
- }
-
- /* Backup is not possible if the number of bytes of reserve space differ
- ** between source and destination. If there is a difference, try to
- ** fix the destination to agree with the source. If that is not possible,
- ** then the backup cannot proceed.
- */
- if( nSrcReserve!=nDestReserve ){
- u32 newPgsz = nSrcPgsz;
- rc = sqlite3PagerSetPagesize(pDestPager, &newPgsz, nSrcReserve);
- if( rc==SQLITE_OK && newPgsz!=nSrcPgsz ) rc = SQLITE_READONLY;
- }
-#endif
-
- /* This loop runs once for each destination page spanned by the source
+ /* This loop runs once for each destination page spanned by the source
** page. For each iteration, variable iOff is set to the byte offset
** of the destination page.
*/
@@ -73432,7 +81784,7 @@ static int backupOnePage(
** Then clear the Btree layer MemPage.isInit flag. Both this module
** and the pager code use this trick (clearing the first byte
** of the page 'extra' space to invalidate the Btree layers
- ** cached parse of the page). MemPage.isInit is marked
+ ** cached parse of the page). MemPage.isInit is marked
** "MUST BE FIRST" for this purpose.
*/
memcpy(zOut, zIn, nCopy);
@@ -73452,7 +81804,7 @@ static int backupOnePage(
** exactly iSize bytes. If pFile is not larger than iSize bytes, then
** this function is a no-op.
**
-** Return SQLITE_OK if everything is successful, or an SQLite error
+** Return SQLITE_OK if everything is successful, or an SQLite error
** code if an error occurs.
*/
static int backupTruncateFile(sqlite3_file *pFile, i64 iSize){
@@ -73516,7 +81868,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
** one now. If a transaction is opened here, then it will be closed
** before this function exits.
*/
- if( rc==SQLITE_OK && 0==sqlite3BtreeIsInReadTrans(p->pSrc) ){
+ if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlite3BtreeTxnState(p->pSrc) ){
rc = sqlite3BtreeBeginTrans(p->pSrc, 0, 0);
bCloseTrans = 1;
}
@@ -73534,7 +81886,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Lock the destination database, if it is not locked already. */
if( SQLITE_OK==rc && p->bDestLocked==0
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2,
- (int*)&p->iDestSchema))
+ (int*)&p->iDestSchema))
){
p->bDestLocked = 1;
}
@@ -73544,10 +81896,13 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
pgszDest = sqlite3BtreeGetPageSize(p->pDest);
destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest));
- if( SQLITE_OK==rc && destMode==PAGER_JOURNALMODE_WAL && pgszSrc!=pgszDest ){
+ if( SQLITE_OK==rc
+ && (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager))
+ && pgszSrc!=pgszDest
+ ){
rc = SQLITE_READONLY;
}
-
+
/* Now that there is a read-lock on the source database, query the
** source pager for the number of pages in the database.
*/
@@ -73574,7 +81929,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
attachBackupObject(p);
}
}
-
+
/* Update the schema version field in the destination database. This
** is to make sure that the schema-version really does change in
** the case where the source and destination databases have the
@@ -73600,12 +81955,12 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
int nDestTruncate;
/* Set nDestTruncate to the final number of pages in the destination
** database. The complication here is that the destination page
- ** size may be different to the source page size.
+ ** size may be different to the source page size.
**
- ** If the source page size is smaller than the destination page size,
+ ** If the source page size is smaller than the destination page size,
** round up. In this case the call to sqlite3OsTruncate() below will
** fix the size of the file. However it is important to call
- ** sqlite3PagerTruncateImage() here so that any pages in the
+ ** sqlite3PagerTruncateImage() here so that any pages in the
** destination file that lie beyond the nDestTruncate page mark are
** journalled by PagerCommitPhaseOne() before they are destroyed
** by the file truncation.
@@ -73629,7 +81984,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
**
** * The destination may need to be truncated, and
**
- ** * Data stored on the pages immediately following the
+ ** * Data stored on the pages immediately following the
** pending-byte page in the source database may need to be
** copied into the destination database.
*/
@@ -73641,7 +81996,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
i64 iEnd;
assert( pFile );
- assert( nDestTruncate==0
+ assert( nDestTruncate==0
|| (i64)nDestTruncate*(i64)pgszDest >= iSize || (
nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
&& iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
@@ -73651,7 +82006,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
** database has been stored in the journal for pDestPager and the
** journal synced to disk. So at this point we may safely modify
** the database file in any way, knowing that if a power failure
- ** occurs, the original database will be reconstructed from the
+ ** occurs, the original database will be reconstructed from the
** journal file. */
sqlite3PagerPagecount(pDestPager, &nDstPage);
for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){
@@ -73671,8 +82026,8 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Write the extra pages and truncate the database file as required */
iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
for(
- iOff=PENDING_BYTE+pgszSrc;
- rc==SQLITE_OK && iOff<iEnd;
+ iOff=PENDING_BYTE+pgszSrc;
+ rc==SQLITE_OK && iOff<iEnd;
iOff+=pgszSrc
){
PgHdr *pSrcPg = 0;
@@ -73696,7 +82051,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
}
-
+
/* Finish committing the transaction to the destination database. */
if( SQLITE_OK==rc
&& SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
@@ -73705,7 +82060,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
}
}
}
-
+
/* If bCloseTrans is true, then this function opened a read transaction
** on the source database. Close the read transaction here. There is
** no need to check the return values of the btree methods here, as
@@ -73717,7 +82072,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0);
assert( rc2==SQLITE_OK );
}
-
+
if( rc==SQLITE_IOERR_NOMEM ){
rc = SQLITE_NOMEM_BKPT;
}
@@ -73754,8 +82109,10 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
}
if( p->isAttached ){
pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc));
+ assert( pp!=0 );
while( *pp!=p ){
pp = &(*pp)->pNext;
+ assert( pp!=0 );
}
*pp = p->pNext;
}
@@ -73797,7 +82154,7 @@ SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p){
}
/*
-** Return the total number of pages in the source database as of the most
+** Return the total number of pages in the source database as of the most
** recent call to sqlite3_backup_step().
*/
SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p){
@@ -73812,7 +82169,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p){
/*
** This function is called after the contents of page iPage of the
-** source database have been modified. If page iPage has already been
+** source database have been modified. If page iPage has already been
** copied into the destination database, then the data written to the
** destination is now invalidated. The destination copy of iPage needs
** to be updated with the new data before the backup operation is
@@ -73855,7 +82212,7 @@ SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, con
** Restart the backup process. This is called when the pager layer
** detects that the database has been modified by an external database
** connection. In this case there is no way of knowing which of the
-** pages that have been copied into the destination database are still
+** pages that have been copied into the destination database are still
** valid and which are not, so the entire process needs to be restarted.
**
** It is assumed that the mutex associated with the BtShared object
@@ -73875,8 +82232,8 @@ SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *pBackup){
** Copy the complete content of pBtFrom into pBtTo. A transaction
** must be active for both files.
**
-** The size of file pTo may be reduced by this operation. If anything
-** goes wrong, the transaction on pTo is rolled back. If successful, the
+** The size of file pTo may be reduced by this operation. If anything
+** goes wrong, the transaction on pTo is rolled back. If successful, the
** transaction is committed before returning.
*/
SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
@@ -73886,7 +82243,7 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
sqlite3BtreeEnter(pTo);
sqlite3BtreeEnter(pFrom);
- assert( sqlite3BtreeIsInTrans(pTo) );
+ assert( sqlite3BtreeTxnState(pTo)==SQLITE_TXN_WRITE );
pFd = sqlite3PagerFile(sqlite3BtreePager(pTo));
if( pFd->pMethods ){
i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom);
@@ -73906,15 +82263,11 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
b.pDest = pTo;
b.iNext = 1;
-#ifdef SQLITE_HAS_CODEC
- sqlite3PagerAlignReserve(sqlite3BtreePager(pTo), sqlite3BtreePager(pFrom));
-#endif
-
/* 0x7FFFFFFF is the hard limit for the number of pages in a database
** file. By passing this as the number of pages to copy to
- ** sqlite3_backup_step(), we can guarantee that the copy finishes
+ ** sqlite3_backup_step(), we can guarantee that the copy finishes
** within a single call (unless an error occurs). The assert() statement
- ** checks this assumption - (p->rc) should be set to either SQLITE_DONE
+ ** checks this assumption - (p->rc) should be set to either SQLITE_DONE
** or an error code. */
sqlite3_backup_step(&b, 0x7FFFFFFF);
assert( b.rc!=SQLITE_OK );
@@ -73926,7 +82279,7 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
}
- assert( sqlite3BtreeIsInTrans(pTo)==0 );
+ assert( sqlite3BtreeTxnState(pTo)!=SQLITE_TXN_WRITE );
copy_finished:
sqlite3BtreeLeave(pFrom);
sqlite3BtreeLeave(pTo);
@@ -73956,6 +82309,11 @@ copy_finished:
/* #include "sqliteInt.h" */
/* #include "vdbeInt.h" */
+/* True if X is a power of two. 0 is considered a power of two here.
+** In other words, return true if X has at most one bit set.
+*/
+#define ISPOWEROF2(X) (((X)&((X)-1))==0)
+
#ifdef SQLITE_DEBUG
/*
** Check invariants on a Mem object.
@@ -73964,7 +82322,7 @@ copy_finished:
** this: assert( sqlite3VdbeCheckMemInvariants(pMem) );
*/
SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){
- /* If MEM_Dyn is set then Mem.xDel!=0.
+ /* If MEM_Dyn is set then Mem.xDel!=0.
** Mem.xDel might not be initialized if MEM_Dyn is clear.
*/
assert( (p->flags & MEM_Dyn)==0 || p->xDel!=0 );
@@ -73975,8 +82333,8 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){
** That saves a few cycles in inner loops. */
assert( (p->flags & MEM_Dyn)==0 || p->szMalloc==0 );
- /* Cannot be both MEM_Int and MEM_Real at the same time */
- assert( (p->flags & (MEM_Int|MEM_Real))!=(MEM_Int|MEM_Real) );
+ /* Cannot have more than one of MEM_Int, MEM_Real, or MEM_IntReal */
+ assert( ISPOWEROF2(p->flags & (MEM_Int|MEM_Real|MEM_IntReal)) );
if( p->flags & MEM_Null ){
/* Cannot be both MEM_Null and some other type */
@@ -73995,7 +82353,7 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){
((p->flags&MEM_Static)!=0 ? 1 : 0) <= 1 );
/* No other bits set */
- assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype
+ assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype|MEM_FromBind
|MEM_Dyn|MEM_Ephem|MEM_Static))==0 );
}else{
/* A pure NULL might have other flags, such as MEM_Static, MEM_Dyn,
@@ -74008,7 +82366,9 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){
/* The szMalloc field holds the correct memory allocation size */
assert( p->szMalloc==0
- || p->szMalloc==sqlite3DbMallocSize(p->db,p->zMalloc) );
+ || (p->flags==MEM_Undefined
+ && p->szMalloc<=sqlite3DbMallocSize(p->db,p->zMalloc))
+ || p->szMalloc==sqlite3DbMallocSize(p->db,p->zMalloc));
/* If p holds a string or blob, the Mem.z must point to exactly
** one of the following:
@@ -74019,7 +82379,7 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){
** (4) A static string or blob
*/
if( (p->flags & (MEM_Str|MEM_Blob)) && p->n>0 ){
- assert(
+ assert(
((p->szMalloc>0 && p->z==p->zMalloc)? 1 : 0) +
((p->flags&MEM_Dyn)!=0 ? 1 : 0) +
((p->flags&MEM_Ephem)!=0 ? 1 : 0) +
@@ -74030,9 +82390,41 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){
}
#endif
+/*
+** Render a Mem object which is one of MEM_Int, MEM_Real, or MEM_IntReal
+** into a buffer.
+*/
+static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){
+ StrAccum acc;
+ assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) );
+ assert( sz>22 );
+ if( p->flags & MEM_Int ){
+#if GCC_VERSION>=7000000
+ /* Work-around for GCC bug
+ ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96270 */
+ i64 x;
+ assert( (p->flags&MEM_Int)*2==sizeof(x) );
+ memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2);
+ p->n = sqlite3Int64ToText(x, zBuf);
+#else
+ p->n = sqlite3Int64ToText(p->u.i, zBuf);
+#endif
+ }else{
+ sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0);
+ sqlite3_str_appendf(&acc, "%!.15g",
+ (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r);
+ assert( acc.zText==zBuf && acc.mxAlloc<=0 );
+ zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */
+ p->n = acc.nChar;
+ }
+}
+
#ifdef SQLITE_DEBUG
/*
-** Check that string value of pMem agrees with its integer or real value.
+** Validity checks on pMem. pMem holds a string.
+**
+** (1) Check that string value of pMem agrees with its integer or real value.
+** (2) Check that the string is correctly zero terminated
**
** A single int or real value always converts to the same strings. But
** many different strings can be converted into the same int or real.
@@ -74050,17 +82442,27 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){
**
** This routine is for use inside of assert() statements only.
*/
-SQLITE_PRIVATE int sqlite3VdbeMemConsistentDualRep(Mem *p){
+SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){
+ Mem tmp;
char zBuf[100];
char *z;
int i, j, incr;
if( (p->flags & MEM_Str)==0 ) return 1;
- if( (p->flags & (MEM_Int|MEM_Real))==0 ) return 1;
- if( p->flags & MEM_Int ){
- sqlite3_snprintf(sizeof(zBuf),zBuf,"%lld",p->u.i);
- }else{
- sqlite3_snprintf(sizeof(zBuf),zBuf,"%!.15g",p->u.r);
- }
+ if( p->db && p->db->mallocFailed ) return 1;
+ if( p->flags & MEM_Term ){
+ /* Insure that the string is properly zero-terminated. Pay particular
+ ** attention to the case where p->n is odd */
+ if( p->szMalloc>0 && p->z==p->zMalloc ){
+ assert( p->enc==SQLITE_UTF8 || p->szMalloc >= ((p->n+1)&~1)+2 );
+ assert( p->enc!=SQLITE_UTF8 || p->szMalloc >= p->n+1 );
+ }
+ assert( p->z[p->n]==0 );
+ assert( p->enc==SQLITE_UTF8 || p->z[(p->n+1)&~1]==0 );
+ assert( p->enc==SQLITE_UTF8 || p->z[((p->n+1)&~1)+1]==0 );
+ }
+ if( (p->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ) return 1;
+ memcpy(&tmp, p, sizeof(tmp));
+ vdbeMemRenderNum(sizeof(zBuf), zBuf, &tmp);
z = p->z;
i = j = 0;
incr = 1;
@@ -74093,10 +82495,15 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
#ifndef SQLITE_OMIT_UTF16
int rc;
#endif
+ assert( pMem!=0 );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE
|| desiredEnc==SQLITE_UTF16BE );
- if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){
+ if( !(pMem->flags&MEM_Str) ){
+ pMem->enc = desiredEnc;
+ return SQLITE_OK;
+ }
+ if( pMem->enc==desiredEnc ){
return SQLITE_OK;
}
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
@@ -74116,8 +82523,7 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
}
/*
-** Make sure pMem->z points to a writable allocation of at least
-** min(n,32) bytes.
+** Make sure pMem->z points to a writable allocation of at least n bytes.
**
** If the bPreserve argument is true, then copy of the content of
** pMem->z into the new allocation. pMem must be either a string or
@@ -74135,10 +82541,17 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPre
testcase( bPreserve && pMem->z==0 );
assert( pMem->szMalloc==0
- || pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) );
- if( n<32 ) n = 32;
+ || (pMem->flags==MEM_Undefined
+ && pMem->szMalloc<=sqlite3DbMallocSize(pMem->db,pMem->zMalloc))
+ || pMem->szMalloc==sqlite3DbMallocSize(pMem->db,pMem->zMalloc));
if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){
- pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
+ if( pMem->db ){
+ pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
+ }else{
+ pMem->zMalloc = sqlite3Realloc(pMem->z, n);
+ if( pMem->zMalloc==0 ) sqlite3_free(pMem->z);
+ pMem->z = pMem->zMalloc;
+ }
bPreserve = 0;
}else{
if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc);
@@ -74174,34 +82587,74 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPre
**
** Any prior string or blob content in the pMem object may be discarded.
** The pMem->xDel destructor is called, if it exists. Though MEM_Str
-** and MEM_Blob values may be discarded, MEM_Int, MEM_Real, and MEM_Null
-** values are preserved.
+** and MEM_Blob values may be discarded, MEM_Int, MEM_Real, MEM_IntReal,
+** and MEM_Null values are preserved.
**
** Return SQLITE_OK on success or an error code (probably SQLITE_NOMEM)
** if unable to complete the resizing.
*/
SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){
- assert( szNew>0 );
+ assert( CORRUPT_DB || szNew>0 );
assert( (pMem->flags & MEM_Dyn)==0 || pMem->szMalloc==0 );
if( pMem->szMalloc<szNew ){
return sqlite3VdbeMemGrow(pMem, szNew, 0);
}
assert( (pMem->flags & MEM_Dyn)==0 );
pMem->z = pMem->zMalloc;
- pMem->flags &= (MEM_Null|MEM_Int|MEM_Real);
+ pMem->flags &= (MEM_Null|MEM_Int|MEM_Real|MEM_IntReal);
return SQLITE_OK;
}
/*
+** If pMem is already a string, detect if it is a zero-terminated
+** string, or make it into one if possible, and mark it as such.
+**
+** This is an optimization. Correct operation continues even if
+** this routine is a no-op.
+*/
+SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){
+ if( (pMem->flags & (MEM_Str|MEM_Term|MEM_Ephem|MEM_Static))!=MEM_Str ){
+ /* pMem must be a string, and it cannot be an ephemeral or static string */
+ return;
+ }
+ if( pMem->enc!=SQLITE_UTF8 ) return;
+ if( NEVER(pMem->z==0) ) return;
+ if( pMem->flags & MEM_Dyn ){
+ if( pMem->xDel==sqlite3_free
+ && sqlite3_msize(pMem->z) >= (u64)(pMem->n+1)
+ ){
+ pMem->z[pMem->n] = 0;
+ pMem->flags |= MEM_Term;
+ return;
+ }
+ if( pMem->xDel==sqlite3RCStrUnref ){
+ /* Blindly assume that all RCStr objects are zero-terminated */
+ pMem->flags |= MEM_Term;
+ return;
+ }
+ }else if( pMem->szMalloc >= pMem->n+1 ){
+ pMem->z[pMem->n] = 0;
+ pMem->flags |= MEM_Term;
+ return;
+ }
+}
+
+/*
** It is already known that pMem contains an unterminated string.
** Add the zero terminator.
+**
+** Three bytes of zero are added. In this way, there is guaranteed
+** to be a double-zero byte at an even byte boundary in order to
+** terminate a UTF16 string, even if the initial size of the buffer
+** is an odd number of bytes.
*/
static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){
- if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){
+ if( sqlite3VdbeMemGrow(pMem, pMem->n+3, 1) ){
return SQLITE_NOMEM_BKPT;
}
pMem->z[pMem->n] = 0;
pMem->z[pMem->n+1] = 0;
+ pMem->z[pMem->n+2] = 0;
pMem->flags |= MEM_Term;
return SQLITE_OK;
}
@@ -74213,6 +82666,7 @@ static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){
** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
*/
SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
+ assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){
@@ -74237,19 +82691,24 @@ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
#ifndef SQLITE_OMIT_INCRBLOB
SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){
int nByte;
+ assert( pMem!=0 );
assert( pMem->flags & MEM_Zero );
- assert( pMem->flags&MEM_Blob );
+ assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) );
+ testcase( sqlite3_value_nochange(pMem) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
/* Set nByte to the number of bytes required to store the expanded blob. */
nByte = pMem->n + pMem->u.nZero;
if( nByte<=0 ){
+ if( (pMem->flags & MEM_Blob)==0 ) return SQLITE_OK;
nByte = 1;
}
if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){
return SQLITE_NOMEM_BKPT;
}
+ assert( pMem->z!=0 );
+ assert( sqlite3DbMallocSize(pMem->db,pMem->z) >= nByte );
memset(&pMem->z[pMem->n], 0, pMem->u.nZero);
pMem->n += pMem->u.nZero;
@@ -74262,6 +82721,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){
** Make sure the given Mem is \u0000 terminated.
*/
SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
+ assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
testcase( (pMem->flags & (MEM_Term|MEM_Str))==(MEM_Term|MEM_Str) );
testcase( (pMem->flags & (MEM_Term|MEM_Str))==0 );
@@ -74273,12 +82733,12 @@ SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
}
/*
-** Add MEM_Str to the set of representations for the given Mem. Numbers
-** are converted using sqlite3_snprintf(). Converting a BLOB to a string
-** is a no-op.
+** Add MEM_Str to the set of representations for the given Mem. This
+** routine is only called if pMem is a number of some kind, not a NULL
+** or a BLOB.
**
-** Existing representations MEM_Int and MEM_Real are invalidated if
-** bForce is true but are retained if bForce is false.
+** Existing representations MEM_Int, MEM_Real, or MEM_IntReal are invalidated
+** if bForce is true but are retained if bForce is false.
**
** A MEM_Null value will never be passed to this function. This function is
** used for converting values to text for returning to the user (i.e. via
@@ -74287,13 +82747,13 @@ SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
** user and the latter is an internal programming error.
*/
SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
- int fg = pMem->flags;
const int nByte = 32;
+ assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
- assert( !(fg&MEM_Zero) );
- assert( !(fg&(MEM_Str|MEM_Blob)) );
- assert( fg&(MEM_Int|MEM_Real) );
+ assert( !(pMem->flags&MEM_Zero) );
+ assert( !(pMem->flags&(MEM_Str|MEM_Blob)) );
+ assert( pMem->flags&(MEM_Int|MEM_Real|MEM_IntReal) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
@@ -74303,23 +82763,12 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
return SQLITE_NOMEM_BKPT;
}
- /* For a Real or Integer, use sqlite3_snprintf() to produce the UTF-8
- ** string representation of the value. Then, if the required encoding
- ** is UTF-16le or UTF-16be do a translation.
- **
- ** FIX ME: It would be better if sqlite3_snprintf() could do UTF-16.
- */
- if( fg & MEM_Int ){
- sqlite3_snprintf(nByte, pMem->z, "%lld", pMem->u.i);
- }else{
- assert( fg & MEM_Real );
- sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r);
- }
+ vdbeMemRenderNum(nByte, pMem->z, pMem);
assert( pMem->z!=0 );
- pMem->n = sqlite3Strlen30NN(pMem->z);
+ assert( pMem->n==(int)sqlite3Strlen30NN(pMem->z) );
pMem->enc = SQLITE_UTF8;
pMem->flags |= MEM_Str|MEM_Term;
- if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real);
+ if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
sqlite3VdbeChangeEncoding(pMem, enc);
return SQLITE_OK;
}
@@ -74336,9 +82785,11 @@ SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
sqlite3_context ctx;
Mem t;
assert( pFunc!=0 );
+ assert( pMem!=0 );
+ assert( pMem->db!=0 );
assert( pFunc->xFinalize!=0 );
assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef );
- assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
+ assert( sqlite3_mutex_held(pMem->db->mutex) );
memset(&ctx, 0, sizeof(ctx));
memset(&t, 0, sizeof(t));
t.flags = MEM_Null;
@@ -74346,6 +82797,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
ctx.pOut = &t;
ctx.pMem = pMem;
ctx.pFunc = pFunc;
+ ctx.enc = ENC(t.db);
pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */
assert( (pMem->flags & MEM_Dyn)==0 );
if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc);
@@ -74358,25 +82810,23 @@ SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
** This routine calls the xValue method for that function and stores
** the results in memory cell pMem.
**
-** SQLITE_ERROR is returned if xValue() reports an error. SQLITE_OK
+** SQLITE_ERROR is returned if xValue() reports an error. SQLITE_OK
** otherwise.
*/
#ifndef SQLITE_OMIT_WINDOWFUNC
SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pFunc){
sqlite3_context ctx;
- Mem t;
assert( pFunc!=0 );
assert( pFunc->xValue!=0 );
assert( (pAccum->flags & MEM_Null)!=0 || pFunc==pAccum->u.pDef );
- assert( pAccum->db==0 || sqlite3_mutex_held(pAccum->db->mutex) );
+ assert( pAccum->db!=0 );
+ assert( sqlite3_mutex_held(pAccum->db->mutex) );
memset(&ctx, 0, sizeof(ctx));
- memset(&t, 0, sizeof(t));
- t.flags = MEM_Null;
- t.db = pAccum->db;
sqlite3VdbeMemSetNull(pOut);
ctx.pOut = pOut;
ctx.pMem = pAccum;
ctx.pFunc = pFunc;
+ ctx.enc = ENC(pAccum->db);
pFunc->xValue(&ctx);
return ctx.isError;
}
@@ -74442,34 +82892,12 @@ SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){
}
}
-/*
-** Convert a 64-bit IEEE double into a 64-bit signed integer.
-** If the double is out of range of a 64-bit signed integer then
-** return the closest available 64-bit signed integer.
+/* Like sqlite3VdbeMemRelease() but faster for cases where we
+** know in advance that the Mem is not MEM_Dyn or MEM_Agg.
*/
-static SQLITE_NOINLINE i64 doubleToInt64(double r){
-#ifdef SQLITE_OMIT_FLOATING_POINT
- /* When floating-point is omitted, double and int64 are the same thing */
- return r;
-#else
- /*
- ** Many compilers we encounter do not define constants for the
- ** minimum and maximum 64-bit integers, or they define them
- ** inconsistently. And many do not understand the "LL" notation.
- ** So we define our own static constants here using nothing
- ** larger than a 32-bit integer constant.
- */
- static const i64 maxInt = LARGEST_INT64;
- static const i64 minInt = SMALLEST_INT64;
-
- if( r<=(double)minInt ){
- return minInt;
- }else if( r>=(double)maxInt ){
- return maxInt;
- }else{
- return (i64)r;
- }
-#endif
+SQLITE_PRIVATE void sqlite3VdbeMemReleaseMalloc(Mem *p){
+ assert( !VdbeMemDynamic(p) );
+ if( p->szMalloc ) vdbeMemClear(p);
}
/*
@@ -74483,22 +82911,23 @@ static SQLITE_NOINLINE i64 doubleToInt64(double r){
**
** If pMem represents a string value, its encoding might be changed.
*/
-static SQLITE_NOINLINE i64 memIntValue(Mem *pMem){
+static SQLITE_NOINLINE i64 memIntValue(const Mem *pMem){
i64 value = 0;
sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc);
return value;
}
-SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem *pMem){
+SQLITE_PRIVATE i64 sqlite3VdbeIntValue(const Mem *pMem){
int flags;
+ assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
flags = pMem->flags;
- if( flags & MEM_Int ){
+ if( flags & (MEM_Int|MEM_IntReal) ){
+ testcase( flags & MEM_IntReal );
return pMem->u.i;
}else if( flags & MEM_Real ){
- return doubleToInt64(pMem->u.r);
- }else if( flags & (MEM_Str|MEM_Blob) ){
- assert( pMem->z || pMem->n==0 );
+ return sqlite3RealToI64(pMem->u.r);
+ }else if( (flags & (MEM_Str|MEM_Blob))!=0 && pMem->z!=0 ){
return memIntValue(pMem);
}else{
return 0;
@@ -74518,11 +82947,13 @@ static SQLITE_NOINLINE double memRealValue(Mem *pMem){
return val;
}
SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){
+ assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
if( pMem->flags & MEM_Real ){
return pMem->u.r;
- }else if( pMem->flags & MEM_Int ){
+ }else if( pMem->flags & (MEM_Int|MEM_IntReal) ){
+ testcase( pMem->flags & MEM_IntReal );
return (double)pMem->u.i;
}else if( pMem->flags & (MEM_Str|MEM_Blob) ){
return memRealValue(pMem);
@@ -74534,40 +82965,45 @@ SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){
/*
** Return 1 if pMem represents true, and return 0 if pMem represents false.
-** Return the value ifNull if pMem is NULL.
+** Return the value ifNull if pMem is NULL.
*/
SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem *pMem, int ifNull){
- if( pMem->flags & MEM_Int ) return pMem->u.i!=0;
+ testcase( pMem->flags & MEM_IntReal );
+ if( pMem->flags & (MEM_Int|MEM_IntReal) ) return pMem->u.i!=0;
if( pMem->flags & MEM_Null ) return ifNull;
return sqlite3VdbeRealValue(pMem)!=0.0;
}
/*
-** The MEM structure is already a MEM_Real. Try to also make it a
-** MEM_Int if we can.
+** The MEM structure is already a MEM_Real or MEM_IntReal. Try to
+** make it a MEM_Int if we can.
*/
SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){
- i64 ix;
- assert( pMem->flags & MEM_Real );
+ assert( pMem!=0 );
+ assert( pMem->flags & (MEM_Real|MEM_IntReal) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
- ix = doubleToInt64(pMem->u.r);
-
- /* Only mark the value as an integer if
- **
- ** (1) the round-trip conversion real->int->real is a no-op, and
- ** (2) The integer is neither the largest nor the smallest
- ** possible integer (ticket #3922)
- **
- ** The second and third terms in the following conditional enforces
- ** the second condition under the assumption that addition overflow causes
- ** values to wrap around.
- */
- if( pMem->u.r==ix && ix>SMALLEST_INT64 && ix<LARGEST_INT64 ){
- pMem->u.i = ix;
+ if( pMem->flags & MEM_IntReal ){
MemSetTypeFlag(pMem, MEM_Int);
+ }else{
+ i64 ix = sqlite3RealToI64(pMem->u.r);
+
+ /* Only mark the value as an integer if
+ **
+ ** (1) the round-trip conversion real->int->real is a no-op, and
+ ** (2) The integer is neither the largest nor the smallest
+ ** possible integer (ticket #3922)
+ **
+ ** The second and third terms in the following conditional enforces
+ ** the second condition under the assumption that addition overflow causes
+ ** values to wrap around.
+ */
+ if( pMem->u.r==ix && ix>SMALLEST_INT64 && ix<LARGEST_INT64 ){
+ pMem->u.i = ix;
+ MemSetTypeFlag(pMem, MEM_Int);
+ }
}
}
@@ -74575,6 +83011,7 @@ SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){
** Convert pMem to type integer. Invalidate any prior representations.
*/
SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem *pMem){
+ assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
@@ -74589,6 +83026,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem *pMem){
** Invalidate any prior representations.
*/
SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem *pMem){
+ assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
@@ -74600,17 +83038,31 @@ SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem *pMem){
/* Compare a floating point value to an integer. Return true if the two
** values are the same within the precision of the floating point value.
**
+** This function assumes that i was obtained by assignment from r1.
+**
** For some versions of GCC on 32-bit machines, if you do the more obvious
** comparison of "r1==(double)i" you sometimes get an answer of false even
** though the r1 and (double)i values are bit-for-bit the same.
*/
-static int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){
+SQLITE_PRIVATE int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){
double r2 = (double)i;
- return memcmp(&r1, &r2, sizeof(r1))==0;
+ return r1==0.0
+ || (memcmp(&r1, &r2, sizeof(r1))==0
+ && i >= -2251799813685248LL && i < 2251799813685248LL);
+}
+
+/* Convert a floating point value to its closest integer. Do so in
+** a way that avoids 'outside the range of representable values' warnings
+** from UBSAN.
+*/
+SQLITE_PRIVATE i64 sqlite3RealToI64(double r){
+ if( r<-9223372036854774784.0 ) return SMALLEST_INT64;
+ if( r>+9223372036854774784.0 ) return LARGEST_INT64;
+ return (i64)r;
}
/*
-** Convert pMem so that it has types MEM_Real or MEM_Int or both.
+** Convert pMem so that it has type MEM_Real or MEM_Int.
** Invalidate any prior representations.
**
** Every effort is made to force the conversion, even if the input
@@ -74618,25 +83070,27 @@ static int sqlite3RealSameAsInt(double r1, sqlite3_int64 i){
** as much of the string as we can and ignore the rest.
*/
SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
- if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){
+ assert( pMem!=0 );
+ testcase( pMem->flags & MEM_Int );
+ testcase( pMem->flags & MEM_Real );
+ testcase( pMem->flags & MEM_IntReal );
+ testcase( pMem->flags & MEM_Null );
+ if( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null))==0 ){
int rc;
+ sqlite3_int64 ix;
assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
- rc = sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc);
- if( rc==0 ){
+ rc = sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc);
+ if( ((rc==0 || rc==1) && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1)
+ || sqlite3RealSameAsInt(pMem->u.r, (ix = sqlite3RealToI64(pMem->u.r)))
+ ){
+ pMem->u.i = ix;
MemSetTypeFlag(pMem, MEM_Int);
}else{
- i64 i = pMem->u.i;
- sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc);
- if( rc==1 && sqlite3RealSameAsInt(pMem->u.r, i) ){
- pMem->u.i = i;
- MemSetTypeFlag(pMem, MEM_Int);
- }else{
- MemSetTypeFlag(pMem, MEM_Real);
- }
+ MemSetTypeFlag(pMem, MEM_Real);
}
}
- assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 );
+ assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null))!=0 );
pMem->flags &= ~(MEM_Str|MEM_Blob|MEM_Zero);
return SQLITE_OK;
}
@@ -74648,8 +83102,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
** affinity even if that results in loss of data. This routine is
** used (for example) to implement the SQL "cast()" operator.
*/
-SQLITE_PRIVATE void sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){
- if( pMem->flags & MEM_Null ) return;
+SQLITE_PRIVATE int sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){
+ if( pMem->flags & MEM_Null ) return SQLITE_OK;
switch( aff ){
case SQLITE_AFF_BLOB: { /* Really a cast to BLOB */
if( (pMem->flags & MEM_Blob)==0 ){
@@ -74674,15 +83128,20 @@ SQLITE_PRIVATE void sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){
break;
}
default: {
+ int rc;
assert( aff==SQLITE_AFF_TEXT );
assert( MEM_Str==(MEM_Blob>>3) );
pMem->flags |= (pMem->flags&MEM_Blob)>>3;
sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding);
assert( pMem->flags & MEM_Str || pMem->db->mallocFailed );
- pMem->flags &= ~(MEM_Int|MEM_Real|MEM_Blob|MEM_Zero);
- break;
+ pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal|MEM_Blob|MEM_Zero);
+ if( encoding!=SQLITE_UTF8 ) pMem->n &= ~1;
+ rc = sqlite3VdbeChangeEncoding(pMem, encoding);
+ if( rc ) return rc;
+ sqlite3VdbeMemZeroTerminateIfAble(pMem);
}
}
+ return SQLITE_OK;
}
/*
@@ -74718,13 +83177,14 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){
}
}
SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value *p){
- sqlite3VdbeMemSetNull((Mem*)p);
+ sqlite3VdbeMemSetNull((Mem*)p);
}
/*
** Delete any previous value and set the value to be a BLOB of length
** n containing all zeros.
*/
+#ifndef SQLITE_OMIT_INCRBLOB
SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){
sqlite3VdbeMemRelease(pMem);
pMem->flags = MEM_Blob|MEM_Zero;
@@ -74734,6 +83194,21 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){
pMem->enc = SQLITE_UTF8;
pMem->z = 0;
}
+#else
+SQLITE_PRIVATE int sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){
+ int nByte = n>0?n:1;
+ if( sqlite3VdbeMemGrow(pMem, nByte, 0) ){
+ return SQLITE_NOMEM_BKPT;
+ }
+ assert( pMem->z!=0 );
+ assert( sqlite3DbMallocSize(pMem->db, pMem->z)>=nByte );
+ memset(pMem->z, 0, nByte);
+ pMem->n = n>0?n:0;
+ pMem->flags = MEM_Blob;
+ pMem->enc = SQLITE_UTF8;
+ return SQLITE_OK;
+}
+#endif
/*
** The pMem is known to contain content that needs to be destroyed prior
@@ -74773,6 +83248,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetPointer(
void (*xDestructor)(void*)
){
assert( pMem->flags==MEM_Null );
+ vdbeMemClear(pMem);
pMem->u.zPType = zPType ? zPType : "";
pMem->z = pPtr;
pMem->flags = MEM_Null|MEM_Dyn|MEM_Subtype|MEM_Term;
@@ -74839,7 +83315,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem *p){
}
return n>p->db->aLimit[SQLITE_LIMIT_LENGTH];
}
- return 0;
+ return 0;
}
#ifdef SQLITE_DEBUG
@@ -74848,26 +83324,28 @@ SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem *p){
** its link to a shallow copy and by marking any current shallow
** copies of this cell as invalid.
**
-** This is used for testing and debugging only - to make sure shallow
-** copies are not misused.
+** This is used for testing and debugging only - to help ensure that shallow
+** copies (created by OP_SCopy) are not misused.
*/
SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
int i;
Mem *pX;
- for(i=0, pX=pVdbe->aMem; i<pVdbe->nMem; i++, pX++){
+ for(i=1, pX=pVdbe->aMem+1; i<pVdbe->nMem; i++, pX++){
if( pX->pScopyFrom==pMem ){
- /* If pX is marked as a shallow copy of pMem, then verify that
+ u16 mFlags;
+ if( pVdbe->db->flags & SQLITE_VdbeTrace ){
+ sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n",
+ (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem));
+ }
+ /* If pX is marked as a shallow copy of pMem, then try to verify that
** no significant changes have been made to pX since the OP_SCopy.
** A significant change would indicated a missed call to this
** function for pX. Minor changes, such as adding or removing a
** dual type, are allowed, as long as the underlying value is the
** same. */
- u16 mFlags = pMem->flags & pX->flags & pX->mScopyFlags;
- assert( (mFlags&MEM_Int)==0 || pMem->u.i==pX->u.i );
- assert( (mFlags&MEM_Real)==0 || pMem->u.r==pX->u.r );
- assert( (mFlags&MEM_Str)==0 || (pMem->n==pX->n && pMem->z==pX->z) );
- assert( (mFlags&MEM_Blob)==0 || sqlite3BlobCompare(pMem,pX)==0 );
-
+ mFlags = pMem->flags & pX->flags & pX->mScopyFlags;
+ assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i );
+
/* pMem is the register that is changing. But also mark pX as
** undefined so that we can quickly detect the shallow-copy error */
pX->flags = MEM_Undefined;
@@ -74878,7 +83356,6 @@ SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
}
#endif /* SQLITE_DEBUG */
-
/*
** Make an shallow copy of pFrom into pTo. Prior contents of
** pTo are freed. The pFrom->z field is not duplicated. If
@@ -74944,8 +83421,8 @@ SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){
** Change the value of a Mem to be a string or a BLOB.
**
** The memory management strategy depends on the value of the xDel
-** parameter. If the value passed is SQLITE_TRANSIENT, then the
-** string is copied into a (possibly existing) buffer managed by the
+** parameter. If the value passed is SQLITE_TRANSIENT, then the
+** string is copied into a (possibly existing) buffer managed by the
** Mem structure. Otherwise, any existing buffer is freed and the
** pointer copied.
**
@@ -74954,20 +83431,29 @@ SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){
** stored without allocating memory, then it is. If a memory allocation
** is required to store the string, then value of pMem is unchanged. In
** either case, SQLITE_TOOBIG is returned.
+**
+** The "enc" parameter is the text encoding for the string, or zero
+** to store a blob.
+**
+** If n is negative, then the string consists of all bytes up to but
+** excluding the first zero character. The n parameter must be
+** non-negative for blobs.
*/
SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
Mem *pMem, /* Memory cell to set to string value */
const char *z, /* String pointer */
- int n, /* Bytes in string, or negative */
+ i64 n, /* Bytes in string, or negative */
u8 enc, /* Encoding of z. 0 for BLOBs */
void (*xDel)(void*) /* Destructor function */
){
- int nByte = n; /* New value for pMem->n */
+ i64 nByte = n; /* New value for pMem->n */
int iLimit; /* Maximum allowed string or blob size */
- u16 flags = 0; /* New value for pMem->flags */
+ u16 flags; /* New value for pMem->flags */
+ assert( pMem!=0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
+ assert( enc!=0 || n>=0 );
/* If z is a NULL pointer, set pMem to contain an SQL NULL. */
if( !z ){
@@ -74980,16 +83466,30 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
}else{
iLimit = SQLITE_MAX_LENGTH;
}
- flags = (enc==0?MEM_Blob:MEM_Str);
if( nByte<0 ){
assert( enc!=0 );
if( enc==SQLITE_UTF8 ){
- nByte = 0x7fffffff & (int)strlen(z);
- if( nByte>iLimit ) nByte = iLimit+1;
+ nByte = strlen(z);
}else{
for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){}
}
- flags |= MEM_Term;
+ flags= MEM_Str|MEM_Term;
+ }else if( enc==0 ){
+ flags = MEM_Blob;
+ enc = SQLITE_UTF8;
+ }else{
+ flags = MEM_Str;
+ }
+ if( nByte>iLimit ){
+ if( xDel && xDel!=SQLITE_TRANSIENT ){
+ if( xDel==SQLITE_DYNAMIC ){
+ sqlite3DbFree(pMem->db, (void*)z);
+ }else{
+ xDel((void*)z);
+ }
+ }
+ sqlite3VdbeMemSetNull(pMem);
+ return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG);
}
/* The following block sets the new values of Mem.z and Mem.xDel. It
@@ -74997,44 +83497,39 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
** management (one of MEM_Dyn or MEM_Static).
*/
if( xDel==SQLITE_TRANSIENT ){
- int nAlloc = nByte;
+ i64 nAlloc = nByte;
if( flags&MEM_Term ){
nAlloc += (enc==SQLITE_UTF8?1:2);
}
- if( nByte>iLimit ){
- return SQLITE_TOOBIG;
- }
testcase( nAlloc==0 );
testcase( nAlloc==31 );
testcase( nAlloc==32 );
- if( sqlite3VdbeMemClearAndResize(pMem, MAX(nAlloc,32)) ){
+ if( sqlite3VdbeMemClearAndResize(pMem, (int)MAX(nAlloc,32)) ){
return SQLITE_NOMEM_BKPT;
}
memcpy(pMem->z, z, nAlloc);
- }else if( xDel==SQLITE_DYNAMIC ){
- sqlite3VdbeMemRelease(pMem);
- pMem->zMalloc = pMem->z = (char *)z;
- pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
}else{
sqlite3VdbeMemRelease(pMem);
pMem->z = (char *)z;
- pMem->xDel = xDel;
- flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn);
+ if( xDel==SQLITE_DYNAMIC ){
+ pMem->zMalloc = pMem->z;
+ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
+ }else{
+ pMem->xDel = xDel;
+ flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn);
+ }
}
- pMem->n = nByte;
+ pMem->n = (int)(nByte & 0x7fffffff);
pMem->flags = flags;
- pMem->enc = (enc==0 ? SQLITE_UTF8 : enc);
+ pMem->enc = enc;
#ifndef SQLITE_OMIT_UTF16
- if( pMem->enc!=SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){
+ if( enc>SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){
return SQLITE_NOMEM_BKPT;
}
#endif
- if( nByte>iLimit ){
- return SQLITE_TOOBIG;
- }
return SQLITE_OK;
}
@@ -75054,7 +83549,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
** If this routine fails for any reason (malloc returns NULL or unable
** to read from the disk) then the pMem is left in an inconsistent state.
*/
-static SQLITE_NOINLINE int vdbeMemFromBtreeResize(
+SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(
BtCursor *pCur, /* Cursor pointing at record to retrieve. */
u32 offset, /* Offset from the start of data to return bytes from. */
u32 amt, /* Number of bytes to return. */
@@ -75062,6 +83557,9 @@ static SQLITE_NOINLINE int vdbeMemFromBtreeResize(
){
int rc;
pMem->flags = MEM_Null;
+ if( sqlite3BtreeMaxRecordSize(pCur)<offset+amt ){
+ return SQLITE_CORRUPT_BKPT;
+ }
if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+1)) ){
rc = sqlite3BtreePayload(pCur, offset, amt, pMem->z);
if( rc==SQLITE_OK ){
@@ -75074,31 +83572,28 @@ static SQLITE_NOINLINE int vdbeMemFromBtreeResize(
}
return rc;
}
-SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(
+SQLITE_PRIVATE int sqlite3VdbeMemFromBtreeZeroOffset(
BtCursor *pCur, /* Cursor pointing at record to retrieve. */
- u32 offset, /* Offset from the start of data to return bytes from. */
u32 amt, /* Number of bytes to return. */
Mem *pMem /* OUT: Return data in this Mem structure. */
){
- char *zData; /* Data from the btree layer */
u32 available = 0; /* Number of bytes available on the local btree page */
int rc = SQLITE_OK; /* Return code */
assert( sqlite3BtreeCursorIsValid(pCur) );
assert( !VdbeMemDynamic(pMem) );
- /* Note: the calls to BtreeKeyFetch() and DataFetch() below assert()
+ /* Note: the calls to BtreeKeyFetch() and DataFetch() below assert()
** that both the BtShared and database handle mutexes are held. */
assert( !sqlite3VdbeMemIsRowSet(pMem) );
- zData = (char *)sqlite3BtreePayloadFetch(pCur, &available);
- assert( zData!=0 );
+ pMem->z = (char *)sqlite3BtreePayloadFetch(pCur, &available);
+ assert( pMem->z!=0 );
- if( offset+amt<=available ){
- pMem->z = &zData[offset];
+ if( amt<=available ){
pMem->flags = MEM_Blob|MEM_Ephem;
pMem->n = (int)amt;
}else{
- rc = vdbeMemFromBtreeResize(pCur, offset, amt, pMem);
+ rc = sqlite3VdbeMemFromBtree(pCur, 0, amt, pMem);
}
return rc;
@@ -75135,7 +83630,7 @@ static SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){
assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0
|| pVal->db->mallocFailed );
if( pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) ){
- assert( sqlite3VdbeMemConsistentDualRep(pVal) );
+ assert( sqlite3VdbeMemValidStrRep(pVal) );
return pVal->z;
}else{
return 0;
@@ -75158,7 +83653,7 @@ SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
assert( !sqlite3VdbeMemIsRowSet(pVal) );
if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){
- assert( sqlite3VdbeMemConsistentDualRep(pVal) );
+ assert( sqlite3VdbeMemValidStrRep(pVal) );
return pVal->z;
}
if( pVal->flags&MEM_Null ){
@@ -75167,6 +83662,24 @@ SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
return valueToText(pVal, enc);
}
+/* Return true if sqlit3_value object pVal is a string or blob value
+** that uses the destructor specified in the second argument.
+**
+** TODO: Maybe someday promote this interface into a published API so
+** that third-party extensions can get access to it?
+*/
+SQLITE_PRIVATE int sqlite3ValueIsOfClass(const sqlite3_value *pVal, void(*xFree)(void*)){
+ if( ALWAYS(pVal!=0)
+ && ALWAYS((pVal->flags & (MEM_Str|MEM_Blob))!=0)
+ && (pVal->flags & MEM_Dyn)!=0
+ && pVal->xDel==xFree
+ ){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
/*
** Create a new sqlite3_value object.
*/
@@ -75180,7 +83693,7 @@ SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *db){
}
/*
-** Context object passed by sqlite3Stat4ProbeSetValue() through to
+** Context object passed by sqlite3Stat4ProbeSetValue() through to
** valueNew(). See comments above valueNew() for details.
*/
struct ValueNewStat4Ctx {
@@ -75195,14 +83708,14 @@ struct ValueNewStat4Ctx {
** the second argument to this function is NULL, the object is allocated
** by calling sqlite3ValueNew().
**
-** Otherwise, if the second argument is non-zero, then this function is
+** Otherwise, if the second argument is non-zero, then this function is
** being called indirectly by sqlite3Stat4ProbeSetValue(). If it has not
-** already been allocated, allocate the UnpackedRecord structure that
+** already been allocated, allocate the UnpackedRecord structure that
** that function will return to its caller here. Then return a pointer to
** an sqlite3_value within the UnpackedRecord.a[] array.
*/
static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( p ){
UnpackedRecord *pRec = p->ppRec[0];
@@ -75211,7 +83724,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
int nByte; /* Bytes of space to allocate */
int i; /* Counter variable */
int nCol = pIdx->nColumn; /* Number of index columns including rowid */
-
+
nByte = sizeof(Mem) * nCol + ROUND8(sizeof(UnpackedRecord));
pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte);
if( pRec ){
@@ -75232,13 +83745,14 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
if( pRec==0 ) return 0;
p->ppRec[0] = pRec;
}
-
+
pRec->nField = p->iVal+1;
+ sqlite3VdbeMemSetNull(&pRec->aMem[p->iVal]);
return &pRec->aMem[p->iVal];
}
#else
UNUSED_PARAMETER(p);
-#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+#endif /* defined(SQLITE_ENABLE_STAT4) */
return sqlite3ValueNew(db);
}
@@ -75251,21 +83765,21 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
** * the SQLITE_FUNC_NEEDCOLL function flag is not set,
**
** then this routine attempts to invoke the SQL function. Assuming no
-** error occurs, output parameter (*ppVal) is set to point to a value
+** error occurs, output parameter (*ppVal) is set to point to a value
** object containing the result before returning SQLITE_OK.
**
** Affinity aff is applied to the result of the function before returning.
-** If the result is a text value, the sqlite3_value object uses encoding
+** If the result is a text value, the sqlite3_value object uses encoding
** enc.
**
** If the conditions above are not met, this function returns SQLITE_OK
** and sets (*ppVal) to NULL. Or, if an error occurs, (*ppVal) is set to
** NULL and an SQLite error code returned.
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
static int valueFromFunction(
sqlite3 *db, /* The database connection */
- Expr *p, /* The expression to evaluate */
+ const Expr *p, /* The expression to evaluate */
u8 enc, /* Encoding to use */
u8 aff, /* Affinity to use */
sqlite3_value **ppVal, /* Write the new value here */
@@ -75282,12 +83796,17 @@ static int valueFromFunction(
assert( pCtx!=0 );
assert( (p->flags & EP_TokenOnly)==0 );
+ assert( ExprUseXList(p) );
pList = p->x.pList;
if( pList ) nVal = pList->nExpr;
+ assert( !ExprHasProperty(p, EP_IntValue) );
pFunc = sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0);
+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+ if( pFunc==0 ) return SQLITE_OK;
+#endif
assert( pFunc );
- if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
- || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
+ if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
+ || (pFunc->funcFlags & (SQLITE_FUNC_NEEDCOLL|SQLITE_FUNC_RUNONLY))!=0
){
return SQLITE_OK;
}
@@ -75310,10 +83829,10 @@ static int valueFromFunction(
goto value_from_function_out;
}
- assert( pCtx->pParse->rc==SQLITE_OK );
memset(&ctx, 0, sizeof(ctx));
ctx.pOut = pVal;
ctx.pFunc = pFunc;
+ ctx.enc = ENC(db);
pFunc->xSFunc(&ctx, nVal, apVal);
if( ctx.isError ){
rc = ctx.isError;
@@ -75322,16 +83841,16 @@ static int valueFromFunction(
sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8);
assert( rc==SQLITE_OK );
rc = sqlite3VdbeChangeEncoding(pVal, enc);
- if( rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal) ){
+ if( NEVER(rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal)) ){
rc = SQLITE_TOOBIG;
pCtx->pParse->nErr++;
}
}
- pCtx->pParse->rc = rc;
value_from_function_out:
if( rc!=SQLITE_OK ){
pVal = 0;
+ pCtx->pParse->rc = rc;
}
if( apVal ){
for(i=0; i<nVal; i++){
@@ -75345,7 +83864,7 @@ static int valueFromFunction(
}
#else
# define valueFromFunction(a,b,c,d,e,f) SQLITE_OK
-#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+#endif /* defined(SQLITE_ENABLE_STAT4) */
/*
** Extract a value from the supplied expression in the manner described
@@ -75359,7 +83878,7 @@ static int valueFromFunction(
*/
static int valueFromExpr(
sqlite3 *db, /* The database connection */
- Expr *pExpr, /* The expression to evaluate */
+ const Expr *pExpr, /* The expression to evaluate */
u8 enc, /* Encoding to use */
u8 affinity, /* Affinity to use */
sqlite3_value **ppVal, /* Write the new value here */
@@ -75374,11 +83893,7 @@ static int valueFromExpr(
assert( pExpr!=0 );
while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft;
-#if defined(SQLITE_ENABLE_STAT3_OR_STAT4)
if( op==TK_REGISTER ) op = pExpr->op2;
-#else
- if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
-#endif
/* Compressed expressions only appear when parsing the DEFAULT clause
** on a table column definition, and hence only when pCtx==0. This
@@ -75387,12 +83902,21 @@ static int valueFromExpr(
assert( (pExpr->flags & EP_TokenOnly)==0 || pCtx==0 );
if( op==TK_CAST ){
- u8 aff = sqlite3AffinityType(pExpr->u.zToken,0);
+ u8 aff;
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+ aff = sqlite3AffinityType(pExpr->u.zToken,0);
rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx);
testcase( rc!=SQLITE_OK );
if( *ppVal ){
- sqlite3VdbeMemCast(*ppVal, aff, SQLITE_UTF8);
- sqlite3ValueApplyAffinity(*ppVal, affinity, SQLITE_UTF8);
+#ifdef SQLITE_ENABLE_STAT4
+ rc = ExpandBlob(*ppVal);
+#else
+ /* zero-blobs only come from functions, not literal values. And
+ ** functions are only processed under STAT4 */
+ assert( (ppVal[0][0].flags & MEM_Zero)==0 );
+#endif
+ sqlite3VdbeMemCast(*ppVal, aff, enc);
+ sqlite3ValueApplyAffinity(*ppVal, affinity, enc);
}
return rc;
}
@@ -75423,20 +83947,29 @@ static int valueFromExpr(
}else{
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
}
- if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str;
+ assert( (pVal->flags & MEM_IntReal)==0 );
+ if( pVal->flags & (MEM_Int|MEM_IntReal|MEM_Real) ){
+ testcase( pVal->flags & MEM_Int );
+ testcase( pVal->flags & MEM_Real );
+ pVal->flags &= ~MEM_Str;
+ }
if( enc!=SQLITE_UTF8 ){
rc = sqlite3VdbeChangeEncoding(pVal, enc);
}
}else if( op==TK_UMINUS ) {
/* This branch happens for multiple negative signs. Ex: -(-5) */
- if( SQLITE_OK==valueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal,pCtx)
+ if( SQLITE_OK==valueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal,pCtx)
&& pVal!=0
){
sqlite3VdbeMemNumerify(pVal);
if( pVal->flags & MEM_Real ){
pVal->u.r = -pVal->u.r;
}else if( pVal->u.i==SMALLEST_INT64 ){
+#ifndef SQLITE_OMIT_FLOATING_POINT
pVal->u.r = -(double)SMALLEST_INT64;
+#else
+ pVal->u.r = LARGEST_INT64;
+#endif
MemSetTypeFlag(pVal, MEM_Real);
}else{
pVal->u.i = -pVal->u.i;
@@ -75446,11 +83979,12 @@ static int valueFromExpr(
}else if( op==TK_NULL ){
pVal = valueNew(db, pCtx);
if( pVal==0 ) goto no_mem;
- sqlite3VdbeMemNumerify(pVal);
+ sqlite3VdbeMemSetNull(pVal);
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
else if( op==TK_BLOB ){
int nVal;
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' );
assert( pExpr->u.zToken[1]=='\'' );
pVal = valueNew(db, pCtx);
@@ -75462,28 +83996,32 @@ static int valueFromExpr(
0, SQLITE_DYNAMIC);
}
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
else if( op==TK_FUNCTION && pCtx!=0 ){
rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx);
}
#endif
else if( op==TK_TRUEFALSE ){
- pVal = valueNew(db, pCtx);
- pVal->flags = MEM_Int;
- pVal->u.i = pExpr->u.zToken[4]==0;
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+ pVal = valueNew(db, pCtx);
+ if( pVal ){
+ pVal->flags = MEM_Int;
+ pVal->u.i = pExpr->u.zToken[4]==0;
+ sqlite3ValueApplyAffinity(pVal, affinity, enc);
+ }
}
*ppVal = pVal;
return rc;
no_mem:
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- if( pCtx==0 || pCtx->pParse->nErr==0 )
+#ifdef SQLITE_ENABLE_STAT4
+ if( pCtx==0 || NEVER(pCtx->pParse->nErr==0) )
#endif
sqlite3OomFault(db);
sqlite3DbFree(db, zVal);
assert( *ppVal==0 );
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pCtx==0 ) sqlite3ValueFree(pVal);
#else
assert( pCtx==0 ); sqlite3ValueFree(pVal);
@@ -75503,7 +84041,7 @@ no_mem:
*/
SQLITE_PRIVATE int sqlite3ValueFromExpr(
sqlite3 *db, /* The database connection */
- Expr *pExpr, /* The expression to evaluate */
+ const Expr *pExpr, /* The expression to evaluate */
u8 enc, /* Encoding to use */
u8 affinity, /* Affinity to use */
sqlite3_value **ppVal /* Write the new value here */
@@ -75511,56 +84049,7 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
return pExpr ? valueFromExpr(db, pExpr, enc, affinity, ppVal, 0) : 0;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-/*
-** The implementation of the sqlite_record() function. This function accepts
-** a single argument of any type. The return value is a formatted database
-** record (a blob) containing the argument value.
-**
-** This is used to convert the value stored in the 'sample' column of the
-** sqlite_stat3 table to the record format SQLite uses internally.
-*/
-static void recordFunc(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- const int file_format = 1;
- u32 iSerial; /* Serial type */
- int nSerial; /* Bytes of space for iSerial as varint */
- u32 nVal; /* Bytes of space required for argv[0] */
- int nRet;
- sqlite3 *db;
- u8 *aRet;
-
- UNUSED_PARAMETER( argc );
- iSerial = sqlite3VdbeSerialType(argv[0], file_format, &nVal);
- nSerial = sqlite3VarintLen(iSerial);
- db = sqlite3_context_db_handle(context);
-
- nRet = 1 + nSerial + nVal;
- aRet = sqlite3DbMallocRawNN(db, nRet);
- if( aRet==0 ){
- sqlite3_result_error_nomem(context);
- }else{
- aRet[0] = nSerial+1;
- putVarint32(&aRet[1], iSerial);
- sqlite3VdbeSerialPut(&aRet[1+nSerial], argv[0], iSerial);
- sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT);
- sqlite3DbFreeNN(db, aRet);
- }
-}
-
-/*
-** Register built-in functions used to help read ANALYZE data.
-*/
-SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void){
- static FuncDef aAnalyzeTableFuncs[] = {
- FUNCTION(sqlite_record, 1, 0, 0, recordFunc),
- };
- sqlite3InsertBuiltinFuncs(aAnalyzeTableFuncs, ArraySize(aAnalyzeTableFuncs));
-}
-
+#ifdef SQLITE_ENABLE_STAT4
/*
** Attempt to extract a value from pExpr and use it to construct *ppVal.
**
@@ -75621,8 +84110,8 @@ static int stat4ValueFromExpr(
}
/*
-** This function is used to allocate and populate UnpackedRecord
-** structures intended to be compared against sample index keys stored
+** This function is used to allocate and populate UnpackedRecord
+** structures intended to be compared against sample index keys stored
** in the sqlite_stat4 table.
**
** A single call to this function populates zero or more fields of the
@@ -75633,14 +84122,14 @@ static int stat4ValueFromExpr(
**
** * The expression is a bound variable, and this is a reprepare, or
**
-** * The sqlite3ValueFromExpr() function is able to extract a value
+** * The sqlite3ValueFromExpr() function is able to extract a value
** from the expression (i.e. the expression is a literal value).
**
** Or, if pExpr is a TK_VECTOR, one field is populated for each of the
** vector components that match either of the two latter criteria listed
** above.
**
-** Before any value is appended to the record, the affinity of the
+** Before any value is appended to the record, the affinity of the
** corresponding column within index pIdx is applied to it. Before
** this function returns, output parameter *pnExtract is set to the
** number of values appended to the record.
@@ -75691,9 +84180,9 @@ SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(
/*
** Attempt to extract a value from expression pExpr using the methods
-** as described for sqlite3Stat4ProbeSetValue() above.
+** as described for sqlite3Stat4ProbeSetValue() above.
**
-** If successful, set *ppVal to point to a new value object and return
+** If successful, set *ppVal to point to a new value object and return
** SQLITE_OK. If no value can be extracted, but no other error occurs
** (e.g. OOM), return SQLITE_OK and set *ppVal to NULL. Or, if an error
** does occur, return an SQLite error code. The final value of *ppVal
@@ -75713,7 +84202,7 @@ SQLITE_PRIVATE int sqlite3Stat4ValueFromExpr(
** the column value into *ppVal. If *ppVal is initially NULL then a new
** sqlite3_value object is allocated.
**
-** If *ppVal is initially NULL then the caller is responsible for
+** If *ppVal is initially NULL then the caller is responsible for
** ensuring that the value written into *ppVal is eventually freed.
*/
SQLITE_PRIVATE int sqlite3Stat4Column(
@@ -75812,6 +84301,9 @@ SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
if( (p->flags & MEM_Str)!=0 && pVal->enc==enc ){
return p->n;
}
+ if( (p->flags & MEM_Str)!=0 && enc!=SQLITE_UTF8 && pVal->enc!=SQLITE_UTF8 ){
+ return p->n;
+ }
if( (p->flags & MEM_Blob)!=0 ){
if( p->flags & MEM_Zero ){
return p->n + p->u.nZero;
@@ -75837,11 +84329,15 @@ SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
**
*************************************************************************
** This file contains code used for creating, destroying, and populating
-** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.)
+** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.)
*/
/* #include "sqliteInt.h" */
/* #include "vdbeInt.h" */
+/* Forward references */
+static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef);
+static void vdbeFreeOpArray(sqlite3 *, Op *, int);
+
/*
** Create a new virtual database engine.
*/
@@ -75853,23 +84349,30 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){
memset(&p->aOp, 0, sizeof(Vdbe)-offsetof(Vdbe,aOp));
p->db = db;
if( db->pVdbe ){
- db->pVdbe->pPrev = p;
+ db->pVdbe->ppVPrev = &p->pVNext;
}
- p->pNext = db->pVdbe;
- p->pPrev = 0;
+ p->pVNext = db->pVdbe;
+ p->ppVPrev = &db->pVdbe;
db->pVdbe = p;
- p->magic = VDBE_MAGIC_INIT;
+ assert( p->eVdbeState==VDBE_INIT_STATE );
p->pParse = pParse;
pParse->pVdbe = p;
assert( pParse->aLabel==0 );
assert( pParse->nLabel==0 );
- assert( pParse->nOpAlloc==0 );
+ assert( p->nOpAlloc==0 );
assert( pParse->szOpAlloc==0 );
sqlite3VdbeAddOp2(p, OP_Init, 0, 1);
return p;
}
/*
+** Return the Parse object that owns a Vdbe object.
+*/
+SQLITE_PRIVATE Parse *sqlite3VdbeParser(Vdbe *p){
+ return p->pParse;
+}
+
+/*
** Change the error string stored in Vdbe.zErrMsg
*/
SQLITE_PRIVATE void sqlite3VdbeError(Vdbe *p, const char *zFormat, ...){
@@ -75891,31 +84394,68 @@ SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, u8 prepFlag
}
assert( p->zSql==0 );
p->zSql = sqlite3DbStrNDup(p->db, z, n);
+}
+
#ifdef SQLITE_ENABLE_NORMALIZE
- assert( p->zNormSql==0 );
- if( p->zSql && (prepFlags & SQLITE_PREPARE_NORMALIZE)!=0 ){
- sqlite3Normalize(p, p->zSql, n, prepFlags);
- assert( p->zNormSql!=0 || p->db->mallocFailed );
+/*
+** Add a new element to the Vdbe->pDblStr list.
+*/
+SQLITE_PRIVATE void sqlite3VdbeAddDblquoteStr(sqlite3 *db, Vdbe *p, const char *z){
+ if( p ){
+ int n = sqlite3Strlen30(z);
+ DblquoteStr *pStr = sqlite3DbMallocRawNN(db,
+ sizeof(*pStr)+n+1-sizeof(pStr->z));
+ if( pStr ){
+ pStr->pNextStr = p->pDblStr;
+ p->pDblStr = pStr;
+ memcpy(pStr->z, z, n+1);
+ }
}
+}
#endif
+
+#ifdef SQLITE_ENABLE_NORMALIZE
+/*
+** zId of length nId is a double-quoted identifier. Check to see if
+** that identifier is really used as a string literal.
+*/
+SQLITE_PRIVATE int sqlite3VdbeUsesDoubleQuotedString(
+ Vdbe *pVdbe, /* The prepared statement */
+ const char *zId /* The double-quoted identifier, already dequoted */
+){
+ DblquoteStr *pStr;
+ assert( zId!=0 );
+ if( pVdbe->pDblStr==0 ) return 0;
+ for(pStr=pVdbe->pDblStr; pStr; pStr=pStr->pNextStr){
+ if( strcmp(zId, pStr->z)==0 ) return 1;
+ }
+ return 0;
}
+#endif
/*
-** Swap all content between two VDBE structures.
+** Swap byte-code between two VDBE structures.
+**
+** This happens after pB was previously run and returned
+** SQLITE_SCHEMA. The statement was then reprepared in pA.
+** This routine transfers the new bytecode in pA over to pB
+** so that pB can be run again. The old pB byte code is
+** moved back to pA so that it will be cleaned up when pA is
+** finalized.
*/
SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
- Vdbe tmp, *pTmp;
+ Vdbe tmp, *pTmp, **ppTmp;
char *zTmp;
assert( pA->db==pB->db );
tmp = *pA;
*pA = *pB;
*pB = tmp;
- pTmp = pA->pNext;
- pA->pNext = pB->pNext;
- pB->pNext = pTmp;
- pTmp = pA->pPrev;
- pA->pPrev = pB->pPrev;
- pB->pPrev = pTmp;
+ pTmp = pA->pVNext;
+ pA->pVNext = pB->pVNext;
+ pB->pVNext = pTmp;
+ ppTmp = pA->ppVPrev;
+ pA->ppVPrev = pB->ppVPrev;
+ pB->ppVPrev = ppTmp;
zTmp = pA->zSql;
pA->zSql = pB->zSql;
pB->zSql = zTmp;
@@ -75931,13 +84471,13 @@ SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
}
/*
-** Resize the Vdbe.aOp array so that it is at least nOp elements larger
+** Resize the Vdbe.aOp array so that it is at least nOp elements larger
** than its current size. nOp is guaranteed to be less than or equal
** to 1024/sizeof(Op).
**
** If an out-of-memory error occurs while resizing the array, return
-** SQLITE_NOMEM. In this case Vdbe.aOp and Parse.nOpAlloc remain
-** unchanged (this is so that any opcodes already allocated can be
+** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain
+** unchanged (this is so that any opcodes already allocated can be
** correctly deallocated along with the rest of the Vdbe).
*/
static int growOpArray(Vdbe *v, int nOp){
@@ -75945,16 +84485,18 @@ static int growOpArray(Vdbe *v, int nOp){
Parse *p = v->pParse;
/* The SQLITE_TEST_REALLOC_STRESS compile-time option is designed to force
- ** more frequent reallocs and hence provide more opportunities for
+ ** more frequent reallocs and hence provide more opportunities for
** simulated OOM faults. SQLITE_TEST_REALLOC_STRESS is generally used
** during testing only. With SQLITE_TEST_REALLOC_STRESS grow the op array
** by the minimum* amount required until the size reaches 512. Normal
** operation (without SQLITE_TEST_REALLOC_STRESS) is to double the current
** size of the op array or add 1KB of space, whichever is smaller. */
#ifdef SQLITE_TEST_REALLOC_STRESS
- int nNew = (p->nOpAlloc>=512 ? p->nOpAlloc*2 : p->nOpAlloc+nOp);
+ sqlite3_int64 nNew = (v->nOpAlloc>=512 ? 2*(sqlite3_int64)v->nOpAlloc
+ : (sqlite3_int64)v->nOpAlloc+nOp);
#else
- int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op)));
+ sqlite3_int64 nNew = (v->nOpAlloc ? 2*(sqlite3_int64)v->nOpAlloc
+ : (sqlite3_int64)(1024/sizeof(Op)));
UNUSED_PARAMETER(nOp);
#endif
@@ -75964,12 +84506,12 @@ static int growOpArray(Vdbe *v, int nOp){
return SQLITE_NOMEM;
}
- assert( nOp<=(1024/sizeof(Op)) );
- assert( nNew>=(p->nOpAlloc+nOp) );
+ assert( nOp<=(int)(1024/sizeof(Op)) );
+ assert( nNew>=(v->nOpAlloc+nOp) );
pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op));
if( pNew ){
p->szOpAlloc = sqlite3DbMallocSize(p->db, pNew);
- p->nOpAlloc = p->szOpAlloc/sizeof(Op);
+ v->nOpAlloc = p->szOpAlloc/sizeof(Op);
v->aOp = pNew;
}
return (pNew ? SQLITE_OK : SQLITE_NOMEM_BKPT);
@@ -75978,15 +84520,54 @@ static int growOpArray(Vdbe *v, int nOp){
#ifdef SQLITE_DEBUG
/* This routine is just a convenient place to set a breakpoint that will
** fire after each opcode is inserted and displayed using
-** "PRAGMA vdbe_addoptrace=on".
-*/
-static void test_addop_breakpoint(void){
- static int n = 0;
+** "PRAGMA vdbe_addoptrace=on". Parameters "pc" (program counter) and
+** pOp are available to make the breakpoint conditional.
+**
+** Other useful labels for breakpoints include:
+** test_trace_breakpoint(pc,pOp)
+** sqlite3CorruptError(lineno)
+** sqlite3MisuseError(lineno)
+** sqlite3CantopenError(lineno)
+*/
+static void test_addop_breakpoint(int pc, Op *pOp){
+ static u64 n = 0;
+ (void)pc;
+ (void)pOp;
n++;
+ if( n==LARGEST_UINT64 ) abort(); /* so that n is used, preventing a warning */
}
#endif
/*
+** Slow paths for sqlite3VdbeAddOp3() and sqlite3VdbeAddOp4Int() for the
+** unusual case when we need to increase the size of the Vdbe.aOp[] array
+** before adding the new opcode.
+*/
+static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){
+ assert( p->nOpAlloc<=p->nOp );
+ if( growOpArray(p, 1) ) return 1;
+ assert( p->nOpAlloc>p->nOp );
+ return sqlite3VdbeAddOp3(p, op, p1, p2, p3);
+}
+static SQLITE_NOINLINE int addOp4IntSlow(
+ Vdbe *p, /* Add the opcode to this VM */
+ int op, /* The new opcode */
+ int p1, /* The P1 operand */
+ int p2, /* The P2 operand */
+ int p3, /* The P3 operand */
+ int p4 /* The P4 operand as an integer */
+){
+ int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3);
+ if( p->db->mallocFailed==0 ){
+ VdbeOp *pOp = &p->aOp[addr];
+ pOp->p4type = P4_INT32;
+ pOp->p4.i = p4;
+ }
+ return addr;
+}
+
+
+/*
** Add a new instruction to the list of instructions current in the
** VDBE. Return the address of the new instruction.
**
@@ -75996,30 +84577,31 @@ static void test_addop_breakpoint(void){
**
** op The opcode for this instruction
**
-** p1, p2, p3 Operands
-**
-** Use the sqlite3VdbeResolveLabel() function to fix an address and
-** the sqlite3VdbeChangeP4() function to change the value of the P4
-** operand.
+** p1, p2, p3, p4 Operands
*/
-static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){
- assert( p->pParse->nOpAlloc<=p->nOp );
- if( growOpArray(p, 1) ) return 1;
- assert( p->pParse->nOpAlloc>p->nOp );
- return sqlite3VdbeAddOp3(p, op, p1, p2, p3);
+SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){
+ return sqlite3VdbeAddOp3(p, op, 0, 0, 0);
+}
+SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){
+ return sqlite3VdbeAddOp3(p, op, p1, 0, 0);
+}
+SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){
+ return sqlite3VdbeAddOp3(p, op, p1, p2, 0);
}
SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
int i;
VdbeOp *pOp;
i = p->nOp;
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->eVdbeState==VDBE_INIT_STATE );
assert( op>=0 && op<0xff );
- if( p->pParse->nOpAlloc<=i ){
+ if( p->nOpAlloc<=i ){
return growOp3(p, op, p1, p2, p3);
}
+ assert( p->aOp!=0 );
p->nOp++;
pOp = &p->aOp[i];
+ assert( pOp!=0 );
pOp->opcode = (u8)op;
pOp->p5 = 0;
pOp->p1 = p1;
@@ -76027,32 +84609,78 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
pOp->p3 = p3;
pOp->p4.p = 0;
pOp->p4type = P4_NOTUSED;
+
+ /* Replicate this logic in sqlite3VdbeAddOp4Int()
+ ** vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
pOp->zComment = 0;
#endif
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
+ pOp->nExec = 0;
+ pOp->nCycle = 0;
+#endif
#ifdef SQLITE_DEBUG
if( p->db->flags & SQLITE_VdbeAddopTrace ){
sqlite3VdbePrintOp(0, i, &p->aOp[i]);
- test_addop_breakpoint();
+ test_addop_breakpoint(i, &p->aOp[i]);
}
#endif
-#ifdef VDBE_PROFILE
- pOp->cycles = 0;
- pOp->cnt = 0;
-#endif
#ifdef SQLITE_VDBE_COVERAGE
pOp->iSrcLine = 0;
#endif
+ /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ ** Replicate in sqlite3VdbeAddOp4Int() */
+
return i;
}
-SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){
- return sqlite3VdbeAddOp3(p, op, 0, 0, 0);
-}
-SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe *p, int op, int p1){
- return sqlite3VdbeAddOp3(p, op, p1, 0, 0);
-}
-SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){
- return sqlite3VdbeAddOp3(p, op, p1, p2, 0);
+SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(
+ Vdbe *p, /* Add the opcode to this VM */
+ int op, /* The new opcode */
+ int p1, /* The P1 operand */
+ int p2, /* The P2 operand */
+ int p3, /* The P3 operand */
+ int p4 /* The P4 operand as an integer */
+){
+ int i;
+ VdbeOp *pOp;
+
+ i = p->nOp;
+ if( p->nOpAlloc<=i ){
+ return addOp4IntSlow(p, op, p1, p2, p3, p4);
+ }
+ p->nOp++;
+ pOp = &p->aOp[i];
+ assert( pOp!=0 );
+ pOp->opcode = (u8)op;
+ pOp->p5 = 0;
+ pOp->p1 = p1;
+ pOp->p2 = p2;
+ pOp->p3 = p3;
+ pOp->p4.i = p4;
+ pOp->p4type = P4_INT32;
+
+ /* Replicate this logic in sqlite3VdbeAddOp3()
+ ** vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ pOp->zComment = 0;
+#endif
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
+ pOp->nExec = 0;
+ pOp->nCycle = 0;
+#endif
+#ifdef SQLITE_DEBUG
+ if( p->db->flags & SQLITE_VdbeAddopTrace ){
+ sqlite3VdbePrintOp(0, i, &p->aOp[i]);
+ test_addop_breakpoint(i, &p->aOp[i]);
+ }
+#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ pOp->iSrcLine = 0;
+#endif
+ /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ ** Replicate in sqlite3VdbeAddOp3() */
+
+ return i;
}
/* Generate code for an unconditional jump to instruction iDest
@@ -76117,6 +84745,50 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp4(
}
/*
+** Add an OP_Function or OP_PureFunc opcode.
+**
+** The eCallCtx argument is information (typically taken from Expr.op2)
+** that describes the calling context of the function. 0 means a general
+** function call. NC_IsCheck means called by a check constraint,
+** NC_IdxExpr means called as part of an index expression. NC_PartIdx
+** means in the WHERE clause of a partial index. NC_GenCol means called
+** while computing a generated column value. 0 is the usual case.
+*/
+SQLITE_PRIVATE int sqlite3VdbeAddFunctionCall(
+ Parse *pParse, /* Parsing context */
+ int p1, /* Constant argument mask */
+ int p2, /* First argument register */
+ int p3, /* Register into which results are written */
+ int nArg, /* Number of argument */
+ const FuncDef *pFunc, /* The function to be invoked */
+ int eCallCtx /* Calling context */
+){
+ Vdbe *v = pParse->pVdbe;
+ int nByte;
+ int addr;
+ sqlite3_context *pCtx;
+ assert( v );
+ nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlite3_value*);
+ pCtx = sqlite3DbMallocRawNN(pParse->db, nByte);
+ if( pCtx==0 ){
+ assert( pParse->db->mallocFailed );
+ freeEphemeralFunction(pParse->db, (FuncDef*)pFunc);
+ return 0;
+ }
+ pCtx->pOut = 0;
+ pCtx->pFunc = (FuncDef*)pFunc;
+ pCtx->pVdbe = 0;
+ pCtx->isError = 0;
+ pCtx->argc = nArg;
+ pCtx->iOp = sqlite3VdbeCurrentAddr(v);
+ addr = sqlite3VdbeAddOp4(v, eCallCtx ? OP_PureFunc : OP_Function,
+ p1, p2, p3, (char*)pCtx, P4_FUNCCTX);
+ sqlite3VdbeChangeP5(v, eCallCtx & NC_SelfRef);
+ sqlite3MayAbort(pParse);
+ return addr;
+}
+
+/*
** Add an opcode that includes the p4 value with a P4_INT64 or
** P4_REAL type.
*/
@@ -76147,13 +84819,30 @@ SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse *pParse){
}
/*
+** Set a debugger breakpoint on the following routine in order to
+** monitor the EXPLAIN QUERY PLAN code generation.
+*/
+#if defined(SQLITE_DEBUG)
+SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char *z1, const char *z2){
+ (void)z1;
+ (void)z2;
+}
+#endif
+
+/*
** Add a new OP_Explain opcode.
**
** If the bPush flag is true, then make this opcode the parent for
** subsequent Explains until sqlite3VdbeExplainPop() is called.
*/
-SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
- if( pParse->explain==2 ){
+SQLITE_PRIVATE int sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
+ int addr = 0;
+#if !defined(SQLITE_DEBUG)
+ /* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined.
+ ** But omit them (for performance) during production builds */
+ if( pParse->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
+#endif
+ {
char *zMsg;
Vdbe *v;
va_list ap;
@@ -76163,16 +84852,22 @@ SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt
va_end(ap);
v = pParse->pVdbe;
iThis = v->nOp;
- sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0,
+ addr = sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0,
zMsg, P4_DYNAMIC);
- if( bPush) pParse->addrExplain = iThis;
+ sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetLastOp(v)->p4.z);
+ if( bPush){
+ pParse->addrExplain = iThis;
+ }
+ sqlite3VdbeScanStatus(v, iThis, -1, -1, 0, 0);
}
+ return addr;
}
/*
** Pop the EXPLAIN QUERY PLAN stack one level.
*/
SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse *pParse){
+ sqlite3ExplainBreakpoint("POP", 0);
pParse->addrExplain = sqlite3VdbeExplainParent(pParse);
}
#endif /* SQLITE_OMIT_EXPLAIN */
@@ -76185,30 +84880,12 @@ SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse *pParse){
** The zWhere string must have been obtained from sqlite3_malloc().
** This routine will take ownership of the allocated memory.
*/
-SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
+SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere, u16 p5){
int j;
sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
+ sqlite3VdbeChangeP5(p, p5);
for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
-}
-
-/*
-** Add an opcode that includes the p4 value as an integer.
-*/
-SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(
- Vdbe *p, /* Add the opcode to this VM */
- int op, /* The new opcode */
- int p1, /* The P1 operand */
- int p2, /* The P2 operand */
- int p3, /* The P3 operand */
- int p4 /* The P4 operand as an integer */
-){
- int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3);
- if( p->db->mallocFailed==0 ){
- VdbeOp *pOp = &p->aOp[addr];
- pOp->p4type = P4_INT32;
- pOp->p4.i = p4;
- }
- return addr;
+ sqlite3MayAbort(p->pParse);
}
/* Insert the end of a co-routine
@@ -76237,21 +84914,22 @@ SQLITE_PRIVATE void sqlite3VdbeEndCoroutine(Vdbe *v, int regYield){
** The VDBE knows that a P2 value is a label because labels are
** always negative and P2 values are suppose to be non-negative.
** Hence, a negative P2 value is a label that has yet to be resolved.
+** (Later:) This is only true for opcodes that have the OPFLG_JUMP
+** property.
+**
+** Variable usage notes:
**
-** Zero is returned if a malloc() fails.
+** Parse.aLabel[x] Stores the address that the x-th label resolves
+** into. For testing (SQLITE_DEBUG), unresolved
+** labels stores -1, but that is not required.
+** Parse.nLabelAlloc Number of slots allocated to Parse.aLabel[]
+** Parse.nLabel The *negative* of the number of labels that have
+** been issued. The negative is stored because
+** that gives a performance improvement over storing
+** the equivalent positive value.
*/
-SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){
- Parse *p = v->pParse;
- int i = p->nLabel++;
- assert( v->magic==VDBE_MAGIC_INIT );
- if( (i & (i-1))==0 ){
- p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel,
- (i*2+1)*sizeof(p->aLabel[0]));
- }
- if( p->aLabel ){
- p->aLabel[i] = -1;
- }
- return ADDR(i);
+SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Parse *pParse){
+ return --pParse->nLabel;
}
/*
@@ -76259,18 +84937,38 @@ SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){
** be inserted. The parameter "x" must have been obtained from
** a prior call to sqlite3VdbeMakeLabel().
*/
+static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){
+ int nNewSize = 10 - p->nLabel;
+ p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel,
+ nNewSize*sizeof(p->aLabel[0]));
+ if( p->aLabel==0 ){
+ p->nLabelAlloc = 0;
+ }else{
+#ifdef SQLITE_DEBUG
+ int i;
+ for(i=p->nLabelAlloc; i<nNewSize; i++) p->aLabel[i] = -1;
+#endif
+ if( nNewSize>=100 && (nNewSize/100)>(p->nLabelAlloc/100) ){
+ sqlite3ProgressCheck(p);
+ }
+ p->nLabelAlloc = nNewSize;
+ p->aLabel[j] = v->nOp;
+ }
+}
SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){
Parse *p = v->pParse;
int j = ADDR(x);
- assert( v->magic==VDBE_MAGIC_INIT );
- assert( j<p->nLabel );
+ assert( v->eVdbeState==VDBE_INIT_STATE );
+ assert( j<-p->nLabel );
assert( j>=0 );
- if( p->aLabel ){
#ifdef SQLITE_DEBUG
- if( p->db->flags & SQLITE_VdbeAddopTrace ){
- printf("RESOLVE LABEL %d to %d\n", x, v->nOp);
- }
+ if( p->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("RESOLVE LABEL %d to %d\n", x, v->nOp);
+ }
#endif
+ if( p->nLabelAlloc + p->nLabel < 0 ){
+ resizeResolveLabel(p,v,j);
+ }else{
assert( p->aLabel[j]==(-1) ); /* Labels may only be resolved once */
p->aLabel[j] = v->nOp;
}
@@ -76280,33 +84978,39 @@ SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){
** Mark the VDBE as one that can only be run one time.
*/
SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe *p){
- p->runOnlyOnce = 1;
+ sqlite3VdbeAddOp2(p, OP_Expire, 1, 1);
}
/*
-** Mark the VDBE as one that can only be run multiple times.
+** Mark the VDBE as one that can be run multiple times.
*/
SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe *p){
- p->runOnlyOnce = 0;
+ int i;
+ for(i=1; ALWAYS(i<p->nOp); i++){
+ if( ALWAYS(p->aOp[i].opcode==OP_Expire) ){
+ p->aOp[1].opcode = OP_Noop;
+ break;
+ }
+ }
}
#ifdef SQLITE_DEBUG /* sqlite3AssertMayAbort() logic */
/*
** The following type and function are used to iterate through all opcodes
-** in a Vdbe main program and each of the sub-programs (triggers) it may
+** in a Vdbe main program and each of the sub-programs (triggers) it may
** invoke directly or indirectly. It should be used as follows:
**
** Op *pOp;
** VdbeOpIter sIter;
**
** memset(&sIter, 0, sizeof(sIter));
-** sIter.v = v; // v is of type Vdbe*
+** sIter.v = v; // v is of type Vdbe*
** while( (pOp = opIterNext(&sIter)) ){
** // Do something with pOp
** }
** sqlite3DbFree(v->db, sIter.apSub);
-**
+**
*/
typedef struct VdbeOpIter VdbeOpIter;
struct VdbeOpIter {
@@ -76339,7 +85043,7 @@ static Op *opIterNext(VdbeOpIter *p){
p->iSub++;
p->iAddr = 0;
}
-
+
if( pRet->p4type==P4_SUBPROGRAM ){
int nByte = (p->nSub+1)*sizeof(SubProgram*);
int j;
@@ -76370,9 +85074,10 @@ static Op *opIterNext(VdbeOpIter *p){
** * OP_HaltIfNull with P1=SQLITE_CONSTRAINT and P2=OE_Abort.
** * OP_Destroy
** * OP_VUpdate
+** * OP_VCreate
** * OP_VRename
** * OP_FkCounter with P2==0 (immediate foreign key constraint)
-** * OP_CreateBtree/BTREE_INTKEY and OP_InitCoroutine
+** * OP_CreateBtree/BTREE_INTKEY and OP_InitCoroutine
** (for CREATE TABLE AS SELECT ...)
**
** Then check that the value of Parse.mayAbort is true if an
@@ -76386,22 +85091,37 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
int hasAbort = 0;
int hasFkCounter = 0;
int hasCreateTable = 0;
+ int hasCreateIndex = 0;
int hasInitCoroutine = 0;
Op *pOp;
VdbeOpIter sIter;
+
+ if( v==0 ) return 0;
memset(&sIter, 0, sizeof(sIter));
sIter.v = v;
while( (pOp = opIterNext(&sIter))!=0 ){
int opcode = pOp->opcode;
- if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename
- || ((opcode==OP_Halt || opcode==OP_HaltIfNull)
- && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort))
+ if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename
+ || opcode==OP_VDestroy
+ || opcode==OP_VCreate
+ || opcode==OP_ParseSchema
+ || opcode==OP_Function || opcode==OP_PureFunc
+ || ((opcode==OP_Halt || opcode==OP_HaltIfNull)
+ && ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort))
){
hasAbort = 1;
break;
}
if( opcode==OP_CreateBtree && pOp->p3==BTREE_INTKEY ) hasCreateTable = 1;
+ if( mayAbort ){
+ /* hasCreateIndex may also be set for some DELETE statements that use
+ ** OP_Clear. So this routine may end up returning true in the case
+ ** where a "DELETE FROM tbl" has a statement-journal but does not
+ ** require one. This is not so bad - it is an inefficiency, not a bug. */
+ if( opcode==OP_CreateBtree && pOp->p3==BTREE_BLOBKEY ) hasCreateIndex = 1;
+ if( opcode==OP_Clear ) hasCreateIndex = 1;
+ }
if( opcode==OP_InitCoroutine ) hasInitCoroutine = 1;
#ifndef SQLITE_OMIT_FOREIGN_KEY
if( opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1 ){
@@ -76417,7 +85137,8 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
** true for this case to prevent the assert() in the callers frame
** from failing. */
return ( v->db->mallocFailed || hasAbort==mayAbort || hasFkCounter
- || (hasCreateTable && hasInitCoroutine) );
+ || (hasCreateTable && hasInitCoroutine) || hasCreateIndex
+ );
}
#endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */
@@ -76460,7 +85181,7 @@ SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe *p){
** (3) Update the Vdbe.readOnly and Vdbe.bIsReader flags to accurately
** indicate what the prepared statement actually does.
**
-** (4) Initialize the p4.xAdvance pointer on opcodes that use it.
+** (4) (discontinued)
**
** (5) Reclaim the memory allocated for storing labels.
**
@@ -76473,11 +85194,13 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
Op *pOp;
Parse *pParse = p->pParse;
int *aLabel = pParse->aLabel;
+
+ assert( pParse->db->mallocFailed==0 ); /* tag-20230419-1 */
p->readOnly = 1;
p->bIsReader = 0;
pOp = &p->aOp[p->nOp-1];
- while(1){
-
+ assert( p->aOp[0].opcode==OP_Init );
+ while( 1 /* Loop terminates when it reaches the OP_Init opcode */ ){
/* Only JUMP opcodes and the short list of special opcodes in the switch
** below need to be considered. The mkopcodeh.tcl generator script groups
** all these opcodes together near the front of the opcode list. Skip
@@ -76490,7 +85213,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
switch( pOp->opcode ){
case OP_Transaction: {
if( pOp->p2!=0 ) p->readOnly = 0;
- /* fall thru */
+ /* no break */ deliberate_fall_through
}
case OP_AutoCommit:
case OP_Savepoint: {
@@ -76506,24 +85229,9 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
p->bIsReader = 1;
break;
}
- case OP_Next:
- case OP_SorterNext: {
- pOp->p4.xAdvance = sqlite3BtreeNext;
- pOp->p4type = P4_ADVANCE;
- /* The code generator never codes any of these opcodes as a jump
- ** to a label. They are always coded as a jump backwards to a
- ** known address */
+ case OP_Init: {
assert( pOp->p2>=0 );
- break;
- }
- case OP_Prev: {
- pOp->p4.xAdvance = sqlite3BtreePrevious;
- pOp->p4type = P4_ADVANCE;
- /* The code generator never codes any of these opcodes as a jump
- ** to a label. They are always coded as a jump backwards to a
- ** known address */
- assert( pOp->p2>=0 );
- break;
+ goto resolve_p2_values_loop_exit;
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
case OP_VUpdate: {
@@ -76537,6 +85245,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
n = pOp[-1].p1;
if( n>nMaxArgs ) nMaxArgs = n;
/* Fall through into the default case */
+ /* no break */ deliberate_fall_through
}
#endif
default: {
@@ -76545,7 +85254,8 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
** have non-negative values for P2. */
assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 );
- assert( ADDR(pOp->p2)<pParse->nLabel );
+ assert( ADDR(pOp->p2)<-pParse->nLabel );
+ assert( aLabel!=0 ); /* True because of tag-20230419-1 */
pOp->p2 = aLabel[ADDR(pOp->p2)];
}
break;
@@ -76556,21 +85266,112 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
** have non-negative values for P2. */
assert( (sqlite3OpcodeProperty[pOp->opcode]&OPFLG_JUMP)==0 || pOp->p2>=0);
}
- if( pOp==p->aOp ) break;
+ assert( pOp>p->aOp );
pOp--;
}
- sqlite3DbFree(p->db, pParse->aLabel);
- pParse->aLabel = 0;
+resolve_p2_values_loop_exit:
+ if( aLabel ){
+ sqlite3DbNNFreeNN(p->db, pParse->aLabel);
+ pParse->aLabel = 0;
+ }
pParse->nLabel = 0;
*pMaxFuncArgs = nMaxArgs;
assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) );
}
+#ifdef SQLITE_DEBUG
+/*
+** Check to see if a subroutine contains a jump to a location outside of
+** the subroutine. If a jump outside the subroutine is detected, add code
+** that will cause the program to halt with an error message.
+**
+** The subroutine consists of opcodes between iFirst and iLast. Jumps to
+** locations within the subroutine are acceptable. iRetReg is a register
+** that contains the return address. Jumps to outside the range of iFirst
+** through iLast are also acceptable as long as the jump destination is
+** an OP_Return to iReturnAddr.
+**
+** A jump to an unresolved label means that the jump destination will be
+** beyond the current address. That is normally a jump to an early
+** termination and is consider acceptable.
+**
+** This routine only runs during debug builds. The purpose is (of course)
+** to detect invalid escapes out of a subroutine. The OP_Halt opcode
+** is generated rather than an assert() or other error, so that ".eqp full"
+** will still work to show the original bytecode, to aid in debugging.
+*/
+SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn(
+ Vdbe *v, /* The byte-code program under construction */
+ int iFirst, /* First opcode of the subroutine */
+ int iLast, /* Last opcode of the subroutine */
+ int iRetReg /* Subroutine return address register */
+){
+ VdbeOp *pOp;
+ Parse *pParse;
+ int i;
+ sqlite3_str *pErr = 0;
+ assert( v!=0 );
+ pParse = v->pParse;
+ assert( pParse!=0 );
+ if( pParse->nErr ) return;
+ assert( iLast>=iFirst );
+ assert( iLast<v->nOp );
+ pOp = &v->aOp[iFirst];
+ for(i=iFirst; i<=iLast; i++, pOp++){
+ if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ){
+ int iDest = pOp->p2; /* Jump destination */
+ if( iDest==0 ) continue;
+ if( pOp->opcode==OP_Gosub ) continue;
+ if( pOp->p3==20230325 && pOp->opcode==OP_NotNull ){
+ /* This is a deliberately taken illegal branch. tag-20230325-2 */
+ continue;
+ }
+ if( iDest<0 ){
+ int j = ADDR(iDest);
+ assert( j>=0 );
+ if( j>=-pParse->nLabel || pParse->aLabel[j]<0 ){
+ continue;
+ }
+ iDest = pParse->aLabel[j];
+ }
+ if( iDest<iFirst || iDest>iLast ){
+ int j = iDest;
+ for(; j<v->nOp; j++){
+ VdbeOp *pX = &v->aOp[j];
+ if( pX->opcode==OP_Return ){
+ if( pX->p1==iRetReg ) break;
+ continue;
+ }
+ if( pX->opcode==OP_Noop ) continue;
+ if( pX->opcode==OP_Explain ) continue;
+ if( pErr==0 ){
+ pErr = sqlite3_str_new(0);
+ }else{
+ sqlite3_str_appendchar(pErr, 1, '\n');
+ }
+ sqlite3_str_appendf(pErr,
+ "Opcode at %d jumps to %d which is outside the "
+ "subroutine at %d..%d",
+ i, iDest, iFirst, iLast);
+ break;
+ }
+ }
+ }
+ }
+ if( pErr ){
+ char *zErr = sqlite3_str_finish(pErr);
+ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_INTERNAL, OE_Abort, 0, zErr, 0);
+ sqlite3_free(zErr);
+ sqlite3MayAbort(pParse);
+ }
+}
+#endif /* SQLITE_DEBUG */
+
/*
** Return the address of the next instruction to be inserted.
*/
SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe *p){
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->eVdbeState==VDBE_INIT_STATE );
return p->nOp;
}
@@ -76584,7 +85385,7 @@ SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe *p){
*/
#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
SQLITE_PRIVATE void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){
- assert( p->nOp + N <= p->pParse->nOpAlloc );
+ assert( p->nOp + N <= p->nOpAlloc );
}
#endif
@@ -76618,12 +85419,12 @@ SQLITE_PRIVATE void sqlite3VdbeVerifyAbortable(Vdbe *p, int onError){
/*
** This function returns a pointer to the array of opcodes associated with
** the Vdbe passed as the first argument. It is the callers responsibility
-** to arrange for the returned array to be eventually freed using the
+** to arrange for the returned array to be eventually freed using the
** vdbeFreeOpArray() function.
**
** Before returning, *pnOp is set to the number of entries in the returned
-** array. Also, *pnMaxArg is set to the larger of its current value and
-** the number of entries in the Vdbe.apArg[] array required to execute the
+** array. Also, *pnMaxArg is set to the larger of its current value and
+** the number of entries in the Vdbe.apArg[] array required to execute the
** returned program.
*/
SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){
@@ -76655,8 +85456,8 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(
int i;
VdbeOp *pOut, *pFirst;
assert( nOp>0 );
- assert( p->magic==VDBE_MAGIC_INIT );
- if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p, nOp) ){
+ assert( p->eVdbeState==VDBE_INIT_STATE );
+ if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){
return 0;
}
pFirst = pOut = &p->aOp[p->nOp];
@@ -76697,41 +85498,108 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(
SQLITE_PRIVATE void sqlite3VdbeScanStatus(
Vdbe *p, /* VM to add scanstatus() to */
int addrExplain, /* Address of OP_Explain (or 0) */
- int addrLoop, /* Address of loop counter */
+ int addrLoop, /* Address of loop counter */
int addrVisit, /* Address of rows visited counter */
LogEst nEst, /* Estimated number of output rows */
const char *zName /* Name of table or index being scanned */
){
- int nByte = (p->nScan+1) * sizeof(ScanStatus);
- ScanStatus *aNew;
- aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte);
- if( aNew ){
- ScanStatus *pNew = &aNew[p->nScan++];
- pNew->addrExplain = addrExplain;
- pNew->addrLoop = addrLoop;
- pNew->addrVisit = addrVisit;
- pNew->nEst = nEst;
- pNew->zName = sqlite3DbStrDup(p->db, zName);
- p->aScan = aNew;
+ if( IS_STMT_SCANSTATUS(p->db) ){
+ sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus);
+ ScanStatus *aNew;
+ aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte);
+ if( aNew ){
+ ScanStatus *pNew = &aNew[p->nScan++];
+ memset(pNew, 0, sizeof(ScanStatus));
+ pNew->addrExplain = addrExplain;
+ pNew->addrLoop = addrLoop;
+ pNew->addrVisit = addrVisit;
+ pNew->nEst = nEst;
+ pNew->zName = sqlite3DbStrDup(p->db, zName);
+ p->aScan = aNew;
+ }
+ }
+}
+
+/*
+** Add the range of instructions from addrStart to addrEnd (inclusive) to
+** the set of those corresponding to the sqlite3_stmt_scanstatus() counters
+** associated with the OP_Explain instruction at addrExplain. The
+** sum of the sqlite3Hwtime() values for each of these instructions
+** will be returned for SQLITE_SCANSTAT_NCYCLE requests.
+*/
+SQLITE_PRIVATE void sqlite3VdbeScanStatusRange(
+ Vdbe *p,
+ int addrExplain,
+ int addrStart,
+ int addrEnd
+){
+ if( IS_STMT_SCANSTATUS(p->db) ){
+ ScanStatus *pScan = 0;
+ int ii;
+ for(ii=p->nScan-1; ii>=0; ii--){
+ pScan = &p->aScan[ii];
+ if( pScan->addrExplain==addrExplain ) break;
+ pScan = 0;
+ }
+ if( pScan ){
+ if( addrEnd<0 ) addrEnd = sqlite3VdbeCurrentAddr(p)-1;
+ for(ii=0; ii<ArraySize(pScan->aAddrRange); ii+=2){
+ if( pScan->aAddrRange[ii]==0 ){
+ pScan->aAddrRange[ii] = addrStart;
+ pScan->aAddrRange[ii+1] = addrEnd;
+ break;
+ }
+ }
+ }
}
}
-#endif
+
+/*
+** Set the addresses for the SQLITE_SCANSTAT_NLOOP and SQLITE_SCANSTAT_NROW
+** counters for the query element associated with the OP_Explain at
+** addrExplain.
+*/
+SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters(
+ Vdbe *p,
+ int addrExplain,
+ int addrLoop,
+ int addrVisit
+){
+ if( IS_STMT_SCANSTATUS(p->db) ){
+ ScanStatus *pScan = 0;
+ int ii;
+ for(ii=p->nScan-1; ii>=0; ii--){
+ pScan = &p->aScan[ii];
+ if( pScan->addrExplain==addrExplain ) break;
+ pScan = 0;
+ }
+ if( pScan ){
+ if( addrLoop>0 ) pScan->addrLoop = addrLoop;
+ if( addrVisit>0 ) pScan->addrVisit = addrVisit;
+ }
+ }
+}
+#endif /* defined(SQLITE_ENABLE_STMT_SCANSTATUS) */
/*
** Change the value of the opcode, or P1, P2, P3, or P5 operands
** for a specific instruction.
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, u32 addr, u8 iNewOpcode){
+SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, int addr, u8 iNewOpcode){
+ assert( addr>=0 );
sqlite3VdbeGetOp(p,addr)->opcode = iNewOpcode;
}
-SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, u32 addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){
+ assert( addr>=0 );
sqlite3VdbeGetOp(p,addr)->p1 = val;
}
-SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
+ assert( addr>=0 || p->db->mallocFailed );
sqlite3VdbeGetOp(p,addr)->p2 = val;
}
-SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){
+ assert( addr>=0 );
sqlite3VdbeGetOp(p,addr)->p3 = val;
}
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){
@@ -76740,6 +85608,18 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){
}
/*
+** If the previous opcode is an OP_Column that delivers results
+** into register iDest, then add the OPFLAG_TYPEOFARG flag to that
+** opcode.
+*/
+SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe *p, int iDest){
+ VdbeOp *pOp = sqlite3VdbeGetLastOp(p);
+ if( pOp->p3==iDest && pOp->opcode==OP_Column ){
+ pOp->p5 |= OPFLAG_TYPEOFARG;
+ }
+}
+
+/*
** Change the P2 operand of instruction addr so that it points to
** the address of the next instruction to be coded.
*/
@@ -76747,29 +85627,57 @@ SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){
sqlite3VdbeChangeP2(p, addr, p->nOp);
}
+/*
+** Change the P2 operand of the jump instruction at addr so that
+** the jump lands on the next opcode. Or if the jump instruction was
+** the previous opcode (and is thus a no-op) then simply back up
+** the next instruction counter by one slot so that the jump is
+** overwritten by the next inserted opcode.
+**
+** This routine is an optimization of sqlite3VdbeJumpHere() that
+** strives to omit useless byte-code like this:
+**
+** 7 Once 0 8 0
+** 8 ...
+*/
+SQLITE_PRIVATE void sqlite3VdbeJumpHereOrPopInst(Vdbe *p, int addr){
+ if( addr==p->nOp-1 ){
+ assert( p->aOp[addr].opcode==OP_Once
+ || p->aOp[addr].opcode==OP_If
+ || p->aOp[addr].opcode==OP_FkIfZero );
+ assert( p->aOp[addr].p4type==0 );
+#ifdef SQLITE_VDBE_COVERAGE
+ sqlite3VdbeGetLastOp(p)->iSrcLine = 0; /* Erase VdbeCoverage() macros */
+#endif
+ p->nOp--;
+ }else{
+ sqlite3VdbeChangeP2(p, addr, p->nOp);
+ }
+}
+
/*
** If the input FuncDef structure is ephemeral, then free it. If
-** the FuncDef is not ephermal, then do nothing.
+** the FuncDef is not ephemeral, then do nothing.
*/
static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){
+ assert( db!=0 );
if( (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){
- sqlite3DbFreeNN(db, pDef);
+ sqlite3DbNNFreeNN(db, pDef);
}
}
-static void vdbeFreeOpArray(sqlite3 *, Op *, int);
-
/*
** Delete a P4 value if necessary.
*/
static SQLITE_NOINLINE void freeP4Mem(sqlite3 *db, Mem *p){
if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
static SQLITE_NOINLINE void freeP4FuncCtx(sqlite3 *db, sqlite3_context *p){
+ assert( db!=0 );
freeEphemeralFunction(db, p->pFunc);
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
static void freeP4(sqlite3 *db, int p4type, void *p4){
assert( db );
@@ -76781,9 +85689,8 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
case P4_REAL:
case P4_INT64:
case P4_DYNAMIC:
- case P4_DYNBLOB:
case P4_INTARRAY: {
- sqlite3DbFree(db, p4);
+ if( p4 ) sqlite3DbNNFreeNN(db, p4);
break;
}
case P4_KEYINFO: {
@@ -76812,24 +85719,32 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
break;
}
+ case P4_TABLEREF: {
+ if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4);
+ break;
+ }
}
}
/*
** Free the space allocated for aOp and any p4 values allocated for the
-** opcodes contained within. If aOp is not NULL it is assumed to contain
-** nOp entries.
+** opcodes contained within. If aOp is not NULL it is assumed to contain
+** nOp entries.
*/
static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
+ assert( nOp>=0 );
+ assert( db!=0 );
if( aOp ){
- Op *pOp;
- for(pOp=&aOp[nOp-1]; pOp>=aOp; pOp--){
+ Op *pOp = &aOp[nOp-1];
+ while(1){ /* Exit via break */
if( pOp->p4type <= P4_FREE_IF_LE ) freeP4(db, pOp->p4type, pOp->p4.p);
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
sqlite3DbFree(db, pOp->zComment);
-#endif
+#endif
+ if( pOp==aOp ) break;
+ pOp--;
}
- sqlite3DbFreeNN(db, aOp);
+ sqlite3DbNNFreeNN(db, aOp);
}
}
@@ -76844,6 +85759,13 @@ SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *pVdbe, SubProgram *p){
}
/*
+** Return true if the given Vdbe has any SubPrograms.
+*/
+SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe *pVdbe){
+ return pVdbe->pProgram!=0;
+}
+
+/*
** Change the opcode at addr into OP_Noop
*/
SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
@@ -76870,6 +85792,40 @@ SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
}
}
+#ifdef SQLITE_DEBUG
+/*
+** Generate an OP_ReleaseReg opcode to indicate that a range of
+** registers, except any identified by mask, are no longer in use.
+*/
+SQLITE_PRIVATE void sqlite3VdbeReleaseRegisters(
+ Parse *pParse, /* Parsing context */
+ int iFirst, /* Index of first register to be released */
+ int N, /* Number of registers to release */
+ u32 mask, /* Mask of registers to NOT release */
+ int bUndefine /* If true, mark registers as undefined */
+){
+ if( N==0 || OptimizationDisabled(pParse->db, SQLITE_ReleaseReg) ) return;
+ assert( pParse->pVdbe );
+ assert( iFirst>=1 );
+ assert( iFirst+N-1<=pParse->nMem );
+ if( N<=31 && mask!=0 ){
+ while( N>0 && (mask&1)!=0 ){
+ mask >>= 1;
+ iFirst++;
+ N--;
+ }
+ while( N>0 && N<=32 && (mask & MASKBIT32(N-1))!=0 ){
+ mask &= ~MASKBIT32(N-1);
+ N--;
+ }
+ }
+ if( N>0 ){
+ sqlite3VdbeAddOp3(pParse->pVdbe, OP_ReleaseReg, iFirst, N, *(int*)&mask);
+ if( bUndefine ) sqlite3VdbeChangeP5(pParse->pVdbe, 1);
+ }
+}
+#endif /* SQLITE_DEBUG */
+
/*
** Change the value of the P4 operand for a specific instruction.
** This routine is useful when a large program is loaded from a
@@ -76880,7 +85836,7 @@ SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
** the string is made into memory obtained from sqlite3_malloc().
** A value of n==0 means copy bytes of zP4 up to and including the
** first null byte. If n>0 then copy n+1 bytes of zP4.
-**
+**
** Other values of n (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points
** to a string or structure that is guaranteed to exist for the lifetime of
** the Vdbe. In these cases we can just copy the pointer.
@@ -76894,7 +85850,7 @@ static void SQLITE_NOINLINE vdbeChangeP4Full(
int n
){
if( pOp->p4type ){
- freeP4(p->db, pOp->p4type, pOp->p4.p);
+ assert( pOp->p4type > P4_FREE_IF_LE );
pOp->p4type = 0;
pOp->p4.p = 0;
}
@@ -76911,7 +85867,7 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int
sqlite3 *db;
assert( p!=0 );
db = p->db;
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->eVdbeState==VDBE_INIT_STATE );
assert( p->aOp!=0 || db->mallocFailed );
if( db->mallocFailed ){
if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4);
@@ -76941,7 +85897,7 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int
}
/*
-** Change the P4 operand of the most recently coded instruction
+** Change the P4 operand of the most recently coded instruction
** to the value defined by the arguments. This is a high-speed
** version of sqlite3VdbeChangeP4().
**
@@ -76956,7 +85912,7 @@ SQLITE_PRIVATE void sqlite3VdbeAppendP4(Vdbe *p, void *pP4, int n){
if( p->db->mallocFailed ){
freeP4(p->db, n, pP4);
}else{
- assert( pP4!=0 );
+ assert( pP4!=0 || n==P4_DYNAMIC );
assert( p->nOp>0 );
pOp = &p->aOp[p->nOp-1];
assert( pOp->p4type==P4_NOTUSED );
@@ -76987,7 +85943,7 @@ SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){
*/
static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){
assert( p->nOp>0 || p->aOp==0 );
- assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
+ assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->pParse->nErr>0 );
if( p->nOp ){
assert( p->aOp );
sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment);
@@ -77018,19 +85974,19 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
** Set the value if the iSrcLine field for the previously coded instruction.
*/
SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe *v, int iLine){
- sqlite3VdbeGetOp(v,-1)->iSrcLine = iLine;
+ sqlite3VdbeGetLastOp(v)->iSrcLine = iLine;
}
#endif /* SQLITE_VDBE_COVERAGE */
/*
-** Return the opcode for a given address. If the address is -1, then
-** return the most recently inserted opcode.
+** Return the opcode for a given address. The address must be non-negative.
+** See sqlite3VdbeGetLastOp() to get the most recently added opcode.
**
** If a memory allocation error has occurred prior to the calling of this
** routine, then a pointer to a dummy VdbeOp will be returned. That opcode
** is readable but not writable, though it is cast to a writable value.
** The return of a dummy opcode allows the call to continue functioning
-** after an OOM fault without having to check to see if the return from
+** after an OOM fault without having to check to see if the return from
** this routine is a valid pointer. But because the dummy.opcode is 0,
** dummy will never be written to. This is verified by code inspection and
** by running with Valgrind.
@@ -77039,10 +85995,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
/* C89 specifies that the constant "dummy" will be initialized to all
** zeros, which is correct. MSVC generates a warning, nevertheless. */
static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
- assert( p->magic==VDBE_MAGIC_INIT );
- if( addr<0 ){
- addr = p->nOp - 1;
- }
+ assert( p->eVdbeState==VDBE_INIT_STATE );
assert( (addr>=0 && addr<p->nOp) || p->db->mallocFailed );
if( p->db->mallocFailed ){
return (VdbeOp*)&dummy;
@@ -77051,6 +86004,12 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
}
}
+/* Return the most recently added opcode
+*/
+SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe *p){
+ return sqlite3VdbeGetOp(p, p->nOp - 1);
+}
+
#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
/*
** Return an integer value for one of the parameters to the opcode pOp
@@ -77077,78 +86036,90 @@ static int translateP(char c, const Op *pOp){
** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0
** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x
*/
-static int displayComment(
+SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(
+ sqlite3 *db, /* Optional - Oom error reporting only */
const Op *pOp, /* The opcode to be commented */
- const char *zP4, /* Previously obtained value for P4 */
- char *zTemp, /* Write result here */
- int nTemp /* Space available in zTemp[] */
+ const char *zP4 /* Previously obtained value for P4 */
){
const char *zOpName;
const char *zSynopsis;
int nOpName;
- int ii, jj;
+ int ii;
char zAlt[50];
+ StrAccum x;
+
+ sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH);
zOpName = sqlite3OpcodeName(pOp->opcode);
nOpName = sqlite3Strlen30(zOpName);
if( zOpName[nOpName+1] ){
int seenCom = 0;
char c;
- zSynopsis = zOpName += nOpName + 1;
+ zSynopsis = zOpName + nOpName + 1;
if( strncmp(zSynopsis,"IF ",3)==0 ){
- if( pOp->p5 & SQLITE_STOREP2 ){
- sqlite3_snprintf(sizeof(zAlt), zAlt, "r[P2] = (%s)", zSynopsis+3);
- }else{
- sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3);
- }
+ sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3);
zSynopsis = zAlt;
}
- for(ii=jj=0; jj<nTemp-1 && (c = zSynopsis[ii])!=0; ii++){
+ for(ii=0; (c = zSynopsis[ii])!=0; ii++){
if( c=='P' ){
c = zSynopsis[++ii];
if( c=='4' ){
- sqlite3_snprintf(nTemp-jj, zTemp+jj, "%s", zP4);
+ sqlite3_str_appendall(&x, zP4);
}else if( c=='X' ){
- sqlite3_snprintf(nTemp-jj, zTemp+jj, "%s", pOp->zComment);
- seenCom = 1;
+ if( pOp->zComment && pOp->zComment[0] ){
+ sqlite3_str_appendall(&x, pOp->zComment);
+ seenCom = 1;
+ break;
+ }
}else{
int v1 = translateP(c, pOp);
int v2;
- sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1);
if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){
ii += 3;
- jj += sqlite3Strlen30(zTemp+jj);
v2 = translateP(zSynopsis[ii], pOp);
if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){
ii += 2;
v2++;
}
- if( v2>1 ){
- sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1);
+ if( v2<2 ){
+ sqlite3_str_appendf(&x, "%d", v1);
+ }else{
+ sqlite3_str_appendf(&x, "%d..%d", v1, v1+v2-1);
+ }
+ }else if( strncmp(zSynopsis+ii+1, "@NP", 3)==0 ){
+ sqlite3_context *pCtx = pOp->p4.pCtx;
+ if( pOp->p4type!=P4_FUNCCTX || pCtx->argc==1 ){
+ sqlite3_str_appendf(&x, "%d", v1);
+ }else if( pCtx->argc>1 ){
+ sqlite3_str_appendf(&x, "%d..%d", v1, v1+pCtx->argc-1);
+ }else if( x.accError==0 ){
+ assert( x.nChar>2 );
+ x.nChar -= 2;
+ ii++;
+ }
+ ii += 3;
+ }else{
+ sqlite3_str_appendf(&x, "%d", v1);
+ if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){
+ ii += 4;
}
- }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){
- ii += 4;
}
}
- jj += sqlite3Strlen30(zTemp+jj);
}else{
- zTemp[jj++] = c;
+ sqlite3_str_appendchar(&x, 1, c);
}
}
- if( !seenCom && jj<nTemp-5 && pOp->zComment ){
- sqlite3_snprintf(nTemp-jj, zTemp+jj, "; %s", pOp->zComment);
- jj += sqlite3Strlen30(zTemp+jj);
+ if( !seenCom && pOp->zComment ){
+ sqlite3_str_appendf(&x, "; %s", pOp->zComment);
}
- if( jj<nTemp ) zTemp[jj] = 0;
}else if( pOp->zComment ){
- sqlite3_snprintf(nTemp, zTemp, "%s", pOp->zComment);
- jj = sqlite3Strlen30(zTemp);
- }else{
- zTemp[0] = 0;
- jj = 0;
+ sqlite3_str_appendall(&x, pOp->zComment);
+ }
+ if( (x.accError & SQLITE_NOMEM)!=0 && db!=0 ){
+ sqlite3OomFault(db);
}
- return jj;
+ return sqlite3StrAccumFinish(&x);
}
-#endif /* SQLITE_DEBUG */
+#endif /* SQLITE_ENABLE_EXPLAIN_COMMENTS */
#if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS)
/*
@@ -77159,6 +86130,7 @@ static void displayP4Expr(StrAccum *p, Expr *pExpr){
const char *zOp = 0;
switch( pExpr->op ){
case TK_STRING:
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3_str_appendf(p, "%Q", pExpr->u.zToken);
break;
case TK_INTEGER:
@@ -77229,23 +86201,25 @@ static void displayP4Expr(StrAccum *p, Expr *pExpr){
** Compute a string that describes the P4 parameter for an opcode.
** Use zTemp for any required temporary buffer space.
*/
-static char *displayP4(Op *pOp, char *zTemp, int nTemp){
- char *zP4 = zTemp;
+SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){
+ char *zP4 = 0;
StrAccum x;
- assert( nTemp>=20 );
- sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0);
+
+ sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH);
switch( pOp->p4type ){
case P4_KEYINFO: {
int j;
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
- assert( pKeyInfo->aSortOrder!=0 );
+ assert( pKeyInfo->aSortFlags!=0 );
sqlite3_str_appendf(&x, "k(%d", pKeyInfo->nKeyField);
for(j=0; j<pKeyInfo->nKeyField; j++){
CollSeq *pColl = pKeyInfo->aColl[j];
const char *zColl = pColl ? pColl->zName : "";
if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
- sqlite3_str_appendf(&x, ",%s%s",
- pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
+ sqlite3_str_appendf(&x, ",%s%s%s",
+ (pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_DESC) ? "-" : "",
+ (pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_BIGNULL)? "N." : "",
+ zColl);
}
sqlite3_str_append(&x, ")", 1);
break;
@@ -77257,8 +86231,11 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
}
#endif
case P4_COLLSEQ: {
+ static const char *const encnames[] = {"?", "8", "16LE", "16BE"};
CollSeq *pColl = pOp->p4.pColl;
- sqlite3_str_appendf(&x, "(%.20s)", pColl->zName);
+ assert( pColl->enc<4 );
+ sqlite3_str_appendf(&x, "%.18s-%s", pColl->zName,
+ encnames[pColl->enc]);
break;
}
case P4_FUNCDEF: {
@@ -77266,13 +86243,11 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg);
break;
}
-#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
case P4_FUNCCTX: {
FuncDef *pDef = pOp->p4.pCtx->pFunc;
sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg);
break;
}
-#endif
case P4_INT64: {
sqlite3_str_appendf(&x, "%lld", *pOp->p4.pI64);
break;
@@ -77289,7 +86264,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
Mem *pMem = pOp->p4.pMem;
if( pMem->flags & MEM_Str ){
zP4 = pMem->z;
- }else if( pMem->flags & MEM_Int ){
+ }else if( pMem->flags & (MEM_Int|MEM_IntReal) ){
sqlite3_str_appendf(&x, "%lld", pMem->u.i);
}else if( pMem->flags & MEM_Real ){
sqlite3_str_appendf(&x, "%.16g", pMem->u.r);
@@ -77309,41 +86284,33 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
}
#endif
case P4_INTARRAY: {
- int i;
- int *ai = pOp->p4.ai;
- int n = ai[0]; /* The first element of an INTARRAY is always the
+ u32 i;
+ u32 *ai = pOp->p4.ai;
+ u32 n = ai[0]; /* The first element of an INTARRAY is always the
** count of the number of elements to follow */
for(i=1; i<=n; i++){
- sqlite3_str_appendf(&x, ",%d", ai[i]);
+ sqlite3_str_appendf(&x, "%c%u", (i==1 ? '[' : ','), ai[i]);
}
- zTemp[0] = '[';
sqlite3_str_append(&x, "]", 1);
break;
}
case P4_SUBPROGRAM: {
- sqlite3_str_appendf(&x, "program");
- break;
- }
- case P4_DYNBLOB:
- case P4_ADVANCE: {
- zTemp[0] = 0;
+ zP4 = "program";
break;
}
case P4_TABLE: {
- sqlite3_str_appendf(&x, "%s", pOp->p4.pTab->zName);
+ zP4 = pOp->p4.pTab->zName;
break;
}
default: {
zP4 = pOp->p4.z;
- if( zP4==0 ){
- zP4 = zTemp;
- zTemp[0] = 0;
- }
}
}
- sqlite3StrAccumFinish(&x);
- assert( zP4!=0 );
- return zP4;
+ if( zP4 ) sqlite3_str_appendall(&x, zP4);
+ if( (x.accError & SQLITE_NOMEM)!=0 ){
+ sqlite3OomFault(db);
+ }
+ return sqlite3StrAccumFinish(&x);
}
#endif /* VDBE_DISPLAY_P4 */
@@ -77374,13 +86341,13 @@ SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe *p, int i){
**
** If SQLite is not threadsafe but does support shared-cache mode, then
** sqlite3BtreeEnter() is invoked to set the BtShared.db variables
-** of all of BtShared structures accessible via the database handle
+** of all of BtShared structures accessible via the database handle
** associated with the VM.
**
** If SQLite is not threadsafe and does not support shared-cache mode, this
** function is a no-op.
**
-** The p->btreeMask field is a bitmask of all btrees that the prepared
+** The p->btreeMask field is a bitmask of all btrees that the prepared
** statement p will ever use. Let N be the number of bits in p->btreeMask
** corresponding to btrees that use shared cache. Then the runtime of
** this routine is N*N. But as N is rarely more than 1, this should not
@@ -77433,44 +86400,69 @@ SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){
*/
SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){
char *zP4;
- char zPtr[50];
- char zCom[100];
+ char *zCom;
+ sqlite3 dummyDb;
static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n";
if( pOut==0 ) pOut = stdout;
- zP4 = displayP4(pOp, zPtr, sizeof(zPtr));
+ sqlite3BeginBenignMalloc();
+ dummyDb.mallocFailed = 1;
+ zP4 = sqlite3VdbeDisplayP4(&dummyDb, pOp);
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
- displayComment(pOp, zP4, zCom, sizeof(zCom));
+ zCom = sqlite3VdbeDisplayComment(0, pOp, zP4);
#else
- zCom[0] = 0;
+ zCom = 0;
#endif
/* NB: The sqlite3OpcodeName() function is implemented by code created
** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the
** information from the vdbe.c source text */
- fprintf(pOut, zFormat1, pc,
- sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5,
- zCom
+ fprintf(pOut, zFormat1, pc,
+ sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3,
+ zP4 ? zP4 : "", pOp->p5,
+ zCom ? zCom : ""
);
fflush(pOut);
+ sqlite3_free(zP4);
+ sqlite3_free(zCom);
+ sqlite3EndBenignMalloc();
}
#endif
/*
** Initialize an array of N Mem element.
+**
+** This is a high-runner, so only those fields that really do need to
+** be initialized are set. The Mem structure is organized so that
+** the fields that get initialized are nearby and hopefully on the same
+** cache line.
+**
+** Mem.flags = flags
+** Mem.db = db
+** Mem.szMalloc = 0
+**
+** All other fields of Mem can safely remain uninitialized for now. They
+** will be initialized before use.
*/
static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){
- while( (N--)>0 ){
- p->db = db;
- p->flags = flags;
- p->szMalloc = 0;
+ if( N>0 ){
+ do{
+ p->flags = flags;
+ p->db = db;
+ p->szMalloc = 0;
#ifdef SQLITE_DEBUG
- p->pScopyFrom = 0;
+ p->pScopyFrom = 0;
#endif
- p++;
+ p++;
+ }while( (--N)>0 );
}
}
/*
-** Release an array of N Mem elements
+** Release auxiliary memory held in an array of N Mem elements.
+**
+** After this routine returns, all Mem elements in the array will still
+** be valid. Those Mem elements that were not holding auxiliary resources
+** will be unchanged. Mem elements which had something freed will be
+** set to MEM_Undefined.
*/
static void releaseMemArray(Mem *p, int N){
if( p && N ){
@@ -77487,28 +86479,33 @@ static void releaseMemArray(Mem *p, int N){
assert( sqlite3VdbeCheckMemInvariants(p) );
/* This block is really an inlined version of sqlite3VdbeMemRelease()
- ** that takes advantage of the fact that the memory cell value is
+ ** that takes advantage of the fact that the memory cell value is
** being set to NULL after releasing any dynamic resources.
**
- ** The justification for duplicating code is that according to
- ** callgrind, this causes a certain test case to hit the CPU 4.7
- ** percent less (x86 linux, gcc version 4.1.2, -O6) than if
+ ** The justification for duplicating code is that according to
+ ** callgrind, this causes a certain test case to hit the CPU 4.7
+ ** percent less (x86 linux, gcc version 4.1.2, -O6) than if
** sqlite3MemRelease() were called from here. With -O2, this jumps
- ** to 6.6 percent. The test case is inserting 1000 rows into a table
- ** with no indexes using a single prepared INSERT statement, bind()
+ ** to 6.6 percent. The test case is inserting 1000 rows into a table
+ ** with no indexes using a single prepared INSERT statement, bind()
** and reset(). Inserts are grouped into a transaction.
*/
testcase( p->flags & MEM_Agg );
testcase( p->flags & MEM_Dyn );
- testcase( p->xDel==sqlite3VdbeFrameMemDel );
if( p->flags&(MEM_Agg|MEM_Dyn) ){
+ testcase( (p->flags & MEM_Dyn)!=0 && p->xDel==sqlite3VdbeFrameMemDel );
sqlite3VdbeMemRelease(p);
+ p->flags = MEM_Undefined;
}else if( p->szMalloc ){
- sqlite3DbFreeNN(db, p->zMalloc);
+ sqlite3DbNNFreeNN(db, p->zMalloc);
p->szMalloc = 0;
+ p->flags = MEM_Undefined;
}
-
- p->flags = MEM_Undefined;
+#ifdef SQLITE_DEBUG
+ else{
+ p->flags = MEM_Undefined;
+ }
+#endif
}while( (++p)<pEnd );
}
}
@@ -77541,6 +86538,121 @@ SQLITE_PRIVATE void sqlite3VdbeFrameMemDel(void *pArg){
pFrame->v->pDelFrame = pFrame;
}
+#if defined(SQLITE_ENABLE_BYTECODE_VTAB) || !defined(SQLITE_OMIT_EXPLAIN)
+/*
+** Locate the next opcode to be displayed in EXPLAIN or EXPLAIN
+** QUERY PLAN output.
+**
+** Return SQLITE_ROW on success. Return SQLITE_DONE if there are no
+** more opcodes to be displayed.
+*/
+SQLITE_PRIVATE int sqlite3VdbeNextOpcode(
+ Vdbe *p, /* The statement being explained */
+ Mem *pSub, /* Storage for keeping track of subprogram nesting */
+ int eMode, /* 0: normal. 1: EQP. 2: TablesUsed */
+ int *piPc, /* IN/OUT: Current rowid. Overwritten with next rowid */
+ int *piAddr, /* OUT: Write index into (*paOp)[] here */
+ Op **paOp /* OUT: Write the opcode array here */
+){
+ int nRow; /* Stop when row count reaches this */
+ int nSub = 0; /* Number of sub-vdbes seen so far */
+ SubProgram **apSub = 0; /* Array of sub-vdbes */
+ int i; /* Next instruction address */
+ int rc = SQLITE_OK; /* Result code */
+ Op *aOp = 0; /* Opcode array */
+ int iPc; /* Rowid. Copy of value in *piPc */
+
+ /* When the number of output rows reaches nRow, that means the
+ ** listing has finished and sqlite3_step() should return SQLITE_DONE.
+ ** nRow is the sum of the number of rows in the main program, plus
+ ** the sum of the number of rows in all trigger subprograms encountered
+ ** so far. The nRow value will increase as new trigger subprograms are
+ ** encountered, but p->pc will eventually catch up to nRow.
+ */
+ nRow = p->nOp;
+ if( pSub!=0 ){
+ if( pSub->flags&MEM_Blob ){
+ /* pSub is initiallly NULL. It is initialized to a BLOB by
+ ** the P4_SUBPROGRAM processing logic below */
+ nSub = pSub->n/sizeof(Vdbe*);
+ apSub = (SubProgram **)pSub->z;
+ }
+ for(i=0; i<nSub; i++){
+ nRow += apSub[i]->nOp;
+ }
+ }
+ iPc = *piPc;
+ while(1){ /* Loop exits via break */
+ i = iPc++;
+ if( i>=nRow ){
+ p->rc = SQLITE_OK;
+ rc = SQLITE_DONE;
+ break;
+ }
+ if( i<p->nOp ){
+ /* The rowid is small enough that we are still in the
+ ** main program. */
+ aOp = p->aOp;
+ }else{
+ /* We are currently listing subprograms. Figure out which one and
+ ** pick up the appropriate opcode. */
+ int j;
+ i -= p->nOp;
+ assert( apSub!=0 );
+ assert( nSub>0 );
+ for(j=0; i>=apSub[j]->nOp; j++){
+ i -= apSub[j]->nOp;
+ assert( i<apSub[j]->nOp || j+1<nSub );
+ }
+ aOp = apSub[j]->aOp;
+ }
+
+ /* When an OP_Program opcode is encounter (the only opcode that has
+ ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
+ ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
+ ** has not already been seen.
+ */
+ if( pSub!=0 && aOp[i].p4type==P4_SUBPROGRAM ){
+ int nByte = (nSub+1)*sizeof(SubProgram*);
+ int j;
+ for(j=0; j<nSub; j++){
+ if( apSub[j]==aOp[i].p4.pProgram ) break;
+ }
+ if( j==nSub ){
+ p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0);
+ if( p->rc!=SQLITE_OK ){
+ rc = SQLITE_ERROR;
+ break;
+ }
+ apSub = (SubProgram **)pSub->z;
+ apSub[nSub++] = aOp[i].p4.pProgram;
+ MemSetTypeFlag(pSub, MEM_Blob);
+ pSub->n = nSub*sizeof(SubProgram*);
+ nRow += aOp[i].p4.pProgram->nOp;
+ }
+ }
+ if( eMode==0 ) break;
+#ifdef SQLITE_ENABLE_BYTECODE_VTAB
+ if( eMode==2 ){
+ Op *pOp = aOp + i;
+ if( pOp->opcode==OP_OpenRead ) break;
+ if( pOp->opcode==OP_OpenWrite && (pOp->p5 & OPFLAG_P2ISREG)==0 ) break;
+ if( pOp->opcode==OP_ReopenIdx ) break;
+ }else
+#endif
+ {
+ assert( eMode==1 );
+ if( aOp[i].opcode==OP_Explain ) break;
+ if( aOp[i].opcode==OP_Init && iPc>1 ) break;
+ }
+ }
+ *piPc = iPc;
+ *piAddr = i;
+ *paOp = aOp;
+ return rc;
+}
+#endif /* SQLITE_ENABLE_BYTECODE_VTAB || !SQLITE_OMIT_EXPLAIN */
+
/*
** Delete a VdbeFrame object and its contents. VdbeFrame objects are
@@ -77552,7 +86664,7 @@ SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame *p){
VdbeCursor **apCsr = (VdbeCursor **)&aMem[p->nChildMem];
assert( sqlite3VdbeFrameIsValid(p) );
for(i=0; i<p->nChildCsr; i++){
- sqlite3VdbeFreeCursor(p->v, apCsr[i]);
+ if( apCsr[i] ) sqlite3VdbeFreeCursorNN(p->v, apCsr[i]);
}
releaseMemArray(aMem, p->nChildMem);
sqlite3VdbeDeleteAuxData(p->v->db, &p->pAuxData, -1, 0);
@@ -77581,19 +86693,17 @@ SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame *p){
SQLITE_PRIVATE int sqlite3VdbeList(
Vdbe *p /* The VDBE */
){
- int nRow; /* Stop when row count reaches this */
- int nSub = 0; /* Number of sub-vdbes seen so far */
- SubProgram **apSub = 0; /* Array of sub-vdbes */
Mem *pSub = 0; /* Memory cell hold array of subprogs */
sqlite3 *db = p->db; /* The database connection */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
Mem *pMem = &p->aMem[1]; /* First Mem of result set */
int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0);
- Op *pOp = 0;
+ Op *aOp; /* Array of opcodes */
+ Op *pOp; /* Current opcode */
assert( p->explain );
- assert( p->magic==VDBE_MAGIC_RUN );
+ assert( p->eVdbeState==VDBE_RUN_STATE );
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM );
/* Even though this opcode does not use dynamic strings for
@@ -77601,7 +86711,6 @@ SQLITE_PRIVATE int sqlite3VdbeList(
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
*/
releaseMemArray(pMem, 8);
- p->pResultSet = 0;
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
@@ -77610,14 +86719,6 @@ SQLITE_PRIVATE int sqlite3VdbeList(
return SQLITE_ERROR;
}
- /* When the number of output rows reaches nRow, that means the
- ** listing has finished and sqlite3_step() should return SQLITE_DONE.
- ** nRow is the sum of the number of rows in the main program, plus
- ** the sum of the number of rows in all trigger subprograms encountered
- ** so far. The nRow value will increase as new trigger subprograms are
- ** encountered, but p->pc will eventually catch up to nRow.
- */
- nRow = p->nOp;
if( bListSubprogs ){
/* The first 8 memory cells are used for the result set. So we will
** commandeer the 9th cell to use as storage for an array of pointers
@@ -77625,144 +86726,55 @@ SQLITE_PRIVATE int sqlite3VdbeList(
** cells. */
assert( p->nMem>9 );
pSub = &p->aMem[9];
- if( pSub->flags&MEM_Blob ){
- /* On the first call to sqlite3_step(), pSub will hold a NULL. It is
- ** initialized to a BLOB by the P4_SUBPROGRAM processing logic below */
- nSub = pSub->n/sizeof(Vdbe*);
- apSub = (SubProgram **)pSub->z;
- }
- for(i=0; i<nSub; i++){
- nRow += apSub[i]->nOp;
- }
+ }else{
+ pSub = 0;
}
- while(1){ /* Loop exits via break */
- i = p->pc++;
- if( i>=nRow ){
- p->rc = SQLITE_OK;
- rc = SQLITE_DONE;
- break;
- }
- if( i<p->nOp ){
- /* The output line number is small enough that we are still in the
- ** main program. */
- pOp = &p->aOp[i];
- }else{
- /* We are currently listing subprograms. Figure out which one and
- ** pick up the appropriate opcode. */
- int j;
- i -= p->nOp;
- for(j=0; i>=apSub[j]->nOp; j++){
- i -= apSub[j]->nOp;
- }
- pOp = &apSub[j]->aOp[i];
- }
-
- /* When an OP_Program opcode is encounter (the only opcode that has
- ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
- ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
- ** has not already been seen.
- */
- if( bListSubprogs && pOp->p4type==P4_SUBPROGRAM ){
- int nByte = (nSub+1)*sizeof(SubProgram*);
- int j;
- for(j=0; j<nSub; j++){
- if( apSub[j]==pOp->p4.pProgram ) break;
- }
- if( j==nSub ){
- p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0);
- if( p->rc!=SQLITE_OK ){
- rc = SQLITE_ERROR;
- break;
- }
- apSub = (SubProgram **)pSub->z;
- apSub[nSub++] = pOp->p4.pProgram;
- pSub->flags |= MEM_Blob;
- pSub->n = nSub*sizeof(SubProgram*);
- nRow += pOp->p4.pProgram->nOp;
- }
- }
- if( p->explain<2 ) break;
- if( pOp->opcode==OP_Explain ) break;
- if( pOp->opcode==OP_Init && p->pc>1 ) break;
- }
+ /* Figure out which opcode is next to display */
+ rc = sqlite3VdbeNextOpcode(p, pSub, p->explain==2, &p->pc, &i, &aOp);
if( rc==SQLITE_OK ){
- if( db->u1.isInterrupted ){
+ pOp = aOp + i;
+ if( AtomicLoad(&db->u1.isInterrupted) ){
p->rc = SQLITE_INTERRUPT;
rc = SQLITE_ERROR;
sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
}else{
- char *zP4;
- if( p->explain==1 ){
- pMem->flags = MEM_Int;
- pMem->u.i = i; /* Program counter */
- pMem++;
-
- pMem->flags = MEM_Static|MEM_Str|MEM_Term;
- pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
- assert( pMem->z!=0 );
- pMem->n = sqlite3Strlen30(pMem->z);
- pMem->enc = SQLITE_UTF8;
- pMem++;
- }
-
- pMem->flags = MEM_Int;
- pMem->u.i = pOp->p1; /* P1 */
- pMem++;
-
- pMem->flags = MEM_Int;
- pMem->u.i = pOp->p2; /* P2 */
- pMem++;
-
- pMem->flags = MEM_Int;
- pMem->u.i = pOp->p3; /* P3 */
- pMem++;
-
- if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */
- assert( p->db->mallocFailed );
- return SQLITE_ERROR;
- }
- pMem->flags = MEM_Str|MEM_Term;
- zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
- if( zP4!=pMem->z ){
- pMem->n = 0;
- sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
- }else{
- assert( pMem->z!=0 );
- pMem->n = sqlite3Strlen30(pMem->z);
- pMem->enc = SQLITE_UTF8;
- }
- pMem++;
-
- if( p->explain==1 ){
- if( sqlite3VdbeMemClearAndResize(pMem, 4) ){
- assert( p->db->mallocFailed );
- return SQLITE_ERROR;
- }
- pMem->flags = MEM_Str|MEM_Term;
- pMem->n = 2;
- sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */
- pMem->enc = SQLITE_UTF8;
- pMem++;
-
+ char *zP4 = sqlite3VdbeDisplayP4(db, pOp);
+ if( p->explain==2 ){
+ sqlite3VdbeMemSetInt64(pMem, pOp->p1);
+ sqlite3VdbeMemSetInt64(pMem+1, pOp->p2);
+ sqlite3VdbeMemSetInt64(pMem+2, pOp->p3);
+ sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlite3_free);
+ assert( p->nResColumn==4 );
+ }else{
+ sqlite3VdbeMemSetInt64(pMem+0, i);
+ sqlite3VdbeMemSetStr(pMem+1, (char*)sqlite3OpcodeName(pOp->opcode),
+ -1, SQLITE_UTF8, SQLITE_STATIC);
+ sqlite3VdbeMemSetInt64(pMem+2, pOp->p1);
+ sqlite3VdbeMemSetInt64(pMem+3, pOp->p2);
+ sqlite3VdbeMemSetInt64(pMem+4, pOp->p3);
+ /* pMem+5 for p4 is done last */
+ sqlite3VdbeMemSetInt64(pMem+6, pOp->p5);
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
- if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
- assert( p->db->mallocFailed );
- return SQLITE_ERROR;
+ {
+ char *zCom = sqlite3VdbeDisplayComment(db, pOp, zP4);
+ sqlite3VdbeMemSetStr(pMem+7, zCom, -1, SQLITE_UTF8, sqlite3_free);
}
- pMem->flags = MEM_Str|MEM_Term;
- pMem->n = displayComment(pOp, zP4, pMem->z, 500);
- pMem->enc = SQLITE_UTF8;
#else
- pMem->flags = MEM_Null; /* Comment */
+ sqlite3VdbeMemSetNull(pMem+7);
#endif
+ sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free);
+ assert( p->nResColumn==8 );
+ }
+ p->pResultRow = pMem;
+ if( db->mallocFailed ){
+ p->rc = SQLITE_NOMEM;
+ rc = SQLITE_ERROR;
+ }else{
+ p->rc = SQLITE_OK;
+ rc = SQLITE_ROW;
}
-
- p->nResColumn = 8 - 4*(p->explain-1);
- p->pResultSet = &p->aMem[1];
- p->rc = SQLITE_OK;
- rc = SQLITE_ROW;
}
}
return rc;
@@ -77823,9 +86835,9 @@ SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe *p){
** of a ReusableSpace object by the allocSpace() routine below.
*/
struct ReusableSpace {
- u8 *pSpace; /* Available memory */
- int nFree; /* Bytes of available memory */
- int nNeeded; /* Total bytes that could not be allocated */
+ u8 *pSpace; /* Available memory */
+ sqlite3_int64 nFree; /* Bytes of available memory */
+ sqlite3_int64 nNeeded; /* Total bytes that could not be allocated */
};
/* Try to allocate nByte bytes of 8-byte aligned bulk memory for pBuf
@@ -77845,11 +86857,11 @@ struct ReusableSpace {
static void *allocSpace(
struct ReusableSpace *p, /* Bulk memory available for allocation */
void *pBuf, /* Pointer to a prior allocation */
- int nByte /* Bytes of memory needed */
+ sqlite3_int64 nByte /* Bytes of memory needed. */
){
assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) );
if( pBuf==0 ){
- nByte = ROUND8(nByte);
+ nByte = ROUND8P(nByte);
if( nByte <= p->nFree ){
p->nFree -= nByte;
pBuf = &p->pSpace[p->nFree];
@@ -77866,18 +86878,19 @@ static void *allocSpace(
** running it.
*/
SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
-#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
+#if defined(SQLITE_DEBUG)
int i;
#endif
assert( p!=0 );
- assert( p->magic==VDBE_MAGIC_INIT || p->magic==VDBE_MAGIC_RESET );
+ assert( p->eVdbeState==VDBE_INIT_STATE
+ || p->eVdbeState==VDBE_READY_STATE
+ || p->eVdbeState==VDBE_HALT_STATE );
/* There should be at least one opcode.
*/
assert( p->nOp>0 );
- /* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */
- p->magic = VDBE_MAGIC_RUN;
+ p->eVdbeState = VDBE_READY_STATE;
#ifdef SQLITE_DEBUG
for(i=0; i<p->nMem; i++){
@@ -77894,8 +86907,8 @@ SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
p->nFkConstraint = 0;
#ifdef VDBE_PROFILE
for(i=0; i<p->nOp; i++){
- p->aOp[i].cnt = 0;
- p->aOp[i].cycles = 0;
+ p->aOp[i].nExec = 0;
+ p->aOp[i].nCycle = 0;
}
#endif
}
@@ -77905,11 +86918,11 @@ SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
** creating the virtual machine. This involves things such
** as allocating registers and initializing the program counter.
** After the VDBE has be prepped, it can be executed by one or more
-** calls to sqlite3VdbeExec().
+** calls to sqlite3VdbeExec().
**
** This function may be called exactly once on each virtual machine.
** After this routine is called the VM has been "packaged" and is ready
-** to run. After this routine is called, further calls to
+** to run. After this routine is called, further calls to
** sqlite3VdbeAddOp() functions are prohibited. This routine disconnects
** the Vdbe from the Parse object that helped generate it so that the
** the Vdbe becomes an independent entity and the Parse object can be
@@ -77933,15 +86946,17 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
assert( p!=0 );
assert( p->nOp>0 );
assert( pParse!=0 );
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->eVdbeState==VDBE_INIT_STATE );
assert( pParse==p->pParse );
+ p->pVList = pParse->pVList;
+ pParse->pVList = 0;
db = p->db;
assert( db->mallocFailed==0 );
nVar = pParse->nVar;
nMem = pParse->nMem;
nCursor = pParse->nTab;
nArg = pParse->nMaxArg;
-
+
/* Each cursor uses a memory cell. The first cursor (cursor 0) can
** use aMem[0] which is not otherwise used by the VDBE program. Allocate
** space at the end of aMem[] for cursors 1 and greater.
@@ -77954,7 +86969,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
** opcode array. This extra memory will be reallocated for other elements
** of the prepared statement.
*/
- n = ROUND8(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */
+ n = ROUND8P(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */
x.pSpace = &((u8*)p->aOp)[n]; /* Unused opcode memory */
assert( EIGHT_BYTE_ALIGNMENT(x.pSpace) );
x.nFree = ROUNDDOWN8(pParse->szOpAlloc - n); /* Bytes of unused memory */
@@ -77963,38 +86978,39 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
resolveP2Values(p, &nArg);
p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
- if( pParse->explain && nMem<10 ){
- nMem = 10;
+ if( pParse->explain ){
+ if( nMem<10 ) nMem = 10;
+ p->explain = pParse->explain;
+ p->nResColumn = 12 - 4*p->explain;
}
p->expired = 0;
/* Memory for registers, parameters, cursor, etc, is allocated in one or two
- ** passes. On the first pass, we try to reuse unused memory at the
+ ** passes. On the first pass, we try to reuse unused memory at the
** end of the opcode array. If we are unable to satisfy all memory
** requirements by reusing the opcode array tail, then the second
- ** pass will fill in the remainder using a fresh memory allocation.
+ ** pass will fill in the remainder using a fresh memory allocation.
**
** This two-pass approach that reuses as much memory as possible from
** the leftover memory at the end of the opcode array. This can significantly
** reduce the amount of memory held by a prepared statement.
*/
- do {
- x.nNeeded = 0;
- p->aMem = allocSpace(&x, p->aMem, nMem*sizeof(Mem));
- p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem));
- p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*));
- p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*));
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64));
-#endif
- if( x.nNeeded==0 ) break;
+ x.nNeeded = 0;
+ p->aMem = allocSpace(&x, 0, nMem*sizeof(Mem));
+ p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem));
+ p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*));
+ p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*));
+ if( x.nNeeded ){
x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded);
x.nFree = x.nNeeded;
- }while( !db->mallocFailed );
+ if( !db->mallocFailed ){
+ p->aMem = allocSpace(&x, p->aMem, nMem*sizeof(Mem));
+ p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem));
+ p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*));
+ p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*));
+ }
+ }
- p->pVList = pParse->pVList;
- pParse->pVList = 0;
- p->explain = pParse->explain;
if( db->mallocFailed ){
p->nVar = 0;
p->nCursor = 0;
@@ -78006,36 +87022,42 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
p->nMem = nMem;
initMemArray(p->aMem, nMem, db, MEM_Undefined);
memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*));
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- memset(p->anExec, 0, p->nOp*sizeof(i64));
-#endif
}
sqlite3VdbeRewind(p);
}
/*
-** Close a VDBE cursor and release all the resources that cursor
+** Close a VDBE cursor and release all the resources that cursor
** happens to hold.
*/
SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
- if( pCx==0 ){
+ if( pCx ) sqlite3VdbeFreeCursorNN(p,pCx);
+}
+static SQLITE_NOINLINE void freeCursorWithCache(Vdbe *p, VdbeCursor *pCx){
+ VdbeTxtBlbCache *pCache = pCx->pCache;
+ assert( pCx->colCache );
+ pCx->colCache = 0;
+ pCx->pCache = 0;
+ if( pCache->pCValue ){
+ sqlite3RCStrUnref(pCache->pCValue);
+ pCache->pCValue = 0;
+ }
+ sqlite3DbFree(p->db, pCache);
+ sqlite3VdbeFreeCursorNN(p, pCx);
+}
+SQLITE_PRIVATE void sqlite3VdbeFreeCursorNN(Vdbe *p, VdbeCursor *pCx){
+ if( pCx->colCache ){
+ freeCursorWithCache(p, pCx);
return;
}
- assert( pCx->pBtx==0 || pCx->eCurType==CURTYPE_BTREE );
switch( pCx->eCurType ){
case CURTYPE_SORTER: {
sqlite3VdbeSorterClose(p->db, pCx);
break;
}
case CURTYPE_BTREE: {
- if( pCx->isEphemeral ){
- if( pCx->pBtx ) sqlite3BtreeClose(pCx->pBtx);
- /* The pCx->pCursor will be close automatically, if it exists, by
- ** the call above. */
- }else{
- assert( pCx->uc.pCursor!=0 );
- sqlite3BtreeCloseCursor(pCx->uc.pCursor);
- }
+ assert( pCx->uc.pCursor!=0 );
+ sqlite3BtreeCloseCursor(pCx->uc.pCursor);
break;
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -78055,14 +87077,12 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
** Close all cursors in the current frame.
*/
static void closeCursorsInFrame(Vdbe *p){
- if( p->apCsr ){
- int i;
- for(i=0; i<p->nCursor; i++){
- VdbeCursor *pC = p->apCsr[i];
- if( pC ){
- sqlite3VdbeFreeCursor(p, pC);
- p->apCsr[i] = 0;
- }
+ int i;
+ for(i=0; i<p->nCursor; i++){
+ VdbeCursor *pC = p->apCsr[i];
+ if( pC ){
+ sqlite3VdbeFreeCursorNN(p, pC);
+ p->apCsr[i] = 0;
}
}
}
@@ -78075,9 +87095,6 @@ static void closeCursorsInFrame(Vdbe *p){
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
Vdbe *v = pFrame->v;
closeCursorsInFrame(v);
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- v->anExec = pFrame->anExec;
-#endif
v->aOp = pFrame->aOp;
v->nOp = pFrame->nOp;
v->aMem = pFrame->aMem;
@@ -78096,7 +87113,7 @@ SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
/*
** Close all cursors.
**
-** Also release any dynamic memory held by the VM in the Vdbe.aMem memory
+** Also release any dynamic memory held by the VM in the Vdbe.aMem memory
** cell array. This is necessary as the memory cell array may contain
** pointers to VdbeFrame objects, which may in turn contain pointers to
** open cursors.
@@ -78111,9 +87128,7 @@ static void closeAllCursors(Vdbe *p){
}
assert( p->nFrame==0 );
closeCursorsInFrame(p);
- if( p->aMem ){
- releaseMemArray(p->aMem, p->nMem);
- }
+ releaseMemArray(p->aMem, p->nMem);
while( p->pDelFrame ){
VdbeFrame *pDel = p->pDelFrame;
p->pDelFrame = pDel->pParent;
@@ -78135,12 +87150,12 @@ SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){
int n;
sqlite3 *db = p->db;
- if( p->nResColumn ){
- releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
+ if( p->nResAlloc ){
+ releaseMemArray(p->aColName, p->nResAlloc*COLNAME_N);
sqlite3DbFree(db, p->aColName);
}
n = nResColumn*COLNAME_N;
- p->nResColumn = (u16)nResColumn;
+ p->nResColumn = p->nResAlloc = (u16)nResColumn;
p->aColName = (Mem*)sqlite3DbMallocRawNN(db, sizeof(Mem)*n );
if( p->aColName==0 ) return;
initMemArray(p->aColName, n, db, MEM_Null);
@@ -78165,14 +87180,14 @@ SQLITE_PRIVATE int sqlite3VdbeSetColName(
){
int rc;
Mem *pColName;
- assert( idx<p->nResColumn );
+ assert( idx<p->nResAlloc );
assert( var<COLNAME_N );
if( p->db->mallocFailed ){
assert( !zName || xDel!=SQLITE_DYNAMIC );
return SQLITE_NOMEM_BKPT;
}
assert( p->aColName!=0 );
- pColName = &(p->aColName[idx+var*p->nResColumn]);
+ pColName = &(p->aColName[idx+var*p->nResAlloc]);
rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel);
assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 );
return rc;
@@ -78182,43 +87197,43 @@ SQLITE_PRIVATE int sqlite3VdbeSetColName(
** A read or write transaction may or may not be active on database handle
** db. If a transaction is active, commit it. If there is a
** write-transaction spanning more than one database file, this routine
-** takes care of the master journal trickery.
+** takes care of the super-journal trickery.
*/
static int vdbeCommit(sqlite3 *db, Vdbe *p){
int i;
int nTrans = 0; /* Number of databases with an active write-transaction
** that are candidates for a two-phase commit using a
- ** master-journal */
+ ** super-journal */
int rc = SQLITE_OK;
int needXcommit = 0;
#ifdef SQLITE_OMIT_VIRTUALTABLE
- /* With this option, sqlite3VtabSync() is defined to be simply
- ** SQLITE_OK so p is not used.
+ /* With this option, sqlite3VtabSync() is defined to be simply
+ ** SQLITE_OK so p is not used.
*/
UNUSED_PARAMETER(p);
#endif
/* Before doing anything else, call the xSync() callback for any
** virtual module tables written in this transaction. This has to
- ** be done before determining whether a master journal file is
+ ** be done before determining whether a super-journal file is
** required, as an xSync() callback may add an attached database
** to the transaction.
*/
rc = sqlite3VtabSync(db, p);
/* This loop determines (a) if the commit hook should be invoked and
- ** (b) how many database files have open write transactions, not
- ** including the temp database. (b) is important because if more than
- ** one database file has an open write transaction, a master journal
+ ** (b) how many database files have open write transactions, not
+ ** including the temp database. (b) is important because if more than
+ ** one database file has an open write transaction, a super-journal
** file is required for an atomic commit.
- */
- for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
+ */
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
- if( sqlite3BtreeIsInTrans(pBt) ){
- /* Whether or not a database might need a master journal depends upon
+ if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){
+ /* Whether or not a database might need a super-journal depends upon
** its journal mode (among other things). This matrix determines which
- ** journal modes use a master journal and which do not */
+ ** journal modes use a super-journal and which do not */
static const u8 aMJNeeded[] = {
/* DELETE */ 1,
/* PERSIST */ 1,
@@ -78234,7 +87249,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
if( db->aDb[i].safety_level!=PAGER_SYNCHRONOUS_OFF
&& aMJNeeded[sqlite3PagerGetJournalMode(pPager)]
&& sqlite3PagerIsMemdb(pPager)==0
- ){
+ ){
assert( i!=1 );
nTrans++;
}
@@ -78256,11 +87271,11 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
/* The simple case - no more than one database file (not counting the
** TEMP database) has a transaction active. There is no need for the
- ** master-journal.
+ ** super-journal.
**
** If the return value of sqlite3BtreeGetFilename() is a zero length
- ** string, it means the main database is :memory: or a temp file. In
- ** that case we do not support atomic multi-file commits, so use the
+ ** string, it means the main database is :memory: or a temp file. In
+ ** that case we do not support atomic multi-file commits, so use the
** simple case then too.
*/
if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt))
@@ -78273,7 +87288,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
}
}
- /* Do the commit only if all databases successfully complete phase 1.
+ /* Do the commit only if all databases successfully complete phase 1.
** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
** IO error while deleting or truncating a journal file. It is unlikely,
** but could happen. In this case abandon processing and return the error.
@@ -78290,124 +87305,125 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
}
/* The complex case - There is a multi-file write-transaction active.
- ** This requires a master journal file to ensure the transaction is
+ ** This requires a super-journal file to ensure the transaction is
** committed atomically.
*/
#ifndef SQLITE_OMIT_DISKIO
else{
sqlite3_vfs *pVfs = db->pVfs;
- char *zMaster = 0; /* File-name for the master journal */
+ char *zSuper = 0; /* File-name for the super-journal */
char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt);
- sqlite3_file *pMaster = 0;
+ sqlite3_file *pSuperJrnl = 0;
i64 offset = 0;
int res;
int retryCount = 0;
int nMainFile;
- /* Select a master journal file name */
+ /* Select a super-journal file name */
nMainFile = sqlite3Strlen30(zMainFile);
- zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz", zMainFile);
- if( zMaster==0 ) return SQLITE_NOMEM_BKPT;
+ zSuper = sqlite3MPrintf(db, "%.4c%s%.16c", 0,zMainFile,0);
+ if( zSuper==0 ) return SQLITE_NOMEM_BKPT;
+ zSuper += 4;
do {
u32 iRandom;
if( retryCount ){
if( retryCount>100 ){
- sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster);
- sqlite3OsDelete(pVfs, zMaster, 0);
+ sqlite3_log(SQLITE_FULL, "MJ delete: %s", zSuper);
+ sqlite3OsDelete(pVfs, zSuper, 0);
break;
}else if( retryCount==1 ){
- sqlite3_log(SQLITE_FULL, "MJ collide: %s", zMaster);
+ sqlite3_log(SQLITE_FULL, "MJ collide: %s", zSuper);
}
}
retryCount++;
sqlite3_randomness(sizeof(iRandom), &iRandom);
- sqlite3_snprintf(13, &zMaster[nMainFile], "-mj%06X9%02X",
+ sqlite3_snprintf(13, &zSuper[nMainFile], "-mj%06X9%02X",
(iRandom>>8)&0xffffff, iRandom&0xff);
- /* The antipenultimate character of the master journal name must
+ /* The antipenultimate character of the super-journal name must
** be "9" to avoid name collisions when using 8+3 filenames. */
- assert( zMaster[sqlite3Strlen30(zMaster)-3]=='9' );
- sqlite3FileSuffix3(zMainFile, zMaster);
- rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
+ assert( zSuper[sqlite3Strlen30(zSuper)-3]=='9' );
+ sqlite3FileSuffix3(zMainFile, zSuper);
+ rc = sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res);
}while( rc==SQLITE_OK && res );
if( rc==SQLITE_OK ){
- /* Open the master journal. */
- rc = sqlite3OsOpenMalloc(pVfs, zMaster, &pMaster,
+ /* Open the super-journal. */
+ rc = sqlite3OsOpenMalloc(pVfs, zSuper, &pSuperJrnl,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
- SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0
+ SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_SUPER_JOURNAL, 0
);
}
if( rc!=SQLITE_OK ){
- sqlite3DbFree(db, zMaster);
+ sqlite3DbFree(db, zSuper-4);
return rc;
}
-
+
/* Write the name of each database file in the transaction into the new
- ** master journal file. If an error occurs at this point close
- ** and delete the master journal file. All the individual journal files
- ** still have 'null' as the master journal pointer, so they will roll
+ ** super-journal file. If an error occurs at this point close
+ ** and delete the super-journal file. All the individual journal files
+ ** still have 'null' as the super-journal pointer, so they will roll
** back independently if a failure occurs.
*/
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
- if( sqlite3BtreeIsInTrans(pBt) ){
+ if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){
char const *zFile = sqlite3BtreeGetJournalname(pBt);
if( zFile==0 ){
continue; /* Ignore TEMP and :memory: databases */
}
assert( zFile[0]!=0 );
- rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset);
+ rc = sqlite3OsWrite(pSuperJrnl, zFile, sqlite3Strlen30(zFile)+1,offset);
offset += sqlite3Strlen30(zFile)+1;
if( rc!=SQLITE_OK ){
- sqlite3OsCloseFree(pMaster);
- sqlite3OsDelete(pVfs, zMaster, 0);
- sqlite3DbFree(db, zMaster);
+ sqlite3OsCloseFree(pSuperJrnl);
+ sqlite3OsDelete(pVfs, zSuper, 0);
+ sqlite3DbFree(db, zSuper-4);
return rc;
}
}
}
- /* Sync the master journal file. If the IOCAP_SEQUENTIAL device
+ /* Sync the super-journal file. If the IOCAP_SEQUENTIAL device
** flag is set this is not required.
*/
- if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL)
- && SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL))
+ if( 0==(sqlite3OsDeviceCharacteristics(pSuperJrnl)&SQLITE_IOCAP_SEQUENTIAL)
+ && SQLITE_OK!=(rc = sqlite3OsSync(pSuperJrnl, SQLITE_SYNC_NORMAL))
){
- sqlite3OsCloseFree(pMaster);
- sqlite3OsDelete(pVfs, zMaster, 0);
- sqlite3DbFree(db, zMaster);
+ sqlite3OsCloseFree(pSuperJrnl);
+ sqlite3OsDelete(pVfs, zSuper, 0);
+ sqlite3DbFree(db, zSuper-4);
return rc;
}
/* Sync all the db files involved in the transaction. The same call
- ** sets the master journal pointer in each individual journal. If
- ** an error occurs here, do not delete the master journal file.
+ ** sets the super-journal pointer in each individual journal. If
+ ** an error occurs here, do not delete the super-journal file.
**
** If the error occurs during the first call to
** sqlite3BtreeCommitPhaseOne(), then there is a chance that the
- ** master journal file will be orphaned. But we cannot delete it,
- ** in case the master journal file name was written into the journal
+ ** super-journal file will be orphaned. But we cannot delete it,
+ ** in case the super-journal file name was written into the journal
** file before the failure occurred.
*/
- for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
- rc = sqlite3BtreeCommitPhaseOne(pBt, zMaster);
+ rc = sqlite3BtreeCommitPhaseOne(pBt, zSuper);
}
}
- sqlite3OsCloseFree(pMaster);
+ sqlite3OsCloseFree(pSuperJrnl);
assert( rc!=SQLITE_BUSY );
if( rc!=SQLITE_OK ){
- sqlite3DbFree(db, zMaster);
+ sqlite3DbFree(db, zSuper-4);
return rc;
}
- /* Delete the master journal file. This commits the transaction. After
+ /* Delete the super-journal file. This commits the transaction. After
** doing this the directory is synced again before any individual
** transaction files are deleted.
*/
- rc = sqlite3OsDelete(pVfs, zMaster, 1);
- sqlite3DbFree(db, zMaster);
- zMaster = 0;
+ rc = sqlite3OsDelete(pVfs, zSuper, 1);
+ sqlite3DbFree(db, zSuper-4);
+ zSuper = 0;
if( rc ){
return rc;
}
@@ -78421,7 +87437,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
*/
disable_simulated_io_errors();
sqlite3BeginBenignMalloc();
- for(i=0; i<db->nDb; i++){
+ for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
sqlite3BtreeCommitPhaseTwo(pBt, 1);
@@ -78437,7 +87453,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
return rc;
}
-/*
+/*
** This routine checks that the sqlite3.nVdbeActive count variable
** matches the number of vdbe's in the list sqlite3.pVdbe that are
** currently active. An assertion fails if the two counts do not match.
@@ -78459,7 +87475,7 @@ static void checkActiveVdbeCnt(sqlite3 *db){
if( p->readOnly==0 ) nWrite++;
if( p->bIsReader ) nRead++;
}
- p = p->pNext;
+ p = p->pVNext;
}
assert( cnt==db->nVdbeActive );
assert( nWrite==db->nVdbeWrite );
@@ -78473,10 +87489,10 @@ static void checkActiveVdbeCnt(sqlite3 *db){
** If the Vdbe passed as the first argument opened a statement-transaction,
** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or
** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement
-** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the
+** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the
** statement transaction is committed.
**
-** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned.
+** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned.
** Otherwise SQLITE_OK.
*/
static SQLITE_NOINLINE int vdbeCloseStatement(Vdbe *p, int eOp){
@@ -78489,7 +87505,7 @@ static SQLITE_NOINLINE int vdbeCloseStatement(Vdbe *p, int eOp){
assert( db->nStatement>0 );
assert( p->iStatement==(db->nStatement+db->nSavepoint) );
- for(i=0; i<db->nDb; i++){
+ for(i=0; i<db->nDb; i++){
int rc2 = SQLITE_OK;
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
@@ -78516,8 +87532,8 @@ static SQLITE_NOINLINE int vdbeCloseStatement(Vdbe *p, int eOp){
}
}
- /* If the statement transaction is being rolled back, also restore the
- ** database handles deferred constraint counter to the value it had when
+ /* If the statement transaction is being rolled back, also restore the
+ ** database handles deferred constraint counter to the value it had when
** the statement transaction was opened. */
if( eOp==SAVEPOINT_ROLLBACK ){
db->nDeferredCons = p->nStmtDefCons;
@@ -78534,25 +87550,26 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
/*
-** This function is called when a transaction opened by the database
-** handle associated with the VM passed as an argument is about to be
+** This function is called when a transaction opened by the database
+** handle associated with the VM passed as an argument is about to be
** committed. If there are outstanding deferred foreign key constraint
** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK.
**
-** If there are outstanding FK violations and this function returns
+** If there are outstanding FK violations and this function returns
** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
** and write an error message to it. Then return SQLITE_ERROR.
*/
#ifndef SQLITE_OMIT_FOREIGN_KEY
SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
sqlite3 *db = p->db;
- if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0)
- || (!deferred && p->nFkConstraint>0)
+ if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0)
+ || (!deferred && p->nFkConstraint>0)
){
p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
p->errorAction = OE_Abort;
sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
- return SQLITE_ERROR;
+ if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
+ return SQLITE_CONSTRAINT_FOREIGNKEY;
}
return SQLITE_OK;
}
@@ -78563,9 +87580,9 @@ SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
** has made changes and is in autocommit mode, then commit those
** changes. If a rollback is needed, then do the rollback.
**
-** This routine is the only way to move the state of a VM from
-** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT. It is harmless to
-** call this on a VM that is in the SQLITE_MAGIC_HALT state.
+** This routine is the only way to move the sqlite3eOpenState of a VM from
+** SQLITE_STATE_RUN to SQLITE_STATE_HALT. It is harmless to
+** call this on a VM that is in the SQLITE_STATE_HALT state.
**
** Return an error code. If the commit could not complete because of
** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it
@@ -78577,7 +87594,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
/* This function contains the logic that determines if a statement or
** transaction will be committed or rolled back as a result of the
- ** execution of this virtual machine.
+ ** execution of this virtual machine.
**
** If any of the following errors occur:
**
@@ -78591,9 +87608,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
** one, or the complete transaction if there is no statement transaction.
*/
- if( p->magic!=VDBE_MAGIC_RUN ){
- return SQLITE_OK;
- }
+ assert( p->eVdbeState==VDBE_RUN_STATE );
if( db->mallocFailed ){
p->rc = SQLITE_NOMEM_BKPT;
}
@@ -78602,7 +87617,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
/* No commit or rollback needed if the program never started or if the
** SQL statement does not read or write a database file. */
- if( p->pc>=0 && p->bIsReader ){
+ if( p->bIsReader ){
int mrc; /* Primary error code from p->rc */
int eStatementOp = 0;
int isSpecialError; /* Set to true if a 'special' error */
@@ -78611,20 +87626,26 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
sqlite3VdbeEnter(p);
/* Check for one of the special errors */
- mrc = p->rc & 0xff;
- isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
- || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
+ if( p->rc ){
+ mrc = p->rc & 0xff;
+ isSpecialError = mrc==SQLITE_NOMEM
+ || mrc==SQLITE_IOERR
+ || mrc==SQLITE_INTERRUPT
+ || mrc==SQLITE_FULL;
+ }else{
+ mrc = isSpecialError = 0;
+ }
if( isSpecialError ){
- /* If the query was read-only and the error code is SQLITE_INTERRUPT,
- ** no rollback is necessary. Otherwise, at least a savepoint
- ** transaction must be rolled back to restore the database to a
+ /* If the query was read-only and the error code is SQLITE_INTERRUPT,
+ ** no rollback is necessary. Otherwise, at least a savepoint
+ ** transaction must be rolled back to restore the database to a
** consistent state.
**
** Even if the statement is read-only, it is important to perform
- ** a statement or transaction rollback operation. If the error
+ ** a statement or transaction rollback operation. If the error
** occurred while writing to the journal, sub-journal or database
** file as part of an effort to free up cache space (see function
- ** pagerStress() in pager.c), the rollback is required to restore
+ ** pagerStress() in pager.c), the rollback is required to restore
** the pager to a consistent state.
*/
if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
@@ -78643,19 +87664,19 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
}
/* Check for immediate foreign key violations. */
- if( p->rc==SQLITE_OK ){
+ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
sqlite3VdbeCheckFk(p, 0);
}
-
- /* If the auto-commit flag is set and this is the only active writer
- ** VM, then we do either a commit or rollback of the current transaction.
+
+ /* If the auto-commit flag is set and this is the only active writer
+ ** VM, then we do either a commit or rollback of the current transaction.
**
- ** Note: This block also runs if one of the special errors handled
- ** above has occurred.
+ ** Note: This block also runs if one of the special errors handled
+ ** above has occurred.
*/
- if( !sqlite3VtabInSync(db)
- && db->autoCommit
- && db->nVdbeWrite==(p->readOnly==0)
+ if( !sqlite3VtabInSync(db)
+ && db->autoCommit
+ && db->nVdbeWrite==(p->readOnly==0)
){
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
rc = sqlite3VdbeCheckFk(p, 1);
@@ -78665,10 +87686,13 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
return SQLITE_ERROR;
}
rc = SQLITE_CONSTRAINT_FOREIGNKEY;
- }else{
- /* The auto-commit flag is true, the vdbe program was successful
+ }else if( db->flags & SQLITE_CorruptRdOnly ){
+ rc = SQLITE_CORRUPT;
+ db->flags &= ~SQLITE_CorruptRdOnly;
+ }else{
+ /* The auto-commit flag is true, the vdbe program was successful
** or hit an 'OR FAIL' constraint and there are no deferred foreign
- ** key constraints to hold up the transaction. This means a commit
+ ** key constraints to hold up the transaction. This means a commit
** is required. */
rc = vdbeCommit(db, p);
}
@@ -78676,15 +87700,18 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
sqlite3VdbeLeave(p);
return SQLITE_BUSY;
}else if( rc!=SQLITE_OK ){
+ sqlite3SystemError(db, rc);
p->rc = rc;
sqlite3RollbackAll(db, SQLITE_OK);
p->nChange = 0;
}else{
db->nDeferredCons = 0;
db->nDeferredImmCons = 0;
- db->flags &= ~SQLITE_DeferFKs;
+ db->flags &= ~(u64)SQLITE_DeferFKs;
sqlite3CommitInternalChanges(db);
}
+ }else if( p->rc==SQLITE_SCHEMA && db->nVdbeActive>1 ){
+ p->nChange = 0;
}else{
sqlite3RollbackAll(db, SQLITE_OK);
p->nChange = 0;
@@ -78702,7 +87729,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
p->nChange = 0;
}
}
-
+
/* If eStatementOp is non-zero, then a statement transaction needs to
** be committed or rolled back. Call sqlite3VdbeCloseStatement() to
** do so. If this operation returns an error, and the current statement
@@ -78723,9 +87750,9 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
p->nChange = 0;
}
}
-
+
/* If this was an INSERT, UPDATE or DELETE and no statement transaction
- ** has been rolled back, update the database connection change-counter.
+ ** has been rolled back, update the database connection change-counter.
*/
if( p->changeCntOn ){
if( eStatementOp!=SAVEPOINT_ROLLBACK ){
@@ -78741,22 +87768,20 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
}
/* We have successfully halted and closed the VM. Record this fact. */
- if( p->pc>=0 ){
- db->nVdbeActive--;
- if( !p->readOnly ) db->nVdbeWrite--;
- if( p->bIsReader ) db->nVdbeRead--;
- assert( db->nVdbeActive>=db->nVdbeRead );
- assert( db->nVdbeRead>=db->nVdbeWrite );
- assert( db->nVdbeWrite>=0 );
- }
- p->magic = VDBE_MAGIC_HALT;
+ db->nVdbeActive--;
+ if( !p->readOnly ) db->nVdbeWrite--;
+ if( p->bIsReader ) db->nVdbeRead--;
+ assert( db->nVdbeActive>=db->nVdbeRead );
+ assert( db->nVdbeRead>=db->nVdbeWrite );
+ assert( db->nVdbeWrite>=0 );
+ p->eVdbeState = VDBE_HALT_STATE;
checkActiveVdbeCnt(db);
if( db->mallocFailed ){
p->rc = SQLITE_NOMEM_BKPT;
}
/* If the auto-commit flag is set to true, then any locks that were held
- ** by connection db have now been released. Call sqlite3ConnectionUnlocked()
+ ** by connection db have now been released. Call sqlite3ConnectionUnlocked()
** to invoke any required unlock-notify callbacks.
*/
if( db->autoCommit ){
@@ -78778,7 +87803,7 @@ SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){
/*
** Copy the error code and error message belonging to the VDBE passed
-** as the first argument to its database handle (so that they will be
+** as the first argument to its database handle (so that they will be
** returned by calls to sqlite3_errcode() and sqlite3_errmsg()).
**
** This function does not clear the VDBE error code or message, just
@@ -78798,12 +87823,13 @@ SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){
sqlite3ValueSetNull(db->pErr);
}
db->errCode = rc;
+ db->errByteOffset = -1;
return rc;
}
#ifdef SQLITE_ENABLE_SQLLOG
/*
-** If an SQLITE_CONFIG_SQLLOG hook is registered and the VM has been run,
+** If an SQLITE_CONFIG_SQLLOG hook is registered and the VM has been run,
** invoke it.
*/
static void vdbeInvokeSqllog(Vdbe *v){
@@ -78830,8 +87856,8 @@ static void vdbeInvokeSqllog(Vdbe *v){
** again.
**
** To look at it another way, this routine resets the state of the
-** virtual machine from VDBE_MAGIC_RUN or VDBE_MAGIC_HALT back to
-** VDBE_MAGIC_INIT.
+** virtual machine from VDBE_RUN_STATE or VDBE_HALT_STATE back to
+** VDBE_READY_STATE.
*/
SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
@@ -78845,7 +87871,7 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
** error, then it might not have been halted properly. So halt
** it now.
*/
- sqlite3VdbeHalt(p);
+ if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p);
/* If the VDBE has been run even partially, then transfer the error code
** and error message from the VDBE into the main database structure. But
@@ -78854,29 +87880,28 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
*/
if( p->pc>=0 ){
vdbeInvokeSqllog(p);
- sqlite3VdbeTransferError(p);
- if( p->runOnlyOnce ) p->expired = 1;
- }else if( p->rc && p->expired ){
- /* The expired flag was set on the VDBE before the first call
- ** to sqlite3_step(). For consistency (since sqlite3_step() was
- ** called), set the database error in this case as well.
- */
- sqlite3ErrorWithMsg(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg);
+ if( db->pErr || p->zErrMsg ){
+ sqlite3VdbeTransferError(p);
+ }else{
+ db->errCode = p->rc;
+ }
}
/* Reset register contents and reclaim error message memory.
*/
#ifdef SQLITE_DEBUG
- /* Execute assert() statements to ensure that the Vdbe.apCsr[] and
+ /* Execute assert() statements to ensure that the Vdbe.apCsr[] and
** Vdbe.aMem[] arrays have already been cleaned up. */
if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
if( p->aMem ){
for(i=0; i<p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined );
}
#endif
- sqlite3DbFree(db, p->zErrMsg);
- p->zErrMsg = 0;
- p->pResultSet = 0;
+ if( p->zErrMsg ){
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = 0;
+ }
+ p->pResultRow = 0;
#ifdef SQLITE_DEBUG
p->nWrite = 0;
#endif
@@ -78904,10 +87929,12 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
}
for(i=0; i<p->nOp; i++){
char zHdr[100];
+ i64 cnt = p->aOp[i].nExec;
+ i64 cycles = p->aOp[i].nCycle;
sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ",
- p->aOp[i].cnt,
- p->aOp[i].cycles,
- p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0
+ cnt,
+ cycles,
+ cnt>0 ? cycles/cnt : 0
);
fprintf(out, "%s", zHdr);
sqlite3VdbePrintOp(out, i, &p->aOp[i]);
@@ -78916,17 +87943,19 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
}
}
#endif
- p->magic = VDBE_MAGIC_RESET;
return p->rc & db->errMask;
}
-
+
/*
** Clean up and delete a VDBE after execution. Return an integer which is
** the result code. Write any error message text into *pzErrMsg.
*/
SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe *p){
int rc = SQLITE_OK;
- if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){
+ assert( VDBE_RUN_STATE>VDBE_READY_STATE );
+ assert( VDBE_HALT_STATE>VDBE_READY_STATE );
+ assert( VDBE_INIT_STATE<VDBE_READY_STATE );
+ if( p->eVdbeState>=VDBE_READY_STATE ){
rc = sqlite3VdbeReset(p);
assert( (rc & p->db->errMask)==rc );
}
@@ -78940,8 +87969,8 @@ SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe *p){
** the first argument.
**
** Or, if iOp is greater than or equal to zero, then the destructor is
-** only invoked for those auxiliary data pointers created by the user
-** function invoked by the OP_Function opcode at instruction iOp of
+** only invoked for those auxiliary data pointers created by the user
+** function invoked by the OP_Function opcode at instruction iOp of
** VM pVdbe, and only then if:
**
** * the associated function parameter is the 32nd or later (counting
@@ -78978,25 +88007,35 @@ SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3 *db, AuxData **pp, int iOp,
** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with
** the database connection and frees the object itself.
*/
-SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
+static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
SubProgram *pSub, *pNext;
+ assert( db!=0 );
assert( p->db==0 || p->db==db );
- releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
+ if( p->aColName ){
+ releaseMemArray(p->aColName, p->nResAlloc*COLNAME_N);
+ sqlite3DbNNFreeNN(db, p->aColName);
+ }
for(pSub=p->pProgram; pSub; pSub=pNext){
pNext = pSub->pNext;
vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
sqlite3DbFree(db, pSub);
}
- if( p->magic!=VDBE_MAGIC_INIT ){
+ if( p->eVdbeState!=VDBE_INIT_STATE ){
releaseMemArray(p->aVar, p->nVar);
- sqlite3DbFree(db, p->pVList);
- sqlite3DbFree(db, p->pFree);
+ if( p->pVList ) sqlite3DbNNFreeNN(db, p->pVList);
+ if( p->pFree ) sqlite3DbNNFreeNN(db, p->pFree);
}
vdbeFreeOpArray(db, p->aOp, p->nOp);
- sqlite3DbFree(db, p->aColName);
- sqlite3DbFree(db, p->zSql);
+ if( p->zSql ) sqlite3DbNNFreeNN(db, p->zSql);
#ifdef SQLITE_ENABLE_NORMALIZE
sqlite3DbFree(db, p->zNormSql);
+ {
+ DblquoteStr *pThis, *pNxt;
+ for(pThis=p->pDblStr; pThis; pThis=pNxt){
+ pNxt = pThis->pNextStr;
+ sqlite3DbFree(db, pThis);
+ }
+ }
#endif
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
{
@@ -79017,20 +88056,17 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
assert( p!=0 );
db = p->db;
+ assert( db!=0 );
assert( sqlite3_mutex_held(db->mutex) );
sqlite3VdbeClearObject(db, p);
- if( p->pPrev ){
- p->pPrev->pNext = p->pNext;
- }else{
- assert( db->pVdbe==p );
- db->pVdbe = p->pNext;
- }
- if( p->pNext ){
- p->pNext->pPrev = p->pPrev;
+ if( db->pnBytesFreed==0 ){
+ assert( p->ppVPrev!=0 );
+ *p->ppVPrev = p->pVNext;
+ if( p->pVNext ){
+ p->pVNext->ppVPrev = p->ppVPrev;
+ }
}
- p->magic = VDBE_MAGIC_DEAD;
- p->db = 0;
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
/*
@@ -79038,7 +88074,7 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
** carried out. Seek the cursor now. If an error occurs, return
** the appropriate error code.
*/
-static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){
+SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeFinishMoveto(VdbeCursor *p){
int res, rc;
#ifdef SQLITE_TEST
extern int sqlite3_search_count;
@@ -79046,7 +88082,7 @@ static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){
assert( p->deferredMoveto );
assert( p->isTable );
assert( p->eCurType==CURTYPE_BTREE );
- rc = sqlite3BtreeMovetoUnpacked(p->uc.pCursor, 0, p->movetoTarget, 0, &res);
+ rc = sqlite3BtreeTableMoveto(p->uc.pCursor, p->movetoTarget, 0, &res);
if( rc ) return rc;
if( res!=0 ) return SQLITE_CORRUPT_BKPT;
#ifdef SQLITE_TEST
@@ -79064,7 +88100,7 @@ static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){
** is supposed to be pointing. If the row was deleted out from under the
** cursor, set the cursor to point to a NULL row.
*/
-static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){
+SQLITE_PRIVATE int SQLITE_NOINLINE sqlite3VdbeHandleMovedCursor(VdbeCursor *p){
int isDifferentRow, rc;
assert( p->eCurType==CURTYPE_BTREE );
assert( p->uc.pCursor!=0 );
@@ -79080,40 +88116,9 @@ static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){
** if need be. Return any I/O error from the restore operation.
*/
SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor *p){
- assert( p->eCurType==CURTYPE_BTREE );
- if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
- return handleMovedCursor(p);
- }
- return SQLITE_OK;
-}
-
-/*
-** Make sure the cursor p is ready to read or write the row to which it
-** was last positioned. Return an error code if an OOM fault or I/O error
-** prevents us from positioning the cursor to its correct position.
-**
-** If a MoveTo operation is pending on the given cursor, then do that
-** MoveTo now. If no move is pending, check to see if the row has been
-** deleted out from under the cursor and if it has, mark the row as
-** a NULL row.
-**
-** If the cursor is already pointing to the correct row and that row has
-** not been deleted out from under the cursor, then this routine is a no-op.
-*/
-SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
- VdbeCursor *p = *pp;
- assert( p->eCurType==CURTYPE_BTREE || p->eCurType==CURTYPE_PSEUDO );
- if( p->deferredMoveto ){
- int iMap;
- if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 ){
- *pp = p->pAltCursor;
- *piCol = iMap - 1;
- return SQLITE_OK;
- }
- return handleDeferredMoveto(p);
- }
+ assert( p->eCurType==CURTYPE_BTREE || IsNullCursor(p) );
if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
- return handleMovedCursor(p);
+ return sqlite3VdbeHandleMovedCursor(p);
}
return SQLITE_OK;
}
@@ -79124,7 +88129,7 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
** sqlite3VdbeSerialType()
** sqlite3VdbeSerialTypeLen()
** sqlite3VdbeSerialLen()
-** sqlite3VdbeSerialPut()
+** sqlite3VdbeSerialPut() <--- in-lined into OP_MakeRecord as of 2022-04-02
** sqlite3VdbeSerialGet()
**
** encapsulate the code that serializes values for storage in SQLite
@@ -79160,8 +88165,17 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
** of SQLite will not understand those serial types.
*/
+#if 0 /* Inlined into the OP_MakeRecord opcode */
/*
** Return the serial-type for the value stored in pMem.
+**
+** This routine might convert a large MEM_IntReal value into MEM_Real.
+**
+** 2019-07-11: The primary user of this subroutine was the OP_MakeRecord
+** opcode in the byte-code engine. But by moving this routine in-line, we
+** can omit some redundant tests and make that opcode a lot faster. So
+** this routine is now only used by the STAT3 logic and STAT3 support has
+** ended. The code is kept here for historical reference only.
*/
SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
int flags = pMem->flags;
@@ -79172,11 +88186,13 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
*pLen = 0;
return 0;
}
- if( flags&MEM_Int ){
+ if( flags&(MEM_Int|MEM_IntReal) ){
/* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */
# define MAX_6BYTE ((((i64)0x00008000)<<32)-1)
i64 i = pMem->u.i;
u64 u;
+ testcase( flags & MEM_Int );
+ testcase( flags & MEM_IntReal );
if( i<0 ){
u = ~i;
}else{
@@ -79196,6 +88212,15 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
if( u<=2147483647 ){ *pLen = 4; return 4; }
if( u<=MAX_6BYTE ){ *pLen = 6; return 5; }
*pLen = 8;
+ if( flags&MEM_IntReal ){
+ /* If the value is IntReal and is going to take up 8 bytes to store
+ ** as an integer, then we might as well make it an 8-byte floating
+ ** point value */
+ pMem->u.r = (double)pMem->u.i;
+ pMem->flags &= ~MEM_IntReal;
+ pMem->flags |= MEM_Real;
+ return 7;
+ }
return 6;
}
if( flags&MEM_Real ){
@@ -79211,12 +88236,13 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
*pLen = n;
return ((n*2) + 12 + ((flags&MEM_Str)!=0));
}
+#endif /* inlined into OP_MakeRecord */
/*
** The sizes for serial types less than 128
*/
-static const u8 sqlite3SmallTypeSizes[] = {
- /* 0 1 2 3 4 5 6 7 8 9 */
+SQLITE_PRIVATE const u8 sqlite3SmallTypeSizes[128] = {
+ /* 0 1 2 3 4 5 6 7 8 9 */
/* 0 */ 0, 1, 2, 3, 4, 6, 8, 8, 0, 0,
/* 10 */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3,
/* 20 */ 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
@@ -79239,19 +88265,19 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32 serial_type){
if( serial_type>=128 ){
return (serial_type-12)/2;
}else{
- assert( serial_type<12
+ assert( serial_type<12
|| sqlite3SmallTypeSizes[serial_type]==(serial_type - 12)/2 );
return sqlite3SmallTypeSizes[serial_type];
}
}
SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8 serial_type){
assert( serial_type<128 );
- return sqlite3SmallTypeSizes[serial_type];
+ return sqlite3SmallTypeSizes[serial_type];
}
/*
-** If we are on an architecture with mixed-endian floating
-** points (ex: ARM7) then swap the lower 4 bytes with the
+** If we are on an architecture with mixed-endian floating
+** points (ex: ARM7) then swap the lower 4 bytes with the
** upper 4 bytes. Return the result.
**
** For most architectures, this is a no-op.
@@ -79273,7 +88299,7 @@ SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8 serial_type){
** (2007-08-30) Frank van Vugt has studied this problem closely
** and has send his findings to the SQLite developers. Frank
** writes that some Linux kernels offer floating point hardware
-** emulation that uses only 32-bit mantissas instead of a full
+** emulation that uses only 32-bit mantissas instead of a full
** 48-bits as required by the IEEE standard. (This is the
** CONFIG_FPE_FASTFPE option.) On such systems, floating point
** byte swapping becomes very complicated. To avoid problems,
@@ -79284,7 +88310,7 @@ SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8 serial_type){
** so we trust him.
*/
#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT
-static u64 floatSwap(u64 in){
+SQLITE_PRIVATE u64 sqlite3FloatSwap(u64 in){
union {
u64 r;
u32 i[2];
@@ -79297,59 +88323,8 @@ static u64 floatSwap(u64 in){
u.i[1] = t;
return u.r;
}
-# define swapMixedEndianFloat(X) X = floatSwap(X)
-#else
-# define swapMixedEndianFloat(X)
-#endif
+#endif /* SQLITE_MIXED_ENDIAN_64BIT_FLOAT */
-/*
-** Write the serialized data blob for the value stored in pMem into
-** buf. It is assumed that the caller has allocated sufficient space.
-** Return the number of bytes written.
-**
-** nBuf is the amount of space left in buf[]. The caller is responsible
-** for allocating enough space to buf[] to hold the entire field, exclusive
-** of the pMem->u.nZero bytes for a MEM_Zero value.
-**
-** Return the number of bytes actually written into buf[]. The number
-** of bytes in the zero-filled tail is included in the return value only
-** if those bytes were zeroed in buf[].
-*/
-SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){
- u32 len;
-
- /* Integer and Real */
- if( serial_type<=7 && serial_type>0 ){
- u64 v;
- u32 i;
- if( serial_type==7 ){
- assert( sizeof(v)==sizeof(pMem->u.r) );
- memcpy(&v, &pMem->u.r, sizeof(v));
- swapMixedEndianFloat(v);
- }else{
- v = pMem->u.i;
- }
- len = i = sqlite3SmallTypeSizes[serial_type];
- assert( i>0 );
- do{
- buf[--i] = (u8)(v&0xFF);
- v >>= 8;
- }while( i );
- return len;
- }
-
- /* String or blob */
- if( serial_type>=12 ){
- assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0)
- == (int)sqlite3VdbeSerialTypeLen(serial_type) );
- len = pMem->n;
- if( len>0 ) memcpy(buf, pMem->z, len);
- return len;
- }
-
- /* NULL or constants 0 or 1 */
- return 0;
-}
/* Input "x" is a sequence of unsigned characters that represent a
** big-endian integer. Return the equivalent native integer
@@ -79362,14 +88337,14 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){
/*
** Deserialize the data blob pointed to by buf as serial type serial_type
-** and store the result in pMem. Return the number of bytes read.
+** and store the result in pMem.
**
** This function is implemented as two separate routines for performance.
** The few cases that require local variables are broken out into a separate
** routine so that in most cases the overhead of moving the stack pointer
** is avoided.
-*/
-static u32 SQLITE_NOINLINE serialGet(
+*/
+static void serialGet(
const unsigned char *buf, /* Buffer to deserialize from */
u32 serial_type, /* Serial type to deserialize */
Mem *pMem /* Memory cell to write value into */
@@ -79401,11 +88376,10 @@ static u32 SQLITE_NOINLINE serialGet(
assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 );
swapMixedEndianFloat(x);
memcpy(&pMem->u.r, &x, sizeof(x));
- pMem->flags = sqlite3IsNaN(pMem->u.r) ? MEM_Null : MEM_Real;
+ pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real;
}
- return 8;
}
-SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
+SQLITE_PRIVATE void sqlite3VdbeSerialGet(
const unsigned char *buf, /* Buffer to deserialize from */
u32 serial_type, /* Serial type to deserialize */
Mem *pMem /* Memory cell to write value into */
@@ -79416,13 +88390,13 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
pMem->flags = MEM_Null|MEM_Zero;
pMem->n = 0;
pMem->u.nZero = 0;
- break;
+ return;
}
case 11: /* Reserved for future use */
case 0: { /* Null */
/* EVIDENCE-OF: R-24078-09375 Value is a NULL. */
pMem->flags = MEM_Null;
- break;
+ return;
}
case 1: {
/* EVIDENCE-OF: R-44885-25196 Value is an 8-bit twos-complement
@@ -79430,7 +88404,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
pMem->u.i = ONE_BYTE_INT(buf);
pMem->flags = MEM_Int;
testcase( pMem->u.i<0 );
- return 1;
+ return;
}
case 2: { /* 2-byte signed integer */
/* EVIDENCE-OF: R-49794-35026 Value is a big-endian 16-bit
@@ -79438,7 +88412,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
pMem->u.i = TWO_BYTE_INT(buf);
pMem->flags = MEM_Int;
testcase( pMem->u.i<0 );
- return 2;
+ return;
}
case 3: { /* 3-byte signed integer */
/* EVIDENCE-OF: R-37839-54301 Value is a big-endian 24-bit
@@ -79446,19 +88420,19 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
pMem->u.i = THREE_BYTE_INT(buf);
pMem->flags = MEM_Int;
testcase( pMem->u.i<0 );
- return 3;
+ return;
}
case 4: { /* 4-byte signed integer */
/* EVIDENCE-OF: R-01849-26079 Value is a big-endian 32-bit
** twos-complement integer. */
pMem->u.i = FOUR_BYTE_INT(buf);
-#ifdef __HP_cc
+#ifdef __HP_cc
/* Work around a sign-extension bug in the HP compiler for HP/UX */
if( buf[0]&0x80 ) pMem->u.i |= 0xffffffff80000000LL;
#endif
pMem->flags = MEM_Int;
testcase( pMem->u.i<0 );
- return 4;
+ return;
}
case 5: { /* 6-byte signed integer */
/* EVIDENCE-OF: R-50385-09674 Value is a big-endian 48-bit
@@ -79466,13 +88440,14 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
pMem->u.i = FOUR_BYTE_UINT(buf+2) + (((i64)1)<<32)*TWO_BYTE_INT(buf);
pMem->flags = MEM_Int;
testcase( pMem->u.i<0 );
- return 6;
+ return;
}
case 6: /* 8-byte signed integer */
case 7: { /* IEEE floating point */
/* These use local variables, so do them in a separate routine
** to avoid having to move the frame pointer in the common case */
- return serialGet(buf,serial_type,pMem);
+ serialGet(buf,serial_type,pMem);
+ return;
}
case 8: /* Integer 0 */
case 9: { /* Integer 1 */
@@ -79480,7 +88455,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
/* EVIDENCE-OF: R-18143-12121 Value is the integer 1. */
pMem->u.i = serial_type-8;
pMem->flags = MEM_Int;
- return 0;
+ return;
}
default: {
/* EVIDENCE-OF: R-14606-31564 Value is a BLOB that is (N-12)/2 bytes in
@@ -79491,10 +88466,10 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
pMem->z = (char *)buf;
pMem->n = (serial_type-12)/2;
pMem->flags = aFlag[serial_type&1];
- return pMem->n;
+ return;
}
}
- return 0;
+ return;
}
/*
** This routine is used to allocate sufficient space for an UnpackedRecord
@@ -79504,7 +88479,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
** The space is either allocated using sqlite3DbMallocRaw() or from within
** the unaligned buffer passed via the second and third arguments (presumably
** stack space). If the former, then *ppFree is set to a pointer that should
-** be eventually freed by the caller using sqlite3DbFree(). Or, if the
+** be eventually freed by the caller using sqlite3DbFree(). Or, if the
** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL
** before returning.
**
@@ -79515,21 +88490,21 @@ SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
){
UnpackedRecord *p; /* Unpacked record to return */
int nByte; /* Number of bytes required for *p */
- nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1);
+ nByte = ROUND8P(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1);
p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
if( !p ) return 0;
- p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
- assert( pKeyInfo->aSortOrder!=0 );
+ p->aMem = (Mem*)&((char*)p)[ROUND8P(sizeof(UnpackedRecord))];
+ assert( pKeyInfo->aSortFlags!=0 );
p->pKeyInfo = pKeyInfo;
p->nField = pKeyInfo->nKeyField + 1;
return p;
}
/*
-** Given the nKey-byte encoding of a record in pKey[], populate the
+** Given the nKey-byte encoding of a record in pKey[], populate the
** UnpackedRecord structure indicated by the fourth argument with the
** contents of the decoded record.
-*/
+*/
SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(
KeyInfo *pKeyInfo, /* Information about the record format */
int nKey, /* Size of the binary record */
@@ -79537,7 +88512,7 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(
UnpackedRecord *p /* Populate this structure before returning. */
){
const unsigned char *aKey = (const unsigned char *)pKey;
- int d;
+ u32 d;
u32 idx; /* Offset in aKey[] to read from */
u16 u; /* Unsigned loop counter */
u32 szHdr;
@@ -79548,7 +88523,7 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(
idx = getVarint32(aKey, szHdr);
d = szHdr;
u = 0;
- while( idx<szHdr && d<=nKey ){
+ while( idx<szHdr && d<=(u32)nKey ){
u32 serial_type;
idx += getVarint32(&aKey[idx], serial_type);
@@ -79557,10 +88532,18 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(
/* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
pMem->szMalloc = 0;
pMem->z = 0;
- d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
+ sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
+ d += sqlite3VdbeSerialTypeLen(serial_type);
pMem++;
if( (++u)>=p->nField ) break;
}
+ if( d>(u32)nKey && u ){
+ assert( CORRUPT_DB );
+ /* In a corrupt record entry, the last pMem might have been set up using
+ ** uninitialized memory. Overwrite its value with NULL, to prevent
+ ** warnings from MSAN. */
+ sqlite3VdbeMemSetNull(pMem-1);
+ }
assert( u<=pKeyInfo->nKeyField + 1 );
p->nField = u;
}
@@ -79600,18 +88583,18 @@ static int vdbeRecordCompareDebug(
/* Compilers may complain that mem1.u.i is potentially uninitialized.
** We could initialize it, as shown here, to silence those complaints.
- ** But in fact, mem1.u.i will never actually be used uninitialized, and doing
+ ** But in fact, mem1.u.i will never actually be used uninitialized, and doing
** the unnecessary initialization has a measurable negative performance
** impact, since this routine is a very high runner. And so, we choose
** to ignore the compiler warnings and leave this variable uninitialized.
*/
/* mem1.u.i = 0; // not needed, here to silence compiler warning */
-
+
idx1 = getVarint32(aKey1, szHdr1);
if( szHdr1>98307 ) return SQLITE_CORRUPT;
d1 = szHdr1;
assert( pKeyInfo->nAllField>=pPKey2->nField || CORRUPT_DB );
- assert( pKeyInfo->aSortOrder!=0 );
+ assert( pKeyInfo->aSortFlags!=0 );
assert( pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
do{
@@ -79626,22 +88609,38 @@ static int vdbeRecordCompareDebug(
** Use that approximation to avoid the more expensive call to
** sqlite3VdbeSerialTypeLen() in the common case.
*/
- if( d1+serial_type1+2>(u32)nKey1
- && d1+sqlite3VdbeSerialTypeLen(serial_type1)>(u32)nKey1
+ if( d1+(u64)serial_type1+2>(u64)nKey1
+ && d1+(u64)sqlite3VdbeSerialTypeLen(serial_type1)>(u64)nKey1
){
+ if( serial_type1>=1
+ && serial_type1<=7
+ && d1+(u64)sqlite3VdbeSerialTypeLen(serial_type1)<=(u64)nKey1+8
+ && CORRUPT_DB
+ ){
+ return 1; /* corrupt record not detected by
+ ** sqlite3VdbeRecordCompareWithSkip(). Return true
+ ** to avoid firing the assert() */
+ }
break;
}
/* Extract the values to be compared.
*/
- d1 += sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1);
+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1);
+ d1 += sqlite3VdbeSerialTypeLen(serial_type1);
/* Do the comparison
*/
- rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], pKeyInfo->aColl[i]);
+ rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i],
+ pKeyInfo->nAllField>i ? pKeyInfo->aColl[i] : 0);
if( rc!=0 ){
assert( mem1.szMalloc==0 ); /* See comment below */
- if( pKeyInfo->aSortOrder[i] ){
+ if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL)
+ && ((mem1.flags & MEM_Null) || (pPKey2->aMem[i].flags & MEM_Null))
+ ){
+ rc = -rc;
+ }
+ if( pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC ){
rc = -rc; /* Invert the result for DESC sort order. */
}
goto debugCompareEnd;
@@ -79683,7 +88682,7 @@ debugCompareEnd:
** incorrectly.
*/
static void vdbeAssertFieldCountWithinLimits(
- int nKey, const void *pKey, /* The record to verify */
+ int nKey, const void *pKey, /* The record to verify */
const KeyInfo *pKeyInfo /* Compare size with this KeyInfo */
){
int nField = 0;
@@ -79709,7 +88708,7 @@ static void vdbeAssertFieldCountWithinLimits(
/*
** Both *pMem1 and *pMem2 contain string values. Compare the two values
** using the collation sequence pColl. As usual, return a negative , zero
-** or positive value if *pMem1 is less than, equal to or greater than
+** or positive value if *pMem1 is less than, equal to or greater than
** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
*/
static int vdbeCompareMemString(
@@ -79739,8 +88738,8 @@ static int vdbeCompareMemString(
}else{
rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
}
- sqlite3VdbeMemRelease(&c1);
- sqlite3VdbeMemRelease(&c2);
+ sqlite3VdbeMemReleaseMalloc(&c1);
+ sqlite3VdbeMemReleaseMalloc(&c2);
return rc;
}
}
@@ -79790,17 +88789,33 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem
return n1 - n2;
}
+/* The following two functions are used only within testcase() to prove
+** test coverage. These functions do no exist for production builds.
+** We must use separate SQLITE_NOINLINE functions here, since otherwise
+** optimizer code movement causes gcov to become very confused.
+*/
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG)
+static int SQLITE_NOINLINE doubleLt(double a, double b){ return a<b; }
+static int SQLITE_NOINLINE doubleEq(double a, double b){ return a==b; }
+#endif
+
/*
** Do a comparison between a 64-bit signed integer and a 64-bit floating-point
** number. Return negative, zero, or positive if the first (i64) is less than,
** equal to, or greater than the second (double).
*/
-static int sqlite3IntFloatCompare(i64 i, double r){
- if( sizeof(LONGDOUBLE_TYPE)>8 ){
+SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){
+ if( sqlite3IsNaN(r) ){
+ /* SQLite considers NaN to be a NULL. And all integer values are greater
+ ** than NULL */
+ return 1;
+ }
+ if( sqlite3Config.bUseLongDouble ){
LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i;
- if( x<r ) return -1;
- if( x>r ) return +1;
- return 0;
+ testcase( x<r );
+ testcase( x>r );
+ testcase( x==r );
+ return (x<r) ? -1 : (x>r);
}else{
i64 y;
double s;
@@ -79810,9 +88825,10 @@ static int sqlite3IntFloatCompare(i64 i, double r){
if( i<y ) return -1;
if( i>y ) return +1;
s = (double)i;
- if( s<r ) return -1;
- if( s>r ) return +1;
- return 0;
+ testcase( doubleLt(s,r) );
+ testcase( doubleLt(r,s) );
+ testcase( doubleEq(r,s) );
+ return (s<r) ? -1 : (s>r);
}
}
@@ -79833,7 +88849,7 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const C
f2 = pMem2->flags;
combined_flags = f1|f2;
assert( !sqlite3VdbeMemIsRowSet(pMem1) && !sqlite3VdbeMemIsRowSet(pMem2) );
-
+
/* If one value is NULL, it is less than the other. If both values
** are NULL, return 0.
*/
@@ -79843,8 +88859,13 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const C
/* At least one of the two values is a number
*/
- if( combined_flags&(MEM_Int|MEM_Real) ){
- if( (f1 & f2 & MEM_Int)!=0 ){
+ if( combined_flags&(MEM_Int|MEM_Real|MEM_IntReal) ){
+ testcase( combined_flags & MEM_Int );
+ testcase( combined_flags & MEM_Real );
+ testcase( combined_flags & MEM_IntReal );
+ if( (f1 & f2 & (MEM_Int|MEM_IntReal))!=0 ){
+ testcase( f1 & f2 & MEM_Int );
+ testcase( f1 & f2 & MEM_IntReal );
if( pMem1->u.i < pMem2->u.i ) return -1;
if( pMem1->u.i > pMem2->u.i ) return +1;
return 0;
@@ -79854,15 +88875,23 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const C
if( pMem1->u.r > pMem2->u.r ) return +1;
return 0;
}
- if( (f1&MEM_Int)!=0 ){
+ if( (f1&(MEM_Int|MEM_IntReal))!=0 ){
+ testcase( f1 & MEM_Int );
+ testcase( f1 & MEM_IntReal );
if( (f2&MEM_Real)!=0 ){
return sqlite3IntFloatCompare(pMem1->u.i, pMem2->u.r);
+ }else if( (f2&(MEM_Int|MEM_IntReal))!=0 ){
+ if( pMem1->u.i < pMem2->u.i ) return -1;
+ if( pMem1->u.i > pMem2->u.i ) return +1;
+ return 0;
}else{
return -1;
}
}
if( (f1&MEM_Real)!=0 ){
- if( (f2&MEM_Int)!=0 ){
+ if( (f2&(MEM_Int|MEM_IntReal))!=0 ){
+ testcase( f2 & MEM_Int );
+ testcase( f2 & MEM_IntReal );
return -sqlite3IntFloatCompare(pMem2->u.i, pMem1->u.r);
}else{
return -1;
@@ -79883,7 +88912,7 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const C
}
assert( pMem1->enc==pMem2->enc || pMem1->db->mallocFailed );
- assert( pMem1->enc==SQLITE_UTF8 ||
+ assert( pMem1->enc==SQLITE_UTF8 ||
pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE );
/* The collation sequence must be defined at this point, even if
@@ -79898,7 +88927,7 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const C
/* If a NULL pointer was passed as the collate function, fall through
** to the blob case and use memcmp(). */
}
-
+
/* Both values must be blobs. Compare using memcmp(). */
return sqlite3BlobCompare(pMem1, pMem2);
}
@@ -79906,7 +88935,7 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const C
/*
** The first argument passed to this function is a serial-type that
-** corresponds to an integer - all values between 1 and 9 inclusive
+** corresponds to an integer - all values between 1 and 9 inclusive
** except 7. The second points to a buffer containing an integer value
** serialized according to serial_type. This function deserializes
** and returns the value.
@@ -79948,7 +88977,7 @@ static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){
/*
** This function compares the two table rows or index records
** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero
-** or positive integer if key1 is less than, equal to or
+** or positive integer if key1 is less than, equal to or
** greater than key2. The {nKey1, pKey1} key must be a blob
** created by the OP_MakeRecord opcode of the VDBE. The pPKey2
** key must be a parsed key such as obtained from
@@ -79957,12 +88986,12 @@ static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){
** If argument bSkip is non-zero, it is assumed that the caller has already
** determined that the first fields of the keys are equal.
**
-** Key1 and Key2 do not have to contain the same number of fields. If all
-** fields that appear in both keys are equal, then pPKey2->default_rc is
+** Key1 and Key2 do not have to contain the same number of fields. If all
+** fields that appear in both keys are equal, then pPKey2->default_rc is
** returned.
**
-** If database corruption is discovered, set pPKey2->errCode to
-** SQLITE_CORRUPT and return 0. If an OOM error is encountered,
+** If database corruption is discovered, set pPKey2->errCode to
+** SQLITE_CORRUPT and return 0. If an OOM error is encountered,
** pPKey2->errCode is set to SQLITE_NOMEM and, if it is not NULL, the
** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db).
*/
@@ -79985,37 +89014,47 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
** two elements in the keys are equal. Fix the various stack variables so
** that this routine begins comparing at the second field. */
if( bSkip ){
- u32 s1;
- idx1 = 1 + getVarint32(&aKey1[1], s1);
+ u32 s1 = aKey1[1];
+ if( s1<0x80 ){
+ idx1 = 2;
+ }else{
+ idx1 = 1 + sqlite3GetVarint32(&aKey1[1], &s1);
+ }
szHdr1 = aKey1[0];
d1 = szHdr1 + sqlite3VdbeSerialTypeLen(s1);
i = 1;
pRhs++;
}else{
- idx1 = getVarint32(aKey1, szHdr1);
- d1 = szHdr1;
- if( d1>(unsigned)nKey1 ){
- pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
- return 0; /* Corruption */
+ if( (szHdr1 = aKey1[0])<0x80 ){
+ idx1 = 1;
+ }else{
+ idx1 = sqlite3GetVarint32(aKey1, &szHdr1);
}
+ d1 = szHdr1;
i = 0;
}
+ if( d1>(unsigned)nKey1 ){
+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }
VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */
- assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField
+ assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField
|| CORRUPT_DB );
- assert( pPKey2->pKeyInfo->aSortOrder!=0 );
+ assert( pPKey2->pKeyInfo->aSortFlags!=0 );
assert( pPKey2->pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
- do{
+ while( 1 /*exit-by-break*/ ){
u32 serial_type;
/* RHS is an integer */
- if( pRhs->flags & MEM_Int ){
+ if( pRhs->flags & (MEM_Int|MEM_IntReal) ){
+ testcase( pRhs->flags & MEM_Int );
+ testcase( pRhs->flags & MEM_IntReal );
serial_type = aKey1[idx1];
testcase( serial_type==12 );
if( serial_type>=10 ){
- rc = +1;
+ rc = serial_type==10 ? -1 : +1;
}else if( serial_type==0 ){
rc = -1;
}else if( serial_type==7 ){
@@ -80037,10 +89076,10 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
serial_type = aKey1[idx1];
if( serial_type>=10 ){
/* Serial types 12 or greater are strings and blobs (greater than
- ** numbers). Types 10 and 11 are currently "reserved for future
+ ** numbers). Types 10 and 11 are currently "reserved for future
** use", so it doesn't really matter what the results of comparing
- ** them to numberic values are. */
- rc = +1;
+ ** them to numeric values are. */
+ rc = serial_type==10 ? -1 : +1;
}else if( serial_type==0 ){
rc = -1;
}else{
@@ -80059,7 +89098,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
/* RHS is a string */
else if( pRhs->flags & MEM_Str ){
- getVarint32(&aKey1[idx1], serial_type);
+ getVarint32NR(&aKey1[idx1], serial_type);
testcase( serial_type==12 );
if( serial_type<12 ){
rc = -1;
@@ -80069,10 +89108,12 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
mem1.n = (serial_type - 12) / 2;
testcase( (d1+mem1.n)==(unsigned)nKey1 );
testcase( (d1+mem1.n+1)==(unsigned)nKey1 );
- if( (d1+mem1.n) > (unsigned)nKey1 ){
+ if( (d1+mem1.n) > (unsigned)nKey1
+ || (pKeyInfo = pPKey2->pKeyInfo)->nAllField<=i
+ ){
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
return 0; /* Corruption */
- }else if( (pKeyInfo = pPKey2->pKeyInfo)->aColl[i] ){
+ }else if( pKeyInfo->aColl[i] ){
mem1.enc = pKeyInfo->enc;
mem1.db = pKeyInfo->db;
mem1.flags = MEM_Str;
@@ -80083,7 +89124,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
}else{
int nCmp = MIN(mem1.n, pRhs->n);
rc = memcmp(&aKey1[d1], pRhs->z, nCmp);
- if( rc==0 ) rc = mem1.n - pRhs->n;
+ if( rc==0 ) rc = mem1.n - pRhs->n;
}
}
}
@@ -80091,7 +89132,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
/* RHS is a blob */
else if( pRhs->flags & MEM_Blob ){
assert( (pRhs->flags & MEM_Zero)==0 || pRhs->n==0 );
- getVarint32(&aKey1[idx1], serial_type);
+ getVarint32NR(&aKey1[idx1], serial_type);
testcase( serial_type==12 );
if( serial_type<12 || (serial_type & 0x01) ){
rc = -1;
@@ -80119,12 +89160,18 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
/* RHS is null */
else{
serial_type = aKey1[idx1];
- rc = (serial_type!=0);
+ rc = (serial_type!=0 && serial_type!=10);
}
if( rc!=0 ){
- if( pPKey2->pKeyInfo->aSortOrder[i] ){
- rc = -rc;
+ int sortFlags = pPKey2->pKeyInfo->aSortFlags[i];
+ if( sortFlags ){
+ if( (sortFlags & KEYINFO_ORDER_BIGNULL)==0
+ || ((sortFlags & KEYINFO_ORDER_DESC)
+ !=(serial_type==0 || (pRhs->flags&MEM_Null)))
+ ){
+ rc = -rc;
+ }
}
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) );
assert( mem1.szMalloc==0 ); /* See comment below */
@@ -80135,8 +89182,13 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
if( i==pPKey2->nField ) break;
pRhs++;
d1 += sqlite3VdbeSerialTypeLen(serial_type);
+ if( d1>(unsigned)nKey1 ) break;
idx1 += sqlite3VarintLen(serial_type);
- }while( idx1<(unsigned)szHdr1 && d1<=(unsigned)nKey1 );
+ if( idx1>=(unsigned)szHdr1 ){
+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corrupt index */
+ }
+ }
/* No memory allocation is ever used on mem1. Prove this using
** the following assert(). If the assert() fails, it indicates a
@@ -80146,8 +89198,8 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
/* rc==0 here means that one or both of the keys ran out of fields and
** all the fields up to that point were equal. Return the default_rc
** value. */
- assert( CORRUPT_DB
- || vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc)
+ assert( CORRUPT_DB
+ || vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc)
|| pPKey2->pKeyInfo->db->mallocFailed
);
pPKey2->eqSeen = 1;
@@ -80162,8 +89214,8 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
/*
-** This function is an optimized version of sqlite3VdbeRecordCompare()
-** that (a) the first field of pPKey2 is an integer, and (b) the
+** This function is an optimized version of sqlite3VdbeRecordCompare()
+** that (a) the first field of pPKey2 is an integer, and (b) the
** size-of-header varint at the start of (pKey1/nKey1) fits in a single
** byte (i.e. is less than 128).
**
@@ -80218,7 +89270,7 @@ static int vdbeRecordCompareInt(
testcase( lhs<0 );
break;
}
- case 8:
+ case 8:
lhs = 0;
break;
case 9:
@@ -80226,11 +89278,11 @@ static int vdbeRecordCompareInt(
break;
/* This case could be removed without changing the results of running
- ** this code. Including it causes gcc to generate a faster switch
+ ** this code. Including it causes gcc to generate a faster switch
** statement (since the range of switch targets now starts at zero and
** is contiguous) but does not cause any duplicate code to be generated
- ** (as gcc is clever enough to combine the two like cases). Other
- ** compilers might be similar. */
+ ** (as gcc is clever enough to combine the two like cases). Other
+ ** compilers might be similar. */
case 0: case 7:
return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2);
@@ -80238,13 +89290,14 @@ static int vdbeRecordCompareInt(
return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2);
}
- v = pPKey2->aMem[0].u.i;
+ assert( pPKey2->u.i == pPKey2->aMem[0].u.i );
+ v = pPKey2->u.i;
if( v>lhs ){
res = pPKey2->r1;
}else if( v<lhs ){
res = pPKey2->r2;
}else if( pPKey2->nField>1 ){
- /* The first fields of the two keys are equal. Compare the trailing
+ /* The first fields of the two keys are equal. Compare the trailing
** fields. */
res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
}else{
@@ -80259,9 +89312,9 @@ static int vdbeRecordCompareInt(
}
/*
-** This function is an optimized version of sqlite3VdbeRecordCompare()
+** This function is an optimized version of sqlite3VdbeRecordCompare()
** that (a) the first field of pPKey2 is a string, that (b) the first field
-** uses the collation sequence BINARY and (c) that the size-of-header varint
+** uses the collation sequence BINARY and (c) that the size-of-header varint
** at the start of (pKey1/nKey1) fits in a single byte.
*/
static int vdbeRecordCompareString(
@@ -80273,11 +89326,20 @@ static int vdbeRecordCompareString(
int res;
assert( pPKey2->aMem[0].flags & MEM_Str );
+ assert( pPKey2->aMem[0].n == pPKey2->n );
+ assert( pPKey2->aMem[0].z == pPKey2->u.z );
vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo);
- getVarint32(&aKey1[1], serial_type);
+ serial_type = (signed char)(aKey1[1]);
+
+vrcs_restart:
if( serial_type<12 ){
+ if( serial_type<0 ){
+ sqlite3GetVarint32(&aKey1[1], (u32*)&serial_type);
+ if( serial_type>=12 ) goto vrcs_restart;
+ assert( CORRUPT_DB );
+ }
res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */
- }else if( !(serial_type & 0x01) ){
+ }else if( !(serial_type & 0x01) ){
res = pPKey2->r2; /* (pKey1/nKey1) is a blob */
}else{
int nCmp;
@@ -80289,11 +89351,15 @@ static int vdbeRecordCompareString(
pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
return 0; /* Corruption */
}
- nCmp = MIN( pPKey2->aMem[0].n, nStr );
- res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp);
+ nCmp = MIN( pPKey2->n, nStr );
+ res = memcmp(&aKey1[szHdr], pPKey2->u.z, nCmp);
- if( res==0 ){
- res = nStr - pPKey2->aMem[0].n;
+ if( res>0 ){
+ res = pPKey2->r2;
+ }else if( res<0 ){
+ res = pPKey2->r1;
+ }else{
+ res = nStr - pPKey2->n;
if( res==0 ){
if( pPKey2->nField>1 ){
res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
@@ -80306,10 +89372,6 @@ static int vdbeRecordCompareString(
}else{
res = pPKey2->r1;
}
- }else if( res>0 ){
- res = pPKey2->r2;
- }else{
- res = pPKey2->r1;
}
}
@@ -80329,7 +89391,7 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
/* varintRecordCompareInt() and varintRecordCompareString() both assume
** that the size-of-header varint that occurs at the start of each record
** fits in a single byte (i.e. is 127 or less). varintRecordCompareInt()
- ** also assumes that it is safe to overread a buffer by at least the
+ ** also assumes that it is safe to overread a buffer by at least the
** maximum possible legal header size plus 8 bytes. Because there is
** guaranteed to be at least 74 (but not 136) bytes of padding following each
** buffer passed to varintRecordCompareInt() this makes it convenient to
@@ -80341,7 +89403,10 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
** header size is (12*5 + 1 + 1) bytes. */
if( p->pKeyInfo->nAllField<=13 ){
int flags = p->aMem[0].flags;
- if( p->pKeyInfo->aSortOrder[0] ){
+ if( p->pKeyInfo->aSortFlags[0] ){
+ if( p->pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL ){
+ return sqlite3VdbeRecordCompare;
+ }
p->r1 = 1;
p->r2 = -1;
}else{
@@ -80349,13 +89414,18 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
p->r2 = 1;
}
if( (flags & MEM_Int) ){
+ p->u.i = p->aMem[0].u.i;
return vdbeRecordCompareInt;
}
testcase( flags & MEM_Real );
testcase( flags & MEM_Null );
testcase( flags & MEM_Blob );
- if( (flags & (MEM_Real|MEM_Null|MEM_Blob))==0 && p->pKeyInfo->aColl[0]==0 ){
+ if( (flags & (MEM_Real|MEM_IntReal|MEM_Null|MEM_Blob))==0
+ && p->pKeyInfo->aColl[0]==0
+ ){
assert( flags & MEM_Str );
+ p->u.z = p->aMem[0].z;
+ p->n = p->aMem[0].n;
return vdbeRecordCompareString;
}
}
@@ -80382,7 +89452,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
/* Get the size of the index entry. Only indices entries of less
** than 2GiB are support - anything large must be database corruption.
** Any corruption is detected in sqlite3BtreeParseCellPtr(), though, so
- ** this code can safely assume that nCellKey is 32-bits
+ ** this code can safely assume that nCellKey is 32-bits
*/
assert( sqlite3BtreeCursorIsValid(pCur) );
nCellKey = sqlite3BtreePayloadSize(pCur);
@@ -80390,15 +89460,15 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
/* Read in the complete content of the index entry */
sqlite3VdbeMemInit(&m, db, 0);
- rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, &m);
+ rc = sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m);
if( rc ){
return rc;
}
/* The index entry must begin with a header size */
- (void)getVarint32((u8*)m.z, szHdr);
+ getVarint32NR((u8*)m.z, szHdr);
testcase( szHdr==3 );
- testcase( szHdr==m.n );
+ testcase( szHdr==(u32)m.n );
testcase( szHdr>0x7fffffff );
assert( m.n>=0 );
if( unlikely(szHdr<3 || szHdr>(unsigned)m.n) ){
@@ -80407,7 +89477,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
/* The last field of the index should be an integer - the ROWID.
** Verify that the last entry really is an integer. */
- (void)getVarint32((u8*)&m.z[szHdr-1], typeRowid);
+ getVarint32NR((u8*)&m.z[szHdr-1], typeRowid);
testcase( typeRowid==1 );
testcase( typeRowid==2 );
testcase( typeRowid==3 );
@@ -80428,14 +89498,14 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
/* Fetch the integer off the end of the index record */
sqlite3VdbeSerialGet((u8*)&m.z[m.n-lenRowid], typeRowid, &v);
*rowid = v.u.i;
- sqlite3VdbeMemRelease(&m);
+ sqlite3VdbeMemReleaseMalloc(&m);
return SQLITE_OK;
/* Jump here if database corruption is detected after m has been
** allocated. Free the m object and return SQLITE_CORRUPT. */
idx_rowid_corruption:
testcase( m.szMalloc!=0 );
- sqlite3VdbeMemRelease(&m);
+ sqlite3VdbeMemReleaseMalloc(&m);
return SQLITE_CORRUPT_BKPT;
}
@@ -80447,7 +89517,7 @@ idx_rowid_corruption:
**
** pUnpacked is either created without a rowid or is truncated so that it
** omits the rowid at the end. The rowid at the end of the index entry
-** is ignored as well. Hence, this routine only compares the prefixes
+** is ignored as well. Hence, this routine only compares the prefixes
** of the keys prior to the final rowid, not the entire key.
*/
SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(
@@ -80472,20 +89542,20 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(
return SQLITE_CORRUPT_BKPT;
}
sqlite3VdbeMemInit(&m, db, 0);
- rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, &m);
+ rc = sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m);
if( rc ){
return rc;
}
*res = sqlite3VdbeRecordCompareWithSkip(m.n, m.z, pUnpacked, 0);
- sqlite3VdbeMemRelease(&m);
+ sqlite3VdbeMemReleaseMalloc(&m);
return SQLITE_OK;
}
/*
** This routine sets the value to be returned by subsequent calls to
-** sqlite3_changes() on the database handle 'db'.
+** sqlite3_changes() on the database handle 'db'.
*/
-SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *db, int nChange){
+SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *db, i64 nChange){
assert( sqlite3_mutex_held(db->mutex) );
db->nChange = nChange;
db->nTotalChange += nChange;
@@ -80519,7 +89589,7 @@ SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe *v){
*/
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3 *db, int iCode){
Vdbe *p;
- for(p = db->pVdbe; p; p=p->pNext){
+ for(p = db->pVdbe; p; p=p->pVNext){
p->expired = iCode+1;
}
}
@@ -80540,7 +89610,7 @@ SQLITE_PRIVATE u8 sqlite3VdbePrepareFlags(Vdbe *v){
/*
** Return a pointer to an sqlite3_value structure containing the value bound
-** parameter iVar of VM v. Except, if the value is an SQL NULL, return
+** parameter iVar of VM v. Except, if the value is an SQL NULL, return
** 0 instead. Unless it is NULL, apply affinity aff (one of the SQLITE_AFF_*
** constants) to the value before returning it.
**
@@ -80588,18 +89658,44 @@ SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
** features such as 'now'.
*/
SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context *pCtx){
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ const VdbeOp *pOp;
+#ifdef SQLITE_ENABLE_STAT4
if( pCtx->pVdbe==0 ) return 1;
#endif
- if( pCtx->pVdbe->aOp[pCtx->iOp].opcode==OP_PureFunc ){
- sqlite3_result_error(pCtx,
- "non-deterministic function in index expression or CHECK constraint",
- -1);
+ pOp = pCtx->pVdbe->aOp + pCtx->iOp;
+ if( pOp->opcode==OP_PureFunc ){
+ const char *zContext;
+ char *zMsg;
+ if( pOp->p5 & NC_IsCheck ){
+ zContext = "a CHECK constraint";
+ }else if( pOp->p5 & NC_GenCol ){
+ zContext = "a generated column";
+ }else{
+ zContext = "an index";
+ }
+ zMsg = sqlite3_mprintf("non-deterministic use of %s() in %s",
+ pCtx->pFunc->zName, zContext);
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
return 0;
}
return 1;
}
+#if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG)
+/*
+** This Walker callback is used to help verify that calls to
+** sqlite3BtreeCursorHint() with opcode BTREE_HINT_RANGE have
+** byte-code register values correctly initialized.
+*/
+SQLITE_PRIVATE int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_REGISTER ){
+ assert( (pWalker->u.aMem[pExpr->iTable].flags & MEM_Undefined)==0 );
+ }
+ return WRC_Continue;
+}
+#endif /* SQLITE_ENABLE_CURSOR_HINTS && SQLITE_DEBUG */
+
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
@@ -80620,7 +89716,7 @@ SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
-** If the second argument is not NULL, release any allocations associated
+** If the second argument is not NULL, release any allocations associated
** with the memory cells in the p->aMem[] array. Also free the UnpackedRecord
** structure itself, using sqlite3DbFree().
**
@@ -80628,13 +89724,14 @@ SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
** the vdbeUnpackRecord() function found in vdbeapi.c.
*/
static void vdbeFreeUnpacked(sqlite3 *db, int nField, UnpackedRecord *p){
+ assert( db!=0 );
if( p ){
int i;
for(i=0; i<nField; i++){
Mem *pMem = &p->aMem[i];
- if( pMem->zMalloc ) sqlite3VdbeMemRelease(pMem);
+ if( pMem->zMalloc ) sqlite3VdbeMemReleaseMalloc(pMem);
}
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -80653,13 +89750,24 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
const char *zDb, /* Database name */
Table *pTab, /* Modified table */
i64 iKey1, /* Initial key value */
- int iReg /* Register for new.* record */
+ int iReg, /* Register for new.* record */
+ int iBlobWrite
){
sqlite3 *db = v->db;
i64 iKey2;
PreUpdate preupdate;
const char *zTbl = pTab->zName;
static const u8 fakeSortOrder = 0;
+#ifdef SQLITE_DEBUG
+ int nRealCol;
+ if( pTab->tabFlags & TF_WithoutRowid ){
+ nRealCol = sqlite3PrimaryKeyIndex(pTab)->nColumn;
+ }else if( pTab->tabFlags & TF_HasVirtual ){
+ nRealCol = pTab->nNVCol;
+ }else{
+ nRealCol = pTab->nCol;
+ }
+#endif
assert( db->pPreUpdate==0 );
memset(&preupdate, 0, sizeof(PreUpdate));
@@ -80674,8 +89782,10 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
}
}
- assert( pCsr->nField==pTab->nCol
- || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1)
+ assert( pCsr!=0 );
+ assert( pCsr->eCurType==CURTYPE_BTREE );
+ assert( pCsr->nField==nRealCol
+ || (pCsr->nField==nRealCol+1 && op==SQLITE_DELETE && iReg==-1)
);
preupdate.v = v;
@@ -80685,10 +89795,11 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
preupdate.keyinfo.db = db;
preupdate.keyinfo.enc = ENC(db);
preupdate.keyinfo.nKeyField = pTab->nCol;
- preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder;
+ preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder;
preupdate.iKey1 = iKey1;
preupdate.iKey2 = iKey2;
preupdate.pTab = pTab;
+ preupdate.iBlobWrite = iBlobWrite;
db->pPreUpdate = &preupdate;
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
@@ -80701,7 +89812,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
for(i=0; i<pCsr->nField; i++){
sqlite3VdbeMemRelease(&preupdate.aNew[i]);
}
- sqlite3DbFreeNN(db, preupdate.aNew);
+ sqlite3DbNNFreeNN(db, preupdate.aNew);
}
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -80725,6 +89836,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
*/
/* #include "sqliteInt.h" */
/* #include "vdbeInt.h" */
+/* #include "opcodes.h" */
#ifndef SQLITE_OMIT_DEPRECATED
/*
@@ -80772,16 +89884,18 @@ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){
sqlite3_int64 iNow;
sqlite3_int64 iElapse;
assert( p->startTime>0 );
- assert( db->xProfile!=0 || (db->mTrace & SQLITE_TRACE_PROFILE)!=0 );
+ assert( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 );
assert( db->init.busy==0 );
assert( p->zSql!=0 );
sqlite3OsCurrentTimeInt64(db->pVfs, &iNow);
iElapse = (iNow - p->startTime)*1000000;
+#ifndef SQLITE_OMIT_DEPRECATED
if( db->xProfile ){
db->xProfile(db->pProfileArg, p->zSql, iElapse);
}
+#endif
if( db->mTrace & SQLITE_TRACE_PROFILE ){
- db->xTrace(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse);
+ db->trace.xV2(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse);
}
p->startTime = 0;
}
@@ -80816,7 +89930,9 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){
if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT;
sqlite3_mutex_enter(db->mutex);
checkProfileCallback(db, v);
- rc = sqlite3VdbeFinalize(v);
+ assert( v->eVdbeState>=VDBE_READY_STATE );
+ rc = sqlite3VdbeReset(v);
+ sqlite3VdbeDelete(v);
rc = sqlite3ApiExit(db, rc);
sqlite3LeaveMutexAndCloseZombie(db);
}
@@ -80857,7 +89973,15 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
int rc = SQLITE_OK;
Vdbe *p = (Vdbe*)pStmt;
#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex;
+ sqlite3_mutex *mutex;
+#endif
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pStmt==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+#if SQLITE_THREADSAFE
+ mutex = p->db->mutex;
#endif
sqlite3_mutex_enter(mutex);
for(i=0; i<p->nVar; i++){
@@ -80942,47 +90066,102 @@ SQLITE_API const void *sqlite3_value_text16le(sqlite3_value *pVal){
*/
SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){
static const u8 aType[] = {
- SQLITE_BLOB, /* 0x00 */
- SQLITE_NULL, /* 0x01 */
- SQLITE_TEXT, /* 0x02 */
- SQLITE_NULL, /* 0x03 */
- SQLITE_INTEGER, /* 0x04 */
- SQLITE_NULL, /* 0x05 */
- SQLITE_INTEGER, /* 0x06 */
- SQLITE_NULL, /* 0x07 */
- SQLITE_FLOAT, /* 0x08 */
- SQLITE_NULL, /* 0x09 */
- SQLITE_FLOAT, /* 0x0a */
- SQLITE_NULL, /* 0x0b */
- SQLITE_INTEGER, /* 0x0c */
- SQLITE_NULL, /* 0x0d */
- SQLITE_INTEGER, /* 0x0e */
- SQLITE_NULL, /* 0x0f */
- SQLITE_BLOB, /* 0x10 */
- SQLITE_NULL, /* 0x11 */
- SQLITE_TEXT, /* 0x12 */
- SQLITE_NULL, /* 0x13 */
- SQLITE_INTEGER, /* 0x14 */
- SQLITE_NULL, /* 0x15 */
- SQLITE_INTEGER, /* 0x16 */
- SQLITE_NULL, /* 0x17 */
- SQLITE_FLOAT, /* 0x18 */
- SQLITE_NULL, /* 0x19 */
- SQLITE_FLOAT, /* 0x1a */
- SQLITE_NULL, /* 0x1b */
- SQLITE_INTEGER, /* 0x1c */
- SQLITE_NULL, /* 0x1d */
- SQLITE_INTEGER, /* 0x1e */
- SQLITE_NULL, /* 0x1f */
+ SQLITE_BLOB, /* 0x00 (not possible) */
+ SQLITE_NULL, /* 0x01 NULL */
+ SQLITE_TEXT, /* 0x02 TEXT */
+ SQLITE_NULL, /* 0x03 (not possible) */
+ SQLITE_INTEGER, /* 0x04 INTEGER */
+ SQLITE_NULL, /* 0x05 (not possible) */
+ SQLITE_INTEGER, /* 0x06 INTEGER + TEXT */
+ SQLITE_NULL, /* 0x07 (not possible) */
+ SQLITE_FLOAT, /* 0x08 FLOAT */
+ SQLITE_NULL, /* 0x09 (not possible) */
+ SQLITE_FLOAT, /* 0x0a FLOAT + TEXT */
+ SQLITE_NULL, /* 0x0b (not possible) */
+ SQLITE_INTEGER, /* 0x0c (not possible) */
+ SQLITE_NULL, /* 0x0d (not possible) */
+ SQLITE_INTEGER, /* 0x0e (not possible) */
+ SQLITE_NULL, /* 0x0f (not possible) */
+ SQLITE_BLOB, /* 0x10 BLOB */
+ SQLITE_NULL, /* 0x11 (not possible) */
+ SQLITE_TEXT, /* 0x12 (not possible) */
+ SQLITE_NULL, /* 0x13 (not possible) */
+ SQLITE_INTEGER, /* 0x14 INTEGER + BLOB */
+ SQLITE_NULL, /* 0x15 (not possible) */
+ SQLITE_INTEGER, /* 0x16 (not possible) */
+ SQLITE_NULL, /* 0x17 (not possible) */
+ SQLITE_FLOAT, /* 0x18 FLOAT + BLOB */
+ SQLITE_NULL, /* 0x19 (not possible) */
+ SQLITE_FLOAT, /* 0x1a (not possible) */
+ SQLITE_NULL, /* 0x1b (not possible) */
+ SQLITE_INTEGER, /* 0x1c (not possible) */
+ SQLITE_NULL, /* 0x1d (not possible) */
+ SQLITE_INTEGER, /* 0x1e (not possible) */
+ SQLITE_NULL, /* 0x1f (not possible) */
+ SQLITE_FLOAT, /* 0x20 INTREAL */
+ SQLITE_NULL, /* 0x21 (not possible) */
+ SQLITE_FLOAT, /* 0x22 INTREAL + TEXT */
+ SQLITE_NULL, /* 0x23 (not possible) */
+ SQLITE_FLOAT, /* 0x24 (not possible) */
+ SQLITE_NULL, /* 0x25 (not possible) */
+ SQLITE_FLOAT, /* 0x26 (not possible) */
+ SQLITE_NULL, /* 0x27 (not possible) */
+ SQLITE_FLOAT, /* 0x28 (not possible) */
+ SQLITE_NULL, /* 0x29 (not possible) */
+ SQLITE_FLOAT, /* 0x2a (not possible) */
+ SQLITE_NULL, /* 0x2b (not possible) */
+ SQLITE_FLOAT, /* 0x2c (not possible) */
+ SQLITE_NULL, /* 0x2d (not possible) */
+ SQLITE_FLOAT, /* 0x2e (not possible) */
+ SQLITE_NULL, /* 0x2f (not possible) */
+ SQLITE_BLOB, /* 0x30 (not possible) */
+ SQLITE_NULL, /* 0x31 (not possible) */
+ SQLITE_TEXT, /* 0x32 (not possible) */
+ SQLITE_NULL, /* 0x33 (not possible) */
+ SQLITE_FLOAT, /* 0x34 (not possible) */
+ SQLITE_NULL, /* 0x35 (not possible) */
+ SQLITE_FLOAT, /* 0x36 (not possible) */
+ SQLITE_NULL, /* 0x37 (not possible) */
+ SQLITE_FLOAT, /* 0x38 (not possible) */
+ SQLITE_NULL, /* 0x39 (not possible) */
+ SQLITE_FLOAT, /* 0x3a (not possible) */
+ SQLITE_NULL, /* 0x3b (not possible) */
+ SQLITE_FLOAT, /* 0x3c (not possible) */
+ SQLITE_NULL, /* 0x3d (not possible) */
+ SQLITE_FLOAT, /* 0x3e (not possible) */
+ SQLITE_NULL, /* 0x3f (not possible) */
};
+#ifdef SQLITE_DEBUG
+ {
+ int eType = SQLITE_BLOB;
+ if( pVal->flags & MEM_Null ){
+ eType = SQLITE_NULL;
+ }else if( pVal->flags & (MEM_Real|MEM_IntReal) ){
+ eType = SQLITE_FLOAT;
+ }else if( pVal->flags & MEM_Int ){
+ eType = SQLITE_INTEGER;
+ }else if( pVal->flags & MEM_Str ){
+ eType = SQLITE_TEXT;
+ }
+ assert( eType == aType[pVal->flags&MEM_AffMask] );
+ }
+#endif
return aType[pVal->flags&MEM_AffMask];
}
+SQLITE_API int sqlite3_value_encoding(sqlite3_value *pVal){
+ return pVal->enc;
+}
/* Return true if a parameter to xUpdate represents an unchanged column */
SQLITE_API int sqlite3_value_nochange(sqlite3_value *pVal){
return (pVal->flags&(MEM_Null|MEM_Zero))==(MEM_Null|MEM_Zero);
}
+/* Return true if a parameter value originated from an sqlite3_bind() */
+SQLITE_API int sqlite3_value_frombind(sqlite3_value *pVal){
+ return (pVal->flags&MEM_FromBind)!=0;
+}
+
/* Make a copy of an sqlite3_value object
*/
SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){
@@ -81001,6 +90180,9 @@ SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){
sqlite3ValueFree(pNew);
pNew = 0;
}
+ }else if( pNew->flags & MEM_Null ){
+ /* Do not duplicate pointer values */
+ pNew->flags &= ~(MEM_Term|MEM_Subtype);
}
return pNew;
}
@@ -81011,18 +90193,18 @@ SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){
SQLITE_API void sqlite3_value_free(sqlite3_value *pOld){
sqlite3ValueFree(pOld);
}
-
+
/**************************** sqlite3_result_ *******************************
** The following routines are used by user-defined functions to specify
** the function result.
**
** The setStrOrError() function calls sqlite3VdbeMemSetStr() to store the
-** result as a string or blob but if the string or blob is too large, it
-** then sets the error code to SQLITE_TOOBIG
+** result as a string or blob. Appropriate errors are set if the string/blob
+** is too big or if an OOM occurs.
**
** The invokeValueDestructor(P,X) routine invokes destructor function X()
-** on value P is not going to be used and need to be destroyed.
+** on value P if P is not going to be used and need to be destroyed.
*/
static void setResultStrOrError(
sqlite3_context *pCtx, /* Function context */
@@ -81031,14 +90213,28 @@ static void setResultStrOrError(
u8 enc, /* Encoding of z. 0 for BLOBs */
void (*xDel)(void*) /* Destructor function */
){
- if( sqlite3VdbeMemSetStr(pCtx->pOut, z, n, enc, xDel)==SQLITE_TOOBIG ){
+ Mem *pOut = pCtx->pOut;
+ int rc = sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel);
+ if( rc ){
+ if( rc==SQLITE_TOOBIG ){
+ sqlite3_result_error_toobig(pCtx);
+ }else{
+ /* The only errors possible from sqlite3VdbeMemSetStr are
+ ** SQLITE_TOOBIG and SQLITE_NOMEM */
+ assert( rc==SQLITE_NOMEM );
+ sqlite3_result_error_nomem(pCtx);
+ }
+ return;
+ }
+ sqlite3VdbeChangeEncoding(pOut, pCtx->enc);
+ if( sqlite3VdbeMemTooBig(pOut) ){
sqlite3_result_error_toobig(pCtx);
}
}
static int invokeValueDestructor(
const void *p, /* Value to destroy */
void (*xDel)(void*), /* The destructor */
- sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */
+ sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if not NULL */
){
assert( xDel!=SQLITE_DYNAMIC );
if( xDel==0 ){
@@ -81048,27 +90244,46 @@ static int invokeValueDestructor(
}else{
xDel((void*)p);
}
- if( pCtx ) sqlite3_result_error_toobig(pCtx);
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx!=0 ){
+ sqlite3_result_error_toobig(pCtx);
+ }
+#else
+ assert( pCtx!=0 );
+ sqlite3_result_error_toobig(pCtx);
+#endif
return SQLITE_TOOBIG;
}
SQLITE_API void sqlite3_result_blob(
- sqlite3_context *pCtx,
- const void *z,
- int n,
+ sqlite3_context *pCtx,
+ const void *z,
+ int n,
void (*xDel)(void *)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 || n<0 ){
+ invokeValueDestructor(z, xDel, pCtx);
+ return;
+ }
+#endif
assert( n>=0 );
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, 0, xDel);
}
SQLITE_API void sqlite3_result_blob64(
- sqlite3_context *pCtx,
- const void *z,
+ sqlite3_context *pCtx,
+ const void *z,
sqlite3_uint64 n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
assert( xDel!=SQLITE_DYNAMIC );
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
if( n>0x7fffffff ){
(void)invokeValueDestructor(z, xDel, pCtx);
}else{
@@ -81076,30 +90291,48 @@ SQLITE_API void sqlite3_result_blob64(
}
}
SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetDouble(pCtx->pOut, rVal);
}
SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
}
#endif
SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal);
}
SQLITE_API void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetInt64(pCtx->pOut, iVal);
}
SQLITE_API void sqlite3_result_null(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetNull(pCtx->pOut);
}
@@ -81109,118 +90342,204 @@ SQLITE_API void sqlite3_result_pointer(
const char *zPType,
void (*xDestructor)(void*)
){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(pPtr, xDestructor, 0);
+ return;
+ }
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
sqlite3VdbeMemRelease(pOut);
pOut->flags = MEM_Null;
sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor);
}
SQLITE_API void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
+#if defined(SQLITE_STRICT_SUBTYPE) && SQLITE_STRICT_SUBTYPE+0!=0
+ if( pCtx->pFunc!=0
+ && (pCtx->pFunc->funcFlags & SQLITE_RESULT_SUBTYPE)==0
+ ){
+ char zErr[200];
+ sqlite3_snprintf(sizeof(zErr), zErr,
+ "misuse of sqlite3_result_subtype() by %s()",
+ pCtx->pFunc->zName);
+ sqlite3_result_error(pCtx, zErr, -1);
+ return;
+ }
+#endif /* SQLITE_STRICT_SUBTYPE */
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
pOut->eSubtype = eSubtype & 0xff;
pOut->flags |= MEM_Subtype;
}
SQLITE_API void sqlite3_result_text(
- sqlite3_context *pCtx,
- const char *z,
+ sqlite3_context *pCtx,
+ const char *z,
int n,
void (*xDel)(void *)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel);
}
SQLITE_API void sqlite3_result_text64(
- sqlite3_context *pCtx,
- const char *z,
+ sqlite3_context *pCtx,
+ const char *z,
sqlite3_uint64 n,
void (*xDel)(void *),
unsigned char enc
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
assert( xDel!=SQLITE_DYNAMIC );
- if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
+ if( enc!=SQLITE_UTF8 ){
+ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
+ n &= ~(u64)1;
+ }
if( n>0x7fffffff ){
(void)invokeValueDestructor(z, xDel, pCtx);
}else{
setResultStrOrError(pCtx, z, (int)n, enc, xDel);
+ sqlite3VdbeMemZeroTerminateIfAble(pCtx->pOut);
}
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API void sqlite3_result_text16(
- sqlite3_context *pCtx,
- const void *z,
- int n,
+ sqlite3_context *pCtx,
+ const void *z,
+ int n,
void (*xDel)(void *)
){
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
- setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel);
+ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16NATIVE, xDel);
}
SQLITE_API void sqlite3_result_text16be(
- sqlite3_context *pCtx,
- const void *z,
- int n,
+ sqlite3_context *pCtx,
+ const void *z,
+ int n,
void (*xDel)(void *)
){
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
- setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel);
+ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16BE, xDel);
}
SQLITE_API void sqlite3_result_text16le(
- sqlite3_context *pCtx,
- const void *z,
- int n,
+ sqlite3_context *pCtx,
+ const void *z,
+ int n,
void (*xDel)(void *)
){
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
- setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel);
+ setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16LE, xDel);
}
#endif /* SQLITE_OMIT_UTF16 */
SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
+ Mem *pOut;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+ if( pValue==0 ){
+ sqlite3_result_null(pCtx);
+ return;
+ }
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
- sqlite3VdbeMemCopy(pCtx->pOut, pValue);
+ sqlite3VdbeMemCopy(pOut, pValue);
+ sqlite3VdbeChangeEncoding(pOut, pCtx->enc);
+ if( sqlite3VdbeMemTooBig(pOut) ){
+ sqlite3_result_error_toobig(pCtx);
+ }
}
SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){
- assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
- sqlite3VdbeMemSetZeroBlob(pCtx->pOut, n);
+ sqlite3_result_zeroblob64(pCtx, n>0 ? n : 0);
}
SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return SQLITE_MISUSE_BKPT;
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ sqlite3_result_error_toobig(pCtx);
return SQLITE_TOOBIG;
}
+#ifndef SQLITE_OMIT_INCRBLOB
sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n);
return SQLITE_OK;
+#else
+ return sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n);
+#endif
}
SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
pCtx->isError = errCode ? errCode : -1;
#ifdef SQLITE_DEBUG
if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode;
#endif
if( pCtx->pOut->flags & MEM_Null ){
- sqlite3VdbeMemSetStr(pCtx->pOut, sqlite3ErrStr(errCode), -1,
- SQLITE_UTF8, SQLITE_STATIC);
+ setResultStrOrError(pCtx, sqlite3ErrStr(errCode), -1, SQLITE_UTF8,
+ SQLITE_STATIC);
}
}
/* Force an SQLITE_TOOBIG error. */
SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_TOOBIG;
- sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1,
+ sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1,
SQLITE_UTF8, SQLITE_STATIC);
}
/* An SQLITE_NOMEM error. */
SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetNull(pCtx->pOut);
pCtx->isError = SQLITE_NOMEM_BKPT;
sqlite3OomFault(pCtx->pOut->db);
}
+#ifndef SQLITE_UNTESTABLE
+/* Force the INT64 value currently stored as the result to be
+** a MEM_IntReal value. See the SQLITE_TESTCTRL_RESULT_INTREAL
+** test-control.
+*/
+SQLITE_PRIVATE void sqlite3ResultIntReal(sqlite3_context *pCtx){
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ if( pCtx->pOut->flags & MEM_Int ){
+ pCtx->pOut->flags &= ~MEM_Int;
+ pCtx->pOut->flags |= MEM_IntReal;
+ }
+}
+#endif
+
+
/*
-** This function is called after a transaction has been committed. It
+** This function is called after a transaction has been committed. It
** invokes callbacks registered with sqlite3_wal_hook() as required.
*/
static int doWalCallbacks(sqlite3 *db){
@@ -81249,7 +90568,7 @@ static int doWalCallbacks(sqlite3 *db){
** statement is completely executed or an error occurs.
**
** This routine implements the bulk of the logic behind the sqlite_step()
-** API. The only thing omitted is the automatic recompile if a
+** API. The only thing omitted is the automatic recompile if a
** schema change has occurred. That detail is handled by the
** outer sqlite3_step() wrapper procedure.
*/
@@ -81258,73 +90577,83 @@ static int sqlite3Step(Vdbe *p){
int rc;
assert(p);
- if( p->magic!=VDBE_MAGIC_RUN ){
- /* We used to require that sqlite3_reset() be called before retrying
- ** sqlite3_step() after any error or after SQLITE_DONE. But beginning
- ** with version 3.7.0, we changed this so that sqlite3_reset() would
- ** be called automatically instead of throwing the SQLITE_MISUSE error.
- ** This "automatic-reset" change is not technically an incompatibility,
- ** since any application that receives an SQLITE_MISUSE is broken by
- ** definition.
- **
- ** Nevertheless, some published applications that were originally written
- ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE
- ** returns, and those were broken by the automatic-reset change. As a
- ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the
- ** legacy behavior of returning SQLITE_MISUSE for cases where the
- ** previous sqlite3_step() returned something other than a SQLITE_LOCKED
- ** or SQLITE_BUSY error.
- */
-#ifdef SQLITE_OMIT_AUTORESET
- if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){
- sqlite3_reset((sqlite3_stmt*)p);
- }else{
- return SQLITE_MISUSE_BKPT;
- }
-#else
- sqlite3_reset((sqlite3_stmt*)p);
-#endif
- }
-
- /* Check that malloc() has not failed. If it has, return early. */
db = p->db;
- if( db->mallocFailed ){
- p->rc = SQLITE_NOMEM;
- return SQLITE_NOMEM_BKPT;
- }
+ if( p->eVdbeState!=VDBE_RUN_STATE ){
+ restart_step:
+ if( p->eVdbeState==VDBE_READY_STATE ){
+ if( p->expired ){
+ p->rc = SQLITE_SCHEMA;
+ rc = SQLITE_ERROR;
+ if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){
+ /* If this statement was prepared using saved SQL and an
+ ** error has occurred, then return the error code in p->rc to the
+ ** caller. Set the error code in the database handle to the same
+ ** value.
+ */
+ rc = sqlite3VdbeTransferError(p);
+ }
+ goto end_of_step;
+ }
- if( p->pc<=0 && p->expired ){
- p->rc = SQLITE_SCHEMA;
- rc = SQLITE_ERROR;
- goto end_of_step;
- }
- if( p->pc<0 ){
- /* If there are no other statements currently running, then
- ** reset the interrupt flag. This prevents a call to sqlite3_interrupt
- ** from interrupting a statement that has not yet started.
- */
- if( db->nVdbeActive==0 ){
- db->u1.isInterrupted = 0;
- }
+ /* If there are no other statements currently running, then
+ ** reset the interrupt flag. This prevents a call to sqlite3_interrupt
+ ** from interrupting a statement that has not yet started.
+ */
+ if( db->nVdbeActive==0 ){
+ AtomicStore(&db->u1.isInterrupted, 0);
+ }
- assert( db->nVdbeWrite>0 || db->autoCommit==0
- || (db->nDeferredCons==0 && db->nDeferredImmCons==0)
- );
+ assert( db->nVdbeWrite>0 || db->autoCommit==0
+ || (db->nDeferredCons==0 && db->nDeferredImmCons==0)
+ );
#ifndef SQLITE_OMIT_TRACE
- if( (db->xProfile || (db->mTrace & SQLITE_TRACE_PROFILE)!=0)
- && !db->init.busy && p->zSql ){
- sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime);
- }else{
- assert( p->startTime==0 );
- }
+ if( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0
+ && !db->init.busy && p->zSql ){
+ sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime);
+ }else{
+ assert( p->startTime==0 );
+ }
#endif
- db->nVdbeActive++;
- if( p->readOnly==0 ) db->nVdbeWrite++;
- if( p->bIsReader ) db->nVdbeRead++;
- p->pc = 0;
+ db->nVdbeActive++;
+ if( p->readOnly==0 ) db->nVdbeWrite++;
+ if( p->bIsReader ) db->nVdbeRead++;
+ p->pc = 0;
+ p->eVdbeState = VDBE_RUN_STATE;
+ }else
+
+ if( ALWAYS(p->eVdbeState==VDBE_HALT_STATE) ){
+ /* We used to require that sqlite3_reset() be called before retrying
+ ** sqlite3_step() after any error or after SQLITE_DONE. But beginning
+ ** with version 3.7.0, we changed this so that sqlite3_reset() would
+ ** be called automatically instead of throwing the SQLITE_MISUSE error.
+ ** This "automatic-reset" change is not technically an incompatibility,
+ ** since any application that receives an SQLITE_MISUSE is broken by
+ ** definition.
+ **
+ ** Nevertheless, some published applications that were originally written
+ ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE
+ ** returns, and those were broken by the automatic-reset change. As a
+ ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the
+ ** legacy behavior of returning SQLITE_MISUSE for cases where the
+ ** previous sqlite3_step() returned something other than a SQLITE_LOCKED
+ ** or SQLITE_BUSY error.
+ */
+#ifdef SQLITE_OMIT_AUTORESET
+ if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){
+ sqlite3_reset((sqlite3_stmt*)p);
+ }else{
+ return SQLITE_MISUSE_BKPT;
+ }
+#else
+ sqlite3_reset((sqlite3_stmt*)p);
+#endif
+ assert( p->eVdbeState==VDBE_READY_STATE );
+ goto restart_step;
+ }
}
+
#ifdef SQLITE_DEBUG
p->rcApp = SQLITE_OK;
#endif
@@ -81339,45 +90668,44 @@ static int sqlite3Step(Vdbe *p){
db->nVdbeExec--;
}
+ if( rc==SQLITE_ROW ){
+ assert( p->rc==SQLITE_OK );
+ assert( db->mallocFailed==0 );
+ db->errCode = SQLITE_ROW;
+ return SQLITE_ROW;
+ }else{
#ifndef SQLITE_OMIT_TRACE
- /* If the statement completed successfully, invoke the profile callback */
- if( rc!=SQLITE_ROW ) checkProfileCallback(db, p);
+ /* If the statement completed successfully, invoke the profile callback */
+ checkProfileCallback(db, p);
#endif
-
- if( rc==SQLITE_DONE && db->autoCommit ){
- assert( p->rc==SQLITE_OK );
- p->rc = doWalCallbacks(db);
- if( p->rc!=SQLITE_OK ){
- rc = SQLITE_ERROR;
+ p->pResultRow = 0;
+ if( rc==SQLITE_DONE && db->autoCommit ){
+ assert( p->rc==SQLITE_OK );
+ p->rc = doWalCallbacks(db);
+ if( p->rc!=SQLITE_OK ){
+ rc = SQLITE_ERROR;
+ }
+ }else if( rc!=SQLITE_DONE && (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){
+ /* If this statement was prepared using saved SQL and an
+ ** error has occurred, then return the error code in p->rc to the
+ ** caller. Set the error code in the database handle to the same value.
+ */
+ rc = sqlite3VdbeTransferError(p);
}
}
db->errCode = rc;
if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){
p->rc = SQLITE_NOMEM_BKPT;
+ if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ) rc = p->rc;
}
end_of_step:
- /* At this point local variable rc holds the value that should be
- ** returned if this statement was compiled using the legacy
- ** sqlite3_prepare() interface. According to the docs, this can only
- ** be one of the values in the first assert() below. Variable p->rc
- ** contains the value that would be returned if sqlite3_finalize()
- ** were called on statement p.
- */
- assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR
+ /* There are only a limited number of result codes allowed from the
+ ** statements prepared using the legacy sqlite3_prepare() interface */
+ assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0
+ || rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR
|| (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE
);
- assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp );
- if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0
- && rc!=SQLITE_ROW
- && rc!=SQLITE_DONE
- ){
- /* If this statement was prepared using saved SQL and an
- ** error has occurred, then return the error code in p->rc to the
- ** caller. Set the error code in the database handle to the same value.
- */
- rc = sqlite3VdbeTransferError(p);
- }
return (rc&db->errMask);
}
@@ -81397,21 +90725,20 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
}
db = v->db;
sqlite3_mutex_enter(db->mutex);
- v->doingRerun = 0;
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
&& cnt++ < SQLITE_MAX_SCHEMA_RETRY ){
int savedPc = v->pc;
rc = sqlite3Reprepare(v);
if( rc!=SQLITE_OK ){
- /* This case occurs after failing to recompile an sql statement.
- ** The error message from the SQL compiler has already been loaded
- ** into the database handle. This block copies the error message
+ /* This case occurs after failing to recompile an sql statement.
+ ** The error message from the SQL compiler has already been loaded
+ ** into the database handle. This block copies the error message
** from the database handle into the statement and sets the statement
- ** program counter to 0 to ensure that when the statement is
+ ** program counter to 0 to ensure that when the statement is
** finalized or reset the parser error message is available via
** sqlite3_errmsg() and sqlite3_errcode().
*/
- const char *zErr = (const char *)sqlite3_value_text(db->pErr);
+ const char *zErr = (const char *)sqlite3_value_text(db->pErr);
sqlite3DbFree(db, v->zErrMsg);
if( !db->mallocFailed ){
v->zErrMsg = sqlite3DbStrDup(db, zErr);
@@ -81423,7 +90750,13 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
break;
}
sqlite3_reset(pStmt);
- if( savedPc>=0 ) v->doingRerun = 1;
+ if( savedPc>=0 ){
+ /* Setting minWriteFileFormat to 254 is a signal to the OP_Init and
+ ** OP_Trace opcodes to *not* perform SQLITE_TRACE_STMT because it has
+ ** already been done once on a prior invocation that failed due to
+ ** SQLITE_SCHEMA. tag-20220401a */
+ v->minWriteFileFormat = 254;
+ }
assert( v->expired==0 );
}
sqlite3_mutex_leave(db->mutex);
@@ -81436,6 +90769,9 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
** pointer to it.
*/
SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#endif
assert( p && p->pFunc );
return p->pFunc->pUserData;
}
@@ -81451,7 +90787,11 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
** application defined function.
*/
SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#else
assert( p && p->pOut );
+#endif
return p->pOut->db;
}
@@ -81470,11 +90810,97 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
** value, as a signal to the xUpdate routine that the column is unchanged.
*/
SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#else
assert( p );
+#endif
return sqlite3_value_nochange(p->pOut);
}
/*
+** The destructor function for a ValueList object. This needs to be
+** a separate function, unknowable to the application, to ensure that
+** calls to sqlite3_vtab_in_first()/sqlite3_vtab_in_next() that are not
+** preceded by activation of IN processing via sqlite3_vtab_int() do not
+** try to access a fake ValueList object inserted by a hostile extension.
+*/
+SQLITE_PRIVATE void sqlite3VdbeValueListFree(void *pToDelete){
+ sqlite3_free(pToDelete);
+}
+
+/*
+** Implementation of sqlite3_vtab_in_first() (if bNext==0) and
+** sqlite3_vtab_in_next() (if bNext!=0).
+*/
+static int valueFromValueList(
+ sqlite3_value *pVal, /* Pointer to the ValueList object */
+ sqlite3_value **ppOut, /* Store the next value from the list here */
+ int bNext /* 1 for _next(). 0 for _first() */
+){
+ int rc;
+ ValueList *pRhs;
+
+ *ppOut = 0;
+ if( pVal==0 ) return SQLITE_MISUSE_BKPT;
+ if( (pVal->flags & MEM_Dyn)==0 || pVal->xDel!=sqlite3VdbeValueListFree ){
+ return SQLITE_ERROR;
+ }else{
+ assert( (pVal->flags&(MEM_TypeMask|MEM_Term|MEM_Subtype)) ==
+ (MEM_Null|MEM_Term|MEM_Subtype) );
+ assert( pVal->eSubtype=='p' );
+ assert( pVal->u.zPType!=0 && strcmp(pVal->u.zPType,"ValueList")==0 );
+ pRhs = (ValueList*)pVal->z;
+ }
+ if( bNext ){
+ rc = sqlite3BtreeNext(pRhs->pCsr, 0);
+ }else{
+ int dummy = 0;
+ rc = sqlite3BtreeFirst(pRhs->pCsr, &dummy);
+ assert( rc==SQLITE_OK || sqlite3BtreeEof(pRhs->pCsr) );
+ if( sqlite3BtreeEof(pRhs->pCsr) ) rc = SQLITE_DONE;
+ }
+ if( rc==SQLITE_OK ){
+ u32 sz; /* Size of current row in bytes */
+ Mem sMem; /* Raw content of current row */
+ memset(&sMem, 0, sizeof(sMem));
+ sz = sqlite3BtreePayloadSize(pRhs->pCsr);
+ rc = sqlite3VdbeMemFromBtreeZeroOffset(pRhs->pCsr,(int)sz,&sMem);
+ if( rc==SQLITE_OK ){
+ u8 *zBuf = (u8*)sMem.z;
+ u32 iSerial;
+ sqlite3_value *pOut = pRhs->pOut;
+ int iOff = 1 + getVarint32(&zBuf[1], iSerial);
+ sqlite3VdbeSerialGet(&zBuf[iOff], iSerial, pOut);
+ pOut->enc = ENC(pOut->db);
+ if( (pOut->flags & MEM_Ephem)!=0 && sqlite3VdbeMemMakeWriteable(pOut) ){
+ rc = SQLITE_NOMEM;
+ }else{
+ *ppOut = pOut;
+ }
+ }
+ sqlite3VdbeMemRelease(&sMem);
+ }
+ return rc;
+}
+
+/*
+** Set the iterator value pVal to point to the first value in the set.
+** Set (*ppOut) to point to this value before returning.
+*/
+SQLITE_API int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut){
+ return valueFromValueList(pVal, ppOut, 0);
+}
+
+/*
+** Set the iterator value pVal to point to the next value in the set.
+** Set (*ppOut) to point to this value before returning.
+*/
+SQLITE_API int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut){
+ return valueFromValueList(pVal, ppOut, 1);
+}
+
+/*
** Return the current time for a statement. If the current time
** is requested more than once within the same run of a single prepared
** statement, the exact same time is returned for each invocation regardless
@@ -81483,7 +90909,7 @@ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){
*/
SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){
int rc;
-#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifndef SQLITE_ENABLE_STAT4
sqlite3_int64 *piTime = &p->pVdbe->iCurrentTime;
assert( p->pVdbe!=0 );
#else
@@ -81547,8 +90973,11 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
AuxData *pAuxData;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return 0;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
-#if SQLITE_ENABLE_STAT3_OR_STAT4
+#if SQLITE_ENABLE_STAT4
if( pCtx->pVdbe==0 ) return 0;
#else
assert( pCtx->pVdbe!=0 );
@@ -81573,16 +91002,20 @@ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
** access code.
*/
SQLITE_API void sqlite3_set_auxdata(
- sqlite3_context *pCtx,
- int iArg,
- void *pAux,
+ sqlite3_context *pCtx,
+ int iArg,
+ void *pAux,
void (*xDelete)(void*)
){
AuxData *pAuxData;
- Vdbe *pVdbe = pCtx->pVdbe;
+ Vdbe *pVdbe;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
+ pVdbe= pCtx->pVdbe;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pVdbe==0 ) goto failed;
#else
assert( pVdbe!=0 );
@@ -81617,7 +91050,7 @@ failed:
#ifndef SQLITE_OMIT_DEPRECATED
/*
-** Return the number of times the Step function of an aggregate has been
+** Return the number of times the Step function of an aggregate has been
** called.
**
** This function is deprecated. Do not use it for new code. It is
@@ -81636,7 +91069,8 @@ SQLITE_API int sqlite3_aggregate_count(sqlite3_context *p){
*/
SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){
Vdbe *pVm = (Vdbe *)pStmt;
- return pVm ? pVm->nResColumn : 0;
+ if( pVm==0 ) return 0;
+ return pVm->nResColumn;
}
/*
@@ -81645,7 +91079,7 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){
*/
SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt){
Vdbe *pVm = (Vdbe *)pStmt;
- if( pVm==0 || pVm->pResultSet==0 ) return 0;
+ if( pVm==0 || pVm->pResultRow==0 ) return 0;
return pVm->nResColumn;
}
@@ -81662,21 +91096,21 @@ static const Mem *columnNullValue(void){
** these assert()s from failing, when building with SQLITE_DEBUG defined
** using gcc, we force nullMem to be 8-byte aligned using the magical
** __attribute__((aligned(8))) macro. */
- static const Mem nullMem
+ static const Mem nullMem
#if defined(SQLITE_DEBUG) && defined(__GNUC__)
- __attribute__((aligned(8)))
+ __attribute__((aligned(8)))
#endif
= {
/* .u = */ {0},
+ /* .z = */ (char*)0,
+ /* .n = */ (int)0,
/* .flags = */ (u16)MEM_Null,
/* .enc = */ (u8)0,
/* .eSubtype = */ (u8)0,
- /* .n = */ (int)0,
- /* .z = */ (char*)0,
- /* .zMalloc = */ (char*)0,
+ /* .db = */ (sqlite3*)0,
/* .szMalloc = */ (int)0,
/* .uTemp = */ (u32)0,
- /* .db = */ (sqlite3*)0,
+ /* .zMalloc = */ (char*)0,
/* .xDel = */ (void(*)(void*))0,
#ifdef SQLITE_DEBUG
/* .pScopyFrom = */ (Mem*)0,
@@ -81700,8 +91134,8 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
if( pVm==0 ) return (Mem*)columnNullValue();
assert( pVm->db );
sqlite3_mutex_enter(pVm->db->mutex);
- if( pVm->pResultSet!=0 && i<pVm->nResColumn && i>=0 ){
- pOut = &pVm->pResultSet[i];
+ if( pVm->pResultRow!=0 && i<pVm->nResColumn && i>=0 ){
+ pOut = &pVm->pResultRow[i];
}else{
sqlite3Error(pVm->db, SQLITE_RANGE);
pOut = (Mem*)columnNullValue();
@@ -81710,9 +91144,9 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
}
/*
-** This function is called after invoking an sqlite3_value_XXX function on a
+** This function is called after invoking an sqlite3_value_XXX function on a
** column value (i.e. a value returned by evaluating an SQL expression in the
-** select list of a SELECT statement) that may cause a malloc() failure. If
+** select list of a SELECT statement) that may cause a malloc() failure. If
** malloc() has failed, the threads mallocFailed flag is cleared and the result
** code of statement pStmt set to SQLITE_NOMEM.
**
@@ -81725,7 +91159,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
** sqlite3_column_real()
** sqlite3_column_bytes()
** sqlite3_column_bytes16()
-** sqiite3_column_blob()
+** sqlite3_column_blob()
*/
static void columnMallocFailure(sqlite3_stmt *pStmt)
{
@@ -81751,8 +91185,8 @@ SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){
const void *val;
val = sqlite3_value_blob( columnMem(pStmt,i) );
/* Even though there is no encoding conversion, value_blob() might
- ** need to call malloc() to expand the result of a zeroblob()
- ** expression.
+ ** need to call malloc() to expand the result of a zeroblob()
+ ** expression.
*/
columnMallocFailure(pStmt);
return val;
@@ -81810,6 +91244,32 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
}
/*
+** Column names appropriate for EXPLAIN or EXPLAIN QUERY PLAN.
+*/
+static const char * const azExplainColNames8[] = {
+ "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", /* EXPLAIN */
+ "id", "parent", "notused", "detail" /* EQP */
+};
+static const u16 azExplainColNames16data[] = {
+ /* 0 */ 'a', 'd', 'd', 'r', 0,
+ /* 5 */ 'o', 'p', 'c', 'o', 'd', 'e', 0,
+ /* 12 */ 'p', '1', 0,
+ /* 15 */ 'p', '2', 0,
+ /* 18 */ 'p', '3', 0,
+ /* 21 */ 'p', '4', 0,
+ /* 24 */ 'p', '5', 0,
+ /* 27 */ 'c', 'o', 'm', 'm', 'e', 'n', 't', 0,
+ /* 35 */ 'i', 'd', 0,
+ /* 38 */ 'p', 'a', 'r', 'e', 'n', 't', 0,
+ /* 45 */ 'n', 'o', 't', 'u', 's', 'e', 'd', 0,
+ /* 53 */ 'd', 'e', 't', 'a', 'i', 'l', 0
+};
+static const u8 iExplainColNames16[] = {
+ 0, 5, 12, 15, 18, 21, 24, 27,
+ 35, 38, 45, 53
+};
+
+/*
** Convert the N-th element of pStmt->pColName[] into a string using
** xFunc() then return that string. If N is out of range, return 0.
**
@@ -81826,10 +91286,10 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
** or a constant) then useTypes 2, 3, and 4 return NULL.
*/
static const void *columnName(
- sqlite3_stmt *pStmt,
- int N,
- const void *(*xFunc)(Mem*),
- int useType
+ sqlite3_stmt *pStmt, /* The statement */
+ int N, /* Which column to get the name for */
+ int useUtf16, /* True to return the name as UTF16 */
+ int useType /* What type of name */
){
const void *ret;
Vdbe *p;
@@ -81841,25 +91301,48 @@ static const void *columnName(
return 0;
}
#endif
+ if( N<0 ) return 0;
ret = 0;
p = (Vdbe *)pStmt;
db = p->db;
assert( db!=0 );
- n = sqlite3_column_count(pStmt);
- if( N<n && N>=0 ){
+ sqlite3_mutex_enter(db->mutex);
+
+ if( p->explain ){
+ if( useType>0 ) goto columnName_end;
+ n = p->explain==1 ? 8 : 4;
+ if( N>=n ) goto columnName_end;
+ if( useUtf16 ){
+ int i = iExplainColNames16[N + 8*p->explain - 8];
+ ret = (void*)&azExplainColNames16data[i];
+ }else{
+ ret = (void*)azExplainColNames8[N + 8*p->explain - 8];
+ }
+ goto columnName_end;
+ }
+ n = p->nResColumn;
+ if( N<n ){
+ u8 prior_mallocFailed = db->mallocFailed;
N += useType*n;
- sqlite3_mutex_enter(db->mutex);
- assert( db->mallocFailed==0 );
- ret = xFunc(&p->aColName[N]);
- /* A malloc may have failed inside of the xFunc() call. If this
+#ifndef SQLITE_OMIT_UTF16
+ if( useUtf16 ){
+ ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]);
+ }else
+#endif
+ {
+ ret = sqlite3_value_text((sqlite3_value*)&p->aColName[N]);
+ }
+ /* A malloc may have failed inside of the _text() call. If this
** is the case, clear the mallocFailed flag and return NULL.
*/
- if( db->mallocFailed ){
+ assert( db->mallocFailed==0 || db->mallocFailed==1 );
+ if( db->mallocFailed > prior_mallocFailed ){
sqlite3OomClear(db);
ret = 0;
}
- sqlite3_mutex_leave(db->mutex);
}
+columnName_end:
+ sqlite3_mutex_leave(db->mutex);
return ret;
}
@@ -81868,13 +91351,11 @@ static const void *columnName(
** statement pStmt.
*/
SQLITE_API const char *sqlite3_column_name(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_NAME);
+ return columnName(pStmt, N, 0, COLNAME_NAME);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_NAME);
+ return columnName(pStmt, N, 1, COLNAME_NAME);
}
#endif
@@ -81893,13 +91374,11 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){
** of the result set of SQL statement pStmt.
*/
SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DECLTYPE);
+ return columnName(pStmt, N, 0, COLNAME_DECLTYPE);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DECLTYPE);
+ return columnName(pStmt, N, 1, COLNAME_DECLTYPE);
}
#endif /* SQLITE_OMIT_UTF16 */
#endif /* SQLITE_OMIT_DECLTYPE */
@@ -81911,13 +91390,11 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){
** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DATABASE);
+ return columnName(pStmt, N, 0, COLNAME_DATABASE);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DATABASE);
+ return columnName(pStmt, N, 1, COLNAME_DATABASE);
}
#endif /* SQLITE_OMIT_UTF16 */
@@ -81927,13 +91404,11 @@ SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N
** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_TABLE);
+ return columnName(pStmt, N, 0, COLNAME_TABLE);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_TABLE);
+ return columnName(pStmt, N, 1, COLNAME_TABLE);
}
#endif /* SQLITE_OMIT_UTF16 */
@@ -81943,26 +91418,24 @@ SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){
** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_COLUMN);
+ return columnName(pStmt, N, 0, COLNAME_COLUMN);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_COLUMN);
+ return columnName(pStmt, N, 1, COLNAME_COLUMN);
}
#endif /* SQLITE_OMIT_UTF16 */
#endif /* SQLITE_ENABLE_COLUMN_METADATA */
/******************************* sqlite3_bind_ ***************************
-**
+**
** Routines used to attach values to wildcards in a compiled SQL statement.
*/
/*
-** Unbind the value bound to variable i in virtual machine p. This is the
+** Unbind the value bound to variable i in virtual machine p. This is the
** the same as binding a NULL value to the column. If the "i" parameter is
-** out of range, then SQLITE_RANGE is returned. Othewise SQLITE_OK.
+** out of range, then SQLITE_RANGE is returned. Otherwise SQLITE_OK.
**
** A successful evaluation of this routine acquires the mutex on p.
** the mutex is released if any kind of error occurs.
@@ -81970,34 +91443,33 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){
** The error code stored in database p->db is overwritten with the return
** value in any case.
*/
-static int vdbeUnbind(Vdbe *p, int i){
+static int vdbeUnbind(Vdbe *p, unsigned int i){
Mem *pVar;
if( vdbeSafetyNotNull(p) ){
return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(p->db->mutex);
- if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){
- sqlite3Error(p->db, SQLITE_MISUSE);
+ if( p->eVdbeState!=VDBE_READY_STATE ){
+ sqlite3Error(p->db, SQLITE_MISUSE_BKPT);
sqlite3_mutex_leave(p->db->mutex);
- sqlite3_log(SQLITE_MISUSE,
+ sqlite3_log(SQLITE_MISUSE,
"bind on a busy prepared statement: [%s]", p->zSql);
return SQLITE_MISUSE_BKPT;
}
- if( i<1 || i>p->nVar ){
+ if( i>=(unsigned int)p->nVar ){
sqlite3Error(p->db, SQLITE_RANGE);
sqlite3_mutex_leave(p->db->mutex);
return SQLITE_RANGE;
}
- i--;
pVar = &p->aVar[i];
sqlite3VdbeMemRelease(pVar);
pVar->flags = MEM_Null;
- sqlite3Error(p->db, SQLITE_OK);
+ p->db->errCode = SQLITE_OK;
- /* If the bit corresponding to this variable in Vdbe.expmask is set, then
+ /* If the bit corresponding to this variable in Vdbe.expmask is set, then
** binding a new value to this variable invalidates the current query plan.
**
- ** IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host
+ ** IMPLEMENTATION-OF: R-57496-20354 If the specific value bound to a host
** parameter in the WHERE clause might influence the choice of query plan
** for a statement, then the statement will be automatically recompiled,
** as if there had been a schema change, on the first sqlite3_step() call
@@ -82017,7 +91489,7 @@ static int bindText(
sqlite3_stmt *pStmt, /* The statement to bind against */
int i, /* Index of the parameter to bind */
const void *zData, /* Pointer to the data to be bound */
- int nData, /* Number of bytes of data to be bound */
+ i64 nData, /* Number of bytes of data to be bound */
void (*xDel)(void*), /* Destructor for the data */
u8 encoding /* Encoding for the data */
){
@@ -82025,7 +91497,7 @@ static int bindText(
Mem *pVar;
int rc;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
if( zData!=0 ){
pVar = &p->aVar[i-1];
@@ -82050,10 +91522,10 @@ static int bindText(
** Bind a blob value to an SQL statement variable.
*/
SQLITE_API int sqlite3_bind_blob(
- sqlite3_stmt *pStmt,
- int i,
- const void *zData,
- int nData,
+ sqlite3_stmt *pStmt,
+ int i,
+ const void *zData,
+ int nData,
void (*xDel)(void*)
){
#ifdef SQLITE_ENABLE_API_ARMOR
@@ -82062,23 +91534,19 @@ SQLITE_API int sqlite3_bind_blob(
return bindText(pStmt, i, zData, nData, xDel, 0);
}
SQLITE_API int sqlite3_bind_blob64(
- sqlite3_stmt *pStmt,
- int i,
- const void *zData,
- sqlite3_uint64 nData,
+ sqlite3_stmt *pStmt,
+ int i,
+ const void *zData,
+ sqlite3_uint64 nData,
void (*xDel)(void*)
){
assert( xDel!=SQLITE_DYNAMIC );
- if( nData>0x7fffffff ){
- return invokeValueDestructor(zData, xDel, 0);
- }else{
- return bindText(pStmt, i, zData, (int)nData, xDel, 0);
- }
+ return bindText(pStmt, i, zData, nData, xDel, 0);
}
SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue);
sqlite3_mutex_leave(p->db->mutex);
@@ -82091,7 +91559,7 @@ SQLITE_API int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){
SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue);
sqlite3_mutex_leave(p->db->mutex);
@@ -82101,7 +91569,7 @@ SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValu
SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){
int rc;
Vdbe *p = (Vdbe*)pStmt;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3_mutex_leave(p->db->mutex);
}
@@ -82116,7 +91584,7 @@ SQLITE_API int sqlite3_bind_pointer(
){
int rc;
Vdbe *p = (Vdbe*)pStmt;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
sqlite3VdbeMemSetPointer(&p->aVar[i-1], pPtr, zPTtype, xDestructor);
sqlite3_mutex_leave(p->db->mutex);
@@ -82125,40 +91593,39 @@ SQLITE_API int sqlite3_bind_pointer(
}
return rc;
}
-SQLITE_API int sqlite3_bind_text(
- sqlite3_stmt *pStmt,
- int i,
- const char *zData,
- int nData,
+SQLITE_API int sqlite3_bind_text(
+ sqlite3_stmt *pStmt,
+ int i,
+ const char *zData,
+ int nData,
void (*xDel)(void*)
){
return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF8);
}
-SQLITE_API int sqlite3_bind_text64(
- sqlite3_stmt *pStmt,
- int i,
- const char *zData,
- sqlite3_uint64 nData,
+SQLITE_API int sqlite3_bind_text64(
+ sqlite3_stmt *pStmt,
+ int i,
+ const char *zData,
+ sqlite3_uint64 nData,
void (*xDel)(void*),
unsigned char enc
){
assert( xDel!=SQLITE_DYNAMIC );
- if( nData>0x7fffffff ){
- return invokeValueDestructor(zData, xDel, 0);
- }else{
+ if( enc!=SQLITE_UTF8 ){
if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
- return bindText(pStmt, i, zData, (int)nData, xDel, enc);
+ nData &= ~(u16)1;
}
+ return bindText(pStmt, i, zData, nData, xDel, enc);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API int sqlite3_bind_text16(
- sqlite3_stmt *pStmt,
- int i,
- const void *zData,
- int nData,
+ sqlite3_stmt *pStmt,
+ int i,
+ const void *zData,
+ int n,
void (*xDel)(void*)
){
- return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE);
+ return bindText(pStmt, i, zData, n & ~(u64)1, xDel, SQLITE_UTF16NATIVE);
}
#endif /* SQLITE_OMIT_UTF16 */
SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){
@@ -82169,7 +91636,10 @@ SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_valu
break;
}
case SQLITE_FLOAT: {
- rc = sqlite3_bind_double(pStmt, i, pValue->u.r);
+ assert( pValue->flags & (MEM_Real|MEM_IntReal) );
+ rc = sqlite3_bind_double(pStmt, i,
+ (pValue->flags & MEM_Real) ? pValue->u.r : (double)pValue->u.i
+ );
break;
}
case SQLITE_BLOB: {
@@ -82195,9 +91665,13 @@ SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_valu
SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
int rc;
Vdbe *p = (Vdbe *)pStmt;
- rc = vdbeUnbind(p, i);
+ rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
+#ifndef SQLITE_OMIT_INCRBLOB
sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n);
+#else
+ rc = sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n);
+#endif
sqlite3_mutex_leave(p->db->mutex);
}
return rc;
@@ -82205,6 +91679,9 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt *pStmt, int i, sqlite3_uint64 n){
int rc;
Vdbe *p = (Vdbe *)pStmt;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(p->db->mutex);
if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){
rc = SQLITE_TOOBIG;
@@ -82219,7 +91696,7 @@ SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt *pStmt, int i, sqlite3_uint6
/*
** Return the number of wildcards that can be potentially bound to.
-** This routine is added to support DBD::SQLite.
+** This routine is added to support DBD::SQLite.
*/
SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe*)pStmt;
@@ -82318,11 +91795,55 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){
}
/*
+** Return 1 if the statement is an EXPLAIN and return 2 if the
+** statement is an EXPLAIN QUERY PLAN
+*/
+SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){
+ return pStmt ? ((Vdbe*)pStmt)->explain : 0;
+}
+
+/*
+** Set the explain mode for a statement.
+*/
+SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode){
+ Vdbe *v = (Vdbe*)pStmt;
+ int rc;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pStmt==0 ) return SQLITE_MISUSE_BKPT;
+#endif
+ sqlite3_mutex_enter(v->db->mutex);
+ if( ((int)v->explain)==eMode ){
+ rc = SQLITE_OK;
+ }else if( eMode<0 || eMode>2 ){
+ rc = SQLITE_ERROR;
+ }else if( (v->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ){
+ rc = SQLITE_ERROR;
+ }else if( v->eVdbeState!=VDBE_READY_STATE ){
+ rc = SQLITE_BUSY;
+ }else if( v->nMem>=10 && (eMode!=2 || v->haveEqpOps) ){
+ /* No reprepare necessary */
+ v->explain = eMode;
+ rc = SQLITE_OK;
+ }else{
+ v->explain = eMode;
+ rc = sqlite3Reprepare(v);
+ v->haveEqpOps = eMode==2;
+ }
+ if( v->explain ){
+ v->nResColumn = 12 - 4*v->explain;
+ }else{
+ v->nResColumn = v->nResAlloc;
+ }
+ sqlite3_mutex_leave(v->db->mutex);
+ return rc;
+}
+
+/*
** Return true if the prepared statement is in need of being reset.
*/
SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
Vdbe *v = (Vdbe*)pStmt;
- return v!=0 && v->magic==VDBE_MAGIC_RUN && v->pc>=0;
+ return v!=0 && v->eVdbeState==VDBE_RUN_STATE;
}
/*
@@ -82343,7 +91864,7 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){
if( pStmt==0 ){
pNext = (sqlite3_stmt*)pDb->pVdbe;
}else{
- pNext = (sqlite3_stmt*)((Vdbe*)pStmt)->pNext;
+ pNext = (sqlite3_stmt*)((Vdbe*)pStmt)->pVNext;
}
sqlite3_mutex_leave(pDb->mutex);
return pNext;
@@ -82356,7 +91877,7 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
Vdbe *pVdbe = (Vdbe*)pStmt;
u32 v;
#ifdef SQLITE_ENABLE_API_ARMOR
- if( !pStmt
+ if( !pStmt
|| (op!=SQLITE_STMTSTATUS_MEMUSED && (op<0||op>=ArraySize(pVdbe->aCounter)))
){
(void)SQLITE_MISUSE_BKPT;
@@ -82368,9 +91889,11 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
sqlite3_mutex_enter(db->mutex);
v = 0;
db->pnBytesFreed = (int*)&v;
- sqlite3VdbeClearObject(db, pVdbe);
- sqlite3DbFree(db, pVdbe);
+ assert( db->lookaside.pEnd==db->lookaside.pTrueEnd );
+ db->lookaside.pEnd = db->lookaside.pStart;
+ sqlite3VdbeDelete(pVdbe);
db->pnBytesFreed = 0;
+ db->lookaside.pEnd = db->lookaside.pTrueEnd;
sqlite3_mutex_leave(db->mutex);
}else{
v = pVdbe->aCounter[op];
@@ -82418,7 +91941,13 @@ SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt){
*/
SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe *)pStmt;
- return p ? p->zNormSql : 0;
+ if( p==0 ) return 0;
+ if( p->zNormSql==0 && ALWAYS(p->zSql!=0) ){
+ sqlite3_mutex_enter(p->db->mutex);
+ p->zNormSql = sqlite3Normalize(p, p->zSql);
+ sqlite3_mutex_leave(p->db->mutex);
+ }
+ return p->zNormSql;
}
#endif /* SQLITE_ENABLE_NORMALIZE */
@@ -82429,8 +91958,8 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt){
** if successful, or a NULL pointer if an OOM error is encountered.
*/
static UnpackedRecord *vdbeUnpackRecord(
- KeyInfo *pKeyInfo,
- int nKey,
+ KeyInfo *pKeyInfo,
+ int nKey,
const void *pKey
){
UnpackedRecord *pRet; /* Return value */
@@ -82448,10 +91977,16 @@ static UnpackedRecord *vdbeUnpackRecord(
** a field of the row currently being updated or deleted.
*/
SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
Mem *pMem;
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 || ppValue==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ p = db->pPreUpdate;
/* Test that this call is being made from within an SQLITE_DELETE or
** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */
if( !p || p->op==SQLITE_INSERT ){
@@ -82459,7 +91994,7 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa
goto preupdate_old_out;
}
if( p->pPk ){
- iIdx = sqlite3ColumnOfIndex(p->pPk, iIdx);
+ iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx);
}
if( iIdx>=p->pCsr->nField || iIdx<0 ){
rc = SQLITE_RANGE;
@@ -82471,6 +92006,7 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa
u32 nRec;
u8 *aRec;
+ assert( p->pCsr->eCurType==CURTYPE_BTREE );
nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
aRec = sqlite3DbMallocRaw(db, nRec);
if( !aRec ) goto preupdate_old_out;
@@ -82492,7 +92028,9 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa
}else if( iIdx>=p->pUnpacked->nField ){
*ppValue = (sqlite3_value *)columnNullValue();
}else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
- if( pMem->flags & MEM_Int ){
+ if( pMem->flags & (MEM_Int|MEM_IntReal) ){
+ testcase( pMem->flags & MEM_Int );
+ testcase( pMem->flags & MEM_IntReal );
sqlite3VdbeMemRealify(pMem);
}
}
@@ -82509,7 +92047,12 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa
** the number of columns in the row being updated, deleted or inserted.
*/
SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
return (p ? p->keyinfo.nKeyField : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -82520,34 +92063,61 @@ SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){
** only. It returns zero if the change that caused the callback was made
** immediately by a user SQL statement. Or, if the change was made by a
** trigger program, it returns the number of trigger programs currently
-** on the stack (1 for a top-level trigger, 2 for a trigger fired by a
+** on the stack (1 for a top-level trigger, 2 for a trigger fired by a
** top-level trigger etc.).
**
** For the purposes of the previous paragraph, a foreign key CASCADE, SET NULL
** or SET DEFAULT action is considered a trigger.
*/
SQLITE_API int sqlite3_preupdate_depth(sqlite3 *db){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
return (p ? p->v->nFrame : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
+** This function is designed to be called from within a pre-update callback
+** only.
+*/
+SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *db){
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
+ return (p ? p->iBlobWrite : -1);
+}
+#endif
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
** This function is called from within a pre-update callback to retrieve
** a field of the row currently being updated or inserted.
*/
SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
int rc = SQLITE_OK;
Mem *pMem;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 || ppValue==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ p = db->pPreUpdate;
if( !p || p->op==SQLITE_DELETE ){
rc = SQLITE_MISUSE_BKPT;
goto preupdate_new_out;
}
if( p->pPk && p->op!=SQLITE_UPDATE ){
- iIdx = sqlite3ColumnOfIndex(p->pPk, iIdx);
+ iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx);
}
if( iIdx>=p->pCsr->nField || iIdx<0 ){
rc = SQLITE_RANGE;
@@ -82612,23 +92182,78 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa
/*
** Return status data for a single loop within query pStmt.
*/
-SQLITE_API int sqlite3_stmt_scanstatus(
+SQLITE_API int sqlite3_stmt_scanstatus_v2(
sqlite3_stmt *pStmt, /* Prepared statement being queried */
- int idx, /* Index of loop to report on */
+ int iScan, /* Index of loop to report on */
int iScanStatusOp, /* Which metric to return */
+ int flags,
void *pOut /* OUT: Write the answer here */
){
Vdbe *p = (Vdbe*)pStmt;
- ScanStatus *pScan;
- if( idx<0 || idx>=p->nScan ) return 1;
- pScan = &p->aScan[idx];
+ VdbeOp *aOp;
+ int nOp;
+ ScanStatus *pScan = 0;
+ int idx;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 || pOut==0
+ || iScanStatusOp<SQLITE_SCANSTAT_NLOOP
+ || iScanStatusOp>SQLITE_SCANSTAT_NCYCLE ){
+ return 1;
+ }
+#endif
+ aOp = p->aOp;
+ nOp = p->nOp;
+ if( p->pFrame ){
+ VdbeFrame *pFrame;
+ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
+ aOp = pFrame->aOp;
+ nOp = pFrame->nOp;
+ }
+
+ if( iScan<0 ){
+ int ii;
+ if( iScanStatusOp==SQLITE_SCANSTAT_NCYCLE ){
+ i64 res = 0;
+ for(ii=0; ii<nOp; ii++){
+ res += aOp[ii].nCycle;
+ }
+ *(i64*)pOut = res;
+ return 0;
+ }
+ return 1;
+ }
+ if( flags & SQLITE_SCANSTAT_COMPLEX ){
+ idx = iScan;
+ pScan = &p->aScan[idx];
+ }else{
+ /* If the COMPLEX flag is clear, then this function must ignore any
+ ** ScanStatus structures with ScanStatus.addrLoop set to 0. */
+ for(idx=0; idx<p->nScan; idx++){
+ pScan = &p->aScan[idx];
+ if( pScan->zName ){
+ iScan--;
+ if( iScan<0 ) break;
+ }
+ }
+ }
+ if( idx>=p->nScan ) return 1;
+
switch( iScanStatusOp ){
case SQLITE_SCANSTAT_NLOOP: {
- *(sqlite3_int64*)pOut = p->anExec[pScan->addrLoop];
+ if( pScan->addrLoop>0 ){
+ *(sqlite3_int64*)pOut = aOp[pScan->addrLoop].nExec;
+ }else{
+ *(sqlite3_int64*)pOut = -1;
+ }
break;
}
case SQLITE_SCANSTAT_NVISIT: {
- *(sqlite3_int64*)pOut = p->anExec[pScan->addrVisit];
+ if( pScan->addrVisit>0 ){
+ *(sqlite3_int64*)pOut = aOp[pScan->addrVisit].nExec;
+ }else{
+ *(sqlite3_int64*)pOut = -1;
+ }
break;
}
case SQLITE_SCANSTAT_EST: {
@@ -82647,7 +92272,7 @@ SQLITE_API int sqlite3_stmt_scanstatus(
}
case SQLITE_SCANSTAT_EXPLAIN: {
if( pScan->addrExplain ){
- *(const char**)pOut = p->aOp[ pScan->addrExplain ].p4.z;
+ *(const char**)pOut = aOp[ pScan->addrExplain ].p4.z;
}else{
*(const char**)pOut = 0;
}
@@ -82655,12 +92280,51 @@ SQLITE_API int sqlite3_stmt_scanstatus(
}
case SQLITE_SCANSTAT_SELECTID: {
if( pScan->addrExplain ){
- *(int*)pOut = p->aOp[ pScan->addrExplain ].p1;
+ *(int*)pOut = aOp[ pScan->addrExplain ].p1;
+ }else{
+ *(int*)pOut = -1;
+ }
+ break;
+ }
+ case SQLITE_SCANSTAT_PARENTID: {
+ if( pScan->addrExplain ){
+ *(int*)pOut = aOp[ pScan->addrExplain ].p2;
}else{
*(int*)pOut = -1;
}
break;
}
+ case SQLITE_SCANSTAT_NCYCLE: {
+ i64 res = 0;
+ if( pScan->aAddrRange[0]==0 ){
+ res = -1;
+ }else{
+ int ii;
+ for(ii=0; ii<ArraySize(pScan->aAddrRange); ii+=2){
+ int iIns = pScan->aAddrRange[ii];
+ int iEnd = pScan->aAddrRange[ii+1];
+ if( iIns==0 ) break;
+ if( iIns>0 ){
+ while( iIns<=iEnd ){
+ res += aOp[iIns].nCycle;
+ iIns++;
+ }
+ }else{
+ int iOp;
+ for(iOp=0; iOp<nOp; iOp++){
+ Op *pOp = &aOp[iOp];
+ if( pOp->p1!=iEnd ) continue;
+ if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_NCYCLE)==0 ){
+ continue;
+ }
+ res += aOp[iOp].nCycle;
+ }
+ }
+ }
+ }
+ *(i64*)pOut = res;
+ break;
+ }
default: {
return 1;
}
@@ -82669,11 +92333,28 @@ SQLITE_API int sqlite3_stmt_scanstatus(
}
/*
+** Return status data for a single loop within query pStmt.
+*/
+SQLITE_API int sqlite3_stmt_scanstatus(
+ sqlite3_stmt *pStmt, /* Prepared statement being queried */
+ int iScan, /* Index of loop to report on */
+ int iScanStatusOp, /* Which metric to return */
+ void *pOut /* OUT: Write the answer here */
+){
+ return sqlite3_stmt_scanstatus_v2(pStmt, iScan, iScanStatusOp, 0, pOut);
+}
+
+/*
** Zero all counters associated with the sqlite3_stmt_scanstatus() data.
*/
SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe*)pStmt;
- memset(p->anExec, 0, p->nOp * sizeof(i64));
+ int ii;
+ for(ii=0; p!=0 && ii<p->nOp; ii++){
+ Op *pOp = &p->aOp[ii];
+ pOp->nExec = 0;
+ pOp->nCycle = 0;
+ }
}
#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */
@@ -82729,8 +92410,8 @@ static int findNextHostParameter(const char *zSql, int *pnToken){
/*
** This function returns a pointer to a nul-terminated string in memory
** obtained from sqlite3DbMalloc(). If sqlite3.nVdbeExec is 1, then the
-** string contains a copy of zRawSql but with host parameters expanded to
-** their current bindings. Or, if sqlite3.nVdbeExec is greater than 1,
+** string contains a copy of zRawSql but with host parameters expanded to
+** their current bindings. Or, if sqlite3.nVdbeExec is greater than 1,
** then the returned string holds a copy of zRawSql with "-- " prepended
** to each line of text.
**
@@ -82765,11 +92446,9 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
#ifndef SQLITE_OMIT_UTF16
Mem utf8; /* Used to convert UTF16 into UTF8 for display */
#endif
- char zBase[100]; /* Initial working space */
db = p->db;
- sqlite3StrAccumInit(&out, 0, zBase, sizeof(zBase),
- db->aLimit[SQLITE_LIMIT_LENGTH]);
+ sqlite3StrAccumInit(&out, 0, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
if( db->nVdbeExec>1 ){
while( *zRawSql ){
const char *zStart = zRawSql;
@@ -82806,12 +92485,12 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
assert( idx>0 );
}
zRawSql += nToken;
- nextIndex = idx + 1;
+ nextIndex = MAX(idx + 1, nextIndex);
assert( idx>0 && idx<=p->nVar );
pVar = &p->aVar[idx-1];
if( pVar->flags & MEM_Null ){
sqlite3_str_append(&out, "NULL", 4);
- }else if( pVar->flags & MEM_Int ){
+ }else if( pVar->flags & (MEM_Int|MEM_IntReal) ){
sqlite3_str_appendf(&out, "%lld", pVar->u.i);
}else if( pVar->flags & MEM_Real ){
sqlite3_str_appendf(&out, "%!.15g", pVar->u.r);
@@ -82836,7 +92515,7 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
nOut = SQLITE_TRACE_SIZE_LIMIT;
while( nOut<pVar->n && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; }
}
-#endif
+#endif
sqlite3_str_appendf(&out, "'%.*q'", nOut, pVar->z);
#ifdef SQLITE_TRACE_SIZE_LIMIT
if( nOut<pVar->n ){
@@ -82995,17 +92674,49 @@ SQLITE_API int sqlite3_found_count = 0;
# define UPDATE_MAX_BLOBSIZE(P)
#endif
+#ifdef SQLITE_DEBUG
+/* This routine provides a convenient place to set a breakpoint during
+** tracing with PRAGMA vdbe_trace=on. The breakpoint fires right after
+** each opcode is printed. Variables "pc" (program counter) and pOp are
+** available to add conditionals to the breakpoint. GDB example:
+**
+** break test_trace_breakpoint if pc=22
+**
+** Other useful labels for breakpoints include:
+** test_addop_breakpoint(pc,pOp)
+** sqlite3CorruptError(lineno)
+** sqlite3MisuseError(lineno)
+** sqlite3CantopenError(lineno)
+*/
+static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){
+ static u64 n = 0;
+ (void)pc;
+ (void)pOp;
+ (void)v;
+ n++;
+ if( n==LARGEST_UINT64 ) abort(); /* So that n is used, preventing a warning */
+}
+#endif
+
/*
** Invoke the VDBE coverage callback, if that callback is defined. This
** feature is used for test suite validation only and does not appear an
** production builds.
**
-** M is an integer between 2 and 4. 2 indicates a ordinary two-way
-** branch (I=0 means fall through and I=1 means taken). 3 indicates
-** a 3-way branch where the third way is when one of the operands is
-** NULL. 4 indicates the OP_Jump instruction which has three destinations
-** depending on whether the first operand is less than, equal to, or greater
-** than the second.
+** M is the type of branch. I is the direction taken for this instance of
+** the branch.
+**
+** M: 2 - two-way branch (I=0: fall-thru 1: jump )
+** 3 - two-way + NULL (I=0: fall-thru 1: jump 2: NULL )
+** 4 - OP_Jump (I=0: jump p1 1: jump p2 2: jump p3)
+**
+** In other words, if M is 2, then I is either 0 (for fall-through) or
+** 1 (for when the branch is taken). If M is 3, the I is 0 for an
+** ordinary fall-through, I is 1 if the branch was taken, and I is 2
+** if the result of comparison is NULL. For M=3, I=2 the jump may or
+** may not be taken, depending on the SQLITE_JUMPIFNULL flags in p5.
+** When M is 4, that means that an OP_Jump is being run. I is 0, 1, or 2
+** depending on if the operands are less than, equal, or greater than.
**
** iSrcLine is the source code line (from the __LINE__ macro) that
** generated the VDBE instruction combined with flag bits. The source
@@ -83016,9 +92727,9 @@ SQLITE_API int sqlite3_found_count = 0;
** alternate branch are never taken. If a branch is never taken then
** flags should be 0x06 since only the fall-through approach is allowed.
**
-** Bit 0x04 of the flags indicates an OP_Jump opcode that is only
+** Bit 0x08 of the flags indicates an OP_Jump opcode that is only
** interested in equal or not-equal. In other words, I==0 and I==2
-** should be treated the same.
+** should be treated as equivalent
**
** Since only a line number is retained, not the filename, this macro
** only works for amalgamation builds. But that is ok, since these macros
@@ -83042,6 +92753,18 @@ SQLITE_API int sqlite3_found_count = 0;
mNever = iSrcLine >> 24;
assert( (I & mNever)==0 );
if( sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/
+ /* Invoke the branch coverage callback with three arguments:
+ ** iSrcLine - the line number of the VdbeCoverage() macro, with
+ ** flags removed.
+ ** I - Mask of bits 0x07 indicating which cases are are
+ ** fulfilled by this instance of the jump. 0x01 means
+ ** fall-thru, 0x02 means taken, 0x04 means NULL. Any
+ ** impossible cases (ex: if the comparison is never NULL)
+ ** are filled in automatically so that the coverage
+ ** measurement logic does not flag those impossible cases
+ ** as missed coverage.
+ ** M - Type of jump. Same as M argument above
+ */
I |= mNever;
if( M==2 ) I |= 0x04;
if( M==4 ){
@@ -83054,14 +92777,6 @@ SQLITE_API int sqlite3_found_count = 0;
#endif
/*
-** Convert the given register into a string if it isn't one
-** already. Return non-zero if a malloc() fails.
-*/
-#define Stringify(P, enc) \
- if(((P)->flags&(MEM_Str|MEM_Blob))==0 && sqlite3VdbeMemStringify(P,enc,0)) \
- { goto no_mem; }
-
-/*
** An ephemeral string value (signified by the MEM_Ephem flag) contains
** a pointer to a dynamically allocated string where some other entity
** is responsible for deallocating that string. Because the register
@@ -83087,11 +92802,10 @@ static VdbeCursor *allocateCursor(
Vdbe *p, /* The virtual machine */
int iCur, /* Index of the new VdbeCursor */
int nField, /* Number of fields in the table or index */
- int iDb, /* Database the cursor belongs to, or -1 */
u8 eCurType /* Type of the new cursor */
){
/* Find the memory cell that will be used to store the blob of memory
- ** required for this VdbeCursor structure. It is convenient to use a
+ ** required for this VdbeCursor structure. It is convenient to use a
** vdbe memory cell to manage the memory allocation required for a
** VdbeCursor structure for the following reasons:
**
@@ -83112,32 +92826,65 @@ static VdbeCursor *allocateCursor(
int nByte;
VdbeCursor *pCx = 0;
- nByte =
- ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
+ nByte =
+ ROUND8P(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
(eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0);
assert( iCur>=0 && iCur<p->nCursor );
if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
- sqlite3VdbeFreeCursor(p, p->apCsr[iCur]);
+ sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]);
p->apCsr[iCur] = 0;
}
- if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){
- p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z;
- memset(pCx, 0, offsetof(VdbeCursor,pAltCursor));
- pCx->eCurType = eCurType;
- pCx->iDb = iDb;
- pCx->nField = nField;
- pCx->aOffset = &pCx->aType[nField];
- if( eCurType==CURTYPE_BTREE ){
- pCx->uc.pCursor = (BtCursor*)
- &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
- sqlite3BtreeCursorZero(pCx->uc.pCursor);
+
+ /* There used to be a call to sqlite3VdbeMemClearAndResize() to make sure
+ ** the pMem used to hold space for the cursor has enough storage available
+ ** in pMem->zMalloc. But for the special case of the aMem[] entries used
+ ** to hold cursors, it is faster to in-line the logic. */
+ assert( pMem->flags==MEM_Undefined );
+ assert( (pMem->flags & MEM_Dyn)==0 );
+ assert( pMem->szMalloc==0 || pMem->z==pMem->zMalloc );
+ if( pMem->szMalloc<nByte ){
+ if( pMem->szMalloc>0 ){
+ sqlite3DbFreeNN(pMem->db, pMem->zMalloc);
+ }
+ pMem->z = pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, nByte);
+ if( pMem->zMalloc==0 ){
+ pMem->szMalloc = 0;
+ return 0;
}
+ pMem->szMalloc = nByte;
+ }
+
+ p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->zMalloc;
+ memset(pCx, 0, offsetof(VdbeCursor,pAltCursor));
+ pCx->eCurType = eCurType;
+ pCx->nField = nField;
+ pCx->aOffset = &pCx->aType[nField];
+ if( eCurType==CURTYPE_BTREE ){
+ pCx->uc.pCursor = (BtCursor*)
+ &pMem->z[ROUND8P(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
+ sqlite3BtreeCursorZero(pCx->uc.pCursor);
}
return pCx;
}
/*
+** The string in pRec is known to look like an integer and to have a
+** floating point value of rValue. Return true and set *piValue to the
+** integer value if the string is in range to be an integer. Otherwise,
+** return false.
+*/
+static int alsoAnInt(Mem *pRec, double rValue, i64 *piValue){
+ i64 iValue;
+ iValue = sqlite3RealToI64(rValue);
+ if( sqlite3RealSameAsInt(rValue,iValue) ){
+ *piValue = iValue;
+ return 1;
+ }
+ return 0==sqlite3Atoi64(pRec->z, piValue, pRec->n, pRec->enc);
+}
+
+/*
** Try to convert a value into a numeric representation if we can
** do so without loss of information. In other words, if the string
** looks like a number, convert it into a number. If it does not
@@ -83154,12 +92901,12 @@ static VdbeCursor *allocateCursor(
*/
static void applyNumericAffinity(Mem *pRec, int bTryForInt){
double rValue;
- i64 iValue;
u8 enc = pRec->enc;
- assert( (pRec->flags & (MEM_Str|MEM_Int|MEM_Real))==MEM_Str );
- if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return;
- if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){
- pRec->u.i = iValue;
+ int rc;
+ assert( (pRec->flags & (MEM_Str|MEM_Int|MEM_Real|MEM_IntReal))==MEM_Str );
+ rc = sqlite3AtoF(pRec->z, &rValue, pRec->n, enc);
+ if( rc<=0 ) return;
+ if( rc==1 && alsoAnInt(pRec, rValue, &pRec->u.i) ){
pRec->flags |= MEM_Int;
}else{
pRec->u.r = rValue;
@@ -83179,16 +92926,21 @@ static void applyNumericAffinity(Mem *pRec, int bTryForInt){
** SQLITE_AFF_INTEGER:
** SQLITE_AFF_REAL:
** SQLITE_AFF_NUMERIC:
-** Try to convert pRec to an integer representation or a
+** Try to convert pRec to an integer representation or a
** floating-point representation if an integer representation
** is not possible. Note that the integer representation is
** always preferred, even if the affinity is REAL, because
** an integer representation is more space efficient on disk.
**
+** SQLITE_AFF_FLEXNUM:
+** If the value is text, then try to convert it into a number of
+** some kind (integer or real) but do not make any other changes.
+**
** SQLITE_AFF_TEXT:
** Convert pRec to a text representation.
**
** SQLITE_AFF_BLOB:
+** SQLITE_AFF_NONE:
** No-op. pRec is unchanged.
*/
static void applyAffinity(
@@ -83198,26 +92950,29 @@ static void applyAffinity(
){
if( affinity>=SQLITE_AFF_NUMERIC ){
assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL
- || affinity==SQLITE_AFF_NUMERIC );
+ || affinity==SQLITE_AFF_NUMERIC || affinity==SQLITE_AFF_FLEXNUM );
if( (pRec->flags & MEM_Int)==0 ){ /*OPTIMIZATION-IF-FALSE*/
- if( (pRec->flags & MEM_Real)==0 ){
+ if( (pRec->flags & (MEM_Real|MEM_IntReal))==0 ){
if( pRec->flags & MEM_Str ) applyNumericAffinity(pRec,1);
- }else{
+ }else if( affinity<=SQLITE_AFF_REAL ){
sqlite3VdbeIntegerAffinity(pRec);
}
}
}else if( affinity==SQLITE_AFF_TEXT ){
/* Only attempt the conversion to TEXT if there is an integer or real
** representation (blob and NULL do not get converted) but no string
- ** representation. It would be harmless to repeat the conversion if
+ ** representation. It would be harmless to repeat the conversion if
** there is already a string rep, but it is pointless to waste those
** CPU cycles. */
if( 0==(pRec->flags&MEM_Str) ){ /*OPTIMIZATION-IF-FALSE*/
- if( (pRec->flags&(MEM_Real|MEM_Int)) ){
+ if( (pRec->flags&(MEM_Real|MEM_Int|MEM_IntReal)) ){
+ testcase( pRec->flags & MEM_Int );
+ testcase( pRec->flags & MEM_Real );
+ testcase( pRec->flags & MEM_IntReal );
sqlite3VdbeMemStringify(pRec, enc, 1);
}
}
- pRec->flags &= ~(MEM_Real|MEM_Int);
+ pRec->flags &= ~(MEM_Real|MEM_Int|MEM_IntReal);
}
}
@@ -83238,12 +92993,12 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value *pVal){
}
/*
-** Exported version of applyAffinity(). This one works on sqlite3_value*,
+** Exported version of applyAffinity(). This one works on sqlite3_value*,
** not the internal Mem* type.
*/
SQLITE_PRIVATE void sqlite3ValueApplyAffinity(
- sqlite3_value *pVal,
- u8 affinity,
+ sqlite3_value *pVal,
+ u8 affinity,
u8 enc
){
applyAffinity((Mem *)pVal, affinity, enc);
@@ -83256,12 +93011,24 @@ SQLITE_PRIVATE void sqlite3ValueApplyAffinity(
** accordingly.
*/
static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){
- assert( (pMem->flags & (MEM_Int|MEM_Real))==0 );
+ int rc;
+ sqlite3_int64 ix;
+ assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 );
assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 );
- if( sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc)==0 ){
- return 0;
+ if( ExpandBlob(pMem) ){
+ pMem->u.i = 0;
+ return MEM_Int;
}
- if( sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc)==0 ){
+ rc = sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc);
+ if( rc<=0 ){
+ if( rc==0 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1 ){
+ pMem->u.i = ix;
+ return MEM_Int;
+ }else{
+ return MEM_Real;
+ }
+ }else if( rc==1 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)==0 ){
+ pMem->u.i = ix;
return MEM_Int;
}
return MEM_Real;
@@ -83269,18 +93036,24 @@ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){
/*
** Return the numeric type for pMem, either MEM_Int or MEM_Real or both or
-** none.
+** none.
**
** Unlike applyNumericAffinity(), this routine does not modify pMem->flags.
** But it does set pMem->u.r and pMem->u.i appropriately.
*/
static u16 numericType(Mem *pMem){
- if( pMem->flags & (MEM_Int|MEM_Real) ){
- return pMem->flags & (MEM_Int|MEM_Real);
- }
- if( pMem->flags & (MEM_Str|MEM_Blob) ){
- return computeNumericType(pMem);
- }
+ assert( (pMem->flags & MEM_Null)==0
+ || pMem->db==0 || pMem->db->mallocFailed );
+ if( pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null) ){
+ testcase( pMem->flags & MEM_Int );
+ testcase( pMem->flags & MEM_Real );
+ testcase( pMem->flags & MEM_IntReal );
+ return pMem->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Null);
+ }
+ assert( pMem->flags & (MEM_Str|MEM_Blob) );
+ testcase( pMem->flags & MEM_Str );
+ testcase( pMem->flags & MEM_Blob );
+ return computeNumericType(pMem);
return 0;
}
@@ -83289,12 +93062,9 @@ static u16 numericType(Mem *pMem){
** Write a nice string representation of the contents of cell pMem
** into buffer zBuf, length nBuf.
*/
-SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){
- char *zCsr = zBuf;
+SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr){
int f = pMem->flags;
-
static const char *const encnames[] = {"(X)", "(8)", "(16LE)", "(16BE)"};
-
if( f&MEM_Blob ){
int i;
char c;
@@ -83310,55 +93080,43 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){
}else{
c = 's';
}
- *(zCsr++) = c;
- sqlite3_snprintf(100, zCsr, "%d[", pMem->n);
- zCsr += sqlite3Strlen30(zCsr);
- for(i=0; i<16 && i<pMem->n; i++){
- sqlite3_snprintf(100, zCsr, "%02X", ((int)pMem->z[i] & 0xFF));
- zCsr += sqlite3Strlen30(zCsr);
+ sqlite3_str_appendf(pStr, "%cx[", c);
+ for(i=0; i<25 && i<pMem->n; i++){
+ sqlite3_str_appendf(pStr, "%02X", ((int)pMem->z[i] & 0xFF));
}
- for(i=0; i<16 && i<pMem->n; i++){
+ sqlite3_str_appendf(pStr, "|");
+ for(i=0; i<25 && i<pMem->n; i++){
char z = pMem->z[i];
- if( z<32 || z>126 ) *zCsr++ = '.';
- else *zCsr++ = z;
+ sqlite3_str_appendchar(pStr, 1, (z<32||z>126)?'.':z);
}
- *(zCsr++) = ']';
+ sqlite3_str_appendf(pStr,"]");
if( f & MEM_Zero ){
- sqlite3_snprintf(100, zCsr,"+%dz",pMem->u.nZero);
- zCsr += sqlite3Strlen30(zCsr);
+ sqlite3_str_appendf(pStr, "+%dz",pMem->u.nZero);
}
- *zCsr = '\0';
}else if( f & MEM_Str ){
- int j, k;
- zBuf[0] = ' ';
+ int j;
+ u8 c;
if( f & MEM_Dyn ){
- zBuf[1] = 'z';
+ c = 'z';
assert( (f & (MEM_Static|MEM_Ephem))==0 );
}else if( f & MEM_Static ){
- zBuf[1] = 't';
+ c = 't';
assert( (f & (MEM_Dyn|MEM_Ephem))==0 );
}else if( f & MEM_Ephem ){
- zBuf[1] = 'e';
+ c = 'e';
assert( (f & (MEM_Static|MEM_Dyn))==0 );
}else{
- zBuf[1] = 's';
+ c = 's';
}
- k = 2;
- sqlite3_snprintf(100, &zBuf[k], "%d", pMem->n);
- k += sqlite3Strlen30(&zBuf[k]);
- zBuf[k++] = '[';
- for(j=0; j<15 && j<pMem->n; j++){
- u8 c = pMem->z[j];
- if( c>=0x20 && c<0x7f ){
- zBuf[k++] = c;
- }else{
- zBuf[k++] = '.';
- }
+ sqlite3_str_appendf(pStr, " %c%d[", c, pMem->n);
+ for(j=0; j<25 && j<pMem->n; j++){
+ c = pMem->z[j];
+ sqlite3_str_appendchar(pStr, 1, (c>=0x20&&c<=0x7f) ? c : '.');
+ }
+ sqlite3_str_appendf(pStr, "]%s", encnames[pMem->enc]);
+ if( f & MEM_Term ){
+ sqlite3_str_appendf(pStr, "(0-term)");
}
- zBuf[k++] = ']';
- sqlite3_snprintf(100,&zBuf[k], encnames[pMem->enc]);
- k += sqlite3Strlen30(&zBuf[k]);
- zBuf[k++] = 0;
}
}
#endif
@@ -83374,142 +93132,66 @@ static void memTracePrint(Mem *p){
printf(p->flags & MEM_Zero ? " NULL-nochng" : " NULL");
}else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){
printf(" si:%lld", p->u.i);
+ }else if( (p->flags & (MEM_IntReal))!=0 ){
+ printf(" ir:%lld", p->u.i);
}else if( p->flags & MEM_Int ){
printf(" i:%lld", p->u.i);
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( p->flags & MEM_Real ){
- printf(" r:%g", p->u.r);
+ printf(" r:%.17g", p->u.r);
#endif
}else if( sqlite3VdbeMemIsRowSet(p) ){
printf(" (rowset)");
}else{
- char zBuf[200];
- sqlite3VdbeMemPrettyPrint(p, zBuf);
- printf(" %s", zBuf);
+ StrAccum acc;
+ char zBuf[1000];
+ sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
+ sqlite3VdbeMemPrettyPrint(p, &acc);
+ printf(" %s", sqlite3StrAccumFinish(&acc));
}
if( p->flags & MEM_Subtype ) printf(" subtype=0x%02x", p->eSubtype);
}
static void registerTrace(int iReg, Mem *p){
- printf("REG[%d] = ", iReg);
+ printf("R[%d] = ", iReg);
memTracePrint(p);
+ if( p->pScopyFrom ){
+ printf(" <== R[%d]", (int)(p->pScopyFrom - &p[-iReg]));
+ }
printf("\n");
sqlite3VdbeCheckMemInvariants(p);
}
+/**/ void sqlite3PrintMem(Mem *pMem){
+ memTracePrint(pMem);
+ printf("\n");
+ fflush(stdout);
+}
#endif
#ifdef SQLITE_DEBUG
-# define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M)
-#else
-# define REGISTER_TRACE(R,M)
-#endif
-
-
-#ifdef VDBE_PROFILE
-
-/*
-** hwtime.h contains inline assembler code for implementing
-** high-performance timing routines.
-*/
-/************** Include hwtime.h in the middle of vdbe.c *********************/
-/************** Begin file hwtime.h ******************************************/
-/*
-** 2008 May 27
-**
-** 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 contains inline asm code for retrieving "high-performance"
-** counters for x86 class CPUs.
-*/
-#ifndef SQLITE_HWTIME_H
-#define SQLITE_HWTIME_H
-
/*
-** The following routine only works on pentium-class (or newer) processors.
-** It uses the RDTSC opcode to read the cycle count value out of the
-** processor and returns that value. This can be used for high-res
-** profiling.
+** Show the values of all registers in the virtual machine. Used for
+** interactive debugging.
*/
-#if (defined(__GNUC__) || defined(_MSC_VER)) && \
- (defined(i386) || defined(__i386__) || defined(_M_IX86))
-
- #if defined(__GNUC__)
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned int lo, hi;
- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
- return (sqlite_uint64)hi << 32 | lo;
- }
-
- #elif defined(_MSC_VER)
-
- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
- __asm {
- rdtsc
- ret ; return value at EDX:EAX
- }
- }
-
- #endif
+SQLITE_PRIVATE void sqlite3VdbeRegisterDump(Vdbe *v){
+ int i;
+ for(i=1; i<v->nMem; i++) registerTrace(i, v->aMem+i);
+}
+#endif /* SQLITE_DEBUG */
-#elif (defined(__GNUC__) && defined(__x86_64__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long val;
- __asm__ __volatile__ ("rdtsc" : "=A" (val));
- return val;
- }
-
-#elif (defined(__GNUC__) && defined(__ppc__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long long retval;
- unsigned long junk;
- __asm__ __volatile__ ("\n\
- 1: mftbu %1\n\
- mftb %L0\n\
- mftbu %0\n\
- cmpw %0,%1\n\
- bne 1b"
- : "=r" (retval), "=r" (junk));
- return retval;
- }
+#ifdef SQLITE_DEBUG
+# define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M)
#else
-
- #error Need implementation of sqlite3Hwtime() for your platform.
-
- /*
- ** To compile without implementing sqlite3Hwtime() for your platform,
- ** you can remove the above #error and use the following
- ** stub function. You will lose timing support for many
- ** of the debugging and testing utilities, but it should at
- ** least compile and run.
- */
-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
-
-#endif
-
-#endif /* !defined(SQLITE_HWTIME_H) */
-
-/************** End of hwtime.h **********************************************/
-/************** Continuing where we left off in vdbe.c ***********************/
-
+# define REGISTER_TRACE(R,M)
#endif
#ifndef NDEBUG
/*
** This function is only called from within an assert() expression. It
** checks that the sqlite3.nTransaction variable is correctly set to
-** the number of non-transaction savepoints currently in the
+** the number of non-transaction savepoints currently in the
** linked list starting at sqlite3.pSavepoint.
-**
+**
** Usage:
**
** assert( checkSavepointCount(db) );
@@ -83546,65 +93228,194 @@ static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){
}
}
+/*
+** Compute a bloom filter hash using pOp->p4.i registers from aMem[] beginning
+** with pOp->p3. Return the hash.
+*/
+static u64 filterHash(const Mem *aMem, const Op *pOp){
+ int i, mx;
+ u64 h = 0;
+
+ assert( pOp->p4type==P4_INT32 );
+ for(i=pOp->p3, mx=i+pOp->p4.i; i<mx; i++){
+ const Mem *p = &aMem[i];
+ if( p->flags & (MEM_Int|MEM_IntReal) ){
+ h += p->u.i;
+ }else if( p->flags & MEM_Real ){
+ h += sqlite3VdbeIntValue(p);
+ }else if( p->flags & (MEM_Str|MEM_Blob) ){
+ /* All strings have the same hash and all blobs have the same hash,
+ ** though, at least, those hashes are different from each other and
+ ** from NULL. */
+ h += 4093 + (p->flags & (MEM_Str|MEM_Blob));
+ }
+ }
+ return h;
+}
+
+
+/*
+** For OP_Column, factor out the case where content is loaded from
+** overflow pages, so that the code to implement this case is separate
+** the common case where all content fits on the page. Factoring out
+** the code reduces register pressure and helps the common case
+** to run faster.
+*/
+static SQLITE_NOINLINE int vdbeColumnFromOverflow(
+ VdbeCursor *pC, /* The BTree cursor from which we are reading */
+ int iCol, /* The column to read */
+ int t, /* The serial-type code for the column value */
+ i64 iOffset, /* Offset to the start of the content value */
+ u32 cacheStatus, /* Current Vdbe.cacheCtr value */
+ u32 colCacheCtr, /* Current value of the column cache counter */
+ Mem *pDest /* Store the value into this register. */
+){
+ int rc;
+ sqlite3 *db = pDest->db;
+ int encoding = pDest->enc;
+ int len = sqlite3VdbeSerialTypeLen(t);
+ assert( pC->eCurType==CURTYPE_BTREE );
+ if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) return SQLITE_TOOBIG;
+ if( len > 4000 && pC->pKeyInfo==0 ){
+ /* Cache large column values that are on overflow pages using
+ ** an RCStr (reference counted string) so that if they are reloaded,
+ ** that do not have to be copied a second time. The overhead of
+ ** creating and managing the cache is such that this is only
+ ** profitable for larger TEXT and BLOB values.
+ **
+ ** Only do this on table-btrees so that writes to index-btrees do not
+ ** need to clear the cache. This buys performance in the common case
+ ** in exchange for generality.
+ */
+ VdbeTxtBlbCache *pCache;
+ char *pBuf;
+ if( pC->colCache==0 ){
+ pC->pCache = sqlite3DbMallocZero(db, sizeof(VdbeTxtBlbCache) );
+ if( pC->pCache==0 ) return SQLITE_NOMEM;
+ pC->colCache = 1;
+ }
+ pCache = pC->pCache;
+ if( pCache->pCValue==0
+ || pCache->iCol!=iCol
+ || pCache->cacheStatus!=cacheStatus
+ || pCache->colCacheCtr!=colCacheCtr
+ || pCache->iOffset!=sqlite3BtreeOffset(pC->uc.pCursor)
+ ){
+ if( pCache->pCValue ) sqlite3RCStrUnref(pCache->pCValue);
+ pBuf = pCache->pCValue = sqlite3RCStrNew( len+3 );
+ if( pBuf==0 ) return SQLITE_NOMEM;
+ rc = sqlite3BtreePayload(pC->uc.pCursor, iOffset, len, pBuf);
+ if( rc ) return rc;
+ pBuf[len] = 0;
+ pBuf[len+1] = 0;
+ pBuf[len+2] = 0;
+ pCache->iCol = iCol;
+ pCache->cacheStatus = cacheStatus;
+ pCache->colCacheCtr = colCacheCtr;
+ pCache->iOffset = sqlite3BtreeOffset(pC->uc.pCursor);
+ }else{
+ pBuf = pCache->pCValue;
+ }
+ assert( t>=12 );
+ sqlite3RCStrRef(pBuf);
+ if( t&1 ){
+ rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, encoding,
+ sqlite3RCStrUnref);
+ pDest->flags |= MEM_Term;
+ }else{
+ rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, 0,
+ sqlite3RCStrUnref);
+ }
+ }else{
+ rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, iOffset, len, pDest);
+ if( rc ) return rc;
+ sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest);
+ if( (t&1)!=0 && encoding==SQLITE_UTF8 ){
+ pDest->z[len] = 0;
+ pDest->flags |= MEM_Term;
+ }
+ }
+ pDest->flags &= ~MEM_Ephem;
+ return rc;
+}
+
+
+/*
+** Return the symbolic name for the data type of a pMem
+*/
+static const char *vdbeMemTypeName(Mem *pMem){
+ static const char *azTypes[] = {
+ /* SQLITE_INTEGER */ "INT",
+ /* SQLITE_FLOAT */ "REAL",
+ /* SQLITE_TEXT */ "TEXT",
+ /* SQLITE_BLOB */ "BLOB",
+ /* SQLITE_NULL */ "NULL"
+ };
+ return azTypes[sqlite3_value_type(pMem)-1];
+}
/*
** Execute as much of a VDBE program as we can.
-** This is the core of sqlite3_step().
+** This is the core of sqlite3_step().
*/
SQLITE_PRIVATE int sqlite3VdbeExec(
Vdbe *p /* The VDBE */
){
Op *aOp = p->aOp; /* Copy of p->aOp */
Op *pOp = aOp; /* Current operation */
-#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
- Op *pOrigOp; /* Value of pOp at the top of the loop */
-#endif
#ifdef SQLITE_DEBUG
+ Op *pOrigOp; /* Value of pOp at the top of the loop */
int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */
+ u8 iCompareIsInit = 0; /* iCompare is initialized */
#endif
int rc = SQLITE_OK; /* Value to return */
sqlite3 *db = p->db; /* The database */
u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */
u8 encoding = ENC(db); /* The database encoding */
int iCompare = 0; /* Result of last comparison */
- unsigned nVmStep = 0; /* Number of virtual machine steps */
+ u64 nVmStep = 0; /* Number of virtual machine steps */
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- unsigned nProgressLimit; /* Invoke xProgress() when nVmStep reaches this */
+ u64 nProgressLimit; /* Invoke xProgress() when nVmStep reaches this */
#endif
Mem *aMem = p->aMem; /* Copy of p->aMem */
Mem *pIn1 = 0; /* 1st input operand */
Mem *pIn2 = 0; /* 2nd input operand */
Mem *pIn3 = 0; /* 3rd input operand */
Mem *pOut = 0; /* Output operand */
-#ifdef VDBE_PROFILE
- u64 start; /* CPU clock count at start of opcode */
+ u32 colCacheCtr = 0; /* Column cache counter */
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
+ u64 *pnCycle = 0;
+ int bStmtScanStatus = IS_STMT_SCANSTATUS(db)!=0;
#endif
/*** INSERT STACK UNION HERE ***/
- assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
- sqlite3VdbeEnter(p);
+ assert( p->eVdbeState==VDBE_RUN_STATE ); /* sqlite3_step() verifies this */
+ if( DbMaskNonZero(p->lockMask) ){
+ sqlite3VdbeEnter(p);
+ }
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ if( db->xProgress ){
+ u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
+ assert( 0 < db->nProgressOps );
+ nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps);
+ }else{
+ nProgressLimit = LARGEST_UINT64;
+ }
+#endif
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
** sqlite3_column_text16() failed. */
goto no_mem;
}
assert( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_BUSY );
+ testcase( p->rc!=SQLITE_OK );
+ p->rc = SQLITE_OK;
assert( p->bIsReader || p->readOnly!=0 );
p->iCurrentTime = 0;
assert( p->explain==0 );
- p->pResultSet = 0;
db->busyHandler.nBusy = 0;
- if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
+ if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt;
sqlite3VdbeIOTraceSql(p);
-#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- if( db->xProgress ){
- u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
- assert( 0 < db->nProgressOps );
- nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps);
- }else{
- nProgressLimit = 0xffffffff;
- }
-#endif
#ifdef SQLITE_DEBUG
sqlite3BeginBenignMalloc();
if( p->pc==0
@@ -83638,12 +93449,18 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
assert( rc==SQLITE_OK );
assert( pOp>=aOp && pOp<&aOp[p->nOp]);
-#ifdef VDBE_PROFILE
- start = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
-#endif
nVmStep++;
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- if( p->anExec ) p->anExec[(int)(pOp-aOp)]++;
+
+#if defined(VDBE_PROFILE)
+ pOp->nExec++;
+ pnCycle = &pOp->nCycle;
+ if( sqlite3NProfileCnt==0 ) *pnCycle -= sqlite3Hwtime();
+#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+ if( bStmtScanStatus ){
+ pOp->nExec++;
+ pnCycle = &pOp->nCycle;
+ *pnCycle -= sqlite3Hwtime();
+ }
#endif
/* Only allow tracing if SQLITE_DEBUG is defined.
@@ -83651,9 +93468,10 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
#ifdef SQLITE_DEBUG
if( db->flags & SQLITE_VdbeTrace ){
sqlite3VdbePrintOp(stdout, (int)(pOp - aOp), pOp);
+ test_trace_breakpoint((int)(pOp - aOp),pOp,p);
}
#endif
-
+
/* Check to see if we need to simulate an interrupt. This only happens
** if we have a special test build.
@@ -83704,10 +93522,10 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
}
}
#endif
-#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
+#ifdef SQLITE_DEBUG
pOrigOp = pOp;
#endif
-
+
switch( pOp->opcode ){
/*****************************************************************************
@@ -83748,7 +93566,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
/* Opcode: Goto * P2 * * *
**
** An unconditional jump to address P2.
-** The next instruction executed will be
+** The next instruction executed will be
** the one at index P2 from the beginning of
** the program.
**
@@ -83758,13 +93576,27 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
** to the current line should be indented for EXPLAIN output.
*/
case OP_Goto: { /* jump */
+
+#ifdef SQLITE_DEBUG
+ /* In debugging mode, when the p5 flags is set on an OP_Goto, that
+ ** means we should really jump back to the preceding OP_ReleaseReg
+ ** instruction. */
+ if( pOp->p5 ){
+ assert( pOp->p2 < (int)(pOp - aOp) );
+ assert( pOp->p2 > 1 );
+ pOp = &aOp[pOp->p2 - 2];
+ assert( pOp[1].opcode==OP_ReleaseReg );
+ goto check_for_interrupt;
+ }
+#endif
+
jump_to_p2_and_check_for_interrupt:
pOp = &aOp[pOp->p2 - 1];
/* Opcodes that are used as the bottom of a loop (OP_Next, OP_Prev,
** OP_VNext, or OP_SorterNext) all jump here upon
** completion. Check to see if sqlite3_interrupt() has been called
- ** or if the progress callback needs to be invoked.
+ ** or if the progress callback needs to be invoked.
**
** This code uses unstructured "goto" statements and does not look clean.
** But that is not due to sloppy coding habits. The code is written this
@@ -83772,7 +93604,7 @@ jump_to_p2_and_check_for_interrupt:
** checks on every opcode. This helps sqlite3_step() to run about 1.5%
** faster according to "valgrind --tool=cachegrind" */
check_for_interrupt:
- if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
+ if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt;
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
/* Call the progress callback if it is configured and the required number
** of VDBE ops have been executed (either since this invocation of
@@ -83780,16 +93612,17 @@ check_for_interrupt:
** If the progress callback returns non-zero, exit the virtual machine with
** a return code SQLITE_ABORT.
*/
- if( nVmStep>=nProgressLimit && db->xProgress!=0 ){
+ while( nVmStep>=nProgressLimit && db->xProgress!=0 ){
assert( db->nProgressOps!=0 );
- nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps);
+ nProgressLimit += db->nProgressOps;
if( db->xProgress(db->pProgressArg) ){
+ nProgressLimit = LARGEST_UINT64;
rc = SQLITE_INTERRUPT;
goto abort_due_to_error;
}
}
#endif
-
+
break;
}
@@ -83806,24 +93639,39 @@ case OP_Gosub: { /* jump */
pIn1->flags = MEM_Int;
pIn1->u.i = (int)(pOp-aOp);
REGISTER_TRACE(pOp->p1, pIn1);
-
- /* Most jump operations do a goto to this spot in order to update
- ** the pOp pointer. */
-jump_to_p2:
- pOp = &aOp[pOp->p2 - 1];
- break;
+ goto jump_to_p2_and_check_for_interrupt;
}
-/* Opcode: Return P1 * * * *
+/* Opcode: Return P1 P2 P3 * *
**
-** Jump to the next instruction after the address in register P1. After
-** the jump, register P1 becomes undefined.
+** Jump to the address stored in register P1. If P1 is a return address
+** register, then this accomplishes a return from a subroutine.
+**
+** If P3 is 1, then the jump is only taken if register P1 holds an integer
+** values, otherwise execution falls through to the next opcode, and the
+** OP_Return becomes a no-op. If P3 is 0, then register P1 must hold an
+** integer or else an assert() is raised. P3 should be set to 1 when
+** this opcode is used in combination with OP_BeginSubrtn, and set to 0
+** otherwise.
+**
+** The value in register P1 is unchanged by this opcode.
+**
+** P2 is not used by the byte-code engine. However, if P2 is positive
+** and also less than the current address, then the "EXPLAIN" output
+** formatter in the CLI will indent all opcodes from the P2 opcode up
+** to be not including the current Return. P2 should be the first opcode
+** in the subroutine from which this opcode is returning. Thus the P2
+** value is a byte-code indentation hint. See tag-20220407a in
+** wherecode.c and shell.c.
*/
case OP_Return: { /* in1 */
pIn1 = &aMem[pOp->p1];
- assert( pIn1->flags==MEM_Int );
- pOp = &aOp[pIn1->u.i];
- pIn1->flags = MEM_Undefined;
+ if( pIn1->flags & MEM_Int ){
+ if( pOp->p3 ){ VdbeBranchTaken(1, 2); }
+ pOp = &aOp[pIn1->u.i];
+ }else if( ALWAYS(pOp->p3) ){
+ VdbeBranchTaken(0, 2);
+ }
break;
}
@@ -83846,7 +93694,14 @@ case OP_InitCoroutine: { /* jump */
assert( !VdbeMemDynamic(pOut) );
pOut->u.i = pOp->p3 - 1;
pOut->flags = MEM_Int;
- if( pOp->p2 ) goto jump_to_p2;
+ if( pOp->p2==0 ) break;
+
+ /* Most jump operations do a goto to this spot in order to update
+ ** the pOp pointer. */
+jump_to_p2:
+ assert( pOp->p2>0 ); /* There are never any jumps to instruction 0 */
+ assert( pOp->p2<p->nOp ); /* Jumps must be in range */
+ pOp = &aOp[pOp->p2 - 1];
break;
}
@@ -83911,6 +93766,7 @@ case OP_HaltIfNull: { /* in3 */
#endif
if( (pIn3->flags & MEM_Null)==0 ) break;
/* Fall through into OP_Halt */
+ /* no break */ deliberate_fall_through
}
/* Opcode: Halt P1 P2 * P4 P5
@@ -83924,14 +93780,14 @@ case OP_HaltIfNull: { /* in3 */
** whether or not to rollback the current transaction. Do not rollback
** if P2==OE_Fail. Do the rollback if P2==OE_Rollback. If P2==OE_Abort,
** then back out all changes that have occurred during this execution of the
-** VDBE, but do not rollback the transaction.
+** VDBE, but do not rollback the transaction.
**
** If P4 is not null then it is an error message string.
**
** P5 is a value between 0 and 4, inclusive, that modifies the P4 string.
**
** 0: (no change)
-** 1: NOT NULL contraint failed: P4
+** 1: NOT NULL constraint failed: P4
** 2: UNIQUE constraint failed: P4
** 3: CHECK constraint failed: P4
** 4: FOREIGN KEY constraint failed: P4
@@ -83947,11 +93803,16 @@ case OP_Halt: {
VdbeFrame *pFrame;
int pcx;
- pcx = (int)(pOp - aOp);
#ifdef SQLITE_DEBUG
if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); }
#endif
- if( pOp->p1==SQLITE_OK && p->pFrame ){
+
+ /* A deliberately coded "OP_Halt SQLITE_INTERNAL * * * *" opcode indicates
+ ** something is wrong with the code generator. Raise an assertion in order
+ ** to bring this to the attention of fuzzers and other testing tools. */
+ assert( pOp->p1!=SQLITE_INTERNAL );
+
+ if( p->pFrame && pOp->p1==SQLITE_OK ){
/* Halt the sub-program. Return control to the parent frame. */
pFrame = p->pFrame;
p->pFrame = pFrame->pParent;
@@ -83959,7 +93820,7 @@ case OP_Halt: {
sqlite3VdbeSetChanges(db, p->nChange);
pcx = sqlite3VdbeFrameRestore(pFrame);
if( pOp->p2==OE_Ignore ){
- /* Instruction pcx is the OP_Program that invoked the sub-program
+ /* Instruction pcx is the OP_Program that invoked the sub-program
** currently being halted. If the p2 instruction of this OP_Halt
** instruction is set to OE_Ignore, then the sub-program is throwing
** an IGNORE exception. In this case jump to the address specified
@@ -83973,7 +93834,6 @@ case OP_Halt: {
}
p->rc = pOp->p1;
p->errorAction = (u8)pOp->p2;
- p->pc = pcx;
assert( pOp->p5<=4 );
if( p->rc ){
if( pOp->p5 ){
@@ -83990,6 +93850,7 @@ case OP_Halt: {
}else{
sqlite3VdbeError(p, "%s", pOp->p4.z);
}
+ pcx = (int)(pOp - aOp);
sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg);
}
rc = sqlite3VdbeHalt(p);
@@ -84047,7 +93908,7 @@ case OP_Real: { /* same as TK_FLOAT, out2 */
/* Opcode: String8 * P2 * P4 *
** Synopsis: r[P2]='P4'
**
-** P4 points to a nul terminated UTF-8 string. This opcode is transformed
+** P4 points to a nul terminated UTF-8 string. This opcode is transformed
** into a String opcode before it is executed for the first time. During
** this transformation, the length of string P4 is computed and stored
** as the P1 parameter.
@@ -84055,13 +93916,13 @@ case OP_Real: { /* same as TK_FLOAT, out2 */
case OP_String8: { /* same as TK_STRING, out2 */
assert( pOp->p4.z!=0 );
pOut = out2Prerelease(p, pOp);
- pOp->opcode = OP_String;
pOp->p1 = sqlite3Strlen30(pOp->p4.z);
#ifndef SQLITE_OMIT_UTF16
if( encoding!=SQLITE_UTF8 ){
rc = sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC);
assert( rc==SQLITE_OK || rc==SQLITE_TOOBIG );
+ if( rc ) goto too_big;
if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem;
assert( pOut->szMalloc>0 && pOut->zMalloc==pOut->z );
assert( VdbeMemDynamic(pOut)==0 );
@@ -84074,15 +93935,16 @@ case OP_String8: { /* same as TK_STRING, out2 */
pOp->p4.z = pOut->z;
pOp->p1 = pOut->n;
}
- testcase( rc==SQLITE_TOOBIG );
#endif
if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
+ pOp->opcode = OP_String;
assert( rc==SQLITE_OK );
/* Fall through to the next case, OP_String */
+ /* no break */ deliberate_fall_through
}
-
+
/* Opcode: String P1 P2 P3 P4 P5
** Synopsis: r[P2]='P4' (len=P1)
**
@@ -84114,6 +93976,28 @@ case OP_String: { /* out2 */
break;
}
+/* Opcode: BeginSubrtn * P2 * * *
+** Synopsis: r[P2]=NULL
+**
+** Mark the beginning of a subroutine that can be entered in-line
+** or that can be called using OP_Gosub. The subroutine should
+** be terminated by an OP_Return instruction that has a P1 operand that
+** is the same as the P2 operand to this opcode and that has P3 set to 1.
+** If the subroutine is entered in-line, then the OP_Return will simply
+** fall through. But if the subroutine is entered using OP_Gosub, then
+** the OP_Return will jump back to the first instruction after the OP_Gosub.
+**
+** This routine works by loading a NULL into the P2 register. When the
+** return address register contains a NULL, the OP_Return instruction is
+** a no-op that simply falls through to the next instruction (assuming that
+** the OP_Return opcode has a P3 value of 1). Thus if the subroutine is
+** entered in-line, then the OP_Return will cause in-line execution to
+** continue. But if the subroutine is entered via OP_Gosub, then the
+** OP_Return will cause a return to the address following the OP_Gosub.
+**
+** This opcode is identical to OP_Null. It has a different name
+** only to make the byte code easier to read and verify.
+*/
/* Opcode: Null P1 P2 P3 * *
** Synopsis: r[P2..P3]=NULL
**
@@ -84126,6 +94010,7 @@ case OP_String: { /* out2 */
** NULL values will not compare equal even if SQLITE_NULLEQ is set on
** OP_Ne or OP_Eq.
*/
+case OP_BeginSubrtn:
case OP_Null: { /* out2 */
int cnt;
u16 nullFlag;
@@ -84167,12 +94052,18 @@ case OP_SoftNull: {
** Synopsis: r[P2]=P4 (len=P1)
**
** P4 points to a blob of data P1 bytes long. Store this
-** blob in register P2.
+** blob in register P2. If P4 is a NULL pointer, then construct
+** a zero-filled blob that is P1 bytes long in P2.
*/
case OP_Blob: { /* out2 */
assert( pOp->p1 <= SQLITE_MAX_LENGTH );
pOut = out2Prerelease(p, pOp);
- sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0);
+ if( pOp->p4.z==0 ){
+ sqlite3VdbeMemSetZeroBlob(pOut, pOp->p1);
+ if( sqlite3VdbeMemExpandBlob(pOut) ) goto no_mem;
+ }else{
+ sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0);
+ }
pOut->enc = encoding;
UPDATE_MAX_BLOBSIZE(pOut);
break;
@@ -84196,7 +94087,10 @@ case OP_Variable: { /* out2 */
goto too_big;
}
pOut = &aMem[pOp->p2];
- sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static);
+ if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut);
+ memcpy(pOut, pVar, MEMCELLSIZE);
+ pOut->flags &= ~(MEM_Dyn|MEM_Ephem);
+ pOut->flags |= MEM_Static|MEM_FromBind;
UPDATE_MAX_BLOBSIZE(pOut);
break;
}
@@ -84230,8 +94124,13 @@ case OP_Move: {
memAboutToChange(p, pOut);
sqlite3VdbeMemMove(pOut, pIn1);
#ifdef SQLITE_DEBUG
- if( pOut->pScopyFrom>=&aMem[p1] && pOut->pScopyFrom<pOut ){
- pOut->pScopyFrom += pOp->p2 - p1;
+ pIn1->pScopyFrom = 0;
+ { int i;
+ for(i=1; i<p->nMem; i++){
+ if( aMem[i].pScopyFrom==pIn1 ){
+ aMem[i].pScopyFrom = pOut;
+ }
+ }
}
#endif
Deephemeralize(pOut);
@@ -84242,11 +94141,16 @@ case OP_Move: {
break;
}
-/* Opcode: Copy P1 P2 P3 * *
+/* Opcode: Copy P1 P2 P3 * P5
** Synopsis: r[P2@P3+1]=r[P1@P3+1]
**
** Make a copy of registers P1..P1+P3 into registers P2..P2+P3.
**
+** If the 0x0002 bit of P5 is set then also clear the MEM_Subtype flag in the
+** destination. The 0x0001 bit of P5 indicates that this Copy opcode cannot
+** be merged. The 0x0001 bit is used by the query planner and does not
+** come into play during query execution.
+**
** This instruction makes a deep copy of the value. A duplicate
** is made of any string or blob constant. See also OP_SCopy.
*/
@@ -84261,6 +94165,9 @@ case OP_Copy: {
memAboutToChange(p, pOut);
sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
Deephemeralize(pOut);
+ if( (pOut->flags & MEM_Subtype)!=0 && (pOp->p5 & 0x0002)!=0 ){
+ pOut->flags &= ~MEM_Subtype;
+ }
#ifdef SQLITE_DEBUG
pOut->pScopyFrom = 0;
#endif
@@ -84313,6 +94220,24 @@ case OP_IntCopy: { /* out2 */
break;
}
+/* Opcode: FkCheck * * * * *
+**
+** Halt with an SQLITE_CONSTRAINT error if there are any unresolved
+** foreign key constraint violations. If there are no foreign key
+** constraint violations, this is a no-op.
+**
+** FK constraint violations are also checked when the prepared statement
+** exits. This opcode is used to raise foreign key constraint errors prior
+** to returning results such as a row change count or the result of a
+** RETURNING clause.
+*/
+case OP_FkCheck: {
+ if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ break;
+}
+
/* Opcode: ResultRow P1 P2 * * *
** Synopsis: output=r[P1@P2]
**
@@ -84323,76 +94248,32 @@ case OP_IntCopy: { /* out2 */
** the result row.
*/
case OP_ResultRow: {
- Mem *pMem;
- int i;
assert( p->nResColumn==pOp->p2 );
- assert( pOp->p1>0 );
+ assert( pOp->p1>0 || CORRUPT_DB );
assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
-#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- /* Run the progress counter just before returning.
- */
- if( db->xProgress!=0
- && nVmStep>=nProgressLimit
- && db->xProgress(db->pProgressArg)!=0
- ){
- rc = SQLITE_INTERRUPT;
- goto abort_due_to_error;
- }
-#endif
-
- /* If this statement has violated immediate foreign key constraints, do
- ** not return the number of rows modified. And do not RELEASE the statement
- ** transaction. It needs to be rolled back. */
- if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){
- assert( db->flags&SQLITE_CountRows );
- assert( p->usesStmtJournal );
- goto abort_due_to_error;
- }
-
- /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
- ** DML statements invoke this opcode to return the number of rows
- ** modified to the user. This is the only way that a VM that
- ** opens a statement transaction may invoke this opcode.
- **
- ** In case this is such a statement, close any statement transaction
- ** opened by this VM before returning control to the user. This is to
- ** ensure that statement-transactions are always nested, not overlapping.
- ** If the open statement-transaction is not closed here, then the user
- ** may step another VM that opens its own statement transaction. This
- ** may lead to overlapping statement transactions.
- **
- ** The statement transaction is never a top-level transaction. Hence
- ** the RELEASE call below can never fail.
- */
- assert( p->iStatement==0 || db->flags&SQLITE_CountRows );
- rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE);
- assert( rc==SQLITE_OK );
-
- /* Invalidate all ephemeral cursor row caches */
p->cacheCtr = (p->cacheCtr + 2)|1;
-
- /* Make sure the results of the current row are \000 terminated
- ** and have an assigned type. The results are de-ephemeralized as
- ** a side effect.
- */
- pMem = p->pResultSet = &aMem[pOp->p1];
- for(i=0; i<pOp->p2; i++){
- assert( memIsValid(&pMem[i]) );
- Deephemeralize(&pMem[i]);
- assert( (pMem[i].flags & MEM_Ephem)==0
- || (pMem[i].flags & (MEM_Str|MEM_Blob))==0 );
- sqlite3VdbeMemNulTerminate(&pMem[i]);
- REGISTER_TRACE(pOp->p1+i, &pMem[i]);
+ p->pResultRow = &aMem[pOp->p1];
+#ifdef SQLITE_DEBUG
+ {
+ Mem *pMem = p->pResultRow;
+ int i;
+ for(i=0; i<pOp->p2; i++){
+ assert( memIsValid(&pMem[i]) );
+ REGISTER_TRACE(pOp->p1+i, &pMem[i]);
+ /* The registers in the result will not be used again when the
+ ** prepared statement restarts. This is because sqlite3_column()
+ ** APIs might have caused type conversions of made other changes to
+ ** the register values. Therefore, we can go ahead and break any
+ ** OP_SCopy dependencies. */
+ pMem[i].pScopyFrom = 0;
+ }
}
+#endif
if( db->mallocFailed ) goto no_mem;
-
if( db->mTrace & SQLITE_TRACE_ROW ){
- db->xTrace(SQLITE_TRACE_ROW, db->pTraceArg, p, 0);
+ db->trace.xV2(SQLITE_TRACE_ROW, db->pTraceArg, p, 0);
}
-
- /* Return SQLITE_ROW
- */
p->pc = (int)(pOp - aOp) + 1;
rc = SQLITE_ROW;
goto vdbe_return;
@@ -84412,19 +94293,37 @@ case OP_ResultRow: {
** to avoid a memcpy().
*/
case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
- i64 nByte;
+ i64 nByte; /* Total size of the output string or blob */
+ u16 flags1; /* Initial flags for P1 */
+ u16 flags2; /* Initial flags for P2 */
pIn1 = &aMem[pOp->p1];
pIn2 = &aMem[pOp->p2];
pOut = &aMem[pOp->p3];
+ testcase( pOut==pIn2 );
assert( pIn1!=pOut );
- if( (pIn1->flags | pIn2->flags) & MEM_Null ){
+ flags1 = pIn1->flags;
+ testcase( flags1 & MEM_Null );
+ testcase( pIn2->flags & MEM_Null );
+ if( (flags1 | pIn2->flags) & MEM_Null ){
sqlite3VdbeMemSetNull(pOut);
break;
}
- if( ExpandBlob(pIn1) || ExpandBlob(pIn2) ) goto no_mem;
- Stringify(pIn1, encoding);
- Stringify(pIn2, encoding);
+ if( (flags1 & (MEM_Str|MEM_Blob))==0 ){
+ if( sqlite3VdbeMemStringify(pIn1,encoding,0) ) goto no_mem;
+ flags1 = pIn1->flags & ~MEM_Str;
+ }else if( (flags1 & MEM_Zero)!=0 ){
+ if( sqlite3VdbeMemExpandBlob(pIn1) ) goto no_mem;
+ flags1 = pIn1->flags & ~MEM_Str;
+ }
+ flags2 = pIn2->flags;
+ if( (flags2 & (MEM_Str|MEM_Blob))==0 ){
+ if( sqlite3VdbeMemStringify(pIn2,encoding,0) ) goto no_mem;
+ flags2 = pIn2->flags & ~MEM_Str;
+ }else if( (flags2 & MEM_Zero)!=0 ){
+ if( sqlite3VdbeMemExpandBlob(pIn2) ) goto no_mem;
+ flags2 = pIn2->flags & ~MEM_Str;
+ }
nByte = pIn1->n + pIn2->n;
if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
@@ -84435,8 +94334,13 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
MemSetTypeFlag(pOut, MEM_Str);
if( pOut!=pIn2 ){
memcpy(pOut->z, pIn2->z, pIn2->n);
+ assert( (pIn2->flags & MEM_Dyn) == (flags2 & MEM_Dyn) );
+ pIn2->flags = flags2;
}
memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n);
+ assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) );
+ pIn1->flags = flags1;
+ if( encoding>SQLITE_UTF8 ) nByte &= ~1;
pOut->z[nByte]=0;
pOut->z[nByte+1] = 0;
pOut->flags |= MEM_Term;
@@ -84472,15 +94376,15 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
** Synopsis: r[P3]=r[P2]/r[P1]
**
** Divide the value in register P1 by the value in register P2
-** and store the result in register P3 (P3=P2/P1). If the value in
-** register P1 is zero, then the result is NULL. If either input is
+** and store the result in register P3 (P3=P2/P1). If the value in
+** register P1 is zero, then the result is NULL. If either input is
** NULL, the result is NULL.
*/
/* Opcode: Remainder P1 P2 P3 * *
** Synopsis: r[P3]=r[P2]%r[P1]
**
-** Compute the remainder after integer register P2 is divided by
-** register P1 and store the result in register P3.
+** Compute the remainder after integer register P2 is divided by
+** register P1 and store the result in register P3.
** If the value in register P1 is zero the result is NULL.
** If either operand is NULL, the result is NULL.
*/
@@ -84489,8 +94393,6 @@ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */
case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */
case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */
case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
- char bIntint; /* Started out as two integer operands */
- u16 flags; /* Combined MEM_* flags from both inputs */
u16 type1; /* Numeric type of left operand */
u16 type2; /* Numeric type of right operand */
i64 iA; /* Integer value of left operand */
@@ -84499,15 +94401,14 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
double rB; /* Real value of right operand */
pIn1 = &aMem[pOp->p1];
- type1 = numericType(pIn1);
+ type1 = pIn1->flags;
pIn2 = &aMem[pOp->p2];
- type2 = numericType(pIn2);
+ type2 = pIn2->flags;
pOut = &aMem[pOp->p3];
- flags = pIn1->flags | pIn2->flags;
if( (type1 & type2 & MEM_Int)!=0 ){
+int_math:
iA = pIn1->u.i;
iB = pIn2->u.i;
- bIntint = 1;
switch( pOp->opcode ){
case OP_Add: if( sqlite3AddInt64(&iB,iA) ) goto fp_math; break;
case OP_Subtract: if( sqlite3SubInt64(&iB,iA) ) goto fp_math; break;
@@ -84527,10 +94428,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
}
pOut->u.i = iB;
MemSetTypeFlag(pOut, MEM_Int);
- }else if( (flags & MEM_Null)!=0 ){
+ }else if( ((type1 | type2) & MEM_Null)!=0 ){
goto arithmetic_result_is_null;
}else{
- bIntint = 0;
+ type1 = numericType(pIn1);
+ type2 = numericType(pIn2);
+ if( (type1 & type2 & MEM_Int)!=0 ) goto int_math;
fp_math:
rA = sqlite3VdbeRealValue(pIn1);
rB = sqlite3VdbeRealValue(pIn2);
@@ -84545,8 +94448,8 @@ fp_math:
break;
}
default: {
- iA = (i64)rA;
- iB = (i64)rB;
+ iA = sqlite3VdbeIntValue(pIn1);
+ iB = sqlite3VdbeIntValue(pIn2);
if( iA==0 ) goto arithmetic_result_is_null;
if( iA==-1 ) iA = 1;
rB = (double)(iB % iA);
@@ -84562,9 +94465,6 @@ fp_math:
}
pOut->u.r = rB;
MemSetTypeFlag(pOut, MEM_Real);
- if( ((type1|type2)&MEM_Real)==0 && !bIntint ){
- sqlite3VdbeIntegerAffinity(pOut);
- }
#endif
}
break;
@@ -84681,7 +94581,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
/* Opcode: AddImm P1 P2 * * *
** Synopsis: r[P1]=r[P1]+P2
-**
+**
** Add the constant P2 to the value in register P1.
** The result is always an integer.
**
@@ -84691,12 +94591,12 @@ case OP_AddImm: { /* in1 */
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
sqlite3VdbeMemIntegerify(pIn1);
- pIn1->u.i += pOp->p2;
+ *(u64*)&pIn1->u.i += (u64)pOp->p2;
break;
}
/* Opcode: MustBeInt P1 P2 * * *
-**
+**
** Force the value in register P1 to be an integer. If the value
** in P1 is not an integer and cannot be converted into an integer
** without data loss, then jump immediately to P2, or if P2==0
@@ -84706,8 +94606,8 @@ case OP_MustBeInt: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
if( (pIn1->flags & MEM_Int)==0 ){
applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
- VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2);
if( (pIn1->flags & MEM_Int)==0 ){
+ VdbeBranchTaken(1, 2);
if( pOp->p2==0 ){
rc = SQLITE_MISMATCH;
goto abort_due_to_error;
@@ -84716,6 +94616,7 @@ case OP_MustBeInt: { /* jump, in1 */
}
}
}
+ VdbeBranchTaken(0, 2);
MemSetTypeFlag(pIn1, MEM_Int);
break;
}
@@ -84732,8 +94633,11 @@ case OP_MustBeInt: { /* jump, in1 */
*/
case OP_RealAffinity: { /* in1 */
pIn1 = &aMem[pOp->p1];
- if( pIn1->flags & MEM_Int ){
+ if( pIn1->flags & (MEM_Int|MEM_IntReal) ){
+ testcase( pIn1->flags & MEM_Int );
+ testcase( pIn1->flags & MEM_IntReal );
sqlite3VdbeMemRealify(pIn1);
+ REGISTER_TRACE(pOp->p1, pIn1);
}
break;
}
@@ -84744,7 +94648,7 @@ case OP_RealAffinity: { /* in1 */
** Synopsis: affinity(r[P1])
**
** Force the value in register P1 to be the type defined by P2.
-**
+**
** <ul>
** <li> P2=='A' &rarr; BLOB
** <li> P2=='B' &rarr; TEXT
@@ -84765,9 +94669,11 @@ case OP_Cast: { /* in1 */
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
rc = ExpandBlob(pIn1);
- sqlite3VdbeMemCast(pIn1, pOp->p2, encoding);
- UPDATE_MAX_BLOBSIZE(pIn1);
if( rc ) goto abort_due_to_error;
+ rc = sqlite3VdbeMemCast(pIn1, pOp->p2, encoding);
+ if( rc ) goto abort_due_to_error;
+ UPDATE_MAX_BLOBSIZE(pIn1);
+ REGISTER_TRACE(pOp->p1, pIn1);
break;
}
#endif /* SQLITE_OMIT_CAST */
@@ -84776,18 +94682,17 @@ case OP_Cast: { /* in1 */
** Synopsis: IF r[P3]==r[P1]
**
** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then
-** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then
-** store the result of comparison in register P2.
+** jump to address P2.
**
** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
-** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
+** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
** to coerce both inputs according to this affinity before the
** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric
** affinity is used. Note that the affinity conversions are stored
** back into the input registers P1 and P3. So this opcode can cause
** persistent changes to registers P1 and P3.
**
-** Once any conversions have taken place, and neither value is NULL,
+** Once any conversions have taken place, and neither value is NULL,
** the values are compared. If both values are blobs then memcmp() is
** used to determine the results of the comparison. If both values
** are text, then the appropriate collating function specified in
@@ -84803,9 +94708,8 @@ case OP_Cast: { /* in1 */
** If neither operand is NULL the result is the same as it would be if
** the SQLITE_NULLEQ flag were omitted from P5.
**
-** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
-** content of r[P2] is only changed if the new value is NULL or 0 (false).
-** In other words, a prior r[P2] value will not be overwritten by 1 (true).
+** This opcode saves the result of comparison for use by the new
+** OP_Jump opcode.
*/
/* Opcode: Ne P1 P2 P3 P4 P5
** Synopsis: IF r[P3]!=r[P1]
@@ -84813,31 +94717,26 @@ case OP_Cast: { /* in1 */
** This works just like the Eq opcode except that the jump is taken if
** the operands in registers P1 and P3 are not equal. See the Eq opcode for
** additional information.
-**
-** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
-** content of r[P2] is only changed if the new value is NULL or 1 (true).
-** In other words, a prior r[P2] value will not be overwritten by 0 (false).
*/
/* Opcode: Lt P1 P2 P3 P4 P5
** Synopsis: IF r[P3]<r[P1]
**
** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then
-** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5 store
-** the result of comparison (0 or 1 or NULL) into register P2.
+** jump to address P2.
**
** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or
-** reg(P3) is NULL then the take the jump. If the SQLITE_JUMPIFNULL
+** reg(P3) is NULL then the take the jump. If the SQLITE_JUMPIFNULL
** bit is clear then fall through if either operand is NULL.
**
** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
-** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
+** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
** to coerce both inputs according to this affinity before the
** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric
** affinity is used. Note that the affinity conversions are stored
** back into the input registers P1 and P3. So this opcode can cause
** persistent changes to registers P1 and P3.
**
-** Once any conversions have taken place, and neither value is NULL,
+** Once any conversions have taken place, and neither value is NULL,
** the values are compared. If both values are blobs then memcmp() is
** used to determine the results of the comparison. If both values
** are text, then the appropriate collating function specified in
@@ -84846,6 +94745,9 @@ case OP_Cast: { /* in1 */
** numeric, then a numeric comparison is used. If the two values
** are of different types, then numbers are considered less than
** strings and strings are considered less than blobs.
+**
+** This opcode saves the result of comparison for use by the new
+** OP_Jump opcode.
*/
/* Opcode: Le P1 P2 P3 P4 P5
** Synopsis: IF r[P3]<=r[P1]
@@ -84883,6 +94785,33 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
pIn3 = &aMem[pOp->p3];
flags1 = pIn1->flags;
flags3 = pIn3->flags;
+ if( (flags1 & flags3 & MEM_Int)!=0 ){
+ /* Common case of comparison of two integers */
+ if( pIn3->u.i > pIn1->u.i ){
+ if( sqlite3aGTb[pOp->opcode] ){
+ VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3);
+ goto jump_to_p2;
+ }
+ iCompare = +1;
+ VVA_ONLY( iCompareIsInit = 1; )
+ }else if( pIn3->u.i < pIn1->u.i ){
+ if( sqlite3aLTb[pOp->opcode] ){
+ VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3);
+ goto jump_to_p2;
+ }
+ iCompare = -1;
+ VVA_ONLY( iCompareIsInit = 1; )
+ }else{
+ if( sqlite3aEQb[pOp->opcode] ){
+ VdbeBranchTaken(1, (pOp->p5 & SQLITE_NULLEQ)?2:3);
+ goto jump_to_p2;
+ }
+ iCompare = 0;
+ VVA_ONLY( iCompareIsInit = 1; )
+ }
+ VdbeBranchTaken(0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
+ break;
+ }
if( (flags1 | flags3)&MEM_Null ){
/* One or both operands are NULL */
if( pOp->p5 & SQLITE_NULLEQ ){
@@ -84890,74 +94819,58 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
** OP_Eq or OP_Ne) then take the jump or not depending on whether
** or not both operands are null.
*/
- assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne );
assert( (flags1 & MEM_Cleared)==0 );
- assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 );
+ assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 || CORRUPT_DB );
+ testcase( (pOp->p5 & SQLITE_JUMPIFNULL)!=0 );
if( (flags1&flags3&MEM_Null)!=0
&& (flags3&MEM_Cleared)==0
){
res = 0; /* Operands are equal */
}else{
- res = 1; /* Operands are not equal */
+ res = ((flags3 & MEM_Null) ? -1 : +1); /* Operands are not equal */
}
}else{
/* SQLITE_NULLEQ is clear and at least one operand is NULL,
** then the result is always NULL.
** The jump is taken if the SQLITE_JUMPIFNULL bit is set.
*/
- if( pOp->p5 & SQLITE_STOREP2 ){
- pOut = &aMem[pOp->p2];
- iCompare = 1; /* Operands are not equal */
- memAboutToChange(p, pOut);
- MemSetTypeFlag(pOut, MEM_Null);
- REGISTER_TRACE(pOp->p2, pOut);
- }else{
- VdbeBranchTaken(2,3);
- if( pOp->p5 & SQLITE_JUMPIFNULL ){
- goto jump_to_p2;
- }
+ VdbeBranchTaken(2,3);
+ if( pOp->p5 & SQLITE_JUMPIFNULL ){
+ goto jump_to_p2;
}
+ iCompare = 1; /* Operands are not equal */
+ VVA_ONLY( iCompareIsInit = 1; )
break;
}
}else{
- /* Neither operand is NULL. Do a comparison. */
+ /* Neither operand is NULL and we couldn't do the special high-speed
+ ** integer comparison case. So do a general-case comparison. */
affinity = pOp->p5 & SQLITE_AFF_MASK;
if( affinity>=SQLITE_AFF_NUMERIC ){
if( (flags1 | flags3)&MEM_Str ){
- if( (flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
+ if( (flags1 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn1,0);
- assert( flags3==pIn3->flags );
- /* testcase( flags3!=pIn3->flags );
- ** this used to be possible with pIn1==pIn3, but not since
- ** the column cache was removed. The following assignment
- ** is essentially a no-op. But, it provides defense-in-depth
- ** in case our analysis is incorrect, so it is left in. */
+ assert( flags3==pIn3->flags || CORRUPT_DB );
flags3 = pIn3->flags;
}
- if( (flags3 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
+ if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn3,0);
}
}
- /* Handle the common case of integer comparison here, as an
- ** optimization, to avoid a call to sqlite3MemCompare() */
- if( (pIn1->flags & pIn3->flags & MEM_Int)!=0 ){
- if( pIn3->u.i > pIn1->u.i ){ res = +1; goto compare_op; }
- if( pIn3->u.i < pIn1->u.i ){ res = -1; goto compare_op; }
- res = 0;
- goto compare_op;
- }
- }else if( affinity==SQLITE_AFF_TEXT ){
- if( (flags1 & MEM_Str)==0 && (flags1 & (MEM_Int|MEM_Real))!=0 ){
+ }else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){
+ if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn1->flags & MEM_Int );
testcase( pIn1->flags & MEM_Real );
+ testcase( pIn1->flags & MEM_IntReal );
sqlite3VdbeMemStringify(pIn1, encoding, 1);
testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) );
flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
- assert( pIn1!=pIn3 );
+ if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str;
}
- if( (flags3 & MEM_Str)==0 && (flags3 & (MEM_Int|MEM_Real))!=0 ){
+ if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn3->flags & MEM_Int );
testcase( pIn3->flags & MEM_Real );
+ testcase( pIn3->flags & MEM_IntReal );
sqlite3VdbeMemStringify(pIn3, encoding, 1);
testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn) );
flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask);
@@ -84966,7 +94879,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 );
res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
}
-compare_op:
+
/* At this point, res is negative, zero, or positive if reg[P1] is
** less than, equal to, or greater than reg[P3], respectively. Compute
** the answer to this operator in res2, depending on what the comparison
@@ -84975,69 +94888,56 @@ compare_op:
** order: NE, EQ, GT, LE, LT, GE */
assert( OP_Eq==OP_Ne+1 ); assert( OP_Gt==OP_Ne+2 ); assert( OP_Le==OP_Ne+3 );
assert( OP_Lt==OP_Ne+4 ); assert( OP_Ge==OP_Ne+5 );
- if( res<0 ){ /* ne, eq, gt, le, lt, ge */
- static const unsigned char aLTb[] = { 1, 0, 0, 1, 1, 0 };
- res2 = aLTb[pOp->opcode - OP_Ne];
+ if( res<0 ){
+ res2 = sqlite3aLTb[pOp->opcode];
}else if( res==0 ){
- static const unsigned char aEQb[] = { 0, 1, 0, 1, 0, 1 };
- res2 = aEQb[pOp->opcode - OP_Ne];
+ res2 = sqlite3aEQb[pOp->opcode];
}else{
- static const unsigned char aGTb[] = { 1, 0, 1, 0, 0, 1 };
- res2 = aGTb[pOp->opcode - OP_Ne];
+ res2 = sqlite3aGTb[pOp->opcode];
}
+ iCompare = res;
+ VVA_ONLY( iCompareIsInit = 1; )
/* Undo any changes made by applyAffinity() to the input registers. */
- assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) );
- pIn1->flags = flags1;
assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) );
pIn3->flags = flags3;
+ assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) );
+ pIn1->flags = flags1;
- if( pOp->p5 & SQLITE_STOREP2 ){
- pOut = &aMem[pOp->p2];
- iCompare = res;
- if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){
- /* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1
- ** and prevents OP_Ne from overwriting NULL with 0. This flag
- ** is only used in contexts where either:
- ** (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0)
- ** (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1)
- ** Therefore it is not necessary to check the content of r[P2] for
- ** NULL. */
- assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq );
- assert( res2==0 || res2==1 );
- testcase( res2==0 && pOp->opcode==OP_Eq );
- testcase( res2==1 && pOp->opcode==OP_Eq );
- testcase( res2==0 && pOp->opcode==OP_Ne );
- testcase( res2==1 && pOp->opcode==OP_Ne );
- if( (pOp->opcode==OP_Eq)==res2 ) break;
- }
- memAboutToChange(p, pOut);
- MemSetTypeFlag(pOut, MEM_Int);
- pOut->u.i = res2;
- REGISTER_TRACE(pOp->p2, pOut);
- }else{
- VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
- if( res2 ){
- goto jump_to_p2;
- }
+ VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
+ if( res2 ){
+ goto jump_to_p2;
}
break;
}
-/* Opcode: ElseNotEq * P2 * * *
+/* Opcode: ElseEq * P2 * * *
+**
+** This opcode must follow an OP_Lt or OP_Gt comparison operator. There
+** can be zero or more OP_ReleaseReg opcodes intervening, but no other
+** opcodes are allowed to occur between this instruction and the previous
+** OP_Lt or OP_Gt.
**
-** This opcode must immediately follow an OP_Lt or OP_Gt comparison operator.
-** If result of an OP_Eq comparison on the same two operands
-** would have be NULL or false (0), then then jump to P2.
-** If the result of an OP_Eq comparison on the two previous operands
-** would have been true (1), then fall through.
+** If the result of an OP_Eq comparison on the same two operands as
+** the prior OP_Lt or OP_Gt would have been true, then jump to P2. If
+** the result of an OP_Eq comparison on the two previous operands
+** would have been false or NULL, then fall through.
*/
-case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */
- assert( pOp>aOp );
- assert( pOp[-1].opcode==OP_Lt || pOp[-1].opcode==OP_Gt );
- assert( pOp[-1].p5 & SQLITE_STOREP2 );
- VdbeBranchTaken(iCompare!=0, 2);
- if( iCompare!=0 ) goto jump_to_p2;
+case OP_ElseEq: { /* same as TK_ESCAPE, jump */
+
+#ifdef SQLITE_DEBUG
+ /* Verify the preconditions of this opcode - that it follows an OP_Lt or
+ ** OP_Gt with zero or more intervening OP_ReleaseReg opcodes */
+ int iAddr;
+ for(iAddr = (int)(pOp - aOp) - 1; ALWAYS(iAddr>=0); iAddr--){
+ if( aOp[iAddr].opcode==OP_ReleaseReg ) continue;
+ assert( aOp[iAddr].opcode==OP_Lt || aOp[iAddr].opcode==OP_Gt );
+ break;
+ }
+#endif /* SQLITE_DEBUG */
+ assert( iCompareIsInit );
+ VdbeBranchTaken(iCompare==0, 2);
+ if( iCompare==0 ) goto jump_to_p2;
break;
}
@@ -85047,9 +94947,8 @@ case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */
** Set the permutation used by the OP_Compare operator in the next
** instruction. The permutation is stored in the P4 operand.
**
-** The permutation is only valid until the next OP_Compare that has
-** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should
-** occur immediately prior to the OP_Compare.
+** The permutation is only valid for the next opcode which must be
+** an OP_Compare that has the OPFLAG_PERMUTE bit set in P5.
**
** The first integer in the P4 integer array is the length of the array
** and does not become part of the permutation.
@@ -85081,6 +94980,8 @@ case OP_Permutation: {
** The comparison is a sort comparison, so NULLs compare equal,
** NULLs are less than numbers, numbers are less than strings,
** and strings are less than blobs.
+**
+** This opcode must be immediately followed by an OP_Jump opcode.
*/
case OP_Compare: {
int n;
@@ -85088,10 +94989,10 @@ case OP_Compare: {
int p1;
int p2;
const KeyInfo *pKeyInfo;
- int idx;
+ u32 idx;
CollSeq *pColl; /* Collating sequence to use on this term */
int bRev; /* True for DESCENDING sort order */
- int *aPermute; /* The permutation */
+ u32 *aPermute; /* The permutation */
if( (pOp->p5 & OPFLAG_PERMUTE)==0 ){
aPermute = 0;
@@ -85111,7 +95012,7 @@ case OP_Compare: {
#ifdef SQLITE_DEBUG
if( aPermute ){
int k, mx = 0;
- for(k=0; k<n; k++) if( aPermute[k]>mx ) mx = aPermute[k];
+ for(k=0; k<n; k++) if( aPermute[k]>(u32)mx ) mx = aPermute[k];
assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 );
assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 );
}else{
@@ -85120,30 +95021,41 @@ case OP_Compare: {
}
#endif /* SQLITE_DEBUG */
for(i=0; i<n; i++){
- idx = aPermute ? aPermute[i] : i;
+ idx = aPermute ? aPermute[i] : (u32)i;
assert( memIsValid(&aMem[p1+idx]) );
assert( memIsValid(&aMem[p2+idx]) );
REGISTER_TRACE(p1+idx, &aMem[p1+idx]);
REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
assert( i<pKeyInfo->nKeyField );
pColl = pKeyInfo->aColl[i];
- bRev = pKeyInfo->aSortOrder[i];
+ bRev = (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC);
iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl);
+ VVA_ONLY( iCompareIsInit = 1; )
if( iCompare ){
+ if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL)
+ && ((aMem[p1+idx].flags & MEM_Null) || (aMem[p2+idx].flags & MEM_Null))
+ ){
+ iCompare = -iCompare;
+ }
if( bRev ) iCompare = -iCompare;
break;
}
}
+ assert( pOp[1].opcode==OP_Jump );
break;
}
/* Opcode: Jump P1 P2 P3 * *
**
** Jump to the instruction at address P1, P2, or P3 depending on whether
-** in the most recent OP_Compare instruction the P1 vector was less than
+** in the most recent OP_Compare instruction the P1 vector was less than,
** equal to, or greater than the P2 vector, respectively.
+**
+** This opcode must immediately follow an OP_Compare opcode.
*/
case OP_Jump: { /* jump */
+ assert( pOp>aOp && pOp[-1].opcode==OP_Compare );
+ assert( iCompareIsInit );
if( iCompare<0 ){
VdbeBranchTaken(0,4); pOp = &aOp[pOp->p1 - 1];
}else if( iCompare==0 ){
@@ -85205,13 +95117,13 @@ case OP_Or: { /* same as TK_OR, in1, in2, out3 */
** IS NOT FALSE operators.
**
** Interpret the value in register P1 as a boolean value. Store that
-** boolean (a 0 or 1) in register P2. Or if the value in register P1 is
+** boolean (a 0 or 1) in register P2. Or if the value in register P1 is
** NULL, then the P3 is stored in register P2. Invert the answer if P4
** is 1.
**
** The logic is summarized like this:
**
-** <ul>
+** <ul>
** <li> If P3==0 and P4==0 then r[P2] := r[P1] IS TRUE
** <li> If P3==1 and P4==1 then r[P2] := r[P1] IS FALSE
** <li> If P3==0 and P4==1 then r[P2] := r[P1] IS NOT TRUE
@@ -85231,7 +95143,7 @@ case OP_IsTrue: { /* in1, out2 */
** Synopsis: r[P2]= !r[P1]
**
** Interpret the value in register P1 as a boolean value. Store the
-** boolean complement in register P2. If the value in register P1 is
+** boolean complement in register P2. If the value in register P1 is
** NULL, then a NULL is stored in P2.
*/
case OP_Not: { /* same as TK_NOT, in1, out2 */
@@ -85343,10 +95255,121 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */
break;
}
+/* Opcode: IsType P1 P2 P3 P4 P5
+** Synopsis: if typeof(P1.P3) in P5 goto P2
+**
+** Jump to P2 if the type of a column in a btree is one of the types specified
+** by the P5 bitmask.
+**
+** P1 is normally a cursor on a btree for which the row decode cache is
+** valid through at least column P3. In other words, there should have been
+** a prior OP_Column for column P3 or greater. If the cursor is not valid,
+** then this opcode might give spurious results.
+** The the btree row has fewer than P3 columns, then use P4 as the
+** datatype.
+**
+** If P1 is -1, then P3 is a register number and the datatype is taken
+** from the value in that register.
+**
+** P5 is a bitmask of data types. SQLITE_INTEGER is the least significant
+** (0x01) bit. SQLITE_FLOAT is the 0x02 bit. SQLITE_TEXT is 0x04.
+** SQLITE_BLOB is 0x08. SQLITE_NULL is 0x10.
+**
+** WARNING: This opcode does not reliably distinguish between NULL and REAL
+** when P1>=0. If the database contains a NaN value, this opcode will think
+** that the datatype is REAL when it should be NULL. When P1<0 and the value
+** is already stored in register P3, then this opcode does reliably
+** distinguish between NULL and REAL. The problem only arises then P1>=0.
+**
+** Take the jump to address P2 if and only if the datatype of the
+** value determined by P1 and P3 corresponds to one of the bits in the
+** P5 bitmask.
+**
+*/
+case OP_IsType: { /* jump */
+ VdbeCursor *pC;
+ u16 typeMask;
+ u32 serialType;
+
+ assert( pOp->p1>=(-1) && pOp->p1<p->nCursor );
+ assert( pOp->p1>=0 || (pOp->p3>=0 && pOp->p3<=(p->nMem+1 - p->nCursor)) );
+ if( pOp->p1>=0 ){
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pOp->p3>=0 );
+ if( pOp->p3<pC->nHdrParsed ){
+ serialType = pC->aType[pOp->p3];
+ if( serialType>=12 ){
+ if( serialType&1 ){
+ typeMask = 0x04; /* SQLITE_TEXT */
+ }else{
+ typeMask = 0x08; /* SQLITE_BLOB */
+ }
+ }else{
+ static const unsigned char aMask[] = {
+ 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x2,
+ 0x01, 0x01, 0x10, 0x10
+ };
+ testcase( serialType==0 );
+ testcase( serialType==1 );
+ testcase( serialType==2 );
+ testcase( serialType==3 );
+ testcase( serialType==4 );
+ testcase( serialType==5 );
+ testcase( serialType==6 );
+ testcase( serialType==7 );
+ testcase( serialType==8 );
+ testcase( serialType==9 );
+ testcase( serialType==10 );
+ testcase( serialType==11 );
+ typeMask = aMask[serialType];
+ }
+ }else{
+ typeMask = 1 << (pOp->p4.i - 1);
+ testcase( typeMask==0x01 );
+ testcase( typeMask==0x02 );
+ testcase( typeMask==0x04 );
+ testcase( typeMask==0x08 );
+ testcase( typeMask==0x10 );
+ }
+ }else{
+ assert( memIsValid(&aMem[pOp->p3]) );
+ typeMask = 1 << (sqlite3_value_type((sqlite3_value*)&aMem[pOp->p3])-1);
+ testcase( typeMask==0x01 );
+ testcase( typeMask==0x02 );
+ testcase( typeMask==0x04 );
+ testcase( typeMask==0x08 );
+ testcase( typeMask==0x10 );
+ }
+ VdbeBranchTaken( (typeMask & pOp->p5)!=0, 2);
+ if( typeMask & pOp->p5 ){
+ goto jump_to_p2;
+ }
+ break;
+}
+
+/* Opcode: ZeroOrNull P1 P2 P3 * *
+** Synopsis: r[P2] = 0 OR NULL
+**
+** If both registers P1 and P3 are NOT NULL, then store a zero in
+** register P2. If either registers P1 or P3 are NULL then put
+** a NULL in register P2.
+*/
+case OP_ZeroOrNull: { /* in1, in2, out2, in3 */
+ if( (aMem[pOp->p1].flags & MEM_Null)!=0
+ || (aMem[pOp->p3].flags & MEM_Null)!=0
+ ){
+ sqlite3VdbeMemSetNull(aMem + pOp->p2);
+ }else{
+ sqlite3VdbeMemSetInt64(aMem + pOp->p2, 0);
+ }
+ break;
+}
+
/* Opcode: NotNull P1 P2 * * *
** Synopsis: if r[P1]!=NULL goto P2
**
-** Jump to P2 if the value in register P1 is not NULL.
+** Jump to P2 if the value in register P1 is not NULL.
*/
case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
pIn1 = &aMem[pOp->p1];
@@ -85364,11 +95387,14 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
** If it is, then set register P3 to NULL and jump immediately to P2.
** If P1 is not on a NULL row, then fall through without making any
** changes.
+**
+** If P1 is not an open cursor, then this opcode is a no-op.
*/
case OP_IfNullRow: { /* jump */
+ VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- assert( p->apCsr[pOp->p1]!=0 );
- if( p->apCsr[pOp->p1]->nullRow ){
+ pC = p->apCsr[pOp->p1];
+ if( pC && pC->nullRow ){
sqlite3VdbeMemSetNull(aMem + pOp->p3);
goto jump_to_p2;
}
@@ -85396,22 +95422,30 @@ case OP_Offset: { /* out3 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
pOut = &p->aMem[pOp->p3];
- if( NEVER(pC==0) || pC->eCurType!=CURTYPE_BTREE ){
+ if( pC==0 || pC->eCurType!=CURTYPE_BTREE ){
sqlite3VdbeMemSetNull(pOut);
}else{
- sqlite3VdbeMemSetInt64(pOut, sqlite3BtreeOffset(pC->uc.pCursor));
+ if( pC->deferredMoveto ){
+ rc = sqlite3VdbeFinishMoveto(pC);
+ if( rc ) goto abort_due_to_error;
+ }
+ if( sqlite3BtreeEof(pC->uc.pCursor) ){
+ sqlite3VdbeMemSetNull(pOut);
+ }else{
+ sqlite3VdbeMemSetInt64(pOut, sqlite3BtreeOffset(pC->uc.pCursor));
+ }
}
break;
}
#endif /* SQLITE_ENABLE_OFFSET_SQL_FUNC */
/* Opcode: Column P1 P2 P3 P4 P5
-** Synopsis: r[P3]=PX
+** Synopsis: r[P3]=PX cursor P1 column P2
**
** Interpret the data that cursor P1 points to as a structure built using
** the MakeRecord instruction. (See the MakeRecord opcode for additional
** information about the format of the data.) Extract the P2-th column
-** from this record. If there are less that (P2+1)
+** from this record. If there are less than (P2+1)
** values in the record, extract a NULL.
**
** The value extracted is stored in register P3.
@@ -85420,20 +95454,17 @@ case OP_Offset: { /* out3 */
** if the P4 argument is a P4_MEM use the value of the P4 argument as
** the result.
**
-** If the OPFLAG_CLEARCACHE bit is set on P5 and P1 is a pseudo-table cursor,
-** then the cache of the cursor is reset prior to extracting the column.
-** The first OP_Column against a pseudo-table after the value of the content
-** register has changed should have this bit set.
-**
-** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then
-** the result is guaranteed to only be used as the argument of a length()
-** or typeof() function, respectively. The loading of large blobs can be
-** skipped for length() and all content loading can be skipped for typeof().
+** If the OPFLAG_LENGTHARG bit is set in P5 then the result is guaranteed
+** to only be used by the length() function or the equivalent. The content
+** of large blobs is not loaded, thus saving CPU cycles. If the
+** OPFLAG_TYPEOFARG bit is set then the result will only be used by the
+** typeof() function or the IS NULL or IS NOT NULL operators or the
+** equivalent. In this case, all content loading can be omitted.
*/
-case OP_Column: {
- int p2; /* column number to retrieve */
+case OP_Column: { /* ncycle */
+ u32 p2; /* column number to retrieve */
VdbeCursor *pC; /* The VDBE cursor */
- BtCursor *pCrsr; /* The BTree cursor */
+ BtCursor *pCrsr; /* The B-Tree cursor corresponding to pC */
u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */
int len; /* The length of the serialized data for the column */
int i; /* Loop counter */
@@ -85446,43 +95477,54 @@ case OP_Column: {
u32 t; /* A type code from the record header */
Mem *pReg; /* PseudoTable input register */
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
pC = p->apCsr[pOp->p1];
- p2 = pOp->p2;
+ p2 = (u32)pOp->p2;
- /* If the cursor cache is stale (meaning it is not currently point at
- ** the correct row) then bring it up-to-date by doing the necessary
- ** B-Tree seek. */
- rc = sqlite3VdbeCursorMoveto(&pC, &p2);
- if( rc ) goto abort_due_to_error;
-
- assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
- pDest = &aMem[pOp->p3];
- memAboutToChange(p, pDest);
- assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+op_column_restart:
assert( pC!=0 );
- assert( p2<pC->nField );
+ assert( p2<(u32)pC->nField
+ || (pC->eCurType==CURTYPE_PSEUDO && pC->seekResult==0) );
aOffset = pC->aOffset;
+ assert( aOffset==pC->aType+pC->nField );
assert( pC->eCurType!=CURTYPE_VTAB );
assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow );
assert( pC->eCurType!=CURTYPE_SORTER );
if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/
if( pC->nullRow ){
- if( pC->eCurType==CURTYPE_PSEUDO ){
+ if( pC->eCurType==CURTYPE_PSEUDO && pC->seekResult>0 ){
/* For the special case of as pseudo-cursor, the seekResult field
** identifies the register that holds the record */
- assert( pC->seekResult>0 );
pReg = &aMem[pC->seekResult];
assert( pReg->flags & MEM_Blob );
assert( memIsValid(pReg) );
pC->payloadSize = pC->szRow = pReg->n;
pC->aRow = (u8*)pReg->z;
}else{
+ pDest = &aMem[pOp->p3];
+ memAboutToChange(p, pDest);
sqlite3VdbeMemSetNull(pDest);
goto op_column_out;
}
}else{
pCrsr = pC->uc.pCursor;
+ if( pC->deferredMoveto ){
+ u32 iMap;
+ assert( !pC->isEphemeral );
+ if( pC->ub.aAltMap && (iMap = pC->ub.aAltMap[1+p2])>0 ){
+ pC = pC->pAltCursor;
+ p2 = iMap - 1;
+ goto op_column_restart;
+ }
+ rc = sqlite3VdbeFinishMoveto(pC);
+ if( rc ) goto abort_due_to_error;
+ }else if( sqlite3BtreeCursorHasMoved(pCrsr) ){
+ rc = sqlite3VdbeHandleMovedCursor(pC);
+ if( rc ) goto abort_due_to_error;
+ goto op_column_restart;
+ }
assert( pC->eCurType==CURTYPE_BTREE );
assert( pCrsr );
assert( sqlite3BtreeCursorIsValid(pCrsr) );
@@ -85490,15 +95532,15 @@ case OP_Column: {
pC->aRow = sqlite3BtreePayloadFetch(pCrsr, &pC->szRow);
assert( pC->szRow<=pC->payloadSize );
assert( pC->szRow<=65536 ); /* Maximum page size is 64KiB */
- if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
- goto too_big;
- }
}
pC->cacheStatus = p->cacheCtr;
- pC->iHdrOffset = getVarint32(pC->aRow, aOffset[0]);
+ if( (aOffset[0] = pC->aRow[0])<0x80 ){
+ pC->iHdrOffset = 1;
+ }else{
+ pC->iHdrOffset = sqlite3GetVarint32(pC->aRow, aOffset);
+ }
pC->nHdrParsed = 0;
-
if( pC->szRow<aOffset[0] ){ /*OPTIMIZATION-IF-FALSE*/
/* pC->aRow does not have to hold the entire row, but it does at least
** need to cover the header of the record. If pC->aRow does not contain
@@ -85538,6 +95580,10 @@ case OP_Column: {
testcase( aOffset[0]==0 );
goto op_column_read_header;
}
+ }else if( sqlite3BtreeCursorHasMoved(pC->uc.pCursor) ){
+ rc = sqlite3VdbeHandleMovedCursor(pC);
+ if( rc ) goto abort_due_to_error;
+ goto op_column_restart;
}
/* Make sure at least the first p2+1 entries of the header have been
@@ -85545,19 +95591,19 @@ case OP_Column: {
*/
if( pC->nHdrParsed<=p2 ){
/* If there is more header available for parsing in the record, try
- ** to extract additional fields up through the p2+1-th field
+ ** to extract additional fields up through the p2+1-th field
*/
if( pC->iHdrOffset<aOffset[0] ){
/* Make sure zData points to enough of the record to cover the header. */
if( pC->aRow==0 ){
memset(&sMem, 0, sizeof(sMem));
- rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, 0, aOffset[0], &sMem);
+ rc = sqlite3VdbeMemFromBtreeZeroOffset(pC->uc.pCursor,aOffset[0],&sMem);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
zData = (u8*)sMem.z;
}else{
zData = pC->aRow;
}
-
+
/* Fill in pC->aType[i] and aOffset[i] values through the p2-th field. */
op_column_read_header:
i = pC->nHdrParsed;
@@ -85566,16 +95612,16 @@ case OP_Column: {
zEndHdr = zData + aOffset[0];
testcase( zHdr>=zEndHdr );
do{
- if( (t = zHdr[0])<0x80 ){
+ if( (pC->aType[i] = t = zHdr[0])<0x80 ){
zHdr++;
offset64 += sqlite3VdbeOneByteSerialTypeLen(t);
}else{
zHdr += sqlite3GetVarint32(zHdr, &t);
+ pC->aType[i] = t;
offset64 += sqlite3VdbeSerialTypeLen(t);
}
- pC->aType[i++] = t;
- aOffset[i] = (u32)(offset64 & 0xffffffff);
- }while( i<=p2 && zHdr<zEndHdr );
+ aOffset[++i] = (u32)(offset64 & 0xffffffff);
+ }while( (u32)i<=p2 && zHdr<zEndHdr );
/* The record is corrupt if any of the following are true:
** (1) the bytes of the header extend past the declared header size
@@ -85606,6 +95652,8 @@ case OP_Column: {
** columns. So the result will be either the default value or a NULL.
*/
if( pC->nHdrParsed<=p2 ){
+ pDest = &aMem[pOp->p3];
+ memAboutToChange(p, pDest);
if( pOp->p4type==P4_MEM ){
sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static);
}else{
@@ -85623,6 +95671,8 @@ case OP_Column: {
*/
assert( p2<pC->nHdrParsed );
assert( rc==SQLITE_OK );
+ pDest = &aMem[pOp->p3];
+ memAboutToChange(p, pDest);
assert( sqlite3VdbeCheckMemInvariants(pDest) );
if( VdbeMemDynamic(pDest) ){
sqlite3VdbeMemSetNull(pDest);
@@ -85643,6 +95693,7 @@ case OP_Column: {
pDest->n = len = (t-12)/2;
pDest->enc = encoding;
if( pDest->szMalloc < len+2 ){
+ if( len>db->aLimit[SQLITE_LIMIT_LENGTH] ) goto too_big;
pDest->flags = MEM_Null;
if( sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem;
}else{
@@ -85654,30 +95705,39 @@ case OP_Column: {
pDest->flags = aFlag[t&1];
}
}else{
+ u8 p5;
pDest->enc = encoding;
+ assert( pDest->db==db );
/* This branch happens only when content is on overflow pages */
- if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
- && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0))
- || (len = sqlite3VdbeSerialTypeLen(t))==0
+ if( ((p5 = (pOp->p5 & OPFLAG_BYTELENARG))!=0
+ && (p5==OPFLAG_TYPEOFARG
+ || (t>=12 && ((t&1)==0 || p5==OPFLAG_BYTELENARG))
+ )
+ )
+ || sqlite3VdbeSerialTypeLen(t)==0
){
/* Content is irrelevant for
** 1. the typeof() function,
** 2. the length(X) function if X is a blob, and
** 3. if the content length is zero.
** So we might as well use bogus content rather than reading
- ** content from disk.
+ ** content from disk.
**
** Although sqlite3VdbeSerialGet() may read at most 8 bytes from the
** buffer passed to it, debugging function VdbeMemPrettyPrint() may
- ** read up to 16. So 16 bytes of bogus content is supplied.
+ ** read more. Use the global constant sqlite3CtypeMap[] as the array,
+ ** as that array is 256 bytes long (plenty for VdbeMemPrettyPrint())
+ ** and it begins with a bunch of zeros.
*/
- static u8 aZero[16]; /* This is the bogus content */
- sqlite3VdbeSerialGet(aZero, t, pDest);
+ sqlite3VdbeSerialGet((u8*)sqlite3CtypeMap, t, pDest);
}else{
- rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, aOffset[p2], len, pDest);
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
- sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest);
- pDest->flags &= ~MEM_Ephem;
+ rc = vdbeColumnFromOverflow(pC, p2, t, aOffset[p2],
+ p->cacheCtr, colCacheCtr, pDest);
+ if( rc ){
+ if( rc==SQLITE_NOMEM ) goto no_mem;
+ if( rc==SQLITE_TOOBIG ) goto too_big;
+ goto abort_due_to_error;
+ }
}
}
@@ -85696,6 +95756,110 @@ op_column_corrupt:
}
}
+/* Opcode: TypeCheck P1 P2 P3 P4 *
+** Synopsis: typecheck(r[P1@P2])
+**
+** Apply affinities to the range of P2 registers beginning with P1.
+** Take the affinities from the Table object in P4. If any value
+** cannot be coerced into the correct type, then raise an error.
+**
+** This opcode is similar to OP_Affinity except that this opcode
+** forces the register type to the Table column type. This is used
+** to implement "strict affinity".
+**
+** GENERATED ALWAYS AS ... STATIC columns are only checked if P3
+** is zero. When P3 is non-zero, no type checking occurs for
+** static generated columns. Virtual columns are computed at query time
+** and so they are never checked.
+**
+** Preconditions:
+**
+** <ul>
+** <li> P2 should be the number of non-virtual columns in the
+** table of P4.
+** <li> Table P4 should be a STRICT table.
+** </ul>
+**
+** If any precondition is false, an assertion fault occurs.
+*/
+case OP_TypeCheck: {
+ Table *pTab;
+ Column *aCol;
+ int i;
+
+ assert( pOp->p4type==P4_TABLE );
+ pTab = pOp->p4.pTab;
+ assert( pTab->tabFlags & TF_Strict );
+ assert( pTab->nNVCol==pOp->p2 );
+ aCol = pTab->aCol;
+ pIn1 = &aMem[pOp->p1];
+ for(i=0; i<pTab->nCol; i++){
+ if( aCol[i].colFlags & COLFLAG_GENERATED ){
+ if( aCol[i].colFlags & COLFLAG_VIRTUAL ) continue;
+ if( pOp->p3 ){ pIn1++; continue; }
+ }
+ assert( pIn1 < &aMem[pOp->p1+pOp->p2] );
+ applyAffinity(pIn1, aCol[i].affinity, encoding);
+ if( (pIn1->flags & MEM_Null)==0 ){
+ switch( aCol[i].eCType ){
+ case COLTYPE_BLOB: {
+ if( (pIn1->flags & MEM_Blob)==0 ) goto vdbe_type_error;
+ break;
+ }
+ case COLTYPE_INTEGER:
+ case COLTYPE_INT: {
+ if( (pIn1->flags & MEM_Int)==0 ) goto vdbe_type_error;
+ break;
+ }
+ case COLTYPE_TEXT: {
+ if( (pIn1->flags & MEM_Str)==0 ) goto vdbe_type_error;
+ break;
+ }
+ case COLTYPE_REAL: {
+ testcase( (pIn1->flags & (MEM_Real|MEM_IntReal))==MEM_Real );
+ assert( (pIn1->flags & MEM_IntReal)==0 );
+ if( pIn1->flags & MEM_Int ){
+ /* When applying REAL affinity, if the result is still an MEM_Int
+ ** that will fit in 6 bytes, then change the type to MEM_IntReal
+ ** so that we keep the high-resolution integer value but know that
+ ** the type really wants to be REAL. */
+ testcase( pIn1->u.i==140737488355328LL );
+ testcase( pIn1->u.i==140737488355327LL );
+ testcase( pIn1->u.i==-140737488355328LL );
+ testcase( pIn1->u.i==-140737488355329LL );
+ if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL){
+ pIn1->flags |= MEM_IntReal;
+ pIn1->flags &= ~MEM_Int;
+ }else{
+ pIn1->u.r = (double)pIn1->u.i;
+ pIn1->flags |= MEM_Real;
+ pIn1->flags &= ~MEM_Int;
+ }
+ }else if( (pIn1->flags & (MEM_Real|MEM_IntReal))==0 ){
+ goto vdbe_type_error;
+ }
+ break;
+ }
+ default: {
+ /* COLTYPE_ANY. Accept anything. */
+ break;
+ }
+ }
+ }
+ REGISTER_TRACE((int)(pIn1-aMem), pIn1);
+ pIn1++;
+ }
+ assert( pIn1 == &aMem[pOp->p1+pOp->p2] );
+ break;
+
+vdbe_type_error:
+ sqlite3VdbeError(p, "cannot store %s value in %s column %s.%s",
+ vdbeMemTypeName(pIn1), sqlite3StdType[aCol[i].eCType-1],
+ pTab->zName, aCol[i].zCnName);
+ rc = SQLITE_CONSTRAINT_DATATYPE;
+ goto abort_due_to_error;
+}
+
/* Opcode: Affinity P1 P2 * P4 *
** Synopsis: affinity(r[P1@P2])
**
@@ -85713,12 +95877,33 @@ case OP_Affinity: {
assert( pOp->p2>0 );
assert( zAffinity[pOp->p2]==0 );
pIn1 = &aMem[pOp->p1];
- do{
+ while( 1 /*exit-by-break*/ ){
assert( pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)] );
- assert( memIsValid(pIn1) );
- applyAffinity(pIn1, *(zAffinity++), encoding);
+ assert( zAffinity[0]==SQLITE_AFF_NONE || memIsValid(pIn1) );
+ applyAffinity(pIn1, zAffinity[0], encoding);
+ if( zAffinity[0]==SQLITE_AFF_REAL && (pIn1->flags & MEM_Int)!=0 ){
+ /* When applying REAL affinity, if the result is still an MEM_Int
+ ** that will fit in 6 bytes, then change the type to MEM_IntReal
+ ** so that we keep the high-resolution integer value but know that
+ ** the type really wants to be REAL. */
+ testcase( pIn1->u.i==140737488355328LL );
+ testcase( pIn1->u.i==140737488355327LL );
+ testcase( pIn1->u.i==-140737488355328LL );
+ testcase( pIn1->u.i==-140737488355329LL );
+ if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL ){
+ pIn1->flags |= MEM_IntReal;
+ pIn1->flags &= ~MEM_Int;
+ }else{
+ pIn1->u.r = (double)pIn1->u.i;
+ pIn1->flags |= MEM_Real;
+ pIn1->flags &= ~(MEM_Int|MEM_Str);
+ }
+ }
+ REGISTER_TRACE((int)(pIn1-aMem), pIn1);
+ zAffinity++;
+ if( zAffinity[0]==0 ) break;
pIn1++;
- }while( zAffinity[0] );
+ }
break;
}
@@ -85737,9 +95922,19 @@ case OP_Affinity: {
** macros defined in sqliteInt.h.
**
** If P4 is NULL then all index fields have the affinity BLOB.
+**
+** The meaning of P5 depends on whether or not the SQLITE_ENABLE_NULL_TRIM
+** compile-time option is enabled:
+**
+** * If SQLITE_ENABLE_NULL_TRIM is enabled, then the P5 is the index
+** of the right-most table that can be null-trimmed.
+**
+** * If SQLITE_ENABLE_NULL_TRIM is omitted, then P5 has the value
+** OPFLAG_NOCHNG_MAGIC if the OP_MakeRecord opcode is allowed to
+** accept no-change records with serial_type 10. This value is
+** only used inside an assert() and does not affect the end result.
*/
case OP_MakeRecord: {
- u8 *zNewRecord; /* A buffer to hold the data for the new record */
Mem *pRec; /* The new record */
u64 nData; /* Number of bytes of data space */
int nHdr; /* Number of bytes of header space */
@@ -85751,22 +95946,21 @@ case OP_MakeRecord: {
Mem *pLast; /* Last field of the record */
int nField; /* Number of fields in the record */
char *zAffinity; /* The affinity string for the record */
- int file_format; /* File format to use for encoding */
- int i; /* Space used in zNewRecord[] header */
- int j; /* Space used in zNewRecord[] content */
u32 len; /* Length of a field */
+ u8 *zHdr; /* Where to write next byte of the header */
+ u8 *zPayload; /* Where to write next byte of the payload */
/* Assuming the record contains N fields, the record format looks
** like this:
**
** ------------------------------------------------------------------------
- ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 |
+ ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 |
** ------------------------------------------------------------------------
**
** Data(0) is taken from register P1. Data(1) comes from register P1+1
** and so forth.
**
- ** Each type field is a varint representing the serial type of the
+ ** Each type field is a varint representing the serial type of the
** corresponding data element (see sqlite3VdbeSerialType()). The
** hdr-size field is also a varint which is the offset from the beginning
** of the record to data0.
@@ -85780,7 +95974,6 @@ case OP_MakeRecord: {
pData0 = &aMem[nField];
nField = pOp->p2;
pLast = &pData0[nField-1];
- file_format = p->minWriteFileFormat;
/* Identify the output register */
assert( pOp->p3<pOp->p1 || pOp->p3>=pOp->p1+pOp->p2 );
@@ -85793,7 +95986,14 @@ case OP_MakeRecord: {
if( zAffinity ){
pRec = pData0;
do{
- applyAffinity(pRec++, *(zAffinity++), encoding);
+ applyAffinity(pRec, zAffinity[0], encoding);
+ if( zAffinity[0]==SQLITE_AFF_REAL && (pRec->flags & MEM_Int) ){
+ pRec->flags |= MEM_IntReal;
+ pRec->flags &= ~(MEM_Int);
+ }
+ REGISTER_TRACE((int)(pRec-aMem), pRec);
+ zAffinity++;
+ pRec++;
assert( zAffinity[0]==0 || pRec<=pLast );
}while( zAffinity[0] );
}
@@ -85813,34 +96013,122 @@ case OP_MakeRecord: {
#endif
/* Loop through the elements that will make up the record to figure
- ** out how much space is required for the new record.
+ ** out how much space is required for the new record. After this loop,
+ ** the Mem.uTemp field of each term should hold the serial-type that will
+ ** be used for that term in the generated record:
+ **
+ ** Mem.uTemp value type
+ ** --------------- ---------------
+ ** 0 NULL
+ ** 1 1-byte signed integer
+ ** 2 2-byte signed integer
+ ** 3 3-byte signed integer
+ ** 4 4-byte signed integer
+ ** 5 6-byte signed integer
+ ** 6 8-byte signed integer
+ ** 7 IEEE float
+ ** 8 Integer constant 0
+ ** 9 Integer constant 1
+ ** 10,11 reserved for expansion
+ ** N>=12 and even BLOB
+ ** N>=13 and odd text
+ **
+ ** The following additional values are computed:
+ ** nHdr Number of bytes needed for the record header
+ ** nData Number of bytes of data space needed for the record
+ ** nZero Zero bytes at the end of the record
*/
pRec = pLast;
do{
assert( memIsValid(pRec) );
- serial_type = sqlite3VdbeSerialType(pRec, file_format, &len);
- if( pRec->flags & MEM_Zero ){
- if( serial_type==0 ){
+ if( pRec->flags & MEM_Null ){
+ if( pRec->flags & MEM_Zero ){
/* Values with MEM_Null and MEM_Zero are created by xColumn virtual
** table methods that never invoke sqlite3_result_xxxxx() while
** computing an unchanging column value in an UPDATE statement.
** Give such values a special internal-use-only serial-type of 10
** so that they can be passed through to xUpdate and have
** a true sqlite3_value_nochange(). */
+#ifndef SQLITE_ENABLE_NULL_TRIM
assert( pOp->p5==OPFLAG_NOCHNG_MAGIC || CORRUPT_DB );
- serial_type = 10;
- }else if( nData ){
- if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
+#endif
+ pRec->uTemp = 10;
+ }else{
+ pRec->uTemp = 0;
+ }
+ nHdr++;
+ }else if( pRec->flags & (MEM_Int|MEM_IntReal) ){
+ /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */
+ i64 i = pRec->u.i;
+ u64 uu;
+ testcase( pRec->flags & MEM_Int );
+ testcase( pRec->flags & MEM_IntReal );
+ if( i<0 ){
+ uu = ~i;
}else{
- nZero += pRec->u.nZero;
- len -= pRec->u.nZero;
+ uu = i;
+ }
+ nHdr++;
+ testcase( uu==127 ); testcase( uu==128 );
+ testcase( uu==32767 ); testcase( uu==32768 );
+ testcase( uu==8388607 ); testcase( uu==8388608 );
+ testcase( uu==2147483647 ); testcase( uu==2147483648LL );
+ testcase( uu==140737488355327LL ); testcase( uu==140737488355328LL );
+ if( uu<=127 ){
+ if( (i&1)==i && p->minWriteFileFormat>=4 ){
+ pRec->uTemp = 8+(u32)uu;
+ }else{
+ nData++;
+ pRec->uTemp = 1;
+ }
+ }else if( uu<=32767 ){
+ nData += 2;
+ pRec->uTemp = 2;
+ }else if( uu<=8388607 ){
+ nData += 3;
+ pRec->uTemp = 3;
+ }else if( uu<=2147483647 ){
+ nData += 4;
+ pRec->uTemp = 4;
+ }else if( uu<=140737488355327LL ){
+ nData += 6;
+ pRec->uTemp = 5;
+ }else{
+ nData += 8;
+ if( pRec->flags & MEM_IntReal ){
+ /* If the value is IntReal and is going to take up 8 bytes to store
+ ** as an integer, then we might as well make it an 8-byte floating
+ ** point value */
+ pRec->u.r = (double)pRec->u.i;
+ pRec->flags &= ~MEM_IntReal;
+ pRec->flags |= MEM_Real;
+ pRec->uTemp = 7;
+ }else{
+ pRec->uTemp = 6;
+ }
+ }
+ }else if( pRec->flags & MEM_Real ){
+ nHdr++;
+ nData += 8;
+ pRec->uTemp = 7;
+ }else{
+ assert( db->mallocFailed || pRec->flags&(MEM_Str|MEM_Blob) );
+ assert( pRec->n>=0 );
+ len = (u32)pRec->n;
+ serial_type = (len*2) + 12 + ((pRec->flags & MEM_Str)!=0);
+ if( pRec->flags & MEM_Zero ){
+ serial_type += pRec->u.nZero*2;
+ if( nData ){
+ if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
+ len += pRec->u.nZero;
+ }else{
+ nZero += pRec->u.nZero;
+ }
}
+ nData += len;
+ nHdr += sqlite3VarintLen(serial_type);
+ pRec->uTemp = serial_type;
}
- nData += len;
- testcase( serial_type==127 );
- testcase( serial_type==128 );
- nHdr += serial_type<=127 ? 1 : sqlite3VarintLen(serial_type);
- pRec->uTemp = serial_type;
if( pRec==pData0 ) break;
pRec--;
}while(1);
@@ -85862,7 +96150,7 @@ case OP_MakeRecord: {
}
nByte = nHdr+nData;
- /* Make sure the output register has a buffer large enough to store
+ /* Make sure the output register has a buffer large enough to store
** the new record. The output register (pOp->p3) is not allowed to
** be one of the input registers (because the following call to
** sqlite3VdbeMemClearAndResize() could clobber the value before it is used).
@@ -85881,44 +96169,93 @@ case OP_MakeRecord: {
goto no_mem;
}
}
- zNewRecord = (u8 *)pOut->z;
+ pOut->n = (int)nByte;
+ pOut->flags = MEM_Blob;
+ if( nZero ){
+ pOut->u.nZero = nZero;
+ pOut->flags |= MEM_Zero;
+ }
+ UPDATE_MAX_BLOBSIZE(pOut);
+ zHdr = (u8 *)pOut->z;
+ zPayload = zHdr + nHdr;
/* Write the record */
- i = putVarint32(zNewRecord, nHdr);
- j = nHdr;
+ if( nHdr<0x80 ){
+ *(zHdr++) = nHdr;
+ }else{
+ zHdr += sqlite3PutVarint(zHdr,nHdr);
+ }
assert( pData0<=pLast );
pRec = pData0;
- do{
+ while( 1 /*exit-by-break*/ ){
serial_type = pRec->uTemp;
/* EVIDENCE-OF: R-06529-47362 Following the size varint are one or more
- ** additional varints, one per column. */
- i += putVarint32(&zNewRecord[i], serial_type); /* serial type */
- /* EVIDENCE-OF: R-64536-51728 The values for each column in the record
+ ** additional varints, one per column.
+ ** EVIDENCE-OF: R-64536-51728 The values for each column in the record
** immediately follow the header. */
- j += sqlite3VdbeSerialPut(&zNewRecord[j], pRec, serial_type); /* content */
- }while( (++pRec)<=pLast );
- assert( i==nHdr );
- assert( j==nByte );
+ if( serial_type<=7 ){
+ *(zHdr++) = serial_type;
+ if( serial_type==0 ){
+ /* NULL value. No change in zPayload */
+ }else{
+ u64 v;
+ if( serial_type==7 ){
+ assert( sizeof(v)==sizeof(pRec->u.r) );
+ memcpy(&v, &pRec->u.r, sizeof(v));
+ swapMixedEndianFloat(v);
+ }else{
+ v = pRec->u.i;
+ }
+ len = sqlite3SmallTypeSizes[serial_type];
+ assert( len>=1 && len<=8 && len!=5 && len!=7 );
+ switch( len ){
+ default: zPayload[7] = (u8)(v&0xff); v >>= 8;
+ zPayload[6] = (u8)(v&0xff); v >>= 8;
+ case 6: zPayload[5] = (u8)(v&0xff); v >>= 8;
+ zPayload[4] = (u8)(v&0xff); v >>= 8;
+ case 4: zPayload[3] = (u8)(v&0xff); v >>= 8;
+ case 3: zPayload[2] = (u8)(v&0xff); v >>= 8;
+ case 2: zPayload[1] = (u8)(v&0xff); v >>= 8;
+ case 1: zPayload[0] = (u8)(v&0xff);
+ }
+ zPayload += len;
+ }
+ }else if( serial_type<0x80 ){
+ *(zHdr++) = serial_type;
+ if( serial_type>=14 && pRec->n>0 ){
+ assert( pRec->z!=0 );
+ memcpy(zPayload, pRec->z, pRec->n);
+ zPayload += pRec->n;
+ }
+ }else{
+ zHdr += sqlite3PutVarint(zHdr, serial_type);
+ if( pRec->n ){
+ assert( pRec->z!=0 );
+ memcpy(zPayload, pRec->z, pRec->n);
+ zPayload += pRec->n;
+ }
+ }
+ if( pRec==pLast ) break;
+ pRec++;
+ }
+ assert( nHdr==(int)(zHdr - (u8*)pOut->z) );
+ assert( nByte==(int)(zPayload - (u8*)pOut->z) );
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
- pOut->n = (int)nByte;
- pOut->flags = MEM_Blob;
- if( nZero ){
- pOut->u.nZero = nZero;
- pOut->flags |= MEM_Zero;
- }
REGISTER_TRACE(pOp->p3, pOut);
- UPDATE_MAX_BLOBSIZE(pOut);
break;
}
-/* Opcode: Count P1 P2 * * *
+/* Opcode: Count P1 P2 P3 * *
** Synopsis: r[P2]=count()
**
-** Store the number of entries (an integer value) in the table or index
-** opened by cursor P1 in register P2
+** Store the number of entries (an integer value) in the table or index
+** opened by cursor P1 in register P2.
+**
+** If P3==0, then an exact count is obtained, which involves visiting
+** every btree page of the table. But if P3 is non-zero, an estimate
+** is returned based on the current cursor position.
*/
-#ifndef SQLITE_OMIT_BTREECOUNT
case OP_Count: { /* out2 */
i64 nEntry;
BtCursor *pCrsr;
@@ -85926,20 +96263,24 @@ case OP_Count: { /* out2 */
assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE );
pCrsr = p->apCsr[pOp->p1]->uc.pCursor;
assert( pCrsr );
- nEntry = 0; /* Not needed. Only used to silence a warning. */
- rc = sqlite3BtreeCount(pCrsr, &nEntry);
- if( rc ) goto abort_due_to_error;
+ if( pOp->p3 ){
+ nEntry = sqlite3BtreeRowCountEst(pCrsr);
+ }else{
+ nEntry = 0; /* Not needed. Only used to silence a warning. */
+ rc = sqlite3BtreeCount(db, pCrsr, &nEntry);
+ if( rc ) goto abort_due_to_error;
+ }
pOut = out2Prerelease(p, pOp);
pOut->u.i = nEntry;
- break;
+ goto check_for_interrupt;
}
-#endif
/* Opcode: Savepoint P1 * * P4 *
**
** Open, release or rollback the savepoint named by parameter P4, depending
-** on the value of P1. To open a new savepoint, P1==0. To release (commit) an
-** existing savepoint, P1==1, or to rollback an existing savepoint P1==2.
+** on the value of P1. To open a new savepoint set P1==0 (SAVEPOINT_BEGIN).
+** To release (commit) an existing savepoint set P1==1 (SAVEPOINT_RELEASE).
+** To rollback an existing savepoint set P1==2 (SAVEPOINT_ROLLBACK).
*/
case OP_Savepoint: {
int p1; /* Value of P1 operand */
@@ -85955,7 +96296,7 @@ case OP_Savepoint: {
zName = pOp->p4.z;
/* Assert that the p1 parameter is valid. Also that if there is no open
- ** transaction, then there cannot be any savepoints.
+ ** transaction, then there cannot be any savepoints.
*/
assert( db->pSavepoint==0 || db->autoCommit==0 );
assert( p1==SAVEPOINT_BEGIN||p1==SAVEPOINT_RELEASE||p1==SAVEPOINT_ROLLBACK );
@@ -85965,7 +96306,7 @@ case OP_Savepoint: {
if( p1==SAVEPOINT_BEGIN ){
if( db->nVdbeWrite>0 ){
- /* A new savepoint cannot be created if there are active write
+ /* A new savepoint cannot be created if there are active write
** statements (i.e. open read/write incremental blob handles).
*/
sqlite3VdbeError(p, "cannot open savepoint - SQL statements in progress");
@@ -85989,7 +96330,7 @@ case OP_Savepoint: {
if( pNew ){
pNew->zName = (char *)&pNew[1];
memcpy(pNew->zName, zName, nName+1);
-
+
/* If there is no open transaction, then mark this as a special
** "transaction savepoint". */
if( db->autoCommit ){
@@ -86007,12 +96348,13 @@ case OP_Savepoint: {
}
}
}else{
+ assert( p1==SAVEPOINT_RELEASE || p1==SAVEPOINT_ROLLBACK );
iSavepoint = 0;
/* Find the named savepoint. If there is no such savepoint, then an
** an error is returned to the user. */
for(
- pSavepoint = db->pSavepoint;
+ pSavepoint = db->pSavepoint;
pSavepoint && sqlite3StrICmp(pSavepoint->zName, zName);
pSavepoint = pSavepoint->pNext
){
@@ -86022,7 +96364,7 @@ case OP_Savepoint: {
sqlite3VdbeError(p, "no such savepoint: %s", zName);
rc = SQLITE_ERROR;
}else if( db->nVdbeWrite>0 && p1==SAVEPOINT_RELEASE ){
- /* It is not possible to release (commit) a savepoint if there are
+ /* It is not possible to release (commit) a savepoint if there are
** active write statements.
*/
sqlite3VdbeError(p, "cannot release savepoint - "
@@ -86031,8 +96373,8 @@ case OP_Savepoint: {
}else{
/* Determine whether or not this is a transaction savepoint. If so,
- ** and this is a RELEASE command, then the current transaction
- ** is committed.
+ ** and this is a RELEASE command, then the current transaction
+ ** is committed.
*/
int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
if( isTransaction && p1==SAVEPOINT_RELEASE ){
@@ -86046,8 +96388,12 @@ case OP_Savepoint: {
p->rc = rc = SQLITE_BUSY;
goto vdbe_return;
}
- db->isTransactionSavepoint = 0;
rc = p->rc;
+ if( rc ){
+ db->autoCommit = 0;
+ }else{
+ db->isTransactionSavepoint = 0;
+ }
}else{
int isSchemaChange;
iSavepoint = db->nSavepoint - iSavepoint - 1;
@@ -86060,6 +96406,7 @@ case OP_Savepoint: {
if( rc!=SQLITE_OK ) goto abort_due_to_error;
}
}else{
+ assert( p1==SAVEPOINT_RELEASE );
isSchemaChange = 0;
}
for(ii=0; ii<db->nDb; ii++){
@@ -86074,8 +96421,9 @@ case OP_Savepoint: {
db->mDbFlags |= DBFLAG_SchemaChange;
}
}
-
- /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all
+ if( rc ) goto abort_due_to_error;
+
+ /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all
** savepoints nested inside of the savepoint being operated on. */
while( db->pSavepoint!=pSavepoint ){
pTmp = db->pSavepoint;
@@ -86084,8 +96432,8 @@ case OP_Savepoint: {
db->nSavepoint--;
}
- /* If it is a RELEASE, then destroy the savepoint being operated on
- ** too. If it is a ROLLBACK TO, then set the number of deferred
+ /* If it is a RELEASE, then destroy the savepoint being operated on
+ ** too. If it is a ROLLBACK TO, then set the number of deferred
** constraint violations present in the database to the value stored
** when the savepoint was created. */
if( p1==SAVEPOINT_RELEASE ){
@@ -86096,6 +96444,7 @@ case OP_Savepoint: {
db->nSavepoint--;
}
}else{
+ assert( p1==SAVEPOINT_ROLLBACK );
db->nDeferredCons = pSavepoint->nDeferredCons;
db->nDeferredImmCons = pSavepoint->nDeferredImmCons;
}
@@ -86107,7 +96456,10 @@ case OP_Savepoint: {
}
}
if( rc ) goto abort_due_to_error;
-
+ if( p->eVdbeState==VDBE_HALT_STATE ){
+ rc = SQLITE_DONE;
+ goto vdbe_return;
+ }
break;
}
@@ -86138,7 +96490,7 @@ case OP_AutoCommit: {
db->autoCommit = 1;
}else if( desiredAutoCommit && db->nVdbeWrite>0 ){
/* If this instruction implements a COMMIT and other VMs are writing
- ** return an error indicating that the other VMs must complete first.
+ ** return an error indicating that the other VMs must complete first.
*/
sqlite3VdbeError(p, "cannot commit transaction - "
"SQL statements in progress");
@@ -86155,7 +96507,6 @@ case OP_AutoCommit: {
p->rc = rc = SQLITE_BUSY;
goto vdbe_return;
}
- assert( db->nStatement==0 );
sqlite3CloseSavepoints(db);
if( p->rc==SQLITE_OK ){
rc = SQLITE_DONE;
@@ -86168,20 +96519,21 @@ case OP_AutoCommit: {
(!desiredAutoCommit)?"cannot start a transaction within a transaction":(
(iRollback)?"cannot rollback - no transaction is active":
"cannot commit - no transaction is active"));
-
+
rc = SQLITE_ERROR;
goto abort_due_to_error;
}
- break;
+ /*NOTREACHED*/ assert(0);
}
/* Opcode: Transaction P1 P2 P3 P4 P5
**
** Begin a transaction on database P1 if a transaction is not already
** active.
-** If P2 is non-zero, then a write-transaction is started, or if a
+** If P2 is non-zero, then a write-transaction is started, or if a
** read-transaction is already active, it is upgraded to a write-transaction.
-** If P2 is zero, then a read-transaction is started.
+** If P2 is zero, then a read-transaction is started. If P2 is 2 or more
+** then an exclusive transaction is started.
**
** P1 is the index of the database file on which the transaction is
** started. Index 0 is the main database file and index 1 is the
@@ -86211,17 +96563,28 @@ case OP_AutoCommit: {
*/
case OP_Transaction: {
Btree *pBt;
+ Db *pDb;
int iMeta = 0;
assert( p->bIsReader );
assert( p->readOnly==0 || pOp->p2==0 );
+ assert( pOp->p2>=0 && pOp->p2<=2 );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( DbMaskTest(p->btreeMask, pOp->p1) );
- if( pOp->p2 && (db->flags & SQLITE_QueryOnly)!=0 ){
- rc = SQLITE_READONLY;
+ assert( rc==SQLITE_OK );
+ if( pOp->p2 && (db->flags & (SQLITE_QueryOnly|SQLITE_CorruptRdOnly))!=0 ){
+ if( db->flags & SQLITE_QueryOnly ){
+ /* Writes prohibited by the "PRAGMA query_only=TRUE" statement */
+ rc = SQLITE_READONLY;
+ }else{
+ /* Writes prohibited due to a prior SQLITE_CORRUPT in the current
+ ** transaction */
+ rc = SQLITE_CORRUPT;
+ }
goto abort_due_to_error;
}
- pBt = db->aDb[pOp->p1].pBt;
+ pDb = &db->aDb[pOp->p1];
+ pBt = pDb->pBt;
if( pBt ){
rc = sqlite3BtreeBeginTrans(pBt, pOp->p2, &iMeta);
@@ -86236,13 +96599,14 @@ case OP_Transaction: {
goto abort_due_to_error;
}
- if( pOp->p2 && p->usesStmtJournal
- && (db->autoCommit==0 || db->nVdbeRead>1)
+ if( p->usesStmtJournal
+ && pOp->p2
+ && (db->autoCommit==0 || db->nVdbeRead>1)
){
- assert( sqlite3BtreeIsInTrans(pBt) );
+ assert( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE );
if( p->iStatement==0 ){
assert( db->nStatement>=0 && db->nSavepoint>=0 );
- db->nStatement++;
+ db->nStatement++;
p->iStatement = db->nSavepoint + db->nStatement;
}
@@ -86259,9 +96623,9 @@ case OP_Transaction: {
}
}
assert( pOp->p5==0 || pOp->p4type==P4_INT32 );
- if( pOp->p5
- && (iMeta!=pOp->p3
- || db->aDb[pOp->p1].pSchema->iGeneration!=pOp->p4.i)
+ if( rc==SQLITE_OK
+ && pOp->p5
+ && (iMeta!=pOp->p3 || pDb->pSchema->iGeneration!=pOp->p4.i)
){
/*
** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema
@@ -86270,7 +96634,7 @@ case OP_Transaction: {
*/
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
- /* If the schema-cookie from the database file matches the cookie
+ /* If the schema-cookie from the database file matches the cookie
** stored with the in-memory representation of the schema, do
** not reload the schema from the database file.
**
@@ -86280,7 +96644,7 @@ case OP_Transaction: {
** prepared queries. If such a query is out-of-date, we do not want to
** discard the database schema, as the user code implementing the
** v-table would have to be ready for the sqlite3_vtab structure itself
- ** to be invalidated whenever sqlite3_step() is called from within
+ ** to be invalidated whenever sqlite3_step() is called from within
** a v-table method.
*/
if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
@@ -86288,6 +96652,11 @@ case OP_Transaction: {
}
p->expired = 1;
rc = SQLITE_SCHEMA;
+
+ /* Set changeCntOn to 0 to prevent the value returned by sqlite3_changes()
+ ** from being modified in sqlite3VdbeHalt(). If this statement is
+ ** reprepared, changeCntOn will be set again. */
+ p->changeCntOn = 0;
}
if( rc ) goto abort_due_to_error;
break;
@@ -86324,15 +96693,20 @@ case OP_ReadCookie: { /* out2 */
break;
}
-/* Opcode: SetCookie P1 P2 P3 * *
+/* Opcode: SetCookie P1 P2 P3 * P5
**
** Write the integer value P3 into cookie number P2 of database P1.
** P2==1 is the schema version. P2==2 is the database format.
-** P2==3 is the recommended pager cache
-** size, and so forth. P1==0 is the main database file and P1==1 is the
+** P2==3 is the recommended pager cache
+** size, and so forth. P1==0 is the main database file and P1==1 is the
** database file used to store temporary tables.
**
** A transaction must be started before executing this opcode.
+**
+** If P2 is the SCHEMA_VERSION cookie (cookie number 1) then the internal
+** schema version is set to P3-P5. The "PRAGMA schema_version=N" statement
+** has P5 set to 1, so that the internal schema version will be different
+** from the database schema version, resulting in a schema reset.
*/
case OP_SetCookie: {
Db *pDb;
@@ -86349,8 +96723,9 @@ case OP_SetCookie: {
rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3);
if( pOp->p2==BTREE_SCHEMA_VERSION ){
/* When the schema cookie changes, record the new cookie internally */
- pDb->pSchema->schema_cookie = pOp->p3;
+ *(u32*)&pDb->pSchema->schema_cookie = *(u32*)&pOp->p3 - pOp->p5;
db->mDbFlags |= DBFLAG_SchemaChange;
+ sqlite3FkClearTriggerCache(db, pOp->p1);
}else if( pOp->p2==BTREE_FILE_FORMAT ){
/* Record changes in the file format */
pDb->pSchema->file_format = pOp->p3;
@@ -86369,8 +96744,8 @@ case OP_SetCookie: {
** Synopsis: root=P2 iDb=P3
**
** Open a read-only cursor for the database table whose root page is
-** P2 in a database file. The database file is determined by P3.
-** P3==0 means the main database, P3==1 means the database used for
+** P2 in a database file. The database file is determined by P3.
+** P3==0 means the main database, P3==1 means the database used for
** temporary tables, and P3>1 means used the corresponding attached
** database. Give the new cursor an identifier of P1. The P1
** values need not be contiguous but all P1 values should be small integers.
@@ -86380,14 +96755,14 @@ case OP_SetCookie: {
** <ul>
** <li> <b>0x02 OPFLAG_SEEKEQ</b>: This cursor will only be used for
** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT
-** of OP_SeekLE/OP_IdxGT)
+** of OP_SeekLE/OP_IdxLT)
** </ul>
**
** The P4 value may be either an integer (P4_INT32) or a pointer to
-** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
+** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
** object, then table being opened must be an [index b-tree] where the
-** KeyInfo object defines the content and collating
-** sequence of that index b-tree. Otherwise, if P4 is an integer
+** KeyInfo object defines the content and collating
+** sequence of that index b-tree. Otherwise, if P4 is an integer
** value, then the table being opened must be a [table b-tree] with a
** number of columns no less than the value of P4.
**
@@ -86410,7 +96785,7 @@ case OP_SetCookie: {
** <ul>
** <li> <b>0x02 OPFLAG_SEEKEQ</b>: This cursor will only be used for
** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT
-** of OP_SeekLE/OP_IdxGT)
+** of OP_SeekLE/OP_IdxLT)
** </ul>
**
** See also: OP_OpenRead, OP_OpenWrite
@@ -86423,10 +96798,10 @@ case OP_SetCookie: {
** OPFLAG_P2ISREG bit is set in P5 - see below).
**
** The P4 value may be either an integer (P4_INT32) or a pointer to
-** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
+** a KeyInfo structure (P4_KEYINFO). If it is a pointer to a KeyInfo
** object, then table being opened must be an [index b-tree] where the
-** KeyInfo object defines the content and collating
-** sequence of that index b-tree. Otherwise, if P4 is an integer
+** KeyInfo object defines the content and collating
+** sequence of that index b-tree. Otherwise, if P4 is an integer
** value, then the table being opened must be a [table b-tree] with a
** number of columns no less than the value of P4.
**
@@ -86434,7 +96809,7 @@ case OP_SetCookie: {
** <ul>
** <li> <b>0x02 OPFLAG_SEEKEQ</b>: This cursor will only be used for
** equality lookups (implemented as a pair of opcodes OP_SeekGE/OP_IdxGT
-** of OP_SeekLE/OP_IdxGT)
+** of OP_SeekLE/OP_IdxLT)
** <li> <b>0x08 OPFLAG_FORDELETE</b>: This cursor is used only to seek
** and subsequently delete entries in an index btree. This is a
** hint to the storage engine that the storage engine is allowed to
@@ -86449,10 +96824,10 @@ case OP_SetCookie: {
**
** See also: OP_OpenRead, OP_ReopenIdx
*/
-case OP_ReopenIdx: {
+case OP_ReopenIdx: { /* ncycle */
int nField;
KeyInfo *pKeyInfo;
- int p2;
+ u32 p2;
int iDb;
int wrFlag;
Btree *pX;
@@ -86464,11 +96839,13 @@ case OP_ReopenIdx: {
pCur = p->apCsr[pOp->p1];
if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){
assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */
+ assert( pCur->eCurType==CURTYPE_BTREE );
+ sqlite3BtreeClearCursor(pCur->uc.pCursor);
goto open_cursor_set_hints;
}
/* If the cursor is not currently open or is open on a different
** index, then fall through into OP_OpenRead to force a reopen */
-case OP_OpenRead:
+case OP_OpenRead: /* ncycle */
case OP_OpenWrite:
assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ );
@@ -86483,7 +96860,7 @@ case OP_OpenWrite:
nField = 0;
pKeyInfo = 0;
- p2 = pOp->p2;
+ p2 = (u32)pOp->p2;
iDb = pOp->p3;
assert( iDb>=0 && iDb<db->nDb );
assert( DbMaskTest(p->btreeMask, iDb) );
@@ -86502,7 +96879,7 @@ case OP_OpenWrite:
}
if( pOp->p5 & OPFLAG_P2ISREG ){
assert( p2>0 );
- assert( p2<=(p->nMem+1 - p->nCursor) );
+ assert( p2<=(u32)(p->nMem+1 - p->nCursor) );
assert( pOp->opcode==OP_OpenWrite );
pIn2 = &aMem[p2];
assert( memIsValid(pIn2) );
@@ -86526,8 +96903,9 @@ case OP_OpenWrite:
assert( pOp->p1>=0 );
assert( nField>=0 );
testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */
- pCur = allocateCursor(p, pOp->p1, nField, iDb, CURTYPE_BTREE);
+ pCur = allocateCursor(p, pOp->p1, nField, CURTYPE_BTREE);
if( pCur==0 ) goto no_mem;
+ pCur->iDb = iDb;
pCur->nullRow = 1;
pCur->isOrdered = 1;
pCur->pgnoRoot = p2;
@@ -86539,16 +96917,14 @@ case OP_OpenWrite:
/* Set the VdbeCursor.isTable variable. Previous versions of
** SQLite used to check if the root-page flags were sane at this point
** and report database corruption if they were not, but this check has
- ** since moved into the btree layer. */
+ ** since moved into the btree layer. */
pCur->isTable = pOp->p4type!=P4_KEYINFO;
open_cursor_set_hints:
assert( OPFLAG_BULKCSR==BTREE_BULKLOAD );
assert( OPFLAG_SEEKEQ==BTREE_SEEK_EQ );
testcase( pOp->p5 & OPFLAG_BULKCSR );
-#ifdef SQLITE_ENABLE_CURSOR_HINTS
testcase( pOp->p2 & OPFLAG_SEEKEQ );
-#endif
sqlite3BtreeCursorHintFlags(pCur->uc.pCursor,
(pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ)));
if( rc ) goto abort_due_to_error;
@@ -86563,20 +96939,26 @@ open_cursor_set_hints:
**
** Duplicate ephemeral cursors are used for self-joins of materialized views.
*/
-case OP_OpenDup: {
+case OP_OpenDup: { /* ncycle */
VdbeCursor *pOrig; /* The original cursor to be duplicated */
VdbeCursor *pCx; /* The new cursor */
pOrig = p->apCsr[pOp->p2];
- assert( pOrig->pBtx!=0 ); /* Only ephemeral cursors can be duplicated */
+ assert( pOrig );
+ assert( pOrig->isEphemeral ); /* Only ephemeral cursors can be duplicated */
- pCx = allocateCursor(p, pOp->p1, pOrig->nField, -1, CURTYPE_BTREE);
+ pCx = allocateCursor(p, pOp->p1, pOrig->nField, CURTYPE_BTREE);
if( pCx==0 ) goto no_mem;
pCx->nullRow = 1;
pCx->isEphemeral = 1;
pCx->pKeyInfo = pOrig->pKeyInfo;
pCx->isTable = pOrig->isTable;
- rc = sqlite3BtreeCursor(pOrig->pBtx, MASTER_ROOT, BTREE_WRCSR,
+ pCx->pgnoRoot = pOrig->pgnoRoot;
+ pCx->isOrdered = pOrig->isOrdered;
+ pCx->ub.pBtx = pOrig->ub.pBtx;
+ pCx->noReuse = 1;
+ pOrig->noReuse = 1;
+ rc = sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR,
pCx->pKeyInfo, pCx->uc.pCursor);
/* The sqlite3BtreeCursor() routine can only fail for the first cursor
** opened for a database. Since there is already an open cursor when this
@@ -86586,14 +96968,17 @@ case OP_OpenDup: {
}
-/* Opcode: OpenEphemeral P1 P2 * P4 P5
+/* Opcode: OpenEphemeral P1 P2 P3 P4 P5
** Synopsis: nColumn=P2
**
** Open a new cursor P1 to a transient table.
-** The cursor is always opened read/write even if
+** The cursor is always opened read/write even if
** the main database is read-only. The ephemeral
** table is deleted automatically when the cursor is closed.
**
+** If the cursor P1 is already opened on an ephemeral table, the table
+** is cleared (all content is erased).
+**
** P2 is the number of columns in the ephemeral table.
** The cursor points to a BTree table if P4==0 and to a BTree index
** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure
@@ -86603,6 +96988,10 @@ case OP_OpenDup: {
** in btree.h. These flags control aspects of the operation of
** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
** added automatically.
+**
+** If P3 is positive, then reg[P3] is modified slightly so that it
+** can be used as zero-length data for OP_Insert. This is an optimization
+** that avoids an extra OP_Blob opcode to initialize that register.
*/
/* Opcode: OpenAutoindex P1 P2 * P4 *
** Synopsis: nColumn=P2
@@ -86612,12 +97001,12 @@ case OP_OpenDup: {
** by this opcode will be used for automatically created transient
** indices in joins.
*/
-case OP_OpenAutoindex:
-case OP_OpenEphemeral: {
+case OP_OpenAutoindex: /* ncycle */
+case OP_OpenEphemeral: { /* ncycle */
VdbeCursor *pCx;
KeyInfo *pKeyInfo;
- static const int vfsFlags =
+ static const int vfsFlags =
SQLITE_OPEN_READWRITE |
SQLITE_OPEN_CREATE |
SQLITE_OPEN_EXCLUSIVE |
@@ -86625,41 +97014,66 @@ case OP_OpenEphemeral: {
SQLITE_OPEN_TRANSIENT_DB;
assert( pOp->p1>=0 );
assert( pOp->p2>=0 );
- pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE);
- if( pCx==0 ) goto no_mem;
- pCx->nullRow = 1;
- pCx->isEphemeral = 1;
- rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx,
- BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
- if( rc==SQLITE_OK ){
- rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0);
- }
- if( rc==SQLITE_OK ){
- /* If a transient index is required, create it by calling
- ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before
- ** opening it. If a transient table is required, just use the
- ** automatically created table with root-page 1 (an BLOB_INTKEY table).
- */
- if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
- int pgno;
- assert( pOp->p4type==P4_KEYINFO );
- rc = sqlite3BtreeCreateTable(pCx->pBtx, &pgno, BTREE_BLOBKEY | pOp->p5);
+ if( pOp->p3>0 ){
+ /* Make register reg[P3] into a value that can be used as the data
+ ** form sqlite3BtreeInsert() where the length of the data is zero. */
+ assert( pOp->p2==0 ); /* Only used when number of columns is zero */
+ assert( pOp->opcode==OP_OpenEphemeral );
+ assert( aMem[pOp->p3].flags & MEM_Null );
+ aMem[pOp->p3].n = 0;
+ aMem[pOp->p3].z = "";
+ }
+ pCx = p->apCsr[pOp->p1];
+ if( pCx && !pCx->noReuse && ALWAYS(pOp->p2<=pCx->nField) ){
+ /* If the ephemeral table is already open and has no duplicates from
+ ** OP_OpenDup, then erase all existing content so that the table is
+ ** empty again, rather than creating a new table. */
+ assert( pCx->isEphemeral );
+ pCx->seqCount = 0;
+ pCx->cacheStatus = CACHE_STALE;
+ rc = sqlite3BtreeClearTable(pCx->ub.pBtx, pCx->pgnoRoot, 0);
+ }else{
+ pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_BTREE);
+ if( pCx==0 ) goto no_mem;
+ pCx->isEphemeral = 1;
+ rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->ub.pBtx,
+ BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5,
+ vfsFlags);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3BtreeBeginTrans(pCx->ub.pBtx, 1, 0);
if( rc==SQLITE_OK ){
- assert( pgno==MASTER_ROOT+1 );
- assert( pKeyInfo->db==db );
- assert( pKeyInfo->enc==ENC(db) );
- rc = sqlite3BtreeCursor(pCx->pBtx, pgno, BTREE_WRCSR,
- pKeyInfo, pCx->uc.pCursor);
+ /* If a transient index is required, create it by calling
+ ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before
+ ** opening it. If a transient table is required, just use the
+ ** automatically created table with root-page 1 (an BLOB_INTKEY table).
+ */
+ if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
+ assert( pOp->p4type==P4_KEYINFO );
+ rc = sqlite3BtreeCreateTable(pCx->ub.pBtx, &pCx->pgnoRoot,
+ BTREE_BLOBKEY | pOp->p5);
+ if( rc==SQLITE_OK ){
+ assert( pCx->pgnoRoot==SCHEMA_ROOT+1 );
+ assert( pKeyInfo->db==db );
+ assert( pKeyInfo->enc==ENC(db) );
+ rc = sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR,
+ pKeyInfo, pCx->uc.pCursor);
+ }
+ pCx->isTable = 0;
+ }else{
+ pCx->pgnoRoot = SCHEMA_ROOT;
+ rc = sqlite3BtreeCursor(pCx->ub.pBtx, SCHEMA_ROOT, BTREE_WRCSR,
+ 0, pCx->uc.pCursor);
+ pCx->isTable = 1;
+ }
+ }
+ pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
+ if( rc ){
+ sqlite3BtreeClose(pCx->ub.pBtx);
}
- pCx->isTable = 0;
- }else{
- rc = sqlite3BtreeCursor(pCx->pBtx, MASTER_ROOT, BTREE_WRCSR,
- 0, pCx->uc.pCursor);
- pCx->isTable = 1;
}
}
if( rc ) goto abort_due_to_error;
- pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
+ pCx->nullRow = 1;
break;
}
@@ -86678,7 +97092,7 @@ case OP_SorterOpen: {
assert( pOp->p1>=0 );
assert( pOp->p2>=0 );
- pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_SORTER);
+ pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_SORTER);
if( pCx==0 ) goto no_mem;
pCx->pKeyInfo = pOp->p4.pKeyInfo;
assert( pCx->pKeyInfo->db==db );
@@ -86711,7 +97125,7 @@ case OP_SequenceTest: {
**
** Open a new cursor that points to a fake table that contains a single
** row of data. The content of that one row is the content of memory
-** register P2. In other words, cursor P1 becomes an alias for the
+** register P2. In other words, cursor P1 becomes an alias for the
** MEM_Blob content contained in register P2.
**
** A pseudo-table created by this opcode is used to hold a single
@@ -86727,7 +97141,7 @@ case OP_OpenPseudo: {
assert( pOp->p1>=0 );
assert( pOp->p3>=0 );
- pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, CURTYPE_PSEUDO);
+ pCx = allocateCursor(p, pOp->p1, pOp->p3, CURTYPE_PSEUDO);
if( pCx==0 ) goto no_mem;
pCx->nullRow = 1;
pCx->seekResult = pOp->p2;
@@ -86746,7 +97160,7 @@ case OP_OpenPseudo: {
** Close a cursor previously opened as P1. If P1 is not
** currently open, this instruction is a no-op.
*/
-case OP_Close: {
+case OP_Close: { /* ncycle */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
sqlite3VdbeFreeCursor(p, p->apCsr[pOp->p1]);
p->apCsr[pOp->p1] = 0;
@@ -86776,21 +97190,23 @@ case OP_ColumnsUsed: {
/* Opcode: SeekGE P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
**
-** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
-** use the value in register P3 as the key. If cursor P1 refers
-** to an SQL index, then P3 is the first in an array of P4 registers
-** that are used as an unpacked index key.
+** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
+** use the value in register P3 as the key. If cursor P1 refers
+** to an SQL index, then P3 is the first in an array of P4 registers
+** that are used as an unpacked index key.
**
-** Reposition cursor P1 so that it points to the smallest entry that
-** is greater than or equal to the key value. If there are no records
+** Reposition cursor P1 so that it points to the smallest entry that
+** is greater than or equal to the key value. If there are no records
** greater than or equal to the key and P2 is not zero, then jump to P2.
**
** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this
-** opcode will always land on a record that equally equals the key, or
-** else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this
-** opcode must be followed by an IdxLE opcode with the same arguments.
-** The IdxLE opcode will be skipped if this opcode succeeds, but the
-** IdxLE opcode will be used on subsequent loop iterations.
+** opcode will either land on a record that exactly matches the key, or
+** else it will cause a jump to P2. When the cursor is OPFLAG_SEEKEQ,
+** this opcode must be followed by an IdxLE opcode with the same arguments.
+** The IdxGT opcode will be skipped if this opcode succeeds, but the
+** IdxGT opcode will be used on subsequent loop iterations. The
+** OPFLAG_SEEKEQ flags is a hint to the btree layer to say that this
+** is an equality search.
**
** This opcode leaves the cursor configured to move in forward order,
** from the beginning toward the end. In other words, the cursor is
@@ -86801,13 +97217,13 @@ case OP_ColumnsUsed: {
/* Opcode: SeekGT P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
**
-** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
-** use the value in register P3 as a key. If cursor P1 refers
-** to an SQL index, then P3 is the first in an array of P4 registers
-** that are used as an unpacked index key.
+** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
+** use the value in register P3 as a key. If cursor P1 refers
+** to an SQL index, then P3 is the first in an array of P4 registers
+** that are used as an unpacked index key.
**
-** Reposition cursor P1 so that it points to the smallest entry that
-** is greater than the key value. If there are no records greater than
+** Reposition cursor P1 so that it points to the smallest entry that
+** is greater than the key value. If there are no records greater than
** the key and P2 is not zero, then jump to P2.
**
** This opcode leaves the cursor configured to move in forward order,
@@ -86816,16 +97232,16 @@ case OP_ColumnsUsed: {
**
** See also: Found, NotFound, SeekLt, SeekGe, SeekLe
*/
-/* Opcode: SeekLT P1 P2 P3 P4 *
+/* Opcode: SeekLT P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
**
-** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
-** use the value in register P3 as a key. If cursor P1 refers
-** to an SQL index, then P3 is the first in an array of P4 registers
-** that are used as an unpacked index key.
+** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
+** use the value in register P3 as a key. If cursor P1 refers
+** to an SQL index, then P3 is the first in an array of P4 registers
+** that are used as an unpacked index key.
**
-** Reposition cursor P1 so that it points to the largest entry that
-** is less than the key value. If there are no records less than
+** Reposition cursor P1 so that it points to the largest entry that
+** is less than the key value. If there are no records less than
** the key and P2 is not zero, then jump to P2.
**
** This opcode leaves the cursor configured to move in reverse order,
@@ -86837,13 +97253,13 @@ case OP_ColumnsUsed: {
/* Opcode: SeekLE P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
**
-** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
-** use the value in register P3 as a key. If cursor P1 refers
-** to an SQL index, then P3 is the first in an array of P4 registers
-** that are used as an unpacked index key.
+** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
+** use the value in register P3 as a key. If cursor P1 refers
+** to an SQL index, then P3 is the first in an array of P4 registers
+** that are used as an unpacked index key.
**
-** Reposition cursor P1 so that it points to the largest entry that
-** is less than or equal to the key value. If there are no records
+** Reposition cursor P1 so that it points to the largest entry that
+** is less than or equal to the key value. If there are no records
** less than or equal to the key and P2 is not zero, then jump to P2.
**
** This opcode leaves the cursor configured to move in reverse order,
@@ -86851,18 +97267,20 @@ case OP_ColumnsUsed: {
** configured to use Prev, not Next.
**
** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this
-** opcode will always land on a record that equally equals the key, or
-** else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this
-** opcode must be followed by an IdxGE opcode with the same arguments.
+** opcode will either land on a record that exactly matches the key, or
+** else it will cause a jump to P2. When the cursor is OPFLAG_SEEKEQ,
+** this opcode must be followed by an IdxLE opcode with the same arguments.
** The IdxGE opcode will be skipped if this opcode succeeds, but the
-** IdxGE opcode will be used on subsequent loop iterations.
+** IdxGE opcode will be used on subsequent loop iterations. The
+** OPFLAG_SEEKEQ flags is a hint to the btree layer to say that this
+** is an equality search.
**
** See also: Found, NotFound, SeekGt, SeekGe, SeekLt
*/
-case OP_SeekLT: /* jump, in3, group */
-case OP_SeekLE: /* jump, in3, group */
-case OP_SeekGE: /* jump, in3, group */
-case OP_SeekGT: { /* jump, in3, group */
+case OP_SeekLT: /* jump, in3, group, ncycle */
+case OP_SeekLE: /* jump, in3, group, ncycle */
+case OP_SeekGE: /* jump, in3, group, ncycle */
+case OP_SeekGT: { /* jump, in3, group, ncycle */
int res; /* Comparison result */
int oc; /* Opcode */
VdbeCursor *pC; /* The cursor to seek */
@@ -86888,8 +97306,11 @@ case OP_SeekGT: { /* jump, in3, group */
pC->seekOp = pOp->opcode;
#endif
+ pC->deferredMoveto = 0;
+ pC->cacheStatus = CACHE_STALE;
if( pC->isTable ){
- /* The BTREE_SEEK_EQ flag is only set on index cursors */
+ u16 flags3, newType;
+ /* The OPFLAG_SEEKEQ/BTREE_SEEK_EQ flag is only set on index cursors */
assert( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ)==0
|| CORRUPT_DB );
@@ -86897,20 +97318,29 @@ case OP_SeekGT: { /* jump, in3, group */
** blob, or NULL. But it needs to be an integer before we can do
** the seek, so convert it. */
pIn3 = &aMem[pOp->p3];
- if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
+ flags3 = pIn3->flags;
+ if( (flags3 & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn3, 0);
}
- iKey = sqlite3VdbeIntValue(pIn3);
+ iKey = sqlite3VdbeIntValue(pIn3); /* Get the integer key value */
+ newType = pIn3->flags; /* Record the type after applying numeric affinity */
+ pIn3->flags = flags3; /* But convert the type back to its original */
/* If the P3 value could not be converted into an integer without
** loss of information, then special processing is required... */
- if( (pIn3->flags & MEM_Int)==0 ){
- if( (pIn3->flags & MEM_Real)==0 ){
- /* If the P3 value cannot be converted into any kind of a number,
- ** then the seek is not possible, so jump to P2 */
- VdbeBranchTaken(1,2); goto jump_to_p2;
- break;
+ if( (newType & (MEM_Int|MEM_IntReal))==0 ){
+ int c;
+ if( (newType & MEM_Real)==0 ){
+ if( (newType & MEM_Null) || oc>=OP_SeekGE ){
+ VdbeBranchTaken(1,2);
+ goto jump_to_p2;
+ }else{
+ rc = sqlite3BtreeLast(pC->uc.pCursor, &res);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ goto seek_not_found;
+ }
}
+ c = sqlite3IntFloatCompare(iKey, pIn3->u.r);
/* If the approximation iKey is larger than the actual real search
** term, substitute >= for > and < for <=. e.g. if the search term
@@ -86919,7 +97349,7 @@ case OP_SeekGT: { /* jump, in3, group */
** (x > 4.9) -> (x >= 5)
** (x <= 4.9) -> (x < 5)
*/
- if( pIn3->u.r<(double)iKey ){
+ if( c>0 ){
assert( OP_SeekGE==(OP_SeekGT-1) );
assert( OP_SeekLT==(OP_SeekLE-1) );
assert( (OP_SeekLE & 0x0001)==(OP_SeekGT & 0x0001) );
@@ -86928,27 +97358,30 @@ case OP_SeekGT: { /* jump, in3, group */
/* If the approximation iKey is smaller than the actual real search
** term, substitute <= for < and > for >=. */
- else if( pIn3->u.r>(double)iKey ){
+ else if( c<0 ){
assert( OP_SeekLE==(OP_SeekLT+1) );
assert( OP_SeekGT==(OP_SeekGE+1) );
assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) );
if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++;
}
- }
- rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)iKey, 0, &res);
+ }
+ rc = sqlite3BtreeTableMoveto(pC->uc.pCursor, (u64)iKey, 0, &res);
pC->movetoTarget = iKey; /* Used by OP_Delete */
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
}else{
- /* For a cursor with the BTREE_SEEK_EQ hint, only the OP_SeekGE and
- ** OP_SeekLE opcodes are allowed, and these must be immediately followed
- ** by an OP_IdxGT or OP_IdxLT opcode, respectively, with the same key.
+ /* For a cursor with the OPFLAG_SEEKEQ/BTREE_SEEK_EQ hint, only the
+ ** OP_SeekGE and OP_SeekLE opcodes are allowed, and these must be
+ ** immediately followed by an OP_IdxGT or OP_IdxLT opcode, respectively,
+ ** with the same key.
*/
if( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ) ){
eqOnly = 1;
assert( pOp->opcode==OP_SeekGE || pOp->opcode==OP_SeekLE );
assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT );
+ assert( pOp->opcode==OP_SeekGE || pOp[1].opcode==OP_IdxLT );
+ assert( pOp->opcode==OP_SeekLE || pOp[1].opcode==OP_IdxGT );
assert( pOp[1].p1==pOp[0].p1 );
assert( pOp[1].p2==pOp[0].p2 );
assert( pOp[1].p3==pOp[0].p3 );
@@ -86976,10 +97409,16 @@ case OP_SeekGT: { /* jump, in3, group */
r.aMem = &aMem[pOp->p3];
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
+ {
+ int i;
+ for(i=0; i<r.nField; i++){
+ assert( memIsValid(&r.aMem[i]) );
+ if( i>0 ) REGISTER_TRACE(pOp->p3+i, &r.aMem[i]);
+ }
+ }
#endif
r.eqSeen = 0;
- rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, &r, 0, 0, &res);
+ rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &res);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
@@ -86988,8 +97427,6 @@ case OP_SeekGT: { /* jump, in3, group */
goto seek_not_found;
}
}
- pC->deferredMoveto = 0;
- pC->cacheStatus = CACHE_STALE;
#ifdef SQLITE_TEST
sqlite3_search_count++;
#endif
@@ -87040,22 +97477,237 @@ seek_not_found:
break;
}
-/* Opcode: SeekHit P1 P2 * * *
-** Synopsis: seekHit=P2
+
+/* Opcode: SeekScan P1 P2 * * P5
+** Synopsis: Scan-ahead up to P1 rows
+**
+** This opcode is a prefix opcode to OP_SeekGE. In other words, this
+** opcode must be immediately followed by OP_SeekGE. This constraint is
+** checked by assert() statements.
+**
+** This opcode uses the P1 through P4 operands of the subsequent
+** OP_SeekGE. In the text that follows, the operands of the subsequent
+** OP_SeekGE opcode are denoted as SeekOP.P1 through SeekOP.P4. Only
+** the P1, P2 and P5 operands of this opcode are also used, and are called
+** This.P1, This.P2 and This.P5.
+**
+** This opcode helps to optimize IN operators on a multi-column index
+** where the IN operator is on the later terms of the index by avoiding
+** unnecessary seeks on the btree, substituting steps to the next row
+** of the b-tree instead. A correct answer is obtained if this opcode
+** is omitted or is a no-op.
+**
+** The SeekGE.P3 and SeekGE.P4 operands identify an unpacked key which
+** is the desired entry that we want the cursor SeekGE.P1 to be pointing
+** to. Call this SeekGE.P3/P4 row the "target".
+**
+** If the SeekGE.P1 cursor is not currently pointing to a valid row,
+** then this opcode is a no-op and control passes through into the OP_SeekGE.
+**
+** If the SeekGE.P1 cursor is pointing to a valid row, then that row
+** might be the target row, or it might be near and slightly before the
+** target row, or it might be after the target row. If the cursor is
+** currently before the target row, then this opcode attempts to position
+** the cursor on or after the target row by invoking sqlite3BtreeStep()
+** on the cursor between 1 and This.P1 times.
+**
+** The This.P5 parameter is a flag that indicates what to do if the
+** cursor ends up pointing at a valid row that is past the target
+** row. If This.P5 is false (0) then a jump is made to SeekGE.P2. If
+** This.P5 is true (non-zero) then a jump is made to This.P2. The P5==0
+** case occurs when there are no inequality constraints to the right of
+** the IN constraint. The jump to SeekGE.P2 ends the loop. The P5!=0 case
+** occurs when there are inequality constraints to the right of the IN
+** operator. In that case, the This.P2 will point either directly to or
+** to setup code prior to the OP_IdxGT or OP_IdxGE opcode that checks for
+** loop terminate.
+**
+** Possible outcomes from this opcode:<ol>
+**
+** <li> If the cursor is initially not pointed to any valid row, then
+** fall through into the subsequent OP_SeekGE opcode.
+**
+** <li> If the cursor is left pointing to a row that is before the target
+** row, even after making as many as This.P1 calls to
+** sqlite3BtreeNext(), then also fall through into OP_SeekGE.
+**
+** <li> If the cursor is left pointing at the target row, either because it
+** was at the target row to begin with or because one or more
+** sqlite3BtreeNext() calls moved the cursor to the target row,
+** then jump to This.P2..,
+**
+** <li> If the cursor started out before the target row and a call to
+** to sqlite3BtreeNext() moved the cursor off the end of the index
+** (indicating that the target row definitely does not exist in the
+** btree) then jump to SeekGE.P2, ending the loop.
+**
+** <li> If the cursor ends up on a valid row that is past the target row
+** (indicating that the target row does not exist in the btree) then
+** jump to SeekOP.P2 if This.P5==0 or to This.P2 if This.P5>0.
+** </ol>
+*/
+case OP_SeekScan: { /* ncycle */
+ VdbeCursor *pC;
+ int res;
+ int nStep;
+ UnpackedRecord r;
+
+ assert( pOp[1].opcode==OP_SeekGE );
+
+ /* If pOp->p5 is clear, then pOp->p2 points to the first instruction past the
+ ** OP_IdxGT that follows the OP_SeekGE. Otherwise, it points to the first
+ ** opcode past the OP_SeekGE itself. */
+ assert( pOp->p2>=(int)(pOp-aOp)+2 );
+#ifdef SQLITE_DEBUG
+ if( pOp->p5==0 ){
+ /* There are no inequality constraints following the IN constraint. */
+ assert( pOp[1].p1==aOp[pOp->p2-1].p1 );
+ assert( pOp[1].p2==aOp[pOp->p2-1].p2 );
+ assert( pOp[1].p3==aOp[pOp->p2-1].p3 );
+ assert( aOp[pOp->p2-1].opcode==OP_IdxGT
+ || aOp[pOp->p2-1].opcode==OP_IdxGE );
+ testcase( aOp[pOp->p2-1].opcode==OP_IdxGE );
+ }else{
+ /* There are inequality constraints. */
+ assert( pOp->p2==(int)(pOp-aOp)+2 );
+ assert( aOp[pOp->p2-1].opcode==OP_SeekGE );
+ }
+#endif
+
+ assert( pOp->p1>0 );
+ pC = p->apCsr[pOp[1].p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( !pC->isTable );
+ if( !sqlite3BtreeCursorIsValidNN(pC->uc.pCursor) ){
+#ifdef SQLITE_DEBUG
+ if( db->flags&SQLITE_VdbeTrace ){
+ printf("... cursor not valid - fall through\n");
+ }
+#endif
+ break;
+ }
+ nStep = pOp->p1;
+ assert( nStep>=1 );
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = (u16)pOp[1].p4.i;
+ r.default_rc = 0;
+ r.aMem = &aMem[pOp[1].p3];
+#ifdef SQLITE_DEBUG
+ {
+ int i;
+ for(i=0; i<r.nField; i++){
+ assert( memIsValid(&r.aMem[i]) );
+ REGISTER_TRACE(pOp[1].p3+i, &aMem[pOp[1].p3+i]);
+ }
+ }
+#endif
+ res = 0; /* Not needed. Only used to silence a warning. */
+ while(1){
+ rc = sqlite3VdbeIdxKeyCompare(db, pC, &r, &res);
+ if( rc ) goto abort_due_to_error;
+ if( res>0 && pOp->p5==0 ){
+ seekscan_search_fail:
+ /* Jump to SeekGE.P2, ending the loop */
+#ifdef SQLITE_DEBUG
+ if( db->flags&SQLITE_VdbeTrace ){
+ printf("... %d steps and then skip\n", pOp->p1 - nStep);
+ }
+#endif
+ VdbeBranchTaken(1,3);
+ pOp++;
+ goto jump_to_p2;
+ }
+ if( res>=0 ){
+ /* Jump to This.P2, bypassing the OP_SeekGE opcode */
+#ifdef SQLITE_DEBUG
+ if( db->flags&SQLITE_VdbeTrace ){
+ printf("... %d steps and then success\n", pOp->p1 - nStep);
+ }
+#endif
+ VdbeBranchTaken(2,3);
+ goto jump_to_p2;
+ break;
+ }
+ if( nStep<=0 ){
+#ifdef SQLITE_DEBUG
+ if( db->flags&SQLITE_VdbeTrace ){
+ printf("... fall through after %d steps\n", pOp->p1);
+ }
+#endif
+ VdbeBranchTaken(0,3);
+ break;
+ }
+ nStep--;
+ pC->cacheStatus = CACHE_STALE;
+ rc = sqlite3BtreeNext(pC->uc.pCursor, 0);
+ if( rc ){
+ if( rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
+ goto seekscan_search_fail;
+ }else{
+ goto abort_due_to_error;
+ }
+ }
+ }
+
+ break;
+}
+
+
+/* Opcode: SeekHit P1 P2 P3 * *
+** Synopsis: set P2<=seekHit<=P3
+**
+** Increase or decrease the seekHit value for cursor P1, if necessary,
+** so that it is no less than P2 and no greater than P3.
**
-** Set the seekHit flag on cursor P1 to the value in P2.
-** The seekHit flag is used by the IfNoHope opcode.
+** The seekHit integer represents the maximum of terms in an index for which
+** there is known to be at least one match. If the seekHit value is smaller
+** than the total number of equality terms in an index lookup, then the
+** OP_IfNoHope opcode might run to see if the IN loop can be abandoned
+** early, thus saving work. This is part of the IN-early-out optimization.
**
-** P1 must be a valid b-tree cursor. P2 must be a boolean value,
-** either 0 or 1.
+** P1 must be a valid b-tree cursor.
*/
-case OP_SeekHit: {
+case OP_SeekHit: { /* ncycle */
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- assert( pOp->p2==0 || pOp->p2==1 );
- pC->seekHit = pOp->p2 & 1;
+ assert( pOp->p3>=pOp->p2 );
+ if( pC->seekHit<pOp->p2 ){
+#ifdef SQLITE_DEBUG
+ if( db->flags&SQLITE_VdbeTrace ){
+ printf("seekHit changes from %d to %d\n", pC->seekHit, pOp->p2);
+ }
+#endif
+ pC->seekHit = pOp->p2;
+ }else if( pC->seekHit>pOp->p3 ){
+#ifdef SQLITE_DEBUG
+ if( db->flags&SQLITE_VdbeTrace ){
+ printf("seekHit changes from %d to %d\n", pC->seekHit, pOp->p3);
+ }
+#endif
+ pC->seekHit = pOp->p3;
+ }
+ break;
+}
+
+/* Opcode: IfNotOpen P1 P2 * * *
+** Synopsis: if( !csr[P1] ) goto P2
+**
+** If cursor P1 is not open or if P1 is set to a NULL row using the
+** OP_NullRow opcode, then jump to instruction P2. Otherwise, fall through.
+*/
+case OP_IfNotOpen: { /* jump */
+ VdbeCursor *pCur;
+
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pCur = p->apCsr[pOp->p1];
+ VdbeBranchTaken(pCur==0 || pCur->nullRow, 2);
+ if( pCur==0 || pCur->nullRow ){
+ goto jump_to_p2_and_check_for_interrupt;
+ }
break;
}
@@ -87082,9 +97734,9 @@ case OP_SeekHit: {
** If P4==0 then register P3 holds a blob constructed by MakeRecord. If
** P4>0 then register P3 is the first of P4 registers that form an unpacked
** record.
-**
+**
** Cursor P1 is on an index btree. If the record identified by P3 and P4
-** is not the prefix of any entry in P1 then a jump is made to P2. If P1
+** is not the prefix of any entry in P1 then a jump is made to P2. If P1
** does contain an entry whose prefix matches the P3/P4 record then control
** falls through to the next instruction and P1 is left pointing at the
** matching entry.
@@ -87099,16 +97751,20 @@ case OP_SeekHit: {
** Synopsis: key=r[P3@P4]
**
** Register P3 is the first of P4 registers that form an unpacked
-** record.
+** record. Cursor P1 is an index btree. P2 is a jump destination.
+** In other words, the operands to this opcode are the same as the
+** operands to OP_NotFound and OP_IdxGT.
**
-** Cursor P1 is on an index btree. If the seekHit flag is set on P1, then
-** this opcode is a no-op. But if the seekHit flag of P1 is clear, then
-** check to see if there is any entry in P1 that matches the
-** prefix identified by P3 and P4. If no entry matches the prefix,
-** jump to P2. Otherwise fall through.
+** This opcode is an optimization attempt only. If this opcode always
+** falls through, the correct answer is still obtained, but extra work
+** is performed.
**
-** This opcode behaves like OP_NotFound if the seekHit
-** flag is clear and it behaves like OP_Noop if the seekHit flag is set.
+** A value of N in the seekHit flag of cursor P1 means that there exists
+** a key P3:N that will match some record in the index. We want to know
+** if it is possible for a record P3:P4 to match some record in the
+** index. If it is not possible, we can skip some work. So if seekHit
+** is less than P4, attempt to find out if a match is possible by running
+** OP_NotFound.
**
** This opcode is used in IN clause processing for a multi-column key.
** If an IN clause is attached to an element of the key other than the
@@ -87128,7 +97784,7 @@ case OP_SeekHit: {
** If P4==0 then register P3 holds a blob constructed by MakeRecord. If
** P4>0 then register P3 is the first of P4 registers that form an unpacked
** record.
-**
+**
** Cursor P1 is on an index btree. If the record identified by P3 and P4
** contains any NULL value, jump immediately to P2. If all terms of the
** record are not-NULL then a check is done to determine if any row in the
@@ -87145,23 +97801,26 @@ case OP_SeekHit: {
**
** See also: NotFound, Found, NotExists
*/
-case OP_IfNoHope: { /* jump, in3 */
+case OP_IfNoHope: { /* jump, in3, ncycle */
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- if( pC->seekHit ) break;
+#ifdef SQLITE_DEBUG
+ if( db->flags&SQLITE_VdbeTrace ){
+ printf("seekHit is %d\n", pC->seekHit);
+ }
+#endif
+ if( pC->seekHit>=pOp->p4.i ) break;
/* Fall through into OP_NotFound */
+ /* no break */ deliberate_fall_through
}
-case OP_NoConflict: /* jump, in3 */
-case OP_NotFound: /* jump, in3 */
-case OP_Found: { /* jump, in3 */
+case OP_NoConflict: /* jump, in3, ncycle */
+case OP_NotFound: /* jump, in3, ncycle */
+case OP_Found: { /* jump, in3, ncycle */
int alreadyExists;
- int takeJump;
int ii;
VdbeCursor *pC;
- int res;
- UnpackedRecord *pFree;
UnpackedRecord *pIdxKey;
UnpackedRecord r;
@@ -87176,14 +97835,15 @@ case OP_Found: { /* jump, in3 */
#ifdef SQLITE_DEBUG
pC->seekOp = pOp->opcode;
#endif
- pIn3 = &aMem[pOp->p3];
+ r.aMem = &aMem[pOp->p3];
assert( pC->eCurType==CURTYPE_BTREE );
assert( pC->uc.pCursor!=0 );
assert( pC->isTable==0 );
- if( pOp->p4.i>0 ){
+ r.nField = (u16)pOp->p4.i;
+ if( r.nField>0 ){
+ /* Key values in an array of registers */
r.pKeyInfo = pC->pKeyInfo;
- r.nField = (u16)pOp->p4.i;
- r.aMem = pIn3;
+ r.default_rc = 0;
#ifdef SQLITE_DEBUG
for(ii=0; ii<r.nField; ii++){
assert( memIsValid(&r.aMem[ii]) );
@@ -87191,37 +97851,25 @@ case OP_Found: { /* jump, in3 */
if( ii ) REGISTER_TRACE(pOp->p3+ii, &r.aMem[ii]);
}
#endif
- pIdxKey = &r;
- pFree = 0;
+ rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, &r, &pC->seekResult);
}else{
- assert( pIn3->flags & MEM_Blob );
- rc = ExpandBlob(pIn3);
+ /* Composite key generated by OP_MakeRecord */
+ assert( r.aMem->flags & MEM_Blob );
+ assert( pOp->opcode!=OP_NoConflict );
+ rc = ExpandBlob(r.aMem);
assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
if( rc ) goto no_mem;
- pFree = pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo);
+ pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo);
if( pIdxKey==0 ) goto no_mem;
- sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
+ sqlite3VdbeRecordUnpack(pC->pKeyInfo, r.aMem->n, r.aMem->z, pIdxKey);
+ pIdxKey->default_rc = 0;
+ rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &pC->seekResult);
+ sqlite3DbFreeNN(db, pIdxKey);
}
- pIdxKey->default_rc = 0;
- takeJump = 0;
- if( pOp->opcode==OP_NoConflict ){
- /* For the OP_NoConflict opcode, take the jump if any of the
- ** input fields are NULL, since any key with a NULL will not
- ** conflict */
- for(ii=0; ii<pIdxKey->nField; ii++){
- if( pIdxKey->aMem[ii].flags & MEM_Null ){
- takeJump = 1;
- break;
- }
- }
- }
- rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, pIdxKey, 0, 0, &res);
- if( pFree ) sqlite3DbFreeNN(db, pFree);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
- pC->seekResult = res;
- alreadyExists = (res==0);
+ alreadyExists = (pC->seekResult==0);
pC->nullRow = 1-alreadyExists;
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
@@ -87229,8 +97877,25 @@ case OP_Found: { /* jump, in3 */
VdbeBranchTaken(alreadyExists!=0,2);
if( alreadyExists ) goto jump_to_p2;
}else{
- VdbeBranchTaken(takeJump||alreadyExists==0,2);
- if( takeJump || !alreadyExists ) goto jump_to_p2;
+ if( !alreadyExists ){
+ VdbeBranchTaken(1,2);
+ goto jump_to_p2;
+ }
+ if( pOp->opcode==OP_NoConflict ){
+ /* For the OP_NoConflict opcode, take the jump if any of the
+ ** input fields are NULL, since any key with a NULL will not
+ ** conflict */
+ for(ii=0; ii<r.nField; ii++){
+ if( r.aMem[ii].flags & MEM_Null ){
+ VdbeBranchTaken(1,2);
+ goto jump_to_p2;
+ }
+ }
+ }
+ VdbeBranchTaken(0,2);
+ if( pOp->opcode==OP_IfNoHope ){
+ pC->seekHit = pOp->p4.i;
+ }
}
break;
}
@@ -87240,9 +97905,9 @@ case OP_Found: { /* jump, in3 */
**
** P1 is the index of a cursor open on an SQL table btree (with integer
** keys). If register P3 does not contain an integer or if P1 does not
-** contain a record with rowid P3 then jump immediately to P2.
+** contain a record with rowid P3 then jump immediately to P2.
** Or, if P2 is 0, raise an SQLITE_CORRUPT error. If P1 does contain
-** a record with rowid P3 then
+** a record with rowid P3 then
** leave the cursor pointing at that record and fall through to the next
** instruction.
**
@@ -87265,7 +97930,7 @@ case OP_Found: { /* jump, in3 */
** P1 is the index of a cursor open on an SQL table btree (with integer
** keys). P3 is an integer rowid. If P1 does not contain a record with
** rowid P3 then jump immediately to P2. Or, if P2 is 0, raise an
-** SQLITE_CORRUPT error. If P1 does contain a record with rowid P3 then
+** SQLITE_CORRUPT error. If P1 does contain a record with rowid P3 then
** leave the cursor pointing at that record and fall through to the next
** instruction.
**
@@ -87282,42 +97947,48 @@ case OP_Found: { /* jump, in3 */
**
** See also: Found, NotFound, NoConflict, SeekRowid
*/
-case OP_SeekRowid: { /* jump, in3 */
+case OP_SeekRowid: { /* jump, in3, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
u64 iKey;
pIn3 = &aMem[pOp->p3];
- if( (pIn3->flags & MEM_Int)==0 ){
- /* Make sure pIn3->u.i contains a valid integer representation of
- ** the key value, but do not change the datatype of the register, as
- ** other parts of the perpared statement might be depending on the
- ** current datatype. */
- u16 origFlags = pIn3->flags;
- int isNotInt;
- applyAffinity(pIn3, SQLITE_AFF_NUMERIC, encoding);
- isNotInt = (pIn3->flags & MEM_Int)==0;
- pIn3->flags = origFlags;
- if( isNotInt ) goto jump_to_p2;
+ testcase( pIn3->flags & MEM_Int );
+ testcase( pIn3->flags & MEM_IntReal );
+ testcase( pIn3->flags & MEM_Real );
+ testcase( (pIn3->flags & (MEM_Str|MEM_Int))==MEM_Str );
+ if( (pIn3->flags & (MEM_Int|MEM_IntReal))==0 ){
+ /* If pIn3->u.i does not contain an integer, compute iKey as the
+ ** integer value of pIn3. Jump to P2 if pIn3 cannot be converted
+ ** into an integer without loss of information. Take care to avoid
+ ** changing the datatype of pIn3, however, as it is used by other
+ ** parts of the prepared statement. */
+ Mem x = pIn3[0];
+ applyAffinity(&x, SQLITE_AFF_NUMERIC, encoding);
+ if( (x.flags & MEM_Int)==0 ) goto jump_to_p2;
+ iKey = x.u.i;
+ goto notExistsWithKey;
}
/* Fall through into OP_NotExists */
-case OP_NotExists: /* jump, in3 */
+ /* no break */ deliberate_fall_through
+case OP_NotExists: /* jump, in3, ncycle */
pIn3 = &aMem[pOp->p3];
assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ iKey = pIn3->u.i;
+notExistsWithKey:
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
#ifdef SQLITE_DEBUG
- pC->seekOp = OP_SeekRowid;
+ if( pOp->opcode==OP_SeekRowid ) pC->seekOp = OP_SeekRowid;
#endif
assert( pC->isTable );
assert( pC->eCurType==CURTYPE_BTREE );
pCrsr = pC->uc.pCursor;
assert( pCrsr!=0 );
res = 0;
- iKey = pIn3->u.i;
- rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
+ rc = sqlite3BtreeTableMoveto(pCrsr, iKey, 0, &res);
assert( rc==SQLITE_OK || res==0 );
pC->movetoTarget = iKey; /* Used by OP_Delete */
pC->nullRow = 0;
@@ -87343,7 +98014,7 @@ case OP_NotExists: /* jump, in3 */
** Find the next available sequence number for cursor P1.
** Write the sequence number into register P2.
** The sequence number on the cursor is incremented after this
-** instruction.
+** instruction.
*/
case OP_Sequence: { /* out2 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
@@ -87363,9 +98034,9 @@ case OP_Sequence: { /* out2 */
** table that cursor P1 points to. The new record number is written
** written to register P2.
**
-** If P3>0 then P3 is a register in the root frame of this VDBE that holds
+** If P3>0 then P3 is a register in the root frame of this VDBE that holds
** the largest previously generated record number. No new record numbers are
-** allowed to be less than this value. When this value reaches its maximum,
+** allowed to be less than this value. When this value reaches its maximum,
** an SQLITE_FULL error is generated. The P3 register is updated with the '
** generated record number. This P3 mechanism is used to help implement the
** AUTOINCREMENT feature.
@@ -87375,8 +98046,10 @@ case OP_NewRowid: { /* out2 */
VdbeCursor *pC; /* Cursor of table to get the new rowid */
int res; /* Result of an sqlite3BtreeLast() */
int cnt; /* Counter to limit the number of searches */
+#ifndef SQLITE_OMIT_AUTOINCREMENT
Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */
VdbeFrame *pFrame; /* Root frame of VDBE */
+#endif
v = 0;
res = 0;
@@ -87472,7 +98145,7 @@ case OP_NewRowid: { /* out2 */
do{
sqlite3_randomness(sizeof(v), &v);
v &= (MAX_ROWID>>1); v++; /* Ensure that v is greater than zero */
- }while( ((rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)v,
+ }while( ((rc = sqlite3BtreeTableMoveto(pC->uc.pCursor, (u64)v,
0, &res))==SQLITE_OK)
&& (res==0)
&& (++cnt<100));
@@ -87514,8 +98187,8 @@ case OP_NewRowid: { /* out2 */
** is part of an INSERT operation. The difference is only important to
** the update hook.
**
-** Parameter P4 may point to a Table structure, or may be NULL. If it is
-** not NULL, then the update-hook (sqlite3.xUpdateCallback) is invoked
+** Parameter P4 may point to a Table structure, or may be NULL. If it is
+** not NULL, then the update-hook (sqlite3.xUpdateCallback) is invoked
** following a successful insert.
**
** (WARNING/TODO: If P1 is a pseudo-cursor and P2 is dynamically
@@ -87527,14 +98200,7 @@ case OP_NewRowid: { /* out2 */
** This instruction only works on tables. The equivalent instruction
** for indices is OP_IdxInsert.
*/
-/* Opcode: InsertInt P1 P2 P3 P4 P5
-** Synopsis: intkey=P3 data=r[P2]
-**
-** This works exactly like OP_Insert except that the key is the
-** integer value P3, not the value of the integer stored in register P3.
-*/
-case OP_Insert:
-case OP_InsertInt: {
+case OP_Insert: {
Mem *pData; /* MEM cell holding data for the record to be inserted */
Mem *pKey; /* MEM cell holding key for the record */
VdbeCursor *pC; /* Cursor to table into which insert is written */
@@ -87549,22 +98215,18 @@ case OP_InsertInt: {
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->deferredMoveto==0 );
assert( pC->uc.pCursor!=0 );
assert( (pOp->p5 & OPFLAG_ISNOOP) || pC->isTable );
assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC );
REGISTER_TRACE(pOp->p2, pData);
sqlite3VdbeIncrWriteCounter(p, pC);
- if( pOp->opcode==OP_Insert ){
- pKey = &aMem[pOp->p3];
- assert( pKey->flags & MEM_Int );
- assert( memIsValid(pKey) );
- REGISTER_TRACE(pOp->p3, pKey);
- x.nKey = pKey->u.i;
- }else{
- assert( pOp->opcode==OP_InsertInt );
- x.nKey = pOp->p3;
- }
+ pKey = &aMem[pOp->p3];
+ assert( pKey->flags & MEM_Int );
+ assert( memIsValid(pKey) );
+ REGISTER_TRACE(pOp->p3, pKey);
+ x.nKey = pKey->u.i;
if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
assert( pC->iDb>=0 );
@@ -87573,14 +98235,14 @@ case OP_InsertInt: {
assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) );
}else{
pTab = 0;
- zDb = 0; /* Not needed. Silence a compiler warning. */
+ zDb = 0;
}
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/* Invoke the pre-update hook, if any */
if( pTab ){
if( db->xPreUpdateCallback && !(pOp->p5 & OPFLAG_ISUPDATE) ){
- sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey,pOp->p2);
+ sqlite3VdbePreUpdateHook(p,pC,SQLITE_INSERT,zDb,pTab,x.nKey,pOp->p2,-1);
}
if( db->xUpdateCallback==0 || pTab->aCol==0 ){
/* Prevent post-update hook from running in cases when it should not */
@@ -87590,9 +98252,12 @@ case OP_InsertInt: {
if( pOp->p5 & OPFLAG_ISNOOP ) break;
#endif
- if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
- if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey;
- assert( pData->flags & (MEM_Blob|MEM_Str) );
+ assert( (pOp->p5 & OPFLAG_LASTROWID)==0 || (pOp->p5 & OPFLAG_NCHANGE)!=0 );
+ if( pOp->p5 & OPFLAG_NCHANGE ){
+ p->nChange++;
+ if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey;
+ }
+ assert( (pData->flags & (MEM_Blob|MEM_Str))!=0 || pData->n==0 );
x.pData = pData->z;
x.nData = pData->n;
seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
@@ -87602,11 +98267,14 @@ case OP_InsertInt: {
x.nZero = 0;
}
x.pKey = 0;
+ assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT );
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
- (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), seekResult
+ (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
+ seekResult
);
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
+ colCacheCtr++;
/* Invoke the update-hook if required. */
if( rc ) goto abort_due_to_error;
@@ -87620,6 +98288,33 @@ case OP_InsertInt: {
break;
}
+/* Opcode: RowCell P1 P2 P3 * *
+**
+** P1 and P2 are both open cursors. Both must be opened on the same type
+** of table - intkey or index. This opcode is used as part of copying
+** the current row from P2 into P1. If the cursors are opened on intkey
+** tables, register P3 contains the rowid to use with the new record in
+** P1. If they are opened on index tables, P3 is not used.
+**
+** This opcode must be followed by either an Insert or InsertIdx opcode
+** with the OPFLAG_PREFORMAT flag set to complete the insert operation.
+*/
+case OP_RowCell: {
+ VdbeCursor *pDest; /* Cursor to write to */
+ VdbeCursor *pSrc; /* Cursor to read from */
+ i64 iKey; /* Rowid value to insert with */
+ assert( pOp[1].opcode==OP_Insert || pOp[1].opcode==OP_IdxInsert );
+ assert( pOp[1].opcode==OP_Insert || pOp->p3==0 );
+ assert( pOp[1].opcode==OP_IdxInsert || pOp->p3>0 );
+ assert( pOp[1].p5 & OPFLAG_PREFORMAT );
+ pDest = p->apCsr[pOp->p1];
+ pSrc = p->apCsr[pOp->p2];
+ iKey = pOp->p3 ? aMem[pOp->p3].u.i : 0;
+ rc = sqlite3BtreeTransferRow(pDest->uc.pCursor, pSrc->uc.pCursor, iKey);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ break;
+};
+
/* Opcode: Delete P1 P2 P3 P4 P5
**
** Delete the record at which the P1 cursor is currently pointing.
@@ -87628,27 +98323,32 @@ case OP_InsertInt: {
** the cursor will be left pointing at either the next or the previous
** record in the table. If it is left pointing at the next record, then
** the next Next instruction will be a no-op. As a result, in this case
-** it is ok to delete a record from within a Next loop. If
+** it is ok to delete a record from within a Next loop. If
** OPFLAG_SAVEPOSITION bit of P5 is clear, then the cursor will be
** left in an undefined state.
**
** If the OPFLAG_AUXDELETE bit is set on P5, that indicates that this
-** delete one of several associated with deleting a table row and all its
-** associated index entries. Exactly one of those deletes is the "primary"
-** delete. The others are all on OPFLAG_FORDELETE cursors or else are
-** marked with the AUXDELETE flag.
+** delete is one of several associated with deleting a table row and
+** all its associated index entries. Exactly one of those deletes is
+** the "primary" delete. The others are all on OPFLAG_FORDELETE
+** cursors or else are marked with the AUXDELETE flag.
+**
+** If the OPFLAG_NCHANGE (0x01) flag of P2 (NB: P2 not P5) is set, then
+** the row change count is incremented (otherwise not).
**
-** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row
-** change count is incremented (otherwise not).
+** If the OPFLAG_ISNOOP (0x40) flag of P2 (not P5!) is set, then the
+** pre-update-hook for deletes is run, but the btree is otherwise unchanged.
+** This happens when the OP_Delete is to be shortly followed by an OP_Insert
+** with the same key, causing the btree entry to be overwritten.
**
** P1 must not be pseudo-table. It has to be a real table with
** multiple rows.
**
-** If P4 is not NULL then it points to a Table object. In this case either
+** If P4 is not NULL then it points to a Table object. In this case either
** the update or pre-update hook, or both, may be invoked. The P1 cursor must
-** have been positioned using OP_NotFound prior to invoking this opcode in
-** this case. Specifically, if one is configured, the pre-update hook is
-** invoked if P4 is not NULL. The update-hook is invoked if one is configured,
+** have been positioned using OP_NotFound prior to invoking this opcode in
+** this case. Specifically, if one is configured, the pre-update hook is
+** invoked if P4 is not NULL. The update-hook is invoked if one is configured,
** P4 is not NULL, and the OPFLAG_NCHANGE flag is set in P2.
**
** If the OPFLAG_ISUPDATE flag is set in P2, then P3 contains the address
@@ -87671,19 +98371,23 @@ case OP_Delete: {
sqlite3VdbeIncrWriteCounter(p, pC);
#ifdef SQLITE_DEBUG
- if( pOp->p4type==P4_TABLE && HasRowid(pOp->p4.pTab) && pOp->p5==0 ){
+ if( pOp->p4type==P4_TABLE
+ && HasRowid(pOp->p4.pTab)
+ && pOp->p5==0
+ && sqlite3BtreeCursorIsValidNN(pC->uc.pCursor)
+ ){
/* If p5 is zero, the seek operation that positioned the cursor prior to
** OP_Delete will have also set the pC->movetoTarget field to the rowid of
** the row that is being deleted */
i64 iKey = sqlite3BtreeIntegerKey(pC->uc.pCursor);
- assert( pC->movetoTarget==iKey );
+ assert( CORRUPT_DB || pC->movetoTarget==iKey );
}
#endif
/* If the update-hook or pre-update-hook will be invoked, set zDb to
** the name of the db to pass as to it. Also set local pTab to a copy
** of p4.pTab. Finally, if p5 is true, indicating that this cursor was
- ** last moved with OP_Next or OP_Prev, not Seek or NotFound, set
+ ** last moved with OP_Next or OP_Prev, not Seek or NotFound, set
** VdbeCursor.movetoTarget to the current rowid. */
if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
assert( pC->iDb>=0 );
@@ -87694,27 +98398,28 @@ case OP_Delete: {
pC->movetoTarget = sqlite3BtreeIntegerKey(pC->uc.pCursor);
}
}else{
- zDb = 0; /* Not needed. Silence a compiler warning. */
- pTab = 0; /* Not needed. Silence a compiler warning. */
+ zDb = 0;
+ pTab = 0;
}
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/* Invoke the pre-update-hook if required. */
- if( db->xPreUpdateCallback && pOp->p4.pTab ){
- assert( !(opflags & OPFLAG_ISUPDATE)
- || HasRowid(pTab)==0
- || (aMem[pOp->p3].flags & MEM_Int)
+ assert( db->xPreUpdateCallback==0 || pTab==pOp->p4.pTab );
+ if( db->xPreUpdateCallback && pTab ){
+ assert( !(opflags & OPFLAG_ISUPDATE)
+ || HasRowid(pTab)==0
+ || (aMem[pOp->p3].flags & MEM_Int)
);
sqlite3VdbePreUpdateHook(p, pC,
- (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE,
+ (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE,
zDb, pTab, pC->movetoTarget,
- pOp->p3
+ pOp->p3, -1
);
}
if( opflags & OPFLAG_ISNOOP ) break;
#endif
-
- /* Only flags that can be set are SAVEPOISTION and AUXDELETE */
+
+ /* Only flags that can be set are SAVEPOISTION and AUXDELETE */
assert( (pOp->p5 & ~(OPFLAG_SAVEPOSITION|OPFLAG_AUXDELETE))==0 );
assert( OPFLAG_SAVEPOSITION==BTREE_SAVEPOSITION );
assert( OPFLAG_AUXDELETE==BTREE_AUXDELETE );
@@ -87735,13 +98440,14 @@ case OP_Delete: {
rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5);
pC->cacheStatus = CACHE_STALE;
+ colCacheCtr++;
pC->seekResult = 0;
if( rc ) goto abort_due_to_error;
/* Invoke the update-hook if required. */
if( opflags & OPFLAG_NCHANGE ){
p->nChange++;
- if( db->xUpdateCallback && HasRowid(pTab) ){
+ if( db->xUpdateCallback && ALWAYS(pTab!=0) && HasRowid(pTab) ){
db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName,
pC->movetoTarget);
assert( pC->iDb>=0 );
@@ -87767,7 +98473,7 @@ case OP_ResetCount: {
** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2
**
** P1 is a sorter cursor. This instruction compares a prefix of the
-** record blob in register P3 against a prefix of the entry that
+** record blob in register P3 against a prefix of the entry that
** the sorter cursor currently points to. Only the first P4 fields
** of r[P3] and the sorter record are compared.
**
@@ -87802,13 +98508,13 @@ case OP_SorterCompare: {
** Write into register P2 the current sorter data for sorter cursor P1.
** Then clear the column header cache on cursor P3.
**
-** This opcode is normally use to move a record out of the sorter and into
+** This opcode is normally used to move a record out of the sorter and into
** a register that is the source for a pseudo-table cursor created using
** OpenPseudo. That pseudo-table cursor is the one that is identified by
** parameter P3. Clearing the P3 column cache as part of this opcode saves
** us from having to issue a separate NullRow instruction to clear that cache.
*/
-case OP_SorterData: {
+case OP_SorterData: { /* ncycle */
VdbeCursor *pC;
pOut = &aMem[pOp->p2];
@@ -87825,10 +98531,10 @@ case OP_SorterData: {
/* Opcode: RowData P1 P2 P3 * *
** Synopsis: r[P2]=data
**
-** Write into register P2 the complete row content for the row at
+** Write into register P2 the complete row content for the row at
** which cursor P1 is currently pointing.
-** There is no interpretation of the data.
-** It is just copied onto the P2 register exactly as
+** There is no interpretation of the data.
+** It is just copied onto the P2 register exactly as
** it is found in the database file.
**
** If cursor P1 is an index, then the content is the key of the row.
@@ -87876,17 +98582,13 @@ case OP_RowData: {
*/
assert( pC->deferredMoveto==0 );
assert( sqlite3BtreeCursorIsValid(pCrsr) );
-#if 0 /* Not required due to the previous to assert() statements */
- rc = sqlite3VdbeCursorMoveto(pC);
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
-#endif
n = sqlite3BtreePayloadSize(pCrsr);
if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
testcase( n==0 );
- rc = sqlite3VdbeMemFromBtree(pCrsr, 0, n, pOut);
+ rc = sqlite3VdbeMemFromBtreeZeroOffset(pCrsr, n, pOut);
if( rc ) goto abort_due_to_error;
if( !pOp->p3 ) Deephemeralize(pOut);
UPDATE_MAX_BLOBSIZE(pOut);
@@ -87895,7 +98597,7 @@ case OP_RowData: {
}
/* Opcode: Rowid P1 P2 * * *
-** Synopsis: r[P2]=rowid
+** Synopsis: r[P2]=PX rowid of P1
**
** Store in register P2 an integer which is the key of the table entry that
** P1 is currently point to.
@@ -87904,7 +98606,7 @@ case OP_RowData: {
** be a separate OP_VRowid opcode for use with virtual tables, but this
** one opcode now works for both table types.
*/
-case OP_Rowid: { /* out2 */
+case OP_Rowid: { /* out2, ncycle */
VdbeCursor *pC;
i64 v;
sqlite3_vtab *pVtab;
@@ -87950,13 +98652,25 @@ case OP_Rowid: { /* out2 */
** Move the cursor P1 to a null row. Any OP_Column operations
** that occur while the cursor is on the null row will always
** write a NULL.
+**
+** If cursor P1 is not previously opened, open it now to a special
+** pseudo-cursor that always returns NULL for every column.
*/
case OP_NullRow: {
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
- assert( pC!=0 );
+ if( pC==0 ){
+ /* If the cursor is not already open, create a special kind of
+ ** pseudo-cursor that always gives null rows. */
+ pC = allocateCursor(p, pOp->p1, 1, CURTYPE_PSEUDO);
+ if( pC==0 ) goto no_mem;
+ pC->seekResult = 0;
+ pC->isTable = 1;
+ pC->noReuse = 1;
+ pC->uc.pCursor = sqlite3BtreeFakeValidCursor();
+ }
pC->nullRow = 1;
pC->cacheStatus = CACHE_STALE;
if( pC->eCurType==CURTYPE_BTREE ){
@@ -87981,7 +98695,7 @@ case OP_NullRow: {
*/
/* Opcode: Last P1 P2 * * *
**
-** The next use of the Rowid or Column or Prev instruction for P1
+** The next use of the Rowid or Column or Prev instruction for P1
** will refer to the last entry in the database table or index.
** If the table or index is empty and P2>0, then jump immediately to P2.
** If P2 is 0 or if the table or index is not empty, fall through
@@ -87991,8 +98705,8 @@ case OP_NullRow: {
** from the end toward the beginning. In other words, the cursor is
** configured to use Prev, not Next.
*/
-case OP_SeekEnd:
-case OP_Last: { /* jump */
+case OP_SeekEnd: /* ncycle */
+case OP_Last: { /* jump, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -88075,37 +98789,40 @@ case OP_IfSmaller: { /* jump */
** regression tests can determine whether or not the optimizer is
** correctly optimizing out sorts.
*/
-case OP_SorterSort: /* jump */
-case OP_Sort: { /* jump */
+case OP_SorterSort: /* jump ncycle */
+case OP_Sort: { /* jump ncycle */
#ifdef SQLITE_TEST
sqlite3_sort_count++;
sqlite3_search_count--;
#endif
p->aCounter[SQLITE_STMTSTATUS_SORT]++;
/* Fall through into OP_Rewind */
+ /* no break */ deliberate_fall_through
}
-/* Opcode: Rewind P1 P2 * * P5
+/* Opcode: Rewind P1 P2 * * *
**
-** The next use of the Rowid or Column or Next instruction for P1
+** The next use of the Rowid or Column or Next instruction for P1
** will refer to the first entry in the database table or index.
** If the table or index is empty, jump immediately to P2.
-** If the table or index is not empty, fall through to the following
+** If the table or index is not empty, fall through to the following
** instruction.
**
-** If P5 is non-zero and the table is not empty, then the "skip-next"
-** flag is set on the cursor so that the next OP_Next instruction
-** executed on it is a no-op.
+** If P2 is zero, that is an assertion that the P1 table is never
+** empty and hence the jump will never be taken.
**
** This opcode leaves the cursor configured to move in forward order,
** from the beginning toward the end. In other words, the cursor is
** configured to use Next, not Prev.
*/
-case OP_Rewind: { /* jump */
+case OP_Rewind: { /* jump, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pOp->p5==0 );
+ assert( pOp->p2>=0 && pOp->p2<p->nOp );
+
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) );
@@ -88120,21 +98837,19 @@ case OP_Rewind: { /* jump */
pCrsr = pC->uc.pCursor;
assert( pCrsr );
rc = sqlite3BtreeFirst(pCrsr, &res);
-#ifndef SQLITE_OMIT_WINDOWFUNC
- if( pOp->p5 ) sqlite3BtreeSkipNext(pCrsr);
-#endif
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
}
if( rc ) goto abort_due_to_error;
pC->nullRow = (u8)res;
- assert( pOp->p2>0 && pOp->p2<p->nOp );
- VdbeBranchTaken(res!=0,2);
- if( res ) goto jump_to_p2;
+ if( pOp->p2>0 ){
+ VdbeBranchTaken(res!=0,2);
+ if( res ) goto jump_to_p2;
+ }
break;
}
-/* Opcode: Next P1 P2 P3 P4 P5
+/* Opcode: Next P1 P2 P3 * P5
**
** Advance cursor P1 so that it points to the next key/data pair in its
** table or index. If there are no more key/value pairs then fall through
@@ -88153,15 +98868,12 @@ case OP_Rewind: { /* jump */
** omitted if that index had been unique. P3 is usually 0. P3 is
** always either 0 or 1.
**
-** P4 is always of type P4_ADVANCE. The function pointer points to
-** sqlite3BtreeNext().
-**
** If P5 is positive and the jump is taken, then event counter
** number P5-1 in the prepared statement is incremented.
**
** See also: Prev
*/
-/* Opcode: Prev P1 P2 P3 P4 P5
+/* Opcode: Prev P1 P2 P3 * P5
**
** Back up cursor P1 so that it points to the previous key/data pair in its
** table or index. If there is no previous key/value pairs then fall through
@@ -88181,9 +98893,6 @@ case OP_Rewind: { /* jump */
** omitted if that index had been unique. P3 is usually 0. P3 is
** always either 0 or 1.
**
-** P4 is always of type P4_ADVANCE. The function pointer points to
-** sqlite3BtreePrevious().
-**
** If P5 is positive and the jump is taken, then event counter
** number P5-1 in the prepared statement is incremented.
*/
@@ -88201,29 +98910,37 @@ case OP_SorterNext: { /* jump */
assert( isSorter(pC) );
rc = sqlite3VdbeSorterNext(db, pC);
goto next_tail;
-case OP_Prev: /* jump */
-case OP_Next: /* jump */
+
+case OP_Prev: /* jump, ncycle */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- assert( pOp->p5<ArraySize(p->aCounter) );
+ assert( pOp->p5==0
+ || pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP
+ || pOp->p5==SQLITE_STMTSTATUS_AUTOINDEX);
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->deferredMoveto==0 );
assert( pC->eCurType==CURTYPE_BTREE );
- assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
- assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious );
-
- /* The Next opcode is only used after SeekGT, SeekGE, Rewind, and Found.
- ** The Prev opcode is only used after SeekLT, SeekLE, and Last. */
- assert( pOp->opcode!=OP_Next
- || pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE
- || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found
- || pC->seekOp==OP_NullRow);
- assert( pOp->opcode!=OP_Prev
- || pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE
- || pC->seekOp==OP_Last
+ assert( pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE
+ || pC->seekOp==OP_Last || pC->seekOp==OP_IfNoHope
|| pC->seekOp==OP_NullRow);
+ rc = sqlite3BtreePrevious(pC->uc.pCursor, pOp->p3);
+ goto next_tail;
+
+case OP_Next: /* jump, ncycle */
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pOp->p5==0
+ || pOp->p5==SQLITE_STMTSTATUS_FULLSCAN_STEP
+ || pOp->p5==SQLITE_STMTSTATUS_AUTOINDEX);
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->deferredMoveto==0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE
+ || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found
+ || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid
+ || pC->seekOp==OP_IfNoHope);
+ rc = sqlite3BtreeNext(pC->uc.pCursor, pOp->p3);
- rc = pOp->p4.xAdvance(pC->uc.pCursor, pOp->p3);
next_tail:
pC->cacheStatus = CACHE_STALE;
VdbeBranchTaken(rc==SQLITE_OK,2);
@@ -88264,11 +98981,41 @@ next_tail:
** run faster by avoiding an unnecessary seek on cursor P1. However,
** the OPFLAG_USESEEKRESULT flag must only be set if there have been no prior
** seeks on the cursor or if the most recent seek used a key equivalent
-** to P2.
+** to P2.
**
** This instruction only works for indices. The equivalent instruction
** for tables is OP_Insert.
*/
+case OP_IdxInsert: { /* in2 */
+ VdbeCursor *pC;
+ BtreePayload x;
+
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pC = p->apCsr[pOp->p1];
+ sqlite3VdbeIncrWriteCounter(p, pC);
+ assert( pC!=0 );
+ assert( !isSorter(pC) );
+ pIn2 = &aMem[pOp->p2];
+ assert( (pIn2->flags & MEM_Blob) || (pOp->p5 & OPFLAG_PREFORMAT) );
+ if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->isTable==0 );
+ rc = ExpandBlob(pIn2);
+ if( rc ) goto abort_due_to_error;
+ x.nKey = pIn2->n;
+ x.pKey = pIn2->z;
+ x.aMem = aMem + pOp->p3;
+ x.nMem = (u16)pOp->p4.i;
+ rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
+ (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
+ ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
+ );
+ assert( pC->deferredMoveto==0 );
+ pC->cacheStatus = CACHE_STALE;
+ if( rc) goto abort_due_to_error;
+ break;
+}
+
/* Opcode: SorterInsert P1 P2 * * *
** Synopsis: key=r[P2]
**
@@ -88276,47 +99023,38 @@ next_tail:
** MakeRecord instructions. This opcode writes that key
** into the sorter P1. Data for the entry is nil.
*/
-case OP_SorterInsert: /* in2 */
-case OP_IdxInsert: { /* in2 */
+case OP_SorterInsert: { /* in2 */
VdbeCursor *pC;
- BtreePayload x;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
sqlite3VdbeIncrWriteCounter(p, pC);
assert( pC!=0 );
- assert( isSorter(pC)==(pOp->opcode==OP_SorterInsert) );
+ assert( isSorter(pC) );
pIn2 = &aMem[pOp->p2];
assert( pIn2->flags & MEM_Blob );
- if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
- assert( pC->eCurType==CURTYPE_BTREE || pOp->opcode==OP_SorterInsert );
assert( pC->isTable==0 );
rc = ExpandBlob(pIn2);
if( rc ) goto abort_due_to_error;
- if( pOp->opcode==OP_SorterInsert ){
- rc = sqlite3VdbeSorterWrite(pC, pIn2);
- }else{
- x.nKey = pIn2->n;
- x.pKey = pIn2->z;
- x.aMem = aMem + pOp->p3;
- x.nMem = (u16)pOp->p4.i;
- rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
- (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)),
- ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
- );
- assert( pC->deferredMoveto==0 );
- pC->cacheStatus = CACHE_STALE;
- }
+ rc = sqlite3VdbeSorterWrite(pC, pIn2);
if( rc) goto abort_due_to_error;
break;
}
-/* Opcode: IdxDelete P1 P2 P3 * *
+/* Opcode: IdxDelete P1 P2 P3 * P5
** Synopsis: key=r[P2@P3]
**
** The content of P3 registers starting at register P2 form
-** an unpacked index key. This opcode removes that entry from the
+** an unpacked index key. This opcode removes that entry from the
** index opened by cursor P1.
+**
+** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error
+** if no matching index entry is found. This happens when running
+** an UPDATE or DELETE statement and the index entry to be updated
+** or deleted is not found. For some uses of IdxDelete
+** (example: the EXCEPT operator) it does not matter that no matching
+** entry is found. For those cases, P5 is zero. Also, do not raise
+** this (self-correcting and non-critical) error if in writable_schema mode.
*/
case OP_IdxDelete: {
VdbeCursor *pC;
@@ -88333,16 +99071,18 @@ case OP_IdxDelete: {
sqlite3VdbeIncrWriteCounter(p, pC);
pCrsr = pC->uc.pCursor;
assert( pCrsr!=0 );
- assert( pOp->p5==0 );
r.pKeyInfo = pC->pKeyInfo;
r.nField = (u16)pOp->p3;
r.default_rc = 0;
r.aMem = &aMem[pOp->p2];
- rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
+ rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res);
if( rc ) goto abort_due_to_error;
if( res==0 ){
rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
if( rc ) goto abort_due_to_error;
+ }else if( pOp->p5 && !sqlite3WritableSchema(db) ){
+ rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption");
+ goto abort_due_to_error;
}
assert( pC->deferredMoveto==0 );
pC->cacheStatus = CACHE_STALE;
@@ -88363,8 +99103,8 @@ case OP_IdxDelete: {
**
** P4 may be an array of integers (type P4_INTARRAY) containing
** one entry for each column in the P3 table. If array entry a(i)
-** is non-zero, then reading column a(i)-1 from cursor P3 is
-** equivalent to performing the deferred seek and then reading column i
+** is non-zero, then reading column a(i)-1 from cursor P3 is
+** equivalent to performing the deferred seek and then reading column i
** from P1. This information is stored in P3 and used to redirect
** reads against P3 over to P1, thus possibly avoiding the need to
** seek and read cursor P3.
@@ -88378,8 +99118,8 @@ case OP_IdxDelete: {
**
** See also: Rowid, MakeRecord.
*/
-case OP_DeferredSeek:
-case OP_IdxRowid: { /* out2 */
+case OP_DeferredSeek: /* ncycle */
+case OP_IdxRowid: { /* out2, ncycle */
VdbeCursor *pC; /* The P1 index cursor */
VdbeCursor *pTabCur; /* The P2 table cursor (OP_DeferredSeek only) */
i64 rowid; /* Rowid that P1 current points to */
@@ -88387,9 +99127,9 @@ case OP_IdxRowid: { /* out2 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->eCurType==CURTYPE_BTREE || IsNullCursor(pC) );
assert( pC->uc.pCursor!=0 );
- assert( pC->isTable==0 );
+ assert( pC->isTable==0 || IsNullCursor(pC) );
assert( pC->deferredMoveto==0 );
assert( !pC->nullRow || pOp->opcode==OP_IdxRowid );
@@ -88397,10 +99137,10 @@ case OP_IdxRowid: { /* out2 */
** of sqlite3VdbeCursorRestore() and sqlite3VdbeIdxRowid(). */
rc = sqlite3VdbeCursorRestore(pC);
- /* sqlite3VbeCursorRestore() can only fail if the record has been deleted
- ** out from under the cursor. That will never happens for an IdxRowid
- ** or Seek opcode */
- if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
+ /* sqlite3VdbeCursorRestore() may fail if the cursor has been disturbed
+ ** since it was last positioned and an error (e.g. OOM or an IO error)
+ ** occurs while trying to reposition it. */
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
if( !pC->nullRow ){
rowid = 0; /* Not needed. Only used to silence a warning. */
@@ -88418,8 +99158,11 @@ case OP_IdxRowid: { /* out2 */
pTabCur->nullRow = 0;
pTabCur->movetoTarget = rowid;
pTabCur->deferredMoveto = 1;
+ pTabCur->cacheStatus = CACHE_STALE;
assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 );
- pTabCur->aAltMap = pOp->p4.ai;
+ assert( !pTabCur->isEphemeral );
+ pTabCur->ub.aAltMap = pOp->p4.ai;
+ assert( !pC->isEphemeral );
pTabCur->pAltCursor = pC;
}else{
pOut = out2Prerelease(p, pOp);
@@ -88432,32 +99175,50 @@ case OP_IdxRowid: { /* out2 */
break;
}
-/* Opcode: IdxGE P1 P2 P3 P4 P5
+/* Opcode: FinishSeek P1 * * * *
+**
+** If cursor P1 was previously moved via OP_DeferredSeek, complete that
+** seek operation now, without further delay. If the cursor seek has
+** already occurred, this instruction is a no-op.
+*/
+case OP_FinishSeek: { /* ncycle */
+ VdbeCursor *pC; /* The P1 index cursor */
+
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pC = p->apCsr[pOp->p1];
+ if( pC->deferredMoveto ){
+ rc = sqlite3VdbeFinishMoveto(pC);
+ if( rc ) goto abort_due_to_error;
+ }
+ break;
+}
+
+/* Opcode: IdxGE P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
**
-** The P4 register values beginning with P3 form an unpacked index
-** key that omits the PRIMARY KEY. Compare this key value against the index
-** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID
+** The P4 register values beginning with P3 form an unpacked index
+** key that omits the PRIMARY KEY. Compare this key value against the index
+** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID
** fields at the end.
**
** If the P1 index entry is greater than or equal to the key value
** then jump to P2. Otherwise fall through to the next instruction.
*/
-/* Opcode: IdxGT P1 P2 P3 P4 P5
+/* Opcode: IdxGT P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
**
-** The P4 register values beginning with P3 form an unpacked index
-** key that omits the PRIMARY KEY. Compare this key value against the index
-** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID
+** The P4 register values beginning with P3 form an unpacked index
+** key that omits the PRIMARY KEY. Compare this key value against the index
+** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID
** fields at the end.
**
** If the P1 index entry is greater than the key value
** then jump to P2. Otherwise fall through to the next instruction.
*/
-/* Opcode: IdxLT P1 P2 P3 P4 P5
+/* Opcode: IdxLT P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
**
-** The P4 register values beginning with P3 form an unpacked index
+** The P4 register values beginning with P3 form an unpacked index
** key that omits the PRIMARY KEY or ROWID. Compare this key value against
** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or
** ROWID on the P1 index.
@@ -88465,10 +99226,10 @@ case OP_IdxRowid: { /* out2 */
** If the P1 index entry is less than the key value then jump to P2.
** Otherwise fall through to the next instruction.
*/
-/* Opcode: IdxLE P1 P2 P3 P4 P5
+/* Opcode: IdxLE P1 P2 P3 P4 *
** Synopsis: key=r[P3@P4]
**
-** The P4 register values beginning with P3 form an unpacked index
+** The P4 register values beginning with P3 form an unpacked index
** key that omits the PRIMARY KEY or ROWID. Compare this key value against
** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or
** ROWID on the P1 index.
@@ -88476,10 +99237,10 @@ case OP_IdxRowid: { /* out2 */
** If the P1 index entry is less than or equal to the key value then jump
** to P2. Otherwise fall through to the next instruction.
*/
-case OP_IdxLE: /* jump */
-case OP_IdxGT: /* jump */
-case OP_IdxLT: /* jump */
-case OP_IdxGE: { /* jump */
+case OP_IdxLE: /* jump, ncycle */
+case OP_IdxGT: /* jump, ncycle */
+case OP_IdxLT: /* jump, ncycle */
+case OP_IdxGE: { /* jump, ncycle */
VdbeCursor *pC;
int res;
UnpackedRecord r;
@@ -88491,7 +99252,6 @@ case OP_IdxGE: { /* jump */
assert( pC->eCurType==CURTYPE_BTREE );
assert( pC->uc.pCursor!=0);
assert( pC->deferredMoveto==0 );
- assert( pOp->p5==0 || pOp->p5==1 );
assert( pOp->p4type==P4_INT32 );
r.pKeyInfo = pC->pKeyInfo;
r.nField = (u16)pOp->p4.i;
@@ -88512,8 +99272,31 @@ case OP_IdxGE: { /* jump */
}
}
#endif
- res = 0; /* Not needed. Only used to silence a warning. */
- rc = sqlite3VdbeIdxKeyCompare(db, pC, &r, &res);
+
+ /* Inlined version of sqlite3VdbeIdxKeyCompare() */
+ {
+ i64 nCellKey = 0;
+ BtCursor *pCur;
+ Mem m;
+
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCur = pC->uc.pCursor;
+ assert( sqlite3BtreeCursorIsValid(pCur) );
+ nCellKey = sqlite3BtreePayloadSize(pCur);
+ /* nCellKey will always be between 0 and 0xffffffff because of the way
+ ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */
+ if( nCellKey<=0 || nCellKey>0x7fffffff ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto abort_due_to_error;
+ }
+ sqlite3VdbeMemInit(&m, db, 0);
+ rc = sqlite3VdbeMemFromBtreeZeroOffset(pCur, (u32)nCellKey, &m);
+ if( rc ) goto abort_due_to_error;
+ res = sqlite3VdbeRecordCompareWithSkip(m.n, m.z, &r, 0);
+ sqlite3VdbeMemReleaseMalloc(&m);
+ }
+ /* End of inlined sqlite3VdbeIdxKeyCompare() */
+
assert( (OP_IdxLE&1)==(OP_IdxLT&1) && (OP_IdxGE&1)==(OP_IdxGT&1) );
if( (pOp->opcode&1)==(OP_IdxLT&1) ){
assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxLT );
@@ -88523,7 +99306,7 @@ case OP_IdxGE: { /* jump */
res++;
}
VdbeBranchTaken(res>0,2);
- if( rc ) goto abort_due_to_error;
+ assert( rc==SQLITE_OK );
if( res>0 ) goto jump_to_p2;
break;
}
@@ -88534,7 +99317,7 @@ case OP_IdxGE: { /* jump */
** file is given by P1.
**
** The table being destroyed is in the main database file if P3==0. If
-** P3==1 then the table to be clear is in the auxiliary database file
+** P3==1 then the table to be destroyed is in the auxiliary database file
** that is used to store tables create using CREATE TEMPORARY TABLE.
**
** If AUTOVACUUM is enabled then it is possible that another root page
@@ -88542,15 +99325,15 @@ case OP_IdxGE: { /* jump */
** root pages contiguous at the beginning of the database. The former
** value of the root page that moved - its value before the move occurred -
** is stored in register P2. If no page movement was required (because the
-** table being dropped was already the last one in the database) then a
-** zero is stored in register P2. If AUTOVACUUM is disabled then a zero
+** table being dropped was already the last one in the database) then a
+** zero is stored in register P2. If AUTOVACUUM is disabled then a zero
** is stored in register P2.
**
** This opcode throws an error if there are any active reader VMs when
-** it is invoked. This is done to avoid the difficulty associated with
-** updating existing cursors when a root page is moved in an AUTOVACUUM
-** database. This error is thrown even if the database is not an AUTOVACUUM
-** db in order to avoid introducing an incompatibility between autovacuum
+** it is invoked. This is done to avoid the difficulty associated with
+** updating existing cursors when a root page is moved in an AUTOVACUUM
+** database. This error is thrown even if the database is not an AUTOVACUUM
+** db in order to avoid introducing an incompatibility between autovacuum
** and non-autovacuum modes.
**
** See also: Clear
@@ -88594,28 +99377,25 @@ case OP_Destroy: { /* out2 */
** in the database file is given by P1. But, unlike Destroy, do not
** remove the table or index from the database file.
**
-** The table being clear is in the main database file if P2==0. If
-** P2==1 then the table to be clear is in the auxiliary database file
+** The table being cleared is in the main database file if P2==0. If
+** P2==1 then the table to be cleared is in the auxiliary database file
** that is used to store tables create using CREATE TEMPORARY TABLE.
**
-** If the P3 value is non-zero, then the table referred to must be an
-** intkey table (an SQL table, not an index). In this case the row change
-** count is incremented by the number of rows in the table being cleared.
-** If P3 is greater than zero, then the value stored in register P3 is
-** also incremented by the number of rows in the table being cleared.
+** If the P3 value is non-zero, then the row change count is incremented
+** by the number of rows in the table being cleared. If P3 is greater
+** than zero, then the value stored in register P3 is also incremented
+** by the number of rows in the table being cleared.
**
** See also: Destroy
*/
case OP_Clear: {
- int nChange;
-
+ i64 nChange;
+
sqlite3VdbeIncrWriteCounter(p, 0);
nChange = 0;
assert( p->readOnly==0 );
assert( DbMaskTest(p->btreeMask, pOp->p2) );
- rc = sqlite3BtreeClearTable(
- db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0)
- );
+ rc = sqlite3BtreeClearTable(db->aDb[pOp->p2].pBt, (u32)pOp->p1, &nChange);
if( pOp->p3 ){
p->nChange += nChange;
if( pOp->p3>0 ){
@@ -88638,7 +99418,7 @@ case OP_Clear: {
*/
case OP_ResetSorter: {
VdbeCursor *pC;
-
+
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
@@ -88663,7 +99443,7 @@ case OP_ResetSorter: {
** The root page number of the new b-tree is stored in register P2.
*/
case OP_CreateBtree: { /* out2 */
- int pgno;
+ Pgno pgno;
Db *pDb;
sqlite3VdbeIncrWriteCounter(p, 0);
@@ -88684,19 +99464,47 @@ case OP_CreateBtree: { /* out2 */
/* Opcode: SqlExec * * * P4 *
**
** Run the SQL statement or statements specified in the P4 string.
+** Disable Auth and Trace callbacks while those statements are running if
+** P1 is true.
*/
case OP_SqlExec: {
+ char *zErr;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth;
+#endif
+ u8 mTrace;
+
sqlite3VdbeIncrWriteCounter(p, 0);
db->nSqlExec++;
- rc = sqlite3_exec(db, pOp->p4.z, 0, 0, 0);
+ zErr = 0;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ xAuth = db->xAuth;
+#endif
+ mTrace = db->mTrace;
+ if( pOp->p1 ){
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = 0;
+#endif
+ db->mTrace = 0;
+ }
+ rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr);
db->nSqlExec--;
- if( rc ) goto abort_due_to_error;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+#endif
+ db->mTrace = mTrace;
+ if( zErr || rc ){
+ sqlite3VdbeError(p, "%s", zErr);
+ sqlite3_free(zErr);
+ if( rc==SQLITE_NOMEM ) goto no_mem;
+ goto abort_due_to_error;
+ }
break;
}
/* Opcode: ParseSchema P1 * * P4 *
**
-** Read and parse all entries from the SQLITE_MASTER table of database P1
+** Read and parse all entries from the schema table of database P1
** that match the WHERE clause P4. If P4 is a NULL pointer, then the
** entire schema for P1 is reparsed.
**
@@ -88705,12 +99513,12 @@ case OP_SqlExec: {
*/
case OP_ParseSchema: {
int iDb;
- const char *zMaster;
+ const char *zSchema;
char *zSql;
InitData initData;
/* Any prepared statement that invokes this opcode will hold mutexes
- ** on every btree. This is a prerequisite for invoking
+ ** on every btree. This is a prerequisite for invoking
** sqlite3InitCallback().
*/
#ifdef SQLITE_DEBUG
@@ -88721,35 +99529,45 @@ case OP_ParseSchema: {
iDb = pOp->p1;
assert( iDb>=0 && iDb<db->nDb );
- assert( DbHasProperty(db, iDb, DB_SchemaLoaded) );
+ assert( DbHasProperty(db, iDb, DB_SchemaLoaded)
+ || db->mallocFailed
+ || (CORRUPT_DB && (db->flags & SQLITE_NoSchemaError)!=0) );
#ifndef SQLITE_OMIT_ALTERTABLE
if( pOp->p4.z==0 ){
sqlite3SchemaClear(db->aDb[iDb].pSchema);
db->mDbFlags &= ~DBFLAG_SchemaKnownOk;
- rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable);
+ rc = sqlite3InitOne(db, iDb, &p->zErrMsg, pOp->p5);
db->mDbFlags |= DBFLAG_SchemaChange;
p->expired = 0;
}else
#endif
{
- zMaster = MASTER_NAME;
+ zSchema = LEGACY_SCHEMA_TABLE;
initData.db = db;
initData.iDb = iDb;
initData.pzErrMsg = &p->zErrMsg;
initData.mInitFlags = 0;
+ initData.mxPage = sqlite3BtreeLastPage(db->aDb[iDb].pBt);
zSql = sqlite3MPrintf(db,
- "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid",
- db->aDb[iDb].zDbSName, zMaster, pOp->p4.z);
+ "SELECT*FROM\"%w\".%s WHERE %s ORDER BY rowid",
+ db->aDb[iDb].zDbSName, zSchema, pOp->p4.z);
if( zSql==0 ){
rc = SQLITE_NOMEM_BKPT;
}else{
assert( db->init.busy==0 );
db->init.busy = 1;
initData.rc = SQLITE_OK;
+ initData.nInitRow = 0;
assert( !db->mallocFailed );
rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
if( rc==SQLITE_OK ) rc = initData.rc;
+ if( rc==SQLITE_OK && initData.nInitRow==0 ){
+ /* The OP_ParseSchema opcode with a non-NULL P4 argument should parse
+ ** at least one SQL statement. Any less than that indicates that
+ ** the sqlite_schema table is corrupt. */
+ rc = SQLITE_CORRUPT_BKPT;
+ }
sqlite3DbFreeNN(db, zSql);
db->init.busy = 0;
}
@@ -88761,7 +99579,7 @@ case OP_ParseSchema: {
}
goto abort_due_to_error;
}
- break;
+ break;
}
#if !defined(SQLITE_OMIT_ANALYZE)
@@ -88775,7 +99593,7 @@ case OP_LoadAnalysis: {
assert( pOp->p1>=0 && pOp->p1<db->nDb );
rc = sqlite3AnalysisLoad(db, pOp->p1);
if( rc ) goto abort_due_to_error;
- break;
+ break;
}
#endif /* !defined(SQLITE_OMIT_ANALYZE) */
@@ -88783,7 +99601,7 @@ case OP_LoadAnalysis: {
**
** Remove the internal (in-memory) data structures that describe
** the table named P4 in database P1. This is called after a table
-** is dropped from disk (using the Destroy opcode) in order to keep
+** is dropped from disk (using the Destroy opcode) in order to keep
** the internal representation of the
** schema consistent with what is on disk.
*/
@@ -88811,7 +99629,7 @@ case OP_DropIndex: {
**
** Remove the internal (in-memory) data structures that describe
** the trigger named P4 in database P1. This is called after a trigger
-** is dropped from disk (using the Destroy opcode) in order to keep
+** is dropped from disk (using the Destroy opcode) in order to keep
** the internal representation of the
** schema consistent with what is on disk.
*/
@@ -88831,7 +99649,7 @@ case OP_DropTrigger: {
**
** The register P3 contains one less than the maximum number of allowed errors.
** At most reg(P3) errors will be reported.
-** In other words, the analysis stops as soon as reg(P1) errors are
+** In other words, the analysis stops as soon as reg(P1) errors are
** seen. Reg(P1) is updated with the number of errors remaining.
**
** The root page numbers of all tables in the database are integers
@@ -88844,7 +99662,7 @@ case OP_DropTrigger: {
*/
case OP_IntegrityCk: {
int nRoot; /* Number of tables to check. (Number of root pages.) */
- int *aRoot; /* Array of rootpage numbers for tables to be checked */
+ Pgno *aRoot; /* Array of rootpage numbers for tables to be checked */
int nErr; /* Number of errors reported */
char *z; /* Text of the error report */
Mem *pnErr; /* Register keeping track of errors remaining */
@@ -88853,7 +99671,7 @@ case OP_IntegrityCk: {
nRoot = pOp->p2;
aRoot = pOp->p4.ai;
assert( nRoot>0 );
- assert( aRoot[0]==nRoot );
+ assert( aRoot[0]==(Pgno)nRoot );
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
pnErr = &aMem[pOp->p3];
assert( (pnErr->flags & MEM_Int)!=0 );
@@ -88861,20 +99679,21 @@ case OP_IntegrityCk: {
pIn1 = &aMem[pOp->p1];
assert( pOp->p5<db->nDb );
assert( DbMaskTest(p->btreeMask, pOp->p5) );
- z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, &aRoot[1], nRoot,
- (int)pnErr->u.i+1, &nErr);
+ rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot,
+ (int)pnErr->u.i+1, &nErr, &z);
sqlite3VdbeMemSetNull(pIn1);
if( nErr==0 ){
assert( z==0 );
- }else if( z==0 ){
- goto no_mem;
+ }else if( rc ){
+ sqlite3_free(z);
+ goto abort_due_to_error;
}else{
pnErr->u.i -= nErr-1;
sqlite3VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, sqlite3_free);
}
UPDATE_MAX_BLOBSIZE(pIn1);
sqlite3VdbeChangeEncoding(pIn1, encoding);
- break;
+ goto check_for_interrupt;
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -88911,7 +99730,7 @@ case OP_RowSetRead: { /* jump, in1, out3 */
pIn1 = &aMem[pOp->p1];
assert( (pIn1->flags & MEM_Blob)==0 || sqlite3VdbeMemIsRowSet(pIn1) );
- if( (pIn1->flags & MEM_Blob)==0
+ if( (pIn1->flags & MEM_Blob)==0
|| sqlite3RowSetNext((RowSet*)pIn1->z, &val)==0
){
/* The boolean index is empty */
@@ -88983,13 +99802,13 @@ case OP_RowSetTest: { /* jump, in1, in3 */
/* Opcode: Program P1 P2 P3 P4 P5
**
-** Execute the trigger program passed as P4 (type P4_SUBPROGRAM).
+** Execute the trigger program passed as P4 (type P4_SUBPROGRAM).
**
-** P1 contains the address of the memory cell that contains the first memory
-** cell in an array of values used as arguments to the sub-program. P2
-** contains the address to jump to if the sub-program throws an IGNORE
-** exception using the RAISE() function. Register P3 contains the address
-** of a memory cell in this (the parent) VM that is used to allocate the
+** P1 contains the address of the memory cell that contains the first memory
+** cell in an array of values used as arguments to the sub-program. P2
+** contains the address to jump to if the sub-program throws an IGNORE
+** exception using the RAISE() function. Register P3 contains the address
+** of a memory cell in this (the parent) VM that is used to allocate the
** memory required by the sub-vdbe at runtime.
**
** P4 is a pointer to the VM containing the trigger program.
@@ -89009,17 +99828,17 @@ case OP_Program: { /* jump */
pProgram = pOp->p4.pProgram;
pRt = &aMem[pOp->p3];
assert( pProgram->nOp>0 );
-
- /* If the p5 flag is clear, then recursive invocation of triggers is
+
+ /* If the p5 flag is clear, then recursive invocation of triggers is
** disabled for backwards compatibility (p5 is set if this sub-program
** is really a trigger, not a foreign key action, and the flag set
** and cleared by the "PRAGMA recursive_triggers" command is clear).
- **
- ** It is recursive invocation of triggers, at the SQL level, that is
- ** disabled. In some cases a single trigger may generate more than one
- ** SubProgram (if the trigger may be executed with more than one different
+ **
+ ** It is recursive invocation of triggers, at the SQL level, that is
+ ** disabled. In some cases a single trigger may generate more than one
+ ** SubProgram (if the trigger may be executed with more than one different
** ON CONFLICT algorithm). SubProgram structures associated with a
- ** single trigger all have the same value for the SubProgram.token
+ ** single trigger all have the same value for the SubProgram.token
** variable. */
if( pOp->p5 ){
t = pProgram->token;
@@ -89035,10 +99854,10 @@ case OP_Program: { /* jump */
/* Register pRt is used to store the memory required to save the state
** of the current program, and the memory required at runtime to execute
- ** the trigger program. If this trigger has been fired before, then pRt
+ ** the trigger program. If this trigger has been fired before, then pRt
** is already allocated. Otherwise, it must be initialized. */
if( (pRt->flags&MEM_Blob)==0 ){
- /* SubProgram.nMem is set to the number of memory cells used by the
+ /* SubProgram.nMem is set to the number of memory cells used by the
** program stored in SubProgram.aOp. As well as these, one memory
** cell is required for each cursor used by the program. Set local
** variable nMem (and later, VdbeFrame.nChildMem) to this value.
@@ -89071,9 +99890,6 @@ case OP_Program: { /* jump */
pFrame->aOp = p->aOp;
pFrame->nOp = p->nOp;
pFrame->token = pProgram->token;
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- pFrame->anExec = p->anExec;
-#endif
#ifdef SQLITE_DEBUG
pFrame->iFrameMagic = SQLITE_FRAME_MAGIC;
#endif
@@ -89086,7 +99902,7 @@ case OP_Program: { /* jump */
}else{
pFrame = (VdbeFrame*)pRt->z;
assert( pRt->xDel==sqlite3VdbeFrameMemDel );
- assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem
+ assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem
|| (pProgram->nCsr==0 && pProgram->nMem+1==pFrame->nChildMem) );
assert( pProgram->nCsr==pFrame->nChildCsr );
assert( (int)(pOp - aOp)==pFrame->pc );
@@ -89110,20 +99926,27 @@ case OP_Program: { /* jump */
memset(pFrame->aOnce, 0, (pProgram->nOp + 7)/8);
p->aOp = aOp = pProgram->aOp;
p->nOp = pProgram->nOp;
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
- p->anExec = 0;
+#ifdef SQLITE_DEBUG
+ /* Verify that second and subsequent executions of the same trigger do not
+ ** try to reuse register values from the first use. */
+ {
+ int i;
+ for(i=0; i<p->nMem; i++){
+ aMem[i].pScopyFrom = 0; /* Prevent false-positive AboutToChange() errs */
+ MemSetTypeFlag(&aMem[i], MEM_Undefined); /* Fault if this reg is reused */
+ }
+ }
#endif
pOp = &aOp[-1];
-
- break;
+ goto check_for_interrupt;
}
/* Opcode: Param P1 P2 * * *
**
-** This opcode is only ever present in sub-programs called via the
-** OP_Program instruction. Copy a value currently stored in a memory
-** cell of the calling (parent) frame to cell P2 in the current frames
-** address space. This is used by trigger programs to access the new.*
+** This opcode is only ever present in sub-programs called via the
+** OP_Program instruction. Copy a value currently stored in a memory
+** cell of the calling (parent) frame to cell P2 in the current frames
+** address space. This is used by trigger programs to access the new.*
** and old.* values.
**
** The address of the cell in the parent frame is determined by adding
@@ -89135,7 +99958,7 @@ case OP_Param: { /* out2 */
Mem *pIn;
pOut = out2Prerelease(p, pOp);
pFrame = p->pFrame;
- pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1];
+ pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1];
sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem);
break;
}
@@ -89147,8 +99970,8 @@ case OP_Param: { /* out2 */
** Synopsis: fkctr[P1]+=P2
**
** Increment a "constraint counter" by P2 (P2 may be negative or positive).
-** If P1 is non-zero, the database constraint counter is incremented
-** (deferred foreign key constraints). Otherwise, if P1 is zero, the
+** If P1 is non-zero, the database constraint counter is incremented
+** (deferred foreign key constraints). Otherwise, if P1 is zero, the
** statement counter is incremented (immediate foreign key constraints).
*/
case OP_FkCounter: {
@@ -89166,7 +99989,7 @@ case OP_FkCounter: {
** Synopsis: if fkctr[P1]==0 goto P2
**
** This opcode tests if a foreign key constraint-counter is currently zero.
-** If so, jump to instruction P2. Otherwise, fall through to the next
+** If so, jump to instruction P2. Otherwise, fall through to the next
** instruction.
**
** If P1 is non-zero, then the jump is taken if the database constraint-counter
@@ -89192,7 +100015,7 @@ case OP_FkIfZero: { /* jump */
**
** P1 is a register in the root frame of this VM (the root frame is
** different from the current frame if this instruction is being executed
-** within a sub-program). Set the value of register P1 to the maximum of
+** within a sub-program). Set the value of register P1 to the maximum of
** its current value and the value in register P2.
**
** This instruction throws an error if the memory cell is not initially
@@ -89242,7 +100065,7 @@ case OP_IfPos: { /* jump, in1 */
** Synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)
**
** This opcode performs a commonly used computation associated with
-** LIMIT and OFFSET process. r[P1] holds the limit counter. r[P3]
+** LIMIT and OFFSET processing. r[P1] holds the limit counter. r[P3]
** holds the offset counter. The opcode computes the combined value
** of the LIMIT and OFFSET and stores that value in r[P2]. The r[P2]
** value computed is the total number of rows that will need to be
@@ -89252,7 +100075,7 @@ case OP_IfPos: { /* jump, in1 */
** and r[P2] is set to be the value of the LIMIT, r[P1].
**
** if r[P1] is zero or negative, that means there is no LIMIT
-** and r[P2] is set to -1.
+** and r[P2] is set to -1.
**
** Otherwise, r[P2] is set to the sum of r[P1] and r[P3].
*/
@@ -89284,7 +100107,7 @@ case OP_OffsetLimit: { /* in1, out2, in3 */
**
** Register P1 must contain an integer. If the content of register P1 is
** initially greater than zero, then decrement the value in register P1.
-** If it is non-zero (negative or positive) and then also jump to P2.
+** If it is non-zero (negative or positive) and then also jump to P2.
** If register P1 is initially zero, leave it unchanged and fall through.
*/
case OP_IfNotZero: { /* jump, in1 */
@@ -89318,7 +100141,7 @@ case OP_DecrJumpZero: { /* jump, in1 */
** Synopsis: accum=r[P3] step(r[P2@P5])
**
** Execute the xStep function for an aggregate.
-** The function has P5 arguments. P4 is a pointer to the
+** The function has P5 arguments. P4 is a pointer to the
** FuncDef structure that specifies the function. Register P3 is the
** accumulator.
**
@@ -89329,7 +100152,7 @@ case OP_DecrJumpZero: { /* jump, in1 */
** Synopsis: accum=r[P3] inverse(r[P2@P5])
**
** Execute the xInverse function for an aggregate.
-** The function has P5 arguments. P4 is a pointer to the
+** The function has P5 arguments. P4 is a pointer to the
** FuncDef structure that specifies the function. Register P3 is the
** accumulator.
**
@@ -89340,7 +100163,7 @@ case OP_DecrJumpZero: { /* jump, in1 */
** Synopsis: accum=r[P3] step(r[P2@P5])
**
** Execute the xStep (if P1==0) or xInverse (if P1!=0) function for an
-** aggregate. The function has P5 arguments. P4 is a pointer to the
+** aggregate. The function has P5 arguments. P4 is a pointer to the
** FuncDef structure that specifies the function. Register P3 is the
** accumulator.
**
@@ -89374,6 +100197,7 @@ case OP_AggStep: {
pCtx->pVdbe = p;
pCtx->skipFlag = 0;
pCtx->isError = 0;
+ pCtx->enc = encoding;
pCtx->argc = n;
pOp->p4type = P4_FUNCCTX;
pOp->p4.pCtx = pCtx;
@@ -89383,6 +100207,7 @@ case OP_AggStep: {
pOp->opcode = OP_AggStep1;
/* Fall through into OP_AggStep */
+ /* no break */ deliberate_fall_through
}
case OP_AggStep1: {
int i;
@@ -89407,7 +100232,7 @@ case OP_AggStep1: {
/* If this function is inside of a trigger, the register array in aMem[]
** might change from one evaluation to the next. The next block of code
** checks to see if the register array has changed, and if so it
- ** reinitializes the relavant parts of the sqlite3_context object */
+ ** reinitializes the relevant parts of the sqlite3_context object */
if( pCtx->pMem != pMem ){
pCtx->pMem = pMem;
for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i];
@@ -89456,7 +100281,7 @@ case OP_AggStep1: {
** Synopsis: accum=r[P1] N=P2
**
** P1 is the memory location that is the accumulator for an aggregate
-** or window function. Execute the finalizer function
+** or window function. Execute the finalizer function
** for an aggregate and store the result in P1.
**
** P2 is the number of arguments that the step function takes and
@@ -89487,6 +100312,7 @@ case OP_AggFinal: {
assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pOp->p3 ){
+ memAboutToChange(p, &aMem[pOp->p3]);
rc = sqlite3VdbeMemAggValue(pMem, &aMem[pOp->p3], pOp->p4.pFunc);
pMem = &aMem[pOp->p3];
}else
@@ -89494,16 +100320,14 @@ case OP_AggFinal: {
{
rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
}
-
+
if( rc ){
sqlite3VdbeError(p, "%s", sqlite3_value_text(pMem));
goto abort_due_to_error;
}
sqlite3VdbeChangeEncoding(pMem, encoding);
UPDATE_MAX_BLOBSIZE(pMem);
- if( sqlite3VdbeMemTooBig(pMem) ){
- goto too_big;
- }
+ REGISTER_TRACE((int)(pMem-aMem), pMem);
break;
}
@@ -89540,9 +100364,9 @@ case OP_Checkpoint: {
}
for(i=0, pMem = &aMem[pOp->p3]; i<3; i++, pMem++){
sqlite3VdbeMemSetInt64(pMem, (i64)aRes[i]);
- }
+ }
break;
-};
+};
#endif
#ifndef SQLITE_OMIT_PRAGMA
@@ -89568,9 +100392,9 @@ case OP_JournalMode: { /* out2 */
pOut = out2Prerelease(p, pOp);
eNew = pOp->p3;
- assert( eNew==PAGER_JOURNALMODE_DELETE
- || eNew==PAGER_JOURNALMODE_TRUNCATE
- || eNew==PAGER_JOURNALMODE_PERSIST
+ assert( eNew==PAGER_JOURNALMODE_DELETE
+ || eNew==PAGER_JOURNALMODE_TRUNCATE
+ || eNew==PAGER_JOURNALMODE_PERSIST
|| eNew==PAGER_JOURNALMODE_OFF
|| eNew==PAGER_JOURNALMODE_MEMORY
|| eNew==PAGER_JOURNALMODE_WAL
@@ -89583,13 +100407,14 @@ case OP_JournalMode: { /* out2 */
pPager = sqlite3BtreePager(pBt);
eOld = sqlite3PagerGetJournalMode(pPager);
if( eNew==PAGER_JOURNALMODE_QUERY ) eNew = eOld;
+ assert( sqlite3BtreeHoldsMutex(pBt) );
if( !sqlite3PagerOkToChangeJournalMode(pPager) ) eNew = eOld;
#ifndef SQLITE_OMIT_WAL
zFilename = sqlite3PagerFilename(pPager, 1);
/* Do not allow a transition to journal_mode=WAL for a database
- ** in temporary storage or if the VFS does not support shared memory
+ ** in temporary storage or if the VFS does not support shared memory
*/
if( eNew==PAGER_JOURNALMODE_WAL
&& (sqlite3Strlen30(zFilename)==0 /* Temp file */
@@ -89609,12 +100434,12 @@ case OP_JournalMode: { /* out2 */
);
goto abort_due_to_error;
}else{
-
+
if( eOld==PAGER_JOURNALMODE_WAL ){
/* If leaving WAL mode, close the log file. If successful, the call
- ** to PagerCloseWal() checkpoints and deletes the write-ahead-log
- ** file. An EXCLUSIVE lock may still be held on the database file
- ** after a successful return.
+ ** to PagerCloseWal() checkpoints and deletes the write-ahead-log
+ ** file. An EXCLUSIVE lock may still be held on the database file
+ ** after a successful return.
*/
rc = sqlite3PagerCloseWal(pPager, db);
if( rc==SQLITE_OK ){
@@ -89625,11 +100450,11 @@ case OP_JournalMode: { /* out2 */
** as an intermediate */
sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_OFF);
}
-
+
/* Open a transaction on the database file. Regardless of the journal
** mode, this transaction always uses a rollback journal.
*/
- assert( sqlite3BtreeIsInTrans(pBt)==0 );
+ assert( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE );
if( rc==SQLITE_OK ){
rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
}
@@ -89651,14 +100476,19 @@ case OP_JournalMode: { /* out2 */
#endif /* SQLITE_OMIT_PRAGMA */
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
-/* Opcode: Vacuum P1 * * * *
+/* Opcode: Vacuum P1 P2 * * *
**
** Vacuum the entire database P1. P1 is 0 for "main", and 2 or more
** for an attached database. The "temp" database may not be vacuumed.
+**
+** If P2 is not zero, then it is a register holding a string which is
+** the file into which the result of vacuum should be written. When
+** P2 is zero, the vacuum overwrites the original database.
*/
case OP_Vacuum: {
assert( p->readOnly==0 );
- rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1);
+ rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1,
+ pOp->p2 ? &aMem[pOp->p2] : 0);
if( rc ) goto abort_due_to_error;
break;
}
@@ -89695,7 +100525,7 @@ case OP_IncrVacuum: { /* jump */
** is executed using sqlite3_step() it will either automatically
** reprepare itself (if it was originally created using sqlite3_prepare_v2())
** or it will fail with SQLITE_SCHEMA.
-**
+**
** If P1 is 0, then all SQL statements become expired. If P1 is non-zero,
** then only the currently executing statement is expired.
**
@@ -89715,12 +100545,42 @@ case OP_Expire: {
break;
}
+/* Opcode: CursorLock P1 * * * *
+**
+** Lock the btree to which cursor P1 is pointing so that the btree cannot be
+** written by an other cursor.
+*/
+case OP_CursorLock: {
+ VdbeCursor *pC;
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ sqlite3BtreeCursorPin(pC->uc.pCursor);
+ break;
+}
+
+/* Opcode: CursorUnlock P1 * * * *
+**
+** Unlock the btree to which cursor P1 is pointing so that it can be
+** written by other cursors.
+*/
+case OP_CursorUnlock: {
+ VdbeCursor *pC;
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ sqlite3BtreeCursorUnpin(pC->uc.pCursor);
+ break;
+}
+
#ifndef SQLITE_OMIT_SHARED_CACHE
/* Opcode: TableLock P1 P2 P3 P4 *
** Synopsis: iDb=P1 root=P2 write=P3
**
** Obtain a lock on a particular table. This instruction is only used when
-** the shared-cache feature is enabled.
+** the shared-cache feature is enabled.
**
** P1 is the index of the database in sqlite3.aDb[] of the database
** on which the lock is acquired. A readlock is obtained if P3==0 or
@@ -89734,7 +100594,7 @@ case OP_Expire: {
case OP_TableLock: {
u8 isWriteLock = (u8)pOp->p3;
if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommit) ){
- int p1 = pOp->p1;
+ int p1 = pOp->p1;
assert( p1>=0 && p1<db->nDb );
assert( DbMaskTest(p->btreeMask, p1) );
assert( isWriteLock==0 || isWriteLock==1 );
@@ -89754,7 +100614,7 @@ case OP_TableLock: {
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VBegin * * * P4 *
**
-** P4 may be a pointer to an sqlite3_vtab structure. If so, call the
+** P4 may be a pointer to an sqlite3_vtab structure. If so, call the
** xBegin method for that table.
**
** Also, whether or not P4 is set, check that this is not being called from
@@ -89774,7 +100634,7 @@ case OP_VBegin: {
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VCreate P1 P2 * * *
**
-** P2 is a register that holds the name of a virtual table in database
+** P2 is a register that holds the name of a virtual table in database
** P1. Call the xCreate method for that table.
*/
case OP_VCreate: {
@@ -89810,6 +100670,7 @@ case OP_VDestroy: {
db->nVDestroy++;
rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z);
db->nVDestroy--;
+ assert( p->errorAction==OE_Abort && p->usesStmtJournal );
if( rc ) goto abort_due_to_error;
break;
}
@@ -89822,7 +100683,7 @@ case OP_VDestroy: {
** P1 is a cursor number. This opcode opens a cursor to the virtual
** table and stores that cursor in P1.
*/
-case OP_VOpen: {
+case OP_VOpen: { /* ncycle */
VdbeCursor *pCur;
sqlite3_vtab_cursor *pVCur;
sqlite3_vtab *pVtab;
@@ -89845,7 +100706,7 @@ case OP_VOpen: {
pVCur->pVtab = pVtab;
/* Initialize vdbe cursor object */
- pCur = allocateCursor(p, pOp->p1, 0, -1, CURTYPE_VTAB);
+ pCur = allocateCursor(p, pOp->p1, 0, CURTYPE_VTAB);
if( pCur ){
pCur->uc.pVCur = pVCur;
pVtab->nRef++;
@@ -89859,6 +100720,80 @@ case OP_VOpen: {
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
+/* Opcode: VCheck P1 P2 P3 P4 *
+**
+** P4 is a pointer to a Table object that is a virtual table in schema P1
+** that supports the xIntegrity() method. This opcode runs the xIntegrity()
+** method for that virtual table, using P3 as the integer argument. If
+** an error is reported back, the table name is prepended to the error
+** message and that message is stored in P2. If no errors are seen,
+** register P2 is set to NULL.
+*/
+case OP_VCheck: { /* out2 */
+ Table *pTab;
+ sqlite3_vtab *pVtab;
+ const sqlite3_module *pModule;
+ char *zErr = 0;
+
+ pOut = &aMem[pOp->p2];
+ sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */
+ assert( pOp->p4type==P4_TABLEREF );
+ pTab = pOp->p4.pTab;
+ assert( pTab!=0 );
+ assert( pTab->nTabRef>0 );
+ assert( IsVirtual(pTab) );
+ if( pTab->u.vtab.p==0 ) break;
+ pVtab = pTab->u.vtab.p->pVtab;
+ assert( pVtab!=0 );
+ pModule = pVtab->pModule;
+ assert( pModule!=0 );
+ assert( pModule->iVersion>=4 );
+ assert( pModule->xIntegrity!=0 );
+ sqlite3VtabLock(pTab->u.vtab.p);
+ assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName,
+ pOp->p3, &zErr);
+ sqlite3VtabUnlock(pTab->u.vtab.p);
+ if( rc ){
+ sqlite3_free(zErr);
+ goto abort_due_to_error;
+ }
+ if( zErr ){
+ sqlite3VdbeMemSetStr(pOut, zErr, -1, SQLITE_UTF8, sqlite3_free);
+ }
+ break;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/* Opcode: VInitIn P1 P2 P3 * *
+** Synopsis: r[P2]=ValueList(P1,P3)
+**
+** Set register P2 to be a pointer to a ValueList object for cursor P1
+** with cache register P3 and output register P3+1. This ValueList object
+** can be used as the first argument to sqlite3_vtab_in_first() and
+** sqlite3_vtab_in_next() to extract all of the values stored in the P1
+** cursor. Register P3 is used to hold the values returned by
+** sqlite3_vtab_in_first() and sqlite3_vtab_in_next().
+*/
+case OP_VInitIn: { /* out2, ncycle */
+ VdbeCursor *pC; /* The cursor containing the RHS values */
+ ValueList *pRhs; /* New ValueList object to put in reg[P2] */
+
+ pC = p->apCsr[pOp->p1];
+ pRhs = sqlite3_malloc64( sizeof(*pRhs) );
+ if( pRhs==0 ) goto no_mem;
+ pRhs->pCsr = pC->uc.pCursor;
+ pRhs->pOut = &aMem[pOp->p3];
+ pOut = out2Prerelease(p, pOp);
+ pOut->flags = MEM_Null;
+ sqlite3VdbeMemSetPointer(pOut, pRhs, "ValueList", sqlite3VdbeValueListFree);
+ break;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VFilter P1 P2 P3 P4 *
** Synopsis: iplan=r[P3] zplan='P4'
**
@@ -89878,7 +100813,7 @@ case OP_VOpen: {
**
** A jump is made to P2 if the result set after filtering would be empty.
*/
-case OP_VFilter: { /* jump */
+case OP_VFilter: { /* jump, ncycle */
int nArg;
int iQuery;
const sqlite3_module *pModule;
@@ -89896,6 +100831,7 @@ case OP_VFilter: { /* jump */
pCur = p->apCsr[pOp->p1];
assert( memIsValid(pQuery) );
REGISTER_TRACE(pOp->p3, pQuery);
+ assert( pCur!=0 );
assert( pCur->eCurType==CURTYPE_VTAB );
pVCur = pCur->uc.pVCur;
pVtab = pVCur->pVtab;
@@ -89907,7 +100843,6 @@ case OP_VFilter: { /* jump */
iQuery = (int)pQuery->u.i;
/* Invoke the xFilter method */
- res = 0;
apArg = p->apArg;
for(i = 0; i<nArg; i++){
apArg[i] = &pArgc[i+1];
@@ -89938,14 +100873,15 @@ case OP_VFilter: { /* jump */
** bits (OPFLAG_LENGTHARG or OPFLAG_TYPEOFARG) but those bits are
** unused by OP_VColumn.
*/
-case OP_VColumn: {
+case OP_VColumn: { /* ncycle */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
Mem *pDest;
sqlite3_context sContext;
+ FuncDef nullFunc;
VdbeCursor *pCur = p->apCsr[pOp->p1];
- assert( pCur->eCurType==CURTYPE_VTAB );
+ assert( pCur!=0 );
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
@@ -89953,12 +100889,17 @@ case OP_VColumn: {
sqlite3VdbeMemSetNull(pDest);
break;
}
+ assert( pCur->eCurType==CURTYPE_VTAB );
pVtab = pCur->uc.pVCur->pVtab;
pModule = pVtab->pModule;
assert( pModule->xColumn );
memset(&sContext, 0, sizeof(sContext));
sContext.pOut = pDest;
- testcase( (pOp->p5 & OPFLAG_NOCHNG)==0 && pOp->p5!=0 );
+ sContext.enc = encoding;
+ nullFunc.pUserData = 0;
+ nullFunc.funcFlags = SQLITE_RESULT_SUBTYPE;
+ sContext.pFunc = &nullFunc;
+ assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 );
if( pOp->p5 & OPFLAG_NOCHNG ){
sqlite3VdbeMemSetNull(pDest);
pDest->flags = MEM_Null|MEM_Zero;
@@ -89976,9 +100917,6 @@ case OP_VColumn: {
REGISTER_TRACE(pOp->p3, pDest);
UPDATE_MAX_BLOBSIZE(pDest);
- if( sqlite3VdbeMemTooBig(pDest) ){
- goto too_big;
- }
if( rc ) goto abort_due_to_error;
break;
}
@@ -89991,14 +100929,14 @@ case OP_VColumn: {
** jump to instruction P2. Or, if the virtual table has reached
** the end of its result set, then fall through to the next instruction.
*/
-case OP_VNext: { /* jump */
+case OP_VNext: { /* jump, ncycle */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
int res;
VdbeCursor *pCur;
- res = 0;
pCur = p->apCsr[pOp->p1];
+ assert( pCur!=0 );
assert( pCur->eCurType==CURTYPE_VTAB );
if( pCur->nullRow ){
break;
@@ -90009,7 +100947,7 @@ case OP_VNext: { /* jump */
/* Invoke the xNext() method of the module. There is no way for the
** underlying implementation to return an error if one occurs during
- ** xNext(). Instead, if an error occurs, true is returned (indicating that
+ ** xNext(). Instead, if an error occurs, true is returned (indicating that
** data is available) and the error code returned when xColumn or
** some other method is next invoked on the save virtual table cursor.
*/
@@ -90037,7 +100975,7 @@ case OP_VRename: {
sqlite3_vtab *pVtab;
Mem *pName;
int isLegacy;
-
+
isLegacy = (db->flags & SQLITE_LegacyAlter);
db->flags |= SQLITE_LegacyAlter;
pVtab = pOp->p4.pVtab->pVtab;
@@ -90053,7 +100991,7 @@ case OP_VRename: {
rc = sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8);
if( rc ) goto abort_due_to_error;
rc = pVtab->pModule->xRename(pVtab, pName->z);
- if( isLegacy==0 ) db->flags &= ~SQLITE_LegacyAlter;
+ if( isLegacy==0 ) db->flags &= ~(u64)SQLITE_LegacyAlter;
sqlite3VtabImportErrmsg(p, pVtab);
p->expired = 0;
if( rc ) goto abort_due_to_error;
@@ -90067,23 +101005,23 @@ case OP_VRename: {
**
** P4 is a pointer to a virtual table object, an sqlite3_vtab structure.
** This opcode invokes the corresponding xUpdate method. P2 values
-** are contiguous memory cells starting at P3 to pass to the xUpdate
-** invocation. The value in register (P3+P2-1) corresponds to the
+** are contiguous memory cells starting at P3 to pass to the xUpdate
+** invocation. The value in register (P3+P2-1) corresponds to the
** p2th element of the argv array passed to xUpdate.
**
** The xUpdate method will do a DELETE or an INSERT or both.
** The argv[0] element (which corresponds to memory cell P3)
-** is the rowid of a row to delete. If argv[0] is NULL then no
-** deletion occurs. The argv[1] element is the rowid of the new
-** row. This can be NULL to have the virtual table select the new
-** rowid for itself. The subsequent elements in the array are
+** is the rowid of a row to delete. If argv[0] is NULL then no
+** deletion occurs. The argv[1] element is the rowid of the new
+** row. This can be NULL to have the virtual table select the new
+** rowid for itself. The subsequent elements in the array are
** the values of columns in the new row.
**
** If P2==1 then no insert is performed. argv[0] is the rowid of
** a row to delete.
**
** P1 is a boolean flag. If it is set to true and the xUpdate call
-** is successful, then the value returned by sqlite3_last_insert_rowid()
+** is successful, then the value returned by sqlite3_last_insert_rowid()
** is set to the value of the rowid for the row just inserted.
**
** P5 is the error actions (OE_Replace, OE_Fail, OE_Ignore, etc) to
@@ -90094,11 +101032,11 @@ case OP_VUpdate: {
const sqlite3_module *pModule;
int nArg;
int i;
- sqlite_int64 rowid;
+ sqlite_int64 rowid = 0;
Mem **apArg;
Mem *pX;
- assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback
+ assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback
|| pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
);
assert( p->readOnly==0 );
@@ -90183,72 +101121,52 @@ case OP_MaxPgcnt: { /* out2 */
}
#endif
-/* Opcode: Function0 P1 P2 P3 P4 P5
-** Synopsis: r[P3]=func(r[P2@P5])
+/* Opcode: Function P1 P2 P3 P4 *
+** Synopsis: r[P3]=func(r[P2@NP])
**
-** Invoke a user function (P4 is a pointer to a FuncDef object that
-** defines the function) with P5 arguments taken from register P2 and
-** successors. The result of the function is stored in register P3.
-** Register P3 must not be one of the function inputs.
+** Invoke a user function (P4 is a pointer to an sqlite3_context object that
+** contains a pointer to the function to be run) with arguments taken
+** from register P2 and successors. The number of arguments is in
+** the sqlite3_context object that P4 points to.
+** The result of the function is stored
+** in register P3. Register P3 must not be one of the function inputs.
**
-** P1 is a 32-bit bitmask indicating whether or not each argument to the
+** P1 is a 32-bit bitmask indicating whether or not each argument to the
** function was determined to be constant at compile time. If the first
** argument was constant then bit 0 of P1 is set. This is used to determine
** whether meta data associated with a user function argument using the
** sqlite3_set_auxdata() API may be safely retained until the next
** invocation of this opcode.
**
-** See also: Function, AggStep, AggFinal
+** See also: AggStep, AggFinal, PureFunc
*/
-/* Opcode: Function P1 P2 P3 P4 P5
-** Synopsis: r[P3]=func(r[P2@P5])
+/* Opcode: PureFunc P1 P2 P3 P4 *
+** Synopsis: r[P3]=func(r[P2@NP])
**
** Invoke a user function (P4 is a pointer to an sqlite3_context object that
-** contains a pointer to the function to be run) with P5 arguments taken
-** from register P2 and successors. The result of the function is stored
+** contains a pointer to the function to be run) with arguments taken
+** from register P2 and successors. The number of arguments is in
+** the sqlite3_context object that P4 points to.
+** The result of the function is stored
** in register P3. Register P3 must not be one of the function inputs.
**
-** P1 is a 32-bit bitmask indicating whether or not each argument to the
+** P1 is a 32-bit bitmask indicating whether or not each argument to the
** function was determined to be constant at compile time. If the first
** argument was constant then bit 0 of P1 is set. This is used to determine
** whether meta data associated with a user function argument using the
** sqlite3_set_auxdata() API may be safely retained until the next
** invocation of this opcode.
**
-** SQL functions are initially coded as OP_Function0 with P4 pointing
-** to a FuncDef object. But on first evaluation, the P4 operand is
-** automatically converted into an sqlite3_context object and the operation
-** changed to this OP_Function opcode. In this way, the initialization of
-** the sqlite3_context object occurs only once, rather than once for each
-** evaluation of the function.
+** This opcode works exactly like OP_Function. The only difference is in
+** its name. This opcode is used in places where the function must be
+** purely non-deterministic. Some built-in date/time functions can be
+** either deterministic of non-deterministic, depending on their arguments.
+** When those function are used in a non-deterministic way, they will check
+** to see if they were called using OP_PureFunc instead of OP_Function, and
+** if they were, they throw an error.
**
-** See also: Function0, AggStep, AggFinal
+** See also: AggStep, AggFinal, Function
*/
-case OP_PureFunc0: /* group */
-case OP_Function0: { /* group */
- int n;
- sqlite3_context *pCtx;
-
- assert( pOp->p4type==P4_FUNCDEF );
- n = pOp->p5;
- assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
- assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) );
- assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
- pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
- if( pCtx==0 ) goto no_mem;
- pCtx->pOut = 0;
- pCtx->pFunc = pOp->p4.pFunc;
- pCtx->iOp = (int)(pOp - aOp);
- pCtx->pVdbe = p;
- pCtx->isError = 0;
- pCtx->argc = n;
- pOp->p4type = P4_FUNCCTX;
- pOp->p4.pCtx = pCtx;
- assert( OP_PureFunc == OP_PureFunc0+2 );
- assert( OP_Function == OP_Function0+2 );
- pOp->opcode += 2;
- /* Fall through into OP_Function */
-}
case OP_PureFunc: /* group */
case OP_Function: { /* group */
int i;
@@ -90260,12 +101178,15 @@ case OP_Function: { /* group */
/* If this function is inside of a trigger, the register array in aMem[]
** might change from one evaluation to the next. The next block of code
** checks to see if the register array has changed, and if so it
- ** reinitializes the relavant parts of the sqlite3_context object */
+ ** reinitializes the relevant parts of the sqlite3_context object */
pOut = &aMem[pOp->p3];
if( pCtx->pOut != pOut ){
+ pCtx->pVdbe = p;
pCtx->pOut = pOut;
+ pCtx->enc = encoding;
for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i];
}
+ assert( pCtx->pVdbe==p );
memAboutToChange(p, pOut);
#ifdef SQLITE_DEBUG
@@ -90289,17 +101210,134 @@ case OP_Function: { /* group */
if( rc ) goto abort_due_to_error;
}
- /* Copy the result of the function into register P3 */
- if( pOut->flags & (MEM_Str|MEM_Blob) ){
- sqlite3VdbeChangeEncoding(pOut, encoding);
- if( sqlite3VdbeMemTooBig(pOut) ) goto too_big;
- }
+ assert( (pOut->flags&MEM_Str)==0
+ || pOut->enc==encoding
+ || db->mallocFailed );
+ assert( !sqlite3VdbeMemTooBig(pOut) );
REGISTER_TRACE(pOp->p3, pOut);
UPDATE_MAX_BLOBSIZE(pOut);
break;
}
+/* Opcode: ClrSubtype P1 * * * *
+** Synopsis: r[P1].subtype = 0
+**
+** Clear the subtype from register P1.
+*/
+case OP_ClrSubtype: { /* in1 */
+ pIn1 = &aMem[pOp->p1];
+ pIn1->flags &= ~MEM_Subtype;
+ break;
+}
+
+/* Opcode: GetSubtype P1 P2 * * *
+** Synopsis: r[P2] = r[P1].subtype
+**
+** Extract the subtype value from register P1 and write that subtype
+** into register P2. If P1 has no subtype, then P1 gets a NULL.
+*/
+case OP_GetSubtype: { /* in1 out2 */
+ pIn1 = &aMem[pOp->p1];
+ pOut = &aMem[pOp->p2];
+ if( pIn1->flags & MEM_Subtype ){
+ sqlite3VdbeMemSetInt64(pOut, pIn1->eSubtype);
+ }else{
+ sqlite3VdbeMemSetNull(pOut);
+ }
+ break;
+}
+
+/* Opcode: SetSubtype P1 P2 * * *
+** Synopsis: r[P2].subtype = r[P1]
+**
+** Set the subtype value of register P2 to the integer from register P1.
+** If P1 is NULL, clear the subtype from p2.
+*/
+case OP_SetSubtype: { /* in1 out2 */
+ pIn1 = &aMem[pOp->p1];
+ pOut = &aMem[pOp->p2];
+ if( pIn1->flags & MEM_Null ){
+ pOut->flags &= ~MEM_Subtype;
+ }else{
+ assert( pIn1->flags & MEM_Int );
+ pOut->flags |= MEM_Subtype;
+ pOut->eSubtype = (u8)(pIn1->u.i & 0xff);
+ }
+ break;
+}
+
+/* Opcode: FilterAdd P1 * P3 P4 *
+** Synopsis: filter(P1) += key(P3@P4)
+**
+** Compute a hash on the P4 registers starting with r[P3] and
+** add that hash to the bloom filter contained in r[P1].
+*/
+case OP_FilterAdd: {
+ u64 h;
+
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
+ pIn1 = &aMem[pOp->p1];
+ assert( pIn1->flags & MEM_Blob );
+ assert( pIn1->n>0 );
+ h = filterHash(aMem, pOp);
+#ifdef SQLITE_DEBUG
+ if( db->flags&SQLITE_VdbeTrace ){
+ int ii;
+ for(ii=pOp->p3; ii<pOp->p3+pOp->p4.i; ii++){
+ registerTrace(ii, &aMem[ii]);
+ }
+ printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n));
+ }
+#endif
+ h %= (pIn1->n*8);
+ pIn1->z[h/8] |= 1<<(h&7);
+ break;
+}
+
+/* Opcode: Filter P1 P2 P3 P4 *
+** Synopsis: if key(P3@P4) not in filter(P1) goto P2
+**
+** Compute a hash on the key contained in the P4 registers starting
+** with r[P3]. Check to see if that hash is found in the
+** bloom filter hosted by register P1. If it is not present then
+** maybe jump to P2. Otherwise fall through.
+**
+** False negatives are harmless. It is always safe to fall through,
+** even if the value is in the bloom filter. A false negative causes
+** more CPU cycles to be used, but it should still yield the correct
+** answer. However, an incorrect answer may well arise from a
+** false positive - if the jump is taken when it should fall through.
+*/
+case OP_Filter: { /* jump */
+ u64 h;
+
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
+ pIn1 = &aMem[pOp->p1];
+ assert( (pIn1->flags & MEM_Blob)!=0 );
+ assert( pIn1->n >= 1 );
+ h = filterHash(aMem, pOp);
+#ifdef SQLITE_DEBUG
+ if( db->flags&SQLITE_VdbeTrace ){
+ int ii;
+ for(ii=pOp->p3; ii<pOp->p3+pOp->p4.i; ii++){
+ registerTrace(ii, &aMem[ii]);
+ }
+ printf("hash: %llu modulo %d -> %u\n", h, pIn1->n, (int)(h%pIn1->n));
+ }
+#endif
+ h %= (pIn1->n*8);
+ if( (pIn1->z[h/8] & (1<<(h&7)))==0 ){
+ VdbeBranchTaken(1, 2);
+ p->aCounter[SQLITE_STMTSTATUS_FILTER_HIT]++;
+ goto jump_to_p2;
+ }else{
+ p->aCounter[SQLITE_STMTSTATUS_FILTER_MISS]++;
+ VdbeBranchTaken(0, 2);
+ }
+ break;
+}
+
/* Opcode: Trace P1 P2 * P4 *
**
** Write P4 on the statement trace output if statement tracing is
@@ -90348,23 +101386,22 @@ case OP_Init: { /* jump */
#ifndef SQLITE_OMIT_TRACE
if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0
- && !p->doingRerun
+ && p->minWriteFileFormat!=254 /* tag-20220401a */
&& (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0
){
#ifndef SQLITE_OMIT_DEPRECATED
if( db->mTrace & SQLITE_TRACE_LEGACY ){
- void (*x)(void*,const char*) = (void(*)(void*,const char*))db->xTrace;
char *z = sqlite3VdbeExpandSql(p, zTrace);
- x(db->pTraceArg, z);
+ db->trace.xLegacy(db->pTraceArg, z);
sqlite3_free(z);
}else
#endif
if( db->nVdbeExec>1 ){
char *z = sqlite3MPrintf(db, "-- %s", zTrace);
- (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, z);
+ (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, z);
sqlite3DbFree(db, z);
}else{
- (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace);
+ (void)db->trace.xV2(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace);
}
}
#ifdef SQLITE_USE_FCNTL_TRACE
@@ -90437,6 +101474,55 @@ case OP_Abortable: {
}
#endif
+#ifdef SQLITE_DEBUG
+/* Opcode: ReleaseReg P1 P2 P3 * P5
+** Synopsis: release r[P1@P2] mask P3
+**
+** Release registers from service. Any content that was in the
+** the registers is unreliable after this opcode completes.
+**
+** The registers released will be the P2 registers starting at P1,
+** except if bit ii of P3 set, then do not release register P1+ii.
+** In other words, P3 is a mask of registers to preserve.
+**
+** Releasing a register clears the Mem.pScopyFrom pointer. That means
+** that if the content of the released register was set using OP_SCopy,
+** a change to the value of the source register for the OP_SCopy will no longer
+** generate an assertion fault in sqlite3VdbeMemAboutToChange().
+**
+** If P5 is set, then all released registers have their type set
+** to MEM_Undefined so that any subsequent attempt to read the released
+** register (before it is reinitialized) will generate an assertion fault.
+**
+** P5 ought to be set on every call to this opcode.
+** However, there are places in the code generator will release registers
+** before their are used, under the (valid) assumption that the registers
+** will not be reallocated for some other purpose before they are used and
+** hence are safe to release.
+**
+** This opcode is only available in testing and debugging builds. It is
+** not generated for release builds. The purpose of this opcode is to help
+** validate the generated bytecode. This opcode does not actually contribute
+** to computing an answer.
+*/
+case OP_ReleaseReg: {
+ Mem *pMem;
+ int i;
+ u32 constMask;
+ assert( pOp->p1>0 );
+ assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
+ pMem = &aMem[pOp->p1];
+ constMask = pOp->p3;
+ for(i=0; i<pOp->p2; i++, pMem++){
+ if( i>=32 || (constMask & MASKBIT32(i))==0 ){
+ pMem->pScopyFrom = 0;
+ if( i<32 && pOp->p5 ) MemSetTypeFlag(pMem, MEM_Undefined);
+ }
+ }
+ break;
+}
+#endif
+
/* Opcode: Noop * * * * *
**
** Do nothing. This instruction is often useful as a jump
@@ -90462,11 +101548,13 @@ default: { /* This is really OP_Noop, OP_Explain */
*****************************************************************************/
}
-#ifdef VDBE_PROFILE
- {
- u64 endTime = sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
- if( endTime>start ) pOrigOp->cycles += endTime - start;
- pOrigOp->cnt++;
+#if defined(VDBE_PROFILE)
+ *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
+ pnCycle = 0;
+#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+ if( pnCycle ){
+ *pnCycle += sqlite3Hwtime();
+ pnCycle = 0;
}
#endif
@@ -90488,6 +101576,12 @@ default: { /* This is really OP_Noop, OP_Explain */
if( opProperty & OPFLG_OUT3 ){
registerTrace(pOrigOp->p3, &aMem[pOrigOp->p3]);
}
+ if( opProperty==0xff ){
+ /* Never happens. This code exists to avoid a harmless linkage
+ ** warning about sqlite3VdbeRegisterDump() being defined but not
+ ** used. */
+ sqlite3VdbeRegisterDump(p);
+ }
}
#endif /* SQLITE_DEBUG */
#endif /* NDEBUG */
@@ -90497,18 +101591,37 @@ default: { /* This is really OP_Noop, OP_Explain */
** an error of some kind.
*/
abort_due_to_error:
- if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT;
+ if( db->mallocFailed ){
+ rc = SQLITE_NOMEM_BKPT;
+ }else if( rc==SQLITE_IOERR_CORRUPTFS ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }
assert( rc );
+#ifdef SQLITE_DEBUG
+ if( db->flags & SQLITE_VdbeTrace ){
+ const char *zTrace = p->zSql;
+ if( zTrace==0 ){
+ if( aOp[0].opcode==OP_Trace ){
+ zTrace = aOp[0].p4.z;
+ }
+ if( zTrace==0 ) zTrace = "???";
+ }
+ printf("ABORT-due-to-error (rc=%d): %s\n", rc, zTrace);
+ }
+#endif
if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){
sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
}
p->rc = rc;
sqlite3SystemError(db, rc);
testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(rc, "statement aborts at %d: [%s] %s",
+ sqlite3_log(rc, "statement aborts at %d: [%s] %s",
(int)(pOp - aOp), p->zSql, p->zErrMsg);
- sqlite3VdbeHalt(p);
+ if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p);
if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db);
+ if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){
+ db->flags |= SQLITE_CorruptRdOnly;
+ }
rc = SQLITE_ERROR;
if( resetSchemaOnFault>0 ){
sqlite3ResetOneSchema(db, resetSchemaOnFault-1);
@@ -90518,11 +101631,34 @@ abort_due_to_error:
** release the mutexes on btrees that were acquired at the
** top. */
vdbe_return:
- testcase( nVmStep>0 );
+#if defined(VDBE_PROFILE)
+ if( pnCycle ){
+ *pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
+ pnCycle = 0;
+ }
+#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+ if( pnCycle ){
+ *pnCycle += sqlite3Hwtime();
+ pnCycle = 0;
+ }
+#endif
+
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ while( nVmStep>=nProgressLimit && db->xProgress!=0 ){
+ nProgressLimit += db->nProgressOps;
+ if( db->xProgress(db->pProgressArg) ){
+ nProgressLimit = LARGEST_UINT64;
+ rc = SQLITE_INTERRUPT;
+ goto abort_due_to_error;
+ }
+ }
+#endif
p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep;
- sqlite3VdbeLeave(p);
- assert( rc!=SQLITE_OK || nExtraDelete==0
- || sqlite3_strlike("DELETE%",p->zSql,0)!=0
+ if( DbMaskNonZero(p->lockMask) ){
+ sqlite3VdbeLeave(p);
+ }
+ assert( rc!=SQLITE_OK || nExtraDelete==0
+ || sqlite3_strlike("DELETE%",p->zSql,0)!=0
);
return rc;
@@ -90546,10 +101682,8 @@ no_mem:
** flag.
*/
abort_due_to_interrupt:
- assert( db->u1.isInterrupted );
- rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
- p->rc = rc;
- sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
+ assert( AtomicLoad(&db->u1.isInterrupted) );
+ rc = SQLITE_INTERRUPT;
goto abort_due_to_error;
}
@@ -90606,7 +101740,7 @@ struct Incrblob {
** sqlite3DbFree().
**
** If an error does occur, then the b-tree cursor is closed. All subsequent
-** calls to sqlite3_blob_read(), blob_write() or blob_reopen() will
+** calls to sqlite3_blob_read(), blob_write() or blob_reopen() will
** immediately return SQLITE_ABORT.
*/
static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
@@ -90614,11 +101748,10 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
char *zErr = 0; /* Error message */
Vdbe *v = (Vdbe *)p->pStmt;
- /* Set the value of register r[1] in the SQL statement to integer iRow.
+ /* Set the value of register r[1] in the SQL statement to integer iRow.
** This is done directly as a performance optimization
*/
- v->aMem[1].flags = MEM_Int;
- v->aMem[1].u.i = iRow;
+ sqlite3VdbeMemSetInt64(&v->aMem[1], iRow);
/* If the statement has been run before (and is paused at the OP_ResultRow)
** then back it up to the point where it does the OP_NotExists. This could
@@ -90633,7 +101766,10 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
}
if( rc==SQLITE_ROW ){
VdbeCursor *pC = v->apCsr[0];
- u32 type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0;
+ u32 type;
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0;
testcase( pC->nHdrParsed==p->iCol );
testcase( pC->nHdrParsed==p->iCol+1 );
if( type<12 ){
@@ -90698,7 +101834,7 @@ SQLITE_API int sqlite3_blob_open(
#endif
*ppBlob = 0;
#ifdef SQLITE_ENABLE_API_ARMOR
- if( !sqlite3SafetyCheckOk(db) || zTable==0 ){
+ if( !sqlite3SafetyCheckOk(db) || zTable==0 || zColumn==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
@@ -90707,10 +101843,9 @@ SQLITE_API int sqlite3_blob_open(
sqlite3_mutex_enter(db->mutex);
pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
- do {
- memset(&sParse, 0, sizeof(Parse));
+ while(1){
+ sqlite3ParseObjectInit(&sParse,db);
if( !pBlob ) goto blob_open_out;
- sParse.db = db;
sqlite3DbFree(db, zErr);
zErr = 0;
@@ -90725,7 +101860,7 @@ SQLITE_API int sqlite3_blob_open(
sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable);
}
#ifndef SQLITE_OMIT_VIEW
- if( pTab && pTab->pSelect ){
+ if( pTab && IsView(pTab) ){
pTab = 0;
sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
}
@@ -90745,7 +101880,7 @@ SQLITE_API int sqlite3_blob_open(
/* Now search pTab for the exact column. */
for(iCol=0; iCol<pTab->nCol; iCol++) {
- if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){
+ if( sqlite3StrICmp(pTab->aCol[iCol].zCnName, zColumn)==0 ){
break;
}
}
@@ -90758,7 +101893,7 @@ SQLITE_API int sqlite3_blob_open(
}
/* If the value is being opened for writing, check that the
- ** column is not indexed, and that it is not part of a foreign key.
+ ** column is not indexed, and that it is not part of a foreign key.
*/
if( wrFlag ){
const char *zFault = 0;
@@ -90767,10 +101902,11 @@ SQLITE_API int sqlite3_blob_open(
if( db->flags&SQLITE_ForeignKeys ){
/* Check that the column is not part of an FK child key definition. It
** is not necessary to check if it is part of a parent key, as parent
- ** key columns must be indexed. The check below will pick up this
+ ** key columns must be indexed. The check below will pick up this
** case. */
FKey *pFKey;
- for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+ assert( IsOrdinaryTable(pTab) );
+ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
int j;
for(j=0; j<pFKey->nCol; j++){
if( pFKey->aCol[j].iFrom==iCol ){
@@ -90801,8 +101937,8 @@ SQLITE_API int sqlite3_blob_open(
pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(&sParse);
assert( pBlob->pStmt || db->mallocFailed );
if( pBlob->pStmt ){
-
- /* This VDBE program seeks a btree cursor to the identified
+
+ /* This VDBE program seeks a btree cursor to the identified
** db/table/row entry. The reason for using a vdbe program instead
** of writing code to use the b-tree layer directly is that the
** vdbe program will take advantage of the various transaction,
@@ -90810,11 +101946,11 @@ SQLITE_API int sqlite3_blob_open(
**
** After seeking the cursor, the vdbe executes an OP_ResultRow.
** Code external to the Vdbe then "borrows" the b-tree cursor and
- ** uses it to implement the blob_read(), blob_write() and
+ ** uses it to implement the blob_read(), blob_write() and
** blob_bytes() functions.
**
** The sqlite3_blob_close() function finalizes the vdbe program,
- ** which closes the b-tree cursor and (possibly) commits the
+ ** which closes the b-tree cursor and (possibly) commits the
** transaction.
*/
static const int iLn = VDBE_OFFSET_LINENO(2);
@@ -90831,7 +101967,7 @@ SQLITE_API int sqlite3_blob_open(
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
VdbeOp *aOp;
- sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
+ sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
pTab->pSchema->schema_cookie,
pTab->pSchema->iGeneration);
sqlite3VdbeChangeP5(v, 1);
@@ -90839,7 +101975,7 @@ SQLITE_API int sqlite3_blob_open(
aOp = sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
/* Make sure a mutex is held on the table to be accessed */
- sqlite3VdbeUsesBtree(v, iDb);
+ sqlite3VdbeUsesBtree(v, iDb);
if( db->mallocFailed==0 ){
assert( aOp!=0 );
@@ -90855,17 +101991,17 @@ SQLITE_API int sqlite3_blob_open(
if( db->mallocFailed==0 ){
#endif
- /* Remove either the OP_OpenWrite or OpenRead. Set the P2
+ /* Remove either the OP_OpenWrite or OpenRead. Set the P2
** parameter of the other to pTab->tnum. */
if( wrFlag ) aOp[1].opcode = OP_OpenWrite;
aOp[1].p2 = pTab->tnum;
- aOp[1].p3 = iDb;
+ aOp[1].p3 = iDb;
/* Configure the number of columns. Configure the cursor to
** think that the table has one more column than it really
** does. An OP_Column to retrieve this imaginary column will
** always return an SQL NULL. This is useful because it means
- ** we can invoke OP_Column to fill in the vdbe cursors type
+ ** we can invoke OP_Column to fill in the vdbe cursors type
** and offset cache without causing any IO.
*/
aOp[1].p4type = P4_INT32;
@@ -90878,7 +102014,7 @@ SQLITE_API int sqlite3_blob_open(
sqlite3VdbeMakeReady(v, &sParse);
}
}
-
+
pBlob->iCol = iCol;
pBlob->db = db;
sqlite3BtreeLeaveAll(db);
@@ -90886,7 +102022,9 @@ SQLITE_API int sqlite3_blob_open(
goto blob_open_out;
}
rc = blobSeekToRow(pBlob, iRow, &zErr);
- } while( (++nAttempt)<SQLITE_MAX_SCHEMA_RETRY && rc==SQLITE_SCHEMA );
+ if( (++nAttempt)>=SQLITE_MAX_SCHEMA_RETRY || rc!=SQLITE_SCHEMA ) break;
+ sqlite3ParseObjectReset(&sParse);
+ }
blob_open_out:
if( rc==SQLITE_OK && db->mallocFailed==0 ){
@@ -90895,9 +102033,9 @@ blob_open_out:
if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);
sqlite3DbFree(db, pBlob);
}
- sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
+ sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr);
sqlite3DbFree(db, zErr);
- sqlite3ParserReset(&sParse);
+ sqlite3ParseObjectReset(&sParse);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
@@ -90913,11 +102051,12 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *pBlob){
sqlite3 *db;
if( p ){
+ sqlite3_stmt *pStmt = p->pStmt;
db = p->db;
sqlite3_mutex_enter(db->mutex);
- rc = sqlite3_finalize(p->pStmt);
sqlite3DbFree(db, p);
sqlite3_mutex_leave(db->mutex);
+ rc = sqlite3_finalize(pStmt);
}else{
rc = SQLITE_OK;
}
@@ -90928,10 +102067,10 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *pBlob){
** Perform a read or write operation on a blob
*/
static int blobReadWrite(
- sqlite3_blob *pBlob,
- void *z,
- int n,
- int iOffset,
+ sqlite3_blob *pBlob,
+ void *z,
+ int n,
+ int iOffset,
int (*xCall)(BtCursor*, u32, u32, void*)
){
int rc;
@@ -90961,14 +102100,14 @@ static int blobReadWrite(
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
if( xCall==sqlite3BtreePutData && db->xPreUpdateCallback ){
- /* If a pre-update hook is registered and this is a write cursor,
- ** invoke it here.
- **
+ /* If a pre-update hook is registered and this is a write cursor,
+ ** invoke it here.
+ **
** TODO: The preupdate-hook is passed SQLITE_DELETE, even though this
** operation should really be an SQLITE_UPDATE. This is probably
- ** incorrect, but is convenient because at this point the new.* values
- ** are not easily obtainable. And for the sessions module, an
- ** SQLITE_UPDATE where the PK columns do not change is handled in the
+ ** incorrect, but is convenient because at this point the new.* values
+ ** are not easily obtainable. And for the sessions module, an
+ ** SQLITE_UPDATE where the PK columns do not change is handled in the
** same way as an SQLITE_DELETE (the SQLITE_DELETE code is actually
** slightly more efficient). Since you cannot write to a PK column
** using the incremental-blob API, this works. For the sessions module
@@ -90976,8 +102115,10 @@ static int blobReadWrite(
*/
sqlite3_int64 iKey;
iKey = sqlite3BtreeIntegerKey(p->pCsr);
+ assert( v->apCsr[0]!=0 );
+ assert( v->apCsr[0]->eCurType==CURTYPE_BTREE );
sqlite3VdbePreUpdateHook(
- v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1
+ v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1, p->iCol
);
}
#endif
@@ -91028,8 +102169,8 @@ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *pBlob){
**
** If an error occurs, or if the specified row does not exist or does not
** contain a blob or text value, then an error code is returned and the
-** database handle error code and message set. If this happens, then all
-** subsequent calls to sqlite3_blob_xxx() functions (except blob_close())
+** database handle error code and message set. If this happens, then all
+** subsequent calls to sqlite3_blob_xxx() functions (except blob_close())
** immediately return SQLITE_ABORT.
*/
SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
@@ -91048,9 +102189,10 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
rc = SQLITE_ABORT;
}else{
char *zErr;
+ ((Vdbe*)p->pStmt)->rc = SQLITE_OK;
rc = blobSeekToRow(p, iRow, &zErr);
if( rc!=SQLITE_OK ){
- sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
+ sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr);
sqlite3DbFree(db, zErr);
}
assert( rc!=SQLITE_SCHEMA );
@@ -91123,7 +102265,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
** is like Close() followed by Init() only
** much faster.
**
-** The interfaces above must be called in a particular order. Write() can
+** The interfaces above must be called in a particular order. Write() can
** only occur in between Init()/Reset() and Rewind(). Next(), Rowkey(), and
** Compare() can only occur in between Rewind() and Close()/Reset(). i.e.
**
@@ -91131,16 +102273,16 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
** for each record: Write()
** Rewind()
** Rowkey()/Compare()
-** Next()
+** Next()
** Close()
**
** Algorithm:
**
-** Records passed to the sorter via calls to Write() are initially held
+** Records passed to the sorter via calls to Write() are initially held
** unsorted in main memory. Assuming the amount of memory used never exceeds
** a threshold, when Rewind() is called the set of records is sorted using
** an in-memory merge sort. In this case, no temporary files are required
-** and subsequent calls to Rowkey(), Next() and Compare() read records
+** and subsequent calls to Rowkey(), Next() and Compare() read records
** directly from main memory.
**
** If the amount of space used to store records in main memory exceeds the
@@ -91150,10 +102292,10 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
** of PMAs may be created by merging existing PMAs together - for example
** merging two or more level-0 PMAs together creates a level-1 PMA.
**
-** The threshold for the amount of main memory to use before flushing
+** The threshold for the amount of main memory to use before flushing
** records to a PMA is roughly the same as the limit configured for the
-** page-cache of the main database. Specifically, the threshold is set to
-** the value returned by "PRAGMA main.page_size" multipled by
+** page-cache of the main database. Specifically, the threshold is set to
+** the value returned by "PRAGMA main.page_size" multiplied by
** that returned by "PRAGMA main.cache_size", in bytes.
**
** If the sorter is running in single-threaded mode, then all PMAs generated
@@ -91170,28 +102312,28 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
** than zero, and (b) worker threads have been enabled at runtime by calling
** "PRAGMA threads=N" with some value of N greater than 0.
**
-** When Rewind() is called, any data remaining in memory is flushed to a
+** When Rewind() is called, any data remaining in memory is flushed to a
** final PMA. So at this point the data is stored in some number of sorted
** PMAs within temporary files on disk.
**
** If there are fewer than SORTER_MAX_MERGE_COUNT PMAs in total and the
** sorter is running in single-threaded mode, then these PMAs are merged
-** incrementally as keys are retreived from the sorter by the VDBE. The
+** incrementally as keys are retrieved from the sorter by the VDBE. The
** MergeEngine object, described in further detail below, performs this
** merge.
**
** Or, if running in multi-threaded mode, then a background thread is
** launched to merge the existing PMAs. Once the background thread has
-** merged T bytes of data into a single sorted PMA, the main thread
+** merged T bytes of data into a single sorted PMA, the main thread
** begins reading keys from that PMA while the background thread proceeds
** with merging the next T bytes of data. And so on.
**
-** Parameter T is set to half the value of the memory threshold used
+** Parameter T is set to half the value of the memory threshold used
** by Write() above to determine when to create a new PMA.
**
-** If there are more than SORTER_MAX_MERGE_COUNT PMAs in total when
-** Rewind() is called, then a hierarchy of incremental-merges is used.
-** First, T bytes of data from the first SORTER_MAX_MERGE_COUNT PMAs on
+** If there are more than SORTER_MAX_MERGE_COUNT PMAs in total when
+** Rewind() is called, then a hierarchy of incremental-merges is used.
+** First, T bytes of data from the first SORTER_MAX_MERGE_COUNT PMAs on
** disk are merged together. Then T bytes of data from the second set, and
** so on, such that no operation ever merges more than SORTER_MAX_MERGE_COUNT
** PMAs at a time. This done is to improve locality.
@@ -91206,7 +102348,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
/* #include "sqliteInt.h" */
/* #include "vdbeInt.h" */
-/*
+/*
** If SQLITE_DEBUG_SORTER_THREADS is defined, this module outputs various
** messages to stderr that may be helpful in understanding the performance
** characteristics of the sorter in multi-threaded mode.
@@ -91235,7 +102377,7 @@ typedef struct SorterList SorterList; /* In-memory list of records */
typedef struct IncrMerger IncrMerger; /* Read & merge multiple PMAs */
/*
-** A container for a temp file handle and the current amount of data
+** A container for a temp file handle and the current amount of data
** stored in the file.
*/
struct SorterFile {
@@ -91254,7 +102396,7 @@ struct SorterFile {
struct SorterList {
SorterRecord *pList; /* Linked list of records */
u8 *aMemory; /* If non-NULL, bulk memory to hold pList */
- int szPMA; /* Size of pList as PMA in bytes */
+ i64 szPMA; /* Size of pList as PMA in bytes */
};
/*
@@ -91275,17 +102417,17 @@ struct SorterList {
** the MergeEngine.nTree variable.
**
** The final (N/2) elements of aTree[] contain the results of comparing
-** pairs of PMA keys together. Element i contains the result of
+** pairs of PMA keys together. Element i contains the result of
** comparing aReadr[2*i-N] and aReadr[2*i-N+1]. Whichever key is smaller, the
-** aTree element is set to the index of it.
+** aTree element is set to the index of it.
**
** For the purposes of this comparison, EOF is considered greater than any
** other key value. If the keys are equal (only possible with two EOF
** values), it doesn't matter which index is stored.
**
-** The (N/4) elements of aTree[] that precede the final (N/2) described
+** The (N/4) elements of aTree[] that precede the final (N/2) described
** above contains the index of the smallest of each block of 4 PmaReaders
-** And so on. So that aTree[1] contains the index of the PmaReader that
+** And so on. So that aTree[1] contains the index of the PmaReader that
** currently points to the smallest key value. aTree[0] is unused.
**
** Example:
@@ -91301,7 +102443,7 @@ struct SorterList {
**
** aTree[] = { X, 5 0, 5 0, 3, 5, 6 }
**
-** The current element is "Apple" (the value of the key indicated by
+** The current element is "Apple" (the value of the key indicated by
** PmaReader 5). When the Next() operation is invoked, PmaReader 5 will
** be advanced to the next key in its segment. Say the next key is
** "Eggplant":
@@ -91339,11 +102481,11 @@ struct MergeEngine {
**
** Essentially, this structure contains all those fields of the VdbeSorter
** structure for which each thread requires a separate instance. For example,
-** each thread requries its own UnpackedRecord object to unpack records in
+** each thread requeries its own UnpackedRecord object to unpack records in
** as part of comparison operations.
**
-** Before a background thread is launched, variable bDone is set to 0. Then,
-** right before it exits, the thread itself sets bDone to 1. This is used for
+** Before a background thread is launched, variable bDone is set to 0. Then,
+** right before it exits, the thread itself sets bDone to 1. This is used for
** two purposes:
**
** 1. When flushing the contents of memory to a level-0 PMA on disk, to
@@ -91363,10 +102505,10 @@ typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int);
struct SortSubtask {
SQLiteThread *pThread; /* Background thread, if any */
int bDone; /* Set if thread is finished but not joined */
+ int nPMA; /* Number of PMAs currently in file */
VdbeSorter *pSorter; /* Sorter that owns this sub-task */
UnpackedRecord *pUnpacked; /* Space to unpack a record */
SorterList list; /* List for thread to write to a PMA */
- int nPMA; /* Number of PMAs currently in file */
SorterCompare xCompare; /* Compare function to use */
SorterFile file; /* Temp file for level-0 PMAs */
SorterFile file2; /* Space for other PMAs */
@@ -91374,7 +102516,7 @@ struct SortSubtask {
/*
-** Main sorter structure. A single instance of this is allocated for each
+** Main sorter structure. A single instance of this is allocated for each
** sorter cursor created by the VDBE.
**
** mxKeysize:
@@ -91411,7 +102553,7 @@ struct VdbeSorter {
** PMA, in sorted order. The next key to be read is cached in nKey/aKey.
** aKey might point into aMap or into aBuffer. If neither of those locations
** contain a contiguous representation of the key, then aAlloc is allocated
-** and the key is copied into aAlloc and aKey is made to poitn to aAlloc.
+** and the key is copied into aAlloc and aKey is made to point to aAlloc.
**
** pFd==0 at EOF.
*/
@@ -91430,21 +102572,21 @@ struct PmaReader {
};
/*
-** Normally, a PmaReader object iterates through an existing PMA stored
+** Normally, a PmaReader object iterates through an existing PMA stored
** within a temp file. However, if the PmaReader.pIncr variable points to
** an object of the following type, it may be used to iterate/merge through
** multiple PMAs simultaneously.
**
-** There are two types of IncrMerger object - single (bUseThread==0) and
-** multi-threaded (bUseThread==1).
+** There are two types of IncrMerger object - single (bUseThread==0) and
+** multi-threaded (bUseThread==1).
**
-** A multi-threaded IncrMerger object uses two temporary files - aFile[0]
-** and aFile[1]. Neither file is allowed to grow to more than mxSz bytes in
-** size. When the IncrMerger is initialized, it reads enough data from
-** pMerger to populate aFile[0]. It then sets variables within the
-** corresponding PmaReader object to read from that file and kicks off
-** a background thread to populate aFile[1] with the next mxSz bytes of
-** sorted record data from pMerger.
+** A multi-threaded IncrMerger object uses two temporary files - aFile[0]
+** and aFile[1]. Neither file is allowed to grow to more than mxSz bytes in
+** size. When the IncrMerger is initialized, it reads enough data from
+** pMerger to populate aFile[0]. It then sets variables within the
+** corresponding PmaReader object to read from that file and kicks off
+** a background thread to populate aFile[1] with the next mxSz bytes of
+** sorted record data from pMerger.
**
** When the PmaReader reaches the end of aFile[0], it blocks until the
** background thread has finished populating aFile[1]. It then exchanges
@@ -91455,7 +102597,7 @@ struct PmaReader {
**
** A single-threaded IncrMerger does not open any temporary files of its
** own. Instead, it has exclusive access to mxSz bytes of space beginning
-** at offset iStartOff of file pTask->file2. And instead of using a
+** at offset iStartOff of file pTask->file2. And instead of using a
** background thread to prepare data for the PmaReader, with a single
** threaded IncrMerger the allocate part of pTask->file2 is "refilled" with
** keys from pMerger by the calling thread whenever the PmaReader runs out
@@ -91567,7 +102709,7 @@ static int vdbePmaReadBlob(
assert( p->aBuffer );
- /* If there is no more data to be read from the buffer, read the next
+ /* If there is no more data to be read from the buffer, read the next
** p->nBuffer bytes of data from the file into it. Or, if there are less
** than p->nBuffer bytes remaining in the PMA, read all remaining data. */
iBuf = p->iReadOff % p->nBuffer;
@@ -91588,11 +102730,11 @@ static int vdbePmaReadBlob(
assert( rc!=SQLITE_IOERR_SHORT_READ );
if( rc!=SQLITE_OK ) return rc;
}
- nAvail = p->nBuffer - iBuf;
+ nAvail = p->nBuffer - iBuf;
if( nByte<=nAvail ){
/* The requested data is available in the in-memory buffer. In this
- ** case there is no need to make a copy of the data, just return a
+ ** case there is no need to make a copy of the data, just return a
** pointer into the buffer to the caller. */
*ppOut = &p->aBuffer[iBuf];
p->iReadOff += nByte;
@@ -91605,7 +102747,7 @@ static int vdbePmaReadBlob(
/* Extend the p->aAlloc[] allocation if required. */
if( p->nAlloc<nByte ){
u8 *aNew;
- int nNew = MAX(128, p->nAlloc*2);
+ sqlite3_int64 nNew = MAX(128, 2*(sqlite3_int64)p->nAlloc);
while( nByte>nNew ) nNew = nNew*2;
aNew = sqlite3Realloc(p->aAlloc, nNew);
if( !aNew ) return SQLITE_NOMEM_BKPT;
@@ -91671,7 +102813,7 @@ static int vdbePmaReadVarint(PmaReader *p, u64 *pnOut){
/*
** Attempt to memory map file pFile. If successful, set *pp to point to the
-** new mapping and return SQLITE_OK. If the mapping is not attempted
+** new mapping and return SQLITE_OK. If the mapping is not attempted
** (because the file is too large or the VFS layer is configured not to use
** mmap), return SQLITE_OK and set *pp to NULL.
**
@@ -91692,7 +102834,7 @@ static int vdbeSorterMapFile(SortSubtask *pTask, SorterFile *pFile, u8 **pp){
/*
** Attach PmaReader pReadr to file pFile (if it is not already attached to
-** that file) and seek it to offset iOff within the file. Return SQLITE_OK
+** that file) and seek it to offset iOff within the file. Return SQLITE_OK
** if successful, or an SQLite error code if an error occurs.
*/
static int vdbePmaReaderSeek(
@@ -91782,11 +102924,11 @@ static int vdbePmaReaderNext(PmaReader *pReadr){
/*
** Initialize PmaReader pReadr to scan through the PMA stored in file pFile
-** starting at offset iStart and ending at offset iEof-1. This function
-** leaves the PmaReader pointing to the first key in the PMA (or EOF if the
+** starting at offset iStart and ending at offset iEof-1. This function
+** leaves the PmaReader pointing to the first key in the PMA (or EOF if the
** PMA is empty).
**
-** If the pnByte parameter is NULL, then it is assumed that the file
+** If the pnByte parameter is NULL, then it is assumed that the file
** contains a single PMA, and that that PMA omits the initial length varint.
*/
static int vdbePmaReaderInit(
@@ -91819,7 +102961,7 @@ static int vdbePmaReaderInit(
/*
** A version of vdbeSorterCompare() that assumes that it has already been
-** determined that the first field of key1 is equal to the first field of
+** determined that the first field of key1 is equal to the first field of
** key2.
*/
static int vdbeSorterCompareTail(
@@ -91837,7 +102979,7 @@ static int vdbeSorterCompareTail(
}
/*
-** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2,
+** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2,
** size nKey2 bytes). Use (pTask->pKeyInfo) for the collation sequences
** used by the comparison. Return the result of the comparison.
**
@@ -91883,8 +103025,8 @@ static int vdbeSorterCompareText(
int n2;
int res;
- getVarint32(&p1[1], n1);
- getVarint32(&p2[1], n2);
+ getVarint32NR(&p1[1], n1);
+ getVarint32NR(&p2[1], n2);
res = memcmp(v1, v2, (MIN(n1, n2) - 13)/2);
if( res==0 ){
res = n1 - n2;
@@ -91897,7 +103039,8 @@ static int vdbeSorterCompareText(
);
}
}else{
- if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
+ assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
+ if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
res = res * -1;
}
}
@@ -91965,7 +103108,8 @@ static int vdbeSorterCompareInt(
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
);
}
- }else if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
+ }else if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
+ assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
res = res * -1;
}
@@ -91981,7 +103125,7 @@ static int vdbeSorterCompareInt(
** is non-zero and the sorter is able to guarantee a stable sort, nField
** is used instead. This is used when sorting records for a CREATE INDEX
** statement. In this case, keys are always delivered to the sorter in
-** order of the primary key, which happens to be make up the final part
+** order of the primary key, which happens to be make up the final part
** of the records being sorted. So if the sort is stable, there is never
** any reason to compare PK fields and they can be ignored for a small
** performance boost.
@@ -92026,7 +103170,8 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
}
#endif
- assert( pCsr->pKeyInfo && pCsr->pBtx==0 );
+ assert( pCsr->pKeyInfo );
+ assert( !pCsr->isEphemeral );
assert( pCsr->eCurType==CURTYPE_SORTER );
szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*);
sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask);
@@ -92036,13 +103181,16 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
if( pSorter==0 ){
rc = SQLITE_NOMEM_BKPT;
}else{
+ Btree *pBt = db->aDb[0].pBt;
pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz);
memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo);
pKeyInfo->db = 0;
if( nField && nWorker==0 ){
pKeyInfo->nKeyField = nField;
}
- pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
+ sqlite3BtreeEnter(pBt);
+ pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(pBt);
+ sqlite3BtreeLeave(pBt);
pSorter->nTask = nWorker + 1;
pSorter->iPrev = (u8)(nWorker - 1);
pSorter->bUseThreads = (pSorter->nTask>1);
@@ -92078,8 +103226,9 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
}
}
- if( pKeyInfo->nAllField<13
+ if( pKeyInfo->nAllField<13
&& (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl)
+ && (pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL)==0
){
pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
}
@@ -92102,7 +103251,7 @@ static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){
}
/*
-** Free all resources owned by the object indicated by argument pTask. All
+** Free all resources owned by the object indicated by argument pTask. All
** fields of *pTask are zeroed before returning.
*/
static void vdbeSortSubtaskCleanup(sqlite3 *db, SortSubtask *pTask){
@@ -92135,8 +103284,9 @@ static void vdbeSorterWorkDebug(SortSubtask *pTask, const char *zEvent){
fprintf(stderr, "%lld:%d %s\n", t, iTask, zEvent);
}
static void vdbeSorterRewindDebug(const char *zEvent){
- i64 t;
- sqlite3OsCurrentTimeInt64(sqlite3_vfs_find(0), &t);
+ i64 t = 0;
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
+ if( ALWAYS(pVfs) ) sqlite3OsCurrentTimeInt64(pVfs, &t);
fprintf(stderr, "%lld:X %s\n", t, zEvent);
}
static void vdbeSorterPopulateDebug(
@@ -92201,7 +103351,7 @@ static int vdbeSorterCreateThread(
}
/*
-** Join all outstanding threads launched by SorterWrite() to create
+** Join all outstanding threads launched by SorterWrite() to create
** level-0 PMAs.
*/
static int vdbeSorterJoinAll(VdbeSorter *pSorter, int rcin){
@@ -92210,10 +103360,10 @@ static int vdbeSorterJoinAll(VdbeSorter *pSorter, int rcin){
/* This function is always called by the main user thread.
**
- ** If this function is being called after SorterRewind() has been called,
+ ** If this function is being called after SorterRewind() has been called,
** it is possible that thread pSorter->aTask[pSorter->nTask-1].pThread
** is currently attempt to join one of the other threads. To avoid a race
- ** condition where this thread also attempts to join the same object, join
+ ** condition where this thread also attempts to join the same object, join
** thread pSorter->aTask[pSorter->nTask-1].pThread first. */
for(i=pSorter->nTask-1; i>=0; i--){
SortSubtask *pTask = &pSorter->aTask[i];
@@ -92350,7 +103500,7 @@ static void vdbeSorterExtendFile(sqlite3 *db, sqlite3_file *pFd, i64 nByte){
sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_CHUNK_SIZE, &chunksize);
sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_SIZE_HINT, &nByte);
sqlite3OsFetch(pFd, 0, (int)nByte, &p);
- sqlite3OsUnfetch(pFd, 0, p);
+ if( p ) sqlite3OsUnfetch(pFd, 0, p);
}
}
#else
@@ -92385,8 +103535,8 @@ static int vdbeSorterOpenTempFile(
}
/*
-** If it has not already been allocated, allocate the UnpackedRecord
-** structure at pTask->pUnpacked. Return SQLITE_OK if successful (or
+** If it has not already been allocated, allocate the UnpackedRecord
+** structure at pTask->pUnpacked. Return SQLITE_OK if successful (or
** if no allocation was required), or SQLITE_NOMEM otherwise.
*/
static int vdbeSortAllocUnpacked(SortSubtask *pTask){
@@ -92449,32 +103599,28 @@ static SorterCompare vdbeSorterGetCompare(VdbeSorter *p){
if( p->typeMask==SORTER_TYPE_INTEGER ){
return vdbeSorterCompareInt;
}else if( p->typeMask==SORTER_TYPE_TEXT ){
- return vdbeSorterCompareText;
+ return vdbeSorterCompareText;
}
return vdbeSorterCompare;
}
/*
-** Sort the linked list of records headed at pTask->pList. Return
-** SQLITE_OK if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if
+** Sort the linked list of records headed at pTask->pList. Return
+** SQLITE_OK if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if
** an error occurs.
*/
static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){
int i;
- SorterRecord **aSlot;
SorterRecord *p;
int rc;
+ SorterRecord *aSlot[64];
rc = vdbeSortAllocUnpacked(pTask);
if( rc!=SQLITE_OK ) return rc;
p = pList->pList;
pTask->xCompare = vdbeSorterGetCompare(pTask->pSorter);
-
- aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *));
- if( !aSlot ){
- return SQLITE_NOMEM_BKPT;
- }
+ memset(aSlot, 0, sizeof(aSlot));
while( p ){
SorterRecord *pNext;
@@ -92499,15 +103645,14 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){
}
p = 0;
- for(i=0; i<64; i++){
+ for(i=0; i<ArraySize(aSlot); i++){
if( aSlot[i]==0 ) continue;
p = p ? vdbeSorterMerge(pTask, p, aSlot[i]) : aSlot[i];
}
pList->pList = p;
- sqlite3_free(aSlot);
- assert( pTask->pUnpacked->errCode==SQLITE_OK
- || pTask->pUnpacked->errCode==SQLITE_NOMEM
+ assert( pTask->pUnpacked->errCode==SQLITE_OK
+ || pTask->pUnpacked->errCode==SQLITE_NOMEM
);
return pTask->pUnpacked->errCode;
}
@@ -92548,8 +103693,8 @@ static void vdbePmaWriteBlob(PmaWriter *p, u8 *pData, int nData){
memcpy(&p->aBuffer[p->iBufEnd], &pData[nData-nRem], nCopy);
p->iBufEnd += nCopy;
if( p->iBufEnd==p->nBuffer ){
- p->eFWErr = sqlite3OsWrite(p->pFd,
- &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
+ p->eFWErr = sqlite3OsWrite(p->pFd,
+ &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
p->iWriteOff + p->iBufStart
);
p->iBufStart = p->iBufEnd = 0;
@@ -92564,7 +103709,7 @@ static void vdbePmaWriteBlob(PmaWriter *p, u8 *pData, int nData){
/*
** Flush any buffered data to disk and clean up the PMA-writer object.
** The results of using the PMA-writer after this call are undefined.
-** Return SQLITE_OK if flushing the buffered data succeeds or is not
+** Return SQLITE_OK if flushing the buffered data succeeds or is not
** required. Otherwise, return an SQLite error code.
**
** Before returning, set *piEof to the offset immediately following the
@@ -92573,8 +103718,8 @@ static void vdbePmaWriteBlob(PmaWriter *p, u8 *pData, int nData){
static int vdbePmaWriterFinish(PmaWriter *p, i64 *piEof){
int rc;
if( p->eFWErr==0 && ALWAYS(p->aBuffer) && p->iBufEnd>p->iBufStart ){
- p->eFWErr = sqlite3OsWrite(p->pFd,
- &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
+ p->eFWErr = sqlite3OsWrite(p->pFd,
+ &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
p->iWriteOff + p->iBufStart
);
}
@@ -92586,11 +103731,11 @@ static int vdbePmaWriterFinish(PmaWriter *p, i64 *piEof){
}
/*
-** Write value iVal encoded as a varint to the PMA. Return
+** Write value iVal encoded as a varint to the PMA. Return
** SQLITE_OK if successful, or an SQLite error code if an error occurs.
*/
static void vdbePmaWriteVarint(PmaWriter *p, u64 iVal){
- int nByte;
+ int nByte;
u8 aByte[10];
nByte = sqlite3PutVarint(aByte, iVal);
vdbePmaWriteBlob(p, aByte, nByte);
@@ -92598,7 +103743,7 @@ static void vdbePmaWriteVarint(PmaWriter *p, u64 iVal){
/*
** Write the current contents of in-memory linked-list pList to a level-0
-** PMA in the temp file belonging to sub-task pTask. Return SQLITE_OK if
+** PMA in the temp file belonging to sub-task pTask. Return SQLITE_OK if
** successful, or an SQLite error code otherwise.
**
** The format of a PMA is:
@@ -92606,8 +103751,8 @@ static void vdbePmaWriteVarint(PmaWriter *p, u64 iVal){
** * A varint. This varint contains the total number of bytes of content
** in the PMA (not including the varint itself).
**
-** * One or more records packed end-to-end in order of ascending keys.
-** Each record consists of a varint followed by a blob of data (the
+** * One or more records packed end-to-end in order of ascending keys.
+** Each record consists of a varint followed by a blob of data (the
** key). The varint is the number of bytes in the blob of data.
*/
static int vdbeSorterListToPMA(SortSubtask *pTask, SorterList *pList){
@@ -92616,7 +103761,7 @@ static int vdbeSorterListToPMA(SortSubtask *pTask, SorterList *pList){
PmaWriter writer; /* Object used to write to the file */
#ifdef SQLITE_DEBUG
- /* Set iSz to the expected size of file pTask->file after writing the PMA.
+ /* Set iSz to the expected size of file pTask->file after writing the PMA.
** This is used by an assert() statement at the end of this function. */
i64 iSz = pList->szPMA + sqlite3VarintLen(pList->szPMA) + pTask->file.iEof;
#endif
@@ -92769,7 +103914,7 @@ static int vdbeSorterFlushPMA(VdbeSorter *pSorter){
SortSubtask *pTask = 0; /* Thread context used to create new PMA */
int nWorker = (pSorter->nTask-1);
- /* Set the flag to indicate that at least one PMA has been written.
+ /* Set the flag to indicate that at least one PMA has been written.
** Or will be, anyhow. */
pSorter->bUsePMA = 1;
@@ -92779,7 +103924,7 @@ static int vdbeSorterFlushPMA(VdbeSorter *pSorter){
** the background thread from a sub-tasks previous turn is still running,
** skip it. If the first (pSorter->nTask-1) sub-tasks are all still busy,
** fall back to using the final sub-task. The first (pSorter->nTask-1)
- ** sub-tasks are prefered as they use background threads - the final
+ ** sub-tasks are preferred as they use background threads - the final
** sub-task uses the main thread. */
for(i=0; i<nWorker; i++){
int iTest = (pSorter->iPrev + i + 1) % nWorker;
@@ -92796,13 +103941,16 @@ static int vdbeSorterFlushPMA(VdbeSorter *pSorter){
rc = vdbeSorterListToPMA(&pSorter->aTask[nWorker], &pSorter->list);
}else{
/* Launch a background thread for this operation */
- u8 *aMem = pTask->list.aMemory;
- void *pCtx = (void*)pTask;
+ u8 *aMem;
+ void *pCtx;
+ assert( pTask!=0 );
assert( pTask->pThread==0 && pTask->bDone==0 );
assert( pTask->list.pList==0 );
assert( pTask->list.aMemory==0 || pSorter->list.aMemory!=0 );
+ aMem = pTask->list.aMemory;
+ pCtx = (void*)pTask;
pSorter->iPrev = (u8)(pTask - pSorter->aTask);
pTask->list = pSorter->list;
pSorter->list.pList = 0;
@@ -92834,13 +103982,13 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
int rc = SQLITE_OK; /* Return Code */
SorterRecord *pNew; /* New list element */
int bFlush; /* True to flush contents of memory to PMA */
- int nReq; /* Bytes of memory required */
- int nPMA; /* Bytes of PMA space required */
+ i64 nReq; /* Bytes of memory required */
+ i64 nPMA; /* Bytes of PMA space required */
int t; /* serial type of first record field */
assert( pCsr->eCurType==CURTYPE_SORTER );
pSorter = pCsr->uc.pSorter;
- getVarint32((const u8*)&pVal->z[1], t);
+ getVarint32NR((const u8*)&pVal->z[1], t);
if( t>0 && t<10 && t!=7 ){
pSorter->typeMask &= SORTER_TYPE_INTEGER;
}else if( t>10 && (t & 0x01) ){
@@ -92857,14 +104005,14 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
** If using the single large allocation mode (pSorter->aMemory!=0), then
** flush the contents of memory to a new PMA if (a) at least one value is
** already in memory and (b) the new value will not fit in memory.
- **
+ **
** Or, if using separate allocations for each record, flush the contents
** of memory to a PMA if either of the following are true:
**
- ** * The total memory allocated for the in-memory list is greater
+ ** * The total memory allocated for the in-memory list is greater
** than (page-size * cache-size), or
**
- ** * The total memory allocated for the in-memory list is greater
+ ** * The total memory allocated for the in-memory list is greater
** than (page-size * 10) and sqlite3HeapNearlyFull() returns true.
*/
nReq = pVal->n + sizeof(SorterRecord);
@@ -92896,15 +104044,19 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
if( nMin>pSorter->nMemory ){
u8 *aNew;
- int iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory;
- int nNew = pSorter->nMemory * 2;
+ sqlite3_int64 nNew = 2 * (sqlite3_int64)pSorter->nMemory;
+ int iListOff = -1;
+ if( pSorter->list.pList ){
+ iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory;
+ }
while( nNew < nMin ) nNew = nNew*2;
if( nNew > pSorter->mxPmaSize ) nNew = pSorter->mxPmaSize;
if( nNew < nMin ) nNew = nMin;
-
aNew = sqlite3Realloc(pSorter->list.aMemory, nNew);
if( !aNew ) return SQLITE_NOMEM_BKPT;
- pSorter->list.pList = (SorterRecord*)&aNew[iListOff];
+ if( iListOff>=0 ){
+ pSorter->list.pList = (SorterRecord*)&aNew[iListOff];
+ }
pSorter->list.aMemory = aNew;
pSorter->nMemory = nNew;
}
@@ -92999,11 +104151,11 @@ static int vdbeIncrBgPopulate(IncrMerger *pIncr){
** aFile[0] such that the PmaReader should start rereading it from the
** beginning.
**
-** For single-threaded objects, this is accomplished by literally reading
-** keys from pIncr->pMerger and repopulating aFile[0].
+** For single-threaded objects, this is accomplished by literally reading
+** keys from pIncr->pMerger and repopulating aFile[0].
**
-** For multi-threaded objects, all that is required is to wait until the
-** background thread is finished (if it is not already) and then swap
+** For multi-threaded objects, all that is required is to wait until the
+** background thread is finished (if it is not already) and then swap
** aFile[0] and aFile[1] in place. If the contents of pMerger have not
** been exhausted, this function also launches a new background thread
** to populate the new aFile[1].
@@ -93066,6 +104218,7 @@ static int vdbeIncrMergerNew(
vdbeMergeEngineFree(pMerger);
rc = SQLITE_NOMEM_BKPT;
}
+ assert( *ppOut!=0 || rc!=SQLITE_OK );
return rc;
}
@@ -93143,7 +104296,7 @@ static void vdbeMergeEngineCompare(
#define INCRINIT_TASK 1
#define INCRINIT_ROOT 2
-/*
+/*
** Forward reference required as the vdbeIncrMergeInit() and
** vdbePmaReaderIncrInit() routines are called mutually recursively when
** building a merge tree.
@@ -93152,7 +104305,7 @@ static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode);
/*
** Initialize the MergeEngine object passed as the second argument. Once this
-** function returns, the first key of merged data may be read from the
+** function returns, the first key of merged data may be read from the
** MergeEngine object in the usual fashion.
**
** If argument eMode is INCRINIT_ROOT, then it is assumed that any IncrMerge
@@ -93162,8 +104315,8 @@ static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode);
** required is to call vdbePmaReaderNext() on each PmaReader to point it at
** its first key.
**
-** Otherwise, if eMode is any value other than INCRINIT_ROOT, then use
-** vdbePmaReaderIncrMergeInit() to initialize each PmaReader that feeds data
+** Otherwise, if eMode is any value other than INCRINIT_ROOT, then use
+** vdbePmaReaderIncrMergeInit() to initialize each PmaReader that feeds data
** to pMerger.
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
@@ -93218,19 +104371,19 @@ static int vdbeMergeEngineInit(
** object at (pReadr->pIncr).
**
** If argument eMode is set to INCRINIT_NORMAL, then all PmaReaders
-** in the sub-tree headed by pReadr are also initialized. Data is then
-** loaded into the buffers belonging to pReadr and it is set to point to
+** in the sub-tree headed by pReadr are also initialized. Data is then
+** loaded into the buffers belonging to pReadr and it is set to point to
** the first key in its range.
**
** If argument eMode is set to INCRINIT_TASK, then pReadr is guaranteed
** to be a multi-threaded PmaReader and this function is being called in a
-** background thread. In this case all PmaReaders in the sub-tree are
+** background thread. In this case all PmaReaders in the sub-tree are
** initialized as for INCRINIT_NORMAL and the aFile[1] buffer belonging to
** pReadr is populated. However, pReadr itself is not set up to point
** to its first key. A call to vdbePmaReaderNext() is still required to do
-** that.
+** that.
**
-** The reason this function does not call vdbePmaReaderNext() immediately
+** The reason this function does not call vdbePmaReaderNext() immediately
** in the INCRINIT_TASK case is that vdbePmaReaderNext() assumes that it has
** to block on thread (pTask->thread) before accessing aFile[1]. But, since
** this entire function is being run by thread (pTask->thread), that will
@@ -93255,7 +104408,7 @@ static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){
rc = vdbeMergeEngineInit(pTask, pIncr->pMerger, eMode);
- /* Set up the required files for pIncr. A multi-theaded IncrMerge object
+ /* Set up the required files for pIncr. A multi-threaded IncrMerge object
** requires two temp files to itself, whereas a single-threaded object
** only requires a region of pTask->file2. */
if( rc==SQLITE_OK ){
@@ -93286,12 +104439,12 @@ static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){
if( rc==SQLITE_OK && pIncr->bUseThread ){
/* Use the current thread to populate aFile[1], even though this
** PmaReader is multi-threaded. If this is an INCRINIT_TASK object,
- ** then this function is already running in background thread
- ** pIncr->pTask->thread.
+ ** then this function is already running in background thread
+ ** pIncr->pTask->thread.
**
- ** If this is the INCRINIT_ROOT object, then it is running in the
+ ** If this is the INCRINIT_ROOT object, then it is running in the
** main VDBE thread. But that is Ok, as that thread cannot return
- ** control to the VDBE or proceed with anything useful until the
+ ** control to the VDBE or proceed with anything useful until the
** first results are ready from this merger object anyway.
*/
assert( eMode==INCRINIT_ROOT || eMode==INCRINIT_TASK );
@@ -93308,7 +104461,7 @@ static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){
#if SQLITE_MAX_WORKER_THREADS>0
/*
-** The main routine for vdbePmaReaderIncrMergeInit() operations run in
+** The main routine for vdbePmaReaderIncrMergeInit() operations run in
** background threads.
*/
static void *vdbePmaReaderBgIncrInit(void *pCtx){
@@ -93326,8 +104479,8 @@ static void *vdbePmaReaderBgIncrInit(void *pCtx){
** (if pReadr->pIncr==0), then this function is a no-op. Otherwise, it invokes
** the vdbePmaReaderIncrMergeInit() function with the parameters passed to
** this routine to initialize the incremental merge.
-**
-** If the IncrMerger object is multi-threaded (IncrMerger.bUseThread==1),
+**
+** If the IncrMerger object is multi-threaded (IncrMerger.bUseThread==1),
** then a background thread is launched to call vdbePmaReaderIncrMergeInit().
** Or, if the IncrMerger is single threaded, the same function is called
** using the current thread.
@@ -93357,7 +104510,7 @@ static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode){
** to NULL and return an SQLite error code.
**
** When this function is called, *piOffset is set to the offset of the
-** first PMA to read from pTask->file. Assuming no error occurs, it is
+** first PMA to read from pTask->file. Assuming no error occurs, it is
** set to the offset immediately following the last byte of the last
** PMA before returning. If an error does occur, then the final value of
** *piOffset is undefined.
@@ -93467,12 +104620,12 @@ static int vdbeSorterAddToTree(
/*
** This function is called as part of a SorterRewind() operation on a sorter
** that has already written two or more level-0 PMAs to one or more temp
-** files. It builds a tree of MergeEngine/IncrMerger/PmaReader objects that
+** files. It builds a tree of MergeEngine/IncrMerger/PmaReader objects that
** can be used to incrementally merge all PMAs on disk.
**
** If successful, SQLITE_OK is returned and *ppOut set to point to the
** MergeEngine object at the root of the tree before returning. Or, if an
-** error occurs, an SQLite error code is returned and the final value
+** error occurs, an SQLite error code is returned and the final value
** of *ppOut is undefined.
*/
static int vdbeSorterMergeTreeBuild(
@@ -93484,8 +104637,8 @@ static int vdbeSorterMergeTreeBuild(
int iTask;
#if SQLITE_MAX_WORKER_THREADS>0
- /* If the sorter uses more than one task, then create the top-level
- ** MergeEngine here. This MergeEngine will read data from exactly
+ /* If the sorter uses more than one task, then create the top-level
+ ** MergeEngine here. This MergeEngine will read data from exactly
** one PmaReader per sub-task. */
assert( pSorter->bUseThreads || pSorter->nTask==1 );
if( pSorter->nTask>1 ){
@@ -93594,7 +104747,7 @@ static int vdbeSorterSetupMerge(VdbeSorter *pSorter){
}
for(iTask=0; rc==SQLITE_OK && iTask<pSorter->nTask; iTask++){
/* Check that:
- **
+ **
** a) The incremental merge object is configured to use the
** right task, and
** b) If it is using task (nTask-1), it is configured to run
@@ -93657,7 +104810,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *pCsr, int *pbEof){
return rc;
}
- /* Write the current in-memory list to a PMA. When the VdbeSorterWrite()
+ /* Write the current in-memory list to a PMA. When the VdbeSorterWrite()
** function flushes the contents of memory to disk, it immediately always
** creates a new list consisting of a single key immediately afterwards.
** So the list is never empty at this point. */
@@ -93669,7 +104822,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *pCsr, int *pbEof){
vdbeSorterRewindDebug("rewind");
- /* Assuming no errors have occurred, set up a merger structure to
+ /* Assuming no errors have occurred, set up a merger structure to
** incrementally read and merge all remaining PMAs. */
assert( pSorter->pReader==0 );
if( rc==SQLITE_OK ){
@@ -93723,7 +104876,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr){
}
/*
-** Return a pointer to a buffer owned by the sorter that contains the
+** Return a pointer to a buffer owned by the sorter that contains the
** current key.
*/
static void *vdbeSorterRowkey(
@@ -93823,6 +104976,455 @@ SQLITE_PRIVATE int sqlite3VdbeSorterCompare(
}
/************** End of vdbesort.c ********************************************/
+/************** Begin file vdbevtab.c ****************************************/
+/*
+** 2020-03-23
+**
+** 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 virtual-tables for examining the bytecode content
+** of a prepared statement.
+*/
+/* #include "sqliteInt.h" */
+#if defined(SQLITE_ENABLE_BYTECODE_VTAB) && !defined(SQLITE_OMIT_VIRTUALTABLE)
+/* #include "vdbeInt.h" */
+
+/* An instance of the bytecode() table-valued function.
+*/
+typedef struct bytecodevtab bytecodevtab;
+struct bytecodevtab {
+ sqlite3_vtab base; /* Base class - must be first */
+ sqlite3 *db; /* Database connection */
+ int bTablesUsed; /* 2 for tables_used(). 0 for bytecode(). */
+};
+
+/* A cursor for scanning through the bytecode
+*/
+typedef struct bytecodevtab_cursor bytecodevtab_cursor;
+struct bytecodevtab_cursor {
+ sqlite3_vtab_cursor base; /* Base class - must be first */
+ sqlite3_stmt *pStmt; /* The statement whose bytecode is displayed */
+ int iRowid; /* The rowid of the output table */
+ int iAddr; /* Address */
+ int needFinalize; /* Cursors owns pStmt and must finalize it */
+ int showSubprograms; /* Provide a listing of subprograms */
+ Op *aOp; /* Operand array */
+ char *zP4; /* Rendered P4 value */
+ const char *zType; /* tables_used.type */
+ const char *zSchema; /* tables_used.schema */
+ const char *zName; /* tables_used.name */
+ Mem sub; /* Subprograms */
+};
+
+/*
+** Create a new bytecode() table-valued function.
+*/
+static int bytecodevtabConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ bytecodevtab *pNew;
+ int rc;
+ int isTabUsed = pAux!=0;
+ const char *azSchema[2] = {
+ /* bytecode() schema */
+ "CREATE TABLE x("
+ "addr INT,"
+ "opcode TEXT,"
+ "p1 INT,"
+ "p2 INT,"
+ "p3 INT,"
+ "p4 TEXT,"
+ "p5 INT,"
+ "comment TEXT,"
+ "subprog TEXT,"
+ "nexec INT,"
+ "ncycle INT,"
+ "stmt HIDDEN"
+ ");",
+
+ /* Tables_used() schema */
+ "CREATE TABLE x("
+ "type TEXT,"
+ "schema TEXT,"
+ "name TEXT,"
+ "wr INT,"
+ "subprog TEXT,"
+ "stmt HIDDEN"
+ ");"
+ };
+
+ (void)argc;
+ (void)argv;
+ (void)pzErr;
+ rc = sqlite3_declare_vtab(db, azSchema[isTabUsed]);
+ if( rc==SQLITE_OK ){
+ pNew = sqlite3_malloc( sizeof(*pNew) );
+ *ppVtab = (sqlite3_vtab*)pNew;
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ pNew->db = db;
+ pNew->bTablesUsed = isTabUsed*2;
+ }
+ return rc;
+}
+
+/*
+** This method is the destructor for bytecodevtab objects.
+*/
+static int bytecodevtabDisconnect(sqlite3_vtab *pVtab){
+ bytecodevtab *p = (bytecodevtab*)pVtab;
+ sqlite3_free(p);
+ return SQLITE_OK;
+}
+
+/*
+** Constructor for a new bytecodevtab_cursor object.
+*/
+static int bytecodevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ bytecodevtab *pVTab = (bytecodevtab*)p;
+ bytecodevtab_cursor *pCur;
+ pCur = sqlite3_malloc( sizeof(*pCur) );
+ if( pCur==0 ) return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(*pCur));
+ sqlite3VdbeMemInit(&pCur->sub, pVTab->db, 1);
+ *ppCursor = &pCur->base;
+ return SQLITE_OK;
+}
+
+/*
+** Clear all internal content from a bytecodevtab cursor.
+*/
+static void bytecodevtabCursorClear(bytecodevtab_cursor *pCur){
+ sqlite3_free(pCur->zP4);
+ pCur->zP4 = 0;
+ sqlite3VdbeMemRelease(&pCur->sub);
+ sqlite3VdbeMemSetNull(&pCur->sub);
+ if( pCur->needFinalize ){
+ sqlite3_finalize(pCur->pStmt);
+ }
+ pCur->pStmt = 0;
+ pCur->needFinalize = 0;
+ pCur->zType = 0;
+ pCur->zSchema = 0;
+ pCur->zName = 0;
+}
+
+/*
+** Destructor for a bytecodevtab_cursor.
+*/
+static int bytecodevtabClose(sqlite3_vtab_cursor *cur){
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
+ bytecodevtabCursorClear(pCur);
+ sqlite3_free(pCur);
+ return SQLITE_OK;
+}
+
+
+/*
+** Advance a bytecodevtab_cursor to its next row of output.
+*/
+static int bytecodevtabNext(sqlite3_vtab_cursor *cur){
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
+ bytecodevtab *pTab = (bytecodevtab*)cur->pVtab;
+ int rc;
+ if( pCur->zP4 ){
+ sqlite3_free(pCur->zP4);
+ pCur->zP4 = 0;
+ }
+ if( pCur->zName ){
+ pCur->zName = 0;
+ pCur->zType = 0;
+ pCur->zSchema = 0;
+ }
+ rc = sqlite3VdbeNextOpcode(
+ (Vdbe*)pCur->pStmt,
+ pCur->showSubprograms ? &pCur->sub : 0,
+ pTab->bTablesUsed,
+ &pCur->iRowid,
+ &pCur->iAddr,
+ &pCur->aOp);
+ if( rc!=SQLITE_OK ){
+ sqlite3VdbeMemSetNull(&pCur->sub);
+ pCur->aOp = 0;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the cursor has been moved off of the last
+** row of output.
+*/
+static int bytecodevtabEof(sqlite3_vtab_cursor *cur){
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
+ return pCur->aOp==0;
+}
+
+/*
+** Return values of columns for the row at which the bytecodevtab_cursor
+** is currently pointing.
+*/
+static int bytecodevtabColumn(
+ sqlite3_vtab_cursor *cur, /* The cursor */
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
+ int i /* Which column to return */
+){
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
+ bytecodevtab *pVTab = (bytecodevtab*)cur->pVtab;
+ Op *pOp = pCur->aOp + pCur->iAddr;
+ if( pVTab->bTablesUsed ){
+ if( i==4 ){
+ i = 8;
+ }else{
+ if( i<=2 && pCur->zType==0 ){
+ Schema *pSchema;
+ HashElem *k;
+ int iDb = pOp->p3;
+ Pgno iRoot = (Pgno)pOp->p2;
+ sqlite3 *db = pVTab->db;
+ pSchema = db->aDb[iDb].pSchema;
+ pCur->zSchema = db->aDb[iDb].zDbSName;
+ for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
+ Table *pTab = (Table*)sqliteHashData(k);
+ if( !IsVirtual(pTab) && pTab->tnum==iRoot ){
+ pCur->zName = pTab->zName;
+ pCur->zType = "table";
+ break;
+ }
+ }
+ if( pCur->zName==0 ){
+ for(k=sqliteHashFirst(&pSchema->idxHash); k; k=sqliteHashNext(k)){
+ Index *pIdx = (Index*)sqliteHashData(k);
+ if( pIdx->tnum==iRoot ){
+ pCur->zName = pIdx->zName;
+ pCur->zType = "index";
+ }
+ }
+ }
+ }
+ i += 20;
+ }
+ }
+ switch( i ){
+ case 0: /* addr */
+ sqlite3_result_int(ctx, pCur->iAddr);
+ break;
+ case 1: /* opcode */
+ sqlite3_result_text(ctx, (char*)sqlite3OpcodeName(pOp->opcode),
+ -1, SQLITE_STATIC);
+ break;
+ case 2: /* p1 */
+ sqlite3_result_int(ctx, pOp->p1);
+ break;
+ case 3: /* p2 */
+ sqlite3_result_int(ctx, pOp->p2);
+ break;
+ case 4: /* p3 */
+ sqlite3_result_int(ctx, pOp->p3);
+ break;
+ case 5: /* p4 */
+ case 7: /* comment */
+ if( pCur->zP4==0 ){
+ pCur->zP4 = sqlite3VdbeDisplayP4(pVTab->db, pOp);
+ }
+ if( i==5 ){
+ sqlite3_result_text(ctx, pCur->zP4, -1, SQLITE_STATIC);
+ }else{
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ char *zCom = sqlite3VdbeDisplayComment(pVTab->db, pOp, pCur->zP4);
+ sqlite3_result_text(ctx, zCom, -1, sqlite3_free);
+#endif
+ }
+ break;
+ case 6: /* p5 */
+ sqlite3_result_int(ctx, pOp->p5);
+ break;
+ case 8: { /* subprog */
+ Op *aOp = pCur->aOp;
+ assert( aOp[0].opcode==OP_Init );
+ assert( aOp[0].p4.z==0 || strncmp(aOp[0].p4.z,"-" "- ",3)==0 );
+ if( pCur->iRowid==pCur->iAddr+1 ){
+ break; /* Result is NULL for the main program */
+ }else if( aOp[0].p4.z!=0 ){
+ sqlite3_result_text(ctx, aOp[0].p4.z+3, -1, SQLITE_STATIC);
+ }else{
+ sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC);
+ }
+ break;
+ }
+
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ case 9: /* nexec */
+ sqlite3_result_int(ctx, pOp->nExec);
+ break;
+ case 10: /* ncycle */
+ sqlite3_result_int(ctx, pOp->nCycle);
+ break;
+#else
+ case 9: /* nexec */
+ case 10: /* ncycle */
+ sqlite3_result_int(ctx, 0);
+ break;
+#endif
+
+ case 20: /* tables_used.type */
+ sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC);
+ break;
+ case 21: /* tables_used.schema */
+ sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC);
+ break;
+ case 22: /* tables_used.name */
+ sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC);
+ break;
+ case 23: /* tables_used.wr */
+ sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite);
+ break;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Return the rowid for the current row. In this implementation, the
+** rowid is the same as the output value.
+*/
+static int bytecodevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
+ *pRowid = pCur->iRowid;
+ return SQLITE_OK;
+}
+
+/*
+** Initialize a cursor.
+**
+** idxNum==0 means show all subprograms
+** idxNum==1 means show only the main bytecode and omit subprograms.
+*/
+static int bytecodevtabFilter(
+ sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor;
+ bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab;
+ int rc = SQLITE_OK;
+ (void)idxStr;
+
+ bytecodevtabCursorClear(pCur);
+ pCur->iRowid = 0;
+ pCur->iAddr = 0;
+ pCur->showSubprograms = idxNum==0;
+ assert( argc==1 );
+ if( sqlite3_value_type(argv[0])==SQLITE_TEXT ){
+ const char *zSql = (const char*)sqlite3_value_text(argv[0]);
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pStmt, 0);
+ pCur->needFinalize = 1;
+ }
+ }else{
+ pCur->pStmt = (sqlite3_stmt*)sqlite3_value_pointer(argv[0],"stmt-pointer");
+ }
+ if( pCur->pStmt==0 ){
+ pVTab->base.zErrMsg = sqlite3_mprintf(
+ "argument to %s() is not a valid SQL statement",
+ pVTab->bTablesUsed ? "tables_used" : "bytecode"
+ );
+ rc = SQLITE_ERROR;
+ }else{
+ bytecodevtabNext(pVtabCursor);
+ }
+ return rc;
+}
+
+/*
+** We must have a single stmt=? constraint that will be passed through
+** into the xFilter method. If there is no valid stmt=? constraint,
+** then return an SQLITE_CONSTRAINT error.
+*/
+static int bytecodevtabBestIndex(
+ sqlite3_vtab *tab,
+ sqlite3_index_info *pIdxInfo
+){
+ int i;
+ int rc = SQLITE_CONSTRAINT;
+ struct sqlite3_index_constraint *p;
+ bytecodevtab *pVTab = (bytecodevtab*)tab;
+ int iBaseCol = pVTab->bTablesUsed ? 4 : 10;
+ pIdxInfo->estimatedCost = (double)100;
+ pIdxInfo->estimatedRows = 100;
+ pIdxInfo->idxNum = 0;
+ for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){
+ if( p->usable==0 ) continue;
+ if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==iBaseCol+1 ){
+ rc = SQLITE_OK;
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
+ }
+ if( p->op==SQLITE_INDEX_CONSTRAINT_ISNULL && p->iColumn==iBaseCol ){
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ pIdxInfo->idxNum = 1;
+ }
+ }
+ return rc;
+}
+
+/*
+** This following structure defines all the methods for the
+** virtual table.
+*/
+static sqlite3_module bytecodevtabModule = {
+ /* iVersion */ 0,
+ /* xCreate */ 0,
+ /* xConnect */ bytecodevtabConnect,
+ /* xBestIndex */ bytecodevtabBestIndex,
+ /* xDisconnect */ bytecodevtabDisconnect,
+ /* xDestroy */ 0,
+ /* xOpen */ bytecodevtabOpen,
+ /* xClose */ bytecodevtabClose,
+ /* xFilter */ bytecodevtabFilter,
+ /* xNext */ bytecodevtabNext,
+ /* xEof */ bytecodevtabEof,
+ /* xColumn */ bytecodevtabColumn,
+ /* xRowid */ bytecodevtabRowid,
+ /* xUpdate */ 0,
+ /* xBegin */ 0,
+ /* xSync */ 0,
+ /* xCommit */ 0,
+ /* xRollback */ 0,
+ /* xFindMethod */ 0,
+ /* xRename */ 0,
+ /* xSavepoint */ 0,
+ /* xRelease */ 0,
+ /* xRollbackTo */ 0,
+ /* xShadowName */ 0,
+ /* xIntegrity */ 0
+};
+
+
+SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){
+ int rc;
+ rc = sqlite3_create_module(db, "bytecode", &bytecodevtabModule, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db);
+ }
+ return rc;
+}
+#elif defined(SQLITE_ENABLE_BYTECODE_VTAB)
+SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){ return SQLITE_OK; }
+#endif /* SQLITE_ENABLE_BYTECODE_VTAB */
+
+/************** End of vdbevtab.c ********************************************/
/************** Begin file memjournal.c **************************************/
/*
** 2008 October 7
@@ -93896,7 +105498,6 @@ struct MemJournal {
int nChunkSize; /* In-memory chunk-size */
int nSpill; /* Bytes of data before flushing */
- int nSize; /* Bytes of data currently in memory */
FileChunk *pFirst; /* Head of in-memory chunk-list */
FilePoint endpoint; /* Pointer to the end of the file */
FilePoint readpoint; /* Pointer to the end of the last xRead() */
@@ -93922,18 +105523,13 @@ static int memjrnlRead(
int iChunkOffset;
FileChunk *pChunk;
-#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
- || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
if( (iAmt+iOfst)>p->endpoint.iOffset ){
return SQLITE_IOERR_SHORT_READ;
}
-#endif
-
- assert( (iAmt+iOfst)<=p->endpoint.iOffset );
assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 );
if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
sqlite3_int64 iOff = 0;
- for(pChunk=p->pFirst;
+ for(pChunk=p->pFirst;
ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst;
pChunk=pChunk->pNext
){
@@ -93962,14 +105558,13 @@ static int memjrnlRead(
/*
** Free the list of FileChunk structures headed at MemJournal.pFirst.
*/
-static void memjrnlFreeChunks(MemJournal *p){
+static void memjrnlFreeChunks(FileChunk *pFirst){
FileChunk *pIter;
FileChunk *pNext;
- for(pIter=p->pFirst; pIter; pIter=pNext){
+ for(pIter=pFirst; pIter; pIter=pNext){
pNext = pIter->pNext;
sqlite3_free(pIter);
- }
- p->pFirst = 0;
+ }
}
/*
@@ -93996,7 +105591,7 @@ static int memjrnlCreateFile(MemJournal *p){
}
if( rc==SQLITE_OK ){
/* No error has occurred. Free the in-memory buffers. */
- memjrnlFreeChunks(&copy);
+ memjrnlFreeChunks(copy.pFirst);
}
}
if( rc!=SQLITE_OK ){
@@ -94011,6 +105606,9 @@ static int memjrnlCreateFile(MemJournal *p){
}
+/* Forward reference */
+static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size);
+
/*
** Write data to the file.
*/
@@ -94040,23 +105638,21 @@ static int memjrnlWrite(
** access writes are not required. The only exception to this is when
** the in-memory journal is being used by a connection using the
** atomic-write optimization. In this case the first 28 bytes of the
- ** journal file may be written as part of committing the transaction. */
- assert( iOfst==p->endpoint.iOffset || iOfst==0 );
-#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
- || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
+ ** journal file may be written as part of committing the transaction. */
+ assert( iOfst<=p->endpoint.iOffset );
+ if( iOfst>0 && iOfst!=p->endpoint.iOffset ){
+ memjrnlTruncate(pJfd, iOfst);
+ }
if( iOfst==0 && p->pFirst ){
assert( p->nChunkSize>iAmt );
memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt);
- }else
-#else
- assert( iOfst>0 || p->pFirst==0 );
-#endif
- {
+ }else{
while( nWrite>0 ){
FileChunk *pChunk = p->endpoint.pChunk;
int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize);
int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset);
+ assert( pChunk!=0 || iChunkOffset==0 );
if( iChunkOffset==0 ){
/* New chunk is required to extend the file. */
FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize));
@@ -94071,15 +105667,15 @@ static int memjrnlWrite(
assert( !p->pFirst );
p->pFirst = pNew;
}
- p->endpoint.pChunk = pNew;
+ pChunk = p->endpoint.pChunk = pNew;
}
- memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace);
+ assert( pChunk!=0 );
+ memcpy((u8*)pChunk->zChunk + iChunkOffset, zWrite, iSpace);
zWrite += iSpace;
nWrite -= iSpace;
p->endpoint.iOffset += iSpace;
}
- p->nSize = iAmt + iOfst;
}
}
@@ -94087,19 +105683,29 @@ static int memjrnlWrite(
}
/*
-** Truncate the file.
-**
-** If the journal file is already on disk, truncate it there. Or, if it
-** is still in main memory but is being truncated to zero bytes in size,
-** ignore
+** Truncate the in-memory file.
*/
static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
MemJournal *p = (MemJournal *)pJfd;
- if( ALWAYS(size==0) ){
- memjrnlFreeChunks(p);
- p->nSize = 0;
- p->endpoint.pChunk = 0;
- p->endpoint.iOffset = 0;
+ assert( p->endpoint.pChunk==0 || p->endpoint.pChunk->pNext==0 );
+ if( size<p->endpoint.iOffset ){
+ FileChunk *pIter = 0;
+ if( size==0 ){
+ memjrnlFreeChunks(p->pFirst);
+ p->pFirst = 0;
+ }else{
+ i64 iOff = p->nChunkSize;
+ for(pIter=p->pFirst; ALWAYS(pIter) && iOff<size; pIter=pIter->pNext){
+ iOff += p->nChunkSize;
+ }
+ if( ALWAYS(pIter) ){
+ memjrnlFreeChunks(pIter->pNext);
+ pIter->pNext = 0;
+ }
+ }
+
+ p->endpoint.pChunk = pIter;
+ p->endpoint.iOffset = size;
p->readpoint.pChunk = 0;
p->readpoint.iOffset = 0;
}
@@ -94111,15 +105717,15 @@ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
*/
static int memjrnlClose(sqlite3_file *pJfd){
MemJournal *p = (MemJournal *)pJfd;
- memjrnlFreeChunks(p);
+ memjrnlFreeChunks(p->pFirst);
return SQLITE_OK;
}
/*
** Sync the file.
**
-** If the real file has been created, call its xSync method. Otherwise,
-** syncing an in-memory journal is a no-op.
+** If the real file has been created, call its xSync method. Otherwise,
+** syncing an in-memory journal is a no-op.
*/
static int memjrnlSync(sqlite3_file *pJfd, int flags){
UNUSED_PARAMETER2(pJfd, flags);
@@ -94160,11 +105766,11 @@ static const struct sqlite3_io_methods MemJournalMethods = {
0 /* xUnfetch */
};
-/*
-** Open a journal file.
+/*
+** Open a journal file.
**
-** The behaviour of the journal file depends on the value of parameter
-** nSpill. If nSpill is 0, then the journal file is always create and
+** The behaviour of the journal file depends on the value of parameter
+** nSpill. If nSpill is 0, then the journal file is always create and
** accessed using the underlying VFS. If nSpill is less than zero, then
** all content is always stored in main-memory. Finally, if nSpill is a
** positive value, then the journal file is initially created in-memory
@@ -94181,6 +105787,8 @@ SQLITE_PRIVATE int sqlite3JournalOpen(
){
MemJournal *p = (MemJournal*)pJfd;
+ assert( zName || nSpill<0 || (flags & SQLITE_OPEN_EXCLUSIVE) );
+
/* Zero the file-handle object. If nSpill was passed zero, initialize
** it using the sqlite3OsOpen() function of the underlying VFS. In this
** case none of the code in this module is executed as a result of calls
@@ -94197,7 +105805,7 @@ SQLITE_PRIVATE int sqlite3JournalOpen(
assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) );
}
- p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods;
+ pJfd->pMethods = (const sqlite3_io_methods*)&MemJournalMethods;
p->nSpill = nSpill;
p->flags = flags;
p->zJournal = zName;
@@ -94215,15 +105823,15 @@ SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *pJfd){
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
|| defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
/*
-** If the argument p points to a MemJournal structure that is not an
+** If the argument p points to a MemJournal structure that is not an
** in-memory-only journal file (i.e. is one that was opened with a +ve
-** nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying
+** nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying
** file has not yet been created, create it now.
*/
SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *pJfd){
int rc = SQLITE_OK;
MemJournal *p = (MemJournal*)pJfd;
- if( p->pMethod==&MemJournalMethods && (
+ if( pJfd->pMethods==&MemJournalMethods && (
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
p->nSpill>0
#else
@@ -94251,7 +105859,7 @@ SQLITE_PRIVATE int sqlite3JournalIsInMemory(sqlite3_file *p){
return p->pMethods==&MemJournalMethods;
}
-/*
+/*
** Return the number of bytes required to store a JournalFile that uses vfs
** pVfs to create the underlying on-disk files.
*/
@@ -94280,6 +105888,31 @@ SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
/* #include <string.h> */
+#if !defined(SQLITE_OMIT_WINDOWFUNC)
+/*
+** Walk all expressions linked into the list of Window objects passed
+** as the second argument.
+*/
+static int walkWindowList(Walker *pWalker, Window *pList, int bOneOnly){
+ Window *pWin;
+ for(pWin=pList; pWin; pWin=pWin->pNextWin){
+ int rc;
+ rc = sqlite3WalkExprList(pWalker, pWin->pOrderBy);
+ if( rc ) return WRC_Abort;
+ rc = sqlite3WalkExprList(pWalker, pWin->pPartition);
+ if( rc ) return WRC_Abort;
+ rc = sqlite3WalkExpr(pWalker, pWin->pFilter);
+ if( rc ) return WRC_Abort;
+ rc = sqlite3WalkExpr(pWalker, pWin->pStart);
+ if( rc ) return WRC_Abort;
+ rc = sqlite3WalkExpr(pWalker, pWin->pEnd);
+ if( rc ) return WRC_Abort;
+ if( bOneOnly ) break;
+ }
+ return WRC_Continue;
+}
+#endif
+
/*
** Walk an expression tree. Invoke the callback once for each node
** of the expression, while descending. (In other words, the callback
@@ -94299,7 +105932,7 @@ SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
** The return value from this routine is WRC_Abort to abandon the tree walk
** and WRC_Continue to continue.
*/
-static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
+SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3WalkExprNN(Walker *pWalker, Expr *pExpr){
int rc;
testcase( ExprHasProperty(pExpr, EP_TokenOnly) );
testcase( ExprHasProperty(pExpr, EP_Reduced) );
@@ -94307,31 +105940,34 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
rc = pWalker->xExprCallback(pWalker, pExpr);
if( rc ) return rc & WRC_Abort;
if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
- if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
- assert( pExpr->x.pList==0 || pExpr->pRight==0 );
+ assert( pExpr->x.pList==0 || pExpr->pRight==0 );
+ if( pExpr->pLeft && sqlite3WalkExprNN(pWalker, pExpr->pLeft) ){
+ return WRC_Abort;
+ }
if( pExpr->pRight ){
+ assert( !ExprHasProperty(pExpr, EP_WinFunc) );
pExpr = pExpr->pRight;
continue;
- }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ }else if( ExprUseXSelect(pExpr) ){
+ assert( !ExprHasProperty(pExpr, EP_WinFunc) );
if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
- }else if( pExpr->x.pList ){
- if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
- }
+ }else{
+ if( pExpr->x.pList ){
+ if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( ExprHasProperty(pExpr, EP_WinFunc) ){
- Window *pWin = pExpr->y.pWin;
- if( sqlite3WalkExprList(pWalker, pWin->pPartition) ) return WRC_Abort;
- if( sqlite3WalkExprList(pWalker, pWin->pOrderBy) ) return WRC_Abort;
- if( sqlite3WalkExpr(pWalker, pWin->pFilter) ) return WRC_Abort;
- }
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ if( walkWindowList(pWalker, pExpr->y.pWin, 1) ) return WRC_Abort;
+ }
#endif
+ }
}
break;
}
return WRC_Continue;
}
SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
- return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue;
+ return pExpr ? sqlite3WalkExprNN(pWalker,pExpr) : WRC_Continue;
}
/*
@@ -94350,6 +105986,16 @@ SQLITE_PRIVATE int sqlite3WalkExprList(Walker *pWalker, ExprList *p){
}
/*
+** This is a no-op callback for Walker->xSelectCallback2. If this
+** callback is set, then the Select->pWinDefn list is traversed.
+*/
+SQLITE_PRIVATE void sqlite3WalkWinDefnDummyCallback(Walker *pWalker, Select *p){
+ UNUSED_PARAMETER(pWalker);
+ UNUSED_PARAMETER(p);
+ /* No-op */
+}
+
+/*
** Walk all expressions associated with SELECT statement p. Do
** not invoke the SELECT callback on p, but do (of course) invoke
** any expr callbacks and SELECT callbacks that come from subqueries.
@@ -94362,6 +106008,22 @@ SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){
if( sqlite3WalkExpr(pWalker, p->pHaving) ) return WRC_Abort;
if( sqlite3WalkExprList(pWalker, p->pOrderBy) ) return WRC_Abort;
if( sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort;
+#if !defined(SQLITE_OMIT_WINDOWFUNC)
+ if( p->pWinDefn ){
+ Parse *pParse;
+ if( pWalker->xSelectCallback2==sqlite3WalkWinDefnDummyCallback
+ || ((pParse = pWalker->pParse)!=0 && IN_RENAME_OBJECT)
+#ifndef SQLITE_OMIT_CTE
+ || pWalker->xSelectCallback2==sqlite3SelectPopWith
+#endif
+ ){
+ /* The following may return WRC_Abort if there are unresolvable
+ ** symbols (e.g. a table that does not exist) in a window definition. */
+ int rc = walkWindowList(pWalker, p->pWinDefn, 0);
+ return rc;
+ }
+ }
+#endif
return WRC_Continue;
}
@@ -94369,33 +106031,34 @@ SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){
** Walk the parse trees associated with all subqueries in the
** FROM clause of SELECT statement p. Do not invoke the select
** callback on p, but do invoke it on each FROM clause subquery
-** and on any subqueries further down in the tree. Return
+** and on any subqueries further down in the tree. Return
** WRC_Abort or WRC_Continue;
*/
SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
SrcList *pSrc;
int i;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
pSrc = p->pSrc;
- assert( pSrc!=0 );
- for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
- if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){
- return WRC_Abort;
- }
- if( pItem->fg.isTabFunc
- && sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg)
- ){
- return WRC_Abort;
+ if( ALWAYS(pSrc) ){
+ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
+ if( pItem->pSelect && sqlite3WalkSelect(pWalker, pItem->pSelect) ){
+ return WRC_Abort;
+ }
+ if( pItem->fg.isTabFunc
+ && sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg)
+ ){
+ return WRC_Abort;
+ }
}
}
return WRC_Continue;
-}
+}
/*
** Call sqlite3WalkExpr() for every expression in Select statement p.
** Invoke sqlite3WalkSelect() for subqueries in the FROM clause and
-** on the compound select chain, p->pPrior.
+** on the compound select chain, p->pPrior.
**
** If it is not NULL, the xSelectCallback() callback is invoked before
** the walk of the expressions and FROM clause. The xSelectCallback2()
@@ -94429,6 +106092,43 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){
return WRC_Continue;
}
+/* Increase the walkerDepth when entering a subquery, and
+** decrease when leaving the subquery.
+*/
+SQLITE_PRIVATE int sqlite3WalkerDepthIncrease(Walker *pWalker, Select *pSelect){
+ UNUSED_PARAMETER(pSelect);
+ pWalker->walkerDepth++;
+ return WRC_Continue;
+}
+SQLITE_PRIVATE void sqlite3WalkerDepthDecrease(Walker *pWalker, Select *pSelect){
+ UNUSED_PARAMETER(pSelect);
+ pWalker->walkerDepth--;
+}
+
+
+/*
+** No-op routine for the parse-tree walker.
+**
+** When this routine is the Walker.xExprCallback then expression trees
+** are walked without any actions being taken at each node. Presumably,
+** when this routine is used for Walker.xExprCallback then
+** Walker.xSelectCallback is set to do something useful for every
+** subquery in the parser tree.
+*/
+SQLITE_PRIVATE int sqlite3ExprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
+ UNUSED_PARAMETER2(NotUsed, NotUsed2);
+ return WRC_Continue;
+}
+
+/*
+** No-op routine for the parse-tree walker for SELECT statements.
+** subquery in the parser tree.
+*/
+SQLITE_PRIVATE int sqlite3SelectWalkNoop(Walker *NotUsed, Select *NotUsed2){
+ UNUSED_PARAMETER2(NotUsed, NotUsed2);
+ return WRC_Continue;
+}
+
/************** End of walker.c **********************************************/
/************** Begin file resolve.c *****************************************/
/*
@@ -94450,6 +106150,11 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){
/* #include "sqliteInt.h" */
/*
+** Magic table number to mean the EXCLUDED table in an UPSERT statement.
+*/
+#define EXCLUDED_TABLE_NUMBER 2
+
+/*
** Walk the expression tree pExpr and increase the aggregate function
** depth (the Expr.op2 field) by N on every TK_AGG_FUNCTION node.
** This needs to occur when copying a TK_AGG_FUNCTION node from an
@@ -94457,6 +106162,8 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){
**
** incrAggFunctionDepth(pExpr,n) is the main routine. incrAggDepth(..)
** is a helper function - a callback for the tree walker.
+**
+** See also the sqlite3WindowExtraAggFuncDepth() routine in window.c
*/
static int incrAggDepth(Walker *pWalker, Expr *pExpr){
if( pExpr->op==TK_AGG_FUNCTION ) pExpr->op2 += pWalker->u.n;
@@ -94496,7 +106203,6 @@ static void resolveAlias(
ExprList *pEList, /* A result set */
int iCol, /* A column in the result set. 0..pEList->nExpr-1 */
Expr *pExpr, /* Transform this into an alias to the result set */
- const char *zType, /* "GROUP" or "ORDER" or "" */
int nSubquery /* Number of subqueries that the label is moving */
){
Expr *pOrig; /* The iCol-th column of the result set */
@@ -94508,64 +106214,60 @@ static void resolveAlias(
assert( pOrig!=0 );
db = pParse->db;
pDup = sqlite3ExprDup(db, pOrig, 0);
- if( pDup!=0 ){
- if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
+ if( db->mallocFailed ){
+ sqlite3ExprDelete(db, pDup);
+ pDup = 0;
+ }else{
+ Expr temp;
+ incrAggFunctionDepth(pDup, nSubquery);
if( pExpr->op==TK_COLLATE ){
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
}
- ExprSetProperty(pDup, EP_Alias);
-
- /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This
- ** prevents ExprDelete() from deleting the Expr structure itself,
- ** allowing it to be repopulated by the memcpy() on the following line.
- ** The pExpr->u.zToken might point into memory that will be freed by the
- ** sqlite3DbFree(db, pDup) on the last line of this block, so be sure to
- ** make a copy of the token before doing the sqlite3DbFree().
- */
- ExprSetProperty(pExpr, EP_Static);
- sqlite3ExprDelete(db, pExpr);
- memcpy(pExpr, pDup, sizeof(*pExpr));
- if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){
- assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 );
- pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
- pExpr->flags |= EP_MemToken;
+ memcpy(&temp, pDup, sizeof(Expr));
+ memcpy(pDup, pExpr, sizeof(Expr));
+ memcpy(pExpr, &temp, sizeof(Expr));
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ if( ALWAYS(pExpr->y.pWin!=0) ){
+ pExpr->y.pWin->pOwner = pExpr;
+ }
}
- sqlite3DbFree(db, pDup);
+ sqlite3ExprDeferredDelete(pParse, pDup);
}
- ExprSetProperty(pExpr, EP_Alias);
}
-
/*
-** Return TRUE if the name zCol occurs anywhere in the USING clause.
+** Subqueries store the original database, table and column names for their
+** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN",
+** and mark the expression-list item by setting ExprList.a[].fg.eEName
+** to ENAME_TAB.
**
-** Return FALSE if the USING clause is NULL or if it does not contain
-** zCol.
-*/
-static int nameInUsingClause(IdList *pUsing, const char *zCol){
- if( pUsing ){
- int k;
- for(k=0; k<pUsing->nId; k++){
- if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1;
- }
- }
- return 0;
-}
-
-/*
-** Subqueries stores the original database, table and column names for their
-** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN".
-** Check to see if the zSpan given to this routine matches the zDb, zTab,
-** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will
-** match anything.
+** Check to see if the zSpan/eEName of the expression-list item passed to this
+** routine matches the zDb, zTab, and zCol. If any of zDb, zTab, and zCol are
+** NULL then those fields will match anything. Return true if there is a match,
+** or false otherwise.
+**
+** SF_NestedFrom subqueries also store an entry for the implicit rowid (or
+** _rowid_, or oid) column by setting ExprList.a[].fg.eEName to ENAME_ROWID,
+** and setting zSpan to "DATABASE.TABLE.<rowid-alias>". This type of pItem
+** argument matches if zCol is a rowid alias. If it is not NULL, (*pbRowid)
+** is set to 1 if there is this kind of match.
*/
-SQLITE_PRIVATE int sqlite3MatchSpanName(
- const char *zSpan,
+SQLITE_PRIVATE int sqlite3MatchEName(
+ const struct ExprList_item *pItem,
const char *zCol,
const char *zTab,
- const char *zDb
+ const char *zDb,
+ int *pbRowid
){
int n;
+ const char *zSpan;
+ int eEName = pItem->fg.eEName;
+ if( eEName!=ENAME_TAB && (eEName!=ENAME_ROWID || NEVER(pbRowid==0)) ){
+ return 0;
+ }
+ assert( pbRowid==0 || *pbRowid==0 );
+ zSpan = pItem->zEName;
for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){
return 0;
@@ -94576,15 +106278,110 @@ SQLITE_PRIVATE int sqlite3MatchSpanName(
return 0;
}
zSpan += n+1;
- if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){
- return 0;
+ if( zCol ){
+ if( eEName==ENAME_TAB && sqlite3StrICmp(zSpan, zCol)!=0 ) return 0;
+ if( eEName==ENAME_ROWID && sqlite3IsRowid(zCol)==0 ) return 0;
}
+ if( eEName==ENAME_ROWID ) *pbRowid = 1;
return 1;
}
/*
+** Return TRUE if the double-quoted string mis-feature should be supported.
+*/
+static int areDoubleQuotedStringsEnabled(sqlite3 *db, NameContext *pTopNC){
+ if( db->init.busy ) return 1; /* Always support for legacy schemas */
+ if( pTopNC->ncFlags & NC_IsDDL ){
+ /* Currently parsing a DDL statement */
+ if( sqlite3WritableSchema(db) && (db->flags & SQLITE_DqsDML)!=0 ){
+ return 1;
+ }
+ return (db->flags & SQLITE_DqsDDL)!=0;
+ }else{
+ /* Currently parsing a DML statement */
+ return (db->flags & SQLITE_DqsDML)!=0;
+ }
+}
+
+/*
+** The argument is guaranteed to be a non-NULL Expr node of type TK_COLUMN.
+** return the appropriate colUsed mask.
+*/
+SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr *pExpr){
+ int n;
+ Table *pExTab;
+
+ n = pExpr->iColumn;
+ assert( ExprUseYTab(pExpr) );
+ pExTab = pExpr->y.pTab;
+ assert( pExTab!=0 );
+ assert( n < pExTab->nCol );
+ if( (pExTab->tabFlags & TF_HasGenerated)!=0
+ && (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0
+ ){
+ testcase( pExTab->nCol==BMS-1 );
+ testcase( pExTab->nCol==BMS );
+ return pExTab->nCol>=BMS ? ALLBITS : MASKBIT(pExTab->nCol)-1;
+ }else{
+ testcase( n==BMS-1 );
+ testcase( n==BMS );
+ if( n>=BMS ) n = BMS-1;
+ return ((Bitmask)1)<<n;
+ }
+}
+
+/*
+** Create a new expression term for the column specified by pMatch and
+** iColumn. Append this new expression term to the FULL JOIN Match set
+** in *ppList. Create a new *ppList if this is the first term in the
+** set.
+*/
+static void extendFJMatch(
+ Parse *pParse, /* Parsing context */
+ ExprList **ppList, /* ExprList to extend */
+ SrcItem *pMatch, /* Source table containing the column */
+ i16 iColumn /* The column number */
+){
+ Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0);
+ if( pNew ){
+ pNew->iTable = pMatch->iCursor;
+ pNew->iColumn = iColumn;
+ pNew->y.pTab = pMatch->pTab;
+ assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 );
+ ExprSetProperty(pNew, EP_CanBeNull);
+ *ppList = sqlite3ExprListAppend(pParse, *ppList, pNew);
+ }
+}
+
+/*
+** Return TRUE (non-zero) if zTab is a valid name for the schema table pTab.
+*/
+static SQLITE_NOINLINE int isValidSchemaTableName(
+ const char *zTab, /* Name as it appears in the SQL */
+ Table *pTab, /* The schema table we are trying to match */
+ Schema *pSchema /* non-NULL if a database qualifier is present */
+){
+ const char *zLegacy;
+ assert( pTab!=0 );
+ assert( pTab->tnum==1 );
+ if( sqlite3StrNICmp(zTab, "sqlite_", 7)!=0 ) return 0;
+ zLegacy = pTab->zName;
+ if( strcmp(zLegacy+7, &LEGACY_TEMP_SCHEMA_TABLE[7])==0 ){
+ if( sqlite3StrICmp(zTab+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){
+ return 1;
+ }
+ if( pSchema==0 ) return 0;
+ if( sqlite3StrICmp(zTab+7, &LEGACY_SCHEMA_TABLE[7])==0 ) return 1;
+ if( sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1;
+ }else{
+ if( sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1;
+ }
+ return 0;
+}
+
+/*
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
-** that name in the set of source tables in pSrcList and make the pExpr
+** that name in the set of source tables in pSrcList and make the pExpr
** expression node refer back to that source column. The following changes
** are made to pExpr:
**
@@ -94619,19 +106416,21 @@ static int lookupName(
){
int i, j; /* Loop counters */
int cnt = 0; /* Number of matching column names */
- int cntTab = 0; /* Number of matching table names */
+ int cntTab = 0; /* Number of potential "rowid" matches */
int nSubquery = 0; /* How many levels of subquery */
sqlite3 *db = pParse->db; /* The database connection */
- struct SrcList_item *pItem; /* Use for looping over pSrcList items */
- struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
+ SrcItem *pItem; /* Use for looping over pSrcList items */
+ SrcItem *pMatch = 0; /* The matching pSrcList item */
NameContext *pTopNC = pNC; /* First namecontext in the list */
Schema *pSchema = 0; /* Schema of the expression */
int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */
- Table *pTab = 0; /* Table hold the row */
+ Table *pTab = 0; /* Table holding the row */
Column *pCol; /* A column of pTab */
+ ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */
assert( pNC ); /* the name context cannot be NULL. */
assert( zCol ); /* The Z in X.Y.Z cannot be NULL */
+ assert( zDb==0 || zTab!=0 );
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
/* Initialize the node to no-match */
@@ -94659,6 +106458,12 @@ static int lookupName(
break;
}
}
+ if( i==db->nDb && sqlite3StrICmp("main", zDb)==0 ){
+ /* This branch is taken when the main database has been renamed
+ ** using SQLITE_DBCONFIG_MAINDBNAME. */
+ pSchema = db->aDb[0].pSchema;
+ zDb = db->aDb[0].zDbSName;
+ }
}
}
@@ -94670,63 +106475,139 @@ static int lookupName(
if( pSrcList ){
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
+ u8 hCol;
pTab = pItem->pTab;
assert( pTab!=0 && pTab->zName!=0 );
- assert( pTab->nCol>0 );
- if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){
+ assert( pTab->nCol>0 || pParse->nErr );
+ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) );
+ if( pItem->fg.isNestedFrom ){
+ /* In this case, pItem is a subquery that has been formed from a
+ ** parenthesized subset of the FROM clause terms. Example:
+ ** .... FROM t1 LEFT JOIN (t2 RIGHT JOIN t3 USING(x)) USING(y) ...
+ ** \_________________________/
+ ** This pItem -------------^
+ */
int hit = 0;
+ assert( pItem->pSelect!=0 );
pEList = pItem->pSelect->pEList;
+ assert( pEList!=0 );
+ assert( pEList->nExpr==pTab->nCol );
for(j=0; j<pEList->nExpr; j++){
- if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){
+ int bRowid = 0; /* True if possible rowid match */
+ if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb, &bRowid) ){
+ continue;
+ }
+ if( bRowid==0 ){
+ if( cnt>0 ){
+ if( pItem->fg.isUsing==0
+ || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
+ ){
+ /* Two or more tables have the same column name which is
+ ** not joined by USING. This is an error. Signal as much
+ ** by clearing pFJMatch and letting cnt go above 1. */
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else
+ if( (pItem->fg.jointype & JT_RIGHT)==0 ){
+ /* An INNER or LEFT JOIN. Use the left-most table */
+ continue;
+ }else
+ if( (pItem->fg.jointype & JT_LEFT)==0 ){
+ /* A RIGHT JOIN. Use the right-most table */
+ cnt = 0;
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else{
+ /* For a FULL JOIN, we must construct a coalesce() func */
+ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
+ }
+ }
cnt++;
- cntTab = 2;
- pMatch = pItem;
- pExpr->iColumn = j;
hit = 1;
+ }else if( cnt>0 ){
+ /* This is a potential rowid match, but there has already been
+ ** a real match found. So this can be ignored. */
+ continue;
}
+ cntTab++;
+ pMatch = pItem;
+ pExpr->iColumn = j;
+ pEList->a[j].fg.bUsed = 1;
+
+ /* rowid cannot be part of a USING clause - assert() this. */
+ assert( bRowid==0 || pEList->a[j].fg.bUsingTerm==0 );
+ if( pEList->a[j].fg.bUsingTerm ) break;
}
if( hit || zTab==0 ) continue;
}
- if( zDb && pTab->pSchema!=pSchema ){
- continue;
- }
+ assert( zDb==0 || zTab!=0 );
if( zTab ){
- const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName;
- assert( zTabName!=0 );
- if( sqlite3StrICmp(zTabName, zTab)!=0 ){
- continue;
+ if( zDb ){
+ if( pTab->pSchema!=pSchema ) continue;
+ if( pSchema==0 && strcmp(zDb,"*")!=0 ) continue;
+ }
+ if( pItem->zAlias!=0 ){
+ if( sqlite3StrICmp(zTab, pItem->zAlias)!=0 ){
+ continue;
+ }
+ }else if( sqlite3StrICmp(zTab, pTab->zName)!=0 ){
+ if( pTab->tnum!=1 ) continue;
+ if( !isValidSchemaTableName(zTab, pTab, pSchema) ) continue;
}
+ assert( ExprUseYTab(pExpr) );
if( IN_RENAME_OBJECT && pItem->zAlias ){
sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab);
}
}
- if( 0==(cntTab++) ){
- pMatch = pItem;
- }
+ hCol = sqlite3StrIHash(zCol);
for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
- if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
- /* If there has been exactly one prior match and this match
- ** is for the right-hand table of a NATURAL JOIN or is in a
- ** USING clause, then skip this match.
- */
- if( cnt==1 ){
- if( pItem->fg.jointype & JT_NATURAL ) continue;
- if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
+ if( pCol->hName==hCol
+ && sqlite3StrICmp(pCol->zCnName, zCol)==0
+ ){
+ if( cnt>0 ){
+ if( pItem->fg.isUsing==0
+ || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
+ ){
+ /* Two or more tables have the same column name which is
+ ** not joined by USING. This is an error. Signal as much
+ ** by clearing pFJMatch and letting cnt go above 1. */
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else
+ if( (pItem->fg.jointype & JT_RIGHT)==0 ){
+ /* An INNER or LEFT JOIN. Use the left-most table */
+ continue;
+ }else
+ if( (pItem->fg.jointype & JT_LEFT)==0 ){
+ /* A RIGHT JOIN. Use the right-most table */
+ cnt = 0;
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else{
+ /* For a FULL JOIN, we must construct a coalesce() func */
+ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
+ }
}
cnt++;
pMatch = pItem;
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
+ if( pItem->fg.isNestedFrom ){
+ sqlite3SrcItemColumnUsed(pItem, j);
+ }
break;
}
}
+ if( 0==cnt && VisibleRowid(pTab) ){
+ cntTab++;
+ pMatch = pItem;
+ }
}
if( pMatch ){
pExpr->iTable = pMatch->iCursor;
+ assert( ExprUseYTab(pExpr) );
pExpr->y.pTab = pMatch->pTab;
- /* RIGHT JOIN not (yet) supported */
- assert( (pMatch->fg.jointype & JT_RIGHT)==0 );
- if( (pMatch->fg.jointype & JT_LEFT)!=0 ){
+ if( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ){
ExprSetProperty(pExpr, EP_CanBeNull);
}
pSchema = pExpr->y.pTab->pSchema;
@@ -94734,41 +106615,53 @@ static int lookupName(
} /* if( pSrcList ) */
#if !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT)
- /* If we have not already resolved the name, then maybe
+ /* If we have not already resolved the name, then maybe
** it is a new.* or old.* trigger argument reference. Or
- ** maybe it is an excluded.* from an upsert.
+ ** maybe it is an excluded.* from an upsert. Or maybe it is
+ ** a reference in the RETURNING clause to a table being modified.
*/
- if( zDb==0 && zTab!=0 && cntTab==0 ){
+ if( cnt==0 && zDb==0 ){
pTab = 0;
#ifndef SQLITE_OMIT_TRIGGER
if( pParse->pTriggerTab!=0 ){
int op = pParse->eTriggerOp;
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
- if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
+ if( pParse->bReturning ){
+ if( (pNC->ncFlags & NC_UBaseReg)!=0
+ && ALWAYS(zTab==0
+ || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0)
+ ){
+ pExpr->iTable = op!=TK_DELETE;
+ pTab = pParse->pTriggerTab;
+ }
+ }else if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){
pExpr->iTable = 1;
pTab = pParse->pTriggerTab;
- }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
+ }else if( op!=TK_INSERT && zTab && sqlite3StrICmp("old",zTab)==0 ){
pExpr->iTable = 0;
pTab = pParse->pTriggerTab;
}
}
#endif /* SQLITE_OMIT_TRIGGER */
#ifndef SQLITE_OMIT_UPSERT
- if( (pNC->ncFlags & NC_UUpsert)!=0 ){
+ if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab!=0 ){
Upsert *pUpsert = pNC->uNC.pUpsert;
if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){
pTab = pUpsert->pUpsertSrc->a[0].pTab;
- pExpr->iTable = 2;
+ pExpr->iTable = EXCLUDED_TABLE_NUMBER;
}
}
#endif /* SQLITE_OMIT_UPSERT */
- if( pTab ){
+ if( pTab ){
int iCol;
+ u8 hCol = sqlite3StrIHash(zCol);
pSchema = pTab->pSchema;
cntTab++;
for(iCol=0, pCol=pTab->aCol; iCol<pTab->nCol; iCol++, pCol++){
- if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
+ if( pCol->hName==hCol
+ && sqlite3StrICmp(pCol->zCnName, zCol)==0
+ ){
if( iCol==pTab->iPKey ){
iCol = -1;
}
@@ -94781,37 +106674,48 @@ static int lookupName(
}
if( iCol<pTab->nCol ){
cnt++;
+ pMatch = 0;
#ifndef SQLITE_OMIT_UPSERT
- if( pExpr->iTable==2 ){
+ if( pExpr->iTable==EXCLUDED_TABLE_NUMBER ){
testcase( iCol==(-1) );
+ assert( ExprUseYTab(pExpr) );
if( IN_RENAME_OBJECT ){
pExpr->iColumn = iCol;
pExpr->y.pTab = pTab;
eNewExprOp = TK_COLUMN;
}else{
- pExpr->iTable = pNC->uNC.pUpsert->regData + iCol;
+ pExpr->iTable = pNC->uNC.pUpsert->regData +
+ sqlite3TableColumnToStorage(pTab, iCol);
eNewExprOp = TK_REGISTER;
- ExprSetProperty(pExpr, EP_Alias);
}
}else
#endif /* SQLITE_OMIT_UPSERT */
{
-#ifndef SQLITE_OMIT_TRIGGER
- if( iCol<0 ){
- pExpr->affinity = SQLITE_AFF_INTEGER;
- }else if( pExpr->iTable==0 ){
- testcase( iCol==31 );
- testcase( iCol==32 );
- pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
- }else{
- testcase( iCol==31 );
- testcase( iCol==32 );
- pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
- }
+ assert( ExprUseYTab(pExpr) );
pExpr->y.pTab = pTab;
- pExpr->iColumn = (i16)iCol;
- eNewExprOp = TK_TRIGGER;
+ if( pParse->bReturning ){
+ eNewExprOp = TK_REGISTER;
+ pExpr->op2 = TK_COLUMN;
+ pExpr->iColumn = iCol;
+ pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable +
+ sqlite3TableColumnToStorage(pTab, iCol) + 1;
+ }else{
+ pExpr->iColumn = (i16)iCol;
+ eNewExprOp = TK_TRIGGER;
+#ifndef SQLITE_OMIT_TRIGGER
+ if( iCol<0 ){
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
+ }else if( pExpr->iTable==0 ){
+ testcase( iCol==31 );
+ testcase( iCol==32 );
+ pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
+ }else{
+ testcase( iCol==31 );
+ testcase( iCol==32 );
+ pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
+ }
#endif /* SQLITE_OMIT_TRIGGER */
+ }
}
}
}
@@ -94824,13 +106728,13 @@ static int lookupName(
if( cnt==0
&& cntTab==1
&& pMatch
- && (pNC->ncFlags & NC_IdxExpr)==0
+ && (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0
&& sqlite3IsRowid(zCol)
- && VisibleRowid(pMatch->pTab)
+ && ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom)
){
cnt = 1;
- pExpr->iColumn = -1;
- pExpr->affinity = SQLITE_AFF_INTEGER;
+ if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1;
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
}
/*
@@ -94851,29 +106755,37 @@ static int lookupName(
** is supported for backwards compatibility only. Hence, we issue a warning
** on sqlite3_log() whenever the capability is used.
*/
- if( (pNC->ncFlags & NC_UEList)!=0
- && cnt==0
+ if( cnt==0
+ && (pNC->ncFlags & NC_UEList)!=0
&& zTab==0
){
pEList = pNC->uNC.pEList;
assert( pEList!=0 );
for(j=0; j<pEList->nExpr; j++){
- char *zAs = pEList->a[j].zName;
- if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
+ char *zAs = pEList->a[j].zEName;
+ if( pEList->a[j].fg.eEName==ENAME_NAME
+ && sqlite3_stricmp(zAs, zCol)==0
+ ){
Expr *pOrig;
assert( pExpr->pLeft==0 && pExpr->pRight==0 );
- assert( pExpr->x.pList==0 );
- assert( pExpr->x.pSelect==0 );
+ assert( ExprUseXList(pExpr)==0 || pExpr->x.pList==0 );
+ assert( ExprUseXSelect(pExpr)==0 || pExpr->x.pSelect==0 );
pOrig = pEList->a[j].pExpr;
if( (pNC->ncFlags&NC_AllowAgg)==0 && ExprHasProperty(pOrig, EP_Agg) ){
sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs);
return WRC_Abort;
}
+ if( ExprHasProperty(pOrig, EP_Win)
+ && ((pNC->ncFlags&NC_AllowWin)==0 || pNC!=pTopNC )
+ ){
+ sqlite3ErrorMsg(pParse, "misuse of aliased window function %s",zAs);
+ return WRC_Abort;
+ }
if( sqlite3ExprVectorSize(pOrig)!=1 ){
sqlite3ErrorMsg(pParse, "row value misused");
return WRC_Abort;
}
- resolveAlias(pParse, pEList, j, pExpr, "", nSubquery);
+ resolveAlias(pParse, pEList, j, pExpr, nSubquery);
cnt = 1;
pMatch = 0;
assert( zTab==0 && zDb==0 );
@@ -94882,7 +106794,7 @@ static int lookupName(
}
goto lookupname_end;
}
- }
+ }
}
/* Advance to the next name context. The loop will exit when either
@@ -94906,9 +106818,30 @@ static int lookupName(
*/
if( cnt==0 && zTab==0 ){
assert( pExpr->op==TK_ID );
- if( ExprHasProperty(pExpr,EP_DblQuoted) ){
+ if( ExprHasProperty(pExpr,EP_DblQuoted)
+ && areDoubleQuotedStringsEnabled(db, pTopNC)
+ ){
+ /* If a double-quoted identifier does not match any known column name,
+ ** then treat it as a string.
+ **
+ ** This hack was added in the early days of SQLite in a misguided attempt
+ ** to be compatible with MySQL 3.x, which used double-quotes for strings.
+ ** I now sorely regret putting in this hack. The effect of this hack is
+ ** that misspelled identifier names are silently converted into strings
+ ** rather than causing an error, to the frustration of countless
+ ** programmers. To all those frustrated programmers, my apologies.
+ **
+ ** Someday, I hope to get rid of this hack. Unfortunately there is
+ ** a huge amount of legacy SQL that uses it. So for now, we just
+ ** issue a warning.
+ */
+ sqlite3_log(SQLITE_WARNING,
+ "double-quoted string literal: \"%w\"", zCol);
+#ifdef SQLITE_ENABLE_NORMALIZE
+ sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol);
+#endif
pExpr->op = TK_STRING;
- pExpr->y.pTab = 0;
+ memset(&pExpr->y, 0, sizeof(pExpr->y));
return WRC_Prune;
}
if( sqlite3ExprIdToTrueFalse(pExpr) ){
@@ -94917,11 +106850,37 @@ static int lookupName(
}
/*
- ** cnt==0 means there was not match. cnt>1 means there were two or
- ** more matches. Either way, we have an error.
+ ** cnt==0 means there was not match.
+ ** cnt>1 means there were two or more matches.
+ **
+ ** cnt==0 is always an error. cnt>1 is often an error, but might
+ ** be multiple matches for a NATURAL LEFT JOIN or a LEFT JOIN USING.
*/
+ assert( pFJMatch==0 || cnt>0 );
+ assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) );
if( cnt!=1 ){
const char *zErr;
+ if( pFJMatch ){
+ if( pFJMatch->nExpr==cnt-1 ){
+ if( ExprHasProperty(pExpr,EP_Leaf) ){
+ ExprClearProperty(pExpr,EP_Leaf);
+ }else{
+ sqlite3ExprDelete(db, pExpr->pLeft);
+ pExpr->pLeft = 0;
+ sqlite3ExprDelete(db, pExpr->pRight);
+ pExpr->pRight = 0;
+ }
+ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
+ pExpr->op = TK_FUNCTION;
+ pExpr->u.zToken = "coalesce";
+ pExpr->x.pList = pFJMatch;
+ cnt = 1;
+ goto lookupname_end;
+ }else{
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }
+ }
zErr = cnt==0 ? "no such column" : "ambiguous column name";
if( zDb ){
sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol);
@@ -94930,40 +106889,51 @@ static int lookupName(
}else{
sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol);
}
+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr);
pParse->checkSchema = 1;
- pTopNC->nErr++;
+ pTopNC->nNcErr++;
+ eNewExprOp = TK_NULL;
+ }
+ assert( pFJMatch==0 );
+
+ /* Remove all substructure from pExpr */
+ if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
+ sqlite3ExprDelete(db, pExpr->pLeft);
+ pExpr->pLeft = 0;
+ sqlite3ExprDelete(db, pExpr->pRight);
+ pExpr->pRight = 0;
+ ExprSetProperty(pExpr, EP_Leaf);
}
/* If a column from a table in pSrcList is referenced, then record
** this fact in the pSrcList.a[].colUsed bitmask. Column 0 causes
- ** bit 0 to be set. Column 1 sets bit 1. And so forth. If the
- ** column number is greater than the number of bits in the bitmask
- ** then set the high-order bit of the bitmask.
+ ** bit 0 to be set. Column 1 sets bit 1. And so forth. Bit 63 is
+ ** set if the 63rd or any subsequent column is used.
+ **
+ ** The colUsed mask is an optimization used to help determine if an
+ ** index is a covering index. The correct answer is still obtained
+ ** if the mask contains extra set bits. However, it is important to
+ ** avoid setting bits beyond the maximum column number of the table.
+ ** (See ticket [b92e5e8ec2cdbaa1]).
+ **
+ ** If a generated column is referenced, set bits for every column
+ ** of the table.
*/
- if( pExpr->iColumn>=0 && pMatch!=0 ){
- int n = pExpr->iColumn;
- testcase( n==BMS-1 );
- if( n>=BMS ){
- n = BMS-1;
- }
- assert( pMatch->iCursor==pExpr->iTable );
- pMatch->colUsed |= ((Bitmask)1)<<n;
+ if( pExpr->iColumn>=0 && cnt==1 && pMatch!=0 ){
+ pMatch->colUsed |= sqlite3ExprColUsed(pExpr);
}
- /* Clean up and return
- */
- sqlite3ExprDelete(db, pExpr->pLeft);
- pExpr->pLeft = 0;
- sqlite3ExprDelete(db, pExpr->pRight);
- pExpr->pRight = 0;
pExpr->op = eNewExprOp;
- ExprSetProperty(pExpr, EP_Leaf);
lookupname_end:
if( cnt==1 ){
assert( pNC!=0 );
- if( !ExprHasProperty(pExpr, EP_Alias) ){
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ if( pParse->db->xAuth
+ && (pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER)
+ ){
sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
}
+#endif
/* Increment the nRef value on all name contexts from TopNC up to
** the point where the name matched. */
for(;;){
@@ -94985,16 +106955,26 @@ lookupname_end:
SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){
Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0);
if( p ){
- struct SrcList_item *pItem = &pSrc->a[iSrc];
- p->y.pTab = pItem->pTab;
+ SrcItem *pItem = &pSrc->a[iSrc];
+ Table *pTab;
+ assert( ExprUseYTab(p) );
+ pTab = p->y.pTab = pItem->pTab;
p->iTable = pItem->iCursor;
if( p->y.pTab->iPKey==iCol ){
p->iColumn = -1;
}else{
p->iColumn = (ynVar)iCol;
- testcase( iCol==BMS );
- testcase( iCol==BMS-1 );
- pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol);
+ if( (pTab->tabFlags & TF_HasGenerated)!=0
+ && (pTab->aCol[iCol].colFlags & COLFLAG_GENERATED)!=0
+ ){
+ testcase( pTab->nCol==63 );
+ testcase( pTab->nCol==64 );
+ pItem->colUsed = pTab->nCol>=64 ? ALLBITS : MASKBIT(pTab->nCol)-1;
+ }else{
+ testcase( iCol==BMS );
+ testcase( iCol==BMS-1 );
+ pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol);
+ }
}
}
return p;
@@ -95003,23 +106983,41 @@ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSr
/*
** Report an error that an expression is not valid for some set of
** pNC->ncFlags values determined by validMask.
-*/
-static void notValid(
- Parse *pParse, /* Leave error message here */
- NameContext *pNC, /* The name context */
- const char *zMsg, /* Type of error */
- int validMask /* Set of contexts for which prohibited */
-){
- assert( (validMask&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr))==0 );
- if( (pNC->ncFlags & validMask)!=0 ){
- const char *zIn = "partial index WHERE clauses";
- if( pNC->ncFlags & NC_IdxExpr ) zIn = "index expressions";
+**
+** static void notValid(
+** Parse *pParse, // Leave error message here
+** NameContext *pNC, // The name context
+** const char *zMsg, // Type of error
+** int validMask, // Set of contexts for which prohibited
+** Expr *pExpr // Invalidate this expression on error
+** ){...}
+**
+** As an optimization, since the conditional is almost always false
+** (because errors are rare), the conditional is moved outside of the
+** function call using a macro.
+*/
+static void notValidImpl(
+ Parse *pParse, /* Leave error message here */
+ NameContext *pNC, /* The name context */
+ const char *zMsg, /* Type of error */
+ Expr *pExpr, /* Invalidate this expression on error */
+ Expr *pError /* Associate error with this expression */
+){
+ const char *zIn = "partial index WHERE clauses";
+ if( pNC->ncFlags & NC_IdxExpr ) zIn = "index expressions";
#ifndef SQLITE_OMIT_CHECK
- else if( pNC->ncFlags & NC_IsCheck ) zIn = "CHECK constraints";
+ else if( pNC->ncFlags & NC_IsCheck ) zIn = "CHECK constraints";
#endif
- sqlite3ErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn);
- }
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ else if( pNC->ncFlags & NC_GenCol ) zIn = "generated columns";
+#endif
+ sqlite3ErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn);
+ if( pExpr ) pExpr->op = TK_NULL;
+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pError);
}
+#define sqlite3ResolveNotValid(P,N,M,X,E,R) \
+ assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \
+ if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R);
/*
** Expression p should encode a floating point value between 1.0 and 0.0.
@@ -95029,6 +107027,7 @@ static void notValid(
static int exprProbability(Expr *p){
double r = -1.0;
if( p->op!=TK_FLOAT ) return -1;
+ assert( !ExprHasProperty(p, EP_IntValue) );
sqlite3AtoF(p->u.zToken, &r, sqlite3Strlen30(p->u.zToken), SQLITE_UTF8);
assert( r>=0.0 );
if( r>1.0 ) return -1;
@@ -95066,33 +107065,70 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
#endif
switch( pExpr->op ){
-#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
/* The special operator TK_ROW means use the rowid for the first
** column in the FROM clause. This is used by the LIMIT and ORDER BY
- ** clause processing on UPDATE and DELETE statements.
+ ** clause processing on UPDATE and DELETE statements, and by
+ ** UPDATE ... FROM statement processing.
*/
case TK_ROW: {
SrcList *pSrcList = pNC->pSrcList;
- struct SrcList_item *pItem;
- assert( pSrcList && pSrcList->nSrc==1 );
+ SrcItem *pItem;
+ assert( pSrcList && pSrcList->nSrc>=1 );
pItem = pSrcList->a;
- assert( HasRowid(pItem->pTab) && pItem->pTab->pSelect==0 );
pExpr->op = TK_COLUMN;
+ assert( ExprUseYTab(pExpr) );
pExpr->y.pTab = pItem->pTab;
pExpr->iTable = pItem->iCursor;
- pExpr->iColumn = -1;
- pExpr->affinity = SQLITE_AFF_INTEGER;
+ pExpr->iColumn--;
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
break;
}
-#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
- && !defined(SQLITE_OMIT_SUBQUERY) */
+
+ /* An optimization: Attempt to convert
+ **
+ ** "expr IS NOT NULL" --> "TRUE"
+ ** "expr IS NULL" --> "FALSE"
+ **
+ ** if we can prove that "expr" is never NULL. Call this the
+ ** "NOT NULL strength reduction optimization".
+ **
+ ** If this optimization occurs, also restore the NameContext ref-counts
+ ** to the state they where in before the "column" LHS expression was
+ ** resolved. This prevents "column" from being counted as having been
+ ** referenced, which might prevent a SELECT from being erroneously
+ ** marked as correlated.
+ */
+ case TK_NOTNULL:
+ case TK_ISNULL: {
+ int anRef[8];
+ NameContext *p;
+ int i;
+ for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
+ anRef[i] = p->nRef;
+ }
+ sqlite3WalkExpr(pWalker, pExpr->pLeft);
+ if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
+ testcase( ExprHasProperty(pExpr, EP_OuterON) );
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+ pExpr->u.iValue = (pExpr->op==TK_NOTNULL);
+ pExpr->flags |= EP_IntValue;
+ pExpr->op = TK_INTEGER;
+
+ for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
+ p->nRef = anRef[i];
+ }
+ sqlite3ExprDelete(pParse->db, pExpr->pLeft);
+ pExpr->pLeft = 0;
+ }
+ return WRC_Prune;
+ }
/* A column name: ID
** Or table name and column name: ID.ID
** Or a database, table and column: ID.ID.ID
**
** The TK_ID and TK_OUT cases are combined so that there will only
- ** be one call to lookupName(). Then the compiler will in-line
+ ** be one call to lookupName(). Then the compiler will in-line
** lookupName() for a size reduction and performance increase.
*/
case TK_ID:
@@ -95105,21 +107141,28 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
if( pExpr->op==TK_ID ){
zDb = 0;
zTable = 0;
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
zColumn = pExpr->u.zToken;
}else{
Expr *pLeft = pExpr->pLeft;
- notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
+ testcase( pNC->ncFlags & NC_IdxExpr );
+ testcase( pNC->ncFlags & NC_GenCol );
+ sqlite3ResolveNotValid(pParse, pNC, "the \".\" operator",
+ NC_IdxExpr|NC_GenCol, 0, pExpr);
pRight = pExpr->pRight;
if( pRight->op==TK_ID ){
zDb = 0;
}else{
assert( pRight->op==TK_DOT );
+ assert( !ExprHasProperty(pRight, EP_IntValue) );
zDb = pLeft->u.zToken;
pLeft = pRight->pLeft;
pRight = pRight->pRight;
}
+ assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) );
zTable = pLeft->u.zToken;
zColumn = pRight->u.zToken;
+ assert( ExprUseYTab(pExpr) );
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight);
sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft);
@@ -95136,14 +107179,16 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
int no_such_func = 0; /* True if no such function exists */
int wrong_num_args = 0; /* True if wrong number of arguments */
int is_agg = 0; /* True if is an aggregate function */
- int nId; /* Number of characters in function name */
const char *zId; /* The function name. */
FuncDef *pDef; /* Information about the function */
u8 enc = ENC(pParse->db); /* The database encoding */
-
- assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
+ int savedAllowFlags = (pNC->ncFlags & (NC_AllowAgg | NC_AllowWin));
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0);
+#endif
+ assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) );
+ assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER );
zId = pExpr->u.zToken;
- nId = sqlite3Strlen30(zId);
pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0);
if( pDef==0 ){
pDef = sqlite3FindFunction(pParse->db, zId, -2, enc, 0);
@@ -95155,14 +107200,14 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}else{
is_agg = pDef->xFinalize!=0;
if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
- ExprSetProperty(pExpr, EP_Unlikely|EP_Skip);
+ ExprSetProperty(pExpr, EP_Unlikely);
if( n==2 ){
pExpr->iTable = exprProbability(pList->a[1].pExpr);
if( pExpr->iTable<0 ){
sqlite3ErrorMsg(pParse,
- "second argument to likelihood() must be a "
- "constant between 0.0 and 1.0");
- pNC->nErr++;
+ "second argument to %#T() must be a "
+ "constant between 0.0 and 1.0", pExpr);
+ pNC->nNcErr++;
}
}else{
/* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is
@@ -95175,16 +107220,16 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
** to likelihood(X,0.9375). */
/* TUNING: unlikely() probability is 0.0625. likely() is 0.9375 */
pExpr->iTable = pDef->zName[0]=='u' ? 8388608 : 125829120;
- }
+ }
}
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0,pDef->zName,0);
if( auth!=SQLITE_OK ){
if( auth==SQLITE_DENY ){
- sqlite3ErrorMsg(pParse, "not authorized to use function: %s",
- pDef->zName);
- pNC->nErr++;
+ sqlite3ErrorMsg(pParse, "not authorized to use function: %#T",
+ pExpr);
+ pNC->nNcErr++;
}
pExpr->op = TK_NULL;
return WRC_Prune;
@@ -95194,24 +107239,39 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){
/* For the purposes of the EP_ConstFunc flag, date and time
** functions and other functions that change slowly are considered
- ** constant because they are constant for the duration of one query */
+ ** constant because they are constant for the duration of one query.
+ ** This allows them to be factored out of inner loops. */
ExprSetProperty(pExpr,EP_ConstFunc);
}
if( (pDef->funcFlags & SQLITE_FUNC_CONSTANT)==0 ){
- /* Date/time functions that use 'now', and other functions like
+ /* Clearly non-deterministic functions like random(), but also
+ ** date/time functions that use 'now', and other functions like
** sqlite_version() that might change over time cannot be used
- ** in an index. */
- notValid(pParse, pNC, "non-deterministic functions",
- NC_IdxExpr|NC_PartIdx);
+ ** in an index or generated column. Curiously, they can be used
+ ** in a CHECK constraint. SQLServer, MySQL, and PostgreSQL all
+ ** all this. */
+ sqlite3ResolveNotValid(pParse, pNC, "non-deterministic functions",
+ NC_IdxExpr|NC_PartIdx|NC_GenCol, 0, pExpr);
+ }else{
+ assert( (NC_SelfRef & 0xff)==NC_SelfRef ); /* Must fit in 8 bits */
+ pExpr->op2 = pNC->ncFlags & NC_SelfRef;
+ if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL);
}
if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0
&& pParse->nested==0
- && sqlite3Config.bInternalFunctions==0
+ && (pParse->db->mDbFlags & DBFLAG_InternalFunc)==0
){
/* Internal-use-only functions are disallowed unless the
- ** SQL is being compiled using sqlite3NestedParse() */
+ ** SQL is being compiled using sqlite3NestedParse() or
+ ** the SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test-control has be
+ ** used to activate internal functions for testing purposes */
no_such_func = 1;
pDef = 0;
+ }else
+ if( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0
+ && !IN_RENAME_OBJECT
+ ){
+ sqlite3ExprFunctionUsable(pParse, pExpr, pDef);
}
}
@@ -95221,30 +107281,30 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|| (pDef->xValue==0 && pDef->xInverse==0)
|| (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize)
);
- if( pDef && pDef->xValue==0 && ExprHasProperty(pExpr, EP_WinFunc) ){
- sqlite3ErrorMsg(pParse,
- "%.*s() may not be used as a window function", nId, zId
+ if( pDef && pDef->xValue==0 && pWin ){
+ sqlite3ErrorMsg(pParse,
+ "%#T() may not be used as a window function", pExpr
);
- pNC->nErr++;
- }else if(
+ pNC->nNcErr++;
+ }else if(
(is_agg && (pNC->ncFlags & NC_AllowAgg)==0)
- || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pExpr->y.pWin)
- || (is_agg && pExpr->y.pWin && (pNC->ncFlags & NC_AllowWin)==0)
+ || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pWin)
+ || (is_agg && pWin && (pNC->ncFlags & NC_AllowWin)==0)
){
const char *zType;
- if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->y.pWin ){
+ if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pWin ){
zType = "window";
}else{
zType = "aggregate";
}
- sqlite3ErrorMsg(pParse, "misuse of %s function %.*s()",zType,nId,zId);
- pNC->nErr++;
+ sqlite3ErrorMsg(pParse, "misuse of %s function %#T()",zType,pExpr);
+ pNC->nNcErr++;
is_agg = 0;
}
#else
if( (is_agg && (pNC->ncFlags & NC_AllowAgg)==0) ){
- sqlite3ErrorMsg(pParse,"misuse of aggregate function %.*s()",nId,zId);
- pNC->nErr++;
+ sqlite3ErrorMsg(pParse,"misuse of aggregate function %#T()",pExpr);
+ pNC->nNcErr++;
is_agg = 0;
}
#endif
@@ -95253,59 +107313,96 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
&& pParse->explain==0
#endif
){
- sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
- pNC->nErr++;
+ sqlite3ErrorMsg(pParse, "no such function: %#T", pExpr);
+ pNC->nNcErr++;
}else if( wrong_num_args ){
- sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
- nId, zId);
- pNC->nErr++;
+ sqlite3ErrorMsg(pParse,"wrong number of arguments to function %#T()",
+ pExpr);
+ pNC->nNcErr++;
+ }
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ else if( is_agg==0 && ExprHasProperty(pExpr, EP_WinFunc) ){
+ sqlite3ErrorMsg(pParse,
+ "FILTER may not be used with non-aggregate %#T()",
+ pExpr
+ );
+ pNC->nNcErr++;
+ }
+#endif
+ else if( is_agg==0 && pExpr->pLeft ){
+ sqlite3ExprOrderByAggregateError(pParse, pExpr);
+ pNC->nNcErr++;
}
if( is_agg ){
+ /* Window functions may not be arguments of aggregate functions.
+ ** Or arguments of other window functions. But aggregate functions
+ ** may be arguments for window functions. */
#ifndef SQLITE_OMIT_WINDOWFUNC
- pNC->ncFlags &= ~(pExpr->y.pWin ? NC_AllowWin : NC_AllowAgg);
+ pNC->ncFlags &= ~(NC_AllowWin | (!pWin ? NC_AllowAgg : 0));
#else
pNC->ncFlags &= ~NC_AllowAgg;
#endif
}
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ else if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ is_agg = 1;
+ }
+#endif
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList);
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( pExpr->y.pWin ){
+ if( pWin ){
Select *pSel = pNC->pWinSelect;
- sqlite3WalkExprList(pWalker, pExpr->y.pWin->pPartition);
- sqlite3WalkExprList(pWalker, pExpr->y.pWin->pOrderBy);
- sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
- sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef);
- if( 0==pSel->pWin
- || 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->y.pWin)
- ){
- pExpr->y.pWin->pNextWin = pSel->pWin;
- pSel->pWin = pExpr->y.pWin;
+ assert( pWin==0 || (ExprUseYWin(pExpr) && pWin==pExpr->y.pWin) );
+ if( IN_RENAME_OBJECT==0 ){
+ sqlite3WindowUpdate(pParse, pSel ? pSel->pWinDefn : 0, pWin, pDef);
+ if( pParse->db->mallocFailed ) break;
}
- pNC->ncFlags |= NC_AllowWin;
+ sqlite3WalkExprList(pWalker, pWin->pPartition);
+ sqlite3WalkExprList(pWalker, pWin->pOrderBy);
+ sqlite3WalkExpr(pWalker, pWin->pFilter);
+ sqlite3WindowLink(pSel, pWin);
+ pNC->ncFlags |= NC_HasWin;
}else
#endif /* SQLITE_OMIT_WINDOWFUNC */
{
- NameContext *pNC2 = pNC;
+ NameContext *pNC2; /* For looping up thru outer contexts */
pExpr->op = TK_AGG_FUNCTION;
pExpr->op2 = 0;
- while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
- pExpr->op2++;
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
+ }
+#endif
+ pNC2 = pNC;
+ while( pNC2
+ && sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0
+ ){
+ pExpr->op2 += (1 + pNC2->nNestedSelect);
pNC2 = pNC2->pNext;
}
- assert( pDef!=0 );
- if( pNC2 ){
+ assert( pDef!=0 || IN_RENAME_OBJECT );
+ if( pNC2 && pDef ){
+ pExpr->op2 += pNC2->nNestedSelect;
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
+ assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg );
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
- pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
-
+ testcase( (pDef->funcFlags & SQLITE_FUNC_ANYORDER)!=0 );
+ pNC2->ncFlags |= NC_HasAgg
+ | ((pDef->funcFlags^SQLITE_FUNC_ANYORDER)
+ & (SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER));
}
- pNC->ncFlags |= NC_AllowAgg;
}
+ pNC->ncFlags |= savedAllowFlags;
}
/* FIX ME: Compute pExpr->affinity based on the expected return
- ** type of the function
+ ** type of the function
*/
return WRC_Prune;
}
@@ -95315,29 +107412,41 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
#endif
case TK_IN: {
testcase( pExpr->op==TK_IN );
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ if( ExprUseXSelect(pExpr) ){
int nRef = pNC->nRef;
- notValid(pParse, pNC, "subqueries", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
- sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
+ testcase( pNC->ncFlags & NC_IsCheck );
+ testcase( pNC->ncFlags & NC_PartIdx );
+ testcase( pNC->ncFlags & NC_IdxExpr );
+ testcase( pNC->ncFlags & NC_GenCol );
+ if( pNC->ncFlags & NC_SelfRef ){
+ notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr);
+ }else{
+ sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
+ }
assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){
ExprSetProperty(pExpr, EP_VarSelect);
- pNC->ncFlags |= NC_VarSelect;
}
+ pNC->ncFlags |= NC_Subquery;
}
break;
}
case TK_VARIABLE: {
- notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
+ testcase( pNC->ncFlags & NC_IsCheck );
+ testcase( pNC->ncFlags & NC_PartIdx );
+ testcase( pNC->ncFlags & NC_IdxExpr );
+ testcase( pNC->ncFlags & NC_GenCol );
+ sqlite3ResolveNotValid(pParse, pNC, "parameters",
+ NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol, pExpr, pExpr);
break;
}
case TK_IS:
case TK_ISNOT: {
- Expr *pRight;
+ Expr *pRight = sqlite3ExprSkipCollateAndLikely(pExpr->pRight);
assert( !ExprHasProperty(pExpr, EP_Reduced) );
/* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE",
** and "x IS NOT FALSE". */
- if( (pRight = pExpr->pRight)->op==TK_ID ){
+ if( ALWAYS(pRight) && (pRight->op==TK_ID || pRight->op==TK_TRUEFALSE) ){
int rc = resolveExprStep(pWalker, pRight);
if( rc==WRC_Abort ) return WRC_Abort;
if( pRight->op==TK_TRUEFALSE ){
@@ -95346,7 +107455,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
return WRC_Continue;
}
}
- /* Fall thru */
+ /* no break */ deliberate_fall_through
}
case TK_BETWEEN:
case TK_EQ:
@@ -95360,6 +107469,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
assert( pExpr->pLeft!=0 );
nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
if( pExpr->op==TK_BETWEEN ){
+ assert( ExprUseXList(pExpr) );
nRight = sqlite3ExprVectorSize(pExpr->x.pList->a[0].pExpr);
if( nRight==nLeft ){
nRight = sqlite3ExprVectorSize(pExpr->x.pList->a[1].pExpr);
@@ -95379,11 +107489,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_ISNOT );
testcase( pExpr->op==TK_BETWEEN );
sqlite3ErrorMsg(pParse, "row value misused");
+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr);
}
- break;
+ break;
}
}
- return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
+ assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 );
+ return pParse->nErr ? WRC_Abort : WRC_Continue;
}
/*
@@ -95408,10 +107520,13 @@ static int resolveAsName(
UNUSED_PARAMETER(pParse);
if( pE->op==TK_ID ){
- char *zCol = pE->u.zToken;
+ const char *zCol;
+ assert( !ExprHasProperty(pE, EP_IntValue) );
+ zCol = pE->u.zToken;
for(i=0; i<pEList->nExpr; i++){
- char *zAs = pEList->a[i].zName;
- if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
+ if( pEList->a[i].fg.eEName==ENAME_NAME
+ && sqlite3_stricmp(pEList->a[i].zEName, zCol)==0
+ ){
return i+1;
}
}
@@ -95458,8 +107573,8 @@ static int resolveOrderByTermToExprList(
nc.pParse = pParse;
nc.pSrcList = pSelect->pSrc;
nc.uNC.pEList = pEList;
- nc.ncFlags = NC_AllowAgg|NC_UEList;
- nc.nErr = 0;
+ nc.ncFlags = NC_AllowAgg|NC_UEList|NC_NoSelect;
+ nc.nNcErr = 0;
db = pParse->db;
savedSuppErr = db->suppressErr;
db->suppressErr = 1;
@@ -95488,11 +107603,13 @@ static void resolveOutOfRangeError(
Parse *pParse, /* The error context into which to write the error */
const char *zType, /* "ORDER" or "GROUP" */
int i, /* The index (1-based) of the term out of range */
- int mx /* Largest permissible value of i */
+ int mx, /* Largest permissible value of i */
+ Expr *pError /* Associate the error with the expression */
){
- sqlite3ErrorMsg(pParse,
+ sqlite3ErrorMsg(pParse,
"%r %s BY term out of range - should be "
"between 1 and %d", i, zType, mx);
+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pError);
}
/*
@@ -95528,7 +107645,7 @@ static int resolveCompoundOrderBy(
return 1;
}
for(i=0; i<pOrderBy->nExpr; i++){
- pOrderBy->a[i].done = 0;
+ pOrderBy->a[i].fg.done = 0;
}
pSelect->pNext = 0;
while( pSelect->pPrior ){
@@ -95543,43 +107660,60 @@ static int resolveCompoundOrderBy(
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
int iCol = -1;
Expr *pE, *pDup;
- if( pItem->done ) continue;
- pE = sqlite3ExprSkipCollate(pItem->pExpr);
+ if( pItem->fg.done ) continue;
+ pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr);
+ if( NEVER(pE==0) ) continue;
if( sqlite3ExprIsInteger(pE, &iCol) ){
if( iCol<=0 || iCol>pEList->nExpr ){
- resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr);
+ resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr, pE);
return 1;
}
}else{
iCol = resolveAsName(pParse, pEList, pE);
if( iCol==0 ){
+ /* Now test if expression pE matches one of the values returned
+ ** by pSelect. In the usual case this is done by duplicating the
+ ** expression, resolving any symbols in it, and then comparing
+ ** it against each expression returned by the SELECT statement.
+ ** Once the comparisons are finished, the duplicate expression
+ ** is deleted.
+ **
+ ** If this is running as part of an ALTER TABLE operation and
+ ** the symbols resolve successfully, also resolve the symbols in the
+ ** actual expression. This allows the code in alter.c to modify
+ ** column references within the ORDER BY expression as required. */
pDup = sqlite3ExprDup(db, pE, 0);
if( !db->mallocFailed ){
assert(pDup);
iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup);
+ if( IN_RENAME_OBJECT && iCol>0 ){
+ resolveOrderByTermToExprList(pParse, pSelect, pE);
+ }
}
sqlite3ExprDelete(db, pDup);
}
}
if( iCol>0 ){
/* Convert the ORDER BY term into an integer column number iCol,
- ** taking care to preserve the COLLATE clause if it exists */
- Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
- if( pNew==0 ) return 1;
- pNew->flags |= EP_IntValue;
- pNew->u.iValue = iCol;
- if( pItem->pExpr==pE ){
- pItem->pExpr = pNew;
- }else{
- Expr *pParent = pItem->pExpr;
- assert( pParent->op==TK_COLLATE );
- while( pParent->pLeft->op==TK_COLLATE ) pParent = pParent->pLeft;
- assert( pParent->pLeft==pE );
- pParent->pLeft = pNew;
+ ** taking care to preserve the COLLATE clause if it exists. */
+ if( !IN_RENAME_OBJECT ){
+ Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
+ if( pNew==0 ) return 1;
+ pNew->flags |= EP_IntValue;
+ pNew->u.iValue = iCol;
+ if( pItem->pExpr==pE ){
+ pItem->pExpr = pNew;
+ }else{
+ Expr *pParent = pItem->pExpr;
+ assert( pParent->op==TK_COLLATE );
+ while( pParent->pLeft->op==TK_COLLATE ) pParent = pParent->pLeft;
+ assert( pParent->pLeft==pE );
+ pParent->pLeft = pNew;
+ }
+ sqlite3ExprDelete(db, pE);
+ pItem->u.x.iOrderByCol = (u16)iCol;
}
- sqlite3ExprDelete(db, pE);
- pItem->u.x.iOrderByCol = (u16)iCol;
- pItem->done = 1;
+ pItem->fg.done = 1;
}else{
moreToDo = 1;
}
@@ -95587,7 +107721,7 @@ static int resolveCompoundOrderBy(
pSelect = pSelect->pNext;
}
for(i=0; i<pOrderBy->nExpr; i++){
- if( pOrderBy->a[i].done==0 ){
+ if( pOrderBy->a[i].fg.done==0 ){
sqlite3ErrorMsg(pParse, "%r ORDER BY term does not match any "
"column in the result set", i+1);
return 1;
@@ -95617,7 +107751,7 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
ExprList *pEList;
struct ExprList_item *pItem;
- if( pOrderBy==0 || pParse->db->mallocFailed ) return 0;
+ if( pOrderBy==0 || pParse->db->mallocFailed || IN_RENAME_OBJECT ) return 0;
if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType);
return 1;
@@ -95627,16 +107761,45 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
if( pItem->u.x.iOrderByCol ){
if( pItem->u.x.iOrderByCol>pEList->nExpr ){
- resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
+ resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr, 0);
return 1;
}
- resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,
- zType,0);
+ resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,0);
}
}
return 0;
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+/*
+** Walker callback for windowRemoveExprFromSelect().
+*/
+static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){
+ UNUSED_PARAMETER(pWalker);
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ Window *pWin = pExpr->y.pWin;
+ sqlite3WindowUnlinkFromSelect(pWin);
+ }
+ return WRC_Continue;
+}
+
+/*
+** Remove any Window objects owned by the expression pExpr from the
+** Select.pWin list of Select object pSelect.
+*/
+static void windowRemoveExprFromSelect(Select *pSelect, Expr *pExpr){
+ if( pSelect->pWin ){
+ Walker sWalker;
+ memset(&sWalker, 0, sizeof(Walker));
+ sWalker.xExprCallback = resolveRemoveWindowsCb;
+ sWalker.u.pSelect = pSelect;
+ sqlite3WalkExpr(&sWalker, pExpr);
+ }
+}
+#else
+# define windowRemoveExprFromSelect(a, b)
+#endif /* SQLITE_OMIT_WINDOWFUNC */
+
/*
** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect.
** The Name context of the SELECT statement is pNC. zType is either
@@ -95667,12 +107830,13 @@ static int resolveOrderGroupBy(
Parse *pParse; /* Parsing context */
int nResult; /* Number of terms in the result set */
- if( pOrderBy==0 ) return 0;
+ assert( pOrderBy!=0 );
nResult = pSelect->pEList->nExpr;
pParse = pNC->pParse;
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
Expr *pE = pItem->pExpr;
- Expr *pE2 = sqlite3ExprSkipCollate(pE);
+ Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE);
+ if( NEVER(pE2==0) ) continue;
if( zType[0]!='G' ){
iCol = resolveAsName(pParse, pSelect->pEList, pE2);
if( iCol>0 ){
@@ -95689,7 +107853,7 @@ static int resolveOrderGroupBy(
** number so that sqlite3ResolveOrderGroupBy() will convert the
** order-by term to a copy of the result-set expression */
if( iCol<1 || iCol>0xffff ){
- resolveOutOfRangeError(pParse, zType, i+1, nResult);
+ resolveOutOfRangeError(pParse, zType, i+1, nResult, pE2);
return 1;
}
pItem->u.x.iOrderByCol = (u16)iCol;
@@ -95703,19 +107867,10 @@ static int resolveOrderGroupBy(
}
for(j=0; j<pSelect->pEList->nExpr; j++){
if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){
-#ifndef SQLITE_OMIT_WINDOWFUNC
- if( ExprHasProperty(pE, EP_WinFunc) ){
- /* Since this window function is being changed into a reference
- ** to the same window function the result set, remove the instance
- ** of this window function from the Select.pWin list. */
- Window **pp;
- for(pp=&pSelect->pWin; *pp; pp=&(*pp)->pNextWin){
- if( *pp==pE->y.pWin ){
- *pp = (*pp)->pNextWin;
- }
- }
- }
-#endif
+ /* Since this expression is being changed into a reference
+ ** to an identical expression in the result set, remove all Window
+ ** objects belonging to the expression from the Select.pWin list. */
+ windowRemoveExprFromSelect(pSelect, pE);
pItem->u.x.iOrderByCol = j+1;
}
}
@@ -95736,7 +107891,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
ExprList *pGroupBy; /* The GROUP BY clause */
Select *pLeftmost; /* Left-most of SELECT of a compound */
sqlite3 *db; /* Database connection */
-
+
assert( p!=0 );
if( p->selFlags & SF_Resolved ){
@@ -95756,7 +107911,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
*/
if( (p->selFlags & SF_Expanded)==0 ){
sqlite3SelectPrep(pParse, p, pOuterNC);
- return (pParse->nErr || db->mallocFailed) ? WRC_Abort : WRC_Prune;
+ return pParse->nErr ? WRC_Abort : WRC_Prune;
}
isCompound = p->pPrior!=0;
@@ -95790,64 +107945,62 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
pSub->pOrderBy = p->pOrderBy;
p->pOrderBy = 0;
}
-
- /* Recursively resolve names in all subqueries
+
+ /* Recursively resolve names in all subqueries in the FROM clause
*/
+ if( pOuterNC ) pOuterNC->nNestedSelect++;
for(i=0; i<p->pSrc->nSrc; i++){
- struct SrcList_item *pItem = &p->pSrc->a[i];
- if( pItem->pSelect ){
- NameContext *pNC; /* Used to iterate name contexts */
- int nRef = 0; /* Refcount for pOuterNC and outer contexts */
+ SrcItem *pItem = &p->pSrc->a[i];
+ if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
+ int nRef = pOuterNC ? pOuterNC->nRef : 0;
const char *zSavedContext = pParse->zAuthContext;
- /* Count the total number of references to pOuterNC and all of its
- ** parent contexts. After resolving references to expressions in
- ** pItem->pSelect, check if this value has changed. If so, then
- ** SELECT statement pItem->pSelect must be correlated. Set the
- ** pItem->fg.isCorrelated flag if this is the case. */
- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef;
-
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC);
pParse->zAuthContext = zSavedContext;
- if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
+ if( pParse->nErr ) return WRC_Abort;
+ assert( db->mallocFailed==0 );
- for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
- assert( pItem->fg.isCorrelated==0 && nRef<=0 );
- pItem->fg.isCorrelated = (nRef!=0);
+ /* If the number of references to the outer context changed when
+ ** expressions in the sub-select were resolved, the sub-select
+ ** is correlated. It is not required to check the refcount on any
+ ** but the innermost outer context object, as lookupName() increments
+ ** the refcount on all contexts between the current one and the
+ ** context containing the column when it resolves a name. */
+ if( pOuterNC ){
+ assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef );
+ pItem->fg.isCorrelated = (pOuterNC->nRef>nRef);
+ }
}
}
-
+ if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){
+ pOuterNC->nNestedSelect--;
+ }
+
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
** resolve the result-set expression list.
*/
sNC.ncFlags = NC_AllowAgg|NC_AllowWin;
sNC.pSrcList = p->pSrc;
sNC.pNext = pOuterNC;
-
+
/* Resolve names in the result set. */
if( sqlite3ResolveExprListNames(&sNC, p->pEList) ) return WRC_Abort;
sNC.ncFlags &= ~NC_AllowWin;
-
- /* If there are no aggregate functions in the result-set, and no GROUP BY
+
+ /* If there are no aggregate functions in the result-set, and no GROUP BY
** expression, do not allow aggregates in any of the other expressions.
*/
assert( (p->selFlags & SF_Aggregate)==0 );
pGroupBy = p->pGroupBy;
if( pGroupBy || (sNC.ncFlags & NC_HasAgg)!=0 ){
assert( NC_MinMaxAgg==SF_MinMaxAgg );
- p->selFlags |= SF_Aggregate | (sNC.ncFlags&NC_MinMaxAgg);
+ assert( NC_OrderAgg==SF_OrderByReqd );
+ p->selFlags |= SF_Aggregate | (sNC.ncFlags&(NC_MinMaxAgg|NC_OrderAgg));
}else{
sNC.ncFlags &= ~NC_AllowAgg;
}
-
- /* If a HAVING clause is present, then there must be a GROUP BY clause.
- */
- if( p->pHaving && !pGroupBy ){
- sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING");
- return WRC_Abort;
- }
-
+
/* Add the output column list to the name-context before parsing the
** other expressions in the SELECT statement. This is so that
** expressions in the WHERE clause (etc.) can refer to expressions by
@@ -95856,29 +108009,48 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** Minor point: If this is the case, then the expression will be
** re-evaluated for each reference to it.
*/
- assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert))==0 );
+ assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert|NC_UBaseReg))==0 );
sNC.uNC.pEList = p->pEList;
sNC.ncFlags |= NC_UEList;
- if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
+ if( p->pHaving ){
+ if( (p->selFlags & SF_Aggregate)==0 ){
+ sqlite3ErrorMsg(pParse, "HAVING clause on a non-aggregate query");
+ return WRC_Abort;
+ }
+ if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
+ }
if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
/* Resolve names in table-valued-function arguments */
for(i=0; i<p->pSrc->nSrc; i++){
- struct SrcList_item *pItem = &p->pSrc->a[i];
+ SrcItem *pItem = &p->pSrc->a[i];
if( pItem->fg.isTabFunc
- && sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg)
+ && sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg)
){
return WRC_Abort;
}
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( IN_RENAME_OBJECT ){
+ Window *pWin;
+ for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){
+ if( sqlite3ResolveExprListNames(&sNC, pWin->pOrderBy)
+ || sqlite3ResolveExprListNames(&sNC, pWin->pPartition)
+ ){
+ return WRC_Abort;
+ }
+ }
+ }
+#endif
+
/* The ORDER BY and GROUP BY clauses may not refer to terms in
- ** outer queries
+ ** outer queries
*/
sNC.pNext = 0;
sNC.ncFlags |= NC_AllowAgg|NC_AllowWin;
- /* If this is a converted compound query, move the ORDER BY clause from
+ /* If this is a converted compound query, move the ORDER BY clause from
** the sub-query back to the parent query. At this point each term
** within the ORDER BY clause has been transformed to an integer value.
** These integers will be replaced by copies of the corresponding result
@@ -95899,7 +108071,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** is not detected until much later, and so we need to go ahead and
** resolve those symbols on the incorrect ORDER BY for consistency.
*/
- if( isCompound<=nCompound /* Defer right-most ORDER BY of a compound */
+ if( p->pOrderBy!=0
+ && isCompound<=nCompound /* Defer right-most ORDER BY of a compound */
&& resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER")
){
return WRC_Abort;
@@ -95908,13 +108081,13 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
return WRC_Abort;
}
sNC.ncFlags &= ~NC_AllowWin;
-
- /* Resolve the GROUP BY clause. At the same time, make sure
+
+ /* Resolve the GROUP BY clause. At the same time, make sure
** the GROUP BY clause does not contain aggregate functions.
*/
if( pGroupBy ){
struct ExprList_item *pItem;
-
+
if( resolveOrderGroupBy(&sNC, p, pGroupBy, "GROUP") || db->mallocFailed ){
return WRC_Abort;
}
@@ -95956,7 +108129,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** checking on function usage and set a flag if any aggregate functions
** are seen.
**
-** To resolve table columns references we look for nodes (or subtrees) of the
+** To resolve table columns references we look for nodes (or subtrees) of the
** form X.Y.Z or Y.Z or just Z where
**
** X: The name of a database. Ex: "main" or "temp" or
@@ -95988,7 +108161,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
**
** SELECT a+b AS x, c+d AS y FROM t1 ORDER BY a+b;
**
-** Function calls are checked to make sure that the function is
+** Function calls are checked to make sure that the function is
** defined and that the correct number of arguments are specified.
** If the function is an aggregate function, then the NC_HasAgg flag is
** set and the opcode is changed from TK_FUNCTION to TK_AGG_FUNCTION.
@@ -95998,19 +108171,19 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** An error message is left in pParse if anything is amiss. The number
** if errors is returned.
*/
-SQLITE_PRIVATE int sqlite3ResolveExprNames(
+SQLITE_PRIVATE int sqlite3ResolveExprNames(
NameContext *pNC, /* Namespace to resolve expressions in. */
Expr *pExpr /* The expression to be analyzed. */
){
- u16 savedHasAgg;
+ int savedHasAgg;
Walker w;
if( pExpr==0 ) return SQLITE_OK;
- savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg);
- pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg);
+ savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
+ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
w.pParse = pNC->pParse;
w.xExprCallback = resolveExprStep;
- w.xSelectCallback = resolveSelectStep;
+ w.xSelectCallback = (pNC->ncFlags & NC_NoSelect) ? 0 : resolveSelectStep;
w.xSelectCallback2 = 0;
w.u.pNC = pNC;
#if SQLITE_MAX_EXPR_DEPTH>0
@@ -96019,15 +108192,18 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames(
return SQLITE_ERROR;
}
#endif
- sqlite3WalkExpr(&w, pExpr);
+ assert( pExpr!=0 );
+ sqlite3WalkExprNN(&w, pExpr);
#if SQLITE_MAX_EXPR_DEPTH>0
w.pParse->nHeight -= pExpr->nHeight;
#endif
- if( pNC->ncFlags & NC_HasAgg ){
- ExprSetProperty(pExpr, EP_Agg);
- }
+ assert( EP_Agg==NC_HasAgg );
+ assert( EP_Win==NC_HasWin );
+ testcase( pNC->ncFlags & NC_HasAgg );
+ testcase( pNC->ncFlags & NC_HasWin );
+ ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) );
pNC->ncFlags |= savedHasAgg;
- return pNC->nErr>0 || w.pParse->nErr>0;
+ return pNC->nNcErr>0 || w.pParse->nErr>0;
}
/*
@@ -96035,22 +108211,53 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames(
** just like sqlite3ResolveExprNames() except that it works for an expression
** list rather than a single expression.
*/
-SQLITE_PRIVATE int sqlite3ResolveExprListNames(
+SQLITE_PRIVATE int sqlite3ResolveExprListNames(
NameContext *pNC, /* Namespace to resolve expressions in. */
ExprList *pList /* The expression list to be analyzed. */
){
int i;
- if( pList ){
- for(i=0; i<pList->nExpr; i++){
- if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort;
+ int savedHasAgg = 0;
+ Walker w;
+ if( pList==0 ) return WRC_Continue;
+ w.pParse = pNC->pParse;
+ w.xExprCallback = resolveExprStep;
+ w.xSelectCallback = resolveSelectStep;
+ w.xSelectCallback2 = 0;
+ w.u.pNC = pNC;
+ savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
+ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
+ for(i=0; i<pList->nExpr; i++){
+ Expr *pExpr = pList->a[i].pExpr;
+ if( pExpr==0 ) continue;
+#if SQLITE_MAX_EXPR_DEPTH>0
+ w.pParse->nHeight += pExpr->nHeight;
+ if( sqlite3ExprCheckHeight(w.pParse, w.pParse->nHeight) ){
+ return WRC_Abort;
+ }
+#endif
+ sqlite3WalkExprNN(&w, pExpr);
+#if SQLITE_MAX_EXPR_DEPTH>0
+ w.pParse->nHeight -= pExpr->nHeight;
+#endif
+ assert( EP_Agg==NC_HasAgg );
+ assert( EP_Win==NC_HasWin );
+ testcase( pNC->ncFlags & NC_HasAgg );
+ testcase( pNC->ncFlags & NC_HasWin );
+ if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg) ){
+ ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) );
+ savedHasAgg |= pNC->ncFlags &
+ (NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
+ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
}
+ if( w.pParse->nErr>0 ) return WRC_Abort;
}
+ pNC->ncFlags |= savedHasAgg;
return WRC_Continue;
}
/*
** Resolve all names in all expressions of a SELECT and in all
-** decendents of the SELECT, including compounds off of p->pPrior,
+** descendants of the SELECT, including compounds off of p->pPrior,
** subqueries in expressions, and subqueries used as FROM clause
** terms.
**
@@ -96077,38 +108284,56 @@ SQLITE_PRIVATE void sqlite3ResolveSelectNames(
}
/*
-** Resolve names in expressions that can only reference a single table:
+** Resolve names in expressions that can only reference a single table
+** or which cannot reference any tables at all. Examples:
**
-** * CHECK constraints
-** * WHERE clauses on partial indices
+** "type" flag
+** ------------
+** (1) CHECK constraints NC_IsCheck
+** (2) WHERE clauses on partial indices NC_PartIdx
+** (3) Expressions in indexes on expressions NC_IdxExpr
+** (4) Expression arguments to VACUUM INTO. 0
+** (5) GENERATED ALWAYS as expressions NC_GenCol
**
-** The Expr.iTable value for Expr.op==TK_COLUMN nodes of the expression
-** is set to -1 and the Expr.iColumn value is set to the column number.
+** In all cases except (4), the Expr.iTable value for Expr.op==TK_COLUMN
+** nodes of the expression is set to -1 and the Expr.iColumn value is
+** set to the column number. In case (4), TK_COLUMN nodes cause an error.
**
** Any errors cause an error message to be set in pParse.
*/
-SQLITE_PRIVATE void sqlite3ResolveSelfReference(
- Parse *pParse, /* Parsing context */
- Table *pTab, /* The table being referenced */
- int type, /* NC_IsCheck or NC_PartIdx or NC_IdxExpr */
- Expr *pExpr, /* Expression to resolve. May be NULL. */
- ExprList *pList /* Expression list to resolve. May be NULL. */
+SQLITE_PRIVATE int sqlite3ResolveSelfReference(
+ Parse *pParse, /* Parsing context */
+ Table *pTab, /* The table being referenced, or NULL */
+ int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */
+ Expr *pExpr, /* Expression to resolve. May be NULL. */
+ ExprList *pList /* Expression list to resolve. May be NULL. */
){
SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
NameContext sNC; /* Name context for pParse->pNewTable */
+ int rc;
- assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr );
+ assert( type==0 || pTab!=0 );
+ assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
+ || type==NC_GenCol || pTab==0 );
memset(&sNC, 0, sizeof(sNC));
memset(&sSrc, 0, sizeof(sSrc));
- sSrc.nSrc = 1;
- sSrc.a[0].zName = pTab->zName;
- sSrc.a[0].pTab = pTab;
- sSrc.a[0].iCursor = -1;
+ if( pTab ){
+ sSrc.nSrc = 1;
+ sSrc.a[0].zName = pTab->zName;
+ sSrc.a[0].pTab = pTab;
+ sSrc.a[0].iCursor = -1;
+ if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){
+ /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP
+ ** schema elements */
+ type |= NC_FromDDL;
+ }
+ }
sNC.pParse = pParse;
sNC.pSrcList = &sSrc;
- sNC.ncFlags = type;
- if( sqlite3ResolveExprNames(&sNC, pExpr) ) return;
- if( pList ) sqlite3ResolveExprListNames(&sNC, pList);
+ sNC.ncFlags = type | NC_IsDDL;
+ if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc;
+ if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList);
+ return rc;
}
/************** End of resolve.c *********************************************/
@@ -96136,16 +108361,16 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree);
/*
** Return the affinity character for a single column of a table.
*/
-SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table *pTab, int iCol){
- assert( iCol<pTab->nCol );
- return iCol>=0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER;
+SQLITE_PRIVATE char sqlite3TableColumnAffinity(const Table *pTab, int iCol){
+ if( iCol<0 || NEVER(iCol>=pTab->nCol) ) return SQLITE_AFF_INTEGER;
+ return pTab->aCol[iCol].affinity;
}
/*
** Return the 'affinity' of the expression pExpr if any.
**
** If pExpr is a column, a reference to a column via an 'AS' alias,
-** or a sub-select with a column as the return value, then the
+** or a sub-select with a column as the return value, then the
** affinity of that column is returned. Otherwise, 0x00 is returned,
** indicating no affinity for the expression.
**
@@ -96157,32 +108382,122 @@ SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table *pTab, int iCol){
** SELECT a AS b FROM t1 WHERE b;
** SELECT * FROM t1 WHERE (select a from t1);
*/
-SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){
+SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){
int op;
- pExpr = sqlite3ExprSkipCollate(pExpr);
- if( pExpr->flags & EP_Generic ) return 0;
op = pExpr->op;
- if( op==TK_SELECT ){
- assert( pExpr->flags&EP_xIsSelect );
- return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
- }
- if( op==TK_REGISTER ) op = pExpr->op2;
+ while( 1 /* exit-by-break */ ){
+ if( op==TK_COLUMN || (op==TK_AGG_COLUMN && pExpr->y.pTab!=0) ){
+ assert( ExprUseYTab(pExpr) );
+ assert( pExpr->y.pTab!=0 );
+ return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
+ }
+ if( op==TK_SELECT ){
+ assert( ExprUseXSelect(pExpr) );
+ assert( pExpr->x.pSelect!=0 );
+ assert( pExpr->x.pSelect->pEList!=0 );
+ assert( pExpr->x.pSelect->pEList->a[0].pExpr!=0 );
+ return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
+ }
#ifndef SQLITE_OMIT_CAST
- if( op==TK_CAST ){
- assert( !ExprHasProperty(pExpr, EP_IntValue) );
- return sqlite3AffinityType(pExpr->u.zToken, 0);
- }
+ if( op==TK_CAST ){
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+ return sqlite3AffinityType(pExpr->u.zToken, 0);
+ }
#endif
- if( (op==TK_AGG_COLUMN || op==TK_COLUMN) && pExpr->y.pTab ){
- return sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
- }
- if( op==TK_SELECT_COLUMN ){
- assert( pExpr->pLeft->flags&EP_xIsSelect );
- return sqlite3ExprAffinity(
- pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
- );
+ if( op==TK_SELECT_COLUMN ){
+ assert( pExpr->pLeft!=0 && ExprUseXSelect(pExpr->pLeft) );
+ assert( pExpr->iColumn < pExpr->iTable );
+ assert( pExpr->iColumn >= 0 );
+ assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr );
+ return sqlite3ExprAffinity(
+ pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
+ );
+ }
+ if( op==TK_VECTOR ){
+ assert( ExprUseXList(pExpr) );
+ return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr);
+ }
+ if( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){
+ assert( pExpr->op==TK_COLLATE
+ || pExpr->op==TK_IF_NULL_ROW
+ || (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) );
+ pExpr = pExpr->pLeft;
+ op = pExpr->op;
+ continue;
+ }
+ if( op!=TK_REGISTER || (op = pExpr->op2)==TK_REGISTER ) break;
}
- return pExpr->affinity;
+ return pExpr->affExpr;
+}
+
+/*
+** Make a guess at all the possible datatypes of the result that could
+** be returned by an expression. Return a bitmask indicating the answer:
+**
+** 0x01 Numeric
+** 0x02 Text
+** 0x04 Blob
+**
+** If the expression must return NULL, then 0x00 is returned.
+*/
+SQLITE_PRIVATE int sqlite3ExprDataType(const Expr *pExpr){
+ while( pExpr ){
+ switch( pExpr->op ){
+ case TK_COLLATE:
+ case TK_IF_NULL_ROW:
+ case TK_UPLUS: {
+ pExpr = pExpr->pLeft;
+ break;
+ }
+ case TK_NULL: {
+ pExpr = 0;
+ break;
+ }
+ case TK_STRING: {
+ return 0x02;
+ }
+ case TK_BLOB: {
+ return 0x04;
+ }
+ case TK_CONCAT: {
+ return 0x06;
+ }
+ case TK_VARIABLE:
+ case TK_AGG_FUNCTION:
+ case TK_FUNCTION: {
+ return 0x07;
+ }
+ case TK_COLUMN:
+ case TK_AGG_COLUMN:
+ case TK_SELECT:
+ case TK_CAST:
+ case TK_SELECT_COLUMN:
+ case TK_VECTOR: {
+ int aff = sqlite3ExprAffinity(pExpr);
+ if( aff>=SQLITE_AFF_NUMERIC ) return 0x05;
+ if( aff==SQLITE_AFF_TEXT ) return 0x06;
+ return 0x07;
+ }
+ case TK_CASE: {
+ int res = 0;
+ int ii;
+ ExprList *pList = pExpr->x.pList;
+ assert( ExprUseXList(pExpr) && pList!=0 );
+ assert( pList->nExpr > 0);
+ for(ii=1; ii<pList->nExpr; ii+=2){
+ res |= sqlite3ExprDataType(pList->a[ii].pExpr);
+ }
+ if( pList->nExpr % 2 ){
+ res |= sqlite3ExprDataType(pList->a[pList->nExpr-1].pExpr);
+ }
+ return res;
+ }
+ default: {
+ return 0x01;
+ }
+ } /* End of switch(op) */
+ } /* End of while(pExpr) */
+ return 0x00;
}
/*
@@ -96194,7 +108509,7 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){
** and the pExpr parameter is returned unchanged.
*/
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(
- Parse *pParse, /* Parsing context */
+ const Parse *pParse, /* Parsing context */
Expr *pExpr, /* Add the "COLLATE" clause to this expression */
const Token *pCollName, /* Name of collating sequence */
int dequote /* True to dequote pCollName */
@@ -96209,7 +108524,11 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(
}
return pExpr;
}
-SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){
+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(
+ const Parse *pParse, /* Parsing context */
+ Expr *pExpr, /* Add the "COLLATE" clause to this expression */
+ const char *zC /* The collating sequence name */
+){
Token s;
assert( zC!=0 );
sqlite3TokenInit(&s, (char*)zC);
@@ -96217,13 +108536,25 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, con
}
/*
-** Skip over any TK_COLLATE operators and any unlikely()
-** or likelihood() function at the root of an expression.
+** Skip over any TK_COLLATE operators.
*/
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
+ assert( pExpr->op==TK_COLLATE );
+ pExpr = pExpr->pLeft;
+ }
+ return pExpr;
+}
+
+/*
+** Skip over any TK_COLLATE operators and/or any unlikely()
+** or likelihood() or likely() functions at the root of an
+** expression.
+*/
+SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
+ while( pExpr && ExprHasProperty(pExpr, EP_Skip|EP_Unlikely) ){
if( ExprHasProperty(pExpr, EP_Unlikely) ){
- assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
+ assert( ExprUseXList(pExpr) );
assert( pExpr->x.pList->nExpr>0 );
assert( pExpr->op==TK_FUNCTION );
pExpr = pExpr->x.pList->a[0].pExpr;
@@ -96231,7 +108562,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
assert( pExpr->op==TK_COLLATE );
pExpr = pExpr->pLeft;
}
- }
+ }
return pExpr;
}
@@ -96249,22 +108580,21 @@ SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
** COLLATE operators take first precedence. Left operands take
** precedence over right operands.
*/
-SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
+SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){
sqlite3 *db = pParse->db;
CollSeq *pColl = 0;
- Expr *p = pExpr;
+ const Expr *p = pExpr;
while( p ){
int op = p->op;
- if( p->flags & EP_Generic ) break;
- if( (op==TK_AGG_COLUMN || op==TK_COLUMN
- || op==TK_REGISTER || op==TK_TRIGGER)
- && p->y.pTab!=0
+ if( op==TK_REGISTER ) op = p->op2;
+ if( (op==TK_AGG_COLUMN && p->y.pTab!=0)
+ || op==TK_COLUMN || op==TK_TRIGGER
){
- /* op==TK_REGISTER && p->y.pTab!=0 happens when pExpr was originally
- ** a TK_COLUMN but was previously evaluated and cached in a register */
- int j = p->iColumn;
- if( j>=0 ){
- const char *zColl = p->y.pTab->aCol[j].zColl;
+ int j;
+ assert( ExprUseYTab(p) );
+ assert( p->y.pTab!=0 );
+ if( (j = p->iColumn)>=0 ){
+ const char *zColl = sqlite3ColumnColl(&p->y.pTab->aCol[j]);
pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0);
}
break;
@@ -96273,7 +108603,13 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
p = p->pLeft;
continue;
}
- if( op==TK_COLLATE || (op==TK_REGISTER && p->op2==TK_COLLATE) ){
+ if( op==TK_VECTOR ){
+ assert( ExprUseXList(p) );
+ p = p->x.pList->a[0].pExpr;
+ continue;
+ }
+ if( op==TK_COLLATE ){
+ assert( !ExprHasProperty(p, EP_IntValue) );
pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken);
break;
}
@@ -96283,13 +108619,10 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
}else{
Expr *pNext = p->pRight;
/* The Expr.x union is never used at the same time as Expr.pRight */
- assert( p->x.pList==0 || p->pRight==0 );
- /* p->flags holds EP_Collate and p->pLeft->flags does not. And
- ** p->x.pSelect cannot. So if p->x.pLeft exists, it must hold at
- ** least one EP_Collate. Thus the following two ALWAYS. */
- if( p->x.pList!=0 && ALWAYS(!ExprHasProperty(p, EP_xIsSelect)) ){
+ assert( !ExprUseXList(p) || p->x.pList==0 || p->pRight==0 );
+ if( ExprUseXList(p) && p->x.pList!=0 && !db->mallocFailed ){
int i;
- for(i=0; ALWAYS(i<p->x.pList->nExpr); i++){
+ for(i=0; i<p->x.pList->nExpr; i++){
if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){
pNext = p->x.pList->a[i].pExpr;
break;
@@ -96302,7 +108635,7 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
break;
}
}
- if( sqlite3CheckCollSeq(pParse, pColl) ){
+ if( sqlite3CheckCollSeq(pParse, pColl) ){
pColl = 0;
}
return pColl;
@@ -96311,14 +108644,14 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
/*
** Return the collation sequence for the expression pExpr. If
** there is no defined collating sequence, return a pointer to the
-** defautl collation sequence.
+** default collation sequence.
**
** See also: sqlite3ExprCollSeq()
**
** The sqlite3ExprCollSeq() routine works the same except that it
** returns NULL if there is no defined collation.
*/
-SQLITE_PRIVATE CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, Expr *pExpr){
+SQLITE_PRIVATE CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, const Expr *pExpr){
CollSeq *p = sqlite3ExprCollSeq(pParse, pExpr);
if( p==0 ) p = pParse->db->pDfltColl;
assert( p!=0 );
@@ -96328,7 +108661,7 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprNNCollSeq(Parse *pParse, Expr *pExpr){
/*
** Return TRUE if the two expressions have equivalent collating sequences.
*/
-SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse *pParse, Expr *pE1, Expr *pE2){
+SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse *pParse, const Expr *pE1, const Expr *pE2){
CollSeq *pColl1 = sqlite3ExprNNCollSeq(pParse, pE1);
CollSeq *pColl2 = sqlite3ExprNNCollSeq(pParse, pE2);
return sqlite3StrICmp(pColl1->zName, pColl2->zName)==0;
@@ -96339,9 +108672,9 @@ SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse *pParse, Expr *pE1, Expr *pE2){
** type affinity of the other operand. This routine returns the
** type affinity that should be used for the comparison operator.
*/
-SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2){
+SQLITE_PRIVATE char sqlite3CompareAffinity(const Expr *pExpr, char aff2){
char aff1 = sqlite3ExprAffinity(pExpr);
- if( aff1 && aff2 ){
+ if( aff1>SQLITE_AFF_NONE && aff2>SQLITE_AFF_NONE ){
/* Both sides of the comparison are columns. If one has numeric
** affinity, use that. Otherwise use no affinity.
*/
@@ -96350,15 +108683,10 @@ SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2){
}else{
return SQLITE_AFF_BLOB;
}
- }else if( !aff1 && !aff2 ){
- /* Neither side of the comparison is a column. Compare the
- ** results directly.
- */
- return SQLITE_AFF_BLOB;
}else{
/* One side is a column, the other is not. Use the columns affinity. */
- assert( aff1==0 || aff2==0 );
- return (aff1 + aff2);
+ assert( aff1<=SQLITE_AFF_NONE || aff2<=SQLITE_AFF_NONE );
+ return (aff1<=SQLITE_AFF_NONE ? aff2 : aff1) | SQLITE_AFF_NONE;
}
}
@@ -96366,7 +108694,7 @@ SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2){
** pExpr is a comparison operator. Return the type affinity that should
** be applied to both operands prior to doing the comparison.
*/
-static char comparisonAffinity(Expr *pExpr){
+static char comparisonAffinity(const Expr *pExpr){
char aff;
assert( pExpr->op==TK_EQ || pExpr->op==TK_IN || pExpr->op==TK_LT ||
pExpr->op==TK_GT || pExpr->op==TK_GE || pExpr->op==TK_LE ||
@@ -96375,7 +108703,7 @@ static char comparisonAffinity(Expr *pExpr){
aff = sqlite3ExprAffinity(pExpr->pLeft);
if( pExpr->pRight ){
aff = sqlite3CompareAffinity(pExpr->pRight, aff);
- }else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ }else if( ExprUseXSelect(pExpr) ){
aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff);
}else if( aff==0 ){
aff = SQLITE_AFF_BLOB;
@@ -96389,23 +108717,26 @@ static char comparisonAffinity(Expr *pExpr){
** if the index with affinity idx_affinity may be used to implement
** the comparison in pExpr.
*/
-SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){
+SQLITE_PRIVATE int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity){
char aff = comparisonAffinity(pExpr);
- switch( aff ){
- case SQLITE_AFF_BLOB:
- return 1;
- case SQLITE_AFF_TEXT:
- return idx_affinity==SQLITE_AFF_TEXT;
- default:
- return sqlite3IsNumericAffinity(idx_affinity);
+ if( aff<SQLITE_AFF_TEXT ){
+ return 1;
+ }
+ if( aff==SQLITE_AFF_TEXT ){
+ return idx_affinity==SQLITE_AFF_TEXT;
}
+ return sqlite3IsNumericAffinity(idx_affinity);
}
/*
** Return the P5 value that should be used for a binary comparison
** opcode (OP_Eq, OP_Ge etc.) used to compare pExpr1 and pExpr2.
*/
-static u8 binaryCompareP5(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){
+static u8 binaryCompareP5(
+ const Expr *pExpr1, /* Left operand */
+ const Expr *pExpr2, /* Right operand */
+ int jumpIfNull /* Extra flags added to P5 */
+){
u8 aff = (char)sqlite3ExprAffinity(pExpr2);
aff = (u8)sqlite3CompareAffinity(pExpr1, aff) | (u8)jumpIfNull;
return aff;
@@ -96424,9 +108755,9 @@ static u8 binaryCompareP5(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){
** it is not considered.
*/
SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(
- Parse *pParse,
- Expr *pLeft,
- Expr *pRight
+ Parse *pParse,
+ const Expr *pLeft,
+ const Expr *pRight
){
CollSeq *pColl;
assert( pLeft );
@@ -96443,6 +108774,22 @@ SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(
return pColl;
}
+/* Expression p is a comparison operator. Return a collation sequence
+** appropriate for the comparison operator.
+**
+** This is normally just a wrapper around sqlite3BinaryCompareCollSeq().
+** However, if the OP_Commuted flag is set, then the order of the operands
+** is reversed in the sqlite3BinaryCompareCollSeq() call so that the
+** correct collating sequence is found.
+*/
+SQLITE_PRIVATE CollSeq *sqlite3ExprCompareCollSeq(Parse *pParse, const Expr *p){
+ if( ExprHasProperty(p, EP_Commuted) ){
+ return sqlite3BinaryCompareCollSeq(pParse, p->pRight, p->pLeft);
+ }else{
+ return sqlite3BinaryCompareCollSeq(pParse, p->pLeft, p->pRight);
+ }
+}
+
/*
** Generate code for a comparison operator.
*/
@@ -96453,13 +108800,19 @@ static int codeCompare(
int opcode, /* The comparison opcode */
int in1, int in2, /* Register holding operands */
int dest, /* Jump here if true. */
- int jumpIfNull /* If true, jump if either operand is NULL */
+ int jumpIfNull, /* If true, jump if either operand is NULL */
+ int isCommuted /* The comparison has been commuted */
){
int p5;
int addr;
CollSeq *p4;
- p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight);
+ if( pParse->nErr ) return 0;
+ if( isCommuted ){
+ p4 = sqlite3BinaryCompareCollSeq(pParse, pRight, pLeft);
+ }else{
+ p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight);
+ }
p5 = binaryCompareP5(pLeft, pRight, jumpIfNull);
addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1,
(void*)p4, P4_COLLSEQ);
@@ -96476,22 +108829,24 @@ static int codeCompare(
** But a TK_SELECT might be either a vector or a scalar. It is only
** considered a vector if it has two or more result columns.
*/
-SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr){
+SQLITE_PRIVATE int sqlite3ExprIsVector(const Expr *pExpr){
return sqlite3ExprVectorSize(pExpr)>1;
}
/*
-** If the expression passed as the only argument is of type TK_VECTOR
+** If the expression passed as the only argument is of type TK_VECTOR
** return the number of expressions in the vector. Or, if the expression
** is a sub-select, return the number of columns in the sub-select. For
** any other type of expression, return 1.
*/
-SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr){
+SQLITE_PRIVATE int sqlite3ExprVectorSize(const Expr *pExpr){
u8 op = pExpr->op;
if( op==TK_REGISTER ) op = pExpr->op2;
if( op==TK_VECTOR ){
+ assert( ExprUseXList(pExpr) );
return pExpr->x.pList->nExpr;
}else if( op==TK_SELECT ){
+ assert( ExprUseXSelect(pExpr) );
return pExpr->x.pSelect->pEList->nExpr;
}else{
return 1;
@@ -96514,12 +108869,14 @@ SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr){
** been positioned.
*/
SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){
- assert( i<sqlite3ExprVectorSize(pVector) );
+ assert( i<sqlite3ExprVectorSize(pVector) || pVector->op==TK_ERROR );
if( sqlite3ExprIsVector(pVector) ){
assert( pVector->op2==0 || pVector->op==TK_REGISTER );
if( pVector->op==TK_SELECT || pVector->op2==TK_SELECT ){
+ assert( ExprUseXSelect(pVector) );
return pVector->x.pSelect->pEList->a[i].pExpr;
}else{
+ assert( ExprUseXList(pVector) );
return pVector->x.pList->a[i].pExpr;
}
}
@@ -96531,7 +108888,7 @@ SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){
** sqlite3ExprCode() will generate all necessary code to compute
** the iField-th column of the vector expression pVector.
**
-** It is ok for pVector to be a scalar (as long as iField==0).
+** It is ok for pVector to be a scalar (as long as iField==0).
** In that case, this routine works like sqlite3ExprDup().
**
** The caller owns the returned Expr object and is responsible for
@@ -96550,11 +108907,12 @@ SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){
SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(
Parse *pParse, /* Parsing context */
Expr *pVector, /* The vector. List of expressions or a sub-SELECT */
- int iField /* Which column of the vector to return */
+ int iField, /* Which column of the vector to return */
+ int nField /* Total number of columns in the vector */
){
Expr *pRet;
if( pVector->op==TK_SELECT ){
- assert( pVector->flags & EP_xIsSelect );
+ assert( ExprUseXSelect(pVector) );
/* The TK_SELECT_COLUMN Expr node:
**
** pLeft: pVector containing TK_SELECT. Not deleted.
@@ -96573,12 +108931,23 @@ SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(
*/
pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0);
if( pRet ){
+ ExprSetProperty(pRet, EP_FullSize);
+ pRet->iTable = nField;
pRet->iColumn = iField;
pRet->pLeft = pVector;
}
- assert( pRet==0 || pRet->iTable==0 );
}else{
- if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr;
+ if( pVector->op==TK_VECTOR ){
+ Expr **ppVector;
+ assert( ExprUseXList(pVector) );
+ ppVector = &pVector->x.pList->a[iField].pExpr;
+ pVector = *ppVector;
+ if( IN_RENAME_OBJECT ){
+ /* This must be a vector UPDATE inside a trigger */
+ *ppVector = 0;
+ return pVector;
+ }
+ }
pRet = sqlite3ExprDup(pParse->db, pVector, 0);
}
return pRet;
@@ -96586,7 +108955,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(
/*
** If expression pExpr is of type TK_SELECT, generate code to evaluate
-** it. Return the register in which the result is stored (or, if the
+** it. Return the register in which the result is stored (or, if the
** sub-select returns more than one column, the first in an array
** of registers in which the result is stored).
**
@@ -96596,7 +108965,7 @@ static int exprCodeSubselect(Parse *pParse, Expr *pExpr){
int reg = 0;
#ifndef SQLITE_OMIT_SUBQUERY
if( pExpr->op==TK_SELECT ){
- reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
+ reg = sqlite3CodeSubselect(pParse, pExpr);
}
#endif
return reg;
@@ -96608,10 +108977,10 @@ static int exprCodeSubselect(Parse *pParse, Expr *pExpr){
** the register number of a register that contains the value of
** element iField of the vector.
**
-** If pVector is a TK_SELECT expression, then code for it must have
+** If pVector is a TK_SELECT expression, then code for it must have
** already been generated using the exprCodeSubselect() routine. In this
** case parameter regSelect should be the first in an array of registers
-** containing the results of the sub-select.
+** containing the results of the sub-select.
**
** If pVector is of type TK_VECTOR, then code for the requested field
** is generated. In this case (*pRegFree) may be set to the number of
@@ -96629,17 +108998,22 @@ static int exprVectorRegister(
int *pRegFree /* OUT: Temp register to free */
){
u8 op = pVector->op;
- assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT );
+ assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT || op==TK_ERROR );
if( op==TK_REGISTER ){
*ppExpr = sqlite3VectorFieldSubexpr(pVector, iField);
return pVector->iTable+iField;
}
if( op==TK_SELECT ){
+ assert( ExprUseXSelect(pVector) );
*ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr;
return regSelect+iField;
}
- *ppExpr = pVector->x.pList->a[iField].pExpr;
- return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree);
+ if( op==TK_VECTOR ){
+ assert( ExprUseXList(pVector) );
+ *ppExpr = pVector->x.pList->a[iField].pExpr;
+ return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree);
+ }
+ return 0;
}
/*
@@ -96668,37 +109042,44 @@ static void codeVectorCompare(
int regLeft = 0;
int regRight = 0;
u8 opx = op;
- int addrDone = sqlite3VdbeMakeLabel(v);
+ int addrCmp = 0;
+ int addrDone = sqlite3VdbeMakeLabel(pParse);
+ int isCommuted = ExprHasProperty(pExpr,EP_Commuted);
+ assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
+ if( pParse->nErr ) return;
if( nLeft!=sqlite3ExprVectorSize(pRight) ){
sqlite3ErrorMsg(pParse, "row value misused");
return;
}
- assert( pExpr->op==TK_EQ || pExpr->op==TK_NE
- || pExpr->op==TK_IS || pExpr->op==TK_ISNOT
- || pExpr->op==TK_LT || pExpr->op==TK_GT
- || pExpr->op==TK_LE || pExpr->op==TK_GE
+ assert( pExpr->op==TK_EQ || pExpr->op==TK_NE
+ || pExpr->op==TK_IS || pExpr->op==TK_ISNOT
+ || pExpr->op==TK_LT || pExpr->op==TK_GT
+ || pExpr->op==TK_LE || pExpr->op==TK_GE
);
assert( pExpr->op==op || (pExpr->op==TK_IS && op==TK_EQ)
|| (pExpr->op==TK_ISNOT && op==TK_NE) );
assert( p5==0 || pExpr->op!=op );
assert( p5==SQLITE_NULLEQ || pExpr->op==op );
- p5 |= SQLITE_STOREP2;
- if( opx==TK_LE ) opx = TK_LT;
- if( opx==TK_GE ) opx = TK_GT;
+ if( op==TK_LE ) opx = TK_LT;
+ if( op==TK_GE ) opx = TK_GT;
+ if( op==TK_NE ) opx = TK_EQ;
regLeft = exprCodeSubselect(pParse, pLeft);
regRight = exprCodeSubselect(pParse, pRight);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, dest);
for(i=0; 1 /*Loop exits by "break"*/; i++){
int regFree1 = 0, regFree2 = 0;
- Expr *pL, *pR;
+ Expr *pL = 0, *pR = 0;
int r1, r2;
assert( i>=0 && i<nLeft );
+ if( addrCmp ) sqlite3VdbeJumpHere(v, addrCmp);
r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, &regFree1);
r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, &regFree2);
- codeCompare(pParse, pL, pR, opx, r1, r2, dest, p5);
+ addrCmp = sqlite3VdbeCurrentAddr(v);
+ codeCompare(pParse, pL, pR, opx, r1, r2, addrDone, p5, isCommuted);
testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
@@ -96707,26 +109088,32 @@ static void codeVectorCompare(
testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
sqlite3ReleaseTempReg(pParse, regFree1);
sqlite3ReleaseTempReg(pParse, regFree2);
+ if( (opx==TK_LT || opx==TK_GT) && i<nLeft-1 ){
+ addrCmp = sqlite3VdbeAddOp0(v, OP_ElseEq);
+ testcase(opx==TK_LT); VdbeCoverageIf(v,opx==TK_LT);
+ testcase(opx==TK_GT); VdbeCoverageIf(v,opx==TK_GT);
+ }
+ if( p5==SQLITE_NULLEQ ){
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, dest);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_ZeroOrNull, r1, dest, r2);
+ }
if( i==nLeft-1 ){
break;
}
if( opx==TK_EQ ){
- sqlite3VdbeAddOp2(v, OP_IfNot, dest, addrDone); VdbeCoverage(v);
- p5 |= SQLITE_KEEPNULL;
- }else if( opx==TK_NE ){
- sqlite3VdbeAddOp2(v, OP_If, dest, addrDone); VdbeCoverage(v);
- p5 |= SQLITE_KEEPNULL;
+ sqlite3VdbeAddOp2(v, OP_NotNull, dest, addrDone); VdbeCoverage(v);
}else{
assert( op==TK_LT || op==TK_GT || op==TK_LE || op==TK_GE );
- sqlite3VdbeAddOp2(v, OP_ElseNotEq, 0, addrDone);
- VdbeCoverageIf(v, op==TK_LT);
- VdbeCoverageIf(v, op==TK_GT);
- VdbeCoverageIf(v, op==TK_LE);
- VdbeCoverageIf(v, op==TK_GE);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone);
if( i==nLeft-2 ) opx = op;
}
}
+ sqlite3VdbeJumpHere(v, addrCmp);
sqlite3VdbeResolveLabel(v, addrDone);
+ if( op==TK_NE ){
+ sqlite3VdbeAddOp2(v, OP_Not, dest, dest);
+ }
}
#if SQLITE_MAX_EXPR_DEPTH>0
@@ -96739,7 +109126,7 @@ SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse *pParse, int nHeight){
int rc = SQLITE_OK;
int mxHeight = pParse->db->aLimit[SQLITE_LIMIT_EXPR_DEPTH];
if( nHeight>mxHeight ){
- sqlite3ErrorMsg(pParse,
+ sqlite3ErrorMsg(pParse,
"Expression tree is too large (maximum depth %d)", mxHeight
);
rc = SQLITE_ERROR;
@@ -96756,14 +109143,14 @@ SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse *pParse, int nHeight){
** to by pnHeight, the second parameter, then set *pnHeight to that
** value.
*/
-static void heightOfExpr(Expr *p, int *pnHeight){
+static void heightOfExpr(const Expr *p, int *pnHeight){
if( p ){
if( p->nHeight>*pnHeight ){
*pnHeight = p->nHeight;
}
}
}
-static void heightOfExprList(ExprList *p, int *pnHeight){
+static void heightOfExprList(const ExprList *p, int *pnHeight){
if( p ){
int i;
for(i=0; i<p->nExpr; i++){
@@ -96771,8 +109158,8 @@ static void heightOfExprList(ExprList *p, int *pnHeight){
}
}
}
-static void heightOfSelect(Select *pSelect, int *pnHeight){
- Select *p;
+static void heightOfSelect(const Select *pSelect, int *pnHeight){
+ const Select *p;
for(p=pSelect; p; p=p->pPrior){
heightOfExpr(p->pWhere, pnHeight);
heightOfExpr(p->pHaving, pnHeight);
@@ -96784,20 +109171,21 @@ static void heightOfSelect(Select *pSelect, int *pnHeight){
}
/*
-** Set the Expr.nHeight variable in the structure passed as an
-** argument. An expression with no children, Expr.pList or
+** Set the Expr.nHeight variable in the structure passed as an
+** argument. An expression with no children, Expr.pList or
** Expr.pSelect member has a height of 1. Any other expression
-** has a height equal to the maximum height of any other
+** has a height equal to the maximum height of any other
** referenced Expr plus one.
**
** Also propagate EP_Propagate flags up from Expr.x.pList to Expr.flags,
** if appropriate.
*/
static void exprSetHeight(Expr *p){
- int nHeight = 0;
- heightOfExpr(p->pLeft, &nHeight);
- heightOfExpr(p->pRight, &nHeight);
- if( ExprHasProperty(p, EP_xIsSelect) ){
+ int nHeight = p->pLeft ? p->pLeft->nHeight : 0;
+ if( NEVER(p->pRight) && p->pRight->nHeight>nHeight ){
+ nHeight = p->pRight->nHeight;
+ }
+ if( ExprUseXSelect(p) ){
heightOfSelect(p->x.pSelect, &nHeight);
}else if( p->x.pList ){
heightOfExprList(p->x.pList, &nHeight);
@@ -96812,7 +109200,7 @@ static void exprSetHeight(Expr *p){
** leave an error in pParse.
**
** Also propagate all EP_Propagate flags from the Expr.x.pList into
-** Expr.flags.
+** Expr.flags.
*/
SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){
if( pParse->nErr ) return;
@@ -96824,7 +109212,7 @@ SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){
** Return the maximum height of any expression tree referenced
** by the select statement passed as an argument.
*/
-SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){
+SQLITE_PRIVATE int sqlite3SelectExprHeight(const Select *p){
int nHeight = 0;
heightOfSelect(p, &nHeight);
return nHeight;
@@ -96832,10 +109220,11 @@ SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){
#else /* ABOVE: Height enforcement enabled. BELOW: Height enforcement off */
/*
** Propagate all EP_Propagate flags from the Expr.x.pList into
-** Expr.flags.
+** Expr.flags.
*/
SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){
- if( p && p->x.pList && !ExprHasProperty(p, EP_xIsSelect) ){
+ if( pParse->nErr ) return;
+ if( p && ExprUseXList(p) && p->x.pList ){
p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList);
}
}
@@ -96843,6 +109232,15 @@ SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){
#endif /* SQLITE_MAX_EXPR_DEPTH>0 */
/*
+** Set the error offset for an Expr node, if possible.
+*/
+SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr *pExpr, int iOfst){
+ if( pExpr==0 ) return;
+ if( NEVER(ExprUseWJoin(pExpr)) ) return;
+ pExpr->w.iOfst = iOfst;
+}
+
+/*
** This routine is the core allocator for Expr nodes.
**
** Construct a new expression node and return a pointer to it. Memory
@@ -96887,7 +109285,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
pNew->iAgg = -1;
if( pToken ){
if( nExtra==0 ){
- pNew->flags |= EP_IntValue|EP_Leaf;
+ pNew->flags |= EP_IntValue|EP_Leaf|(iValue?EP_IsTrue:EP_IsFalse);
pNew->u.iValue = iValue;
}else{
pNew->u.zToken = (char*)&pNew[1];
@@ -96895,14 +109293,13 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n);
pNew->u.zToken[pToken->n] = 0;
if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){
- if( pNew->u.zToken[0]=='"' ) pNew->flags |= EP_DblQuoted;
- sqlite3Dequote(pNew->u.zToken);
+ sqlite3DequoteExpr(pNew);
}
}
}
#if SQLITE_MAX_EXPR_DEPTH>0
pNew->nHeight = 1;
-#endif
+#endif
}
return pNew;
}
@@ -96939,15 +109336,26 @@ SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(
sqlite3ExprDelete(db, pLeft);
sqlite3ExprDelete(db, pRight);
}else{
+ assert( ExprUseXList(pRoot) );
+ assert( pRoot->x.pSelect==0 );
if( pRight ){
pRoot->pRight = pRight;
pRoot->flags |= EP_Propagate & pRight->flags;
+#if SQLITE_MAX_EXPR_DEPTH>0
+ pRoot->nHeight = pRight->nHeight+1;
+ }else{
+ pRoot->nHeight = 1;
+#endif
}
if( pLeft ){
pRoot->pLeft = pLeft;
pRoot->flags |= EP_Propagate & pLeft->flags;
+#if SQLITE_MAX_EXPR_DEPTH>0
+ if( pLeft->nHeight>=pRoot->nHeight ){
+ pRoot->nHeight = pLeft->nHeight+1;
+ }
+#endif
}
- exprSetHeight(pRoot);
}
}
@@ -96965,20 +109373,16 @@ SQLITE_PRIVATE Expr *sqlite3PExpr(
Expr *pRight /* Right operand */
){
Expr *p;
- if( op==TK_AND && pParse->nErr==0 ){
- /* Take advantage of short-circuit false optimization for AND */
- p = sqlite3ExprAnd(pParse->db, pLeft, pRight);
- }else{
- p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr));
- if( p ){
- memset(p, 0, sizeof(Expr));
- p->op = op & TKFLG_MASK;
- p->iAgg = -1;
- }
+ p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr));
+ if( p ){
+ memset(p, 0, sizeof(Expr));
+ p->op = op & 0xff;
+ p->iAgg = -1;
sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight);
- }
- if( p ) {
sqlite3ExprCheckHeight(pParse, p->nHeight);
+ }else{
+ sqlite3ExprDelete(pParse->db, pLeft);
+ sqlite3ExprDelete(pParse->db, pRight);
}
return p;
}
@@ -96998,55 +109402,89 @@ SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse *pParse, Expr *pExpr, Select *pS
}
}
-
/*
-** If the expression is always either TRUE or FALSE (respectively),
-** then return 1. If one cannot determine the truth value of the
-** expression at compile-time return 0.
+** Expression list pEList is a list of vector values. This function
+** converts the contents of pEList to a VALUES(...) Select statement
+** returning 1 row for each element of the list. For example, the
+** expression list:
**
-** This is an optimization. If is OK to return 0 here even if
-** the expression really is always false or false (a false negative).
-** But it is a bug to return 1 if the expression might have different
-** boolean values in different circumstances (a false positive.)
+** ( (1,2), (3,4) (5,6) )
**
-** Note that if the expression is part of conditional for a
-** LEFT JOIN, then we cannot determine at compile-time whether or not
-** is it true or false, so always return 0.
+** is translated to the equivalent of:
+**
+** VALUES(1,2), (3,4), (5,6)
+**
+** Each of the vector values in pEList must contain exactly nElem terms.
+** If a list element that is not a vector or does not contain nElem terms,
+** an error message is left in pParse.
+**
+** This is used as part of processing IN(...) expressions with a list
+** of vectors on the RHS. e.g. "... IN ((1,2), (3,4), (5,6))".
*/
-static int exprAlwaysTrue(Expr *p){
- int v = 0;
- if( ExprHasProperty(p, EP_FromJoin) ) return 0;
- if( !sqlite3ExprIsInteger(p, &v) ) return 0;
- return v!=0;
-}
-static int exprAlwaysFalse(Expr *p){
- int v = 0;
- if( ExprHasProperty(p, EP_FromJoin) ) return 0;
- if( !sqlite3ExprIsInteger(p, &v) ) return 0;
- return v==0;
+SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse *pParse, int nElem, ExprList *pEList){
+ int ii;
+ Select *pRet = 0;
+ assert( nElem>1 );
+ for(ii=0; ii<pEList->nExpr; ii++){
+ Select *pSel;
+ Expr *pExpr = pEList->a[ii].pExpr;
+ int nExprElem;
+ if( pExpr->op==TK_VECTOR ){
+ assert( ExprUseXList(pExpr) );
+ nExprElem = pExpr->x.pList->nExpr;
+ }else{
+ nExprElem = 1;
+ }
+ if( nExprElem!=nElem ){
+ sqlite3ErrorMsg(pParse, "IN(...) element has %d term%s - expected %d",
+ nExprElem, nExprElem>1?"s":"", nElem
+ );
+ break;
+ }
+ assert( ExprUseXList(pExpr) );
+ pSel = sqlite3SelectNew(pParse, pExpr->x.pList, 0, 0, 0, 0, 0, SF_Values,0);
+ pExpr->x.pList = 0;
+ if( pSel ){
+ if( pRet ){
+ pSel->op = TK_ALL;
+ pSel->pPrior = pRet;
+ }
+ pRet = pSel;
+ }
+ }
+
+ if( pRet && pRet->pPrior ){
+ pRet->selFlags |= SF_MultiValue;
+ }
+ sqlite3ExprListDelete(pParse->db, pEList);
+ return pRet;
}
/*
** Join two expressions using an AND operator. If either expression is
** NULL, then just return the other expression.
**
-** If one side or the other of the AND is known to be false, then instead
-** of returning an AND expression, just return a constant expression with
-** a value of false.
+** If one side or the other of the AND is known to be false, and neither side
+** is part of an ON clause, then instead of returning an AND expression,
+** just return a constant expression with a value of false.
*/
-SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3 *db, Expr *pLeft, Expr *pRight){
- if( pLeft==0 ){
+SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){
+ sqlite3 *db = pParse->db;
+ if( pLeft==0 ){
return pRight;
}else if( pRight==0 ){
return pLeft;
- }else if( exprAlwaysFalse(pLeft) || exprAlwaysFalse(pRight) ){
- sqlite3ExprDelete(db, pLeft);
- sqlite3ExprDelete(db, pRight);
- return sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[0], 0);
}else{
- Expr *pNew = sqlite3ExprAlloc(db, TK_AND, 0, 0);
- sqlite3ExprAttachSubtrees(db, pNew, pLeft, pRight);
- return pNew;
+ u32 f = pLeft->flags | pRight->flags;
+ if( (f&(EP_OuterON|EP_InnerON|EP_IsFalse))==EP_IsFalse
+ && !IN_RENAME_OBJECT
+ ){
+ sqlite3ExprDeferredDelete(pParse, pLeft);
+ sqlite3ExprDeferredDelete(pParse, pRight);
+ return sqlite3Expr(db, TK_INTEGER, "0");
+ }else{
+ return sqlite3PExpr(pParse, TK_AND, pLeft, pRight);
+ }
}
}
@@ -97057,7 +109495,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3 *db, Expr *pLeft, Expr *pRight){
SQLITE_PRIVATE Expr *sqlite3ExprFunction(
Parse *pParse, /* Parsing context */
ExprList *pList, /* Argument list */
- Token *pToken, /* Name of the function */
+ const Token *pToken, /* Name of the function */
int eDistinct /* SF_Distinct or SF_ALL or 0 */
){
Expr *pNew;
@@ -97068,20 +109506,120 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction(
sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */
return 0;
}
- if( pList && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
+ assert( !ExprHasProperty(pNew, EP_InnerON|EP_OuterON) );
+ pNew->w.iOfst = (int)(pToken->z - pParse->zTail);
+ if( pList
+ && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG]
+ && !pParse->nested
+ ){
sqlite3ErrorMsg(pParse, "too many arguments on function %T", pToken);
}
pNew->x.pList = pList;
ExprSetProperty(pNew, EP_HasFunc);
- assert( !ExprHasProperty(pNew, EP_xIsSelect) );
+ assert( ExprUseXList(pNew) );
sqlite3ExprSetHeightAndFlags(pParse, pNew);
if( eDistinct==SF_Distinct ) ExprSetProperty(pNew, EP_Distinct);
return pNew;
}
/*
+** Report an error when attempting to use an ORDER BY clause within
+** the arguments of a non-aggregate function.
+*/
+SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse *pParse, Expr *p){
+ sqlite3ErrorMsg(pParse,
+ "ORDER BY may not be used with non-aggregate %#T()", p
+ );
+}
+
+/*
+** Attach an ORDER BY clause to a function call.
+**
+** functionname( arguments ORDER BY sortlist )
+** \_____________________/ \______/
+** pExpr pOrderBy
+**
+** The ORDER BY clause is inserted into a new Expr node of type TK_ORDER
+** and added to the Expr.pLeft field of the parent TK_FUNCTION node.
+*/
+SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(
+ Parse *pParse, /* Parsing context */
+ Expr *pExpr, /* The function call to which ORDER BY is to be added */
+ ExprList *pOrderBy /* The ORDER BY clause to add */
+){
+ Expr *pOB;
+ sqlite3 *db = pParse->db;
+ if( NEVER(pOrderBy==0) ){
+ assert( db->mallocFailed );
+ return;
+ }
+ if( pExpr==0 ){
+ assert( db->mallocFailed );
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+ assert( pExpr->op==TK_FUNCTION );
+ assert( pExpr->pLeft==0 );
+ assert( ExprUseXList(pExpr) );
+ if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){
+ /* Ignore ORDER BY on zero-argument aggregates */
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pOrderBy);
+ return;
+ }
+ if( IsWindowFunc(pExpr) ){
+ sqlite3ExprOrderByAggregateError(pParse, pExpr);
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+
+ pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0);
+ if( pOB==0 ){
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+ pOB->x.pList = pOrderBy;
+ assert( ExprUseXList(pOB) );
+ pExpr->pLeft = pOB;
+ ExprSetProperty(pOB, EP_FullSize);
+}
+
+/*
+** Check to see if a function is usable according to current access
+** rules:
+**
+** SQLITE_FUNC_DIRECT - Only usable from top-level SQL
+**
+** SQLITE_FUNC_UNSAFE - Usable if TRUSTED_SCHEMA or from
+** top-level SQL
+**
+** If the function is not usable, create an error.
+*/
+SQLITE_PRIVATE void sqlite3ExprFunctionUsable(
+ Parse *pParse, /* Parsing and code generating context */
+ const Expr *pExpr, /* The function invocation */
+ const FuncDef *pDef /* The function being invoked */
+){
+ assert( !IN_RENAME_OBJECT );
+ assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 );
+ if( ExprHasProperty(pExpr, EP_FromDDL) ){
+ if( (pDef->funcFlags & SQLITE_FUNC_DIRECT)!=0
+ || (pParse->db->flags & SQLITE_TrustedSchema)==0
+ ){
+ /* Functions prohibited in triggers and views if:
+ ** (1) tagged with SQLITE_DIRECTONLY
+ ** (2) not tagged with SQLITE_INNOCUOUS (which means it
+ ** is tagged with SQLITE_FUNC_UNSAFE) and
+ ** SQLITE_DBCONFIG_TRUSTED_SCHEMA is off (meaning
+ ** that the schema is possibly tainted).
+ */
+ sqlite3ErrorMsg(pParse, "unsafe use of %#T()", pExpr);
+ }
+ }
+}
+
+/*
** Assign a variable number to an expression that encodes a wildcard
-** in the original SQL statement.
+** in the original SQL statement.
**
** Wildcards consisting of a single "?" are assigned the next sequential
** variable number.
@@ -97130,6 +109668,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n
if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr);
return;
}
x = (ynVar)i;
@@ -97157,6 +109696,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n
pExpr->iColumn = x;
if( x>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
sqlite3ErrorMsg(pParse, "too many SQL variables");
+ sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr);
}
}
@@ -97165,50 +109705,94 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n
*/
static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
assert( p!=0 );
- /* Sanity check: Assert that the IntValue is non-negative if it exists */
- assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 );
-
- assert( !ExprHasProperty(p, EP_WinFunc) || p->y.pWin!=0 || db->mallocFailed );
- assert( p->op!=TK_FUNCTION || ExprHasProperty(p, EP_TokenOnly|EP_Reduced)
- || p->y.pWin==0 || ExprHasProperty(p, EP_WinFunc) );
+ assert( db!=0 );
+ assert( !ExprUseUValue(p) || p->u.iValue>=0 );
+ assert( !ExprUseYWin(p) || !ExprUseYSub(p) );
+ assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed );
+ assert( p->op!=TK_FUNCTION || !ExprUseYSub(p) );
#ifdef SQLITE_DEBUG
if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){
assert( p->pLeft==0 );
assert( p->pRight==0 );
- assert( p->x.pSelect==0 );
+ assert( !ExprUseXSelect(p) || p->x.pSelect==0 );
+ assert( !ExprUseXList(p) || p->x.pList==0 );
}
#endif
if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){
/* The Expr.x union is never used at the same time as Expr.pRight */
- assert( p->x.pList==0 || p->pRight==0 );
+ assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 );
if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft);
if( p->pRight ){
+ assert( !ExprHasProperty(p, EP_WinFunc) );
sqlite3ExprDeleteNN(db, p->pRight);
- }else if( ExprHasProperty(p, EP_xIsSelect) ){
+ }else if( ExprUseXSelect(p) ){
+ assert( !ExprHasProperty(p, EP_WinFunc) );
sqlite3SelectDelete(db, p->x.pSelect);
}else{
sqlite3ExprListDelete(db, p->x.pList);
- }
- if( ExprHasProperty(p, EP_WinFunc) ){
- assert( p->op==TK_FUNCTION );
- sqlite3WindowDelete(db, p->y.pWin);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( ExprHasProperty(p, EP_WinFunc) ){
+ sqlite3WindowDelete(db, p->y.pWin);
+ }
+#endif
}
}
- if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
if( !ExprHasProperty(p, EP_Static) ){
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
}
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
if( p ) sqlite3ExprDeleteNN(db, p);
}
+SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3 *db, void *p){
+ if( ALWAYS(p) ) sqlite3ExprDeleteNN(db, (Expr*)p);
+}
+
+/*
+** Clear both elements of an OnOrUsing object
+*/
+SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){
+ if( p==0 ){
+ /* Nothing to clear */
+ }else if( p->pOn ){
+ sqlite3ExprDeleteNN(db, p->pOn);
+ }else if( p->pUsing ){
+ sqlite3IdListDelete(db, p->pUsing);
+ }
+}
/*
-** Return the number of bytes allocated for the expression structure
+** Arrange to cause pExpr to be deleted when the pParse is deleted.
+** This is similar to sqlite3ExprDelete() except that the delete is
+** deferred until the pParse is deleted.
+**
+** The pExpr might be deleted immediately on an OOM error.
+**
+** The deferred delete is (currently) implemented by adding the
+** pExpr to the pParse->pConstExpr list with a register number of 0.
+*/
+SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr);
+}
+
+/* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the
+** expression.
+*/
+SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){
+ if( p ){
+ if( IN_RENAME_OBJECT ){
+ sqlite3RenameExprUnmap(pParse, p);
+ }
+ sqlite3ExprDeleteNN(pParse->db, p);
+ }
+}
+
+/*
+** Return the number of bytes allocated for the expression structure
** passed as the first argument. This is always one of EXPR_FULLSIZE,
** EXPR_REDUCEDSIZE or EXPR_TOKENONLYSIZE.
*/
-static int exprStructSize(Expr *p){
+static int exprStructSize(const Expr *p){
if( ExprHasProperty(p, EP_TokenOnly) ) return EXPR_TOKENONLYSIZE;
if( ExprHasProperty(p, EP_Reduced) ) return EXPR_REDUCEDSIZE;
return EXPR_FULLSIZE;
@@ -97219,14 +109803,14 @@ static int exprStructSize(Expr *p){
** to store a copy of an expression or expression tree. They differ in
** how much of the tree is measured.
**
-** dupedExprStructSize() Size of only the Expr structure
+** dupedExprStructSize() Size of only the Expr structure
** dupedExprNodeSize() Size of Expr + space for token
** dupedExprSize() Expr + token + subtree components
**
***************************************************************************
**
-** The dupedExprStructSize() function returns two values OR-ed together:
-** (1) the space required for a copy of the Expr structure only and
+** The dupedExprStructSize() function returns two values OR-ed together:
+** (1) the space required for a copy of the Expr structure only and
** (2) the EP_xxx flags that indicate what the structure size should be.
** The return values is always one of:
**
@@ -97248,22 +109832,17 @@ static int exprStructSize(Expr *p){
** of dupedExprStructSize() contain multiple assert() statements that attempt
** to enforce this constraint.
*/
-static int dupedExprStructSize(Expr *p, int flags){
+static int dupedExprStructSize(const Expr *p, int flags){
int nSize;
assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */
assert( EXPR_FULLSIZE<=0xfff );
assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 );
- if( 0==flags || p->op==TK_SELECT_COLUMN
-#ifndef SQLITE_OMIT_WINDOWFUNC
- || ExprHasProperty(p, EP_WinFunc)
-#endif
- ){
+ if( 0==flags || ExprHasProperty(p, EP_FullSize) ){
nSize = EXPR_FULLSIZE;
}else{
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
- assert( !ExprHasProperty(p, EP_FromJoin) );
- assert( !ExprHasProperty(p, EP_MemToken) );
- assert( !ExprHasProperty(p, EP_NoReduce) );
+ assert( !ExprHasProperty(p, EP_OuterON) );
+ assert( !ExprHasVVAProperty(p, EP_NoReduce) );
if( p->pLeft || p->x.pList ){
nSize = EXPR_REDUCEDSIZE | EP_Reduced;
}else{
@@ -97275,11 +109854,11 @@ static int dupedExprStructSize(Expr *p, int flags){
}
/*
-** This function returns the space in bytes required to store the copy
+** This function returns the space in bytes required to store the copy
** of the Expr structure and a copy of the Expr.u.zToken string (if that
** string is defined.)
*/
-static int dupedExprNodeSize(Expr *p, int flags){
+static int dupedExprNodeSize(const Expr *p, int flags){
int nByte = dupedExprStructSize(p, flags) & 0xfff;
if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
nByte += sqlite3Strlen30NN(p->u.zToken)+1;
@@ -97288,56 +109867,94 @@ static int dupedExprNodeSize(Expr *p, int flags){
}
/*
-** Return the number of bytes required to create a duplicate of the
-** expression passed as the first argument. The second argument is a
-** mask containing EXPRDUP_XXX flags.
+** Return the number of bytes required to create a duplicate of the
+** expression passed as the first argument.
**
** The value returned includes space to create a copy of the Expr struct
** itself and the buffer referred to by Expr.u.zToken, if any.
**
-** If the EXPRDUP_REDUCE flag is set, then the return value includes
-** space to duplicate all Expr nodes in the tree formed by Expr.pLeft
-** and Expr.pRight variables (but not for any structures pointed to or
-** descended from the Expr.x.pList or Expr.x.pSelect variables).
+** The return value includes space to duplicate all Expr nodes in the
+** tree formed by Expr.pLeft and Expr.pRight, but not any other
+** substructure such as Expr.x.pList, Expr.x.pSelect, and Expr.y.pWin.
*/
-static int dupedExprSize(Expr *p, int flags){
- int nByte = 0;
- if( p ){
- nByte = dupedExprNodeSize(p, flags);
- if( flags&EXPRDUP_REDUCE ){
- nByte += dupedExprSize(p->pLeft, flags) + dupedExprSize(p->pRight, flags);
- }
- }
+static int dupedExprSize(const Expr *p){
+ int nByte;
+ assert( p!=0 );
+ nByte = dupedExprNodeSize(p, EXPRDUP_REDUCE);
+ if( p->pLeft ) nByte += dupedExprSize(p->pLeft);
+ if( p->pRight ) nByte += dupedExprSize(p->pRight);
+ assert( nByte==ROUND8(nByte) );
return nByte;
}
/*
-** This function is similar to sqlite3ExprDup(), except that if pzBuffer
-** is not NULL then *pzBuffer is assumed to point to a buffer large enough
-** to store the copy of expression p, the copies of p->u.zToken
-** (if applicable), and the copies of the p->pLeft and p->pRight expressions,
-** if any. Before returning, *pzBuffer is set to the first byte past the
-** portion of the buffer copied into by this function.
+** An EdupBuf is a memory allocation used to stored multiple Expr objects
+** together with their Expr.zToken content. This is used to help implement
+** compression while doing sqlite3ExprDup(). The top-level Expr does the
+** allocation for itself and many of its decendents, then passes an instance
+** of the structure down into exprDup() so that they decendents can have
+** access to that memory.
+*/
+typedef struct EdupBuf EdupBuf;
+struct EdupBuf {
+ u8 *zAlloc; /* Memory space available for storage */
+#ifdef SQLITE_DEBUG
+ u8 *zEnd; /* First byte past the end of memory */
+#endif
+};
+
+/*
+** This function is similar to sqlite3ExprDup(), except that if pEdupBuf
+** is not NULL then it points to memory that can be used to store a copy
+** of the input Expr p together with its p->u.zToken (if any). pEdupBuf
+** is updated with the new buffer tail prior to returning.
*/
-static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
+static Expr *exprDup(
+ sqlite3 *db, /* Database connection (for memory allocation) */
+ const Expr *p, /* Expr tree to be duplicated */
+ int dupFlags, /* EXPRDUP_REDUCE for compression. 0 if not */
+ EdupBuf *pEdupBuf /* Preallocated storage space, or NULL */
+){
Expr *pNew; /* Value to return */
- u8 *zAlloc; /* Memory space from which to build Expr object */
+ EdupBuf sEdupBuf; /* Memory space from which to build Expr object */
u32 staticFlag; /* EP_Static if space not obtained from malloc */
+ int nToken = -1; /* Space needed for p->u.zToken. -1 means unknown */
assert( db!=0 );
assert( p );
assert( dupFlags==0 || dupFlags==EXPRDUP_REDUCE );
- assert( pzBuffer==0 || dupFlags==EXPRDUP_REDUCE );
+ assert( pEdupBuf==0 || dupFlags==EXPRDUP_REDUCE );
/* Figure out where to write the new Expr structure. */
- if( pzBuffer ){
- zAlloc = *pzBuffer;
+ if( pEdupBuf ){
+ sEdupBuf.zAlloc = pEdupBuf->zAlloc;
+#ifdef SQLITE_DEBUG
+ sEdupBuf.zEnd = pEdupBuf->zEnd;
+#endif
staticFlag = EP_Static;
+ assert( sEdupBuf.zAlloc!=0 );
+ assert( dupFlags==EXPRDUP_REDUCE );
}else{
- zAlloc = sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags));
+ int nAlloc;
+ if( dupFlags ){
+ nAlloc = dupedExprSize(p);
+ }else if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
+ nToken = sqlite3Strlen30NN(p->u.zToken)+1;
+ nAlloc = ROUND8(EXPR_FULLSIZE + nToken);
+ }else{
+ nToken = 0;
+ nAlloc = ROUND8(EXPR_FULLSIZE);
+ }
+ assert( nAlloc==ROUND8(nAlloc) );
+ sEdupBuf.zAlloc = sqlite3DbMallocRawNN(db, nAlloc);
+#ifdef SQLITE_DEBUG
+ sEdupBuf.zEnd = sEdupBuf.zAlloc ? sEdupBuf.zAlloc+nAlloc : 0;
+#endif
+
staticFlag = 0;
}
- pNew = (Expr *)zAlloc;
+ pNew = (Expr *)sEdupBuf.zAlloc;
+ assert( EIGHT_BYTE_ALIGNMENT(pNew) );
if( pNew ){
/* Set nNewSize to the size allocated for the structure pointed to
@@ -97346,68 +109963,83 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
** by the copy of the p->u.zToken string (if any).
*/
const unsigned nStructSize = dupedExprStructSize(p, dupFlags);
- const int nNewSize = nStructSize & 0xfff;
- int nToken;
- if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
- nToken = sqlite3Strlen30(p->u.zToken) + 1;
- }else{
- nToken = 0;
+ int nNewSize = nStructSize & 0xfff;
+ if( nToken<0 ){
+ if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
+ nToken = sqlite3Strlen30(p->u.zToken) + 1;
+ }else{
+ nToken = 0;
+ }
}
if( dupFlags ){
+ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >= nNewSize+nToken );
assert( ExprHasProperty(p, EP_Reduced)==0 );
- memcpy(zAlloc, p, nNewSize);
+ memcpy(sEdupBuf.zAlloc, p, nNewSize);
}else{
u32 nSize = (u32)exprStructSize(p);
- memcpy(zAlloc, p, nSize);
- if( nSize<EXPR_FULLSIZE ){
- memset(&zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
+ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >=
+ (int)EXPR_FULLSIZE+nToken );
+ memcpy(sEdupBuf.zAlloc, p, nSize);
+ if( nSize<EXPR_FULLSIZE ){
+ memset(&sEdupBuf.zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
}
+ nNewSize = EXPR_FULLSIZE;
}
/* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */
- pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken);
+ pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static);
pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly);
pNew->flags |= staticFlag;
+ ExprClearVVAProperties(pNew);
+ if( dupFlags ){
+ ExprSetVVAProperty(pNew, EP_Immutable);
+ }
/* Copy the p->u.zToken string, if any. */
- if( nToken ){
- char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize];
+ assert( nToken>=0 );
+ if( nToken>0 ){
+ char *zToken = pNew->u.zToken = (char*)&sEdupBuf.zAlloc[nNewSize];
memcpy(zToken, p->u.zToken, nToken);
+ nNewSize += nToken;
}
+ sEdupBuf.zAlloc += ROUND8(nNewSize);
+
+ if( ((p->flags|pNew->flags)&(EP_TokenOnly|EP_Leaf))==0 ){
- if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_Leaf)) ){
/* Fill in the pNew->x.pSelect or pNew->x.pList member. */
- if( ExprHasProperty(p, EP_xIsSelect) ){
+ if( ExprUseXSelect(p) ){
pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, dupFlags);
}else{
- pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, dupFlags);
+ pNew->x.pList = sqlite3ExprListDup(db, p->x.pList,
+ p->op!=TK_ORDER ? dupFlags : 0);
}
- }
- /* Fill in pNew->pLeft and pNew->pRight. */
- if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){
- zAlloc += dupedExprNodeSize(p, dupFlags);
- if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){
- pNew->pLeft = p->pLeft ?
- exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0;
- pNew->pRight = p->pRight ?
- exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0;
- }
#ifndef SQLITE_OMIT_WINDOWFUNC
if( ExprHasProperty(p, EP_WinFunc) ){
pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin);
assert( ExprHasProperty(pNew, EP_WinFunc) );
}
#endif /* SQLITE_OMIT_WINDOWFUNC */
- if( pzBuffer ){
- *pzBuffer = zAlloc;
- }
- }else{
- if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
- if( pNew->op==TK_SELECT_COLUMN ){
+
+ /* Fill in pNew->pLeft and pNew->pRight. */
+ if( dupFlags ){
+ if( p->op==TK_SELECT_COLUMN ){
+ pNew->pLeft = p->pLeft;
+ assert( p->pRight==0
+ || p->pRight==p->pLeft
+ || ExprHasProperty(p->pLeft, EP_Subquery) );
+ }else{
+ pNew->pLeft = p->pLeft ?
+ exprDup(db, p->pLeft, EXPRDUP_REDUCE, &sEdupBuf) : 0;
+ }
+ pNew->pRight = p->pRight ?
+ exprDup(db, p->pRight, EXPRDUP_REDUCE, &sEdupBuf) : 0;
+ }else{
+ if( p->op==TK_SELECT_COLUMN ){
pNew->pLeft = p->pLeft;
- assert( p->iColumn==0 || p->pRight==0 );
- assert( p->pRight==0 || p->pRight==p->pLeft );
+ assert( p->pRight==0
+ || p->pRight==p->pLeft
+ || ExprHasProperty(p->pLeft, EP_Subquery) );
}else{
pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
}
@@ -97415,19 +110047,21 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
}
}
}
+ if( pEdupBuf ) memcpy(pEdupBuf, &sEdupBuf, sizeof(sEdupBuf));
+ assert( sEdupBuf.zAlloc <= sEdupBuf.zEnd );
return pNew;
}
/*
-** Create and return a deep copy of the object passed as the second
+** Create and return a deep copy of the object passed as the second
** argument. If an OOM condition is encountered, NULL is returned
** and the db->mallocFailed flag set.
*/
#ifndef SQLITE_OMIT_CTE
-static With *withDup(sqlite3 *db, With *p){
+SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){
With *pRet = 0;
if( p ){
- int nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1);
+ sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1);
pRet = sqlite3DbMallocZero(db, nByte);
if( pRet ){
int i;
@@ -97436,15 +110070,49 @@ static With *withDup(sqlite3 *db, With *p){
pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0);
pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0);
pRet->a[i].zName = sqlite3DbStrDup(db, p->a[i].zName);
+ pRet->a[i].eM10d = p->a[i].eM10d;
}
}
}
return pRet;
}
#else
-# define withDup(x,y) 0
+# define sqlite3WithDup(x,y) 0
+#endif
+
+#ifndef SQLITE_OMIT_WINDOWFUNC
+/*
+** The gatherSelectWindows() procedure and its helper routine
+** gatherSelectWindowsCallback() are used to scan all the expressions
+** an a newly duplicated SELECT statement and gather all of the Window
+** objects found there, assembling them onto the linked list at Select->pWin.
+*/
+static int gatherSelectWindowsCallback(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_FUNCTION && ExprHasProperty(pExpr, EP_WinFunc) ){
+ Select *pSelect = pWalker->u.pSelect;
+ Window *pWin = pExpr->y.pWin;
+ assert( pWin );
+ assert( IsWindowFunc(pExpr) );
+ assert( pWin->ppThis==0 );
+ sqlite3WindowLink(pSelect, pWin);
+ }
+ return WRC_Continue;
+}
+static int gatherSelectWindowsSelectCallback(Walker *pWalker, Select *p){
+ return p==pWalker->u.pSelect ? WRC_Continue : WRC_Prune;
+}
+static void gatherSelectWindows(Select *p){
+ Walker w;
+ w.xExprCallback = gatherSelectWindowsCallback;
+ w.xSelectCallback = gatherSelectWindowsSelectCallback;
+ w.xSelectCallback2 = 0;
+ w.pParse = 0;
+ w.u.pSelect = p;
+ sqlite3WalkSelect(&w, p);
+}
#endif
+
/*
** The following group of routines make deep copies of expressions,
** expression lists, ID lists, and select statements. The copies can
@@ -97452,7 +110120,7 @@ static With *withDup(sqlite3 *db, With *p){
** without effecting the originals.
**
** The expression list, ID, and source lists return by sqlite3ExprListDup(),
-** sqlite3IdListDup(), and sqlite3SrcListDup() can not be further expanded
+** sqlite3IdListDup(), and sqlite3SrcListDup() can not be further expanded
** by subsequent calls to sqlite*ListAppend() routines.
**
** Any tables that the SrcList might point to are not duplicated.
@@ -97462,48 +110130,49 @@ static With *withDup(sqlite3 *db, With *p){
** truncated version of the usual Expr structure that will be stored as
** part of the in-memory representation of the database schema.
*/
-SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3 *db, Expr *p, int flags){
+SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3 *db, const Expr *p, int flags){
assert( flags==0 || flags==EXPRDUP_REDUCE );
return p ? exprDup(db, p, flags, 0) : 0;
}
-SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
+SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, const ExprList *p, int flags){
ExprList *pNew;
- struct ExprList_item *pItem, *pOldItem;
+ struct ExprList_item *pItem;
+ const struct ExprList_item *pOldItem;
int i;
- Expr *pPriorSelectCol = 0;
+ Expr *pPriorSelectColOld = 0;
+ Expr *pPriorSelectColNew = 0;
assert( db!=0 );
if( p==0 ) return 0;
pNew = sqlite3DbMallocRawNN(db, sqlite3DbMallocSize(db, p));
if( pNew==0 ) return 0;
pNew->nExpr = p->nExpr;
+ pNew->nAlloc = p->nAlloc;
pItem = pNew->a;
pOldItem = p->a;
for(i=0; i<p->nExpr; i++, pItem++, pOldItem++){
Expr *pOldExpr = pOldItem->pExpr;
Expr *pNewExpr;
pItem->pExpr = sqlite3ExprDup(db, pOldExpr, flags);
- if( pOldExpr
+ if( pOldExpr
&& pOldExpr->op==TK_SELECT_COLUMN
- && (pNewExpr = pItem->pExpr)!=0
+ && (pNewExpr = pItem->pExpr)!=0
){
- assert( pNewExpr->iColumn==0 || i>0 );
- if( pNewExpr->iColumn==0 ){
- assert( pOldExpr->pLeft==pOldExpr->pRight );
- pPriorSelectCol = pNewExpr->pLeft = pNewExpr->pRight;
- }else{
- assert( i>0 );
- assert( pItem[-1].pExpr!=0 );
- assert( pNewExpr->iColumn==pItem[-1].pExpr->iColumn+1 );
- assert( pPriorSelectCol==pItem[-1].pExpr->pLeft );
- pNewExpr->pLeft = pPriorSelectCol;
- }
- }
- pItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
- pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan);
- pItem->sortOrder = pOldItem->sortOrder;
- pItem->done = 0;
- pItem->bSpanIsTab = pOldItem->bSpanIsTab;
- pItem->bSorterRef = pOldItem->bSorterRef;
+ if( pNewExpr->pRight ){
+ pPriorSelectColOld = pOldExpr->pRight;
+ pPriorSelectColNew = pNewExpr->pRight;
+ pNewExpr->pLeft = pNewExpr->pRight;
+ }else{
+ if( pOldExpr->pLeft!=pPriorSelectColOld ){
+ pPriorSelectColOld = pOldExpr->pLeft;
+ pPriorSelectColNew = sqlite3ExprDup(db, pPriorSelectColOld, flags);
+ pNewExpr->pRight = pPriorSelectColNew;
+ }
+ pNewExpr->pLeft = pPriorSelectColNew;
+ }
+ }
+ pItem->zEName = sqlite3DbStrDup(db, pOldItem->zEName);
+ pItem->fg = pOldItem->fg;
+ pItem->fg.done = 0;
pItem->u = pOldItem->u;
}
return pNew;
@@ -97511,13 +110180,13 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags)
/*
** If cursors, triggers, views and subqueries are all omitted from
-** the build, then none of the following routines, except for
+** the build, then none of the following routines, except for
** sqlite3SelectDup(), can be called. sqlite3SelectDup() is sometimes
** called with a NULL argument.
*/
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
|| !defined(SQLITE_OMIT_SUBQUERY)
-SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
+SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
SrcList *pNew;
int i;
int nByte;
@@ -97528,8 +110197,8 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
if( pNew==0 ) return 0;
pNew->nSrc = pNew->nAlloc = p->nSrc;
for(i=0; i<p->nSrc; i++){
- struct SrcList_item *pNewItem = &pNew->a[i];
- struct SrcList_item *pOldItem = &p->a[i];
+ SrcItem *pNewItem = &pNew->a[i];
+ const SrcItem *pOldItem = &p->a[i];
Table *pTab;
pNewItem->pSchema = pOldItem->pSchema;
pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase);
@@ -97542,9 +110211,12 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
if( pNewItem->fg.isIndexedBy ){
pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
}
- pNewItem->pIBIndex = pOldItem->pIBIndex;
+ pNewItem->u2 = pOldItem->u2;
+ if( pNewItem->fg.isCte ){
+ pNewItem->u2.pCteUse->nUse++;
+ }
if( pNewItem->fg.isTabFunc ){
- pNewItem->u1.pFuncArg =
+ pNewItem->u1.pFuncArg =
sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags);
}
pTab = pNewItem->pTab = pOldItem->pTab;
@@ -97552,41 +110224,39 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
pTab->nTabRef++;
}
pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags);
- pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags);
- pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing);
+ if( pOldItem->fg.isUsing ){
+ assert( pNewItem->fg.isUsing );
+ pNewItem->u3.pUsing = sqlite3IdListDup(db, pOldItem->u3.pUsing);
+ }else{
+ pNewItem->u3.pOn = sqlite3ExprDup(db, pOldItem->u3.pOn, flags);
+ }
pNewItem->colUsed = pOldItem->colUsed;
}
return pNew;
}
-SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
+SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){
IdList *pNew;
int i;
assert( db!=0 );
if( p==0 ) return 0;
- pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
+ assert( p->eU4!=EU4_EXPR );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) );
if( pNew==0 ) return 0;
pNew->nId = p->nId;
- pNew->a = sqlite3DbMallocRawNN(db, p->nId*sizeof(p->a[0]) );
- if( pNew->a==0 ){
- sqlite3DbFreeNN(db, pNew);
- return 0;
- }
- /* Note that because the size of the allocation for p->a[] is not
- ** necessarily a power of two, sqlite3IdListAppend() may not be called
- ** on the duplicate created by this function. */
+ pNew->eU4 = p->eU4;
for(i=0; i<p->nId; i++){
struct IdList_item *pNewItem = &pNew->a[i];
- struct IdList_item *pOldItem = &p->a[i];
+ const struct IdList_item *pOldItem = &p->a[i];
pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
- pNewItem->idx = pOldItem->idx;
+ pNewItem->u4 = pOldItem->u4;
}
return pNew;
}
-SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){
+SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *pDup, int flags){
Select *pRet = 0;
Select *pNext = 0;
Select **pp = &pRet;
- Select *p;
+ const Select *p;
assert( db!=0 );
for(p=pDup; p; p=p->pPrior){
@@ -97608,12 +110278,21 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){
pNew->addrOpenEphm[0] = -1;
pNew->addrOpenEphm[1] = -1;
pNew->nSelectRow = p->nSelectRow;
- pNew->pWith = withDup(db, p->pWith);
+ pNew->pWith = sqlite3WithDup(db, p->pWith);
#ifndef SQLITE_OMIT_WINDOWFUNC
pNew->pWin = 0;
pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn);
+ if( p->pWin && db->mallocFailed==0 ) gatherSelectWindows(pNew);
#endif
pNew->selId = p->selId;
+ if( db->mallocFailed ){
+ /* Any prior OOM might have left the Select object incomplete.
+ ** Delete the whole thing rather than allow an incomplete Select
+ ** to be used by the code generator. */
+ pNew->pNext = 0;
+ sqlite3SelectDelete(db, pNew);
+ break;
+ }
*pp = pNew;
pp = &pNew->pPrior;
pNext = pNew;
@@ -97622,7 +110301,7 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){
return pRet;
}
#else
-SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
+SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *p, int flags){
assert( p==0 );
return 0;
}
@@ -97634,51 +110313,70 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
** initially NULL, then create a new expression list.
**
** The pList argument must be either NULL or a pointer to an ExprList
-** obtained from a prior call to sqlite3ExprListAppend(). This routine
-** may not be used with an ExprList obtained from sqlite3ExprListDup().
-** Reason: This routine assumes that the number of slots in pList->a[]
-** is a power of two. That is true for sqlite3ExprListAppend() returns
-** but is not necessarily true from the return value of sqlite3ExprListDup().
+** obtained from a prior call to sqlite3ExprListAppend().
**
** If a memory allocation error occurs, the entire list is freed and
** NULL is returned. If non-NULL is returned, then it is guaranteed
** that the new entry was successfully appended.
*/
+static const struct ExprList_item zeroItem = {0};
+SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendNew(
+ sqlite3 *db, /* Database handle. Used for memory allocation */
+ Expr *pExpr /* Expression to be appended. Might be NULL */
+){
+ struct ExprList_item *pItem;
+ ExprList *pList;
+
+ pList = sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 );
+ if( pList==0 ){
+ sqlite3ExprDelete(db, pExpr);
+ return 0;
+ }
+ pList->nAlloc = 4;
+ pList->nExpr = 1;
+ pItem = &pList->a[0];
+ *pItem = zeroItem;
+ pItem->pExpr = pExpr;
+ return pList;
+}
+SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendGrow(
+ sqlite3 *db, /* Database handle. Used for memory allocation */
+ ExprList *pList, /* List to which to append. Might be NULL */
+ Expr *pExpr /* Expression to be appended. Might be NULL */
+){
+ struct ExprList_item *pItem;
+ ExprList *pNew;
+ pList->nAlloc *= 2;
+ pNew = sqlite3DbRealloc(db, pList,
+ sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0]));
+ if( pNew==0 ){
+ sqlite3ExprListDelete(db, pList);
+ sqlite3ExprDelete(db, pExpr);
+ return 0;
+ }else{
+ pList = pNew;
+ }
+ pItem = &pList->a[pList->nExpr++];
+ *pItem = zeroItem;
+ pItem->pExpr = pExpr;
+ return pList;
+}
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(
Parse *pParse, /* Parsing context */
ExprList *pList, /* List to which to append. Might be NULL */
Expr *pExpr /* Expression to be appended. Might be NULL */
){
struct ExprList_item *pItem;
- sqlite3 *db = pParse->db;
- assert( db!=0 );
if( pList==0 ){
- pList = sqlite3DbMallocRawNN(db, sizeof(ExprList) );
- if( pList==0 ){
- goto no_mem;
- }
- pList->nExpr = 0;
- }else if( (pList->nExpr & (pList->nExpr-1))==0 ){
- ExprList *pNew;
- pNew = sqlite3DbRealloc(db, pList,
- sizeof(*pList)+(2*pList->nExpr - 1)*sizeof(pList->a[0]));
- if( pNew==0 ){
- goto no_mem;
- }
- pList = pNew;
+ return sqlite3ExprListAppendNew(pParse->db,pExpr);
+ }
+ if( pList->nAlloc<pList->nExpr+1 ){
+ return sqlite3ExprListAppendGrow(pParse->db,pList,pExpr);
}
pItem = &pList->a[pList->nExpr++];
- assert( offsetof(struct ExprList_item,zName)==sizeof(pItem->pExpr) );
- assert( offsetof(struct ExprList_item,pExpr)==0 );
- memset(&pItem->zName,0,sizeof(*pItem)-offsetof(struct ExprList_item,zName));
+ *pItem = zeroItem;
pItem->pExpr = pExpr;
return pList;
-
-no_mem:
- /* Avoid leaking memory if malloc has failed. */
- sqlite3ExprDelete(db, pExpr);
- sqlite3ExprListDelete(db, pList);
- return 0;
}
/*
@@ -97707,8 +110405,8 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(
if( NEVER(pColumns==0) ) goto vector_append_error;
if( pExpr==0 ) goto vector_append_error;
- /* If the RHS is a vector, then we can immediately check to see that
- ** the size of the RHS and LHS match. But if the RHS is a SELECT,
+ /* If the RHS is a vector, then we can immediately check to see that
+ ** the size of the RHS and LHS match. But if the RHS is a SELECT,
** wildcards ("*") in the result set of the SELECT must be expanded before
** we can do the size check, so defer the size check until code generation.
*/
@@ -97719,11 +110417,13 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(
}
for(i=0; i<pColumns->nId; i++){
- Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i);
+ Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i, pColumns->nId);
+ assert( pSubExpr!=0 || db->mallocFailed );
+ if( pSubExpr==0 ) continue;
pList = sqlite3ExprListAppend(pParse, pList, pSubExpr);
if( pList ){
assert( pList->nExpr==iFirst+i+1 );
- pList->a[pList->nExpr-1].zName = pColumns->a[i].zName;
+ pList->a[pList->nExpr-1].zEName = pColumns->a[i].zName;
pColumns->a[i].zName = 0;
}
}
@@ -97732,7 +110432,7 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(
Expr *pFirst = pList->a[iFirst].pExpr;
assert( pFirst!=0 );
assert( pFirst->op==TK_SELECT_COLUMN );
-
+
/* Store the SELECT statement in pRight so it will be deleted when
** sqlite3ExprListDelete() is called */
pFirst->pRight = pExpr;
@@ -97744,7 +110444,7 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(
}
vector_append_error:
- sqlite3ExprDelete(db, pExpr);
+ sqlite3ExprUnmapAndDelete(pParse, pExpr);
sqlite3IdListDelete(db, pColumns);
return pList;
}
@@ -97752,19 +110452,38 @@ vector_append_error:
/*
** Set the sort order for the last element on the given ExprList.
*/
-SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder){
+SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder, int eNulls){
+ struct ExprList_item *pItem;
if( p==0 ) return;
- assert( SQLITE_SO_UNDEFINED<0 && SQLITE_SO_ASC>=0 && SQLITE_SO_DESC>0 );
assert( p->nExpr>0 );
- if( iSortOrder<0 ){
- assert( p->a[p->nExpr-1].sortOrder==SQLITE_SO_ASC );
- return;
+
+ assert( SQLITE_SO_UNDEFINED<0 && SQLITE_SO_ASC==0 && SQLITE_SO_DESC>0 );
+ assert( iSortOrder==SQLITE_SO_UNDEFINED
+ || iSortOrder==SQLITE_SO_ASC
+ || iSortOrder==SQLITE_SO_DESC
+ );
+ assert( eNulls==SQLITE_SO_UNDEFINED
+ || eNulls==SQLITE_SO_ASC
+ || eNulls==SQLITE_SO_DESC
+ );
+
+ pItem = &p->a[p->nExpr-1];
+ assert( pItem->fg.bNulls==0 );
+ if( iSortOrder==SQLITE_SO_UNDEFINED ){
+ iSortOrder = SQLITE_SO_ASC;
+ }
+ pItem->fg.sortFlags = (u8)iSortOrder;
+
+ if( eNulls!=SQLITE_SO_UNDEFINED ){
+ pItem->fg.bNulls = 1;
+ if( iSortOrder!=eNulls ){
+ pItem->fg.sortFlags |= KEYINFO_ORDER_BIGNULL;
+ }
}
- p->a[p->nExpr-1].sortOrder = (u8)iSortOrder;
}
/*
-** Set the ExprList.a[].zName element of the most recently added item
+** Set the ExprList.a[].zEName element of the most recently added item
** on the expression list.
**
** pList might be NULL following an OOM error. But pName should never be
@@ -97774,19 +110493,26 @@ SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder){
SQLITE_PRIVATE void sqlite3ExprListSetName(
Parse *pParse, /* Parsing context */
ExprList *pList, /* List to which to add the span. */
- Token *pName, /* Name to be added */
+ const Token *pName, /* Name to be added */
int dequote /* True to cause the name to be dequoted */
){
assert( pList!=0 || pParse->db->mallocFailed!=0 );
+ assert( pParse->eParseMode!=PARSE_MODE_UNMAP || dequote==0 );
if( pList ){
struct ExprList_item *pItem;
assert( pList->nExpr>0 );
pItem = &pList->a[pList->nExpr-1];
- assert( pItem->zName==0 );
- pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
- if( dequote ) sqlite3Dequote(pItem->zName);
- if( IN_RENAME_OBJECT ){
- sqlite3RenameTokenMap(pParse, (void*)pItem->zName, pName);
+ assert( pItem->zEName==0 );
+ assert( pItem->fg.eEName==ENAME_NAME );
+ pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
+ if( dequote ){
+ /* If dequote==0, then pName->z does not point to part of a DDL
+ ** statement handled by the parser. And so no token need be added
+ ** to the token-map. */
+ sqlite3Dequote(pItem->zEName);
+ if( IN_RENAME_OBJECT ){
+ sqlite3RenameTokenMap(pParse, (const void*)pItem->zEName, pName);
+ }
}
}
}
@@ -97810,8 +110536,10 @@ SQLITE_PRIVATE void sqlite3ExprListSetSpan(
if( pList ){
struct ExprList_item *pItem = &pList->a[pList->nExpr-1];
assert( pList->nExpr>0 );
- sqlite3DbFree(db, pItem->zSpan);
- pItem->zSpan = sqlite3DbSpanDup(db, zStart, zEnd);
+ if( pItem->zEName==0 ){
+ pItem->zEName = sqlite3DbSpanDup(db, zStart, zEnd);
+ pItem->fg.eEName = ENAME_SPAN;
+ }
}
}
@@ -97839,17 +110567,20 @@ static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){
int i = pList->nExpr;
struct ExprList_item *pItem = pList->a;
assert( pList->nExpr>0 );
+ assert( db!=0 );
do{
sqlite3ExprDelete(db, pItem->pExpr);
- sqlite3DbFree(db, pItem->zName);
- sqlite3DbFree(db, pItem->zSpan);
+ if( pItem->zEName ) sqlite3DbNNFreeNN(db, pItem->zEName);
pItem++;
}while( --i>0 );
- sqlite3DbFreeNN(db, pList);
+ sqlite3DbNNFreeNN(db, pList);
}
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
if( pList ) exprListDeleteNN(db, pList);
}
+SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3 *db, void *pList){
+ if( ALWAYS(pList) ) exprListDeleteNN(db, (ExprList*)pList);
+}
/*
** Return the bitwise-OR of all Expr.flags fields in the given
@@ -97881,16 +110612,33 @@ SQLITE_PRIVATE int sqlite3SelectWalkFail(Walker *pWalker, Select *NotUsed){
}
/*
+** Check the input string to see if it is "true" or "false" (in any case).
+**
+** If the string is.... Return
+** "true" EP_IsTrue
+** "false" EP_IsFalse
+** anything else 0
+*/
+SQLITE_PRIVATE u32 sqlite3IsTrueOrFalse(const char *zIn){
+ if( sqlite3StrICmp(zIn, "true")==0 ) return EP_IsTrue;
+ if( sqlite3StrICmp(zIn, "false")==0 ) return EP_IsFalse;
+ return 0;
+}
+
+
+/*
** If the input expression is an ID with the name "true" or "false"
** then convert it into an TK_TRUEFALSE term. Return non-zero if
** the conversion happened, and zero if the expression is unaltered.
*/
SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr *pExpr){
+ u32 v;
assert( pExpr->op==TK_ID || pExpr->op==TK_STRING );
- if( sqlite3StrICmp(pExpr->u.zToken, "true")==0
- || sqlite3StrICmp(pExpr->u.zToken, "false")==0
+ if( !ExprHasProperty(pExpr, EP_Quoted|EP_IntValue)
+ && (v = sqlite3IsTrueOrFalse(pExpr->u.zToken))!=0
){
pExpr->op = TK_TRUEFALSE;
+ ExprSetProperty(pExpr, v);
return 1;
}
return 0;
@@ -97901,12 +110649,41 @@ SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr *pExpr){
** and 0 if it is FALSE.
*/
SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr *pExpr){
+ pExpr = sqlite3ExprSkipCollateAndLikely((Expr*)pExpr);
assert( pExpr->op==TK_TRUEFALSE );
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
assert( sqlite3StrICmp(pExpr->u.zToken,"true")==0
|| sqlite3StrICmp(pExpr->u.zToken,"false")==0 );
return pExpr->u.zToken[4]==0;
}
+/*
+** If pExpr is an AND or OR expression, try to simplify it by eliminating
+** terms that are always true or false. Return the simplified expression.
+** Or return the original expression if no simplification is possible.
+**
+** Examples:
+**
+** (x<10) AND true => (x<10)
+** (x<10) AND false => false
+** (x<10) AND (y=22 OR false) => (x<10) AND (y=22)
+** (x<10) AND (y=22 OR true) => (x<10)
+** (y=22) OR true => true
+*/
+SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){
+ assert( pExpr!=0 );
+ if( pExpr->op==TK_AND || pExpr->op==TK_OR ){
+ Expr *pRight = sqlite3ExprSimplifiedAndOr(pExpr->pRight);
+ Expr *pLeft = sqlite3ExprSimplifiedAndOr(pExpr->pLeft);
+ if( ExprAlwaysTrue(pLeft) || ExprAlwaysFalse(pRight) ){
+ pExpr = pExpr->op==TK_AND ? pRight : pLeft;
+ }else if( ExprAlwaysTrue(pRight) || ExprAlwaysFalse(pLeft) ){
+ pExpr = pExpr->op==TK_AND ? pLeft : pRight;
+ }
+ }
+ return pExpr;
+}
+
/*
** These routines are Walker callbacks used to check expressions to
@@ -97924,11 +110701,12 @@ SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr *pExpr){
** In all cases, the callbacks set Walker.eCode=0 and abort if the expression
** is found to not be a constant.
**
-** The sqlite3ExprIsConstantOrFunction() is used for evaluating expressions
-** in a CREATE TABLE statement. The Walker.eCode value is 5 when parsing
-** an existing schema and 4 when processing a new statement. A bound
-** parameter raises an error for new statements, but is silently converted
-** to NULL for existing schemas. This allows sqlite_master tables that
+** The sqlite3ExprIsConstantOrFunction() is used for evaluating DEFAULT
+** expressions in a CREATE TABLE statement. The Walker.eCode value is 5
+** when parsing an existing schema out of the sqlite_schema table and 4
+** when processing a new CREATE TABLE statement. A bound parameter raises
+** an error for new statements, but is silently converted
+** to NULL for existing schemas. This allows sqlite_schema tables that
** contain a bound parameter because they were generated by older versions
** of SQLite to be parsed by newer versions of SQLite without raising a
** malformed schema error.
@@ -97936,9 +110714,9 @@ SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr *pExpr){
static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
/* If pWalker->eCode is 2 then any term of the expression that comes from
- ** the ON or USING clauses of a left join disqualifies the expression
+ ** the ON or USING clauses of an outer join disqualifies the expression
** from being considered constant. */
- if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_FromJoin) ){
+ if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_OuterON) ){
pWalker->eCode = 0;
return WRC_Abort;
}
@@ -97948,7 +110726,10 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
** and either pWalker->eCode==4 or 5 or the function has the
** SQLITE_FUNC_CONST flag. */
case TK_FUNCTION:
- if( pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_ConstFunc) ){
+ if( (pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_ConstFunc))
+ && !ExprHasProperty(pExpr, EP_WinFunc)
+ ){
+ if( pWalker->eCode==5 ) ExprSetProperty(pExpr, EP_FromDDL);
return WRC_Continue;
}else{
pWalker->eCode = 0;
@@ -97960,7 +110741,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
if( sqlite3ExprIdToTrueFalse(pExpr) ){
return WRC_Prune;
}
- /* Fall thru */
+ /* no break */ deliberate_fall_through
case TK_COLUMN:
case TK_AGG_FUNCTION:
case TK_AGG_COLUMN:
@@ -97974,18 +110755,20 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){
return WRC_Continue;
}
- /* Fall through */
+ /* no break */ deliberate_fall_through
case TK_IF_NULL_ROW:
case TK_REGISTER:
+ case TK_DOT:
testcase( pExpr->op==TK_REGISTER );
testcase( pExpr->op==TK_IF_NULL_ROW );
+ testcase( pExpr->op==TK_DOT );
pWalker->eCode = 0;
return WRC_Abort;
case TK_VARIABLE:
if( pWalker->eCode==5 ){
/* Silently convert bound parameters that appear inside of CREATE
** statements into a NULL when parsing the CREATE statement text out
- ** of the sqlite_master table */
+ ** of the sqlite_schema table */
pExpr->op = TK_NULL;
}else if( pWalker->eCode==4 ){
/* A bound parameter in a CREATE statement that originates from
@@ -97993,7 +110776,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
pWalker->eCode = 0;
return WRC_Abort;
}
- /* Fall through */
+ /* no break */ deliberate_fall_through
default:
testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */
testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */
@@ -98036,7 +110819,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){
**
** When this routine returns true, it indicates that the expression
** can be added to the pParse->pConstExpr list and evaluated once when
-** the prepared statement starts up. See sqlite3ExprCodeAtInit().
+** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce().
*/
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){
return exprIsConst(p, 2, 0);
@@ -98052,6 +110835,78 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
return exprIsConst(p, 3, iCur);
}
+/*
+** Check pExpr to see if it is an constraint on the single data source
+** pSrc = &pSrcList->a[iSrc]. In other words, check to see if pExpr
+** constrains pSrc but does not depend on any other tables or data
+** sources anywhere else in the query. Return true (non-zero) if pExpr
+** is a constraint on pSrc only.
+**
+** This is an optimization. False negatives will perhaps cause slower
+** queries, but false positives will yield incorrect answers. So when in
+** doubt, return 0.
+**
+** To be an single-source constraint, the following must be true:
+**
+** (1) pExpr cannot refer to any table other than pSrc->iCursor.
+**
+** (2) pExpr cannot use subqueries or non-deterministic functions.
+**
+** (3) pSrc cannot be part of the left operand for a RIGHT JOIN.
+** (Is there some way to relax this constraint?)
+**
+** (4) If pSrc is the right operand of a LEFT JOIN, then...
+** (4a) pExpr must come from an ON clause..
+** (4b) and specifically the ON clause associated with the LEFT JOIN.
+**
+** (5) If pSrc is not the right operand of a LEFT JOIN or the left
+** operand of a RIGHT JOIN, then pExpr must be from the WHERE
+** clause, not an ON clause.
+**
+** (6) Either:
+**
+** (6a) pExpr does not originate in an ON or USING clause, or
+**
+** (6b) The ON or USING clause from which pExpr is derived is
+** not to the left of a RIGHT JOIN (or FULL JOIN).
+**
+** Without this restriction, accepting pExpr as a single-table
+** constraint might move the the ON/USING filter expression
+** from the left side of a RIGHT JOIN over to the right side,
+** which leads to incorrect answers. See also restriction (9)
+** on push-down.
+*/
+SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(
+ Expr *pExpr, /* The constraint */
+ const SrcList *pSrcList, /* Complete FROM clause */
+ int iSrc /* Which element of pSrcList to use */
+){
+ const SrcItem *pSrc = &pSrcList->a[iSrc];
+ if( pSrc->fg.jointype & JT_LTORJ ){
+ return 0; /* rule (3) */
+ }
+ if( pSrc->fg.jointype & JT_LEFT ){
+ if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */
+ if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */
+ }else{
+ if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */
+ }
+ if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) /* (6a) */
+ && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (6b) */
+ ){
+ int jj;
+ for(jj=0; jj<iSrc; jj++){
+ if( pExpr->w.iJoin==pSrcList->a[jj].iCursor ){
+ if( (pSrcList->a[jj].fg.jointype & JT_LTORJ)!=0 ){
+ return 0; /* restriction (6) */
+ }
+ break;
+ }
+ }
+ }
+ return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */
+}
+
/*
** sqlite3WalkExpr() callback used by sqlite3ExprIsConstantOrGroupBy().
@@ -98073,7 +110928,7 @@ static int exprNodeIsConstantOrGroupBy(Walker *pWalker, Expr *pExpr){
}
/* Check if pExpr is a sub-select. If so, consider it variable. */
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ if( ExprUseXSelect(pExpr) ){
pWalker->eCode = 0;
return WRC_Abort;
}
@@ -98083,7 +110938,7 @@ static int exprNodeIsConstantOrGroupBy(Walker *pWalker, Expr *pExpr){
/*
** Walk the expression tree passed as the first argument. Return non-zero
-** if the expression consists entirely of constants or copies of terms
+** if the expression consists entirely of constants or copies of terms
** in pGroupBy that sort with the BINARY collation sequence.
**
** This routine is used to determine if a term of the HAVING clause can
@@ -98112,9 +110967,21 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprLi
}
/*
-** Walk an expression tree. Return non-zero if the expression is constant
-** or a function call with constant arguments. Return and 0 if there
-** are any variables.
+** Walk an expression tree for the DEFAULT field of a column definition
+** in a CREATE TABLE statement. Return non-zero if the expression is
+** acceptable for use as a DEFAULT. That is to say, return non-zero if
+** the expression is constant or a function call with constant arguments.
+** Return and 0 if there are any variables.
+**
+** isInit is true when parsing from sqlite_schema. isInit is false when
+** processing a new CREATE TABLE statement. When isInit is true, parameters
+** (such as ? or $abc) in the expression are converted into NULL. When
+** isInit is false, parameters raise an error. Parameters should not be
+** allowed in a CREATE TABLE statement, but some legacy versions of SQLite
+** allowed it, so we need to support it when reading sqlite_schema for
+** backwards compatibility.
+**
+** If isInit is true, set EP_FromDDL on every TK_FUNCTION node.
**
** For the purposes of this function, a double-quoted string (ex: "abc")
** is considered a variable but a single-quoted string (ex: 'abc') is
@@ -98149,9 +111016,9 @@ SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr *p){
** in *pValue. If the expression is not an integer or if it is too big
** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged.
*/
-SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){
+SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr *p, int *pValue){
int rc = 0;
- if( p==0 ) return 0; /* Can only happen following on OOM */
+ if( NEVER(p==0) ) return 0; /* Used to only happen following on OOM */
/* If an expression is an integer literal that fits in a signed 32-bit
** integer, then the EP_IntValue flag will have already been set */
@@ -98168,9 +111035,9 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){
break;
}
case TK_UMINUS: {
- int v;
+ int v = 0;
if( sqlite3ExprIsInteger(p->pLeft, &v) ){
- assert( v!=(-2147483647-1) );
+ assert( ((unsigned int)v)!=0x80000000 );
*pValue = -v;
rc = 1;
}
@@ -98185,7 +111052,7 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){
** Return FALSE if there is no chance that the expression can be NULL.
**
** If the expression might be NULL or if the expression is too complex
-** to tell return TRUE.
+** to tell return TRUE.
**
** This routine is used as an optimization, to skip OP_IsNull opcodes
** when we know that a value cannot be NULL. Hence, a false positive
@@ -98197,7 +111064,11 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){
*/
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
u8 op;
- while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; }
+ assert( p!=0 );
+ while( p->op==TK_UPLUS || p->op==TK_UMINUS ){
+ p = p->pLeft;
+ assert( p!=0 );
+ }
op = p->op;
if( op==TK_REGISTER ) op = p->op2;
switch( op ){
@@ -98207,9 +111078,13 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
case TK_BLOB:
return 0;
case TK_COLUMN:
+ assert( ExprUseYTab(p) );
return ExprHasProperty(p, EP_CanBeNull) ||
- p->y.pTab==0 || /* Reference to column of index on expression */
- (p->iColumn>=0 && p->y.pTab->aCol[p->iColumn].notNull==0);
+ NEVER(p->y.pTab==0) || /* Reference to column of index on expr */
+ (p->iColumn>=0
+ && p->y.pTab->aCol!=0 /* Possible due to prior error */
+ && ALWAYS(p->iColumn<p->y.pTab->nCol)
+ && p->y.pTab->aCol[p->iColumn].notNull==0);
default:
return 1;
}
@@ -98227,27 +111102,30 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
*/
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr *p, char aff){
u8 op;
+ int unaryMinus = 0;
if( aff==SQLITE_AFF_BLOB ) return 1;
- while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; }
+ while( p->op==TK_UPLUS || p->op==TK_UMINUS ){
+ if( p->op==TK_UMINUS ) unaryMinus = 1;
+ p = p->pLeft;
+ }
op = p->op;
if( op==TK_REGISTER ) op = p->op2;
switch( op ){
case TK_INTEGER: {
- return aff==SQLITE_AFF_INTEGER || aff==SQLITE_AFF_NUMERIC;
+ return aff>=SQLITE_AFF_NUMERIC;
}
case TK_FLOAT: {
- return aff==SQLITE_AFF_REAL || aff==SQLITE_AFF_NUMERIC;
+ return aff>=SQLITE_AFF_NUMERIC;
}
case TK_STRING: {
- return aff==SQLITE_AFF_TEXT;
+ return !unaryMinus && aff==SQLITE_AFF_TEXT;
}
case TK_BLOB: {
- return 1;
+ return !unaryMinus;
}
case TK_COLUMN: {
assert( p->iTable>=0 ); /* p cannot be part of a CHECK constraint */
- return p->iColumn<0
- && (aff==SQLITE_AFF_INTEGER || aff==SQLITE_AFF_NUMERIC);
+ return aff>=SQLITE_AFF_NUMERIC && p->iColumn<0;
}
default: {
return 0;
@@ -98264,30 +111142,43 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){
if( sqlite3StrICmp(z, "OID")==0 ) return 1;
return 0;
}
-#ifdef SQLITE_ENABLE_NORMALIZE
-SQLITE_PRIVATE int sqlite3IsRowidN(const char *z, int n){
- if( sqlite3StrNICmp(z, "_ROWID_", n)==0 ) return 1;
- if( sqlite3StrNICmp(z, "ROWID", n)==0 ) return 1;
- if( sqlite3StrNICmp(z, "OID", n)==0 ) return 1;
+
+/*
+** Return a pointer to a buffer containing a usable rowid alias for table
+** pTab. An alias is usable if there is not an explicit user-defined column
+** of the same name.
+*/
+SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab){
+ const char *azOpt[] = {"_ROWID_", "ROWID", "OID"};
+ int ii;
+ assert( VisibleRowid(pTab) );
+ for(ii=0; ii<ArraySize(azOpt); ii++){
+ int iCol;
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ if( sqlite3_stricmp(azOpt[ii], pTab->aCol[iCol].zCnName)==0 ) break;
+ }
+ if( iCol==pTab->nCol ){
+ return azOpt[ii];
+ }
+ }
return 0;
}
-#endif
/*
-** pX is the RHS of an IN operator. If pX is a SELECT statement
+** pX is the RHS of an IN operator. If pX is a SELECT statement
** that can be simplified to a direct table access, then return
** a pointer to the SELECT statement. If pX is not a SELECT statement,
-** or if the SELECT statement needs to be manifested into a transient
+** or if the SELECT statement needs to be materialized into a transient
** table, then return NULL.
*/
#ifndef SQLITE_OMIT_SUBQUERY
-static Select *isCandidateForInOpt(Expr *pX){
+static Select *isCandidateForInOpt(const Expr *pX){
Select *p;
SrcList *pSrc;
ExprList *pEList;
Table *pTab;
int i;
- if( !ExprHasProperty(pX, EP_xIsSelect) ) return 0; /* Not a subquery */
+ if( !ExprUseXSelect(pX) ) return 0; /* Not a subquery */
if( ExprHasProperty(pX, EP_VarSelect) ) return 0; /* Correlated subq */
p = pX->x.pSelect;
if( p->pPrior ) return 0; /* Not a compound SELECT */
@@ -98305,7 +111196,7 @@ static Select *isCandidateForInOpt(Expr *pX){
if( pSrc->a[0].pSelect ) return 0; /* FROM is not a subquery or view */
pTab = pSrc->a[0].pTab;
assert( pTab!=0 );
- assert( pTab->pSelect==0 ); /* FROM clause is not a view */
+ assert( !IsView(pTab) ); /* FROM clause is not a view */
if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */
pEList = p->pEList;
assert( pEList!=0 );
@@ -98340,7 +111231,7 @@ static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){
#ifndef SQLITE_OMIT_SUBQUERY
/*
-** The argument is an IN operator with a list (not a subquery) on the
+** The argument is an IN operator with a list (not a subquery) on the
** right-hand side. Return TRUE if that list is constant.
*/
static int sqlite3InRhsIsConstant(Expr *pIn){
@@ -98365,7 +111256,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
** all members of the RHS set, skipping duplicates.
**
** A cursor is opened on the b-tree object that is the RHS of the IN operator
-** and pX->iTable is set to the index of that cursor.
+** and the *piTab parameter is set to the index of that cursor.
**
** The returned value of this function indicates the b-tree type, as follows:
**
@@ -98373,7 +111264,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
** IN_INDEX_INDEX_ASC - The cursor was opened on an ascending index.
** IN_INDEX_INDEX_DESC - The cursor was opened on a descending index.
** IN_INDEX_EPH - The cursor was opened on a specially created and
-** populated epheremal table.
+** populated ephemeral table.
** IN_INDEX_NOOP - No cursor was allocated. The IN operator must be
** implemented as a sequence of comparisons.
**
@@ -98385,7 +111276,10 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
** If the RHS of the IN operator is a list or a more complex subquery, then
** an ephemeral table might need to be generated from the RHS and then
** pX->iTable made to point to the ephemeral table instead of an
-** existing table.
+** existing table. In this case, the creation and initialization of the
+** ephemeral table might be put inside of a subroutine, the EP_Subrtn flag
+** will be set on pX and the pX->y.sub fields will be set to show where
+** the subroutine is coded.
**
** The inFlags parameter must contain, at a minimum, one of the bits
** IN_INDEX_MEMBERSHIP or IN_INDEX_LOOP but not both. If inFlags contains
@@ -98395,13 +111289,13 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
**
** When IN_INDEX_LOOP is used (and the b-tree will be used to iterate
** through the set members) then the b-tree must not contain duplicates.
-** An epheremal table will be created unless the selected columns are guaranteed
+** An ephemeral table will be created unless the selected columns are guaranteed
** to be unique - either because it is an INTEGER PRIMARY KEY or due to
** a UNIQUE constraint or index.
**
-** When IN_INDEX_MEMBERSHIP is used (and the b-tree will be used
-** for fast set membership tests) then an epheremal table must
-** be used unless <columns> is a single INTEGER PRIMARY KEY column or an
+** When IN_INDEX_MEMBERSHIP is used (and the b-tree will be used
+** for fast set membership tests) then an ephemeral table must
+** be used unless <columns> is a single INTEGER PRIMARY KEY column or an
** index can be found with the specified <columns> as its left-most.
**
** If the IN_INDEX_NOOP_OK and IN_INDEX_MEMBERSHIP are both set and
@@ -98413,7 +111307,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
**
** When the b-tree is being used for membership tests, the calling function
** might need to know whether or not the RHS side of the IN operator
-** contains a NULL. If prRhsHasNull is not a NULL pointer and
+** contains a NULL. If prRhsHasNull is not a NULL pointer and
** if there is any chance that the (...) might contain a NULL value at
** runtime, then a register is allocated and the register number written
** to *prRhsHasNull. If there is no chance that the (...) contains a
@@ -98438,26 +111332,28 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
#ifndef SQLITE_OMIT_SUBQUERY
SQLITE_PRIVATE int sqlite3FindInIndex(
Parse *pParse, /* Parsing context */
- Expr *pX, /* The right-hand side (RHS) of the IN operator */
+ Expr *pX, /* The IN expression */
u32 inFlags, /* IN_INDEX_LOOP, _MEMBERSHIP, and/or _NOOP_OK */
int *prRhsHasNull, /* Register holding NULL status. See notes */
- int *aiMap /* Mapping from Index fields to RHS fields */
+ int *aiMap, /* Mapping from Index fields to RHS fields */
+ int *piTab /* OUT: index to use */
){
Select *p; /* SELECT to the right of IN operator */
int eType = 0; /* Type of RHS table. IN_INDEX_* */
- int iTab = pParse->nTab++; /* Cursor of the RHS table */
+ int iTab; /* Cursor of the RHS table */
int mustBeUnique; /* True if RHS must be unique */
Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
assert( pX->op==TK_IN );
mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0;
+ iTab = pParse->nTab++;
- /* If the RHS of this IN(...) operator is a SELECT, and if it matters
+ /* If the RHS of this IN(...) operator is a SELECT, and if it matters
** whether or not the SELECT result contains NULL values, check whether
- ** or not NULL is actually possible (it may not be, for example, due
+ ** or not NULL is actually possible (it may not be, for example, due
** to NOT NULL constraints in the schema). If no NULL values are possible,
** set prRhsHasNull to 0 before continuing. */
- if( prRhsHasNull && (pX->flags & EP_xIsSelect) ){
+ if( prRhsHasNull && ExprUseXSelect(pX) ){
int i;
ExprList *pEList = pX->x.pSelect->pEList;
for(i=0; i<pEList->nExpr; i++){
@@ -98469,12 +111365,12 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
}
/* Check to see if an existing table or index can be used to
- ** satisfy the query. This is preferable to generating a new
+ ** satisfy the query. This is preferable to generating a new
** ephemeral table. */
if( pParse->nErr==0 && (p = isCandidateForInOpt(pX))!=0 ){
sqlite3 *db = pParse->db; /* Database connection */
Table *pTab; /* Table <table>. */
- i16 iDb; /* Database idx for pTab */
+ int iDb; /* Database idx for pTab */
ExprList *pEList = p->pEList;
int nExpr = pEList->nExpr;
@@ -98485,6 +111381,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
/* Code an OP_Transaction and OP_TableLock for <table>. */
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ assert( iDb>=0 && iDb<SQLITE_MAX_DB );
sqlite3CodeVerifySchema(pParse, iDb);
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
@@ -98504,7 +111401,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
int affinity_ok = 1;
int i;
- /* Check that the affinity that will be used to perform each
+ /* Check that the affinity that will be used to perform each
** comparison is the same as the affinity of each column in table
** on the RHS of the IN operator. If it not, it is not possible to
** use any index of the RHS table. */
@@ -98536,6 +111433,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
Bitmask colUsed; /* Columns of the index used */
Bitmask mCol; /* Mask for the current column */
if( pIdx->nColumn<nExpr ) continue;
+ if( pIdx->pPartIdxWhere!=0 ) continue;
/* Maximum nColumn is BMS-2, not BMS-1, so that we can compute
** BITMASK(nExpr) without overflowing */
testcase( pIdx->nColumn==BMS-2 );
@@ -98548,15 +111446,14 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
continue; /* This index is not unique over the IN RHS columns */
}
}
-
+
colUsed = 0; /* Columns of index used so far */
for(i=0; i<nExpr; i++){
Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i);
Expr *pRhs = pEList->a[i].pExpr;
CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
int j;
-
- assert( pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr );
+
for(j=0; j<nExpr; j++){
if( pIdx->aiColumn[j]!=pRhs->iColumn ) continue;
assert( pIdx->azColl[j] );
@@ -98571,7 +111468,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
colUsed |= mCol;
if( aiMap ) aiMap[i] = j;
}
-
+
assert( i==nExpr || colUsed!=(MASKBIT(nExpr)-1) );
if( colUsed==(MASKBIT(nExpr)-1) ){
/* If we reach this point, that means the index pIdx is usable */
@@ -98583,11 +111480,11 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
VdbeComment((v, "%s", pIdx->zName));
assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 );
eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0];
-
+
if( prRhsHasNull ){
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
i64 mask = (1<<nExpr)-1;
- sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed,
+ sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed,
iTab, 0, 0, (u8*)&mask, P4_INT64);
#endif
*prRhsHasNull = ++pParse->nMem;
@@ -98611,9 +111508,11 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
*/
if( eType==0
&& (inFlags & IN_INDEX_NOOP_OK)
- && !ExprHasProperty(pX, EP_xIsSelect)
+ && ExprUseXList(pX)
&& (!sqlite3InRhsIsConstant(pX) || pX->x.pList->nExpr<=2)
){
+ pParse->nTab--; /* Back out the allocation of the unused cursor */
+ iTab = -1; /* Cursor is not allocated */
eType = IN_INDEX_NOOP;
}
@@ -98626,16 +111525,15 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
eType = IN_INDEX_EPH;
if( inFlags & IN_INDEX_LOOP ){
pParse->nQueryLoop = 0;
- if( pX->pLeft->iColumn<0 && !ExprHasProperty(pX, EP_xIsSelect) ){
- eType = IN_INDEX_ROWID;
- }
}else if( prRhsHasNull ){
*prRhsHasNull = rMayHaveNull = ++pParse->nMem;
}
- sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID);
+ assert( pX->op==TK_IN );
+ sqlite3CodeRhsOfIN(pParse, pX, iTab);
+ if( rMayHaveNull ){
+ sqlite3SetHasNullFlag(v, iTab, rMayHaveNull);
+ }
pParse->nQueryLoop = savedNQueryLoop;
- }else{
- pX->iTable = iTab;
}
if( aiMap && eType!=IN_INDEX_INDEX_ASC && eType!=IN_INDEX_INDEX_DESC ){
@@ -98643,23 +111541,24 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
n = sqlite3ExprVectorSize(pX->pLeft);
for(i=0; i<n; i++) aiMap[i] = i;
}
+ *piTab = iTab;
return eType;
}
#endif
#ifndef SQLITE_OMIT_SUBQUERY
/*
-** Argument pExpr is an (?, ?...) IN(...) expression. This
-** function allocates and returns a nul-terminated string containing
+** Argument pExpr is an (?, ?...) IN(...) expression. This
+** function allocates and returns a nul-terminated string containing
** the affinities to be used for each column of the comparison.
**
** It is the responsibility of the caller to ensure that the returned
** string is eventually freed using sqlite3DbFree().
*/
-static char *exprINAffinity(Parse *pParse, Expr *pExpr){
+static char *exprINAffinity(Parse *pParse, const Expr *pExpr){
Expr *pLeft = pExpr->pLeft;
int nVal = sqlite3ExprVectorSize(pLeft);
- Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0;
+ Select *pSelect = ExprUseXSelect(pExpr) ? pExpr->x.pSelect : 0;
char *zRet;
assert( pExpr->op==TK_IN );
@@ -98683,20 +111582,22 @@ static char *exprINAffinity(Parse *pParse, Expr *pExpr){
#ifndef SQLITE_OMIT_SUBQUERY
/*
-** Load the Parse object passed as the first argument with an error
+** Load the Parse object passed as the first argument with an error
** message of the form:
**
** "sub-select returns N columns - expected M"
-*/
+*/
SQLITE_PRIVATE void sqlite3SubselectError(Parse *pParse, int nActual, int nExpect){
- const char *zFmt = "sub-select returns %d columns - expected %d";
- sqlite3ErrorMsg(pParse, zFmt, nActual, nExpect);
+ if( pParse->nErr==0 ){
+ const char *zFmt = "sub-select returns %d columns - expected %d";
+ sqlite3ErrorMsg(pParse, zFmt, nActual, nExpect);
+ }
}
#endif
/*
** Expression pExpr is a vector that has been used in a context where
-** it is not permitted. If pExpr is a sub-select vector, this routine
+** it is not permitted. If pExpr is a sub-select vector, this routine
** loads the Parse object with a message of the form:
**
** "sub-select returns N columns - expected 1"
@@ -98704,10 +111605,10 @@ SQLITE_PRIVATE void sqlite3SubselectError(Parse *pParse, int nActual, int nExpec
** Or, if it is a regular scalar vector:
**
** "row value misused"
-*/
+*/
SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){
#ifndef SQLITE_OMIT_SUBQUERY
- if( pExpr->flags & EP_xIsSelect ){
+ if( ExprUseXSelect(pExpr) ){
sqlite3SubselectError(pParse, pExpr->x.pSelect->pEList->nExpr, 1);
}else
#endif
@@ -98716,276 +111617,360 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){
}
}
+#ifndef SQLITE_OMIT_SUBQUERY
/*
-** Generate code for scalar subqueries used as a subquery expression, EXISTS,
-** or IN operators. Examples:
+** Generate code that will construct an ephemeral table containing all terms
+** in the RHS of an IN operator. The IN operator can be in either of two
+** forms:
**
-** (SELECT a FROM b) -- subquery
-** EXISTS (SELECT a FROM b) -- EXISTS subquery
** x IN (4,5,11) -- IN operator with list on right-hand side
** x IN (SELECT a FROM b) -- IN operator with subquery on the right
**
-** The pExpr parameter describes the expression that contains the IN
-** operator or subquery.
-**
-** If parameter isRowid is non-zero, then expression pExpr is guaranteed
-** to be of the form "<rowid> IN (?, ?, ?)", where <rowid> is a reference
-** to some integer key column of a table B-Tree. In this case, use an
-** intkey B-Tree to store the set of IN(...) values instead of the usual
-** (slower) variable length keys B-Tree.
-**
-** If rMayHaveNull is non-zero, that means that the operation is an IN
-** (not a SELECT or EXISTS) and that the RHS might contains NULLs.
-** All this routine does is initialize the register given by rMayHaveNull
-** to NULL. Calling routines will take care of changing this register
-** value to non-NULL if the RHS is NULL-free.
-**
-** For a SELECT or EXISTS operator, return the register that holds the
-** result. For a multi-column SELECT, the result is stored in a contiguous
-** array of registers and the return value is the register of the left-most
-** result column. Return 0 for IN operators or if an error occurs.
-*/
-#ifndef SQLITE_OMIT_SUBQUERY
-SQLITE_PRIVATE int sqlite3CodeSubselect(
+** The pExpr parameter is the IN operator. The cursor number for the
+** constructed ephemeral table is returned. The first time the ephemeral
+** table is computed, the cursor number is also stored in pExpr->iTable,
+** however the cursor number returned might not be the same, as it might
+** have been duplicated using OP_OpenDup.
+**
+** If the LHS expression ("x" in the examples) is a column value, or
+** the SELECT statement returns a column value, then the affinity of that
+** column is used to build the index keys. If both 'x' and the
+** SELECT... statement are columns, then numeric affinity is used
+** if either column has NUMERIC or INTEGER affinity. If neither
+** 'x' nor the SELECT... statement are columns, then numeric affinity
+** is used.
+*/
+SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
Parse *pParse, /* Parsing context */
- Expr *pExpr, /* The IN, SELECT, or EXISTS operator */
- int rHasNullFlag, /* Register that records whether NULLs exist in RHS */
- int isRowid /* If true, LHS of IN operator is a rowid */
+ Expr *pExpr, /* The IN operator */
+ int iTab /* Use this cursor number */
){
- int jmpIfDynamic = -1; /* One-time test address */
- int rReg = 0; /* Register storing resulting */
- Vdbe *v = sqlite3GetVdbe(pParse);
- if( NEVER(v==0) ) return 0;
+ int addrOnce = 0; /* Address of the OP_Once instruction at top */
+ int addr; /* Address of OP_OpenEphemeral instruction */
+ Expr *pLeft; /* the LHS of the IN operator */
+ KeyInfo *pKeyInfo = 0; /* Key information */
+ int nVal; /* Size of vector pLeft */
+ Vdbe *v; /* The prepared statement under construction */
+
+ v = pParse->pVdbe;
+ assert( v!=0 );
- /* The evaluation of the IN/EXISTS/SELECT must be repeated every time it
+ /* The evaluation of the IN must be repeated every time it
** is encountered if any of the following is true:
**
** * The right-hand side is a correlated subquery
** * The right-hand side is an expression list containing variables
** * We are inside a trigger
**
- ** If all of the above are false, then we can run this code just once
- ** save the results, and reuse the same result on subsequent invocations.
+ ** If all of the above are false, then we can compute the RHS just once
+ ** and reuse it many names.
*/
- if( !ExprHasProperty(pExpr, EP_VarSelect) ){
- jmpIfDynamic = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
- }
+ if( !ExprHasProperty(pExpr, EP_VarSelect) && pParse->iSelfTab==0 ){
+ /* Reuse of the RHS is allowed */
+ /* If this routine has already been coded, but the previous code
+ ** might not have been invoked yet, so invoke it now as a subroutine.
+ */
+ if( ExprHasProperty(pExpr, EP_Subrtn) ){
+ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+ if( ExprUseXSelect(pExpr) ){
+ ExplainQueryPlan((pParse, 0, "REUSE LIST SUBQUERY %d",
+ pExpr->x.pSelect->selId));
+ }
+ assert( ExprUseYSub(pExpr) );
+ sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn,
+ pExpr->y.sub.iAddr);
+ assert( iTab!=pExpr->iTable );
+ sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable);
+ sqlite3VdbeJumpHere(v, addrOnce);
+ return;
+ }
- switch( pExpr->op ){
- case TK_IN: {
- int addr; /* Address of OP_OpenEphemeral instruction */
- Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
- KeyInfo *pKeyInfo = 0; /* Key information */
- int nVal; /* Size of vector pLeft */
-
- nVal = sqlite3ExprVectorSize(pLeft);
- assert( !isRowid || nVal==1 );
-
- /* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
- ** expression it is handled the same way. An ephemeral table is
- ** filled with index keys representing the results from the
- ** SELECT or the <exprlist>.
- **
- ** If the 'x' expression is a column value, or the SELECT...
- ** statement returns a column value, then the affinity of that
- ** column is used to build the index keys. If both 'x' and the
- ** SELECT... statement are columns, then numeric affinity is used
- ** if either column has NUMERIC or INTEGER affinity. If neither
- ** 'x' nor the SELECT... statement are columns, then numeric affinity
- ** is used.
- */
- pExpr->iTable = pParse->nTab++;
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral,
- pExpr->iTable, (isRowid?0:nVal));
- pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1);
+ /* Begin coding the subroutine */
+ assert( !ExprUseYWin(pExpr) );
+ ExprSetProperty(pExpr, EP_Subrtn);
+ assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
+ pExpr->y.sub.regReturn = ++pParse->nMem;
+ pExpr->y.sub.iAddr =
+ sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1;
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- /* Case 1: expr IN (SELECT ...)
- **
- ** Generate code to write the results of the select into the temporary
- ** table allocated and opened above.
- */
- Select *pSelect = pExpr->x.pSelect;
- ExprList *pEList = pSelect->pEList;
+ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+ }
- ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY",
- jmpIfDynamic>=0?"":"CORRELATED "
- ));
- assert( !isRowid );
- /* If the LHS and RHS of the IN operator do not match, that
- ** error will have been caught long before we reach this point. */
- if( ALWAYS(pEList->nExpr==nVal) ){
- SelectDest dest;
- int i;
- sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
- dest.zAffSdst = exprINAffinity(pParse, pExpr);
- pSelect->iLimit = 0;
- testcase( pSelect->selFlags & SF_Distinct );
- testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
- if( sqlite3Select(pParse, pSelect, &dest) ){
- sqlite3DbFree(pParse->db, dest.zAffSdst);
- sqlite3KeyInfoUnref(pKeyInfo);
- return 0;
- }
- sqlite3DbFree(pParse->db, dest.zAffSdst);
- assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
- assert( pEList!=0 );
- assert( pEList->nExpr>0 );
- assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
- for(i=0; i<nVal; i++){
- Expr *p = sqlite3VectorFieldSubexpr(pLeft, i);
- pKeyInfo->aColl[i] = sqlite3BinaryCompareCollSeq(
- pParse, p, pEList->a[i].pExpr
- );
- }
- }
- }else if( ALWAYS(pExpr->x.pList!=0) ){
- /* Case 2: expr IN (exprlist)
- **
- ** For each expression, build an index key from the evaluation and
- ** store it in the temporary table. If <expr> is a column, then use
- ** that columns affinity when building index keys. If <expr> is not
- ** a column, use numeric affinity.
- */
- char affinity; /* Affinity of the LHS of the IN */
- int i;
- ExprList *pList = pExpr->x.pList;
- struct ExprList_item *pItem;
- int r1, r2, r3;
- affinity = sqlite3ExprAffinity(pLeft);
- if( !affinity ){
- affinity = SQLITE_AFF_BLOB;
- }
- if( pKeyInfo ){
- assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
- pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
- }
+ /* Check to see if this is a vector IN operator */
+ pLeft = pExpr->pLeft;
+ nVal = sqlite3ExprVectorSize(pLeft);
- /* Loop through each expression in <exprlist>. */
- r1 = sqlite3GetTempReg(pParse);
- r2 = sqlite3GetTempReg(pParse);
- if( isRowid ) sqlite3VdbeAddOp4(v, OP_Blob, 0, r2, 0, "", P4_STATIC);
- for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
- Expr *pE2 = pItem->pExpr;
- int iValToIns;
-
- /* If the expression is not constant then we will need to
- ** disable the test that was generated above that makes sure
- ** this code only executes once. Because for a non-constant
- ** expression we need to rerun this code each time.
- */
- if( jmpIfDynamic>=0 && !sqlite3ExprIsConstant(pE2) ){
- sqlite3VdbeChangeToNoop(v, jmpIfDynamic);
- jmpIfDynamic = -1;
- }
+ /* Construct the ephemeral table that will contain the content of
+ ** RHS of the IN operator.
+ */
+ pExpr->iTable = iTab;
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, nVal);
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ if( ExprUseXSelect(pExpr) ){
+ VdbeComment((v, "Result of SELECT %u", pExpr->x.pSelect->selId));
+ }else{
+ VdbeComment((v, "RHS of IN operator"));
+ }
+#endif
+ pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nVal, 1);
- /* Evaluate the expression and insert it into the temp table */
- if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){
- sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns);
- }else{
- r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
- if( isRowid ){
- sqlite3VdbeAddOp2(v, OP_MustBeInt, r3,
- sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
- }else{
- sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
- sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pExpr->iTable, r2, r3, 1);
- }
- }
- }
- sqlite3ReleaseTempReg(pParse, r1);
- sqlite3ReleaseTempReg(pParse, r2);
+ if( ExprUseXSelect(pExpr) ){
+ /* Case 1: expr IN (SELECT ...)
+ **
+ ** Generate code to write the results of the select into the temporary
+ ** table allocated and opened above.
+ */
+ Select *pSelect = pExpr->x.pSelect;
+ ExprList *pEList = pSelect->pEList;
+
+ ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY %d",
+ addrOnce?"":"CORRELATED ", pSelect->selId
+ ));
+ /* If the LHS and RHS of the IN operator do not match, that
+ ** error will have been caught long before we reach this point. */
+ if( ALWAYS(pEList->nExpr==nVal) ){
+ Select *pCopy;
+ SelectDest dest;
+ int i;
+ int rc;
+ sqlite3SelectDestInit(&dest, SRT_Set, iTab);
+ dest.zAffSdst = exprINAffinity(pParse, pExpr);
+ pSelect->iLimit = 0;
+ testcase( pSelect->selFlags & SF_Distinct );
+ testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
+ pCopy = sqlite3SelectDup(pParse->db, pSelect, 0);
+ rc = pParse->db->mallocFailed ? 1 :sqlite3Select(pParse, pCopy, &dest);
+ sqlite3SelectDelete(pParse->db, pCopy);
+ sqlite3DbFree(pParse->db, dest.zAffSdst);
+ if( rc ){
+ sqlite3KeyInfoUnref(pKeyInfo);
+ return;
}
- if( pKeyInfo ){
- sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
+ assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
+ assert( pEList!=0 );
+ assert( pEList->nExpr>0 );
+ assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
+ for(i=0; i<nVal; i++){
+ Expr *p = sqlite3VectorFieldSubexpr(pLeft, i);
+ pKeyInfo->aColl[i] = sqlite3BinaryCompareCollSeq(
+ pParse, p, pEList->a[i].pExpr
+ );
}
- break;
+ }
+ }else if( ALWAYS(pExpr->x.pList!=0) ){
+ /* Case 2: expr IN (exprlist)
+ **
+ ** For each expression, build an index key from the evaluation and
+ ** store it in the temporary table. If <expr> is a column, then use
+ ** that columns affinity when building index keys. If <expr> is not
+ ** a column, use numeric affinity.
+ */
+ char affinity; /* Affinity of the LHS of the IN */
+ int i;
+ ExprList *pList = pExpr->x.pList;
+ struct ExprList_item *pItem;
+ int r1, r2;
+ affinity = sqlite3ExprAffinity(pLeft);
+ if( affinity<=SQLITE_AFF_NONE ){
+ affinity = SQLITE_AFF_BLOB;
+ }else if( affinity==SQLITE_AFF_REAL ){
+ affinity = SQLITE_AFF_NUMERIC;
+ }
+ if( pKeyInfo ){
+ assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
+ pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
}
- case TK_EXISTS:
- case TK_SELECT:
- default: {
- /* Case 3: (SELECT ... FROM ...)
- ** or: EXISTS(SELECT ... FROM ...)
- **
- ** For a SELECT, generate code to put the values for all columns of
- ** the first row into an array of registers and return the index of
- ** the first register.
- **
- ** If this is an EXISTS, write an integer 0 (not exists) or 1 (exists)
- ** into a register and return that register number.
- **
- ** In both cases, the query is augmented with "LIMIT 1". Any
- ** preexisting limit is discarded in place of the new LIMIT 1.
+ /* Loop through each expression in <exprlist>. */
+ r1 = sqlite3GetTempReg(pParse);
+ r2 = sqlite3GetTempReg(pParse);
+ for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
+ Expr *pE2 = pItem->pExpr;
+
+ /* If the expression is not constant then we will need to
+ ** disable the test that was generated above that makes sure
+ ** this code only executes once. Because for a non-constant
+ ** expression we need to rerun this code each time.
*/
- Select *pSel; /* SELECT statement to encode */
- SelectDest dest; /* How to deal with SELECT result */
- int nReg; /* Registers to allocate */
- Expr *pLimit; /* New limit expression */
-
- testcase( pExpr->op==TK_EXISTS );
- testcase( pExpr->op==TK_SELECT );
- assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
- assert( ExprHasProperty(pExpr, EP_xIsSelect) );
-
- pSel = pExpr->x.pSelect;
- ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY",
- jmpIfDynamic>=0?"":"CORRELATED "));
- nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
- sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
- pParse->nMem += nReg;
- if( pExpr->op==TK_SELECT ){
- dest.eDest = SRT_Mem;
- dest.iSdst = dest.iSDParm;
- dest.nSdst = nReg;
- sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1);
- VdbeComment((v, "Init subquery result"));
- }else{
- dest.eDest = SRT_Exists;
- sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
- VdbeComment((v, "Init EXISTS result"));
- }
- pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[1], 0);
- if( pSel->pLimit ){
- sqlite3ExprDelete(pParse->db, pSel->pLimit->pLeft);
- pSel->pLimit->pLeft = pLimit;
- }else{
- pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0);
- }
- pSel->iLimit = 0;
- if( sqlite3Select(pParse, pSel, &dest) ){
- return 0;
+ if( addrOnce && !sqlite3ExprIsConstant(pE2) ){
+ sqlite3VdbeChangeToNoop(v, addrOnce-1);
+ sqlite3VdbeChangeToNoop(v, addrOnce);
+ ExprClearProperty(pExpr, EP_Subrtn);
+ addrOnce = 0;
}
- rReg = dest.iSDParm;
- ExprSetVVAProperty(pExpr, EP_NoReduce);
- break;
+
+ /* Evaluate the expression and insert it into the temp table */
+ sqlite3ExprCode(pParse, pE2, r1);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, r1, 1, r2, &affinity, 1);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r1, 1);
}
+ sqlite3ReleaseTempReg(pParse, r1);
+ sqlite3ReleaseTempReg(pParse, r2);
+ }
+ if( pKeyInfo ){
+ sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
+ }
+ if( addrOnce ){
+ sqlite3VdbeAddOp1(v, OP_NullRow, iTab);
+ sqlite3VdbeJumpHere(v, addrOnce);
+ /* Subroutine return */
+ assert( ExprUseYSub(pExpr) );
+ assert( sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn
+ || pParse->nErr );
+ sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn,
+ pExpr->y.sub.iAddr, 1);
+ VdbeCoverage(v);
+ sqlite3ClearTempRegCache(pParse);
}
+}
+#endif /* SQLITE_OMIT_SUBQUERY */
- if( rHasNullFlag ){
- sqlite3SetHasNullFlag(v, pExpr->iTable, rHasNullFlag);
+/*
+** Generate code for scalar subqueries used as a subquery expression
+** or EXISTS operator:
+**
+** (SELECT a FROM b) -- subquery
+** EXISTS (SELECT a FROM b) -- EXISTS subquery
+**
+** The pExpr parameter is the SELECT or EXISTS operator to be coded.
+**
+** Return the register that holds the result. For a multi-column SELECT,
+** the result is stored in a contiguous array of registers and the
+** return value is the register of the left-most result column.
+** Return 0 if an error occurs.
+*/
+#ifndef SQLITE_OMIT_SUBQUERY
+SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
+ int addrOnce = 0; /* Address of OP_Once at top of subroutine */
+ int rReg = 0; /* Register storing resulting */
+ Select *pSel; /* SELECT statement to encode */
+ SelectDest dest; /* How to deal with SELECT result */
+ int nReg; /* Registers to allocate */
+ Expr *pLimit; /* New limit expression */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrExplain; /* Address of OP_Explain instruction */
+#endif
+
+ Vdbe *v = pParse->pVdbe;
+ assert( v!=0 );
+ if( pParse->nErr ) return 0;
+ testcase( pExpr->op==TK_EXISTS );
+ testcase( pExpr->op==TK_SELECT );
+ assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
+ assert( ExprUseXSelect(pExpr) );
+ pSel = pExpr->x.pSelect;
+
+ /* If this routine has already been coded, then invoke it as a
+ ** subroutine. */
+ if( ExprHasProperty(pExpr, EP_Subrtn) ){
+ ExplainQueryPlan((pParse, 0, "REUSE SUBQUERY %d", pSel->selId));
+ assert( ExprUseYSub(pExpr) );
+ sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn,
+ pExpr->y.sub.iAddr);
+ return pExpr->iTable;
+ }
+
+ /* Begin coding the subroutine */
+ assert( !ExprUseYWin(pExpr) );
+ assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) );
+ ExprSetProperty(pExpr, EP_Subrtn);
+ pExpr->y.sub.regReturn = ++pParse->nMem;
+ pExpr->y.sub.iAddr =
+ sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1;
+
+ /* The evaluation of the EXISTS/SELECT must be repeated every time it
+ ** is encountered if any of the following is true:
+ **
+ ** * The right-hand side is a correlated subquery
+ ** * The right-hand side is an expression list containing variables
+ ** * We are inside a trigger
+ **
+ ** If all of the above are false, then we can run this code just once
+ ** save the results, and reuse the same result on subsequent invocations.
+ */
+ if( !ExprHasProperty(pExpr, EP_VarSelect) ){
+ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}
- if( jmpIfDynamic>=0 ){
- sqlite3VdbeJumpHere(v, jmpIfDynamic);
+ /* For a SELECT, generate code to put the values for all columns of
+ ** the first row into an array of registers and return the index of
+ ** the first register.
+ **
+ ** If this is an EXISTS, write an integer 0 (not exists) or 1 (exists)
+ ** into a register and return that register number.
+ **
+ ** In both cases, the query is augmented with "LIMIT 1". Any
+ ** preexisting limit is discarded in place of the new LIMIT 1.
+ */
+ ExplainQueryPlan2(addrExplain, (pParse, 1, "%sSCALAR SUBQUERY %d",
+ addrOnce?"":"CORRELATED ", pSel->selId));
+ sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, -1);
+ nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
+ sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
+ pParse->nMem += nReg;
+ if( pExpr->op==TK_SELECT ){
+ dest.eDest = SRT_Mem;
+ dest.iSdst = dest.iSDParm;
+ dest.nSdst = nReg;
+ sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1);
+ VdbeComment((v, "Init subquery result"));
+ }else{
+ dest.eDest = SRT_Exists;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
+ VdbeComment((v, "Init EXISTS result"));
+ }
+ if( pSel->pLimit ){
+ /* The subquery already has a limit. If the pre-existing limit is X
+ ** then make the new limit X<>0 so that the new limit is either 1 or 0 */
+ sqlite3 *db = pParse->db;
+ pLimit = sqlite3Expr(db, TK_INTEGER, "0");
+ if( pLimit ){
+ pLimit->affExpr = SQLITE_AFF_NUMERIC;
+ pLimit = sqlite3PExpr(pParse, TK_NE,
+ sqlite3ExprDup(db, pSel->pLimit->pLeft, 0), pLimit);
+ }
+ sqlite3ExprDeferredDelete(pParse, pSel->pLimit->pLeft);
+ pSel->pLimit->pLeft = pLimit;
+ }else{
+ /* If there is no pre-existing limit add a limit of 1 */
+ pLimit = sqlite3Expr(pParse->db, TK_INTEGER, "1");
+ pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0);
+ }
+ pSel->iLimit = 0;
+ if( sqlite3Select(pParse, pSel, &dest) ){
+ pExpr->op2 = pExpr->op;
+ pExpr->op = TK_ERROR;
+ return 0;
+ }
+ pExpr->iTable = rReg = dest.iSDParm;
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
+ if( addrOnce ){
+ sqlite3VdbeJumpHere(v, addrOnce);
}
+ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1);
+ /* Subroutine return */
+ assert( ExprUseYSub(pExpr) );
+ assert( sqlite3VdbeGetOp(v,pExpr->y.sub.iAddr-1)->opcode==OP_BeginSubrtn
+ || pParse->nErr );
+ sqlite3VdbeAddOp3(v, OP_Return, pExpr->y.sub.regReturn,
+ pExpr->y.sub.iAddr, 1);
+ VdbeCoverage(v);
+ sqlite3ClearTempRegCache(pParse);
return rReg;
}
#endif /* SQLITE_OMIT_SUBQUERY */
#ifndef SQLITE_OMIT_SUBQUERY
/*
-** Expr pIn is an IN(...) expression. This function checks that the
-** sub-select on the RHS of the IN() operator has the same number of
-** columns as the vector on the LHS. Or, if the RHS of the IN() is not
+** Expr pIn is an IN(...) expression. This function checks that the
+** sub-select on the RHS of the IN() operator has the same number of
+** columns as the vector on the LHS. Or, if the RHS of the IN() is not
** a sub-query, that the LHS is a vector of size 1.
*/
SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){
int nVector = sqlite3ExprVectorSize(pIn->pLeft);
- if( (pIn->flags & EP_xIsSelect) ){
+ if( ExprUseXSelect(pIn) && !pParse->db->mallocFailed ){
if( nVector!=pIn->x.pSelect->pEList->nExpr ){
sqlite3SubselectError(pParse, pIn->x.pSelect->pEList->nExpr, nVector);
return 1;
@@ -99005,18 +111990,18 @@ SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){
** x IN (SELECT ...)
** x IN (value, value, ...)
**
-** The left-hand side (LHS) is a scalar or vector expression. The
+** The left-hand side (LHS) is a scalar or vector expression. The
** right-hand side (RHS) is an array of zero or more scalar values, or a
** subquery. If the RHS is a subquery, the number of result columns must
** match the number of columns in the vector on the LHS. If the RHS is
-** a list of values, the LHS must be a scalar.
+** a list of values, the LHS must be a scalar.
**
** The IN operator is true if the LHS value is contained within the RHS.
-** The result is false if the LHS is definitely not in the RHS. The
-** result is NULL if the presence of the LHS in the RHS cannot be
+** The result is false if the LHS is definitely not in the RHS. The
+** result is NULL if the presence of the LHS in the RHS cannot be
** determined due to NULLs.
**
-** This routine generates code that jumps to destIfFalse if the LHS is not
+** This routine generates code that jumps to destIfFalse if the LHS is not
** contained within the RHS. If due to NULLs we cannot determine if the LHS
** is contained in the RHS then jump to destIfNull. If the LHS is contained
** within the RHS then fall through.
@@ -99045,8 +112030,11 @@ static void sqlite3ExprCodeIN(
int destStep6 = 0; /* Start of code for Step 6 */
int addrTruthOp; /* Address of opcode that determines the IN is true */
int destNotNull; /* Jump here if a comparison is not true in step 6 */
- int addrTop; /* Top of the step-6 loop */
+ int addrTop; /* Top of the step-6 loop */
+ int iTab = 0; /* Index to use */
+ u8 okConstFactor = pParse->okConstFactor;
+ assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
pLeft = pExpr->pLeft;
if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
zAff = exprINAffinity(pParse, pExpr);
@@ -99057,7 +112045,7 @@ static void sqlite3ExprCodeIN(
if( pParse->db->mallocFailed ) goto sqlite3ExprCodeIN_oom_error;
/* Attempt to compute the RHS. After this step, if anything other than
- ** IN_INDEX_NOOP is returned, the table opened ith cursor pExpr->iTable
+ ** IN_INDEX_NOOP is returned, the table opened with cursor iTab
** contains the values that make up the RHS. If IN_INDEX_NOOP is returned,
** the RHS has not yet been coded. */
v = pParse->pVdbe;
@@ -99065,10 +112053,11 @@ static void sqlite3ExprCodeIN(
VdbeNoopComment((v, "begin IN expr"));
eType = sqlite3FindInIndex(pParse, pExpr,
IN_INDEX_MEMBERSHIP | IN_INDEX_NOOP_OK,
- destIfFalse==destIfNull ? 0 : &rRhsHasNull, aiMap);
+ destIfFalse==destIfNull ? 0 : &rRhsHasNull,
+ aiMap, &iTab);
assert( pParse->nErr || nVector==1 || eType==IN_INDEX_EPH
- || eType==IN_INDEX_INDEX_ASC || eType==IN_INDEX_INDEX_DESC
+ || eType==IN_INDEX_INDEX_ASC || eType==IN_INDEX_INDEX_DESC
);
#ifdef SQLITE_DEBUG
/* Confirm that aiMap[] contains nVector integer values between 0 and
@@ -99080,16 +112069,22 @@ static void sqlite3ExprCodeIN(
}
#endif
- /* Code the LHS, the <expr> from "<expr> IN (...)". If the LHS is a
- ** vector, then it is stored in an array of nVector registers starting
+ /* Code the LHS, the <expr> from "<expr> IN (...)". If the LHS is a
+ ** vector, then it is stored in an array of nVector registers starting
** at r1.
**
** sqlite3FindInIndex() might have reordered the fields of the LHS vector
** so that the fields are in the same order as an existing index. The
** aiMap[] array contains a mapping from the original LHS field order to
** the field order that matches the RHS index.
- */
+ **
+ ** Avoid factoring the LHS of the IN(...) expression out of the loop,
+ ** even if it is constant, as OP_Affinity may be used on the register
+ ** by code generated below. */
+ assert( pParse->okConstFactor==okConstFactor );
+ pParse->okConstFactor = 0;
rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy);
+ pParse->okConstFactor = okConstFactor;
for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
if( i==nVector ){
/* LHS fields are not reordered */
@@ -99109,13 +112104,15 @@ static void sqlite3ExprCodeIN(
** This is step (1) in the in-operator.md optimized algorithm.
*/
if( eType==IN_INDEX_NOOP ){
- ExprList *pList = pExpr->x.pList;
- CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
- int labelOk = sqlite3VdbeMakeLabel(v);
+ ExprList *pList;
+ CollSeq *pColl;
+ int labelOk = sqlite3VdbeMakeLabel(pParse);
int r2, regToFree;
int regCkNull = 0;
int ii;
- assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
+ assert( ExprUseXList(pExpr) );
+ pList = pExpr->x.pList;
+ pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
if( destIfNull!=destIfFalse ){
regCkNull = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull);
@@ -99125,19 +112122,25 @@ static void sqlite3ExprCodeIN(
if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){
sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull);
}
+ sqlite3ReleaseTempReg(pParse, regToFree);
if( ii<pList->nExpr-1 || destIfNull!=destIfFalse ){
- sqlite3VdbeAddOp4(v, OP_Eq, rLhs, labelOk, r2,
+ int op = rLhs!=r2 ? OP_Eq : OP_NotNull;
+ sqlite3VdbeAddOp4(v, op, rLhs, labelOk, r2,
(void*)pColl, P4_COLLSEQ);
- VdbeCoverageIf(v, ii<pList->nExpr-1);
- VdbeCoverageIf(v, ii==pList->nExpr-1);
+ VdbeCoverageIf(v, ii<pList->nExpr-1 && op==OP_Eq);
+ VdbeCoverageIf(v, ii==pList->nExpr-1 && op==OP_Eq);
+ VdbeCoverageIf(v, ii<pList->nExpr-1 && op==OP_NotNull);
+ VdbeCoverageIf(v, ii==pList->nExpr-1 && op==OP_NotNull);
sqlite3VdbeChangeP5(v, zAff[0]);
}else{
+ int op = rLhs!=r2 ? OP_Ne : OP_IsNull;
assert( destIfNull==destIfFalse );
- sqlite3VdbeAddOp4(v, OP_Ne, rLhs, destIfFalse, r2,
- (void*)pColl, P4_COLLSEQ); VdbeCoverage(v);
+ sqlite3VdbeAddOp4(v, op, rLhs, destIfFalse, r2,
+ (void*)pColl, P4_COLLSEQ);
+ VdbeCoverageIf(v, op==OP_Ne);
+ VdbeCoverageIf(v, op==OP_IsNull);
sqlite3VdbeChangeP5(v, zAff[0] | SQLITE_JUMPIFNULL);
}
- sqlite3ReleaseTempReg(pParse, regToFree);
}
if( regCkNull ){
sqlite3VdbeAddOp2(v, OP_IsNull, regCkNull, destIfNull); VdbeCoverage(v);
@@ -99155,10 +112158,11 @@ static void sqlite3ExprCodeIN(
if( destIfNull==destIfFalse ){
destStep2 = destIfFalse;
}else{
- destStep2 = destStep6 = sqlite3VdbeMakeLabel(v);
+ destStep2 = destStep6 = sqlite3VdbeMakeLabel(pParse);
}
for(i=0; i<nVector; i++){
Expr *p = sqlite3VectorFieldSubexpr(pExpr->pLeft, i);
+ if( pParse->nErr ) goto sqlite3ExprCodeIN_oom_error;
if( sqlite3ExprCanBeNull(p) ){
sqlite3VdbeAddOp2(v, OP_IsNull, rLhs+i, destStep2);
VdbeCoverage(v);
@@ -99173,19 +112177,19 @@ static void sqlite3ExprCodeIN(
/* In this case, the RHS is the ROWID of table b-tree and so we also
** know that the RHS is non-NULL. Hence, we combine steps 3 and 4
** into a single opcode. */
- sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, rLhs);
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, iTab, destIfFalse, rLhs);
VdbeCoverage(v);
addrTruthOp = sqlite3VdbeAddOp0(v, OP_Goto); /* Return True */
}else{
sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector);
if( destIfFalse==destIfNull ){
/* Combine Step 3 and Step 5 into a single opcode */
- sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse,
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iTab, destIfFalse,
rLhs, nVector); VdbeCoverage(v);
goto sqlite3ExprCodeIN_finished;
}
/* Ordinary Step 3, for the case where FALSE and NULL are distinct */
- addrTruthOp = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0,
+ addrTruthOp = sqlite3VdbeAddOp4Int(v, OP_Found, iTab, 0,
rLhs, nVector); VdbeCoverage(v);
}
@@ -99198,7 +112202,7 @@ static void sqlite3ExprCodeIN(
}
/* Step 5. If we do not care about the difference between NULL and
- ** FALSE, then just return false.
+ ** FALSE, then just return false.
*/
if( destIfFalse==destIfNull ) sqlite3VdbeGoto(v, destIfFalse);
@@ -99210,10 +112214,10 @@ static void sqlite3ExprCodeIN(
** of the RHS.
*/
if( destStep6 ) sqlite3VdbeResolveLabel(v, destStep6);
- addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse);
+ addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, destIfFalse);
VdbeCoverage(v);
if( nVector>1 ){
- destNotNull = sqlite3VdbeMakeLabel(v);
+ destNotNull = sqlite3VdbeMakeLabel(pParse);
}else{
/* For nVector==1, combine steps 6 and 7 by immediately returning
** FALSE if the first comparison is not NULL */
@@ -99225,7 +112229,7 @@ static void sqlite3ExprCodeIN(
int r3 = sqlite3GetTempReg(pParse);
p = sqlite3VectorFieldSubexpr(pLeft, i);
pColl = sqlite3ExprCollSeq(pParse, p);
- sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, i, r3);
+ sqlite3VdbeAddOp3(v, OP_Column, iTab, i, r3);
sqlite3VdbeAddOp4(v, OP_Ne, rLhs+i, destNotNull, r3,
(void*)pColl, P4_COLLSEQ);
VdbeCoverage(v);
@@ -99234,7 +112238,7 @@ static void sqlite3ExprCodeIN(
sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull);
if( nVector>1 ){
sqlite3VdbeResolveLabel(v, destNotNull);
- sqlite3VdbeAddOp2(v, OP_Next, pExpr->iTable, addrTop+1);
+ sqlite3VdbeAddOp2(v, OP_Next, iTab, addrTop+1);
VdbeCoverage(v);
/* Step 7: If we reach this point, we know that the result must
@@ -99259,7 +112263,7 @@ sqlite3ExprCodeIN_oom_error:
** Generate an instruction that will put the floating point
** value described by z[0..n-1] into register iMem.
**
-** The z[] string will probably not be zero-terminated. But the
+** The z[] string will probably not be zero-terminated. But the
** z[n] character is guaranteed to be something that does not look
** like the continuation of the number.
*/
@@ -99296,11 +112300,12 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
c = sqlite3DecOrHexToI64(z, &value);
if( (c==3 && !negFlag) || (c==2) || (negFlag && value==SMALLEST_INT64)){
#ifdef SQLITE_OMIT_FLOATING_POINT
- sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z);
+ sqlite3ErrorMsg(pParse, "oversized integer: %s%#T", negFlag?"-":"",pExpr);
#else
#ifndef SQLITE_OMIT_HEX_INTEGER
if( sqlite3_strnicmp(z,"0x",2)==0 ){
- sqlite3ErrorMsg(pParse, "hex literal too big: %s%s", negFlag?"-":"",z);
+ sqlite3ErrorMsg(pParse, "hex literal too big: %s%#T",
+ negFlag?"-":"",pExpr);
}else
#endif
{
@@ -99338,38 +112343,92 @@ SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn(
}
}
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+/*
+** Generate code that will compute the value of generated column pCol
+** and store the result in register regOut
+*/
+SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(
+ Parse *pParse, /* Parsing context */
+ Table *pTab, /* Table containing the generated column */
+ Column *pCol, /* The generated column */
+ int regOut /* Put the result in this register */
+){
+ int iAddr;
+ Vdbe *v = pParse->pVdbe;
+ int nErr = pParse->nErr;
+ assert( v!=0 );
+ assert( pParse->iSelfTab!=0 );
+ if( pParse->iSelfTab>0 ){
+ iAddr = sqlite3VdbeAddOp3(v, OP_IfNullRow, pParse->iSelfTab-1, 0, regOut);
+ }else{
+ iAddr = 0;
+ }
+ sqlite3ExprCodeCopy(pParse, sqlite3ColumnExpr(pTab,pCol), regOut);
+ if( pCol->affinity>=SQLITE_AFF_TEXT ){
+ sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1);
+ }
+ if( iAddr ) sqlite3VdbeJumpHere(v, iAddr);
+ if( pParse->nErr>nErr ) pParse->db->errByteOffset = -1;
+}
+#endif /* SQLITE_OMIT_GENERATED_COLUMNS */
+
/*
** Generate code to extract the value of the iCol-th column of a table.
*/
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(
- Vdbe *v, /* The VDBE under construction */
+ Vdbe *v, /* Parsing context */
Table *pTab, /* The table containing the value */
int iTabCur, /* The table cursor. Or the PK cursor for WITHOUT ROWID */
int iCol, /* Index of the column to extract */
int regOut /* Extract the value into this register */
){
- if( pTab==0 ){
- sqlite3VdbeAddOp3(v, OP_Column, iTabCur, iCol, regOut);
- return;
- }
+ Column *pCol;
+ assert( v!=0 );
+ assert( pTab!=0 );
+ assert( iCol!=XN_EXPR );
if( iCol<0 || iCol==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut);
+ VdbeComment((v, "%s.rowid", pTab->zName));
}else{
- int op = IsVirtual(pTab) ? OP_VColumn : OP_Column;
- int x = iCol;
- if( !HasRowid(pTab) && !IsVirtual(pTab) ){
- x = sqlite3ColumnOfIndex(sqlite3PrimaryKeyIndex(pTab), iCol);
+ int op;
+ int x;
+ if( IsVirtual(pTab) ){
+ op = OP_VColumn;
+ x = iCol;
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ }else if( (pCol = &pTab->aCol[iCol])->colFlags & COLFLAG_VIRTUAL ){
+ Parse *pParse = sqlite3VdbeParser(v);
+ if( pCol->colFlags & COLFLAG_BUSY ){
+ sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"",
+ pCol->zCnName);
+ }else{
+ int savedSelfTab = pParse->iSelfTab;
+ pCol->colFlags |= COLFLAG_BUSY;
+ pParse->iSelfTab = iTabCur+1;
+ sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, regOut);
+ pParse->iSelfTab = savedSelfTab;
+ pCol->colFlags &= ~COLFLAG_BUSY;
+ }
+ return;
+#endif
+ }else if( !HasRowid(pTab) ){
+ testcase( iCol!=sqlite3TableColumnToStorage(pTab, iCol) );
+ x = sqlite3TableColumnToIndex(sqlite3PrimaryKeyIndex(pTab), iCol);
+ op = OP_Column;
+ }else{
+ x = sqlite3TableColumnToStorage(pTab,iCol);
+ testcase( x!=iCol );
+ op = OP_Column;
}
sqlite3VdbeAddOp3(v, op, iTabCur, x, regOut);
- }
- if( iCol>=0 ){
sqlite3ColumnDefault(v, pTab, iCol, regOut);
}
}
/*
** Generate code that will extract the iColumn-th column from
-** table pTab and store the column value in register iReg.
+** table pTab and store the column value in register iReg.
**
** There must be an open cursor to pTab in iTable when this routine
** is called. If iColumn<0 then code is generated that extracts the rowid.
@@ -99382,11 +112441,14 @@ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(
int iReg, /* Store results here */
u8 p5 /* P5 value for OP_Column + FLAGS */
){
- Vdbe *v = pParse->pVdbe;
- assert( v!=0 );
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iTable, iColumn, iReg);
+ assert( pParse->pVdbe!=0 );
+ assert( (p5 & (OPFLAG_NOCHNG|OPFLAG_TYPEOFARG|OPFLAG_LENGTHARG))==p5 );
+ assert( IsVirtual(pTab) || (p5 & OPFLAG_NOCHNG)==0 );
+ sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pTab, iTable, iColumn, iReg);
if( p5 ){
- sqlite3VdbeChangeP5(v, p5);
+ VdbeOp *pOp = sqlite3VdbeGetLastOp(pParse->pVdbe);
+ if( pOp->opcode==OP_Column ) pOp->p5 = p5;
+ if( pOp->opcode==OP_VColumn ) pOp->p5 = (p5 & OPFLAG_NOCHNG);
}
return iReg;
}
@@ -99396,7 +112458,6 @@ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(
** over to iTo..iTo+nReg-1.
*/
SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int nReg){
- assert( iFrom>=iTo+nReg || iFrom+nReg<=iTo );
sqlite3VdbeAddOp3(pParse->pVdbe, OP_Move, iFrom, iTo, nReg);
}
@@ -99405,7 +112466,9 @@ SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int n
** register iReg. The caller must ensure that iReg already contains
** the correct value for the expression.
*/
-static void exprToRegister(Expr *p, int iReg){
+static void exprToRegister(Expr *pExpr, int iReg){
+ Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr);
+ if( NEVER(p==0) ) return;
p->op2 = p->op;
p->op = TK_REGISTER;
p->iTable = iReg;
@@ -99414,7 +112477,7 @@ static void exprToRegister(Expr *p, int iReg){
/*
** Evaluate an expression (either a vector or a scalar expression) and store
-** the result in continguous temporary registers. Return the index of
+** the result in contiguous temporary registers. Return the index of
** the first register used to store the result.
**
** If the returned result register is a temporary scalar, then also write
@@ -99433,12 +112496,13 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){
#if SQLITE_OMIT_SUBQUERY
iResult = 0;
#else
- iResult = sqlite3CodeSubselect(pParse, p, 0, 0);
+ iResult = sqlite3CodeSubselect(pParse, p);
#endif
}else{
int i;
iResult = pParse->nMem+1;
pParse->nMem += nResult;
+ assert( ExprUseXList(p) );
for(i=0; i<nResult; i++){
sqlite3ExprCodeFactorable(pParse, p->x.pList->a[i].pExpr, i+iResult);
}
@@ -99447,6 +112511,230 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){
return iResult;
}
+/*
+** If the last opcode is a OP_Copy, then set the do-not-merge flag (p5)
+** so that a subsequent copy will not be merged into this one.
+*/
+static void setDoNotMergeFlagOnCopy(Vdbe *v){
+ if( sqlite3VdbeGetLastOp(v)->opcode==OP_Copy ){
+ sqlite3VdbeChangeP5(v, 1); /* Tag trailing OP_Copy as not mergeable */
+ }
+}
+
+/*
+** Generate code to implement special SQL functions that are implemented
+** in-line rather than by using the usual callbacks.
+*/
+static int exprCodeInlineFunction(
+ Parse *pParse, /* Parsing context */
+ ExprList *pFarg, /* List of function arguments */
+ int iFuncId, /* Function ID. One of the INTFUNC_... values */
+ int target /* Store function result in this register */
+){
+ int nFarg;
+ Vdbe *v = pParse->pVdbe;
+ assert( v!=0 );
+ assert( pFarg!=0 );
+ nFarg = pFarg->nExpr;
+ assert( nFarg>0 ); /* All in-line functions have at least one argument */
+ switch( iFuncId ){
+ case INLINEFUNC_coalesce: {
+ /* Attempt a direct implementation of the built-in COALESCE() and
+ ** IFNULL() functions. This avoids unnecessary evaluation of
+ ** arguments past the first non-NULL argument.
+ */
+ int endCoalesce = sqlite3VdbeMakeLabel(pParse);
+ int i;
+ assert( nFarg>=2 );
+ sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target);
+ for(i=1; i<nFarg; i++){
+ sqlite3VdbeAddOp2(v, OP_NotNull, target, endCoalesce);
+ VdbeCoverage(v);
+ sqlite3ExprCode(pParse, pFarg->a[i].pExpr, target);
+ }
+ setDoNotMergeFlagOnCopy(v);
+ sqlite3VdbeResolveLabel(v, endCoalesce);
+ break;
+ }
+ case INLINEFUNC_iif: {
+ Expr caseExpr;
+ memset(&caseExpr, 0, sizeof(caseExpr));
+ caseExpr.op = TK_CASE;
+ caseExpr.x.pList = pFarg;
+ return sqlite3ExprCodeTarget(pParse, &caseExpr, target);
+ }
+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
+ case INLINEFUNC_sqlite_offset: {
+ Expr *pArg = pFarg->a[0].pExpr;
+ if( pArg->op==TK_COLUMN && pArg->iTable>=0 ){
+ sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Null, 0, target);
+ }
+ break;
+ }
+#endif
+ default: {
+ /* The UNLIKELY() function is a no-op. The result is the value
+ ** of the first argument.
+ */
+ assert( nFarg==1 || nFarg==2 );
+ target = sqlite3ExprCodeTarget(pParse, pFarg->a[0].pExpr, target);
+ break;
+ }
+
+ /***********************************************************************
+ ** Test-only SQL functions that are only usable if enabled
+ ** via SQLITE_TESTCTRL_INTERNAL_FUNCTIONS
+ */
+#if !defined(SQLITE_UNTESTABLE)
+ case INLINEFUNC_expr_compare: {
+ /* Compare two expressions using sqlite3ExprCompare() */
+ assert( nFarg==2 );
+ sqlite3VdbeAddOp2(v, OP_Integer,
+ sqlite3ExprCompare(0,pFarg->a[0].pExpr, pFarg->a[1].pExpr,-1),
+ target);
+ break;
+ }
+
+ case INLINEFUNC_expr_implies_expr: {
+ /* Compare two expressions using sqlite3ExprImpliesExpr() */
+ assert( nFarg==2 );
+ sqlite3VdbeAddOp2(v, OP_Integer,
+ sqlite3ExprImpliesExpr(pParse,pFarg->a[0].pExpr, pFarg->a[1].pExpr,-1),
+ target);
+ break;
+ }
+
+ case INLINEFUNC_implies_nonnull_row: {
+ /* Result of sqlite3ExprImpliesNonNullRow() */
+ Expr *pA1;
+ assert( nFarg==2 );
+ pA1 = pFarg->a[1].pExpr;
+ if( pA1->op==TK_COLUMN ){
+ sqlite3VdbeAddOp2(v, OP_Integer,
+ sqlite3ExprImpliesNonNullRow(pFarg->a[0].pExpr,pA1->iTable,1),
+ target);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Null, 0, target);
+ }
+ break;
+ }
+
+ case INLINEFUNC_affinity: {
+ /* The AFFINITY() function evaluates to a string that describes
+ ** the type affinity of the argument. This is used for testing of
+ ** the SQLite type logic.
+ */
+ const char *azAff[] = { "blob", "text", "numeric", "integer",
+ "real", "flexnum" };
+ char aff;
+ assert( nFarg==1 );
+ aff = sqlite3ExprAffinity(pFarg->a[0].pExpr);
+ assert( aff<=SQLITE_AFF_NONE
+ || (aff>=SQLITE_AFF_BLOB && aff<=SQLITE_AFF_FLEXNUM) );
+ sqlite3VdbeLoadString(v, target,
+ (aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]);
+ break;
+ }
+#endif /* !defined(SQLITE_UNTESTABLE) */
+ }
+ return target;
+}
+
+/*
+** Check to see if pExpr is one of the indexed expressions on pParse->pIdxEpr.
+** If it is, then resolve the expression by reading from the index and
+** return the register into which the value has been read. If pExpr is
+** not an indexed expression, then return negative.
+*/
+static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
+ Parse *pParse, /* The parsing context */
+ Expr *pExpr, /* The expression to potentially bypass */
+ int target /* Where to store the result of the expression */
+){
+ IndexedExpr *p;
+ Vdbe *v;
+ for(p=pParse->pIdxEpr; p; p=p->pIENext){
+ u8 exprAff;
+ int iDataCur = p->iDataCur;
+ if( iDataCur<0 ) continue;
+ if( pParse->iSelfTab ){
+ if( p->iDataCur!=pParse->iSelfTab-1 ) continue;
+ iDataCur = -1;
+ }
+ if( sqlite3ExprCompare(0, pExpr, p->pExpr, iDataCur)!=0 ) continue;
+ assert( p->aff>=SQLITE_AFF_BLOB && p->aff<=SQLITE_AFF_NUMERIC );
+ exprAff = sqlite3ExprAffinity(pExpr);
+ if( (exprAff<=SQLITE_AFF_BLOB && p->aff!=SQLITE_AFF_BLOB)
+ || (exprAff==SQLITE_AFF_TEXT && p->aff!=SQLITE_AFF_TEXT)
+ || (exprAff>=SQLITE_AFF_NUMERIC && p->aff!=SQLITE_AFF_NUMERIC)
+ ){
+ /* Affinity mismatch on a generated column */
+ continue;
+ }
+
+ v = pParse->pVdbe;
+ assert( v!=0 );
+ if( p->bMaybeNullRow ){
+ /* If the index is on a NULL row due to an outer join, then we
+ ** cannot extract the value from the index. The value must be
+ ** computed using the original expression. */
+ int addr = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp3(v, OP_IfNullRow, p->iIdxCur, addr+3, target);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target);
+ VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol));
+ sqlite3VdbeGoto(v, 0);
+ p = pParse->pIdxEpr;
+ pParse->pIdxEpr = 0;
+ sqlite3ExprCode(pParse, pExpr, target);
+ pParse->pIdxEpr = p;
+ sqlite3VdbeJumpHere(v, addr+2);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Column, p->iIdxCur, p->iIdxCol, target);
+ VdbeComment((v, "%s expr-column %d", p->zIdxName, p->iIdxCol));
+ }
+ return target;
+ }
+ return -1; /* Not found */
+}
+
+
+/*
+** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This
+** function checks the Parse.pIdxPartExpr list to see if this column
+** can be replaced with a constant value. If so, it generates code to
+** put the constant value in a register (ideally, but not necessarily,
+** register iTarget) and returns the register number.
+**
+** Or, if the TK_COLUMN cannot be replaced by a constant, zero is
+** returned.
+*/
+static int exprPartidxExprLookup(Parse *pParse, Expr *pExpr, int iTarget){
+ IndexedExpr *p;
+ for(p=pParse->pIdxPartExpr; p; p=p->pIENext){
+ if( pExpr->iColumn==p->iIdxCol && pExpr->iTable==p->iDataCur ){
+ Vdbe *v = pParse->pVdbe;
+ int addr = 0;
+ int ret;
+
+ if( p->bMaybeNullRow ){
+ addr = sqlite3VdbeAddOp1(v, OP_IfNullRow, p->iIdxCur);
+ }
+ ret = sqlite3ExprCodeTarget(pParse, p->pExpr, iTarget);
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_Affinity, ret, 1, 0,
+ (const char*)&p->aff, 1);
+ if( addr ){
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeChangeP3(v, addr, ret);
+ }
+ return ret;
+ }
+ }
+ return 0;
+}
+
/*
** Generate code into the current Vdbe to evaluate the given
@@ -99470,50 +112758,86 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
int p5 = 0;
assert( target>0 && target<=pParse->nMem );
- if( v==0 ){
- assert( pParse->db->mallocFailed );
- return 0;
- }
+ assert( v!=0 );
expr_code_doover:
if( pExpr==0 ){
op = TK_NULL;
+ }else if( pParse->pIdxEpr!=0
+ && !ExprHasProperty(pExpr, EP_Leaf)
+ && (r1 = sqlite3IndexedExprLookup(pParse, pExpr, target))>=0
+ ){
+ return r1;
}else{
+ assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
op = pExpr->op;
}
+ assert( op!=TK_ORDER );
switch( op ){
case TK_AGG_COLUMN: {
AggInfo *pAggInfo = pExpr->pAggInfo;
- struct AggInfo_col *pCol = &pAggInfo->aCol[pExpr->iAgg];
+ struct AggInfo_col *pCol;
+ assert( pAggInfo!=0 );
+ assert( pExpr->iAgg>=0 );
+ if( pExpr->iAgg>=pAggInfo->nColumn ){
+ /* Happens when the left table of a RIGHT JOIN is null and
+ ** is using an expression index */
+ sqlite3VdbeAddOp2(v, OP_Null, 0, target);
+#ifdef SQLITE_VDBE_COVERAGE
+ /* Verify that the OP_Null above is exercised by tests
+ ** tag-20230325-2 */
+ sqlite3VdbeAddOp3(v, OP_NotNull, target, 1, 20230325);
+ VdbeCoverageNeverTaken(v);
+#endif
+ break;
+ }
+ pCol = &pAggInfo->aCol[pExpr->iAgg];
if( !pAggInfo->directMode ){
- assert( pCol->iMem>0 );
- return pCol->iMem;
+ return AggInfoColumnReg(pAggInfo, pExpr->iAgg);
}else if( pAggInfo->useSortingIdx ){
+ Table *pTab = pCol->pTab;
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
pCol->iSorterColumn, target);
+ if( pTab==0 ){
+ /* No comment added */
+ }else if( pCol->iColumn<0 ){
+ VdbeComment((v,"%s.rowid",pTab->zName));
+ }else{
+ VdbeComment((v,"%s.%s",
+ pTab->zName, pTab->aCol[pCol->iColumn].zCnName));
+ if( pTab->aCol[pCol->iColumn].affinity==SQLITE_AFF_REAL ){
+ sqlite3VdbeAddOp1(v, OP_RealAffinity, target);
+ }
+ }
+ return target;
+ }else if( pExpr->y.pTab==0 ){
+ /* This case happens when the argument to an aggregate function
+ ** is rewritten by aggregateConvertIndexedExprRefToColumn() */
+ sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, pExpr->iColumn, target);
return target;
}
/* Otherwise, fall thru into the TK_COLUMN case */
+ /* no break */ deliberate_fall_through
}
case TK_COLUMN: {
int iTab = pExpr->iTable;
+ int iReg;
if( ExprHasProperty(pExpr, EP_FixedCol) ){
/* This COLUMN expression is really a constant due to WHERE clause
** constraints, and that constant is coded by the pExpr->pLeft
- ** expresssion. However, make sure the constant has the correct
+ ** expression. However, make sure the constant has the correct
** datatype by applying the Affinity of the table column to the
** constant.
*/
- int iReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target);
- int aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
- if( aff!=SQLITE_AFF_BLOB ){
- static const char zAff[] = "B\000C\000D\000E";
+ int aff;
+ iReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target);
+ assert( ExprUseYTab(pExpr) );
+ assert( pExpr->y.pTab!=0 );
+ aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
+ if( aff>SQLITE_AFF_BLOB ){
+ static const char zAff[] = "B\000C\000D\000E\000F";
assert( SQLITE_AFF_BLOB=='A' );
assert( SQLITE_AFF_TEXT=='B' );
- if( iReg!=target ){
- sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target);
- iReg = target;
- }
sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0,
&zAff[(aff-'B')*2], P4_STATIC);
}
@@ -99521,17 +112845,66 @@ expr_code_doover:
}
if( iTab<0 ){
if( pParse->iSelfTab<0 ){
- /* Generating CHECK constraints or inserting into partial index */
- return pExpr->iColumn - pParse->iSelfTab;
+ /* Other columns in the same row for CHECK constraints or
+ ** generated columns or for inserting into partial index.
+ ** The row is unpacked into registers beginning at
+ ** 0-(pParse->iSelfTab). The rowid (if any) is in a register
+ ** immediately prior to the first column.
+ */
+ Column *pCol;
+ Table *pTab;
+ int iSrc;
+ int iCol = pExpr->iColumn;
+ assert( ExprUseYTab(pExpr) );
+ pTab = pExpr->y.pTab;
+ assert( pTab!=0 );
+ assert( iCol>=XN_ROWID );
+ assert( iCol<pTab->nCol );
+ if( iCol<0 ){
+ return -1-pParse->iSelfTab;
+ }
+ pCol = pTab->aCol + iCol;
+ testcase( iCol!=sqlite3TableColumnToStorage(pTab,iCol) );
+ iSrc = sqlite3TableColumnToStorage(pTab, iCol) - pParse->iSelfTab;
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ if( pCol->colFlags & COLFLAG_GENERATED ){
+ if( pCol->colFlags & COLFLAG_BUSY ){
+ sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"",
+ pCol->zCnName);
+ return 0;
+ }
+ pCol->colFlags |= COLFLAG_BUSY;
+ if( pCol->colFlags & COLFLAG_NOTAVAIL ){
+ sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, iSrc);
+ }
+ pCol->colFlags &= ~(COLFLAG_BUSY|COLFLAG_NOTAVAIL);
+ return iSrc;
+ }else
+#endif /* SQLITE_OMIT_GENERATED_COLUMNS */
+ if( pCol->affinity==SQLITE_AFF_REAL ){
+ sqlite3VdbeAddOp2(v, OP_SCopy, iSrc, target);
+ sqlite3VdbeAddOp1(v, OP_RealAffinity, target);
+ return target;
+ }else{
+ return iSrc;
+ }
}else{
/* Coding an expression that is part of an index where column names
** in the index refer to the table to which the index belongs */
iTab = pParse->iSelfTab - 1;
}
}
- return sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab,
+ else if( pParse->pIdxPartExpr
+ && 0!=(r1 = exprPartidxExprLookup(pParse, pExpr, target))
+ ){
+ return r1;
+ }
+ assert( ExprUseYTab(pExpr) );
+ assert( pExpr->y.pTab!=0 );
+ iReg = sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab,
pExpr->iColumn, iTab, target,
pExpr->op2);
+ return iReg;
}
case TK_INTEGER: {
codeInteger(pParse, pExpr, 0, target);
@@ -99553,7 +112926,12 @@ expr_code_doover:
sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
return target;
}
- case TK_NULL: {
+ default: {
+ /* Make NULL the default case so that if a bug causes an illegal
+ ** Expr node to be passed into this function, it will be handled
+ ** sanely and not crash. But keep the assert() to bring the problem
+ ** to the attention of the developers. */
+ assert( op==TK_NULL || op==TK_ERROR || pParse->db->mallocFailed );
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
return target;
}
@@ -99580,7 +112958,7 @@ expr_code_doover:
sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target);
if( pExpr->u.zToken[1]!=0 ){
const char *z = sqlite3VListNumToName(pParse->pVList, pExpr->iColumn);
- assert( pExpr->u.zToken[0]=='?' || strcmp(pExpr->u.zToken, z)==0 );
+ assert( pExpr->u.zToken[0]=='?' || (z && !strcmp(pExpr->u.zToken, z)) );
pParse->pVList[0] = 0; /* Indicate VList may no longer be enlarged */
sqlite3VdbeAppendP4(v, (char*)z, P4_STATIC);
}
@@ -99592,11 +112970,9 @@ expr_code_doover:
#ifndef SQLITE_OMIT_CAST
case TK_CAST: {
/* Expressions of the form: CAST(pLeft AS token) */
- inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
- if( inReg!=target ){
- sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target);
- inReg = target;
- }
+ sqlite3ExprCode(pParse, pExpr->pLeft, target);
+ assert( inReg==target );
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3VdbeAddOp2(v, OP_Cast, target,
sqlite3AffinityType(pExpr->u.zToken, 0));
return inReg;
@@ -99619,14 +112995,21 @@ expr_code_doover:
}else{
r1 = sqlite3ExprCodeTemp(pParse, pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
- codeCompare(pParse, pLeft, pExpr->pRight, op,
- r1, r2, inReg, SQLITE_STOREP2 | p5);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, inReg);
+ codeCompare(pParse, pLeft, pExpr->pRight, op, r1, r2,
+ sqlite3VdbeCurrentAddr(v)+2, p5,
+ ExprHasProperty(pExpr,EP_Commuted));
assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
+ if( p5==SQLITE_NULLEQ ){
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, inReg);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_ZeroOrNull, r1, inReg, r2);
+ }
testcase( regFree1==0 );
testcase( regFree2==0 );
}
@@ -99642,7 +113025,7 @@ expr_code_doover:
case TK_BITOR:
case TK_SLASH:
case TK_LSHIFT:
- case TK_RSHIFT:
+ case TK_RSHIFT:
case TK_CONCAT: {
assert( TK_AND==OP_And ); testcase( op==TK_AND );
assert( TK_OR==OP_Or ); testcase( op==TK_OR );
@@ -99678,6 +113061,7 @@ expr_code_doover:
tempX.op = TK_INTEGER;
tempX.flags = EP_IntValue|EP_TokenOnly;
tempX.u.iValue = 0;
+ ExprClearVVAProperties(&tempX);
r1 = sqlite3ExprCodeTemp(pParse, &tempX, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree2);
sqlite3VdbeAddOp3(v, OP_Subtract, r2, r1, target);
@@ -99723,11 +113107,14 @@ expr_code_doover:
}
case TK_AGG_FUNCTION: {
AggInfo *pInfo = pExpr->pAggInfo;
- if( pInfo==0 ){
+ if( pInfo==0
+ || NEVER(pExpr->iAgg<0)
+ || NEVER(pExpr->iAgg>=pInfo->nFunc)
+ ){
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- sqlite3ErrorMsg(pParse, "misuse of aggregate: %s()", pExpr->u.zToken);
+ sqlite3ErrorMsg(pParse, "misuse of aggregate: %#T()", pExpr);
}else{
- return pInfo->aFunc[pExpr->iAgg].iMem;
+ return AggInfoFuncReg(pInfo, pExpr->iAgg);
}
break;
}
@@ -99749,16 +113136,13 @@ expr_code_doover:
#endif
if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){
- /* SQL functions can be expensive. So try to move constant functions
- ** out of the inner loop, even if that means an extra OP_Copy. */
- return sqlite3ExprCodeAtInit(pParse, pExpr, -1);
- }
- assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- if( ExprHasProperty(pExpr, EP_TokenOnly) ){
- pFarg = 0;
- }else{
- pFarg = pExpr->x.pList;
+ /* SQL functions can be expensive. So try to avoid running them
+ ** multiple times if we know they always give the same result */
+ return sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
}
+ assert( !ExprHasProperty(pExpr, EP_TokenOnly) );
+ assert( ExprUseXList(pExpr) );
+ pFarg = pExpr->x.pList;
nFarg = pFarg ? pFarg->nExpr : 0;
assert( !ExprHasProperty(pExpr, EP_IntValue) );
zId = pExpr->u.zToken;
@@ -99769,51 +113153,18 @@ expr_code_doover:
}
#endif
if( pDef==0 || pDef->xFinalize!=0 ){
- sqlite3ErrorMsg(pParse, "unknown function: %s()", zId);
+ sqlite3ErrorMsg(pParse, "unknown function: %#T()", pExpr);
break;
}
-
- /* Attempt a direct implementation of the built-in COALESCE() and
- ** IFNULL() functions. This avoids unnecessary evaluation of
- ** arguments past the first non-NULL argument.
- */
- if( pDef->funcFlags & SQLITE_FUNC_COALESCE ){
- int endCoalesce = sqlite3VdbeMakeLabel(v);
- assert( nFarg>=2 );
- sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target);
- for(i=1; i<nFarg; i++){
- sqlite3VdbeAddOp2(v, OP_NotNull, target, endCoalesce);
- VdbeCoverage(v);
- sqlite3ExprCode(pParse, pFarg->a[i].pExpr, target);
- }
- sqlite3VdbeResolveLabel(v, endCoalesce);
- break;
+ if( (pDef->funcFlags & SQLITE_FUNC_INLINE)!=0 && ALWAYS(pFarg!=0) ){
+ assert( (pDef->funcFlags & SQLITE_FUNC_UNSAFE)==0 );
+ assert( (pDef->funcFlags & SQLITE_FUNC_DIRECT)==0 );
+ return exprCodeInlineFunction(pParse, pFarg,
+ SQLITE_PTR_TO_INT(pDef->pUserData), target);
+ }else if( pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE) ){
+ sqlite3ExprFunctionUsable(pParse, pExpr, pDef);
}
- /* The UNLIKELY() function is a no-op. The result is the value
- ** of the first argument.
- */
- if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
- assert( nFarg>=1 );
- return sqlite3ExprCodeTarget(pParse, pFarg->a[0].pExpr, target);
- }
-
-#ifdef SQLITE_DEBUG
- /* The AFFINITY() function evaluates to a string that describes
- ** the type affinity of the argument. This is used for testing of
- ** the SQLite type logic.
- */
- if( pDef->funcFlags & SQLITE_FUNC_AFFINITY ){
- const char *azAff[] = { "blob", "text", "numeric", "integer", "real" };
- char aff;
- assert( nFarg==1 );
- aff = sqlite3ExprAffinity(pFarg->a[0].pExpr);
- sqlite3VdbeLoadString(v, target,
- aff ? azAff[aff-SQLITE_AFF_BLOB] : "none");
- return target;
- }
-#endif
-
for(i=0; i<nFarg; i++){
if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){
testcase( i==31 );
@@ -99831,10 +113182,10 @@ expr_code_doover:
r1 = sqlite3GetTempRange(pParse, nFarg);
}
- /* For length() and typeof() functions with a column argument,
+ /* For length() and typeof() and octet_length() functions,
** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG
- ** or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data
- ** loading.
+ ** or OPFLAG_TYPEOFARG or OPFLAG_BYTELENARG respectively, to avoid
+ ** unnecessary data loading.
*/
if( (pDef->funcFlags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){
u8 exprOp;
@@ -99844,14 +113195,16 @@ expr_code_doover:
if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){
assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG );
assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG );
- testcase( pDef->funcFlags & OPFLAG_LENGTHARG );
- pFarg->a[0].pExpr->op2 =
- pDef->funcFlags & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG);
+ assert( SQLITE_FUNC_BYTELEN==OPFLAG_BYTELENARG );
+ assert( (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG)==OPFLAG_BYTELENARG );
+ testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_LENGTHARG );
+ testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_TYPEOFARG );
+ testcase( (pDef->funcFlags & OPFLAG_BYTELENARG)==OPFLAG_BYTELENARG);
+ pFarg->a[0].pExpr->op2 = pDef->funcFlags & OPFLAG_BYTELENARG;
}
}
- sqlite3ExprCodeExprList(pParse, pFarg, r1, 0,
- SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR);
+ sqlite3ExprCodeExprList(pParse, pFarg, r1, 0, SQLITE_ECEL_FACTOR);
}else{
r1 = 0;
}
@@ -99864,7 +113217,7 @@ expr_code_doover:
** see if it is a column in a virtual table. This is done because
** the left operand of infix functions (the operand we want to
** control overloading) ends up as the second argument to the
- ** function. The expression "A glob B" is equivalent to
+ ** function. The expression "A glob B" is equivalent to
** "glob(B,A). We want to use the A in "A glob B" to test
** for function overloading. But we use the B term in "glob(B,A)".
*/
@@ -99875,26 +113228,17 @@ expr_code_doover:
}
#endif
if( pDef->funcFlags & SQLITE_FUNC_NEEDCOLL ){
- if( !pColl ) pColl = db->pDfltColl;
+ if( !pColl ) pColl = db->pDfltColl;
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
}
-#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
- if( pDef->funcFlags & SQLITE_FUNC_OFFSET ){
- Expr *pArg = pFarg->a[0].pExpr;
- if( pArg->op==TK_COLUMN ){
- sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target);
+ sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg,
+ pDef, pExpr->op2);
+ if( nFarg ){
+ if( constMask==0 ){
+ sqlite3ReleaseTempRange(pParse, r1, nFarg);
}else{
- sqlite3VdbeAddOp2(v, OP_Null, 0, target);
+ sqlite3VdbeReleaseRegisters(pParse, r1, nFarg, constMask, 1);
}
- }else
-#endif
- {
- sqlite3VdbeAddOp4(v, pParse->iSelfTab ? OP_PureFunc0 : OP_Function0,
- constMask, r1, target, (char*)pDef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, (u8)nFarg);
- }
- if( nFarg && constMask==0 ){
- sqlite3ReleaseTempRange(pParse, r1, nFarg);
}
return target;
}
@@ -99904,30 +113248,36 @@ expr_code_doover:
int nCol;
testcase( op==TK_EXISTS );
testcase( op==TK_SELECT );
- if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){
+ if( pParse->db->mallocFailed ){
+ return 0;
+ }else if( op==TK_SELECT
+ && ALWAYS( ExprUseXSelect(pExpr) )
+ && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1
+ ){
sqlite3SubselectError(pParse, nCol, 1);
}else{
- return sqlite3CodeSubselect(pParse, pExpr, 0, 0);
+ return sqlite3CodeSubselect(pParse, pExpr);
}
break;
}
case TK_SELECT_COLUMN: {
int n;
- if( pExpr->pLeft->iTable==0 ){
- pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft, 0, 0);
+ Expr *pLeft = pExpr->pLeft;
+ if( pLeft->iTable==0 || pParse->withinRJSubrtn > pLeft->op2 ){
+ pLeft->iTable = sqlite3CodeSubselect(pParse, pLeft);
+ pLeft->op2 = pParse->withinRJSubrtn;
}
- assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT );
- if( pExpr->iTable
- && pExpr->iTable!=(n = sqlite3ExprVectorSize(pExpr->pLeft))
- ){
+ assert( pLeft->op==TK_SELECT || pLeft->op==TK_ERROR );
+ n = sqlite3ExprVectorSize(pLeft);
+ if( pExpr->iTable!=n ){
sqlite3ErrorMsg(pParse, "%d columns assigned %d values",
pExpr->iTable, n);
}
- return pExpr->pLeft->iTable + pExpr->iColumn;
+ return pLeft->iTable + pExpr->iColumn;
}
case TK_IN: {
- int destIfFalse = sqlite3VdbeMakeLabel(v);
- int destIfNull = sqlite3VdbeMakeLabel(v);
+ int destIfFalse = sqlite3VdbeMakeLabel(pParse);
+ int destIfNull = sqlite3VdbeMakeLabel(pParse);
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull);
sqlite3VdbeAddOp2(v, OP_Integer, 1, target);
@@ -99954,8 +113304,23 @@ expr_code_doover:
exprCodeBetween(pParse, pExpr, target, 0, 0);
return target;
}
+ case TK_COLLATE: {
+ if( !ExprHasProperty(pExpr, EP_Collate) ){
+ /* A TK_COLLATE Expr node without the EP_Collate tag is a so-called
+ ** "SOFT-COLLATE" that is added to constraints that are pushed down
+ ** from outer queries into sub-queries by the push-down optimization.
+ ** Clear subtypes as subtypes may not cross a subquery boundary.
+ */
+ assert( pExpr->pLeft );
+ sqlite3ExprCode(pParse, pExpr->pLeft, target);
+ sqlite3VdbeAddOp1(v, OP_ClrSubtype, target);
+ return target;
+ }else{
+ pExpr = pExpr->pLeft;
+ goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. */
+ }
+ }
case TK_SPAN:
- case TK_COLLATE:
case TK_UPLUS: {
pExpr = pExpr->pLeft;
goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. OSSFuzz. */
@@ -99971,7 +113336,7 @@ expr_code_doover:
**
** The expression is implemented using an OP_Param opcode. The p1
** parameter is set to 0 for an old.rowid reference, or to (i+1)
- ** to reference another column of the old.* pseudo-table, where
+ ** to reference another column of the old.* pseudo-table, where
** i is the index of the column. For a new.rowid reference, p1 is
** set to (n+1), where n is the number of columns in each pseudo-table.
** For a reference to any other column in the new.* pseudo-table, p1
@@ -99985,20 +113350,27 @@ expr_code_doover:
**
** p1==0 -> old.rowid p1==3 -> new.rowid
** p1==1 -> old.a p1==4 -> new.a
- ** p1==2 -> old.b p1==5 -> new.b
+ ** p1==2 -> old.b p1==5 -> new.b
*/
- Table *pTab = pExpr->y.pTab;
- int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn;
+ Table *pTab;
+ int iCol;
+ int p1;
+
+ assert( ExprUseYTab(pExpr) );
+ pTab = pExpr->y.pTab;
+ iCol = pExpr->iColumn;
+ p1 = pExpr->iTable * (pTab->nCol+1) + 1
+ + sqlite3TableColumnToStorage(pTab, iCol);
assert( pExpr->iTable==0 || pExpr->iTable==1 );
- assert( pExpr->iColumn>=-1 && pExpr->iColumn<pTab->nCol );
- assert( pTab->iPKey<0 || pExpr->iColumn!=pTab->iPKey );
+ assert( iCol>=-1 && iCol<pTab->nCol );
+ assert( pTab->iPKey<0 || iCol!=pTab->iPKey );
assert( p1>=0 && p1<(pTab->nCol*2+2) );
sqlite3VdbeAddOp2(v, OP_Param, p1, target);
VdbeComment((v, "r[%d]=%s.%s", target,
(pExpr->iTable ? "new" : "old"),
- (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[pExpr->iColumn].zName)
+ (pExpr->iColumn<0 ? "rowid" : pExpr->y.pTab->aCol[iCol].zCnName)
));
#ifndef SQLITE_OMIT_FLOATING_POINT
@@ -100007,9 +113379,7 @@ expr_code_doover:
**
** EVIDENCE-OF: R-60985-57662 SQLite will convert the value back to
** floating point when extracting it from the record. */
- if( pExpr->iColumn>=0
- && pTab->aCol[pExpr->iColumn].affinity==SQLITE_AFF_REAL
- ){
+ if( iCol>=0 && pTab->aCol[iCol].affinity==SQLITE_AFF_REAL ){
sqlite3VdbeAddOp1(v, OP_RealAffinity, target);
}
#endif
@@ -100021,12 +113391,43 @@ expr_code_doover:
break;
}
+ /* TK_IF_NULL_ROW Expr nodes are inserted ahead of expressions
+ ** that derive from the right-hand table of a LEFT JOIN. The
+ ** Expr.iTable value is the table number for the right-hand table.
+ ** The expression is only evaluated if that table is not currently
+ ** on a LEFT JOIN NULL row.
+ */
case TK_IF_NULL_ROW: {
int addrINR;
- addrINR = sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable);
- inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
+ u8 okConstFactor = pParse->okConstFactor;
+ AggInfo *pAggInfo = pExpr->pAggInfo;
+ if( pAggInfo ){
+ assert( pExpr->iAgg>=0 && pExpr->iAgg<pAggInfo->nColumn );
+ if( !pAggInfo->directMode ){
+ inReg = AggInfoColumnReg(pAggInfo, pExpr->iAgg);
+ break;
+ }
+ if( pExpr->pAggInfo->useSortingIdx ){
+ sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
+ pAggInfo->aCol[pExpr->iAgg].iSorterColumn,
+ target);
+ inReg = target;
+ break;
+ }
+ }
+ addrINR = sqlite3VdbeAddOp3(v, OP_IfNullRow, pExpr->iTable, 0, target);
+ /* The OP_IfNullRow opcode above can overwrite the result register with
+ ** NULL. So we have to ensure that the result register is not a value
+ ** that is suppose to be a constant. Two defenses are needed:
+ ** (1) Temporarily disable factoring of constant expressions
+ ** (2) Make sure the computed value really is stored in register
+ ** "target" and not someplace else.
+ */
+ pParse->okConstFactor = 0; /* note (1) above */
+ sqlite3ExprCode(pParse, pExpr->pLeft, target);
+ assert( target==inReg );
+ pParse->okConstFactor = okConstFactor;
sqlite3VdbeJumpHere(v, addrINR);
- sqlite3VdbeChangeP3(v, addrINR, inReg);
break;
}
@@ -100051,7 +113452,7 @@ expr_code_doover:
** or if there is no matching Ei, the ELSE term Y, or if there is
** no ELSE term, NULL.
*/
- default: assert( op==TK_CASE ); {
+ case TK_CASE: {
int endLabel; /* GOTO label for end of CASE stmt */
int nextCase; /* GOTO label for next WHEN clause */
int nExpr; /* 2x number of WHEN terms */
@@ -100061,21 +113462,27 @@ expr_code_doover:
Expr opCompare; /* The X==Ei expression */
Expr *pX; /* The X expression */
Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */
+ Expr *pDel = 0;
+ sqlite3 *db = pParse->db;
- assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList );
+ assert( ExprUseXList(pExpr) && pExpr->x.pList!=0 );
assert(pExpr->x.pList->nExpr > 0);
pEList = pExpr->x.pList;
aListelem = pEList->a;
nExpr = pEList->nExpr;
- endLabel = sqlite3VdbeMakeLabel(v);
+ endLabel = sqlite3VdbeMakeLabel(pParse);
if( (pX = pExpr->pLeft)!=0 ){
- tempX = *pX;
+ pDel = sqlite3ExprDup(db, pX, 0);
+ if( db->mallocFailed ){
+ sqlite3ExprDelete(db, pDel);
+ break;
+ }
testcase( pX->op==TK_COLUMN );
- exprToRegister(&tempX, exprCodeVector(pParse, &tempX, &regFree1));
+ exprToRegister(pDel, exprCodeVector(pParse, pDel, &regFree1));
testcase( regFree1==0 );
memset(&opCompare, 0, sizeof(opCompare));
opCompare.op = TK_EQ;
- opCompare.pLeft = &tempX;
+ opCompare.pLeft = pDel;
pTest = &opCompare;
/* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001:
** The value in regFree1 might get SCopy-ed into the file result.
@@ -100090,7 +113497,7 @@ expr_code_doover:
}else{
pTest = aListelem[i].pExpr;
}
- nextCase = sqlite3VdbeMakeLabel(v);
+ nextCase = sqlite3VdbeMakeLabel(pParse);
testcase( pTest->op==TK_COLUMN );
sqlite3ExprIfFalse(pParse, pTest, nextCase, SQLITE_JUMPIFNULL);
testcase( aListelem[i+1].pExpr->op==TK_COLUMN );
@@ -100103,32 +113510,35 @@ expr_code_doover:
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
+ sqlite3ExprDelete(db, pDel);
+ setDoNotMergeFlagOnCopy(v);
sqlite3VdbeResolveLabel(v, endLabel);
break;
}
#ifndef SQLITE_OMIT_TRIGGER
case TK_RAISE: {
- assert( pExpr->affinity==OE_Rollback
- || pExpr->affinity==OE_Abort
- || pExpr->affinity==OE_Fail
- || pExpr->affinity==OE_Ignore
+ assert( pExpr->affExpr==OE_Rollback
+ || pExpr->affExpr==OE_Abort
+ || pExpr->affExpr==OE_Fail
+ || pExpr->affExpr==OE_Ignore
);
- if( !pParse->pTriggerTab ){
+ if( !pParse->pTriggerTab && !pParse->nested ){
sqlite3ErrorMsg(pParse,
"RAISE() may only be used within a trigger-program");
return 0;
}
- if( pExpr->affinity==OE_Abort ){
+ if( pExpr->affExpr==OE_Abort ){
sqlite3MayAbort(pParse);
}
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- if( pExpr->affinity==OE_Ignore ){
+ if( pExpr->affExpr==OE_Ignore ){
sqlite3VdbeAddOp4(
v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
VdbeCoverage(v);
}else{
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER,
- pExpr->affinity, pExpr->u.zToken, 0, 0);
+ sqlite3HaltConstraint(pParse,
+ pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR,
+ pExpr->affExpr, pExpr->u.zToken, 0, 0);
}
break;
@@ -100141,40 +113551,66 @@ expr_code_doover:
}
/*
-** Factor out the code of the given expression to initialization time.
+** Generate code that will evaluate expression pExpr just one time
+** per prepared statement execution.
**
-** If regDest>=0 then the result is always stored in that register and the
-** result is not reusable. If regDest<0 then this routine is free to
-** store the value whereever it wants. The register where the expression
-** is stored is returned. When regDest<0, two identical expressions will
-** code to the same register.
+** If the expression uses functions (that might throw an exception) then
+** guard them with an OP_Once opcode to ensure that the code is only executed
+** once. If no functions are involved, then factor the code out and put it at
+** the end of the prepared statement in the initialization section.
+**
+** If regDest>0 then the result is always stored in that register and the
+** result is not reusable. If regDest<0 then this routine is free to
+** store the value wherever it wants. The register where the expression
+** is stored is returned. When regDest<0, two identical expressions might
+** code to the same register, if they do not contain function calls and hence
+** are factored out into the initialization section at the end of the
+** prepared statement.
*/
-SQLITE_PRIVATE int sqlite3ExprCodeAtInit(
+SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(
Parse *pParse, /* Parsing context */
Expr *pExpr, /* The expression to code when the VDBE initializes */
int regDest /* Store the value in this register */
){
ExprList *p;
assert( ConstFactorOk(pParse) );
+ assert( regDest!=0 );
p = pParse->pConstExpr;
if( regDest<0 && p ){
struct ExprList_item *pItem;
int i;
for(pItem=p->a, i=p->nExpr; i>0; pItem++, i--){
- if( pItem->reusable && sqlite3ExprCompare(0,pItem->pExpr,pExpr,-1)==0 ){
+ if( pItem->fg.reusable
+ && sqlite3ExprCompare(0,pItem->pExpr,pExpr,-1)==0
+ ){
return pItem->u.iConstExprReg;
}
}
}
pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
- p = sqlite3ExprListAppend(pParse, p, pExpr);
- if( p ){
- struct ExprList_item *pItem = &p->a[p->nExpr-1];
- pItem->reusable = regDest<0;
- if( regDest<0 ) regDest = ++pParse->nMem;
- pItem->u.iConstExprReg = regDest;
+ if( pExpr!=0 && ExprHasProperty(pExpr, EP_HasFunc) ){
+ Vdbe *v = pParse->pVdbe;
+ int addr;
+ assert( v );
+ addr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+ pParse->okConstFactor = 0;
+ if( !pParse->db->mallocFailed ){
+ if( regDest<0 ) regDest = ++pParse->nMem;
+ sqlite3ExprCode(pParse, pExpr, regDest);
+ }
+ pParse->okConstFactor = 1;
+ sqlite3ExprDelete(pParse->db, pExpr);
+ sqlite3VdbeJumpHere(v, addr);
+ }else{
+ p = sqlite3ExprListAppend(pParse, p, pExpr);
+ if( p ){
+ struct ExprList_item *pItem = &p->a[p->nExpr-1];
+ pItem->fg.reusable = regDest<0;
+ if( regDest<0 ) regDest = ++pParse->nMem;
+ pItem->u.iConstExprReg = regDest;
+ }
+ pParse->pConstExpr = p;
}
- pParse->pConstExpr = p;
return regDest;
}
@@ -100193,13 +113629,14 @@ SQLITE_PRIVATE int sqlite3ExprCodeAtInit(
*/
SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
int r2;
- pExpr = sqlite3ExprSkipCollate(pExpr);
+ pExpr = sqlite3ExprSkipCollateAndLikely(pExpr);
if( ConstFactorOk(pParse)
+ && ALWAYS(pExpr!=0)
&& pExpr->op!=TK_REGISTER
&& sqlite3ExprIsConstantNotJoin(pExpr)
){
*pReg = 0;
- r2 = sqlite3ExprCodeAtInit(pParse, pExpr, -1);
+ r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
}else{
int r1 = sqlite3GetTempReg(pParse);
r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1);
@@ -100221,15 +113658,23 @@ SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
int inReg;
+ assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) );
assert( target>0 && target<=pParse->nMem );
- if( pExpr && pExpr->op==TK_REGISTER ){
- sqlite3VdbeAddOp2(pParse->pVdbe, OP_Copy, pExpr->iTable, target);
- }else{
- inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
- assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
- if( inReg!=target && pParse->pVdbe ){
- sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target);
+ assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
+ if( pParse->pVdbe==0 ) return;
+ inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
+ if( inReg!=target ){
+ u8 op;
+ Expr *pX = sqlite3ExprSkipCollateAndLikely(pExpr);
+ testcase( pX!=pExpr );
+ if( ALWAYS(pX)
+ && (ExprHasProperty(pX,EP_Subquery) || pX->op==TK_REGISTER)
+ ){
+ op = OP_Copy;
+ }else{
+ op = OP_SCopy;
}
+ sqlite3VdbeAddOp2(pParse->pVdbe, op, inReg, target);
}
}
@@ -100253,37 +113698,13 @@ SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse *pParse, Expr *pExpr, int target){
*/
SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){
if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pExpr) ){
- sqlite3ExprCodeAtInit(pParse, pExpr, target);
+ sqlite3ExprCodeRunJustOnce(pParse, pExpr, target);
}else{
- sqlite3ExprCode(pParse, pExpr, target);
+ sqlite3ExprCodeCopy(pParse, pExpr, target);
}
}
/*
-** Generate code that evaluates the given expression and puts the result
-** in register target.
-**
-** Also make a copy of the expression results into another "cache" register
-** and modify the expression so that the next time it is evaluated,
-** the result is a copy of the cache register.
-**
-** This routine is used for expressions that are used multiple
-** times. They are evaluated once and the results of the expression
-** are reused.
-*/
-SQLITE_PRIVATE void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
- Vdbe *v = pParse->pVdbe;
- int iMem;
-
- assert( target>0 );
- assert( pExpr->op!=TK_REGISTER );
- sqlite3ExprCode(pParse, pExpr, target);
- iMem = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Copy, target, iMem);
- exprToRegister(pExpr, iMem);
-}
-
-/*
** Generate code that pushes the value of every element of the given
** expression list into a sequence of registers beginning at target.
**
@@ -100322,7 +113743,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList(
for(pItem=pList->a, i=0; i<n; i++, pItem++){
Expr *pExpr = pItem->pExpr;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
- if( pItem->bSorterRef ){
+ if( pItem->fg.bSorterRef ){
i--;
n--;
}else
@@ -100337,15 +113758,16 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList(
}else if( (flags & SQLITE_ECEL_FACTOR)!=0
&& sqlite3ExprIsConstantNotJoin(pExpr)
){
- sqlite3ExprCodeAtInit(pParse, pExpr, target+i);
+ sqlite3ExprCodeRunJustOnce(pParse, pExpr, target+i);
}else{
int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
if( inReg!=target+i ){
VdbeOp *pOp;
if( copyOp==OP_Copy
- && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy
+ && (pOp=sqlite3VdbeGetLastOp(v))->opcode==OP_Copy
&& pOp->p1+pOp->p3+1==inReg
&& pOp->p2+pOp->p3+1==target+i
+ && pOp->p5==0 /* The do-not-merge flag must be clear */
){
pOp->p3++;
}else{
@@ -100362,7 +113784,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList(
**
** x BETWEEN y AND z
**
-** The above is equivalent to
+** The above is equivalent to
**
** x>=y AND x<=z
**
@@ -100384,41 +113806,44 @@ static void exprCodeBetween(
void (*xJump)(Parse*,Expr*,int,int), /* Action to take */
int jumpIfNull /* Take the jump if the BETWEEN is NULL */
){
- Expr exprAnd; /* The AND operator in x>=y AND x<=z */
+ Expr exprAnd; /* The AND operator in x>=y AND x<=z */
Expr compLeft; /* The x>=y term */
Expr compRight; /* The x<=z term */
- Expr exprX; /* The x subexpression */
int regFree1 = 0; /* Temporary use register */
-
+ Expr *pDel = 0;
+ sqlite3 *db = pParse->db;
memset(&compLeft, 0, sizeof(Expr));
memset(&compRight, 0, sizeof(Expr));
memset(&exprAnd, 0, sizeof(Expr));
- assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- exprX = *pExpr->pLeft;
- exprAnd.op = TK_AND;
- exprAnd.pLeft = &compLeft;
- exprAnd.pRight = &compRight;
- compLeft.op = TK_GE;
- compLeft.pLeft = &exprX;
- compLeft.pRight = pExpr->x.pList->a[0].pExpr;
- compRight.op = TK_LE;
- compRight.pLeft = &exprX;
- compRight.pRight = pExpr->x.pList->a[1].pExpr;
- exprToRegister(&exprX, exprCodeVector(pParse, &exprX, &regFree1));
- if( xJump ){
- xJump(pParse, &exprAnd, dest, jumpIfNull);
- }else{
- /* Mark the expression is being from the ON or USING clause of a join
- ** so that the sqlite3ExprCodeTarget() routine will not attempt to move
- ** it into the Parse.pConstExpr list. We should use a new bit for this,
- ** for clarity, but we are out of bits in the Expr.flags field so we
- ** have to reuse the EP_FromJoin bit. Bummer. */
- exprX.flags |= EP_FromJoin;
- sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
+ assert( ExprUseXList(pExpr) );
+ pDel = sqlite3ExprDup(db, pExpr->pLeft, 0);
+ if( db->mallocFailed==0 ){
+ exprAnd.op = TK_AND;
+ exprAnd.pLeft = &compLeft;
+ exprAnd.pRight = &compRight;
+ compLeft.op = TK_GE;
+ compLeft.pLeft = pDel;
+ compLeft.pRight = pExpr->x.pList->a[0].pExpr;
+ compRight.op = TK_LE;
+ compRight.pLeft = pDel;
+ compRight.pRight = pExpr->x.pList->a[1].pExpr;
+ exprToRegister(pDel, exprCodeVector(pParse, pDel, &regFree1));
+ if( xJump ){
+ xJump(pParse, &exprAnd, dest, jumpIfNull);
+ }else{
+ /* Mark the expression is being from the ON or USING clause of a join
+ ** so that the sqlite3ExprCodeTarget() routine will not attempt to move
+ ** it into the Parse.pConstExpr list. We should use a new bit for this,
+ ** for clarity, but we are out of bits in the Expr.flags field so we
+ ** have to reuse the EP_OuterON bit. Bummer. */
+ pDel->flags |= EP_OuterON;
+ sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
+ }
+ sqlite3ReleaseTempReg(pParse, regFree1);
}
- sqlite3ReleaseTempReg(pParse, regFree1);
+ sqlite3ExprDelete(db, pDel);
/* Ensure adequate test coverage */
testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 && regFree1==0 );
@@ -100456,20 +113881,26 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 );
if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */
if( NEVER(pExpr==0) ) return; /* No way this can happen */
+ assert( !ExprHasVVAProperty(pExpr, EP_Immutable) );
op = pExpr->op;
switch( op ){
- case TK_AND: {
- int d2 = sqlite3VdbeMakeLabel(v);
- testcase( jumpIfNull==0 );
- sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL);
- sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
- sqlite3VdbeResolveLabel(v, d2);
- break;
- }
+ case TK_AND:
case TK_OR: {
- testcase( jumpIfNull==0 );
- sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
- sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
+ Expr *pAlt = sqlite3ExprSimplifiedAndOr(pExpr);
+ if( pAlt!=pExpr ){
+ sqlite3ExprIfTrue(pParse, pAlt, dest, jumpIfNull);
+ }else if( op==TK_AND ){
+ int d2 = sqlite3VdbeMakeLabel(pParse);
+ testcase( jumpIfNull==0 );
+ sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,
+ jumpIfNull^SQLITE_JUMPIFNULL);
+ sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
+ sqlite3VdbeResolveLabel(v, d2);
+ }else{
+ testcase( jumpIfNull==0 );
+ sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
+ sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
+ }
break;
}
case TK_NOT: {
@@ -100500,7 +113931,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
testcase( op==TK_ISNOT );
op = (op==TK_IS) ? TK_EQ : TK_NE;
jumpIfNull = SQLITE_NULLEQ;
- /* Fall thru */
+ /* no break */ deliberate_fall_through
case TK_LT:
case TK_LE:
case TK_GT:
@@ -100512,7 +113943,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
- r1, r2, dest, jumpIfNull);
+ r1, r2, dest, jumpIfNull, ExprHasProperty(pExpr,EP_Commuted));
assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
@@ -100532,6 +113963,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL );
assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
+ sqlite3VdbeTypeofColumn(v, r1);
sqlite3VdbeAddOp2(v, op, r1, dest);
VdbeCoverageIf(v, op==TK_ISNULL);
VdbeCoverageIf(v, op==TK_NOTNULL);
@@ -100545,7 +113977,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
}
#ifndef SQLITE_OMIT_SUBQUERY
case TK_IN: {
- int destIfFalse = sqlite3VdbeMakeLabel(v);
+ int destIfFalse = sqlite3VdbeMakeLabel(pParse);
int destIfNull = jumpIfNull ? dest : destIfFalse;
sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull);
sqlite3VdbeGoto(v, dest);
@@ -100555,9 +113987,9 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
#endif
default: {
default_expr:
- if( exprAlwaysTrue(pExpr) ){
+ if( ExprAlwaysTrue(pExpr) ){
sqlite3VdbeGoto(v, dest);
- }else if( exprAlwaysFalse(pExpr) ){
+ }else if( ExprAlwaysFalse(pExpr) ){
/* No-op */
}else{
r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
@@ -100570,7 +114002,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
}
}
sqlite3ReleaseTempReg(pParse, regFree1);
- sqlite3ReleaseTempReg(pParse, regFree2);
+ sqlite3ReleaseTempReg(pParse, regFree2);
}
/*
@@ -100592,6 +114024,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 );
if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */
if( pExpr==0 ) return;
+ assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
/* The value of pExpr->op and op are related as follows:
**
@@ -100625,18 +114058,23 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
assert( pExpr->op!=TK_GE || op==OP_Lt );
switch( pExpr->op ){
- case TK_AND: {
- testcase( jumpIfNull==0 );
- sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
- sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
- break;
- }
+ case TK_AND:
case TK_OR: {
- int d2 = sqlite3VdbeMakeLabel(v);
- testcase( jumpIfNull==0 );
- sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL);
- sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
- sqlite3VdbeResolveLabel(v, d2);
+ Expr *pAlt = sqlite3ExprSimplifiedAndOr(pExpr);
+ if( pAlt!=pExpr ){
+ sqlite3ExprIfFalse(pParse, pAlt, dest, jumpIfNull);
+ }else if( pExpr->op==TK_AND ){
+ testcase( jumpIfNull==0 );
+ sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
+ sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
+ }else{
+ int d2 = sqlite3VdbeMakeLabel(pParse);
+ testcase( jumpIfNull==0 );
+ sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2,
+ jumpIfNull^SQLITE_JUMPIFNULL);
+ sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
+ sqlite3VdbeResolveLabel(v, d2);
+ }
break;
}
case TK_NOT: {
@@ -100670,7 +114108,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
testcase( pExpr->op==TK_ISNOT );
op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ;
jumpIfNull = SQLITE_NULLEQ;
- /* Fall thru */
+ /* no break */ deliberate_fall_through
case TK_LT:
case TK_LE:
case TK_GT:
@@ -100682,7 +114120,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
- r1, r2, dest, jumpIfNull);
+ r1, r2, dest, jumpIfNull,ExprHasProperty(pExpr,EP_Commuted));
assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
@@ -100700,6 +114138,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
case TK_ISNULL:
case TK_NOTNULL: {
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
+ sqlite3VdbeTypeofColumn(v, r1);
sqlite3VdbeAddOp2(v, op, r1, dest);
testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL);
testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL);
@@ -100716,7 +114155,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
if( jumpIfNull ){
sqlite3ExprCodeIN(pParse, pExpr, dest, dest);
}else{
- int destIfNull = sqlite3VdbeMakeLabel(v);
+ int destIfNull = sqlite3VdbeMakeLabel(pParse);
sqlite3ExprCodeIN(pParse, pExpr, dest, destIfNull);
sqlite3VdbeResolveLabel(v, destIfNull);
}
@@ -100724,10 +114163,10 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
}
#endif
default: {
- default_expr:
- if( exprAlwaysFalse(pExpr) ){
+ default_expr:
+ if( ExprAlwaysFalse(pExpr) ){
sqlite3VdbeGoto(v, dest);
- }else if( exprAlwaysTrue(pExpr) ){
+ }else if( ExprAlwaysTrue(pExpr) ){
/* no-op */
}else{
r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
@@ -100770,11 +114209,15 @@ SQLITE_PRIVATE void sqlite3ExprIfFalseDup(Parse *pParse, Expr *pExpr, int dest,i
** Otherwise, if the values are not the same or if pExpr is not a simple
** SQL value, zero is returned.
*/
-static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){
+static int exprCompareVariable(
+ const Parse *pParse,
+ const Expr *pVar,
+ const Expr *pExpr
+){
int res = 0;
int iVar;
sqlite3_value *pL, *pR = 0;
-
+
sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR);
if( pR ){
iVar = pVar->iColumn;
@@ -100816,13 +114259,18 @@ static int exprCompareVariable(Parse *pParse, Expr *pVar, Expr *pExpr){
** an incorrect 0 or 1 could lead to a malfunction.
**
** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in
-** pParse->pReprepare can be matched against literals in pB. The
+** pParse->pReprepare can be matched against literals in pB. The
** pParse->pVdbe->expmask bitmask is updated for each variable referenced.
-** If pParse is NULL (the normal case) then any TK_VARIABLE term in
+** If pParse is NULL (the normal case) then any TK_VARIABLE term in
** Argument pParse should normally be NULL. If it is not NULL and pA or
** pB causes a return value of 2.
*/
-SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){
+SQLITE_PRIVATE int sqlite3ExprCompare(
+ const Parse *pParse,
+ const Expr *pA,
+ const Expr *pB,
+ int iTab
+){
u32 combinedFlags;
if( pA==0 || pB==0 ){
return pB==pA ? 0 : 2;
@@ -100837,58 +114285,76 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa
}
return 2;
}
- if( pA->op!=pB->op ){
+ if( pA->op!=pB->op || pA->op==TK_RAISE ){
if( pA->op==TK_COLLATE && sqlite3ExprCompare(pParse, pA->pLeft,pB,iTab)<2 ){
return 1;
}
if( pB->op==TK_COLLATE && sqlite3ExprCompare(pParse, pA,pB->pLeft,iTab)<2 ){
return 1;
}
- return 2;
+ if( pA->op==TK_AGG_COLUMN && pB->op==TK_COLUMN
+ && pB->iTable<0 && pA->iTable==iTab
+ ){
+ /* fall through */
+ }else{
+ return 2;
+ }
}
- if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){
- if( pA->op==TK_FUNCTION ){
+ assert( !ExprHasProperty(pA, EP_IntValue) );
+ assert( !ExprHasProperty(pB, EP_IntValue) );
+ if( pA->u.zToken ){
+ if( pA->op==TK_FUNCTION || pA->op==TK_AGG_FUNCTION ){
if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
#ifndef SQLITE_OMIT_WINDOWFUNC
- /* Justification for the assert():
- ** window functions have p->op==TK_FUNCTION but aggregate functions
- ** have p->op==TK_AGG_FUNCTION. So any comparison between an aggregate
- ** function and a window function should have failed before reaching
- ** this point. And, it is not possible to have a window function and
- ** a scalar function with the same name and number of arguments. So
- ** if we reach this point, either A and B both window functions or
- ** neither are a window functions. */
- assert( ExprHasProperty(pA,EP_WinFunc)==ExprHasProperty(pB,EP_WinFunc) );
+ assert( pA->op==pB->op );
+ if( ExprHasProperty(pA,EP_WinFunc)!=ExprHasProperty(pB,EP_WinFunc) ){
+ return 2;
+ }
if( ExprHasProperty(pA,EP_WinFunc) ){
- if( sqlite3WindowCompare(pParse,pA->y.pWin,pB->y.pWin)!=0 ) return 2;
+ if( sqlite3WindowCompare(pParse, pA->y.pWin, pB->y.pWin, 1)!=0 ){
+ return 2;
+ }
}
#endif
+ }else if( pA->op==TK_NULL ){
+ return 0;
}else if( pA->op==TK_COLLATE ){
if( sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
- }else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
+ }else
+ if( pB->u.zToken!=0
+ && pA->op!=TK_COLUMN
+ && pA->op!=TK_AGG_COLUMN
+ && strcmp(pA->u.zToken,pB->u.zToken)!=0
+ ){
return 2;
}
}
- if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
+ if( (pA->flags & (EP_Distinct|EP_Commuted))
+ != (pB->flags & (EP_Distinct|EP_Commuted)) ) return 2;
if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){
if( combinedFlags & EP_xIsSelect ) return 2;
if( (combinedFlags & EP_FixedCol)==0
&& sqlite3ExprCompare(pParse, pA->pLeft, pB->pLeft, iTab) ) return 2;
if( sqlite3ExprCompare(pParse, pA->pRight, pB->pRight, iTab) ) return 2;
if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2;
- assert( (combinedFlags & EP_Reduced)==0 );
- if( pA->op!=TK_STRING && pA->op!=TK_TRUEFALSE ){
+ if( pA->op!=TK_STRING
+ && pA->op!=TK_TRUEFALSE
+ && ALWAYS((combinedFlags & EP_Reduced)==0)
+ ){
if( pA->iColumn!=pB->iColumn ) return 2;
- if( pA->iTable!=pB->iTable
- && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2;
+ if( pA->op2!=pB->op2 && pA->op==TK_TRUTH ) return 2;
+ if( pA->op!=TK_IN && pA->iTable!=pB->iTable && pA->iTable!=iTab ){
+ return 2;
+ }
}
}
return 0;
}
/*
-** Compare two ExprList objects. Return 0 if they are identical and
-** non-zero if they differ in any way.
+** Compare two ExprList objects. Return 0 if they are identical, 1
+** if they are certainly different, or 2 if it is not possible to
+** determine if they are identical or not.
**
** If any subelement of pB has Expr.iTable==(-1) then it is allowed
** to compare equal to an equivalent element in pA with Expr.iTable==iTab.
@@ -100901,16 +114367,17 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa
** Two NULL pointers are considered to be the same. But a NULL pointer
** always differs from a non-NULL pointer.
*/
-SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){
+SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB, int iTab){
int i;
if( pA==0 && pB==0 ) return 0;
if( pA==0 || pB==0 ) return 1;
if( pA->nExpr!=pB->nExpr ) return 1;
for(i=0; i<pA->nExpr; i++){
+ int res;
Expr *pExprA = pA->a[i].pExpr;
Expr *pExprB = pB->a[i].pExpr;
- if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1;
- if( sqlite3ExprCompare(0, pExprA, pExprB, iTab) ) return 1;
+ if( pA->a[i].fg.sortFlags!=pB->a[i].fg.sortFlags ) return 1;
+ if( (res = sqlite3ExprCompare(0, pExprA, pExprB, iTab)) ) return res;
}
return 0;
}
@@ -100919,7 +114386,7 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){
** Like sqlite3ExprCompare() except COLLATE operators at the top-level
** are ignored.
*/
-SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){
+SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){
return sqlite3ExprCompare(0,
sqlite3ExprSkipCollate(pA),
sqlite3ExprSkipCollate(pB),
@@ -100927,6 +114394,84 @@ SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){
}
/*
+** Return non-zero if Expr p can only be true if pNN is not NULL.
+**
+** Or if seenNot is true, return non-zero if Expr p can only be
+** non-NULL if pNN is not NULL
+*/
+static int exprImpliesNotNull(
+ const Parse *pParse,/* Parsing context */
+ const Expr *p, /* The expression to be checked */
+ const Expr *pNN, /* The expression that is NOT NULL */
+ int iTab, /* Table being evaluated */
+ int seenNot /* Return true only if p can be any non-NULL value */
+){
+ assert( p );
+ assert( pNN );
+ if( sqlite3ExprCompare(pParse, p, pNN, iTab)==0 ){
+ return pNN->op!=TK_NULL;
+ }
+ switch( p->op ){
+ case TK_IN: {
+ if( seenNot && ExprHasProperty(p, EP_xIsSelect) ) return 0;
+ assert( ExprUseXSelect(p) || (p->x.pList!=0 && p->x.pList->nExpr>0) );
+ return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
+ }
+ case TK_BETWEEN: {
+ ExprList *pList;
+ assert( ExprUseXList(p) );
+ pList = p->x.pList;
+ assert( pList!=0 );
+ assert( pList->nExpr==2 );
+ if( seenNot ) return 0;
+ if( exprImpliesNotNull(pParse, pList->a[0].pExpr, pNN, iTab, 1)
+ || exprImpliesNotNull(pParse, pList->a[1].pExpr, pNN, iTab, 1)
+ ){
+ return 1;
+ }
+ return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
+ }
+ case TK_EQ:
+ case TK_NE:
+ case TK_LT:
+ case TK_LE:
+ case TK_GT:
+ case TK_GE:
+ case TK_PLUS:
+ case TK_MINUS:
+ case TK_BITOR:
+ case TK_LSHIFT:
+ case TK_RSHIFT:
+ case TK_CONCAT:
+ seenNot = 1;
+ /* no break */ deliberate_fall_through
+ case TK_STAR:
+ case TK_REM:
+ case TK_BITAND:
+ case TK_SLASH: {
+ if( exprImpliesNotNull(pParse, p->pRight, pNN, iTab, seenNot) ) return 1;
+ /* no break */ deliberate_fall_through
+ }
+ case TK_SPAN:
+ case TK_COLLATE:
+ case TK_UPLUS:
+ case TK_UMINUS: {
+ return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, seenNot);
+ }
+ case TK_TRUTH: {
+ if( seenNot ) return 0;
+ if( p->op2!=TK_IS ) return 0;
+ return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
+ }
+ case TK_BITNOT:
+ case TK_NOT: {
+ return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
+ }
+ }
+ return 0;
+}
+
+/*
** Return true if we can prove the pE2 will always be true if pE1 is
** true. Return false if we cannot complete the proof or if pE2 might
** be false. Examples:
@@ -100937,21 +114482,26 @@ SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){
** pE1: x!=123 pE2: x IS NOT NULL Result: true
** pE1: x!=?1 pE2: x IS NOT NULL Result: true
** pE1: x IS NULL pE2: x IS NOT NULL Result: false
-** pE1: x IS ?2 pE2: x IS NOT NULL Reuslt: false
+** pE1: x IS ?2 pE2: x IS NOT NULL Result: false
**
** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has
** Expr.iTable<0 then assume a table number given by iTab.
**
-** If pParse is not NULL, then the values of bound variables in pE1 are
+** If pParse is not NULL, then the values of bound variables in pE1 are
** compared against literal values in pE2 and pParse->pVdbe->expmask is
-** modified to record which bound variables are referenced. If pParse
+** modified to record which bound variables are referenced. If pParse
** is NULL, then false will be returned if pE1 contains any bound variables.
**
** When in doubt, return false. Returning true might give a performance
** improvement. Returning false might cause a performance reduction, but
** it will always give the correct answer and is hence always safe.
*/
-SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, int iTab){
+SQLITE_PRIVATE int sqlite3ExprImpliesExpr(
+ const Parse *pParse,
+ const Expr *pE1,
+ const Expr *pE2,
+ int iTab
+){
if( sqlite3ExprCompare(pParse, pE1, pE2, iTab)==0 ){
return 1;
}
@@ -100961,19 +114511,37 @@ SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, i
){
return 1;
}
- if( pE2->op==TK_NOTNULL && pE1->op!=TK_ISNULL && pE1->op!=TK_IS ){
- Expr *pX = sqlite3ExprSkipCollate(pE1->pLeft);
- testcase( pX!=pE1->pLeft );
- if( sqlite3ExprCompare(pParse, pX, pE2->pLeft, iTab)==0 ) return 1;
+ if( pE2->op==TK_NOTNULL
+ && exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0)
+ ){
+ return 1;
}
return 0;
}
+/* This is a helper function to impliesNotNullRow(). In this routine,
+** set pWalker->eCode to one only if *both* of the input expressions
+** separately have the implies-not-null-row property.
+*/
+static void bothImplyNotNullRow(Walker *pWalker, Expr *pE1, Expr *pE2){
+ if( pWalker->eCode==0 ){
+ sqlite3WalkExpr(pWalker, pE1);
+ if( pWalker->eCode ){
+ pWalker->eCode = 0;
+ sqlite3WalkExpr(pWalker, pE2);
+ }
+ }
+}
+
/*
-** This is the Expr node callback for sqlite3ExprImpliesNotNullRow().
+** This is the Expr node callback for sqlite3ExprImpliesNonNullRow().
** If the expression node requires that the table at pWalker->iCur
** have one or more non-NULL column, then set pWalker->eCode to 1 and abort.
**
+** pWalker->mWFlags is non-zero if this inquiry is being undertaking on
+** behalf of a RIGHT JOIN (or FULL JOIN). That makes a difference when
+** evaluating terms in the ON clause of an inner join.
+**
** This routine controls an optimization. False positives (setting
** pWalker->eCode to 1 when it should not be) are deadly, but false-negatives
** (never setting pWalker->eCode) is a harmless missed optimization.
@@ -100981,25 +114549,34 @@ SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Parse *pParse, Expr *pE1, Expr *pE2, i
static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_AGG_COLUMN );
testcase( pExpr->op==TK_AGG_FUNCTION );
- if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune;
+ if( ExprHasProperty(pExpr, EP_OuterON) ) return WRC_Prune;
+ if( ExprHasProperty(pExpr, EP_InnerON) && pWalker->mWFlags ){
+ /* If iCur is used in an inner-join ON clause to the left of a
+ ** RIGHT JOIN, that does *not* mean that the table must be non-null.
+ ** But it is difficult to check for that condition precisely.
+ ** To keep things simple, any use of iCur from any inner-join is
+ ** ignored while attempting to simplify a RIGHT JOIN. */
+ return WRC_Prune;
+ }
switch( pExpr->op ){
case TK_ISNOT:
- case TK_NOT:
case TK_ISNULL:
+ case TK_NOTNULL:
case TK_IS:
- case TK_OR:
- case TK_CASE:
- case TK_IN:
+ case TK_VECTOR:
case TK_FUNCTION:
+ case TK_TRUTH:
+ case TK_CASE:
testcase( pExpr->op==TK_ISNOT );
- testcase( pExpr->op==TK_NOT );
testcase( pExpr->op==TK_ISNULL );
+ testcase( pExpr->op==TK_NOTNULL );
testcase( pExpr->op==TK_IS );
- testcase( pExpr->op==TK_OR );
- testcase( pExpr->op==TK_CASE );
- testcase( pExpr->op==TK_IN );
+ testcase( pExpr->op==TK_VECTOR );
testcase( pExpr->op==TK_FUNCTION );
+ testcase( pExpr->op==TK_TRUTH );
+ testcase( pExpr->op==TK_CASE );
return WRC_Prune;
+
case TK_COLUMN:
if( pWalker->u.iCur==pExpr->iTable ){
pWalker->eCode = 1;
@@ -101007,6 +114584,40 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
}
return WRC_Prune;
+ case TK_OR:
+ case TK_AND:
+ /* Both sides of an AND or OR must separately imply non-null-row.
+ ** Consider these cases:
+ ** 1. NOT (x AND y)
+ ** 2. x OR y
+ ** If only one of x or y is non-null-row, then the overall expression
+ ** can be true if the other arm is false (case 1) or true (case 2).
+ */
+ testcase( pExpr->op==TK_OR );
+ testcase( pExpr->op==TK_AND );
+ bothImplyNotNullRow(pWalker, pExpr->pLeft, pExpr->pRight);
+ return WRC_Prune;
+
+ case TK_IN:
+ /* Beware of "x NOT IN ()" and "x NOT IN (SELECT 1 WHERE false)",
+ ** both of which can be true. But apart from these cases, if
+ ** the left-hand side of the IN is NULL then the IN itself will be
+ ** NULL. */
+ if( ExprUseXList(pExpr) && ALWAYS(pExpr->x.pList->nExpr>0) ){
+ sqlite3WalkExpr(pWalker, pExpr->pLeft);
+ }
+ return WRC_Prune;
+
+ case TK_BETWEEN:
+ /* In "x NOT BETWEEN y AND z" either x must be non-null-row or else
+ ** both y and z must be non-null row */
+ assert( ExprUseXList(pExpr) );
+ assert( pExpr->x.pList->nExpr==2 );
+ sqlite3WalkExpr(pWalker, pExpr->pLeft);
+ bothImplyNotNullRow(pWalker, pExpr->x.pList->a[0].pExpr,
+ pExpr->x.pList->a[1].pExpr);
+ return WRC_Prune;
+
/* Virtual tables are allowed to use constraints like x=NULL. So
** a term of the form x=y does not prove that y is not null if x
** is the column of a virtual table */
@@ -101015,18 +114626,30 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
case TK_LT:
case TK_LE:
case TK_GT:
- case TK_GE:
+ case TK_GE: {
+ Expr *pLeft = pExpr->pLeft;
+ Expr *pRight = pExpr->pRight;
testcase( pExpr->op==TK_EQ );
testcase( pExpr->op==TK_NE );
testcase( pExpr->op==TK_LT );
testcase( pExpr->op==TK_LE );
testcase( pExpr->op==TK_GT );
testcase( pExpr->op==TK_GE );
- if( (pExpr->pLeft->op==TK_COLUMN && IsVirtual(pExpr->pLeft->y.pTab))
- || (pExpr->pRight->op==TK_COLUMN && IsVirtual(pExpr->pRight->y.pTab))
+ /* The y.pTab=0 assignment in wherecode.c always happens after the
+ ** impliesNotNullRow() test */
+ assert( pLeft->op!=TK_COLUMN || ExprUseYTab(pLeft) );
+ assert( pRight->op!=TK_COLUMN || ExprUseYTab(pRight) );
+ if( (pLeft->op==TK_COLUMN
+ && ALWAYS(pLeft->y.pTab!=0)
+ && IsVirtual(pLeft->y.pTab))
+ || (pRight->op==TK_COLUMN
+ && ALWAYS(pRight->y.pTab!=0)
+ && IsVirtual(pRight->y.pTab))
){
- return WRC_Prune;
+ return WRC_Prune;
}
+ /* no break */ deliberate_fall_through
+ }
default:
return WRC_Continue;
}
@@ -101045,8 +114668,8 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
** False positives are not allowed, however. A false positive may result
** in an incorrect answer.
**
-** Terms of p that are marked with EP_FromJoin (and hence that come from
-** the ON or USING clauses of LEFT JOINS) are excluded from the analysis.
+** Terms of p that are marked with EP_OuterON (and hence that come from
+** the ON or USING clauses of OUTER JOINS) are excluded from the analysis.
**
** This routine is used to check if a LEFT JOIN can be converted into
** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE
@@ -101054,12 +114677,23 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
** be non-NULL, then the LEFT JOIN can be safely converted into an
** ordinary join.
*/
-SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){
+SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab, int isRJ){
Walker w;
+ p = sqlite3ExprSkipCollateAndLikely(p);
+ if( p==0 ) return 0;
+ if( p->op==TK_NOTNULL ){
+ p = p->pLeft;
+ }else{
+ while( p->op==TK_AND ){
+ if( sqlite3ExprImpliesNonNullRow(p->pLeft, iTab, isRJ) ) return 1;
+ p = p->pRight;
+ }
+ }
w.xExprCallback = impliesNotNullRow;
w.xSelectCallback = 0;
w.xSelectCallback2 = 0;
w.eCode = 0;
+ w.mWFlags = isRJ!=0;
w.u.iCur = iTab;
sqlite3WalkExpr(&w, p);
return w.eCode;
@@ -101078,14 +114712,14 @@ struct IdxCover {
};
/*
-** Check to see if there are references to columns in table
+** Check to see if there are references to columns in table
** pWalker->u.pIdxCover->iCur can be satisfied using the index
** pWalker->u.pIdxCover->pIdx.
*/
static int exprIdxCover(Walker *pWalker, Expr *pExpr){
if( pExpr->op==TK_COLUMN
&& pExpr->iTable==pWalker->u.pIdxCover->iCur
- && sqlite3ColumnOfIndex(pWalker->u.pIdxCover->pIdx, pExpr->iColumn)<0
+ && sqlite3TableColumnToIndex(pWalker->u.pIdxCover->pIdx, pExpr->iColumn)<0
){
pWalker->eCode = 1;
return WRC_Abort;
@@ -101120,62 +114754,189 @@ SQLITE_PRIVATE int sqlite3ExprCoveredByIndex(
}
-/*
-** An instance of the following structure is used by the tree walker
-** to count references to table columns in the arguments of an
-** aggregate function, in order to implement the
-** sqlite3FunctionThisSrc() routine.
-*/
-struct SrcCount {
- SrcList *pSrc; /* One particular FROM clause in a nested query */
- int nThis; /* Number of references to columns in pSrcList */
- int nOther; /* Number of references to columns in other FROM clauses */
+/* Structure used to pass information throughout the Walker in order to
+** implement sqlite3ReferencesSrcList().
+*/
+struct RefSrcList {
+ sqlite3 *db; /* Database connection used for sqlite3DbRealloc() */
+ SrcList *pRef; /* Looking for references to these tables */
+ i64 nExclude; /* Number of tables to exclude from the search */
+ int *aiExclude; /* Cursor IDs for tables to exclude from the search */
};
/*
-** Count the number of references to columns.
+** Walker SELECT callbacks for sqlite3ReferencesSrcList().
+**
+** When entering a new subquery on the pExpr argument, add all FROM clause
+** entries for that subquery to the exclude list.
+**
+** When leaving the subquery, remove those entries from the exclude list.
+*/
+static int selectRefEnter(Walker *pWalker, Select *pSelect){
+ struct RefSrcList *p = pWalker->u.pRefSrcList;
+ SrcList *pSrc = pSelect->pSrc;
+ i64 i, j;
+ int *piNew;
+ if( pSrc->nSrc==0 ) return WRC_Continue;
+ j = p->nExclude;
+ p->nExclude += pSrc->nSrc;
+ piNew = sqlite3DbRealloc(p->db, p->aiExclude, p->nExclude*sizeof(int));
+ if( piNew==0 ){
+ p->nExclude = 0;
+ return WRC_Abort;
+ }else{
+ p->aiExclude = piNew;
+ }
+ for(i=0; i<pSrc->nSrc; i++, j++){
+ p->aiExclude[j] = pSrc->a[i].iCursor;
+ }
+ return WRC_Continue;
+}
+static void selectRefLeave(Walker *pWalker, Select *pSelect){
+ struct RefSrcList *p = pWalker->u.pRefSrcList;
+ SrcList *pSrc = pSelect->pSrc;
+ if( p->nExclude ){
+ assert( p->nExclude>=pSrc->nSrc );
+ p->nExclude -= pSrc->nSrc;
+ }
+}
+
+/* This is the Walker EXPR callback for sqlite3ReferencesSrcList().
+**
+** Set the 0x01 bit of pWalker->eCode if there is a reference to any
+** of the tables shown in RefSrcList.pRef.
+**
+** Set the 0x02 bit of pWalker->eCode if there is a reference to a
+** table is in neither RefSrcList.pRef nor RefSrcList.aiExclude.
*/
-static int exprSrcCount(Walker *pWalker, Expr *pExpr){
- /* The NEVER() on the second term is because sqlite3FunctionUsesThisSrc()
- ** is always called before sqlite3ExprAnalyzeAggregates() and so the
- ** TK_COLUMNs have not yet been converted into TK_AGG_COLUMN. If
- ** sqlite3FunctionUsesThisSrc() is used differently in the future, the
- ** NEVER() will need to be removed. */
- if( pExpr->op==TK_COLUMN || NEVER(pExpr->op==TK_AGG_COLUMN) ){
+static int exprRefToSrcList(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_COLUMN
+ || pExpr->op==TK_AGG_COLUMN
+ ){
int i;
- struct SrcCount *p = pWalker->u.pSrcCount;
- SrcList *pSrc = p->pSrc;
+ struct RefSrcList *p = pWalker->u.pRefSrcList;
+ SrcList *pSrc = p->pRef;
int nSrc = pSrc ? pSrc->nSrc : 0;
for(i=0; i<nSrc; i++){
- if( pExpr->iTable==pSrc->a[i].iCursor ) break;
+ if( pExpr->iTable==pSrc->a[i].iCursor ){
+ pWalker->eCode |= 1;
+ return WRC_Continue;
+ }
}
- if( i<nSrc ){
- p->nThis++;
- }else{
- p->nOther++;
+ for(i=0; i<p->nExclude && p->aiExclude[i]!=pExpr->iTable; i++){}
+ if( i>=p->nExclude ){
+ pWalker->eCode |= 2;
}
}
return WRC_Continue;
}
/*
-** Determine if any of the arguments to the pExpr Function reference
-** pSrcList. Return true if they do. Also return true if the function
-** has no arguments or has only constant arguments. Return false if pExpr
-** references columns but not columns of tables found in pSrcList.
+** Check to see if pExpr references any tables in pSrcList.
+** Possible return values:
+**
+** 1 pExpr does references a table in pSrcList.
+**
+** 0 pExpr references some table that is not defined in either
+** pSrcList or in subqueries of pExpr itself.
+**
+** -1 pExpr only references no tables at all, or it only
+** references tables defined in subqueries of pExpr itself.
+**
+** As currently used, pExpr is always an aggregate function call. That
+** fact is exploited for efficiency.
*/
-SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){
+SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList *pSrcList){
Walker w;
- struct SrcCount cnt;
+ struct RefSrcList x;
+ assert( pParse->db!=0 );
+ memset(&w, 0, sizeof(w));
+ memset(&x, 0, sizeof(x));
+ w.xExprCallback = exprRefToSrcList;
+ w.xSelectCallback = selectRefEnter;
+ w.xSelectCallback2 = selectRefLeave;
+ w.u.pRefSrcList = &x;
+ x.db = pParse->db;
+ x.pRef = pSrcList;
assert( pExpr->op==TK_AGG_FUNCTION );
- w.xExprCallback = exprSrcCount;
- w.xSelectCallback = 0;
- w.u.pSrcCount = &cnt;
- cnt.pSrc = pSrcList;
- cnt.nThis = 0;
- cnt.nOther = 0;
+ assert( ExprUseXList(pExpr) );
sqlite3WalkExprList(&w, pExpr->x.pList);
- return cnt.nThis>0 || cnt.nOther==0;
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ assert( pExpr->pLeft->x.pList!=0 );
+ sqlite3WalkExprList(&w, pExpr->pLeft->x.pList);
+ }
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter);
+ }
+#endif
+ if( x.aiExclude ) sqlite3DbNNFreeNN(pParse->db, x.aiExclude);
+ if( w.eCode & 0x01 ){
+ return 1;
+ }else if( w.eCode ){
+ return 0;
+ }else{
+ return -1;
+ }
+}
+
+/*
+** This is a Walker expression node callback.
+**
+** For Expr nodes that contain pAggInfo pointers, make sure the AggInfo
+** object that is referenced does not refer directly to the Expr. If
+** it does, make a copy. This is done because the pExpr argument is
+** subject to change.
+**
+** The copy is scheduled for deletion using the sqlite3ExprDeferredDelete()
+** which builds on the sqlite3ParserAddCleanup() mechanism.
+*/
+static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){
+ if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced))
+ && pExpr->pAggInfo!=0
+ ){
+ AggInfo *pAggInfo = pExpr->pAggInfo;
+ int iAgg = pExpr->iAgg;
+ Parse *pParse = pWalker->pParse;
+ sqlite3 *db = pParse->db;
+ assert( iAgg>=0 );
+ if( pExpr->op!=TK_AGG_FUNCTION ){
+ if( iAgg<pAggInfo->nColumn
+ && pAggInfo->aCol[iAgg].pCExpr==pExpr
+ ){
+ pExpr = sqlite3ExprDup(db, pExpr, 0);
+ if( pExpr ){
+ pAggInfo->aCol[iAgg].pCExpr = pExpr;
+ sqlite3ExprDeferredDelete(pParse, pExpr);
+ }
+ }
+ }else{
+ assert( pExpr->op==TK_AGG_FUNCTION );
+ if( ALWAYS(iAgg<pAggInfo->nFunc)
+ && pAggInfo->aFunc[iAgg].pFExpr==pExpr
+ ){
+ pExpr = sqlite3ExprDup(db, pExpr, 0);
+ if( pExpr ){
+ pAggInfo->aFunc[iAgg].pFExpr = pExpr;
+ sqlite3ExprDeferredDelete(pParse, pExpr);
+ }
+ }
+ }
+ }
+ return WRC_Continue;
+}
+
+/*
+** Initialize a Walker object so that will persist AggInfo entries referenced
+** by the tree that is walked.
+*/
+SQLITE_PRIVATE void sqlite3AggInfoPersistWalkerInit(Walker *pWalker, Parse *pParse){
+ memset(pWalker, 0, sizeof(*pWalker));
+ pWalker->pParse = pParse;
+ pWalker->xExprCallback = agginfoPersistExprCb;
+ pWalker->xSelectCallback = sqlite3SelectWalkNoop;
}
/*
@@ -101192,7 +114953,7 @@ static int addAggInfoColumn(sqlite3 *db, AggInfo *pInfo){
&i
);
return i;
-}
+}
/*
** Add a new element to the pAggInfo->aFunc[] array. Return the index of
@@ -101201,14 +114962,82 @@ static int addAggInfoColumn(sqlite3 *db, AggInfo *pInfo){
static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){
int i;
pInfo->aFunc = sqlite3ArrayAllocate(
- db,
+ db,
pInfo->aFunc,
sizeof(pInfo->aFunc[0]),
&pInfo->nFunc,
&i
);
return i;
-}
+}
+
+/*
+** Search the AggInfo object for an aCol[] entry that has iTable and iColumn.
+** Return the index in aCol[] of the entry that describes that column.
+**
+** If no prior entry is found, create a new one and return -1. The
+** new column will have an index of pAggInfo->nColumn-1.
+*/
+static void findOrCreateAggInfoColumn(
+ Parse *pParse, /* Parsing context */
+ AggInfo *pAggInfo, /* The AggInfo object to search and/or modify */
+ Expr *pExpr /* Expr describing the column to find or insert */
+){
+ struct AggInfo_col *pCol;
+ int k;
+
+ assert( pAggInfo->iFirstReg==0 );
+ pCol = pAggInfo->aCol;
+ for(k=0; k<pAggInfo->nColumn; k++, pCol++){
+ if( pCol->pCExpr==pExpr ) return;
+ if( pCol->iTable==pExpr->iTable
+ && pCol->iColumn==pExpr->iColumn
+ && pExpr->op!=TK_IF_NULL_ROW
+ ){
+ goto fix_up_expr;
+ }
+ }
+ k = addAggInfoColumn(pParse->db, pAggInfo);
+ if( k<0 ){
+ /* OOM on resize */
+ assert( pParse->db->mallocFailed );
+ return;
+ }
+ pCol = &pAggInfo->aCol[k];
+ assert( ExprUseYTab(pExpr) );
+ pCol->pTab = pExpr->y.pTab;
+ pCol->iTable = pExpr->iTable;
+ pCol->iColumn = pExpr->iColumn;
+ pCol->iSorterColumn = -1;
+ pCol->pCExpr = pExpr;
+ if( pAggInfo->pGroupBy && pExpr->op!=TK_IF_NULL_ROW ){
+ int j, n;
+ ExprList *pGB = pAggInfo->pGroupBy;
+ struct ExprList_item *pTerm = pGB->a;
+ n = pGB->nExpr;
+ for(j=0; j<n; j++, pTerm++){
+ Expr *pE = pTerm->pExpr;
+ if( pE->op==TK_COLUMN
+ && pE->iTable==pExpr->iTable
+ && pE->iColumn==pExpr->iColumn
+ ){
+ pCol->iSorterColumn = j;
+ break;
+ }
+ }
+ }
+ if( pCol->iSorterColumn<0 ){
+ pCol->iSorterColumn = pAggInfo->nSortingColumn++;
+ }
+fix_up_expr:
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
+ assert( pExpr->pAggInfo==0 || pExpr->pAggInfo==pAggInfo );
+ pExpr->pAggInfo = pAggInfo;
+ if( pExpr->op==TK_COLUMN ){
+ pExpr->op = TK_AGG_COLUMN;
+ }
+ pExpr->iAgg = (i16)k;
+}
/*
** This is the xExprCallback for a tree walker. It is used to
@@ -101223,86 +115052,77 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
AggInfo *pAggInfo = pNC->uNC.pAggInfo;
assert( pNC->ncFlags & NC_UAggInfo );
+ assert( pAggInfo->iFirstReg==0 );
switch( pExpr->op ){
+ default: {
+ IndexedExpr *pIEpr;
+ Expr tmp;
+ assert( pParse->iSelfTab==0 );
+ if( (pNC->ncFlags & NC_InAggFunc)==0 ) break;
+ if( pParse->pIdxEpr==0 ) break;
+ for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){
+ int iDataCur = pIEpr->iDataCur;
+ if( iDataCur<0 ) continue;
+ if( sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break;
+ }
+ if( pIEpr==0 ) break;
+ if( NEVER(!ExprUseYTab(pExpr)) ) break;
+ for(i=0; i<pSrcList->nSrc; i++){
+ if( pSrcList->a[0].iCursor==pIEpr->iDataCur ) break;
+ }
+ if( i>=pSrcList->nSrc ) break;
+ if( NEVER(pExpr->pAggInfo!=0) ) break; /* Resolved by outer context */
+ if( pParse->nErr ){ return WRC_Abort; }
+
+ /* If we reach this point, it means that expression pExpr can be
+ ** translated into a reference to an index column as described by
+ ** pIEpr.
+ */
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.op = TK_AGG_COLUMN;
+ tmp.iTable = pIEpr->iIdxCur;
+ tmp.iColumn = pIEpr->iIdxCol;
+ findOrCreateAggInfoColumn(pParse, pAggInfo, &tmp);
+ if( pParse->nErr ){ return WRC_Abort; }
+ assert( pAggInfo->aCol!=0 );
+ assert( tmp.iAgg<pAggInfo->nColumn );
+ pAggInfo->aCol[tmp.iAgg].pCExpr = pExpr;
+ pExpr->pAggInfo = pAggInfo;
+ pExpr->iAgg = tmp.iAgg;
+ return WRC_Prune;
+ }
+ case TK_IF_NULL_ROW:
case TK_AGG_COLUMN:
case TK_COLUMN: {
testcase( pExpr->op==TK_AGG_COLUMN );
testcase( pExpr->op==TK_COLUMN );
+ testcase( pExpr->op==TK_IF_NULL_ROW );
/* Check to see if the column is in one of the tables in the FROM
** clause of the aggregate query */
if( ALWAYS(pSrcList!=0) ){
- struct SrcList_item *pItem = pSrcList->a;
+ SrcItem *pItem = pSrcList->a;
for(i=0; i<pSrcList->nSrc; i++, pItem++){
- struct AggInfo_col *pCol;
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
if( pExpr->iTable==pItem->iCursor ){
- /* If we reach this point, it means that pExpr refers to a table
- ** that is in the FROM clause of the aggregate query.
- **
- ** Make an entry for the column in pAggInfo->aCol[] if there
- ** is not an entry there already.
- */
- int k;
- pCol = pAggInfo->aCol;
- for(k=0; k<pAggInfo->nColumn; k++, pCol++){
- if( pCol->iTable==pExpr->iTable &&
- pCol->iColumn==pExpr->iColumn ){
- break;
- }
- }
- if( (k>=pAggInfo->nColumn)
- && (k = addAggInfoColumn(pParse->db, pAggInfo))>=0
- ){
- pCol = &pAggInfo->aCol[k];
- pCol->pTab = pExpr->y.pTab;
- pCol->iTable = pExpr->iTable;
- pCol->iColumn = pExpr->iColumn;
- pCol->iMem = ++pParse->nMem;
- pCol->iSorterColumn = -1;
- pCol->pExpr = pExpr;
- if( pAggInfo->pGroupBy ){
- int j, n;
- ExprList *pGB = pAggInfo->pGroupBy;
- struct ExprList_item *pTerm = pGB->a;
- n = pGB->nExpr;
- for(j=0; j<n; j++, pTerm++){
- Expr *pE = pTerm->pExpr;
- if( pE->op==TK_COLUMN && pE->iTable==pExpr->iTable &&
- pE->iColumn==pExpr->iColumn ){
- pCol->iSorterColumn = j;
- break;
- }
- }
- }
- if( pCol->iSorterColumn<0 ){
- pCol->iSorterColumn = pAggInfo->nSortingColumn++;
- }
- }
- /* There is now an entry for pExpr in pAggInfo->aCol[] (either
- ** because it was there before or because we just created it).
- ** Convert the pExpr to be a TK_AGG_COLUMN referring to that
- ** pAggInfo->aCol[] entry.
- */
- ExprSetVVAProperty(pExpr, EP_NoReduce);
- pExpr->pAggInfo = pAggInfo;
- pExpr->op = TK_AGG_COLUMN;
- pExpr->iAgg = (i16)k;
+ findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr);
break;
} /* endif pExpr->iTable==pItem->iCursor */
} /* end loop over pSrcList */
}
- return WRC_Prune;
+ return WRC_Continue;
}
case TK_AGG_FUNCTION: {
if( (pNC->ncFlags & NC_InAggFunc)==0
&& pWalker->walkerDepth==pExpr->op2
+ && pExpr->pAggInfo==0
){
- /* Check to see if pExpr is a duplicate of another aggregate
+ /* Check to see if pExpr is a duplicate of another aggregate
** function that is already in the pAggInfo structure
*/
struct AggInfo_func *pItem = pAggInfo->aFunc;
for(i=0; i<pAggInfo->nFunc; i++, pItem++){
- if( sqlite3ExprCompare(0, pItem->pExpr, pExpr, -1)==0 ){
+ if( NEVER(pItem->pFExpr==pExpr) ) break;
+ if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){
break;
}
}
@@ -101312,15 +115132,44 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
u8 enc = ENC(pParse->db);
i = addAggInfoFunc(pParse->db, pAggInfo);
if( i>=0 ){
+ int nArg;
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
pItem = &pAggInfo->aFunc[i];
- pItem->pExpr = pExpr;
- pItem->iMem = ++pParse->nMem;
- assert( !ExprHasProperty(pExpr, EP_IntValue) );
+ pItem->pFExpr = pExpr;
+ assert( ExprUseUToken(pExpr) );
+ nArg = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
pItem->pFunc = sqlite3FindFunction(pParse->db,
- pExpr->u.zToken,
- pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0);
- if( pExpr->flags & EP_Distinct ){
+ pExpr->u.zToken, nArg, enc, 0);
+ assert( pItem->bOBUnique==0 );
+ if( pExpr->pLeft
+ && (pItem->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)==0
+ ){
+ /* The NEEDCOLL test above causes any ORDER BY clause on
+ ** aggregate min() or max() to be ignored. */
+ ExprList *pOBList;
+ assert( nArg>0 );
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ pItem->iOBTab = pParse->nTab++;
+ pOBList = pExpr->pLeft->x.pList;
+ assert( pOBList->nExpr>0 );
+ assert( pItem->bOBUnique==0 );
+ if( pOBList->nExpr==1
+ && nArg==1
+ && sqlite3ExprCompare(0,pOBList->a[0].pExpr,
+ pExpr->x.pList->a[0].pExpr,0)==0
+ ){
+ pItem->bOBPayload = 0;
+ pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct);
+ }else{
+ pItem->bOBPayload = 1;
+ }
+ pItem->bUseSubtype =
+ (pItem->pFunc->funcFlags & SQLITE_SUBTYPE)!=0;
+ }else{
+ pItem->iOBTab = -1;
+ }
+ if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){
pItem->iDistinct = pParse->nTab++;
}else{
pItem->iDistinct = -1;
@@ -101341,15 +115190,6 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
}
return WRC_Continue;
}
-static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){
- UNUSED_PARAMETER(pSelect);
- pWalker->walkerDepth++;
- return WRC_Continue;
-}
-static void analyzeAggregatesInSelectEnd(Walker *pWalker, Select *pSelect){
- UNUSED_PARAMETER(pSelect);
- pWalker->walkerDepth--;
-}
/*
** Analyze the pExpr expression looking for aggregate functions and
@@ -101363,10 +115203,11 @@ static void analyzeAggregatesInSelectEnd(Walker *pWalker, Select *pSelect){
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
Walker w;
w.xExprCallback = analyzeAggregate;
- w.xSelectCallback = analyzeAggregatesInSelect;
- w.xSelectCallback2 = analyzeAggregatesInSelectEnd;
+ w.xSelectCallback = sqlite3WalkerDepthIncrease;
+ w.xSelectCallback2 = sqlite3WalkerDepthDecrease;
w.walkerDepth = 0;
w.u.pNC = pNC;
+ w.pParse = 0;
assert( pNC->pSrcList!=0 );
sqlite3WalkExpr(&w, pExpr);
}
@@ -101402,8 +115243,11 @@ SQLITE_PRIVATE int sqlite3GetTempReg(Parse *pParse){
** purpose.
*/
SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse *pParse, int iReg){
- if( iReg && pParse->nTempReg<ArraySize(pParse->aTempReg) ){
- pParse->aTempReg[pParse->nTempReg++] = iReg;
+ if( iReg ){
+ sqlite3VdbeReleaseRegisters(pParse, iReg, 1, 0, 0);
+ if( pParse->nTempReg<ArraySize(pParse->aTempReg) ){
+ pParse->aTempReg[pParse->nTempReg++] = iReg;
+ }
}
}
@@ -101429,6 +115273,7 @@ SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
sqlite3ReleaseTempReg(pParse, iReg);
return;
}
+ sqlite3VdbeReleaseRegisters(pParse, iReg, nReg, 0, 0);
if( nReg>pParse->nRangeReg ){
pParse->nRangeReg = nReg;
pParse->iRangeReg = iReg;
@@ -101437,6 +115282,11 @@ SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
/*
** Mark all temporary registers as being unavailable for reuse.
+**
+** Always invoke this procedure after coding a subroutine or co-routine
+** that might be invoked from other parts of the code, to ensure that
+** the sub/co-routine does not use registers in common with the code that
+** invokes the sub/co-routine.
*/
SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){
pParse->nTempReg = 0;
@@ -101444,6 +115294,37 @@ SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){
}
/*
+** Make sure sufficient registers have been allocated so that
+** iReg is a valid register number.
+*/
+SQLITE_PRIVATE void sqlite3TouchRegister(Parse *pParse, int iReg){
+ if( pParse->nMem<iReg ) pParse->nMem = iReg;
+}
+
+#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG)
+/*
+** Return the latest reusable register in the set of all registers.
+** The value returned is no less than iMin. If any register iMin or
+** greater is in permanent use, then return one more than that last
+** permanent register.
+*/
+SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse *pParse, int iMin){
+ const ExprList *pList = pParse->pConstExpr;
+ if( pList ){
+ int i;
+ for(i=0; i<pList->nExpr; i++){
+ if( pList->a[i].u.iConstExprReg>=iMin ){
+ iMin = pList->a[i].u.iConstExprReg + 1;
+ }
+ }
+ }
+ pParse->nTempReg = 0;
+ pParse->nRangeReg = 0;
+ return iMin;
+}
+#endif /* SQLITE_ENABLE_STAT4 || SQLITE_DEBUG */
+
+/*
** Validate that no temporary register falls within the range of
** iFirst..iLast, inclusive. This routine is only call from within assert()
** statements.
@@ -101462,6 +115343,14 @@ SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){
return 0;
}
}
+ if( pParse->pConstExpr ){
+ ExprList *pList = pParse->pConstExpr;
+ for(i=0; i<pList->nExpr; i++){
+ int iReg = pList->a[i].u.iConstExprReg;
+ if( iReg==0 ) continue;
+ if( iReg>=iFirst && iReg<=iLast ) return 0;
+ }
+ }
return 1;
}
#endif /* SQLITE_DEBUG */
@@ -101498,9 +115387,16 @@ SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){
**
** Or, if zName is not a system table, zero is returned.
*/
-static int isSystemTable(Parse *pParse, const char *zName){
- if( 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
- sqlite3ErrorMsg(pParse, "table %s may not be altered", zName);
+static int isAlterableTable(Parse *pParse, Table *pTab){
+ if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7)
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ || (pTab->tabFlags & TF_Eponymous)!=0
+ || ( (pTab->tabFlags & TF_Shadow)!=0
+ && sqlite3ReadOnlyShadowTables(pParse->db)
+ )
+#endif
+ ){
+ sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName);
return 1;
}
return 0;
@@ -101513,25 +115409,56 @@ static int isSystemTable(Parse *pParse, const char *zName){
** statement to ensure that the operation has not rendered any schema
** objects unusable.
*/
-static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
- sqlite3NestedParse(pParse,
+static void renameTestSchema(
+ Parse *pParse, /* Parse context */
+ const char *zDb, /* Name of db to verify schema of */
+ int bTemp, /* True if this is the temp db */
+ const char *zWhen, /* "when" part of error message */
+ int bNoDQS /* Do not allow DQS in the schema */
+){
+ pParse->colNamesSet = 1;
+ sqlite3NestedParse(pParse,
"SELECT 1 "
- "FROM \"%w\".%s "
- "WHERE name NOT LIKE 'sqlite_%%'"
+ "FROM \"%w\"." LEGACY_SCHEMA_TABLE " "
+ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
" AND sql NOT LIKE 'create virtual%%'"
- " AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ",
- zDb, MASTER_NAME,
- zDb, bTemp
+ " AND sqlite_rename_test(%Q, sql, type, name, %d, %Q, %d)=NULL ",
+ zDb,
+ zDb, bTemp, zWhen, bNoDQS
);
if( bTemp==0 ){
- sqlite3NestedParse(pParse,
+ sqlite3NestedParse(pParse,
"SELECT 1 "
- "FROM temp.%s "
- "WHERE name NOT LIKE 'sqlite_%%'"
+ "FROM temp." LEGACY_SCHEMA_TABLE " "
+ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
" AND sql NOT LIKE 'create virtual%%'"
- " AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ",
- MASTER_NAME, zDb
+ " AND sqlite_rename_test(%Q, sql, type, name, 1, %Q, %d)=NULL ",
+ zDb, zWhen, bNoDQS
+ );
+ }
+}
+
+/*
+** Generate VM code to replace any double-quoted strings (but not double-quoted
+** identifiers) within the "sql" column of the sqlite_schema table in
+** database zDb with their single-quoted equivalents. If argument bTemp is
+** not true, similarly update all SQL statements in the sqlite_schema table
+** of the temp db.
+*/
+static void renameFixQuotes(Parse *pParse, const char *zDb, int bTemp){
+ sqlite3NestedParse(pParse,
+ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE
+ " SET sql = sqlite_rename_quotefix(%Q, sql)"
+ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
+ " AND sql NOT LIKE 'create virtual%%'" , zDb, zDb
+ );
+ if( bTemp==0 ){
+ sqlite3NestedParse(pParse,
+ "UPDATE temp." LEGACY_SCHEMA_TABLE
+ " SET sql = sqlite_rename_quotefix('temp', sql)"
+ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
+ " AND sql NOT LIKE 'create virtual%%'"
);
}
}
@@ -101540,18 +115467,18 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
** Generate code to reload the schema for database iDb. And, if iDb!=1, for
** the temp database as well.
*/
-static void renameReloadSchema(Parse *pParse, int iDb){
+static void renameReloadSchema(Parse *pParse, int iDb, u16 p5){
Vdbe *v = pParse->pVdbe;
if( v ){
sqlite3ChangeCookie(pParse, iDb);
- sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0);
- if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0);
+ sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0, p5);
+ if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0, p5);
}
}
/*
-** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy"
-** command.
+** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy"
+** command.
*/
SQLITE_PRIVATE void sqlite3AlterRenameTable(
Parse *pParse, /* Parser context. */
@@ -101561,15 +115488,13 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
int iDb; /* Database that contains the table */
char *zDb; /* Name of database iDb */
Table *pTab; /* Table being renamed */
- char *zName = 0; /* NULL-terminated version of pName */
+ char *zName = 0; /* NULL-terminated version of pName */
sqlite3 *db = pParse->db; /* Database connection */
int nTabName; /* Number of UTF-8 characters in zTabName */
const char *zTabName; /* Original name of the table */
Vdbe *v;
VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */
- u32 savedDbFlags; /* Saved value of db->mDbFlags */
- savedDbFlags = db->mDbFlags;
if( NEVER(db->mallocFailed) ) goto exit_rename_table;
assert( pSrc->nSrc==1 );
assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
@@ -101578,7 +115503,6 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
if( !pTab ) goto exit_rename_table;
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
zDb = db->aDb[iDb].zDbSName;
- db->mDbFlags |= DBFLAG_PreferBuiltin;
/* Get a NULL terminated version of the new table name. */
zName = sqlite3NameFromToken(db, pName);
@@ -101587,8 +115511,11 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
/* Check that a table or index named 'zName' does not already exist
** in database iDb. If so, this is an error.
*/
- if( sqlite3FindTable(db, zName, zDb) || sqlite3FindIndex(db, zName, zDb) ){
- sqlite3ErrorMsg(pParse,
+ if( sqlite3FindTable(db, zName, zDb)
+ || sqlite3FindIndex(db, zName, zDb)
+ || sqlite3IsShadowTableOf(db, pTab, zName)
+ ){
+ sqlite3ErrorMsg(pParse,
"there is already another table or index with this name: %s", zName);
goto exit_rename_table;
}
@@ -101596,15 +115523,15 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
/* Make sure it is not a system table being altered, or a reserved name
** that the table is being renamed to.
*/
- if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){
+ if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){
goto exit_rename_table;
}
- if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto
- exit_rename_table;
+ if( SQLITE_OK!=sqlite3CheckObjectName(pParse,zName,"table",zName) ){
+ goto exit_rename_table;
}
#ifndef SQLITE_OMIT_VIEW
- if( pTab->pSelect ){
+ if( IsView(pTab) ){
sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName);
goto exit_rename_table;
}
@@ -101629,15 +115556,15 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
}
#endif
- /* Begin a transaction for database iDb.
- ** Then modify the schema cookie (since the ALTER TABLE modifies the
- ** schema). Open a statement transaction if the table is a virtual
- ** table.
- */
+ /* Begin a transaction for database iDb. Then modify the schema cookie
+ ** (since the ALTER TABLE modifies the schema). Call sqlite3MayAbort(),
+ ** as the scalar functions (e.g. sqlite_rename_table()) invoked by the
+ ** nested SQL may raise an exception. */
v = sqlite3GetVdbe(pParse);
if( v==0 ){
goto exit_rename_table;
}
+ sqlite3MayAbort(pParse);
/* figure out how many UTF-8 characters are in zName */
zTabName = pTab->zName;
@@ -101645,33 +115572,34 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
/* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in
** the schema to use the new table name. */
- sqlite3NestedParse(pParse,
- "UPDATE \"%w\".%s SET "
+ sqlite3NestedParse(pParse,
+ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
"sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, %d) "
"WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)"
- "AND name NOT LIKE 'sqlite_%%'"
- , zDb, MASTER_NAME, zDb, zTabName, zName, (iDb==1), zTabName
+ "AND name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
+ , zDb, zDb, zTabName, zName, (iDb==1), zTabName
);
- /* Update the tbl_name and name columns of the sqlite_master table
+ /* Update the tbl_name and name columns of the sqlite_schema table
** as required. */
sqlite3NestedParse(pParse,
- "UPDATE %Q.%s SET "
+ "UPDATE %Q." LEGACY_SCHEMA_TABLE " SET "
"tbl_name = %Q, "
"name = CASE "
"WHEN type='table' THEN %Q "
- "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN "
+ "WHEN name LIKE 'sqliteX_autoindex%%' ESCAPE 'X' "
+ " AND type='index' THEN "
"'sqlite_autoindex_' || %Q || substr(name,%d+18) "
"ELSE name END "
"WHERE tbl_name=%Q COLLATE nocase AND "
- "(type='table' OR type='index' OR type='trigger');",
- zDb, MASTER_NAME,
- zName, zName, zName,
+ "(type='table' OR type='index' OR type='trigger');",
+ zDb,
+ zName, zName, zName,
nTabName, zTabName
);
#ifndef SQLITE_OMIT_AUTOINCREMENT
- /* If the sqlite_sequence table exists in this database, then update
+ /* If the sqlite_sequence table exists in this database, then update
** it with the new table name.
*/
if( sqlite3FindTable(db, "sqlite_sequence", zDb) ){
@@ -101682,15 +115610,15 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
#endif
/* If the table being renamed is not itself part of the temp database,
- ** edit view and trigger definitions within the temp database
+ ** edit view and trigger definitions within the temp database
** as required. */
if( iDb!=1 ){
- sqlite3NestedParse(pParse,
- "UPDATE sqlite_temp_master SET "
+ sqlite3NestedParse(pParse,
+ "UPDATE sqlite_temp_schema SET "
"sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), "
"tbl_name = "
"CASE WHEN tbl_name=%Q COLLATE nocase AND "
- " sqlite_rename_test(%Q, sql, type, name, 1) "
+ " sqlite_rename_test(%Q, sql, type, name, 1, 'after rename', 0) "
"THEN %Q ELSE tbl_name END "
"WHERE type IN ('view', 'trigger')"
, zDb, zTabName, zName, zTabName, zDb, zName);
@@ -101706,17 +115634,31 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
int i = ++pParse->nMem;
sqlite3VdbeLoadString(v, i, zName);
sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB);
- sqlite3MayAbort(pParse);
}
#endif
- renameReloadSchema(pParse, iDb);
- renameTestSchema(pParse, zDb, iDb==1);
+ renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
+ renameTestSchema(pParse, zDb, iDb==1, "after rename", 0);
exit_rename_table:
sqlite3SrcListDelete(db, pSrc);
sqlite3DbFree(db, zName);
- db->mDbFlags = savedDbFlags;
+}
+
+/*
+** Write code that will raise an error if the table described by
+** zDb and zTab is not empty.
+*/
+static void sqlite3ErrorIfNotEmpty(
+ Parse *pParse, /* Parsing context */
+ const char *zDb, /* Schema holding the table */
+ const char *zTab, /* Table to check for empty */
+ const char *zErr /* Error message text */
+){
+ sqlite3NestedParse(pParse,
+ "SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",
+ zErr, zDb, zTab
+ );
}
/*
@@ -101741,7 +115683,9 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
int r1; /* Temporary registers */
db = pParse->db;
- if( pParse->nErr || db->mallocFailed ) return;
+ assert( db->pParse==pParse );
+ if( pParse->nErr ) return;
+ assert( db->mallocFailed==0 );
pNew = pParse->pNewTable;
assert( pNew );
@@ -101750,7 +115694,7 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
zDb = db->aDb[iDb].zDbSName;
zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */
pCol = &pNew->aCol[pNew->nCol-1];
- pDflt = pCol->pDflt;
+ pDflt = sqlite3ColumnExpr(pNew, pCol);
pTab = sqlite3FindTable(db, zTab, zDb);
assert( pTab );
@@ -101761,14 +115705,6 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
}
#endif
- /* If the default value for the new column was specified with a
- ** literal NULL, then set pDflt to 0. This simplifies checking
- ** for an SQL NULL default below.
- */
- assert( pDflt==0 || pDflt->op==TK_SPAN );
- if( pDflt && pDflt->pLeft->op==TK_NULL ){
- pDflt = 0;
- }
/* Check that the new column is not specified as PRIMARY KEY or UNIQUE.
** If there is a NOT NULL constraint, then the default value for the
@@ -101779,65 +115715,81 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
return;
}
if( pNew->pIndex ){
- sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column");
- return;
- }
- if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){
- sqlite3ErrorMsg(pParse,
- "Cannot add a REFERENCES column with non-NULL default value");
- return;
- }
- if( pCol->notNull && !pDflt ){
- sqlite3ErrorMsg(pParse,
- "Cannot add a NOT NULL column with default value NULL");
+ sqlite3ErrorMsg(pParse,
+ "Cannot add a UNIQUE column");
return;
}
-
- /* Ensure the default expression is something that sqlite3ValueFromExpr()
- ** can handle (i.e. not CURRENT_TIME etc.)
- */
- if( pDflt ){
- sqlite3_value *pVal = 0;
- int rc;
- rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_BLOB, &pVal);
- assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
- if( rc!=SQLITE_OK ){
- assert( db->mallocFailed == 1 );
- return;
+ if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){
+ /* If the default value for the new column was specified with a
+ ** literal NULL, then set pDflt to 0. This simplifies checking
+ ** for an SQL NULL default below.
+ */
+ assert( pDflt==0 || pDflt->op==TK_SPAN );
+ if( pDflt && pDflt->pLeft->op==TK_NULL ){
+ pDflt = 0;
}
- if( !pVal ){
- sqlite3ErrorMsg(pParse, "Cannot add a column with non-constant default");
- return;
+ assert( IsOrdinaryTable(pNew) );
+ if( (db->flags&SQLITE_ForeignKeys) && pNew->u.tab.pFKey && pDflt ){
+ sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
+ "Cannot add a REFERENCES column with non-NULL default value");
+ }
+ if( pCol->notNull && !pDflt ){
+ sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
+ "Cannot add a NOT NULL column with default value NULL");
+ }
+
+
+ /* Ensure the default expression is something that sqlite3ValueFromExpr()
+ ** can handle (i.e. not CURRENT_TIME etc.)
+ */
+ if( pDflt ){
+ sqlite3_value *pVal = 0;
+ int rc;
+ rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_BLOB, &pVal);
+ assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
+ if( rc!=SQLITE_OK ){
+ assert( db->mallocFailed == 1 );
+ return;
+ }
+ if( !pVal ){
+ sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
+ "Cannot add a column with non-constant default");
+ }
+ sqlite3ValueFree(pVal);
}
- sqlite3ValueFree(pVal);
+ }else if( pCol->colFlags & COLFLAG_STORED ){
+ sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "cannot add a STORED column");
}
+
/* Modify the CREATE TABLE statement. */
zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
if( zCol ){
char *zEnd = &zCol[pColDef->n-1];
- u32 savedDbFlags = db->mDbFlags;
while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){
*zEnd-- = '\0';
}
- db->mDbFlags |= DBFLAG_PreferBuiltin;
- sqlite3NestedParse(pParse,
- "UPDATE \"%w\".%s SET "
- "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) "
- "WHERE type = 'table' AND name = %Q",
- zDb, MASTER_NAME, pNew->addColOffset, zCol, pNew->addColOffset+1,
+ /* substr() operations on characters, but addColOffset is in bytes. So we
+ ** have to use printf() to translate between these units: */
+ assert( IsOrdinaryTable(pTab) );
+ assert( IsOrdinaryTable(pNew) );
+ sqlite3NestedParse(pParse,
+ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
+ "sql = printf('%%.%ds, ',sql) || %Q"
+ " || substr(sql,1+length(printf('%%.%ds',sql))) "
+ "WHERE type = 'table' AND name = %Q",
+ zDb, pNew->u.tab.addColOffset, zCol, pNew->u.tab.addColOffset,
zTab
);
sqlite3DbFree(db, zCol);
- db->mDbFlags = savedDbFlags;
}
- /* Make sure the schema version is at least 3. But do not upgrade
- ** from less than 3 to 4, as that will corrupt any preexisting DESC
- ** index.
- */
v = sqlite3GetVdbe(pParse);
if( v ){
+ /* Make sure the schema version is at least 3. But do not upgrade
+ ** from less than 3 to 4, as that will corrupt any preexisting DESC
+ ** index.
+ */
r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT);
sqlite3VdbeUsesBtree(v, iDb);
@@ -101846,22 +115798,42 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3);
sqlite3ReleaseTempReg(pParse, r1);
- }
- /* Reload the table definition */
- renameReloadSchema(pParse, iDb);
+ /* Reload the table definition */
+ renameReloadSchema(pParse, iDb, INITFLAG_AlterAdd);
+
+ /* Verify that constraints are still satisfied */
+ if( pNew->pCheck!=0
+ || (pCol->notNull && (pCol->colFlags & COLFLAG_GENERATED)!=0)
+ || (pTab->tabFlags & TF_Strict)!=0
+ ){
+ sqlite3NestedParse(pParse,
+ "SELECT CASE WHEN quick_check GLOB 'CHECK*'"
+ " THEN raise(ABORT,'CHECK constraint failed')"
+ " WHEN quick_check GLOB 'non-* value in*'"
+ " THEN raise(ABORT,'type mismatch on DEFAULT')"
+ " ELSE raise(ABORT,'NOT NULL constraint failed')"
+ " END"
+ " FROM pragma_quick_check(%Q,%Q)"
+ " WHERE quick_check GLOB 'CHECK*'"
+ " OR quick_check GLOB 'NULL*'"
+ " OR quick_check GLOB 'non-* value in*'",
+ zTab, zDb
+ );
+ }
+ }
}
/*
** This function is called by the parser after the table-name in
-** an "ALTER TABLE <table-name> ADD" statement is parsed. Argument
+** an "ALTER TABLE <table-name> ADD" statement is parsed. Argument
** pSrc is the full-name of the table being altered.
**
** This routine makes a (partial) copy of the Table structure
** for the table being altered and sets Parse.pNewTable to point
** to it. Routines called by the parser as the column definition
-** is parsed (i.e. sqlite3AddColumn()) add the new Column data to
-** the copy. The copy of the Table structure is deleted by tokenize.c
+** is parsed (i.e. sqlite3AddColumn()) add the new Column data to
+** the copy. The copy of the Table structure is deleted by tokenize.c
** after parsing is finished.
**
** Routine sqlite3AlterFinishAddColumn() will be called to complete
@@ -101890,15 +115862,17 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
#endif
/* Make sure this is not an attempt to ALTER a view. */
- if( pTab->pSelect ){
+ if( IsView(pTab) ){
sqlite3ErrorMsg(pParse, "Cannot add a column to a view");
goto exit_begin_add_column;
}
- if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){
+ if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){
goto exit_begin_add_column;
}
- assert( pTab->addColOffset>0 );
+ sqlite3MayAbort(pParse);
+ assert( IsOrdinaryTable(pTab) );
+ assert( pTab->u.tab.addColOffset>0 );
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
/* Put a copy of the Table struct in Parse.pNewTable for the
@@ -101925,13 +115899,14 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
for(i=0; i<pNew->nCol; i++){
Column *pCol = &pNew->aCol[i];
- pCol->zName = sqlite3DbStrDup(db, pCol->zName);
- pCol->zColl = 0;
- pCol->pDflt = 0;
+ pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName);
+ pCol->hName = sqlite3StrIHash(pCol->zCnName);
}
+ assert( IsOrdinaryTable(pNew) );
+ pNew->u.tab.pDfltList = sqlite3ExprListDup(db, pTab->u.tab.pDfltList, 0);
pNew->pSchema = db->aDb[iDb].pSchema;
- pNew->addColOffset = pTab->addColOffset;
- pNew->nTabRef = 1;
+ pNew->u.tab.addColOffset = pTab->u.tab.addColOffset;
+ assert( pNew->nTabRef==1 );
exit_begin_add_column:
sqlite3SrcListDelete(db, pSrc);
@@ -101947,10 +115922,10 @@ exit_begin_add_column:
** Or, if pTab is not a view or virtual table, zero is returned.
*/
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
-static int isRealTable(Parse *pParse, Table *pTab){
+static int isRealTable(Parse *pParse, Table *pTab, int bDrop){
const char *zType = 0;
#ifndef SQLITE_OMIT_VIEW
- if( pTab->pSelect ){
+ if( IsView(pTab) ){
zType = "view";
}
#endif
@@ -101960,15 +115935,16 @@ static int isRealTable(Parse *pParse, Table *pTab){
}
#endif
if( zType ){
- sqlite3ErrorMsg(
- pParse, "cannot rename columns of %s \"%s\"", zType, pTab->zName
+ sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"",
+ (bDrop ? "drop column from" : "rename columns of"),
+ zType, pTab->zName
);
return 1;
}
return 0;
}
#else /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */
-# define isRealTable(x,y) (0)
+# define isRealTable(x,y,z) (0)
#endif
/*
@@ -101996,10 +115972,10 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn(
if( !pTab ) goto exit_rename_column;
/* Cannot alter a system table */
- if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ) goto exit_rename_column;
- if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column;
+ if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column;
+ if( SQLITE_OK!=isRealTable(pParse, pTab, 0) ) goto exit_rename_column;
- /* Which schema holds the table to be altered */
+ /* Which schema holds the table to be altered */
iSchema = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iSchema>=0 );
zDb = db->aDb[iSchema].zDbSName;
@@ -102016,42 +115992,46 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn(
zOld = sqlite3NameFromToken(db, pOld);
if( !zOld ) goto exit_rename_column;
for(iCol=0; iCol<pTab->nCol; iCol++){
- if( 0==sqlite3StrICmp(pTab->aCol[iCol].zName, zOld) ) break;
+ if( 0==sqlite3StrICmp(pTab->aCol[iCol].zCnName, zOld) ) break;
}
if( iCol==pTab->nCol ){
- sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zOld);
+ sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pOld);
goto exit_rename_column;
}
+ /* Ensure the schema contains no double-quoted strings */
+ renameTestSchema(pParse, zDb, iSchema==1, "", 0);
+ renameFixQuotes(pParse, zDb, iSchema==1);
+
/* Do the rename operation using a recursive UPDATE statement that
** uses the sqlite_rename_column() SQL function to compute the new
- ** CREATE statement text for the sqlite_master table.
+ ** CREATE statement text for the sqlite_schema table.
*/
+ sqlite3MayAbort(pParse);
zNew = sqlite3NameFromToken(db, pNew);
if( !zNew ) goto exit_rename_column;
assert( pNew->n>0 );
bQuote = sqlite3Isquote(pNew->z[0]);
- sqlite3NestedParse(pParse,
- "UPDATE \"%w\".%s SET "
+ sqlite3NestedParse(pParse,
+ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
"sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, %d) "
- "WHERE name NOT LIKE 'sqlite_%%' AND (type != 'index' OR tbl_name = %Q)"
- " AND sql NOT LIKE 'create virtual%%'",
- zDb, MASTER_NAME,
+ "WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X' "
+ " AND (type != 'index' OR tbl_name = %Q)",
+ zDb,
zDb, pTab->zName, iCol, zNew, bQuote, iSchema==1,
pTab->zName
);
- sqlite3NestedParse(pParse,
- "UPDATE temp.%s SET "
+ sqlite3NestedParse(pParse,
+ "UPDATE temp." LEGACY_SCHEMA_TABLE " SET "
"sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d, 1) "
"WHERE type IN ('trigger', 'view')",
- MASTER_NAME,
zDb, pTab->zName, iCol, zNew, bQuote
);
/* Drop and reload the database schema. */
- renameReloadSchema(pParse, iSchema);
- renameTestSchema(pParse, zDb, iSchema==1);
+ renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename);
+ renameTestSchema(pParse, zDb, iSchema==1, "after rename", 1);
exit_rename_column:
sqlite3SrcListDelete(db, pSrc);
@@ -102078,7 +116058,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn(
** the parse tree.
*/
struct RenameToken {
- void *p; /* Parse tree element created by token t */
+ const void *p; /* Parse tree element created by token t */
Token t; /* The token that created parse tree element p */
RenameToken *pNext; /* Next is a list of all RenameToken objects */
};
@@ -102092,7 +116072,7 @@ struct RenameCtx {
RenameToken *pList; /* List of tokens to overwrite */
int nList; /* Number of tokens in pList */
int iCol; /* Index of column being renamed */
- Table *pTab; /* Table being ALTERed */
+ Table *pTab; /* Table being ALTERed */
const char *zOld; /* Old column name */
};
@@ -102100,14 +116080,14 @@ struct RenameCtx {
/*
** This function is only for debugging. It performs two tasks:
**
-** 1. Checks that pointer pPtr does not already appear in the
+** 1. Checks that pointer pPtr does not already appear in the
** rename-token list.
**
** 2. Dereferences each pointer in the rename-token list.
**
** The second is most effective when debugging under valgrind or
-** address-sanitizer or similar. If any of these pointers no longer
-** point to valid objects, an exception is raised by the memory-checking
+** address-sanitizer or similar. If any of these pointers no longer
+** point to valid objects, an exception is raised by the memory-checking
** tool.
**
** The point of this is to prevent comparisons of invalid pointer values.
@@ -102120,16 +116100,19 @@ struct RenameCtx {
** Technically, as x no longer points into a valid object or to the byte
** following a valid object, it may not be used in comparison operations.
*/
-static void renameTokenCheckAll(Parse *pParse, void *pPtr){
- if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){
- RenameToken *p;
- u8 i = 0;
+static void renameTokenCheckAll(Parse *pParse, const void *pPtr){
+ assert( pParse==pParse->db->pParse );
+ assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 );
+ if( pParse->nErr==0 ){
+ const RenameToken *p;
+ u32 i = 1;
for(p=pParse->pRename; p; p=p->pNext){
if( p->p ){
assert( p->p!=pPtr );
- i += *(u8*)(p->p);
+ i += *(u8*)(p->p) | 1;
}
}
+ assert( i>0 );
}
}
#else
@@ -102148,16 +116131,22 @@ static void renameTokenCheckAll(Parse *pParse, void *pPtr){
** with tail recursion in tokenExpr() routine, for a small performance
** improvement.
*/
-SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
+SQLITE_PRIVATE const void *sqlite3RenameTokenMap(
+ Parse *pParse,
+ const void *pPtr,
+ const Token *pToken
+){
RenameToken *pNew;
assert( pPtr || pParse->db->mallocFailed );
renameTokenCheckAll(pParse, pPtr);
- pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
- if( pNew ){
- pNew->p = pPtr;
- pNew->t = *pToken;
- pNew->pNext = pParse->pRename;
- pParse->pRename = pNew;
+ if( ALWAYS(pParse->eParseMode!=PARSE_MODE_UNMAP) ){
+ pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
+ if( pNew ){
+ pNew->p = pPtr;
+ pNew->t = *pToken;
+ pNew->pNext = pParse->pRename;
+ pParse->pRename = pNew;
+ }
}
return pPtr;
@@ -102168,7 +116157,7 @@ SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pTo
** with parse tree element pFrom. This function remaps the associated token
** to parse tree element pTo.
*/
-SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){
+SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse *pParse, const void *pTo, const void *pFrom){
RenameToken *p;
renameTokenCheckAll(pParse, pTo);
for(p=pParse->pRename; p; p=p->pNext){
@@ -102184,7 +116173,96 @@ SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFro
*/
static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){
Parse *pParse = pWalker->pParse;
- sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr);
+ sqlite3RenameTokenRemap(pParse, 0, (const void*)pExpr);
+ if( ExprUseYTab(pExpr) ){
+ sqlite3RenameTokenRemap(pParse, 0, (const void*)&pExpr->y.pTab);
+ }
+ return WRC_Continue;
+}
+
+/*
+** Iterate through the Select objects that are part of WITH clauses attached
+** to select statement pSelect.
+*/
+static void renameWalkWith(Walker *pWalker, Select *pSelect){
+ With *pWith = pSelect->pWith;
+ if( pWith ){
+ Parse *pParse = pWalker->pParse;
+ int i;
+ With *pCopy = 0;
+ assert( pWith->nCte>0 );
+ if( (pWith->a[0].pSelect->selFlags & SF_Expanded)==0 ){
+ /* Push a copy of the With object onto the with-stack. We use a copy
+ ** here as the original will be expanded and resolved (flags SF_Expanded
+ ** and SF_Resolved) below. And the parser code that uses the with-stack
+ ** fails if the Select objects on it have already been expanded and
+ ** resolved. */
+ pCopy = sqlite3WithDup(pParse->db, pWith);
+ pCopy = sqlite3WithPush(pParse, pCopy, 1);
+ }
+ for(i=0; i<pWith->nCte; i++){
+ Select *p = pWith->a[i].pSelect;
+ NameContext sNC;
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pParse = pParse;
+ if( pCopy ) sqlite3SelectPrep(sNC.pParse, p, &sNC);
+ if( sNC.pParse->db->mallocFailed ) return;
+ sqlite3WalkSelect(pWalker, p);
+ sqlite3RenameExprlistUnmap(pParse, pWith->a[i].pCols);
+ }
+ if( pCopy && pParse->pWith==pCopy ){
+ pParse->pWith = pCopy->pOuter;
+ }
+ }
+}
+
+/*
+** Unmap all tokens in the IdList object passed as the second argument.
+*/
+static void unmapColumnIdlistNames(
+ Parse *pParse,
+ const IdList *pIdList
+){
+ int ii;
+ assert( pIdList!=0 );
+ for(ii=0; ii<pIdList->nId; ii++){
+ sqlite3RenameTokenRemap(pParse, 0, (const void*)pIdList->a[ii].zName);
+ }
+}
+
+/*
+** Walker callback used by sqlite3RenameExprUnmap().
+*/
+static int renameUnmapSelectCb(Walker *pWalker, Select *p){
+ Parse *pParse = pWalker->pParse;
+ int i;
+ if( pParse->nErr ) return WRC_Abort;
+ testcase( p->selFlags & SF_View );
+ testcase( p->selFlags & SF_CopyCte );
+ if( p->selFlags & (SF_View|SF_CopyCte) ){
+ return WRC_Prune;
+ }
+ if( ALWAYS(p->pEList) ){
+ ExprList *pList = p->pEList;
+ for(i=0; i<pList->nExpr; i++){
+ if( pList->a[i].zEName && pList->a[i].fg.eEName==ENAME_NAME ){
+ sqlite3RenameTokenRemap(pParse, 0, (void*)pList->a[i].zEName);
+ }
+ }
+ }
+ if( ALWAYS(p->pSrc) ){ /* Every Select as a SrcList, even if it is empty */
+ SrcList *pSrc = p->pSrc;
+ for(i=0; i<pSrc->nSrc; i++){
+ sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
+ if( pSrc->a[i].fg.isUsing==0 ){
+ sqlite3WalkExpr(pWalker, pSrc->a[i].u3.pOn);
+ }else{
+ unmapColumnIdlistNames(pParse, pSrc->a[i].u3.pUsing);
+ }
+ }
+ }
+
+ renameWalkWith(pWalker, p);
return WRC_Continue;
}
@@ -102192,15 +116270,19 @@ static int renameUnmapExprCb(Walker *pWalker, Expr *pExpr){
** Remove all nodes that are part of expression pExpr from the rename list.
*/
SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse *pParse, Expr *pExpr){
+ u8 eMode = pParse->eParseMode;
Walker sWalker;
memset(&sWalker, 0, sizeof(Walker));
sWalker.pParse = pParse;
sWalker.xExprCallback = renameUnmapExprCb;
+ sWalker.xSelectCallback = renameUnmapSelectCb;
+ pParse->eParseMode = PARSE_MODE_UNMAP;
sqlite3WalkExpr(&sWalker, pExpr);
+ pParse->eParseMode = eMode;
}
/*
-** Remove all nodes that are part of expression-list pEList from the
+** Remove all nodes that are part of expression-list pEList from the
** rename list.
*/
SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse *pParse, ExprList *pEList){
@@ -102212,7 +116294,9 @@ SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse *pParse, ExprList *pEList){
sWalker.xExprCallback = renameUnmapExprCb;
sqlite3WalkExprList(&sWalker, pEList);
for(i=0; i<pEList->nExpr; i++){
- sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zName);
+ if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME) ){
+ sqlite3RenameTokenRemap(pParse, 0, (void*)pEList->a[i].zEName);
+ }
}
}
}
@@ -102231,23 +116315,35 @@ static void renameTokenFree(sqlite3 *db, RenameToken *pToken){
/*
** Search the Parse object passed as the first argument for a RenameToken
-** object associated with parse tree element pPtr. If found, remove it
-** from the Parse object and add it to the list maintained by the
-** RenameCtx object passed as the second argument.
+** object associated with parse tree element pPtr. If found, return a pointer
+** to it. Otherwise, return NULL.
+**
+** If the second argument passed to this function is not NULL and a matching
+** RenameToken object is found, remove it from the Parse object and add it to
+** the list maintained by the RenameCtx object.
*/
-static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
+static RenameToken *renameTokenFind(
+ Parse *pParse,
+ struct RenameCtx *pCtx,
+ const void *pPtr
+){
RenameToken **pp;
- assert( pPtr!=0 );
+ if( NEVER(pPtr==0) ){
+ return 0;
+ }
for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){
if( (*pp)->p==pPtr ){
RenameToken *pToken = *pp;
- *pp = pToken->pNext;
- pToken->pNext = pCtx->pList;
- pCtx->pList = pToken;
- pCtx->nList++;
- break;
+ if( pCtx ){
+ *pp = pToken->pNext;
+ pToken->pNext = pCtx->pList;
+ pCtx->pList = pToken;
+ pCtx->nList++;
+ }
+ return pToken;
}
}
+ return 0;
}
/*
@@ -102256,8 +116352,12 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
** descend into sub-select statements.
*/
static int renameColumnSelectCb(Walker *pWalker, Select *p){
- UNUSED_PARAMETER(pWalker);
- UNUSED_PARAMETER(p);
+ if( p->selFlags & (SF_View|SF_CopyCte) ){
+ testcase( p->selFlags & SF_View );
+ testcase( p->selFlags & SF_CopyCte );
+ return WRC_Prune;
+ }
+ renameWalkWith(pWalker, p);
return WRC_Continue;
}
@@ -102272,13 +116372,14 @@ static int renameColumnSelectCb(Walker *pWalker, Select *p){
*/
static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){
RenameCtx *p = pWalker->u.pRename;
- if( pExpr->op==TK_TRIGGER
- && pExpr->iColumn==p->iCol
+ if( pExpr->op==TK_TRIGGER
+ && pExpr->iColumn==p->iCol
&& pWalker->pParse->pTriggerTab==p->pTab
){
renameTokenFind(pWalker->pParse, p, (void*)pExpr);
- }else if( pExpr->op==TK_COLUMN
- && pExpr->iColumn==p->iCol
+ }else if( pExpr->op==TK_COLUMN
+ && pExpr->iColumn==p->iCol
+ && ALWAYS(ExprUseYTab(pExpr))
&& p->pTab==pExpr->y.pTab
){
renameTokenFind(pWalker->pParse, p, (void*)pExpr);
@@ -102310,15 +116411,15 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
}
/*
-** An error occured while parsing or otherwise processing a database
+** An error occurred while parsing or otherwise processing a database
** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an
** ALTER TABLE RENAME COLUMN program. The error message emitted by the
** sub-routine is currently stored in pParse->zErrMsg. This function
** adds context to the error message and then stores it in pCtx.
*/
static void renameColumnParseError(
- sqlite3_context *pCtx,
- int bPost,
+ sqlite3_context *pCtx,
+ const char *zWhen,
sqlite3_value *pType,
sqlite3_value *pObject,
Parse *pParse
@@ -102327,59 +116428,63 @@ static void renameColumnParseError(
const char *zN = (const char*)sqlite3_value_text(pObject);
char *zErr;
- zErr = sqlite3_mprintf("error in %s %s%s: %s",
- zT, zN, (bPost ? " after rename" : ""),
+ zErr = sqlite3MPrintf(pParse->db, "error in %s %s%s%s: %s",
+ zT, zN, (zWhen[0] ? " " : ""), zWhen,
pParse->zErrMsg
);
sqlite3_result_error(pCtx, zErr, -1);
- sqlite3_free(zErr);
+ sqlite3DbFree(pParse->db, zErr);
}
/*
** For each name in the the expression-list pEList (i.e. each
-** pEList->a[i].zName) that matches the string in zOld, extract the
+** pEList->a[i].zName) that matches the string in zOld, extract the
** corresponding rename-token from Parse object pParse and add it
** to the RenameCtx pCtx.
*/
static void renameColumnElistNames(
- Parse *pParse,
- RenameCtx *pCtx,
- ExprList *pEList,
+ Parse *pParse,
+ RenameCtx *pCtx,
+ const ExprList *pEList,
const char *zOld
){
if( pEList ){
int i;
for(i=0; i<pEList->nExpr; i++){
- char *zName = pEList->a[i].zName;
- if( 0==sqlite3_stricmp(zName, zOld) ){
- renameTokenFind(pParse, pCtx, (void*)zName);
+ const char *zName = pEList->a[i].zEName;
+ if( ALWAYS(pEList->a[i].fg.eEName==ENAME_NAME)
+ && ALWAYS(zName!=0)
+ && 0==sqlite3_stricmp(zName, zOld)
+ ){
+ renameTokenFind(pParse, pCtx, (const void*)zName);
}
}
}
}
/*
-** For each name in the the id-list pIdList (i.e. each pIdList->a[i].zName)
-** that matches the string in zOld, extract the corresponding rename-token
+** For each name in the the id-list pIdList (i.e. each pIdList->a[i].zName)
+** that matches the string in zOld, extract the corresponding rename-token
** from Parse object pParse and add it to the RenameCtx pCtx.
*/
static void renameColumnIdlistNames(
- Parse *pParse,
- RenameCtx *pCtx,
- IdList *pIdList,
+ Parse *pParse,
+ RenameCtx *pCtx,
+ const IdList *pIdList,
const char *zOld
){
if( pIdList ){
int i;
for(i=0; i<pIdList->nId; i++){
- char *zName = pIdList->a[i].zName;
+ const char *zName = pIdList->a[i].zName;
if( 0==sqlite3_stricmp(zName, zOld) ){
- renameTokenFind(pParse, pCtx, (void*)zName);
+ renameTokenFind(pParse, pCtx, (const void*)zName);
}
}
}
}
+
/*
** Parse the SQL statement zSql using Parse object (*p). The Parse object
** is initialized by this function before it is used.
@@ -102387,31 +116492,27 @@ static void renameColumnIdlistNames(
static int renameParseSql(
Parse *p, /* Memory to use for Parse object */
const char *zDb, /* Name of schema SQL belongs to */
- int bTable, /* 1 -> RENAME TABLE, 0 -> RENAME COLUMN */
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL to parse */
int bTemp /* True if SQL is from temp schema */
){
int rc;
- char *zErr = 0;
+ sqlite3ParseObjectInit(p, db);
+ if( zSql==0 ){
+ return SQLITE_NOMEM;
+ }
+ if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){
+ return SQLITE_CORRUPT_BKPT;
+ }
db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb);
-
- /* Parse the SQL statement passed as the first argument. If no error
- ** occurs and the parse does not result in a new table, index or
- ** trigger object, the database must be corrupt. */
- memset(p, 0, sizeof(Parse));
- p->eParseMode = (bTable ? PARSE_MODE_RENAME_TABLE : PARSE_MODE_RENAME_COLUMN);
+ p->eParseMode = PARSE_MODE_RENAME;
p->db = db;
p->nQueryLoop = 1;
- rc = sqlite3RunParser(p, zSql, &zErr);
- assert( p->zErrMsg==0 );
- assert( rc!=SQLITE_OK || zErr==0 );
- assert( (0!=p->pNewTable) + (0!=p->pNewIndex) + (0!=p->pNewTrigger)<2 );
- p->zErrMsg = zErr;
+ rc = sqlite3RunParser(p, zSql);
if( db->mallocFailed ) rc = SQLITE_NOMEM;
- if( rc==SQLITE_OK
- && p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0
+ if( rc==SQLITE_OK
+ && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0)
){
rc = SQLITE_CORRUPT_BKPT;
}
@@ -102448,56 +116549,81 @@ static int renameEditSql(
const char *zNew, /* New token text */
int bQuote /* True to always quote token */
){
- int nNew = sqlite3Strlen30(zNew);
- int nSql = sqlite3Strlen30(zSql);
+ i64 nNew = sqlite3Strlen30(zNew);
+ i64 nSql = sqlite3Strlen30(zSql);
sqlite3 *db = sqlite3_context_db_handle(pCtx);
int rc = SQLITE_OK;
- char *zQuot;
+ char *zQuot = 0;
char *zOut;
- int nQuot;
-
- /* Set zQuot to point to a buffer containing a quoted copy of the
- ** identifier zNew. If the corresponding identifier in the original
- ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to
- ** point to zQuot so that all substitutions are made using the
- ** quoted version of the new column name. */
- zQuot = sqlite3MPrintf(db, "\"%w\"", zNew);
- if( zQuot==0 ){
- return SQLITE_NOMEM;
+ i64 nQuot = 0;
+ char *zBuf1 = 0;
+ char *zBuf2 = 0;
+
+ if( zNew ){
+ /* Set zQuot to point to a buffer containing a quoted copy of the
+ ** identifier zNew. If the corresponding identifier in the original
+ ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to
+ ** point to zQuot so that all substitutions are made using the
+ ** quoted version of the new column name. */
+ zQuot = sqlite3MPrintf(db, "\"%w\" ", zNew);
+ if( zQuot==0 ){
+ return SQLITE_NOMEM;
+ }else{
+ nQuot = sqlite3Strlen30(zQuot)-1;
+ }
+
+ assert( nQuot>=nNew );
+ zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1);
}else{
- nQuot = sqlite3Strlen30(zQuot);
- }
- if( bQuote ){
- zNew = zQuot;
- nNew = nQuot;
+ zOut = (char*)sqlite3DbMallocZero(db, (nSql*2+1) * 3);
+ if( zOut ){
+ zBuf1 = &zOut[nSql*2+1];
+ zBuf2 = &zOut[nSql*4+2];
+ }
}
/* At this point pRename->pList contains a list of RenameToken objects
** corresponding to all tokens in the input SQL that must be replaced
- ** with the new column name. All that remains is to construct and
- ** return the edited SQL string. */
- assert( nQuot>=nNew );
- zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1);
+ ** with the new column name, or with single-quoted versions of themselves.
+ ** All that remains is to construct and return the edited SQL string. */
if( zOut ){
int nOut = nSql;
memcpy(zOut, zSql, nSql);
while( pRename->pList ){
int iOff; /* Offset of token to replace in zOut */
- RenameToken *pBest = renameColumnTokenNext(pRename);
-
u32 nReplace;
const char *zReplace;
- if( sqlite3IsIdChar(*pBest->t.z) ){
- nReplace = nNew;
- zReplace = zNew;
+ RenameToken *pBest = renameColumnTokenNext(pRename);
+
+ if( zNew ){
+ if( bQuote==0 && sqlite3IsIdChar(*pBest->t.z) ){
+ nReplace = nNew;
+ zReplace = zNew;
+ }else{
+ nReplace = nQuot;
+ zReplace = zQuot;
+ if( pBest->t.z[pBest->t.n]=='"' ) nReplace++;
+ }
}else{
- nReplace = nQuot;
- zReplace = zQuot;
+ /* Dequote the double-quoted token. Then requote it again, this time
+ ** using single quotes. If the character immediately following the
+ ** original token within the input SQL was a single quote ('), then
+ ** add another space after the new, single-quoted version of the
+ ** token. This is so that (SELECT "string"'alias') maps to
+ ** (SELECT 'string' 'alias'), and not (SELECT 'string''alias'). */
+ memcpy(zBuf1, pBest->t.z, pBest->t.n);
+ zBuf1[pBest->t.n] = 0;
+ sqlite3Dequote(zBuf1);
+ sqlite3_snprintf(nSql*2, zBuf2, "%Q%s", zBuf1,
+ pBest->t.z[pBest->t.n]=='\'' ? " " : ""
+ );
+ zReplace = zBuf2;
+ nReplace = sqlite3Strlen30(zReplace);
}
iOff = pBest->t.z - zSql;
if( pBest->t.n!=nReplace ){
- memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
+ memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
nOut - (iOff + pBest->t.n)
);
nOut += nReplace - pBest->t.n;
@@ -102518,12 +116644,25 @@ static int renameEditSql(
}
/*
+** Set all pEList->a[].fg.eEName fields in the expression-list to val.
+*/
+static void renameSetENames(ExprList *pEList, int val){
+ if( pEList ){
+ int i;
+ for(i=0; i<pEList->nExpr; i++){
+ assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME );
+ pEList->a[i].fg.eEName = val;
+ }
+ }
+}
+
+/*
** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming
-** it was read from the schema of database zDb. Return SQLITE_OK if
+** it was read from the schema of database zDb. Return SQLITE_OK if
** successful. Otherwise, return an SQLite error code and leave an error
** message in the Parse object.
*/
-static int renameResolveTrigger(Parse *pParse, const char *zDb){
+static int renameResolveTrigger(Parse *pParse){
sqlite3 *db = pParse->db;
Trigger *pNew = pParse->pNewTrigger;
TriggerStep *pStep;
@@ -102533,7 +116672,7 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
assert( pNew->pTabSchema );
- pParse->pTriggerTab = sqlite3FindTable(db, pNew->table,
+ pParse->pTriggerTab = sqlite3FindTable(db, pNew->table,
db->aDb[sqlite3SchemaToIndex(db, pNew->pTabSchema)].zDbSName
);
pParse->eTriggerOp = pNew->op;
@@ -102554,27 +116693,58 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
if( pParse->nErr ) rc = pParse->rc;
}
if( rc==SQLITE_OK && pStep->zTarget ){
- Table *pTarget = sqlite3LocateTable(pParse, 0, pStep->zTarget, zDb);
- if( pTarget==0 ){
- rc = SQLITE_ERROR;
- }else if( SQLITE_OK==(rc = sqlite3ViewGetColumnNames(pParse, pTarget)) ){
- SrcList sSrc;
- memset(&sSrc, 0, sizeof(sSrc));
- sSrc.nSrc = 1;
- sSrc.a[0].zName = pStep->zTarget;
- sSrc.a[0].pTab = pTarget;
- sNC.pSrcList = &sSrc;
- if( pStep->pWhere ){
+ SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep);
+ if( pSrc ){
+ Select *pSel = sqlite3SelectNew(
+ pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0
+ );
+ if( pSel==0 ){
+ pStep->pExprList = 0;
+ pSrc = 0;
+ rc = SQLITE_NOMEM;
+ }else{
+ /* pStep->pExprList contains an expression-list used for an UPDATE
+ ** statement. So the a[].zEName values are the RHS of the
+ ** "<col> = <expr>" clauses of the UPDATE statement. So, before
+ ** running SelectPrep(), change all the eEName values in
+ ** pStep->pExprList to ENAME_SPAN (from their current value of
+ ** ENAME_NAME). This is to prevent any ids in ON() clauses that are
+ ** part of pSrc from being incorrectly resolved against the
+ ** a[].zEName values as if they were column aliases. */
+ renameSetENames(pStep->pExprList, ENAME_SPAN);
+ sqlite3SelectPrep(pParse, pSel, 0);
+ renameSetENames(pStep->pExprList, ENAME_NAME);
+ rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
+ assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList );
+ assert( pSrc==pSel->pSrc );
+ if( pStep->pExprList ) pSel->pEList = 0;
+ pSel->pSrc = 0;
+ sqlite3SelectDelete(db, pSel);
+ }
+ if( pStep->pFrom ){
+ int i;
+ for(i=0; i<pStep->pFrom->nSrc && rc==SQLITE_OK; i++){
+ SrcItem *p = &pStep->pFrom->a[i];
+ if( p->pSelect ){
+ sqlite3SelectPrep(pParse, p->pSelect, 0);
+ }
+ }
+ }
+
+ if( db->mallocFailed ){
+ rc = SQLITE_NOMEM;
+ }
+ sNC.pSrcList = pSrc;
+ if( rc==SQLITE_OK && pStep->pWhere ){
rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
}
if( rc==SQLITE_OK ){
rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
}
assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
- if( pStep->pUpsert ){
+ if( pStep->pUpsert && rc==SQLITE_OK ){
Upsert *pUpsert = pStep->pUpsert;
- assert( rc==SQLITE_OK );
- pUpsert->pUpsertSrc = &sSrc;
+ pUpsert->pUpsertSrc = pSrc;
sNC.uNC.pUpsert = pUpsert;
sNC.ncFlags = NC_UUpsert;
rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
@@ -102590,6 +116760,10 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){
}
sNC.ncFlags = 0;
}
+ sNC.pSrcList = 0;
+ sqlite3SrcListDelete(db, pSrc);
+ }else{
+ rc = SQLITE_NOMEM;
}
}
}
@@ -102618,6 +116792,12 @@ static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){
sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere);
sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere);
}
+ if( pStep->pFrom ){
+ int i;
+ for(i=0; i<pStep->pFrom->nSrc; i++){
+ sqlite3WalkSelect(pWalker, pStep->pFrom->a[i].pSelect);
+ }
+ }
}
}
@@ -102627,21 +116807,25 @@ static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){
*/
static void renameParseCleanup(Parse *pParse){
sqlite3 *db = pParse->db;
+ Index *pIdx;
if( pParse->pVdbe ){
sqlite3VdbeFinalize(pParse->pVdbe);
}
sqlite3DeleteTable(db, pParse->pNewTable);
- if( pParse->pNewIndex ) sqlite3FreeIndex(db, pParse->pNewIndex);
+ while( (pIdx = pParse->pNewIndex)!=0 ){
+ pParse->pNewIndex = pIdx->pNext;
+ sqlite3FreeIndex(db, pIdx);
+ }
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
sqlite3DbFree(db, pParse->zErrMsg);
renameTokenFree(db, pParse->pRename);
- sqlite3ParserReset(pParse);
+ sqlite3ParseObjectReset(pParse);
}
/*
** SQL function:
**
-** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld)
+** sqlite_rename_column(SQL,TYPE,OBJ,DB,TABLE,COL,NEWNAME,QUOTE,TEMP)
**
** 0. zSql: SQL statement to rewrite
** 1. type: Type of object ("table", "view" etc.)
@@ -102659,7 +116843,8 @@ static void renameParseCleanup(Parse *pParse){
**
** This function is used internally by the ALTER TABLE RENAME COLUMN command.
** It is only accessible to SQL created using sqlite3NestedParse(). It is
-** not reachable from ordinary SQL passed into sqlite3_prepare().
+** not reachable from ordinary SQL passed into sqlite3_prepare() unless the
+** SQLITE_TESTCTRL_INTERNAL_FUNCTIONS test setting is enabled.
*/
static void renameColumnFunc(
sqlite3_context *context,
@@ -102697,14 +116882,14 @@ static void renameColumnFunc(
sqlite3BtreeLeaveAll(db);
return;
}
- zOld = pTab->aCol[iCol].zName;
+ zOld = pTab->aCol[iCol].zCnName;
memset(&sCtx, 0, sizeof(sCtx));
sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol);
#ifndef SQLITE_OMIT_AUTHORIZATION
db->xAuth = 0;
#endif
- rc = renameParseSql(&sParse, zDb, 0, db, zSql, bTemp);
+ rc = renameParseSql(&sParse, zDb, db, zSql, bTemp);
/* Find tokens that need to be replaced. */
memset(&sWalker, 0, sizeof(Walker));
@@ -102716,25 +116901,27 @@ static void renameColumnFunc(
sCtx.pTab = pTab;
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
if( sParse.pNewTable ){
- Select *pSelect = sParse.pNewTable->pSelect;
- if( pSelect ){
+ if( IsView(sParse.pNewTable) ){
+ Select *pSelect = sParse.pNewTable->u.view.pSelect;
+ pSelect->selFlags &= ~SF_View;
sParse.rc = SQLITE_OK;
- sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, 0);
+ sqlite3SelectPrep(&sParse, pSelect, 0);
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
if( rc==SQLITE_OK ){
sqlite3WalkSelect(&sWalker, pSelect);
}
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
- }else{
+ }else if( IsOrdinaryTable(sParse.pNewTable) ){
/* A regular table */
int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName);
FKey *pFKey;
- assert( sParse.pNewTable->pSelect==0 );
sCtx.pTab = sParse.pNewTable;
if( bFKOnly==0 ){
- renameTokenFind(
- &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zName
- );
+ if( iCol<sParse.pNewTable->nCol ){
+ renameTokenFind(
+ &sParse, &sCtx, (void*)sParse.pNewTable->aCol[iCol].zCnName
+ );
+ }
if( sCtx.iCol<0 ){
renameTokenFind(&sParse, &sCtx, (void*)&sParse.pNewTable->iPKey);
}
@@ -102742,9 +116929,20 @@ static void renameColumnFunc(
for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){
sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
}
+ for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){
+ sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
+ }
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ for(i=0; i<sParse.pNewTable->nCol; i++){
+ Expr *pExpr = sqlite3ColumnExpr(sParse.pNewTable,
+ &sParse.pNewTable->aCol[i]);
+ sqlite3WalkExpr(&sWalker, pExpr);
+ }
+#endif
}
- for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+ assert( IsOrdinaryTable(sParse.pNewTable) );
+ for(pFKey=sParse.pNewTable->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
for(i=0; i<pFKey->nCol; i++){
if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){
renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]);
@@ -102763,11 +116961,11 @@ static void renameColumnFunc(
}else{
/* A trigger */
TriggerStep *pStep;
- rc = renameResolveTrigger(&sParse, (bTemp ? 0 : zDb));
+ rc = renameResolveTrigger(&sParse);
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
- if( pStep->zTarget ){
+ if( pStep->zTarget ){
Table *pTarget = sqlite3LocateTable(&sParse, 0, pStep->zTarget, zDb);
if( pTarget==pTab ){
if( pStep->pUpsert ){
@@ -102795,8 +116993,10 @@ static void renameColumnFunc(
renameColumnFunc_done:
if( rc!=SQLITE_OK ){
- if( sParse.zErrMsg ){
- renameColumnParseError(context, 0, argv[1], argv[2], &sParse);
+ if( rc==SQLITE_ERROR && sqlite3WritableSchema(db) ){
+ sqlite3_result_value(context, argv[0]);
+ }else if( sParse.zErrMsg ){
+ renameColumnParseError(context, "", argv[1], argv[2], &sParse);
}else{
sqlite3_result_error_code(context, rc);
}
@@ -102811,29 +117011,42 @@ renameColumnFunc_done:
}
/*
-** Walker expression callback used by "RENAME TABLE".
+** Walker expression callback used by "RENAME TABLE".
*/
static int renameTableExprCb(Walker *pWalker, Expr *pExpr){
RenameCtx *p = pWalker->u.pRename;
- if( pExpr->op==TK_COLUMN && p->pTab==pExpr->y.pTab ){
+ if( pExpr->op==TK_COLUMN
+ && ALWAYS(ExprUseYTab(pExpr))
+ && p->pTab==pExpr->y.pTab
+ ){
renameTokenFind(pWalker->pParse, p, (void*)&pExpr->y.pTab);
}
return WRC_Continue;
}
/*
-** Walker select callback used by "RENAME TABLE".
+** Walker select callback used by "RENAME TABLE".
*/
static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
int i;
RenameCtx *p = pWalker->u.pRename;
SrcList *pSrc = pSelect->pSrc;
+ if( pSelect->selFlags & (SF_View|SF_CopyCte) ){
+ testcase( pSelect->selFlags & SF_View );
+ testcase( pSelect->selFlags & SF_CopyCte );
+ return WRC_Prune;
+ }
+ if( NEVER(pSrc==0) ){
+ assert( pWalker->pParse->db->mallocFailed );
+ return WRC_Abort;
+ }
for(i=0; i<pSrc->nSrc; i++){
- struct SrcList_item *pItem = &pSrc->a[i];
+ SrcItem *pItem = &pSrc->a[i];
if( pItem->pTab==p->pTab ){
renameTokenFind(pWalker->pParse, p, pItem->zName);
}
}
+ renameWalkWith(pWalker, pSelect);
return WRC_Continue;
}
@@ -102842,7 +117055,7 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
/*
** This C function implements an SQL user function that is used by SQL code
** generated by the ALTER TABLE ... RENAME command to modify the definition
-** of any foreign key constraints that use the table being renamed as the
+** of any foreign key constraints that use the table being renamed as the
** parent table. It is passed three arguments:
**
** 0: The database containing the table being renamed.
@@ -102893,29 +117106,38 @@ static void renameTableFunc(
sWalker.xSelectCallback = renameTableSelectCb;
sWalker.u.pRename = &sCtx;
- rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp);
+ rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
if( rc==SQLITE_OK ){
int isLegacy = (db->flags & SQLITE_LegacyAlter);
if( sParse.pNewTable ){
Table *pTab = sParse.pNewTable;
- if( pTab->pSelect ){
+ if( IsView(pTab) ){
if( isLegacy==0 ){
+ Select *pSelect = pTab->u.view.pSelect;
NameContext sNC;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = &sParse;
- sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC);
- if( sParse.nErr ) rc = sParse.rc;
- sqlite3WalkSelect(&sWalker, pTab->pSelect);
+ assert( pSelect->selFlags & SF_View );
+ pSelect->selFlags &= ~SF_View;
+ sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC);
+ if( sParse.nErr ){
+ rc = sParse.rc;
+ }else{
+ sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect);
+ }
}
}else{
/* Modify any FK definitions to point to the new table. */
#ifndef SQLITE_OMIT_FOREIGN_KEY
- if( isLegacy==0 || (db->flags & SQLITE_ForeignKeys) ){
+ if( (isLegacy==0 || (db->flags & SQLITE_ForeignKeys))
+ && !IsVirtual(pTab)
+ ){
FKey *pFKey;
- for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+ assert( IsOrdinaryTable(pTab) );
+ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){
renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo);
}
@@ -102947,20 +117169,29 @@ static void renameTableFunc(
else{
Trigger *pTrigger = sParse.pNewTrigger;
TriggerStep *pStep;
- if( 0==sqlite3_stricmp(sParse.pNewTrigger->table, zOld)
+ if( 0==sqlite3_stricmp(sParse.pNewTrigger->table, zOld)
&& sCtx.pTab->pSchema==pTrigger->pTabSchema
){
renameTokenFind(&sParse, &sCtx, sParse.pNewTrigger->table);
}
if( isLegacy==0 ){
- rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb);
+ rc = renameResolveTrigger(&sParse);
if( rc==SQLITE_OK ){
renameWalkTrigger(&sWalker, pTrigger);
for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){
renameTokenFind(&sParse, &sCtx, pStep->zTarget);
}
+ if( pStep->pFrom ){
+ int i;
+ for(i=0; i<pStep->pFrom->nSrc; i++){
+ SrcItem *pItem = &pStep->pFrom->a[i];
+ if( 0==sqlite3_stricmp(pItem->zName, zOld) ){
+ renameTokenFind(&sParse, &sCtx, pItem->zName);
+ }
+ }
+ }
}
}
}
@@ -102972,8 +117203,10 @@ static void renameTableFunc(
rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote);
}
if( rc!=SQLITE_OK ){
- if( sParse.zErrMsg ){
- renameColumnParseError(context, 0, argv[1], argv[2], &sParse);
+ if( rc==SQLITE_ERROR && sqlite3WritableSchema(db) ){
+ sqlite3_result_value(context, argv[3]);
+ }else if( sParse.zErrMsg ){
+ renameColumnParseError(context, "", argv[1], argv[2], &sParse);
}else{
sqlite3_result_error_code(context, rc);
}
@@ -102990,7 +117223,131 @@ static void renameTableFunc(
return;
}
-/*
+static int renameQuotefixExprCb(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_STRING && (pExpr->flags & EP_DblQuoted) ){
+ renameTokenFind(pWalker->pParse, pWalker->u.pRename, (const void*)pExpr);
+ }
+ return WRC_Continue;
+}
+
+/* SQL function: sqlite_rename_quotefix(DB,SQL)
+**
+** Rewrite the DDL statement "SQL" so that any string literals that use
+** double-quotes use single quotes instead.
+**
+** Two arguments must be passed:
+**
+** 0: Database name ("main", "temp" etc.).
+** 1: SQL statement to edit.
+**
+** The returned value is the modified SQL statement. For example, given
+** the database schema:
+**
+** CREATE TABLE t1(a, b, c);
+**
+** SELECT sqlite_rename_quotefix('main',
+** 'CREATE VIEW v1 AS SELECT "a", "string" FROM t1'
+** );
+**
+** returns the string:
+**
+** CREATE VIEW v1 AS SELECT "a", 'string' FROM t1
+**
+** If there is a error in the input SQL, then raise an error, except
+** if PRAGMA writable_schema=ON, then just return the input string
+** unmodified following an error.
+*/
+static void renameQuotefixFunc(
+ sqlite3_context *context,
+ int NotUsed,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ char const *zDb = (const char*)sqlite3_value_text(argv[0]);
+ char const *zInput = (const char*)sqlite3_value_text(argv[1]);
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth = db->xAuth;
+ db->xAuth = 0;
+#endif
+
+ sqlite3BtreeEnterAll(db);
+
+ UNUSED_PARAMETER(NotUsed);
+ if( zDb && zInput ){
+ int rc;
+ Parse sParse;
+ rc = renameParseSql(&sParse, zDb, db, zInput, 0);
+
+ if( rc==SQLITE_OK ){
+ RenameCtx sCtx;
+ Walker sWalker;
+
+ /* Walker to find tokens that need to be replaced. */
+ memset(&sCtx, 0, sizeof(RenameCtx));
+ memset(&sWalker, 0, sizeof(Walker));
+ sWalker.pParse = &sParse;
+ sWalker.xExprCallback = renameQuotefixExprCb;
+ sWalker.xSelectCallback = renameColumnSelectCb;
+ sWalker.u.pRename = &sCtx;
+
+ if( sParse.pNewTable ){
+ if( IsView(sParse.pNewTable) ){
+ Select *pSelect = sParse.pNewTable->u.view.pSelect;
+ pSelect->selFlags &= ~SF_View;
+ sParse.rc = SQLITE_OK;
+ sqlite3SelectPrep(&sParse, pSelect, 0);
+ rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
+ if( rc==SQLITE_OK ){
+ sqlite3WalkSelect(&sWalker, pSelect);
+ }
+ }else{
+ int i;
+ sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck);
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ for(i=0; i<sParse.pNewTable->nCol; i++){
+ sqlite3WalkExpr(&sWalker,
+ sqlite3ColumnExpr(sParse.pNewTable,
+ &sParse.pNewTable->aCol[i]));
+ }
+#endif /* SQLITE_OMIT_GENERATED_COLUMNS */
+ }
+ }else if( sParse.pNewIndex ){
+ sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr);
+ sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
+ }else{
+#ifndef SQLITE_OMIT_TRIGGER
+ rc = renameResolveTrigger(&sParse);
+ if( rc==SQLITE_OK ){
+ renameWalkTrigger(&sWalker, sParse.pNewTrigger);
+ }
+#endif /* SQLITE_OMIT_TRIGGER */
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = renameEditSql(context, &sCtx, zInput, 0, 0);
+ }
+ renameTokenFree(db, sCtx.pList);
+ }
+ if( rc!=SQLITE_OK ){
+ if( sqlite3WritableSchema(db) && rc==SQLITE_ERROR ){
+ sqlite3_result_value(context, argv[1]);
+ }else{
+ sqlite3_result_error_code(context, rc);
+ }
+ }
+ renameParseCleanup(&sParse);
+ }
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+#endif
+
+ sqlite3BtreeLeaveAll(db);
+}
+
+/* Function: sqlite_rename_test(DB,SQL,TYPE,NAME,ISTEMP,WHEN,DQS)
+**
** An SQL user function that checks that there are no parse or symbol
** resolution problems in a CREATE TRIGGER|TABLE|VIEW|INDEX statement.
** After an ALTER TABLE .. RENAME operation is performed and the schema
@@ -103002,12 +117359,16 @@ static void renameTableFunc(
** 2: Object type ("view", "table", "trigger" or "index").
** 3: Object name.
** 4: True if object is from temp schema.
+** 5: "when" part of error message.
+** 6: True to disable the DQS quirk when parsing SQL.
**
-** Unless it finds an error, this function normally returns NULL. However, it
-** returns integer value 1 if:
+** The return value is computed as follows:
**
-** * the SQL argument creates a trigger, and
-** * the table that the trigger is attached to is in database zDb.
+** A. If an error is seen and not in PRAGMA writable_schema=ON mode,
+** then raise the error.
+** B. Else if a trigger is created and the the table that the trigger is
+** attached to is in database zDb, then return 1.
+** C. Otherwise return NULL.
*/
static void renameTableTest(
sqlite3_context *context,
@@ -103019,6 +117380,8 @@ static void renameTableTest(
char const *zInput = (const char*)sqlite3_value_text(argv[1]);
int bTemp = sqlite3_value_int(argv[4]);
int isLegacy = (db->flags & SQLITE_LegacyAlter);
+ char const *zWhen = (const char*)sqlite3_value_text(argv[5]);
+ int bNoDQS = sqlite3_value_int(argv[6]);
#ifndef SQLITE_OMIT_AUTHORIZATION
sqlite3_xauth xAuth = db->xAuth;
@@ -103026,33 +117389,41 @@ static void renameTableTest(
#endif
UNUSED_PARAMETER(NotUsed);
+
if( zDb && zInput ){
int rc;
Parse sParse;
- rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp);
+ int flags = db->flags;
+ if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL);
+ rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
+ db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL));
if( rc==SQLITE_OK ){
- if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){
+ if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){
NameContext sNC;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = &sParse;
- sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC);
+ sqlite3SelectPrep(&sParse, sParse.pNewTable->u.view.pSelect, &sNC);
if( sParse.nErr ) rc = sParse.rc;
}
else if( sParse.pNewTrigger ){
if( isLegacy==0 ){
- rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb);
+ rc = renameResolveTrigger(&sParse);
}
if( rc==SQLITE_OK ){
int i1 = sqlite3SchemaToIndex(db, sParse.pNewTrigger->pTabSchema);
int i2 = sqlite3FindDbName(db, zDb);
- if( i1==i2 ) sqlite3_result_int(context, 1);
+ if( i1==i2 ){
+ /* Handle output case B */
+ sqlite3_result_int(context, 1);
+ }
}
}
}
- if( rc!=SQLITE_OK ){
- renameColumnParseError(context, 1, argv[2], argv[3], &sParse);
+ if( rc!=SQLITE_OK && zWhen && !sqlite3WritableSchema(db) ){
+ /* Output case A */
+ renameColumnParseError(context, zWhen, argv[2], argv[3],&sParse);
}
renameParseCleanup(&sParse);
}
@@ -103063,13 +117434,231 @@ static void renameTableTest(
}
/*
+** The implementation of internal UDF sqlite_drop_column().
+**
+** Arguments:
+**
+** argv[0]: An integer - the index of the schema containing the table
+** argv[1]: CREATE TABLE statement to modify.
+** argv[2]: An integer - the index of the column to remove.
+**
+** The value returned is a string containing the CREATE TABLE statement
+** with column argv[2] removed.
+*/
+static void dropColumnFunc(
+ sqlite3_context *context,
+ int NotUsed,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ int iSchema = sqlite3_value_int(argv[0]);
+ const char *zSql = (const char*)sqlite3_value_text(argv[1]);
+ int iCol = sqlite3_value_int(argv[2]);
+ const char *zDb = db->aDb[iSchema].zDbSName;
+ int rc;
+ Parse sParse;
+ RenameToken *pCol;
+ Table *pTab;
+ const char *zEnd;
+ char *zNew = 0;
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth = db->xAuth;
+ db->xAuth = 0;
+#endif
+
+ UNUSED_PARAMETER(NotUsed);
+ rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1);
+ if( rc!=SQLITE_OK ) goto drop_column_done;
+ pTab = sParse.pNewTable;
+ if( pTab==0 || pTab->nCol==1 || iCol>=pTab->nCol ){
+ /* This can happen if the sqlite_schema table is corrupt */
+ rc = SQLITE_CORRUPT_BKPT;
+ goto drop_column_done;
+ }
+
+ pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName);
+ if( iCol<pTab->nCol-1 ){
+ RenameToken *pEnd;
+ pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zCnName);
+ zEnd = (const char*)pEnd->t.z;
+ }else{
+ assert( IsOrdinaryTable(pTab) );
+ zEnd = (const char*)&zSql[pTab->u.tab.addColOffset];
+ while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--;
+ }
+
+ zNew = sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd);
+ sqlite3_result_text(context, zNew, -1, SQLITE_TRANSIENT);
+ sqlite3_free(zNew);
+
+drop_column_done:
+ renameParseCleanup(&sParse);
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+#endif
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error_code(context, rc);
+ }
+}
+
+/*
+** This function is called by the parser upon parsing an
+**
+** ALTER TABLE pSrc DROP COLUMN pName
+**
+** statement. Argument pSrc contains the possibly qualified name of the
+** table being edited, and token pName the name of the column to drop.
+*/
+SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const Token *pName){
+ sqlite3 *db = pParse->db; /* Database handle */
+ Table *pTab; /* Table to modify */
+ int iDb; /* Index of db containing pTab in aDb[] */
+ const char *zDb; /* Database containing pTab ("main" etc.) */
+ char *zCol = 0; /* Name of column to drop */
+ int iCol; /* Index of column zCol in pTab->aCol[] */
+
+ /* Look up the table being altered. */
+ assert( pParse->pNewTable==0 );
+ assert( sqlite3BtreeHoldsAllMutexes(db) );
+ if( NEVER(db->mallocFailed) ) goto exit_drop_column;
+ pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
+ if( !pTab ) goto exit_drop_column;
+
+ /* Make sure this is not an attempt to ALTER a view, virtual table or
+ ** system table. */
+ if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_drop_column;
+ if( SQLITE_OK!=isRealTable(pParse, pTab, 1) ) goto exit_drop_column;
+
+ /* Find the index of the column being dropped. */
+ zCol = sqlite3NameFromToken(db, pName);
+ if( zCol==0 ){
+ assert( db->mallocFailed );
+ goto exit_drop_column;
+ }
+ iCol = sqlite3ColumnIndex(pTab, zCol);
+ if( iCol<0 ){
+ sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pName);
+ goto exit_drop_column;
+ }
+
+ /* Do not allow the user to drop a PRIMARY KEY column or a column
+ ** constrained by a UNIQUE constraint. */
+ if( pTab->aCol[iCol].colFlags & (COLFLAG_PRIMKEY|COLFLAG_UNIQUE) ){
+ sqlite3ErrorMsg(pParse, "cannot drop %s column: \"%s\"",
+ (pTab->aCol[iCol].colFlags&COLFLAG_PRIMKEY) ? "PRIMARY KEY" : "UNIQUE",
+ zCol
+ );
+ goto exit_drop_column;
+ }
+
+ /* Do not allow the number of columns to go to zero */
+ if( pTab->nCol<=1 ){
+ sqlite3ErrorMsg(pParse, "cannot drop column \"%s\": no other columns exist",zCol);
+ goto exit_drop_column;
+ }
+
+ /* Edit the sqlite_schema table */
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ assert( iDb>=0 );
+ zDb = db->aDb[iDb].zDbSName;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ /* Invoke the authorization callback. */
+ if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){
+ goto exit_drop_column;
+ }
+#endif
+ renameTestSchema(pParse, zDb, iDb==1, "", 0);
+ renameFixQuotes(pParse, zDb, iDb==1);
+ sqlite3NestedParse(pParse,
+ "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET "
+ "sql = sqlite_drop_column(%d, sql, %d) "
+ "WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)"
+ , zDb, iDb, iCol, pTab->zName
+ );
+
+ /* Drop and reload the database schema. */
+ renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop);
+ renameTestSchema(pParse, zDb, iDb==1, "after drop column", 1);
+
+ /* Edit rows of table on disk */
+ if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){
+ int i;
+ int addr;
+ int reg;
+ int regRec;
+ Index *pPk = 0;
+ int nField = 0; /* Number of non-virtual columns after drop */
+ int iCur;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ iCur = pParse->nTab++;
+ sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
+ addr = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
+ reg = ++pParse->nMem;
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, reg);
+ pParse->nMem += pTab->nCol;
+ }else{
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ pParse->nMem += pPk->nColumn;
+ for(i=0; i<pPk->nKeyCol; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, i, reg+i+1);
+ }
+ nField = pPk->nKeyCol;
+ }
+ regRec = ++pParse->nMem;
+ for(i=0; i<pTab->nCol; i++){
+ if( i!=iCol && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){
+ int regOut;
+ if( pPk ){
+ int iPos = sqlite3TableColumnToIndex(pPk, i);
+ int iColPos = sqlite3TableColumnToIndex(pPk, iCol);
+ if( iPos<pPk->nKeyCol ) continue;
+ regOut = reg+1+iPos-(iPos>iColPos);
+ }else{
+ regOut = reg+1+nField;
+ }
+ if( i==pTab->iPKey ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regOut);
+ }else{
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut);
+ }
+ nField++;
+ }
+ }
+ if( nField==0 ){
+ /* dbsqlfuzz 5f09e7bcc78b4954d06bf9f2400d7715f48d1fef */
+ pParse->nMem++;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, reg+1);
+ nField = 1;
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg+1, nField, regRec);
+ if( pPk ){
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iCur, regRec, reg+1, pPk->nKeyCol);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, reg);
+ }
+ sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION);
+
+ sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addr);
+ }
+
+exit_drop_column:
+ sqlite3DbFree(db, zCol);
+ sqlite3SrcListDelete(db, pSrc);
+}
+
+/*
** Register built-in functions used to help implement ALTER TABLE
*/
SQLITE_PRIVATE void sqlite3AlterFunctions(void){
static FuncDef aAlterTableFuncs[] = {
- INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
- INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
- INTERNAL_FUNCTION(sqlite_rename_test, 5, renameTableTest),
+ INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
+ INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
+ INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest),
+ INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc),
+ INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc),
};
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
}
@@ -103106,13 +117695,13 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){
** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled
** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated.
** The sqlite_stat2 table is superseded by sqlite_stat3, which is only
-** created and used by SQLite versions 3.7.9 and later and with
+** created and used by SQLite versions 3.7.9 through 3.29.0 when
** SQLITE_ENABLE_STAT3 defined. The functionality of sqlite_stat3
-** is a superset of sqlite_stat2. The sqlite_stat4 is an enhanced
-** version of sqlite_stat3 and is only available when compiled with
-** SQLITE_ENABLE_STAT4 and in SQLite versions 3.8.1 and later. It is
-** not possible to enable both STAT3 and STAT4 at the same time. If they
-** are both enabled, then STAT4 takes precedence.
+** is a superset of sqlite_stat2 and is also now deprecated. The
+** sqlite_stat4 is an enhanced version of sqlite_stat3 and is only
+** available when compiled with SQLITE_ENABLE_STAT4 and in SQLite
+** versions 3.8.1 and later. STAT4 is the only variant that is still
+** supported.
**
** For most applications, sqlite_stat1 provides all the statistics required
** for the query planner to make good choices.
@@ -103128,7 +117717,7 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){
** integer is the average number of rows in the index that have the same
** value in the first column of the index. The third integer is the average
** number of rows in the index that have the same value for the first two
-** columns. The N-th integer (for N>1) is the average number of rows in
+** columns. The N-th integer (for N>1) is the average number of rows in
** the index which have the same value for the first N-1 columns. For
** a K-column index, there will be K+1 integers in the stat column. If
** the index is unique, then the last integer will be 1.
@@ -103138,7 +117727,7 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){
** must be separated from the last integer by a single space. If the
** "unordered" keyword is present, then the query planner assumes that
** the index is unordered and will not use the index for a range query.
-**
+**
** If the sqlite_stat1.idx column is NULL, then the sqlite_stat1.stat
** column contains a single integer which is the (estimated) number of
** rows in the table identified by sqlite_stat1.tbl.
@@ -103196,9 +117785,9 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){
** number of entries that are strictly less than the sample. The first
** integer in nLt contains the number of entries in the index where the
** left-most column is less than the left-most column of the sample.
-** The K-th integer in the nLt entry is the number of index entries
+** The K-th integer in the nLt entry is the number of index entries
** where the first K columns are less than the first K columns of the
-** sample. The nDLt column is like nLt except that it contains the
+** sample. The nDLt column is like nLt except that it contains the
** number of distinct entries in the index that are less than the
** sample.
**
@@ -103223,17 +117812,11 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){
#if defined(SQLITE_ENABLE_STAT4)
# define IsStat4 1
-# define IsStat3 0
-#elif defined(SQLITE_ENABLE_STAT3)
-# define IsStat4 0
-# define IsStat3 1
#else
# define IsStat4 0
-# define IsStat3 0
# undef SQLITE_STAT4_SAMPLES
# define SQLITE_STAT4_SAMPLES 1
#endif
-#define IsStat34 (IsStat3+IsStat4) /* 1 for STAT3 or STAT4. 0 otherwise */
/*
** This routine generates code that opens the sqlite_statN tables.
@@ -103262,21 +117845,22 @@ static void openStatTable(
{ "sqlite_stat1", "tbl,idx,stat" },
#if defined(SQLITE_ENABLE_STAT4)
{ "sqlite_stat4", "tbl,idx,neq,nlt,ndlt,sample" },
- { "sqlite_stat3", 0 },
-#elif defined(SQLITE_ENABLE_STAT3)
- { "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" },
- { "sqlite_stat4", 0 },
#else
- { "sqlite_stat3", 0 },
{ "sqlite_stat4", 0 },
#endif
+ { "sqlite_stat3", 0 },
};
int i;
sqlite3 *db = pParse->db;
Db *pDb;
Vdbe *v = sqlite3GetVdbe(pParse);
- int aRoot[ArraySize(aTable)];
+ u32 aRoot[ArraySize(aTable)];
u8 aCreateTbl[ArraySize(aTable)];
+#ifdef SQLITE_ENABLE_STAT4
+ const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1;
+#else
+ const int nToOpen = 1;
+#endif
if( v==0 ) return;
assert( sqlite3BtreeHoldsAllMutexes(db) );
@@ -103289,24 +117873,24 @@ static void openStatTable(
for(i=0; i<ArraySize(aTable); i++){
const char *zTab = aTable[i].zName;
Table *pStat;
+ aCreateTbl[i] = 0;
if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
- if( aTable[i].zCols ){
- /* The sqlite_statN table does not exist. Create it. Note that a
- ** side-effect of the CREATE TABLE statement is to leave the rootpage
- ** of the new table in register pParse->regRoot. This is important
+ if( i<nToOpen ){
+ /* The sqlite_statN table does not exist. Create it. Note that a
+ ** side-effect of the CREATE TABLE statement is to leave the rootpage
+ ** of the new table in register pParse->regRoot. This is important
** because the OpenWrite opcode below will be needing it. */
sqlite3NestedParse(pParse,
"CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
);
- aRoot[i] = pParse->regRoot;
+ aRoot[i] = (u32)pParse->regRoot;
aCreateTbl[i] = OPFLAG_P2ISREG;
}
}else{
- /* The table already exists. If zWhere is not NULL, delete all entries
+ /* The table already exists. If zWhere is not NULL, delete all entries
** associated with the table zWhere. If zWhere is NULL, delete the
** entire contents of the table. */
aRoot[i] = pStat->tnum;
- aCreateTbl[i] = 0;
sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
if( zWhere ){
sqlite3NestedParse(pParse,
@@ -103319,15 +117903,15 @@ static void openStatTable(
#endif
}else{
/* The sqlite_stat[134] table already exists. Delete all rows. */
- sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb);
+ sqlite3VdbeAddOp2(v, OP_Clear, (int)aRoot[i], iDb);
}
}
}
/* Open the sqlite_stat[134] tables for writing. */
- for(i=0; aTable[i].zCols; i++){
+ for(i=0; i<nToOpen; i++){
assert( i<ArraySize(aTable) );
- sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3);
+ sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, (int)aRoot[i], iDb, 3);
sqlite3VdbeChangeP5(v, aCreateTbl[i]);
VdbeComment((v, aTable[i].zName));
}
@@ -103345,12 +117929,12 @@ static void openStatTable(
** share an instance of the following structure to hold their state
** information.
*/
-typedef struct Stat4Accum Stat4Accum;
-typedef struct Stat4Sample Stat4Sample;
-struct Stat4Sample {
- tRowcnt *anEq; /* sqlite_stat4.nEq */
+typedef struct StatAccum StatAccum;
+typedef struct StatSample StatSample;
+struct StatSample {
tRowcnt *anDLt; /* sqlite_stat4.nDLt */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
+ tRowcnt *anEq; /* sqlite_stat4.nEq */
tRowcnt *anLt; /* sqlite_stat4.nLt */
union {
i64 iRowid; /* Rowid in main table of the key */
@@ -103361,28 +117945,33 @@ struct Stat4Sample {
int iCol; /* If !isPSample, the reason for inclusion */
u32 iHash; /* Tiebreaker hash */
#endif
-};
-struct Stat4Accum {
- tRowcnt nRow; /* Number of rows in the entire table */
- tRowcnt nPSample; /* How often to do a periodic sample */
+};
+struct StatAccum {
+ sqlite3 *db; /* Database connection, for malloc() */
+ tRowcnt nEst; /* Estimated number of rows */
+ tRowcnt nRow; /* Number of rows visited so far */
+ int nLimit; /* Analysis row-scan limit */
int nCol; /* Number of columns in index + pk/rowid */
int nKeyCol; /* Number of index columns w/o the pk/rowid */
+ u8 nSkipAhead; /* Number of times of skip-ahead */
+ StatSample current; /* Current row as a StatSample */
+#ifdef SQLITE_ENABLE_STAT4
+ tRowcnt nPSample; /* How often to do a periodic sample */
int mxSample; /* Maximum number of samples to accumulate */
- Stat4Sample current; /* Current row as a Stat4Sample */
u32 iPrn; /* Pseudo-random number used for sampling */
- Stat4Sample *aBest; /* Array of nCol best samples */
+ StatSample *aBest; /* Array of nCol best samples */
int iMin; /* Index in a[] of entry with minimum score */
int nSample; /* Current number of samples */
int nMaxEqZero; /* Max leading 0 in anEq[] for any a[] entry */
int iGet; /* Index of current sample accessed by stat_get() */
- Stat4Sample *a; /* Array of mxSample Stat4Sample objects */
- sqlite3 *db; /* Database connection, for malloc() */
+ StatSample *a; /* Array of mxSample StatSample objects */
+#endif
};
-/* Reclaim memory used by a Stat4Sample
+/* Reclaim memory used by a StatSample
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-static void sampleClear(sqlite3 *db, Stat4Sample *p){
+#ifdef SQLITE_ENABLE_STAT4
+static void sampleClear(sqlite3 *db, StatSample *p){
assert( db!=0 );
if( p->nRowid ){
sqlite3DbFree(db, p->u.aRowid);
@@ -103393,8 +117982,8 @@ static void sampleClear(sqlite3 *db, Stat4Sample *p){
/* Initialize the BLOB value of a ROWID
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
+#ifdef SQLITE_ENABLE_STAT4
+static void sampleSetRowid(sqlite3 *db, StatSample *p, int n, const u8 *pData){
assert( db!=0 );
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
p->u.aRowid = sqlite3DbMallocRawNN(db, n);
@@ -103409,8 +117998,8 @@ static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
/* Initialize the INTEGER value of a ROWID.
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
+#ifdef SQLITE_ENABLE_STAT4
+static void sampleSetRowidInt64(sqlite3 *db, StatSample *p, i64 iRowid){
assert( db!=0 );
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
p->nRowid = 0;
@@ -103422,8 +118011,8 @@ static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
/*
** Copy the contents of object (*pFrom) into (*pTo).
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
+#ifdef SQLITE_ENABLE_STAT4
+static void sampleCopy(StatAccum *p, StatSample *pTo, StatSample *pFrom){
pTo->isPSample = pFrom->isPSample;
pTo->iCol = pFrom->iCol;
pTo->iHash = pFrom->iHash;
@@ -103439,40 +118028,41 @@ static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
#endif
/*
-** Reclaim all memory of a Stat4Accum structure.
+** Reclaim all memory of a StatAccum structure.
*/
-static void stat4Destructor(void *pOld){
- Stat4Accum *p = (Stat4Accum*)pOld;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- int i;
- for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
- for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
- sampleClear(p->db, &p->current);
+static void statAccumDestructor(void *pOld){
+ StatAccum *p = (StatAccum*)pOld;
+#ifdef SQLITE_ENABLE_STAT4
+ if( p->mxSample ){
+ int i;
+ for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
+ for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
+ sampleClear(p->db, &p->current);
+ }
#endif
sqlite3DbFree(p->db, p);
}
/*
-** Implementation of the stat_init(N,K,C) SQL function. The three parameters
+** Implementation of the stat_init(N,K,C,L) SQL function. The four parameters
** are:
** N: The number of columns in the index including the rowid/pk (note 1)
** K: The number of columns in the index excluding the rowid/pk.
-** C: The number of rows in the index (note 2)
+** C: Estimated number of rows in the index
+** L: A limit on the number of rows to scan, or 0 for no-limit
**
** Note 1: In the special case of the covering index that implements a
** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
** total number of columns in the table.
**
-** Note 2: C is only used for STAT3 and STAT4.
-**
** For indexes on ordinary rowid tables, N==K+1. But for indexes on
** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
** PRIMARY KEY of the table. The covering index that implements the
** original WITHOUT ROWID table as N==K as a special case.
**
-** This routine allocates the Stat4Accum object in heap memory. The return
-** value is a pointer to the Stat4Accum object. The datatype of the
-** return value is BLOB, but it is really just a pointer to the Stat4Accum
+** This routine allocates the StatAccum object in heap memory. The return
+** value is a pointer to the StatAccum object. The datatype of the
+** return value is BLOB, but it is really just a pointer to the StatAccum
** object.
*/
static void statInit(
@@ -103480,14 +118070,15 @@ static void statInit(
int argc,
sqlite3_value **argv
){
- Stat4Accum *p;
+ StatAccum *p;
int nCol; /* Number of columns in index being sampled */
int nKeyCol; /* Number of key columns */
int nColUp; /* nCol rounded up for alignment */
int n; /* Bytes of space to allocate */
- sqlite3 *db; /* Database connection */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- int mxSample = SQLITE_STAT4_SAMPLES;
+ sqlite3 *db = sqlite3_context_db_handle(context); /* Database connection */
+#ifdef SQLITE_ENABLE_STAT4
+ /* Maximum number of samples. 0 if STAT4 data is not collected */
+ int mxSample = OptimizationEnabled(db,SQLITE_Stat4) ?SQLITE_STAT4_SAMPLES :0;
#endif
/* Decode the three function arguments */
@@ -103499,17 +118090,17 @@ static void statInit(
assert( nKeyCol<=nCol );
assert( nKeyCol>0 );
- /* Allocate the space required for the Stat4Accum object */
- n = sizeof(*p)
- + sizeof(tRowcnt)*nColUp /* Stat4Accum.anEq */
- + sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- + sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */
- + sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */
- + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
+ /* Allocate the space required for the StatAccum object */
+ n = sizeof(*p)
+ + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */
+#ifdef SQLITE_ENABLE_STAT4
+ n += sizeof(tRowcnt)*nColUp; /* StatAccum.anEq */
+ if( mxSample ){
+ n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
+ + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
+ + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample);
+ }
#endif
- ;
- db = sqlite3_context_db_handle(context);
p = sqlite3DbMallocZero(db, n);
if( p==0 ){
sqlite3_result_error_nomem(context);
@@ -103517,25 +118108,28 @@ static void statInit(
}
p->db = db;
+ p->nEst = sqlite3_value_int64(argv[2]);
p->nRow = 0;
+ p->nLimit = sqlite3_value_int64(argv[3]);
p->nCol = nCol;
p->nKeyCol = nKeyCol;
+ p->nSkipAhead = 0;
p->current.anDLt = (tRowcnt*)&p[1];
- p->current.anEq = &p->current.anDLt[nColUp];
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- {
+#ifdef SQLITE_ENABLE_STAT4
+ p->current.anEq = &p->current.anDLt[nColUp];
+ p->mxSample = p->nLimit==0 ? mxSample : 0;
+ if( mxSample ){
u8 *pSpace; /* Allocated space not yet assigned */
int i; /* Used to iterate through p->aSample[] */
p->iGet = -1;
- p->mxSample = mxSample;
- p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1);
+ p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1);
p->current.anLt = &p->current.anEq[nColUp];
p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]);
-
- /* Set up the Stat4Accum.a[] and aBest[] arrays */
- p->a = (struct Stat4Sample*)&p->current.anLt[nColUp];
+
+ /* Set up the StatAccum.a[] and aBest[] arrays */
+ p->a = (struct StatSample*)&p->current.anLt[nColUp];
p->aBest = &p->a[mxSample];
pSpace = (u8*)(&p->a[mxSample+nCol]);
for(i=0; i<(mxSample+nCol); i++){
@@ -103544,7 +118138,7 @@ static void statInit(
p->a[i].anDLt = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp);
}
assert( (pSpace - (u8*)p)==n );
-
+
for(i=0; i<nCol; i++){
p->aBest[i].iCol = i;
}
@@ -103555,10 +118149,10 @@ static void statInit(
** only the pointer (the 2nd parameter) matters. The size of the object
** (given by the 3rd parameter) is never used and can be any positive
** value. */
- sqlite3_result_blob(context, p, sizeof(*p), stat4Destructor);
+ sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor);
}
static const FuncDef statInitFuncdef = {
- 2+IsStat34, /* nArg */
+ 4, /* nArg */
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
@@ -103571,20 +118165,20 @@ static const FuncDef statInitFuncdef = {
#ifdef SQLITE_ENABLE_STAT4
/*
-** pNew and pOld are both candidate non-periodic samples selected for
-** the same column (pNew->iCol==pOld->iCol). Ignoring this column and
+** pNew and pOld are both candidate non-periodic samples selected for
+** the same column (pNew->iCol==pOld->iCol). Ignoring this column and
** considering only any trailing columns and the sample hash value, this
** function returns true if sample pNew is to be preferred over pOld.
** In other words, if we assume that the cardinalities of the selected
** column for pNew and pOld are equal, is pNew to be preferred over pOld.
**
** This function assumes that for each argument sample, the contents of
-** the anEq[] array from pSample->anEq[pSample->iCol+1] onwards are valid.
+** the anEq[] array from pSample->anEq[pSample->iCol+1] onwards are valid.
*/
static int sampleIsBetterPost(
- Stat4Accum *pAccum,
- Stat4Sample *pNew,
- Stat4Sample *pOld
+ StatAccum *pAccum,
+ StatSample *pNew,
+ StatSample *pOld
){
int nCol = pAccum->nCol;
int i;
@@ -103598,17 +118192,17 @@ static int sampleIsBetterPost(
}
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Return true if pNew is to be preferred over pOld.
**
** This function assumes that for each argument sample, the contents of
-** the anEq[] array from pSample->anEq[pSample->iCol] onwards are valid.
+** the anEq[] array from pSample->anEq[pSample->iCol] onwards are valid.
*/
static int sampleIsBetter(
- Stat4Accum *pAccum,
- Stat4Sample *pNew,
- Stat4Sample *pOld
+ StatAccum *pAccum,
+ StatSample *pNew,
+ StatSample *pOld
){
tRowcnt nEqNew = pNew->anEq[pNew->iCol];
tRowcnt nEqOld = pOld->anEq[pOld->iCol];
@@ -103617,46 +118211,41 @@ static int sampleIsBetter(
assert( IsStat4 || (pNew->iCol==0 && pOld->iCol==0) );
if( (nEqNew>nEqOld) ) return 1;
-#ifdef SQLITE_ENABLE_STAT4
if( nEqNew==nEqOld ){
if( pNew->iCol<pOld->iCol ) return 1;
return (pNew->iCol==pOld->iCol && sampleIsBetterPost(pAccum, pNew, pOld));
}
return 0;
-#else
- return (nEqNew==nEqOld && pNew->iHash>pOld->iHash);
-#endif
}
/*
** Copy the contents of sample *pNew into the p->a[] array. If necessary,
** remove the least desirable sample from p->a[] to make room.
*/
-static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
- Stat4Sample *pSample = 0;
+static void sampleInsert(StatAccum *p, StatSample *pNew, int nEqZero){
+ StatSample *pSample = 0;
int i;
assert( IsStat4 || nEqZero==0 );
-#ifdef SQLITE_ENABLE_STAT4
- /* Stat4Accum.nMaxEqZero is set to the maximum number of leading 0
- ** values in the anEq[] array of any sample in Stat4Accum.a[]. In
+ /* StatAccum.nMaxEqZero is set to the maximum number of leading 0
+ ** values in the anEq[] array of any sample in StatAccum.a[]. In
** other words, if nMaxEqZero is n, then it is guaranteed that there
- ** are no samples with Stat4Sample.anEq[m]==0 for (m>=n). */
+ ** are no samples with StatSample.anEq[m]==0 for (m>=n). */
if( nEqZero>p->nMaxEqZero ){
p->nMaxEqZero = nEqZero;
}
if( pNew->isPSample==0 ){
- Stat4Sample *pUpgrade = 0;
+ StatSample *pUpgrade = 0;
assert( pNew->anEq[pNew->iCol]>0 );
- /* This sample is being added because the prefix that ends in column
+ /* This sample is being added because the prefix that ends in column
** iCol occurs many times in the table. However, if we have already
** added a sample that shares this prefix, there is no need to add
** this one. Instead, upgrade the priority of the highest priority
** existing sample that shares this prefix. */
for(i=p->nSample-1; i>=0; i--){
- Stat4Sample *pOld = &p->a[i];
+ StatSample *pOld = &p->a[i];
if( pOld->anEq[pNew->iCol]==0 ){
if( pOld->isPSample ) return;
assert( pOld->iCol>pNew->iCol );
@@ -103672,11 +118261,10 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
goto find_new_min;
}
}
-#endif
/* If necessary, remove sample iMin to make room for the new sample. */
if( p->nSample>=p->mxSample ){
- Stat4Sample *pMin = &p->a[p->iMin];
+ StatSample *pMin = &p->a[p->iMin];
tRowcnt *anEq = pMin->anEq;
tRowcnt *anLt = pMin->anLt;
tRowcnt *anDLt = pMin->anDLt;
@@ -103693,10 +118281,8 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
/* The "rows less-than" for the rowid column must be greater than that
** for the last sample in the p->a[] array. Otherwise, the samples would
** be out of order. */
-#ifdef SQLITE_ENABLE_STAT4
- assert( p->nSample==0
+ assert( p->nSample==0
|| pNew->anLt[p->nCol-1] > p->a[p->nSample-1].anLt[p->nCol-1] );
-#endif
/* Insert the new sample */
pSample = &p->a[p->nSample];
@@ -103706,9 +118292,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
/* Zero the first nEqZero entries in the anEq[] array. */
memset(pSample->anEq, 0, sizeof(tRowcnt)*nEqZero);
-#ifdef SQLITE_ENABLE_STAT4
- find_new_min:
-#endif
+find_new_min:
if( p->nSample>=p->mxSample ){
int iMin = -1;
for(i=0; i<p->mxSample; i++){
@@ -103721,22 +118305,22 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
p->iMin = iMin;
}
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
+#ifdef SQLITE_ENABLE_STAT4
/*
** Field iChng of the index being scanned has changed. So at this point
** p->current contains a sample that reflects the previous row of the
** index. The value of anEq[iChng] and subsequent anEq[] elements are
** correct at this point.
*/
-static void samplePushPrevious(Stat4Accum *p, int iChng){
-#ifdef SQLITE_ENABLE_STAT4
+static void samplePushPrevious(StatAccum *p, int iChng){
int i;
/* Check if any samples from the aBest[] array should be pushed
** into IndexSample.a[] at this point. */
for(i=(p->nCol-2); i>=iChng; i--){
- Stat4Sample *pBest = &p->aBest[i];
+ StatSample *pBest = &p->aBest[i];
pBest->anEq[i] = p->current.anEq[i];
if( p->nSample<p->mxSample || sampleIsBetter(p, pBest, &p->a[p->iMin]) ){
sampleInsert(p, pBest, i);
@@ -103760,50 +118344,27 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){
}
p->nMaxEqZero = iChng;
}
-#endif
-
-#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4)
- if( iChng==0 ){
- tRowcnt nLt = p->current.anLt[0];
- tRowcnt nEq = p->current.anEq[0];
-
- /* Check if this is to be a periodic sample. If so, add it. */
- if( (nLt/p->nPSample)!=(nLt+nEq)/p->nPSample ){
- p->current.isPSample = 1;
- sampleInsert(p, &p->current, 0);
- p->current.isPSample = 0;
- }else
-
- /* Or if it is a non-periodic sample. Add it in this case too. */
- if( p->nSample<p->mxSample
- || sampleIsBetter(p, &p->current, &p->a[p->iMin])
- ){
- sampleInsert(p, &p->current, 0);
- }
- }
-#endif
-
-#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
- UNUSED_PARAMETER( p );
- UNUSED_PARAMETER( iChng );
-#endif
}
+#endif /* SQLITE_ENABLE_STAT4 */
/*
** Implementation of the stat_push SQL function: stat_push(P,C,R)
** Arguments:
**
-** P Pointer to the Stat4Accum object created by stat_init()
+** P Pointer to the StatAccum object created by stat_init()
** C Index of left-most column to differ from previous row
** R Rowid for the current row. Might be a key record for
** WITHOUT ROWID tables.
**
-** This SQL function always returns NULL. It's purpose it to accumulate
-** statistical data and/or samples in the Stat4Accum object about the
-** index being analyzed. The stat_get() SQL function will later be used to
-** extract relevant information for constructing the sqlite_statN tables.
+** The purpose of this routine is to collect statistical data and/or
+** samples from the index being analyzed into the StatAccum object.
+** The stat_get() SQL function will be used afterwards to
+** retrieve the information gathered.
+**
+** This SQL function usually returns NULL, but might return an integer
+** if it wants the byte-code to do special processing.
**
-** The R parameter is only used for STAT3 and STAT4
+** The R parameter is only used for STAT4
*/
static void statPush(
sqlite3_context *context,
@@ -103813,7 +118374,7 @@ static void statPush(
int i;
/* The three function arguments */
- Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
+ StatAccum *p = (StatAccum*)sqlite3_value_blob(argv[0]);
int iChng = sqlite3_value_int(argv[1]);
UNUSED_PARAMETER( argc );
@@ -103823,39 +118384,44 @@ static void statPush(
if( p->nRow==0 ){
/* This is the first call to this function. Do initialization. */
+#ifdef SQLITE_ENABLE_STAT4
for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
+#endif
}else{
/* Second and subsequent calls get processed here */
- samplePushPrevious(p, iChng);
+#ifdef SQLITE_ENABLE_STAT4
+ if( p->mxSample ) samplePushPrevious(p, iChng);
+#endif
/* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
** to the current row of the index. */
+#ifdef SQLITE_ENABLE_STAT4
for(i=0; i<iChng; i++){
p->current.anEq[i]++;
}
+#endif
for(i=iChng; i<p->nCol; i++){
p->current.anDLt[i]++;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- p->current.anLt[i] += p->current.anEq[i];
-#endif
+#ifdef SQLITE_ENABLE_STAT4
+ if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];
p->current.anEq[i] = 1;
+#endif
}
}
- p->nRow++;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
- sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
- }else{
- sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
- sqlite3_value_blob(argv[2]));
- }
- p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
-#endif
+ p->nRow++;
#ifdef SQLITE_ENABLE_STAT4
- {
- tRowcnt nLt = p->current.anLt[p->nCol-1];
+ if( p->mxSample ){
+ tRowcnt nLt;
+ if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
+ sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
+ }else{
+ sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
+ sqlite3_value_blob(argv[2]));
+ }
+ p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
+ nLt = p->current.anLt[p->nCol-1];
/* Check if this is to be a periodic sample. If so, add it. */
if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){
p->current.isPSample = 1;
@@ -103871,11 +118437,16 @@ static void statPush(
sampleCopy(p, &p->aBest[i], &p->current);
}
}
- }
+ }else
#endif
+ if( p->nLimit && p->nRow>(tRowcnt)p->nLimit*(p->nSkipAhead+1) ){
+ p->nSkipAhead++;
+ sqlite3_result_int(context, p->current.anDLt[0]>0);
+ }
}
+
static const FuncDef statPushFuncdef = {
- 2+IsStat34, /* nArg */
+ 2+IsStat4, /* nArg */
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
@@ -103895,18 +118466,18 @@ static const FuncDef statPushFuncdef = {
/*
** Implementation of the stat_get(P,J) SQL function. This routine is
** used to query statistical information that has been gathered into
-** the Stat4Accum object by prior calls to stat_push(). The P parameter
-** has type BLOB but it is really just a pointer to the Stat4Accum object.
+** the StatAccum object by prior calls to stat_push(). The P parameter
+** has type BLOB but it is really just a pointer to the StatAccum object.
** The content to returned is determined by the parameter J
** which is one of the STAT_GET_xxxx values defined above.
**
** The stat_get(P,J) function is not available to generic SQL. It is
** inserted as part of a manually constructed bytecode program. (See
** the callStatGet() routine below.) It is guaranteed that the P
-** parameter will always be a poiner to a Stat4Accum object, never a
+** parameter will always be a pointer to a StatAccum object, never a
** NULL.
**
-** If neither STAT3 nor STAT4 are enabled, then J is always
+** If STAT4 is not enabled, then J is always
** STAT_GET_STAT1 and is hence omitted and this routine becomes
** a one-parameter function, stat_get(P), that always returns the
** stat1 table entry information.
@@ -103916,15 +118487,16 @@ static void statGet(
int argc,
sqlite3_value **argv
){
- Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- /* STAT3 and STAT4 have a parameter on this routine. */
+ StatAccum *p = (StatAccum*)sqlite3_value_blob(argv[0]);
+#ifdef SQLITE_ENABLE_STAT4
+ /* STAT4 has a parameter on this routine. */
int eCall = sqlite3_value_int(argv[1]);
assert( argc==2 );
- assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ
+ assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ
|| eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT
- || eCall==STAT_GET_NDLT
+ || eCall==STAT_GET_NDLT
);
+ assert( eCall==STAT_GET_STAT1 || p->mxSample );
if( eCall==STAT_GET_STAT1 )
#else
assert( argc==1 );
@@ -103933,54 +118505,54 @@ static void statGet(
/* Return the value to store in the "stat" column of the sqlite_stat1
** table for this index.
**
- ** The value is a string composed of a list of integers describing
- ** the index. The first integer in the list is the total number of
- ** entries in the index. There is one additional integer in the list
+ ** The value is a string composed of a list of integers describing
+ ** the index. The first integer in the list is the total number of
+ ** entries in the index. There is one additional integer in the list
** for each indexed column. This additional integer is an estimate of
- ** the number of rows matched by a stabbing query on the index using
+ ** the number of rows matched by a equality query on the index using
** a key with the corresponding number of fields. In other words,
- ** if the index is on columns (a,b) and the sqlite_stat1 value is
+ ** if the index is on columns (a,b) and the sqlite_stat1 value is
** "100 10 2", then SQLite estimates that:
**
** * the index contains 100 rows,
** * "WHERE a=?" matches 10 rows, and
** * "WHERE a=? AND b=?" matches 2 rows.
**
- ** If D is the count of distinct values and K is the total number of
- ** rows, then each estimate is computed as:
+ ** If D is the count of distinct values and K is the total number of
+ ** rows, then each estimate is usually computed as:
**
** I = (K+D-1)/D
+ **
+ ** In other words, I is K/D rounded up to the next whole integer.
+ ** However, if I is between 1.0 and 1.1 (in other words if I is
+ ** close to 1.0 but just a little larger) then do not round up but
+ ** instead keep the I value at 1.0.
*/
- char *z;
- int i;
+ sqlite3_str sStat; /* Text of the constructed "stat" line */
+ int i; /* Loop counter */
- char *zRet = sqlite3MallocZero( (p->nKeyCol+1)*25 );
- if( zRet==0 ){
- sqlite3_result_error_nomem(context);
- return;
- }
-
- sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow);
- z = zRet + sqlite3Strlen30(zRet);
+ sqlite3StrAccumInit(&sStat, 0, 0, 0, (p->nKeyCol+1)*100);
+ sqlite3_str_appendf(&sStat, "%llu",
+ p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow);
for(i=0; i<p->nKeyCol; i++){
u64 nDistinct = p->current.anDLt[i] + 1;
u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
- sqlite3_snprintf(24, z, " %llu", iVal);
- z += sqlite3Strlen30(z);
+ if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1;
+ sqlite3_str_appendf(&sStat, " %llu", iVal);
+#ifdef SQLITE_ENABLE_STAT4
assert( p->current.anEq[i] );
+#endif
}
- assert( z[0]=='\0' && z>zRet );
-
- sqlite3_result_text(context, zRet, -1, sqlite3_free);
+ sqlite3ResultStrAccum(context, &sStat);
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
else if( eCall==STAT_GET_ROWID ){
if( p->iGet<0 ){
samplePushPrevious(p, 0);
p->iGet = 0;
}
if( p->iGet<p->nSample ){
- Stat4Sample *pS = p->a + p->iGet;
+ StatSample *pS = p->a + p->iGet;
if( pS->nRowid==0 ){
sqlite3_result_int64(context, pS->u.iRowid);
}else{
@@ -103990,44 +118562,33 @@ static void statGet(
}
}else{
tRowcnt *aCnt = 0;
+ sqlite3_str sStat;
+ int i;
assert( p->iGet<p->nSample );
switch( eCall ){
case STAT_GET_NEQ: aCnt = p->a[p->iGet].anEq; break;
case STAT_GET_NLT: aCnt = p->a[p->iGet].anLt; break;
default: {
- aCnt = p->a[p->iGet].anDLt;
+ aCnt = p->a[p->iGet].anDLt;
p->iGet++;
break;
}
}
-
- if( IsStat3 ){
- sqlite3_result_int64(context, (i64)aCnt[0]);
- }else{
- char *zRet = sqlite3MallocZero(p->nCol * 25);
- if( zRet==0 ){
- sqlite3_result_error_nomem(context);
- }else{
- int i;
- char *z = zRet;
- for(i=0; i<p->nCol; i++){
- sqlite3_snprintf(24, z, "%llu ", (u64)aCnt[i]);
- z += sqlite3Strlen30(z);
- }
- assert( z[0]=='\0' && z>zRet );
- z[-1] = '\0';
- sqlite3_result_text(context, zRet, -1, sqlite3_free);
- }
+ sqlite3StrAccumInit(&sStat, 0, 0, 0, p->nCol*100);
+ for(i=0; i<p->nCol; i++){
+ sqlite3_str_appendf(&sStat, "%llu ", (u64)aCnt[i]);
}
+ if( sStat.nChar ) sStat.nChar--;
+ sqlite3ResultStrAccum(context, &sStat);
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
#ifndef SQLITE_DEBUG
UNUSED_PARAMETER( argc );
#endif
}
static const FuncDef statGetFuncdef = {
- 1+IsStat34, /* nArg */
+ 1+IsStat4, /* nArg */
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
@@ -104038,20 +118599,44 @@ static const FuncDef statGetFuncdef = {
{0}
};
-static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
- assert( regOut!=regStat4 && regOut!=regStat4+1 );
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- sqlite3VdbeAddOp2(v, OP_Integer, iParam, regStat4+1);
+static void callStatGet(Parse *pParse, int regStat, int iParam, int regOut){
+#ifdef SQLITE_ENABLE_STAT4
+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat+1);
#elif SQLITE_DEBUG
assert( iParam==STAT_GET_STAT1 );
#else
UNUSED_PARAMETER( iParam );
#endif
- sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4, regOut,
- (char*)&statGetFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 1 + IsStat34);
+ assert( regOut!=regStat && regOut!=regStat+1 );
+ sqlite3VdbeAddFunctionCall(pParse, 0, regStat, regOut, 1+IsStat4,
+ &statGetFuncdef, 0);
}
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+/* Add a comment to the most recent VDBE opcode that is the name
+** of the k-th column of the pIdx index.
+*/
+static void analyzeVdbeCommentIndexWithColumnName(
+ Vdbe *v, /* Prepared statement under construction */
+ Index *pIdx, /* Index whose column is being loaded */
+ int k /* Which column index */
+){
+ int i; /* Index of column in the table */
+ assert( k>=0 && k<pIdx->nColumn );
+ i = pIdx->aiColumn[k];
+ if( NEVER(i==XN_ROWID) ){
+ VdbeComment((v,"%s.rowid",pIdx->zName));
+ }else if( i==XN_EXPR ){
+ assert( pIdx->bHasExpr );
+ VdbeComment((v,"%s.expr(%d)",pIdx->zName, k));
+ }else{
+ VdbeComment((v,"%s.%s", pIdx->zName, pIdx->pTable->aCol[i].zCnName));
+ }
+}
+#else
+# define analyzeVdbeCommentIndexWithColumnName(a,b,c)
+#endif /* SQLITE_DEBUG */
+
/*
** Generate code to do an analysis of all indices associated with
** a single table.
@@ -104074,26 +118659,29 @@ static void analyzeOneTable(
int iDb; /* Index of database containing pTab */
u8 needTableCnt = 1; /* True to count the table */
int regNewRowid = iMem++; /* Rowid for the inserted record */
- int regStat4 = iMem++; /* Register to hold Stat4Accum object */
+ int regStat = iMem++; /* Register to hold StatAccum object */
int regChng = iMem++; /* Index of changed index field */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
int regRowid = iMem++; /* Rowid argument passed to stat_push() */
-#endif
int regTemp = iMem++; /* Temporary use register */
+ int regTemp2 = iMem++; /* Second temporary use register */
int regTabname = iMem++; /* Register containing table name */
int regIdxname = iMem++; /* Register containing index name */
int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */
int regPrev = iMem; /* MUST BE LAST (see below) */
+#ifdef SQLITE_ENABLE_STAT4
+ int doOnce = 1; /* Flag for a one-time computation */
+#endif
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
- Table *pStat1 = 0;
+ Table *pStat1 = 0;
#endif
- pParse->nMem = MAX(pParse->nMem, iMem);
+ sqlite3TouchRegister(pParse, iMem);
+ assert( sqlite3NoTempsInRange(pParse, regNewRowid, iMem) );
v = sqlite3GetVdbe(pParse);
if( v==0 || NEVER(pTab==0) ){
return;
}
- if( pTab->tnum==0 ){
+ if( !IsOrdinaryTable(pTab) ){
/* Do not gather statistics on views or virtual tables */
return;
}
@@ -104120,11 +118708,11 @@ static void analyzeOneTable(
memcpy(pStat1->zName, "sqlite_stat1", 13);
pStat1->nCol = 3;
pStat1->iPKey = -1;
- sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNBLOB);
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_Noop, 0, 0, 0,(char*)pStat1,P4_DYNAMIC);
}
#endif
- /* Establish a read-lock on the table at the shared-cache level.
+ /* Establish a read-lock on the table at the shared-cache level.
** Open a read-only cursor on the table. Also allocate a cursor number
** to use for scanning indexes (iIdxCur). No index cursor is opened at
** this time though. */
@@ -104190,11 +118778,11 @@ static void analyzeOneTable(
** end_of_scan:
*/
- /* Make sure there are enough memory cells allocated to accommodate
+ /* Make sure there are enough memory cells allocated to accommodate
** the regPrev array and a trailing rowid (the rowid slot is required
- ** when building a record to insert into the sample column of
+ ** when building a record to insert into the sample column of
** the sqlite_stat4 table. */
- pParse->nMem = MAX(pParse->nMem, regPrev+nColTest);
+ sqlite3TouchRegister(pParse, regPrev+nColTest);
/* Open a read-only cursor on the index being analyzed. */
assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
@@ -104203,23 +118791,31 @@ static void analyzeOneTable(
VdbeComment((v, "%s", pIdx->zName));
/* Invoke the stat_init() function. The arguments are:
- **
+ **
** (1) the number of columns in the index including the rowid
** (or for a WITHOUT ROWID table, the number of PK columns),
** (2) the number of columns in the key without the rowid/pk
- ** (3) the number of rows in the index,
- **
- **
- ** The third argument is only used for STAT3 and STAT4
+ ** (3) estimated number of rows in the index,
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
+ sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1);
+ assert( regRowid==regStat+2 );
+ sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid);
+#ifdef SQLITE_ENABLE_STAT4
+ if( OptimizationEnabled(db, SQLITE_Stat4) ){
+ sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp);
+ addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
+ VdbeCoverage(v);
+ }else
#endif
- sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
- sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
- sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4+1, regStat4,
- (char*)&statInitFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 2+IsStat34);
+ {
+ addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1);
+ }
+ assert( regTemp2==regStat+4 );
+ sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
+ sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4,
+ &statInitFuncdef, 0);
/* Implementation of the following:
**
@@ -104229,13 +118825,11 @@ static void analyzeOneTable(
** goto next_push_0;
**
*/
- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
- VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
addrNextRow = sqlite3VdbeCurrentAddr(v);
if( nColTest>0 ){
- int endDistinctTest = sqlite3VdbeMakeLabel(v);
+ int endDistinctTest = sqlite3VdbeMakeLabel(pParse);
int *aGotoChng; /* Array of jump instruction addresses */
aGotoChng = sqlite3DbMallocRawNN(db, sizeof(int)*nColTest);
if( aGotoChng==0 ) continue;
@@ -104254,7 +118848,7 @@ static void analyzeOneTable(
addrNextRow = sqlite3VdbeCurrentAddr(v);
if( nColTest==1 && pIdx->nKeyCol==1 && IsUniqueIndex(pIdx) ){
/* For a single-column UNIQUE index, once we have found a non-NULL
- ** row, we know that all the rest will be distinct, so skip
+ ** row, we know that all the rest will be distinct, so skip
** subsequent distinctness tests. */
sqlite3VdbeAddOp2(v, OP_NotNull, regPrev, endDistinctTest);
VdbeCoverage(v);
@@ -104263,15 +118857,16 @@ static void analyzeOneTable(
char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp);
- aGotoChng[i] =
+ analyzeVdbeCommentIndexWithColumnName(v,pIdx,i);
+ aGotoChng[i] =
sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ);
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
VdbeCoverage(v);
}
sqlite3VdbeAddOp2(v, OP_Integer, nColTest, regChng);
sqlite3VdbeGoto(v, endDistinctTest);
-
-
+
+
/*
** chng_addr_0:
** regPrev(0) = idx(0)
@@ -104283,44 +118878,60 @@ static void analyzeOneTable(
for(i=0; i<nColTest; i++){
sqlite3VdbeJumpHere(v, aGotoChng[i]);
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i);
+ analyzeVdbeCommentIndexWithColumnName(v,pIdx,i);
}
sqlite3VdbeResolveLabel(v, endDistinctTest);
sqlite3DbFree(db, aGotoChng);
}
-
+
/*
** chng_addr_N:
- ** regRowid = idx(rowid) // STAT34 only
- ** stat_push(P, regChng, regRowid) // 3rd parameter STAT34 only
+ ** regRowid = idx(rowid) // STAT4 only
+ ** stat_push(P, regChng, regRowid) // 3rd parameter STAT4 only
** Next csr
** if !eof(csr) goto next_row;
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- assert( regRowid==(regStat4+2) );
- if( HasRowid(pTab) ){
- sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
- }else{
- Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
- int j, k, regKey;
- regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
- for(j=0; j<pPk->nKeyCol; j++){
- k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
- assert( k>=0 && k<pIdx->nColumn );
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
- VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
+#ifdef SQLITE_ENABLE_STAT4
+ if( OptimizationEnabled(db, SQLITE_Stat4) ){
+ assert( regRowid==(regStat+2) );
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
+ int j, k, regKey;
+ regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
+ for(j=0; j<pPk->nKeyCol; j++){
+ k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
+ assert( k>=0 && k<pIdx->nColumn );
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
+ analyzeVdbeCommentIndexWithColumnName(v,pIdx,k);
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
+ sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
}
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
- sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
}
#endif
- assert( regChng==(regStat4+1) );
- sqlite3VdbeAddOp4(v, OP_Function0, 1, regStat4, regTemp,
- (char*)&statPushFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 2+IsStat34);
- sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
+ assert( regChng==(regStat+1) );
+ {
+ sqlite3VdbeAddFunctionCall(pParse, 1, regStat, regTemp, 2+IsStat4,
+ &statPushFuncdef, 0);
+ if( db->nAnalysisLimit ){
+ int j1, j2, j3;
+ j1 = sqlite3VdbeAddOp1(v, OP_IsNull, regTemp); VdbeCoverage(v);
+ j2 = sqlite3VdbeAddOp1(v, OP_If, regTemp); VdbeCoverage(v);
+ j3 = sqlite3VdbeAddOp4Int(v, OP_SeekGT, iIdxCur, 0, regPrev, 1);
+ VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, j2);
+ sqlite3VdbeJumpHere(v, j3);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
+ }
+ }
/* Add the entry to the stat1 table. */
- callStatGet(v, regStat4, STAT_GET_STAT1, regStat1);
+ callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1);
assert( "BBB"[0]==SQLITE_AFF_TEXT );
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
@@ -104330,9 +118941,9 @@ static void analyzeOneTable(
#endif
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
- /* Add the entries to the stat3 or stat4 table. */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- {
+ /* Add the entries to the stat4 table. */
+#ifdef SQLITE_ENABLE_STAT4
+ if( OptimizationEnabled(db, SQLITE_Stat4) && db->nAnalysisLimit==0 ){
int regEq = regStat1;
int regLt = regStat1+1;
int regDLt = regStat1+2;
@@ -104343,32 +118954,56 @@ static void analyzeOneTable(
int addrIsNull;
u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
- pParse->nMem = MAX(pParse->nMem, regCol+nCol);
+ if( doOnce ){
+ int mxCol = nCol;
+ Index *pX;
+
+ /* Compute the maximum number of columns in any index */
+ for(pX=pTab->pIndex; pX; pX=pX->pNext){
+ int nColX; /* Number of columns in pX */
+ if( !HasRowid(pTab) && IsPrimaryKeyIndex(pX) ){
+ nColX = pX->nKeyCol;
+ }else{
+ nColX = pX->nColumn;
+ }
+ if( nColX>mxCol ) mxCol = nColX;
+ }
+
+ /* Allocate space to compute results for the largest index */
+ sqlite3TouchRegister(pParse, regCol+mxCol);
+ doOnce = 0;
+#ifdef SQLITE_DEBUG
+ /* Verify that the call to sqlite3ClearTempRegCache() below
+ ** really is needed.
+ ** https://sqlite.org/forum/forumpost/83cb4a95a0 (2023-03-25)
+ */
+ testcase( !sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) );
+#endif
+ sqlite3ClearTempRegCache(pParse); /* tag-20230325-1 */
+ assert( sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) );
+ }
+ assert( sqlite3NoTempsInRange(pParse, regEq, regCol+nCol) );
addrNext = sqlite3VdbeCurrentAddr(v);
- callStatGet(v, regStat4, STAT_GET_ROWID, regSampleRowid);
+ callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid);
addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
VdbeCoverage(v);
- callStatGet(v, regStat4, STAT_GET_NEQ, regEq);
- callStatGet(v, regStat4, STAT_GET_NLT, regLt);
- callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
+ callStatGet(pParse, regStat, STAT_GET_NEQ, regEq);
+ callStatGet(pParse, regStat, STAT_GET_NLT, regLt);
+ callStatGet(pParse, regStat, STAT_GET_NDLT, regDLt);
sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
VdbeCoverage(v);
-#ifdef SQLITE_ENABLE_STAT3
- sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, 0, regSample);
-#else
for(i=0; i<nCol; i++){
sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i);
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol, regSample);
-#endif
sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp);
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid);
sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); /* P1==1 for end-of-loop */
sqlite3VdbeJumpHere(v, addrIsNull);
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/* End of analysis */
sqlite3VdbeJumpHere(v, addrRewind);
@@ -104428,6 +119063,11 @@ static void analyzeDatabase(Parse *pParse, int iDb){
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
Table *pTab = (Table*)sqliteHashData(k);
analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab);
+#ifdef SQLITE_ENABLE_STAT4
+ iMem = sqlite3FirstAvailableRegister(pParse, iMem);
+#else
+ assert( iMem==sqlite3FirstAvailableRegister(pParse,iMem) );
+#endif
}
loadAnalysis(pParse, iDb);
}
@@ -104543,7 +119183,7 @@ static void decodeIntArray(
int i;
tRowcnt v;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( z==0 ) z = "";
#else
assert( z!=0 );
@@ -104554,7 +119194,7 @@ static void decodeIntArray(
v = v*10 + c - '0';
z++;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( aOut ) aOut[i] = v;
if( aLog ) aLog[i] = sqlite3LogEst(v);
#else
@@ -104565,7 +119205,7 @@ static void decodeIntArray(
#endif
if( *z==' ' ) z++;
}
-#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifndef SQLITE_ENABLE_STAT4
assert( pIndex!=0 ); {
#else
if( pIndex ){
@@ -104576,7 +119216,9 @@ static void decodeIntArray(
if( sqlite3_strglob("unordered*", z)==0 ){
pIndex->bUnordered = 1;
}else if( sqlite3_strglob("sz=[0-9]*", z)==0 ){
- pIndex->szIdxRow = sqlite3LogEst(sqlite3Atoi(z+3));
+ int sz = sqlite3Atoi(z+3);
+ if( sz<2 ) sz = 2;
+ pIndex->szIdxRow = sqlite3LogEst(sz);
}else if( sqlite3_strglob("noskipscan*", z)==0 ){
pIndex->noSkipScan = 1;
}
@@ -104588,12 +119230,22 @@ static void decodeIntArray(
while( z[0]!=0 && z[0]!=' ' ) z++;
while( z[0]==' ' ) z++;
}
+
+ /* Set the bLowQual flag if the peak number of rows obtained
+ ** from a full equality match is so large that a full table scan
+ ** seems likely to be faster than using the index.
+ */
+ if( aLog[0] > 66 /* Index has more than 100 rows */
+ && aLog[0] <= aLog[nOut-1] /* And only a single value seen */
+ ){
+ pIndex->bLowQual = 1;
+ }
}
}
/*
** This callback is invoked once for each index when reading the
-** sqlite_stat1 table.
+** sqlite_stat1 table.
**
** argv[0] = name of the table
** argv[1] = name of the index (might be NULL)
@@ -104630,8 +119282,8 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
if( pIndex ){
tRowcnt *aiRowEst = 0;
int nCol = pIndex->nKeyCol+1;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- /* Index.aiRowEst may already be set here if there are duplicate
+#ifdef SQLITE_ENABLE_STAT4
+ /* Index.aiRowEst may already be set here if there are duplicate
** sqlite_stat1 entries for this index. In that case just clobber
** the old data with the new instead of allocating a new array. */
if( pIndex->aiRowEst==0 ){
@@ -104666,7 +119318,9 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
** and its contents.
*/
SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ assert( db!=0 );
+ assert( pIdx!=0 );
+#ifdef SQLITE_ENABLE_STAT4
if( pIdx->aSample ){
int j;
for(j=0; j<pIdx->nSample; j++){
@@ -104675,20 +119329,20 @@ SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
}
sqlite3DbFree(db, pIdx->aSample);
}
- if( db && db->pnBytesFreed==0 ){
+ if( db->pnBytesFreed==0 ){
pIdx->nSample = 0;
pIdx->aSample = 0;
}
#else
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pIdx);
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Populate the pIdx->aAvgEq[] array based on the samples currently
-** stored in pIdx->aSample[].
+** stored in pIdx->aSample[].
*/
static void initAvgEq(Index *pIdx){
if( pIdx ){
@@ -104724,12 +119378,12 @@ static void initAvgEq(Index *pIdx){
pIdx->nRowEst0 = nRow;
/* Set nSum to the number of distinct (iCol+1) field prefixes that
- ** occur in the stat4 table for this index. Set sumEq to the sum of
- ** the nEq values for column iCol for the same set (adding the value
+ ** occur in the stat4 table for this index. Set sumEq to the sum of
+ ** the nEq values for column iCol for the same set (adding the value
** only once where there exist duplicate prefixes). */
for(i=0; i<nSample; i++){
if( i==(pIdx->nSample-1)
- || aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol]
+ || aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol]
){
sumEq += aSample[i].anEq[iCol];
nSum100 += 100;
@@ -104763,12 +119417,11 @@ static Index *findIndexOrPrimaryKey(
}
/*
-** Load the content from either the sqlite_stat4 or sqlite_stat3 table
+** Load the content from either the sqlite_stat4
** into the relevant Index.aSample[] arrays.
**
** Arguments zSql1 and zSql2 must point to SQL statements that return
-** data equivalent to the following (statements are different for stat3,
-** see the caller of this function for details):
+** data equivalent to the following:
**
** zSql1: SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx
** zSql2: SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4
@@ -104777,7 +119430,6 @@ static Index *findIndexOrPrimaryKey(
*/
static int loadStatTbl(
sqlite3 *db, /* Database handle */
- int bStat3, /* Assume single column records only */
const char *zSql1, /* SQL statement 1 (see above) */
const char *zSql2, /* SQL statement 2 (see above) */
const char *zDb /* Database name (e.g. "main") */
@@ -104811,19 +119463,20 @@ static int loadStatTbl(
if( zIndex==0 ) continue;
nSample = sqlite3_column_int(pStmt, 1);
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
- assert( pIdx==0 || bStat3 || pIdx->nSample==0 );
- /* Index.nSample is non-zero at this point if data has already been
- ** loaded from the stat4 table. In this case ignore stat3 data. */
- if( pIdx==0 || pIdx->nSample ) continue;
- if( bStat3==0 ){
- assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
- if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
- nIdxCol = pIdx->nKeyCol;
- }else{
- nIdxCol = pIdx->nColumn;
- }
+ assert( pIdx==0 || pIdx->nSample==0 );
+ if( pIdx==0 ) continue;
+ if( pIdx->aSample!=0 ){
+ /* The same index appears in sqlite_stat4 under multiple names */
+ continue;
+ }
+ assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
+ if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
+ nIdxCol = pIdx->nKeyCol;
+ }else{
+ nIdxCol = pIdx->nColumn;
}
pIdx->nSampleCol = nIdxCol;
+ pIdx->mxSample = nSample;
nByte = sizeof(IndexSample) * nSample;
nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample;
nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
@@ -104835,6 +119488,7 @@ static int loadStatTbl(
}
pSpace = (tRowcnt*)&pIdx->aSample[nSample];
pIdx->aAvgEq = pSpace; pSpace += nIdxCol;
+ pIdx->pTable->tabFlags |= TF_HasStat4;
for(i=0; i<nSample; i++){
pIdx->aSample[i].anEq = pSpace; pSpace += nIdxCol;
pIdx->aSample[i].anLt = pSpace; pSpace += nIdxCol;
@@ -104862,10 +119516,14 @@ static int loadStatTbl(
if( zIndex==0 ) continue;
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
if( pIdx==0 ) continue;
- /* This next condition is true if data has already been loaded from
- ** the sqlite_stat4 table. In this case ignore stat3 data. */
+ if( pIdx->nSample>=pIdx->mxSample ){
+ /* Too many slots used because the same index appears in
+ ** sqlite_stat4 using multiple names */
+ continue;
+ }
+ /* This next condition is true if data has already been loaded from
+ ** the sqlite_stat4 table. */
nCol = pIdx->nSampleCol;
- if( bStat3 && nCol>1 ) continue;
if( pIdx!=pPrevIdx ){
initAvgEq(pPrevIdx);
pPrevIdx = pIdx;
@@ -104875,14 +119533,15 @@ static int loadStatTbl(
decodeIntArray((char*)sqlite3_column_text(pStmt,2),nCol,pSample->anLt,0,0);
decodeIntArray((char*)sqlite3_column_text(pStmt,3),nCol,pSample->anDLt,0,0);
- /* Take a copy of the sample. Add two 0x00 bytes the end of the buffer.
+ /* Take a copy of the sample. Add 8 extra 0x00 bytes the end of the buffer.
** This is in case the sample record is corrupted. In that case, the
** sqlite3VdbeRecordCompare() may read up to two varints past the
** end of the allocated buffer before it realizes it is dealing with
- ** a corrupt record. Adding the two 0x00 bytes prevents this from causing
+ ** a corrupt record. Or it might try to read a large integer from the
+ ** buffer. In any case, eight 0x00 bytes prevents this from causing
** a buffer overread. */
pSample->n = sqlite3_column_bytes(pStmt, 4);
- pSample->p = sqlite3DbMallocZero(db, pSample->n + 2);
+ pSample->p = sqlite3DbMallocZero(db, pSample->n + 8);
if( pSample->p==0 ){
sqlite3_finalize(pStmt);
return SQLITE_NOMEM_BKPT;
@@ -104898,45 +119557,40 @@ static int loadStatTbl(
}
/*
-** Load content from the sqlite_stat4 and sqlite_stat3 tables into
+** Load content from the sqlite_stat4 table into
** the Index.aSample[] arrays of all indices.
*/
static int loadStat4(sqlite3 *db, const char *zDb){
int rc = SQLITE_OK; /* Result codes from subroutines */
+ const Table *pStat4;
assert( db->lookaside.bDisable );
- if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){
- rc = loadStatTbl(db, 0,
- "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx",
+ if( OptimizationEnabled(db, SQLITE_Stat4)
+ && (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0
+ && IsOrdinaryTable(pStat4)
+ ){
+ rc = loadStatTbl(db,
+ "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx COLLATE nocase",
"SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4",
zDb
);
}
-
- if( rc==SQLITE_OK && sqlite3FindTable(db, "sqlite_stat3", zDb) ){
- rc = loadStatTbl(db, 1,
- "SELECT idx,count(*) FROM %Q.sqlite_stat3 GROUP BY idx",
- "SELECT idx,neq,nlt,ndlt,sqlite_record(sample) FROM %Q.sqlite_stat3",
- zDb
- );
- }
-
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/*
-** Load the content of the sqlite_stat1 and sqlite_stat3/4 tables. The
+** Load the content of the sqlite_stat1 and sqlite_stat4 tables. The
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
-** arrays. The contents of sqlite_stat3/4 are used to populate the
+** arrays. The contents of sqlite_stat4 are used to populate the
** Index.aSample[] arrays.
**
** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
-** is returned. In this case, even if SQLITE_ENABLE_STAT3/4 was defined
-** during compilation and the sqlite_stat3/4 table is present, no data is
+** is returned. In this case, even if SQLITE_ENABLE_STAT4 was defined
+** during compilation and the sqlite_stat4 table is present, no data is
** read from it.
**
-** If SQLITE_ENABLE_STAT3/4 was defined during compilation and the
+** If SQLITE_ENABLE_STAT4 was defined during compilation and the
** sqlite_stat4 table is not present in the database, SQLITE_ERROR is
** returned. However, in this case, data is read from the sqlite_stat1
** table (if it is present) before returning.
@@ -104951,6 +119605,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
char *zSql;
int rc = SQLITE_OK;
Schema *pSchema = db->aDb[iDb].pSchema;
+ const Table *pStat1;
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
@@ -104964,7 +119619,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
pIdx->hasStat1 = 0;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3DeleteIndexSamples(db, pIdx);
pIdx->aSample = 0;
#endif
@@ -104973,8 +119628,10 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
/* Load new statistics out of the sqlite_stat1 table */
sInfo.db = db;
sInfo.zDatabase = db->aDb[iDb].zDbSName;
- if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){
- zSql = sqlite3MPrintf(db,
+ if( (pStat1 = sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase))
+ && IsOrdinaryTable(pStat1)
+ ){
+ zSql = sqlite3MPrintf(db,
"SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
if( zSql==0 ){
rc = SQLITE_NOMEM_BKPT;
@@ -104992,11 +119649,11 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
}
/* Load the statistics from the sqlite_stat4 table. */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( rc==SQLITE_OK ){
- db->lookaside.bDisable++;
+ DisableLookaside;
rc = loadStat4(db, sInfo.zDatabase);
- db->lookaside.bDisable--;
+ EnableLookaside;
}
for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
@@ -105064,6 +119721,17 @@ static int resolveAttachExpr(NameContext *pName, Expr *pExpr)
}
/*
+** Return true if zName points to a name that may be used to refer to
+** database iDb attached to handle db.
+*/
+SQLITE_PRIVATE int sqlite3DbIsNamed(sqlite3 *db, int iDb, const char *zName){
+ return (
+ sqlite3StrICmp(db->aDb[iDb].zDbSName, zName)==0
+ || (iDb==0 && sqlite3StrICmp("main", zName)==0)
+ );
+}
+
+/*
** An SQL user-function registered to do the work of an ATTACH statement. The
** three arguments to the function come directly from an attach statement:
**
@@ -105092,7 +119760,7 @@ static void attachFunc(
char *zErr = 0;
unsigned int flags;
Db *aNew; /* New array of Db pointers */
- Db *pNew; /* Db object for the newly attached database */
+ Db *pNew = 0; /* Db object for the newly attached database */
char *zErrDyn = 0;
sqlite3_vfs *pVfs;
@@ -105102,7 +119770,7 @@ static void attachFunc(
if( zFile==0 ) zFile = "";
if( zName==0 ) zName = "";
-#ifdef SQLITE_ENABLE_DESERIALIZE
+#ifndef SQLITE_OMIT_DESERIALIZE
# define REOPEN_AS_MEMDB(db) (db->init.reopenMemdb)
#else
# define REOPEN_AS_MEMDB(db) (0)
@@ -105112,13 +119780,26 @@ static void attachFunc(
/* This is not a real ATTACH. Instead, this routine is being called
** from sqlite3_deserialize() to close database db->init.iDb and
** reopen it as a MemDB */
+ Btree *pNewBt = 0;
pVfs = sqlite3_vfs_find("memdb");
if( pVfs==0 ) return;
- pNew = &db->aDb[db->init.iDb];
- if( pNew->pBt ) sqlite3BtreeClose(pNew->pBt);
- pNew->pBt = 0;
- pNew->pSchema = 0;
- rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB);
+ rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNewBt, 0, SQLITE_OPEN_MAIN_DB);
+ if( rc==SQLITE_OK ){
+ Schema *pNewSchema = sqlite3SchemaGet(db, pNewBt);
+ if( pNewSchema ){
+ /* Both the Btree and the new Schema were allocated successfully.
+ ** Close the old db and update the aDb[] slot with the new memdb
+ ** values. */
+ pNew = &db->aDb[db->init.iDb];
+ if( ALWAYS(pNew->pBt) ) sqlite3BtreeClose(pNew->pBt);
+ pNew->pBt = pNewBt;
+ pNew->pSchema = pNewSchema;
+ }else{
+ sqlite3BtreeClose(pNewBt);
+ rc = SQLITE_NOMEM;
+ }
+ }
+ if( rc ) goto attach_error;
}else{
/* This is a real ATTACH
**
@@ -105129,20 +119810,19 @@ static void attachFunc(
** * Specified database name already being used.
*/
if( db->nDb>=db->aLimit[SQLITE_LIMIT_ATTACHED]+2 ){
- zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d",
+ zErrDyn = sqlite3MPrintf(db, "too many attached databases - max %d",
db->aLimit[SQLITE_LIMIT_ATTACHED]
);
goto attach_error;
}
for(i=0; i<db->nDb; i++){
- char *z = db->aDb[i].zDbSName;
- assert( z && zName );
- if( sqlite3StrICmp(z, zName)==0 ){
+ assert( zName );
+ if( sqlite3DbIsNamed(db, i, zName) ){
zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
goto attach_error;
}
}
-
+
/* Allocate the new entry in the db->aDb[] array and initialize the schema
** hash tables.
*/
@@ -105157,7 +119837,7 @@ static void attachFunc(
db->aDb = aNew;
pNew = &db->aDb[db->nDb];
memset(pNew, 0, sizeof(*pNew));
-
+
/* Open the database file. If the btree is successfully opened, use
** it to obtain the database schema. At this point the schema may
** or may not be initialized.
@@ -105173,8 +119853,8 @@ static void attachFunc(
assert( pVfs );
flags |= SQLITE_OPEN_MAIN_DB;
rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags);
- sqlite3_free( zPath );
db->nDb++;
+ pNew->zDbSName = sqlite3DbStrDup(db, zName);
}
db->noSharedCache = 0;
if( rc==SQLITE_CONSTRAINT ){
@@ -105186,7 +119866,7 @@ static void attachFunc(
if( !pNew->pSchema ){
rc = SQLITE_NOMEM_BKPT;
}else if( pNew->pSchema->file_format && pNew->pSchema->enc!=ENC(db) ){
- zErrDyn = sqlite3MPrintf(db,
+ zErrDyn = sqlite3MPrintf(db,
"attached databases must use the same text encoding as main database");
rc = SQLITE_ERROR;
}
@@ -105202,46 +119882,13 @@ static void attachFunc(
sqlite3BtreeLeave(pNew->pBt);
}
pNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
- if( !REOPEN_AS_MEMDB(db) ) pNew->zDbSName = sqlite3DbStrDup(db, zName);
if( rc==SQLITE_OK && pNew->zDbSName==0 ){
rc = SQLITE_NOMEM_BKPT;
}
-
-
-#ifdef SQLITE_HAS_CODEC
- if( rc==SQLITE_OK ){
- extern int sqlite3CodecAttach(sqlite3*, int, const void*, int);
- extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*);
- int nKey;
- char *zKey;
- int t = sqlite3_value_type(argv[2]);
- switch( t ){
- case SQLITE_INTEGER:
- case SQLITE_FLOAT:
- zErrDyn = sqlite3DbStrDup(db, "Invalid key value");
- rc = SQLITE_ERROR;
- break;
-
- case SQLITE_TEXT:
- case SQLITE_BLOB:
- nKey = sqlite3_value_bytes(argv[2]);
- zKey = (char *)sqlite3_value_blob(argv[2]);
- rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
- break;
-
- case SQLITE_NULL:
- /* No key specified. Use the key from the main database */
- sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
- if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){
- rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
- }
- break;
- }
- }
-#endif
+ sqlite3_free_filename( zPath );
/* If the file was opened successfully, read the schema for the new database.
- ** If this fails, or if opening the file failed, then close the file and
+ ** If this fails, or if opening the file failed, then close the file and
** remove the entry from the db->aDb[] array. i.e. put everything back the
** way we found it.
*/
@@ -105249,12 +119896,14 @@ static void attachFunc(
sqlite3BtreeEnterAll(db);
db->init.iDb = 0;
db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
- rc = sqlite3Init(db, &zErrDyn);
+ if( !REOPEN_AS_MEMDB(db) ){
+ rc = sqlite3Init(db, &zErrDyn);
+ }
sqlite3BtreeLeaveAll(db);
assert( zErrDyn==0 || rc!=SQLITE_OK );
}
#ifdef SQLITE_USER_AUTHENTICATION
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){
u8 newAuth = 0;
rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth);
if( newAuth<db->auth.authLevel ){
@@ -105263,7 +119912,7 @@ static void attachFunc(
}
#endif
if( rc ){
- if( !REOPEN_AS_MEMDB(db) ){
+ if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){
int iDb = db->nDb - 1;
assert( iDb>=2 );
if( db->aDb[iDb].pBt ){
@@ -105283,7 +119932,7 @@ static void attachFunc(
}
goto attach_error;
}
-
+
return;
attach_error:
@@ -105312,6 +119961,7 @@ static void detachFunc(
sqlite3 *db = sqlite3_context_db_handle(context);
int i;
Db *pDb = 0;
+ HashElem *pEntry;
char zErr[128];
UNUSED_PARAMETER(NotUsed);
@@ -105320,7 +119970,7 @@ static void detachFunc(
for(i=0; i<db->nDb; i++){
pDb = &db->aDb[i];
if( pDb->pBt==0 ) continue;
- if( sqlite3StrICmp(pDb->zDbSName, zName)==0 ) break;
+ if( sqlite3DbIsNamed(db, i, zName) ) break;
}
if( i>=db->nDb ){
@@ -105331,11 +119981,25 @@ static void detachFunc(
sqlite3_snprintf(sizeof(zErr),zErr, "cannot detach database %s", zName);
goto detach_error;
}
- if( sqlite3BtreeIsInReadTrans(pDb->pBt) || sqlite3BtreeIsInBackup(pDb->pBt) ){
+ if( sqlite3BtreeTxnState(pDb->pBt)!=SQLITE_TXN_NONE
+ || sqlite3BtreeIsInBackup(pDb->pBt)
+ ){
sqlite3_snprintf(sizeof(zErr),zErr, "database %s is locked", zName);
goto detach_error;
}
+ /* If any TEMP triggers reference the schema being detached, move those
+ ** triggers to reference the TEMP schema itself. */
+ assert( db->aDb[1].pSchema );
+ pEntry = sqliteHashFirst(&db->aDb[1].pSchema->trigHash);
+ while( pEntry ){
+ Trigger *pTrig = (Trigger*)sqliteHashData(pEntry);
+ if( pTrig->pTabSchema==pDb->pSchema ){
+ pTrig->pTabSchema = pTrig->pSchema;
+ }
+ pEntry = sqliteHashNext(pEntry);
+ }
+
sqlite3BtreeClose(pDb->pBt);
pDb->pBt = 0;
pDb->pSchema = 0;
@@ -105365,22 +120029,25 @@ static void codeAttach(
sqlite3* db = pParse->db;
int regArgs;
+ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto attach_end;
+
if( pParse->nErr ) goto attach_end;
memset(&sName, 0, sizeof(NameContext));
sName.pParse = pParse;
- if(
- SQLITE_OK!=(rc = resolveAttachExpr(&sName, pFilename)) ||
- SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) ||
- SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey))
+ if(
+ SQLITE_OK!=resolveAttachExpr(&sName, pFilename) ||
+ SQLITE_OK!=resolveAttachExpr(&sName, pDbname) ||
+ SQLITE_OK!=resolveAttachExpr(&sName, pKey)
){
goto attach_end;
}
#ifndef SQLITE_OMIT_AUTHORIZATION
- if( pAuthArg ){
+ if( ALWAYS(pAuthArg) ){
char *zAuthArg;
if( pAuthArg->op==TK_STRING ){
+ assert( !ExprHasProperty(pAuthArg, EP_IntValue) );
zAuthArg = pAuthArg->u.zToken;
}else{
zAuthArg = 0;
@@ -105401,18 +120068,15 @@ static void codeAttach(
assert( v || db->mallocFailed );
if( v ){
- sqlite3VdbeAddOp4(v, OP_Function0, 0, regArgs+3-pFunc->nArg, regArgs+3,
- (char *)pFunc, P4_FUNCDEF);
- assert( pFunc->nArg==-1 || (pFunc->nArg&0xff)==pFunc->nArg );
- sqlite3VdbeChangeP5(v, (u8)(pFunc->nArg));
-
+ sqlite3VdbeAddFunctionCall(pParse, 0, regArgs+3-pFunc->nArg, regArgs+3,
+ pFunc->nArg, pFunc, 0);
/* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this
** statement only). For DETACH, set it to false (expire all existing
** statements).
*/
sqlite3VdbeAddOp1(v, OP_Expire, (type==SQLITE_ATTACH));
}
-
+
attach_end:
sqlite3ExprDelete(db, pFilename);
sqlite3ExprDelete(db, pDbname);
@@ -105461,6 +120125,69 @@ SQLITE_PRIVATE void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *p
#endif /* SQLITE_OMIT_ATTACH */
/*
+** Expression callback used by sqlite3FixAAAA() routines.
+*/
+static int fixExprCb(Walker *p, Expr *pExpr){
+ DbFixer *pFix = p->u.pFix;
+ if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL);
+ if( pExpr->op==TK_VARIABLE ){
+ if( pFix->pParse->db->init.busy ){
+ pExpr->op = TK_NULL;
+ }else{
+ sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
+ return WRC_Abort;
+ }
+ }
+ return WRC_Continue;
+}
+
+/*
+** Select callback used by sqlite3FixAAAA() routines.
+*/
+static int fixSelectCb(Walker *p, Select *pSelect){
+ DbFixer *pFix = p->u.pFix;
+ int i;
+ SrcItem *pItem;
+ sqlite3 *db = pFix->pParse->db;
+ int iDb = sqlite3FindDbName(db, pFix->zDb);
+ SrcList *pList = pSelect->pSrc;
+
+ if( NEVER(pList==0) ) return WRC_Continue;
+ for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
+ if( pFix->bTemp==0 ){
+ if( pItem->zDatabase ){
+ if( iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){
+ sqlite3ErrorMsg(pFix->pParse,
+ "%s %T cannot reference objects in database %s",
+ pFix->zType, pFix->pName, pItem->zDatabase);
+ return WRC_Abort;
+ }
+ sqlite3DbFree(db, pItem->zDatabase);
+ pItem->zDatabase = 0;
+ pItem->fg.notCte = 1;
+ }
+ pItem->pSchema = pFix->pSchema;
+ pItem->fg.fromDDL = 1;
+ }
+#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
+ if( pList->a[i].fg.isUsing==0
+ && sqlite3WalkExpr(&pFix->w, pList->a[i].u3.pOn)
+ ){
+ return WRC_Abort;
+ }
+#endif
+ }
+ if( pSelect->pWith ){
+ for(i=0; i<pSelect->pWith->nCte; i++){
+ if( sqlite3WalkSelect(p, pSelect->pWith->a[i].pSelect) ){
+ return WRC_Abort;
+ }
+ }
+ }
+ return WRC_Continue;
+}
+
+/*
** Initialize a DbFixer structure. This routine must be called prior
** to passing the structure to one of the sqliteFixAAAA() routines below.
*/
@@ -105471,16 +120198,21 @@ SQLITE_PRIVATE void sqlite3FixInit(
const char *zType, /* "view", "trigger", or "index" */
const Token *pName /* Name of the view, trigger, or index */
){
- sqlite3 *db;
-
- db = pParse->db;
+ sqlite3 *db = pParse->db;
assert( db->nDb>iDb );
pFix->pParse = pParse;
pFix->zDb = db->aDb[iDb].zDbSName;
pFix->pSchema = db->aDb[iDb].pSchema;
pFix->zType = zType;
pFix->pName = pName;
- pFix->bVarOnly = (iDb==1);
+ pFix->bTemp = (iDb==1);
+ pFix->w.pParse = pParse;
+ pFix->w.xExprCallback = fixExprCb;
+ pFix->w.xSelectCallback = fixSelectCb;
+ pFix->w.xSelectCallback2 = sqlite3WalkWinDefnDummyCallback;
+ pFix->w.walkerDepth = 0;
+ pFix->w.eCode = 0;
+ pFix->w.u.pFix = pFix;
}
/*
@@ -105501,112 +120233,27 @@ SQLITE_PRIVATE int sqlite3FixSrcList(
DbFixer *pFix, /* Context of the fixation */
SrcList *pList /* The Source list to check and modify */
){
- int i;
- const char *zDb;
- struct SrcList_item *pItem;
-
- if( NEVER(pList==0) ) return 0;
- zDb = pFix->zDb;
- for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
- if( pFix->bVarOnly==0 ){
- if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
- sqlite3ErrorMsg(pFix->pParse,
- "%s %T cannot reference objects in database %s",
- pFix->zType, pFix->pName, pItem->zDatabase);
- return 1;
- }
- sqlite3DbFree(pFix->pParse->db, pItem->zDatabase);
- pItem->zDatabase = 0;
- pItem->pSchema = pFix->pSchema;
- }
-#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
- if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
- if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
-#endif
- if( pItem->fg.isTabFunc && sqlite3FixExprList(pFix, pItem->u1.pFuncArg) ){
- return 1;
- }
+ int res = 0;
+ if( pList ){
+ Select s;
+ memset(&s, 0, sizeof(s));
+ s.pSrc = pList;
+ res = sqlite3WalkSelect(&pFix->w, &s);
}
- return 0;
+ return res;
}
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
SQLITE_PRIVATE int sqlite3FixSelect(
DbFixer *pFix, /* Context of the fixation */
Select *pSelect /* The SELECT statement to be fixed to one database */
){
- while( pSelect ){
- if( sqlite3FixExprList(pFix, pSelect->pEList) ){
- return 1;
- }
- if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pSelect->pWhere) ){
- return 1;
- }
- if( sqlite3FixExprList(pFix, pSelect->pGroupBy) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pSelect->pHaving) ){
- return 1;
- }
- if( sqlite3FixExprList(pFix, pSelect->pOrderBy) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pSelect->pLimit) ){
- return 1;
- }
- if( pSelect->pWith ){
- int i;
- for(i=0; i<pSelect->pWith->nCte; i++){
- if( sqlite3FixSelect(pFix, pSelect->pWith->a[i].pSelect) ){
- return 1;
- }
- }
- }
- pSelect = pSelect->pPrior;
- }
- return 0;
+ return sqlite3WalkSelect(&pFix->w, pSelect);
}
SQLITE_PRIVATE int sqlite3FixExpr(
DbFixer *pFix, /* Context of the fixation */
Expr *pExpr /* The expression to be fixed to one database */
){
- while( pExpr ){
- if( pExpr->op==TK_VARIABLE ){
- if( pFix->pParse->db->init.busy ){
- pExpr->op = TK_NULL;
- }else{
- sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
- return 1;
- }
- }
- if( ExprHasProperty(pExpr, EP_TokenOnly|EP_Leaf) ) break;
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
- }else{
- if( sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1;
- }
- if( sqlite3FixExpr(pFix, pExpr->pRight) ){
- return 1;
- }
- pExpr = pExpr->pLeft;
- }
- return 0;
-}
-SQLITE_PRIVATE int sqlite3FixExprList(
- DbFixer *pFix, /* Context of the fixation */
- ExprList *pList /* The expression to be fixed to one database */
-){
- int i;
- struct ExprList_item *pItem;
- if( pList==0 ) return 0;
- for(i=0, pItem=pList->a; i<pList->nExpr; i++, pItem++){
- if( sqlite3FixExpr(pFix, pItem->pExpr) ){
- return 1;
- }
- }
- return 0;
+ return sqlite3WalkExpr(&pFix->w, pExpr);
}
#endif
@@ -105616,29 +120263,30 @@ SQLITE_PRIVATE int sqlite3FixTriggerStep(
TriggerStep *pStep /* The trigger step be fixed to one database */
){
while( pStep ){
- if( sqlite3FixSelect(pFix, pStep->pSelect) ){
- return 1;
- }
- if( sqlite3FixExpr(pFix, pStep->pWhere) ){
- return 1;
- }
- if( sqlite3FixExprList(pFix, pStep->pExprList) ){
+ if( sqlite3WalkSelect(&pFix->w, pStep->pSelect)
+ || sqlite3WalkExpr(&pFix->w, pStep->pWhere)
+ || sqlite3WalkExprList(&pFix->w, pStep->pExprList)
+ || sqlite3FixSrcList(pFix, pStep->pFrom)
+ ){
return 1;
}
#ifndef SQLITE_OMIT_UPSERT
- if( pStep->pUpsert ){
- Upsert *pUp = pStep->pUpsert;
- if( sqlite3FixExprList(pFix, pUp->pUpsertTarget)
- || sqlite3FixExpr(pFix, pUp->pUpsertTargetWhere)
- || sqlite3FixExprList(pFix, pUp->pUpsertSet)
- || sqlite3FixExpr(pFix, pUp->pUpsertWhere)
- ){
- return 1;
+ {
+ Upsert *pUp;
+ for(pUp=pStep->pUpsert; pUp; pUp=pUp->pNextUpsert){
+ if( sqlite3WalkExprList(&pFix->w, pUp->pUpsertTarget)
+ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertTargetWhere)
+ || sqlite3WalkExprList(&pFix->w, pUp->pUpsertSet)
+ || sqlite3WalkExpr(&pFix->w, pUp->pUpsertWhere)
+ ){
+ return 1;
+ }
}
}
#endif
pStep = pStep->pNext;
}
+
return 0;
}
#endif
@@ -105725,7 +120373,7 @@ SQLITE_API int sqlite3_set_authorizer(
sqlite3_mutex_enter(db->mutex);
db->xAuth = (sqlite3_xauth)xAuth;
db->pAuthArg = pArg;
- sqlite3ExpirePreparedStatements(db, 0);
+ if( db->xAuth ) sqlite3ExpirePreparedStatements(db, 1);
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
@@ -105777,10 +120425,10 @@ SQLITE_PRIVATE int sqlite3AuthReadCol(
/*
** The pExpr should be a TK_COLUMN expression. The table referred to
-** is in pTabList or else it is the NEW or OLD table of a trigger.
+** is in pTabList or else it is the NEW or OLD table of a trigger.
** Check to see if it is OK to read this particular column.
**
-** If the auth function returns SQLITE_IGNORE, change the TK_COLUMN
+** If the auth function returns SQLITE_IGNORE, change the TK_COLUMN
** instruction into a TK_NULL. If the auth function returns SQLITE_DENY,
** then generate an error.
*/
@@ -105790,7 +120438,6 @@ SQLITE_PRIVATE void sqlite3AuthRead(
Schema *pSchema, /* The schema of the expression */
SrcList *pTabList /* All table that pExpr might refer to */
){
- sqlite3 *db = pParse->db;
Table *pTab = 0; /* The table being read */
const char *zCol; /* Name of the column of the table */
int iSrc; /* Index in pTabList->a[] of table being read */
@@ -105798,8 +120445,8 @@ SQLITE_PRIVATE void sqlite3AuthRead(
int iCol; /* Index of column in table */
assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER );
- assert( !IN_RENAME_OBJECT || db->xAuth==0 );
- if( db->xAuth==0 ) return;
+ assert( !IN_RENAME_OBJECT );
+ assert( pParse->db->xAuth!=0 );
iDb = sqlite3SchemaToIndex(pParse->db, pSchema);
if( iDb<0 ){
/* An attempt to read a column out of a subquery or other
@@ -105811,7 +120458,7 @@ SQLITE_PRIVATE void sqlite3AuthRead(
pTab = pParse->pTriggerTab;
}else{
assert( pTabList );
- for(iSrc=0; ALWAYS(iSrc<pTabList->nSrc); iSrc++){
+ for(iSrc=0; iSrc<pTabList->nSrc; iSrc++){
if( pExpr->iTable==pTabList->a[iSrc].iCursor ){
pTab = pTabList->a[iSrc].pTab;
break;
@@ -105819,18 +120466,18 @@ SQLITE_PRIVATE void sqlite3AuthRead(
}
}
iCol = pExpr->iColumn;
- if( NEVER(pTab==0) ) return;
+ if( pTab==0 ) return;
if( iCol>=0 ){
assert( iCol<pTab->nCol );
- zCol = pTab->aCol[iCol].zName;
+ zCol = pTab->aCol[iCol].zCnName;
}else if( pTab->iPKey>=0 ){
assert( pTab->iPKey<pTab->nCol );
- zCol = pTab->aCol[pTab->iPKey].zName;
+ zCol = pTab->aCol[pTab->iPKey].zCnName;
}else{
zCol = "ROWID";
}
- assert( iDb>=0 && iDb<db->nDb );
+ assert( iDb>=0 && iDb<pParse->db->nDb );
if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){
pExpr->op = TK_NULL;
}
@@ -105852,15 +120499,11 @@ SQLITE_PRIVATE int sqlite3AuthCheck(
sqlite3 *db = pParse->db;
int rc;
- /* Don't do any authorization checks if the database is initialising
+ /* Don't do any authorization checks if the database is initializing
** or if the parser is being invoked from within sqlite3_declare_vtab.
*/
assert( !IN_RENAME_OBJECT || db->xAuth==0 );
- if( db->init.busy || IN_SPECIAL_PARSE ){
- return SQLITE_OK;
- }
-
- if( db->xAuth==0 ){
+ if( db->xAuth==0 || db->init.busy || IN_SPECIAL_PARSE ){
return SQLITE_OK;
}
@@ -105897,7 +120540,7 @@ SQLITE_PRIVATE int sqlite3AuthCheck(
*/
SQLITE_PRIVATE void sqlite3AuthContextPush(
Parse *pParse,
- AuthContext *pContext,
+ AuthContext *pContext,
const char *zContext
){
assert( pParse );
@@ -105954,13 +120597,13 @@ SQLITE_PRIVATE void sqlite3AuthContextPop(AuthContext *pContext){
*/
struct TableLock {
int iDb; /* The database containing the table to be locked */
- int iTab; /* The root page of the table to be locked */
+ Pgno iTab; /* The root page of the table to be locked */
u8 isWriteLock; /* True for write lock. False for a read lock */
const char *zLockName; /* Name of the table */
};
/*
-** Record the fact that we want to lock a table at run-time.
+** Record the fact that we want to lock a table at run-time.
**
** The table to be locked has root page iTab and is found in database iDb.
** A read or a write lock can be taken depending on isWritelock.
@@ -105969,21 +120612,20 @@ struct TableLock {
** code to make the lock occur is generated by a later call to
** codeTableLocks() which occurs during sqlite3FinishCoding().
*/
-SQLITE_PRIVATE void sqlite3TableLock(
+static SQLITE_NOINLINE void lockTable(
Parse *pParse, /* Parsing context */
int iDb, /* Index of the database containing the table to lock */
- int iTab, /* Root page number of the table to be locked */
+ Pgno iTab, /* Root page number of the table to be locked */
u8 isWriteLock, /* True for a write lock */
const char *zName /* Name of the table to be locked */
){
- Parse *pToplevel = sqlite3ParseToplevel(pParse);
+ Parse *pToplevel;
int i;
int nBytes;
TableLock *p;
assert( iDb>=0 );
- if( iDb==1 ) return;
- if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return;
+ pToplevel = sqlite3ParseToplevel(pParse);
for(i=0; i<pToplevel->nTableLock; i++){
p = &pToplevel->aTableLock[i];
if( p->iDb==iDb && p->iTab==iTab ){
@@ -106006,6 +120648,17 @@ SQLITE_PRIVATE void sqlite3TableLock(
sqlite3OomFault(pToplevel->db);
}
}
+SQLITE_PRIVATE void sqlite3TableLock(
+ Parse *pParse, /* Parsing context */
+ int iDb, /* Index of the database containing the table to lock */
+ Pgno iTab, /* Root page number of the table to be locked */
+ u8 isWriteLock, /* True for a write lock */
+ const char *zName /* Name of the table to be locked */
+){
+ if( iDb==1 ) return;
+ if( !sqlite3BtreeSharable(pParse->db->aDb[iDb].pBt) ) return;
+ lockTable(pParse, iDb, iTab, isWriteLock, zName);
+}
/*
** Code an OP_TableLock instruction for each table locked by the
@@ -106013,10 +120666,8 @@ SQLITE_PRIVATE void sqlite3TableLock(
*/
static void codeTableLocks(Parse *pParse){
int i;
- Vdbe *pVdbe;
-
- pVdbe = sqlite3GetVdbe(pParse);
- assert( pVdbe!=0 ); /* sqlite3GetVdbe cannot fail: VDBE already allocated */
+ Vdbe *pVdbe = pParse->pVdbe;
+ assert( pVdbe!=0 );
for(i=0; i<pParse->nTableLock; i++){
TableLock *p = &pParse->aTableLock[i];
@@ -106055,22 +120706,53 @@ SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask m){
SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
sqlite3 *db;
Vdbe *v;
+ int iDb, i;
assert( pParse->pToplevel==0 );
db = pParse->db;
+ assert( db->pParse==pParse );
if( pParse->nested ) return;
- if( db->mallocFailed || pParse->nErr ){
- if( pParse->rc==SQLITE_OK ) pParse->rc = SQLITE_ERROR;
+ if( pParse->nErr ){
+ if( db->mallocFailed ) pParse->rc = SQLITE_NOMEM;
return;
}
+ assert( db->mallocFailed==0 );
/* Begin by generating some termination code at the end of the
** vdbe program
*/
- v = sqlite3GetVdbe(pParse);
- assert( !pParse->isMultiWrite
+ v = pParse->pVdbe;
+ if( v==0 ){
+ if( db->init.busy ){
+ pParse->rc = SQLITE_DONE;
+ return;
+ }
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) pParse->rc = SQLITE_ERROR;
+ }
+ assert( !pParse->isMultiWrite
|| sqlite3VdbeAssertMayAbort(v, pParse->mayAbort));
if( v ){
+ if( pParse->bReturning ){
+ Returning *pReturning = pParse->u1.pReturning;
+ int addrRewind;
+ int reg;
+
+ if( pReturning->nRetCol ){
+ sqlite3VdbeAddOp0(v, OP_FkCheck);
+ addrRewind =
+ sqlite3VdbeAddOp1(v, OP_Rewind, pReturning->iRetCur);
+ VdbeCoverage(v);
+ reg = pReturning->iRetReg;
+ for(i=0; i<pReturning->nRetCol; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, pReturning->iRetCur, i, reg+i);
+ }
+ sqlite3VdbeAddOp2(v, OP_ResultRow, reg, i);
+ sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1);
+ VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addrRewind);
+ }
+ }
sqlite3VdbeAddOp0(v, OP_Halt);
#if SQLITE_USER_AUTHENTICATION
@@ -106090,67 +120772,76 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
** transaction on each used database and to verify the schema cookie
** on each used database.
*/
- if( db->mallocFailed==0
- && (DbMaskNonZero(pParse->cookieMask) || pParse->pConstExpr)
- ){
- int iDb, i;
- assert( sqlite3VdbeGetOp(v, 0)->opcode==OP_Init );
- sqlite3VdbeJumpHere(v, 0);
- for(iDb=0; iDb<db->nDb; iDb++){
- Schema *pSchema;
- if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue;
- sqlite3VdbeUsesBtree(v, iDb);
- pSchema = db->aDb[iDb].pSchema;
- sqlite3VdbeAddOp4Int(v,
- OP_Transaction, /* Opcode */
- iDb, /* P1 */
- DbMaskTest(pParse->writeMask,iDb), /* P2 */
- pSchema->schema_cookie, /* P3 */
- pSchema->iGeneration /* P4 */
- );
- if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1);
- VdbeComment((v,
- "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite));
- }
+ assert( pParse->nErr>0 || sqlite3VdbeGetOp(v, 0)->opcode==OP_Init );
+ sqlite3VdbeJumpHere(v, 0);
+ assert( db->nDb>0 );
+ iDb = 0;
+ do{
+ Schema *pSchema;
+ if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue;
+ sqlite3VdbeUsesBtree(v, iDb);
+ pSchema = db->aDb[iDb].pSchema;
+ sqlite3VdbeAddOp4Int(v,
+ OP_Transaction, /* Opcode */
+ iDb, /* P1 */
+ DbMaskTest(pParse->writeMask,iDb), /* P2 */
+ pSchema->schema_cookie, /* P3 */
+ pSchema->iGeneration /* P4 */
+ );
+ if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1);
+ VdbeComment((v,
+ "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite));
+ }while( ++iDb<db->nDb );
#ifndef SQLITE_OMIT_VIRTUALTABLE
- for(i=0; i<pParse->nVtabLock; i++){
- char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]);
- sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
- }
- pParse->nVtabLock = 0;
+ for(i=0; i<pParse->nVtabLock; i++){
+ char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]);
+ sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
+ }
+ pParse->nVtabLock = 0;
#endif
- /* Once all the cookies have been verified and transactions opened,
- ** obtain the required table-locks. This is a no-op unless the
- ** shared-cache feature is enabled.
- */
- codeTableLocks(pParse);
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ /* Once all the cookies have been verified and transactions opened,
+ ** obtain the required table-locks. This is a no-op unless the
+ ** shared-cache feature is enabled.
+ */
+ if( pParse->nTableLock ) codeTableLocks(pParse);
+#endif
- /* Initialize any AUTOINCREMENT data structures required.
- */
- sqlite3AutoincrementBegin(pParse);
+ /* Initialize any AUTOINCREMENT data structures required.
+ */
+ if( pParse->pAinc ) sqlite3AutoincrementBegin(pParse);
- /* Code constant expressions that where factored out of inner loops */
- if( pParse->pConstExpr ){
- ExprList *pEL = pParse->pConstExpr;
- pParse->okConstFactor = 0;
- for(i=0; i<pEL->nExpr; i++){
- sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg);
- }
+ /* Code constant expressions that were factored out of inner loops.
+ */
+ if( pParse->pConstExpr ){
+ ExprList *pEL = pParse->pConstExpr;
+ pParse->okConstFactor = 0;
+ for(i=0; i<pEL->nExpr; i++){
+ assert( pEL->a[i].u.iConstExprReg>0 );
+ sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg);
}
+ }
- /* Finally, jump back to the beginning of the executable code. */
- sqlite3VdbeGoto(v, 1);
+ if( pParse->bReturning ){
+ Returning *pRet = pParse->u1.pReturning;
+ if( pRet->nRetCol ){
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol);
+ }
}
- }
+ /* Finally, jump back to the beginning of the executable code. */
+ sqlite3VdbeGoto(v, 1);
+ }
/* Get the VDBE program ready for execution
*/
- if( v && pParse->nErr==0 && !db->mallocFailed ){
+ assert( v!=0 || pParse->nErr );
+ assert( db->mallocFailed==0 || pParse->nErr );
+ if( pParse->nErr==0 ){
/* A minimum of one cursor is required if autoincrement is used
* See ticket [a696379c1f08866] */
- if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1;
+ assert( pParse->pAinc==0 || pParse->nTab>0 );
sqlite3VdbeMakeReady(v, pParse);
pParse->rc = SQLITE_DONE;
}else{
@@ -106161,35 +120852,43 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
/*
** Run the parser and code generator recursively in order to generate
** code for the SQL statement given onto the end of the pParse context
-** currently under construction. When the parser is run recursively
-** this way, the final OP_Halt is not appended and other initialization
-** and finalization steps are omitted because those are handling by the
-** outermost parser.
+** currently under construction. Notes:
**
-** Not everything is nestable. This facility is designed to permit
-** INSERT, UPDATE, and DELETE operations against SQLITE_MASTER. Use
-** care if you decide to try to use this routine for some other purposes.
+** * The final OP_Halt is not appended and other initialization
+** and finalization steps are omitted because those are handling by the
+** outermost parser.
+**
+** * Built-in SQL functions always take precedence over application-defined
+** SQL functions. In other words, it is not possible to override a
+** built-in function.
*/
SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
va_list ap;
char *zSql;
- char *zErrMsg = 0;
sqlite3 *db = pParse->db;
+ u32 savedDbFlags = db->mDbFlags;
char saveBuf[PARSE_TAIL_SZ];
if( pParse->nErr ) return;
+ if( pParse->eParseMode ) return;
assert( pParse->nested<10 ); /* Nesting should only be of limited depth */
va_start(ap, zFormat);
zSql = sqlite3VMPrintf(db, zFormat, ap);
va_end(ap);
if( zSql==0 ){
- return; /* A malloc must have failed */
+ /* This can result either from an OOM or because the formatted string
+ ** exceeds SQLITE_LIMIT_LENGTH. In the latter case, we need to set
+ ** an error */
+ if( !db->mallocFailed ) pParse->rc = SQLITE_TOOBIG;
+ pParse->nErr++;
+ return;
}
pParse->nested++;
memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ);
memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ);
- sqlite3RunParser(pParse, zSql, &zErrMsg);
- sqlite3DbFree(db, zErrMsg);
+ db->mDbFlags |= DBFLAG_PreferBuiltin;
+ sqlite3RunParser(pParse, zSql);
+ db->mDbFlags = savedDbFlags;
sqlite3DbFree(db, zSql);
memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ);
pParse->nested--;
@@ -106230,22 +120929,59 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha
return 0;
}
#endif
- while(1){
- for(i=OMIT_TEMPDB; i<db->nDb; i++){
- int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
- if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
- assert( sqlite3SchemaMutexHeld(db, j, 0) );
- p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
- if( p ) return p;
+ if( zDatabase ){
+ for(i=0; i<db->nDb; i++){
+ if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break;
+ }
+ if( i>=db->nDb ){
+ /* No match against the official names. But always match "main"
+ ** to schema 0 as a legacy fallback. */
+ if( sqlite3StrICmp(zDatabase,"main")==0 ){
+ i = 0;
+ }else{
+ return 0;
+ }
+ }
+ p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName);
+ if( p==0 && sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){
+ if( i==1 ){
+ if( sqlite3StrICmp(zName+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0
+ || sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0
+ || sqlite3StrICmp(zName+7, &LEGACY_SCHEMA_TABLE[7])==0
+ ){
+ p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash,
+ LEGACY_TEMP_SCHEMA_TABLE);
+ }
+ }else{
+ if( sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 ){
+ p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash,
+ LEGACY_SCHEMA_TABLE);
+ }
+ }
+ }
+ }else{
+ /* Match against TEMP first */
+ p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash, zName);
+ if( p ) return p;
+ /* The main database is second */
+ p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, zName);
+ if( p ) return p;
+ /* Attached databases are in order of attachment */
+ for(i=2; i<db->nDb; i++){
+ assert( sqlite3SchemaMutexHeld(db, i, 0) );
+ p = sqlite3HashFind(&db->aDb[i].pSchema->tblHash, zName);
+ if( p ) break;
+ }
+ if( p==0 && sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){
+ if( sqlite3StrICmp(zName+7, &PREFERRED_SCHEMA_TABLE[7])==0 ){
+ p = sqlite3HashFind(&db->aDb[0].pSchema->tblHash, LEGACY_SCHEMA_TABLE);
+ }else if( sqlite3StrICmp(zName+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){
+ p = sqlite3HashFind(&db->aDb[1].pSchema->tblHash,
+ LEGACY_TEMP_SCHEMA_TABLE);
}
}
- /* Not found. If the name we were looking for was temp.sqlite_master
- ** then change the name to sqlite_temp_master and try again. */
- if( sqlite3StrICmp(zName, MASTER_NAME)!=0 ) break;
- if( sqlite3_stricmp(zDatabase, db->aDb[1].zDbSName)!=0 ) break;
- zName = TEMP_MASTER_NAME;
}
- return 0;
+ return p;
}
/*
@@ -106269,7 +121005,7 @@ SQLITE_PRIVATE Table *sqlite3LocateTable(
/* Read the database schema. If an error occurs, leave an error message
** and code in pParse and return NULL. */
- if( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0
+ if( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0
&& SQLITE_OK!=sqlite3ReadSchema(pParse)
){
return 0;
@@ -106277,27 +121013,36 @@ SQLITE_PRIVATE Table *sqlite3LocateTable(
p = sqlite3FindTable(db, zName, zDbase);
if( p==0 ){
- const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table";
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* If zName is the not the name of a table in the schema created using
** CREATE, then check to see if it is the name of an virtual table that
** can be an eponymous virtual table. */
- Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
- if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
- pMod = sqlite3PragmaVtabRegister(db, zName);
- }
- if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
- return pMod->pEpoTab;
+ if( (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)==0 && db->init.busy==0 ){
+ Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
+ if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
+ pMod = sqlite3PragmaVtabRegister(db, zName);
+ }
+ if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
+ testcase( pMod->pEpoTab==0 );
+ return pMod->pEpoTab;
+ }
}
#endif
- if( (flags & LOCATE_NOERR)==0 ){
- if( zDbase ){
- sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
- }else{
- sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
- }
- pParse->checkSchema = 1;
+ if( flags & LOCATE_NOERR ) return 0;
+ pParse->checkSchema = 1;
+ }else if( IsVirtual(p) && (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)!=0 ){
+ p = 0;
+ }
+
+ if( p==0 ){
+ const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table";
+ if( zDbase ){
+ sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
+ }else{
+ sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
}
+ }else{
+ assert( HasRowid(p) || p->iPKey<0 );
}
return p;
@@ -106313,9 +121058,9 @@ SQLITE_PRIVATE Table *sqlite3LocateTable(
** sqlite3FixSrcList() for details.
*/
SQLITE_PRIVATE Table *sqlite3LocateTableItem(
- Parse *pParse,
+ Parse *pParse,
u32 flags,
- struct SrcList_item *p
+ SrcItem *p
){
const char *zDb;
assert( p->pSchema==0 || p->zDatabase==0 );
@@ -106329,7 +121074,23 @@ SQLITE_PRIVATE Table *sqlite3LocateTableItem(
}
/*
-** Locate the in-memory structure that describes
+** Return the preferred table name for system tables. Translate legacy
+** names into the new preferred names, as appropriate.
+*/
+SQLITE_PRIVATE const char *sqlite3PreferredTableName(const char *zName){
+ if( sqlite3StrNICmp(zName, "sqlite_", 7)==0 ){
+ if( sqlite3StrICmp(zName+7, &LEGACY_SCHEMA_TABLE[7])==0 ){
+ return PREFERRED_SCHEMA_TABLE;
+ }
+ if( sqlite3StrICmp(zName+7, &LEGACY_TEMP_SCHEMA_TABLE[7])==0 ){
+ return PREFERRED_TEMP_SCHEMA_TABLE;
+ }
+ }
+ return zName;
+}
+
+/*
+** Locate the in-memory structure that describes
** a particular index given the name of that index
** and the name of the database that contains the index.
** Return NULL if not found.
@@ -106349,7 +121110,7 @@ SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const cha
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
Schema *pSchema = db->aDb[j].pSchema;
assert( pSchema );
- if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zDbSName) ) continue;
+ if( zDb && sqlite3DbIsNamed(db, j, zDb)==0 ) continue;
assert( sqlite3SchemaMutexHeld(db, j, 0) );
p = sqlite3HashFind(&pSchema->idxHash, zName);
if( p ) break;
@@ -106368,7 +121129,7 @@ SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3 *db, Index *p){
sqlite3ExprListDelete(db, p->aColExpr);
sqlite3DbFree(db, p->zColAff);
if( p->isResized ) sqlite3DbFree(db, (void *)p->azColl);
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3_free(p->aiRowEst);
#endif
sqlite3DbFree(db, p);
@@ -106493,6 +121254,84 @@ SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3 *db){
}
/*
+** Set the expression associated with a column. This is usually
+** the DEFAULT value, but might also be the expression that computes
+** the value for a generated column.
+*/
+SQLITE_PRIVATE void sqlite3ColumnSetExpr(
+ Parse *pParse, /* Parsing context */
+ Table *pTab, /* The table containing the column */
+ Column *pCol, /* The column to receive the new DEFAULT expression */
+ Expr *pExpr /* The new default expression */
+){
+ ExprList *pList;
+ assert( IsOrdinaryTable(pTab) );
+ pList = pTab->u.tab.pDfltList;
+ if( pCol->iDflt==0
+ || NEVER(pList==0)
+ || NEVER(pList->nExpr<pCol->iDflt)
+ ){
+ pCol->iDflt = pList==0 ? 1 : pList->nExpr+1;
+ pTab->u.tab.pDfltList = sqlite3ExprListAppend(pParse, pList, pExpr);
+ }else{
+ sqlite3ExprDelete(pParse->db, pList->a[pCol->iDflt-1].pExpr);
+ pList->a[pCol->iDflt-1].pExpr = pExpr;
+ }
+}
+
+/*
+** Return the expression associated with a column. The expression might be
+** the DEFAULT clause or the AS clause of a generated column.
+** Return NULL if the column has no associated expression.
+*/
+SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){
+ if( pCol->iDflt==0 ) return 0;
+ if( !IsOrdinaryTable(pTab) ) return 0;
+ if( NEVER(pTab->u.tab.pDfltList==0) ) return 0;
+ if( NEVER(pTab->u.tab.pDfltList->nExpr<pCol->iDflt) ) return 0;
+ return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
+}
+
+/*
+** Set the collating sequence name for a column.
+*/
+SQLITE_PRIVATE void sqlite3ColumnSetColl(
+ sqlite3 *db,
+ Column *pCol,
+ const char *zColl
+){
+ i64 nColl;
+ i64 n;
+ char *zNew;
+ assert( zColl!=0 );
+ n = sqlite3Strlen30(pCol->zCnName) + 1;
+ if( pCol->colFlags & COLFLAG_HASTYPE ){
+ n += sqlite3Strlen30(pCol->zCnName+n) + 1;
+ }
+ nColl = sqlite3Strlen30(zColl) + 1;
+ zNew = sqlite3DbRealloc(db, pCol->zCnName, nColl+n);
+ if( zNew ){
+ pCol->zCnName = zNew;
+ memcpy(pCol->zCnName + n, zColl, nColl);
+ pCol->colFlags |= COLFLAG_HASCOLL;
+ }
+}
+
+/*
+** Return the collating sequence name for a column
+*/
+SQLITE_PRIVATE const char *sqlite3ColumnColl(Column *pCol){
+ const char *z;
+ if( (pCol->colFlags & COLFLAG_HASCOLL)==0 ) return 0;
+ z = pCol->zCnName;
+ while( *z ){ z++; }
+ if( pCol->colFlags & COLFLAG_HASTYPE ){
+ do{ z++; }while( *z );
+ }
+ return z+1;
+}
+
+/*
** Delete memory allocated for the column names of a table or view (the
** Table.aCol[] array).
*/
@@ -106500,13 +121339,23 @@ SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
int i;
Column *pCol;
assert( pTable!=0 );
+ assert( db!=0 );
if( (pCol = pTable->aCol)!=0 ){
for(i=0; i<pTable->nCol; i++, pCol++){
- sqlite3DbFree(db, pCol->zName);
- sqlite3ExprDelete(db, pCol->pDflt);
- sqlite3DbFree(db, pCol->zColl);
+ assert( pCol->zCnName==0 || pCol->hName==sqlite3StrIHash(pCol->zCnName) );
+ sqlite3DbFree(db, pCol->zCnName);
+ }
+ sqlite3DbNNFreeNN(db, pTable->aCol);
+ if( IsOrdinaryTable(pTable) ){
+ sqlite3ExprListDelete(db, pTable->u.tab.pDfltList);
+ }
+ if( db->pnBytesFreed==0 ){
+ pTable->aCol = 0;
+ pTable->nCol = 0;
+ if( IsOrdinaryTable(pTable) ){
+ pTable->u.tab.pDfltList = 0;
+ }
}
- sqlite3DbFree(db, pTable->aCol);
}
}
@@ -106516,10 +121365,10 @@ SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
**
** This routine just deletes the data structure. It does not unlink
** the table data structure from the hash table. But it does destroy
-** memory structures of the indices and foreign keys associated with
+** memory structures of the indices and foreign keys associated with
** the table.
**
-** The db parameter is optional. It is needed if the Table object
+** The db parameter is optional. It is needed if the Table object
** contains lookaside memory. (Table objects in the schema do not use
** lookaside memory, but some ephemeral Table objects do.) Or the
** db parameter can be used with db->pnBytesFreed to measure the memory
@@ -106530,10 +121379,15 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
#ifdef SQLITE_DEBUG
/* Record the number of outstanding lookaside allocations in schema Tables
- ** prior to doing any free() operations. Since schema Tables do not use
- ** lookaside, this number should not change. */
+ ** prior to doing any free() operations. Since schema Tables do not use
+ ** lookaside, this number should not change.
+ **
+ ** If malloc has already failed, it may be that it failed while allocating
+ ** a Table object that was going to be marked ephemeral. So do not check
+ ** that no lookaside memory is used in this case either. */
int nLookaside = 0;
- if( db && (pTable->tabFlags & TF_Ephemeral)==0 ){
+ assert( db!=0 );
+ if( !db->mallocFailed && (pTable->tabFlags & TF_Ephemeral)==0 ){
nLookaside = sqlite3LookasideUsed(db, 0);
}
#endif
@@ -106543,8 +121397,8 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
pNext = pIndex->pNext;
assert( pIndex->pSchema==pTable->pSchema
|| (IsVirtual(pTable) && pIndex->idxType!=SQLITE_IDXTYPE_APPDEF) );
- if( (db==0 || db->pnBytesFreed==0) && !IsVirtual(pTable) ){
- char *zName = pIndex->zName;
+ if( db->pnBytesFreed==0 && !IsVirtual(pTable) ){
+ char *zName = pIndex->zName;
TESTONLY ( Index *pOld = ) sqlite3HashInsert(
&pIndex->pSchema->idxHash, zName, 0
);
@@ -106554,25 +121408,25 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
sqlite3FreeIndex(db, pIndex);
}
- /* Delete any foreign keys attached to this table. */
- sqlite3FkDelete(db, pTable);
+ if( IsOrdinaryTable(pTable) ){
+ sqlite3FkDelete(db, pTable);
+ }
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ else if( IsVirtual(pTable) ){
+ sqlite3VtabClear(db, pTable);
+ }
+#endif
+ else{
+ assert( IsView(pTable) );
+ sqlite3SelectDelete(db, pTable->u.view.pSelect);
+ }
/* Delete the Table structure itself.
*/
-#ifdef SQLITE_ENABLE_NORMALIZE
- if( pTable->pColHash ){
- sqlite3HashClear(pTable->pColHash);
- sqlite3_free(pTable->pColHash);
- }
-#endif
sqlite3DeleteColumnNames(db, pTable);
sqlite3DbFree(db, pTable->zName);
sqlite3DbFree(db, pTable->zColAff);
- sqlite3SelectDelete(db, pTable->pSelect);
sqlite3ExprListDelete(db, pTable->pCheck);
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- sqlite3VtabClear(db, pTable);
-#endif
sqlite3DbFree(db, pTable);
/* Verify that no lookaside memory was used by schema tables */
@@ -106580,10 +121434,14 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
}
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
/* Do not delete the table until the reference count reaches zero. */
+ assert( db!=0 );
if( !pTable ) return;
- if( ((!db || db->pnBytesFreed==0) && (--pTable->nTabRef)>0) ) return;
+ if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return;
deleteTable(db, pTable);
}
+SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){
+ sqlite3DeleteTable(db, (Table*)pTable);
+}
/*
@@ -106618,10 +121476,10 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char
** are not \000 terminated and are not persistent. The returned string
** is \000 terminated and is persistent.
*/
-SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
+SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, const Token *pName){
char *zName;
if( pName ){
- zName = sqlite3DbStrNDup(db, (char*)pName->z, pName->n);
+ zName = sqlite3DbStrNDup(db, (const char*)pName->z, pName->n);
sqlite3Dequote(zName);
}else{
zName = 0;
@@ -106630,13 +121488,13 @@ SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
}
/*
-** Open the sqlite_master table stored in database number iDb for
+** Open the sqlite_schema table stored in database number iDb for
** writing. The table is opened using cursor 0.
*/
-SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *p, int iDb){
+SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *p, int iDb){
Vdbe *v = sqlite3GetVdbe(p);
- sqlite3TableLock(p, iDb, MASTER_ROOT, 1, MASTER_NAME);
- sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, MASTER_ROOT, iDb, 5);
+ sqlite3TableLock(p, iDb, SCHEMA_ROOT, 1, LEGACY_SCHEMA_TABLE);
+ sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, SCHEMA_ROOT, iDb, 5);
if( p->nTab==0 ){
p->nTab = 1;
}
@@ -106665,7 +121523,7 @@ SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *db, const char *zName){
/*
** The token *pName contains the name of a database (either "main" or
** "temp" or the name of an attached db). This routine returns the
-** index of the named database in db->aDb[], or -1 if the named db
+** index of the named database in db->aDb[], or -1 if the named db
** does not exist.
*/
SQLITE_PRIVATE int sqlite3FindDb(sqlite3 *db, Token *pName){
@@ -106681,7 +121539,7 @@ SQLITE_PRIVATE int sqlite3FindDb(sqlite3 *db, Token *pName){
** pName1 and pName2. If the table name was fully qualified, for example:
**
** CREATE TABLE xxx.yyy (...);
-**
+**
** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
** the table name is not fully qualified, i.e.:
**
@@ -106715,7 +121573,7 @@ SQLITE_PRIVATE int sqlite3TwoPartName(
return -1;
}
}else{
- assert( db->init.iDb==0 || db->init.busy || IN_RENAME_OBJECT
+ assert( db->init.iDb==0 || db->init.busy || IN_SPECIAL_PARSE
|| (db->mDbFlags & DBFLAG_Vacuum)!=0);
iDb = db->init.iDb;
*pUnqual = pName1;
@@ -106743,13 +121601,42 @@ SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3 *db){
** trigger). All names are legal except those that begin with the string
** "sqlite_" (in upper, lower or mixed case). This portion of the namespace
** is reserved for internal use.
+**
+** When parsing the sqlite_schema table, this routine also checks to
+** make sure the "type", "name", and "tbl_name" columns are consistent
+** with the SQL.
*/
-SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *pParse, const char *zName){
- if( !pParse->db->init.busy && pParse->nested==0
- && sqlite3WritableSchema(pParse->db)==0
- && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
- sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName);
- return SQLITE_ERROR;
+SQLITE_PRIVATE int sqlite3CheckObjectName(
+ Parse *pParse, /* Parsing context */
+ const char *zName, /* Name of the object to check */
+ const char *zType, /* Type of this object */
+ const char *zTblName /* Parent table name for triggers and indexes */
+){
+ sqlite3 *db = pParse->db;
+ if( sqlite3WritableSchema(db)
+ || db->init.imposterTable
+ || !sqlite3Config.bExtraSchemaChecks
+ ){
+ /* Skip these error checks for writable_schema=ON */
+ return SQLITE_OK;
+ }
+ if( db->init.busy ){
+ if( sqlite3_stricmp(zType, db->init.azInit[0])
+ || sqlite3_stricmp(zName, db->init.azInit[1])
+ || sqlite3_stricmp(zTblName, db->init.azInit[2])
+ ){
+ sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */
+ return SQLITE_ERROR;
+ }
+ }else{
+ if( (pParse->nested==0 && 0==sqlite3StrNICmp(zName, "sqlite_", 7))
+ || (sqlite3ReadOnlyShadowTables(db) && sqlite3ShadowTableName(db, zName))
+ ){
+ sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s",
+ zName);
+ return SQLITE_ERROR;
+ }
+
}
return SQLITE_OK;
}
@@ -106764,10 +121651,12 @@ SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table *pTab){
}
/*
-** Return the column of index pIdx that corresponds to table
-** column iCol. Return -1 if not found.
+** Convert an table column number into a index column number. That is,
+** for the column iCol in the table (as defined by the CREATE TABLE statement)
+** find the (first) offset of that column in index pIdx. Or return -1
+** if column iCol is not used in index pIdx.
*/
-SQLITE_PRIVATE i16 sqlite3ColumnOfIndex(Index *pIdx, i16 iCol){
+SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index *pIdx, i16 iCol){
int i;
for(i=0; i<pIdx->nColumn; i++){
if( iCol==pIdx->aiColumn[i] ) return i;
@@ -106775,6 +121664,101 @@ SQLITE_PRIVATE i16 sqlite3ColumnOfIndex(Index *pIdx, i16 iCol){
return -1;
}
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+/* Convert a storage column number into a table column number.
+**
+** The storage column number (0,1,2,....) is the index of the value
+** as it appears in the record on disk. The true column number
+** is the index (0,1,2,...) of the column in the CREATE TABLE statement.
+**
+** The storage column number is less than the table column number if
+** and only there are VIRTUAL columns to the left.
+**
+** If SQLITE_OMIT_GENERATED_COLUMNS, this routine is a no-op macro.
+*/
+SQLITE_PRIVATE i16 sqlite3StorageColumnToTable(Table *pTab, i16 iCol){
+ if( pTab->tabFlags & TF_HasVirtual ){
+ int i;
+ for(i=0; i<=iCol; i++){
+ if( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL ) iCol++;
+ }
+ }
+ return iCol;
+}
+#endif
+
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+/* Convert a table column number into a storage column number.
+**
+** The storage column number (0,1,2,....) is the index of the value
+** as it appears in the record on disk. Or, if the input column is
+** the N-th virtual column (zero-based) then the storage number is
+** the number of non-virtual columns in the table plus N.
+**
+** The true column number is the index (0,1,2,...) of the column in
+** the CREATE TABLE statement.
+**
+** If the input column is a VIRTUAL column, then it should not appear
+** in storage. But the value sometimes is cached in registers that
+** follow the range of registers used to construct storage. This
+** avoids computing the same VIRTUAL column multiple times, and provides
+** values for use by OP_Param opcodes in triggers. Hence, if the
+** input column is a VIRTUAL table, put it after all the other columns.
+**
+** In the following, N means "normal column", S means STORED, and
+** V means VIRTUAL. Suppose the CREATE TABLE has columns like this:
+**
+** CREATE TABLE ex(N,S,V,N,S,V,N,S,V);
+** -- 0 1 2 3 4 5 6 7 8
+**
+** Then the mapping from this function is as follows:
+**
+** INPUTS: 0 1 2 3 4 5 6 7 8
+** OUTPUTS: 0 1 6 2 3 7 4 5 8
+**
+** So, in other words, this routine shifts all the virtual columns to
+** the end.
+**
+** If SQLITE_OMIT_GENERATED_COLUMNS then there are no virtual columns and
+** this routine is a no-op macro. If the pTab does not have any virtual
+** columns, then this routine is no-op that always return iCol. If iCol
+** is negative (indicating the ROWID column) then this routine return iCol.
+*/
+SQLITE_PRIVATE i16 sqlite3TableColumnToStorage(Table *pTab, i16 iCol){
+ int i;
+ i16 n;
+ assert( iCol<pTab->nCol );
+ if( (pTab->tabFlags & TF_HasVirtual)==0 || iCol<0 ) return iCol;
+ for(i=0, n=0; i<iCol; i++){
+ if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) n++;
+ }
+ if( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL ){
+ /* iCol is a virtual column itself */
+ return pTab->nNVCol + i - n;
+ }else{
+ /* iCol is a normal or stored column */
+ return n;
+ }
+}
+#endif
+
+/*
+** Insert a single OP_JournalMode query opcode in order to force the
+** prepared statement to return false for sqlite3_stmt_readonly(). This
+** is used by CREATE TABLE IF NOT EXISTS and similar if the table already
+** exists, so that the prepared statement for CREATE TABLE IF NOT EXISTS
+** will return false for sqlite3_stmt_readonly() even if that statement
+** is a read-only no-op.
+*/
+static void sqlite3ForceNotReadOnly(Parse *pParse){
+ int iReg = ++pParse->nMem;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ if( v ){
+ sqlite3VdbeAddOp3(v, OP_JournalMode, 0, iReg, PAGER_JOURNALMODE_QUERY);
+ sqlite3VdbeUsesBtree(v, 0);
+ }
+}
+
/*
** Begin constructing a new table representation in memory. This is
** the first of several action routines that get called in response
@@ -106808,7 +121792,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
Token *pName; /* Unqualified name of the table to create */
if( db->init.busy && db->init.newTnum==1 ){
- /* Special case: Parsing the sqlite_master or sqlite_temp_master schema */
+ /* Special case: Parsing the sqlite_schema or sqlite_temp_schema schema */
iDb = db->init.iDb;
zName = sqlite3DbStrDup(db, SCHEMA_TABLE(iDb));
pName = pName1;
@@ -106817,7 +121801,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
if( iDb<0 ) return;
if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){
- /* If creating a temp table, the name may not be qualified. Unless
+ /* If creating a temp table, the name may not be qualified. Unless
** the database name is "temp" anyway. */
sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
return;
@@ -106830,7 +121814,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
}
pParse->sNameToken = *pName;
if( zName==0 ) return;
- if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ if( sqlite3CheckObjectName(pParse, zName, isView?"view":"table", zName) ){
goto begin_table_error;
}
if( db->init.iDb==1 ) isTemp = 1;
@@ -106870,10 +121854,12 @@ SQLITE_PRIVATE void sqlite3StartTable(
pTable = sqlite3FindTable(db, zName, zDb);
if( pTable ){
if( !noErr ){
- sqlite3ErrorMsg(pParse, "table %T already exists", pName);
+ sqlite3ErrorMsg(pParse, "%s %T already exists",
+ (IsView(pTable)? "view" : "table"), pName);
}else{
assert( !db->init.busy || CORRUPT_DB );
sqlite3CodeVerifySchema(pParse, iDb);
+ sqlite3ForceNotReadOnly(pParse);
}
goto begin_table_error;
}
@@ -106902,22 +121888,11 @@ SQLITE_PRIVATE void sqlite3StartTable(
assert( pParse->pNewTable==0 );
pParse->pNewTable = pTable;
- /* If this is the magic sqlite_sequence table used by autoincrement,
- ** then record a pointer to this table in the main database structure
- ** so that INSERT can find the table easily.
- */
-#ifndef SQLITE_OMIT_AUTOINCREMENT
- if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){
- assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- pTable->pSchema->pSeqTab = pTable;
- }
-#endif
-
/* Begin generating the code that will insert the table record into
- ** the SQLITE_MASTER table. Note in particular that we must go ahead
+ ** the schema table. Note in particular that we must go ahead
** and allocate the record number for the table entry now. Before any
** PRIMARY KEY or UNIQUE keywords are parsed. Those keywords will cause
- ** indices to be created and the table record must come before the
+ ** indices to be created and the table record must come before the
** indices. Hence, the record number for the table must be allocated
** now.
*/
@@ -106935,7 +121910,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
}
#endif
- /* If the file format and encoding in the database have not been set,
+ /* If the file format and encoding in the database have not been set,
** set them now.
*/
reg1 = pParse->regRowid = ++pParse->nMem;
@@ -106950,7 +121925,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, ENC(db));
sqlite3VdbeJumpHere(v, addr1);
- /* This just creates a place-holder record in the sqlite_master table.
+ /* This just creates a place-holder record in the sqlite_schema table.
** The record created does not contain anything yet. It will be replaced
** by the real entry in code generated at sqlite3EndTable().
**
@@ -106965,10 +121940,11 @@ SQLITE_PRIVATE void sqlite3StartTable(
}else
#endif
{
- pParse->addrCrTab =
+ assert( !pParse->bReturning );
+ pParse->u1.addrCrTab =
sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY);
}
- sqlite3OpenMasterTable(pParse, iDb);
+ sqlite3OpenSchemaTable(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC);
sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
@@ -106981,6 +121957,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
/* If an error occurs, we jump here */
begin_table_error:
+ pParse->checkSchema = 1;
sqlite3DbFree(db, zName);
return;
}
@@ -106990,14 +121967,84 @@ begin_table_error:
*/
#if SQLITE_ENABLE_HIDDEN_COLUMNS
SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
- if( sqlite3_strnicmp(pCol->zName, "__hidden__", 10)==0 ){
+ if( sqlite3_strnicmp(pCol->zCnName, "__hidden__", 10)==0 ){
pCol->colFlags |= COLFLAG_HIDDEN;
+ if( pTab ) pTab->tabFlags |= TF_HasHidden;
}else if( pTab && pCol!=pTab->aCol && (pCol[-1].colFlags & COLFLAG_HIDDEN) ){
pTab->tabFlags |= TF_OOOHidden;
}
}
#endif
+/*
+** Clean up the data structures associated with the RETURNING clause.
+*/
+static void sqlite3DeleteReturning(sqlite3 *db, void *pArg){
+ Returning *pRet = (Returning*)pArg;
+ Hash *pHash;
+ pHash = &(db->aDb[1].pSchema->trigHash);
+ sqlite3HashInsert(pHash, pRet->zName, 0);
+ sqlite3ExprListDelete(db, pRet->pReturnEL);
+ sqlite3DbFree(db, pRet);
+}
+
+/*
+** Add the RETURNING clause to the parse currently underway.
+**
+** This routine creates a special TEMP trigger that will fire for each row
+** of the DML statement. That TEMP trigger contains a single SELECT
+** statement with a result set that is the argument of the RETURNING clause.
+** The trigger has the Trigger.bReturning flag and an opcode of
+** TK_RETURNING instead of TK_SELECT, so that the trigger code generator
+** knows to handle it specially. The TEMP trigger is automatically
+** removed at the end of the parse.
+**
+** When this routine is called, we do not yet know if the RETURNING clause
+** is attached to a DELETE, INSERT, or UPDATE, so construct it as a
+** RETURNING trigger instead. It will then be converted into the appropriate
+** type on the first call to sqlite3TriggersExist().
+*/
+SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){
+ Returning *pRet;
+ Hash *pHash;
+ sqlite3 *db = pParse->db;
+ if( pParse->pNewTrigger ){
+ sqlite3ErrorMsg(pParse, "cannot use RETURNING in a trigger");
+ }else{
+ assert( pParse->bReturning==0 || pParse->ifNotExists );
+ }
+ pParse->bReturning = 1;
+ pRet = sqlite3DbMallocZero(db, sizeof(*pRet));
+ if( pRet==0 ){
+ sqlite3ExprListDelete(db, pList);
+ return;
+ }
+ pParse->u1.pReturning = pRet;
+ pRet->pParse = pParse;
+ pRet->pReturnEL = pList;
+ sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet);
+ testcase( pParse->earlyCleanup );
+ if( db->mallocFailed ) return;
+ sqlite3_snprintf(sizeof(pRet->zName), pRet->zName,
+ "sqlite_returning_%p", pParse);
+ pRet->retTrig.zName = pRet->zName;
+ pRet->retTrig.op = TK_RETURNING;
+ pRet->retTrig.tr_tm = TRIGGER_AFTER;
+ pRet->retTrig.bReturning = 1;
+ pRet->retTrig.pSchema = db->aDb[1].pSchema;
+ pRet->retTrig.pTabSchema = db->aDb[1].pSchema;
+ pRet->retTrig.step_list = &pRet->retTStep;
+ pRet->retTStep.op = TK_RETURNING;
+ pRet->retTStep.pTrig = &pRet->retTrig;
+ pRet->retTStep.pExprList = pList;
+ pHash = &(db->aDb[1].pSchema->trigHash);
+ assert( sqlite3HashFind(pHash, pRet->zName)==0
+ || pParse->nErr || pParse->ifNotExists );
+ if( sqlite3HashInsert(pHash, pRet->zName, &pRet->retTrig)
+ ==&pRet->retTrig ){
+ sqlite3OomFault(db);
+ }
+}
/*
** Add a new column to the table currently being constructed.
@@ -107007,64 +122054,110 @@ SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
** first to get things going. Then this routine is called for each
** column.
*/
-SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
+SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){
Table *p;
int i;
char *z;
char *zType;
Column *pCol;
sqlite3 *db = pParse->db;
+ u8 hName;
+ Column *aNew;
+ u8 eType = COLTYPE_CUSTOM;
+ u8 szEst = 1;
+ char affinity = SQLITE_AFF_BLOB;
+
if( (p = pParse->pNewTable)==0 ) return;
if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
return;
}
- z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
+ if( !IN_RENAME_OBJECT ) sqlite3DequoteToken(&sName);
+
+ /* Because keywords GENERATE ALWAYS can be converted into identifiers
+ ** by the parser, we can sometimes end up with a typename that ends
+ ** with "generated always". Check for this case and omit the surplus
+ ** text. */
+ if( sType.n>=16
+ && sqlite3_strnicmp(sType.z+(sType.n-6),"always",6)==0
+ ){
+ sType.n -= 6;
+ while( ALWAYS(sType.n>0) && sqlite3Isspace(sType.z[sType.n-1]) ) sType.n--;
+ if( sType.n>=9
+ && sqlite3_strnicmp(sType.z+(sType.n-9),"generated",9)==0
+ ){
+ sType.n -= 9;
+ while( sType.n>0 && sqlite3Isspace(sType.z[sType.n-1]) ) sType.n--;
+ }
+ }
+
+ /* Check for standard typenames. For standard typenames we will
+ ** set the Column.eType field rather than storing the typename after
+ ** the column name, in order to save space. */
+ if( sType.n>=3 ){
+ sqlite3DequoteToken(&sType);
+ for(i=0; i<SQLITE_N_STDTYPE; i++){
+ if( sType.n==sqlite3StdTypeLen[i]
+ && sqlite3_strnicmp(sType.z, sqlite3StdType[i], sType.n)==0
+ ){
+ sType.n = 0;
+ eType = i+1;
+ affinity = sqlite3StdTypeAffinity[i];
+ if( affinity<=SQLITE_AFF_TEXT ) szEst = 5;
+ break;
+ }
+ }
+ }
+
+ z = sqlite3DbMallocRaw(db, (i64)sName.n + 1 + (i64)sType.n + (sType.n>0) );
if( z==0 ) return;
- if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, pName);
- memcpy(z, pName->z, pName->n);
- z[pName->n] = 0;
+ if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, &sName);
+ memcpy(z, sName.z, sName.n);
+ z[sName.n] = 0;
sqlite3Dequote(z);
+ hName = sqlite3StrIHash(z);
for(i=0; i<p->nCol; i++){
- if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){
+ if( p->aCol[i].hName==hName && sqlite3StrICmp(z, p->aCol[i].zCnName)==0 ){
sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
sqlite3DbFree(db, z);
return;
}
}
- if( (p->nCol & 0x7)==0 ){
- Column *aNew;
- aNew = sqlite3DbRealloc(db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0]));
- if( aNew==0 ){
- sqlite3DbFree(db, z);
- return;
- }
- p->aCol = aNew;
+ aNew = sqlite3DbRealloc(db,p->aCol,((i64)p->nCol+1)*sizeof(p->aCol[0]));
+ if( aNew==0 ){
+ sqlite3DbFree(db, z);
+ return;
}
+ p->aCol = aNew;
pCol = &p->aCol[p->nCol];
memset(pCol, 0, sizeof(p->aCol[0]));
- pCol->zName = z;
+ pCol->zCnName = z;
+ pCol->hName = hName;
sqlite3ColumnPropertiesFromName(p, pCol);
-
- if( pType->n==0 ){
+
+ if( sType.n==0 ){
/* If there is no type specified, columns have the default affinity
** 'BLOB' with a default size of 4 bytes. */
- pCol->affinity = SQLITE_AFF_BLOB;
- pCol->szEst = 1;
+ pCol->affinity = affinity;
+ pCol->eCType = eType;
+ pCol->szEst = szEst;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
- if( 4>=sqlite3GlobalConfig.szSorterRef ){
- pCol->colFlags |= COLFLAG_SORTERREF;
+ if( affinity==SQLITE_AFF_BLOB ){
+ if( 4>=sqlite3GlobalConfig.szSorterRef ){
+ pCol->colFlags |= COLFLAG_SORTERREF;
+ }
}
#endif
}else{
zType = z + sqlite3Strlen30(z) + 1;
- memcpy(zType, pType->z, pType->n);
- zType[pType->n] = 0;
+ memcpy(zType, sType.z, sType.n);
+ zType[sType.n] = 0;
sqlite3Dequote(zType);
pCol->affinity = sqlite3AffinityType(zType, pCol);
pCol->colFlags |= COLFLAG_HASTYPE;
}
p->nCol++;
+ p->nNVCol++;
pParse->constraintName.n = 0;
}
@@ -107100,11 +122193,11 @@ SQLITE_PRIVATE void sqlite3AddNotNull(Parse *pParse, int onError){
** Scan the column type name zType (length nType) and return the
** associated affinity type.
**
-** This routine does a case-independent search of zType for the
+** This routine does a case-independent search of zType for the
** substrings in the following table. If one of the substrings is
** found, the corresponding affinity is returned. If zType contains
-** more than one of the substrings, entries toward the top of
-** the table take priority. For example, if zType is 'BLOBINT',
+** more than one of the substrings, entries toward the top of
+** the table take priority. For example, if zType is 'BLOBINT',
** SQLITE_AFF_INTEGER is returned.
**
** Substring | Affinity
@@ -107128,7 +122221,8 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, Column *pCol){
assert( zIn!=0 );
while( zIn[0] ){
- h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff];
+ u8 x = *(u8*)zIn;
+ h = (h<<8) + sqlite3UpperToLower[x];
zIn++;
if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */
aff = SQLITE_AFF_TEXT;
@@ -107202,30 +122296,37 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue(
Parse *pParse, /* Parsing context */
Expr *pExpr, /* The parsed expression of the default value */
const char *zStart, /* Start of the default value text */
- const char *zEnd /* First character past end of defaut value text */
+ const char *zEnd /* First character past end of default value text */
){
Table *p;
Column *pCol;
sqlite3 *db = pParse->db;
p = pParse->pNewTable;
if( p!=0 ){
+ int isInit = db->init.busy && db->init.iDb!=1;
pCol = &(p->aCol[p->nCol-1]);
- if( !sqlite3ExprIsConstantOrFunction(pExpr, db->init.busy) ){
+ if( !sqlite3ExprIsConstantOrFunction(pExpr, isInit) ){
sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant",
- pCol->zName);
+ pCol->zCnName);
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ }else if( pCol->colFlags & COLFLAG_GENERATED ){
+ testcase( pCol->colFlags & COLFLAG_VIRTUAL );
+ testcase( pCol->colFlags & COLFLAG_STORED );
+ sqlite3ErrorMsg(pParse, "cannot use DEFAULT on a generated column");
+#endif
}else{
/* A copy of pExpr is used instead of the original, as pExpr contains
** tokens that point to volatile memory.
*/
- Expr x;
- sqlite3ExprDelete(db, pCol->pDflt);
+ Expr x, *pDfltExpr;
memset(&x, 0, sizeof(x));
x.op = TK_SPAN;
x.u.zToken = sqlite3DbSpanDup(db, zStart, zEnd);
x.pLeft = pExpr;
x.flags = EP_Skip;
- pCol->pDflt = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE);
+ pDfltExpr = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE);
sqlite3DbFree(db, x.u.zToken);
+ sqlite3ColumnSetExpr(pParse, p, pCol, pDfltExpr);
}
}
if( IN_RENAME_OBJECT ){
@@ -107236,7 +122337,7 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue(
/*
** Backwards Compatibility Hack:
-**
+**
** Historical versions of SQLite accepted strings as column names in
** indexes and PRIMARY KEY constraints and in UNIQUE constraints. Example:
**
@@ -107247,7 +122348,7 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue(
** accept it. This routine does the necessary conversion. It converts
** the expression given in its argument from a TK_STRING into a TK_ID
** if the expression is just a TK_STRING with an optional COLLATE clause.
-** If the epxression is anything other than TK_STRING, the expression is
+** If the expression is anything other than TK_STRING, the expression is
** unchanged.
*/
static void sqlite3StringToId(Expr *p){
@@ -107259,7 +122360,22 @@ static void sqlite3StringToId(Expr *p){
}
/*
-** Designate the PRIMARY KEY for the table. pList is a list of names
+** Tag the given column as being part of the PRIMARY KEY
+*/
+static void makeColumnPartOfPrimaryKey(Parse *pParse, Column *pCol){
+ pCol->colFlags |= COLFLAG_PRIMKEY;
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ if( pCol->colFlags & COLFLAG_GENERATED ){
+ testcase( pCol->colFlags & COLFLAG_VIRTUAL );
+ testcase( pCol->colFlags & COLFLAG_STORED );
+ sqlite3ErrorMsg(pParse,
+ "generated columns cannot be part of the PRIMARY KEY");
+ }
+#endif
+}
+
+/*
+** Designate the PRIMARY KEY for the table. pList is a list of names
** of columns that form the primary key. If pList is NULL, then the
** most recently added column of the table is the primary key.
**
@@ -107289,7 +122405,7 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
int nTerm;
if( pTab==0 ) goto primary_key_exit;
if( pTab->tabFlags & TF_HasPrimaryKey ){
- sqlite3ErrorMsg(pParse,
+ sqlite3ErrorMsg(pParse,
"table \"%s\" has more than one primary key", pTab->zName);
goto primary_key_exit;
}
@@ -107297,7 +122413,7 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
if( pList==0 ){
iCol = pTab->nCol - 1;
pCol = &pTab->aCol[iCol];
- pCol->colFlags |= COLFLAG_PRIMKEY;
+ makeColumnPartOfPrimaryKey(pParse, pCol);
nTerm = 1;
}else{
nTerm = pList->nExpr;
@@ -107306,11 +122422,13 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
assert( pCExpr!=0 );
sqlite3StringToId(pCExpr);
if( pCExpr->op==TK_ID ){
- const char *zCName = pCExpr->u.zToken;
+ const char *zCName;
+ assert( !ExprHasProperty(pCExpr, EP_IntValue) );
+ zCName = pCExpr->u.zToken;
for(iCol=0; iCol<pTab->nCol; iCol++){
- if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){
+ if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zCnName)==0 ){
pCol = &pTab->aCol[iCol];
- pCol->colFlags |= COLFLAG_PRIMKEY;
+ makeColumnPartOfPrimaryKey(pParse, pCol);
break;
}
}
@@ -107319,17 +122437,19 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
}
if( nTerm==1
&& pCol
- && sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0
+ && pCol->eCType==COLTYPE_INTEGER
&& sortOrder!=SQLITE_SO_DESC
){
if( IN_RENAME_OBJECT && pList ){
- sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pList->a[0].pExpr);
+ Expr *pCExpr = sqlite3ExprSkipCollate(pList->a[0].pExpr);
+ sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pCExpr);
}
pTab->iPKey = iCol;
pTab->keyConf = (u8)onError;
assert( autoInc==0 || autoInc==1 );
pTab->tabFlags |= autoInc*TF_Autoincrement;
- if( pList ) pParse->iPkSortOrder = pList->a[0].sortOrder;
+ if( pList ) pParse->iPkSortOrder = pList->a[0].fg.sortFlags;
+ (void)sqlite3HasExplicitNulls(pParse, pList);
}else if( autoInc ){
#ifndef SQLITE_OMIT_AUTOINCREMENT
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
@@ -107350,8 +122470,10 @@ primary_key_exit:
** Add a new CHECK constraint to the table currently under construction.
*/
SQLITE_PRIVATE void sqlite3AddCheckConstraint(
- Parse *pParse, /* Parsing context */
- Expr *pCheckExpr /* The check expression */
+ Parse *pParse, /* Parsing context */
+ Expr *pCheckExpr, /* The check expression */
+ const char *zStart, /* Opening "(" */
+ const char *zEnd /* Closing ")" */
){
#ifndef SQLITE_OMIT_CHECK
Table *pTab = pParse->pNewTable;
@@ -107362,6 +122484,13 @@ SQLITE_PRIVATE void sqlite3AddCheckConstraint(
pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr);
if( pParse->constraintName.n ){
sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->constraintName, 1);
+ }else{
+ Token t;
+ for(zStart++; sqlite3Isspace(zStart[0]); zStart++){}
+ while( sqlite3Isspace(zEnd[-1]) ){ zEnd--; }
+ t.z = zStart;
+ t.n = (int)(zEnd - t.z);
+ sqlite3ExprListSetName(pParse, pTab->pCheck, &t, 1);
}
}else
#endif
@@ -107380,7 +122509,7 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){
char *zColl; /* Dequoted name of collation sequence */
sqlite3 *db;
- if( (p = pParse->pNewTable)==0 ) return;
+ if( (p = pParse->pNewTable)==0 || IN_RENAME_OBJECT ) return;
i = p->nCol-1;
db = pParse->db;
zColl = sqlite3NameFromToken(db, pToken);
@@ -107388,9 +122517,8 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){
if( sqlite3LocateCollSeq(pParse, zColl) ){
Index *pIdx;
- sqlite3DbFree(db, p->aCol[i].zColl);
- p->aCol[i].zColl = zColl;
-
+ sqlite3ColumnSetColl(db, &p->aCol[i], zColl);
+
/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
** then an index may have been created on this column before the
** collation type was added. Correct this if it is the case.
@@ -107398,49 +122526,73 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){
for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){
assert( pIdx->nKeyCol==1 );
if( pIdx->aiColumn[0]==i ){
- pIdx->azColl[0] = p->aCol[i].zColl;
+ pIdx->azColl[0] = sqlite3ColumnColl(&p->aCol[i]);
}
}
- }else{
- sqlite3DbFree(db, zColl);
}
+ sqlite3DbFree(db, zColl);
}
-/*
-** This function returns the collation sequence for database native text
-** encoding identified by the string zName, length nName.
-**
-** If the requested collation sequence is not available, or not available
-** in the database native encoding, the collation factory is invoked to
-** request it. If the collation factory does not supply such a sequence,
-** and the sequence is available in another text encoding, then that is
-** returned instead.
-**
-** If no versions of the requested collations sequence are available, or
-** another error occurs, NULL is returned and an error message written into
-** pParse.
-**
-** This routine is a wrapper around sqlite3FindCollSeq(). This routine
-** invokes the collation factory if the named collation cannot be found
-** and generates an error message.
-**
-** See also: sqlite3FindCollSeq(), sqlite3GetCollSeq()
+/* Change the most recently parsed column to be a GENERATED ALWAYS AS
+** column.
*/
-SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
- sqlite3 *db = pParse->db;
- u8 enc = ENC(db);
- u8 initbusy = db->init.busy;
- CollSeq *pColl;
-
- pColl = sqlite3FindCollSeq(db, enc, zName, initbusy);
- if( !initbusy && (!pColl || !pColl->xCmp) ){
- pColl = sqlite3GetCollSeq(pParse, enc, pColl, zName);
+SQLITE_PRIVATE void sqlite3AddGenerated(Parse *pParse, Expr *pExpr, Token *pType){
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ u8 eType = COLFLAG_VIRTUAL;
+ Table *pTab = pParse->pNewTable;
+ Column *pCol;
+ if( pTab==0 ){
+ /* generated column in an CREATE TABLE IF NOT EXISTS that already exists */
+ goto generated_done;
+ }
+ pCol = &(pTab->aCol[pTab->nCol-1]);
+ if( IN_DECLARE_VTAB ){
+ sqlite3ErrorMsg(pParse, "virtual tables cannot use computed columns");
+ goto generated_done;
+ }
+ if( pCol->iDflt>0 ) goto generated_error;
+ if( pType ){
+ if( pType->n==7 && sqlite3StrNICmp("virtual",pType->z,7)==0 ){
+ /* no-op */
+ }else if( pType->n==6 && sqlite3StrNICmp("stored",pType->z,6)==0 ){
+ eType = COLFLAG_STORED;
+ }else{
+ goto generated_error;
+ }
+ }
+ if( eType==COLFLAG_VIRTUAL ) pTab->nNVCol--;
+ pCol->colFlags |= eType;
+ assert( TF_HasVirtual==COLFLAG_VIRTUAL );
+ assert( TF_HasStored==COLFLAG_STORED );
+ pTab->tabFlags |= eType;
+ if( pCol->colFlags & COLFLAG_PRIMKEY ){
+ makeColumnPartOfPrimaryKey(pParse, pCol); /* For the error message */
+ }
+ if( ALWAYS(pExpr) && pExpr->op==TK_ID ){
+ /* The value of a generated column needs to be a real expression, not
+ ** just a reference to another column, in order for covering index
+ ** optimizations to work correctly. So if the value is not an expression,
+ ** turn it into one by adding a unary "+" operator. */
+ pExpr = sqlite3PExpr(pParse, TK_UPLUS, pExpr, 0);
}
+ if( pExpr && pExpr->op!=TK_RAISE ) pExpr->affExpr = pCol->affinity;
+ sqlite3ColumnSetExpr(pParse, pTab, pCol, pExpr);
+ pExpr = 0;
+ goto generated_done;
- return pColl;
+generated_error:
+ sqlite3ErrorMsg(pParse, "error in generated column \"%s\"",
+ pCol->zCnName);
+generated_done:
+ sqlite3ExprDelete(pParse->db, pExpr);
+#else
+ /* Throw and error for the GENERATED ALWAYS AS clause if the
+ ** SQLITE_OMIT_GENERATED_COLUMNS compile-time option is used. */
+ sqlite3ErrorMsg(pParse, "generated columns not supported");
+ sqlite3ExprDelete(pParse->db, pExpr);
+#endif
}
-
/*
** Generate code that will increment the schema cookie.
**
@@ -107464,7 +122616,7 @@ SQLITE_PRIVATE void sqlite3ChangeCookie(Parse *pParse, int iDb){
sqlite3 *db = pParse->db;
Vdbe *v = pParse->pVdbe;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION,
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION,
(int)(1+(unsigned)db->aDb[iDb].pSchema->schema_cookie));
}
@@ -107485,14 +122637,14 @@ static int identLength(const char *z){
}
/*
-** The first parameter is a pointer to an output buffer. The second
+** The first parameter is a pointer to an output buffer. The second
** parameter is a pointer to an integer that contains the offset at
** which to write into the output buffer. This function copies the
** nul-terminated string pointed to by the third parameter, zSignedIdent,
** to the specified offset in the buffer and updates *pIdx to refer
** to the first byte after the last byte written before returning.
-**
-** If the string zSignedIdent consists entirely of alpha-numeric
+**
+** If the string zSignedIdent consists entirely of alphanumeric
** characters, does not begin with a digit and is not an SQL keyword,
** then it is copied to the output buffer exactly as it is. Otherwise,
** it is quoted using double-quotes.
@@ -107532,10 +122684,10 @@ static char *createTableStmt(sqlite3 *db, Table *p){
Column *pCol;
n = 0;
for(pCol = p->aCol, i=0; i<p->nCol; i++, pCol++){
- n += identLength(pCol->zName) + 5;
+ n += identLength(pCol->zCnName) + 5;
}
n += identLength(p->zName);
- if( n<50 ){
+ if( n<50 ){
zSep = "";
zSep2 = ",";
zEnd = ")";
@@ -107560,7 +122712,8 @@ static char *createTableStmt(sqlite3 *db, Table *p){
/* SQLITE_AFF_TEXT */ " TEXT",
/* SQLITE_AFF_NUMERIC */ " NUM",
/* SQLITE_AFF_INTEGER */ " INT",
- /* SQLITE_AFF_REAL */ " REAL"
+ /* SQLITE_AFF_REAL */ " REAL",
+ /* SQLITE_AFF_FLEXNUM */ " NUM",
};
int len;
const char *zType;
@@ -107568,7 +122721,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){
sqlite3_snprintf(n-k, &zStmt[k], zSep);
k += sqlite3Strlen30(&zStmt[k]);
zSep = zSep2;
- identPut(zStmt, &k, pCol->zName);
+ identPut(zStmt, &k, pCol->zCnName);
assert( pCol->affinity-SQLITE_AFF_BLOB >= 0 );
assert( pCol->affinity-SQLITE_AFF_BLOB < ArraySize(azType) );
testcase( pCol->affinity==SQLITE_AFF_BLOB );
@@ -107576,10 +122729,12 @@ static char *createTableStmt(sqlite3 *db, Table *p){
testcase( pCol->affinity==SQLITE_AFF_NUMERIC );
testcase( pCol->affinity==SQLITE_AFF_INTEGER );
testcase( pCol->affinity==SQLITE_AFF_REAL );
-
+ testcase( pCol->affinity==SQLITE_AFF_FLEXNUM );
+
zType = azType[pCol->affinity - SQLITE_AFF_BLOB];
len = sqlite3Strlen30(zType);
- assert( pCol->affinity==SQLITE_AFF_BLOB
+ assert( pCol->affinity==SQLITE_AFF_BLOB
+ || pCol->affinity==SQLITE_AFF_FLEXNUM
|| pCol->affinity==sqlite3AffinityType(zType, 0) );
memcpy(&zStmt[k], zType, len);
k += len;
@@ -107598,12 +122753,15 @@ static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){
int nByte;
if( pIdx->nColumn>=N ) return SQLITE_OK;
assert( pIdx->isResized==0 );
- nByte = (sizeof(char*) + sizeof(i16) + 1)*N;
+ nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*N;
zExtra = sqlite3DbMallocZero(db, nByte);
if( zExtra==0 ) return SQLITE_NOMEM_BKPT;
memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn);
pIdx->azColl = (const char**)zExtra;
zExtra += sizeof(char*)*N;
+ memcpy(zExtra, pIdx->aiRowLogEst, sizeof(LogEst)*(pIdx->nKeyCol+1));
+ pIdx->aiRowLogEst = (LogEst*)zExtra;
+ zExtra += sizeof(LogEst)*N;
memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn);
pIdx->aiColumn = (i16*)zExtra;
zExtra += sizeof(i16)*N;
@@ -107638,41 +122796,91 @@ static void estimateIndexWidth(Index *pIdx){
for(i=0; i<pIdx->nColumn; i++){
i16 x = pIdx->aiColumn[i];
assert( x<pIdx->pTable->nCol );
- wIndex += x<0 ? 1 : aCol[pIdx->aiColumn[i]].szEst;
+ wIndex += x<0 ? 1 : aCol[x].szEst;
}
pIdx->szIdxRow = sqlite3LogEst(wIndex*4);
}
-/* Return true if value x is found any of the first nCol entries of aiCol[]
+/* Return true if column number x is any of the first nCol entries of aiCol[].
+** This is used to determine if the column number x appears in any of the
+** first nCol entries of an index.
*/
static int hasColumn(const i16 *aiCol, int nCol, int x){
- while( nCol-- > 0 ) if( x==*(aiCol++) ) return 1;
+ while( nCol-- > 0 ){
+ if( x==*(aiCol++) ){
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** Return true if any of the first nKey entries of index pIdx exactly
+** match the iCol-th entry of pPk. pPk is always a WITHOUT ROWID
+** PRIMARY KEY index. pIdx is an index on the same table. pIdx may
+** or may not be the same index as pPk.
+**
+** The first nKey entries of pIdx are guaranteed to be ordinary columns,
+** not a rowid or expression.
+**
+** This routine differs from hasColumn() in that both the column and the
+** collating sequence must match for this routine, but for hasColumn() only
+** the column name must match.
+*/
+static int isDupColumn(Index *pIdx, int nKey, Index *pPk, int iCol){
+ int i, j;
+ assert( nKey<=pIdx->nColumn );
+ assert( iCol<MAX(pPk->nColumn,pPk->nKeyCol) );
+ assert( pPk->idxType==SQLITE_IDXTYPE_PRIMARYKEY );
+ assert( pPk->pTable->tabFlags & TF_WithoutRowid );
+ assert( pPk->pTable==pIdx->pTable );
+ testcase( pPk==pIdx );
+ j = pPk->aiColumn[iCol];
+ assert( j!=XN_ROWID && j!=XN_EXPR );
+ for(i=0; i<nKey; i++){
+ assert( pIdx->aiColumn[i]>=0 || j>=0 );
+ if( pIdx->aiColumn[i]==j
+ && sqlite3StrICmp(pIdx->azColl[i], pPk->azColl[iCol])==0
+ ){
+ return 1;
+ }
+ }
return 0;
}
/* Recompute the colNotIdxed field of the Index.
**
** colNotIdxed is a bitmask that has a 0 bit representing each indexed
-** columns that are within the first 63 columns of the table. The
+** columns that are within the first 63 columns of the table and a 1 for
+** all other bits (all columns that are not in the index). The
** high-order bit of colNotIdxed is always 1. All unindexed columns
** of the table have a 1.
**
+** 2019-10-24: For the purpose of this computation, virtual columns are
+** not considered to be covered by the index, even if they are in the
+** index, because we do not trust the logic in whereIndexExprTrans() to be
+** able to find all instances of a reference to the indexed table column
+** and convert them into references to the index. Hence we always want
+** the actual table at hand in order to recompute the virtual column, if
+** necessary.
+**
** The colNotIdxed mask is AND-ed with the SrcList.a[].colUsed mask
** to determine if the index is covering index.
*/
static void recomputeColumnsNotIndexed(Index *pIdx){
Bitmask m = 0;
int j;
+ Table *pTab = pIdx->pTable;
for(j=pIdx->nColumn-1; j>=0; j--){
int x = pIdx->aiColumn[j];
- if( x>=0 ){
+ if( x>=0 && (pTab->aCol[x].colFlags & COLFLAG_VIRTUAL)==0 ){
testcase( x==BMS-1 );
testcase( x==BMS-2 );
if( x<BMS-1 ) m |= MASKBIT(x);
}
}
pIdx->colNotIdxed = ~m;
- assert( (pIdx->colNotIdxed>>63)==1 );
+ assert( (pIdx->colNotIdxed>>63)==1 ); /* See note-20221022-a */
}
/*
@@ -107683,11 +122891,11 @@ static void recomputeColumnsNotIndexed(Index *pIdx){
** Changes include:
**
** (1) Set all columns of the PRIMARY KEY schema object to be NOT NULL.
-** (2) Convert P3 parameter of the OP_CreateBtree from BTREE_INTKEY
+** (2) Convert P3 parameter of the OP_CreateBtree from BTREE_INTKEY
** into BTREE_BLOBKEY.
-** (3) Bypass the creation of the sqlite_master table entry
+** (3) Bypass the creation of the sqlite_schema table entry
** for the PRIMARY KEY as the primary key index is now
-** identified by the sqlite_master table entry of the table itself.
+** identified by the sqlite_schema table entry of the table itself.
** (4) Set the Index.tnum of the PRIMARY KEY Index object in the
** schema to the rootpage from the main table.
** (5) Add all table columns to the PRIMARY KEY Index object
@@ -107703,6 +122911,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
Index *pIdx;
Index *pPk;
int nPk;
+ int nExtra;
int i, j;
sqlite3 *db = pParse->db;
Vdbe *v = pParse->pVdbe;
@@ -107711,39 +122920,55 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
*/
if( !db->init.imposterTable ){
for(i=0; i<pTab->nCol; i++){
- if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 ){
+ if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0
+ && (pTab->aCol[i].notNull==OE_None)
+ ){
pTab->aCol[i].notNull = OE_Abort;
}
}
+ pTab->tabFlags |= TF_HasNotNull;
}
/* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY
** into BTREE_BLOBKEY.
*/
- if( pParse->addrCrTab ){
+ assert( !pParse->bReturning );
+ if( pParse->u1.addrCrTab ){
assert( v );
- sqlite3VdbeChangeP3(v, pParse->addrCrTab, BTREE_BLOBKEY);
+ sqlite3VdbeChangeP3(v, pParse->u1.addrCrTab, BTREE_BLOBKEY);
}
/* Locate the PRIMARY KEY index. Or, if this table was originally
- ** an INTEGER PRIMARY KEY table, create a new PRIMARY KEY index.
+ ** an INTEGER PRIMARY KEY table, create a new PRIMARY KEY index.
*/
if( pTab->iPKey>=0 ){
ExprList *pList;
Token ipkToken;
- sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zName);
- pList = sqlite3ExprListAppend(pParse, 0,
+ sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zCnName);
+ pList = sqlite3ExprListAppend(pParse, 0,
sqlite3ExprAlloc(db, TK_ID, &ipkToken, 0));
- if( pList==0 ) return;
- pList->a[0].sortOrder = pParse->iPkSortOrder;
+ if( pList==0 ){
+ pTab->tabFlags &= ~TF_WithoutRowid;
+ return;
+ }
+ if( IN_RENAME_OBJECT ){
+ sqlite3RenameTokenRemap(pParse, pList->a[0].pExpr, &pTab->iPKey);
+ }
+ pList->a[0].fg.sortFlags = pParse->iPkSortOrder;
assert( pParse->pNewTable==pTab );
+ pTab->iPKey = -1;
sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0,
SQLITE_IDXTYPE_PRIMARYKEY);
- if( db->mallocFailed || pParse->nErr ) return;
+ if( pParse->nErr ){
+ pTab->tabFlags &= ~TF_WithoutRowid;
+ return;
+ }
+ assert( db->mallocFailed==0 );
pPk = sqlite3PrimaryKeyIndex(pTab);
- pTab->iPKey = -1;
+ assert( pPk->nKeyCol==1 );
}else{
pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pPk!=0 );
/*
** Remove all redundant columns from the PRIMARY KEY. For example, change
@@ -107751,9 +122976,12 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
** code assumes the PRIMARY KEY contains no repeated columns.
*/
for(i=j=1; i<pPk->nKeyCol; i++){
- if( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) ){
+ if( isDupColumn(pPk, j, pPk, i) ){
pPk->nColumn--;
}else{
+ testcase( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) );
+ pPk->azColl[j] = pPk->azColl[i];
+ pPk->aSortOrder[j] = pPk->aSortOrder[i];
pPk->aiColumn[j++] = pPk->aiColumn[i];
}
}
@@ -107762,15 +122990,15 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
assert( pPk!=0 );
pPk->isCovering = 1;
if( !db->init.imposterTable ) pPk->uniqNotNull = 1;
- nPk = pPk->nKeyCol;
+ nPk = pPk->nColumn = pPk->nKeyCol;
- /* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
+ /* Bypass the creation of the PRIMARY KEY btree and the sqlite_schema
** table entry. This is only required if currently generating VDBE
** code for a CREATE TABLE (not when parsing one as part of reading
** a database schema). */
if( v && pPk->tnum>0 ){
assert( db->init.busy==0 );
- sqlite3VdbeChangeOpcode(v, pPk->tnum, OP_Goto);
+ sqlite3VdbeChangeOpcode(v, (int)pPk->tnum, OP_Goto);
}
/* The root page of the PRIMARY KEY is the table root page */
@@ -107783,7 +123011,10 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
int n;
if( IsPrimaryKeyIndex(pIdx) ) continue;
for(i=n=0; i<nPk; i++){
- if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ) n++;
+ if( !isDupColumn(pIdx, pIdx->nKeyCol, pPk, i) ){
+ testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) );
+ n++;
+ }
}
if( n==0 ){
/* This index is a superset of the primary key */
@@ -107792,9 +123023,14 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
}
if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return;
for(i=0, j=pIdx->nKeyCol; i<nPk; i++){
- if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ){
+ if( !isDupColumn(pIdx, pIdx->nKeyCol, pPk, i) ){
+ testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) );
pIdx->aiColumn[j] = pPk->aiColumn[i];
pIdx->azColl[j] = pPk->azColl[i];
+ if( pPk->aSortOrder[i] ){
+ /* See ticket https://www.sqlite.org/src/info/bba7b69f9849b5bf */
+ pIdx->bAscKeyBug = 1;
+ }
j++;
}
}
@@ -107804,24 +123040,84 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
/* Add all table columns to the PRIMARY KEY index
*/
- if( nPk<pTab->nCol ){
- if( resizeIndexObject(db, pPk, pTab->nCol) ) return;
- for(i=0, j=nPk; i<pTab->nCol; i++){
- if( !hasColumn(pPk->aiColumn, j, i) ){
- assert( j<pPk->nColumn );
- pPk->aiColumn[j] = i;
- pPk->azColl[j] = sqlite3StrBINARY;
- j++;
- }
+ nExtra = 0;
+ for(i=0; i<pTab->nCol; i++){
+ if( !hasColumn(pPk->aiColumn, nPk, i)
+ && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) nExtra++;
+ }
+ if( resizeIndexObject(db, pPk, nPk+nExtra) ) return;
+ for(i=0, j=nPk; i<pTab->nCol; i++){
+ if( !hasColumn(pPk->aiColumn, j, i)
+ && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0
+ ){
+ assert( j<pPk->nColumn );
+ pPk->aiColumn[j] = i;
+ pPk->azColl[j] = sqlite3StrBINARY;
+ j++;
}
- assert( pPk->nColumn==j );
- assert( pTab->nCol==j );
- }else{
- pPk->nColumn = pTab->nCol;
}
+ assert( pPk->nColumn==j );
+ assert( pTab->nNVCol<=j );
recomputeColumnsNotIndexed(pPk);
}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Return true if pTab is a virtual table and zName is a shadow table name
+** for that virtual table.
+*/
+SQLITE_PRIVATE int sqlite3IsShadowTableOf(sqlite3 *db, Table *pTab, const char *zName){
+ int nName; /* Length of zName */
+ Module *pMod; /* Module for the virtual table */
+
+ if( !IsVirtual(pTab) ) return 0;
+ nName = sqlite3Strlen30(pTab->zName);
+ if( sqlite3_strnicmp(zName, pTab->zName, nName)!=0 ) return 0;
+ if( zName[nName]!='_' ) return 0;
+ pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->u.vtab.azArg[0]);
+ if( pMod==0 ) return 0;
+ if( pMod->pModule->iVersion<3 ) return 0;
+ if( pMod->pModule->xShadowName==0 ) return 0;
+ return pMod->pModule->xShadowName(zName+nName+1);
+}
+#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Table pTab is a virtual table. If it the virtual table implementation
+** exists and has an xShadowName method, then loop over all other ordinary
+** tables within the same schema looking for shadow tables of pTab, and mark
+** any shadow tables seen using the TF_Shadow flag.
+*/
+SQLITE_PRIVATE void sqlite3MarkAllShadowTablesOf(sqlite3 *db, Table *pTab){
+ int nName; /* Length of pTab->zName */
+ Module *pMod; /* Module for the virtual table */
+ HashElem *k; /* For looping through the symbol table */
+
+ assert( IsVirtual(pTab) );
+ pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->u.vtab.azArg[0]);
+ if( pMod==0 ) return;
+ if( NEVER(pMod->pModule==0) ) return;
+ if( pMod->pModule->iVersion<3 ) return;
+ if( pMod->pModule->xShadowName==0 ) return;
+ assert( pTab->zName!=0 );
+ nName = sqlite3Strlen30(pTab->zName);
+ for(k=sqliteHashFirst(&pTab->pSchema->tblHash); k; k=sqliteHashNext(k)){
+ Table *pOther = sqliteHashData(k);
+ assert( pOther->zName!=0 );
+ if( !IsOrdinaryTable(pOther) ) continue;
+ if( pOther->tabFlags & TF_Shadow ) continue;
+ if( sqlite3StrNICmp(pOther->zName, pTab->zName, nName)==0
+ && pOther->zName[nName]=='_'
+ && pMod->pModule->xShadowName(pOther->zName+nName+1)
+ ){
+ pOther->tabFlags |= TF_Shadow;
+ }
+ }
+}
+#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
+
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Return true if zName is a shadow table name in the current database
@@ -107830,11 +123126,9 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
** zName is temporarily modified while this routine is running, but is
** restored to its original value prior to this routine returning.
*/
-static int isShadowTableName(sqlite3 *db, char *zName){
+SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName){
char *zTail; /* Pointer to the last "_" in zName */
Table *pTab; /* Table that zName is a shadow of */
- Module *pMod; /* Module for the virtual table */
-
zTail = strrchr(zName, '_');
if( zTail==0 ) return 0;
*zTail = 0;
@@ -107842,16 +123136,38 @@ static int isShadowTableName(sqlite3 *db, char *zName){
*zTail = '_';
if( pTab==0 ) return 0;
if( !IsVirtual(pTab) ) return 0;
- pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]);
- if( pMod==0 ) return 0;
- if( pMod->pModule->iVersion<3 ) return 0;
- if( pMod->pModule->xShadowName==0 ) return 0;
- return pMod->pModule->xShadowName(zTail+1);
+ return sqlite3IsShadowTableOf(db, pTab, zName);
}
-#else
-# define isShadowTableName(x,y) 0
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
+
+#ifdef SQLITE_DEBUG
+/*
+** Mark all nodes of an expression as EP_Immutable, indicating that
+** they should not be changed. Expressions attached to a table or
+** index definition are tagged this way to help ensure that we do
+** not pass them into code generator routines by mistake.
+*/
+static int markImmutableExprStep(Walker *pWalker, Expr *pExpr){
+ (void)pWalker;
+ ExprSetVVAProperty(pExpr, EP_Immutable);
+ return WRC_Continue;
+}
+static void markExprListImmutable(ExprList *pList){
+ if( pList ){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = markImmutableExprStep;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
+ w.xSelectCallback2 = 0;
+ sqlite3WalkExprList(&w, pList);
+ }
+}
+#else
+#define markExprListImmutable(X) /* no-op */
+#endif /* SQLITE_DEBUG */
+
+
/*
** This routine is called to report the final ")" that terminates
** a CREATE TABLE statement.
@@ -107860,15 +123176,15 @@ static int isShadowTableName(sqlite3 *db, char *zName){
** is added to the internal hash tables, assuming no errors have
** occurred.
**
-** An entry for the table is made in the master table on disk, unless
+** An entry for the table is made in the schema table on disk, unless
** this is a temporary table or db->init.busy==1. When db->init.busy==1
-** it means we are reading the sqlite_master table because we just
-** connected to the database or because the sqlite_master table has
+** it means we are reading the sqlite_schema table because we just
+** connected to the database or because the sqlite_schema table has
** recently changed, so the entry for this table already exists in
-** the sqlite_master table. We do not want to create it again.
+** the sqlite_schema table. We do not want to create it again.
**
** If the pSelect argument is not NULL, it means that this routine
-** was called to create a table generated from a
+** was called to create a table generated from a
** "CREATE TABLE ... AS SELECT ..." statement. The column names of
** the new table will match the result set of the SELECT.
*/
@@ -107876,7 +123192,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
Parse *pParse, /* Parse context */
Token *pCons, /* The ',' token after the last column defn. */
Token *pEnd, /* The ')' before options in the CREATE TABLE */
- u8 tabOpts, /* Extra table options. Usually 0. */
+ u32 tabOpts, /* Extra table options. Usually 0. */
Select *pSelect /* Select from a "CREATE ... AS SELECT" */
){
Table *p; /* The new table */
@@ -107887,25 +123203,24 @@ SQLITE_PRIVATE void sqlite3EndTable(
if( pEnd==0 && pSelect==0 ){
return;
}
- assert( !db->mallocFailed );
p = pParse->pNewTable;
if( p==0 ) return;
- if( pSelect==0 && isShadowTableName(db, p->zName) ){
+ if( pSelect==0 && sqlite3ShadowTableName(db, p->zName) ){
p->tabFlags |= TF_Shadow;
}
/* If the db->init.busy is 1 it means we are reading the SQL off the
- ** "sqlite_master" or "sqlite_temp_master" table on the disk.
+ ** "sqlite_schema" or "sqlite_temp_schema" table on the disk.
** So do not write to the disk again. Extract the root page number
** for the table from the db->init.newTnum field. (The page number
** should have been put there by the sqliteOpenCb routine.)
**
- ** If the root page number is 1, that means this is the sqlite_master
+ ** If the root page number is 1, that means this is the sqlite_schema
** table itself. So mark it read-only.
*/
if( db->init.busy ){
- if( pSelect ){
+ if( pSelect || (!IsOrdinaryTable(p) && db->init.newTnum) ){
sqlite3ErrorMsg(pParse, "");
return;
}
@@ -107913,6 +123228,49 @@ SQLITE_PRIVATE void sqlite3EndTable(
if( p->tnum==1 ) p->tabFlags |= TF_Readonly;
}
+ /* Special processing for tables that include the STRICT keyword:
+ **
+ ** * Do not allow custom column datatypes. Every column must have
+ ** a datatype that is one of INT, INTEGER, REAL, TEXT, or BLOB.
+ **
+ ** * If a PRIMARY KEY is defined, other than the INTEGER PRIMARY KEY,
+ ** then all columns of the PRIMARY KEY must have a NOT NULL
+ ** constraint.
+ */
+ if( tabOpts & TF_Strict ){
+ int ii;
+ p->tabFlags |= TF_Strict;
+ for(ii=0; ii<p->nCol; ii++){
+ Column *pCol = &p->aCol[ii];
+ if( pCol->eCType==COLTYPE_CUSTOM ){
+ if( pCol->colFlags & COLFLAG_HASTYPE ){
+ sqlite3ErrorMsg(pParse,
+ "unknown datatype for %s.%s: \"%s\"",
+ p->zName, pCol->zCnName, sqlite3ColumnType(pCol, "")
+ );
+ }else{
+ sqlite3ErrorMsg(pParse, "missing datatype for %s.%s",
+ p->zName, pCol->zCnName);
+ }
+ return;
+ }else if( pCol->eCType==COLTYPE_ANY ){
+ pCol->affinity = SQLITE_AFF_BLOB;
+ }
+ if( (pCol->colFlags & COLFLAG_PRIMKEY)!=0
+ && p->iPKey!=ii
+ && pCol->notNull == OE_None
+ ){
+ pCol->notNull = OE_Abort;
+ p->tabFlags |= TF_HasNotNull;
+ }
+ }
+ }
+
+ assert( (p->tabFlags & TF_HasPrimaryKey)==0
+ || p->iPKey>=0 || sqlite3PrimaryKeyIndex(p)!=0 );
+ assert( (p->tabFlags & TF_HasPrimaryKey)!=0
+ || (p->iPKey<0 && sqlite3PrimaryKeyIndex(p)==0) );
+
/* Special processing for WITHOUT ROWID Tables */
if( tabOpts & TF_WithoutRowid ){
if( (p->tabFlags & TF_Autoincrement) ){
@@ -107922,12 +123280,11 @@ SQLITE_PRIVATE void sqlite3EndTable(
}
if( (p->tabFlags & TF_HasPrimaryKey)==0 ){
sqlite3ErrorMsg(pParse, "PRIMARY KEY missing on table %s", p->zName);
- }else{
- p->tabFlags |= TF_WithoutRowid | TF_NoVisibleRowid;
- convertToWithoutRowidTable(pParse, p);
+ return;
}
+ p->tabFlags |= TF_WithoutRowid | TF_NoVisibleRowid;
+ convertToWithoutRowidTable(pParse, p);
}
-
iDb = sqlite3SchemaToIndex(db, p->pSchema);
#ifndef SQLITE_OMIT_CHECK
@@ -107935,8 +123292,47 @@ SQLITE_PRIVATE void sqlite3EndTable(
*/
if( p->pCheck ){
sqlite3ResolveSelfReference(pParse, p, NC_IsCheck, 0, p->pCheck);
+ if( pParse->nErr ){
+ /* If errors are seen, delete the CHECK constraints now, else they might
+ ** actually be used if PRAGMA writable_schema=ON is set. */
+ sqlite3ExprListDelete(db, p->pCheck);
+ p->pCheck = 0;
+ }else{
+ markExprListImmutable(p->pCheck);
+ }
}
#endif /* !defined(SQLITE_OMIT_CHECK) */
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ if( p->tabFlags & TF_HasGenerated ){
+ int ii, nNG = 0;
+ testcase( p->tabFlags & TF_HasVirtual );
+ testcase( p->tabFlags & TF_HasStored );
+ for(ii=0; ii<p->nCol; ii++){
+ u32 colFlags = p->aCol[ii].colFlags;
+ if( (colFlags & COLFLAG_GENERATED)!=0 ){
+ Expr *pX = sqlite3ColumnExpr(p, &p->aCol[ii]);
+ testcase( colFlags & COLFLAG_VIRTUAL );
+ testcase( colFlags & COLFLAG_STORED );
+ if( sqlite3ResolveSelfReference(pParse, p, NC_GenCol, pX, 0) ){
+ /* If there are errors in resolving the expression, change the
+ ** expression to a NULL. This prevents code generators that operate
+ ** on the expression from inserting extra parts into the expression
+ ** tree that have been allocated from lookaside memory, which is
+ ** illegal in a schema and will lead to errors or heap corruption
+ ** when the database connection closes. */
+ sqlite3ColumnSetExpr(pParse, p, &p->aCol[ii],
+ sqlite3ExprAlloc(db, TK_NULL, 0, 0));
+ }
+ }else{
+ nNG++;
+ }
+ }
+ if( nNG==0 ){
+ sqlite3ErrorMsg(pParse, "must have at least one non-generated column");
+ return;
+ }
+ }
+#endif
/* Estimate the average row size for the table and for all implied indices */
estimateTableWidth(p);
@@ -107945,7 +123341,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
}
/* If not initializing, then create a record for the new table
- ** in the SQLITE_MASTER table of the database.
+ ** in the schema table of the database.
**
** If this is a TEMPORARY table, write the entry into the auxiliary
** file instead of into the main database file.
@@ -107962,10 +123358,10 @@ SQLITE_PRIVATE void sqlite3EndTable(
sqlite3VdbeAddOp1(v, OP_Close, 0);
- /*
+ /*
** Initialize zType for the new view or table.
*/
- if( p->pSelect==0 ){
+ if( IsOrdinaryTable(p) ){
/* A regular table */
zType = "table";
zType2 = "TABLE";
@@ -107999,6 +123395,11 @@ SQLITE_PRIVATE void sqlite3EndTable(
int addrInsLoop; /* Top of the loop for inserting rows */
Table *pSelTab; /* A table that describes the SELECT results */
+ if( IN_SPECIAL_PARSE ){
+ pParse->rc = SQLITE_ERROR;
+ pParse->nErr++;
+ return;
+ }
regYield = ++pParse->nMem;
regRec = ++pParse->nMem;
regRowid = ++pParse->nMem;
@@ -108010,10 +123411,10 @@ SQLITE_PRIVATE void sqlite3EndTable(
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
if( pParse->nErr ) return;
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect, SQLITE_AFF_BLOB);
if( pSelTab==0 ) return;
assert( p->aCol==0 );
- p->nCol = pSelTab->nCol;
+ p->nCol = p->nNVCol = pSelTab->nCol;
p->aCol = pSelTab->aCol;
pSelTab->nCol = 0;
pSelTab->aCol = 0;
@@ -108041,20 +123442,20 @@ SQLITE_PRIVATE void sqlite3EndTable(
Token *pEnd2 = tabOpts ? &pParse->sLastToken : pEnd;
n = (int)(pEnd2->z - pParse->sNameToken.z);
if( pEnd2->z[0]!=';' ) n += pEnd2->n;
- zStmt = sqlite3MPrintf(db,
+ zStmt = sqlite3MPrintf(db,
"CREATE %s %.*s", zType2, n, pParse->sNameToken.z
);
}
- /* A slot for the record has already been allocated in the
- ** SQLITE_MASTER table. We just need to update that slot with all
+ /* A slot for the record has already been allocated in the
+ ** schema table. We just need to update that slot with all
** the information we've collected.
*/
sqlite3NestedParse(pParse,
- "UPDATE %Q.%s "
- "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q "
- "WHERE rowid=#%d",
- db->aDb[iDb].zDbSName, MASTER_NAME,
+ "UPDATE %Q." LEGACY_SCHEMA_TABLE
+ " SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q"
+ " WHERE rowid=#%d",
+ db->aDb[iDb].zDbSName,
zType,
p->zName,
p->zName,
@@ -108069,7 +123470,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
/* Check to see if we need to create an sqlite_sequence table for
** keeping track of autoincrement keys.
*/
- if( (p->tabFlags & TF_Autoincrement)!=0 ){
+ if( (p->tabFlags & TF_Autoincrement)!=0 && !IN_SPECIAL_PARSE ){
Db *pDb = &db->aDb[iDb];
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( pDb->pSchema->pSeqTab==0 ){
@@ -108083,9 +123484,19 @@ SQLITE_PRIVATE void sqlite3EndTable(
/* Reparse everything to update our internal data structures */
sqlite3VdbeAddParseSchemaOp(v, iDb,
- sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName));
- }
+ sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0);
+ /* Test for cycles in generated columns and illegal expressions
+ ** in CHECK constraints and in DEFAULT clauses. */
+ if( p->tabFlags & TF_HasGenerated ){
+ sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0,
+ sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"",
+ db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC);
+ }
+ sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0,
+ sqlite3MPrintf(db, "PRAGMA \"%w\".integrity_check(%Q)",
+ db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC);
+ }
/* Add the table to the in-memory representation of the database.
*/
@@ -108093,6 +123504,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
Table *pOld;
Schema *pSchema = p->pSchema;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ assert( HasRowid(p) || p->iPKey<0 );
pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, p);
if( pOld ){
assert( p==pOld ); /* Malloc must have failed inside HashInsert() */
@@ -108102,19 +123514,27 @@ SQLITE_PRIVATE void sqlite3EndTable(
pParse->pNewTable = 0;
db->mDbFlags |= DBFLAG_SchemaChange;
-#ifndef SQLITE_OMIT_ALTERTABLE
- if( !p->pSelect ){
- const char *zName = (const char *)pParse->sNameToken.z;
- int nName;
- assert( !pSelect && pCons && pEnd );
- if( pCons->z==0 ){
- pCons = pEnd;
- }
- nName = (int)((const char *)pCons->z - zName);
- p->addColOffset = 13 + sqlite3Utf8CharLen(zName, nName);
+ /* If this is the magic sqlite_sequence table used by autoincrement,
+ ** then record a pointer to this table in the main database structure
+ ** so that INSERT can find the table easily. */
+ assert( !pParse->nested );
+#ifndef SQLITE_OMIT_AUTOINCREMENT
+ if( strcmp(p->zName, "sqlite_sequence")==0 ){
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ p->pSchema->pSeqTab = p;
}
#endif
}
+
+#ifndef SQLITE_OMIT_ALTERTABLE
+ if( !pSelect && IsOrdinaryTable(p) ){
+ assert( pCons && pEnd );
+ if( pCons->z==0 ){
+ pCons = pEnd;
+ }
+ p->u.tab.addColOffset = 13 + (int)(pCons->z - pParse->sNameToken.z);
+ }
+#endif
}
#ifndef SQLITE_OMIT_VIEW
@@ -108147,6 +123567,16 @@ SQLITE_PRIVATE void sqlite3CreateView(
sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr);
p = pParse->pNewTable;
if( p==0 || pParse->nErr ) goto create_view_fail;
+
+ /* Legacy versions of SQLite allowed the use of the magic "rowid" column
+ ** on a view, even though views do not have rowids. The following flag
+ ** setting fixes this problem. But the fix can be disabled by compiling
+ ** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that
+ ** depend upon the old buggy behavior. */
+#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
+ p->tabFlags |= TF_NoVisibleRowid;
+#endif
+
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
iDb = sqlite3SchemaToIndex(db, p->pSchema);
sqlite3FixInit(&sFix, pParse, iDb, "view", pName);
@@ -108157,13 +123587,15 @@ SQLITE_PRIVATE void sqlite3CreateView(
** allocated rather than point to the input string - which means that
** they will persist after the current sqlite3_exec() call returns.
*/
+ pSelect->selFlags |= SF_View;
if( IN_RENAME_OBJECT ){
- p->pSelect = pSelect;
+ p->u.view.pSelect = pSelect;
pSelect = 0;
}else{
- p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
+ p->u.view.pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
}
p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE);
+ p->eTabType = TABTYP_VIEW;
if( db->mallocFailed ) goto create_view_fail;
/* Locate the end of the CREATE VIEW statement. Make sEnd point to
@@ -108182,7 +123614,7 @@ SQLITE_PRIVATE void sqlite3CreateView(
sEnd.z = &z[n-1];
sEnd.n = 1;
- /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
+ /* Use sqlite3EndTable() to add the view to the schema table */
sqlite3EndTable(pParse, 0, &sEnd, 0, 0);
create_view_fail:
@@ -108201,11 +123633,10 @@ create_view_fail:
** the columns of the view in the pTable structure. Return the number
** of errors. If an error is seen leave an error message in pParse->zErrMsg.
*/
-SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
+static SQLITE_NOINLINE int viewGetColumnNames(Parse *pParse, Table *pTable){
Table *pSelTab; /* A fake table from which we get the result set */
Select *pSel; /* Copy of the SELECT that implements the view */
int nErr = 0; /* Number of errors encountered */
- int n; /* Temporarily holds the number of cursors assigned */
sqlite3 *db = pParse->db; /* Database connection for malloc errors */
#ifndef SQLITE_OMIT_VIRTUALTABLE
int rc;
@@ -108217,20 +123648,20 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
assert( pTable );
#ifndef SQLITE_OMIT_VIRTUALTABLE
- db->nSchemaLock++;
- rc = sqlite3VtabCallConnect(pParse, pTable);
- db->nSchemaLock--;
- if( rc ){
- return 1;
+ if( IsVirtual(pTable) ){
+ db->nSchemaLock++;
+ rc = sqlite3VtabCallConnect(pParse, pTable);
+ db->nSchemaLock--;
+ return rc;
}
- if( IsVirtual(pTable) ) return 0;
#endif
#ifndef SQLITE_OMIT_VIEW
/* A positive nCol means the columns names for this view are
- ** already known.
+ ** already known. This routine is not called unless either the
+ ** table is virtual or nCol is zero.
*/
- if( pTable->nCol>0 ) return 0;
+ assert( pTable->nCol<=0 );
/* A negative nCol is a special marker meaning that we are currently
** trying to compute the column names. If we enter this routine with
@@ -108242,7 +123673,7 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
** Actually, the error above is now caught prior to reaching this point.
** But the following test is still important as it does come up
** in the following:
- **
+ **
** CREATE TABLE main.ex1(a);
** CREATE TEMP VIEW ex1 AS SELECT a FROM ex1;
** SELECT * FROM temp.ex1;
@@ -108260,72 +123691,75 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
** to be permanent. So the computation is done on a copy of the SELECT
** statement that defines the view.
*/
- assert( pTable->pSelect );
- pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
+ assert( IsView(pTable) );
+ pSel = sqlite3SelectDup(db, pTable->u.view.pSelect, 0);
if( pSel ){
-#ifndef SQLITE_OMIT_ALTERTABLE
u8 eParseMode = pParse->eParseMode;
+ int nTab = pParse->nTab;
+ int nSelect = pParse->nSelect;
pParse->eParseMode = PARSE_MODE_NORMAL;
-#endif
- n = pParse->nTab;
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
pTable->nCol = -1;
- db->lookaside.bDisable++;
+ DisableLookaside;
#ifndef SQLITE_OMIT_AUTHORIZATION
xAuth = db->xAuth;
db->xAuth = 0;
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE);
db->xAuth = xAuth;
#else
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE);
#endif
- pParse->nTab = n;
- if( pTable->pCheck ){
+ pParse->nTab = nTab;
+ pParse->nSelect = nSelect;
+ if( pSelTab==0 ){
+ pTable->nCol = 0;
+ nErr++;
+ }else if( pTable->pCheck ){
/* CREATE VIEW name(arglist) AS ...
** The names of the columns in the table are taken from
** arglist which is stored in pTable->pCheck. The pCheck field
** normally holds CHECK constraints on an ordinary table, but for
** a VIEW it holds the list of column names.
*/
- sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
+ sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
&pTable->nCol, &pTable->aCol);
- if( db->mallocFailed==0
- && pParse->nErr==0
+ if( pParse->nErr==0
&& pTable->nCol==pSel->pEList->nExpr
){
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel);
+ assert( db->mallocFailed==0 );
+ sqlite3SubqueryColumnTypes(pParse, pTable, pSel, SQLITE_AFF_NONE);
}
- }else if( pSelTab ){
+ }else{
/* CREATE VIEW name AS... without an argument list. Construct
** the column names from the SELECT statement that defines the view.
*/
assert( pTable->aCol==0 );
pTable->nCol = pSelTab->nCol;
pTable->aCol = pSelTab->aCol;
+ pTable->tabFlags |= (pSelTab->tabFlags & COLFLAG_NOINSERT);
pSelTab->nCol = 0;
pSelTab->aCol = 0;
assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
- }else{
- pTable->nCol = 0;
- nErr++;
}
+ pTable->nNVCol = pTable->nCol;
sqlite3DeleteTable(db, pSelTab);
sqlite3SelectDelete(db, pSel);
- db->lookaside.bDisable--;
-#ifndef SQLITE_OMIT_ALTERTABLE
+ EnableLookaside;
pParse->eParseMode = eParseMode;
-#endif
} else {
nErr++;
}
pTable->pSchema->schemaFlags |= DB_UnresetViews;
if( db->mallocFailed ){
sqlite3DeleteColumnNames(db, pTable);
- pTable->aCol = 0;
- pTable->nCol = 0;
}
#endif /* SQLITE_OMIT_VIEW */
- return nErr;
+ return nErr;
+}
+SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
+ assert( pTable!=0 );
+ if( !IsVirtual(pTable) && pTable->nCol>0 ) return 0;
+ return viewGetColumnNames(pParse, pTable);
}
#endif /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */
@@ -108339,10 +123773,8 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
if( !DbHasProperty(db, idx, DB_UnresetViews) ) return;
for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){
Table *pTab = sqliteHashData(i);
- if( pTab->pSelect ){
+ if( IsView(pTab) ){
sqlite3DeleteColumnNames(db, pTab);
- pTab->aCol = 0;
- pTab->nCol = 0;
}
}
DbClearProperty(db, idx, DB_UnresetViews);
@@ -108361,7 +123793,7 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
** on tables and/or indices that are the process of being deleted.
** If you are unlucky, one of those deleted indices or tables might
** have the same rootpage number as the real table or index that is
-** being moved. So we cannot stop searching after the first match
+** being moved. So we cannot stop searching after the first match
** because the first match might be for one of the deleted indices
** or tables and not the table/index that is actually being moved.
** We must continue looping until all tables and indices with
@@ -108369,7 +123801,7 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
** in order to be certain that we got the right one.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
-SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){
+SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, Pgno iFrom, Pgno iTo){
HashElem *pElem;
Hash *pHash;
Db *pDb;
@@ -108395,10 +123827,10 @@ SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iT
/*
** Write code to erase the table with root-page iTable from database iDb.
-** Also write code to modify the sqlite_master table and internal schema
+** Also write code to modify the sqlite_schema table and internal schema
** if a root-page of another table is moved by the btree-layer whilst
** erasing iTable (this can happen with an auto-vacuum database).
-*/
+*/
static void destroyRootPage(Parse *pParse, int iTable, int iDb){
Vdbe *v = sqlite3GetVdbe(pParse);
int r1 = sqlite3GetTempReg(pParse);
@@ -108408,30 +123840,31 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
#ifndef SQLITE_OMIT_AUTOVACUUM
/* OP_Destroy stores an in integer r1. If this integer
** is non-zero, then it is the root page number of a table moved to
- ** location iTable. The following code modifies the sqlite_master table to
+ ** location iTable. The following code modifies the sqlite_schema table to
** reflect this.
**
** The "#NNN" in the SQL is a special constant that means whatever value
** is in register NNN. See grammar rules associated with the TK_REGISTER
** token for additional information.
*/
- sqlite3NestedParse(pParse,
- "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
- pParse->db->aDb[iDb].zDbSName, MASTER_NAME, iTable, r1, r1);
+ sqlite3NestedParse(pParse,
+ "UPDATE %Q." LEGACY_SCHEMA_TABLE
+ " SET rootpage=%d WHERE #%d AND rootpage=#%d",
+ pParse->db->aDb[iDb].zDbSName, iTable, r1, r1);
#endif
sqlite3ReleaseTempReg(pParse, r1);
}
/*
** Write VDBE code to erase table pTab and all associated indices on disk.
-** Code to update the sqlite_master tables and internal schema definitions
+** Code to update the sqlite_schema tables and internal schema definitions
** in case a root-page belonging to another table is moved by the btree layer
** is also added (this can happen with an auto-vacuum database).
*/
static void destroyTable(Parse *pParse, Table *pTab){
/* If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM
** is not defined), then it is important to call OP_Destroy on the
- ** table and index root-pages in order, starting with the numerically
+ ** table and index root-pages in order, starting with the numerically
** largest root-page number. This guarantees that none of the root-pages
** to be destroyed is relocated by an earlier OP_Destroy. i.e. if the
** following were coded:
@@ -108441,22 +123874,22 @@ static void destroyTable(Parse *pParse, Table *pTab){
** OP_Destroy 5 0
**
** and root page 5 happened to be the largest root-page number in the
- ** database, then root page 5 would be moved to page 4 by the
+ ** database, then root page 5 would be moved to page 4 by the
** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit
** a free-list page.
*/
- int iTab = pTab->tnum;
- int iDestroyed = 0;
+ Pgno iTab = pTab->tnum;
+ Pgno iDestroyed = 0;
while( 1 ){
Index *pIdx;
- int iLargest = 0;
+ Pgno iLargest = 0;
if( iDestroyed==0 || iTab<iDestroyed ){
iLargest = iTab;
}
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- int iIdx = pIdx->tnum;
+ Pgno iIdx = pIdx->tnum;
assert( pIdx->pSchema==pTab->pSchema );
if( (iDestroyed==0 || (iIdx<iDestroyed)) && iIdx>iLargest ){
iLargest = iIdx;
@@ -108517,12 +123950,12 @@ SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, in
#endif
/* Drop all triggers associated with the table being dropped. Code
- ** is generated to remove entries from sqlite_master and/or
- ** sqlite_temp_master if required.
+ ** is generated to remove entries from sqlite_schema and/or
+ ** sqlite_temp_schema if required.
*/
pTrigger = sqlite3TriggerList(pParse, pTab);
while( pTrigger ){
- assert( pTrigger->pSchema==pTab->pSchema ||
+ assert( pTrigger->pSchema==pTab->pSchema ||
pTrigger->pSchema==db->aDb[1].pSchema );
sqlite3DropTriggerPtr(pParse, pTrigger);
pTrigger = pTrigger->pNext;
@@ -108542,16 +123975,17 @@ SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, in
}
#endif
- /* Drop all SQLITE_MASTER table and index entries that refer to the
- ** table. The program name loops through the master table and deletes
+ /* Drop all entries in the schema table that refer to the
+ ** table. The program name loops through the schema table and deletes
** every row that refers to a table of the same name as the one being
** dropped. Triggers are handled separately because a trigger can be
** created in the temp database that refers to a table in another
** database.
*/
- sqlite3NestedParse(pParse,
- "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
- pDb->zDbSName, MASTER_NAME, pTab->zName);
+ sqlite3NestedParse(pParse,
+ "DELETE FROM %Q." LEGACY_SCHEMA_TABLE
+ " WHERE tbl_name=%Q and type!='trigger'",
+ pDb->zDbSName, pTab->zName);
if( !isView && !IsVirtual(pTab) ){
destroyTable(pParse, pTab);
}
@@ -108561,6 +123995,7 @@ SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, in
*/
if( IsVirtual(pTab) ){
sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0);
+ sqlite3MayAbort(pParse);
}
sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
sqlite3ChangeCookie(pParse, iDb);
@@ -108568,6 +124003,41 @@ SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, in
}
/*
+** Return TRUE if shadow tables should be read-only in the current
+** context.
+*/
+SQLITE_PRIVATE int sqlite3ReadOnlyShadowTables(sqlite3 *db){
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( (db->flags & SQLITE_Defensive)!=0
+ && db->pVtabCtx==0
+ && db->nVdbeExec==0
+ && !sqlite3VtabInSync(db)
+ ){
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+** Return true if it is not allowed to drop the given table
+*/
+static int tableMayNotBeDropped(sqlite3 *db, Table *pTab){
+ if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
+ if( sqlite3StrNICmp(pTab->zName+7, "stat", 4)==0 ) return 0;
+ if( sqlite3StrNICmp(pTab->zName+7, "parameters", 10)==0 ) return 0;
+ return 1;
+ }
+ if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){
+ return 1;
+ }
+ if( pTab->tabFlags & TF_Eponymous ){
+ return 1;
+ }
+ return 0;
+}
+
+/*
** This routine is called to do the work of a DROP TABLE statement.
** pName is the name of the table to be dropped.
*/
@@ -108589,7 +124059,10 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView,
if( noErr ) db->suppressErr--;
if( pTab==0 ){
- if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
+ if( noErr ){
+ sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
+ sqlite3ForceNotReadOnly(pParse);
+ }
goto exit_drop_table;
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
@@ -108636,8 +124109,7 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView,
}
}
#endif
- if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
- && sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){
+ if( tableMayNotBeDropped(db, pTab) ){
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
goto exit_drop_table;
}
@@ -108646,17 +124118,17 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView,
/* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used
** on a table.
*/
- if( isView && pTab->pSelect==0 ){
+ if( isView && !IsView(pTab) ){
sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName);
goto exit_drop_table;
}
- if( !isView && pTab->pSelect ){
+ if( !isView && IsView(pTab) ){
sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName);
goto exit_drop_table;
}
#endif
- /* Generate code to remove the table from the master table
+ /* Generate code to remove the table from the schema table
** on disk.
*/
v = sqlite3GetVdbe(pParse);
@@ -108701,7 +124173,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey(
FKey *pFKey = 0;
FKey *pNextTo;
Table *p = pParse->pNewTable;
- int nByte;
+ i64 nByte;
int i;
int nCol;
char *z;
@@ -108714,7 +124186,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey(
if( pToCol && pToCol->nExpr!=1 ){
sqlite3ErrorMsg(pParse, "foreign key on %s"
" should reference only one column of table %T",
- p->aCol[iCol].zName, pTo);
+ p->aCol[iCol].zCnName, pTo);
goto fk_end;
}
nCol = 1;
@@ -108729,7 +124201,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey(
nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1;
if( pToCol ){
for(i=0; i<pToCol->nExpr; i++){
- nByte += sqlite3Strlen30(pToCol->a[i].zName) + 1;
+ nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1;
}
}
pFKey = sqlite3DbMallocZero(db, nByte );
@@ -108737,7 +124209,8 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey(
goto fk_end;
}
pFKey->pFrom = p;
- pFKey->pNextFrom = p->pFKey;
+ assert( IsOrdinaryTable(p) );
+ pFKey->pNextFrom = p->u.tab.pFKey;
z = (char*)&pFKey->aCol[nCol];
pFKey->zTo = z;
if( IN_RENAME_OBJECT ){
@@ -108754,30 +124227,30 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey(
for(i=0; i<nCol; i++){
int j;
for(j=0; j<p->nCol; j++){
- if( sqlite3StrICmp(p->aCol[j].zName, pFromCol->a[i].zName)==0 ){
+ if( sqlite3StrICmp(p->aCol[j].zCnName, pFromCol->a[i].zEName)==0 ){
pFKey->aCol[i].iFrom = j;
break;
}
}
if( j>=p->nCol ){
- sqlite3ErrorMsg(pParse,
- "unknown column \"%s\" in foreign key definition",
- pFromCol->a[i].zName);
+ sqlite3ErrorMsg(pParse,
+ "unknown column \"%s\" in foreign key definition",
+ pFromCol->a[i].zEName);
goto fk_end;
}
if( IN_RENAME_OBJECT ){
- sqlite3RenameTokenRemap(pParse, &pFKey->aCol[i], pFromCol->a[i].zName);
+ sqlite3RenameTokenRemap(pParse, &pFKey->aCol[i], pFromCol->a[i].zEName);
}
}
}
if( pToCol ){
for(i=0; i<nCol; i++){
- int n = sqlite3Strlen30(pToCol->a[i].zName);
+ int n = sqlite3Strlen30(pToCol->a[i].zEName);
pFKey->aCol[i].zCol = z;
if( IN_RENAME_OBJECT ){
- sqlite3RenameTokenRemap(pParse, z, pToCol->a[i].zName);
+ sqlite3RenameTokenRemap(pParse, z, pToCol->a[i].zEName);
}
- memcpy(z, pToCol->a[i].zName, n);
+ memcpy(z, pToCol->a[i].zEName, n);
z[n] = 0;
z += n+1;
}
@@ -108787,7 +124260,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey(
pFKey->aAction[1] = (u8)((flags >> 8 ) & 0xff); /* ON UPDATE action */
assert( sqlite3SchemaMutexHeld(db, 0, p->pSchema) );
- pNextTo = (FKey *)sqlite3HashInsert(&p->pSchema->fkeyHash,
+ pNextTo = (FKey *)sqlite3HashInsert(&p->pSchema->fkeyHash,
pFKey->zTo, (void *)pFKey
);
if( pNextTo==pFKey ){
@@ -108802,7 +124275,8 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey(
/* Link the foreign key to the table as the last step.
*/
- p->pFKey = pFKey;
+ assert( IsOrdinaryTable(p) );
+ p->u.tab.pFKey = pFKey;
pFKey = 0;
fk_end:
@@ -108823,7 +124297,9 @@ SQLITE_PRIVATE void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){
#ifndef SQLITE_OMIT_FOREIGN_KEY
Table *pTab;
FKey *pFKey;
- if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return;
+ if( (pTab = pParse->pNewTable)==0 ) return;
+ if( NEVER(!IsOrdinaryTable(pTab)) ) return;
+ if( (pFKey = pTab->u.tab.pFKey)==0 ) return;
assert( isDeferred==0 || isDeferred==1 ); /* EV: R-30323-21917 */
pFKey->isDeferred = (u8)isDeferred;
#endif
@@ -108847,7 +124323,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
int iSorter; /* Cursor opened by OpenSorter (if in use) */
int addr1; /* Address of top of loop */
int addr2; /* Address to jump to for next iteration */
- int tnum; /* Root page of index */
+ Pgno tnum; /* Root page of index */
int iPartIdxLabel; /* Jump to this label to skip a row */
Vdbe *v; /* Generate code into this virtual machine */
KeyInfo *pKey; /* KeyInfo for index */
@@ -108868,12 +124344,12 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
v = sqlite3GetVdbe(pParse);
if( v==0 ) return;
if( memRootPage>=0 ){
- tnum = memRootPage;
+ tnum = (Pgno)memRootPage;
}else{
tnum = pIndex->tnum;
}
pKey = sqlite3KeyInfoOfIndex(pParse, pIndex);
- assert( pKey!=0 || db->mallocFailed || pParse->nErr );
+ assert( pKey!=0 || pParse->nErr );
/* Open the sorter cursor if we are to use one. */
iSorter = pParse->nTab++;
@@ -108893,7 +124369,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr1);
if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb);
- sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb,
+ sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, (int)tnum, iDb,
(char *)pKey, P4_KEYINFO);
sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0));
@@ -108907,10 +124383,27 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
sqlite3UniqueConstraint(pParse, OE_Abort, pIndex);
sqlite3VdbeJumpHere(v, j2);
}else{
+ /* Most CREATE INDEX and REINDEX statements that are not UNIQUE can not
+ ** abort. The exception is if one of the indexed expressions contains a
+ ** user function that throws an exception when it is evaluated. But the
+ ** overhead of adding a statement journal to a CREATE INDEX statement is
+ ** very small (since most of the pages written do not contain content that
+ ** needs to be restored if the statement aborts), so we call
+ ** sqlite3MayAbort() for all CREATE INDEX statements. */
+ sqlite3MayAbort(pParse);
addr2 = sqlite3VdbeCurrentAddr(v);
}
sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx);
- sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx);
+ if( !pIndex->bAscKeyBug ){
+ /* This OP_SeekEnd opcode makes index insert for a REINDEX go much
+ ** faster by avoiding unnecessary seeks. But the optimization does
+ ** not work for UNIQUE constraint indexes on WITHOUT ROWID tables
+ ** with DESC primary keys, since those indexes have there keys in
+ ** a different order from the main table.
+ ** See ticket: https://www.sqlite.org/src/info/bba7b69f9849b5bf
+ */
+ sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx);
+ }
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
sqlite3ReleaseTempReg(pParse, regRecord);
@@ -108958,8 +124451,29 @@ SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(
}
/*
-** Create a new index for an SQL table. pName1.pName2 is the name of the index
-** and pTblList is the name of the table that is to be indexed. Both will
+** If expression list pList contains an expression that was parsed with
+** an explicit "NULLS FIRST" or "NULLS LAST" clause, leave an error in
+** pParse and return non-zero. Otherwise, return zero.
+*/
+SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse *pParse, ExprList *pList){
+ if( pList ){
+ int i;
+ for(i=0; i<pList->nExpr; i++){
+ if( pList->a[i].fg.bNulls ){
+ u8 sf = pList->a[i].fg.sortFlags;
+ sqlite3ErrorMsg(pParse, "unsupported use of NULLS %s",
+ (sf==0 || sf==3) ? "FIRST" : "LAST"
+ );
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Create a new index for an SQL table. pName1.pName2 is the name of the index
+** and pTblList is the name of the table that is to be indexed. Both will
** be NULL for a primary key or an index that is created to satisfy a
** UNIQUE constraint. If pTable and pIndex are NULL, use pParse->pNewTable
** as the table to be indexed. pParse->pNewTable is a table that is
@@ -108967,7 +124481,7 @@ SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(
**
** pList is a list of columns to be indexed. pList will be NULL if this
** is a primary key or unique-constraint on the most recent column added
-** to the table currently under construction.
+** to the table currently under construction.
*/
SQLITE_PRIVATE void sqlite3CreateIndex(
Parse *pParse, /* All information about this parse */
@@ -108999,22 +124513,27 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
char *zExtra = 0; /* Extra space after the Index object */
Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */
- if( db->mallocFailed || pParse->nErr>0 ){
+ assert( db->pParse==pParse );
+ if( pParse->nErr ){
goto exit_create_index;
}
+ assert( db->mallocFailed==0 );
if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){
goto exit_create_index;
}
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto exit_create_index;
}
+ if( sqlite3HasExplicitNulls(pParse, pList) ){
+ goto exit_create_index;
+ }
/*
** Find the table that is to be indexed. Return early if not found.
*/
if( pTblName!=0 ){
- /* Use the two-part index name to determine the database
+ /* Use the two-part index name to determine the database
** to search for the table. 'Fix' the table name to this db
** before looking up the table.
*/
@@ -109026,7 +124545,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
#ifndef SQLITE_OMIT_TEMPDB
/* If the index name was unqualified, check if the table
** is a temp table. If so, set the database to 1. Do not do this
- ** if initialising a database schema.
+ ** if initializing a database schema.
*/
if( !db->init.busy ){
pTab = sqlite3SrcListLookup(pParse, pTblName);
@@ -109046,7 +124565,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
assert( db->mallocFailed==0 || pTab==0 );
if( pTab==0 ) goto exit_create_index;
if( iDb==1 && db->aDb[iDb].pSchema!=pTab->pSchema ){
- sqlite3ErrorMsg(pParse,
+ sqlite3ErrorMsg(pParse,
"cannot create a TEMP index on non-TEMP table \"%s\"",
pTab->zName);
goto exit_create_index;
@@ -109062,22 +124581,18 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
pDb = &db->aDb[iDb];
assert( pTab!=0 );
- assert( pParse->nErr==0 );
- if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
+ if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
&& db->init.busy==0
+ && pTblName!=0
#if SQLITE_USER_AUTHENTICATION
&& sqlite3UserAuthTable(pTab->zName)==0
#endif
-#ifdef SQLITE_ALLOW_SQLITE_MASTER_INDEX
- && sqlite3StrICmp(&pTab->zName[7],"master")!=0
-#endif
- && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0
- ){
+ ){
sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
goto exit_create_index;
}
#ifndef SQLITE_OMIT_VIEW
- if( pTab->pSelect ){
+ if( IsView(pTab) ){
sqlite3ErrorMsg(pParse, "views may not be indexed");
goto exit_create_index;
}
@@ -109091,10 +124606,10 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
/*
** Find the name of the index. Make sure there is not already another
- ** index or table with the same name.
+ ** index or table with the same name.
**
** Exception: If we are reading the names of permanent indices from the
- ** sqlite_master table (because some other process changed the schema) and
+ ** sqlite_schema table (because some other process changed the schema) and
** one of the index names collides with the name of a temporary table or
** index, then we will continue to process this index.
**
@@ -109106,12 +124621,12 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
zName = sqlite3NameFromToken(db, pName);
if( zName==0 ) goto exit_create_index;
assert( pName->z!=0 );
- if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName,"index",pTab->zName) ){
goto exit_create_index;
}
if( !IN_RENAME_OBJECT ){
if( !db->init.busy ){
- if( sqlite3FindTable(db, zName, 0)!=0 ){
+ if( sqlite3FindTable(db, zName, pDb->zDbSName)!=0 ){
sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
goto exit_create_index;
}
@@ -109122,6 +124637,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
}else{
assert( !db->init.busy );
sqlite3CodeVerifySchema(pParse, iDb);
+ sqlite3ForceNotReadOnly(pParse);
}
goto exit_create_index;
}
@@ -109167,14 +124683,15 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
Token prevCol;
Column *pCol = &pTab->aCol[pTab->nCol-1];
pCol->colFlags |= COLFLAG_UNIQUE;
- sqlite3TokenInit(&prevCol, pCol->zName);
+ sqlite3TokenInit(&prevCol, pCol->zCnName);
pList = sqlite3ExprListAppend(pParse, 0,
sqlite3ExprAlloc(db, TK_ID, &prevCol, 0));
if( pList==0 ) goto exit_create_index;
assert( pList->nExpr==1 );
- sqlite3ExprListSetSortOrder(pList, sortOrder);
+ sqlite3ExprListSetSortOrder(pList, sortOrder, SQLITE_SO_UNDEFINED);
}else{
sqlite3ExprListCheckLength(pParse, pList, "index");
+ if( pParse->nErr ) goto exit_create_index;
}
/* Figure out how many bytes of space are required to store explicitly
@@ -109184,15 +124701,17 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
Expr *pExpr = pList->a[i].pExpr;
assert( pExpr!=0 );
if( pExpr->op==TK_COLLATE ){
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
nExtra += (1 + sqlite3Strlen30(pExpr->u.zToken));
}
}
- /*
- ** Allocate the index structure.
+ /*
+ ** Allocate the index structure.
*/
nName = sqlite3Strlen30(zName);
nExtraCol = pPk ? pPk->nKeyCol : 1;
+ assert( pList->nExpr + nExtraCol <= 32767 /* Fits in i16 */ );
pIndex = sqlite3AllocateIndexObject(db, pList->nExpr + nExtraCol,
nName + nExtra + 1, &zExtra);
if( db->mallocFailed ){
@@ -109260,19 +124779,27 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
j = XN_EXPR;
pIndex->aiColumn[i] = XN_EXPR;
pIndex->uniqNotNull = 0;
+ pIndex->bHasExpr = 1;
}else{
j = pCExpr->iColumn;
assert( j<=0x7fff );
if( j<0 ){
j = pTab->iPKey;
- }else if( pTab->aCol[j].notNull==0 ){
- pIndex->uniqNotNull = 0;
+ }else{
+ if( pTab->aCol[j].notNull==0 ){
+ pIndex->uniqNotNull = 0;
+ }
+ if( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL ){
+ pIndex->bHasVCol = 1;
+ pIndex->bHasExpr = 1;
+ }
}
pIndex->aiColumn[i] = (i16)j;
}
zColl = 0;
if( pListItem->pExpr->op==TK_COLLATE ){
int nColl;
+ assert( !ExprHasProperty(pListItem->pExpr, EP_IntValue) );
zColl = pListItem->pExpr->u.zToken;
nColl = sqlite3Strlen30(zColl) + 1;
assert( nExtra>=nColl );
@@ -109281,14 +124808,14 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
zExtra += nColl;
nExtra -= nColl;
}else if( j>=0 ){
- zColl = pTab->aCol[j].zColl;
+ zColl = sqlite3ColumnColl(&pTab->aCol[j]);
}
if( !zColl ) zColl = sqlite3StrBINARY;
if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl) ){
goto exit_create_index;
}
pIndex->azColl[i] = zColl;
- requestedSortOrder = pListItem->sortOrder & sortOrderMask;
+ requestedSortOrder = pListItem->fg.sortFlags & sortOrderMask;
pIndex->aSortOrder[i] = (u8)requestedSortOrder;
}
@@ -109300,9 +124827,10 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
for(j=0; j<pPk->nKeyCol; j++){
int x = pPk->aiColumn[j];
assert( x>=0 );
- if( hasColumn(pIndex->aiColumn, pIndex->nKeyCol, x) ){
- pIndex->nColumn--;
+ if( isDupColumn(pIndex, pIndex->nKeyCol, pPk, j) ){
+ pIndex->nColumn--;
}else{
+ testcase( hasColumn(pIndex->aiColumn,pIndex->nKeyCol,x) );
pIndex->aiColumn[i] = x;
pIndex->azColl[i] = pPk->azColl[j];
pIndex->aSortOrder[i] = pPk->aSortOrder[j];
@@ -109319,14 +124847,14 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
/* If this index contains every column of its table, then mark
** it as a covering index */
- assert( HasRowid(pTab)
- || pTab->iPKey<0 || sqlite3ColumnOfIndex(pIndex, pTab->iPKey)>=0 );
+ assert( HasRowid(pTab)
+ || pTab->iPKey<0 || sqlite3TableColumnToIndex(pIndex, pTab->iPKey)>=0 );
recomputeColumnsNotIndexed(pIndex);
if( pTblName!=0 && pIndex->nColumn>=pTab->nCol ){
pIndex->isCovering = 1;
for(j=0; j<pTab->nCol; j++){
if( j==pTab->iPKey ) continue;
- if( sqlite3ColumnOfIndex(pIndex,j)>=0 ) continue;
+ if( sqlite3TableColumnToIndex(pIndex,j)>=0 ) continue;
pIndex->isCovering = 0;
break;
}
@@ -109375,13 +124903,13 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
if( pIdx->onError!=pIndex->onError ){
/* This constraint creates the same index as a previous
** constraint specified somewhere in the CREATE TABLE statement.
- ** However the ON CONFLICT clauses are different. If both this
+ ** However the ON CONFLICT clauses are different. If both this
** constraint and the previous equivalent constraint have explicit
** ON CONFLICT clauses this is an error. Otherwise, use the
** explicitly specified behavior for the index.
*/
if( !(pIdx->onError==OE_Default || pIndex->onError==OE_Default) ){
- sqlite3ErrorMsg(pParse,
+ sqlite3ErrorMsg(pParse,
"conflicting ON CONFLICT clauses specified", 0);
}
if( pIdx->onError==OE_Default ){
@@ -109389,6 +124917,11 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
}
}
if( idxType==SQLITE_IDXTYPE_PRIMARYKEY ) pIdx->idxType = idxType;
+ if( IN_RENAME_OBJECT ){
+ pIndex->pNext = pParse->pNewIndex;
+ pParse->pNewIndex = pIndex;
+ pIndex = 0;
+ }
goto exit_create_index;
}
}
@@ -109397,14 +124930,22 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
if( !IN_RENAME_OBJECT ){
/* Link the new Index structure to its table and to the other
- ** in-memory database structures.
+ ** in-memory database structures.
*/
assert( pParse->nErr==0 );
if( db->init.busy ){
Index *p;
assert( !IN_SPECIAL_PARSE );
assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
- p = sqlite3HashInsert(&pIndex->pSchema->idxHash,
+ if( pTblName!=0 ){
+ pIndex->tnum = db->init.newTnum;
+ if( sqlite3IndexHasDuplicateRootPage(pIndex) ){
+ sqlite3ErrorMsg(pParse, "invalid rootpage");
+ pParse->rc = SQLITE_CORRUPT_BKPT;
+ goto exit_create_index;
+ }
+ }
+ p = sqlite3HashInsert(&pIndex->pSchema->idxHash,
pIndex->zName, pIndex);
if( p ){
assert( p==pIndex ); /* Malloc must have failed */
@@ -109412,16 +124953,13 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
goto exit_create_index;
}
db->mDbFlags |= DBFLAG_SchemaChange;
- if( pTblName!=0 ){
- pIndex->tnum = db->init.newTnum;
- }
}
/* If this is the initial CREATE INDEX statement (or CREATE TABLE if the
** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then
** emit code to allocate the index rootpage on disk and make an entry for
- ** the index in the sqlite_master table and populate the index with
- ** content. But, do not do this if we are simply reading the sqlite_master
+ ** the index in the sqlite_schema table and populate the index with
+ ** content. But, do not do this if we are simply reading the sqlite_schema
** table to parse the schema, or if this index is the PRIMARY KEY index
** of a WITHOUT ROWID table.
**
@@ -109441,17 +124979,18 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
sqlite3BeginWriteOperation(pParse, 1, iDb);
/* Create the rootpage for the index using CreateIndex. But before
- ** doing so, code a Noop instruction and store its address in
- ** Index.tnum. This is required in case this index is actually a
- ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In
+ ** doing so, code a Noop instruction and store its address in
+ ** Index.tnum. This is required in case this index is actually a
+ ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In
** that case the convertToWithoutRowidTable() routine will replace
** the Noop with a Goto to jump over the VDBE code generated below. */
- pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop);
+ pIndex->tnum = (Pgno)sqlite3VdbeAddOp0(v, OP_Noop);
sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY);
/* Gather the complete text of the CREATE INDEX statement into
** the zStmt variable
*/
+ assert( pName!=0 || pStart==0 );
if( pStart ){
int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n;
if( pName->z[n-1]==';' ) n--;
@@ -109464,16 +125003,16 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
zStmt = 0;
}
- /* Add an entry in sqlite_master for this index
+ /* Add an entry in sqlite_schema for this index
*/
- sqlite3NestedParse(pParse,
- "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
- db->aDb[iDb].zDbSName, MASTER_NAME,
- pIndex->zName,
- pTab->zName,
- iMem,
- zStmt
- );
+ sqlite3NestedParse(pParse,
+ "INSERT INTO %Q." LEGACY_SCHEMA_TABLE " VALUES('index',%Q,%Q,#%d,%Q);",
+ db->aDb[iDb].zDbSName,
+ pIndex->zName,
+ pTab->zName,
+ iMem,
+ zStmt
+ );
sqlite3DbFree(db, zStmt);
/* Fill the index with data and reparse the schema. Code an OP_Expire
@@ -109483,33 +125022,16 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
sqlite3RefillIndex(pParse, pIndex, iMem);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddParseSchemaOp(v, iDb,
- sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
+ sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName), 0);
sqlite3VdbeAddOp2(v, OP_Expire, 0, 1);
}
- sqlite3VdbeJumpHere(v, pIndex->tnum);
+ sqlite3VdbeJumpHere(v, (int)pIndex->tnum);
}
}
-
- /* When adding an index to the list of indices for a table, make
- ** sure all indices labeled OE_Replace come after all those labeled
- ** OE_Ignore. This is necessary for the correct constraint check
- ** processing (in sqlite3GenerateConstraintChecks()) as part of
- ** UPDATE and INSERT statements.
- */
if( db->init.busy || pTblName==0 ){
- if( onError!=OE_Replace || pTab->pIndex==0
- || pTab->pIndex->onError==OE_Replace){
- pIndex->pNext = pTab->pIndex;
- pTab->pIndex = pIndex;
- }else{
- Index *pOther = pTab->pIndex;
- while( pOther->pNext && pOther->pNext->onError!=OE_Replace ){
- pOther = pOther->pNext;
- }
- pIndex->pNext = pOther->pNext;
- pOther->pNext = pIndex;
- }
+ pIndex->pNext = pTab->pIndex;
+ pTab->pIndex = pIndex;
pIndex = 0;
}
else if( IN_RENAME_OBJECT ){
@@ -109521,6 +125043,35 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
/* Clean up before exiting */
exit_create_index:
if( pIndex ) sqlite3FreeIndex(db, pIndex);
+ if( pTab ){
+ /* Ensure all REPLACE indexes on pTab are at the end of the pIndex list.
+ ** The list was already ordered when this routine was entered, so at this
+ ** point at most a single index (the newly added index) will be out of
+ ** order. So we have to reorder at most one index. */
+ Index **ppFrom;
+ Index *pThis;
+ for(ppFrom=&pTab->pIndex; (pThis = *ppFrom)!=0; ppFrom=&pThis->pNext){
+ Index *pNext;
+ if( pThis->onError!=OE_Replace ) continue;
+ while( (pNext = pThis->pNext)!=0 && pNext->onError!=OE_Replace ){
+ *ppFrom = pNext;
+ pThis->pNext = pNext->pNext;
+ pNext->pNext = pThis;
+ ppFrom = &pNext->pNext;
+ }
+ break;
+ }
+#ifdef SQLITE_DEBUG
+ /* Verify that all REPLACE indexes really are now at the end
+ ** of the index list. In other words, no other index type ever
+ ** comes after a REPLACE index on the list. */
+ for(pThis = pTab->pIndex; pThis; pThis=pThis->pNext){
+ assert( pThis->onError!=OE_Replace
+ || pThis->pNext==0
+ || pThis->pNext->onError==OE_Replace );
+ }
+#endif
+ }
sqlite3ExprDelete(db, pPIWhere);
sqlite3ExprListDelete(db, pList);
sqlite3SrcListDelete(db, pTblName);
@@ -109546,21 +125097,33 @@ exit_create_index:
** are based on typical values found in actual indices.
*/
SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){
- /* 10, 9, 8, 7, 6 */
- LogEst aVal[] = { 33, 32, 30, 28, 26 };
+ /* 10, 9, 8, 7, 6 */
+ static const LogEst aVal[] = { 33, 32, 30, 28, 26 };
LogEst *a = pIdx->aiRowLogEst;
+ LogEst x;
int nCopy = MIN(ArraySize(aVal), pIdx->nKeyCol);
int i;
/* Indexes with default row estimates should not have stat1 data */
assert( !pIdx->hasStat1 );
- /* Set the first entry (number of rows in the index) to the estimated
+ /* Set the first entry (number of rows in the index) to the estimated
** number of rows in the table, or half the number of rows in the table
- ** for a partial index. But do not let the estimate drop below 10. */
- a[0] = pIdx->pTable->nRowLogEst;
- if( pIdx->pPartIdxWhere!=0 ) a[0] -= 10; assert( 10==sqlite3LogEst(2) );
- if( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) );
+ ** for a partial index.
+ **
+ ** 2020-05-27: If some of the stat data is coming from the sqlite_stat1
+ ** table but other parts we are having to guess at, then do not let the
+ ** estimated number of rows in the table be less than 1000 (LogEst 99).
+ ** Failure to do this can cause the indexes for which we do not have
+ ** stat1 data to be ignored by the query planner.
+ */
+ x = pIdx->pTable->nRowLogEst;
+ assert( 99==sqlite3LogEst(1000) );
+ if( x<99 ){
+ pIdx->pTable->nRowLogEst = x = 99;
+ }
+ if( pIdx->pPartIdxWhere!=0 ){ x -= 10; assert( 10==sqlite3LogEst(2) ); }
+ a[0] = x;
/* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is
** 6 and each subsequent value (if any) is 5. */
@@ -109583,10 +125146,10 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists
sqlite3 *db = pParse->db;
int iDb;
- assert( pParse->nErr==0 ); /* Never called with prior errors */
if( db->mallocFailed ){
goto exit_drop_index;
}
+ assert( pParse->nErr==0 ); /* Never called with prior non-OOM errors */
assert( pName->nSrc==1 );
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto exit_drop_index;
@@ -109594,9 +125157,10 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists
pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase);
if( pIndex==0 ){
if( !ifExists ){
- sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0);
+ sqlite3ErrorMsg(pParse, "no such index: %S", pName->a);
}else{
sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase);
+ sqlite3ForceNotReadOnly(pParse);
}
pParse->checkSchema = 1;
goto exit_drop_index;
@@ -109616,20 +125180,20 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
goto exit_drop_index;
}
- if( !OMIT_TEMPDB && iDb ) code = SQLITE_DROP_TEMP_INDEX;
+ if( !OMIT_TEMPDB && iDb==1 ) code = SQLITE_DROP_TEMP_INDEX;
if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){
goto exit_drop_index;
}
}
#endif
- /* Generate code to remove the index and from the master table */
+ /* Generate code to remove the index and from the schema table */
v = sqlite3GetVdbe(pParse);
if( v ){
sqlite3BeginWriteOperation(pParse, 1, iDb);
sqlite3NestedParse(pParse,
- "DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
- db->aDb[iDb].zDbSName, MASTER_NAME, pIndex->zName
+ "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='index'",
+ db->aDb[iDb].zDbSName, pIndex->zName
);
sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName);
sqlite3ChangeCookie(pParse, iDb);
@@ -109666,9 +125230,9 @@ SQLITE_PRIVATE void *sqlite3ArrayAllocate(
int *pIdx /* Write the index of a new slot here */
){
char *z;
- int n = *pnEntry;
+ sqlite3_int64 n = *pIdx = *pnEntry;
if( (n & (n-1))==0 ){
- int sz = (n==0) ? 1 : 2*n;
+ sqlite3_int64 sz = (n==0) ? 1 : 2*n;
void *pNew = sqlite3DbRealloc(db, pArray, sz*szEntry);
if( pNew==0 ){
*pIdx = -1;
@@ -109678,7 +125242,6 @@ SQLITE_PRIVATE void *sqlite3ArrayAllocate(
}
z = (char*)pArray;
memset(&z[n * szEntry], 0, szEntry);
- *pIdx = n;
++*pnEntry;
return pArray;
}
@@ -109695,18 +125258,17 @@ SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *
if( pList==0 ){
pList = sqlite3DbMallocZero(db, sizeof(IdList) );
if( pList==0 ) return 0;
+ }else{
+ IdList *pNew;
+ pNew = sqlite3DbRealloc(db, pList,
+ sizeof(IdList) + pList->nId*sizeof(pList->a));
+ if( pNew==0 ){
+ sqlite3IdListDelete(db, pList);
+ return 0;
+ }
+ pList = pNew;
}
- pList->a = sqlite3ArrayAllocate(
- db,
- pList->a,
- sizeof(pList->a[0]),
- &pList->nId,
- &i
- );
- if( i<0 ){
- sqlite3IdListDelete(db, pList);
- return 0;
- }
+ i = pList->nId++;
pList->a[i].zName = sqlite3NameFromToken(db, pToken);
if( IN_RENAME_OBJECT && pList->a[i].zName ){
sqlite3RenameTokenMap(pParse, (void*)pList->a[i].zName, pToken);
@@ -109719,12 +125281,13 @@ SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *
*/
SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3 *db, IdList *pList){
int i;
+ assert( db!=0 );
if( pList==0 ) return;
+ assert( pList->eU4!=EU4_EXPR ); /* EU4_EXPR mode is not currently used */
for(i=0; i<pList->nId; i++){
sqlite3DbFree(db, pList->a[i].zName);
}
- sqlite3DbFree(db, pList->a);
- sqlite3DbFreeNN(db, pList);
+ sqlite3DbNNFreeNN(db, pList);
}
/*
@@ -109733,7 +125296,7 @@ SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3 *db, IdList *pList){
*/
SQLITE_PRIVATE int sqlite3IdListIndex(IdList *pList, const char *zName){
int i;
- if( pList==0 ) return -1;
+ assert( pList!=0 );
for(i=0; i<pList->nId; i++){
if( sqlite3StrICmp(pList->a[i].zName, zName)==0 ) return i;
}
@@ -109741,6 +125304,18 @@ SQLITE_PRIVATE int sqlite3IdListIndex(IdList *pList, const char *zName){
}
/*
+** Maximum size of a SrcList object.
+** The SrcList object is used to represent the FROM clause of a
+** SELECT statement, and the query planner cannot deal with more
+** than 64 tables in a join. So any value larger than 64 here
+** is sufficient for most uses. Smaller values, like say 10, are
+** appropriate for small and memory-limited applications.
+*/
+#ifndef SQLITE_MAX_SRCLIST
+# define SQLITE_MAX_SRCLIST 200
+#endif
+
+/*
** Expand the space allocated for the given SrcList object by
** creating nExtra new slots beginning at iStart. iStart is zero based.
** New slots are zeroed.
@@ -109756,11 +125331,12 @@ SQLITE_PRIVATE int sqlite3IdListIndex(IdList *pList, const char *zName){
** the iStart value would be 0. The result then would
** be: nil, nil, nil, A, B.
**
-** If a memory allocation fails the SrcList is unchanged. The
-** db->mallocFailed flag will be set to true.
+** If a memory allocation fails or the SrcList becomes too large, leave
+** the original SrcList unchanged, return NULL, and leave an error message
+** in pParse.
*/
SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
- sqlite3 *db, /* Database connection to notify of OOM errors */
+ Parse *pParse, /* Parsing context into which errors are reported */
SrcList *pSrc, /* The SrcList to be enlarged */
int nExtra, /* Number of new slots to add to pSrc->a[] */
int iStart /* Index in pSrc->a[] of first new slot */
@@ -109776,17 +125352,23 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
/* Allocate additional space if needed */
if( (u32)pSrc->nSrc+nExtra>pSrc->nAlloc ){
SrcList *pNew;
- int nAlloc = pSrc->nSrc*2+nExtra;
- int nGot;
+ sqlite3_int64 nAlloc = 2*(sqlite3_int64)pSrc->nSrc+nExtra;
+ sqlite3 *db = pParse->db;
+
+ if( pSrc->nSrc+nExtra>=SQLITE_MAX_SRCLIST ){
+ sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d",
+ SQLITE_MAX_SRCLIST);
+ return 0;
+ }
+ if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST;
pNew = sqlite3DbRealloc(db, pSrc,
sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) );
if( pNew==0 ){
assert( db->mallocFailed );
- return pSrc;
+ return 0;
}
pSrc = pNew;
- nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1;
- pSrc->nAlloc = nGot;
+ pSrc->nAlloc = nAlloc;
}
/* Move existing slots that come after the newly inserted slots
@@ -109811,7 +125393,8 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
** Append a new table name to the given SrcList. Create a new SrcList if
** need be. A new entry is created in the SrcList even if pTable is NULL.
**
-** A SrcList is returned, or NULL if there is an OOM error. The returned
+** A SrcList is returned, or NULL if there is an OOM error or if the
+** SrcList grows to large. The returned
** SrcList might be the same as the SrcList that was input or it might be
** a new one. If an OOM error does occurs, then the prior value of pList
** that is input to this routine is automatically freed.
@@ -109820,7 +125403,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
** database name prefix. Like this: "database.table". The pDatabase
** points to the table name and the pTable points to the database name.
** The SrcList.a[].zName field is filled with the table name which might
-** come from pTable (if pDatabase is NULL) or from pDatabase.
+** come from pTable (if pDatabase is NULL) or from pDatabase.
** SrcList.a[].zDatabase is filled with the database name from pTable,
** or with NULL if no database is specified.
**
@@ -109842,27 +125425,32 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
** before being added to the SrcList.
*/
SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(
- sqlite3 *db, /* Connection to notify of malloc failures */
+ Parse *pParse, /* Parsing context, in which errors are reported */
SrcList *pList, /* Append to this SrcList. NULL creates a new SrcList */
Token *pTable, /* Table to append */
Token *pDatabase /* Database of the table */
){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
+ sqlite3 *db;
assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */
- assert( db!=0 );
+ assert( pParse!=0 );
+ assert( pParse->db!=0 );
+ db = pParse->db;
if( pList==0 ){
- pList = sqlite3DbMallocRawNN(db, sizeof(SrcList) );
+ pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) );
if( pList==0 ) return 0;
pList->nAlloc = 1;
pList->nSrc = 1;
memset(&pList->a[0], 0, sizeof(pList->a[0]));
pList->a[0].iCursor = -1;
}else{
- pList = sqlite3SrcListEnlarge(db, pList, 1, pList->nSrc);
- }
- if( db->mallocFailed ){
- sqlite3SrcListDelete(db, pList);
- return 0;
+ SrcList *pNew = sqlite3SrcListEnlarge(pParse, pList, 1, pList->nSrc);
+ if( pNew==0 ){
+ sqlite3SrcListDelete(db, pList);
+ return 0;
+ }else{
+ pList = pNew;
+ }
}
pItem = &pList->a[pList->nSrc-1];
if( pDatabase && pDatabase->z==0 ){
@@ -109883,11 +125471,11 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(
*/
SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
int i;
- struct SrcList_item *pItem;
- assert(pList || pParse->db->mallocFailed );
- if( pList ){
+ SrcItem *pItem;
+ assert( pList || pParse->db->mallocFailed );
+ if( ALWAYS(pList) ){
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
- if( pItem->iCursor>=0 ) break;
+ if( pItem->iCursor>=0 ) continue;
pItem->iCursor = pParse->nTab++;
if( pItem->pSelect ){
sqlite3SrcListAssignCursors(pParse, pItem->pSelect->pSrc);
@@ -109901,20 +125489,24 @@ SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
*/
SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
int i;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
+ assert( db!=0 );
if( pList==0 ) return;
for(pItem=pList->a, i=0; i<pList->nSrc; i++, pItem++){
- sqlite3DbFree(db, pItem->zDatabase);
- sqlite3DbFree(db, pItem->zName);
- sqlite3DbFree(db, pItem->zAlias);
+ if( pItem->zDatabase ) sqlite3DbNNFreeNN(db, pItem->zDatabase);
+ if( pItem->zName ) sqlite3DbNNFreeNN(db, pItem->zName);
+ if( pItem->zAlias ) sqlite3DbNNFreeNN(db, pItem->zAlias);
if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy);
if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg);
sqlite3DeleteTable(db, pItem->pTab);
- sqlite3SelectDelete(db, pItem->pSelect);
- sqlite3ExprDelete(db, pItem->pOn);
- sqlite3IdListDelete(db, pItem->pUsing);
+ if( pItem->pSelect ) sqlite3SelectDelete(db, pItem->pSelect);
+ if( pItem->fg.isUsing ){
+ sqlite3IdListDelete(db, pItem->u3.pUsing);
+ }else if( pItem->u3.pOn ){
+ sqlite3ExprDelete(db, pItem->u3.pOn);
+ }
}
- sqlite3DbFreeNN(db, pList);
+ sqlite3DbNNFreeNN(db, pList);
}
/*
@@ -109940,18 +125532,17 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(
Token *pDatabase, /* Name of the database containing pTable */
Token *pAlias, /* The right-hand side of the AS subexpression */
Select *pSubquery, /* A subquery used in place of a table name */
- Expr *pOn, /* The ON clause of a join */
- IdList *pUsing /* The USING clause of a join */
+ OnOrUsing *pOnUsing /* Either the ON clause or the USING clause */
){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
sqlite3 *db = pParse->db;
- if( !p && (pOn || pUsing) ){
- sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s",
- (pOn ? "ON" : "USING")
+ if( !p && pOnUsing!=0 && (pOnUsing->pOn || pOnUsing->pUsing) ){
+ sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s",
+ (pOnUsing->pOn ? "ON" : "USING")
);
goto append_from_error;
}
- p = sqlite3SrcListAppend(db, p, pTable, pDatabase);
+ p = sqlite3SrcListAppend(pParse, p, pTable, pDatabase);
if( p==0 ){
goto append_from_error;
}
@@ -109967,41 +125558,75 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(
if( pAlias->n ){
pItem->zAlias = sqlite3NameFromToken(db, pAlias);
}
- pItem->pSelect = pSubquery;
- pItem->pOn = pOn;
- pItem->pUsing = pUsing;
+ if( pSubquery ){
+ pItem->pSelect = pSubquery;
+ if( pSubquery->selFlags & SF_NestedFrom ){
+ pItem->fg.isNestedFrom = 1;
+ }
+ }
+ assert( pOnUsing==0 || pOnUsing->pOn==0 || pOnUsing->pUsing==0 );
+ assert( pItem->fg.isUsing==0 );
+ if( pOnUsing==0 ){
+ pItem->u3.pOn = 0;
+ }else if( pOnUsing->pUsing ){
+ pItem->fg.isUsing = 1;
+ pItem->u3.pUsing = pOnUsing->pUsing;
+ }else{
+ pItem->u3.pOn = pOnUsing->pOn;
+ }
return p;
- append_from_error:
+append_from_error:
assert( p==0 );
- sqlite3ExprDelete(db, pOn);
- sqlite3IdListDelete(db, pUsing);
+ sqlite3ClearOnOrUsing(db, pOnUsing);
sqlite3SelectDelete(db, pSubquery);
return 0;
}
/*
-** Add an INDEXED BY or NOT INDEXED clause to the most recently added
+** Add an INDEXED BY or NOT INDEXED clause to the most recently added
** element of the source-list passed as the second argument.
*/
SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){
assert( pIndexedBy!=0 );
if( p && pIndexedBy->n>0 ){
- struct SrcList_item *pItem;
+ SrcItem *pItem;
assert( p->nSrc>0 );
pItem = &p->a[p->nSrc-1];
assert( pItem->fg.notIndexed==0 );
assert( pItem->fg.isIndexedBy==0 );
assert( pItem->fg.isTabFunc==0 );
if( pIndexedBy->n==1 && !pIndexedBy->z ){
- /* A "NOT INDEXED" clause was supplied. See parse.y
+ /* A "NOT INDEXED" clause was supplied. See parse.y
** construct "indexed_opt" for details. */
pItem->fg.notIndexed = 1;
}else{
pItem->u1.zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy);
pItem->fg.isIndexedBy = 1;
+ assert( pItem->fg.isCte==0 ); /* No collision on union u2 */
+ }
+ }
+}
+
+/*
+** Append the contents of SrcList p2 to SrcList p1 and return the resulting
+** SrcList. Or, if an error occurs, return NULL. In all cases, p1 and p2
+** are deleted by this function.
+*/
+SQLITE_PRIVATE SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2){
+ assert( p1 && p1->nSrc==1 );
+ if( p2 ){
+ SrcList *pNew = sqlite3SrcListEnlarge(pParse, p1, p2->nSrc, 1);
+ if( pNew==0 ){
+ sqlite3SrcListDelete(pParse->db, p2);
+ }else{
+ p1 = pNew;
+ memcpy(&p1->a[1], p2->a, p2->nSrc*sizeof(SrcItem));
+ sqlite3DbFree(pParse->db, p2);
+ p1->a[0].fg.jointype |= (JT_LTORJ & p1->a[1].fg.jointype);
}
}
+ return p1;
}
/*
@@ -110010,7 +125635,7 @@ SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pI
*/
SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){
if( p ){
- struct SrcList_item *pItem = &p->a[p->nSrc-1];
+ SrcItem *pItem = &p->a[p->nSrc-1];
assert( pItem->fg.notIndexed==0 );
assert( pItem->fg.isIndexedBy==0 );
assert( pItem->fg.isTabFunc==0 );
@@ -110035,14 +125660,34 @@ SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *
** The operator is "natural cross join". The A and B operands are stored
** in p->a[0] and p->a[1], respectively. The parser initially stores the
** operator with A. This routine shifts that operator over to B.
+**
+** Additional changes:
+**
+** * All tables to the left of the right-most RIGHT JOIN are tagged with
+** JT_LTORJ (mnemonic: Left Table Of Right Join) so that the
+** code generator can easily tell that the table is part of
+** the left operand of at least one RIGHT JOIN.
*/
-SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList *p){
- if( p ){
- int i;
- for(i=p->nSrc-1; i>0; i--){
- p->a[i].fg.jointype = p->a[i-1].fg.jointype;
- }
+SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse *pParse, SrcList *p){
+ (void)pParse;
+ if( p && p->nSrc>1 ){
+ int i = p->nSrc-1;
+ u8 allFlags = 0;
+ do{
+ allFlags |= p->a[i].fg.jointype = p->a[i-1].fg.jointype;
+ }while( (--i)>0 );
p->a[0].fg.jointype = 0;
+
+ /* All terms to the left of a RIGHT JOIN should be tagged with the
+ ** JT_LTORJ flags */
+ if( allFlags & JT_RIGHT ){
+ for(i=p->nSrc-1; ALWAYS(i>0) && (p->a[i].fg.jointype&JT_RIGHT)==0; i--){}
+ i--;
+ assert( i>=0 );
+ do{
+ p->a[i].fg.jointype |= JT_LTORJ;
+ }while( (--i)>=0 );
+ }
}
}
@@ -110064,7 +125709,16 @@ SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){
if( !v ) return;
if( type!=TK_DEFERRED ){
for(i=0; i<db->nDb; i++){
- sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
+ int eTxnType;
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt && sqlite3BtreeIsReadonly(pBt) ){
+ eTxnType = 0; /* Read txn */
+ }else if( type==TK_EXCLUSIVE ){
+ eTxnType = 2; /* Exclusive txn */
+ }else{
+ eTxnType = 1; /* Write txn */
+ }
+ sqlite3VdbeAddOp2(v, OP_Transaction, i, eTxnType);
sqlite3VdbeUsesBtree(v, i);
}
}
@@ -110084,7 +125738,7 @@ SQLITE_PRIVATE void sqlite3EndTransaction(Parse *pParse, int eType){
assert( pParse->db!=0 );
assert( eType==TK_COMMIT || eType==TK_END || eType==TK_ROLLBACK );
isRollback = eType==TK_ROLLBACK;
- if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION,
+ if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION,
isRollback ? "ROLLBACK" : "COMMIT", 0, 0) ){
return;
}
@@ -110096,7 +125750,7 @@ SQLITE_PRIVATE void sqlite3EndTransaction(Parse *pParse, int eType){
/*
** This function is called by the parser when it parses a command to create,
-** release or rollback an SQL savepoint.
+** release or rollback an SQL savepoint.
*/
SQLITE_PRIVATE void sqlite3Savepoint(Parse *pParse, int op, Token *pName){
char *zName = sqlite3NameFromToken(pParse->db, pName);
@@ -110123,7 +125777,7 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){
if( db->aDb[1].pBt==0 && !pParse->explain ){
int rc;
Btree *pBt;
- static const int flags =
+ static const int flags =
SQLITE_OPEN_READWRITE |
SQLITE_OPEN_CREATE |
SQLITE_OPEN_EXCLUSIVE |
@@ -110139,7 +125793,7 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){
}
db->aDb[1].pBt = pBt;
assert( db->aDb[1].pSchema );
- if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
+ if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, 0, 0) ){
sqlite3OomFault(db);
return 1;
}
@@ -110153,13 +125807,11 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){
** will occur at the end of the top-level VDBE and will be generated
** later, by sqlite3FinishCoding().
*/
-SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
- Parse *pToplevel = sqlite3ParseToplevel(pParse);
-
- assert( iDb>=0 && iDb<pParse->db->nDb );
- assert( pParse->db->aDb[iDb].pBt!=0 || iDb==1 );
- assert( iDb<SQLITE_MAX_ATTACHED+2 );
- assert( sqlite3SchemaMutexHeld(pParse->db, iDb, 0) );
+static void sqlite3CodeVerifySchemaAtToplevel(Parse *pToplevel, int iDb){
+ assert( iDb>=0 && iDb<pToplevel->db->nDb );
+ assert( pToplevel->db->aDb[iDb].pBt!=0 || iDb==1 );
+ assert( iDb<SQLITE_MAX_DB );
+ assert( sqlite3SchemaMutexHeld(pToplevel->db, iDb, 0) );
if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){
DbMaskSet(pToplevel->cookieMask, iDb);
if( !OMIT_TEMPDB && iDb==1 ){
@@ -110167,9 +125819,13 @@ SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
}
}
}
+SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
+ sqlite3CodeVerifySchemaAtToplevel(sqlite3ParseToplevel(pParse), iDb);
+}
+
/*
-** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each
+** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each
** attached database. Otherwise, invoke it for the database named zDb only.
*/
SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb){
@@ -110198,7 +125854,7 @@ SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb)
*/
SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
- sqlite3CodeVerifySchema(pParse, iDb);
+ sqlite3CodeVerifySchemaAtToplevel(pToplevel, iDb);
DbMaskSet(pToplevel->writeMask, iDb);
pToplevel->isMultiWrite |= setStatement;
}
@@ -110215,9 +125871,9 @@ SQLITE_PRIVATE void sqlite3MultiWrite(Parse *pParse){
pToplevel->isMultiWrite = 1;
}
-/*
+/*
** The code generator calls this routine if is discovers that it is
-** possible to abort a statement prior to completion. In order to
+** possible to abort a statement prior to completion. In order to
** perform this abort without corrupting the database, we need to make
** sure that the statement is protected by a statement transaction.
**
@@ -110226,7 +125882,7 @@ SQLITE_PRIVATE void sqlite3MultiWrite(Parse *pParse){
** such that the abort must occur after the multiwrite. This makes
** some statements involving the REPLACE conflict resolution algorithm
** go a little faster. But taking advantage of this time dependency
-** makes it more difficult to prove that the code is correct (in
+** makes it more difficult to prove that the code is correct (in
** particular, it prevents us from writing an effective
** implementation of sqlite3AssertMayAbort()) and so we have chosen
** to take the safe route and skip the optimization.
@@ -110249,8 +125905,10 @@ SQLITE_PRIVATE void sqlite3HaltConstraint(
i8 p4type, /* P4_STATIC or P4_TRANSIENT */
u8 p5Errmsg /* P5_ErrMsg type */
){
- Vdbe *v = sqlite3GetVdbe(pParse);
- assert( (errCode&0xff)==SQLITE_CONSTRAINT );
+ Vdbe *v;
+ assert( pParse->pVdbe!=0 );
+ v = sqlite3GetVdbe(pParse);
+ assert( (errCode&0xff)==SQLITE_CONSTRAINT || pParse->nested );
if( onError==OE_Abort ){
sqlite3MayAbort(pParse);
}
@@ -110271,14 +125929,15 @@ SQLITE_PRIVATE void sqlite3UniqueConstraint(
StrAccum errMsg;
Table *pTab = pIdx->pTable;
- sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200);
+ sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0,
+ pParse->db->aLimit[SQLITE_LIMIT_LENGTH]);
if( pIdx->aColExpr ){
sqlite3_str_appendf(&errMsg, "index '%q'", pIdx->zName);
}else{
for(j=0; j<pIdx->nKeyCol; j++){
char *zCol;
assert( pIdx->aiColumn[j]>=0 );
- zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
+ zCol = pTab->aCol[pIdx->aiColumn[j]].zCnName;
if( j ) sqlite3_str_append(&errMsg, ", ", 2);
sqlite3_str_appendall(&errMsg, pTab->zName);
sqlite3_str_append(&errMsg, ".", 1);
@@ -110286,8 +125945,8 @@ SQLITE_PRIVATE void sqlite3UniqueConstraint(
}
}
zErr = sqlite3StrAccumFinish(&errMsg);
- sqlite3HaltConstraint(pParse,
- IsPrimaryKeyIndex(pIdx) ? SQLITE_CONSTRAINT_PRIMARYKEY
+ sqlite3HaltConstraint(pParse,
+ IsPrimaryKeyIndex(pIdx) ? SQLITE_CONSTRAINT_PRIMARYKEY
: SQLITE_CONSTRAINT_UNIQUE,
onError, zErr, P4_DYNAMIC, P5_ConstraintUnique);
}
@@ -110299,13 +125958,13 @@ SQLITE_PRIVATE void sqlite3UniqueConstraint(
SQLITE_PRIVATE void sqlite3RowidConstraint(
Parse *pParse, /* Parsing context */
int onError, /* Conflict resolution algorithm */
- Table *pTab /* The table with the non-unique rowid */
+ Table *pTab /* The table with the non-unique rowid */
){
char *zMsg;
int rc;
if( pTab->iPKey>=0 ){
zMsg = sqlite3MPrintf(pParse->db, "%s.%s", pTab->zName,
- pTab->aCol[pTab->iPKey].zName);
+ pTab->aCol[pTab->iPKey].zCnName);
rc = SQLITE_CONSTRAINT_PRIMARYKEY;
}else{
zMsg = sqlite3MPrintf(pParse->db, "%s.rowid", pTab->zName);
@@ -110340,13 +125999,15 @@ static int collationMatch(const char *zColl, Index *pIndex){
*/
#ifndef SQLITE_OMIT_REINDEX
static void reindexTable(Parse *pParse, Table *pTab, char const *zColl){
- Index *pIndex; /* An index associated with pTab */
+ if( !IsVirtual(pTab) ){
+ Index *pIndex; /* An index associated with pTab */
- for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){
- if( zColl==0 || collationMatch(zColl, pIndex) ){
- int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
- sqlite3BeginWriteOperation(pParse, 0, iDb);
- sqlite3RefillIndex(pParse, pIndex, -1);
+ for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){
+ if( zColl==0 || collationMatch(zColl, pIndex) ){
+ int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+ sqlite3BeginWriteOperation(pParse, 0, iDb);
+ sqlite3RefillIndex(pParse, pIndex, -1);
+ }
}
}
}
@@ -110426,7 +126087,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
if( iDb<0 ) return;
z = sqlite3NameFromToken(db, pObjName);
if( z==0 ) return;
- zDb = db->aDb[iDb].zDbSName;
+ zDb = pName2->n ? db->aDb[iDb].zDbSName : 0;
pTab = sqlite3FindTable(db, z, zDb);
if( pTab ){
reindexTable(pParse, pTab, 0);
@@ -110436,6 +126097,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
pIndex = sqlite3FindIndex(db, z, zDb);
sqlite3DbFree(db, z);
if( pIndex ){
+ iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema);
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3RefillIndex(pParse, pIndex, -1);
return;
@@ -110467,7 +126129,8 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
const char *zColl = pIdx->azColl[i];
pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 :
sqlite3LocateCollSeq(pParse, zColl);
- pKey->aSortOrder[i] = pIdx->aSortOrder[i];
+ pKey->aSortFlags[i] = pIdx->aSortOrder[i];
+ assert( 0==(pKey->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) );
}
if( pParse->nErr ){
assert( pParse->rc==SQLITE_ERROR_MISSING_COLLSEQ );
@@ -110490,24 +126153,76 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
}
#ifndef SQLITE_OMIT_CTE
-/*
-** This routine is invoked once per CTE by the parser while parsing a
-** WITH clause.
+/*
+** Create a new CTE object
*/
-SQLITE_PRIVATE With *sqlite3WithAdd(
+SQLITE_PRIVATE Cte *sqlite3CteNew(
Parse *pParse, /* Parsing context */
- With *pWith, /* Existing WITH clause, or NULL */
Token *pName, /* Name of the common-table */
ExprList *pArglist, /* Optional column name list for the table */
- Select *pQuery /* Query used to initialize the table */
+ Select *pQuery, /* Query used to initialize the table */
+ u8 eM10d /* The MATERIALIZED flag */
+){
+ Cte *pNew;
+ sqlite3 *db = pParse->db;
+
+ pNew = sqlite3DbMallocZero(db, sizeof(*pNew));
+ assert( pNew!=0 || db->mallocFailed );
+
+ if( db->mallocFailed ){
+ sqlite3ExprListDelete(db, pArglist);
+ sqlite3SelectDelete(db, pQuery);
+ }else{
+ pNew->pSelect = pQuery;
+ pNew->pCols = pArglist;
+ pNew->zName = sqlite3NameFromToken(pParse->db, pName);
+ pNew->eM10d = eM10d;
+ }
+ return pNew;
+}
+
+/*
+** Clear information from a Cte object, but do not deallocate storage
+** for the object itself.
+*/
+static void cteClear(sqlite3 *db, Cte *pCte){
+ assert( pCte!=0 );
+ sqlite3ExprListDelete(db, pCte->pCols);
+ sqlite3SelectDelete(db, pCte->pSelect);
+ sqlite3DbFree(db, pCte->zName);
+}
+
+/*
+** Free the contents of the CTE object passed as the second argument.
+*/
+SQLITE_PRIVATE void sqlite3CteDelete(sqlite3 *db, Cte *pCte){
+ assert( pCte!=0 );
+ cteClear(db, pCte);
+ sqlite3DbFree(db, pCte);
+}
+
+/*
+** This routine is invoked once per CTE by the parser while parsing a
+** WITH clause. The CTE described by the third argument is added to
+** the WITH clause of the second argument. If the second argument is
+** NULL, then a new WITH argument is created.
+*/
+SQLITE_PRIVATE With *sqlite3WithAdd(
+ Parse *pParse, /* Parsing context */
+ With *pWith, /* Existing WITH clause, or NULL */
+ Cte *pCte /* CTE to add to the WITH clause */
){
sqlite3 *db = pParse->db;
With *pNew;
char *zName;
+ if( pCte==0 ){
+ return pWith;
+ }
+
/* Check that the CTE name is unique within this WITH clause. If
** not, store an error in the Parse structure. */
- zName = sqlite3NameFromToken(pParse->db, pName);
+ zName = pCte->zName;
if( zName && pWith ){
int i;
for(i=0; i<pWith->nCte; i++){
@@ -110518,7 +126233,7 @@ SQLITE_PRIVATE With *sqlite3WithAdd(
}
if( pWith ){
- int nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte);
+ sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte);
pNew = sqlite3DbRealloc(db, pWith, nByte);
}else{
pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
@@ -110526,16 +126241,11 @@ SQLITE_PRIVATE With *sqlite3WithAdd(
assert( (pNew!=0 && zName!=0) || db->mallocFailed );
if( db->mallocFailed ){
- sqlite3ExprListDelete(db, pArglist);
- sqlite3SelectDelete(db, pQuery);
- sqlite3DbFree(db, zName);
+ sqlite3CteDelete(db, pCte);
pNew = pWith;
}else{
- pNew->a[pNew->nCte].pSelect = pQuery;
- pNew->a[pNew->nCte].pCols = pArglist;
- pNew->a[pNew->nCte].zName = zName;
- pNew->a[pNew->nCte].zCteErr = 0;
- pNew->nCte++;
+ pNew->a[pNew->nCte++] = *pCte;
+ sqlite3DbFree(db, pCte);
}
return pNew;
@@ -110548,20 +126258,20 @@ SQLITE_PRIVATE void sqlite3WithDelete(sqlite3 *db, With *pWith){
if( pWith ){
int i;
for(i=0; i<pWith->nCte; i++){
- struct Cte *pCte = &pWith->a[i];
- sqlite3ExprListDelete(db, pCte->pCols);
- sqlite3SelectDelete(db, pCte->pSelect);
- sqlite3DbFree(db, pCte->zName);
+ cteClear(db, &pWith->a[i]);
}
sqlite3DbFree(db, pWith);
}
}
+SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){
+ sqlite3WithDelete(db, (With*)pWith);
+}
#endif /* !defined(SQLITE_OMIT_CTE) */
/************** End of build.c ***********************************************/
/************** Begin file callback.c ****************************************/
/*
-** 2005 May 23
+** 2005 May 23
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -110628,58 +126338,13 @@ static int synthCollSeq(sqlite3 *db, CollSeq *pColl){
}
/*
-** This function is responsible for invoking the collation factory callback
-** or substituting a collation sequence of a different encoding when the
-** requested collation sequence is not available in the desired encoding.
-**
-** If it is not NULL, then pColl must point to the database native encoding
-** collation sequence with name zName, length nName.
-**
-** The return value is either the collation sequence to be used in database
-** db for collation type name zName, length nName, or NULL, if no collation
-** sequence can be found. If no collation is found, leave an error message.
-**
-** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq()
-*/
-SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(
- Parse *pParse, /* Parsing context */
- u8 enc, /* The desired encoding for the collating sequence */
- CollSeq *pColl, /* Collating sequence with native encoding, or NULL */
- const char *zName /* Collating sequence name */
-){
- CollSeq *p;
- sqlite3 *db = pParse->db;
-
- p = pColl;
- if( !p ){
- p = sqlite3FindCollSeq(db, enc, zName, 0);
- }
- if( !p || !p->xCmp ){
- /* No collation sequence of this type for this encoding is registered.
- ** Call the collation factory to see if it can supply us with one.
- */
- callCollNeeded(db, enc, zName);
- p = sqlite3FindCollSeq(db, enc, zName, 0);
- }
- if( p && !p->xCmp && synthCollSeq(db, p) ){
- p = 0;
- }
- assert( !p || p->xCmp );
- if( p==0 ){
- sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
- pParse->rc = SQLITE_ERROR_MISSING_COLLSEQ;
- }
- return p;
-}
-
-/*
** This routine is called on a collation sequence before it is used to
** check that it is defined. An undefined collation sequence exists when
** a database is loaded that contains references to collation sequences
** that have not been defined by sqlite3_create_collation() etc.
**
** If required, this routine calls the 'collation needed' callback to
-** request a definition of the collating sequence. If this doesn't work,
+** request a definition of the collating sequence. If this doesn't work,
** an equivalent collating sequence that uses a text encoding different
** from the main database is substituted, if one is available.
*/
@@ -110733,7 +126398,7 @@ static CollSeq *findCollSeqEntry(
memcpy(pColl[0].zName, zName, nName);
pDel = sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, pColl);
- /* If a malloc() failure occurred in sqlite3HashInsert(), it will
+ /* If a malloc() failure occurred in sqlite3HashInsert(), it will
** return the pColl pointer to be deleted (because it wasn't added
** to the hash table).
*/
@@ -110764,20 +126429,113 @@ static CollSeq *findCollSeqEntry(
** See also: sqlite3LocateCollSeq(), sqlite3GetCollSeq()
*/
SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(
- sqlite3 *db,
- u8 enc,
- const char *zName,
- int create
+ sqlite3 *db, /* Database connection to search */
+ u8 enc, /* Desired text encoding */
+ const char *zName, /* Name of the collating sequence. Might be NULL */
+ int create /* True to create CollSeq if doesn't already exist */
){
CollSeq *pColl;
+ assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
+ assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE );
if( zName ){
pColl = findCollSeqEntry(db, zName, create);
+ if( pColl ) pColl += enc-1;
}else{
pColl = db->pDfltColl;
}
- assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
- assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE );
- if( pColl ) pColl += enc-1;
+ return pColl;
+}
+
+/*
+** Change the text encoding for a database connection. This means that
+** the pDfltColl must change as well.
+*/
+SQLITE_PRIVATE void sqlite3SetTextEncoding(sqlite3 *db, u8 enc){
+ assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
+ db->enc = enc;
+ /* EVIDENCE-OF: R-08308-17224 The default collating function for all
+ ** strings is BINARY.
+ */
+ db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0);
+ sqlite3ExpirePreparedStatements(db, 1);
+}
+
+/*
+** This function is responsible for invoking the collation factory callback
+** or substituting a collation sequence of a different encoding when the
+** requested collation sequence is not available in the desired encoding.
+**
+** If it is not NULL, then pColl must point to the database native encoding
+** collation sequence with name zName, length nName.
+**
+** The return value is either the collation sequence to be used in database
+** db for collation type name zName, length nName, or NULL, if no collation
+** sequence can be found. If no collation is found, leave an error message.
+**
+** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq()
+*/
+SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(
+ Parse *pParse, /* Parsing context */
+ u8 enc, /* The desired encoding for the collating sequence */
+ CollSeq *pColl, /* Collating sequence with native encoding, or NULL */
+ const char *zName /* Collating sequence name */
+){
+ CollSeq *p;
+ sqlite3 *db = pParse->db;
+
+ p = pColl;
+ if( !p ){
+ p = sqlite3FindCollSeq(db, enc, zName, 0);
+ }
+ if( !p || !p->xCmp ){
+ /* No collation sequence of this type for this encoding is registered.
+ ** Call the collation factory to see if it can supply us with one.
+ */
+ callCollNeeded(db, enc, zName);
+ p = sqlite3FindCollSeq(db, enc, zName, 0);
+ }
+ if( p && !p->xCmp && synthCollSeq(db, p) ){
+ p = 0;
+ }
+ assert( !p || p->xCmp );
+ if( p==0 ){
+ sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
+ pParse->rc = SQLITE_ERROR_MISSING_COLLSEQ;
+ }
+ return p;
+}
+
+/*
+** This function returns the collation sequence for database native text
+** encoding identified by the string zName.
+**
+** If the requested collation sequence is not available, or not available
+** in the database native encoding, the collation factory is invoked to
+** request it. If the collation factory does not supply such a sequence,
+** and the sequence is available in another text encoding, then that is
+** returned instead.
+**
+** If no versions of the requested collations sequence are available, or
+** another error occurs, NULL is returned and an error message written into
+** pParse.
+**
+** This routine is a wrapper around sqlite3FindCollSeq(). This routine
+** invokes the collation factory if the named collation cannot be found
+** and generates an error message.
+**
+** See also: sqlite3FindCollSeq(), sqlite3GetCollSeq()
+*/
+SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
+ sqlite3 *db = pParse->db;
+ u8 enc = ENC(db);
+ u8 initbusy = db->init.busy;
+ CollSeq *pColl;
+
+ pColl = sqlite3FindCollSeq(db, enc, zName, initbusy);
+ if( !initbusy && (!pColl || !pColl->xCmp) ){
+ pColl = sqlite3GetCollSeq(pParse, enc, pColl, zName);
+ }
+
return pColl;
}
@@ -110791,7 +126549,7 @@ SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(
** is also -1. In other words, we are searching for a function that
** takes a variable number of arguments.
**
-** If nArg is -2 that means that we are searching for any function
+** If nArg is -2 that means that we are searching for any function
** regardless of the number of arguments it uses, so return a positive
** match score for any
**
@@ -110816,12 +126574,13 @@ static int matchQuality(
u8 enc /* Desired text encoding */
){
int match;
-
- /* nArg of -2 is a special case */
- if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH;
+ assert( p->nArg>=-1 );
/* Wrong number of arguments means "no match" */
- if( p->nArg!=nArg && p->nArg>=0 ) return 0;
+ if( p->nArg!=nArg ){
+ if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH;
+ if( p->nArg>=0 ) return 0;
+ }
/* Give a better score to a function with a specific number of arguments
** than to function that accepts any number of arguments. */
@@ -110845,33 +126604,19 @@ static int matchQuality(
** Search a FuncDefHash for a function with the given name. Return
** a pointer to the matching FuncDef if found, or 0 if there is no match.
*/
-static FuncDef *functionSearch(
+SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(
int h, /* Hash of the name */
const char *zFunc /* Name of function */
){
FuncDef *p;
for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){
+ assert( p->funcFlags & SQLITE_FUNC_BUILTIN );
if( sqlite3StrICmp(p->zName, zFunc)==0 ){
return p;
}
}
return 0;
}
-#ifdef SQLITE_ENABLE_NORMALIZE
-SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN(
- int h, /* Hash of the name */
- const char *zFunc, /* Name of function */
- int nFunc /* Length of the name */
-){
- FuncDef *p;
- for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){
- if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 ){
- return p;
- }
- }
- return 0;
-}
-#endif /* SQLITE_ENABLE_NORMALIZE */
/*
** Insert a new FuncDef into a FuncDefHash hash table.
@@ -110886,8 +126631,8 @@ SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(
const char *zName = aDef[i].zName;
int nName = sqlite3Strlen30(zName);
int h = SQLITE_FUNC_HASH(zName[0], nName);
- assert( zName[0]>='a' && zName[0]<='z' );
- pOther = functionSearch(h, zName);
+ assert( aDef[i].funcFlags & SQLITE_FUNC_BUILTIN );
+ pOther = sqlite3FunctionSearch(h, zName);
if( pOther ){
assert( pOther!=&aDef[i] && pOther->pNext!=&aDef[i] );
aDef[i].pNext = pOther->pNext;
@@ -110899,8 +126644,8 @@ SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(
}
}
}
-
-
+
+
/*
** Locate a user function given a name, a number of arguments and a flag
@@ -110961,11 +126706,11 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
** have fields overwritten with new information appropriate for the
** new function. But the FuncDefs for built-in functions are read-only.
** So we must not search for built-ins when creating a new function.
- */
+ */
if( !createFlag && (pBest==0 || (db->mDbFlags & DBFLAG_PreferBuiltin)!=0) ){
bestScore = 0;
h = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zName[0]], nName);
- p = functionSearch(h, zName);
+ p = sqlite3FunctionSearch(h, zName);
while( p ){
int score = matchQuality(p, nArg, enc);
if( score>bestScore ){
@@ -110980,7 +126725,7 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
** exact match for the name, number of arguments and encoding, then add a
** new entry to the hash table and return it.
*/
- if( createFlag && bestScore<FUNC_PERFECT_MATCH &&
+ if( createFlag && bestScore<FUNC_PERFECT_MATCH &&
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
FuncDef *pOther;
u8 *z;
@@ -111007,7 +126752,7 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
/*
** Free all resources held by the schema structure. The void* argument points
-** at a Schema struct. This function does not call sqlite3DbFree(db, ) on the
+** at a Schema struct. This function does not call sqlite3DbFree(db, ) on the
** pointer itself, it just cleans up subsidiary resources (i.e. the contents
** of the schema hash tables).
**
@@ -111018,19 +126763,21 @@ SQLITE_PRIVATE void sqlite3SchemaClear(void *p){
Hash temp2;
HashElem *pElem;
Schema *pSchema = (Schema *)p;
+ sqlite3 xdb;
+ memset(&xdb, 0, sizeof(xdb));
temp1 = pSchema->tblHash;
temp2 = pSchema->trigHash;
sqlite3HashInit(&pSchema->trigHash);
sqlite3HashClear(&pSchema->idxHash);
for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
- sqlite3DeleteTrigger(0, (Trigger*)sqliteHashData(pElem));
+ sqlite3DeleteTrigger(&xdb, (Trigger*)sqliteHashData(pElem));
}
sqlite3HashClear(&temp2);
sqlite3HashInit(&pSchema->tblHash);
for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
Table *pTab = sqliteHashData(pElem);
- sqlite3DeleteTable(0, pTab);
+ sqlite3DeleteTable(&xdb, pTab);
}
sqlite3HashClear(&temp1);
sqlite3HashClear(&pSchema->fkeyHash);
@@ -111087,7 +126834,7 @@ SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
** (as in the FROM clause of a SELECT statement) in this case it contains
** the name of a single table, as one might find in an INSERT, DELETE,
** or UPDATE statement. Look up that table in the symbol table and
-** return a pointer. Set an error message and return NULL if the table
+** return a pointer. Set an error message and return NULL if the table
** name is not found or if any other error occurs.
**
** The following fields are initialized appropriate in pSrc:
@@ -111097,21 +126844,32 @@ SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
**
*/
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
- struct SrcList_item *pItem = pSrc->a;
+ SrcItem *pItem = pSrc->a;
Table *pTab;
- assert( pItem && pSrc->nSrc==1 );
+ assert( pItem && pSrc->nSrc>=1 );
pTab = sqlite3LocateTableItem(pParse, 0, pItem);
- sqlite3DeleteTable(pParse->db, pItem->pTab);
+ if( pItem->pTab ) sqlite3DeleteTable(pParse->db, pItem->pTab);
pItem->pTab = pTab;
+ pItem->fg.notCte = 1;
if( pTab ){
pTab->nTabRef++;
- }
- if( sqlite3IndexedByLookup(pParse, pItem) ){
- pTab = 0;
+ if( pItem->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pItem) ){
+ pTab = 0;
+ }
}
return pTab;
}
+/* Generate byte-code that will report the number of rows modified
+** by a DELETE, INSERT, or UPDATE statement.
+*/
+SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe *v, int regCounter, const char *zColName){
+ sqlite3VdbeAddOp0(v, OP_FkCheck);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, regCounter, 1);
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zColName, SQLITE_STATIC);
+}
+
/* Return true if table pTab is read-only.
**
** A table is read-only if any of the following are true:
@@ -111119,18 +126877,42 @@ SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
** 1) It is a virtual table and no implementation of the xUpdate method
** has been provided
**
-** 2) It is a system table (i.e. sqlite_master), this call is not
-** part of a nested parse and writable_schema pragma has not
+** 2) A trigger is currently being coded and the table is a virtual table
+** that is SQLITE_VTAB_DIRECTONLY or if PRAGMA trusted_schema=OFF and
+** the table is not SQLITE_VTAB_INNOCUOUS.
+**
+** 3) It is a system table (i.e. sqlite_schema), this call is not
+** part of a nested parse and writable_schema pragma has not
** been specified
**
-** 3) The table is a shadow table, the database connection is in
+** 4) The table is a shadow table, the database connection is in
** defensive mode, and the current sqlite3_prepare()
** is for a top-level SQL statement.
*/
+static int vtabIsReadOnly(Parse *pParse, Table *pTab){
+ if( sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ){
+ return 1;
+ }
+
+ /* Within triggers:
+ ** * Do not allow DELETE, INSERT, or UPDATE of SQLITE_VTAB_DIRECTONLY
+ ** virtual tables
+ ** * Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS
+ ** virtual tables if PRAGMA trusted_schema=ON.
+ */
+ if( pParse->pToplevel!=0
+ && pTab->u.vtab.p->eVtabRisk >
+ ((pParse->db->flags & SQLITE_TrustedSchema)!=0)
+ ){
+ sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"",
+ pTab->zName);
+ }
+ return 0;
+}
static int tabIsReadOnly(Parse *pParse, Table *pTab){
sqlite3 *db;
if( IsVirtual(pTab) ){
- return sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0;
+ return vtabIsReadOnly(pParse, pTab);
}
if( (pTab->tabFlags & (TF_Readonly|TF_Shadow))==0 ) return 0;
db = pParse->db;
@@ -111138,25 +126920,25 @@ static int tabIsReadOnly(Parse *pParse, Table *pTab){
return sqlite3WritableSchema(db)==0 && pParse->nested==0;
}
assert( pTab->tabFlags & TF_Shadow );
- return (db->flags & SQLITE_Defensive)!=0
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- && db->pVtabCtx==0
-#endif
- && db->nVdbeExec==0;
+ return sqlite3ReadOnlyShadowTables(db);
}
/*
-** Check to make sure the given table is writable. If it is not
-** writable, generate an error message and return 1. If it is
-** writable return 0;
+** Check to make sure the given table is writable.
+**
+** If pTab is not writable -> generate an error message and return 1.
+** If pTab is writable but other errors have occurred -> return 1.
+** If pTab is writable and no prior errors -> return 0;
*/
-SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
+SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, Trigger *pTrigger){
if( tabIsReadOnly(pParse, pTab) ){
sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName);
return 1;
}
#ifndef SQLITE_OMIT_VIEW
- if( !viewOk && pTab->pSelect ){
+ if( IsView(pTab)
+ && (pTrigger==0 || (pTrigger->bReturning && pTrigger->pNext==0))
+ ){
sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName);
return 1;
}
@@ -111185,15 +126967,15 @@ SQLITE_PRIVATE void sqlite3MaterializeView(
sqlite3 *db = pParse->db;
int iDb = sqlite3SchemaToIndex(db, pView->pSchema);
pWhere = sqlite3ExprDup(db, pWhere, 0);
- pFrom = sqlite3SrcListAppend(db, 0, 0, 0);
+ pFrom = sqlite3SrcListAppend(pParse, 0, 0, 0);
if( pFrom ){
assert( pFrom->nSrc==1 );
pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
- assert( pFrom->a[0].pOn==0 );
- assert( pFrom->a[0].pUsing==0 );
+ assert( pFrom->a[0].fg.isUsing==0 );
+ assert( pFrom->a[0].u3.pOn==0 );
}
- pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy,
+ pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy,
SF_IncludeHidden, pLimit);
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
sqlite3Select(pParse, pSel, &dest);
@@ -111221,7 +127003,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
sqlite3 *db = pParse->db;
Expr *pLhs = NULL; /* LHS of IN(SELECT...) operator */
Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */
- ExprList *pEList = NULL; /* Expression list contaning only pSelectRowid */
+ ExprList *pEList = NULL; /* Expression list containing only pSelectRowid*/
SrcList *pSelectSrc = NULL; /* SELECT rowid FROM x ... (dup of pSrc) */
Select *pSelect = NULL; /* Complete SELECT tree */
Table *pTab;
@@ -111242,11 +127024,11 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
return pWhere;
}
- /* Generate a select expression tree to enforce the limit/offset
+ /* Generate a select expression tree to enforce the limit/offset
** term for the DELETE or UPDATE statement. For example:
** DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
** becomes:
- ** DELETE FROM table_a WHERE rowid IN (
+ ** DELETE FROM table_a WHERE rowid IN (
** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1
** );
*/
@@ -111259,14 +127041,20 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
);
}else{
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pPk!=0 );
+ assert( pPk->nKeyCol>=1 );
if( pPk->nKeyCol==1 ){
- const char *zName = pTab->aCol[pPk->aiColumn[0]].zName;
+ const char *zName;
+ assert( pPk->aiColumn[0]>=0 && pPk->aiColumn[0]<pTab->nCol );
+ zName = pTab->aCol[pPk->aiColumn[0]].zCnName;
pLhs = sqlite3Expr(db, TK_ID, zName);
pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, zName));
}else{
int i;
for(i=0; i<pPk->nKeyCol; i++){
- Expr *p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zName);
+ Expr *p;
+ assert( pPk->aiColumn[i]>=0 && pPk->aiColumn[i]<pTab->nCol );
+ p = sqlite3Expr(db, TK_ID, pTab->aCol[pPk->aiColumn[i]].zCnName);
pEList = sqlite3ExprListAppend(pParse, pEList, p);
}
pLhs = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
@@ -111279,16 +127067,23 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
** and the SELECT subtree. */
pSrc->a[0].pTab = 0;
- pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
+ pSelectSrc = sqlite3SrcListDup(db, pSrc, 0);
pSrc->a[0].pTab = pTab;
- pSrc->a[0].pIBIndex = 0;
+ if( pSrc->a[0].fg.isIndexedBy ){
+ assert( pSrc->a[0].fg.isCte==0 );
+ pSrc->a[0].u2.pIBIndex = 0;
+ pSrc->a[0].fg.isIndexedBy = 0;
+ sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy);
+ }else if( pSrc->a[0].fg.isCte ){
+ pSrc->a[0].u2.pCteUse->nUse++;
+ }
/* generate the SELECT expression tree. */
- pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0,
+ pSelect = sqlite3SelectNew(pParse, pEList, pSelectSrc, pWhere, 0 ,0,
pOrderBy,0,pLimit
);
- /* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
+ /* now generate the new WHERE rowid IN clause for the DELETE/UPDATE */
pInClause = sqlite3PExpr(pParse, TK_IN, pLhs, 0);
sqlite3PExprAddSelect(pParse, pInClause, pSelect);
return pInClause;
@@ -111340,7 +127135,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
int addrEphOpen = 0; /* Instruction to open the Ephemeral table */
int bComplex; /* True if there are triggers or FKs or
** subqueries in the WHERE clause */
-
+
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to delete from a view */
Trigger *pTrigger; /* List of table triggers, if required */
@@ -111348,12 +127143,13 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
memset(&sContext, 0, sizeof(sContext));
db = pParse->db;
- if( pParse->nErr || db->mallocFailed ){
+ assert( db->pParse==pParse );
+ if( pParse->nErr ){
goto delete_from_cleanup;
}
+ assert( db->mallocFailed==0 );
assert( pTabList->nSrc==1 );
-
/* Locate the table which we want to delete. This table has to be
** put in an SrcList structure because some of the subroutines we
** will be calling are designed to work with multiple tables and expect
@@ -111367,7 +127163,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
*/
#ifndef SQLITE_OMIT_TRIGGER
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
- isView = pTab->pSelect!=0;
+ isView = IsView(pTab);
#else
# define pTrigger 0
# define isView 0
@@ -111378,6 +127174,14 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
# define isView 0
#endif
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x10000 ){
+ sqlite3TreeViewLine(0, "In sqlite3Delete() at %s:%d", __FILE__, __LINE__);
+ sqlite3TreeViewDelete(pParse->pWith, pTabList, pWhere,
+ pOrderBy, pLimit, pTrigger);
+ }
+#endif
+
#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
if( !isView ){
pWhere = sqlite3LimitWhere(
@@ -111394,12 +127198,12 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
goto delete_from_cleanup;
}
- if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){
+ if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){
goto delete_from_cleanup;
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb<db->nDb );
- rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0,
+ rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0,
db->aDb[iDb].zDbSName);
assert( rcauth==SQLITE_OK || rcauth==SQLITE_DENY || rcauth==SQLITE_IGNORE );
if( rcauth==SQLITE_DENY ){
@@ -111435,7 +127239,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
*/
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
if( isView ){
- sqlite3MaterializeView(pParse, pTab,
+ sqlite3MaterializeView(pParse, pTab,
pWhere, pOrderBy, pLimit, iTabCur
);
iDataCur = iIdxCur = iTabCur;
@@ -111459,6 +127263,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
if( (db->flags & SQLITE_CountRows)!=0
&& !pParse->nested
&& !pParse->pTriggerTab
+ && !pParse->bReturning
){
memCnt = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
@@ -111467,7 +127272,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
#ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION
/* Special case: A DELETE without a WHERE clause deletes everything.
** It is easier just to erase the whole table. Prior to version 3.6.5,
- ** this optimization caused the row change count (the value returned by
+ ** this optimization caused the row change count (the value returned by
** API function sqlite3_count_changes) to be set incorrectly.
**
** The "rcauth==SQLITE_OK" terms is the
@@ -111492,18 +127297,22 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
}
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
assert( pIdx->pSchema==pTab->pSchema );
- sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
+ if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
+ sqlite3VdbeAddOp3(v, OP_Clear, pIdx->tnum, iDb, memCnt ? memCnt : -1);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
+ }
}
}else
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
{
- u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK|WHERE_SEEK_TABLE;
- if( sNC.ncFlags & NC_VarSelect ) bComplex = 1;
+ u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK;
+ if( sNC.ncFlags & NC_Subquery ) bComplex = 1;
wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);
if( HasRowid(pTab) ){
/* For a rowid table, initialize the RowSet to an empty set */
pPk = 0;
- nPk = 1;
+ assert( nPk==1 );
iRowSet = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
}else{
@@ -111518,7 +127327,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
addrEphOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEphCur, nPk);
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
}
-
+
/* Construct a query to find the rowid or primary key for every row
** to be deleted, based on the WHERE clause. Set variable eOnePass
** to indicate the strategy used to implement this delete:
@@ -111527,18 +127336,22 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
** ONEPASS_SINGLE: One-pass approach - at most one row deleted.
** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted.
*/
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,0,wcf,iTabCur+1);
if( pWInfo==0 ) goto delete_from_cleanup;
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI );
- assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF );
+ assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF
+ || OptimizationDisabled(db, SQLITE_OnePass) );
if( eOnePass!=ONEPASS_SINGLE ) sqlite3MultiWrite(pParse);
-
+ if( sqlite3WhereUsesDeferredSeek(pWInfo) ){
+ sqlite3VdbeAddOp1(v, OP_FinishSeek, iTabCur);
+ }
+
/* Keep track of the number of rows to be deleted */
if( memCnt ){
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
}
-
+
/* Extract the rowid or primary key for the current row */
if( pPk ){
for(i=0; i<nPk; i++){
@@ -111551,7 +127364,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
iKey = ++pParse->nMem;
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, -1, iKey);
}
-
+
if( eOnePass!=ONEPASS_OFF ){
/* For ONEPASS, no need to store the rowid/primary-key. There is only
** one, so just keep it in its register(s) and fall through to the
@@ -111567,6 +127380,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;
if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);
+ addrBypass = sqlite3VdbeMakeLabel(pParse);
}else{
if( pPk ){
/* Add the PK key for this row to the temporary table */
@@ -111580,19 +127394,12 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
nKey = 1; /* OP_DeferredSeek always uses a single rowid */
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
}
- }
-
- /* If this DELETE cannot use the ONEPASS strategy, this is the
- ** end of the WHERE loop */
- if( eOnePass!=ONEPASS_OFF ){
- addrBypass = sqlite3VdbeMakeLabel(v);
- }else{
sqlite3WhereEnd(pWInfo);
}
-
- /* Unless this is a view, open cursors for the table we are
+
+ /* Unless this is a view, open cursors for the table we are
** deleting from and all its indices. If this is a view, then the
- ** only effect this statement has is to fire the INSTEAD OF
+ ** only effect this statement has is to fire the INSTEAD OF
** triggers.
*/
if( !isView ){
@@ -111605,16 +127412,18 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
iTabCur, aToOpen, &iDataCur, &iIdxCur);
assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
- if( eOnePass==ONEPASS_MULTI ) sqlite3VdbeJumpHere(v, iAddrOnce);
+ if( eOnePass==ONEPASS_MULTI ){
+ sqlite3VdbeJumpHereOrPopInst(v, iAddrOnce);
+ }
}
-
+
/* Set up a loop over the rowids/primary-keys that were found in the
** where-clause loop above.
*/
if( eOnePass!=ONEPASS_OFF ){
assert( nKey==nPk ); /* OP_Found will use an unpacked key */
if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){
- assert( pPk!=0 || pTab->pSelect!=0 );
+ assert( pPk!=0 || IsView(pTab) );
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
VdbeCoverage(v);
}
@@ -111630,8 +127439,8 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey);
VdbeCoverage(v);
assert( nKey==1 );
- }
-
+ }
+
/* Delete the row */
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pTab) ){
@@ -111654,7 +127463,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
iKey, nKey, count, OE_Default, eOnePass, aiCurOnePass[1]);
}
-
+
/* End of the loop over all rowids/primary-keys. */
if( eOnePass!=ONEPASS_OFF ){
sqlite3VdbeResolveLabel(v, addrBypass);
@@ -111665,7 +127474,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
}else{
sqlite3VdbeGoto(v, addrLoop);
sqlite3VdbeJumpHere(v, addrLoop);
- }
+ }
} /* End non-truncate path */
/* Update the sqlite_sequence table by storing the content of the
@@ -111676,25 +127485,23 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
sqlite3AutoincrementEnd(pParse);
}
- /* Return the number of rows that were deleted. If this routine is
+ /* Return the number of rows that were deleted. If this routine is
** generating code because of a call to sqlite3NestedParse(), do not
** invoke the callback function.
*/
if( memCnt ){
- sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
+ sqlite3CodeChangeCount(v, memCnt, "rows deleted");
}
delete_from_cleanup:
sqlite3AuthContextPop(&sContext);
sqlite3SrcListDelete(db, pTabList);
sqlite3ExprDelete(db, pWhere);
-#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
+#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
sqlite3ExprListDelete(db, pOrderBy);
sqlite3ExprDelete(db, pLimit);
#endif
- sqlite3DbFree(db, aToOpen);
+ if( aToOpen ) sqlite3DbNNFreeNN(db, aToOpen);
return;
}
/* Make sure "isView" and other macros defined above are undefined. Otherwise
@@ -111735,7 +127542,7 @@ delete_from_cleanup:
** and nPk before reading from it.
**
** If eMode is ONEPASS_MULTI, then this call is being made as part
-** of a ONEPASS delete that affects multiple rows. In this case, if
+** of a ONEPASS delete that affects multiple rows. In this case, if
** iIdxNoSeek is a valid cursor number (>=0) and is not the same as
** iDataCur, then its position should be preserved following the delete
** operation. Or, if iIdxNoSeek is not a valid cursor number, the
@@ -111771,17 +127578,17 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete(
VdbeModuleComment((v, "BEGIN: GenRowDel(%d,%d,%d,%d)",
iDataCur, iIdxCur, iPk, (int)nPk));
- /* Seek cursor iCur to the row to delete. If this row no longer exists
+ /* Seek cursor iCur to the row to delete. If this row no longer exists
** (this can happen if a trigger program has already deleted it), do
** not attempt to delete it or fire any DELETE triggers. */
- iLabel = sqlite3VdbeMakeLabel(v);
+ iLabel = sqlite3VdbeMakeLabel(pParse);
opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
if( eMode==ONEPASS_OFF ){
sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
VdbeCoverageIf(v, opSeek==OP_NotExists);
VdbeCoverageIf(v, opSeek==OP_NotFound);
}
-
+
/* If there are any triggers to fire, allocate a range of registers to
** use for the old.* references in the triggers. */
if( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){
@@ -111798,24 +127605,25 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete(
iOld = pParse->nMem+1;
pParse->nMem += (1 + pTab->nCol);
- /* Populate the OLD.* pseudo-table register array. These values will be
+ /* Populate the OLD.* pseudo-table register array. These values will be
** used by any BEFORE and AFTER triggers that exist. */
sqlite3VdbeAddOp2(v, OP_Copy, iPk, iOld);
for(iCol=0; iCol<pTab->nCol; iCol++){
testcase( mask!=0xffffffff && iCol==31 );
testcase( mask!=0xffffffff && iCol==32 );
if( mask==0xffffffff || (iCol<=31 && (mask & MASKBIT32(iCol))!=0) ){
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, iCol, iOld+iCol+1);
+ int kk = sqlite3TableColumnToStorage(pTab, iCol);
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, iCol, iOld+kk+1);
}
}
/* Invoke BEFORE DELETE trigger programs. */
addrStart = sqlite3VdbeCurrentAddr(v);
- sqlite3CodeRowTrigger(pParse, pTrigger,
+ sqlite3CodeRowTrigger(pParse, pTrigger,
TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel
);
- /* If any BEFORE triggers were coded, then seek the cursor to the
+ /* If any BEFORE triggers were coded, then seek the cursor to the
** row to be deleted again. It may be that the BEFORE triggers moved
** the cursor or already deleted the row that the cursor was
** pointing to.
@@ -111832,22 +127640,22 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete(
}
/* Do FK processing. This call checks that any FK constraints that
- ** refer to this table (i.e. constraints attached to other tables)
+ ** refer to this table (i.e. constraints attached to other tables)
** are not violated by deleting this row. */
sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0);
}
/* Delete the index and table entries. Skip this step if pTab is really
** a view (in which case the only effect of the DELETE statement is to
- ** fire the INSTEAD OF triggers).
+ ** fire the INSTEAD OF triggers).
**
** If variable 'count' is non-zero, then this OP_Delete instruction should
** invoke the update-hook. The pre-update-hook, on the other hand should
** be invoked unless table pTab is a system table. The difference is that
- ** the update-hook is not invoked for rows removed by REPLACE, but the
+ ** the update-hook is not invoked for rows removed by REPLACE, but the
** pre-update-hook is.
- */
- if( pTab->pSelect==0 ){
+ */
+ if( !IsView(pTab) ){
u8 p5 = 0;
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
@@ -111866,16 +127674,18 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete(
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
** handle rows (possibly in other tables) that refer via a foreign key
- ** to the row just deleted. */
+ ** to the row just deleted. */
sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0);
/* Invoke AFTER DELETE trigger programs. */
- sqlite3CodeRowTrigger(pParse, pTrigger,
- TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel
- );
+ if( pTrigger ){
+ sqlite3CodeRowTrigger(pParse, pTrigger,
+ TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel
+ );
+ }
/* Jump here if the row had already been deleted before any BEFORE
- ** trigger programs were invoked. Or if a trigger program throws a
+ ** trigger programs were invoked. Or if a trigger program throws a
** RAISE(IGNORE) exception. */
sqlite3VdbeResolveLabel(v, iLabel);
VdbeModuleComment((v, "END: GenRowDel()"));
@@ -111927,6 +127737,7 @@ SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(
&iPartIdxLabel, pPrior, r1);
sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
+ sqlite3VdbeChangeP5(v, 1); /* Cause IdxDelete to error if no entry found */
sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
pPrior = pIdx;
}
@@ -111959,7 +127770,7 @@ SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(
** its key into the same sequence of registers and if pPrior and pIdx share
** a column in common, then the register corresponding to that column already
** holds the correct value and the loading of that register is skipped.
-** This optimization is helpful when doing a DELETE or an INTEGRITY_CHECK
+** This optimization is helpful when doing a DELETE or an INTEGRITY_CHECK
** on a table with multiple indices, and especially with the ROWID or
** PRIMARY KEY columns of the index.
*/
@@ -111980,11 +127791,13 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey(
if( piPartIdxLabel ){
if( pIdx->pPartIdxWhere ){
- *piPartIdxLabel = sqlite3VdbeMakeLabel(v);
+ *piPartIdxLabel = sqlite3VdbeMakeLabel(pParse);
pParse->iSelfTab = iDataCur + 1;
- sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
+ sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
SQLITE_JUMPIFNULL);
pParse->iSelfTab = 0;
+ pPrior = 0; /* Ticket a9efb42811fa41ee 2019-11-02;
+ ** pPartIdxWhere may have corrupted regPrior registers */
}else{
*piPartIdxLabel = 0;
}
@@ -112001,20 +127814,18 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey(
continue;
}
sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j);
- /* If the column affinity is REAL but the number is an integer, then it
- ** might be stored in the table as an integer (using a compact
- ** representation) then converted to REAL by an OP_RealAffinity opcode.
- ** But we are getting ready to store this value back into an index, where
- ** it should be converted by to INTEGER again. So omit the OP_RealAffinity
- ** opcode if it is present */
- sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity);
+ if( pIdx->aiColumn[j]>=0 ){
+ /* If the column affinity is REAL but the number is an integer, then it
+ ** might be stored in the table as an integer (using a compact
+ ** representation) then converted to REAL by an OP_RealAffinity opcode.
+ ** But we are getting ready to store this value back into an index, where
+ ** it should be converted by to INTEGER again. So omit the
+ ** OP_RealAffinity opcode if it is present */
+ sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity);
+ }
}
if( regOut ){
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut);
- if( pIdx->pTable->pSelect ){
- const char *zAff = sqlite3IndexAffinityStr(pParse->db, pIdx);
- sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT);
- }
}
sqlite3ReleaseTempRange(pParse, regBase, nCol);
return regBase;
@@ -112051,6 +127862,9 @@ SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){
/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
/* #include <assert.h> */
+#ifndef SQLITE_OMIT_FLOATING_POINT
+/* #include <math.h> */
+#endif
/* #include "vdbeInt.h" */
/*
@@ -112129,6 +127943,18 @@ static void typeofFunc(
sqlite3_result_text(context, azType[i], -1, SQLITE_STATIC);
}
+/* subtype(X)
+**
+** Return the subtype of X
+*/
+static void subtypeFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ UNUSED_PARAMETER(argc);
+ sqlite3_result_int(context, sqlite3_value_subtype(argv[0]));
+}
/*
** Implementation of the length() function
@@ -112170,10 +127996,46 @@ static void lengthFunc(
}
/*
+** Implementation of the octet_length() function
+*/
+static void bytelengthFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( argc==1 );
+ UNUSED_PARAMETER(argc);
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_BLOB: {
+ sqlite3_result_int(context, sqlite3_value_bytes(argv[0]));
+ break;
+ }
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT: {
+ i64 m = sqlite3_context_db_handle(context)->enc<=SQLITE_UTF8 ? 1 : 2;
+ sqlite3_result_int64(context, sqlite3_value_bytes(argv[0])*m);
+ break;
+ }
+ case SQLITE_TEXT: {
+ if( sqlite3_value_encoding(argv[0])<=SQLITE_UTF8 ){
+ sqlite3_result_int(context, sqlite3_value_bytes(argv[0]));
+ }else{
+ sqlite3_result_int(context, sqlite3_value_bytes16(argv[0]));
+ }
+ break;
+ }
+ default: {
+ sqlite3_result_null(context);
+ break;
+ }
+ }
+}
+
+/*
** Implementation of the abs() function.
**
** IMP: R-23979-26855 The abs(X) function returns the absolute value of
-** the numeric argument X.
+** the numeric argument X.
*/
static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
assert( argc==1 );
@@ -112190,7 +128052,7 @@ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
return;
}
iVal = -iVal;
- }
+ }
sqlite3_result_int64(context, iVal);
break;
}
@@ -112236,6 +128098,9 @@ static void instrFunc(
int typeHaystack, typeNeedle;
int N = 1;
int isText;
+ unsigned char firstChar;
+ sqlite3_value *pC1 = 0;
+ sqlite3_value *pC2 = 0;
UNUSED_PARAMETER(argc);
typeHaystack = sqlite3_value_type(argv[0]);
@@ -112248,13 +128113,26 @@ static void instrFunc(
zHaystack = sqlite3_value_blob(argv[0]);
zNeedle = sqlite3_value_blob(argv[1]);
isText = 0;
- }else{
+ }else if( typeHaystack!=SQLITE_BLOB && typeNeedle!=SQLITE_BLOB ){
zHaystack = sqlite3_value_text(argv[0]);
zNeedle = sqlite3_value_text(argv[1]);
isText = 1;
+ }else{
+ pC1 = sqlite3_value_dup(argv[0]);
+ zHaystack = sqlite3_value_text(pC1);
+ if( zHaystack==0 ) goto endInstrOOM;
+ nHaystack = sqlite3_value_bytes(pC1);
+ pC2 = sqlite3_value_dup(argv[1]);
+ zNeedle = sqlite3_value_text(pC2);
+ if( zNeedle==0 ) goto endInstrOOM;
+ nNeedle = sqlite3_value_bytes(pC2);
+ isText = 1;
}
- if( zNeedle==0 || (nHaystack && zHaystack==0) ) return;
- while( nNeedle<=nHaystack && memcmp(zHaystack, zNeedle, nNeedle)!=0 ){
+ if( zNeedle==0 || (nHaystack && zHaystack==0) ) goto endInstrOOM;
+ firstChar = zNeedle[0];
+ while( nNeedle<=nHaystack
+ && (zHaystack[0]!=firstChar || memcmp(zHaystack, zNeedle, nNeedle)!=0)
+ ){
N++;
do{
nHaystack--;
@@ -112264,10 +128142,17 @@ static void instrFunc(
if( nNeedle>nHaystack ) N = 0;
}
sqlite3_result_int(context, N);
+endInstr:
+ sqlite3_value_free(pC1);
+ sqlite3_value_free(pC2);
+ return;
+endInstrOOM:
+ sqlite3_result_error_nomem(context);
+ goto endInstr;
}
/*
-** Implementation of the printf() function.
+** Implementation of the printf() (a.k.a. format()) SQL function.
*/
static void printfFunc(
sqlite3_context *context,
@@ -112417,12 +128302,12 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
** handle the rounding directly,
** otherwise use printf.
*/
- if( n==0 && r>=0 && r<LARGEST_INT64-1 ){
- r = (double)((sqlite_int64)(r+0.5));
- }else if( n==0 && r<0 && (-r)<LARGEST_INT64-1 ){
- r = -(double)((sqlite_int64)((-r)+0.5));
+ if( r<-4503599627370496.0 || r>+4503599627370496.0 ){
+ /* The value has no fractional part so there is nothing to round */
+ }else if( n==0 ){
+ r = (double)((sqlite_int64)(r+(r<0?-0.5:+0.5)));
}else{
- zBuf = sqlite3_mprintf("%.*f",n,r);
+ zBuf = sqlite3_mprintf("%!.*f",n,r);
if( zBuf==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -112512,7 +128397,7 @@ static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
#define noopFunc versionFunc /* Substitute function - never called */
/*
-** Implementation of random(). Return a random integer.
+** Implementation of random(). Return a random integer.
*/
static void randomFunc(
sqlite3_context *context,
@@ -112523,11 +128408,11 @@ static void randomFunc(
UNUSED_PARAMETER2(NotUsed, NotUsed2);
sqlite3_randomness(sizeof(r), &r);
if( r<0 ){
- /* We need to prevent a random number of 0x8000000000000000
+ /* We need to prevent a random number of 0x8000000000000000
** (or -9223372036854775808) since when you do abs() of that
** number of you get the same value back again. To do this
** in a way that is testable, mask the sign bit off of negative
- ** values, resulting in a positive value. Then take the
+ ** values, resulting in a positive value. Then take the
** 2s complement of that positive value. The end result can
** therefore be no less than -9223372036854775807.
*/
@@ -112545,11 +128430,11 @@ static void randomBlob(
int argc,
sqlite3_value **argv
){
- int n;
+ sqlite3_int64 n;
unsigned char *p;
assert( argc==1 );
UNUSED_PARAMETER(argc);
- n = sqlite3_value_int(argv[0]);
+ n = sqlite3_value_int64(argv[0]);
if( n<1 ){
n = 1;
}
@@ -112565,8 +128450,8 @@ static void randomBlob(
** value is the same as the sqlite3_last_insert_rowid() API function.
*/
static void last_insert_rowid(
- sqlite3_context *context,
- int NotUsed,
+ sqlite3_context *context,
+ int NotUsed,
sqlite3_value **NotUsed2
){
sqlite3 *db = sqlite3_context_db_handle(context);
@@ -112580,9 +128465,9 @@ static void last_insert_rowid(
/*
** Implementation of the changes() SQL function.
**
-** IMP: R-62073-11209 The changes() SQL function is a wrapper
-** around the sqlite3_changes() C/C++ function and hence follows the same
-** rules for counting changes.
+** IMP: R-32760-32347 The changes() SQL function is a wrapper
+** around the sqlite3_changes64() C/C++ function and hence follows the
+** same rules for counting changes.
*/
static void changes(
sqlite3_context *context,
@@ -112591,12 +128476,12 @@ static void changes(
){
sqlite3 *db = sqlite3_context_db_handle(context);
UNUSED_PARAMETER2(NotUsed, NotUsed2);
- sqlite3_result_int(context, sqlite3_changes(db));
+ sqlite3_result_int64(context, sqlite3_changes64(db));
}
/*
** Implementation of the total_changes() SQL function. The return value is
-** the same as the sqlite3_total_changes() API function.
+** the same as the sqlite3_total_changes64() API function.
*/
static void total_changes(
sqlite3_context *context,
@@ -112605,9 +128490,9 @@ static void total_changes(
){
sqlite3 *db = sqlite3_context_db_handle(context);
UNUSED_PARAMETER2(NotUsed, NotUsed2);
- /* IMP: R-52756-41993 This function is a wrapper around the
- ** sqlite3_total_changes() C/C++ interface. */
- sqlite3_result_int(context, sqlite3_total_changes(db));
+ /* IMP: R-11217-42568 This function is a wrapper around the
+ ** sqlite3_total_changes64() C/C++ interface. */
+ sqlite3_result_int64(context, sqlite3_total_changes64(db));
}
/*
@@ -112622,7 +128507,7 @@ struct compareInfo {
/*
** For LIKE and GLOB matching on EBCDIC machines, assume that every
-** character is exactly one byte in size. Also, provde the Utf8Read()
+** character is exactly one byte in size. Also, provide the Utf8Read()
** macro for fast reading of the next character in the common case where
** the next character is ASCII.
*/
@@ -112674,7 +128559,7 @@ static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };
** it the last character in the list.
**
** Like matching rules:
-**
+**
** '%' Matches any sequence of zero or more characters
**
*** '_' Matches any one character
@@ -112697,13 +128582,14 @@ static int patternCompare(
u32 matchAll = pInfo->matchAll; /* "*" or "%" */
u8 noCase = pInfo->noCase; /* True if uppercase==lowercase */
const u8 *zEscaped = 0; /* One past the last escaped input char */
-
+
while( (c = Utf8Read(zPattern))!=0 ){
if( c==matchAll ){ /* Match "*" */
/* Skip over multiple "*" characters in the pattern. If there
** are also "?" characters, skip those as well, but consume a
** single character of the input string for each "?" skipped */
- while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){
+ while( (c=Utf8Read(zPattern)) == matchAll
+ || (c == matchOne && matchOne!=0) ){
if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){
return SQLITE_NOWILDCARDMATCH;
}
@@ -112736,7 +128622,7 @@ static int patternCompare(
** c but in the other case and search the input string for either
** c or cx.
*/
- if( c<=0x80 ){
+ if( c<0x80 ){
char zStop[3];
int bMatch;
if( noCase ){
@@ -112819,7 +128705,13 @@ static int patternCompare(
** non-zero if there is no match.
*/
SQLITE_API int sqlite3_strglob(const char *zGlobPattern, const char *zString){
- return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '[');
+ if( zString==0 ){
+ return zGlobPattern!=0;
+ }else if( zGlobPattern==0 ){
+ return 1;
+ }else {
+ return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '[');
+ }
}
/*
@@ -112827,7 +128719,13 @@ SQLITE_API int sqlite3_strglob(const char *zGlobPattern, const char *zString){
** a miss - like strcmp().
*/
SQLITE_API int sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc){
- return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc);
+ if( zStr==0 ){
+ return zPattern!=0;
+ }else if( zPattern==0 ){
+ return 1;
+ }else{
+ return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc);
+ }
}
/*
@@ -112842,7 +128740,7 @@ SQLITE_API int sqlite3_like_count = 0;
/*
** Implementation of the like() SQL function. This function implements
-** the build-in LIKE operator. The first argument to the function is the
+** the built-in LIKE operator. The first argument to the function is the
** pattern and the second argument is the string. So, the SQL statements:
**
** A LIKE B
@@ -112853,8 +128751,8 @@ SQLITE_API int sqlite3_like_count = 0;
** the GLOB operator.
*/
static void likeFunc(
- sqlite3_context *context,
- int argc,
+ sqlite3_context *context,
+ int argc,
sqlite3_value **argv
){
const unsigned char *zA, *zB;
@@ -112862,6 +128760,7 @@ static void likeFunc(
int nPat;
sqlite3 *db = sqlite3_context_db_handle(context);
struct compareInfo *pInfo = sqlite3_user_data(context);
+ struct compareInfo backupInfo;
#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
if( sqlite3_value_type(argv[0])==SQLITE_BLOB
@@ -112874,8 +128773,6 @@ static void likeFunc(
return;
}
#endif
- zB = sqlite3_value_text(argv[0]);
- zA = sqlite3_value_text(argv[1]);
/* Limit the length of the LIKE or GLOB pattern to avoid problems
** of deep recursion and N*N behavior in patternCompare().
@@ -112887,8 +128784,6 @@ static void likeFunc(
sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1);
return;
}
- assert( zB==sqlite3_value_text(argv[0]) ); /* Encoding did not change */
-
if( argc==3 ){
/* The escape character string must consist of a single UTF-8 character.
** Otherwise, return an error.
@@ -112896,14 +128791,22 @@ static void likeFunc(
const unsigned char *zEsc = sqlite3_value_text(argv[2]);
if( zEsc==0 ) return;
if( sqlite3Utf8CharLen((char*)zEsc, -1)!=1 ){
- sqlite3_result_error(context,
+ sqlite3_result_error(context,
"ESCAPE expression must be a single character", -1);
return;
}
escape = sqlite3Utf8Read(&zEsc);
+ if( escape==pInfo->matchAll || escape==pInfo->matchOne ){
+ memcpy(&backupInfo, pInfo, sizeof(backupInfo));
+ pInfo = &backupInfo;
+ if( escape==pInfo->matchAll ) pInfo->matchAll = 0;
+ if( escape==pInfo->matchOne ) pInfo->matchOne = 0;
+ }
}else{
escape = pInfo->matchSet;
}
+ zB = sqlite3_value_text(argv[0]);
+ zA = sqlite3_value_text(argv[1]);
if( zA && zB ){
#ifdef SQLITE_TEST
sqlite3_like_count++;
@@ -113001,8 +128904,8 @@ static void compileoptionusedFunc(
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
/*
-** Implementation of the sqlite_compileoption_get() function.
-** The result is a string that identifies the compiler options
+** Implementation of the sqlite_compileoption_get() function.
+** The result is a string that identifies the compiler options
** used to build SQLite.
*/
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
@@ -113026,43 +128929,46 @@ static void compileoptiongetFunc(
** digits. */
static const char hexdigits[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
/*
-** Implementation of the QUOTE() function. This function takes a single
-** argument. If the argument is numeric, the return value is the same as
-** the argument. If the argument is NULL, the return value is the string
-** "NULL". Otherwise, the argument is enclosed in single quotes with
-** single-quote escapes.
+** Append to pStr text that is the SQL literal representation of the
+** value contained in pValue.
*/
-static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
- assert( argc==1 );
- UNUSED_PARAMETER(argc);
- switch( sqlite3_value_type(argv[0]) ){
+SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
+ /* As currently implemented, the string must be initially empty.
+ ** we might relax this requirement in the future, but that will
+ ** require enhancements to the implementation. */
+ assert( pStr!=0 && pStr->nChar==0 );
+
+ switch( sqlite3_value_type(pValue) ){
case SQLITE_FLOAT: {
double r1, r2;
- char zBuf[50];
- r1 = sqlite3_value_double(argv[0]);
- sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
- sqlite3AtoF(zBuf, &r2, 20, SQLITE_UTF8);
- if( r1!=r2 ){
- sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.20e", r1);
+ const char *zVal;
+ r1 = sqlite3_value_double(pValue);
+ sqlite3_str_appendf(pStr, "%!.15g", r1);
+ zVal = sqlite3_str_value(pStr);
+ if( zVal ){
+ sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8);
+ if( r1!=r2 ){
+ sqlite3_str_reset(pStr);
+ sqlite3_str_appendf(pStr, "%!.20e", r1);
+ }
}
- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
break;
}
case SQLITE_INTEGER: {
- sqlite3_result_value(context, argv[0]);
+ sqlite3_str_appendf(pStr, "%lld", sqlite3_value_int64(pValue));
break;
}
case SQLITE_BLOB: {
- char *zText = 0;
- char const *zBlob = sqlite3_value_blob(argv[0]);
- int nBlob = sqlite3_value_bytes(argv[0]);
- assert( zBlob==sqlite3_value_blob(argv[0]) ); /* No encoding change */
- zText = (char *)contextMalloc(context, (2*(i64)nBlob)+4);
- if( zText ){
+ char const *zBlob = sqlite3_value_blob(pValue);
+ i64 nBlob = sqlite3_value_bytes(pValue);
+ assert( zBlob==sqlite3_value_blob(pValue) ); /* No encoding change */
+ sqlite3StrAccumEnlarge(pStr, nBlob*2 + 4);
+ if( pStr->accError==0 ){
+ char *zText = pStr->zText;
int i;
for(i=0; i<nBlob; i++){
zText[(i*2)+2] = hexdigits[(zBlob[i]>>4)&0x0F];
@@ -113072,45 +128978,51 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
zText[(nBlob*2)+3] = '\0';
zText[0] = 'X';
zText[1] = '\'';
- sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT);
- sqlite3_free(zText);
+ pStr->nChar = nBlob*2 + 3;
}
break;
}
case SQLITE_TEXT: {
- int i,j;
- u64 n;
- const unsigned char *zArg = sqlite3_value_text(argv[0]);
- char *z;
-
- if( zArg==0 ) return;
- for(i=0, n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; }
- z = contextMalloc(context, ((i64)i)+((i64)n)+3);
- if( z ){
- z[0] = '\'';
- for(i=0, j=1; zArg[i]; i++){
- z[j++] = zArg[i];
- if( zArg[i]=='\'' ){
- z[j++] = '\'';
- }
- }
- z[j++] = '\'';
- z[j] = 0;
- sqlite3_result_text(context, z, j, sqlite3_free);
- }
+ const unsigned char *zArg = sqlite3_value_text(pValue);
+ sqlite3_str_appendf(pStr, "%Q", zArg);
break;
}
default: {
- assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
- sqlite3_result_text(context, "NULL", 4, SQLITE_STATIC);
+ assert( sqlite3_value_type(pValue)==SQLITE_NULL );
+ sqlite3_str_append(pStr, "NULL", 4);
break;
}
}
}
/*
+** Implementation of the QUOTE() function.
+**
+** The quote(X) function returns the text of an SQL literal which is the
+** value of its argument suitable for inclusion into an SQL statement.
+** Strings are surrounded by single-quotes with escapes on interior quotes
+** as needed. BLOBs are encoded as hexadecimal literals. Strings with
+** embedded NUL characters cannot be represented as string literals in SQL
+** and hence the returned string literal is truncated prior to the first NUL.
+*/
+static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
+ sqlite3_str str;
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ assert( argc==1 );
+ UNUSED_PARAMETER(argc);
+ sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
+ sqlite3QuoteValue(&str,argv[0]);
+ sqlite3_result_text(context, sqlite3StrAccumFinish(&str), str.nChar,
+ SQLITE_DYNAMIC);
+ if( str.accError!=SQLITE_OK ){
+ sqlite3_result_null(context);
+ sqlite3_result_error_code(context, str.accError);
+ }
+}
+
+/*
** The unicode() function. Return the integer unicode code-point value
-** for the first character of the input string.
+** for the first character of the input string.
*/
static void unicodeFunc(
sqlite3_context *context,
@@ -113161,6 +129073,7 @@ static void charFunc(
*zOut++ = 0x80 + (u8)(c & 0x3F);
} \
}
+ *zOut = 0;
sqlite3_result_text64(context, (char*)z, zOut-z, sqlite3_free, SQLITE_UTF8);
}
@@ -113189,10 +129102,101 @@ static void hexFunc(
*(z++) = hexdigits[c&0xf];
}
*z = 0;
- sqlite3_result_text(context, zHex, n*2, sqlite3_free);
+ sqlite3_result_text64(context, zHex, (u64)(z-zHex),
+ sqlite3_free, SQLITE_UTF8);
+ }
+}
+
+/*
+** Buffer zStr contains nStr bytes of utf-8 encoded text. Return 1 if zStr
+** contains character ch, or 0 if it does not.
+*/
+static int strContainsChar(const u8 *zStr, int nStr, u32 ch){
+ const u8 *zEnd = &zStr[nStr];
+ const u8 *z = zStr;
+ while( z<zEnd ){
+ u32 tst = Utf8Read(z);
+ if( tst==ch ) return 1;
+ }
+ return 0;
+}
+
+/*
+** The unhex() function. This function may be invoked with either one or
+** two arguments. In both cases the first argument is interpreted as text
+** a text value containing a set of pairs of hexadecimal digits which are
+** decoded and returned as a blob.
+**
+** If there is only a single argument, then it must consist only of an
+** even number of hexadecimal digits. Otherwise, return NULL.
+**
+** Or, if there is a second argument, then any character that appears in
+** the second argument is also allowed to appear between pairs of hexadecimal
+** digits in the first argument. If any other character appears in the
+** first argument, or if one of the allowed characters appears between
+** two hexadecimal digits that make up a single byte, NULL is returned.
+**
+** The following expressions are all true:
+**
+** unhex('ABCD') IS x'ABCD'
+** unhex('AB CD') IS NULL
+** unhex('AB CD', ' ') IS x'ABCD'
+** unhex('A BCD', ' ') IS NULL
+*/
+static void unhexFunc(
+ sqlite3_context *pCtx,
+ int argc,
+ sqlite3_value **argv
+){
+ const u8 *zPass = (const u8*)"";
+ int nPass = 0;
+ const u8 *zHex = sqlite3_value_text(argv[0]);
+ int nHex = sqlite3_value_bytes(argv[0]);
+#ifdef SQLITE_DEBUG
+ const u8 *zEnd = zHex ? &zHex[nHex] : 0;
+#endif
+ u8 *pBlob = 0;
+ u8 *p = 0;
+
+ assert( argc==1 || argc==2 );
+ if( argc==2 ){
+ zPass = sqlite3_value_text(argv[1]);
+ nPass = sqlite3_value_bytes(argv[1]);
+ }
+ if( !zHex || !zPass ) return;
+
+ p = pBlob = contextMalloc(pCtx, (nHex/2)+1);
+ if( pBlob ){
+ u8 c; /* Most significant digit of next byte */
+ u8 d; /* Least significant digit of next byte */
+
+ while( (c = *zHex)!=0x00 ){
+ while( !sqlite3Isxdigit(c) ){
+ u32 ch = Utf8Read(zHex);
+ assert( zHex<=zEnd );
+ if( !strContainsChar(zPass, nPass, ch) ) goto unhex_null;
+ c = *zHex;
+ if( c==0x00 ) goto unhex_done;
+ }
+ zHex++;
+ assert( *zEnd==0x00 );
+ assert( zHex<=zEnd );
+ d = *(zHex++);
+ if( !sqlite3Isxdigit(d) ) goto unhex_null;
+ *(p++) = (sqlite3HexToInt(c)<<4) | sqlite3HexToInt(d);
+ }
}
+
+ unhex_done:
+ sqlite3_result_blob(pCtx, pBlob, (p - pBlob), sqlite3_free);
+ return;
+
+ unhex_null:
+ sqlite3_free(pBlob);
+ return;
}
+
/*
** The zeroblob(N) function returns a zero-filled blob of size N bytes.
*/
@@ -113266,7 +129270,7 @@ static void replaceFunc(
if( zOut==0 ){
return;
}
- loopLimit = nStr - nPattern;
+ loopLimit = nStr - nPattern;
cntExpand = 0;
for(i=j=0; i<=loopLimit; i++){
if( zStr[i]!=zPattern[0] || memcmp(&zStr[i], zPattern, nPattern) ){
@@ -113287,7 +129291,7 @@ static void replaceFunc(
** whose index is a power of two: 1, 2, 4, 8, 16, 32, ... */
u8 *zOld;
zOld = zOut;
- zOut = sqlite3_realloc64(zOut, (int)nOut + (nOut - nStr - 1));
+ zOut = sqlite3Realloc(zOut, (int)nOut + (nOut - nStr - 1));
if( zOut==0 ){
sqlite3_result_error_nomem(context);
sqlite3_free(zOld);
@@ -113319,10 +129323,10 @@ static void trimFunc(
){
const unsigned char *zIn; /* Input string */
const unsigned char *zCharSet; /* Set of characters to trim */
- int nIn; /* Number of bytes in input */
+ unsigned int nIn; /* Number of bytes in input */
int flags; /* 1: trimleft 2: trimright 3: trim */
int i; /* Loop counter */
- unsigned char *aLen = 0; /* Length of each character in zCharSet */
+ unsigned int *aLen = 0; /* Length of each character in zCharSet */
unsigned char **azChar = 0; /* Individual characters in zCharSet */
int nChar; /* Number of characters in zCharSet */
@@ -113331,13 +129335,13 @@ static void trimFunc(
}
zIn = sqlite3_value_text(argv[0]);
if( zIn==0 ) return;
- nIn = sqlite3_value_bytes(argv[0]);
+ nIn = (unsigned)sqlite3_value_bytes(argv[0]);
assert( zIn==sqlite3_value_text(argv[0]) );
if( argc==1 ){
- static const unsigned char lenOne[] = { 1 };
+ static const unsigned lenOne[] = { 1 };
static unsigned char * const azOne[] = { (u8*)" " };
nChar = 1;
- aLen = (u8*)lenOne;
+ aLen = (unsigned*)lenOne;
azChar = (unsigned char **)azOne;
zCharSet = 0;
}else if( (zCharSet = sqlite3_value_text(argv[1]))==0 ){
@@ -113348,15 +129352,16 @@ static void trimFunc(
SQLITE_SKIP_UTF8(z);
}
if( nChar>0 ){
- azChar = contextMalloc(context, ((i64)nChar)*(sizeof(char*)+1));
+ azChar = contextMalloc(context,
+ ((i64)nChar)*(sizeof(char*)+sizeof(unsigned)));
if( azChar==0 ){
return;
}
- aLen = (unsigned char*)&azChar[nChar];
+ aLen = (unsigned*)&azChar[nChar];
for(z=zCharSet, nChar=0; *z; nChar++){
azChar[nChar] = (unsigned char *)z;
SQLITE_SKIP_UTF8(z);
- aLen[nChar] = (u8)(z - azChar[nChar]);
+ aLen[nChar] = (unsigned)(z - azChar[nChar]);
}
}
}
@@ -113364,7 +129369,7 @@ static void trimFunc(
flags = SQLITE_PTR_TO_INT(sqlite3_user_data(context));
if( flags & 1 ){
while( nIn>0 ){
- int len = 0;
+ unsigned int len = 0;
for(i=0; i<nChar; i++){
len = aLen[i];
if( len<=nIn && memcmp(zIn, azChar[i], len)==0 ) break;
@@ -113376,7 +129381,7 @@ static void trimFunc(
}
if( flags & 2 ){
while( nIn>0 ){
- int len = 0;
+ unsigned int len = 0;
for(i=0; i<nChar; i++){
len = aLen[i];
if( len<=nIn && memcmp(&zIn[nIn-len],azChar[i],len)==0 ) break;
@@ -113392,12 +129397,87 @@ static void trimFunc(
sqlite3_result_text(context, (char*)zIn, nIn, SQLITE_TRANSIENT);
}
+/* The core implementation of the CONCAT(...) and CONCAT_WS(SEP,...)
+** functions.
+**
+** Return a string value that is the concatenation of all non-null
+** entries in argv[]. Use zSep as the separator.
+*/
+static void concatFuncCore(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv,
+ int nSep,
+ const char *zSep
+){
+ i64 j, k, n = 0;
+ int i;
+ char *z;
+ for(i=0; i<argc; i++){
+ n += sqlite3_value_bytes(argv[i]);
+ }
+ n += (argc-1)*nSep;
+ z = sqlite3_malloc64(n+1);
+ if( z==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ j = 0;
+ for(i=0; i<argc; i++){
+ k = sqlite3_value_bytes(argv[i]);
+ if( k>0 ){
+ const char *v = (const char*)sqlite3_value_text(argv[i]);
+ if( v!=0 ){
+ if( j>0 && nSep>0 ){
+ memcpy(&z[j], zSep, nSep);
+ j += nSep;
+ }
+ memcpy(&z[j], v, k);
+ j += k;
+ }
+ }
+ }
+ z[j] = 0;
+ assert( j<=n );
+ sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8);
+}
+
+/*
+** The CONCAT(...) function. Generate a string result that is the
+** concatentation of all non-null arguments.
+*/
+static void concatFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ concatFuncCore(context, argc, argv, 0, "");
+}
+
+/*
+** The CONCAT_WS(separator, ...) function.
+**
+** Generate a string that is the concatenation of 2nd through the Nth
+** argument. Use the first argument (which must be non-NULL) as the
+** separator.
+*/
+static void concatwsFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int nSep = sqlite3_value_bytes(argv[0]);
+ const char *zSep = (const char*)sqlite3_value_text(argv[0]);
+ if( zSep==0 ) return;
+ concatFuncCore(context, argc-1, argv+1, nSep, zSep);
+}
+
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
/*
** The "unknown" function is automatically substituted in place of
** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN
-** when the SQLITE_ENABLE_UNKNOWN_FUNCTION compile-time option is used.
+** when the SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION compile-time option is used.
** When the "sqlite3" command-line shell is built using this functionality,
** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries
** involving application-defined functions to be examined in a generic
@@ -113409,6 +129489,9 @@ static void unknownFunc(
sqlite3_value **argv
){
/* no-op */
+ (void)context;
+ (void)argc;
+ (void)argv;
}
#endif /*SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION*/
@@ -113422,7 +129505,7 @@ static void unknownFunc(
** Compute the soundex encoding of a word.
**
** IMP: R-59782-00072 The soundex(X) function returns a string that is the
-** soundex encoding of the string X.
+** soundex encoding of the string X.
*/
static void soundexFunc(
sqlite3_context *context,
@@ -113510,14 +129593,69 @@ static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){
*/
typedef struct SumCtx SumCtx;
struct SumCtx {
- double rSum; /* Floating point sum */
- i64 iSum; /* Integer sum */
+ double rSum; /* Running sum as as a double */
+ double rErr; /* Error term for Kahan-Babushka-Neumaier summation */
+ i64 iSum; /* Running sum as a signed integer */
i64 cnt; /* Number of elements summed */
- u8 overflow; /* True if integer overflow seen */
- u8 approx; /* True if non-integer value was input to the sum */
+ u8 approx; /* True if any non-integer value was input to the sum */
+ u8 ovrfl; /* Integer overflow seen */
};
/*
+** Do one step of the Kahan-Babushka-Neumaier summation.
+**
+** https://en.wikipedia.org/wiki/Kahan_summation_algorithm
+**
+** Variables are marked "volatile" to defeat c89 x86 floating point
+** optimizations can mess up this algorithm.
+*/
+static void kahanBabuskaNeumaierStep(
+ volatile SumCtx *pSum,
+ volatile double r
+){
+ volatile double s = pSum->rSum;
+ volatile double t = s + r;
+ if( fabs(s) > fabs(r) ){
+ pSum->rErr += (s - t) + r;
+ }else{
+ pSum->rErr += (r - t) + s;
+ }
+ pSum->rSum = t;
+}
+
+/*
+** Add a (possibly large) integer to the running sum.
+*/
+static void kahanBabuskaNeumaierStepInt64(volatile SumCtx *pSum, i64 iVal){
+ if( iVal<=-4503599627370496LL || iVal>=+4503599627370496LL ){
+ i64 iBig, iSm;
+ iSm = iVal % 16384;
+ iBig = iVal - iSm;
+ kahanBabuskaNeumaierStep(pSum, iBig);
+ kahanBabuskaNeumaierStep(pSum, iSm);
+ }else{
+ kahanBabuskaNeumaierStep(pSum, (double)iVal);
+ }
+}
+
+/*
+** Initialize the Kahan-Babaska-Neumaier sum from a 64-bit integer
+*/
+static void kahanBabuskaNeumaierInit(
+ volatile SumCtx *p,
+ i64 iVal
+){
+ if( iVal<=-4503599627370496LL || iVal>=+4503599627370496LL ){
+ i64 iSm = iVal % 16384;
+ p->rSum = (double)(iVal - iSm);
+ p->rErr = (double)iSm;
+ }else{
+ p->rSum = (double)iVal;
+ p->rErr = 0.0;
+ }
+}
+
+/*
** Routines used to compute the sum, average, and total.
**
** The SUM() function follows the (broken) SQL standard which means
@@ -113536,15 +129674,29 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
type = sqlite3_value_numeric_type(argv[0]);
if( p && type!=SQLITE_NULL ){
p->cnt++;
- if( type==SQLITE_INTEGER ){
- i64 v = sqlite3_value_int64(argv[0]);
- p->rSum += v;
- if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){
- p->approx = p->overflow = 1;
+ if( p->approx==0 ){
+ if( type!=SQLITE_INTEGER ){
+ kahanBabuskaNeumaierInit(p, p->iSum);
+ p->approx = 1;
+ kahanBabuskaNeumaierStep(p, sqlite3_value_double(argv[0]));
+ }else{
+ i64 x = p->iSum;
+ if( sqlite3AddInt64(&x, sqlite3_value_int64(argv[0]))==0 ){
+ p->iSum = x;
+ }else{
+ p->ovrfl = 1;
+ kahanBabuskaNeumaierInit(p, p->iSum);
+ p->approx = 1;
+ kahanBabuskaNeumaierStepInt64(p, sqlite3_value_int64(argv[0]));
+ }
}
}else{
- p->rSum += sqlite3_value_double(argv[0]);
- p->approx = 1;
+ if( type==SQLITE_INTEGER ){
+ kahanBabuskaNeumaierStepInt64(p, sqlite3_value_int64(argv[0]));
+ }else{
+ p->ovrfl = 0;
+ kahanBabuskaNeumaierStep(p, sqlite3_value_double(argv[0]));
+ }
}
}
}
@@ -113561,13 +129713,18 @@ static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){
if( ALWAYS(p) && type!=SQLITE_NULL ){
assert( p->cnt>0 );
p->cnt--;
- assert( type==SQLITE_INTEGER || p->approx );
- if( type==SQLITE_INTEGER && p->approx==0 ){
- i64 v = sqlite3_value_int64(argv[0]);
- p->rSum -= v;
- p->iSum -= v;
+ if( !p->approx ){
+ p->iSum -= sqlite3_value_int64(argv[0]);
+ }else if( type==SQLITE_INTEGER ){
+ i64 iVal = sqlite3_value_int64(argv[0]);
+ if( iVal!=SMALLEST_INT64 ){
+ kahanBabuskaNeumaierStepInt64(p, -iVal);
+ }else{
+ kahanBabuskaNeumaierStepInt64(p, LARGEST_INT64);
+ kahanBabuskaNeumaierStepInt64(p, 1);
+ }
}else{
- p->rSum -= sqlite3_value_double(argv[0]);
+ kahanBabuskaNeumaierStep(p, -sqlite3_value_double(argv[0]));
}
}
}
@@ -113578,10 +129735,14 @@ static void sumFinalize(sqlite3_context *context){
SumCtx *p;
p = sqlite3_aggregate_context(context, 0);
if( p && p->cnt>0 ){
- if( p->overflow ){
- sqlite3_result_error(context,"integer overflow",-1);
- }else if( p->approx ){
- sqlite3_result_double(context, p->rSum);
+ if( p->approx ){
+ if( p->ovrfl ){
+ sqlite3_result_error(context,"integer overflow",-1);
+ }else if( !sqlite3IsNaN(p->rErr) ){
+ sqlite3_result_double(context, p->rSum+p->rErr);
+ }else{
+ sqlite3_result_double(context, p->rSum);
+ }
}else{
sqlite3_result_int64(context, p->iSum);
}
@@ -113591,14 +129752,29 @@ static void avgFinalize(sqlite3_context *context){
SumCtx *p;
p = sqlite3_aggregate_context(context, 0);
if( p && p->cnt>0 ){
- sqlite3_result_double(context, p->rSum/(double)p->cnt);
+ double r;
+ if( p->approx ){
+ r = p->rSum;
+ if( !sqlite3IsNaN(p->rErr) ) r += p->rErr;
+ }else{
+ r = (double)(p->iSum);
+ }
+ sqlite3_result_double(context, r/(double)p->cnt);
}
}
static void totalFinalize(sqlite3_context *context){
SumCtx *p;
+ double r = 0.0;
p = sqlite3_aggregate_context(context, 0);
- /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
- sqlite3_result_double(context, p ? p->rSum : (double)0);
+ if( p ){
+ if( p->approx ){
+ r = p->rSum;
+ if( !sqlite3IsNaN(p->rErr) ) r += p->rErr;
+ }else{
+ r = (double)(p->iSum);
+ }
+ }
+ sqlite3_result_double(context, r);
}
/*
@@ -113625,13 +129801,13 @@ static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){
#ifndef SQLITE_OMIT_DEPRECATED
/* The sqlite3_aggregate_count() function is deprecated. But just to make
- ** sure it still operates correctly, verify that its count agrees with our
+ ** sure it still operates correctly, verify that its count agrees with our
** internal count when using count(*) and when the total count can be
** expressed as a 32-bit integer. */
assert( argc==1 || p==0 || p->n>0x7fffffff || p->bInverse
|| p->n==sqlite3_aggregate_count(context) );
#endif
-}
+}
static void countFinalize(sqlite3_context *context){
CountCtx *p;
p = sqlite3_aggregate_context(context, 0);
@@ -113648,7 +129824,7 @@ static void countInverse(sqlite3_context *ctx, int argc, sqlite3_value **argv){
p->bInverse = 1;
#endif
}
-}
+}
#else
# define countInverse 0
#endif /* SQLITE_OMIT_WINDOWFUNC */
@@ -113657,8 +129833,8 @@ static void countInverse(sqlite3_context *ctx, int argc, sqlite3_value **argv){
** Routines to implement min() and max() aggregate functions.
*/
static void minmaxStep(
- sqlite3_context *context,
- int NotUsed,
+ sqlite3_context *context,
+ int NotUsed,
sqlite3_value **argv
){
Mem *pArg = (Mem *)argv[0];
@@ -113717,97 +129893,168 @@ static void minMaxFinalize(sqlite3_context *context){
/*
** group_concat(EXPR, ?SEPARATOR?)
+** string_agg(EXPR, SEPARATOR)
+**
+** The SEPARATOR goes before the EXPR string. This is tragic. The
+** groupConcatInverse() implementation would have been easier if the
+** SEPARATOR were appended after EXPR. And the order is undocumented,
+** so we could change it, in theory. But the old behavior has been
+** around for so long that we dare not, for fear of breaking something.
*/
+typedef struct {
+ StrAccum str; /* The accumulated concatenation */
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ int nAccum; /* Number of strings presently concatenated */
+ int nFirstSepLength; /* Used to detect separator length change */
+ /* If pnSepLengths!=0, refs an array of inter-string separator lengths,
+ ** stored as actually incorporated into presently accumulated result.
+ ** (Hence, its slots in use number nAccum-1 between method calls.)
+ ** If pnSepLengths==0, nFirstSepLength is the length used throughout.
+ */
+ int *pnSepLengths;
+#endif
+} GroupConcatCtx;
+
static void groupConcatStep(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zVal;
- StrAccum *pAccum;
+ GroupConcatCtx *pGCC;
const char *zSep;
int nVal, nSep;
assert( argc==1 || argc==2 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
- pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum));
-
- if( pAccum ){
+ pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC));
+ if( pGCC ){
sqlite3 *db = sqlite3_context_db_handle(context);
- int firstTerm = pAccum->mxAlloc==0;
- pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH];
- if( !firstTerm ){
- if( argc==2 ){
- zSep = (char*)sqlite3_value_text(argv[1]);
- nSep = sqlite3_value_bytes(argv[1]);
- }else{
- zSep = ",";
- nSep = 1;
+ int firstTerm = pGCC->str.mxAlloc==0;
+ pGCC->str.mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH];
+ if( argc==1 ){
+ if( !firstTerm ){
+ sqlite3_str_appendchar(&pGCC->str, 1, ',');
+ }
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ else{
+ pGCC->nFirstSepLength = 1;
+ }
+#endif
+ }else if( !firstTerm ){
+ zSep = (char*)sqlite3_value_text(argv[1]);
+ nSep = sqlite3_value_bytes(argv[1]);
+ if( zSep ){
+ sqlite3_str_append(&pGCC->str, zSep, nSep);
+ }
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ else{
+ nSep = 0;
+ }
+ if( nSep != pGCC->nFirstSepLength || pGCC->pnSepLengths != 0 ){
+ int *pnsl = pGCC->pnSepLengths;
+ if( pnsl == 0 ){
+ /* First separator length variation seen, start tracking them. */
+ pnsl = (int*)sqlite3_malloc64((pGCC->nAccum+1) * sizeof(int));
+ if( pnsl!=0 ){
+ int i = 0, nA = pGCC->nAccum-1;
+ while( i<nA ) pnsl[i++] = pGCC->nFirstSepLength;
+ }
+ }else{
+ pnsl = (int*)sqlite3_realloc64(pnsl, pGCC->nAccum * sizeof(int));
+ }
+ if( pnsl!=0 ){
+ if( ALWAYS(pGCC->nAccum>0) ){
+ pnsl[pGCC->nAccum-1] = nSep;
+ }
+ pGCC->pnSepLengths = pnsl;
+ }else{
+ sqlite3StrAccumSetError(&pGCC->str, SQLITE_NOMEM);
+ }
}
- if( zSep ) sqlite3_str_append(pAccum, zSep, nSep);
+#endif
+ }
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ else{
+ pGCC->nFirstSepLength = sqlite3_value_bytes(argv[1]);
}
+ pGCC->nAccum += 1;
+#endif
zVal = (char*)sqlite3_value_text(argv[0]);
nVal = sqlite3_value_bytes(argv[0]);
- if( zVal ) sqlite3_str_append(pAccum, zVal, nVal);
+ if( zVal ) sqlite3_str_append(&pGCC->str, zVal, nVal);
}
}
+
#ifndef SQLITE_OMIT_WINDOWFUNC
static void groupConcatInverse(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
- int n;
- StrAccum *pAccum;
+ GroupConcatCtx *pGCC;
assert( argc==1 || argc==2 );
+ (void)argc; /* Suppress unused parameter warning */
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
- pAccum = (StrAccum*)sqlite3_aggregate_context(context, sizeof(*pAccum));
- /* pAccum is always non-NULL since groupConcatStep() will have always
- ** run frist to initialize it */
- if( ALWAYS(pAccum) ){
- n = sqlite3_value_bytes(argv[0]);
- if( argc==2 ){
- n += sqlite3_value_bytes(argv[1]);
+ pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC));
+ /* pGCC is always non-NULL since groupConcatStep() will have always
+ ** run first to initialize it */
+ if( ALWAYS(pGCC) ){
+ int nVS;
+ /* Must call sqlite3_value_text() to convert the argument into text prior
+ ** to invoking sqlite3_value_bytes(), in case the text encoding is UTF16 */
+ (void)sqlite3_value_text(argv[0]);
+ nVS = sqlite3_value_bytes(argv[0]);
+ pGCC->nAccum -= 1;
+ if( pGCC->pnSepLengths!=0 ){
+ assert(pGCC->nAccum >= 0);
+ if( pGCC->nAccum>0 ){
+ nVS += *pGCC->pnSepLengths;
+ memmove(pGCC->pnSepLengths, pGCC->pnSepLengths+1,
+ (pGCC->nAccum-1)*sizeof(int));
+ }
}else{
- n++;
+ /* If removing single accumulated string, harmlessly over-do. */
+ nVS += pGCC->nFirstSepLength;
}
- if( n>=(int)pAccum->nChar ){
- pAccum->nChar = 0;
+ if( nVS>=(int)pGCC->str.nChar ){
+ pGCC->str.nChar = 0;
}else{
- pAccum->nChar -= n;
- memmove(pAccum->zText, &pAccum->zText[n], pAccum->nChar);
+ pGCC->str.nChar -= nVS;
+ memmove(pGCC->str.zText, &pGCC->str.zText[nVS], pGCC->str.nChar);
+ }
+ if( pGCC->str.nChar==0 ){
+ pGCC->str.mxAlloc = 0;
+ sqlite3_free(pGCC->pnSepLengths);
+ pGCC->pnSepLengths = 0;
}
- if( pAccum->nChar==0 ) pAccum->mxAlloc = 0;
}
}
#else
# define groupConcatInverse 0
#endif /* SQLITE_OMIT_WINDOWFUNC */
static void groupConcatFinalize(sqlite3_context *context){
- StrAccum *pAccum;
- pAccum = sqlite3_aggregate_context(context, 0);
- if( pAccum ){
- if( pAccum->accError==SQLITE_TOOBIG ){
- sqlite3_result_error_toobig(context);
- }else if( pAccum->accError==SQLITE_NOMEM ){
- sqlite3_result_error_nomem(context);
- }else{
- sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
- sqlite3_free);
- }
+ GroupConcatCtx *pGCC
+ = (GroupConcatCtx*)sqlite3_aggregate_context(context, 0);
+ if( pGCC ){
+ sqlite3ResultStrAccum(context, &pGCC->str);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ sqlite3_free(pGCC->pnSepLengths);
+#endif
}
}
#ifndef SQLITE_OMIT_WINDOWFUNC
static void groupConcatValue(sqlite3_context *context){
- sqlite3_str *pAccum;
- pAccum = (sqlite3_str*)sqlite3_aggregate_context(context, 0);
- if( pAccum ){
+ GroupConcatCtx *pGCC
+ = (GroupConcatCtx*)sqlite3_aggregate_context(context, 0);
+ if( pGCC ){
+ StrAccum *pAccum = &pGCC->str;
if( pAccum->accError==SQLITE_TOOBIG ){
sqlite3_result_error_toobig(context);
}else if( pAccum->accError==SQLITE_NOMEM ){
sqlite3_result_error_nomem(context);
- }else{
+ }else{
const char *zText = sqlite3_str_value(pAccum);
- sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT);
}
}
}
@@ -113829,42 +130076,36 @@ SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 *db){
}
/*
-** Set the LIKEOPT flag on the 2-argument function with the given name.
-*/
-static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){
- FuncDef *pDef;
- pDef = sqlite3FindFunction(db, zName, 2, SQLITE_UTF8, 0);
- if( ALWAYS(pDef) ){
- pDef->funcFlags |= flagVal;
- }
-}
-
-/*
-** Register the built-in LIKE and GLOB functions. The caseSensitive
+** Re-register the built-in LIKE functions. The caseSensitive
** parameter determines whether or not the LIKE operator is case
-** sensitive. GLOB is always case sensitive.
+** sensitive.
*/
SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
+ FuncDef *pDef;
struct compareInfo *pInfo;
+ int flags;
+ int nArg;
if( caseSensitive ){
pInfo = (struct compareInfo*)&likeInfoAlt;
+ flags = SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE;
}else{
pInfo = (struct compareInfo*)&likeInfoNorm;
+ flags = SQLITE_FUNC_LIKE;
+ }
+ for(nArg=2; nArg<=3; nArg++){
+ sqlite3CreateFunc(db, "like", nArg, SQLITE_UTF8, pInfo, likeFunc,
+ 0, 0, 0, 0, 0);
+ pDef = sqlite3FindFunction(db, "like", nArg, SQLITE_UTF8, 0);
+ pDef->funcFlags |= flags;
+ pDef->funcFlags &= ~SQLITE_FUNC_UNSAFE;
}
- sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0);
- sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0);
- sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8,
- (struct compareInfo*)&globInfo, likeFunc, 0, 0, 0, 0, 0);
- setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE);
- setLikeOptFlag(db, "like",
- caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE);
}
/*
** pExpr points to an expression which implements a function. If
** it is appropriate to apply the LIKE optimization to that function
** then set aWc[0] through aWc[2] to the wildcard characters and the
-** escape character and then return TRUE. If the function is not a
+** escape character and then return TRUE. If the function is not a
** LIKE-style function then return FALSE.
**
** The expression "a LIKE b ESCAPE c" is only considered a valid LIKE
@@ -113880,38 +130121,286 @@ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive)
SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
FuncDef *pDef;
int nExpr;
- if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){
+ assert( pExpr!=0 );
+ assert( pExpr->op==TK_FUNCTION );
+ assert( ExprUseXList(pExpr) );
+ if( !pExpr->x.pList ){
return 0;
}
- assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
nExpr = pExpr->x.pList->nExpr;
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
pDef = sqlite3FindFunction(db, pExpr->u.zToken, nExpr, SQLITE_UTF8, 0);
+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+ if( pDef==0 ) return 0;
+#endif
if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
return 0;
}
+
+ /* The memcpy() statement assumes that the wildcard characters are
+ ** the first three statements in the compareInfo structure. The
+ ** asserts() that follow verify that assumption
+ */
+ memcpy(aWc, pDef->pUserData, 3);
+ assert( (char*)&likeInfoAlt == (char*)&likeInfoAlt.matchAll );
+ assert( &((char*)&likeInfoAlt)[1] == (char*)&likeInfoAlt.matchOne );
+ assert( &((char*)&likeInfoAlt)[2] == (char*)&likeInfoAlt.matchSet );
+
if( nExpr<3 ){
aWc[3] = 0;
}else{
Expr *pEscape = pExpr->x.pList->a[2].pExpr;
char *zEscape;
if( pEscape->op!=TK_STRING ) return 0;
+ assert( !ExprHasProperty(pEscape, EP_IntValue) );
zEscape = pEscape->u.zToken;
if( zEscape[0]==0 || zEscape[1]!=0 ) return 0;
+ if( zEscape[0]==aWc[0] ) return 0;
+ if( zEscape[0]==aWc[1] ) return 0;
aWc[3] = zEscape[0];
}
- /* The memcpy() statement assumes that the wildcard characters are
- ** the first three statements in the compareInfo structure. The
- ** asserts() that follow verify that assumption
- */
- memcpy(aWc, pDef->pUserData, 3);
- assert( (char*)&likeInfoAlt == (char*)&likeInfoAlt.matchAll );
- assert( &((char*)&likeInfoAlt)[1] == (char*)&likeInfoAlt.matchOne );
- assert( &((char*)&likeInfoAlt)[2] == (char*)&likeInfoAlt.matchSet );
*pIsNocase = (pDef->funcFlags & SQLITE_FUNC_CASE)==0;
return 1;
}
+/* Mathematical Constants */
+#ifndef M_PI
+# define M_PI 3.141592653589793238462643383279502884
+#endif
+#ifndef M_LN10
+# define M_LN10 2.302585092994045684017991454684364208
+#endif
+#ifndef M_LN2
+# define M_LN2 0.693147180559945309417232121458176568
+#endif
+
+
+/* Extra math functions that require linking with -lm
+*/
+#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
+/*
+** Implementation SQL functions:
+**
+** ceil(X)
+** ceiling(X)
+** floor(X)
+**
+** The sqlite3_user_data() pointer is a pointer to the libm implementation
+** of the underlying C function.
+*/
+static void ceilingFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( argc==1 );
+ switch( sqlite3_value_numeric_type(argv[0]) ){
+ case SQLITE_INTEGER: {
+ sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
+ break;
+ }
+ case SQLITE_FLOAT: {
+ double (*x)(double) = (double(*)(double))sqlite3_user_data(context);
+ sqlite3_result_double(context, x(sqlite3_value_double(argv[0])));
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+/*
+** On some systems, ceil() and floor() are intrinsic function. You are
+** unable to take a pointer to these functions. Hence, we here wrap them
+** in our own actual functions.
+*/
+static double xCeil(double x){ return ceil(x); }
+static double xFloor(double x){ return floor(x); }
+
+/*
+** Some systems do not have log2() and log10() in their standard math
+** libraries.
+*/
+#if defined(HAVE_LOG10) && HAVE_LOG10==0
+# define log10(X) (0.4342944819032517867*log(X))
+#endif
+#if defined(HAVE_LOG2) && HAVE_LOG2==0
+# define log2(X) (1.442695040888963456*log(X))
+#endif
+
+
+/*
+** Implementation of SQL functions:
+**
+** ln(X) - natural logarithm
+** log(X) - log X base 10
+** log10(X) - log X base 10
+** log(B,X) - log X base B
+*/
+static void logFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ double x, b, ans;
+ assert( argc==1 || argc==2 );
+ switch( sqlite3_value_numeric_type(argv[0]) ){
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT:
+ x = sqlite3_value_double(argv[0]);
+ if( x<=0.0 ) return;
+ break;
+ default:
+ return;
+ }
+ if( argc==2 ){
+ switch( sqlite3_value_numeric_type(argv[0]) ){
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT:
+ b = log(x);
+ if( b<=0.0 ) return;
+ x = sqlite3_value_double(argv[1]);
+ if( x<=0.0 ) return;
+ break;
+ default:
+ return;
+ }
+ ans = log(x)/b;
+ }else{
+ switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){
+ case 1:
+ ans = log10(x);
+ break;
+ case 2:
+ ans = log2(x);
+ break;
+ default:
+ ans = log(x);
+ break;
+ }
+ }
+ sqlite3_result_double(context, ans);
+}
+
+/*
+** Functions to converts degrees to radians and radians to degrees.
+*/
+static double degToRad(double x){ return x*(M_PI/180.0); }
+static double radToDeg(double x){ return x*(180.0/M_PI); }
+
+/*
+** Implementation of 1-argument SQL math functions:
+**
+** exp(X) - Compute e to the X-th power
+*/
+static void math1Func(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int type0;
+ double v0, ans;
+ double (*x)(double);
+ assert( argc==1 );
+ type0 = sqlite3_value_numeric_type(argv[0]);
+ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
+ v0 = sqlite3_value_double(argv[0]);
+ x = (double(*)(double))sqlite3_user_data(context);
+ ans = x(v0);
+ sqlite3_result_double(context, ans);
+}
+
+/*
+** Implementation of 2-argument SQL math functions:
+**
+** power(X,Y) - Compute X to the Y-th power
+*/
+static void math2Func(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int type0, type1;
+ double v0, v1, ans;
+ double (*x)(double,double);
+ assert( argc==2 );
+ type0 = sqlite3_value_numeric_type(argv[0]);
+ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
+ type1 = sqlite3_value_numeric_type(argv[1]);
+ if( type1!=SQLITE_INTEGER && type1!=SQLITE_FLOAT ) return;
+ v0 = sqlite3_value_double(argv[0]);
+ v1 = sqlite3_value_double(argv[1]);
+ x = (double(*)(double,double))sqlite3_user_data(context);
+ ans = x(v0, v1);
+ sqlite3_result_double(context, ans);
+}
+
+/*
+** Implementation of 0-argument pi() function.
+*/
+static void piFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( argc==0 );
+ (void)argv;
+ sqlite3_result_double(context, M_PI);
+}
+
+#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
+
+/*
+** Implementation of sign(X) function.
+*/
+static void signFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int type0;
+ double x;
+ UNUSED_PARAMETER(argc);
+ assert( argc==1 );
+ type0 = sqlite3_value_numeric_type(argv[0]);
+ if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
+ x = sqlite3_value_double(argv[0]);
+ sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0);
+}
+
+#ifdef SQLITE_DEBUG
+/*
+** Implementation of fpdecode(x,y,z) function.
+**
+** x is a real number that is to be decoded. y is the precision.
+** z is the maximum real precision.
+*/
+static void fpdecodeFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ FpDecode s;
+ double x;
+ int y, z;
+ char zBuf[100];
+ UNUSED_PARAMETER(argc);
+ assert( argc==3 );
+ x = sqlite3_value_double(argv[0]);
+ y = sqlite3_value_int(argv[1]);
+ z = sqlite3_value_int(argv[2]);
+ sqlite3FpDecode(&s, x, y, z);
+ if( s.isSpecial==2 ){
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "NaN");
+ }else{
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%c%.*s/%d", s.sign, s.n, s.z, s.iDP);
+ }
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+}
+#endif /* SQLITE_DEBUG */
+
/*
** All of the FuncDef structures in the aBuiltinFunc[] array above
** to the global function hash table. This occurs at start-time (as
@@ -113931,12 +130420,20 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
** For peak efficiency, put the most frequently used function last.
*/
static FuncDef aBuiltinFunc[] = {
+/***** Functions only available with SQLITE_TESTCTRL_INTERNAL_FUNCTIONS *****/
+#if !defined(SQLITE_UNTESTABLE)
+ TEST_FUNC(implies_nonnull_row, 2, INLINEFUNC_implies_nonnull_row, 0),
+ TEST_FUNC(expr_compare, 2, INLINEFUNC_expr_compare, 0),
+ TEST_FUNC(expr_implies_expr, 2, INLINEFUNC_expr_implies_expr, 0),
+ TEST_FUNC(affinity, 1, INLINEFUNC_affinity, 0),
+#endif /* !defined(SQLITE_UNTESTABLE) */
+/***** Regular functions *****/
#ifdef SQLITE_SOUNDEX
FUNCTION(soundex, 1, 0, 0, soundexFunc ),
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
- VFUNCTION(load_extension, 1, 0, 0, loadExt ),
- VFUNCTION(load_extension, 2, 0, 0, loadExt ),
+ SFUNCTION(load_extension, 1, 0, 0, loadExt ),
+ SFUNCTION(load_extension, 2, 0, 0, loadExt ),
#endif
#if SQLITE_USER_AUTHENTICATION
FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ),
@@ -113945,15 +130442,11 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
- FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
- FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
- FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
-#ifdef SQLITE_DEBUG
- FUNCTION2(affinity, 1, 0, 0, noopFunc, SQLITE_FUNC_AFFINITY),
-#endif
+ INLINE_FUNC(unlikely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
+ INLINE_FUNC(likelihood, 2, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
+ INLINE_FUNC(likely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
- FUNCTION2(sqlite_offset, 1, 0, 0, noopFunc, SQLITE_FUNC_OFFSET|
- SQLITE_FUNC_TYPEOF),
+ INLINE_FUNC(sqlite_offset, 1, INLINEFUNC_sqlite_offset, 0 ),
#endif
FUNCTION(ltrim, 1, 1, 0, trimFunc ),
FUNCTION(ltrim, 2, 1, 0, trimFunc ),
@@ -113964,18 +130457,24 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(min, -1, 0, 1, minmaxFunc ),
FUNCTION(min, 0, 0, 1, 0 ),
WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
- SQLITE_FUNC_MINMAX ),
+ SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ),
FUNCTION(max, -1, 1, 1, minmaxFunc ),
FUNCTION(max, 0, 1, 1, 0 ),
WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
- SQLITE_FUNC_MINMAX ),
+ SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ),
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
+ FUNCTION2(subtype, 1, 0, 0, subtypeFunc, SQLITE_FUNC_TYPEOF),
FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
+ FUNCTION2(octet_length, 1, 0, 0, bytelengthFunc,SQLITE_FUNC_BYTELEN),
FUNCTION(instr, 2, 0, 0, instrFunc ),
FUNCTION(printf, -1, 0, 0, printfFunc ),
+ FUNCTION(format, -1, 0, 0, printfFunc ),
FUNCTION(unicode, 1, 0, 0, unicodeFunc ),
FUNCTION(char, -1, 0, 0, charFunc ),
FUNCTION(abs, 1, 0, 0, absFunc ),
+#ifdef SQLITE_DEBUG
+ FUNCTION(fpdecode, 3, 0, 0, fpdecodeFunc ),
+#endif
#ifndef SQLITE_OMIT_FLOATING_POINT
FUNCTION(round, 1, 0, 0, roundFunc ),
FUNCTION(round, 2, 0, 0, roundFunc ),
@@ -113983,7 +130482,14 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(upper, 1, 0, 0, upperFunc ),
FUNCTION(lower, 1, 0, 0, lowerFunc ),
FUNCTION(hex, 1, 0, 0, hexFunc ),
- FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
+ FUNCTION(unhex, 1, 0, 0, unhexFunc ),
+ FUNCTION(unhex, 2, 0, 0, unhexFunc ),
+ FUNCTION(concat, -1, 0, 0, concatFunc ),
+ FUNCTION(concat, 0, 0, 0, 0 ),
+ FUNCTION(concat_ws, -1, 0, 0, concatwsFunc ),
+ FUNCTION(concat_ws, 0, 0, 0, 0 ),
+ FUNCTION(concat_ws, 1, 0, 0, 0 ),
+ INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ),
VFUNCTION(random, 0, 0, 0, randomFunc ),
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
@@ -113998,18 +130504,23 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
FUNCTION(substr, 2, 0, 0, substrFunc ),
FUNCTION(substr, 3, 0, 0, substrFunc ),
+ FUNCTION(substring, 2, 0, 0, substrFunc ),
+ FUNCTION(substring, 3, 0, 0, substrFunc ),
WAGGREGATE(sum, 1,0,0, sumStep, sumFinalize, sumFinalize, sumInverse, 0),
WAGGREGATE(total, 1,0,0, sumStep,totalFinalize,totalFinalize,sumInverse, 0),
WAGGREGATE(avg, 1,0,0, sumStep, avgFinalize, avgFinalize, sumInverse, 0),
- WAGGREGATE(count, 0,0,0, countStep,
- countFinalize, countFinalize, countInverse, SQLITE_FUNC_COUNT ),
- WAGGREGATE(count, 1,0,0, countStep,
- countFinalize, countFinalize, countInverse, 0 ),
- WAGGREGATE(group_concat, 1, 0, 0, groupConcatStep,
+ WAGGREGATE(count, 0,0,0, countStep,
+ countFinalize, countFinalize, countInverse,
+ SQLITE_FUNC_COUNT|SQLITE_FUNC_ANYORDER ),
+ WAGGREGATE(count, 1,0,0, countStep,
+ countFinalize, countFinalize, countInverse, SQLITE_FUNC_ANYORDER ),
+ WAGGREGATE(group_concat, 1, 0, 0, groupConcatStep,
+ groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
+ WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
- WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
+ WAGGREGATE(string_agg, 2, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
-
+
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
#ifdef SQLITE_CASE_SENSITIVE_LIKE
LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
@@ -114023,16 +130534,52 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
#endif
FUNCTION(coalesce, 1, 0, 0, 0 ),
FUNCTION(coalesce, 0, 0, 0, 0 ),
- FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
+#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
+ MFUNCTION(ceil, 1, xCeil, ceilingFunc ),
+ MFUNCTION(ceiling, 1, xCeil, ceilingFunc ),
+ MFUNCTION(floor, 1, xFloor, ceilingFunc ),
+#if SQLITE_HAVE_C99_MATH_FUNCS
+ MFUNCTION(trunc, 1, trunc, ceilingFunc ),
+#endif
+ FUNCTION(ln, 1, 0, 0, logFunc ),
+ FUNCTION(log, 1, 1, 0, logFunc ),
+ FUNCTION(log10, 1, 1, 0, logFunc ),
+ FUNCTION(log2, 1, 2, 0, logFunc ),
+ FUNCTION(log, 2, 0, 0, logFunc ),
+ MFUNCTION(exp, 1, exp, math1Func ),
+ MFUNCTION(pow, 2, pow, math2Func ),
+ MFUNCTION(power, 2, pow, math2Func ),
+ MFUNCTION(mod, 2, fmod, math2Func ),
+ MFUNCTION(acos, 1, acos, math1Func ),
+ MFUNCTION(asin, 1, asin, math1Func ),
+ MFUNCTION(atan, 1, atan, math1Func ),
+ MFUNCTION(atan2, 2, atan2, math2Func ),
+ MFUNCTION(cos, 1, cos, math1Func ),
+ MFUNCTION(sin, 1, sin, math1Func ),
+ MFUNCTION(tan, 1, tan, math1Func ),
+ MFUNCTION(cosh, 1, cosh, math1Func ),
+ MFUNCTION(sinh, 1, sinh, math1Func ),
+ MFUNCTION(tanh, 1, tanh, math1Func ),
+#if SQLITE_HAVE_C99_MATH_FUNCS
+ MFUNCTION(acosh, 1, acosh, math1Func ),
+ MFUNCTION(asinh, 1, asinh, math1Func ),
+ MFUNCTION(atanh, 1, atanh, math1Func ),
+#endif
+ MFUNCTION(sqrt, 1, sqrt, math1Func ),
+ MFUNCTION(radians, 1, degToRad, math1Func ),
+ MFUNCTION(degrees, 1, radToDeg, math1Func ),
+ FUNCTION(pi, 0, 0, 0, piFunc ),
+#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
+ FUNCTION(sign, 1, 0, 0, signFunc ),
+ INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ),
+ INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ),
};
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions();
#endif
sqlite3WindowFunctions();
-#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
- sqlite3AnalyzeFunctions();
-#endif
sqlite3RegisterDateTimeFunctions();
+ sqlite3RegisterJsonFunctions();
sqlite3InsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc));
#if 0 /* Enable to print out how the built-in functions are hashed */
@@ -114044,6 +130591,7 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
for(p=sqlite3BuiltinFunctions.a[i]; p; p=p->u.pHash){
int n = sqlite3Strlen30(p->zName);
int h = p->zName[0] + n;
+ assert( p->funcFlags & SQLITE_FUNC_BUILTIN );
printf(" %s(%d)", p->zName, h);
}
printf("\n");
@@ -114079,25 +130627,25 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
** Foreign keys in SQLite come in two flavours: deferred and immediate.
** If an immediate foreign key constraint is violated,
** SQLITE_CONSTRAINT_FOREIGNKEY is returned and the current
-** statement transaction rolled back. If a
-** deferred foreign key constraint is violated, no action is taken
-** immediately. However if the application attempts to commit the
+** statement transaction rolled back. If a
+** deferred foreign key constraint is violated, no action is taken
+** immediately. However if the application attempts to commit the
** transaction before fixing the constraint violation, the attempt fails.
**
** Deferred constraints are implemented using a simple counter associated
-** with the database handle. The counter is set to zero each time a
-** database transaction is opened. Each time a statement is executed
+** with the database handle. The counter is set to zero each time a
+** database transaction is opened. Each time a statement is executed
** that causes a foreign key violation, the counter is incremented. Each
** time a statement is executed that removes an existing violation from
** the database, the counter is decremented. When the transaction is
** committed, the commit fails if the current value of the counter is
** greater than zero. This scheme has two big drawbacks:
**
-** * When a commit fails due to a deferred foreign key constraint,
+** * When a commit fails due to a deferred foreign key constraint,
** there is no way to tell which foreign constraint is not satisfied,
** or which row it is not satisfied for.
**
-** * If the database contains foreign key violations when the
+** * If the database contains foreign key violations when the
** transaction is opened, this may cause the mechanism to malfunction.
**
** Despite these problems, this approach is adopted as it seems simpler
@@ -114109,26 +130657,26 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
** the parent table for a match. If none is found increment the
** constraint counter.
**
-** I.2) For each FK for which the table is the parent table,
+** I.2) For each FK for which the table is the parent table,
** search the child table for rows that correspond to the new
** row in the parent table. Decrement the counter for each row
** found (as the constraint is now satisfied).
**
** DELETE operations:
**
-** D.1) For each FK for which the table is the child table,
-** search the parent table for a row that corresponds to the
-** deleted row in the child table. If such a row is not found,
+** D.1) For each FK for which the table is the child table,
+** search the parent table for a row that corresponds to the
+** deleted row in the child table. If such a row is not found,
** decrement the counter.
**
-** D.2) For each FK for which the table is the parent table, search
-** the child table for rows that correspond to the deleted row
+** D.2) For each FK for which the table is the parent table, search
+** the child table for rows that correspond to the deleted row
** in the parent table. For each found increment the counter.
**
** UPDATE operations:
**
** An UPDATE command requires that all 4 steps above are taken, but only
-** for FK constraints for which the affected columns are actually
+** for FK constraints for which the affected columns are actually
** modified (values must be compared at runtime).
**
** Note that I.1 and D.1 are very similar operations, as are I.2 and D.2.
@@ -114137,10 +130685,10 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
** For the purposes of immediate FK constraints, the OR REPLACE conflict
** resolution is considered to delete rows before the new row is inserted.
** If a delete caused by OR REPLACE violates an FK constraint, an exception
-** is thrown, even if the FK constraint would be satisfied after the new
+** is thrown, even if the FK constraint would be satisfied after the new
** row is inserted.
**
-** Immediate constraints are usually handled similarly. The only difference
+** Immediate constraints are usually handled similarly. The only difference
** is that the counter used is stored as part of each individual statement
** object (struct Vdbe). If, after the statement has run, its immediate
** constraint counter is greater than zero,
@@ -114151,7 +130699,7 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
** INSERT violates a foreign key constraint. This is necessary as such
** an INSERT does not open a statement transaction.
**
-** TODO: How should dropping a table be handled? How should renaming a
+** TODO: How should dropping a table be handled? How should renaming a
** table be handled?
**
**
@@ -114162,7 +130710,7 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
** for those two operations needs to know whether or not the operation
** requires any FK processing and, if so, which columns of the original
** row are required by the FK processing VDBE code (i.e. if FKs were
-** implemented using triggers, which of the old.* columns would be
+** implemented using triggers, which of the old.* columns would be
** accessed). No information is required by the code-generator before
** coding an INSERT operation. The functions used by the UPDATE/DELETE
** generation code to query for this information are:
@@ -114199,13 +130747,13 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
/*
** A foreign key constraint requires that the key columns in the parent
** table are collectively subject to a UNIQUE or PRIMARY KEY constraint.
-** Given that pParent is the parent table for foreign key constraint pFKey,
-** search the schema for a unique index on the parent key columns.
+** Given that pParent is the parent table for foreign key constraint pFKey,
+** search the schema for a unique index on the parent key columns.
+**
+** If successful, zero is returned. If the parent key is an INTEGER PRIMARY
+** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx
+** is set to point to the unique index.
**
-** If successful, zero is returned. If the parent key is an INTEGER PRIMARY
-** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx
-** is set to point to the unique index.
-**
** If the parent key consists of a single column (the foreign key constraint
** is not a composite foreign key), output variable *paiCol is set to NULL.
** Otherwise, it is set to point to an allocated array of size N, where
@@ -114228,8 +130776,8 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
** PRIMARY KEY, or
**
** 4) No parent key columns were provided explicitly as part of the
-** foreign key definition, and the PRIMARY KEY of the parent table
-** consists of a different number of columns to the child key in
+** foreign key definition, and the PRIMARY KEY of the parent table
+** consists of a different number of columns to the child key in
** the child table.
**
** then non-zero is returned, and a "foreign key mismatch" error loaded
@@ -114253,9 +130801,9 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex(
assert( !paiCol || *paiCol==0 );
assert( pParse );
- /* If this is a non-composite (single column) foreign key, check if it
- ** maps to the INTEGER PRIMARY KEY of table pParent. If so, leave *ppIdx
- ** and *paiCol set to zero and return early.
+ /* If this is a non-composite (single column) foreign key, check if it
+ ** maps to the INTEGER PRIMARY KEY of table pParent. If so, leave *ppIdx
+ ** and *paiCol set to zero and return early.
**
** Otherwise, for a composite foreign key (more than one column), allocate
** space for the aiCol array (returned via output parameter *paiCol).
@@ -114264,14 +130812,16 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex(
if( nCol==1 ){
/* The FK maps to the IPK if any of the following are true:
**
- ** 1) There is an INTEGER PRIMARY KEY column and the FK is implicitly
+ ** 1) There is an INTEGER PRIMARY KEY column and the FK is implicitly
** mapped to the primary key of table pParent, or
** 2) The FK is explicitly mapped to a column declared as INTEGER
** PRIMARY KEY.
*/
if( pParent->iPKey>=0 ){
if( !zKey ) return 0;
- if( !sqlite3StrICmp(pParent->aCol[pParent->iPKey].zName, zKey) ) return 0;
+ if( !sqlite3StrICmp(pParent->aCol[pParent->iPKey].zCnName, zKey) ){
+ return 0;
+ }
}
}else if( paiCol ){
assert( nCol>1 );
@@ -114281,14 +130831,14 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex(
}
for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->nKeyCol==nCol && IsUniqueIndex(pIdx) && pIdx->pPartIdxWhere==0 ){
+ if( pIdx->nKeyCol==nCol && IsUniqueIndex(pIdx) && pIdx->pPartIdxWhere==0 ){
/* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number
** of columns. If each indexed column corresponds to a foreign key
** column of pFKey, then this index is a winner. */
if( zKey==0 ){
- /* If zKey is NULL, then this foreign key is implicitly mapped to
- ** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be
+ /* If zKey is NULL, then this foreign key is implicitly mapped to
+ ** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be
** identified by the test. */
if( IsPrimaryKeyIndex(pIdx) ){
if( aiCol ){
@@ -114313,11 +130863,11 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex(
/* If the index uses a collation sequence that is different from
** the default collation sequence for the column, this index is
** unusable. Bail out early in this case. */
- zDfltColl = pParent->aCol[iCol].zColl;
+ zDfltColl = sqlite3ColumnColl(&pParent->aCol[iCol]);
if( !zDfltColl ) zDfltColl = sqlite3StrBINARY;
if( sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break;
- zIdxCol = pParent->aCol[iCol].zName;
+ zIdxCol = pParent->aCol[iCol].zCnName;
for(j=0; j<nCol; j++){
if( sqlite3StrICmp(pFKey->aCol[j].zCol, zIdxCol)==0 ){
if( aiCol ) aiCol[i] = pFKey->aCol[j].iFrom;
@@ -114346,15 +130896,15 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex(
}
/*
-** This function is called when a row is inserted into or deleted from the
-** child table of foreign key constraint pFKey. If an SQL UPDATE is executed
+** This function is called when a row is inserted into or deleted from the
+** child table of foreign key constraint pFKey. If an SQL UPDATE is executed
** on the child table of pFKey, this function is invoked twice for each row
** affected - once to "delete" the old row, and then again to "insert" the
** new row.
**
** Each time it is called, this function generates VDBE code to locate the
-** row in the parent table that corresponds to the row being inserted into
-** or deleted from the child table. If the parent row can be found, no
+** row in the parent table that corresponds to the row being inserted into
+** or deleted from the child table. If the parent row can be found, no
** special action is taken. Otherwise, if the parent row can *not* be
** found in the parent table:
**
@@ -114368,7 +130918,7 @@ SQLITE_PRIVATE int sqlite3FkLocateIndex(
**
** DELETE deferred Decrement the "deferred constraint counter".
**
-** These operations are identified in the comment at the top of this file
+** These operations are identified in the comment at the top of this file
** (fkey.c) as "I.1" and "D.1".
*/
static void fkLookupParent(
@@ -114385,27 +130935,27 @@ static void fkLookupParent(
int i; /* Iterator variable */
Vdbe *v = sqlite3GetVdbe(pParse); /* Vdbe to add code to */
int iCur = pParse->nTab - 1; /* Cursor number to use */
- int iOk = sqlite3VdbeMakeLabel(v); /* jump here if parent key found */
+ int iOk = sqlite3VdbeMakeLabel(pParse); /* jump here if parent key found */
sqlite3VdbeVerifyAbortable(v,
(!pFKey->isDeferred
&& !(pParse->db->flags & SQLITE_DeferFKs)
- && !pParse->pToplevel
+ && !pParse->pToplevel
&& !pParse->isMultiWrite) ? OE_Abort : OE_Ignore);
/* If nIncr is less than zero, then check at runtime if there are any
** outstanding constraints to resolve. If there are not, there is no need
** to check if deleting this row resolves any outstanding violations.
**
- ** Check if any of the key columns in the child table row are NULL. If
- ** any are, then the constraint is considered satisfied. No need to
+ ** Check if any of the key columns in the child table row are NULL. If
+ ** any are, then the constraint is considered satisfied. No need to
** search for a matching row in the parent table. */
if( nIncr<0 ){
sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, iOk);
VdbeCoverage(v);
}
for(i=0; i<pFKey->nCol; i++){
- int iReg = aiCol[i] + regData + 1;
+ int iReg = sqlite3TableColumnToStorage(pFKey->pFrom,aiCol[i]) + regData + 1;
sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk); VdbeCoverage(v);
}
@@ -114415,16 +130965,17 @@ static void fkLookupParent(
** column of the parent table (table pTab). */
int iMustBeInt; /* Address of MustBeInt instruction */
int regTemp = sqlite3GetTempReg(pParse);
-
- /* Invoke MustBeInt to coerce the child key value to an integer (i.e.
+
+ /* Invoke MustBeInt to coerce the child key value to an integer (i.e.
** apply the affinity of the parent key). If this fails, then there
** is no matching parent key. Before using MustBeInt, make a copy of
** the value. Otherwise, the value inserted into the child key column
** will have INTEGER affinity applied to it, which may not be correct. */
- sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp);
+ sqlite3VdbeAddOp2(v, OP_SCopy,
+ sqlite3TableColumnToStorage(pFKey->pFrom,aiCol[0])+1+regData, regTemp);
iMustBeInt = sqlite3VdbeAddOp2(v, OP_MustBeInt, regTemp, 0);
VdbeCoverage(v);
-
+
/* If the parent table is the same as the child table, and we are about
** to increment the constraint-counter (i.e. this is an INSERT operation),
** then check if the row being inserted matches itself. If so, do not
@@ -114433,7 +130984,7 @@ static void fkLookupParent(
sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
}
-
+
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); VdbeCoverage(v);
sqlite3VdbeGoto(v, iOk);
@@ -114443,20 +130994,21 @@ static void fkLookupParent(
}else{
int nCol = pFKey->nCol;
int regTemp = sqlite3GetTempRange(pParse, nCol);
- int regRec = sqlite3GetTempReg(pParse);
-
+
sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb);
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
for(i=0; i<nCol; i++){
- sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i);
+ sqlite3VdbeAddOp2(v, OP_Copy,
+ sqlite3TableColumnToStorage(pFKey->pFrom, aiCol[i])+1+regData,
+ regTemp+i);
}
-
+
/* If the parent table is the same as the child table, and we are about
** to increment the constraint-counter (i.e. this is an INSERT operation),
** then check if the row being inserted matches itself. If so, do not
- ** increment the constraint-counter.
+ ** increment the constraint-counter.
**
- ** If any of the parent-key values are NULL, then the row cannot match
+ ** If any of the parent-key values are NULL, then the row cannot match
** itself. So set JUMPIFNULL to make sure we do the OP_Found if any
** of the parent-key values are NULL (at this point it is known that
** none of the child key values are).
@@ -114464,8 +131016,11 @@ static void fkLookupParent(
if( pTab==pFKey->pFrom && nIncr==1 ){
int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1;
for(i=0; i<nCol; i++){
- int iChild = aiCol[i]+1+regData;
- int iParent = pIdx->aiColumn[i]+1+regData;
+ int iChild = sqlite3TableColumnToStorage(pFKey->pFrom,aiCol[i])
+ +1+regData;
+ int iParent = 1+regData;
+ iParent += sqlite3TableColumnToStorage(pIdx->pTable,
+ pIdx->aiColumn[i]);
assert( pIdx->aiColumn[i]>=0 );
assert( aiCol[i]!=pTab->iPKey );
if( pIdx->aiColumn[i]==pTab->iPKey ){
@@ -114477,19 +131032,18 @@ static void fkLookupParent(
}
sqlite3VdbeGoto(v, iOk);
}
-
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec,
+
+ sqlite3VdbeAddOp4(v, OP_Affinity, regTemp, nCol, 0,
sqlite3IndexAffinityStr(pParse->db,pIdx), nCol);
- sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v);
-
- sqlite3ReleaseTempReg(pParse, regRec);
+ sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regTemp, nCol);
+ VdbeCoverage(v);
sqlite3ReleaseTempRange(pParse, regTemp, nCol);
}
}
if( !pFKey->isDeferred && !(pParse->db->flags & SQLITE_DeferFKs)
- && !pParse->pToplevel
- && !pParse->isMultiWrite
+ && !pParse->pToplevel
+ && !pParse->isMultiWrite
){
/* Special case: If this is an INSERT statement that will insert exactly
** one row into the table, raise a constraint immediately instead of
@@ -114533,14 +131087,14 @@ static Expr *exprTableRegister(
if( pExpr ){
if( iCol>=0 && iCol!=pTab->iPKey ){
pCol = &pTab->aCol[iCol];
- pExpr->iTable = regBase + iCol + 1;
- pExpr->affinity = pCol->affinity;
- zColl = pCol->zColl;
+ pExpr->iTable = regBase + sqlite3TableColumnToStorage(pTab,iCol) + 1;
+ pExpr->affExpr = pCol->affinity;
+ zColl = sqlite3ColumnColl(pCol);
if( zColl==0 ) zColl = db->pDfltColl->zName;
pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl);
}else{
pExpr->iTable = regBase;
- pExpr->affinity = SQLITE_AFF_INTEGER;
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
}
}
return pExpr;
@@ -114558,6 +131112,7 @@ static Expr *exprTableColumn(
){
Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0);
if( pExpr ){
+ assert( ExprUseYTab(pExpr) );
pExpr->y.pTab = pTab;
pExpr->iTable = iCursor;
pExpr->iColumn = iCol;
@@ -114567,7 +131122,7 @@ static Expr *exprTableColumn(
/*
** This function is called to generate code executed when a row is deleted
-** from the parent table of foreign key constraint pFKey and, if pFKey is
+** from the parent table of foreign key constraint pFKey and, if pFKey is
** deferred, when a row is inserted into the same table. When generating
** code for an SQL UPDATE operation, this function may be called twice -
** once to "delete" the old row and once to "insert" the new row.
@@ -114583,18 +131138,14 @@ static Expr *exprTableColumn(
** Operation | FK type | Action taken
** --------------------------------------------------------------------------
** DELETE immediate Increment the "immediate constraint counter".
-** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
-** throw a "FOREIGN KEY constraint failed" exception.
**
** INSERT immediate Decrement the "immediate constraint counter".
**
** DELETE deferred Increment the "deferred constraint counter".
-** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
-** throw a "FOREIGN KEY constraint failed" exception.
**
** INSERT deferred Decrement the "deferred constraint counter".
**
-** These operations are identified in the comment at the top of this file
+** These operations are identified in the comment at the top of this file
** (fkey.c) as "I.2" and "D.2".
*/
static void fkScanChildren(
@@ -114637,17 +131188,17 @@ static void fkScanChildren(
Expr *pLeft; /* Value from parent table row */
Expr *pRight; /* Column ref to child table */
Expr *pEq; /* Expression (pLeft = pRight) */
- i16 iCol; /* Index of column in child table */
+ i16 iCol; /* Index of column in child table */
const char *zCol; /* Name of column in child table */
iCol = pIdx ? pIdx->aiColumn[i] : -1;
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
assert( iCol>=0 );
- zCol = pFKey->pFrom->aCol[iCol].zName;
+ zCol = pFKey->pFrom->aCol[iCol].zCnName;
pRight = sqlite3Expr(db, TK_ID, zCol);
pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight);
- pWhere = sqlite3ExprAnd(db, pWhere, pEq);
+ pWhere = sqlite3ExprAnd(pParse, pWhere, pEq);
}
/* If the child table is the same as the parent table, then add terms
@@ -114658,8 +131209,11 @@ static void fkScanChildren(
** NOT( $current_a==a AND $current_b==b AND ... )
**
** The first form is used for rowid tables. The second form is used
- ** for WITHOUT ROWID tables. In the second form, the primary key is
- ** (a,b,...)
+ ** for WITHOUT ROWID tables. In the second form, the *parent* key is
+ ** (a,b,...). Either the parent or primary key could be used to
+ ** uniquely identify the current row, but the parent key is more convenient
+ ** as the required values have already been loaded into registers
+ ** by the caller.
*/
if( pTab==pFKey->pFrom && nIncr>0 ){
Expr *pNe; /* Expression (pLeft != pRight) */
@@ -114671,19 +131225,18 @@ static void fkScanChildren(
pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight);
}else{
Expr *pEq, *pAll = 0;
- Index *pPk = sqlite3PrimaryKeyIndex(pTab);
assert( pIdx!=0 );
- for(i=0; i<pPk->nKeyCol; i++){
+ for(i=0; i<pIdx->nKeyCol; i++){
i16 iCol = pIdx->aiColumn[i];
assert( iCol>=0 );
pLeft = exprTableRegister(pParse, pTab, regData, iCol);
- pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol);
- pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight);
- pAll = sqlite3ExprAnd(db, pAll, pEq);
+ pRight = sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zCnName);
+ pEq = sqlite3PExpr(pParse, TK_IS, pLeft, pRight);
+ pAll = sqlite3ExprAnd(pParse, pAll, pEq);
}
pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0);
}
- pWhere = sqlite3ExprAnd(db, pWhere, pNe);
+ pWhere = sqlite3ExprAnd(pParse, pWhere, pNe);
}
/* Resolve the references in the WHERE clause. */
@@ -114696,7 +131249,7 @@ static void fkScanChildren(
** clause. For each row found, increment either the deferred or immediate
** foreign key constraint counter. */
if( pParse->nErr==0 ){
- pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0, 0);
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr);
if( pWInfo ){
sqlite3WhereEnd(pWInfo);
@@ -114706,7 +131259,7 @@ static void fkScanChildren(
/* Clean up the WHERE clause constructed above. */
sqlite3ExprDelete(db, pWhere);
if( iFkIfZero ){
- sqlite3VdbeJumpHere(v, iFkIfZero);
+ sqlite3VdbeJumpHereOrPopInst(v, iFkIfZero);
}
}
@@ -114729,7 +131282,7 @@ SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *pTab){
}
/*
-** The second argument is a Trigger structure allocated by the
+** The second argument is a Trigger structure allocated by the
** fkActionTrigger() routine. This function deletes the Trigger structure
** and all of its sub-components.
**
@@ -114748,6 +131301,25 @@ static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){
}
/*
+** Clear the apTrigger[] cache of CASCADE triggers for all foreign keys
+** in a particular database. This needs to happen when the schema
+** changes.
+*/
+SQLITE_PRIVATE void sqlite3FkClearTriggerCache(sqlite3 *db, int iDb){
+ HashElem *k;
+ Hash *pHash = &db->aDb[iDb].pSchema->tblHash;
+ for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k)){
+ Table *pTab = sqliteHashData(k);
+ FKey *pFKey;
+ if( !IsOrdinaryTable(pTab) ) continue;
+ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
+ fkTriggerDelete(db, pFKey->apTrigger[0]); pFKey->apTrigger[0] = 0;
+ fkTriggerDelete(db, pFKey->apTrigger[1]); pFKey->apTrigger[1] = 0;
+ }
+ }
+}
+
+/*
** This function is called to generate code that runs when table pTab is
** being dropped from the database. The SrcList passed as the second argument
** to this function contains a single entry guaranteed to resolve to
@@ -114757,7 +131329,7 @@ static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){
**
** (a) The table is the parent table of a FK constraint, or
** (b) The table is the child table of a deferred FK constraint and it is
-** determined at runtime that there are outstanding deferred FK
+** determined at runtime that there are outstanding deferred FK
** constraint violations in the database,
**
** then the equivalent of "DELETE FROM <tbl>" is executed before dropping
@@ -114766,24 +131338,24 @@ static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){
*/
SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
sqlite3 *db = pParse->db;
- if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) ){
+ if( (db->flags&SQLITE_ForeignKeys) && IsOrdinaryTable(pTab) ){
int iSkip = 0;
Vdbe *v = sqlite3GetVdbe(pParse);
assert( v ); /* VDBE has already been allocated */
- assert( pTab->pSelect==0 ); /* Not a view */
+ assert( IsOrdinaryTable(pTab) );
if( sqlite3FkReferences(pTab)==0 ){
/* Search for a deferred foreign key constraint for which this table
- ** is the child table. If one cannot be found, return without
+ ** is the child table. If one cannot be found, return without
** generating any VDBE code. If one can be found, then jump over
** the entire DELETE if there are no outstanding deferred constraints
** when this statement is run. */
FKey *p;
- for(p=pTab->pFKey; p; p=p->pNextFrom){
+ for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){
if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break;
}
if( !p ) return;
- iSkip = sqlite3VdbeMakeLabel(v);
+ iSkip = sqlite3VdbeMakeLabel(pParse);
sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); VdbeCoverage(v);
}
@@ -114791,10 +131363,10 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa
sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0, 0, 0);
pParse->disableTriggers = 0;
- /* If the DELETE has generated immediate foreign key constraint
+ /* If the DELETE has generated immediate foreign key constraint
** violations, halt the VDBE and return an error at this point, before
** any modifications to the schema are made. This is because statement
- ** transactions are not able to rollback schema changes.
+ ** transactions are not able to rollback schema changes.
**
** If the SQLITE_DeferFKs flag is set, then this is not required, as
** the statement transaction will not be rolled back even if FK
@@ -114818,7 +131390,7 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa
/*
** The second argument points to an FKey object representing a foreign key
** for which pTab is the child table. An UPDATE statement against pTab
-** is currently being processed. For each column of the table that is
+** is currently being processed. For each column of the table that is
** actually updated, the corresponding element in the aChange[] array
** is zero or greater (if a column is unmodified the corresponding element
** is set to -1). If the rowid column is modified by the UPDATE statement
@@ -114845,7 +131417,7 @@ static int fkChildIsModified(
/*
** The second argument points to an FKey object representing a foreign key
** for which pTab is the parent table. An UPDATE statement against pTab
-** is currently being processed. For each column of the table that is
+** is currently being processed. For each column of the table that is
** actually updated, the corresponding element in the aChange[] array
** is zero or greater (if a column is unmodified the corresponding element
** is set to -1). If the rowid column is modified by the UPDATE statement
@@ -114855,9 +131427,9 @@ static int fkChildIsModified(
** parent key for FK constraint *p are modified.
*/
static int fkParentIsModified(
- Table *pTab,
- FKey *p,
- int *aChange,
+ Table *pTab,
+ FKey *p,
+ int *aChange,
int bChngRowid
){
int i;
@@ -114868,7 +131440,7 @@ static int fkParentIsModified(
if( aChange[iKey]>=0 || (iKey==pTab->iPKey && bChngRowid) ){
Column *pCol = &pTab->aCol[iKey];
if( zKey ){
- if( 0==sqlite3StrICmp(pCol->zName, zKey) ) return 1;
+ if( 0==sqlite3StrICmp(pCol->zCnName, zKey) ) return 1;
}else if( pCol->colFlags & COLFLAG_PRIMKEY ){
return 1;
}
@@ -114890,6 +131462,7 @@ static int isSetNullAction(Parse *pParse, FKey *pFKey){
if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull)
|| (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull)
){
+ assert( (pTop->db->flags & SQLITE_FkNoAction)==0 );
return 1;
}
}
@@ -114898,7 +131471,7 @@ static int isSetNullAction(Parse *pParse, FKey *pFKey){
/*
** This function is called when inserting, deleting or updating a row of
-** table pTab to generate VDBE code to perform foreign key constraint
+** table pTab to generate VDBE code to perform foreign key constraint
** processing for the operation.
**
** For a DELETE operation, parameter regOld is passed the index of the
@@ -114914,11 +131487,11 @@ static int isSetNullAction(Parse *pParse, FKey *pFKey){
** For an UPDATE operation, this function is called twice. Once before
** the original record is deleted from the table using the calling convention
** described for DELETE. Then again after the original record is deleted
-** but before the new record is inserted using the INSERT convention.
+** but before the new record is inserted using the INSERT convention.
*/
SQLITE_PRIVATE void sqlite3FkCheck(
Parse *pParse, /* Parse context */
- Table *pTab, /* Row is being deleted from this table */
+ Table *pTab, /* Row is being deleted from this table */
int regOld, /* Previous row data is stored here */
int regNew, /* New row data is stored here */
int *aChange, /* Array indicating UPDATEd columns (or 0) */
@@ -114935,13 +131508,14 @@ SQLITE_PRIVATE void sqlite3FkCheck(
/* If foreign-keys are disabled, this function is a no-op. */
if( (db->flags&SQLITE_ForeignKeys)==0 ) return;
+ if( !IsOrdinaryTable(pTab) ) return;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
zDb = db->aDb[iDb].zDbSName;
/* Loop through all the foreign key constraints for which pTab is the
** child table (the table that the foreign key definition is part of). */
- for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
Table *pTo; /* Parent table of foreign key pFKey */
Index *pIdx = 0; /* Index on key columns in pTo */
int *aiFree = 0;
@@ -114950,16 +131524,16 @@ SQLITE_PRIVATE void sqlite3FkCheck(
int i;
int bIgnore = 0;
- if( aChange
+ if( aChange
&& sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0
- && fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0
+ && fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0
){
continue;
}
- /* Find the parent table of this foreign key. Also find a unique index
- ** on the parent key columns in the parent table. If either of these
- ** schema items cannot be located, set an error in pParse and return
+ /* Find the parent table of this foreign key. Also find a unique index
+ ** on the parent key columns in the parent table. If either of these
+ ** schema items cannot be located, set an error in pParse and return
** early. */
if( pParse->disableTriggers ){
pTo = sqlite3FindTable(db, pFKey->zTo, zDb);
@@ -114980,7 +131554,9 @@ SQLITE_PRIVATE void sqlite3FkCheck(
Vdbe *v = sqlite3GetVdbe(pParse);
int iJump = sqlite3VdbeCurrentAddr(v) + pFKey->nCol + 1;
for(i=0; i<pFKey->nCol; i++){
- int iReg = pFKey->aCol[i].iFrom + regOld + 1;
+ int iFromCol, iReg;
+ iFromCol = pFKey->aCol[i].iFrom;
+ iReg = sqlite3TableColumnToStorage(pFKey->pFrom,iFromCol) + regOld+1;
sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump); VdbeCoverage(v);
}
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, -1);
@@ -115001,36 +131577,36 @@ SQLITE_PRIVATE void sqlite3FkCheck(
}
assert( pIdx==0 || pIdx->aiColumn[i]>=0 );
#ifndef SQLITE_OMIT_AUTHORIZATION
- /* Request permission to read the parent key columns. If the
+ /* Request permission to read the parent key columns. If the
** authorization callback returns SQLITE_IGNORE, behave as if any
** values read from the parent table are NULL. */
if( db->xAuth ){
int rcauth;
- char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName;
+ char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zCnName;
rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb);
bIgnore = (rcauth==SQLITE_IGNORE);
}
#endif
}
- /* Take a shared-cache advisory read-lock on the parent table. Allocate
- ** a cursor to use to search the unique index on the parent key columns
+ /* Take a shared-cache advisory read-lock on the parent table. Allocate
+ ** a cursor to use to search the unique index on the parent key columns
** in the parent table. */
sqlite3TableLock(pParse, iDb, pTo->tnum, 0, pTo->zName);
pParse->nTab++;
if( regOld!=0 ){
/* A row is being removed from the child table. Search for the parent.
- ** If the parent does not exist, removing the child row resolves an
+ ** If the parent does not exist, removing the child row resolves an
** outstanding foreign key constraint violation. */
fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1, bIgnore);
}
if( regNew!=0 && !isSetNullAction(pParse, pFKey) ){
/* A row is being added to the child table. If a parent row cannot
- ** be found, adding the child row has violated the FK constraint.
+ ** be found, adding the child row has violated the FK constraint.
**
** If this operation is being performed as part of a trigger program
- ** that is actually a "SET NULL" action belonging to this very
+ ** that is actually a "SET NULL" action belonging to this very
** foreign key, then omit this scan altogether. As all child key
** values are guaranteed to be NULL, it is not possible for adding
** this row to cause an FK violation. */
@@ -115051,8 +131627,8 @@ SQLITE_PRIVATE void sqlite3FkCheck(
continue;
}
- if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs)
- && !pParse->pToplevel && !pParse->isMultiWrite
+ if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs)
+ && !pParse->pToplevel && !pParse->isMultiWrite
){
assert( regOld==0 && regNew!=0 );
/* Inserting a single row into a parent table cannot cause (or fix)
@@ -115068,19 +131644,21 @@ SQLITE_PRIVATE void sqlite3FkCheck(
/* Create a SrcList structure containing the child table. We need the
** child table as a SrcList for sqlite3WhereBegin() */
- pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
+ pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
if( pSrc ){
- struct SrcList_item *pItem = pSrc->a;
+ SrcItem *pItem = pSrc->a;
pItem->pTab = pFKey->pFrom;
pItem->zName = pFKey->pFrom->zName;
pItem->pTab->nTabRef++;
pItem->iCursor = pParse->nTab++;
-
+
if( regNew!=0 ){
fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1);
}
if( regOld!=0 ){
int eAction = pFKey->aAction[aChange!=0];
+ if( (db->flags & SQLITE_FkNoAction) ) eAction = OE_None;
+
fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1);
/* If this is a deferred FK constraint, or a CASCADE or SET NULL
** action applies, then any foreign key violations caused by
@@ -115094,10 +131672,10 @@ SQLITE_PRIVATE void sqlite3FkCheck(
**
** Note 2: At first glance it may seem like SQLite could simply omit
** all OP_FkCounter related scans when either CASCADE or SET NULL
- ** applies. The trouble starts if the CASCADE or SET NULL action
- ** trigger causes other triggers or action rules attached to the
+ ** applies. The trouble starts if the CASCADE or SET NULL action
+ ** trigger causes other triggers or action rules attached to the
** child table to fire. In these cases the fk constraint counters
- ** might be set incorrectly if any OP_FkCounter related scans are
+ ** might be set incorrectly if any OP_FkCounter related scans are
** omitted. */
if( !pFKey->isDeferred && eAction!=OE_Cascade && eAction!=OE_SetNull ){
sqlite3MayAbort(pParse);
@@ -115113,7 +131691,7 @@ SQLITE_PRIVATE void sqlite3FkCheck(
#define COLUMN_MASK(x) (((x)>31) ? 0xffffffff : ((u32)1<<(x)))
/*
-** This function is called before generating code to update or delete a
+** This function is called before generating code to update or delete a
** row contained in table pTab.
*/
SQLITE_PRIVATE u32 sqlite3FkOldmask(
@@ -115121,10 +131699,10 @@ SQLITE_PRIVATE u32 sqlite3FkOldmask(
Table *pTab /* Table being modified */
){
u32 mask = 0;
- if( pParse->db->flags&SQLITE_ForeignKeys ){
+ if( pParse->db->flags&SQLITE_ForeignKeys && IsOrdinaryTable(pTab) ){
FKey *p;
int i;
- for(p=pTab->pFKey; p; p=p->pNextFrom){
+ for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){
for(i=0; i<p->nCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom);
}
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
@@ -115143,22 +131721,24 @@ SQLITE_PRIVATE u32 sqlite3FkOldmask(
/*
-** This function is called before generating code to update or delete a
+** This function is called before generating code to update or delete a
** row contained in table pTab. If the operation is a DELETE, then
** parameter aChange is passed a NULL value. For an UPDATE, aChange points
** to an array of size N, where N is the number of columns in table pTab.
-** If the i'th column is not modified by the UPDATE, then the corresponding
+** If the i'th column is not modified by the UPDATE, then the corresponding
** entry in the aChange[] array is set to -1. If the column is modified,
** the value is 0 or greater. Parameter chngRowid is set to true if the
** UPDATE statement modifies the rowid fields of the table.
**
** If any foreign key processing will be required, this function returns
-** non-zero. If there is no foreign key related processing, this function
+** non-zero. If there is no foreign key related processing, this function
** returns zero.
**
** For an UPDATE, this function returns 2 if:
**
-** * There are any FKs for which pTab is the child and the parent table, or
+** * There are any FKs for which pTab is the child and the parent table
+** and any FK processing at all is required (even of a different FK), or
+**
** * the UPDATE modifies one or more parent keys for which the action is
** not "NO ACTION" (i.e. is CASCADE, SET DEFAULT or SET NULL).
**
@@ -115170,40 +131750,45 @@ SQLITE_PRIVATE int sqlite3FkRequired(
int *aChange, /* Non-NULL for UPDATE operations */
int chngRowid /* True for UPDATE that affects rowid */
){
- int eRet = 0;
- if( pParse->db->flags&SQLITE_ForeignKeys ){
+ int eRet = 1; /* Value to return if bHaveFK is true */
+ int bHaveFK = 0; /* If FK processing is required */
+ if( pParse->db->flags&SQLITE_ForeignKeys && IsOrdinaryTable(pTab) ){
if( !aChange ){
- /* A DELETE operation. Foreign key processing is required if the
- ** table in question is either the child or parent table for any
+ /* A DELETE operation. Foreign key processing is required if the
+ ** table in question is either the child or parent table for any
** foreign key constraint. */
- eRet = (sqlite3FkReferences(pTab) || pTab->pFKey);
+ bHaveFK = (sqlite3FkReferences(pTab) || pTab->u.tab.pFKey);
}else{
/* This is an UPDATE. Foreign key processing is only required if the
** operation modifies one or more child or parent key columns. */
FKey *p;
/* Check if any child key columns are being modified. */
- for(p=pTab->pFKey; p; p=p->pNextFrom){
- if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) return 2;
+ for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){
if( fkChildIsModified(pTab, p, aChange, chngRowid) ){
- eRet = 1;
+ if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) eRet = 2;
+ bHaveFK = 1;
}
}
/* Check if any parent key columns are being modified. */
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
if( fkParentIsModified(pTab, p, aChange, chngRowid) ){
- if( p->aAction[1]!=OE_None ) return 2;
- eRet = 1;
+ if( (pParse->db->flags & SQLITE_FkNoAction)==0
+ && p->aAction[1]!=OE_None
+ ){
+ return 2;
+ }
+ bHaveFK = 1;
}
}
}
}
- return eRet;
+ return bHaveFK ? eRet : 0;
}
/*
-** This function is called when an UPDATE or DELETE operation is being
+** This function is called when an UPDATE or DELETE operation is being
** compiled on table pTab, which is the parent table of foreign-key pFKey.
** If the current operation is an UPDATE, then the pChanges parameter is
** passed a pointer to the list of columns being modified. If it is a
@@ -115211,11 +131796,11 @@ SQLITE_PRIVATE int sqlite3FkRequired(
**
** It returns a pointer to a Trigger structure containing a trigger
** equivalent to the ON UPDATE or ON DELETE action specified by pFKey.
-** If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is
-** returned (these actions require no special handling by the triggers
-** sub-system, code for them is created by fkScanChildren()).
+** If the action is "NO ACTION" then a NULL pointer is returned (these actions
+** require no special handling by the triggers sub-system, code for them is
+** created by fkScanChildren()).
**
-** For example, if pFKey is the foreign key and pTab is table "p" in
+** For example, if pFKey is the foreign key and pTab is table "p" in
** the following schema:
**
** CREATE TABLE p(pk PRIMARY KEY);
@@ -115228,7 +131813,7 @@ SQLITE_PRIVATE int sqlite3FkRequired(
** END;
**
** The returned pointer is cached as part of the foreign key object. It
-** is eventually freed along with the rest of the foreign key object by
+** is eventually freed along with the rest of the foreign key object by
** sqlite3FkDelete().
*/
static Trigger *fkActionTrigger(
@@ -115243,6 +131828,7 @@ static Trigger *fkActionTrigger(
int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */
action = pFKey->aAction[iAction];
+ if( (db->flags & SQLITE_FkNoAction) ) action = OE_None;
if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){
return 0;
}
@@ -115276,20 +131862,20 @@ static Trigger *fkActionTrigger(
assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKey<pTab->nCol) );
assert( pIdx==0 || pIdx->aiColumn[i]>=0 );
sqlite3TokenInit(&tToCol,
- pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName);
- sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zName);
+ pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zCnName);
+ sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zCnName);
/* Create the expression "OLD.zToCol = zFromCol". It is important
** that the "OLD.zToCol" term is on the LHS of the = operator, so
** that the affinity and collation sequence associated with the
** parent table are used for the comparison. */
pEq = sqlite3PExpr(pParse, TK_EQ,
- sqlite3PExpr(pParse, TK_DOT,
+ sqlite3PExpr(pParse, TK_DOT,
sqlite3ExprAlloc(db, TK_ID, &tOld, 0),
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)),
sqlite3ExprAlloc(db, TK_ID, &tFromCol, 0)
);
- pWhere = sqlite3ExprAnd(db, pWhere, pEq);
+ pWhere = sqlite3ExprAnd(pParse, pWhere, pEq);
/* For ON UPDATE, construct the next term of the WHEN clause.
** The final WHEN clause will be like this:
@@ -115298,24 +131884,32 @@ static Trigger *fkActionTrigger(
*/
if( pChanges ){
pEq = sqlite3PExpr(pParse, TK_IS,
- sqlite3PExpr(pParse, TK_DOT,
+ sqlite3PExpr(pParse, TK_DOT,
sqlite3ExprAlloc(db, TK_ID, &tOld, 0),
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)),
- sqlite3PExpr(pParse, TK_DOT,
+ sqlite3PExpr(pParse, TK_DOT,
sqlite3ExprAlloc(db, TK_ID, &tNew, 0),
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0))
);
- pWhen = sqlite3ExprAnd(db, pWhen, pEq);
+ pWhen = sqlite3ExprAnd(pParse, pWhen, pEq);
}
-
+
if( action!=OE_Restrict && (action!=OE_Cascade || pChanges) ){
Expr *pNew;
if( action==OE_Cascade ){
- pNew = sqlite3PExpr(pParse, TK_DOT,
+ pNew = sqlite3PExpr(pParse, TK_DOT,
sqlite3ExprAlloc(db, TK_ID, &tNew, 0),
sqlite3ExprAlloc(db, TK_ID, &tToCol, 0));
}else if( action==OE_SetDflt ){
- Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt;
+ Column *pCol = pFKey->pFrom->aCol + iFromCol;
+ Expr *pDflt;
+ if( pCol->colFlags & COLFLAG_GENERATED ){
+ testcase( pCol->colFlags & COLFLAG_VIRTUAL );
+ testcase( pCol->colFlags & COLFLAG_STORED );
+ pDflt = 0;
+ }else{
+ pDflt = sqlite3ColumnExpr(pFKey->pFrom, pCol);
+ }
if( pDflt ){
pNew = sqlite3ExprDup(db, pDflt, 0);
}else{
@@ -115334,18 +131928,23 @@ static Trigger *fkActionTrigger(
nFrom = sqlite3Strlen30(zFrom);
if( action==OE_Restrict ){
- Token tFrom;
- Expr *pRaise;
+ int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ SrcList *pSrc;
+ Expr *pRaise;
- tFrom.z = zFrom;
- tFrom.n = nFrom;
pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
if( pRaise ){
- pRaise->affinity = OE_Abort;
+ pRaise->affExpr = OE_Abort;
}
- pSelect = sqlite3SelectNew(pParse,
+ pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
+ if( pSrc ){
+ assert( pSrc->nSrc==1 );
+ pSrc->a[0].zName = sqlite3DbStrDup(db, zFrom);
+ pSrc->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
+ }
+ pSelect = sqlite3SelectNew(pParse,
sqlite3ExprListAppend(pParse, 0, pRaise),
- sqlite3SrcListAppend(db, 0, &tFrom, 0),
+ pSrc,
pWhere,
0, 0, 0, 0, 0
);
@@ -115353,9 +131952,9 @@ static Trigger *fkActionTrigger(
}
/* Disable lookaside memory allocation */
- db->lookaside.bDisable++;
+ DisableLookaside;
- pTrigger = (Trigger *)sqlite3DbMallocZero(db,
+ pTrigger = (Trigger *)sqlite3DbMallocZero(db,
sizeof(Trigger) + /* struct Trigger */
sizeof(TriggerStep) + /* Single step in trigger program */
nFrom + 1 /* Space for pStep->zTarget */
@@ -115364,7 +131963,7 @@ static Trigger *fkActionTrigger(
pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1];
pStep->zTarget = (char *)&pStep[1];
memcpy((char *)pStep->zTarget, zFrom, nFrom);
-
+
pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE);
pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
@@ -115375,7 +131974,7 @@ static Trigger *fkActionTrigger(
}
/* Re-enable the lookaside buffer, if it was disabled earlier. */
- db->lookaside.bDisable--;
+ EnableLookaside;
sqlite3ExprDelete(db, pWhere);
sqlite3ExprDelete(db, pWhen);
@@ -115386,16 +131985,18 @@ static Trigger *fkActionTrigger(
return 0;
}
assert( pStep!=0 );
+ assert( pTrigger!=0 );
switch( action ){
case OE_Restrict:
- pStep->op = TK_SELECT;
+ pStep->op = TK_SELECT;
break;
- case OE_Cascade:
- if( !pChanges ){
- pStep->op = TK_DELETE;
- break;
+ case OE_Cascade:
+ if( !pChanges ){
+ pStep->op = TK_DELETE;
+ break;
}
+ /* no break */ deliberate_fall_through
default:
pStep->op = TK_UPDATE;
}
@@ -115421,9 +132022,9 @@ SQLITE_PRIVATE void sqlite3FkActions(
int *aChange, /* Array indicating UPDATEd columns (or 0) */
int bChngRowid /* True if rowid is UPDATEd */
){
- /* If foreign-key support is enabled, iterate through all FKs that
- ** refer to table pTab. If there is an action associated with the FK
- ** for this operation (either update or delete), invoke the associated
+ /* If foreign-key support is enabled, iterate through all FKs that
+ ** refer to table pTab. If there is an action associated with the FK
+ ** for this operation (either update or delete), invoke the associated
** trigger sub-program. */
if( pParse->db->flags&SQLITE_ForeignKeys ){
FKey *pFKey; /* Iterator variable */
@@ -115449,18 +132050,18 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){
FKey *pFKey; /* Iterator variable */
FKey *pNext; /* Copy of pFKey->pNextFrom */
- assert( db==0 || IsVirtual(pTab)
- || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
- for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){
+ assert( IsOrdinaryTable(pTab) );
+ assert( db!=0 );
+ for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pNext){
+ assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
/* Remove the FK from the fkeyHash hash table. */
- if( !db || db->pnBytesFreed==0 ){
+ if( db->pnBytesFreed==0 ){
if( pFKey->pPrevTo ){
pFKey->pPrevTo->pNextTo = pFKey->pNextTo;
}else{
- void *p = (void *)pFKey->pNextTo;
- const char *z = (p ? pFKey->pNextTo->zTo : pFKey->zTo);
- sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, p);
+ const char *z = (pFKey->pNextTo ? pFKey->pNextTo->zTo : pFKey->zTo);
+ sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, pFKey->pNextTo);
}
if( pFKey->pNextTo ){
pFKey->pNextTo->pPrevTo = pFKey->pPrevTo;
@@ -115503,7 +132104,7 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){
/* #include "sqliteInt.h" */
/*
-** Generate code that will
+** Generate code that will
**
** (1) acquire a lock for table pTab then
** (2) open pTab as cursor iCur.
@@ -115520,17 +132121,20 @@ SQLITE_PRIVATE void sqlite3OpenTable(
){
Vdbe *v;
assert( !IsVirtual(pTab) );
- v = sqlite3GetVdbe(pParse);
+ assert( pParse->pVdbe!=0 );
+ v = pParse->pVdbe;
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
- sqlite3TableLock(pParse, iDb, pTab->tnum,
- (opcode==OP_OpenWrite)?1:0, pTab->zName);
+ if( !pParse->db->noSharedCache ){
+ sqlite3TableLock(pParse, iDb, pTab->tnum,
+ (opcode==OP_OpenWrite)?1:0, pTab->zName);
+ }
if( HasRowid(pTab) ){
- sqlite3VdbeAddOp4Int(v, opcode, iCur, pTab->tnum, iDb, pTab->nCol);
+ sqlite3VdbeAddOp4Int(v, opcode, iCur, pTab->tnum, iDb, pTab->nNVCol);
VdbeComment((v, "%s", pTab->zName));
}else{
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
assert( pPk!=0 );
- assert( pPk->tnum==pTab->tnum );
+ assert( pPk->tnum==pTab->tnum || CORRUPT_DB );
sqlite3VdbeAddOp3(v, opcode, iCur, pPk->tnum, iDb);
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
VdbeComment((v, "%s", pTab->zName));
@@ -115539,7 +132143,7 @@ SQLITE_PRIVATE void sqlite3OpenTable(
/*
** Return a pointer to the column affinity string associated with index
-** pIdx. A column affinity string has one character for each column in
+** pIdx. A column affinity string has one character for each column in
** the table, according to the affinity of the column:
**
** Character Column affinity
@@ -115557,81 +132161,139 @@ SQLITE_PRIVATE void sqlite3OpenTable(
** is managed along with the rest of the Index structure. It will be
** released when sqlite3DeleteIndex() is called.
*/
-SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
+static SQLITE_NOINLINE const char *computeIndexAffStr(sqlite3 *db, Index *pIdx){
+ /* The first time a column affinity string for a particular index is
+ ** required, it is allocated and populated here. It is then stored as
+ ** a member of the Index structure for subsequent use.
+ **
+ ** The column affinity string will eventually be deleted by
+ ** sqliteDeleteIndex() when the Index structure itself is cleaned
+ ** up.
+ */
+ int n;
+ Table *pTab = pIdx->pTable;
+ pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1);
if( !pIdx->zColAff ){
- /* The first time a column affinity string for a particular index is
- ** required, it is allocated and populated here. It is then stored as
- ** a member of the Index structure for subsequent use.
- **
- ** The column affinity string will eventually be deleted by
- ** sqliteDeleteIndex() when the Index structure itself is cleaned
- ** up.
- */
- int n;
- Table *pTab = pIdx->pTable;
- pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1);
- if( !pIdx->zColAff ){
- sqlite3OomFault(db);
- return 0;
+ sqlite3OomFault(db);
+ return 0;
+ }
+ for(n=0; n<pIdx->nColumn; n++){
+ i16 x = pIdx->aiColumn[n];
+ char aff;
+ if( x>=0 ){
+ aff = pTab->aCol[x].affinity;
+ }else if( x==XN_ROWID ){
+ aff = SQLITE_AFF_INTEGER;
+ }else{
+ assert( x==XN_EXPR );
+ assert( pIdx->bHasExpr );
+ assert( pIdx->aColExpr!=0 );
+ aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr);
}
- for(n=0; n<pIdx->nColumn; n++){
- i16 x = pIdx->aiColumn[n];
- if( x>=0 ){
- pIdx->zColAff[n] = pTab->aCol[x].affinity;
- }else if( x==XN_ROWID ){
- pIdx->zColAff[n] = SQLITE_AFF_INTEGER;
- }else{
- char aff;
- assert( x==XN_EXPR );
- assert( pIdx->aColExpr!=0 );
- aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr);
- if( aff==0 ) aff = SQLITE_AFF_BLOB;
- pIdx->zColAff[n] = aff;
+ if( aff<SQLITE_AFF_BLOB ) aff = SQLITE_AFF_BLOB;
+ if( aff>SQLITE_AFF_NUMERIC) aff = SQLITE_AFF_NUMERIC;
+ pIdx->zColAff[n] = aff;
+ }
+ pIdx->zColAff[n] = 0;
+ return pIdx->zColAff;
+}
+SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
+ if( !pIdx->zColAff ) return computeIndexAffStr(db, pIdx);
+ return pIdx->zColAff;
+}
+
+
+/*
+** Compute an affinity string for a table. Space is obtained
+** from sqlite3DbMalloc(). The caller is responsible for freeing
+** the space when done.
+*/
+SQLITE_PRIVATE char *sqlite3TableAffinityStr(sqlite3 *db, const Table *pTab){
+ char *zColAff;
+ zColAff = (char *)sqlite3DbMallocRaw(db, pTab->nCol+1);
+ if( zColAff ){
+ int i, j;
+ for(i=j=0; i<pTab->nCol; i++){
+ if( (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){
+ zColAff[j++] = pTab->aCol[i].affinity;
}
}
- pIdx->zColAff[n] = 0;
+ do{
+ zColAff[j--] = 0;
+ }while( j>=0 && zColAff[j]<=SQLITE_AFF_BLOB );
}
-
- return pIdx->zColAff;
+ return zColAff;
}
/*
+** Make changes to the evolving bytecode to do affinity transformations
+** of values that are about to be gathered into a row for table pTab.
+**
+** For ordinary (legacy, non-strict) tables:
+** -----------------------------------------
+**
** Compute the affinity string for table pTab, if it has not already been
** computed. As an optimization, omit trailing SQLITE_AFF_BLOB affinities.
**
-** If the affinity exists (if it is no entirely SQLITE_AFF_BLOB values) and
-** if iReg>0 then code an OP_Affinity opcode that will set the affinities
-** for register iReg and following. Or if affinities exists and iReg==0,
+** If the affinity string is empty (because it was all SQLITE_AFF_BLOB entries
+** which were then optimized out) then this routine becomes a no-op.
+**
+** Otherwise if iReg>0 then code an OP_Affinity opcode that will set the
+** affinities for register iReg and following. Or if iReg==0,
** then just set the P4 operand of the previous opcode (which should be
** an OP_MakeRecord) to the affinity string.
**
** A column affinity string has one character per column:
**
-** Character Column affinity
-** ------------------------------
-** 'A' BLOB
-** 'B' TEXT
-** 'C' NUMERIC
-** 'D' INTEGER
-** 'E' REAL
+** Character Column affinity
+** --------- ---------------
+** 'A' BLOB
+** 'B' TEXT
+** 'C' NUMERIC
+** 'D' INTEGER
+** 'E' REAL
+**
+** For STRICT tables:
+** ------------------
+**
+** Generate an appropriate OP_TypeCheck opcode that will verify the
+** datatypes against the column definitions in pTab. If iReg==0, that
+** means an OP_MakeRecord opcode has already been generated and should be
+** the last opcode generated. The new OP_TypeCheck needs to be inserted
+** before the OP_MakeRecord. The new OP_TypeCheck should use the same
+** register set as the OP_MakeRecord. If iReg>0 then register iReg is
+** the first of a series of registers that will form the new record.
+** Apply the type checking to that array of registers.
*/
SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
int i;
- char *zColAff = pTab->zColAff;
+ char *zColAff;
+ if( pTab->tabFlags & TF_Strict ){
+ if( iReg==0 ){
+ /* Move the previous opcode (which should be OP_MakeRecord) forward
+ ** by one slot and insert a new OP_TypeCheck where the current
+ ** OP_MakeRecord is found */
+ VdbeOp *pPrev;
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
+ pPrev = sqlite3VdbeGetLastOp(v);
+ assert( pPrev!=0 );
+ assert( pPrev->opcode==OP_MakeRecord || sqlite3VdbeDb(v)->mallocFailed );
+ pPrev->opcode = OP_TypeCheck;
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, pPrev->p1, pPrev->p2, pPrev->p3);
+ }else{
+ /* Insert an isolated OP_Typecheck */
+ sqlite3VdbeAddOp2(v, OP_TypeCheck, iReg, pTab->nNVCol);
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
+ }
+ return;
+ }
+ zColAff = pTab->zColAff;
if( zColAff==0 ){
- sqlite3 *db = sqlite3VdbeDb(v);
- zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1);
+ zColAff = sqlite3TableAffinityStr(0, pTab);
if( !zColAff ){
- sqlite3OomFault(db);
+ sqlite3OomFault(sqlite3VdbeDb(v));
return;
}
-
- for(i=0; i<pTab->nCol; i++){
- zColAff[i] = pTab->aCol[i].affinity;
- }
- do{
- zColAff[i--] = 0;
- }while( i>=0 && zColAff[i]==SQLITE_AFF_BLOB );
pTab->zColAff = zColAff;
}
assert( zColAff!=0 );
@@ -115640,6 +132302,8 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
if( iReg ){
sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i);
}else{
+ assert( sqlite3VdbeGetLastOp(v)->opcode==OP_MakeRecord
+ || sqlite3VdbeDb(v)->mallocFailed );
sqlite3VdbeChangeP4(v, -1, zColAff, i);
}
}
@@ -115647,9 +132311,9 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
/*
** Return non-zero if the table pTab in database iDb or any of its indices
-** have been opened at any point in the VDBE program. This is used to see if
-** a statement of the form "INSERT INTO <iDb, pTab> SELECT ..." can
-** run without using a temporary table for the results of the SELECT.
+** have been opened at any point in the VDBE program. This is used to see if
+** a statement of the form "INSERT INTO <iDb, pTab> SELECT ..." can
+** run without using a temporary table for the results of the SELECT.
*/
static int readsTable(Parse *p, int iDb, Table *pTab){
Vdbe *v = sqlite3GetVdbe(p);
@@ -115664,7 +132328,7 @@ static int readsTable(Parse *p, int iDb, Table *pTab){
assert( pOp!=0 );
if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){
Index *pIndex;
- int tnum = pOp->p2;
+ Pgno tnum = pOp->p2;
if( tnum==pTab->tnum ){
return 1;
}
@@ -115685,6 +132349,125 @@ static int readsTable(Parse *p, int iDb, Table *pTab){
return 0;
}
+/* This walker callback will compute the union of colFlags flags for all
+** referenced columns in a CHECK constraint or generated column expression.
+*/
+static int exprColumnFlagUnion(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_COLUMN && pExpr->iColumn>=0 ){
+ assert( pExpr->iColumn < pWalker->u.pTab->nCol );
+ pWalker->eCode |= pWalker->u.pTab->aCol[pExpr->iColumn].colFlags;
+ }
+ return WRC_Continue;
+}
+
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+/*
+** All regular columns for table pTab have been puts into registers
+** starting with iRegStore. The registers that correspond to STORED
+** or VIRTUAL columns have not yet been initialized. This routine goes
+** back and computes the values for those columns based on the previously
+** computed normal columns.
+*/
+SQLITE_PRIVATE void sqlite3ComputeGeneratedColumns(
+ Parse *pParse, /* Parsing context */
+ int iRegStore, /* Register holding the first column */
+ Table *pTab /* The table */
+){
+ int i;
+ Walker w;
+ Column *pRedo;
+ int eProgress;
+ VdbeOp *pOp;
+
+ assert( pTab->tabFlags & TF_HasGenerated );
+ testcase( pTab->tabFlags & TF_HasVirtual );
+ testcase( pTab->tabFlags & TF_HasStored );
+
+ /* Before computing generated columns, first go through and make sure
+ ** that appropriate affinity has been applied to the regular columns
+ */
+ sqlite3TableAffinity(pParse->pVdbe, pTab, iRegStore);
+ if( (pTab->tabFlags & TF_HasStored)!=0 ){
+ pOp = sqlite3VdbeGetLastOp(pParse->pVdbe);
+ if( pOp->opcode==OP_Affinity ){
+ /* Change the OP_Affinity argument to '@' (NONE) for all stored
+ ** columns. '@' is the no-op affinity and those columns have not
+ ** yet been computed. */
+ int ii, jj;
+ char *zP4 = pOp->p4.z;
+ assert( zP4!=0 );
+ assert( pOp->p4type==P4_DYNAMIC );
+ for(ii=jj=0; zP4[jj]; ii++){
+ if( pTab->aCol[ii].colFlags & COLFLAG_VIRTUAL ){
+ continue;
+ }
+ if( pTab->aCol[ii].colFlags & COLFLAG_STORED ){
+ zP4[jj] = SQLITE_AFF_NONE;
+ }
+ jj++;
+ }
+ }else if( pOp->opcode==OP_TypeCheck ){
+ /* If an OP_TypeCheck was generated because the table is STRICT,
+ ** then set the P3 operand to indicate that generated columns should
+ ** not be checked */
+ pOp->p3 = 1;
+ }
+ }
+
+ /* Because there can be multiple generated columns that refer to one another,
+ ** this is a two-pass algorithm. On the first pass, mark all generated
+ ** columns as "not available".
+ */
+ for(i=0; i<pTab->nCol; i++){
+ if( pTab->aCol[i].colFlags & COLFLAG_GENERATED ){
+ testcase( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL );
+ testcase( pTab->aCol[i].colFlags & COLFLAG_STORED );
+ pTab->aCol[i].colFlags |= COLFLAG_NOTAVAIL;
+ }
+ }
+
+ w.u.pTab = pTab;
+ w.xExprCallback = exprColumnFlagUnion;
+ w.xSelectCallback = 0;
+ w.xSelectCallback2 = 0;
+
+ /* On the second pass, compute the value of each NOT-AVAILABLE column.
+ ** Companion code in the TK_COLUMN case of sqlite3ExprCodeTarget() will
+ ** compute dependencies and mark remove the COLSPAN_NOTAVAIL mark, as
+ ** they are needed.
+ */
+ pParse->iSelfTab = -iRegStore;
+ do{
+ eProgress = 0;
+ pRedo = 0;
+ for(i=0; i<pTab->nCol; i++){
+ Column *pCol = pTab->aCol + i;
+ if( (pCol->colFlags & COLFLAG_NOTAVAIL)!=0 ){
+ int x;
+ pCol->colFlags |= COLFLAG_BUSY;
+ w.eCode = 0;
+ sqlite3WalkExpr(&w, sqlite3ColumnExpr(pTab, pCol));
+ pCol->colFlags &= ~COLFLAG_BUSY;
+ if( w.eCode & COLFLAG_NOTAVAIL ){
+ pRedo = pCol;
+ continue;
+ }
+ eProgress = 1;
+ assert( pCol->colFlags & COLFLAG_GENERATED );
+ x = sqlite3TableColumnToStorage(pTab, i) + iRegStore;
+ sqlite3ExprCodeGeneratedColumn(pParse, pTab, pCol, x);
+ pCol->colFlags &= ~COLFLAG_NOTAVAIL;
+ }
+ }
+ }while( pRedo && eProgress );
+ if( pRedo ){
+ sqlite3ErrorMsg(pParse, "generated column loop on \"%s\"", pRedo->zCnName);
+ }
+ pParse->iSelfTab = 0;
+}
+#endif /* SQLITE_OMIT_GENERATED_COLUMNS */
+
+
#ifndef SQLITE_OMIT_AUTOINCREMENT
/*
** Locate or create an AutoincInfo structure associated with table pTab
@@ -115728,7 +132511,7 @@ static int autoIncBegin(
** Ticket d8dc2b3a58cd5dc2918a1d4acb 2018-05-23 */
if( pSeqTab==0
|| !HasRowid(pSeqTab)
- || IsVirtual(pSeqTab)
+ || NEVER(IsVirtual(pSeqTab))
|| pSeqTab->nCol!=2
){
pParse->nErr++;
@@ -115740,7 +132523,9 @@ static int autoIncBegin(
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
if( pInfo==0 ){
pInfo = sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo));
- if( pInfo==0 ) return 0;
+ sqlite3ParserAddCleanup(pToplevel, sqlite3DbFree, pInfo);
+ testcase( pParse->earlyCleanup );
+ if( pParse->db->mallocFailed ) return 0;
pInfo->pNext = pToplevel->pAinc;
pToplevel->pAinc = pInfo;
pInfo->pTab = pTab;
@@ -115756,7 +132541,7 @@ static int autoIncBegin(
/*
** This routine generates code that will initialize all of the
-** register used by the autoincrement tracker.
+** register used by the autoincrement tracker.
*/
SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){
AutoincInfo *p; /* Information about an AUTOINCREMENT */
@@ -115785,7 +132570,7 @@ SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){
/* 8 */ {OP_Goto, 0, 11, 0},
/* 9 */ {OP_Next, 0, 2, 0},
/* 10 */ {OP_Integer, 0, 0, 0},
- /* 11 */ {OP_Close, 0, 0, 0}
+ /* 11 */ {OP_Close, 0, 0, 0}
};
VdbeOp *aOp;
pDb = &db->aDb[p->iDb];
@@ -115807,6 +132592,7 @@ SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){
aOp[7].p2 = memId+2;
aOp[7].p1 = memId;
aOp[10].p2 = memId;
+ if( pParse->nTab==0 ) pParse->nTab = 1;
}
}
@@ -115991,7 +132777,7 @@ SQLITE_PRIVATE void sqlite3Insert(
Parse *pParse, /* Parser context */
SrcList *pTabList, /* Name of table into which we are inserting */
Select *pSelect, /* A SELECT statement to use as the data source */
- IdList *pColumn, /* Column names corresponding to IDLIST. */
+ IdList *pColumn, /* Column names corresponding to IDLIST, or NULL. */
int onError, /* How to handle constraint errors */
Upsert *pUpsert /* ON CONFLICT clauses for upsert, or NULL */
){
@@ -116016,6 +132802,7 @@ SQLITE_PRIVATE void sqlite3Insert(
u8 withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */
u8 bIdListInOrder; /* True if IDLIST is in table order */
ExprList *pList = 0; /* List of VALUES() to be inserted */
+ int iRegStore; /* Register in which to store next column */
/* Register allocations */
int regFromSelect = 0;/* Base register for data coming from SELECT */
@@ -116033,9 +132820,11 @@ SQLITE_PRIVATE void sqlite3Insert(
#endif
db = pParse->db;
- if( pParse->nErr || db->mallocFailed ){
+ assert( db->pParse==pParse );
+ if( pParse->nErr ){
goto insert_cleanup;
}
+ assert( db->mallocFailed==0 );
dest.iSDParm = 0; /* Suppress a harmless compiler warning */
/* If the Select object is really just a simple VALUES() list with a
@@ -116069,7 +132858,7 @@ SQLITE_PRIVATE void sqlite3Insert(
*/
#ifndef SQLITE_OMIT_TRIGGER
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask);
- isView = pTab->pSelect!=0;
+ isView = IsView(pTab);
#else
# define pTrigger 0
# define tmask 0
@@ -116081,6 +132870,14 @@ SQLITE_PRIVATE void sqlite3Insert(
#endif
assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) );
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x10000 ){
+ sqlite3TreeViewLine(0, "In sqlite3Insert() at %s:%d", __FILE__, __LINE__);
+ sqlite3TreeViewInsert(pParse->pWith, pTabList, pColumn, pSelect, pList,
+ onError, pUpsert, pTrigger);
+ }
+#endif
+
/* If pTab is really a view, make sure it has been initialized.
** ViewGetColumnNames() is a no-op if pTab is not a view.
*/
@@ -116090,7 +132887,7 @@ SQLITE_PRIVATE void sqlite3Insert(
/* Cannot insert into a read-only table.
*/
- if( sqlite3IsReadOnly(pParse, pTab, tmask) ){
+ if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){
goto insert_cleanup;
}
@@ -116111,7 +132908,11 @@ SQLITE_PRIVATE void sqlite3Insert(
**
** This is the 2nd template.
*/
- if( pColumn==0 && xferOptimization(pParse, pTab, pSelect, onError, iDb) ){
+ if( pColumn==0
+ && pSelect!=0
+ && pTrigger==0
+ && xferOptimization(pParse, pTab, pSelect, onError, iDb)
+ ){
assert( !pTrigger );
assert( pList==0 );
goto insert_end;
@@ -116123,8 +132924,8 @@ SQLITE_PRIVATE void sqlite3Insert(
*/
regAutoinc = autoIncBegin(pParse, iDb, pTab);
- /* Allocate registers for holding the rowid of the new row,
- ** the content of the new row, and the assembled row record.
+ /* Allocate a block registers to hold the rowid and the values
+ ** for all columns of the new row.
*/
regRowid = regIns = pParse->nMem+1;
pParse->nMem += pTab->nCol + 1;
@@ -116135,7 +132936,7 @@ SQLITE_PRIVATE void sqlite3Insert(
regData = regRowid+1;
/* If the INSERT statement included an IDLIST term, then make sure
- ** all elements of the IDLIST really are columns of the table and
+ ** all elements of the IDLIST really are columns of the table and
** remember the column indices.
**
** If the table has an INTEGER PRIMARY KEY column and that column
@@ -116143,21 +132944,39 @@ SQLITE_PRIVATE void sqlite3Insert(
** the index into IDLIST of the primary key column. ipkColumn is
** the index of the primary key as it appears in IDLIST, not as
** is appears in the original table. (The index of the INTEGER
- ** PRIMARY KEY in the original table is pTab->iPKey.)
+ ** PRIMARY KEY in the original table is pTab->iPKey.) After this
+ ** loop, if ipkColumn==(-1), that means that integer primary key
+ ** is unspecified, and hence the table is either WITHOUT ROWID or
+ ** it will automatically generated an integer primary key.
+ **
+ ** bIdListInOrder is true if the columns in IDLIST are in storage
+ ** order. This enables an optimization that avoids shuffling the
+ ** columns into storage order. False negatives are harmless,
+ ** but false positives will cause database corruption.
*/
- bIdListInOrder = (pTab->tabFlags & TF_OOOHidden)==0;
+ bIdListInOrder = (pTab->tabFlags & (TF_OOOHidden|TF_HasStored))==0;
if( pColumn ){
+ assert( pColumn->eU4!=EU4_EXPR );
+ pColumn->eU4 = EU4_IDX;
for(i=0; i<pColumn->nId; i++){
- pColumn->a[i].idx = -1;
+ pColumn->a[i].u4.idx = -1;
}
for(i=0; i<pColumn->nId; i++){
for(j=0; j<pTab->nCol; j++){
- if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
- pColumn->a[i].idx = j;
+ if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zCnName)==0 ){
+ pColumn->a[i].u4.idx = j;
if( i!=j ) bIdListInOrder = 0;
if( j==pTab->iPKey ){
ipkColumn = i; assert( !withoutRowid );
}
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){
+ sqlite3ErrorMsg(pParse,
+ "cannot INSERT into generated column \"%s\"",
+ pTab->aCol[j].zCnName);
+ goto insert_cleanup;
+ }
+#endif
break;
}
}
@@ -116167,7 +132986,7 @@ SQLITE_PRIVATE void sqlite3Insert(
bIdListInOrder = 0;
}else{
sqlite3ErrorMsg(pParse, "table %S has no column named %s",
- pTabList, 0, pColumn->a[i].zName);
+ pTabList->a, pColumn->a[i].zName);
pParse->checkSchema = 1;
goto insert_cleanup;
}
@@ -116195,7 +133014,9 @@ SQLITE_PRIVATE void sqlite3Insert(
dest.nSdst = pTab->nCol;
rc = sqlite3Select(pParse, pSelect, &dest);
regFromSelect = dest.iSdst;
- if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup;
+ assert( db->pParse==pParse );
+ if( rc || pParse->nErr ) goto insert_cleanup;
+ assert( db->mallocFailed==0 );
sqlite3VdbeEndCoroutine(v, regYield);
sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
assert( pSelect->pEList );
@@ -116207,7 +133028,7 @@ SQLITE_PRIVATE void sqlite3Insert(
** the destination table (template 3).
**
** A temp table must be used if the table being updated is also one
- ** of the tables being read by the SELECT statement. Also use a
+ ** of the tables being read by the SELECT statement. Also use a
** temp table in the case of row triggers.
*/
if( pTrigger || readsTable(pParse, iDb, pTab) ){
@@ -116243,7 +133064,7 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3ReleaseTempReg(pParse, regTempRowid);
}
}else{
- /* This is the case if the data for the INSERT is coming from a
+ /* This is the case if the data for the INSERT is coming from a
** single-row VALUES clause
*/
NameContext sNC;
@@ -116262,35 +133083,54 @@ SQLITE_PRIVATE void sqlite3Insert(
}
/* If there is no IDLIST term but the table has an integer primary
- ** key, the set the ipkColumn variable to the integer primary key
+ ** key, the set the ipkColumn variable to the integer primary key
** column index in the original table definition.
*/
if( pColumn==0 && nColumn>0 ){
ipkColumn = pTab->iPKey;
- }
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ if( ipkColumn>=0 && (pTab->tabFlags & TF_HasGenerated)!=0 ){
+ testcase( pTab->tabFlags & TF_HasVirtual );
+ testcase( pTab->tabFlags & TF_HasStored );
+ for(i=ipkColumn-1; i>=0; i--){
+ if( pTab->aCol[i].colFlags & COLFLAG_GENERATED ){
+ testcase( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL );
+ testcase( pTab->aCol[i].colFlags & COLFLAG_STORED );
+ ipkColumn--;
+ }
+ }
+ }
+#endif
- /* Make sure the number of columns in the source data matches the number
- ** of columns to be inserted into the table.
- */
- for(i=0; i<pTab->nCol; i++){
- nHidden += (IsHiddenColumn(&pTab->aCol[i]) ? 1 : 0);
- }
- if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){
- sqlite3ErrorMsg(pParse,
- "table %S has %d columns but %d values were supplied",
- pTabList, 0, pTab->nCol-nHidden, nColumn);
- goto insert_cleanup;
+ /* Make sure the number of columns in the source data matches the number
+ ** of columns to be inserted into the table.
+ */
+ assert( TF_HasHidden==COLFLAG_HIDDEN );
+ assert( TF_HasGenerated==COLFLAG_GENERATED );
+ assert( COLFLAG_NOINSERT==(COLFLAG_GENERATED|COLFLAG_HIDDEN) );
+ if( (pTab->tabFlags & (TF_HasGenerated|TF_HasHidden))!=0 ){
+ for(i=0; i<pTab->nCol; i++){
+ if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++;
+ }
+ }
+ if( nColumn!=(pTab->nCol-nHidden) ){
+ sqlite3ErrorMsg(pParse,
+ "table %S has %d columns but %d values were supplied",
+ pTabList->a, pTab->nCol-nHidden, nColumn);
+ goto insert_cleanup;
+ }
}
if( pColumn!=0 && nColumn!=pColumn->nId ){
sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
goto insert_cleanup;
}
-
+
/* Initialize the count of rows to be inserted
*/
if( (db->flags & SQLITE_CountRows)!=0
&& !pParse->nested
&& !pParse->pTriggerTab
+ && !pParse->bReturning
){
regRowCount = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
@@ -116301,7 +133141,7 @@ SQLITE_PRIVATE void sqlite3Insert(
int nIdx;
nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, -1, 0,
&iDataCur, &iIdxCur);
- aRegIdx = sqlite3DbMallocRawNN(db, sizeof(int)*(nIdx+1));
+ aRegIdx = sqlite3DbMallocRawNN(db, sizeof(int)*(nIdx+2));
if( aRegIdx==0 ){
goto insert_cleanup;
}
@@ -116310,17 +133150,37 @@ SQLITE_PRIVATE void sqlite3Insert(
aRegIdx[i] = ++pParse->nMem;
pParse->nMem += pIdx->nColumn;
}
+ aRegIdx[i] = ++pParse->nMem; /* Register to store the table record */
}
#ifndef SQLITE_OMIT_UPSERT
if( pUpsert ){
- pTabList->a[0].iCursor = iDataCur;
- pUpsert->pUpsertSrc = pTabList;
- pUpsert->regData = regData;
- pUpsert->iDataCur = iDataCur;
- pUpsert->iIdxCur = iIdxCur;
- if( pUpsert->pUpsertTarget ){
- sqlite3UpsertAnalyzeTarget(pParse, pTabList, pUpsert);
+ Upsert *pNx;
+ if( IsVirtual(pTab) ){
+ sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"",
+ pTab->zName);
+ goto insert_cleanup;
}
+ if( IsView(pTab) ){
+ sqlite3ErrorMsg(pParse, "cannot UPSERT a view");
+ goto insert_cleanup;
+ }
+ if( sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget) ){
+ goto insert_cleanup;
+ }
+ pTabList->a[0].iCursor = iDataCur;
+ pNx = pUpsert;
+ do{
+ pNx->pUpsertSrc = pTabList;
+ pNx->regData = regData;
+ pNx->iDataCur = iDataCur;
+ pNx->iIdxCur = iIdxCur;
+ if( pNx->pUpsertTarget ){
+ if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx) ){
+ goto insert_cleanup;
+ }
+ }
+ pNx = pNx->pNextUpsert;
+ }while( pNx!=0 );
}
#endif
@@ -116347,13 +133207,106 @@ SQLITE_PRIVATE void sqlite3Insert(
** goto C
** D: ...
*/
+ sqlite3VdbeReleaseRegisters(pParse, regData, pTab->nCol, 0, 0);
addrInsTop = addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
VdbeCoverage(v);
+ if( ipkColumn>=0 ){
+ /* tag-20191021-001: If the INTEGER PRIMARY KEY is being generated by the
+ ** SELECT, go ahead and copy the value into the rowid slot now, so that
+ ** the value does not get overwritten by a NULL at tag-20191021-002. */
+ sqlite3VdbeAddOp2(v, OP_Copy, regFromSelect+ipkColumn, regRowid);
+ }
+ }
+
+ /* Compute data for ordinary columns of the new entry. Values
+ ** are written in storage order into registers starting with regData.
+ ** Only ordinary columns are computed in this loop. The rowid
+ ** (if there is one) is computed later and generated columns are
+ ** computed after the rowid since they might depend on the value
+ ** of the rowid.
+ */
+ nHidden = 0;
+ iRegStore = regData; assert( regData==regRowid+1 );
+ for(i=0; i<pTab->nCol; i++, iRegStore++){
+ int k;
+ u32 colFlags;
+ assert( i>=nHidden );
+ if( i==pTab->iPKey ){
+ /* tag-20191021-002: References to the INTEGER PRIMARY KEY are filled
+ ** using the rowid. So put a NULL in the IPK slot of the record to avoid
+ ** using excess space. The file format definition requires this extra
+ ** NULL - we cannot optimize further by skipping the column completely */
+ sqlite3VdbeAddOp1(v, OP_SoftNull, iRegStore);
+ continue;
+ }
+ if( ((colFlags = pTab->aCol[i].colFlags) & COLFLAG_NOINSERT)!=0 ){
+ nHidden++;
+ if( (colFlags & COLFLAG_VIRTUAL)!=0 ){
+ /* Virtual columns do not participate in OP_MakeRecord. So back up
+ ** iRegStore by one slot to compensate for the iRegStore++ in the
+ ** outer for() loop */
+ iRegStore--;
+ continue;
+ }else if( (colFlags & COLFLAG_STORED)!=0 ){
+ /* Stored columns are computed later. But if there are BEFORE
+ ** triggers, the slots used for stored columns will be OP_Copy-ed
+ ** to a second block of registers, so the register needs to be
+ ** initialized to NULL to avoid an uninitialized register read */
+ if( tmask & TRIGGER_BEFORE ){
+ sqlite3VdbeAddOp1(v, OP_SoftNull, iRegStore);
+ }
+ continue;
+ }else if( pColumn==0 ){
+ /* Hidden columns that are not explicitly named in the INSERT
+ ** get there default value */
+ sqlite3ExprCodeFactorable(pParse,
+ sqlite3ColumnExpr(pTab, &pTab->aCol[i]),
+ iRegStore);
+ continue;
+ }
+ }
+ if( pColumn ){
+ assert( pColumn->eU4==EU4_IDX );
+ for(j=0; j<pColumn->nId && pColumn->a[j].u4.idx!=i; j++){}
+ if( j>=pColumn->nId ){
+ /* A column not named in the insert column list gets its
+ ** default value */
+ sqlite3ExprCodeFactorable(pParse,
+ sqlite3ColumnExpr(pTab, &pTab->aCol[i]),
+ iRegStore);
+ continue;
+ }
+ k = j;
+ }else if( nColumn==0 ){
+ /* This is INSERT INTO ... DEFAULT VALUES. Load the default value. */
+ sqlite3ExprCodeFactorable(pParse,
+ sqlite3ColumnExpr(pTab, &pTab->aCol[i]),
+ iRegStore);
+ continue;
+ }else{
+ k = i - nHidden;
+ }
+
+ if( useTempTable ){
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, k, iRegStore);
+ }else if( pSelect ){
+ if( regFromSelect!=regData ){
+ sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+k, iRegStore);
+ }
+ }else{
+ Expr *pX = pList->a[k].pExpr;
+ int y = sqlite3ExprCodeTarget(pParse, pX, iRegStore);
+ if( y!=iRegStore ){
+ sqlite3VdbeAddOp2(v,
+ ExprHasProperty(pX, EP_Subquery) ? OP_Copy : OP_SCopy, y, iRegStore);
+ }
+ }
}
+
/* Run the BEFORE and INSTEAD OF triggers, if there are any
*/
- endOfLoop = sqlite3VdbeMakeLabel(v);
+ endOfLoop = sqlite3VdbeMakeLabel(pParse);
if( tmask & TRIGGER_BEFORE ){
int regCols = sqlite3GetTempRange(pParse, pTab->nCol+1);
@@ -116380,30 +133333,21 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v);
}
- /* Cannot have triggers on a virtual table. If it were possible,
- ** this block would have to account for hidden column.
- */
- assert( !IsVirtual(pTab) );
+ /* Copy the new data already generated. */
+ assert( pTab->nNVCol>0 || pParse->nErr>0 );
+ sqlite3VdbeAddOp3(v, OP_Copy, regRowid+1, regCols+1, pTab->nNVCol-1);
- /* Create the new column data
- */
- for(i=j=0; i<pTab->nCol; i++){
- if( pColumn ){
- for(j=0; j<pColumn->nId; j++){
- if( pColumn->a[j].idx==i ) break;
- }
- }
- if( (!useTempTable && !pList) || (pColumn && j>=pColumn->nId)
- || (pColumn==0 && IsOrdinaryHiddenColumn(&pTab->aCol[i])) ){
- sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i+1);
- }else if( useTempTable ){
- sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i+1);
- }else{
- assert( pSelect==0 ); /* Otherwise useTempTable is true */
- sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i+1);
- }
- if( pColumn==0 && !IsOrdinaryHiddenColumn(&pTab->aCol[i]) ) j++;
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ /* Compute the new value for generated columns after all other
+ ** columns have already been computed. This must be done after
+ ** computing the ROWID in case one of the generated columns
+ ** refers to the ROWID. */
+ if( pTab->tabFlags & TF_HasGenerated ){
+ testcase( pTab->tabFlags & TF_HasVirtual );
+ testcase( pTab->tabFlags & TF_HasStored );
+ sqlite3ComputeGeneratedColumns(pParse, regCols+1, pTab);
}
+#endif
/* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
** do not attempt any conversions before assembling the record.
@@ -116415,36 +133359,30 @@ SQLITE_PRIVATE void sqlite3Insert(
}
/* Fire BEFORE or INSTEAD OF triggers */
- sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE,
+ sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE,
pTab, regCols-pTab->nCol-1, onError, endOfLoop);
sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol+1);
}
- /* Compute the content of the next row to insert into a range of
- ** registers beginning at regIns.
- */
if( !isView ){
if( IsVirtual(pTab) ){
/* The row that the VUpdate opcode will delete: none */
sqlite3VdbeAddOp2(v, OP_Null, 0, regIns);
}
if( ipkColumn>=0 ){
+ /* Compute the new rowid */
if( useTempTable ){
sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regRowid);
}else if( pSelect ){
- sqlite3VdbeAddOp2(v, OP_Copy, regFromSelect+ipkColumn, regRowid);
+ /* Rowid already initialized at tag-20191021-001 */
}else{
- VdbeOp *pOp;
- sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid);
- pOp = sqlite3VdbeGetOp(v, -1);
- assert( pOp!=0 );
- if( pOp->opcode==OP_Null && !IsVirtual(pTab) ){
+ Expr *pIpk = pList->a[ipkColumn].pExpr;
+ if( pIpk->op==TK_NULL && !IsVirtual(pTab) ){
+ sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc);
appendFlag = 1;
- pOp->opcode = OP_NewRowid;
- pOp->p1 = iDataCur;
- pOp->p2 = regRowid;
- pOp->p3 = regAutoinc;
+ }else{
+ sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid);
}
}
/* If the PRIMARY KEY expression is NULL, then use OP_NewRowid
@@ -116470,45 +133408,15 @@ SQLITE_PRIVATE void sqlite3Insert(
}
autoIncStep(pParse, regAutoinc, regRowid);
- /* Compute data for all columns of the new entry, beginning
- ** with the first column.
- */
- nHidden = 0;
- for(i=0; i<pTab->nCol; i++){
- int iRegStore = regRowid+1+i;
- if( i==pTab->iPKey ){
- /* The value of the INTEGER PRIMARY KEY column is always a NULL.
- ** Whenever this column is read, the rowid will be substituted
- ** in its place. Hence, fill this column with a NULL to avoid
- ** taking up data space with information that will never be used.
- ** As there may be shallow copies of this value, make it a soft-NULL */
- sqlite3VdbeAddOp1(v, OP_SoftNull, iRegStore);
- continue;
- }
- if( pColumn==0 ){
- if( IsHiddenColumn(&pTab->aCol[i]) ){
- j = -1;
- nHidden++;
- }else{
- j = i - nHidden;
- }
- }else{
- for(j=0; j<pColumn->nId; j++){
- if( pColumn->a[j].idx==i ) break;
- }
- }
- if( j<0 || nColumn==0 || (pColumn && j>=pColumn->nId) ){
- sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore);
- }else if( useTempTable ){
- sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore);
- }else if( pSelect ){
- if( regFromSelect!=regData ){
- sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore);
- }
- }else{
- sqlite3ExprCode(pParse, pList->a[j].pExpr, iRegStore);
- }
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ /* Compute the new value for generated columns after all other
+ ** columns have already been computed. This must be done after
+ ** computing the ROWID in case one of the generated columns
+ ** is derived from the INTEGER PRIMARY KEY. */
+ if( pTab->tabFlags & TF_HasGenerated ){
+ sqlite3ComputeGeneratedColumns(pParse, regRowid+1, pTab);
}
+#endif
/* Generate code to check constraints and generate index keys and
** do the insertion.
@@ -116523,28 +133431,35 @@ SQLITE_PRIVATE void sqlite3Insert(
}else
#endif
{
- int isReplace; /* Set to true if constraints may cause a replace */
+ int isReplace = 0;/* Set to true if constraints may cause a replace */
int bUseSeek; /* True to use OPFLAG_SEEKRESULT */
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0, pUpsert
);
- sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
+ if( db->flags & SQLITE_ForeignKeys ){
+ sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
+ }
/* Set the OPFLAG_USESEEKRESULT flag if either (a) there are no REPLACE
** constraints or (b) there are no triggers and this table is not a
** parent table in a foreign key constraint. It is safe to set the
** flag in the second case as if any REPLACE constraint is hit, an
- ** OP_Delete or OP_IdxDelete instruction will be executed on each
+ ** OP_Delete or OP_IdxDelete instruction will be executed on each
** cursor that is disturbed. And these instructions both clear the
** VdbeCursor.seekResult variable, disabling the OPFLAG_USESEEKRESULT
** functionality. */
- bUseSeek = (isReplace==0 || (pTrigger==0 &&
- ((db->flags & SQLITE_ForeignKeys)==0 || sqlite3FkReferences(pTab)==0)
- ));
+ bUseSeek = (isReplace==0 || !sqlite3VdbeHasSubProgram(v));
sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
regIns, aRegIdx, 0, appendFlag, bUseSeek
);
}
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ }else if( pParse->bReturning ){
+ /* If there is a RETURNING clause, populate the rowid register with
+ ** constant value -1, in case one or more of the returned expressions
+ ** refer to the "rowid" of the view. */
+ sqlite3VdbeAddOp2(v, OP_Integer, -1, regRowid);
+#endif
}
/* Update the count of rows that are inserted
@@ -116555,7 +133470,7 @@ SQLITE_PRIVATE void sqlite3Insert(
if( pTrigger ){
/* Code AFTER triggers */
- sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER,
+ sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER,
pTab, regData-2-pTab->nCol, onError, endOfLoop);
}
@@ -116569,10 +133484,21 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3VdbeAddOp1(v, OP_Close, srcTab);
}else if( pSelect ){
sqlite3VdbeGoto(v, addrCont);
+#ifdef SQLITE_DEBUG
+ /* If we are jumping back to an OP_Yield that is preceded by an
+ ** OP_ReleaseReg, set the p5 flag on the OP_Goto so that the
+ ** OP_ReleaseReg will be included in the loop. */
+ if( sqlite3VdbeGetOp(v, addrCont-1)->opcode==OP_ReleaseReg ){
+ assert( sqlite3VdbeGetOp(v, addrCont)->opcode==OP_Yield );
+ sqlite3VdbeChangeP5(v, 1);
+ }
+#endif
sqlite3VdbeJumpHere(v, addrInsTop);
}
+#ifndef SQLITE_OMIT_XFER_OPT
insert_end:
+#endif /* SQLITE_OMIT_XFER_OPT */
/* Update the sqlite_sequence table by storing the content of the
** maximum rowid counter values recorded while inserting into
** autoincrement tables.
@@ -116582,14 +133508,12 @@ insert_end:
}
/*
- ** Return the number of rows inserted. If this routine is
+ ** Return the number of rows inserted. If this routine is
** generating code because of a call to sqlite3NestedParse(), do not
** invoke the callback function.
*/
if( regRowCount ){
- sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
+ sqlite3CodeChangeCount(v, regRowCount, "rows inserted");
}
insert_cleanup:
@@ -116598,7 +133522,7 @@ insert_cleanup:
sqlite3UpsertDelete(db, pUpsert);
sqlite3SelectDelete(db, pSelect);
sqlite3IdListDelete(db, pColumn);
- sqlite3DbFree(db, aRegIdx);
+ if( aRegIdx ) sqlite3DbNNFreeNN(db, aRegIdx);
}
/* Make sure "isView" and other macros defined above are undefined. Otherwise
@@ -116615,7 +133539,7 @@ insert_cleanup:
#endif
/*
-** Meanings of bits in of pWalker->eCode for
+** Meanings of bits in of pWalker->eCode for
** sqlite3ExprReferencesUpdatedColumn()
*/
#define CKCNSTRNT_COLUMN 0x01 /* CHECK constraint uses a changing column */
@@ -116624,7 +133548,7 @@ insert_cleanup:
/* This is the Walker callback from sqlite3ExprReferencesUpdatedColumn().
* Set bit 0x01 of pWalker->eCode if pWalker->eCode to 0 and if this
** expression node references any of the
-** columns that are being modifed by an UPDATE statement.
+** columns that are being modified by an UPDATE statement.
*/
static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){
if( pExpr->op==TK_COLUMN ){
@@ -116678,6 +133602,70 @@ SQLITE_PRIVATE int sqlite3ExprReferencesUpdatedColumn(
}
/*
+** The sqlite3GenerateConstraintChecks() routine usually wants to visit
+** the indexes of a table in the order provided in the Table->pIndex list.
+** However, sometimes (rarely - when there is an upsert) it wants to visit
+** the indexes in a different order. The following data structures accomplish
+** this.
+**
+** The IndexIterator object is used to walk through all of the indexes
+** of a table in either Index.pNext order, or in some other order established
+** by an array of IndexListTerm objects.
+*/
+typedef struct IndexListTerm IndexListTerm;
+typedef struct IndexIterator IndexIterator;
+struct IndexIterator {
+ int eType; /* 0 for Index.pNext list. 1 for an array of IndexListTerm */
+ int i; /* Index of the current item from the list */
+ union {
+ struct { /* Use this object for eType==0: A Index.pNext list */
+ Index *pIdx; /* The current Index */
+ } lx;
+ struct { /* Use this object for eType==1; Array of IndexListTerm */
+ int nIdx; /* Size of the array */
+ IndexListTerm *aIdx; /* Array of IndexListTerms */
+ } ax;
+ } u;
+};
+
+/* When IndexIterator.eType==1, then each index is an array of instances
+** of the following object
+*/
+struct IndexListTerm {
+ Index *p; /* The index */
+ int ix; /* Which entry in the original Table.pIndex list is this index*/
+};
+
+/* Return the first index on the list */
+static Index *indexIteratorFirst(IndexIterator *pIter, int *pIx){
+ assert( pIter->i==0 );
+ if( pIter->eType ){
+ *pIx = pIter->u.ax.aIdx[0].ix;
+ return pIter->u.ax.aIdx[0].p;
+ }else{
+ *pIx = 0;
+ return pIter->u.lx.pIdx;
+ }
+}
+
+/* Return the next index from the list. Return NULL when out of indexes */
+static Index *indexIteratorNext(IndexIterator *pIter, int *pIx){
+ if( pIter->eType ){
+ int i = ++pIter->i;
+ if( i>=pIter->u.ax.nIdx ){
+ *pIx = i;
+ return 0;
+ }
+ *pIx = pIter->u.ax.aIdx[i].ix;
+ return pIter->u.ax.aIdx[i].p;
+ }else{
+ ++(*pIx);
+ pIter->u.lx.pIdx = pIter->u.lx.pIdx->pNext;
+ return pIter->u.lx.pIdx;
+ }
+}
+
+/*
** Generate code to do constraint checks prior to an INSERT or an UPDATE
** on table pTab.
**
@@ -116712,6 +133700,14 @@ SQLITE_PRIVATE int sqlite3ExprReferencesUpdatedColumn(
** the same as the order of indices on the linked list of indices
** at pTab->pIndex.
**
+** (2019-05-07) The generated code also creates a new record for the
+** main table, if pTab is a rowid table, and stores that record in the
+** register identified by aRegIdx[nIdx] - in other words in the first
+** entry of aRegIdx[] past the last index. It is important that the
+** record be generated during constraint checks to avoid affinity changes
+** to the register content that occur after constraint checks but before
+** the new record is inserted.
+**
** The caller must have already opened writeable cursors on the main
** table and all applicable indices (that is to say, all indices for which
** aRegIdx[] is not zero). iDataCur is the cursor for the main table when
@@ -116775,34 +133771,41 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
int *aiChng, /* column i is unchanged if aiChng[i]<0 */
Upsert *pUpsert /* ON CONFLICT clauses, if any. NULL otherwise */
){
- Vdbe *v; /* VDBE under constrution */
+ Vdbe *v; /* VDBE under construction */
Index *pIdx; /* Pointer to one of the indices */
- Index *pPk = 0; /* The PRIMARY KEY index */
+ Index *pPk = 0; /* The PRIMARY KEY index for WITHOUT ROWID tables */
sqlite3 *db; /* Database connection */
int i; /* loop counter */
int ix; /* Index loop counter */
int nCol; /* Number of columns */
int onError; /* Conflict resolution strategy */
- int addr1; /* Address of jump instruction */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
- Index *pUpIdx = 0; /* Index to which to apply the upsert */
- u8 isUpdate; /* True if this is an UPDATE operation */
+ Upsert *pUpsertClause = 0; /* The specific ON CONFLICT clause for pIdx */
+ u8 isUpdate; /* True if this is an UPDATE operation */
u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */
- int upsertBypass = 0; /* Address of Goto to bypass upsert subroutine */
- int upsertJump = 0; /* Address of Goto that jumps into upsert subroutine */
+ int upsertIpkReturn = 0; /* Address of Goto at end of IPK uniqueness check */
+ int upsertIpkDelay = 0; /* Address of Goto to bypass initial IPK check */
int ipkTop = 0; /* Top of the IPK uniqueness check */
int ipkBottom = 0; /* OP_Goto at the end of the IPK uniqueness check */
+ /* Variables associated with retesting uniqueness constraints after
+ ** replace triggers fire have run */
+ int regTrigCnt; /* Register used to count replace trigger invocations */
+ int addrRecheck = 0; /* Jump here to recheck all uniqueness constraints */
+ int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */
+ Trigger *pTrigger; /* List of DELETE triggers on the table pTab */
+ int nReplaceTrig = 0; /* Number of replace triggers coded */
+ IndexIterator sIdxIter; /* Index iterator */
isUpdate = regOldData!=0;
db = pParse->db;
- v = sqlite3GetVdbe(pParse);
+ v = pParse->pVdbe;
assert( v!=0 );
- assert( pTab->pSelect==0 ); /* This table is not a VIEW */
+ assert( !IsView(pTab) ); /* This table is not a VIEW */
nCol = pTab->nCol;
-
+
/* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for
- ** normal rowid tables. nPkField is the number of key fields in the
+ ** normal rowid tables. nPkField is the number of key fields in the
** pPk index or 1 for a rowid table. In other words, nPkField is the
** number of fields in the true primary key of the table. */
if( HasRowid(pTab) ){
@@ -116819,56 +133822,105 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
/* Test all NOT NULL constraints.
*/
- for(i=0; i<nCol; i++){
- if( i==pTab->iPKey ){
- continue; /* ROWID is never NULL */
- }
- if( aiChng && aiChng[i]<0 ){
- /* Don't bother checking for NOT NULL on columns that do not change */
- continue;
- }
- onError = pTab->aCol[i].notNull;
- if( onError==OE_None ) continue; /* This column is allowed to be NULL */
- if( overrideError!=OE_Default ){
- onError = overrideError;
- }else if( onError==OE_Default ){
- onError = OE_Abort;
- }
- if( onError==OE_Replace && pTab->aCol[i].pDflt==0 ){
- onError = OE_Abort;
- }
- assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
- || onError==OE_Ignore || onError==OE_Replace );
- switch( onError ){
- case OE_Abort:
- sqlite3MayAbort(pParse);
- /* Fall through */
- case OE_Rollback:
- case OE_Fail: {
- char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
- pTab->aCol[i].zName);
- sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError,
- regNewData+1+i);
- sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC);
- sqlite3VdbeChangeP5(v, P5_ConstraintNotNull);
- VdbeCoverage(v);
- break;
- }
- case OE_Ignore: {
- sqlite3VdbeAddOp2(v, OP_IsNull, regNewData+1+i, ignoreDest);
- VdbeCoverage(v);
+ if( pTab->tabFlags & TF_HasNotNull ){
+ int b2ndPass = 0; /* True if currently running 2nd pass */
+ int nSeenReplace = 0; /* Number of ON CONFLICT REPLACE operations */
+ int nGenerated = 0; /* Number of generated columns with NOT NULL */
+ while(1){ /* Make 2 passes over columns. Exit loop via "break" */
+ for(i=0; i<nCol; i++){
+ int iReg; /* Register holding column value */
+ Column *pCol = &pTab->aCol[i]; /* The column to check for NOT NULL */
+ int isGenerated; /* non-zero if column is generated */
+ onError = pCol->notNull;
+ if( onError==OE_None ) continue; /* No NOT NULL on this column */
+ if( i==pTab->iPKey ){
+ continue; /* ROWID is never NULL */
+ }
+ isGenerated = pCol->colFlags & COLFLAG_GENERATED;
+ if( isGenerated && !b2ndPass ){
+ nGenerated++;
+ continue; /* Generated columns processed on 2nd pass */
+ }
+ if( aiChng && aiChng[i]<0 && !isGenerated ){
+ /* Do not check NOT NULL on columns that do not change */
+ continue;
+ }
+ if( overrideError!=OE_Default ){
+ onError = overrideError;
+ }else if( onError==OE_Default ){
+ onError = OE_Abort;
+ }
+ if( onError==OE_Replace ){
+ if( b2ndPass /* REPLACE becomes ABORT on the 2nd pass */
+ || pCol->iDflt==0 /* REPLACE is ABORT if no DEFAULT value */
+ ){
+ testcase( pCol->colFlags & COLFLAG_VIRTUAL );
+ testcase( pCol->colFlags & COLFLAG_STORED );
+ testcase( pCol->colFlags & COLFLAG_GENERATED );
+ onError = OE_Abort;
+ }else{
+ assert( !isGenerated );
+ }
+ }else if( b2ndPass && !isGenerated ){
+ continue;
+ }
+ assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
+ || onError==OE_Ignore || onError==OE_Replace );
+ testcase( i!=sqlite3TableColumnToStorage(pTab, i) );
+ iReg = sqlite3TableColumnToStorage(pTab, i) + regNewData + 1;
+ switch( onError ){
+ case OE_Replace: {
+ int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, iReg);
+ VdbeCoverage(v);
+ assert( (pCol->colFlags & COLFLAG_GENERATED)==0 );
+ nSeenReplace++;
+ sqlite3ExprCodeCopy(pParse,
+ sqlite3ColumnExpr(pTab, pCol), iReg);
+ sqlite3VdbeJumpHere(v, addr1);
+ break;
+ }
+ case OE_Abort:
+ sqlite3MayAbort(pParse);
+ /* no break */ deliberate_fall_through
+ case OE_Rollback:
+ case OE_Fail: {
+ char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
+ pCol->zCnName);
+ testcase( zMsg==0 && db->mallocFailed==0 );
+ sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL,
+ onError, iReg);
+ sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC);
+ sqlite3VdbeChangeP5(v, P5_ConstraintNotNull);
+ VdbeCoverage(v);
+ break;
+ }
+ default: {
+ assert( onError==OE_Ignore );
+ sqlite3VdbeAddOp2(v, OP_IsNull, iReg, ignoreDest);
+ VdbeCoverage(v);
+ break;
+ }
+ } /* end switch(onError) */
+ } /* end loop i over columns */
+ if( nGenerated==0 && nSeenReplace==0 ){
+ /* If there are no generated columns with NOT NULL constraints
+ ** and no NOT NULL ON CONFLICT REPLACE constraints, then a single
+ ** pass is sufficient */
break;
}
- default: {
- assert( onError==OE_Replace );
- addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i);
- VdbeCoverage(v);
- sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i);
- sqlite3VdbeJumpHere(v, addr1);
- break;
+ if( b2ndPass ) break; /* Never need more than 2 passes */
+ b2ndPass = 1;
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ if( nSeenReplace>0 && (pTab->tabFlags & TF_HasGenerated)!=0 ){
+ /* If any NOT NULL ON CONFLICT REPLACE constraints fired on the
+ ** first pass, recomputed values for all generated columns, as
+ ** those values might depend on columns affected by the REPLACE.
+ */
+ sqlite3ComputeGeneratedColumns(pParse, regNewData+1, pTab);
}
- }
- }
+#endif
+ } /* end of 2-pass loop */
+ } /* end if( has-not-null-constraints ) */
/* Test all CHECK constraints
*/
@@ -116879,6 +133931,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
onError = overrideError!=OE_Default ? overrideError : OE_Abort;
for(i=0; i<pCheck->nExpr; i++){
int allOk;
+ Expr *pCopy;
Expr *pExpr = pCheck->a[i].pExpr;
if( aiChng
&& !sqlite3ExprReferencesUpdatedColumn(pExpr, aiChng, pkChng)
@@ -116887,15 +133940,23 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
** updated so there is no point it verifying the check constraint */
continue;
}
- allOk = sqlite3VdbeMakeLabel(v);
+ if( bAffinityDone==0 ){
+ sqlite3TableAffinity(v, pTab, regNewData+1);
+ bAffinityDone = 1;
+ }
+ allOk = sqlite3VdbeMakeLabel(pParse);
sqlite3VdbeVerifyAbortable(v, onError);
- sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL);
+ pCopy = sqlite3ExprDup(db, pExpr, 0);
+ if( !db->mallocFailed ){
+ sqlite3ExprIfTrue(pParse, pCopy, allOk, SQLITE_JUMPIFNULL);
+ }
+ sqlite3ExprDelete(db, pCopy);
if( onError==OE_Ignore ){
sqlite3VdbeGoto(v, ignoreDest);
}else{
- char *zName = pCheck->a[i].zName;
- if( zName==0 ) zName = pTab->zName;
- if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */
+ char *zName = pCheck->a[i].zEName;
+ assert( zName!=0 || pParse->db->mallocFailed );
+ if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-26383-51744 */
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK,
onError, zName, P4_TRANSIENT,
P5_ConstraintCheck);
@@ -116934,19 +133995,107 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
** list of indexes attached to a table puts all OE_Replace indexes last
** in the list. See sqlite3CreateIndex() for where that happens.
*/
-
+ sIdxIter.eType = 0;
+ sIdxIter.i = 0;
+ sIdxIter.u.ax.aIdx = 0; /* Silence harmless compiler warning */
+ sIdxIter.u.lx.pIdx = pTab->pIndex;
if( pUpsert ){
if( pUpsert->pUpsertTarget==0 ){
- /* An ON CONFLICT DO NOTHING clause, without a constraint-target.
- ** Make all unique constraint resolution be OE_Ignore */
- assert( pUpsert->pUpsertSet==0 );
- overrideError = OE_Ignore;
- pUpsert = 0;
- }else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){
- /* If the constraint-target uniqueness check must be run first.
- ** Jump to that uniqueness check now */
- upsertJump = sqlite3VdbeAddOp0(v, OP_Goto);
- VdbeComment((v, "UPSERT constraint goes first"));
+ /* There is just on ON CONFLICT clause and it has no constraint-target */
+ assert( pUpsert->pNextUpsert==0 );
+ if( pUpsert->isDoUpdate==0 ){
+ /* A single ON CONFLICT DO NOTHING clause, without a constraint-target.
+ ** Make all unique constraint resolution be OE_Ignore */
+ overrideError = OE_Ignore;
+ pUpsert = 0;
+ }else{
+ /* A single ON CONFLICT DO UPDATE. Make all resolutions OE_Update */
+ overrideError = OE_Update;
+ }
+ }else if( pTab->pIndex!=0 ){
+ /* Otherwise, we'll need to run the IndexListTerm array version of the
+ ** iterator to ensure that all of the ON CONFLICT conditions are
+ ** checked first and in order. */
+ int nIdx, jj;
+ u64 nByte;
+ Upsert *pTerm;
+ u8 *bUsed;
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
+ assert( aRegIdx[nIdx]>0 );
+ }
+ sIdxIter.eType = 1;
+ sIdxIter.u.ax.nIdx = nIdx;
+ nByte = (sizeof(IndexListTerm)+1)*nIdx + nIdx;
+ sIdxIter.u.ax.aIdx = sqlite3DbMallocZero(db, nByte);
+ if( sIdxIter.u.ax.aIdx==0 ) return; /* OOM */
+ bUsed = (u8*)&sIdxIter.u.ax.aIdx[nIdx];
+ pUpsert->pToFree = sIdxIter.u.ax.aIdx;
+ for(i=0, pTerm=pUpsert; pTerm; pTerm=pTerm->pNextUpsert){
+ if( pTerm->pUpsertTarget==0 ) break;
+ if( pTerm->pUpsertIdx==0 ) continue; /* Skip ON CONFLICT for the IPK */
+ jj = 0;
+ pIdx = pTab->pIndex;
+ while( ALWAYS(pIdx!=0) && pIdx!=pTerm->pUpsertIdx ){
+ pIdx = pIdx->pNext;
+ jj++;
+ }
+ if( bUsed[jj] ) continue; /* Duplicate ON CONFLICT clause ignored */
+ bUsed[jj] = 1;
+ sIdxIter.u.ax.aIdx[i].p = pIdx;
+ sIdxIter.u.ax.aIdx[i].ix = jj;
+ i++;
+ }
+ for(jj=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, jj++){
+ if( bUsed[jj] ) continue;
+ sIdxIter.u.ax.aIdx[i].p = pIdx;
+ sIdxIter.u.ax.aIdx[i].ix = jj;
+ i++;
+ }
+ assert( i==nIdx );
+ }
+ }
+
+ /* Determine if it is possible that triggers (either explicitly coded
+ ** triggers or FK resolution actions) might run as a result of deletes
+ ** that happen when OE_Replace conflict resolution occurs. (Call these
+ ** "replace triggers".) If any replace triggers run, we will need to
+ ** recheck all of the uniqueness constraints after they have all run.
+ ** But on the recheck, the resolution is OE_Abort instead of OE_Replace.
+ **
+ ** If replace triggers are a possibility, then
+ **
+ ** (1) Allocate register regTrigCnt and initialize it to zero.
+ ** That register will count the number of replace triggers that
+ ** fire. Constraint recheck only occurs if the number is positive.
+ ** (2) Initialize pTrigger to the list of all DELETE triggers on pTab.
+ ** (3) Initialize addrRecheck and lblRecheckOk
+ **
+ ** The uniqueness rechecking code will create a series of tests to run
+ ** in a second pass. The addrRecheck and lblRecheckOk variables are
+ ** used to link together these tests which are separated from each other
+ ** in the generate bytecode.
+ */
+ if( (db->flags & (SQLITE_RecTriggers|SQLITE_ForeignKeys))==0 ){
+ /* There are not DELETE triggers nor FK constraints. No constraint
+ ** rechecks are needed. */
+ pTrigger = 0;
+ regTrigCnt = 0;
+ }else{
+ if( db->flags&SQLITE_RecTriggers ){
+ pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
+ regTrigCnt = pTrigger!=0 || sqlite3FkRequired(pParse, pTab, 0, 0);
+ }else{
+ pTrigger = 0;
+ regTrigCnt = sqlite3FkRequired(pParse, pTab, 0, 0);
+ }
+ if( regTrigCnt ){
+ /* Replace triggers might exist. Allocate the counter and
+ ** initialize it to zero. */
+ regTrigCnt = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regTrigCnt);
+ VdbeComment((v, "trigger count"));
+ lblRecheckOk = sqlite3VdbeMakeLabel(pParse);
+ addrRecheck = lblRecheckOk;
}
}
@@ -116954,7 +134103,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
** exist in the table.
*/
if( pkChng && pPk==0 ){
- int addrRowidOk = sqlite3VdbeMakeLabel(v);
+ int addrRowidOk = sqlite3VdbeMakeLabel(pParse);
/* Figure out what action to take in case of a rowid collision */
onError = pTab->keyConf;
@@ -116965,11 +134114,20 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
}
/* figure out whether or not upsert applies in this case */
- if( pUpsert && pUpsert->pUpsertIdx==0 ){
- if( pUpsert->pUpsertSet==0 ){
- onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */
- }else{
- onError = OE_Update; /* DO UPDATE */
+ if( pUpsert ){
+ pUpsertClause = sqlite3UpsertOfIndex(pUpsert,0);
+ if( pUpsertClause!=0 ){
+ if( pUpsertClause->isDoUpdate==0 ){
+ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */
+ }else{
+ onError = OE_Update; /* DO UPDATE */
+ }
+ }
+ if( pUpsertClause!=pUpsert ){
+ /* The first ON CONFLICT clause has a conflict target other than
+ ** the IPK. We have to jump ahead to that first ON CONFLICT clause
+ ** and then come back here and deal with the IPK afterwards */
+ upsertIpkDelay = sqlite3VdbeAddOp0(v, OP_Goto);
}
}
@@ -116979,8 +134137,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
** the UNIQUE constraints have run.
*/
if( onError==OE_Replace /* IPK rule is REPLACE */
- && onError!=overrideError /* Rules for other contraints are different */
+ && onError!=overrideError /* Rules for other constraints are different */
&& pTab->pIndex /* There exist other constraints */
+ && !upsertIpkDelay /* IPK check already deferred by UPSERT */
){
ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
VdbeComment((v, "defer IPK REPLACE until last"));
@@ -117005,7 +134164,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
switch( onError ){
default: {
onError = OE_Abort;
- /* Fall thru into the next case */
+ /* no break */ deliberate_fall_through
}
case OE_Rollback:
case OE_Abort:
@@ -117023,10 +134182,10 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
** the triggers and remove both the table and index b-tree entries.
**
** Otherwise, if there are no triggers or the recursive-triggers
- ** flag is not set, but the table has one or more indexes, call
- ** GenerateRowIndexDelete(). This removes the index b-tree entries
- ** only. The table b-tree entry will be replaced by the new entry
- ** when it is inserted.
+ ** flag is not set, but the table has one or more indexes, call
+ ** GenerateRowIndexDelete(). This removes the index b-tree entries
+ ** only. The table b-tree entry will be replaced by the new entry
+ ** when it is inserted.
**
** If either GenerateRowDelete() or GenerateRowIndexDelete() is called,
** also invoke MultiWrite() to indicate that this VDBE may require
@@ -117039,14 +134198,12 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
** to run without a statement journal if there are no indexes on the
** table.
*/
- Trigger *pTrigger = 0;
- if( db->flags&SQLITE_RecTriggers ){
- pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
- }
- if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
+ if( regTrigCnt ){
sqlite3MultiWrite(pParse);
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
regNewData, 1, 0, OE_Replace, 1, -1);
+ sqlite3VdbeAddOp2(v, OP_AddImm, regTrigCnt, 1); /* incr trigger cnt */
+ nReplaceTrig++;
}else{
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
assert( HasRowid(pTab) );
@@ -117068,7 +134225,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
#ifndef SQLITE_OMIT_UPSERT
case OE_Update: {
sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur);
- /* Fall through */
+ /* no break */ deliberate_fall_through
}
#endif
case OE_Ignore: {
@@ -117078,7 +134235,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
}
}
sqlite3VdbeResolveLabel(v, addrRowidOk);
- if( ipkTop ){
+ if( pUpsert && pUpsertClause!=pUpsert ){
+ upsertIpkReturn = sqlite3VdbeAddOp0(v, OP_Goto);
+ }else if( ipkTop ){
ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, ipkTop-1);
}
@@ -117091,26 +134250,29 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
** This loop also handles the case of the PRIMARY KEY index for a
** WITHOUT ROWID table.
*/
- for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){
- int regIdx; /* Range of registers hold conent for pIdx */
+ for(pIdx = indexIteratorFirst(&sIdxIter, &ix);
+ pIdx;
+ pIdx = indexIteratorNext(&sIdxIter, &ix)
+ ){
+ int regIdx; /* Range of registers holding content for pIdx */
int regR; /* Range of registers holding conflicting PK */
int iThisCur; /* Cursor for this UNIQUE index */
int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */
+ int addrConflictCk; /* First opcode in the conflict check logic */
if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */
- if( pUpIdx==pIdx ){
- addrUniqueOk = upsertJump+1;
- upsertBypass = sqlite3VdbeGoto(v, 0);
- VdbeComment((v, "Skip upsert subroutine"));
- sqlite3VdbeJumpHere(v, upsertJump);
- }else{
- addrUniqueOk = sqlite3VdbeMakeLabel(v);
+ if( pUpsert ){
+ pUpsertClause = sqlite3UpsertOfIndex(pUpsert, pIdx);
+ if( upsertIpkDelay && pUpsertClause==pUpsert ){
+ sqlite3VdbeJumpHere(v, upsertIpkDelay);
+ }
}
- if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){
+ addrUniqueOk = sqlite3VdbeMakeLabel(pParse);
+ if( bAffinityDone==0 ){
sqlite3TableAffinity(v, pTab, regNewData+1);
bAffinityDone = 1;
}
- VdbeNoopComment((v, "uniqueness check for %s", pIdx->zName));
+ VdbeNoopComment((v, "prep index %s", pIdx->zName));
iThisCur = iIdxCur+ix;
@@ -117135,23 +134297,27 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[i].pExpr, regIdx+i);
pParse->iSelfTab = 0;
VdbeComment((v, "%s column %d", pIdx->zName, i));
+ }else if( iField==XN_ROWID || iField==pTab->iPKey ){
+ x = regNewData;
+ sqlite3VdbeAddOp2(v, OP_IntCopy, x, regIdx+i);
+ VdbeComment((v, "rowid"));
}else{
- if( iField==XN_ROWID || iField==pTab->iPKey ){
- x = regNewData;
- }else{
- x = iField + regNewData + 1;
- }
- sqlite3VdbeAddOp2(v, iField<0 ? OP_IntCopy : OP_SCopy, x, regIdx+i);
- VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
+ testcase( sqlite3TableColumnToStorage(pTab, iField)!=iField );
+ x = sqlite3TableColumnToStorage(pTab, iField) + regNewData + 1;
+ sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i);
+ VdbeComment((v, "%s", pTab->aCol[iField].zCnName));
}
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]);
VdbeComment((v, "for %s", pIdx->zName));
#ifdef SQLITE_ENABLE_NULL_TRIM
- if( pIdx->idxType==2 ) sqlite3SetMakeRecordP5(v, pIdx->pTable);
+ if( pIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){
+ sqlite3SetMakeRecordP5(v, pIdx->pTable);
+ }
#endif
+ sqlite3VdbeReleaseRegisters(pParse, regIdx, pIdx->nColumn, 0, 0);
- /* In an UPDATE operation, if this index is the PRIMARY KEY index
+ /* In an UPDATE operation, if this index is the PRIMARY KEY index
** of a WITHOUT ROWID table and there has been no change the
** primary key, then no collision is possible. The collision detection
** logic below can all be skipped. */
@@ -117162,7 +134328,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
/* Find out what action to take in case there is a uniqueness conflict */
onError = pIdx->onError;
- if( onError==OE_None ){
+ if( onError==OE_None ){
sqlite3VdbeResolveLabel(v, addrUniqueOk);
continue; /* pIdx is not a UNIQUE index */
}
@@ -117173,8 +134339,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
}
/* Figure out if the upsert clause applies to this index */
- if( pUpIdx==pIdx ){
- if( pUpsert->pUpsertSet==0 ){
+ if( pUpsertClause ){
+ if( pUpsertClause->isDoUpdate==0 ){
onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */
}else{
onError = OE_Update; /* DO UPDATE */
@@ -117187,26 +134353,33 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
** (3) There are no secondary indexes on the table
** (4) No delete triggers need to be fired if there is a conflict
** (5) No FK constraint counters need to be updated if a conflict occurs.
- */
+ **
+ ** This is not possible for ENABLE_PREUPDATE_HOOK builds, as the row
+ ** must be explicitly deleted in order to ensure any pre-update hook
+ ** is invoked. */
+ assert( IsOrdinaryTable(pTab) );
+#ifndef SQLITE_ENABLE_PREUPDATE_HOOK
if( (ix==0 && pIdx->pNext==0) /* Condition 3 */
&& pPk==pIdx /* Condition 2 */
&& onError==OE_Replace /* Condition 1 */
&& ( 0==(db->flags&SQLITE_RecTriggers) || /* Condition 4 */
0==sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0))
&& ( 0==(db->flags&SQLITE_ForeignKeys) || /* Condition 5 */
- (0==pTab->pFKey && 0==sqlite3FkReferences(pTab)))
+ (0==pTab->u.tab.pFKey && 0==sqlite3FkReferences(pTab)))
){
sqlite3VdbeResolveLabel(v, addrUniqueOk);
continue;
}
+#endif /* ifndef SQLITE_ENABLE_PREUPDATE_HOOK */
/* Check to see if the new index entry will be unique */
sqlite3VdbeVerifyAbortable(v, onError);
- sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
- regIdx, pIdx->nKeyCol); VdbeCoverage(v);
+ addrConflictCk =
+ sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
+ regIdx, pIdx->nKeyCol); VdbeCoverage(v);
/* Generate code to handle collisions */
- regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField);
+ regR = pIdx==pPk ? regIdx : sqlite3GetTempRange(pParse, nPkField);
if( isUpdate || onError==OE_Replace ){
if( HasRowid(pTab) ){
sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR);
@@ -117224,16 +134397,16 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
if( pIdx!=pPk ){
for(i=0; i<pPk->nKeyCol; i++){
assert( pPk->aiColumn[i]>=0 );
- x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]);
+ x = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]);
sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i);
VdbeComment((v, "%s.%s", pTab->zName,
- pTab->aCol[pPk->aiColumn[i]].zName));
+ pTab->aCol[pPk->aiColumn[i]].zCnName));
}
}
if( isUpdate ){
- /* If currently processing the PRIMARY KEY of a WITHOUT ROWID
+ /* If currently processing the PRIMARY KEY of a WITHOUT ROWID
** table, only conflict if the new PRIMARY KEY values are actually
- ** different from the old.
+ ** different from the old. See TH3 withoutrowid04.test.
**
** For a UNIQUE index, only conflict if the PRIMARY KEY values
** of the matched index row are different from the original PRIMARY
@@ -117241,7 +134414,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol;
int op = OP_Ne;
int regCmp = (IsPrimaryKeyIndex(pIdx) ? regIdx : regR);
-
+
for(i=0; i<pPk->nKeyCol; i++){
char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]);
x = pPk->aiColumn[i];
@@ -117250,7 +134423,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
addrJump = addrUniqueOk;
op = OP_Eq;
}
- sqlite3VdbeAddOp4(v, op,
+ x = sqlite3TableColumnToStorage(pTab, x);
+ sqlite3VdbeAddOp4(v, op,
regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ
);
sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
@@ -117277,7 +134451,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
#ifndef SQLITE_OMIT_UPSERT
case OE_Update: {
sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix);
- /* Fall through */
+ /* no break */ deliberate_fall_through
}
#endif
case OE_Ignore: {
@@ -117286,37 +134460,128 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
break;
}
default: {
- Trigger *pTrigger = 0;
+ int nConflictCk; /* Number of opcodes in conflict check logic */
+
assert( onError==OE_Replace );
- if( db->flags&SQLITE_RecTriggers ){
- pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
- }
- if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
+ nConflictCk = sqlite3VdbeCurrentAddr(v) - addrConflictCk;
+ assert( nConflictCk>0 || db->mallocFailed );
+ testcase( nConflictCk<=0 );
+ testcase( nConflictCk>1 );
+ if( regTrigCnt ){
sqlite3MultiWrite(pParse);
+ nReplaceTrig++;
+ }
+ if( pTrigger && isUpdate ){
+ sqlite3VdbeAddOp1(v, OP_CursorLock, iDataCur);
}
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
regR, nPkField, 0, OE_Replace,
(pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), iThisCur);
+ if( pTrigger && isUpdate ){
+ sqlite3VdbeAddOp1(v, OP_CursorUnlock, iDataCur);
+ }
+ if( regTrigCnt ){
+ int addrBypass; /* Jump destination to bypass recheck logic */
+
+ sqlite3VdbeAddOp2(v, OP_AddImm, regTrigCnt, 1); /* incr trigger cnt */
+ addrBypass = sqlite3VdbeAddOp0(v, OP_Goto); /* Bypass recheck */
+ VdbeComment((v, "bypass recheck"));
+
+ /* Here we insert code that will be invoked after all constraint
+ ** checks have run, if and only if one or more replace triggers
+ ** fired. */
+ sqlite3VdbeResolveLabel(v, lblRecheckOk);
+ lblRecheckOk = sqlite3VdbeMakeLabel(pParse);
+ if( pIdx->pPartIdxWhere ){
+ /* Bypass the recheck if this partial index is not defined
+ ** for the current row */
+ sqlite3VdbeAddOp2(v, OP_IsNull, regIdx-1, lblRecheckOk);
+ VdbeCoverage(v);
+ }
+ /* Copy the constraint check code from above, except change
+ ** the constraint-ok jump destination to be the address of
+ ** the next retest block */
+ while( nConflictCk>0 ){
+ VdbeOp x; /* Conflict check opcode to copy */
+ /* The sqlite3VdbeAddOp4() call might reallocate the opcode array.
+ ** Hence, make a complete copy of the opcode, rather than using
+ ** a pointer to the opcode. */
+ x = *sqlite3VdbeGetOp(v, addrConflictCk);
+ if( x.opcode!=OP_IdxRowid ){
+ int p2; /* New P2 value for copied conflict check opcode */
+ const char *zP4;
+ if( sqlite3OpcodeProperty[x.opcode]&OPFLG_JUMP ){
+ p2 = lblRecheckOk;
+ }else{
+ p2 = x.p2;
+ }
+ zP4 = x.p4type==P4_INT32 ? SQLITE_INT_TO_PTR(x.p4.i) : x.p4.z;
+ sqlite3VdbeAddOp4(v, x.opcode, x.p1, p2, x.p3, zP4, x.p4type);
+ sqlite3VdbeChangeP5(v, x.p5);
+ VdbeCoverageIf(v, p2!=x.p2);
+ }
+ nConflictCk--;
+ addrConflictCk++;
+ }
+ /* If the retest fails, issue an abort */
+ sqlite3UniqueConstraint(pParse, OE_Abort, pIdx);
+
+ sqlite3VdbeJumpHere(v, addrBypass); /* Terminate the recheck bypass */
+ }
seenReplace = 1;
break;
}
}
- if( pUpIdx==pIdx ){
- sqlite3VdbeGoto(v, upsertJump+1);
- sqlite3VdbeJumpHere(v, upsertBypass);
- }else{
- sqlite3VdbeResolveLabel(v, addrUniqueOk);
- }
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
+ if( pUpsertClause
+ && upsertIpkReturn
+ && sqlite3UpsertNextIsIPK(pUpsertClause)
+ ){
+ sqlite3VdbeGoto(v, upsertIpkDelay+1);
+ sqlite3VdbeJumpHere(v, upsertIpkReturn);
+ upsertIpkReturn = 0;
+ }
}
/* If the IPK constraint is a REPLACE, run it last */
if( ipkTop ){
- sqlite3VdbeGoto(v, ipkTop+1);
+ sqlite3VdbeGoto(v, ipkTop);
VdbeComment((v, "Do IPK REPLACE"));
+ assert( ipkBottom>0 );
sqlite3VdbeJumpHere(v, ipkBottom);
}
+ /* Recheck all uniqueness constraints after replace triggers have run */
+ testcase( regTrigCnt!=0 && nReplaceTrig==0 );
+ assert( regTrigCnt!=0 || nReplaceTrig==0 );
+ if( nReplaceTrig ){
+ sqlite3VdbeAddOp2(v, OP_IfNot, regTrigCnt, lblRecheckOk);VdbeCoverage(v);
+ if( !pPk ){
+ if( isUpdate ){
+ sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRecheck, regOldData);
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
+ VdbeCoverage(v);
+ }
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRecheck, regNewData);
+ VdbeCoverage(v);
+ sqlite3RowidConstraint(pParse, OE_Abort, pTab);
+ }else{
+ sqlite3VdbeGoto(v, addrRecheck);
+ }
+ sqlite3VdbeResolveLabel(v, lblRecheckOk);
+ }
+
+ /* Generate the table record */
+ if( HasRowid(pTab) ){
+ int regRec = aRegIdx[ix];
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regNewData+1, pTab->nNVCol, regRec);
+ sqlite3SetMakeRecordP5(v, pTab);
+ if( !bAffinityDone ){
+ sqlite3TableAffinity(v, pTab, 0);
+ }
+ }
+
*pbMayReplace = seenReplace;
VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
}
@@ -117336,7 +134601,7 @@ SQLITE_PRIVATE void sqlite3SetMakeRecordP5(Vdbe *v, Table *pTab){
if( pTab->pSchema->file_format<2 ) return;
for(i=pTab->nCol-1; i>0; i--){
- if( pTab->aCol[i].pDflt!=0 ) break;
+ if( pTab->aCol[i].iDflt!=0 ) break;
if( pTab->aCol[i].colFlags & COLFLAG_PRIMKEY ) break;
}
sqlite3VdbeChangeP5(v, i+1);
@@ -117344,6 +134609,32 @@ SQLITE_PRIVATE void sqlite3SetMakeRecordP5(Vdbe *v, Table *pTab){
#endif
/*
+** Table pTab is a WITHOUT ROWID table that is being written to. The cursor
+** number is iCur, and register regData contains the new record for the
+** PK index. This function adds code to invoke the pre-update hook,
+** if one is registered.
+*/
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+static void codeWithoutRowidPreupdate(
+ Parse *pParse, /* Parse context */
+ Table *pTab, /* Table being updated */
+ int iCur, /* Cursor number for table */
+ int regData /* Data containing new record */
+){
+ Vdbe *v = pParse->pVdbe;
+ int r = sqlite3GetTempReg(pParse);
+ assert( !HasRowid(pTab) );
+ assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) || CORRUPT_DB );
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
+ sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE);
+ sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
+ sqlite3ReleaseTempReg(pParse, r);
+}
+#else
+# define codeWithoutRowidPreupdate(a,b,c,d)
+#endif
+
+/*
** This routine generates code to finish the INSERT or UPDATE operation
** that was started by a prior call to sqlite3GenerateConstraintChecks.
** A consecutive range of registers starting at regNewData contains the
@@ -117366,39 +134657,33 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion(
Vdbe *v; /* Prepared statements under construction */
Index *pIdx; /* An index being inserted or updated */
u8 pik_flags; /* flag values passed to the btree insert */
- int regData; /* Content registers (after the rowid) */
- int regRec; /* Register holding assembled record for the table */
int i; /* Loop counter */
- u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */
assert( update_flags==0
|| update_flags==OPFLAG_ISUPDATE
|| update_flags==(OPFLAG_ISUPDATE|OPFLAG_SAVEPOSITION)
);
- v = sqlite3GetVdbe(pParse);
+ v = pParse->pVdbe;
assert( v!=0 );
- assert( pTab->pSelect==0 ); /* This table is not a VIEW */
+ assert( !IsView(pTab) ); /* This table is not a VIEW */
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+ /* All REPLACE indexes are at the end of the list */
+ assert( pIdx->onError!=OE_Replace
+ || pIdx->pNext==0
+ || pIdx->pNext->onError==OE_Replace );
if( aRegIdx[i]==0 ) continue;
- bAffinityDone = 1;
if( pIdx->pPartIdxWhere ){
sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
VdbeCoverage(v);
}
pik_flags = (useSeekResult ? OPFLAG_USESEEKRESULT : 0);
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
- assert( pParse->nested==0 );
pik_flags |= OPFLAG_NCHANGE;
pik_flags |= (update_flags & OPFLAG_SAVEPOSITION);
-#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
if( update_flags==0 ){
- sqlite3VdbeAddOp4(v, OP_InsertInt,
- iIdxCur+i, aRegIdx[i], 0, (char*)pTab, P4_TABLE
- );
- sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
+ codeWithoutRowidPreupdate(pParse, pTab, iIdxCur+i, aRegIdx[i]);
}
-#endif
}
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i],
aRegIdx[i]+1,
@@ -117406,13 +134691,6 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion(
sqlite3VdbeChangeP5(v, pik_flags);
}
if( !HasRowid(pTab) ) return;
- regData = regNewData + 1;
- regRec = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
- sqlite3SetMakeRecordP5(v, pTab);
- if( !bAffinityDone ){
- sqlite3TableAffinity(v, pTab, 0);
- }
if( pParse->nested ){
pik_flags = 0;
}else{
@@ -117425,7 +134703,7 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion(
if( useSeekResult ){
pik_flags |= OPFLAG_USESEEKRESULT;
}
- sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regNewData);
+ sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, aRegIdx[i], regNewData);
if( !pParse->nested ){
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
}
@@ -117471,29 +134749,32 @@ SQLITE_PRIVATE int sqlite3OpenTableAndIndices(
assert( op==OP_OpenRead || op==OP_OpenWrite );
assert( op==OP_OpenWrite || p5==0 );
+ assert( piDataCur!=0 );
+ assert( piIdxCur!=0 );
if( IsVirtual(pTab) ){
/* This routine is a no-op for virtual tables. Leave the output
- ** variables *piDataCur and *piIdxCur uninitialized so that valgrind
- ** can detect if they are used by mistake in the caller. */
+ ** variables *piDataCur and *piIdxCur set to illegal cursor numbers
+ ** for improved error detection. */
+ *piDataCur = *piIdxCur = -999;
return 0;
}
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
- v = sqlite3GetVdbe(pParse);
+ v = pParse->pVdbe;
assert( v!=0 );
if( iBase<0 ) iBase = pParse->nTab;
iDataCur = iBase++;
- if( piDataCur ) *piDataCur = iDataCur;
+ *piDataCur = iDataCur;
if( HasRowid(pTab) && (aToOpen==0 || aToOpen[0]) ){
sqlite3OpenTable(pParse, iDataCur, iDb, pTab, op);
- }else{
+ }else if( pParse->db->noSharedCache==0 ){
sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName);
}
- if( piIdxCur ) *piIdxCur = iBase;
+ *piIdxCur = iBase;
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
int iIdxCur = iBase++;
assert( pIdx->pSchema==pTab->pSchema );
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
- if( piDataCur ) *piDataCur = iIdxCur;
+ *piDataCur = iIdxCur;
p5 = 0;
}
if( aToOpen==0 || aToOpen[i+1] ){
@@ -117535,7 +134816,7 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
int i;
assert( pDest && pSrc );
assert( pDest->pTable!=pSrc->pTable );
- if( pDest->nKeyCol!=pSrc->nKeyCol ){
+ if( pDest->nKeyCol!=pSrc->nKeyCol || pDest->nColumn!=pSrc->nColumn ){
return 0; /* Different number of columns */
}
if( pDest->onError!=pSrc->onError ){
@@ -117572,7 +134853,7 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
**
** INSERT INTO tab1 SELECT * FROM tab2;
**
-** The xfer optimization transfers raw records from tab2 over to tab1.
+** The xfer optimization transfers raw records from tab2 over to tab1.
** Columns are not decoded and reassembled, which greatly improves
** performance. Raw index records are transferred in the same way.
**
@@ -117603,7 +134884,7 @@ static int xferOptimization(
ExprList *pEList; /* The result set of the SELECT */
Table *pSrc; /* The table in the FROM clause of SELECT */
Index *pSrcIdx, *pDestIdx; /* Source and destination indices */
- struct SrcList_item *pItem; /* An element of pSelect->pSrc */
+ SrcItem *pItem; /* An element of pSelect->pSrc */
int i; /* Loop counter */
int iDbSrc; /* The database of pSrc */
int iSrc, iDest; /* Cursors from source and destination */
@@ -117615,18 +134896,13 @@ static int xferOptimization(
int destHasUniqueIdx = 0; /* True if pDest has a UNIQUE index */
int regData, regRowid; /* Registers holding data and rowid */
- if( pSelect==0 ){
- return 0; /* Must be of the form INSERT INTO ... SELECT ... */
- }
+ assert( pSelect!=0 );
if( pParse->pWith || pSelect->pWith ){
/* Do not attempt to process this query if there are an WITH clauses
** attached to it. Proceeding may generate a false "no such table: xxx"
** error if pSelect reads from a CTE named "xxx". */
return 0;
}
- if( sqlite3TriggerList(pParse, pDest) ){
- return 0; /* tab1 must not have triggers */
- }
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pDest) ){
return 0; /* tab1 must not be a virtual table */
@@ -117682,19 +134958,15 @@ static int xferOptimization(
if( pSrc==0 ){
return 0; /* FROM clause does not contain a real table */
}
- if( pSrc==pDest ){
+ if( pSrc->tnum==pDest->tnum && pSrc->pSchema==pDest->pSchema ){
+ testcase( pSrc!=pDest ); /* Possible due to bad sqlite_schema.rootpage */
return 0; /* tab1 and tab2 may not be the same table */
}
if( HasRowid(pDest)!=HasRowid(pSrc) ){
return 0; /* source and destination must both be WITHOUT ROWID or not */
}
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( IsVirtual(pSrc) ){
- return 0; /* tab2 must not be a virtual table */
- }
-#endif
- if( pSrc->pSelect ){
- return 0; /* tab2 may not be a view */
+ if( !IsOrdinaryTable(pSrc) ){
+ return 0; /* tab2 may not be a view or virtual table */
}
if( pDest->nCol!=pSrc->nCol ){
return 0; /* Number of columns must be the same in tab1 and tab2 */
@@ -117702,32 +134974,75 @@ static int xferOptimization(
if( pDest->iPKey!=pSrc->iPKey ){
return 0; /* Both tables must have the same INTEGER PRIMARY KEY */
}
+ if( (pDest->tabFlags & TF_Strict)!=0 && (pSrc->tabFlags & TF_Strict)==0 ){
+ return 0; /* Cannot feed from a non-strict into a strict table */
+ }
for(i=0; i<pDest->nCol; i++){
Column *pDestCol = &pDest->aCol[i];
Column *pSrcCol = &pSrc->aCol[i];
#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS
- if( (db->mDbFlags & DBFLAG_Vacuum)==0
- && (pDestCol->colFlags | pSrcCol->colFlags) & COLFLAG_HIDDEN
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0
+ && (pDestCol->colFlags | pSrcCol->colFlags) & COLFLAG_HIDDEN
){
return 0; /* Neither table may have __hidden__ columns */
}
#endif
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ /* Even if tables t1 and t2 have identical schemas, if they contain
+ ** generated columns, then this statement is semantically incorrect:
+ **
+ ** INSERT INTO t2 SELECT * FROM t1;
+ **
+ ** The reason is that generated column values are returned by the
+ ** the SELECT statement on the right but the INSERT statement on the
+ ** left wants them to be omitted.
+ **
+ ** Nevertheless, this is a useful notational shorthand to tell SQLite
+ ** to do a bulk transfer all of the content from t1 over to t2.
+ **
+ ** We could, in theory, disable this (except for internal use by the
+ ** VACUUM command where it is actually needed). But why do that? It
+ ** seems harmless enough, and provides a useful service.
+ */
+ if( (pDestCol->colFlags & COLFLAG_GENERATED) !=
+ (pSrcCol->colFlags & COLFLAG_GENERATED) ){
+ return 0; /* Both columns have the same generated-column type */
+ }
+ /* But the transfer is only allowed if both the source and destination
+ ** tables have the exact same expressions for generated columns.
+ ** This requirement could be relaxed for VIRTUAL columns, I suppose.
+ */
+ if( (pDestCol->colFlags & COLFLAG_GENERATED)!=0 ){
+ if( sqlite3ExprCompare(0,
+ sqlite3ColumnExpr(pSrc, pSrcCol),
+ sqlite3ColumnExpr(pDest, pDestCol), -1)!=0 ){
+ testcase( pDestCol->colFlags & COLFLAG_VIRTUAL );
+ testcase( pDestCol->colFlags & COLFLAG_STORED );
+ return 0; /* Different generator expressions */
+ }
+ }
+#endif
if( pDestCol->affinity!=pSrcCol->affinity ){
return 0; /* Affinity must be the same on all columns */
}
- if( sqlite3_stricmp(pDestCol->zColl, pSrcCol->zColl)!=0 ){
+ if( sqlite3_stricmp(sqlite3ColumnColl(pDestCol),
+ sqlite3ColumnColl(pSrcCol))!=0 ){
return 0; /* Collating sequence must be the same on all columns */
}
if( pDestCol->notNull && !pSrcCol->notNull ){
return 0; /* tab2 must be NOT NULL if tab1 is */
}
/* Default values for second and subsequent columns need to match. */
- if( i>0 ){
- assert( pDestCol->pDflt==0 || pDestCol->pDflt->op==TK_SPAN );
- assert( pSrcCol->pDflt==0 || pSrcCol->pDflt->op==TK_SPAN );
- if( (pDestCol->pDflt==0)!=(pSrcCol->pDflt==0)
- || (pDestCol->pDflt && strcmp(pDestCol->pDflt->u.zToken,
- pSrcCol->pDflt->u.zToken)!=0)
+ if( (pDestCol->colFlags & COLFLAG_GENERATED)==0 && i>0 ){
+ Expr *pDestExpr = sqlite3ColumnExpr(pDest, pDestCol);
+ Expr *pSrcExpr = sqlite3ColumnExpr(pSrc, pSrcCol);
+ assert( pDestExpr==0 || pDestExpr->op==TK_SPAN );
+ assert( pDestExpr==0 || !ExprHasProperty(pDestExpr, EP_IntValue) );
+ assert( pSrcExpr==0 || pSrcExpr->op==TK_SPAN );
+ assert( pSrcExpr==0 || !ExprHasProperty(pSrcExpr, EP_IntValue) );
+ if( (pDestExpr==0)!=(pSrcExpr==0)
+ || (pDestExpr!=0 && strcmp(pDestExpr->u.zToken,
+ pSrcExpr->u.zToken)!=0)
){
return 0; /* Default values must be the same for all columns */
}
@@ -117743,6 +135058,13 @@ static int xferOptimization(
if( pSrcIdx==0 ){
return 0; /* pDestIdx has no corresponding index in pSrc */
}
+ if( pSrcIdx->tnum==pDestIdx->tnum && pSrc->pSchema==pDest->pSchema
+ && sqlite3FaultSim(411)==SQLITE_OK ){
+ /* The sqlite3FaultSim() call allows this corruption test to be
+ ** bypassed during testing, in order to exercise other corruption tests
+ ** further downstream. */
+ return 0; /* Corrupt schema - two indexes on the same btree */
+ }
}
#ifndef SQLITE_OMIT_CHECK
if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){
@@ -117750,14 +135072,15 @@ static int xferOptimization(
}
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
- /* Disallow the transfer optimization if the destination table constains
+ /* Disallow the transfer optimization if the destination table contains
** any foreign key constraints. This is more restrictive than necessary.
- ** But the main beneficiary of the transfer optimization is the VACUUM
+ ** But the main beneficiary of the transfer optimization is the VACUUM
** command, and the VACUUM command disables foreign key constraints. So
** the extra complication to make this rule less restrictive is probably
** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e]
*/
- if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){
+ assert( IsOrdinaryTable(pDest) );
+ if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->u.tab.pFKey!=0 ){
return 0;
}
#endif
@@ -117779,6 +135102,7 @@ static int xferOptimization(
iDest = pParse->nTab++;
regAutoinc = autoIncBegin(pParse, iDbDest, pDest);
regData = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regData);
regRowid = sqlite3GetTempReg(pParse);
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
assert( HasRowid(pDest) || destHasUniqueIdx );
@@ -117799,7 +135123,7 @@ static int xferOptimization(
** (If the destination is not initially empty, the rowid fields
** of index entries might need to change.)
**
- ** (2) The destination has a unique index. (The xfer optimization
+ ** (2) The destination has a unique index. (The xfer optimization
** is unable to test uniqueness.)
**
** (3) onError is something other than OE_Abort and OE_Rollback.
@@ -117814,29 +135138,42 @@ static int xferOptimization(
emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
if( pDest->iPKey>=0 ){
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
- sqlite3VdbeVerifyAbortable(v, onError);
- addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
- VdbeCoverage(v);
- sqlite3RowidConstraint(pParse, onError, pDest);
- sqlite3VdbeJumpHere(v, addr2);
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
+ sqlite3VdbeVerifyAbortable(v, onError);
+ addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
+ VdbeCoverage(v);
+ sqlite3RowidConstraint(pParse, onError, pDest);
+ sqlite3VdbeJumpHere(v, addr2);
+ }
autoIncStep(pParse, regAutoinc, regRowid);
- }else if( pDest->pIndex==0 ){
+ }else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){
addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
}else{
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
assert( (pDest->tabFlags & TF_Autoincrement)==0 );
}
- sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
+
if( db->mDbFlags & DBFLAG_Vacuum ){
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
- insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|
- OPFLAG_APPEND|OPFLAG_USESEEKRESULT;
+ insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT;
}else{
- insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND;
+ insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND|OPFLAG_PREFORMAT;
+ }
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
+ sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
+ insFlags &= ~OPFLAG_PREFORMAT;
+ }else
+#endif
+ {
+ sqlite3VdbeAddOp3(v, OP_RowCell, iDest, iSrc, regRowid);
+ }
+ sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid);
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
+ sqlite3VdbeChangeP4(v, -1, (char*)pDest, P4_TABLE);
}
- sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
- (char*)pDest, P4_TABLE);
sqlite3VdbeChangeP5(v, insFlags);
+
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
@@ -117858,19 +135195,18 @@ static int xferOptimization(
sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR);
VdbeComment((v, "%s", pDestIdx->zName));
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
if( db->mDbFlags & DBFLAG_Vacuum ){
/* This INSERT command is part of a VACUUM operation, which guarantees
** that the destination table is empty. If all indexed columns use
** collation sequence BINARY, then it can also be assumed that the
- ** index will be populated by inserting keys in strictly sorted
+ ** index will be populated by inserting keys in strictly sorted
** order. In this case, instead of seeking within the b-tree as part
** of every OP_IdxInsert opcode, an OP_SeekEnd is added before the
- ** OP_IdxInsert to seek to the point within the b-tree where each key
+ ** OP_IdxInsert to seek to the point within the b-tree where each key
** should be inserted. This is faster.
**
** If any of the indexed columns use a collation sequence other than
- ** BINARY, this optimization is disabled. This is because the user
+ ** BINARY, this optimization is disabled. This is because the user
** might change the definition of a collation sequence and then run
** a VACUUM command. In that case keys may not be written in strictly
** sorted order. */
@@ -117879,13 +135215,22 @@ static int xferOptimization(
if( sqlite3_stricmp(sqlite3StrBINARY, zColl) ) break;
}
if( i==pSrcIdx->nColumn ){
- idxInsFlags = OPFLAG_USESEEKRESULT;
+ idxInsFlags = OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT;
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
+ sqlite3VdbeAddOp2(v, OP_RowCell, iDest, iSrc);
}
- }
- if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){
+ }else if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){
idxInsFlags |= OPFLAG_NCHANGE;
}
+ if( idxInsFlags!=(OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT) ){
+ sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
+ if( (db->mDbFlags & DBFLAG_Vacuum)==0
+ && !HasRowid(pDest)
+ && IsPrimaryKeyIndex(pDestIdx)
+ ){
+ codeWithoutRowidPreupdate(pParse, pDest, iDest, regData);
+ }
+ }
sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND);
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
@@ -117958,7 +135303,7 @@ SQLITE_API int sqlite3_exec(
sqlite3_mutex_enter(db->mutex);
sqlite3Error(db, SQLITE_OK);
while( rc==SQLITE_OK && zSql[0] ){
- int nCol;
+ int nCol = 0;
char **azVals = 0;
pStmt = 0;
@@ -117972,19 +135317,18 @@ SQLITE_API int sqlite3_exec(
zSql = zLeftover;
continue;
}
-
callbackIsInit = 0;
- nCol = sqlite3_column_count(pStmt);
while( 1 ){
int i;
rc = sqlite3_step(pStmt);
/* Invoke the callback function if required */
- if( xCallback && (SQLITE_ROW==rc ||
+ if( xCallback && (SQLITE_ROW==rc ||
(SQLITE_DONE==rc && !callbackIsInit
&& db->flags&SQLITE_NullCallback)) ){
if( !callbackIsInit ){
+ nCol = sqlite3_column_count(pStmt);
azCols = sqlite3DbMallocRaw(db, (2*nCol+1)*sizeof(const char*));
if( azCols==0 ){
goto exec_out;
@@ -118089,7 +135433,7 @@ exec_out:
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
-** as extensions by SQLite should #include this file instead of
+** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef SQLITE3EXT_H
@@ -118396,6 +135740,53 @@ struct sqlite3_api_routines {
void(*xDestroy)(void*));
/* Version 3.26.0 and later */
const char *(*normalized_sql)(sqlite3_stmt*);
+ /* Version 3.28.0 and later */
+ int (*stmt_isexplain)(sqlite3_stmt*);
+ int (*value_frombind)(sqlite3_value*);
+ /* Version 3.30.0 and later */
+ int (*drop_modules)(sqlite3*,const char**);
+ /* Version 3.31.0 and later */
+ sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
+ const char *(*uri_key)(const char*,int);
+ const char *(*filename_database)(const char*);
+ const char *(*filename_journal)(const char*);
+ const char *(*filename_wal)(const char*);
+ /* Version 3.32.0 and later */
+ const char *(*create_filename)(const char*,const char*,const char*,
+ int,const char**);
+ void (*free_filename)(const char*);
+ sqlite3_file *(*database_file_object)(const char*);
+ /* Version 3.34.0 and later */
+ int (*txn_state)(sqlite3*,const char*);
+ /* Version 3.36.1 and later */
+ sqlite3_int64 (*changes64)(sqlite3*);
+ sqlite3_int64 (*total_changes64)(sqlite3*);
+ /* Version 3.37.0 and later */
+ int (*autovacuum_pages)(sqlite3*,
+ unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
+ void*, void(*)(void*));
+ /* Version 3.38.0 and later */
+ int (*error_offset)(sqlite3*);
+ int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
+ int (*vtab_distinct)(sqlite3_index_info*);
+ int (*vtab_in)(sqlite3_index_info*,int,int);
+ int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
+ int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
+ /* Version 3.39.0 and later */
+ int (*deserialize)(sqlite3*,const char*,unsigned char*,
+ sqlite3_int64,sqlite3_int64,unsigned);
+ unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
+ unsigned int);
+ const char *(*db_name)(sqlite3*,int);
+ /* Version 3.40.0 and later */
+ int (*value_encoding)(sqlite3_value*);
+ /* Version 3.41.0 and later */
+ int (*is_interrupted)(sqlite3*);
+ /* Version 3.43.0 and later */
+ int (*stmt_explain)(sqlite3_stmt*,int);
+ /* Version 3.44.0 and later */
+ void *(*get_clientdata)(sqlite3*,const char*);
+ int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
};
/*
@@ -118685,17 +136076,61 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_create_window_function sqlite3_api->create_window_function
/* Version 3.26.0 and later */
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
+/* Version 3.28.0 and later */
+#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
+#define sqlite3_value_frombind sqlite3_api->value_frombind
+/* Version 3.30.0 and later */
+#define sqlite3_drop_modules sqlite3_api->drop_modules
+/* Version 3.31.0 and later */
+#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
+#define sqlite3_uri_key sqlite3_api->uri_key
+#define sqlite3_filename_database sqlite3_api->filename_database
+#define sqlite3_filename_journal sqlite3_api->filename_journal
+#define sqlite3_filename_wal sqlite3_api->filename_wal
+/* Version 3.32.0 and later */
+#define sqlite3_create_filename sqlite3_api->create_filename
+#define sqlite3_free_filename sqlite3_api->free_filename
+#define sqlite3_database_file_object sqlite3_api->database_file_object
+/* Version 3.34.0 and later */
+#define sqlite3_txn_state sqlite3_api->txn_state
+/* Version 3.36.1 and later */
+#define sqlite3_changes64 sqlite3_api->changes64
+#define sqlite3_total_changes64 sqlite3_api->total_changes64
+/* Version 3.37.0 and later */
+#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
+/* Version 3.38.0 and later */
+#define sqlite3_error_offset sqlite3_api->error_offset
+#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
+#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
+#define sqlite3_vtab_in sqlite3_api->vtab_in
+#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
+#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
+/* Version 3.39.0 and later */
+#ifndef SQLITE_OMIT_DESERIALIZE
+#define sqlite3_deserialize sqlite3_api->deserialize
+#define sqlite3_serialize sqlite3_api->serialize
+#endif
+#define sqlite3_db_name sqlite3_api->db_name
+/* Version 3.40.0 and later */
+#define sqlite3_value_encoding sqlite3_api->value_encoding
+/* Version 3.41.0 and later */
+#define sqlite3_is_interrupted sqlite3_api->is_interrupted
+/* Version 3.43.0 and later */
+#define sqlite3_stmt_explain sqlite3_api->stmt_explain
+/* Version 3.44.0 and later */
+#define sqlite3_get_clientdata sqlite3_api->get_clientdata
+#define sqlite3_set_clientdata sqlite3_api->set_clientdata
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
- /* This case when the file really is being compiled as a loadable
+ /* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
# define SQLITE_EXTENSION_INIT3 \
extern const sqlite3_api_routines *sqlite3_api;
#else
- /* This case when the file is being statically linked into the
+ /* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
@@ -118987,8 +136422,8 @@ static const sqlite3_api_routines sqlite3Apis = {
sqlite3_memory_highwater,
sqlite3_memory_used,
#ifdef SQLITE_MUTEX_OMIT
- 0,
- 0,
+ 0,
+ 0,
0,
0,
0,
@@ -119144,12 +136579,79 @@ static const sqlite3_api_routines sqlite3Apis = {
sqlite3_create_window_function,
/* Version 3.26.0 and later */
#ifdef SQLITE_ENABLE_NORMALIZE
- sqlite3_normalized_sql
+ sqlite3_normalized_sql,
#else
- 0
+ 0,
#endif
+ /* Version 3.28.0 and later */
+ sqlite3_stmt_isexplain,
+ sqlite3_value_frombind,
+ /* Version 3.30.0 and later */
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3_drop_modules,
+#else
+ 0,
+#endif
+ /* Version 3.31.0 and later */
+ sqlite3_hard_heap_limit64,
+ sqlite3_uri_key,
+ sqlite3_filename_database,
+ sqlite3_filename_journal,
+ sqlite3_filename_wal,
+ /* Version 3.32.0 and later */
+ sqlite3_create_filename,
+ sqlite3_free_filename,
+ sqlite3_database_file_object,
+ /* Version 3.34.0 and later */
+ sqlite3_txn_state,
+ /* Version 3.36.1 and later */
+ sqlite3_changes64,
+ sqlite3_total_changes64,
+ /* Version 3.37.0 and later */
+ sqlite3_autovacuum_pages,
+ /* Version 3.38.0 and later */
+ sqlite3_error_offset,
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3_vtab_rhs_value,
+ sqlite3_vtab_distinct,
+ sqlite3_vtab_in,
+ sqlite3_vtab_in_first,
+ sqlite3_vtab_in_next,
+#else
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+#endif
+ /* Version 3.39.0 and later */
+#ifndef SQLITE_OMIT_DESERIALIZE
+ sqlite3_deserialize,
+ sqlite3_serialize,
+#else
+ 0,
+ 0,
+#endif
+ sqlite3_db_name,
+ /* Version 3.40.0 and later */
+ sqlite3_value_encoding,
+ /* Version 3.41.0 and later */
+ sqlite3_is_interrupted,
+ /* Version 3.43.0 and later */
+ sqlite3_stmt_explain,
+ /* Version 3.44.0 and later */
+ sqlite3_get_clientdata,
+ sqlite3_set_clientdata
};
+/* True if x is the directory separator character
+*/
+#if SQLITE_OS_WIN
+# define DirSep(X) ((X)=='/'||(X)=='\\')
+#else
+# define DirSep(X) ((X)=='/')
+#endif
+
/*
** Attempt to load an SQLite extension library contained in the file
** zFile. The entry point is zProc. zProc may be 0 in which case a
@@ -119158,7 +136660,7 @@ static const sqlite3_api_routines sqlite3Apis = {
**
** Return SQLITE_OK on success and SQLITE_ERROR if something goes wrong.
**
-** If an error occurs and pzErrMsg is not 0, then fill *pzErrMsg with
+** If an error occurs and pzErrMsg is not 0, then fill *pzErrMsg with
** error message text. The calling function should free this memory
** by calling sqlite3DbFree(db, ).
*/
@@ -119175,14 +136677,14 @@ static int sqlite3LoadExtension(
const char *zEntry;
char *zAltEntry = 0;
void **aHandle;
- u64 nMsg = 300 + sqlite3Strlen30(zFile);
+ u64 nMsg = strlen(zFile);
int ii;
int rc;
/* Shared library endings to try if zFile cannot be loaded as written */
static const char *azEndings[] = {
#if SQLITE_OS_WIN
- "dll"
+ "dll"
#elif defined(__APPLE__)
"dylib"
#else
@@ -119209,34 +136711,40 @@ static int sqlite3LoadExtension(
zEntry = zProc ? zProc : "sqlite3_extension_init";
+ /* tag-20210611-1. Some dlopen() implementations will segfault if given
+ ** an oversize filename. Most filesystems have a pathname limit of 4K,
+ ** so limit the extension filename length to about twice that.
+ ** https://sqlite.org/forum/forumpost/08a0d6d9bf
+ **
+ ** Later (2023-03-25): Save an extra 6 bytes for the filename suffix.
+ ** See https://sqlite.org/forum/forumpost/24083b579d.
+ */
+ if( nMsg>SQLITE_MAX_PATHLEN ) goto extension_not_found;
+
+ /* Do not allow sqlite3_load_extension() to link to a copy of the
+ ** running application, by passing in an empty filename. */
+ if( nMsg==0 ) goto extension_not_found;
+
handle = sqlite3OsDlOpen(pVfs, zFile);
#if SQLITE_OS_UNIX || SQLITE_OS_WIN
for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){
char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]);
if( zAltFile==0 ) return SQLITE_NOMEM_BKPT;
- handle = sqlite3OsDlOpen(pVfs, zAltFile);
+ if( nMsg+strlen(azEndings[ii])+1<=SQLITE_MAX_PATHLEN ){
+ handle = sqlite3OsDlOpen(pVfs, zAltFile);
+ }
sqlite3_free(zAltFile);
}
#endif
- if( handle==0 ){
- if( pzErrMsg ){
- *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg);
- if( zErrmsg ){
- sqlite3_snprintf(nMsg, zErrmsg,
- "unable to open shared library [%s]", zFile);
- sqlite3OsDlError(pVfs, nMsg-1, zErrmsg);
- }
- }
- return SQLITE_ERROR;
- }
+ if( handle==0 ) goto extension_not_found;
xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);
/* If no entry point was specified and the default legacy
** entry point name "sqlite3_extension_init" was not found, then
** construct an entry point name "sqlite3_X_init" where the X is
- ** replaced by the lowercase value of every ASCII alphabetic
+ ** replaced by the lowercase value of every ASCII alphabetic
** character in the filename after the last "/" upto the first ".",
- ** and eliding the first three characters if they are "lib".
+ ** and eliding the first three characters if they are "lib".
** Examples:
**
** /usr/local/lib/libExample5.4.3.so ==> sqlite3_example_init
@@ -119251,7 +136759,7 @@ static int sqlite3LoadExtension(
return SQLITE_NOMEM_BKPT;
}
memcpy(zAltEntry, "sqlite3_", 8);
- for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){}
+ for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){}
iFile++;
if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3;
for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){
@@ -119265,10 +136773,11 @@ static int sqlite3LoadExtension(
}
if( xInit==0 ){
if( pzErrMsg ){
- nMsg += sqlite3Strlen30(zEntry);
+ nMsg += strlen(zEntry) + 300;
*pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg);
if( zErrmsg ){
- sqlite3_snprintf(nMsg, zErrmsg,
+ assert( nMsg<0x7fffffff ); /* zErrmsg would be NULL if not so */
+ sqlite3_snprintf((int)nMsg, zErrmsg,
"no entry point [%s] in shared library [%s]", zEntry, zFile);
sqlite3OsDlError(pVfs, nMsg-1, zErrmsg);
}
@@ -119302,6 +136811,19 @@ static int sqlite3LoadExtension(
db->aExtension[db->nExtension++] = handle;
return SQLITE_OK;
+
+extension_not_found:
+ if( pzErrMsg ){
+ nMsg += 300;
+ *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg);
+ if( zErrmsg ){
+ assert( nMsg<0x7fffffff ); /* zErrmsg would be NULL if not so */
+ sqlite3_snprintf((int)nMsg, zErrmsg,
+ "unable to open shared library [%.*s]", SQLITE_MAX_PATHLEN, zFile);
+ sqlite3OsDlError(pVfs, nMsg-1, zErrmsg);
+ }
+ }
+ return SQLITE_ERROR;
}
SQLITE_API int sqlite3_load_extension(
sqlite3 *db, /* Load the extension into this database connection */
@@ -119335,11 +136857,14 @@ SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3 *db){
** default so as not to open security holes in older applications.
*/
SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
if( onoff ){
db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc;
}else{
- db->flags &= ~(SQLITE_LoadExtension|SQLITE_LoadExtFunc);
+ db->flags &= ~(u64)(SQLITE_LoadExtension|SQLITE_LoadExtFunc);
}
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
@@ -119351,12 +136876,12 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
** The following object holds the list of automatically loaded
** extensions.
**
-** This list is shared across threads. The SQLITE_MUTEX_STATIC_MASTER
+** This list is shared across threads. The SQLITE_MUTEX_STATIC_MAIN
** mutex must be held while accessing this list.
*/
typedef struct sqlite3AutoExtList sqlite3AutoExtList;
static SQLITE_WSD struct sqlite3AutoExtList {
- u32 nExt; /* Number of entries in aExt[] */
+ u32 nExt; /* Number of entries in aExt[] */
void (**aExt)(void); /* Pointers to the extension init functions */
} sqlite3Autoext = { 0, 0 };
@@ -119384,6 +136909,9 @@ SQLITE_API int sqlite3_auto_extension(
void (*xInit)(void)
){
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( xInit==0 ) return SQLITE_MISUSE_BKPT;
+#endif
#ifndef SQLITE_OMIT_AUTOINIT
rc = sqlite3_initialize();
if( rc ){
@@ -119393,7 +136921,7 @@ SQLITE_API int sqlite3_auto_extension(
{
u32 i;
#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
#endif
wsdAutoextInit;
sqlite3_mutex_enter(mutex);
@@ -119431,11 +136959,14 @@ SQLITE_API int sqlite3_cancel_auto_extension(
void (*xInit)(void)
){
#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
#endif
int i;
int n = 0;
wsdAutoextInit;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( xInit==0 ) return 0;
+#endif
sqlite3_mutex_enter(mutex);
for(i=(int)wsdAutoext.nExt-1; i>=0; i--){
if( wsdAutoext.aExt[i]==xInit ){
@@ -119458,7 +136989,7 @@ SQLITE_API void sqlite3_reset_auto_extension(void){
#endif
{
#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
#endif
wsdAutoextInit;
sqlite3_mutex_enter(mutex);
@@ -119488,7 +137019,7 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
for(i=0; go; i++){
char *zErrmsg;
#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN);
#endif
#ifdef SQLITE_OMIT_LOAD_EXTENSION
const sqlite3_api_routines *pThunk = 0;
@@ -119543,7 +137074,7 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
** that includes the PragType_XXXX macro definitions and the aPragmaName[]
** object. This ensures that the aPragmaName[] table is arranged in
** lexicographical order to facility a binary search of the pragma name.
-** Do not edit pragma.h directly. Edit and rerun the script in at
+** Do not edit pragma.h directly. Edit and rerun the script in at
** ../tool/mkpragmatab.tcl. */
/************** Include pragma.h in the middle of pragma.c *******************/
/************** Begin file pragma.h ******************************************/
@@ -119554,51 +137085,51 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
*/
/* The various pragma types */
-#define PragTyp_HEADER_VALUE 0
-#define PragTyp_AUTO_VACUUM 1
-#define PragTyp_FLAG 2
-#define PragTyp_BUSY_TIMEOUT 3
-#define PragTyp_CACHE_SIZE 4
-#define PragTyp_CACHE_SPILL 5
-#define PragTyp_CASE_SENSITIVE_LIKE 6
-#define PragTyp_COLLATION_LIST 7
-#define PragTyp_COMPILE_OPTIONS 8
-#define PragTyp_DATA_STORE_DIRECTORY 9
-#define PragTyp_DATABASE_LIST 10
-#define PragTyp_DEFAULT_CACHE_SIZE 11
-#define PragTyp_ENCODING 12
-#define PragTyp_FOREIGN_KEY_CHECK 13
-#define PragTyp_FOREIGN_KEY_LIST 14
-#define PragTyp_FUNCTION_LIST 15
-#define PragTyp_INCREMENTAL_VACUUM 16
-#define PragTyp_INDEX_INFO 17
-#define PragTyp_INDEX_LIST 18
-#define PragTyp_INTEGRITY_CHECK 19
-#define PragTyp_JOURNAL_MODE 20
-#define PragTyp_JOURNAL_SIZE_LIMIT 21
-#define PragTyp_LOCK_PROXY_FILE 22
-#define PragTyp_LOCKING_MODE 23
-#define PragTyp_PAGE_COUNT 24
-#define PragTyp_MMAP_SIZE 25
-#define PragTyp_MODULE_LIST 26
-#define PragTyp_OPTIMIZE 27
-#define PragTyp_PAGE_SIZE 28
-#define PragTyp_PRAGMA_LIST 29
-#define PragTyp_SECURE_DELETE 30
-#define PragTyp_SHRINK_MEMORY 31
-#define PragTyp_SOFT_HEAP_LIMIT 32
-#define PragTyp_SYNCHRONOUS 33
-#define PragTyp_TABLE_INFO 34
-#define PragTyp_TEMP_STORE 35
-#define PragTyp_TEMP_STORE_DIRECTORY 36
-#define PragTyp_THREADS 37
-#define PragTyp_WAL_AUTOCHECKPOINT 38
-#define PragTyp_WAL_CHECKPOINT 39
-#define PragTyp_ACTIVATE_EXTENSIONS 40
-#define PragTyp_HEXKEY 41
-#define PragTyp_KEY 42
-#define PragTyp_LOCK_STATUS 43
-#define PragTyp_PARSER_TRACE 44
+#define PragTyp_ACTIVATE_EXTENSIONS 0
+#define PragTyp_ANALYSIS_LIMIT 1
+#define PragTyp_HEADER_VALUE 2
+#define PragTyp_AUTO_VACUUM 3
+#define PragTyp_FLAG 4
+#define PragTyp_BUSY_TIMEOUT 5
+#define PragTyp_CACHE_SIZE 6
+#define PragTyp_CACHE_SPILL 7
+#define PragTyp_CASE_SENSITIVE_LIKE 8
+#define PragTyp_COLLATION_LIST 9
+#define PragTyp_COMPILE_OPTIONS 10
+#define PragTyp_DATA_STORE_DIRECTORY 11
+#define PragTyp_DATABASE_LIST 12
+#define PragTyp_DEFAULT_CACHE_SIZE 13
+#define PragTyp_ENCODING 14
+#define PragTyp_FOREIGN_KEY_CHECK 15
+#define PragTyp_FOREIGN_KEY_LIST 16
+#define PragTyp_FUNCTION_LIST 17
+#define PragTyp_HARD_HEAP_LIMIT 18
+#define PragTyp_INCREMENTAL_VACUUM 19
+#define PragTyp_INDEX_INFO 20
+#define PragTyp_INDEX_LIST 21
+#define PragTyp_INTEGRITY_CHECK 22
+#define PragTyp_JOURNAL_MODE 23
+#define PragTyp_JOURNAL_SIZE_LIMIT 24
+#define PragTyp_LOCK_PROXY_FILE 25
+#define PragTyp_LOCKING_MODE 26
+#define PragTyp_PAGE_COUNT 27
+#define PragTyp_MMAP_SIZE 28
+#define PragTyp_MODULE_LIST 29
+#define PragTyp_OPTIMIZE 30
+#define PragTyp_PAGE_SIZE 31
+#define PragTyp_PRAGMA_LIST 32
+#define PragTyp_SECURE_DELETE 33
+#define PragTyp_SHRINK_MEMORY 34
+#define PragTyp_SOFT_HEAP_LIMIT 35
+#define PragTyp_SYNCHRONOUS 36
+#define PragTyp_TABLE_INFO 37
+#define PragTyp_TABLE_LIST 38
+#define PragTyp_TEMP_STORE 39
+#define PragTyp_TEMP_STORE_DIRECTORY 40
+#define PragTyp_THREADS 41
+#define PragTyp_WAL_AUTOCHECKPOINT 42
+#define PragTyp_WAL_CHECKPOINT 43
+#define PragTyp_LOCK_STATUS 44
#define PragTyp_STATS 45
/* Property flags associated with various pragma. */
@@ -119617,56 +137148,66 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
*/
static const char *const pragCName[] = {
/* 0 */ "id", /* Used by: foreign_key_list */
- /* 1 */ "seq",
- /* 2 */ "table",
- /* 3 */ "from",
- /* 4 */ "to",
- /* 5 */ "on_update",
- /* 6 */ "on_delete",
- /* 7 */ "match",
+ /* 1 */ "seq",
+ /* 2 */ "table",
+ /* 3 */ "from",
+ /* 4 */ "to",
+ /* 5 */ "on_update",
+ /* 6 */ "on_delete",
+ /* 7 */ "match",
/* 8 */ "cid", /* Used by: table_xinfo */
- /* 9 */ "name",
- /* 10 */ "type",
- /* 11 */ "notnull",
- /* 12 */ "dflt_value",
- /* 13 */ "pk",
- /* 14 */ "hidden",
+ /* 9 */ "name",
+ /* 10 */ "type",
+ /* 11 */ "notnull",
+ /* 12 */ "dflt_value",
+ /* 13 */ "pk",
+ /* 14 */ "hidden",
/* table_info reuses 8 */
- /* 15 */ "seqno", /* Used by: index_xinfo */
- /* 16 */ "cid",
- /* 17 */ "name",
- /* 18 */ "desc",
- /* 19 */ "coll",
- /* 20 */ "key",
- /* 21 */ "tbl", /* Used by: stats */
- /* 22 */ "idx",
- /* 23 */ "wdth",
- /* 24 */ "hght",
- /* 25 */ "flgs",
- /* 26 */ "seq", /* Used by: index_list */
- /* 27 */ "name",
- /* 28 */ "unique",
- /* 29 */ "origin",
- /* 30 */ "partial",
- /* 31 */ "table", /* Used by: foreign_key_check */
- /* 32 */ "rowid",
- /* 33 */ "parent",
- /* 34 */ "fkid",
- /* index_info reuses 15 */
- /* 35 */ "seq", /* Used by: database_list */
- /* 36 */ "name",
- /* 37 */ "file",
- /* 38 */ "busy", /* Used by: wal_checkpoint */
- /* 39 */ "log",
- /* 40 */ "checkpointed",
- /* 41 */ "name", /* Used by: function_list */
- /* 42 */ "builtin",
- /* collation_list reuses 26 */
- /* 43 */ "database", /* Used by: lock_status */
- /* 44 */ "status",
- /* 45 */ "cache_size", /* Used by: default_cache_size */
+ /* 15 */ "schema", /* Used by: table_list */
+ /* 16 */ "name",
+ /* 17 */ "type",
+ /* 18 */ "ncol",
+ /* 19 */ "wr",
+ /* 20 */ "strict",
+ /* 21 */ "seqno", /* Used by: index_xinfo */
+ /* 22 */ "cid",
+ /* 23 */ "name",
+ /* 24 */ "desc",
+ /* 25 */ "coll",
+ /* 26 */ "key",
+ /* 27 */ "name", /* Used by: function_list */
+ /* 28 */ "builtin",
+ /* 29 */ "type",
+ /* 30 */ "enc",
+ /* 31 */ "narg",
+ /* 32 */ "flags",
+ /* 33 */ "tbl", /* Used by: stats */
+ /* 34 */ "idx",
+ /* 35 */ "wdth",
+ /* 36 */ "hght",
+ /* 37 */ "flgs",
+ /* 38 */ "seq", /* Used by: index_list */
+ /* 39 */ "name",
+ /* 40 */ "unique",
+ /* 41 */ "origin",
+ /* 42 */ "partial",
+ /* 43 */ "table", /* Used by: foreign_key_check */
+ /* 44 */ "rowid",
+ /* 45 */ "parent",
+ /* 46 */ "fkid",
+ /* index_info reuses 21 */
+ /* 47 */ "seq", /* Used by: database_list */
+ /* 48 */ "name",
+ /* 49 */ "file",
+ /* 50 */ "busy", /* Used by: wal_checkpoint */
+ /* 51 */ "log",
+ /* 52 */ "checkpointed",
+ /* collation_list reuses 38 */
+ /* 53 */ "database", /* Used by: lock_status */
+ /* 54 */ "status",
+ /* 55 */ "cache_size", /* Used by: default_cache_size */
/* module_list pragma_list reuses 9 */
- /* 46 */ "timeout", /* Used by: busy_timeout */
+ /* 56 */ "timeout", /* Used by: busy_timeout */
};
/* Definitions of all built-in pragmas */
@@ -119679,13 +137220,18 @@ typedef struct PragmaName {
u64 iArg; /* Extra argument */
} PragmaName;
static const PragmaName aPragmaName[] = {
-#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
+#if defined(SQLITE_ENABLE_CEROD)
{/* zName: */ "activate_extensions",
/* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS,
/* ePragFlg: */ 0,
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
+ {/* zName: */ "analysis_limit",
+ /* ePragTyp: */ PragTyp_ANALYSIS_LIMIT,
+ /* ePragFlg: */ PragFlg_Result0,
+ /* ColNames: */ 0, 0,
+ /* iArg: */ 0 },
#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
{/* zName: */ "application_id",
/* ePragTyp: */ PragTyp_HEADER_VALUE,
@@ -119712,7 +137258,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "busy_timeout",
/* ePragTyp: */ PragTyp_BUSY_TIMEOUT,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 46, 1,
+ /* ColNames: */ 56, 1,
/* iArg: */ 0 },
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
{/* zName: */ "cache_size",
@@ -119728,11 +137274,13 @@ static const PragmaName aPragmaName[] = {
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
+#if !defined(SQLITE_OMIT_CASE_SENSITIVE_LIKE_PRAGMA)
{/* zName: */ "case_sensitive_like",
/* ePragTyp: */ PragTyp_CASE_SENSITIVE_LIKE,
/* ePragFlg: */ PragFlg_NoColumns,
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
+#endif
{/* zName: */ "cell_size_check",
/* ePragTyp: */ PragTyp_FLAG,
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
@@ -119749,7 +137297,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "collation_list",
/* ePragTyp: */ PragTyp_COLLATION_LIST,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 26, 2,
+ /* ColNames: */ 38, 2,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
@@ -119783,15 +137331,15 @@ static const PragmaName aPragmaName[] = {
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
{/* zName: */ "database_list",
/* ePragTyp: */ PragTyp_DATABASE_LIST,
- /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
- /* ColNames: */ 35, 3,
+ /* ePragFlg: */ PragFlg_Result0,
+ /* ColNames: */ 47, 3,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
{/* zName: */ "default_cache_size",
/* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq|PragFlg_NoColumns1,
- /* ColNames: */ 45, 1,
+ /* ColNames: */ 55, 1,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
@@ -119820,8 +137368,8 @@ static const PragmaName aPragmaName[] = {
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
{/* zName: */ "foreign_key_check",
/* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK,
- /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0,
- /* ColNames: */ 31, 4,
+ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt,
+ /* ColNames: */ 43, 4,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FOREIGN_KEY)
@@ -119860,26 +137408,19 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_FullFSync },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
-#if defined(SQLITE_INTROSPECTION_PRAGMAS)
+#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
{/* zName: */ "function_list",
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 41, 2,
+ /* ColNames: */ 27, 6,
/* iArg: */ 0 },
#endif
#endif
-#if defined(SQLITE_HAS_CODEC)
- {/* zName: */ "hexkey",
- /* ePragTyp: */ PragTyp_HEXKEY,
- /* ePragFlg: */ 0,
- /* ColNames: */ 0, 0,
- /* iArg: */ 2 },
- {/* zName: */ "hexrekey",
- /* ePragTyp: */ PragTyp_HEXKEY,
- /* ePragFlg: */ 0,
+ {/* zName: */ "hard_heap_limit",
+ /* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT,
+ /* ePragFlg: */ PragFlg_Result0,
/* ColNames: */ 0, 0,
- /* iArg: */ 3 },
-#endif
+ /* iArg: */ 0 },
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
#if !defined(SQLITE_OMIT_CHECK)
{/* zName: */ "ignore_check_constraints",
@@ -119900,23 +137441,23 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "index_info",
/* ePragTyp: */ PragTyp_INDEX_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
- /* ColNames: */ 15, 3,
+ /* ColNames: */ 21, 3,
/* iArg: */ 0 },
{/* zName: */ "index_list",
/* ePragTyp: */ PragTyp_INDEX_LIST,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
- /* ColNames: */ 26, 5,
+ /* ColNames: */ 38, 5,
/* iArg: */ 0 },
{/* zName: */ "index_xinfo",
/* ePragTyp: */ PragTyp_INDEX_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
- /* ColNames: */ 15, 6,
+ /* ColNames: */ 21, 6,
/* iArg: */ 1 },
#endif
#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
{/* zName: */ "integrity_check",
/* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
- /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1,
+ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt,
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
@@ -119932,24 +137473,12 @@ static const PragmaName aPragmaName[] = {
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
-#if defined(SQLITE_HAS_CODEC)
- {/* zName: */ "key",
- /* ePragTyp: */ PragTyp_KEY,
- /* ePragFlg: */ 0,
- /* ColNames: */ 0, 0,
- /* iArg: */ 0 },
-#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
{/* zName: */ "legacy_alter_table",
/* ePragTyp: */ PragTyp_FLAG,
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
/* ColNames: */ 0, 0,
/* iArg: */ SQLITE_LegacyAlter },
- {/* zName: */ "legacy_file_format",
- /* ePragTyp: */ PragTyp_FLAG,
- /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
- /* ColNames: */ 0, 0,
- /* iArg: */ SQLITE_LegacyFileFmt },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE
{/* zName: */ "lock_proxy_file",
@@ -119962,7 +137491,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "lock_status",
/* ePragTyp: */ PragTyp_LOCK_STATUS,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 43, 2,
+ /* ColNames: */ 53, 2,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
@@ -119984,7 +137513,7 @@ static const PragmaName aPragmaName[] = {
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
#if !defined(SQLITE_OMIT_VIRTUALTABLE)
-#if defined(SQLITE_INTROSPECTION_PRAGMAS)
+#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
{/* zName: */ "module_list",
/* ePragTyp: */ PragTyp_MODULE_LIST,
/* ePragFlg: */ PragFlg_Result0,
@@ -120010,14 +137539,16 @@ static const PragmaName aPragmaName[] = {
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
-#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_PARSER_TRACE)
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if defined(SQLITE_DEBUG)
{/* zName: */ "parser_trace",
- /* ePragTyp: */ PragTyp_PARSER_TRACE,
- /* ePragFlg: */ 0,
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
/* ColNames: */ 0, 0,
- /* iArg: */ 0 },
+ /* iArg: */ SQLITE_ParserTrace },
+#endif
#endif
-#if defined(SQLITE_INTROSPECTION_PRAGMAS)
+#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
{/* zName: */ "pragma_list",
/* ePragTyp: */ PragTyp_PRAGMA_LIST,
/* ePragFlg: */ PragFlg_Result0,
@@ -120034,7 +137565,7 @@ static const PragmaName aPragmaName[] = {
#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
{/* zName: */ "quick_check",
/* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
- /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1,
+ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_Result1|PragFlg_SchemaOpt,
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
@@ -120049,15 +137580,6 @@ static const PragmaName aPragmaName[] = {
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
/* ColNames: */ 0, 0,
/* iArg: */ SQLITE_RecTriggers },
-#endif
-#if defined(SQLITE_HAS_CODEC)
- {/* zName: */ "rekey",
- /* ePragTyp: */ PragTyp_KEY,
- /* ePragFlg: */ 0,
- /* ColNames: */ 0, 0,
- /* iArg: */ 1 },
-#endif
-#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
{/* zName: */ "reverse_unordered_selects",
/* ePragTyp: */ PragTyp_FLAG,
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
@@ -120108,7 +137630,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "stats",
/* ePragTyp: */ PragTyp_STATS,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
- /* ColNames: */ 21, 5,
+ /* ColNames: */ 33, 5,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
@@ -120124,6 +137646,11 @@ static const PragmaName aPragmaName[] = {
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
/* ColNames: */ 8, 6,
/* iArg: */ 0 },
+ {/* zName: */ "table_list",
+ /* ePragTyp: */ PragTyp_TABLE_LIST,
+ /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1,
+ /* ColNames: */ 15, 6,
+ /* iArg: */ 0 },
{/* zName: */ "table_xinfo",
/* ePragTyp: */ PragTyp_TABLE_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
@@ -120142,23 +137669,18 @@ static const PragmaName aPragmaName[] = {
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
#endif
-#if defined(SQLITE_HAS_CODEC)
- {/* zName: */ "textkey",
- /* ePragTyp: */ PragTyp_KEY,
- /* ePragFlg: */ 0,
- /* ColNames: */ 0, 0,
- /* iArg: */ 4 },
- {/* zName: */ "textrekey",
- /* ePragTyp: */ PragTyp_KEY,
- /* ePragFlg: */ 0,
- /* ColNames: */ 0, 0,
- /* iArg: */ 5 },
-#endif
{/* zName: */ "threads",
/* ePragTyp: */ PragTyp_THREADS,
/* ePragFlg: */ PragFlg_Result0,
/* ColNames: */ 0, 0,
/* iArg: */ 0 },
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ {/* zName: */ "trusted_schema",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
+ /* ColNames: */ 0, 0,
+ /* iArg: */ SQLITE_TrustedSchema },
+#endif
#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
{/* zName: */ "user_version",
/* ePragTyp: */ PragTyp_HEADER_VALUE,
@@ -120204,7 +137726,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "wal_checkpoint",
/* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
/* ePragFlg: */ PragFlg_NeedSchema,
- /* ColNames: */ 38, 3,
+ /* ColNames: */ 50, 3,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
@@ -120215,14 +137737,14 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
#endif
};
-/* Number of pragmas: 62 on by default, 81 total. */
+/* Number of pragmas: 68 on by default, 78 total. */
/************** End of pragma.h **********************************************/
/************** Continuing where we left off in pragma.c *********************/
/*
** Interpret the given string as a safety level. Return 0 for OFF,
-** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or
+** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or
** unrecognized string argument. The FULL and EXTRA option is disallowed
** if the omitFull parameter it 1.
**
@@ -120281,7 +137803,7 @@ static int getLockingMode(const char *z){
/*
** Interpret the given string as an auto-vacuum mode value.
**
-** The following strings, "none", "full" and "incremental" are
+** The following strings, "none", "full" and "incremental" are
** acceptable, as are their numeric equivalents: 0, 1 and 2 respectively.
*/
static int getAutoVacuum(const char *z){
@@ -120321,7 +137843,9 @@ static int getTempStore(const char *z){
static int invalidateTempStorage(Parse *pParse){
sqlite3 *db = pParse->db;
if( db->aDb[1].pBt!=0 ){
- if( !db->autoCommit || sqlite3BtreeIsInReadTrans(db->aDb[1].pBt) ){
+ if( !db->autoCommit
+ || sqlite3BtreeTxnState(db->aDb[1].pBt)!=SQLITE_TXN_NONE
+ ){
sqlite3ErrorMsg(pParse, "temporary storage cannot be changed "
"from within a transaction");
return SQLITE_ERROR;
@@ -120433,7 +137957,7 @@ static const char *actionName(u8 action){
case OE_SetDflt: zName = "SET DEFAULT"; break;
case OE_Cascade: zName = "CASCADE"; break;
case OE_Restrict: zName = "RESTRICT"; break;
- default: zName = "NO ACTION";
+ default: zName = "NO ACTION";
assert( action==OE_None ); break;
}
return zName;
@@ -120486,6 +138010,56 @@ static const PragmaName *pragmaLocate(const char *zName){
}
/*
+** Create zero or more entries in the output for the SQL functions
+** defined by FuncDef p.
+*/
+static void pragmaFunclistLine(
+ Vdbe *v, /* The prepared statement being created */
+ FuncDef *p, /* A particular function definition */
+ int isBuiltin, /* True if this is a built-in function */
+ int showInternFuncs /* True if showing internal functions */
+){
+ u32 mask =
+ SQLITE_DETERMINISTIC |
+ SQLITE_DIRECTONLY |
+ SQLITE_SUBTYPE |
+ SQLITE_INNOCUOUS |
+ SQLITE_FUNC_INTERNAL
+ ;
+ if( showInternFuncs ) mask = 0xffffffff;
+ for(; p; p=p->pNext){
+ const char *zType;
+ static const char *azEnc[] = { 0, "utf8", "utf16le", "utf16be" };
+
+ assert( SQLITE_FUNC_ENCMASK==0x3 );
+ assert( strcmp(azEnc[SQLITE_UTF8],"utf8")==0 );
+ assert( strcmp(azEnc[SQLITE_UTF16LE],"utf16le")==0 );
+ assert( strcmp(azEnc[SQLITE_UTF16BE],"utf16be")==0 );
+
+ if( p->xSFunc==0 ) continue;
+ if( (p->funcFlags & SQLITE_FUNC_INTERNAL)!=0
+ && showInternFuncs==0
+ ){
+ continue;
+ }
+ if( p->xValue!=0 ){
+ zType = "w";
+ }else if( p->xFinalize!=0 ){
+ zType = "a";
+ }else{
+ zType = "s";
+ }
+ sqlite3VdbeMultiLoad(v, 1, "sissii",
+ p->zName, isBuiltin,
+ zType, azEnc[p->funcFlags&SQLITE_FUNC_ENCMASK],
+ p->nArg,
+ (p->funcFlags & mask) ^ SQLITE_INNOCUOUS
+ );
+ }
+}
+
+
+/*
** Helper subroutine for PRAGMA integrity_check:
**
** Generate code to output a single-column result row with a value of the
@@ -120502,7 +138076,7 @@ static int integrityCheckResultRow(Vdbe *v){
}
/*
-** Process a pragma statement.
+** Process a pragma statement.
**
** Pragmas are of this form:
**
@@ -120517,7 +138091,7 @@ static int integrityCheckResultRow(Vdbe *v){
** id and pId2 is any empty string.
*/
SQLITE_PRIVATE void sqlite3Pragma(
- Parse *pParse,
+ Parse *pParse,
Token *pId1, /* First part of [schema.]id field */
Token *pId2, /* Second part of [schema.]id field, or NULL */
Token *pValue, /* Token for <value>, or NULL */
@@ -120545,8 +138119,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( iDb<0 ) return;
pDb = &db->aDb[iDb];
- /* If the temp database has been explicitly named as part of the
- ** pragma, make sure it is open.
+ /* If the temp database has been explicitly named as part of the
+ ** pragma, make sure it is open.
*/
if( iDb==1 && sqlite3OpenTempDatabase(pParse) ){
return;
@@ -120606,7 +138180,11 @@ SQLITE_PRIVATE void sqlite3Pragma(
/* Locate the pragma in the lookup table */
pPragma = pragmaLocate(zLeft);
- if( pPragma==0 ) goto pragma_out;
+ if( pPragma==0 ){
+ /* IMP: R-43042-22504 No error messages are generated if an
+ ** unknown pragma is issued. */
+ goto pragma_out;
+ }
/* Make sure the database schema is loaded if the pragma requires that */
if( (pPragma->mPragFlg & PragFlg_NeedSchema)!=0 ){
@@ -120614,7 +138192,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
/* Register the result column names for pragmas that return results */
- if( (pPragma->mPragFlg & PragFlg_NoColumns)==0
+ if( (pPragma->mPragFlg & PragFlg_NoColumns)==0
&& ((pPragma->mPragFlg & PragFlg_NoColumns1)==0 || zRight==0)
){
setPragmaResultColumnNames(v, pPragma);
@@ -120622,7 +138200,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
/* Jump to the appropriate pragma handler */
switch( pPragma->ePragTyp ){
-
+
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
/*
** PRAGMA [schema.]default_cache_size
@@ -120696,7 +138274,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** buffer that the pager module resizes using sqlite3_realloc().
*/
db->nextPagesize = sqlite3Atoi(zRight);
- if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){
+ if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,0,0) ){
sqlite3OomFault(db);
}
}
@@ -120738,7 +138316,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** PRAGMA [schema.]max_page_count=N
**
** The first form reports the current setting for the
- ** maximum number of pages in the database file. The
+ ** maximum number of pages in the database file. The
** second form attempts to change this setting. Both
** forms return the current setting.
**
@@ -120752,13 +138330,19 @@ SQLITE_PRIVATE void sqlite3Pragma(
*/
case PragTyp_PAGE_COUNT: {
int iReg;
+ i64 x = 0;
sqlite3CodeVerifySchema(pParse, iDb);
iReg = ++pParse->nMem;
if( sqlite3Tolower(zLeft[0])=='p' ){
sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg);
}else{
- sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg,
- sqlite3AbsInt32(sqlite3Atoi(zRight)));
+ if( zRight && sqlite3DecOrHexToI64(zRight,&x)==0 ){
+ if( x<0 ) x = 0;
+ else if( x>0xfffffffe ) x = 0xfffffffe;
+ }else{
+ x = 0;
+ }
+ sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, (int)x);
}
sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1);
break;
@@ -120834,6 +138418,11 @@ SQLITE_PRIVATE void sqlite3Pragma(
** then do a query */
eMode = PAGER_JOURNALMODE_QUERY;
}
+ if( eMode==PAGER_JOURNALMODE_OFF && (db->flags & SQLITE_Defensive)!=0 ){
+ /* Do not allow journal-mode "OFF" in defensive since the database
+ ** can become corrupted using ordinary SQL when the journal is off */
+ eMode = PAGER_JOURNALMODE_QUERY;
+ }
}
if( eMode==PAGER_JOURNALMODE_QUERY && pId2->n==0 ){
/* Convert "PRAGMA journal_mode" into "PRAGMA main.journal_mode" */
@@ -120894,7 +138483,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
*/
rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto);
if( rc==SQLITE_OK && (eAuto==1 || eAuto==2) ){
- /* When setting the auto_vacuum mode to either "full" or
+ /* When setting the auto_vacuum mode to either "full" or
** "incremental", write the value of meta[6] in the database
** file. Before writing to meta[6], check that meta[3] indicates
** that this really is an auto-vacuum capable database.
@@ -120931,7 +138520,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
case PragTyp_INCREMENTAL_VACUUM: {
- int iLimit, addr;
+ int iLimit = 0, addr;
if( zRight==0 || !sqlite3GetInt32(zRight, &iLimit) || iLimit<=0 ){
iLimit = 0x7fffffff;
}
@@ -120977,7 +138566,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
**
** The first form reports the current local setting for the
** page cache spill size. The second form turns cache spill on
- ** or off. When turnning cache spill on, the size is set to the
+ ** or off. When turning cache spill on, the size is set to the
** current cache_size. The third form sets a spill size that
** may be different form the cache size.
** If N is positive then that is the
@@ -120996,7 +138585,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( !zRight ){
returnSingleInt(v,
- (db->flags & SQLITE_CacheSpill)==0 ? 0 :
+ (db->flags & SQLITE_CacheSpill)==0 ? 0 :
sqlite3BtreeSetSpillSize(pDb->pBt,0));
}else{
int size = 1;
@@ -121006,7 +138595,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( sqlite3GetBoolean(zRight, size!=0) ){
db->flags |= SQLITE_CacheSpill;
}else{
- db->flags &= ~SQLITE_CacheSpill;
+ db->flags &= ~(u64)SQLITE_CacheSpill;
}
setAllPagerFlags(db);
}
@@ -121088,6 +138677,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
**
*/
case PragTyp_TEMP_STORE_DIRECTORY: {
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
if( !zRight ){
returnSingleText(v, sqlite3_temp_directory);
}else{
@@ -121097,6 +138687,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
if( rc!=SQLITE_OK || res==0 ){
sqlite3ErrorMsg(pParse, "not a writable directory");
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
goto pragma_out;
}
}
@@ -121114,6 +138705,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
#endif /* SQLITE_OMIT_WSD */
}
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
break;
}
@@ -121132,6 +138724,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
**
*/
case PragTyp_DATA_STORE_DIRECTORY: {
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
if( !zRight ){
returnSingleText(v, sqlite3_data_directory);
}else{
@@ -121141,6 +138734,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
if( rc!=SQLITE_OK || res==0 ){
sqlite3ErrorMsg(pParse, "not a writable directory");
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
goto pragma_out;
}
}
@@ -121152,6 +138746,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
#endif /* SQLITE_OMIT_WSD */
}
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
break;
}
#endif
@@ -121170,7 +138765,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
Pager *pPager = sqlite3BtreePager(pDb->pBt);
char *proxy_file_path = NULL;
sqlite3_file *pFile = sqlite3PagerFile(pPager);
- sqlite3OsFileControlHint(pFile, SQLITE_GET_LOCKPROXYFILE,
+ sqlite3OsFileControlHint(pFile, SQLITE_GET_LOCKPROXYFILE,
&proxy_file_path);
returnSingleText(v, proxy_file_path);
}else{
@@ -121178,10 +138773,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3_file *pFile = sqlite3PagerFile(pPager);
int res;
if( zRight[0] ){
- res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE,
+ res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE,
zRight);
} else {
- res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE,
+ res=sqlite3OsFileControl(pFile, SQLITE_SET_LOCKPROXYFILE,
NULL);
}
if( res!=SQLITE_OK ){
@@ -121191,8 +138786,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
break;
}
-#endif /* SQLITE_ENABLE_LOCKING_STYLE */
-
+#endif /* SQLITE_ENABLE_LOCKING_STYLE */
+
/*
** PRAGMA [schema.]synchronous
** PRAGMA [schema.]synchronous=OFF|ON|NORMAL|FULL|EXTRA
@@ -121207,7 +138802,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
returnSingleInt(v, pDb->safety_level-1);
}else{
if( !db->autoCommit ){
- sqlite3ErrorMsg(pParse,
+ sqlite3ErrorMsg(pParse,
"Safety level may not be changed inside a transaction");
}else if( iDb!=1 ){
int iLevel = (getSafetyLevel(zRight,0,1)+1) & PAGER_SYNCHRONOUS_MASK;
@@ -121241,13 +138836,25 @@ SQLITE_PRIVATE void sqlite3Pragma(
#endif
if( sqlite3GetBoolean(zRight, 0) ){
- db->flags |= mask;
+ if( (mask & SQLITE_WriteSchema)==0
+ || (db->flags & SQLITE_Defensive)==0
+ ){
+ db->flags |= mask;
+ }
}else{
db->flags &= ~mask;
if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0;
+ if( (mask & SQLITE_WriteSchema)!=0
+ && sqlite3_stricmp(zRight, "reset")==0
+ ){
+ /* IMP: R-60817-01178 If the argument is "RESET" then schema
+ ** writing is disabled (as with "PRAGMA writable_schema=OFF") and,
+ ** in addition, the schema is reloaded. */
+ sqlite3ResetAllSchemasOfConnection(db);
+ }
}
- /* Many of the flag-pragmas modify the code generated by the SQL
+ /* Many of the flag-pragmas modify the code generated by the SQL
** compiler (eg. count_changes). So add an opcode to expire all
** compiled SQL statements after modifying a pragma value.
*/
@@ -121274,21 +138881,30 @@ SQLITE_PRIVATE void sqlite3Pragma(
*/
case PragTyp_TABLE_INFO: if( zRight ){
Table *pTab;
+ sqlite3CodeVerifyNamedSchema(pParse, zDb);
pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb);
if( pTab ){
- int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
int i, k;
int nHidden = 0;
Column *pCol;
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
pParse->nMem = 7;
- sqlite3CodeVerifySchema(pParse, iTabDb);
sqlite3ViewGetColumnNames(pParse, pTab);
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
- int isHidden = IsHiddenColumn(pCol);
- if( isHidden && pPragma->iArg==0 ){
- nHidden++;
- continue;
+ int isHidden = 0;
+ const Expr *pColExpr;
+ if( pCol->colFlags & COLFLAG_NOINSERT ){
+ if( pPragma->iArg==0 ){
+ nHidden++;
+ continue;
+ }
+ if( pCol->colFlags & COLFLAG_VIRTUAL ){
+ isHidden = 2; /* GENERATED ALWAYS AS ... VIRTUAL */
+ }else if( pCol->colFlags & COLFLAG_STORED ){
+ isHidden = 3; /* GENERATED ALWAYS AS ... STORED */
+ }else{ assert( pCol->colFlags & COLFLAG_HIDDEN );
+ isHidden = 1; /* HIDDEN */
+ }
}
if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){
k = 0;
@@ -121297,13 +138913,16 @@ SQLITE_PRIVATE void sqlite3Pragma(
}else{
for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){}
}
- assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN );
+ pColExpr = sqlite3ColumnExpr(pTab,pCol);
+ assert( pColExpr==0 || pColExpr->op==TK_SPAN || isHidden>=2 );
+ assert( pColExpr==0 || !ExprHasProperty(pColExpr, EP_IntValue)
+ || isHidden>=2 );
sqlite3VdbeMultiLoad(v, 1, pPragma->iArg ? "issisii" : "issisi",
i-nHidden,
- pCol->zName,
+ pCol->zCnName,
sqlite3ColumnType(pCol,""),
pCol->notNull ? 1 : 0,
- pCol->pDflt ? pCol->pDflt->u.zToken : 0,
+ (isHidden>=2 || pColExpr==0) ? 0 : pColExpr->u.zToken,
k,
isHidden);
}
@@ -121311,6 +138930,85 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
break;
+ /*
+ ** PRAGMA table_list
+ **
+ ** Return a single row for each table, virtual table, or view in the
+ ** entire schema.
+ **
+ ** schema: Name of attached database hold this table
+ ** name: Name of the table itself
+ ** type: "table", "view", "virtual", "shadow"
+ ** ncol: Number of columns
+ ** wr: True for a WITHOUT ROWID table
+ ** strict: True for a STRICT table
+ */
+ case PragTyp_TABLE_LIST: {
+ int ii;
+ pParse->nMem = 6;
+ sqlite3CodeVerifyNamedSchema(pParse, zDb);
+ for(ii=0; ii<db->nDb; ii++){
+ HashElem *k;
+ Hash *pHash;
+ int initNCol;
+ if( zDb && sqlite3_stricmp(zDb, db->aDb[ii].zDbSName)!=0 ) continue;
+
+ /* Ensure that the Table.nCol field is initialized for all views
+ ** and virtual tables. Each time we initialize a Table.nCol value
+ ** for a table, that can potentially disrupt the hash table, so restart
+ ** the initialization scan.
+ */
+ pHash = &db->aDb[ii].pSchema->tblHash;
+ initNCol = sqliteHashCount(pHash);
+ while( initNCol-- ){
+ for(k=sqliteHashFirst(pHash); 1; k=sqliteHashNext(k) ){
+ Table *pTab;
+ if( k==0 ){ initNCol = 0; break; }
+ pTab = sqliteHashData(k);
+ if( pTab->nCol==0 ){
+ char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName);
+ if( zSql ){
+ sqlite3_stmt *pDummy = 0;
+ (void)sqlite3_prepare(db, zSql, -1, &pDummy, 0);
+ (void)sqlite3_finalize(pDummy);
+ sqlite3DbFree(db, zSql);
+ }
+ if( db->mallocFailed ){
+ sqlite3ErrorMsg(db->pParse, "out of memory");
+ db->pParse->rc = SQLITE_NOMEM_BKPT;
+ }
+ pHash = &db->aDb[ii].pSchema->tblHash;
+ break;
+ }
+ }
+ }
+
+ for(k=sqliteHashFirst(pHash); k; k=sqliteHashNext(k) ){
+ Table *pTab = sqliteHashData(k);
+ const char *zType;
+ if( zRight && sqlite3_stricmp(zRight, pTab->zName)!=0 ) continue;
+ if( IsView(pTab) ){
+ zType = "view";
+ }else if( IsVirtual(pTab) ){
+ zType = "virtual";
+ }else if( pTab->tabFlags & TF_Shadow ){
+ zType = "shadow";
+ }else{
+ zType = "table";
+ }
+ sqlite3VdbeMultiLoad(v, 1, "sssiii",
+ db->aDb[ii].zDbSName,
+ sqlite3PreferredTableName(pTab->zName),
+ zType,
+ pTab->nCol,
+ (pTab->tabFlags & TF_WithoutRowid)!=0,
+ (pTab->tabFlags & TF_Strict)!=0
+ );
+ }
+ }
+ }
+ break;
+
#ifdef SQLITE_DEBUG
case PragTyp_STATS: {
Index *pIdx;
@@ -121320,7 +139018,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){
Table *pTab = sqliteHashData(i);
sqlite3VdbeMultiLoad(v, 1, "ssiii",
- pTab->zName,
+ sqlite3PreferredTableName(pTab->zName),
0,
pTab->szTabRow,
pTab->nRowLogEst,
@@ -121342,6 +139040,15 @@ SQLITE_PRIVATE void sqlite3Pragma(
Index *pIdx;
Table *pTab;
pIdx = sqlite3FindIndex(db, zRight, zDb);
+ if( pIdx==0 ){
+ /* If there is no index named zRight, check to see if there is a
+ ** WITHOUT ROWID table named zRight, and if there is, show the
+ ** structure of the PRIMARY KEY index for that table. */
+ pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb);
+ if( pTab && !HasRowid(pTab) ){
+ pIdx = sqlite3PrimaryKeyIndex(pTab);
+ }
+ }
if( pIdx ){
int iIdxDb = sqlite3SchemaToIndex(db, pIdx->pSchema);
int i;
@@ -121361,7 +139068,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
for(i=0; i<mx; i++){
i16 cnum = pIdx->aiColumn[i];
sqlite3VdbeMultiLoad(v, 1, "iisX", i, cnum,
- cnum<0 ? 0 : pTab->aCol[cnum].zName);
+ cnum<0 ? 0 : pTab->aCol[cnum].zCnName);
if( pPragma->iArg ){
sqlite3VdbeMultiLoad(v, 4, "isiX",
pIdx->aSortOrder[i],
@@ -121421,21 +139128,23 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
break;
-#ifdef SQLITE_INTROSPECTION_PRAGMAS
+#ifndef SQLITE_OMIT_INTROSPECTION_PRAGMAS
case PragTyp_FUNCTION_LIST: {
int i;
HashElem *j;
FuncDef *p;
- pParse->nMem = 2;
+ int showInternFunc = (db->mDbFlags & DBFLAG_InternalFunc)!=0;
+ pParse->nMem = 6;
for(i=0; i<SQLITE_FUNC_HASH_SZ; i++){
for(p=sqlite3BuiltinFunctions.a[i]; p; p=p->u.pHash ){
- if( p->funcFlags & SQLITE_FUNC_INTERNAL ) continue;
- sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 1);
+ assert( p->funcFlags & SQLITE_FUNC_BUILTIN );
+ pragmaFunclistLine(v, p, 1, showInternFunc);
}
}
for(j=sqliteHashFirst(&db->aFunc); j; j=sqliteHashNext(j)){
p = (FuncDef*)sqliteHashData(j);
- sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 0);
+ assert( (p->funcFlags & SQLITE_FUNC_BUILTIN)==0 );
+ pragmaFunclistLine(v, p, 0, showInternFunc);
}
}
break;
@@ -121468,11 +139177,11 @@ SQLITE_PRIVATE void sqlite3Pragma(
FKey *pFK;
Table *pTab;
pTab = sqlite3FindTable(db, zRight, zDb);
- if( pTab ){
- pFK = pTab->pFKey;
+ if( pTab && IsOrdinaryTable(pTab) ){
+ pFK = pTab->u.tab.pFKey;
if( pFK ){
int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
- int i = 0;
+ int i = 0;
pParse->nMem = 8;
sqlite3CodeVerifySchema(pParse, iTabDb);
while(pFK){
@@ -121482,7 +139191,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
i,
j,
pFK->zTo,
- pTab->aCol[pFK->aCol[j].iFrom].zName,
+ pTab->aCol[pFK->aCol[j].iFrom].zCnName,
pFK->aCol[j].zCol,
actionName(pFK->aAction[1]), /* ON UPDATE */
actionName(pFK->aAction[0]), /* ON DELETE */
@@ -121509,7 +139218,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
HashElem *k; /* Loop counter: Next table in schema */
int x; /* result variable */
int regResult; /* 3 registers to hold a result row */
- int regKey; /* Register to hold key for checking the FK */
int regRow; /* Registers to hold a row from pTab */
int addrTop; /* Top of a loop checking foreign keys */
int addrOk; /* Jump here if the key is OK */
@@ -121517,11 +139225,9 @@ SQLITE_PRIVATE void sqlite3Pragma(
regResult = pParse->nMem+1;
pParse->nMem += 4;
- regKey = ++pParse->nMem;
regRow = ++pParse->nMem;
k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash);
while( k ){
- int iTabDb;
if( zRight ){
pTab = sqlite3LocateTable(pParse, 0, zRight, zDb);
k = 0;
@@ -121529,24 +139235,26 @@ SQLITE_PRIVATE void sqlite3Pragma(
pTab = (Table*)sqliteHashData(k);
k = sqliteHashNext(k);
}
- if( pTab==0 || pTab->pFKey==0 ) continue;
- iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
- sqlite3CodeVerifySchema(pParse, iTabDb);
- sqlite3TableLock(pParse, iTabDb, pTab->tnum, 0, pTab->zName);
- if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow;
- sqlite3OpenTable(pParse, 0, iTabDb, pTab, OP_OpenRead);
+ if( pTab==0 || !IsOrdinaryTable(pTab) || pTab->u.tab.pFKey==0 ) continue;
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ zDb = db->aDb[iDb].zDbSName;
+ sqlite3CodeVerifySchema(pParse, iDb);
+ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
+ sqlite3TouchRegister(pParse, pTab->nCol+regRow);
+ sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead);
sqlite3VdbeLoadString(v, regResult, pTab->zName);
- for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
+ assert( IsOrdinaryTable(pTab) );
+ for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){
pParent = sqlite3FindTable(db, pFK->zTo, zDb);
if( pParent==0 ) continue;
pIdx = 0;
- sqlite3TableLock(pParse, iTabDb, pParent->tnum, 0, pParent->zName);
+ sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName);
x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0);
if( x==0 ){
if( pIdx==0 ){
- sqlite3OpenTable(pParse, i, iTabDb, pParent, OP_OpenRead);
+ sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead);
}else{
- sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iTabDb);
+ sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb);
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
}
}else{
@@ -121558,20 +139266,22 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( pFK ) break;
if( pParse->nTab<i ) pParse->nTab = i;
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); VdbeCoverage(v);
- for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
+ assert( IsOrdinaryTable(pTab) );
+ for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){
pParent = sqlite3FindTable(db, pFK->zTo, zDb);
pIdx = 0;
aiCols = 0;
if( pParent ){
x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols);
- assert( x==0 );
+ assert( x==0 || db->mallocFailed );
}
- addrOk = sqlite3VdbeMakeLabel(v);
+ addrOk = sqlite3VdbeMakeLabel(pParse);
/* Generate code to read the child key values into registers
- ** regRow..regRow+n. If any of the child key values are NULL, this
- ** row cannot cause an FK violation. Jump directly to addrOk in
+ ** regRow..regRow+n. If any of the child key values are NULL, this
+ ** row cannot cause an FK violation. Jump directly to addrOk in
** this case. */
+ sqlite3TouchRegister(pParse, regRow + pFK->nCol);
for(j=0; j<pFK->nCol; j++){
int iCol = aiCols ? aiCols[j] : pFK->aCol[j].iFrom;
sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, iCol, regRow+j);
@@ -121581,15 +139291,15 @@ SQLITE_PRIVATE void sqlite3Pragma(
/* Generate code to query the parent index for a matching parent
** key. If a match is found, jump to addrOk. */
if( pIdx ){
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey,
+ sqlite3VdbeAddOp4(v, OP_Affinity, regRow, pFK->nCol, 0,
sqlite3IndexAffinityStr(db,pIdx), pFK->nCol);
- sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
+ sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regRow, pFK->nCol);
VdbeCoverage(v);
}else if( pParent ){
int jmp = sqlite3VdbeCurrentAddr(v)+2;
sqlite3VdbeAddOp3(v, OP_SeekRowid, i, jmp, regRow); VdbeCoverage(v);
sqlite3VdbeGoto(v, addrOk);
- assert( pFK->nCol==1 );
+ assert( pFK->nCol==1 || db->mallocFailed );
}
/* Generate code to report an FK violation to the caller. */
@@ -121611,19 +139321,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
#endif /* !defined(SQLITE_OMIT_TRIGGER) */
#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
-#ifndef NDEBUG
- case PragTyp_PARSER_TRACE: {
- if( zRight ){
- if( sqlite3GetBoolean(zRight, 0) ){
- sqlite3ParserTrace(stdout, "parser: ");
- }else{
- sqlite3ParserTrace(0, 0);
- }
- }
- }
- break;
-#endif
-
+#ifndef SQLITE_OMIT_CASE_SENSITIVE_LIKE_PRAGMA
/* Reinstall the LIKE and GLOB functions. The variant of LIKE
** used will be case sensitive or not depending on the RHS.
*/
@@ -121633,6 +139331,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
break;
+#endif /* SQLITE_OMIT_CASE_SENSITIVE_LIKE_PRAGMA */
#ifndef SQLITE_INTEGRITY_CHECK_ERROR_MAX
# define SQLITE_INTEGRITY_CHECK_ERROR_MAX 100
@@ -121646,13 +139345,26 @@ SQLITE_PRIVATE void sqlite3Pragma(
**
** Verify the integrity of the database.
**
- ** The "quick_check" is reduced version of
+ ** The "quick_check" is reduced version of
** integrity_check designed to detect most database corruption
** without the overhead of cross-checking indexes. Quick_check
- ** is linear time wherease integrity_check is O(NlogN).
+ ** is linear time whereas integrity_check is O(NlogN).
+ **
+ ** The maximum number of errors is 100 by default. A different default
+ ** can be specified using a numeric parameter N.
+ **
+ ** Or, the parameter N can be the name of a table. In that case, only
+ ** the one table named is verified. The freelist is only verified if
+ ** the named table is "sqlite_schema" (or one of its aliases).
+ **
+ ** All schemas are checked by default. To check just a single
+ ** schema, use the form:
+ **
+ ** PRAGMA schema.integrity_check;
*/
case PragTyp_INTEGRITY_CHECK: {
int i, j, addr, mxErr;
+ Table *pObjTab = 0; /* Check only this one table, if not NULL */
int isQuick = (sqlite3Tolower(zLeft[0])=='q');
@@ -121675,9 +139387,13 @@ SQLITE_PRIVATE void sqlite3Pragma(
/* Set the maximum error count */
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
if( zRight ){
- sqlite3GetInt32(zRight, &mxErr);
- if( mxErr<=0 ){
- mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
+ if( sqlite3GetInt32(zRight, &mxErr) ){
+ if( mxErr<=0 ){
+ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
+ }
+ }else{
+ pObjTab = sqlite3LocateTable(pParse, 0, zRight,
+ iDb>=0 ? db->aDb[iDb].zDbSName : 0);
}
}
sqlite3VdbeAddOp2(v, OP_Integer, mxErr-1, 1); /* reg[1] holds errors left */
@@ -121694,6 +139410,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( iDb>=0 && i!=iDb ) continue;
sqlite3CodeVerifySchema(pParse, i);
+ pParse->okConstFactor = 0; /* tag-20230327-1 */
/* Do an integrity check of the B-Tree
**
@@ -121706,15 +139423,21 @@ SQLITE_PRIVATE void sqlite3Pragma(
Table *pTab = sqliteHashData(x); /* Current table */
Index *pIdx; /* An index on pTab */
int nIdx; /* Number of indexes on pTab */
+ if( pObjTab && pObjTab!=pTab ) continue;
if( HasRowid(pTab) ) cnt++;
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
if( nIdx>mxIdx ) mxIdx = nIdx;
}
+ if( cnt==0 ) continue;
+ if( pObjTab ) cnt++;
aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1));
if( aRoot==0 ) break;
- for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ cnt = 0;
+ if( pObjTab ) aRoot[++cnt] = 0;
+ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx;
+ if( pObjTab && pObjTab!=pTab ) continue;
if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
aRoot[++cnt] = pIdx->tnum;
@@ -121723,7 +139446,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
aRoot[0] = cnt;
/* Make sure sufficient number of registers have been allocated */
- pParse->nMem = MAX( pParse->nMem, 8+mxIdx );
+ sqlite3TouchRegister(pParse, 8+mxIdx);
sqlite3ClearTempRegCache(pParse);
/* Do the b-tree integrity checks */
@@ -121742,17 +139465,52 @@ SQLITE_PRIVATE void sqlite3Pragma(
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx, *pPk;
- Index *pPrior = 0;
+ Index *pPrior = 0; /* Previous index */
int loopTop;
int iDataCur, iIdxCur;
int r1 = -1;
+ int bStrict; /* True for a STRICT table */
+ int r2; /* Previous key for WITHOUT ROWID tables */
+ int mxCol; /* Maximum non-virtual column number */
- if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */
- pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
+ if( pObjTab && pObjTab!=pTab ) continue;
+ if( !IsOrdinaryTable(pTab) ){
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3_vtab *pVTab;
+ int a1;
+ if( !IsVirtual(pTab) ) continue;
+ if( pTab->nCol<=0 ){
+ const char *zMod = pTab->u.vtab.azArg[0];
+ if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
+ }
+ sqlite3ViewGetColumnNames(pParse, pTab);
+ if( pTab->u.vtab.p==0 ) continue;
+ pVTab = pTab->u.vtab.p->pVtab;
+ if( NEVER(pVTab==0) ) continue;
+ if( NEVER(pVTab->pModule==0) ) continue;
+ if( pVTab->pModule->iVersion<4 ) continue;
+ if( pVTab->pModule->xIntegrity==0 ) continue;
+ sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
+ pTab->nTabRef++;
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
+ a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
+ integrityCheckResultRow(v);
+ sqlite3VdbeJumpHere(v, a1);
+#endif
+ continue;
+ }
+ if( isQuick || HasRowid(pTab) ){
+ pPk = 0;
+ r2 = 0;
+ }else{
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ r2 = sqlite3GetTempRange(pParse, pPk->nKeyCol);
+ sqlite3VdbeAddOp3(v, OP_Null, 1, r2, r2+pPk->nKeyCol-1);
+ }
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0,
1, 0, &iDataCur, &iIdxCur);
/* reg[7] counts the number of entries in the table.
- ** reg[8+i] counts the number of entries in the i-th index
+ ** reg[8+i] counts the number of entries in the i-th index
*/
sqlite3VdbeAddOp2(v, OP_Integer, 0, 7);
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
@@ -121762,39 +139520,194 @@ SQLITE_PRIVATE void sqlite3Pragma(
assert( sqlite3NoTempsInRange(pParse,1,7+j) );
sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v);
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
+
+ /* Fetch the right-most column from the table. This will cause
+ ** the entire record header to be parsed and sanity checked. It
+ ** will also prepopulate the cursor column cache that is used
+ ** by the OP_IsType code, so it is a required step.
+ */
+ assert( !IsVirtual(pTab) );
+ if( HasRowid(pTab) ){
+ mxCol = -1;
+ for(j=0; j<pTab->nCol; j++){
+ if( (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)==0 ) mxCol++;
+ }
+ if( mxCol==pTab->iPKey ) mxCol--;
+ }else{
+ /* COLFLAG_VIRTUAL columns are not included in the WITHOUT ROWID
+ ** PK index column-count, so there is no need to account for them
+ ** in this case. */
+ mxCol = sqlite3PrimaryKeyIndex(pTab)->nColumn-1;
+ }
+ if( mxCol>=0 ){
+ sqlite3VdbeAddOp3(v, OP_Column, iDataCur, mxCol, 3);
+ sqlite3VdbeTypeofColumn(v, 3);
+ }
+
if( !isQuick ){
- /* Sanity check on record header decoding */
- sqlite3VdbeAddOp3(v, OP_Column, iDataCur, pTab->nCol-1, 3);
- sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
+ if( pPk ){
+ /* Verify WITHOUT ROWID keys are in ascending order */
+ int a1;
+ char *zErr;
+ a1 = sqlite3VdbeAddOp4Int(v, OP_IdxGT, iDataCur, 0,r2,pPk->nKeyCol);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp1(v, OP_IsNull, r2); VdbeCoverage(v);
+ zErr = sqlite3MPrintf(db,
+ "row not in PRIMARY KEY order for %s",
+ pTab->zName);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
+ integrityCheckResultRow(v);
+ sqlite3VdbeJumpHere(v, a1);
+ sqlite3VdbeJumpHere(v, a1+1);
+ for(j=0; j<pPk->nKeyCol; j++){
+ sqlite3ExprCodeLoadIndexColumn(pParse, pPk, iDataCur, j, r2+j);
+ }
+ }
}
- /* Verify that all NOT NULL columns really are NOT NULL */
+ /* Verify datatypes for all columns:
+ **
+ ** (1) NOT NULL columns may not contain a NULL
+ ** (2) Datatype must be exact for non-ANY columns in STRICT tables
+ ** (3) Datatype for TEXT columns in non-STRICT tables must be
+ ** NULL, TEXT, or BLOB.
+ ** (4) Datatype for numeric columns in non-STRICT tables must not
+ ** be a TEXT value that can be losslessly converted to numeric.
+ */
+ bStrict = (pTab->tabFlags & TF_Strict)!=0;
for(j=0; j<pTab->nCol; j++){
char *zErr;
- int jmp2;
+ Column *pCol = pTab->aCol + j; /* The column to be checked */
+ int labelError; /* Jump here to report an error */
+ int labelOk; /* Jump here if all looks ok */
+ int p1, p3, p4; /* Operands to the OP_IsType opcode */
+ int doTypeCheck; /* Check datatypes (besides NOT NULL) */
+
if( j==pTab->iPKey ) continue;
- if( pTab->aCol[j].notNull==0 ) continue;
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3);
- sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
- jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v);
- zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName,
- pTab->aCol[j].zName);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
+ if( bStrict ){
+ doTypeCheck = pCol->eCType>COLTYPE_ANY;
+ }else{
+ doTypeCheck = pCol->affinity>SQLITE_AFF_BLOB;
+ }
+ if( pCol->notNull==0 && !doTypeCheck ) continue;
+
+ /* Compute the operands that will be needed for OP_IsType */
+ p4 = SQLITE_NULL;
+ if( pCol->colFlags & COLFLAG_VIRTUAL ){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3);
+ p1 = -1;
+ p3 = 3;
+ }else{
+ if( pCol->iDflt ){
+ sqlite3_value *pDfltValue = 0;
+ sqlite3ValueFromExpr(db, sqlite3ColumnExpr(pTab,pCol), ENC(db),
+ pCol->affinity, &pDfltValue);
+ if( pDfltValue ){
+ p4 = sqlite3_value_type(pDfltValue);
+ sqlite3ValueFree(pDfltValue);
+ }
+ }
+ p1 = iDataCur;
+ if( !HasRowid(pTab) ){
+ testcase( j!=sqlite3TableColumnToStorage(pTab, j) );
+ p3 = sqlite3TableColumnToIndex(sqlite3PrimaryKeyIndex(pTab), j);
+ }else{
+ p3 = sqlite3TableColumnToStorage(pTab,j);
+ testcase( p3!=j);
+ }
+ }
+
+ labelError = sqlite3VdbeMakeLabel(pParse);
+ labelOk = sqlite3VdbeMakeLabel(pParse);
+ if( pCol->notNull ){
+ /* (1) NOT NULL columns may not contain a NULL */
+ int jmp3;
+ int jmp2 = sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4);
+ VdbeCoverage(v);
+ if( p1<0 ){
+ sqlite3VdbeChangeP5(v, 0x0f); /* INT, REAL, TEXT, or BLOB */
+ jmp3 = jmp2;
+ }else{
+ sqlite3VdbeChangeP5(v, 0x0d); /* INT, TEXT, or BLOB */
+ /* OP_IsType does not detect NaN values in the database file
+ ** which should be treated as a NULL. So if the header type
+ ** is REAL, we have to load the actual data using OP_Column
+ ** to reliably determine if the value is a NULL. */
+ sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3);
+ jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk);
+ VdbeCoverage(v);
+ }
+ zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName,
+ pCol->zCnName);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
+ if( doTypeCheck ){
+ sqlite3VdbeGoto(v, labelError);
+ sqlite3VdbeJumpHere(v, jmp2);
+ sqlite3VdbeJumpHere(v, jmp3);
+ }else{
+ /* VDBE byte code will fall thru */
+ }
+ }
+ if( bStrict && doTypeCheck ){
+ /* (2) Datatype must be exact for non-ANY columns in STRICT tables*/
+ static unsigned char aStdTypeMask[] = {
+ 0x1f, /* ANY */
+ 0x18, /* BLOB */
+ 0x11, /* INT */
+ 0x11, /* INTEGER */
+ 0x13, /* REAL */
+ 0x14 /* TEXT */
+ };
+ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4);
+ assert( pCol->eCType>=1 && pCol->eCType<=sizeof(aStdTypeMask) );
+ sqlite3VdbeChangeP5(v, aStdTypeMask[pCol->eCType-1]);
+ VdbeCoverage(v);
+ zErr = sqlite3MPrintf(db, "non-%s value in %s.%s",
+ sqlite3StdType[pCol->eCType-1],
+ pTab->zName, pTab->aCol[j].zCnName);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
+ }else if( !bStrict && pCol->affinity==SQLITE_AFF_TEXT ){
+ /* (3) Datatype for TEXT columns in non-STRICT tables must be
+ ** NULL, TEXT, or BLOB. */
+ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4);
+ sqlite3VdbeChangeP5(v, 0x1c); /* NULL, TEXT, or BLOB */
+ VdbeCoverage(v);
+ zErr = sqlite3MPrintf(db, "NUMERIC value in %s.%s",
+ pTab->zName, pTab->aCol[j].zCnName);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
+ }else if( !bStrict && pCol->affinity>=SQLITE_AFF_NUMERIC ){
+ /* (4) Datatype for numeric columns in non-STRICT tables must not
+ ** be a TEXT value that can be converted to numeric. */
+ sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4);
+ sqlite3VdbeChangeP5(v, 0x1b); /* NULL, INT, FLOAT, or BLOB */
+ VdbeCoverage(v);
+ if( p1>=0 ){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3);
+ }
+ sqlite3VdbeAddOp4(v, OP_Affinity, 3, 1, 0, "C", P4_STATIC);
+ sqlite3VdbeAddOp4Int(v, OP_IsType, -1, labelOk, 3, p4);
+ sqlite3VdbeChangeP5(v, 0x1c); /* NULL, TEXT, or BLOB */
+ VdbeCoverage(v);
+ zErr = sqlite3MPrintf(db, "TEXT value in %s.%s",
+ pTab->zName, pTab->aCol[j].zCnName);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
+ }
+ sqlite3VdbeResolveLabel(v, labelError);
integrityCheckResultRow(v);
- sqlite3VdbeJumpHere(v, jmp2);
+ sqlite3VdbeResolveLabel(v, labelOk);
}
/* Verify CHECK constraints */
if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){
ExprList *pCheck = sqlite3ExprListDup(db, pTab->pCheck, 0);
if( db->mallocFailed==0 ){
- int addrCkFault = sqlite3VdbeMakeLabel(v);
- int addrCkOk = sqlite3VdbeMakeLabel(v);
+ int addrCkFault = sqlite3VdbeMakeLabel(pParse);
+ int addrCkOk = sqlite3VdbeMakeLabel(pParse);
char *zErr;
int k;
pParse->iSelfTab = iDataCur + 1;
for(k=pCheck->nExpr-1; k>0; k--){
sqlite3ExprIfFalse(pParse, pCheck->a[k].pExpr, addrCkFault, 0);
}
- sqlite3ExprIfTrue(pParse, pCheck->a[0].pExpr, addrCkOk,
+ sqlite3ExprIfTrue(pParse, pCheck->a[0].pExpr, addrCkOk,
SQLITE_JUMPIFNULL);
sqlite3VdbeResolveLabel(v, addrCkFault);
pParse->iSelfTab = 0;
@@ -121809,8 +139722,9 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( !isQuick ){ /* Omit the remaining tests for quick_check */
/* Validate index entries for the current row */
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
- int jmp2, jmp3, jmp4, jmp5;
- int ckUniq = sqlite3VdbeMakeLabel(v);
+ int jmp2, jmp3, jmp4, jmp5, label6;
+ int kk;
+ int ckUniq = sqlite3VdbeMakeLabel(pParse);
if( pPk==pIdx ) continue;
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3,
pPrior, r1);
@@ -121827,13 +139741,49 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
jmp4 = integrityCheckResultRow(v);
sqlite3VdbeJumpHere(v, jmp2);
+
+ /* The OP_IdxRowid opcode is an optimized version of OP_Column
+ ** that extracts the rowid off the end of the index record.
+ ** But it only works correctly if index record does not have
+ ** any extra bytes at the end. Verify that this is the case. */
+ if( HasRowid(pTab) ){
+ int jmp7;
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur+j, 3);
+ jmp7 = sqlite3VdbeAddOp3(v, OP_Eq, 3, 0, r1+pIdx->nColumn-1);
+ VdbeCoverageNeverNull(v);
+ sqlite3VdbeLoadString(v, 3,
+ "rowid not at end-of-record for row ");
+ sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
+ sqlite3VdbeLoadString(v, 4, " of index ");
+ sqlite3VdbeGoto(v, jmp5-1);
+ sqlite3VdbeJumpHere(v, jmp7);
+ }
+
+ /* Any indexed columns with non-BINARY collations must still hold
+ ** the exact same text value as the table. */
+ label6 = 0;
+ for(kk=0; kk<pIdx->nKeyCol; kk++){
+ if( pIdx->azColl[kk]==sqlite3StrBINARY ) continue;
+ if( label6==0 ) label6 = sqlite3VdbeMakeLabel(pParse);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur+j, kk, 3);
+ sqlite3VdbeAddOp3(v, OP_Ne, 3, label6, r1+kk); VdbeCoverage(v);
+ }
+ if( label6 ){
+ int jmp6 = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeResolveLabel(v, label6);
+ sqlite3VdbeLoadString(v, 3, "row ");
+ sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
+ sqlite3VdbeLoadString(v, 4, " values differ from index ");
+ sqlite3VdbeGoto(v, jmp5-1);
+ sqlite3VdbeJumpHere(v, jmp6);
+ }
+
/* For UNIQUE indexes, verify that only one entry exists with the
** current key. The entry is unique if (1) any column is NULL
** or (2) the next entry has a different key */
if( IsUniqueIndex(pIdx) ){
- int uniqOk = sqlite3VdbeMakeLabel(v);
+ int uniqOk = sqlite3VdbeMakeLabel(pParse);
int jmp6;
- int kk;
for(kk=0; kk<pIdx->nKeyCol; kk++){
int iCol = pIdx->aiColumn[kk];
assert( iCol!=XN_ROWID && iCol<pTab->nCol );
@@ -121856,7 +139806,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, loopTop-1);
-#ifndef SQLITE_OMIT_BTREECOUNT
if( !isQuick ){
sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
@@ -121869,9 +139818,11 @@ SQLITE_PRIVATE void sqlite3Pragma(
integrityCheckResultRow(v);
sqlite3VdbeJumpHere(v, addr);
}
+ if( pPk ){
+ sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol);
+ }
}
-#endif /* SQLITE_OMIT_BTREECOUNT */
- }
+ }
}
{
static const int iLn = VDBE_OFFSET_LINENO(2);
@@ -121913,7 +139864,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** encoding that will be used for the main database file if a new file
** is created. If an existing main database file is opened, then the
** default text encoding for the existing database is used.
- **
+ **
** In all cases new databases created using the ATTACH command are
** created to use the same default text encoding as the main database. If
** the main database has not been initialized and/or created when ATTACH
@@ -121951,14 +139902,12 @@ SQLITE_PRIVATE void sqlite3Pragma(
** will be overwritten when the schema is next loaded. If it does not
** already exists, it will be created to use the new encoding value.
*/
- if(
- !(DbHasProperty(db, 0, DB_SchemaLoaded)) ||
- DbHasProperty(db, 0, DB_Empty)
- ){
+ if( (db->mDbFlags & DBFLAG_EncodingFixed)==0 ){
for(pEnc=&encnames[0]; pEnc->zName; pEnc++){
if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){
- SCHEMA_ENC(db) = ENC(db) =
- pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE;
+ u8 enc = pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE;
+ SCHEMA_ENC(db) = enc;
+ sqlite3SetTextEncoding(db, enc);
break;
}
}
@@ -122021,6 +139970,12 @@ SQLITE_PRIVATE void sqlite3Pragma(
aOp[1].p1 = iDb;
aOp[1].p2 = iCookie;
aOp[1].p3 = sqlite3Atoi(zRight);
+ aOp[1].p5 = 1;
+ if( iCookie==BTREE_SCHEMA_VERSION && (db->flags & SQLITE_Defensive)!=0 ){
+ /* Do not allow the use of PRAGMA schema_version=VALUE in defensive
+ ** mode. Change the OP_SetCookie opcode into a no-op. */
+ aOp[1].opcode = OP_Noop;
+ }
}else{
/* Read the specified cookie value */
static const VdbeOpList readCookie[] = {
@@ -122068,7 +140023,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** Checkpoint the database.
*/
case PragTyp_WAL_CHECKPOINT: {
- int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED);
+ int iBt = (pId2->z?iDb:SQLITE_MAX_DB);
int eMode = SQLITE_CHECKPOINT_PASSIVE;
if( zRight ){
if( sqlite3StrICmp(zRight, "full")==0 ){
@@ -122097,8 +140052,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( zRight ){
sqlite3_wal_autocheckpoint(db, sqlite3Atoi(zRight));
}
- returnSingleInt(v,
- db->xWalCallback==sqlite3WalDefaultHook ?
+ returnSingleInt(v,
+ db->xWalCallback==sqlite3WalDefaultHook ?
SQLITE_PTR_TO_INT(db->pWalArg) : 0);
}
break;
@@ -122138,7 +140093,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** 0x0002 Run ANALYZE on tables that might benefit. On by default.
** See below for additional information.
**
- ** 0x0004 (Not yet implemented) Record usage and performance
+ ** 0x0004 (Not yet implemented) Record usage and performance
** information from the current session in the
** database file so that it will be available to "optimize"
** pragmas run by future database connections.
@@ -122149,7 +140104,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** The default MASK is and always shall be 0xfffe. 0xfffe means perform all
** of the optimizations listed above except Debug Mode, including new
** optimizations that have not yet been invented. If new optimizations are
- ** ever added that should be off by default, those off-by-default
+ ** ever added that should be off by default, those off-by-default
** optimizations will have bitmasks of 0x10000 or larger.
**
** DETERMINATION OF WHEN TO RUN ANALYZE
@@ -122177,7 +140132,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
Schema *pSchema; /* The current schema */
Table *pTab; /* A table in the schema */
Index *pIdx; /* An index of the table */
- LogEst szThreshold; /* Size threshold above which reanalysis is needd */
+ LogEst szThreshold; /* Size threshold above which reanalysis needed */
char *zSubSql; /* SQL statement for the OP_SqlExec opcode */
u32 opMask; /* Mask of operations to perform */
@@ -122210,7 +140165,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
if( szThreshold ){
sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
- sqlite3VdbeAddOp3(v, OP_IfSmaller, iTabCur,
+ sqlite3VdbeAddOp3(v, OP_IfSmaller, iTabCur,
sqlite3VdbeCurrentAddr(v)+2+(opMask&1), szThreshold);
VdbeCoverage(v);
}
@@ -122268,6 +140223,27 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
/*
+ ** PRAGMA hard_heap_limit
+ ** PRAGMA hard_heap_limit = N
+ **
+ ** Invoke sqlite3_hard_heap_limit64() to query or set the hard heap
+ ** limit. The hard heap limit can be activated or lowered by this
+ ** pragma, but not raised or deactivated. Only the
+ ** sqlite3_hard_heap_limit64() C-language API can raise or deactivate
+ ** the hard heap limit. This allows an application to set a heap limit
+ ** constraint that cannot be relaxed by an untrusted SQL script.
+ */
+ case PragTyp_HARD_HEAP_LIMIT: {
+ sqlite3_int64 N;
+ if( zRight && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK ){
+ sqlite3_int64 iPrior = sqlite3_hard_heap_limit64(-1);
+ if( N>0 && (iPrior==0 || iPrior>N) ) sqlite3_hard_heap_limit64(N);
+ }
+ returnSingleInt(v, sqlite3_hard_heap_limit64(-1));
+ break;
+ }
+
+ /*
** PRAGMA threads
** PRAGMA threads = N
**
@@ -122286,6 +140262,25 @@ SQLITE_PRIVATE void sqlite3Pragma(
break;
}
+ /*
+ ** PRAGMA analysis_limit
+ ** PRAGMA analysis_limit = N
+ **
+ ** Configure the maximum number of rows that ANALYZE will examine
+ ** in each index that it looks at. Return the new limit.
+ */
+ case PragTyp_ANALYSIS_LIMIT: {
+ sqlite3_int64 N;
+ if( zRight
+ && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK /* IMP: R-40975-20399 */
+ && N>=0
+ ){
+ db->nAnalysisLimit = (int)(N&0x7fffffff);
+ }
+ returnSingleInt(v, db->nAnalysisLimit); /* IMP: R-57594-65522 */
+ break;
+ }
+
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/*
** Report the current state of file logs for all databases
@@ -122304,7 +140299,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
pBt = db->aDb[i].pBt;
if( pBt==0 || sqlite3BtreePager(pBt)==0 ){
zState = "closed";
- }else if( sqlite3_file_control(db, i ? db->aDb[i].zDbSName : 0,
+ }else if( sqlite3_file_control(db, i ? db->aDb[i].zDbSName : 0,
SQLITE_FCNTL_LOCKSTATE, &j)==SQLITE_OK ){
zState = azLockName[j];
}
@@ -122314,57 +140309,11 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
#endif
-#ifdef SQLITE_HAS_CODEC
- /* Pragma iArg
- ** ---------- ------
- ** key 0
- ** rekey 1
- ** hexkey 2
- ** hexrekey 3
- ** textkey 4
- ** textrekey 5
- */
- case PragTyp_KEY: {
- if( zRight ){
- int n = pPragma->iArg<4 ? sqlite3Strlen30(zRight) : -1;
- if( (pPragma->iArg & 1)==0 ){
- sqlite3_key_v2(db, zDb, zRight, n);
- }else{
- sqlite3_rekey_v2(db, zDb, zRight, n);
- }
- }
- break;
- }
- case PragTyp_HEXKEY: {
- if( zRight ){
- u8 iByte;
- int i;
- char zKey[40];
- for(i=0, iByte=0; i<sizeof(zKey)*2 && sqlite3Isxdigit(zRight[i]); i++){
- iByte = (iByte<<4) + sqlite3HexToInt(zRight[i]);
- if( (i&1)!=0 ) zKey[i/2] = iByte;
- }
- if( (pPragma->iArg & 1)==0 ){
- sqlite3_key_v2(db, zDb, zKey, i/2);
- }else{
- sqlite3_rekey_v2(db, zDb, zKey, i/2);
- }
- }
- break;
- }
-#endif
-#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
+#if defined(SQLITE_ENABLE_CEROD)
case PragTyp_ACTIVATE_EXTENSIONS: if( zRight ){
-#ifdef SQLITE_HAS_CODEC
- if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){
- sqlite3_activate_see(&zRight[4]);
- }
-#endif
-#ifdef SQLITE_ENABLE_CEROD
if( sqlite3StrNICmp(zRight, "cerod-", 6)==0 ){
sqlite3_activate_cerod(&zRight[6]);
}
-#endif
}
break;
#endif
@@ -122374,7 +140323,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
/* The following block is a no-op unless SQLITE_DEBUG is defined. Its only
** purpose is to execute assert() statements to verify that if the
** PragFlg_NoColumns1 flag is set and the caller specified an argument
- ** to the PRAGMA, the implementation has not added any OP_ResultRow
+ ** to the PRAGMA, the implementation has not added any OP_ResultRow
** instructions to the VM. */
if( (pPragma->mPragFlg & PragFlg_NoColumns1) && zRight ){
sqlite3VdbeVerifyNoResultRow(v);
@@ -122405,7 +140354,7 @@ struct PragmaVtabCursor {
char *azArg[2]; /* Value of the argument and schema */
};
-/*
+/*
** Pragma virtual table module xConnect method.
*/
static int pragmaVtabConnect(
@@ -122467,7 +140416,7 @@ static int pragmaVtabConnect(
return rc;
}
-/*
+/*
** Pragma virtual table module xDisconnect method.
*/
static int pragmaVtabDisconnect(sqlite3_vtab *pVtab){
@@ -122565,11 +140514,11 @@ static int pragmaVtabNext(sqlite3_vtab_cursor *pVtabCursor){
return rc;
}
-/*
+/*
** Pragma virtual table module xFilter method.
*/
static int pragmaVtabFilter(
- sqlite3_vtab_cursor *pVtabCursor,
+ sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
@@ -122624,11 +140573,11 @@ static int pragmaVtabEof(sqlite3_vtab_cursor *pVtabCursor){
}
/* The xColumn method simply returns the corresponding column from
-** the PRAGMA.
+** the PRAGMA.
*/
static int pragmaVtabColumn(
- sqlite3_vtab_cursor *pVtabCursor,
- sqlite3_context *ctx,
+ sqlite3_vtab_cursor *pVtabCursor,
+ sqlite3_context *ctx,
int i
){
PragmaVtabCursor *pCsr = (PragmaVtabCursor*)pVtabCursor;
@@ -122641,7 +140590,7 @@ static int pragmaVtabColumn(
return SQLITE_OK;
}
-/*
+/*
** Pragma virtual table module xRowid method.
*/
static int pragmaVtabRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *p){
@@ -122675,7 +140624,8 @@ static const sqlite3_module pragmaVtabModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/*
@@ -122722,7 +140672,7 @@ SQLITE_PRIVATE Module *sqlite3PragmaVtabRegister(sqlite3 *db, const char *zName)
*/
static void corruptSchema(
InitData *pData, /* Initialization context */
- const char *zObj, /* Object being parsed at the point of error */
+ char **azObj, /* Type and name of object being parsed */
const char *zExtra /* Error information */
){
sqlite3 *db = pData->db;
@@ -122730,14 +140680,23 @@ static void corruptSchema(
pData->rc = SQLITE_NOMEM_BKPT;
}else if( pData->pzErrMsg[0]!=0 ){
/* A error message has already been generated. Do not overwrite it */
- }else if( pData->mInitFlags & INITFLAG_AlterTable ){
- *pData->pzErrMsg = sqlite3DbStrDup(db, zExtra);
+ }else if( pData->mInitFlags & (INITFLAG_AlterMask) ){
+ static const char *azAlterType[] = {
+ "rename",
+ "drop column",
+ "add column"
+ };
+ *pData->pzErrMsg = sqlite3MPrintf(db,
+ "error in %s %s after %s: %s", azObj[0], azObj[1],
+ azAlterType[(pData->mInitFlags&INITFLAG_AlterMask)-1],
+ zExtra
+ );
pData->rc = SQLITE_ERROR;
}else if( db->flags & SQLITE_WriteSchema ){
pData->rc = SQLITE_CORRUPT_BKPT;
}else{
char *z;
- if( zObj==0 ) zObj = "?";
+ const char *zObj = azObj[1] ? azObj[1] : "?";
z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
*pData->pzErrMsg = z;
@@ -122746,15 +140705,42 @@ static void corruptSchema(
}
/*
+** Check to see if any sibling index (another index on the same table)
+** of pIndex has the same root page number, and if it does, return true.
+** This would indicate a corrupt schema.
+*/
+SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index *pIndex){
+ Index *p;
+ for(p=pIndex->pTable->pIndex; p; p=p->pNext){
+ if( p->tnum==pIndex->tnum && p!=pIndex ) return 1;
+ }
+ return 0;
+}
+
+/* forward declaration */
+static int sqlite3Prepare(
+ sqlite3 *db, /* Database handle. */
+ const char *zSql, /* UTF-8 encoded SQL statement. */
+ int nBytes, /* Length of zSql in bytes. */
+ u32 prepFlags, /* Zero or more SQLITE_PREPARE_* flags */
+ Vdbe *pReprepare, /* VM being reprepared */
+ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
+ const char **pzTail /* OUT: End of parsed string */
+);
+
+
+/*
** This is the callback routine for the code that initializes the
** database. See sqlite3Init() below for additional information.
** This routine is also called from the OP_ParseSchema opcode of the VDBE.
**
** Each callback contains the following information:
**
-** argv[0] = name of thing being created
-** argv[1] = root page number for table or index. 0 for trigger or view.
-** argv[2] = SQL text for the CREATE statement.
+** argv[0] = type of object: "table", "index", "trigger", or "view".
+** argv[1] = name of thing being created
+** argv[2] = associated table if an index or trigger
+** argv[3] = root page number for table or index. 0 for trigger or view.
+** argv[4] = SQL text for the CREATE statement.
**
*/
SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
@@ -122762,24 +140748,32 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
sqlite3 *db = pData->db;
int iDb = pData->iDb;
- assert( argc==3 );
+ assert( argc==5 );
UNUSED_PARAMETER2(NotUsed, argc);
assert( sqlite3_mutex_held(db->mutex) );
- DbClearProperty(db, iDb, DB_Empty);
+ db->mDbFlags |= DBFLAG_EncodingFixed;
+ if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
+ pData->nInitRow++;
if( db->mallocFailed ){
- corruptSchema(pData, argv[0], 0);
+ corruptSchema(pData, argv, 0);
return 1;
}
assert( iDb>=0 && iDb<db->nDb );
- if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
- if( argv[1]==0 ){
- corruptSchema(pData, argv[0], 0);
- }else if( sqlite3_strnicmp(argv[2],"create ",7)==0 ){
+ if( argv[3]==0 ){
+ corruptSchema(pData, argv, 0);
+ }else if( argv[4]
+ && 'c'==sqlite3UpperToLower[(unsigned char)argv[4][0]]
+ && 'r'==sqlite3UpperToLower[(unsigned char)argv[4][1]] ){
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
** But because db->init.busy is set to 1, no VDBE code is generated
** or executed. All the parser does is build the internal data
** structures that describe the table, index, or view.
+ **
+ ** No other valid SQL statement, other than the variable CREATE statements,
+ ** can begin with the letters "C" and "R". Thus, it is not possible run
+ ** any other kind of statement while parsing the schema, even a corrupt
+ ** schema.
*/
int rc;
u8 saved_iDb = db->init.iDb;
@@ -122788,9 +140782,17 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
assert( db->init.busy );
db->init.iDb = iDb;
- db->init.newTnum = sqlite3Atoi(argv[1]);
+ if( sqlite3GetUInt32(argv[3], &db->init.newTnum)==0
+ || (db->init.newTnum>pData->mxPage && pData->mxPage>0)
+ ){
+ if( sqlite3Config.bExtraSchemaChecks ){
+ corruptSchema(pData, argv, "invalid rootpage");
+ }
+ }
db->init.orphanTrigger = 0;
- TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
+ db->init.azInit = (const char**)argv;
+ pStmt = 0;
+ TESTONLY(rcp = ) sqlite3Prepare(db, argv[4], -1, 0, 0, &pStmt, 0);
rc = db->errCode;
assert( (rc&0xFF)==(rcp&0xFF) );
db->init.iDb = saved_iDb;
@@ -122799,17 +140801,18 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
if( db->init.orphanTrigger ){
assert( iDb==1 );
}else{
- pData->rc = rc;
+ if( rc > pData->rc ) pData->rc = rc;
if( rc==SQLITE_NOMEM ){
sqlite3OomFault(db);
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
- corruptSchema(pData, argv[0], sqlite3_errmsg(db));
+ corruptSchema(pData, argv, sqlite3_errmsg(db));
}
}
}
+ db->init.azInit = sqlite3StdType; /* Any array of string ptrs will do */
sqlite3_finalize(pStmt);
- }else if( argv[0]==0 || (argv[2]!=0 && argv[2][0]!=0) ){
- corruptSchema(pData, argv[0], 0);
+ }else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){
+ corruptSchema(pData, argv, 0);
}else{
/* If the SQL column is blank it means this is an index that
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
@@ -122818,16 +140821,18 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
** to do here is record the root page number for that index.
*/
Index *pIndex;
- pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
+ pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName);
if( pIndex==0 ){
- /* This can occur if there exists an index on a TEMP table which
- ** has the same name as another index on a permanent index. Since
- ** the permanent table is hidden by the TEMP table, we can also
- ** safely ignore the index on the permanent table.
- */
- /* Do Nothing */;
- }else if( sqlite3GetInt32(argv[1], &pIndex->tnum)==0 ){
- corruptSchema(pData, argv[0], "invalid rootpage");
+ corruptSchema(pData, argv, "orphan index");
+ }else
+ if( sqlite3GetUInt32(argv[3],&pIndex->tnum)==0
+ || pIndex->tnum<2
+ || pIndex->tnum>pData->mxPage
+ || sqlite3IndexHasDuplicateRootPage(pIndex)
+ ){
+ if( sqlite3Config.bExtraSchemaChecks ){
+ corruptSchema(pData, argv, "invalid rootpage");
+ }
}
}
return 0;
@@ -122848,11 +140853,12 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
int size;
#endif
Db *pDb;
- char const *azArg[4];
+ char const *azArg[6];
int meta[5];
InitData initData;
- const char *zMasterName;
+ const char *zSchemaTabName;
int openedTransaction = 0;
+ int mask = ((db->mDbFlags & DBFLAG_EncodingFixed) | ~DBFLAG_EncodingFixed);
assert( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 );
assert( iDb>=0 && iDb<db->nDb );
@@ -122862,22 +140868,27 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
db->init.busy = 1;
- /* Construct the in-memory representation schema tables (sqlite_master or
- ** sqlite_temp_master) by invoking the parser directly. The appropriate
+ /* Construct the in-memory representation schema tables (sqlite_schema or
+ ** sqlite_temp_schema) by invoking the parser directly. The appropriate
** table name will be inserted automatically by the parser so we can just
** use the abbreviation "x" here. The parser will also automatically tag
** the schema table as read-only. */
- azArg[0] = zMasterName = SCHEMA_TABLE(iDb);
- azArg[1] = "1";
- azArg[2] = "CREATE TABLE x(type text,name text,tbl_name text,"
+ azArg[0] = "table";
+ azArg[1] = zSchemaTabName = SCHEMA_TABLE(iDb);
+ azArg[2] = azArg[1];
+ azArg[3] = "1";
+ azArg[4] = "CREATE TABLE x(type text,name text,tbl_name text,"
"rootpage int,sql text)";
- azArg[3] = 0;
+ azArg[5] = 0;
initData.db = db;
initData.iDb = iDb;
initData.rc = SQLITE_OK;
initData.pzErrMsg = pzErrMsg;
initData.mInitFlags = mFlags;
- sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
+ initData.nInitRow = 0;
+ initData.mxPage = 0;
+ sqlite3InitCallback(&initData, 5, (char **)azArg, 0);
+ db->mDbFlags &= mask;
if( initData.rc ){
rc = initData.rc;
goto error_out;
@@ -122894,10 +140905,10 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
}
/* If there is not already a read-only (or read-write) transaction opened
- ** on the b-tree database, open one now. If a transaction is opened, it
+ ** on the b-tree database, open one now. If a transaction is opened, it
** will be closed before this function returns. */
sqlite3BtreeEnter(pDb->pBt);
- if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){
+ if( sqlite3BtreeTxnState(pDb->pBt)==SQLITE_TXN_NONE ){
rc = sqlite3BtreeBeginTrans(pDb->pBt, 0, 0);
if( rc!=SQLITE_OK ){
sqlite3SetString(pzErrMsg, db, sqlite3ErrStr(rc));
@@ -122937,27 +140948,32 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
** as sqlite3.enc.
*/
if( meta[BTREE_TEXT_ENCODING-1] ){ /* text encoding */
- if( iDb==0 ){
-#ifndef SQLITE_OMIT_UTF16
+ if( iDb==0 && (db->mDbFlags & DBFLAG_EncodingFixed)==0 ){
u8 encoding;
+#ifndef SQLITE_OMIT_UTF16
/* If opening the main database, set ENC(db). */
encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3;
if( encoding==0 ) encoding = SQLITE_UTF8;
- ENC(db) = encoding;
#else
- ENC(db) = SQLITE_UTF8;
+ encoding = SQLITE_UTF8;
#endif
+ if( db->nVdbeActive>0 && encoding!=ENC(db)
+ && (db->mDbFlags & DBFLAG_Vacuum)==0
+ ){
+ rc = SQLITE_LOCKED;
+ goto initone_error_out;
+ }else{
+ sqlite3SetTextEncoding(db, encoding);
+ }
}else{
/* If opening an attached database, the encoding much match ENC(db) */
- if( meta[BTREE_TEXT_ENCODING-1]!=ENC(db) ){
+ if( (meta[BTREE_TEXT_ENCODING-1] & 3)!=ENC(db) ){
sqlite3SetString(pzErrMsg, db, "attached databases must use the same"
" text encoding as main database");
rc = SQLITE_ERROR;
goto initone_error_out;
}
}
- }else{
- DbSetProperty(db, iDb, DB_Empty);
}
pDb->pSchema->enc = ENC(db);
@@ -122994,17 +141010,18 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
** indices that the user might have created.
*/
if( iDb==0 && meta[BTREE_FILE_FORMAT-1]>=4 ){
- db->flags &= ~SQLITE_LegacyFileFmt;
+ db->flags &= ~(u64)SQLITE_LegacyFileFmt;
}
/* Read the schema information out of the schema tables
*/
assert( db->init.busy );
+ initData.mxPage = sqlite3BtreeLastPage(pDb->pBt);
{
char *zSql;
- zSql = sqlite3MPrintf(db,
- "SELECT name, rootpage, sql FROM \"%w\".%s ORDER BY rowid",
- db->aDb[iDb].zDbSName, zMasterName);
+ zSql = sqlite3MPrintf(db,
+ "SELECT*FROM\"%w\".%s ORDER BY rowid",
+ db->aDb[iDb].zDbSName, zSchemaTabName);
#ifndef SQLITE_OMIT_AUTHORIZATION
{
sqlite3_xauth xAuth;
@@ -123024,18 +141041,22 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
}
#endif
}
+ assert( pDb == &(db->aDb[iDb]) );
if( db->mallocFailed ){
rc = SQLITE_NOMEM_BKPT;
sqlite3ResetAllSchemasOfConnection(db);
- }
- if( rc==SQLITE_OK || (db->flags&SQLITE_NoSchemaError)){
- /* Black magic: If the SQLITE_NoSchemaError flag is set, then consider
- ** the schema loaded, even if errors occurred. In this situation the
- ** current sqlite3_prepare() operation will fail, but the following one
- ** will attempt to compile the supplied statement against whatever subset
- ** of the schema was loaded before the error occurred. The primary
- ** purpose of this is to allow access to the sqlite_master table
- ** even when its contents have been corrupted.
+ pDb = &db->aDb[iDb];
+ }else
+ if( rc==SQLITE_OK || ((db->flags&SQLITE_NoSchemaError) && rc!=SQLITE_NOMEM)){
+ /* Hack: If the SQLITE_NoSchemaError flag is set, then consider
+ ** the schema loaded, even if errors (other than OOM) occurred. In
+ ** this situation the current sqlite3_prepare() operation will fail,
+ ** but the following one will attempt to compile the supplied statement
+ ** against whatever subset of the schema was loaded before the error
+ ** occurred.
+ **
+ ** The primary purpose of this is to allow access to the sqlite_schema
+ ** table even when its contents have been corrupted.
*/
DbSetProperty(db, iDb, DB_SchemaLoaded);
rc = SQLITE_OK;
@@ -123069,13 +141090,12 @@ error_out:
** error occurs, write an error message into *pzErrMsg.
**
** After a database is initialized, the DB_SchemaLoaded bit is set
-** bit is set in the flags field of the Db structure. If the database
-** file was of zero-length, then the DB_Empty flag is also set.
+** bit is set in the flags field of the Db structure.
*/
SQLITE_PRIVATE int sqlite3Init(sqlite3 *db, char **pzErrMsg){
int i, rc;
int commit_internal = !(db->mDbFlags&DBFLAG_SchemaChange);
-
+
assert( sqlite3_mutex_held(db->mutex) );
assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) );
assert( db->init.busy==0 );
@@ -123140,25 +141160,26 @@ static void schemaIsValid(Parse *pParse){
if( pBt==0 ) continue;
/* If there is not already a read-only (or read-write) transaction opened
- ** on the b-tree database, open one now. If a transaction is opened, it
+ ** on the b-tree database, open one now. If a transaction is opened, it
** will be closed immediately after reading the meta-value. */
- if( !sqlite3BtreeIsInReadTrans(pBt) ){
+ if( sqlite3BtreeTxnState(pBt)==SQLITE_TXN_NONE ){
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
sqlite3OomFault(db);
+ pParse->rc = SQLITE_NOMEM;
}
if( rc!=SQLITE_OK ) return;
openedTransaction = 1;
}
- /* Read the schema cookie from the database. If it does not match the
+ /* Read the schema cookie from the database. If it does not match the
** value stored as part of the in-memory schema representation,
** set Parse.rc to SQLITE_SCHEMA. */
sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){
+ if( DbHasProperty(db, iDb, DB_SchemaLoaded) ) pParse->rc = SQLITE_SCHEMA;
sqlite3ResetOneSchema(db, iDb);
- pParse->rc = SQLITE_SCHEMA;
}
/* Close the transaction, if one was opened. */
@@ -123176,17 +141197,18 @@ static void schemaIsValid(Parse *pParse){
** attached database is returned.
*/
SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
- int i = -1000000;
+ int i = -32768;
- /* If pSchema is NULL, then return -1000000. This happens when code in
+ /* If pSchema is NULL, then return -32768. This happens when code in
** expr.c is trying to resolve a reference to a transient table (i.e. one
- ** created by a sub-select). In this case the return value of this
+ ** created by a sub-select). In this case the return value of this
** function should never be used.
**
- ** We return -1000000 instead of the more usual -1 simply because using
- ** -1000000 as the incorrect index into db->aDb[] is much
+ ** We return -32768 instead of the more usual -1 simply because using
+ ** -32768 as the incorrect index into db->aDb[] is much
** more likely to cause a segfault than -1 (of course there are assert()
- ** statements too, but it never hurts to play the odds).
+ ** statements too, but it never hurts to play the odds) and
+ ** -32768 will still fit into a 16-bit signed integer.
*/
assert( sqlite3_mutex_held(db->mutex) );
if( pSchema ){
@@ -123204,18 +141226,108 @@ SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
/*
** Free all memory allocations in the pParse object
*/
-SQLITE_PRIVATE void sqlite3ParserReset(Parse *pParse){
+SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse *pParse){
sqlite3 *db = pParse->db;
- sqlite3DbFree(db, pParse->aLabel);
- sqlite3ExprListDelete(db, pParse->pConstExpr);
- if( db ){
- assert( db->lookaside.bDisable >= pParse->disableLookaside );
- db->lookaside.bDisable -= pParse->disableLookaside;
+ assert( db!=0 );
+ assert( db->pParse==pParse );
+ assert( pParse->nested==0 );
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ if( pParse->aTableLock ) sqlite3DbNNFreeNN(db, pParse->aTableLock);
+#endif
+ while( pParse->pCleanup ){
+ ParseCleanup *pCleanup = pParse->pCleanup;
+ pParse->pCleanup = pCleanup->pNext;
+ pCleanup->xCleanup(db, pCleanup->pPtr);
+ sqlite3DbNNFreeNN(db, pCleanup);
+ }
+ if( pParse->aLabel ) sqlite3DbNNFreeNN(db, pParse->aLabel);
+ if( pParse->pConstExpr ){
+ sqlite3ExprListDelete(db, pParse->pConstExpr);
+ }
+ assert( db->lookaside.bDisable >= pParse->disableLookaside );
+ db->lookaside.bDisable -= pParse->disableLookaside;
+ db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue;
+ assert( pParse->db->pParse==pParse );
+ db->pParse = pParse->pOuterParse;
+}
+
+/*
+** Add a new cleanup operation to a Parser. The cleanup should happen when
+** the parser object is destroyed. But, beware: the cleanup might happen
+** immediately.
+**
+** Use this mechanism for uncommon cleanups. There is a higher setup
+** cost for this mechanism (an extra malloc), so it should not be used
+** for common cleanups that happen on most calls. But for less
+** common cleanups, we save a single NULL-pointer comparison in
+** sqlite3ParseObjectReset(), which reduces the total CPU cycle count.
+**
+** If a memory allocation error occurs, then the cleanup happens immediately.
+** When either SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the
+** pParse->earlyCleanup flag is set in that case. Calling code show verify
+** that test cases exist for which this happens, to guard against possible
+** use-after-free errors following an OOM. The preferred way to do this is
+** to immediately follow the call to this routine with:
+**
+** testcase( pParse->earlyCleanup );
+**
+** This routine returns a copy of its pPtr input (the third parameter)
+** except if an early cleanup occurs, in which case it returns NULL. So
+** another way to check for early cleanup is to check the return value.
+** Or, stop using the pPtr parameter with this call and use only its
+** return value thereafter. Something like this:
+**
+** pObj = sqlite3ParserAddCleanup(pParse, destructor, pObj);
+*/
+SQLITE_PRIVATE void *sqlite3ParserAddCleanup(
+ Parse *pParse, /* Destroy when this Parser finishes */
+ void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */
+ void *pPtr /* Pointer to object to be cleaned up */
+){
+ ParseCleanup *pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup));
+ if( pCleanup ){
+ pCleanup->pNext = pParse->pCleanup;
+ pParse->pCleanup = pCleanup;
+ pCleanup->pPtr = pPtr;
+ pCleanup->xCleanup = xCleanup;
+ }else{
+ xCleanup(pParse->db, pPtr);
+ pPtr = 0;
+#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
+ pParse->earlyCleanup = 1;
+#endif
}
- pParse->disableLookaside = 0;
+ return pPtr;
}
/*
+** Turn bulk memory into a valid Parse object and link that Parse object
+** into database connection db.
+**
+** Call sqlite3ParseObjectReset() to undo this operation.
+**
+** Caution: Do not confuse this routine with sqlite3ParseObjectInit() which
+** is generated by Lemon.
+*/
+SQLITE_PRIVATE void sqlite3ParseObjectInit(Parse *pParse, sqlite3 *db){
+ memset(PARSE_HDR(pParse), 0, PARSE_HDR_SZ);
+ memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ);
+ assert( db->pParse!=pParse );
+ pParse->pOuterParse = db->pParse;
+ db->pParse = pParse;
+ pParse->db = db;
+ if( db->mallocFailed ) sqlite3ErrorMsg(pParse, "out of memory");
+}
+
+/*
+** Maximum number of times that we will try again to prepare a statement
+** that returns SQLITE_ERROR_RETRY.
+*/
+#ifndef SQLITE_MAX_PREPARE_RETRY
+# define SQLITE_MAX_PREPARE_RETRY 25
+#endif
+
+/*
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
*/
static int sqlite3Prepare(
@@ -123227,16 +141339,28 @@ static int sqlite3Prepare(
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
const char **pzTail /* OUT: End of parsed string */
){
- char *zErrMsg = 0; /* Error message */
int rc = SQLITE_OK; /* Result code */
int i; /* Loop counter */
Parse sParse; /* Parsing context */
- memset(&sParse, 0, PARSE_HDR_SZ);
+ /* sqlite3ParseObjectInit(&sParse, db); // inlined for performance */
+ memset(PARSE_HDR(&sParse), 0, PARSE_HDR_SZ);
memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ);
- sParse.pReprepare = pReprepare;
+ sParse.pOuterParse = db->pParse;
+ db->pParse = &sParse;
+ sParse.db = db;
+ if( pReprepare ){
+ sParse.pReprepare = pReprepare;
+ sParse.explain = sqlite3_stmt_isexplain((sqlite3_stmt*)pReprepare);
+ }else{
+ assert( sParse.pReprepare==0 );
+ }
assert( ppStmt && *ppStmt==0 );
- /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */
+ if( db->mallocFailed ){
+ sqlite3ErrorMsg(&sParse, "out of memory");
+ db->errCode = rc = SQLITE_NOMEM;
+ goto end_prepare;
+ }
assert( sqlite3_mutex_held(db->mutex) );
/* For a long-term use prepared statement avoid the use of
@@ -123244,8 +141368,9 @@ static int sqlite3Prepare(
*/
if( prepFlags & SQLITE_PREPARE_PERSISTENT ){
sParse.disableLookaside++;
- db->lookaside.bDisable++;
+ DisableLookaside;
}
+ sParse.prepFlags = prepFlags & 0xff;
/* Check to verify that it is possible to get a read lock on all
** database schemas. The inability to get a read lock indicates that
@@ -123262,31 +141387,34 @@ static int sqlite3Prepare(
** This thread is currently holding mutexes on all Btrees (because
** of the sqlite3BtreeEnterAll() in sqlite3LockAndPrepare()) so it
** is not possible for another thread to start a new schema change
- ** while this routine is running. Hence, we do not need to hold
- ** locks on the schema, we just need to make sure nobody else is
+ ** while this routine is running. Hence, we do not need to hold
+ ** locks on the schema, we just need to make sure nobody else is
** holding them.
**
** Note that setting READ_UNCOMMITTED overrides most lock detection,
** but it does *not* override schema lock detection, so this all still
** works even if READ_UNCOMMITTED is set.
*/
- for(i=0; i<db->nDb; i++) {
- Btree *pBt = db->aDb[i].pBt;
- if( pBt ){
- assert( sqlite3BtreeHoldsMutex(pBt) );
- rc = sqlite3BtreeSchemaLocked(pBt);
- if( rc ){
- const char *zDb = db->aDb[i].zDbSName;
- sqlite3ErrorWithMsg(db, rc, "database schema is locked: %s", zDb);
- testcase( db->flags & SQLITE_ReadUncommit );
- goto end_prepare;
+ if( !db->noSharedCache ){
+ for(i=0; i<db->nDb; i++) {
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt ){
+ assert( sqlite3BtreeHoldsMutex(pBt) );
+ rc = sqlite3BtreeSchemaLocked(pBt);
+ if( rc ){
+ const char *zDb = db->aDb[i].zDbSName;
+ sqlite3ErrorWithMsg(db, rc, "database schema is locked: %s", zDb);
+ testcase( db->flags & SQLITE_ReadUncommit );
+ goto end_prepare;
+ }
}
}
}
- sqlite3VtabUnlockList(db);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( db->pDisconnect ) sqlite3VtabUnlockList(db);
+#endif
- sParse.db = db;
if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){
char *zSqlCopy;
int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
@@ -123299,68 +141427,50 @@ static int sqlite3Prepare(
}
zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes);
if( zSqlCopy ){
- sqlite3RunParser(&sParse, zSqlCopy, &zErrMsg);
+ sqlite3RunParser(&sParse, zSqlCopy);
sParse.zTail = &zSql[sParse.zTail-zSqlCopy];
sqlite3DbFree(db, zSqlCopy);
}else{
sParse.zTail = &zSql[nBytes];
}
}else{
- sqlite3RunParser(&sParse, zSql, &zErrMsg);
+ sqlite3RunParser(&sParse, zSql);
}
assert( 0==sParse.nQueryLoop );
- if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK;
- if( sParse.checkSchema ){
- schemaIsValid(&sParse);
- }
- if( db->mallocFailed ){
- sParse.rc = SQLITE_NOMEM_BKPT;
- }
if( pzTail ){
*pzTail = sParse.zTail;
}
- rc = sParse.rc;
-
-#ifndef SQLITE_OMIT_EXPLAIN
- if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
- static const char * const azColName[] = {
- "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
- "id", "parent", "notused", "detail"
- };
- int iFirst, mx;
- if( sParse.explain==2 ){
- sqlite3VdbeSetNumCols(sParse.pVdbe, 4);
- iFirst = 8;
- mx = 12;
- }else{
- sqlite3VdbeSetNumCols(sParse.pVdbe, 8);
- iFirst = 0;
- mx = 8;
- }
- for(i=iFirst; i<mx; i++){
- sqlite3VdbeSetColName(sParse.pVdbe, i-iFirst, COLNAME_NAME,
- azColName[i], SQLITE_STATIC);
- }
- }
-#endif
if( db->init.busy==0 ){
sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail-zSql), prepFlags);
}
- if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
- sqlite3VdbeFinalize(sParse.pVdbe);
- assert(!(*ppStmt));
+ if( db->mallocFailed ){
+ sParse.rc = SQLITE_NOMEM_BKPT;
+ sParse.checkSchema = 0;
+ }
+ if( sParse.rc!=SQLITE_OK && sParse.rc!=SQLITE_DONE ){
+ if( sParse.checkSchema && db->init.busy==0 ){
+ schemaIsValid(&sParse);
+ }
+ if( sParse.pVdbe ){
+ sqlite3VdbeFinalize(sParse.pVdbe);
+ }
+ assert( 0==(*ppStmt) );
+ rc = sParse.rc;
+ if( sParse.zErrMsg ){
+ sqlite3ErrorWithMsg(db, rc, "%s", sParse.zErrMsg);
+ sqlite3DbFree(db, sParse.zErrMsg);
+ }else{
+ sqlite3Error(db, rc);
+ }
}else{
+ assert( sParse.zErrMsg==0 );
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
+ rc = SQLITE_OK;
+ sqlite3ErrorClear(db);
}
- if( zErrMsg ){
- sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg);
- sqlite3DbFree(db, zErrMsg);
- }else{
- sqlite3Error(db, rc);
- }
/* Delete any TriggerPrg structures allocated while parsing this statement. */
while( sParse.pTriggerPrg ){
@@ -123371,7 +141481,7 @@ static int sqlite3Prepare(
end_prepare:
- sqlite3ParserReset(&sParse);
+ sqlite3ParseObjectReset(&sParse);
return rc;
}
static int sqlite3LockAndPrepare(
@@ -123401,309 +141511,25 @@ static int sqlite3LockAndPrepare(
** reset is considered a permanent error. */
rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
assert( rc==SQLITE_OK || *ppStmt==0 );
- }while( rc==SQLITE_ERROR_RETRY
+ if( rc==SQLITE_OK || db->mallocFailed ) break;
+ }while( (rc==SQLITE_ERROR_RETRY && (cnt++)<SQLITE_MAX_PREPARE_RETRY)
|| (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );
sqlite3BtreeLeaveAll(db);
rc = sqlite3ApiExit(db, rc);
assert( (rc&db->errMask)==rc );
+ db->busyHandler.nBusy = 0;
sqlite3_mutex_leave(db->mutex);
+ assert( rc==SQLITE_OK || (*ppStmt)==0 );
return rc;
}
-#ifdef SQLITE_ENABLE_NORMALIZE
-/*
-** Checks if the specified token is a table, column, or function name,
-** based on the databases associated with the statement being prepared.
-** If the function fails, zero is returned and pRc is filled with the
-** error code.
-*/
-static int shouldTreatAsIdentifier(
- sqlite3 *db, /* Database handle. */
- const char *zToken, /* Pointer to start of token to be checked */
- int nToken, /* Length of token to be checked */
- int *pRc /* Pointer to error code upon failure */
-){
- int bFound = 0; /* Non-zero if token is an identifier name. */
- int i, j; /* Database and column loop indexes. */
- Schema *pSchema; /* Schema for current database. */
- Hash *pHash; /* Hash table of tables for current database. */
- HashElem *e; /* Hash element for hash table iteration. */
- Table *pTab; /* Database table for columns being checked. */
-
- if( sqlite3IsRowidN(zToken, nToken) ){
- return 1;
- }
- if( nToken>0 ){
- int hash = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zToken[0]], nToken);
- if( sqlite3FunctionSearchN(hash, zToken, nToken) ) return 1;
- }
- assert( db!=0 );
- sqlite3_mutex_enter(db->mutex);
- sqlite3BtreeEnterAll(db);
- for(i=0; i<db->nDb; i++){
- pHash = &db->aFunc;
- if( sqlite3HashFindN(pHash, zToken, nToken) ){
- bFound = 1;
- break;
- }
- pSchema = db->aDb[i].pSchema;
- if( pSchema==0 ) continue;
- pHash = &pSchema->tblHash;
- if( sqlite3HashFindN(pHash, zToken, nToken) ){
- bFound = 1;
- break;
- }
- for(e=sqliteHashFirst(pHash); e; e=sqliteHashNext(e)){
- pTab = sqliteHashData(e);
- if( pTab==0 ) continue;
- pHash = pTab->pColHash;
- if( pHash==0 ){
- pTab->pColHash = pHash = sqlite3_malloc(sizeof(Hash));
- if( pHash ){
- sqlite3HashInit(pHash);
- for(j=0; j<pTab->nCol; j++){
- Column *pCol = &pTab->aCol[j];
- sqlite3HashInsert(pHash, pCol->zName, pCol);
- }
- }else{
- *pRc = SQLITE_NOMEM_BKPT;
- bFound = 0;
- goto done;
- }
- }
- if( pHash && sqlite3HashFindN(pHash, zToken, nToken) ){
- bFound = 1;
- goto done;
- }
- }
- }
-done:
- sqlite3BtreeLeaveAll(db);
- sqlite3_mutex_leave(db->mutex);
- return bFound;
-}
-
-/*
-** Attempt to estimate the final output buffer size needed for the fully
-** normalized version of the specified SQL string. This should take into
-** account any potential expansion that could occur (e.g. via IN clauses
-** being expanded, etc). This size returned is the total number of bytes
-** including the NUL terminator.
-*/
-static int estimateNormalizedSize(
- const char *zSql, /* The original SQL string */
- int nSql, /* Length of original SQL string */
- u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */
-){
- int nOut = nSql + 4;
- const char *z = zSql;
- while( nOut<nSql*5 ){
- while( z[0]!=0 && z[0]!='I' && z[0]!='i' ){ z++; }
- if( z[0]==0 ) break;
- z++;
- if( z[0]!='N' && z[0]!='n' ) break;
- z++;
- while( sqlite3Isspace(z[0]) ){ z++; }
- if( z[0]!='(' ) break;
- z++;
- nOut += 5; /* ?,?,? */
- }
- return nOut;
-}
-
-/*
-** Copy the current token into the output buffer while dealing with quoted
-** identifiers. By default, all letters will be converted into lowercase.
-** If the bUpper flag is set, uppercase will be used. The piOut argument
-** will be used to update the target index into the output string.
-*/
-static void copyNormalizedToken(
- const char *zSql, /* The original SQL string */
- int iIn, /* Current index into the original SQL string */
- int nToken, /* Number of bytes in the current token */
- int tokenFlags, /* Flags returned by the tokenizer */
- char *zOut, /* The output string */
- int *piOut /* Pointer to target index into the output string */
-){
- int bQuoted = tokenFlags & SQLITE_TOKEN_QUOTED;
- int bKeyword = tokenFlags & SQLITE_TOKEN_KEYWORD;
- int j = *piOut, k = 0;
- for(; k<nToken; k++){
- if( bQuoted ){
- if( k==0 && iIn>0 ){
- zOut[j++] = '"';
- continue;
- }else if( k==nToken-1 ){
- zOut[j++] = '"';
- continue;
- }
- }
- if( bKeyword ){
- zOut[j++] = sqlite3Toupper(zSql[iIn+k]);
- }else{
- zOut[j++] = sqlite3Tolower(zSql[iIn+k]);
- }
- }
- *piOut = j;
-}
-
-/*
-** Perform normalization of the SQL contained in the prepared statement and
-** store the result in the zNormSql field. The schema for the associated
-** databases are consulted while performing the normalization in order to
-** determine if a token appears to be an identifier. All identifiers are
-** left intact in the normalized SQL and all literals are replaced with a
-** single '?'.
-*/
-SQLITE_PRIVATE void sqlite3Normalize(
- Vdbe *pVdbe, /* VM being reprepared */
- const char *zSql, /* The original SQL string */
- int nSql, /* Size of the input string in bytes */
- u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */
-){
- sqlite3 *db; /* Database handle. */
- char *z; /* The output string */
- int nZ; /* Size of the output string in bytes */
- int i; /* Next character to read from zSql[] */
- int j; /* Next character to fill in on z[] */
- int tokenType = 0; /* Type of the next token */
- int prevTokenType = 0; /* Type of the previous token, except spaces */
- int n; /* Size of the next token */
- int nParen = 0; /* Nesting level of parenthesis */
- Hash inHash; /* Table of parenthesis levels to output index. */
-
- db = sqlite3VdbeDb(pVdbe);
- assert( db!=0 );
- assert( pVdbe->zNormSql==0 );
- if( zSql==0 ) return;
- nZ = estimateNormalizedSize(zSql, nSql, prepFlags);
- z = sqlite3DbMallocRawNN(db, nZ);
- if( z==0 ) return;
- sqlite3HashInit(&inHash);
- for(i=j=0; i<nSql && zSql[i]; i+=n){
- int flags = 0;
- if( tokenType!=TK_SPACE ) prevTokenType = tokenType;
- n = sqlite3GetTokenNormalized((unsigned char*)zSql+i, &tokenType, &flags);
- switch( tokenType ){
- case TK_SPACE: {
- break;
- }
- case TK_ILLEGAL: {
- sqlite3DbFree(db, z);
- sqlite3HashClear(&inHash);
- return;
- }
- case TK_STRING:
- case TK_INTEGER:
- case TK_FLOAT:
- case TK_VARIABLE:
- case TK_BLOB: {
- z[j++] = '?';
- break;
- }
- case TK_LP:
- case TK_RP: {
- if( tokenType==TK_LP ){
- nParen++;
- if( prevTokenType==TK_IN ){
- assert( nParen<nSql );
- sqlite3HashInsert(&inHash, zSql+nParen, SQLITE_INT_TO_PTR(j));
- }
- }else{
- int jj;
- assert( nParen<nSql );
- jj = SQLITE_PTR_TO_INT(sqlite3HashFind(&inHash, zSql+nParen));
- if( jj>0 ){
- sqlite3HashInsert(&inHash, zSql+nParen, 0);
- assert( jj+6<nZ );
- memcpy(z+jj+1, "?,?,?", 5);
- j = jj+6;
- assert( nZ-1-j>=0 );
- assert( nZ-1-j<nZ );
- memset(z+j, 0, nZ-1-j);
- }
- nParen--;
- }
- assert( nParen>=0 );
- /* Fall through */
- }
- case TK_MINUS:
- case TK_SEMI:
- case TK_PLUS:
- case TK_STAR:
- case TK_SLASH:
- case TK_REM:
- case TK_EQ:
- case TK_LE:
- case TK_NE:
- case TK_LSHIFT:
- case TK_LT:
- case TK_RSHIFT:
- case TK_GT:
- case TK_GE:
- case TK_BITOR:
- case TK_CONCAT:
- case TK_COMMA:
- case TK_BITAND:
- case TK_BITNOT:
- case TK_DOT:
- case TK_IN:
- case TK_IS:
- case TK_NOT:
- case TK_NULL:
- case TK_ID: {
- if( tokenType==TK_NULL ){
- if( prevTokenType==TK_IS || prevTokenType==TK_NOT ){
- /* NULL is a keyword in this case, not a literal value */
- }else{
- /* Here the NULL is a literal value */
- z[j++] = '?';
- break;
- }
- }
- if( j>0 && sqlite3IsIdChar(z[j-1]) && sqlite3IsIdChar(zSql[i]) ){
- z[j++] = ' ';
- }
- if( tokenType==TK_ID ){
- int i2 = i, n2 = n, rc = SQLITE_OK;
- if( nParen>0 ){
- assert( nParen<nSql );
- sqlite3HashInsert(&inHash, zSql+nParen, 0);
- }
- if( flags&SQLITE_TOKEN_QUOTED ){ i2++; n2-=2; }
- if( shouldTreatAsIdentifier(db, zSql+i2, n2, &rc)==0 ){
- if( rc!=SQLITE_OK ){
- sqlite3DbFree(db, z);
- sqlite3HashClear(&inHash);
- return;
- }
- if( sqlite3_keyword_check(zSql+i2, n2)==0 ){
- z[j++] = '?';
- break;
- }
- }
- }
- copyNormalizedToken(zSql, i, n, flags, z, &j);
- break;
- }
- }
- }
- assert( j<nZ && "one" );
- while( j>0 && z[j-1]==' ' ){ j--; }
- if( j>0 && z[j-1]!=';' ){ z[j++] = ';'; }
- z[j] = 0;
- assert( j<nZ && "two" );
- pVdbe->zNormSql = z;
- sqlite3HashClear(&inHash);
-}
-#endif /* SQLITE_ENABLE_NORMALIZE */
/*
** Rerun the compilation of a statement after a schema change.
**
** If the statement is successfully recompiled, return SQLITE_OK. Otherwise,
** if the statement cannot be recompiled because another connection has
-** locked the sqlite3_master table, return SQLITE_LOCKED. If any other error
+** locked the sqlite3_schema table, return SQLITE_LOCKED. If any other error
** occurs, return SQLITE_SCHEMA.
*/
SQLITE_PRIVATE int sqlite3Reprepare(Vdbe *p){
@@ -123804,7 +141630,7 @@ SQLITE_API int sqlite3_prepare_v3(
** Compile the UTF-16 encoded SQL statement zSql into a statement handle.
*/
static int sqlite3Prepare16(
- sqlite3 *db, /* Database handle. */
+ sqlite3 *db, /* Database handle. */
const void *zSql, /* UTF-16 encoded SQL statement. */
int nBytes, /* Length of zSql in bytes. */
u32 prepFlags, /* Zero or more SQLITE_PREPARE_* flags */
@@ -123847,7 +141673,7 @@ static int sqlite3Prepare16(
int chars_parsed = sqlite3Utf8CharLen(zSql8, (int)(zTail8-zSql8));
*pzTail = (u8 *)zSql + sqlite3Utf16ByteLen(zSql, chars_parsed);
}
- sqlite3DbFree(db, zSql8);
+ sqlite3DbFree(db, zSql8);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
@@ -123862,7 +141688,7 @@ static int sqlite3Prepare16(
** occurs.
*/
SQLITE_API int sqlite3_prepare16(
- sqlite3 *db, /* Database handle. */
+ sqlite3 *db, /* Database handle. */
const void *zSql, /* UTF-16 encoded SQL statement. */
int nBytes, /* Length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
@@ -123874,7 +141700,7 @@ SQLITE_API int sqlite3_prepare16(
return rc;
}
SQLITE_API int sqlite3_prepare16_v2(
- sqlite3 *db, /* Database handle. */
+ sqlite3 *db, /* Database handle. */
const void *zSql, /* UTF-16 encoded SQL statement. */
int nBytes, /* Length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
@@ -123886,7 +141712,7 @@ SQLITE_API int sqlite3_prepare16_v2(
return rc;
}
SQLITE_API int sqlite3_prepare16_v3(
- sqlite3 *db, /* Database handle. */
+ sqlite3 *db, /* Database handle. */
const void *zSql, /* UTF-16 encoded SQL statement. */
int nBytes, /* Length of zSql in bytes. */
unsigned int prepFlags, /* Zero or more SQLITE_PREPARE_* flags */
@@ -123922,27 +141748,13 @@ SQLITE_API int sqlite3_prepare16_v3(
/* #include "sqliteInt.h" */
/*
-** Trace output macros
-*/
-#if SELECTTRACE_ENABLED
-/***/ int sqlite3SelectTrace = 0;
-# define SELECTTRACE(K,P,S,X) \
- if(sqlite3SelectTrace&(K)) \
- sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\
- sqlite3DebugPrintf X
-#else
-# define SELECTTRACE(K,P,S,X)
-#endif
-
-
-/*
** An instance of the following object is used to record information about
** how to process the DISTINCT keyword, to simplify passing that information
** into the selectInnerLoop() routine.
*/
typedef struct DistinctCtx DistinctCtx;
struct DistinctCtx {
- u8 isTnct; /* True if the DISTINCT keyword is present */
+ u8 isTnct; /* 0: Not distinct. 1: DISTICT 2: DISTINCT and ORDER BY */
u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
int tabTnct; /* Ephemeral table used for DISTINCT processing */
int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
@@ -123986,14 +141798,22 @@ struct SortCtx {
} aDefer[4];
#endif
struct RowLoadInfo *pDeferredRowLoad; /* Deferred row loading info or NULL */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrPush; /* First instruction to push data into sorter */
+ int addrPushEnd; /* Last instruction that pushes data into sorter */
+#endif
};
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
/*
** Delete all the content of a Select structure. Deallocate the structure
-** itself only if bFree is true.
+** itself depending on the value of bFree
+**
+** If bFree==1, call sqlite3DbFree() on the p object.
+** If bFree==0, Leave the first Select object unfreed
*/
static void clearSelect(sqlite3 *db, Select *p, int bFree){
+ assert( db!=0 );
while( p ){
Select *pPrior = p->pPrior;
sqlite3ExprListDelete(db, p->pEList);
@@ -124003,13 +141823,17 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
sqlite3ExprDelete(db, p->pHaving);
sqlite3ExprListDelete(db, p->pOrderBy);
sqlite3ExprDelete(db, p->pLimit);
+ if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith);
#ifndef SQLITE_OMIT_WINDOWFUNC
if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){
sqlite3WindowListDelete(db, p->pWinDefn);
}
+ while( p->pWin ){
+ assert( p->pWin->ppThis==&p->pWin );
+ sqlite3WindowUnlinkFromSelect(p->pWin);
+ }
#endif
- if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith);
- if( bFree ) sqlite3DbFreeNN(db, p);
+ if( bFree ) sqlite3DbNNFreeNN(db, p);
p = pPrior;
bFree = 1;
}
@@ -124021,6 +141845,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){
pDest->eDest = (u8)eDest;
pDest->iSDParm = iParm;
+ pDest->iSDParm2 = 0;
pDest->zAffSdst = 0;
pDest->iSdst = 0;
pDest->nSdst = 0;
@@ -124042,9 +141867,9 @@ SQLITE_PRIVATE Select *sqlite3SelectNew(
u32 selFlags, /* Flag parameters, such as SF_Distinct */
Expr *pLimit /* LIMIT value. NULL means not used */
){
- Select *pNew;
+ Select *pNew, *pAllocated;
Select standin;
- pNew = sqlite3DbMallocRawNN(pParse->db, sizeof(*pNew) );
+ pAllocated = pNew = sqlite3DbMallocRawNN(pParse->db, sizeof(*pNew) );
if( pNew==0 ){
assert( pParse->db->mallocFailed );
pNew = &standin;
@@ -124078,12 +141903,11 @@ SQLITE_PRIVATE Select *sqlite3SelectNew(
#endif
if( pParse->db->mallocFailed ) {
clearSelect(pParse->db, pNew, pNew!=&standin);
- pNew = 0;
+ pAllocated = 0;
}else{
assert( pNew->pSrc!=0 || pParse->nErr>0 );
}
- assert( pNew!=&standin );
- return pNew;
+ return pAllocated;
}
@@ -124093,6 +141917,9 @@ SQLITE_PRIVATE Select *sqlite3SelectNew(
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1);
}
+SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3 *db, void *p){
+ if( ALWAYS(p) ) clearSelect(db, (Select*)p, 1);
+}
/*
** Return a pointer to the right-most SELECT statement in a compound.
@@ -124118,6 +141945,52 @@ static Select *findRightmost(Select *p){
**
** If an illegal or unsupported join type is seen, then still return
** a join type, but put an error in the pParse structure.
+**
+** These are the valid join types:
+**
+**
+** pA pB pC Return Value
+** ------- ----- ----- ------------
+** CROSS - - JT_CROSS
+** INNER - - JT_INNER
+** LEFT - - JT_LEFT|JT_OUTER
+** LEFT OUTER - JT_LEFT|JT_OUTER
+** RIGHT - - JT_RIGHT|JT_OUTER
+** RIGHT OUTER - JT_RIGHT|JT_OUTER
+** FULL - - JT_LEFT|JT_RIGHT|JT_OUTER
+** FULL OUTER - JT_LEFT|JT_RIGHT|JT_OUTER
+** NATURAL INNER - JT_NATURAL|JT_INNER
+** NATURAL LEFT - JT_NATURAL|JT_LEFT|JT_OUTER
+** NATURAL LEFT OUTER JT_NATURAL|JT_LEFT|JT_OUTER
+** NATURAL RIGHT - JT_NATURAL|JT_RIGHT|JT_OUTER
+** NATURAL RIGHT OUTER JT_NATURAL|JT_RIGHT|JT_OUTER
+** NATURAL FULL - JT_NATURAL|JT_LEFT|JT_RIGHT
+** NATURAL FULL OUTER JT_NATRUAL|JT_LEFT|JT_RIGHT
+**
+** To preserve historical compatibly, SQLite also accepts a variety
+** of other non-standard and in many cases nonsensical join types.
+** This routine makes as much sense at it can from the nonsense join
+** type and returns a result. Examples of accepted nonsense join types
+** include but are not limited to:
+**
+** INNER CROSS JOIN -> same as JOIN
+** NATURAL CROSS JOIN -> same as NATURAL JOIN
+** OUTER LEFT JOIN -> same as LEFT JOIN
+** LEFT NATURAL JOIN -> same as NATURAL LEFT JOIN
+** LEFT RIGHT JOIN -> same as FULL JOIN
+** RIGHT OUTER FULL JOIN -> same as FULL JOIN
+** CROSS CROSS CROSS JOIN -> same as JOIN
+**
+** The only restrictions on the join type name are:
+**
+** * "INNER" cannot appear together with "OUTER", "LEFT", "RIGHT",
+** or "FULL".
+**
+** * "CROSS" cannot appear together with "OUTER", "LEFT", "RIGHT,
+** or "FULL".
+**
+** * If "OUTER" is present then there must also be one of
+** "LEFT", "RIGHT", or "FULL"
*/
SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){
int jointype = 0;
@@ -124130,13 +142003,13 @@ SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *p
u8 nChar; /* Length of the keyword in characters */
u8 code; /* Join type mask */
} aKeyword[] = {
- /* natural */ { 0, 7, JT_NATURAL },
- /* left */ { 6, 4, JT_LEFT|JT_OUTER },
- /* outer */ { 10, 5, JT_OUTER },
- /* right */ { 14, 5, JT_RIGHT|JT_OUTER },
- /* full */ { 19, 4, JT_LEFT|JT_RIGHT|JT_OUTER },
- /* inner */ { 23, 5, JT_INNER },
- /* cross */ { 28, 5, JT_INNER|JT_CROSS },
+ /* (0) natural */ { 0, 7, JT_NATURAL },
+ /* (1) left */ { 6, 4, JT_LEFT|JT_OUTER },
+ /* (2) outer */ { 10, 5, JT_OUTER },
+ /* (3) right */ { 14, 5, JT_RIGHT|JT_OUTER },
+ /* (4) full */ { 19, 4, JT_LEFT|JT_RIGHT|JT_OUTER },
+ /* (5) inner */ { 23, 5, JT_INNER },
+ /* (6) cross */ { 28, 5, JT_INNER|JT_CROSS },
};
int i, j;
apAll[0] = pA;
@@ -124145,7 +142018,7 @@ SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *p
for(i=0; i<3 && apAll[i]; i++){
p = apAll[i];
for(j=0; j<ArraySize(aKeyword); j++){
- if( p->n==aKeyword[j].nChar
+ if( p->n==aKeyword[j].nChar
&& sqlite3StrNICmp((char*)p->z, &zKeyText[aKeyword[j].i], p->n)==0 ){
jointype |= aKeyword[j].code;
break;
@@ -124159,18 +142032,15 @@ SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *p
}
if(
(jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) ||
- (jointype & JT_ERROR)!=0
+ (jointype & JT_ERROR)!=0 ||
+ (jointype & (JT_OUTER|JT_LEFT|JT_RIGHT))==JT_OUTER
){
- const char *zSp = " ";
- assert( pB!=0 );
- if( pC==0 ){ zSp++; }
- sqlite3ErrorMsg(pParse, "unknown or unsupported join type: "
- "%T %T%s%T", pA, pB, zSp, pC);
- jointype = JT_INNER;
- }else if( (jointype & JT_OUTER)!=0
- && (jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ){
- sqlite3ErrorMsg(pParse,
- "RIGHT and FULL OUTER JOINs are not currently supported");
+ const char *zSp1 = " ";
+ const char *zSp2 = " ";
+ if( pB==0 ){ zSp1++; }
+ if( pC==0 ){ zSp2++; }
+ sqlite3ErrorMsg(pParse, "unknown join type: "
+ "%T%s%T%s%T", pA, zSp1, pB, zSp2, pC);
jointype = JT_INNER;
}
return jointype;
@@ -124180,17 +142050,36 @@ SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *p
** Return the index of a column in a table. Return -1 if the column
** is not contained in the table.
*/
-static int columnIndex(Table *pTab, const char *zCol){
+SQLITE_PRIVATE int sqlite3ColumnIndex(Table *pTab, const char *zCol){
int i;
- for(i=0; i<pTab->nCol; i++){
- if( sqlite3StrICmp(pTab->aCol[i].zName, zCol)==0 ) return i;
+ u8 h = sqlite3StrIHash(zCol);
+ Column *pCol;
+ for(pCol=pTab->aCol, i=0; i<pTab->nCol; pCol++, i++){
+ if( pCol->hName==h && sqlite3StrICmp(pCol->zCnName, zCol)==0 ) return i;
}
return -1;
}
/*
-** Search the first N tables in pSrc, from left to right, looking for a
-** table that has a column named zCol.
+** Mark a subquery result column as having been used.
+*/
+SQLITE_PRIVATE void sqlite3SrcItemColumnUsed(SrcItem *pItem, int iCol){
+ assert( pItem!=0 );
+ assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) );
+ if( pItem->fg.isNestedFrom ){
+ ExprList *pResults;
+ assert( pItem->pSelect!=0 );
+ pResults = pItem->pSelect->pEList;
+ assert( pResults!=0 );
+ assert( iCol>=0 && iCol<pResults->nExpr );
+ pResults->a[iCol].fg.bUsed = 1;
+ }
+}
+
+/*
+** Search the tables iStart..iEnd (inclusive) in pSrc, looking for a
+** table that has a column named zCol. The search is left-to-right.
+** The first match found is returned.
**
** When found, set *piTab and *piCol to the table index and column index
** of the matching column and return TRUE.
@@ -124199,19 +142088,27 @@ static int columnIndex(Table *pTab, const char *zCol){
*/
static int tableAndColumnIndex(
SrcList *pSrc, /* Array of tables to search */
- int N, /* Number of tables in pSrc->a[] to search */
+ int iStart, /* First member of pSrc->a[] to check */
+ int iEnd, /* Last member of pSrc->a[] to check */
const char *zCol, /* Name of the column we are looking for */
int *piTab, /* Write index of pSrc->a[] here */
- int *piCol /* Write index of pSrc->a[*piTab].pTab->aCol[] here */
+ int *piCol, /* Write index of pSrc->a[*piTab].pTab->aCol[] here */
+ int bIgnoreHidden /* Ignore hidden columns */
){
int i; /* For looping over tables in pSrc */
int iCol; /* Index of column matching zCol */
+ assert( iEnd<pSrc->nSrc );
+ assert( iStart>=0 );
assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */
- for(i=0; i<N; i++){
- iCol = columnIndex(pSrc->a[i].pTab, zCol);
- if( iCol>=0 ){
+
+ for(i=iStart; i<=iEnd; i++){
+ iCol = sqlite3ColumnIndex(pSrc->a[i].pTab, zCol);
+ if( iCol>=0
+ && (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0)
+ ){
if( piTab ){
+ sqlite3SrcItemColumnUsed(&pSrc->a[i], iCol);
*piTab = i;
*piCol = iCol;
}
@@ -124222,63 +142119,19 @@ static int tableAndColumnIndex(
}
/*
-** This function is used to add terms implied by JOIN syntax to the
-** WHERE clause expression of a SELECT statement. The new term, which
-** is ANDed with the existing WHERE clause, is of the form:
-**
-** (tab1.col1 = tab2.col2)
-**
-** where tab1 is the iSrc'th table in SrcList pSrc and tab2 is the
-** (iSrc+1)'th. Column col1 is column iColLeft of tab1, and col2 is
-** column iColRight of tab2.
-*/
-static void addWhereTerm(
- Parse *pParse, /* Parsing context */
- SrcList *pSrc, /* List of tables in FROM clause */
- int iLeft, /* Index of first table to join in pSrc */
- int iColLeft, /* Index of column in first table */
- int iRight, /* Index of second table in pSrc */
- int iColRight, /* Index of column in second table */
- int isOuterJoin, /* True if this is an OUTER join */
- Expr **ppWhere /* IN/OUT: The WHERE clause to add to */
-){
- sqlite3 *db = pParse->db;
- Expr *pE1;
- Expr *pE2;
- Expr *pEq;
-
- assert( iLeft<iRight );
- assert( pSrc->nSrc>iRight );
- assert( pSrc->a[iLeft].pTab );
- assert( pSrc->a[iRight].pTab );
-
- pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft);
- pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight);
-
- pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2);
- if( pEq && isOuterJoin ){
- ExprSetProperty(pEq, EP_FromJoin);
- assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
- ExprSetVVAProperty(pEq, EP_NoReduce);
- pEq->iRightJoinTable = (i16)pE2->iTable;
- }
- *ppWhere = sqlite3ExprAnd(db, *ppWhere, pEq);
-}
-
-/*
-** Set the EP_FromJoin property on all terms of the given expression.
-** And set the Expr.iRightJoinTable to iTable for every term in the
+** Set the EP_OuterON property on all terms of the given expression.
+** And set the Expr.w.iJoin to iTable for every term in the
** expression.
**
-** The EP_FromJoin property is used on terms of an expression to tell
-** the LEFT OUTER JOIN processing logic that this term is part of the
+** The EP_OuterON property is used on terms of an expression to tell
+** the OUTER JOIN processing logic that this term is part of the
** join restriction specified in the ON or USING clause and not a part
** of the more general WHERE clause. These terms are moved over to the
** WHERE clause during join processing but we need to remember that they
** originated in the ON or USING clause.
**
-** The Expr.iRightJoinTable tells the WHERE clause processing that the
-** expression depends on table iRightJoinTable even if that table is not
+** The Expr.w.iJoin tells the WHERE clause processing that the
+** expression depends on table w.iJoin even if that table is not
** explicitly mentioned in the expression. That information is needed
** for cases like this:
**
@@ -124291,143 +142144,223 @@ static void addWhereTerm(
** after the t1 loop and rows with t1.x!=5 will never appear in
** the output, which is incorrect.
*/
-static void setJoinExpr(Expr *p, int iTable){
+SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){
+ assert( joinFlag==EP_OuterON || joinFlag==EP_InnerON );
while( p ){
- ExprSetProperty(p, EP_FromJoin);
+ ExprSetProperty(p, joinFlag);
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(p, EP_NoReduce);
- p->iRightJoinTable = (i16)iTable;
- if( p->op==TK_FUNCTION && p->x.pList ){
- int i;
- for(i=0; i<p->x.pList->nExpr; i++){
- setJoinExpr(p->x.pList->a[i].pExpr, iTable);
+ p->w.iJoin = iTable;
+ if( p->op==TK_FUNCTION ){
+ assert( ExprUseXList(p) );
+ if( p->x.pList ){
+ int i;
+ for(i=0; i<p->x.pList->nExpr; i++){
+ sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag);
+ }
}
}
- setJoinExpr(p->pLeft, iTable);
+ sqlite3SetJoinExpr(p->pLeft, iTable, joinFlag);
p = p->pRight;
- }
+ }
}
-/* Undo the work of setJoinExpr(). In the expression tree p, convert every
-** term that is marked with EP_FromJoin and iRightJoinTable==iTable into
-** an ordinary term that omits the EP_FromJoin mark.
+/* Undo the work of sqlite3SetJoinExpr(). This is used when a LEFT JOIN
+** is simplified into an ordinary JOIN, and when an ON expression is
+** "pushed down" into the WHERE clause of a subquery.
+**
+** Convert every term that is marked with EP_OuterON and w.iJoin==iTable into
+** an ordinary term that omits the EP_OuterON mark. Or if iTable<0, then
+** just clear every EP_OuterON and EP_InnerON mark from the expression tree.
**
-** This happens when a LEFT JOIN is simplified into an ordinary JOIN.
+** If nullable is true, that means that Expr p might evaluate to NULL even
+** if it is a reference to a NOT NULL column. This can happen, for example,
+** if the table that p references is on the left side of a RIGHT JOIN.
+** If nullable is true, then take care to not remove the EP_CanBeNull bit.
+** See forum thread https://sqlite.org/forum/forumpost/b40696f50145d21c
*/
-static void unsetJoinExpr(Expr *p, int iTable){
+static void unsetJoinExpr(Expr *p, int iTable, int nullable){
while( p ){
- if( ExprHasProperty(p, EP_FromJoin)
- && (iTable<0 || p->iRightJoinTable==iTable) ){
- ExprClearProperty(p, EP_FromJoin);
+ if( iTable<0 || (ExprHasProperty(p, EP_OuterON) && p->w.iJoin==iTable) ){
+ ExprClearProperty(p, EP_OuterON|EP_InnerON);
+ if( iTable>=0 ) ExprSetProperty(p, EP_InnerON);
}
- if( p->op==TK_FUNCTION && p->x.pList ){
- int i;
- for(i=0; i<p->x.pList->nExpr; i++){
- unsetJoinExpr(p->x.pList->a[i].pExpr, iTable);
+ if( p->op==TK_COLUMN && p->iTable==iTable && !nullable ){
+ ExprClearProperty(p, EP_CanBeNull);
+ }
+ if( p->op==TK_FUNCTION ){
+ assert( ExprUseXList(p) );
+ assert( p->pLeft==0 );
+ if( p->x.pList ){
+ int i;
+ for(i=0; i<p->x.pList->nExpr; i++){
+ unsetJoinExpr(p->x.pList->a[i].pExpr, iTable, nullable);
+ }
}
}
- unsetJoinExpr(p->pLeft, iTable);
+ unsetJoinExpr(p->pLeft, iTable, nullable);
p = p->pRight;
- }
+ }
}
/*
** This routine processes the join information for a SELECT statement.
-** ON and USING clauses are converted into extra terms of the WHERE clause.
-** NATURAL joins also create extra WHERE clause terms.
+**
+** * A NATURAL join is converted into a USING join. After that, we
+** do not need to be concerned with NATURAL joins and we only have
+** think about USING joins.
+**
+** * ON and USING clauses result in extra terms being added to the
+** WHERE clause to enforce the specified constraints. The extra
+** WHERE clause terms will be tagged with EP_OuterON or
+** EP_InnerON so that we know that they originated in ON/USING.
**
** The terms of a FROM clause are contained in the Select.pSrc structure.
** The left most table is the first entry in Select.pSrc. The right-most
** table is the last entry. The join operator is held in the entry to
-** the left. Thus entry 0 contains the join operator for the join between
+** the right. Thus entry 1 contains the join operator for the join between
** entries 0 and 1. Any ON or USING clauses associated with the join are
-** also attached to the left entry.
+** also attached to the right entry.
**
** This routine returns the number of errors encountered.
*/
-static int sqliteProcessJoin(Parse *pParse, Select *p){
+static int sqlite3ProcessJoin(Parse *pParse, Select *p){
SrcList *pSrc; /* All tables in the FROM clause */
int i, j; /* Loop counters */
- struct SrcList_item *pLeft; /* Left table being joined */
- struct SrcList_item *pRight; /* Right table being joined */
+ SrcItem *pLeft; /* Left table being joined */
+ SrcItem *pRight; /* Right table being joined */
pSrc = p->pSrc;
pLeft = &pSrc->a[0];
pRight = &pLeft[1];
for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){
Table *pRightTab = pRight->pTab;
- int isOuter;
+ u32 joinType;
if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue;
- isOuter = (pRight->fg.jointype & JT_OUTER)!=0;
+ joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_OuterON : EP_InnerON;
- /* When the NATURAL keyword is present, add WHERE clause terms for
- ** every column that the two tables have in common.
+ /* If this is a NATURAL join, synthesize an appropriate USING clause
+ ** to specify which columns should be joined.
*/
if( pRight->fg.jointype & JT_NATURAL ){
- if( pRight->pOn || pRight->pUsing ){
+ IdList *pUsing = 0;
+ if( pRight->fg.isUsing || pRight->u3.pOn ){
sqlite3ErrorMsg(pParse, "a NATURAL join may not have "
"an ON or USING clause", 0);
return 1;
}
for(j=0; j<pRightTab->nCol; j++){
char *zName; /* Name of column in the right table */
- int iLeft; /* Matching left table */
- int iLeftCol; /* Matching column in the left table */
- zName = pRightTab->aCol[j].zName;
- if( tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol) ){
- addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, j,
- isOuter, &p->pWhere);
+ if( IsHiddenColumn(&pRightTab->aCol[j]) ) continue;
+ zName = pRightTab->aCol[j].zCnName;
+ if( tableAndColumnIndex(pSrc, 0, i, zName, 0, 0, 1) ){
+ pUsing = sqlite3IdListAppend(pParse, pUsing, 0);
+ if( pUsing ){
+ assert( pUsing->nId>0 );
+ assert( pUsing->a[pUsing->nId-1].zName==0 );
+ pUsing->a[pUsing->nId-1].zName = sqlite3DbStrDup(pParse->db, zName);
+ }
}
}
- }
-
- /* Disallow both ON and USING clauses in the same join
- */
- if( pRight->pOn && pRight->pUsing ){
- sqlite3ErrorMsg(pParse, "cannot have both ON and USING "
- "clauses in the same join");
- return 1;
- }
-
- /* Add the ON clause to the end of the WHERE clause, connected by
- ** an AND operator.
- */
- if( pRight->pOn ){
- if( isOuter ) setJoinExpr(pRight->pOn, pRight->iCursor);
- p->pWhere = sqlite3ExprAnd(pParse->db, p->pWhere, pRight->pOn);
- pRight->pOn = 0;
+ if( pUsing ){
+ pRight->fg.isUsing = 1;
+ pRight->fg.isSynthUsing = 1;
+ pRight->u3.pUsing = pUsing;
+ }
+ if( pParse->nErr ) return 1;
}
/* Create extra terms on the WHERE clause for each column named
- ** in the USING clause. Example: If the two tables to be joined are
+ ** in the USING clause. Example: If the two tables to be joined are
** A and B and the USING clause names X, Y, and Z, then add this
** to the WHERE clause: A.X=B.X AND A.Y=B.Y AND A.Z=B.Z
** Report an error if any column mentioned in the USING clause is
** not contained in both tables to be joined.
*/
- if( pRight->pUsing ){
- IdList *pList = pRight->pUsing;
+ if( pRight->fg.isUsing ){
+ IdList *pList = pRight->u3.pUsing;
+ sqlite3 *db = pParse->db;
+ assert( pList!=0 );
for(j=0; j<pList->nId; j++){
char *zName; /* Name of the term in the USING clause */
int iLeft; /* Table on the left with matching column name */
int iLeftCol; /* Column number of matching column on the left */
int iRightCol; /* Column number of matching column on the right */
+ Expr *pE1; /* Reference to the column on the LEFT of the join */
+ Expr *pE2; /* Reference to the column on the RIGHT of the join */
+ Expr *pEq; /* Equality constraint. pE1 == pE2 */
zName = pList->a[j].zName;
- iRightCol = columnIndex(pRightTab, zName);
+ iRightCol = sqlite3ColumnIndex(pRightTab, zName);
if( iRightCol<0
- || !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol)
+ || tableAndColumnIndex(pSrc, 0, i, zName, &iLeft, &iLeftCol,
+ pRight->fg.isSynthUsing)==0
){
sqlite3ErrorMsg(pParse, "cannot join using column %s - column "
"not present in both tables", zName);
return 1;
}
- addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, iRightCol,
- isOuter, &p->pWhere);
+ pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol);
+ sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol);
+ if( (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
+ /* This branch runs if the query contains one or more RIGHT or FULL
+ ** JOINs. If only a single table on the left side of this join
+ ** contains the zName column, then this branch is a no-op.
+ ** But if there are two or more tables on the left side
+ ** of the join, construct a coalesce() function that gathers all
+ ** such tables. Raise an error if more than one of those references
+ ** to zName is not also within a prior USING clause.
+ **
+ ** We really ought to raise an error if there are two or more
+ ** non-USING references to zName on the left of an INNER or LEFT
+ ** JOIN. But older versions of SQLite do not do that, so we avoid
+ ** adding a new error so as to not break legacy applications.
+ */
+ ExprList *pFuncArgs = 0; /* Arguments to the coalesce() */
+ static const Token tkCoalesce = { "coalesce", 8 };
+ while( tableAndColumnIndex(pSrc, iLeft+1, i, zName, &iLeft, &iLeftCol,
+ pRight->fg.isSynthUsing)!=0 ){
+ if( pSrc->a[iLeft].fg.isUsing==0
+ || sqlite3IdListIndex(pSrc->a[iLeft].u3.pUsing, zName)<0
+ ){
+ sqlite3ErrorMsg(pParse, "ambiguous reference to %s in USING()",
+ zName);
+ break;
+ }
+ pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1);
+ pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol);
+ sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol);
+ }
+ if( pFuncArgs ){
+ pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1);
+ pE1 = sqlite3ExprFunction(pParse, pFuncArgs, &tkCoalesce, 0);
+ }
+ }
+ pE2 = sqlite3CreateColumnExpr(db, pSrc, i+1, iRightCol);
+ sqlite3SrcItemColumnUsed(pRight, iRightCol);
+ pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2);
+ assert( pE2!=0 || pEq==0 );
+ if( pEq ){
+ ExprSetProperty(pEq, joinType);
+ assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
+ ExprSetVVAProperty(pEq, EP_NoReduce);
+ pEq->w.iJoin = pE2->iTable;
+ }
+ p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pEq);
}
}
+
+ /* Add the ON clause to the end of the WHERE clause, connected by
+ ** an AND operator.
+ */
+ else if( pRight->u3.pOn ){
+ sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType);
+ p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn);
+ pRight->u3.pOn = 0;
+ pRight->fg.isOn = 1;
+ }
}
return 0;
}
@@ -124521,14 +142454,18 @@ static void pushOntoSorter(
** (2) All output columns are included in the sort record. In that
** case regData==regOrigData.
** (3) Some output columns are omitted from the sort record due to
- ** the SQLITE_ENABLE_SORTER_REFERENCE optimization, or due to the
- ** SQLITE_ECEL_OMITREF optimization, or due to the
- ** SortCtx.pDeferredRowLoad optimiation. In any of these cases
+ ** the SQLITE_ENABLE_SORTER_REFERENCES optimization, or due to the
+ ** SQLITE_ECEL_OMITREF optimization, or due to the
+ ** SortCtx.pDeferredRowLoad optimization. In any of these cases
** regOrigData is 0 to prevent this routine from trying to copy
** values that might not yet exist.
*/
assert( nData==1 || regData==regOrigData || regOrigData==0 );
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ pSort->addrPush = sqlite3VdbeCurrentAddr(v);
+#endif
+
if( nPrefixReg ){
assert( nPrefixReg==nExpr+bSeq );
regBase = regData - nPrefixReg;
@@ -124538,7 +142475,7 @@ static void pushOntoSorter(
}
assert( pSelect->iOffset==0 || pSelect->iLimit!=0 );
iLimit = pSelect->iOffset ? pSelect->iOffset+1 : pSelect->iLimit;
- pSort->labelDone = sqlite3VdbeMakeLabel(v);
+ pSort->labelDone = sqlite3VdbeMakeLabel(pParse);
sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, regOrigData,
SQLITE_ECEL_DUP | (regOrigData? SQLITE_ECEL_REF : 0));
if( bSeq ){
@@ -124560,7 +142497,7 @@ static void pushOntoSorter(
pParse->nMem += pSort->nOBSat;
nKey = nExpr - pSort->nOBSat + bSeq;
if( bSeq ){
- addrFirst = sqlite3VdbeAddOp1(v, OP_IfNot, regBase+nExpr);
+ addrFirst = sqlite3VdbeAddOp1(v, OP_IfNot, regBase+nExpr);
}else{
addrFirst = sqlite3VdbeAddOp1(v, OP_SequenceTest, pSort->iECursor);
}
@@ -124570,14 +142507,15 @@ static void pushOntoSorter(
if( pParse->db->mallocFailed ) return;
pOp->p2 = nKey + nData;
pKI = pOp->p4.pKeyInfo;
- memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */
+ memset(pKI->aSortFlags, 0, pKI->nKeyField); /* Makes OP_Jump testable */
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
testcase( pKI->nAllField > pKI->nKeyField+2 );
pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat,
pKI->nAllField-pKI->nKeyField-1);
+ pOp = 0; /* Ensure pOp not used after sqlite3VdbeAddOp3() */
addrJmp = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v);
- pSort->labelBkOut = sqlite3VdbeMakeLabel(v);
+ pSort->labelBkOut = sqlite3VdbeMakeLabel(pParse);
pSort->regReturn = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor);
@@ -124593,10 +142531,10 @@ static void pushOntoSorter(
/* At this point the values for the new sorter entry are stored
** in an array of registers. They need to be composed into a record
** and inserted into the sorter if either (a) there are currently
- ** less than LIMIT+OFFSET items or (b) the new record is smaller than
+ ** less than LIMIT+OFFSET items or (b) the new record is smaller than
** the largest record currently in the sorter. If (b) is true and there
** are already LIMIT+OFFSET items in the sorter, delete the largest
- ** entry before inserting the new one. This way there are never more
+ ** entry before inserting the new one. This way there are never more
** than LIMIT+OFFSET items in the sorter.
**
** If the new record does not need to be inserted into the sorter,
@@ -124628,6 +142566,9 @@ static void pushOntoSorter(
sqlite3VdbeChangeP2(v, iSkip,
pSort->labelOBLopt ? pSort->labelOBLopt : sqlite3VdbeCurrentAddr(v));
}
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ pSort->addrPushEnd = sqlite3VdbeCurrentAddr(v)-1;
+#endif
}
/*
@@ -124645,38 +142586,164 @@ static void codeOffset(
}
/*
-** Add code that will check to make sure the N registers starting at iMem
-** form a distinct entry. iTab is a sorting index that holds previously
-** seen combinations of the N values. A new entry is made in iTab
-** if the current N values are new.
+** Add code that will check to make sure the array of registers starting at
+** iMem form a distinct entry. This is used by both "SELECT DISTINCT ..." and
+** distinct aggregates ("SELECT count(DISTINCT <expr>) ..."). Three strategies
+** are available. Which is used depends on the value of parameter eTnctType,
+** as follows:
**
-** A jump to addrRepeat is made and the N+1 values are popped from the
-** stack if the top N elements are not distinct.
-*/
-static void codeDistinct(
+** WHERE_DISTINCT_UNORDERED/WHERE_DISTINCT_NOOP:
+** Build an ephemeral table that contains all entries seen before and
+** skip entries which have been seen before.
+**
+** Parameter iTab is the cursor number of an ephemeral table that must
+** be opened before the VM code generated by this routine is executed.
+** The ephemeral cursor table is queried for a record identical to the
+** record formed by the current array of registers. If one is found,
+** jump to VM address addrRepeat. Otherwise, insert a new record into
+** the ephemeral cursor and proceed.
+**
+** The returned value in this case is a copy of parameter iTab.
+**
+** WHERE_DISTINCT_ORDERED:
+** In this case rows are being delivered sorted order. The ephemeral
+** table is not required. Instead, the current set of values
+** is compared against previous row. If they match, the new row
+** is not distinct and control jumps to VM address addrRepeat. Otherwise,
+** the VM program proceeds with processing the new row.
+**
+** The returned value in this case is the register number of the first
+** in an array of registers used to store the previous result row so that
+** it can be compared to the next. The caller must ensure that this
+** register is initialized to NULL. (The fixDistinctOpenEph() routine
+** will take care of this initialization.)
+**
+** WHERE_DISTINCT_UNIQUE:
+** In this case it has already been determined that the rows are distinct.
+** No special action is required. The return value is zero.
+**
+** Parameter pEList is the list of expressions used to generated the
+** contents of each row. It is used by this routine to determine (a)
+** how many elements there are in the array of registers and (b) the
+** collation sequences that should be used for the comparisons if
+** eTnctType is WHERE_DISTINCT_ORDERED.
+*/
+static int codeDistinct(
Parse *pParse, /* Parsing and code generating context */
+ int eTnctType, /* WHERE_DISTINCT_* value */
int iTab, /* A sorting index used to test for distinctness */
int addrRepeat, /* Jump to here if not distinct */
- int N, /* Number of elements */
- int iMem /* First element */
+ ExprList *pEList, /* Expression for each element */
+ int regElem /* First element */
){
- Vdbe *v;
- int r1;
+ int iRet = 0;
+ int nResultCol = pEList->nExpr;
+ Vdbe *v = pParse->pVdbe;
- v = pParse->pVdbe;
- r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1);
- sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r1, iMem, N);
- sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
- sqlite3ReleaseTempReg(pParse, r1);
+ switch( eTnctType ){
+ case WHERE_DISTINCT_ORDERED: {
+ int i;
+ int iJump; /* Jump destination */
+ int regPrev; /* Previous row content */
+
+ /* Allocate space for the previous row */
+ iRet = regPrev = pParse->nMem+1;
+ pParse->nMem += nResultCol;
+
+ iJump = sqlite3VdbeCurrentAddr(v) + nResultCol;
+ for(i=0; i<nResultCol; i++){
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pEList->a[i].pExpr);
+ if( i<nResultCol-1 ){
+ sqlite3VdbeAddOp3(v, OP_Ne, regElem+i, iJump, regPrev+i);
+ VdbeCoverage(v);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Eq, regElem+i, addrRepeat, regPrev+i);
+ VdbeCoverage(v);
+ }
+ sqlite3VdbeChangeP4(v, -1, (const char *)pColl, P4_COLLSEQ);
+ sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+ }
+ assert( sqlite3VdbeCurrentAddr(v)==iJump || pParse->db->mallocFailed );
+ sqlite3VdbeAddOp3(v, OP_Copy, regElem, regPrev, nResultCol-1);
+ break;
+ }
+
+ case WHERE_DISTINCT_UNIQUE: {
+ /* nothing to do */
+ break;
+ }
+
+ default: {
+ int r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, regElem, nResultCol);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regElem, nResultCol, r1);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r1, regElem, nResultCol);
+ sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+ sqlite3ReleaseTempReg(pParse, r1);
+ iRet = iTab;
+ break;
+ }
+ }
+
+ return iRet;
+}
+
+/*
+** This routine runs after codeDistinct(). It makes necessary
+** adjustments to the OP_OpenEphemeral opcode that the codeDistinct()
+** routine made use of. This processing must be done separately since
+** sometimes codeDistinct is called before the OP_OpenEphemeral is actually
+** laid down.
+**
+** WHERE_DISTINCT_NOOP:
+** WHERE_DISTINCT_UNORDERED:
+**
+** No adjustments necessary. This function is a no-op.
+**
+** WHERE_DISTINCT_UNIQUE:
+**
+** The ephemeral table is not needed. So change the
+** OP_OpenEphemeral opcode into an OP_Noop.
+**
+** WHERE_DISTINCT_ORDERED:
+**
+** The ephemeral table is not needed. But we do need register
+** iVal to be initialized to NULL. So change the OP_OpenEphemeral
+** into an OP_Null on the iVal register.
+*/
+static void fixDistinctOpenEph(
+ Parse *pParse, /* Parsing and code generating context */
+ int eTnctType, /* WHERE_DISTINCT_* value */
+ int iVal, /* Value returned by codeDistinct() */
+ int iOpenEphAddr /* Address of OP_OpenEphemeral instruction for iTab */
+){
+ if( pParse->nErr==0
+ && (eTnctType==WHERE_DISTINCT_UNIQUE || eTnctType==WHERE_DISTINCT_ORDERED)
+ ){
+ Vdbe *v = pParse->pVdbe;
+ sqlite3VdbeChangeToNoop(v, iOpenEphAddr);
+ if( sqlite3VdbeGetOp(v, iOpenEphAddr+1)->opcode==OP_Explain ){
+ sqlite3VdbeChangeToNoop(v, iOpenEphAddr+1);
+ }
+ if( eTnctType==WHERE_DISTINCT_ORDERED ){
+ /* Change the OP_OpenEphemeral to an OP_Null that sets the MEM_Cleared
+ ** bit on the first register of the previous value. This will cause the
+ ** OP_Ne added in codeDistinct() to always fail on the first iteration of
+ ** the loop even if the first row is all NULLs. */
+ VdbeOp *pOp = sqlite3VdbeGetOp(v, iOpenEphAddr);
+ pOp->opcode = OP_Null;
+ pOp->p1 = 1;
+ pOp->p2 = iVal;
+ }
+ }
}
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
/*
** This function is called as part of inner-loop generation for a SELECT
-** statement with an ORDER BY that is not optimized by an index. It
-** determines the expressions, if any, that the sorter-reference
+** statement with an ORDER BY that is not optimized by an index. It
+** determines the expressions, if any, that the sorter-reference
** optimization should be used for. The sorter-reference optimization
** is used for SELECT queries like:
**
@@ -124686,11 +142753,11 @@ static void codeDistinct(
** storing values read from that column in the sorter records, the PK of
** the row from table t1 is stored instead. Then, as records are extracted from
** the sorter to return to the user, the required value of bigblob is
-** retrieved directly from table t1. If the values are very large, this
+** retrieved directly from table t1. If the values are very large, this
** can be more efficient than storing them directly in the sorter records.
**
-** The ExprList_item.bSorterRef flag is set for each expression in pEList
-** for which the sorter-reference optimization should be enabled.
+** The ExprList_item.fg.bSorterRef flag is set for each expression in pEList
+** for which the sorter-reference optimization should be enabled.
** Additionally, the pSort->aDefer[] array is populated with entries
** for all cursors required to evaluate all selected expressions. Finally.
** output variable (*ppExtra) is set to an expression list containing
@@ -124710,9 +142777,13 @@ static void selectExprDefer(
struct ExprList_item *pItem = &pEList->a[i];
if( pItem->u.x.iOrderByCol==0 ){
Expr *pExpr = pItem->pExpr;
- Table *pTab = pExpr->y.pTab;
- if( pExpr->op==TK_COLUMN && pExpr->iColumn>=0 && pTab && !IsVirtual(pTab)
- && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF)
+ Table *pTab;
+ if( pExpr->op==TK_COLUMN
+ && pExpr->iColumn>=0
+ && ALWAYS( ExprUseYTab(pExpr) )
+ && (pTab = pExpr->y.pTab)!=0
+ && IsOrdinaryTable(pTab)
+ && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF)!=0
){
int j;
for(j=0; j<nDefer; j++){
@@ -124733,6 +142804,7 @@ static void selectExprDefer(
Expr *pNew = sqlite3PExpr(pParse, TK_COLUMN, 0, 0);
if( pNew ){
pNew->iTable = pExpr->iTable;
+ assert( ExprUseYTab(pNew) );
pNew->y.pTab = pExpr->y.pTab;
pNew->iColumn = pPk ? pPk->aiColumn[k] : -1;
pExtra = sqlite3ExprListAppend(pParse, pExtra, pNew);
@@ -124744,7 +142816,7 @@ static void selectExprDefer(
nDefer++;
}
}
- pItem->bSorterRef = 1;
+ pItem->fg.bSorterRef = 1;
}
}
}
@@ -124759,7 +142831,7 @@ static void selectExprDefer(
**
** If srcTab is negative, then the p->pEList expressions
** are evaluated in order to get the data for this row. If srcTab is
-** zero or more, then data is pulled from srcTab and p->pEList is used only
+** zero or more, then data is pulled from srcTab and p->pEList is used only
** to get the number of columns and the collation sequence for each column.
*/
static void selectInnerLoop(
@@ -124823,7 +142895,7 @@ static void selectInnerLoop(
if( srcTab>=0 ){
for(i=0; i<nResultCol; i++){
sqlite3VdbeAddOp3(v, OP_Column, srcTab, i, regResult+i);
- VdbeComment((v, "%s", p->pEList->a[i].zName));
+ VdbeComment((v, "%s", p->pEList->a[i].zEName));
}
}else if( eDest!=SRT_Exists ){
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
@@ -124841,8 +142913,8 @@ static void selectInnerLoop(
}
if( pSort && hasDistinct==0 && eDest!=SRT_EphemTab && eDest!=SRT_Table ){
/* For each expression in p->pEList that is a copy of an expression in
- ** the ORDER BY clause (pSort->pOrderBy), set the associated
- ** iOrderByCol value to one more than the index of the ORDER BY
+ ** the ORDER BY clause (pSort->pOrderBy), set the associated
+ ** iOrderByCol value to one more than the index of the ORDER BY
** expression within the sort-key that pushOntoSorter() will generate.
** This allows the p->pEList field to be omitted from the sorted record,
** saving space and CPU cycles. */
@@ -124858,7 +142930,7 @@ static void selectInnerLoop(
selectExprDefer(pParse, pSort, p->pEList, &pExtra);
if( pExtra && pParse->db->mallocFailed==0 ){
/* If there are any extra PK columns to add to the sorter records,
- ** allocate extra memory cells and adjust the OpenEphemeral
+ ** allocate extra memory cells and adjust the OpenEphemeral
** instruction to account for the larger records. This is only
** required if there are one or more WITHOUT ROWID tables with
** composite primary keys in the SortCtx.aDefer[] array. */
@@ -124875,7 +142947,7 @@ static void selectInnerLoop(
for(i=0; i<pEList->nExpr; i++){
if( pEList->a[i].u.x.iOrderByCol>0
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
- || pEList->a[i].bSorterRef
+ || pEList->a[i].fg.bSorterRef
#endif
){
nResultCol--;
@@ -124888,8 +142960,9 @@ static void selectInnerLoop(
testcase( eDest==SRT_Mem );
testcase( eDest==SRT_Coroutine );
testcase( eDest==SRT_Output );
- assert( eDest==SRT_Set || eDest==SRT_Mem
- || eDest==SRT_Coroutine || eDest==SRT_Output );
+ assert( eDest==SRT_Set || eDest==SRT_Mem
+ || eDest==SRT_Coroutine || eDest==SRT_Output
+ || eDest==SRT_Upfrom );
}
sRowLoadInfo.regResult = regResult;
sRowLoadInfo.ecelFlags = ecelFlags;
@@ -124899,7 +142972,7 @@ static void selectInnerLoop(
if( pExtra ) nResultCol += pExtra->nExpr;
#endif
if( p->iLimit
- && (ecelFlags & SQLITE_ECEL_OMITREF)!=0
+ && (ecelFlags & SQLITE_ECEL_OMITREF)!=0
&& nPrefixReg>0
){
assert( pSort!=0 );
@@ -124916,58 +142989,11 @@ static void selectInnerLoop(
** part of the result.
*/
if( hasDistinct ){
- switch( pDistinct->eTnctType ){
- case WHERE_DISTINCT_ORDERED: {
- VdbeOp *pOp; /* No longer required OpenEphemeral instr. */
- int iJump; /* Jump destination */
- int regPrev; /* Previous row content */
-
- /* Allocate space for the previous row */
- regPrev = pParse->nMem+1;
- pParse->nMem += nResultCol;
-
- /* Change the OP_OpenEphemeral coded earlier to an OP_Null
- ** sets the MEM_Cleared bit on the first register of the
- ** previous value. This will cause the OP_Ne below to always
- ** fail on the first iteration of the loop even if the first
- ** row is all NULLs.
- */
- sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
- pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct);
- pOp->opcode = OP_Null;
- pOp->p1 = 1;
- pOp->p2 = regPrev;
-
- iJump = sqlite3VdbeCurrentAddr(v) + nResultCol;
- for(i=0; i<nResultCol; i++){
- CollSeq *pColl = sqlite3ExprCollSeq(pParse, p->pEList->a[i].pExpr);
- if( i<nResultCol-1 ){
- sqlite3VdbeAddOp3(v, OP_Ne, regResult+i, iJump, regPrev+i);
- VdbeCoverage(v);
- }else{
- sqlite3VdbeAddOp3(v, OP_Eq, regResult+i, iContinue, regPrev+i);
- VdbeCoverage(v);
- }
- sqlite3VdbeChangeP4(v, -1, (const char *)pColl, P4_COLLSEQ);
- sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
- }
- assert( sqlite3VdbeCurrentAddr(v)==iJump || pParse->db->mallocFailed );
- sqlite3VdbeAddOp3(v, OP_Copy, regResult, regPrev, nResultCol-1);
- break;
- }
-
- case WHERE_DISTINCT_UNIQUE: {
- sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
- break;
- }
-
- default: {
- assert( pDistinct->eTnctType==WHERE_DISTINCT_UNORDERED );
- codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol,
- regResult);
- break;
- }
- }
+ int eType = pDistinct->eTnctType;
+ int iTab = pDistinct->tabTnct;
+ assert( nResultCol==p->pEList->nExpr );
+ iTab = codeDistinct(pParse, eType, iTab, iContinue, p->pEList, regResult);
+ fixDistinctOpenEph(pParse, eType, iTab, pDistinct->addrTnct);
if( pSort==0 ){
codeOffset(v, p->iOffset, iContinue);
}
@@ -125009,6 +143035,16 @@ static void selectInnerLoop(
testcase( eDest==SRT_Fifo );
testcase( eDest==SRT_DistFifo );
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg);
+#if !defined(SQLITE_ENABLE_NULL_TRIM) && defined(SQLITE_DEBUG)
+ /* A destination of SRT_Table and a non-zero iSDParm2 parameter means
+ ** that this is an "UPDATE ... FROM" on a virtual table or view. In this
+ ** case set the p5 parameter of the OP_MakeRecord to OPFLAG_NOCHNG_MAGIC.
+ ** This does not affect operation in any way - it just allows MakeRecord
+ ** to process OPFLAG_NOCHANGE values without an assert() failing. */
+ if( eDest==SRT_Table && pDest->iSDParm2 ){
+ sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC);
+ }
+#endif
#ifndef SQLITE_OMIT_CTE
if( eDest==SRT_DistFifo ){
/* If the destination is DistFifo, then cursor (iParm+1) is open
@@ -125037,6 +143073,30 @@ static void selectInnerLoop(
break;
}
+ case SRT_Upfrom: {
+ if( pSort ){
+ pushOntoSorter(
+ pParse, pSort, p, regResult, regOrig, nResultCol, nPrefixReg);
+ }else{
+ int i2 = pDest->iSDParm2;
+ int r1 = sqlite3GetTempReg(pParse);
+
+ /* If the UPDATE FROM join is an aggregate that matches no rows, it
+ ** might still be trying to return one row, because that is what
+ ** aggregates do. Don't record that empty row in the output table. */
+ sqlite3VdbeAddOp2(v, OP_IsNull, regResult, iBreak); VdbeCoverage(v);
+
+ sqlite3VdbeAddOp3(v, OP_MakeRecord,
+ regResult+(i2<0), nResultCol-(i2<0), r1);
+ if( i2<0 ){
+ sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regResult);
+ }else{
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, i2);
+ }
+ }
+ break;
+ }
+
#ifndef SQLITE_OMIT_SUBQUERY
/* If we are creating a set for an "expr IN (SELECT ...)" construct,
** then there should be a single item on the stack. Write this
@@ -125053,7 +143113,7 @@ static void selectInnerLoop(
}else{
int r1 = sqlite3GetTempReg(pParse);
assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol );
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
r1, pDest->zAffSdst, nResultCol);
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol);
sqlite3ReleaseTempReg(pParse, r1);
@@ -125061,6 +143121,7 @@ static void selectInnerLoop(
break;
}
+
/* If any row exist in the result set, record that fact and abort.
*/
case SRT_Exists: {
@@ -125070,7 +143131,7 @@ static void selectInnerLoop(
}
/* If this is a scalar select that is part of an expression, then
- ** store the results in the appropriate memory cell or array of
+ ** store the results in the appropriate memory cell or array of
** memory cells and break out of the scan loop.
*/
case SRT_Mem: {
@@ -125125,7 +143186,7 @@ static void selectInnerLoop(
/* If the destination is DistQueue, then cursor (iParm+1) is open
** on a second ephemeral index that holds all values every previously
** added to the queue. */
- addrTest = sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, 0,
+ addrTest = sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, 0,
regResult, nResultCol);
VdbeCoverage(v);
}
@@ -125181,7 +143242,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*);
KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
if( p ){
- p->aSortOrder = (u8*)&p->aColl[N+X];
+ p->aSortFlags = (u8*)&p->aColl[N+X];
p->nKeyField = (u16)N;
p->nAllField = (u16)(N+X);
p->enc = ENC(db);
@@ -125189,7 +143250,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
p->nRef = 1;
memset(&p[1], 0, nExtra);
}else{
- sqlite3OomFault(db);
+ return (KeyInfo*)sqlite3OomFault(db);
}
return p;
}
@@ -125199,9 +143260,10 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
*/
SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo *p){
if( p ){
+ assert( p->db!=0 );
assert( p->nRef>0 );
p->nRef--;
- if( p->nRef==0 ) sqlite3DbFreeNN(p->db, p);
+ if( p->nRef==0 ) sqlite3DbNNFreeNN(p->db, p);
}
}
@@ -125258,7 +143320,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(
assert( sqlite3KeyInfoIsWriteable(pInfo) );
for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){
pInfo->aColl[i-iStart] = sqlite3ExprNNCollSeq(pParse, pItem->pExpr);
- pInfo->aSortOrder[i-iStart] = pItem->sortOrder;
+ pInfo->aSortFlags[i-iStart] = pItem->fg.sortFlags;
}
}
return pInfo;
@@ -125267,7 +143329,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(
/*
** Name of the connection operator, used for error messages.
*/
-static const char *selectOpName(int id){
+SQLITE_PRIVATE const char *sqlite3SelectOpName(int id){
char *z;
switch( id ){
case TK_ALL: z = "UNION ALL"; break;
@@ -125324,7 +143386,7 @@ static void generateSortTail(
){
Vdbe *v = pParse->pVdbe; /* The prepared statement */
int addrBreak = pSort->labelDone; /* Jump here to exit loop */
- int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */
+ int addrContinue = sqlite3VdbeMakeLabel(pParse);/* Jump here for next cycle */
int addr; /* Top of output loop. Jump for Next. */
int addrOnce = 0;
int iTab;
@@ -125340,6 +143402,16 @@ static void generateSortTail(
int bSeq; /* True if sorter record includes seq. no. */
int nRefKey = 0;
struct ExprList_item *aOutEx = p->pEList->a;
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrExplain; /* Address of OP_Explain instruction */
+#endif
+
+ ExplainQueryPlan2(addrExplain, (pParse, 0,
+ "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat>0?"RIGHT PART OF ":"")
+ );
+ sqlite3VdbeScanStatusRange(v, addrExplain,pSort->addrPush,pSort->addrPushEnd);
+ sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, pSort->addrPush);
+
assert( addrBreak<0 );
if( pSort->labelBkOut ){
@@ -125360,11 +143432,19 @@ static void generateSortTail(
iTab = pSort->iECursor;
if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){
+ if( eDest==SRT_Mem && p->iOffset ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pDest->iSdst);
+ }
regRowid = 0;
regRow = pDest->iSdst;
}else{
regRowid = sqlite3GetTempReg(pParse);
- regRow = sqlite3GetTempRange(pParse, nColumn);
+ if( eDest==SRT_EphemTab || eDest==SRT_Table ){
+ regRow = sqlite3GetTempReg(pParse);
+ nColumn = 0;
+ }else{
+ regRow = sqlite3GetTempRange(pParse, nColumn);
+ }
}
nKey = pOrderBy->nExpr - pSort->nOBSat;
if( pSort->sortFlags & SORTFLAG_UseSorter ){
@@ -125373,12 +143453,12 @@ static void generateSortTail(
if( pSort->labelBkOut ){
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut,
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut,
nKey+1+nColumn+nRefKey);
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
VdbeCoverage(v);
- codeOffset(v, p->iOffset, addrContinue);
+ assert( p->iLimit==0 && p->iOffset==0 );
sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab);
bSeq = 0;
}else{
@@ -125386,10 +143466,13 @@ static void generateSortTail(
codeOffset(v, p->iOffset, addrContinue);
iSortTab = iTab;
bSeq = 1;
+ if( p->iOffset>0 ){
+ sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1);
+ }
}
for(i=0, iCol=nKey+bSeq-1; i<nColumn; i++){
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
- if( aOutEx[i].bSorterRef ) continue;
+ if( aOutEx[i].fg.bSorterRef ) continue;
#endif
if( aOutEx[i].u.x.iOrderByCol==0 ) iCol++;
}
@@ -125406,7 +143489,7 @@ static void generateSortTail(
sqlite3VdbeAddOp1(v, OP_NullRow, iCsr);
if( HasRowid(pTab) ){
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iKey++, regKey);
- sqlite3VdbeAddOp3(v, OP_SeekRowid, iCsr,
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, iCsr,
sqlite3VdbeCurrentAddr(v)+1, regKey);
}else{
int k;
@@ -125426,7 +143509,7 @@ static void generateSortTail(
#endif
for(i=nColumn-1; i>=0; i--){
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
- if( aOutEx[i].bSorterRef ){
+ if( aOutEx[i].fg.bSorterRef ){
sqlite3ExprCode(pParse, aOutEx[i].pExpr, regRow+i);
}else
#endif
@@ -125438,12 +143521,14 @@ static void generateSortTail(
iRead = iCol--;
}
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i);
- VdbeComment((v, "%s", aOutEx[i].zName?aOutEx[i].zName : aOutEx[i].zSpan));
+ VdbeComment((v, "%s", aOutEx[i].zEName));
}
}
+ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1);
switch( eDest ){
case SRT_Table:
case SRT_EphemTab: {
+ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq, regRow);
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
@@ -125462,8 +143547,19 @@ static void generateSortTail(
break;
}
#endif
+ case SRT_Upfrom: {
+ int i2 = pDest->iSDParm2;
+ int r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord,regRow+(i2<0),nColumn-(i2<0),r1);
+ if( i2<0 ){
+ sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, regRow);
+ }else{
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regRow, i2);
+ }
+ break;
+ }
default: {
- assert( eDest==SRT_Output || eDest==SRT_Coroutine );
+ assert( eDest==SRT_Output || eDest==SRT_Coroutine );
testcase( eDest==SRT_Output );
testcase( eDest==SRT_Coroutine );
if( eDest==SRT_Output ){
@@ -125490,6 +143586,7 @@ static void generateSortTail(
}else{
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); VdbeCoverage(v);
}
+ sqlite3VdbeScanStatusRange(v, addrExplain, sqlite3VdbeCurrentAddr(v)-1, -1);
if( pSort->regReturn ) sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn);
sqlite3VdbeResolveLabel(v, addrBreak);
}
@@ -125498,21 +143595,18 @@ static void generateSortTail(
** Return a pointer to a string containing the 'declaration type' of the
** expression pExpr. The string may be treated as static by the caller.
**
-** Also try to estimate the size of the returned value and return that
-** result in *pEstWidth.
-**
** The declaration type is the exact datatype definition extracted from the
** original CREATE TABLE statement if the expression is a column. The
** declaration type for a ROWID field is INTEGER. Exactly when an expression
** is considered a column can be complex in the presence of subqueries. The
-** result-set expression in all of the following SELECT statements is
+** result-set expression in all of the following SELECT statements is
** considered a column by this function.
**
** SELECT col FROM tbl;
** SELECT (SELECT col FROM tbl;
** SELECT (SELECT col FROM tbl);
** SELECT abc FROM (SELECT col AS abc FROM tbl);
-**
+**
** The declaration type for any expression other than a column is NULL.
**
** This routine has either 3 or 6 parameters depending on whether or not
@@ -125524,7 +143618,7 @@ static void generateSortTail(
# define columnType(A,B,C,D,E) columnTypeImpl(A,B)
#endif
static const char *columnTypeImpl(
- NameContext *pNC,
+ NameContext *pNC,
#ifndef SQLITE_ENABLE_COLUMN_METADATA
Expr *pExpr
#else
@@ -125544,8 +143638,6 @@ static const char *columnTypeImpl(
assert( pExpr!=0 );
assert( pNC->pSrcList!=0 );
- assert( pExpr->op!=TK_AGG_COLUMN ); /* This routine runes before aggregates
- ** are processed */
switch( pExpr->op ){
case TK_COLUMN: {
/* The expression is a column. Locate the table the column is being
@@ -125569,33 +143661,39 @@ static const char *columnTypeImpl(
if( pTab==0 ){
/* At one time, code such as "SELECT new.x" within a trigger would
** cause this condition to run. Since then, we have restructured how
- ** trigger code is generated and so this condition is no longer
+ ** trigger code is generated and so this condition is no longer
** possible. However, it can still be true for statements like
** the following:
**
** CREATE TABLE t1(col INTEGER);
** SELECT (SELECT t1.col) FROM FROM t1;
**
- ** when columnType() is called on the expression "t1.col" in the
+ ** when columnType() is called on the expression "t1.col" in the
** sub-select. In this case, set the column type to NULL, even
** though it should really be "INTEGER".
**
** This is not a problem, as the column type of "t1.col" is never
- ** used. When columnType() is called on the expression
+ ** used. When columnType() is called on the expression
** "(SELECT t1.col)", the correct type is returned (see the TK_SELECT
** branch below. */
break;
}
- assert( pTab && pExpr->y.pTab==pTab );
+ assert( pTab && ExprUseYTab(pExpr) && pExpr->y.pTab==pTab );
if( pS ){
/* The "table" is actually a sub-select or a view in the FROM clause
** of the SELECT statement. Return the declaration type and origin
** data for the result-set column of the sub-select.
*/
- if( iCol>=0 && iCol<pS->pEList->nExpr ){
+ if( iCol<pS->pEList->nExpr
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ && iCol>=0
+#else
+ && ALWAYS(iCol>=0)
+#endif
+ ){
/* If iCol is less than zero, then the expression requests the
- ** rowid of the sub-select or view. This expression is legal (see
+ ** rowid of the sub-select or view. This expression is legal (see
** test case misc2.2.2) - it always evaluates to NULL.
*/
NameContext sNC;
@@ -125603,7 +143701,7 @@ static const char *columnTypeImpl(
sNC.pSrcList = pS->pSrc;
sNC.pNext = pNC;
sNC.pParse = pNC->pParse;
- zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol);
+ zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol);
}
}else{
/* A real table or a CTE table */
@@ -125615,7 +143713,7 @@ static const char *columnTypeImpl(
zType = "INTEGER";
zOrigCol = "rowid";
}else{
- zOrigCol = pTab->aCol[iCol].zName;
+ zOrigCol = pTab->aCol[iCol].zCnName;
zType = sqlite3ColumnType(&pTab->aCol[iCol],0);
}
zOrigTab = pTab->zName;
@@ -125641,19 +143739,21 @@ static const char *columnTypeImpl(
** statement.
*/
NameContext sNC;
- Select *pS = pExpr->x.pSelect;
- Expr *p = pS->pEList->a[0].pExpr;
- assert( ExprHasProperty(pExpr, EP_xIsSelect) );
+ Select *pS;
+ Expr *p;
+ assert( ExprUseXSelect(pExpr) );
+ pS = pExpr->x.pSelect;
+ p = pS->pEList->a[0].pExpr;
sNC.pSrcList = pS->pSrc;
sNC.pNext = pNC;
sNC.pParse = pNC->pParse;
- zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol);
+ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol);
break;
}
#endif
}
-#ifdef SQLITE_ENABLE_COLUMN_METADATA
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
if( pzOrigDb ){
assert( pzOrigTab && pzOrigCol );
*pzOrigDb = zOrigDb;
@@ -125689,7 +143789,7 @@ static void generateColumnTypes(
const char *zOrigCol = 0;
zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol);
- /* The vdbe must make its own copy of the column-type and other
+ /* The vdbe must make its own copy of the column-type and other
** column specific strings, in case the schema is reset before this
** virtual machine is deleted.
*/
@@ -125735,7 +143835,7 @@ static void generateColumnTypes(
** then the result column name with the table name
** prefix, ex: TABLE.COLUMN. Otherwise use zSpan.
*/
-static void generateColumnNames(
+SQLITE_PRIVATE void sqlite3GenerateColumnNames(
Parse *pParse, /* Parser context */
Select *pSelect /* Generate column names for this SELECT statement */
){
@@ -125748,17 +143848,10 @@ static void generateColumnNames(
int fullName; /* TABLE.COLUMN if no AS clause and is a direct table ref */
int srcName; /* COLUMN or TABLE.COLUMN if no AS clause and is direct */
-#ifndef SQLITE_OMIT_EXPLAIN
- /* If this is an EXPLAIN, skip this step */
- if( pParse->explain ){
- return;
- }
-#endif
-
if( pParse->colNamesSet ) return;
/* Column names are determined by the left-most term of a compound select */
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
- SELECTTRACE(1,pParse,pSelect,("generating column names\n"));
+ TREETRACE(0x80,pParse,pSelect,("generating column names\n"));
pTabList = pSelect->pSrc;
pEList = pSelect->pEList;
assert( v!=0 );
@@ -125772,10 +143865,11 @@ static void generateColumnNames(
assert( p!=0 );
assert( p->op!=TK_AGG_COLUMN ); /* Agg processing has not run yet */
- assert( p->op!=TK_COLUMN || p->y.pTab!=0 ); /* Covering idx not yet coded */
- if( pEList->a[i].zName ){
+ assert( p->op!=TK_COLUMN
+ || (ExprUseYTab(p) && p->y.pTab!=0) ); /* Covering idx not yet coded */
+ if( pEList->a[i].zEName && pEList->a[i].fg.eEName==ENAME_NAME ){
/* An AS clause always takes first priority */
- char *zName = pEList->a[i].zName;
+ char *zName = pEList->a[i].zEName;
sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT);
}else if( srcName && p->op==TK_COLUMN ){
char *zCol;
@@ -125787,7 +143881,7 @@ static void generateColumnNames(
if( iCol<0 ){
zCol = "rowid";
}else{
- zCol = pTab->aCol[iCol].zName;
+ zCol = pTab->aCol[iCol].zCnName;
}
if( fullName ){
char *zName = 0;
@@ -125797,7 +143891,7 @@ static void generateColumnNames(
sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT);
}
}else{
- const char *z = pEList->a[i].zSpan;
+ const char *z = pEList->a[i].zEName;
z = z==0 ? sqlite3MPrintf(db, "column%d", i+1) : sqlite3DbStrDup(db, z);
sqlite3VdbeSetColName(v, i, COLNAME_NAME, z, SQLITE_DYNAMIC);
}
@@ -125825,7 +143919,7 @@ static void generateColumnNames(
** and will break if those assumptions changes. Hence, use extreme caution
** when modifying this routine to avoid breaking legacy.
**
-** See Also: generateColumnNames()
+** See Also: sqlite3GenerateColumnNames()
*/
SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
Parse *pParse, /* Parsing context */
@@ -125841,13 +143935,14 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
char *zName; /* Column name */
int nName; /* Size of name in zName[] */
Hash ht; /* Hash table of column names */
+ Table *pTab;
sqlite3HashInit(&ht);
if( pEList ){
nCol = pEList->nExpr;
aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
testcase( aCol==0 );
- if( nCol>32767 ) nCol = 32767;
+ if( NEVER(nCol>32767) ) nCol = 32767;
}else{
nCol = 0;
aCol = 0;
@@ -125856,34 +143951,37 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
*pnCol = nCol;
*paCol = aCol;
- for(i=0, pCol=aCol; i<nCol && !db->mallocFailed; i++, pCol++){
+ for(i=0, pCol=aCol; i<nCol && !pParse->nErr; i++, pCol++){
+ struct ExprList_item *pX = &pEList->a[i];
+ struct ExprList_item *pCollide;
/* Get an appropriate name for the column
*/
- if( (zName = pEList->a[i].zName)!=0 ){
+ if( (zName = pX->zEName)!=0 && pX->fg.eEName==ENAME_NAME ){
/* If the column contains an "AS <name>" phrase, use <name> as the name */
}else{
- Expr *pColExpr = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
- while( pColExpr->op==TK_DOT ){
+ Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pX->pExpr);
+ while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){
pColExpr = pColExpr->pRight;
assert( pColExpr!=0 );
}
- assert( pColExpr->op!=TK_AGG_COLUMN );
- if( pColExpr->op==TK_COLUMN ){
+ if( pColExpr->op==TK_COLUMN
+ && ALWAYS( ExprUseYTab(pColExpr) )
+ && ALWAYS( pColExpr->y.pTab!=0 )
+ ){
/* For columns use the column name name */
int iCol = pColExpr->iColumn;
- Table *pTab = pColExpr->y.pTab;
- assert( pTab!=0 );
+ pTab = pColExpr->y.pTab;
if( iCol<0 ) iCol = pTab->iPKey;
- zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
+ zName = iCol>=0 ? pTab->aCol[iCol].zCnName : "rowid";
}else if( pColExpr->op==TK_ID ){
assert( !ExprHasProperty(pColExpr, EP_IntValue) );
zName = pColExpr->u.zToken;
}else{
/* Use the original text of the column expression as its name */
- zName = pEList->a[i].zSpan;
+ assert( zName==pX->zEName ); /* pointer comparison intended */
}
}
- if( zName ){
+ if( zName && !sqlite3IsTrueOrFalse(zName) ){
zName = sqlite3DbStrDup(db, zName);
}else{
zName = sqlite3MPrintf(db,"column%d",i+1);
@@ -125893,85 +143991,135 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
** append an integer to the name so that it becomes unique.
*/
cnt = 0;
- while( zName && sqlite3HashFind(&ht, zName)!=0 ){
+ while( zName && (pCollide = sqlite3HashFind(&ht, zName))!=0 ){
+ if( pCollide->fg.bUsingTerm ){
+ pCol->colFlags |= COLFLAG_NOEXPAND;
+ }
nName = sqlite3Strlen30(zName);
if( nName>0 ){
for(j=nName-1; j>0 && sqlite3Isdigit(zName[j]); j--){}
if( zName[j]==':' ) nName = j;
}
zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt);
- if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt);
+ sqlite3ProgressCheck(pParse);
+ if( cnt>3 ){
+ sqlite3_randomness(sizeof(cnt), &cnt);
+ }
+ }
+ pCol->zCnName = zName;
+ pCol->hName = sqlite3StrIHash(zName);
+ if( pX->fg.bNoExpand ){
+ pCol->colFlags |= COLFLAG_NOEXPAND;
}
- pCol->zName = zName;
sqlite3ColumnPropertiesFromName(0, pCol);
- if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){
+ if( zName && sqlite3HashInsert(&ht, zName, pX)==pX ){
sqlite3OomFault(db);
}
}
sqlite3HashClear(&ht);
- if( db->mallocFailed ){
+ if( pParse->nErr ){
for(j=0; j<i; j++){
- sqlite3DbFree(db, aCol[j].zName);
+ sqlite3DbFree(db, aCol[j].zCnName);
}
sqlite3DbFree(db, aCol);
*paCol = 0;
*pnCol = 0;
- return SQLITE_NOMEM_BKPT;
+ return pParse->rc;
}
return SQLITE_OK;
}
/*
-** Add type and collation information to a column list based on
-** a SELECT statement.
-**
-** The column list presumably came from selectColumnNamesFromExprList().
-** The column list has only names, not types or collations. This
-** routine goes through and adds the types and collations.
+** pTab is a transient Table object that represents a subquery of some
+** kind (maybe a parenthesized subquery in the FROM clause of a larger
+** query, or a VIEW, or a CTE). This routine computes type information
+** for that Table object based on the Select object that implements the
+** subquery. For the purposes of this routine, "type information" means:
**
-** This routine requires that all identifiers in the SELECT
-** statement be resolved.
+** * The datatype name, as it might appear in a CREATE TABLE statement
+** * Which collating sequence to use for the column
+** * The affinity of the column
*/
-SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(
- Parse *pParse, /* Parsing contexts */
- Table *pTab, /* Add column type information to this table */
- Select *pSelect /* SELECT used to determine types and collations */
+SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(
+ Parse *pParse, /* Parsing contexts */
+ Table *pTab, /* Add column type information to this table */
+ Select *pSelect, /* SELECT used to determine types and collations */
+ char aff /* Default affinity. */
){
sqlite3 *db = pParse->db;
- NameContext sNC;
Column *pCol;
CollSeq *pColl;
- int i;
+ int i,j;
Expr *p;
struct ExprList_item *a;
+ NameContext sNC;
assert( pSelect!=0 );
- assert( (pSelect->selFlags & SF_Resolved)!=0 );
- assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed );
- if( db->mallocFailed ) return;
+ testcase( (pSelect->selFlags & SF_Resolved)==0 );
+ assert( (pSelect->selFlags & SF_Resolved)!=0 || IN_RENAME_OBJECT );
+ assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 );
+ assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB );
+ if( db->mallocFailed || IN_RENAME_OBJECT ) return;
+ while( pSelect->pPrior ) pSelect = pSelect->pPrior;
+ a = pSelect->pEList->a;
memset(&sNC, 0, sizeof(sNC));
sNC.pSrcList = pSelect->pSrc;
- a = pSelect->pEList->a;
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
const char *zType;
- int n, m;
+ i64 n;
+ pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT);
p = a[i].pExpr;
- zType = columnType(&sNC, p, 0, 0, 0);
/* pCol->szEst = ... // Column size est for SELECT tables never used */
pCol->affinity = sqlite3ExprAffinity(p);
+ if( pCol->affinity<=SQLITE_AFF_NONE ){
+ pCol->affinity = aff;
+ }
+ if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){
+ int m = 0;
+ Select *pS2;
+ for(m=0, pS2=pSelect->pNext; pS2; pS2=pS2->pNext){
+ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr);
+ }
+ if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){
+ pCol->affinity = SQLITE_AFF_BLOB;
+ }else
+ if( pCol->affinity>=SQLITE_AFF_NUMERIC && (m&0x02)!=0 ){
+ pCol->affinity = SQLITE_AFF_BLOB;
+ }
+ if( pCol->affinity>=SQLITE_AFF_NUMERIC && p->op==TK_CAST ){
+ pCol->affinity = SQLITE_AFF_FLEXNUM;
+ }
+ }
+ zType = columnType(&sNC, p, 0, 0, 0);
+ if( zType==0 || pCol->affinity!=sqlite3AffinityType(zType, 0) ){
+ if( pCol->affinity==SQLITE_AFF_NUMERIC
+ || pCol->affinity==SQLITE_AFF_FLEXNUM
+ ){
+ zType = "NUM";
+ }else{
+ zType = 0;
+ for(j=1; j<SQLITE_N_STDTYPE; j++){
+ if( sqlite3StdTypeAffinity[j]==pCol->affinity ){
+ zType = sqlite3StdType[j];
+ break;
+ }
+ }
+ }
+ }
if( zType ){
- m = sqlite3Strlen30(zType);
- n = sqlite3Strlen30(pCol->zName);
- pCol->zName = sqlite3DbReallocOrFree(db, pCol->zName, n+m+2);
- if( pCol->zName ){
- memcpy(&pCol->zName[n+1], zType, m+1);
+ i64 m = sqlite3Strlen30(zType);
+ n = sqlite3Strlen30(pCol->zCnName);
+ pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2);
+ pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL);
+ if( pCol->zCnName ){
+ memcpy(&pCol->zCnName[n+1], zType, m+1);
pCol->colFlags |= COLFLAG_HASTYPE;
}
}
- if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_BLOB;
pColl = sqlite3ExprCollSeq(pParse, p);
- if( pColl && pCol->zColl==0 ){
- pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
+ if( pColl ){
+ assert( pTab->pIndex==0 );
+ sqlite3ColumnSetColl(db, pCol, pColl->zName);
}
}
pTab->szTabRow = 1; /* Any non-zero value works */
@@ -125981,30 +144129,27 @@ SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(
** Given a SELECT statement, generate a Table structure that describes
** the result set of that SELECT.
*/
-SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
+SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, char aff){
Table *pTab;
sqlite3 *db = pParse->db;
- int savedFlags;
+ u64 savedFlags;
savedFlags = db->flags;
- db->flags &= ~SQLITE_FullColNames;
+ db->flags &= ~(u64)SQLITE_FullColNames;
db->flags |= SQLITE_ShortColNames;
sqlite3SelectPrep(pParse, pSelect, 0);
+ db->flags = savedFlags;
if( pParse->nErr ) return 0;
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
- db->flags = savedFlags;
pTab = sqlite3DbMallocZero(db, sizeof(Table) );
if( pTab==0 ){
return 0;
}
- /* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside
- ** is disabled */
- assert( db->lookaside.bDisable );
pTab->nTabRef = 1;
pTab->zName = 0;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect);
+ sqlite3SubqueryColumnTypes(pParse, pTab, pSelect, aff);
pTab->iPKey = -1;
if( db->mallocFailed ){
sqlite3DeleteTable(db, pTab);
@@ -126034,9 +144179,9 @@ SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){
** Compute the iLimit and iOffset fields of the SELECT based on the
** pLimit expressions. pLimit->pLeft and pLimit->pRight hold the expressions
** that appear in the original SQL statement after the LIMIT and OFFSET
-** keywords. Or NULL if those keywords are omitted. iLimit and iOffset
-** are the integer memory register numbers for counters used to compute
-** the limit and offset. If there is no limit and/or offset, then
+** keywords. Or NULL if those keywords are omitted. iLimit and iOffset
+** are the integer memory register numbers for counters used to compute
+** the limit and offset. If there is no limit and/or offset, then
** iLimit and iOffset are negative.
**
** This routine changes the values of iLimit and iOffset only if
@@ -126062,7 +144207,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
if( p->iLimit ) return;
- /*
+ /*
** "LIMIT -1" always shows all rows. There is some
** controversy about what the correct behavior should be.
** The current implementation interprets "LIMIT 0" to mean
@@ -126138,7 +144283,7 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){
*/
static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
ExprList *pOrderBy = p->pOrderBy;
- int nOrderBy = p->pOrderBy->nExpr;
+ int nOrderBy = ALWAYS(pOrderBy!=0) ? pOrderBy->nExpr : 0;
sqlite3 *db = pParse->db;
KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1);
if( pRet ){
@@ -126158,7 +144303,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
}
assert( sqlite3KeyInfoIsWriteable(pRet) );
pRet->aColl[i] = pColl;
- pRet->aSortOrder[i] = pOrderBy->a[i].sortOrder;
+ pRet->aSortFlags[i] = pOrderBy->a[i].fg.sortFlags;
}
}
@@ -126190,7 +144335,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
** inserted into the Queue table. The iDistinct table keeps a copy of all rows
** that have ever been inserted into Queue and causes duplicates to be
** discarded. If the operator is UNION ALL, then duplicates are allowed.
-**
+**
** If the query has an ORDER BY, then entries in the Queue table are kept in
** ORDER BY order and the first entry is extracted for each cycle. Without
** an ORDER BY, the Queue table is just a FIFO.
@@ -126210,7 +144355,8 @@ static void generateWithRecursiveQuery(
SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */
int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */
Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
- Select *pSetup = p->pPrior; /* The setup query */
+ Select *pSetup; /* The setup query */
+ Select *pFirstRec; /* Left-most recursive term */
int addrTop; /* Top of the loop */
int addrCont, addrBreak; /* CONTINUE and BREAK addresses */
int iCurrent = 0; /* The Current table */
@@ -126218,7 +144364,7 @@ static void generateWithRecursiveQuery(
int iQueue; /* The Queue table */
int iDistinct = 0; /* To ensure unique results if UNION */
int eDest = SRT_Fifo; /* How to write to Queue */
- SelectDest destQueue; /* SelectDest targetting the Queue table */
+ SelectDest destQueue; /* SelectDest targeting the Queue table */
int i; /* Loop counter */
int rc; /* Result code */
ExprList *pOrderBy; /* The ORDER BY clause */
@@ -126236,7 +144382,7 @@ static void generateWithRecursiveQuery(
if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return;
/* Process the LIMIT and OFFSET clauses, if they exist */
- addrBreak = sqlite3VdbeMakeLabel(v);
+ addrBreak = sqlite3VdbeMakeLabel(pParse);
p->nSelectRow = 320; /* 4 billion rows */
computeLimitRegisters(pParse, p, addrBreak);
pLimit = p->pLimit;
@@ -126286,7 +144432,24 @@ static void generateWithRecursiveQuery(
/* Detach the ORDER BY clause from the compound SELECT */
p->pOrderBy = 0;
+ /* Figure out how many elements of the compound SELECT are part of the
+ ** recursive query. Make sure no recursive elements use aggregate
+ ** functions. Mark the recursive elements as UNION ALL even if they
+ ** are really UNION because the distinctness will be enforced by the
+ ** iDistinct table. pFirstRec is left pointing to the left-most
+ ** recursive term of the CTE.
+ */
+ for(pFirstRec=p; ALWAYS(pFirstRec!=0); pFirstRec=pFirstRec->pPrior){
+ if( pFirstRec->selFlags & SF_Aggregate ){
+ sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
+ goto end_of_recursive_query;
+ }
+ pFirstRec->op = TK_ALL;
+ if( (pFirstRec->pPrior->selFlags & SF_Recursive)==0 ) break;
+ }
+
/* Store the results of the setup-query in Queue. */
+ pSetup = pFirstRec->pPrior;
pSetup->pNext = 0;
ExplainQueryPlan((pParse, 1, "SETUP"));
rc = sqlite3Select(pParse, pSetup, &destQueue);
@@ -126306,7 +144469,7 @@ static void generateWithRecursiveQuery(
sqlite3VdbeAddOp1(v, OP_Delete, iQueue);
/* Output the single row in Current */
- addrCont = sqlite3VdbeMakeLabel(v);
+ addrCont = sqlite3VdbeMakeLabel(pParse);
codeOffset(v, regOffset, addrCont);
selectInnerLoop(pParse, p, iCurrent,
0, 0, pDest, addrCont, addrBreak);
@@ -126319,15 +144482,11 @@ static void generateWithRecursiveQuery(
/* Execute the recursive SELECT taking the single row in Current as
** the value for the recursive-table. Store the results in the Queue.
*/
- if( p->selFlags & SF_Aggregate ){
- sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
- }else{
- p->pPrior = 0;
- ExplainQueryPlan((pParse, 1, "RECURSIVE STEP"));
- sqlite3Select(pParse, p, &destQueue);
- assert( p->pPrior==0 );
- p->pPrior = pSetup;
- }
+ pFirstRec->pPrior = 0;
+ ExplainQueryPlan((pParse, 1, "RECURSIVE STEP"));
+ sqlite3Select(pParse, p, &destQueue);
+ assert( pFirstRec->pPrior==0 );
+ pFirstRec->pPrior = pSetup;
/* Keep running the loop until the Queue is empty */
sqlite3VdbeGoto(v, addrTop);
@@ -126362,7 +144521,7 @@ static int multiSelectOrderBy(
** The "LIMIT of exactly 1" case of condition (1) comes about when a VALUES
** clause occurs within scalar expression (ex: "SELECT (VALUES(1),(2),(3))").
** The sqlite3CodeSubselect will have added the LIMIT 1 clause in tht case.
-** Since the limit is exactly 1, we only need to evalutes the left-most VALUES.
+** Since the limit is exactly 1, we only need to evaluate the left-most VALUES.
*/
static int multiSelectValues(
Parse *pParse, /* Parsing context */
@@ -126377,6 +144536,9 @@ static int multiSelectValues(
assert( p->selFlags & SF_Values );
assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) );
assert( p->pNext==0 || p->pEList->nExpr==p->pNext->pEList->nExpr );
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( p->pWin ) return -1;
+#endif
if( p->pPrior==0 ) break;
assert( p->pPrior->pNext==p );
p = p->pPrior;
@@ -126394,13 +144556,23 @@ static int multiSelectValues(
}
/*
+** Return true if the SELECT statement which is known to be the recursive
+** part of a recursive CTE still has its anchor terms attached. If the
+** anchor terms have already been removed, then return false.
+*/
+static int hasAnchor(Select *p){
+ while( p && (p->selFlags & SF_Recursive)!=0 ){ p = p->pPrior; }
+ return p!=0;
+}
+
+/*
** This routine is called to process a compound query form from
** two or more separate queries using UNION, UNION ALL, EXCEPT, or
** INTERSECT
**
** "p" points to the right-most of the two queries. the query on the
** left is p->pPrior. The left query could also be a compound query
-** in which case this routine will be called recursively.
+** in which case this routine will be called recursively.
**
** The results of the total query are to be written into a destination
** of type eDest with parameter iParm.
@@ -126441,15 +144613,12 @@ static int multiSelect(
*/
assert( p && p->pPrior ); /* Calling function guarantees this much */
assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION );
+ assert( p->selFlags & SF_Compound );
db = pParse->db;
pPrior = p->pPrior;
dest = *pDest;
- if( pPrior->pOrderBy || pPrior->pLimit ){
- sqlite3ErrorMsg(pParse,"%s clause should come after %s not before",
- pPrior->pOrderBy!=0 ? "ORDER BY" : "LIMIT", selectOpName(p->op));
- rc = 1;
- goto multi_select_end;
- }
+ assert( pPrior->pOrderBy==0 );
+ assert( pPrior->pLimit==0 );
v = sqlite3GetVdbe(pParse);
assert( v!=0 ); /* The VDBE already created by calling function */
@@ -126466,7 +144635,8 @@ static int multiSelect(
*/
if( p->selFlags & SF_MultiValue ){
rc = multiSelectValues(pParse, p, &dest);
- goto multi_select_end;
+ if( rc>=0 ) goto multi_select_end;
+ rc = SQLITE_OK;
}
/* Make sure all SELECTs in the statement have the same number of elements
@@ -126476,7 +144646,7 @@ static int multiSelect(
assert( p->pEList->nExpr==pPrior->pEList->nExpr );
#ifndef SQLITE_OMIT_CTE
- if( p->selFlags & SF_Recursive ){
+ if( (p->selFlags & SF_Recursive)!=0 && hasAnchor(p) ){
generateWithRecursiveQuery(pParse, p, &dest);
}else
#endif
@@ -126499,13 +144669,14 @@ static int multiSelect(
switch( p->op ){
case TK_ALL: {
int addr = 0;
- int nLimit;
+ int nLimit = 0; /* Initialize to suppress harmless compiler warning */
assert( !pPrior->pLimit );
pPrior->iLimit = p->iLimit;
pPrior->iOffset = p->iOffset;
pPrior->pLimit = p->pLimit;
+ TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n"));
rc = sqlite3Select(pParse, pPrior, &dest);
- p->pLimit = 0;
+ pPrior->pLimit = 0;
if( rc ){
goto multi_select_end;
}
@@ -126521,14 +144692,15 @@ static int multiSelect(
}
}
ExplainQueryPlan((pParse, 1, "UNION ALL"));
+ TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n"));
rc = sqlite3Select(pParse, p, &dest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
p->pPrior = pPrior;
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
- if( pPrior->pLimit
- && sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit)
- && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
+ if( p->pLimit
+ && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit)
+ && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
){
p->nSelectRow = sqlite3LogEst((u64)nLimit);
}
@@ -126545,7 +144717,7 @@ static int multiSelect(
Expr *pLimit; /* Saved values of p->nLimit */
int addr;
SelectDest uniondest;
-
+
testcase( p->op==TK_EXCEPT );
testcase( p->op==TK_UNION );
priorOp = SRT_Union;
@@ -126567,16 +144739,18 @@ static int multiSelect(
findRightmost(p)->selFlags |= SF_UsesEphemeral;
assert( p->pEList );
}
-
+
+
/* Code the SELECT statements to our left
*/
assert( !pPrior->pOrderBy );
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
+ TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION left...\n"));
rc = sqlite3Select(pParse, pPrior, &uniondest);
if( rc ){
goto multi_select_end;
}
-
+
/* Code the current SELECT statement
*/
if( p->op==TK_EXCEPT ){
@@ -126590,12 +144764,11 @@ static int multiSelect(
p->pLimit = 0;
uniondest.eDest = op;
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
- selectOpName(p->op)));
+ sqlite3SelectOpName(p->op)));
+ TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION right...\n"));
rc = sqlite3Select(pParse, p, &uniondest);
testcase( rc!=SQLITE_OK );
- /* Query flattening in sqlite3Select() might refill p->pOrderBy.
- ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */
- sqlite3ExprListDelete(db, p->pOrderBy);
+ assert( p->pOrderBy==0 );
pDelete = p->pPrior;
p->pPrior = pPrior;
p->pOrderBy = 0;
@@ -126606,16 +144779,16 @@ static int multiSelect(
p->pLimit = pLimit;
p->iLimit = 0;
p->iOffset = 0;
-
+
/* Convert the data in the temporary table into whatever form
** it is that we currently need.
*/
assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
- if( dest.eDest!=priorOp ){
+ assert( p->pEList || db->mallocFailed );
+ if( dest.eDest!=priorOp && db->mallocFailed==0 ){
int iCont, iBreak, iStart;
- assert( p->pEList );
- iBreak = sqlite3VdbeMakeLabel(v);
- iCont = sqlite3VdbeMakeLabel(v);
+ iBreak = sqlite3VdbeMakeLabel(pParse);
+ iCont = sqlite3VdbeMakeLabel(pParse);
computeLimitRegisters(pParse, p, iBreak);
sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
iStart = sqlite3VdbeCurrentAddr(v);
@@ -126635,7 +144808,7 @@ static int multiSelect(
int addr;
SelectDest intersectdest;
int r1;
-
+
/* INTERSECT is different from the others since it requires
** two temporary tables. Hence it has its own case. Begin
** by allocating the tables we will need.
@@ -126643,21 +144816,22 @@ static int multiSelect(
tab1 = pParse->nTab++;
tab2 = pParse->nTab++;
assert( p->pOrderBy==0 );
-
+
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
assert( p->addrOpenEphm[0] == -1 );
p->addrOpenEphm[0] = addr;
findRightmost(p)->selFlags |= SF_UsesEphemeral;
assert( p->pEList );
-
+
/* Code the SELECTs to our left into temporary table "tab1".
*/
sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
+ TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT left...\n"));
rc = sqlite3Select(pParse, pPrior, &intersectdest);
if( rc ){
goto multi_select_end;
}
-
+
/* Code the current SELECT into temporary table "tab2"
*/
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
@@ -126668,7 +144842,8 @@ static int multiSelect(
p->pLimit = 0;
intersectdest.iSDParm = tab2;
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
- selectOpName(p->op)));
+ sqlite3SelectOpName(p->op)));
+ TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT right...\n"));
rc = sqlite3Select(pParse, p, &intersectdest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
@@ -126678,13 +144853,14 @@ static int multiSelect(
}
sqlite3ExprDelete(db, p->pLimit);
p->pLimit = pLimit;
-
+
/* Generate code to take the intersection of the two temporary
** tables.
*/
+ if( rc ) break;
assert( p->pEList );
- iBreak = sqlite3VdbeMakeLabel(v);
- iCont = sqlite3VdbeMakeLabel(v);
+ iBreak = sqlite3VdbeMakeLabel(pParse);
+ iCont = sqlite3VdbeMakeLabel(pParse);
computeLimitRegisters(pParse, p, iBreak);
sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
r1 = sqlite3GetTempReg(pParse);
@@ -126702,15 +144878,16 @@ static int multiSelect(
break;
}
}
-
+
#ifndef SQLITE_OMIT_EXPLAIN
if( p->pNext==0 ){
ExplainQueryPlanPop(pParse);
}
#endif
}
-
- /* Compute collating sequences used by
+ if( pParse->nErr ) goto multi_select_end;
+
+ /* Compute collating sequences used by
** temporary tables needed to implement the compound select.
** Attach the KeyInfo structure to all temporary tables.
**
@@ -126727,6 +144904,7 @@ static int multiSelect(
int nCol; /* Number of columns in result set */
assert( p->pNext==0 );
+ assert( p->pEList!=0 );
nCol = p->pEList->nExpr;
pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1);
if( !pKeyInfo ){
@@ -126761,7 +144939,9 @@ static int multiSelect(
multi_select_end:
pDest->iSdst = dest.iSdst;
pDest->nSdst = dest.nSdst;
- sqlite3SelectDelete(db, pDelete);
+ if( pDelete ){
+ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete);
+ }
return rc;
}
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
@@ -126775,13 +144955,14 @@ SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){
sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms");
}else{
sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
- " do not have the same number of result columns", selectOpName(p->op));
+ " do not have the same number of result columns",
+ sqlite3SelectOpName(p->op));
}
}
/*
** Code an output subroutine for a coroutine implementation of a
-** SELECT statment.
+** SELECT statement.
**
** The data to be output is contained in pIn->iSdst. There are
** pIn->nSdst columns to be output. pDest is where the output should
@@ -126814,9 +144995,9 @@ static int generateOutputSubroutine(
int addr;
addr = sqlite3VdbeCurrentAddr(v);
- iContinue = sqlite3VdbeMakeLabel(v);
+ iContinue = sqlite3VdbeMakeLabel(pParse);
- /* Suppress duplicates for UNION, EXCEPT, and INTERSECT
+ /* Suppress duplicates for UNION, EXCEPT, and INTERSECT
*/
if( regPrev ){
int addr1, addr2;
@@ -126858,7 +145039,7 @@ static int generateOutputSubroutine(
int r1;
testcase( pIn->nSdst>1 );
r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst,
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst,
r1, pDest->zAffSdst, pIn->nSdst);
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pDest->iSDParm, r1,
pIn->iSdst, pIn->nSdst);
@@ -126868,11 +145049,12 @@ static int generateOutputSubroutine(
/* If this is a scalar select that is part of an expression, then
** store the results in the appropriate memory cell and break out
- ** of the scan loop.
+ ** of the scan loop. Note that the select might return multiple columns
+ ** if it is the RHS of a row-value IN operator.
*/
case SRT_Mem: {
- assert( pIn->nSdst==1 || pParse->nErr>0 ); testcase( pIn->nSdst!=1 );
- sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, 1);
+ testcase( pIn->nSdst>1 );
+ sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst);
/* The LIMIT clause will jump out of the loop for us */
break;
}
@@ -126895,7 +145077,7 @@ static int generateOutputSubroutine(
** SRT_Output. This routine is never called with any other
** destination other than the ones handled above or SRT_Output.
**
- ** For SRT_Output, results are stored in a sequence of registers.
+ ** For SRT_Output, results are stored in a sequence of registers.
** Then the OP_ResultRow opcode is used to cause sqlite3_step() to
** return the next row of result.
*/
@@ -126952,7 +145134,7 @@ static int generateOutputSubroutine(
**
** EofB: Called when data is exhausted from selectB.
**
-** The implementation of the latter five subroutines depend on which
+** The implementation of the latter five subroutines depend on which
** <operator> is used:
**
**
@@ -127002,7 +145184,7 @@ static int generateOutputSubroutine(
**
** We call AltB, AeqB, AgtB, EofA, and EofB "subroutines" but they are not
** actually called using Gosub and they do not Return. EofA and EofB loop
-** until all data is exhausted then jump to the "end" labe. AltB, AeqB,
+** until all data is exhausted then jump to the "end" label. AltB, AeqB,
** and AgtB jump to either L2 or to one of EofA or EofB.
*/
#ifndef SQLITE_OMIT_COMPOUND_SELECT
@@ -127013,6 +145195,8 @@ static int multiSelectOrderBy(
){
int i, j; /* Loop counters */
Select *pPrior; /* Another SELECT immediately to our left */
+ Select *pSplit; /* Left-most SELECT in the right-hand group */
+ int nSelect; /* Number of SELECT statements in the compound */
Vdbe *v; /* Generate code to this VDBE */
SelectDest destA; /* Destination for coroutine A */
SelectDest destB; /* Destination for coroutine B */
@@ -127037,29 +145221,28 @@ static int multiSelectOrderBy(
int savedOffset; /* Saved value of p->iOffset */
int labelCmpr; /* Label for the start of the merge algorithm */
int labelEnd; /* Label for the end of the overall SELECT stmt */
- int addr1; /* Jump instructions that get retargetted */
+ int addr1; /* Jump instructions that get retargeted */
int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */
KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */
KeyInfo *pKeyMerge; /* Comparison information for merging rows */
sqlite3 *db; /* Database connection */
ExprList *pOrderBy; /* The ORDER BY clause */
int nOrderBy; /* Number of terms in the ORDER BY clause */
- int *aPermute; /* Mapping from ORDER BY terms to result set columns */
+ u32 *aPermute; /* Mapping from ORDER BY terms to result set columns */
assert( p->pOrderBy!=0 );
assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */
db = pParse->db;
v = pParse->pVdbe;
assert( v!=0 ); /* Already thrown the error if VDBE alloc failed */
- labelEnd = sqlite3VdbeMakeLabel(v);
- labelCmpr = sqlite3VdbeMakeLabel(v);
+ labelEnd = sqlite3VdbeMakeLabel(pParse);
+ labelCmpr = sqlite3VdbeMakeLabel(pParse);
/* Patch up the ORDER BY clause
*/
- op = p->op;
- pPrior = p->pPrior;
- assert( pPrior->pOrderBy==0 );
+ op = p->op;
+ assert( p->pPrior->pOrderBy==0 );
pOrderBy = p->pOrderBy;
assert( pOrderBy );
nOrderBy = pOrderBy->nExpr;
@@ -127072,6 +145255,7 @@ static int multiSelectOrderBy(
for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){
struct ExprList_item *pItem;
for(j=0, pItem=pOrderBy->a; j<nOrderBy; j++, pItem++){
+ assert( pItem!=0 );
assert( pItem->u.x.iOrderByCol>0 );
if( pItem->u.x.iOrderByCol==i ) break;
}
@@ -127093,11 +145277,12 @@ static int multiSelectOrderBy(
** to the right and the left are evaluated, they use the correct
** collation.
*/
- aPermute = sqlite3DbMallocRawNN(db, sizeof(int)*(nOrderBy + 1));
+ aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1));
if( aPermute ){
struct ExprList_item *pItem;
aPermute[0] = nOrderBy;
for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){
+ assert( pItem!=0 );
assert( pItem->u.x.iOrderByCol>0 );
assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr );
aPermute[i] = pItem->u.x.iOrderByCol - 1;
@@ -127107,11 +145292,6 @@ static int multiSelectOrderBy(
pKeyMerge = 0;
}
- /* Reattach the ORDER BY clause to the query.
- */
- p->pOrderBy = pOrderBy;
- pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy, 0);
-
/* Allocate a range of temporary registers and the KeyInfo needed
** for the logic that removes duplicate result rows when the
** operator is UNION, EXCEPT, or INTERSECT (but not UNION ALL).
@@ -127129,19 +145309,37 @@ static int multiSelectOrderBy(
assert( sqlite3KeyInfoIsWriteable(pKeyDup) );
for(i=0; i<nExpr; i++){
pKeyDup->aColl[i] = multiSelectCollSeq(pParse, p, i);
- pKeyDup->aSortOrder[i] = 0;
+ pKeyDup->aSortFlags[i] = 0;
}
}
}
-
+
/* Separate the left and the right query from one another
*/
- p->pPrior = 0;
+ nSelect = 1;
+ if( (op==TK_ALL || op==TK_UNION)
+ && OptimizationEnabled(db, SQLITE_BalancedMerge)
+ ){
+ for(pSplit=p; pSplit->pPrior!=0 && pSplit->op==op; pSplit=pSplit->pPrior){
+ nSelect++;
+ assert( pSplit->pPrior->pNext==pSplit );
+ }
+ }
+ if( nSelect<=3 ){
+ pSplit = p;
+ }else{
+ pSplit = p;
+ for(i=2; i<nSelect; i+=2){ pSplit = pSplit->pPrior; }
+ }
+ pPrior = pSplit->pPrior;
+ assert( pPrior!=0 );
+ pSplit->pPrior = 0;
pPrior->pNext = 0;
+ assert( p->pOrderBy == pOrderBy );
+ assert( pOrderBy!=0 || db->mallocFailed );
+ pPrior->pOrderBy = sqlite3ExprListDup(pParse->db, pOrderBy, 0);
sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER");
- if( pPrior->pPrior==0 ){
- sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER");
- }
+ sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER");
/* Compute the limit registers */
computeLimitRegisters(pParse, p, labelEnd);
@@ -127164,7 +145362,7 @@ static int multiSelectOrderBy(
sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
- ExplainQueryPlan((pParse, 1, "MERGE (%s)", selectOpName(p->op)));
+ ExplainQueryPlan((pParse, 1, "MERGE (%s)", sqlite3SelectOpName(p->op)));
/* Generate a coroutine to evaluate the SELECT statement to the
** left of the compound operator - the "A" select.
@@ -127178,7 +145376,7 @@ static int multiSelectOrderBy(
sqlite3VdbeEndCoroutine(v, regAddrA);
sqlite3VdbeJumpHere(v, addr1);
- /* Generate a coroutine to evaluate the SELECT statement on
+ /* Generate a coroutine to evaluate the SELECT statement on
** the right - the "B" select
*/
addrSelectB = sqlite3VdbeCurrentAddr(v) + 1;
@@ -127187,7 +145385,7 @@ static int multiSelectOrderBy(
savedLimit = p->iLimit;
savedOffset = p->iOffset;
p->iLimit = regLimitB;
- p->iOffset = 0;
+ p->iOffset = 0;
ExplainQueryPlan((pParse, 1, "RIGHT"));
sqlite3Select(pParse, p, &destB);
p->iLimit = savedLimit;
@@ -127201,7 +145399,7 @@ static int multiSelectOrderBy(
addrOutA = generateOutputSubroutine(pParse,
p, &destA, pDest, regOutA,
regPrev, pKeyDup, labelEnd);
-
+
/* Generate a subroutine that outputs the current row of the B
** select as the next output row of the compound select.
*/
@@ -127218,7 +145416,7 @@ static int multiSelectOrderBy(
*/
if( op==TK_EXCEPT || op==TK_INTERSECT ){
addrEofA_noB = addrEofA = labelEnd;
- }else{
+ }else{
VdbeNoopComment((v, "eof-A subroutine"));
addrEofA = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd);
@@ -127233,7 +145431,7 @@ static int multiSelectOrderBy(
if( op==TK_INTERSECT ){
addrEofB = addrEofA;
if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
- }else{
+ }else{
VdbeNoopComment((v, "eof-B subroutine"));
addrEofB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, labelEnd); VdbeCoverage(v);
@@ -127290,13 +145488,15 @@ static int multiSelectOrderBy(
*/
sqlite3VdbeResolveLabel(v, labelEnd);
- /* Reassembly the compound query so that it will be freed correctly
- ** by the calling function */
- if( p->pPrior ){
- sqlite3SelectDelete(db, p->pPrior);
+ /* Make arrangements to free the 2nd and subsequent arms of the compound
+ ** after the parse has finished */
+ if( pSplit->pPrior ){
+ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSplit->pPrior);
}
- p->pPrior = pPrior;
- pPrior->pNext = p;
+ pSplit->pPrior = pPrior;
+ pPrior->pNext = pSplit;
+ sqlite3ExprListDelete(db, pPrior->pOrderBy);
+ pPrior->pOrderBy = 0;
/*** TBD: Insert subroutine calls to close cursors on incomplete
**** subqueries ****/
@@ -127312,13 +145512,42 @@ static int multiSelectOrderBy(
**
** All references to columns in table iTable are to be replaced by corresponding
** expressions in pEList.
+**
+** ## About "isOuterJoin":
+**
+** The isOuterJoin column indicates that the replacement will occur into a
+** position in the parent that NULL-able due to an OUTER JOIN. Either the
+** target slot in the parent is the right operand of a LEFT JOIN, or one of
+** the left operands of a RIGHT JOIN. In either case, we need to potentially
+** bypass the substituted expression with OP_IfNullRow.
+**
+** Suppose the original expression is an integer constant. Even though the table
+** has the nullRow flag set, because the expression is an integer constant,
+** it will not be NULLed out. So instead, we insert an OP_IfNullRow opcode
+** that checks to see if the nullRow flag is set on the table. If the nullRow
+** flag is set, then the value in the register is set to NULL and the original
+** expression is bypassed. If the nullRow flag is not set, then the original
+** expression runs to populate the register.
+**
+** Example where this is needed:
+**
+** CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT);
+** CREATE TABLE t2(x INT UNIQUE);
+**
+** SELECT a,b,m,x FROM t1 LEFT JOIN (SELECT 59 AS m,x FROM t2) ON b=x;
+**
+** When the subquery on the right side of the LEFT JOIN is flattened, we
+** have to add OP_IfNullRow in front of the OP_Integer that implements the
+** "m" value of the subquery so that a NULL will be loaded instead of 59
+** when processing a non-matched row of the left.
*/
typedef struct SubstContext {
Parse *pParse; /* The parsing context */
int iTable; /* Replace references to this table */
int iNewTable; /* New table number */
- int isLeftJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */
+ int isOuterJoin; /* Add TK_IF_NULL_ROW opcodes on each replacement */
ExprList *pEList; /* Replacement expressions */
+ ExprList *pCList; /* Collation sequences for replacement expr */
} SubstContext;
/* Forward Declarations */
@@ -127328,13 +145557,13 @@ static void substSelect(SubstContext*, Select*, int);
/*
** Scan through the expression pExpr. Replace every reference to
** a column in table number iTable with a copy of the iColumn-th
-** entry in pEList. (But leave references to the ROWID column
+** entry in pEList. (But leave references to the ROWID column
** unchanged.)
**
** This routine is part of the flattening procedure. A subquery
** whose result set is defined by pEList appears as entry in the
** FROM clause of a SELECT such that the VDBE cursor assigned to that
-** FORM clause entry is iTable. This routine makes the necessary
+** FORM clause entry is iTable. This routine makes the necessary
** changes to pExpr so that it refers directly to the source table
** of the subquery rather the result set of the subquery.
*/
@@ -127343,41 +145572,81 @@ static Expr *substExpr(
Expr *pExpr /* Expr in which substitution occurs */
){
if( pExpr==0 ) return 0;
- if( ExprHasProperty(pExpr, EP_FromJoin)
- && pExpr->iRightJoinTable==pSubst->iTable
+ if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON)
+ && pExpr->w.iJoin==pSubst->iTable
){
- pExpr->iRightJoinTable = pSubst->iNewTable;
+ testcase( ExprHasProperty(pExpr, EP_InnerON) );
+ pExpr->w.iJoin = pSubst->iNewTable;
}
- if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){
+ if( pExpr->op==TK_COLUMN
+ && pExpr->iTable==pSubst->iTable
+ && !ExprHasProperty(pExpr, EP_FixedCol)
+ ){
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
if( pExpr->iColumn<0 ){
pExpr->op = TK_NULL;
- }else{
+ }else
+#endif
+ {
Expr *pNew;
- Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr;
+ int iColumn;
+ Expr *pCopy;
Expr ifNullRow;
- assert( pSubst->pEList!=0 && pExpr->iColumn<pSubst->pEList->nExpr );
+ iColumn = pExpr->iColumn;
+ assert( iColumn>=0 );
+ assert( pSubst->pEList!=0 && iColumn<pSubst->pEList->nExpr );
assert( pExpr->pRight==0 );
+ pCopy = pSubst->pEList->a[iColumn].pExpr;
if( sqlite3ExprIsVector(pCopy) ){
sqlite3VectorErrorMsg(pSubst->pParse, pCopy);
}else{
sqlite3 *db = pSubst->pParse->db;
- if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){
+ if( pSubst->isOuterJoin
+ && (pCopy->op!=TK_COLUMN || pCopy->iTable!=pSubst->iNewTable)
+ ){
memset(&ifNullRow, 0, sizeof(ifNullRow));
ifNullRow.op = TK_IF_NULL_ROW;
ifNullRow.pLeft = pCopy;
ifNullRow.iTable = pSubst->iNewTable;
+ ifNullRow.iColumn = -99;
+ ifNullRow.flags = EP_IfNullRow;
pCopy = &ifNullRow;
}
+ testcase( ExprHasProperty(pCopy, EP_Subquery) );
pNew = sqlite3ExprDup(db, pCopy, 0);
- if( pNew && pSubst->isLeftJoin ){
+ if( db->mallocFailed ){
+ sqlite3ExprDelete(db, pNew);
+ return pExpr;
+ }
+ if( pSubst->isOuterJoin ){
ExprSetProperty(pNew, EP_CanBeNull);
}
- if( pNew && ExprHasProperty(pExpr,EP_FromJoin) ){
- pNew->iRightJoinTable = pExpr->iRightJoinTable;
- ExprSetProperty(pNew, EP_FromJoin);
+ if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
+ sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
+ pExpr->flags & (EP_OuterON|EP_InnerON));
}
sqlite3ExprDelete(db, pExpr);
pExpr = pNew;
+ if( pExpr->op==TK_TRUEFALSE ){
+ pExpr->u.iValue = sqlite3ExprTruthValue(pExpr);
+ pExpr->op = TK_INTEGER;
+ ExprSetProperty(pExpr, EP_IntValue);
+ }
+
+ /* Ensure that the expression now has an implicit collation sequence,
+ ** just as it did when it was a column of a view or sub-query. */
+ {
+ CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pExpr);
+ CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
+ pSubst->pCList->a[iColumn].pExpr
+ );
+ if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){
+ pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
+ (pColl ? pColl->zName : "BINARY")
+ );
+ }
+ }
+ ExprClearProperty(pExpr, EP_Collate);
}
}
}else{
@@ -127386,11 +145655,19 @@ static Expr *substExpr(
}
pExpr->pLeft = substExpr(pSubst, pExpr->pLeft);
pExpr->pRight = substExpr(pSubst, pExpr->pRight);
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ if( ExprUseXSelect(pExpr) ){
substSelect(pSubst, pExpr->x.pSelect, 1);
}else{
substExprList(pSubst, pExpr->x.pList);
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ Window *pWin = pExpr->y.pWin;
+ pWin->pFilter = substExpr(pSubst, pWin->pFilter);
+ substExprList(pSubst, pWin->pPartition);
+ substExprList(pSubst, pWin->pOrderBy);
+ }
+#endif
}
return pExpr;
}
@@ -127410,7 +145687,7 @@ static void substSelect(
int doPrior /* Do substitutes on p->pPrior too */
){
SrcList *pSrc;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
int i;
if( !p ) return;
do{
@@ -127433,6 +145710,175 @@ static void substSelect(
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
+** pSelect is a SELECT statement and pSrcItem is one item in the FROM
+** clause of that SELECT.
+**
+** This routine scans the entire SELECT statement and recomputes the
+** pSrcItem->colUsed mask.
+*/
+static int recomputeColumnsUsedExpr(Walker *pWalker, Expr *pExpr){
+ SrcItem *pItem;
+ if( pExpr->op!=TK_COLUMN ) return WRC_Continue;
+ pItem = pWalker->u.pSrcItem;
+ if( pItem->iCursor!=pExpr->iTable ) return WRC_Continue;
+ if( pExpr->iColumn<0 ) return WRC_Continue;
+ pItem->colUsed |= sqlite3ExprColUsed(pExpr);
+ return WRC_Continue;
+}
+static void recomputeColumnsUsed(
+ Select *pSelect, /* The complete SELECT statement */
+ SrcItem *pSrcItem /* Which FROM clause item to recompute */
+){
+ Walker w;
+ if( NEVER(pSrcItem->pTab==0) ) return;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = recomputeColumnsUsedExpr;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
+ w.u.pSrcItem = pSrcItem;
+ pSrcItem->colUsed = 0;
+ sqlite3WalkSelect(&w, pSelect);
+}
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+/*
+** Assign new cursor numbers to each of the items in pSrc. For each
+** new cursor number assigned, set an entry in the aCsrMap[] array
+** to map the old cursor number to the new:
+**
+** aCsrMap[iOld+1] = iNew;
+**
+** The array is guaranteed by the caller to be large enough for all
+** existing cursor numbers in pSrc. aCsrMap[0] is the array size.
+**
+** If pSrc contains any sub-selects, call this routine recursively
+** on the FROM clause of each such sub-select, with iExcept set to -1.
+*/
+static void srclistRenumberCursors(
+ Parse *pParse, /* Parse context */
+ int *aCsrMap, /* Array to store cursor mappings in */
+ SrcList *pSrc, /* FROM clause to renumber */
+ int iExcept /* FROM clause item to skip */
+){
+ int i;
+ SrcItem *pItem;
+ for(i=0, pItem=pSrc->a; i<pSrc->nSrc; i++, pItem++){
+ if( i!=iExcept ){
+ Select *p;
+ assert( pItem->iCursor < aCsrMap[0] );
+ if( !pItem->fg.isRecursive || aCsrMap[pItem->iCursor+1]==0 ){
+ aCsrMap[pItem->iCursor+1] = pParse->nTab++;
+ }
+ pItem->iCursor = aCsrMap[pItem->iCursor+1];
+ for(p=pItem->pSelect; p; p=p->pPrior){
+ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1);
+ }
+ }
+ }
+}
+
+/*
+** *piCursor is a cursor number. Change it if it needs to be mapped.
+*/
+static void renumberCursorDoMapping(Walker *pWalker, int *piCursor){
+ int *aCsrMap = pWalker->u.aiCol;
+ int iCsr = *piCursor;
+ if( iCsr < aCsrMap[0] && aCsrMap[iCsr+1]>0 ){
+ *piCursor = aCsrMap[iCsr+1];
+ }
+}
+
+/*
+** Expression walker callback used by renumberCursors() to update
+** Expr objects to match newly assigned cursor numbers.
+*/
+static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){
+ int op = pExpr->op;
+ if( op==TK_COLUMN || op==TK_IF_NULL_ROW ){
+ renumberCursorDoMapping(pWalker, &pExpr->iTable);
+ }
+ if( ExprHasProperty(pExpr, EP_OuterON) ){
+ renumberCursorDoMapping(pWalker, &pExpr->w.iJoin);
+ }
+ return WRC_Continue;
+}
+
+/*
+** Assign a new cursor number to each cursor in the FROM clause (Select.pSrc)
+** of the SELECT statement passed as the second argument, and to each
+** cursor in the FROM clause of any FROM clause sub-selects, recursively.
+** Except, do not assign a new cursor number to the iExcept'th element in
+** the FROM clause of (*p). Update all expressions and other references
+** to refer to the new cursor numbers.
+**
+** Argument aCsrMap is an array that may be used for temporary working
+** space. Two guarantees are made by the caller:
+**
+** * the array is larger than the largest cursor number used within the
+** select statement passed as an argument, and
+**
+** * the array entries for all cursor numbers that do *not* appear in
+** FROM clauses of the select statement as described above are
+** initialized to zero.
+*/
+static void renumberCursors(
+ Parse *pParse, /* Parse context */
+ Select *p, /* Select to renumber cursors within */
+ int iExcept, /* FROM clause item to skip */
+ int *aCsrMap /* Working space */
+){
+ Walker w;
+ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, iExcept);
+ memset(&w, 0, sizeof(w));
+ w.u.aiCol = aCsrMap;
+ w.xExprCallback = renumberCursorsCb;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
+ sqlite3WalkSelect(&w, p);
+}
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+
+/*
+** If pSel is not part of a compound SELECT, return a pointer to its
+** expression list. Otherwise, return a pointer to the expression list
+** of the leftmost SELECT in the compound.
+*/
+static ExprList *findLeftmostExprlist(Select *pSel){
+ while( pSel->pPrior ){
+ pSel = pSel->pPrior;
+ }
+ return pSel->pEList;
+}
+
+/*
+** Return true if any of the result-set columns in the compound query
+** have incompatible affinities on one or more arms of the compound.
+*/
+static int compoundHasDifferentAffinities(Select *p){
+ int ii;
+ ExprList *pList;
+ assert( p!=0 );
+ assert( p->pEList!=0 );
+ assert( p->pPrior!=0 );
+ pList = p->pEList;
+ for(ii=0; ii<pList->nExpr; ii++){
+ char aff;
+ Select *pSub1;
+ assert( pList->a[ii].pExpr!=0 );
+ aff = sqlite3ExprAffinity(pList->a[ii].pExpr);
+ for(pSub1=p->pPrior; pSub1; pSub1=pSub1->pPrior){
+ assert( pSub1->pEList!=0 );
+ assert( pSub1->pEList->nExpr>ii );
+ assert( pSub1->pEList->a[ii].pExpr!=0 );
+ if( sqlite3ExprAffinity(pSub1->pEList->a[ii].pExpr)!=aff ){
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+/*
** This routine attempts to flatten subqueries as a performance optimization.
** This routine returns 1 if it makes changes and 0 if no flattening occurs.
**
@@ -127454,7 +145900,7 @@ static void substSelect(
** SELECT x+y AS a FROM t1 WHERE z<100 AND a>5
**
** The code generated for this simplification gives the same result
-** but only has to scan the data once. And because indices might
+** but only has to scan the data once. And because indices might
** exist on the table t1, a complete scan of the data might be
** avoided.
**
@@ -127475,12 +145921,15 @@ static void substSelect(
** (3a) the subquery may not be a join and
** (3b) the FROM clause of the subquery may not contain a virtual
** table and
-** (3c) the outer query may not be an aggregate.
+** (**) Was: "The outer query may not have a GROUP BY." This case
+** is now managed correctly
+** (3d) the outer query may not be DISTINCT.
+** See also (26) for restrictions on RIGHT JOIN.
**
** (4) The subquery can not be DISTINCT.
**
** (**) At one point restrictions (4) and (5) defined a subset of DISTINCT
-** sub-queries that were excluded from this optimization. Restriction
+** sub-queries that were excluded from this optimization. Restriction
** (4) has since been expanded to exclude all DISTINCT subqueries.
**
** (**) We no longer attempt to flatten aggregate subqueries. Was:
@@ -127496,8 +145945,8 @@ static void substSelect(
** (9) If the subquery uses LIMIT then the outer query may not be aggregate.
**
** (**) Restriction (10) was removed from the code on 2005-02-05 but we
-** accidently carried the comment forward until 2014-09-15. Original
-** constraint: "If the subquery is aggregate then the outer query
+** accidentally carried the comment forward until 2014-09-15. Original
+** constraint: "If the subquery is aggregate then the outer query
** may not use LIMIT."
**
** (11) The subquery and the outer query may not both have ORDER BY clauses.
@@ -127515,7 +145964,7 @@ static void substSelect(
**
** (16) If the outer query is aggregate, then the subquery may not
** use ORDER BY. (Ticket #2942) This used to not matter
-** until we introduced the group_concat() function.
+** until we introduced the group_concat() function.
**
** (17) If the subquery is a compound select, then
** (17a) all compound operators must be a UNION ALL, and
@@ -127524,8 +145973,14 @@ static void substSelect(
** (17c) every term within the subquery compound must have a FROM clause
** (17d) the outer query may not be
** (17d1) aggregate, or
-** (17d2) DISTINCT, or
-** (17d3) a join.
+** (17d2) DISTINCT
+** (17e) the subquery may not contain window functions, and
+** (17f) the subquery must not be the RHS of a LEFT JOIN.
+** (17g) either the subquery is the first element of the outer
+** query or there are no RIGHT or FULL JOINs in any arm
+** of the subquery. (This is a duplicate of condition (27b).)
+** (17h) The corresponding result set expressions in all arms of the
+** compound must have the same affinity.
**
** The parent and sub-query may contain WHERE clauses. Subject to
** rules (11), (13) and (14), they may also contain ORDER BY,
@@ -127541,8 +145996,8 @@ static void substSelect(
** syntax error and return a detailed message.
**
** (18) If the sub-query is a compound select, then all terms of the
-** ORDER BY clause of the parent must be simple references to
-** columns of the sub-query.
+** ORDER BY clause of the parent must be copies of a term returned
+** by the parent query.
**
** (19) If the subquery uses LIMIT then the outer query may not
** have a WHERE clause.
@@ -127558,14 +146013,13 @@ static void substSelect(
**
** (22) The subquery may not be a recursive CTE.
**
-** (**) Subsumed into restriction (17d3). Was: If the outer query is
-** a recursive CTE, then the sub-query may not be a compound query.
-** This restriction is because transforming the
+** (23) If the outer query is a recursive CTE, then the sub-query may not be
+** a compound query. This restriction is because transforming the
** parent to a compound query confuses the code that handles
** recursive queries in multiSelect().
**
** (**) We no longer attempt to flatten aggregate subqueries. Was:
-** The subquery may not be an aggregate that uses the built-in min() or
+** The subquery may not be an aggregate that uses the built-in min() or
** or max() functions. (Without this restriction, a query like:
** "SELECT x FROM (SELECT max(y), x FROM t1)" would not necessarily
** return the value X for which Y was maximal.)
@@ -127574,6 +146028,18 @@ static void substSelect(
** function in the select list or ORDER BY clause, flattening
** is not attempted.
**
+** (26) The subquery may not be the right operand of a RIGHT JOIN.
+** See also (3) for restrictions on LEFT JOIN.
+**
+** (27) The subquery may not contain a FULL or RIGHT JOIN unless it
+** is the first element of the parent query. Two subcases:
+** (27a) the subquery is not a compound query.
+** (27b) the subquery is a compound query and the RIGHT JOIN occurs
+** in any arm of the compound query. (See also (17g).)
+**
+** (28) The subquery is not a MATERIALIZED CTE. (This is handled
+** in the caller before ever reaching this routine.)
+**
**
** In this routine, the "p" parameter is a pointer to the outer query.
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
@@ -127599,11 +146065,13 @@ static int flattenSubquery(
SrcList *pSubSrc; /* The FROM clause of the subquery */
int iParent; /* VDBE cursor number of the pSub result set temp table */
int iNewParent = -1;/* Replacement table for iParent */
- int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */
+ int isOuterJoin = 0; /* True if pSub is the right side of a LEFT JOIN */
int i; /* Loop counter */
Expr *pWhere; /* The WHERE clause */
- struct SrcList_item *pSubitem; /* The subquery */
+ SrcItem *pSubitem; /* The subquery */
sqlite3 *db = pParse->db;
+ Walker w; /* Walker to persist agginfo data */
+ int *aCsrMap = 0;
/* Check to see if flattening is permitted. Return 0 if not.
*/
@@ -127663,29 +146131,26 @@ static int flattenSubquery(
**
** which is not at all the same thing.
**
- ** If the subquery is the right operand of a LEFT JOIN, then the outer
- ** query cannot be an aggregate. (3c) This is an artifact of the way
- ** aggregates are processed - there is no mechanism to determine if
- ** the LEFT JOIN table should be all-NULL.
- **
** See also tickets #306, #350, and #3300.
*/
- if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){
- isLeftJoin = 1;
- if( pSubSrc->nSrc>1 || isAgg || IsVirtual(pSubSrc->a[0].pTab) ){
- /* (3a) (3c) (3b) */
+ if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){
+ if( pSubSrc->nSrc>1 /* (3a) */
+ || IsVirtual(pSubSrc->a[0].pTab) /* (3b) */
+ || (p->selFlags & SF_Distinct)!=0 /* (3d) */
+ || (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */
+ ){
return 0;
}
+ isOuterJoin = 1;
}
-#ifdef SQLITE_EXTRA_IFNULLROW
- else if( iFrom>0 && !isAgg ){
- /* Setting isLeftJoin to -1 causes OP_IfNullRow opcodes to be generated for
- ** every reference to any result column from subquery in a join, even
- ** though they are not necessary. This will stress-test the OP_IfNullRow
- ** opcode. */
- isLeftJoin = -1;
+
+ assert( pSubSrc->nSrc>0 ); /* True by restriction (7) */
+ if( iFrom>0 && (pSubSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
+ return 0; /* Restriction (27a) */
}
-#endif
+
+ /* Condition (28) is blocked by the caller */
+ assert( !pSubitem->fg.isCte || pSubitem->u2.pCteUse->eM10d!=M10d_Yes );
/* Restriction (17): If the sub-query is a compound SELECT, then it must
** use only the UNION ALL operator. And none of the simple select queries
@@ -127693,45 +146158,60 @@ static int flattenSubquery(
** queries.
*/
if( pSub->pPrior ){
+ int ii;
if( pSub->pOrderBy ){
return 0; /* Restriction (20) */
}
- if( isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){
- return 0; /* (17d1), (17d2), or (17d3) */
+ if( isAgg || (p->selFlags & SF_Distinct)!=0 || isOuterJoin>0 ){
+ return 0; /* (17d1), (17d2), or (17f) */
}
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
assert( pSub->pSrc!=0 );
+ assert( (pSub->selFlags & SF_Recursive)==0 );
assert( pSub->pEList->nExpr==pSub1->pEList->nExpr );
if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0 /* (17b) */
|| (pSub1->pPrior && pSub1->op!=TK_ALL) /* (17a) */
|| pSub1->pSrc->nSrc<1 /* (17c) */
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ || pSub1->pWin /* (17e) */
+#endif
){
return 0;
}
+ if( iFrom>0 && (pSub1->pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
+ /* Without this restriction, the JT_LTORJ flag would end up being
+ ** omitted on left-hand tables of the right join that is being
+ ** flattened. */
+ return 0; /* Restrictions (17g), (27b) */
+ }
testcase( pSub1->pSrc->nSrc>1 );
}
/* Restriction (18). */
if( p->pOrderBy ){
- int ii;
for(ii=0; ii<p->pOrderBy->nExpr; ii++){
if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0;
}
}
- }
- /* Ex-restriction (23):
- ** The only way that the recursive part of a CTE can contain a compound
- ** subquery is for the subquery to be one term of a join. But if the
- ** subquery is a join, then the flattening has already been stopped by
- ** restriction (17d3)
- */
- assert( (p->selFlags & SF_Recursive)==0 || pSub->pPrior==0 );
+ /* Restriction (23) */
+ if( (p->selFlags & SF_Recursive) ) return 0;
+
+ /* Restriction (17h) */
+ if( compoundHasDifferentAffinities(pSub) ) return 0;
+
+ if( pSrc->nSrc>1 ){
+ if( pParse->nSelect>500 ) return 0;
+ if( OptimizationDisabled(db, SQLITE_FlttnUnionAll) ) return 0;
+ aCsrMap = sqlite3DbMallocZero(db, ((i64)pParse->nTab+1)*sizeof(int));
+ if( aCsrMap ) aCsrMap[0] = pParse->nTab;
+ }
+ }
/***** If we reach this point, flattening is permitted. *****/
- SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n",
+ TREETRACE(0x4,pParse,p,("flatten %u.%p from term %d\n",
pSub->selId, pSub, iFrom));
/* Authorize the subquery */
@@ -127740,14 +146220,25 @@ static int flattenSubquery(
testcase( i==SQLITE_DENY );
pParse->zAuthContext = zSavedAuthContext;
+ /* Delete the transient structures associated with the subquery */
+ pSub1 = pSubitem->pSelect;
+ sqlite3DbFree(db, pSubitem->zDatabase);
+ sqlite3DbFree(db, pSubitem->zName);
+ sqlite3DbFree(db, pSubitem->zAlias);
+ pSubitem->zDatabase = 0;
+ pSubitem->zName = 0;
+ pSubitem->zAlias = 0;
+ pSubitem->pSelect = 0;
+ assert( pSubitem->fg.isUsing!=0 || pSubitem->u3.pOn==0 );
+
/* If the sub-query is a compound SELECT statement, then (by restrictions
- ** 17 and 18 above) it must be a UNION ALL and the parent query must
+ ** 17 and 18 above) it must be a UNION ALL and the parent query must
** be of the form:
**
- ** SELECT <expr-list> FROM (<sub-query>) <where-clause>
+ ** SELECT <expr-list> FROM (<sub-query>) <where-clause>
**
** followed by any ORDER BY, LIMIT and/or OFFSET clauses. This block
- ** creates N-1 copies of the parent query without any ORDER BY, LIMIT or
+ ** creates N-1 copies of the parent query without any ORDER BY, LIMIT or
** OFFSET clauses and joins them to the left-hand-side of the original
** using UNION ALL operators. In this case N is the number of simple
** select statements in the compound sub-query.
@@ -127778,43 +146269,37 @@ static int flattenSubquery(
ExprList *pOrderBy = p->pOrderBy;
Expr *pLimit = p->pLimit;
Select *pPrior = p->pPrior;
+ Table *pItemTab = pSubitem->pTab;
+ pSubitem->pTab = 0;
p->pOrderBy = 0;
- p->pSrc = 0;
p->pPrior = 0;
p->pLimit = 0;
pNew = sqlite3SelectDup(db, p, 0);
p->pLimit = pLimit;
p->pOrderBy = pOrderBy;
- p->pSrc = pSrc;
p->op = TK_ALL;
+ pSubitem->pTab = pItemTab;
if( pNew==0 ){
p->pPrior = pPrior;
}else{
+ pNew->selId = ++pParse->nSelect;
+ if( aCsrMap && ALWAYS(db->mallocFailed==0) ){
+ renumberCursors(pParse, pNew, iFrom, aCsrMap);
+ }
pNew->pPrior = pPrior;
if( pPrior ) pPrior->pNext = pNew;
pNew->pNext = p;
p->pPrior = pNew;
- SELECTTRACE(2,pParse,p,("compound-subquery flattener"
+ TREETRACE(0x4,pParse,p,("compound-subquery flattener"
" creates %u as peer\n",pNew->selId));
}
- if( db->mallocFailed ) return 1;
+ assert( pSubitem->pSelect==0 );
+ }
+ sqlite3DbFree(db, aCsrMap);
+ if( db->mallocFailed ){
+ pSubitem->pSelect = pSub1;
+ return 1;
}
-
- /* Begin flattening the iFrom-th entry of the FROM clause
- ** in the outer query.
- */
- pSub = pSub1 = pSubitem->pSelect;
-
- /* Delete the transient table structure associated with the
- ** subquery
- */
- sqlite3DbFree(db, pSubitem->zDatabase);
- sqlite3DbFree(db, pSubitem->zName);
- sqlite3DbFree(db, pSubitem->zAlias);
- pSubitem->zDatabase = 0;
- pSubitem->zName = 0;
- pSubitem->zAlias = 0;
- pSubitem->pSelect = 0;
/* Defer deleting the Table object associated with the
** subquery until code generation is
@@ -127827,8 +146312,8 @@ static int flattenSubquery(
Table *pTabToDel = pSubitem->pTab;
if( pTabToDel->nTabRef==1 ){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
- pTabToDel->pNextZombie = pToplevel->pZombieTab;
- pToplevel->pZombieTab = pTabToDel;
+ sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel);
+ testcase( pToplevel->earlyCleanup );
}else{
pTabToDel->nTabRef--;
}
@@ -127848,23 +146333,18 @@ static int flattenSubquery(
** those references with expressions that resolve to the subquery FROM
** elements we are now copying in.
*/
+ pSub = pSub1;
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
int nSubSrc;
u8 jointype = 0;
+ u8 ltorj = pSrc->a[iFrom].fg.jointype & JT_LTORJ;
+ assert( pSub!=0 );
pSubSrc = pSub->pSrc; /* FROM clause of subquery */
nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
pSrc = pParent->pSrc; /* FROM clause of the outer query */
- if( pSrc ){
- assert( pParent==p ); /* First time through the loop */
- jointype = pSubitem->fg.jointype;
- }else{
- assert( pParent!=p ); /* 2nd and subsequent times through the loop */
- pSrc = pParent->pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
- if( pSrc==0 ){
- assert( db->mallocFailed );
- break;
- }
+ if( pParent==p ){
+ jointype = pSubitem->fg.jointype; /* First time through the loop */
}
/* The subquery uses a single slot of the FROM clause of the outer
@@ -127883,27 +146363,29 @@ static int flattenSubquery(
** for the two elements in the FROM clause of the subquery.
*/
if( nSubSrc>1 ){
- pParent->pSrc = pSrc = sqlite3SrcListEnlarge(db, pSrc, nSubSrc-1,iFrom+1);
- if( db->mallocFailed ){
- break;
- }
+ pSrc = sqlite3SrcListEnlarge(pParse, pSrc, nSubSrc-1,iFrom+1);
+ if( pSrc==0 ) break;
+ pParent->pSrc = pSrc;
}
/* Transfer the FROM clause terms from the subquery into the
** outer query.
*/
for(i=0; i<nSubSrc; i++){
- sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing);
- assert( pSrc->a[i+iFrom].fg.isTabFunc==0 );
- pSrc->a[i+iFrom] = pSubSrc->a[i];
+ SrcItem *pItem = &pSrc->a[i+iFrom];
+ if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
+ assert( pItem->fg.isTabFunc==0 );
+ *pItem = pSubSrc->a[i];
+ pItem->fg.jointype |= ltorj;
iNewParent = pSubSrc->a[i].iCursor;
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
- pSrc->a[iFrom].fg.jointype = jointype;
-
- /* Now begin substituting subquery result set expressions for
+ pSrc->a[iFrom].fg.jointype &= JT_LTORJ;
+ pSrc->a[iFrom].fg.jointype |= jointype | ltorj;
+
+ /* Now begin substituting subquery result set expressions for
** references to the iParent in the outer query.
- **
+ **
** Example:
**
** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b;
@@ -127913,12 +146395,12 @@ static int flattenSubquery(
** We look at every expression in the outer query and every place we see
** "a" we substitute "x*3" and every place we see "b" we substitute "y+10".
*/
- if( pSub->pOrderBy ){
+ if( pSub->pOrderBy && (pParent->selFlags & SF_NoopOrderBy)==0 ){
/* At this point, any non-zero iOrderByCol values indicate that the
** ORDER BY column expression is identical to the iOrderByCol'th
** expression returned by SELECT statement pSub. Since these values
** do not necessarily correspond to columns in SELECT statement pParent,
- ** zero them before transfering the ORDER BY clause.
+ ** zero them before transferring the ORDER BY clause.
**
** Not doing this may cause an error if a subsequent call to this
** function attempts to flatten a compound sub-query into pParent
@@ -127932,26 +146414,34 @@ static int flattenSubquery(
pParent->pOrderBy = pOrderBy;
pSub->pOrderBy = 0;
}
- pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
- if( isLeftJoin>0 ){
- setJoinExpr(pWhere, iNewParent);
+ pWhere = pSub->pWhere;
+ pSub->pWhere = 0;
+ if( isOuterJoin>0 ){
+ sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON);
+ }
+ if( pWhere ){
+ if( pParent->pWhere ){
+ pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere);
+ }else{
+ pParent->pWhere = pWhere;
+ }
}
- pParent->pWhere = sqlite3ExprAnd(db, pWhere, pParent->pWhere);
if( db->mallocFailed==0 ){
SubstContext x;
x.pParse = pParse;
x.iTable = iParent;
x.iNewTable = iNewParent;
- x.isLeftJoin = isLeftJoin;
+ x.isOuterJoin = isOuterJoin;
x.pEList = pSub->pEList;
+ x.pCList = findLeftmostExprlist(pSub);
substSelect(&x, pParent, 0);
}
-
- /* The flattened query is distinct if either the inner or the
- ** outer query is distinct.
- */
- pParent->selFlags |= pSub->selFlags & SF_Distinct;
-
+
+ /* The flattened query is a compound if either the inner or the
+ ** outer query is a compound. */
+ pParent->selFlags |= pSub->selFlags & SF_Compound;
+ assert( (pSub->selFlags & SF_Distinct)==0 ); /* restriction (17b) */
+
/*
** SELECT ... FROM (SELECT ... LIMIT a OFFSET b) LIMIT x OFFSET y;
**
@@ -127962,16 +146452,23 @@ static int flattenSubquery(
pParent->pLimit = pSub->pLimit;
pSub->pLimit = 0;
}
+
+ /* Recompute the SrcItem.colUsed masks for the flattened
+ ** tables. */
+ for(i=0; i<nSubSrc; i++){
+ recomputeColumnsUsed(pParent, &pSrc->a[i+iFrom]);
+ }
}
- /* Finially, delete what is left of the subquery and return
- ** success.
+ /* Finally, delete what is left of the subquery and return success.
*/
+ sqlite3AggInfoPersistWalkerInit(&w, pParse);
+ sqlite3WalkSelect(&w,pSub1);
sqlite3SelectDelete(db, pSub1);
-#if SELECTTRACE_ENABLED
- if( sqlite3SelectTrace & 0x100 ){
- SELECTTRACE(0x100,pParse,p,("After flattening:\n"));
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x4 ){
+ TREETRACE(0x4,pParse,p,("After flattening:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -127987,34 +146484,53 @@ static int flattenSubquery(
typedef struct WhereConst WhereConst;
struct WhereConst {
Parse *pParse; /* Parsing context */
+ u8 *pOomFault; /* Pointer to pParse->db->mallocFailed */
int nConst; /* Number for COLUMN=CONSTANT terms */
int nChng; /* Number of times a constant is propagated */
+ int bHasAffBlob; /* At least one column in apExpr[] as affinity BLOB */
+ u32 mExcludeOn; /* Which ON expressions to exclude from considertion.
+ ** Either EP_OuterON or EP_InnerON|EP_OuterON */
Expr **apExpr; /* [i*2] is COLUMN and [i*2+1] is VALUE */
};
/*
** Add a new entry to the pConst object. Except, do not add duplicate
-** pColumn entires.
+** pColumn entries. Also, do not add if doing so would not be appropriate.
+**
+** The caller guarantees the pColumn is a column and pValue is a constant.
+** This routine has to do some additional checks before completing the
+** insert.
*/
static void constInsert(
- WhereConst *pConst, /* The WhereConst into which we are inserting */
- Expr *pColumn, /* The COLUMN part of the constraint */
- Expr *pValue /* The VALUE part of the constraint */
+ WhereConst *pConst, /* The WhereConst into which we are inserting */
+ Expr *pColumn, /* The COLUMN part of the constraint */
+ Expr *pValue, /* The VALUE part of the constraint */
+ Expr *pExpr /* Overall expression: COLUMN=VALUE or VALUE=COLUMN */
){
int i;
assert( pColumn->op==TK_COLUMN );
+ assert( sqlite3ExprIsConstant(pValue) );
+
+ if( ExprHasProperty(pColumn, EP_FixedCol) ) return;
+ if( sqlite3ExprAffinity(pValue)!=0 ) return;
+ if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pConst->pParse,pExpr)) ){
+ return;
+ }
/* 2018-10-25 ticket [cf5ed20f]
** Make sure the same pColumn is not inserted more than once */
for(i=0; i<pConst->nConst; i++){
- const Expr *pExpr = pConst->apExpr[i*2];
- assert( pExpr->op==TK_COLUMN );
- if( pExpr->iTable==pColumn->iTable
- && pExpr->iColumn==pColumn->iColumn
+ const Expr *pE2 = pConst->apExpr[i*2];
+ assert( pE2->op==TK_COLUMN );
+ if( pE2->iTable==pColumn->iTable
+ && pE2->iColumn==pColumn->iColumn
){
return; /* Already present. Return without doing anything. */
}
}
+ if( sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){
+ pConst->bHasAffBlob = 1;
+ }
pConst->nConst++;
pConst->apExpr = sqlite3DbReallocOrFree(pConst->pParse->db, pConst->apExpr,
@@ -128022,7 +146538,6 @@ static void constInsert(
if( pConst->apExpr==0 ){
pConst->nConst = 0;
}else{
- if( ExprHasProperty(pValue, EP_FixedCol) ) pValue = pValue->pLeft;
pConst->apExpr[pConst->nConst*2-2] = pColumn;
pConst->apExpr[pConst->nConst*2-1] = pValue;
}
@@ -128036,8 +146551,12 @@ static void constInsert(
*/
static void findConstInWhere(WhereConst *pConst, Expr *pExpr){
Expr *pRight, *pLeft;
- if( pExpr==0 ) return;
- if( ExprHasProperty(pExpr, EP_FromJoin) ) return;
+ if( NEVER(pExpr==0) ) return;
+ if( ExprHasProperty(pExpr, pConst->mExcludeOn) ){
+ testcase( ExprHasProperty(pExpr, EP_OuterON) );
+ testcase( ExprHasProperty(pExpr, EP_InnerON) );
+ return;
+ }
if( pExpr->op==TK_AND ){
findConstInWhere(pConst, pExpr->pRight);
findConstInWhere(pConst, pExpr->pLeft);
@@ -128048,58 +146567,100 @@ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){
pLeft = pExpr->pLeft;
assert( pRight!=0 );
assert( pLeft!=0 );
- if( pRight->op==TK_COLUMN
- && !ExprHasProperty(pRight, EP_FixedCol)
- && sqlite3ExprIsConstant(pLeft)
- && sqlite3IsBinary(sqlite3BinaryCompareCollSeq(pConst->pParse,pLeft,pRight))
- ){
- constInsert(pConst, pRight, pLeft);
- }else
- if( pLeft->op==TK_COLUMN
- && !ExprHasProperty(pLeft, EP_FixedCol)
- && sqlite3ExprIsConstant(pRight)
- && sqlite3IsBinary(sqlite3BinaryCompareCollSeq(pConst->pParse,pLeft,pRight))
- ){
- constInsert(pConst, pLeft, pRight);
+ if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pLeft) ){
+ constInsert(pConst,pRight,pLeft,pExpr);
+ }
+ if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pRight) ){
+ constInsert(pConst,pLeft,pRight,pExpr);
}
}
/*
-** This is a Walker expression callback. pExpr is a candidate expression
-** to be replaced by a value. If pExpr is equivalent to one of the
-** columns named in pWalker->u.pConst, then overwrite it with its
-** corresponding value.
+** This is a helper function for Walker callback propagateConstantExprRewrite().
+**
+** Argument pExpr is a candidate expression to be replaced by a value. If
+** pExpr is equivalent to one of the columns named in pWalker->u.pConst,
+** then overwrite it with the corresponding value. Except, do not do so
+** if argument bIgnoreAffBlob is non-zero and the affinity of pExpr
+** is SQLITE_AFF_BLOB.
*/
-static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){
+static int propagateConstantExprRewriteOne(
+ WhereConst *pConst,
+ Expr *pExpr,
+ int bIgnoreAffBlob
+){
int i;
- WhereConst *pConst;
+ if( pConst->pOomFault[0] ) return WRC_Prune;
if( pExpr->op!=TK_COLUMN ) return WRC_Continue;
- if( ExprHasProperty(pExpr, EP_FixedCol) ) return WRC_Continue;
- pConst = pWalker->u.pConst;
+ if( ExprHasProperty(pExpr, EP_FixedCol|pConst->mExcludeOn) ){
+ testcase( ExprHasProperty(pExpr, EP_FixedCol) );
+ testcase( ExprHasProperty(pExpr, EP_OuterON) );
+ testcase( ExprHasProperty(pExpr, EP_InnerON) );
+ return WRC_Continue;
+ }
for(i=0; i<pConst->nConst; i++){
Expr *pColumn = pConst->apExpr[i*2];
if( pColumn==pExpr ) continue;
if( pColumn->iTable!=pExpr->iTable ) continue;
if( pColumn->iColumn!=pExpr->iColumn ) continue;
+ if( bIgnoreAffBlob && sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){
+ break;
+ }
/* A match is found. Add the EP_FixedCol property */
pConst->nChng++;
ExprClearProperty(pExpr, EP_Leaf);
ExprSetProperty(pExpr, EP_FixedCol);
assert( pExpr->pLeft==0 );
pExpr->pLeft = sqlite3ExprDup(pConst->pParse->db, pConst->apExpr[i*2+1], 0);
+ if( pConst->pParse->db->mallocFailed ) return WRC_Prune;
break;
}
return WRC_Prune;
}
/*
+** This is a Walker expression callback. pExpr is a node from the WHERE
+** clause of a SELECT statement. This function examines pExpr to see if
+** any substitutions based on the contents of pWalker->u.pConst should
+** be made to pExpr or its immediate children.
+**
+** A substitution is made if:
+**
+** + pExpr is a column with an affinity other than BLOB that matches
+** one of the columns in pWalker->u.pConst, or
+**
+** + pExpr is a binary comparison operator (=, <=, >=, <, >) that
+** uses an affinity other than TEXT and one of its immediate
+** children is a column that matches one of the columns in
+** pWalker->u.pConst.
+*/
+static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){
+ WhereConst *pConst = pWalker->u.pConst;
+ assert( TK_GT==TK_EQ+1 );
+ assert( TK_LE==TK_EQ+2 );
+ assert( TK_LT==TK_EQ+3 );
+ assert( TK_GE==TK_EQ+4 );
+ if( pConst->bHasAffBlob ){
+ if( (pExpr->op>=TK_EQ && pExpr->op<=TK_GE)
+ || pExpr->op==TK_IS
+ ){
+ propagateConstantExprRewriteOne(pConst, pExpr->pLeft, 0);
+ if( pConst->pOomFault[0] ) return WRC_Prune;
+ if( sqlite3ExprAffinity(pExpr->pLeft)!=SQLITE_AFF_TEXT ){
+ propagateConstantExprRewriteOne(pConst, pExpr->pRight, 0);
+ }
+ }
+ }
+ return propagateConstantExprRewriteOne(pConst, pExpr, pConst->bHasAffBlob);
+}
+
+/*
** The WHERE-clause constant propagation optimization.
**
** If the WHERE clause contains terms of the form COLUMN=CONSTANT or
-** CONSTANT=COLUMN that must be tree (in other words, if the terms top-level
-** AND-connected terms that are not part of a ON clause from a LEFT JOIN)
-** then throughout the query replace all other occurrences of COLUMN
-** with CONSTANT within the WHERE clause.
+** CONSTANT=COLUMN that are top-level AND-connected terms that are not
+** part of a ON clause from a LEFT JOIN, then throughout the query
+** replace all other occurrences of COLUMN with CONSTANT.
**
** For example, the query:
**
@@ -128120,7 +146681,7 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){
** SELECT * FROM t1 WHERE a=123 AND b=123;
**
** The two SELECT statements above should return different answers. b=a
-** is alway true because the comparison uses numeric affinity, but b=123
+** is always true because the comparison uses numeric affinity, but b=123
** is false because it uses text affinity and '0123' is not the same as '123'.
** To work around this, the expression tree is not actually changed from
** "b=a" to "b=123" but rather the "a" in "b=a" is tagged with EP_FixedCol
@@ -128128,6 +146689,21 @@ static int propagateConstantExprRewrite(Walker *pWalker, Expr *pExpr){
** routines know to generate the constant "123" instead of looking up the
** column value. Also, to avoid collation problems, this optimization is
** only attempted if the "a=123" term uses the default BINARY collation.
+**
+** 2021-05-25 forum post 6a06202608: Another troublesome case is...
+**
+** CREATE TABLE t1(x);
+** INSERT INTO t1 VALUES(10.0);
+** SELECT 1 FROM t1 WHERE x=10 AND x LIKE 10;
+**
+** The query should return no rows, because the t1.x value is '10.0' not '10'
+** and '10.0' is not LIKE '10'. But if we are not careful, the first WHERE
+** term "x=10" will cause the second WHERE term to become "10 LIKE 10",
+** resulting in a false positive. To avoid this, constant propagation for
+** columns with BLOB affinity is only allowed if the constant is used with
+** operators ==, <=, <, >=, >, or IS in a way that will cause the correct
+** type conversions to occur. See logic associated with the bHasAffBlob flag
+** for details.
*/
static int propagateConstants(
Parse *pParse, /* The parsing context */
@@ -128137,10 +146713,23 @@ static int propagateConstants(
Walker w;
int nChng = 0;
x.pParse = pParse;
+ x.pOomFault = &pParse->db->mallocFailed;
do{
x.nConst = 0;
x.nChng = 0;
x.apExpr = 0;
+ x.bHasAffBlob = 0;
+ if( ALWAYS(p->pSrc!=0)
+ && p->pSrc->nSrc>0
+ && (p->pSrc->a[0].fg.jointype & JT_LTORJ)!=0
+ ){
+ /* Do not propagate constants on any ON clause if there is a
+ ** RIGHT JOIN anywhere in the query */
+ x.mExcludeOn = EP_InnerON | EP_OuterON;
+ }else{
+ /* Do not propagate constants through the ON clause of a LEFT JOIN */
+ x.mExcludeOn = EP_OuterON;
+ }
findConstInWhere(&x, p->pWhere);
if( x.nConst ){
memset(&w, 0, sizeof(w));
@@ -128154,11 +146743,40 @@ static int propagateConstants(
sqlite3DbFree(x.pParse->db, x.apExpr);
nChng += x.nChng;
}
- }while( x.nChng );
+ }while( x.nChng );
return nChng;
}
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+# if !defined(SQLITE_OMIT_WINDOWFUNC)
+/*
+** This function is called to determine whether or not it is safe to
+** push WHERE clause expression pExpr down to FROM clause sub-query
+** pSubq, which contains at least one window function. Return 1
+** if it is safe and the expression should be pushed down, or 0
+** otherwise.
+**
+** It is only safe to push the expression down if it consists only
+** of constants and copies of expressions that appear in the PARTITION
+** BY clause of all window function used by the sub-query. It is safe
+** to filter out entire partitions, but not rows within partitions, as
+** this may change the results of the window functions.
+**
+** At the time this function is called it is guaranteed that
+**
+** * the sub-query uses only one distinct window frame, and
+** * that the window frame has a PARTITION BY clause.
+*/
+static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
+ assert( pSubq->pWin->pPartition );
+ assert( (pSubq->selFlags & SF_MultiPart)==0 );
+ assert( pSubq->pPrior==0 );
+ return sqlite3ExprIsConstantOrGroupBy(pParse, pExpr, pSubq->pWin->pPartition);
+}
+# endif /* SQLITE_OMIT_WINDOWFUNC */
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** Make copies of relevant WHERE clause terms of the outer query into
** the WHERE clause of subquery. Example:
@@ -128205,9 +146823,47 @@ static int propagateConstants(
** But if the (b2=2) term were to be pushed down into the bb subquery,
** then the (1,1,NULL) row would be suppressed.
**
-** (6) The inner query features one or more window-functions (since
-** changes to the WHERE clause of the inner query could change the
-** window over which window functions are calculated).
+** (6) Window functions make things tricky as changes to the WHERE clause
+** of the inner query could change the window over which window
+** functions are calculated. Therefore, do not attempt the optimization
+** if:
+**
+** (6a) The inner query uses multiple incompatible window partitions.
+**
+** (6b) The inner query is a compound and uses window-functions.
+**
+** (6c) The WHERE clause does not consist entirely of constants and
+** copies of expressions found in the PARTITION BY clause of
+** all window-functions used by the sub-query. It is safe to
+** filter out entire partitions, as this does not change the
+** window over which any window-function is calculated.
+**
+** (7) The inner query is a Common Table Expression (CTE) that should
+** be materialized. (This restriction is implemented in the calling
+** routine.)
+**
+** (8) If the subquery is a compound that uses UNION, INTERSECT,
+** or EXCEPT, then all of the result set columns for all arms of
+** the compound must use the BINARY collating sequence.
+**
+** (9) All three of the following are true:
+**
+** (9a) The WHERE clause expression originates in the ON or USING clause
+** of a join (either an INNER or an OUTER join), and
+**
+** (9b) The subquery is to the right of the ON/USING clause
+**
+** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING
+** clause and the subquery.
+**
+** Without this restriction, the push-down optimization might move
+** the ON/USING filter expression from the left side of a RIGHT JOIN
+** over to the right side, which leads to incorrect answers. See
+** also restriction (6) in sqlite3ExprIsSingleTableConstraint().
+**
+** (10) The inner query is not the right-hand table of a RIGHT JOIN.
+**
+** (11) The subquery is not a VALUES clause
**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
@@ -128216,17 +146872,56 @@ static int pushDownWhereTerms(
Parse *pParse, /* Parse context (for malloc() and error reporting) */
Select *pSubq, /* The subquery whose WHERE clause is to be augmented */
Expr *pWhere, /* The WHERE clause of the outer query */
- int iCursor, /* Cursor number of the subquery */
- int isLeftJoin /* True if pSubq is the right term of a LEFT JOIN */
+ SrcList *pSrcList, /* The complete from clause of the outer query */
+ int iSrc /* Which FROM clause term to try to push into */
){
Expr *pNew;
+ SrcItem *pSrc; /* The subquery FROM term into which WHERE is pushed */
int nChng = 0;
+ pSrc = &pSrcList->a[iSrc];
if( pWhere==0 ) return 0;
- if( pSubq->selFlags & SF_Recursive ) return 0; /* restriction (2) */
+ if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ){
+ return 0; /* restrictions (2) and (11) */
+ }
+ if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ){
+ return 0; /* restrictions (10) */
+ }
+ if( pSubq->pPrior ){
+ Select *pSel;
+ int notUnionAll = 0;
+ for(pSel=pSubq; pSel; pSel=pSel->pPrior){
+ u8 op = pSel->op;
+ assert( op==TK_ALL || op==TK_SELECT
+ || op==TK_UNION || op==TK_INTERSECT || op==TK_EXCEPT );
+ if( op!=TK_ALL && op!=TK_SELECT ){
+ notUnionAll = 1;
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( pSubq->pWin ) return 0; /* restriction (6) */
+ if( pSel->pWin ) return 0; /* restriction (6b) */
#endif
+ }
+ if( notUnionAll ){
+ /* If any of the compound arms are connected using UNION, INTERSECT,
+ ** or EXCEPT, then we must ensure that none of the columns use a
+ ** non-BINARY collating sequence. */
+ for(pSel=pSubq; pSel; pSel=pSel->pPrior){
+ int ii;
+ const ExprList *pList = pSel->pEList;
+ assert( pList!=0 );
+ for(ii=0; ii<pList->nExpr; ii++){
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[ii].pExpr);
+ if( !sqlite3IsBinary(pColl) ){
+ return 0; /* Restriction (8) */
+ }
+ }
+ }
+ }
+ }else{
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( pSubq->pWin && pSubq->pWin->pPartition==0 ) return 0;
+#endif
+ }
#ifdef SQLITE_DEBUG
/* Only the first term of a compound can have a WITH clause. But make
@@ -128234,7 +146929,7 @@ static int pushDownWhereTerms(
** in the future.
*/
{
- Select *pX;
+ Select *pX;
for(pX=pSubq; pX; pX=pX->pPrior){
assert( (pX->selFlags & (SF_Recursive))==0 );
}
@@ -128245,35 +146940,67 @@ static int pushDownWhereTerms(
return 0; /* restriction (3) */
}
while( pWhere->op==TK_AND ){
- nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight,
- iCursor, isLeftJoin);
+ nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrcList, iSrc);
pWhere = pWhere->pLeft;
}
+
+#if 0 /* These checks now done by sqlite3ExprIsSingleTableConstraint() */
+ if( ExprHasProperty(pWhere, EP_OuterON|EP_InnerON) /* (9a) */
+ && (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (9c) */
+ ){
+ int jj;
+ for(jj=0; jj<iSrc; jj++){
+ if( pWhere->w.iJoin==pSrcList->a[jj].iCursor ){
+ /* If we reach this point, both (9a) and (9b) are satisfied.
+ ** The following loop checks (9c):
+ */
+ for(jj++; jj<iSrc; jj++){
+ if( (pSrcList->a[jj].fg.jointype & JT_RIGHT)!=0 ){
+ return 0; /* restriction (9) */
+ }
+ }
+ }
+ }
+ }
if( isLeftJoin
- && (ExprHasProperty(pWhere,EP_FromJoin)==0
- || pWhere->iRightJoinTable!=iCursor)
+ && (ExprHasProperty(pWhere,EP_OuterON)==0
+ || pWhere->w.iJoin!=iCursor)
){
return 0; /* restriction (4) */
}
- if( ExprHasProperty(pWhere,EP_FromJoin) && pWhere->iRightJoinTable!=iCursor ){
+ if( ExprHasProperty(pWhere,EP_OuterON)
+ && pWhere->w.iJoin!=iCursor
+ ){
return 0; /* restriction (5) */
}
- if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
+#endif
+
+ if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){
nChng++;
+ pSubq->selFlags |= SF_PushDown;
while( pSubq ){
SubstContext x;
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
- unsetJoinExpr(pNew, -1);
+ unsetJoinExpr(pNew, -1, 1);
x.pParse = pParse;
- x.iTable = iCursor;
- x.iNewTable = iCursor;
- x.isLeftJoin = 0;
+ x.iTable = pSrc->iCursor;
+ x.iNewTable = pSrc->iCursor;
+ x.isOuterJoin = 0;
x.pEList = pSubq->pEList;
+ x.pCList = findLeftmostExprlist(pSubq);
pNew = substExpr(&x, pNew);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){
+ /* Restriction 6c has prevented push-down in this case */
+ sqlite3ExprDelete(pParse->db, pNew);
+ nChng--;
+ break;
+ }
+#endif
if( pSubq->selFlags & SF_Aggregate ){
- pSubq->pHaving = sqlite3ExprAnd(pParse->db, pSubq->pHaving, pNew);
+ pSubq->pHaving = sqlite3ExprAnd(pParse, pSubq->pHaving, pNew);
}else{
- pSubq->pWhere = sqlite3ExprAnd(pParse->db, pSubq->pWhere, pNew);
+ pSubq->pWhere = sqlite3ExprAnd(pParse, pSubq->pWhere, pNew);
}
pSubq = pSubq->pPrior;
}
@@ -128283,8 +147010,80 @@ static int pushDownWhereTerms(
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
/*
+** Check to see if a subquery contains result-set columns that are
+** never used. If it does, change the value of those result-set columns
+** to NULL so that they do not cause unnecessary work to compute.
+**
+** Return the number of column that were changed to NULL.
+*/
+static int disableUnusedSubqueryResultColumns(SrcItem *pItem){
+ int nCol;
+ Select *pSub; /* The subquery to be simplified */
+ Select *pX; /* For looping over compound elements of pSub */
+ Table *pTab; /* The table that describes the subquery */
+ int j; /* Column number */
+ int nChng = 0; /* Number of columns converted to NULL */
+ Bitmask colUsed; /* Columns that may not be NULLed out */
+
+ assert( pItem!=0 );
+ if( pItem->fg.isCorrelated || pItem->fg.isCte ){
+ return 0;
+ }
+ assert( pItem->pTab!=0 );
+ pTab = pItem->pTab;
+ assert( pItem->pSelect!=0 );
+ pSub = pItem->pSelect;
+ assert( pSub->pEList->nExpr==pTab->nCol );
+ for(pX=pSub; pX; pX=pX->pPrior){
+ if( (pX->selFlags & (SF_Distinct|SF_Aggregate))!=0 ){
+ testcase( pX->selFlags & SF_Distinct );
+ testcase( pX->selFlags & SF_Aggregate );
+ return 0;
+ }
+ if( pX->pPrior && pX->op!=TK_ALL ){
+ /* This optimization does not work for compound subqueries that
+ ** use UNION, INTERSECT, or EXCEPT. Only UNION ALL is allowed. */
+ return 0;
+ }
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( pX->pWin ){
+ /* This optimization does not work for subqueries that use window
+ ** functions. */
+ return 0;
+ }
+#endif
+ }
+ colUsed = pItem->colUsed;
+ if( pSub->pOrderBy ){
+ ExprList *pList = pSub->pOrderBy;
+ for(j=0; j<pList->nExpr; j++){
+ u16 iCol = pList->a[j].u.x.iOrderByCol;
+ if( iCol>0 ){
+ iCol--;
+ colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol);
+ }
+ }
+ }
+ nCol = pTab->nCol;
+ for(j=0; j<nCol; j++){
+ Bitmask m = j<BMS-1 ? MASKBIT(j) : TOPBIT;
+ if( (m & colUsed)!=0 ) continue;
+ for(pX=pSub; pX; pX=pX->pPrior) {
+ Expr *pY = pX->pEList->a[j].pExpr;
+ if( pY->op==TK_NULL ) continue;
+ pY->op = TK_NULL;
+ ExprClearProperty(pY, EP_Skip|EP_Unlikely);
+ pX->selFlags |= SF_PushDown;
+ nChng++;
+ }
+ }
+ return nChng;
+}
+
+
+/*
** The pFunc is the only aggregate function in the query. Check to see
-** if the query is a candidate for the min/max optimization.
+** if the query is a candidate for the min/max optimization.
**
** If the query is a candidate for the min/max optimization, then set
** *ppMinMax to be an ORDER BY clause to be used for the optimization
@@ -128300,40 +147099,58 @@ static int pushDownWhereTerms(
*/
static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
int eRet = WHERE_ORDERBY_NORMAL; /* Return value */
- ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */
+ ExprList *pEList; /* Arguments to agg function */
const char *zFunc; /* Name of aggregate function pFunc */
ExprList *pOrderBy;
- u8 sortOrder;
+ u8 sortFlags = 0;
assert( *ppMinMax==0 );
assert( pFunc->op==TK_AGG_FUNCTION );
- if( pEList==0 || pEList->nExpr!=1 ) return eRet;
+ assert( !IsWindowFunc(pFunc) );
+ assert( ExprUseXList(pFunc) );
+ pEList = pFunc->x.pList;
+ if( pEList==0
+ || pEList->nExpr!=1
+ || ExprHasProperty(pFunc, EP_WinFunc)
+ || OptimizationDisabled(db, SQLITE_MinMaxOpt)
+ ){
+ return eRet;
+ }
+ assert( !ExprHasProperty(pFunc, EP_IntValue) );
zFunc = pFunc->u.zToken;
if( sqlite3StrICmp(zFunc, "min")==0 ){
eRet = WHERE_ORDERBY_MIN;
- sortOrder = SQLITE_SO_ASC;
+ if( sqlite3ExprCanBeNull(pEList->a[0].pExpr) ){
+ sortFlags = KEYINFO_ORDER_BIGNULL;
+ }
}else if( sqlite3StrICmp(zFunc, "max")==0 ){
eRet = WHERE_ORDERBY_MAX;
- sortOrder = SQLITE_SO_DESC;
+ sortFlags = KEYINFO_ORDER_DESC;
}else{
return eRet;
}
*ppMinMax = pOrderBy = sqlite3ExprListDup(db, pEList, 0);
assert( pOrderBy!=0 || db->mallocFailed );
- if( pOrderBy ) pOrderBy->a[0].sortOrder = sortOrder;
+ if( pOrderBy ) pOrderBy->a[0].fg.sortFlags = sortFlags;
return eRet;
}
/*
** The select statement passed as the first argument is an aggregate query.
-** The second argument is the associated aggregate-info object. This
+** The second argument is the associated aggregate-info object. This
** function tests if the SELECT is of the form:
**
** SELECT count(*) FROM <tbl>
**
** where table is a database table, not a sub-select or view. If the query
** does match this pattern, then a pointer to the Table object representing
-** <tbl> is returned. Otherwise, 0 is returned.
+** <tbl> is returned. Otherwise, NULL is returned.
+**
+** This routine checks to see if it is safe to use the count optimization.
+** A correct answer is still obtained (though perhaps more slowly) if
+** this routine returns NULL when it could have returned a table pointer.
+** But returning the pointer when NULL should have been returned can
+** result in incorrect answers and/or crashes. So, when in doubt, return NULL.
*/
static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
Table *pTab;
@@ -128341,20 +147158,28 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
assert( !p->pGroupBy );
- if( p->pWhere || p->pEList->nExpr!=1
- || p->pSrc->nSrc!=1 || p->pSrc->a[0].pSelect
+ if( p->pWhere
+ || p->pEList->nExpr!=1
+ || p->pSrc->nSrc!=1
+ || p->pSrc->a[0].pSelect
+ || pAggInfo->nFunc!=1
+ || p->pHaving
){
return 0;
}
pTab = p->pSrc->a[0].pTab;
+ assert( pTab!=0 );
+ assert( !IsView(pTab) );
+ if( !IsOrdinaryTable(pTab) ) return 0;
pExpr = p->pEList->a[0].pExpr;
- assert( pTab && !pTab->pSelect && pExpr );
-
- if( IsVirtual(pTab) ) return 0;
+ assert( pExpr!=0 );
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
- if( NEVER(pAggInfo->nFunc==0) ) return 0;
+ if( pExpr->pAggInfo!=pAggInfo ) return 0;
if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0;
- if( pExpr->flags&EP_Distinct ) return 0;
+ assert( pAggInfo->aFunc[0].pFExpr==pExpr );
+ testcase( ExprHasProperty(pExpr, EP_Distinct) );
+ testcase( ExprHasProperty(pExpr, EP_WinFunc) );
+ if( ExprHasProperty(pExpr, EP_Distinct|EP_WinFunc) ) return 0;
return pTab;
}
@@ -128362,30 +147187,33 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
/*
** If the source-list item passed as an argument was augmented with an
** INDEXED BY clause, then try to locate the specified index. If there
-** was such a clause and the named index cannot be found, return
-** SQLITE_ERROR and leave an error in pParse. Otherwise, populate
+** was such a clause and the named index cannot be found, return
+** SQLITE_ERROR and leave an error in pParse. Otherwise, populate
** pFrom->pIndex and return SQLITE_OK.
*/
-SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){
- if( pFrom->pTab && pFrom->fg.isIndexedBy ){
- Table *pTab = pFrom->pTab;
- char *zIndexedBy = pFrom->u1.zIndexedBy;
- Index *pIdx;
- for(pIdx=pTab->pIndex;
- pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy);
- pIdx=pIdx->pNext
- );
- if( !pIdx ){
- sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0);
- pParse->checkSchema = 1;
- return SQLITE_ERROR;
- }
- pFrom->pIBIndex = pIdx;
+SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){
+ Table *pTab = pFrom->pTab;
+ char *zIndexedBy = pFrom->u1.zIndexedBy;
+ Index *pIdx;
+ assert( pTab!=0 );
+ assert( pFrom->fg.isIndexedBy!=0 );
+
+ for(pIdx=pTab->pIndex;
+ pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy);
+ pIdx=pIdx->pNext
+ );
+ if( !pIdx ){
+ sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0);
+ pParse->checkSchema = 1;
+ return SQLITE_ERROR;
}
+ assert( pFrom->fg.isCte==0 );
+ pFrom->u2.pIBIndex = pIdx;
return SQLITE_OK;
}
+
/*
-** Detect compound SELECT statements that use an ORDER BY clause with
+** Detect compound SELECT statements that use an ORDER BY clause with
** an alternative collating sequence.
**
** SELECT ... FROM t1 EXCEPT SELECT ... FROM t2 ORDER BY .. COLLATE ...
@@ -128420,6 +147248,14 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
for(pX=p; pX && (pX->op==TK_ALL || pX->op==TK_SELECT); pX=pX->pPrior){}
if( pX==0 ) return WRC_Continue;
a = p->pOrderBy->a;
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ /* If iOrderByCol is already non-zero, then it has already been matched
+ ** to a result column of the SELECT statement. This occurs when the
+ ** SELECT is rewritten for window-functions processing and then passed
+ ** to sqlite3SelectPrep() and similar a second time. The rewriting done
+ ** by this function is not required in this case. */
+ if( a[0].u.x.iOrderByCol ) return WRC_Continue;
+#endif
for(i=p->pOrderBy->nExpr-1; i>=0; i--){
if( a[i].pExpr->flags & EP_Collate ) break;
}
@@ -128432,7 +147268,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
pNew = sqlite3DbMallocZero(db, sizeof(*pNew) );
if( pNew==0 ) return WRC_Abort;
memset(&dummy, 0, sizeof(dummy));
- pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0,0);
+ pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0);
if( pNewSrc==0 ) return WRC_Abort;
*pNew = *p;
p->pSrc = pNewSrc;
@@ -128445,6 +147281,9 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
p->pPrior = 0;
p->pNext = 0;
p->pWith = 0;
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ p->pWinDefn = 0;
+#endif
p->selFlags &= ~SF_Compound;
assert( (p->selFlags & SF_Converted)==0 );
p->selFlags |= SF_Converted;
@@ -128459,7 +147298,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
** arguments. If it does, leave an error message in pParse and return
** non-zero, since pFrom is not allowed to be a table-valued function.
*/
-static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){
+static int cannotBeFunction(Parse *pParse, SrcItem *pFrom){
if( pFrom->fg.isTabFunc ){
sqlite3ErrorMsg(pParse, "'%s' is not a function", pFrom->zName);
return 1;
@@ -128469,9 +147308,9 @@ static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){
#ifndef SQLITE_OMIT_CTE
/*
-** Argument pWith (which may be NULL) points to a linked list of nested
-** WITH contexts, from inner to outermost. If the table identified by
-** FROM clause element pItem is really a common-table-expression (CTE)
+** Argument pWith (which may be NULL) points to a linked list of nested
+** WITH contexts, from inner to outermost. If the table identified by
+** FROM clause element pItem is really a common-table-expression (CTE)
** then return a pointer to the CTE definition for that table. Otherwise
** return NULL.
**
@@ -128480,21 +147319,22 @@ static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){
*/
static struct Cte *searchWith(
With *pWith, /* Current innermost WITH clause */
- struct SrcList_item *pItem, /* FROM clause element to resolve */
+ SrcItem *pItem, /* FROM clause element to resolve */
With **ppContext /* OUT: WITH clause return value belongs to */
){
- const char *zName;
- if( pItem->zDatabase==0 && (zName = pItem->zName)!=0 ){
- With *p;
- for(p=pWith; p; p=p->pOuter){
- int i;
- for(i=0; i<p->nCte; i++){
- if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){
- *ppContext = p;
- return &p->a[i];
- }
+ const char *zName = pItem->zName;
+ With *p;
+ assert( pItem->zDatabase==0 );
+ assert( zName!=0 );
+ for(p=pWith; p; p=p->pOuter){
+ int i;
+ for(i=0; i<p->nCte; i++){
+ if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){
+ *ppContext = p;
+ return &p->a[i];
}
}
+ if( p->bView ) break;
}
return 0;
}
@@ -128504,55 +147344,91 @@ static struct Cte *searchWith(
**
** This routine pushes the WITH clause passed as the second argument
** onto the top of the stack. If argument bFree is true, then this
-** WITH clause will never be popped from the stack. In this case it
-** should be freed along with the Parse object. In other cases, when
-** bFree==0, the With object will be freed along with the SELECT
+** WITH clause will never be popped from the stack but should instead
+** be freed along with the Parse object. In other cases, when
+** bFree==0, the With object will be freed along with the SELECT
** statement with which it is associated.
+**
+** This routine returns a copy of pWith. Or, if bFree is true and
+** the pWith object is destroyed immediately due to an OOM condition,
+** then this routine return NULL.
+**
+** If bFree is true, do not continue to use the pWith pointer after
+** calling this routine, Instead, use only the return value.
*/
-SQLITE_PRIVATE void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
- assert( bFree==0 || (pParse->pWith==0 && pParse->pWithToFree==0) );
+SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
if( pWith ){
- assert( pParse->pWith!=pWith );
- pWith->pOuter = pParse->pWith;
- pParse->pWith = pWith;
- if( bFree ) pParse->pWithToFree = pWith;
+ if( bFree ){
+ pWith = (With*)sqlite3ParserAddCleanup(pParse, sqlite3WithDeleteGeneric,
+ pWith);
+ if( pWith==0 ) return 0;
+ }
+ if( pParse->nErr==0 ){
+ assert( pParse->pWith!=pWith );
+ pWith->pOuter = pParse->pWith;
+ pParse->pWith = pWith;
+ }
}
+ return pWith;
}
/*
-** This function checks if argument pFrom refers to a CTE declared by
-** a WITH clause on the stack currently maintained by the parser. And,
-** if currently processing a CTE expression, if it is a recursive
-** reference to the current CTE.
+** This function checks if argument pFrom refers to a CTE declared by
+** a WITH clause on the stack currently maintained by the parser (on the
+** pParse->pWith linked list). And if currently processing a CTE
+** CTE expression, through routine checks to see if the reference is
+** a recursive reference to the CTE.
**
-** If pFrom falls into either of the two categories above, pFrom->pTab
-** and other fields are populated accordingly. The caller should check
-** (pFrom->pTab!=0) to determine whether or not a successful match
-** was found.
+** If pFrom matches a CTE according to either of these two above, pFrom->pTab
+** and other fields are populated accordingly.
**
-** Whether or not a match is found, SQLITE_OK is returned if no error
-** occurs. If an error does occur, an error message is stored in the
-** parser and some error code other than SQLITE_OK returned.
+** Return 0 if no match is found.
+** Return 1 if a match is found.
+** Return 2 if an error condition is detected.
*/
-static int withExpand(
- Walker *pWalker,
- struct SrcList_item *pFrom
+static int resolveFromTermToCte(
+ Parse *pParse, /* The parsing context */
+ Walker *pWalker, /* Current tree walker */
+ SrcItem *pFrom /* The FROM clause term to check */
){
- Parse *pParse = pWalker->pParse;
- sqlite3 *db = pParse->db;
- struct Cte *pCte; /* Matched CTE (or NULL if no match) */
- With *pWith; /* WITH clause that pCte belongs to */
+ Cte *pCte; /* Matched CTE (or NULL if no match) */
+ With *pWith; /* The matching WITH */
assert( pFrom->pTab==0 );
-
+ if( pParse->pWith==0 ){
+ /* There are no WITH clauses in the stack. No match is possible */
+ return 0;
+ }
+ if( pParse->nErr ){
+ /* Prior errors might have left pParse->pWith in a goofy state, so
+ ** go no further. */
+ return 0;
+ }
+ if( pFrom->zDatabase!=0 ){
+ /* The FROM term contains a schema qualifier (ex: main.t1) and so
+ ** it cannot possibly be a CTE reference. */
+ return 0;
+ }
+ if( pFrom->fg.notCte ){
+ /* The FROM term is specifically excluded from matching a CTE.
+ ** (1) It is part of a trigger that used to have zDatabase but had
+ ** zDatabase removed by sqlite3FixTriggerStep().
+ ** (2) This is the first term in the FROM clause of an UPDATE.
+ */
+ return 0;
+ }
pCte = searchWith(pParse->pWith, pFrom, &pWith);
if( pCte ){
+ sqlite3 *db = pParse->db;
Table *pTab;
ExprList *pEList;
Select *pSel;
Select *pLeft; /* Left-most SELECT statement */
+ Select *pRecTerm; /* Left-most recursive term */
int bMayRecursive; /* True if compound joined by UNION [ALL] */
With *pSavedWith; /* Initial value of pParse->pWith */
+ int iRecTab = -1; /* Cursor for recursive table */
+ CteUse *pCteUse;
/* If pCte->zCteErr is non-NULL at this point, then this is an illegal
** recursive reference to CTE pCte. Leave an error in pParse and return
@@ -128560,63 +147436,95 @@ static int withExpand(
** In this case, proceed. */
if( pCte->zCteErr ){
sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName);
- return SQLITE_ERROR;
+ return 2;
}
- if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR;
+ if( cannotBeFunction(pParse, pFrom) ) return 2;
assert( pFrom->pTab==0 );
- pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
- if( pTab==0 ) return WRC_Abort;
+ pTab = sqlite3DbMallocZero(db, sizeof(Table));
+ if( pTab==0 ) return 2;
+ pCteUse = pCte->pUse;
+ if( pCteUse==0 ){
+ pCte->pUse = pCteUse = sqlite3DbMallocZero(db, sizeof(pCteUse[0]));
+ if( pCteUse==0
+ || sqlite3ParserAddCleanup(pParse,sqlite3DbFree,pCteUse)==0
+ ){
+ sqlite3DbFree(db, pTab);
+ return 2;
+ }
+ pCteUse->eM10d = pCte->eM10d;
+ }
+ pFrom->pTab = pTab;
pTab->nTabRef = 1;
pTab->zName = sqlite3DbStrDup(db, pCte->zName);
pTab->iPKey = -1;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
- if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
+ if( db->mallocFailed ) return 2;
+ pFrom->pSelect->selFlags |= SF_CopyCte;
assert( pFrom->pSelect );
+ if( pFrom->fg.isIndexedBy ){
+ sqlite3ErrorMsg(pParse, "no such index: \"%s\"", pFrom->u1.zIndexedBy);
+ return 2;
+ }
+ pFrom->fg.isCte = 1;
+ pFrom->u2.pCteUse = pCteUse;
+ pCteUse->nUse++;
/* Check if this is a recursive CTE. */
- pSel = pFrom->pSelect;
+ pRecTerm = pSel = pFrom->pSelect;
bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION );
- if( bMayRecursive ){
+ while( bMayRecursive && pRecTerm->op==pSel->op ){
int i;
- SrcList *pSrc = pFrom->pSelect->pSrc;
+ SrcList *pSrc = pRecTerm->pSrc;
+ assert( pRecTerm->pPrior!=0 );
for(i=0; i<pSrc->nSrc; i++){
- struct SrcList_item *pItem = &pSrc->a[i];
- if( pItem->zDatabase==0
- && pItem->zName!=0
+ SrcItem *pItem = &pSrc->a[i];
+ if( pItem->zDatabase==0
+ && pItem->zName!=0
&& 0==sqlite3StrICmp(pItem->zName, pCte->zName)
- ){
+ ){
pItem->pTab = pTab;
- pItem->fg.isRecursive = 1;
pTab->nTabRef++;
- pSel->selFlags |= SF_Recursive;
+ pItem->fg.isRecursive = 1;
+ if( pRecTerm->selFlags & SF_Recursive ){
+ sqlite3ErrorMsg(pParse,
+ "multiple references to recursive table: %s", pCte->zName
+ );
+ return 2;
+ }
+ pRecTerm->selFlags |= SF_Recursive;
+ if( iRecTab<0 ) iRecTab = pParse->nTab++;
+ pItem->iCursor = iRecTab;
}
}
+ if( (pRecTerm->selFlags & SF_Recursive)==0 ) break;
+ pRecTerm = pRecTerm->pPrior;
}
- /* Only one recursive reference is permitted. */
- if( pTab->nTabRef>2 ){
- sqlite3ErrorMsg(
- pParse, "multiple references to recursive table: %s", pCte->zName
- );
- return SQLITE_ERROR;
- }
- assert( pTab->nTabRef==1 ||
- ((pSel->selFlags&SF_Recursive) && pTab->nTabRef==2 ));
-
pCte->zCteErr = "circular reference: %s";
pSavedWith = pParse->pWith;
pParse->pWith = pWith;
- if( bMayRecursive ){
- Select *pPrior = pSel->pPrior;
- assert( pPrior->pWith==0 );
- pPrior->pWith = pSel->pWith;
- sqlite3WalkSelect(pWalker, pPrior);
- pPrior->pWith = 0;
+ if( pSel->selFlags & SF_Recursive ){
+ int rc;
+ assert( pRecTerm!=0 );
+ assert( (pRecTerm->selFlags & SF_Recursive)==0 );
+ assert( pRecTerm->pNext!=0 );
+ assert( (pRecTerm->pNext->selFlags & SF_Recursive)!=0 );
+ assert( pRecTerm->pWith==0 );
+ pRecTerm->pWith = pSel->pWith;
+ rc = sqlite3WalkSelect(pWalker, pRecTerm);
+ pRecTerm->pWith = 0;
+ if( rc ){
+ pParse->pWith = pSavedWith;
+ return 2;
+ }
}else{
- sqlite3WalkSelect(pWalker, pSel);
+ if( sqlite3WalkSelect(pWalker, pSel) ){
+ pParse->pWith = pSavedWith;
+ return 2;
+ }
}
pParse->pWith = pWith;
@@ -128628,7 +147536,7 @@ static int withExpand(
pCte->zName, pEList->nExpr, pCte->pCols->nExpr
);
pParse->pWith = pSavedWith;
- return SQLITE_ERROR;
+ return 2;
}
pEList = pCte->pCols;
}
@@ -128644,43 +147552,41 @@ static int withExpand(
}
pCte->zCteErr = 0;
pParse->pWith = pSavedWith;
+ return 1; /* Success */
}
-
- return SQLITE_OK;
+ return 0; /* No match */
}
#endif
#ifndef SQLITE_OMIT_CTE
/*
-** If the SELECT passed as the second argument has an associated WITH
+** If the SELECT passed as the second argument has an associated WITH
** clause, pop it from the stack stored as part of the Parse object.
**
** This function is used as the xSelectCallback2() callback by
** sqlite3SelectExpand() when walking a SELECT tree to resolve table
-** names and other FROM clause elements.
+** names and other FROM clause elements.
*/
-static void selectPopWith(Walker *pWalker, Select *p){
+SQLITE_PRIVATE void sqlite3SelectPopWith(Walker *pWalker, Select *p){
Parse *pParse = pWalker->pParse;
if( OK_IF_ALWAYS_TRUE(pParse->pWith) && p->pPrior==0 ){
With *pWith = findRightmost(p)->pWith;
if( pWith!=0 ){
- assert( pParse->pWith==pWith );
+ assert( pParse->pWith==pWith || pParse->nErr );
pParse->pWith = pWith->pOuter;
}
}
}
-#else
-#define selectPopWith 0
#endif
/*
-** The SrcList_item structure passed as the second argument represents a
+** The SrcItem structure passed as the second argument represents a
** sub-query in the FROM clause of a SELECT statement. This function
-** allocates and populates the SrcList_item.pTab object. If successful,
+** allocates and populates the SrcItem.pTab object. If successful,
** SQLITE_OK is returned. Otherwise, if an OOM error is encountered,
** SQLITE_NOMEM.
*/
-SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){
+SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){
Select *pSel = pFrom->pSelect;
Table *pTab;
@@ -128691,17 +147597,47 @@ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFr
if( pFrom->zAlias ){
pTab->zName = sqlite3DbStrDup(pParse->db, pFrom->zAlias);
}else{
- pTab->zName = sqlite3MPrintf(pParse->db, "subquery_%u", pSel->selId);
+ pTab->zName = sqlite3MPrintf(pParse->db, "%!S", pFrom);
}
while( pSel->pPrior ){ pSel = pSel->pPrior; }
sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol);
pTab->iPKey = -1;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
- pTab->tabFlags |= TF_Ephemeral;
+#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
+ /* The usual case - do not allow ROWID on a subquery */
+ pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
+#else
+ pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */
+#endif
+ return pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
+}
- return SQLITE_OK;
+
+/*
+** Check the N SrcItem objects to the right of pBase. (N might be zero!)
+** If any of those SrcItem objects have a USING clause containing zName
+** then return true.
+**
+** If N is zero, or none of the N SrcItem objects to the right of pBase
+** contains a USING clause, or if none of the USING clauses contain zName,
+** then return false.
+*/
+static int inAnyUsingClause(
+ const char *zName, /* Name we are looking for */
+ SrcItem *pBase, /* The base SrcItem. Looking at pBase[1] and following */
+ int N /* How many SrcItems to check */
+){
+ while( N>0 ){
+ N--;
+ pBase++;
+ if( pBase->fg.isUsing==0 ) continue;
+ if( NEVER(pBase->u3.pUsing==0) ) continue;
+ if( sqlite3IdListIndex(pBase->u3.pUsing, zName)>=0 ) return 1;
+ }
+ return 0;
}
+
/*
** This routine is a Walker callback for "expanding" a SELECT statement.
** "Expanding" means to do the following:
@@ -128709,7 +147645,7 @@ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFr
** (1) Make sure VDBE cursor numbers have been assigned to every
** element of the FROM clause.
**
-** (2) Fill in the pTabList->a[].pTab fields in the SrcList that
+** (2) Fill in the pTabList->a[].pTab fields in the SrcList that
** defines FROM clause. When views appear in the FROM clause,
** fill pTabList->a[].pSelect with a copy of the SELECT statement
** that implements the view. A copy is made of the view's SELECT
@@ -128728,10 +147664,10 @@ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFr
*/
static int selectExpander(Walker *pWalker, Select *p){
Parse *pParse = pWalker->pParse;
- int i, j, k;
+ int i, j, k, rc;
SrcList *pTabList;
ExprList *pEList;
- struct SrcList_item *pFrom;
+ SrcItem *pFrom;
sqlite3 *db = pParse->db;
Expr *pE, *pRight, *pExpr;
u16 selFlags = p->selFlags;
@@ -128745,8 +147681,21 @@ static int selectExpander(Walker *pWalker, Select *p){
if( (selFlags & SF_Expanded)!=0 ){
return WRC_Prune;
}
+ if( pWalker->eCode ){
+ /* Renumber selId because it has been copied from a view */
+ p->selId = ++pParse->nSelect;
+ }
pTabList = p->pSrc;
pEList = p->pEList;
+ if( pParse->pWith && (p->selFlags & SF_View) ){
+ if( p->pWith==0 ){
+ p->pWith = (With*)sqlite3DbMallocZero(db, sizeof(With));
+ if( p->pWith==0 ){
+ return WRC_Abort;
+ }
+ }
+ p->pWith->bView = 1;
+ }
sqlite3WithPush(pParse, p->pWith, 0);
/* Make sure cursor numbers have been assigned to all entries in
@@ -128761,12 +147710,8 @@ static int selectExpander(Walker *pWalker, Select *p){
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab;
assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 );
- if( pFrom->fg.isRecursive ) continue;
- assert( pFrom->pTab==0 );
-#ifndef SQLITE_OMIT_CTE
- if( withExpand(pWalker, pFrom) ) return WRC_Abort;
- if( pFrom->pTab ) {} else
-#endif
+ if( pFrom->pTab ) continue;
+ assert( pFrom->fg.isRecursive==0 );
if( pFrom->zName==0 ){
#ifndef SQLITE_OMIT_SUBQUERY
Select *pSel = pFrom->pSelect;
@@ -128776,6 +147721,12 @@ static int selectExpander(Walker *pWalker, Select *p){
if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort;
if( sqlite3ExpandSubquery(pParse, pFrom) ) return WRC_Abort;
#endif
+#ifndef SQLITE_OMIT_CTE
+ }else if( (rc = resolveFromTermToCte(pParse, pWalker, pFrom))!=0 ){
+ if( rc>1 ) return WRC_Abort;
+ pTab = pFrom->pTab;
+ assert( pTab!=0 );
+#endif
}else{
/* An ordinary table or view name in the FROM clause */
assert( pFrom->pTab==0 );
@@ -128791,29 +147742,52 @@ static int selectExpander(Walker *pWalker, Select *p){
if( !IsVirtual(pTab) && cannotBeFunction(pParse, pFrom) ){
return WRC_Abort;
}
-#if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE)
- if( IsVirtual(pTab) || pTab->pSelect ){
+#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
+ if( !IsOrdinaryTable(pTab) ){
i16 nCol;
+ u8 eCodeOrig = pWalker->eCode;
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
assert( pFrom->pSelect==0 );
- pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
+ if( IsView(pTab) ){
+ if( (db->flags & SQLITE_EnableView)==0
+ && pTab->pSchema!=db->aDb[1].pSchema
+ ){
+ sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
+ pTab->zName);
+ }
+ pFrom->pSelect = sqlite3SelectDup(db, pTab->u.view.pSelect, 0);
+ }
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ else if( ALWAYS(IsVirtual(pTab))
+ && pFrom->fg.fromDDL
+ && ALWAYS(pTab->u.vtab.p!=0)
+ && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0)
+ ){
+ sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"",
+ pTab->zName);
+ }
+ assert( SQLITE_VTABRISK_Normal==1 && SQLITE_VTABRISK_High==2 );
+#endif
nCol = pTab->nCol;
pTab->nCol = -1;
+ pWalker->eCode = 1; /* Turn on Select.selId renumbering */
sqlite3WalkSelect(pWalker, pFrom->pSelect);
+ pWalker->eCode = eCodeOrig;
pTab->nCol = nCol;
}
#endif
}
/* Locate the index named by the INDEXED BY clause, if any. */
- if( sqlite3IndexedByLookup(pParse, pFrom) ){
+ if( pFrom->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pFrom) ){
return WRC_Abort;
}
}
/* Process NATURAL keywords, and ON and USING clauses of joins.
*/
- if( db->mallocFailed || sqliteProcessJoin(pParse, p) ){
+ assert( db->mallocFailed==0 || pParse->nErr!=0 );
+ if( pParse->nErr || sqlite3ProcessJoin(pParse, p) ){
return WRC_Abort;
}
@@ -128860,10 +147834,9 @@ static int selectExpander(Walker *pWalker, Select *p){
*/
pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr);
if( pNew ){
- pNew->a[pNew->nExpr-1].zName = a[k].zName;
- pNew->a[pNew->nExpr-1].zSpan = a[k].zSpan;
- a[k].zName = 0;
- a[k].zSpan = 0;
+ pNew->a[pNew->nExpr-1].zEName = a[k].zEName;
+ pNew->a[pNew->nExpr-1].fg.eEName = a[k].fg.eEName;
+ a[k].zEName = 0;
}
a[k].pExpr = 0;
}else{
@@ -128871,101 +147844,174 @@ static int selectExpander(Walker *pWalker, Select *p){
** expanded. */
int tableSeen = 0; /* Set to 1 when TABLE matches */
char *zTName = 0; /* text of name of TABLE */
+ int iErrOfst;
if( pE->op==TK_DOT ){
+ assert( (selFlags & SF_NestedFrom)==0 );
assert( pE->pLeft!=0 );
assert( !ExprHasProperty(pE->pLeft, EP_IntValue) );
zTName = pE->pLeft->u.zToken;
+ assert( ExprUseWOfst(pE->pLeft) );
+ iErrOfst = pE->pRight->w.iOfst;
+ }else{
+ assert( ExprUseWOfst(pE) );
+ iErrOfst = pE->w.iOfst;
}
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
- Table *pTab = pFrom->pTab;
- Select *pSub = pFrom->pSelect;
- char *zTabName = pFrom->zAlias;
- const char *zSchemaName = 0;
- int iDb;
- if( zTabName==0 ){
+ int nAdd; /* Number of cols including rowid */
+ Table *pTab = pFrom->pTab; /* Table for this data source */
+ ExprList *pNestedFrom; /* Result-set of a nested FROM clause */
+ char *zTabName; /* AS name for this data source */
+ const char *zSchemaName = 0; /* Schema name for this data source */
+ int iDb; /* Schema index for this data src */
+ IdList *pUsing; /* USING clause for pFrom[1] */
+
+ if( (zTabName = pFrom->zAlias)==0 ){
zTabName = pTab->zName;
}
if( db->mallocFailed ) break;
- if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){
- pSub = 0;
+ assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom->pSelect) );
+ if( pFrom->fg.isNestedFrom ){
+ assert( pFrom->pSelect!=0 );
+ pNestedFrom = pFrom->pSelect->pEList;
+ assert( pNestedFrom!=0 );
+ assert( pNestedFrom->nExpr==pTab->nCol );
+ assert( VisibleRowid(pTab)==0 );
+ }else{
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
continue;
}
+ pNestedFrom = 0;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
zSchemaName = iDb>=0 ? db->aDb[iDb].zDbSName : "*";
}
- for(j=0; j<pTab->nCol; j++){
- char *zName = pTab->aCol[j].zName;
- char *zColname; /* The computed column name */
- char *zToFree; /* Malloced string that needs to be freed */
- Token sColname; /* Computed column name as a token */
-
- assert( zName );
- if( zTName && pSub
- && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0
- ){
- continue;
+ if( i+1<pTabList->nSrc
+ && pFrom[1].fg.isUsing
+ && (selFlags & SF_NestedFrom)!=0
+ ){
+ int ii;
+ pUsing = pFrom[1].u3.pUsing;
+ for(ii=0; ii<pUsing->nId; ii++){
+ const char *zUName = pUsing->a[ii].zName;
+ pRight = sqlite3Expr(db, TK_ID, zUName);
+ sqlite3ExprSetErrorOffset(pRight, iErrOfst);
+ pNew = sqlite3ExprListAppend(pParse, pNew, pRight);
+ if( pNew ){
+ struct ExprList_item *pX = &pNew->a[pNew->nExpr-1];
+ assert( pX->zEName==0 );
+ pX->zEName = sqlite3MPrintf(db,"..%s", zUName);
+ pX->fg.eEName = ENAME_TAB;
+ pX->fg.bUsingTerm = 1;
+ }
}
+ }else{
+ pUsing = 0;
+ }
- /* If a column is marked as 'hidden', omit it from the expanded
- ** result-set list unless the SELECT has the SF_IncludeHidden
- ** bit set.
- */
- if( (p->selFlags & SF_IncludeHidden)==0
- && IsHiddenColumn(&pTab->aCol[j])
- ){
- continue;
- }
- tableSeen = 1;
+ nAdd = pTab->nCol + (VisibleRowid(pTab) && (selFlags&SF_NestedFrom));
+ for(j=0; j<nAdd; j++){
+ const char *zName;
+ struct ExprList_item *pX; /* Newly added ExprList term */
+
+ if( j==pTab->nCol ){
+ zName = sqlite3RowidAlias(pTab);
+ if( zName==0 ) continue;
+ }else{
+ zName = pTab->aCol[j].zCnName;
+
+ /* If pTab is actually an SF_NestedFrom sub-select, do not
+ ** expand any ENAME_ROWID columns. */
+ if( pNestedFrom && pNestedFrom->a[j].fg.eEName==ENAME_ROWID ){
+ continue;
+ }
- if( i>0 && zTName==0 ){
- if( (pFrom->fg.jointype & JT_NATURAL)!=0
- && tableAndColumnIndex(pTabList, i, zName, 0, 0)
+ if( zTName
+ && pNestedFrom
+ && sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0, 0)==0
){
- /* In a NATURAL join, omit the join columns from the
- ** table to the right of the join */
continue;
}
- if( sqlite3IdListIndex(pFrom->pUsing, zName)>=0 ){
+
+ /* If a column is marked as 'hidden', omit it from the expanded
+ ** result-set list unless the SELECT has the SF_IncludeHidden
+ ** bit set.
+ */
+ if( (p->selFlags & SF_IncludeHidden)==0
+ && IsHiddenColumn(&pTab->aCol[j])
+ ){
+ continue;
+ }
+ if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
+ && zTName==0
+ && (selFlags & (SF_NestedFrom))==0
+ ){
+ continue;
+ }
+ }
+ assert( zName );
+ tableSeen = 1;
+
+ if( i>0 && zTName==0 && (selFlags & SF_NestedFrom)==0 ){
+ if( pFrom->fg.isUsing
+ && sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0
+ ){
/* In a join with a USING clause, omit columns in the
** using clause from the table on the right. */
continue;
}
}
pRight = sqlite3Expr(db, TK_ID, zName);
- zColname = zName;
- zToFree = 0;
- if( longNames || pTabList->nSrc>1 ){
+ if( (pTabList->nSrc>1
+ && ( (pFrom->fg.jointype & JT_LTORJ)==0
+ || (selFlags & SF_NestedFrom)!=0
+ || !inAnyUsingClause(zName,pFrom,pTabList->nSrc-i-1)
+ )
+ )
+ || IN_RENAME_OBJECT
+ ){
Expr *pLeft;
pLeft = sqlite3Expr(db, TK_ID, zTabName);
pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
+ if( IN_RENAME_OBJECT && pE->pLeft ){
+ sqlite3RenameTokenRemap(pParse, pLeft, pE->pLeft);
+ }
if( zSchemaName ){
pLeft = sqlite3Expr(db, TK_ID, zSchemaName);
pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr);
}
- if( longNames ){
- zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName);
- zToFree = zColname;
- }
}else{
pExpr = pRight;
}
+ sqlite3ExprSetErrorOffset(pExpr, iErrOfst);
pNew = sqlite3ExprListAppend(pParse, pNew, pExpr);
- sqlite3TokenInit(&sColname, zColname);
- sqlite3ExprListSetName(pParse, pNew, &sColname, 0);
- if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){
- struct ExprList_item *pX = &pNew->a[pNew->nExpr-1];
- if( pSub ){
- pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan);
- testcase( pX->zSpan==0 );
+ if( pNew==0 ){
+ break; /* OOM */
+ }
+ pX = &pNew->a[pNew->nExpr-1];
+ assert( pX->zEName==0 );
+ if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){
+ if( pNestedFrom ){
+ pX->zEName = sqlite3DbStrDup(db, pNestedFrom->a[j].zEName);
+ testcase( pX->zEName==0 );
}else{
- pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s",
- zSchemaName, zTabName, zColname);
- testcase( pX->zSpan==0 );
+ pX->zEName = sqlite3MPrintf(db, "%s.%s.%s",
+ zSchemaName, zTabName, zName);
+ testcase( pX->zEName==0 );
}
- pX->bSpanIsTab = 1;
+ pX->fg.eEName = (j==pTab->nCol ? ENAME_ROWID : ENAME_TAB);
+ if( (pFrom->fg.isUsing
+ && sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0)
+ || (pUsing && sqlite3IdListIndex(pUsing, zName)>=0)
+ || (j<pTab->nCol && (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND))
+ ){
+ pX->fg.bNoExpand = 1;
+ }
+ }else if( longNames ){
+ pX->zEName = sqlite3MPrintf(db, "%s.%s", zTabName, zName);
+ pX->fg.eEName = ENAME_NAME;
+ }else{
+ pX->zEName = sqlite3DbStrDup(db, zName);
+ pX->fg.eEName = ENAME_NAME;
}
- sqlite3DbFree(db, zToFree);
}
}
if( !tableSeen ){
@@ -128989,29 +148035,12 @@ static int selectExpander(Walker *pWalker, Select *p){
p->selFlags |= SF_ComplexResult;
}
}
- return WRC_Continue;
-}
-
-/*
-** No-op routine for the parse-tree walker.
-**
-** When this routine is the Walker.xExprCallback then expression trees
-** are walked without any actions being taken at each node. Presumably,
-** when this routine is used for Walker.xExprCallback then
-** Walker.xSelectCallback is set to do something useful for every
-** subquery in the parser tree.
-*/
-SQLITE_PRIVATE int sqlite3ExprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
- UNUSED_PARAMETER2(NotUsed, NotUsed2);
- return WRC_Continue;
-}
-
-/*
-** No-op routine for the parse-tree walker for SELECT statements.
-** subquery in the parser tree.
-*/
-SQLITE_PRIVATE int sqlite3SelectWalkNoop(Walker *NotUsed, Select *NotUsed2){
- UNUSED_PARAMETER2(NotUsed, NotUsed2);
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x8 ){
+ TREETRACE(0x8,pParse,p,("After result-set wildcard expansion:\n"));
+ sqlite3TreeViewSelect(0, p, 0);
+ }
+#endif
return WRC_Continue;
}
@@ -129048,7 +148077,8 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
sqlite3WalkSelect(&w, pSelect);
}
w.xSelectCallback = selectExpander;
- w.xSelectCallback2 = selectPopWith;
+ w.xSelectCallback2 = sqlite3SelectPopWith;
+ w.eCode = 0;
sqlite3WalkSelect(&w, pSelect);
}
@@ -129058,25 +148088,26 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
** This is a Walker.xSelectCallback callback for the sqlite3SelectTypeInfo()
** interface.
**
-** For each FROM-clause subquery, add Column.zType and Column.zColl
-** information to the Table structure that represents the result set
-** of that subquery.
+** For each FROM-clause subquery, add Column.zType, Column.zColl, and
+** Column.affinity information to the Table structure that represents
+** the result set of that subquery.
**
** The Table structure that represents the result set was constructed
-** by selectExpander() but the type and collation information was omitted
-** at that point because identifiers had not yet been resolved. This
-** routine is called after identifier resolution.
+** by selectExpander() but the type and collation and affinity information
+** was omitted at that point because identifiers had not yet been resolved.
+** This routine is called after identifier resolution.
*/
static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
Parse *pParse;
int i;
SrcList *pTabList;
- struct SrcList_item *pFrom;
+ SrcItem *pFrom;
- assert( p->selFlags & SF_Resolved );
if( p->selFlags & SF_HasTypeInfo ) return;
p->selFlags |= SF_HasTypeInfo;
pParse = pWalker->pParse;
+ testcase( (p->selFlags & SF_Resolved)==0 );
+ assert( (p->selFlags & SF_Resolved) || IN_RENAME_OBJECT );
pTabList = p->pSrc;
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab = pFrom->pTab;
@@ -129085,8 +148116,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
/* A sub-query in the FROM clause of a SELECT */
Select *pSel = pFrom->pSelect;
if( pSel ){
- while( pSel->pPrior ) pSel = pSel->pPrior;
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel);
+ sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE);
}
}
}
@@ -129131,15 +148161,194 @@ SQLITE_PRIVATE void sqlite3SelectPrep(
NameContext *pOuterNC /* Name context for container */
){
assert( p!=0 || pParse->db->mallocFailed );
+ assert( pParse->db->pParse==pParse );
if( pParse->db->mallocFailed ) return;
if( p->selFlags & SF_HasTypeInfo ) return;
sqlite3SelectExpand(pParse, p);
- if( pParse->nErr || pParse->db->mallocFailed ) return;
+ if( pParse->nErr ) return;
sqlite3ResolveSelectNames(pParse, p, pOuterNC);
- if( pParse->nErr || pParse->db->mallocFailed ) return;
+ if( pParse->nErr ) return;
sqlite3SelectAddTypeInfo(pParse, p);
}
+#if TREETRACE_ENABLED
+/*
+** Display all information about an AggInfo object
+*/
+static void printAggInfo(AggInfo *pAggInfo){
+ int ii;
+ for(ii=0; ii<pAggInfo->nColumn; ii++){
+ struct AggInfo_col *pCol = &pAggInfo->aCol[ii];
+ sqlite3DebugPrintf(
+ "agg-column[%d] pTab=%s iTable=%d iColumn=%d iMem=%d"
+ " iSorterColumn=%d %s\n",
+ ii, pCol->pTab ? pCol->pTab->zName : "NULL",
+ pCol->iTable, pCol->iColumn, pAggInfo->iFirstReg+ii,
+ pCol->iSorterColumn,
+ ii>=pAggInfo->nAccumulator ? "" : " Accumulator");
+ sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0);
+ }
+ for(ii=0; ii<pAggInfo->nFunc; ii++){
+ sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n",
+ ii, pAggInfo->iFirstReg+pAggInfo->nColumn+ii);
+ sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pFExpr, 0);
+ }
+}
+#endif /* TREETRACE_ENABLED */
+
+/*
+** Analyze the arguments to aggregate functions. Create new pAggInfo->aCol[]
+** entries for columns that are arguments to aggregate functions but which
+** are not otherwise used.
+**
+** The aCol[] entries in AggInfo prior to nAccumulator are columns that
+** are referenced outside of aggregate functions. These might be columns
+** that are part of the GROUP by clause, for example. Other database engines
+** would throw an error if there is a column reference that is not in the
+** GROUP BY clause and that is not part of an aggregate function argument.
+** But SQLite allows this.
+**
+** The aCol[] entries beginning with the aCol[nAccumulator] and following
+** are column references that are used exclusively as arguments to
+** aggregate functions. This routine is responsible for computing
+** (or recomputing) those aCol[] entries.
+*/
+static void analyzeAggFuncArgs(
+ AggInfo *pAggInfo,
+ NameContext *pNC
+){
+ int i;
+ assert( pAggInfo!=0 );
+ assert( pAggInfo->iFirstReg==0 );
+ pNC->ncFlags |= NC_InAggFunc;
+ for(i=0; i<pAggInfo->nFunc; i++){
+ Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
+ assert( pExpr->op==TK_FUNCTION || pExpr->op==TK_AGG_FUNCTION );
+ assert( ExprUseXList(pExpr) );
+ sqlite3ExprAnalyzeAggList(pNC, pExpr->x.pList);
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ sqlite3ExprAnalyzeAggList(pNC, pExpr->pLeft->x.pList);
+ }
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ assert( !IsWindowFunc(pExpr) );
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ sqlite3ExprAnalyzeAggregates(pNC, pExpr->y.pWin->pFilter);
+ }
+#endif
+ }
+ pNC->ncFlags &= ~NC_InAggFunc;
+}
+
+/*
+** An index on expressions is being used in the inner loop of an
+** aggregate query with a GROUP BY clause. This routine attempts
+** to adjust the AggInfo object to take advantage of index and to
+** perhaps use the index as a covering index.
+**
+*/
+static void optimizeAggregateUseOfIndexedExpr(
+ Parse *pParse, /* Parsing context */
+ Select *pSelect, /* The SELECT statement being processed */
+ AggInfo *pAggInfo, /* The aggregate info */
+ NameContext *pNC /* Name context used to resolve agg-func args */
+){
+ assert( pAggInfo->iFirstReg==0 );
+ assert( pSelect!=0 );
+ assert( pSelect->pGroupBy!=0 );
+ pAggInfo->nColumn = pAggInfo->nAccumulator;
+ if( ALWAYS(pAggInfo->nSortingColumn>0) ){
+ int mx = pSelect->pGroupBy->nExpr - 1;
+ int j, k;
+ for(j=0; j<pAggInfo->nColumn; j++){
+ k = pAggInfo->aCol[j].iSorterColumn;
+ if( k>mx ) mx = k;
+ }
+ pAggInfo->nSortingColumn = mx+1;
+ }
+ analyzeAggFuncArgs(pAggInfo, pNC);
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x20 ){
+ IndexedExpr *pIEpr;
+ TREETRACE(0x20, pParse, pSelect,
+ ("AggInfo (possibly) adjusted for Indexed Exprs\n"));
+ sqlite3TreeViewSelect(0, pSelect, 0);
+ for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){
+ printf("data-cursor=%d index={%d,%d}\n",
+ pIEpr->iDataCur, pIEpr->iIdxCur, pIEpr->iIdxCol);
+ sqlite3TreeViewExpr(0, pIEpr->pExpr, 0);
+ }
+ printAggInfo(pAggInfo);
+ }
+#else
+ UNUSED_PARAMETER(pSelect);
+ UNUSED_PARAMETER(pParse);
+#endif
+}
+
+/*
+** Walker callback for aggregateConvertIndexedExprRefToColumn().
+*/
+static int aggregateIdxEprRefToColCallback(Walker *pWalker, Expr *pExpr){
+ AggInfo *pAggInfo;
+ struct AggInfo_col *pCol;
+ UNUSED_PARAMETER(pWalker);
+ if( pExpr->pAggInfo==0 ) return WRC_Continue;
+ if( pExpr->op==TK_AGG_COLUMN ) return WRC_Continue;
+ if( pExpr->op==TK_AGG_FUNCTION ) return WRC_Continue;
+ if( pExpr->op==TK_IF_NULL_ROW ) return WRC_Continue;
+ pAggInfo = pExpr->pAggInfo;
+ if( NEVER(pExpr->iAgg>=pAggInfo->nColumn) ) return WRC_Continue;
+ assert( pExpr->iAgg>=0 );
+ pCol = &pAggInfo->aCol[pExpr->iAgg];
+ pExpr->op = TK_AGG_COLUMN;
+ pExpr->iTable = pCol->iTable;
+ pExpr->iColumn = pCol->iColumn;
+ ExprClearProperty(pExpr, EP_Skip|EP_Collate|EP_Unlikely);
+ return WRC_Prune;
+}
+
+/*
+** Convert every pAggInfo->aFunc[].pExpr such that any node within
+** those expressions that has pAppInfo set is changed into a TK_AGG_COLUMN
+** opcode.
+*/
+static void aggregateConvertIndexedExprRefToColumn(AggInfo *pAggInfo){
+ int i;
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = aggregateIdxEprRefToColCallback;
+ for(i=0; i<pAggInfo->nFunc; i++){
+ sqlite3WalkExpr(&w, pAggInfo->aFunc[i].pFExpr);
+ }
+}
+
+
+/*
+** Allocate a block of registers so that there is one register for each
+** pAggInfo->aCol[] and pAggInfo->aFunc[] entry in pAggInfo. The first
+** register in this block is stored in pAggInfo->iFirstReg.
+**
+** This routine may only be called once for each AggInfo object. Prior
+** to calling this routine:
+**
+** * The aCol[] and aFunc[] arrays may be modified
+** * The AggInfoColumnReg() and AggInfoFuncReg() macros may not be used
+**
+** After calling this routine:
+**
+** * The aCol[] and aFunc[] arrays are fixed
+** * The AggInfoColumnReg() and AggInfoFuncReg() macros may be used
+**
+*/
+static void assignAggregateRegisters(Parse *pParse, AggInfo *pAggInfo){
+ assert( pAggInfo!=0 );
+ assert( pAggInfo->iFirstReg==0 );
+ pAggInfo->iFirstReg = pParse->nMem + 1;
+ pParse->nMem += pAggInfo->nColumn + pAggInfo->nFunc;
+}
+
/*
** Reset the aggregate accumulator.
**
@@ -129153,34 +148362,58 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
int i;
struct AggInfo_func *pFunc;
int nReg = pAggInfo->nFunc + pAggInfo->nColumn;
+ assert( pAggInfo->iFirstReg>0 );
+ assert( pParse->db->pParse==pParse );
+ assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 );
if( nReg==0 ) return;
-#ifdef SQLITE_DEBUG
- /* Verify that all AggInfo registers are within the range specified by
- ** AggInfo.mnReg..AggInfo.mxReg */
- assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 );
- for(i=0; i<pAggInfo->nColumn; i++){
- assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg
- && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg );
- }
- for(i=0; i<pAggInfo->nFunc; i++){
- assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg
- && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg );
- }
-#endif
- sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg);
+ if( pParse->nErr ) return;
+ sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->iFirstReg,
+ pAggInfo->iFirstReg+nReg-1);
for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){
if( pFunc->iDistinct>=0 ){
- Expr *pE = pFunc->pExpr;
- assert( !ExprHasProperty(pE, EP_xIsSelect) );
+ Expr *pE = pFunc->pFExpr;
+ assert( ExprUseXList(pE) );
if( pE->x.pList==0 || pE->x.pList->nExpr!=1 ){
sqlite3ErrorMsg(pParse, "DISTINCT aggregates must have exactly one "
"argument");
pFunc->iDistinct = -1;
}else{
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pE->x.pList,0,0);
- sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
- (char*)pKeyInfo, P4_KEYINFO);
+ pFunc->iDistAddr = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
+ pFunc->iDistinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO);
+ ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(DISTINCT)",
+ pFunc->pFunc->zName));
+ }
+ }
+ if( pFunc->iOBTab>=0 ){
+ ExprList *pOBList;
+ KeyInfo *pKeyInfo;
+ int nExtra = 0;
+ assert( pFunc->pFExpr->pLeft!=0 );
+ assert( pFunc->pFExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pFunc->pFExpr->pLeft) );
+ assert( pFunc->pFunc!=0 );
+ pOBList = pFunc->pFExpr->pLeft->x.pList;
+ if( !pFunc->bOBUnique ){
+ nExtra++; /* One extra column for the OP_Sequence */
+ }
+ if( pFunc->bOBPayload ){
+ /* extra columns for the function arguments */
+ assert( ExprUseXList(pFunc->pFExpr) );
+ nExtra += pFunc->pFExpr->x.pList->nExpr;
+ }
+ if( pFunc->bUseSubtype ){
+ nExtra += pFunc->pFExpr->x.pList->nExpr;
+ }
+ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra);
+ if( !pFunc->bOBUnique && pParse->nErr==0 ){
+ pKeyInfo->nKeyField++;
}
+ sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
+ pFunc->iOBTab, pOBList->nExpr+nExtra, 0,
+ (char*)pKeyInfo, P4_KEYINFO);
+ ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(ORDER BY)",
+ pFunc->pFunc->zName));
}
}
}
@@ -129194,24 +148427,81 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
int i;
struct AggInfo_func *pF;
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
- ExprList *pList = pF->pExpr->x.pList;
- assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
- sqlite3VdbeAddOp2(v, OP_AggFinal, pF->iMem, pList ? pList->nExpr : 0);
+ ExprList *pList;
+ assert( ExprUseXList(pF->pFExpr) );
+ pList = pF->pFExpr->x.pList;
+ if( pF->iOBTab>=0 ){
+ /* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs
+ ** were stored in emphermal table pF->iOBTab. Here, we extract those
+ ** inputs (in ORDER BY order) and make all calls to OP_AggStep
+ ** before doing the OP_AggFinal call. */
+ int iTop; /* Start of loop for extracting columns */
+ int nArg; /* Number of columns to extract */
+ int nKey; /* Key columns to be skipped */
+ int regAgg; /* Extract into this array */
+ int j; /* Loop counter */
+
+ assert( pF->pFunc!=0 );
+ nArg = pList->nExpr;
+ regAgg = sqlite3GetTempRange(pParse, nArg);
+
+ if( pF->bOBPayload==0 ){
+ nKey = 0;
+ }else{
+ assert( pF->pFExpr->pLeft!=0 );
+ assert( ExprUseXList(pF->pFExpr->pLeft) );
+ assert( pF->pFExpr->pLeft->x.pList!=0 );
+ nKey = pF->pFExpr->pLeft->x.pList->nExpr;
+ if( ALWAYS(!pF->bOBUnique) ) nKey++;
+ }
+ iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab); VdbeCoverage(v);
+ for(j=nArg-1; j>=0; j--){
+ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j);
+ }
+ if( pF->bUseSubtype ){
+ int regSubtype = sqlite3GetTempReg(pParse);
+ int iBaseCol = nKey + nArg + (pF->bOBPayload==0 && pF->bOBUnique==0);
+ for(j=nArg-1; j>=0; j--){
+ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, iBaseCol+j, regSubtype);
+ sqlite3VdbeAddOp2(v, OP_SetSubtype, regSubtype, regAgg+j);
+ }
+ sqlite3ReleaseTempReg(pParse, regSubtype);
+ }
+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
+ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, (u8)nArg);
+ sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, iTop);
+ sqlite3ReleaseTempRange(pParse, regAgg, nArg);
+ }
+ sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
+ pList ? pList->nExpr : 0);
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
}
}
-
/*
-** Update the accumulator memory cells for an aggregate based on
-** the current cursor position.
+** Generate code that will update the accumulator memory cells for an
+** aggregate based on the current cursor position.
**
** If regAcc is non-zero and there are no min() or max() aggregates
** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator
-** registers i register regAcc contains 0. The caller will take care
+** registers if register regAcc contains 0. The caller will take care
** of setting and clearing regAcc.
+**
+** For an ORDER BY aggregate, the actual accumulator memory cell update
+** is deferred until after all input rows have been received, so that they
+** can be run in the requested order. In that case, instead of invoking
+** OP_AggStep to update the accumulator, just add the arguments that would
+** have been passed into OP_AggStep into the sorting ephemeral table
+** (along with the appropriate sort key).
*/
-static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
+static void updateAccumulator(
+ Parse *pParse,
+ int regAcc,
+ AggInfo *pAggInfo,
+ int eDistinctType
+){
Vdbe *v = pParse->pVdbe;
int i;
int regHit = 0;
@@ -129219,45 +148509,132 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
struct AggInfo_func *pF;
struct AggInfo_col *pC;
+ assert( pAggInfo->iFirstReg>0 );
+ if( pParse->nErr ) return;
pAggInfo->directMode = 1;
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
int nArg;
int addrNext = 0;
int regAgg;
- ExprList *pList = pF->pExpr->x.pList;
- assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
- if( pList ){
+ int regAggSz = 0;
+ int regDistinct = 0;
+ ExprList *pList;
+ assert( ExprUseXList(pF->pFExpr) );
+ assert( !IsWindowFunc(pF->pFExpr) );
+ assert( pF->pFunc!=0 );
+ pList = pF->pFExpr->x.pList;
+ if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){
+ Expr *pFilter = pF->pFExpr->y.pWin->pFilter;
+ if( pAggInfo->nAccumulator
+ && (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
+ && regAcc
+ ){
+ /* If regAcc==0, there there exists some min() or max() function
+ ** without a FILTER clause that will ensure the magnet registers
+ ** are populated. */
+ if( regHit==0 ) regHit = ++pParse->nMem;
+ /* If this is the first row of the group (regAcc contains 0), clear the
+ ** "magnet" register regHit so that the accumulator registers
+ ** are populated if the FILTER clause jumps over the the
+ ** invocation of min() or max() altogether. Or, if this is not
+ ** the first row (regAcc contains 1), set the magnet register so that
+ ** the accumulators are not populated unless the min()/max() is invoked
+ ** and indicates that they should be. */
+ sqlite3VdbeAddOp2(v, OP_Copy, regAcc, regHit);
+ }
+ addrNext = sqlite3VdbeMakeLabel(pParse);
+ sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL);
+ }
+ if( pF->iOBTab>=0 ){
+ /* Instead of invoking AggStep, we must push the arguments that would
+ ** have been passed to AggStep onto the sorting table. */
+ int jj; /* Registered used so far in building the record */
+ ExprList *pOBList; /* The ORDER BY clause */
+ assert( pList!=0 );
+ nArg = pList->nExpr;
+ assert( nArg>0 );
+ assert( pF->pFExpr->pLeft!=0 );
+ assert( pF->pFExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pF->pFExpr->pLeft) );
+ pOBList = pF->pFExpr->pLeft->x.pList;
+ assert( pOBList!=0 );
+ assert( pOBList->nExpr>0 );
+ regAggSz = pOBList->nExpr;
+ if( !pF->bOBUnique ){
+ regAggSz++; /* One register for OP_Sequence */
+ }
+ if( pF->bOBPayload ){
+ regAggSz += nArg;
+ }
+ if( pF->bUseSubtype ){
+ regAggSz += nArg;
+ }
+ regAggSz++; /* One extra register to hold result of MakeRecord */
+ regAgg = sqlite3GetTempRange(pParse, regAggSz);
+ regDistinct = regAgg;
+ sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP);
+ jj = pOBList->nExpr;
+ if( !pF->bOBUnique ){
+ sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj);
+ jj++;
+ }
+ if( pF->bOBPayload ){
+ regDistinct = regAgg+jj;
+ sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP);
+ jj += nArg;
+ }
+ if( pF->bUseSubtype ){
+ int kk;
+ int regBase = pF->bOBPayload ? regDistinct : regAgg;
+ for(kk=0; kk<nArg; kk++, jj++){
+ sqlite3VdbeAddOp2(v, OP_GetSubtype, regBase+kk, regAgg+jj);
+ }
+ }
+ }else if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
+ regDistinct = regAgg;
sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
}else{
nArg = 0;
regAgg = 0;
}
- if( pF->iDistinct>=0 ){
- addrNext = sqlite3VdbeMakeLabel(v);
- testcase( nArg==0 ); /* Error condition */
- testcase( nArg>1 ); /* Also an error */
- codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
- }
- if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
- CollSeq *pColl = 0;
- struct ExprList_item *pItem;
- int j;
- assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */
- for(j=0, pItem=pList->a; !pColl && j<nArg; j++, pItem++){
- pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
+ if( pF->iDistinct>=0 && pList ){
+ if( addrNext==0 ){
+ addrNext = sqlite3VdbeMakeLabel(pParse);
}
- if( !pColl ){
- pColl = pParse->db->pDfltColl;
+ pF->iDistinct = codeDistinct(pParse, eDistinctType,
+ pF->iDistinct, addrNext, pList, regDistinct);
+ }
+ if( pF->iOBTab>=0 ){
+ /* Insert a new record into the ORDER BY table */
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regAgg, regAggSz-1,
+ regAgg+regAggSz-1);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pF->iOBTab, regAgg+regAggSz-1,
+ regAgg, regAggSz-1);
+ sqlite3ReleaseTempRange(pParse, regAgg, regAggSz);
+ }else{
+ /* Invoke the AggStep function */
+ if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
+ CollSeq *pColl = 0;
+ struct ExprList_item *pItem;
+ int j;
+ assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */
+ for(j=0, pItem=pList->a; !pColl && j<nArg; j++, pItem++){
+ pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
+ }
+ if( !pColl ){
+ pColl = pParse->db->pDfltColl;
+ }
+ if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
+ sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
+ (char *)pColl, P4_COLLSEQ);
}
- if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
- sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
+ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, (u8)nArg);
+ sqlite3ReleaseTempRange(pParse, regAgg, nArg);
}
- sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, pF->iMem);
- sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, (u8)nArg);
- sqlite3ReleaseTempRange(pParse, regAgg, nArg);
if( addrNext ){
sqlite3VdbeResolveLabel(v, addrNext);
}
@@ -129269,11 +148646,12 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v);
}
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
- sqlite3ExprCode(pParse, pC->pExpr, pC->iMem);
+ sqlite3ExprCode(pParse, pC->pCExpr, AggInfoColumnReg(pAggInfo,i));
}
+
pAggInfo->directMode = 0;
if( addrHitTest ){
- sqlite3VdbeJumpHere(v, addrHitTest);
+ sqlite3VdbeJumpHereOrPopInst(v, addrHitTest);
}
}
@@ -129289,7 +148667,7 @@ static void explainSimpleCount(
){
if( pParse->explain==2 ){
int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx)));
- sqlite3VdbeExplain(pParse, 0, "SCAN TABLE %s%s%s",
+ sqlite3VdbeExplain(pParse, 0, "SCAN %s%s%s",
pTab->zName,
bCover ? " USING COVERING INDEX " : "",
bCover ? pIdx->zName : ""
@@ -129303,10 +148681,10 @@ static void explainSimpleCount(
/*
** sqlite3WalkExpr() callback used by havingToWhere().
**
-** If the node passed to the callback is a TK_AND node, return
+** If the node passed to the callback is a TK_AND node, return
** WRC_Continue to tell sqlite3WalkExpr() to iterate through child nodes.
**
-** Otherwise, return WRC_Prune. In this case, also check if the
+** Otherwise, return WRC_Prune. In this case, also check if the
** sub-expression matches the criteria for being moved to the WHERE
** clause. If so, add it to the WHERE clause and replace the sub-expression
** within the HAVING expression with a constant "1".
@@ -129314,13 +148692,23 @@ static void explainSimpleCount(
static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
if( pExpr->op!=TK_AND ){
Select *pS = pWalker->u.pSelect;
- if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){
+ /* This routine is called before the HAVING clause of the current
+ ** SELECT is analyzed for aggregates. So if pExpr->pAggInfo is set
+ ** here, it indicates that the expression is a correlated reference to a
+ ** column from an outer aggregate query, or an aggregate function that
+ ** belongs to an outer query. Do not move the expression to the WHERE
+ ** clause in this obscure case, as doing so may corrupt the outer Select
+ ** statements AggInfo structure. */
+ if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy)
+ && ExprAlwaysFalse(pExpr)==0
+ && pExpr->pAggInfo==0
+ ){
sqlite3 *db = pWalker->pParse->db;
- Expr *pNew = sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[1], 0);
+ Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1");
if( pNew ){
Expr *pWhere = pS->pWhere;
SWAP(Expr, *pNew, *pExpr);
- pNew = sqlite3ExprAnd(db, pWhere, pNew);
+ pNew = sqlite3ExprAnd(pWalker->pParse, pWhere, pNew);
pS->pWhere = pNew;
pWalker->eCode = 1;
}
@@ -129352,33 +148740,47 @@ static void havingToWhere(Parse *pParse, Select *p){
sWalker.xExprCallback = havingToWhereExprCb;
sWalker.u.pSelect = p;
sqlite3WalkExpr(&sWalker, p->pHaving);
-#if SELECTTRACE_ENABLED
- if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){
- SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
+#if TREETRACE_ENABLED
+ if( sWalker.eCode && (sqlite3TreeTrace & 0x100)!=0 ){
+ TREETRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
}
/*
-** Check to see if the pThis entry of pTabList is a self-join of a prior view.
-** If it is, then return the SrcList_item for the prior view. If it is not,
-** then return 0.
+** Check to see if the pThis entry of pTabList is a self-join of another view.
+** Search FROM-clause entries in the range of iFirst..iEnd, including iFirst
+** but stopping before iEnd.
+**
+** If pThis is a self-join, then return the SrcItem for the first other
+** instance of that view found. If pThis is not a self-join then return 0.
*/
-static struct SrcList_item *isSelfJoinView(
+static SrcItem *isSelfJoinView(
SrcList *pTabList, /* Search for self-joins in this FROM clause */
- struct SrcList_item *pThis /* Search for prior reference to this subquery */
-){
- struct SrcList_item *pItem;
- for(pItem = pTabList->a; pItem<pThis; pItem++){
+ SrcItem *pThis, /* Search for prior reference to this subquery */
+ int iFirst, int iEnd /* Range of FROM-clause entries to search. */
+){
+ SrcItem *pItem;
+ assert( pThis->pSelect!=0 );
+ if( pThis->pSelect->selFlags & SF_PushDown ) return 0;
+ while( iFirst<iEnd ){
+ Select *pS1;
+ pItem = &pTabList->a[iFirst++];
if( pItem->pSelect==0 ) continue;
if( pItem->fg.viaCoroutine ) continue;
if( pItem->zName==0 ) continue;
- if( sqlite3_stricmp(pItem->zDatabase, pThis->zDatabase)!=0 ) continue;
+ assert( pItem->pTab!=0 );
+ assert( pThis->pTab!=0 );
+ if( pItem->pTab->pSchema!=pThis->pTab->pSchema ) continue;
if( sqlite3_stricmp(pItem->zName, pThis->zName)!=0 ) continue;
- if( sqlite3ExprCompare(0,
- pThis->pSelect->pWhere, pItem->pSelect->pWhere, -1)
- ){
+ pS1 = pItem->pSelect;
+ if( pItem->pTab->pSchema==0 && pThis->pSelect->selId!=pS1->selId ){
+ /* The query flattener left two different CTE tables with identical
+ ** names in the same FROM clause. */
+ continue;
+ }
+ if( pItem->pSelect->selFlags & SF_PushDown ){
/* The view was modified by some other optimization such as
** pushDownWhereTerms() */
continue;
@@ -129388,7 +148790,16 @@ static struct SrcList_item *isSelfJoinView(
return 0;
}
-#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
+/*
+** Deallocate a single AggInfo object
+*/
+static void agginfoFree(sqlite3 *db, void *pArg){
+ AggInfo *p = (AggInfo*)pArg;
+ sqlite3DbFree(db, p->aCol);
+ sqlite3DbFree(db, p->aFunc);
+ sqlite3DbFreeNN(db, p);
+}
+
/*
** Attempt to transform a query of the form
**
@@ -129403,7 +148814,8 @@ static struct SrcList_item *isSelfJoinView(
** * The subquery is a UNION ALL of two or more terms
** * The subquery does not have a LIMIT clause
** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries
-** * The outer query is a simple count(*)
+** * The outer query is a simple count(*) with no WHERE clause or other
+** extraneous syntax.
**
** Return TRUE if the optimization is undertaken.
*/
@@ -129414,20 +148826,29 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
sqlite3 *db;
if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate */
if( p->pEList->nExpr!=1 ) return 0; /* Single result column */
+ if( p->pWhere ) return 0;
+ if( p->pHaving ) return 0;
+ if( p->pGroupBy ) return 0;
+ if( p->pOrderBy ) return 0;
pExpr = p->pEList->a[0].pExpr;
if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */
+ assert( ExprUseUToken(pExpr) );
if( sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Is count() */
+ assert( ExprUseXList(pExpr) );
if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */
if( p->pSrc->nSrc!=1 ) return 0; /* One table in FROM */
+ if( ExprHasProperty(pExpr, EP_WinFunc) ) return 0;/* Not a window function */
pSub = p->pSrc->a[0].pSelect;
if( pSub==0 ) return 0; /* The FROM is a subquery */
- if( pSub->pPrior==0 ) return 0; /* Must be a compound ry */
+ if( pSub->pPrior==0 ) return 0; /* Must be a compound */
+ if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */
do{
if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */
if( pSub->pWhere ) return 0; /* No WHERE clause */
if( pSub->pLimit ) return 0; /* No LIMIT clause */
if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */
- pSub = pSub->pPrior; /* Repeat over compound */
+ assert( pSub->pHaving==0 ); /* Due to the previous */
+ pSub = pSub->pPrior; /* Repeat over compound */
}while( pSub );
/* If we reach this point then it is OK to perform the transformation */
@@ -129447,7 +148868,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
pSub->selFlags |= SF_Aggregate;
pSub->selFlags &= ~SF_Compound;
pSub->nSelectRow = 0;
- sqlite3ExprListDelete(db, pSub->pEList);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList);
pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm);
pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
@@ -129462,18 +148883,102 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
p->pEList->a[0].pExpr = pExpr;
p->selFlags &= ~SF_Aggregate;
-#if SELECTTRACE_ENABLED
- if( sqlite3SelectTrace & 0x400 ){
- SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n"));
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x200 ){
+ TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
return 1;
}
-#endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */
/*
-** Generate code for the SELECT statement given in the p argument.
+** If any term of pSrc, or any SF_NestedFrom sub-query, is not the same
+** as pSrcItem but has the same alias as p0, then return true.
+** Otherwise return false.
+*/
+static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){
+ int i;
+ for(i=0; i<pSrc->nSrc; i++){
+ SrcItem *p1 = &pSrc->a[i];
+ if( p1==p0 ) continue;
+ if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){
+ return 1;
+ }
+ if( p1->pSelect
+ && (p1->pSelect->selFlags & SF_NestedFrom)!=0
+ && sameSrcAlias(p0, p1->pSelect->pSrc)
+ ){
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** Return TRUE (non-zero) if the i-th entry in the pTabList SrcList can
+** be implemented as a co-routine. The i-th entry is guaranteed to be
+** a subquery.
+**
+** The subquery is implemented as a co-routine if all of the following are
+** true:
+**
+** (1) The subquery will likely be implemented in the outer loop of
+** the query. This will be the case if any one of the following
+** conditions hold:
+** (a) The subquery is the only term in the FROM clause
+** (b) The subquery is the left-most term and a CROSS JOIN or similar
+** requires it to be the outer loop
+** (c) All of the following are true:
+** (i) The subquery is the left-most subquery in the FROM clause
+** (ii) There is nothing that would prevent the subquery from
+** being used as the outer loop if the sqlite3WhereBegin()
+** routine nominates it to that position.
+** (iii) The query is not a UPDATE ... FROM
+** (2) The subquery is not a CTE that should be materialized because
+** (a) the AS MATERIALIZED keyword is used, or
+** (b) the CTE is used multiple times and does not have the
+** NOT MATERIALIZED keyword
+** (3) The subquery is not part of a left operand for a RIGHT JOIN
+** (4) The SQLITE_Coroutine optimization disable flag is not set
+** (5) The subquery is not self-joined
+*/
+static int fromClauseTermCanBeCoroutine(
+ Parse *pParse, /* Parsing context */
+ SrcList *pTabList, /* FROM clause */
+ int i, /* Which term of the FROM clause holds the subquery */
+ int selFlags /* Flags on the SELECT statement */
+){
+ SrcItem *pItem = &pTabList->a[i];
+ if( pItem->fg.isCte ){
+ const CteUse *pCteUse = pItem->u2.pCteUse;
+ if( pCteUse->eM10d==M10d_Yes ) return 0; /* (2a) */
+ if( pCteUse->nUse>=2 && pCteUse->eM10d!=M10d_No ) return 0; /* (2b) */
+ }
+ if( pTabList->a[0].fg.jointype & JT_LTORJ ) return 0; /* (3) */
+ if( OptimizationDisabled(pParse->db, SQLITE_Coroutines) ) return 0; /* (4) */
+ if( isSelfJoinView(pTabList, pItem, i+1, pTabList->nSrc)!=0 ){
+ return 0; /* (5) */
+ }
+ if( i==0 ){
+ if( pTabList->nSrc==1 ) return 1; /* (1a) */
+ if( pTabList->a[1].fg.jointype & JT_CROSS ) return 1; /* (1b) */
+ if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */
+ return 1;
+ }
+ if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */
+ while( 1 /*exit-by-break*/ ){
+ if( pItem->fg.jointype & (JT_OUTER|JT_CROSS) ) return 0; /* (1c-ii) */
+ if( i==0 ) break;
+ i--;
+ pItem--;
+ if( pItem->pSelect!=0 ) return 0; /* (1c-i) */
+ }
+ return 1;
+}
+
+/*
+** Generate code for the SELECT statement given in the p argument.
**
** The results are returned according to the SelectDest structure.
** See comments in sqliteInt.h for further information.
@@ -129499,26 +149004,31 @@ SQLITE_PRIVATE int sqlite3Select(
Expr *pWhere; /* The WHERE clause. May be NULL */
ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */
Expr *pHaving; /* The HAVING clause. May be NULL */
+ AggInfo *pAggInfo = 0; /* Aggregate information */
int rc = 1; /* Value to return from this function */
DistinctCtx sDistinct; /* Info on how to code the DISTINCT keyword */
SortCtx sSort; /* Info on how to code the ORDER BY clause */
- AggInfo sAggInfo; /* Information used by aggregate queries */
int iEnd; /* Address of the end of the query */
sqlite3 *db; /* The database connection */
ExprList *pMinMaxOrderBy = 0; /* Added ORDER BY for min/max queries */
u8 minMaxFlag; /* Flag for min/max queries */
db = pParse->db;
+ assert( pParse==db->pParse );
v = sqlite3GetVdbe(pParse);
- if( p==0 || db->mallocFailed || pParse->nErr ){
+ if( p==0 || pParse->nErr ){
return 1;
}
+ assert( db->mallocFailed==0 );
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
- memset(&sAggInfo, 0, sizeof(sAggInfo));
-#if SELECTTRACE_ENABLED
- SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain));
- if( sqlite3SelectTrace & 0x100 ){
- sqlite3TreeViewSelect(0, p, 0);
+#if TREETRACE_ENABLED
+ TREETRACE(0x1,pParse,p, ("begin processing:\n", pParse->addrExplain));
+ if( sqlite3TreeTrace & 0x10000 ){
+ if( (sqlite3TreeTrace & 0x10001)==0x10000 ){
+ sqlite3TreeViewLine(0, "In sqlite3Select() at %s:%d",
+ __FILE__, __LINE__);
+ }
+ sqlite3ShowSelect(p);
}
#endif
@@ -129526,40 +149036,77 @@ SQLITE_PRIVATE int sqlite3Select(
assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo );
assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue );
assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue );
- if( IgnorableOrderby(pDest) ){
- assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union ||
- pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard ||
- pDest->eDest==SRT_Queue || pDest->eDest==SRT_DistFifo ||
- pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_Fifo);
- /* If ORDER BY makes no difference in the output then neither does
- ** DISTINCT so it can be removed too. */
- sqlite3ExprListDelete(db, p->pOrderBy);
- p->pOrderBy = 0;
+ if( IgnorableDistinct(pDest) ){
+ assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union ||
+ pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard ||
+ pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo );
+ /* All of these destinations are also able to ignore the ORDER BY clause */
+ if( p->pOrderBy ){
+#if TREETRACE_ENABLED
+ TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n"));
+ if( sqlite3TreeTrace & 0x800 ){
+ sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
+ }
+#endif
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
+ p->pOrderBy);
+ testcase( pParse->earlyCleanup );
+ p->pOrderBy = 0;
+ }
p->selFlags &= ~SF_Distinct;
+ p->selFlags |= SF_NoopOrderBy;
}
sqlite3SelectPrep(pParse, p, 0);
- if( pParse->nErr || db->mallocFailed ){
+ if( pParse->nErr ){
goto select_end;
}
+ assert( db->mallocFailed==0 );
assert( p->pEList!=0 );
-#if SELECTTRACE_ENABLED
- if( sqlite3SelectTrace & 0x104 ){
- SELECTTRACE(0x104,pParse,p, ("after name resolution:\n"));
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x10 ){
+ TREETRACE(0x10,pParse,p, ("after name resolution:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
+ /* If the SF_UFSrcCheck flag is set, then this function is being called
+ ** as part of populating the temp table for an UPDATE...FROM statement.
+ ** In this case, it is an error if the target object (pSrc->a[0]) name
+ ** or alias is duplicated within FROM clause (pSrc->a[1..n]).
+ **
+ ** Postgres disallows this case too. The reason is that some other
+ ** systems handle this case differently, and not all the same way,
+ ** which is just confusing. To avoid this, we follow PG's lead and
+ ** disallow it altogether. */
+ if( p->selFlags & SF_UFSrcCheck ){
+ SrcItem *p0 = &p->pSrc->a[0];
+ if( sameSrcAlias(p0, p->pSrc) ){
+ sqlite3ErrorMsg(pParse,
+ "target object/alias may not appear in FROM clause: %s",
+ p0->zAlias ? p0->zAlias : p0->pTab->zName
+ );
+ goto select_end;
+ }
+
+ /* Clear the SF_UFSrcCheck flag. The check has already been performed,
+ ** and leaving this flag set can cause errors if a compound sub-query
+ ** in p->pSrc is flattened into this query and this function called
+ ** again as part of compound SELECT processing. */
+ p->selFlags &= ~SF_UFSrcCheck;
+ }
+
if( pDest->eDest==SRT_Output ){
- generateColumnNames(pParse, p);
+ sqlite3GenerateColumnNames(pParse, p);
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( sqlite3WindowRewrite(pParse, p) ){
+ assert( pParse->nErr );
goto select_end;
}
-#if SELECTTRACE_ENABLED
- if( sqlite3SelectTrace & 0x108 ){
- SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n"));
+#if TREETRACE_ENABLED
+ if( p->pWin && (sqlite3TreeTrace & 0x40)!=0 ){
+ TREETRACE(0x40,pParse,p, ("after window rewrite:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -129569,29 +149116,72 @@ SQLITE_PRIVATE int sqlite3Select(
memset(&sSort, 0, sizeof(sSort));
sSort.pOrderBy = p->pOrderBy;
- /* Try to various optimizations (flattening subqueries, and strength
+ /* Try to do various optimizations (flattening subqueries, and strength
** reduction of join operators) in the FROM clause up into the main query
*/
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
for(i=0; !p->pPrior && i<pTabList->nSrc; i++){
- struct SrcList_item *pItem = &pTabList->a[i];
+ SrcItem *pItem = &pTabList->a[i];
Select *pSub = pItem->pSelect;
Table *pTab = pItem->pTab;
- /* Convert LEFT JOIN into JOIN if there are terms of the right table
- ** of the LEFT JOIN used in the WHERE clause.
+ /* The expander should have already created transient Table objects
+ ** even for FROM clause elements such as subqueries that do not correspond
+ ** to a real table */
+ assert( pTab!=0 );
+
+ /* Try to simplify joins:
+ **
+ ** LEFT JOIN -> JOIN
+ ** RIGHT JOIN -> JOIN
+ ** FULL JOIN -> RIGHT JOIN
+ **
+ ** If terms of the i-th table are used in the WHERE clause in such a
+ ** way that the i-th table cannot be the NULL row of a join, then
+ ** perform the appropriate simplification. This is called
+ ** "OUTER JOIN strength reduction" in the SQLite documentation.
*/
- if( (pItem->fg.jointype & JT_LEFT)!=0
- && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor)
+ if( (pItem->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
+ && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor,
+ pItem->fg.jointype & JT_LTORJ)
&& OptimizationEnabled(db, SQLITE_SimplifyJoin)
){
- SELECTTRACE(0x100,pParse,p,
- ("LEFT-JOIN simplifies to JOIN on term %d\n",i));
- pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
- unsetJoinExpr(p->pWhere, pItem->iCursor);
+ if( pItem->fg.jointype & JT_LEFT ){
+ if( pItem->fg.jointype & JT_RIGHT ){
+ TREETRACE(0x1000,pParse,p,
+ ("FULL-JOIN simplifies to RIGHT-JOIN on term %d\n",i));
+ pItem->fg.jointype &= ~JT_LEFT;
+ }else{
+ TREETRACE(0x1000,pParse,p,
+ ("LEFT-JOIN simplifies to JOIN on term %d\n",i));
+ pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
+ unsetJoinExpr(p->pWhere, pItem->iCursor, 0);
+ }
+ }
+ if( pItem->fg.jointype & JT_LTORJ ){
+ for(j=i+1; j<pTabList->nSrc; j++){
+ SrcItem *pI2 = &pTabList->a[j];
+ if( pI2->fg.jointype & JT_RIGHT ){
+ if( pI2->fg.jointype & JT_LEFT ){
+ TREETRACE(0x1000,pParse,p,
+ ("FULL-JOIN simplifies to LEFT-JOIN on term %d\n",j));
+ pI2->fg.jointype &= ~JT_RIGHT;
+ }else{
+ TREETRACE(0x1000,pParse,p,
+ ("RIGHT-JOIN simplifies to JOIN on term %d\n",j));
+ pI2->fg.jointype &= ~(JT_RIGHT|JT_OUTER);
+ unsetJoinExpr(p->pWhere, pI2->iCursor, 1);
+ }
+ }
+ }
+ for(j=pTabList->nSrc-1; j>=0; j--){
+ pTabList->a[j].fg.jointype &= ~JT_LTORJ;
+ if( pTabList->a[j].fg.jointype & JT_RIGHT ) break;
+ }
+ }
}
- /* No futher action if this term of the FROM clause is no a subquery */
+ /* No further action if this term of the FROM clause is not a subquery */
if( pSub==0 ) continue;
/* Catch mismatch in the declared columns of a view and the number of
@@ -129602,6 +149192,14 @@ SQLITE_PRIVATE int sqlite3Select(
goto select_end;
}
+ /* Do not attempt the usual optimizations (flattening and ORDER BY
+ ** elimination) on a MATERIALIZED common table expression because
+ ** a MATERIALIZED common table expression is an optimization fence.
+ */
+ if( pItem->fg.isCte && pItem->u2.pCteUse->eM10d==M10d_Yes ){
+ continue;
+ }
+
/* Do not try to flatten an aggregate subquery.
**
** Flattening an aggregate subquery is only possible if the outer query
@@ -129612,6 +149210,42 @@ SQLITE_PRIVATE int sqlite3Select(
if( (pSub->selFlags & SF_Aggregate)!=0 ) continue;
assert( pSub->pGroupBy==0 );
+ /* If a FROM-clause subquery has an ORDER BY clause that is not
+ ** really doing anything, then delete it now so that it does not
+ ** interfere with query flattening. See the discussion at
+ ** https://sqlite.org/forum/forumpost/2d76f2bcf65d256a
+ **
+ ** Beware of these cases where the ORDER BY clause may not be safely
+ ** omitted:
+ **
+ ** (1) There is also a LIMIT clause
+ ** (2) The subquery was added to help with window-function
+ ** processing
+ ** (3) The subquery is in the FROM clause of an UPDATE
+ ** (4) The outer query uses an aggregate function other than
+ ** the built-in count(), min(), or max().
+ ** (5) The ORDER BY isn't going to accomplish anything because
+ ** one of:
+ ** (a) The outer query has a different ORDER BY clause
+ ** (b) The subquery is part of a join
+ ** See forum post 062d576715d277c8
+ **
+ ** Also retain the ORDER BY if the OmitOrderBy optimization is disabled.
+ */
+ if( pSub->pOrderBy!=0
+ && (p->pOrderBy!=0 || pTabList->nSrc>1) /* Condition (5) */
+ && pSub->pLimit==0 /* Condition (1) */
+ && (pSub->selFlags & SF_OrderByReqd)==0 /* Condition (2) */
+ && (p->selFlags & SF_OrderByReqd)==0 /* Condition (3) and (4) */
+ && OptimizationEnabled(db, SQLITE_OmitOrderBy)
+ ){
+ TREETRACE(0x800,pParse,p,
+ ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
+ pSub->pOrderBy);
+ pSub->pOrderBy = 0;
+ }
+
/* If the outer query contains a "complex" result set (that is,
** if the result set of the outer query uses functions or subqueries)
** and if the subquery contains an ORDER BY clause and if
@@ -129634,12 +149268,13 @@ SQLITE_PRIVATE int sqlite3Select(
&& i==0
&& (p->selFlags & SF_ComplexResult)!=0
&& (pTabList->nSrc==1
- || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0)
+ || (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0)
){
continue;
}
if( flattenSubquery(pParse, p, i, isAgg) ){
+ if( pParse->nErr ) goto select_end;
/* This subquery can be absorbed into its parent. */
i = -1;
}
@@ -129657,9 +149292,9 @@ SQLITE_PRIVATE int sqlite3Select(
*/
if( p->pPrior ){
rc = multiSelect(pParse, p, pDest);
-#if SELECTTRACE_ENABLED
- SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
- if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
+#if TREETRACE_ENABLED
+ TREETRACE(0x400,pParse,p,("end compound-select processing\n"));
+ if( (sqlite3TreeTrace & 0x400)!=0 && ExplainQueryPlanParent(pParse)==0 ){
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -129673,36 +149308,35 @@ SQLITE_PRIVATE int sqlite3Select(
** as the equivalent optimization will be handled by query planner in
** sqlite3WhereBegin().
*/
- if( pTabList->nSrc>1
+ if( p->pWhere!=0
+ && p->pWhere->op==TK_AND
&& OptimizationEnabled(db, SQLITE_PropagateConst)
&& propagateConstants(pParse, p)
){
-#if SELECTTRACE_ENABLED
- if( sqlite3SelectTrace & 0x100 ){
- SELECTTRACE(0x100,pParse,p,("After constant propagation:\n"));
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x2000 ){
+ TREETRACE(0x2000,pParse,p,("After constant propagation:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
}else{
- SELECTTRACE(0x100,pParse,p,("Constant propagation not helpful\n"));
+ TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n"));
}
-#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
if( OptimizationEnabled(db, SQLITE_QueryFlattener|SQLITE_CountOfView)
&& countOfViewOptimization(pParse, p)
){
if( db->mallocFailed ) goto select_end;
- pEList = p->pEList;
pTabList = p->pSrc;
}
-#endif
/* For each term in the FROM clause, do two things:
** (1) Authorized unreferenced tables
** (2) Generate code for all sub-queries
*/
for(i=0; i<pTabList->nSrc; i++){
- struct SrcList_item *pItem = &pTabList->a[i];
+ SrcItem *pItem = &pTabList->a[i];
+ SrcItem *pPrior;
SelectDest dest;
Select *pSub;
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
@@ -129725,7 +149359,7 @@ SQLITE_PRIVATE int sqlite3Select(
** assume the column name is non-NULL and segfault. The use of an empty
** string for the fake column name seems safer.
*/
- if( pItem->colUsed==0 ){
+ if( pItem->colUsed==0 && pItem->zName!=0 ){
sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", pItem->zDatabase);
}
@@ -129735,22 +149369,8 @@ SQLITE_PRIVATE int sqlite3Select(
pSub = pItem->pSelect;
if( pSub==0 ) continue;
- /* Sometimes the code for a subquery will be generated more than
- ** once, if the subquery is part of the WHERE clause in a LEFT JOIN,
- ** for example. In that case, do not regenerate the code to manifest
- ** a view or the co-routine to implement a view. The first instance
- ** is sufficient, though the subroutine to manifest the view does need
- ** to be invoked again. */
- if( pItem->addrFillSub ){
- if( pItem->fg.viaCoroutine==0 ){
- /* The subroutine that manifests the view might be a one-time routine,
- ** or it might need to be rerun on each iteration because it
- ** encodes a correlated subquery. */
- testcase( sqlite3VdbeGetOp(v, pItem->addrFillSub)->opcode==OP_Once );
- sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub);
- }
- continue;
- }
+ /* The code for a subquery should only be generated once. */
+ assert( pItem->addrFillSub==0 );
/* Increment Parse.nHeight by the height of the largest expression
** tree referred to by this, the parent select. The child select
@@ -129765,47 +149385,55 @@ SQLITE_PRIVATE int sqlite3Select(
** inside the subquery. This can help the subquery to run more efficiently.
*/
if( OptimizationEnabled(db, SQLITE_PushDown)
- && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
- (pItem->fg.jointype & JT_OUTER)!=0)
+ && (pItem->fg.isCte==0
+ || (pItem->u2.pCteUse->eM10d!=M10d_Yes && pItem->u2.pCteUse->nUse<2))
+ && pushDownWhereTerms(pParse, pSub, p->pWhere, pTabList, i)
){
-#if SELECTTRACE_ENABLED
- if( sqlite3SelectTrace & 0x100 ){
- SELECTTRACE(0x100,pParse,p,
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x4000 ){
+ TREETRACE(0x4000,pParse,p,
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
+ assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 );
}else{
- SELECTTRACE(0x100,pParse,p,("Push-down not possible\n"));
+ TREETRACE(0x4000,pParse,p,("Push-down not possible\n"));
+ }
+
+ /* Convert unused result columns of the subquery into simple NULL
+ ** expressions, to avoid unneeded searching and computation.
+ */
+ if( OptimizationEnabled(db, SQLITE_NullUnusedCols)
+ && disableUnusedSubqueryResultColumns(pItem)
+ ){
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x4000 ){
+ TREETRACE(0x4000,pParse,p,
+ ("Change unused result columns to NULL for subquery %d:\n",
+ pSub->selId));
+ sqlite3TreeViewSelect(0, p, 0);
+ }
+#endif
}
zSavedAuthContext = pParse->zAuthContext;
pParse->zAuthContext = pItem->zName;
/* Generate code to implement the subquery
- **
- ** The subquery is implemented as a co-routine if the subquery is
- ** guaranteed to be the outer loop (so that it does not need to be
- ** computed more than once)
- **
- ** TODO: Are there other reasons beside (1) to use a co-routine
- ** implementation?
*/
- if( i==0
- && (pTabList->nSrc==1
- || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */
- ){
+ if( fromClauseTermCanBeCoroutine(pParse, pTabList, i, p->selFlags) ){
/* Implement a co-routine that will return a single row of the result
** set on each invocation.
*/
int addrTop = sqlite3VdbeCurrentAddr(v)+1;
-
+
pItem->regReturn = ++pParse->nMem;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop);
- VdbeComment((v, "%s", pItem->pTab->zName));
+ VdbeComment((v, "%!S", pItem));
pItem->addrFillSub = addrTop;
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
- ExplainQueryPlan((pParse, 1, "CO-ROUTINE %u", pSub->selId));
+ ExplainQueryPlan((pParse, 1, "CO-ROUTINE %!S", pItem));
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = pSub->nSelectRow;
pItem->fg.viaCoroutine = 1;
@@ -129813,46 +149441,67 @@ SQLITE_PRIVATE int sqlite3Select(
sqlite3VdbeEndCoroutine(v, pItem->regReturn);
sqlite3VdbeJumpHere(v, addrTop-1);
sqlite3ClearTempRegCache(pParse);
- }else{
- /* Generate a subroutine that will fill an ephemeral table with
- ** the content of this subquery. pItem->addrFillSub will point
- ** to the address of the generated subroutine. pItem->regReturn
- ** is a register allocated to hold the subroutine return address
- */
+ }else if( pItem->fg.isCte && pItem->u2.pCteUse->addrM9e>0 ){
+ /* This is a CTE for which materialization code has already been
+ ** generated. Invoke the subroutine to compute the materialization,
+ ** the make the pItem->iCursor be a copy of the ephemeral table that
+ ** holds the result of the materialization. */
+ CteUse *pCteUse = pItem->u2.pCteUse;
+ sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e);
+ if( pItem->iCursor!=pCteUse->iCur ){
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pCteUse->iCur);
+ VdbeComment((v, "%!S", pItem));
+ }
+ pSub->nSelectRow = pCteUse->nRowEst;
+ }else if( (pPrior = isSelfJoinView(pTabList, pItem, 0, i))!=0 ){
+ /* This view has already been materialized by a prior entry in
+ ** this same FROM clause. Reuse it. */
+ if( pPrior->addrFillSub ){
+ sqlite3VdbeAddOp2(v, OP_Gosub, pPrior->regReturn, pPrior->addrFillSub);
+ }
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
+ pSub->nSelectRow = pPrior->pSelect->nSelectRow;
+ }else{
+ /* Materialize the view. If the view is not correlated, generate a
+ ** subroutine to do the materialization so that subsequent uses of
+ ** the same view can reuse the materialization. */
int topAddr;
int onceAddr = 0;
- int retAddr;
- struct SrcList_item *pPrior;
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrExplain;
+#endif
- assert( pItem->addrFillSub==0 );
pItem->regReturn = ++pParse->nMem;
- topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
+ topAddr = sqlite3VdbeAddOp0(v, OP_Goto);
pItem->addrFillSub = topAddr+1;
+ pItem->fg.isMaterialized = 1;
if( pItem->fg.isCorrelated==0 ){
/* If the subquery is not correlated and if we are not inside of
** a trigger, then we only need to compute the value of the subquery
** once. */
onceAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
- VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName));
- }else{
- VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
- }
- pPrior = isSelfJoinView(pTabList, pItem);
- if( pPrior ){
- sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
- assert( pPrior->pSelect!=0 );
- pSub->nSelectRow = pPrior->pSelect->nSelectRow;
+ VdbeComment((v, "materialize %!S", pItem));
}else{
- sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
- ExplainQueryPlan((pParse, 1, "MATERIALIZE %u", pSub->selId));
- sqlite3Select(pParse, pSub, &dest);
+ VdbeNoopComment((v, "materialize %!S", pItem));
}
+ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
+
+ ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem));
+ sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = pSub->nSelectRow;
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
- retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
- VdbeComment((v, "end %s", pItem->pTab->zName));
- sqlite3VdbeChangeP1(v, topAddr, retAddr);
+ sqlite3VdbeAddOp2(v, OP_Return, pItem->regReturn, topAddr+1);
+ VdbeComment((v, "end %!S", pItem));
+ sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1);
+ sqlite3VdbeJumpHere(v, topAddr);
sqlite3ClearTempRegCache(pParse);
+ if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){
+ CteUse *pCteUse = pItem->u2.pCteUse;
+ pCteUse->addrM9e = pItem->addrFillSub;
+ pCteUse->regRtn = pItem->regReturn;
+ pCteUse->iCur = pItem->iCursor;
+ pCteUse->nRowEst = pSub->nSelectRow;
+ }
}
if( db->mallocFailed ) goto select_end;
pParse->nHeight -= sqlite3SelectExprHeight(p);
@@ -129868,14 +149517,14 @@ SQLITE_PRIVATE int sqlite3Select(
pHaving = p->pHaving;
sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0;
-#if SELECTTRACE_ENABLED
- if( sqlite3SelectTrace & 0x400 ){
- SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n"));
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x8000 ){
+ TREETRACE(0x8000,pParse,p,("After all FROM-clause analysis:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
- /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
+ /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
** if the select-list is the same as the ORDER BY list, then this query
** can be rewritten as a GROUP BY. In other words, this:
**
@@ -129885,24 +149534,29 @@ SQLITE_PRIVATE int sqlite3Select(
**
** SELECT xyz FROM ... GROUP BY xyz ORDER BY xyz
**
- ** The second form is preferred as a single index (or temp-table) may be
- ** used for both the ORDER BY and DISTINCT processing. As originally
- ** written the query must use a temp-table for at least one of the ORDER
+ ** The second form is preferred as a single index (or temp-table) may be
+ ** used for both the ORDER BY and DISTINCT processing. As originally
+ ** written the query must use a temp-table for at least one of the ORDER
** BY and DISTINCT, and an index or separate temp-table for the other.
*/
- if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
+ if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
&& sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ && p->pWin==0
+#endif
){
p->selFlags &= ~SF_Distinct;
pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);
+ p->selFlags |= SF_Aggregate;
/* Notice that even thought SF_Distinct has been cleared from p->selFlags,
** the sDistinct.isTnct is still set. Hence, isTnct represents the
** original setting of the SF_Distinct flag, not the current setting */
assert( sDistinct.isTnct );
+ sDistinct.isTnct = 2;
-#if SELECTTRACE_ENABLED
- if( sqlite3SelectTrace & 0x400 ){
- SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x20000 ){
+ TREETRACE(0x20000,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -129934,15 +149588,27 @@ SQLITE_PRIVATE int sqlite3Select(
*/
if( pDest->eDest==SRT_EphemTab ){
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr);
+ if( p->selFlags & SF_NestedFrom ){
+ /* Delete or NULL-out result columns that will never be used */
+ int ii;
+ for(ii=pEList->nExpr-1; ii>0 && pEList->a[ii].fg.bUsed==0; ii--){
+ sqlite3ExprDelete(db, pEList->a[ii].pExpr);
+ sqlite3DbFree(db, pEList->a[ii].zEName);
+ pEList->nExpr--;
+ }
+ for(ii=0; ii<pEList->nExpr; ii++){
+ if( pEList->a[ii].fg.bUsed==0 ) pEList->a[ii].pExpr->op = TK_NULL;
+ }
+ }
}
/* Set the limiter.
*/
- iEnd = sqlite3VdbeMakeLabel(v);
+ iEnd = sqlite3VdbeMakeLabel(pParse);
if( (p->selFlags & SF_FixedLimit)==0 ){
p->nSelectRow = 320; /* 4 billion rows */
}
- computeLimitRegisters(pParse, p, iEnd);
+ if( p->pLimit ) computeLimitRegisters(pParse, p, iEnd);
if( p->iLimit==0 && sSort.addrSortIndex>=0 ){
sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen);
sSort.sortFlags |= SORTFLAG_UseSorter;
@@ -129967,18 +149633,18 @@ SQLITE_PRIVATE int sqlite3Select(
u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0)
| (p->selFlags & SF_FixedLimit);
#ifndef SQLITE_OMIT_WINDOWFUNC
- Window *pWin = p->pWin; /* Master window object (or NULL) */
+ Window *pWin = p->pWin; /* Main window object (or NULL) */
if( pWin ){
- sqlite3WindowCodeInit(pParse, pWin);
+ sqlite3WindowCodeInit(pParse, p);
}
#endif
assert( WHERE_USE_LIMIT==SF_FixedLimit );
/* Begin the database scan. */
- SELECTTRACE(1,pParse,p,("WhereBegin\n"));
+ TREETRACE(0x2,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
- p->pEList, wctrlFlags, p->nSelectRow);
+ p->pEList, p, wctrlFlags, p->nSelectRow);
if( pWInfo==0 ) goto select_end;
if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){
p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo);
@@ -129993,8 +149659,9 @@ SQLITE_PRIVATE int sqlite3Select(
sSort.pOrderBy = 0;
}
}
+ TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
- /* If sorting index that was created by a prior OP_OpenEphemeral
+ /* If sorting index that was created by a prior OP_OpenEphemeral
** instruction ended up not being needed, then change the OP_OpenEphemeral
** into an OP_Noop.
*/
@@ -130005,9 +149672,9 @@ SQLITE_PRIVATE int sqlite3Select(
assert( p->pEList==pEList );
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pWin ){
- int addrGosub = sqlite3VdbeMakeLabel(v);
- int iCont = sqlite3VdbeMakeLabel(v);
- int iBreak = sqlite3VdbeMakeLabel(v);
+ int addrGosub = sqlite3VdbeMakeLabel(pParse);
+ int iCont = sqlite3VdbeMakeLabel(pParse);
+ int iBreak = sqlite3VdbeMakeLabel(pParse);
int regGosub = ++pParse->nMem;
sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub);
@@ -130031,6 +149698,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* End the database scan loop.
*/
+ TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
}
}else{
@@ -130064,38 +149732,62 @@ SQLITE_PRIVATE int sqlite3Select(
}
assert( 66==sqlite3LogEst(100) );
if( p->nSelectRow>66 ) p->nSelectRow = 66;
+
+ /* If there is both a GROUP BY and an ORDER BY clause and they are
+ ** identical, then it may be possible to disable the ORDER BY clause
+ ** on the grounds that the GROUP BY will cause elements to come out
+ ** in the correct order. It also may not - the GROUP BY might use a
+ ** database index that causes rows to be grouped together as required
+ ** but not actually sorted. Either way, record the fact that the
+ ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
+ ** variable. */
+ if( sSort.pOrderBy && pGroupBy->nExpr==sSort.pOrderBy->nExpr ){
+ int ii;
+ /* The GROUP BY processing doesn't care whether rows are delivered in
+ ** ASC or DESC order - only that each group is returned contiguously.
+ ** So set the ASC/DESC flags in the GROUP BY to match those in the
+ ** ORDER BY to maximize the chances of rows being delivered in an
+ ** order that makes the ORDER BY redundant. */
+ for(ii=0; ii<pGroupBy->nExpr; ii++){
+ u8 sortFlags;
+ sortFlags = sSort.pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC;
+ pGroupBy->a[ii].fg.sortFlags = sortFlags;
+ }
+ if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){
+ orderByGrp = 1;
+ }
+ }
}else{
assert( 0==sqlite3LogEst(1) );
p->nSelectRow = 0;
}
- /* If there is both a GROUP BY and an ORDER BY clause and they are
- ** identical, then it may be possible to disable the ORDER BY clause
- ** on the grounds that the GROUP BY will cause elements to come out
- ** in the correct order. It also may not - the GROUP BY might use a
- ** database index that causes rows to be grouped together as required
- ** but not actually sorted. Either way, record the fact that the
- ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
- ** variable. */
- if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){
- orderByGrp = 1;
- }
-
/* Create a label to jump to when we want to abort the query */
- addrEnd = sqlite3VdbeMakeLabel(v);
+ addrEnd = sqlite3VdbeMakeLabel(pParse);
/* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in
** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the
** SELECT statement.
*/
+ pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
+ if( pAggInfo ){
+ sqlite3ParserAddCleanup(pParse, agginfoFree, pAggInfo);
+ testcase( pParse->earlyCleanup );
+ }
+ if( db->mallocFailed ){
+ goto select_end;
+ }
+ pAggInfo->selId = p->selId;
+#ifdef SQLITE_DEBUG
+ pAggInfo->pSelect = p;
+#endif
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
sNC.pSrcList = pTabList;
- sNC.uNC.pAggInfo = &sAggInfo;
+ sNC.uNC.pAggInfo = pAggInfo;
VVA_ONLY( sNC.ncFlags = NC_UAggInfo; )
- sAggInfo.mnReg = pParse->nMem+1;
- sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0;
- sAggInfo.pGroupBy = pGroupBy;
+ pAggInfo->nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0;
+ pAggInfo->pGroupBy = pGroupBy;
sqlite3ExprAnalyzeAggList(&sNC, pEList);
sqlite3ExprAnalyzeAggList(&sNC, sSort.pOrderBy);
if( pHaving ){
@@ -130108,35 +149800,23 @@ SQLITE_PRIVATE int sqlite3Select(
}
sqlite3ExprAnalyzeAggregates(&sNC, pHaving);
}
- sAggInfo.nAccumulator = sAggInfo.nColumn;
- if( p->pGroupBy==0 && p->pHaving==0 && sAggInfo.nFunc==1 ){
- minMaxFlag = minMaxQuery(db, sAggInfo.aFunc[0].pExpr, &pMinMaxOrderBy);
+ pAggInfo->nAccumulator = pAggInfo->nColumn;
+ if( p->pGroupBy==0 && p->pHaving==0 && pAggInfo->nFunc==1 ){
+ minMaxFlag = minMaxQuery(db, pAggInfo->aFunc[0].pFExpr, &pMinMaxOrderBy);
}else{
minMaxFlag = WHERE_ORDERBY_NORMAL;
}
- for(i=0; i<sAggInfo.nFunc; i++){
- assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) );
- sNC.ncFlags |= NC_InAggFunc;
- sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList);
- sNC.ncFlags &= ~NC_InAggFunc;
- }
- sAggInfo.mxReg = pParse->nMem;
+ analyzeAggFuncArgs(pAggInfo, &sNC);
if( db->mallocFailed ) goto select_end;
-#if SELECTTRACE_ENABLED
- if( sqlite3SelectTrace & 0x400 ){
- int ii;
- SELECTTRACE(0x400,pParse,p,("After aggregate analysis:\n"));
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x20 ){
+ TREETRACE(0x20,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
sqlite3TreeViewSelect(0, p, 0);
- for(ii=0; ii<sAggInfo.nColumn; ii++){
- sqlite3DebugPrintf("agg-column[%d] iMem=%d\n",
- ii, sAggInfo.aCol[ii].iMem);
- sqlite3TreeViewExpr(0, sAggInfo.aCol[ii].pExpr, 0);
- }
- for(ii=0; ii<sAggInfo.nFunc; ii++){
- sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n",
- ii, sAggInfo.aFunc[ii].iMem);
- sqlite3TreeViewExpr(0, sAggInfo.aFunc[ii].pExpr, 0);
+ if( minMaxFlag ){
+ sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag);
+ sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY");
}
+ printAggInfo(pAggInfo);
}
#endif
@@ -130146,7 +149826,7 @@ SQLITE_PRIVATE int sqlite3Select(
*/
if( pGroupBy ){
KeyInfo *pKeyInfo; /* Keying information for the group by clause */
- int addr1; /* A-vs-B comparision jump */
+ int addr1; /* A-vs-B comparison jump */
int addrOutputRow; /* Start of subroutine that outputs a result row */
int regOutputRow; /* Return address register for output subroutine */
int addrSetAbort; /* Set the abort flag and return */
@@ -130154,16 +149834,33 @@ SQLITE_PRIVATE int sqlite3Select(
int addrSortingIdx; /* The OP_OpenEphemeral for the sorting index */
int addrReset; /* Subroutine for resetting the accumulator */
int regReset; /* Return address register for reset subroutine */
+ ExprList *pDistinct = 0;
+ u16 distFlag = 0;
+ int eDist = WHERE_DISTINCT_NOOP;
+
+ if( pAggInfo->nFunc==1
+ && pAggInfo->aFunc[0].iDistinct>=0
+ && ALWAYS(pAggInfo->aFunc[0].pFExpr!=0)
+ && ALWAYS(ExprUseXList(pAggInfo->aFunc[0].pFExpr))
+ && pAggInfo->aFunc[0].pFExpr->x.pList!=0
+ ){
+ Expr *pExpr = pAggInfo->aFunc[0].pFExpr->x.pList->a[0].pExpr;
+ pExpr = sqlite3ExprDup(db, pExpr, 0);
+ pDistinct = sqlite3ExprListDup(db, pGroupBy, 0);
+ pDistinct = sqlite3ExprListAppend(pParse, pDistinct, pExpr);
+ distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0;
+ }
/* If there is a GROUP BY clause we might need a sorting index to
** implement it. Allocate that sorting index now. If it turns out
** that we do not need it after all, the OP_SorterOpen instruction
- ** will be converted into a Noop.
+ ** will be converted into a Noop.
*/
- sAggInfo.sortingIdx = pParse->nTab++;
- pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pGroupBy,0,sAggInfo.nColumn);
- addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
- sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
+ pAggInfo->sortingIdx = pParse->nTab++;
+ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pGroupBy,
+ 0, pAggInfo->nColumn);
+ addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
+ pAggInfo->sortingIdx, pAggInfo->nSortingColumn,
0, (char*)pKeyInfo, P4_KEYINFO);
/* Initialize memory locations used by GROUP BY aggregate processing
@@ -130171,9 +149868,9 @@ SQLITE_PRIVATE int sqlite3Select(
iUseFlag = ++pParse->nMem;
iAbortFlag = ++pParse->nMem;
regOutputRow = ++pParse->nMem;
- addrOutputRow = sqlite3VdbeMakeLabel(v);
+ addrOutputRow = sqlite3VdbeMakeLabel(pParse);
regReset = ++pParse->nMem;
- addrReset = sqlite3VdbeMakeLabel(v);
+ addrReset = sqlite3VdbeMakeLabel(pParse);
iAMem = pParse->nMem + 1;
pParse->nMem += pGroupBy->nExpr;
iBMem = pParse->nMem + 1;
@@ -130188,11 +149885,21 @@ SQLITE_PRIVATE int sqlite3Select(
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
- SELECTTRACE(1,pParse,p,("WhereBegin\n"));
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0,
- WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0
+ TREETRACE(0x2,pParse,p,("WhereBegin\n"));
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, pDistinct,
+ p, (sDistinct.isTnct==2 ? WHERE_DISTINCTBY : WHERE_GROUPBY)
+ | (orderByGrp ? WHERE_SORTBYGROUP : 0) | distFlag, 0
);
- if( pWInfo==0 ) goto select_end;
+ if( pWInfo==0 ){
+ sqlite3ExprListDelete(db, pDistinct);
+ goto select_end;
+ }
+ if( pParse->pIdxEpr ){
+ optimizeAggregateUseOfIndexedExpr(pParse, p, pAggInfo, &sNC);
+ }
+ assignAggregateRegisters(pParse, pAggInfo);
+ eDist = sqlite3WhereIsDistinct(pWInfo);
+ TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){
/* The optimizer is able to deliver rows in group by order so
** we do not have to sort. The OP_OpenEphemeral table will be
@@ -130210,16 +149917,20 @@ SQLITE_PRIVATE int sqlite3Select(
int nCol;
int nGroupBy;
- explainTempTable(pParse,
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrExp; /* Address of OP_Explain instruction */
+#endif
+ ExplainQueryPlan2(addrExp, (pParse, 0, "USE TEMP B-TREE FOR %s",
(sDistinct.isTnct && (p->selFlags&SF_Distinct)==0) ?
- "DISTINCT" : "GROUP BY");
+ "DISTINCT" : "GROUP BY"
+ ));
groupBySort = 1;
nGroupBy = pGroupBy->nExpr;
nCol = nGroupBy;
j = nGroupBy;
- for(i=0; i<sAggInfo.nColumn; i++){
- if( sAggInfo.aCol[i].iSorterColumn>=j ){
+ for(i=0; i<pAggInfo->nColumn; i++){
+ if( pAggInfo->aCol[i].iSorterColumn>=j ){
nCol++;
j++;
}
@@ -130227,27 +149938,50 @@ SQLITE_PRIVATE int sqlite3Select(
regBase = sqlite3GetTempRange(pParse, nCol);
sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0);
j = nGroupBy;
- for(i=0; i<sAggInfo.nColumn; i++){
- struct AggInfo_col *pCol = &sAggInfo.aCol[i];
+ pAggInfo->directMode = 1;
+ for(i=0; i<pAggInfo->nColumn; i++){
+ struct AggInfo_col *pCol = &pAggInfo->aCol[i];
if( pCol->iSorterColumn>=j ){
- int r1 = j + regBase;
- sqlite3ExprCodeGetColumnOfTable(v,
- pCol->pTab, pCol->iTable, pCol->iColumn, r1);
+ sqlite3ExprCode(pParse, pCol->pCExpr, j + regBase);
j++;
}
}
+ pAggInfo->directMode = 0;
regRecord = sqlite3GetTempReg(pParse);
+ sqlite3VdbeScanStatusCounters(v, addrExp, 0, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord);
- sqlite3VdbeAddOp2(v, OP_SorterInsert, sAggInfo.sortingIdx, regRecord);
+ sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord);
+ sqlite3VdbeScanStatusRange(v, addrExp, sqlite3VdbeCurrentAddr(v)-2, -1);
sqlite3ReleaseTempReg(pParse, regRecord);
sqlite3ReleaseTempRange(pParse, regBase, nCol);
+ TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
- sAggInfo.sortingIdxPTab = sortPTab = pParse->nTab++;
+ pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++;
sortOut = sqlite3GetTempReg(pParse);
+ sqlite3VdbeScanStatusCounters(v, addrExp, sqlite3VdbeCurrentAddr(v), 0);
sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol);
- sqlite3VdbeAddOp2(v, OP_SorterSort, sAggInfo.sortingIdx, addrEnd);
+ sqlite3VdbeAddOp2(v, OP_SorterSort, pAggInfo->sortingIdx, addrEnd);
VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v);
- sAggInfo.useSortingIdx = 1;
+ pAggInfo->useSortingIdx = 1;
+ sqlite3VdbeScanStatusRange(v, addrExp, -1, sortPTab);
+ sqlite3VdbeScanStatusRange(v, addrExp, -1, pAggInfo->sortingIdx);
+ }
+
+ /* If there are entries in pAgggInfo->aFunc[] that contain subexpressions
+ ** that are indexed (and that were previously identified and tagged
+ ** in optimizeAggregateUseOfIndexedExpr()) then those subexpressions
+ ** must now be converted into a TK_AGG_COLUMN node so that the value
+ ** is correctly pulled from the index rather than being recomputed. */
+ if( pParse->pIdxEpr ){
+ aggregateConvertIndexedExprRefToColumn(pAggInfo);
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x20 ){
+ TREETRACE(0x20, pParse, p,
+ ("AggInfo function expressions converted to reference index\n"));
+ sqlite3TreeViewSelect(0, p, 0);
+ printAggInfo(pAggInfo);
+ }
+#endif
}
/* If the index or temporary table used by the GROUP BY sort
@@ -130255,9 +149989,9 @@ SQLITE_PRIVATE int sqlite3Select(
** clause, cancel the ephemeral table open coded earlier.
**
** This is an optimization - the correct answer should result regardless.
- ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER to
+ ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER to
** disable this optimization for testing purposes. */
- if( orderByGrp && OptimizationEnabled(db, SQLITE_GroupByOrder)
+ if( orderByGrp && OptimizationEnabled(db, SQLITE_GroupByOrder)
&& (groupBySort || sqlite3WhereIsSorted(pWInfo))
){
sSort.pOrderBy = 0;
@@ -130271,14 +150005,14 @@ SQLITE_PRIVATE int sqlite3Select(
*/
addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
if( groupBySort ){
- sqlite3VdbeAddOp3(v, OP_SorterData, sAggInfo.sortingIdx,
+ sqlite3VdbeAddOp3(v, OP_SorterData, pAggInfo->sortingIdx,
sortOut, sortPTab);
}
for(j=0; j<pGroupBy->nExpr; j++){
if( groupBySort ){
sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j);
}else{
- sAggInfo.directMode = 1;
+ pAggInfo->directMode = 1;
sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j);
}
}
@@ -130308,19 +150042,21 @@ SQLITE_PRIVATE int sqlite3Select(
** the current row
*/
sqlite3VdbeJumpHere(v, addr1);
- updateAccumulator(pParse, iUseFlag, &sAggInfo);
+ updateAccumulator(pParse, iUseFlag, pAggInfo, eDist);
sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag);
VdbeComment((v, "indicate data in accumulator"));
/* End of the loop
*/
if( groupBySort ){
- sqlite3VdbeAddOp2(v, OP_SorterNext, sAggInfo.sortingIdx, addrTopOfLoop);
+ sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop);
VdbeCoverage(v);
}else{
+ TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
sqlite3VdbeChangeToNoop(v, addrSortingIdx);
}
+ sqlite3ExprListDelete(db, pDistinct);
/* Output the final row of result
*/
@@ -130348,7 +150084,7 @@ SQLITE_PRIVATE int sqlite3Select(
VdbeCoverage(v);
VdbeComment((v, "Groupby result generator entry point"));
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
- finalizeAggFunctions(pParse, &sAggInfo);
+ finalizeAggFunctions(pParse, pAggInfo);
sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL);
selectInnerLoop(pParse, p, -1, &sSort,
&sDistinct, pDest,
@@ -130359,16 +150095,19 @@ SQLITE_PRIVATE int sqlite3Select(
/* Generate a subroutine that will reset the group-by accumulator
*/
sqlite3VdbeResolveLabel(v, addrReset);
- resetAccumulator(pParse, &sAggInfo);
+ resetAccumulator(pParse, pAggInfo);
sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
VdbeComment((v, "indicate accumulator empty"));
sqlite3VdbeAddOp1(v, OP_Return, regReset);
-
+
+ if( distFlag!=0 && eDist!=WHERE_DISTINCT_NOOP ){
+ struct AggInfo_func *pF = &pAggInfo->aFunc[0];
+ fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr);
+ }
} /* endif pGroupBy. Begin aggregate queries without GROUP BY: */
else {
-#ifndef SQLITE_OMIT_BTREECOUNT
Table *pTab;
- if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){
+ if( (pTab = isSimpleCount(p, pAggInfo))!=0 ){
/* If isSimpleCount() returns a pointer to a Table structure, then
** the SQL statement is of the form:
**
@@ -130387,7 +150126,7 @@ SQLITE_PRIVATE int sqlite3Select(
Index *pIdx; /* Iterator variable */
KeyInfo *pKeyInfo = 0; /* Keyinfo for scanned index */
Index *pBest = 0; /* Best index found so far */
- int iRoot = pTab->tnum; /* Root page of scanned b-tree */
+ Pgno iRoot = pTab->tnum; /* Root page of scanned b-tree */
sqlite3CodeVerifySchema(pParse, iDb);
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
@@ -130398,17 +150137,19 @@ SQLITE_PRIVATE int sqlite3Select(
**
** (2013-10-03) Do not count the entries in a partial index.
**
- ** In practice the KeyInfo structure will not be used. It is only
+ ** In practice the KeyInfo structure will not be used. It is only
** passed to keep OP_OpenRead happy.
*/
if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab);
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->bUnordered==0
- && pIdx->szIdxRow<pTab->szTabRow
- && pIdx->pPartIdxWhere==0
- && (!pBest || pIdx->szIdxRow<pBest->szIdxRow)
- ){
- pBest = pIdx;
+ if( !p->pSrc->a[0].fg.notIndexed ){
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->bUnordered==0
+ && pIdx->szIdxRow<pTab->szTabRow
+ && pIdx->pPartIdxWhere==0
+ && (!pBest || pIdx->szIdxRow<pBest->szIdxRow)
+ ){
+ pBest = pIdx;
+ }
}
}
if( pBest ){
@@ -130417,39 +150158,55 @@ SQLITE_PRIVATE int sqlite3Select(
}
/* Open a read-only cursor, execute the OP_Count, close the cursor. */
- sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, iRoot, iDb, 1);
+ sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, (int)iRoot, iDb, 1);
if( pKeyInfo ){
sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
}
- sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem);
+ assignAggregateRegisters(pParse, pAggInfo);
+ sqlite3VdbeAddOp2(v, OP_Count, iCsr, AggInfoFuncReg(pAggInfo,0));
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
explainSimpleCount(pParse, pTab, pBest);
- }else
-#endif /* SQLITE_OMIT_BTREECOUNT */
- {
+ }else{
int regAcc = 0; /* "populate accumulators" flag */
-
- /* If there are accumulator registers but no min() or max() functions,
- ** allocate register regAcc. Register regAcc will contain 0 the first
- ** time the inner loop runs, and 1 thereafter. The code generated
- ** by updateAccumulator() only updates the accumulator registers if
- ** regAcc contains 0. */
- if( sAggInfo.nAccumulator ){
- for(i=0; i<sAggInfo.nFunc; i++){
- if( sAggInfo.aFunc[i].pFunc->funcFlags&SQLITE_FUNC_NEEDCOLL ) break;
+ ExprList *pDistinct = 0;
+ u16 distFlag = 0;
+ int eDist;
+
+ /* If there are accumulator registers but no min() or max() functions
+ ** without FILTER clauses, allocate register regAcc. Register regAcc
+ ** will contain 0 the first time the inner loop runs, and 1 thereafter.
+ ** The code generated by updateAccumulator() uses this to ensure
+ ** that the accumulator registers are (a) updated only once if
+ ** there are no min() or max functions or (b) always updated for the
+ ** first row visited by the aggregate, so that they are updated at
+ ** least once even if the FILTER clause means the min() or max()
+ ** function visits zero rows. */
+ if( pAggInfo->nAccumulator ){
+ for(i=0; i<pAggInfo->nFunc; i++){
+ if( ExprHasProperty(pAggInfo->aFunc[i].pFExpr, EP_WinFunc) ){
+ continue;
+ }
+ if( pAggInfo->aFunc[i].pFunc->funcFlags&SQLITE_FUNC_NEEDCOLL ){
+ break;
+ }
}
- if( i==sAggInfo.nFunc ){
+ if( i==pAggInfo->nFunc ){
regAcc = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regAcc);
}
+ }else if( pAggInfo->nFunc==1 && pAggInfo->aFunc[0].iDistinct>=0 ){
+ assert( ExprUseXList(pAggInfo->aFunc[0].pFExpr) );
+ pDistinct = pAggInfo->aFunc[0].pFExpr->x.pList;
+ distFlag = pDistinct ? (WHERE_WANT_DISTINCT|WHERE_AGG_DISTINCT) : 0;
}
+ assignAggregateRegisters(pParse, pAggInfo);
/* This case runs if the aggregate has no GROUP BY clause. The
** processing is much simpler since there is only a single row
** of output.
*/
assert( p->pGroupBy==0 );
- resetAccumulator(pParse, &sAggInfo);
+ resetAccumulator(pParse, pAggInfo);
/* If this query is a candidate for the min/max optimization, then
** minMaxFlag will have been previously set to either
@@ -130459,30 +150216,38 @@ SQLITE_PRIVATE int sqlite3Select(
assert( minMaxFlag==WHERE_ORDERBY_NORMAL || pMinMaxOrderBy!=0 );
assert( pMinMaxOrderBy==0 || pMinMaxOrderBy->nExpr==1 );
- SELECTTRACE(1,pParse,p,("WhereBegin\n"));
+ TREETRACE(0x2,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy,
- 0, minMaxFlag, 0);
+ pDistinct, p, minMaxFlag|distFlag, 0);
if( pWInfo==0 ){
goto select_end;
}
- updateAccumulator(pParse, regAcc, &sAggInfo);
+ TREETRACE(0x2,pParse,p,("WhereBegin returns\n"));
+ eDist = sqlite3WhereIsDistinct(pWInfo);
+ updateAccumulator(pParse, regAcc, pAggInfo, eDist);
+ if( eDist!=WHERE_DISTINCT_NOOP ){
+ struct AggInfo_func *pF = pAggInfo->aFunc;
+ if( pF ){
+ fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr);
+ }
+ }
+
if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc);
- if( sqlite3WhereIsOrdered(pWInfo)>0 ){
- sqlite3VdbeGoto(v, sqlite3WhereBreakLabel(pWInfo));
- VdbeComment((v, "%s() by index",
- (minMaxFlag==WHERE_ORDERBY_MIN?"min":"max")));
+ if( minMaxFlag ){
+ sqlite3WhereMinMaxOptEarlyOut(v, pWInfo);
}
+ TREETRACE(0x2,pParse,p,("WhereEnd\n"));
sqlite3WhereEnd(pWInfo);
- finalizeAggFunctions(pParse, &sAggInfo);
+ finalizeAggFunctions(pParse, pAggInfo);
}
sSort.pOrderBy = 0;
sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);
- selectInnerLoop(pParse, p, -1, 0, 0,
+ selectInnerLoop(pParse, p, -1, 0, 0,
pDest, addrEnd, addrEnd);
}
sqlite3VdbeResolveLabel(v, addrEnd);
-
+
} /* endif aggregate query */
if( sDistinct.eTnctType==WHERE_DISTINCT_UNORDERED ){
@@ -130493,8 +150258,6 @@ SQLITE_PRIVATE int sqlite3Select(
** and send them to the callback one by one.
*/
if( sSort.pOrderBy ){
- explainTempTable(pParse,
- sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
assert( p->pEList==pEList );
generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
}
@@ -130511,12 +150274,29 @@ SQLITE_PRIVATE int sqlite3Select(
** successful coding of the SELECT.
*/
select_end:
+ assert( db->mallocFailed==0 || db->mallocFailed==1 );
+ assert( db->mallocFailed==0 || pParse->nErr!=0 );
sqlite3ExprListDelete(db, pMinMaxOrderBy);
- sqlite3DbFree(db, sAggInfo.aCol);
- sqlite3DbFree(db, sAggInfo.aFunc);
-#if SELECTTRACE_ENABLED
- SELECTTRACE(0x1,pParse,p,("end processing\n"));
- if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
+#ifdef SQLITE_DEBUG
+ if( pAggInfo && !db->mallocFailed ){
+ for(i=0; i<pAggInfo->nColumn; i++){
+ Expr *pExpr = pAggInfo->aCol[i].pCExpr;
+ if( pExpr==0 ) continue;
+ assert( pExpr->pAggInfo==pAggInfo );
+ assert( pExpr->iAgg==i );
+ }
+ for(i=0; i<pAggInfo->nFunc; i++){
+ Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
+ assert( pExpr!=0 );
+ assert( pExpr->pAggInfo==pAggInfo );
+ assert( pExpr->iAgg==i );
+ }
+ }
+#endif
+
+#if TREETRACE_ENABLED
+ TREETRACE(0x1,pParse,p,("end processing\n"));
+ if( (sqlite3TreeTrace & 0x40000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
sqlite3TreeViewSelect(0, p, 0);
}
#endif
@@ -130584,7 +150364,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
if( p->nData + need > p->nAlloc ){
char **azNew;
p->nAlloc = p->nAlloc*2 + need;
- azNew = sqlite3_realloc64( p->azResult, sizeof(char*)*p->nAlloc );
+ azNew = sqlite3Realloc( p->azResult, sizeof(char*)*p->nAlloc );
if( azNew==0 ) goto malloc_failed;
p->azResult = azNew;
}
@@ -130637,7 +150417,7 @@ malloc_failed:
** at the conclusion of the call.
**
** The result that is written to ***pazResult is held in memory obtained
-** from malloc(). But the caller cannot free this memory directly.
+** from malloc(). But the caller cannot free this memory directly.
** Instead, the entire table should be passed to sqlite3_free_table() when
** the calling procedure is finished using it.
*/
@@ -130693,7 +150473,7 @@ SQLITE_API int sqlite3_get_table(
}
if( res.nAlloc>res.nData ){
char **azNew;
- azNew = sqlite3_realloc64( res.azResult, sizeof(char*)*res.nData );
+ azNew = sqlite3Realloc( res.azResult, sizeof(char*)*res.nData );
if( azNew==0 ){
sqlite3_free_table(&res.azResult[1]);
db->errCode = SQLITE_NOMEM;
@@ -130755,6 +150535,7 @@ SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerS
sqlite3SelectDelete(db, pTmp->pSelect);
sqlite3IdListDelete(db, pTmp->pIdList);
sqlite3UpsertDelete(db, pTmp->pUpsert);
+ sqlite3SrcListDelete(db, pTmp->pFrom);
sqlite3DbFree(db, pTmp->zSpan);
sqlite3DbFree(db, pTmp);
@@ -130762,7 +150543,7 @@ SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerS
}
/*
-** Given table pTab, return a list of all the triggers attached to
+** Given table pTab, return a list of all the triggers attached to
** the table. The list is connected by Trigger.pNext pointers.
**
** All of the triggers on pTab that are in the same database as pTab
@@ -130776,28 +150557,48 @@ SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerS
** pTab as well as the triggers lised in pTab->pTrigger.
*/
SQLITE_PRIVATE Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
- Schema * const pTmpSchema = pParse->db->aDb[1].pSchema;
- Trigger *pList = 0; /* List of triggers to return */
-
- if( pParse->disableTriggers ){
- return 0;
+ Schema *pTmpSchema; /* Schema of the pTab table */
+ Trigger *pList; /* List of triggers to return */
+ HashElem *p; /* Loop variable for TEMP triggers */
+
+ assert( pParse->disableTriggers==0 );
+ pTmpSchema = pParse->db->aDb[1].pSchema;
+ p = sqliteHashFirst(&pTmpSchema->trigHash);
+ pList = pTab->pTrigger;
+ while( p ){
+ Trigger *pTrig = (Trigger *)sqliteHashData(p);
+ if( pTrig->pTabSchema==pTab->pSchema
+ && pTrig->table
+ && 0==sqlite3StrICmp(pTrig->table, pTab->zName)
+ && (pTrig->pTabSchema!=pTmpSchema || pTrig->bReturning)
+ ){
+ pTrig->pNext = pList;
+ pList = pTrig;
+ }else if( pTrig->op==TK_RETURNING ){
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ assert( pParse->db->pVtabCtx==0 );
+#endif
+ assert( pParse->bReturning );
+ assert( &(pParse->u1.pReturning->retTrig) == pTrig );
+ pTrig->table = pTab->zName;
+ pTrig->pTabSchema = pTab->pSchema;
+ pTrig->pNext = pList;
+ pList = pTrig;
+ }
+ p = sqliteHashNext(p);
}
-
- if( pTmpSchema!=pTab->pSchema ){
- HashElem *p;
- assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) );
- for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){
- Trigger *pTrig = (Trigger *)sqliteHashData(p);
- if( pTrig->pTabSchema==pTab->pSchema
- && 0==sqlite3StrICmp(pTrig->table, pTab->zName)
- ){
- pTrig->pNext = (pList ? pList : pTab->pTrigger);
- pList = pTrig;
- }
+#if 0
+ if( pList ){
+ Trigger *pX;
+ printf("Triggers for %s:", pTab->zName);
+ for(pX=pList; pX; pX=pX->pNext){
+ printf(" %s", pX->zName);
}
+ printf("\n");
+ fflush(stdout);
}
-
- return (pList ? pList : pTab->pTrigger);
+#endif
+ return pList;
}
/*
@@ -130857,7 +150658,7 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
** ^^^^^^^^
**
** To maintain backwards compatibility, ignore the database
- ** name on pTableName if we are reparsing out of SQLITE_MASTER.
+ ** name on pTableName if we are reparsing out of the schema table
*/
if( db->init.busy && iDb!=1 ){
sqlite3DbFree(db, pTableName->a[0].zDatabase);
@@ -130885,28 +150686,25 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
pTab = sqlite3SrcListLookup(pParse, pTableName);
if( !pTab ){
/* The table does not exist. */
- if( db->init.iDb==1 ){
- /* Ticket #3810.
- ** Normally, whenever a table is dropped, all associated triggers are
- ** dropped too. But if a TEMP trigger is created on a non-TEMP table
- ** and the table is dropped by a different database connection, the
- ** trigger is not visible to the database connection that does the
- ** drop so the trigger cannot be dropped. This results in an
- ** "orphaned trigger" - a trigger whose associated table is missing.
- */
- db->init.orphanTrigger = 1;
- }
- goto trigger_cleanup;
+ goto trigger_orphan_error;
}
if( IsVirtual(pTab) ){
sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables");
- goto trigger_cleanup;
+ goto trigger_orphan_error;
+ }
+ if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){
+ sqlite3ErrorMsg(pParse, "cannot create triggers on shadow tables");
+ goto trigger_orphan_error;
}
/* Check that the trigger name is not reserved and that no trigger of the
** specified name exists */
zName = sqlite3NameFromToken(db, pName);
- if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ if( zName==0 ){
+ assert( db->mallocFailed );
+ goto trigger_cleanup;
+ }
+ if( sqlite3CheckObjectName(pParse, zName, "trigger", pTab->zName) ){
goto trigger_cleanup;
}
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
@@ -130917,6 +150715,7 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
}else{
assert( !db->init.busy );
sqlite3CodeVerifySchema(pParse, iDb);
+ VVA_ONLY( pParse->ifNotExists = 1; )
}
goto trigger_cleanup;
}
@@ -130931,15 +150730,15 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
/* INSTEAD of triggers are only for views and views only support INSTEAD
** of triggers.
*/
- if( pTab->pSelect && tr_tm!=TK_INSTEAD ){
- sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S",
- (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
- goto trigger_cleanup;
+ if( IsView(pTab) && tr_tm!=TK_INSTEAD ){
+ sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S",
+ (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName->a);
+ goto trigger_orphan_error;
}
- if( !pTab->pSelect && tr_tm==TK_INSTEAD ){
+ if( !IsView(pTab) && tr_tm==TK_INSTEAD ){
sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF"
- " trigger on table: %S", pTableName, 0);
- goto trigger_cleanup;
+ " trigger on table: %S", pTableName->a);
+ goto trigger_orphan_error;
}
#ifndef SQLITE_OMIT_AUTHORIZATION
@@ -130999,6 +150798,23 @@ trigger_cleanup:
}else{
assert( pParse->pNewTrigger==pTrigger );
}
+ return;
+
+trigger_orphan_error:
+ if( db->init.iDb==1 ){
+ /* Ticket #3810.
+ ** Normally, whenever a table is dropped, all associated triggers are
+ ** dropped too. But if a TEMP trigger is created on a non-TEMP table
+ ** and the table is dropped by a different database connection, the
+ ** trigger is not visible to the database connection that does the
+ ** drop so the trigger cannot be dropped. This results in an
+ ** "orphaned trigger" - a trigger whose associated table is missing.
+ **
+ ** 2020-11-05 see also https://sqlite.org/forum/forumpost/157dc791df
+ */
+ db->init.orphanTrigger = 1;
+ }
+ goto trigger_cleanup;
}
/*
@@ -131028,8 +150844,8 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
}
sqlite3TokenInit(&nameToken, pTrig->zName);
sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken);
- if( sqlite3FixTriggerStep(&sFix, pTrig->step_list)
- || sqlite3FixExpr(&sFix, pTrig->pWhen)
+ if( sqlite3FixTriggerStep(&sFix, pTrig->step_list)
+ || sqlite3FixExpr(&sFix, pTrig->pWhen)
){
goto triggerfinish_cleanup;
}
@@ -131043,32 +150859,51 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
#endif
/* if we are not initializing,
- ** build the sqlite_master entry
+ ** build the sqlite_schema entry
*/
if( !db->init.busy ){
Vdbe *v;
char *z;
- /* Make an entry in the sqlite_master table */
+ /* If this is a new CREATE TABLE statement, and if shadow tables
+ ** are read-only, and the trigger makes a change to a shadow table,
+ ** then raise an error - do not allow the trigger to be created. */
+ if( sqlite3ReadOnlyShadowTables(db) ){
+ TriggerStep *pStep;
+ for(pStep=pTrig->step_list; pStep; pStep=pStep->pNext){
+ if( pStep->zTarget!=0
+ && sqlite3ShadowTableName(db, pStep->zTarget)
+ ){
+ sqlite3ErrorMsg(pParse,
+ "trigger \"%s\" may not write to shadow table \"%s\"",
+ pTrig->zName, pStep->zTarget);
+ goto triggerfinish_cleanup;
+ }
+ }
+ }
+
+ /* Make an entry in the sqlite_schema table */
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto triggerfinish_cleanup;
sqlite3BeginWriteOperation(pParse, 0, iDb);
z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n);
testcase( z==0 );
sqlite3NestedParse(pParse,
- "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')",
- db->aDb[iDb].zDbSName, MASTER_NAME, zName,
+ "INSERT INTO %Q." LEGACY_SCHEMA_TABLE
+ " VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')",
+ db->aDb[iDb].zDbSName, zName,
pTrig->table, z);
sqlite3DbFree(db, z);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddParseSchemaOp(v, iDb,
- sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName));
+ sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName), 0);
}
if( db->init.busy ){
Trigger *pLink = pTrig;
Hash *pHash = &db->aDb[iDb].pSchema->trigHash;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ assert( pLink!=0 );
pTrig = sqlite3HashInsert(pHash, zName, pTrig);
if( pTrig ){
sqlite3OomFault(db);
@@ -131096,14 +150931,14 @@ static char *triggerSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){
int i;
if( z ) for(i=0; z[i]; i++) if( sqlite3Isspace(z[i]) ) z[i] = ' ';
return z;
-}
+}
/*
** Turn a SELECT statement (that the pSelect parameter points to) into
** a trigger step. Return a pointer to a TriggerStep structure.
**
** The parser calls this routine when it finds a SELECT statement in
-** body of a TRIGGER.
+** body of a TRIGGER.
*/
SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(
sqlite3 *db, /* Database connection */
@@ -131139,6 +150974,7 @@ static TriggerStep *triggerStepAllocate(
sqlite3 *db = pParse->db;
TriggerStep *pTriggerStep;
+ if( pParse->nErr ) return 0;
pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1);
if( pTriggerStep ){
char *z = (char*)&pTriggerStep[1];
@@ -131187,6 +151023,9 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(
pTriggerStep->pIdList = pColumn;
pTriggerStep->pUpsert = pUpsert;
pTriggerStep->orconf = orconf;
+ if( pUpsert ){
+ sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget);
+ }
}else{
testcase( pColumn );
sqlite3IdListDelete(db, pColumn);
@@ -131206,6 +151045,7 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(
SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(
Parse *pParse, /* Parser */
Token *pTableName, /* Name of the table to be updated */
+ SrcList *pFrom, /* FROM clause for an UPDATE-FROM, or NULL */
ExprList *pEList, /* The SET clause: list of column and new values */
Expr *pWhere, /* The WHERE clause */
u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
@@ -131220,16 +151060,20 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(
if( IN_RENAME_OBJECT ){
pTriggerStep->pExprList = pEList;
pTriggerStep->pWhere = pWhere;
+ pTriggerStep->pFrom = pFrom;
pEList = 0;
pWhere = 0;
+ pFrom = 0;
}else{
pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
+ pTriggerStep->pFrom = sqlite3SrcListDup(db, pFrom, EXPRDUP_REDUCE);
}
pTriggerStep->orconf = orconf;
}
sqlite3ExprListDelete(db, pEList);
sqlite3ExprDelete(db, pWhere);
+ sqlite3SrcListDelete(db, pFrom);
return pTriggerStep;
}
@@ -131262,11 +151106,11 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(
return pTriggerStep;
}
-/*
+/*
** Recursively delete a Trigger structure
*/
SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
- if( pTrigger==0 ) return;
+ if( pTrigger==0 || pTrigger->bReturning ) return;
sqlite3DeleteTriggerStep(db, pTrigger->step_list);
sqlite3DbFree(db, pTrigger->zName);
sqlite3DbFree(db, pTrigger->table);
@@ -131276,7 +151120,7 @@ SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
}
/*
-** This function is called to drop a trigger from the database schema.
+** This function is called to drop a trigger from the database schema.
**
** This may be called directly from the parser and therefore identifies
** the trigger by name. The sqlite3DropTriggerPtr() routine does the
@@ -131301,14 +151145,14 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr)
assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) );
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
- if( zDb && sqlite3StrICmp(db->aDb[j].zDbSName, zDb) ) continue;
+ if( zDb && sqlite3DbIsNamed(db, j, zDb)==0 ) continue;
assert( sqlite3SchemaMutexHeld(db, j, 0) );
pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName);
if( pTrigger ) break;
}
if( !pTrigger ){
if( !noErr ){
- sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0);
+ sqlite3ErrorMsg(pParse, "no such trigger: %S", pName->a);
}else{
sqlite3CodeVerifyNamedSchema(pParse, zDb);
}
@@ -131331,7 +151175,7 @@ static Table *tableOfTrigger(Trigger *pTrigger){
/*
-** Drop a trigger given a pointer to that trigger.
+** Drop a trigger given a pointer to that trigger.
*/
SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
Table *pTable;
@@ -131342,10 +151186,9 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
iDb = sqlite3SchemaToIndex(pParse->db, pTrigger->pSchema);
assert( iDb>=0 && iDb<db->nDb );
pTable = tableOfTrigger(pTrigger);
- assert( pTable );
- assert( pTable->pSchema==pTrigger->pSchema || iDb==1 );
+ assert( (pTable && pTable->pSchema==pTrigger->pSchema) || iDb==1 );
#ifndef SQLITE_OMIT_AUTHORIZATION
- {
+ if( pTable ){
int code = SQLITE_DROP_TRIGGER;
const char *zDb = db->aDb[iDb].zDbSName;
const char *zTab = SCHEMA_TABLE(iDb);
@@ -131359,11 +151202,10 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
/* Generate code to destroy the database record of the trigger.
*/
- assert( pTable!=0 );
if( (v = sqlite3GetVdbe(pParse))!=0 ){
sqlite3NestedParse(pParse,
- "DELETE FROM %Q.%s WHERE name=%Q AND type='trigger'",
- db->aDb[iDb].zDbSName, MASTER_NAME, pTrigger->zName
+ "DELETE FROM %Q." LEGACY_SCHEMA_TABLE " WHERE name=%Q AND type='trigger'",
+ db->aDb[iDb].zDbSName, pTrigger->zName
);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0);
@@ -131383,9 +151225,15 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const ch
if( ALWAYS(pTrigger) ){
if( pTrigger->pSchema==pTrigger->pTabSchema ){
Table *pTab = tableOfTrigger(pTrigger);
- Trigger **pp;
- for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext));
- *pp = (*pp)->pNext;
+ if( pTab ){
+ Trigger **pp;
+ for(pp=&pTab->pTrigger; *pp; pp=&((*pp)->pNext)){
+ if( *pp==pTrigger ){
+ *pp = (*pp)->pNext;
+ break;
+ }
+ }
+ }
}
sqlite3DeleteTrigger(db, pTrigger);
db->mDbFlags |= DBFLAG_SchemaChange;
@@ -131405,18 +151253,27 @@ static int checkColumnOverlap(IdList *pIdList, ExprList *pEList){
int e;
if( pIdList==0 || NEVER(pEList==0) ) return 1;
for(e=0; e<pEList->nExpr; e++){
- if( sqlite3IdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1;
+ if( sqlite3IdListIndex(pIdList, pEList->a[e].zEName)>=0 ) return 1;
}
- return 0;
+ return 0;
+}
+
+/*
+** Return true if any TEMP triggers exist
+*/
+static int tempTriggersExist(sqlite3 *db){
+ if( NEVER(db->aDb[1].pSchema==0) ) return 0;
+ if( sqliteHashFirst(&db->aDb[1].pSchema->trigHash)==0 ) return 0;
+ return 1;
}
/*
** Return a list of all triggers on table pTab if there exists at least
-** one trigger that must be fired when an operation of type 'op' is
+** one trigger that must be fired when an operation of type 'op' is
** performed on the table, and, if that operation is an UPDATE, if at
** least one of the columns in pChanges is being modified.
*/
-SQLITE_PRIVATE Trigger *sqlite3TriggersExist(
+static SQLITE_NOINLINE Trigger *triggersReallyExist(
Parse *pParse, /* Parse context */
Table *pTab, /* The table the contains the triggers */
int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
@@ -131427,20 +151284,74 @@ SQLITE_PRIVATE Trigger *sqlite3TriggersExist(
Trigger *pList = 0;
Trigger *p;
- if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){
- pList = sqlite3TriggerList(pParse, pTab);
- }
- assert( pList==0 || IsVirtual(pTab)==0 );
- for(p=pList; p; p=p->pNext){
- if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
- mask |= p->tr_tm;
+ pList = sqlite3TriggerList(pParse, pTab);
+ assert( pList==0 || IsVirtual(pTab)==0
+ || (pList->bReturning && pList->pNext==0) );
+ if( pList!=0 ){
+ p = pList;
+ if( (pParse->db->flags & SQLITE_EnableTrigger)==0
+ && pTab->pTrigger!=0
+ ){
+ /* The SQLITE_DBCONFIG_ENABLE_TRIGGER setting is off. That means that
+ ** only TEMP triggers are allowed. Truncate the pList so that it
+ ** includes only TEMP triggers */
+ if( pList==pTab->pTrigger ){
+ pList = 0;
+ goto exit_triggers_exist;
+ }
+ while( ALWAYS(p->pNext) && p->pNext!=pTab->pTrigger ) p = p->pNext;
+ p->pNext = 0;
+ p = pList;
}
+ do{
+ if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
+ mask |= p->tr_tm;
+ }else if( p->op==TK_RETURNING ){
+ /* The first time a RETURNING trigger is seen, the "op" value tells
+ ** us what time of trigger it should be. */
+ assert( sqlite3IsToplevel(pParse) );
+ p->op = op;
+ if( IsVirtual(pTab) ){
+ if( op!=TK_INSERT ){
+ sqlite3ErrorMsg(pParse,
+ "%s RETURNING is not available on virtual tables",
+ op==TK_DELETE ? "DELETE" : "UPDATE");
+ }
+ p->tr_tm = TRIGGER_BEFORE;
+ }else{
+ p->tr_tm = TRIGGER_AFTER;
+ }
+ mask |= p->tr_tm;
+ }else if( p->bReturning && p->op==TK_INSERT && op==TK_UPDATE
+ && sqlite3IsToplevel(pParse) ){
+ /* Also fire a RETURNING trigger for an UPSERT */
+ mask |= p->tr_tm;
+ }
+ p = p->pNext;
+ }while( p );
}
+exit_triggers_exist:
if( pMask ){
*pMask = mask;
}
return (mask ? pList : 0);
}
+SQLITE_PRIVATE Trigger *sqlite3TriggersExist(
+ Parse *pParse, /* Parse context */
+ Table *pTab, /* The table the contains the triggers */
+ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
+ ExprList *pChanges, /* Columns that change in an UPDATE statement */
+ int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
+){
+ assert( pTab!=0 );
+ if( (pTab->pTrigger==0 && !tempTriggersExist(pParse->db))
+ || pParse->disableTriggers
+ ){
+ if( pMask ) *pMask = 0;
+ return 0;
+ }
+ return triggersReallyExist(pParse,pTab,op,pChanges,pMask);
+}
/*
** Convert the pStep->zTarget string into a SrcList and return a pointer
@@ -131452,37 +151363,195 @@ SQLITE_PRIVATE Trigger *sqlite3TriggersExist(
** trigger is in TEMP in which case it can refer to any other database it
** wants.
*/
-static SrcList *targetSrcList(
+SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc(
Parse *pParse, /* The parsing context */
TriggerStep *pStep /* The trigger containing the target token */
){
sqlite3 *db = pParse->db;
- int iDb; /* Index of the database to use */
- SrcList *pSrc; /* SrcList to be returned */
-
- pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
+ SrcList *pSrc; /* SrcList to be returned */
+ char *zName = sqlite3DbStrDup(db, pStep->zTarget);
+ pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
+ assert( pSrc==0 || pSrc->nSrc==1 );
+ assert( zName || pSrc==0 );
if( pSrc ){
- assert( pSrc->nSrc>0 );
- pSrc->a[pSrc->nSrc-1].zName = sqlite3DbStrDup(db, pStep->zTarget);
- iDb = sqlite3SchemaToIndex(db, pStep->pTrig->pSchema);
- if( iDb==0 || iDb>=2 ){
- const char *zDb;
- assert( iDb<db->nDb );
- zDb = db->aDb[iDb].zDbSName;
- pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, zDb);
+ Schema *pSchema = pStep->pTrig->pSchema;
+ pSrc->a[0].zName = zName;
+ if( pSchema!=db->aDb[1].pSchema ){
+ pSrc->a[0].pSchema = pSchema;
+ }
+ if( pStep->pFrom ){
+ SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0);
+ if( pDup && pDup->nSrc>1 && !IN_RENAME_OBJECT ){
+ Select *pSubquery;
+ Token as;
+ pSubquery = sqlite3SelectNew(pParse,0,pDup,0,0,0,0,SF_NestedFrom,0);
+ as.n = 0;
+ as.z = 0;
+ pDup = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
+ }
+ pSrc = sqlite3SrcListAppendList(pParse, pSrc, pDup);
}
+ }else{
+ sqlite3DbFree(db, zName);
}
return pSrc;
}
/*
-** Generate VDBE code for the statements inside the body of a single
+** Return true if the pExpr term from the RETURNING clause argument
+** list is of the form "*". Raise an error if the terms if of the
+** form "table.*".
+*/
+static int isAsteriskTerm(
+ Parse *pParse, /* Parsing context */
+ Expr *pTerm /* A term in the RETURNING clause */
+){
+ assert( pTerm!=0 );
+ if( pTerm->op==TK_ASTERISK ) return 1;
+ if( pTerm->op!=TK_DOT ) return 0;
+ assert( pTerm->pRight!=0 );
+ assert( pTerm->pLeft!=0 );
+ if( pTerm->pRight->op!=TK_ASTERISK ) return 0;
+ sqlite3ErrorMsg(pParse, "RETURNING may not use \"TABLE.*\" wildcards");
+ return 1;
+}
+
+/* The input list pList is the list of result set terms from a RETURNING
+** clause. The table that we are returning from is pTab.
+**
+** This routine makes a copy of the pList, and at the same time expands
+** any "*" wildcards to be the complete set of columns from pTab.
+*/
+static ExprList *sqlite3ExpandReturning(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList, /* The arguments to RETURNING */
+ Table *pTab /* The table being updated */
+){
+ ExprList *pNew = 0;
+ sqlite3 *db = pParse->db;
+ int i;
+
+ for(i=0; i<pList->nExpr; i++){
+ Expr *pOldExpr = pList->a[i].pExpr;
+ if( NEVER(pOldExpr==0) ) continue;
+ if( isAsteriskTerm(pParse, pOldExpr) ){
+ int jj;
+ for(jj=0; jj<pTab->nCol; jj++){
+ Expr *pNewExpr;
+ if( IsHiddenColumn(pTab->aCol+jj) ) continue;
+ pNewExpr = sqlite3Expr(db, TK_ID, pTab->aCol[jj].zCnName);
+ pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr);
+ if( !db->mallocFailed ){
+ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1];
+ pItem->zEName = sqlite3DbStrDup(db, pTab->aCol[jj].zCnName);
+ pItem->fg.eEName = ENAME_NAME;
+ }
+ }
+ }else{
+ Expr *pNewExpr = sqlite3ExprDup(db, pOldExpr, 0);
+ pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr);
+ if( !db->mallocFailed && ALWAYS(pList->a[i].zEName!=0) ){
+ struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1];
+ pItem->zEName = sqlite3DbStrDup(db, pList->a[i].zEName);
+ pItem->fg.eEName = pList->a[i].fg.eEName;
+ }
+ }
+ }
+ return pNew;
+}
+
+/*
+** Generate code for the RETURNING trigger. Unlike other triggers
+** that invoke a subprogram in the bytecode, the code for RETURNING
+** is generated in-line.
+*/
+static void codeReturningTrigger(
+ Parse *pParse, /* Parse context */
+ Trigger *pTrigger, /* The trigger step that defines the RETURNING */
+ Table *pTab, /* The table to code triggers from */
+ int regIn /* The first in an array of registers */
+){
+ Vdbe *v = pParse->pVdbe;
+ sqlite3 *db = pParse->db;
+ ExprList *pNew;
+ Returning *pReturning;
+ Select sSelect;
+ SrcList sFrom;
+
+ assert( v!=0 );
+ if( !pParse->bReturning ){
+ /* This RETURNING trigger must be for a different statement as
+ ** this statement lacks a RETURNING clause. */
+ return;
+ }
+ assert( db->pParse==pParse );
+ pReturning = pParse->u1.pReturning;
+ if( pTrigger != &(pReturning->retTrig) ){
+ /* This RETURNING trigger is for a different statement */
+ return;
+ }
+ memset(&sSelect, 0, sizeof(sSelect));
+ memset(&sFrom, 0, sizeof(sFrom));
+ sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
+ sSelect.pSrc = &sFrom;
+ sFrom.nSrc = 1;
+ sFrom.a[0].pTab = pTab;
+ sFrom.a[0].iCursor = -1;
+ sqlite3SelectPrep(pParse, &sSelect, 0);
+ if( pParse->nErr==0 ){
+ assert( db->mallocFailed==0 );
+ sqlite3GenerateColumnNames(pParse, &sSelect);
+ }
+ sqlite3ExprListDelete(db, sSelect.pEList);
+ pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab);
+ if( pParse->nErr==0 ){
+ NameContext sNC;
+ memset(&sNC, 0, sizeof(sNC));
+ if( pReturning->nRetCol==0 ){
+ pReturning->nRetCol = pNew->nExpr;
+ pReturning->iRetCur = pParse->nTab++;
+ }
+ sNC.pParse = pParse;
+ sNC.uNC.iBaseReg = regIn;
+ sNC.ncFlags = NC_UBaseReg;
+ pParse->eTriggerOp = pTrigger->op;
+ pParse->pTriggerTab = pTab;
+ if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK
+ && ALWAYS(!db->mallocFailed)
+ ){
+ int i;
+ int nCol = pNew->nExpr;
+ int reg = pParse->nMem+1;
+ pParse->nMem += nCol+2;
+ pReturning->iRetReg = reg;
+ for(i=0; i<nCol; i++){
+ Expr *pCol = pNew->a[i].pExpr;
+ assert( pCol!=0 ); /* Due to !db->mallocFailed ~9 lines above */
+ sqlite3ExprCodeFactorable(pParse, pCol, reg+i);
+ if( sqlite3ExprAffinity(pCol)==SQLITE_AFF_REAL ){
+ sqlite3VdbeAddOp1(v, OP_RealAffinity, reg+i);
+ }
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, i, reg+i);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, pReturning->iRetCur, reg+i+1);
+ sqlite3VdbeAddOp3(v, OP_Insert, pReturning->iRetCur, reg+i, reg+i+1);
+ }
+ }
+ sqlite3ExprListDelete(db, pNew);
+ pParse->eTriggerOp = 0;
+ pParse->pTriggerTab = 0;
+}
+
+
+
+/*
+** Generate VDBE code for the statements inside the body of a single
** trigger.
*/
static int codeTriggerProgram(
Parse *pParse, /* The parser context */
TriggerStep *pStepList, /* List of statements inside the trigger body */
- int orconf /* Conflict algorithm. (OE_Abort, etc) */
+ int orconf /* Conflict algorithm. (OE_Abort, etc) */
){
TriggerStep *pStep;
Vdbe *v = pParse->pVdbe;
@@ -131518,29 +151587,32 @@ static int codeTriggerProgram(
switch( pStep->op ){
case TK_UPDATE: {
- sqlite3Update(pParse,
- targetSrcList(pParse, pStep),
- sqlite3ExprListDup(db, pStep->pExprList, 0),
- sqlite3ExprDup(db, pStep->pWhere, 0),
+ sqlite3Update(pParse,
+ sqlite3TriggerStepSrc(pParse, pStep),
+ sqlite3ExprListDup(db, pStep->pExprList, 0),
+ sqlite3ExprDup(db, pStep->pWhere, 0),
pParse->eOrconf, 0, 0, 0
);
+ sqlite3VdbeAddOp0(v, OP_ResetCount);
break;
}
case TK_INSERT: {
- sqlite3Insert(pParse,
- targetSrcList(pParse, pStep),
- sqlite3SelectDup(db, pStep->pSelect, 0),
- sqlite3IdListDup(db, pStep->pIdList),
+ sqlite3Insert(pParse,
+ sqlite3TriggerStepSrc(pParse, pStep),
+ sqlite3SelectDup(db, pStep->pSelect, 0),
+ sqlite3IdListDup(db, pStep->pIdList),
pParse->eOrconf,
sqlite3UpsertDup(db, pStep->pUpsert)
);
+ sqlite3VdbeAddOp0(v, OP_ResetCount);
break;
}
case TK_DELETE: {
- sqlite3DeleteFrom(pParse,
- targetSrcList(pParse, pStep),
+ sqlite3DeleteFrom(pParse,
+ sqlite3TriggerStepSrc(pParse, pStep),
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
);
+ sqlite3VdbeAddOp0(v, OP_ResetCount);
break;
}
default: assert( pStep->op==TK_SELECT ); {
@@ -131551,9 +151623,6 @@ static int codeTriggerProgram(
sqlite3SelectDelete(db, pSelect);
break;
}
- }
- if( pStep->op!=TK_SELECT ){
- sqlite3VdbeAddOp0(v, OP_ResetCount);
}
}
@@ -131596,7 +151665,7 @@ static void transferParseError(Parse *pTo, Parse *pFrom){
}
/*
-** Create and populate a new TriggerPrg object with a sub-program
+** Create and populate a new TriggerPrg object with a sub-program
** implementing trigger pTrigger with ON CONFLICT policy orconf.
*/
static TriggerPrg *codeRowTrigger(
@@ -131612,14 +151681,14 @@ static TriggerPrg *codeRowTrigger(
Vdbe *v; /* Temporary VM */
NameContext sNC; /* Name context for sub-vdbe */
SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */
- Parse *pSubParse; /* Parse context for sub-vdbe */
int iEndTrigger = 0; /* Label to jump to if WHEN is false */
+ Parse sSubParse; /* Parse context for sub-vdbe */
assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) );
assert( pTop->pVdbe );
/* Allocate the TriggerPrg and SubProgram objects. To ensure that they
- ** are freed if an error occurs, link them into the Parse.pTriggerPrg
+ ** are freed if an error occurs, link them into the Parse.pTriggerPrg
** list of the top-level Parse object sooner rather than later. */
pPrg = sqlite3DbMallocZero(db, sizeof(TriggerPrg));
if( !pPrg ) return 0;
@@ -131633,22 +151702,21 @@ static TriggerPrg *codeRowTrigger(
pPrg->aColmask[0] = 0xffffffff;
pPrg->aColmask[1] = 0xffffffff;
- /* Allocate and populate a new Parse context to use for coding the
+ /* Allocate and populate a new Parse context to use for coding the
** trigger sub-program. */
- pSubParse = sqlite3StackAllocZero(db, sizeof(Parse));
- if( !pSubParse ) return 0;
+ sqlite3ParseObjectInit(&sSubParse, db);
memset(&sNC, 0, sizeof(sNC));
- sNC.pParse = pSubParse;
- pSubParse->db = db;
- pSubParse->pTriggerTab = pTab;
- pSubParse->pToplevel = pTop;
- pSubParse->zAuthContext = pTrigger->zName;
- pSubParse->eTriggerOp = pTrigger->op;
- pSubParse->nQueryLoop = pParse->nQueryLoop;
-
- v = sqlite3GetVdbe(pSubParse);
+ sNC.pParse = &sSubParse;
+ sSubParse.pTriggerTab = pTab;
+ sSubParse.pToplevel = pTop;
+ sSubParse.zAuthContext = pTrigger->zName;
+ sSubParse.eTriggerOp = pTrigger->op;
+ sSubParse.nQueryLoop = pParse->nQueryLoop;
+ sSubParse.prepFlags = pParse->prepFlags;
+
+ v = sqlite3GetVdbe(&sSubParse);
if( v ){
- VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)",
+ VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)",
pTrigger->zName, onErrorText(orconf),
(pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"),
(pTrigger->op==TK_UPDATE ? "UPDATE" : ""),
@@ -131658,28 +151726,28 @@ static TriggerPrg *codeRowTrigger(
));
#ifndef SQLITE_OMIT_TRACE
if( pTrigger->zName ){
- sqlite3VdbeChangeP4(v, -1,
+ sqlite3VdbeChangeP4(v, -1,
sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC
);
}
#endif
/* If one was specified, code the WHEN clause. If it evaluates to false
- ** (or NULL) the sub-vdbe is immediately halted by jumping to the
+ ** (or NULL) the sub-vdbe is immediately halted by jumping to the
** OP_Halt inserted at the end of the program. */
if( pTrigger->pWhen ){
pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0);
- if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen)
- && db->mallocFailed==0
+ if( db->mallocFailed==0
+ && SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen)
){
- iEndTrigger = sqlite3VdbeMakeLabel(v);
- sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL);
+ iEndTrigger = sqlite3VdbeMakeLabel(&sSubParse);
+ sqlite3ExprIfFalse(&sSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL);
}
sqlite3ExprDelete(db, pWhen);
}
/* Code the trigger program into the sub-vdbe. */
- codeTriggerProgram(pSubParse, pTrigger->step_list, orconf);
+ codeTriggerProgram(&sSubParse, pTrigger->step_list, orconf);
/* Insert an OP_Halt at the end of the sub-program. */
if( iEndTrigger ){
@@ -131687,27 +151755,27 @@ static TriggerPrg *codeRowTrigger(
}
sqlite3VdbeAddOp0(v, OP_Halt);
VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf)));
+ transferParseError(pParse, &sSubParse);
- transferParseError(pParse, pSubParse);
- if( db->mallocFailed==0 && pParse->nErr==0 ){
+ if( pParse->nErr==0 ){
+ assert( db->mallocFailed==0 );
pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg);
}
- pProgram->nMem = pSubParse->nMem;
- pProgram->nCsr = pSubParse->nTab;
+ pProgram->nMem = sSubParse.nMem;
+ pProgram->nCsr = sSubParse.nTab;
pProgram->token = (void *)pTrigger;
- pPrg->aColmask[0] = pSubParse->oldmask;
- pPrg->aColmask[1] = pSubParse->newmask;
+ pPrg->aColmask[0] = sSubParse.oldmask;
+ pPrg->aColmask[1] = sSubParse.newmask;
sqlite3VdbeDelete(v);
+ }else{
+ transferParseError(pParse, &sSubParse);
}
- assert( !pSubParse->pAinc && !pSubParse->pZombieTab );
- assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg );
- sqlite3ParserReset(pSubParse);
- sqlite3StackFree(db, pSubParse);
-
+ assert( !sSubParse.pTriggerPrg && !sSubParse.nMaxArg );
+ sqlite3ParseObjectReset(&sSubParse);
return pPrg;
}
-
+
/*
** Return a pointer to a TriggerPrg object containing the sub-program for
** trigger pTrigger with default ON CONFLICT algorithm orconf. If no such
@@ -131729,21 +151797,22 @@ static TriggerPrg *getRowTrigger(
** process of being coded). If this is the case, then an entry with
** a matching TriggerPrg.pTrigger field will be present somewhere
** in the Parse.pTriggerPrg list. Search for such an entry. */
- for(pPrg=pRoot->pTriggerPrg;
- pPrg && (pPrg->pTrigger!=pTrigger || pPrg->orconf!=orconf);
+ for(pPrg=pRoot->pTriggerPrg;
+ pPrg && (pPrg->pTrigger!=pTrigger || pPrg->orconf!=orconf);
pPrg=pPrg->pNext
);
/* If an existing TriggerPrg could not be located, create a new one. */
if( !pPrg ){
pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf);
+ pParse->db->errByteOffset = -1;
}
return pPrg;
}
/*
-** Generate code for the trigger program associated with trigger p on
+** Generate code for the trigger program associated with trigger p on
** table pTab. The reg, orconf and ignoreJump parameters passed to this
** function are the same as those described in the header function for
** sqlite3CodeRowTrigger()
@@ -131759,9 +151828,9 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(
Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */
TriggerPrg *pPrg;
pPrg = getRowTrigger(pParse, p, pTab, orconf);
- assert( pPrg || pParse->nErr || pParse->db->mallocFailed );
+ assert( pPrg || pParse->nErr );
- /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program
+ /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program
** is a pointer to the sub-vdbe containing the trigger program. */
if( pPrg ){
int bRecursive = (p->zName && 0==(pParse->db->flags&SQLITE_RecTriggers));
@@ -131790,7 +151859,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(
** If there are no triggers that fire at the specified time for the specified
** operation on pTab, this function is a no-op.
**
-** The reg argument is the address of the first in an array of registers
+** The reg argument is the address of the first in an array of registers
** that contain the values substituted for the new.* and old.* references
** in the trigger program. If N is the number of columns in table pTab
** (a copy of pTab->nCol), then registers are populated as follows:
@@ -131802,17 +151871,17 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(
** ... ...
** reg+N OLD.* value of right-most column of pTab
** reg+N+1 NEW.rowid
-** reg+N+2 OLD.* value of left-most column of pTab
+** reg+N+2 NEW.* value of left-most column of pTab
** ... ...
** reg+N+N+1 NEW.* value of right-most column of pTab
**
** For ON DELETE triggers, the registers containing the NEW.* values will
-** never be accessed by the trigger program, so they are not allocated or
-** populated by the caller (there is no data to populate them with anyway).
+** never be accessed by the trigger program, so they are not allocated or
+** populated by the caller (there is no data to populate them with anyway).
** Similarly, for ON INSERT triggers the values stored in the OLD.* registers
** are never accessed, and so are not allocated by the caller. So, for an
** ON INSERT trigger, the value passed to this function as parameter reg
-** is not a readable register, although registers (reg+N) through
+** is not a readable register, although registers (reg+N) through
** (reg+N+N+1) are.
**
** Parameter orconf is the default conflict resolution algorithm for the
@@ -131844,23 +151913,31 @@ SQLITE_PRIVATE void sqlite3CodeRowTrigger(
** or else it must be a TEMP trigger. */
assert( p->pSchema!=0 );
assert( p->pTabSchema!=0 );
- assert( p->pSchema==p->pTabSchema
+ assert( p->pSchema==p->pTabSchema
|| p->pSchema==pParse->db->aDb[1].pSchema );
- /* Determine whether we should code this trigger */
- if( p->op==op
- && p->tr_tm==tr_tm
+ /* Determine whether we should code this trigger. One of two choices:
+ ** 1. The trigger is an exact match to the current DML statement
+ ** 2. This is a RETURNING trigger for INSERT but we are currently
+ ** doing the UPDATE part of an UPSERT.
+ */
+ if( (p->op==op || (p->bReturning && p->op==TK_INSERT && op==TK_UPDATE))
+ && p->tr_tm==tr_tm
&& checkColumnOverlap(p->pColumns, pChanges)
){
- sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
+ if( !p->bReturning ){
+ sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
+ }else if( sqlite3IsToplevel(pParse) ){
+ codeReturningTrigger(pParse, p, pTab, reg);
+ }
}
}
}
/*
-** Triggers may access values stored in the old.* or new.* pseudo-table.
-** This function returns a 32-bit bitmask indicating which columns of the
-** old.* or new.* tables actually are used by triggers. This information
+** Triggers may access values stored in the old.* or new.* pseudo-table.
+** This function returns a 32-bit bitmask indicating which columns of the
+** old.* or new.* tables actually are used by triggers. This information
** may be used by the caller, for example, to avoid having to load the entire
** old.* record into memory when executing an UPDATE or DELETE command.
**
@@ -131870,7 +151947,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTrigger(
** are more than 32 columns in the table, and at least one of the columns
** with an index greater than 32 may be accessed, 0xffffffff is returned.
**
-** It is not possible to determine if the old.rowid or new.rowid column is
+** It is not possible to determine if the old.rowid or new.rowid column is
** accessed by triggers. The caller must always assume that it is.
**
** Parameter isNew must be either 1 or 0. If it is 0, then the mask returned
@@ -131896,14 +151973,22 @@ SQLITE_PRIVATE u32 sqlite3TriggerColmask(
Trigger *p;
assert( isNew==1 || isNew==0 );
+ if( IsView(pTab) ){
+ return 0xffffffff;
+ }
for(p=pTrigger; p; p=p->pNext){
- if( p->op==op && (tr_tm&p->tr_tm)
+ if( p->op==op
+ && (tr_tm&p->tr_tm)
&& checkColumnOverlap(p->pColumns,pChanges)
){
- TriggerPrg *pPrg;
- pPrg = getRowTrigger(pParse, p, pTab, orconf);
- if( pPrg ){
- mask |= pPrg->aColmask[isNew];
+ if( p->bReturning ){
+ mask = 0xffffffff;
+ }else{
+ TriggerPrg *pPrg;
+ pPrg = getRowTrigger(pParse, p, pTab, orconf);
+ if( pPrg ){
+ mask |= pPrg->aColmask[isNew];
+ }
}
}
}
@@ -131947,10 +152032,10 @@ static void updateVirtualTable(
/*
** The most recently coded instruction was an OP_Column to retrieve the
-** i-th column of table pTab. This routine sets the P4 parameter of the
+** i-th column of table pTab. This routine sets the P4 parameter of the
** OP_Column to the default value, if any.
**
-** The default value of a column is specified by a DEFAULT clause in the
+** The default value of a column is specified by a DEFAULT clause in the
** column definition. This was either supplied by the user when the table
** was created, or added later to the table definition by an ALTER TABLE
** command. If the latter, then the row-records in the table btree on disk
@@ -131959,38 +152044,42 @@ static void updateVirtualTable(
** If the former, then all row-records are guaranteed to include a value
** for the column and the P4 value is not required.
**
-** Column definitions created by an ALTER TABLE command may only have
+** Column definitions created by an ALTER TABLE command may only have
** literal default values specified: a number, null or a string. (If a more
-** complicated default expression value was provided, it is evaluated
+** complicated default expression value was provided, it is evaluated
** when the ALTER TABLE is executed and one of the literal values written
-** into the sqlite_master table.)
+** into the sqlite_schema table.)
**
** Therefore, the P4 parameter is only required if the default value for
** the column is a literal number, string or null. The sqlite3ValueFromExpr()
** function is capable of transforming these types of expressions into
** sqlite3_value objects.
**
-** If parameter iReg is not negative, code an OP_RealAffinity instruction
-** on register iReg. This is used when an equivalent integer value is
-** stored in place of an 8-byte floating point value in order to save
-** space.
+** If column as REAL affinity and the table is an ordinary b-tree table
+** (not a virtual table) then the value might have been stored as an
+** integer. In that case, add an OP_RealAffinity opcode to make sure
+** it has been converted into REAL.
*/
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
+ Column *pCol;
assert( pTab!=0 );
- if( !pTab->pSelect ){
+ assert( pTab->nCol>i );
+ pCol = &pTab->aCol[i];
+ if( pCol->iDflt ){
sqlite3_value *pValue = 0;
u8 enc = ENC(sqlite3VdbeDb(v));
- Column *pCol = &pTab->aCol[i];
- VdbeComment((v, "%s.%s", pTab->zName, pCol->zName));
+ assert( !IsView(pTab) );
+ VdbeComment((v, "%s.%s", pTab->zName, pCol->zCnName));
assert( i<pTab->nCol );
- sqlite3ValueFromExpr(sqlite3VdbeDb(v), pCol->pDflt, enc,
+ sqlite3ValueFromExpr(sqlite3VdbeDb(v),
+ sqlite3ColumnExpr(pTab,pCol), enc,
pCol->affinity, &pValue);
if( pValue ){
sqlite3VdbeAppendP4(v, pValue, P4_MEM);
}
}
#ifndef SQLITE_OMIT_FLOATING_POINT
- if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
+ if( pCol->affinity==SQLITE_AFF_REAL && !IsVirtual(pTab) ){
sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg);
}
#endif
@@ -132048,11 +152137,152 @@ static int indexWhereClauseMightChange(
}
/*
+** Allocate and return a pointer to an expression of type TK_ROW with
+** Expr.iColumn set to value (iCol+1). The resolver will modify the
+** expression to be a TK_COLUMN reading column iCol of the first
+** table in the source-list (pSrc->a[0]).
+*/
+static Expr *exprRowColumn(Parse *pParse, int iCol){
+ Expr *pRet = sqlite3PExpr(pParse, TK_ROW, 0, 0);
+ if( pRet ) pRet->iColumn = iCol+1;
+ return pRet;
+}
+
+/*
+** Assuming both the pLimit and pOrderBy parameters are NULL, this function
+** generates VM code to run the query:
+**
+** SELECT <other-columns>, pChanges FROM pTabList WHERE pWhere
+**
+** and write the results to the ephemeral table already opened as cursor
+** iEph. None of pChanges, pTabList or pWhere are modified or consumed by
+** this function, they must be deleted by the caller.
+**
+** Or, if pLimit and pOrderBy are not NULL, and pTab is not a view:
+**
+** SELECT <other-columns>, pChanges FROM pTabList
+** WHERE pWhere
+** GROUP BY <other-columns>
+** ORDER BY pOrderBy LIMIT pLimit
+**
+** If pTab is a view, the GROUP BY clause is omitted.
+**
+** Exactly how results are written to table iEph, and exactly what
+** the <other-columns> in the query above are is determined by the type
+** of table pTabList->a[0].pTab.
+**
+** If the table is a WITHOUT ROWID table, then argument pPk must be its
+** PRIMARY KEY. In this case <other-columns> are the primary key columns
+** of the table, in order. The results of the query are written to ephemeral
+** table iEph as index keys, using OP_IdxInsert.
+**
+** If the table is actually a view, then <other-columns> are all columns of
+** the view. The results are written to the ephemeral table iEph as records
+** with automatically assigned integer keys.
+**
+** If the table is a virtual or ordinary intkey table, then <other-columns>
+** is its rowid. For a virtual table, the results are written to iEph as
+** records with automatically assigned integer keys For intkey tables, the
+** rowid value in <other-columns> is used as the integer key, and the
+** remaining fields make up the table record.
+*/
+static void updateFromSelect(
+ Parse *pParse, /* Parse context */
+ int iEph, /* Cursor for open eph. table */
+ Index *pPk, /* PK if table 0 is WITHOUT ROWID */
+ ExprList *pChanges, /* List of expressions to return */
+ SrcList *pTabList, /* List of tables to select from */
+ Expr *pWhere, /* WHERE clause for query */
+ ExprList *pOrderBy, /* ORDER BY clause */
+ Expr *pLimit /* LIMIT clause */
+){
+ int i;
+ SelectDest dest;
+ Select *pSelect = 0;
+ ExprList *pList = 0;
+ ExprList *pGrp = 0;
+ Expr *pLimit2 = 0;
+ ExprList *pOrderBy2 = 0;
+ sqlite3 *db = pParse->db;
+ Table *pTab = pTabList->a[0].pTab;
+ SrcList *pSrc;
+ Expr *pWhere2;
+ int eDest;
+
+#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
+ if( pOrderBy && pLimit==0 ) {
+ sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on UPDATE");
+ return;
+ }
+ pOrderBy2 = sqlite3ExprListDup(db, pOrderBy, 0);
+ pLimit2 = sqlite3ExprDup(db, pLimit, 0);
+#else
+ UNUSED_PARAMETER(pOrderBy);
+ UNUSED_PARAMETER(pLimit);
+#endif
+
+ pSrc = sqlite3SrcListDup(db, pTabList, 0);
+ pWhere2 = sqlite3ExprDup(db, pWhere, 0);
+
+ assert( pTabList->nSrc>1 );
+ if( pSrc ){
+ assert( pSrc->a[0].fg.notCte );
+ pSrc->a[0].iCursor = -1;
+ pSrc->a[0].pTab->nTabRef--;
+ pSrc->a[0].pTab = 0;
+ }
+ if( pPk ){
+ for(i=0; i<pPk->nKeyCol; i++){
+ Expr *pNew = exprRowColumn(pParse, pPk->aiColumn[i]);
+#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
+ if( pLimit ){
+ pGrp = sqlite3ExprListAppend(pParse, pGrp, sqlite3ExprDup(db, pNew, 0));
+ }
+#endif
+ pList = sqlite3ExprListAppend(pParse, pList, pNew);
+ }
+ eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom;
+ }else if( IsView(pTab) ){
+ for(i=0; i<pTab->nCol; i++){
+ pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i));
+ }
+ eDest = SRT_Table;
+ }else{
+ eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom;
+ pList = sqlite3ExprListAppend(pParse, 0, sqlite3PExpr(pParse,TK_ROW,0,0));
+#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
+ if( pLimit ){
+ pGrp = sqlite3ExprListAppend(pParse, 0, sqlite3PExpr(pParse,TK_ROW,0,0));
+ }
+#endif
+ }
+ assert( pChanges!=0 || pParse->db->mallocFailed );
+ if( pChanges ){
+ for(i=0; i<pChanges->nExpr; i++){
+ pList = sqlite3ExprListAppend(pParse, pList,
+ sqlite3ExprDup(db, pChanges->a[i].pExpr, 0)
+ );
+ }
+ }
+ pSelect = sqlite3SelectNew(pParse, pList,
+ pSrc, pWhere2, pGrp, 0, pOrderBy2,
+ SF_UFSrcCheck|SF_IncludeHidden|SF_UpdateFrom, pLimit2
+ );
+ if( pSelect ) pSelect->selFlags |= SF_OrderByReqd;
+ sqlite3SelectDestInit(&dest, eDest, iEph);
+ dest.iSDParm2 = (pPk ? pPk->nKeyCol : -1);
+ sqlite3Select(pParse, pSelect, &dest);
+ sqlite3SelectDelete(db, pSelect);
+}
+
+/*
** Process an UPDATE statement.
**
-** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
-** \_______/ \________/ \______/ \________________/
-* onError pTabList pChanges pWhere
+** UPDATE OR IGNORE tbl SET a=b, c=d FROM tbl2... WHERE e<5 AND f NOT NULL;
+** \_______/ \_/ \______/ \_____/ \________________/
+** onError | pChanges | pWhere
+** \_______________________/
+** pTabList
*/
SQLITE_PRIVATE void sqlite3Update(
Parse *pParse, /* The parser context */
@@ -132064,19 +152294,20 @@ SQLITE_PRIVATE void sqlite3Update(
Expr *pLimit, /* LIMIT clause. May be null */
Upsert *pUpsert /* ON CONFLICT clause, or null */
){
- int i, j; /* Loop counters */
+ int i, j, k; /* Loop counters */
Table *pTab; /* The table to be updated */
int addrTop = 0; /* VDBE instruction address of the start of the loop */
- WhereInfo *pWInfo; /* Information about the WHERE clause */
+ WhereInfo *pWInfo = 0; /* Information about the WHERE clause */
Vdbe *v; /* The virtual database engine */
Index *pIdx; /* For looping over indices */
Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */
int nIdx; /* Number of indices that need updating */
+ int nAllIdx; /* Total number of indexes */
int iBaseCur; /* Base cursor number */
int iDataCur; /* Cursor for the canonical data btree */
int iIdxCur; /* Cursor for the first index */
sqlite3 *db; /* The database structure */
- int *aRegIdx = 0; /* First register in array assigned to each index */
+ int *aRegIdx = 0; /* Registers for to each index and the main table */
int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
** an expression for the i-th column of the table.
** aXRef[i]==-1 if the i-th column is not changed. */
@@ -132085,6 +152316,7 @@ SQLITE_PRIVATE void sqlite3Update(
u8 chngRowid; /* Rowid changed in a normal table */
u8 chngKey; /* Either chngPk or chngRowid */
Expr *pRowidExpr = 0; /* Expression defining the new record number */
+ int iRowidExpr = -1; /* Index of "rowid=" (or IPK) assignment in pChanges */
AuthContext sContext; /* The authorization context */
NameContext sNC; /* The name-context to resolve expressions in */
int iDb; /* Database containing the table being updated */
@@ -132107,6 +152339,8 @@ SQLITE_PRIVATE void sqlite3Update(
int iPk = 0; /* First of nPk cells holding PRIMARY KEY value */
i16 nPk = 0; /* Number of components of the PRIMARY KEY */
int bReplace = 0; /* True if REPLACE conflict resolution might happen */
+ int bFinishSeek = 1; /* The OP_FinishSeek opcode is needed */
+ int nChangeFrom = 0; /* If there is a FROM, pChanges->nExpr, else 0 */
/* Register Allocations */
int regRowCount = 0; /* A count of rows changed */
@@ -132119,12 +152353,13 @@ SQLITE_PRIVATE void sqlite3Update(
memset(&sContext, 0, sizeof(sContext));
db = pParse->db;
- if( pParse->nErr || db->mallocFailed ){
+ assert( db->pParse==pParse );
+ if( pParse->nErr ){
goto update_cleanup;
}
- assert( pTabList->nSrc==1 );
+ assert( db->mallocFailed==0 );
- /* Locate the table which we want to update.
+ /* Locate the table which we want to update.
*/
pTab = sqlite3SrcListLookup(pParse, pTabList);
if( pTab==0 ) goto update_cleanup;
@@ -132135,7 +152370,7 @@ SQLITE_PRIVATE void sqlite3Update(
*/
#ifndef SQLITE_OMIT_TRIGGER
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, &tmask);
- isView = pTab->pSelect!=0;
+ isView = IsView(pTab);
assert( pTrigger || tmask==0 );
#else
# define pTrigger 0
@@ -132147,8 +152382,23 @@ SQLITE_PRIVATE void sqlite3Update(
# define isView 0
#endif
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x10000 ){
+ sqlite3TreeViewLine(0, "In sqlite3Update() at %s:%d", __FILE__, __LINE__);
+ sqlite3TreeViewUpdate(pParse->pWith, pTabList, pChanges, pWhere,
+ onError, pOrderBy, pLimit, pUpsert, pTrigger);
+ }
+#endif
+
+ /* If there was a FROM clause, set nChangeFrom to the number of expressions
+ ** in the change-list. Otherwise, set it to 0. There cannot be a FROM
+ ** clause if this function is being called to generate code for part of
+ ** an UPSERT statement. */
+ nChangeFrom = (pTabList->nSrc>1) ? pChanges->nExpr : 0;
+ assert( nChangeFrom==0 || pUpsert==0 );
+
#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
- if( !isView ){
+ if( !isView && nChangeFrom==0 ){
pWhere = sqlite3LimitWhere(
pParse, pTabList, pWhere, pOrderBy, pLimit, "UPDATE"
);
@@ -132160,7 +152410,7 @@ SQLITE_PRIVATE void sqlite3Update(
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto update_cleanup;
}
- if( sqlite3IsReadOnly(pParse, pTab, tmask) ){
+ if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){
goto update_cleanup;
}
@@ -132187,13 +152437,13 @@ SQLITE_PRIVATE void sqlite3Update(
}
pTabList->a[0].iCursor = iDataCur;
- /* Allocate space for aXRef[], aRegIdx[], and aToOpen[].
+ /* Allocate space for aXRef[], aRegIdx[], and aToOpen[].
** Initialize aXRef[] and aToOpen[] to their default values.
*/
- aXRef = sqlite3DbMallocRawNN(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 );
+ aXRef = sqlite3DbMallocRawNN(db, sizeof(int) * (pTab->nCol+nIdx+1) + nIdx+2 );
if( aXRef==0 ) goto update_cleanup;
aRegIdx = aXRef+pTab->nCol;
- aToOpen = (u8*)(aRegIdx+nIdx);
+ aToOpen = (u8*)(aRegIdx+nIdx+1);
memset(aToOpen, 1, nIdx+1);
aToOpen[nIdx+1] = 0;
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
@@ -132205,6 +152455,10 @@ SQLITE_PRIVATE void sqlite3Update(
sNC.uNC.pUpsert = pUpsert;
sNC.ncFlags = NC_UUpsert;
+ /* Begin generating code. */
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) goto update_cleanup;
+
/* Resolve the column names in all the expressions of the
** of the UPDATE statement. Also find the column index
** for each column to be updated in the pChanges array. For each
@@ -132213,28 +152467,45 @@ SQLITE_PRIVATE void sqlite3Update(
*/
chngRowid = chngPk = 0;
for(i=0; i<pChanges->nExpr; i++){
- if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
+ u8 hCol = sqlite3StrIHash(pChanges->a[i].zEName);
+ /* If this is an UPDATE with a FROM clause, do not resolve expressions
+ ** here. The call to sqlite3Select() below will do that. */
+ if( nChangeFrom==0 && sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
goto update_cleanup;
}
for(j=0; j<pTab->nCol; j++){
- if( sqlite3StrICmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){
+ if( pTab->aCol[j].hName==hCol
+ && sqlite3StrICmp(pTab->aCol[j].zCnName, pChanges->a[i].zEName)==0
+ ){
if( j==pTab->iPKey ){
chngRowid = 1;
pRowidExpr = pChanges->a[i].pExpr;
+ iRowidExpr = i;
}else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){
chngPk = 1;
}
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ else if( pTab->aCol[j].colFlags & COLFLAG_GENERATED ){
+ testcase( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL );
+ testcase( pTab->aCol[j].colFlags & COLFLAG_STORED );
+ sqlite3ErrorMsg(pParse,
+ "cannot UPDATE generated column \"%s\"",
+ pTab->aCol[j].zCnName);
+ goto update_cleanup;
+ }
+#endif
aXRef[j] = i;
break;
}
}
if( j>=pTab->nCol ){
- if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zName) ){
+ if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zEName) ){
j = -1;
chngRowid = 1;
pRowidExpr = pChanges->a[i].pExpr;
+ iRowidExpr = i;
}else{
- sqlite3ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName);
+ sqlite3ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zEName);
pParse->checkSchema = 1;
goto update_cleanup;
}
@@ -132243,7 +152514,7 @@ SQLITE_PRIVATE void sqlite3Update(
{
int rc;
rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName,
- j<0 ? "ROWID" : pTab->aCol[j].zName,
+ j<0 ? "ROWID" : pTab->aCol[j].zCnName,
db->aDb[iDb].zDbSName);
if( rc==SQLITE_DENY ){
goto update_cleanup;
@@ -132258,7 +152529,36 @@ SQLITE_PRIVATE void sqlite3Update(
assert( chngPk==0 || chngPk==1 );
chngKey = chngRowid + chngPk;
- /* The SET expressions are not actually used inside the WHERE loop.
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ /* Mark generated columns as changing if their generator expressions
+ ** reference any changing column. The actual aXRef[] value for
+ ** generated expressions is not used, other than to check to see that it
+ ** is non-negative, so the value of aXRef[] for generated columns can be
+ ** set to any non-negative number. We use 99999 so that the value is
+ ** obvious when looking at aXRef[] in a symbolic debugger.
+ */
+ if( pTab->tabFlags & TF_HasGenerated ){
+ int bProgress;
+ testcase( pTab->tabFlags & TF_HasVirtual );
+ testcase( pTab->tabFlags & TF_HasStored );
+ do{
+ bProgress = 0;
+ for(i=0; i<pTab->nCol; i++){
+ if( aXRef[i]>=0 ) continue;
+ if( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)==0 ) continue;
+ if( sqlite3ExprReferencesUpdatedColumn(
+ sqlite3ColumnExpr(pTab, &pTab->aCol[i]),
+ aXRef, chngRowid)
+ ){
+ aXRef[i] = 99999;
+ bProgress = 1;
+ }
+ }
+ }while( bProgress );
+ }
+#endif
+
+ /* The SET expressions are not actually used inside the WHERE loop.
** So reset the colUsed mask. Unless this is a virtual table. In that
** case, set all bits of the colUsed mask (to ensure that the virtual
** table implementation makes all columns available).
@@ -132271,7 +152571,8 @@ SQLITE_PRIVATE void sqlite3Update(
** being updated. Fill in aRegIdx[] with a register number that will hold
** the key for accessing each index.
*/
- for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
+ if( onError==OE_Replace ) bReplace = 1;
+ for(nAllIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nAllIdx++){
int reg;
if( chngKey || hasFK>1 || pIdx==pPk
|| indexWhereClauseMightChange(pIdx,aXRef,chngRowid)
@@ -132284,33 +152585,35 @@ SQLITE_PRIVATE void sqlite3Update(
if( indexColumnIsBeingUpdated(pIdx, i, aXRef, chngRowid) ){
reg = ++pParse->nMem;
pParse->nMem += pIdx->nColumn;
- if( (onError==OE_Replace)
- || (onError==OE_Default && pIdx->onError==OE_Replace)
- ){
+ if( onError==OE_Default && pIdx->onError==OE_Replace ){
bReplace = 1;
}
break;
}
}
}
- if( reg==0 ) aToOpen[j+1] = 0;
- aRegIdx[j] = reg;
+ if( reg==0 ) aToOpen[nAllIdx+1] = 0;
+ aRegIdx[nAllIdx] = reg;
}
+ aRegIdx[nAllIdx] = ++pParse->nMem; /* Register storing the table record */
if( bReplace ){
- /* If REPLACE conflict resolution might be invoked, open cursors on all
+ /* If REPLACE conflict resolution might be invoked, open cursors on all
** indexes in case they are needed to delete records. */
memset(aToOpen, 1, nIdx+1);
}
- /* Begin generating code. */
- v = sqlite3GetVdbe(pParse);
- if( v==0 ) goto update_cleanup;
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
sqlite3BeginWriteOperation(pParse, pTrigger || hasFK, iDb);
/* Allocate required registers. */
if( !IsVirtual(pTab) ){
- regRowSet = ++pParse->nMem;
+ /* For now, regRowSet and aRegIdx[nAllIdx] share the same register.
+ ** If regRowSet turns out to be needed, then aRegIdx[nAllIdx] will be
+ ** reallocated. aRegIdx[nAllIdx] is the register in which the main
+ ** table record is written. regRowSet holds the RowSet for the
+ ** two-pass update algorithm. */
+ assert( aRegIdx[nAllIdx]==pParse->nMem );
+ regRowSet = aRegIdx[nAllIdx];
regOldRowid = regNewRowid = ++pParse->nMem;
if( chngPk || pTrigger || hasFK ){
regOld = pParse->nMem + 1;
@@ -132332,8 +152635,8 @@ SQLITE_PRIVATE void sqlite3Update(
** an ephemeral table.
*/
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
- if( isView ){
- sqlite3MaterializeView(pParse, pTab,
+ if( nChangeFrom==0 && isView ){
+ sqlite3MaterializeView(pParse, pTab,
pWhere, pOrderBy, pLimit, iDataCur
);
pOrderBy = 0;
@@ -132344,7 +152647,7 @@ SQLITE_PRIVATE void sqlite3Update(
/* Resolve the column names in all the expressions in the
** WHERE clause.
*/
- if( sqlite3ResolveExprNames(&sNC, pWhere) ){
+ if( nChangeFrom==0 && sqlite3ResolveExprNames(&sNC, pWhere) ){
goto update_cleanup;
}
@@ -132358,155 +152661,226 @@ SQLITE_PRIVATE void sqlite3Update(
#endif
/* Jump to labelBreak to abandon further processing of this UPDATE */
- labelContinue = labelBreak = sqlite3VdbeMakeLabel(v);
+ labelContinue = labelBreak = sqlite3VdbeMakeLabel(pParse);
/* Not an UPSERT. Normal processing. Begin by
** initialize the count of updated rows */
if( (db->flags&SQLITE_CountRows)!=0
&& !pParse->pTriggerTab
&& !pParse->nested
+ && !pParse->bReturning
&& pUpsert==0
){
regRowCount = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
}
- if( HasRowid(pTab) ){
+ if( nChangeFrom==0 && HasRowid(pTab) ){
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
+ iEph = pParse->nTab++;
+ addrOpen = sqlite3VdbeAddOp3(v, OP_OpenEphemeral, iEph, 0, regRowSet);
}else{
- assert( pPk!=0 );
- nPk = pPk->nKeyCol;
+ assert( pPk!=0 || HasRowid(pTab) );
+ nPk = pPk ? pPk->nKeyCol : 0;
iPk = pParse->nMem+1;
pParse->nMem += nPk;
+ pParse->nMem += nChangeFrom;
regKey = ++pParse->nMem;
if( pUpsert==0 ){
+ int nEphCol = nPk + nChangeFrom + (isView ? pTab->nCol : 0);
iEph = pParse->nTab++;
- sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1);
- addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
- sqlite3VdbeSetP4KeyInfo(pParse, pPk);
- }
- }
-
- if( pUpsert ){
- /* If this is an UPSERT, then all cursors have already been opened by
- ** the outer INSERT and the data cursor should be pointing at the row
- ** that is to be updated. So bypass the code that searches for the
- ** row(s) to be updated.
- */
- pWInfo = 0;
- eOnePass = ONEPASS_SINGLE;
- sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL);
- }else{
- /* Begin the database scan.
- **
- ** Do not consider a single-pass strategy for a multi-row update if
- ** there are any triggers or foreign keys to process, or rows may
- ** be deleted as a result of REPLACE conflict handling. Any of these
- ** things might disturb a cursor being used to scan through the table
- ** or index, causing a single-pass approach to malfunction. */
- flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE;
- if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
- flags |= WHERE_ONEPASS_MULTIROW;
- }
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
- if( pWInfo==0 ) goto update_cleanup;
-
- /* A one-pass strategy that might update more than one row may not
- ** be used if any column of the index used for the scan is being
- ** updated. Otherwise, if there is an index on "b", statements like
- ** the following could create an infinite loop:
- **
- ** UPDATE t1 SET b=b+1 WHERE b>?
- **
- ** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
- ** strategy that uses an index for which one or more columns are being
- ** updated. */
- eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
- if( eOnePass!=ONEPASS_SINGLE ){
- sqlite3MultiWrite(pParse);
- if( eOnePass==ONEPASS_MULTI ){
- int iCur = aiCurOnePass[1];
- if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){
- eOnePass = ONEPASS_OFF;
+ if( pPk ) sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1);
+ addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nEphCol);
+ if( pPk ){
+ KeyInfo *pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pPk);
+ if( pKeyInfo ){
+ pKeyInfo->nAllField = nEphCol;
+ sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO);
}
- assert( iCur!=iDataCur || !HasRowid(pTab) );
+ }
+ if( nChangeFrom ){
+ updateFromSelect(
+ pParse, iEph, pPk, pChanges, pTabList, pWhere, pOrderBy, pLimit
+ );
+#ifndef SQLITE_OMIT_SUBQUERY
+ if( isView ) iDataCur = iEph;
+#endif
}
}
}
- if( HasRowid(pTab) ){
- /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
- ** mode, write the rowid into the FIFO. In either of the one-pass modes,
- ** leave it in register regOldRowid. */
- sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
- if( eOnePass==ONEPASS_OFF ){
- sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
- }
- }else{
- /* Read the PK of the current row into an array of registers. In
- ** ONEPASS_OFF mode, serialize the array into a record and store it in
- ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change
- ** the OP_OpenEphemeral instruction to a Noop (the ephemeral table
- ** is not required) and leave the PK fields in the array of registers. */
- for(i=0; i<nPk; i++){
- assert( pPk->aiColumn[i]>=0 );
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,pPk->aiColumn[i],iPk+i);
+ if( nChangeFrom ){
+ sqlite3MultiWrite(pParse);
+ eOnePass = ONEPASS_OFF;
+ nKey = nPk;
+ regKey = iPk;
+ }else{
+ if( pUpsert ){
+ /* If this is an UPSERT, then all cursors have already been opened by
+ ** the outer INSERT and the data cursor should be pointing at the row
+ ** that is to be updated. So bypass the code that searches for the
+ ** row(s) to be updated.
+ */
+ pWInfo = 0;
+ eOnePass = ONEPASS_SINGLE;
+ sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL);
+ bFinishSeek = 0;
+ }else{
+ /* Begin the database scan.
+ **
+ ** Do not consider a single-pass strategy for a multi-row update if
+ ** there is anything that might disrupt the cursor being used to do
+ ** the UPDATE:
+ ** (1) This is a nested UPDATE
+ ** (2) There are triggers
+ ** (3) There are FOREIGN KEY constraints
+ ** (4) There are REPLACE conflict handlers
+ ** (5) There are subqueries in the WHERE clause
+ */
+ flags = WHERE_ONEPASS_DESIRED;
+ if( !pParse->nested
+ && !pTrigger
+ && !hasFK
+ && !chngKey
+ && !bReplace
+ && (pWhere==0 || !ExprHasProperty(pWhere, EP_Subquery))
+ ){
+ flags |= WHERE_ONEPASS_MULTIROW;
+ }
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,0,0,flags,iIdxCur);
+ if( pWInfo==0 ) goto update_cleanup;
+
+ /* A one-pass strategy that might update more than one row may not
+ ** be used if any column of the index used for the scan is being
+ ** updated. Otherwise, if there is an index on "b", statements like
+ ** the following could create an infinite loop:
+ **
+ ** UPDATE t1 SET b=b+1 WHERE b>?
+ **
+ ** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
+ ** strategy that uses an index for which one or more columns are being
+ ** updated. */
+ eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
+ bFinishSeek = sqlite3WhereUsesDeferredSeek(pWInfo);
+ if( eOnePass!=ONEPASS_SINGLE ){
+ sqlite3MultiWrite(pParse);
+ if( eOnePass==ONEPASS_MULTI ){
+ int iCur = aiCurOnePass[1];
+ if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){
+ eOnePass = ONEPASS_OFF;
+ }
+ assert( iCur!=iDataCur || !HasRowid(pTab) );
+ }
+ }
}
- if( eOnePass ){
- if( addrOpen ) sqlite3VdbeChangeToNoop(v, addrOpen);
- nKey = nPk;
- regKey = iPk;
+
+ if( HasRowid(pTab) ){
+ /* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
+ ** mode, write the rowid into the FIFO. In either of the one-pass modes,
+ ** leave it in register regOldRowid. */
+ sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
+ if( eOnePass==ONEPASS_OFF ){
+ aRegIdx[nAllIdx] = ++pParse->nMem;
+ sqlite3VdbeAddOp3(v, OP_Insert, iEph, regRowSet, regOldRowid);
+ }else{
+ if( ALWAYS(addrOpen) ) sqlite3VdbeChangeToNoop(v, addrOpen);
+ }
}else{
- sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey,
- sqlite3IndexAffinityStr(db, pPk), nPk);
- sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEph, regKey, iPk, nPk);
+ /* Read the PK of the current row into an array of registers. In
+ ** ONEPASS_OFF mode, serialize the array into a record and store it in
+ ** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change
+ ** the OP_OpenEphemeral instruction to a Noop (the ephemeral table
+ ** is not required) and leave the PK fields in the array of registers. */
+ for(i=0; i<nPk; i++){
+ assert( pPk->aiColumn[i]>=0 );
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,
+ pPk->aiColumn[i], iPk+i);
+ }
+ if( eOnePass ){
+ if( addrOpen ) sqlite3VdbeChangeToNoop(v, addrOpen);
+ nKey = nPk;
+ regKey = iPk;
+ }else{
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey,
+ sqlite3IndexAffinityStr(db, pPk), nPk);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEph, regKey, iPk, nPk);
+ }
}
}
if( pUpsert==0 ){
- if( eOnePass!=ONEPASS_MULTI ){
+ if( nChangeFrom==0 && eOnePass!=ONEPASS_MULTI ){
sqlite3WhereEnd(pWInfo);
}
-
+
if( !isView ){
int addrOnce = 0;
-
+ int iNotUsed1 = 0;
+ int iNotUsed2 = 0;
+
/* Open every index that needs updating. */
if( eOnePass!=ONEPASS_OFF ){
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
}
-
+
if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur,
- aToOpen, 0, 0);
- if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
+ aToOpen, &iNotUsed1, &iNotUsed2);
+ if( addrOnce ){
+ sqlite3VdbeJumpHereOrPopInst(v, addrOnce);
+ }
}
-
+
/* Top of the update loop */
if( eOnePass!=ONEPASS_OFF ){
- if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){
+ if( aiCurOnePass[0]!=iDataCur
+ && aiCurOnePass[1]!=iDataCur
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ && !isView
+#endif
+ ){
assert( pPk );
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey,nKey);
VdbeCoverage(v);
}
if( eOnePass!=ONEPASS_SINGLE ){
- labelContinue = sqlite3VdbeMakeLabel(v);
+ labelContinue = sqlite3VdbeMakeLabel(pParse);
}
sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
VdbeCoverageIf(v, pPk==0);
VdbeCoverageIf(v, pPk!=0);
- }else if( pPk ){
- labelContinue = sqlite3VdbeMakeLabel(v);
+ }else if( pPk || nChangeFrom ){
+ labelContinue = sqlite3VdbeMakeLabel(pParse);
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
- addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
- sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
- VdbeCoverage(v);
+ addrTop = sqlite3VdbeCurrentAddr(v);
+ if( nChangeFrom ){
+ if( !isView ){
+ if( pPk ){
+ for(i=0; i<nPk; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, iEph, i, iPk+i);
+ }
+ sqlite3VdbeAddOp4Int(
+ v, OP_NotFound, iDataCur, labelContinue, iPk, nPk
+ ); VdbeCoverage(v);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid);
+ sqlite3VdbeAddOp3(
+ v, OP_NotExists, iDataCur, labelContinue, regOldRowid
+ ); VdbeCoverage(v);
+ }
+ }
+ }else{
+ sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey,0);
+ VdbeCoverage(v);
+ }
}else{
- labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak,
- regOldRowid);
+ sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
+ labelContinue = sqlite3VdbeMakeLabel(pParse);
+ addrTop = sqlite3VdbeAddOp2(v, OP_Rowid, iEph, regOldRowid);
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
VdbeCoverage(v);
@@ -132519,7 +152893,12 @@ SQLITE_PRIVATE void sqlite3Update(
** already populated. */
assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid );
if( chngRowid ){
- sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
+ assert( iRowidExpr>=0 );
+ if( nChangeFrom==0 ){
+ sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Column, iEph, iRowidExpr, regNewRowid);
+ }
sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); VdbeCoverage(v);
}
@@ -132527,18 +152906,20 @@ SQLITE_PRIVATE void sqlite3Update(
** information is needed */
if( chngPk || hasFK || pTrigger ){
u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
- oldmask |= sqlite3TriggerColmask(pParse,
+ oldmask |= sqlite3TriggerColmask(pParse,
pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError
);
for(i=0; i<pTab->nCol; i++){
+ u32 colFlags = pTab->aCol[i].colFlags;
+ k = sqlite3TableColumnToStorage(pTab, i) + regOld;
if( oldmask==0xffffffff
|| (i<32 && (oldmask & MASKBIT32(i))!=0)
- || (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0
+ || (colFlags & COLFLAG_PRIMKEY)!=0
){
testcase( oldmask!=0xffffffff && i==31 );
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regOld+i);
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, k);
}else{
- sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, k);
}
}
if( chngRowid==0 && pPk==0 ){
@@ -132554,104 +152935,142 @@ SQLITE_PRIVATE void sqlite3Update(
** If there are one or more BEFORE triggers, then do not populate the
** registers associated with columns that are (a) not modified by
** this UPDATE statement and (b) not accessed by new.* references. The
- ** values for registers not modified by the UPDATE must be reloaded from
- ** the database after the BEFORE triggers are fired anyway (as the trigger
+ ** values for registers not modified by the UPDATE must be reloaded from
+ ** the database after the BEFORE triggers are fired anyway (as the trigger
** may have modified them). So not loading those that are not going to
** be used eliminates some redundant opcodes.
*/
newmask = sqlite3TriggerColmask(
pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
);
- for(i=0; i<pTab->nCol; i++){
+ for(i=0, k=regNew; i<pTab->nCol; i++, k++){
if( i==pTab->iPKey ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, k);
+ }else if( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)!=0 ){
+ if( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL ) k--;
}else{
j = aXRef[i];
if( j>=0 ){
- sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i);
+ if( nChangeFrom ){
+ int nOff = (isView ? pTab->nCol : nPk);
+ assert( eOnePass==ONEPASS_OFF );
+ sqlite3VdbeAddOp3(v, OP_Column, iEph, nOff+j, k);
+ }else{
+ sqlite3ExprCode(pParse, pChanges->a[j].pExpr, k);
+ }
}else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask & MASKBIT32(i)) ){
- /* This branch loads the value of a column that will not be changed
+ /* This branch loads the value of a column that will not be changed
** into a register. This is done if there are no BEFORE triggers, or
** if there are one or more BEFORE triggers that use this value via
** a new.* reference in a trigger program.
*/
testcase( i==31 );
testcase( i==32 );
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, k);
+ bFinishSeek = 0;
}else{
- sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, k);
}
}
}
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ if( pTab->tabFlags & TF_HasGenerated ){
+ testcase( pTab->tabFlags & TF_HasVirtual );
+ testcase( pTab->tabFlags & TF_HasStored );
+ sqlite3ComputeGeneratedColumns(pParse, regNew, pTab);
+ }
+#endif
/* Fire any BEFORE UPDATE triggers. This happens before constraints are
** verified. One could argue that this is wrong.
*/
if( tmask&TRIGGER_BEFORE ){
sqlite3TableAffinity(v, pTab, regNew);
- sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
+ sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
TRIGGER_BEFORE, pTab, regOldRowid, onError, labelContinue);
- /* The row-trigger may have deleted the row being updated. In this
- ** case, jump to the next row. No updates or AFTER triggers are
- ** required. This behavior - what happens when the row being updated
- ** is deleted or renamed by a BEFORE trigger - is left undefined in the
- ** documentation.
- */
- if( pPk ){
- sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue,regKey,nKey);
- VdbeCoverage(v);
- }else{
- sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
- VdbeCoverage(v);
- }
+ if( !isView ){
+ /* The row-trigger may have deleted the row being updated. In this
+ ** case, jump to the next row. No updates or AFTER triggers are
+ ** required. This behavior - what happens when the row being updated
+ ** is deleted or renamed by a BEFORE trigger - is left undefined in the
+ ** documentation.
+ */
+ if( pPk ){
+ sqlite3VdbeAddOp4Int(v, OP_NotFound,iDataCur,labelContinue,regKey,nKey);
+ VdbeCoverage(v);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid);
+ VdbeCoverage(v);
+ }
- /* After-BEFORE-trigger-reload-loop:
- ** If it did not delete it, the BEFORE trigger may still have modified
- ** some of the columns of the row being updated. Load the values for
- ** all columns not modified by the update statement into their registers
- ** in case this has happened. Only unmodified columns are reloaded.
- ** The values computed for modified columns use the values before the
- ** BEFORE trigger runs. See test case trigger1-18.0 (added 2018-04-26)
- ** for an example.
- */
- for(i=0; i<pTab->nCol; i++){
- if( aXRef[i]<0 && i!=pTab->iPKey ){
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
+ /* After-BEFORE-trigger-reload-loop:
+ ** If it did not delete it, the BEFORE trigger may still have modified
+ ** some of the columns of the row being updated. Load the values for
+ ** all columns not modified by the update statement into their registers
+ ** in case this has happened. Only unmodified columns are reloaded.
+ ** The values computed for modified columns use the values before the
+ ** BEFORE trigger runs. See test case trigger1-18.0 (added 2018-04-26)
+ ** for an example.
+ */
+ for(i=0, k=regNew; i<pTab->nCol; i++, k++){
+ if( pTab->aCol[i].colFlags & COLFLAG_GENERATED ){
+ if( pTab->aCol[i].colFlags & COLFLAG_VIRTUAL ) k--;
+ }else if( aXRef[i]<0 && i!=pTab->iPKey ){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, k);
+ }
+ }
+#ifndef SQLITE_OMIT_GENERATED_COLUMNS
+ if( pTab->tabFlags & TF_HasGenerated ){
+ testcase( pTab->tabFlags & TF_HasVirtual );
+ testcase( pTab->tabFlags & TF_HasStored );
+ sqlite3ComputeGeneratedColumns(pParse, regNew, pTab);
}
+#endif
}
}
if( !isView ){
- int addr1 = 0; /* Address of jump instruction */
-
/* Do constraint checks. */
assert( regOldRowid>0 );
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace,
aXRef, 0);
+ /* If REPLACE conflict handling may have been used, or if the PK of the
+ ** row is changing, then the GenerateConstraintChecks() above may have
+ ** moved cursor iDataCur. Reseek it. */
+ if( bReplace || chngKey ){
+ if( pPk ){
+ sqlite3VdbeAddOp4Int(v, OP_NotFound,iDataCur,labelContinue,regKey,nKey);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid);
+ }
+ VdbeCoverage(v);
+ }
+
/* Do FK constraint checks. */
if( hasFK ){
sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
}
/* Delete the index entries associated with the current record. */
- if( bReplace || chngKey ){
- if( pPk ){
- addr1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey);
- }else{
- addr1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
- }
- VdbeCoverageNeverTaken(v);
- }
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1);
+ /* We must run the OP_FinishSeek opcode to resolve a prior
+ ** OP_DeferredSeek if there is any possibility that there have been
+ ** no OP_Column opcodes since the OP_DeferredSeek was issued. But
+ ** we want to avoid the OP_FinishSeek if possible, as running it
+ ** costs CPU cycles. */
+ if( bFinishSeek ){
+ sqlite3VdbeAddOp1(v, OP_FinishSeek, iDataCur);
+ }
+
/* If changing the rowid value, or if there are foreign key constraints
** to process, delete the old record. Otherwise, add a noop OP_Delete
** to invoke the pre-update hook.
**
- ** That (regNew==regnewRowid+1) is true is also important for the
+ ** That (regNew==regnewRowid+1) is true is also important for the
** pre-update hook. If the caller invokes preupdate_new(), the returned
** value is copied from memory cell (regNewRowid+1+iCol), where iCol
** is the column index supplied by the user.
@@ -132674,37 +153093,36 @@ SQLITE_PRIVATE void sqlite3Update(
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
}
#endif
- if( bReplace || chngKey ){
- sqlite3VdbeJumpHere(v, addr1);
- }
if( hasFK ){
sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey);
}
-
+
/* Insert the new index entries and the new record. */
sqlite3CompleteInsertion(
- pParse, pTab, iDataCur, iIdxCur, regNewRowid, aRegIdx,
- OPFLAG_ISUPDATE | (eOnePass==ONEPASS_MULTI ? OPFLAG_SAVEPOSITION : 0),
+ pParse, pTab, iDataCur, iIdxCur, regNewRowid, aRegIdx,
+ OPFLAG_ISUPDATE | (eOnePass==ONEPASS_MULTI ? OPFLAG_SAVEPOSITION : 0),
0, 0
);
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
** handle rows (possibly in other tables) that refer via a foreign key
- ** to the row just updated. */
+ ** to the row just updated. */
if( hasFK ){
sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngKey);
}
}
- /* Increment the row counter
+ /* Increment the row counter
*/
if( regRowCount ){
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
}
- sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
- TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue);
+ if( pTrigger ){
+ sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
+ TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue);
+ }
/* Repeat the above with the next record to be updated, until
** all record selected by the WHERE clause have been updated.
@@ -132714,11 +153132,9 @@ SQLITE_PRIVATE void sqlite3Update(
}else if( eOnePass==ONEPASS_MULTI ){
sqlite3VdbeResolveLabel(v, labelContinue);
sqlite3WhereEnd(pWInfo);
- }else if( pPk ){
+ }else{
sqlite3VdbeResolveLabel(v, labelContinue);
sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
- }else{
- sqlite3VdbeGoto(v, labelContinue);
}
sqlite3VdbeResolveLabel(v, labelBreak);
@@ -132735,9 +153151,7 @@ SQLITE_PRIVATE void sqlite3Update(
** that information.
*/
if( regRowCount ){
- sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
+ sqlite3CodeChangeCount(v, regRowCount, "rows updated");
}
update_cleanup:
@@ -132746,7 +153160,7 @@ update_cleanup:
sqlite3SrcListDelete(db, pTabList);
sqlite3ExprListDelete(db, pChanges);
sqlite3ExprDelete(db, pWhere);
-#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
+#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
sqlite3ExprListDelete(db, pOrderBy);
sqlite3ExprDelete(db, pLimit);
#endif
@@ -132766,8 +153180,8 @@ update_cleanup:
/*
** Generate code for an UPDATE of a virtual table.
**
-** There are two possible strategies - the default and the special
-** "onepass" strategy. Onepass is only used if the virtual table
+** There are two possible strategies - the default and the special
+** "onepass" strategy. Onepass is only used if the virtual table
** implementation indicates that pWhere may match at most one row.
**
** The default strategy is to create an ephemeral table that contains
@@ -132799,11 +153213,11 @@ static void updateVirtualTable(
int i; /* Loop counter */
sqlite3 *db = pParse->db; /* Database connection */
const char *pVTab = (const char*)sqlite3GetVTable(db, pTab);
- WhereInfo *pWInfo;
+ WhereInfo *pWInfo = 0;
int nArg = 2 + pTab->nCol; /* Number of arguments to VUpdate */
int regArg; /* First register in VUpdate arg array */
int regRec; /* Register in which to assemble record */
- int regRowid; /* Register for ephem table rowid */
+ int regRowid; /* Register for ephemeral table rowid */
int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */
int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */
int eOnePass; /* True to use onepass strategy */
@@ -132817,73 +153231,119 @@ static void updateVirtualTable(
addr= sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, nArg);
regArg = pParse->nMem + 1;
pParse->nMem += nArg;
- regRec = ++pParse->nMem;
- regRowid = ++pParse->nMem;
+ if( pSrc->nSrc>1 ){
+ Index *pPk = 0;
+ Expr *pRow;
+ ExprList *pList;
+ if( HasRowid(pTab) ){
+ if( pRowid ){
+ pRow = sqlite3ExprDup(db, pRowid, 0);
+ }else{
+ pRow = sqlite3PExpr(pParse, TK_ROW, 0, 0);
+ }
+ }else{
+ i16 iPk; /* PRIMARY KEY column */
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pPk!=0 );
+ assert( pPk->nKeyCol==1 );
+ iPk = pPk->aiColumn[0];
+ if( aXRef[iPk]>=0 ){
+ pRow = sqlite3ExprDup(db, pChanges->a[aXRef[iPk]].pExpr, 0);
+ }else{
+ pRow = exprRowColumn(pParse, iPk);
+ }
+ }
+ pList = sqlite3ExprListAppend(pParse, 0, pRow);
- /* Start scanning the virtual table */
- pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0);
- if( pWInfo==0 ) return;
+ for(i=0; i<pTab->nCol; i++){
+ if( aXRef[i]>=0 ){
+ pList = sqlite3ExprListAppend(pParse, pList,
+ sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0)
+ );
+ }else{
+ Expr *pRowExpr = exprRowColumn(pParse, i);
+ if( pRowExpr ) pRowExpr->op2 = OPFLAG_NOCHNG;
+ pList = sqlite3ExprListAppend(pParse, pList, pRowExpr);
+ }
+ }
- /* Populate the argument registers. */
- for(i=0; i<pTab->nCol; i++){
- if( aXRef[i]>=0 ){
- sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
- }else{
- sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
- sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG);/* Enable sqlite3_vtab_nochange() */
+ updateFromSelect(pParse, ephemTab, pPk, pList, pSrc, pWhere, 0, 0);
+ sqlite3ExprListDelete(db, pList);
+ eOnePass = ONEPASS_OFF;
+ }else{
+ regRec = ++pParse->nMem;
+ regRowid = ++pParse->nMem;
+
+ /* Start scanning the virtual table */
+ pWInfo = sqlite3WhereBegin(
+ pParse, pSrc, pWhere, 0, 0, 0, WHERE_ONEPASS_DESIRED, 0
+ );
+ if( pWInfo==0 ) return;
+
+ /* Populate the argument registers. */
+ for(i=0; i<pTab->nCol; i++){
+ assert( (pTab->aCol[i].colFlags & COLFLAG_GENERATED)==0 );
+ if( aXRef[i]>=0 ){
+ sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
+ sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG);/* For sqlite3_vtab_nochange() */
+ }
}
- }
- if( HasRowid(pTab) ){
- sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
- if( pRowid ){
- sqlite3ExprCode(pParse, pRowid, regArg+1);
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
+ if( pRowid ){
+ sqlite3ExprCode(pParse, pRowid, regArg+1);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
+ }
}else{
- sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
+ Index *pPk; /* PRIMARY KEY index */
+ i16 iPk; /* PRIMARY KEY column */
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pPk!=0 );
+ assert( pPk->nKeyCol==1 );
+ iPk = pPk->aiColumn[0];
+ sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg);
+ sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1);
}
- }else{
- Index *pPk; /* PRIMARY KEY index */
- i16 iPk; /* PRIMARY KEY column */
- pPk = sqlite3PrimaryKeyIndex(pTab);
- assert( pPk!=0 );
- assert( pPk->nKeyCol==1 );
- iPk = pPk->aiColumn[0];
- sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg);
- sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1);
- }
- eOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
+ eOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
- /* There is no ONEPASS_MULTI on virtual tables */
- assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
+ /* There is no ONEPASS_MULTI on virtual tables */
+ assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
- if( eOnePass ){
- /* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
- ** above. */
- sqlite3VdbeChangeToNoop(v, addr);
- sqlite3VdbeAddOp1(v, OP_Close, iCsr);
- }else{
- /* Create a record from the argument register contents and insert it into
- ** the ephemeral table. */
- sqlite3MultiWrite(pParse);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec);
-#ifdef SQLITE_DEBUG
- /* Signal an assert() within OP_MakeRecord that it is allowed to
- ** accept no-change records with serial_type 10 */
- sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC);
+ if( eOnePass ){
+ /* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
+ ** above. */
+ sqlite3VdbeChangeToNoop(v, addr);
+ sqlite3VdbeAddOp1(v, OP_Close, iCsr);
+ }else{
+ /* Create a record from the argument register contents and insert it into
+ ** the ephemeral table. */
+ sqlite3MultiWrite(pParse);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec);
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_NULL_TRIM)
+ /* Signal an assert() within OP_MakeRecord that it is allowed to
+ ** accept no-change records with serial_type 10 */
+ sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC);
#endif
- sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid);
+ }
}
if( eOnePass==ONEPASS_OFF ){
/* End the virtual table scan */
- sqlite3WhereEnd(pWInfo);
+ if( pSrc->nSrc==1 ){
+ sqlite3WhereEnd(pWInfo);
+ }
- /* Begin scannning through the ephemeral table. */
+ /* Begin scanning through the ephemeral table. */
addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v);
- /* Extract arguments from the current row of the ephemeral table and
+ /* Extract arguments from the current row of the ephemeral table and
** invoke the VUpdate method. */
for(i=0; i<nArg; i++){
sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i, regArg+i);
@@ -132928,16 +153388,23 @@ static void updateVirtualTable(
/*
** Free a list of Upsert objects
*/
-SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
- if( p ){
+static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){
+ do{
+ Upsert *pNext = p->pNextUpsert;
sqlite3ExprListDelete(db, p->pUpsertTarget);
sqlite3ExprDelete(db, p->pUpsertTargetWhere);
sqlite3ExprListDelete(db, p->pUpsertSet);
sqlite3ExprDelete(db, p->pUpsertWhere);
+ sqlite3DbFree(db, p->pToFree);
sqlite3DbFree(db, p);
- }
+ p = pNext;
+ }while( p );
+}
+SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
+ if( p ) upsertDelete(db, p);
}
+
/*
** Duplicate an Upsert object.
*/
@@ -132947,7 +153414,8 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){
sqlite3ExprListDup(db, p->pUpsertTarget, 0),
sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),
sqlite3ExprListDup(db, p->pUpsertSet, 0),
- sqlite3ExprDup(db, p->pUpsertWhere, 0)
+ sqlite3ExprDup(db, p->pUpsertWhere, 0),
+ sqlite3UpsertDup(db, p->pNextUpsert)
);
}
@@ -132959,22 +153427,25 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertNew(
ExprList *pTarget, /* Target argument to ON CONFLICT, or NULL */
Expr *pTargetWhere, /* Optional WHERE clause on the target */
ExprList *pSet, /* UPDATE columns, or NULL for a DO NOTHING */
- Expr *pWhere /* WHERE clause for the ON CONFLICT UPDATE */
+ Expr *pWhere, /* WHERE clause for the ON CONFLICT UPDATE */
+ Upsert *pNext /* Next ON CONFLICT clause in the list */
){
Upsert *pNew;
- pNew = sqlite3DbMallocRaw(db, sizeof(Upsert));
+ pNew = sqlite3DbMallocZero(db, sizeof(Upsert));
if( pNew==0 ){
sqlite3ExprListDelete(db, pTarget);
sqlite3ExprDelete(db, pTargetWhere);
sqlite3ExprListDelete(db, pSet);
sqlite3ExprDelete(db, pWhere);
+ sqlite3UpsertDelete(db, pNext);
return 0;
}else{
pNew->pUpsertTarget = pTarget;
pNew->pUpsertTargetWhere = pTargetWhere;
pNew->pUpsertSet = pSet;
pNew->pUpsertWhere = pWhere;
- pNew->pUpsertIdx = 0;
+ pNew->isDoUpdate = pSet!=0;
+ pNew->pNextUpsert = pNext;
}
return pNew;
}
@@ -132999,6 +153470,7 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
Expr *pTerm; /* One term of the conflict-target clause */
NameContext sNC; /* Context for resolving symbolic names */
Expr sCol[2]; /* Index column converted into an Expr */
+ int nClause = 0; /* Counter of ON CONFLICT clauses */
assert( pTabList->nSrc==1 );
assert( pTabList->a[0].pTab!=0 );
@@ -133012,87 +153484,132 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
sNC.pSrcList = pTabList;
- rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
- if( rc ) return rc;
- rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
- if( rc ) return rc;
+ for(; pUpsert && pUpsert->pUpsertTarget;
+ pUpsert=pUpsert->pNextUpsert, nClause++){
+ rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
+ if( rc ) return rc;
+ rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
+ if( rc ) return rc;
- /* Check to see if the conflict target matches the rowid. */
- pTab = pTabList->a[0].pTab;
- pTarget = pUpsert->pUpsertTarget;
- iCursor = pTabList->a[0].iCursor;
- if( HasRowid(pTab)
- && pTarget->nExpr==1
- && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN
- && pTerm->iColumn==XN_ROWID
- ){
- /* The conflict-target is the rowid of the primary table */
- assert( pUpsert->pUpsertIdx==0 );
- return SQLITE_OK;
- }
+ /* Check to see if the conflict target matches the rowid. */
+ pTab = pTabList->a[0].pTab;
+ pTarget = pUpsert->pUpsertTarget;
+ iCursor = pTabList->a[0].iCursor;
+ if( HasRowid(pTab)
+ && pTarget->nExpr==1
+ && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN
+ && pTerm->iColumn==XN_ROWID
+ ){
+ /* The conflict-target is the rowid of the primary table */
+ assert( pUpsert->pUpsertIdx==0 );
+ continue;
+ }
- /* Initialize sCol[0..1] to be an expression parse tree for a
- ** single column of an index. The sCol[0] node will be the TK_COLLATE
- ** operator and sCol[1] will be the TK_COLUMN operator. Code below
- ** will populate the specific collation and column number values
- ** prior to comparing against the conflict-target expression.
- */
- memset(sCol, 0, sizeof(sCol));
- sCol[0].op = TK_COLLATE;
- sCol[0].pLeft = &sCol[1];
- sCol[1].op = TK_COLUMN;
- sCol[1].iTable = pTabList->a[0].iCursor;
+ /* Initialize sCol[0..1] to be an expression parse tree for a
+ ** single column of an index. The sCol[0] node will be the TK_COLLATE
+ ** operator and sCol[1] will be the TK_COLUMN operator. Code below
+ ** will populate the specific collation and column number values
+ ** prior to comparing against the conflict-target expression.
+ */
+ memset(sCol, 0, sizeof(sCol));
+ sCol[0].op = TK_COLLATE;
+ sCol[0].pLeft = &sCol[1];
+ sCol[1].op = TK_COLUMN;
+ sCol[1].iTable = pTabList->a[0].iCursor;
- /* Check for matches against other indexes */
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- int ii, jj, nn;
- if( !IsUniqueIndex(pIdx) ) continue;
- if( pTarget->nExpr!=pIdx->nKeyCol ) continue;
- if( pIdx->pPartIdxWhere ){
- if( pUpsert->pUpsertTargetWhere==0 ) continue;
- if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere,
- pIdx->pPartIdxWhere, iCursor)!=0 ){
- continue;
+ /* Check for matches against other indexes */
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ int ii, jj, nn;
+ if( !IsUniqueIndex(pIdx) ) continue;
+ if( pTarget->nExpr!=pIdx->nKeyCol ) continue;
+ if( pIdx->pPartIdxWhere ){
+ if( pUpsert->pUpsertTargetWhere==0 ) continue;
+ if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere,
+ pIdx->pPartIdxWhere, iCursor)!=0 ){
+ continue;
+ }
}
- }
- nn = pIdx->nKeyCol;
- for(ii=0; ii<nn; ii++){
- Expr *pExpr;
- sCol[0].u.zToken = (char*)pIdx->azColl[ii];
- if( pIdx->aiColumn[ii]==XN_EXPR ){
- assert( pIdx->aColExpr!=0 );
- assert( pIdx->aColExpr->nExpr>ii );
- pExpr = pIdx->aColExpr->a[ii].pExpr;
- if( pExpr->op!=TK_COLLATE ){
- sCol[0].pLeft = pExpr;
+ nn = pIdx->nKeyCol;
+ for(ii=0; ii<nn; ii++){
+ Expr *pExpr;
+ sCol[0].u.zToken = (char*)pIdx->azColl[ii];
+ if( pIdx->aiColumn[ii]==XN_EXPR ){
+ assert( pIdx->aColExpr!=0 );
+ assert( pIdx->aColExpr->nExpr>ii );
+ assert( pIdx->bHasExpr );
+ pExpr = pIdx->aColExpr->a[ii].pExpr;
+ if( pExpr->op!=TK_COLLATE ){
+ sCol[0].pLeft = pExpr;
+ pExpr = &sCol[0];
+ }
+ }else{
+ sCol[0].pLeft = &sCol[1];
+ sCol[1].iColumn = pIdx->aiColumn[ii];
pExpr = &sCol[0];
}
- }else{
- sCol[0].pLeft = &sCol[1];
- sCol[1].iColumn = pIdx->aiColumn[ii];
- pExpr = &sCol[0];
- }
- for(jj=0; jj<nn; jj++){
- if( sqlite3ExprCompare(pParse, pTarget->a[jj].pExpr, pExpr,iCursor)<2 ){
- break; /* Column ii of the index matches column jj of target */
+ for(jj=0; jj<nn; jj++){
+ if( sqlite3ExprCompare(0,pTarget->a[jj].pExpr,pExpr,iCursor)<2 ){
+ break; /* Column ii of the index matches column jj of target */
+ }
+ }
+ if( jj>=nn ){
+ /* The target contains no match for column jj of the index */
+ break;
}
}
- if( jj>=nn ){
- /* The target contains no match for column jj of the index */
- break;
+ if( ii<nn ){
+ /* Column ii of the index did not match any term of the conflict target.
+ ** Continue the search with the next index. */
+ continue;
}
+ pUpsert->pUpsertIdx = pIdx;
+ break;
}
- if( ii<nn ){
- /* Column ii of the index did not match any term of the conflict target.
- ** Continue the search with the next index. */
- continue;
+ if( pUpsert->pUpsertIdx==0 ){
+ char zWhich[16];
+ if( nClause==0 && pUpsert->pNextUpsert==0 ){
+ zWhich[0] = 0;
+ }else{
+ sqlite3_snprintf(sizeof(zWhich),zWhich,"%r ", nClause+1);
+ }
+ sqlite3ErrorMsg(pParse, "%sON CONFLICT clause does not match any "
+ "PRIMARY KEY or UNIQUE constraint", zWhich);
+ return SQLITE_ERROR;
}
- pUpsert->pUpsertIdx = pIdx;
- return SQLITE_OK;
}
- sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any "
- "PRIMARY KEY or UNIQUE constraint");
- return SQLITE_ERROR;
+ return SQLITE_OK;
+}
+
+/*
+** Return true if pUpsert is the last ON CONFLICT clause with a
+** conflict target, or if pUpsert is followed by another ON CONFLICT
+** clause that targets the INTEGER PRIMARY KEY.
+*/
+SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert *pUpsert){
+ Upsert *pNext;
+ if( NEVER(pUpsert==0) ) return 0;
+ pNext = pUpsert->pNextUpsert;
+ if( pNext==0 ) return 1;
+ if( pNext->pUpsertTarget==0 ) return 1;
+ if( pNext->pUpsertIdx==0 ) return 1;
+ return 0;
+}
+
+/*
+** Given the list of ON CONFLICT clauses described by pUpsert, and
+** a particular index pIdx, return a pointer to the particular ON CONFLICT
+** clause that applies to the index. Or, if the index is not subject to
+** any ON CONFLICT clause, return NULL.
+*/
+SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert *pUpsert, Index *pIdx){
+ while(
+ pUpsert
+ && pUpsert->pUpsertTarget!=0
+ && pUpsert->pUpsertIdx!=pIdx
+ ){
+ pUpsert = pUpsert->pNextUpsert;
+ }
+ return pUpsert;
}
/*
@@ -133115,11 +153632,14 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate(
sqlite3 *db = pParse->db;
SrcList *pSrc; /* FROM clause for the UPDATE */
int iDataCur;
+ int i;
+ Upsert *pTop = pUpsert;
assert( v!=0 );
assert( pUpsert!=0 );
- VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
iDataCur = pUpsert->iDataCur;
+ pUpsert = sqlite3UpsertOfIndex(pTop, pIdx);
+ VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
if( pIdx && iCur!=iDataCur ){
if( HasRowid(pTab) ){
int regRowid = sqlite3GetTempReg(pParse);
@@ -133131,31 +153651,35 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate(
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
int nPk = pPk->nKeyCol;
int iPk = pParse->nMem+1;
- int i;
pParse->nMem += nPk;
for(i=0; i<nPk; i++){
int k;
assert( pPk->aiColumn[i]>=0 );
- k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]);
+ k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[i]);
sqlite3VdbeAddOp3(v, OP_Column, iCur, k, iPk+i);
VdbeComment((v, "%s.%s", pIdx->zName,
- pTab->aCol[pPk->aiColumn[i]].zName));
+ pTab->aCol[pPk->aiColumn[i]].zCnName));
}
sqlite3VdbeVerifyAbortable(v, OE_Abort);
i = sqlite3VdbeAddOp4Int(v, OP_Found, iDataCur, 0, iPk, nPk);
VdbeCoverage(v);
- sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CORRUPT, OE_Abort, 0,
+ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CORRUPT, OE_Abort, 0,
"corrupt database", P4_STATIC);
+ sqlite3MayAbort(pParse);
sqlite3VdbeJumpHere(v, i);
}
}
- /* pUpsert does not own pUpsertSrc - the outer INSERT statement does. So
- ** we have to make a copy before passing it down into sqlite3Update() */
- pSrc = sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0);
- sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet,
- pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert);
- pUpsert->pUpsertSet = 0; /* Will have been deleted by sqlite3Update() */
- pUpsert->pUpsertWhere = 0; /* Will have been deleted by sqlite3Update() */
+ /* pUpsert does not own pTop->pUpsertSrc - the outer INSERT statement does.
+ ** So we have to make a copy before passing it down into sqlite3Update() */
+ pSrc = sqlite3SrcListDup(db, pTop->pUpsertSrc, 0);
+ /* excluded.* columns of type REAL need to be converted to a hard real */
+ for(i=0; i<pTab->nCol; i++){
+ if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
+ sqlite3VdbeAddOp1(v, OP_RealAffinity, pTop->regData+i);
+ }
+ }
+ sqlite3Update(pParse, pSrc, sqlite3ExprListDup(db,pUpsert->pUpsertSet,0),
+ sqlite3ExprDup(db,pUpsert->pUpsertWhere,0), OE_Abort, 0, 0, pUpsert);
VdbeNoopComment((v, "End DO UPDATE of UPSERT"));
}
@@ -133206,7 +153730,7 @@ static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 );
/* The secondary SQL must be one of CREATE TABLE, CREATE INDEX,
** or INSERT. Historically there have been attacks that first
- ** corrupt the sqlite_master.sql field with other kinds of statements
+ ** corrupt the sqlite_schema.sql field with other kinds of statements
** then run VACUUM to get those statements to execute at inappropriate
** times. */
if( zSubSql
@@ -133267,16 +153791,17 @@ static int execSqlF(sqlite3 *db, char **pzErrMsg, const char *zSql, ...){
** transient would cause the database file to appear to be deleted
** following reboot.
*/
-SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm){
+SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm, Expr *pInto){
Vdbe *v = sqlite3GetVdbe(pParse);
int iDb = 0;
- if( v==0 ) return;
+ if( v==0 ) goto build_vacuum_end;
+ if( pParse->nErr ) goto build_vacuum_end;
if( pNm ){
#ifndef SQLITE_BUG_COMPATIBLE_20160819
/* Default behavior: Report an error if the argument to VACUUM is
** not recognized */
iDb = sqlite3TwoPartName(pParse, pNm, pNm, &pNm);
- if( iDb<0 ) return;
+ if( iDb<0 ) goto build_vacuum_end;
#else
/* When SQLITE_BUG_COMPATIBLE_20160819 is defined, unrecognized arguments
** to VACUUM are silently ignored. This is a back-out of a bug fix that
@@ -133288,40 +153813,67 @@ SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm){
#endif
}
if( iDb!=1 ){
- sqlite3VdbeAddOp1(v, OP_Vacuum, iDb);
+ int iIntoReg = 0;
+ if( pInto && sqlite3ResolveSelfReference(pParse,0,0,pInto,0)==0 ){
+ iIntoReg = ++pParse->nMem;
+ sqlite3ExprCode(pParse, pInto, iIntoReg);
+ }
+ sqlite3VdbeAddOp2(v, OP_Vacuum, iDb, iIntoReg);
sqlite3VdbeUsesBtree(v, iDb);
}
+build_vacuum_end:
+ sqlite3ExprDelete(pParse->db, pInto);
return;
}
/*
** This routine implements the OP_Vacuum opcode of the VDBE.
*/
-SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
+SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum(
+ char **pzErrMsg, /* Write error message here */
+ sqlite3 *db, /* Database connection */
+ int iDb, /* Which attached DB to vacuum */
+ sqlite3_value *pOut /* Write results here, if not NULL. VACUUM INTO */
+){
int rc = SQLITE_OK; /* Return code from service routines */
Btree *pMain; /* The database being vacuumed */
Btree *pTemp; /* The temporary database we vacuum into */
- u16 saved_mDbFlags; /* Saved value of db->mDbFlags */
- u32 saved_flags; /* Saved value of db->flags */
- int saved_nChange; /* Saved value of db->nChange */
- int saved_nTotalChange; /* Saved value of db->nTotalChange */
+ u32 saved_mDbFlags; /* Saved value of db->mDbFlags */
+ u64 saved_flags; /* Saved value of db->flags */
+ i64 saved_nChange; /* Saved value of db->nChange */
+ i64 saved_nTotalChange; /* Saved value of db->nTotalChange */
+ u32 saved_openFlags; /* Saved value of db->openFlags */
u8 saved_mTrace; /* Saved trace settings */
Db *pDb = 0; /* Database to detach at end of vacuum */
int isMemDb; /* True if vacuuming a :memory: database */
int nRes; /* Bytes of reserved space at the end of each page */
int nDb; /* Number of attached databases */
const char *zDbMain; /* Schema name of database to vacuum */
+ const char *zOut; /* Name of output file */
+ u32 pgflags = PAGER_SYNCHRONOUS_OFF; /* sync flags for output db */
if( !db->autoCommit ){
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
- return SQLITE_ERROR;
+ return SQLITE_ERROR; /* IMP: R-12218-18073 */
}
if( db->nVdbeActive>1 ){
sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
- return SQLITE_ERROR;
+ return SQLITE_ERROR; /* IMP: R-15610-35227 */
+ }
+ saved_openFlags = db->openFlags;
+ if( pOut ){
+ if( sqlite3_value_type(pOut)!=SQLITE_TEXT ){
+ sqlite3SetString(pzErrMsg, db, "non-text filename");
+ return SQLITE_ERROR;
+ }
+ zOut = (const char*)sqlite3_value_text(pOut);
+ db->openFlags &= ~SQLITE_OPEN_READONLY;
+ db->openFlags |= SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE;
+ }else{
+ zOut = "";
}
- /* Save the current value of the database flags so that it can be
+ /* Save the current value of the database flags so that it can be
** restored before returning. Then set the writable-schema flag, and
** disable CHECK and foreign key constraints. */
saved_flags = db->flags;
@@ -133331,7 +153883,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
saved_mTrace = db->mTrace;
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
- db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder
+ db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
| SQLITE_Defensive | SQLITE_CountRows);
db->mTrace = 0;
@@ -133345,7 +153897,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
** (possibly synchronous) transaction opened on the main database before
** sqlite3BtreeCopyFile() is called.
**
- ** An optimisation would be to use a non-journaled pager.
+ ** An optimization would be to use a non-journaled pager.
** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but
** that actually made the VACUUM run slower. Very little journalling
** actually occurs when doing a vacuum since the vacuum_db is initially
@@ -133354,35 +153906,33 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
** to write the journal header file.
*/
nDb = db->nDb;
- rc = execSql(db, pzErrMsg, "ATTACH''AS vacuum_db");
+ rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut);
+ db->openFlags = saved_openFlags;
if( rc!=SQLITE_OK ) goto end_of_vacuum;
assert( (db->nDb-1)==nDb );
pDb = &db->aDb[nDb];
assert( strcmp(pDb->zDbSName,"vacuum_db")==0 );
pTemp = pDb->pBt;
+ if( pOut ){
+ sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp));
+ i64 sz = 0;
+ if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){
+ rc = SQLITE_ERROR;
+ sqlite3SetString(pzErrMsg, db, "output file already exists");
+ goto end_of_vacuum;
+ }
+ db->mDbFlags |= DBFLAG_VacuumInto;
- /* The call to execSql() to attach the temp database has left the file
- ** locked (as there was more than one active statement when the transaction
- ** to read the schema was concluded. Unlock it here so that this doesn't
- ** cause problems for the call to BtreeSetPageSize() below. */
- sqlite3BtreeCommit(pTemp);
-
- nRes = sqlite3BtreeGetOptimalReserve(pMain);
-
- /* A VACUUM cannot change the pagesize of an encrypted database. */
-#ifdef SQLITE_HAS_CODEC
- if( db->nextPagesize ){
- extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*);
- int nKey;
- char *zKey;
- sqlite3CodecGetKey(db, iDb, (void**)&zKey, &nKey);
- if( nKey ) db->nextPagesize = 0;
+ /* For a VACUUM INTO, the pager-flags are set to the same values as
+ ** they are for the database being vacuumed, except that PAGER_CACHESPILL
+ ** is always set. */
+ pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK);
}
-#endif
+ nRes = sqlite3BtreeGetRequestedReserve(pMain);
sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
- sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL);
+ sqlite3BtreeSetPagerFlags(pTemp, pgflags|PAGER_CACHESPILL);
/* Begin a transaction and take an exclusive lock on the main database
** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below,
@@ -133390,12 +153940,14 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
*/
rc = execSql(db, pzErrMsg, "BEGIN");
if( rc!=SQLITE_OK ) goto end_of_vacuum;
- rc = sqlite3BtreeBeginTrans(pMain, 2, 0);
+ rc = sqlite3BtreeBeginTrans(pMain, pOut==0 ? 2 : 0, 0);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
/* Do not attempt to change the page size for a WAL database */
if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain))
- ==PAGER_JOURNALMODE_WAL ){
+ ==PAGER_JOURNALMODE_WAL
+ && pOut==0
+ ){
db->nextPagesize = 0;
}
@@ -133417,14 +153969,14 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
*/
db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */
rc = execSqlF(db, pzErrMsg,
- "SELECT sql FROM \"%w\".sqlite_master"
+ "SELECT sql FROM \"%w\".sqlite_schema"
" WHERE type='table'AND name<>'sqlite_sequence'"
" AND coalesce(rootpage,1)>0",
zDbMain
);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = execSqlF(db, pzErrMsg,
- "SELECT sql FROM \"%w\".sqlite_master"
+ "SELECT sql FROM \"%w\".sqlite_schema"
" WHERE type='index'",
zDbMain
);
@@ -133438,7 +153990,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
rc = execSqlF(db, pzErrMsg,
"SELECT'INSERT INTO vacuum_db.'||quote(name)"
"||' SELECT*FROM\"%w\".'||quote(name)"
- "FROM vacuum_db.sqlite_master "
+ "FROM vacuum_db.sqlite_schema "
"WHERE type='table'AND coalesce(rootpage,1)>0",
zDbMain
);
@@ -133449,18 +154001,18 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
/* Copy the triggers, views, and virtual tables from the main database
** over to the temporary database. None of these objects has any
** associated storage, so all we have to do is copy their entries
- ** from the SQLITE_MASTER table.
+ ** from the schema table.
*/
rc = execSqlF(db, pzErrMsg,
- "INSERT INTO vacuum_db.sqlite_master"
- " SELECT*FROM \"%w\".sqlite_master"
+ "INSERT INTO vacuum_db.sqlite_schema"
+ " SELECT*FROM \"%w\".sqlite_schema"
" WHERE type IN('view','trigger')"
" OR(type='table'AND rootpage=0)",
zDbMain
);
if( rc ) goto end_of_vacuum;
- /* At this point, there is a write transaction open on both the
+ /* At this point, there is a write transaction open on both the
** vacuum database and the main database. Assuming no error occurs,
** both transactions are closed by this block - the main database
** transaction by sqlite3BtreeCopyFile() and the other by an explicit
@@ -133484,8 +154036,8 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
BTREE_APPLICATION_ID, 0, /* Preserve the application id */
};
- assert( 1==sqlite3BtreeIsInTrans(pTemp) );
- assert( 1==sqlite3BtreeIsInTrans(pMain) );
+ assert( SQLITE_TXN_WRITE==sqlite3BtreeTxnState(pTemp) );
+ assert( pOut!=0 || SQLITE_TXN_WRITE==sqlite3BtreeTxnState(pMain) );
/* Copy Btree meta values */
for(i=0; i<ArraySize(aCopy); i+=2){
@@ -133496,17 +154048,24 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
if( NEVER(rc!=SQLITE_OK) ) goto end_of_vacuum;
}
- rc = sqlite3BtreeCopyFile(pMain, pTemp);
+ if( pOut==0 ){
+ rc = sqlite3BtreeCopyFile(pMain, pTemp);
+ }
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = sqlite3BtreeCommit(pTemp);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
#ifndef SQLITE_OMIT_AUTOVACUUM
- sqlite3BtreeSetAutoVacuum(pMain, sqlite3BtreeGetAutoVacuum(pTemp));
+ if( pOut==0 ){
+ sqlite3BtreeSetAutoVacuum(pMain, sqlite3BtreeGetAutoVacuum(pTemp));
+ }
#endif
}
assert( rc==SQLITE_OK );
- rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1);
+ if( pOut==0 ){
+ nRes = sqlite3BtreeGetRequestedReserve(pTemp);
+ rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1);
+ }
end_of_vacuum:
/* Restore the original value of db->flags */
@@ -133516,7 +154075,7 @@ end_of_vacuum:
db->nChange = saved_nChange;
db->nTotalChange = saved_nTotalChange;
db->mTrace = saved_mTrace;
- sqlite3BtreeSetPageSize(pMain, -1, -1, 1);
+ sqlite3BtreeSetPageSize(pMain, -1, 0, 1);
/* Currently there is an SQL level transaction open on the vacuum
** database. No locks are held on any other files (since the main file
@@ -133534,7 +154093,7 @@ end_of_vacuum:
}
/* This both clears the schemas and reduces the size of the db->aDb[]
- ** array. */
+ ** array. */
sqlite3ResetAllSchemasOfConnection(db);
return rc;
@@ -133563,7 +154122,7 @@ end_of_vacuum:
/*
** Before a virtual table xCreate() or xConnect() method is invoked, the
** sqlite3.pVtabCtx member variable is set to point to an instance of
-** this struct allocated on the stack. It is used by the implementation of
+** this struct allocated on the stack. It is used by the implementation of
** the sqlite3_declare_vtab() and sqlite3_vtab_config() APIs, both of which
** are invoked only from within xCreate and xConnect methods.
*/
@@ -133578,6 +154137,9 @@ struct VtabCtx {
** Construct and install a Module object for a virtual table. When this
** routine is called, it is guaranteed that all appropriate locks are held
** and the module is not already part of the connection.
+**
+** If there already exists a module with zName, replace it with the new one.
+** If pModule==0, then delete the module zName if it exists.
*/
SQLITE_PRIVATE Module *sqlite3VtabCreateModule(
sqlite3 *db, /* Database in which module is registered */
@@ -133587,25 +154149,36 @@ SQLITE_PRIVATE Module *sqlite3VtabCreateModule(
void (*xDestroy)(void *) /* Module destructor function */
){
Module *pMod;
- int nName = sqlite3Strlen30(zName);
- pMod = (Module *)sqlite3Malloc(sizeof(Module) + nName + 1);
- if( pMod==0 ){
- sqlite3OomFault(db);
+ Module *pDel;
+ char *zCopy;
+ if( pModule==0 ){
+ zCopy = (char*)zName;
+ pMod = 0;
}else{
- Module *pDel;
- char *zCopy = (char *)(&pMod[1]);
+ int nName = sqlite3Strlen30(zName);
+ pMod = (Module *)sqlite3Malloc(sizeof(Module) + nName + 1);
+ if( pMod==0 ){
+ sqlite3OomFault(db);
+ return 0;
+ }
+ zCopy = (char *)(&pMod[1]);
memcpy(zCopy, zName, nName+1);
pMod->zName = zCopy;
pMod->pModule = pModule;
pMod->pAux = pAux;
pMod->xDestroy = xDestroy;
pMod->pEpoTab = 0;
- pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod);
- assert( pDel==0 || pDel==pMod );
- if( pDel ){
+ pMod->nRefModule = 1;
+ }
+ pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod);
+ if( pDel ){
+ if( pDel==pMod ){
sqlite3OomFault(db);
sqlite3DbFree(db, pDel);
pMod = 0;
+ }else{
+ sqlite3VtabEponymousTableClear(db, pDel);
+ sqlite3VtabModuleUnref(db, pDel);
}
}
return pMod;
@@ -133626,11 +154199,7 @@ static int createModule(
int rc = SQLITE_OK;
sqlite3_mutex_enter(db->mutex);
- if( sqlite3HashFind(&db->aModule, zName) ){
- rc = SQLITE_MISUSE_BKPT;
- }else{
- (void)sqlite3VtabCreateModule(db, zName, pModule, pAux, xDestroy);
- }
+ (void)sqlite3VtabCreateModule(db, zName, pModule, pAux, xDestroy);
rc = sqlite3ApiExit(db, rc);
if( rc!=SQLITE_OK && xDestroy ) xDestroy(pAux);
sqlite3_mutex_leave(db->mutex);
@@ -133670,9 +154239,47 @@ SQLITE_API int sqlite3_create_module_v2(
}
/*
+** External API to drop all virtual-table modules, except those named
+** on the azNames list.
+*/
+SQLITE_API int sqlite3_drop_modules(sqlite3 *db, const char** azNames){
+ HashElem *pThis, *pNext;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
+ for(pThis=sqliteHashFirst(&db->aModule); pThis; pThis=pNext){
+ Module *pMod = (Module*)sqliteHashData(pThis);
+ pNext = sqliteHashNext(pThis);
+ if( azNames ){
+ int ii;
+ for(ii=0; azNames[ii]!=0 && strcmp(azNames[ii],pMod->zName)!=0; ii++){}
+ if( azNames[ii]!=0 ) continue;
+ }
+ createModule(db, pMod->zName, 0, 0, 0);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Decrement the reference count on a Module object. Destroy the
+** module when the reference count reaches zero.
+*/
+SQLITE_PRIVATE void sqlite3VtabModuleUnref(sqlite3 *db, Module *pMod){
+ assert( pMod->nRefModule>0 );
+ pMod->nRefModule--;
+ if( pMod->nRefModule==0 ){
+ if( pMod->xDestroy ){
+ pMod->xDestroy(pMod->pAux);
+ }
+ assert( pMod->pEpoTab==0 );
+ sqlite3DbFree(db, pMod);
+ }
+}
+
+/*
** Lock the virtual table so that it cannot be disconnected.
** Locks nest. Every lock should have a corresponding unlock.
-** If an unlock is omitted, resources leaks will occur.
+** If an unlock is omitted, resources leaks will occur.
**
** If a disconnect is attempted while a virtual table is locked,
** the disconnect is deferred until all locks have been removed.
@@ -133684,13 +154291,13 @@ SQLITE_PRIVATE void sqlite3VtabLock(VTable *pVTab){
/*
** pTab is a pointer to a Table structure representing a virtual-table.
-** Return a pointer to the VTable object used by connection db to access
+** Return a pointer to the VTable object used by connection db to access
** this virtual-table, if one has been created, or NULL otherwise.
*/
SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){
VTable *pVtab;
assert( IsVirtual(pTab) );
- for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext);
+ for(pVtab=pTab->u.vtab.p; pVtab && pVtab->db!=db; pVtab=pVtab->pNext);
return pVtab;
}
@@ -133703,7 +154310,8 @@ SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){
assert( db );
assert( pVTab->nRef>0 );
- assert( db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ZOMBIE );
+ assert( db->eOpenState==SQLITE_STATE_OPEN
+ || db->eOpenState==SQLITE_STATE_ZOMBIE );
pVTab->nRef--;
if( pVTab->nRef==0 ){
@@ -133711,27 +154319,31 @@ SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){
if( p ){
p->pModule->xDisconnect(p);
}
+ sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod);
sqlite3DbFree(db, pVTab);
}
}
/*
** Table p is a virtual table. This function moves all elements in the
-** p->pVTable list to the sqlite3.pDisconnect lists of their associated
-** database connections to be disconnected at the next opportunity.
+** p->u.vtab.p list to the sqlite3.pDisconnect lists of their associated
+** database connections to be disconnected at the next opportunity.
** Except, if argument db is not NULL, then the entry associated with
-** connection db is left in the p->pVTable list.
+** connection db is left in the p->u.vtab.p list.
*/
static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){
VTable *pRet = 0;
- VTable *pVTable = p->pVTable;
- p->pVTable = 0;
+ VTable *pVTable;
+
+ assert( IsVirtual(p) );
+ pVTable = p->u.vtab.p;
+ p->u.vtab.p = 0;
- /* Assert that the mutex (if any) associated with the BtShared database
- ** that contains table p is held by the caller. See header comments
+ /* Assert that the mutex (if any) associated with the BtShared database
+ ** that contains table p is held by the caller. See header comments
** above function sqlite3VtabUnlockList() for an explanation of why
** this makes it safe to access the sqlite3.pDisconnect list of any
- ** database connection that may have an entry in the p->pVTable list.
+ ** database connection that may have an entry in the p->u.vtab.p list.
*/
assert( db==0 || sqlite3SchemaMutexHeld(db, 0, p->pSchema) );
@@ -133741,7 +154353,7 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){
assert( db2 );
if( db2==db ){
pRet = pVTable;
- p->pVTable = pRet;
+ p->u.vtab.p = pRet;
pRet->pNext = 0;
}else{
pVTable->pNext = db2->pDisconnect;
@@ -133769,7 +154381,7 @@ SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p){
assert( sqlite3BtreeHoldsAllMutexes(db) );
assert( sqlite3_mutex_held(db->mutex) );
- for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){
+ for(ppVTab=&p->u.vtab.p; *ppVTab; ppVTab=&(*ppVTab)->pNext){
if( (*ppVTab)->db==db ){
VTable *pVTab = *ppVTab;
*ppVTab = pVTab->pNext;
@@ -133784,7 +154396,7 @@ SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p){
** Disconnect all the virtual table objects in the sqlite3.pDisconnect list.
**
** This function may only be called when the mutexes associated with all
-** shared b-tree databases opened using connection db are held by the
+** shared b-tree databases opened using connection db are held by the
** caller. This is done to protect the sqlite3.pDisconnect list. The
** sqlite3.pDisconnect list is accessed only as follows:
**
@@ -133797,18 +154409,17 @@ SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p){
** or, if the virtual table is stored in a non-sharable database, then
** the database handle mutex is held.
**
-** As a result, a sqlite3.pDisconnect cannot be accessed simultaneously
+** As a result, a sqlite3.pDisconnect cannot be accessed simultaneously
** by multiple threads. It is thread-safe.
*/
SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){
VTable *p = db->pDisconnect;
- db->pDisconnect = 0;
assert( sqlite3BtreeHoldsAllMutexes(db) );
assert( sqlite3_mutex_held(db->mutex) );
if( p ){
- sqlite3ExpirePreparedStatements(db, 0);
+ db->pDisconnect = 0;
do {
VTable *pNext = p->pNext;
sqlite3VtabUnlock(p);
@@ -133823,42 +154434,51 @@ SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){
** record.
**
** Since it is a virtual-table, the Table structure contains a pointer
-** to the head of a linked list of VTable structures. Each VTable
+** to the head of a linked list of VTable structures. Each VTable
** structure is associated with a single sqlite3* user of the schema.
-** The reference count of the VTable structure associated with database
-** connection db is decremented immediately (which may lead to the
+** The reference count of the VTable structure associated with database
+** connection db is decremented immediately (which may lead to the
** structure being xDisconnected and free). Any other VTable structures
-** in the list are moved to the sqlite3.pDisconnect list of the associated
+** in the list are moved to the sqlite3.pDisconnect list of the associated
** database connection.
*/
SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){
- if( !db || db->pnBytesFreed==0 ) vtabDisconnectAll(0, p);
- if( p->azModuleArg ){
+ assert( IsVirtual(p) );
+ assert( db!=0 );
+ if( db->pnBytesFreed==0 ) vtabDisconnectAll(0, p);
+ if( p->u.vtab.azArg ){
int i;
- for(i=0; i<p->nModuleArg; i++){
- if( i!=1 ) sqlite3DbFree(db, p->azModuleArg[i]);
+ for(i=0; i<p->u.vtab.nArg; i++){
+ if( i!=1 ) sqlite3DbFree(db, p->u.vtab.azArg[i]);
}
- sqlite3DbFree(db, p->azModuleArg);
+ sqlite3DbFree(db, p->u.vtab.azArg);
}
}
/*
-** Add a new module argument to pTable->azModuleArg[].
+** Add a new module argument to pTable->u.vtab.azArg[].
** The string is not copied - the pointer is stored. The
** string will be freed automatically when the table is
** deleted.
*/
-static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){
- int nBytes = sizeof(char *)*(2+pTable->nModuleArg);
+static void addModuleArgument(Parse *pParse, Table *pTable, char *zArg){
+ sqlite3_int64 nBytes;
char **azModuleArg;
- azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes);
+ sqlite3 *db = pParse->db;
+
+ assert( IsVirtual(pTable) );
+ nBytes = sizeof(char *)*(2+pTable->u.vtab.nArg);
+ if( pTable->u.vtab.nArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){
+ sqlite3ErrorMsg(pParse, "too many columns on %s", pTable->zName);
+ }
+ azModuleArg = sqlite3DbRealloc(db, pTable->u.vtab.azArg, nBytes);
if( azModuleArg==0 ){
sqlite3DbFree(db, zArg);
}else{
- int i = pTable->nModuleArg++;
+ int i = pTable->u.vtab.nArg++;
azModuleArg[i] = zArg;
azModuleArg[i+1] = 0;
- pTable->azModuleArg = azModuleArg;
+ pTable->u.vtab.azArg = azModuleArg;
}
}
@@ -133881,13 +154501,14 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse(
pTable = pParse->pNewTable;
if( pTable==0 ) return;
assert( 0==pTable->pIndex );
+ pTable->eTabType = TABTYP_VTAB;
db = pParse->db;
- assert( pTable->nModuleArg==0 );
- addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName));
- addModuleArgument(db, pTable, 0);
- addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName));
+ assert( pTable->u.vtab.nArg==0 );
+ addModuleArgument(pParse, pTable, sqlite3NameFromToken(db, pModuleName));
+ addModuleArgument(pParse, pTable, 0);
+ addModuleArgument(pParse, pTable, sqlite3DbStrDup(db, pTable->zName));
assert( (pParse->sNameToken.z==pName2->z && pName2->z!=0)
|| (pParse->sNameToken.z==pName1->z && pName2->z==0)
);
@@ -133898,14 +154519,14 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse(
#ifndef SQLITE_OMIT_AUTHORIZATION
/* Creating a virtual table invokes the authorization callback twice.
** The first invocation, to obtain permission to INSERT a row into the
- ** sqlite_master table, has already been made by sqlite3StartTable().
+ ** sqlite_schema table, has already been made by sqlite3StartTable().
** The second call, to obtain permission to create the table, is made now.
*/
- if( pTable->azModuleArg ){
+ if( pTable->u.vtab.azArg ){
int iDb = sqlite3SchemaToIndex(db, pTable->pSchema);
assert( iDb>=0 ); /* The database the table is being created in */
- sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName,
- pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName);
+ sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName,
+ pTable->u.vtab.azArg[0], pParse->db->aDb[iDb].zDbSName);
}
#endif
}
@@ -133920,7 +154541,7 @@ static void addArgumentToVtab(Parse *pParse){
const char *z = (const char*)pParse->sArg.z;
int n = pParse->sArg.n;
sqlite3 *db = pParse->db;
- addModuleArgument(db, pParse->pNewTable, sqlite3DbStrNDup(db, z, n));
+ addModuleArgument(pParse, pParse->pNewTable, sqlite3DbStrNDup(db, z, n));
}
}
@@ -133933,15 +154554,16 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
sqlite3 *db = pParse->db; /* The database connection */
if( pTab==0 ) return;
+ assert( IsVirtual(pTab) );
addArgumentToVtab(pParse);
pParse->sArg.z = 0;
- if( pTab->nModuleArg<1 ) return;
-
+ if( pTab->u.vtab.nArg<1 ) return;
+
/* If the CREATE VIRTUAL TABLE statement is being entered for the
** first time (in other words if the virtual table is actually being
- ** created now instead of just being read out of sqlite_master) then
+ ** created now instead of just being read out of sqlite_schema) then
** do additional initialization work and store the statement text
- ** in the sqlite_master table.
+ ** in the sqlite_schema table.
*/
if( !db->init.busy ){
char *zStmt;
@@ -133950,54 +154572,52 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
int iReg;
Vdbe *v;
+ sqlite3MayAbort(pParse);
+
/* Compute the complete text of the CREATE VIRTUAL TABLE statement */
if( pEnd ){
pParse->sNameToken.n = (int)(pEnd->z - pParse->sNameToken.z) + pEnd->n;
}
zStmt = sqlite3MPrintf(db, "CREATE VIRTUAL TABLE %T", &pParse->sNameToken);
- /* A slot for the record has already been allocated in the
- ** SQLITE_MASTER table. We just need to update that slot with all
- ** the information we've collected.
+ /* A slot for the record has already been allocated in the
+ ** schema table. We just need to update that slot with all
+ ** the information we've collected.
**
** The VM register number pParse->regRowid holds the rowid of an
- ** entry in the sqlite_master table tht was created for this vtab
+ ** entry in the sqlite_schema table that was created for this vtab
** by sqlite3StartTable().
*/
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
sqlite3NestedParse(pParse,
- "UPDATE %Q.%s "
+ "UPDATE %Q." LEGACY_SCHEMA_TABLE " "
"SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q "
"WHERE rowid=#%d",
- db->aDb[iDb].zDbSName, MASTER_NAME,
+ db->aDb[iDb].zDbSName,
pTab->zName,
pTab->zName,
zStmt,
pParse->regRowid
);
- sqlite3DbFree(db, zStmt);
v = sqlite3GetVdbe(pParse);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp0(v, OP_Expire);
- zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
- sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
+ zWhere = sqlite3MPrintf(db, "name=%Q AND sql=%Q", pTab->zName, zStmt);
+ sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere, 0);
+ sqlite3DbFree(db, zStmt);
iReg = ++pParse->nMem;
sqlite3VdbeLoadString(v, iReg, pTab->zName);
sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg);
- }
-
- /* If we are rereading the sqlite_master table create the in-memory
- ** record of the table. The xConnect() method is not called until
- ** the first time the virtual table is used in an SQL statement. This
- ** allows a schema that contains virtual tables to be loaded before
- ** the required virtual table implementations are registered. */
- else {
+ }else{
+ /* If we are rereading the sqlite_schema table create the in-memory
+ ** record of the table. */
Table *pOld;
Schema *pSchema = pTab->pSchema;
const char *zName = pTab->zName;
- assert( sqlite3SchemaMutexHeld(db, 0, pSchema) );
+ assert( zName!=0 );
+ sqlite3MarkAllShadowTablesOf(db, pTab);
pOld = sqlite3HashInsert(&pSchema->tblHash, zName, pTab);
if( pOld ){
sqlite3OomFault(db);
@@ -134039,7 +154659,7 @@ SQLITE_PRIVATE void sqlite3VtabArgExtend(Parse *pParse, Token *p){
** to this procedure.
*/
static int vtabCallConstructor(
- sqlite3 *db,
+ sqlite3 *db,
Table *pTab,
Module *pMod,
int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**),
@@ -134048,17 +154668,20 @@ static int vtabCallConstructor(
VtabCtx sCtx;
VTable *pVTable;
int rc;
- const char *const*azArg = (const char *const*)pTab->azModuleArg;
- int nArg = pTab->nModuleArg;
+ const char *const*azArg;
+ int nArg = pTab->u.vtab.nArg;
char *zErr = 0;
char *zModuleName;
int iDb;
VtabCtx *pCtx;
+ assert( IsVirtual(pTab) );
+ azArg = (const char *const*)pTab->u.vtab.azArg;
+
/* Check that the virtual-table is not already being initialized */
for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){
if( pCtx->pTab==pTab ){
- *pzErr = sqlite3MPrintf(db,
+ *pzErr = sqlite3MPrintf(db,
"vtable constructor called recursively: %s", pTab->zName
);
return SQLITE_LOCKED;
@@ -134078,9 +154701,10 @@ static int vtabCallConstructor(
}
pVTable->db = db;
pVTable->pMod = pMod;
+ pVTable->eVtabRisk = SQLITE_VTABRISK_Normal;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
- pTab->azModuleArg[1] = db->aDb[iDb].zDbSName;
+ pTab->u.vtab.azArg[1] = db->aDb[iDb].zDbSName;
/* Invoke the virtual table constructor */
assert( &db->pVtabCtx );
@@ -134090,7 +154714,9 @@ static int vtabCallConstructor(
sCtx.pPrior = db->pVtabCtx;
sCtx.bDeclared = 0;
db->pVtabCtx = &sCtx;
+ pTab->nTabRef++;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
+ sqlite3DeleteTable(db, pTab);
db->pVtabCtx = sCtx.pPrior;
if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
assert( sCtx.pTab==pTab );
@@ -134108,6 +154734,7 @@ static int vtabCallConstructor(
** the sqlite3_vtab object if successful. */
memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0]));
pVTable->pVtab->pModule = pMod->pModule;
+ pMod->nRefModule++;
pVTable->nRef = 1;
if( sCtx.bDeclared==0 ){
const char *zFormat = "vtable constructor did not declare schema: %s";
@@ -134116,14 +154743,14 @@ static int vtabCallConstructor(
rc = SQLITE_ERROR;
}else{
int iCol;
- u8 oooHidden = 0;
+ u16 oooHidden = 0;
/* If everything went according to plan, link the new VTable structure
- ** into the linked list headed by pTab->pVTable. Then loop through the
+ ** into the linked list headed by pTab->u.vtab.p. Then loop through the
** columns of the table to see if any of them contain the token "hidden".
** If so, set the Column COLFLAG_HIDDEN flag and remove the token from
** the type string. */
- pVTable->pNext = pTab->pVTable;
- pTab->pVTable = pVTable;
+ pVTable->pNext = pTab->u.vtab.p;
+ pTab->u.vtab.p = pVTable;
for(iCol=0; iCol<pTab->nCol; iCol++){
char *zType = sqlite3ColumnType(&pTab->aCol[iCol], "");
@@ -134149,6 +154776,7 @@ static int vtabCallConstructor(
zType[i-1] = '\0';
}
pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN;
+ pTab->tabFlags |= TF_HasHidden;
oooHidden = TF_OOOHidden;
}else{
pTab->tabFlags |= oooHidden;
@@ -134163,7 +154791,7 @@ static int vtabCallConstructor(
/*
** This function is invoked by the parser to call the xConnect() method
-** of the virtual table pTab. If an error occurs, an error code is returned
+** of the virtual table pTab. If an error occurs, an error code is returned
** and an error left in pParse.
**
** This call is a no-op if table pTab is not a virtual table.
@@ -134175,16 +154803,17 @@ SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
int rc;
assert( pTab );
- if( !IsVirtual(pTab) || sqlite3GetVTable(db, pTab) ){
+ assert( IsVirtual(pTab) );
+ if( sqlite3GetVTable(db, pTab) ){
return SQLITE_OK;
}
/* Locate the required virtual table module */
- zMod = pTab->azModuleArg[0];
+ zMod = pTab->u.vtab.azArg[0];
pMod = (Module*)sqlite3HashFind(&db->aModule, zMod);
if( !pMod ){
- const char *zModule = pTab->azModuleArg[0];
+ const char *zModule = pTab->u.vtab.azArg[0];
sqlite3ErrorMsg(pParse, "no such module: %s", zModule);
rc = SQLITE_ERROR;
}else{
@@ -134209,7 +154838,8 @@ static int growVTrans(sqlite3 *db){
/* Grow the sqlite3.aVTrans array if required */
if( (db->nVTrans%ARRAY_INCR)==0 ){
VTable **aVTrans;
- int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR);
+ sqlite3_int64 nBytes = sizeof(sqlite3_vtab*)*
+ ((sqlite3_int64)db->nVTrans + ARRAY_INCR);
aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes);
if( !aVTrans ){
return SQLITE_NOMEM_BKPT;
@@ -134233,7 +154863,7 @@ static void addToVTrans(sqlite3 *db, VTable *pVTab){
/*
** This function is invoked by the vdbe to call the xCreate method
-** of the virtual table named zTab in database iDb.
+** of the virtual table named zTab in database iDb.
**
** If an error occurs, *pzErr is set to point to an English language
** description of the error and an SQLITE_XXX error code is returned.
@@ -134246,14 +154876,14 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab,
const char *zMod;
pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
- assert( pTab && IsVirtual(pTab) && !pTab->pVTable );
+ assert( pTab && IsVirtual(pTab) && !pTab->u.vtab.p );
/* Locate the required virtual table module */
- zMod = pTab->azModuleArg[0];
+ zMod = pTab->u.vtab.azArg[0];
pMod = (Module*)sqlite3HashFind(&db->aModule, zMod);
- /* If the module has been registered and includes a Create method,
- ** invoke it now. If the module has not been registered, return an
+ /* If the module has been registered and includes a Create method,
+ ** invoke it now. If the module has not been registered, return an
** error. Otherwise, do nothing.
*/
if( pMod==0 || pMod->pModule->xCreate==0 || pMod->pModule->xDestroy==0 ){
@@ -134284,8 +154914,8 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
VtabCtx *pCtx;
int rc = SQLITE_OK;
Table *pTab;
- char *zErr = 0;
Parse sParse;
+ int initBusy;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){
@@ -134295,28 +154925,35 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
sqlite3_mutex_enter(db->mutex);
pCtx = db->pVtabCtx;
if( !pCtx || pCtx->bDeclared ){
- sqlite3Error(db, SQLITE_MISUSE);
+ sqlite3Error(db, SQLITE_MISUSE_BKPT);
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT;
}
pTab = pCtx->pTab;
assert( IsVirtual(pTab) );
- memset(&sParse, 0, sizeof(sParse));
+ sqlite3ParseObjectInit(&sParse, db);
sParse.eParseMode = PARSE_MODE_DECLARE_VTAB;
- sParse.db = db;
+ sParse.disableTriggers = 1;
+ /* We should never be able to reach this point while loading the
+ ** schema. Nevertheless, defend against that (turn off db->init.busy)
+ ** in case a bug arises. */
+ assert( db->init.busy==0 );
+ initBusy = db->init.busy;
+ db->init.busy = 0;
sParse.nQueryLoop = 1;
- if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr)
- && sParse.pNewTable
- && !db->mallocFailed
- && !sParse.pNewTable->pSelect
- && !IsVirtual(sParse.pNewTable)
+ if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable)
+ && ALWAYS(sParse.pNewTable!=0)
+ && ALWAYS(!db->mallocFailed)
+ && IsOrdinaryTable(sParse.pNewTable)
){
+ assert( sParse.zErrMsg==0 );
if( !pTab->aCol ){
Table *pNew = sParse.pNewTable;
Index *pIdx;
pTab->aCol = pNew->aCol;
- pTab->nCol = pNew->nCol;
+ sqlite3ExprListDelete(db, pNew->u.tab.pDfltList);
+ pTab->nNVCol = pTab->nCol = pNew->nCol;
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
pNew->nCol = 0;
pNew->aCol = 0;
@@ -134340,8 +154977,9 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
}
pCtx->bDeclared = 1;
}else{
- sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
- sqlite3DbFree(db, zErr);
+ sqlite3ErrorWithMsg(db, SQLITE_ERROR,
+ (sParse.zErrMsg ? "%s" : 0), sParse.zErrMsg);
+ sqlite3DbFree(db, sParse.zErrMsg);
rc = SQLITE_ERROR;
}
sParse.eParseMode = PARSE_MODE_NORMAL;
@@ -134350,7 +154988,8 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
sqlite3VdbeFinalize(sParse.pVdbe);
}
sqlite3DeleteTable(db, sParse.pNewTable);
- sqlite3ParserReset(&sParse);
+ sqlite3ParseObjectReset(&sParse);
+ db->init.busy = initBusy;
assert( (rc&0xff)==rc );
rc = sqlite3ApiExit(db, rc);
@@ -134370,10 +155009,13 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab
Table *pTab;
pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
- if( pTab!=0 && ALWAYS(pTab->pVTable!=0) ){
+ if( ALWAYS(pTab!=0)
+ && ALWAYS(IsVirtual(pTab))
+ && ALWAYS(pTab->u.vtab.p!=0)
+ ){
VTable *p;
int (*xDestroy)(sqlite3_vtab *);
- for(p=pTab->pVTable; p; p=p->pNext){
+ for(p=pTab->u.vtab.p; p; p=p->pNext){
assert( p->pVtab );
if( p->pVtab->nRef>0 ){
return SQLITE_LOCKED;
@@ -134381,15 +155023,18 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab
}
p = vtabDisconnectAll(db, pTab);
xDestroy = p->pMod->pModule->xDestroy;
- assert( xDestroy!=0 ); /* Checked before the virtual table is created */
+ if( xDestroy==0 ) xDestroy = p->pMod->pModule->xDisconnect;
+ assert( xDestroy!=0 );
+ pTab->nTabRef++;
rc = xDestroy(p->pVtab);
/* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */
if( rc==SQLITE_OK ){
- assert( pTab->pVTable==p && p->pNext==0 );
+ assert( pTab->u.vtab.p==p && p->pNext==0 );
p->pVtab = 0;
- pTab->pVTable = 0;
+ pTab->u.vtab.p = 0;
sqlite3VtabUnlock(p);
}
+ sqlite3DeleteTable(db, pTab);
}
return rc;
@@ -134401,7 +155046,7 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab
** called is identified by the second argument, "offset", which is
** the offset of the method to call in the sqlite3_module structure.
**
-** The array is cleared after invoking the callbacks.
+** The array is cleared after invoking the callbacks.
*/
static void callFinaliser(sqlite3 *db, int offset){
int i;
@@ -134450,7 +155095,7 @@ SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, Vdbe *p){
}
/*
-** Invoke the xRollback method of all virtual tables in the
+** Invoke the xRollback method of all virtual tables in the
** sqlite3.aVTrans array. Then clear the array itself.
*/
SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db){
@@ -134459,7 +155104,7 @@ SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db){
}
/*
-** Invoke the xCommit method of all virtual tables in the
+** Invoke the xCommit method of all virtual tables in the
** sqlite3.aVTrans array. Then clear the array itself.
*/
SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db){
@@ -134481,7 +155126,7 @@ SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
/* Special case: If db->aVTrans is NULL and db->nVTrans is greater
** than zero, then this function is being called from within a
- ** virtual module xSync() callback. It is illegal to write to
+ ** virtual module xSync() callback. It is illegal to write to
** virtual module tables in this case, so return SQLITE_LOCKED.
*/
if( sqlite3VtabInSync(db) ){
@@ -134489,7 +155134,7 @@ SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
}
if( !pVTab ){
return SQLITE_OK;
- }
+ }
pModule = pVTab->pVtab->pModule;
if( pModule->xBegin ){
@@ -134502,7 +155147,7 @@ SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
}
}
- /* Invoke the xBegin method. If successful, add the vtab to the
+ /* Invoke the xBegin method. If successful, add the vtab to the
** sqlite3.aVTrans[] array. */
rc = growVTrans(db);
if( rc==SQLITE_OK ){
@@ -134526,11 +155171,11 @@ SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
** as the second argument to the virtual table method invoked.
**
** If op is SAVEPOINT_BEGIN, the xSavepoint method is invoked. If it is
-** SAVEPOINT_ROLLBACK, the xRollbackTo method. Otherwise, if op is
+** SAVEPOINT_ROLLBACK, the xRollbackTo method. Otherwise, if op is
** SAVEPOINT_RELEASE, then the xRelease method of each virtual table with
** an open transaction is invoked.
**
-** If any virtual table method returns an error code other than SQLITE_OK,
+** If any virtual table method returns an error code other than SQLITE_OK,
** processing is abandoned and the error returned to the caller of this
** function immediately. If all calls to virtual table methods are successful,
** SQLITE_OK is returned.
@@ -134547,6 +155192,7 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
const sqlite3_module *pMod = pVTab->pMod->pModule;
if( pVTab->pVtab && pMod->iVersion>=2 ){
int (*xMethod)(sqlite3_vtab *, int);
+ sqlite3VtabLock(pVTab);
switch( op ){
case SAVEPOINT_BEGIN:
xMethod = pMod->xSavepoint;
@@ -134560,8 +155206,12 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
break;
}
if( xMethod && pVTab->iSavepoint>iSavepoint ){
+ u64 savedFlags = (db->flags & SQLITE_Defensive);
+ db->flags &= ~(u64)SQLITE_Defensive;
rc = xMethod(pVTab->pVtab, iSavepoint);
+ db->flags |= savedFlags;
}
+ sqlite3VtabUnlock(pVTab);
}
}
}
@@ -134577,7 +155227,7 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
** This routine is used to allow virtual table implementations to
** overload MATCH, LIKE, GLOB, and REGEXP operators.
**
-** Return either the pDef argument (indicating no change) or a
+** Return either the pDef argument (indicating no change) or a
** new FuncDef structure that is marked as ephemeral using the
** SQLITE_FUNC_EPHEM flag.
*/
@@ -134598,15 +155248,16 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(
/* Check to see the left operand is a column in a virtual table */
if( NEVER(pExpr==0) ) return pDef;
if( pExpr->op!=TK_COLUMN ) return pDef;
+ assert( ExprUseYTab(pExpr) );
pTab = pExpr->y.pTab;
- if( pTab==0 ) return pDef;
+ if( NEVER(pTab==0) ) return pDef;
if( !IsVirtual(pTab) ) return pDef;
pVtab = sqlite3GetVTable(db, pTab)->pVtab;
assert( pVtab!=0 );
assert( pVtab->pModule!=0 );
pMod = (sqlite3_module *)pVtab->pModule;
if( pMod->xFindFunction==0 ) return pDef;
-
+
/* Call the xFindFunction method on the virtual table implementation
** to see if the implementation wants to overload this function.
**
@@ -134660,7 +155311,7 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
if( pTab==pToplevel->apVtabLock[i] ) return;
}
n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]);
- apVtabLock = sqlite3_realloc64(pToplevel->apVtabLock, n);
+ apVtabLock = sqlite3Realloc(pToplevel->apVtabLock, n);
if( apVtabLock ){
pToplevel->apVtabLock = apVtabLock;
pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab;
@@ -134672,12 +155323,13 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
/*
** Check to see if virtual table module pMod can be have an eponymous
** virtual table instance. If it can, create one if one does not already
-** exist. Return non-zero if the eponymous virtual table instance exists
-** when this routine returns, and return zero if it does not exist.
+** exist. Return non-zero if either the eponymous virtual table instance
+** exists when this routine returns or if an attempt to create it failed
+** and an error message was left in pParse.
**
** An eponymous virtual table instance is one that is named after its
** module, and more importantly, does not require a CREATE VIRTUAL TABLE
-** statement in order to come into existance. Eponymous virtual table
+** statement in order to come into existence. Eponymous virtual table
** instances always exist. They cannot be DROP-ed.
**
** Any virtual table module for which xConnect and xCreate are the same
@@ -134700,18 +155352,19 @@ SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){
}
pMod->pEpoTab = pTab;
pTab->nTabRef = 1;
+ pTab->eTabType = TABTYP_VTAB;
pTab->pSchema = db->aDb[0].pSchema;
- assert( pTab->nModuleArg==0 );
+ assert( pTab->u.vtab.nArg==0 );
pTab->iPKey = -1;
- addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName));
- addModuleArgument(db, pTab, 0);
- addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName));
+ pTab->tabFlags |= TF_Eponymous;
+ addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName));
+ addModuleArgument(pParse, pTab, 0);
+ addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName));
rc = vtabCallConstructor(db, pTab, pMod, pModule->xConnect, &zErr);
if( rc ){
sqlite3ErrorMsg(pParse, "%s", zErr);
sqlite3DbFree(db, zErr);
sqlite3VtabEponymousTableClear(db, pMod);
- return 0;
}
return 1;
}
@@ -134724,7 +155377,7 @@ SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3 *db, Module *pMod){
Table *pTab = pMod->pEpoTab;
if( pTab!=0 ){
/* Mark the table as Ephemeral prior to deleting it, so that the
- ** sqlite3DeleteTable() routine will know that it is not stored in
+ ** sqlite3DeleteTable() routine will know that it is not stored in
** the schema. */
pTab->tabFlags |= TF_Ephemeral;
sqlite3DeleteTable(db, pTab);
@@ -134740,8 +155393,8 @@ SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3 *db, Module *pMod){
** within an xUpdate method.
*/
SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *db){
- static const unsigned char aMap[] = {
- SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE
+ static const unsigned char aMap[] = {
+ SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE
};
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
@@ -134753,35 +155406,49 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *db){
}
/*
-** Call from within the xCreate() or xConnect() methods to provide
+** Call from within the xCreate() or xConnect() methods to provide
** the SQLite core with additional information about the behavior
** of the virtual table being implemented.
*/
SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){
va_list ap;
int rc = SQLITE_OK;
+ VtabCtx *p;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
sqlite3_mutex_enter(db->mutex);
- va_start(ap, op);
- switch( op ){
- case SQLITE_VTAB_CONSTRAINT_SUPPORT: {
- VtabCtx *p = db->pVtabCtx;
- if( !p ){
- rc = SQLITE_MISUSE_BKPT;
- }else{
- assert( p->pTab==0 || IsVirtual(p->pTab) );
+ p = db->pVtabCtx;
+ if( !p ){
+ rc = SQLITE_MISUSE_BKPT;
+ }else{
+ assert( p->pTab==0 || IsVirtual(p->pTab) );
+ va_start(ap, op);
+ switch( op ){
+ case SQLITE_VTAB_CONSTRAINT_SUPPORT: {
p->pVTable->bConstraint = (u8)va_arg(ap, int);
+ break;
+ }
+ case SQLITE_VTAB_INNOCUOUS: {
+ p->pVTable->eVtabRisk = SQLITE_VTABRISK_Low;
+ break;
+ }
+ case SQLITE_VTAB_DIRECTONLY: {
+ p->pVTable->eVtabRisk = SQLITE_VTABRISK_High;
+ break;
+ }
+ case SQLITE_VTAB_USES_ALL_SCHEMAS: {
+ p->pVTable->bAllSchemas = 1;
+ break;
+ }
+ default: {
+ rc = SQLITE_MISUSE_BKPT;
+ break;
}
- break;
}
- default:
- rc = SQLITE_MISUSE_BKPT;
- break;
+ va_end(ap);
}
- va_end(ap);
if( rc!=SQLITE_OK ) sqlite3Error(db, rc);
sqlite3_mutex_leave(db->mutex);
@@ -134830,20 +155497,9 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){
** planner logic in "where.c". These definitions are broken out into
** a separate source file for easier editing.
*/
+#ifndef SQLITE_WHEREINT_H
+#define SQLITE_WHEREINT_H
-/*
-** Trace output macros
-*/
-#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
-/***/ extern int sqlite3WhereTrace;
-#endif
-#if defined(SQLITE_DEBUG) \
- && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
-# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X
-# define WHERETRACE_ENABLED 1
-#else
-# define WHERETRACE(K,X)
-#endif
/* Forward references
*/
@@ -134859,6 +155515,28 @@ typedef struct WhereLoopBuilder WhereLoopBuilder;
typedef struct WhereScan WhereScan;
typedef struct WhereOrCost WhereOrCost;
typedef struct WhereOrSet WhereOrSet;
+typedef struct WhereMemBlock WhereMemBlock;
+typedef struct WhereRightJoin WhereRightJoin;
+
+/*
+** This object is a header on a block of allocated memory that will be
+** automatically freed when its WInfo object is destructed.
+*/
+struct WhereMemBlock {
+ WhereMemBlock *pNext; /* Next block in the chain */
+ u64 sz; /* Bytes of space */
+};
+
+/*
+** Extra information attached to a WhereLevel that is a RIGHT JOIN.
+*/
+struct WhereRightJoin {
+ int iMatch; /* Cursor used to determine prior matched rows */
+ int regBloom; /* Bloom filter for iRJMatch */
+ int regReturn; /* Return register for the interior subroutine */
+ int addrSubrtn; /* Starting address for the interior subroutine */
+ int endSubrtn; /* The last opcode in the interior subroutine */
+};
/*
** This object contains information needed to implement a single nested
@@ -134885,13 +155563,17 @@ struct WhereLevel {
int addrCont; /* Jump here to continue with the next loop cycle */
int addrFirst; /* First instruction of interior of the loop */
int addrBody; /* Beginning of the body of this loop */
+ int regBignull; /* big-null flag reg. True if a NULL-scan is needed */
+ int addrBignull; /* Jump here for next part of big-null scan */
#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */
int addrLikeRep; /* LIKE range processing address */
#endif
+ int regFilter; /* Bloom filter */
+ WhereRightJoin *pRJ; /* Extra information for RIGHT JOIN */
u8 iFrom; /* Which entry in the FROM clause */
u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */
- int p1, p2; /* Operands of the opcode used to ends the loop */
+ int p1, p2; /* Operands of the opcode used to end the loop */
union { /* Information that depends on pWLoop->wsFlags */
struct {
int nIn; /* Number of entries in aInLoop[] */
@@ -134899,11 +155581,11 @@ struct WhereLevel {
int iCur; /* The VDBE cursor used by this IN operator */
int addrInTop; /* Top of the IN loop */
int iBase; /* Base register of multi-key index record */
- int nPrefix; /* Number of prior entires in the key */
+ int nPrefix; /* Number of prior entries in the key */
u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */
} *aInLoop; /* Information about each nested IN operator */
} in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */
- Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */
+ Index *pCoveringIdx; /* Possible covering index for WHERE_MULTI_OR */
} u;
struct WhereLoop *pWLoop; /* The selected WhereLoop object */
Bitmask notReady; /* FROM entries not usable at this level */
@@ -134942,15 +155624,17 @@ struct WhereLoop {
u16 nEq; /* Number of equality constraints */
u16 nBtm; /* Size of BTM vector */
u16 nTop; /* Size of TOP vector */
- u16 nIdxCol; /* Index column used for ORDER BY */
+ u16 nDistinctCol; /* Index columns used to sort for DISTINCT */
Index *pIndex; /* Index used, or NULL */
} btree;
struct { /* Information for virtual tables */
int idxNum; /* Index number */
- u8 needFree; /* True if sqlite3_free(idxStr) is needed */
+ u32 needFree : 1; /* True if sqlite3_free(idxStr) is needed */
+ u32 bOmitOffset : 1; /* True to let virtual table handle offset */
i8 isOrdered; /* True if satisfies ORDER BY */
u16 omitMask; /* Terms that may be omitted */
char *idxStr; /* Index identifier string */
+ u32 mHandleIn; /* Terms to handle as IN(...) instead of == */
} vtab;
} u;
u32 wsFlags; /* WHERE_* flags describing the plan */
@@ -134966,7 +155650,7 @@ struct WhereLoop {
/* This object holds the prerequisites and the cost of running a
** subquery on one operand of an OR operator in the WHERE clause.
-** See WhereOrSet for additional information
+** See WhereOrSet for additional information
*/
struct WhereOrCost {
Bitmask prereq; /* Prerequisites */
@@ -135018,7 +155702,7 @@ struct WherePath {
** clause subexpression is separated from the others by AND operators,
** usually, or sometimes subexpressions separated by OR.
**
-** All WhereTerms are collected into a single WhereClause structure.
+** All WhereTerms are collected into a single WhereClause structure.
** The following identity holds:
**
** WhereTerm.pWC->a[WhereTerm.idx] == WhereTerm
@@ -135073,9 +155757,11 @@ struct WhereTerm {
u8 eMatchOp; /* Op for vtab MATCH/LIKE/GLOB/REGEXP terms */
int iParent; /* Disable pWC->a[iParent] when this term disabled */
int leftCursor; /* Cursor number of X in "X <op> <expr>" */
- int iField; /* Field in (?,?,?) IN (SELECT...) vector */
union {
- int leftColumn; /* Column number of X in "X <op> <expr>" */
+ struct {
+ int leftColumn; /* Column number of X in "X <op> <expr>" */
+ int iField; /* Field in (?,?,?) IN (SELECT...) vector */
+ } x; /* Opcode other than OP_OR or OP_AND */
WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
} u;
@@ -135086,23 +155772,26 @@ struct WhereTerm {
/*
** Allowed values of WhereTerm.wtFlags
*/
-#define TERM_DYNAMIC 0x01 /* Need to call sqlite3ExprDelete(db, pExpr) */
-#define TERM_VIRTUAL 0x02 /* Added by the optimizer. Do not code */
-#define TERM_CODED 0x04 /* This term is already coded */
-#define TERM_COPIED 0x08 /* Has a child */
-#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
-#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
-#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
-#else
-# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
-#endif
-#define TERM_LIKEOPT 0x100 /* Virtual terms from the LIKE optimization */
-#define TERM_LIKECOND 0x200 /* Conditionally this LIKE operator term */
-#define TERM_LIKE 0x400 /* The original LIKE operator */
-#define TERM_IS 0x800 /* Term.pExpr is an IS operator */
+#define TERM_DYNAMIC 0x0001 /* Need to call sqlite3ExprDelete(db, pExpr) */
+#define TERM_VIRTUAL 0x0002 /* Added by the optimizer. Do not code */
+#define TERM_CODED 0x0004 /* This term is already coded */
+#define TERM_COPIED 0x0008 /* Has a child */
+#define TERM_ORINFO 0x0010 /* Need to free the WhereTerm.u.pOrInfo object */
+#define TERM_ANDINFO 0x0020 /* Need to free the WhereTerm.u.pAndInfo obj */
+#define TERM_OK 0x0040 /* Used during OR-clause processing */
+#define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */
+#define TERM_LIKEOPT 0x0100 /* Virtual terms from the LIKE optimization */
+#define TERM_LIKECOND 0x0200 /* Conditionally this LIKE operator term */
+#define TERM_LIKE 0x0400 /* The original LIKE operator */
+#define TERM_IS 0x0800 /* Term.pExpr is an IS operator */
#define TERM_VARSELECT 0x1000 /* Term.pExpr contains a correlated sub-query */
+#define TERM_HEURTRUTH 0x2000 /* Heuristic truthProb used */
+#ifdef SQLITE_ENABLE_STAT4
+# define TERM_HIGHTRUTH 0x4000 /* Term excludes few rows */
+#else
+# define TERM_HIGHTRUTH 0 /* Only used with STAT4 */
+#endif
+#define TERM_SLICE 0x8000 /* One slice of a row-value/vector comparison */
/*
** An instance of the WhereScan object is used as an iterator for locating
@@ -135113,11 +155802,11 @@ struct WhereScan {
WhereClause *pWC; /* WhereClause currently being scanned */
const char *zCollName; /* Required collating sequence, if not NULL */
Expr *pIdxExpr; /* Search for this index expression */
- char idxaff; /* Must match this affinity, if zCollName!=NULL */
- unsigned char nEquiv; /* Number of entries in aEquiv[] */
- unsigned char iEquiv; /* Next unused slot in aEquiv[] */
- u32 opMask; /* Acceptable operators */
int k; /* Resume scanning at this->pWC->a[this->k] */
+ u32 opMask; /* Acceptable operators */
+ char idxaff; /* Must match this affinity, if zCollName!=NULL */
+ unsigned char iEquiv; /* Current slot in aiCur[] and aiColumn[] */
+ unsigned char nEquiv; /* Number of entries in aiCur[] and aiColumn[] */
int aiCur[11]; /* Cursors in the equivalence class */
i16 aiColumn[11]; /* Corresponding column number in the eq-class */
};
@@ -135141,7 +155830,8 @@ struct WhereClause {
u8 hasOr; /* True if any a[].eOperator is WO_OR */
int nTerm; /* Number of terms */
int nSlot; /* Number of entries in a[] */
- WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */
+ int nBase; /* Number of terms through the last non-Virtual */
+ WhereTerm *a; /* Each a[] describes a term of the WHERE clause */
#if defined(SQLITE_SMALL_STACK)
WhereTerm aStatic[1]; /* Initial static space for a[] */
#else
@@ -135170,8 +155860,8 @@ struct WhereAndInfo {
** An instance of the following structure keeps track of a mapping
** between VDBE cursor numbers and bits of the bitmasks in WhereTerm.
**
-** The VDBE cursor numbers are small integers contained in
-** SrcList_item.iCursor and Expr.iTable fields. For any given WHERE
+** The VDBE cursor numbers are small integers contained in
+** SrcItem.iCursor and Expr.iTable fields. For any given WHERE
** clause, the cursor numbers might not begin with 0 and they might
** contain gaps in the numbering sequence. But we want to make maximum
** use of the bits in our bitmasks. This structure provides a mapping
@@ -135199,31 +155889,28 @@ struct WhereMaskSet {
};
/*
-** Initialize a WhereMaskSet object
-*/
-#define initMaskSet(P) (P)->n=0
-
-/*
** This object is a convenience wrapper holding all information needed
** to construct WhereLoop objects for a particular query.
*/
struct WhereLoopBuilder {
WhereInfo *pWInfo; /* Information about this WHERE */
WhereClause *pWC; /* WHERE clause terms */
- ExprList *pOrderBy; /* ORDER BY clause */
WhereLoop *pNew; /* Template WhereLoop */
WhereOrSet *pOrSet; /* Record best loops here, if not NULL */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
UnpackedRecord *pRec; /* Probe for stat4 (if required) */
int nRecValid; /* Number of valid fields currently in pRec */
#endif
- unsigned int bldFlags; /* SQLITE_BLDF_* flags */
+ unsigned char bldFlags1; /* First set of SQLITE_BLDF_* flags */
+ unsigned char bldFlags2; /* Second set of SQLITE_BLDF_* flags */
unsigned int iPlanLimit; /* Search limiter */
};
/* Allowed values for WhereLoopBuider.bldFlags */
-#define SQLITE_BLDF_INDEXED 0x0001 /* An index is used */
-#define SQLITE_BLDF_UNIQUE 0x0002 /* All keys of a UNIQUE index used */
+#define SQLITE_BLDF1_INDEXED 0x0001 /* An index is used */
+#define SQLITE_BLDF1_UNIQUE 0x0002 /* All keys of a UNIQUE index used */
+
+#define SQLITE_BLDF2_2NDPASS 0x0004 /* Second builder pass needed */
/* The WhereLoopBuilder.iPlanLimit is used to limit the number of
** index+constraint combinations the query planner will consider for a
@@ -135260,24 +155947,30 @@ struct WhereInfo {
SrcList *pTabList; /* List of tables in the join */
ExprList *pOrderBy; /* The ORDER BY clause or NULL */
ExprList *pResultSet; /* Result set of the query */
+#if WHERETRACE_ENABLED
Expr *pWhere; /* The complete WHERE clause */
- LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */
+#endif
+ Select *pSelect; /* The entire SELECT statement containing WHERE */
int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
int iContinue; /* Jump here to continue with next record */
int iBreak; /* Jump here to break out of the loop */
int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
+ LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */
u8 nLevel; /* Number of nested loop */
i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */
- u8 sorted; /* True if really sorted (not just grouped) */
u8 eOnePass; /* ONEPASS_OFF, or _SINGLE, or _MULTI */
- u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
u8 eDistinct; /* One of the WHERE_DISTINCT_* values */
- u8 bOrderedInnerLoop; /* True if only the inner-most loop is ordered */
+ unsigned bDeferredSeek :1; /* Uses OP_DeferredSeek */
+ unsigned untestedTerms :1; /* Not all WHERE terms resolved by outer loop */
+ unsigned bOrderedInnerLoop:1;/* True if only the inner-most loop is ordered */
+ unsigned sorted :1; /* True if really sorted (not just grouped) */
+ LogEst nRowOut; /* Estimated number of output rows */
int iTop; /* The very beginning of the WHERE loop */
+ int iEndWhere; /* End of the WHERE clause itself */
WhereLoop *pLoops; /* List of all WhereLoop objects */
+ WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */
Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
- LogEst nRowOut; /* Estimated number of output rows */
WhereClause sWC; /* Decomposition of the WHERE clause */
WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */
WhereLevel a[1]; /* Information about each nest loop in WHERE */
@@ -135291,6 +155984,8 @@ struct WhereInfo {
SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
#ifdef WHERETRACE_ENABLED
SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC);
+SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm);
+SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC);
#endif
SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm(
WhereClause *pWC, /* The WHERE clause to be searched */
@@ -135300,6 +155995,8 @@ SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm(
u32 op, /* Mask of WO_xx values describing operator */
Index *pIdx /* Must be compatible with this index, if not NULL */
);
+SQLITE_PRIVATE void *sqlite3WhereMalloc(WhereInfo *pWInfo, u64 nByte);
+SQLITE_PRIVATE void *sqlite3WhereRealloc(WhereInfo *pWInfo, void *pOld, u64 nByte);
/* wherecode.c: */
#ifndef SQLITE_OMIT_EXPLAIN
@@ -135309,8 +156006,14 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
);
+SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter(
+ const Parse *pParse, /* Parse context */
+ const WhereInfo *pWInfo, /* WHERE clause */
+ const WhereLevel *pLevel /* Bloom filter on this level */
+);
#else
# define sqlite3WhereExplainOneScan(u,v,w,x) 0
+# define sqlite3WhereExplainBloomFilter(u,v,w) 0
#endif /* SQLITE_OMIT_EXPLAIN */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
@@ -135323,20 +156026,29 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
# define sqlite3WhereAddScanStatus(a, b, c, d) ((void)d)
#endif
SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
+ Parse *pParse, /* Parsing context */
+ Vdbe *v, /* Prepared statement under construction */
WhereInfo *pWInfo, /* Complete information about the WHERE clause */
int iLevel, /* Which level of pWInfo->a[] should be coded */
+ WhereLevel *pLevel, /* The current level pointer */
Bitmask notReady /* Which tables are currently available */
);
+SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
+ WhereInfo *pWInfo,
+ int iLevel,
+ WhereLevel *pLevel
+);
/* whereexpr.c: */
SQLITE_PRIVATE void sqlite3WhereClauseInit(WhereClause*,WhereInfo*);
SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause*);
SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause*,Expr*,u8);
+SQLITE_PRIVATE void sqlite3WhereAddLimit(WhereClause*, Select*);
SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*);
SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet*, Expr*);
SQLITE_PRIVATE Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*);
SQLITE_PRIVATE void sqlite3WhereExprAnalyze(SrcList*, WhereClause*);
-SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);
+SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
@@ -135368,8 +156080,9 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereC
#define WO_AND 0x0400 /* Two or more AND-connected terms */
#define WO_EQUIV 0x0800 /* Of the form A==B, both columns */
#define WO_NOOP 0x1000 /* This term does not restrict search space */
+#define WO_ROWVAL 0x2000 /* A row-value term */
-#define WO_ALL 0x1fff /* Mask of all possible WO_* values */
+#define WO_ALL 0x3fff /* Mask of all possible WO_* values */
#define WO_SINGLE 0x01ff /* Mask of all non-compound WO_* values */
/*
@@ -135397,6 +156110,16 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereC
#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/
#define WHERE_PARTIALIDX 0x00020000 /* The automatic index is partial */
#define WHERE_IN_EARLYOUT 0x00040000 /* Perhaps quit IN loops early */
+#define WHERE_BIGNULL_SORT 0x00080000 /* Column nEq of index is BIGNULL */
+#define WHERE_IN_SEEKSCAN 0x00100000 /* Seek-scan optimization for IN */
+#define WHERE_TRANSCONS 0x00200000 /* Uses a transitive constraint */
+#define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */
+#define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */
+#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */
+ /* 0x02000000 -- available for reuse */
+#define WHERE_EXPRIDX 0x04000000 /* Uses an index-on-expressions */
+
+#endif /* !defined(SQLITE_WHEREINT_H) */
/************** End of whereInt.h ********************************************/
/************** Continuing where we left off in wherecode.c ******************/
@@ -135410,7 +156133,7 @@ static const char *explainIndexColumnName(Index *pIdx, int i){
i = pIdx->aiColumn[i];
if( i==XN_EXPR ) return "<expr>";
if( i==XN_ROWID ) return "rowid";
- return pIdx->pTable->aCol[i].zName;
+ return pIdx->pTable->aCol[i].zCnName;
}
/*
@@ -135452,7 +156175,7 @@ static void explainAppendTerm(
}
/*
-** Argument pLevel describes a strategy for scanning table pTab. This
+** Argument pLevel describes a strategy for scanning table pTab. This
** function appends text to pStr that describes the subset of table
** rows scanned by the strategy in the form of an SQL expression.
**
@@ -135492,9 +156215,9 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
/*
** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
-** command, or if either SQLITE_DEBUG or SQLITE_ENABLE_STMT_SCANSTATUS was
-** defined at compile-time. If it is not a no-op, a single OP_Explain opcode
-** is added to the output to describe the table scan strategy in pLevel.
+** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG
+** was defined at compile-time. If it is not a no-op, a single OP_Explain
+** opcode is added to the output to describe the table scan strategy in pLevel.
**
** If an OP_Explain opcode is added to the VM, its address is returned.
** Otherwise, if no OP_Explain is coded, zero is returned.
@@ -135506,11 +156229,11 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
int ret = 0;
-#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
- if( sqlite3ParseToplevel(pParse)->explain==2 )
+#if !defined(SQLITE_DEBUG)
+ if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
#endif
{
- struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
+ SrcItem *pItem = &pTabList->a[pLevel->iFrom];
Vdbe *v = pParse->pVdbe; /* VM being constructed */
sqlite3 *db = pParse->db; /* Database handle */
int isSearch; /* True for a SEARCH. False for SCAN. */
@@ -135529,16 +156252,8 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
- sqlite3_str_appendall(&str, isSearch ? "SEARCH" : "SCAN");
- if( pItem->pSelect ){
- sqlite3_str_appendf(&str, " SUBQUERY %u", pItem->pSelect->selId);
- }else{
- sqlite3_str_appendf(&str, " TABLE %s", pItem->zName);
- }
-
- if( pItem->zAlias ){
- sqlite3_str_appendf(&str, " AS %s", pItem->zAlias);
- }
+ str.printfFlags = SQLITE_PRINTF_INTERNAL;
+ sqlite3_str_appendf(&str, "%s %S", isSearch ? "SEARCH" : "SCAN", pItem);
if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
const char *zFmt = 0;
Index *pIdx;
@@ -135565,19 +156280,27 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
explainIndexRange(&str, pLoop);
}
}else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
- const char *zRangeOp;
+ char cRangeOp;
+#if 0 /* Better output, but breaks many tests */
+ const Table *pTab = pItem->pTab;
+ const char *zRowid = pTab->iPKey>=0 ? pTab->aCol[pTab->iPKey].zCnName:
+ "rowid";
+#else
+ const char *zRowid = "rowid";
+#endif
+ sqlite3_str_appendf(&str, " USING INTEGER PRIMARY KEY (%s", zRowid);
if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){
- zRangeOp = "=";
+ cRangeOp = '=';
}else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
- zRangeOp = ">? AND rowid<";
+ sqlite3_str_appendf(&str, ">? AND %s", zRowid);
+ cRangeOp = '<';
}else if( flags&WHERE_BTM_LIMIT ){
- zRangeOp = ">";
+ cRangeOp = '>';
}else{
assert( flags&WHERE_TOP_LIMIT);
- zRangeOp = "<";
+ cRangeOp = '<';
}
- sqlite3_str_appendf(&str,
- " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
+ sqlite3_str_appendf(&str, "%c?)", cRangeOp);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
@@ -135585,6 +156308,9 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
}
#endif
+ if( pItem->fg.jointype & JT_LEFT ){
+ sqlite3_str_appendf(&str, " LEFT-JOIN");
+ }
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
if( pLoop->nOut>=10 ){
sqlite3_str_appendf(&str, " (~%llu rows)",
@@ -135594,21 +156320,74 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
}
#endif
zMsg = sqlite3StrAccumFinish(&str);
+ sqlite3ExplainBreakpoint("",zMsg);
ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
}
return ret;
}
+
+/*
+** Add a single OP_Explain opcode that describes a Bloom filter.
+**
+** Or if not processing EXPLAIN QUERY PLAN and not in a SQLITE_DEBUG and/or
+** SQLITE_ENABLE_STMT_SCANSTATUS build, then OP_Explain opcodes are not
+** required and this routine is a no-op.
+**
+** If an OP_Explain opcode is added to the VM, its address is returned.
+** Otherwise, if no OP_Explain is coded, zero is returned.
+*/
+SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter(
+ const Parse *pParse, /* Parse context */
+ const WhereInfo *pWInfo, /* WHERE clause */
+ const WhereLevel *pLevel /* Bloom filter on this level */
+){
+ int ret = 0;
+ SrcItem *pItem = &pWInfo->pTabList->a[pLevel->iFrom];
+ Vdbe *v = pParse->pVdbe; /* VM being constructed */
+ sqlite3 *db = pParse->db; /* Database handle */
+ char *zMsg; /* Text to add to EQP output */
+ int i; /* Loop counter */
+ WhereLoop *pLoop; /* The where loop */
+ StrAccum str; /* EQP output string */
+ char zBuf[100]; /* Initial space for EQP output string */
+
+ sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
+ str.printfFlags = SQLITE_PRINTF_INTERNAL;
+ sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem);
+ pLoop = pLevel->pWLoop;
+ if( pLoop->wsFlags & WHERE_IPK ){
+ const Table *pTab = pItem->pTab;
+ if( pTab->iPKey>=0 ){
+ sqlite3_str_appendf(&str, "%s=?", pTab->aCol[pTab->iPKey].zCnName);
+ }else{
+ sqlite3_str_appendf(&str, "rowid=?");
+ }
+ }else{
+ for(i=pLoop->nSkip; i<pLoop->u.btree.nEq; i++){
+ const char *z = explainIndexColumnName(pLoop->u.btree.pIndex, i);
+ if( i>pLoop->nSkip ) sqlite3_str_append(&str, " AND ", 5);
+ sqlite3_str_appendf(&str, "%s=?", z);
+ }
+ }
+ sqlite3_str_append(&str, ")", 1);
+ zMsg = sqlite3StrAccumFinish(&str);
+ ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
+ pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
+
+ sqlite3VdbeScanStatus(v, sqlite3VdbeCurrentAddr(v)-1, 0, 0, 0, 0);
+ return ret;
+}
#endif /* SQLITE_OMIT_EXPLAIN */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
/*
** Configure the VM passed as the first argument with an
-** sqlite3_stmt_scanstatus() entry corresponding to the scan used to
-** implement level pLvl. Argument pSrclist is a pointer to the FROM
+** sqlite3_stmt_scanstatus() entry corresponding to the scan used to
+** implement level pLvl. Argument pSrclist is a pointer to the FROM
** clause that the scan reads data from.
**
-** If argument addrExplain is not 0, it must be the address of an
+** If argument addrExplain is not 0, it must be the address of an
** OP_Explain instruction that describes the same loop.
*/
SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
@@ -135617,16 +156396,37 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
WhereLevel *pLvl, /* Level to add scanstatus() entry for */
int addrExplain /* Address of OP_Explain (or 0) */
){
- const char *zObj = 0;
- WhereLoop *pLoop = pLvl->pWLoop;
- if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){
- zObj = pLoop->u.btree.pIndex->zName;
- }else{
- zObj = pSrclist->a[pLvl->iFrom].zName;
+ if( IS_STMT_SCANSTATUS( sqlite3VdbeDb(v) ) ){
+ const char *zObj = 0;
+ WhereLoop *pLoop = pLvl->pWLoop;
+ int wsFlags = pLoop->wsFlags;
+ int viaCoroutine = 0;
+
+ if( (wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){
+ zObj = pLoop->u.btree.pIndex->zName;
+ }else{
+ zObj = pSrclist->a[pLvl->iFrom].zName;
+ viaCoroutine = pSrclist->a[pLvl->iFrom].fg.viaCoroutine;
+ }
+ sqlite3VdbeScanStatus(
+ v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj
+ );
+
+ if( viaCoroutine==0 ){
+ if( (wsFlags & (WHERE_MULTI_OR|WHERE_AUTO_INDEX))==0 ){
+ sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur);
+ }
+ if( wsFlags & WHERE_INDEXED ){
+ sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur);
+ }
+ }else{
+ int addr = pSrclist->a[pLvl->iFrom].addrFillSub;
+ VdbeOp *pOp = sqlite3VdbeGetOp(v, addr-1);
+ assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine );
+ assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr );
+ sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1);
+ }
}
- sqlite3VdbeScanStatus(
- v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj
- );
}
#endif
@@ -135664,7 +156464,7 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
**
** Only the parent term was in the original WHERE clause. The child1
** and child2 terms were added by the LIKE optimization. If both of
-** the virtual child terms are valid, then testing of the parent can be
+** the virtual child terms are valid, then testing of the parent can be
** skipped.
**
** Usually the parent term is marked as TERM_CODED. But if the parent
@@ -135677,7 +156477,7 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
int nLoop = 0;
assert( pTerm!=0 );
while( (pTerm->wtFlags & TERM_CODED)==0
- && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
+ && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_OuterON))
&& (pLevel->notReady & pTerm->prereqAll)==0
){
if( nLoop && (pTerm->wtFlags & TERM_LIKE)!=0 ){
@@ -135685,6 +156485,12 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
}else{
pTerm->wtFlags |= TERM_CODED;
}
+#ifdef WHERETRACE_ENABLED
+ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){
+ sqlite3DebugPrintf("DISABLE-");
+ sqlite3WhereTermPrint(pTerm, (int)(pTerm - (pTerm->pWC->a)));
+ }
+#endif
if( pTerm->iParent<0 ) break;
pTerm = &pTerm->pWC->a[pTerm->iParent];
assert( pTerm!=0 );
@@ -135696,11 +156502,11 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
/*
** Code an OP_Affinity opcode to apply the column affinity string zAff
-** to the n registers starting at base.
+** to the n registers starting at base.
**
-** As an optimization, SQLITE_AFF_BLOB entries (which are no-ops) at the
-** beginning and end of zAff are ignored. If all entries in zAff are
-** SQLITE_AFF_BLOB, then no code gets generated.
+** As an optimization, SQLITE_AFF_BLOB and SQLITE_AFF_NONE entries (which
+** are no-ops) at the beginning and end of zAff are ignored. If all entries
+** in zAff are SQLITE_AFF_BLOB or SQLITE_AFF_NONE, then no code gets generated.
**
** This routine makes its own copy of zAff so that the caller is free
** to modify zAff after this routine returns.
@@ -135713,15 +156519,16 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
}
assert( v!=0 );
- /* Adjust base and n to skip over SQLITE_AFF_BLOB entries at the beginning
- ** and end of the affinity string.
+ /* Adjust base and n to skip over SQLITE_AFF_BLOB and SQLITE_AFF_NONE
+ ** entries at the beginning and end of the affinity string.
*/
- while( n>0 && zAff[0]==SQLITE_AFF_BLOB ){
+ assert( SQLITE_AFF_NONE<SQLITE_AFF_BLOB );
+ while( n>0 && zAff[0]<=SQLITE_AFF_BLOB ){
n--;
base++;
zAff++;
}
- while( n>1 && zAff[n-1]==SQLITE_AFF_BLOB ){
+ while( n>1 && zAff[n-1]<=SQLITE_AFF_BLOB ){
n--;
}
@@ -135732,7 +156539,7 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
}
/*
-** Expression pRight, which is the RHS of a comparison operation, is
+** Expression pRight, which is the RHS of a comparison operation, is
** either a vector of n elements or, if n==1, a scalar expression.
** Before the comparison operation, affinity zAff is to be applied
** to the pRight values. This function modifies characters within the
@@ -135794,60 +156601,75 @@ static Expr *removeUnindexableInClauseTerms(
Expr *pX /* The IN expression to be reduced */
){
sqlite3 *db = pParse->db;
- Expr *pNew = sqlite3ExprDup(db, pX, 0);
+ Select *pSelect; /* Pointer to the SELECT on the RHS */
+ Expr *pNew;
+ pNew = sqlite3ExprDup(db, pX, 0);
if( db->mallocFailed==0 ){
- ExprList *pOrigRhs = pNew->x.pSelect->pEList; /* Original unmodified RHS */
- ExprList *pOrigLhs = pNew->pLeft->x.pList; /* Original unmodified LHS */
- ExprList *pRhs = 0; /* New RHS after modifications */
- ExprList *pLhs = 0; /* New LHS after mods */
- int i; /* Loop counter */
- Select *pSelect; /* Pointer to the SELECT on the RHS */
-
- for(i=iEq; i<pLoop->nLTerm; i++){
- if( pLoop->aLTerm[i]->pExpr==pX ){
- int iField = pLoop->aLTerm[i]->iField - 1;
- if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */
- pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr);
- pOrigRhs->a[iField].pExpr = 0;
- assert( pOrigLhs->a[iField].pExpr!=0 );
- pLhs = sqlite3ExprListAppend(pParse, pLhs, pOrigLhs->a[iField].pExpr);
- pOrigLhs->a[iField].pExpr = 0;
- }
- }
- sqlite3ExprListDelete(db, pOrigRhs);
- sqlite3ExprListDelete(db, pOrigLhs);
- pNew->pLeft->x.pList = pLhs;
- pNew->x.pSelect->pEList = pRhs;
- if( pLhs && pLhs->nExpr==1 ){
- /* Take care here not to generate a TK_VECTOR containing only a
- ** single value. Since the parser never creates such a vector, some
- ** of the subroutines do not handle this case. */
- Expr *p = pLhs->a[0].pExpr;
- pLhs->a[0].pExpr = 0;
- sqlite3ExprDelete(db, pNew->pLeft);
- pNew->pLeft = p;
- }
- pSelect = pNew->x.pSelect;
- if( pSelect->pOrderBy ){
- /* If the SELECT statement has an ORDER BY clause, zero the
- ** iOrderByCol variables. These are set to non-zero when an
- ** ORDER BY term exactly matches one of the terms of the
- ** result-set. Since the result-set of the SELECT statement may
- ** have been modified or reordered, these variables are no longer
- ** set correctly. Since setting them is just an optimization,
- ** it's easiest just to zero them here. */
- ExprList *pOrderBy = pSelect->pOrderBy;
- for(i=0; i<pOrderBy->nExpr; i++){
- pOrderBy->a[i].u.x.iOrderByCol = 0;
+ for(pSelect=pNew->x.pSelect; pSelect; pSelect=pSelect->pPrior){
+ ExprList *pOrigRhs; /* Original unmodified RHS */
+ ExprList *pOrigLhs = 0; /* Original unmodified LHS */
+ ExprList *pRhs = 0; /* New RHS after modifications */
+ ExprList *pLhs = 0; /* New LHS after mods */
+ int i; /* Loop counter */
+
+ assert( ExprUseXSelect(pNew) );
+ pOrigRhs = pSelect->pEList;
+ assert( pNew->pLeft!=0 );
+ assert( ExprUseXList(pNew->pLeft) );
+ if( pSelect==pNew->x.pSelect ){
+ pOrigLhs = pNew->pLeft->x.pList;
+ }
+ for(i=iEq; i<pLoop->nLTerm; i++){
+ if( pLoop->aLTerm[i]->pExpr==pX ){
+ int iField;
+ assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 );
+ iField = pLoop->aLTerm[i]->u.x.iField - 1;
+ if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */
+ pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr);
+ pOrigRhs->a[iField].pExpr = 0;
+ if( pOrigLhs ){
+ assert( pOrigLhs->a[iField].pExpr!=0 );
+ pLhs = sqlite3ExprListAppend(pParse,pLhs,pOrigLhs->a[iField].pExpr);
+ pOrigLhs->a[iField].pExpr = 0;
+ }
+ }
+ }
+ sqlite3ExprListDelete(db, pOrigRhs);
+ if( pOrigLhs ){
+ sqlite3ExprListDelete(db, pOrigLhs);
+ pNew->pLeft->x.pList = pLhs;
+ }
+ pSelect->pEList = pRhs;
+ if( pLhs && pLhs->nExpr==1 ){
+ /* Take care here not to generate a TK_VECTOR containing only a
+ ** single value. Since the parser never creates such a vector, some
+ ** of the subroutines do not handle this case. */
+ Expr *p = pLhs->a[0].pExpr;
+ pLhs->a[0].pExpr = 0;
+ sqlite3ExprDelete(db, pNew->pLeft);
+ pNew->pLeft = p;
+ }
+ if( pSelect->pOrderBy ){
+ /* If the SELECT statement has an ORDER BY clause, zero the
+ ** iOrderByCol variables. These are set to non-zero when an
+ ** ORDER BY term exactly matches one of the terms of the
+ ** result-set. Since the result-set of the SELECT statement may
+ ** have been modified or reordered, these variables are no longer
+ ** set correctly. Since setting them is just an optimization,
+ ** it's easiest just to zero them here. */
+ ExprList *pOrderBy = pSelect->pOrderBy;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ pOrderBy->a[i].u.x.iOrderByCol = 0;
+ }
}
- }
#if 0
- printf("For indexing, change the IN expr:\n");
- sqlite3TreeViewExpr(0, pX, 0);
- printf("Into:\n");
- sqlite3TreeViewExpr(0, pNew, 0);
+ printf("For indexing, change the IN expr:\n");
+ sqlite3TreeViewExpr(0, pX, 0);
+ printf("Into:\n");
+ sqlite3TreeViewExpr(0, pNew, 0);
#endif
+ }
}
return pNew;
}
@@ -135855,7 +156677,7 @@ static Expr *removeUnindexableInClauseTerms(
/*
** Generate code for a single equality term of the WHERE clause. An equality
-** term can be either X=expr or X IN (...). pTerm is the term to be
+** term can be either X=expr or X IN (...). pTerm is the term to be
** coded.
**
** The current value for the constraint is left in a register, the index
@@ -135919,41 +156741,51 @@ static int codeEqualityTerm(
if( pLoop->aLTerm[i]->pExpr==pX ) nEq++;
}
- if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){
- eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
+ iTab = 0;
+ if( !ExprUseXSelect(pX) || pX->x.pSelect->pEList->nExpr==1 ){
+ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab);
}else{
- sqlite3 *db = pParse->db;
- pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX);
-
- if( !db->mallocFailed ){
- aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq);
- eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap);
- pTerm->pExpr->iTable = pX->iTable;
+ Expr *pExpr = pTerm->pExpr;
+ if( pExpr->iTable==0 || !ExprHasProperty(pExpr, EP_Subrtn) ){
+ sqlite3 *db = pParse->db;
+ pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX);
+ if( !db->mallocFailed ){
+ aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq);
+ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap,&iTab);
+ pExpr->iTable = iTab;
+ }
+ sqlite3ExprDelete(db, pX);
+ }else{
+ int n = sqlite3ExprVectorSize(pX->pLeft);
+ aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*MAX(nEq,n));
+ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab);
}
- sqlite3ExprDelete(db, pX);
- pX = pTerm->pExpr;
+ pX = pExpr;
}
if( eType==IN_INDEX_INDEX_DESC ){
testcase( bRev );
bRev = !bRev;
}
- iTab = pX->iTable;
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0);
VdbeCoverageIf(v, bRev);
VdbeCoverageIf(v, !bRev);
- assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
+ assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
pLoop->wsFlags |= WHERE_IN_ABLE;
if( pLevel->u.in.nIn==0 ){
- pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
+ pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse);
+ }
+ if( iEq>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0 ){
+ pLoop->wsFlags |= WHERE_IN_EARLYOUT;
}
i = pLevel->u.in.nIn;
pLevel->u.in.nIn += nEq;
pLevel->u.in.aInLoop =
- sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop,
- sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
+ sqlite3WhereRealloc(pTerm->pWC->pWInfo,
+ pLevel->u.in.aInLoop,
+ sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
pIn = pLevel->u.in.aInLoop;
if( pIn ){
int iMap = 0; /* Index in aiMap[] */
@@ -135962,7 +156794,6 @@ static int codeEqualityTerm(
if( pLoop->aLTerm[i]->pExpr==pX ){
int iOut = iReg + i - iEq;
if( eType==IN_INDEX_ROWID ){
- testcase( nEq>1 ); /* Happens with a UNIQUE index on ROWID */
pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut);
}else{
int iCol = aiMap ? aiMap[iMap++] : 0;
@@ -135972,10 +156803,9 @@ static int codeEqualityTerm(
if( i==iEq ){
pIn->iCur = iTab;
pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next;
- if( iEq>0 && (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 ){
+ if( iEq>0 ){
pIn->iBase = iReg - i;
pIn->nPrefix = i;
- pLoop->wsFlags |= WHERE_IN_EARLYOUT;
}else{
pIn->nPrefix = 0;
}
@@ -135985,13 +156815,36 @@ static int codeEqualityTerm(
pIn++;
}
}
+ testcase( iEq>0
+ && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0
+ && (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 );
+ if( iEq>0
+ && (pLoop->wsFlags & (WHERE_IN_SEEKSCAN|WHERE_VIRTUALTABLE))==0
+ ){
+ sqlite3VdbeAddOp3(v, OP_SeekHit, pLevel->iIdxCur, 0, iEq);
+ }
}else{
pLevel->u.in.nIn = 0;
}
sqlite3DbFree(pParse->db, aiMap);
#endif
}
- disableTerm(pLevel, pTerm);
+
+ /* As an optimization, try to disable the WHERE clause term that is
+ ** driving the index as it will always be true. The correct answer is
+ ** obtained regardless, but we might get the answer with fewer CPU cycles
+ ** by omitting the term.
+ **
+ ** But do not disable the term unless we are certain that the term is
+ ** not a transitive constraint. For an example of where that does not
+ ** work, see https://sqlite.org/forum/forumpost/eb8613976a (2021-05-04)
+ */
+ if( (pLevel->pWLoop->wsFlags & WHERE_TRANSCONS)==0
+ || (pTerm->eOperator & WO_EQUIV)==0
+ ){
+ disableTerm(pLevel, pTerm);
+ }
+
return iReg;
}
@@ -136002,7 +156855,7 @@ static int codeEqualityTerm(
** For example, consider table t1(a,b,c,d,e,f) with index i1(a,b,c).
** Suppose the WHERE clause is this: a==5 AND b IN (1,2,3) AND c>5 AND c<10
** The index has as many as three equality constraints, but in this
-** example, the third "c" value is an inequality. So only two
+** example, the third "c" value is an inequality. So only two
** constraints are coded. This routine will generate code to evaluate
** a==5 and b IN (1,2,3). The current values for a and b will be stored
** in consecutive registers and the index of the first register is returned.
@@ -136069,7 +156922,7 @@ static int codeAllEqualityTerms(
/* Figure out how many memory cells we will need then allocate them.
*/
regBase = pParse->nMem + 1;
- nReg = pLoop->u.btree.nEq + nExtraReg;
+ nReg = nEq + nExtraReg;
pParse->nMem += nReg;
zAff = sqlite3DbStrDup(pParse->db,sqlite3IndexAffinityStr(pParse->db,pIdx));
@@ -136077,11 +156930,13 @@ static int codeAllEqualityTerms(
if( nSkip ){
int iIdxCur = pLevel->iIdxCur;
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regBase, regBase+nSkip-1);
sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur);
VdbeCoverageIf(v, bRev==0);
VdbeCoverageIf(v, bRev!=0);
VdbeComment((v, "begin skip-scan on %s", pIdx->zName));
j = sqlite3VdbeAddOp0(v, OP_Goto);
+ assert( pLevel->addrSkip==0 );
pLevel->addrSkip = sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLT:OP_SeekGT),
iIdxCur, 0, regBase, nSkip);
VdbeCoverageIf(v, bRev==0);
@@ -136092,7 +156947,7 @@ static int codeAllEqualityTerms(
testcase( pIdx->aiColumn[j]==XN_EXPR );
VdbeComment((v, "%s", explainIndexColumnName(pIdx, j)));
}
- }
+ }
/* Evaluate the equality constraints
*/
@@ -136101,7 +156956,7 @@ static int codeAllEqualityTerms(
int r1;
pTerm = pLoop->aLTerm[j];
assert( pTerm!=0 );
- /* The following testcase is true for indices with redundant columns.
+ /* The following testcase is true for indices with redundant columns.
** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
testcase( pTerm->wtFlags & TERM_VIRTUAL );
@@ -136111,14 +156966,14 @@ static int codeAllEqualityTerms(
sqlite3ReleaseTempReg(pParse, regBase);
regBase = r1;
}else{
- sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j);
+ sqlite3VdbeAddOp2(v, OP_Copy, r1, regBase+j);
}
}
if( pTerm->eOperator & WO_IN ){
if( pTerm->pExpr->flags & EP_xIsSelect ){
/* No affinity ever needs to be (or should be) applied to a value
- ** from the RHS of an "? IN (SELECT ...)" expression. The
- ** sqlite3FindInIndex() routine has already ensured that the
+ ** from the RHS of an "? IN (SELECT ...)" expression. The
+ ** sqlite3FindInIndex() routine has already ensured that the
** affinity of the comparison has been applied to the value. */
if( zAff ) zAff[j] = SQLITE_AFF_BLOB;
}
@@ -136128,7 +156983,8 @@ static int codeAllEqualityTerms(
sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk);
VdbeCoverage(v);
}
- if( zAff ){
+ if( pParse->nErr==0 ){
+ assert( pParse->db->mallocFailed==0 );
if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){
zAff[j] = SQLITE_AFF_BLOB;
}
@@ -136145,7 +157001,7 @@ static int codeAllEqualityTerms(
#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
/*
** If the most recently coded instruction is a constant range constraint
-** (a string literal) that originated from the LIKE optimization, then
+** (a string literal) that originated from the LIKE optimization, then
** set P3 and P5 on the OP_String opcode so that the string will be cast
** to a BLOB at appropriate times.
**
@@ -136168,9 +157024,9 @@ static void whereLikeOptimizationStringFixup(
if( pTerm->wtFlags & TERM_LIKEOPT ){
VdbeOp *pOp;
assert( pLevel->iLikeRepCntr>0 );
- pOp = sqlite3VdbeGetOp(v, -1);
+ pOp = sqlite3VdbeGetLastOp(v);
assert( pOp!=0 );
- assert( pOp->opcode==OP_String8
+ assert( pOp->opcode==OP_String8
|| pTerm->pWC->pWInfo->pParse->db->mallocFailed );
pOp->p3 = (int)(pLevel->iLikeRepCntr>>1); /* Register holding counter */
pOp->p5 = (u8)(pLevel->iLikeRepCntr&1); /* ASC or DESC */
@@ -136203,7 +157059,7 @@ static int codeCursorHintCheckExpr(Walker *pWalker, Expr *pExpr){
assert( pHint->pIdx!=0 );
if( pExpr->op==TK_COLUMN
&& pExpr->iTable==pHint->iTabCur
- && sqlite3ColumnOfIndex(pHint->pIdx, pExpr->iColumn)<0
+ && sqlite3TableColumnToIndex(pHint->pIdx, pExpr->iColumn)<0
){
pWalker->eCode = 1;
}
@@ -136213,7 +157069,7 @@ static int codeCursorHintCheckExpr(Walker *pWalker, Expr *pExpr){
/*
** Test whether or not expression pExpr, which was part of a WHERE clause,
** should be included in the cursor-hint for a table that is on the rhs
-** of a LEFT JOIN. Set Walker.eCode to non-zero before returning if the
+** of a LEFT JOIN. Set Walker.eCode to non-zero before returning if the
** expression is not suitable.
**
** An expression is unsuitable if it might evaluate to non NULL even if
@@ -136226,9 +157082,9 @@ static int codeCursorHintCheckExpr(Walker *pWalker, Expr *pExpr){
** CASE WHEN col THEN 0 ELSE 1 END
*/
static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){
- if( pExpr->op==TK_IS
- || pExpr->op==TK_ISNULL || pExpr->op==TK_ISNOT
- || pExpr->op==TK_NOTNULL || pExpr->op==TK_CASE
+ if( pExpr->op==TK_IS
+ || pExpr->op==TK_ISNULL || pExpr->op==TK_ISNOT
+ || pExpr->op==TK_NOTNULL || pExpr->op==TK_CASE
){
pWalker->eCode = 1;
}else if( pExpr->op==TK_FUNCTION ){
@@ -136249,40 +157105,41 @@ static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){
** that accesses any table other than the one identified by
** CCurHint.iTabCur, then do the following:
**
-** 1) allocate a register and code an OP_Column instruction to read
+** 1) allocate a register and code an OP_Column instruction to read
** the specified column into the new register, and
**
-** 2) transform the expression node to a TK_REGISTER node that reads
+** 2) transform the expression node to a TK_REGISTER node that reads
** from the newly populated register.
**
-** Also, if the node is a TK_COLUMN that does access the table idenified
+** Also, if the node is a TK_COLUMN that does access the table identified
** by pCCurHint.iTabCur, and an index is being used (which we will
** know because CCurHint.pIdx!=0) then transform the TK_COLUMN into
** an access of the index rather than the original table.
*/
static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
int rc = WRC_Continue;
+ int reg;
struct CCurHint *pHint = pWalker->u.pCCurHint;
if( pExpr->op==TK_COLUMN ){
if( pExpr->iTable!=pHint->iTabCur ){
- int reg = ++pWalker->pParse->nMem; /* Register for column value */
- sqlite3ExprCode(pWalker->pParse, pExpr, reg);
+ reg = ++pWalker->pParse->nMem; /* Register for column value */
+ reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg);
pExpr->op = TK_REGISTER;
pExpr->iTable = reg;
}else if( pHint->pIdx!=0 ){
pExpr->iTable = pHint->iIdxCur;
- pExpr->iColumn = sqlite3ColumnOfIndex(pHint->pIdx, pExpr->iColumn);
+ pExpr->iColumn = sqlite3TableColumnToIndex(pHint->pIdx, pExpr->iColumn);
assert( pExpr->iColumn>=0 );
}
- }else if( pExpr->op==TK_AGG_FUNCTION ){
- /* An aggregate function in the WHERE clause of a query means this must
- ** be a correlated sub-query, and expression pExpr is an aggregate from
- ** the parent context. Do not walk the function arguments in this case.
- **
- ** todo: It should be possible to replace this node with a TK_REGISTER
- ** expression, as the result of the expression must be stored in a
- ** register at this point. The same holds for TK_AGG_COLUMN nodes. */
+ }else if( pExpr->pAggInfo ){
rc = WRC_Prune;
+ reg = ++pWalker->pParse->nMem; /* Register for column value */
+ reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg);
+ pExpr->op = TK_REGISTER;
+ pExpr->iTable = reg;
+ }else if( pExpr->op==TK_TRUEFALSE ){
+ /* Do not walk disabled expressions. tag-20230504-1 */
+ return WRC_Prune;
}
return rc;
}
@@ -136291,7 +157148,7 @@ static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
** Insert an OP_CursorHint instruction if it is appropriate to do so.
*/
static void codeCursorHint(
- struct SrcList_item *pTabItem, /* FROM clause item */
+ SrcItem *pTabItem, /* FROM clause item */
WhereInfo *pWInfo, /* The where clause */
WhereLevel *pLevel, /* Which loop to provide hints for */
WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */
@@ -136318,23 +157175,23 @@ static void codeCursorHint(
sWalker.pParse = pParse;
sWalker.u.pCCurHint = &sHint;
pWC = &pWInfo->sWC;
- for(i=0; i<pWC->nTerm; i++){
+ for(i=0; i<pWC->nBase; i++){
pTerm = &pWC->a[i];
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
if( pTerm->prereqAll & pLevel->notReady ) continue;
- /* Any terms specified as part of the ON(...) clause for any LEFT
+ /* Any terms specified as part of the ON(...) clause for any LEFT
** JOIN for which the current table is not the rhs are omitted
- ** from the cursor-hint.
+ ** from the cursor-hint.
**
- ** If this table is the rhs of a LEFT JOIN, "IS" or "IS NULL" terms
+ ** If this table is the rhs of a LEFT JOIN, "IS" or "IS NULL" terms
** that were specified as part of the WHERE clause must be excluded.
** This is to address the following:
**
** SELECT ... t1 LEFT JOIN t2 ON (t1.a=t2.b) WHERE t2.c IS NULL;
**
** Say there is a single row in t2 that matches (t1.a=t2.b), but its
- ** t2.c values is not NULL. If the (t2.c IS NULL) constraint is
+ ** t2.c values is not NULL. If the (t2.c IS NULL) constraint is
** pushed down to the cursor, this row is filtered out, causing
** SQLite to synthesize a row of NULL values. Which does match the
** WHERE clause, and so the query returns a row. Which is incorrect.
@@ -136347,8 +157204,8 @@ static void codeCursorHint(
*/
if( pTabItem->fg.jointype & JT_LEFT ){
Expr *pExpr = pTerm->pExpr;
- if( !ExprHasProperty(pExpr, EP_FromJoin)
- || pExpr->iRightJoinTable!=pTabItem->iCursor
+ if( !ExprHasProperty(pExpr, EP_OuterON)
+ || pExpr->w.iJoin!=pTabItem->iCursor
){
sWalker.eCode = 0;
sWalker.xExprCallback = codeCursorHintIsOrFunction;
@@ -136356,7 +157213,7 @@ static void codeCursorHint(
if( sWalker.eCode ) continue;
}
}else{
- if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue;
+ if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) continue;
}
/* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize
@@ -136380,12 +157237,12 @@ static void codeCursorHint(
}
/* If we survive all prior tests, that means this term is worth hinting */
- pExpr = sqlite3ExprAnd(db, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0));
+ pExpr = sqlite3ExprAnd(pParse, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0));
}
if( pExpr!=0 ){
sWalker.xExprCallback = codeCursorHintFixExpr;
- sqlite3WalkExpr(&sWalker, pExpr);
- sqlite3VdbeAddOp4(v, OP_CursorHint,
+ if( pParse->nErr==0 ) sqlite3WalkExpr(&sWalker, pExpr);
+ sqlite3VdbeAddOp4(v, OP_CursorHint,
(sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0,
(const char*)pExpr, P4_EXPR);
}
@@ -136397,20 +157254,28 @@ static void codeCursorHint(
/*
** Cursor iCur is open on an intkey b-tree (a table). Register iRowid contains
** a rowid value just read from cursor iIdxCur, open on index pIdx. This
-** function generates code to do a deferred seek of cursor iCur to the
+** function generates code to do a deferred seek of cursor iCur to the
** rowid stored in register iRowid.
**
** Normally, this is just:
**
** OP_DeferredSeek $iCur $iRowid
**
+** Which causes a seek on $iCur to the row with rowid $iRowid.
+**
** However, if the scan currently being coded is a branch of an OR-loop and
-** the statement currently being coded is a SELECT, then P3 of OP_DeferredSeek
-** is set to iIdxCur and P4 is set to point to an array of integers
-** containing one entry for each column of the table cursor iCur is open
-** on. For each table column, if the column is the i'th column of the
-** index, then the corresponding array entry is set to (i+1). If the column
-** does not appear in the index at all, the array entry is set to 0.
+** the statement currently being coded is a SELECT, then additional information
+** is added that might allow OP_Column to omit the seek and instead do its
+** lookup on the index, thus avoiding an expensive seek operation. To
+** enable this optimization, the P3 of OP_DeferredSeek is set to iIdxCur
+** and P4 is set to an array of integers containing one entry for each column
+** in the table. For each table column, if the column is the i'th
+** column of the index, then the corresponding array entry is set to (i+1).
+** If the column does not appear in the index at all, the array entry is set
+** to 0. The OP_Column opcode can check this array to see if the column it
+** wants is in the index and if it is, it will substitute the index cursor
+** and column number and continue with those new values, rather than seeking
+** the table cursor.
*/
static void codeDeferredSeek(
WhereInfo *pWInfo, /* Where clause context */
@@ -136423,19 +157288,24 @@ static void codeDeferredSeek(
assert( iIdxCur>0 );
assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 );
-
+
+ pWInfo->bDeferredSeek = 1;
sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur);
- if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
+ if( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))
&& DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask)
){
int i;
Table *pTab = pIdx->pTable;
- int *ai = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*(pTab->nCol+1));
+ u32 *ai = (u32*)sqlite3DbMallocZero(pParse->db, sizeof(u32)*(pTab->nCol+1));
if( ai ){
ai[0] = pTab->nCol;
for(i=0; i<pIdx->nColumn-1; i++){
+ int x1, x2;
assert( pIdx->aiColumn[i]<pTab->nCol );
- if( pIdx->aiColumn[i]>=0 ) ai[pIdx->aiColumn[i]+1] = i+1;
+ x1 = pIdx->aiColumn[i];
+ x2 = sqlite3TableColumnToStorage(pTab, x1);
+ testcase( x1!=x2 );
+ if( x1>=0 ) ai[x2+1] = i+1;
}
sqlite3VdbeChangeP4(v, -1, (char*)ai, P4_INTARRAY);
}
@@ -136455,86 +157325,119 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
assert( nReg>0 );
if( p && sqlite3ExprIsVector(p) ){
#ifndef SQLITE_OMIT_SUBQUERY
- if( (p->flags & EP_xIsSelect) ){
+ if( ExprUseXSelect(p) ){
Vdbe *v = pParse->pVdbe;
- int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0);
+ int iSelect;
+ assert( p->op==TK_SELECT );
+ iSelect = sqlite3CodeSubselect(pParse, p);
sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1);
}else
#endif
{
int i;
- ExprList *pList = p->x.pList;
+ const ExprList *pList;
+ assert( ExprUseXList(p) );
+ pList = p->x.pList;
assert( nReg<=pList->nExpr );
for(i=0; i<nReg; i++){
sqlite3ExprCode(pParse, pList->a[i].pExpr, iReg+i);
}
}
}else{
- assert( nReg==1 );
+ assert( nReg==1 || pParse->nErr );
sqlite3ExprCode(pParse, p, iReg);
}
}
-/* An instance of the IdxExprTrans object carries information about a
-** mapping from an expression on table columns into a column in an index
-** down through the Walker.
-*/
-typedef struct IdxExprTrans {
- Expr *pIdxExpr; /* The index expression */
- int iTabCur; /* The cursor of the corresponding table */
- int iIdxCur; /* The cursor for the index */
- int iIdxCol; /* The column for the index */
-} IdxExprTrans;
-
-/* The walker node callback used to transform matching expressions into
-** a reference to an index column for an index on an expression.
-**
-** If pExpr matches, then transform it into a reference to the index column
-** that contains the value of pExpr.
+/*
+** The pTruth expression is always true because it is the WHERE clause
+** a partial index that is driving a query loop. Look through all of the
+** WHERE clause terms on the query, and if any of those terms must be
+** true because pTruth is true, then mark those WHERE clause terms as
+** coded.
*/
-static int whereIndexExprTransNode(Walker *p, Expr *pExpr){
- IdxExprTrans *pX = p->u.pIdxTrans;
- if( sqlite3ExprCompare(0, pExpr, pX->pIdxExpr, pX->iTabCur)==0 ){
- pExpr->op = TK_COLUMN;
- pExpr->iTable = pX->iIdxCur;
- pExpr->iColumn = pX->iIdxCol;
- pExpr->y.pTab = 0;
- return WRC_Prune;
- }else{
- return WRC_Continue;
+static void whereApplyPartialIndexConstraints(
+ Expr *pTruth,
+ int iTabCur,
+ WhereClause *pWC
+){
+ int i;
+ WhereTerm *pTerm;
+ while( pTruth->op==TK_AND ){
+ whereApplyPartialIndexConstraints(pTruth->pLeft, iTabCur, pWC);
+ pTruth = pTruth->pRight;
+ }
+ for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
+ Expr *pExpr;
+ if( pTerm->wtFlags & TERM_CODED ) continue;
+ pExpr = pTerm->pExpr;
+ if( sqlite3ExprCompare(0, pExpr, pTruth, iTabCur)==0 ){
+ pTerm->wtFlags |= TERM_CODED;
+ }
}
}
/*
-** For an indexes on expression X, locate every instance of expression X
-** in pExpr and change that subexpression into a reference to the appropriate
-** column of the index.
+** This routine is called right after An OP_Filter has been generated and
+** before the corresponding index search has been performed. This routine
+** checks to see if there are additional Bloom filters in inner loops that
+** can be checked prior to doing the index lookup. If there are available
+** inner-loop Bloom filters, then evaluate those filters now, before the
+** index lookup. The idea is that a Bloom filter check is way faster than
+** an index lookup, and the Bloom filter might return false, meaning that
+** the index lookup can be skipped.
+**
+** We know that an inner loop uses a Bloom filter because it has the
+** WhereLevel.regFilter set. If an inner-loop Bloom filter is checked,
+** then clear the WhereLevel.regFilter value to prevent the Bloom filter
+** from being checked a second time when the inner loop is evaluated.
*/
-static void whereIndexExprTrans(
- Index *pIdx, /* The Index */
- int iTabCur, /* Cursor of the table that is being indexed */
- int iIdxCur, /* Cursor of the index itself */
- WhereInfo *pWInfo /* Transform expressions in this WHERE clause */
+static SQLITE_NOINLINE void filterPullDown(
+ Parse *pParse, /* Parsing context */
+ WhereInfo *pWInfo, /* Complete information about the WHERE clause */
+ int iLevel, /* Which level of pWInfo->a[] should be coded */
+ int addrNxt, /* Jump here to bypass inner loops */
+ Bitmask notReady /* Loops that are not ready */
){
- int iIdxCol; /* Column number of the index */
- ExprList *aColExpr; /* Expressions that are indexed */
- Walker w;
- IdxExprTrans x;
- aColExpr = pIdx->aColExpr;
- if( aColExpr==0 ) return; /* Not an index on expressions */
- memset(&w, 0, sizeof(w));
- w.xExprCallback = whereIndexExprTransNode;
- w.u.pIdxTrans = &x;
- x.iTabCur = iTabCur;
- x.iIdxCur = iIdxCur;
- for(iIdxCol=0; iIdxCol<aColExpr->nExpr; iIdxCol++){
- if( pIdx->aiColumn[iIdxCol]!=XN_EXPR ) continue;
- assert( aColExpr->a[iIdxCol].pExpr!=0 );
- x.iIdxCol = iIdxCol;
- x.pIdxExpr = aColExpr->a[iIdxCol].pExpr;
- sqlite3WalkExpr(&w, pWInfo->pWhere);
- sqlite3WalkExprList(&w, pWInfo->pOrderBy);
- sqlite3WalkExprList(&w, pWInfo->pResultSet);
+ while( ++iLevel < pWInfo->nLevel ){
+ WhereLevel *pLevel = &pWInfo->a[iLevel];
+ WhereLoop *pLoop = pLevel->pWLoop;
+ if( pLevel->regFilter==0 ) continue;
+ if( pLevel->pWLoop->nSkip ) continue;
+ /* ,--- Because sqlite3ConstructBloomFilter() has will not have set
+ ** vvvvv--' pLevel->regFilter if this were true. */
+ if( NEVER(pLoop->prereq & notReady) ) continue;
+ assert( pLevel->addrBrk==0 );
+ pLevel->addrBrk = addrNxt;
+ if( pLoop->wsFlags & WHERE_IPK ){
+ WhereTerm *pTerm = pLoop->aLTerm[0];
+ int regRowid;
+ assert( pTerm!=0 );
+ assert( pTerm->pExpr!=0 );
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ regRowid = sqlite3GetTempReg(pParse);
+ regRowid = codeEqualityTerm(pParse, pTerm, pLevel, 0, 0, regRowid);
+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_MustBeInt, regRowid, addrNxt);
+ VdbeCoverage(pParse->pVdbe);
+ sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter,
+ addrNxt, regRowid, 1);
+ VdbeCoverage(pParse->pVdbe);
+ }else{
+ u16 nEq = pLoop->u.btree.nEq;
+ int r1;
+ char *zStartAff;
+
+ assert( pLoop->wsFlags & WHERE_INDEXED );
+ assert( (pLoop->wsFlags & WHERE_COLUMN_IN)==0 );
+ r1 = codeAllEqualityTerms(pParse,pLevel,0,0,&zStartAff);
+ codeApplyAffinity(pParse, r1, nEq, zStartAff);
+ sqlite3DbFree(pParse->db, zStartAff);
+ sqlite3VdbeAddOp4Int(pParse->pVdbe, OP_Filter, pLevel->regFilter,
+ addrNxt, r1, nEq);
+ VdbeCoverage(pParse->pVdbe);
+ }
+ pLevel->regFilter = 0;
+ pLevel->addrBrk = 0;
}
}
@@ -136543,23 +157446,22 @@ static void whereIndexExprTrans(
** implementation described by pWInfo.
*/
SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
+ Parse *pParse, /* Parsing context */
+ Vdbe *v, /* Prepared statement under construction */
WhereInfo *pWInfo, /* Complete information about the WHERE clause */
int iLevel, /* Which level of pWInfo->a[] should be coded */
+ WhereLevel *pLevel, /* The current level pointer */
Bitmask notReady /* Which tables are currently available */
){
int j, k; /* Loop counters */
int iCur; /* The VDBE cursor for the table */
int addrNxt; /* Where to jump to continue with the next IN case */
- int omitTable; /* True if we use the index only */
int bRev; /* True if we need to scan in reverse order */
- WhereLevel *pLevel; /* The where level to be coded */
WhereLoop *pLoop; /* The WhereLoop object being coded */
WhereClause *pWC; /* Decomposition of the entire WHERE clause */
WhereTerm *pTerm; /* A WHERE clause term */
- Parse *pParse; /* Parsing context */
sqlite3 *db; /* Database connection */
- Vdbe *v; /* The prepared stmt under constructions */
- struct SrcList_item *pTabItem; /* FROM clause term being coded */
+ SrcItem *pTabItem; /* FROM clause term being coded */
int addrBrk; /* Jump here to break out of the loop */
int addrHalt; /* addrBrk for the outermost loop */
int addrCont; /* Jump here to continue with next cycle */
@@ -136568,19 +157470,31 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
Index *pIdx = 0; /* Index used by loop (if any) */
int iLoop; /* Iteration of constraint generator loop */
- pParse = pWInfo->pParse;
- v = pParse->pVdbe;
pWC = &pWInfo->sWC;
db = pParse->db;
- pLevel = &pWInfo->a[iLevel];
pLoop = pLevel->pWLoop;
pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
iCur = pTabItem->iCursor;
pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur);
bRev = (pWInfo->revMask>>iLevel)&1;
- omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0
- && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0;
VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName));
+#if WHERETRACE_ENABLED /* 0x4001 */
+ if( sqlite3WhereTrace & 0x1 ){
+ sqlite3DebugPrintf("Coding level %d of %d: notReady=%llx iFrom=%d\n",
+ iLevel, pWInfo->nLevel, (u64)notReady, pLevel->iFrom);
+ if( sqlite3WhereTrace & 0x1000 ){
+ sqlite3WhereLoopPrint(pLoop, pWC);
+ }
+ }
+ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){
+ if( iLevel==0 ){
+ sqlite3DebugPrintf("WHERE clause being coded:\n");
+ sqlite3TreeViewExpr(0, pWInfo->pWhere, 0);
+ }
+ sqlite3DebugPrintf("All WHERE-clause terms before coding:\n");
+ sqlite3WhereClausePrint(pWC);
+ }
+#endif
/* Create labels for the "break" and "continue" instructions
** for the current loop. Jump to addrBrk to break out of a loop.
@@ -136592,14 +157506,14 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
** there are no IN operators in the constraints, the "addrNxt" label
** is the same as "addrBrk".
*/
- addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
- addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(v);
+ addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse);
+ addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(pParse);
/* If this is the right table of a LEFT OUTER JOIN, allocate and
** initialize a memory cell that records if this table matches any
** row of the left table of the join.
*/
- assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
+ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))
|| pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0
);
if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
@@ -136610,7 +157524,10 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
/* Compute a safe address to jump to if we discover that the table for
** this loop is empty and can never contribute content. */
- for(j=iLevel; j>0 && pWInfo->a[j].iLeftJoin==0; j--){}
+ for(j=iLevel; j>0; j--){
+ if( pWInfo->a[j].iLeftJoin ) break;
+ if( pWInfo->a[j].pRJ ) break;
+ }
addrHalt = pWInfo->a[j].addrBrk;
/* Special case of a FROM clause subquery implemented as a co-routine */
@@ -136631,7 +157548,6 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
int iReg; /* P3 Value for OP_VFilter */
int addrNotFound;
int nConstraint = pLoop->nLTerm;
- int iIn; /* Counter for IN constraints */
iReg = sqlite3GetTempRange(pParse, nConstraint+2);
addrNotFound = pLevel->addrBrk;
@@ -136640,11 +157556,27 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
pTerm = pLoop->aLTerm[j];
if( NEVER(pTerm==0) ) continue;
if( pTerm->eOperator & WO_IN ){
- codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
- addrNotFound = pLevel->addrNxt;
+ if( SMASKBIT32(j) & pLoop->u.vtab.mHandleIn ){
+ int iTab = pParse->nTab++;
+ int iCache = ++pParse->nMem;
+ sqlite3CodeRhsOfIN(pParse, pTerm->pExpr, iTab);
+ sqlite3VdbeAddOp3(v, OP_VInitIn, iTab, iTarget, iCache);
+ }else{
+ codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
+ addrNotFound = pLevel->addrNxt;
+ }
}else{
Expr *pRight = pTerm->pExpr->pRight;
codeExprOrVector(pParse, pRight, iTarget, 1);
+ if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET
+ && pLoop->u.vtab.bOmitOffset
+ ){
+ assert( pTerm->eOperator==WO_AUX );
+ assert( pWInfo->pSelect!=0 );
+ assert( pWInfo->pSelect->iOffset>0 );
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWInfo->pSelect->iOffset);
+ VdbeComment((v,"Zero OFFSET counter"));
+ }
}
}
sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg);
@@ -136654,50 +157586,74 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
pLoop->u.vtab.needFree ? P4_DYNAMIC : P4_STATIC);
VdbeCoverage(v);
pLoop->u.vtab.needFree = 0;
+ /* An OOM inside of AddOp4(OP_VFilter) instruction above might have freed
+ ** the u.vtab.idxStr. NULL it out to prevent a use-after-free */
+ if( db->mallocFailed ) pLoop->u.vtab.idxStr = 0;
pLevel->p1 = iCur;
pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext;
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
- iIn = pLevel->u.in.nIn;
- for(j=nConstraint-1; j>=0; j--){
+ assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
+
+ for(j=0; j<nConstraint; j++){
pTerm = pLoop->aLTerm[j];
if( j<16 && (pLoop->u.vtab.omitMask>>j)&1 ){
disableTerm(pLevel, pTerm);
- }else if( (pTerm->eOperator & WO_IN)!=0 ){
+ continue;
+ }
+ if( (pTerm->eOperator & WO_IN)!=0
+ && (SMASKBIT32(j) & pLoop->u.vtab.mHandleIn)==0
+ && !db->mallocFailed
+ ){
Expr *pCompare; /* The comparison operator */
Expr *pRight; /* RHS of the comparison */
VdbeOp *pOp; /* Opcode to access the value of the IN constraint */
+ int iIn; /* IN loop corresponding to the j-th constraint */
/* Reload the constraint value into reg[iReg+j+2]. The same value
** was loaded into the same register prior to the OP_VFilter, but
** the xFilter implementation might have changed the datatype or
- ** encoding of the value in the register, so it *must* be reloaded. */
- assert( pLevel->u.in.aInLoop!=0 || db->mallocFailed );
- if( !db->mallocFailed ){
- assert( iIn>0 );
- pOp = sqlite3VdbeGetOp(v, pLevel->u.in.aInLoop[--iIn].addrInTop);
- assert( pOp->opcode==OP_Column || pOp->opcode==OP_Rowid );
- assert( pOp->opcode!=OP_Column || pOp->p3==iReg+j+2 );
- assert( pOp->opcode!=OP_Rowid || pOp->p2==iReg+j+2 );
- testcase( pOp->opcode==OP_Rowid );
- sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3);
+ ** encoding of the value in the register, so it *must* be reloaded.
+ */
+ for(iIn=0; ALWAYS(iIn<pLevel->u.in.nIn); iIn++){
+ pOp = sqlite3VdbeGetOp(v, pLevel->u.in.aInLoop[iIn].addrInTop);
+ if( (pOp->opcode==OP_Column && pOp->p3==iReg+j+2)
+ || (pOp->opcode==OP_Rowid && pOp->p2==iReg+j+2)
+ ){
+ testcase( pOp->opcode==OP_Rowid );
+ sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3);
+ break;
+ }
}
- /* Generate code that will continue to the next row if
- ** the IN constraint is not satisfied */
+ /* Generate code that will continue to the next row if
+ ** the IN constraint is not satisfied
+ */
pCompare = sqlite3PExpr(pParse, TK_EQ, 0, 0);
- assert( pCompare!=0 || db->mallocFailed );
- if( pCompare ){
- pCompare->pLeft = pTerm->pExpr->pLeft;
+ if( !db->mallocFailed ){
+ int iFld = pTerm->u.x.iField;
+ Expr *pLeft = pTerm->pExpr->pLeft;
+ assert( pLeft!=0 );
+ if( iFld>0 ){
+ assert( pLeft->op==TK_VECTOR );
+ assert( ExprUseXList(pLeft) );
+ assert( iFld<=pLeft->x.pList->nExpr );
+ pCompare->pLeft = pLeft->x.pList->a[iFld-1].pExpr;
+ }else{
+ pCompare->pLeft = pLeft;
+ }
pCompare->pRight = pRight = sqlite3Expr(db, TK_REGISTER, 0);
if( pRight ){
pRight->iTable = iReg+j+2;
- sqlite3ExprIfFalse(pParse, pCompare, pLevel->addrCont, 0);
+ sqlite3ExprIfFalse(
+ pParse, pCompare, pLevel->addrCont, SQLITE_JUMPIFNULL
+ );
}
pCompare->pLeft = 0;
- sqlite3ExprDelete(db, pCompare);
}
+ sqlite3ExprDelete(db, pCompare);
}
}
+
/* These registers need to be preserved in case there is an IN operator
** loop. So we could deallocate the registers here (and potentially
** reuse them later) if (pLoop->wsFlags & WHERE_IN_ABLE)==0. But it seems
@@ -136720,12 +157676,19 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
pTerm = pLoop->aLTerm[0];
assert( pTerm!=0 );
assert( pTerm->pExpr!=0 );
- assert( omitTable==0 );
testcase( pTerm->wtFlags & TERM_VIRTUAL );
iReleaseReg = ++pParse->nMem;
iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg);
addrNxt = pLevel->addrNxt;
+ if( pLevel->regFilter ){
+ sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt,
+ iRowidReg, 1);
+ VdbeCoverage(v);
+ filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady);
+ }
sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
VdbeCoverage(v);
pLevel->op = OP_Noop;
@@ -136739,7 +157702,6 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
int memEndValue = 0;
WhereTerm *pStart, *pEnd;
- assert( omitTable==0 );
j = 0;
pStart = pEnd = 0;
if( pLoop->wsFlags & WHERE_BTM_LIMIT ) pStart = pLoop->aLTerm[j++];
@@ -136756,7 +157718,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
int r1, rTemp; /* Registers for holding the start boundary */
int op; /* Cursor seek operation */
- /* The following constant maps TK_xx codes into corresponding
+ /* The following constant maps TK_xx codes into corresponding
** seek opcodes. It depends on a particular ordering of TK_xx
*/
const u8 aMoveOp[] = {
@@ -136767,7 +157729,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
};
assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */
assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */
- assert( TK_GE==TK_GT+3 ); /* ... is correcct. */
+ assert( TK_GE==TK_GT+3 ); /* ... is correct. */
assert( (pStart->wtFlags & TERM_VNULL)==0 );
testcase( pStart->wtFlags & TERM_VIRTUAL );
@@ -136812,8 +157774,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
testcase( pEnd->wtFlags & TERM_VIRTUAL );
memEndValue = ++pParse->nMem;
codeExprOrVector(pParse, pX->pRight, memEndValue, 1);
- if( 0==sqlite3ExprIsVector(pX->pRight)
- && (pX->op==TK_LT || pX->op==TK_GT)
+ if( 0==sqlite3ExprIsVector(pX->pRight)
+ && (pX->op==TK_LT || pX->op==TK_GT)
){
testOp = bRev ? OP_Le : OP_Ge;
}else{
@@ -136841,14 +157803,14 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}else if( pLoop->wsFlags & WHERE_INDEXED ){
/* Case 4: A scan using an index.
**
- ** The WHERE clause may contain zero or more equality
+ ** The WHERE clause may contain zero or more equality
** terms ("==" or "IN" operators) that refer to the N
** left-most columns of the index. It may also contain
** inequality constraints (>, <, >= or <=) on the indexed
- ** column that immediately follows the N equalities. Only
+ ** column that immediately follows the N equalities. Only
** the right-most column can be an inequality - the rest must
- ** use the "==" and "IN" operators. For example, if the
- ** index is on (x,y,z), then the following clauses are all
+ ** use the "==" and "IN" operators. For example, if the
+ ** index is on (x,y,z), then the following clauses are all
** optimized:
**
** x=5
@@ -136869,7 +157831,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
** This case is also used when there are no WHERE clause
** constraints but an index is selected anyway, in order
** to force the output order to conform to an ORDER BY.
- */
+ */
static const u8 aStartOp[] = {
0,
0,
@@ -136903,40 +157865,23 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
char *zEndAff = 0; /* Affinity for end of range constraint */
u8 bSeekPastNull = 0; /* True to seek past initial nulls */
u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */
+ int omitTable; /* True if we use the index only */
+ int regBignull = 0; /* big-null flag register */
+ int addrSeekScan = 0; /* Opcode of the OP_SeekScan, if any */
pIdx = pLoop->u.btree.pIndex;
iIdxCur = pLevel->iIdxCur;
assert( nEq>=pLoop->nSkip );
- /* If this loop satisfies a sort order (pOrderBy) request that
- ** was passed to this function to implement a "SELECT min(x) ..."
- ** query, then the caller will only allow the loop to run for
- ** a single iteration. This means that the first row returned
- ** should not have a NULL value stored in 'x'. If column 'x' is
- ** the first one after the nEq equality constraints in the index,
- ** this requires some special handling.
- */
- assert( pWInfo->pOrderBy==0
- || pWInfo->pOrderBy->nExpr==1
- || (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 );
- if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0
- && pWInfo->nOBSat>0
- && (pIdx->nKeyCol>nEq)
- ){
- assert( pLoop->nSkip==0 );
- bSeekPastNull = 1;
- nExtraReg = 1;
- }
-
- /* Find any inequality constraint terms for the start and end
- ** of the range.
+ /* Find any inequality constraint terms for the start and end
+ ** of the range.
*/
j = nEq;
if( pLoop->wsFlags & WHERE_BTM_LIMIT ){
pRangeStart = pLoop->aLTerm[j++];
nExtraReg = MAX(nExtraReg, pLoop->u.btree.nBtm);
/* Like optimization range constraints always occur in pairs */
- assert( (pRangeStart->wtFlags & TERM_LIKEOPT)==0 ||
+ assert( (pRangeStart->wtFlags & TERM_LIKEOPT)==0 ||
(pLoop->wsFlags & WHERE_TOP_LIMIT)!=0 );
}
if( pLoop->wsFlags & WHERE_TOP_LIMIT ){
@@ -136968,18 +157913,44 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 );
+ /* If the WHERE_BIGNULL_SORT flag is set, then index column nEq uses
+ ** a non-default "big-null" sort (either ASC NULLS LAST or DESC NULLS
+ ** FIRST). In both cases separate ordered scans are made of those
+ ** index entries for which the column is null and for those for which
+ ** it is not. For an ASC sort, the non-NULL entries are scanned first.
+ ** For DESC, NULL entries are scanned first.
+ */
+ if( (pLoop->wsFlags & (WHERE_TOP_LIMIT|WHERE_BTM_LIMIT))==0
+ && (pLoop->wsFlags & WHERE_BIGNULL_SORT)!=0
+ ){
+ assert( bSeekPastNull==0 && nExtraReg==0 && nBtm==0 && nTop==0 );
+ assert( pRangeEnd==0 && pRangeStart==0 );
+ testcase( pLoop->nSkip>0 );
+ nExtraReg = 1;
+ bSeekPastNull = 1;
+ pLevel->regBignull = regBignull = ++pParse->nMem;
+ if( pLevel->iLeftJoin ){
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regBignull);
+ }
+ pLevel->addrBignull = sqlite3VdbeMakeLabel(pParse);
+ }
+
/* If we are doing a reverse order scan on an ascending index, or
- ** a forward order scan on a descending index, interchange the
+ ** a forward order scan on a descending index, interchange the
** start and end terms (pRangeStart and pRangeEnd).
*/
- if( (nEq<pIdx->nKeyCol && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC))
- || (bRev && pIdx->nKeyCol==nEq)
- ){
+ if( (nEq<pIdx->nColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC)) ){
SWAP(WhereTerm *, pRangeEnd, pRangeStart);
SWAP(u8, bSeekPastNull, bStopAtNull);
SWAP(u8, nBtm, nTop);
}
+ if( iLevel>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 ){
+ /* In case OP_SeekScan is used, ensure that the index cursor does not
+ ** point to a valid row for the first iteration of this loop. */
+ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur);
+ }
+
/* Generate code to evaluate all constraint terms using == or IN
** and store the values of those terms in an array of registers
** starting at regBase.
@@ -136990,7 +157961,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( zStartAff && nTop ){
zEndAff = sqlite3DbStrDup(db, &zStartAff[nEq]);
}
- addrNxt = pLevel->addrNxt;
+ addrNxt = (regBignull ? pLevel->addrBignull : pLevel->addrNxt);
testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
testcase( pRangeStart && (pRangeStart->eOperator & WO_GE)!=0 );
@@ -137014,7 +157985,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
if( zStartAff ){
updateRangeAffinityStr(pRight, nBtm, &zStartAff[nEq]);
- }
+ }
nConstraint += nBtm;
testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
if( sqlite3ExprIsVector(pRight)==0 ){
@@ -137024,10 +157995,14 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
bSeekPastNull = 0;
}else if( bSeekPastNull ){
+ startEq = 0;
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ start_constraints = 1;
nConstraint++;
- startEq = 0;
+ }else if( regBignull ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
start_constraints = 1;
+ nConstraint++;
}
codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull, zStartAff);
if( pLoop->nSkip>0 && nConstraint==pLoop->nSkip ){
@@ -137035,11 +158010,38 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
** above has already left the cursor sitting on the correct row,
** so no further seeking is needed */
}else{
- if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
- sqlite3VdbeAddOp1(v, OP_SeekHit, iIdxCur);
+ if( regBignull ){
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, regBignull);
+ VdbeComment((v, "NULL-scan pass ctr"));
+ }
+ if( pLevel->regFilter ){
+ sqlite3VdbeAddOp4Int(v, OP_Filter, pLevel->regFilter, addrNxt,
+ regBase, nEq);
+ VdbeCoverage(v);
+ filterPullDown(pParse, pWInfo, iLevel, addrNxt, notReady);
}
+
op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
assert( op!=0 );
+ if( (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 && op==OP_SeekGE ){
+ assert( regBignull==0 );
+ /* TUNING: The OP_SeekScan opcode seeks to reduce the number
+ ** of expensive seek operations by replacing a single seek with
+ ** 1 or more step operations. The question is, how many steps
+ ** should we try before giving up and going with a seek. The cost
+ ** of a seek is proportional to the logarithm of the of the number
+ ** of entries in the tree, so basing the number of steps to try
+ ** on the estimated number of rows in the btree seems like a good
+ ** guess. */
+ addrSeekScan = sqlite3VdbeAddOp1(v, OP_SeekScan,
+ (pIdx->aiRowLogEst[0]+9)/10);
+ if( pRangeStart || pRangeEnd ){
+ sqlite3VdbeChangeP5(v, 1);
+ sqlite3VdbeChangeP2(v, addrSeekScan, sqlite3VdbeCurrentAddr(v)+1);
+ addrSeekScan = 0;
+ }
+ VdbeCoverage(v);
+ }
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
VdbeCoverage(v);
VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind );
@@ -137048,14 +158050,33 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE );
VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT );
+
+ assert( bSeekPastNull==0 || bStopAtNull==0 );
+ if( regBignull ){
+ assert( bSeekPastNull==1 || bStopAtNull==1 );
+ assert( bSeekPastNull==!bStopAtNull );
+ assert( bStopAtNull==startEq );
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
+ op = aStartOp[(nConstraint>1)*4 + 2 + bRev];
+ sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase,
+ nConstraint-startEq);
+ VdbeCoverage(v);
+ VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind );
+ VdbeCoverageIf(v, op==OP_Last); testcase( op==OP_Last );
+ VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE );
+ VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
+ assert( op==OP_Rewind || op==OP_Last || op==OP_SeekGE || op==OP_SeekLE);
+ }
}
/* Load the value for the inequality constraint at the end of the
** range (if any).
*/
nConstraint = nEq;
+ assert( pLevel->p2==0 );
if( pRangeEnd ){
Expr *pRight = pRangeEnd->pExpr->pRight;
+ assert( addrSeekScan==0 );
codeExprOrVector(pParse, pRight, regBase+nEq, nTop);
whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd);
if( (pRangeEnd->wtFlags & TERM_VNULL)==0
@@ -137079,68 +158100,91 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
endEq = 1;
}
}else if( bStopAtNull ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
- endEq = 0;
+ if( regBignull==0 ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ endEq = 0;
+ }
nConstraint++;
}
- sqlite3DbFree(db, zStartAff);
- sqlite3DbFree(db, zEndAff);
+ if( zStartAff ) sqlite3DbNNFreeNN(db, zStartAff);
+ if( zEndAff ) sqlite3DbNNFreeNN(db, zEndAff);
/* Top of the loop body */
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
/* Check if the index cursor is past the end of the range. */
if( nConstraint ){
+ if( regBignull ){
+ /* Except, skip the end-of-range check while doing the NULL-scan */
+ sqlite3VdbeAddOp2(v, OP_IfNot, regBignull, sqlite3VdbeCurrentAddr(v)+3);
+ VdbeComment((v, "If NULL-scan 2nd pass"));
+ VdbeCoverage(v);
+ }
op = aEndOp[bRev*2 + endEq];
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE );
testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
+ if( addrSeekScan ) sqlite3VdbeJumpHere(v, addrSeekScan);
+ }
+ if( regBignull ){
+ /* During a NULL-scan, check to see if we have reached the end of
+ ** the NULLs */
+ assert( bSeekPastNull==!bStopAtNull );
+ assert( bSeekPastNull+bStopAtNull==1 );
+ assert( nConstraint+bSeekPastNull>0 );
+ sqlite3VdbeAddOp2(v, OP_If, regBignull, sqlite3VdbeCurrentAddr(v)+2);
+ VdbeComment((v, "If NULL-scan 1st pass"));
+ VdbeCoverage(v);
+ op = aEndOp[bRev*2 + bSeekPastNull];
+ sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase,
+ nConstraint+bSeekPastNull);
+ testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
+ testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE );
+ testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
+ testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
}
- if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
- sqlite3VdbeAddOp2(v, OP_SeekHit, iIdxCur, 1);
+ if( (pLoop->wsFlags & WHERE_IN_EARLYOUT)!=0 ){
+ sqlite3VdbeAddOp3(v, OP_SeekHit, iIdxCur, nEq, nEq);
}
/* Seek the table cursor, if required */
+ omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0
+ && (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0;
if( omitTable ){
/* pIdx is a covering index. No need to access the main table. */
}else if( HasRowid(pIdx->pTable) ){
- if( (pWInfo->wctrlFlags & WHERE_SEEK_TABLE) || (
- (pWInfo->wctrlFlags & WHERE_SEEK_UNIQ_TABLE)
- && (pWInfo->eOnePass==ONEPASS_SINGLE)
- )){
- iRowidReg = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg);
- VdbeCoverage(v);
- }else{
- codeDeferredSeek(pWInfo, pIdx, iCur, iIdxCur);
- }
+ codeDeferredSeek(pWInfo, pIdx, iCur, iIdxCur);
}else if( iCur!=iIdxCur ){
Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol);
for(j=0; j<pPk->nKeyCol; j++){
- k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
+ k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, iRowidReg+j);
}
sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont,
iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
}
- /* If pIdx is an index on one or more expressions, then look through
- ** all the expressions in pWInfo and try to transform matching expressions
- ** into reference to index columns.
- **
- ** Do not do this for the RHS of a LEFT JOIN. This is because the
- ** expression may be evaluated after OP_NullRow has been executed on
- ** the cursor. In this case it is important to do the full evaluation,
- ** as the result of the expression may not be NULL, even if all table
- ** column values are. https://www.sqlite.org/src/info/7fa8049685b50b5a
- */
if( pLevel->iLeftJoin==0 ){
- whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo);
+ /* If a partial index is driving the loop, try to eliminate WHERE clause
+ ** terms from the query that must be true due to the WHERE clause of
+ ** the partial index.
+ **
+ ** 2019-11-02 ticket 623eff57e76d45f6: This optimization does not work
+ ** for a LEFT JOIN.
+ */
+ if( pIdx->pPartIdxWhere ){
+ whereApplyPartialIndexConstraints(pIdx->pPartIdxWhere, iCur, pWC);
+ }
+ }else{
+ testcase( pIdx->pPartIdxWhere );
+ /* The following assert() is not a requirement, merely an observation:
+ ** The OR-optimization doesn't work for the right hand table of
+ ** a LEFT JOIN: */
+ assert( (pWInfo->wctrlFlags & (WHERE_OR_SUBCLAUSE|WHERE_RIGHT_JOIN))==0 );
}
/* Record the instruction used to terminate the loop. */
@@ -137214,11 +158258,10 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
int regRowset = 0; /* Register for RowSet object */
int regRowid = 0; /* Register holding rowid */
- int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
+ int iLoopBody = sqlite3VdbeMakeLabel(pParse);/* Start of loop body */
int iRetInit; /* Address of regReturn init */
int untestedTerms = 0; /* Some terms not completely tested */
int ii; /* Loop counter */
- u16 wctrlFlags; /* Flags for sub-WHERE clause */
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
Table *pTab = pTabItem->pTab;
@@ -137236,9 +158279,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
*/
if( pWInfo->nLevel>1 ){
int nNotReady; /* The number of notReady tables */
- struct SrcList_item *origSrc; /* Original list of tables */
+ SrcItem *origSrc; /* Original list of tables */
nNotReady = pWInfo->nLevel - iLevel - 1;
- pOrTab = sqlite3StackAllocRaw(db,
+ pOrTab = sqlite3DbMallocRawNN(db,
sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
if( pOrTab==0 ) return notReady;
pOrTab->nAlloc = (u8)(nNotReady + 1);
@@ -137252,15 +158295,15 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
pOrTab = pWInfo->pTabList;
}
- /* Initialize the rowset register to contain NULL. An SQL NULL is
+ /* Initialize the rowset register to contain NULL. An SQL NULL is
** equivalent to an empty rowset. Or, create an ephemeral index
** capable of holding primary keys in the case of a WITHOUT ROWID.
**
- ** Also initialize regReturn to contain the address of the instruction
+ ** Also initialize regReturn to contain the address of the instruction
** immediately following the OP_Return at the bottom of the loop. This
** is required in a few obscure LEFT JOIN cases where control jumps
- ** over the top of the loop into the body of it. In this case the
- ** correct response for the end-of-loop code (the OP_Return) is to
+ ** over the top of the loop into the body of it. In this case the
+ ** correct response for the end-of-loop code (the OP_Return) is to
** fall through to the next instruction, just as an OP_Next does if
** called on an uninitialized cursor.
*/
@@ -137279,18 +158322,32 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
/* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y
- ** Then for every term xN, evaluate as the subexpression: xN AND z
+ ** Then for every term xN, evaluate as the subexpression: xN AND y
** That way, terms in y that are factored into the disjunction will
** be picked up by the recursive calls to sqlite3WhereBegin() below.
**
** Actually, each subexpression is converted to "xN AND w" where w is
** the "interesting" terms of z - terms that did not originate in the
- ** ON or USING clause of a LEFT JOIN, and terms that are usable as
+ ** ON or USING clause of a LEFT JOIN, and terms that are usable as
** indices.
**
** This optimization also only applies if the (x1 OR x2 OR ...) term
** is not contained in the ON clause of a LEFT JOIN.
** See ticket http://www.sqlite.org/src/info/f2369304e4
+ **
+ ** 2022-02-04: Do not push down slices of a row-value comparison.
+ ** In other words, "w" or "y" may not be a slice of a vector. Otherwise,
+ ** the initialization of the right-hand operand of the vector comparison
+ ** might not occur, or might occur only in an OR branch that is not
+ ** taken. dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1.
+ **
+ ** 2022-03-03: Do not push down expressions that involve subqueries.
+ ** The subquery might get coded as a subroutine. Any table-references
+ ** in the subquery might be resolved to index-references for the index on
+ ** the OR branch in which the subroutine is coded. But if the subroutine
+ ** is invoked from a different OR branch that uses a different index, such
+ ** index-references will not work. tag-20220303a
+ ** https://sqlite.org/forum/forumpost/36937b197273d403
*/
if( pWC->nTerm>1 ){
int iTerm;
@@ -137299,14 +158356,22 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( &pWC->a[iTerm] == pTerm ) continue;
testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL );
testcase( pWC->a[iTerm].wtFlags & TERM_CODED );
- if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue;
+ testcase( pWC->a[iTerm].wtFlags & TERM_SLICE );
+ if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED|TERM_SLICE))!=0 ){
+ continue;
+ }
if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue;
- testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO );
+ if( ExprHasProperty(pExpr, EP_Subquery) ) continue; /* tag-20220303a */
pExpr = sqlite3ExprDup(db, pExpr, 0);
- pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr);
+ pAndExpr = sqlite3ExprAnd(pParse, pAndExpr, pExpr);
}
if( pAndExpr ){
- pAndExpr = sqlite3PExpr(pParse, TK_AND|TKFLG_DONTFOLD, 0, pAndExpr);
+ /* The extra 0x10000 bit on the opcode is masked off and does not
+ ** become part of the new Expr.op. However, it does make the
+ ** op==TK_AND comparison inside of sqlite3PExpr() false, and this
+ ** prevents sqlite3PExpr() from applying the AND short-circuit
+ ** optimization, which we do not want here. */
+ pAndExpr = sqlite3PExpr(pParse, TK_AND|0x10000, 0, pAndExpr);
}
}
@@ -137314,26 +158379,32 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
** eliminating duplicates from other WHERE clauses, the action for each
** sub-WHERE clause is to to invoke the main loop body as a subroutine.
*/
- wctrlFlags = WHERE_OR_SUBCLAUSE | (pWInfo->wctrlFlags & WHERE_SEEK_TABLE);
ExplainQueryPlan((pParse, 1, "MULTI-INDEX OR"));
for(ii=0; ii<pOrWc->nTerm; ii++){
WhereTerm *pOrTerm = &pOrWc->a[ii];
if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */
+ Expr *pDelete; /* Local copy of OR clause term */
int jmp1 = 0; /* Address of jump operation */
- assert( (pTabItem[0].fg.jointype & JT_LEFT)==0
- || ExprHasProperty(pOrExpr, EP_FromJoin)
- );
+ testcase( (pTabItem[0].fg.jointype & JT_LEFT)!=0
+ && !ExprHasProperty(pOrExpr, EP_OuterON)
+ ); /* See TH3 vtab25.400 and ticket 614b25314c766238 */
+ pDelete = pOrExpr = sqlite3ExprDup(db, pOrExpr, 0);
+ if( db->mallocFailed ){
+ sqlite3ExprDelete(db, pDelete);
+ continue;
+ }
if( pAndExpr ){
pAndExpr->pLeft = pOrExpr;
pOrExpr = pAndExpr;
}
/* Loop through table entries that match term pOrTerm. */
- WHERETRACE(0xffff, ("Subplan for OR-clause:\n"));
- pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
- wctrlFlags, iCovCur);
- assert( pSubWInfo || pParse->nErr || db->mallocFailed );
+ ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1));
+ WHERETRACE(0xffffffff, ("Subplan for OR-clause:\n"));
+ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, 0,
+ WHERE_OR_SUBCLAUSE, iCovCur);
+ assert( pSubWInfo || pParse->nErr );
if( pSubWInfo ){
WhereLoop *pSubLoop;
int addrExplain = sqlite3WhereExplainOneScan(
@@ -137363,7 +158434,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
r = sqlite3GetTempRange(pParse, nPk);
for(iPk=0; iPk<nPk; iPk++){
int iCol = pPk->aiColumn[iPk];
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol, r+iPk);
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+iPk);
}
/* Check if the temp table already contains this key. If so,
@@ -137374,9 +158445,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
**
** Use some of the same optimizations as OP_RowSetTest: If iSet
** is zero, assume that the key cannot already be present in
- ** the temp table. And if iSet is -1, assume that there is no
- ** need to insert the key into the temp table, as it will never
- ** be tested for. */
+ ** the temp table. And if iSet is -1, assume that there is no
+ ** need to insert the key into the temp table, as it will never
+ ** be tested for. */
if( iSet ){
jmp1 = sqlite3VdbeAddOp4Int(v, OP_Found, regRowset, 0, r, nPk);
VdbeCoverage(v);
@@ -137415,8 +158486,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
** If the call to sqlite3WhereBegin() above resulted in a scan that
** uses an index, and this is either the first OR-connected term
** processed or the index is the same as that used by all previous
- ** terms, set pCov to the candidate covering index. Otherwise, set
- ** pCov to NULL to indicate that no candidate covering index will
+ ** terms, set pCov to the candidate covering index. Otherwise, set
+ ** pCov to NULL to indicate that no candidate covering index will
** be available.
*/
pSubLoop = pSubWInfo->a[0].pWLoop;
@@ -137430,14 +158501,22 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}else{
pCov = 0;
}
+ if( sqlite3WhereUsesDeferredSeek(pSubWInfo) ){
+ pWInfo->bDeferredSeek = 1;
+ }
/* Finish the loop through table entries that match term pOrTerm. */
sqlite3WhereEnd(pSubWInfo);
+ ExplainQueryPlanPop(pParse);
}
+ sqlite3ExprDelete(db, pDelete);
}
}
ExplainQueryPlanPop(pParse);
- pLevel->u.pCovidx = pCov;
+ assert( pLevel->pWLoop==pLoop );
+ assert( (pLoop->wsFlags & WHERE_MULTI_OR)!=0 );
+ assert( (pLoop->wsFlags & WHERE_IN_ABLE)==0 );
+ pLevel->u.pCoveringIdx = pCov;
if( pCov ) pLevel->iIdxCur = iCovCur;
if( pAndExpr ){
pAndExpr->pLeft = 0;
@@ -137447,7 +158526,15 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
sqlite3VdbeGoto(v, pLevel->addrBrk);
sqlite3VdbeResolveLabel(v, iLoopBody);
- if( pWInfo->nLevel>1 ) sqlite3StackFree(db, pOrTab);
+ /* Set the P2 operand of the OP_Return opcode that will end the current
+ ** loop to point to this spot, which is the top of the next containing
+ ** loop. The byte-code formatter will use that P2 value as a hint to
+ ** indent everything in between the this point and the final OP_Return.
+ ** See tag-20220407a in vdbe.c and shell.c */
+ assert( pLevel->op==OP_Return );
+ pLevel->p2 = sqlite3VdbeCurrentAddr(v);
+
+ if( pWInfo->nLevel>1 ){ sqlite3DbFreeNN(db, pOrTab); }
if( !untestedTerms ) disableTerm(pLevel, pTerm);
}else
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
@@ -137487,7 +158574,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
**
** iLoop==1: Code only expressions that are entirely covered by pIdx.
** iLoop==2: Code remaining expressions that do not contain correlated
- ** sub-queries.
+ ** sub-queries.
** iLoop==3: Code all remaining expressions.
**
** An effort is made to skip unnecessary iterations of the loop.
@@ -137509,10 +158596,22 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
pE = pTerm->pExpr;
assert( pE!=0 );
- if( (pTabItem->fg.jointype&JT_LEFT) && !ExprHasProperty(pE,EP_FromJoin) ){
- continue;
+ if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ){
+ if( !ExprHasProperty(pE,EP_OuterON|EP_InnerON) ){
+ /* Defer processing WHERE clause constraints until after outer
+ ** join processing. tag-20220513a */
+ continue;
+ }else if( (pTabItem->fg.jointype & JT_LEFT)==JT_LEFT
+ && !ExprHasProperty(pE,EP_OuterON) ){
+ continue;
+ }else{
+ Bitmask m = sqlite3WhereGetMask(&pWInfo->sMaskSet, pE->w.iJoin);
+ if( m & pLevel->notReady ){
+ /* An ON clause that is not ripe */
+ continue;
+ }
+ }
}
-
if( iLoop==1 && !sqlite3ExprCoveredByIndex(pE, pLevel->iTabCur, pIdx) ){
iNext = 2;
continue;
@@ -137534,15 +158633,20 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
u32 x = pLevel->iLikeRepCntr;
if( x>0 ){
skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If,(int)(x>>1));
+ VdbeCoverageIf(v, (x&1)==1);
+ VdbeCoverageIf(v, (x&1)==0);
}
- VdbeCoverage(v);
#endif
}
-#ifdef WHERETRACE_ENABLED /* 0xffff */
+#ifdef WHERETRACE_ENABLED /* 0xffffffff */
if( sqlite3WhereTrace ){
VdbeNoopComment((v, "WhereTerm[%d] (%p) priority=%d",
pWC->nTerm-j, pTerm, iLoop));
}
+ if( sqlite3WhereTrace & 0x4000 ){
+ sqlite3DebugPrintf("Coding auxiliary constraint:\n");
+ sqlite3WhereTermPrint(pTerm, pWC->nTerm-j);
+ }
#endif
sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL);
if( skipLikeAddr ) sqlite3VdbeJumpHere(v, skipLikeAddr);
@@ -137559,23 +158663,30 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
** then we cannot use the "t1.a=t2.b" constraint, but we can code
** the implied "t1.a=123" constraint.
*/
- for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
+ for(pTerm=pWC->a, j=pWC->nBase; j>0; j--, pTerm++){
Expr *pE, sEAlt;
WhereTerm *pAlt;
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue;
if( (pTerm->eOperator & WO_EQUIV)==0 ) continue;
if( pTerm->leftCursor!=iCur ) continue;
- if( pLevel->iLeftJoin ) continue;
+ if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ) continue;
pE = pTerm->pExpr;
- assert( !ExprHasProperty(pE, EP_FromJoin) );
+#ifdef WHERETRACE_ENABLED /* 0x4001 */
+ if( (sqlite3WhereTrace & 0x4001)==0x4001 ){
+ sqlite3DebugPrintf("Coding transitive constraint:\n");
+ sqlite3WhereTermPrint(pTerm, pWC->nTerm-j);
+ }
+#endif
+ assert( !ExprHasProperty(pE, EP_OuterON) );
assert( (pTerm->prereqRight & pLevel->notReady)!=0 );
- pAlt = sqlite3WhereFindTerm(pWC, iCur, pTerm->u.leftColumn, notReady,
+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
+ pAlt = sqlite3WhereFindTerm(pWC, iCur, pTerm->u.x.leftColumn, notReady,
WO_EQ|WO_IN|WO_IS, 0);
if( pAlt==0 ) continue;
if( pAlt->wtFlags & (TERM_CODED) ) continue;
- if( (pAlt->eOperator & WO_IN)
- && (pAlt->pExpr->flags & EP_xIsSelect)
+ if( (pAlt->eOperator & WO_IN)
+ && ExprUseXSelect(pAlt->pExpr)
&& (pAlt->pExpr->x.pSelect->pEList->nExpr>1)
){
continue;
@@ -137587,16 +158698,82 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
sEAlt = *pAlt->pExpr;
sEAlt.pLeft = pE->pLeft;
sqlite3ExprIfFalse(pParse, &sEAlt, addrCont, SQLITE_JUMPIFNULL);
+ pAlt->wtFlags |= TERM_CODED;
+ }
+
+ /* For a RIGHT OUTER JOIN, record the fact that the current row has
+ ** been matched at least once.
+ */
+ if( pLevel->pRJ ){
+ Table *pTab;
+ int nPk;
+ int r;
+ int jmp1 = 0;
+ WhereRightJoin *pRJ = pLevel->pRJ;
+
+ /* pTab is the right-hand table of the RIGHT JOIN. Generate code that
+ ** will record that the current row of that table has been matched at
+ ** least once. This is accomplished by storing the PK for the row in
+ ** both the iMatch index and the regBloom Bloom filter.
+ */
+ pTab = pWInfo->pTabList->a[pLevel->iFrom].pTab;
+ if( HasRowid(pTab) ){
+ r = sqlite3GetTempRange(pParse, 2);
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, pLevel->iTabCur, -1, r+1);
+ nPk = 1;
+ }else{
+ int iPk;
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ nPk = pPk->nKeyCol;
+ r = sqlite3GetTempRange(pParse, nPk+1);
+ for(iPk=0; iPk<nPk; iPk++){
+ int iCol = pPk->aiColumn[iPk];
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+1+iPk);
+ }
+ }
+ jmp1 = sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, 0, r+1, nPk);
+ VdbeCoverage(v);
+ VdbeComment((v, "match against %s", pTab->zName));
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, r+1, nPk, r);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pRJ->iMatch, r, r+1, nPk);
+ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pRJ->regBloom, 0, r+1, nPk);
+ sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+ sqlite3VdbeJumpHere(v, jmp1);
+ sqlite3ReleaseTempRange(pParse, r, nPk+1);
}
/* For a LEFT OUTER JOIN, generate code that will record the fact that
- ** at least one row of the right table has matched the left table.
+ ** at least one row of the right table has matched the left table.
*/
if( pLevel->iLeftJoin ){
pLevel->addrFirst = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin);
VdbeComment((v, "record LEFT JOIN hit"));
- for(pTerm=pWC->a, j=0; j<pWC->nTerm; j++, pTerm++){
+ if( pLevel->pRJ==0 ){
+ goto code_outer_join_constraints; /* WHERE clause constraints */
+ }
+ }
+
+ if( pLevel->pRJ ){
+ /* Create a subroutine used to process all interior loops and code
+ ** of the RIGHT JOIN. During normal operation, the subroutine will
+ ** be in-line with the rest of the code. But at the end, a separate
+ ** loop will run that invokes this subroutine for unmatched rows
+ ** of pTab, with all tables to left begin set to NULL.
+ */
+ WhereRightJoin *pRJ = pLevel->pRJ;
+ sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pRJ->regReturn);
+ pRJ->addrSubrtn = sqlite3VdbeCurrentAddr(v);
+ assert( pParse->withinRJSubrtn < 255 );
+ pParse->withinRJSubrtn++;
+
+ /* WHERE clause constraints must be deferred until after outer join
+ ** row elimination has completed, since WHERE clause constraints apply
+ ** to the results of the OUTER JOIN. The following loop generates the
+ ** appropriate WHERE clause constraint checks. tag-20220513a.
+ */
+ code_outer_join_constraints:
+ for(pTerm=pWC->a, j=0; j<pWC->nBase; j++, pTerm++){
testcase( pTerm->wtFlags & TERM_VIRTUAL );
testcase( pTerm->wtFlags & TERM_CODED );
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
@@ -137604,15 +158781,117 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
assert( pWInfo->untestedTerms );
continue;
}
+ if( pTabItem->fg.jointype & JT_LTORJ ) continue;
assert( pTerm->pExpr );
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
pTerm->wtFlags |= TERM_CODED;
}
}
+#if WHERETRACE_ENABLED /* 0x4001 */
+ if( sqlite3WhereTrace & 0x4000 ){
+ sqlite3DebugPrintf("All WHERE-clause terms after coding level %d:\n",
+ iLevel);
+ sqlite3WhereClausePrint(pWC);
+ }
+ if( sqlite3WhereTrace & 0x1 ){
+ sqlite3DebugPrintf("End Coding level %d: notReady=%llx\n",
+ iLevel, (u64)pLevel->notReady);
+ }
+#endif
return pLevel->notReady;
}
+/*
+** Generate the code for the loop that finds all non-matched terms
+** for a RIGHT JOIN.
+*/
+SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
+ WhereInfo *pWInfo,
+ int iLevel,
+ WhereLevel *pLevel
+){
+ Parse *pParse = pWInfo->pParse;
+ Vdbe *v = pParse->pVdbe;
+ WhereRightJoin *pRJ = pLevel->pRJ;
+ Expr *pSubWhere = 0;
+ WhereClause *pWC = &pWInfo->sWC;
+ WhereInfo *pSubWInfo;
+ WhereLoop *pLoop = pLevel->pWLoop;
+ SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
+ SrcList sFrom;
+ Bitmask mAll = 0;
+ int k;
+
+ ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pTab->zName));
+ sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
+ pRJ->regReturn);
+ for(k=0; k<iLevel; k++){
+ int iIdxCur;
+ mAll |= pWInfo->a[k].pWLoop->maskSelf;
+ sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur);
+ iIdxCur = pWInfo->a[k].iIdxCur;
+ if( iIdxCur ){
+ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur);
+ }
+ }
+ if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){
+ mAll |= pLoop->maskSelf;
+ for(k=0; k<pWC->nTerm; k++){
+ WhereTerm *pTerm = &pWC->a[k];
+ if( (pTerm->wtFlags & (TERM_VIRTUAL|TERM_SLICE))!=0
+ && pTerm->eOperator!=WO_ROWVAL
+ ){
+ break;
+ }
+ if( pTerm->prereqAll & ~mAll ) continue;
+ if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
+ pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
+ sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
+ }
+ }
+ sFrom.nSrc = 1;
+ sFrom.nAlloc = 1;
+ memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem));
+ sFrom.a[0].fg.jointype = 0;
+ assert( pParse->withinRJSubrtn < 100 );
+ pParse->withinRJSubrtn++;
+ pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0,
+ WHERE_RIGHT_JOIN, 0);
+ if( pSubWInfo ){
+ int iCur = pLevel->iTabCur;
+ int r = ++pParse->nMem;
+ int nPk;
+ int jmp;
+ int addrCont = sqlite3WhereContinueLabel(pSubWInfo);
+ Table *pTab = pTabItem->pTab;
+ if( HasRowid(pTab) ){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, r);
+ nPk = 1;
+ }else{
+ int iPk;
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ nPk = pPk->nKeyCol;
+ pParse->nMem += nPk - 1;
+ for(iPk=0; iPk<nPk; iPk++){
+ int iCol = pPk->aiColumn[iPk];
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol,r+iPk);
+ }
+ }
+ jmp = sqlite3VdbeAddOp4Int(v, OP_Filter, pRJ->regBloom, 0, r, nPk);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp4Int(v, OP_Found, pRJ->iMatch, addrCont, r, nPk);
+ VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, jmp);
+ sqlite3VdbeAddOp2(v, OP_Gosub, pRJ->regReturn, pRJ->addrSubrtn);
+ sqlite3WhereEnd(pSubWInfo);
+ }
+ sqlite3ExprDelete(pParse->db, pSubWhere);
+ ExplainQueryPlanPop(pParse);
+ assert( pParse->withinRJSubrtn>0 );
+ pParse->withinRJSubrtn--;
+}
+
/************** End of wherecode.c *******************************************/
/************** Begin file whereexpr.c ***************************************/
/*
@@ -137630,7 +158909,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
** the WHERE clause of SQL statements.
**
** This file was originally part of where.c but was split out to improve
-** readability and editabiliity. This file contains utility routines for
+** readability and editability. This file contains utility routines for
** analyzing Expr objects in the WHERE clause.
*/
/* #include "sqliteInt.h" */
@@ -137681,7 +158960,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
if( pWC->nTerm>=pWC->nSlot ){
WhereTerm *pOld = pWC->a;
sqlite3 *db = pWC->pWInfo->pParse->db;
- pWC->a = sqlite3DbMallocRawNN(db, sizeof(pWC->a[0])*pWC->nSlot*2 );
+ pWC->a = sqlite3WhereMalloc(pWC->pWInfo, sizeof(pWC->a[0])*pWC->nSlot*2 );
if( pWC->a==0 ){
if( wtFlags & TERM_DYNAMIC ){
sqlite3ExprDelete(db, p);
@@ -137690,18 +158969,16 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
return 0;
}
memcpy(pWC->a, pOld, sizeof(pWC->a[0])*pWC->nTerm);
- if( pOld!=pWC->aStatic ){
- sqlite3DbFree(db, pOld);
- }
- pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]);
+ pWC->nSlot = pWC->nSlot*2;
}
pTerm = &pWC->a[idx = pWC->nTerm++];
+ if( (wtFlags & TERM_VIRTUAL)==0 ) pWC->nBase = pWC->nTerm;
if( p && ExprHasProperty(p, EP_Unlikely) ){
pTerm->truthProb = sqlite3LogEst(p->iTable) - 270;
}else{
pTerm->truthProb = 1;
}
- pTerm->pExpr = sqlite3ExprSkipCollate(p);
+ pTerm->pExpr = sqlite3ExprSkipCollateAndLikely(p);
pTerm->wtFlags = wtFlags;
pTerm->pWC = pWC;
pTerm->iParent = -1;
@@ -137726,31 +159003,14 @@ static int allowedOp(int op){
/*
** Commute a comparison operator. Expressions of the form "X op Y"
** are converted into "Y op X".
-**
-** If left/right precedence rules come into play when determining the
-** collating sequence, then COLLATE operators are adjusted to ensure
-** that the collating sequence does not change. For example:
-** "Y collate NOCASE op X" becomes "X op Y" because any collation sequence on
-** the left hand side of a comparison overrides any collation sequence
-** attached to the right. For the same reason the EP_Collate flag
-** is not commuted.
-*/
-static void exprCommute(Parse *pParse, Expr *pExpr){
- u16 expRight = (pExpr->pRight->flags & EP_Collate);
- u16 expLeft = (pExpr->pLeft->flags & EP_Collate);
- assert( allowedOp(pExpr->op) && pExpr->op!=TK_IN );
- if( expRight==expLeft ){
- /* Either X and Y both have COLLATE operator or neither do */
- if( expRight ){
- /* Both X and Y have COLLATE operators. Make sure X is always
- ** used by clearing the EP_Collate flag from Y. */
- pExpr->pRight->flags &= ~EP_Collate;
- }else if( sqlite3ExprCollSeq(pParse, pExpr->pLeft)!=0 ){
- /* Neither X nor Y have COLLATE operators, but X has a non-default
- ** collating sequence. So add the EP_Collate marker on X to cause
- ** it to be searched first. */
- pExpr->pLeft->flags |= EP_Collate;
- }
+*/
+static u16 exprCommute(Parse *pParse, Expr *pExpr){
+ if( pExpr->pLeft->op==TK_VECTOR
+ || pExpr->pRight->op==TK_VECTOR
+ || sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight) !=
+ sqlite3BinaryCompareCollSeq(pParse, pExpr->pRight, pExpr->pLeft)
+ ){
+ pExpr->flags ^= EP_Commuted;
}
SWAP(Expr*,pExpr->pRight,pExpr->pLeft);
if( pExpr->op>=TK_GT ){
@@ -137761,6 +159021,7 @@ static void exprCommute(Parse *pParse, Expr *pExpr){
assert( pExpr->op>=TK_GT && pExpr->op<=TK_GE );
pExpr->op = ((pExpr->op-TK_GT)^2)+TK_GT;
}
+ return 0;
}
/*
@@ -137828,6 +159089,7 @@ static int isLikeOrGlob(
#ifdef SQLITE_EBCDIC
if( *pnoCase ) return 0;
#endif
+ assert( ExprUseXList(pExpr) );
pList = pExpr->x.pList;
pLeft = pList->a[1].pExpr;
@@ -137843,7 +159105,8 @@ static int isLikeOrGlob(
sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
}else if( op==TK_STRING ){
- z = (u8*)pRight->u.zToken;
+ assert( !ExprHasProperty(pRight, EP_IntValue) );
+ z = (u8*)pRight->u.zToken;
}
if( z ){
@@ -137862,7 +159125,7 @@ static int isLikeOrGlob(
** range search. The third is because the caller assumes that the pattern
** consists of at least one character after all escapes have been
** removed. */
- if( cnt!=0 && 255!=(u8)z[cnt-1] && (cnt>1 || z[0]!=wc[3]) ){
+ if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && 255!=(u8)z[cnt-1] ){
Expr *pPrefix;
/* A "complete" match if the pattern ends with "*" or "%" */
@@ -137872,34 +159135,49 @@ static int isLikeOrGlob(
pPrefix = sqlite3Expr(db, TK_STRING, (char*)z);
if( pPrefix ){
int iFrom, iTo;
- char *zNew = pPrefix->u.zToken;
+ char *zNew;
+ assert( !ExprHasProperty(pPrefix, EP_IntValue) );
+ zNew = pPrefix->u.zToken;
zNew[cnt] = 0;
for(iFrom=iTo=0; iFrom<cnt; iFrom++){
if( zNew[iFrom]==wc[3] ) iFrom++;
zNew[iTo++] = zNew[iFrom];
}
zNew[iTo] = 0;
+ assert( iTo>0 );
- /* If the RHS begins with a digit or a minus sign, then the LHS must be
- ** an ordinary column (not a virtual table column) with TEXT affinity.
- ** Otherwise the LHS might be numeric and "lhs >= rhs" would be false
- ** even though "lhs LIKE rhs" is true. But if the RHS does not start
- ** with a digit or '-', then "lhs LIKE rhs" will always be false if
- ** the LHS is numeric and so the optimization still works.
+ /* If the LHS is not an ordinary column with TEXT affinity, then the
+ ** pattern prefix boundaries (both the start and end boundaries) must
+ ** not look like a number. Otherwise the pattern might be treated as
+ ** a number, which will invalidate the LIKE optimization.
**
- ** 2018-09-10 ticket c94369cae9b561b1f996d0054bfab11389f9d033
- ** The RHS pattern must not be '/%' because the termination condition
- ** will then become "x<'0'" and if the affinity is numeric, will then
- ** be converted into "x<0", which is incorrect.
+ ** Getting this right has been a persistent source of bugs in the
+ ** LIKE optimization. See, for example:
+ ** 2018-09-10 https://sqlite.org/src/info/c94369cae9b561b1
+ ** 2019-05-02 https://sqlite.org/src/info/b043a54c3de54b28
+ ** 2019-06-10 https://sqlite.org/src/info/fd76310a5e843e07
+ ** 2019-06-14 https://sqlite.org/src/info/ce8717f0885af975
+ ** 2019-09-03 https://sqlite.org/src/info/0f0428096f17252a
*/
- if( sqlite3Isdigit(zNew[0])
- || zNew[0]=='-'
- || (zNew[0]+1=='0' && iTo==1)
+ if( pLeft->op!=TK_COLUMN
+ || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
+ || (ALWAYS( ExprUseYTab(pLeft) )
+ && ALWAYS(pLeft->y.pTab)
+ && IsVirtual(pLeft->y.pTab)) /* Might be numeric */
){
- if( pLeft->op!=TK_COLUMN
- || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
- || IsVirtual(pLeft->y.pTab) /* Value might be numeric */
- ){
+ int isNum;
+ double rDummy;
+ isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8);
+ if( isNum<=0 ){
+ if( iTo==1 && zNew[0]=='-' ){
+ isNum = +1;
+ }else{
+ zNew[iTo-1]++;
+ isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8);
+ zNew[iTo-1]--;
+ }
+ }
+ if( isNum>0 ){
sqlite3ExprDelete(db, pPrefix);
sqlite3ValueFree(pVal);
return 0;
@@ -137913,13 +159191,14 @@ static int isLikeOrGlob(
if( op==TK_VARIABLE ){
Vdbe *v = pParse->pVdbe;
sqlite3VdbeSetVarmask(v, pRight->iColumn);
+ assert( !ExprHasProperty(pRight, EP_IntValue) );
if( *pisComplete && pRight->u.zToken[1] ){
/* If the rhs of the LIKE expression is a variable, and the current
** value of the variable means there is no need to invoke the LIKE
** function, then no OP_Variable will be added to the program.
** This causes problems for the sqlite3_bind_parameter_name()
** API. To work around them, add a dummy OP_Variable here.
- */
+ */
int r1 = sqlite3GetTempReg(pParse);
sqlite3ExprCodeTarget(pParse, pRight, r1);
sqlite3VdbeChangeP3(v, sqlite3VdbeCurrentAddr(v)-1, 0);
@@ -137956,7 +159235,7 @@ static int isLikeOrGlob(
** 9. column IS NOT NULL SQLITE_INDEX_CONSTRAINT_ISNOTNULL
**
** In every case, "column" must be a column of a virtual table. If there
-** is a match, set *ppLeft to the "column" expression, set *ppRight to the
+** is a match, set *ppLeft to the "column" expression, set *ppRight to the
** "expr" expression (even though in forms (6) and (8) the column is on the
** right and the expression is on the left). Also set *peOp2 to the
** appropriate virtual table operator. The return value is 1 or 2 if there
@@ -137986,6 +159265,7 @@ static int isAuxiliaryVtabOperator(
Expr *pCol; /* Column reference */
int i;
+ assert( ExprUseXList(pExpr) );
pList = pExpr->x.pList;
if( pList==0 || pList->nExpr!=2 ){
return 0;
@@ -137999,8 +159279,10 @@ static int isAuxiliaryVtabOperator(
** MATCH(expression,vtab_column)
*/
pCol = pList->a[1].pExpr;
- if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){
+ assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) );
+ if( ExprIsVtab(pCol) ){
for(i=0; i<ArraySize(aOp); i++){
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){
*peOp2 = aOp[i].eOp2;
*ppRight = pList->a[0].pExpr;
@@ -138021,7 +159303,9 @@ static int isAuxiliaryVtabOperator(
** with function names in an arbitrary case.
*/
pCol = pList->a[0].pExpr;
- if( pCol->op==TK_COLUMN && IsVirtual(pCol->y.pTab) ){
+ assert( pCol->op!=TK_COLUMN || ExprUseYTab(pCol) );
+ assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) );
+ if( ExprIsVtab(pCol) ){
sqlite3_vtab *pVtab;
sqlite3_module *pMod;
void (*xNotUsed)(sqlite3_context*,int,sqlite3_value**);
@@ -138029,6 +159313,7 @@ static int isAuxiliaryVtabOperator(
pVtab = sqlite3GetVTable(db, pCol->y.pTab)->pVtab;
assert( pVtab!=0 );
assert( pVtab->pModule!=0 );
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
pMod = (sqlite3_module *)pVtab->pModule;
if( pMod->xFindFunction!=0 ){
i = pMod->xFindFunction(pVtab,2, pExpr->u.zToken, &xNotUsed, &pNotUsed);
@@ -138044,10 +159329,13 @@ static int isAuxiliaryVtabOperator(
int res = 0;
Expr *pLeft = pExpr->pLeft;
Expr *pRight = pExpr->pRight;
- if( pLeft->op==TK_COLUMN && IsVirtual(pLeft->y.pTab) ){
+ assert( pLeft->op!=TK_COLUMN || (ExprUseYTab(pLeft) && pLeft->y.pTab!=0) );
+ if( ExprIsVtab(pLeft) ){
res++;
}
- if( pRight && pRight->op==TK_COLUMN && IsVirtual(pRight->y.pTab) ){
+ assert( pRight==0 || pRight->op!=TK_COLUMN
+ || (ExprUseYTab(pRight) && pRight->y.pTab!=0) );
+ if( pRight && ExprIsVtab(pRight) ){
res++;
SWAP(Expr*, pLeft, pRight);
}
@@ -138067,9 +159355,9 @@ static int isAuxiliaryVtabOperator(
** a join, then transfer the appropriate markings over to derived.
*/
static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
- if( pDerived ){
- pDerived->flags |= pBase->flags & EP_FromJoin;
- pDerived->iRightJoinTable = pBase->iRightJoinTable;
+ if( pDerived && ExprHasProperty(pBase, EP_OuterON|EP_InnerON) ){
+ pDerived->flags |= pBase->flags & (EP_OuterON|EP_InnerON);
+ pDerived->w.iJoin = pBase->w.iJoin;
}
}
@@ -138115,7 +159403,7 @@ static WhereTerm *whereNthSubterm(WhereTerm *pTerm, int N){
**
** The following is NOT generated:
**
-** x<y OR x>y --> x!=y
+** x<y OR x>y --> x!=y
*/
static void whereCombineDisjuncts(
SrcList *pSrc, /* the FROM clause */
@@ -138129,6 +159417,7 @@ static void whereCombineDisjuncts(
int op; /* Operator for the combined expression */
int idxNew; /* Index in pWC of the next virtual term */
+ if( (pOne->wtFlags | pTwo->wtFlags) & TERM_VNULL ) return;
if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return;
if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return;
if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp
@@ -138212,10 +159501,10 @@ static void whereCombineDisjuncts(
** WhereTerm.u.pOrInfo->indexable |= the cursor number for table T
**
** A subterm is "indexable" if it is of the form
-** "T.C <op> <expr>" where C is any column of table T and
+** "T.C <op> <expr>" where C is any column of table T and
** <op> is one of "=", "<", "<=", ">", ">=", "IS NULL", or "IN".
** A subterm is also indexable if it is an AND of two or more
-** subsubterms at least one of which is indexable. Indexable AND
+** subsubterms at least one of which is indexable. Indexable AND
** subterms have their eOperator set to WO_AND and they have
** u.pAndInfo set to a dynamically allocated WhereAndTerm object.
**
@@ -138297,6 +159586,7 @@ static void exprAnalyzeOrTerm(
pOrTerm->u.pAndInfo = pAndInfo;
pOrTerm->wtFlags |= TERM_ANDINFO;
pOrTerm->eOperator = WO_AND;
+ pOrTerm->leftCursor = -1;
pAndWC = &pAndInfo->wc;
memset(pAndWC->aStatic, 0, sizeof(pAndWC->aStatic));
sqlite3WhereClauseInit(pAndWC, pWC->pWInfo);
@@ -138306,7 +159596,7 @@ static void exprAnalyzeOrTerm(
if( !db->mallocFailed ){
for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){
assert( pAndTerm->pExpr );
- if( allowedOp(pAndTerm->pExpr->op)
+ if( allowedOp(pAndTerm->pExpr->op)
|| pAndTerm->eOperator==WO_AUX
){
b |= sqlite3WhereGetMask(&pWInfo->sMaskSet, pAndTerm->leftCursor);
@@ -138339,11 +159629,10 @@ static void exprAnalyzeOrTerm(
** empty.
*/
pOrInfo->indexable = indexable;
+ pTerm->eOperator = WO_OR;
+ pTerm->leftCursor = -1;
if( indexable ){
- pTerm->eOperator = WO_OR;
pWC->hasOr = 1;
- }else{
- pTerm->eOperator = WO_OR;
}
/* For a two-way OR, attempt to implementation case 2.
@@ -138394,10 +159683,11 @@ static void exprAnalyzeOrTerm(
** and column is found but leave okToChngToIN false if not found.
*/
for(j=0; j<2 && !okToChngToIN; j++){
+ Expr *pLeft = 0;
pOrTerm = pOrWc->a;
for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){
assert( pOrTerm->eOperator & WO_EQ );
- pOrTerm->wtFlags &= ~TERM_OR_OK;
+ pOrTerm->wtFlags &= ~TERM_OK;
if( pOrTerm->leftCursor==iCursor ){
/* This is the 2-bit case and we are on the second iteration and
** current term is from the first iteration. So skip this term. */
@@ -138408,15 +159698,17 @@ static void exprAnalyzeOrTerm(
pOrTerm->leftCursor))==0 ){
/* This term must be of the form t1.a==t2.b where t2 is in the
** chngToIN set but t1 is not. This term will be either preceded
- ** or follwed by an inverted copy (t2.b==t1.a). Skip this term
+ ** or followed by an inverted copy (t2.b==t1.a). Skip this term
** and use its inversion. */
testcase( pOrTerm->wtFlags & TERM_COPIED );
testcase( pOrTerm->wtFlags & TERM_VIRTUAL );
assert( pOrTerm->wtFlags & (TERM_COPIED|TERM_VIRTUAL) );
continue;
}
- iColumn = pOrTerm->u.leftColumn;
+ assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 );
+ iColumn = pOrTerm->u.x.leftColumn;
iCursor = pOrTerm->leftCursor;
+ pLeft = pOrTerm->pExpr->pLeft;
break;
}
if( i<0 ){
@@ -138434,9 +159726,12 @@ static void exprAnalyzeOrTerm(
okToChngToIN = 1;
for(; i>=0 && okToChngToIN; i--, pOrTerm++){
assert( pOrTerm->eOperator & WO_EQ );
+ assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 );
if( pOrTerm->leftCursor!=iCursor ){
- pOrTerm->wtFlags &= ~TERM_OR_OK;
- }else if( pOrTerm->u.leftColumn!=iColumn ){
+ pOrTerm->wtFlags &= ~TERM_OK;
+ }else if( pOrTerm->u.x.leftColumn!=iColumn || (iColumn==XN_EXPR
+ && sqlite3ExprCompare(pParse, pOrTerm->pExpr->pLeft, pLeft, -1)
+ )){
okToChngToIN = 0;
}else{
int affLeft, affRight;
@@ -138449,14 +159744,14 @@ static void exprAnalyzeOrTerm(
if( affRight!=0 && affRight!=affLeft ){
okToChngToIN = 0;
}else{
- pOrTerm->wtFlags |= TERM_OR_OK;
+ pOrTerm->wtFlags |= TERM_OK;
}
}
}
}
/* At this point, okToChngToIN is true if original pTerm satisfies
- ** case 1. In that case, construct a new virtual term that is
+ ** case 1. In that case, construct a new virtual term that is
** pTerm converted into an IN operator.
*/
if( okToChngToIN ){
@@ -138466,10 +159761,11 @@ static void exprAnalyzeOrTerm(
Expr *pNew; /* The complete IN operator */
for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){
- if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue;
+ if( (pOrTerm->wtFlags & TERM_OK)==0 ) continue;
assert( pOrTerm->eOperator & WO_EQ );
+ assert( (pOrTerm->eOperator & (WO_OR|WO_AND))==0 );
assert( pOrTerm->leftCursor==iCursor );
- assert( pOrTerm->u.leftColumn==iColumn );
+ assert( pOrTerm->u.x.leftColumn==iColumn );
pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0);
pList = sqlite3ExprListAppend(pWInfo->pParse, pList, pDup);
pLeft = pOrTerm->pExpr->pLeft;
@@ -138480,12 +159776,12 @@ static void exprAnalyzeOrTerm(
if( pNew ){
int idxNew;
transferJoinMarkings(pNew, pExpr);
- assert( !ExprHasProperty(pNew, EP_xIsSelect) );
+ assert( ExprUseXList(pNew) );
pNew->x.pList = pList;
idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew==0 );
exprAnalyze(pSrc, pWC, idxNew);
- /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where used again */
+ /* pTerm = &pWC->a[idxTerm]; // would be needed if pTerm where reused */
markTermAsChild(pWC, idxNew, idxTerm);
}else{
sqlite3ExprListDelete(db, pList);
@@ -138515,7 +159811,7 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr){
CollSeq *pColl;
if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0;
if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0;
- if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0;
+ if( ExprHasProperty(pExpr, EP_OuterON) ) return 0;
aff1 = sqlite3ExprAffinity(pExpr->pLeft);
aff2 = sqlite3ExprAffinity(pExpr->pRight);
if( aff1!=aff2
@@ -138523,7 +159819,7 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr){
){
return 0;
}
- pColl = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight);
+ pColl = sqlite3ExprCompareCollSeq(pParse, pExpr);
if( sqlite3IsBinary(pColl) ) return 1;
return sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight);
}
@@ -138546,7 +159842,9 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
int i;
for(i=0; i<pSrc->nSrc; i++){
mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect);
- mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn);
+ if( pSrc->a[i].fg.isUsing==0 ){
+ mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].u3.pOn);
+ }
if( pSrc->a[i].fg.isTabFunc ){
mask |= sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg);
}
@@ -138572,42 +159870,48 @@ static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
*/
static SQLITE_NOINLINE int exprMightBeIndexed2(
SrcList *pFrom, /* The FROM clause */
- Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */
int *aiCurCol, /* Write the referenced table cursor and column here */
- Expr *pExpr /* An operand of a comparison operator */
+ Expr *pExpr, /* An operand of a comparison operator */
+ int j /* Start looking with the j-th pFrom entry */
){
Index *pIdx;
int i;
int iCur;
- for(i=0; mPrereq>1; i++, mPrereq>>=1){}
- iCur = pFrom->a[i].iCursor;
- for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->aColExpr==0 ) continue;
- for(i=0; i<pIdx->nKeyCol; i++){
- if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
- if( sqlite3ExprCompareSkip(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){
- aiCurCol[0] = iCur;
- aiCurCol[1] = XN_EXPR;
- return 1;
+ do{
+ iCur = pFrom->a[j].iCursor;
+ for(pIdx=pFrom->a[j].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->aColExpr==0 ) continue;
+ for(i=0; i<pIdx->nKeyCol; i++){
+ if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
+ assert( pIdx->bHasExpr );
+ if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0
+ && pExpr->op!=TK_STRING
+ ){
+ aiCurCol[0] = iCur;
+ aiCurCol[1] = XN_EXPR;
+ return 1;
+ }
}
}
- }
+ }while( ++j < pFrom->nSrc );
return 0;
}
static int exprMightBeIndexed(
SrcList *pFrom, /* The FROM clause */
- Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */
int *aiCurCol, /* Write the referenced table cursor & column here */
Expr *pExpr, /* An operand of a comparison operator */
int op /* The specific comparison operator */
){
- /* If this expression is a vector to the left or right of a
- ** inequality constraint (>, <, >= or <=), perform the processing
+ int i;
+
+ /* If this expression is a vector to the left or right of a
+ ** inequality constraint (>, <, >= or <=), perform the processing
** on the first element of the vector. */
assert( TK_GT+1==TK_LE && TK_GT+2==TK_LT && TK_GT+3==TK_GE );
assert( TK_IS<TK_GE && TK_ISNULL<TK_GE && TK_IN<TK_GE );
assert( op<=TK_GE );
if( pExpr->op==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){
+ assert( ExprUseXList(pExpr) );
pExpr = pExpr->x.pList->a[0].pExpr;
}
@@ -138616,11 +159920,19 @@ static int exprMightBeIndexed(
aiCurCol[1] = pExpr->iColumn;
return 1;
}
- if( mPrereq==0 ) return 0; /* No table references */
- if( (mPrereq&(mPrereq-1))!=0 ) return 0; /* Refs more than one table */
- return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr);
+
+ for(i=0; i<pFrom->nSrc; i++){
+ Index *pIdx;
+ for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->aColExpr ){
+ return exprMightBeIndexed2(pFrom,aiCurCol,pExpr,i);
+ }
+ }
+ }
+ return 0;
}
+
/*
** The input to this routine is an WhereTerm structure with only the
** "pExpr" field filled in. The job of this routine is to analyze the
@@ -138648,8 +159960,8 @@ static void exprAnalyze(
WhereTerm *pTerm; /* The term to be analyzed */
WhereMaskSet *pMaskSet; /* Set of table index masks */
Expr *pExpr; /* The expression to be analyzed */
- Bitmask prereqLeft; /* Prerequesites of the pExpr->pLeft */
- Bitmask prereqAll; /* Prerequesites of pExpr */
+ Bitmask prereqLeft; /* Prerequisites of the pExpr->pLeft */
+ Bitmask prereqAll; /* Prerequisites of pExpr */
Bitmask extraRight = 0; /* Extra dependencies on LEFT JOIN */
Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */
int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */
@@ -138663,36 +159975,67 @@ static void exprAnalyze(
if( db->mallocFailed ){
return;
}
+ assert( pWC->nTerm > idxTerm );
pTerm = &pWC->a[idxTerm];
pMaskSet = &pWInfo->sMaskSet;
pExpr = pTerm->pExpr;
+ assert( pExpr!=0 ); /* Because malloc() has not failed */
assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE );
+ pMaskSet->bVarSelect = 0;
prereqLeft = sqlite3WhereExprUsage(pMaskSet, pExpr->pLeft);
op = pExpr->op;
if( op==TK_IN ){
assert( pExpr->pRight==0 );
if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ if( ExprUseXSelect(pExpr) ){
pTerm->prereqRight = exprSelectUsage(pMaskSet, pExpr->x.pSelect);
}else{
pTerm->prereqRight = sqlite3WhereExprListUsage(pMaskSet, pExpr->x.pList);
}
- }else if( op==TK_ISNULL ){
- pTerm->prereqRight = 0;
+ prereqAll = prereqLeft | pTerm->prereqRight;
}else{
pTerm->prereqRight = sqlite3WhereExprUsage(pMaskSet, pExpr->pRight);
+ if( pExpr->pLeft==0
+ || ExprHasProperty(pExpr, EP_xIsSelect|EP_IfNullRow)
+ || pExpr->x.pList!=0
+ ){
+ prereqAll = sqlite3WhereExprUsageNN(pMaskSet, pExpr);
+ }else{
+ prereqAll = prereqLeft | pTerm->prereqRight;
+ }
}
- pMaskSet->bVarSelect = 0;
- prereqAll = sqlite3WhereExprUsageNN(pMaskSet, pExpr);
if( pMaskSet->bVarSelect ) pTerm->wtFlags |= TERM_VARSELECT;
- if( ExprHasProperty(pExpr, EP_FromJoin) ){
- Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable);
- prereqAll |= x;
- extraRight = x-1; /* ON clause terms may not be used with an index
- ** on left table of a LEFT JOIN. Ticket #3015 */
- if( (prereqAll>>1)>=x ){
- sqlite3ErrorMsg(pParse, "ON clause references tables to its right");
- return;
+
+#ifdef SQLITE_DEBUG
+ if( prereqAll!=sqlite3WhereExprUsageNN(pMaskSet, pExpr) ){
+ printf("\n*** Incorrect prereqAll computed for:\n");
+ sqlite3TreeViewExpr(0,pExpr,0);
+ assert( 0 );
+ }
+#endif
+
+ if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ){
+ Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin);
+ if( ExprHasProperty(pExpr, EP_OuterON) ){
+ prereqAll |= x;
+ extraRight = x-1; /* ON clause terms may not be used with an index
+ ** on left table of a LEFT JOIN. Ticket #3015 */
+ if( (prereqAll>>1)>=x ){
+ sqlite3ErrorMsg(pParse, "ON clause references tables to its right");
+ return;
+ }
+ }else if( (prereqAll>>1)>=x ){
+ /* The ON clause of an INNER JOIN references a table to its right.
+ ** Most other SQL database engines raise an error. But SQLite versions
+ ** 3.0 through 3.38 just put the ON clause constraint into the WHERE
+ ** clause and carried on. Beginning with 3.39, raise an error only
+ ** if there is a RIGHT or FULL JOIN in the query. This makes SQLite
+ ** more like other systems, and also preserves legacy. */
+ if( ALWAYS(pSrc->nSrc>0) && (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
+ sqlite3ErrorMsg(pParse, "ON clause references tables to its right");
+ return;
+ }
+ ExprClearProperty(pExpr, EP_InnerON);
}
}
pTerm->prereqAll = prereqAll;
@@ -138705,25 +160048,28 @@ static void exprAnalyze(
Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
- if( pTerm->iField>0 ){
+ if( pTerm->u.x.iField>0 ){
assert( op==TK_IN );
assert( pLeft->op==TK_VECTOR );
- pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr;
+ assert( ExprUseXList(pLeft) );
+ pLeft = pLeft->x.pList->a[pTerm->u.x.iField-1].pExpr;
}
- if( exprMightBeIndexed(pSrc, prereqLeft, aiCurCol, pLeft, op) ){
+ if( exprMightBeIndexed(pSrc, aiCurCol, pLeft, op) ){
pTerm->leftCursor = aiCurCol[0];
- pTerm->u.leftColumn = aiCurCol[1];
+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
+ pTerm->u.x.leftColumn = aiCurCol[1];
pTerm->eOperator = operatorMask(op) & opMask;
}
if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
- if( pRight
- && exprMightBeIndexed(pSrc, pTerm->prereqRight, aiCurCol, pRight, op)
+ if( pRight
+ && exprMightBeIndexed(pSrc, aiCurCol, pRight, op)
+ && !ExprHasProperty(pRight, EP_FixedCol)
){
WhereTerm *pNew;
Expr *pDup;
u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */
- assert( pTerm->iField==0 );
+ assert( pTerm->u.x.iField==0 );
if( pTerm->leftCursor>=0 ){
int idxNew;
pDup = sqlite3ExprDup(db, pExpr, 0);
@@ -138747,13 +160093,25 @@ static void exprAnalyze(
pDup = pExpr;
pNew = pTerm;
}
- exprCommute(pParse, pDup);
+ pNew->wtFlags |= exprCommute(pParse, pDup);
pNew->leftCursor = aiCurCol[0];
- pNew->u.leftColumn = aiCurCol[1];
+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
+ pNew->u.x.leftColumn = aiCurCol[1];
testcase( (prereqLeft | extraRight) != prereqLeft );
pNew->prereqRight = prereqLeft | extraRight;
pNew->prereqAll = prereqAll;
pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
+ }else
+ if( op==TK_ISNULL
+ && !ExprHasProperty(pExpr,EP_OuterON)
+ && 0==sqlite3ExprCanBeNull(pLeft)
+ ){
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+ pExpr->op = TK_TRUEFALSE; /* See tag-20230504-1 */
+ pExpr->u.zToken = "false";
+ ExprSetProperty(pExpr, EP_IsFalse);
+ pTerm->prereqAll = 0;
+ pTerm->eOperator = 0;
}
}
@@ -138774,15 +160132,17 @@ static void exprAnalyze(
** BETWEEN term is skipped.
*/
else if( pExpr->op==TK_BETWEEN && pWC->op==TK_AND ){
- ExprList *pList = pExpr->x.pList;
+ ExprList *pList;
int i;
static const u8 ops[] = {TK_GE, TK_LE};
+ assert( ExprUseXList(pExpr) );
+ pList = pExpr->x.pList;
assert( pList!=0 );
assert( pList->nExpr==2 );
for(i=0; i<2; i++){
Expr *pNewExpr;
int idxNew;
- pNewExpr = sqlite3PExpr(pParse, ops[i],
+ pNewExpr = sqlite3PExpr(pParse, ops[i],
sqlite3ExprDup(db, pExpr->pLeft, 0),
sqlite3ExprDup(db, pList->a[i].pExpr, 0));
transferJoinMarkings(pNewExpr, pExpr);
@@ -138805,6 +160165,42 @@ static void exprAnalyze(
pTerm = &pWC->a[idxTerm];
}
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
+ /* The form "x IS NOT NULL" can sometimes be evaluated more efficiently
+ ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
+ ** virtual term of that form.
+ **
+ ** The virtual term must be tagged with TERM_VNULL.
+ */
+ else if( pExpr->op==TK_NOTNULL ){
+ if( pExpr->pLeft->op==TK_COLUMN
+ && pExpr->pLeft->iColumn>=0
+ && !ExprHasProperty(pExpr, EP_OuterON)
+ ){
+ Expr *pNewExpr;
+ Expr *pLeft = pExpr->pLeft;
+ int idxNew;
+ WhereTerm *pNewTerm;
+
+ pNewExpr = sqlite3PExpr(pParse, TK_GT,
+ sqlite3ExprDup(db, pLeft, 0),
+ sqlite3ExprAlloc(db, TK_NULL, 0, 0));
+
+ idxNew = whereClauseInsert(pWC, pNewExpr,
+ TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL);
+ if( idxNew ){
+ pNewTerm = &pWC->a[idxNew];
+ pNewTerm->prereqRight = 0;
+ pNewTerm->leftCursor = pLeft->iTable;
+ pNewTerm->u.x.leftColumn = pLeft->iColumn;
+ pNewTerm->eOperator = WO_GT;
+ markTermAsChild(pWC, idxNew, idxTerm);
+ pTerm = &pWC->a[idxTerm];
+ pTerm->wtFlags |= TERM_COPIED;
+ pNewTerm->prereqAll = pTerm->prereqAll;
+ }
+ }
+ }
+
#ifndef SQLITE_OMIT_LIKE_OPTIMIZATION
/* Add constraints to reduce the search space on a LIKE or GLOB
@@ -138820,7 +160216,8 @@ static void exprAnalyze(
** bound is made all lowercase so that the bounds also work when comparing
** BLOBs.
*/
- if( pWC->op==TK_AND
+ else if( pExpr->op==TK_FUNCTION
+ && pWC->op==TK_AND
&& isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase)
){
Expr *pLeft; /* LHS of LIKE/GLOB operator */
@@ -138832,8 +160229,12 @@ static void exprAnalyze(
const char *zCollSeqName; /* Name of collating sequence */
const u16 wtFlags = TERM_LIKEOPT | TERM_VIRTUAL | TERM_DYNAMIC;
+ assert( ExprUseXList(pExpr) );
pLeft = pExpr->x.pList->a[1].pExpr;
pStr2 = sqlite3ExprDup(db, pStr1, 0);
+ assert( pStr1==0 || !ExprHasProperty(pStr1, EP_IntValue) );
+ assert( pStr2==0 || !ExprHasProperty(pStr2, EP_IntValue) );
+
/* Convert the lower bound to upper-case and the upper bound to
** lower-case (upper-case is less than lower-case in ASCII) so that
@@ -138856,7 +160257,7 @@ static void exprAnalyze(
if( noCase ){
/* The point is to increment the last character before the first
** wildcard. But if we increment '@', that will push it into the
- ** alphabetic range where case conversions will mess up the
+ ** alphabetic range where case conversions will mess up the
** inequality. To avoid this, make sure to also run the full
** LIKE on all candidate expressions by clearing the isComplete flag
*/
@@ -138873,7 +160274,6 @@ static void exprAnalyze(
transferJoinMarkings(pNewExpr1, pExpr);
idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags);
testcase( idxNew1==0 );
- exprAnalyze(pSrc, pWC, idxNew1);
pNewExpr2 = sqlite3ExprDup(db, pLeft, 0);
pNewExpr2 = sqlite3PExpr(pParse, TK_LT,
sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName),
@@ -138881,6 +160281,7 @@ static void exprAnalyze(
transferJoinMarkings(pNewExpr2, pExpr);
idxNew2 = whereClauseInsert(pWC, pNewExpr2, wtFlags);
testcase( idxNew2==0 );
+ exprAnalyze(pSrc, pWC, idxNew1);
exprAnalyze(pSrc, pWC, idxNew2);
pTerm = &pWC->a[idxTerm];
if( isComplete ){
@@ -138890,6 +160291,69 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */
+ /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
+ ** new terms for each component comparison - "a = ?" and "b = ?". The
+ ** new terms completely replace the original vector comparison, which is
+ ** no longer used.
+ **
+ ** This is only required if at least one side of the comparison operation
+ ** is not a sub-select.
+ **
+ ** tag-20220128a
+ */
+ if( (pExpr->op==TK_EQ || pExpr->op==TK_IS)
+ && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1
+ && sqlite3ExprVectorSize(pExpr->pRight)==nLeft
+ && ( (pExpr->pLeft->flags & EP_xIsSelect)==0
+ || (pExpr->pRight->flags & EP_xIsSelect)==0)
+ && pWC->op==TK_AND
+ ){
+ int i;
+ for(i=0; i<nLeft; i++){
+ int idxNew;
+ Expr *pNew;
+ Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i, nLeft);
+ Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i, nLeft);
+
+ pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight);
+ transferJoinMarkings(pNew, pExpr);
+ idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_SLICE);
+ exprAnalyze(pSrc, pWC, idxNew);
+ }
+ pTerm = &pWC->a[idxTerm];
+ pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
+ pTerm->eOperator = WO_ROWVAL;
+ }
+
+ /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
+ ** a virtual term for each vector component. The expression object
+ ** used by each such virtual term is pExpr (the full vector IN(...)
+ ** expression). The WhereTerm.u.x.iField variable identifies the index within
+ ** the vector on the LHS that the virtual term represents.
+ **
+ ** This only works if the RHS is a simple SELECT (not a compound) that does
+ ** not use window functions.
+ */
+ else if( pExpr->op==TK_IN
+ && pTerm->u.x.iField==0
+ && pExpr->pLeft->op==TK_VECTOR
+ && ALWAYS( ExprUseXSelect(pExpr) )
+ && (pExpr->x.pSelect->pPrior==0 || (pExpr->x.pSelect->selFlags & SF_Values))
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ && pExpr->x.pSelect->pWin==0
+#endif
+ && pWC->op==TK_AND
+ ){
+ int i;
+ for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
+ int idxNew;
+ idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL|TERM_SLICE);
+ pWC->a[idxNew].u.x.iField = i+1;
+ exprAnalyze(pSrc, pWC, idxNew);
+ markTermAsChild(pWC, idxNew, idxTerm);
+ }
+ }
+
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Add a WO_AUX auxiliary term to the constraint set if the
** current expression is of the form "column OP expr" where OP
@@ -138900,7 +160364,7 @@ static void exprAnalyze(
** virtual tables. The native query optimizer does not attempt
** to do anything with MATCH functions.
*/
- if( pWC->op==TK_AND ){
+ else if( pWC->op==TK_AND ){
Expr *pRight = 0, *pLeft = 0;
int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight);
while( res-- > 0 ){
@@ -138912,17 +160376,18 @@ static void exprAnalyze(
prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft);
if( (prereqExpr & prereqColumn)==0 ){
Expr *pNewExpr;
- pNewExpr = sqlite3PExpr(pParse, TK_MATCH,
+ pNewExpr = sqlite3PExpr(pParse, TK_MATCH,
0, sqlite3ExprDup(db, pRight, 0));
- if( ExprHasProperty(pExpr, EP_FromJoin) && pNewExpr ){
- ExprSetProperty(pNewExpr, EP_FromJoin);
+ if( ExprHasProperty(pExpr, EP_OuterON) && pNewExpr ){
+ ExprSetProperty(pNewExpr, EP_OuterON);
+ pNewExpr->w.iJoin = pExpr->w.iJoin;
}
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew==0 );
pNewTerm = &pWC->a[idxNew];
pNewTerm->prereqRight = prereqExpr;
pNewTerm->leftCursor = pLeft->iTable;
- pNewTerm->u.leftColumn = pLeft->iColumn;
+ pNewTerm->u.x.leftColumn = pLeft->iColumn;
pNewTerm->eOperator = WO_AUX;
pNewTerm->eMatchOp = eOp2;
markTermAsChild(pWC, idxNew, idxTerm);
@@ -138935,98 +160400,6 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
- /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
- ** new terms for each component comparison - "a = ?" and "b = ?". The
- ** new terms completely replace the original vector comparison, which is
- ** no longer used.
- **
- ** This is only required if at least one side of the comparison operation
- ** is not a sub-select. */
- if( pWC->op==TK_AND
- && (pExpr->op==TK_EQ || pExpr->op==TK_IS)
- && (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1
- && sqlite3ExprVectorSize(pExpr->pRight)==nLeft
- && ( (pExpr->pLeft->flags & EP_xIsSelect)==0
- || (pExpr->pRight->flags & EP_xIsSelect)==0)
- ){
- int i;
- for(i=0; i<nLeft; i++){
- int idxNew;
- Expr *pNew;
- Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i);
- Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);
-
- pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight);
- transferJoinMarkings(pNew, pExpr);
- idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
- exprAnalyze(pSrc, pWC, idxNew);
- }
- pTerm = &pWC->a[idxTerm];
- pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
- pTerm->eOperator = 0;
- }
-
- /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
- ** a virtual term for each vector component. The expression object
- ** used by each such virtual term is pExpr (the full vector IN(...)
- ** expression). The WhereTerm.iField variable identifies the index within
- ** the vector on the LHS that the virtual term represents.
- **
- ** This only works if the RHS is a simple SELECT, not a compound
- */
- if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->iField==0
- && pExpr->pLeft->op==TK_VECTOR
- && pExpr->x.pSelect->pPrior==0
- ){
- int i;
- for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
- int idxNew;
- idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
- pWC->a[idxNew].iField = i+1;
- exprAnalyze(pSrc, pWC, idxNew);
- markTermAsChild(pWC, idxNew, idxTerm);
- }
- }
-
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- /* When sqlite_stat3 histogram data is available an operator of the
- ** form "x IS NOT NULL" can sometimes be evaluated more efficiently
- ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
- ** virtual term of that form.
- **
- ** Note that the virtual term must be tagged with TERM_VNULL.
- */
- if( pExpr->op==TK_NOTNULL
- && pExpr->pLeft->op==TK_COLUMN
- && pExpr->pLeft->iColumn>=0
- && !ExprHasProperty(pExpr, EP_FromJoin)
- && OptimizationEnabled(db, SQLITE_Stat34)
- ){
- Expr *pNewExpr;
- Expr *pLeft = pExpr->pLeft;
- int idxNew;
- WhereTerm *pNewTerm;
-
- pNewExpr = sqlite3PExpr(pParse, TK_GT,
- sqlite3ExprDup(db, pLeft, 0),
- sqlite3ExprAlloc(db, TK_NULL, 0, 0));
-
- idxNew = whereClauseInsert(pWC, pNewExpr,
- TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL);
- if( idxNew ){
- pNewTerm = &pWC->a[idxNew];
- pNewTerm->prereqRight = 0;
- pNewTerm->leftCursor = pLeft->iTable;
- pNewTerm->u.leftColumn = pLeft->iColumn;
- pNewTerm->eOperator = WO_GT;
- markTermAsChild(pWC, idxNew, idxTerm);
- pTerm = &pWC->a[idxTerm];
- pTerm->wtFlags |= TERM_COPIED;
- pNewTerm->prereqAll = pTerm->prereqAll;
- }
- }
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
-
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
*/
@@ -139058,8 +160431,9 @@ static void exprAnalyze(
** all terms of the WHERE clause.
*/
SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
- Expr *pE2 = sqlite3ExprSkipCollate(pExpr);
+ Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr);
pWC->op = op;
+ assert( pE2!=0 || pExpr==0 );
if( pE2==0 ) return;
if( pE2->op!=op ){
whereClauseInsert(pWC, pExpr, 0);
@@ -139070,6 +160444,120 @@ SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
}
/*
+** Add either a LIMIT (if eMatchOp==SQLITE_INDEX_CONSTRAINT_LIMIT) or
+** OFFSET (if eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET) term to the
+** where-clause passed as the first argument. The value for the term
+** is found in register iReg.
+**
+** In the common case where the value is a simple integer
+** (example: "LIMIT 5 OFFSET 10") then the expression codes as a
+** TK_INTEGER so that it will be available to sqlite3_vtab_rhs_value().
+** If not, then it codes as a TK_REGISTER expression.
+*/
+static void whereAddLimitExpr(
+ WhereClause *pWC, /* Add the constraint to this WHERE clause */
+ int iReg, /* Register that will hold value of the limit/offset */
+ Expr *pExpr, /* Expression that defines the limit/offset */
+ int iCsr, /* Cursor to which the constraint applies */
+ int eMatchOp /* SQLITE_INDEX_CONSTRAINT_LIMIT or _OFFSET */
+){
+ Parse *pParse = pWC->pWInfo->pParse;
+ sqlite3 *db = pParse->db;
+ Expr *pNew;
+ int iVal = 0;
+
+ if( sqlite3ExprIsInteger(pExpr, &iVal) && iVal>=0 ){
+ Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0);
+ if( pVal==0 ) return;
+ ExprSetProperty(pVal, EP_IntValue);
+ pVal->u.iValue = iVal;
+ pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal);
+ }else{
+ Expr *pVal = sqlite3Expr(db, TK_REGISTER, 0);
+ if( pVal==0 ) return;
+ pVal->iTable = iReg;
+ pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal);
+ }
+ if( pNew ){
+ WhereTerm *pTerm;
+ int idx;
+ idx = whereClauseInsert(pWC, pNew, TERM_DYNAMIC|TERM_VIRTUAL);
+ pTerm = &pWC->a[idx];
+ pTerm->leftCursor = iCsr;
+ pTerm->eOperator = WO_AUX;
+ pTerm->eMatchOp = eMatchOp;
+ }
+}
+
+/*
+** Possibly add terms corresponding to the LIMIT and OFFSET clauses of the
+** SELECT statement passed as the second argument. These terms are only
+** added if:
+**
+** 1. The SELECT statement has a LIMIT clause, and
+** 2. The SELECT statement is not an aggregate or DISTINCT query, and
+** 3. The SELECT statement has exactly one object in its from clause, and
+** that object is a virtual table, and
+** 4. There are no terms in the WHERE clause that will not be passed
+** to the virtual table xBestIndex method.
+** 5. The ORDER BY clause, if any, will be made available to the xBestIndex
+** method.
+**
+** LIMIT and OFFSET terms are ignored by most of the planner code. They
+** exist only so that they may be passed to the xBestIndex method of the
+** single virtual table in the FROM clause of the SELECT.
+*/
+SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Select *p){
+ assert( p!=0 && p->pLimit!=0 ); /* 1 -- checked by caller */
+ if( p->pGroupBy==0
+ && (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */
+ && (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pTab)) /* 3 */
+ ){
+ ExprList *pOrderBy = p->pOrderBy;
+ int iCsr = p->pSrc->a[0].iCursor;
+ int ii;
+
+ /* Check condition (4). Return early if it is not met. */
+ for(ii=0; ii<pWC->nTerm; ii++){
+ if( pWC->a[ii].wtFlags & TERM_CODED ){
+ /* This term is a vector operation that has been decomposed into
+ ** other, subsequent terms. It can be ignored. See tag-20220128a */
+ assert( pWC->a[ii].wtFlags & TERM_VIRTUAL );
+ assert( pWC->a[ii].eOperator==WO_ROWVAL );
+ continue;
+ }
+ if( pWC->a[ii].nChild ){
+ /* If this term has child terms, then they are also part of the
+ ** pWC->a[] array. So this term can be ignored, as a LIMIT clause
+ ** will only be added if each of the child terms passes the
+ ** (leftCursor==iCsr) test below. */
+ continue;
+ }
+ if( pWC->a[ii].leftCursor!=iCsr ) return;
+ }
+
+ /* Check condition (5). Return early if it is not met. */
+ if( pOrderBy ){
+ for(ii=0; ii<pOrderBy->nExpr; ii++){
+ Expr *pExpr = pOrderBy->a[ii].pExpr;
+ if( pExpr->op!=TK_COLUMN ) return;
+ if( pExpr->iTable!=iCsr ) return;
+ if( pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) return;
+ }
+ }
+
+ /* All conditions are met. Add the terms to the where-clause object. */
+ assert( p->pLimit->op==TK_LIMIT );
+ whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft,
+ iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT);
+ if( p->iOffset>0 ){
+ whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight,
+ iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET);
+ }
+ }
+}
+
+/*
** Initialize a preallocated WhereClause structure.
*/
SQLITE_PRIVATE void sqlite3WhereClauseInit(
@@ -139080,6 +160568,7 @@ SQLITE_PRIVATE void sqlite3WhereClauseInit(
pWC->hasOr = 0;
pWC->pOuter = 0;
pWC->nTerm = 0;
+ pWC->nBase = 0;
pWC->nSlot = ArraySize(pWC->aStatic);
pWC->a = pWC->aStatic;
}
@@ -139090,22 +160579,36 @@ SQLITE_PRIVATE void sqlite3WhereClauseInit(
** sqlite3WhereClauseInit().
*/
SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause *pWC){
- int i;
- WhereTerm *a;
sqlite3 *db = pWC->pWInfo->pParse->db;
- for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){
- if( a->wtFlags & TERM_DYNAMIC ){
- sqlite3ExprDelete(db, a->pExpr);
+ assert( pWC->nTerm>=pWC->nBase );
+ if( pWC->nTerm>0 ){
+ WhereTerm *a = pWC->a;
+ WhereTerm *aLast = &pWC->a[pWC->nTerm-1];
+#ifdef SQLITE_DEBUG
+ int i;
+ /* Verify that every term past pWC->nBase is virtual */
+ for(i=pWC->nBase; i<pWC->nTerm; i++){
+ assert( (pWC->a[i].wtFlags & TERM_VIRTUAL)!=0 );
}
- if( a->wtFlags & TERM_ORINFO ){
- whereOrInfoDelete(db, a->u.pOrInfo);
- }else if( a->wtFlags & TERM_ANDINFO ){
- whereAndInfoDelete(db, a->u.pAndInfo);
+#endif
+ while(1){
+ assert( a->eMatchOp==0 || a->eOperator==WO_AUX );
+ if( a->wtFlags & TERM_DYNAMIC ){
+ sqlite3ExprDelete(db, a->pExpr);
+ }
+ if( a->wtFlags & (TERM_ORINFO|TERM_ANDINFO) ){
+ if( a->wtFlags & TERM_ORINFO ){
+ assert( (a->wtFlags & TERM_ANDINFO)==0 );
+ whereOrInfoDelete(db, a->u.pOrInfo);
+ }else{
+ assert( (a->wtFlags & TERM_ANDINFO)!=0 );
+ whereAndInfoDelete(db, a->u.pAndInfo);
+ }
+ }
+ if( a==aLast ) break;
+ a++;
}
}
- if( pWC->a!=pWC->aStatic ){
- sqlite3DbFree(db, pWC->a);
- }
}
@@ -139113,28 +160616,68 @@ SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause *pWC){
** These routines walk (recursively) an expression tree and generate
** a bitmask indicating which tables are used in that expression
** tree.
+**
+** sqlite3WhereExprUsage(MaskSet, Expr) ->
+**
+** Return a Bitmask of all tables referenced by Expr. Expr can be
+** be NULL, in which case 0 is returned.
+**
+** sqlite3WhereExprUsageNN(MaskSet, Expr) ->
+**
+** Same as sqlite3WhereExprUsage() except that Expr must not be
+** NULL. The "NN" suffix on the name stands for "Not Null".
+**
+** sqlite3WhereExprListUsage(MaskSet, ExprList) ->
+**
+** Return a Bitmask of all tables referenced by every expression
+** in the expression list ExprList. ExprList can be NULL, in which
+** case 0 is returned.
+**
+** sqlite3WhereExprUsageFull(MaskSet, ExprList) ->
+**
+** Internal use only. Called only by sqlite3WhereExprUsageNN() for
+** complex expressions that require pushing register values onto
+** the stack. Many calls to sqlite3WhereExprUsageNN() do not need
+** the more complex analysis done by this routine. Hence, the
+** computations done by this routine are broken out into a separate
+** "no-inline" function to avoid the stack push overhead in the
+** common case where it is not needed.
*/
-SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){
+static SQLITE_NOINLINE Bitmask sqlite3WhereExprUsageFull(
+ WhereMaskSet *pMaskSet,
+ Expr *p
+){
Bitmask mask;
- if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){
- return sqlite3WhereGetMask(pMaskSet, p->iTable);
- }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
- assert( p->op!=TK_IF_NULL_ROW );
- return 0;
- }
mask = (p->op==TK_IF_NULL_ROW) ? sqlite3WhereGetMask(pMaskSet, p->iTable) : 0;
if( p->pLeft ) mask |= sqlite3WhereExprUsageNN(pMaskSet, p->pLeft);
if( p->pRight ){
mask |= sqlite3WhereExprUsageNN(pMaskSet, p->pRight);
assert( p->x.pList==0 );
- }else if( ExprHasProperty(p, EP_xIsSelect) ){
+ }else if( ExprUseXSelect(p) ){
if( ExprHasProperty(p, EP_VarSelect) ) pMaskSet->bVarSelect = 1;
mask |= exprSelectUsage(pMaskSet, p->x.pSelect);
}else if( p->x.pList ){
mask |= sqlite3WhereExprListUsage(pMaskSet, p->x.pList);
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( (p->op==TK_FUNCTION || p->op==TK_AGG_FUNCTION) && ExprUseYWin(p) ){
+ assert( p->y.pWin!=0 );
+ mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pPartition);
+ mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pOrderBy);
+ mask |= sqlite3WhereExprUsage(pMaskSet, p->y.pWin->pFilter);
+ }
+#endif
return mask;
}
+SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){
+ if( p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){
+ return sqlite3WhereGetMask(pMaskSet, p->iTable);
+ }else if( ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
+ assert( p->op!=TK_IF_NULL_ROW );
+ return 0;
+ }
+ return sqlite3WhereExprUsageFull(pMaskSet, p);
+}
SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){
return p ? sqlite3WhereExprUsageNN(pMaskSet,p) : 0;
}
@@ -139151,7 +160694,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereExprListUsage(WhereMaskSet *pMaskSet, ExprLis
/*
-** Call exprAnalyze on all terms in a WHERE clause.
+** Call exprAnalyze on all terms in a WHERE clause.
**
** Note that exprAnalyze() might add new virtual terms onto the
** end of the WHERE clause. We do not want to analyze these new
@@ -139170,14 +160713,14 @@ SQLITE_PRIVATE void sqlite3WhereExprAnalyze(
/*
** For table-valued-functions, transform the function arguments into
-** new WHERE clause terms.
+** new WHERE clause terms.
**
** Each function argument translates into an equality constraint against
** a HIDDEN column in the table.
*/
SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(
Parse *pParse, /* Parsing context */
- struct SrcList_item *pItem, /* The FROM clause term to process */
+ SrcItem *pItem, /* The FROM clause term to process */
WhereClause *pWC /* Xfer function arguments to here */
){
Table *pTab;
@@ -139192,6 +160735,7 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(
if( pArgs==0 ) return;
for(j=k=0; j<pArgs->nExpr; j++){
Expr *pRhs;
+ u32 joinType;
while( k<pTab->nCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;}
if( k>=pTab->nCol ){
sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d",
@@ -139202,10 +160746,21 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(
if( pColRef==0 ) return;
pColRef->iTable = pItem->iCursor;
pColRef->iColumn = k++;
+ assert( ExprUseYTab(pColRef) );
pColRef->y.pTab = pTab;
- pRhs = sqlite3PExpr(pParse, TK_UPLUS,
+ pItem->colUsed |= sqlite3ExprColUsed(pColRef);
+ pRhs = sqlite3PExpr(pParse, TK_UPLUS,
sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs);
+ if( pItem->fg.jointype & (JT_LEFT|JT_RIGHT) ){
+ testcase( pItem->fg.jointype & JT_LEFT ); /* testtag-20230227a */
+ testcase( pItem->fg.jointype & JT_RIGHT ); /* testtag-20230227b */
+ joinType = EP_OuterON;
+ }else{
+ testcase( pItem->fg.jointype & JT_LTORJ ); /* testtag-20230227c */
+ joinType = EP_InnerON;
+ }
+ sqlite3SetJoinExpr(pTerm, pItem->iCursor, joinType);
whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
}
}
@@ -139244,19 +160799,19 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(
*/
typedef struct HiddenIndexInfo HiddenIndexInfo;
struct HiddenIndexInfo {
- WhereClause *pWC; /* The Where clause being analyzed */
- Parse *pParse; /* The parsing context */
+ WhereClause *pWC; /* The Where clause being analyzed */
+ Parse *pParse; /* The parsing context */
+ int eDistinct; /* Value to return from sqlite3_vtab_distinct() */
+ u32 mIn; /* Mask of terms that are <col> IN (...) */
+ u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */
+ sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST
+ ** because extra space is allocated to hold up
+ ** to nTerm such values */
};
/* Forward declaration of methods */
static int whereLoopResize(sqlite3*, WhereLoop*, int);
-/* Test variable that can be set to enable WHERE tracing */
-#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
-/***/ int sqlite3WhereTrace = 0;
-#endif
-
-
/*
** Return the estimated number of output rows from a WHERE clause
*/
@@ -139273,11 +160828,15 @@ SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo *pWInfo){
}
/*
-** Return TRUE if the WHERE clause returns rows in ORDER BY order.
-** Return FALSE if the output needs to be sorted.
+** Return the number of ORDER BY terms that are satisfied by the
+** WHERE clause. A return of 0 means that the output must be
+** completely sorted. A return equal to the number of ORDER BY
+** terms means that no sorting is needed at all. A return that
+** is positive but less than the number of ORDER BY terms means that
+** block sorting is required.
*/
SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
- return pWInfo->nOBSat;
+ return pWInfo->nOBSat<0 ? 0 : pWInfo->nOBSat;
}
/*
@@ -139298,7 +160857,7 @@ SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
** be the continuation for the inner-most loop.
**
** It is always safe for this routine to return the continuation of the
-** inner-most loop, in the sense that a correct answer will result.
+** inner-most loop, in the sense that a correct answer will result.
** Returning the continuation the second inner loop is an optimization
** that might make the code run a little faster, but should not change
** the final answer.
@@ -139306,13 +160865,39 @@ SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
SQLITE_PRIVATE int sqlite3WhereOrderByLimitOptLabel(WhereInfo *pWInfo){
WhereLevel *pInner;
if( !pWInfo->bOrderedInnerLoop ){
- /* The ORDER BY LIMIT optimization does not apply. Jump to the
+ /* The ORDER BY LIMIT optimization does not apply. Jump to the
** continuation of the inner-most loop. */
return pWInfo->iContinue;
}
pInner = &pWInfo->a[pWInfo->nLevel-1];
assert( pInner->addrNxt!=0 );
- return pInner->addrNxt;
+ return pInner->pRJ ? pWInfo->iContinue : pInner->addrNxt;
+}
+
+/*
+** While generating code for the min/max optimization, after handling
+** the aggregate-step call to min() or max(), check to see if any
+** additional looping is required. If the output order is such that
+** we are certain that the correct answer has already been found, then
+** code an OP_Goto to by pass subsequent processing.
+**
+** Any extra OP_Goto that is coded here is an optimization. The
+** correct answer should be obtained regardless. This OP_Goto just
+** makes the answer appear faster.
+*/
+SQLITE_PRIVATE void sqlite3WhereMinMaxOptEarlyOut(Vdbe *v, WhereInfo *pWInfo){
+ WhereLevel *pInner;
+ int i;
+ if( !pWInfo->bOrderedInnerLoop ) return;
+ if( pWInfo->nOBSat==0 ) return;
+ for(i=pWInfo->nLevel-1; i>=0; i--){
+ pInner = &pWInfo->a[i];
+ if( (pInner->pWLoop->wsFlags & WHERE_COLUMN_IN)!=0 ){
+ sqlite3VdbeGoto(v, pInner->addrNxt);
+ return;
+ }
+ }
+ sqlite3VdbeGoto(v, pWInfo->iBreak);
}
/*
@@ -139334,10 +160919,10 @@ SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo *pWInfo){
/*
** Return ONEPASS_OFF (0) if an UPDATE or DELETE statement is unable to
-** operate directly on the rowis returned by a WHERE clause. Return
+** operate directly on the rowids returned by a WHERE clause. Return
** ONEPASS_SINGLE (1) if the statement can operation directly because only
** a single row is to be changed. Return ONEPASS_MULTI (2) if the one-pass
-** optimization can be used on multiple
+** optimization can be used on multiple
**
** If the ONEPASS optimization is used (if this routine returns true)
** then also write the indices of open cursors used by ONEPASS
@@ -139362,6 +160947,14 @@ SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){
}
/*
+** Return TRUE if the WHERE loop uses the OP_DeferredSeek opcode to move
+** the data cursor to the row selected by the index cursor.
+*/
+SQLITE_PRIVATE int sqlite3WhereUsesDeferredSeek(WhereInfo *pWInfo){
+ return pWInfo->bDeferredSeek;
+}
+
+/*
** Move the content of pSrc into pDest
*/
static void whereOrMove(WhereOrSet *pDest, WhereOrSet *pSrc){
@@ -139416,7 +161009,12 @@ whereOrInsert_done:
SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){
int i;
assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 );
- for(i=0; i<pMaskSet->n; i++){
+ assert( pMaskSet->n>0 || pMaskSet->ix[0]<0 );
+ assert( iCursor>=-1 );
+ if( pMaskSet->ix[0]==iCursor ){
+ return 1;
+ }
+ for(i=1; i<pMaskSet->n; i++){
if( pMaskSet->ix[i]==iCursor ){
return MASKBIT(i);
}
@@ -139424,6 +161022,30 @@ SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){
return 0;
}
+/* Allocate memory that is automatically freed when pWInfo is freed.
+*/
+SQLITE_PRIVATE void *sqlite3WhereMalloc(WhereInfo *pWInfo, u64 nByte){
+ WhereMemBlock *pBlock;
+ pBlock = sqlite3DbMallocRawNN(pWInfo->pParse->db, nByte+sizeof(*pBlock));
+ if( pBlock ){
+ pBlock->pNext = pWInfo->pMemToFree;
+ pBlock->sz = nByte;
+ pWInfo->pMemToFree = pBlock;
+ pBlock++;
+ }
+ return (void*)pBlock;
+}
+SQLITE_PRIVATE void *sqlite3WhereRealloc(WhereInfo *pWInfo, void *pOld, u64 nByte){
+ void *pNew = sqlite3WhereMalloc(pWInfo, nByte);
+ if( pNew && pOld ){
+ WhereMemBlock *pOldBlk = (WhereMemBlock*)pOld;
+ pOldBlk--;
+ assert( pOldBlk->sz<nByte );
+ memcpy(pNew, pOld, pOldBlk->sz);
+ }
+ return pNew;
+}
+
/*
** Create a new mask for cursor iCursor.
**
@@ -139438,6 +161060,18 @@ static void createMask(WhereMaskSet *pMaskSet, int iCursor){
}
/*
+** If the right-hand branch of the expression is a TK_COLUMN, then return
+** a pointer to the right-hand branch. Otherwise, return NULL.
+*/
+static Expr *whereRightSubexprIsColumn(Expr *p){
+ p = sqlite3ExprSkipCollateAndLikely(p->pRight);
+ if( ALWAYS(p!=0) && p->op==TK_COLUMN && !ExprHasProperty(p, EP_FixedCol) ){
+ return p;
+ }
+ return 0;
+}
+
+/*
** Advance to the next WhereTerm that matches according to the criteria
** established when the pScan object was initialized by whereScanInit().
** Return NULL if there are no more matching WhereTerms.
@@ -139456,18 +161090,20 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
iColumn = pScan->aiColumn[pScan->iEquiv-1];
iCur = pScan->aiCur[pScan->iEquiv-1];
assert( pWC!=0 );
+ assert( iCur>=0 );
do{
for(pTerm=pWC->a+k; k<pWC->nTerm; k++, pTerm++){
+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 || pTerm->leftCursor<0 );
if( pTerm->leftCursor==iCur
- && pTerm->u.leftColumn==iColumn
+ && pTerm->u.x.leftColumn==iColumn
&& (iColumn!=XN_EXPR
|| sqlite3ExprCompareSkip(pTerm->pExpr->pLeft,
pScan->pIdxExpr,iCur)==0)
- && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin))
+ && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_OuterON))
){
if( (pTerm->eOperator & WO_EQUIV)!=0
&& pScan->nEquiv<ArraySize(pScan->aiCur)
- && (pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight))->op==TK_COLUMN
+ && (pX = whereRightSubexprIsColumn(pTerm->pExpr))!=0
){
int j;
for(j=0; j<pScan->nEquiv; j++){
@@ -139492,15 +161128,15 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
continue;
}
assert(pX->pLeft);
- pColl = sqlite3BinaryCompareCollSeq(pParse,
- pX->pLeft, pX->pRight);
+ pColl = sqlite3ExprCompareCollSeq(pParse, pX);
if( pColl==0 ) pColl = pParse->db->pDfltColl;
if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){
continue;
}
}
if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0
- && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN
+ && (pX = pTerm->pExpr->pRight, ALWAYS(pX!=0))
+ && pX->op==TK_COLUMN
&& pX->iTable==pScan->aiCur[0]
&& pX->iColumn==pScan->aiColumn[0]
){
@@ -139509,6 +161145,18 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
}
pScan->pWC = pWC;
pScan->k = k+1;
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x20000 ){
+ int ii;
+ sqlite3DebugPrintf("SCAN-TERM %p: nEquiv=%d",
+ pTerm, pScan->nEquiv);
+ for(ii=0; ii<pScan->nEquiv; ii++){
+ sqlite3DebugPrintf(" {%d:%d}",
+ pScan->aiCur[ii], pScan->aiColumn[ii]);
+ }
+ sqlite3DebugPrintf("\n");
+ }
+#endif
return pTerm;
}
}
@@ -139525,6 +161173,17 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
}
/*
+** This is whereScanInit() for the case of an index on an expression.
+** It is factored out into a separate tail-recursion subroutine so that
+** the normal whereScanInit() routine, which is a high-runner, does not
+** need to push registers onto the stack as part of its prologue.
+*/
+static SQLITE_NOINLINE WhereTerm *whereScanInitIndexExpr(WhereScan *pScan){
+ pScan->idxaff = sqlite3ExprAffinity(pScan->pIdxExpr);
+ return whereScanNext(pScan);
+}
+
+/*
** Initialize a WHERE clause scanner object. Return a pointer to the
** first match. Return NULL if there are no matches.
**
@@ -139556,27 +161215,29 @@ static WhereTerm *whereScanInit(
pScan->pIdxExpr = 0;
pScan->idxaff = 0;
pScan->zCollName = 0;
+ pScan->opMask = opMask;
+ pScan->k = 0;
+ pScan->aiCur[0] = iCur;
+ pScan->nEquiv = 1;
+ pScan->iEquiv = 1;
if( pIdx ){
int j = iColumn;
iColumn = pIdx->aiColumn[j];
- if( iColumn==XN_EXPR ){
- pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr;
- pScan->zCollName = pIdx->azColl[j];
- }else if( iColumn==pIdx->pTable->iPKey ){
+ if( iColumn==pIdx->pTable->iPKey ){
iColumn = XN_ROWID;
}else if( iColumn>=0 ){
pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
pScan->zCollName = pIdx->azColl[j];
+ }else if( iColumn==XN_EXPR ){
+ pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr;
+ pScan->zCollName = pIdx->azColl[j];
+ pScan->aiColumn[0] = XN_EXPR;
+ return whereScanInitIndexExpr(pScan);
}
}else if( iColumn==XN_EXPR ){
return 0;
}
- pScan->opMask = opMask;
- pScan->k = 0;
- pScan->aiCur[0] = iCur;
pScan->aiColumn[0] = iColumn;
- pScan->nEquiv = 1;
- pScan->iEquiv = 1;
return whereScanNext(pScan);
}
@@ -139586,7 +161247,7 @@ static WhereTerm *whereScanInit(
** if pIdx!=0 and <op> is one of the WO_xx operator codes specified by
** the op parameter. Return a pointer to the term. Return 0 if not found.
**
-** If pIdx!=0 then it must be one of the indexes of table iCur.
+** If pIdx!=0 then it must be one of the indexes of table iCur.
** Search for terms matching the iColumn-th column of pIdx
** rather than the iColumn-th column of table iCur.
**
@@ -139650,8 +161311,9 @@ static int findIndexCol(
const char *zColl = pIdx->azColl[iCol];
for(i=0; i<pList->nExpr; i++){
- Expr *p = sqlite3ExprSkipCollate(pList->a[i].pExpr);
- if( p->op==TK_COLUMN
+ Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr);
+ if( ALWAYS(p!=0)
+ && (p->op==TK_COLUMN || p->op==TK_AGG_COLUMN)
&& p->iColumn==pIdx->aiColumn[iCol]
&& p->iTable==iBase
){
@@ -139699,23 +161361,25 @@ static int isDistinctRedundant(
){
Table *pTab;
Index *pIdx;
- int i;
+ int i;
int iBase;
/* If there is more than one table or sub-select in the FROM clause of
- ** this query, then it will not be possible to show that the DISTINCT
+ ** this query, then it will not be possible to show that the DISTINCT
** clause is redundant. */
if( pTabList->nSrc!=1 ) return 0;
iBase = pTabList->a[0].iCursor;
pTab = pTabList->a[0].pTab;
- /* If any of the expressions is an IPK column on table iBase, then return
+ /* If any of the expressions is an IPK column on table iBase, then return
** true. Note: The (p->iTable==iBase) part of this test may be false if the
** current SELECT is a correlated sub-query.
*/
for(i=0; i<pDistinct->nExpr; i++){
- Expr *p = sqlite3ExprSkipCollate(pDistinct->a[i].pExpr);
- if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1;
+ Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr);
+ if( NEVER(p==0) ) continue;
+ if( p->op!=TK_COLUMN && p->op!=TK_AGG_COLUMN ) continue;
+ if( p->iTable==iBase && p->iColumn<0 ) return 1;
}
/* Loop through all indices on the table, checking each to see if it makes
@@ -139733,6 +161397,7 @@ static int isDistinctRedundant(
*/
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
if( !IsUniqueIndex(pIdx) ) continue;
+ if( pIdx->pPartIdxWhere ) continue;
for(i=0; i<pIdx->nKeyCol; i++){
if( 0==sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask)0, WO_EQ, pIdx) ){
if( findIndexCol(pParse, pDistinct, iBase, pIdx, i)<0 ) break;
@@ -139760,20 +161425,20 @@ static LogEst estLog(LogEst N){
** Convert OP_Column opcodes to OP_Copy in previously generated code.
**
** This routine runs over generated VDBE code and translates OP_Column
-** opcodes into OP_Copy when the table is being accessed via co-routine
+** opcodes into OP_Copy when the table is being accessed via co-routine
** instead of via table lookup.
**
-** If the bIncrRowid parameter is 0, then any OP_Rowid instructions on
-** cursor iTabCur are transformed into OP_Null. Or, if bIncrRowid is non-zero,
-** then each OP_Rowid is transformed into an instruction to increment the
-** value stored in its output register.
+** If the iAutoidxCur is not zero, then any OP_Rowid instructions on
+** cursor iTabCur are transformed into OP_Sequence opcode for the
+** iAutoidxCur cursor, in order to generate unique rowids for the
+** automatic index being generated.
*/
static void translateColumnToCopy(
Parse *pParse, /* Parsing context */
int iStart, /* Translate from this opcode to the end */
int iTabCur, /* OP_Column/OP_Rowid references to this table */
int iRegister, /* The first column is in this register */
- int bIncrRowid /* If non-zero, transform OP_rowid to OP_AddImm(1) */
+ int iAutoidxCur /* If non-zero, cursor of autoindex being generated */
){
Vdbe *v = pParse->pVdbe;
VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart);
@@ -139782,21 +161447,30 @@ static void translateColumnToCopy(
for(; iStart<iEnd; iStart++, pOp++){
if( pOp->p1!=iTabCur ) continue;
if( pOp->opcode==OP_Column ){
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("TRANSLATE OP_Column to OP_Copy at %d\n", iStart);
+ }
+#endif
pOp->opcode = OP_Copy;
pOp->p1 = pOp->p2 + iRegister;
pOp->p2 = pOp->p3;
pOp->p3 = 0;
+ pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */
}else if( pOp->opcode==OP_Rowid ){
- if( bIncrRowid ){
- /* Increment the value stored in the P2 operand of the OP_Rowid. */
- pOp->opcode = OP_AddImm;
- pOp->p1 = pOp->p2;
- pOp->p2 = 1;
- }else{
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("TRANSLATE OP_Rowid to OP_Sequence at %d\n", iStart);
+ }
+#endif
+ pOp->opcode = OP_Sequence;
+ pOp->p1 = iAutoidxCur;
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ if( iAutoidxCur==0 ){
pOp->opcode = OP_Null;
- pOp->p1 = 0;
pOp->p3 = 0;
}
+#endif
}
}
}
@@ -139808,16 +161482,18 @@ static void translateColumnToCopy(
** are no-ops.
*/
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED)
-static void TRACE_IDX_INPUTS(sqlite3_index_info *p){
+static void whereTraceIndexInfoInputs(sqlite3_index_info *p){
int i;
- if( !sqlite3WhereTrace ) return;
+ if( (sqlite3WhereTrace & 0x10)==0 ) return;
for(i=0; i<p->nConstraint; i++){
- sqlite3DebugPrintf(" constraint[%d]: col=%d termid=%d op=%d usabled=%d\n",
+ sqlite3DebugPrintf(
+ " constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n",
i,
p->aConstraint[i].iColumn,
p->aConstraint[i].iTermOffset,
p->aConstraint[i].op,
- p->aConstraint[i].usable);
+ p->aConstraint[i].usable,
+ sqlite3_vtab_collation(p,i));
}
for(i=0; i<p->nOrderBy; i++){
sqlite3DebugPrintf(" orderby[%d]: col=%d desc=%d\n",
@@ -139826,9 +161502,9 @@ static void TRACE_IDX_INPUTS(sqlite3_index_info *p){
p->aOrderBy[i].desc);
}
}
-static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){
+static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){
int i;
- if( !sqlite3WhereTrace ) return;
+ if( (sqlite3WhereTrace & 0x10)==0 ) return;
for(i=0; i<p->nConstraint; i++){
sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n",
i,
@@ -139842,10 +161518,47 @@ static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){
sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows);
}
#else
-#define TRACE_IDX_INPUTS(A)
-#define TRACE_IDX_OUTPUTS(A)
+#define whereTraceIndexInfoInputs(A)
+#define whereTraceIndexInfoOutputs(A)
#endif
+/*
+** We know that pSrc is an operand of an outer join. Return true if
+** pTerm is a constraint that is compatible with that join.
+**
+** pTerm must be EP_OuterON if pSrc is the right operand of an
+** outer join. pTerm can be either EP_OuterON or EP_InnerON if pSrc
+** is the left operand of a RIGHT join.
+**
+** See https://sqlite.org/forum/forumpost/206d99a16dd9212f
+** for an example of a WHERE clause constraints that may not be used on
+** the right table of a RIGHT JOIN because the constraint implies a
+** not-NULL condition on the left table of the RIGHT JOIN.
+*/
+static int constraintCompatibleWithOuterJoin(
+ const WhereTerm *pTerm, /* WHERE clause term to check */
+ const SrcItem *pSrc /* Table we are trying to access */
+){
+ assert( (pSrc->fg.jointype&(JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ); /* By caller */
+ testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT );
+ testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ );
+ testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) )
+ testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
+ if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
+ || pTerm->pExpr->w.iJoin != pSrc->iCursor
+ ){
+ return 0;
+ }
+ if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0
+ && ExprHasProperty(pTerm->pExpr, EP_InnerON)
+ ){
+ return 0;
+ }
+ return 1;
+}
+
+
+
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/*
** Return TRUE if the WHERE clause term pTerm is of a form where it
@@ -139853,25 +161566,23 @@ static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){
** index existed.
*/
static int termCanDriveIndex(
- WhereTerm *pTerm, /* WHERE clause term to check */
- struct SrcList_item *pSrc, /* Table we are trying to access */
- Bitmask notReady /* Tables in outer loops of the join */
+ const WhereTerm *pTerm, /* WHERE clause term to check */
+ const SrcItem *pSrc, /* Table we are trying to access */
+ const Bitmask notReady /* Tables in outer loops of the join */
){
char aff;
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0;
- if( (pSrc->fg.jointype & JT_LEFT)
- && !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
- && (pTerm->eOperator & WO_IS)
+ assert( (pSrc->fg.jointype & JT_RIGHT)==0 );
+ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0
+ && !constraintCompatibleWithOuterJoin(pTerm,pSrc)
){
- /* Cannot use an IS term from the WHERE clause as an index driver for
- ** the RHS of a LEFT JOIN. Such a term can only be used if it is from
- ** the ON clause. */
- return 0;
+ return 0; /* See https://sqlite.org/forum/forumpost/51e6959f61 */
}
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
- if( pTerm->u.leftColumn<0 ) return 0;
- aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
+ if( pTerm->u.x.leftColumn<0 ) return 0;
+ aff = pSrc->pTab->aCol[pTerm->u.x.leftColumn].affinity;
if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0;
testcase( pTerm->pExpr->op==TK_IS );
return 1;
@@ -139880,16 +161591,66 @@ static int termCanDriveIndex(
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
+
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+/*
+** Argument pIdx represents an automatic index that the current statement
+** will create and populate. Add an OP_Explain with text of the form:
+**
+** CREATE AUTOMATIC INDEX ON <table>(<cols>) [WHERE <expr>]
+**
+** This is only required if sqlite3_stmt_scanstatus() is enabled, to
+** associate an SQLITE_SCANSTAT_NCYCLE and SQLITE_SCANSTAT_NLOOP
+** values with. In order to avoid breaking legacy code and test cases,
+** the OP_Explain is not added if this is an EXPLAIN QUERY PLAN command.
+*/
+static void explainAutomaticIndex(
+ Parse *pParse,
+ Index *pIdx, /* Automatic index to explain */
+ int bPartial, /* True if pIdx is a partial index */
+ int *pAddrExplain /* OUT: Address of OP_Explain */
+){
+ if( IS_STMT_SCANSTATUS(pParse->db) && pParse->explain!=2 ){
+ Table *pTab = pIdx->pTable;
+ const char *zSep = "";
+ char *zText = 0;
+ int ii = 0;
+ sqlite3_str *pStr = sqlite3_str_new(pParse->db);
+ sqlite3_str_appendf(pStr,"CREATE AUTOMATIC INDEX ON %s(", pTab->zName);
+ assert( pIdx->nColumn>1 );
+ assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID );
+ for(ii=0; ii<(pIdx->nColumn-1); ii++){
+ const char *zName = 0;
+ int iCol = pIdx->aiColumn[ii];
+
+ zName = pTab->aCol[iCol].zCnName;
+ sqlite3_str_appendf(pStr, "%s%s", zSep, zName);
+ zSep = ", ";
+ }
+ zText = sqlite3_str_finish(pStr);
+ if( zText==0 ){
+ sqlite3OomFault(pParse->db);
+ }else{
+ *pAddrExplain = sqlite3VdbeExplain(
+ pParse, 0, "%s)%s", zText, (bPartial ? " WHERE <expr>" : "")
+ );
+ sqlite3_free(zText);
+ }
+ }
+}
+#else
+# define explainAutomaticIndex(a,b,c,d)
+#endif
+
/*
** Generate code to construct the Index object for an automatic index
** and to set up the WhereLevel object pLevel so that the code generator
** makes use of the automatic index.
*/
-static void constructAutomaticIndex(
+static SQLITE_NOINLINE void constructAutomaticIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
- struct SrcList_item *pSrc, /* The FROM clause term to get the next index */
- Bitmask notReady, /* Mask of cursors that are not available */
+ const Bitmask notReady, /* Mask of cursors that are not available */
WhereLevel *pLevel /* Write new index here */
){
int nKeyCol; /* Number of columns in the constructed index */
@@ -139909,12 +161670,17 @@ static void constructAutomaticIndex(
char *zNotUsed; /* Extra space on the end of pIdx */
Bitmask idxCols; /* Bitmap of columns used for indexing */
Bitmask extraCols; /* Bitmap of additional columns */
- u8 sentWarning = 0; /* True if a warnning has been issued */
+ u8 sentWarning = 0; /* True if a warning has been issued */
+ u8 useBloomFilter = 0; /* True to also add a Bloom filter */
Expr *pPartial = 0; /* Partial Index Expression */
int iContinue = 0; /* Jump here to skip excluded rows */
- struct SrcList_item *pTabItem; /* FROM clause term being indexed */
+ SrcList *pTabList; /* The complete FROM clause */
+ SrcItem *pSrc; /* The FROM clause term to get the next index */
int addrCounter = 0; /* Address where integer counter is initialized */
int regBase; /* Array of registers where record is assembled */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrExp = 0; /* Address of OP_Explain */
+#endif
/* Generate code to skip over the creation and initialization of the
** transient index on 2nd and subsequent iterations of the loop. */
@@ -139925,31 +161691,35 @@ static void constructAutomaticIndex(
/* Count the number of columns that will be added to the index
** and used to match WHERE clause constraints */
nKeyCol = 0;
+ pTabList = pWC->pWInfo->pTabList;
+ pSrc = &pTabList->a[pLevel->iFrom];
pTable = pSrc->pTab;
pWCEnd = &pWC->a[pWC->nTerm];
pLoop = pLevel->pWLoop;
idxCols = 0;
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
Expr *pExpr = pTerm->pExpr;
- assert( !ExprHasProperty(pExpr, EP_FromJoin) /* prereq always non-zero */
- || pExpr->iRightJoinTable!=pSrc->iCursor /* for the right-hand */
- || pLoop->prereq!=0 ); /* table of a LEFT JOIN */
- if( pLoop->prereq==0
- && (pTerm->wtFlags & TERM_VIRTUAL)==0
- && !ExprHasProperty(pExpr, EP_FromJoin)
- && sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor) ){
- pPartial = sqlite3ExprAnd(pParse->db, pPartial,
+ /* Make the automatic index a partial index if there are terms in the
+ ** WHERE clause (or the ON clause of a LEFT join) that constrain which
+ ** rows of the target table (pSrc) that can be used. */
+ if( (pTerm->wtFlags & TERM_VIRTUAL)==0
+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom)
+ ){
+ pPartial = sqlite3ExprAnd(pParse, pPartial,
sqlite3ExprDup(pParse->db, pExpr, 0));
}
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
- int iCol = pTerm->u.leftColumn;
- Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
+ int iCol;
+ Bitmask cMask;
+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
+ iCol = pTerm->u.x.leftColumn;
+ cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
testcase( iCol==BMS );
testcase( iCol==BMS-1 );
if( !sentWarning ){
sqlite3_log(SQLITE_WARNING_AUTOINDEX,
"automatic index on %s(%s)", pTable->zName,
- pTable->aCol[iCol].zName);
+ pTable->aCol[iCol].zCnName);
sentWarning = 1;
}
if( (idxCols & cMask)==0 ){
@@ -139961,7 +161731,7 @@ static void constructAutomaticIndex(
}
}
}
- assert( nKeyCol>0 );
+ assert( nKeyCol>0 || pParse->db->mallocFailed );
pLoop->u.btree.nEq = pLoop->nLTerm = nKeyCol;
pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED
| WHERE_AUTO_INDEX;
@@ -139974,7 +161744,11 @@ static void constructAutomaticIndex(
** original table changes and the index and table cannot both be used
** if they go out of sync.
*/
- extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1));
+ if( IsView(pTable) ){
+ extraCols = ALLBITS;
+ }else{
+ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1));
+ }
mxBitCol = MIN(BMS-1,pTable->nCol);
testcase( pTable->nCol==BMS-1 );
testcase( pTable->nCol==BMS-2 );
@@ -139995,17 +161769,31 @@ static void constructAutomaticIndex(
idxCols = 0;
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
- int iCol = pTerm->u.leftColumn;
- Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
+ int iCol;
+ Bitmask cMask;
+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
+ iCol = pTerm->u.x.leftColumn;
+ cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
testcase( iCol==BMS-1 );
testcase( iCol==BMS );
if( (idxCols & cMask)==0 ){
Expr *pX = pTerm->pExpr;
idxCols |= cMask;
- pIdx->aiColumn[n] = pTerm->u.leftColumn;
- pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
+ pIdx->aiColumn[n] = pTerm->u.x.leftColumn;
+ pColl = sqlite3ExprCompareCollSeq(pParse, pX);
+ assert( pColl!=0 || pParse->nErr>0 ); /* TH3 collate01.800 */
pIdx->azColl[n] = pColl ? pColl->zName : sqlite3StrBINARY;
n++;
+ if( ALWAYS(pX->pLeft!=0)
+ && sqlite3ExprAffinity(pX->pLeft)!=SQLITE_AFF_TEXT
+ ){
+ /* TUNING: only use a Bloom filter on an automatic index
+ ** if one or more key columns has the ability to hold numeric
+ ** values, since strings all have the same hash in the Bloom
+ ** filter implementation and hence a Bloom filter on a text column
+ ** is not usually helpful. */
+ useBloomFilter = 1;
+ }
}
}
}
@@ -140032,26 +161820,32 @@ static void constructAutomaticIndex(
pIdx->azColl[n] = sqlite3StrBINARY;
/* Create the automatic index */
+ explainAutomaticIndex(pParse, pIdx, pPartial!=0, &addrExp);
assert( pLevel->iIdxCur>=0 );
pLevel->iIdxCur = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1);
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
VdbeComment((v, "for %s", pTable->zName));
+ if( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) && useBloomFilter ){
+ sqlite3WhereExplainBloomFilter(pParse, pWC->pWInfo, pLevel);
+ pLevel->regFilter = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter);
+ }
/* Fill the automatic index with content */
- pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom];
- if( pTabItem->fg.viaCoroutine ){
- int regYield = pTabItem->regReturn;
+ assert( pSrc == &pWC->pWInfo->pTabList->a[pLevel->iFrom] );
+ if( pSrc->fg.viaCoroutine ){
+ int regYield = pSrc->regReturn;
addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0);
- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSrc->addrFillSub);
addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
VdbeCoverage(v);
- VdbeComment((v, "next row of %s", pTabItem->pTab->zName));
+ VdbeComment((v, "next row of %s", pSrc->pTab->zName));
}else{
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
}
if( pPartial ){
- iContinue = sqlite3VdbeMakeLabel(v);
+ iContinue = sqlite3VdbeMakeLabel(pParse);
sqlite3ExprIfFalse(pParse, pPartial, iContinue, SQLITE_JUMPIFNULL);
pLoop->wsFlags |= WHERE_PARTIALIDX;
}
@@ -140059,46 +161853,194 @@ static void constructAutomaticIndex(
regBase = sqlite3GenerateIndexKey(
pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0
);
+ if( pLevel->regFilter ){
+ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0,
+ regBase, pLoop->u.btree.nEq);
+ }
+ sqlite3VdbeScanStatusCounters(v, addrExp, addrExp, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue);
- if( pTabItem->fg.viaCoroutine ){
+ if( pSrc->fg.viaCoroutine ){
sqlite3VdbeChangeP2(v, addrCounter, regBase+n);
testcase( pParse->db->mallocFailed );
+ assert( pLevel->iIdxCur>0 );
translateColumnToCopy(pParse, addrTop, pLevel->iTabCur,
- pTabItem->regResult, 1);
+ pSrc->regResult, pLevel->iIdxCur);
sqlite3VdbeGoto(v, addrTop);
+ pSrc->fg.viaCoroutine = 0;
}else{
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
+ sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX);
}
- sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX);
sqlite3VdbeJumpHere(v, addrTop);
sqlite3ReleaseTempReg(pParse, regRecord);
-
+
/* Jump here when skipping the initialization */
sqlite3VdbeJumpHere(v, addrInit);
+ sqlite3VdbeScanStatusRange(v, addrExp, addrExp, -1);
end_auto_index_create:
sqlite3ExprDelete(pParse->db, pPartial);
}
#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
+/*
+** Generate bytecode that will initialize a Bloom filter that is appropriate
+** for pLevel.
+**
+** If there are inner loops within pLevel that have the WHERE_BLOOMFILTER
+** flag set, initialize a Bloomfilter for them as well. Except don't do
+** this recursive initialization if the SQLITE_BloomPulldown optimization has
+** been turned off.
+**
+** When the Bloom filter is initialized, the WHERE_BLOOMFILTER flag is cleared
+** from the loop, but the regFilter value is set to a register that implements
+** the Bloom filter. When regFilter is positive, the
+** sqlite3WhereCodeOneLoopStart() will generate code to test the Bloom filter
+** and skip the subsequence B-Tree seek if the Bloom filter indicates that
+** no matching rows exist.
+**
+** This routine may only be called if it has previously been determined that
+** the loop would benefit from a Bloom filter, and the WHERE_BLOOMFILTER bit
+** is set.
+*/
+static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
+ WhereInfo *pWInfo, /* The WHERE clause */
+ int iLevel, /* Index in pWInfo->a[] that is pLevel */
+ WhereLevel *pLevel, /* Make a Bloom filter for this FROM term */
+ Bitmask notReady /* Loops that are not ready */
+){
+ int addrOnce; /* Address of opening OP_Once */
+ int addrTop; /* Address of OP_Rewind */
+ int addrCont; /* Jump here to skip a row */
+ const WhereTerm *pTerm; /* For looping over WHERE clause terms */
+ const WhereTerm *pWCEnd; /* Last WHERE clause term */
+ Parse *pParse = pWInfo->pParse; /* Parsing context */
+ Vdbe *v = pParse->pVdbe; /* VDBE under construction */
+ WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */
+ int iCur; /* Cursor for table getting the filter */
+ IndexedExpr *saved_pIdxEpr; /* saved copy of Parse.pIdxEpr */
+ IndexedExpr *saved_pIdxPartExpr; /* saved copy of Parse.pIdxPartExpr */
+
+ saved_pIdxEpr = pParse->pIdxEpr;
+ saved_pIdxPartExpr = pParse->pIdxPartExpr;
+ pParse->pIdxEpr = 0;
+ pParse->pIdxPartExpr = 0;
+
+ assert( pLoop!=0 );
+ assert( v!=0 );
+ assert( pLoop->wsFlags & WHERE_BLOOMFILTER );
+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 );
+
+ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+ do{
+ const SrcList *pTabList;
+ const SrcItem *pItem;
+ const Table *pTab;
+ u64 sz;
+ int iSrc;
+ sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel);
+ addrCont = sqlite3VdbeMakeLabel(pParse);
+ iCur = pLevel->iTabCur;
+ pLevel->regFilter = ++pParse->nMem;
+
+ /* The Bloom filter is a Blob held in a register. Initialize it
+ ** to zero-filled blob of at least 80K bits, but maybe more if the
+ ** estimated size of the table is larger. We could actually
+ ** measure the size of the table at run-time using OP_Count with
+ ** P3==1 and use that value to initialize the blob. But that makes
+ ** testing complicated. By basing the blob size on the value in the
+ ** sqlite_stat1 table, testing is much easier.
+ */
+ pTabList = pWInfo->pTabList;
+ iSrc = pLevel->iFrom;
+ pItem = &pTabList->a[iSrc];
+ assert( pItem!=0 );
+ pTab = pItem->pTab;
+ assert( pTab!=0 );
+ sz = sqlite3LogEstToInt(pTab->nRowLogEst);
+ if( sz<10000 ){
+ sz = 10000;
+ }else if( sz>10000000 ){
+ sz = 10000000;
+ }
+ sqlite3VdbeAddOp2(v, OP_Blob, (int)sz, pLevel->regFilter);
+
+ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
+ pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm];
+ for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
+ Expr *pExpr = pTerm->pExpr;
+ if( (pTerm->wtFlags & TERM_VIRTUAL)==0
+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc)
+ ){
+ sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
+ }
+ }
+ if( pLoop->wsFlags & WHERE_IPK ){
+ int r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1);
+ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, 1);
+ sqlite3ReleaseTempReg(pParse, r1);
+ }else{
+ Index *pIdx = pLoop->u.btree.pIndex;
+ int n = pLoop->u.btree.nEq;
+ int r1 = sqlite3GetTempRange(pParse, n);
+ int jj;
+ for(jj=0; jj<n; jj++){
+ assert( pIdx->pTable==pItem->pTab );
+ sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iCur, jj, r1+jj);
+ }
+ sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n);
+ sqlite3ReleaseTempRange(pParse, r1, n);
+ }
+ sqlite3VdbeResolveLabel(v, addrCont);
+ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1);
+ VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addrTop);
+ pLoop->wsFlags &= ~WHERE_BLOOMFILTER;
+ if( OptimizationDisabled(pParse->db, SQLITE_BloomPulldown) ) break;
+ while( ++iLevel < pWInfo->nLevel ){
+ const SrcItem *pTabItem;
+ pLevel = &pWInfo->a[iLevel];
+ pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
+ if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ) ) continue;
+ pLoop = pLevel->pWLoop;
+ if( NEVER(pLoop==0) ) continue;
+ if( pLoop->prereq & notReady ) continue;
+ if( (pLoop->wsFlags & (WHERE_BLOOMFILTER|WHERE_COLUMN_IN))
+ ==WHERE_BLOOMFILTER
+ ){
+ /* This is a candidate for bloom-filter pull-down (early evaluation).
+ ** The test that WHERE_COLUMN_IN is omitted is important, as we are
+ ** not able to do early evaluation of bloom filters that make use of
+ ** the IN operator */
+ break;
+ }
+ }
+ }while( iLevel < pWInfo->nLevel );
+ sqlite3VdbeJumpHere(v, addrOnce);
+ pParse->pIdxEpr = saved_pIdxEpr;
+ pParse->pIdxPartExpr = saved_pIdxPartExpr;
+}
+
+
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
-** Allocate and populate an sqlite3_index_info structure. It is the
+** Allocate and populate an sqlite3_index_info structure. It is the
** responsibility of the caller to eventually release the structure
-** by passing the pointer returned by this function to sqlite3_free().
+** by passing the pointer returned by this function to freeIndexInfo().
*/
static sqlite3_index_info *allocateIndexInfo(
- Parse *pParse, /* The parsing context */
+ WhereInfo *pWInfo, /* The WHERE clause */
WhereClause *pWC, /* The WHERE clause being analyzed */
Bitmask mUnusable, /* Ignore terms with these prereqs */
- struct SrcList_item *pSrc, /* The FROM clause term that is the vtab */
- ExprList *pOrderBy, /* The ORDER BY clause */
+ SrcItem *pSrc, /* The FROM clause term that is the vtab */
u16 *pmNoOmit /* Mask of terms not to omit */
){
int i, j;
int nTerm;
+ Parse *pParse = pWInfo->pParse;
struct sqlite3_index_constraint *pIdxCons;
struct sqlite3_index_orderby *pIdxOrderBy;
struct sqlite3_index_constraint_usage *pUsage;
@@ -140107,10 +162049,21 @@ static sqlite3_index_info *allocateIndexInfo(
int nOrderBy;
sqlite3_index_info *pIdxInfo;
u16 mNoOmit = 0;
+ const Table *pTab;
+ int eDistinct = 0;
+ ExprList *pOrderBy = pWInfo->pOrderBy;
+
+ assert( pSrc!=0 );
+ pTab = pSrc->pTab;
+ assert( pTab!=0 );
+ assert( IsVirtual(pTab) );
- /* Count the number of possible WHERE clause constraints referring
- ** to this virtual table */
+ /* Find all WHERE clause constraints referring to this virtual table.
+ ** Mark each term with the TERM_OK flag. Set nTerm to the number of
+ ** terms found.
+ */
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
+ pTerm->wtFlags &= ~TERM_OK;
if( pTerm->leftCursor != pSrc->iCursor ) continue;
if( pTerm->prereqRight & mUnusable ) continue;
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
@@ -140120,11 +162073,20 @@ static sqlite3_index_info *allocateIndexInfo(
testcase( pTerm->eOperator & WO_ALL );
if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue;
if( pTerm->wtFlags & TERM_VNULL ) continue;
- assert( pTerm->u.leftColumn>=(-1) );
+
+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
+ assert( pTerm->u.x.leftColumn>=XN_ROWID );
+ assert( pTerm->u.x.leftColumn<pTab->nCol );
+ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0
+ && !constraintCompatibleWithOuterJoin(pTerm,pSrc)
+ ){
+ continue;
+ }
nTerm++;
+ pTerm->wtFlags |= TERM_OK;
}
- /* If the ORDER BY clause contains only columns in the current
+ /* If the ORDER BY clause contains only columns in the current
** virtual table then allocate space for the aOrderBy part of
** the sqlite3_index_info structure.
*/
@@ -140133,10 +162095,49 @@ static sqlite3_index_info *allocateIndexInfo(
int n = pOrderBy->nExpr;
for(i=0; i<n; i++){
Expr *pExpr = pOrderBy->a[i].pExpr;
- if( pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor ) break;
+ Expr *pE2;
+
+ /* Skip over constant terms in the ORDER BY clause */
+ if( sqlite3ExprIsConstant(pExpr) ){
+ continue;
+ }
+
+ /* Virtual tables are unable to deal with NULLS FIRST */
+ if( pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) break;
+
+ /* First case - a direct column references without a COLLATE operator */
+ if( pExpr->op==TK_COLUMN && pExpr->iTable==pSrc->iCursor ){
+ assert( pExpr->iColumn>=XN_ROWID && pExpr->iColumn<pTab->nCol );
+ continue;
+ }
+
+ /* 2nd case - a column reference with a COLLATE operator. Only match
+ ** of the COLLATE operator matches the collation of the column. */
+ if( pExpr->op==TK_COLLATE
+ && (pE2 = pExpr->pLeft)->op==TK_COLUMN
+ && pE2->iTable==pSrc->iCursor
+ ){
+ const char *zColl; /* The collating sequence name */
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+ assert( pExpr->u.zToken!=0 );
+ assert( pE2->iColumn>=XN_ROWID && pE2->iColumn<pTab->nCol );
+ pExpr->iColumn = pE2->iColumn;
+ if( pE2->iColumn<0 ) continue; /* Collseq does not matter for rowid */
+ zColl = sqlite3ColumnColl(&pTab->aCol[pE2->iColumn]);
+ if( zColl==0 ) zColl = sqlite3StrBINARY;
+ if( sqlite3_stricmp(pExpr->u.zToken, zColl)==0 ) continue;
+ }
+
+ /* No matches cause a break out of the loop */
+ break;
}
- if( i==n){
+ if( i==n ){
nOrderBy = n;
+ if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) ){
+ eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0);
+ }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){
+ eDistinct = 1;
+ }
}
}
@@ -140144,60 +162145,35 @@ static sqlite3_index_info *allocateIndexInfo(
*/
pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
+ (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
- + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) );
+ + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden)
+ + sizeof(sqlite3_value*)*nTerm );
if( pIdxInfo==0 ){
sqlite3ErrorMsg(pParse, "out of memory");
return 0;
}
-
- /* Initialize the structure. The sqlite3_index_info structure contains
- ** many fields that are declared "const" to prevent xBestIndex from
- ** changing them. We have to do some funky casting in order to
- ** initialize those fields.
- */
pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1];
- pIdxCons = (struct sqlite3_index_constraint*)&pHidden[1];
+ pIdxCons = (struct sqlite3_index_constraint*)&pHidden->aRhs[nTerm];
pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm];
pUsage = (struct sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy];
- *(int*)&pIdxInfo->nConstraint = nTerm;
- *(int*)&pIdxInfo->nOrderBy = nOrderBy;
- *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint = pIdxCons;
- *(struct sqlite3_index_orderby**)&pIdxInfo->aOrderBy = pIdxOrderBy;
- *(struct sqlite3_index_constraint_usage**)&pIdxInfo->aConstraintUsage =
- pUsage;
-
+ pIdxInfo->aConstraint = pIdxCons;
+ pIdxInfo->aOrderBy = pIdxOrderBy;
+ pIdxInfo->aConstraintUsage = pUsage;
pHidden->pWC = pWC;
pHidden->pParse = pParse;
+ pHidden->eDistinct = eDistinct;
+ pHidden->mIn = 0;
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
u16 op;
- if( pTerm->leftCursor != pSrc->iCursor ) continue;
- if( pTerm->prereqRight & mUnusable ) continue;
- assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
- testcase( pTerm->eOperator & WO_IN );
- testcase( pTerm->eOperator & WO_IS );
- testcase( pTerm->eOperator & WO_ISNULL );
- testcase( pTerm->eOperator & WO_ALL );
- if( (pTerm->eOperator & ~(WO_EQUIV))==0 ) continue;
- if( pTerm->wtFlags & TERM_VNULL ) continue;
- if( (pSrc->fg.jointype & JT_LEFT)!=0
- && !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
- && (pTerm->eOperator & (WO_IS|WO_ISNULL))
- ){
- /* An "IS" term in the WHERE clause where the virtual table is the rhs
- ** of a LEFT JOIN. Do not pass this term to the virtual table
- ** implementation, as this can lead to incorrect results from SQL such
- ** as:
- **
- ** "LEFT JOIN vtab WHERE vtab.col IS NULL" */
- testcase( pTerm->eOperator & WO_ISNULL );
- testcase( pTerm->eOperator & WO_IS );
- continue;
- }
- assert( pTerm->u.leftColumn>=(-1) );
- pIdxCons[j].iColumn = pTerm->u.leftColumn;
+ if( (pTerm->wtFlags & TERM_OK)==0 ) continue;
+ pIdxCons[j].iColumn = pTerm->u.x.leftColumn;
pIdxCons[j].iTermOffset = i;
op = pTerm->eOperator & WO_ALL;
- if( op==WO_IN ) op = WO_EQ;
+ if( op==WO_IN ){
+ if( (pTerm->wtFlags & TERM_SLICE)==0 ){
+ pHidden->mIn |= SMASKBIT32(j);
+ }
+ op = WO_EQ;
+ }
if( op==WO_AUX ){
pIdxCons[j].op = pTerm->eMatchOp;
}else if( op & (WO_ISNULL|WO_IS) ){
@@ -140219,9 +162195,10 @@ static sqlite3_index_info *allocateIndexInfo(
assert( pTerm->eOperator&(WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_AUX) );
if( op & (WO_LT|WO_LE|WO_GT|WO_GE)
- && sqlite3ExprIsVector(pTerm->pExpr->pRight)
+ && sqlite3ExprIsVector(pTerm->pExpr->pRight)
){
- if( i<16 ) mNoOmit |= (1 << i);
+ testcase( j!=i );
+ if( j<16 ) mNoOmit |= (1 << j);
if( op==WO_LT ) pIdxCons[j].op = WO_LE;
if( op==WO_GT ) pIdxCons[j].op = WO_GE;
}
@@ -140229,17 +162206,43 @@ static sqlite3_index_info *allocateIndexInfo(
j++;
}
- for(i=0; i<nOrderBy; i++){
+ assert( j==nTerm );
+ pIdxInfo->nConstraint = j;
+ for(i=j=0; i<nOrderBy; i++){
Expr *pExpr = pOrderBy->a[i].pExpr;
- pIdxOrderBy[i].iColumn = pExpr->iColumn;
- pIdxOrderBy[i].desc = pOrderBy->a[i].sortOrder;
+ if( sqlite3ExprIsConstant(pExpr) ) continue;
+ assert( pExpr->op==TK_COLUMN
+ || (pExpr->op==TK_COLLATE && pExpr->pLeft->op==TK_COLUMN
+ && pExpr->iColumn==pExpr->pLeft->iColumn) );
+ pIdxOrderBy[j].iColumn = pExpr->iColumn;
+ pIdxOrderBy[j].desc = pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC;
+ j++;
}
+ pIdxInfo->nOrderBy = j;
*pmNoOmit = mNoOmit;
return pIdxInfo;
}
/*
+** Free an sqlite3_index_info structure allocated by allocateIndexInfo()
+** and possibly modified by xBestIndex methods.
+*/
+static void freeIndexInfo(sqlite3 *db, sqlite3_index_info *pIdxInfo){
+ HiddenIndexInfo *pHidden;
+ int i;
+ assert( pIdxInfo!=0 );
+ pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
+ assert( pHidden->pParse!=0 );
+ assert( pHidden->pParse->db==db );
+ for(i=0; i<pIdxInfo->nConstraint; i++){
+ sqlite3ValueFree(pHidden->aRhs[i]); /* IMP: R-14553-25174 */
+ pHidden->aRhs[i] = 0;
+ }
+ sqlite3DbFree(db, pIdxInfo);
+}
+
+/*
** The table object reference passed as the second argument to this function
** must represent a virtual table. This function invokes the xBestIndex()
** method of the virtual table with the sqlite3_index_info object that
@@ -140259,9 +162262,11 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab;
int rc;
- TRACE_IDX_INPUTS(p);
+ whereTraceIndexInfoInputs(p);
+ pParse->db->nSchemaLock++;
rc = pVtab->pModule->xBestIndex(pVtab, p);
- TRACE_IDX_OUTPUTS(p);
+ pParse->db->nSchemaLock--;
+ whereTraceIndexInfoOutputs(p);
if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){
if( rc==SQLITE_NOMEM ){
@@ -140272,13 +162277,16 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg);
}
}
+ if( pTab->u.vtab.p->bAllSchemas ){
+ sqlite3VtabUsesAllSchemas(pParse);
+ }
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = 0;
return rc;
}
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Estimate the location of a particular key among all keys in an
** index. Store the results in aStat as follows:
@@ -140289,8 +162297,8 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
** Return the index of the sample that is the smallest sample that
** is greater than or equal to pRec. Note that this index is not an index
** into the aSample[] array - it is an index into a virtual set of samples
-** based on the contents of aSample[] and the number of fields in record
-** pRec.
+** based on the contents of aSample[] and the number of fields in record
+** pRec.
*/
static int whereKeyStats(
Parse *pParse, /* Database connection */
@@ -140314,7 +162322,8 @@ static int whereKeyStats(
#endif
assert( pRec!=0 );
assert( pIdx->nSample>0 );
- assert( pRec->nField>0 && pRec->nField<=pIdx->nSampleCol );
+ assert( pRec->nField>0 );
+
/* Do a binary search to find the first sample greater than or equal
** to pRec. If pRec contains a single field, the set of samples to search
@@ -140326,41 +162335,46 @@ static int whereKeyStats(
** consider prefixes of those samples. For example, if the set of samples
** in aSample is:
**
- ** aSample[0] = (a, 5)
- ** aSample[1] = (a, 10)
- ** aSample[2] = (b, 5)
- ** aSample[3] = (c, 100)
+ ** aSample[0] = (a, 5)
+ ** aSample[1] = (a, 10)
+ ** aSample[2] = (b, 5)
+ ** aSample[3] = (c, 100)
** aSample[4] = (c, 105)
**
- ** Then the search space should ideally be the samples above and the
- ** unique prefixes [a], [b] and [c]. But since that is hard to organize,
+ ** Then the search space should ideally be the samples above and the
+ ** unique prefixes [a], [b] and [c]. But since that is hard to organize,
** the code actually searches this set:
**
- ** 0: (a)
- ** 1: (a, 5)
- ** 2: (a, 10)
- ** 3: (a, 10)
- ** 4: (b)
- ** 5: (b, 5)
- ** 6: (c)
- ** 7: (c, 100)
+ ** 0: (a)
+ ** 1: (a, 5)
+ ** 2: (a, 10)
+ ** 3: (a, 10)
+ ** 4: (b)
+ ** 5: (b, 5)
+ ** 6: (c)
+ ** 7: (c, 100)
** 8: (c, 105)
** 9: (c, 105)
**
** For each sample in the aSample[] array, N samples are present in the
- ** effective sample array. In the above, samples 0 and 1 are based on
+ ** effective sample array. In the above, samples 0 and 1 are based on
** sample aSample[0]. Samples 2 and 3 on aSample[1] etc.
**
** Often, sample i of each block of N effective samples has (i+1) fields.
** Except, each sample may be extended to ensure that it is greater than or
- ** equal to the previous sample in the array. For example, in the above,
- ** sample 2 is the first sample of a block of N samples, so at first it
- ** appears that it should be 1 field in size. However, that would make it
- ** smaller than sample 1, so the binary search would not work. As a result,
- ** it is extended to two fields. The duplicates that this creates do not
+ ** equal to the previous sample in the array. For example, in the above,
+ ** sample 2 is the first sample of a block of N samples, so at first it
+ ** appears that it should be 1 field in size. However, that would make it
+ ** smaller than sample 1, so the binary search would not work. As a result,
+ ** it is extended to two fields. The duplicates that this creates do not
** cause any problems.
*/
- nField = pRec->nField;
+ if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
+ nField = pIdx->nKeyCol;
+ }else{
+ nField = pIdx->nColumn;
+ }
+ nField = MIN(pRec->nField, nField);
iCol = 0;
iSample = pIdx->nSample * nField;
do{
@@ -140371,7 +162385,7 @@ static int whereKeyStats(
iSamp = iTest / nField;
if( iSamp>0 ){
/* The proposed effective sample is a prefix of sample aSample[iSamp].
- ** Specifically, the shortest prefix of at least (1 + iTest%nField)
+ ** Specifically, the shortest prefix of at least (1 + iTest%nField)
** fields that is greater than the previous effective sample. */
for(n=(iTest % nField) + 1; n<nField; n++){
if( aSample[iSamp-1].anLt[n-1]!=aSample[iSamp].anLt[n-1] ) break;
@@ -140406,8 +162420,8 @@ static int whereKeyStats(
assert( i<pIdx->nSample );
assert( iCol==nField-1 );
pRec->nField = nField;
- assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)
- || pParse->db->mallocFailed
+ assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)
+ || pParse->db->mallocFailed
);
}else{
/* Unless i==pIdx->nSample, indicating that pRec is larger than
@@ -140415,7 +162429,7 @@ static int whereKeyStats(
** (iCol+1) field prefix of sample i. */
assert( i<=pIdx->nSample && i>=0 );
pRec->nField = iCol+1;
- assert( i==pIdx->nSample
+ assert( i==pIdx->nSample
|| sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0
|| pParse->db->mallocFailed );
@@ -140426,12 +162440,12 @@ static int whereKeyStats(
if( iCol>0 ){
pRec->nField = iCol;
assert( sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0
- || pParse->db->mallocFailed );
+ || pParse->db->mallocFailed || CORRUPT_DB );
}
if( i>0 ){
pRec->nField = nField;
assert( sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0
- || pParse->db->mallocFailed );
+ || pParse->db->mallocFailed || CORRUPT_DB );
}
}
}
@@ -140443,12 +162457,12 @@ static int whereKeyStats(
aStat[0] = aSample[i].anLt[iCol];
aStat[1] = aSample[i].anEq[iCol];
}else{
- /* At this point, the (iCol+1) field prefix of aSample[i] is the first
+ /* At this point, the (iCol+1) field prefix of aSample[i] is the first
** sample that is greater than pRec. Or, if i==pIdx->nSample then pRec
** is larger than all samples in the array. */
tRowcnt iUpper, iGap;
if( i>=pIdx->nSample ){
- iUpper = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]);
+ iUpper = pIdx->nRowEst0;
}else{
iUpper = aSample[i].anLt[iCol];
}
@@ -140471,11 +162485,11 @@ static int whereKeyStats(
pRec->nField = nField;
return i;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/*
** If it is not NULL, pTerm is a term that provides an upper or lower
-** bound on a range scan. Without considering pTerm, it is estimated
+** bound on a range scan. Without considering pTerm, it is estimated
** that the scan will visit nNew rows. This function returns the number
** estimated to be visited after taking pTerm into account.
**
@@ -140497,7 +162511,7 @@ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Return the affinity for a single column of an index.
*/
@@ -140506,24 +162520,25 @@ SQLITE_PRIVATE char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCo
if( !pIdx->zColAff ){
if( sqlite3IndexAffinityStr(db, pIdx)==0 ) return SQLITE_AFF_BLOB;
}
+ assert( pIdx->zColAff[iCol]!=0 );
return pIdx->zColAff[iCol];
}
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-/*
+#ifdef SQLITE_ENABLE_STAT4
+/*
** This function is called to estimate the number of rows visited by a
** range-scan on a skip-scan index. For example:
**
** CREATE INDEX i1 ON t1(a, b, c);
** SELECT * FROM t1 WHERE a=? AND c BETWEEN ? AND ?;
**
-** Value pLoop->nOut is currently set to the estimated number of rows
-** visited for scanning (a=? AND b=?). This function reduces that estimate
+** Value pLoop->nOut is currently set to the estimated number of rows
+** visited for scanning (a=? AND b=?). This function reduces that estimate
** by some factor to account for the (c BETWEEN ? AND ?) expression based
-** on the stat4 data for the index. this scan will be peformed multiple
-** times (once for each (a,b) combination that matches a=?) is dealt with
+** on the stat4 data for the index. this scan will be performed multiple
+** times (once for each (a,b) combination that matches a=?) is dealt with
** by the caller.
**
** It does this by scanning through all stat4 samples, comparing values
@@ -140544,7 +162559,7 @@ SQLITE_PRIVATE char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCo
** estimate of the number of rows delivered remains unchanged), *pbDone
** is left as is.
**
-** If an error occurs, an SQLite error code is returned. Otherwise,
+** If an error occurs, an SQLite error code is returned. Otherwise,
** SQLITE_OK.
*/
static int whereRangeSkipScanEst(
@@ -140562,7 +162577,7 @@ static int whereRangeSkipScanEst(
int rc = SQLITE_OK;
u8 aff = sqlite3IndexColumnAffinity(db, p, nEq);
CollSeq *pColl;
-
+
sqlite3_value *p1 = 0; /* Value extracted from pLower */
sqlite3_value *p2 = 0; /* Value extracted from pUpper */
sqlite3_value *pVal = 0; /* Value extracted from record */
@@ -140594,7 +162609,7 @@ static int whereRangeSkipScanEst(
nDiff = (nUpper - nLower);
if( nDiff<=0 ) nDiff = 1;
- /* If there is both an upper and lower bound specified, and the
+ /* If there is both an upper and lower bound specified, and the
** comparisons indicate that they are close together, use the fallback
** method (assume that the scan visits 1/64 of the rows) for estimating
** the number of rows visited. Otherwise, estimate the number of rows
@@ -140603,7 +162618,7 @@ static int whereRangeSkipScanEst(
int nAdjust = (sqlite3LogEst(p->nSample) - sqlite3LogEst(nDiff));
pLoop->nOut -= nAdjust;
*pbDone = 1;
- WHERETRACE(0x10, ("range skip-scan regions: %u..%u adjust=%d est=%d\n",
+ WHERETRACE(0x20, ("range skip-scan regions: %u..%u adjust=%d est=%d\n",
nLower, nUpper, nAdjust*-1, pLoop->nOut));
}
@@ -140617,7 +162632,7 @@ static int whereRangeSkipScanEst(
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/*
** This function is used to estimate the number of rows that will be visited
@@ -140641,7 +162656,7 @@ static int whereRangeSkipScanEst(
**
** ... FROM t1 WHERE a = ? AND b > ? AND b < ? ...
**
-** then nEq is set to 1 (as the range restricted column, b, is the second
+** then nEq is set to 1 (as the range restricted column, b, is the second
** left-most column of the index). Or, if the query is:
**
** ... FROM t1 WHERE a > ? AND a < ? ...
@@ -140649,13 +162664,13 @@ static int whereRangeSkipScanEst(
** then nEq is set to 0.
**
** When this function is called, *pnOut is set to the sqlite3LogEst() of the
-** number of rows that the index scan is expected to visit without
-** considering the range constraints. If nEq is 0, then *pnOut is the number of
+** number of rows that the index scan is expected to visit without
+** considering the range constraints. If nEq is 0, then *pnOut is the number of
** rows in the index. Assuming no error occurs, *pnOut is adjusted (reduced)
** to account for the range constraints pLower and pUpper.
-**
+**
** In the absence of sqlite_stat4 ANALYZE data, or if such data cannot be
-** used, a single range inequality reduces the search space by a factor of 4.
+** used, a single range inequality reduces the search space by a factor of 4.
** and a pair of constraints (x>? AND x<?) reduces the expected number of
** rows visited by a factor of 64.
*/
@@ -140670,12 +162685,12 @@ static int whereRangeScanEst(
int nOut = pLoop->nOut;
LogEst nNew;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
Index *p = pLoop->u.btree.pIndex;
int nEq = pLoop->u.btree.nEq;
- if( p->nSample>0 && nEq<p->nSampleCol
- && OptimizationEnabled(pParse->db, SQLITE_Stat34)
+ if( p->nSample>0 && ALWAYS(nEq<p->nSampleCol)
+ && OptimizationEnabled(pParse->db, SQLITE_Stat4)
){
if( nEq==pBuilder->nRecValid ){
UnpackedRecord *pRec = pBuilder->pRec;
@@ -140683,7 +162698,7 @@ static int whereRangeScanEst(
int nBtm = pLoop->u.btree.nBtm;
int nTop = pLoop->u.btree.nTop;
- /* Variable iLower will be set to the estimate of the number of rows in
+ /* Variable iLower will be set to the estimate of the number of rows in
** the index that are less than the lower bound of the range query. The
** lower bound being the concatenation of $P and $L, where $P is the
** key-prefix formed by the nEq values matched against the nEq left-most
@@ -140692,7 +162707,7 @@ static int whereRangeScanEst(
** Or, if pLower is NULL or $L cannot be extracted from it (because it
** is not a simple variable or literal value), the lower bound of the
** range is $P. Due to a quirk in the way whereKeyStats() works, even
- ** if $L is available, whereKeyStats() is called for both ($P) and
+ ** if $L is available, whereKeyStats() is called for both ($P) and
** ($P:$L) and the larger of the two returned values is used.
**
** Similarly, iUpper is to be set to the estimate of the number of rows
@@ -140716,7 +162731,7 @@ static int whereRangeScanEst(
iLower = 0;
iUpper = p->nRowEst0;
}else{
- /* Note: this call could be optimized away - since the same values must
+ /* Note: this call could be optimized away - since the same values must
** have been requested when testing key $P in whereEqualScanEst(). */
whereKeyStats(pParse, p, pRec, 0, a);
iLower = a[0];
@@ -140773,15 +162788,16 @@ static int whereRangeScanEst(
/* TUNING: If both iUpper and iLower are derived from the same
** sample, then assume they are 4x more selective. This brings
** the estimated selectivity more in line with what it would be
- ** if estimated without the use of STAT3/4 tables. */
- if( iLwrIdx==iUprIdx ) nNew -= 20; assert( 20==sqlite3LogEst(4) );
+ ** if estimated without the use of STAT4 tables. */
+ if( iLwrIdx==iUprIdx ){ nNew -= 20; }
+ assert( 20==sqlite3LogEst(4) );
}else{
nNew = 10; assert( 10==sqlite3LogEst(2) );
}
if( nNew<nOut ){
nOut = nNew;
}
- WHERETRACE(0x10, ("STAT4 range scan: %u..%u est=%d\n",
+ WHERETRACE(0x20, ("STAT4 range scan: %u..%u est=%d\n",
(u32)iLower, (u32)iUpper, nOut));
}
}else{
@@ -140795,7 +162811,7 @@ static int whereRangeScanEst(
UNUSED_PARAMETER(pBuilder);
assert( pLower || pUpper );
#endif
- assert( pUpper==0 || (pUpper->wtFlags & TERM_VNULL)==0 );
+ assert( pUpper==0 || (pUpper->wtFlags & TERM_VNULL)==0 || pParse->nErr>0 );
nNew = whereRangeAdjust(pLower, nOut);
nNew = whereRangeAdjust(pUpper, nNew);
@@ -140804,7 +162820,7 @@ static int whereRangeScanEst(
** reduced by an additional 75%. This means that, by default, an open-ended
** range query (e.g. col > ?) is assumed to match 1/4 of the rows in the
** index. While a closed range (e.g. col BETWEEN ? AND ?) is estimated to
- ** match 1/64 of the index. */
+ ** match 1/64 of the index. */
if( pLower && pLower->truthProb>0 && pUpper && pUpper->truthProb>0 ){
nNew -= 20;
}
@@ -140814,7 +162830,7 @@ static int whereRangeScanEst(
if( nNew<nOut ) nOut = nNew;
#if defined(WHERETRACE_ENABLED)
if( pLoop->nOut>nOut ){
- WHERETRACE(0x10,("Range scan lowers nOut from %d to %d\n",
+ WHERETRACE(0x20,("Range scan lowers nOut from %d to %d\n",
pLoop->nOut, nOut));
}
#endif
@@ -140822,16 +162838,16 @@ static int whereRangeScanEst(
return rc;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Estimate the number of rows that will be returned based on
** an equality constraint x=VALUE and where that VALUE occurs in
** the histogram data. This only works when x is the left-most
-** column of an index and sqlite_stat3 histogram data is available
+** column of an index and sqlite_stat4 histogram data is available
** for that index. When pExpr==NULL that means the constraint is
** "x IS NULL" instead of "x=VALUE".
**
-** Write the estimated row count into *pnRow and return SQLITE_OK.
+** Write the estimated row count into *pnRow and return SQLITE_OK.
** If unable to make an estimate, leave *pnRow unchanged and return
** non-zero.
**
@@ -140879,15 +162895,15 @@ static int whereEqualScanEst(
pBuilder->nRecValid = nEq;
whereKeyStats(pParse, p, pRec, 0, a);
- WHERETRACE(0x10,("equality scan regions %s(%d): %d\n",
+ WHERETRACE(0x20,("equality scan regions %s(%d): %d\n",
p->zName, nEq-1, (int)a[1]));
*pnRow = a[1];
-
+
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Estimate the number of rows that will be returned based on
** an IN constraint where the right-hand side of the IN operator
@@ -140895,7 +162911,7 @@ static int whereEqualScanEst(
**
** WHERE x IN (1,2,3,4)
**
-** Write the estimated row count into *pnRow and return SQLITE_OK.
+** Write the estimated row count into *pnRow and return SQLITE_OK.
** If unable to make an estimate, leave *pnRow unchanged and return
** non-zero.
**
@@ -140927,48 +162943,57 @@ static int whereInScanEst(
}
if( rc==SQLITE_OK ){
- if( nRowEst > nRow0 ) nRowEst = nRow0;
+ if( nRowEst > (tRowcnt)nRow0 ) nRowEst = nRow0;
*pnRow = nRowEst;
- WHERETRACE(0x10,("IN row estimate: est=%d\n", nRowEst));
+ WHERETRACE(0x20,("IN row estimate: est=%d\n", nRowEst));
}
assert( pBuilder->nRecValid==nRecValid );
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
#ifdef WHERETRACE_ENABLED
/*
** Print the content of a WhereTerm object
*/
-static void whereTermPrint(WhereTerm *pTerm, int iTerm){
+SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
if( pTerm==0 ){
sqlite3DebugPrintf("TERM-%-3d NULL\n", iTerm);
}else{
- char zType[4];
+ char zType[8];
char zLeft[50];
- memcpy(zType, "...", 4);
+ memcpy(zType, "....", 5);
if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V';
if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E';
- if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L';
+ if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) zType[2] = 'L';
+ if( pTerm->wtFlags & TERM_CODED ) zType[3] = 'C';
if( pTerm->eOperator & WO_SINGLE ){
+ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
sqlite3_snprintf(sizeof(zLeft),zLeft,"left={%d:%d}",
- pTerm->leftCursor, pTerm->u.leftColumn);
+ pTerm->leftCursor, pTerm->u.x.leftColumn);
}else if( (pTerm->eOperator & WO_OR)!=0 && pTerm->u.pOrInfo!=0 ){
- sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%lld",
+ sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%llx",
pTerm->u.pOrInfo->indexable);
}else{
sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor);
}
sqlite3DebugPrintf(
- "TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x",
- iTerm, pTerm, zType, zLeft, pTerm->truthProb,
- pTerm->eOperator, pTerm->wtFlags);
- if( pTerm->iField ){
- sqlite3DebugPrintf(" iField=%d\n", pTerm->iField);
- }else{
- sqlite3DebugPrintf("\n");
+ "TERM-%-3d %p %s %-12s op=%03x wtFlags=%04x",
+ iTerm, pTerm, zType, zLeft, pTerm->eOperator, pTerm->wtFlags);
+ /* The 0x10000 .wheretrace flag causes extra information to be
+ ** shown about each Term */
+ if( sqlite3WhereTrace & 0x10000 ){
+ sqlite3DebugPrintf(" prob=%-3d prereq=%llx,%llx",
+ pTerm->truthProb, (u64)pTerm->prereqAll, (u64)pTerm->prereqRight);
}
+ if( (pTerm->eOperator & (WO_OR|WO_AND))==0 && pTerm->u.x.iField ){
+ sqlite3DebugPrintf(" iField=%d", pTerm->u.x.iField);
+ }
+ if( pTerm->iParent>=0 ){
+ sqlite3DebugPrintf(" iParent=%d", pTerm->iParent);
+ }
+ sqlite3DebugPrintf("\n");
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
}
}
@@ -140981,7 +163006,7 @@ static void whereTermPrint(WhereTerm *pTerm, int iTerm){
SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){
int i;
for(i=0; i<pWC->nTerm; i++){
- whereTermPrint(&pWC->a[i], i);
+ sqlite3WhereTermPrint(&pWC->a[i], i);
}
}
#endif
@@ -140989,17 +163014,34 @@ SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){
#ifdef WHERETRACE_ENABLED
/*
** Print a WhereLoop object for debugging purposes
-*/
-static void whereLoopPrint(WhereLoop *p, WhereClause *pWC){
- WhereInfo *pWInfo = pWC->pWInfo;
- int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
- struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab;
- Table *pTab = pItem->pTab;
- Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
- sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
- p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
- sqlite3DebugPrintf(" %12s",
- pItem->zAlias ? pItem->zAlias : pTab->zName);
+**
+** Format example:
+**
+** .--- Position in WHERE clause rSetup, rRun, nOut ---.
+** | |
+** | .--- selfMask nTerm ------. |
+** | | | |
+** | | .-- prereq Idx wsFlags----. | |
+** | | | Name | | |
+** | | | __|__ nEq ---. ___|__ | __|__
+** | / \ / \ / \ | / \ / \ / \
+** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31
+*/
+SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){
+ if( pWC ){
+ WhereInfo *pWInfo = pWC->pWInfo;
+ int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
+ SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
+ Table *pTab = pItem->pTab;
+ Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
+ sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
+ p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
+ sqlite3DebugPrintf(" %12s",
+ pItem->zAlias ? pItem->zAlias : pTab->zName);
+ }else{
+ sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d",
+ p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab);
+ }
if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
const char *zName;
if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
@@ -141015,7 +163057,7 @@ static void whereLoopPrint(WhereLoop *p, WhereClause *pWC){
}else{
char *z;
if( p->u.vtab.idxStr ){
- z = sqlite3_mprintf("(%d,\"%s\",%x)",
+ z = sqlite3_mprintf("(%d,\"%s\",%#x)",
p->u.vtab.idxNum, p->u.vtab.idxStr, p->u.vtab.omitMask);
}else{
z = sqlite3_mprintf("(%d,%x)", p->u.vtab.idxNum, p->u.vtab.omitMask);
@@ -141024,18 +163066,27 @@ static void whereLoopPrint(WhereLoop *p, WhereClause *pWC){
sqlite3_free(z);
}
if( p->wsFlags & WHERE_SKIPSCAN ){
- sqlite3DebugPrintf(" f %05x %d-%d", p->wsFlags, p->nLTerm,p->nSkip);
+ sqlite3DebugPrintf(" f %06x %d-%d", p->wsFlags, p->nLTerm,p->nSkip);
}else{
- sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm);
+ sqlite3DebugPrintf(" f %06x N %d", p->wsFlags, p->nLTerm);
}
sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
- if( p->nLTerm && (sqlite3WhereTrace & 0x100)!=0 ){
+ if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){
int i;
for(i=0; i<p->nLTerm; i++){
- whereTermPrint(p->aLTerm[i], i);
+ sqlite3WhereTermPrint(p->aLTerm[i], i);
}
}
}
+SQLITE_PRIVATE void sqlite3ShowWhereLoop(const WhereLoop *p){
+ if( p ) sqlite3WhereLoopPrint(p, 0);
+}
+SQLITE_PRIVATE void sqlite3ShowWhereLoopList(const WhereLoop *p){
+ while( p ){
+ sqlite3ShowWhereLoop(p);
+ p = p->pNextLoop;
+ }
+}
#endif
/*
@@ -141067,12 +163118,18 @@ static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){
}
/*
-** Deallocate internal memory used by a WhereLoop object
+** Deallocate internal memory used by a WhereLoop object. Leave the
+** object in an initialized state, as if it had been newly allocated.
*/
static void whereLoopClear(sqlite3 *db, WhereLoop *p){
- if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFreeNN(db, p->aLTerm);
+ if( p->aLTerm!=p->aLTermSpace ){
+ sqlite3DbFreeNN(db, p->aLTerm);
+ p->aLTerm = p->aLTermSpace;
+ p->nLSlot = ArraySize(p->aLTermSpace);
+ }
whereLoopClearUnion(db, p);
- whereLoopInit(p);
+ p->nLTerm = 0;
+ p->wsFlags = 0;
}
/*
@@ -141096,8 +163153,10 @@ static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){
*/
static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){
whereLoopClearUnion(db, pTo);
- if( whereLoopResize(db, pTo, pFrom->nLTerm) ){
- memset(&pTo->u, 0, sizeof(pTo->u));
+ if( pFrom->nLTerm > pTo->nLSlot
+ && whereLoopResize(db, pTo, pFrom->nLTerm)
+ ){
+ memset(pTo, 0, WHERE_LOOP_XFER_SZ);
return SQLITE_NOMEM_BKPT;
}
memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ);
@@ -141114,79 +163173,91 @@ static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){
** Delete a WhereLoop object
*/
static void whereLoopDelete(sqlite3 *db, WhereLoop *p){
+ assert( db!=0 );
whereLoopClear(db, p);
- sqlite3DbFreeNN(db, p);
+ sqlite3DbNNFreeNN(db, p);
}
/*
** Free a WhereInfo structure
*/
static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
- int i;
assert( pWInfo!=0 );
- for(i=0; i<pWInfo->nLevel; i++){
- WhereLevel *pLevel = &pWInfo->a[i];
- if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){
- sqlite3DbFree(db, pLevel->u.in.aInLoop);
- }
- }
+ assert( db!=0 );
sqlite3WhereClauseClear(&pWInfo->sWC);
while( pWInfo->pLoops ){
WhereLoop *p = pWInfo->pLoops;
pWInfo->pLoops = p->pNextLoop;
whereLoopDelete(db, p);
}
- sqlite3DbFreeNN(db, pWInfo);
+ while( pWInfo->pMemToFree ){
+ WhereMemBlock *pNext = pWInfo->pMemToFree->pNext;
+ sqlite3DbNNFreeNN(db, pWInfo->pMemToFree);
+ pWInfo->pMemToFree = pNext;
+ }
+ sqlite3DbNNFreeNN(db, pWInfo);
}
/*
-** Return TRUE if all of the following are true:
+** Return TRUE if X is a proper subset of Y but is of equal or less cost.
+** In other words, return true if all constraints of X are also part of Y
+** and Y has additional constraints that might speed the search that X lacks
+** but the cost of running X is not more than the cost of running Y.
**
-** (1) X has the same or lower cost that Y
-** (2) X uses fewer WHERE clause terms than Y
-** (3) Every WHERE clause term used by X is also used by Y
-** (4) X skips at least as many columns as Y
-** (5) If X is a covering index, than Y is too
+** In other words, return true if the cost relationwship between X and Y
+** is inverted and needs to be adjusted.
**
-** Conditions (2) and (3) mean that X is a "proper subset" of Y.
-** If X is a proper subset of Y then Y is a better choice and ought
-** to have a lower cost. This routine returns TRUE when that cost
-** relationship is inverted and needs to be adjusted. Constraint (4)
-** was added because if X uses skip-scan less than Y it still might
-** deserve a lower cost even if it is a proper subset of Y. Constraint (5)
-** was added because a covering index probably deserves to have a lower cost
-** than a non-covering index even if it is a proper subset.
+** Case 1:
+**
+** (1a) X and Y use the same index.
+** (1b) X has fewer == terms than Y
+** (1c) Neither X nor Y use skip-scan
+** (1d) X does not have a a greater cost than Y
+**
+** Case 2:
+**
+** (2a) X has the same or lower cost, or returns the same or fewer rows,
+** than Y.
+** (2b) X uses fewer WHERE clause terms than Y
+** (2c) Every WHERE clause term used by X is also used by Y
+** (2d) X skips at least as many columns as Y
+** (2e) If X is a covering index, than Y is too
*/
static int whereLoopCheaperProperSubset(
const WhereLoop *pX, /* First WhereLoop to compare */
const WhereLoop *pY /* Compare against this WhereLoop */
){
int i, j;
- if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
- return 0; /* X is not a subset of Y */
+ if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; /* (1d) and (2a) */
+ assert( (pX->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ assert( (pY->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ if( pX->u.btree.nEq < pY->u.btree.nEq /* (1b) */
+ && pX->u.btree.pIndex==pY->u.btree.pIndex /* (1a) */
+ && pX->nSkip==0 && pY->nSkip==0 /* (1c) */
+ ){
+ return 1; /* Case 1 is true */
}
- if( pY->nSkip > pX->nSkip ) return 0;
- if( pX->rRun >= pY->rRun ){
- if( pX->rRun > pY->rRun ) return 0; /* X costs more than Y */
- if( pX->nOut > pY->nOut ) return 0; /* X costs more than Y */
+ if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
+ return 0; /* (2b) */
}
+ if( pY->nSkip > pX->nSkip ) return 0; /* (2d) */
for(i=pX->nLTerm-1; i>=0; i--){
if( pX->aLTerm[i]==0 ) continue;
for(j=pY->nLTerm-1; j>=0; j--){
if( pY->aLTerm[j]==pX->aLTerm[i] ) break;
}
- if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */
+ if( j<0 ) return 0; /* (2c) */
}
- if( (pX->wsFlags&WHERE_IDX_ONLY)!=0
+ if( (pX->wsFlags&WHERE_IDX_ONLY)!=0
&& (pY->wsFlags&WHERE_IDX_ONLY)==0 ){
- return 0; /* Constraint (5) */
+ return 0; /* (2e) */
}
- return 1; /* All conditions meet */
+ return 1; /* Case 2 is true */
}
/*
-** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so
-** that:
+** Try to adjust the cost and number of output rows of WhereLoop pTemplate
+** upwards or downwards so that:
**
** (1) pTemplate costs less than any other WhereLoops that are a proper
** subset of pTemplate
@@ -141204,19 +163275,23 @@ static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){
if( p->iTab!=pTemplate->iTab ) continue;
if( (p->wsFlags & WHERE_INDEXED)==0 ) continue;
if( whereLoopCheaperProperSubset(p, pTemplate) ){
- /* Adjust pTemplate cost downward so that it is cheaper than its
+ /* Adjust pTemplate cost downward so that it is cheaper than its
** subset p. */
WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n",
- pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut-1));
- pTemplate->rRun = p->rRun;
- pTemplate->nOut = p->nOut - 1;
+ pTemplate->rRun, pTemplate->nOut,
+ MIN(p->rRun, pTemplate->rRun),
+ MIN(p->nOut - 1, pTemplate->nOut)));
+ pTemplate->rRun = MIN(p->rRun, pTemplate->rRun);
+ pTemplate->nOut = MIN(p->nOut - 1, pTemplate->nOut);
}else if( whereLoopCheaperProperSubset(pTemplate, p) ){
/* Adjust pTemplate cost upward so that it is costlier than p since
** pTemplate is a proper subset of p */
WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n",
- pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut+1));
- pTemplate->rRun = p->rRun;
- pTemplate->nOut = p->nOut + 1;
+ pTemplate->rRun, pTemplate->nOut,
+ MAX(p->rRun, pTemplate->rRun),
+ MAX(p->nOut + 1, pTemplate->nOut)));
+ pTemplate->rRun = MAX(p->rRun, pTemplate->rRun);
+ pTemplate->nOut = MAX(p->nOut + 1, pTemplate->nOut);
}
}
}
@@ -141250,7 +163325,7 @@ static WhereLoop **whereLoopFindLesser(
/* In the current implementation, the rSetup value is either zero
** or the cost of building an automatic index (NlogN) and the NlogN
** is the same for compatible WhereLoops. */
- assert( p->rSetup==0 || pTemplate->rSetup==0
+ assert( p->rSetup==0 || pTemplate->rSetup==0
|| p->rSetup==pTemplate->rSetup );
/* whereLoopAddBtree() always generates and inserts the automatic index
@@ -141258,7 +163333,7 @@ static WhereLoop **whereLoopFindLesser(
** rSetup. Call this SETUP-INVARIANT */
assert( p->rSetup>=pTemplate->rSetup );
- /* Any loop using an appliation-defined index (or PRIMARY KEY or
+ /* Any loop using an application-defined index (or PRIMARY KEY or
** UNIQUE constraint) with one or more == constraints is better
** than an automatic index. Unless it is a skip-scan. */
if( (p->wsFlags & WHERE_AUTO_INDEX)!=0
@@ -141285,7 +163360,7 @@ static WhereLoop **whereLoopFindLesser(
/* If pTemplate is always better than p, then cause p to be overwritten
** with pTemplate. pTemplate is better than p if:
- ** (1) pTemplate has no more dependences than p, and
+ ** (1) pTemplate has no more dependencies than p, and
** (2) pTemplate has an equal or lower cost than p.
*/
if( (p->prereq & pTemplate->prereq)==pTemplate->prereq /* (1) */
@@ -141315,7 +163390,7 @@ static WhereLoop **whereLoopFindLesser(
**
** When accumulating multiple loops (when pBuilder->pOrSet is NULL) we
** still might overwrite similar loops with the new template if the
-** new template is better. Loops may be overwritten if the following
+** new template is better. Loops may be overwritten if the following
** conditions are met:
**
** (1) They have the same iTab.
@@ -141337,6 +163412,8 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
}
pBuilder->iPlanLimit--;
+ whereLoopAdjustCost(pWInfo->pLoops, pTemplate);
+
/* If pBuilder->pOrSet is defined, then only keep track of the costs
** and prereqs.
*/
@@ -141351,7 +163428,7 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
#if WHERETRACE_ENABLED /* 0x8 */
if( sqlite3WhereTrace & 0x8 ){
sqlite3DebugPrintf(x?" or-%d: ":" or-X: ", n);
- whereLoopPrint(pTemplate, pBuilder->pWC);
+ sqlite3WhereLoopPrint(pTemplate, pBuilder->pWC);
}
#endif
}
@@ -141360,7 +163437,6 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
/* Look for an existing WhereLoop to replace with pTemplate
*/
- whereLoopAdjustCost(pWInfo->pLoops, pTemplate);
ppPrev = whereLoopFindLesser(&pWInfo->pLoops, pTemplate);
if( ppPrev==0 ){
@@ -141369,10 +163445,10 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
#if WHERETRACE_ENABLED /* 0x8 */
if( sqlite3WhereTrace & 0x8 ){
sqlite3DebugPrintf(" skip: ");
- whereLoopPrint(pTemplate, pBuilder->pWC);
+ sqlite3WhereLoopPrint(pTemplate, pBuilder->pWC);
}
#endif
- return SQLITE_OK;
+ return SQLITE_OK;
}else{
p = *ppPrev;
}
@@ -141385,12 +163461,12 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
if( sqlite3WhereTrace & 0x8 ){
if( p!=0 ){
sqlite3DebugPrintf("replace: ");
- whereLoopPrint(p, pBuilder->pWC);
+ sqlite3WhereLoopPrint(p, pBuilder->pWC);
sqlite3DebugPrintf(" with: ");
}else{
sqlite3DebugPrintf(" add: ");
}
- whereLoopPrint(pTemplate, pBuilder->pWC);
+ sqlite3WhereLoopPrint(pTemplate, pBuilder->pWC);
}
#endif
if( p==0 ){
@@ -141402,7 +163478,7 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
}else{
/* We will be overwriting WhereLoop p[]. But before we do, first
** go through the rest of the list and delete any other entries besides
- ** p[] that are also supplated by pTemplate */
+ ** p[] that are also supplanted by pTemplate */
WhereLoop **ppTail = &p->pNextLoop;
WhereLoop *pToDel;
while( *ppTail ){
@@ -141414,7 +163490,7 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
#if WHERETRACE_ENABLED /* 0x8 */
if( sqlite3WhereTrace & 0x8 ){
sqlite3DebugPrintf(" delete: ");
- whereLoopPrint(pToDel, pBuilder->pWC);
+ sqlite3WhereLoopPrint(pToDel, pBuilder->pWC);
}
#endif
whereLoopDelete(db, pToDel);
@@ -141423,7 +163499,7 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
rc = whereLoopXfer(db, p, pTemplate);
if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
Index *pIndex = p->u.btree.pIndex;
- if( pIndex && pIndex->tnum==0 ){
+ if( pIndex && pIndex->idxType==SQLITE_IDXTYPE_IPK ){
p->u.btree.pIndex = 0;
}
}
@@ -141466,14 +163542,15 @@ static void whereLoopOutputAdjust(
){
WhereTerm *pTerm, *pX;
Bitmask notAllowed = ~(pLoop->prereq|pLoop->maskSelf);
- int i, j, k;
+ int i, j;
LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */
assert( (pLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
- for(i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++){
- if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) break;
- if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue;
+ for(i=pWC->nBase, pTerm=pWC->a; i>0; i--, pTerm++){
+ assert( pTerm!=0 );
if( (pTerm->prereqAll & notAllowed)!=0 ) continue;
+ if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue;
+ if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) continue;
for(j=pLoop->nLTerm-1; j>=0; j--){
pX = pLoop->aLTerm[j];
if( pX==0 ) continue;
@@ -141481,6 +163558,24 @@ static void whereLoopOutputAdjust(
if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break;
}
if( j<0 ){
+ sqlite3ProgressCheck(pWC->pWInfo->pParse);
+ if( pLoop->maskSelf==pTerm->prereqAll ){
+ /* If there are extra terms in the WHERE clause not used by an index
+ ** that depend only on the table being scanned, and that will tend to
+ ** cause many rows to be omitted, then mark that table as
+ ** "self-culling".
+ **
+ ** 2022-03-24: Self-culling only applies if either the extra terms
+ ** are straight comparison operators that are non-true with NULL
+ ** operand, or if the loop is not an OUTER JOIN.
+ */
+ if( (pTerm->eOperator & 0x3f)!=0
+ || (pWC->pWInfo->pTabList->a[pLoop->iTab].fg.jointype
+ & (JT_LEFT|JT_LTORJ))==0
+ ){
+ pLoop->wsFlags |= WHERE_SELFCULL;
+ }
+ }
if( pTerm->truthProb<=0 ){
/* If a truth probability is specified using the likelihood() hints,
** then use the probability provided by the application. */
@@ -141489,23 +163584,31 @@ static void whereLoopOutputAdjust(
/* In the absence of explicit truth probabilities, use heuristics to
** guess a reasonable truth probability. */
pLoop->nOut--;
- if( pTerm->eOperator&(WO_EQ|WO_IS) ){
+ if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0
+ && (pTerm->wtFlags & TERM_HIGHTRUTH)==0 /* tag-20200224-1 */
+ ){
Expr *pRight = pTerm->pExpr->pRight;
+ int k = 0;
testcase( pTerm->pExpr->op==TK_IS );
if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){
k = 10;
}else{
k = 20;
}
- if( iReduce<k ) iReduce = k;
+ if( iReduce<k ){
+ pTerm->wtFlags |= TERM_HEURTRUTH;
+ iReduce = k;
+ }
}
}
}
}
- if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce;
+ if( pLoop->nOut > nRow-iReduce ){
+ pLoop->nOut = nRow - iReduce;
+ }
}
-/*
+/*
** Term pTerm is a vector range comparison operation. The first comparison
** in the vector can be optimized using column nEq of the index. This
** function returns the total number of vector elements that can be used
@@ -141534,14 +163637,17 @@ static int whereRangeVectorLen(
nCmp = MIN(nCmp, (pIdx->nColumn - nEq));
for(i=1; i<nCmp; i++){
- /* Test if comparison i of pTerm is compatible with column (i+nEq)
+ /* Test if comparison i of pTerm is compatible with column (i+nEq)
** of the index. If not, exit the loop. */
char aff; /* Comparison affinity */
char idxaff = 0; /* Indexed columns affinity */
CollSeq *pColl; /* Comparison collation sequence */
- Expr *pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr;
- Expr *pRhs = pTerm->pExpr->pRight;
- if( pRhs->flags & EP_xIsSelect ){
+ Expr *pLhs, *pRhs;
+
+ assert( ExprUseXList(pTerm->pExpr->pLeft) );
+ pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr;
+ pRhs = pTerm->pExpr->pRight;
+ if( ExprUseXSelect(pRhs) ){
pRhs = pRhs->x.pSelect->pEList->a[i].pExpr;
}else{
pRhs = pRhs->x.pList->a[i].pExpr;
@@ -141551,9 +163657,9 @@ static int whereRangeVectorLen(
** the right column of the right source table. And that the sort
** order of the index column is the same as the sort order of the
** leftmost index column. */
- if( pLhs->op!=TK_COLUMN
- || pLhs->iTable!=iCur
- || pLhs->iColumn!=pIdx->aiColumn[i+nEq]
+ if( pLhs->op!=TK_COLUMN
+ || pLhs->iTable!=iCur
+ || pLhs->iColumn!=pIdx->aiColumn[i+nEq]
|| pIdx->aSortOrder[i+nEq]!=pIdx->aSortOrder[nEq]
){
break;
@@ -141572,7 +163678,7 @@ static int whereRangeVectorLen(
}
/*
-** Adjust the cost C by the costMult facter T. This only occurs if
+** Adjust the cost C by the costMult factor T. This only occurs if
** compiled with -DSQLITE_ENABLE_COSTMULT
*/
#ifdef SQLITE_ENABLE_COSTMULT
@@ -141582,24 +163688,24 @@ static int whereRangeVectorLen(
#endif
/*
-** We have so far matched pBuilder->pNew->u.btree.nEq terms of the
+** We have so far matched pBuilder->pNew->u.btree.nEq terms of the
** index pIndex. Try to match one more.
**
-** When this function is called, pBuilder->pNew->nOut contains the
-** number of rows expected to be visited by filtering using the nEq
-** terms only. If it is modified, this value is restored before this
+** When this function is called, pBuilder->pNew->nOut contains the
+** number of rows expected to be visited by filtering using the nEq
+** terms only. If it is modified, this value is restored before this
** function returns.
**
-** If pProbe->tnum==0, that means pIndex is a fake index used for the
-** INTEGER PRIMARY KEY.
+** If pProbe->idxType==SQLITE_IDXTYPE_IPK, that means pIndex is
+** a fake index used for the INTEGER PRIMARY KEY.
*/
static int whereLoopAddBtreeIndex(
WhereLoopBuilder *pBuilder, /* The WhereLoop factory */
- struct SrcList_item *pSrc, /* FROM clause term being analyzed */
+ SrcItem *pSrc, /* FROM clause term being analyzed */
Index *pProbe, /* An index on pSrc */
LogEst nInMul /* log(Number of iterations due to IN) */
){
- WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyse context */
+ WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyze context */
Parse *pParse = pWInfo->pParse; /* Parsing context */
sqlite3 *db = pParse->db; /* Database connection malloc context */
WhereLoop *pNew; /* Template WhereLoop under construction */
@@ -141620,9 +163726,13 @@ static int whereLoopAddBtreeIndex(
WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */
pNew = pBuilder->pNew;
- if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
- WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d\n",
- pProbe->pTable->zName,pProbe->zName, pNew->u.btree.nEq));
+ assert( db->mallocFailed==0 || pParse->nErr>0 );
+ if( pParse->nErr ){
+ return pParse->rc;
+ }
+ WHERETRACE(0x800, ("BEGIN %s.addBtreeIdx(%s), nEq=%d, nSkip=%d, rRun=%d\n",
+ pProbe->pTable->zName,pProbe->zName,
+ pNew->u.btree.nEq, pNew->nSkip, pNew->rRun));
assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
@@ -141632,9 +163742,14 @@ static int whereLoopAddBtreeIndex(
assert( pNew->u.btree.nBtm==0 );
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
}
- if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+ if( pProbe->bUnordered || pProbe->bLowQual ){
+ if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+ if( pProbe->bLowQual ) opMask &= ~(WO_EQ|WO_IN|WO_IS);
+ }
assert( pNew->u.btree.nEq<pProbe->nColumn );
+ assert( pNew->u.btree.nEq<pProbe->nKeyCol
+ || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY );
saved_nEq = pNew->u.btree.nEq;
saved_nBtm = pNew->u.btree.nBtm;
@@ -141654,7 +163769,7 @@ static int whereLoopAddBtreeIndex(
LogEst rCostIdx;
LogEst nOutUnadjusted; /* nOut before IN() and WHERE adjustments */
int nIn = 0;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
int nRecValid = pBuilder->nRecValid;
#endif
if( (eOp==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
@@ -141668,38 +163783,38 @@ static int whereLoopAddBtreeIndex(
** to mix with a lower range bound from some other source */
if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue;
- /* Do not allow constraints from the WHERE clause to be used by the
- ** right table of a LEFT JOIN. Only constraints in the ON clause are
- ** allowed */
- if( (pSrc->fg.jointype & JT_LEFT)!=0
- && !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
+ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0
+ && !constraintCompatibleWithOuterJoin(pTerm,pSrc)
){
continue;
}
-
if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){
- pBuilder->bldFlags |= SQLITE_BLDF_UNIQUE;
+ pBuilder->bldFlags1 |= SQLITE_BLDF1_UNIQUE;
}else{
- pBuilder->bldFlags |= SQLITE_BLDF_INDEXED;
+ pBuilder->bldFlags1 |= SQLITE_BLDF1_INDEXED;
}
pNew->wsFlags = saved_wsFlags;
pNew->u.btree.nEq = saved_nEq;
pNew->u.btree.nBtm = saved_nBtm;
pNew->u.btree.nTop = saved_nTop;
pNew->nLTerm = saved_nLTerm;
- if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
+ if( pNew->nLTerm>=pNew->nLSlot
+ && whereLoopResize(db, pNew, pNew->nLTerm+1)
+ ){
+ break; /* OOM while trying to enlarge the pNew->aLTerm array */
+ }
pNew->aLTerm[pNew->nLTerm++] = pTerm;
pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf;
assert( nInMul==0
- || (pNew->wsFlags & WHERE_COLUMN_NULL)!=0
- || (pNew->wsFlags & WHERE_COLUMN_IN)!=0
- || (pNew->wsFlags & WHERE_SKIPSCAN)!=0
+ || (pNew->wsFlags & WHERE_COLUMN_NULL)!=0
+ || (pNew->wsFlags & WHERE_COLUMN_IN)!=0
+ || (pNew->wsFlags & WHERE_SKIPSCAN)!=0
);
if( eOp & WO_IN ){
Expr *pExpr = pTerm->pExpr;
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ if( ExprUseXSelect(pExpr) ){
/* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */
int i;
nIn = 46; assert( 46==sqlite3LogEst(25) );
@@ -141715,15 +163830,13 @@ static int whereLoopAddBtreeIndex(
}else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
/* "x IN (value, value, ...)" */
nIn = sqlite3LogEst(pExpr->x.pList->nExpr);
- assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
- ** changes "x IN (?)" into "x=?". */
}
- if( pProbe->hasStat1 ){
- LogEst M, logK, safetyMargin;
+ if( pProbe->hasStat1 && rLogSize>=10 ){
+ LogEst M, logK, x;
/* Let:
** N = the total number of rows in the table
** K = the number of entries on the RHS of the IN operator
- ** M = the number of rows in the table that match terms to the
+ ** M = the number of rows in the table that match terms to the
** to the left in the same index. If the IN operator is on
** the left-most index column, M==N.
**
@@ -141737,20 +163850,30 @@ static int whereLoopAddBtreeIndex(
** a safety margin of 2 (LogEst: 10) that favors using the IN operator
** with the index, as using an index has better worst-case behavior.
** If we do not have real sqlite_stat1 data, always prefer to use
- ** the index.
+ ** the index. Do not bother with this optimization on very small
+ ** tables (less than 2 rows) as it is pointless in that case.
*/
M = pProbe->aiRowLogEst[saved_nEq];
logK = estLog(nIn);
- safetyMargin = 10; /* TUNING: extra weight for indexed IN */
- if( M + logK + safetyMargin < nIn + rLogSize ){
+ /* TUNING v----- 10 to bias toward indexed IN */
+ x = M + logK + 10 - (nIn + rLogSize);
+ if( x>=0 ){
WHERETRACE(0x40,
- ("Scan preferred over IN operator on column %d of \"%s\" (%d<%d)\n",
- saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize));
- continue;
+ ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d) "
+ "prefers indexed lookup\n",
+ saved_nEq, M, logK, nIn, rLogSize, x));
+ }else if( nInMul<2 && OptimizationEnabled(db, SQLITE_SeekScan) ){
+ WHERETRACE(0x40,
+ ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d"
+ " nInMul=%d) prefers skip-scan\n",
+ saved_nEq, M, logK, nIn, rLogSize, x, nInMul));
+ pNew->wsFlags |= WHERE_IN_SEEKSCAN;
}else{
WHERETRACE(0x40,
- ("IN operator preferred on column %d of \"%s\" (%d>=%d)\n",
- saved_nEq, pProbe->zName, M+logK+10, nIn+rLogSize));
+ ("IN operator (N=%d M=%d logK=%d nIn=%d rLogSize=%d x=%d"
+ " nInMul=%d) prefers normal scan\n",
+ saved_nEq, M, logK, nIn, rLogSize, x, nInMul));
+ continue;
}
}
pNew->wsFlags |= WHERE_COLUMN_IN;
@@ -141758,61 +163881,63 @@ static int whereLoopAddBtreeIndex(
int iCol = pProbe->aiColumn[saved_nEq];
pNew->wsFlags |= WHERE_COLUMN_EQ;
assert( saved_nEq==pNew->u.btree.nEq );
- if( iCol==XN_ROWID
+ if( iCol==XN_ROWID
|| (iCol>=0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1)
){
- if( iCol==XN_ROWID || pProbe->uniqNotNull
- || (pProbe->nKeyCol==1 && pProbe->onError && eOp==WO_EQ)
+ if( iCol==XN_ROWID || pProbe->uniqNotNull
+ || (pProbe->nKeyCol==1 && pProbe->onError && eOp==WO_EQ)
){
pNew->wsFlags |= WHERE_ONEROW;
}else{
pNew->wsFlags |= WHERE_UNQ_WANTED;
}
}
+ if( scan.iEquiv>1 ) pNew->wsFlags |= WHERE_TRANSCONS;
}else if( eOp & WO_ISNULL ){
pNew->wsFlags |= WHERE_COLUMN_NULL;
- }else if( eOp & (WO_GT|WO_GE) ){
- testcase( eOp & WO_GT );
- testcase( eOp & WO_GE );
- pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
- pNew->u.btree.nBtm = whereRangeVectorLen(
- pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
- );
- pBtm = pTerm;
- pTop = 0;
- if( pTerm->wtFlags & TERM_LIKEOPT ){
- /* Range contraints that come from the LIKE optimization are
- ** always used in pairs. */
- pTop = &pTerm[1];
- assert( (pTop-(pTerm->pWC->a))<pTerm->pWC->nTerm );
- assert( pTop->wtFlags & TERM_LIKEOPT );
- assert( pTop->eOperator==WO_LT );
- if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
- pNew->aLTerm[pNew->nLTerm++] = pTop;
- pNew->wsFlags |= WHERE_TOP_LIMIT;
- pNew->u.btree.nTop = 1;
- }
- }else{
- assert( eOp & (WO_LT|WO_LE) );
- testcase( eOp & WO_LT );
- testcase( eOp & WO_LE );
- pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
- pNew->u.btree.nTop = whereRangeVectorLen(
+ }else{
+ int nVecLen = whereRangeVectorLen(
pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
);
- pTop = pTerm;
- pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
- pNew->aLTerm[pNew->nLTerm-2] : 0;
+ if( eOp & (WO_GT|WO_GE) ){
+ testcase( eOp & WO_GT );
+ testcase( eOp & WO_GE );
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
+ pNew->u.btree.nBtm = nVecLen;
+ pBtm = pTerm;
+ pTop = 0;
+ if( pTerm->wtFlags & TERM_LIKEOPT ){
+ /* Range constraints that come from the LIKE optimization are
+ ** always used in pairs. */
+ pTop = &pTerm[1];
+ assert( (pTop-(pTerm->pWC->a))<pTerm->pWC->nTerm );
+ assert( pTop->wtFlags & TERM_LIKEOPT );
+ assert( pTop->eOperator==WO_LT );
+ if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
+ pNew->aLTerm[pNew->nLTerm++] = pTop;
+ pNew->wsFlags |= WHERE_TOP_LIMIT;
+ pNew->u.btree.nTop = 1;
+ }
+ }else{
+ assert( eOp & (WO_LT|WO_LE) );
+ testcase( eOp & WO_LT );
+ testcase( eOp & WO_LE );
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
+ pNew->u.btree.nTop = nVecLen;
+ pTop = pTerm;
+ pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
+ pNew->aLTerm[pNew->nLTerm-2] : 0;
+ }
}
/* At this point pNew->nOut is set to the number of rows expected to
** be visited by the index scan before considering term pTerm, or the
- ** values of nIn and nInMul. In other words, assuming that all
+ ** values of nIn and nInMul. In other words, assuming that all
** "x IN(...)" terms are replaced with "x = ?". This block updates
** the value of pNew->nOut to account for pTerm (but not nIn/nInMul). */
assert( pNew->nOut==saved_nOut );
if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
- /* Adjust nOut using stat3/stat4 data. Or, if there is no stat3/stat4
+ /* Adjust nOut using stat4 data. Or, if there is no stat4
** data, using some other estimate. */
whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew);
}else{
@@ -141826,13 +163951,13 @@ static int whereLoopAddBtreeIndex(
pNew->nOut += pTerm->truthProb;
pNew->nOut -= nIn;
}else{
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
tRowcnt nOut = 0;
- if( nInMul==0
- && pProbe->nSample
- && pNew->u.btree.nEq<=pProbe->nSampleCol
- && ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect))
- && OptimizationEnabled(db, SQLITE_Stat34)
+ if( nInMul==0
+ && pProbe->nSample
+ && ALWAYS(pNew->u.btree.nEq<=pProbe->nSampleCol)
+ && ((eOp & WO_IN)==0 || ExprUseXList(pTerm->pExpr))
+ && OptimizationEnabled(db, SQLITE_Stat4)
){
Expr *pExpr = pTerm->pExpr;
if( (eOp & (WO_EQ|WO_ISNULL|WO_IS))!=0 ){
@@ -141847,6 +163972,27 @@ static int whereLoopAddBtreeIndex(
if( rc!=SQLITE_OK ) break; /* Jump out of the pTerm loop */
if( nOut ){
pNew->nOut = sqlite3LogEst(nOut);
+ if( nEq==1
+ /* TUNING: Mark terms as "low selectivity" if they seem likely
+ ** to be true for half or more of the rows in the table.
+ ** See tag-202002240-1 */
+ && pNew->nOut+10 > pProbe->aiRowLogEst[0]
+ ){
+#if WHERETRACE_ENABLED /* 0x01 */
+ if( sqlite3WhereTrace & 0x20 ){
+ sqlite3DebugPrintf(
+ "STAT4 determines term has low selectivity:\n");
+ sqlite3WhereTermPrint(pTerm, 999);
+ }
+#endif
+ pTerm->wtFlags |= TERM_HIGHTRUTH;
+ if( pTerm->wtFlags & TERM_HEURTRUTH ){
+ /* If the term has previously been used with an assumption of
+ ** higher selectivity, then set the flag to rerun the
+ ** loop computations. */
+ pBuilder->bldFlags2 |= SQLITE_BLDF2_2NDPASS;
+ }
+ }
if( pNew->nOut>saved_nOut ) pNew->nOut = saved_nOut;
pNew->nOut -= nIn;
}
@@ -141856,8 +164002,8 @@ static int whereLoopAddBtreeIndex(
{
pNew->nOut += (pProbe->aiRowLogEst[nEq] - pProbe->aiRowLogEst[nEq-1]);
if( eOp & WO_ISNULL ){
- /* TUNING: If there is no likelihood() value, assume that a
- ** "col IS NULL" expression matches twice as many rows
+ /* TUNING: If there is no likelihood() value, assume that a
+ ** "col IS NULL" expression matches twice as many rows
** as (col=?). */
pNew->nOut += 10;
}
@@ -141869,9 +164015,18 @@ static int whereLoopAddBtreeIndex(
** it to pNew->rRun, which is currently set to the cost of the index
** seek only. Then, if this is a non-covering index, add the cost of
** visiting the rows in the main table. */
- rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
+ assert( pSrc->pTab->szTabRow>0 );
+ if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){
+ /* The pProbe->szIdxRow is low for an IPK table since the interior
+ ** pages are small. Thus szIdxRow gives a good estimate of seek cost.
+ ** But the leaf pages are full-size, so pProbe->szIdxRow would badly
+ ** under-estimate the scanning cost. */
+ rCostIdx = pNew->nOut + 16;
+ }else{
+ rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
+ }
pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx);
- if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
+ if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK|WHERE_EXPRIDX))==0 ){
pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16);
}
ApplyCostMultiplier(pNew->rRun, pProbe->pTable->costMult);
@@ -141890,11 +164045,16 @@ static int whereLoopAddBtreeIndex(
if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
&& pNew->u.btree.nEq<pProbe->nColumn
+ && (pNew->u.btree.nEq<pProbe->nKeyCol ||
+ pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY)
){
+ if( pNew->u.btree.nEq>3 ){
+ sqlite3ProgressCheck(pParse);
+ }
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn);
}
pNew->nOut = saved_nOut;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
pBuilder->nRecValid = nRecValid;
#endif
}
@@ -141909,18 +164069,20 @@ static int whereLoopAddBtreeIndex(
/* Consider using a skip-scan if there are no WHERE clause constraints
** available for the left-most terms of the index, and if the average
- ** number of repeats in the left-most terms is at least 18.
+ ** number of repeats in the left-most terms is at least 18.
**
** The magic number 18 is selected on the basis that scanning 17 rows
** is almost always quicker than an index seek (even though if the index
** contains fewer than 2^17 rows we assume otherwise in other parts of
- ** the code). And, even if it is not, it should not be too much slower.
+ ** the code). And, even if it is not, it should not be too much slower.
** On the other hand, the extra seeks could end up being significantly
** more expensive. */
assert( 42==sqlite3LogEst(18) );
if( saved_nEq==saved_nSkip
&& saved_nEq+1<pProbe->nKeyCol
+ && saved_nEq==pNew->nLTerm
&& pProbe->noSkipScan==0
+ && pProbe->hasStat1!=0
&& OptimizationEnabled(db, SQLITE_SkipScan)
&& pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
&& (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
@@ -141967,7 +164129,8 @@ static int indexMightHelpWithOrderBy(
if( pIndex->bUnordered ) return 0;
if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0;
for(ii=0; ii<pOB->nExpr; ii++){
- Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr);
+ Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr);
+ if( NEVER(pExpr==0) ) continue;
if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
if( pExpr->iColumn<0 ) return 1;
for(jj=0; jj<pIndex->nKeyCol; jj++){
@@ -141988,19 +164151,30 @@ static int indexMightHelpWithOrderBy(
/* Check to see if a partial index with pPartIndexWhere can be used
** in the current query. Return true if it can be and false if not.
*/
-static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
+static int whereUsablePartialIndex(
+ int iTab, /* The table for which we want an index */
+ u8 jointype, /* The JT_* flags on the join */
+ WhereClause *pWC, /* The WHERE clause of the query */
+ Expr *pWhere /* The WHERE clause from the partial index */
+){
int i;
WhereTerm *pTerm;
- Parse *pParse = pWC->pWInfo->pParse;
+ Parse *pParse;
+
+ if( jointype & JT_LTORJ ) return 0;
+ pParse = pWC->pWInfo->pParse;
while( pWhere->op==TK_AND ){
- if( !whereUsablePartialIndex(iTab,pWC,pWhere->pLeft) ) return 0;
+ if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0;
pWhere = pWhere->pRight;
}
if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0;
for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
- Expr *pExpr = pTerm->pExpr;
- if( (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab)
- && sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab)
+ Expr *pExpr;
+ pExpr = pTerm->pExpr;
+ if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab)
+ && ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON))
+ && sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab)
+ && (pTerm->wtFlags & TERM_VNULL)==0
){
return 1;
}
@@ -142009,6 +164183,243 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
}
/*
+** pIdx is an index containing expressions. Check it see if any of the
+** expressions in the index match the pExpr expression.
+*/
+static int exprIsCoveredByIndex(
+ const Expr *pExpr,
+ const Index *pIdx,
+ int iTabCur
+){
+ int i;
+ for(i=0; i<pIdx->nColumn; i++){
+ if( pIdx->aiColumn[i]==XN_EXPR
+ && sqlite3ExprCompare(0, pExpr, pIdx->aColExpr->a[i].pExpr, iTabCur)==0
+ ){
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** Structure passed to the whereIsCoveringIndex Walker callback.
+*/
+typedef struct CoveringIndexCheck CoveringIndexCheck;
+struct CoveringIndexCheck {
+ Index *pIdx; /* The index */
+ int iTabCur; /* Cursor number for the corresponding table */
+ u8 bExpr; /* Uses an indexed expression */
+ u8 bUnidx; /* Uses an unindexed column not within an indexed expr */
+};
+
+/*
+** Information passed in is pWalk->u.pCovIdxCk. Call it pCk.
+**
+** If the Expr node references the table with cursor pCk->iTabCur, then
+** make sure that column is covered by the index pCk->pIdx. We know that
+** all columns less than 63 (really BMS-1) are covered, so we don't need
+** to check them. But we do need to check any column at 63 or greater.
+**
+** If the index does not cover the column, then set pWalk->eCode to
+** non-zero and return WRC_Abort to stop the search.
+**
+** If this node does not disprove that the index can be a covering index,
+** then just return WRC_Continue, to continue the search.
+**
+** If pCk->pIdx contains indexed expressions and one of those expressions
+** matches pExpr, then prune the search.
+*/
+static int whereIsCoveringIndexWalkCallback(Walker *pWalk, Expr *pExpr){
+ int i; /* Loop counter */
+ const Index *pIdx; /* The index of interest */
+ const i16 *aiColumn; /* Columns contained in the index */
+ u16 nColumn; /* Number of columns in the index */
+ CoveringIndexCheck *pCk; /* Info about this search */
+
+ pCk = pWalk->u.pCovIdxCk;
+ pIdx = pCk->pIdx;
+ if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN) ){
+ /* if( pExpr->iColumn<(BMS-1) && pIdx->bHasExpr==0 ) return WRC_Continue;*/
+ if( pExpr->iTable!=pCk->iTabCur ) return WRC_Continue;
+ pIdx = pWalk->u.pCovIdxCk->pIdx;
+ aiColumn = pIdx->aiColumn;
+ nColumn = pIdx->nColumn;
+ for(i=0; i<nColumn; i++){
+ if( aiColumn[i]==pExpr->iColumn ) return WRC_Continue;
+ }
+ pCk->bUnidx = 1;
+ return WRC_Abort;
+ }else if( pIdx->bHasExpr
+ && exprIsCoveredByIndex(pExpr, pIdx, pWalk->u.pCovIdxCk->iTabCur) ){
+ pCk->bExpr = 1;
+ return WRC_Prune;
+ }
+ return WRC_Continue;
+}
+
+
+/*
+** pIdx is an index that covers all of the low-number columns used by
+** pWInfo->pSelect (columns from 0 through 62) or an index that has
+** expressions terms. Hence, we cannot determine whether or not it is
+** a covering index by using the colUsed bitmasks. We have to do a search
+** to see if the index is covering. This routine does that search.
+**
+** The return value is one of these:
+**
+** 0 The index is definitely not a covering index
+**
+** WHERE_IDX_ONLY The index is definitely a covering index
+**
+** WHERE_EXPRIDX The index is likely a covering index, but it is
+** difficult to determine precisely because of the
+** expressions that are indexed. Score it as a
+** covering index, but still keep the main table open
+** just in case we need it.
+**
+** This routine is an optimization. It is always safe to return zero.
+** But returning one of the other two values when zero should have been
+** returned can lead to incorrect bytecode and assertion faults.
+*/
+static SQLITE_NOINLINE u32 whereIsCoveringIndex(
+ WhereInfo *pWInfo, /* The WHERE clause context */
+ Index *pIdx, /* Index that is being tested */
+ int iTabCur /* Cursor for the table being indexed */
+){
+ int i, rc;
+ struct CoveringIndexCheck ck;
+ Walker w;
+ if( pWInfo->pSelect==0 ){
+ /* We don't have access to the full query, so we cannot check to see
+ ** if pIdx is covering. Assume it is not. */
+ return 0;
+ }
+ if( pIdx->bHasExpr==0 ){
+ for(i=0; i<pIdx->nColumn; i++){
+ if( pIdx->aiColumn[i]>=BMS-1 ) break;
+ }
+ if( i>=pIdx->nColumn ){
+ /* pIdx does not index any columns greater than 62, but we know from
+ ** colMask that columns greater than 62 are used, so this is not a
+ ** covering index */
+ return 0;
+ }
+ }
+ ck.pIdx = pIdx;
+ ck.iTabCur = iTabCur;
+ ck.bExpr = 0;
+ ck.bUnidx = 0;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = whereIsCoveringIndexWalkCallback;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
+ w.u.pCovIdxCk = &ck;
+ sqlite3WalkSelect(&w, pWInfo->pSelect);
+ if( ck.bUnidx ){
+ rc = 0;
+ }else if( ck.bExpr ){
+ rc = WHERE_EXPRIDX;
+ }else{
+ rc = WHERE_IDX_ONLY;
+ }
+ return rc;
+}
+
+/*
+** This is an sqlite3ParserAddCleanup() callback that is invoked to
+** free the Parse->pIdxEpr list when the Parse object is destroyed.
+*/
+static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
+ IndexedExpr **pp = (IndexedExpr**)pObject;
+ while( *pp!=0 ){
+ IndexedExpr *p = *pp;
+ *pp = p->pIENext;
+ sqlite3ExprDelete(db, p->pExpr);
+ sqlite3DbFreeNN(db, p);
+ }
+}
+
+/*
+** This function is called for a partial index - one with a WHERE clause - in
+** two scenarios. In both cases, it determines whether or not the WHERE
+** clause on the index implies that a column of the table may be safely
+** replaced by a constant expression. For example, in the following
+** SELECT:
+**
+** CREATE INDEX i1 ON t1(b, c) WHERE a=<expr>;
+** SELECT a, b, c FROM t1 WHERE a=<expr> AND b=?;
+**
+** The "a" in the select-list may be replaced by <expr>, iff:
+**
+** (a) <expr> is a constant expression, and
+** (b) The (a=<expr>) comparison uses the BINARY collation sequence, and
+** (c) Column "a" has an affinity other than NONE or BLOB.
+**
+** If argument pItem is NULL, then pMask must not be NULL. In this case this
+** function is being called as part of determining whether or not pIdx
+** is a covering index. This function clears any bits in (*pMask)
+** corresponding to columns that may be replaced by constants as described
+** above.
+**
+** Otherwise, if pItem is not NULL, then this function is being called
+** as part of coding a loop that uses index pIdx. In this case, add entries
+** to the Parse.pIdxPartExpr list for each column that can be replaced
+** by a constant.
+*/
+static void wherePartIdxExpr(
+ Parse *pParse, /* Parse context */
+ Index *pIdx, /* Partial index being processed */
+ Expr *pPart, /* WHERE clause being processed */
+ Bitmask *pMask, /* Mask to clear bits in */
+ int iIdxCur, /* Cursor number for index */
+ SrcItem *pItem /* The FROM clause entry for the table */
+){
+ assert( pItem==0 || (pItem->fg.jointype & JT_RIGHT)==0 );
+ assert( (pItem==0 || pMask==0) && (pMask!=0 || pItem!=0) );
+
+ if( pPart->op==TK_AND ){
+ wherePartIdxExpr(pParse, pIdx, pPart->pRight, pMask, iIdxCur, pItem);
+ pPart = pPart->pLeft;
+ }
+
+ if( (pPart->op==TK_EQ || pPart->op==TK_IS) ){
+ Expr *pLeft = pPart->pLeft;
+ Expr *pRight = pPart->pRight;
+ u8 aff;
+
+ if( pLeft->op!=TK_COLUMN ) return;
+ if( !sqlite3ExprIsConstant(pRight) ) return;
+ if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return;
+ if( pLeft->iColumn<0 ) return;
+ aff = pIdx->pTable->aCol[pLeft->iColumn].affinity;
+ if( aff>=SQLITE_AFF_TEXT ){
+ if( pItem ){
+ sqlite3 *db = pParse->db;
+ IndexedExpr *p = (IndexedExpr*)sqlite3DbMallocRaw(db, sizeof(*p));
+ if( p ){
+ int bNullRow = (pItem->fg.jointype&(JT_LEFT|JT_LTORJ))!=0;
+ p->pExpr = sqlite3ExprDup(db, pRight, 0);
+ p->iDataCur = pItem->iCursor;
+ p->iIdxCur = iIdxCur;
+ p->iIdxCol = pLeft->iColumn;
+ p->bMaybeNullRow = bNullRow;
+ p->pIENext = pParse->pIdxPartExpr;
+ p->aff = aff;
+ pParse->pIdxPartExpr = p;
+ if( p->pIENext==0 ){
+ void *pArg = (void*)&pParse->pIdxPartExpr;
+ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg);
+ }
+ }
+ }else if( pLeft->iColumn<(BMS-1) ){
+ *pMask &= ~((Bitmask)1 << pLeft->iColumn);
+ }
+ }
+ }
+}
+
+
+/*
** Add all WhereLoop objects for a single table of the join where the table
** is identified by pBuilder->pNew->iTab. That table is guaranteed to be
** a b-tree table, not a virtual table.
@@ -142022,18 +164433,18 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
** cost = nRow * K // scan of covering index
** cost = nRow * (K+3.0) // scan of non-covering index
**
-** where K is a value between 1.1 and 3.0 set based on the relative
+** where K is a value between 1.1 and 3.0 set based on the relative
** estimated average size of the index and table records.
**
** For an index scan, where nVisit is the number of index rows visited
-** by the scan, and nSeek is the number of seek operations required on
+** by the scan, and nSeek is the number of seek operations required on
** the index b-tree:
**
** cost = nSeek * (log(nRow) + K * nVisit) // covering index
** cost = nSeek * (log(nRow) + (K+3.0) * nVisit) // non-covering index
**
-** Normally, nSeek is 1. nSeek values greater than 1 come about if the
-** WHERE clause includes "x IN (....)" terms used in place of "x=?". Or when
+** Normally, nSeek is 1. nSeek values greater than 1 come about if the
+** WHERE clause includes "x IN (....)" terms used in place of "x=?". Or when
** implicit "x IN (SELECT x FROM tbl)" terms are added for skip-scans.
**
** The estimated values (nRow, nVisit, nSeek) often contain a large amount
@@ -142046,7 +164457,7 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
*/
static int whereLoopAddBtree(
WhereLoopBuilder *pBuilder, /* WHERE clause information */
- Bitmask mPrereq /* Extra prerequesites for using this table */
+ Bitmask mPrereq /* Extra prerequisites for using this table */
){
WhereInfo *pWInfo; /* WHERE analysis context */
Index *pProbe; /* An index we are evaluating */
@@ -142054,16 +164465,15 @@ static int whereLoopAddBtree(
LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */
i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */
SrcList *pTabList; /* The FROM clause */
- struct SrcList_item *pSrc; /* The FROM clause btree term to add */
+ SrcItem *pSrc; /* The FROM clause btree term to add */
WhereLoop *pNew; /* Template WhereLoop object */
int rc = SQLITE_OK; /* Return code */
int iSortIdx = 1; /* Index number */
int b; /* A boolean value */
LogEst rSize; /* number of rows in the table */
- LogEst rLogSize; /* Logarithm of the number of rows in the table */
WhereClause *pWC; /* The parsed WHERE clause */
Table *pTab; /* Table being queried */
-
+
pNew = pBuilder->pNew;
pWInfo = pBuilder->pWInfo;
pTabList = pWInfo->pTabList;
@@ -142072,9 +164482,10 @@ static int whereLoopAddBtree(
pWC = pBuilder->pWC;
assert( !IsVirtual(pSrc->pTab) );
- if( pSrc->pIBIndex ){
+ if( pSrc->fg.isIndexedBy ){
+ assert( pSrc->fg.isCte==0 );
/* An INDEXED BY clause specifies a particular index to use */
- pProbe = pSrc->pIBIndex;
+ pProbe = pSrc->u2.pIBIndex;
}else if( !HasRowid(pTab) ){
pProbe = pTab->pIndex;
}else{
@@ -142090,7 +164501,8 @@ static int whereLoopAddBtree(
sPk.aiRowLogEst = aiRowEstPk;
sPk.onError = OE_Replace;
sPk.pTable = pTab;
- sPk.szIdxRow = pTab->szTabRow;
+ sPk.szIdxRow = 3; /* TUNING: Interior rows of IPK table are very small */
+ sPk.idxType = SQLITE_IDXTYPE_IPK;
aiRowEstPk[0] = pTab->nRowLogEst;
aiRowEstPk[1] = 0;
pFirst = pSrc->pTab->pIndex;
@@ -142102,22 +164514,24 @@ static int whereLoopAddBtree(
pProbe = &sPk;
}
rSize = pTab->nRowLogEst;
- rLogSize = estLog(rSize);
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/* Automatic indexes */
if( !pBuilder->pOrSet /* Not part of an OR optimization */
- && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
+ && (pWInfo->wctrlFlags & (WHERE_RIGHT_JOIN|WHERE_OR_SUBCLAUSE))==0
&& (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
- && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */
+ && !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */
&& !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */
&& HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */
&& !pSrc->fg.isCorrelated /* Not a correlated subquery */
&& !pSrc->fg.isRecursive /* Not a recursive common table expression. */
+ && (pSrc->fg.jointype & JT_RIGHT)==0 /* Not the right tab of a RIGHT JOIN */
){
/* Generate auto-index WhereLoops */
+ LogEst rLogSize; /* Logarithm of the number of rows in the table */
WhereTerm *pTerm;
WhereTerm *pWCEnd = pWC->a + pWC->nTerm;
+ rLogSize = estLog(rSize);
for(pTerm=pWC->a; rc==SQLITE_OK && pTerm<pWCEnd; pTerm++){
if( pTerm->prereqRight & pNew->maskSelf ) continue;
if( termCanDriveIndex(pTerm, pSrc, 0) ){
@@ -142135,10 +164549,11 @@ static int whereLoopAddBtree(
** those objects, since there is no opportunity to add schema
** indexes on subqueries and views. */
pNew->rSetup = rLogSize + rSize;
- if( pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0 ){
+ if( !IsView(pTab) && (pTab->tabFlags & TF_Ephemeral)==0 ){
pNew->rSetup += 28;
}else{
- pNew->rSetup -= 10;
+ pNew->rSetup -= 25; /* Greatly reduced setup cost for auto indexes
+ ** on ephemeral materializations of views */
}
ApplyCostMultiplier(pNew->rSetup, pTab->costMult);
if( pNew->rSetup<0 ) pNew->rSetup = 0;
@@ -142156,13 +164571,15 @@ static int whereLoopAddBtree(
}
#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
- /* Loop over all indices. If there was an INDEXED BY clause, then only
+ /* Loop over all indices. If there was an INDEXED BY clause, then only
** consider index pProbe. */
- for(; rc==SQLITE_OK && pProbe;
- pProbe=(pSrc->pIBIndex ? 0 : pProbe->pNext), iSortIdx++
+ for(; rc==SQLITE_OK && pProbe;
+ pProbe=(pSrc->fg.isIndexedBy ? 0 : pProbe->pNext), iSortIdx++
){
if( pProbe->pPartIdxWhere!=0
- && !whereUsablePartialIndex(pSrc->iCursor, pWC, pProbe->pPartIdxWhere) ){
+ && !whereUsablePartialIndex(pSrc->iCursor, pSrc->fg.jointype, pWC,
+ pProbe->pPartIdxWhere)
+ ){
testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */
continue; /* Partial index inappropriate for this query */
}
@@ -142179,16 +164596,32 @@ static int whereLoopAddBtree(
pNew->nOut = rSize;
pNew->u.btree.pIndex = pProbe;
b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor);
+
/* The ONEPASS_DESIRED flags never occurs together with ORDER BY */
assert( (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 );
- if( pProbe->tnum<=0 ){
+ if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){
/* Integer primary key index */
pNew->wsFlags = WHERE_IPK;
/* Full table scan */
pNew->iSortIdx = b ? iSortIdx : 0;
- /* TUNING: Cost of full table scan is (N*3.0). */
+ /* TUNING: Cost of full table scan is 3.0*N. The 3.0 factor is an
+ ** extra cost designed to discourage the use of full table scans,
+ ** since index lookups have better worst-case performance if our
+ ** stat guesses are wrong. Reduce the 3.0 penalty slightly
+ ** (to 2.75) if we have valid STAT4 information for the table.
+ ** At 2.75, a full table scan is preferred over using an index on
+ ** a column with just two distinct values where each value has about
+ ** an equal number of appearances. Without STAT4 data, we still want
+ ** to use an index in that case, since the constraint might be for
+ ** the scarcer of the two values, and in that case an index lookup is
+ ** better.
+ */
+#ifdef SQLITE_ENABLE_STAT4
+ pNew->rRun = rSize + 16 - 2*((pTab->tabFlags & TF_HasStat4)!=0);
+#else
pNew->rRun = rSize + 16;
+#endif
ApplyCostMultiplier(pNew->rRun, pTab->costMult);
whereLoopOutputAdjust(pWC, pNew, rSize);
rc = whereLoopInsert(pBuilder, pNew);
@@ -142197,17 +164630,50 @@ static int whereLoopAddBtree(
}else{
Bitmask m;
if( pProbe->isCovering ){
- pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
m = 0;
+ pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
}else{
m = pSrc->colUsed & pProbe->colNotIdxed;
- pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED;
+ if( pProbe->pPartIdxWhere ){
+ wherePartIdxExpr(
+ pWInfo->pParse, pProbe, pProbe->pPartIdxWhere, &m, 0, 0
+ );
+ }
+ pNew->wsFlags = WHERE_INDEXED;
+ if( m==TOPBIT || (pProbe->bHasExpr && !pProbe->bHasVCol && m!=0) ){
+ u32 isCov = whereIsCoveringIndex(pWInfo, pProbe, pSrc->iCursor);
+ if( isCov==0 ){
+ WHERETRACE(0x200,
+ ("-> %s is not a covering index"
+ " according to whereIsCoveringIndex()\n", pProbe->zName));
+ assert( m!=0 );
+ }else{
+ m = 0;
+ pNew->wsFlags |= isCov;
+ if( isCov & WHERE_IDX_ONLY ){
+ WHERETRACE(0x200,
+ ("-> %s is a covering expression index"
+ " according to whereIsCoveringIndex()\n", pProbe->zName));
+ }else{
+ assert( isCov==WHERE_EXPRIDX );
+ WHERETRACE(0x200,
+ ("-> %s might be a covering expression index"
+ " according to whereIsCoveringIndex()\n", pProbe->zName));
+ }
+ }
+ }else if( m==0 ){
+ WHERETRACE(0x200,
+ ("-> %s a covering index according to bitmasks\n",
+ pProbe->zName, m==0 ? "is" : "is not"));
+ pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
+ }
}
/* Full scan via index */
if( b
|| !HasRowid(pTab)
|| pProbe->pPartIdxWhere!=0
+ || pSrc->fg.isIndexedBy
|| ( m==0
&& pProbe->bUnordered==0
&& (pProbe->szIdxRow<pTab->szTabRow)
@@ -142246,27 +164712,34 @@ static int whereLoopAddBtree(
if( pTerm->eOperator & (WO_EQ|WO_IS) ) nLookup -= 19;
}
}
-
+
pNew->rRun = sqlite3LogEstAdd(pNew->rRun, nLookup);
}
ApplyCostMultiplier(pNew->rRun, pTab->costMult);
whereLoopOutputAdjust(pWC, pNew, rSize);
- rc = whereLoopInsert(pBuilder, pNew);
+ if( (pSrc->fg.jointype & JT_RIGHT)!=0 && pProbe->aColExpr ){
+ /* Do not do an SCAN of a index-on-expression in a RIGHT JOIN
+ ** because the cursor used to access the index might not be
+ ** positioned to the correct row during the right-join no-match
+ ** loop. */
+ }else{
+ rc = whereLoopInsert(pBuilder, pNew);
+ }
pNew->nOut = rSize;
if( rc ) break;
}
}
- pBuilder->bldFlags = 0;
+ pBuilder->bldFlags1 = 0;
rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0);
- if( pBuilder->bldFlags==SQLITE_BLDF_INDEXED ){
+ if( pBuilder->bldFlags1==SQLITE_BLDF1_INDEXED ){
/* If a non-unique index is used, or if a prefix of the key for
** unique index is used (making the index functionally non-unique)
** then the sqlite_stat1 data becomes important for scoring the
** plan */
pTab->tabFlags |= TF_StatsUsed;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3Stat4ProbeFree(pBuilder->pRec);
pBuilder->nRecValid = 0;
pBuilder->pRec = 0;
@@ -142278,6 +164751,15 @@ static int whereLoopAddBtree(
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
+** Return true if pTerm is a virtual table LIMIT or OFFSET term.
+*/
+static int isLimitTerm(WhereTerm *pTerm){
+ assert( pTerm->eOperator==WO_AUX || pTerm->eMatchOp==0 );
+ return pTerm->eMatchOp>=SQLITE_INDEX_CONSTRAINT_LIMIT
+ && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET;
+}
+
+/*
** Argument pIdxInfo is already populated with all constraints that may
** be used by the virtual table identified by pBuilder->pNew->iTab. This
** function marks a subset of those constraints usable, invokes the
@@ -142304,9 +164786,11 @@ static int whereLoopAddVirtualOne(
u16 mExclude, /* Exclude terms using these operators */
sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */
u16 mNoOmit, /* Do not omit these constraints */
- int *pbIn /* OUT: True if plan uses an IN(...) op */
+ int *pbIn, /* OUT: True if plan uses an IN(...) op */
+ int *pbRetryLimit /* OUT: Retry without LIMIT/OFFSET */
){
WhereClause *pWC = pBuilder->pWC;
+ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
struct sqlite3_index_constraint *pIdxCons;
struct sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage;
int i;
@@ -142314,21 +164798,22 @@ static int whereLoopAddVirtualOne(
int rc = SQLITE_OK;
WhereLoop *pNew = pBuilder->pNew;
Parse *pParse = pBuilder->pWInfo->pParse;
- struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab];
+ SrcItem *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab];
int nConstraint = pIdxInfo->nConstraint;
assert( (mUsable & mPrereq)==mPrereq );
*pbIn = 0;
pNew->prereq = mPrereq;
- /* Set the usable flag on the subset of constraints identified by
+ /* Set the usable flag on the subset of constraints identified by
** arguments mUsable and mExclude. */
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
for(i=0; i<nConstraint; i++, pIdxCons++){
WhereTerm *pTerm = &pWC->a[pIdxCons->iTermOffset];
pIdxCons->usable = 0;
- if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight
+ if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight
&& (pTerm->eOperator & mExclude)==0
+ && (pbRetryLimit || !isLimitTerm(pTerm))
){
pIdxCons->usable = 1;
}
@@ -142344,6 +164829,7 @@ static int whereLoopAddVirtualOne(
pIdxInfo->estimatedRows = 25;
pIdxInfo->idxFlags = 0;
pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed;
+ pHidden->mHandleIn = 0;
/* Invoke the virtual table xBestIndex() method */
rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo);
@@ -142353,7 +164839,7 @@ static int whereLoopAddVirtualOne(
** that the particular combination of parameters provided is unusable.
** Make no entries in the loop table.
*/
- WHERETRACE(0xffff, (" ^^^^--- non-viable plan rejected!\n"));
+ WHERETRACE(0xffffffff, (" ^^^^--- non-viable plan rejected!\n"));
return SQLITE_OK;
}
return rc;
@@ -142361,8 +164847,8 @@ static int whereLoopAddVirtualOne(
mxTerm = -1;
assert( pNew->nLSlot>=nConstraint );
- for(i=0; i<nConstraint; i++) pNew->aLTerm[i] = 0;
- pNew->u.vtab.omitMask = 0;
+ memset(pNew->aLTerm, 0, sizeof(pNew->aLTerm[0])*nConstraint );
+ memset(&pNew->u.vtab, 0, sizeof(pNew->u.vtab));
pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
for(i=0; i<nConstraint; i++, pIdxCons++){
int iTerm;
@@ -142389,8 +164875,20 @@ static int whereLoopAddVirtualOne(
if( iTerm>mxTerm ) mxTerm = iTerm;
testcase( iTerm==15 );
testcase( iTerm==16 );
- if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm;
- if( (pTerm->eOperator & WO_IN)!=0 ){
+ if( pUsage[i].omit ){
+ if( i<16 && ((1<<i)&mNoOmit)==0 ){
+ testcase( i!=iTerm );
+ pNew->u.vtab.omitMask |= 1<<iTerm;
+ }else{
+ testcase( i!=iTerm );
+ }
+ if( pTerm->eMatchOp==SQLITE_INDEX_CONSTRAINT_OFFSET ){
+ pNew->u.vtab.bOmitOffset = 1;
+ }
+ }
+ if( SMASKBIT32(i) & pHidden->mHandleIn ){
+ pNew->u.vtab.mHandleIn |= MASKBIT32(iTerm);
+ }else if( (pTerm->eOperator & WO_IN)!=0 ){
/* A virtual table that is constrained by an IN clause may not
** consume the ORDER BY clause because (1) the order of IN terms
** is not necessarily related to the order of output terms and
@@ -142400,9 +164898,24 @@ static int whereLoopAddVirtualOne(
pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE;
*pbIn = 1; assert( (mExclude & WO_IN)==0 );
}
+
+ assert( pbRetryLimit || !isLimitTerm(pTerm) );
+ if( isLimitTerm(pTerm) && *pbIn ){
+ /* If there is an IN(...) term handled as an == (separate call to
+ ** xFilter for each value on the RHS of the IN) and a LIMIT or
+ ** OFFSET term handled as well, the plan is unusable. Set output
+ ** variable *pbRetryLimit to true to tell the caller to retry with
+ ** LIMIT and OFFSET disabled. */
+ if( pIdxInfo->needToFreeIdxStr ){
+ sqlite3_free(pIdxInfo->idxStr);
+ pIdxInfo->idxStr = 0;
+ pIdxInfo->needToFreeIdxStr = 0;
+ }
+ *pbRetryLimit = 1;
+ return SQLITE_OK;
+ }
}
}
- pNew->u.vtab.omitMask &= ~mNoOmit;
pNew->nLTerm = mxTerm+1;
for(i=0; i<=mxTerm; i++){
@@ -142437,7 +164950,7 @@ static int whereLoopAddVirtualOne(
sqlite3_free(pNew->u.vtab.idxStr);
pNew->u.vtab.needFree = 0;
}
- WHERETRACE(0xffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n",
+ WHERETRACE(0xffffffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n",
*pbIn, (sqlite3_uint64)mPrereq,
(sqlite3_uint64)(pNew->prereq & ~mPrereq)));
@@ -142445,11 +164958,19 @@ static int whereLoopAddVirtualOne(
}
/*
-** If this function is invoked from within an xBestIndex() callback, it
-** returns a pointer to a buffer containing the name of the collation
-** sequence associated with element iCons of the sqlite3_index_info.aConstraint
-** array. Or, if iCons is out of range or there is no active xBestIndex
-** call, return NULL.
+** Return the collating sequence for a constraint passed into xBestIndex.
+**
+** pIdxInfo must be an sqlite3_index_info structure passed into xBestIndex.
+** This routine depends on there being a HiddenIndexInfo structure immediately
+** following the sqlite3_index_info structure.
+**
+** Return a pointer to the collation name:
+**
+** 1. If there is an explicit COLLATE operator on the constraint, return it.
+**
+** 2. Else, if the column has an alternative collation, return that.
+**
+** 3. Otherwise, return "BINARY".
*/
SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){
HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
@@ -142459,7 +164980,7 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int
int iTerm = pIdxInfo->aConstraint[iCons].iTermOffset;
Expr *pX = pHidden->pWC->a[iTerm].pExpr;
if( pX->pLeft ){
- pC = sqlite3BinaryCompareCollSeq(pHidden->pParse, pX->pLeft, pX->pRight);
+ pC = sqlite3ExprCompareCollSeq(pHidden->pParse, pX);
}
zRet = (pC ? pC->zName : sqlite3StrBINARY);
}
@@ -142467,6 +164988,92 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int
}
/*
+** Return true if constraint iCons is really an IN(...) constraint, or
+** false otherwise. If iCons is an IN(...) constraint, set (if bHandle!=0)
+** or clear (if bHandle==0) the flag to handle it using an iterator.
+*/
+SQLITE_API int sqlite3_vtab_in(sqlite3_index_info *pIdxInfo, int iCons, int bHandle){
+ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
+ u32 m = SMASKBIT32(iCons);
+ if( m & pHidden->mIn ){
+ if( bHandle==0 ){
+ pHidden->mHandleIn &= ~m;
+ }else if( bHandle>0 ){
+ pHidden->mHandleIn |= m;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** This interface is callable from within the xBestIndex callback only.
+**
+** If possible, set (*ppVal) to point to an object containing the value
+** on the right-hand-side of constraint iCons.
+*/
+SQLITE_API int sqlite3_vtab_rhs_value(
+ sqlite3_index_info *pIdxInfo, /* Copy of first argument to xBestIndex */
+ int iCons, /* Constraint for which RHS is wanted */
+ sqlite3_value **ppVal /* Write value extracted here */
+){
+ HiddenIndexInfo *pH = (HiddenIndexInfo*)&pIdxInfo[1];
+ sqlite3_value *pVal = 0;
+ int rc = SQLITE_OK;
+ if( iCons<0 || iCons>=pIdxInfo->nConstraint ){
+ rc = SQLITE_MISUSE_BKPT; /* EV: R-30545-25046 */
+ }else{
+ if( pH->aRhs[iCons]==0 ){
+ WhereTerm *pTerm = &pH->pWC->a[pIdxInfo->aConstraint[iCons].iTermOffset];
+ rc = sqlite3ValueFromExpr(
+ pH->pParse->db, pTerm->pExpr->pRight, ENC(pH->pParse->db),
+ SQLITE_AFF_BLOB, &pH->aRhs[iCons]
+ );
+ testcase( rc!=SQLITE_OK );
+ }
+ pVal = pH->aRhs[iCons];
+ }
+ *ppVal = pVal;
+
+ if( rc==SQLITE_OK && pVal==0 ){ /* IMP: R-19933-32160 */
+ rc = SQLITE_NOTFOUND; /* IMP: R-36424-56542 */
+ }
+
+ return rc;
+}
+
+/*
+** Return true if ORDER BY clause may be handled as DISTINCT.
+*/
+SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info *pIdxInfo){
+ HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
+ assert( pHidden->eDistinct>=0 && pHidden->eDistinct<=3 );
+ return pHidden->eDistinct;
+}
+
+/*
+** Cause the prepared statement that is associated with a call to
+** xBestIndex to potentially use all schemas. If the statement being
+** prepared is read-only, then just start read transactions on all
+** schemas. But if this is a write operation, start writes on all
+** schemas.
+**
+** This is used by the (built-in) sqlite_dbpage virtual table.
+*/
+SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(Parse *pParse){
+ int nDb = pParse->db->nDb;
+ int i;
+ for(i=0; i<nDb; i++){
+ sqlite3CodeVerifySchema(pParse, i);
+ }
+ if( DbMaskNonZero(pParse->writeMask) ){
+ for(i=0; i<nDb; i++){
+ sqlite3BeginWriteOperation(pParse, 0, i);
+ }
+ }
+}
+
+/*
** Add all WhereLoop objects for a table of the join identified by
** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table.
**
@@ -142475,8 +165082,8 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int
** entries that occur before the virtual table in the FROM clause and are
** separated from it by at least one LEFT or CROSS JOIN. Similarly, the
** mUnusable mask contains all FROM clause entries that occur after the
-** virtual table and are separated from it by at least one LEFT or
-** CROSS JOIN.
+** virtual table and are separated from it by at least one LEFT or
+** CROSS JOIN.
**
** For example, if the query were:
**
@@ -142484,9 +165091,9 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int
**
** then mPrereq corresponds to (t1, t2) and mUnusable to (t5, t6).
**
-** All the tables in mPrereq must be scanned before the current virtual
-** table. So any terms for which all prerequisites are satisfied by
-** mPrereq may be specified as "usable" in all calls to xBestIndex.
+** All the tables in mPrereq must be scanned before the current virtual
+** table. So any terms for which all prerequisites are satisfied by
+** mPrereq may be specified as "usable" in all calls to xBestIndex.
** Conversely, all tables in mUnusable must be scanned after the current
** virtual table, so any terms for which the prerequisites overlap with
** mUnusable should always be configured as "not-usable" for xBestIndex.
@@ -142500,13 +165107,14 @@ static int whereLoopAddVirtual(
WhereInfo *pWInfo; /* WHERE analysis context */
Parse *pParse; /* The parsing context */
WhereClause *pWC; /* The WHERE clause */
- struct SrcList_item *pSrc; /* The FROM clause term to search */
+ SrcItem *pSrc; /* The FROM clause term to search */
sqlite3_index_info *p; /* Object to pass to xBestIndex() */
int nConstraint; /* Number of constraints in p */
int bIn; /* True if plan uses IN(...) operator */
WhereLoop *pNew;
Bitmask mBest; /* Tables used by best possible plan */
u16 mNoOmit;
+ int bRetry = 0; /* True to retry with LIMIT/OFFSET disabled */
assert( (mPrereq & mUnusable)==0 );
pWInfo = pBuilder->pWInfo;
@@ -142515,8 +165123,7 @@ static int whereLoopAddVirtual(
pNew = pBuilder->pNew;
pSrc = &pWInfo->pTabList->a[pNew->iTab];
assert( IsVirtual(pSrc->pTab) );
- p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy,
- &mNoOmit);
+ p = allocateIndexInfo(pWInfo, pWC, mUnusable, pSrc, &mNoOmit);
if( p==0 ) return SQLITE_NOMEM_BKPT;
pNew->rSetup = 0;
pNew->wsFlags = WHERE_VIRTUALTABLE;
@@ -142524,21 +165131,29 @@ static int whereLoopAddVirtual(
pNew->u.vtab.needFree = 0;
nConstraint = p->nConstraint;
if( whereLoopResize(pParse->db, pNew, nConstraint) ){
- sqlite3DbFree(pParse->db, p);
+ freeIndexInfo(pParse->db, p);
return SQLITE_NOMEM_BKPT;
}
/* First call xBestIndex() with all constraints usable. */
WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName));
- WHERETRACE(0x40, (" VirtualOne: all usable\n"));
- rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn);
+ WHERETRACE(0x800, (" VirtualOne: all usable\n"));
+ rc = whereLoopAddVirtualOne(
+ pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry
+ );
+ if( bRetry ){
+ assert( rc==SQLITE_OK );
+ rc = whereLoopAddVirtualOne(
+ pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, 0
+ );
+ }
/* If the call to xBestIndex() with all terms enabled produced a plan
- ** that does not require any source tables (IOW: a plan with mBest==0),
- ** then there is no point in making any further calls to xBestIndex()
- ** since they will all return the same result (if the xBestIndex()
- ** implementation is sane). */
- if( rc==SQLITE_OK && (mBest = (pNew->prereq & ~mPrereq))!=0 ){
+ ** that does not require any source tables (IOW: a plan with mBest==0)
+ ** and does not use an IN(...) operator, then there is no point in making
+ ** any further calls to xBestIndex() since they will all return the same
+ ** result (if the xBestIndex() implementation is sane). */
+ if( rc==SQLITE_OK && ((mBest = (pNew->prereq & ~mPrereq))!=0 || bIn) ){
int seenZero = 0; /* True if a plan with no prereqs seen */
int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */
Bitmask mPrev = 0;
@@ -142547,9 +165162,9 @@ static int whereLoopAddVirtual(
/* If the plan produced by the earlier call uses an IN(...) term, call
** xBestIndex again, this time with IN(...) terms disabled. */
if( bIn ){
- WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n"));
+ WHERETRACE(0x800, (" VirtualOne: all usable w/o IN\n"));
rc = whereLoopAddVirtualOne(
- pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn);
+ pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn, 0);
assert( bIn==0 );
mBestNoIn = pNew->prereq & ~mPrereq;
if( mBestNoIn==0 ){
@@ -142558,7 +165173,7 @@ static int whereLoopAddVirtual(
}
}
- /* Call xBestIndex once for each distinct value of (prereqRight & ~mPrereq)
+ /* Call xBestIndex once for each distinct value of (prereqRight & ~mPrereq)
** in the set of terms that apply to the current virtual table. */
while( rc==SQLITE_OK ){
int i;
@@ -142573,10 +165188,10 @@ static int whereLoopAddVirtual(
mPrev = mNext;
if( mNext==ALLBITS ) break;
if( mNext==mBest || mNext==mBestNoIn ) continue;
- WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n",
+ WHERETRACE(0x800, (" VirtualOne: mPrev=%04llx mNext=%04llx\n",
(sqlite3_uint64)mPrev, (sqlite3_uint64)mNext));
rc = whereLoopAddVirtualOne(
- pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn);
+ pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn, 0);
if( pNew->prereq==mPrereq ){
seenZero = 1;
if( bIn==0 ) seenZeroNoIN = 1;
@@ -142587,9 +165202,9 @@ static int whereLoopAddVirtual(
** that requires no source tables at all (i.e. one guaranteed to be
** usable), make a call here with all source tables disabled */
if( rc==SQLITE_OK && seenZero==0 ){
- WHERETRACE(0x40, (" VirtualOne: all disabled\n"));
+ WHERETRACE(0x800, (" VirtualOne: all disabled\n"));
rc = whereLoopAddVirtualOne(
- pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn);
+ pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn, 0);
if( bIn==0 ) seenZeroNoIN = 1;
}
@@ -142597,14 +165212,14 @@ static int whereLoopAddVirtual(
** that requires no source tables at all and does not use an IN(...)
** operator, make a final call to obtain one here. */
if( rc==SQLITE_OK && seenZeroNoIN==0 ){
- WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n"));
+ WHERETRACE(0x800, (" VirtualOne: all disabled and w/o IN\n"));
rc = whereLoopAddVirtualOne(
- pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn);
+ pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn, 0);
}
}
if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr);
- sqlite3DbFreeNN(pParse->db, p);
+ freeIndexInfo(pParse->db, p);
WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc));
return rc;
}
@@ -142615,8 +165230,8 @@ static int whereLoopAddVirtual(
** btrees or virtual tables.
*/
static int whereLoopAddOr(
- WhereLoopBuilder *pBuilder,
- Bitmask mPrereq,
+ WhereLoopBuilder *pBuilder,
+ Bitmask mPrereq,
Bitmask mUnusable
){
WhereInfo *pWInfo = pBuilder->pWInfo;
@@ -142628,8 +165243,8 @@ static int whereLoopAddOr(
WhereClause tempWC;
WhereLoopBuilder sSubBuild;
WhereOrSet sSum, sCur;
- struct SrcList_item *pItem;
-
+ SrcItem *pItem;
+
pWC = pBuilder->pWC;
pWCEnd = pWC->a + pWC->nTerm;
pNew = pBuilder->pNew;
@@ -142637,21 +165252,23 @@ static int whereLoopAddOr(
pItem = pWInfo->pTabList->a + pNew->iTab;
iCur = pItem->iCursor;
+ /* The multi-index OR optimization does not work for RIGHT and FULL JOIN */
+ if( pItem->fg.jointype & JT_RIGHT ) return SQLITE_OK;
+
for(pTerm=pWC->a; pTerm<pWCEnd && rc==SQLITE_OK; pTerm++){
if( (pTerm->eOperator & WO_OR)!=0
- && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0
+ && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0
){
WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
WhereTerm *pOrTerm;
int once = 1;
int i, j;
-
+
sSubBuild = *pBuilder;
- sSubBuild.pOrderBy = 0;
sSubBuild.pOrSet = &sCur;
- WHERETRACE(0x200, ("Begin processing OR-clause %p\n", pTerm));
+ WHERETRACE(0x400, ("Begin processing OR-clause %p\n", pTerm));
for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
if( (pOrTerm->eOperator & WO_AND)!=0 ){
sSubBuild.pWC = &pOrTerm->u.pAndInfo->wc;
@@ -142660,6 +165277,7 @@ static int whereLoopAddOr(
tempWC.pOuter = pWC;
tempWC.op = TK_AND;
tempWC.nTerm = 1;
+ tempWC.nBase = 1;
tempWC.a = pOrTerm;
sSubBuild.pWC = &tempWC;
}else{
@@ -142667,9 +165285,9 @@ static int whereLoopAddOr(
}
sCur.n = 0;
#ifdef WHERETRACE_ENABLED
- WHERETRACE(0x200, ("OR-term %d of %p has %d subterms:\n",
+ WHERETRACE(0x400, ("OR-term %d of %p has %d subterms:\n",
(int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm));
- if( sqlite3WhereTrace & 0x400 ){
+ if( sqlite3WhereTrace & 0x20000 ){
sqlite3WhereClausePrint(sSubBuild.pWC);
}
#endif
@@ -142684,7 +165302,8 @@ static int whereLoopAddOr(
if( rc==SQLITE_OK ){
rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable);
}
- assert( rc==SQLITE_OK || sCur.n==0 );
+ testcase( rc==SQLITE_NOMEM && sCur.n>0 );
+ testcase( rc==SQLITE_DONE );
if( sCur.n==0 ){
sSum.n = 0;
break;
@@ -142714,8 +165333,8 @@ static int whereLoopAddOr(
/* TUNING: Currently sSum.a[i].rRun is set to the sum of the costs
** of all sub-scans required by the OR-scan. However, due to rounding
** errors, it may be that the cost of the OR-scan is equal to its
- ** most expensive sub-scan. Add the smallest possible penalty
- ** (equivalent to multiplying the cost by 1.07) to ensure that
+ ** most expensive sub-scan. Add the smallest possible penalty
+ ** (equivalent to multiplying the cost by 1.07) to ensure that
** this does not happen. Otherwise, for WHERE clauses such as the
** following where there is an index on "y":
**
@@ -142728,14 +165347,14 @@ static int whereLoopAddOr(
pNew->prereq = sSum.a[i].prereq;
rc = whereLoopInsert(pBuilder, pNew);
}
- WHERETRACE(0x200, ("End processing OR-clause %p\n", pTerm));
+ WHERETRACE(0x400, ("End processing OR-clause %p\n", pTerm));
}
}
return rc;
}
/*
-** Add all WhereLoop objects for all tables
+** Add all WhereLoop objects for all tables
*/
static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
WhereInfo *pWInfo = pBuilder->pWInfo;
@@ -142743,33 +165362,54 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
Bitmask mPrior = 0;
int iTab;
SrcList *pTabList = pWInfo->pTabList;
- struct SrcList_item *pItem;
- struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel];
+ SrcItem *pItem;
+ SrcItem *pEnd = &pTabList->a[pWInfo->nLevel];
sqlite3 *db = pWInfo->pParse->db;
int rc = SQLITE_OK;
+ int bFirstPastRJ = 0;
+ int hasRightJoin = 0;
WhereLoop *pNew;
- u8 priorJointype = 0;
+
/* Loop over the tables in the join, from left to right */
pNew = pBuilder->pNew;
- whereLoopInit(pNew);
+
+ /* Verify that pNew has already been initialized */
+ assert( pNew->nLTerm==0 );
+ assert( pNew->wsFlags==0 );
+ assert( pNew->nLSlot>=ArraySize(pNew->aLTermSpace) );
+ assert( pNew->aLTerm!=0 );
+
pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT;
for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){
Bitmask mUnusable = 0;
pNew->iTab = iTab;
pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR;
pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor);
- if( ((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){
- /* This condition is true when pItem is the FROM clause term on the
- ** right-hand-side of a LEFT or CROSS JOIN. */
- mPrereq = mPrior;
+ if( bFirstPastRJ
+ || (pItem->fg.jointype & (JT_OUTER|JT_CROSS|JT_LTORJ))!=0
+ ){
+ /* Add prerequisites to prevent reordering of FROM clause terms
+ ** across CROSS joins and outer joins. The bFirstPastRJ boolean
+ ** prevents the right operand of a RIGHT JOIN from being swapped with
+ ** other elements even further to the right.
+ **
+ ** The JT_LTORJ case and the hasRightJoin flag work together to
+ ** prevent FROM-clause terms from moving from the right side of
+ ** a LEFT JOIN over to the left side of that join if the LEFT JOIN
+ ** is itself on the left side of a RIGHT JOIN.
+ */
+ if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1;
+ mPrereq |= mPrior;
+ bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0;
+ }else if( !hasRightJoin ){
+ mPrereq = 0;
}
- priorJointype = pItem->fg.jointype;
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pItem->pTab) ){
- struct SrcList_item *p;
+ SrcItem *p;
for(p=&pItem[1]; p<pEnd; p++){
- if( mUnusable || (p->fg.jointype & (JT_LEFT|JT_CROSS)) ){
+ if( mUnusable || (p->fg.jointype & (JT_OUTER|JT_CROSS)) ){
mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor);
}
}
@@ -142802,17 +165442,17 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
** Examine a WherePath (with the addition of the extra WhereLoop of the 6th
** parameters) to see if it outputs rows in the requested ORDER BY
** (or GROUP BY) without requiring a separate sort operation. Return N:
-**
+**
** N>0: N terms of the ORDER BY clause are satisfied
** N==0: No terms of the ORDER BY clause are satisfied
-** N<0: Unknown yet how many terms of ORDER BY might be satisfied.
+** N<0: Unknown yet how many terms of ORDER BY might be satisfied.
**
** Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY is not as
** strict. With GROUP BY and DISTINCT the only requirement is that
** equivalent rows appear immediately adjacent to one another. GROUP BY
** and DISTINCT do not require rows to appear in any particular order as long
** as equivalent rows are grouped together. Thus for GROUP BY and DISTINCT
-** the pOrderBy terms can be matched in any order. With ORDER BY, the
+** the pOrderBy terms can be matched in any order. With ORDER BY, the
** pOrderBy terms must be matched in strict left-to-right order.
*/
static i8 wherePathSatisfiesOrderBy(
@@ -142862,7 +165502,7 @@ static i8 wherePathSatisfiesOrderBy(
** row of the WhereLoop. Every one-row WhereLoop is automatically
** order-distinct. A WhereLoop that has no columns in the ORDER BY clause
** is not order-distinct. To be order-distinct is not quite the same as being
- ** UNIQUE since a UNIQUE column or index can have multiple rows that
+ ** UNIQUE since a UNIQUE column or index can have multiple rows that
** are NULL and NULL values are equivalent for the purpose of order-distinct.
** To be order-distinct, the columns must be UNIQUE and NOT NULL.
**
@@ -142882,7 +165522,9 @@ static i8 wherePathSatisfiesOrderBy(
orderDistinctMask = 0;
ready = 0;
eqOpMask = WO_EQ | WO_IS | WO_ISNULL;
- if( wctrlFlags & WHERE_ORDERBY_LIMIT ) eqOpMask |= WO_IN;
+ if( wctrlFlags & (WHERE_ORDERBY_LIMIT|WHERE_ORDERBY_MAX|WHERE_ORDERBY_MIN) ){
+ eqOpMask |= WO_IN;
+ }
for(iLoop=0; isOrderDistinct && obSat<obDone && iLoop<=nLoop; iLoop++){
if( iLoop>0 ) ready |= pLoop->maskSelf;
if( iLoop<nLoop ){
@@ -142892,10 +165534,14 @@ static i8 wherePathSatisfiesOrderBy(
pLoop = pLast;
}
if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
- if( pLoop->u.vtab.isOrdered ) obSat = obDone;
+ if( pLoop->u.vtab.isOrdered
+ && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY)
+ ){
+ obSat = obDone;
+ }
break;
- }else{
- pLoop->u.btree.nIdxCol = 0;
+ }else if( wctrlFlags & WHERE_DISTINCTBY ){
+ pLoop->u.btree.nDistinctCol = 0;
}
iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
@@ -142906,23 +165552,28 @@ static i8 wherePathSatisfiesOrderBy(
*/
for(i=0; i<nOrderBy; i++){
if( MASKBIT(i) & obSat ) continue;
- pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
- if( pOBExpr->op!=TK_COLUMN ) continue;
+ pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
+ if( NEVER(pOBExpr==0) ) continue;
+ if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) continue;
if( pOBExpr->iTable!=iCur ) continue;
pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
~ready, eqOpMask, 0);
if( pTerm==0 ) continue;
if( pTerm->eOperator==WO_IN ){
- /* IN terms are only valid for sorting in the ORDER BY LIMIT
+ /* IN terms are only valid for sorting in the ORDER BY LIMIT
** optimization, and then only if they are actually used
** by the query plan */
- assert( wctrlFlags & WHERE_ORDERBY_LIMIT );
+ assert( wctrlFlags &
+ (WHERE_ORDERBY_LIMIT|WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX) );
for(j=0; j<pLoop->nLTerm && pTerm!=pLoop->aLTerm[j]; j++){}
if( j>=pLoop->nLTerm ) continue;
}
if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0 ){
- if( sqlite3ExprCollSeqMatch(pWInfo->pParse,
- pOrderBy->a[i].pExpr, pTerm->pExpr)==0 ){
+ Parse *pParse = pWInfo->pParse;
+ CollSeq *pColl1 = sqlite3ExprNNCollSeq(pParse, pOrderBy->a[i].pExpr);
+ CollSeq *pColl2 = sqlite3ExprCompareCollSeq(pParse, pTerm->pExpr);
+ assert( pColl1 );
+ if( pColl2==0 || sqlite3StrICmp(pColl1->zName, pColl2->zName) ){
continue;
}
testcase( pTerm->pExpr->op==TK_IS );
@@ -142943,7 +165594,12 @@ static i8 wherePathSatisfiesOrderBy(
assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) );
assert( pIndex->aiColumn[nColumn-1]==XN_ROWID
|| !HasRowid(pIndex->pTable));
- isOrderDistinct = IsUniqueIndex(pIndex);
+ /* All relevant terms of the index must also be non-NULL in order
+ ** for isOrderDistinct to be true. So the isOrderDistint value
+ ** computed here might be a false positive. Corrections will be
+ ** made at tag-20210426-1 below */
+ isOrderDistinct = IsUniqueIndex(pIndex)
+ && (pLoop->wsFlags & WHERE_SKIPSCAN)==0;
}
/* Loop through all columns of the index and deal with the ones
@@ -142954,26 +165610,32 @@ static i8 wherePathSatisfiesOrderBy(
for(j=0; j<nColumn; j++){
u8 bOnce = 1; /* True to run the ORDER BY search loop */
- assert( j>=pLoop->u.btree.nEq
+ assert( j>=pLoop->u.btree.nEq
|| (pLoop->aLTerm[j]==0)==(j<pLoop->nSkip)
);
if( j<pLoop->u.btree.nEq && j>=pLoop->nSkip ){
u16 eOp = pLoop->aLTerm[j]->eOperator;
/* Skip over == and IS and ISNULL terms. (Also skip IN terms when
- ** doing WHERE_ORDERBY_LIMIT processing).
+ ** doing WHERE_ORDERBY_LIMIT processing). Except, IS and ISNULL
+ ** terms imply that the index is not UNIQUE NOT NULL in which case
+ ** the loop need to be marked as not order-distinct because it can
+ ** have repeated NULL rows.
**
- ** If the current term is a column of an ((?,?) IN (SELECT...))
+ ** If the current term is a column of an ((?,?) IN (SELECT...))
** expression for which the SELECT returns more than one column,
** check that it is the only column used by this loop. Otherwise,
** if it is one of two or more, none of the columns can be
- ** considered to match an ORDER BY term. */
+ ** considered to match an ORDER BY term.
+ */
if( (eOp & eqOpMask)!=0 ){
- if( eOp & WO_ISNULL ){
+ if( eOp & (WO_ISNULL|WO_IS) ){
+ testcase( eOp & WO_ISNULL );
+ testcase( eOp & WO_IS );
testcase( isOrderDistinct );
isOrderDistinct = 0;
}
- continue;
+ continue;
}else if( ALWAYS(eOp & WO_IN) ){
/* ALWAYS() justification: eOp is an equality operator due to the
** j<pLoop->u.btree.nEq constraint above. Any equality other
@@ -142995,7 +165657,7 @@ static i8 wherePathSatisfiesOrderBy(
*/
if( pIndex ){
iColumn = pIndex->aiColumn[j];
- revIdx = pIndex->aSortOrder[j];
+ revIdx = pIndex->aSortOrder[j] & KEYINFO_ORDER_DESC;
if( iColumn==pIndex->pTable->iPKey ) iColumn = XN_ROWID;
}else{
iColumn = XN_ROWID;
@@ -143003,33 +165665,38 @@ static i8 wherePathSatisfiesOrderBy(
}
/* An unconstrained column that might be NULL means that this
- ** WhereLoop is not well-ordered
+ ** WhereLoop is not well-ordered. tag-20210426-1
*/
- if( isOrderDistinct
- && iColumn>=0
- && j>=pLoop->u.btree.nEq
- && pIndex->pTable->aCol[iColumn].notNull==0
- ){
- isOrderDistinct = 0;
+ if( isOrderDistinct ){
+ if( iColumn>=0
+ && j>=pLoop->u.btree.nEq
+ && pIndex->pTable->aCol[iColumn].notNull==0
+ ){
+ isOrderDistinct = 0;
+ }
+ if( iColumn==XN_EXPR ){
+ isOrderDistinct = 0;
+ }
}
/* Find the ORDER BY term that corresponds to the j-th column
- ** of the index and mark that ORDER BY term off
+ ** of the index and mark that ORDER BY term off
*/
isMatch = 0;
for(i=0; bOnce && i<nOrderBy; i++){
if( MASKBIT(i) & obSat ) continue;
- pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
+ pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
testcase( wctrlFlags & WHERE_GROUPBY );
testcase( wctrlFlags & WHERE_DISTINCTBY );
+ if( NEVER(pOBExpr==0) ) continue;
if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
if( iColumn>=XN_ROWID ){
- if( pOBExpr->op!=TK_COLUMN ) continue;
+ if( pOBExpr->op!=TK_COLUMN && pOBExpr->op!=TK_AGG_COLUMN ) continue;
if( pOBExpr->iTable!=iCur ) continue;
if( pOBExpr->iColumn!=iColumn ) continue;
}else{
- Expr *pIdxExpr = pIndex->aColExpr->a[j].pExpr;
- if( sqlite3ExprCompareSkip(pOBExpr, pIdxExpr, iCur) ){
+ Expr *pIxExpr = pIndex->aColExpr->a[j].pExpr;
+ if( sqlite3ExprCompareSkip(pOBExpr, pIxExpr, iCur) ){
continue;
}
}
@@ -143037,7 +165704,9 @@ static i8 wherePathSatisfiesOrderBy(
pColl = sqlite3ExprNNCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) continue;
}
- pLoop->u.btree.nIdxCol = j+1;
+ if( wctrlFlags & WHERE_DISTINCTBY ){
+ pLoop->u.btree.nDistinctCol = j+1;
+ }
isMatch = 1;
break;
}
@@ -143045,13 +165714,24 @@ static i8 wherePathSatisfiesOrderBy(
/* Make sure the sort order is compatible in an ORDER BY clause.
** Sort order is irrelevant for a GROUP BY clause. */
if( revSet ){
- if( (rev ^ revIdx)!=pOrderBy->a[i].sortOrder ) isMatch = 0;
+ if( (rev ^ revIdx)
+ != (pOrderBy->a[i].fg.sortFlags&KEYINFO_ORDER_DESC)
+ ){
+ isMatch = 0;
+ }
}else{
- rev = revIdx ^ pOrderBy->a[i].sortOrder;
+ rev = revIdx ^ (pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC);
if( rev ) *pRevMask |= MASKBIT(iLoop);
revSet = 1;
}
}
+ if( isMatch && (pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL) ){
+ if( j==pLoop->u.btree.nEq ){
+ pLoop->wsFlags |= WHERE_BIGNULL_SORT;
+ }else{
+ isMatch = 0;
+ }
+ }
if( isMatch ){
if( iColumn==XN_ROWID ){
testcase( distinctColumns==0 );
@@ -143092,7 +165772,7 @@ static i8 wherePathSatisfiesOrderBy(
if( obSat==obDone ) return (i8)nOrderBy;
if( !isOrderDistinct ){
for(i=nOrderBy-1; i>0; i--){
- Bitmask m = MASKBIT(i) - 1;
+ Bitmask m = ALWAYS(i<BMS) ? MASKBIT(i) - 1 : 0;
if( (obSat&m)==m ) return i;
}
return 0;
@@ -143125,7 +165805,7 @@ static i8 wherePathSatisfiesOrderBy(
** SELECT * FROM t1 GROUP BY y,x ORDER BY y,x; -- IsSorted()==0
*/
SQLITE_PRIVATE int sqlite3WhereIsSorted(WhereInfo *pWInfo){
- assert( pWInfo->wctrlFlags & WHERE_GROUPBY );
+ assert( pWInfo->wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY) );
assert( pWInfo->wctrlFlags & WHERE_SORTBYGROUP );
return pWInfo->sorted;
}
@@ -143143,38 +165823,65 @@ static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){
#endif
/*
-** Return the cost of sorting nRow rows, assuming that the keys have
+** Return the cost of sorting nRow rows, assuming that the keys have
** nOrderby columns and that the first nSorted columns are already in
** order.
*/
static LogEst whereSortingCost(
- WhereInfo *pWInfo,
- LogEst nRow,
- int nOrderBy,
- int nSorted
+ WhereInfo *pWInfo, /* Query planning context */
+ LogEst nRow, /* Estimated number of rows to sort */
+ int nOrderBy, /* Number of ORDER BY clause terms */
+ int nSorted /* Number of initial ORDER BY terms naturally in order */
){
- /* TUNING: Estimated cost of a full external sort, where N is
+ /* Estimated cost of a full external sort, where N is
** the number of rows to sort is:
**
- ** cost = (3.0 * N * log(N)).
- **
- ** Or, if the order-by clause has X terms but only the last Y
- ** terms are out of order, then block-sorting will reduce the
+ ** cost = (K * N * log(N)).
+ **
+ ** Or, if the order-by clause has X terms but only the last Y
+ ** terms are out of order, then block-sorting will reduce the
** sorting cost to:
**
- ** cost = (3.0 * N * log(N)) * (Y/X)
+ ** cost = (K * N * log(N)) * (Y/X)
+ **
+ ** The constant K is at least 2.0 but will be larger if there are a
+ ** large number of columns to be sorted, as the sorting time is
+ ** proportional to the amount of content to be sorted. The algorithm
+ ** does not currently distinguish between fat columns (BLOBs and TEXTs)
+ ** and skinny columns (INTs). It just uses the number of columns as
+ ** an approximation for the row width.
**
- ** The (Y/X) term is implemented using stack variable rScale
- ** below. */
- LogEst rScale, rSortCost;
- assert( nOrderBy>0 && 66==sqlite3LogEst(100) );
- rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66;
- rSortCost = nRow + rScale + 16;
+ ** And extra factor of 2.0 or 3.0 is added to the sorting cost if the sort
+ ** is built using OP_IdxInsert and OP_Sort rather than with OP_SorterInsert.
+ */
+ LogEst rSortCost, nCol;
+ assert( pWInfo->pSelect!=0 );
+ assert( pWInfo->pSelect->pEList!=0 );
+ /* TUNING: sorting cost proportional to the number of output columns: */
+ nCol = sqlite3LogEst((pWInfo->pSelect->pEList->nExpr+59)/30);
+ rSortCost = nRow + nCol;
+ if( nSorted>0 ){
+ /* Scale the result by (Y/X) */
+ rSortCost += sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66;
+ }
/* Multiple by log(M) where M is the number of output rows.
- ** Use the LIMIT for M if it is smaller */
- if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){
- nRow = pWInfo->iLimit;
+ ** Use the LIMIT for M if it is smaller. Or if this sort is for
+ ** a DISTINCT operator, M will be the number of distinct output
+ ** rows, so fudge it downwards a bit.
+ */
+ if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 ){
+ rSortCost += 10; /* TUNING: Extra 2.0x if using LIMIT */
+ if( nSorted!=0 ){
+ rSortCost += 6; /* TUNING: Extra 1.5x if also using partial sort */
+ }
+ if( pWInfo->iLimit<nRow ){
+ nRow = pWInfo->iLimit;
+ }
+ }else if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) ){
+ /* TUNING: In the sort for a DISTINCT operator, assume that the DISTINCT
+ ** reduces the number of output rows by a factor of 2 */
+ if( nRow>10 ){ nRow -= 10; assert( 10==sqlite3LogEst(2) ); }
}
rSortCost += estLog(nRow);
return rSortCost;
@@ -143196,7 +165903,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
int mxChoice; /* Maximum number of simultaneous paths tracked */
int nLoop; /* Number of terms in the join */
Parse *pParse; /* Parsing context */
- sqlite3 *db; /* The database connection */
int iLoop; /* Loop counter over the terms of the join */
int ii, jj; /* Loop counters */
int mxI = 0; /* Index of next entry to replace */
@@ -143215,14 +165921,14 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
int nSpace; /* Bytes of space allocated at pSpace */
pParse = pWInfo->pParse;
- db = pParse->db;
nLoop = pWInfo->nLevel;
/* TUNING: For simple queries, only the best path is tracked.
** For 2-way joins, the 5 best paths are followed.
** For joins of 3 or more tables, track the 10 best paths */
mxChoice = (nLoop<=1) ? 1 : (nLoop==2 ? 5 : 10);
assert( nLoop<=pWInfo->pTabList->nSrc );
- WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d)\n", nRowEst));
+ WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d, nQueryLoop=%d)\n",
+ nRowEst, pParse->nQueryLoop));
/* If nRowEst is zero and there is an ORDER BY clause, ignore it. In this
** case the purpose of this call is to estimate the number of rows returned
@@ -143238,7 +165944,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
/* Allocate and initialize space for aTo, aFrom and aSortCost[] */
nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2;
nSpace += sizeof(LogEst) * nOrderBy;
- pSpace = sqlite3DbMallocRawNN(db, nSpace);
+ pSpace = sqlite3StackAllocRawNN(pParse->db, nSpace);
if( pSpace==0 ) return SQLITE_NOMEM_BKPT;
aTo = (WherePath*)pSpace;
aFrom = aTo+mxChoice;
@@ -143252,7 +165958,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
** space for the aSortCost[] array. Each element of the aSortCost array
** is either zero - meaning it has not yet been initialized - or the
** cost of sorting nRowEst rows of data where the first X terms of
- ** the ORDER BY clause are already in order, where X is the array
+ ** the ORDER BY clause are already in order, where X is the array
** index. */
aSortCost = (LogEst*)pX;
memset(aSortCost, 0, sizeof(LogEst) * nOrderBy);
@@ -143273,7 +165979,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
** in this case the query may return a maximum of one row, the results
** are already in the requested order. Set isOrdered to nOrderBy to
** indicate this. Or, if nLoop is greater than zero, set isOrdered to
- ** -1, indicating that the result set may or may not be ordered,
+ ** -1, indicating that the result set may or may not be ordered,
** depending on the loops added to the current plan. */
aFrom[0].isOrdered = nLoop>0 ? -1 : nOrderBy;
}
@@ -143288,9 +165994,9 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
LogEst nOut; /* Rows visited by (pFrom+pWLoop) */
LogEst rCost; /* Cost of path (pFrom+pWLoop) */
LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */
- i8 isOrdered = pFrom->isOrdered; /* isOrdered for (pFrom+pWLoop) */
+ i8 isOrdered; /* isOrdered for (pFrom+pWLoop) */
Bitmask maskNew; /* Mask of src visited by (..) */
- Bitmask revMask = 0; /* Mask of rev-order loops for (..) */
+ Bitmask revMask; /* Mask of rev-order loops for (..) */
if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue;
@@ -143303,13 +166009,15 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
continue;
}
- /* At this point, pWLoop is a candidate to be the next loop.
+ /* At this point, pWLoop is a candidate to be the next loop.
** Compute its cost */
rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow);
rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted);
nOut = pFrom->nRow + pWLoop->nOut;
maskNew = pFrom->maskLoop | pWLoop->maskSelf;
+ isOrdered = pFrom->isOrdered;
if( isOrdered<0 ){
+ revMask = 0;
isOrdered = wherePathSatisfiesOrderBy(pWInfo,
pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags,
iLoop, pWLoop, &revMask);
@@ -143322,15 +166030,15 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
pWInfo, nRowEst, nOrderBy, isOrdered
);
}
- /* TUNING: Add a small extra penalty (5) to sorting as an
- ** extra encouragment to the query planner to select a plan
+ /* TUNING: Add a small extra penalty (3) to sorting as an
+ ** extra encouragement to the query planner to select a plan
** where the rows emerge in the correct order without any sorting
** required. */
- rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 5;
+ rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 3;
WHERETRACE(0x002,
("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n",
- aSortCost[isOrdered], (nOrderBy-isOrdered), nOrderBy,
+ aSortCost[isOrdered], (nOrderBy-isOrdered), nOrderBy,
rUnsorted, rCost));
}else{
rCost = rUnsorted;
@@ -143395,11 +166103,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
** same set of loops and has the same isOrdered setting as the
** candidate path. Check to see if the candidate should replace
** pTo or if the candidate should be skipped.
- **
+ **
** The conditional is an expanded vector comparison equivalent to:
** (pTo->rCost,pTo->nRow,pTo->rUnsorted) <= (rCost,nOut,rUnsorted)
*/
- if( pTo->rCost<rCost
+ if( pTo->rCost<rCost
|| (pTo->rCost==rCost
&& (pTo->nRow<nOut
|| (pTo->nRow==nOut && pTo->rUnsorted<=rUnsorted)
@@ -143450,8 +166158,8 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
mxCost = aTo[0].rCost;
mxUnsorted = aTo[0].nRow;
for(jj=1, pTo=&aTo[1]; jj<mxChoice; jj++, pTo++){
- if( pTo->rCost>mxCost
- || (pTo->rCost==mxCost && pTo->rUnsorted>mxUnsorted)
+ if( pTo->rCost>mxCost
+ || (pTo->rCost==mxCost && pTo->rUnsorted>mxUnsorted)
){
mxCost = pTo->rCost;
mxUnsorted = pTo->rUnsorted;
@@ -143487,10 +166195,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( nFrom==0 ){
sqlite3ErrorMsg(pParse, "no query solution");
- sqlite3DbFreeNN(db, pSpace);
+ sqlite3StackFreeNN(pParse->db, pSpace);
return SQLITE_ERROR;
}
-
+
/* Find the lowest cost path. pFrom will be left pointing to that path */
pFrom = aFrom;
for(ii=1; ii<nFrom; ii++){
@@ -143518,18 +166226,22 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
}
pWInfo->bOrderedInnerLoop = 0;
if( pWInfo->pOrderBy ){
+ pWInfo->nOBSat = pFrom->isOrdered;
if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){
if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
}
+ if( pWInfo->pSelect->pOrderBy
+ && pWInfo->nOBSat > pWInfo->pSelect->pOrderBy->nExpr ){
+ pWInfo->nOBSat = pWInfo->pSelect->pOrderBy->nExpr;
+ }
}else{
- pWInfo->nOBSat = pFrom->isOrdered;
pWInfo->revMask = pFrom->revLoop;
if( pWInfo->nOBSat<=0 ){
pWInfo->nOBSat = 0;
if( nLoop>0 ){
u32 wsFlags = pFrom->aLoop[nLoop-1]->wsFlags;
- if( (wsFlags & WHERE_ONEROW)==0
+ if( (wsFlags & WHERE_ONEROW)==0
&& (wsFlags&(WHERE_IPK|WHERE_COLUMN_IN))!=(WHERE_IPK|WHERE_COLUMN_IN)
){
Bitmask m = 0;
@@ -143543,13 +166255,18 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
}
}
}
+ }else if( nLoop
+ && pWInfo->nOBSat==1
+ && (pWInfo->wctrlFlags & (WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX))!=0
+ ){
+ pWInfo->bOrderedInnerLoop = 1;
}
}
if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)
&& pWInfo->nOBSat==pWInfo->pOrderBy->nExpr && nLoop>0
){
Bitmask revMask = 0;
- int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy,
+ int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy,
pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], &revMask
);
assert( pWInfo->sorted==0 );
@@ -143564,7 +166281,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
pWInfo->nRowOut = pFrom->nRow;
/* Free temporary memory and return success */
- sqlite3DbFreeNN(db, pSpace);
+ sqlite3StackFreeNN(pParse->db, pSpace);
return SQLITE_OK;
}
@@ -143576,12 +166293,12 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
** times for the common case.
**
** Return non-zero on success, if this query can be handled by this
-** no-frills query planner. Return zero if this query needs the
+** no-frills query planner. Return zero if this query needs the
** general-purpose query planner.
*/
static int whereShortCut(WhereLoopBuilder *pBuilder){
WhereInfo *pWInfo;
- struct SrcList_item *pItem;
+ SrcItem *pItem;
WhereClause *pWC;
WhereTerm *pTerm;
WhereLoop *pLoop;
@@ -143589,6 +166306,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
int j;
Table *pTab;
Index *pIdx;
+ WhereScan scan;
pWInfo = pBuilder->pWInfo;
if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0;
@@ -143596,13 +166314,18 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
pItem = pWInfo->pTabList->a;
pTab = pItem->pTab;
if( IsVirtual(pTab) ) return 0;
- if( pItem->fg.isIndexedBy ) return 0;
+ if( pItem->fg.isIndexedBy || pItem->fg.notIndexed ){
+ testcase( pItem->fg.isIndexedBy );
+ testcase( pItem->fg.notIndexed );
+ return 0;
+ }
iCur = pItem->iCursor;
pWC = &pWInfo->sWC;
pLoop = pBuilder->pNew;
pLoop->wsFlags = 0;
pLoop->nSkip = 0;
- pTerm = sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0);
+ pTerm = whereScanInit(&scan, pWC, iCur, -1, WO_EQ|WO_IS, 0);
+ while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan);
if( pTerm ){
testcase( pTerm->eOperator & WO_IS );
pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW;
@@ -143616,12 +166339,13 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
int opMask;
assert( pLoop->aLTermSpace==pLoop->aLTerm );
if( !IsUniqueIndex(pIdx)
- || pIdx->pPartIdxWhere!=0
- || pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace)
+ || pIdx->pPartIdxWhere!=0
+ || pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace)
) continue;
opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ;
for(j=0; j<pIdx->nKeyCol; j++){
- pTerm = sqlite3WhereFindTerm(pWC, iCur, j, 0, opMask, pIdx);
+ pTerm = whereScanInit(&scan, pWC, iCur, j, opMask, pIdx);
+ while( pTerm && pTerm->prereqRight ) pTerm = whereScanNext(&scan);
if( pTerm==0 ) break;
testcase( pTerm->eOperator & WO_IS );
pLoop->aLTerm[j] = pTerm;
@@ -143650,9 +166374,15 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
}
+ if( scan.iEquiv>1 ) pLoop->wsFlags |= WHERE_TRANSCONS;
#ifdef SQLITE_DEBUG
pLoop->cId = '0';
#endif
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x02 ){
+ sqlite3DebugPrintf("whereShortCut() used to compute solution\n");
+ }
+#endif
return 1;
}
return 0;
@@ -143670,8 +166400,8 @@ static int exprNodeIsDeterministic(Walker *pWalker, Expr *pExpr){
}
/*
-** Return true if the expression contains no non-deterministic SQL
-** functions. Do not consider non-deterministic SQL functions that are
+** Return true if the expression contains no non-deterministic SQL
+** functions. Do not consider non-deterministic SQL functions that are
** part of sub-select statements.
*/
static int exprIsDeterministic(Expr *p){
@@ -143684,6 +166414,289 @@ static int exprIsDeterministic(Expr *p){
return w.eCode;
}
+
+#ifdef WHERETRACE_ENABLED
+/*
+** Display all WhereLoops in pWInfo
+*/
+static void showAllWhereLoops(WhereInfo *pWInfo, WhereClause *pWC){
+ if( sqlite3WhereTrace ){ /* Display all of the WhereLoop objects */
+ WhereLoop *p;
+ int i;
+ static const char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz"
+ "ABCDEFGHIJKLMNOPQRSTUVWYXZ";
+ for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){
+ p->cId = zLabel[i%(sizeof(zLabel)-1)];
+ sqlite3WhereLoopPrint(p, pWC);
+ }
+ }
+}
+# define WHERETRACE_ALL_LOOPS(W,C) showAllWhereLoops(W,C)
+#else
+# define WHERETRACE_ALL_LOOPS(W,C)
+#endif
+
+/* Attempt to omit tables from a join that do not affect the result.
+** For a table to not affect the result, the following must be true:
+**
+** 1) The query must not be an aggregate.
+** 2) The table must be the RHS of a LEFT JOIN.
+** 3) Either the query must be DISTINCT, or else the ON or USING clause
+** must contain a constraint that limits the scan of the table to
+** at most a single row.
+** 4) The table must not be referenced by any part of the query apart
+** from its own USING or ON clause.
+** 5) The table must not have an inner-join ON or USING clause if there is
+** a RIGHT JOIN anywhere in the query. Otherwise the ON/USING clause
+** might move from the right side to the left side of the RIGHT JOIN.
+** Note: Due to (2), this condition can only arise if the table is
+** the right-most table of a subquery that was flattened into the
+** main query and that subquery was the right-hand operand of an
+** inner join that held an ON or USING clause.
+**
+** For example, given:
+**
+** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1);
+** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2);
+** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3);
+**
+** then table t2 can be omitted from the following:
+**
+** SELECT v1, v3 FROM t1
+** LEFT JOIN t2 ON (t1.ipk=t2.ipk)
+** LEFT JOIN t3 ON (t1.ipk=t3.ipk)
+**
+** or from:
+**
+** SELECT DISTINCT v1, v3 FROM t1
+** LEFT JOIN t2
+** LEFT JOIN t3 ON (t1.ipk=t3.ipk)
+*/
+static SQLITE_NOINLINE Bitmask whereOmitNoopJoin(
+ WhereInfo *pWInfo,
+ Bitmask notReady
+){
+ int i;
+ Bitmask tabUsed;
+ int hasRightJoin;
+
+ /* Preconditions checked by the caller */
+ assert( pWInfo->nLevel>=2 );
+ assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_OmitNoopJoin) );
+
+ /* These two preconditions checked by the caller combine to guarantee
+ ** condition (1) of the header comment */
+ assert( pWInfo->pResultSet!=0 );
+ assert( 0==(pWInfo->wctrlFlags & WHERE_AGG_DISTINCT) );
+
+ tabUsed = sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pResultSet);
+ if( pWInfo->pOrderBy ){
+ tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy);
+ }
+ hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0;
+ for(i=pWInfo->nLevel-1; i>=1; i--){
+ WhereTerm *pTerm, *pEnd;
+ SrcItem *pItem;
+ WhereLoop *pLoop;
+ pLoop = pWInfo->a[i].pWLoop;
+ pItem = &pWInfo->pTabList->a[pLoop->iTab];
+ if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue;
+ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)==0
+ && (pLoop->wsFlags & WHERE_ONEROW)==0
+ ){
+ continue;
+ }
+ if( (tabUsed & pLoop->maskSelf)!=0 ) continue;
+ pEnd = pWInfo->sWC.a + pWInfo->sWC.nTerm;
+ for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){
+ if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
+ if( !ExprHasProperty(pTerm->pExpr, EP_OuterON)
+ || pTerm->pExpr->w.iJoin!=pItem->iCursor
+ ){
+ break;
+ }
+ }
+ if( hasRightJoin
+ && ExprHasProperty(pTerm->pExpr, EP_InnerON)
+ && pTerm->pExpr->w.iJoin==pItem->iCursor
+ ){
+ break; /* restriction (5) */
+ }
+ }
+ if( pTerm<pEnd ) continue;
+ WHERETRACE(0xffffffff, ("-> drop loop %c not used\n", pLoop->cId));
+ notReady &= ~pLoop->maskSelf;
+ for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){
+ if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
+ pTerm->wtFlags |= TERM_CODED;
+ }
+ }
+ if( i!=pWInfo->nLevel-1 ){
+ int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel);
+ memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte);
+ }
+ pWInfo->nLevel--;
+ assert( pWInfo->nLevel>0 );
+ }
+ return notReady;
+}
+
+/*
+** Check to see if there are any SEARCH loops that might benefit from
+** using a Bloom filter. Consider a Bloom filter if:
+**
+** (1) The SEARCH happens more than N times where N is the number
+** of rows in the table that is being considered for the Bloom
+** filter.
+** (2) Some searches are expected to find zero rows. (This is determined
+** by the WHERE_SELFCULL flag on the term.)
+** (3) Bloom-filter processing is not disabled. (Checked by the
+** caller.)
+** (4) The size of the table being searched is known by ANALYZE.
+**
+** This block of code merely checks to see if a Bloom filter would be
+** appropriate, and if so sets the WHERE_BLOOMFILTER flag on the
+** WhereLoop. The implementation of the Bloom filter comes further
+** down where the code for each WhereLoop is generated.
+*/
+static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
+ const WhereInfo *pWInfo
+){
+ int i;
+ LogEst nSearch = 0;
+
+ assert( pWInfo->nLevel>=2 );
+ assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) );
+ for(i=0; i<pWInfo->nLevel; i++){
+ WhereLoop *pLoop = pWInfo->a[i].pWLoop;
+ const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ);
+ SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab];
+ Table *pTab = pItem->pTab;
+ if( (pTab->tabFlags & TF_HasStat1)==0 ) break;
+ pTab->tabFlags |= TF_StatsUsed;
+ if( i>=1
+ && (pLoop->wsFlags & reqFlags)==reqFlags
+ /* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */
+ && ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0)
+ ){
+ if( nSearch > pTab->nRowLogEst ){
+ testcase( pItem->fg.jointype & JT_LEFT );
+ pLoop->wsFlags |= WHERE_BLOOMFILTER;
+ pLoop->wsFlags &= ~WHERE_IDX_ONLY;
+ WHERETRACE(0xffffffff, (
+ "-> use Bloom-filter on loop %c because there are ~%.1e "
+ "lookups into %s which has only ~%.1e rows\n",
+ pLoop->cId, (double)sqlite3LogEstToInt(nSearch), pTab->zName,
+ (double)sqlite3LogEstToInt(pTab->nRowLogEst)));
+ }
+ }
+ nSearch += pLoop->nOut;
+ }
+}
+
+/*
+** The index pIdx is used by a query and contains one or more expressions.
+** In other words pIdx is an index on an expression. iIdxCur is the cursor
+** number for the index and iDataCur is the cursor number for the corresponding
+** table.
+**
+** This routine adds IndexedExpr entries to the Parse->pIdxEpr field for
+** each of the expressions in the index so that the expression code generator
+** will know to replace occurrences of the indexed expression with
+** references to the corresponding column of the index.
+*/
+static SQLITE_NOINLINE void whereAddIndexedExpr(
+ Parse *pParse, /* Add IndexedExpr entries to pParse->pIdxEpr */
+ Index *pIdx, /* The index-on-expression that contains the expressions */
+ int iIdxCur, /* Cursor number for pIdx */
+ SrcItem *pTabItem /* The FROM clause entry for the table */
+){
+ int i;
+ IndexedExpr *p;
+ Table *pTab;
+ assert( pIdx->bHasExpr );
+ pTab = pIdx->pTable;
+ for(i=0; i<pIdx->nColumn; i++){
+ Expr *pExpr;
+ int j = pIdx->aiColumn[i];
+ int bMaybeNullRow;
+ if( j==XN_EXPR ){
+ pExpr = pIdx->aColExpr->a[i].pExpr;
+ testcase( pTabItem->fg.jointype & JT_LEFT );
+ testcase( pTabItem->fg.jointype & JT_RIGHT );
+ testcase( pTabItem->fg.jointype & JT_LTORJ );
+ bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0;
+ }else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){
+ pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]);
+ bMaybeNullRow = 0;
+ }else{
+ continue;
+ }
+ if( sqlite3ExprIsConstant(pExpr) ) continue;
+ if( pExpr->op==TK_FUNCTION ){
+ /* Functions that might set a subtype should not be replaced by the
+ ** value taken from an expression index since the index omits the
+ ** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */
+ int n;
+ FuncDef *pDef;
+ sqlite3 *db = pParse->db;
+ assert( ExprUseXList(pExpr) );
+ n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
+ if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){
+ continue;
+ }
+ }
+ p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr));
+ if( p==0 ) break;
+ p->pIENext = pParse->pIdxEpr;
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x200 ){
+ sqlite3DebugPrintf("New pParse->pIdxEpr term {%d,%d}\n", iIdxCur, i);
+ if( sqlite3WhereTrace & 0x5000 ) sqlite3ShowExpr(pExpr);
+ }
+#endif
+ p->pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
+ p->iDataCur = pTabItem->iCursor;
+ p->iIdxCur = iIdxCur;
+ p->iIdxCol = i;
+ p->bMaybeNullRow = bMaybeNullRow;
+ if( sqlite3IndexAffinityStr(pParse->db, pIdx) ){
+ p->aff = pIdx->zColAff[i];
+ }
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ p->zIdxName = pIdx->zName;
+#endif
+ pParse->pIdxEpr = p;
+ if( p->pIENext==0 ){
+ void *pArg = (void*)&pParse->pIdxEpr;
+ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg);
+ }
+ }
+}
+
+/*
+** Set the reverse-scan order mask to one for all tables in the query
+** with the exception of MATERIALIZED common table expressions that have
+** their own internal ORDER BY clauses.
+**
+** This implements the PRAGMA reverse_unordered_selects=ON setting.
+** (Also SQLITE_DBCONFIG_REVERSE_SCANORDER).
+*/
+static SQLITE_NOINLINE void whereReverseScanOrder(WhereInfo *pWInfo){
+ int ii;
+ for(ii=0; ii<pWInfo->pTabList->nSrc; ii++){
+ SrcItem *pItem = &pWInfo->pTabList->a[ii];
+ if( !pItem->fg.isCte
+ || pItem->u2.pCteUse->eM10d!=M10d_Yes
+ || NEVER(pItem->pSelect==0)
+ || pItem->pSelect->pOrderBy==0
+ ){
+ pWInfo->revMask |= MASKBIT(ii);
+ }
+ }
+}
+
/*
** Generate the beginning of the loop used for WHERE clause processing.
** The return value is a pointer to an opaque structure that contains
@@ -143742,7 +166755,7 @@ static int exprIsDeterministic(Expr *p){
**
** OUTER JOINS
**
-** An outer join of tables t1 and t2 is conceptally coded as follows:
+** An outer join of tables t1 and t2 is conceptually coded as follows:
**
** foreach row1 in t1 do
** flag = 0
@@ -143764,7 +166777,7 @@ static int exprIsDeterministic(Expr *p){
** if there is one. If there is no ORDER BY clause or if this routine
** is called from an UPDATE or DELETE statement, then pOrderBy is NULL.
**
-** The iIdxCur parameter is the cursor number of an index. If
+** The iIdxCur parameter is the cursor number of an index. If
** WHERE_OR_SUBCLAUSE is set, iIdxCur is the cursor number of an index
** to use for OR clause processing. The WHERE clause should use this
** specific cursor. If WHERE_ONEPASS_DESIRED is set, then iIdxCur is
@@ -143778,6 +166791,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
Expr *pWhere, /* The WHERE clause */
ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */
ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */
+ Select *pSelect, /* The entire SELECT statement */
u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */
int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number
** If WHERE_USE_LIMIT, then the limit amount */
@@ -143797,8 +166811,8 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
u8 bFordelete = 0; /* OPFLAG_FORDELETE or zero, as appropriate */
assert( (wctrlFlags & WHERE_ONEPASS_MULTIROW)==0 || (
- (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
- && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0
+ (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
+ && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0
));
/* Only one of WHERE_OR_SUBCLAUSE or WHERE_USE_LIMIT */
@@ -143811,17 +166825,13 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
- if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0;
- sWLB.pOrderBy = pOrderBy;
-
- /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
- ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */
- if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){
+ if( pOrderBy && pOrderBy->nExpr>=BMS ){
+ pOrderBy = 0;
wctrlFlags &= ~WHERE_WANT_DISTINCT;
}
/* The number of tables in the FROM clause is limited by the number of
- ** bits in a Bitmask
+ ** bits in a Bitmask
*/
testcase( pTabList->nSrc==BMS );
if( pTabList->nSrc>BMS ){
@@ -143829,7 +166839,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
return 0;
}
- /* This function normally generates a nested loop for all tables in
+ /* This function normally generates a nested loop for all tables in
** pTabList. But if the WHERE_OR_SUBCLAUSE flag is set, then we should
** only generate code for the first table in pTabList and assume that
** any cursors associated with subsequent tables are uninitialized.
@@ -143843,7 +166853,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** field (type Bitmask) it must be aligned on an 8-byte boundary on
** some architectures. Hence the ROUND8() below.
*/
- nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel));
+ nByteWInfo = ROUND8P(sizeof(WhereInfo));
+ if( nTabList>1 ){
+ nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel));
+ }
pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
if( db->mallocFailed ){
sqlite3DbFree(db, pWInfo);
@@ -143853,19 +166866,26 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
pWInfo->pOrderBy = pOrderBy;
+#if WHERETRACE_ENABLED
pWInfo->pWhere = pWhere;
+#endif
pWInfo->pResultSet = pResultSet;
pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1;
pWInfo->nLevel = nTabList;
- pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(v);
+ pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(pParse);
pWInfo->wctrlFlags = wctrlFlags;
pWInfo->iLimit = iAuxArg;
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
- memset(&pWInfo->nOBSat, 0,
+ pWInfo->pSelect = pSelect;
+ memset(&pWInfo->nOBSat, 0,
offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat));
memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel));
assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */
pMaskSet = &pWInfo->sMaskSet;
+ pMaskSet->n = 0;
+ pMaskSet->ix[0] = -99; /* Initialize ix[0] to a value that can never be
+ ** a valid cursor number, to avoid an initial
+ ** test for pMaskSet->n==0 in sqlite3WhereGetMask() */
sWLB.pWInfo = pWInfo;
sWLB.pWC = &pWInfo->sWC;
sWLB.pNew = (WhereLoop*)(((char*)pWInfo)+nByteWInfo);
@@ -143878,15 +166898,16 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* Split the WHERE clause into separate subexpressions where each
** subexpression is separated by an AND operator.
*/
- initMaskSet(pMaskSet);
sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo);
sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND);
-
+
/* Special case: No FROM clause
*/
if( nTabList==0 ){
if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr;
- if( wctrlFlags & WHERE_WANT_DISTINCT ){
+ if( (wctrlFlags & WHERE_WANT_DISTINCT)!=0
+ && OptimizationEnabled(db, SQLITE_DistinctOpt)
+ ){
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
}
ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW"));
@@ -143895,7 +166916,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
**
** The N-th term of the FROM clause is assigned a bitmask of 1<<N.
**
- ** The rule of the previous sentence ensures thta if X is the bitmask for
+ ** The rule of the previous sentence ensures that if X is the bitmask for
** a table T, then X-1 is the bitmask for all other tables to the left of T.
** Knowing the bitmask for all tables to the left of a left join is
** important. Ticket #3015.
@@ -143921,33 +166942,64 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
}
#endif
}
-
+
/* Analyze all of the subexpressions. */
sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC);
- if( db->mallocFailed ) goto whereBeginError;
+ if( pSelect && pSelect->pLimit ){
+ sqlite3WhereAddLimit(&pWInfo->sWC, pSelect);
+ }
+ if( pParse->nErr ) goto whereBeginError;
- /* Special case: WHERE terms that do not refer to any tables in the join
- ** (constant expressions). Evaluate each such term, and jump over all the
- ** generated code if the result is not true.
+ /* The False-WHERE-Term-Bypass optimization:
**
- ** Do not do this if the expression contains non-deterministic functions
- ** that are not within a sub-select. This is not strictly required, but
- ** preserves SQLite's legacy behaviour in the following two cases:
+ ** If there are WHERE terms that are false, then no rows will be output,
+ ** so skip over all of the code generated here.
**
- ** FROM ... WHERE random()>0; -- eval random() once per row
- ** FROM ... WHERE (SELECT random())>0; -- eval random() once overall
- */
- for(ii=0; ii<sWLB.pWC->nTerm; ii++){
- WhereTerm *pT = &sWLB.pWC->a[ii];
+ ** Conditions:
+ **
+ ** (1) The WHERE term must not refer to any tables in the join.
+ ** (2) The term must not come from an ON clause on the
+ ** right-hand side of a LEFT or FULL JOIN.
+ ** (3) The term must not come from an ON clause, or there must be
+ ** no RIGHT or FULL OUTER joins in pTabList.
+ ** (4) If the expression contains non-deterministic functions
+ ** that are not within a sub-select. This is not required
+ ** for correctness but rather to preserves SQLite's legacy
+ ** behaviour in the following two cases:
+ **
+ ** WHERE random()>0; -- eval random() once per row
+ ** WHERE (SELECT random())>0; -- eval random() just once overall
+ **
+ ** Note that the Where term need not be a constant in order for this
+ ** optimization to apply, though it does need to be constant relative to
+ ** the current subquery (condition 1). The term might include variables
+ ** from outer queries so that the value of the term changes from one
+ ** invocation of the current subquery to the next.
+ */
+ for(ii=0; ii<sWLB.pWC->nBase; ii++){
+ WhereTerm *pT = &sWLB.pWC->a[ii]; /* A term of the WHERE clause */
+ Expr *pX; /* The expression of pT */
if( pT->wtFlags & TERM_VIRTUAL ) continue;
- if( pT->prereqAll==0 && (nTabList==0 || exprIsDeterministic(pT->pExpr)) ){
- sqlite3ExprIfFalse(pParse, pT->pExpr, pWInfo->iBreak, SQLITE_JUMPIFNULL);
+ pX = pT->pExpr;
+ assert( pX!=0 );
+ assert( pT->prereqAll!=0 || !ExprHasProperty(pX, EP_OuterON) );
+ if( pT->prereqAll==0 /* Conditions (1) and (2) */
+ && (nTabList==0 || exprIsDeterministic(pX)) /* Condition (4) */
+ && !(ExprHasProperty(pX, EP_InnerON) /* Condition (3) */
+ && (pTabList->a[0].fg.jointype & JT_LTORJ)!=0 )
+ ){
+ sqlite3ExprIfFalse(pParse, pX, pWInfo->iBreak, SQLITE_JUMPIFNULL);
pT->wtFlags |= TERM_CODED;
}
}
if( wctrlFlags & WHERE_WANT_DISTINCT ){
- if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){
+ if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){
+ /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
+ ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */
+ wctrlFlags &= ~WHERE_WANT_DISTINCT;
+ pWInfo->wctrlFlags &= ~WHERE_WANT_DISTINCT;
+ }else if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){
/* The DISTINCT marking is pointless. Ignore it. */
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
}else if( pOrderBy==0 ){
@@ -143959,48 +167011,80 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* Construct the WhereLoop objects */
#if defined(WHERETRACE_ENABLED)
- if( sqlite3WhereTrace & 0xffff ){
+ if( sqlite3WhereTrace & 0xffffffff ){
sqlite3DebugPrintf("*** Optimizer Start *** (wctrlFlags: 0x%x",wctrlFlags);
if( wctrlFlags & WHERE_USE_LIMIT ){
sqlite3DebugPrintf(", limit: %d", iAuxArg);
}
sqlite3DebugPrintf(")\n");
- }
- if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */
- sqlite3WhereClausePrint(sWLB.pWC);
+ if( sqlite3WhereTrace & 0x8000 ){
+ Select sSelect;
+ memset(&sSelect, 0, sizeof(sSelect));
+ sSelect.selFlags = SF_WhereBegin;
+ sSelect.pSrc = pTabList;
+ sSelect.pWhere = pWhere;
+ sSelect.pOrderBy = pOrderBy;
+ sSelect.pEList = pResultSet;
+ sqlite3TreeViewSelect(0, &sSelect, 0);
+ }
+ if( sqlite3WhereTrace & 0x4000 ){ /* Display all WHERE clause terms */
+ sqlite3DebugPrintf("---- WHERE clause at start of analysis:\n");
+ sqlite3WhereClausePrint(sWLB.pWC);
+ }
}
#endif
if( nTabList!=1 || whereShortCut(&sWLB)==0 ){
rc = whereLoopAddAll(&sWLB);
if( rc ) goto whereBeginError;
-
-#ifdef WHERETRACE_ENABLED
- if( sqlite3WhereTrace ){ /* Display all of the WhereLoop objects */
- WhereLoop *p;
- int i;
- static const char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz"
- "ABCDEFGHIJKLMNOPQRSTUVWYXZ";
- for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){
- p->cId = zLabel[i%(sizeof(zLabel)-1)];
- whereLoopPrint(p, sWLB.pWC);
- }
- }
-#endif
-
+
+#ifdef SQLITE_ENABLE_STAT4
+ /* If one or more WhereTerm.truthProb values were used in estimating
+ ** loop parameters, but then those truthProb values were subsequently
+ ** changed based on STAT4 information while computing subsequent loops,
+ ** then we need to rerun the whole loop building process so that all
+ ** loops will be built using the revised truthProb values. */
+ if( sWLB.bldFlags2 & SQLITE_BLDF2_2NDPASS ){
+ WHERETRACE_ALL_LOOPS(pWInfo, sWLB.pWC);
+ WHERETRACE(0xffffffff,
+ ("**** Redo all loop computations due to"
+ " TERM_HIGHTRUTH changes ****\n"));
+ while( pWInfo->pLoops ){
+ WhereLoop *p = pWInfo->pLoops;
+ pWInfo->pLoops = p->pNextLoop;
+ whereLoopDelete(db, p);
+ }
+ rc = whereLoopAddAll(&sWLB);
+ if( rc ) goto whereBeginError;
+ }
+#endif
+ WHERETRACE_ALL_LOOPS(pWInfo, sWLB.pWC);
+
wherePathSolver(pWInfo, 0);
if( db->mallocFailed ) goto whereBeginError;
if( pWInfo->pOrderBy ){
wherePathSolver(pWInfo, pWInfo->nRowOut+1);
if( db->mallocFailed ) goto whereBeginError;
}
+
+ /* TUNING: Assume that a DISTINCT clause on a subquery reduces
+ ** the output size by a factor of 8 (LogEst -30).
+ */
+ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 ){
+ WHERETRACE(0x0080,("nRowOut reduced from %d to %d due to DISTINCT\n",
+ pWInfo->nRowOut, pWInfo->nRowOut-30));
+ pWInfo->nRowOut -= 30;
+ }
+
}
+ assert( pWInfo->pTabList!=0 );
if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
- pWInfo->revMask = ALLBITS;
+ whereReverseScanOrder(pWInfo);
}
- if( pParse->nErr || NEVER(db->mallocFailed) ){
+ if( pParse->nErr ){
goto whereBeginError;
}
+ assert( db->mallocFailed==0 );
#ifdef WHERETRACE_ENABLED
if( sqlite3WhereTrace ){
sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut);
@@ -144023,89 +167107,48 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
}
sqlite3DebugPrintf("\n");
for(ii=0; ii<pWInfo->nLevel; ii++){
- whereLoopPrint(pWInfo->a[ii].pWLoop, sWLB.pWC);
+ sqlite3WhereLoopPrint(pWInfo->a[ii].pWLoop, sWLB.pWC);
}
}
#endif
- /* Attempt to omit tables from the join that do not affect the result.
- ** For a table to not affect the result, the following must be true:
- **
- ** 1) The query must not be an aggregate.
- ** 2) The table must be the RHS of a LEFT JOIN.
- ** 3) Either the query must be DISTINCT, or else the ON or USING clause
- ** must contain a constraint that limits the scan of the table to
- ** at most a single row.
- ** 4) The table must not be referenced by any part of the query apart
- ** from its own USING or ON clause.
- **
- ** For example, given:
+ /* Attempt to omit tables from a join that do not affect the result.
+ ** See the comment on whereOmitNoopJoin() for further information.
**
- ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1);
- ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2);
- ** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3);
- **
- ** then table t2 can be omitted from the following:
- **
- ** SELECT v1, v3 FROM t1
- ** LEFT JOIN t2 USING (t1.ipk=t2.ipk)
- ** LEFT JOIN t3 USING (t1.ipk=t3.ipk)
- **
- ** or from:
- **
- ** SELECT DISTINCT v1, v3 FROM t1
- ** LEFT JOIN t2
- ** LEFT JOIN t3 USING (t1.ipk=t3.ipk)
+ ** This query optimization is factored out into a separate "no-inline"
+ ** procedure to keep the sqlite3WhereBegin() procedure from becoming
+ ** too large. If sqlite3WhereBegin() becomes too large, that prevents
+ ** some C-compiler optimizers from in-lining the
+ ** sqlite3WhereCodeOneLoopStart() procedure, and it is important to
+ ** in-line sqlite3WhereCodeOneLoopStart() for performance reasons.
*/
notReady = ~(Bitmask)0;
if( pWInfo->nLevel>=2
- && pResultSet!=0 /* guarantees condition (1) above */
+ && pResultSet!=0 /* these two combine to guarantee */
+ && 0==(wctrlFlags & WHERE_AGG_DISTINCT) /* condition (1) above */
&& OptimizationEnabled(db, SQLITE_OmitNoopJoin)
){
- int i;
- Bitmask tabUsed = sqlite3WhereExprListUsage(pMaskSet, pResultSet);
- if( sWLB.pOrderBy ){
- tabUsed |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy);
- }
- for(i=pWInfo->nLevel-1; i>=1; i--){
- WhereTerm *pTerm, *pEnd;
- struct SrcList_item *pItem;
- pLoop = pWInfo->a[i].pWLoop;
- pItem = &pWInfo->pTabList->a[pLoop->iTab];
- if( (pItem->fg.jointype & JT_LEFT)==0 ) continue;
- if( (wctrlFlags & WHERE_WANT_DISTINCT)==0
- && (pLoop->wsFlags & WHERE_ONEROW)==0
- ){
- continue;
- }
- if( (tabUsed & pLoop->maskSelf)!=0 ) continue;
- pEnd = sWLB.pWC->a + sWLB.pWC->nTerm;
- for(pTerm=sWLB.pWC->a; pTerm<pEnd; pTerm++){
- if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
- if( !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
- || pTerm->pExpr->iRightJoinTable!=pItem->iCursor
- ){
- break;
- }
- }
- }
- if( pTerm<pEnd ) continue;
- WHERETRACE(0xffff, ("-> drop loop %c not used\n", pLoop->cId));
- notReady &= ~pLoop->maskSelf;
- for(pTerm=sWLB.pWC->a; pTerm<pEnd; pTerm++){
- if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
- pTerm->wtFlags |= TERM_CODED;
- }
- }
- if( i!=pWInfo->nLevel-1 ){
- int nByte = (pWInfo->nLevel-1-i) * sizeof(WhereLevel);
- memmove(&pWInfo->a[i], &pWInfo->a[i+1], nByte);
- }
- pWInfo->nLevel--;
- nTabList--;
- }
+ notReady = whereOmitNoopJoin(pWInfo, notReady);
+ nTabList = pWInfo->nLevel;
+ assert( nTabList>0 );
+ }
+
+ /* Check to see if there are any SEARCH loops that might benefit from
+ ** using a Bloom filter.
+ */
+ if( pWInfo->nLevel>=2
+ && OptimizationEnabled(db, SQLITE_BloomFilter)
+ ){
+ whereCheckIfBloomFilterIsUseful(pWInfo);
+ }
+
+#if defined(WHERETRACE_ENABLED)
+ if( sqlite3WhereTrace & 0x4000 ){ /* Display all terms of the WHERE clause */
+ sqlite3DebugPrintf("---- WHERE clause at end of analysis:\n");
+ sqlite3WhereClausePrint(sWLB.pWC);
}
- WHERETRACE(0xffff,("*** Optimizer Finished ***\n"));
+ WHERETRACE(0xffffffff,("*** Optimizer Finished ***\n"));
+#endif
pWInfo->pParse->nQueryLoop += pWInfo->nRowOut;
/* If the caller is an UPDATE or DELETE statement that is requesting
@@ -144131,10 +167174,12 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){
int wsFlags = pWInfo->a[0].pWLoop->wsFlags;
int bOnerow = (wsFlags & WHERE_ONEROW)!=0;
+ assert( !(wsFlags & WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pTab) );
if( bOnerow || (
0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW)
- && 0==(wsFlags & WHERE_VIRTUALTABLE)
+ && !IsVirtual(pTabList->a[0].pTab)
&& (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK))
+ && OptimizationEnabled(db, SQLITE_OnePass)
)){
pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI;
if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){
@@ -144152,13 +167197,13 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
for(ii=0, pLevel=pWInfo->a; ii<nTabList; ii++, pLevel++){
Table *pTab; /* Table to open */
int iDb; /* Index of database containing table/index */
- struct SrcList_item *pTabItem;
+ SrcItem *pTabItem;
pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
pLoop = pLevel->pWLoop;
- if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
+ if( (pTab->tabFlags & TF_Ephemeral)!=0 || IsView(pTab) ){
/* Do nothing */
}else
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -144170,8 +167215,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* noop */
}else
#endif
- if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
- && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){
+ if( ((pLoop->wsFlags & WHERE_IDX_ONLY)==0
+ && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0)
+ || (pTabItem->fg.jointype & (JT_LTORJ|JT_RIGHT))!=0
+ ){
int op = OP_OpenRead;
if( pWInfo->eOnePass!=ONEPASS_OFF ){
op = OP_OpenWrite;
@@ -144181,7 +167228,14 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
assert( pTabItem->iCursor==pLevel->iTabCur );
testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS-1 );
testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS );
- if( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol<BMS && HasRowid(pTab) ){
+ if( pWInfo->eOnePass==ONEPASS_OFF
+ && pTab->nCol<BMS
+ && (pTab->tabFlags & (TF_HasGenerated|TF_WithoutRowid))==0
+ && (pLoop->wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))==0
+ ){
+ /* If we know that only a prefix of the record will be used,
+ ** it is advantageous to reduce the "column count" field in
+ ** the P4 operand of the OP_OpenRead/Write opcode. */
Bitmask b = pTabItem->colUsed;
int n = 0;
for(; b; b=b>>1, n++){}
@@ -144189,7 +167243,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
assert( n<=pTab->nCol );
}
#ifdef SQLITE_ENABLE_CURSOR_HINTS
- if( pLoop->u.btree.pIndex!=0 ){
+ if( pLoop->u.btree.pIndex!=0 && (pTab->tabFlags & TF_WithoutRowid)==0 ){
sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete);
}else
#endif
@@ -144231,8 +167285,17 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
op = OP_ReopenIdx;
}else{
iIndexCur = pParse->nTab++;
+ if( pIx->bHasExpr && OptimizationEnabled(db, SQLITE_IndexedExpr) ){
+ whereAddIndexedExpr(pParse, pIx, iIndexCur, pTabItem);
+ }
+ if( pIx->pPartIdxWhere && (pTabItem->fg.jointype & JT_RIGHT)==0 ){
+ wherePartIdxExpr(
+ pParse, pIx, pIx->pPartIdxWhere, 0, iIndexCur, pTabItem
+ );
+ }
}
pLevel->iIdxCur = iIndexCur;
+ assert( pIx!=0 );
assert( pIx->pSchema==pTab->pSchema );
assert( iIndexCur>=0 );
if( op ){
@@ -144240,10 +167303,12 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
sqlite3VdbeSetP4KeyInfo(pParse, pIx);
if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0
&& (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0
+ && (pLoop->wsFlags & WHERE_BIGNULL_SORT)==0
+ && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)==0
&& (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0
&& pWInfo->eDistinct!=WHERE_DISTINCT_ORDERED
){
- sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */
+ sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ);
}
VdbeComment((v, "%s", pIx->zName));
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
@@ -144264,6 +167329,37 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
}
}
if( iDb>=0 ) sqlite3CodeVerifySchema(pParse, iDb);
+ if( (pTabItem->fg.jointype & JT_RIGHT)!=0
+ && (pLevel->pRJ = sqlite3WhereMalloc(pWInfo, sizeof(WhereRightJoin)))!=0
+ ){
+ WhereRightJoin *pRJ = pLevel->pRJ;
+ pRJ->iMatch = pParse->nTab++;
+ pRJ->regBloom = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Blob, 65536, pRJ->regBloom);
+ pRJ->regReturn = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pRJ->regReturn);
+ assert( pTab==pTabItem->pTab );
+ if( HasRowid(pTab) ){
+ KeyInfo *pInfo;
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, 1);
+ pInfo = sqlite3KeyInfoAlloc(pParse->db, 1, 0);
+ if( pInfo ){
+ pInfo->aColl[0] = 0;
+ pInfo->aSortFlags[0] = 0;
+ sqlite3VdbeAppendP4(v, pInfo, P4_KEYINFO);
+ }
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, pPk->nKeyCol);
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
+ }
+ pLoop->wsFlags &= ~WHERE_IDX_ONLY;
+ /* The nature of RIGHT JOIN processing is such that it messes up
+ ** the output order. So omit any ORDER BY/GROUP BY elimination
+ ** optimizations. We need to do an actual sort for RIGHT JOIN. */
+ pWInfo->nOBSat = 0;
+ pWInfo->eDistinct = WHERE_DISTINCT_UNORDERED;
+ }
}
pWInfo->iTop = sqlite3VdbeCurrentAddr(v);
if( db->mallocFailed ) goto whereBeginError;
@@ -144275,20 +167371,36 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
for(ii=0; ii<nTabList; ii++){
int addrExplain;
int wsFlags;
+ SrcItem *pSrc;
+ if( pParse->nErr ) goto whereBeginError;
pLevel = &pWInfo->a[ii];
wsFlags = pLevel->pWLoop->wsFlags;
+ pSrc = &pTabList->a[pLevel->iFrom];
+ if( pSrc->fg.isMaterialized ){
+ if( pSrc->fg.isCorrelated ){
+ sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub);
+ }else{
+ int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub);
+ sqlite3VdbeJumpHere(v, iOnce);
+ }
+ }
+ assert( pTabList == pWInfo->pTabList );
+ if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){
+ if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
- if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
- constructAutomaticIndex(pParse, &pWInfo->sWC,
- &pTabList->a[pLevel->iFrom], notReady, pLevel);
+ constructAutomaticIndex(pParse, &pWInfo->sWC, notReady, pLevel);
+#endif
+ }else{
+ sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady);
+ }
if( db->mallocFailed ) goto whereBeginError;
}
-#endif
addrExplain = sqlite3WhereExplainOneScan(
pParse, pTabList, pLevel, wctrlFlags
);
pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
- notReady = sqlite3WhereCodeOneLoopStart(pWInfo, ii, notReady);
+ notReady = sqlite3WhereCodeOneLoopStart(pParse,v,pWInfo,ii,pLevel,notReady);
pWInfo->iContinue = pLevel->addrCont;
if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_OR_SUBCLAUSE)==0 ){
sqlite3WhereAddScanStatus(v, pTabList, pLevel, addrExplain);
@@ -144297,6 +167409,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* Done. */
VdbeModuleComment((v, "Begin WHERE-core"));
+ pWInfo->iEndWhere = sqlite3VdbeCurrentAddr(v);
return pWInfo;
/* Jump here if malloc fails */
@@ -144305,6 +167418,11 @@ whereBeginError:
pParse->nQueryLoop = pWInfo->savedNQueryLoop;
whereInfoFree(db, pWInfo);
}
+#ifdef WHERETRACE_ENABLED
+ /* Prevent harmless compiler warnings about debugging routines
+ ** being declared but never used */
+ sqlite3ShowWhereLoopList(0);
+#endif /* WHERETRACE_ENABLED */
return 0;
}
@@ -144328,8 +167446,28 @@ whereBeginError:
}
#endif
+#ifdef SQLITE_DEBUG
+/*
+** Return true if cursor iCur is opened by instruction k of the
+** bytecode. Used inside of assert() only.
+*/
+static int cursorIsOpen(Vdbe *v, int iCur, int k){
+ while( k>=0 ){
+ VdbeOp *pOp = sqlite3VdbeGetOp(v,k--);
+ if( pOp->p1!=iCur ) continue;
+ if( pOp->opcode==OP_Close ) return 0;
+ if( pOp->opcode==OP_OpenRead ) return 1;
+ if( pOp->opcode==OP_OpenWrite ) return 1;
+ if( pOp->opcode==OP_OpenDup ) return 1;
+ if( pOp->opcode==OP_OpenAutoindex ) return 1;
+ if( pOp->opcode==OP_OpenEphemeral ) return 1;
+ }
+ return 0;
+}
+#endif /* SQLITE_DEBUG */
+
/*
-** Generate the end of the WHERE loop. See comments on
+** Generate the end of the WHERE loop. See comments on
** sqlite3WhereBegin() for additional information.
*/
SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
@@ -144340,6 +167478,8 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
WhereLoop *pLoop;
SrcList *pTabList = pWInfo->pTabList;
sqlite3 *db = pParse->db;
+ int iEnd = sqlite3VdbeCurrentAddr(v);
+ int nRJ = 0;
/* Generate loop termination code.
*/
@@ -144347,6 +167487,17 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
for(i=pWInfo->nLevel-1; i>=0; i--){
int addr;
pLevel = &pWInfo->a[i];
+ if( pLevel->pRJ ){
+ /* Terminate the subroutine that forms the interior of the loop of
+ ** the RIGHT JOIN table */
+ WhereRightJoin *pRJ = pLevel->pRJ;
+ sqlite3VdbeResolveLabel(v, pLevel->addrCont);
+ pLevel->addrCont = 0;
+ pRJ->endSubrtn = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp3(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn, 1);
+ VdbeCoverage(v);
+ nRJ++;
+ }
pLoop = pLevel->pWLoop;
if( pLevel->op!=OP_Noop ){
#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
@@ -144357,7 +167508,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
&& i==pWInfo->nLevel-1 /* Ticket [ef9318757b152e3] 2017-10-21 */
&& (pLoop->wsFlags & WHERE_INDEXED)!=0
&& (pIdx = pLoop->u.btree.pIndex)->hasStat1
- && (n = pLoop->u.btree.nIdxCol)>0
+ && (n = pLoop->u.btree.nDistinctCol)>0
&& pIdx->aiRowLogEst[n]>=36
){
int r1 = pParse->nMem+1;
@@ -144374,32 +167525,61 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
}
#endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */
/* The common case: Advance to the next row */
- sqlite3VdbeResolveLabel(v, pLevel->addrCont);
+ if( pLevel->addrCont ) sqlite3VdbeResolveLabel(v, pLevel->addrCont);
sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3);
sqlite3VdbeChangeP5(v, pLevel->p5);
VdbeCoverage(v);
VdbeCoverageIf(v, pLevel->op==OP_Next);
VdbeCoverageIf(v, pLevel->op==OP_Prev);
VdbeCoverageIf(v, pLevel->op==OP_VNext);
+ if( pLevel->regBignull ){
+ sqlite3VdbeResolveLabel(v, pLevel->addrBignull);
+ sqlite3VdbeAddOp2(v, OP_DecrJumpZero, pLevel->regBignull, pLevel->p2-1);
+ VdbeCoverage(v);
+ }
#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
if( addrSeek ) sqlite3VdbeJumpHere(v, addrSeek);
#endif
- }else{
+ }else if( pLevel->addrCont ){
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
}
- if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){
+ if( (pLoop->wsFlags & WHERE_IN_ABLE)!=0 && pLevel->u.in.nIn>0 ){
struct InLoop *pIn;
int j;
sqlite3VdbeResolveLabel(v, pLevel->addrNxt);
for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){
+ assert( sqlite3VdbeGetOp(v, pIn->addrInTop+1)->opcode==OP_IsNull
+ || pParse->db->mallocFailed );
sqlite3VdbeJumpHere(v, pIn->addrInTop+1);
if( pIn->eEndLoopOp!=OP_Noop ){
if( pIn->nPrefix ){
- assert( pLoop->wsFlags & WHERE_IN_EARLYOUT );
- sqlite3VdbeAddOp4Int(v, OP_IfNoHope, pLevel->iIdxCur,
- sqlite3VdbeCurrentAddr(v)+2,
- pIn->iBase, pIn->nPrefix);
- VdbeCoverage(v);
+ int bEarlyOut =
+ (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
+ && (pLoop->wsFlags & WHERE_IN_EARLYOUT)!=0;
+ if( pLevel->iLeftJoin ){
+ /* For LEFT JOIN queries, cursor pIn->iCur may not have been
+ ** opened yet. This occurs for WHERE clauses such as
+ ** "a = ? AND b IN (...)", where the index is on (a, b). If
+ ** the RHS of the (a=?) is NULL, then the "b IN (...)" may
+ ** never have been coded, but the body of the loop run to
+ ** return the null-row. So, if the cursor is not open yet,
+ ** jump over the OP_Next or OP_Prev instruction about to
+ ** be coded. */
+ sqlite3VdbeAddOp2(v, OP_IfNotOpen, pIn->iCur,
+ sqlite3VdbeCurrentAddr(v) + 2 + bEarlyOut);
+ VdbeCoverage(v);
+ }
+ if( bEarlyOut ){
+ sqlite3VdbeAddOp4Int(v, OP_IfNoHope, pLevel->iIdxCur,
+ sqlite3VdbeCurrentAddr(v)+2,
+ pIn->iBase, pIn->nPrefix);
+ VdbeCoverage(v);
+ /* Retarget the OP_IsNull against the left operand of IN so
+ ** it jumps past the OP_IfNoHope. This is because the
+ ** OP_IsNull also bypasses the OP_Affinity opcode that is
+ ** required by OP_IfNoHope. */
+ sqlite3VdbeJumpHere(v, pIn->addrInTop+1);
+ }
}
sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
VdbeCoverage(v);
@@ -144410,6 +167590,10 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
}
}
sqlite3VdbeResolveLabel(v, pLevel->addrBrk);
+ if( pLevel->pRJ ){
+ sqlite3VdbeAddOp3(v, OP_Return, pLevel->pRJ->regReturn, 0, 1);
+ VdbeCoverage(v);
+ }
if( pLevel->addrSkip ){
sqlite3VdbeGoto(v, pLevel->addrSkip);
VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName));
@@ -144431,9 +167615,15 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
assert( pLevel->iTabCur==pTabList->a[pLevel->iFrom].iCursor );
sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur);
}
- if( (ws & WHERE_INDEXED)
- || ((ws & WHERE_MULTI_OR) && pLevel->u.pCovidx)
+ if( (ws & WHERE_INDEXED)
+ || ((ws & WHERE_MULTI_OR) && pLevel->u.pCoveringIdx)
){
+ if( ws & WHERE_MULTI_OR ){
+ Index *pIx = pLevel->u.pCoveringIdx;
+ int iDb = sqlite3SchemaToIndex(db, pIx->pSchema);
+ sqlite3VdbeAddOp3(v, OP_ReopenIdx, pLevel->iIdxCur, pIx->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIx);
+ }
sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur);
}
if( pLevel->op==OP_Return ){
@@ -144447,21 +167637,25 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
pWInfo->pTabList->a[pLevel->iFrom].pTab->zName));
}
- /* The "break" point is here, just past the end of the outer loop.
- ** Set it.
- */
- sqlite3VdbeResolveLabel(v, pWInfo->iBreak);
-
assert( pWInfo->nLevel<=pTabList->nSrc );
for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
int k, last;
- VdbeOp *pOp;
+ VdbeOp *pOp, *pLastOp;
Index *pIdx = 0;
- struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
+ SrcItem *pTabItem = &pTabList->a[pLevel->iFrom];
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );
pLoop = pLevel->pWLoop;
+ /* Do RIGHT JOIN processing. Generate code that will output the
+ ** unmatched rows of the right operand of the RIGHT JOIN with
+ ** all of the columns of the left operand set to NULL.
+ */
+ if( pLevel->pRJ ){
+ sqlite3WhereRightJoinLoop(pWInfo, i, pLevel);
+ continue;
+ }
+
/* For a co-routine, change all OP_Column references to the table of
** the co-routine into OP_Copy of result contained in a register.
** OP_Rowid becomes OP_Null.
@@ -144477,7 +167671,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
** from the index instead of from the table where possible. In some cases
** this optimization prevents the table from ever being read, which can
** yield a significant performance boost.
- **
+ **
** Calls to the code generator in between sqlite3WhereBegin and
** sqlite3WhereEnd will have created code that references the table
** directly. This loop scans all that code looking for opcodes
@@ -144487,42 +167681,92 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
if( pLoop->wsFlags & (WHERE_INDEXED|WHERE_IDX_ONLY) ){
pIdx = pLoop->u.btree.pIndex;
}else if( pLoop->wsFlags & WHERE_MULTI_OR ){
- pIdx = pLevel->u.pCovidx;
+ pIdx = pLevel->u.pCoveringIdx;
}
if( pIdx
- && (pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable))
&& !db->mallocFailed
){
- last = sqlite3VdbeCurrentAddr(v);
- k = pLevel->addrBody;
+ if( pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable) ){
+ last = iEnd;
+ }else{
+ last = pWInfo->iEndWhere;
+ }
+ if( pIdx->bHasExpr ){
+ IndexedExpr *p = pParse->pIdxEpr;
+ while( p ){
+ if( p->iIdxCur==pLevel->iIdxCur ){
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x200 ){
+ sqlite3DebugPrintf("Disable pParse->pIdxEpr term {%d,%d}\n",
+ p->iIdxCur, p->iIdxCol);
+ if( sqlite3WhereTrace & 0x5000 ) sqlite3ShowExpr(p->pExpr);
+ }
+#endif
+ p->iDataCur = -1;
+ p->iIdxCur = -1;
+ }
+ p = p->pIENext;
+ }
+ }
+ k = pLevel->addrBody + 1;
#ifdef SQLITE_DEBUG
if( db->flags & SQLITE_VdbeAddopTrace ){
- printf("TRANSLATE opcodes in range %d..%d\n", k, last-1);
+ printf("TRANSLATE cursor %d->%d in opcode range %d..%d\n",
+ pLevel->iTabCur, pLevel->iIdxCur, k, last-1);
}
+ /* Proof that the "+1" on the k value above is safe */
+ pOp = sqlite3VdbeGetOp(v, k - 1);
+ assert( pOp->opcode!=OP_Column || pOp->p1!=pLevel->iTabCur );
+ assert( pOp->opcode!=OP_Rowid || pOp->p1!=pLevel->iTabCur );
+ assert( pOp->opcode!=OP_IfNullRow || pOp->p1!=pLevel->iTabCur );
#endif
pOp = sqlite3VdbeGetOp(v, k);
- for(; k<last; k++, pOp++){
- if( pOp->p1!=pLevel->iTabCur ) continue;
- if( pOp->opcode==OP_Column
+ pLastOp = pOp + (last - k);
+ assert( pOp<=pLastOp );
+ do{
+ if( pOp->p1!=pLevel->iTabCur ){
+ /* no-op */
+ }else if( pOp->opcode==OP_Column
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|| pOp->opcode==OP_Offset
#endif
){
int x = pOp->p2;
assert( pIdx->pTable==pTab );
+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
+ if( pOp->opcode==OP_Offset ){
+ /* Do not need to translate the column number */
+ }else
+#endif
if( !HasRowid(pTab) ){
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
x = pPk->aiColumn[x];
assert( x>=0 );
+ }else{
+ testcase( x!=sqlite3StorageColumnToTable(pTab,x) );
+ x = sqlite3StorageColumnToTable(pTab,x);
}
- x = sqlite3ColumnOfIndex(pIdx, x);
+ x = sqlite3TableColumnToIndex(pIdx, x);
if( x>=0 ){
pOp->p2 = x;
pOp->p1 = pLevel->iIdxCur;
OpcodeRewriteTrace(db, k, pOp);
+ }else{
+ /* Unable to translate the table reference into an index
+ ** reference. Verify that this is harmless - that the
+ ** table being referenced really is open.
+ */
+#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
+ || cursorIsOpen(v,pOp->p1,k)
+ || pOp->opcode==OP_Offset
+ );
+#else
+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
+ || cursorIsOpen(v,pOp->p1,k)
+ );
+#endif
}
- assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0
- || pWInfo->eOnePass );
}else if( pOp->opcode==OP_Rowid ){
pOp->p1 = pLevel->iIdxCur;
pOp->opcode = OP_IdxRowid;
@@ -144531,17 +167775,26 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
pOp->p1 = pLevel->iIdxCur;
OpcodeRewriteTrace(db, k, pOp);
}
- }
+#ifdef SQLITE_DEBUG
+ k++;
+#endif
+ }while( (++pOp)<pLastOp );
#ifdef SQLITE_DEBUG
if( db->flags & SQLITE_VdbeAddopTrace ) printf("TRANSLATE complete\n");
#endif
}
}
+ /* The "break" point is here, just past the end of the outer loop.
+ ** Set it.
+ */
+ sqlite3VdbeResolveLabel(v, pWInfo->iBreak);
+
/* Final cleanup
*/
pParse->nQueryLoop = pWInfo->savedNQueryLoop;
whereInfoFree(db, pWInfo);
+ pParse->withinRJSubrtn -= nRJ;
return;
}
@@ -144589,12 +167842,12 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
** (in this case max()) to process rows sorted in order of (c, d), which
** makes things easier for obvious reasons. More generally:
**
-** * FROM, WHERE, GROUP BY and HAVING clauses are all moved to
+** * FROM, WHERE, GROUP BY and HAVING clauses are all moved to
** the sub-query.
**
** * ORDER BY, LIMIT and OFFSET remain part of the parent query.
**
-** * Terminals from each of the expression trees that make up the
+** * Terminals from each of the expression trees that make up the
** select-list and ORDER BY expressions in the parent query are
** selected by the sub-query. For the purposes of the transformation,
** terminals are column references and aggregate functions.
@@ -144603,14 +167856,14 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
** the same window declaration (the OVER bit), then a single scan may
** be used to process more than one window function. For example:
**
-** SELECT max(b) OVER (PARTITION BY c ORDER BY d),
-** min(e) OVER (PARTITION BY c ORDER BY d)
+** SELECT max(b) OVER (PARTITION BY c ORDER BY d),
+** min(e) OVER (PARTITION BY c ORDER BY d)
** FROM t1;
**
** is transformed in the same way as the example above. However:
**
-** SELECT max(b) OVER (PARTITION BY c ORDER BY d),
-** min(e) OVER (PARTITION BY a ORDER BY b)
+** SELECT max(b) OVER (PARTITION BY c ORDER BY d),
+** min(e) OVER (PARTITION BY a ORDER BY b)
** FROM t1;
**
** Must be transformed to:
@@ -144663,15 +167916,15 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
** first_value(expr)
** last_value(expr)
** nth_value(expr, N)
-**
-** These are the same built-in window functions supported by Postgres.
+**
+** These are the same built-in window functions supported by Postgres.
** Although the behaviour of aggregate window functions (functions that
-** can be used as either aggregates or window funtions) allows them to
+** can be used as either aggregates or window functions) allows them to
** be implemented using an API, built-in window functions are much more
-** esoteric. Additionally, some window functions (e.g. nth_value())
+** esoteric. Additionally, some window functions (e.g. nth_value())
** may only be implemented by caching the entire partition in memory.
** As such, some built-in window functions use the same API as aggregate
-** window functions and some are implemented directly using VDBE
+** window functions and some are implemented directly using VDBE
** instructions. Additionally, for those functions that use the API, the
** window frame is sometimes modified before the SELECT statement is
** rewritten. For example, regardless of the specified window frame, the
@@ -144683,7 +167936,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
**
** As well as some of the built-in window functions, aggregate window
** functions min() and max() are implemented using VDBE instructions if
-** the start of the window frame is declared as anything other than
+** the start of the window frame is declared as anything other than
** UNBOUNDED PRECEDING.
*/
@@ -144694,7 +167947,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
*/
static void row_numberStepFunc(
- sqlite3_context *pCtx,
+ sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
@@ -144722,10 +167975,10 @@ struct CallCount {
** Implementation of built-in window function dense_rank(). Assumes that
** the window frame has been set to:
**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
*/
static void dense_rankStepFunc(
- sqlite3_context *pCtx,
+ sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
@@ -144748,13 +168001,103 @@ static void dense_rankValueFunc(sqlite3_context *pCtx){
}
/*
+** Implementation of built-in window function nth_value(). This
+** implementation is used in "slow mode" only - when the EXCLUDE clause
+** is not set to the default value "NO OTHERS".
+*/
+struct NthValueCtx {
+ i64 nStep;
+ sqlite3_value *pValue;
+};
+static void nth_valueStepFunc(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **apArg
+){
+ struct NthValueCtx *p;
+ p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ if( p ){
+ i64 iVal;
+ switch( sqlite3_value_numeric_type(apArg[1]) ){
+ case SQLITE_INTEGER:
+ iVal = sqlite3_value_int64(apArg[1]);
+ break;
+ case SQLITE_FLOAT: {
+ double fVal = sqlite3_value_double(apArg[1]);
+ if( ((i64)fVal)!=fVal ) goto error_out;
+ iVal = (i64)fVal;
+ break;
+ }
+ default:
+ goto error_out;
+ }
+ if( iVal<=0 ) goto error_out;
+
+ p->nStep++;
+ if( iVal==p->nStep ){
+ p->pValue = sqlite3_value_dup(apArg[0]);
+ if( !p->pValue ){
+ sqlite3_result_error_nomem(pCtx);
+ }
+ }
+ }
+ UNUSED_PARAMETER(nArg);
+ UNUSED_PARAMETER(apArg);
+ return;
+
+ error_out:
+ sqlite3_result_error(
+ pCtx, "second argument to nth_value must be a positive integer", -1
+ );
+}
+static void nth_valueFinalizeFunc(sqlite3_context *pCtx){
+ struct NthValueCtx *p;
+ p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, 0);
+ if( p && p->pValue ){
+ sqlite3_result_value(pCtx, p->pValue);
+ sqlite3_value_free(p->pValue);
+ p->pValue = 0;
+ }
+}
+#define nth_valueInvFunc noopStepFunc
+#define nth_valueValueFunc noopValueFunc
+
+static void first_valueStepFunc(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **apArg
+){
+ struct NthValueCtx *p;
+ p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ if( p && p->pValue==0 ){
+ p->pValue = sqlite3_value_dup(apArg[0]);
+ if( !p->pValue ){
+ sqlite3_result_error_nomem(pCtx);
+ }
+ }
+ UNUSED_PARAMETER(nArg);
+ UNUSED_PARAMETER(apArg);
+}
+static void first_valueFinalizeFunc(sqlite3_context *pCtx){
+ struct NthValueCtx *p;
+ p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ if( p && p->pValue ){
+ sqlite3_result_value(pCtx, p->pValue);
+ sqlite3_value_free(p->pValue);
+ p->pValue = 0;
+ }
+}
+#define first_valueInvFunc noopStepFunc
+#define first_valueValueFunc noopValueFunc
+
+/*
** Implementation of built-in window function rank(). Assumes that
** the window frame has been set to:
**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
*/
static void rankStepFunc(
- sqlite3_context *pCtx,
+ sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
@@ -144782,71 +168125,86 @@ static void rankValueFunc(sqlite3_context *pCtx){
** Implementation of built-in window function percent_rank(). Assumes that
** the window frame has been set to:
**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+** GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
*/
static void percent_rankStepFunc(
- sqlite3_context *pCtx,
+ sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
struct CallCount *p;
- UNUSED_PARAMETER(nArg); assert( nArg==1 );
-
+ UNUSED_PARAMETER(nArg); assert( nArg==0 );
+ UNUSED_PARAMETER(apArg);
p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p ){
- if( p->nTotal==0 ){
- p->nTotal = sqlite3_value_int64(apArg[0]);
- }
- p->nStep++;
- if( p->nValue==0 ){
- p->nValue = p->nStep;
- }
+ p->nTotal++;
}
}
+static void percent_rankInvFunc(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **apArg
+){
+ struct CallCount *p;
+ UNUSED_PARAMETER(nArg); assert( nArg==0 );
+ UNUSED_PARAMETER(apArg);
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ p->nStep++;
+}
static void percent_rankValueFunc(sqlite3_context *pCtx){
struct CallCount *p;
p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p ){
+ p->nValue = p->nStep;
if( p->nTotal>1 ){
- double r = (double)(p->nValue-1) / (double)(p->nTotal-1);
+ double r = (double)p->nValue / (double)(p->nTotal-1);
sqlite3_result_double(pCtx, r);
}else{
sqlite3_result_double(pCtx, 0.0);
}
- p->nValue = 0;
}
}
+#define percent_rankFinalizeFunc percent_rankValueFunc
/*
** Implementation of built-in window function cume_dist(). Assumes that
** the window frame has been set to:
**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+** GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING
*/
static void cume_distStepFunc(
- sqlite3_context *pCtx,
+ sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
struct CallCount *p;
- assert( nArg==1 ); UNUSED_PARAMETER(nArg);
-
+ UNUSED_PARAMETER(nArg); assert( nArg==0 );
+ UNUSED_PARAMETER(apArg);
p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p ){
- if( p->nTotal==0 ){
- p->nTotal = sqlite3_value_int64(apArg[0]);
- }
- p->nStep++;
+ p->nTotal++;
}
}
-static void cume_distValueFunc(sqlite3_context *pCtx){
+static void cume_distInvFunc(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **apArg
+){
struct CallCount *p;
+ UNUSED_PARAMETER(nArg); assert( nArg==0 );
+ UNUSED_PARAMETER(apArg);
p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
- if( p && p->nTotal ){
+ p->nStep++;
+}
+static void cume_distValueFunc(sqlite3_context *pCtx){
+ struct CallCount *p;
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, 0);
+ if( p ){
double r = (double)(p->nStep) / (double)(p->nTotal);
sqlite3_result_double(pCtx, r);
}
}
+#define cume_distFinalizeFunc cume_distValueFunc
/*
** Context object for ntile() window function.
@@ -144861,40 +168219,50 @@ struct NtileCtx {
** Implementation of ntile(). This assumes that the window frame has
** been coerced to:
**
-** ROWS UNBOUNDED PRECEDING AND CURRENT ROW
+** ROWS CURRENT ROW AND UNBOUNDED FOLLOWING
*/
static void ntileStepFunc(
- sqlite3_context *pCtx,
+ sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
struct NtileCtx *p;
- assert( nArg==2 ); UNUSED_PARAMETER(nArg);
+ assert( nArg==1 ); UNUSED_PARAMETER(nArg);
p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p ){
if( p->nTotal==0 ){
p->nParam = sqlite3_value_int64(apArg[0]);
- p->nTotal = sqlite3_value_int64(apArg[1]);
if( p->nParam<=0 ){
sqlite3_result_error(
pCtx, "argument of ntile must be a positive integer", -1
);
}
}
- p->iRow++;
+ p->nTotal++;
}
}
+static void ntileInvFunc(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **apArg
+){
+ struct NtileCtx *p;
+ assert( nArg==1 ); UNUSED_PARAMETER(nArg);
+ UNUSED_PARAMETER(apArg);
+ p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ p->iRow++;
+}
static void ntileValueFunc(sqlite3_context *pCtx){
struct NtileCtx *p;
p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p && p->nParam>0 ){
int nSize = (p->nTotal / p->nParam);
if( nSize==0 ){
- sqlite3_result_int64(pCtx, p->iRow);
+ sqlite3_result_int64(pCtx, p->iRow+1);
}else{
i64 nLarge = p->nTotal - p->nParam*nSize;
i64 iSmall = nLarge*(nSize+1);
- i64 iRow = p->iRow-1;
+ i64 iRow = p->iRow;
assert( (nLarge*(nSize+1) + (p->nParam-nLarge)*nSize)==p->nTotal );
@@ -144906,6 +168274,7 @@ static void ntileValueFunc(sqlite3_context *pCtx){
}
}
}
+#define ntileFinalizeFunc ntileValueFunc
/*
** Context object for last_value() window function.
@@ -144919,7 +168288,7 @@ struct LastValueCtx {
** Implementation of last_value().
*/
static void last_valueStepFunc(
- sqlite3_context *pCtx,
+ sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
@@ -144937,7 +168306,7 @@ static void last_valueStepFunc(
}
}
static void last_valueInvFunc(
- sqlite3_context *pCtx,
+ sqlite3_context *pCtx,
int nArg,
sqlite3_value **apArg
){
@@ -144955,7 +168324,7 @@ static void last_valueInvFunc(
}
static void last_valueValueFunc(sqlite3_context *pCtx){
struct LastValueCtx *p;
- p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, 0);
if( p && p->pVal ){
sqlite3_result_value(pCtx, p->pVal);
}
@@ -145014,7 +168383,7 @@ static void noopValueFunc(sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ }
/* Window functions that use all window interfaces: xStep, xFinal,
** xValue, and xInverse */
#define WINDOWFUNCALL(name,nArg,extra) { \
- nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \
+ nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \
name ## StepFunc, name ## FinalizeFunc, name ## ValueFunc, \
name ## InvFunc, name ## Name, {0} \
}
@@ -145022,7 +168391,7 @@ static void noopValueFunc(sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ }
/* Window functions that are implemented using bytecode and thus have
** no-op routines for their methods */
#define WINDOWFUNCNOOP(name,nArg,extra) { \
- nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \
+ nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \
noopStepFunc, noopValueFunc, noopValueFunc, \
noopStepFunc, name ## Name, {0} \
}
@@ -145031,7 +168400,7 @@ static void noopValueFunc(sqlite3_context *p){ UNUSED_PARAMETER(p); /*no-op*/ }
** same routine for xFinalize and xValue and which never call
** xInverse. */
#define WINDOWFUNCX(name,nArg,extra) { \
- nArg, (SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \
+ nArg, (SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_WINDOW|extra), 0, 0, \
name ## StepFunc, name ## ValueFunc, name ## ValueFunc, \
noopStepFunc, name ## Name, {0} \
}
@@ -145045,12 +168414,12 @@ SQLITE_PRIVATE void sqlite3WindowFunctions(void){
WINDOWFUNCX(row_number, 0, 0),
WINDOWFUNCX(dense_rank, 0, 0),
WINDOWFUNCX(rank, 0, 0),
- WINDOWFUNCX(percent_rank, 0, SQLITE_FUNC_WINDOW_SIZE),
- WINDOWFUNCX(cume_dist, 0, SQLITE_FUNC_WINDOW_SIZE),
- WINDOWFUNCX(ntile, 1, SQLITE_FUNC_WINDOW_SIZE),
+ WINDOWFUNCALL(percent_rank, 0, 0),
+ WINDOWFUNCALL(cume_dist, 0, 0),
+ WINDOWFUNCALL(ntile, 1, 0),
WINDOWFUNCALL(last_value, 1, 0),
- WINDOWFUNCNOOP(nth_value, 2, 0),
- WINDOWFUNCNOOP(first_value, 1, 0),
+ WINDOWFUNCALL(nth_value, 2, 0),
+ WINDOWFUNCALL(first_value, 1, 0),
WINDOWFUNCNOOP(lead, 1, 0),
WINDOWFUNCNOOP(lead, 2, 0),
WINDOWFUNCNOOP(lead, 3, 0),
@@ -145061,6 +168430,17 @@ SQLITE_PRIVATE void sqlite3WindowFunctions(void){
sqlite3InsertBuiltinFuncs(aWindowFuncs, ArraySize(aWindowFuncs));
}
+static Window *windowFind(Parse *pParse, Window *pList, const char *zName){
+ Window *p;
+ for(p=pList; p; p=p->pNextWin){
+ if( sqlite3StrICmp(p->zName, zName)==0 ) break;
+ }
+ if( p==0 ){
+ sqlite3ErrorMsg(pParse, "no such window: %s", zName);
+ }
+ return p;
+}
+
/*
** This function is called immediately after resolving the function name
** for a window function within a SELECT statement. Argument pList is a
@@ -145069,7 +168449,7 @@ SQLITE_PRIVATE void sqlite3WindowFunctions(void){
** is the Window object representing the associated OVER clause. This
** function updates the contents of pWin as follows:
**
-** * If the OVER clause refered to a named window (as in "max(x) OVER win"),
+** * If the OVER clause referred to a named window (as in "max(x) OVER win"),
** search list pList for a matching WINDOW definition, and update pWin
** accordingly. If no such WINDOW clause can be found, leave an error
** in pParse.
@@ -145079,56 +168459,74 @@ SQLITE_PRIVATE void sqlite3WindowFunctions(void){
** of this file), pWin is updated here.
*/
SQLITE_PRIVATE void sqlite3WindowUpdate(
- Parse *pParse,
+ Parse *pParse,
Window *pList, /* List of named windows for this SELECT */
Window *pWin, /* Window frame to update */
FuncDef *pFunc /* Window function definition */
){
- if( pWin->zName && pWin->eType==0 ){
- Window *p;
- for(p=pList; p; p=p->pNextWin){
- if( sqlite3StrICmp(p->zName, pWin->zName)==0 ) break;
- }
- if( p==0 ){
- sqlite3ErrorMsg(pParse, "no such window: %s", pWin->zName);
- return;
- }
+ if( pWin->zName && pWin->eFrmType==0 ){
+ Window *p = windowFind(pParse, pList, pWin->zName);
+ if( p==0 ) return;
pWin->pPartition = sqlite3ExprListDup(pParse->db, p->pPartition, 0);
pWin->pOrderBy = sqlite3ExprListDup(pParse->db, p->pOrderBy, 0);
pWin->pStart = sqlite3ExprDup(pParse->db, p->pStart, 0);
pWin->pEnd = sqlite3ExprDup(pParse->db, p->pEnd, 0);
pWin->eStart = p->eStart;
pWin->eEnd = p->eEnd;
- pWin->eType = p->eType;
+ pWin->eFrmType = p->eFrmType;
+ pWin->eExclude = p->eExclude;
+ }else{
+ sqlite3WindowChain(pParse, pWin, pList);
}
+ if( (pWin->eFrmType==TK_RANGE)
+ && (pWin->pStart || pWin->pEnd)
+ && (pWin->pOrderBy==0 || pWin->pOrderBy->nExpr!=1)
+ ){
+ sqlite3ErrorMsg(pParse,
+ "RANGE with offset PRECEDING/FOLLOWING requires one ORDER BY expression"
+ );
+ }else
if( pFunc->funcFlags & SQLITE_FUNC_WINDOW ){
sqlite3 *db = pParse->db;
if( pWin->pFilter ){
- sqlite3ErrorMsg(pParse,
+ sqlite3ErrorMsg(pParse,
"FILTER clause may only be used with aggregate window functions"
);
- }else
- if( pFunc->zName==row_numberName || pFunc->zName==ntileName ){
- sqlite3ExprDelete(db, pWin->pStart);
- sqlite3ExprDelete(db, pWin->pEnd);
- pWin->pStart = pWin->pEnd = 0;
- pWin->eType = TK_ROWS;
- pWin->eStart = TK_UNBOUNDED;
- pWin->eEnd = TK_CURRENT;
- }else
-
- if( pFunc->zName==dense_rankName || pFunc->zName==rankName
- || pFunc->zName==percent_rankName || pFunc->zName==cume_distName
- ){
- sqlite3ExprDelete(db, pWin->pStart);
- sqlite3ExprDelete(db, pWin->pEnd);
- pWin->pStart = pWin->pEnd = 0;
- pWin->eType = TK_RANGE;
- pWin->eStart = TK_UNBOUNDED;
- pWin->eEnd = TK_CURRENT;
+ }else{
+ struct WindowUpdate {
+ const char *zFunc;
+ int eFrmType;
+ int eStart;
+ int eEnd;
+ } aUp[] = {
+ { row_numberName, TK_ROWS, TK_UNBOUNDED, TK_CURRENT },
+ { dense_rankName, TK_RANGE, TK_UNBOUNDED, TK_CURRENT },
+ { rankName, TK_RANGE, TK_UNBOUNDED, TK_CURRENT },
+ { percent_rankName, TK_GROUPS, TK_CURRENT, TK_UNBOUNDED },
+ { cume_distName, TK_GROUPS, TK_FOLLOWING, TK_UNBOUNDED },
+ { ntileName, TK_ROWS, TK_CURRENT, TK_UNBOUNDED },
+ { leadName, TK_ROWS, TK_UNBOUNDED, TK_UNBOUNDED },
+ { lagName, TK_ROWS, TK_UNBOUNDED, TK_CURRENT },
+ };
+ int i;
+ for(i=0; i<ArraySize(aUp); i++){
+ if( pFunc->zName==aUp[i].zFunc ){
+ sqlite3ExprDelete(db, pWin->pStart);
+ sqlite3ExprDelete(db, pWin->pEnd);
+ pWin->pEnd = pWin->pStart = 0;
+ pWin->eFrmType = aUp[i].eFrmType;
+ pWin->eStart = aUp[i].eStart;
+ pWin->eEnd = aUp[i].eEnd;
+ pWin->eExclude = 0;
+ if( pWin->eStart==TK_FOLLOWING ){
+ pWin->pStart = sqlite3Expr(db, TK_INTEGER, "1");
+ }
+ break;
+ }
+ }
}
}
- pWin->pFunc = pFunc;
+ pWin->pWFunc = pFunc;
}
/*
@@ -145140,17 +168538,20 @@ struct WindowRewrite {
Window *pWin;
SrcList *pSrc;
ExprList *pSub;
+ Table *pTab;
Select *pSubSelect; /* Current sub-select, if any */
};
/*
** Callback function used by selectWindowRewriteEList(). If necessary,
-** this function appends to the output expression-list and updates
+** this function appends to the output expression-list and updates
** expression (*ppExpr) in place.
*/
static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){
struct WindowRewrite *p = pWalker->u.pRewrite;
Parse *pParse = pWalker->pParse;
+ assert( p!=0 );
+ assert( p->pWin!=0 );
/* If this function is being called from within a scalar sub-select
** that used by the SELECT statement being processed, only process
@@ -145184,13 +168585,29 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){
}
}
}
- /* Fall through. */
+ /* no break */ deliberate_fall_through
+ case TK_IF_NULL_ROW:
case TK_AGG_FUNCTION:
case TK_COLUMN: {
- Expr *pDup = sqlite3ExprDup(pParse->db, pExpr, 0);
- p->pSub = sqlite3ExprListAppend(pParse, p->pSub, pDup);
+ int iCol = -1;
+ if( pParse->db->mallocFailed ) return WRC_Abort;
+ if( p->pSub ){
+ int i;
+ for(i=0; i<p->pSub->nExpr; i++){
+ if( 0==sqlite3ExprCompare(0, p->pSub->a[i].pExpr, pExpr, -1) ){
+ iCol = i;
+ break;
+ }
+ }
+ }
+ if( iCol<0 ){
+ Expr *pDup = sqlite3ExprDup(pParse->db, pExpr, 0);
+ if( pDup && pDup->op==TK_AGG_FUNCTION ) pDup->op = TK_FUNCTION;
+ p->pSub = sqlite3ExprListAppend(pParse, p->pSub, pDup);
+ }
if( p->pSub ){
+ int f = pExpr->flags & EP_Collate;
assert( ExprHasProperty(pExpr, EP_Static)==0 );
ExprSetProperty(pExpr, EP_Static);
sqlite3ExprDelete(pParse->db, pExpr);
@@ -145198,10 +168615,12 @@ static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){
memset(pExpr, 0, sizeof(Expr));
pExpr->op = TK_COLUMN;
- pExpr->iColumn = p->pSub->nExpr-1;
+ pExpr->iColumn = (iCol<0 ? p->pSub->nExpr-1: iCol);
pExpr->iTable = p->pWin->iEphCsr;
+ pExpr->y.pTab = p->pTab;
+ pExpr->flags = f;
}
-
+ if( pParse->db->mallocFailed ) return WRC_Abort;
break;
}
@@ -145230,30 +168649,33 @@ static int selectWindowRewriteSelectCb(Walker *pWalker, Select *pSelect){
**
** * TK_COLUMN,
** * aggregate function, or
-** * window function with a Window object that is not a member of the
+** * window function with a Window object that is not a member of the
** Window list passed as the second argument (pWin).
**
** Append the node to output expression-list (*ppSub). And replace it
-** with a TK_COLUMN that reads the (N-1)th element of table
+** with a TK_COLUMN that reads the (N-1)th element of table
** pWin->iEphCsr, where N is the number of elements in (*ppSub) after
** appending the new one.
*/
static void selectWindowRewriteEList(
- Parse *pParse,
+ Parse *pParse,
Window *pWin,
SrcList *pSrc,
ExprList *pEList, /* Rewrite expressions in this list */
+ Table *pTab,
ExprList **ppSub /* IN/OUT: Sub-select expression-list */
){
Walker sWalker;
WindowRewrite sRewrite;
+ assert( pWin!=0 );
memset(&sWalker, 0, sizeof(Walker));
memset(&sRewrite, 0, sizeof(WindowRewrite));
sRewrite.pSub = *ppSub;
sRewrite.pWin = pWin;
sRewrite.pSrc = pSrc;
+ sRewrite.pTab = pTab;
sWalker.pParse = pParse;
sWalker.xExprCallback = selectWindowRewriteExprCb;
@@ -145272,30 +168694,76 @@ static void selectWindowRewriteEList(
static ExprList *exprListAppendList(
Parse *pParse, /* Parsing context */
ExprList *pList, /* List to which to append. Might be NULL */
- ExprList *pAppend /* List of values to append. Might be NULL */
+ ExprList *pAppend, /* List of values to append. Might be NULL */
+ int bIntToNull
){
if( pAppend ){
int i;
int nInit = pList ? pList->nExpr : 0;
for(i=0; i<pAppend->nExpr; i++){
- Expr *pDup = sqlite3ExprDup(pParse->db, pAppend->a[i].pExpr, 0);
+ sqlite3 *db = pParse->db;
+ Expr *pDup = sqlite3ExprDup(db, pAppend->a[i].pExpr, 0);
+ if( db->mallocFailed ){
+ sqlite3ExprDelete(db, pDup);
+ break;
+ }
+ if( bIntToNull ){
+ int iDummy;
+ Expr *pSub;
+ pSub = sqlite3ExprSkipCollateAndLikely(pDup);
+ if( sqlite3ExprIsInteger(pSub, &iDummy) ){
+ pSub->op = TK_NULL;
+ pSub->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse);
+ pSub->u.zToken = 0;
+ }
+ }
pList = sqlite3ExprListAppend(pParse, pList, pDup);
- if( pList ) pList->a[nInit+i].sortOrder = pAppend->a[i].sortOrder;
+ if( pList ) pList->a[nInit+i].fg.sortFlags = pAppend->a[i].fg.sortFlags;
}
}
return pList;
}
/*
+** When rewriting a query, if the new subquery in the FROM clause
+** contains TK_AGG_FUNCTION nodes that refer to an outer query,
+** then we have to increase the Expr->op2 values of those nodes
+** due to the extra subquery layer that was added.
+**
+** See also the incrAggDepth() routine in resolve.c
+*/
+static int sqlite3WindowExtraAggFuncDepth(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_AGG_FUNCTION
+ && pExpr->op2>=pWalker->walkerDepth
+ ){
+ pExpr->op2++;
+ }
+ return WRC_Continue;
+}
+
+static int disallowAggregatesInOrderByCb(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_AGG_FUNCTION && pExpr->pAggInfo==0 ){
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+ sqlite3ErrorMsg(pWalker->pParse,
+ "misuse of aggregate: %s()", pExpr->u.zToken);
+ }
+ return WRC_Continue;
+}
+
+/*
** If the SELECT statement passed as the second argument does not invoke
-** any SQL window functions, this function is a no-op. Otherwise, it
+** any SQL window functions, this function is a no-op. Otherwise, it
** rewrites the SELECT statement so that window function xStep functions
** are invoked in the correct order as described under "SELECT REWRITING"
** at the top of this file.
*/
SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
int rc = SQLITE_OK;
- if( p->pWin && p->pPrior==0 ){
+ if( p->pWin
+ && p->pPrior==0
+ && ALWAYS((p->selFlags & SF_WinRewrite)==0)
+ && ALWAYS(!IN_RENAME_OBJECT)
+ ){
Vdbe *v = sqlite3GetVdbe(pParse);
sqlite3 *db = pParse->db;
Select *pSub = 0; /* The subquery */
@@ -145306,48 +168774,80 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
ExprList *pSort = 0;
ExprList *pSublist = 0; /* Expression list for sub-query */
- Window *pMWin = p->pWin; /* Master window object */
+ Window *pMWin = p->pWin; /* Main window object */
Window *pWin; /* Window object iterator */
+ Table *pTab;
+ Walker w;
+
+ u32 selFlags = p->selFlags;
+
+ pTab = sqlite3DbMallocZero(db, sizeof(Table));
+ if( pTab==0 ){
+ return sqlite3ErrorToParser(db, SQLITE_NOMEM);
+ }
+ sqlite3AggInfoPersistWalkerInit(&w, pParse);
+ sqlite3WalkSelect(&w, p);
+ if( (p->selFlags & SF_Aggregate)==0 ){
+ w.xExprCallback = disallowAggregatesInOrderByCb;
+ w.xSelectCallback = 0;
+ sqlite3WalkExprList(&w, p->pOrderBy);
+ }
p->pSrc = 0;
p->pWhere = 0;
p->pGroupBy = 0;
p->pHaving = 0;
+ p->selFlags &= ~SF_Aggregate;
+ p->selFlags |= SF_WinRewrite;
/* Create the ORDER BY clause for the sub-select. This is the concatenation
** of the window PARTITION and ORDER BY clauses. Then, if this makes it
** redundant, remove the ORDER BY from the parent SELECT. */
- pSort = sqlite3ExprListDup(db, pMWin->pPartition, 0);
- pSort = exprListAppendList(pParse, pSort, pMWin->pOrderBy);
- if( pSort && p->pOrderBy ){
+ pSort = exprListAppendList(pParse, 0, pMWin->pPartition, 1);
+ pSort = exprListAppendList(pParse, pSort, pMWin->pOrderBy, 1);
+ if( pSort && p->pOrderBy && p->pOrderBy->nExpr<=pSort->nExpr ){
+ int nSave = pSort->nExpr;
+ pSort->nExpr = p->pOrderBy->nExpr;
if( sqlite3ExprListCompare(pSort, p->pOrderBy, -1)==0 ){
sqlite3ExprListDelete(db, p->pOrderBy);
p->pOrderBy = 0;
}
+ pSort->nExpr = nSave;
}
/* Assign a cursor number for the ephemeral table used to buffer rows.
** The OpenEphemeral instruction is coded later, after it is known how
** many columns the table will have. */
pMWin->iEphCsr = pParse->nTab++;
+ pParse->nTab += 3;
- selectWindowRewriteEList(pParse, pMWin, pSrc, p->pEList, &pSublist);
- selectWindowRewriteEList(pParse, pMWin, pSrc, p->pOrderBy, &pSublist);
+ selectWindowRewriteEList(pParse, pMWin, pSrc, p->pEList, pTab, &pSublist);
+ selectWindowRewriteEList(pParse, pMWin, pSrc, p->pOrderBy, pTab, &pSublist);
pMWin->nBufferCol = (pSublist ? pSublist->nExpr : 0);
- /* Append the PARTITION BY and ORDER BY expressions to the to the
- ** sub-select expression list. They are required to figure out where
+ /* Append the PARTITION BY and ORDER BY expressions to the to the
+ ** sub-select expression list. They are required to figure out where
** boundaries for partitions and sets of peer rows lie. */
- pSublist = exprListAppendList(pParse, pSublist, pMWin->pPartition);
- pSublist = exprListAppendList(pParse, pSublist, pMWin->pOrderBy);
+ pSublist = exprListAppendList(pParse, pSublist, pMWin->pPartition, 0);
+ pSublist = exprListAppendList(pParse, pSublist, pMWin->pOrderBy, 0);
/* Append the arguments passed to each window function to the
** sub-select expression list. Also allocate two registers for each
** window function - one for the accumulator, another for interim
** results. */
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
- pSublist = exprListAppendList(pParse, pSublist, pWin->pOwner->x.pList);
+ ExprList *pArgs;
+ assert( ExprUseXList(pWin->pOwner) );
+ assert( pWin->pWFunc!=0 );
+ pArgs = pWin->pOwner->x.pList;
+ if( pWin->pWFunc->funcFlags & SQLITE_SUBTYPE ){
+ selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist);
+ pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
+ pWin->bExprArgs = 1;
+ }else{
+ pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
+ pSublist = exprListAppendList(pParse, pSublist, pArgs, 0);
+ }
if( pWin->pFilter ){
Expr *pFilter = sqlite3ExprDup(db, pWin->pFilter, 0);
pSublist = sqlite3ExprListAppend(pParse, pSublist, pFilter);
@@ -145360,52 +168860,89 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
/* If there is no ORDER BY or PARTITION BY clause, and the window
** function accepts zero arguments, and there are no other columns
** selected (e.g. "SELECT row_number() OVER () FROM t1"), it is possible
- ** that pSublist is still NULL here. Add a constant expression here to
- ** keep everything legal in this case.
+ ** that pSublist is still NULL here. Add a constant expression here to
+ ** keep everything legal in this case.
*/
if( pSublist==0 ){
- pSublist = sqlite3ExprListAppend(pParse, 0,
- sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[0], 0)
+ pSublist = sqlite3ExprListAppend(pParse, 0,
+ sqlite3Expr(db, TK_INTEGER, "0")
);
}
pSub = sqlite3SelectNew(
pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0
);
- p->pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
- assert( p->pSrc || db->mallocFailed );
+ TREETRACE(0x40,pParse,pSub,
+ ("New window-function subquery in FROM clause of (%u/%p)\n",
+ p->selId, p));
+ p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
+ assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside
+ ** of sqlite3DbMallocRawNN() called from
+ ** sqlite3SrcListAppend() */
if( p->pSrc ){
+ Table *pTab2;
p->pSrc->a[0].pSelect = pSub;
+ p->pSrc->a[0].fg.isCorrelated = 1;
sqlite3SrcListAssignCursors(pParse, p->pSrc);
- if( sqlite3ExpandSubquery(pParse, &p->pSrc->a[0]) ){
+ pSub->selFlags |= SF_Expanded|SF_OrderByReqd;
+ pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE);
+ pSub->selFlags |= (selFlags & SF_Aggregate);
+ if( pTab2==0 ){
+ /* Might actually be some other kind of error, but in that case
+ ** pParse->nErr will be set, so if SQLITE_NOMEM is set, we will get
+ ** the correct error message regardless. */
rc = SQLITE_NOMEM;
}else{
- pSub->selFlags |= SF_Expanded;
- p->selFlags &= ~SF_Aggregate;
- sqlite3SelectPrep(pParse, pSub, 0);
+ memcpy(pTab, pTab2, sizeof(Table));
+ pTab->tabFlags |= TF_Ephemeral;
+ p->pSrc->a[0].pTab = pTab;
+ pTab = pTab2;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = sqlite3WindowExtraAggFuncDepth;
+ w.xSelectCallback = sqlite3WalkerDepthIncrease;
+ w.xSelectCallback2 = sqlite3WalkerDepthDecrease;
+ sqlite3WalkSelect(&w, pSub);
}
-
- sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, pSublist->nExpr);
}else{
sqlite3SelectDelete(db, pSub);
}
if( db->mallocFailed ) rc = SQLITE_NOMEM;
+
+ /* Defer deleting the temporary table pTab because if an error occurred,
+ ** there could still be references to that table embedded in the
+ ** result-set or ORDER BY clause of the SELECT statement p. */
+ sqlite3ParserAddCleanup(pParse, sqlite3DbFree, pTab);
}
+ assert( rc==SQLITE_OK || pParse->nErr!=0 );
return rc;
}
/*
+** Unlink the Window object from the Select to which it is attached,
+** if it is attached.
+*/
+SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window *p){
+ if( p->ppThis ){
+ *p->ppThis = p->pNextWin;
+ if( p->pNextWin ) p->pNextWin->ppThis = p->ppThis;
+ p->ppThis = 0;
+ }
+}
+
+/*
** Free the Window object passed as the second argument.
*/
SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3 *db, Window *p){
if( p ){
+ sqlite3WindowUnlinkFromSelect(p);
sqlite3ExprDelete(db, p->pFilter);
sqlite3ExprListDelete(db, p->pPartition);
sqlite3ExprListDelete(db, p->pOrderBy);
sqlite3ExprDelete(db, p->pEnd);
sqlite3ExprDelete(db, p->pStart);
sqlite3DbFree(db, p->zName);
+ sqlite3DbFree(db, p->zBase);
sqlite3DbFree(db, p);
}
}
@@ -145430,6 +168967,7 @@ SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p){
*/
static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){
if( 0==sqlite3ExprIsConstant(pExpr) ){
+ if( IN_RENAME_OBJECT ) sqlite3RenameExprUnmap(pParse, pExpr);
sqlite3ExprDelete(pParse->db, pExpr);
pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0);
}
@@ -145441,16 +168979,18 @@ static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){
*/
SQLITE_PRIVATE Window *sqlite3WindowAlloc(
Parse *pParse, /* Parsing context */
- int eType, /* Frame type. TK_RANGE or TK_ROWS */
+ int eType, /* Frame type. TK_RANGE, TK_ROWS, TK_GROUPS, or 0 */
int eStart, /* Start type: CURRENT, PRECEDING, FOLLOWING, UNBOUNDED */
Expr *pStart, /* Start window size if TK_PRECEDING or FOLLOWING */
int eEnd, /* End type: CURRENT, FOLLOWING, TK_UNBOUNDED, PRECEDING */
- Expr *pEnd /* End window size if TK_FOLLOWING or PRECEDING */
+ Expr *pEnd, /* End window size if TK_FOLLOWING or PRECEDING */
+ u8 eExclude /* EXCLUDE clause */
){
Window *pWin = 0;
+ int bImplicitFrame = 0;
/* Parser assures the following: */
- assert( eType==TK_RANGE || eType==TK_ROWS );
+ assert( eType==0 || eType==TK_RANGE || eType==TK_ROWS || eType==TK_GROUPS );
assert( eStart==TK_CURRENT || eStart==TK_PRECEDING
|| eStart==TK_UNBOUNDED || eStart==TK_FOLLOWING );
assert( eEnd==TK_CURRENT || eEnd==TK_FOLLOWING
@@ -145458,13 +168998,9 @@ SQLITE_PRIVATE Window *sqlite3WindowAlloc(
assert( (eStart==TK_PRECEDING || eStart==TK_FOLLOWING)==(pStart!=0) );
assert( (eEnd==TK_FOLLOWING || eEnd==TK_PRECEDING)==(pEnd!=0) );
-
- /* If a frame is declared "RANGE" (not "ROWS"), then it may not use
- ** either "<expr> PRECEDING" or "<expr> FOLLOWING".
- */
- if( eType==TK_RANGE && (pStart!=0 || pEnd!=0) ){
- sqlite3ErrorMsg(pParse, "RANGE must use only UNBOUNDED or CURRENT ROW");
- goto windowAllocErr;
+ if( eType==0 ){
+ bImplicitFrame = 1;
+ eType = TK_RANGE;
}
/* Additionally, the
@@ -145484,15 +169020,20 @@ SQLITE_PRIVATE Window *sqlite3WindowAlloc(
if( (eStart==TK_CURRENT && eEnd==TK_PRECEDING)
|| (eStart==TK_FOLLOWING && (eEnd==TK_PRECEDING || eEnd==TK_CURRENT))
){
- sqlite3ErrorMsg(pParse, "unsupported frame delimiter for ROWS");
+ sqlite3ErrorMsg(pParse, "unsupported frame specification");
goto windowAllocErr;
}
pWin = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
if( pWin==0 ) goto windowAllocErr;
- pWin->eType = eType;
+ pWin->eFrmType = eType;
pWin->eStart = eStart;
pWin->eEnd = eEnd;
+ if( eExclude==0 && OptimizationDisabled(pParse->db, SQLITE_WindowFunc) ){
+ eExclude = TK_NO;
+ }
+ pWin->eExclude = eExclude;
+ pWin->bImplicitFrame = bImplicitFrame;
pWin->pEnd = sqlite3WindowOffsetExpr(pParse, pEnd);
pWin->pStart = sqlite3WindowOffsetExpr(pParse, pStart);
return pWin;
@@ -145504,22 +169045,83 @@ windowAllocErr:
}
/*
+** Attach PARTITION and ORDER BY clauses pPartition and pOrderBy to window
+** pWin. Also, if parameter pBase is not NULL, set pWin->zBase to the
+** equivalent nul-terminated string.
+*/
+SQLITE_PRIVATE Window *sqlite3WindowAssemble(
+ Parse *pParse,
+ Window *pWin,
+ ExprList *pPartition,
+ ExprList *pOrderBy,
+ Token *pBase
+){
+ if( pWin ){
+ pWin->pPartition = pPartition;
+ pWin->pOrderBy = pOrderBy;
+ if( pBase ){
+ pWin->zBase = sqlite3DbStrNDup(pParse->db, pBase->z, pBase->n);
+ }
+ }else{
+ sqlite3ExprListDelete(pParse->db, pPartition);
+ sqlite3ExprListDelete(pParse->db, pOrderBy);
+ }
+ return pWin;
+}
+
+/*
+** Window *pWin has just been created from a WINDOW clause. Token pBase
+** is the base window. Earlier windows from the same WINDOW clause are
+** stored in the linked list starting at pWin->pNextWin. This function
+** either updates *pWin according to the base specification, or else
+** leaves an error in pParse.
+*/
+SQLITE_PRIVATE void sqlite3WindowChain(Parse *pParse, Window *pWin, Window *pList){
+ if( pWin->zBase ){
+ sqlite3 *db = pParse->db;
+ Window *pExist = windowFind(pParse, pList, pWin->zBase);
+ if( pExist ){
+ const char *zErr = 0;
+ /* Check for errors */
+ if( pWin->pPartition ){
+ zErr = "PARTITION clause";
+ }else if( pExist->pOrderBy && pWin->pOrderBy ){
+ zErr = "ORDER BY clause";
+ }else if( pExist->bImplicitFrame==0 ){
+ zErr = "frame specification";
+ }
+ if( zErr ){
+ sqlite3ErrorMsg(pParse,
+ "cannot override %s of window: %s", zErr, pWin->zBase
+ );
+ }else{
+ pWin->pPartition = sqlite3ExprListDup(db, pExist->pPartition, 0);
+ if( pExist->pOrderBy ){
+ assert( pWin->pOrderBy==0 );
+ pWin->pOrderBy = sqlite3ExprListDup(db, pExist->pOrderBy, 0);
+ }
+ sqlite3DbFree(db, pWin->zBase);
+ pWin->zBase = 0;
+ }
+ }
+ }
+}
+
+/*
** Attach window object pWin to expression p.
*/
SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
if( p ){
assert( p->op==TK_FUNCTION );
- /* This routine is only called for the parser. If pWin was not
- ** allocated due to an OOM, then the parser would fail before ever
- ** invoking this routine */
- if( ALWAYS(pWin) ){
- p->y.pWin = pWin;
- ExprSetProperty(p, EP_WinFunc);
- pWin->pOwner = p;
- if( p->flags & EP_Distinct ){
- sqlite3ErrorMsg(pParse,
- "DISTINCT is not supported for window functions");
- }
+ assert( pWin );
+ assert( ExprIsFullSize(p) );
+ p->y.pWin = pWin;
+ ExprSetProperty(p, EP_WinFunc|EP_FullSize);
+ pWin->pOwner = p;
+ if( (p->flags & EP_Distinct) && pWin->eFrmType!=TK_FILTER ){
+ sqlite3ErrorMsg(pParse,
+ "DISTINCT is not supported for window functions"
+ );
}
}else{
sqlite3WindowDelete(pParse->db, pWin);
@@ -145527,17 +169129,58 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
}
/*
-** Return 0 if the two window objects are identical, or non-zero otherwise.
-** Identical window objects can be processed in a single scan.
+** Possibly link window pWin into the list at pSel->pWin (window functions
+** to be processed as part of SELECT statement pSel). The window is linked
+** in if either (a) there are no other windows already linked to this
+** SELECT, or (b) the windows already linked use a compatible window frame.
*/
-SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
- if( p1->eType!=p2->eType ) return 1;
+SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin){
+ if( pSel ){
+ if( 0==pSel->pWin || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0) ){
+ pWin->pNextWin = pSel->pWin;
+ if( pSel->pWin ){
+ pSel->pWin->ppThis = &pWin->pNextWin;
+ }
+ pSel->pWin = pWin;
+ pWin->ppThis = &pSel->pWin;
+ }else{
+ if( sqlite3ExprListCompare(pWin->pPartition, pSel->pWin->pPartition,-1) ){
+ pSel->selFlags |= SF_MultiPart;
+ }
+ }
+ }
+}
+
+/*
+** Return 0 if the two window objects are identical, 1 if they are
+** different, or 2 if it cannot be determined if the objects are identical
+** or not. Identical window objects can be processed in a single scan.
+*/
+SQLITE_PRIVATE int sqlite3WindowCompare(
+ const Parse *pParse,
+ const Window *p1,
+ const Window *p2,
+ int bFilter
+){
+ int res;
+ if( NEVER(p1==0) || NEVER(p2==0) ) return 1;
+ if( p1->eFrmType!=p2->eFrmType ) return 1;
if( p1->eStart!=p2->eStart ) return 1;
if( p1->eEnd!=p2->eEnd ) return 1;
+ if( p1->eExclude!=p2->eExclude ) return 1;
if( sqlite3ExprCompare(pParse, p1->pStart, p2->pStart, -1) ) return 1;
if( sqlite3ExprCompare(pParse, p1->pEnd, p2->pEnd, -1) ) return 1;
- if( sqlite3ExprListCompare(p1->pPartition, p2->pPartition, -1) ) return 1;
- if( sqlite3ExprListCompare(p1->pOrderBy, p2->pOrderBy, -1) ) return 1;
+ if( (res = sqlite3ExprListCompare(p1->pPartition, p2->pPartition, -1)) ){
+ return res;
+ }
+ if( (res = sqlite3ExprListCompare(p1->pOrderBy, p2->pOrderBy, -1)) ){
+ return res;
+ }
+ if( bFilter ){
+ if( (res = sqlite3ExprCompare(pParse, p1->pFilter, p2->pFilter, -1)) ){
+ return res;
+ }
+ }
return 0;
}
@@ -145547,19 +169190,41 @@ SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
** to begin iterating through the sub-query results. It is used to allocate
** and initialize registers and cursors used by sqlite3WindowCodeStep().
*/
-SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
+SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){
+ int nEphExpr = pSelect->pSrc->a[0].pSelect->pEList->nExpr;
+ Window *pMWin = pSelect->pWin;
Window *pWin;
Vdbe *v = sqlite3GetVdbe(pParse);
- int nPart = (pMWin->pPartition ? pMWin->pPartition->nExpr : 0);
- nPart += (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
- if( nPart ){
+
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, nEphExpr);
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+1, pMWin->iEphCsr);
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+2, pMWin->iEphCsr);
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+3, pMWin->iEphCsr);
+
+ /* Allocate registers to use for PARTITION BY values, if any. Initialize
+ ** said registers to NULL. */
+ if( pMWin->pPartition ){
+ int nExpr = pMWin->pPartition->nExpr;
pMWin->regPart = pParse->nMem+1;
- pParse->nMem += nPart;
- sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nPart-1);
+ pParse->nMem += nExpr;
+ sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nExpr-1);
+ }
+
+ pMWin->regOne = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regOne);
+
+ if( pMWin->eExclude ){
+ pMWin->regStartRowid = ++pParse->nMem;
+ pMWin->regEndRowid = ++pParse->nMem;
+ pMWin->csrApp = pParse->nTab++;
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regStartRowid);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regEndRowid);
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->csrApp, pMWin->iEphCsr);
+ return;
}
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- FuncDef *p = pWin->pFunc;
+ FuncDef *p = pWin->pWFunc;
if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){
/* The inline versions of min() and max() require a single ephemeral
** table and 3 registers. The registers are used as follows:
@@ -145568,14 +169233,17 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
** regApp+1: integer value used to ensure keys are unique
** regApp+2: output of MakeRecord
*/
- ExprList *pList = pWin->pOwner->x.pList;
- KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pList, 0, 0);
+ ExprList *pList;
+ KeyInfo *pKeyInfo;
+ assert( ExprUseXList(pWin->pOwner) );
+ pList = pWin->pOwner->x.pList;
+ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pList, 0, 0);
pWin->csrApp = pParse->nTab++;
pWin->regApp = pParse->nMem+1;
pParse->nMem += 3;
- if( pKeyInfo && pWin->pFunc->zName[1]=='i' ){
- assert( pKeyInfo->aSortOrder[0]==0 );
- pKeyInfo->aSortOrder[0] = 1;
+ if( pKeyInfo && pWin->pWFunc->zName[1]=='i' ){
+ assert( pKeyInfo->aSortFlags[0]==0 );
+ pKeyInfo->aSortFlags[0] = KEYINFO_ORDER_DESC;
}
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pWin->csrApp, 2);
sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO);
@@ -145584,20 +169252,24 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
else if( p->zName==nth_valueName || p->zName==first_valueName ){
/* Allocate two registers at pWin->regApp. These will be used to
** store the start and end index of the current frame. */
- assert( pMWin->iEphCsr );
pWin->regApp = pParse->nMem+1;
pWin->csrApp = pParse->nTab++;
pParse->nMem += 2;
sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr);
}
else if( p->zName==leadName || p->zName==lagName ){
- assert( pMWin->iEphCsr );
pWin->csrApp = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr);
}
}
}
+#define WINDOW_STARTING_INT 0
+#define WINDOW_ENDING_INT 1
+#define WINDOW_NTH_VALUE_INT 2
+#define WINDOW_STARTING_NUM 3
+#define WINDOW_ENDING_NUM 4
+
/*
** A "PRECEDING <expr>" (eCond==0) or "FOLLOWING <expr>" (eCond==1) or the
** value of the second argument to nth_value() (eCond==2) has just been
@@ -145605,25 +169277,44 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
** code to check that the value is a non-negative integer and throws an
** exception if it is not.
*/
-static void windowCheckIntValue(Parse *pParse, int reg, int eCond){
+static void windowCheckValue(Parse *pParse, int reg, int eCond){
static const char *azErr[] = {
"frame starting offset must be a non-negative integer",
"frame ending offset must be a non-negative integer",
- "second argument to nth_value must be a positive integer"
+ "second argument to nth_value must be a positive integer",
+ "frame starting offset must be a non-negative number",
+ "frame ending offset must be a non-negative number",
};
- static int aOp[] = { OP_Ge, OP_Ge, OP_Gt };
+ static int aOp[] = { OP_Ge, OP_Ge, OP_Gt, OP_Ge, OP_Ge };
Vdbe *v = sqlite3GetVdbe(pParse);
int regZero = sqlite3GetTempReg(pParse);
- assert( eCond==0 || eCond==1 || eCond==2 );
+ assert( eCond>=0 && eCond<ArraySize(azErr) );
sqlite3VdbeAddOp2(v, OP_Integer, 0, regZero);
- sqlite3VdbeAddOp2(v, OP_MustBeInt, reg, sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverageIf(v, eCond==0);
- VdbeCoverageIf(v, eCond==1);
- VdbeCoverageIf(v, eCond==2);
+ if( eCond>=WINDOW_STARTING_NUM ){
+ int regString = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC);
+ sqlite3VdbeAddOp3(v, OP_Ge, regString, sqlite3VdbeCurrentAddr(v)+2, reg);
+ sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC|SQLITE_JUMPIFNULL);
+ VdbeCoverage(v);
+ assert( eCond==3 || eCond==4 );
+ VdbeCoverageIf(v, eCond==3);
+ VdbeCoverageIf(v, eCond==4);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_MustBeInt, reg, sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
+ assert( eCond==0 || eCond==1 || eCond==2 );
+ VdbeCoverageIf(v, eCond==0);
+ VdbeCoverageIf(v, eCond==1);
+ VdbeCoverageIf(v, eCond==2);
+ }
sqlite3VdbeAddOp3(v, aOp[eCond], regZero, sqlite3VdbeCurrentAddr(v)+2, reg);
- VdbeCoverageNeverNullIf(v, eCond==0);
- VdbeCoverageNeverNullIf(v, eCond==1);
+ sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC);
+ VdbeCoverageNeverNullIf(v, eCond==0); /* NULL case captured by */
+ VdbeCoverageNeverNullIf(v, eCond==1); /* the OP_MustBeInt */
VdbeCoverageNeverNullIf(v, eCond==2);
+ VdbeCoverageNeverNullIf(v, eCond==3); /* NULL case caught by */
+ VdbeCoverageNeverNullIf(v, eCond==4); /* the OP_Ge */
+ sqlite3MayAbort(pParse);
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_ERROR, OE_Abort);
sqlite3VdbeAppendP4(v, (void*)azErr[eCond], P4_STATIC);
sqlite3ReleaseTempReg(pParse, regZero);
@@ -145634,13 +169325,118 @@ static void windowCheckIntValue(Parse *pParse, int reg, int eCond){
** with the object passed as the only argument to this function.
*/
static int windowArgCount(Window *pWin){
- ExprList *pList = pWin->pOwner->x.pList;
+ const ExprList *pList;
+ assert( ExprUseXList(pWin->pOwner) );
+ pList = pWin->pOwner->x.pList;
return (pList ? pList->nExpr : 0);
}
+typedef struct WindowCodeArg WindowCodeArg;
+typedef struct WindowCsrAndReg WindowCsrAndReg;
+
+/*
+** See comments above struct WindowCodeArg.
+*/
+struct WindowCsrAndReg {
+ int csr; /* Cursor number */
+ int reg; /* First in array of peer values */
+};
+
+/*
+** A single instance of this structure is allocated on the stack by
+** sqlite3WindowCodeStep() and a pointer to it passed to the various helper
+** routines. This is to reduce the number of arguments required by each
+** helper function.
+**
+** regArg:
+** Each window function requires an accumulator register (just as an
+** ordinary aggregate function does). This variable is set to the first
+** in an array of accumulator registers - one for each window function
+** in the WindowCodeArg.pMWin list.
+**
+** eDelete:
+** The window functions implementation sometimes caches the input rows
+** that it processes in a temporary table. If it is not zero, this
+** variable indicates when rows may be removed from the temp table (in
+** order to reduce memory requirements - it would always be safe just
+** to leave them there). Possible values for eDelete are:
+**
+** WINDOW_RETURN_ROW:
+** An input row can be discarded after it is returned to the caller.
+**
+** WINDOW_AGGINVERSE:
+** An input row can be discarded after the window functions xInverse()
+** callbacks have been invoked in it.
+**
+** WINDOW_AGGSTEP:
+** An input row can be discarded after the window functions xStep()
+** callbacks have been invoked in it.
+**
+** start,current,end
+** Consider a window-frame similar to the following:
+**
+** (ORDER BY a, b GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING)
+**
+** The windows functions implementation caches the input rows in a temp
+** table, sorted by "a, b" (it actually populates the cache lazily, and
+** aggressively removes rows once they are no longer required, but that's
+** a mere detail). It keeps three cursors open on the temp table. One
+** (current) that points to the next row to return to the query engine
+** once its window function values have been calculated. Another (end)
+** points to the next row to call the xStep() method of each window function
+** on (so that it is 2 groups ahead of current). And a third (start) that
+** points to the next row to call the xInverse() method of each window
+** function on.
+**
+** Each cursor (start, current and end) consists of a VDBE cursor
+** (WindowCsrAndReg.csr) and an array of registers (starting at
+** WindowCodeArg.reg) that always contains a copy of the peer values
+** read from the corresponding cursor.
+**
+** Depending on the window-frame in question, all three cursors may not
+** be required. In this case both WindowCodeArg.csr and reg are set to
+** 0.
+*/
+struct WindowCodeArg {
+ Parse *pParse; /* Parse context */
+ Window *pMWin; /* First in list of functions being processed */
+ Vdbe *pVdbe; /* VDBE object */
+ int addrGosub; /* OP_Gosub to this address to return one row */
+ int regGosub; /* Register used with OP_Gosub(addrGosub) */
+ int regArg; /* First in array of accumulator registers */
+ int eDelete; /* See above */
+ int regRowid;
+
+ WindowCsrAndReg start;
+ WindowCsrAndReg current;
+ WindowCsrAndReg end;
+};
+
+/*
+** Generate VM code to read the window frames peer values from cursor csr into
+** an array of registers starting at reg.
+*/
+static void windowReadPeerValues(
+ WindowCodeArg *p,
+ int csr,
+ int reg
+){
+ Window *pMWin = p->pMWin;
+ ExprList *pOrderBy = pMWin->pOrderBy;
+ if( pOrderBy ){
+ Vdbe *v = sqlite3GetVdbe(p->pParse);
+ ExprList *pPart = pMWin->pPartition;
+ int iColOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
+ int i;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, csr, iColOff+i, reg+i);
+ }
+ }
+}
+
/*
-** Generate VM code to invoke either xStep() (if bInverse is 0) or
-** xInverse (if bInverse is non-zero) for each window function in the
+** Generate VM code to invoke either xStep() (if bInverse is 0) or
+** xInverse (if bInverse is non-zero) for each window function in the
** linked list starting at pMWin. Or, for built-in window functions
** that do not use the standard function API, generate the required
** inline VM code.
@@ -145658,41 +169454,39 @@ static int windowArgCount(Window *pWin){
** number of rows in the current partition.
*/
static void windowAggStep(
- Parse *pParse,
+ WindowCodeArg *p,
Window *pMWin, /* Linked list of window functions */
int csr, /* Read arguments from this cursor */
int bInverse, /* True to invoke xInverse instead of xStep */
- int reg, /* Array of registers */
- int regPartSize /* Register containing size of partition */
+ int reg /* Array of registers */
){
+ Parse *pParse = p->pParse;
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- int flags = pWin->pFunc->funcFlags;
+ FuncDef *pFunc = pWin->pWFunc;
int regArg;
- int nArg = windowArgCount(pWin);
+ int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin);
+ int i;
- if( csr>=0 ){
- int i;
- for(i=0; i<nArg; i++){
+ assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED );
+
+ /* All OVER clauses in the same window function aggregate step must
+ ** be the same. */
+ assert( pWin==pMWin || sqlite3WindowCompare(pParse,pWin,pMWin,0)!=1 );
+
+ for(i=0; i<nArg; i++){
+ if( i!=1 || pFunc->zName!=nth_valueName ){
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i);
}
- regArg = reg;
- if( flags & SQLITE_FUNC_WINDOW_SIZE ){
- if( nArg==0 ){
- regArg = regPartSize;
- }else{
- sqlite3VdbeAddOp2(v, OP_SCopy, regPartSize, reg+nArg);
- }
- nArg++;
- }
- }else{
- assert( !(flags & SQLITE_FUNC_WINDOW_SIZE) );
- regArg = reg + pWin->iArgCol;
}
+ regArg = reg;
- if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX)
- && pWin->eStart!=TK_UNBOUNDED
+ if( pMWin->regStartRowid==0
+ && (pFunc->funcFlags & SQLITE_FUNC_MINMAX)
+ && (pWin->eStart!=TK_UNBOUNDED)
){
int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg);
VdbeCoverage(v);
@@ -145709,147 +169503,203 @@ static void windowAggStep(
}
sqlite3VdbeJumpHere(v, addrIsNull);
}else if( pWin->regApp ){
- assert( pWin->pFunc->zName==nth_valueName
- || pWin->pFunc->zName==first_valueName
+ assert( pFunc->zName==nth_valueName
+ || pFunc->zName==first_valueName
);
assert( bInverse==0 || bInverse==1 );
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1);
- }else if( pWin->pFunc->zName==leadName
- || pWin->pFunc->zName==lagName
- ){
- /* no-op */
- }else{
+ }else if( pFunc->xSFunc!=noopStepFunc ){
int addrIf = 0;
if( pWin->pFilter ){
int regTmp;
- assert( nArg==0 || nArg==pWin->pOwner->x.pList->nExpr );
- assert( nArg || pWin->pOwner->x.pList==0 );
- if( csr>0 ){
- regTmp = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
- }else{
- regTmp = regArg + nArg;
- }
+ assert( ExprUseXList(pWin->pOwner) );
+ assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr );
+ assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 );
+ regTmp = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1);
VdbeCoverage(v);
- if( csr>0 ){
- sqlite3ReleaseTempReg(pParse, regTmp);
+ sqlite3ReleaseTempReg(pParse, regTmp);
+ }
+
+ if( pWin->bExprArgs ){
+ int iOp = sqlite3VdbeCurrentAddr(v);
+ int iEnd;
+
+ assert( ExprUseXList(pWin->pOwner) );
+ nArg = pWin->pOwner->x.pList->nExpr;
+ regArg = sqlite3GetTempRange(pParse, nArg);
+ sqlite3ExprCodeExprList(pParse, pWin->pOwner->x.pList, regArg, 0, 0);
+
+ for(iEnd=sqlite3VdbeCurrentAddr(v); iOp<iEnd; iOp++){
+ VdbeOp *pOp = sqlite3VdbeGetOp(v, iOp);
+ if( pOp->opcode==OP_Column && pOp->p1==pMWin->iEphCsr ){
+ pOp->p1 = csr;
+ }
}
}
- if( pWin->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
+ if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
CollSeq *pColl;
assert( nArg>0 );
+ assert( ExprUseXList(pWin->pOwner) );
pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr);
sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ);
}
- sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
+ sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
bInverse, regArg, pWin->regAccum);
- sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+ sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
+ if( pWin->bExprArgs ){
+ sqlite3ReleaseTempRange(pParse, regArg, nArg);
+ }
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
}
}
}
/*
-** Generate VM code to invoke either xValue() (bFinal==0) or xFinalize()
-** (bFinal==1) for each window function in the linked list starting at
+** Values that may be passed as the second argument to windowCodeOp().
+*/
+#define WINDOW_RETURN_ROW 1
+#define WINDOW_AGGINVERSE 2
+#define WINDOW_AGGSTEP 3
+
+/*
+** Generate VM code to invoke either xValue() (bFin==0) or xFinalize()
+** (bFin==1) for each window function in the linked list starting at
** pMWin. Or, for built-in window-functions that do not use the standard
** API, generate the equivalent VM code.
*/
-static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){
+static void windowAggFinal(WindowCodeArg *p, int bFin){
+ Parse *pParse = p->pParse;
+ Window *pMWin = p->pMWin;
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX)
- && pWin->eStart!=TK_UNBOUNDED
+ if( pMWin->regStartRowid==0
+ && (pWin->pWFunc->funcFlags & SQLITE_FUNC_MINMAX)
+ && (pWin->eStart!=TK_UNBOUNDED)
){
sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
sqlite3VdbeAddOp1(v, OP_Last, pWin->csrApp);
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_Column, pWin->csrApp, 0, pWin->regResult);
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
- if( bFinal ){
- sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp);
- }
}else if( pWin->regApp ){
+ assert( pMWin->regStartRowid==0 );
}else{
- if( bFinal ){
- sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, windowArgCount(pWin));
- sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+ int nArg = windowArgCount(pWin);
+ if( bFin ){
+ sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, nArg);
+ sqlite3VdbeAppendP4(v, pWin->pWFunc, P4_FUNCDEF);
sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
}else{
- sqlite3VdbeAddOp3(v, OP_AggValue, pWin->regAccum, windowArgCount(pWin),
- pWin->regResult);
- sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+ sqlite3VdbeAddOp3(v, OP_AggValue,pWin->regAccum,nArg,pWin->regResult);
+ sqlite3VdbeAppendP4(v, pWin->pWFunc, P4_FUNCDEF);
}
}
}
}
/*
-** This function generates VM code to invoke the sub-routine at address
-** lblFlushPart once for each partition with the entire partition cached in
-** the Window.iEphCsr temp table.
+** Generate code to calculate the current values of all window functions in the
+** p->pMWin list by doing a full scan of the current window frame. Store the
+** results in the Window.regResult registers, ready to return the upper
+** layer.
*/
-static void windowPartitionCache(
- Parse *pParse,
- Select *p, /* The rewritten SELECT statement */
- WhereInfo *pWInfo, /* WhereInfo to call WhereEnd() on */
- int regFlushPart, /* Register to use with Gosub lblFlushPart */
- int lblFlushPart, /* Subroutine to Gosub to */
- int *pRegSize /* OUT: Register containing partition size */
-){
- Window *pMWin = p->pWin;
- Vdbe *v = sqlite3GetVdbe(pParse);
- int iSubCsr = p->pSrc->a[0].iCursor;
- int nSub = p->pSrc->a[0].pTab->nCol;
- int k;
+static void windowFullScan(WindowCodeArg *p){
+ Window *pWin;
+ Parse *pParse = p->pParse;
+ Window *pMWin = p->pMWin;
+ Vdbe *v = p->pVdbe;
- int reg = pParse->nMem+1;
- int regRecord = reg+nSub;
- int regRowid = regRecord+1;
+ int regCRowid = 0; /* Current rowid value */
+ int regCPeer = 0; /* Current peer values */
+ int regRowid = 0; /* AggStep rowid value */
+ int regPeer = 0; /* AggStep peer values */
- *pRegSize = regRowid;
- pParse->nMem += nSub + 2;
+ int nPeer;
+ int lblNext;
+ int lblBrk;
+ int addrNext;
+ int csr;
- /* Load the column values for the row returned by the sub-select
- ** into an array of registers starting at reg. */
- for(k=0; k<nSub; k++){
- sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
+ VdbeModuleComment((v, "windowFullScan begin"));
+
+ assert( pMWin!=0 );
+ csr = pMWin->csrApp;
+ nPeer = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
+
+ lblNext = sqlite3VdbeMakeLabel(pParse);
+ lblBrk = sqlite3VdbeMakeLabel(pParse);
+
+ regCRowid = sqlite3GetTempReg(pParse);
+ regRowid = sqlite3GetTempReg(pParse);
+ if( nPeer ){
+ regCPeer = sqlite3GetTempRange(pParse, nPeer);
+ regPeer = sqlite3GetTempRange(pParse, nPeer);
}
- sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, nSub, regRecord);
- /* Check if this is the start of a new partition. If so, call the
- ** flush_partition sub-routine. */
- if( pMWin->pPartition ){
+ sqlite3VdbeAddOp2(v, OP_Rowid, pMWin->iEphCsr, regCRowid);
+ windowReadPeerValues(p, pMWin->iEphCsr, regCPeer);
+
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
+ }
+
+ sqlite3VdbeAddOp3(v, OP_SeekGE, csr, lblBrk, pMWin->regStartRowid);
+ VdbeCoverage(v);
+ addrNext = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp2(v, OP_Rowid, csr, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Gt, pMWin->regEndRowid, lblBrk, regRowid);
+ VdbeCoverageNeverNull(v);
+
+ if( pMWin->eExclude==TK_CURRENT ){
+ sqlite3VdbeAddOp3(v, OP_Eq, regCRowid, lblNext, regRowid);
+ VdbeCoverageNeverNull(v);
+ }else if( pMWin->eExclude!=TK_NO ){
int addr;
- ExprList *pPart = pMWin->pPartition;
- int nPart = pPart->nExpr;
- int regNewPart = reg + pMWin->nBufferCol;
- KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
+ int addrEq = 0;
+ KeyInfo *pKeyInfo = 0;
- addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart);
- sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
- sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2);
- VdbeCoverageEqNe(v);
- sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1);
- sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
- VdbeComment((v, "call flush_partition"));
+ if( pMWin->pOrderBy ){
+ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pMWin->pOrderBy, 0, 0);
+ }
+ if( pMWin->eExclude==TK_TIES ){
+ addrEq = sqlite3VdbeAddOp3(v, OP_Eq, regCRowid, 0, regRowid);
+ VdbeCoverageNeverNull(v);
+ }
+ if( pKeyInfo ){
+ windowReadPeerValues(p, csr, regPeer);
+ sqlite3VdbeAddOp3(v, OP_Compare, regPeer, regCPeer, nPeer);
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
+ addr = sqlite3VdbeCurrentAddr(v)+1;
+ sqlite3VdbeAddOp3(v, OP_Jump, addr, lblNext, addr);
+ VdbeCoverageEqNe(v);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblNext);
+ }
+ if( addrEq ) sqlite3VdbeJumpHere(v, addrEq);
}
- /* Buffer the current row in the ephemeral table. */
- sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
+ windowAggStep(p, pMWin, csr, 0, p->regArg);
- /* End of the input loop */
- sqlite3WhereEnd(pWInfo);
+ sqlite3VdbeResolveLabel(v, lblNext);
+ sqlite3VdbeAddOp2(v, OP_Next, csr, addrNext);
+ VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addrNext-1);
+ sqlite3VdbeJumpHere(v, addrNext+1);
+ sqlite3ReleaseTempReg(pParse, regRowid);
+ sqlite3ReleaseTempReg(pParse, regCRowid);
+ if( nPeer ){
+ sqlite3ReleaseTempRange(pParse, regPeer, nPeer);
+ sqlite3ReleaseTempRange(pParse, regCPeer, nPeer);
+ }
- /* Invoke "flush_partition" to deal with the final (or only) partition */
- sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
- VdbeComment((v, "call flush_partition"));
+ windowAggFinal(p, 1);
+ VdbeModuleComment((v, "windowFullScan end"));
}
/*
@@ -145865,110 +169715,75 @@ static void windowPartitionCache(
** lag()
** lead()
*/
-static void windowReturnOneRow(
- Parse *pParse,
- Window *pMWin,
- int regGosub,
- int addrGosub
-){
- Vdbe *v = sqlite3GetVdbe(pParse);
- Window *pWin;
- for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- FuncDef *pFunc = pWin->pFunc;
- if( pFunc->zName==nth_valueName
- || pFunc->zName==first_valueName
- ){
- int csr = pWin->csrApp;
- int lbl = sqlite3VdbeMakeLabel(v);
- int tmpReg = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
+static void windowReturnOneRow(WindowCodeArg *p){
+ Window *pMWin = p->pMWin;
+ Vdbe *v = p->pVdbe;
- if( pFunc->zName==nth_valueName ){
- sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+1,tmpReg);
- windowCheckIntValue(pParse, tmpReg, 2);
- }else{
- sqlite3VdbeAddOp2(v, OP_Integer, 1, tmpReg);
- }
- sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg);
- sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg);
- VdbeCoverageNeverNull(v);
- sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, 0, tmpReg);
- VdbeCoverageNeverTaken(v);
- sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult);
- sqlite3VdbeResolveLabel(v, lbl);
- sqlite3ReleaseTempReg(pParse, tmpReg);
- }
- else if( pFunc->zName==leadName || pFunc->zName==lagName ){
- int nArg = pWin->pOwner->x.pList->nExpr;
- int iEph = pMWin->iEphCsr;
- int csr = pWin->csrApp;
- int lbl = sqlite3VdbeMakeLabel(v);
- int tmpReg = sqlite3GetTempReg(pParse);
-
- if( nArg<3 ){
+ if( pMWin->regStartRowid ){
+ windowFullScan(p);
+ }else{
+ Parse *pParse = p->pParse;
+ Window *pWin;
+
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+ FuncDef *pFunc = pWin->pWFunc;
+ assert( ExprUseXList(pWin->pOwner) );
+ if( pFunc->zName==nth_valueName
+ || pFunc->zName==first_valueName
+ ){
+ int csr = pWin->csrApp;
+ int lbl = sqlite3VdbeMakeLabel(pParse);
+ int tmpReg = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
- }else{
- sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+2, pWin->regResult);
- }
- sqlite3VdbeAddOp2(v, OP_Rowid, iEph, tmpReg);
- if( nArg<2 ){
- int val = (pFunc->zName==leadName ? 1 : -1);
- sqlite3VdbeAddOp2(v, OP_AddImm, tmpReg, val);
- }else{
- int op = (pFunc->zName==leadName ? OP_Add : OP_Subtract);
- int tmpReg2 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+1, tmpReg2);
- sqlite3VdbeAddOp3(v, op, tmpReg2, tmpReg, tmpReg);
- sqlite3ReleaseTempReg(pParse, tmpReg2);
- }
- sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult);
- sqlite3VdbeResolveLabel(v, lbl);
- sqlite3ReleaseTempReg(pParse, tmpReg);
- }
- }
- sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
-}
+ if( pFunc->zName==nth_valueName ){
+ sqlite3VdbeAddOp3(v, OP_Column,pMWin->iEphCsr,pWin->iArgCol+1,tmpReg);
+ windowCheckValue(pParse, tmpReg, 2);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, tmpReg);
+ }
+ sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg);
+ sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg);
+ VdbeCoverageNeverNull(v);
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, 0, tmpReg);
+ VdbeCoverageNeverTaken(v);
+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult);
+ sqlite3VdbeResolveLabel(v, lbl);
+ sqlite3ReleaseTempReg(pParse, tmpReg);
+ }
+ else if( pFunc->zName==leadName || pFunc->zName==lagName ){
+ int nArg = pWin->pOwner->x.pList->nExpr;
+ int csr = pWin->csrApp;
+ int lbl = sqlite3VdbeMakeLabel(pParse);
+ int tmpReg = sqlite3GetTempReg(pParse);
+ int iEph = pMWin->iEphCsr;
+
+ if( nArg<3 ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Column, iEph,pWin->iArgCol+2,pWin->regResult);
+ }
+ sqlite3VdbeAddOp2(v, OP_Rowid, iEph, tmpReg);
+ if( nArg<2 ){
+ int val = (pFunc->zName==leadName ? 1 : -1);
+ sqlite3VdbeAddOp2(v, OP_AddImm, tmpReg, val);
+ }else{
+ int op = (pFunc->zName==leadName ? OP_Add : OP_Subtract);
+ int tmpReg2 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+1, tmpReg2);
+ sqlite3VdbeAddOp3(v, op, tmpReg2, tmpReg, tmpReg);
+ sqlite3ReleaseTempReg(pParse, tmpReg2);
+ }
-/*
-** Invoke the code generated by windowReturnOneRow() and, optionally, the
-** xInverse() function for each window function, for one or more rows
-** from the Window.iEphCsr temp table. This routine generates VM code
-** similar to:
-**
-** while( regCtr>0 ){
-** regCtr--;
-** windowReturnOneRow()
-** if( bInverse ){
-** AggInverse
-** }
-** Next (Window.iEphCsr)
-** }
-*/
-static void windowReturnRows(
- Parse *pParse,
- Window *pMWin, /* List of window functions */
- int regCtr, /* Register containing number of rows */
- int regGosub, /* Register for Gosub addrGosub */
- int addrGosub, /* Address of sub-routine for ReturnOneRow */
- int regInvArg, /* Array of registers for xInverse args */
- int regInvSize /* Register containing size of partition */
-){
- int addr;
- Vdbe *v = sqlite3GetVdbe(pParse);
- windowAggFinal(pParse, pMWin, 0);
- addr = sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 ,1);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
- windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
- if( regInvArg ){
- windowAggStep(pParse, pMWin, pMWin->iEphCsr, 1, regInvArg, regInvSize);
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult);
+ sqlite3VdbeResolveLabel(v, lbl);
+ sqlite3ReleaseTempReg(pParse, tmpReg);
+ }
+ }
}
- sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, addr);
- VdbeCoverage(v);
- sqlite3VdbeJumpHere(v, addr+1); /* The OP_Goto */
+ sqlite3VdbeAddOp2(v, OP_Gosub, p->regGosub, p->addrGosub);
}
/*
@@ -145983,20 +169798,21 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){
int nArg = 0;
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- FuncDef *pFunc = pWin->pFunc;
+ FuncDef *pFunc = pWin->pWFunc;
+ assert( pWin->regAccum );
sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
nArg = MAX(nArg, windowArgCount(pWin));
- if( pFunc->zName==nth_valueName
- || pFunc->zName==first_valueName
- ){
- sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
- }
+ if( pMWin->regStartRowid==0 ){
+ if( pFunc->zName==nth_valueName || pFunc->zName==first_valueName ){
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
+ }
- if( (pFunc->funcFlags & SQLITE_FUNC_MINMAX) && pWin->csrApp ){
- assert( pWin->eStart!=TK_UNBOUNDED );
- sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
+ if( (pFunc->funcFlags & SQLITE_FUNC_MINMAX) && pWin->csrApp ){
+ assert( pWin->eStart!=TK_UNBOUNDED );
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
+ }
}
}
regArg = pParse->nMem+1;
@@ -146004,672 +169820,358 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){
return regArg;
}
+/*
+** Return true if the current frame should be cached in the ephemeral table,
+** even if there are no xInverse() calls required.
+*/
+static int windowCacheFrame(Window *pMWin){
+ Window *pWin;
+ if( pMWin->regStartRowid ) return 1;
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+ FuncDef *pFunc = pWin->pWFunc;
+ if( (pFunc->zName==nth_valueName)
+ || (pFunc->zName==first_valueName)
+ || (pFunc->zName==leadName)
+ || (pFunc->zName==lagName)
+ ){
+ return 1;
+ }
+ }
+ return 0;
+}
/*
-** This function does the work of sqlite3WindowCodeStep() for all "ROWS"
-** window frame types except for "BETWEEN UNBOUNDED PRECEDING AND CURRENT
-** ROW". Pseudo-code for each follows.
-**
-** ROWS BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING
-**
-** ...
-** if( new partition ){
-** Gosub flush_partition
-** }
-** Insert (record in eph-table)
-** sqlite3WhereEnd()
-** Gosub flush_partition
-**
-** flush_partition:
-** Once {
-** OpenDup (iEphCsr -> csrStart)
-** OpenDup (iEphCsr -> csrEnd)
-** }
-** regStart = <expr1> // PRECEDING expression
-** regEnd = <expr2> // FOLLOWING expression
-** if( regStart<0 || regEnd<0 ){ error! }
-** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done
-** Next(csrEnd) // if EOF skip Aggstep
-** Aggstep (csrEnd)
-** if( (regEnd--)<=0 ){
-** AggFinal (xValue)
-** Gosub addrGosub
-** Next(csr) // if EOF goto flush_partition_done
-** if( (regStart--)<=0 ){
-** AggInverse (csrStart)
-** Next(csrStart)
-** }
-** }
-** flush_partition_done:
-** ResetSorter (csr)
-** Return
-**
-** ROWS BETWEEN <expr> PRECEDING AND CURRENT ROW
-** ROWS BETWEEN CURRENT ROW AND <expr> FOLLOWING
-** ROWS BETWEEN UNBOUNDED PRECEDING AND <expr> FOLLOWING
-**
-** These are similar to the above. For "CURRENT ROW", intialize the
-** register to 0. For "UNBOUNDED PRECEDING" to infinity.
-**
-** ROWS BETWEEN <expr> PRECEDING AND UNBOUNDED FOLLOWING
-** ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-**
-** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done
-** while( 1 ){
-** Next(csrEnd) // Exit while(1) at EOF
-** Aggstep (csrEnd)
-** }
-** while( 1 ){
-** AggFinal (xValue)
-** Gosub addrGosub
-** Next(csr) // if EOF goto flush_partition_done
-** if( (regStart--)<=0 ){
-** AggInverse (csrStart)
-** Next(csrStart)
-** }
-** }
-**
-** For the "CURRENT ROW AND UNBOUNDED FOLLOWING" case, the final if()
-** condition is always true (as if regStart were initialized to 0).
-**
-** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-**
-** This is the only RANGE case handled by this routine. It modifies the
-** second while( 1 ) loop in "ROWS BETWEEN CURRENT ... UNBOUNDED..." to
-** be:
-**
-** while( 1 ){
-** AggFinal (xValue)
-** while( 1 ){
-** regPeer++
-** Gosub addrGosub
-** Next(csr) // if EOF goto flush_partition_done
-** if( new peer ) break;
-** }
-** while( (regPeer--)>0 ){
-** AggInverse (csrStart)
-** Next(csrStart)
-** }
-** }
-**
-** ROWS BETWEEN <expr> FOLLOWING AND <expr> FOLLOWING
-**
-** regEnd = regEnd - regStart
-** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done
-** Aggstep (csrEnd)
-** Next(csrEnd) // if EOF fall-through
-** if( (regEnd--)<=0 ){
-** if( (regStart--)<=0 ){
-** AggFinal (xValue)
-** Gosub addrGosub
-** Next(csr) // if EOF goto flush_partition_done
-** }
-** AggInverse (csrStart)
-** Next (csrStart)
-** }
-**
-** ROWS BETWEEN <expr> PRECEDING AND <expr> PRECEDING
-**
-** Replace the bit after "Rewind" in the above with:
-**
-** if( (regEnd--)<=0 ){
-** AggStep (csrEnd)
-** Next (csrEnd)
-** }
-** AggFinal (xValue)
-** Gosub addrGosub
-** Next(csr) // if EOF goto flush_partition_done
-** if( (regStart--)<=0 ){
-** AggInverse (csr2)
-** Next (csr2)
-** }
+** regOld and regNew are each the first register in an array of size
+** pOrderBy->nExpr. This function generates code to compare the two
+** arrays of registers using the collation sequences and other comparison
+** parameters specified by pOrderBy.
**
+** If the two arrays are not equal, the contents of regNew is copied to
+** regOld and control falls through. Otherwise, if the contents of the arrays
+** are equal, an OP_Goto is executed. The address of the OP_Goto is returned.
*/
-static void windowCodeRowExprStep(
- Parse *pParse,
- Select *p,
- WhereInfo *pWInfo,
- int regGosub,
- int addrGosub
+static void windowIfNewPeer(
+ Parse *pParse,
+ ExprList *pOrderBy,
+ int regNew, /* First in array of new values */
+ int regOld, /* First in array of old values */
+ int addr /* Jump here */
){
- Window *pMWin = p->pWin;
Vdbe *v = sqlite3GetVdbe(pParse);
- int regFlushPart; /* Register for "Gosub flush_partition" */
- int lblFlushPart; /* Label for "Gosub flush_partition" */
- int lblFlushDone; /* Label for "Gosub flush_partition_done" */
-
- int regArg;
- int addr;
- int csrStart = pParse->nTab++;
- int csrEnd = pParse->nTab++;
- int regStart; /* Value of <expr> PRECEDING */
- int regEnd; /* Value of <expr> FOLLOWING */
- int addrGoto;
- int addrTop;
- int addrIfPos1 = 0;
- int addrIfPos2 = 0;
- int regSize = 0;
-
- assert( pMWin->eStart==TK_PRECEDING
- || pMWin->eStart==TK_CURRENT
- || pMWin->eStart==TK_FOLLOWING
- || pMWin->eStart==TK_UNBOUNDED
- );
- assert( pMWin->eEnd==TK_FOLLOWING
- || pMWin->eEnd==TK_CURRENT
- || pMWin->eEnd==TK_UNBOUNDED
- || pMWin->eEnd==TK_PRECEDING
- );
-
- /* Allocate register and label for the "flush_partition" sub-routine. */
- regFlushPart = ++pParse->nMem;
- lblFlushPart = sqlite3VdbeMakeLabel(v);
- lblFlushDone = sqlite3VdbeMakeLabel(v);
-
- regStart = ++pParse->nMem;
- regEnd = ++pParse->nMem;
-
- windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, &regSize);
-
- addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
-
- /* Start of "flush_partition" */
- sqlite3VdbeResolveLabel(v, lblFlushPart);
- sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+3);
- VdbeCoverage(v);
- VdbeComment((v, "Flush_partition subroutine"));
- sqlite3VdbeAddOp2(v, OP_OpenDup, csrStart, pMWin->iEphCsr);
- sqlite3VdbeAddOp2(v, OP_OpenDup, csrEnd, pMWin->iEphCsr);
-
- /* If either regStart or regEnd are not non-negative integers, throw
- ** an exception. */
- if( pMWin->pStart ){
- sqlite3ExprCode(pParse, pMWin->pStart, regStart);
- windowCheckIntValue(pParse, regStart, 0);
- }
- if( pMWin->pEnd ){
- sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
- windowCheckIntValue(pParse, regEnd, 1);
- }
-
- /* If this is "ROWS <expr1> FOLLOWING AND ROWS <expr2> FOLLOWING", do:
- **
- ** if( regEnd<regStart ){
- ** // The frame always consists of 0 rows
- ** regStart = regSize;
- ** }
- ** regEnd = regEnd - regStart;
- */
- if( pMWin->pEnd && pMWin->eStart==TK_FOLLOWING ){
- assert( pMWin->pStart!=0 );
- assert( pMWin->eEnd==TK_FOLLOWING );
- sqlite3VdbeAddOp3(v, OP_Ge, regStart, sqlite3VdbeCurrentAddr(v)+2, regEnd);
- VdbeCoverageNeverNull(v);
- sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart);
- sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regEnd);
- }
-
- if( pMWin->pStart && pMWin->eEnd==TK_PRECEDING ){
- assert( pMWin->pEnd!=0 );
- assert( pMWin->eStart==TK_PRECEDING );
- sqlite3VdbeAddOp3(v, OP_Le, regStart, sqlite3VdbeCurrentAddr(v)+3, regEnd);
- VdbeCoverageNeverNull(v);
- sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart);
- sqlite3VdbeAddOp2(v, OP_Copy, regSize, regEnd);
- }
-
- /* Initialize the accumulator register for each window function to NULL */
- regArg = windowInitAccum(pParse, pMWin);
-
- sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblFlushDone);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Rewind, csrStart, lblFlushDone);
- VdbeCoverageNeverTaken(v);
- sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_Rewind, csrEnd, lblFlushDone);
- VdbeCoverageNeverTaken(v);
- sqlite3VdbeChangeP5(v, 1);
-
- /* Invoke AggStep function for each window function using the row that
- ** csrEnd currently points to. Or, if csrEnd is already at EOF,
- ** do nothing. */
- addrTop = sqlite3VdbeCurrentAddr(v);
- if( pMWin->eEnd==TK_PRECEDING ){
- addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1);
- VdbeCoverage(v);
- }
- sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverage(v);
- addr = sqlite3VdbeAddOp0(v, OP_Goto);
- windowAggStep(pParse, pMWin, csrEnd, 0, regArg, regSize);
- if( pMWin->eEnd==TK_UNBOUNDED ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
- sqlite3VdbeJumpHere(v, addr);
- addrTop = sqlite3VdbeCurrentAddr(v);
+ if( pOrderBy ){
+ int nVal = pOrderBy->nExpr;
+ KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
+ sqlite3VdbeAddOp3(v, OP_Compare, regOld, regNew, nVal);
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_Jump,
+ sqlite3VdbeCurrentAddr(v)+1, addr, sqlite3VdbeCurrentAddr(v)+1
+ );
+ VdbeCoverageEqNe(v);
+ sqlite3VdbeAddOp3(v, OP_Copy, regNew, regOld, nVal-1);
}else{
- sqlite3VdbeJumpHere(v, addr);
- if( pMWin->eEnd==TK_PRECEDING ){
- sqlite3VdbeJumpHere(v, addrIfPos1);
- }
- }
-
- if( pMWin->eEnd==TK_FOLLOWING ){
- addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1);
- VdbeCoverage(v);
- }
- if( pMWin->eStart==TK_FOLLOWING ){
- addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1);
- VdbeCoverage(v);
- }
- windowAggFinal(pParse, pMWin, 0);
- windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
- sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone);
- if( pMWin->eStart==TK_FOLLOWING ){
- sqlite3VdbeJumpHere(v, addrIfPos2);
- }
-
- if( pMWin->eStart==TK_CURRENT
- || pMWin->eStart==TK_PRECEDING
- || pMWin->eStart==TK_FOLLOWING
- ){
- int lblSkipInverse = sqlite3VdbeMakeLabel(v);;
- if( pMWin->eStart==TK_PRECEDING ){
- sqlite3VdbeAddOp3(v, OP_IfPos, regStart, lblSkipInverse, 1);
- VdbeCoverage(v);
- }
- if( pMWin->eStart==TK_FOLLOWING ){
- sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, lblSkipInverse);
- }else{
- sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
- VdbeCoverageAlwaysTaken(v);
- }
- windowAggStep(pParse, pMWin, csrStart, 1, regArg, regSize);
- sqlite3VdbeResolveLabel(v, lblSkipInverse);
- }
- if( pMWin->eEnd==TK_FOLLOWING ){
- sqlite3VdbeJumpHere(v, addrIfPos1);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
}
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
-
- /* flush_partition_done: */
- sqlite3VdbeResolveLabel(v, lblFlushDone);
- sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
- sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
- VdbeComment((v, "end flush_partition subroutine"));
-
- /* Jump to here to skip over flush_partition */
- sqlite3VdbeJumpHere(v, addrGoto);
}
/*
-** This function does the work of sqlite3WindowCodeStep() for cases that
-** would normally be handled by windowCodeDefaultStep() when there are
-** one or more built-in window-functions that require the entire partition
-** to be cached in a temp table before any rows can be returned. Additionally.
-** "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" is always handled by
-** this function.
+** This function is called as part of generating VM programs for RANGE
+** offset PRECEDING/FOLLOWING frame boundaries. Assuming "ASC" order for
+** the ORDER BY term in the window, and that argument op is OP_Ge, it generates
+** code equivalent to:
**
-** Pseudo-code corresponding to the VM code generated by this function
-** for each type of window follows.
+** if( csr1.peerVal + regVal >= csr2.peerVal ) goto lbl;
**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+** The value of parameter op may also be OP_Gt or OP_Le. In these cases the
+** operator in the above pseudo-code is replaced with ">" or "<=", respectively.
**
-** flush_partition:
-** Once {
-** OpenDup (iEphCsr -> csrLead)
-** }
-** Integer ctr 0
-** foreach row (csrLead){
-** if( new peer ){
-** AggFinal (xValue)
-** for(i=0; i<ctr; i++){
-** Gosub addrGosub
-** Next iEphCsr
-** }
-** Integer ctr 0
-** }
-** AggStep (csrLead)
-** Incr ctr
-** }
-**
-** AggFinal (xFinalize)
-** for(i=0; i<ctr; i++){
-** Gosub addrGosub
-** Next iEphCsr
-** }
-**
-** ResetSorter (csr)
-** Return
-**
-** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
-**
-** As above, except that the "if( new peer )" branch is always taken.
+** If the sort-order for the ORDER BY term in the window is DESC, then the
+** comparison is reversed. Instead of adding regVal to csr1.peerVal, it is
+** subtracted. And the comparison operator is inverted to - ">=" becomes "<=",
+** ">" becomes "<", and so on. So, with DESC sort order, if the argument op
+** is OP_Ge, the generated code is equivalent to:
**
-** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
+** if( csr1.peerVal - regVal <= csr2.peerVal ) goto lbl;
**
-** As above, except that each of the for() loops becomes:
-**
-** for(i=0; i<ctr; i++){
-** Gosub addrGosub
-** AggInverse (iEphCsr)
-** Next iEphCsr
-** }
-**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
-**
-** flush_partition:
-** Once {
-** OpenDup (iEphCsr -> csrLead)
-** }
-** foreach row (csrLead) {
-** AggStep (csrLead)
-** }
-** foreach row (iEphCsr) {
-** Gosub addrGosub
-** }
-**
-** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-**
-** flush_partition:
-** Once {
-** OpenDup (iEphCsr -> csrLead)
-** }
-** foreach row (csrLead){
-** AggStep (csrLead)
-** }
-** Rewind (csrLead)
-** Integer ctr 0
-** foreach row (csrLead){
-** if( new peer ){
-** AggFinal (xValue)
-** for(i=0; i<ctr; i++){
-** Gosub addrGosub
-** AggInverse (iEphCsr)
-** Next iEphCsr
-** }
-** Integer ctr 0
-** }
-** Incr ctr
-** }
-**
-** AggFinal (xFinalize)
-** for(i=0; i<ctr; i++){
-** Gosub addrGosub
-** Next iEphCsr
-** }
-**
-** ResetSorter (csr)
-** Return
+** A special type of arithmetic is used such that if csr1.peerVal is not
+** a numeric type (real or integer), then the result of the addition
+** or subtraction is a a copy of csr1.peerVal.
*/
-static void windowCodeCacheStep(
- Parse *pParse,
- Select *p,
- WhereInfo *pWInfo,
- int regGosub,
- int addrGosub
+static void windowCodeRangeTest(
+ WindowCodeArg *p,
+ int op, /* OP_Ge, OP_Gt, or OP_Le */
+ int csr1, /* Cursor number for cursor 1 */
+ int regVal, /* Register containing non-negative number */
+ int csr2, /* Cursor number for cursor 2 */
+ int lbl /* Jump destination if condition is true */
){
- Window *pMWin = p->pWin;
+ Parse *pParse = p->pParse;
Vdbe *v = sqlite3GetVdbe(pParse);
- int k;
- int addr;
- ExprList *pPart = pMWin->pPartition;
- ExprList *pOrderBy = pMWin->pOrderBy;
- int nPeer = pOrderBy ? pOrderBy->nExpr : 0;
- int regNewPeer;
-
- int addrGoto; /* Address of Goto used to jump flush_par.. */
- int addrNext; /* Jump here for next iteration of loop */
- int regFlushPart;
- int lblFlushPart;
- int csrLead;
- int regCtr;
- int regArg; /* Register array to martial function args */
- int regSize;
- int lblEmpty;
- int bReverse = pMWin->pOrderBy && pMWin->eStart==TK_CURRENT
- && pMWin->eEnd==TK_UNBOUNDED;
-
- assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
- || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED)
- || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT)
- || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED)
- );
-
- lblEmpty = sqlite3VdbeMakeLabel(v);
- regNewPeer = pParse->nMem+1;
- pParse->nMem += nPeer;
-
- /* Allocate register and label for the "flush_partition" sub-routine. */
- regFlushPart = ++pParse->nMem;
- lblFlushPart = sqlite3VdbeMakeLabel(v);
-
- csrLead = pParse->nTab++;
- regCtr = ++pParse->nMem;
-
- windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, &regSize);
- addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
-
- /* Start of "flush_partition" */
- sqlite3VdbeResolveLabel(v, lblFlushPart);
- sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_OpenDup, csrLead, pMWin->iEphCsr);
-
- /* Initialize the accumulator register for each window function to NULL */
- regArg = windowInitAccum(pParse, pMWin);
+ ExprList *pOrderBy = p->pMWin->pOrderBy; /* ORDER BY clause for window */
+ int reg1 = sqlite3GetTempReg(pParse); /* Reg. for csr1.peerVal+regVal */
+ int reg2 = sqlite3GetTempReg(pParse); /* Reg. for csr2.peerVal */
+ int regString = ++pParse->nMem; /* Reg. for constant value '' */
+ int arith = OP_Add; /* OP_Add or OP_Subtract */
+ int addrGe; /* Jump destination */
+ int addrDone = sqlite3VdbeMakeLabel(pParse); /* Address past OP_Ge */
+ CollSeq *pColl;
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regCtr);
- sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblEmpty);
- VdbeCoverageNeverTaken(v);
+ /* Read the peer-value from each cursor into a register */
+ windowReadPeerValues(p, csr1, reg1);
+ windowReadPeerValues(p, csr2, reg2);
- if( bReverse ){
- int addr2 = sqlite3VdbeCurrentAddr(v);
- windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
- sqlite3VdbeAddOp2(v, OP_Next, csrLead, addr2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty);
- VdbeCoverageNeverTaken(v);
+ assert( op==OP_Ge || op==OP_Gt || op==OP_Le );
+ assert( pOrderBy && pOrderBy->nExpr==1 );
+ if( pOrderBy->a[0].fg.sortFlags & KEYINFO_ORDER_DESC ){
+ switch( op ){
+ case OP_Ge: op = OP_Le; break;
+ case OP_Gt: op = OP_Lt; break;
+ default: assert( op==OP_Le ); op = OP_Ge; break;
+ }
+ arith = OP_Subtract;
}
- addrNext = sqlite3VdbeCurrentAddr(v);
- if( pOrderBy && (pMWin->eEnd==TK_CURRENT || pMWin->eStart==TK_CURRENT) ){
- int bCurrent = (pMWin->eStart==TK_CURRENT);
- int addrJump = 0; /* Address of OP_Jump below */
- if( pMWin->eType==TK_RANGE ){
- int iOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
- int regPeer = pMWin->regPart + (pPart ? pPart->nExpr : 0);
- KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
- for(k=0; k<nPeer; k++){
- sqlite3VdbeAddOp3(v, OP_Column, csrLead, iOff+k, regNewPeer+k);
- }
- addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
- sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
- addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, nPeer-1);
+ VdbeModuleComment((v, "CodeRangeTest: if( R%d %s R%d %s R%d ) goto lbl",
+ reg1, (arith==OP_Add ? "+" : "-"), regVal,
+ ((op==OP_Ge) ? ">=" : (op==OP_Le) ? "<=" : (op==OP_Gt) ? ">" : "<"), reg2
+ ));
+
+ /* If the BIGNULL flag is set for the ORDER BY, then it is required to
+ ** consider NULL values to be larger than all other values, instead of
+ ** the usual smaller. The VDBE opcodes OP_Ge and so on do not handle this
+ ** (and adding that capability causes a performance regression), so
+ ** instead if the BIGNULL flag is set then cases where either reg1 or
+ ** reg2 are NULL are handled separately in the following block. The code
+ ** generated is equivalent to:
+ **
+ ** if( reg1 IS NULL ){
+ ** if( op==OP_Ge ) goto lbl;
+ ** if( op==OP_Gt && reg2 IS NOT NULL ) goto lbl;
+ ** if( op==OP_Le && reg2 IS NULL ) goto lbl;
+ ** }else if( reg2 IS NULL ){
+ ** if( op==OP_Le ) goto lbl;
+ ** }
+ **
+ ** Additionally, if either reg1 or reg2 are NULL but the jump to lbl is
+ ** not taken, control jumps over the comparison operator coded below this
+ ** block. */
+ if( pOrderBy->a[0].fg.sortFlags & KEYINFO_ORDER_BIGNULL ){
+ /* This block runs if reg1 contains a NULL. */
+ int addr = sqlite3VdbeAddOp1(v, OP_NotNull, reg1); VdbeCoverage(v);
+ switch( op ){
+ case OP_Ge:
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lbl);
+ break;
+ case OP_Gt:
+ sqlite3VdbeAddOp2(v, OP_NotNull, reg2, lbl);
+ VdbeCoverage(v);
+ break;
+ case OP_Le:
+ sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl);
+ VdbeCoverage(v);
+ break;
+ default: assert( op==OP_Lt ); /* no-op */ break;
}
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone);
- windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub,
- (bCurrent ? regArg : 0), (bCurrent ? regSize : 0)
- );
- if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
+ /* This block runs if reg1 is not NULL, but reg2 is. */
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeAddOp2(v, OP_IsNull, reg2,
+ (op==OP_Gt || op==OP_Ge) ? addrDone : lbl);
+ VdbeCoverage(v);
}
- if( bReverse==0 ){
- windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
- }
- sqlite3VdbeAddOp2(v, OP_AddImm, regCtr, 1);
- sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrNext);
+ /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1).
+ ** This block adds (or subtracts for DESC) the numeric value in regVal
+ ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob),
+ ** then leave reg1 as it is. In pseudo-code, this is implemented as:
+ **
+ ** if( reg1>='' ) goto addrGe;
+ ** reg1 = reg1 +/- regVal
+ ** addrGe:
+ **
+ ** Since all strings and blobs are greater-than-or-equal-to an empty string,
+ ** the add/subtract is skipped for these, as required. If reg1 is a NULL,
+ ** then the arithmetic is performed, but since adding or subtracting from
+ ** NULL is always NULL anyway, this case is handled as required too. */
+ sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC);
+ addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1);
VdbeCoverage(v);
+ if( (op==OP_Ge && arith==OP_Add) || (op==OP_Le && arith==OP_Subtract) ){
+ sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v);
+ }
+ sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1);
+ sqlite3VdbeJumpHere(v, addrGe);
+
+ /* Compare registers reg2 and reg1, taking the jump if required. Note that
+ ** control skips over this test if the BIGNULL flag is set and either
+ ** reg1 or reg2 contain a NULL value. */
+ sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v);
+ pColl = sqlite3ExprNNCollSeq(pParse, pOrderBy->a[0].pExpr);
+ sqlite3VdbeAppendP4(v, (void*)pColl, P4_COLLSEQ);
+ sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+ sqlite3VdbeResolveLabel(v, addrDone);
- windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub, 0, 0);
-
- sqlite3VdbeResolveLabel(v, lblEmpty);
- sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
- sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
+ assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le );
+ testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge);
+ testcase(op==OP_Lt); VdbeCoverageIf(v, op==OP_Lt);
+ testcase(op==OP_Le); VdbeCoverageIf(v, op==OP_Le);
+ testcase(op==OP_Gt); VdbeCoverageIf(v, op==OP_Gt);
+ sqlite3ReleaseTempReg(pParse, reg1);
+ sqlite3ReleaseTempReg(pParse, reg2);
- /* Jump to here to skip over flush_partition */
- sqlite3VdbeJumpHere(v, addrGoto);
+ VdbeModuleComment((v, "CodeRangeTest: end"));
}
-
/*
-** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
-**
-** ...
-** if( new partition ){
-** AggFinal (xFinalize)
-** Gosub addrGosub
-** ResetSorter eph-table
-** }
-** else if( new peer ){
-** AggFinal (xValue)
-** Gosub addrGosub
-** ResetSorter eph-table
-** }
-** AggStep
-** Insert (record into eph-table)
-** sqlite3WhereEnd()
-** AggFinal (xFinalize)
-** Gosub addrGosub
-**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
-**
-** As above, except take no action for a "new peer". Invoke
-** the sub-routine once only for each partition.
-**
-** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
-**
-** As above, except that the "new peer" condition is handled in the
-** same way as "new partition" (so there is no "else if" block).
-**
-** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
-**
-** As above, except assume every row is a "new peer".
+** Helper function for sqlite3WindowCodeStep(). Each call to this function
+** generates VM code for a single RETURN_ROW, AGGSTEP or AGGINVERSE
+** operation. Refer to the header comment for sqlite3WindowCodeStep() for
+** details.
*/
-static void windowCodeDefaultStep(
- Parse *pParse,
- Select *p,
- WhereInfo *pWInfo,
- int regGosub,
- int addrGosub
+static int windowCodeOp(
+ WindowCodeArg *p, /* Context object */
+ int op, /* WINDOW_RETURN_ROW, AGGSTEP or AGGINVERSE */
+ int regCountdown, /* Register for OP_IfPos countdown */
+ int jumpOnEof /* Jump here if stepped cursor reaches EOF */
){
- Window *pMWin = p->pWin;
- Vdbe *v = sqlite3GetVdbe(pParse);
- int k;
- int iSubCsr = p->pSrc->a[0].iCursor;
- int nSub = p->pSrc->a[0].pTab->nCol;
- int reg = pParse->nMem+1;
- int regRecord = reg+nSub;
- int regRowid = regRecord+1;
- int addr;
- ExprList *pPart = pMWin->pPartition;
- ExprList *pOrderBy = pMWin->pOrderBy;
+ int csr, reg;
+ Parse *pParse = p->pParse;
+ Window *pMWin = p->pMWin;
+ int ret = 0;
+ Vdbe *v = p->pVdbe;
+ int addrContinue = 0;
+ int bPeer = (pMWin->eFrmType!=TK_ROWS);
- assert( pMWin->eType==TK_RANGE
- || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
- );
+ int lblDone = sqlite3VdbeMakeLabel(pParse);
+ int addrNextRange = 0;
- assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
- || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED)
- || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT)
- || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED && !pOrderBy)
- );
+ /* Special case - WINDOW_AGGINVERSE is always a no-op if the frame
+ ** starts with UNBOUNDED PRECEDING. */
+ if( op==WINDOW_AGGINVERSE && pMWin->eStart==TK_UNBOUNDED ){
+ assert( regCountdown==0 && jumpOnEof==0 );
+ return 0;
+ }
- if( pMWin->eEnd==TK_UNBOUNDED ){
- pOrderBy = 0;
+ if( regCountdown>0 ){
+ if( pMWin->eFrmType==TK_RANGE ){
+ addrNextRange = sqlite3VdbeCurrentAddr(v);
+ assert( op==WINDOW_AGGINVERSE || op==WINDOW_AGGSTEP );
+ if( op==WINDOW_AGGINVERSE ){
+ if( pMWin->eStart==TK_FOLLOWING ){
+ windowCodeRangeTest(
+ p, OP_Le, p->current.csr, regCountdown, p->start.csr, lblDone
+ );
+ }else{
+ windowCodeRangeTest(
+ p, OP_Ge, p->start.csr, regCountdown, p->current.csr, lblDone
+ );
+ }
+ }else{
+ windowCodeRangeTest(
+ p, OP_Gt, p->end.csr, regCountdown, p->current.csr, lblDone
+ );
+ }
+ }else{
+ sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, lblDone, 1);
+ VdbeCoverage(v);
+ }
}
- pParse->nMem += nSub + 2;
+ if( op==WINDOW_RETURN_ROW && pMWin->regStartRowid==0 ){
+ windowAggFinal(p, 0);
+ }
+ addrContinue = sqlite3VdbeCurrentAddr(v);
- /* Load the individual column values of the row returned by
- ** the sub-select into an array of registers. */
- for(k=0; k<nSub; k++){
- sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
+ /* If this is a (RANGE BETWEEN a FOLLOWING AND b FOLLOWING) or
+ ** (RANGE BETWEEN b PRECEDING AND a PRECEDING) frame, ensure the
+ ** start cursor does not advance past the end cursor within the
+ ** temporary table. It otherwise might, if (a>b). Also ensure that,
+ ** if the input cursor is still finding new rows, that the end
+ ** cursor does not go past it to EOF. */
+ if( pMWin->eStart==pMWin->eEnd && regCountdown
+ && pMWin->eFrmType==TK_RANGE
+ ){
+ int regRowid1 = sqlite3GetTempReg(pParse);
+ int regRowid2 = sqlite3GetTempReg(pParse);
+ if( op==WINDOW_AGGINVERSE ){
+ sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1);
+ sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2);
+ sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1);
+ VdbeCoverage(v);
+ }else if( p->regRowid ){
+ sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid1);
+ sqlite3VdbeAddOp3(v, OP_Ge, p->regRowid, lblDone, regRowid1);
+ VdbeCoverageNeverNull(v);
+ }
+ sqlite3ReleaseTempReg(pParse, regRowid1);
+ sqlite3ReleaseTempReg(pParse, regRowid2);
+ assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING );
}
- /* Check if this is the start of a new partition or peer group. */
- if( pPart || pOrderBy ){
- int nPart = (pPart ? pPart->nExpr : 0);
- int addrGoto = 0;
- int addrJump = 0;
- int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
+ switch( op ){
+ case WINDOW_RETURN_ROW:
+ csr = p->current.csr;
+ reg = p->current.reg;
+ windowReturnOneRow(p);
+ break;
- if( pPart ){
- int regNewPart = reg + pMWin->nBufferCol;
- KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
- addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart);
- sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
- addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
- VdbeCoverageEqNe(v);
- windowAggFinal(pParse, pMWin, 1);
- if( pOrderBy ){
- addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
+ case WINDOW_AGGINVERSE:
+ csr = p->start.csr;
+ reg = p->start.reg;
+ if( pMWin->regStartRowid ){
+ assert( pMWin->regEndRowid );
+ sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regStartRowid, 1);
+ }else{
+ windowAggStep(p, pMWin, csr, 1, p->regArg);
}
- }
+ break;
- if( pOrderBy ){
- int regNewPeer = reg + pMWin->nBufferCol + nPart;
- int regPeer = pMWin->regPart + nPart;
-
- if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
- if( pMWin->eType==TK_RANGE ){
- KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
- addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
- sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
- addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
- VdbeCoverage(v);
+ default:
+ assert( op==WINDOW_AGGSTEP );
+ csr = p->end.csr;
+ reg = p->end.reg;
+ if( pMWin->regStartRowid ){
+ assert( pMWin->regEndRowid );
+ sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regEndRowid, 1);
}else{
- addrJump = 0;
+ windowAggStep(p, pMWin, csr, 0, p->regArg);
}
- windowAggFinal(pParse, pMWin, pMWin->eStart==TK_CURRENT);
- if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
- }
-
- sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
- sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1);
- VdbeCoverage(v);
-
- sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
- sqlite3VdbeAddOp3(
- v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1
- );
-
- if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
+ break;
}
- /* Invoke step function for window functions */
- windowAggStep(pParse, pMWin, -1, 0, reg, 0);
+ if( op==p->eDelete ){
+ sqlite3VdbeAddOp1(v, OP_Delete, csr);
+ sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION);
+ }
- /* Buffer the current row in the ephemeral table. */
- if( pMWin->nBufferCol>0 ){
- sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pMWin->nBufferCol, regRecord);
+ if( jumpOnEof ){
+ sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
+ ret = sqlite3VdbeAddOp0(v, OP_Goto);
}else{
- sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord);
- sqlite3VdbeAppendP4(v, (void*)"", 0);
+ sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1+bPeer);
+ VdbeCoverage(v);
+ if( bPeer ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblDone);
+ }
}
- sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
- /* End the database scan loop. */
- sqlite3WhereEnd(pWInfo);
+ if( bPeer ){
+ int nReg = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
+ int regTmp = (nReg ? sqlite3GetTempRange(pParse, nReg) : 0);
+ windowReadPeerValues(p, csr, regTmp);
+ windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg, addrContinue);
+ sqlite3ReleaseTempRange(pParse, regTmp, nReg);
+ }
- windowAggFinal(pParse, pMWin, 1);
- sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
- sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1);
- VdbeCoverage(v);
+ if( addrNextRange ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange);
+ }
+ sqlite3VdbeResolveLabel(v, lblDone);
+ return ret;
}
+
/*
** Allocate and return a duplicate of the Window object indicated by the
** third argument. Set the Window.pOwner field of the new object to
@@ -146681,15 +170183,24 @@ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){
pNew = sqlite3DbMallocZero(db, sizeof(Window));
if( pNew ){
pNew->zName = sqlite3DbStrDup(db, p->zName);
+ pNew->zBase = sqlite3DbStrDup(db, p->zBase);
pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0);
+ pNew->pWFunc = p->pWFunc;
pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0);
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0);
- pNew->eType = p->eType;
+ pNew->eFrmType = p->eFrmType;
pNew->eEnd = p->eEnd;
pNew->eStart = p->eStart;
+ pNew->eExclude = p->eExclude;
+ pNew->regResult = p->regResult;
+ pNew->regAccum = p->regAccum;
+ pNew->iArgCol = p->iArgCol;
+ pNew->iEphCsr = p->iEphCsr;
+ pNew->bExprArgs = p->bExprArgs;
pNew->pStart = sqlite3ExprDup(db, p->pStart, 0);
pNew->pEnd = sqlite3ExprDup(db, p->pEnd, 0);
pNew->pOwner = pOwner;
+ pNew->bImplicitFrame = p->bImplicitFrame;
}
}
return pNew;
@@ -146714,11 +170225,359 @@ SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p){
}
/*
-** sqlite3WhereBegin() has already been called for the SELECT statement
+** Return true if it can be determined at compile time that expression
+** pExpr evaluates to a value that, when cast to an integer, is greater
+** than zero. False otherwise.
+**
+** If an OOM error occurs, this function sets the Parse.db.mallocFailed
+** flag and returns zero.
+*/
+static int windowExprGtZero(Parse *pParse, Expr *pExpr){
+ int ret = 0;
+ sqlite3 *db = pParse->db;
+ sqlite3_value *pVal = 0;
+ sqlite3ValueFromExpr(db, pExpr, db->enc, SQLITE_AFF_NUMERIC, &pVal);
+ if( pVal && sqlite3_value_int(pVal)>0 ){
+ ret = 1;
+ }
+ sqlite3ValueFree(pVal);
+ return ret;
+}
+
+/*
+** sqlite3WhereBegin() has already been called for the SELECT statement
** passed as the second argument when this function is invoked. It generates
-** code to populate the Window.regResult register for each window function and
-** invoke the sub-routine at instruction addrGosub once for each row.
-** This function calls sqlite3WhereEnd() before returning.
+** code to populate the Window.regResult register for each window function
+** and invoke the sub-routine at instruction addrGosub once for each row.
+** sqlite3WhereEnd() is always called before returning.
+**
+** This function handles several different types of window frames, which
+** require slightly different processing. The following pseudo code is
+** used to implement window frames of the form:
+**
+** ROWS BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING
+**
+** Other window frame types use variants of the following:
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+**
+** if( first row of partition ){
+** // Rewind three cursors, all open on the eph table.
+** Rewind(csrEnd);
+** Rewind(csrStart);
+** Rewind(csrCurrent);
+**
+** regEnd = <expr2> // FOLLOWING expression
+** regStart = <expr1> // PRECEDING expression
+** }else{
+** // First time this branch is taken, the eph table contains two
+** // rows. The first row in the partition, which all three cursors
+** // currently point to, and the following row.
+** AGGSTEP
+** if( (regEnd--)<=0 ){
+** RETURN_ROW
+** if( (regStart--)<=0 ){
+** AGGINVERSE
+** }
+** }
+** }
+** }
+** flush:
+** AGGSTEP
+** while( 1 ){
+** RETURN ROW
+** if( csrCurrent is EOF ) break;
+** if( (regStart--)<=0 ){
+** AggInverse(csrStart)
+** Next(csrStart)
+** }
+** }
+**
+** The pseudo-code above uses the following shorthand:
+**
+** AGGSTEP: invoke the aggregate xStep() function for each window function
+** with arguments read from the current row of cursor csrEnd, then
+** step cursor csrEnd forward one row (i.e. sqlite3BtreeNext()).
+**
+** RETURN_ROW: return a row to the caller based on the contents of the
+** current row of csrCurrent and the current state of all
+** aggregates. Then step cursor csrCurrent forward one row.
+**
+** AGGINVERSE: invoke the aggregate xInverse() function for each window
+** functions with arguments read from the current row of cursor
+** csrStart. Then step csrStart forward one row.
+**
+** There are two other ROWS window frames that are handled significantly
+** differently from the above - "BETWEEN <expr> PRECEDING AND <expr> PRECEDING"
+** and "BETWEEN <expr> FOLLOWING AND <expr> FOLLOWING". These are special
+** cases because they change the order in which the three cursors (csrStart,
+** csrCurrent and csrEnd) iterate through the ephemeral table. Cases that
+** use UNBOUNDED or CURRENT ROW are much simpler variations on one of these
+** three.
+**
+** ROWS BETWEEN <expr1> PRECEDING AND <expr2> PRECEDING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** }else{
+** if( (regEnd--)<=0 ){
+** AGGSTEP
+** }
+** RETURN_ROW
+** if( (regStart--)<=0 ){
+** AGGINVERSE
+** }
+** }
+** }
+** flush:
+** if( (regEnd--)<=0 ){
+** AGGSTEP
+** }
+** RETURN_ROW
+**
+**
+** ROWS BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = regEnd - <expr1>
+** }else{
+** AGGSTEP
+** if( (regEnd--)<=0 ){
+** RETURN_ROW
+** }
+** if( (regStart--)<=0 ){
+** AGGINVERSE
+** }
+** }
+** }
+** flush:
+** AGGSTEP
+** while( 1 ){
+** if( (regEnd--)<=0 ){
+** RETURN_ROW
+** if( eof ) break;
+** }
+** if( (regStart--)<=0 ){
+** AGGINVERSE
+** if( eof ) break
+** }
+** }
+** while( !eof csrCurrent ){
+** RETURN_ROW
+** }
+**
+** For the most part, the patterns above are adapted to support UNBOUNDED by
+** assuming that it is equivalent to "infinity PRECEDING/FOLLOWING" and
+** CURRENT ROW by assuming that it is equivalent to "0 PRECEDING/FOLLOWING".
+** This is optimized of course - branches that will never be taken and
+** conditions that are always true are omitted from the VM code. The only
+** exceptional case is:
+**
+** ROWS BETWEEN <expr1> FOLLOWING AND UNBOUNDED FOLLOWING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regStart = <expr1>
+** }else{
+** AGGSTEP
+** }
+** }
+** flush:
+** AGGSTEP
+** while( 1 ){
+** if( (regStart--)<=0 ){
+** AGGINVERSE
+** if( eof ) break
+** }
+** RETURN_ROW
+** }
+** while( !eof csrCurrent ){
+** RETURN_ROW
+** }
+**
+** Also requiring special handling are the cases:
+**
+** ROWS BETWEEN <expr1> PRECEDING AND <expr2> PRECEDING
+** ROWS BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
+**
+** when (expr1 < expr2). This is detected at runtime, not by this function.
+** To handle this case, the pseudo-code programs depicted above are modified
+** slightly to be:
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** if( regEnd < regStart ){
+** RETURN_ROW
+** delete eph table contents
+** continue
+** }
+** ...
+**
+** The new "continue" statement in the above jumps to the next iteration
+** of the outer loop - the one started by sqlite3WhereBegin().
+**
+** The various GROUPS cases are implemented using the same patterns as
+** ROWS. The VM code is modified slightly so that:
+**
+** 1. The else branch in the main loop is only taken if the row just
+** added to the ephemeral table is the start of a new group. In
+** other words, it becomes:
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** }else if( new group ){
+** ...
+** }
+** }
+**
+** 2. Instead of processing a single row, each RETURN_ROW, AGGSTEP or
+** AGGINVERSE step processes the current row of the relevant cursor and
+** all subsequent rows belonging to the same group.
+**
+** RANGE window frames are a little different again. As for GROUPS, the
+** main loop runs once per group only. And RETURN_ROW, AGGSTEP and AGGINVERSE
+** deal in groups instead of rows. As for ROWS and GROUPS, there are three
+** basic cases:
+**
+** RANGE BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** }else{
+** AGGSTEP
+** while( (csrCurrent.key + regEnd) < csrEnd.key ){
+** RETURN_ROW
+** while( csrStart.key + regStart) < csrCurrent.key ){
+** AGGINVERSE
+** }
+** }
+** }
+** }
+** flush:
+** AGGSTEP
+** while( 1 ){
+** RETURN ROW
+** if( csrCurrent is EOF ) break;
+** while( csrStart.key + regStart) < csrCurrent.key ){
+** AGGINVERSE
+** }
+** }
+** }
+**
+** In the above notation, "csr.key" means the current value of the ORDER BY
+** expression (there is only ever 1 for a RANGE that uses an <expr> FOLLOWING
+** or <expr PRECEDING) read from cursor csr.
+**
+** RANGE BETWEEN <expr1> PRECEDING AND <expr2> PRECEDING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** }else{
+** while( (csrEnd.key + regEnd) <= csrCurrent.key ){
+** AGGSTEP
+** }
+** while( (csrStart.key + regStart) < csrCurrent.key ){
+** AGGINVERSE
+** }
+** RETURN_ROW
+** }
+** }
+** flush:
+** while( (csrEnd.key + regEnd) <= csrCurrent.key ){
+** AGGSTEP
+** }
+** while( (csrStart.key + regStart) < csrCurrent.key ){
+** AGGINVERSE
+** }
+** RETURN_ROW
+**
+** RANGE BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** }else{
+** AGGSTEP
+** while( (csrCurrent.key + regEnd) < csrEnd.key ){
+** while( (csrCurrent.key + regStart) > csrStart.key ){
+** AGGINVERSE
+** }
+** RETURN_ROW
+** }
+** }
+** }
+** flush:
+** AGGSTEP
+** while( 1 ){
+** while( (csrCurrent.key + regStart) > csrStart.key ){
+** AGGINVERSE
+** if( eof ) break "while( 1 )" loop.
+** }
+** RETURN_ROW
+** }
+** while( !eof csrCurrent ){
+** RETURN_ROW
+** }
+**
+** The text above leaves out many details. Refer to the code and comments
+** below for a more complete picture.
*/
SQLITE_PRIVATE void sqlite3WindowCodeStep(
Parse *pParse, /* Parse context */
@@ -146728,75 +170587,317 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
int addrGosub /* OP_Gosub here to return each row */
){
Window *pMWin = p->pWin;
+ ExprList *pOrderBy = pMWin->pOrderBy;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ int csrWrite; /* Cursor used to write to eph. table */
+ int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */
+ int nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */
+ int iInput; /* To iterate through sub cols */
+ int addrNe; /* Address of OP_Ne */
+ int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */
+ int addrInteger = 0; /* Address of OP_Integer */
+ int addrEmpty; /* Address of OP_Rewind in flush: */
+ int regNew; /* Array of registers holding new input row */
+ int regRecord; /* regNew array in record form */
+ int regNewPeer = 0; /* Peer values for new row (part of regNew) */
+ int regPeer = 0; /* Peer values for current row */
+ int regFlushPart = 0; /* Register for "Gosub flush_partition" */
+ WindowCodeArg s; /* Context object for sub-routines */
+ int lblWhereEnd; /* Label just before sqlite3WhereEnd() code */
+ int regStart = 0; /* Value of <expr> PRECEDING */
+ int regEnd = 0; /* Value of <expr> FOLLOWING */
+
+ assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT
+ || pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED
+ );
+ assert( pMWin->eEnd==TK_FOLLOWING || pMWin->eEnd==TK_CURRENT
+ || pMWin->eEnd==TK_UNBOUNDED || pMWin->eEnd==TK_PRECEDING
+ );
+ assert( pMWin->eExclude==0 || pMWin->eExclude==TK_CURRENT
+ || pMWin->eExclude==TK_GROUP || pMWin->eExclude==TK_TIES
+ || pMWin->eExclude==TK_NO
+ );
- /* There are three different functions that may be used to do the work
- ** of this one, depending on the window frame and the specific built-in
- ** window functions used (if any).
- **
- ** windowCodeRowExprStep() handles all "ROWS" window frames, except for:
- **
- ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
- **
- ** The exception is because windowCodeRowExprStep() implements all window
- ** frame types by caching the entire partition in a temp table, and
- ** "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" is easy enough to
- ** implement without such a cache.
- **
- ** windowCodeCacheStep() is used for:
- **
- ** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- **
- ** It is also used for anything not handled by windowCodeRowExprStep()
- ** that invokes a built-in window function that requires the entire
- ** partition to be cached in a temp table before any rows are returned
- ** (e.g. nth_value() or percent_rank()).
- **
- ** Finally, assuming there is no built-in window function that requires
- ** the partition to be cached, windowCodeDefaultStep() is used for:
- **
- ** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
- ** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
- ** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
- ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
- **
- ** windowCodeDefaultStep() is the only one of the three functions that
- ** does not cache each partition in a temp table before beginning to
- ** return rows.
- */
- if( pMWin->eType==TK_ROWS
- && (pMWin->eStart!=TK_UNBOUNDED||pMWin->eEnd!=TK_CURRENT||!pMWin->pOrderBy)
- ){
- VdbeModuleComment((pParse->pVdbe, "Begin RowExprStep()"));
- windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub);
- }else{
- Window *pWin;
- int bCache = 0; /* True to use CacheStep() */
-
- if( pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED ){
- bCache = 1;
- }else{
- for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- FuncDef *pFunc = pWin->pFunc;
- if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE)
- || (pFunc->zName==nth_valueName)
- || (pFunc->zName==first_valueName)
- || (pFunc->zName==leadName)
- || (pFunc->zName==lagName)
- ){
- bCache = 1;
- break;
+ lblWhereEnd = sqlite3VdbeMakeLabel(pParse);
+
+ /* Fill in the context object */
+ memset(&s, 0, sizeof(WindowCodeArg));
+ s.pParse = pParse;
+ s.pMWin = pMWin;
+ s.pVdbe = v;
+ s.regGosub = regGosub;
+ s.addrGosub = addrGosub;
+ s.current.csr = pMWin->iEphCsr;
+ csrWrite = s.current.csr+1;
+ s.start.csr = s.current.csr+2;
+ s.end.csr = s.current.csr+3;
+
+ /* Figure out when rows may be deleted from the ephemeral table. There
+ ** are four options - they may never be deleted (eDelete==0), they may
+ ** be deleted as soon as they are no longer part of the window frame
+ ** (eDelete==WINDOW_AGGINVERSE), they may be deleted as after the row
+ ** has been returned to the caller (WINDOW_RETURN_ROW), or they may
+ ** be deleted after they enter the frame (WINDOW_AGGSTEP). */
+ switch( pMWin->eStart ){
+ case TK_FOLLOWING:
+ if( pMWin->eFrmType!=TK_RANGE
+ && windowExprGtZero(pParse, pMWin->pStart)
+ ){
+ s.eDelete = WINDOW_RETURN_ROW;
+ }
+ break;
+ case TK_UNBOUNDED:
+ if( windowCacheFrame(pMWin)==0 ){
+ if( pMWin->eEnd==TK_PRECEDING ){
+ if( pMWin->eFrmType!=TK_RANGE
+ && windowExprGtZero(pParse, pMWin->pEnd)
+ ){
+ s.eDelete = WINDOW_AGGSTEP;
+ }
+ }else{
+ s.eDelete = WINDOW_RETURN_ROW;
}
}
+ break;
+ default:
+ s.eDelete = WINDOW_AGGINVERSE;
+ break;
+ }
+
+ /* Allocate registers for the array of values from the sub-query, the
+ ** same values in record form, and the rowid used to insert said record
+ ** into the ephemeral table. */
+ regNew = pParse->nMem+1;
+ pParse->nMem += nInput;
+ regRecord = ++pParse->nMem;
+ s.regRowid = ++pParse->nMem;
+
+ /* If the window frame contains an "<expr> PRECEDING" or "<expr> FOLLOWING"
+ ** clause, allocate registers to store the results of evaluating each
+ ** <expr>. */
+ if( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ){
+ regStart = ++pParse->nMem;
+ }
+ if( pMWin->eEnd==TK_PRECEDING || pMWin->eEnd==TK_FOLLOWING ){
+ regEnd = ++pParse->nMem;
+ }
+
+ /* If this is not a "ROWS BETWEEN ..." frame, then allocate arrays of
+ ** registers to store copies of the ORDER BY expressions (peer values)
+ ** for the main loop, and for each cursor (start, current and end). */
+ if( pMWin->eFrmType!=TK_ROWS ){
+ int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
+ regNewPeer = regNew + pMWin->nBufferCol;
+ if( pMWin->pPartition ) regNewPeer += pMWin->pPartition->nExpr;
+ regPeer = pParse->nMem+1; pParse->nMem += nPeer;
+ s.start.reg = pParse->nMem+1; pParse->nMem += nPeer;
+ s.current.reg = pParse->nMem+1; pParse->nMem += nPeer;
+ s.end.reg = pParse->nMem+1; pParse->nMem += nPeer;
+ }
+
+ /* Load the column values for the row returned by the sub-select
+ ** into an array of registers starting at regNew. Assemble them into
+ ** a record in register regRecord. */
+ for(iInput=0; iInput<nInput; iInput++){
+ sqlite3VdbeAddOp3(v, OP_Column, csrInput, iInput, regNew+iInput);
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regNew, nInput, regRecord);
+
+ /* An input row has just been read into an array of registers starting
+ ** at regNew. If the window has a PARTITION clause, this block generates
+ ** VM code to check if the input row is the start of a new partition.
+ ** If so, it does an OP_Gosub to an address to be filled in later. The
+ ** address of the OP_Gosub is stored in local variable addrGosubFlush. */
+ if( pMWin->pPartition ){
+ int addr;
+ ExprList *pPart = pMWin->pPartition;
+ int nPart = pPart->nExpr;
+ int regNewPart = regNew + pMWin->nBufferCol;
+ KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
+
+ regFlushPart = ++pParse->nMem;
+ addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart, nPart);
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2);
+ VdbeCoverageEqNe(v);
+ addrGosubFlush = sqlite3VdbeAddOp1(v, OP_Gosub, regFlushPart);
+ VdbeComment((v, "call flush_partition"));
+ sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1);
+ }
+
+ /* Insert the new row into the ephemeral table */
+ sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, s.regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, s.regRowid);
+ addrNe = sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, s.regRowid);
+ VdbeCoverageNeverNull(v);
+
+ /* This block is run for the first row of each partition */
+ s.regArg = windowInitAccum(pParse, pMWin);
+
+ if( regStart ){
+ sqlite3ExprCode(pParse, pMWin->pStart, regStart);
+ windowCheckValue(pParse, regStart, 0 + (pMWin->eFrmType==TK_RANGE?3:0));
+ }
+ if( regEnd ){
+ sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
+ windowCheckValue(pParse, regEnd, 1 + (pMWin->eFrmType==TK_RANGE?3:0));
+ }
+
+ if( pMWin->eFrmType!=TK_RANGE && pMWin->eStart==pMWin->eEnd && regStart ){
+ int op = ((pMWin->eStart==TK_FOLLOWING) ? OP_Ge : OP_Le);
+ int addrGe = sqlite3VdbeAddOp3(v, op, regStart, 0, regEnd);
+ VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound <expr> */
+ VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */
+ windowAggFinal(&s, 0);
+ sqlite3VdbeAddOp1(v, OP_Rewind, s.current.csr);
+ windowReturnOneRow(&s);
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd);
+ sqlite3VdbeJumpHere(v, addrGe);
+ }
+ if( pMWin->eStart==TK_FOLLOWING && pMWin->eFrmType!=TK_RANGE && regEnd ){
+ assert( pMWin->eEnd==TK_FOLLOWING );
+ sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regStart);
+ }
+
+ if( pMWin->eStart!=TK_UNBOUNDED ){
+ sqlite3VdbeAddOp1(v, OP_Rewind, s.start.csr);
+ }
+ sqlite3VdbeAddOp1(v, OP_Rewind, s.current.csr);
+ sqlite3VdbeAddOp1(v, OP_Rewind, s.end.csr);
+ if( regPeer && pOrderBy ){
+ sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, pOrderBy->nExpr-1);
+ sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.start.reg, pOrderBy->nExpr-1);
+ sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.current.reg, pOrderBy->nExpr-1);
+ sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.end.reg, pOrderBy->nExpr-1);
+ }
+
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd);
+
+ sqlite3VdbeJumpHere(v, addrNe);
+
+ /* Beginning of the block executed for the second and subsequent rows. */
+ if( regPeer ){
+ windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer, lblWhereEnd);
+ }
+ if( pMWin->eStart==TK_FOLLOWING ){
+ windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
+ if( pMWin->eEnd!=TK_UNBOUNDED ){
+ if( pMWin->eFrmType==TK_RANGE ){
+ int lbl = sqlite3VdbeMakeLabel(pParse);
+ int addrNext = sqlite3VdbeCurrentAddr(v);
+ windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
+ sqlite3VdbeResolveLabel(v, lbl);
+ }else{
+ windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ }
}
+ }else
+ if( pMWin->eEnd==TK_PRECEDING ){
+ int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE);
+ windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
+ if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ if( !bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ }else{
+ int addr = 0;
+ windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
+ if( pMWin->eEnd!=TK_UNBOUNDED ){
+ if( pMWin->eFrmType==TK_RANGE ){
+ int lbl = 0;
+ addr = sqlite3VdbeCurrentAddr(v);
+ if( regEnd ){
+ lbl = sqlite3VdbeMakeLabel(pParse);
+ windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl);
+ }
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ if( regEnd ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
+ sqlite3VdbeResolveLabel(v, lbl);
+ }
+ }else{
+ if( regEnd ){
+ addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
+ VdbeCoverage(v);
+ }
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ if( regEnd ) sqlite3VdbeJumpHere(v, addr);
+ }
+ }
+ }
- /* Otherwise, call windowCodeDefaultStep(). */
- if( bCache ){
- VdbeModuleComment((pParse->pVdbe, "Begin CacheStep()"));
- windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub);
- }else{
- VdbeModuleComment((pParse->pVdbe, "Begin DefaultStep()"));
- windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub);
+ /* End of the main input loop */
+ sqlite3VdbeResolveLabel(v, lblWhereEnd);
+ sqlite3WhereEnd(pWInfo);
+
+ /* Fall through */
+ if( pMWin->pPartition ){
+ addrInteger = sqlite3VdbeAddOp2(v, OP_Integer, 0, regFlushPart);
+ sqlite3VdbeJumpHere(v, addrGosubFlush);
+ }
+
+ s.regRowid = 0;
+ addrEmpty = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite);
+ VdbeCoverage(v);
+ if( pMWin->eEnd==TK_PRECEDING ){
+ int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE);
+ windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
+ if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ }else if( pMWin->eStart==TK_FOLLOWING ){
+ int addrStart;
+ int addrBreak1;
+ int addrBreak2;
+ int addrBreak3;
+ windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
+ if( pMWin->eFrmType==TK_RANGE ){
+ addrStart = sqlite3VdbeCurrentAddr(v);
+ addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
+ addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
+ }else
+ if( pMWin->eEnd==TK_UNBOUNDED ){
+ addrStart = sqlite3VdbeCurrentAddr(v);
+ addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1);
+ addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1);
+ }else{
+ assert( pMWin->eEnd==TK_FOLLOWING );
+ addrStart = sqlite3VdbeCurrentAddr(v);
+ addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1);
+ addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
+ }
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
+ sqlite3VdbeJumpHere(v, addrBreak2);
+ addrStart = sqlite3VdbeCurrentAddr(v);
+ addrBreak3 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
+ sqlite3VdbeJumpHere(v, addrBreak1);
+ sqlite3VdbeJumpHere(v, addrBreak3);
+ }else{
+ int addrBreak;
+ int addrStart;
+ windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
+ addrStart = sqlite3VdbeCurrentAddr(v);
+ addrBreak = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
+ sqlite3VdbeJumpHere(v, addrBreak);
+ }
+ sqlite3VdbeJumpHere(v, addrEmpty);
+
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr);
+ if( pMWin->pPartition ){
+ if( pMWin->regStartRowid ){
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regStartRowid);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regEndRowid);
}
+ sqlite3VdbeChangeP1(v, addrInteger, sqlite3VdbeCurrentAddr(v));
+ sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
}
}
@@ -146804,8 +170905,11 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
/************** End of window.c **********************************************/
/************** Begin file parse.c *******************************************/
+/* This file is automatically generated by Lemon from input grammar
+** source file "parse.y".
+*/
/*
-** 2000-05-29
+** 2001-09-15
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -146815,22 +170919,15 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
** May you share freely, never taking more than you give.
**
*************************************************************************
-** Driver template for the LEMON parser generator.
-**
-** The "lemon" program processes an LALR(1) input grammar file, then uses
-** this template to construct a parser. The "lemon" program inserts text
-** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the
-** interstitial "-" characters) contained in this template is changed into
-** the value of the %name directive from the grammar. Otherwise, the content
-** of this template is copied straight through into the generate parser
-** source file.
+** This file contains SQLite's SQL parser.
**
-** The following is the concatenation of all %include directives from the
-** input grammar file:
+** The canonical source code to this file ("parse.y") is a Lemon grammar
+** file that specifies the input grammar and actions to take while parsing.
+** That input file is processed by Lemon to generate a C-language
+** implementation of a parser for the given grammar. You might be reading
+** this comment as part of the translated C-code. Edits should be made
+** to the original parse.y sources.
*/
-/* #include <stdio.h> */
-/* #include <assert.h> */
-/************ Begin %include sections from the grammar ************************/
/* #include "sqliteInt.h" */
@@ -146888,9 +170985,31 @@ struct FrameBound { int eType; Expr *pExpr; };
** shared across database connections.
*/
static void disableLookaside(Parse *pParse){
+ sqlite3 *db = pParse->db;
pParse->disableLookaside++;
- pParse->db->lookaside.bDisable++;
+ DisableLookaside;
+}
+
+#if !defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) \
+ && defined(SQLITE_UDL_CAPABLE_PARSER)
+/*
+** Issue an error message if an ORDER BY or LIMIT clause occurs on an
+** UPDATE or DELETE statement.
+*/
+static void updateDeleteLimitError(
+ Parse *pParse,
+ ExprList *pOrderBy,
+ Expr *pLimit
+){
+ if( pOrderBy ){
+ sqlite3ErrorMsg(pParse, "syntax error near \"ORDER BY\"");
+ }else{
+ sqlite3ErrorMsg(pParse, "syntax error near \"LIMIT\"");
+ }
+ sqlite3ExprListDelete(pParse->db, pOrderBy);
+ sqlite3ExprDelete(pParse->db, pLimit);
}
+#endif /* SQLITE_ENABLE_UPDATE_DELETE_LIMIT */
/*
@@ -146899,14 +171018,25 @@ static void disableLookaside(Parse *pParse){
** SQLITE_LIMIT_COMPOUND_SELECT.
*/
static void parserDoubleLinkSelect(Parse *pParse, Select *p){
+ assert( p!=0 );
if( p->pPrior ){
- Select *pNext = 0, *pLoop;
- int mxSelect, cnt = 0;
- for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){
+ Select *pNext = 0, *pLoop = p;
+ int mxSelect, cnt = 1;
+ while(1){
pLoop->pNext = pNext;
pLoop->selFlags |= SF_Compound;
+ pNext = pLoop;
+ pLoop = pLoop->pPrior;
+ if( pLoop==0 ) break;
+ cnt++;
+ if( pLoop->pOrderBy || pLoop->pLimit ){
+ sqlite3ErrorMsg(pParse,"%s clause should come after %s not before",
+ pLoop->pOrderBy!=0 ? "ORDER BY" : "LIMIT",
+ sqlite3SelectOpName(pNext->op));
+ break;
+ }
}
- if( (p->selFlags & SF_MultiValue)==0 &&
+ if( (p->selFlags & SF_MultiValue)==0 &&
(mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 &&
cnt>mxSelect
){
@@ -146915,36 +171045,47 @@ static void disableLookaside(Parse *pParse){
}
}
-
- /* Construct a new Expr object from a single identifier. Use the
- ** new Expr to populate pOut. Set the span of pOut to be the identifier
- ** that created the expression.
+ /* Attach a With object describing the WITH clause to a Select
+ ** object describing the query for which the WITH clause is a prefix.
*/
+ static Select *attachWithToSelect(Parse *pParse, Select *pSelect, With *pWith){
+ if( pSelect ){
+ pSelect->pWith = pWith;
+ parserDoubleLinkSelect(pParse, pSelect);
+ }else{
+ sqlite3WithDelete(pParse->db, pWith);
+ }
+ return pSelect;
+ }
+
+
+ /* Construct a new Expr object from a single token */
static Expr *tokenExpr(Parse *pParse, int op, Token t){
Expr *p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1);
if( p ){
/* memset(p, 0, sizeof(Expr)); */
p->op = (u8)op;
- p->affinity = 0;
+ p->affExpr = 0;
p->flags = EP_Leaf;
- p->iAgg = -1;
+ ExprClearVVAProperties(p);
+ /* p->iAgg = -1; // Not required */
p->pLeft = p->pRight = 0;
- p->x.pList = 0;
p->pAggInfo = 0;
- p->y.pTab = 0;
+ memset(&p->x, 0, sizeof(p->x));
+ memset(&p->y, 0, sizeof(p->y));
p->op2 = 0;
p->iTable = 0;
p->iColumn = 0;
p->u.zToken = (char*)&p[1];
memcpy(p->u.zToken, t.z, t.n);
p->u.zToken[t.n] = 0;
+ p->w.iOfst = (int)(t.z - pParse->zTail);
if( sqlite3Isquote(p->u.zToken[0]) ){
- if( p->u.zToken[0]=='"' ) p->flags |= EP_DblQuoted;
- sqlite3Dequote(p->u.zToken);
+ sqlite3DequoteExpr(p);
}
#if SQLITE_MAX_EXPR_DEPTH>0
p->nHeight = 1;
-#endif
+#endif
if( IN_RENAME_OBJECT ){
return (Expr*)sqlite3RenameTokenMap(pParse, (void*)p, &t);
}
@@ -146986,12 +171127,200 @@ static void disableLookaside(Parse *pParse){
sqlite3ExprListSetName(pParse, p, pIdToken, 1);
return p;
}
+
+#if TK_SPAN>255
+# error too many tokens in the grammar
+#endif
/**************** End of %include directives **********************************/
-/* These constants specify the various numeric values for terminal symbols
-** in a format understandable to "makeheaders". This section is blank unless
-** "lemon" is run with the "-m" command-line option.
-***************** Begin makeheaders token definitions *************************/
-/**************** End makeheaders token definitions ***************************/
+/* These constants specify the various numeric values for terminal symbols.
+***************** Begin token definitions *************************************/
+#ifndef TK_SEMI
+#define TK_SEMI 1
+#define TK_EXPLAIN 2
+#define TK_QUERY 3
+#define TK_PLAN 4
+#define TK_BEGIN 5
+#define TK_TRANSACTION 6
+#define TK_DEFERRED 7
+#define TK_IMMEDIATE 8
+#define TK_EXCLUSIVE 9
+#define TK_COMMIT 10
+#define TK_END 11
+#define TK_ROLLBACK 12
+#define TK_SAVEPOINT 13
+#define TK_RELEASE 14
+#define TK_TO 15
+#define TK_TABLE 16
+#define TK_CREATE 17
+#define TK_IF 18
+#define TK_NOT 19
+#define TK_EXISTS 20
+#define TK_TEMP 21
+#define TK_LP 22
+#define TK_RP 23
+#define TK_AS 24
+#define TK_COMMA 25
+#define TK_WITHOUT 26
+#define TK_ABORT 27
+#define TK_ACTION 28
+#define TK_AFTER 29
+#define TK_ANALYZE 30
+#define TK_ASC 31
+#define TK_ATTACH 32
+#define TK_BEFORE 33
+#define TK_BY 34
+#define TK_CASCADE 35
+#define TK_CAST 36
+#define TK_CONFLICT 37
+#define TK_DATABASE 38
+#define TK_DESC 39
+#define TK_DETACH 40
+#define TK_EACH 41
+#define TK_FAIL 42
+#define TK_OR 43
+#define TK_AND 44
+#define TK_IS 45
+#define TK_MATCH 46
+#define TK_LIKE_KW 47
+#define TK_BETWEEN 48
+#define TK_IN 49
+#define TK_ISNULL 50
+#define TK_NOTNULL 51
+#define TK_NE 52
+#define TK_EQ 53
+#define TK_GT 54
+#define TK_LE 55
+#define TK_LT 56
+#define TK_GE 57
+#define TK_ESCAPE 58
+#define TK_ID 59
+#define TK_COLUMNKW 60
+#define TK_DO 61
+#define TK_FOR 62
+#define TK_IGNORE 63
+#define TK_INITIALLY 64
+#define TK_INSTEAD 65
+#define TK_NO 66
+#define TK_KEY 67
+#define TK_OF 68
+#define TK_OFFSET 69
+#define TK_PRAGMA 70
+#define TK_RAISE 71
+#define TK_RECURSIVE 72
+#define TK_REPLACE 73
+#define TK_RESTRICT 74
+#define TK_ROW 75
+#define TK_ROWS 76
+#define TK_TRIGGER 77
+#define TK_VACUUM 78
+#define TK_VIEW 79
+#define TK_VIRTUAL 80
+#define TK_WITH 81
+#define TK_NULLS 82
+#define TK_FIRST 83
+#define TK_LAST 84
+#define TK_CURRENT 85
+#define TK_FOLLOWING 86
+#define TK_PARTITION 87
+#define TK_PRECEDING 88
+#define TK_RANGE 89
+#define TK_UNBOUNDED 90
+#define TK_EXCLUDE 91
+#define TK_GROUPS 92
+#define TK_OTHERS 93
+#define TK_TIES 94
+#define TK_GENERATED 95
+#define TK_ALWAYS 96
+#define TK_MATERIALIZED 97
+#define TK_REINDEX 98
+#define TK_RENAME 99
+#define TK_CTIME_KW 100
+#define TK_ANY 101
+#define TK_BITAND 102
+#define TK_BITOR 103
+#define TK_LSHIFT 104
+#define TK_RSHIFT 105
+#define TK_PLUS 106
+#define TK_MINUS 107
+#define TK_STAR 108
+#define TK_SLASH 109
+#define TK_REM 110
+#define TK_CONCAT 111
+#define TK_PTR 112
+#define TK_COLLATE 113
+#define TK_BITNOT 114
+#define TK_ON 115
+#define TK_INDEXED 116
+#define TK_STRING 117
+#define TK_JOIN_KW 118
+#define TK_CONSTRAINT 119
+#define TK_DEFAULT 120
+#define TK_NULL 121
+#define TK_PRIMARY 122
+#define TK_UNIQUE 123
+#define TK_CHECK 124
+#define TK_REFERENCES 125
+#define TK_AUTOINCR 126
+#define TK_INSERT 127
+#define TK_DELETE 128
+#define TK_UPDATE 129
+#define TK_SET 130
+#define TK_DEFERRABLE 131
+#define TK_FOREIGN 132
+#define TK_DROP 133
+#define TK_UNION 134
+#define TK_ALL 135
+#define TK_EXCEPT 136
+#define TK_INTERSECT 137
+#define TK_SELECT 138
+#define TK_VALUES 139
+#define TK_DISTINCT 140
+#define TK_DOT 141
+#define TK_FROM 142
+#define TK_JOIN 143
+#define TK_USING 144
+#define TK_ORDER 145
+#define TK_GROUP 146
+#define TK_HAVING 147
+#define TK_LIMIT 148
+#define TK_WHERE 149
+#define TK_RETURNING 150
+#define TK_INTO 151
+#define TK_NOTHING 152
+#define TK_FLOAT 153
+#define TK_BLOB 154
+#define TK_INTEGER 155
+#define TK_VARIABLE 156
+#define TK_CASE 157
+#define TK_WHEN 158
+#define TK_THEN 159
+#define TK_ELSE 160
+#define TK_INDEX 161
+#define TK_ALTER 162
+#define TK_ADD 163
+#define TK_WINDOW 164
+#define TK_OVER 165
+#define TK_FILTER 166
+#define TK_COLUMN 167
+#define TK_AGG_FUNCTION 168
+#define TK_AGG_COLUMN 169
+#define TK_TRUEFALSE 170
+#define TK_ISNOT 171
+#define TK_FUNCTION 172
+#define TK_UMINUS 173
+#define TK_UPLUS 174
+#define TK_TRUTH 175
+#define TK_REGISTER 176
+#define TK_VECTOR 177
+#define TK_SELECT_COLUMN 178
+#define TK_IF_NULL_ROW 179
+#define TK_ASTERISK 180
+#define TK_SPAN 181
+#define TK_ERROR 182
+#define TK_SPACE 183
+#define TK_ILLEGAL 184
+#endif
+/**************** End token definitions ***************************************/
/* The next sections is a series of control #defines.
** various aspects of the generated parser.
@@ -147016,7 +171345,7 @@ static void disableLookaside(Parse *pParse){
** the minor type might be the name of the identifier.
** Each non-terminal can have a different minor type.
** Terminal symbols all have the same minor type, though.
-** This macros defines the minor type for terminal
+** This macros defines the minor type for terminal
** symbols.
** YYMINORTYPE is the data type used for all minor types.
** This is typically a union of many types, one of
@@ -147049,27 +171378,31 @@ static void disableLookaside(Parse *pParse){
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned short int
-#define YYNOCODE 277
+#define YYNOCODE 319
#define YYACTIONTYPE unsigned short int
-#define YYWILDCARD 91
+#define YYWILDCARD 101
#define sqlite3ParserTOKENTYPE Token
typedef union {
int yyinit;
sqlite3ParserTOKENTYPE yy0;
- Expr* yy18;
- struct TrigEvent yy34;
- IdList* yy48;
- int yy70;
- struct {int value; int mask;} yy111;
- struct FrameBound yy119;
- SrcList* yy135;
- TriggerStep* yy207;
- Window* yy327;
- Upsert* yy340;
- const char* yy392;
- ExprList* yy420;
- With* yy449;
- Select* yy489;
+ TriggerStep* yy33;
+ Window* yy41;
+ Select* yy47;
+ SrcList* yy131;
+ struct TrigEvent yy180;
+ struct {int value; int mask;} yy231;
+ IdList* yy254;
+ u32 yy285;
+ ExprList* yy322;
+ Cte* yy385;
+ int yy394;
+ Upsert* yy444;
+ u8 yy516;
+ With* yy521;
+ const char* yy522;
+ Expr* yy528;
+ OnOrUsing yy561;
+ struct FrameBound yy595;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
@@ -147085,17 +171418,18 @@ typedef union {
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
#define YYFALLBACK 1
-#define YYNSTATE 521
-#define YYNRULE 367
-#define YYNTOKEN 155
-#define YY_MAX_SHIFT 520
-#define YY_MIN_SHIFTREDUCE 756
-#define YY_MAX_SHIFTREDUCE 1122
-#define YY_ERROR_ACTION 1123
-#define YY_ACCEPT_ACTION 1124
-#define YY_NO_ACTION 1125
-#define YY_MIN_REDUCE 1126
-#define YY_MAX_REDUCE 1492
+#define YYNSTATE 579
+#define YYNRULE 405
+#define YYNRULE_WITH_ACTION 340
+#define YYNTOKEN 185
+#define YY_MAX_SHIFT 578
+#define YY_MIN_SHIFTREDUCE 838
+#define YY_MAX_SHIFTREDUCE 1242
+#define YY_ERROR_ACTION 1243
+#define YY_ACCEPT_ACTION 1244
+#define YY_NO_ACTION 1245
+#define YY_MIN_REDUCE 1246
+#define YY_MAX_REDUCE 1650
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
@@ -147115,7 +171449,7 @@ typedef union {
/* Next are the tables used to determine what action to take based on the
** current state and lookahead token. These tables are used to implement
** functions that take a state number and lookahead value and return an
-** action integer.
+** action integer.
**
** Suppose the action integer is N. Then the action is determined as
** follows
@@ -147162,574 +171496,625 @@ typedef union {
** yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
-#define YY_ACTTAB_COUNT (2009)
+#define YY_ACTTAB_COUNT (2100)
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 368, 105, 102, 197, 105, 102, 197, 515, 1124, 1,
- /* 10 */ 1, 520, 2, 1128, 515, 1192, 1171, 1456, 275, 370,
- /* 20 */ 127, 1389, 1197, 1197, 1192, 1166, 178, 1205, 64, 64,
- /* 30 */ 477, 887, 322, 428, 348, 37, 37, 808, 362, 888,
- /* 40 */ 509, 509, 509, 112, 113, 103, 1100, 1100, 953, 956,
- /* 50 */ 946, 946, 110, 110, 111, 111, 111, 111, 365, 252,
- /* 60 */ 252, 515, 252, 252, 497, 515, 309, 515, 459, 515,
- /* 70 */ 1079, 491, 512, 478, 6, 512, 809, 134, 498, 228,
- /* 80 */ 194, 428, 37, 37, 515, 208, 64, 64, 64, 64,
- /* 90 */ 13, 13, 109, 109, 109, 109, 108, 108, 107, 107,
- /* 100 */ 107, 106, 401, 258, 381, 13, 13, 398, 397, 428,
- /* 110 */ 252, 252, 370, 476, 405, 1104, 1079, 1080, 1081, 386,
- /* 120 */ 1106, 390, 497, 512, 497, 1423, 1419, 304, 1105, 307,
- /* 130 */ 1256, 496, 370, 499, 16, 16, 112, 113, 103, 1100,
- /* 140 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111,
- /* 150 */ 111, 262, 1107, 495, 1107, 401, 112, 113, 103, 1100,
- /* 160 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111,
- /* 170 */ 111, 129, 1425, 343, 1420, 339, 1059, 492, 1057, 263,
- /* 180 */ 73, 105, 102, 197, 994, 109, 109, 109, 109, 108,
- /* 190 */ 108, 107, 107, 107, 106, 401, 370, 111, 111, 111,
- /* 200 */ 111, 104, 492, 89, 1432, 109, 109, 109, 109, 108,
- /* 210 */ 108, 107, 107, 107, 106, 401, 111, 111, 111, 111,
- /* 220 */ 112, 113, 103, 1100, 1100, 953, 956, 946, 946, 110,
- /* 230 */ 110, 111, 111, 111, 111, 109, 109, 109, 109, 108,
- /* 240 */ 108, 107, 107, 107, 106, 401, 114, 108, 108, 107,
- /* 250 */ 107, 107, 106, 401, 109, 109, 109, 109, 108, 108,
- /* 260 */ 107, 107, 107, 106, 401, 152, 399, 399, 399, 109,
- /* 270 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401,
- /* 280 */ 178, 493, 1412, 434, 1037, 1486, 1079, 515, 1486, 370,
- /* 290 */ 421, 297, 357, 412, 74, 1079, 109, 109, 109, 109,
- /* 300 */ 108, 108, 107, 107, 107, 106, 401, 1413, 37, 37,
- /* 310 */ 1431, 274, 506, 112, 113, 103, 1100, 1100, 953, 956,
- /* 320 */ 946, 946, 110, 110, 111, 111, 111, 111, 1436, 520,
- /* 330 */ 2, 1128, 1079, 1080, 1081, 430, 275, 1079, 127, 366,
- /* 340 */ 933, 1079, 1080, 1081, 220, 1205, 913, 458, 455, 454,
- /* 350 */ 392, 167, 515, 1035, 152, 445, 924, 453, 152, 874,
- /* 360 */ 923, 289, 109, 109, 109, 109, 108, 108, 107, 107,
- /* 370 */ 107, 106, 401, 13, 13, 261, 853, 252, 252, 227,
- /* 380 */ 106, 401, 370, 1079, 1080, 1081, 311, 388, 1079, 296,
- /* 390 */ 512, 923, 923, 925, 231, 323, 1255, 1388, 1423, 490,
- /* 400 */ 274, 506, 12, 208, 274, 506, 112, 113, 103, 1100,
- /* 410 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111,
- /* 420 */ 111, 1440, 286, 1128, 288, 1079, 1097, 247, 275, 1098,
- /* 430 */ 127, 387, 405, 389, 1079, 1080, 1081, 1205, 159, 238,
- /* 440 */ 255, 321, 461, 316, 460, 225, 790, 105, 102, 197,
- /* 450 */ 513, 314, 842, 842, 445, 109, 109, 109, 109, 108,
- /* 460 */ 108, 107, 107, 107, 106, 401, 515, 514, 515, 252,
- /* 470 */ 252, 1079, 1080, 1081, 435, 370, 1098, 933, 1460, 794,
- /* 480 */ 274, 506, 512, 105, 102, 197, 336, 63, 63, 64,
- /* 490 */ 64, 27, 790, 924, 287, 208, 1354, 923, 515, 112,
- /* 500 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110,
- /* 510 */ 111, 111, 111, 111, 107, 107, 107, 106, 401, 49,
- /* 520 */ 49, 515, 28, 1079, 405, 497, 421, 297, 923, 923,
- /* 530 */ 925, 186, 468, 1079, 467, 999, 999, 442, 515, 1079,
- /* 540 */ 334, 515, 45, 45, 1083, 342, 173, 168, 109, 109,
- /* 550 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 13,
- /* 560 */ 13, 205, 13, 13, 252, 252, 1195, 1195, 370, 1079,
- /* 570 */ 1080, 1081, 787, 265, 5, 359, 494, 512, 469, 1079,
- /* 580 */ 1080, 1081, 398, 397, 1079, 1079, 1080, 1081, 3, 282,
- /* 590 */ 1079, 1083, 112, 113, 103, 1100, 1100, 953, 956, 946,
- /* 600 */ 946, 110, 110, 111, 111, 111, 111, 252, 252, 1015,
- /* 610 */ 220, 1079, 873, 458, 455, 454, 943, 943, 954, 957,
- /* 620 */ 512, 252, 252, 453, 1016, 1079, 445, 1107, 1209, 1107,
- /* 630 */ 1079, 1080, 1081, 515, 512, 426, 1079, 1080, 1081, 1017,
- /* 640 */ 512, 109, 109, 109, 109, 108, 108, 107, 107, 107,
- /* 650 */ 106, 401, 1052, 515, 50, 50, 515, 1079, 1080, 1081,
- /* 660 */ 828, 370, 1051, 379, 411, 1064, 1358, 207, 408, 773,
- /* 670 */ 829, 1079, 1080, 1081, 64, 64, 322, 64, 64, 1302,
- /* 680 */ 947, 411, 410, 1358, 1360, 112, 113, 103, 1100, 1100,
- /* 690 */ 953, 956, 946, 946, 110, 110, 111, 111, 111, 111,
- /* 700 */ 294, 482, 515, 1037, 1487, 515, 434, 1487, 354, 1120,
- /* 710 */ 483, 996, 913, 485, 466, 996, 132, 178, 33, 450,
- /* 720 */ 1203, 136, 406, 64, 64, 479, 64, 64, 419, 369,
- /* 730 */ 283, 1146, 252, 252, 109, 109, 109, 109, 108, 108,
- /* 740 */ 107, 107, 107, 106, 401, 512, 224, 440, 411, 266,
- /* 750 */ 1358, 266, 252, 252, 370, 296, 416, 284, 934, 396,
- /* 760 */ 976, 470, 400, 252, 252, 512, 9, 473, 231, 500,
- /* 770 */ 354, 1036, 1035, 1488, 355, 374, 512, 1121, 112, 113,
- /* 780 */ 103, 1100, 1100, 953, 956, 946, 946, 110, 110, 111,
- /* 790 */ 111, 111, 111, 252, 252, 1015, 515, 1347, 295, 252,
- /* 800 */ 252, 252, 252, 1098, 375, 249, 512, 445, 872, 322,
- /* 810 */ 1016, 480, 512, 195, 512, 434, 273, 15, 15, 515,
- /* 820 */ 314, 515, 95, 515, 93, 1017, 367, 109, 109, 109,
- /* 830 */ 109, 108, 108, 107, 107, 107, 106, 401, 515, 1121,
- /* 840 */ 39, 39, 51, 51, 52, 52, 503, 370, 515, 1204,
- /* 850 */ 1098, 918, 439, 341, 133, 436, 223, 222, 221, 53,
- /* 860 */ 53, 322, 1400, 761, 762, 763, 515, 370, 88, 54,
- /* 870 */ 54, 112, 113, 103, 1100, 1100, 953, 956, 946, 946,
- /* 880 */ 110, 110, 111, 111, 111, 111, 407, 55, 55, 196,
- /* 890 */ 515, 112, 113, 103, 1100, 1100, 953, 956, 946, 946,
- /* 900 */ 110, 110, 111, 111, 111, 111, 135, 264, 1149, 376,
- /* 910 */ 515, 40, 40, 515, 872, 515, 993, 515, 993, 116,
- /* 920 */ 109, 109, 109, 109, 108, 108, 107, 107, 107, 106,
- /* 930 */ 401, 41, 41, 515, 43, 43, 44, 44, 56, 56,
- /* 940 */ 109, 109, 109, 109, 108, 108, 107, 107, 107, 106,
- /* 950 */ 401, 515, 379, 515, 57, 57, 515, 799, 515, 379,
- /* 960 */ 515, 445, 200, 515, 323, 515, 1397, 515, 1459, 515,
- /* 970 */ 1287, 817, 58, 58, 14, 14, 515, 59, 59, 118,
- /* 980 */ 118, 60, 60, 515, 46, 46, 61, 61, 62, 62,
- /* 990 */ 47, 47, 515, 190, 189, 91, 515, 140, 140, 515,
- /* 1000 */ 394, 515, 277, 1200, 141, 141, 515, 1115, 515, 992,
- /* 1010 */ 515, 992, 515, 69, 69, 370, 278, 48, 48, 259,
- /* 1020 */ 65, 65, 119, 119, 246, 246, 260, 66, 66, 120,
- /* 1030 */ 120, 121, 121, 117, 117, 370, 515, 512, 383, 112,
- /* 1040 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110,
- /* 1050 */ 111, 111, 111, 111, 515, 872, 515, 139, 139, 112,
- /* 1060 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110,
- /* 1070 */ 111, 111, 111, 111, 1287, 138, 138, 125, 125, 515,
- /* 1080 */ 12, 515, 281, 1287, 515, 445, 131, 1287, 109, 109,
- /* 1090 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 515,
- /* 1100 */ 124, 124, 122, 122, 515, 123, 123, 515, 109, 109,
- /* 1110 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 515,
- /* 1120 */ 68, 68, 463, 783, 515, 70, 70, 302, 67, 67,
- /* 1130 */ 1032, 253, 253, 356, 1287, 191, 196, 1433, 465, 1301,
- /* 1140 */ 38, 38, 384, 94, 512, 42, 42, 177, 848, 274,
- /* 1150 */ 506, 385, 420, 847, 1356, 441, 508, 376, 377, 153,
- /* 1160 */ 423, 872, 432, 370, 224, 251, 194, 887, 182, 293,
- /* 1170 */ 783, 848, 88, 254, 466, 888, 847, 915, 807, 806,
- /* 1180 */ 230, 1241, 910, 370, 17, 413, 797, 112, 113, 103,
- /* 1190 */ 1100, 1100, 953, 956, 946, 946, 110, 110, 111, 111,
- /* 1200 */ 111, 111, 395, 814, 815, 1175, 983, 112, 101, 103,
- /* 1210 */ 1100, 1100, 953, 956, 946, 946, 110, 110, 111, 111,
- /* 1220 */ 111, 111, 375, 422, 427, 429, 298, 230, 230, 88,
- /* 1230 */ 1240, 451, 312, 797, 226, 88, 109, 109, 109, 109,
- /* 1240 */ 108, 108, 107, 107, 107, 106, 401, 86, 433, 979,
- /* 1250 */ 927, 881, 226, 983, 230, 415, 109, 109, 109, 109,
- /* 1260 */ 108, 108, 107, 107, 107, 106, 401, 320, 845, 781,
- /* 1270 */ 846, 100, 130, 100, 1403, 290, 370, 319, 1377, 1376,
- /* 1280 */ 437, 1449, 299, 1237, 303, 306, 308, 310, 1188, 1174,
- /* 1290 */ 1173, 1172, 315, 324, 325, 1228, 370, 927, 1249, 271,
- /* 1300 */ 1286, 113, 103, 1100, 1100, 953, 956, 946, 946, 110,
- /* 1310 */ 110, 111, 111, 111, 111, 1224, 1235, 502, 501, 1292,
- /* 1320 */ 1221, 1155, 103, 1100, 1100, 953, 956, 946, 946, 110,
- /* 1330 */ 110, 111, 111, 111, 111, 1148, 1137, 1136, 1138, 1443,
- /* 1340 */ 446, 244, 184, 98, 507, 188, 4, 353, 327, 109,
- /* 1350 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401,
- /* 1360 */ 510, 329, 331, 199, 414, 456, 292, 285, 318, 109,
- /* 1370 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401,
- /* 1380 */ 11, 1271, 1279, 402, 361, 192, 1171, 1351, 431, 505,
- /* 1390 */ 346, 1350, 333, 98, 507, 504, 4, 187, 1446, 1115,
- /* 1400 */ 233, 1396, 155, 1394, 1112, 152, 72, 75, 378, 425,
- /* 1410 */ 510, 165, 149, 157, 933, 1276, 86, 30, 1268, 417,
- /* 1420 */ 96, 96, 8, 160, 161, 162, 163, 97, 418, 402,
- /* 1430 */ 517, 516, 449, 402, 923, 210, 358, 424, 1282, 438,
- /* 1440 */ 169, 214, 360, 1345, 80, 504, 31, 444, 1365, 301,
- /* 1450 */ 245, 274, 506, 216, 174, 305, 488, 447, 217, 462,
- /* 1460 */ 1139, 487, 218, 363, 933, 923, 923, 925, 926, 24,
- /* 1470 */ 96, 96, 1191, 1190, 1189, 391, 1182, 97, 1163, 402,
- /* 1480 */ 517, 516, 799, 364, 923, 1162, 317, 1161, 98, 507,
- /* 1490 */ 1181, 4, 1458, 472, 393, 269, 270, 475, 481, 1232,
- /* 1500 */ 85, 1233, 326, 328, 232, 510, 495, 1231, 330, 98,
- /* 1510 */ 507, 1230, 4, 486, 335, 923, 923, 925, 926, 24,
- /* 1520 */ 1435, 1068, 404, 181, 336, 256, 510, 115, 402, 332,
- /* 1530 */ 352, 352, 351, 241, 349, 1214, 1414, 770, 338, 10,
- /* 1540 */ 504, 340, 272, 92, 1331, 1213, 87, 183, 484, 402,
- /* 1550 */ 201, 488, 280, 239, 344, 345, 489, 1145, 29, 933,
- /* 1560 */ 279, 504, 1074, 518, 240, 96, 96, 242, 243, 519,
- /* 1570 */ 1134, 1129, 97, 154, 402, 517, 516, 372, 373, 923,
- /* 1580 */ 933, 142, 143, 128, 1381, 267, 96, 96, 852, 757,
- /* 1590 */ 203, 144, 403, 97, 1382, 402, 517, 516, 204, 1380,
- /* 1600 */ 923, 146, 1379, 1159, 1158, 71, 1156, 276, 202, 185,
- /* 1610 */ 923, 923, 925, 926, 24, 198, 257, 126, 991, 989,
- /* 1620 */ 907, 98, 507, 156, 4, 145, 158, 206, 831, 209,
- /* 1630 */ 291, 923, 923, 925, 926, 24, 1005, 911, 510, 164,
- /* 1640 */ 147, 380, 371, 382, 166, 76, 77, 274, 506, 148,
- /* 1650 */ 78, 79, 1008, 211, 212, 1004, 137, 213, 18, 300,
- /* 1660 */ 230, 402, 997, 1109, 443, 215, 32, 170, 171, 772,
- /* 1670 */ 409, 448, 319, 504, 219, 172, 452, 81, 19, 457,
- /* 1680 */ 313, 20, 82, 268, 488, 150, 810, 179, 83, 487,
- /* 1690 */ 464, 151, 933, 180, 959, 84, 1040, 34, 96, 96,
- /* 1700 */ 471, 1041, 35, 474, 193, 97, 248, 402, 517, 516,
- /* 1710 */ 1068, 404, 923, 250, 256, 880, 229, 175, 875, 352,
- /* 1720 */ 352, 351, 241, 349, 100, 21, 770, 22, 1054, 1056,
- /* 1730 */ 7, 98, 507, 1045, 4, 337, 1058, 23, 974, 201,
- /* 1740 */ 176, 280, 88, 923, 923, 925, 926, 24, 510, 279,
- /* 1750 */ 960, 958, 962, 1014, 963, 1013, 235, 234, 25, 36,
- /* 1760 */ 99, 90, 507, 928, 4, 511, 350, 782, 26, 841,
- /* 1770 */ 236, 402, 347, 1069, 237, 1125, 1125, 1451, 510, 203,
- /* 1780 */ 1450, 1125, 1125, 504, 1125, 1125, 1125, 204, 1125, 1125,
- /* 1790 */ 146, 1125, 1125, 1125, 1125, 1125, 1125, 202, 1125, 1125,
- /* 1800 */ 1125, 402, 933, 1125, 1125, 1125, 1125, 1125, 96, 96,
- /* 1810 */ 1125, 1125, 1125, 504, 1125, 97, 1125, 402, 517, 516,
- /* 1820 */ 1125, 1125, 923, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
- /* 1830 */ 1125, 371, 933, 1125, 1125, 1125, 274, 506, 96, 96,
- /* 1840 */ 1125, 1125, 1125, 1125, 1125, 97, 1125, 402, 517, 516,
- /* 1850 */ 1125, 1125, 923, 923, 923, 925, 926, 24, 1125, 409,
- /* 1860 */ 1125, 1125, 1125, 256, 1125, 1125, 1125, 1125, 352, 352,
- /* 1870 */ 351, 241, 349, 1125, 1125, 770, 1125, 1125, 1125, 1125,
- /* 1880 */ 1125, 1125, 1125, 923, 923, 925, 926, 24, 201, 1125,
- /* 1890 */ 280, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 279, 1125,
- /* 1900 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
- /* 1910 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
- /* 1920 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 203, 1125,
- /* 1930 */ 1125, 1125, 1125, 1125, 1125, 1125, 204, 1125, 1125, 146,
- /* 1940 */ 1125, 1125, 1125, 1125, 1125, 1125, 202, 1125, 1125, 1125,
- /* 1950 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
- /* 1960 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
- /* 1970 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
- /* 1980 */ 371, 1125, 1125, 1125, 1125, 274, 506, 1125, 1125, 1125,
- /* 1990 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125,
- /* 2000 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 409,
+ /* 0 */ 572, 210, 572, 119, 116, 231, 572, 119, 116, 231,
+ /* 10 */ 572, 1317, 379, 1296, 410, 566, 566, 566, 572, 411,
+ /* 20 */ 380, 1317, 1279, 42, 42, 42, 42, 210, 1529, 72,
+ /* 30 */ 72, 974, 421, 42, 42, 495, 305, 281, 305, 975,
+ /* 40 */ 399, 72, 72, 126, 127, 81, 1217, 1217, 1054, 1057,
+ /* 50 */ 1044, 1044, 124, 124, 125, 125, 125, 125, 480, 411,
+ /* 60 */ 1244, 1, 1, 578, 2, 1248, 554, 119, 116, 231,
+ /* 70 */ 319, 484, 147, 484, 528, 119, 116, 231, 533, 1330,
+ /* 80 */ 419, 527, 143, 126, 127, 81, 1217, 1217, 1054, 1057,
+ /* 90 */ 1044, 1044, 124, 124, 125, 125, 125, 125, 119, 116,
+ /* 100 */ 231, 329, 123, 123, 123, 123, 122, 122, 121, 121,
+ /* 110 */ 121, 120, 117, 448, 286, 286, 286, 286, 446, 446,
+ /* 120 */ 446, 1568, 378, 1570, 1193, 377, 1164, 569, 1164, 569,
+ /* 130 */ 411, 1568, 541, 261, 228, 448, 102, 146, 453, 318,
+ /* 140 */ 563, 242, 123, 123, 123, 123, 122, 122, 121, 121,
+ /* 150 */ 121, 120, 117, 448, 126, 127, 81, 1217, 1217, 1054,
+ /* 160 */ 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, 143,
+ /* 170 */ 296, 1193, 341, 452, 121, 121, 121, 120, 117, 448,
+ /* 180 */ 128, 1193, 1194, 1193, 149, 445, 444, 572, 120, 117,
+ /* 190 */ 448, 125, 125, 125, 125, 118, 123, 123, 123, 123,
+ /* 200 */ 122, 122, 121, 121, 121, 120, 117, 448, 458, 114,
+ /* 210 */ 13, 13, 550, 123, 123, 123, 123, 122, 122, 121,
+ /* 220 */ 121, 121, 120, 117, 448, 424, 318, 563, 1193, 1194,
+ /* 230 */ 1193, 150, 1225, 411, 1225, 125, 125, 125, 125, 123,
+ /* 240 */ 123, 123, 123, 122, 122, 121, 121, 121, 120, 117,
+ /* 250 */ 448, 469, 344, 1041, 1041, 1055, 1058, 126, 127, 81,
+ /* 260 */ 1217, 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125,
+ /* 270 */ 125, 125, 1282, 526, 224, 1193, 572, 411, 226, 519,
+ /* 280 */ 177, 83, 84, 123, 123, 123, 123, 122, 122, 121,
+ /* 290 */ 121, 121, 120, 117, 448, 1010, 16, 16, 1193, 134,
+ /* 300 */ 134, 126, 127, 81, 1217, 1217, 1054, 1057, 1044, 1044,
+ /* 310 */ 124, 124, 125, 125, 125, 125, 123, 123, 123, 123,
+ /* 320 */ 122, 122, 121, 121, 121, 120, 117, 448, 1045, 550,
+ /* 330 */ 1193, 375, 1193, 1194, 1193, 254, 1438, 401, 508, 505,
+ /* 340 */ 504, 112, 564, 570, 4, 929, 929, 435, 503, 342,
+ /* 350 */ 464, 330, 362, 396, 1238, 1193, 1194, 1193, 567, 572,
+ /* 360 */ 123, 123, 123, 123, 122, 122, 121, 121, 121, 120,
+ /* 370 */ 117, 448, 286, 286, 371, 1581, 1607, 445, 444, 155,
+ /* 380 */ 411, 449, 72, 72, 1289, 569, 1222, 1193, 1194, 1193,
+ /* 390 */ 86, 1224, 273, 561, 547, 520, 520, 572, 99, 1223,
+ /* 400 */ 6, 1281, 476, 143, 126, 127, 81, 1217, 1217, 1054,
+ /* 410 */ 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, 554,
+ /* 420 */ 13, 13, 1031, 511, 1225, 1193, 1225, 553, 110, 110,
+ /* 430 */ 224, 572, 1239, 177, 572, 429, 111, 199, 449, 573,
+ /* 440 */ 449, 432, 1555, 1019, 327, 555, 1193, 272, 289, 370,
+ /* 450 */ 514, 365, 513, 259, 72, 72, 547, 72, 72, 361,
+ /* 460 */ 318, 563, 1613, 123, 123, 123, 123, 122, 122, 121,
+ /* 470 */ 121, 121, 120, 117, 448, 1019, 1019, 1021, 1022, 28,
+ /* 480 */ 286, 286, 1193, 1194, 1193, 1159, 572, 1612, 411, 904,
+ /* 490 */ 192, 554, 358, 569, 554, 940, 537, 521, 1159, 437,
+ /* 500 */ 415, 1159, 556, 1193, 1194, 1193, 572, 548, 548, 52,
+ /* 510 */ 52, 216, 126, 127, 81, 1217, 1217, 1054, 1057, 1044,
+ /* 520 */ 1044, 124, 124, 125, 125, 125, 125, 1193, 478, 136,
+ /* 530 */ 136, 411, 286, 286, 1493, 509, 122, 122, 121, 121,
+ /* 540 */ 121, 120, 117, 448, 1010, 569, 522, 219, 545, 545,
+ /* 550 */ 318, 563, 143, 6, 536, 126, 127, 81, 1217, 1217,
+ /* 560 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
+ /* 570 */ 1557, 123, 123, 123, 123, 122, 122, 121, 121, 121,
+ /* 580 */ 120, 117, 448, 489, 1193, 1194, 1193, 486, 283, 1270,
+ /* 590 */ 960, 254, 1193, 375, 508, 505, 504, 1193, 342, 574,
+ /* 600 */ 1193, 574, 411, 294, 503, 960, 879, 193, 484, 318,
+ /* 610 */ 563, 386, 292, 382, 123, 123, 123, 123, 122, 122,
+ /* 620 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
+ /* 630 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 640 */ 125, 411, 396, 1139, 1193, 872, 101, 286, 286, 1193,
+ /* 650 */ 1194, 1193, 375, 1096, 1193, 1194, 1193, 1193, 1194, 1193,
+ /* 660 */ 569, 459, 33, 375, 235, 126, 127, 81, 1217, 1217,
+ /* 670 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
+ /* 680 */ 1437, 962, 572, 230, 961, 123, 123, 123, 123, 122,
+ /* 690 */ 122, 121, 121, 121, 120, 117, 448, 1159, 230, 1193,
+ /* 700 */ 158, 1193, 1194, 1193, 1556, 13, 13, 303, 960, 1233,
+ /* 710 */ 1159, 154, 411, 1159, 375, 1584, 1177, 5, 371, 1581,
+ /* 720 */ 431, 1239, 3, 960, 123, 123, 123, 123, 122, 122,
+ /* 730 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
+ /* 740 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 750 */ 125, 411, 210, 571, 1193, 1032, 1193, 1194, 1193, 1193,
+ /* 760 */ 390, 855, 156, 1555, 376, 404, 1101, 1101, 492, 572,
+ /* 770 */ 469, 344, 1322, 1322, 1555, 126, 127, 81, 1217, 1217,
+ /* 780 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
+ /* 790 */ 130, 572, 13, 13, 532, 123, 123, 123, 123, 122,
+ /* 800 */ 122, 121, 121, 121, 120, 117, 448, 304, 572, 457,
+ /* 810 */ 229, 1193, 1194, 1193, 13, 13, 1193, 1194, 1193, 1300,
+ /* 820 */ 467, 1270, 411, 1320, 1320, 1555, 1015, 457, 456, 436,
+ /* 830 */ 301, 72, 72, 1268, 123, 123, 123, 123, 122, 122,
+ /* 840 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
+ /* 850 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 860 */ 125, 411, 384, 1076, 1159, 286, 286, 421, 314, 280,
+ /* 870 */ 280, 287, 287, 461, 408, 407, 1539, 1159, 569, 572,
+ /* 880 */ 1159, 1196, 569, 409, 569, 126, 127, 81, 1217, 1217,
+ /* 890 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125,
+ /* 900 */ 457, 1485, 13, 13, 1541, 123, 123, 123, 123, 122,
+ /* 910 */ 122, 121, 121, 121, 120, 117, 448, 202, 572, 462,
+ /* 920 */ 1587, 578, 2, 1248, 843, 844, 845, 1563, 319, 409,
+ /* 930 */ 147, 6, 411, 257, 256, 255, 208, 1330, 9, 1196,
+ /* 940 */ 264, 72, 72, 1436, 123, 123, 123, 123, 122, 122,
+ /* 950 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217,
+ /* 960 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 970 */ 125, 572, 286, 286, 572, 1213, 411, 577, 315, 1248,
+ /* 980 */ 421, 371, 1581, 356, 319, 569, 147, 495, 529, 1644,
+ /* 990 */ 397, 935, 495, 1330, 71, 71, 934, 72, 72, 242,
+ /* 1000 */ 1328, 105, 81, 1217, 1217, 1054, 1057, 1044, 1044, 124,
+ /* 1010 */ 124, 125, 125, 125, 125, 123, 123, 123, 123, 122,
+ /* 1020 */ 122, 121, 121, 121, 120, 117, 448, 1117, 286, 286,
+ /* 1030 */ 1422, 452, 1528, 1213, 443, 286, 286, 1492, 1355, 313,
+ /* 1040 */ 478, 569, 1118, 454, 351, 495, 354, 1266, 569, 209,
+ /* 1050 */ 572, 418, 179, 572, 1031, 242, 385, 1119, 523, 123,
+ /* 1060 */ 123, 123, 123, 122, 122, 121, 121, 121, 120, 117,
+ /* 1070 */ 448, 1020, 108, 72, 72, 1019, 13, 13, 915, 572,
+ /* 1080 */ 1498, 572, 286, 286, 98, 530, 1537, 452, 916, 1334,
+ /* 1090 */ 1329, 203, 411, 286, 286, 569, 152, 211, 1498, 1500,
+ /* 1100 */ 426, 569, 56, 56, 57, 57, 569, 1019, 1019, 1021,
+ /* 1110 */ 447, 572, 411, 531, 12, 297, 126, 127, 81, 1217,
+ /* 1120 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 1130 */ 125, 572, 411, 867, 15, 15, 126, 127, 81, 1217,
+ /* 1140 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 1150 */ 125, 373, 529, 264, 44, 44, 126, 115, 81, 1217,
+ /* 1160 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125,
+ /* 1170 */ 125, 1498, 478, 1271, 417, 123, 123, 123, 123, 122,
+ /* 1180 */ 122, 121, 121, 121, 120, 117, 448, 205, 1213, 495,
+ /* 1190 */ 430, 867, 468, 322, 495, 123, 123, 123, 123, 122,
+ /* 1200 */ 122, 121, 121, 121, 120, 117, 448, 572, 557, 1140,
+ /* 1210 */ 1642, 1422, 1642, 543, 572, 123, 123, 123, 123, 122,
+ /* 1220 */ 122, 121, 121, 121, 120, 117, 448, 572, 1422, 572,
+ /* 1230 */ 13, 13, 542, 323, 1325, 411, 334, 58, 58, 349,
+ /* 1240 */ 1422, 1170, 326, 286, 286, 549, 1213, 300, 895, 530,
+ /* 1250 */ 45, 45, 59, 59, 1140, 1643, 569, 1643, 565, 417,
+ /* 1260 */ 127, 81, 1217, 1217, 1054, 1057, 1044, 1044, 124, 124,
+ /* 1270 */ 125, 125, 125, 125, 1367, 373, 500, 290, 1193, 512,
+ /* 1280 */ 1366, 427, 394, 394, 393, 275, 391, 896, 1138, 852,
+ /* 1290 */ 478, 258, 1422, 1170, 463, 1159, 12, 331, 428, 333,
+ /* 1300 */ 1117, 460, 236, 258, 325, 460, 544, 1544, 1159, 1098,
+ /* 1310 */ 491, 1159, 324, 1098, 440, 1118, 335, 516, 123, 123,
+ /* 1320 */ 123, 123, 122, 122, 121, 121, 121, 120, 117, 448,
+ /* 1330 */ 1119, 318, 563, 1138, 572, 1193, 1194, 1193, 112, 564,
+ /* 1340 */ 201, 4, 238, 433, 935, 490, 285, 228, 1517, 934,
+ /* 1350 */ 170, 560, 572, 142, 1516, 567, 572, 60, 60, 572,
+ /* 1360 */ 416, 572, 441, 572, 535, 302, 875, 8, 487, 572,
+ /* 1370 */ 237, 572, 416, 572, 485, 61, 61, 572, 449, 62,
+ /* 1380 */ 62, 332, 63, 63, 46, 46, 47, 47, 361, 572,
+ /* 1390 */ 561, 572, 48, 48, 50, 50, 51, 51, 572, 295,
+ /* 1400 */ 64, 64, 482, 295, 539, 412, 471, 1031, 572, 538,
+ /* 1410 */ 318, 563, 65, 65, 66, 66, 409, 475, 572, 1031,
+ /* 1420 */ 572, 14, 14, 875, 1020, 110, 110, 409, 1019, 572,
+ /* 1430 */ 474, 67, 67, 111, 455, 449, 573, 449, 98, 317,
+ /* 1440 */ 1019, 132, 132, 133, 133, 572, 1561, 572, 974, 409,
+ /* 1450 */ 6, 1562, 68, 68, 1560, 6, 975, 572, 6, 1559,
+ /* 1460 */ 1019, 1019, 1021, 6, 346, 218, 101, 531, 53, 53,
+ /* 1470 */ 69, 69, 1019, 1019, 1021, 1022, 28, 1586, 1181, 451,
+ /* 1480 */ 70, 70, 290, 87, 215, 31, 1363, 394, 394, 393,
+ /* 1490 */ 275, 391, 350, 109, 852, 107, 572, 112, 564, 483,
+ /* 1500 */ 4, 1212, 572, 239, 153, 572, 39, 236, 1299, 325,
+ /* 1510 */ 112, 564, 1298, 4, 567, 572, 32, 324, 572, 54,
+ /* 1520 */ 54, 572, 1135, 353, 398, 165, 165, 567, 166, 166,
+ /* 1530 */ 572, 291, 355, 572, 17, 357, 572, 449, 77, 77,
+ /* 1540 */ 1313, 55, 55, 1297, 73, 73, 572, 238, 470, 561,
+ /* 1550 */ 449, 472, 364, 135, 135, 170, 74, 74, 142, 163,
+ /* 1560 */ 163, 374, 561, 539, 572, 321, 572, 886, 540, 137,
+ /* 1570 */ 137, 339, 1353, 422, 298, 237, 539, 572, 1031, 572,
+ /* 1580 */ 340, 538, 101, 369, 110, 110, 162, 131, 131, 164,
+ /* 1590 */ 164, 1031, 111, 368, 449, 573, 449, 110, 110, 1019,
+ /* 1600 */ 157, 157, 141, 141, 572, 111, 572, 449, 573, 449,
+ /* 1610 */ 412, 288, 1019, 572, 882, 318, 563, 572, 219, 572,
+ /* 1620 */ 241, 1012, 477, 263, 263, 894, 893, 140, 140, 138,
+ /* 1630 */ 138, 1019, 1019, 1021, 1022, 28, 139, 139, 525, 455,
+ /* 1640 */ 76, 76, 78, 78, 1019, 1019, 1021, 1022, 28, 1181,
+ /* 1650 */ 451, 572, 1083, 290, 112, 564, 1575, 4, 394, 394,
+ /* 1660 */ 393, 275, 391, 572, 1023, 852, 572, 479, 345, 263,
+ /* 1670 */ 101, 567, 882, 1376, 75, 75, 1421, 501, 236, 260,
+ /* 1680 */ 325, 112, 564, 359, 4, 101, 43, 43, 324, 49,
+ /* 1690 */ 49, 901, 902, 161, 449, 101, 977, 978, 567, 1079,
+ /* 1700 */ 1349, 260, 965, 932, 263, 114, 561, 1095, 517, 1095,
+ /* 1710 */ 1083, 1094, 865, 1094, 151, 933, 1144, 114, 238, 1361,
+ /* 1720 */ 558, 449, 1023, 559, 1426, 1278, 170, 1269, 1257, 142,
+ /* 1730 */ 1601, 1256, 1258, 561, 1594, 1031, 496, 278, 213, 1346,
+ /* 1740 */ 310, 110, 110, 939, 311, 312, 237, 11, 234, 111,
+ /* 1750 */ 221, 449, 573, 449, 293, 395, 1019, 1408, 337, 1403,
+ /* 1760 */ 1396, 338, 1031, 299, 343, 1413, 1412, 481, 110, 110,
+ /* 1770 */ 506, 402, 225, 1296, 206, 367, 111, 1358, 449, 573,
+ /* 1780 */ 449, 412, 1359, 1019, 1489, 1488, 318, 563, 1019, 1019,
+ /* 1790 */ 1021, 1022, 28, 562, 207, 220, 80, 564, 389, 4,
+ /* 1800 */ 1597, 1357, 552, 1356, 1233, 181, 267, 232, 1536, 1534,
+ /* 1810 */ 455, 1230, 420, 567, 82, 1019, 1019, 1021, 1022, 28,
+ /* 1820 */ 86, 217, 85, 1494, 190, 175, 183, 465, 185, 466,
+ /* 1830 */ 36, 1409, 186, 187, 188, 499, 449, 244, 37, 99,
+ /* 1840 */ 400, 1415, 1414, 488, 1417, 194, 473, 403, 561, 1483,
+ /* 1850 */ 248, 92, 1505, 494, 198, 279, 112, 564, 250, 4,
+ /* 1860 */ 348, 497, 405, 352, 1259, 251, 252, 515, 1316, 434,
+ /* 1870 */ 1315, 1314, 94, 567, 1307, 886, 1306, 1031, 226, 406,
+ /* 1880 */ 1611, 1610, 438, 110, 110, 1580, 1286, 524, 439, 308,
+ /* 1890 */ 266, 111, 1285, 449, 573, 449, 449, 309, 1019, 366,
+ /* 1900 */ 1284, 1609, 265, 1566, 1565, 442, 372, 1381, 561, 129,
+ /* 1910 */ 550, 1380, 10, 1470, 383, 106, 316, 551, 100, 35,
+ /* 1920 */ 534, 575, 212, 1339, 381, 387, 1187, 1338, 274, 276,
+ /* 1930 */ 1019, 1019, 1021, 1022, 28, 277, 413, 1031, 576, 1254,
+ /* 1940 */ 388, 1521, 1249, 110, 110, 167, 1522, 168, 148, 1520,
+ /* 1950 */ 1519, 111, 306, 449, 573, 449, 222, 223, 1019, 839,
+ /* 1960 */ 169, 79, 450, 214, 414, 233, 320, 145, 1093, 1091,
+ /* 1970 */ 328, 182, 171, 1212, 918, 184, 240, 336, 243, 1107,
+ /* 1980 */ 189, 172, 173, 423, 425, 88, 180, 191, 89, 90,
+ /* 1990 */ 1019, 1019, 1021, 1022, 28, 91, 174, 1110, 245, 1106,
+ /* 2000 */ 246, 159, 18, 247, 347, 1099, 263, 195, 1227, 493,
+ /* 2010 */ 249, 196, 38, 854, 498, 368, 253, 360, 897, 197,
+ /* 2020 */ 502, 93, 19, 20, 507, 884, 363, 510, 95, 307,
+ /* 2030 */ 160, 96, 518, 97, 1175, 1060, 1146, 40, 21, 227,
+ /* 2040 */ 176, 1145, 282, 284, 969, 200, 963, 114, 262, 1165,
+ /* 2050 */ 22, 23, 24, 1161, 1169, 25, 1163, 1150, 34, 26,
+ /* 2060 */ 1168, 546, 27, 204, 101, 103, 104, 1074, 7, 1061,
+ /* 2070 */ 1059, 1063, 1116, 1064, 1115, 268, 269, 29, 41, 270,
+ /* 2080 */ 1024, 866, 113, 30, 568, 392, 1183, 144, 178, 1182,
+ /* 2090 */ 271, 928, 1245, 1245, 1245, 1245, 1245, 1245, 1245, 1602,
};
static const YYCODETYPE yy_lookahead[] = {
- /* 0 */ 184, 238, 239, 240, 238, 239, 240, 163, 155, 156,
- /* 10 */ 157, 158, 159, 160, 163, 191, 192, 183, 165, 19,
- /* 20 */ 167, 258, 202, 203, 200, 191, 163, 174, 184, 185,
- /* 30 */ 174, 31, 163, 163, 171, 184, 185, 35, 175, 39,
- /* 40 */ 179, 180, 181, 43, 44, 45, 46, 47, 48, 49,
- /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 184, 206,
- /* 60 */ 207, 163, 206, 207, 220, 163, 16, 163, 66, 163,
- /* 70 */ 59, 270, 219, 229, 273, 219, 74, 208, 174, 223,
- /* 80 */ 224, 163, 184, 185, 163, 232, 184, 185, 184, 185,
- /* 90 */ 184, 185, 92, 93, 94, 95, 96, 97, 98, 99,
- /* 100 */ 100, 101, 102, 233, 198, 184, 185, 96, 97, 163,
- /* 110 */ 206, 207, 19, 163, 261, 104, 105, 106, 107, 198,
- /* 120 */ 109, 119, 220, 219, 220, 274, 275, 77, 117, 79,
- /* 130 */ 187, 229, 19, 229, 184, 185, 43, 44, 45, 46,
- /* 140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 150 */ 57, 233, 141, 134, 143, 102, 43, 44, 45, 46,
- /* 160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 170 */ 57, 152, 274, 216, 276, 218, 83, 163, 85, 233,
- /* 180 */ 67, 238, 239, 240, 11, 92, 93, 94, 95, 96,
- /* 190 */ 97, 98, 99, 100, 101, 102, 19, 54, 55, 56,
- /* 200 */ 57, 58, 163, 26, 163, 92, 93, 94, 95, 96,
- /* 210 */ 97, 98, 99, 100, 101, 102, 54, 55, 56, 57,
- /* 220 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 230 */ 53, 54, 55, 56, 57, 92, 93, 94, 95, 96,
- /* 240 */ 97, 98, 99, 100, 101, 102, 69, 96, 97, 98,
- /* 250 */ 99, 100, 101, 102, 92, 93, 94, 95, 96, 97,
- /* 260 */ 98, 99, 100, 101, 102, 81, 179, 180, 181, 92,
- /* 270 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
- /* 280 */ 163, 267, 268, 163, 22, 23, 59, 163, 26, 19,
- /* 290 */ 117, 118, 175, 109, 24, 59, 92, 93, 94, 95,
- /* 300 */ 96, 97, 98, 99, 100, 101, 102, 268, 184, 185,
- /* 310 */ 269, 127, 128, 43, 44, 45, 46, 47, 48, 49,
- /* 320 */ 50, 51, 52, 53, 54, 55, 56, 57, 157, 158,
- /* 330 */ 159, 160, 105, 106, 107, 163, 165, 59, 167, 184,
- /* 340 */ 90, 105, 106, 107, 108, 174, 73, 111, 112, 113,
- /* 350 */ 19, 22, 163, 91, 81, 163, 106, 121, 81, 132,
- /* 360 */ 110, 16, 92, 93, 94, 95, 96, 97, 98, 99,
- /* 370 */ 100, 101, 102, 184, 185, 255, 98, 206, 207, 26,
- /* 380 */ 101, 102, 19, 105, 106, 107, 23, 198, 59, 116,
- /* 390 */ 219, 141, 142, 143, 24, 163, 187, 205, 274, 275,
- /* 400 */ 127, 128, 182, 232, 127, 128, 43, 44, 45, 46,
- /* 410 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 420 */ 57, 158, 77, 160, 79, 59, 26, 182, 165, 59,
- /* 430 */ 167, 199, 261, 102, 105, 106, 107, 174, 72, 108,
- /* 440 */ 109, 110, 111, 112, 113, 114, 59, 238, 239, 240,
- /* 450 */ 123, 120, 125, 126, 163, 92, 93, 94, 95, 96,
- /* 460 */ 97, 98, 99, 100, 101, 102, 163, 163, 163, 206,
- /* 470 */ 207, 105, 106, 107, 254, 19, 106, 90, 197, 23,
- /* 480 */ 127, 128, 219, 238, 239, 240, 22, 184, 185, 184,
- /* 490 */ 185, 22, 105, 106, 149, 232, 205, 110, 163, 43,
- /* 500 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 510 */ 54, 55, 56, 57, 98, 99, 100, 101, 102, 184,
- /* 520 */ 185, 163, 53, 59, 261, 220, 117, 118, 141, 142,
- /* 530 */ 143, 131, 174, 59, 229, 116, 117, 118, 163, 59,
- /* 540 */ 163, 163, 184, 185, 59, 242, 72, 22, 92, 93,
- /* 550 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 184,
- /* 560 */ 185, 24, 184, 185, 206, 207, 202, 203, 19, 105,
- /* 570 */ 106, 107, 23, 198, 22, 174, 198, 219, 220, 105,
- /* 580 */ 106, 107, 96, 97, 59, 105, 106, 107, 22, 174,
- /* 590 */ 59, 106, 43, 44, 45, 46, 47, 48, 49, 50,
- /* 600 */ 51, 52, 53, 54, 55, 56, 57, 206, 207, 12,
- /* 610 */ 108, 59, 132, 111, 112, 113, 46, 47, 48, 49,
- /* 620 */ 219, 206, 207, 121, 27, 59, 163, 141, 207, 143,
- /* 630 */ 105, 106, 107, 163, 219, 234, 105, 106, 107, 42,
- /* 640 */ 219, 92, 93, 94, 95, 96, 97, 98, 99, 100,
- /* 650 */ 101, 102, 76, 163, 184, 185, 163, 105, 106, 107,
- /* 660 */ 63, 19, 86, 163, 163, 23, 163, 130, 205, 21,
- /* 670 */ 73, 105, 106, 107, 184, 185, 163, 184, 185, 237,
- /* 680 */ 110, 180, 181, 180, 181, 43, 44, 45, 46, 47,
- /* 690 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 700 */ 174, 163, 163, 22, 23, 163, 163, 26, 22, 23,
- /* 710 */ 220, 29, 73, 220, 272, 33, 22, 163, 24, 19,
- /* 720 */ 174, 208, 259, 184, 185, 19, 184, 185, 80, 175,
- /* 730 */ 230, 174, 206, 207, 92, 93, 94, 95, 96, 97,
- /* 740 */ 98, 99, 100, 101, 102, 219, 46, 65, 247, 195,
- /* 750 */ 247, 197, 206, 207, 19, 116, 117, 118, 23, 220,
- /* 760 */ 112, 174, 220, 206, 207, 219, 22, 174, 24, 174,
- /* 770 */ 22, 23, 91, 264, 265, 168, 219, 91, 43, 44,
- /* 780 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
- /* 790 */ 55, 56, 57, 206, 207, 12, 163, 149, 255, 206,
- /* 800 */ 207, 206, 207, 59, 104, 23, 219, 163, 26, 163,
- /* 810 */ 27, 105, 219, 163, 219, 163, 211, 184, 185, 163,
- /* 820 */ 120, 163, 146, 163, 148, 42, 221, 92, 93, 94,
- /* 830 */ 95, 96, 97, 98, 99, 100, 101, 102, 163, 91,
- /* 840 */ 184, 185, 184, 185, 184, 185, 63, 19, 163, 205,
- /* 850 */ 106, 23, 245, 163, 208, 248, 116, 117, 118, 184,
- /* 860 */ 185, 163, 163, 7, 8, 9, 163, 19, 26, 184,
- /* 870 */ 185, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- /* 880 */ 52, 53, 54, 55, 56, 57, 163, 184, 185, 107,
- /* 890 */ 163, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- /* 900 */ 52, 53, 54, 55, 56, 57, 208, 255, 177, 178,
- /* 910 */ 163, 184, 185, 163, 132, 163, 141, 163, 143, 22,
- /* 920 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
- /* 930 */ 102, 184, 185, 163, 184, 185, 184, 185, 184, 185,
- /* 940 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
- /* 950 */ 102, 163, 163, 163, 184, 185, 163, 115, 163, 163,
- /* 960 */ 163, 163, 15, 163, 163, 163, 163, 163, 23, 163,
- /* 970 */ 163, 26, 184, 185, 184, 185, 163, 184, 185, 184,
- /* 980 */ 185, 184, 185, 163, 184, 185, 184, 185, 184, 185,
- /* 990 */ 184, 185, 163, 96, 97, 147, 163, 184, 185, 163,
- /* 1000 */ 199, 163, 163, 205, 184, 185, 163, 60, 163, 141,
- /* 1010 */ 163, 143, 163, 184, 185, 19, 163, 184, 185, 230,
- /* 1020 */ 184, 185, 184, 185, 206, 207, 230, 184, 185, 184,
- /* 1030 */ 185, 184, 185, 184, 185, 19, 163, 219, 231, 43,
- /* 1040 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 1050 */ 54, 55, 56, 57, 163, 26, 163, 184, 185, 43,
- /* 1060 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 1070 */ 54, 55, 56, 57, 163, 184, 185, 184, 185, 163,
- /* 1080 */ 182, 163, 163, 163, 163, 163, 22, 163, 92, 93,
- /* 1090 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 163,
- /* 1100 */ 184, 185, 184, 185, 163, 184, 185, 163, 92, 93,
- /* 1110 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 163,
- /* 1120 */ 184, 185, 98, 59, 163, 184, 185, 205, 184, 185,
- /* 1130 */ 23, 206, 207, 26, 163, 26, 107, 153, 154, 237,
- /* 1140 */ 184, 185, 231, 147, 219, 184, 185, 249, 124, 127,
- /* 1150 */ 128, 231, 254, 129, 163, 231, 177, 178, 262, 263,
- /* 1160 */ 118, 132, 19, 19, 46, 223, 224, 31, 24, 23,
- /* 1170 */ 106, 124, 26, 22, 272, 39, 129, 23, 109, 110,
- /* 1180 */ 26, 163, 140, 19, 22, 234, 59, 43, 44, 45,
- /* 1190 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 1200 */ 56, 57, 231, 7, 8, 193, 59, 43, 44, 45,
- /* 1210 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 1220 */ 56, 57, 104, 61, 23, 23, 23, 26, 26, 26,
- /* 1230 */ 163, 23, 23, 106, 26, 26, 92, 93, 94, 95,
- /* 1240 */ 96, 97, 98, 99, 100, 101, 102, 138, 105, 23,
- /* 1250 */ 59, 23, 26, 106, 26, 163, 92, 93, 94, 95,
- /* 1260 */ 96, 97, 98, 99, 100, 101, 102, 110, 23, 23,
- /* 1270 */ 23, 26, 26, 26, 163, 163, 19, 120, 163, 163,
- /* 1280 */ 163, 130, 163, 163, 163, 163, 163, 163, 163, 193,
- /* 1290 */ 193, 163, 163, 163, 163, 225, 19, 106, 163, 222,
- /* 1300 */ 163, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 1310 */ 53, 54, 55, 56, 57, 163, 163, 203, 163, 163,
- /* 1320 */ 222, 163, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 1330 */ 53, 54, 55, 56, 57, 163, 163, 163, 163, 163,
- /* 1340 */ 251, 250, 209, 19, 20, 182, 22, 161, 222, 92,
- /* 1350 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
- /* 1360 */ 36, 222, 222, 260, 226, 188, 256, 226, 187, 92,
- /* 1370 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
- /* 1380 */ 210, 213, 213, 59, 213, 196, 192, 187, 256, 244,
- /* 1390 */ 212, 187, 226, 19, 20, 71, 22, 210, 166, 60,
- /* 1400 */ 130, 170, 260, 170, 38, 81, 257, 257, 170, 104,
- /* 1410 */ 36, 22, 43, 201, 90, 236, 138, 235, 213, 18,
- /* 1420 */ 96, 97, 48, 204, 204, 204, 204, 103, 170, 105,
- /* 1430 */ 106, 107, 18, 59, 110, 169, 213, 213, 201, 170,
- /* 1440 */ 201, 169, 236, 213, 146, 71, 235, 62, 253, 252,
- /* 1450 */ 170, 127, 128, 169, 22, 170, 82, 189, 169, 104,
- /* 1460 */ 170, 87, 169, 189, 90, 141, 142, 143, 144, 145,
- /* 1470 */ 96, 97, 186, 186, 186, 64, 194, 103, 186, 105,
- /* 1480 */ 106, 107, 115, 189, 110, 188, 186, 186, 19, 20,
- /* 1490 */ 194, 22, 186, 189, 102, 246, 246, 189, 133, 228,
- /* 1500 */ 104, 228, 227, 227, 170, 36, 134, 228, 227, 19,
- /* 1510 */ 20, 228, 22, 84, 271, 141, 142, 143, 144, 145,
- /* 1520 */ 0, 1, 2, 216, 22, 5, 36, 137, 59, 227,
- /* 1530 */ 10, 11, 12, 13, 14, 217, 269, 17, 216, 22,
- /* 1540 */ 71, 170, 243, 146, 241, 217, 136, 215, 135, 59,
- /* 1550 */ 30, 82, 32, 25, 214, 213, 87, 173, 26, 90,
- /* 1560 */ 40, 71, 13, 172, 164, 96, 97, 164, 6, 162,
- /* 1570 */ 162, 162, 103, 263, 105, 106, 107, 266, 266, 110,
- /* 1580 */ 90, 176, 176, 190, 182, 190, 96, 97, 98, 4,
- /* 1590 */ 70, 176, 3, 103, 182, 105, 106, 107, 78, 182,
- /* 1600 */ 110, 81, 182, 182, 182, 182, 182, 151, 88, 22,
- /* 1610 */ 141, 142, 143, 144, 145, 15, 89, 16, 23, 23,
- /* 1620 */ 128, 19, 20, 139, 22, 119, 131, 24, 20, 133,
- /* 1630 */ 16, 141, 142, 143, 144, 145, 1, 140, 36, 131,
- /* 1640 */ 119, 61, 122, 37, 139, 53, 53, 127, 128, 119,
- /* 1650 */ 53, 53, 105, 34, 130, 1, 5, 104, 22, 149,
- /* 1660 */ 26, 59, 68, 75, 41, 130, 24, 68, 104, 20,
- /* 1670 */ 150, 19, 120, 71, 114, 22, 67, 22, 22, 67,
- /* 1680 */ 23, 22, 22, 67, 82, 37, 28, 23, 138, 87,
- /* 1690 */ 22, 153, 90, 23, 23, 26, 23, 22, 96, 97,
- /* 1700 */ 24, 23, 22, 24, 130, 103, 23, 105, 106, 107,
- /* 1710 */ 1, 2, 110, 23, 5, 105, 34, 22, 132, 10,
- /* 1720 */ 11, 12, 13, 14, 26, 34, 17, 34, 85, 83,
- /* 1730 */ 44, 19, 20, 23, 22, 24, 75, 34, 23, 30,
- /* 1740 */ 26, 32, 26, 141, 142, 143, 144, 145, 36, 40,
- /* 1750 */ 23, 23, 23, 23, 11, 23, 22, 26, 22, 22,
- /* 1760 */ 22, 19, 20, 23, 22, 26, 15, 23, 22, 124,
- /* 1770 */ 130, 59, 23, 1, 130, 277, 277, 130, 36, 70,
- /* 1780 */ 130, 277, 277, 71, 277, 277, 277, 78, 277, 277,
- /* 1790 */ 81, 277, 277, 277, 277, 277, 277, 88, 277, 277,
- /* 1800 */ 277, 59, 90, 277, 277, 277, 277, 277, 96, 97,
- /* 1810 */ 277, 277, 277, 71, 277, 103, 277, 105, 106, 107,
- /* 1820 */ 277, 277, 110, 277, 277, 277, 277, 277, 277, 277,
- /* 1830 */ 277, 122, 90, 277, 277, 277, 127, 128, 96, 97,
- /* 1840 */ 277, 277, 277, 277, 277, 103, 277, 105, 106, 107,
- /* 1850 */ 277, 277, 110, 141, 142, 143, 144, 145, 277, 150,
- /* 1860 */ 277, 277, 277, 5, 277, 277, 277, 277, 10, 11,
- /* 1870 */ 12, 13, 14, 277, 277, 17, 277, 277, 277, 277,
- /* 1880 */ 277, 277, 277, 141, 142, 143, 144, 145, 30, 277,
- /* 1890 */ 32, 277, 277, 277, 277, 277, 277, 277, 40, 277,
- /* 1900 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
- /* 1910 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
- /* 1920 */ 277, 277, 277, 277, 277, 277, 277, 277, 70, 277,
- /* 1930 */ 277, 277, 277, 277, 277, 277, 78, 277, 277, 81,
- /* 1940 */ 277, 277, 277, 277, 277, 277, 88, 277, 277, 277,
- /* 1950 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
- /* 1960 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
- /* 1970 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
- /* 1980 */ 122, 277, 277, 277, 277, 127, 128, 277, 277, 277,
- /* 1990 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
- /* 2000 */ 277, 277, 277, 277, 277, 277, 277, 277, 150, 277,
- /* 2010 */ 277, 277, 277, 277, 277, 277, 277, 277, 277,
+ /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276,
+ /* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19,
+ /* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216,
+ /* 30 */ 217, 31, 193, 216, 217, 193, 228, 213, 230, 39,
+ /* 40 */ 206, 216, 217, 43, 44, 45, 46, 47, 48, 49,
+ /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 193, 19,
+ /* 60 */ 185, 186, 187, 188, 189, 190, 253, 274, 275, 276,
+ /* 70 */ 195, 193, 197, 193, 261, 274, 275, 276, 253, 204,
+ /* 80 */ 238, 204, 81, 43, 44, 45, 46, 47, 48, 49,
+ /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 274, 275,
+ /* 100 */ 276, 262, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 110 */ 110, 111, 112, 113, 239, 240, 239, 240, 210, 211,
+ /* 120 */ 212, 314, 315, 314, 59, 316, 86, 252, 88, 252,
+ /* 130 */ 19, 314, 315, 256, 257, 113, 25, 72, 296, 138,
+ /* 140 */ 139, 266, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 150 */ 110, 111, 112, 113, 43, 44, 45, 46, 47, 48,
+ /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 81,
+ /* 170 */ 292, 59, 292, 298, 108, 109, 110, 111, 112, 113,
+ /* 180 */ 69, 116, 117, 118, 72, 106, 107, 193, 111, 112,
+ /* 190 */ 113, 54, 55, 56, 57, 58, 102, 103, 104, 105,
+ /* 200 */ 106, 107, 108, 109, 110, 111, 112, 113, 120, 25,
+ /* 210 */ 216, 217, 145, 102, 103, 104, 105, 106, 107, 108,
+ /* 220 */ 109, 110, 111, 112, 113, 231, 138, 139, 116, 117,
+ /* 230 */ 118, 164, 153, 19, 155, 54, 55, 56, 57, 102,
+ /* 240 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ /* 250 */ 113, 128, 129, 46, 47, 48, 49, 43, 44, 45,
+ /* 260 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ /* 270 */ 56, 57, 216, 193, 25, 59, 193, 19, 165, 166,
+ /* 280 */ 193, 67, 24, 102, 103, 104, 105, 106, 107, 108,
+ /* 290 */ 109, 110, 111, 112, 113, 73, 216, 217, 59, 216,
+ /* 300 */ 217, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ /* 310 */ 52, 53, 54, 55, 56, 57, 102, 103, 104, 105,
+ /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 121, 145,
+ /* 330 */ 59, 193, 116, 117, 118, 119, 273, 204, 122, 123,
+ /* 340 */ 124, 19, 20, 134, 22, 136, 137, 19, 132, 127,
+ /* 350 */ 128, 129, 24, 22, 23, 116, 117, 118, 36, 193,
+ /* 360 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ /* 370 */ 112, 113, 239, 240, 311, 312, 215, 106, 107, 241,
+ /* 380 */ 19, 59, 216, 217, 223, 252, 115, 116, 117, 118,
+ /* 390 */ 151, 120, 26, 71, 193, 308, 309, 193, 149, 128,
+ /* 400 */ 313, 216, 269, 81, 43, 44, 45, 46, 47, 48,
+ /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 253,
+ /* 420 */ 216, 217, 100, 95, 153, 59, 155, 261, 106, 107,
+ /* 430 */ 25, 193, 101, 193, 193, 231, 114, 25, 116, 117,
+ /* 440 */ 118, 113, 304, 121, 193, 204, 59, 119, 120, 121,
+ /* 450 */ 122, 123, 124, 125, 216, 217, 193, 216, 217, 131,
+ /* 460 */ 138, 139, 230, 102, 103, 104, 105, 106, 107, 108,
+ /* 470 */ 109, 110, 111, 112, 113, 153, 154, 155, 156, 157,
+ /* 480 */ 239, 240, 116, 117, 118, 76, 193, 23, 19, 25,
+ /* 490 */ 22, 253, 23, 252, 253, 108, 87, 204, 89, 261,
+ /* 500 */ 198, 92, 261, 116, 117, 118, 193, 306, 307, 216,
+ /* 510 */ 217, 150, 43, 44, 45, 46, 47, 48, 49, 50,
+ /* 520 */ 51, 52, 53, 54, 55, 56, 57, 59, 193, 216,
+ /* 530 */ 217, 19, 239, 240, 283, 23, 106, 107, 108, 109,
+ /* 540 */ 110, 111, 112, 113, 73, 252, 253, 142, 308, 309,
+ /* 550 */ 138, 139, 81, 313, 145, 43, 44, 45, 46, 47,
+ /* 560 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ /* 570 */ 307, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+ /* 580 */ 111, 112, 113, 281, 116, 117, 118, 285, 23, 193,
+ /* 590 */ 25, 119, 59, 193, 122, 123, 124, 59, 127, 203,
+ /* 600 */ 59, 205, 19, 268, 132, 25, 23, 22, 193, 138,
+ /* 610 */ 139, 249, 204, 251, 102, 103, 104, 105, 106, 107,
+ /* 620 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
+ /* 630 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ /* 640 */ 57, 19, 22, 23, 59, 23, 25, 239, 240, 116,
+ /* 650 */ 117, 118, 193, 11, 116, 117, 118, 116, 117, 118,
+ /* 660 */ 252, 269, 22, 193, 15, 43, 44, 45, 46, 47,
+ /* 670 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ /* 680 */ 273, 143, 193, 118, 143, 102, 103, 104, 105, 106,
+ /* 690 */ 107, 108, 109, 110, 111, 112, 113, 76, 118, 59,
+ /* 700 */ 241, 116, 117, 118, 304, 216, 217, 292, 143, 60,
+ /* 710 */ 89, 241, 19, 92, 193, 193, 23, 22, 311, 312,
+ /* 720 */ 231, 101, 22, 143, 102, 103, 104, 105, 106, 107,
+ /* 730 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
+ /* 740 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ /* 750 */ 57, 19, 193, 193, 59, 23, 116, 117, 118, 59,
+ /* 760 */ 201, 21, 241, 304, 193, 206, 127, 128, 129, 193,
+ /* 770 */ 128, 129, 235, 236, 304, 43, 44, 45, 46, 47,
+ /* 780 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ /* 790 */ 22, 193, 216, 217, 193, 102, 103, 104, 105, 106,
+ /* 800 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 193,
+ /* 810 */ 193, 116, 117, 118, 216, 217, 116, 117, 118, 226,
+ /* 820 */ 80, 193, 19, 235, 236, 304, 23, 211, 212, 231,
+ /* 830 */ 204, 216, 217, 205, 102, 103, 104, 105, 106, 107,
+ /* 840 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
+ /* 850 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ /* 860 */ 57, 19, 193, 123, 76, 239, 240, 193, 253, 239,
+ /* 870 */ 240, 239, 240, 244, 106, 107, 193, 89, 252, 193,
+ /* 880 */ 92, 59, 252, 254, 252, 43, 44, 45, 46, 47,
+ /* 890 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ /* 900 */ 284, 161, 216, 217, 193, 102, 103, 104, 105, 106,
+ /* 910 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 244,
+ /* 920 */ 187, 188, 189, 190, 7, 8, 9, 309, 195, 254,
+ /* 930 */ 197, 313, 19, 127, 128, 129, 262, 204, 22, 117,
+ /* 940 */ 24, 216, 217, 273, 102, 103, 104, 105, 106, 107,
+ /* 950 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
+ /* 960 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ /* 970 */ 57, 193, 239, 240, 193, 59, 19, 188, 253, 190,
+ /* 980 */ 193, 311, 312, 16, 195, 252, 197, 193, 19, 301,
+ /* 990 */ 302, 135, 193, 204, 216, 217, 140, 216, 217, 266,
+ /* 1000 */ 204, 159, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 1010 */ 53, 54, 55, 56, 57, 102, 103, 104, 105, 106,
+ /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 12, 239, 240,
+ /* 1030 */ 193, 298, 238, 117, 253, 239, 240, 238, 259, 260,
+ /* 1040 */ 193, 252, 27, 193, 77, 193, 79, 204, 252, 262,
+ /* 1050 */ 193, 299, 300, 193, 100, 266, 278, 42, 204, 102,
+ /* 1060 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ /* 1070 */ 113, 117, 159, 216, 217, 121, 216, 217, 63, 193,
+ /* 1080 */ 193, 193, 239, 240, 115, 116, 193, 298, 73, 240,
+ /* 1090 */ 238, 231, 19, 239, 240, 252, 22, 24, 211, 212,
+ /* 1100 */ 263, 252, 216, 217, 216, 217, 252, 153, 154, 155,
+ /* 1110 */ 253, 193, 19, 144, 213, 268, 43, 44, 45, 46,
+ /* 1120 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ /* 1130 */ 57, 193, 19, 59, 216, 217, 43, 44, 45, 46,
+ /* 1140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ /* 1150 */ 57, 193, 19, 24, 216, 217, 43, 44, 45, 46,
+ /* 1160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ /* 1170 */ 57, 284, 193, 208, 209, 102, 103, 104, 105, 106,
+ /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 286, 59, 193,
+ /* 1190 */ 232, 117, 291, 193, 193, 102, 103, 104, 105, 106,
+ /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 193, 204, 22,
+ /* 1210 */ 23, 193, 25, 66, 193, 102, 103, 104, 105, 106,
+ /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 193, 193, 193,
+ /* 1230 */ 216, 217, 85, 193, 238, 19, 16, 216, 217, 238,
+ /* 1240 */ 193, 94, 193, 239, 240, 231, 117, 268, 35, 116,
+ /* 1250 */ 216, 217, 216, 217, 22, 23, 252, 25, 208, 209,
+ /* 1260 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 1270 */ 54, 55, 56, 57, 193, 193, 19, 5, 59, 66,
+ /* 1280 */ 193, 263, 10, 11, 12, 13, 14, 74, 101, 17,
+ /* 1290 */ 193, 46, 193, 146, 193, 76, 213, 77, 263, 79,
+ /* 1300 */ 12, 260, 30, 46, 32, 264, 87, 193, 89, 29,
+ /* 1310 */ 263, 92, 40, 33, 232, 27, 193, 108, 102, 103,
+ /* 1320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+ /* 1330 */ 42, 138, 139, 101, 193, 116, 117, 118, 19, 20,
+ /* 1340 */ 255, 22, 70, 130, 135, 65, 256, 257, 193, 140,
+ /* 1350 */ 78, 63, 193, 81, 193, 36, 193, 216, 217, 193,
+ /* 1360 */ 115, 193, 263, 193, 145, 268, 59, 48, 193, 193,
+ /* 1370 */ 98, 193, 115, 193, 291, 216, 217, 193, 59, 216,
+ /* 1380 */ 217, 161, 216, 217, 216, 217, 216, 217, 131, 193,
+ /* 1390 */ 71, 193, 216, 217, 216, 217, 216, 217, 193, 260,
+ /* 1400 */ 216, 217, 19, 264, 85, 133, 244, 100, 193, 90,
+ /* 1410 */ 138, 139, 216, 217, 216, 217, 254, 244, 193, 100,
+ /* 1420 */ 193, 216, 217, 116, 117, 106, 107, 254, 121, 193,
+ /* 1430 */ 115, 216, 217, 114, 162, 116, 117, 118, 115, 244,
+ /* 1440 */ 121, 216, 217, 216, 217, 193, 309, 193, 31, 254,
+ /* 1450 */ 313, 309, 216, 217, 309, 313, 39, 193, 313, 309,
+ /* 1460 */ 153, 154, 155, 313, 193, 150, 25, 144, 216, 217,
+ /* 1470 */ 216, 217, 153, 154, 155, 156, 157, 0, 1, 2,
+ /* 1480 */ 216, 217, 5, 149, 150, 22, 193, 10, 11, 12,
+ /* 1490 */ 13, 14, 193, 158, 17, 160, 193, 19, 20, 116,
+ /* 1500 */ 22, 25, 193, 24, 22, 193, 24, 30, 226, 32,
+ /* 1510 */ 19, 20, 226, 22, 36, 193, 53, 40, 193, 216,
+ /* 1520 */ 217, 193, 23, 193, 25, 216, 217, 36, 216, 217,
+ /* 1530 */ 193, 99, 193, 193, 22, 193, 193, 59, 216, 217,
+ /* 1540 */ 193, 216, 217, 193, 216, 217, 193, 70, 129, 71,
+ /* 1550 */ 59, 129, 193, 216, 217, 78, 216, 217, 81, 216,
+ /* 1560 */ 217, 193, 71, 85, 193, 133, 193, 126, 90, 216,
+ /* 1570 */ 217, 152, 258, 61, 152, 98, 85, 193, 100, 193,
+ /* 1580 */ 23, 90, 25, 121, 106, 107, 23, 216, 217, 216,
+ /* 1590 */ 217, 100, 114, 131, 116, 117, 118, 106, 107, 121,
+ /* 1600 */ 216, 217, 216, 217, 193, 114, 193, 116, 117, 118,
+ /* 1610 */ 133, 22, 121, 193, 59, 138, 139, 193, 142, 193,
+ /* 1620 */ 141, 23, 23, 25, 25, 120, 121, 216, 217, 216,
+ /* 1630 */ 217, 153, 154, 155, 156, 157, 216, 217, 19, 162,
+ /* 1640 */ 216, 217, 216, 217, 153, 154, 155, 156, 157, 1,
+ /* 1650 */ 2, 193, 59, 5, 19, 20, 318, 22, 10, 11,
+ /* 1660 */ 12, 13, 14, 193, 59, 17, 193, 23, 23, 25,
+ /* 1670 */ 25, 36, 117, 193, 216, 217, 193, 23, 30, 25,
+ /* 1680 */ 32, 19, 20, 23, 22, 25, 216, 217, 40, 216,
+ /* 1690 */ 217, 7, 8, 23, 59, 25, 83, 84, 36, 23,
+ /* 1700 */ 193, 25, 23, 23, 25, 25, 71, 153, 145, 155,
+ /* 1710 */ 117, 153, 23, 155, 25, 23, 97, 25, 70, 193,
+ /* 1720 */ 193, 59, 117, 236, 193, 193, 78, 193, 193, 81,
+ /* 1730 */ 141, 193, 193, 71, 193, 100, 288, 287, 242, 255,
+ /* 1740 */ 255, 106, 107, 108, 255, 255, 98, 243, 297, 114,
+ /* 1750 */ 214, 116, 117, 118, 245, 191, 121, 271, 293, 267,
+ /* 1760 */ 267, 246, 100, 246, 245, 271, 271, 293, 106, 107,
+ /* 1770 */ 220, 271, 229, 225, 249, 219, 114, 259, 116, 117,
+ /* 1780 */ 118, 133, 259, 121, 219, 219, 138, 139, 153, 154,
+ /* 1790 */ 155, 156, 157, 280, 249, 243, 19, 20, 245, 22,
+ /* 1800 */ 196, 259, 140, 259, 60, 297, 141, 297, 200, 200,
+ /* 1810 */ 162, 38, 200, 36, 294, 153, 154, 155, 156, 157,
+ /* 1820 */ 151, 150, 294, 283, 22, 43, 234, 18, 237, 200,
+ /* 1830 */ 270, 272, 237, 237, 237, 18, 59, 199, 270, 149,
+ /* 1840 */ 246, 272, 272, 200, 234, 234, 246, 246, 71, 246,
+ /* 1850 */ 199, 158, 290, 62, 22, 200, 19, 20, 199, 22,
+ /* 1860 */ 289, 221, 221, 200, 200, 199, 199, 115, 218, 64,
+ /* 1870 */ 218, 218, 22, 36, 227, 126, 227, 100, 165, 221,
+ /* 1880 */ 224, 224, 24, 106, 107, 312, 218, 305, 113, 282,
+ /* 1890 */ 91, 114, 220, 116, 117, 118, 59, 282, 121, 218,
+ /* 1900 */ 218, 218, 200, 317, 317, 82, 221, 265, 71, 148,
+ /* 1910 */ 145, 265, 22, 277, 200, 158, 279, 140, 147, 25,
+ /* 1920 */ 146, 202, 248, 250, 249, 247, 13, 250, 194, 194,
+ /* 1930 */ 153, 154, 155, 156, 157, 6, 303, 100, 192, 192,
+ /* 1940 */ 246, 213, 192, 106, 107, 207, 213, 207, 222, 213,
+ /* 1950 */ 213, 114, 222, 116, 117, 118, 214, 214, 121, 4,
+ /* 1960 */ 207, 213, 3, 22, 303, 15, 163, 16, 23, 23,
+ /* 1970 */ 139, 151, 130, 25, 20, 142, 24, 16, 144, 1,
+ /* 1980 */ 142, 130, 130, 61, 37, 53, 300, 151, 53, 53,
+ /* 1990 */ 153, 154, 155, 156, 157, 53, 130, 116, 34, 1,
+ /* 2000 */ 141, 5, 22, 115, 161, 68, 25, 68, 75, 41,
+ /* 2010 */ 141, 115, 24, 20, 19, 131, 125, 23, 28, 22,
+ /* 2020 */ 67, 22, 22, 22, 67, 59, 24, 96, 22, 67,
+ /* 2030 */ 23, 149, 22, 25, 23, 23, 23, 22, 34, 141,
+ /* 2040 */ 37, 97, 23, 23, 116, 22, 143, 25, 34, 75,
+ /* 2050 */ 34, 34, 34, 88, 75, 34, 86, 23, 22, 34,
+ /* 2060 */ 93, 24, 34, 25, 25, 142, 142, 23, 44, 23,
+ /* 2070 */ 23, 23, 23, 11, 23, 25, 22, 22, 22, 141,
+ /* 2080 */ 23, 23, 22, 22, 25, 15, 1, 23, 25, 1,
+ /* 2090 */ 141, 135, 319, 319, 319, 319, 319, 319, 319, 141,
+ /* 2100 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2110 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2120 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2130 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2140 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2150 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2160 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2170 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2180 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2190 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2200 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2210 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2220 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2230 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2240 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2250 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2260 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2270 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ /* 2280 */ 319, 319, 319, 319, 319,
};
-#define YY_SHIFT_COUNT (520)
+#define YY_SHIFT_COUNT (578)
#define YY_SHIFT_MIN (0)
-#define YY_SHIFT_MAX (1858)
+#define YY_SHIFT_MAX (2088)
static const unsigned short int yy_shift_ofst[] = {
- /* 0 */ 1709, 1520, 1858, 1324, 1324, 277, 1374, 1469, 1602, 1712,
- /* 10 */ 1712, 1712, 273, 0, 0, 113, 1016, 1712, 1712, 1712,
- /* 20 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 11, 11, 236,
- /* 30 */ 184, 277, 277, 277, 277, 277, 277, 93, 177, 270,
- /* 40 */ 363, 456, 549, 642, 735, 828, 848, 996, 1144, 1016,
- /* 50 */ 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016,
- /* 60 */ 1016, 1016, 1016, 1016, 1016, 1016, 1164, 1016, 1257, 1277,
- /* 70 */ 1277, 1490, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712,
- /* 80 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712,
- /* 90 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712,
- /* 100 */ 1712, 1712, 1712, 1742, 1712, 1712, 1712, 1712, 1712, 1712,
- /* 110 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 143, 162, 162,
- /* 120 */ 162, 162, 162, 204, 151, 416, 531, 648, 700, 531,
- /* 130 */ 486, 486, 531, 353, 353, 353, 353, 409, 279, 53,
- /* 140 */ 2009, 2009, 331, 331, 331, 329, 366, 329, 329, 597,
- /* 150 */ 597, 464, 474, 262, 681, 531, 531, 531, 531, 531,
- /* 160 */ 531, 531, 531, 531, 531, 531, 531, 531, 531, 531,
- /* 170 */ 531, 531, 531, 531, 531, 531, 531, 173, 485, 984,
- /* 180 */ 984, 576, 485, 19, 1022, 2009, 2009, 2009, 387, 250,
- /* 190 */ 250, 525, 502, 278, 552, 227, 480, 566, 531, 531,
- /* 200 */ 531, 531, 531, 531, 531, 531, 531, 531, 639, 531,
- /* 210 */ 531, 531, 531, 531, 531, 531, 531, 531, 531, 531,
- /* 220 */ 531, 2, 2, 2, 531, 531, 531, 531, 782, 531,
- /* 230 */ 531, 531, 744, 531, 531, 783, 531, 531, 531, 531,
- /* 240 */ 531, 531, 531, 531, 419, 682, 327, 370, 370, 370,
- /* 250 */ 370, 1029, 327, 327, 1024, 897, 856, 947, 1109, 706,
- /* 260 */ 706, 1143, 1109, 1109, 1143, 842, 945, 1118, 1136, 1136,
- /* 270 */ 1136, 706, 676, 400, 1047, 694, 1339, 1270, 1270, 1366,
- /* 280 */ 1366, 1270, 1305, 1389, 1369, 1278, 1401, 1401, 1401, 1401,
- /* 290 */ 1270, 1414, 1278, 1278, 1305, 1389, 1369, 1369, 1278, 1270,
- /* 300 */ 1414, 1298, 1385, 1270, 1414, 1432, 1270, 1414, 1270, 1414,
- /* 310 */ 1432, 1355, 1355, 1355, 1411, 1432, 1355, 1367, 1355, 1411,
- /* 320 */ 1355, 1355, 1432, 1392, 1392, 1432, 1365, 1396, 1365, 1396,
- /* 330 */ 1365, 1396, 1365, 1396, 1270, 1372, 1429, 1502, 1390, 1372,
- /* 340 */ 1517, 1270, 1397, 1390, 1410, 1413, 1278, 1528, 1532, 1549,
- /* 350 */ 1549, 1562, 1562, 1562, 2009, 2009, 2009, 2009, 2009, 2009,
- /* 360 */ 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009,
- /* 370 */ 570, 345, 686, 748, 50, 740, 1064, 1107, 469, 537,
- /* 380 */ 1042, 1146, 1162, 1154, 1201, 1202, 1203, 1208, 1209, 1127,
- /* 390 */ 1069, 1196, 1157, 1147, 1226, 1228, 1245, 775, 868, 1246,
- /* 400 */ 1247, 1191, 1151, 1585, 1589, 1587, 1456, 1600, 1527, 1601,
- /* 410 */ 1595, 1596, 1492, 1484, 1506, 1603, 1495, 1608, 1496, 1614,
- /* 420 */ 1635, 1508, 1497, 1521, 1580, 1606, 1505, 1592, 1593, 1597,
- /* 430 */ 1598, 1530, 1547, 1619, 1524, 1654, 1651, 1636, 1553, 1510,
- /* 440 */ 1594, 1634, 1599, 1588, 1623, 1535, 1564, 1642, 1649, 1652,
- /* 450 */ 1552, 1560, 1653, 1609, 1655, 1656, 1657, 1659, 1612, 1658,
- /* 460 */ 1660, 1616, 1648, 1664, 1550, 1668, 1538, 1670, 1671, 1669,
- /* 470 */ 1673, 1675, 1676, 1678, 1680, 1679, 1574, 1683, 1690, 1610,
- /* 480 */ 1682, 1695, 1586, 1698, 1691, 1698, 1693, 1643, 1661, 1646,
- /* 490 */ 1686, 1710, 1711, 1714, 1716, 1703, 1715, 1698, 1727, 1728,
- /* 500 */ 1729, 1730, 1731, 1732, 1734, 1743, 1736, 1737, 1740, 1744,
- /* 510 */ 1738, 1746, 1739, 1645, 1640, 1644, 1647, 1650, 1749, 1751,
- /* 520 */ 1772,
+ /* 0 */ 1648, 1477, 1272, 322, 322, 1, 1319, 1478, 1491, 1837,
+ /* 10 */ 1837, 1837, 471, 0, 0, 214, 1093, 1837, 1837, 1837,
+ /* 20 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
+ /* 30 */ 1837, 271, 271, 1219, 1219, 216, 88, 1, 1, 1,
+ /* 40 */ 1, 1, 40, 111, 258, 361, 469, 512, 583, 622,
+ /* 50 */ 693, 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093,
+ /* 60 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093,
+ /* 70 */ 1093, 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635,
+ /* 80 */ 1662, 1777, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
+ /* 90 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
+ /* 100 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
+ /* 110 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
+ /* 120 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
+ /* 130 */ 1837, 137, 181, 181, 181, 181, 181, 181, 181, 94,
+ /* 140 */ 430, 66, 65, 112, 366, 533, 533, 740, 1257, 533,
+ /* 150 */ 533, 79, 79, 533, 412, 412, 412, 77, 412, 123,
+ /* 160 */ 113, 113, 113, 22, 22, 2100, 2100, 328, 328, 328,
+ /* 170 */ 239, 468, 468, 468, 468, 1015, 1015, 409, 366, 1187,
+ /* 180 */ 1232, 533, 533, 533, 533, 533, 533, 533, 533, 533,
+ /* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533,
+ /* 200 */ 533, 969, 621, 621, 533, 642, 788, 788, 1133, 1133,
+ /* 210 */ 822, 822, 67, 1193, 2100, 2100, 2100, 2100, 2100, 2100,
+ /* 220 */ 2100, 1307, 954, 954, 585, 472, 640, 387, 695, 538,
+ /* 230 */ 541, 700, 533, 533, 533, 533, 533, 533, 533, 533,
+ /* 240 */ 533, 533, 222, 533, 533, 533, 533, 533, 533, 533,
+ /* 250 */ 533, 533, 533, 533, 533, 1213, 1213, 1213, 533, 533,
+ /* 260 */ 533, 565, 533, 533, 533, 916, 1147, 533, 533, 1288,
+ /* 270 */ 533, 533, 533, 533, 533, 533, 533, 533, 639, 1280,
+ /* 280 */ 209, 1129, 1129, 1129, 1129, 580, 209, 209, 1209, 768,
+ /* 290 */ 917, 649, 1315, 1334, 405, 1334, 1383, 249, 1315, 1315,
+ /* 300 */ 249, 1315, 405, 1383, 1441, 464, 1245, 1417, 1417, 1417,
+ /* 310 */ 1323, 1323, 1323, 1323, 184, 184, 1335, 1476, 856, 1482,
+ /* 320 */ 1744, 1744, 1665, 1665, 1773, 1773, 1665, 1669, 1671, 1802,
+ /* 330 */ 1782, 1809, 1809, 1809, 1809, 1665, 1817, 1690, 1671, 1671,
+ /* 340 */ 1690, 1802, 1782, 1690, 1782, 1690, 1665, 1817, 1693, 1791,
+ /* 350 */ 1665, 1817, 1832, 1665, 1817, 1665, 1817, 1832, 1752, 1752,
+ /* 360 */ 1752, 1805, 1850, 1850, 1832, 1752, 1749, 1752, 1805, 1752,
+ /* 370 */ 1752, 1713, 1858, 1775, 1775, 1832, 1665, 1799, 1799, 1823,
+ /* 380 */ 1823, 1761, 1765, 1890, 1665, 1757, 1761, 1771, 1774, 1690,
+ /* 390 */ 1894, 1913, 1913, 1929, 1929, 1929, 2100, 2100, 2100, 2100,
+ /* 400 */ 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100,
+ /* 410 */ 2100, 207, 1220, 331, 620, 967, 806, 1074, 1499, 1432,
+ /* 420 */ 1463, 1479, 1419, 1422, 1557, 1512, 1598, 1599, 1644, 1645,
+ /* 430 */ 1654, 1660, 1555, 1505, 1684, 1462, 1670, 1563, 1619, 1593,
+ /* 440 */ 1676, 1679, 1613, 1680, 1554, 1558, 1689, 1692, 1605, 1589,
+ /* 450 */ 1955, 1959, 1941, 1803, 1950, 1951, 1945, 1946, 1831, 1820,
+ /* 460 */ 1842, 1948, 1948, 1952, 1833, 1954, 1834, 1961, 1978, 1838,
+ /* 470 */ 1851, 1948, 1852, 1922, 1947, 1948, 1836, 1932, 1935, 1936,
+ /* 480 */ 1942, 1866, 1881, 1964, 1859, 1998, 1996, 1980, 1888, 1843,
+ /* 490 */ 1937, 1981, 1939, 1933, 1968, 1869, 1896, 1988, 1993, 1995,
+ /* 500 */ 1884, 1891, 1997, 1953, 1999, 2000, 1994, 2001, 1957, 1966,
+ /* 510 */ 2002, 1931, 1990, 2006, 1962, 2003, 2007, 2004, 1882, 2010,
+ /* 520 */ 2011, 2012, 2008, 2013, 2015, 1944, 1898, 2019, 2020, 1928,
+ /* 530 */ 2014, 2023, 1903, 2022, 2016, 2017, 2018, 2021, 1965, 1974,
+ /* 540 */ 1970, 2024, 1979, 1967, 2025, 2034, 2036, 2037, 2038, 2039,
+ /* 550 */ 2028, 1923, 1924, 2044, 2022, 2046, 2047, 2048, 2049, 2050,
+ /* 560 */ 2051, 2054, 2062, 2055, 2056, 2057, 2058, 2060, 2061, 2059,
+ /* 570 */ 1956, 1938, 1949, 1958, 2063, 2064, 2070, 2085, 2088,
};
-#define YY_REDUCE_COUNT (369)
-#define YY_REDUCE_MIN (-237)
-#define YY_REDUCE_MAX (1424)
+#define YY_REDUCE_COUNT (410)
+#define YY_REDUCE_MIN (-271)
+#define YY_REDUCE_MAX (1753)
static const short yy_reduce_ofst[] = {
- /* 0 */ -147, 171, 263, -96, 358, -144, -149, -102, 124, -156,
- /* 10 */ -98, 305, 401, -57, 209, -237, 245, -94, -79, 189,
- /* 20 */ 375, 490, 493, 378, 303, 539, 542, 501, 503, 554,
- /* 30 */ 415, 526, 546, 557, 587, 593, 595, -234, -234, -234,
- /* 40 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234,
- /* 50 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234,
- /* 60 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234,
- /* 70 */ -234, -50, 335, 470, 633, 656, 658, 660, 675, 685,
- /* 80 */ 703, 727, 747, 750, 752, 754, 770, 788, 790, 793,
- /* 90 */ 795, 797, 800, 802, 804, 806, 813, 820, 829, 833,
- /* 100 */ 836, 838, 843, 845, 847, 849, 873, 891, 893, 916,
- /* 110 */ 918, 921, 936, 941, 944, 956, 961, -234, -234, -234,
- /* 120 */ -234, -234, -234, -234, -234, -234, 463, 607, -176, 14,
- /* 130 */ -139, 87, -137, 818, 925, 818, 925, 898, -234, -234,
- /* 140 */ -234, -234, -166, -166, -166, -130, -131, -82, -54, -180,
- /* 150 */ 364, 41, 513, 509, 509, 117, 500, 789, 796, 646,
- /* 160 */ 192, 291, 644, 798, 120, 807, 543, 911, 920, 652,
- /* 170 */ 924, 922, 232, 698, 801, 971, 39, 220, 731, 442,
- /* 180 */ 902, -199, 979, -43, 421, 896, 942, 605, -184, -126,
- /* 190 */ 155, 172, 281, 304, 377, 538, 650, 690, 699, 723,
- /* 200 */ 803, 839, 853, 919, 991, 1018, 1067, 1092, 951, 1111,
- /* 210 */ 1112, 1115, 1116, 1117, 1119, 1120, 1121, 1122, 1123, 1124,
- /* 220 */ 1125, 1012, 1096, 1097, 1128, 1129, 1130, 1131, 1070, 1135,
- /* 230 */ 1137, 1152, 1077, 1153, 1155, 1114, 1156, 304, 1158, 1172,
- /* 240 */ 1173, 1174, 1175, 1176, 1089, 1091, 1133, 1098, 1126, 1139,
- /* 250 */ 1140, 1070, 1133, 1133, 1170, 1163, 1186, 1103, 1168, 1138,
- /* 260 */ 1141, 1110, 1169, 1171, 1132, 1177, 1189, 1194, 1181, 1200,
- /* 270 */ 1204, 1166, 1145, 1178, 1187, 1232, 1142, 1231, 1233, 1149,
- /* 280 */ 1150, 1238, 1179, 1182, 1212, 1205, 1219, 1220, 1221, 1222,
- /* 290 */ 1258, 1266, 1223, 1224, 1206, 1211, 1237, 1239, 1230, 1269,
- /* 300 */ 1272, 1195, 1197, 1280, 1284, 1268, 1285, 1289, 1290, 1293,
- /* 310 */ 1274, 1286, 1287, 1288, 1282, 1294, 1292, 1297, 1300, 1296,
- /* 320 */ 1301, 1306, 1304, 1249, 1250, 1308, 1271, 1275, 1273, 1276,
- /* 330 */ 1279, 1281, 1283, 1302, 1334, 1307, 1243, 1267, 1318, 1322,
- /* 340 */ 1303, 1371, 1299, 1328, 1332, 1340, 1342, 1384, 1391, 1400,
- /* 350 */ 1403, 1407, 1408, 1409, 1311, 1312, 1310, 1405, 1402, 1412,
- /* 360 */ 1417, 1420, 1406, 1393, 1395, 1421, 1422, 1423, 1424, 1415,
+ /* 0 */ -125, 733, 789, 241, 293, -123, -193, -191, -183, -187,
+ /* 10 */ 166, 238, 133, -207, -199, -267, -176, -6, 204, 489,
+ /* 20 */ 576, 598, -175, 686, 860, 615, 725, 1014, 778, 781,
+ /* 30 */ 857, 616, 887, 87, 240, -192, 408, 626, 796, 843,
+ /* 40 */ 854, 1004, -271, -271, -271, -271, -271, -271, -271, -271,
+ /* 50 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271,
+ /* 60 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271,
+ /* 70 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, 80,
+ /* 80 */ 83, 313, 886, 888, 918, 938, 1021, 1034, 1036, 1141,
+ /* 90 */ 1159, 1163, 1166, 1168, 1170, 1176, 1178, 1180, 1184, 1196,
+ /* 100 */ 1198, 1205, 1215, 1225, 1227, 1236, 1252, 1254, 1264, 1303,
+ /* 110 */ 1309, 1312, 1322, 1325, 1328, 1337, 1340, 1343, 1353, 1371,
+ /* 120 */ 1373, 1384, 1386, 1411, 1413, 1420, 1424, 1426, 1458, 1470,
+ /* 130 */ 1473, -271, -271, -271, -271, -271, -271, -271, -271, -271,
+ /* 140 */ -271, -271, 138, 459, 396, -158, 470, 302, -212, 521,
+ /* 150 */ 201, -195, -92, 559, 630, 632, 630, -271, 632, 901,
+ /* 160 */ 63, 407, 670, -271, -271, -271, -271, 161, 161, 161,
+ /* 170 */ 251, 335, 847, 979, 1097, 537, 588, 618, 628, 688,
+ /* 180 */ 688, -166, -161, 674, 787, 794, 799, 852, 996, -122,
+ /* 190 */ 837, -120, 1018, 1035, 415, 1047, 1001, 958, 1082, 400,
+ /* 200 */ 1099, 779, 1137, 1142, 263, 1083, 1145, 1150, 1041, 1139,
+ /* 210 */ 965, 1050, 362, 849, 752, 629, 675, 1162, 1173, 1090,
+ /* 220 */ 1195, -194, 56, 185, -135, 232, 522, 560, 571, 601,
+ /* 230 */ 617, 669, 683, 711, 850, 893, 1000, 1040, 1049, 1081,
+ /* 240 */ 1087, 1101, 392, 1114, 1123, 1155, 1161, 1175, 1271, 1293,
+ /* 250 */ 1299, 1330, 1339, 1342, 1347, 593, 1282, 1286, 1350, 1359,
+ /* 260 */ 1368, 1314, 1480, 1483, 1507, 1085, 1338, 1526, 1527, 1487,
+ /* 270 */ 1531, 560, 1532, 1534, 1535, 1538, 1539, 1541, 1448, 1450,
+ /* 280 */ 1496, 1484, 1485, 1489, 1490, 1314, 1496, 1496, 1504, 1536,
+ /* 290 */ 1564, 1451, 1486, 1492, 1509, 1493, 1465, 1515, 1494, 1495,
+ /* 300 */ 1517, 1500, 1519, 1474, 1550, 1543, 1548, 1556, 1565, 1566,
+ /* 310 */ 1518, 1523, 1542, 1544, 1525, 1545, 1513, 1553, 1552, 1604,
+ /* 320 */ 1508, 1510, 1608, 1609, 1520, 1528, 1612, 1540, 1559, 1560,
+ /* 330 */ 1592, 1591, 1595, 1596, 1597, 1629, 1638, 1594, 1569, 1570,
+ /* 340 */ 1600, 1568, 1610, 1601, 1611, 1603, 1643, 1651, 1562, 1571,
+ /* 350 */ 1655, 1659, 1640, 1663, 1666, 1664, 1667, 1641, 1650, 1652,
+ /* 360 */ 1653, 1647, 1656, 1657, 1658, 1668, 1672, 1681, 1649, 1682,
+ /* 370 */ 1683, 1573, 1582, 1607, 1615, 1685, 1702, 1586, 1587, 1642,
+ /* 380 */ 1646, 1673, 1675, 1636, 1714, 1637, 1677, 1674, 1678, 1694,
+ /* 390 */ 1719, 1734, 1735, 1746, 1747, 1750, 1633, 1661, 1686, 1738,
+ /* 400 */ 1728, 1733, 1736, 1737, 1740, 1726, 1730, 1742, 1743, 1748,
+ /* 410 */ 1753,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 1492, 1492, 1492, 1340, 1123, 1229, 1123, 1123, 1123, 1340,
- /* 10 */ 1340, 1340, 1123, 1259, 1259, 1391, 1154, 1123, 1123, 1123,
- /* 20 */ 1123, 1123, 1123, 1123, 1339, 1123, 1123, 1123, 1123, 1123,
- /* 30 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1265, 1123,
- /* 40 */ 1123, 1123, 1123, 1123, 1341, 1342, 1123, 1123, 1123, 1390,
- /* 50 */ 1392, 1275, 1274, 1273, 1272, 1373, 1246, 1270, 1263, 1267,
- /* 60 */ 1335, 1336, 1334, 1338, 1342, 1341, 1123, 1266, 1306, 1320,
- /* 70 */ 1305, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 80 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 90 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 100 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 110 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1314, 1319, 1325,
- /* 120 */ 1318, 1315, 1308, 1307, 1309, 1310, 1123, 1144, 1193, 1123,
- /* 130 */ 1123, 1123, 1123, 1409, 1408, 1123, 1123, 1154, 1311, 1312,
- /* 140 */ 1322, 1321, 1398, 1448, 1447, 1123, 1123, 1123, 1123, 1123,
- /* 150 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 160 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 170 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1154, 1150, 1300,
- /* 180 */ 1299, 1418, 1150, 1253, 1123, 1404, 1229, 1220, 1123, 1123,
- /* 190 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 200 */ 1123, 1395, 1393, 1123, 1355, 1123, 1123, 1123, 1123, 1123,
- /* 210 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 220 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 230 */ 1123, 1123, 1225, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 240 */ 1123, 1123, 1123, 1442, 1123, 1368, 1207, 1225, 1225, 1225,
- /* 250 */ 1225, 1227, 1208, 1206, 1219, 1154, 1130, 1484, 1269, 1248,
- /* 260 */ 1248, 1481, 1269, 1269, 1481, 1168, 1462, 1165, 1259, 1259,
- /* 270 */ 1259, 1248, 1337, 1226, 1219, 1123, 1484, 1234, 1234, 1483,
- /* 280 */ 1483, 1234, 1278, 1284, 1196, 1269, 1202, 1202, 1202, 1202,
- /* 290 */ 1234, 1141, 1269, 1269, 1278, 1284, 1196, 1196, 1269, 1234,
- /* 300 */ 1141, 1372, 1478, 1234, 1141, 1348, 1234, 1141, 1234, 1141,
- /* 310 */ 1348, 1194, 1194, 1194, 1183, 1348, 1194, 1168, 1194, 1183,
- /* 320 */ 1194, 1194, 1348, 1352, 1352, 1348, 1252, 1247, 1252, 1247,
- /* 330 */ 1252, 1247, 1252, 1247, 1234, 1253, 1417, 1123, 1264, 1253,
- /* 340 */ 1343, 1234, 1123, 1264, 1262, 1260, 1269, 1147, 1186, 1445,
- /* 350 */ 1445, 1441, 1441, 1441, 1489, 1489, 1404, 1457, 1154, 1154,
- /* 360 */ 1154, 1154, 1457, 1170, 1170, 1154, 1154, 1154, 1154, 1457,
- /* 370 */ 1123, 1123, 1123, 1123, 1123, 1123, 1452, 1123, 1357, 1238,
- /* 380 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 390 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 400 */ 1123, 1123, 1289, 1123, 1126, 1401, 1123, 1123, 1399, 1123,
- /* 410 */ 1123, 1123, 1123, 1123, 1123, 1239, 1123, 1123, 1123, 1123,
- /* 420 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 430 */ 1123, 1123, 1123, 1123, 1480, 1123, 1123, 1123, 1123, 1123,
- /* 440 */ 1123, 1371, 1370, 1123, 1123, 1236, 1123, 1123, 1123, 1123,
- /* 450 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 460 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 470 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 480 */ 1123, 1123, 1123, 1261, 1123, 1416, 1123, 1123, 1123, 1123,
- /* 490 */ 1123, 1123, 1123, 1430, 1254, 1123, 1123, 1471, 1123, 1123,
- /* 500 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123,
- /* 510 */ 1123, 1123, 1466, 1210, 1291, 1123, 1290, 1294, 1123, 1135,
- /* 520 */ 1123,
+ /* 0 */ 1648, 1648, 1648, 1478, 1243, 1354, 1243, 1243, 1243, 1478,
+ /* 10 */ 1478, 1478, 1243, 1384, 1384, 1531, 1276, 1243, 1243, 1243,
+ /* 20 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1477, 1243,
+ /* 30 */ 1243, 1243, 1243, 1564, 1564, 1243, 1243, 1243, 1243, 1243,
+ /* 40 */ 1243, 1243, 1243, 1393, 1243, 1400, 1243, 1243, 1243, 1243,
+ /* 50 */ 1243, 1479, 1480, 1243, 1243, 1243, 1530, 1532, 1495, 1407,
+ /* 60 */ 1406, 1405, 1404, 1513, 1372, 1398, 1391, 1395, 1474, 1475,
+ /* 70 */ 1473, 1626, 1480, 1479, 1243, 1394, 1442, 1458, 1441, 1243,
+ /* 80 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 90 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 100 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 110 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 120 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 130 */ 1243, 1450, 1457, 1456, 1455, 1464, 1454, 1451, 1444, 1443,
+ /* 140 */ 1445, 1446, 1243, 1243, 1267, 1243, 1243, 1264, 1318, 1243,
+ /* 150 */ 1243, 1243, 1243, 1243, 1550, 1549, 1243, 1447, 1243, 1276,
+ /* 160 */ 1435, 1434, 1433, 1461, 1448, 1460, 1459, 1538, 1600, 1599,
+ /* 170 */ 1496, 1243, 1243, 1243, 1243, 1243, 1243, 1564, 1243, 1243,
+ /* 180 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 190 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 200 */ 1243, 1374, 1564, 1564, 1243, 1276, 1564, 1564, 1375, 1375,
+ /* 210 */ 1272, 1272, 1378, 1243, 1545, 1345, 1345, 1345, 1345, 1354,
+ /* 220 */ 1345, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 230 */ 1243, 1243, 1243, 1243, 1243, 1243, 1535, 1533, 1243, 1243,
+ /* 240 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 250 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 260 */ 1243, 1243, 1243, 1243, 1243, 1350, 1243, 1243, 1243, 1243,
+ /* 270 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1593, 1243, 1508,
+ /* 280 */ 1332, 1350, 1350, 1350, 1350, 1352, 1333, 1331, 1344, 1277,
+ /* 290 */ 1250, 1640, 1410, 1399, 1351, 1399, 1637, 1397, 1410, 1410,
+ /* 300 */ 1397, 1410, 1351, 1637, 1293, 1615, 1288, 1384, 1384, 1384,
+ /* 310 */ 1374, 1374, 1374, 1374, 1378, 1378, 1476, 1351, 1344, 1243,
+ /* 320 */ 1640, 1640, 1360, 1360, 1639, 1639, 1360, 1496, 1623, 1419,
+ /* 330 */ 1321, 1327, 1327, 1327, 1327, 1360, 1261, 1397, 1623, 1623,
+ /* 340 */ 1397, 1419, 1321, 1397, 1321, 1397, 1360, 1261, 1512, 1634,
+ /* 350 */ 1360, 1261, 1486, 1360, 1261, 1360, 1261, 1486, 1319, 1319,
+ /* 360 */ 1319, 1308, 1243, 1243, 1486, 1319, 1293, 1319, 1308, 1319,
+ /* 370 */ 1319, 1582, 1243, 1490, 1490, 1486, 1360, 1574, 1574, 1387,
+ /* 380 */ 1387, 1392, 1378, 1481, 1360, 1243, 1392, 1390, 1388, 1397,
+ /* 390 */ 1311, 1596, 1596, 1592, 1592, 1592, 1645, 1645, 1545, 1608,
+ /* 400 */ 1276, 1276, 1276, 1276, 1608, 1295, 1295, 1277, 1277, 1276,
+ /* 410 */ 1608, 1243, 1243, 1243, 1243, 1243, 1243, 1603, 1243, 1540,
+ /* 420 */ 1497, 1364, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 430 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1551, 1243,
+ /* 440 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1424,
+ /* 450 */ 1243, 1246, 1542, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 460 */ 1243, 1401, 1402, 1365, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 470 */ 1243, 1416, 1243, 1243, 1243, 1411, 1243, 1243, 1243, 1243,
+ /* 480 */ 1243, 1243, 1243, 1243, 1636, 1243, 1243, 1243, 1243, 1243,
+ /* 490 */ 1243, 1511, 1510, 1243, 1243, 1362, 1243, 1243, 1243, 1243,
+ /* 500 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1291,
+ /* 510 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 520 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 530 */ 1243, 1243, 1243, 1389, 1243, 1243, 1243, 1243, 1243, 1243,
+ /* 540 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1579, 1379,
+ /* 550 */ 1243, 1243, 1243, 1243, 1627, 1243, 1243, 1243, 1243, 1243,
+ /* 560 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1619,
+ /* 570 */ 1335, 1425, 1243, 1428, 1265, 1243, 1255, 1243, 1243,
};
/********** End of lemon-generated parsing tables *****************************/
-/* The next table maps tokens (terminal symbols) into fallback tokens.
+/* The next table maps tokens (terminal symbols) into fallback tokens.
** If a construct like the following:
-**
+**
** %fallback ID X Y Z.
**
** appears in the grammar, then ID becomes a fallback token for X, Y,
@@ -147768,8 +172153,8 @@ static const YYCODETYPE yyFallback[] = {
0, /* LP => nothing */
0, /* RP => nothing */
0, /* AS => nothing */
- 59, /* WITHOUT => ID */
0, /* COMMA => nothing */
+ 59, /* WITHOUT => ID */
59, /* ABORT => ID */
59, /* ACTION => ID */
59, /* AFTER => ID */
@@ -147825,15 +172210,109 @@ static const YYCODETYPE yyFallback[] = {
59, /* VIEW => ID */
59, /* VIRTUAL => ID */
59, /* WITH => ID */
+ 59, /* NULLS => ID */
+ 59, /* FIRST => ID */
+ 59, /* LAST => ID */
59, /* CURRENT => ID */
59, /* FOLLOWING => ID */
59, /* PARTITION => ID */
59, /* PRECEDING => ID */
59, /* RANGE => ID */
59, /* UNBOUNDED => ID */
+ 59, /* EXCLUDE => ID */
+ 59, /* GROUPS => ID */
+ 59, /* OTHERS => ID */
+ 59, /* TIES => ID */
+ 59, /* GENERATED => ID */
+ 59, /* ALWAYS => ID */
+ 59, /* MATERIALIZED => ID */
59, /* REINDEX => ID */
59, /* RENAME => ID */
59, /* CTIME_KW => ID */
+ 0, /* ANY => nothing */
+ 0, /* BITAND => nothing */
+ 0, /* BITOR => nothing */
+ 0, /* LSHIFT => nothing */
+ 0, /* RSHIFT => nothing */
+ 0, /* PLUS => nothing */
+ 0, /* MINUS => nothing */
+ 0, /* STAR => nothing */
+ 0, /* SLASH => nothing */
+ 0, /* REM => nothing */
+ 0, /* CONCAT => nothing */
+ 0, /* PTR => nothing */
+ 0, /* COLLATE => nothing */
+ 0, /* BITNOT => nothing */
+ 0, /* ON => nothing */
+ 0, /* INDEXED => nothing */
+ 0, /* STRING => nothing */
+ 0, /* JOIN_KW => nothing */
+ 0, /* CONSTRAINT => nothing */
+ 0, /* DEFAULT => nothing */
+ 0, /* NULL => nothing */
+ 0, /* PRIMARY => nothing */
+ 0, /* UNIQUE => nothing */
+ 0, /* CHECK => nothing */
+ 0, /* REFERENCES => nothing */
+ 0, /* AUTOINCR => nothing */
+ 0, /* INSERT => nothing */
+ 0, /* DELETE => nothing */
+ 0, /* UPDATE => nothing */
+ 0, /* SET => nothing */
+ 0, /* DEFERRABLE => nothing */
+ 0, /* FOREIGN => nothing */
+ 0, /* DROP => nothing */
+ 0, /* UNION => nothing */
+ 0, /* ALL => nothing */
+ 0, /* EXCEPT => nothing */
+ 0, /* INTERSECT => nothing */
+ 0, /* SELECT => nothing */
+ 0, /* VALUES => nothing */
+ 0, /* DISTINCT => nothing */
+ 0, /* DOT => nothing */
+ 0, /* FROM => nothing */
+ 0, /* JOIN => nothing */
+ 0, /* USING => nothing */
+ 0, /* ORDER => nothing */
+ 0, /* GROUP => nothing */
+ 0, /* HAVING => nothing */
+ 0, /* LIMIT => nothing */
+ 0, /* WHERE => nothing */
+ 0, /* RETURNING => nothing */
+ 0, /* INTO => nothing */
+ 0, /* NOTHING => nothing */
+ 0, /* FLOAT => nothing */
+ 0, /* BLOB => nothing */
+ 0, /* INTEGER => nothing */
+ 0, /* VARIABLE => nothing */
+ 0, /* CASE => nothing */
+ 0, /* WHEN => nothing */
+ 0, /* THEN => nothing */
+ 0, /* ELSE => nothing */
+ 0, /* INDEX => nothing */
+ 0, /* ALTER => nothing */
+ 0, /* ADD => nothing */
+ 0, /* WINDOW => nothing */
+ 0, /* OVER => nothing */
+ 0, /* FILTER => nothing */
+ 0, /* COLUMN => nothing */
+ 0, /* AGG_FUNCTION => nothing */
+ 0, /* AGG_COLUMN => nothing */
+ 0, /* TRUEFALSE => nothing */
+ 0, /* ISNOT => nothing */
+ 0, /* FUNCTION => nothing */
+ 0, /* UMINUS => nothing */
+ 0, /* UPLUS => nothing */
+ 0, /* TRUTH => nothing */
+ 0, /* REGISTER => nothing */
+ 0, /* VECTOR => nothing */
+ 0, /* SELECT_COLUMN => nothing */
+ 0, /* IF_NULL_ROW => nothing */
+ 0, /* ASTERISK => nothing */
+ 0, /* SPAN => nothing */
+ 0, /* ERROR => nothing */
+ 0, /* SPACE => nothing */
+ 0, /* ILLEGAL => nothing */
};
#endif /* YYFALLBACK */
@@ -147885,6 +172364,7 @@ struct yyParser {
};
typedef struct yyParser yyParser;
+/* #include <assert.h> */
#ifndef NDEBUG
/* #include <stdio.h> */
static FILE *yyTraceFILE = 0;
@@ -147892,10 +172372,10 @@ static char *yyTracePrompt = 0;
#endif /* NDEBUG */
#ifndef NDEBUG
-/*
+/*
** Turn parser tracing on by giving a stream to which to write the trace
** and a prompt to preface each trace message. Tracing is turned off
-** by making either argument NULL
+** by making either argument NULL
**
** Inputs:
** <ul>
@@ -147920,7 +172400,7 @@ SQLITE_PRIVATE void sqlite3ParserTrace(FILE *TraceFILE, char *zTracePrompt){
#if defined(YYCOVERAGE) || !defined(NDEBUG)
/* For tracing shifts, the names of all terminals and nonterminals
** are required. The following table supplies these names */
-static const char *const yyTokenName[] = {
+static const char *const yyTokenName[] = {
/* 0 */ "$",
/* 1 */ "SEMI",
/* 2 */ "EXPLAIN",
@@ -147946,8 +172426,8 @@ static const char *const yyTokenName[] = {
/* 22 */ "LP",
/* 23 */ "RP",
/* 24 */ "AS",
- /* 25 */ "WITHOUT",
- /* 26 */ "COMMA",
+ /* 25 */ "COMMA",
+ /* 26 */ "WITHOUT",
/* 27 */ "ABORT",
/* 28 */ "ACTION",
/* 29 */ "AFTER",
@@ -148003,201 +172483,243 @@ static const char *const yyTokenName[] = {
/* 79 */ "VIEW",
/* 80 */ "VIRTUAL",
/* 81 */ "WITH",
- /* 82 */ "CURRENT",
- /* 83 */ "FOLLOWING",
- /* 84 */ "PARTITION",
- /* 85 */ "PRECEDING",
- /* 86 */ "RANGE",
- /* 87 */ "UNBOUNDED",
- /* 88 */ "REINDEX",
- /* 89 */ "RENAME",
- /* 90 */ "CTIME_KW",
- /* 91 */ "ANY",
- /* 92 */ "BITAND",
- /* 93 */ "BITOR",
- /* 94 */ "LSHIFT",
- /* 95 */ "RSHIFT",
- /* 96 */ "PLUS",
- /* 97 */ "MINUS",
- /* 98 */ "STAR",
- /* 99 */ "SLASH",
- /* 100 */ "REM",
- /* 101 */ "CONCAT",
- /* 102 */ "COLLATE",
- /* 103 */ "BITNOT",
- /* 104 */ "ON",
- /* 105 */ "INDEXED",
- /* 106 */ "STRING",
- /* 107 */ "JOIN_KW",
- /* 108 */ "CONSTRAINT",
- /* 109 */ "DEFAULT",
- /* 110 */ "NULL",
- /* 111 */ "PRIMARY",
- /* 112 */ "UNIQUE",
- /* 113 */ "CHECK",
- /* 114 */ "REFERENCES",
- /* 115 */ "AUTOINCR",
- /* 116 */ "INSERT",
- /* 117 */ "DELETE",
- /* 118 */ "UPDATE",
- /* 119 */ "SET",
- /* 120 */ "DEFERRABLE",
- /* 121 */ "FOREIGN",
- /* 122 */ "DROP",
- /* 123 */ "UNION",
- /* 124 */ "ALL",
- /* 125 */ "EXCEPT",
- /* 126 */ "INTERSECT",
- /* 127 */ "SELECT",
- /* 128 */ "VALUES",
- /* 129 */ "DISTINCT",
- /* 130 */ "DOT",
- /* 131 */ "FROM",
- /* 132 */ "JOIN",
- /* 133 */ "USING",
- /* 134 */ "ORDER",
- /* 135 */ "GROUP",
- /* 136 */ "HAVING",
- /* 137 */ "LIMIT",
- /* 138 */ "WHERE",
- /* 139 */ "INTO",
- /* 140 */ "NOTHING",
- /* 141 */ "FLOAT",
- /* 142 */ "BLOB",
- /* 143 */ "INTEGER",
- /* 144 */ "VARIABLE",
- /* 145 */ "CASE",
- /* 146 */ "WHEN",
- /* 147 */ "THEN",
- /* 148 */ "ELSE",
- /* 149 */ "INDEX",
- /* 150 */ "ALTER",
- /* 151 */ "ADD",
- /* 152 */ "WINDOW",
- /* 153 */ "OVER",
- /* 154 */ "FILTER",
- /* 155 */ "input",
- /* 156 */ "cmdlist",
- /* 157 */ "ecmd",
- /* 158 */ "cmdx",
- /* 159 */ "explain",
- /* 160 */ "cmd",
- /* 161 */ "transtype",
- /* 162 */ "trans_opt",
- /* 163 */ "nm",
- /* 164 */ "savepoint_opt",
- /* 165 */ "create_table",
- /* 166 */ "create_table_args",
- /* 167 */ "createkw",
- /* 168 */ "temp",
- /* 169 */ "ifnotexists",
- /* 170 */ "dbnm",
- /* 171 */ "columnlist",
- /* 172 */ "conslist_opt",
- /* 173 */ "table_options",
- /* 174 */ "select",
- /* 175 */ "columnname",
- /* 176 */ "carglist",
- /* 177 */ "typetoken",
- /* 178 */ "typename",
- /* 179 */ "signed",
- /* 180 */ "plus_num",
- /* 181 */ "minus_num",
- /* 182 */ "scanpt",
- /* 183 */ "ccons",
- /* 184 */ "term",
- /* 185 */ "expr",
- /* 186 */ "onconf",
- /* 187 */ "sortorder",
- /* 188 */ "autoinc",
- /* 189 */ "eidlist_opt",
- /* 190 */ "refargs",
- /* 191 */ "defer_subclause",
- /* 192 */ "refarg",
- /* 193 */ "refact",
- /* 194 */ "init_deferred_pred_opt",
- /* 195 */ "conslist",
- /* 196 */ "tconscomma",
- /* 197 */ "tcons",
- /* 198 */ "sortlist",
- /* 199 */ "eidlist",
- /* 200 */ "defer_subclause_opt",
- /* 201 */ "orconf",
- /* 202 */ "resolvetype",
- /* 203 */ "raisetype",
- /* 204 */ "ifexists",
- /* 205 */ "fullname",
- /* 206 */ "selectnowith",
- /* 207 */ "oneselect",
- /* 208 */ "wqlist",
- /* 209 */ "multiselect_op",
- /* 210 */ "distinct",
- /* 211 */ "selcollist",
- /* 212 */ "from",
- /* 213 */ "where_opt",
- /* 214 */ "groupby_opt",
- /* 215 */ "having_opt",
- /* 216 */ "orderby_opt",
- /* 217 */ "limit_opt",
- /* 218 */ "window_clause",
- /* 219 */ "values",
- /* 220 */ "nexprlist",
- /* 221 */ "sclp",
- /* 222 */ "as",
- /* 223 */ "seltablist",
- /* 224 */ "stl_prefix",
- /* 225 */ "joinop",
- /* 226 */ "indexed_opt",
- /* 227 */ "on_opt",
- /* 228 */ "using_opt",
- /* 229 */ "exprlist",
- /* 230 */ "xfullname",
- /* 231 */ "idlist",
- /* 232 */ "with",
- /* 233 */ "setlist",
- /* 234 */ "insert_cmd",
- /* 235 */ "idlist_opt",
- /* 236 */ "upsert",
- /* 237 */ "over_clause",
- /* 238 */ "likeop",
- /* 239 */ "between_op",
- /* 240 */ "in_op",
- /* 241 */ "paren_exprlist",
- /* 242 */ "case_operand",
- /* 243 */ "case_exprlist",
- /* 244 */ "case_else",
- /* 245 */ "uniqueflag",
- /* 246 */ "collate",
- /* 247 */ "nmnum",
- /* 248 */ "trigger_decl",
- /* 249 */ "trigger_cmd_list",
- /* 250 */ "trigger_time",
- /* 251 */ "trigger_event",
- /* 252 */ "foreach_clause",
- /* 253 */ "when_clause",
- /* 254 */ "trigger_cmd",
- /* 255 */ "trnm",
- /* 256 */ "tridxby",
- /* 257 */ "database_kw_opt",
- /* 258 */ "key_opt",
- /* 259 */ "add_column_fullname",
- /* 260 */ "kwcolumn_opt",
- /* 261 */ "create_vtab",
- /* 262 */ "vtabarglist",
- /* 263 */ "vtabarg",
- /* 264 */ "vtabargtoken",
- /* 265 */ "lp",
- /* 266 */ "anylist",
- /* 267 */ "windowdefn_list",
- /* 268 */ "windowdefn",
- /* 269 */ "window",
- /* 270 */ "frame_opt",
- /* 271 */ "part_opt",
- /* 272 */ "filter_opt",
- /* 273 */ "range_or_rows",
- /* 274 */ "frame_bound",
- /* 275 */ "frame_bound_s",
- /* 276 */ "frame_bound_e",
+ /* 82 */ "NULLS",
+ /* 83 */ "FIRST",
+ /* 84 */ "LAST",
+ /* 85 */ "CURRENT",
+ /* 86 */ "FOLLOWING",
+ /* 87 */ "PARTITION",
+ /* 88 */ "PRECEDING",
+ /* 89 */ "RANGE",
+ /* 90 */ "UNBOUNDED",
+ /* 91 */ "EXCLUDE",
+ /* 92 */ "GROUPS",
+ /* 93 */ "OTHERS",
+ /* 94 */ "TIES",
+ /* 95 */ "GENERATED",
+ /* 96 */ "ALWAYS",
+ /* 97 */ "MATERIALIZED",
+ /* 98 */ "REINDEX",
+ /* 99 */ "RENAME",
+ /* 100 */ "CTIME_KW",
+ /* 101 */ "ANY",
+ /* 102 */ "BITAND",
+ /* 103 */ "BITOR",
+ /* 104 */ "LSHIFT",
+ /* 105 */ "RSHIFT",
+ /* 106 */ "PLUS",
+ /* 107 */ "MINUS",
+ /* 108 */ "STAR",
+ /* 109 */ "SLASH",
+ /* 110 */ "REM",
+ /* 111 */ "CONCAT",
+ /* 112 */ "PTR",
+ /* 113 */ "COLLATE",
+ /* 114 */ "BITNOT",
+ /* 115 */ "ON",
+ /* 116 */ "INDEXED",
+ /* 117 */ "STRING",
+ /* 118 */ "JOIN_KW",
+ /* 119 */ "CONSTRAINT",
+ /* 120 */ "DEFAULT",
+ /* 121 */ "NULL",
+ /* 122 */ "PRIMARY",
+ /* 123 */ "UNIQUE",
+ /* 124 */ "CHECK",
+ /* 125 */ "REFERENCES",
+ /* 126 */ "AUTOINCR",
+ /* 127 */ "INSERT",
+ /* 128 */ "DELETE",
+ /* 129 */ "UPDATE",
+ /* 130 */ "SET",
+ /* 131 */ "DEFERRABLE",
+ /* 132 */ "FOREIGN",
+ /* 133 */ "DROP",
+ /* 134 */ "UNION",
+ /* 135 */ "ALL",
+ /* 136 */ "EXCEPT",
+ /* 137 */ "INTERSECT",
+ /* 138 */ "SELECT",
+ /* 139 */ "VALUES",
+ /* 140 */ "DISTINCT",
+ /* 141 */ "DOT",
+ /* 142 */ "FROM",
+ /* 143 */ "JOIN",
+ /* 144 */ "USING",
+ /* 145 */ "ORDER",
+ /* 146 */ "GROUP",
+ /* 147 */ "HAVING",
+ /* 148 */ "LIMIT",
+ /* 149 */ "WHERE",
+ /* 150 */ "RETURNING",
+ /* 151 */ "INTO",
+ /* 152 */ "NOTHING",
+ /* 153 */ "FLOAT",
+ /* 154 */ "BLOB",
+ /* 155 */ "INTEGER",
+ /* 156 */ "VARIABLE",
+ /* 157 */ "CASE",
+ /* 158 */ "WHEN",
+ /* 159 */ "THEN",
+ /* 160 */ "ELSE",
+ /* 161 */ "INDEX",
+ /* 162 */ "ALTER",
+ /* 163 */ "ADD",
+ /* 164 */ "WINDOW",
+ /* 165 */ "OVER",
+ /* 166 */ "FILTER",
+ /* 167 */ "COLUMN",
+ /* 168 */ "AGG_FUNCTION",
+ /* 169 */ "AGG_COLUMN",
+ /* 170 */ "TRUEFALSE",
+ /* 171 */ "ISNOT",
+ /* 172 */ "FUNCTION",
+ /* 173 */ "UMINUS",
+ /* 174 */ "UPLUS",
+ /* 175 */ "TRUTH",
+ /* 176 */ "REGISTER",
+ /* 177 */ "VECTOR",
+ /* 178 */ "SELECT_COLUMN",
+ /* 179 */ "IF_NULL_ROW",
+ /* 180 */ "ASTERISK",
+ /* 181 */ "SPAN",
+ /* 182 */ "ERROR",
+ /* 183 */ "SPACE",
+ /* 184 */ "ILLEGAL",
+ /* 185 */ "input",
+ /* 186 */ "cmdlist",
+ /* 187 */ "ecmd",
+ /* 188 */ "cmdx",
+ /* 189 */ "explain",
+ /* 190 */ "cmd",
+ /* 191 */ "transtype",
+ /* 192 */ "trans_opt",
+ /* 193 */ "nm",
+ /* 194 */ "savepoint_opt",
+ /* 195 */ "create_table",
+ /* 196 */ "create_table_args",
+ /* 197 */ "createkw",
+ /* 198 */ "temp",
+ /* 199 */ "ifnotexists",
+ /* 200 */ "dbnm",
+ /* 201 */ "columnlist",
+ /* 202 */ "conslist_opt",
+ /* 203 */ "table_option_set",
+ /* 204 */ "select",
+ /* 205 */ "table_option",
+ /* 206 */ "columnname",
+ /* 207 */ "carglist",
+ /* 208 */ "typetoken",
+ /* 209 */ "typename",
+ /* 210 */ "signed",
+ /* 211 */ "plus_num",
+ /* 212 */ "minus_num",
+ /* 213 */ "scanpt",
+ /* 214 */ "scantok",
+ /* 215 */ "ccons",
+ /* 216 */ "term",
+ /* 217 */ "expr",
+ /* 218 */ "onconf",
+ /* 219 */ "sortorder",
+ /* 220 */ "autoinc",
+ /* 221 */ "eidlist_opt",
+ /* 222 */ "refargs",
+ /* 223 */ "defer_subclause",
+ /* 224 */ "generated",
+ /* 225 */ "refarg",
+ /* 226 */ "refact",
+ /* 227 */ "init_deferred_pred_opt",
+ /* 228 */ "conslist",
+ /* 229 */ "tconscomma",
+ /* 230 */ "tcons",
+ /* 231 */ "sortlist",
+ /* 232 */ "eidlist",
+ /* 233 */ "defer_subclause_opt",
+ /* 234 */ "orconf",
+ /* 235 */ "resolvetype",
+ /* 236 */ "raisetype",
+ /* 237 */ "ifexists",
+ /* 238 */ "fullname",
+ /* 239 */ "selectnowith",
+ /* 240 */ "oneselect",
+ /* 241 */ "wqlist",
+ /* 242 */ "multiselect_op",
+ /* 243 */ "distinct",
+ /* 244 */ "selcollist",
+ /* 245 */ "from",
+ /* 246 */ "where_opt",
+ /* 247 */ "groupby_opt",
+ /* 248 */ "having_opt",
+ /* 249 */ "orderby_opt",
+ /* 250 */ "limit_opt",
+ /* 251 */ "window_clause",
+ /* 252 */ "values",
+ /* 253 */ "nexprlist",
+ /* 254 */ "sclp",
+ /* 255 */ "as",
+ /* 256 */ "seltablist",
+ /* 257 */ "stl_prefix",
+ /* 258 */ "joinop",
+ /* 259 */ "on_using",
+ /* 260 */ "indexed_by",
+ /* 261 */ "exprlist",
+ /* 262 */ "xfullname",
+ /* 263 */ "idlist",
+ /* 264 */ "indexed_opt",
+ /* 265 */ "nulls",
+ /* 266 */ "with",
+ /* 267 */ "where_opt_ret",
+ /* 268 */ "setlist",
+ /* 269 */ "insert_cmd",
+ /* 270 */ "idlist_opt",
+ /* 271 */ "upsert",
+ /* 272 */ "returning",
+ /* 273 */ "filter_over",
+ /* 274 */ "likeop",
+ /* 275 */ "between_op",
+ /* 276 */ "in_op",
+ /* 277 */ "paren_exprlist",
+ /* 278 */ "case_operand",
+ /* 279 */ "case_exprlist",
+ /* 280 */ "case_else",
+ /* 281 */ "uniqueflag",
+ /* 282 */ "collate",
+ /* 283 */ "vinto",
+ /* 284 */ "nmnum",
+ /* 285 */ "trigger_decl",
+ /* 286 */ "trigger_cmd_list",
+ /* 287 */ "trigger_time",
+ /* 288 */ "trigger_event",
+ /* 289 */ "foreach_clause",
+ /* 290 */ "when_clause",
+ /* 291 */ "trigger_cmd",
+ /* 292 */ "trnm",
+ /* 293 */ "tridxby",
+ /* 294 */ "database_kw_opt",
+ /* 295 */ "key_opt",
+ /* 296 */ "add_column_fullname",
+ /* 297 */ "kwcolumn_opt",
+ /* 298 */ "create_vtab",
+ /* 299 */ "vtabarglist",
+ /* 300 */ "vtabarg",
+ /* 301 */ "vtabargtoken",
+ /* 302 */ "lp",
+ /* 303 */ "anylist",
+ /* 304 */ "wqitem",
+ /* 305 */ "wqas",
+ /* 306 */ "windowdefn_list",
+ /* 307 */ "windowdefn",
+ /* 308 */ "window",
+ /* 309 */ "frame_opt",
+ /* 310 */ "part_opt",
+ /* 311 */ "filter_clause",
+ /* 312 */ "over_clause",
+ /* 313 */ "range_or_rows",
+ /* 314 */ "frame_bound",
+ /* 315 */ "frame_bound_s",
+ /* 316 */ "frame_bound_e",
+ /* 317 */ "frame_exclude_opt",
+ /* 318 */ "frame_exclude",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
@@ -148224,354 +172746,392 @@ static const char *const yyRuleName[] = {
/* 16 */ "ifnotexists ::= IF NOT EXISTS",
/* 17 */ "temp ::= TEMP",
/* 18 */ "temp ::=",
- /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP table_options",
+ /* 19 */ "create_table_args ::= LP columnlist conslist_opt RP table_option_set",
/* 20 */ "create_table_args ::= AS select",
- /* 21 */ "table_options ::=",
- /* 22 */ "table_options ::= WITHOUT nm",
- /* 23 */ "columnname ::= nm typetoken",
- /* 24 */ "typetoken ::=",
- /* 25 */ "typetoken ::= typename LP signed RP",
- /* 26 */ "typetoken ::= typename LP signed COMMA signed RP",
- /* 27 */ "typename ::= typename ID|STRING",
- /* 28 */ "scanpt ::=",
- /* 29 */ "ccons ::= CONSTRAINT nm",
- /* 30 */ "ccons ::= DEFAULT scanpt term scanpt",
- /* 31 */ "ccons ::= DEFAULT LP expr RP",
- /* 32 */ "ccons ::= DEFAULT PLUS term scanpt",
- /* 33 */ "ccons ::= DEFAULT MINUS term scanpt",
- /* 34 */ "ccons ::= DEFAULT scanpt ID|INDEXED",
- /* 35 */ "ccons ::= NOT NULL onconf",
- /* 36 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc",
- /* 37 */ "ccons ::= UNIQUE onconf",
- /* 38 */ "ccons ::= CHECK LP expr RP",
- /* 39 */ "ccons ::= REFERENCES nm eidlist_opt refargs",
- /* 40 */ "ccons ::= defer_subclause",
- /* 41 */ "ccons ::= COLLATE ID|STRING",
- /* 42 */ "autoinc ::=",
- /* 43 */ "autoinc ::= AUTOINCR",
- /* 44 */ "refargs ::=",
- /* 45 */ "refargs ::= refargs refarg",
- /* 46 */ "refarg ::= MATCH nm",
- /* 47 */ "refarg ::= ON INSERT refact",
- /* 48 */ "refarg ::= ON DELETE refact",
- /* 49 */ "refarg ::= ON UPDATE refact",
- /* 50 */ "refact ::= SET NULL",
- /* 51 */ "refact ::= SET DEFAULT",
- /* 52 */ "refact ::= CASCADE",
- /* 53 */ "refact ::= RESTRICT",
- /* 54 */ "refact ::= NO ACTION",
- /* 55 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt",
- /* 56 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt",
- /* 57 */ "init_deferred_pred_opt ::=",
- /* 58 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED",
- /* 59 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE",
- /* 60 */ "conslist_opt ::=",
- /* 61 */ "tconscomma ::= COMMA",
- /* 62 */ "tcons ::= CONSTRAINT nm",
- /* 63 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf",
- /* 64 */ "tcons ::= UNIQUE LP sortlist RP onconf",
- /* 65 */ "tcons ::= CHECK LP expr RP onconf",
- /* 66 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt",
- /* 67 */ "defer_subclause_opt ::=",
- /* 68 */ "onconf ::=",
- /* 69 */ "onconf ::= ON CONFLICT resolvetype",
- /* 70 */ "orconf ::=",
- /* 71 */ "orconf ::= OR resolvetype",
- /* 72 */ "resolvetype ::= IGNORE",
- /* 73 */ "resolvetype ::= REPLACE",
- /* 74 */ "cmd ::= DROP TABLE ifexists fullname",
- /* 75 */ "ifexists ::= IF EXISTS",
- /* 76 */ "ifexists ::=",
- /* 77 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select",
- /* 78 */ "cmd ::= DROP VIEW ifexists fullname",
- /* 79 */ "cmd ::= select",
- /* 80 */ "select ::= WITH wqlist selectnowith",
- /* 81 */ "select ::= WITH RECURSIVE wqlist selectnowith",
- /* 82 */ "select ::= selectnowith",
- /* 83 */ "selectnowith ::= selectnowith multiselect_op oneselect",
- /* 84 */ "multiselect_op ::= UNION",
- /* 85 */ "multiselect_op ::= UNION ALL",
- /* 86 */ "multiselect_op ::= EXCEPT|INTERSECT",
- /* 87 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt",
- /* 88 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt",
- /* 89 */ "values ::= VALUES LP nexprlist RP",
- /* 90 */ "values ::= values COMMA LP nexprlist RP",
- /* 91 */ "distinct ::= DISTINCT",
- /* 92 */ "distinct ::= ALL",
- /* 93 */ "distinct ::=",
- /* 94 */ "sclp ::=",
- /* 95 */ "selcollist ::= sclp scanpt expr scanpt as",
- /* 96 */ "selcollist ::= sclp scanpt STAR",
- /* 97 */ "selcollist ::= sclp scanpt nm DOT STAR",
- /* 98 */ "as ::= AS nm",
- /* 99 */ "as ::=",
- /* 100 */ "from ::=",
- /* 101 */ "from ::= FROM seltablist",
- /* 102 */ "stl_prefix ::= seltablist joinop",
- /* 103 */ "stl_prefix ::=",
- /* 104 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt",
- /* 105 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt",
- /* 106 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt",
- /* 107 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt",
- /* 108 */ "dbnm ::=",
- /* 109 */ "dbnm ::= DOT nm",
- /* 110 */ "fullname ::= nm",
- /* 111 */ "fullname ::= nm DOT nm",
- /* 112 */ "xfullname ::= nm",
- /* 113 */ "xfullname ::= nm DOT nm",
- /* 114 */ "xfullname ::= nm DOT nm AS nm",
- /* 115 */ "xfullname ::= nm AS nm",
- /* 116 */ "joinop ::= COMMA|JOIN",
- /* 117 */ "joinop ::= JOIN_KW JOIN",
- /* 118 */ "joinop ::= JOIN_KW nm JOIN",
- /* 119 */ "joinop ::= JOIN_KW nm nm JOIN",
- /* 120 */ "on_opt ::= ON expr",
- /* 121 */ "on_opt ::=",
- /* 122 */ "indexed_opt ::=",
- /* 123 */ "indexed_opt ::= INDEXED BY nm",
- /* 124 */ "indexed_opt ::= NOT INDEXED",
- /* 125 */ "using_opt ::= USING LP idlist RP",
- /* 126 */ "using_opt ::=",
- /* 127 */ "orderby_opt ::=",
- /* 128 */ "orderby_opt ::= ORDER BY sortlist",
- /* 129 */ "sortlist ::= sortlist COMMA expr sortorder",
- /* 130 */ "sortlist ::= expr sortorder",
- /* 131 */ "sortorder ::= ASC",
- /* 132 */ "sortorder ::= DESC",
- /* 133 */ "sortorder ::=",
- /* 134 */ "groupby_opt ::=",
- /* 135 */ "groupby_opt ::= GROUP BY nexprlist",
- /* 136 */ "having_opt ::=",
- /* 137 */ "having_opt ::= HAVING expr",
- /* 138 */ "limit_opt ::=",
- /* 139 */ "limit_opt ::= LIMIT expr",
- /* 140 */ "limit_opt ::= LIMIT expr OFFSET expr",
- /* 141 */ "limit_opt ::= LIMIT expr COMMA expr",
- /* 142 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt",
- /* 143 */ "where_opt ::=",
- /* 144 */ "where_opt ::= WHERE expr",
- /* 145 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt",
- /* 146 */ "setlist ::= setlist COMMA nm EQ expr",
- /* 147 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
- /* 148 */ "setlist ::= nm EQ expr",
- /* 149 */ "setlist ::= LP idlist RP EQ expr",
- /* 150 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
- /* 151 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES",
- /* 152 */ "upsert ::=",
- /* 153 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt",
- /* 154 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING",
- /* 155 */ "upsert ::= ON CONFLICT DO NOTHING",
- /* 156 */ "insert_cmd ::= INSERT orconf",
- /* 157 */ "insert_cmd ::= REPLACE",
- /* 158 */ "idlist_opt ::=",
- /* 159 */ "idlist_opt ::= LP idlist RP",
- /* 160 */ "idlist ::= idlist COMMA nm",
- /* 161 */ "idlist ::= nm",
- /* 162 */ "expr ::= LP expr RP",
- /* 163 */ "expr ::= ID|INDEXED",
- /* 164 */ "expr ::= JOIN_KW",
- /* 165 */ "expr ::= nm DOT nm",
- /* 166 */ "expr ::= nm DOT nm DOT nm",
- /* 167 */ "term ::= NULL|FLOAT|BLOB",
- /* 168 */ "term ::= STRING",
- /* 169 */ "term ::= INTEGER",
- /* 170 */ "expr ::= VARIABLE",
- /* 171 */ "expr ::= expr COLLATE ID|STRING",
- /* 172 */ "expr ::= CAST LP expr AS typetoken RP",
- /* 173 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
- /* 174 */ "expr ::= ID|INDEXED LP STAR RP",
- /* 175 */ "expr ::= ID|INDEXED LP distinct exprlist RP over_clause",
- /* 176 */ "expr ::= ID|INDEXED LP STAR RP over_clause",
- /* 177 */ "term ::= CTIME_KW",
- /* 178 */ "expr ::= LP nexprlist COMMA expr RP",
- /* 179 */ "expr ::= expr AND expr",
- /* 180 */ "expr ::= expr OR expr",
- /* 181 */ "expr ::= expr LT|GT|GE|LE expr",
- /* 182 */ "expr ::= expr EQ|NE expr",
- /* 183 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
- /* 184 */ "expr ::= expr PLUS|MINUS expr",
- /* 185 */ "expr ::= expr STAR|SLASH|REM expr",
- /* 186 */ "expr ::= expr CONCAT expr",
- /* 187 */ "likeop ::= NOT LIKE_KW|MATCH",
- /* 188 */ "expr ::= expr likeop expr",
- /* 189 */ "expr ::= expr likeop expr ESCAPE expr",
- /* 190 */ "expr ::= expr ISNULL|NOTNULL",
- /* 191 */ "expr ::= expr NOT NULL",
- /* 192 */ "expr ::= expr IS expr",
- /* 193 */ "expr ::= expr IS NOT expr",
- /* 194 */ "expr ::= NOT expr",
- /* 195 */ "expr ::= BITNOT expr",
- /* 196 */ "expr ::= PLUS|MINUS expr",
- /* 197 */ "between_op ::= BETWEEN",
- /* 198 */ "between_op ::= NOT BETWEEN",
- /* 199 */ "expr ::= expr between_op expr AND expr",
- /* 200 */ "in_op ::= IN",
- /* 201 */ "in_op ::= NOT IN",
- /* 202 */ "expr ::= expr in_op LP exprlist RP",
- /* 203 */ "expr ::= LP select RP",
- /* 204 */ "expr ::= expr in_op LP select RP",
- /* 205 */ "expr ::= expr in_op nm dbnm paren_exprlist",
- /* 206 */ "expr ::= EXISTS LP select RP",
- /* 207 */ "expr ::= CASE case_operand case_exprlist case_else END",
- /* 208 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
- /* 209 */ "case_exprlist ::= WHEN expr THEN expr",
- /* 210 */ "case_else ::= ELSE expr",
- /* 211 */ "case_else ::=",
- /* 212 */ "case_operand ::= expr",
- /* 213 */ "case_operand ::=",
- /* 214 */ "exprlist ::=",
- /* 215 */ "nexprlist ::= nexprlist COMMA expr",
- /* 216 */ "nexprlist ::= expr",
- /* 217 */ "paren_exprlist ::=",
- /* 218 */ "paren_exprlist ::= LP exprlist RP",
- /* 219 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
- /* 220 */ "uniqueflag ::= UNIQUE",
- /* 221 */ "uniqueflag ::=",
- /* 222 */ "eidlist_opt ::=",
- /* 223 */ "eidlist_opt ::= LP eidlist RP",
- /* 224 */ "eidlist ::= eidlist COMMA nm collate sortorder",
- /* 225 */ "eidlist ::= nm collate sortorder",
- /* 226 */ "collate ::=",
- /* 227 */ "collate ::= COLLATE ID|STRING",
- /* 228 */ "cmd ::= DROP INDEX ifexists fullname",
- /* 229 */ "cmd ::= VACUUM",
- /* 230 */ "cmd ::= VACUUM nm",
- /* 231 */ "cmd ::= PRAGMA nm dbnm",
- /* 232 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
- /* 233 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
- /* 234 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
- /* 235 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
- /* 236 */ "plus_num ::= PLUS INTEGER|FLOAT",
- /* 237 */ "minus_num ::= MINUS INTEGER|FLOAT",
- /* 238 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
- /* 239 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
- /* 240 */ "trigger_time ::= BEFORE|AFTER",
- /* 241 */ "trigger_time ::= INSTEAD OF",
- /* 242 */ "trigger_time ::=",
- /* 243 */ "trigger_event ::= DELETE|INSERT",
- /* 244 */ "trigger_event ::= UPDATE",
- /* 245 */ "trigger_event ::= UPDATE OF idlist",
- /* 246 */ "when_clause ::=",
- /* 247 */ "when_clause ::= WHEN expr",
- /* 248 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
- /* 249 */ "trigger_cmd_list ::= trigger_cmd SEMI",
- /* 250 */ "trnm ::= nm DOT nm",
- /* 251 */ "tridxby ::= INDEXED BY nm",
- /* 252 */ "tridxby ::= NOT INDEXED",
- /* 253 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt",
- /* 254 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
- /* 255 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
- /* 256 */ "trigger_cmd ::= scanpt select scanpt",
- /* 257 */ "expr ::= RAISE LP IGNORE RP",
- /* 258 */ "expr ::= RAISE LP raisetype COMMA nm RP",
- /* 259 */ "raisetype ::= ROLLBACK",
- /* 260 */ "raisetype ::= ABORT",
- /* 261 */ "raisetype ::= FAIL",
- /* 262 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 263 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 264 */ "cmd ::= DETACH database_kw_opt expr",
- /* 265 */ "key_opt ::=",
- /* 266 */ "key_opt ::= KEY expr",
- /* 267 */ "cmd ::= REINDEX",
- /* 268 */ "cmd ::= REINDEX nm dbnm",
- /* 269 */ "cmd ::= ANALYZE",
- /* 270 */ "cmd ::= ANALYZE nm dbnm",
- /* 271 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 272 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
- /* 273 */ "add_column_fullname ::= fullname",
- /* 274 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
- /* 275 */ "cmd ::= create_vtab",
- /* 276 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 277 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
- /* 278 */ "vtabarg ::=",
- /* 279 */ "vtabargtoken ::= ANY",
- /* 280 */ "vtabargtoken ::= lp anylist RP",
- /* 281 */ "lp ::= LP",
- /* 282 */ "with ::= WITH wqlist",
- /* 283 */ "with ::= WITH RECURSIVE wqlist",
- /* 284 */ "wqlist ::= nm eidlist_opt AS LP select RP",
- /* 285 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
- /* 286 */ "windowdefn_list ::= windowdefn",
- /* 287 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
- /* 288 */ "windowdefn ::= nm AS window",
- /* 289 */ "window ::= LP part_opt orderby_opt frame_opt RP",
- /* 290 */ "part_opt ::= PARTITION BY nexprlist",
- /* 291 */ "part_opt ::=",
- /* 292 */ "frame_opt ::=",
- /* 293 */ "frame_opt ::= range_or_rows frame_bound_s",
- /* 294 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e",
- /* 295 */ "range_or_rows ::= RANGE",
- /* 296 */ "range_or_rows ::= ROWS",
- /* 297 */ "frame_bound_s ::= frame_bound",
- /* 298 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
- /* 299 */ "frame_bound_e ::= frame_bound",
- /* 300 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
- /* 301 */ "frame_bound ::= expr PRECEDING",
- /* 302 */ "frame_bound ::= CURRENT ROW",
- /* 303 */ "frame_bound ::= expr FOLLOWING",
- /* 304 */ "window_clause ::= WINDOW windowdefn_list",
- /* 305 */ "over_clause ::= filter_opt OVER window",
- /* 306 */ "over_clause ::= filter_opt OVER nm",
- /* 307 */ "filter_opt ::=",
- /* 308 */ "filter_opt ::= FILTER LP WHERE expr RP",
- /* 309 */ "input ::= cmdlist",
- /* 310 */ "cmdlist ::= cmdlist ecmd",
- /* 311 */ "cmdlist ::= ecmd",
- /* 312 */ "ecmd ::= SEMI",
- /* 313 */ "ecmd ::= cmdx SEMI",
- /* 314 */ "ecmd ::= explain cmdx",
- /* 315 */ "trans_opt ::=",
- /* 316 */ "trans_opt ::= TRANSACTION",
- /* 317 */ "trans_opt ::= TRANSACTION nm",
- /* 318 */ "savepoint_opt ::= SAVEPOINT",
- /* 319 */ "savepoint_opt ::=",
- /* 320 */ "cmd ::= create_table create_table_args",
- /* 321 */ "columnlist ::= columnlist COMMA columnname carglist",
- /* 322 */ "columnlist ::= columnname carglist",
- /* 323 */ "nm ::= ID|INDEXED",
- /* 324 */ "nm ::= STRING",
- /* 325 */ "nm ::= JOIN_KW",
- /* 326 */ "typetoken ::= typename",
- /* 327 */ "typename ::= ID|STRING",
- /* 328 */ "signed ::= plus_num",
- /* 329 */ "signed ::= minus_num",
- /* 330 */ "carglist ::= carglist ccons",
- /* 331 */ "carglist ::=",
- /* 332 */ "ccons ::= NULL onconf",
- /* 333 */ "conslist_opt ::= COMMA conslist",
- /* 334 */ "conslist ::= conslist tconscomma tcons",
- /* 335 */ "conslist ::= tcons",
- /* 336 */ "tconscomma ::=",
- /* 337 */ "defer_subclause_opt ::= defer_subclause",
- /* 338 */ "resolvetype ::= raisetype",
- /* 339 */ "selectnowith ::= oneselect",
- /* 340 */ "oneselect ::= values",
- /* 341 */ "sclp ::= selcollist COMMA",
- /* 342 */ "as ::= ID|STRING",
- /* 343 */ "expr ::= term",
- /* 344 */ "likeop ::= LIKE_KW|MATCH",
- /* 345 */ "exprlist ::= nexprlist",
- /* 346 */ "nmnum ::= plus_num",
- /* 347 */ "nmnum ::= nm",
- /* 348 */ "nmnum ::= ON",
- /* 349 */ "nmnum ::= DELETE",
- /* 350 */ "nmnum ::= DEFAULT",
- /* 351 */ "plus_num ::= INTEGER|FLOAT",
- /* 352 */ "foreach_clause ::=",
- /* 353 */ "foreach_clause ::= FOR EACH ROW",
- /* 354 */ "trnm ::= nm",
- /* 355 */ "tridxby ::=",
- /* 356 */ "database_kw_opt ::= DATABASE",
- /* 357 */ "database_kw_opt ::=",
- /* 358 */ "kwcolumn_opt ::=",
- /* 359 */ "kwcolumn_opt ::= COLUMNKW",
- /* 360 */ "vtabarglist ::= vtabarg",
- /* 361 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 362 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 363 */ "anylist ::=",
- /* 364 */ "anylist ::= anylist LP anylist RP",
- /* 365 */ "anylist ::= anylist ANY",
- /* 366 */ "with ::=",
+ /* 21 */ "table_option_set ::=",
+ /* 22 */ "table_option_set ::= table_option_set COMMA table_option",
+ /* 23 */ "table_option ::= WITHOUT nm",
+ /* 24 */ "table_option ::= nm",
+ /* 25 */ "columnname ::= nm typetoken",
+ /* 26 */ "typetoken ::=",
+ /* 27 */ "typetoken ::= typename LP signed RP",
+ /* 28 */ "typetoken ::= typename LP signed COMMA signed RP",
+ /* 29 */ "typename ::= typename ID|STRING",
+ /* 30 */ "scanpt ::=",
+ /* 31 */ "scantok ::=",
+ /* 32 */ "ccons ::= CONSTRAINT nm",
+ /* 33 */ "ccons ::= DEFAULT scantok term",
+ /* 34 */ "ccons ::= DEFAULT LP expr RP",
+ /* 35 */ "ccons ::= DEFAULT PLUS scantok term",
+ /* 36 */ "ccons ::= DEFAULT MINUS scantok term",
+ /* 37 */ "ccons ::= DEFAULT scantok ID|INDEXED",
+ /* 38 */ "ccons ::= NOT NULL onconf",
+ /* 39 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc",
+ /* 40 */ "ccons ::= UNIQUE onconf",
+ /* 41 */ "ccons ::= CHECK LP expr RP",
+ /* 42 */ "ccons ::= REFERENCES nm eidlist_opt refargs",
+ /* 43 */ "ccons ::= defer_subclause",
+ /* 44 */ "ccons ::= COLLATE ID|STRING",
+ /* 45 */ "generated ::= LP expr RP",
+ /* 46 */ "generated ::= LP expr RP ID",
+ /* 47 */ "autoinc ::=",
+ /* 48 */ "autoinc ::= AUTOINCR",
+ /* 49 */ "refargs ::=",
+ /* 50 */ "refargs ::= refargs refarg",
+ /* 51 */ "refarg ::= MATCH nm",
+ /* 52 */ "refarg ::= ON INSERT refact",
+ /* 53 */ "refarg ::= ON DELETE refact",
+ /* 54 */ "refarg ::= ON UPDATE refact",
+ /* 55 */ "refact ::= SET NULL",
+ /* 56 */ "refact ::= SET DEFAULT",
+ /* 57 */ "refact ::= CASCADE",
+ /* 58 */ "refact ::= RESTRICT",
+ /* 59 */ "refact ::= NO ACTION",
+ /* 60 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt",
+ /* 61 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt",
+ /* 62 */ "init_deferred_pred_opt ::=",
+ /* 63 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED",
+ /* 64 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE",
+ /* 65 */ "conslist_opt ::=",
+ /* 66 */ "tconscomma ::= COMMA",
+ /* 67 */ "tcons ::= CONSTRAINT nm",
+ /* 68 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf",
+ /* 69 */ "tcons ::= UNIQUE LP sortlist RP onconf",
+ /* 70 */ "tcons ::= CHECK LP expr RP onconf",
+ /* 71 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt",
+ /* 72 */ "defer_subclause_opt ::=",
+ /* 73 */ "onconf ::=",
+ /* 74 */ "onconf ::= ON CONFLICT resolvetype",
+ /* 75 */ "orconf ::=",
+ /* 76 */ "orconf ::= OR resolvetype",
+ /* 77 */ "resolvetype ::= IGNORE",
+ /* 78 */ "resolvetype ::= REPLACE",
+ /* 79 */ "cmd ::= DROP TABLE ifexists fullname",
+ /* 80 */ "ifexists ::= IF EXISTS",
+ /* 81 */ "ifexists ::=",
+ /* 82 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select",
+ /* 83 */ "cmd ::= DROP VIEW ifexists fullname",
+ /* 84 */ "cmd ::= select",
+ /* 85 */ "select ::= WITH wqlist selectnowith",
+ /* 86 */ "select ::= WITH RECURSIVE wqlist selectnowith",
+ /* 87 */ "select ::= selectnowith",
+ /* 88 */ "selectnowith ::= selectnowith multiselect_op oneselect",
+ /* 89 */ "multiselect_op ::= UNION",
+ /* 90 */ "multiselect_op ::= UNION ALL",
+ /* 91 */ "multiselect_op ::= EXCEPT|INTERSECT",
+ /* 92 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt",
+ /* 93 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt",
+ /* 94 */ "values ::= VALUES LP nexprlist RP",
+ /* 95 */ "values ::= values COMMA LP nexprlist RP",
+ /* 96 */ "distinct ::= DISTINCT",
+ /* 97 */ "distinct ::= ALL",
+ /* 98 */ "distinct ::=",
+ /* 99 */ "sclp ::=",
+ /* 100 */ "selcollist ::= sclp scanpt expr scanpt as",
+ /* 101 */ "selcollist ::= sclp scanpt STAR",
+ /* 102 */ "selcollist ::= sclp scanpt nm DOT STAR",
+ /* 103 */ "as ::= AS nm",
+ /* 104 */ "as ::=",
+ /* 105 */ "from ::=",
+ /* 106 */ "from ::= FROM seltablist",
+ /* 107 */ "stl_prefix ::= seltablist joinop",
+ /* 108 */ "stl_prefix ::=",
+ /* 109 */ "seltablist ::= stl_prefix nm dbnm as on_using",
+ /* 110 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using",
+ /* 111 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using",
+ /* 112 */ "seltablist ::= stl_prefix LP select RP as on_using",
+ /* 113 */ "seltablist ::= stl_prefix LP seltablist RP as on_using",
+ /* 114 */ "dbnm ::=",
+ /* 115 */ "dbnm ::= DOT nm",
+ /* 116 */ "fullname ::= nm",
+ /* 117 */ "fullname ::= nm DOT nm",
+ /* 118 */ "xfullname ::= nm",
+ /* 119 */ "xfullname ::= nm DOT nm",
+ /* 120 */ "xfullname ::= nm DOT nm AS nm",
+ /* 121 */ "xfullname ::= nm AS nm",
+ /* 122 */ "joinop ::= COMMA|JOIN",
+ /* 123 */ "joinop ::= JOIN_KW JOIN",
+ /* 124 */ "joinop ::= JOIN_KW nm JOIN",
+ /* 125 */ "joinop ::= JOIN_KW nm nm JOIN",
+ /* 126 */ "on_using ::= ON expr",
+ /* 127 */ "on_using ::= USING LP idlist RP",
+ /* 128 */ "on_using ::=",
+ /* 129 */ "indexed_opt ::=",
+ /* 130 */ "indexed_by ::= INDEXED BY nm",
+ /* 131 */ "indexed_by ::= NOT INDEXED",
+ /* 132 */ "orderby_opt ::=",
+ /* 133 */ "orderby_opt ::= ORDER BY sortlist",
+ /* 134 */ "sortlist ::= sortlist COMMA expr sortorder nulls",
+ /* 135 */ "sortlist ::= expr sortorder nulls",
+ /* 136 */ "sortorder ::= ASC",
+ /* 137 */ "sortorder ::= DESC",
+ /* 138 */ "sortorder ::=",
+ /* 139 */ "nulls ::= NULLS FIRST",
+ /* 140 */ "nulls ::= NULLS LAST",
+ /* 141 */ "nulls ::=",
+ /* 142 */ "groupby_opt ::=",
+ /* 143 */ "groupby_opt ::= GROUP BY nexprlist",
+ /* 144 */ "having_opt ::=",
+ /* 145 */ "having_opt ::= HAVING expr",
+ /* 146 */ "limit_opt ::=",
+ /* 147 */ "limit_opt ::= LIMIT expr",
+ /* 148 */ "limit_opt ::= LIMIT expr OFFSET expr",
+ /* 149 */ "limit_opt ::= LIMIT expr COMMA expr",
+ /* 150 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret",
+ /* 151 */ "where_opt ::=",
+ /* 152 */ "where_opt ::= WHERE expr",
+ /* 153 */ "where_opt_ret ::=",
+ /* 154 */ "where_opt_ret ::= WHERE expr",
+ /* 155 */ "where_opt_ret ::= RETURNING selcollist",
+ /* 156 */ "where_opt_ret ::= WHERE expr RETURNING selcollist",
+ /* 157 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret",
+ /* 158 */ "setlist ::= setlist COMMA nm EQ expr",
+ /* 159 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
+ /* 160 */ "setlist ::= nm EQ expr",
+ /* 161 */ "setlist ::= LP idlist RP EQ expr",
+ /* 162 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
+ /* 163 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning",
+ /* 164 */ "upsert ::=",
+ /* 165 */ "upsert ::= RETURNING selcollist",
+ /* 166 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert",
+ /* 167 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert",
+ /* 168 */ "upsert ::= ON CONFLICT DO NOTHING returning",
+ /* 169 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning",
+ /* 170 */ "returning ::= RETURNING selcollist",
+ /* 171 */ "insert_cmd ::= INSERT orconf",
+ /* 172 */ "insert_cmd ::= REPLACE",
+ /* 173 */ "idlist_opt ::=",
+ /* 174 */ "idlist_opt ::= LP idlist RP",
+ /* 175 */ "idlist ::= idlist COMMA nm",
+ /* 176 */ "idlist ::= nm",
+ /* 177 */ "expr ::= LP expr RP",
+ /* 178 */ "expr ::= ID|INDEXED|JOIN_KW",
+ /* 179 */ "expr ::= nm DOT nm",
+ /* 180 */ "expr ::= nm DOT nm DOT nm",
+ /* 181 */ "term ::= NULL|FLOAT|BLOB",
+ /* 182 */ "term ::= STRING",
+ /* 183 */ "term ::= INTEGER",
+ /* 184 */ "expr ::= VARIABLE",
+ /* 185 */ "expr ::= expr COLLATE ID|STRING",
+ /* 186 */ "expr ::= CAST LP expr AS typetoken RP",
+ /* 187 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP",
+ /* 188 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP",
+ /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP",
+ /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over",
+ /* 191 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over",
+ /* 192 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over",
+ /* 193 */ "term ::= CTIME_KW",
+ /* 194 */ "expr ::= LP nexprlist COMMA expr RP",
+ /* 195 */ "expr ::= expr AND expr",
+ /* 196 */ "expr ::= expr OR expr",
+ /* 197 */ "expr ::= expr LT|GT|GE|LE expr",
+ /* 198 */ "expr ::= expr EQ|NE expr",
+ /* 199 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
+ /* 200 */ "expr ::= expr PLUS|MINUS expr",
+ /* 201 */ "expr ::= expr STAR|SLASH|REM expr",
+ /* 202 */ "expr ::= expr CONCAT expr",
+ /* 203 */ "likeop ::= NOT LIKE_KW|MATCH",
+ /* 204 */ "expr ::= expr likeop expr",
+ /* 205 */ "expr ::= expr likeop expr ESCAPE expr",
+ /* 206 */ "expr ::= expr ISNULL|NOTNULL",
+ /* 207 */ "expr ::= expr NOT NULL",
+ /* 208 */ "expr ::= expr IS expr",
+ /* 209 */ "expr ::= expr IS NOT expr",
+ /* 210 */ "expr ::= expr IS NOT DISTINCT FROM expr",
+ /* 211 */ "expr ::= expr IS DISTINCT FROM expr",
+ /* 212 */ "expr ::= NOT expr",
+ /* 213 */ "expr ::= BITNOT expr",
+ /* 214 */ "expr ::= PLUS|MINUS expr",
+ /* 215 */ "expr ::= expr PTR expr",
+ /* 216 */ "between_op ::= BETWEEN",
+ /* 217 */ "between_op ::= NOT BETWEEN",
+ /* 218 */ "expr ::= expr between_op expr AND expr",
+ /* 219 */ "in_op ::= IN",
+ /* 220 */ "in_op ::= NOT IN",
+ /* 221 */ "expr ::= expr in_op LP exprlist RP",
+ /* 222 */ "expr ::= LP select RP",
+ /* 223 */ "expr ::= expr in_op LP select RP",
+ /* 224 */ "expr ::= expr in_op nm dbnm paren_exprlist",
+ /* 225 */ "expr ::= EXISTS LP select RP",
+ /* 226 */ "expr ::= CASE case_operand case_exprlist case_else END",
+ /* 227 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
+ /* 228 */ "case_exprlist ::= WHEN expr THEN expr",
+ /* 229 */ "case_else ::= ELSE expr",
+ /* 230 */ "case_else ::=",
+ /* 231 */ "case_operand ::=",
+ /* 232 */ "exprlist ::=",
+ /* 233 */ "nexprlist ::= nexprlist COMMA expr",
+ /* 234 */ "nexprlist ::= expr",
+ /* 235 */ "paren_exprlist ::=",
+ /* 236 */ "paren_exprlist ::= LP exprlist RP",
+ /* 237 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
+ /* 238 */ "uniqueflag ::= UNIQUE",
+ /* 239 */ "uniqueflag ::=",
+ /* 240 */ "eidlist_opt ::=",
+ /* 241 */ "eidlist_opt ::= LP eidlist RP",
+ /* 242 */ "eidlist ::= eidlist COMMA nm collate sortorder",
+ /* 243 */ "eidlist ::= nm collate sortorder",
+ /* 244 */ "collate ::=",
+ /* 245 */ "collate ::= COLLATE ID|STRING",
+ /* 246 */ "cmd ::= DROP INDEX ifexists fullname",
+ /* 247 */ "cmd ::= VACUUM vinto",
+ /* 248 */ "cmd ::= VACUUM nm vinto",
+ /* 249 */ "vinto ::= INTO expr",
+ /* 250 */ "vinto ::=",
+ /* 251 */ "cmd ::= PRAGMA nm dbnm",
+ /* 252 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
+ /* 253 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
+ /* 254 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 255 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
+ /* 256 */ "plus_num ::= PLUS INTEGER|FLOAT",
+ /* 257 */ "minus_num ::= MINUS INTEGER|FLOAT",
+ /* 258 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
+ /* 259 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 260 */ "trigger_time ::= BEFORE|AFTER",
+ /* 261 */ "trigger_time ::= INSTEAD OF",
+ /* 262 */ "trigger_time ::=",
+ /* 263 */ "trigger_event ::= DELETE|INSERT",
+ /* 264 */ "trigger_event ::= UPDATE",
+ /* 265 */ "trigger_event ::= UPDATE OF idlist",
+ /* 266 */ "when_clause ::=",
+ /* 267 */ "when_clause ::= WHEN expr",
+ /* 268 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
+ /* 269 */ "trigger_cmd_list ::= trigger_cmd SEMI",
+ /* 270 */ "trnm ::= nm DOT nm",
+ /* 271 */ "tridxby ::= INDEXED BY nm",
+ /* 272 */ "tridxby ::= NOT INDEXED",
+ /* 273 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt",
+ /* 274 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
+ /* 275 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
+ /* 276 */ "trigger_cmd ::= scanpt select scanpt",
+ /* 277 */ "expr ::= RAISE LP IGNORE RP",
+ /* 278 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 279 */ "raisetype ::= ROLLBACK",
+ /* 280 */ "raisetype ::= ABORT",
+ /* 281 */ "raisetype ::= FAIL",
+ /* 282 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 283 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 284 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 285 */ "key_opt ::=",
+ /* 286 */ "key_opt ::= KEY expr",
+ /* 287 */ "cmd ::= REINDEX",
+ /* 288 */ "cmd ::= REINDEX nm dbnm",
+ /* 289 */ "cmd ::= ANALYZE",
+ /* 290 */ "cmd ::= ANALYZE nm dbnm",
+ /* 291 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 292 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
+ /* 293 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm",
+ /* 294 */ "add_column_fullname ::= fullname",
+ /* 295 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
+ /* 296 */ "cmd ::= create_vtab",
+ /* 297 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 298 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
+ /* 299 */ "vtabarg ::=",
+ /* 300 */ "vtabargtoken ::= ANY",
+ /* 301 */ "vtabargtoken ::= lp anylist RP",
+ /* 302 */ "lp ::= LP",
+ /* 303 */ "with ::= WITH wqlist",
+ /* 304 */ "with ::= WITH RECURSIVE wqlist",
+ /* 305 */ "wqas ::= AS",
+ /* 306 */ "wqas ::= AS MATERIALIZED",
+ /* 307 */ "wqas ::= AS NOT MATERIALIZED",
+ /* 308 */ "wqitem ::= nm eidlist_opt wqas LP select RP",
+ /* 309 */ "wqlist ::= wqitem",
+ /* 310 */ "wqlist ::= wqlist COMMA wqitem",
+ /* 311 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
+ /* 312 */ "windowdefn ::= nm AS LP window RP",
+ /* 313 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 314 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 315 */ "window ::= ORDER BY sortlist frame_opt",
+ /* 316 */ "window ::= nm ORDER BY sortlist frame_opt",
+ /* 317 */ "window ::= nm frame_opt",
+ /* 318 */ "frame_opt ::=",
+ /* 319 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
+ /* 320 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
+ /* 321 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
+ /* 322 */ "frame_bound_s ::= frame_bound",
+ /* 323 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
+ /* 324 */ "frame_bound_e ::= frame_bound",
+ /* 325 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
+ /* 326 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
+ /* 327 */ "frame_bound ::= CURRENT ROW",
+ /* 328 */ "frame_exclude_opt ::=",
+ /* 329 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
+ /* 330 */ "frame_exclude ::= NO OTHERS",
+ /* 331 */ "frame_exclude ::= CURRENT ROW",
+ /* 332 */ "frame_exclude ::= GROUP|TIES",
+ /* 333 */ "window_clause ::= WINDOW windowdefn_list",
+ /* 334 */ "filter_over ::= filter_clause over_clause",
+ /* 335 */ "filter_over ::= over_clause",
+ /* 336 */ "filter_over ::= filter_clause",
+ /* 337 */ "over_clause ::= OVER LP window RP",
+ /* 338 */ "over_clause ::= OVER nm",
+ /* 339 */ "filter_clause ::= FILTER LP WHERE expr RP",
+ /* 340 */ "input ::= cmdlist",
+ /* 341 */ "cmdlist ::= cmdlist ecmd",
+ /* 342 */ "cmdlist ::= ecmd",
+ /* 343 */ "ecmd ::= SEMI",
+ /* 344 */ "ecmd ::= cmdx SEMI",
+ /* 345 */ "ecmd ::= explain cmdx SEMI",
+ /* 346 */ "trans_opt ::=",
+ /* 347 */ "trans_opt ::= TRANSACTION",
+ /* 348 */ "trans_opt ::= TRANSACTION nm",
+ /* 349 */ "savepoint_opt ::= SAVEPOINT",
+ /* 350 */ "savepoint_opt ::=",
+ /* 351 */ "cmd ::= create_table create_table_args",
+ /* 352 */ "table_option_set ::= table_option",
+ /* 353 */ "columnlist ::= columnlist COMMA columnname carglist",
+ /* 354 */ "columnlist ::= columnname carglist",
+ /* 355 */ "nm ::= ID|INDEXED|JOIN_KW",
+ /* 356 */ "nm ::= STRING",
+ /* 357 */ "typetoken ::= typename",
+ /* 358 */ "typename ::= ID|STRING",
+ /* 359 */ "signed ::= plus_num",
+ /* 360 */ "signed ::= minus_num",
+ /* 361 */ "carglist ::= carglist ccons",
+ /* 362 */ "carglist ::=",
+ /* 363 */ "ccons ::= NULL onconf",
+ /* 364 */ "ccons ::= GENERATED ALWAYS AS generated",
+ /* 365 */ "ccons ::= AS generated",
+ /* 366 */ "conslist_opt ::= COMMA conslist",
+ /* 367 */ "conslist ::= conslist tconscomma tcons",
+ /* 368 */ "conslist ::= tcons",
+ /* 369 */ "tconscomma ::=",
+ /* 370 */ "defer_subclause_opt ::= defer_subclause",
+ /* 371 */ "resolvetype ::= raisetype",
+ /* 372 */ "selectnowith ::= oneselect",
+ /* 373 */ "oneselect ::= values",
+ /* 374 */ "sclp ::= selcollist COMMA",
+ /* 375 */ "as ::= ID|STRING",
+ /* 376 */ "indexed_opt ::= indexed_by",
+ /* 377 */ "returning ::=",
+ /* 378 */ "expr ::= term",
+ /* 379 */ "likeop ::= LIKE_KW|MATCH",
+ /* 380 */ "case_operand ::= expr",
+ /* 381 */ "exprlist ::= nexprlist",
+ /* 382 */ "nmnum ::= plus_num",
+ /* 383 */ "nmnum ::= nm",
+ /* 384 */ "nmnum ::= ON",
+ /* 385 */ "nmnum ::= DELETE",
+ /* 386 */ "nmnum ::= DEFAULT",
+ /* 387 */ "plus_num ::= INTEGER|FLOAT",
+ /* 388 */ "foreach_clause ::=",
+ /* 389 */ "foreach_clause ::= FOR EACH ROW",
+ /* 390 */ "trnm ::= nm",
+ /* 391 */ "tridxby ::=",
+ /* 392 */ "database_kw_opt ::= DATABASE",
+ /* 393 */ "database_kw_opt ::=",
+ /* 394 */ "kwcolumn_opt ::=",
+ /* 395 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 396 */ "vtabarglist ::= vtabarg",
+ /* 397 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 398 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 399 */ "anylist ::=",
+ /* 400 */ "anylist ::= anylist LP anylist RP",
+ /* 401 */ "anylist ::= anylist ANY",
+ /* 402 */ "with ::=",
+ /* 403 */ "windowdefn_list ::= windowdefn",
+ /* 404 */ "window ::= frame_opt",
};
#endif /* NDEBUG */
@@ -148605,7 +173165,7 @@ static int yyGrowStack(yyParser *p){
#endif
p->yystksz = newSize;
}
- return pNew==0;
+ return pNew==0;
}
#endif
@@ -148647,7 +173207,7 @@ SQLITE_PRIVATE void sqlite3ParserInit(void *yypRawParser sqlite3ParserCTX_PDECL)
}
#ifndef sqlite3Parser_ENGINEALWAYSONSTACK
-/*
+/*
** This function allocates a new parser.
** The only argument is a pointer to a function which works like
** malloc.
@@ -148674,7 +173234,7 @@ SQLITE_PRIVATE void *sqlite3ParserAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) sql
/* The following function deletes the "minor type" or semantic value
** associated with a symbol. The symbol can be either a terminal
** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
-** a pointer to the value to be deleted. The code used to do the
+** a pointer to the value to be deleted. The code used to do the
** deletions is derived from the %destructor and/or %token_destructor
** directives of the input grammar.
*/
@@ -148689,7 +173249,7 @@ static void yy_destructor(
/* Here is inserted the actions which take place when a
** terminal or non-terminal is destroyed. This can happen
** when the symbol is popped from the stack during a
- ** reduce or during error processing or when a parser is
+ ** reduce or during error processing or when a parser is
** being destroyed before it is finished parsing.
**
** Note: during a reduce, the only symbols destroyed are those
@@ -148697,96 +173257,97 @@ static void yy_destructor(
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
- case 174: /* select */
- case 206: /* selectnowith */
- case 207: /* oneselect */
- case 219: /* values */
+ case 204: /* select */
+ case 239: /* selectnowith */
+ case 240: /* oneselect */
+ case 252: /* values */
{
-sqlite3SelectDelete(pParse->db, (yypminor->yy489));
-}
- break;
- case 184: /* term */
- case 185: /* expr */
- case 213: /* where_opt */
- case 215: /* having_opt */
- case 227: /* on_opt */
- case 242: /* case_operand */
- case 244: /* case_else */
- case 253: /* when_clause */
- case 258: /* key_opt */
- case 272: /* filter_opt */
+sqlite3SelectDelete(pParse->db, (yypminor->yy47));
+}
+ break;
+ case 216: /* term */
+ case 217: /* expr */
+ case 246: /* where_opt */
+ case 248: /* having_opt */
+ case 267: /* where_opt_ret */
+ case 278: /* case_operand */
+ case 280: /* case_else */
+ case 283: /* vinto */
+ case 290: /* when_clause */
+ case 295: /* key_opt */
+ case 311: /* filter_clause */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy18));
-}
- break;
- case 189: /* eidlist_opt */
- case 198: /* sortlist */
- case 199: /* eidlist */
- case 211: /* selcollist */
- case 214: /* groupby_opt */
- case 216: /* orderby_opt */
- case 220: /* nexprlist */
- case 221: /* sclp */
- case 229: /* exprlist */
- case 233: /* setlist */
- case 241: /* paren_exprlist */
- case 243: /* case_exprlist */
- case 271: /* part_opt */
+sqlite3ExprDelete(pParse->db, (yypminor->yy528));
+}
+ break;
+ case 221: /* eidlist_opt */
+ case 231: /* sortlist */
+ case 232: /* eidlist */
+ case 244: /* selcollist */
+ case 247: /* groupby_opt */
+ case 249: /* orderby_opt */
+ case 253: /* nexprlist */
+ case 254: /* sclp */
+ case 261: /* exprlist */
+ case 268: /* setlist */
+ case 277: /* paren_exprlist */
+ case 279: /* case_exprlist */
+ case 310: /* part_opt */
{
-sqlite3ExprListDelete(pParse->db, (yypminor->yy420));
+sqlite3ExprListDelete(pParse->db, (yypminor->yy322));
}
break;
- case 205: /* fullname */
- case 212: /* from */
- case 223: /* seltablist */
- case 224: /* stl_prefix */
- case 230: /* xfullname */
+ case 238: /* fullname */
+ case 245: /* from */
+ case 256: /* seltablist */
+ case 257: /* stl_prefix */
+ case 262: /* xfullname */
{
-sqlite3SrcListDelete(pParse->db, (yypminor->yy135));
+sqlite3SrcListDelete(pParse->db, (yypminor->yy131));
}
break;
- case 208: /* wqlist */
+ case 241: /* wqlist */
{
-sqlite3WithDelete(pParse->db, (yypminor->yy449));
+sqlite3WithDelete(pParse->db, (yypminor->yy521));
}
break;
- case 218: /* window_clause */
- case 267: /* windowdefn_list */
+ case 251: /* window_clause */
+ case 306: /* windowdefn_list */
{
-sqlite3WindowListDelete(pParse->db, (yypminor->yy327));
+sqlite3WindowListDelete(pParse->db, (yypminor->yy41));
}
break;
- case 228: /* using_opt */
- case 231: /* idlist */
- case 235: /* idlist_opt */
+ case 263: /* idlist */
+ case 270: /* idlist_opt */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy48));
+sqlite3IdListDelete(pParse->db, (yypminor->yy254));
}
break;
- case 237: /* over_clause */
- case 268: /* windowdefn */
- case 269: /* window */
- case 270: /* frame_opt */
+ case 273: /* filter_over */
+ case 307: /* windowdefn */
+ case 308: /* window */
+ case 309: /* frame_opt */
+ case 312: /* over_clause */
{
-sqlite3WindowDelete(pParse->db, (yypminor->yy327));
+sqlite3WindowDelete(pParse->db, (yypminor->yy41));
}
break;
- case 249: /* trigger_cmd_list */
- case 254: /* trigger_cmd */
+ case 286: /* trigger_cmd_list */
+ case 291: /* trigger_cmd */
{
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy207));
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy33));
}
break;
- case 251: /* trigger_event */
+ case 288: /* trigger_event */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy34).b);
+sqlite3IdListDelete(pParse->db, (yypminor->yy180).b);
}
break;
- case 274: /* frame_bound */
- case 275: /* frame_bound_s */
- case 276: /* frame_bound_e */
+ case 314: /* frame_bound */
+ case 315: /* frame_bound_s */
+ case 316: /* frame_bound_e */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy119).pExpr);
+sqlite3ExprDelete(pParse->db, (yypminor->yy595).pExpr);
}
break;
/********* End destructor definitions *****************************************/
@@ -148827,7 +173388,7 @@ SQLITE_PRIVATE void sqlite3ParserFinalize(void *p){
}
#ifndef sqlite3Parser_ENGINEALWAYSONSTACK
-/*
+/*
** Deallocate and destroy a parser. Destructors are called for
** all stack elements before shutting the parser down.
**
@@ -148912,15 +173473,18 @@ static YYACTIONTYPE yy_find_shift_action(
do{
i = yy_shift_ofst[stateno];
assert( i>=0 );
- /* assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD ); */
+ assert( i<=YY_ACTTAB_COUNT );
+ assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD );
assert( iLookAhead!=YYNOCODE );
assert( iLookAhead < YYNTOKEN );
i += iLookAhead;
- if( i>=YY_NLOOKAHEAD || yy_lookahead[i]!=iLookAhead ){
+ assert( i<(int)YY_NLOOKAHEAD );
+ if( yy_lookahead[i]!=iLookAhead ){
#ifdef YYFALLBACK
YYCODETYPE iFallback; /* Fallback token */
- if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
- && (iFallback = yyFallback[iLookAhead])!=0 ){
+ assert( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) );
+ iFallback = yyFallback[iLookAhead];
+ if( iFallback!=0 ){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
@@ -148935,16 +173499,8 @@ static YYACTIONTYPE yy_find_shift_action(
#ifdef YYWILDCARD
{
int j = i - iLookAhead + YYWILDCARD;
- if(
-#if YY_SHIFT_MIN+YYWILDCARD<0
- j>=0 &&
-#endif
-#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
- j<YY_ACTTAB_COUNT &&
-#endif
- j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) &&
- yy_lookahead[j]==YYWILDCARD && iLookAhead>0
- ){
+ assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) );
+ if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
@@ -148958,6 +173514,7 @@ static YYACTIONTYPE yy_find_shift_action(
#endif /* YYWILDCARD */
return yy_default[stateno];
}else{
+ assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) );
return yy_action[i];
}
}while(1);
@@ -149053,7 +173610,7 @@ static void yy_shift(
assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
}
#endif
-#if YYSTACKDEPTH>0
+#if YYSTACKDEPTH>0
if( yypParser->yytos>yypParser->yystackEnd ){
yypParser->yytos--;
yyStackOverflow(yypParser);
@@ -149078,380 +173635,824 @@ static void yy_shift(
yyTraceShift(yypParser, yyNewState, "Shift");
}
-/* The following table contains information about every rule that
-** is used during the reduce.
-*/
-static const struct {
- YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
- signed char nrhs; /* Negative of the number of RHS symbols in the rule */
-} yyRuleInfo[] = {
- { 159, -1 }, /* (0) explain ::= EXPLAIN */
- { 159, -3 }, /* (1) explain ::= EXPLAIN QUERY PLAN */
- { 158, -1 }, /* (2) cmdx ::= cmd */
- { 160, -3 }, /* (3) cmd ::= BEGIN transtype trans_opt */
- { 161, 0 }, /* (4) transtype ::= */
- { 161, -1 }, /* (5) transtype ::= DEFERRED */
- { 161, -1 }, /* (6) transtype ::= IMMEDIATE */
- { 161, -1 }, /* (7) transtype ::= EXCLUSIVE */
- { 160, -2 }, /* (8) cmd ::= COMMIT|END trans_opt */
- { 160, -2 }, /* (9) cmd ::= ROLLBACK trans_opt */
- { 160, -2 }, /* (10) cmd ::= SAVEPOINT nm */
- { 160, -3 }, /* (11) cmd ::= RELEASE savepoint_opt nm */
- { 160, -5 }, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
- { 165, -6 }, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
- { 167, -1 }, /* (14) createkw ::= CREATE */
- { 169, 0 }, /* (15) ifnotexists ::= */
- { 169, -3 }, /* (16) ifnotexists ::= IF NOT EXISTS */
- { 168, -1 }, /* (17) temp ::= TEMP */
- { 168, 0 }, /* (18) temp ::= */
- { 166, -5 }, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */
- { 166, -2 }, /* (20) create_table_args ::= AS select */
- { 173, 0 }, /* (21) table_options ::= */
- { 173, -2 }, /* (22) table_options ::= WITHOUT nm */
- { 175, -2 }, /* (23) columnname ::= nm typetoken */
- { 177, 0 }, /* (24) typetoken ::= */
- { 177, -4 }, /* (25) typetoken ::= typename LP signed RP */
- { 177, -6 }, /* (26) typetoken ::= typename LP signed COMMA signed RP */
- { 178, -2 }, /* (27) typename ::= typename ID|STRING */
- { 182, 0 }, /* (28) scanpt ::= */
- { 183, -2 }, /* (29) ccons ::= CONSTRAINT nm */
- { 183, -4 }, /* (30) ccons ::= DEFAULT scanpt term scanpt */
- { 183, -4 }, /* (31) ccons ::= DEFAULT LP expr RP */
- { 183, -4 }, /* (32) ccons ::= DEFAULT PLUS term scanpt */
- { 183, -4 }, /* (33) ccons ::= DEFAULT MINUS term scanpt */
- { 183, -3 }, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */
- { 183, -3 }, /* (35) ccons ::= NOT NULL onconf */
- { 183, -5 }, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */
- { 183, -2 }, /* (37) ccons ::= UNIQUE onconf */
- { 183, -4 }, /* (38) ccons ::= CHECK LP expr RP */
- { 183, -4 }, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */
- { 183, -1 }, /* (40) ccons ::= defer_subclause */
- { 183, -2 }, /* (41) ccons ::= COLLATE ID|STRING */
- { 188, 0 }, /* (42) autoinc ::= */
- { 188, -1 }, /* (43) autoinc ::= AUTOINCR */
- { 190, 0 }, /* (44) refargs ::= */
- { 190, -2 }, /* (45) refargs ::= refargs refarg */
- { 192, -2 }, /* (46) refarg ::= MATCH nm */
- { 192, -3 }, /* (47) refarg ::= ON INSERT refact */
- { 192, -3 }, /* (48) refarg ::= ON DELETE refact */
- { 192, -3 }, /* (49) refarg ::= ON UPDATE refact */
- { 193, -2 }, /* (50) refact ::= SET NULL */
- { 193, -2 }, /* (51) refact ::= SET DEFAULT */
- { 193, -1 }, /* (52) refact ::= CASCADE */
- { 193, -1 }, /* (53) refact ::= RESTRICT */
- { 193, -2 }, /* (54) refact ::= NO ACTION */
- { 191, -3 }, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
- { 191, -2 }, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
- { 194, 0 }, /* (57) init_deferred_pred_opt ::= */
- { 194, -2 }, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */
- { 194, -2 }, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
- { 172, 0 }, /* (60) conslist_opt ::= */
- { 196, -1 }, /* (61) tconscomma ::= COMMA */
- { 197, -2 }, /* (62) tcons ::= CONSTRAINT nm */
- { 197, -7 }, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
- { 197, -5 }, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */
- { 197, -5 }, /* (65) tcons ::= CHECK LP expr RP onconf */
- { 197, -10 }, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
- { 200, 0 }, /* (67) defer_subclause_opt ::= */
- { 186, 0 }, /* (68) onconf ::= */
- { 186, -3 }, /* (69) onconf ::= ON CONFLICT resolvetype */
- { 201, 0 }, /* (70) orconf ::= */
- { 201, -2 }, /* (71) orconf ::= OR resolvetype */
- { 202, -1 }, /* (72) resolvetype ::= IGNORE */
- { 202, -1 }, /* (73) resolvetype ::= REPLACE */
- { 160, -4 }, /* (74) cmd ::= DROP TABLE ifexists fullname */
- { 204, -2 }, /* (75) ifexists ::= IF EXISTS */
- { 204, 0 }, /* (76) ifexists ::= */
- { 160, -9 }, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
- { 160, -4 }, /* (78) cmd ::= DROP VIEW ifexists fullname */
- { 160, -1 }, /* (79) cmd ::= select */
- { 174, -3 }, /* (80) select ::= WITH wqlist selectnowith */
- { 174, -4 }, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */
- { 174, -1 }, /* (82) select ::= selectnowith */
- { 206, -3 }, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */
- { 209, -1 }, /* (84) multiselect_op ::= UNION */
- { 209, -2 }, /* (85) multiselect_op ::= UNION ALL */
- { 209, -1 }, /* (86) multiselect_op ::= EXCEPT|INTERSECT */
- { 207, -9 }, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
- { 207, -10 }, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
- { 219, -4 }, /* (89) values ::= VALUES LP nexprlist RP */
- { 219, -5 }, /* (90) values ::= values COMMA LP nexprlist RP */
- { 210, -1 }, /* (91) distinct ::= DISTINCT */
- { 210, -1 }, /* (92) distinct ::= ALL */
- { 210, 0 }, /* (93) distinct ::= */
- { 221, 0 }, /* (94) sclp ::= */
- { 211, -5 }, /* (95) selcollist ::= sclp scanpt expr scanpt as */
- { 211, -3 }, /* (96) selcollist ::= sclp scanpt STAR */
- { 211, -5 }, /* (97) selcollist ::= sclp scanpt nm DOT STAR */
- { 222, -2 }, /* (98) as ::= AS nm */
- { 222, 0 }, /* (99) as ::= */
- { 212, 0 }, /* (100) from ::= */
- { 212, -2 }, /* (101) from ::= FROM seltablist */
- { 224, -2 }, /* (102) stl_prefix ::= seltablist joinop */
- { 224, 0 }, /* (103) stl_prefix ::= */
- { 223, -7 }, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
- { 223, -9 }, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
- { 223, -7 }, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */
- { 223, -7 }, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
- { 170, 0 }, /* (108) dbnm ::= */
- { 170, -2 }, /* (109) dbnm ::= DOT nm */
- { 205, -1 }, /* (110) fullname ::= nm */
- { 205, -3 }, /* (111) fullname ::= nm DOT nm */
- { 230, -1 }, /* (112) xfullname ::= nm */
- { 230, -3 }, /* (113) xfullname ::= nm DOT nm */
- { 230, -5 }, /* (114) xfullname ::= nm DOT nm AS nm */
- { 230, -3 }, /* (115) xfullname ::= nm AS nm */
- { 225, -1 }, /* (116) joinop ::= COMMA|JOIN */
- { 225, -2 }, /* (117) joinop ::= JOIN_KW JOIN */
- { 225, -3 }, /* (118) joinop ::= JOIN_KW nm JOIN */
- { 225, -4 }, /* (119) joinop ::= JOIN_KW nm nm JOIN */
- { 227, -2 }, /* (120) on_opt ::= ON expr */
- { 227, 0 }, /* (121) on_opt ::= */
- { 226, 0 }, /* (122) indexed_opt ::= */
- { 226, -3 }, /* (123) indexed_opt ::= INDEXED BY nm */
- { 226, -2 }, /* (124) indexed_opt ::= NOT INDEXED */
- { 228, -4 }, /* (125) using_opt ::= USING LP idlist RP */
- { 228, 0 }, /* (126) using_opt ::= */
- { 216, 0 }, /* (127) orderby_opt ::= */
- { 216, -3 }, /* (128) orderby_opt ::= ORDER BY sortlist */
- { 198, -4 }, /* (129) sortlist ::= sortlist COMMA expr sortorder */
- { 198, -2 }, /* (130) sortlist ::= expr sortorder */
- { 187, -1 }, /* (131) sortorder ::= ASC */
- { 187, -1 }, /* (132) sortorder ::= DESC */
- { 187, 0 }, /* (133) sortorder ::= */
- { 214, 0 }, /* (134) groupby_opt ::= */
- { 214, -3 }, /* (135) groupby_opt ::= GROUP BY nexprlist */
- { 215, 0 }, /* (136) having_opt ::= */
- { 215, -2 }, /* (137) having_opt ::= HAVING expr */
- { 217, 0 }, /* (138) limit_opt ::= */
- { 217, -2 }, /* (139) limit_opt ::= LIMIT expr */
- { 217, -4 }, /* (140) limit_opt ::= LIMIT expr OFFSET expr */
- { 217, -4 }, /* (141) limit_opt ::= LIMIT expr COMMA expr */
- { 160, -6 }, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
- { 213, 0 }, /* (143) where_opt ::= */
- { 213, -2 }, /* (144) where_opt ::= WHERE expr */
- { 160, -8 }, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
- { 233, -5 }, /* (146) setlist ::= setlist COMMA nm EQ expr */
- { 233, -7 }, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */
- { 233, -3 }, /* (148) setlist ::= nm EQ expr */
- { 233, -5 }, /* (149) setlist ::= LP idlist RP EQ expr */
- { 160, -7 }, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
- { 160, -7 }, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
- { 236, 0 }, /* (152) upsert ::= */
- { 236, -11 }, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
- { 236, -8 }, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
- { 236, -4 }, /* (155) upsert ::= ON CONFLICT DO NOTHING */
- { 234, -2 }, /* (156) insert_cmd ::= INSERT orconf */
- { 234, -1 }, /* (157) insert_cmd ::= REPLACE */
- { 235, 0 }, /* (158) idlist_opt ::= */
- { 235, -3 }, /* (159) idlist_opt ::= LP idlist RP */
- { 231, -3 }, /* (160) idlist ::= idlist COMMA nm */
- { 231, -1 }, /* (161) idlist ::= nm */
- { 185, -3 }, /* (162) expr ::= LP expr RP */
- { 185, -1 }, /* (163) expr ::= ID|INDEXED */
- { 185, -1 }, /* (164) expr ::= JOIN_KW */
- { 185, -3 }, /* (165) expr ::= nm DOT nm */
- { 185, -5 }, /* (166) expr ::= nm DOT nm DOT nm */
- { 184, -1 }, /* (167) term ::= NULL|FLOAT|BLOB */
- { 184, -1 }, /* (168) term ::= STRING */
- { 184, -1 }, /* (169) term ::= INTEGER */
- { 185, -1 }, /* (170) expr ::= VARIABLE */
- { 185, -3 }, /* (171) expr ::= expr COLLATE ID|STRING */
- { 185, -6 }, /* (172) expr ::= CAST LP expr AS typetoken RP */
- { 185, -5 }, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */
- { 185, -4 }, /* (174) expr ::= ID|INDEXED LP STAR RP */
- { 185, -6 }, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
- { 185, -5 }, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */
- { 184, -1 }, /* (177) term ::= CTIME_KW */
- { 185, -5 }, /* (178) expr ::= LP nexprlist COMMA expr RP */
- { 185, -3 }, /* (179) expr ::= expr AND expr */
- { 185, -3 }, /* (180) expr ::= expr OR expr */
- { 185, -3 }, /* (181) expr ::= expr LT|GT|GE|LE expr */
- { 185, -3 }, /* (182) expr ::= expr EQ|NE expr */
- { 185, -3 }, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- { 185, -3 }, /* (184) expr ::= expr PLUS|MINUS expr */
- { 185, -3 }, /* (185) expr ::= expr STAR|SLASH|REM expr */
- { 185, -3 }, /* (186) expr ::= expr CONCAT expr */
- { 238, -2 }, /* (187) likeop ::= NOT LIKE_KW|MATCH */
- { 185, -3 }, /* (188) expr ::= expr likeop expr */
- { 185, -5 }, /* (189) expr ::= expr likeop expr ESCAPE expr */
- { 185, -2 }, /* (190) expr ::= expr ISNULL|NOTNULL */
- { 185, -3 }, /* (191) expr ::= expr NOT NULL */
- { 185, -3 }, /* (192) expr ::= expr IS expr */
- { 185, -4 }, /* (193) expr ::= expr IS NOT expr */
- { 185, -2 }, /* (194) expr ::= NOT expr */
- { 185, -2 }, /* (195) expr ::= BITNOT expr */
- { 185, -2 }, /* (196) expr ::= PLUS|MINUS expr */
- { 239, -1 }, /* (197) between_op ::= BETWEEN */
- { 239, -2 }, /* (198) between_op ::= NOT BETWEEN */
- { 185, -5 }, /* (199) expr ::= expr between_op expr AND expr */
- { 240, -1 }, /* (200) in_op ::= IN */
- { 240, -2 }, /* (201) in_op ::= NOT IN */
- { 185, -5 }, /* (202) expr ::= expr in_op LP exprlist RP */
- { 185, -3 }, /* (203) expr ::= LP select RP */
- { 185, -5 }, /* (204) expr ::= expr in_op LP select RP */
- { 185, -5 }, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */
- { 185, -4 }, /* (206) expr ::= EXISTS LP select RP */
- { 185, -5 }, /* (207) expr ::= CASE case_operand case_exprlist case_else END */
- { 243, -5 }, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- { 243, -4 }, /* (209) case_exprlist ::= WHEN expr THEN expr */
- { 244, -2 }, /* (210) case_else ::= ELSE expr */
- { 244, 0 }, /* (211) case_else ::= */
- { 242, -1 }, /* (212) case_operand ::= expr */
- { 242, 0 }, /* (213) case_operand ::= */
- { 229, 0 }, /* (214) exprlist ::= */
- { 220, -3 }, /* (215) nexprlist ::= nexprlist COMMA expr */
- { 220, -1 }, /* (216) nexprlist ::= expr */
- { 241, 0 }, /* (217) paren_exprlist ::= */
- { 241, -3 }, /* (218) paren_exprlist ::= LP exprlist RP */
- { 160, -12 }, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- { 245, -1 }, /* (220) uniqueflag ::= UNIQUE */
- { 245, 0 }, /* (221) uniqueflag ::= */
- { 189, 0 }, /* (222) eidlist_opt ::= */
- { 189, -3 }, /* (223) eidlist_opt ::= LP eidlist RP */
- { 199, -5 }, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */
- { 199, -3 }, /* (225) eidlist ::= nm collate sortorder */
- { 246, 0 }, /* (226) collate ::= */
- { 246, -2 }, /* (227) collate ::= COLLATE ID|STRING */
- { 160, -4 }, /* (228) cmd ::= DROP INDEX ifexists fullname */
- { 160, -1 }, /* (229) cmd ::= VACUUM */
- { 160, -2 }, /* (230) cmd ::= VACUUM nm */
- { 160, -3 }, /* (231) cmd ::= PRAGMA nm dbnm */
- { 160, -5 }, /* (232) cmd ::= PRAGMA nm dbnm EQ nmnum */
- { 160, -6 }, /* (233) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- { 160, -5 }, /* (234) cmd ::= PRAGMA nm dbnm EQ minus_num */
- { 160, -6 }, /* (235) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- { 180, -2 }, /* (236) plus_num ::= PLUS INTEGER|FLOAT */
- { 181, -2 }, /* (237) minus_num ::= MINUS INTEGER|FLOAT */
- { 160, -5 }, /* (238) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- { 248, -11 }, /* (239) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- { 250, -1 }, /* (240) trigger_time ::= BEFORE|AFTER */
- { 250, -2 }, /* (241) trigger_time ::= INSTEAD OF */
- { 250, 0 }, /* (242) trigger_time ::= */
- { 251, -1 }, /* (243) trigger_event ::= DELETE|INSERT */
- { 251, -1 }, /* (244) trigger_event ::= UPDATE */
- { 251, -3 }, /* (245) trigger_event ::= UPDATE OF idlist */
- { 253, 0 }, /* (246) when_clause ::= */
- { 253, -2 }, /* (247) when_clause ::= WHEN expr */
- { 249, -3 }, /* (248) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- { 249, -2 }, /* (249) trigger_cmd_list ::= trigger_cmd SEMI */
- { 255, -3 }, /* (250) trnm ::= nm DOT nm */
- { 256, -3 }, /* (251) tridxby ::= INDEXED BY nm */
- { 256, -2 }, /* (252) tridxby ::= NOT INDEXED */
- { 254, -8 }, /* (253) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
- { 254, -8 }, /* (254) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- { 254, -6 }, /* (255) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- { 254, -3 }, /* (256) trigger_cmd ::= scanpt select scanpt */
- { 185, -4 }, /* (257) expr ::= RAISE LP IGNORE RP */
- { 185, -6 }, /* (258) expr ::= RAISE LP raisetype COMMA nm RP */
- { 203, -1 }, /* (259) raisetype ::= ROLLBACK */
- { 203, -1 }, /* (260) raisetype ::= ABORT */
- { 203, -1 }, /* (261) raisetype ::= FAIL */
- { 160, -4 }, /* (262) cmd ::= DROP TRIGGER ifexists fullname */
- { 160, -6 }, /* (263) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- { 160, -3 }, /* (264) cmd ::= DETACH database_kw_opt expr */
- { 258, 0 }, /* (265) key_opt ::= */
- { 258, -2 }, /* (266) key_opt ::= KEY expr */
- { 160, -1 }, /* (267) cmd ::= REINDEX */
- { 160, -3 }, /* (268) cmd ::= REINDEX nm dbnm */
- { 160, -1 }, /* (269) cmd ::= ANALYZE */
- { 160, -3 }, /* (270) cmd ::= ANALYZE nm dbnm */
- { 160, -6 }, /* (271) cmd ::= ALTER TABLE fullname RENAME TO nm */
- { 160, -7 }, /* (272) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- { 259, -1 }, /* (273) add_column_fullname ::= fullname */
- { 160, -8 }, /* (274) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- { 160, -1 }, /* (275) cmd ::= create_vtab */
- { 160, -4 }, /* (276) cmd ::= create_vtab LP vtabarglist RP */
- { 261, -8 }, /* (277) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- { 263, 0 }, /* (278) vtabarg ::= */
- { 264, -1 }, /* (279) vtabargtoken ::= ANY */
- { 264, -3 }, /* (280) vtabargtoken ::= lp anylist RP */
- { 265, -1 }, /* (281) lp ::= LP */
- { 232, -2 }, /* (282) with ::= WITH wqlist */
- { 232, -3 }, /* (283) with ::= WITH RECURSIVE wqlist */
- { 208, -6 }, /* (284) wqlist ::= nm eidlist_opt AS LP select RP */
- { 208, -8 }, /* (285) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
- { 267, -1 }, /* (286) windowdefn_list ::= windowdefn */
- { 267, -3 }, /* (287) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- { 268, -3 }, /* (288) windowdefn ::= nm AS window */
- { 269, -5 }, /* (289) window ::= LP part_opt orderby_opt frame_opt RP */
- { 271, -3 }, /* (290) part_opt ::= PARTITION BY nexprlist */
- { 271, 0 }, /* (291) part_opt ::= */
- { 270, 0 }, /* (292) frame_opt ::= */
- { 270, -2 }, /* (293) frame_opt ::= range_or_rows frame_bound_s */
- { 270, -5 }, /* (294) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */
- { 273, -1 }, /* (295) range_or_rows ::= RANGE */
- { 273, -1 }, /* (296) range_or_rows ::= ROWS */
- { 275, -1 }, /* (297) frame_bound_s ::= frame_bound */
- { 275, -2 }, /* (298) frame_bound_s ::= UNBOUNDED PRECEDING */
- { 276, -1 }, /* (299) frame_bound_e ::= frame_bound */
- { 276, -2 }, /* (300) frame_bound_e ::= UNBOUNDED FOLLOWING */
- { 274, -2 }, /* (301) frame_bound ::= expr PRECEDING */
- { 274, -2 }, /* (302) frame_bound ::= CURRENT ROW */
- { 274, -2 }, /* (303) frame_bound ::= expr FOLLOWING */
- { 218, -2 }, /* (304) window_clause ::= WINDOW windowdefn_list */
- { 237, -3 }, /* (305) over_clause ::= filter_opt OVER window */
- { 237, -3 }, /* (306) over_clause ::= filter_opt OVER nm */
- { 272, 0 }, /* (307) filter_opt ::= */
- { 272, -5 }, /* (308) filter_opt ::= FILTER LP WHERE expr RP */
- { 155, -1 }, /* (309) input ::= cmdlist */
- { 156, -2 }, /* (310) cmdlist ::= cmdlist ecmd */
- { 156, -1 }, /* (311) cmdlist ::= ecmd */
- { 157, -1 }, /* (312) ecmd ::= SEMI */
- { 157, -2 }, /* (313) ecmd ::= cmdx SEMI */
- { 157, -2 }, /* (314) ecmd ::= explain cmdx */
- { 162, 0 }, /* (315) trans_opt ::= */
- { 162, -1 }, /* (316) trans_opt ::= TRANSACTION */
- { 162, -2 }, /* (317) trans_opt ::= TRANSACTION nm */
- { 164, -1 }, /* (318) savepoint_opt ::= SAVEPOINT */
- { 164, 0 }, /* (319) savepoint_opt ::= */
- { 160, -2 }, /* (320) cmd ::= create_table create_table_args */
- { 171, -4 }, /* (321) columnlist ::= columnlist COMMA columnname carglist */
- { 171, -2 }, /* (322) columnlist ::= columnname carglist */
- { 163, -1 }, /* (323) nm ::= ID|INDEXED */
- { 163, -1 }, /* (324) nm ::= STRING */
- { 163, -1 }, /* (325) nm ::= JOIN_KW */
- { 177, -1 }, /* (326) typetoken ::= typename */
- { 178, -1 }, /* (327) typename ::= ID|STRING */
- { 179, -1 }, /* (328) signed ::= plus_num */
- { 179, -1 }, /* (329) signed ::= minus_num */
- { 176, -2 }, /* (330) carglist ::= carglist ccons */
- { 176, 0 }, /* (331) carglist ::= */
- { 183, -2 }, /* (332) ccons ::= NULL onconf */
- { 172, -2 }, /* (333) conslist_opt ::= COMMA conslist */
- { 195, -3 }, /* (334) conslist ::= conslist tconscomma tcons */
- { 195, -1 }, /* (335) conslist ::= tcons */
- { 196, 0 }, /* (336) tconscomma ::= */
- { 200, -1 }, /* (337) defer_subclause_opt ::= defer_subclause */
- { 202, -1 }, /* (338) resolvetype ::= raisetype */
- { 206, -1 }, /* (339) selectnowith ::= oneselect */
- { 207, -1 }, /* (340) oneselect ::= values */
- { 221, -2 }, /* (341) sclp ::= selcollist COMMA */
- { 222, -1 }, /* (342) as ::= ID|STRING */
- { 185, -1 }, /* (343) expr ::= term */
- { 238, -1 }, /* (344) likeop ::= LIKE_KW|MATCH */
- { 229, -1 }, /* (345) exprlist ::= nexprlist */
- { 247, -1 }, /* (346) nmnum ::= plus_num */
- { 247, -1 }, /* (347) nmnum ::= nm */
- { 247, -1 }, /* (348) nmnum ::= ON */
- { 247, -1 }, /* (349) nmnum ::= DELETE */
- { 247, -1 }, /* (350) nmnum ::= DEFAULT */
- { 180, -1 }, /* (351) plus_num ::= INTEGER|FLOAT */
- { 252, 0 }, /* (352) foreach_clause ::= */
- { 252, -3 }, /* (353) foreach_clause ::= FOR EACH ROW */
- { 255, -1 }, /* (354) trnm ::= nm */
- { 256, 0 }, /* (355) tridxby ::= */
- { 257, -1 }, /* (356) database_kw_opt ::= DATABASE */
- { 257, 0 }, /* (357) database_kw_opt ::= */
- { 260, 0 }, /* (358) kwcolumn_opt ::= */
- { 260, -1 }, /* (359) kwcolumn_opt ::= COLUMNKW */
- { 262, -1 }, /* (360) vtabarglist ::= vtabarg */
- { 262, -3 }, /* (361) vtabarglist ::= vtabarglist COMMA vtabarg */
- { 263, -2 }, /* (362) vtabarg ::= vtabarg vtabargtoken */
- { 266, 0 }, /* (363) anylist ::= */
- { 266, -4 }, /* (364) anylist ::= anylist LP anylist RP */
- { 266, -2 }, /* (365) anylist ::= anylist ANY */
- { 232, 0 }, /* (366) with ::= */
+/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
+** of that rule */
+static const YYCODETYPE yyRuleInfoLhs[] = {
+ 189, /* (0) explain ::= EXPLAIN */
+ 189, /* (1) explain ::= EXPLAIN QUERY PLAN */
+ 188, /* (2) cmdx ::= cmd */
+ 190, /* (3) cmd ::= BEGIN transtype trans_opt */
+ 191, /* (4) transtype ::= */
+ 191, /* (5) transtype ::= DEFERRED */
+ 191, /* (6) transtype ::= IMMEDIATE */
+ 191, /* (7) transtype ::= EXCLUSIVE */
+ 190, /* (8) cmd ::= COMMIT|END trans_opt */
+ 190, /* (9) cmd ::= ROLLBACK trans_opt */
+ 190, /* (10) cmd ::= SAVEPOINT nm */
+ 190, /* (11) cmd ::= RELEASE savepoint_opt nm */
+ 190, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
+ 195, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
+ 197, /* (14) createkw ::= CREATE */
+ 199, /* (15) ifnotexists ::= */
+ 199, /* (16) ifnotexists ::= IF NOT EXISTS */
+ 198, /* (17) temp ::= TEMP */
+ 198, /* (18) temp ::= */
+ 196, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */
+ 196, /* (20) create_table_args ::= AS select */
+ 203, /* (21) table_option_set ::= */
+ 203, /* (22) table_option_set ::= table_option_set COMMA table_option */
+ 205, /* (23) table_option ::= WITHOUT nm */
+ 205, /* (24) table_option ::= nm */
+ 206, /* (25) columnname ::= nm typetoken */
+ 208, /* (26) typetoken ::= */
+ 208, /* (27) typetoken ::= typename LP signed RP */
+ 208, /* (28) typetoken ::= typename LP signed COMMA signed RP */
+ 209, /* (29) typename ::= typename ID|STRING */
+ 213, /* (30) scanpt ::= */
+ 214, /* (31) scantok ::= */
+ 215, /* (32) ccons ::= CONSTRAINT nm */
+ 215, /* (33) ccons ::= DEFAULT scantok term */
+ 215, /* (34) ccons ::= DEFAULT LP expr RP */
+ 215, /* (35) ccons ::= DEFAULT PLUS scantok term */
+ 215, /* (36) ccons ::= DEFAULT MINUS scantok term */
+ 215, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */
+ 215, /* (38) ccons ::= NOT NULL onconf */
+ 215, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */
+ 215, /* (40) ccons ::= UNIQUE onconf */
+ 215, /* (41) ccons ::= CHECK LP expr RP */
+ 215, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */
+ 215, /* (43) ccons ::= defer_subclause */
+ 215, /* (44) ccons ::= COLLATE ID|STRING */
+ 224, /* (45) generated ::= LP expr RP */
+ 224, /* (46) generated ::= LP expr RP ID */
+ 220, /* (47) autoinc ::= */
+ 220, /* (48) autoinc ::= AUTOINCR */
+ 222, /* (49) refargs ::= */
+ 222, /* (50) refargs ::= refargs refarg */
+ 225, /* (51) refarg ::= MATCH nm */
+ 225, /* (52) refarg ::= ON INSERT refact */
+ 225, /* (53) refarg ::= ON DELETE refact */
+ 225, /* (54) refarg ::= ON UPDATE refact */
+ 226, /* (55) refact ::= SET NULL */
+ 226, /* (56) refact ::= SET DEFAULT */
+ 226, /* (57) refact ::= CASCADE */
+ 226, /* (58) refact ::= RESTRICT */
+ 226, /* (59) refact ::= NO ACTION */
+ 223, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+ 223, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ 227, /* (62) init_deferred_pred_opt ::= */
+ 227, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ 227, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+ 202, /* (65) conslist_opt ::= */
+ 229, /* (66) tconscomma ::= COMMA */
+ 230, /* (67) tcons ::= CONSTRAINT nm */
+ 230, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
+ 230, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */
+ 230, /* (70) tcons ::= CHECK LP expr RP onconf */
+ 230, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
+ 233, /* (72) defer_subclause_opt ::= */
+ 218, /* (73) onconf ::= */
+ 218, /* (74) onconf ::= ON CONFLICT resolvetype */
+ 234, /* (75) orconf ::= */
+ 234, /* (76) orconf ::= OR resolvetype */
+ 235, /* (77) resolvetype ::= IGNORE */
+ 235, /* (78) resolvetype ::= REPLACE */
+ 190, /* (79) cmd ::= DROP TABLE ifexists fullname */
+ 237, /* (80) ifexists ::= IF EXISTS */
+ 237, /* (81) ifexists ::= */
+ 190, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
+ 190, /* (83) cmd ::= DROP VIEW ifexists fullname */
+ 190, /* (84) cmd ::= select */
+ 204, /* (85) select ::= WITH wqlist selectnowith */
+ 204, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */
+ 204, /* (87) select ::= selectnowith */
+ 239, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */
+ 242, /* (89) multiselect_op ::= UNION */
+ 242, /* (90) multiselect_op ::= UNION ALL */
+ 242, /* (91) multiselect_op ::= EXCEPT|INTERSECT */
+ 240, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+ 240, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
+ 252, /* (94) values ::= VALUES LP nexprlist RP */
+ 252, /* (95) values ::= values COMMA LP nexprlist RP */
+ 243, /* (96) distinct ::= DISTINCT */
+ 243, /* (97) distinct ::= ALL */
+ 243, /* (98) distinct ::= */
+ 254, /* (99) sclp ::= */
+ 244, /* (100) selcollist ::= sclp scanpt expr scanpt as */
+ 244, /* (101) selcollist ::= sclp scanpt STAR */
+ 244, /* (102) selcollist ::= sclp scanpt nm DOT STAR */
+ 255, /* (103) as ::= AS nm */
+ 255, /* (104) as ::= */
+ 245, /* (105) from ::= */
+ 245, /* (106) from ::= FROM seltablist */
+ 257, /* (107) stl_prefix ::= seltablist joinop */
+ 257, /* (108) stl_prefix ::= */
+ 256, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */
+ 256, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
+ 256, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
+ 256, /* (112) seltablist ::= stl_prefix LP select RP as on_using */
+ 256, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */
+ 200, /* (114) dbnm ::= */
+ 200, /* (115) dbnm ::= DOT nm */
+ 238, /* (116) fullname ::= nm */
+ 238, /* (117) fullname ::= nm DOT nm */
+ 262, /* (118) xfullname ::= nm */
+ 262, /* (119) xfullname ::= nm DOT nm */
+ 262, /* (120) xfullname ::= nm DOT nm AS nm */
+ 262, /* (121) xfullname ::= nm AS nm */
+ 258, /* (122) joinop ::= COMMA|JOIN */
+ 258, /* (123) joinop ::= JOIN_KW JOIN */
+ 258, /* (124) joinop ::= JOIN_KW nm JOIN */
+ 258, /* (125) joinop ::= JOIN_KW nm nm JOIN */
+ 259, /* (126) on_using ::= ON expr */
+ 259, /* (127) on_using ::= USING LP idlist RP */
+ 259, /* (128) on_using ::= */
+ 264, /* (129) indexed_opt ::= */
+ 260, /* (130) indexed_by ::= INDEXED BY nm */
+ 260, /* (131) indexed_by ::= NOT INDEXED */
+ 249, /* (132) orderby_opt ::= */
+ 249, /* (133) orderby_opt ::= ORDER BY sortlist */
+ 231, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */
+ 231, /* (135) sortlist ::= expr sortorder nulls */
+ 219, /* (136) sortorder ::= ASC */
+ 219, /* (137) sortorder ::= DESC */
+ 219, /* (138) sortorder ::= */
+ 265, /* (139) nulls ::= NULLS FIRST */
+ 265, /* (140) nulls ::= NULLS LAST */
+ 265, /* (141) nulls ::= */
+ 247, /* (142) groupby_opt ::= */
+ 247, /* (143) groupby_opt ::= GROUP BY nexprlist */
+ 248, /* (144) having_opt ::= */
+ 248, /* (145) having_opt ::= HAVING expr */
+ 250, /* (146) limit_opt ::= */
+ 250, /* (147) limit_opt ::= LIMIT expr */
+ 250, /* (148) limit_opt ::= LIMIT expr OFFSET expr */
+ 250, /* (149) limit_opt ::= LIMIT expr COMMA expr */
+ 190, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
+ 246, /* (151) where_opt ::= */
+ 246, /* (152) where_opt ::= WHERE expr */
+ 267, /* (153) where_opt_ret ::= */
+ 267, /* (154) where_opt_ret ::= WHERE expr */
+ 267, /* (155) where_opt_ret ::= RETURNING selcollist */
+ 267, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */
+ 190, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
+ 268, /* (158) setlist ::= setlist COMMA nm EQ expr */
+ 268, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ 268, /* (160) setlist ::= nm EQ expr */
+ 268, /* (161) setlist ::= LP idlist RP EQ expr */
+ 190, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ 190, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
+ 271, /* (164) upsert ::= */
+ 271, /* (165) upsert ::= RETURNING selcollist */
+ 271, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
+ 271, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
+ 271, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */
+ 271, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
+ 272, /* (170) returning ::= RETURNING selcollist */
+ 269, /* (171) insert_cmd ::= INSERT orconf */
+ 269, /* (172) insert_cmd ::= REPLACE */
+ 270, /* (173) idlist_opt ::= */
+ 270, /* (174) idlist_opt ::= LP idlist RP */
+ 263, /* (175) idlist ::= idlist COMMA nm */
+ 263, /* (176) idlist ::= nm */
+ 217, /* (177) expr ::= LP expr RP */
+ 217, /* (178) expr ::= ID|INDEXED|JOIN_KW */
+ 217, /* (179) expr ::= nm DOT nm */
+ 217, /* (180) expr ::= nm DOT nm DOT nm */
+ 216, /* (181) term ::= NULL|FLOAT|BLOB */
+ 216, /* (182) term ::= STRING */
+ 216, /* (183) term ::= INTEGER */
+ 217, /* (184) expr ::= VARIABLE */
+ 217, /* (185) expr ::= expr COLLATE ID|STRING */
+ 217, /* (186) expr ::= CAST LP expr AS typetoken RP */
+ 217, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
+ 217, /* (188) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
+ 217, /* (189) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ 217, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ 217, /* (191) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
+ 217, /* (192) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ 216, /* (193) term ::= CTIME_KW */
+ 217, /* (194) expr ::= LP nexprlist COMMA expr RP */
+ 217, /* (195) expr ::= expr AND expr */
+ 217, /* (196) expr ::= expr OR expr */
+ 217, /* (197) expr ::= expr LT|GT|GE|LE expr */
+ 217, /* (198) expr ::= expr EQ|NE expr */
+ 217, /* (199) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ 217, /* (200) expr ::= expr PLUS|MINUS expr */
+ 217, /* (201) expr ::= expr STAR|SLASH|REM expr */
+ 217, /* (202) expr ::= expr CONCAT expr */
+ 274, /* (203) likeop ::= NOT LIKE_KW|MATCH */
+ 217, /* (204) expr ::= expr likeop expr */
+ 217, /* (205) expr ::= expr likeop expr ESCAPE expr */
+ 217, /* (206) expr ::= expr ISNULL|NOTNULL */
+ 217, /* (207) expr ::= expr NOT NULL */
+ 217, /* (208) expr ::= expr IS expr */
+ 217, /* (209) expr ::= expr IS NOT expr */
+ 217, /* (210) expr ::= expr IS NOT DISTINCT FROM expr */
+ 217, /* (211) expr ::= expr IS DISTINCT FROM expr */
+ 217, /* (212) expr ::= NOT expr */
+ 217, /* (213) expr ::= BITNOT expr */
+ 217, /* (214) expr ::= PLUS|MINUS expr */
+ 217, /* (215) expr ::= expr PTR expr */
+ 275, /* (216) between_op ::= BETWEEN */
+ 275, /* (217) between_op ::= NOT BETWEEN */
+ 217, /* (218) expr ::= expr between_op expr AND expr */
+ 276, /* (219) in_op ::= IN */
+ 276, /* (220) in_op ::= NOT IN */
+ 217, /* (221) expr ::= expr in_op LP exprlist RP */
+ 217, /* (222) expr ::= LP select RP */
+ 217, /* (223) expr ::= expr in_op LP select RP */
+ 217, /* (224) expr ::= expr in_op nm dbnm paren_exprlist */
+ 217, /* (225) expr ::= EXISTS LP select RP */
+ 217, /* (226) expr ::= CASE case_operand case_exprlist case_else END */
+ 279, /* (227) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ 279, /* (228) case_exprlist ::= WHEN expr THEN expr */
+ 280, /* (229) case_else ::= ELSE expr */
+ 280, /* (230) case_else ::= */
+ 278, /* (231) case_operand ::= */
+ 261, /* (232) exprlist ::= */
+ 253, /* (233) nexprlist ::= nexprlist COMMA expr */
+ 253, /* (234) nexprlist ::= expr */
+ 277, /* (235) paren_exprlist ::= */
+ 277, /* (236) paren_exprlist ::= LP exprlist RP */
+ 190, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ 281, /* (238) uniqueflag ::= UNIQUE */
+ 281, /* (239) uniqueflag ::= */
+ 221, /* (240) eidlist_opt ::= */
+ 221, /* (241) eidlist_opt ::= LP eidlist RP */
+ 232, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */
+ 232, /* (243) eidlist ::= nm collate sortorder */
+ 282, /* (244) collate ::= */
+ 282, /* (245) collate ::= COLLATE ID|STRING */
+ 190, /* (246) cmd ::= DROP INDEX ifexists fullname */
+ 190, /* (247) cmd ::= VACUUM vinto */
+ 190, /* (248) cmd ::= VACUUM nm vinto */
+ 283, /* (249) vinto ::= INTO expr */
+ 283, /* (250) vinto ::= */
+ 190, /* (251) cmd ::= PRAGMA nm dbnm */
+ 190, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ 190, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ 190, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ 190, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ 211, /* (256) plus_num ::= PLUS INTEGER|FLOAT */
+ 212, /* (257) minus_num ::= MINUS INTEGER|FLOAT */
+ 190, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ 285, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ 287, /* (260) trigger_time ::= BEFORE|AFTER */
+ 287, /* (261) trigger_time ::= INSTEAD OF */
+ 287, /* (262) trigger_time ::= */
+ 288, /* (263) trigger_event ::= DELETE|INSERT */
+ 288, /* (264) trigger_event ::= UPDATE */
+ 288, /* (265) trigger_event ::= UPDATE OF idlist */
+ 290, /* (266) when_clause ::= */
+ 290, /* (267) when_clause ::= WHEN expr */
+ 286, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ 286, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */
+ 292, /* (270) trnm ::= nm DOT nm */
+ 293, /* (271) tridxby ::= INDEXED BY nm */
+ 293, /* (272) tridxby ::= NOT INDEXED */
+ 291, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+ 291, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ 291, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ 291, /* (276) trigger_cmd ::= scanpt select scanpt */
+ 217, /* (277) expr ::= RAISE LP IGNORE RP */
+ 217, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */
+ 236, /* (279) raisetype ::= ROLLBACK */
+ 236, /* (280) raisetype ::= ABORT */
+ 236, /* (281) raisetype ::= FAIL */
+ 190, /* (282) cmd ::= DROP TRIGGER ifexists fullname */
+ 190, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ 190, /* (284) cmd ::= DETACH database_kw_opt expr */
+ 295, /* (285) key_opt ::= */
+ 295, /* (286) key_opt ::= KEY expr */
+ 190, /* (287) cmd ::= REINDEX */
+ 190, /* (288) cmd ::= REINDEX nm dbnm */
+ 190, /* (289) cmd ::= ANALYZE */
+ 190, /* (290) cmd ::= ANALYZE nm dbnm */
+ 190, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ 190, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ 190, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ 296, /* (294) add_column_fullname ::= fullname */
+ 190, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ 190, /* (296) cmd ::= create_vtab */
+ 190, /* (297) cmd ::= create_vtab LP vtabarglist RP */
+ 298, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 300, /* (299) vtabarg ::= */
+ 301, /* (300) vtabargtoken ::= ANY */
+ 301, /* (301) vtabargtoken ::= lp anylist RP */
+ 302, /* (302) lp ::= LP */
+ 266, /* (303) with ::= WITH wqlist */
+ 266, /* (304) with ::= WITH RECURSIVE wqlist */
+ 305, /* (305) wqas ::= AS */
+ 305, /* (306) wqas ::= AS MATERIALIZED */
+ 305, /* (307) wqas ::= AS NOT MATERIALIZED */
+ 304, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */
+ 241, /* (309) wqlist ::= wqitem */
+ 241, /* (310) wqlist ::= wqlist COMMA wqitem */
+ 306, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ 307, /* (312) windowdefn ::= nm AS LP window RP */
+ 308, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ 308, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ 308, /* (315) window ::= ORDER BY sortlist frame_opt */
+ 308, /* (316) window ::= nm ORDER BY sortlist frame_opt */
+ 308, /* (317) window ::= nm frame_opt */
+ 309, /* (318) frame_opt ::= */
+ 309, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ 309, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ 313, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */
+ 315, /* (322) frame_bound_s ::= frame_bound */
+ 315, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */
+ 316, /* (324) frame_bound_e ::= frame_bound */
+ 316, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ 314, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */
+ 314, /* (327) frame_bound ::= CURRENT ROW */
+ 317, /* (328) frame_exclude_opt ::= */
+ 317, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ 318, /* (330) frame_exclude ::= NO OTHERS */
+ 318, /* (331) frame_exclude ::= CURRENT ROW */
+ 318, /* (332) frame_exclude ::= GROUP|TIES */
+ 251, /* (333) window_clause ::= WINDOW windowdefn_list */
+ 273, /* (334) filter_over ::= filter_clause over_clause */
+ 273, /* (335) filter_over ::= over_clause */
+ 273, /* (336) filter_over ::= filter_clause */
+ 312, /* (337) over_clause ::= OVER LP window RP */
+ 312, /* (338) over_clause ::= OVER nm */
+ 311, /* (339) filter_clause ::= FILTER LP WHERE expr RP */
+ 185, /* (340) input ::= cmdlist */
+ 186, /* (341) cmdlist ::= cmdlist ecmd */
+ 186, /* (342) cmdlist ::= ecmd */
+ 187, /* (343) ecmd ::= SEMI */
+ 187, /* (344) ecmd ::= cmdx SEMI */
+ 187, /* (345) ecmd ::= explain cmdx SEMI */
+ 192, /* (346) trans_opt ::= */
+ 192, /* (347) trans_opt ::= TRANSACTION */
+ 192, /* (348) trans_opt ::= TRANSACTION nm */
+ 194, /* (349) savepoint_opt ::= SAVEPOINT */
+ 194, /* (350) savepoint_opt ::= */
+ 190, /* (351) cmd ::= create_table create_table_args */
+ 203, /* (352) table_option_set ::= table_option */
+ 201, /* (353) columnlist ::= columnlist COMMA columnname carglist */
+ 201, /* (354) columnlist ::= columnname carglist */
+ 193, /* (355) nm ::= ID|INDEXED|JOIN_KW */
+ 193, /* (356) nm ::= STRING */
+ 208, /* (357) typetoken ::= typename */
+ 209, /* (358) typename ::= ID|STRING */
+ 210, /* (359) signed ::= plus_num */
+ 210, /* (360) signed ::= minus_num */
+ 207, /* (361) carglist ::= carglist ccons */
+ 207, /* (362) carglist ::= */
+ 215, /* (363) ccons ::= NULL onconf */
+ 215, /* (364) ccons ::= GENERATED ALWAYS AS generated */
+ 215, /* (365) ccons ::= AS generated */
+ 202, /* (366) conslist_opt ::= COMMA conslist */
+ 228, /* (367) conslist ::= conslist tconscomma tcons */
+ 228, /* (368) conslist ::= tcons */
+ 229, /* (369) tconscomma ::= */
+ 233, /* (370) defer_subclause_opt ::= defer_subclause */
+ 235, /* (371) resolvetype ::= raisetype */
+ 239, /* (372) selectnowith ::= oneselect */
+ 240, /* (373) oneselect ::= values */
+ 254, /* (374) sclp ::= selcollist COMMA */
+ 255, /* (375) as ::= ID|STRING */
+ 264, /* (376) indexed_opt ::= indexed_by */
+ 272, /* (377) returning ::= */
+ 217, /* (378) expr ::= term */
+ 274, /* (379) likeop ::= LIKE_KW|MATCH */
+ 278, /* (380) case_operand ::= expr */
+ 261, /* (381) exprlist ::= nexprlist */
+ 284, /* (382) nmnum ::= plus_num */
+ 284, /* (383) nmnum ::= nm */
+ 284, /* (384) nmnum ::= ON */
+ 284, /* (385) nmnum ::= DELETE */
+ 284, /* (386) nmnum ::= DEFAULT */
+ 211, /* (387) plus_num ::= INTEGER|FLOAT */
+ 289, /* (388) foreach_clause ::= */
+ 289, /* (389) foreach_clause ::= FOR EACH ROW */
+ 292, /* (390) trnm ::= nm */
+ 293, /* (391) tridxby ::= */
+ 294, /* (392) database_kw_opt ::= DATABASE */
+ 294, /* (393) database_kw_opt ::= */
+ 297, /* (394) kwcolumn_opt ::= */
+ 297, /* (395) kwcolumn_opt ::= COLUMNKW */
+ 299, /* (396) vtabarglist ::= vtabarg */
+ 299, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */
+ 300, /* (398) vtabarg ::= vtabarg vtabargtoken */
+ 303, /* (399) anylist ::= */
+ 303, /* (400) anylist ::= anylist LP anylist RP */
+ 303, /* (401) anylist ::= anylist ANY */
+ 266, /* (402) with ::= */
+ 306, /* (403) windowdefn_list ::= windowdefn */
+ 308, /* (404) window ::= frame_opt */
+};
+
+/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
+** of symbols on the right-hand side of that rule. */
+static const signed char yyRuleInfoNRhs[] = {
+ -1, /* (0) explain ::= EXPLAIN */
+ -3, /* (1) explain ::= EXPLAIN QUERY PLAN */
+ -1, /* (2) cmdx ::= cmd */
+ -3, /* (3) cmd ::= BEGIN transtype trans_opt */
+ 0, /* (4) transtype ::= */
+ -1, /* (5) transtype ::= DEFERRED */
+ -1, /* (6) transtype ::= IMMEDIATE */
+ -1, /* (7) transtype ::= EXCLUSIVE */
+ -2, /* (8) cmd ::= COMMIT|END trans_opt */
+ -2, /* (9) cmd ::= ROLLBACK trans_opt */
+ -2, /* (10) cmd ::= SAVEPOINT nm */
+ -3, /* (11) cmd ::= RELEASE savepoint_opt nm */
+ -5, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
+ -6, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
+ -1, /* (14) createkw ::= CREATE */
+ 0, /* (15) ifnotexists ::= */
+ -3, /* (16) ifnotexists ::= IF NOT EXISTS */
+ -1, /* (17) temp ::= TEMP */
+ 0, /* (18) temp ::= */
+ -5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */
+ -2, /* (20) create_table_args ::= AS select */
+ 0, /* (21) table_option_set ::= */
+ -3, /* (22) table_option_set ::= table_option_set COMMA table_option */
+ -2, /* (23) table_option ::= WITHOUT nm */
+ -1, /* (24) table_option ::= nm */
+ -2, /* (25) columnname ::= nm typetoken */
+ 0, /* (26) typetoken ::= */
+ -4, /* (27) typetoken ::= typename LP signed RP */
+ -6, /* (28) typetoken ::= typename LP signed COMMA signed RP */
+ -2, /* (29) typename ::= typename ID|STRING */
+ 0, /* (30) scanpt ::= */
+ 0, /* (31) scantok ::= */
+ -2, /* (32) ccons ::= CONSTRAINT nm */
+ -3, /* (33) ccons ::= DEFAULT scantok term */
+ -4, /* (34) ccons ::= DEFAULT LP expr RP */
+ -4, /* (35) ccons ::= DEFAULT PLUS scantok term */
+ -4, /* (36) ccons ::= DEFAULT MINUS scantok term */
+ -3, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */
+ -3, /* (38) ccons ::= NOT NULL onconf */
+ -5, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */
+ -2, /* (40) ccons ::= UNIQUE onconf */
+ -4, /* (41) ccons ::= CHECK LP expr RP */
+ -4, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */
+ -1, /* (43) ccons ::= defer_subclause */
+ -2, /* (44) ccons ::= COLLATE ID|STRING */
+ -3, /* (45) generated ::= LP expr RP */
+ -4, /* (46) generated ::= LP expr RP ID */
+ 0, /* (47) autoinc ::= */
+ -1, /* (48) autoinc ::= AUTOINCR */
+ 0, /* (49) refargs ::= */
+ -2, /* (50) refargs ::= refargs refarg */
+ -2, /* (51) refarg ::= MATCH nm */
+ -3, /* (52) refarg ::= ON INSERT refact */
+ -3, /* (53) refarg ::= ON DELETE refact */
+ -3, /* (54) refarg ::= ON UPDATE refact */
+ -2, /* (55) refact ::= SET NULL */
+ -2, /* (56) refact ::= SET DEFAULT */
+ -1, /* (57) refact ::= CASCADE */
+ -1, /* (58) refact ::= RESTRICT */
+ -2, /* (59) refact ::= NO ACTION */
+ -3, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+ -2, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ 0, /* (62) init_deferred_pred_opt ::= */
+ -2, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ -2, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+ 0, /* (65) conslist_opt ::= */
+ -1, /* (66) tconscomma ::= COMMA */
+ -2, /* (67) tcons ::= CONSTRAINT nm */
+ -7, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
+ -5, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */
+ -5, /* (70) tcons ::= CHECK LP expr RP onconf */
+ -10, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
+ 0, /* (72) defer_subclause_opt ::= */
+ 0, /* (73) onconf ::= */
+ -3, /* (74) onconf ::= ON CONFLICT resolvetype */
+ 0, /* (75) orconf ::= */
+ -2, /* (76) orconf ::= OR resolvetype */
+ -1, /* (77) resolvetype ::= IGNORE */
+ -1, /* (78) resolvetype ::= REPLACE */
+ -4, /* (79) cmd ::= DROP TABLE ifexists fullname */
+ -2, /* (80) ifexists ::= IF EXISTS */
+ 0, /* (81) ifexists ::= */
+ -9, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
+ -4, /* (83) cmd ::= DROP VIEW ifexists fullname */
+ -1, /* (84) cmd ::= select */
+ -3, /* (85) select ::= WITH wqlist selectnowith */
+ -4, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */
+ -1, /* (87) select ::= selectnowith */
+ -3, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */
+ -1, /* (89) multiselect_op ::= UNION */
+ -2, /* (90) multiselect_op ::= UNION ALL */
+ -1, /* (91) multiselect_op ::= EXCEPT|INTERSECT */
+ -9, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+ -10, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
+ -4, /* (94) values ::= VALUES LP nexprlist RP */
+ -5, /* (95) values ::= values COMMA LP nexprlist RP */
+ -1, /* (96) distinct ::= DISTINCT */
+ -1, /* (97) distinct ::= ALL */
+ 0, /* (98) distinct ::= */
+ 0, /* (99) sclp ::= */
+ -5, /* (100) selcollist ::= sclp scanpt expr scanpt as */
+ -3, /* (101) selcollist ::= sclp scanpt STAR */
+ -5, /* (102) selcollist ::= sclp scanpt nm DOT STAR */
+ -2, /* (103) as ::= AS nm */
+ 0, /* (104) as ::= */
+ 0, /* (105) from ::= */
+ -2, /* (106) from ::= FROM seltablist */
+ -2, /* (107) stl_prefix ::= seltablist joinop */
+ 0, /* (108) stl_prefix ::= */
+ -5, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */
+ -6, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
+ -8, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
+ -6, /* (112) seltablist ::= stl_prefix LP select RP as on_using */
+ -6, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */
+ 0, /* (114) dbnm ::= */
+ -2, /* (115) dbnm ::= DOT nm */
+ -1, /* (116) fullname ::= nm */
+ -3, /* (117) fullname ::= nm DOT nm */
+ -1, /* (118) xfullname ::= nm */
+ -3, /* (119) xfullname ::= nm DOT nm */
+ -5, /* (120) xfullname ::= nm DOT nm AS nm */
+ -3, /* (121) xfullname ::= nm AS nm */
+ -1, /* (122) joinop ::= COMMA|JOIN */
+ -2, /* (123) joinop ::= JOIN_KW JOIN */
+ -3, /* (124) joinop ::= JOIN_KW nm JOIN */
+ -4, /* (125) joinop ::= JOIN_KW nm nm JOIN */
+ -2, /* (126) on_using ::= ON expr */
+ -4, /* (127) on_using ::= USING LP idlist RP */
+ 0, /* (128) on_using ::= */
+ 0, /* (129) indexed_opt ::= */
+ -3, /* (130) indexed_by ::= INDEXED BY nm */
+ -2, /* (131) indexed_by ::= NOT INDEXED */
+ 0, /* (132) orderby_opt ::= */
+ -3, /* (133) orderby_opt ::= ORDER BY sortlist */
+ -5, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */
+ -3, /* (135) sortlist ::= expr sortorder nulls */
+ -1, /* (136) sortorder ::= ASC */
+ -1, /* (137) sortorder ::= DESC */
+ 0, /* (138) sortorder ::= */
+ -2, /* (139) nulls ::= NULLS FIRST */
+ -2, /* (140) nulls ::= NULLS LAST */
+ 0, /* (141) nulls ::= */
+ 0, /* (142) groupby_opt ::= */
+ -3, /* (143) groupby_opt ::= GROUP BY nexprlist */
+ 0, /* (144) having_opt ::= */
+ -2, /* (145) having_opt ::= HAVING expr */
+ 0, /* (146) limit_opt ::= */
+ -2, /* (147) limit_opt ::= LIMIT expr */
+ -4, /* (148) limit_opt ::= LIMIT expr OFFSET expr */
+ -4, /* (149) limit_opt ::= LIMIT expr COMMA expr */
+ -6, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
+ 0, /* (151) where_opt ::= */
+ -2, /* (152) where_opt ::= WHERE expr */
+ 0, /* (153) where_opt_ret ::= */
+ -2, /* (154) where_opt_ret ::= WHERE expr */
+ -2, /* (155) where_opt_ret ::= RETURNING selcollist */
+ -4, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */
+ -9, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
+ -5, /* (158) setlist ::= setlist COMMA nm EQ expr */
+ -7, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ -3, /* (160) setlist ::= nm EQ expr */
+ -5, /* (161) setlist ::= LP idlist RP EQ expr */
+ -7, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ -8, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
+ 0, /* (164) upsert ::= */
+ -2, /* (165) upsert ::= RETURNING selcollist */
+ -12, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
+ -9, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
+ -5, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */
+ -8, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
+ -2, /* (170) returning ::= RETURNING selcollist */
+ -2, /* (171) insert_cmd ::= INSERT orconf */
+ -1, /* (172) insert_cmd ::= REPLACE */
+ 0, /* (173) idlist_opt ::= */
+ -3, /* (174) idlist_opt ::= LP idlist RP */
+ -3, /* (175) idlist ::= idlist COMMA nm */
+ -1, /* (176) idlist ::= nm */
+ -3, /* (177) expr ::= LP expr RP */
+ -1, /* (178) expr ::= ID|INDEXED|JOIN_KW */
+ -3, /* (179) expr ::= nm DOT nm */
+ -5, /* (180) expr ::= nm DOT nm DOT nm */
+ -1, /* (181) term ::= NULL|FLOAT|BLOB */
+ -1, /* (182) term ::= STRING */
+ -1, /* (183) term ::= INTEGER */
+ -1, /* (184) expr ::= VARIABLE */
+ -3, /* (185) expr ::= expr COLLATE ID|STRING */
+ -6, /* (186) expr ::= CAST LP expr AS typetoken RP */
+ -5, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
+ -8, /* (188) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
+ -4, /* (189) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ -6, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ -9, /* (191) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
+ -5, /* (192) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ -1, /* (193) term ::= CTIME_KW */
+ -5, /* (194) expr ::= LP nexprlist COMMA expr RP */
+ -3, /* (195) expr ::= expr AND expr */
+ -3, /* (196) expr ::= expr OR expr */
+ -3, /* (197) expr ::= expr LT|GT|GE|LE expr */
+ -3, /* (198) expr ::= expr EQ|NE expr */
+ -3, /* (199) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ -3, /* (200) expr ::= expr PLUS|MINUS expr */
+ -3, /* (201) expr ::= expr STAR|SLASH|REM expr */
+ -3, /* (202) expr ::= expr CONCAT expr */
+ -2, /* (203) likeop ::= NOT LIKE_KW|MATCH */
+ -3, /* (204) expr ::= expr likeop expr */
+ -5, /* (205) expr ::= expr likeop expr ESCAPE expr */
+ -2, /* (206) expr ::= expr ISNULL|NOTNULL */
+ -3, /* (207) expr ::= expr NOT NULL */
+ -3, /* (208) expr ::= expr IS expr */
+ -4, /* (209) expr ::= expr IS NOT expr */
+ -6, /* (210) expr ::= expr IS NOT DISTINCT FROM expr */
+ -5, /* (211) expr ::= expr IS DISTINCT FROM expr */
+ -2, /* (212) expr ::= NOT expr */
+ -2, /* (213) expr ::= BITNOT expr */
+ -2, /* (214) expr ::= PLUS|MINUS expr */
+ -3, /* (215) expr ::= expr PTR expr */
+ -1, /* (216) between_op ::= BETWEEN */
+ -2, /* (217) between_op ::= NOT BETWEEN */
+ -5, /* (218) expr ::= expr between_op expr AND expr */
+ -1, /* (219) in_op ::= IN */
+ -2, /* (220) in_op ::= NOT IN */
+ -5, /* (221) expr ::= expr in_op LP exprlist RP */
+ -3, /* (222) expr ::= LP select RP */
+ -5, /* (223) expr ::= expr in_op LP select RP */
+ -5, /* (224) expr ::= expr in_op nm dbnm paren_exprlist */
+ -4, /* (225) expr ::= EXISTS LP select RP */
+ -5, /* (226) expr ::= CASE case_operand case_exprlist case_else END */
+ -5, /* (227) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ -4, /* (228) case_exprlist ::= WHEN expr THEN expr */
+ -2, /* (229) case_else ::= ELSE expr */
+ 0, /* (230) case_else ::= */
+ 0, /* (231) case_operand ::= */
+ 0, /* (232) exprlist ::= */
+ -3, /* (233) nexprlist ::= nexprlist COMMA expr */
+ -1, /* (234) nexprlist ::= expr */
+ 0, /* (235) paren_exprlist ::= */
+ -3, /* (236) paren_exprlist ::= LP exprlist RP */
+ -12, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ -1, /* (238) uniqueflag ::= UNIQUE */
+ 0, /* (239) uniqueflag ::= */
+ 0, /* (240) eidlist_opt ::= */
+ -3, /* (241) eidlist_opt ::= LP eidlist RP */
+ -5, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */
+ -3, /* (243) eidlist ::= nm collate sortorder */
+ 0, /* (244) collate ::= */
+ -2, /* (245) collate ::= COLLATE ID|STRING */
+ -4, /* (246) cmd ::= DROP INDEX ifexists fullname */
+ -2, /* (247) cmd ::= VACUUM vinto */
+ -3, /* (248) cmd ::= VACUUM nm vinto */
+ -2, /* (249) vinto ::= INTO expr */
+ 0, /* (250) vinto ::= */
+ -3, /* (251) cmd ::= PRAGMA nm dbnm */
+ -5, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ -6, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ -5, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ -6, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ -2, /* (256) plus_num ::= PLUS INTEGER|FLOAT */
+ -2, /* (257) minus_num ::= MINUS INTEGER|FLOAT */
+ -5, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ -11, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ -1, /* (260) trigger_time ::= BEFORE|AFTER */
+ -2, /* (261) trigger_time ::= INSTEAD OF */
+ 0, /* (262) trigger_time ::= */
+ -1, /* (263) trigger_event ::= DELETE|INSERT */
+ -1, /* (264) trigger_event ::= UPDATE */
+ -3, /* (265) trigger_event ::= UPDATE OF idlist */
+ 0, /* (266) when_clause ::= */
+ -2, /* (267) when_clause ::= WHEN expr */
+ -3, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ -2, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */
+ -3, /* (270) trnm ::= nm DOT nm */
+ -3, /* (271) tridxby ::= INDEXED BY nm */
+ -2, /* (272) tridxby ::= NOT INDEXED */
+ -9, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+ -8, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ -6, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ -3, /* (276) trigger_cmd ::= scanpt select scanpt */
+ -4, /* (277) expr ::= RAISE LP IGNORE RP */
+ -6, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */
+ -1, /* (279) raisetype ::= ROLLBACK */
+ -1, /* (280) raisetype ::= ABORT */
+ -1, /* (281) raisetype ::= FAIL */
+ -4, /* (282) cmd ::= DROP TRIGGER ifexists fullname */
+ -6, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ -3, /* (284) cmd ::= DETACH database_kw_opt expr */
+ 0, /* (285) key_opt ::= */
+ -2, /* (286) key_opt ::= KEY expr */
+ -1, /* (287) cmd ::= REINDEX */
+ -3, /* (288) cmd ::= REINDEX nm dbnm */
+ -1, /* (289) cmd ::= ANALYZE */
+ -3, /* (290) cmd ::= ANALYZE nm dbnm */
+ -6, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ -7, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ -6, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ -1, /* (294) add_column_fullname ::= fullname */
+ -8, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ -1, /* (296) cmd ::= create_vtab */
+ -4, /* (297) cmd ::= create_vtab LP vtabarglist RP */
+ -8, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 0, /* (299) vtabarg ::= */
+ -1, /* (300) vtabargtoken ::= ANY */
+ -3, /* (301) vtabargtoken ::= lp anylist RP */
+ -1, /* (302) lp ::= LP */
+ -2, /* (303) with ::= WITH wqlist */
+ -3, /* (304) with ::= WITH RECURSIVE wqlist */
+ -1, /* (305) wqas ::= AS */
+ -2, /* (306) wqas ::= AS MATERIALIZED */
+ -3, /* (307) wqas ::= AS NOT MATERIALIZED */
+ -6, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */
+ -1, /* (309) wqlist ::= wqitem */
+ -3, /* (310) wqlist ::= wqlist COMMA wqitem */
+ -3, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ -5, /* (312) windowdefn ::= nm AS LP window RP */
+ -5, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ -6, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ -4, /* (315) window ::= ORDER BY sortlist frame_opt */
+ -5, /* (316) window ::= nm ORDER BY sortlist frame_opt */
+ -2, /* (317) window ::= nm frame_opt */
+ 0, /* (318) frame_opt ::= */
+ -3, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ -6, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ -1, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */
+ -1, /* (322) frame_bound_s ::= frame_bound */
+ -2, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */
+ -1, /* (324) frame_bound_e ::= frame_bound */
+ -2, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ -2, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */
+ -2, /* (327) frame_bound ::= CURRENT ROW */
+ 0, /* (328) frame_exclude_opt ::= */
+ -2, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ -2, /* (330) frame_exclude ::= NO OTHERS */
+ -2, /* (331) frame_exclude ::= CURRENT ROW */
+ -1, /* (332) frame_exclude ::= GROUP|TIES */
+ -2, /* (333) window_clause ::= WINDOW windowdefn_list */
+ -2, /* (334) filter_over ::= filter_clause over_clause */
+ -1, /* (335) filter_over ::= over_clause */
+ -1, /* (336) filter_over ::= filter_clause */
+ -4, /* (337) over_clause ::= OVER LP window RP */
+ -2, /* (338) over_clause ::= OVER nm */
+ -5, /* (339) filter_clause ::= FILTER LP WHERE expr RP */
+ -1, /* (340) input ::= cmdlist */
+ -2, /* (341) cmdlist ::= cmdlist ecmd */
+ -1, /* (342) cmdlist ::= ecmd */
+ -1, /* (343) ecmd ::= SEMI */
+ -2, /* (344) ecmd ::= cmdx SEMI */
+ -3, /* (345) ecmd ::= explain cmdx SEMI */
+ 0, /* (346) trans_opt ::= */
+ -1, /* (347) trans_opt ::= TRANSACTION */
+ -2, /* (348) trans_opt ::= TRANSACTION nm */
+ -1, /* (349) savepoint_opt ::= SAVEPOINT */
+ 0, /* (350) savepoint_opt ::= */
+ -2, /* (351) cmd ::= create_table create_table_args */
+ -1, /* (352) table_option_set ::= table_option */
+ -4, /* (353) columnlist ::= columnlist COMMA columnname carglist */
+ -2, /* (354) columnlist ::= columnname carglist */
+ -1, /* (355) nm ::= ID|INDEXED|JOIN_KW */
+ -1, /* (356) nm ::= STRING */
+ -1, /* (357) typetoken ::= typename */
+ -1, /* (358) typename ::= ID|STRING */
+ -1, /* (359) signed ::= plus_num */
+ -1, /* (360) signed ::= minus_num */
+ -2, /* (361) carglist ::= carglist ccons */
+ 0, /* (362) carglist ::= */
+ -2, /* (363) ccons ::= NULL onconf */
+ -4, /* (364) ccons ::= GENERATED ALWAYS AS generated */
+ -2, /* (365) ccons ::= AS generated */
+ -2, /* (366) conslist_opt ::= COMMA conslist */
+ -3, /* (367) conslist ::= conslist tconscomma tcons */
+ -1, /* (368) conslist ::= tcons */
+ 0, /* (369) tconscomma ::= */
+ -1, /* (370) defer_subclause_opt ::= defer_subclause */
+ -1, /* (371) resolvetype ::= raisetype */
+ -1, /* (372) selectnowith ::= oneselect */
+ -1, /* (373) oneselect ::= values */
+ -2, /* (374) sclp ::= selcollist COMMA */
+ -1, /* (375) as ::= ID|STRING */
+ -1, /* (376) indexed_opt ::= indexed_by */
+ 0, /* (377) returning ::= */
+ -1, /* (378) expr ::= term */
+ -1, /* (379) likeop ::= LIKE_KW|MATCH */
+ -1, /* (380) case_operand ::= expr */
+ -1, /* (381) exprlist ::= nexprlist */
+ -1, /* (382) nmnum ::= plus_num */
+ -1, /* (383) nmnum ::= nm */
+ -1, /* (384) nmnum ::= ON */
+ -1, /* (385) nmnum ::= DELETE */
+ -1, /* (386) nmnum ::= DEFAULT */
+ -1, /* (387) plus_num ::= INTEGER|FLOAT */
+ 0, /* (388) foreach_clause ::= */
+ -3, /* (389) foreach_clause ::= FOR EACH ROW */
+ -1, /* (390) trnm ::= nm */
+ 0, /* (391) tridxby ::= */
+ -1, /* (392) database_kw_opt ::= DATABASE */
+ 0, /* (393) database_kw_opt ::= */
+ 0, /* (394) kwcolumn_opt ::= */
+ -1, /* (395) kwcolumn_opt ::= COLUMNKW */
+ -1, /* (396) vtabarglist ::= vtabarg */
+ -3, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */
+ -2, /* (398) vtabarg ::= vtabarg vtabargtoken */
+ 0, /* (399) anylist ::= */
+ -4, /* (400) anylist ::= anylist LP anylist RP */
+ -2, /* (401) anylist ::= anylist ANY */
+ 0, /* (402) with ::= */
+ -1, /* (403) windowdefn_list ::= windowdefn */
+ -1, /* (404) window ::= frame_opt */
};
static void yy_accept(yyParser*); /* Forward Declaration */
@@ -149481,51 +174482,6 @@ static YYACTIONTYPE yy_reduce(
(void)yyLookahead;
(void)yyLookaheadToken;
yymsp = yypParser->yytos;
-#ifndef NDEBUG
- if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
- yysize = yyRuleInfo[yyruleno].nrhs;
- if( yysize ){
- fprintf(yyTraceFILE, "%sReduce %d [%s], go to state %d.\n",
- yyTracePrompt,
- yyruleno, yyRuleName[yyruleno], yymsp[yysize].stateno);
- }else{
- fprintf(yyTraceFILE, "%sReduce %d [%s].\n",
- yyTracePrompt, yyruleno, yyRuleName[yyruleno]);
- }
- }
-#endif /* NDEBUG */
-
- /* Check that the stack is large enough to grow by a single entry
- ** if the RHS of the rule is empty. This ensures that there is room
- ** enough on the stack to push the LHS value */
- if( yyRuleInfo[yyruleno].nrhs==0 ){
-#ifdef YYTRACKMAXSTACKDEPTH
- if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
- yypParser->yyhwm++;
- assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
- }
-#endif
-#if YYSTACKDEPTH>0
- if( yypParser->yytos>=yypParser->yystackEnd ){
- yyStackOverflow(yypParser);
- /* The call to yyStackOverflow() above pops the stack until it is
- ** empty, causing the main parser loop to exit. So the return value
- ** is never used and does not matter. */
- return 0;
- }
-#else
- if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
- if( yyGrowStack(yypParser) ){
- yyStackOverflow(yypParser);
- /* The call to yyStackOverflow() above pops the stack until it is
- ** empty, causing the main parser loop to exit. So the return value
- ** is never used and does not matter. */
- return 0;
- }
- yymsp = yypParser->yytos;
- }
-#endif
- }
switch( yyruleno ){
/* Beginning here are the reduction cases. A typical example
@@ -149539,24 +174495,25 @@ static YYACTIONTYPE yy_reduce(
/********** Begin reduce actions **********************************************/
YYMINORTYPE yylhsminor;
case 0: /* explain ::= EXPLAIN */
-{ pParse->explain = 1; }
+{ if( pParse->pReprepare==0 ) pParse->explain = 1; }
break;
case 1: /* explain ::= EXPLAIN QUERY PLAN */
-{ pParse->explain = 2; }
+{ if( pParse->pReprepare==0 ) pParse->explain = 2; }
break;
case 2: /* cmdx ::= cmd */
{ sqlite3FinishCoding(pParse); }
break;
case 3: /* cmd ::= BEGIN transtype trans_opt */
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy70);}
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy394);}
break;
case 4: /* transtype ::= */
-{yymsp[1].minor.yy70 = TK_DEFERRED;}
+{yymsp[1].minor.yy394 = TK_DEFERRED;}
break;
case 5: /* transtype ::= DEFERRED */
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
-{yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-X*/}
+ case 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321);
+{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 8: /* cmd ::= COMMIT|END trans_opt */
case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
@@ -149579,7 +174536,7 @@ static YYACTIONTYPE yy_reduce(
break;
case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
{
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy70,0,0,yymsp[-2].minor.yy70);
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy394,0,0,yymsp[-2].minor.yy394);
}
break;
case 14: /* createkw ::= CREATE */
@@ -149587,90 +174544,112 @@ static YYACTIONTYPE yy_reduce(
break;
case 15: /* ifnotexists ::= */
case 18: /* temp ::= */ yytestcase(yyruleno==18);
- case 21: /* table_options ::= */ yytestcase(yyruleno==21);
- case 42: /* autoinc ::= */ yytestcase(yyruleno==42);
- case 57: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==57);
- case 67: /* defer_subclause_opt ::= */ yytestcase(yyruleno==67);
- case 76: /* ifexists ::= */ yytestcase(yyruleno==76);
- case 93: /* distinct ::= */ yytestcase(yyruleno==93);
- case 226: /* collate ::= */ yytestcase(yyruleno==226);
-{yymsp[1].minor.yy70 = 0;}
+ case 47: /* autoinc ::= */ yytestcase(yyruleno==47);
+ case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62);
+ case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72);
+ case 81: /* ifexists ::= */ yytestcase(yyruleno==81);
+ case 98: /* distinct ::= */ yytestcase(yyruleno==98);
+ case 244: /* collate ::= */ yytestcase(yyruleno==244);
+{yymsp[1].minor.yy394 = 0;}
break;
case 16: /* ifnotexists ::= IF NOT EXISTS */
-{yymsp[-2].minor.yy70 = 1;}
+{yymsp[-2].minor.yy394 = 1;}
break;
case 17: /* temp ::= TEMP */
- case 43: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==43);
-{yymsp[0].minor.yy70 = 1;}
+{yymsp[0].minor.yy394 = pParse->db->init.busy==0;}
break;
- case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */
+ case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */
{
- sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy70,0);
+ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy285,0);
}
break;
case 20: /* create_table_args ::= AS select */
{
- sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy489);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy489);
+ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy47);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47);
}
break;
- case 22: /* table_options ::= WITHOUT nm */
+ case 21: /* table_option_set ::= */
+{yymsp[1].minor.yy285 = 0;}
+ break;
+ case 22: /* table_option_set ::= table_option_set COMMA table_option */
+{yylhsminor.yy285 = yymsp[-2].minor.yy285|yymsp[0].minor.yy285;}
+ yymsp[-2].minor.yy285 = yylhsminor.yy285;
+ break;
+ case 23: /* table_option ::= WITHOUT nm */
{
if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
- yymsp[-1].minor.yy70 = TF_WithoutRowid | TF_NoVisibleRowid;
+ yymsp[-1].minor.yy285 = TF_WithoutRowid | TF_NoVisibleRowid;
+ }else{
+ yymsp[-1].minor.yy285 = 0;
+ sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
+ }
+}
+ break;
+ case 24: /* table_option ::= nm */
+{
+ if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){
+ yylhsminor.yy285 = TF_Strict;
}else{
- yymsp[-1].minor.yy70 = 0;
+ yylhsminor.yy285 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
+ yymsp[0].minor.yy285 = yylhsminor.yy285;
break;
- case 23: /* columnname ::= nm typetoken */
-{sqlite3AddColumn(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
+ case 25: /* columnname ::= nm typetoken */
+{sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);}
break;
- case 24: /* typetoken ::= */
- case 60: /* conslist_opt ::= */ yytestcase(yyruleno==60);
- case 99: /* as ::= */ yytestcase(yyruleno==99);
+ case 26: /* typetoken ::= */
+ case 65: /* conslist_opt ::= */ yytestcase(yyruleno==65);
+ case 104: /* as ::= */ yytestcase(yyruleno==104);
{yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;}
break;
- case 25: /* typetoken ::= typename LP signed RP */
+ case 27: /* typetoken ::= typename LP signed RP */
{
yymsp[-3].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z);
}
break;
- case 26: /* typetoken ::= typename LP signed COMMA signed RP */
+ case 28: /* typetoken ::= typename LP signed COMMA signed RP */
{
yymsp[-5].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z);
}
break;
- case 27: /* typename ::= typename ID|STRING */
+ case 29: /* typename ::= typename ID|STRING */
{yymsp[-1].minor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);}
break;
- case 28: /* scanpt ::= */
+ case 30: /* scanpt ::= */
+{
+ assert( yyLookahead!=YYNOCODE );
+ yymsp[1].minor.yy522 = yyLookaheadToken.z;
+}
+ break;
+ case 31: /* scantok ::= */
{
assert( yyLookahead!=YYNOCODE );
- yymsp[1].minor.yy392 = yyLookaheadToken.z;
+ yymsp[1].minor.yy0 = yyLookaheadToken;
}
break;
- case 29: /* ccons ::= CONSTRAINT nm */
- case 62: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==62);
+ case 32: /* ccons ::= CONSTRAINT nm */
+ case 67: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==67);
{pParse->constraintName = yymsp[0].minor.yy0;}
break;
- case 30: /* ccons ::= DEFAULT scanpt term scanpt */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy392,yymsp[0].minor.yy392);}
+ case 33: /* ccons ::= DEFAULT scantok term */
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
- case 31: /* ccons ::= DEFAULT LP expr RP */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
+ case 34: /* ccons ::= DEFAULT LP expr RP */
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
break;
- case 32: /* ccons ::= DEFAULT PLUS term scanpt */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy392);}
+ case 35: /* ccons ::= DEFAULT PLUS scantok term */
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
- case 33: /* ccons ::= DEFAULT MINUS term scanpt */
+ case 36: /* ccons ::= DEFAULT MINUS scantok term */
{
- Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[-1].minor.yy18, 0);
- sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy392);
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy528, 0);
+ sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);
}
break;
- case 34: /* ccons ::= DEFAULT scanpt ID|INDEXED */
+ case 37: /* ccons ::= DEFAULT scantok ID|INDEXED */
{
Expr *p = tokenExpr(pParse, TK_STRING, yymsp[0].minor.yy0);
if( p ){
@@ -149680,556 +174659,597 @@ static YYACTIONTYPE yy_reduce(
sqlite3AddDefaultValue(pParse,p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.z+yymsp[0].minor.yy0.n);
}
break;
- case 35: /* ccons ::= NOT NULL onconf */
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy70);}
+ case 38: /* ccons ::= NOT NULL onconf */
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy394);}
break;
- case 36: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy70,yymsp[0].minor.yy70,yymsp[-2].minor.yy70);}
+ case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy394,yymsp[0].minor.yy394,yymsp[-2].minor.yy394);}
break;
- case 37: /* ccons ::= UNIQUE onconf */
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy70,0,0,0,0,
+ case 40: /* ccons ::= UNIQUE onconf */
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy394,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
- case 38: /* ccons ::= CHECK LP expr RP */
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy18);}
+ case 41: /* ccons ::= CHECK LP expr RP */
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);}
break;
- case 39: /* ccons ::= REFERENCES nm eidlist_opt refargs */
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy420,yymsp[0].minor.yy70);}
+ case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy322,yymsp[0].minor.yy394);}
break;
- case 40: /* ccons ::= defer_subclause */
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy70);}
+ case 43: /* ccons ::= defer_subclause */
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy394);}
break;
- case 41: /* ccons ::= COLLATE ID|STRING */
+ case 44: /* ccons ::= COLLATE ID|STRING */
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
break;
- case 44: /* refargs ::= */
-{ yymsp[1].minor.yy70 = OE_None*0x0101; /* EV: R-19803-45884 */}
+ case 45: /* generated ::= LP expr RP */
+{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy528,0);}
break;
- case 45: /* refargs ::= refargs refarg */
-{ yymsp[-1].minor.yy70 = (yymsp[-1].minor.yy70 & ~yymsp[0].minor.yy111.mask) | yymsp[0].minor.yy111.value; }
+ case 46: /* generated ::= LP expr RP ID */
+{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy528,&yymsp[0].minor.yy0);}
break;
- case 46: /* refarg ::= MATCH nm */
-{ yymsp[-1].minor.yy111.value = 0; yymsp[-1].minor.yy111.mask = 0x000000; }
+ case 48: /* autoinc ::= AUTOINCR */
+{yymsp[0].minor.yy394 = 1;}
break;
- case 47: /* refarg ::= ON INSERT refact */
-{ yymsp[-2].minor.yy111.value = 0; yymsp[-2].minor.yy111.mask = 0x000000; }
+ case 49: /* refargs ::= */
+{ yymsp[1].minor.yy394 = OE_None*0x0101; /* EV: R-19803-45884 */}
break;
- case 48: /* refarg ::= ON DELETE refact */
-{ yymsp[-2].minor.yy111.value = yymsp[0].minor.yy70; yymsp[-2].minor.yy111.mask = 0x0000ff; }
+ case 50: /* refargs ::= refargs refarg */
+{ yymsp[-1].minor.yy394 = (yymsp[-1].minor.yy394 & ~yymsp[0].minor.yy231.mask) | yymsp[0].minor.yy231.value; }
break;
- case 49: /* refarg ::= ON UPDATE refact */
-{ yymsp[-2].minor.yy111.value = yymsp[0].minor.yy70<<8; yymsp[-2].minor.yy111.mask = 0x00ff00; }
+ case 51: /* refarg ::= MATCH nm */
+{ yymsp[-1].minor.yy231.value = 0; yymsp[-1].minor.yy231.mask = 0x000000; }
break;
- case 50: /* refact ::= SET NULL */
-{ yymsp[-1].minor.yy70 = OE_SetNull; /* EV: R-33326-45252 */}
+ case 52: /* refarg ::= ON INSERT refact */
+{ yymsp[-2].minor.yy231.value = 0; yymsp[-2].minor.yy231.mask = 0x000000; }
break;
- case 51: /* refact ::= SET DEFAULT */
-{ yymsp[-1].minor.yy70 = OE_SetDflt; /* EV: R-33326-45252 */}
+ case 53: /* refarg ::= ON DELETE refact */
+{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394; yymsp[-2].minor.yy231.mask = 0x0000ff; }
break;
- case 52: /* refact ::= CASCADE */
-{ yymsp[0].minor.yy70 = OE_Cascade; /* EV: R-33326-45252 */}
+ case 54: /* refarg ::= ON UPDATE refact */
+{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394<<8; yymsp[-2].minor.yy231.mask = 0x00ff00; }
break;
- case 53: /* refact ::= RESTRICT */
-{ yymsp[0].minor.yy70 = OE_Restrict; /* EV: R-33326-45252 */}
+ case 55: /* refact ::= SET NULL */
+{ yymsp[-1].minor.yy394 = OE_SetNull; /* EV: R-33326-45252 */}
break;
- case 54: /* refact ::= NO ACTION */
-{ yymsp[-1].minor.yy70 = OE_None; /* EV: R-33326-45252 */}
+ case 56: /* refact ::= SET DEFAULT */
+{ yymsp[-1].minor.yy394 = OE_SetDflt; /* EV: R-33326-45252 */}
break;
- case 55: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
-{yymsp[-2].minor.yy70 = 0;}
+ case 57: /* refact ::= CASCADE */
+{ yymsp[0].minor.yy394 = OE_Cascade; /* EV: R-33326-45252 */}
break;
- case 56: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
- case 71: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==71);
- case 156: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==156);
-{yymsp[-1].minor.yy70 = yymsp[0].minor.yy70;}
+ case 58: /* refact ::= RESTRICT */
+{ yymsp[0].minor.yy394 = OE_Restrict; /* EV: R-33326-45252 */}
break;
- case 58: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
- case 75: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==75);
- case 198: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==198);
- case 201: /* in_op ::= NOT IN */ yytestcase(yyruleno==201);
- case 227: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==227);
-{yymsp[-1].minor.yy70 = 1;}
+ case 59: /* refact ::= NO ACTION */
+{ yymsp[-1].minor.yy394 = OE_None; /* EV: R-33326-45252 */}
break;
- case 59: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
-{yymsp[-1].minor.yy70 = 0;}
+ case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+{yymsp[-2].minor.yy394 = 0;}
break;
- case 61: /* tconscomma ::= COMMA */
+ case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76);
+ case 171: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==171);
+{yymsp[-1].minor.yy394 = yymsp[0].minor.yy394;}
+ break;
+ case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80);
+ case 217: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==217);
+ case 220: /* in_op ::= NOT IN */ yytestcase(yyruleno==220);
+ case 245: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==245);
+{yymsp[-1].minor.yy394 = 1;}
+ break;
+ case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+{yymsp[-1].minor.yy394 = 0;}
+ break;
+ case 66: /* tconscomma ::= COMMA */
{pParse->constraintName.n = 0;}
break;
- case 63: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy420,yymsp[0].minor.yy70,yymsp[-2].minor.yy70,0);}
+ case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy322,yymsp[0].minor.yy394,yymsp[-2].minor.yy394,0);}
break;
- case 64: /* tcons ::= UNIQUE LP sortlist RP onconf */
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy420,yymsp[0].minor.yy70,0,0,0,0,
+ case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy322,yymsp[0].minor.yy394,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
- case 65: /* tcons ::= CHECK LP expr RP onconf */
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy18);}
+ case 70: /* tcons ::= CHECK LP expr RP onconf */
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy528,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);}
break;
- case 66: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
+ case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
{
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy420, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy420, yymsp[-1].minor.yy70);
- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy70);
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy322, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[-1].minor.yy394);
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy394);
}
break;
- case 68: /* onconf ::= */
- case 70: /* orconf ::= */ yytestcase(yyruleno==70);
-{yymsp[1].minor.yy70 = OE_Default;}
+ case 73: /* onconf ::= */
+ case 75: /* orconf ::= */ yytestcase(yyruleno==75);
+{yymsp[1].minor.yy394 = OE_Default;}
break;
- case 69: /* onconf ::= ON CONFLICT resolvetype */
-{yymsp[-2].minor.yy70 = yymsp[0].minor.yy70;}
+ case 74: /* onconf ::= ON CONFLICT resolvetype */
+{yymsp[-2].minor.yy394 = yymsp[0].minor.yy394;}
break;
- case 72: /* resolvetype ::= IGNORE */
-{yymsp[0].minor.yy70 = OE_Ignore;}
+ case 77: /* resolvetype ::= IGNORE */
+{yymsp[0].minor.yy394 = OE_Ignore;}
break;
- case 73: /* resolvetype ::= REPLACE */
- case 157: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==157);
-{yymsp[0].minor.yy70 = OE_Replace;}
+ case 78: /* resolvetype ::= REPLACE */
+ case 172: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==172);
+{yymsp[0].minor.yy394 = OE_Replace;}
break;
- case 74: /* cmd ::= DROP TABLE ifexists fullname */
+ case 79: /* cmd ::= DROP TABLE ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy135, 0, yymsp[-1].minor.yy70);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy131, 0, yymsp[-1].minor.yy394);
}
break;
- case 77: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
+ case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
{
- sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy420, yymsp[0].minor.yy489, yymsp[-7].minor.yy70, yymsp[-5].minor.yy70);
+ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[0].minor.yy47, yymsp[-7].minor.yy394, yymsp[-5].minor.yy394);
}
break;
- case 78: /* cmd ::= DROP VIEW ifexists fullname */
+ case 83: /* cmd ::= DROP VIEW ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy135, 1, yymsp[-1].minor.yy70);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy131, 1, yymsp[-1].minor.yy394);
}
break;
- case 79: /* cmd ::= select */
+ case 84: /* cmd ::= select */
{
- SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
- sqlite3Select(pParse, yymsp[0].minor.yy489, &dest);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy489);
+ SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
+ sqlite3Select(pParse, yymsp[0].minor.yy47, &dest);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47);
}
break;
- case 80: /* select ::= WITH wqlist selectnowith */
-{
- Select *p = yymsp[0].minor.yy489;
- if( p ){
- p->pWith = yymsp[-1].minor.yy449;
- parserDoubleLinkSelect(pParse, p);
- }else{
- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy449);
- }
- yymsp[-2].minor.yy489 = p;
-}
+ case 85: /* select ::= WITH wqlist selectnowith */
+{yymsp[-2].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);}
break;
- case 81: /* select ::= WITH RECURSIVE wqlist selectnowith */
-{
- Select *p = yymsp[0].minor.yy489;
- if( p ){
- p->pWith = yymsp[-1].minor.yy449;
- parserDoubleLinkSelect(pParse, p);
- }else{
- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy449);
- }
- yymsp[-3].minor.yy489 = p;
-}
+ case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */
+{yymsp[-3].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);}
break;
- case 82: /* select ::= selectnowith */
+ case 87: /* select ::= selectnowith */
{
- Select *p = yymsp[0].minor.yy489;
+ Select *p = yymsp[0].minor.yy47;
if( p ){
parserDoubleLinkSelect(pParse, p);
}
- yymsp[0].minor.yy489 = p; /*A-overwrites-X*/
}
break;
- case 83: /* selectnowith ::= selectnowith multiselect_op oneselect */
+ case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */
{
- Select *pRhs = yymsp[0].minor.yy489;
- Select *pLhs = yymsp[-2].minor.yy489;
+ Select *pRhs = yymsp[0].minor.yy47;
+ Select *pLhs = yymsp[-2].minor.yy47;
if( pRhs && pRhs->pPrior ){
SrcList *pFrom;
Token x;
x.n = 0;
parserDoubleLinkSelect(pParse, pRhs);
- pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0);
+ pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0);
pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
}
if( pRhs ){
- pRhs->op = (u8)yymsp[-1].minor.yy70;
+ pRhs->op = (u8)yymsp[-1].minor.yy394;
pRhs->pPrior = pLhs;
if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
pRhs->selFlags &= ~SF_MultiValue;
- if( yymsp[-1].minor.yy70!=TK_ALL ) pParse->hasCompound = 1;
+ if( yymsp[-1].minor.yy394!=TK_ALL ) pParse->hasCompound = 1;
}else{
sqlite3SelectDelete(pParse->db, pLhs);
}
- yymsp[-2].minor.yy489 = pRhs;
+ yymsp[-2].minor.yy47 = pRhs;
}
break;
- case 84: /* multiselect_op ::= UNION */
- case 86: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==86);
-{yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-OP*/}
+ case 89: /* multiselect_op ::= UNION */
+ case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91);
+{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-OP*/}
break;
- case 85: /* multiselect_op ::= UNION ALL */
-{yymsp[-1].minor.yy70 = TK_ALL;}
+ case 90: /* multiselect_op ::= UNION ALL */
+{yymsp[-1].minor.yy394 = TK_ALL;}
break;
- case 87: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+ case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
{
- yymsp[-8].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy420,yymsp[-5].minor.yy135,yymsp[-4].minor.yy18,yymsp[-3].minor.yy420,yymsp[-2].minor.yy18,yymsp[-1].minor.yy420,yymsp[-7].minor.yy70,yymsp[0].minor.yy18);
+ yymsp[-8].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy322,yymsp[-5].minor.yy131,yymsp[-4].minor.yy528,yymsp[-3].minor.yy322,yymsp[-2].minor.yy528,yymsp[-1].minor.yy322,yymsp[-7].minor.yy394,yymsp[0].minor.yy528);
}
break;
- case 88: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
+ case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
{
- yymsp[-9].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy420,yymsp[-6].minor.yy135,yymsp[-5].minor.yy18,yymsp[-4].minor.yy420,yymsp[-3].minor.yy18,yymsp[-1].minor.yy420,yymsp[-8].minor.yy70,yymsp[0].minor.yy18);
- if( yymsp[-9].minor.yy489 ){
- yymsp[-9].minor.yy489->pWinDefn = yymsp[-2].minor.yy327;
+ yymsp[-9].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy322,yymsp[-6].minor.yy131,yymsp[-5].minor.yy528,yymsp[-4].minor.yy322,yymsp[-3].minor.yy528,yymsp[-1].minor.yy322,yymsp[-8].minor.yy394,yymsp[0].minor.yy528);
+ if( yymsp[-9].minor.yy47 ){
+ yymsp[-9].minor.yy47->pWinDefn = yymsp[-2].minor.yy41;
}else{
- sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy327);
+ sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy41);
}
}
break;
- case 89: /* values ::= VALUES LP nexprlist RP */
+ case 94: /* values ::= VALUES LP nexprlist RP */
{
- yymsp[-3].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy420,0,0,0,0,0,SF_Values,0);
+ yymsp[-3].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values,0);
}
break;
- case 90: /* values ::= values COMMA LP nexprlist RP */
+ case 95: /* values ::= values COMMA LP nexprlist RP */
{
- Select *pRight, *pLeft = yymsp[-4].minor.yy489;
- pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy420,0,0,0,0,0,SF_Values|SF_MultiValue,0);
+ Select *pRight, *pLeft = yymsp[-4].minor.yy47;
+ pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values|SF_MultiValue,0);
if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue;
if( pRight ){
pRight->op = TK_ALL;
pRight->pPrior = pLeft;
- yymsp[-4].minor.yy489 = pRight;
+ yymsp[-4].minor.yy47 = pRight;
}else{
- yymsp[-4].minor.yy489 = pLeft;
+ yymsp[-4].minor.yy47 = pLeft;
}
}
break;
- case 91: /* distinct ::= DISTINCT */
-{yymsp[0].minor.yy70 = SF_Distinct;}
+ case 96: /* distinct ::= DISTINCT */
+{yymsp[0].minor.yy394 = SF_Distinct;}
break;
- case 92: /* distinct ::= ALL */
-{yymsp[0].minor.yy70 = SF_All;}
+ case 97: /* distinct ::= ALL */
+{yymsp[0].minor.yy394 = SF_All;}
break;
- case 94: /* sclp ::= */
- case 127: /* orderby_opt ::= */ yytestcase(yyruleno==127);
- case 134: /* groupby_opt ::= */ yytestcase(yyruleno==134);
- case 214: /* exprlist ::= */ yytestcase(yyruleno==214);
- case 217: /* paren_exprlist ::= */ yytestcase(yyruleno==217);
- case 222: /* eidlist_opt ::= */ yytestcase(yyruleno==222);
-{yymsp[1].minor.yy420 = 0;}
+ case 99: /* sclp ::= */
+ case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132);
+ case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142);
+ case 232: /* exprlist ::= */ yytestcase(yyruleno==232);
+ case 235: /* paren_exprlist ::= */ yytestcase(yyruleno==235);
+ case 240: /* eidlist_opt ::= */ yytestcase(yyruleno==240);
+{yymsp[1].minor.yy322 = 0;}
break;
- case 95: /* selcollist ::= sclp scanpt expr scanpt as */
+ case 100: /* selcollist ::= sclp scanpt expr scanpt as */
{
- yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy420, yymsp[-2].minor.yy18);
- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy420, &yymsp[0].minor.yy0, 1);
- sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy420,yymsp[-3].minor.yy392,yymsp[-1].minor.yy392);
+ yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528);
+ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[0].minor.yy0, 1);
+ sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy322,yymsp[-3].minor.yy522,yymsp[-1].minor.yy522);
}
break;
- case 96: /* selcollist ::= sclp scanpt STAR */
+ case 101: /* selcollist ::= sclp scanpt STAR */
{
Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
- yymsp[-2].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy420, p);
+ sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail));
+ yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy322, p);
}
break;
- case 97: /* selcollist ::= sclp scanpt nm DOT STAR */
+ case 102: /* selcollist ::= sclp scanpt nm DOT STAR */
{
- Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
- Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
- Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
- yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, pDot);
+ Expr *pRight, *pLeft, *pDot;
+ pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
+ sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail));
+ pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0);
+ pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
+ yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot);
}
break;
- case 98: /* as ::= AS nm */
- case 109: /* dbnm ::= DOT nm */ yytestcase(yyruleno==109);
- case 236: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==236);
- case 237: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==237);
+ case 103: /* as ::= AS nm */
+ case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115);
+ case 256: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==256);
+ case 257: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==257);
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
- case 100: /* from ::= */
-{yymsp[1].minor.yy135 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy135));}
+ case 105: /* from ::= */
+ case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108);
+{yymsp[1].minor.yy131 = 0;}
break;
- case 101: /* from ::= FROM seltablist */
+ case 106: /* from ::= FROM seltablist */
{
- yymsp[-1].minor.yy135 = yymsp[0].minor.yy135;
- sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy135);
+ yymsp[-1].minor.yy131 = yymsp[0].minor.yy131;
+ sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy131);
}
break;
- case 102: /* stl_prefix ::= seltablist joinop */
+ case 107: /* stl_prefix ::= seltablist joinop */
{
- if( ALWAYS(yymsp[-1].minor.yy135 && yymsp[-1].minor.yy135->nSrc>0) ) yymsp[-1].minor.yy135->a[yymsp[-1].minor.yy135->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy70;
+ if( ALWAYS(yymsp[-1].minor.yy131 && yymsp[-1].minor.yy131->nSrc>0) ) yymsp[-1].minor.yy131->a[yymsp[-1].minor.yy131->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy394;
}
break;
- case 103: /* stl_prefix ::= */
-{yymsp[1].minor.yy135 = 0;}
+ case 109: /* seltablist ::= stl_prefix nm dbnm as on_using */
+{
+ yymsp[-4].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy131,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561);
+}
break;
- case 104: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
+ case 110: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
{
- yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48);
- sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy135, &yymsp[-2].minor.yy0);
+ yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy561);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-1].minor.yy0);
}
break;
- case 105: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
+ case 111: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
{
- yymsp[-8].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy135,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48);
- sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy135, yymsp[-4].minor.yy420);
+ yymsp[-7].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy131,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561);
+ sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy131, yymsp[-3].minor.yy322);
}
break;
- case 106: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */
+ case 112: /* seltablist ::= stl_prefix LP select RP as on_using */
{
- yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy489,yymsp[-1].minor.yy18,yymsp[0].minor.yy48);
+ yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy47,&yymsp[0].minor.yy561);
}
break;
- case 107: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
+ case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */
{
- if( yymsp[-6].minor.yy135==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy18==0 && yymsp[0].minor.yy48==0 ){
- yymsp[-6].minor.yy135 = yymsp[-4].minor.yy135;
- }else if( yymsp[-4].minor.yy135->nSrc==1 ){
- yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48);
- if( yymsp[-6].minor.yy135 ){
- struct SrcList_item *pNew = &yymsp[-6].minor.yy135->a[yymsp[-6].minor.yy135->nSrc-1];
- struct SrcList_item *pOld = yymsp[-4].minor.yy135->a;
+ if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){
+ yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131;
+ }else if( ALWAYS(yymsp[-3].minor.yy131!=0) && yymsp[-3].minor.yy131->nSrc==1 ){
+ yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561);
+ if( yymsp[-5].minor.yy131 ){
+ SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1];
+ SrcItem *pOld = yymsp[-3].minor.yy131->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
+ if( pNew->pSelect && (pNew->pSelect->selFlags & SF_NestedFrom)!=0 ){
+ pNew->fg.isNestedFrom = 1;
+ }
+ if( pOld->fg.isTabFunc ){
+ pNew->u1.pFuncArg = pOld->u1.pFuncArg;
+ pOld->u1.pFuncArg = 0;
+ pOld->fg.isTabFunc = 0;
+ pNew->fg.isTabFunc = 1;
+ }
pOld->zName = pOld->zDatabase = 0;
pOld->pSelect = 0;
}
- sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy135);
+ sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy131);
}else{
Select *pSubquery;
- sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy135);
- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy135,0,0,0,0,SF_NestedFrom,0);
- yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy18,yymsp[0].minor.yy48);
+ sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy131);
+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy131,0,0,0,0,SF_NestedFrom,0);
+ yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy561);
}
}
break;
- case 108: /* dbnm ::= */
- case 122: /* indexed_opt ::= */ yytestcase(yyruleno==122);
+ case 114: /* dbnm ::= */
+ case 129: /* indexed_opt ::= */ yytestcase(yyruleno==129);
{yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;}
break;
- case 110: /* fullname ::= nm */
+ case 116: /* fullname ::= nm */
{
- yylhsminor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[0].minor.yy0,0);
- if( IN_RENAME_OBJECT && yylhsminor.yy135 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy135->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[0].minor.yy135 = yylhsminor.yy135;
+ yymsp[0].minor.yy131 = yylhsminor.yy131;
break;
- case 111: /* fullname ::= nm DOT nm */
+ case 117: /* fullname ::= nm DOT nm */
{
- yylhsminor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
- if( IN_RENAME_OBJECT && yylhsminor.yy135 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy135->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[-2].minor.yy135 = yylhsminor.yy135;
+ yymsp[-2].minor.yy131 = yylhsminor.yy131;
break;
- case 112: /* xfullname ::= nm */
-{yymsp[0].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
+ case 118: /* xfullname ::= nm */
+{yymsp[0].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
break;
- case 113: /* xfullname ::= nm DOT nm */
-{yymsp[-2].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 119: /* xfullname ::= nm DOT nm */
+{yymsp[-2].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 114: /* xfullname ::= nm DOT nm AS nm */
+ case 120: /* xfullname ::= nm DOT nm AS nm */
{
- yymsp[-4].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
- if( yymsp[-4].minor.yy135 ) yymsp[-4].minor.yy135->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-4].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
+ if( yymsp[-4].minor.yy131 ) yymsp[-4].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
- case 115: /* xfullname ::= nm AS nm */
-{
- yymsp[-2].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
- if( yymsp[-2].minor.yy135 ) yymsp[-2].minor.yy135->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ case 121: /* xfullname ::= nm AS nm */
+{
+ yymsp[-2].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
+ if( yymsp[-2].minor.yy131 ) yymsp[-2].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
- case 116: /* joinop ::= COMMA|JOIN */
-{ yymsp[0].minor.yy70 = JT_INNER; }
+ case 122: /* joinop ::= COMMA|JOIN */
+{ yymsp[0].minor.yy394 = JT_INNER; }
+ break;
+ case 123: /* joinop ::= JOIN_KW JOIN */
+{yymsp[-1].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
break;
- case 117: /* joinop ::= JOIN_KW JOIN */
-{yymsp[-1].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
+ case 124: /* joinop ::= JOIN_KW nm JOIN */
+{yymsp[-2].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
break;
- case 118: /* joinop ::= JOIN_KW nm JOIN */
-{yymsp[-2].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
+ case 125: /* joinop ::= JOIN_KW nm nm JOIN */
+{yymsp[-3].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
break;
- case 119: /* joinop ::= JOIN_KW nm nm JOIN */
-{yymsp[-3].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
+ case 126: /* on_using ::= ON expr */
+{yymsp[-1].minor.yy561.pOn = yymsp[0].minor.yy528; yymsp[-1].minor.yy561.pUsing = 0;}
break;
- case 120: /* on_opt ::= ON expr */
- case 137: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==137);
- case 144: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==144);
- case 210: /* case_else ::= ELSE expr */ yytestcase(yyruleno==210);
-{yymsp[-1].minor.yy18 = yymsp[0].minor.yy18;}
+ case 127: /* on_using ::= USING LP idlist RP */
+{yymsp[-3].minor.yy561.pOn = 0; yymsp[-3].minor.yy561.pUsing = yymsp[-1].minor.yy254;}
break;
- case 121: /* on_opt ::= */
- case 136: /* having_opt ::= */ yytestcase(yyruleno==136);
- case 138: /* limit_opt ::= */ yytestcase(yyruleno==138);
- case 143: /* where_opt ::= */ yytestcase(yyruleno==143);
- case 211: /* case_else ::= */ yytestcase(yyruleno==211);
- case 213: /* case_operand ::= */ yytestcase(yyruleno==213);
-{yymsp[1].minor.yy18 = 0;}
+ case 128: /* on_using ::= */
+{yymsp[1].minor.yy561.pOn = 0; yymsp[1].minor.yy561.pUsing = 0;}
break;
- case 123: /* indexed_opt ::= INDEXED BY nm */
+ case 130: /* indexed_by ::= INDEXED BY nm */
{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
break;
- case 124: /* indexed_opt ::= NOT INDEXED */
+ case 131: /* indexed_by ::= NOT INDEXED */
{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
break;
- case 125: /* using_opt ::= USING LP idlist RP */
-{yymsp[-3].minor.yy48 = yymsp[-1].minor.yy48;}
- break;
- case 126: /* using_opt ::= */
- case 158: /* idlist_opt ::= */ yytestcase(yyruleno==158);
-{yymsp[1].minor.yy48 = 0;}
+ case 133: /* orderby_opt ::= ORDER BY sortlist */
+ case 143: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==143);
+{yymsp[-2].minor.yy322 = yymsp[0].minor.yy322;}
break;
- case 128: /* orderby_opt ::= ORDER BY sortlist */
- case 135: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==135);
-{yymsp[-2].minor.yy420 = yymsp[0].minor.yy420;}
- break;
- case 129: /* sortlist ::= sortlist COMMA expr sortorder */
+ case 134: /* sortlist ::= sortlist COMMA expr sortorder nulls */
{
- yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy420,yymsp[-1].minor.yy18);
- sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy420,yymsp[0].minor.yy70);
+ yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322,yymsp[-2].minor.yy528);
+ sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394);
}
break;
- case 130: /* sortlist ::= expr sortorder */
+ case 135: /* sortlist ::= expr sortorder nulls */
{
- yymsp[-1].minor.yy420 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy18); /*A-overwrites-Y*/
- sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy420,yymsp[0].minor.yy70);
+ yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy528); /*A-overwrites-Y*/
+ sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394);
}
break;
- case 131: /* sortorder ::= ASC */
-{yymsp[0].minor.yy70 = SQLITE_SO_ASC;}
+ case 136: /* sortorder ::= ASC */
+{yymsp[0].minor.yy394 = SQLITE_SO_ASC;}
+ break;
+ case 137: /* sortorder ::= DESC */
+{yymsp[0].minor.yy394 = SQLITE_SO_DESC;}
+ break;
+ case 138: /* sortorder ::= */
+ case 141: /* nulls ::= */ yytestcase(yyruleno==141);
+{yymsp[1].minor.yy394 = SQLITE_SO_UNDEFINED;}
break;
- case 132: /* sortorder ::= DESC */
-{yymsp[0].minor.yy70 = SQLITE_SO_DESC;}
+ case 139: /* nulls ::= NULLS FIRST */
+{yymsp[-1].minor.yy394 = SQLITE_SO_ASC;}
break;
- case 133: /* sortorder ::= */
-{yymsp[1].minor.yy70 = SQLITE_SO_UNDEFINED;}
+ case 140: /* nulls ::= NULLS LAST */
+{yymsp[-1].minor.yy394 = SQLITE_SO_DESC;}
break;
- case 139: /* limit_opt ::= LIMIT expr */
-{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy18,0);}
+ case 144: /* having_opt ::= */
+ case 146: /* limit_opt ::= */ yytestcase(yyruleno==146);
+ case 151: /* where_opt ::= */ yytestcase(yyruleno==151);
+ case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153);
+ case 230: /* case_else ::= */ yytestcase(yyruleno==230);
+ case 231: /* case_operand ::= */ yytestcase(yyruleno==231);
+ case 250: /* vinto ::= */ yytestcase(yyruleno==250);
+{yymsp[1].minor.yy528 = 0;}
break;
- case 140: /* limit_opt ::= LIMIT expr OFFSET expr */
-{yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy18,yymsp[0].minor.yy18);}
+ case 145: /* having_opt ::= HAVING expr */
+ case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152);
+ case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154);
+ case 229: /* case_else ::= ELSE expr */ yytestcase(yyruleno==229);
+ case 249: /* vinto ::= INTO expr */ yytestcase(yyruleno==249);
+{yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;}
break;
- case 141: /* limit_opt ::= LIMIT expr COMMA expr */
-{yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy18,yymsp[-2].minor.yy18);}
+ case 147: /* limit_opt ::= LIMIT expr */
+{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);}
break;
- case 142: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
+ case 148: /* limit_opt ::= LIMIT expr OFFSET expr */
+{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
+ break;
+ case 149: /* limit_opt ::= LIMIT expr COMMA expr */
+{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,yymsp[-2].minor.yy528);}
+ break;
+ case 150: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy135, &yymsp[-1].minor.yy0);
- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy135,yymsp[0].minor.yy18,0,0);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy131, &yymsp[-1].minor.yy0);
+ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy131,yymsp[0].minor.yy528,0,0);
}
break;
- case 145: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
+ case 155: /* where_opt_ret ::= RETURNING selcollist */
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-1].minor.yy528 = 0;}
+ break;
+ case 156: /* where_opt_ret ::= WHERE expr RETURNING selcollist */
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-3].minor.yy528 = yymsp[-2].minor.yy528;}
+ break;
+ case 157: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy135, &yymsp[-3].minor.yy0);
- sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy420,"set list");
- sqlite3Update(pParse,yymsp[-4].minor.yy135,yymsp[-1].minor.yy420,yymsp[0].minor.yy18,yymsp[-5].minor.yy70,0,0,0);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-4].minor.yy0);
+ sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy322,"set list");
+ if( yymsp[-1].minor.yy131 ){
+ SrcList *pFromClause = yymsp[-1].minor.yy131;
+ if( pFromClause->nSrc>1 ){
+ Select *pSubquery;
+ Token as;
+ pSubquery = sqlite3SelectNew(pParse,0,pFromClause,0,0,0,0,SF_NestedFrom,0);
+ as.n = 0;
+ as.z = 0;
+ pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
+ }
+ yymsp[-5].minor.yy131 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy131, pFromClause);
+ }
+ sqlite3Update(pParse,yymsp[-5].minor.yy131,yymsp[-2].minor.yy322,yymsp[0].minor.yy528,yymsp[-6].minor.yy394,0,0,0);
}
break;
- case 146: /* setlist ::= setlist COMMA nm EQ expr */
+ case 158: /* setlist ::= setlist COMMA nm EQ expr */
{
- yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy420, yymsp[0].minor.yy18);
- sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy420, &yymsp[-2].minor.yy0, 1);
+ yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[0].minor.yy528);
+ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, 1);
}
break;
- case 147: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
+ case 159: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
{
- yymsp[-6].minor.yy420 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy420, yymsp[-3].minor.yy48, yymsp[0].minor.yy18);
+ yymsp[-6].minor.yy322 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy322, yymsp[-3].minor.yy254, yymsp[0].minor.yy528);
}
break;
- case 148: /* setlist ::= nm EQ expr */
+ case 160: /* setlist ::= nm EQ expr */
{
- yylhsminor.yy420 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy18);
- sqlite3ExprListSetName(pParse, yylhsminor.yy420, &yymsp[-2].minor.yy0, 1);
+ yylhsminor.yy322 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy528);
+ sqlite3ExprListSetName(pParse, yylhsminor.yy322, &yymsp[-2].minor.yy0, 1);
}
- yymsp[-2].minor.yy420 = yylhsminor.yy420;
+ yymsp[-2].minor.yy322 = yylhsminor.yy322;
break;
- case 149: /* setlist ::= LP idlist RP EQ expr */
+ case 161: /* setlist ::= LP idlist RP EQ expr */
{
- yymsp[-4].minor.yy420 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy48, yymsp[0].minor.yy18);
+ yymsp[-4].minor.yy322 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy254, yymsp[0].minor.yy528);
}
break;
- case 150: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ case 162: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
{
- sqlite3Insert(pParse, yymsp[-3].minor.yy135, yymsp[-1].minor.yy489, yymsp[-2].minor.yy48, yymsp[-5].minor.yy70, yymsp[0].minor.yy340);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy131, yymsp[-1].minor.yy47, yymsp[-2].minor.yy254, yymsp[-5].minor.yy394, yymsp[0].minor.yy444);
}
break;
- case 151: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
+ case 163: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
{
- sqlite3Insert(pParse, yymsp[-3].minor.yy135, 0, yymsp[-2].minor.yy48, yymsp[-5].minor.yy70, 0);
+ sqlite3Insert(pParse, yymsp[-4].minor.yy131, 0, yymsp[-3].minor.yy254, yymsp[-6].minor.yy394, 0);
}
break;
- case 152: /* upsert ::= */
-{ yymsp[1].minor.yy340 = 0; }
+ case 164: /* upsert ::= */
+{ yymsp[1].minor.yy444 = 0; }
break;
- case 153: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
-{ yymsp[-10].minor.yy340 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy420,yymsp[-5].minor.yy18,yymsp[-1].minor.yy420,yymsp[0].minor.yy18);}
+ case 165: /* upsert ::= RETURNING selcollist */
+{ yymsp[-1].minor.yy444 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy322); }
break;
- case 154: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
-{ yymsp[-7].minor.yy340 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy420,yymsp[-2].minor.yy18,0,0); }
+ case 166: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
+{ yymsp[-11].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy322,yymsp[-6].minor.yy528,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,yymsp[0].minor.yy444);}
break;
- case 155: /* upsert ::= ON CONFLICT DO NOTHING */
-{ yymsp[-3].minor.yy340 = sqlite3UpsertNew(pParse->db,0,0,0,0); }
+ case 167: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
+{ yymsp[-8].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy322,yymsp[-3].minor.yy528,0,0,yymsp[0].minor.yy444); }
break;
- case 159: /* idlist_opt ::= LP idlist RP */
-{yymsp[-2].minor.yy48 = yymsp[-1].minor.yy48;}
+ case 168: /* upsert ::= ON CONFLICT DO NOTHING returning */
+{ yymsp[-4].minor.yy444 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
break;
- case 160: /* idlist ::= idlist COMMA nm */
-{yymsp[-2].minor.yy48 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy48,&yymsp[0].minor.yy0);}
+ case 169: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
+{ yymsp[-7].minor.yy444 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,0);}
break;
- case 161: /* idlist ::= nm */
-{yymsp[0].minor.yy48 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
+ case 170: /* returning ::= RETURNING selcollist */
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy322);}
break;
- case 162: /* expr ::= LP expr RP */
-{yymsp[-2].minor.yy18 = yymsp[-1].minor.yy18;}
+ case 173: /* idlist_opt ::= */
+{yymsp[1].minor.yy254 = 0;}
break;
- case 163: /* expr ::= ID|INDEXED */
- case 164: /* expr ::= JOIN_KW */ yytestcase(yyruleno==164);
-{yymsp[0].minor.yy18=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 174: /* idlist_opt ::= LP idlist RP */
+{yymsp[-2].minor.yy254 = yymsp[-1].minor.yy254;}
break;
- case 165: /* expr ::= nm DOT nm */
+ case 175: /* idlist ::= idlist COMMA nm */
+{yymsp[-2].minor.yy254 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
+ break;
+ case 176: /* idlist ::= nm */
+{yymsp[0].minor.yy254 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
+ break;
+ case 177: /* expr ::= LP expr RP */
+{yymsp[-2].minor.yy528 = yymsp[-1].minor.yy528;}
+ break;
+ case 178: /* expr ::= ID|INDEXED|JOIN_KW */
+{yymsp[0].minor.yy528=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ break;
+ case 179: /* expr ::= nm DOT nm */
{
- Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
- Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
- if( IN_RENAME_OBJECT ){
- sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0);
- sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0);
- }
- yylhsminor.yy18 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
+ Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0);
+ Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0);
+ yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
- yymsp[-2].minor.yy18 = yylhsminor.yy18;
+ yymsp[-2].minor.yy528 = yylhsminor.yy528;
break;
- case 166: /* expr ::= nm DOT nm DOT nm */
+ case 180: /* expr ::= nm DOT nm DOT nm */
{
- Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1);
- Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
- Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
+ Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-4].minor.yy0);
+ Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0);
+ Expr *temp3 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0);
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3);
if( IN_RENAME_OBJECT ){
- sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0);
- sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0);
+ sqlite3RenameTokenRemap(pParse, 0, temp1);
}
- yylhsminor.yy18 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
+ yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
- yymsp[-4].minor.yy18 = yylhsminor.yy18;
+ yymsp[-4].minor.yy528 = yylhsminor.yy528;
break;
- case 167: /* term ::= NULL|FLOAT|BLOB */
- case 168: /* term ::= STRING */ yytestcase(yyruleno==168);
-{yymsp[0].minor.yy18=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 181: /* term ::= NULL|FLOAT|BLOB */
+ case 182: /* term ::= STRING */ yytestcase(yyruleno==182);
+{yymsp[0].minor.yy528=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 169: /* term ::= INTEGER */
+ case 183: /* term ::= INTEGER */
{
- yylhsminor.yy18 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
+ yylhsminor.yy528 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
+ if( yylhsminor.yy528 ) yylhsminor.yy528->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail);
}
- yymsp[0].minor.yy18 = yylhsminor.yy18;
+ yymsp[0].minor.yy528 = yylhsminor.yy528;
break;
- case 170: /* expr ::= VARIABLE */
+ case 184: /* expr ::= VARIABLE */
{
if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
u32 n = yymsp[0].minor.yy0.n;
- yymsp[0].minor.yy18 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
- sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy18, n);
+ yymsp[0].minor.yy528 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy528, n);
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
@@ -150238,154 +175258,194 @@ static YYACTIONTYPE yy_reduce(
assert( t.n>=2 );
if( pParse->nested==0 ){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
- yymsp[0].minor.yy18 = 0;
+ yymsp[0].minor.yy528 = 0;
}else{
- yymsp[0].minor.yy18 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
- if( yymsp[0].minor.yy18 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy18->iTable);
+ yymsp[0].minor.yy528 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
+ if( yymsp[0].minor.yy528 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy528->iTable);
}
}
}
break;
- case 171: /* expr ::= expr COLLATE ID|STRING */
+ case 185: /* expr ::= expr COLLATE ID|STRING */
{
- yymsp[-2].minor.yy18 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy18, &yymsp[0].minor.yy0, 1);
+ yymsp[-2].minor.yy528 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy528, &yymsp[0].minor.yy0, 1);
}
break;
- case 172: /* expr ::= CAST LP expr AS typetoken RP */
+ case 186: /* expr ::= CAST LP expr AS typetoken RP */
{
- yymsp[-5].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
- sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy18, yymsp[-3].minor.yy18, 0);
+ yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
+ sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy528, yymsp[-3].minor.yy528, 0);
}
break;
- case 173: /* expr ::= ID|INDEXED LP distinct exprlist RP */
+ case 187: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
{
- yylhsminor.yy18 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy420, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy70);
+ yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy394);
}
- yymsp[-4].minor.yy18 = yylhsminor.yy18;
+ yymsp[-4].minor.yy528 = yylhsminor.yy528;
break;
- case 174: /* expr ::= ID|INDEXED LP STAR RP */
+ case 188: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
{
- yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
+ yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy322, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy394);
+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy528, yymsp[-1].minor.yy322);
}
- yymsp[-3].minor.yy18 = yylhsminor.yy18;
+ yymsp[-7].minor.yy528 = yylhsminor.yy528;
break;
- case 175: /* expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
+ case 189: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
{
- yylhsminor.yy18 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy420, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy70);
- sqlite3WindowAttach(pParse, yylhsminor.yy18, yymsp[0].minor.yy327);
+ yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
- yymsp[-5].minor.yy18 = yylhsminor.yy18;
+ yymsp[-3].minor.yy528 = yylhsminor.yy528;
break;
- case 176: /* expr ::= ID|INDEXED LP STAR RP over_clause */
+ case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
{
- yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
- sqlite3WindowAttach(pParse, yylhsminor.yy18, yymsp[0].minor.yy327);
+ yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy322, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy394);
+ sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
}
- yymsp[-4].minor.yy18 = yylhsminor.yy18;
+ yymsp[-5].minor.yy528 = yylhsminor.yy528;
break;
- case 177: /* term ::= CTIME_KW */
+ case 191: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
{
- yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
+ yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy322, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy394);
+ sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy528, yymsp[-2].minor.yy322);
}
- yymsp[0].minor.yy18 = yylhsminor.yy18;
+ yymsp[-8].minor.yy528 = yylhsminor.yy528;
break;
- case 178: /* expr ::= LP nexprlist COMMA expr RP */
+ case 192: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
{
- ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy420, yymsp[-1].minor.yy18);
- yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
- if( yymsp[-4].minor.yy18 ){
- yymsp[-4].minor.yy18->x.pList = pList;
+ yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
+ sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
+}
+ yymsp[-4].minor.yy528 = yylhsminor.yy528;
+ break;
+ case 193: /* term ::= CTIME_KW */
+{
+ yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
+}
+ yymsp[0].minor.yy528 = yylhsminor.yy528;
+ break;
+ case 194: /* expr ::= LP nexprlist COMMA expr RP */
+{
+ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528);
+ yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
+ if( yymsp[-4].minor.yy528 ){
+ yymsp[-4].minor.yy528->x.pList = pList;
+ if( ALWAYS(pList->nExpr) ){
+ yymsp[-4].minor.yy528->flags |= pList->a[0].pExpr->flags & EP_Propagate;
+ }
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
}
break;
- case 179: /* expr ::= expr AND expr */
- case 180: /* expr ::= expr OR expr */ yytestcase(yyruleno==180);
- case 181: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==181);
- case 182: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==182);
- case 183: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==183);
- case 184: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==184);
- case 185: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==185);
- case 186: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==186);
-{yymsp[-2].minor.yy18=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy18,yymsp[0].minor.yy18);}
+ case 195: /* expr ::= expr AND expr */
+{yymsp[-2].minor.yy528=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
+ break;
+ case 196: /* expr ::= expr OR expr */
+ case 197: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==197);
+ case 198: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==198);
+ case 199: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==199);
+ case 200: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==200);
+ case 201: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==201);
+ case 202: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==202);
+{yymsp[-2].minor.yy528=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
break;
- case 187: /* likeop ::= NOT LIKE_KW|MATCH */
+ case 203: /* likeop ::= NOT LIKE_KW|MATCH */
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
break;
- case 188: /* expr ::= expr likeop expr */
+ case 204: /* expr ::= expr likeop expr */
{
ExprList *pList;
int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
yymsp[-1].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy18);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy18);
- yymsp[-2].minor.yy18 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
- if( bNot ) yymsp[-2].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy18, 0);
- if( yymsp[-2].minor.yy18 ) yymsp[-2].minor.yy18->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy528);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy528);
+ yymsp[-2].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+ if( bNot ) yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy528, 0);
+ if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc;
}
break;
- case 189: /* expr ::= expr likeop expr ESCAPE expr */
+ case 205: /* expr ::= expr likeop expr ESCAPE expr */
{
ExprList *pList;
int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
yymsp[-3].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy18);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy18);
- yymsp[-4].minor.yy18 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
- if( bNot ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0);
- if( yymsp[-4].minor.yy18 ) yymsp[-4].minor.yy18->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy528);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528);
+ yymsp[-4].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
+ if( bNot ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
+ if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->flags |= EP_InfixFunc;
}
break;
- case 190: /* expr ::= expr ISNULL|NOTNULL */
-{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy18,0);}
+ case 206: /* expr ::= expr ISNULL|NOTNULL */
+{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);}
+ break;
+ case 207: /* expr ::= expr NOT NULL */
+{yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);}
+ break;
+ case 208: /* expr ::= expr IS expr */
+{
+ yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-2].minor.yy528, TK_ISNULL);
+}
break;
- case 191: /* expr ::= expr NOT NULL */
-{yymsp[-2].minor.yy18 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy18,0);}
+ case 209: /* expr ::= expr IS NOT expr */
+{
+ yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy528,yymsp[0].minor.yy528);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-3].minor.yy528, TK_NOTNULL);
+}
break;
- case 192: /* expr ::= expr IS expr */
+ case 210: /* expr ::= expr IS NOT DISTINCT FROM expr */
{
- yymsp[-2].minor.yy18 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy18,yymsp[0].minor.yy18);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy18, yymsp[-2].minor.yy18, TK_ISNULL);
+ yymsp[-5].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy528,yymsp[0].minor.yy528);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-5].minor.yy528, TK_ISNULL);
}
break;
- case 193: /* expr ::= expr IS NOT expr */
+ case 211: /* expr ::= expr IS DISTINCT FROM expr */
{
- yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy18,yymsp[0].minor.yy18);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy18, yymsp[-3].minor.yy18, TK_NOTNULL);
+ yymsp[-4].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy528,yymsp[0].minor.yy528);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-4].minor.yy528, TK_NOTNULL);
}
break;
- case 194: /* expr ::= NOT expr */
- case 195: /* expr ::= BITNOT expr */ yytestcase(yyruleno==195);
-{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy18, 0);/*A-overwrites-B*/}
+ case 212: /* expr ::= NOT expr */
+ case 213: /* expr ::= BITNOT expr */ yytestcase(yyruleno==213);
+{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy528, 0);/*A-overwrites-B*/}
break;
- case 196: /* expr ::= PLUS|MINUS expr */
+ case 214: /* expr ::= PLUS|MINUS expr */
{
- yymsp[-1].minor.yy18 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy18, 0);
+ yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy528, 0);
/*A-overwrites-B*/
}
break;
- case 197: /* between_op ::= BETWEEN */
- case 200: /* in_op ::= IN */ yytestcase(yyruleno==200);
-{yymsp[0].minor.yy70 = 0;}
+ case 215: /* expr ::= expr PTR expr */
+{
+ ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528);
+ pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528);
+ yylhsminor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+}
+ yymsp[-2].minor.yy528 = yylhsminor.yy528;
break;
- case 199: /* expr ::= expr between_op expr AND expr */
+ case 216: /* between_op ::= BETWEEN */
+ case 219: /* in_op ::= IN */ yytestcase(yyruleno==219);
+{yymsp[0].minor.yy394 = 0;}
+ break;
+ case 218: /* expr ::= expr between_op expr AND expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy18);
- yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy18, 0);
- if( yymsp[-4].minor.yy18 ){
- yymsp[-4].minor.yy18->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528);
+ yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy528, 0);
+ if( yymsp[-4].minor.yy528 ){
+ yymsp[-4].minor.yy528->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
- }
- if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0);
+ }
+ if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
}
break;
- case 202: /* expr ::= expr in_op LP exprlist RP */
+ case 221: /* expr ::= expr in_op LP exprlist RP */
{
- if( yymsp[-1].minor.yy420==0 ){
+ if( yymsp[-1].minor.yy322==0 ){
/* Expressions of the form
**
** expr1 IN ()
@@ -150394,531 +175454,582 @@ static YYACTIONTYPE yy_reduce(
** simplify to constants 0 (false) and 1 (true), respectively,
** regardless of the value of expr1.
*/
- sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy18);
- yymsp[-4].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy70],1);
- }else if( yymsp[-1].minor.yy420->nExpr==1 ){
- /* Expressions of the form:
- **
- ** expr1 IN (?1)
- ** expr1 NOT IN (?2)
- **
- ** with exactly one value on the RHS can be simplified to something
- ** like this:
- **
- ** expr1 == ?1
- ** expr1 <> ?2
- **
- ** But, the RHS of the == or <> is marked with the EP_Generic flag
- ** so that it may not contribute to the computation of comparison
- ** affinity or the collating sequence to use for comparison. Otherwise,
- ** the semantics would be subtly different from IN or NOT IN.
- */
- Expr *pRHS = yymsp[-1].minor.yy420->a[0].pExpr;
- yymsp[-1].minor.yy420->a[0].pExpr = 0;
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy420);
- /* pRHS cannot be NULL because a malloc error would have been detected
- ** before now and control would have never reached this point */
- if( ALWAYS(pRHS) ){
- pRHS->flags &= ~EP_Collate;
- pRHS->flags |= EP_Generic;
- }
- yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, yymsp[-3].minor.yy70 ? TK_NE : TK_EQ, yymsp[-4].minor.yy18, pRHS);
- }else{
- yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0);
- if( yymsp[-4].minor.yy18 ){
- yymsp[-4].minor.yy18->x.pList = yymsp[-1].minor.yy420;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy18);
+ sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy528);
+ yymsp[-4].minor.yy528 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy394 ? "true" : "false");
+ if( yymsp[-4].minor.yy528 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy528);
+ }else{
+ Expr *pRHS = yymsp[-1].minor.yy322->a[0].pExpr;
+ if( yymsp[-1].minor.yy322->nExpr==1 && sqlite3ExprIsConstant(pRHS) && yymsp[-4].minor.yy528->op!=TK_VECTOR ){
+ yymsp[-1].minor.yy322->a[0].pExpr = 0;
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
+ pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0);
+ yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy528, pRHS);
+ }else if( yymsp[-1].minor.yy322->nExpr==1 && pRHS->op==TK_SELECT ){
+ yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pRHS->x.pSelect);
+ pRHS->x.pSelect = 0;
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy420);
+ yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
+ if( yymsp[-4].minor.yy528==0 ){
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
+ }else if( yymsp[-4].minor.yy528->pLeft->op==TK_VECTOR ){
+ int nExpr = yymsp[-4].minor.yy528->pLeft->x.pList->nExpr;
+ Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy322);
+ if( pSelectRHS ){
+ parserDoubleLinkSelect(pParse, pSelectRHS);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelectRHS);
+ }
+ }else{
+ yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy322;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528);
+ }
}
- if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0);
+ if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
}
}
break;
- case 203: /* expr ::= LP select RP */
+ case 222: /* expr ::= LP select RP */
{
- yymsp[-2].minor.yy18 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy18, yymsp[-1].minor.yy489);
+ yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47);
}
break;
- case 204: /* expr ::= expr in_op LP select RP */
+ case 223: /* expr ::= expr in_op LP select RP */
{
- yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy18, yymsp[-1].minor.yy489);
- if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0);
+ yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, yymsp[-1].minor.yy47);
+ if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
}
break;
- case 205: /* expr ::= expr in_op nm dbnm paren_exprlist */
+ case 224: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
- SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
+ SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
- if( yymsp[0].minor.yy420 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy420);
- yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy18, pSelect);
- if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0);
+ if( yymsp[0].minor.yy322 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy322);
+ yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelect);
+ if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
}
break;
- case 206: /* expr ::= EXISTS LP select RP */
+ case 225: /* expr ::= EXISTS LP select RP */
{
Expr *p;
- p = yymsp[-3].minor.yy18 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
- sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy489);
+ p = yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
+ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy47);
}
break;
- case 207: /* expr ::= CASE case_operand case_exprlist case_else END */
+ case 226: /* expr ::= CASE case_operand case_exprlist case_else END */
{
- yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy18, 0);
- if( yymsp[-4].minor.yy18 ){
- yymsp[-4].minor.yy18->x.pList = yymsp[-1].minor.yy18 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy420,yymsp[-1].minor.yy18) : yymsp[-2].minor.yy420;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy18);
+ yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy528, 0);
+ if( yymsp[-4].minor.yy528 ){
+ yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy528 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528) : yymsp[-2].minor.yy322;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy420);
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy18);
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy322);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528);
}
}
break;
- case 208: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ case 227: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
- yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, yymsp[-2].minor.yy18);
- yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, yymsp[0].minor.yy18);
+ yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy528);
+ yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[0].minor.yy528);
}
break;
- case 209: /* case_exprlist ::= WHEN expr THEN expr */
+ case 228: /* case_exprlist ::= WHEN expr THEN expr */
{
- yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18);
- yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy420, yymsp[0].minor.yy18);
+ yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
+ yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528);
}
break;
- case 212: /* case_operand ::= expr */
-{yymsp[0].minor.yy18 = yymsp[0].minor.yy18; /*A-overwrites-X*/}
+ case 233: /* nexprlist ::= nexprlist COMMA expr */
+{yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);}
break;
- case 215: /* nexprlist ::= nexprlist COMMA expr */
-{yymsp[-2].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy420,yymsp[0].minor.yy18);}
+ case 234: /* nexprlist ::= expr */
+{yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/}
break;
- case 216: /* nexprlist ::= expr */
-{yymsp[0].minor.yy420 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy18); /*A-overwrites-Y*/}
+ case 236: /* paren_exprlist ::= LP exprlist RP */
+ case 241: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==241);
+{yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;}
break;
- case 218: /* paren_exprlist ::= LP exprlist RP */
- case 223: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==223);
-{yymsp[-2].minor.yy420 = yymsp[-1].minor.yy420;}
- break;
- case 219: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ case 237: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
- sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
- sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy420, yymsp[-10].minor.yy70,
- &yymsp[-11].minor.yy0, yymsp[0].minor.yy18, SQLITE_SO_ASC, yymsp[-8].minor.yy70, SQLITE_IDXTYPE_APPDEF);
+ sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
+ sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394,
+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF);
if( IN_RENAME_OBJECT && pParse->pNewIndex ){
sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
}
}
break;
- case 220: /* uniqueflag ::= UNIQUE */
- case 260: /* raisetype ::= ABORT */ yytestcase(yyruleno==260);
-{yymsp[0].minor.yy70 = OE_Abort;}
+ case 238: /* uniqueflag ::= UNIQUE */
+ case 280: /* raisetype ::= ABORT */ yytestcase(yyruleno==280);
+{yymsp[0].minor.yy394 = OE_Abort;}
break;
- case 221: /* uniqueflag ::= */
-{yymsp[1].minor.yy70 = OE_None;}
+ case 239: /* uniqueflag ::= */
+{yymsp[1].minor.yy394 = OE_None;}
break;
- case 224: /* eidlist ::= eidlist COMMA nm collate sortorder */
+ case 242: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
- yymsp[-4].minor.yy420 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy420, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy70, yymsp[0].minor.yy70);
+ yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394);
}
break;
- case 225: /* eidlist ::= nm collate sortorder */
+ case 243: /* eidlist ::= nm collate sortorder */
{
- yymsp[-2].minor.yy420 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy70, yymsp[0].minor.yy70); /*A-overwrites-Y*/
+ yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/
}
break;
- case 228: /* cmd ::= DROP INDEX ifexists fullname */
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy135, yymsp[-1].minor.yy70);}
+ case 246: /* cmd ::= DROP INDEX ifexists fullname */
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);}
break;
- case 229: /* cmd ::= VACUUM */
-{sqlite3Vacuum(pParse,0);}
+ case 247: /* cmd ::= VACUUM vinto */
+{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);}
break;
- case 230: /* cmd ::= VACUUM nm */
-{sqlite3Vacuum(pParse,&yymsp[0].minor.yy0);}
+ case 248: /* cmd ::= VACUUM nm vinto */
+{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);}
break;
- case 231: /* cmd ::= PRAGMA nm dbnm */
+ case 251: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
- case 232: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
+ case 252: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
- case 233: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ case 253: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
- case 234: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
+ case 254: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
break;
- case 235: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ case 255: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
break;
- case 238: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ case 258: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
Token all;
all.z = yymsp[-3].minor.yy0.z;
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy207, &all);
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all);
}
break;
- case 239: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ case 259: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy70, yymsp[-4].minor.yy34.a, yymsp[-4].minor.yy34.b, yymsp[-2].minor.yy135, yymsp[0].minor.yy18, yymsp[-10].minor.yy70, yymsp[-8].minor.yy70);
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394);
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
- case 240: /* trigger_time ::= BEFORE|AFTER */
-{ yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-X*/ }
+ case 260: /* trigger_time ::= BEFORE|AFTER */
+{ yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ }
break;
- case 241: /* trigger_time ::= INSTEAD OF */
-{ yymsp[-1].minor.yy70 = TK_INSTEAD;}
+ case 261: /* trigger_time ::= INSTEAD OF */
+{ yymsp[-1].minor.yy394 = TK_INSTEAD;}
break;
- case 242: /* trigger_time ::= */
-{ yymsp[1].minor.yy70 = TK_BEFORE; }
+ case 262: /* trigger_time ::= */
+{ yymsp[1].minor.yy394 = TK_BEFORE; }
break;
- case 243: /* trigger_event ::= DELETE|INSERT */
- case 244: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==244);
-{yymsp[0].minor.yy34.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy34.b = 0;}
+ case 263: /* trigger_event ::= DELETE|INSERT */
+ case 264: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==264);
+{yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;}
break;
- case 245: /* trigger_event ::= UPDATE OF idlist */
-{yymsp[-2].minor.yy34.a = TK_UPDATE; yymsp[-2].minor.yy34.b = yymsp[0].minor.yy48;}
+ case 265: /* trigger_event ::= UPDATE OF idlist */
+{yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;}
break;
- case 246: /* when_clause ::= */
- case 265: /* key_opt ::= */ yytestcase(yyruleno==265);
- case 307: /* filter_opt ::= */ yytestcase(yyruleno==307);
-{ yymsp[1].minor.yy18 = 0; }
+ case 266: /* when_clause ::= */
+ case 285: /* key_opt ::= */ yytestcase(yyruleno==285);
+{ yymsp[1].minor.yy528 = 0; }
break;
- case 247: /* when_clause ::= WHEN expr */
- case 266: /* key_opt ::= KEY expr */ yytestcase(yyruleno==266);
-{ yymsp[-1].minor.yy18 = yymsp[0].minor.yy18; }
+ case 267: /* when_clause ::= WHEN expr */
+ case 286: /* key_opt ::= KEY expr */ yytestcase(yyruleno==286);
+{ yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; }
break;
- case 248: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ case 268: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
- assert( yymsp[-2].minor.yy207!=0 );
- yymsp[-2].minor.yy207->pLast->pNext = yymsp[-1].minor.yy207;
- yymsp[-2].minor.yy207->pLast = yymsp[-1].minor.yy207;
+ assert( yymsp[-2].minor.yy33!=0 );
+ yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33;
+ yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33;
}
break;
- case 249: /* trigger_cmd_list ::= trigger_cmd SEMI */
-{
- assert( yymsp[-1].minor.yy207!=0 );
- yymsp[-1].minor.yy207->pLast = yymsp[-1].minor.yy207;
+ case 269: /* trigger_cmd_list ::= trigger_cmd SEMI */
+{
+ assert( yymsp[-1].minor.yy33!=0 );
+ yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33;
}
break;
- case 250: /* trnm ::= nm DOT nm */
+ case 270: /* trnm ::= nm DOT nm */
{
yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
- sqlite3ErrorMsg(pParse,
+ sqlite3ErrorMsg(pParse,
"qualified table names are not allowed on INSERT, UPDATE, and DELETE "
"statements within triggers");
}
break;
- case 251: /* tridxby ::= INDEXED BY nm */
+ case 271: /* tridxby ::= INDEXED BY nm */
{
sqlite3ErrorMsg(pParse,
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 252: /* tridxby ::= NOT INDEXED */
+ case 272: /* tridxby ::= NOT INDEXED */
{
sqlite3ErrorMsg(pParse,
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 253: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
-{yylhsminor.yy207 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy420, yymsp[-1].minor.yy18, yymsp[-6].minor.yy70, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy392);}
- yymsp[-7].minor.yy207 = yylhsminor.yy207;
+ case 273: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+{yylhsminor.yy33 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);}
+ yymsp[-8].minor.yy33 = yylhsminor.yy33;
break;
- case 254: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ case 274: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
- yylhsminor.yy207 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy48,yymsp[-2].minor.yy489,yymsp[-6].minor.yy70,yymsp[-1].minor.yy340,yymsp[-7].minor.yy392,yymsp[0].minor.yy392);/*yylhsminor.yy207-overwrites-yymsp[-6].minor.yy70*/
+ yylhsminor.yy33 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/
}
- yymsp[-7].minor.yy207 = yylhsminor.yy207;
+ yymsp[-7].minor.yy33 = yylhsminor.yy33;
break;
- case 255: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-{yylhsminor.yy207 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy18, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy392);}
- yymsp[-5].minor.yy207 = yylhsminor.yy207;
+ case 275: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+{yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);}
+ yymsp[-5].minor.yy33 = yylhsminor.yy33;
break;
- case 256: /* trigger_cmd ::= scanpt select scanpt */
-{yylhsminor.yy207 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy489, yymsp[-2].minor.yy392, yymsp[0].minor.yy392); /*yylhsminor.yy207-overwrites-yymsp[-1].minor.yy489*/}
- yymsp[-2].minor.yy207 = yylhsminor.yy207;
+ case 276: /* trigger_cmd ::= scanpt select scanpt */
+{yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/}
+ yymsp[-2].minor.yy33 = yylhsminor.yy33;
break;
- case 257: /* expr ::= RAISE LP IGNORE RP */
+ case 277: /* expr ::= RAISE LP IGNORE RP */
{
- yymsp[-3].minor.yy18 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
- if( yymsp[-3].minor.yy18 ){
- yymsp[-3].minor.yy18->affinity = OE_Ignore;
+ yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
+ if( yymsp[-3].minor.yy528 ){
+ yymsp[-3].minor.yy528->affExpr = OE_Ignore;
}
}
break;
- case 258: /* expr ::= RAISE LP raisetype COMMA nm RP */
+ case 278: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
- yymsp[-5].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
- if( yymsp[-5].minor.yy18 ) {
- yymsp[-5].minor.yy18->affinity = (char)yymsp[-3].minor.yy70;
+ yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
+ if( yymsp[-5].minor.yy528 ) {
+ yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394;
}
}
break;
- case 259: /* raisetype ::= ROLLBACK */
-{yymsp[0].minor.yy70 = OE_Rollback;}
+ case 279: /* raisetype ::= ROLLBACK */
+{yymsp[0].minor.yy394 = OE_Rollback;}
break;
- case 261: /* raisetype ::= FAIL */
-{yymsp[0].minor.yy70 = OE_Fail;}
+ case 281: /* raisetype ::= FAIL */
+{yymsp[0].minor.yy394 = OE_Fail;}
break;
- case 262: /* cmd ::= DROP TRIGGER ifexists fullname */
+ case 282: /* cmd ::= DROP TRIGGER ifexists fullname */
{
- sqlite3DropTrigger(pParse,yymsp[0].minor.yy135,yymsp[-1].minor.yy70);
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394);
}
break;
- case 263: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ case 283: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
- sqlite3Attach(pParse, yymsp[-3].minor.yy18, yymsp[-1].minor.yy18, yymsp[0].minor.yy18);
+ sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528);
}
break;
- case 264: /* cmd ::= DETACH database_kw_opt expr */
+ case 284: /* cmd ::= DETACH database_kw_opt expr */
{
- sqlite3Detach(pParse, yymsp[0].minor.yy18);
+ sqlite3Detach(pParse, yymsp[0].minor.yy528);
}
break;
- case 267: /* cmd ::= REINDEX */
+ case 287: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
- case 268: /* cmd ::= REINDEX nm dbnm */
+ case 288: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 269: /* cmd ::= ANALYZE */
+ case 289: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
- case 270: /* cmd ::= ANALYZE nm dbnm */
+ case 290: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 271: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+ case 291: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy135,&yymsp[0].minor.yy0);
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0);
}
break;
- case 272: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ case 292: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
break;
- case 273: /* add_column_fullname ::= fullname */
+ case 293: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+{
+ sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0);
+}
+ break;
+ case 294: /* add_column_fullname ::= fullname */
{
disableLookaside(pParse);
- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy135);
+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131);
}
break;
- case 274: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ case 295: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
- sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy135, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
+ sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
break;
- case 275: /* cmd ::= create_vtab */
+ case 296: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
- case 276: /* cmd ::= create_vtab LP vtabarglist RP */
+ case 297: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
- case 277: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ case 298: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy70);
+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394);
}
break;
- case 278: /* vtabarg ::= */
+ case 299: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
- case 279: /* vtabargtoken ::= ANY */
- case 280: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==280);
- case 281: /* lp ::= LP */ yytestcase(yyruleno==281);
+ case 300: /* vtabargtoken ::= ANY */
+ case 301: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==301);
+ case 302: /* lp ::= LP */ yytestcase(yyruleno==302);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
- case 282: /* with ::= WITH wqlist */
- case 283: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==283);
-{ sqlite3WithPush(pParse, yymsp[0].minor.yy449, 1); }
+ case 303: /* with ::= WITH wqlist */
+ case 304: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==304);
+{ sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); }
+ break;
+ case 305: /* wqas ::= AS */
+{yymsp[0].minor.yy516 = M10d_Any;}
+ break;
+ case 306: /* wqas ::= AS MATERIALIZED */
+{yymsp[-1].minor.yy516 = M10d_Yes;}
+ break;
+ case 307: /* wqas ::= AS NOT MATERIALIZED */
+{yymsp[-2].minor.yy516 = M10d_No;}
break;
- case 284: /* wqlist ::= nm eidlist_opt AS LP select RP */
+ case 308: /* wqitem ::= nm eidlist_opt wqas LP select RP */
{
- yymsp[-5].minor.yy449 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy420, yymsp[-1].minor.yy489); /*A-overwrites-X*/
+ yymsp[-5].minor.yy385 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/
}
break;
- case 285: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
+ case 309: /* wqlist ::= wqitem */
{
- yymsp[-7].minor.yy449 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy449, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy420, yymsp[-1].minor.yy489);
+ yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/
}
break;
- case 286: /* windowdefn_list ::= windowdefn */
-{ yylhsminor.yy327 = yymsp[0].minor.yy327; }
- yymsp[0].minor.yy327 = yylhsminor.yy327;
+ case 310: /* wqlist ::= wqlist COMMA wqitem */
+{
+ yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385);
+}
break;
- case 287: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ case 311: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
- assert( yymsp[0].minor.yy327!=0 );
- yymsp[0].minor.yy327->pNextWin = yymsp[-2].minor.yy327;
- yylhsminor.yy327 = yymsp[0].minor.yy327;
+ assert( yymsp[0].minor.yy41!=0 );
+ sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41);
+ yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41;
+ yylhsminor.yy41 = yymsp[0].minor.yy41;
}
- yymsp[-2].minor.yy327 = yylhsminor.yy327;
+ yymsp[-2].minor.yy41 = yylhsminor.yy41;
break;
- case 288: /* windowdefn ::= nm AS window */
+ case 312: /* windowdefn ::= nm AS LP window RP */
{
- if( ALWAYS(yymsp[0].minor.yy327) ){
- yymsp[0].minor.yy327->zName = sqlite3DbStrNDup(pParse->db, yymsp[-2].minor.yy0.z, yymsp[-2].minor.yy0.n);
+ if( ALWAYS(yymsp[-1].minor.yy41) ){
+ yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
}
- yylhsminor.yy327 = yymsp[0].minor.yy327;
+ yylhsminor.yy41 = yymsp[-1].minor.yy41;
}
- yymsp[-2].minor.yy327 = yylhsminor.yy327;
+ yymsp[-4].minor.yy41 = yylhsminor.yy41;
break;
- case 289: /* window ::= LP part_opt orderby_opt frame_opt RP */
+ case 313: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
- yymsp[-4].minor.yy327 = yymsp[-1].minor.yy327;
- if( ALWAYS(yymsp[-4].minor.yy327) ){
- yymsp[-4].minor.yy327->pPartition = yymsp[-3].minor.yy420;
- yymsp[-4].minor.yy327->pOrderBy = yymsp[-2].minor.yy420;
- }
+ yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0);
}
break;
- case 290: /* part_opt ::= PARTITION BY nexprlist */
-{ yymsp[-2].minor.yy420 = yymsp[0].minor.yy420; }
+ case 314: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+{
+ yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0);
+}
+ yymsp[-5].minor.yy41 = yylhsminor.yy41;
break;
- case 291: /* part_opt ::= */
-{ yymsp[1].minor.yy420 = 0; }
+ case 315: /* window ::= ORDER BY sortlist frame_opt */
+{
+ yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0);
+}
break;
- case 292: /* frame_opt ::= */
-{
- yymsp[1].minor.yy327 = sqlite3WindowAlloc(pParse, TK_RANGE, TK_UNBOUNDED, 0, TK_CURRENT, 0);
+ case 316: /* window ::= nm ORDER BY sortlist frame_opt */
+{
+ yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0);
}
+ yymsp[-4].minor.yy41 = yylhsminor.yy41;
break;
- case 293: /* frame_opt ::= range_or_rows frame_bound_s */
-{
- yylhsminor.yy327 = sqlite3WindowAlloc(pParse, yymsp[-1].minor.yy70, yymsp[0].minor.yy119.eType, yymsp[0].minor.yy119.pExpr, TK_CURRENT, 0);
+ case 317: /* window ::= nm frame_opt */
+{
+ yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0);
}
- yymsp[-1].minor.yy327 = yylhsminor.yy327;
+ yymsp[-1].minor.yy41 = yylhsminor.yy41;
break;
- case 294: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */
-{
- yylhsminor.yy327 = sqlite3WindowAlloc(pParse, yymsp[-4].minor.yy70, yymsp[-2].minor.yy119.eType, yymsp[-2].minor.yy119.pExpr, yymsp[0].minor.yy119.eType, yymsp[0].minor.yy119.pExpr);
+ case 318: /* frame_opt ::= */
+{
+ yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
- yymsp[-4].minor.yy327 = yylhsminor.yy327;
break;
- case 295: /* range_or_rows ::= RANGE */
-{ yymsp[0].minor.yy70 = TK_RANGE; }
+ case 319: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+{
+ yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516);
+}
+ yymsp[-2].minor.yy41 = yylhsminor.yy41;
break;
- case 296: /* range_or_rows ::= ROWS */
-{ yymsp[0].minor.yy70 = TK_ROWS; }
+ case 320: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+{
+ yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516);
+}
+ yymsp[-5].minor.yy41 = yylhsminor.yy41;
+ break;
+ case 322: /* frame_bound_s ::= frame_bound */
+ case 324: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==324);
+{yylhsminor.yy595 = yymsp[0].minor.yy595;}
+ yymsp[0].minor.yy595 = yylhsminor.yy595;
+ break;
+ case 323: /* frame_bound_s ::= UNBOUNDED PRECEDING */
+ case 325: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==325);
+ case 327: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==327);
+{yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;}
+ yymsp[-1].minor.yy595 = yylhsminor.yy595;
break;
- case 297: /* frame_bound_s ::= frame_bound */
- case 299: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==299);
-{ yylhsminor.yy119 = yymsp[0].minor.yy119; }
- yymsp[0].minor.yy119 = yylhsminor.yy119;
+ case 326: /* frame_bound ::= expr PRECEDING|FOLLOWING */
+{yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;}
+ yymsp[-1].minor.yy595 = yylhsminor.yy595;
break;
- case 298: /* frame_bound_s ::= UNBOUNDED PRECEDING */
- case 300: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==300);
-{yymsp[-1].minor.yy119.eType = TK_UNBOUNDED; yymsp[-1].minor.yy119.pExpr = 0;}
+ case 328: /* frame_exclude_opt ::= */
+{yymsp[1].minor.yy516 = 0;}
break;
- case 301: /* frame_bound ::= expr PRECEDING */
-{ yylhsminor.yy119.eType = TK_PRECEDING; yylhsminor.yy119.pExpr = yymsp[-1].minor.yy18; }
- yymsp[-1].minor.yy119 = yylhsminor.yy119;
+ case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
+{yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;}
break;
- case 302: /* frame_bound ::= CURRENT ROW */
-{ yymsp[-1].minor.yy119.eType = TK_CURRENT ; yymsp[-1].minor.yy119.pExpr = 0; }
+ case 330: /* frame_exclude ::= NO OTHERS */
+ case 331: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==331);
+{yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/}
break;
- case 303: /* frame_bound ::= expr FOLLOWING */
-{ yylhsminor.yy119.eType = TK_FOLLOWING; yylhsminor.yy119.pExpr = yymsp[-1].minor.yy18; }
- yymsp[-1].minor.yy119 = yylhsminor.yy119;
+ case 332: /* frame_exclude ::= GROUP|TIES */
+{yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/}
break;
- case 304: /* window_clause ::= WINDOW windowdefn_list */
-{ yymsp[-1].minor.yy327 = yymsp[0].minor.yy327; }
+ case 333: /* window_clause ::= WINDOW windowdefn_list */
+{ yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; }
+ break;
+ case 334: /* filter_over ::= filter_clause over_clause */
+{
+ if( yymsp[0].minor.yy41 ){
+ yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528;
+ }else{
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528);
+ }
+ yylhsminor.yy41 = yymsp[0].minor.yy41;
+}
+ yymsp[-1].minor.yy41 = yylhsminor.yy41;
break;
- case 305: /* over_clause ::= filter_opt OVER window */
+ case 335: /* filter_over ::= over_clause */
{
- yylhsminor.yy327 = yymsp[0].minor.yy327;
- assert( yylhsminor.yy327!=0 );
- yylhsminor.yy327->pFilter = yymsp[-2].minor.yy18;
+ yylhsminor.yy41 = yymsp[0].minor.yy41;
}
- yymsp[-2].minor.yy327 = yylhsminor.yy327;
+ yymsp[0].minor.yy41 = yylhsminor.yy41;
break;
- case 306: /* over_clause ::= filter_opt OVER nm */
+ case 336: /* filter_over ::= filter_clause */
{
- yylhsminor.yy327 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
- if( yylhsminor.yy327 ){
- yylhsminor.yy327->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
- yylhsminor.yy327->pFilter = yymsp[-2].minor.yy18;
+ yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yylhsminor.yy41 ){
+ yylhsminor.yy41->eFrmType = TK_FILTER;
+ yylhsminor.yy41->pFilter = yymsp[0].minor.yy528;
}else{
- sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy18);
+ sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528);
+ }
+}
+ yymsp[0].minor.yy41 = yylhsminor.yy41;
+ break;
+ case 337: /* over_clause ::= OVER LP window RP */
+{
+ yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41;
+ assert( yymsp[-3].minor.yy41!=0 );
+}
+ break;
+ case 338: /* over_clause ::= OVER nm */
+{
+ yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yymsp[-1].minor.yy41 ){
+ yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
}
}
- yymsp[-2].minor.yy327 = yylhsminor.yy327;
break;
- case 308: /* filter_opt ::= FILTER LP WHERE expr RP */
-{ yymsp[-4].minor.yy18 = yymsp[-1].minor.yy18; }
+ case 339: /* filter_clause ::= FILTER LP WHERE expr RP */
+{ yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; }
break;
default:
- /* (309) input ::= cmdlist */ yytestcase(yyruleno==309);
- /* (310) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==310);
- /* (311) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=311);
- /* (312) ecmd ::= SEMI */ yytestcase(yyruleno==312);
- /* (313) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==313);
- /* (314) ecmd ::= explain cmdx */ yytestcase(yyruleno==314);
- /* (315) trans_opt ::= */ yytestcase(yyruleno==315);
- /* (316) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==316);
- /* (317) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==317);
- /* (318) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==318);
- /* (319) savepoint_opt ::= */ yytestcase(yyruleno==319);
- /* (320) cmd ::= create_table create_table_args */ yytestcase(yyruleno==320);
- /* (321) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==321);
- /* (322) columnlist ::= columnname carglist */ yytestcase(yyruleno==322);
- /* (323) nm ::= ID|INDEXED */ yytestcase(yyruleno==323);
- /* (324) nm ::= STRING */ yytestcase(yyruleno==324);
- /* (325) nm ::= JOIN_KW */ yytestcase(yyruleno==325);
- /* (326) typetoken ::= typename */ yytestcase(yyruleno==326);
- /* (327) typename ::= ID|STRING */ yytestcase(yyruleno==327);
- /* (328) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=328);
- /* (329) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=329);
- /* (330) carglist ::= carglist ccons */ yytestcase(yyruleno==330);
- /* (331) carglist ::= */ yytestcase(yyruleno==331);
- /* (332) ccons ::= NULL onconf */ yytestcase(yyruleno==332);
- /* (333) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==333);
- /* (334) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==334);
- /* (335) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=335);
- /* (336) tconscomma ::= */ yytestcase(yyruleno==336);
- /* (337) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=337);
- /* (338) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=338);
- /* (339) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=339);
- /* (340) oneselect ::= values */ yytestcase(yyruleno==340);
- /* (341) sclp ::= selcollist COMMA */ yytestcase(yyruleno==341);
- /* (342) as ::= ID|STRING */ yytestcase(yyruleno==342);
- /* (343) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=343);
- /* (344) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==344);
- /* (345) exprlist ::= nexprlist */ yytestcase(yyruleno==345);
- /* (346) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=346);
- /* (347) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=347);
- /* (348) nmnum ::= ON */ yytestcase(yyruleno==348);
- /* (349) nmnum ::= DELETE */ yytestcase(yyruleno==349);
- /* (350) nmnum ::= DEFAULT */ yytestcase(yyruleno==350);
- /* (351) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==351);
- /* (352) foreach_clause ::= */ yytestcase(yyruleno==352);
- /* (353) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==353);
- /* (354) trnm ::= nm */ yytestcase(yyruleno==354);
- /* (355) tridxby ::= */ yytestcase(yyruleno==355);
- /* (356) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==356);
- /* (357) database_kw_opt ::= */ yytestcase(yyruleno==357);
- /* (358) kwcolumn_opt ::= */ yytestcase(yyruleno==358);
- /* (359) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==359);
- /* (360) vtabarglist ::= vtabarg */ yytestcase(yyruleno==360);
- /* (361) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==361);
- /* (362) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==362);
- /* (363) anylist ::= */ yytestcase(yyruleno==363);
- /* (364) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==364);
- /* (365) anylist ::= anylist ANY */ yytestcase(yyruleno==365);
- /* (366) with ::= */ yytestcase(yyruleno==366);
+ /* (340) input ::= cmdlist */ yytestcase(yyruleno==340);
+ /* (341) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==341);
+ /* (342) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=342);
+ /* (343) ecmd ::= SEMI */ yytestcase(yyruleno==343);
+ /* (344) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==344);
+ /* (345) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=345);
+ /* (346) trans_opt ::= */ yytestcase(yyruleno==346);
+ /* (347) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==347);
+ /* (348) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==348);
+ /* (349) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==349);
+ /* (350) savepoint_opt ::= */ yytestcase(yyruleno==350);
+ /* (351) cmd ::= create_table create_table_args */ yytestcase(yyruleno==351);
+ /* (352) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=352);
+ /* (353) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==353);
+ /* (354) columnlist ::= columnname carglist */ yytestcase(yyruleno==354);
+ /* (355) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==355);
+ /* (356) nm ::= STRING */ yytestcase(yyruleno==356);
+ /* (357) typetoken ::= typename */ yytestcase(yyruleno==357);
+ /* (358) typename ::= ID|STRING */ yytestcase(yyruleno==358);
+ /* (359) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=359);
+ /* (360) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=360);
+ /* (361) carglist ::= carglist ccons */ yytestcase(yyruleno==361);
+ /* (362) carglist ::= */ yytestcase(yyruleno==362);
+ /* (363) ccons ::= NULL onconf */ yytestcase(yyruleno==363);
+ /* (364) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==364);
+ /* (365) ccons ::= AS generated */ yytestcase(yyruleno==365);
+ /* (366) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==366);
+ /* (367) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==367);
+ /* (368) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=368);
+ /* (369) tconscomma ::= */ yytestcase(yyruleno==369);
+ /* (370) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=370);
+ /* (371) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=371);
+ /* (372) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=372);
+ /* (373) oneselect ::= values */ yytestcase(yyruleno==373);
+ /* (374) sclp ::= selcollist COMMA */ yytestcase(yyruleno==374);
+ /* (375) as ::= ID|STRING */ yytestcase(yyruleno==375);
+ /* (376) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=376);
+ /* (377) returning ::= */ yytestcase(yyruleno==377);
+ /* (378) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=378);
+ /* (379) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==379);
+ /* (380) case_operand ::= expr */ yytestcase(yyruleno==380);
+ /* (381) exprlist ::= nexprlist */ yytestcase(yyruleno==381);
+ /* (382) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=382);
+ /* (383) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=383);
+ /* (384) nmnum ::= ON */ yytestcase(yyruleno==384);
+ /* (385) nmnum ::= DELETE */ yytestcase(yyruleno==385);
+ /* (386) nmnum ::= DEFAULT */ yytestcase(yyruleno==386);
+ /* (387) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==387);
+ /* (388) foreach_clause ::= */ yytestcase(yyruleno==388);
+ /* (389) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==389);
+ /* (390) trnm ::= nm */ yytestcase(yyruleno==390);
+ /* (391) tridxby ::= */ yytestcase(yyruleno==391);
+ /* (392) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==392);
+ /* (393) database_kw_opt ::= */ yytestcase(yyruleno==393);
+ /* (394) kwcolumn_opt ::= */ yytestcase(yyruleno==394);
+ /* (395) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==395);
+ /* (396) vtabarglist ::= vtabarg */ yytestcase(yyruleno==396);
+ /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==397);
+ /* (398) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==398);
+ /* (399) anylist ::= */ yytestcase(yyruleno==399);
+ /* (400) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==400);
+ /* (401) anylist ::= anylist ANY */ yytestcase(yyruleno==401);
+ /* (402) with ::= */ yytestcase(yyruleno==402);
+ /* (403) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=403);
+ /* (404) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=404);
break;
/********** End reduce actions ************************************************/
};
- assert( yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) );
- yygoto = yyRuleInfo[yyruleno].lhs;
- yysize = yyRuleInfo[yyruleno].nrhs;
+ assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) );
+ yygoto = yyRuleInfoLhs[yyruleno];
+ yysize = yyRuleInfoNRhs[yyruleno];
yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto);
/* There are no SHIFTREDUCE actions on nonterminals because the table
@@ -151064,12 +176175,56 @@ SQLITE_PRIVATE void sqlite3Parser(
}
#endif
- do{
+ while(1){ /* Exit by "break" */
+ assert( yypParser->yytos>=yypParser->yystack );
assert( yyact==yypParser->yytos->stateno );
yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact);
if( yyact >= YY_MIN_REDUCE ){
- yyact = yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor,
- yyminor sqlite3ParserCTX_PARAM);
+ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */
+#ifndef NDEBUG
+ assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) );
+ if( yyTraceFILE ){
+ int yysize = yyRuleInfoNRhs[yyruleno];
+ if( yysize ){
+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
+ yyTracePrompt,
+ yyruleno, yyRuleName[yyruleno],
+ yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action",
+ yypParser->yytos[yysize].stateno);
+ }else{
+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n",
+ yyTracePrompt, yyruleno, yyRuleName[yyruleno],
+ yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action");
+ }
+ }
+#endif /* NDEBUG */
+
+ /* Check that the stack is large enough to grow by a single entry
+ ** if the RHS of the rule is empty. This ensures that there is room
+ ** enough on the stack to push the LHS value */
+ if( yyRuleInfoNRhs[yyruleno]==0 ){
+#ifdef YYTRACKMAXSTACKDEPTH
+ if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
+ yypParser->yyhwm++;
+ assert( yypParser->yyhwm ==
+ (int)(yypParser->yytos - yypParser->yystack));
+ }
+#endif
+#if YYSTACKDEPTH>0
+ if( yypParser->yytos>=yypParser->yystackEnd ){
+ yyStackOverflow(yypParser);
+ break;
+ }
+#else
+ if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
+ if( yyGrowStack(yypParser) ){
+ yyStackOverflow(yypParser);
+ break;
+ }
+ }
+#endif
+ }
+ yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor sqlite3ParserCTX_PARAM);
}else if( yyact <= YY_MAX_SHIFTREDUCE ){
yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
#ifndef YYNOERRORRECOVERY
@@ -151094,7 +176249,7 @@ SQLITE_PRIVATE void sqlite3Parser(
#ifdef YYERRORSYMBOL
/* A syntax error has occurred.
** The response to an error depends upon whether or not the
- ** grammar defines an error token "ERROR".
+ ** grammar defines an error token "ERROR".
**
** This is what we do if the grammar does define ERROR:
**
@@ -151125,14 +176280,13 @@ SQLITE_PRIVATE void sqlite3Parser(
yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
yymajor = YYNOCODE;
}else{
- while( yypParser->yytos >= yypParser->yystack
- && (yyact = yy_find_reduce_action(
- yypParser->yytos->stateno,
- YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE
- ){
+ while( yypParser->yytos > yypParser->yystack ){
+ yyact = yy_find_reduce_action(yypParser->yytos->stateno,
+ YYERRORSYMBOL);
+ if( yyact<=YY_MAX_SHIFTREDUCE ) break;
yy_pop_parser_stack(yypParser);
}
- if( yypParser->yytos < yypParser->yystack || yymajor==0 ){
+ if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
yy_parse_failed(yypParser);
#ifndef YYNOERRORRECOVERY
@@ -151182,7 +176336,7 @@ SQLITE_PRIVATE void sqlite3Parser(
break;
#endif
}
- }while( yypParser->yytos>yypParser->yystack );
+ }
#ifndef NDEBUG
if( yyTraceFILE ){
yyStackEntry *i;
@@ -151204,13 +176358,12 @@ SQLITE_PRIVATE void sqlite3Parser(
*/
SQLITE_PRIVATE int sqlite3ParserFallback(int iToken){
#ifdef YYFALLBACK
- if( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ){
- return yyFallback[iToken];
- }
+ assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) );
+ return yyFallback[iToken];
#else
(void)iToken;
-#endif
return 0;
+#endif
}
/************** End of parse.c ***********************************************/
@@ -151244,8 +176397,8 @@ SQLITE_PRIVATE int sqlite3ParserFallback(int iToken){
** all of them need to be used within the switch.
*/
#define CC_X 0 /* The letter 'x', or start of BLOB literal */
-#define CC_KYWD 1 /* Alphabetics or '_'. Usable in a keyword */
-#define CC_ID 2 /* unicode characters usable in IDs */
+#define CC_KYWD0 1 /* First letter of a keyword */
+#define CC_KYWD 2 /* Alphabetics or '_'. Usable in a keyword */
#define CC_DIGIT 3 /* Digits */
#define CC_DOLLAR 4 /* '$' */
#define CC_VARALPHA 5 /* '@', '#', ':'. Alphabetic SQL variables */
@@ -151270,47 +176423,49 @@ SQLITE_PRIVATE int sqlite3ParserFallback(int iToken){
#define CC_AND 24 /* '&' */
#define CC_TILDA 25 /* '~' */
#define CC_DOT 26 /* '.' */
-#define CC_ILLEGAL 27 /* Illegal character */
-#define CC_NUL 28 /* 0x00 */
+#define CC_ID 27 /* unicode characters usable in IDs */
+#define CC_ILLEGAL 28 /* Illegal character */
+#define CC_NUL 29 /* 0x00 */
+#define CC_BOM 30 /* First byte of UTF8 BOM: 0xEF 0xBB 0xBF */
static const unsigned char aiClass[] = {
#ifdef SQLITE_ASCII
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
-/* 0x */ 28, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27,
-/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 0x */ 29, 28, 28, 28, 28, 28, 28, 28, 28, 7, 7, 28, 7, 7, 28, 28,
+/* 1x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
/* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16,
/* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6,
/* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 9, 27, 27, 27, 1,
+/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 9, 28, 28, 28, 2,
/* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 10, 27, 25, 27,
-/* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-/* 9x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-/* Ax */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-/* Bx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-/* Cx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-/* Dx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-/* Ex */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-/* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
+/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 28, 10, 28, 25, 28,
+/* 8x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 9x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Ax */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Cx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Dx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Ex */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 30,
+/* Fx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27
#endif
#ifdef SQLITE_EBCDIC
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
-/* 0x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 7, 7, 27, 27,
-/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
-/* 2x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
-/* 3x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
-/* 4x */ 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 12, 17, 20, 10,
-/* 5x */ 24, 27, 27, 27, 27, 27, 27, 27, 27, 27, 15, 4, 21, 18, 19, 27,
-/* 6x */ 11, 16, 27, 27, 27, 27, 27, 27, 27, 27, 27, 23, 22, 1, 13, 6,
-/* 7x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 5, 5, 5, 8, 14, 8,
-/* 8x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
-/* 9x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
-/* Ax */ 27, 25, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27,
-/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 9, 27, 27, 27, 27, 27,
-/* Cx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
-/* Dx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
-/* Ex */ 27, 27, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27,
-/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 27, 27, 27, 27, 27, 27,
+/* 0x */ 29, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 7, 7, 28, 28,
+/* 1x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+/* 2x */ 28, 28, 28, 28, 28, 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+/* 3x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+/* 4x */ 7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 12, 17, 20, 10,
+/* 5x */ 24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 15, 4, 21, 18, 19, 28,
+/* 6x */ 11, 16, 28, 28, 28, 28, 28, 28, 28, 28, 28, 23, 22, 2, 13, 6,
+/* 7x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 8, 5, 5, 5, 8, 14, 8,
+/* 8x */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28,
+/* 9x */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28,
+/* Ax */ 28, 25, 1, 1, 1, 1, 1, 0, 2, 2, 28, 28, 28, 28, 28, 28,
+/* Bx */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 9, 28, 28, 28, 28, 28,
+/* Cx */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28,
+/* Dx */ 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, 28, 28, 28, 28, 28,
+/* Ex */ 28, 28, 1, 1, 1, 1, 1, 0, 2, 2, 28, 28, 28, 28, 28, 28,
+/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 28, 28, 28, 28, 28, 28,
#endif
};
@@ -151319,7 +176474,7 @@ static const unsigned char aiClass[] = {
** lower-case ASCII equivalent. On ASCII machines, this is just
** an upper-to-lower case map. On EBCDIC machines we also need
** to adjust the encoding. The mapping is only valid for alphabetics
-** which are the only characters for which this feature is used.
+** which are the only characters for which this feature is used.
**
** Used by keywordhash.h
*/
@@ -151351,7 +176506,7 @@ const unsigned char ebcdicToAscii[] = {
/*
** The sqlite3KeywordCode function looks up an identifier to determine if
-** it is a keyword. If it is a keyword, the token code of that keyword is
+** it is a keyword. If it is a keyword, the token code of that keyword is
** returned. If the input is not a keyword, TK_ID is returned.
**
** The implementation of this routine was generated by a program,
@@ -151375,314 +176530,467 @@ const unsigned char ebcdicToAscii[] = {
** is substantially reduced. This is important for embedded applications
** on platforms with limited memory.
*/
-/* Hash score: 208 */
-/* zKWText[] encodes 923 bytes of keyword text in 614 bytes */
+/* Hash score: 231 */
+/* zKWText[] encodes 1007 bytes of keyword text in 667 bytes */
/* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */
-/* ABLEFTHENDEFERRABLELSEXCEPTRANSACTIONATURALTERAISEXCLUSIVE */
-/* XISTSAVEPOINTERSECTRIGGEREFERENCESCONSTRAINTOFFSETEMPORARY */
-/* UNIQUERYWITHOUTERELEASEATTACHAVINGROUPDATEBEGINNERANGEBETWEEN */
-/* OTHINGLOBYCASCADELETECASECOLLATECREATECURRENT_DATEDETACH */
-/* IMMEDIATEJOINSERTLIKEMATCHPLANALYZEPRAGMABORTVALUESVIRTUALIMIT */
-/* WHENOTNULLWHERECURSIVEAFTERENAMEANDEFAULTAUTOINCREMENTCAST */
-/* COLUMNCOMMITCONFLICTCROSSCURRENT_TIMESTAMPARTITIONDEFERRED */
-/* ISTINCTDROPRECEDINGFAILFILTEREPLACEFOLLOWINGFROMFULLIFISNULL */
-/* ORDERESTRICTOVERIGHTROLLBACKROWSUNBOUNDEDUNIONUSINGVACUUMVIEW */
-/* INDOWINITIALLYPRIMARY */
-static const char zKWText[613] = {
+/* ABLEFTHENDEFERRABLELSEXCLUDELETEMPORARYISNULLSAVEPOINTERSECT */
+/* IESNOTNULLIKEXCEPTRANSACTIONATURALTERAISEXCLUSIVEXISTS */
+/* CONSTRAINTOFFSETRIGGERANGENERATEDETACHAVINGLOBEGINNEREFERENCES */
+/* UNIQUERYWITHOUTERELEASEATTACHBETWEENOTHINGROUPSCASCADEFAULT */
+/* CASECOLLATECREATECURRENT_DATEIMMEDIATEJOINSERTMATCHPLANALYZE */
+/* PRAGMATERIALIZEDEFERREDISTINCTUPDATEVALUESVIRTUALWAYSWHENWHERE */
+/* CURSIVEABORTAFTERENAMEANDROPARTITIONAUTOINCREMENTCASTCOLUMN */
+/* COMMITCONFLICTCROSSCURRENT_TIMESTAMPRECEDINGFAILASTFILTER */
+/* EPLACEFIRSTFOLLOWINGFROMFULLIMITIFORDERESTRICTOTHERSOVER */
+/* ETURNINGRIGHTROLLBACKROWSUNBOUNDEDUNIONUSINGVACUUMVIEWINDOWBY */
+/* INITIALLYPRIMARY */
+static const char zKWText[666] = {
'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H',
'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G',
'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A',
'S','E','L','E','C','T','A','B','L','E','F','T','H','E','N','D','E','F',
- 'E','R','R','A','B','L','E','L','S','E','X','C','E','P','T','R','A','N',
- 'S','A','C','T','I','O','N','A','T','U','R','A','L','T','E','R','A','I',
- 'S','E','X','C','L','U','S','I','V','E','X','I','S','T','S','A','V','E',
- 'P','O','I','N','T','E','R','S','E','C','T','R','I','G','G','E','R','E',
- 'F','E','R','E','N','C','E','S','C','O','N','S','T','R','A','I','N','T',
- 'O','F','F','S','E','T','E','M','P','O','R','A','R','Y','U','N','I','Q',
- 'U','E','R','Y','W','I','T','H','O','U','T','E','R','E','L','E','A','S',
- 'E','A','T','T','A','C','H','A','V','I','N','G','R','O','U','P','D','A',
- 'T','E','B','E','G','I','N','N','E','R','A','N','G','E','B','E','T','W',
- 'E','E','N','O','T','H','I','N','G','L','O','B','Y','C','A','S','C','A',
- 'D','E','L','E','T','E','C','A','S','E','C','O','L','L','A','T','E','C',
- 'R','E','A','T','E','C','U','R','R','E','N','T','_','D','A','T','E','D',
- 'E','T','A','C','H','I','M','M','E','D','I','A','T','E','J','O','I','N',
- 'S','E','R','T','L','I','K','E','M','A','T','C','H','P','L','A','N','A',
- 'L','Y','Z','E','P','R','A','G','M','A','B','O','R','T','V','A','L','U',
- 'E','S','V','I','R','T','U','A','L','I','M','I','T','W','H','E','N','O',
- 'T','N','U','L','L','W','H','E','R','E','C','U','R','S','I','V','E','A',
- 'F','T','E','R','E','N','A','M','E','A','N','D','E','F','A','U','L','T',
- 'A','U','T','O','I','N','C','R','E','M','E','N','T','C','A','S','T','C',
- 'O','L','U','M','N','C','O','M','M','I','T','C','O','N','F','L','I','C',
- 'T','C','R','O','S','S','C','U','R','R','E','N','T','_','T','I','M','E',
- 'S','T','A','M','P','A','R','T','I','T','I','O','N','D','E','F','E','R',
- 'R','E','D','I','S','T','I','N','C','T','D','R','O','P','R','E','C','E',
- 'D','I','N','G','F','A','I','L','F','I','L','T','E','R','E','P','L','A',
- 'C','E','F','O','L','L','O','W','I','N','G','F','R','O','M','F','U','L',
- 'L','I','F','I','S','N','U','L','L','O','R','D','E','R','E','S','T','R',
- 'I','C','T','O','V','E','R','I','G','H','T','R','O','L','L','B','A','C',
- 'K','R','O','W','S','U','N','B','O','U','N','D','E','D','U','N','I','O',
- 'N','U','S','I','N','G','V','A','C','U','U','M','V','I','E','W','I','N',
- 'D','O','W','I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R',
- 'Y',
+ 'E','R','R','A','B','L','E','L','S','E','X','C','L','U','D','E','L','E',
+ 'T','E','M','P','O','R','A','R','Y','I','S','N','U','L','L','S','A','V',
+ 'E','P','O','I','N','T','E','R','S','E','C','T','I','E','S','N','O','T',
+ 'N','U','L','L','I','K','E','X','C','E','P','T','R','A','N','S','A','C',
+ 'T','I','O','N','A','T','U','R','A','L','T','E','R','A','I','S','E','X',
+ 'C','L','U','S','I','V','E','X','I','S','T','S','C','O','N','S','T','R',
+ 'A','I','N','T','O','F','F','S','E','T','R','I','G','G','E','R','A','N',
+ 'G','E','N','E','R','A','T','E','D','E','T','A','C','H','A','V','I','N',
+ 'G','L','O','B','E','G','I','N','N','E','R','E','F','E','R','E','N','C',
+ 'E','S','U','N','I','Q','U','E','R','Y','W','I','T','H','O','U','T','E',
+ 'R','E','L','E','A','S','E','A','T','T','A','C','H','B','E','T','W','E',
+ 'E','N','O','T','H','I','N','G','R','O','U','P','S','C','A','S','C','A',
+ 'D','E','F','A','U','L','T','C','A','S','E','C','O','L','L','A','T','E',
+ 'C','R','E','A','T','E','C','U','R','R','E','N','T','_','D','A','T','E',
+ 'I','M','M','E','D','I','A','T','E','J','O','I','N','S','E','R','T','M',
+ 'A','T','C','H','P','L','A','N','A','L','Y','Z','E','P','R','A','G','M',
+ 'A','T','E','R','I','A','L','I','Z','E','D','E','F','E','R','R','E','D',
+ 'I','S','T','I','N','C','T','U','P','D','A','T','E','V','A','L','U','E',
+ 'S','V','I','R','T','U','A','L','W','A','Y','S','W','H','E','N','W','H',
+ 'E','R','E','C','U','R','S','I','V','E','A','B','O','R','T','A','F','T',
+ 'E','R','E','N','A','M','E','A','N','D','R','O','P','A','R','T','I','T',
+ 'I','O','N','A','U','T','O','I','N','C','R','E','M','E','N','T','C','A',
+ 'S','T','C','O','L','U','M','N','C','O','M','M','I','T','C','O','N','F',
+ 'L','I','C','T','C','R','O','S','S','C','U','R','R','E','N','T','_','T',
+ 'I','M','E','S','T','A','M','P','R','E','C','E','D','I','N','G','F','A',
+ 'I','L','A','S','T','F','I','L','T','E','R','E','P','L','A','C','E','F',
+ 'I','R','S','T','F','O','L','L','O','W','I','N','G','F','R','O','M','F',
+ 'U','L','L','I','M','I','T','I','F','O','R','D','E','R','E','S','T','R',
+ 'I','C','T','O','T','H','E','R','S','O','V','E','R','E','T','U','R','N',
+ 'I','N','G','R','I','G','H','T','R','O','L','L','B','A','C','K','R','O',
+ 'W','S','U','N','B','O','U','N','D','E','D','U','N','I','O','N','U','S',
+ 'I','N','G','V','A','C','U','U','M','V','I','E','W','I','N','D','O','W',
+ 'B','Y','I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R','Y',
};
/* aKWHash[i] is the hash value for the i-th keyword */
static const unsigned char aKWHash[127] = {
- 74, 109, 124, 72, 106, 45, 0, 0, 81, 0, 76, 61, 0,
- 42, 12, 77, 15, 0, 123, 84, 54, 118, 125, 19, 0, 0,
- 130, 0, 128, 121, 0, 22, 96, 0, 9, 0, 0, 115, 69,
- 0, 67, 6, 0, 48, 93, 136, 0, 126, 104, 0, 0, 44,
- 0, 107, 24, 0, 17, 0, 131, 53, 23, 0, 5, 62, 132,
- 99, 0, 0, 135, 110, 60, 134, 57, 113, 55, 0, 94, 0,
- 103, 26, 0, 102, 0, 0, 0, 98, 95, 100, 105, 117, 14,
- 39, 116, 0, 80, 0, 133, 114, 92, 59, 0, 129, 79, 119,
- 86, 46, 83, 0, 0, 97, 40, 122, 120, 0, 127, 0, 0,
- 29, 0, 89, 87, 88, 0, 20, 85, 111, 56,
+ 84, 92, 134, 82, 105, 29, 0, 0, 94, 0, 85, 72, 0,
+ 53, 35, 86, 15, 0, 42, 97, 54, 89, 135, 19, 0, 0,
+ 140, 0, 40, 129, 0, 22, 107, 0, 9, 0, 0, 123, 80,
+ 0, 78, 6, 0, 65, 103, 147, 0, 136, 115, 0, 0, 48,
+ 0, 90, 24, 0, 17, 0, 27, 70, 23, 26, 5, 60, 142,
+ 110, 122, 0, 73, 91, 71, 145, 61, 120, 74, 0, 49, 0,
+ 11, 41, 0, 113, 0, 0, 0, 109, 10, 111, 116, 125, 14,
+ 50, 124, 0, 100, 0, 18, 121, 144, 56, 130, 139, 88, 83,
+ 37, 30, 126, 0, 0, 108, 51, 131, 128, 0, 34, 0, 0,
+ 132, 0, 98, 38, 39, 0, 20, 45, 117, 93,
};
/* aKWNext[] forms the hash collision chain. If aKWHash[i]==0
** then the i-th keyword has no more hash collisions. Otherwise,
** the next keyword with the same hash is aKWHash[i]-1. */
-static const unsigned char aKWNext[136] = {
- 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 2, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0,
- 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 33, 0, 21, 0, 0, 0, 0, 0, 50,
- 0, 43, 3, 47, 0, 0, 32, 0, 0, 0, 0, 0, 0,
- 0, 1, 64, 0, 0, 65, 0, 41, 0, 38, 0, 0, 0,
- 0, 0, 49, 75, 0, 0, 30, 0, 58, 0, 0, 0, 31,
- 63, 16, 34, 10, 0, 0, 0, 0, 0, 0, 0, 11, 70,
- 91, 0, 0, 8, 0, 108, 0, 101, 28, 52, 68, 0, 112,
- 0, 73, 51, 0, 90, 27, 37, 0, 71, 36, 82, 0, 35,
- 66, 25, 18, 0, 0, 78,
+static const unsigned char aKWNext[148] = {0,
+ 0, 0, 0, 0, 4, 0, 43, 0, 0, 106, 114, 0, 0,
+ 0, 2, 0, 0, 143, 0, 0, 0, 13, 0, 0, 0, 0,
+ 141, 0, 0, 119, 52, 0, 0, 137, 12, 0, 0, 62, 0,
+ 138, 0, 133, 0, 0, 36, 0, 0, 28, 77, 0, 0, 0,
+ 0, 59, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 69, 0, 0, 0, 0, 0, 146, 3, 0, 58, 0, 1,
+ 75, 0, 0, 0, 31, 0, 0, 0, 0, 0, 127, 0, 104,
+ 0, 64, 66, 63, 0, 0, 0, 0, 0, 46, 0, 16, 8,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 101, 0,
+ 112, 21, 7, 67, 0, 79, 96, 118, 0, 0, 68, 0, 0,
+ 99, 44, 0, 55, 0, 76, 0, 95, 32, 33, 57, 25, 0,
+ 102, 0, 0, 87,
};
/* aKWLen[i] is the length (in bytes) of the i-th keyword */
-static const unsigned char aKWLen[136] = {
+static const unsigned char aKWLen[148] = {0,
7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6,
- 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 6,
- 11, 6, 2, 7, 5, 5, 9, 6, 9, 9, 7, 10, 10,
- 4, 6, 2, 3, 9, 4, 2, 6, 5, 7, 4, 5, 7,
- 6, 6, 5, 6, 5, 5, 5, 7, 7, 4, 2, 7, 3,
- 6, 4, 7, 6, 12, 6, 9, 4, 6, 4, 5, 4, 7,
- 6, 5, 6, 7, 5, 4, 7, 3, 2, 4, 5, 9, 5,
- 6, 3, 7, 13, 2, 2, 4, 6, 6, 8, 5, 17, 12,
- 7, 9, 8, 8, 2, 4, 9, 4, 6, 7, 9, 4, 4,
- 2, 6, 5, 8, 4, 5, 8, 4, 3, 9, 5, 5, 6,
- 4, 6, 2, 9, 3, 7,
+ 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 7,
+ 6, 9, 4, 2, 6, 5, 9, 9, 4, 7, 3, 2, 4,
+ 4, 6, 11, 6, 2, 7, 5, 5, 9, 6, 10, 4, 6,
+ 2, 3, 7, 5, 9, 6, 6, 4, 5, 5, 10, 6, 5,
+ 7, 4, 5, 7, 6, 7, 7, 6, 5, 7, 3, 7, 4,
+ 7, 6, 12, 9, 4, 6, 5, 4, 7, 6, 12, 8, 8,
+ 2, 6, 6, 7, 6, 4, 5, 9, 5, 5, 6, 3, 4,
+ 9, 13, 2, 2, 4, 6, 6, 8, 5, 17, 12, 7, 9,
+ 4, 4, 6, 7, 5, 9, 4, 4, 5, 2, 5, 8, 6,
+ 4, 9, 5, 8, 4, 3, 9, 5, 5, 6, 4, 6, 2,
+ 2, 9, 3, 7,
};
/* aKWOffset[i] is the index into zKWText[] of the start of
** the text for the i-th keyword. */
-static const unsigned short int aKWOffset[136] = {
+static const unsigned short int aKWOffset[148] = {0,
0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33,
36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81,
- 86, 91, 95, 96, 101, 105, 109, 117, 122, 128, 136, 142, 152,
- 159, 162, 162, 165, 167, 167, 171, 176, 179, 184, 184, 188, 192,
- 199, 204, 209, 212, 218, 221, 225, 230, 236, 242, 245, 247, 248,
- 252, 258, 262, 269, 275, 287, 293, 302, 304, 310, 314, 319, 321,
- 328, 333, 338, 344, 350, 355, 358, 358, 358, 361, 365, 368, 377,
- 381, 387, 389, 396, 398, 400, 409, 413, 419, 425, 433, 438, 438,
- 438, 454, 463, 470, 471, 478, 481, 490, 494, 499, 506, 515, 519,
- 523, 525, 531, 535, 543, 546, 551, 559, 559, 563, 572, 577, 582,
- 588, 591, 594, 597, 602, 606,
+ 86, 90, 90, 94, 99, 101, 105, 111, 119, 123, 123, 123, 126,
+ 129, 132, 137, 142, 146, 147, 152, 156, 160, 168, 174, 181, 184,
+ 184, 187, 189, 195, 198, 206, 211, 216, 219, 222, 226, 236, 239,
+ 244, 244, 248, 252, 259, 265, 271, 277, 277, 283, 284, 288, 295,
+ 299, 306, 312, 324, 333, 335, 341, 346, 348, 355, 359, 370, 377,
+ 378, 385, 391, 397, 402, 408, 412, 415, 424, 429, 433, 439, 441,
+ 444, 453, 455, 457, 466, 470, 476, 482, 490, 495, 495, 495, 511,
+ 520, 523, 527, 532, 539, 544, 553, 557, 560, 565, 567, 571, 579,
+ 585, 588, 597, 602, 610, 610, 614, 623, 628, 633, 639, 642, 645,
+ 648, 650, 655, 659,
};
/* aKWCode[i] is the parser symbol code for the i-th keyword */
-static const unsigned char aKWCode[136] = {
- TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE,
- TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN,
- TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD,
- TK_ADD, TK_DATABASE, TK_AS, TK_SELECT, TK_TABLE,
- TK_JOIN_KW, TK_THEN, TK_END, TK_DEFERRABLE, TK_ELSE,
- TK_EXCEPT, TK_TRANSACTION,TK_ACTION, TK_ON, TK_JOIN_KW,
- TK_ALTER, TK_RAISE, TK_EXCLUSIVE, TK_EXISTS, TK_SAVEPOINT,
- TK_INTERSECT, TK_TRIGGER, TK_REFERENCES, TK_CONSTRAINT, TK_INTO,
- TK_OFFSET, TK_OF, TK_SET, TK_TEMP, TK_TEMP,
- TK_OR, TK_UNIQUE, TK_QUERY, TK_WITHOUT, TK_WITH,
- TK_JOIN_KW, TK_RELEASE, TK_ATTACH, TK_HAVING, TK_GROUP,
- TK_UPDATE, TK_BEGIN, TK_JOIN_KW, TK_RANGE, TK_BETWEEN,
- TK_NOTHING, TK_LIKE_KW, TK_BY, TK_CASCADE, TK_ASC,
- TK_DELETE, TK_CASE, TK_COLLATE, TK_CREATE, TK_CTIME_KW,
- TK_DETACH, TK_IMMEDIATE, TK_JOIN, TK_INSERT, TK_LIKE_KW,
- TK_MATCH, TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_ABORT,
- TK_VALUES, TK_VIRTUAL, TK_LIMIT, TK_WHEN, TK_NOTNULL,
- TK_NOT, TK_NO, TK_NULL, TK_WHERE, TK_RECURSIVE,
- TK_AFTER, TK_RENAME, TK_AND, TK_DEFAULT, TK_AUTOINCR,
- TK_TO, TK_IN, TK_CAST, TK_COLUMNKW, TK_COMMIT,
- TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW, TK_CURRENT,
- TK_PARTITION, TK_DEFERRED, TK_DISTINCT, TK_IS, TK_DROP,
- TK_PRECEDING, TK_FAIL, TK_FILTER, TK_REPLACE, TK_FOLLOWING,
- TK_FROM, TK_JOIN_KW, TK_IF, TK_ISNULL, TK_ORDER,
- TK_RESTRICT, TK_OVER, TK_JOIN_KW, TK_ROLLBACK, TK_ROWS,
- TK_ROW, TK_UNBOUNDED, TK_UNION, TK_USING, TK_VACUUM,
- TK_VIEW, TK_WINDOW, TK_DO, TK_INITIALLY, TK_ALL,
- TK_PRIMARY,
+static const unsigned char aKWCode[148] = {0,
+ TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE,
+ TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN,
+ TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD,
+ TK_ADD, TK_DATABASE, TK_AS, TK_SELECT, TK_TABLE,
+ TK_JOIN_KW, TK_THEN, TK_END, TK_DEFERRABLE, TK_ELSE,
+ TK_EXCLUDE, TK_DELETE, TK_TEMP, TK_TEMP, TK_OR,
+ TK_ISNULL, TK_NULLS, TK_SAVEPOINT, TK_INTERSECT, TK_TIES,
+ TK_NOTNULL, TK_NOT, TK_NO, TK_NULL, TK_LIKE_KW,
+ TK_EXCEPT, TK_TRANSACTION,TK_ACTION, TK_ON, TK_JOIN_KW,
+ TK_ALTER, TK_RAISE, TK_EXCLUSIVE, TK_EXISTS, TK_CONSTRAINT,
+ TK_INTO, TK_OFFSET, TK_OF, TK_SET, TK_TRIGGER,
+ TK_RANGE, TK_GENERATED, TK_DETACH, TK_HAVING, TK_LIKE_KW,
+ TK_BEGIN, TK_JOIN_KW, TK_REFERENCES, TK_UNIQUE, TK_QUERY,
+ TK_WITHOUT, TK_WITH, TK_JOIN_KW, TK_RELEASE, TK_ATTACH,
+ TK_BETWEEN, TK_NOTHING, TK_GROUPS, TK_GROUP, TK_CASCADE,
+ TK_ASC, TK_DEFAULT, TK_CASE, TK_COLLATE, TK_CREATE,
+ TK_CTIME_KW, TK_IMMEDIATE, TK_JOIN, TK_INSERT, TK_MATCH,
+ TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_MATERIALIZED, TK_DEFERRED,
+ TK_DISTINCT, TK_IS, TK_UPDATE, TK_VALUES, TK_VIRTUAL,
+ TK_ALWAYS, TK_WHEN, TK_WHERE, TK_RECURSIVE, TK_ABORT,
+ TK_AFTER, TK_RENAME, TK_AND, TK_DROP, TK_PARTITION,
+ TK_AUTOINCR, TK_TO, TK_IN, TK_CAST, TK_COLUMNKW,
+ TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW,
+ TK_CURRENT, TK_PRECEDING, TK_FAIL, TK_LAST, TK_FILTER,
+ TK_REPLACE, TK_FIRST, TK_FOLLOWING, TK_FROM, TK_JOIN_KW,
+ TK_LIMIT, TK_IF, TK_ORDER, TK_RESTRICT, TK_OTHERS,
+ TK_OVER, TK_RETURNING, TK_JOIN_KW, TK_ROLLBACK, TK_ROWS,
+ TK_ROW, TK_UNBOUNDED, TK_UNION, TK_USING, TK_VACUUM,
+ TK_VIEW, TK_WINDOW, TK_DO, TK_BY, TK_INITIALLY,
+ TK_ALL, TK_PRIMARY,
};
+/* Hash table decoded:
+** 0: INSERT
+** 1: IS
+** 2: ROLLBACK TRIGGER
+** 3: IMMEDIATE
+** 4: PARTITION
+** 5: TEMP
+** 6:
+** 7:
+** 8: VALUES WITHOUT
+** 9:
+** 10: MATCH
+** 11: NOTHING
+** 12:
+** 13: OF
+** 14: TIES IGNORE
+** 15: PLAN
+** 16: INSTEAD INDEXED
+** 17:
+** 18: TRANSACTION RIGHT
+** 19: WHEN
+** 20: SET HAVING
+** 21: MATERIALIZED IF
+** 22: ROWS
+** 23: SELECT
+** 24:
+** 25:
+** 26: VACUUM SAVEPOINT
+** 27:
+** 28: LIKE UNION VIRTUAL REFERENCES
+** 29: RESTRICT
+** 30:
+** 31: THEN REGEXP
+** 32: TO
+** 33:
+** 34: BEFORE
+** 35:
+** 36:
+** 37: FOLLOWING COLLATE CASCADE
+** 38: CREATE
+** 39:
+** 40: CASE REINDEX
+** 41: EACH
+** 42:
+** 43: QUERY
+** 44: AND ADD
+** 45: PRIMARY ANALYZE
+** 46:
+** 47: ROW ASC DETACH
+** 48: CURRENT_TIME CURRENT_DATE
+** 49:
+** 50:
+** 51: EXCLUSIVE TEMPORARY
+** 52:
+** 53: DEFERRED
+** 54: DEFERRABLE
+** 55:
+** 56: DATABASE
+** 57:
+** 58: DELETE VIEW GENERATED
+** 59: ATTACH
+** 60: END
+** 61: EXCLUDE
+** 62: ESCAPE DESC
+** 63: GLOB
+** 64: WINDOW ELSE
+** 65: COLUMN
+** 66: FIRST
+** 67:
+** 68: GROUPS ALL
+** 69: DISTINCT DROP KEY
+** 70: BETWEEN
+** 71: INITIALLY
+** 72: BEGIN
+** 73: FILTER CHECK ACTION
+** 74: GROUP INDEX
+** 75:
+** 76: EXISTS DEFAULT
+** 77:
+** 78: FOR CURRENT_TIMESTAMP
+** 79: EXCEPT
+** 80:
+** 81: CROSS
+** 82:
+** 83:
+** 84:
+** 85: CAST
+** 86: FOREIGN AUTOINCREMENT
+** 87: COMMIT
+** 88: CURRENT AFTER ALTER
+** 89: FULL FAIL CONFLICT
+** 90: EXPLAIN
+** 91: CONSTRAINT
+** 92: FROM ALWAYS
+** 93:
+** 94: ABORT
+** 95:
+** 96: AS DO
+** 97: REPLACE WITH RELEASE
+** 98: BY RENAME
+** 99: RANGE RAISE
+** 100: OTHERS
+** 101: USING NULLS
+** 102: PRAGMA
+** 103: JOIN ISNULL OFFSET
+** 104: NOT
+** 105: OR LAST LEFT
+** 106: LIMIT
+** 107:
+** 108:
+** 109: IN
+** 110: INTO
+** 111: OVER RECURSIVE
+** 112: ORDER OUTER
+** 113:
+** 114: INTERSECT UNBOUNDED
+** 115:
+** 116:
+** 117: RETURNING ON
+** 118:
+** 119: WHERE
+** 120: NO INNER
+** 121: NULL
+** 122:
+** 123: TABLE
+** 124: NATURAL NOTNULL
+** 125: PRECEDING
+** 126: UPDATE UNIQUE
+*/
/* Check to see if z[0..n-1] is a keyword. If it is, write the
** parser symbol code for that keyword into *pType. Always
** return the integer n (the length of the token). */
static int keywordCode(const char *z, int n, int *pType){
int i, j;
const char *zKW;
- if( n>=2 ){
- i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n) % 127;
- for(i=((int)aKWHash[i])-1; i>=0; i=((int)aKWNext[i])-1){
- if( aKWLen[i]!=n ) continue;
- j = 0;
- zKW = &zKWText[aKWOffset[i]];
+ assert( n>=2 );
+ i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127;
+ for(i=(int)aKWHash[i]; i>0; i=aKWNext[i]){
+ if( aKWLen[i]!=n ) continue;
+ zKW = &zKWText[aKWOffset[i]];
#ifdef SQLITE_ASCII
- while( j<n && (z[j]&~0x20)==zKW[j] ){ j++; }
+ if( (z[0]&~0x20)!=zKW[0] ) continue;
+ if( (z[1]&~0x20)!=zKW[1] ) continue;
+ j = 2;
+ while( j<n && (z[j]&~0x20)==zKW[j] ){ j++; }
#endif
#ifdef SQLITE_EBCDIC
- while( j<n && toupper(z[j])==zKW[j] ){ j++; }
-#endif
- if( j<n ) continue;
- testcase( i==0 ); /* REINDEX */
- testcase( i==1 ); /* INDEXED */
- testcase( i==2 ); /* INDEX */
- testcase( i==3 ); /* DESC */
- testcase( i==4 ); /* ESCAPE */
- testcase( i==5 ); /* EACH */
- testcase( i==6 ); /* CHECK */
- testcase( i==7 ); /* KEY */
- testcase( i==8 ); /* BEFORE */
- testcase( i==9 ); /* FOREIGN */
- testcase( i==10 ); /* FOR */
- testcase( i==11 ); /* IGNORE */
- testcase( i==12 ); /* REGEXP */
- testcase( i==13 ); /* EXPLAIN */
- testcase( i==14 ); /* INSTEAD */
- testcase( i==15 ); /* ADD */
- testcase( i==16 ); /* DATABASE */
- testcase( i==17 ); /* AS */
- testcase( i==18 ); /* SELECT */
- testcase( i==19 ); /* TABLE */
- testcase( i==20 ); /* LEFT */
- testcase( i==21 ); /* THEN */
- testcase( i==22 ); /* END */
- testcase( i==23 ); /* DEFERRABLE */
- testcase( i==24 ); /* ELSE */
- testcase( i==25 ); /* EXCEPT */
- testcase( i==26 ); /* TRANSACTION */
- testcase( i==27 ); /* ACTION */
- testcase( i==28 ); /* ON */
- testcase( i==29 ); /* NATURAL */
- testcase( i==30 ); /* ALTER */
- testcase( i==31 ); /* RAISE */
- testcase( i==32 ); /* EXCLUSIVE */
- testcase( i==33 ); /* EXISTS */
- testcase( i==34 ); /* SAVEPOINT */
- testcase( i==35 ); /* INTERSECT */
- testcase( i==36 ); /* TRIGGER */
- testcase( i==37 ); /* REFERENCES */
- testcase( i==38 ); /* CONSTRAINT */
- testcase( i==39 ); /* INTO */
- testcase( i==40 ); /* OFFSET */
- testcase( i==41 ); /* OF */
- testcase( i==42 ); /* SET */
- testcase( i==43 ); /* TEMPORARY */
- testcase( i==44 ); /* TEMP */
- testcase( i==45 ); /* OR */
- testcase( i==46 ); /* UNIQUE */
- testcase( i==47 ); /* QUERY */
- testcase( i==48 ); /* WITHOUT */
- testcase( i==49 ); /* WITH */
- testcase( i==50 ); /* OUTER */
- testcase( i==51 ); /* RELEASE */
- testcase( i==52 ); /* ATTACH */
- testcase( i==53 ); /* HAVING */
- testcase( i==54 ); /* GROUP */
- testcase( i==55 ); /* UPDATE */
- testcase( i==56 ); /* BEGIN */
- testcase( i==57 ); /* INNER */
- testcase( i==58 ); /* RANGE */
- testcase( i==59 ); /* BETWEEN */
- testcase( i==60 ); /* NOTHING */
- testcase( i==61 ); /* GLOB */
- testcase( i==62 ); /* BY */
- testcase( i==63 ); /* CASCADE */
- testcase( i==64 ); /* ASC */
- testcase( i==65 ); /* DELETE */
- testcase( i==66 ); /* CASE */
- testcase( i==67 ); /* COLLATE */
- testcase( i==68 ); /* CREATE */
- testcase( i==69 ); /* CURRENT_DATE */
- testcase( i==70 ); /* DETACH */
- testcase( i==71 ); /* IMMEDIATE */
- testcase( i==72 ); /* JOIN */
- testcase( i==73 ); /* INSERT */
- testcase( i==74 ); /* LIKE */
- testcase( i==75 ); /* MATCH */
- testcase( i==76 ); /* PLAN */
- testcase( i==77 ); /* ANALYZE */
- testcase( i==78 ); /* PRAGMA */
- testcase( i==79 ); /* ABORT */
- testcase( i==80 ); /* VALUES */
- testcase( i==81 ); /* VIRTUAL */
- testcase( i==82 ); /* LIMIT */
- testcase( i==83 ); /* WHEN */
- testcase( i==84 ); /* NOTNULL */
- testcase( i==85 ); /* NOT */
- testcase( i==86 ); /* NO */
- testcase( i==87 ); /* NULL */
- testcase( i==88 ); /* WHERE */
- testcase( i==89 ); /* RECURSIVE */
- testcase( i==90 ); /* AFTER */
- testcase( i==91 ); /* RENAME */
- testcase( i==92 ); /* AND */
- testcase( i==93 ); /* DEFAULT */
- testcase( i==94 ); /* AUTOINCREMENT */
- testcase( i==95 ); /* TO */
- testcase( i==96 ); /* IN */
- testcase( i==97 ); /* CAST */
- testcase( i==98 ); /* COLUMN */
- testcase( i==99 ); /* COMMIT */
- testcase( i==100 ); /* CONFLICT */
- testcase( i==101 ); /* CROSS */
- testcase( i==102 ); /* CURRENT_TIMESTAMP */
- testcase( i==103 ); /* CURRENT_TIME */
- testcase( i==104 ); /* CURRENT */
- testcase( i==105 ); /* PARTITION */
- testcase( i==106 ); /* DEFERRED */
- testcase( i==107 ); /* DISTINCT */
- testcase( i==108 ); /* IS */
- testcase( i==109 ); /* DROP */
- testcase( i==110 ); /* PRECEDING */
- testcase( i==111 ); /* FAIL */
- testcase( i==112 ); /* FILTER */
- testcase( i==113 ); /* REPLACE */
- testcase( i==114 ); /* FOLLOWING */
- testcase( i==115 ); /* FROM */
- testcase( i==116 ); /* FULL */
- testcase( i==117 ); /* IF */
- testcase( i==118 ); /* ISNULL */
- testcase( i==119 ); /* ORDER */
- testcase( i==120 ); /* RESTRICT */
- testcase( i==121 ); /* OVER */
- testcase( i==122 ); /* RIGHT */
- testcase( i==123 ); /* ROLLBACK */
- testcase( i==124 ); /* ROWS */
- testcase( i==125 ); /* ROW */
- testcase( i==126 ); /* UNBOUNDED */
- testcase( i==127 ); /* UNION */
- testcase( i==128 ); /* USING */
- testcase( i==129 ); /* VACUUM */
- testcase( i==130 ); /* VIEW */
- testcase( i==131 ); /* WINDOW */
- testcase( i==132 ); /* DO */
- testcase( i==133 ); /* INITIALLY */
- testcase( i==134 ); /* ALL */
- testcase( i==135 ); /* PRIMARY */
- *pType = aKWCode[i];
- break;
- }
+ if( toupper(z[0])!=zKW[0] ) continue;
+ if( toupper(z[1])!=zKW[1] ) continue;
+ j = 2;
+ while( j<n && toupper(z[j])==zKW[j] ){ j++; }
+#endif
+ if( j<n ) continue;
+ testcase( i==1 ); /* REINDEX */
+ testcase( i==2 ); /* INDEXED */
+ testcase( i==3 ); /* INDEX */
+ testcase( i==4 ); /* DESC */
+ testcase( i==5 ); /* ESCAPE */
+ testcase( i==6 ); /* EACH */
+ testcase( i==7 ); /* CHECK */
+ testcase( i==8 ); /* KEY */
+ testcase( i==9 ); /* BEFORE */
+ testcase( i==10 ); /* FOREIGN */
+ testcase( i==11 ); /* FOR */
+ testcase( i==12 ); /* IGNORE */
+ testcase( i==13 ); /* REGEXP */
+ testcase( i==14 ); /* EXPLAIN */
+ testcase( i==15 ); /* INSTEAD */
+ testcase( i==16 ); /* ADD */
+ testcase( i==17 ); /* DATABASE */
+ testcase( i==18 ); /* AS */
+ testcase( i==19 ); /* SELECT */
+ testcase( i==20 ); /* TABLE */
+ testcase( i==21 ); /* LEFT */
+ testcase( i==22 ); /* THEN */
+ testcase( i==23 ); /* END */
+ testcase( i==24 ); /* DEFERRABLE */
+ testcase( i==25 ); /* ELSE */
+ testcase( i==26 ); /* EXCLUDE */
+ testcase( i==27 ); /* DELETE */
+ testcase( i==28 ); /* TEMPORARY */
+ testcase( i==29 ); /* TEMP */
+ testcase( i==30 ); /* OR */
+ testcase( i==31 ); /* ISNULL */
+ testcase( i==32 ); /* NULLS */
+ testcase( i==33 ); /* SAVEPOINT */
+ testcase( i==34 ); /* INTERSECT */
+ testcase( i==35 ); /* TIES */
+ testcase( i==36 ); /* NOTNULL */
+ testcase( i==37 ); /* NOT */
+ testcase( i==38 ); /* NO */
+ testcase( i==39 ); /* NULL */
+ testcase( i==40 ); /* LIKE */
+ testcase( i==41 ); /* EXCEPT */
+ testcase( i==42 ); /* TRANSACTION */
+ testcase( i==43 ); /* ACTION */
+ testcase( i==44 ); /* ON */
+ testcase( i==45 ); /* NATURAL */
+ testcase( i==46 ); /* ALTER */
+ testcase( i==47 ); /* RAISE */
+ testcase( i==48 ); /* EXCLUSIVE */
+ testcase( i==49 ); /* EXISTS */
+ testcase( i==50 ); /* CONSTRAINT */
+ testcase( i==51 ); /* INTO */
+ testcase( i==52 ); /* OFFSET */
+ testcase( i==53 ); /* OF */
+ testcase( i==54 ); /* SET */
+ testcase( i==55 ); /* TRIGGER */
+ testcase( i==56 ); /* RANGE */
+ testcase( i==57 ); /* GENERATED */
+ testcase( i==58 ); /* DETACH */
+ testcase( i==59 ); /* HAVING */
+ testcase( i==60 ); /* GLOB */
+ testcase( i==61 ); /* BEGIN */
+ testcase( i==62 ); /* INNER */
+ testcase( i==63 ); /* REFERENCES */
+ testcase( i==64 ); /* UNIQUE */
+ testcase( i==65 ); /* QUERY */
+ testcase( i==66 ); /* WITHOUT */
+ testcase( i==67 ); /* WITH */
+ testcase( i==68 ); /* OUTER */
+ testcase( i==69 ); /* RELEASE */
+ testcase( i==70 ); /* ATTACH */
+ testcase( i==71 ); /* BETWEEN */
+ testcase( i==72 ); /* NOTHING */
+ testcase( i==73 ); /* GROUPS */
+ testcase( i==74 ); /* GROUP */
+ testcase( i==75 ); /* CASCADE */
+ testcase( i==76 ); /* ASC */
+ testcase( i==77 ); /* DEFAULT */
+ testcase( i==78 ); /* CASE */
+ testcase( i==79 ); /* COLLATE */
+ testcase( i==80 ); /* CREATE */
+ testcase( i==81 ); /* CURRENT_DATE */
+ testcase( i==82 ); /* IMMEDIATE */
+ testcase( i==83 ); /* JOIN */
+ testcase( i==84 ); /* INSERT */
+ testcase( i==85 ); /* MATCH */
+ testcase( i==86 ); /* PLAN */
+ testcase( i==87 ); /* ANALYZE */
+ testcase( i==88 ); /* PRAGMA */
+ testcase( i==89 ); /* MATERIALIZED */
+ testcase( i==90 ); /* DEFERRED */
+ testcase( i==91 ); /* DISTINCT */
+ testcase( i==92 ); /* IS */
+ testcase( i==93 ); /* UPDATE */
+ testcase( i==94 ); /* VALUES */
+ testcase( i==95 ); /* VIRTUAL */
+ testcase( i==96 ); /* ALWAYS */
+ testcase( i==97 ); /* WHEN */
+ testcase( i==98 ); /* WHERE */
+ testcase( i==99 ); /* RECURSIVE */
+ testcase( i==100 ); /* ABORT */
+ testcase( i==101 ); /* AFTER */
+ testcase( i==102 ); /* RENAME */
+ testcase( i==103 ); /* AND */
+ testcase( i==104 ); /* DROP */
+ testcase( i==105 ); /* PARTITION */
+ testcase( i==106 ); /* AUTOINCREMENT */
+ testcase( i==107 ); /* TO */
+ testcase( i==108 ); /* IN */
+ testcase( i==109 ); /* CAST */
+ testcase( i==110 ); /* COLUMN */
+ testcase( i==111 ); /* COMMIT */
+ testcase( i==112 ); /* CONFLICT */
+ testcase( i==113 ); /* CROSS */
+ testcase( i==114 ); /* CURRENT_TIMESTAMP */
+ testcase( i==115 ); /* CURRENT_TIME */
+ testcase( i==116 ); /* CURRENT */
+ testcase( i==117 ); /* PRECEDING */
+ testcase( i==118 ); /* FAIL */
+ testcase( i==119 ); /* LAST */
+ testcase( i==120 ); /* FILTER */
+ testcase( i==121 ); /* REPLACE */
+ testcase( i==122 ); /* FIRST */
+ testcase( i==123 ); /* FOLLOWING */
+ testcase( i==124 ); /* FROM */
+ testcase( i==125 ); /* FULL */
+ testcase( i==126 ); /* LIMIT */
+ testcase( i==127 ); /* IF */
+ testcase( i==128 ); /* ORDER */
+ testcase( i==129 ); /* RESTRICT */
+ testcase( i==130 ); /* OTHERS */
+ testcase( i==131 ); /* OVER */
+ testcase( i==132 ); /* RETURNING */
+ testcase( i==133 ); /* RIGHT */
+ testcase( i==134 ); /* ROLLBACK */
+ testcase( i==135 ); /* ROWS */
+ testcase( i==136 ); /* ROW */
+ testcase( i==137 ); /* UNBOUNDED */
+ testcase( i==138 ); /* UNION */
+ testcase( i==139 ); /* USING */
+ testcase( i==140 ); /* VACUUM */
+ testcase( i==141 ); /* VIEW */
+ testcase( i==142 ); /* WINDOW */
+ testcase( i==143 ); /* DO */
+ testcase( i==144 ); /* BY */
+ testcase( i==145 ); /* INITIALLY */
+ testcase( i==146 ); /* ALL */
+ testcase( i==147 ); /* PRIMARY */
+ *pType = aKWCode[i];
+ break;
}
return n;
}
SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char *z, int n){
int id = TK_ID;
- keywordCode((char*)z, n, &id);
+ if( n>=2 ) keywordCode((char*)z, n, &id);
return id;
}
-#define SQLITE_N_KEYWORD 136
+#define SQLITE_N_KEYWORD 147
SQLITE_API int sqlite3_keyword_name(int i,const char **pzName,int *pnName){
if( i<0 || i>=SQLITE_N_KEYWORD ) return SQLITE_ERROR;
+ i++;
*pzName = zKWText + aKWOffset[i];
*pnName = aKWLen[i];
return SQLITE_OK;
@@ -151701,14 +177009,14 @@ SQLITE_API int sqlite3_keyword_check(const char *zName, int nName){
** 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,
+** allowed in an identifier. For 7-bit characters,
** sqlite3IsIdChar[X] must be 1.
**
** For EBCDIC, the rules are more complex but have the same
** end result.
**
** Ticket #1066. the SQL standard does not allow '$' in the
-** middle of identifiers. But many SQL implementations do.
+** middle of identifiers. But many SQL implementations do.
** SQLite will allow '$' in identifiers for compatibility.
** But the feature is undocumented.
*/
@@ -151748,12 +177056,12 @@ static int getToken(const unsigned char **pz){
do {
z += sqlite3GetToken(z, &t);
}while( t==TK_SPACE );
- if( t==TK_ID
- || t==TK_STRING
- || t==TK_JOIN_KW
- || t==TK_WINDOW
- || t==TK_OVER
- || sqlite3ParserFallback(t)==TK_ID
+ if( t==TK_ID
+ || t==TK_STRING
+ || t==TK_JOIN_KW
+ || t==TK_WINDOW
+ || t==TK_OVER
+ || sqlite3ParserFallback(t)==TK_ID
){
t = TK_ID;
}
@@ -151770,8 +177078,8 @@ static int getToken(const unsigned char **pz){
**
** SELECT sum(x) OVER ...
**
-** In the above, "OVER" might be a keyword, or it might be an alias for the
-** sum(x) expression. If a "%fallback ID OVER" directive were added to
+** In the above, "OVER" might be a keyword, or it might be an alias for the
+** sum(x) expression. If a "%fallback ID OVER" directive were added to
** grammar, then SQLite would always treat "OVER" as an alias, making it
** impossible to call a window-function without a FILTER clause.
**
@@ -151815,7 +177123,7 @@ static int analyzeFilterKeyword(const unsigned char *z, int lastToken){
#endif /* SQLITE_OMIT_WINDOWFUNC */
/*
-** Return the length (in bytes) of the token that begins at z[0].
+** Return the length (in bytes) of the token that begins at z[0].
** Store the token type in *tokenType before returning.
*/
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
@@ -151838,6 +177146,9 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
return i;
+ }else if( z[1]=='>' ){
+ *tokenType = TK_PTR;
+ return 2 + (z[2]=='>');
}
*tokenType = TK_MINUS;
return 1;
@@ -151972,12 +177283,13 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
}
/* If the next character is a digit, this is a floating point
** number that begins with ".". Fall thru into the next case */
+ /* no break */ deliberate_fall_through
}
case CC_DIGIT: {
testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' );
testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' );
testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' );
- testcase( z[0]=='9' );
+ testcase( z[0]=='9' ); testcase( z[0]=='.' );
*tokenType = TK_INTEGER;
#ifndef SQLITE_OMIT_HEX_INTEGER
if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){
@@ -151993,7 +177305,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_FLOAT;
}
if( (z[i]=='e' || z[i]=='E') &&
- ( sqlite3Isdigit(z[i+1])
+ ( sqlite3Isdigit(z[i+1])
|| ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2]))
)
){
@@ -152048,8 +177360,9 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
if( n==0 ) *tokenType = TK_ILLEGAL;
return i;
}
- case CC_KYWD: {
- for(i=1; aiClass[z[i]]<=CC_KYWD; i++){}
+ case CC_KYWD0: {
+ if( aiClass[z[1]]>CC_KYWD ){ i = 1; break; }
+ for(i=2; aiClass[z[i]]<=CC_KYWD; i++){}
if( IdChar(z[i]) ){
/* This token started out using characters that can appear in keywords,
** but z[i] is a character not allowed within keywords, so this must
@@ -152076,11 +177389,21 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
#endif
/* If it is not a BLOB literal, then it must be an ID, since no
** SQL keywords start with the letter 'x'. Fall through */
+ /* no break */ deliberate_fall_through
}
+ case CC_KYWD:
case CC_ID: {
i = 1;
break;
}
+ case CC_BOM: {
+ if( z[1]==0xbb && z[2]==0xbf ){
+ *tokenType = TK_SPACE;
+ return 3;
+ }
+ i = 1;
+ break;
+ }
case CC_NUL: {
*tokenType = TK_ILLEGAL;
return 0;
@@ -152095,81 +177418,10 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
return i;
}
-#ifdef SQLITE_ENABLE_NORMALIZE
-/*
-** Return the length (in bytes) of the token that begins at z[0].
-** Store the token type in *tokenType before returning. If flags has
-** SQLITE_TOKEN_NORMALIZE flag enabled, use the identifier token type
-** for keywords. Add SQLITE_TOKEN_QUOTED to flags if the token was
-** actually a quoted identifier. Add SQLITE_TOKEN_KEYWORD to flags
-** if the token was recognized as a keyword; this is useful when the
-** SQLITE_TOKEN_NORMALIZE flag is used, because it enables the caller
-** to differentiate between a keyword being treated as an identifier
-** (for normalization purposes) and an actual identifier.
-*/
-SQLITE_PRIVATE int sqlite3GetTokenNormalized(
- const unsigned char *z,
- int *tokenType,
- int *flags
-){
- int n;
- unsigned char iClass = aiClass[*z];
- if( iClass==CC_KYWD ){
- int i;
- for(i=1; aiClass[z[i]]<=CC_KYWD; i++){}
- if( IdChar(z[i]) ){
- /* This token started out using characters that can appear in keywords,
- ** but z[i] is a character not allowed within keywords, so this must
- ** be an identifier instead */
- i++;
- while( IdChar(z[i]) ){ i++; }
- *tokenType = TK_ID;
- return i;
- }
- *tokenType = TK_ID;
- n = keywordCode((char*)z, i, tokenType);
- /* If the token is no longer considered to be an identifier, then it is a
- ** keyword of some kind. Make the token back into an identifier and then
- ** set the SQLITE_TOKEN_KEYWORD flag. Several non-identifier tokens are
- ** used verbatim, including IN, IS, NOT, and NULL. */
- switch( *tokenType ){
- case TK_ID: {
- /* do nothing, handled by caller */
- break;
- }
- case TK_IN:
- case TK_IS:
- case TK_NOT:
- case TK_NULL: {
- *flags |= SQLITE_TOKEN_KEYWORD;
- break;
- }
- default: {
- *tokenType = TK_ID;
- *flags |= SQLITE_TOKEN_KEYWORD;
- break;
- }
- }
- }else{
- n = sqlite3GetToken(z, tokenType);
- /* If the token is considered to be an identifier and the character class
- ** of the first character is a quote, set the SQLITE_TOKEN_QUOTED flag. */
- if( *tokenType==TK_ID && (iClass==CC_QUOTE || iClass==CC_QUOTE2) ){
- *flags |= SQLITE_TOKEN_QUOTED;
- }
- }
- return n;
-}
-#endif /* SQLITE_ENABLE_NORMALIZE */
-
/*
-** Run the parser on the given SQL string. The parser structure is
-** passed in. An SQLITE_ status code is returned. If an error occurs
-** then an and attempt is made to write an error message into
-** memory obtained from sqlite3_malloc() and to make *pzErrMsg point to that
-** error message.
+** Run the parser on the given SQL string.
*/
-SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
+SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){
int nErr = 0; /* Number of errors encountered */
void *pEngine; /* The LEMON-generated LALR(1) parser */
int n = 0; /* Length of the next token token */
@@ -152177,19 +177429,27 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
int lastTokenParsed = -1; /* type of the previous token */
sqlite3 *db = pParse->db; /* The database connection */
int mxSqlLen; /* Max length of an SQL string */
+ Parse *pParentParse = 0; /* Outer parse context, if any */
#ifdef sqlite3Parser_ENGINEALWAYSONSTACK
yyParser sEngine; /* Space to hold the Lemon-generated Parser object */
#endif
+ VVA_ONLY( u8 startedWithOom = db->mallocFailed );
assert( zSql!=0 );
mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
if( db->nVdbeActive==0 ){
- db->u1.isInterrupted = 0;
+ AtomicStore(&db->u1.isInterrupted, 0);
}
pParse->rc = SQLITE_OK;
pParse->zTail = zSql;
- assert( pzErrMsg!=0 );
- /* sqlite3ParserTrace(stdout, "parser: "); */
+#ifdef SQLITE_DEBUG
+ if( db->flags & SQLITE_ParserTrace ){
+ printf("parser: [[[%s]]]\n", zSql);
+ sqlite3ParserTrace(stdout, "parser: ");
+ }else{
+ sqlite3ParserTrace(0, 0);
+ }
+#endif
#ifdef sqlite3Parser_ENGINEALWAYSONSTACK
pEngine = &sEngine;
sqlite3ParserInit(pEngine, pParse);
@@ -152204,24 +177464,28 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
assert( pParse->pNewTrigger==0 );
assert( pParse->nVar==0 );
assert( pParse->pVList==0 );
+ pParentParse = db->pParse;
+ db->pParse = pParse;
while( 1 ){
n = sqlite3GetToken((u8*)zSql, &tokenType);
mxSqlLen -= n;
if( mxSqlLen<0 ){
pParse->rc = SQLITE_TOOBIG;
+ pParse->nErr++;
break;
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( tokenType>=TK_WINDOW ){
assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER
- || tokenType==TK_ILLEGAL || tokenType==TK_WINDOW
+ || tokenType==TK_ILLEGAL || tokenType==TK_WINDOW
);
#else
if( tokenType>=TK_SPACE ){
assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
#endif /* SQLITE_OMIT_WINDOWFUNC */
- if( db->u1.isInterrupted ){
+ if( AtomicLoad(&db->u1.isInterrupted) ){
pParse->rc = SQLITE_INTERRUPT;
+ pParse->nErr++;
break;
}
if( tokenType==TK_SPACE ){
@@ -152251,7 +177515,10 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
#endif /* SQLITE_OMIT_WINDOWFUNC */
}else{
- sqlite3ErrorMsg(pParse, "unrecognized token: \"%.*s\"", n, zSql);
+ Token x;
+ x.z = zSql;
+ x.n = n;
+ sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"", &x);
break;
}
}
@@ -152260,7 +177527,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
sqlite3Parser(pEngine, tokenType, pParse->sLastToken);
lastTokenParsed = tokenType;
zSql += n;
- if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break;
+ assert( db->mallocFailed==0 || pParse->rc!=SQLITE_OK || startedWithOom );
+ if( pParse->rc!=SQLITE_OK ) break;
}
assert( nErr==0 );
#ifdef YYTRACKMAXSTACKDEPTH
@@ -152278,59 +177546,168 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
if( db->mallocFailed ){
pParse->rc = SQLITE_NOMEM_BKPT;
}
- if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){
- pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
- }
- assert( pzErrMsg!=0 );
- if( pParse->zErrMsg ){
- *pzErrMsg = pParse->zErrMsg;
- sqlite3_log(pParse->rc, "%s in \"%s\"",
- *pzErrMsg, pParse->zTail);
- pParse->zErrMsg = 0;
+ if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){
+ if( pParse->zErrMsg==0 ){
+ pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
+ }
+ sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail);
nErr++;
}
pParse->zTail = zSql;
- if( pParse->pVdbe && pParse->nErr>0 && pParse->nested==0 ){
- sqlite3VdbeDelete(pParse->pVdbe);
- pParse->pVdbe = 0;
- }
-#ifndef SQLITE_OMIT_SHARED_CACHE
- if( pParse->nested==0 ){
- sqlite3DbFree(db, pParse->aTableLock);
- pParse->aTableLock = 0;
- pParse->nTableLock = 0;
- }
-#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3_free(pParse->apVtabLock);
#endif
- if( !IN_SPECIAL_PARSE ){
- /* If the pParse->declareVtab flag is set, do not delete any table
+ if( pParse->pNewTable && !IN_SPECIAL_PARSE ){
+ /* If the pParse->declareVtab flag is set, do not delete any table
** structure built up in pParse->pNewTable. The calling code (see vtab.c)
** will take responsibility for freeing the Table structure.
*/
sqlite3DeleteTable(db, pParse->pNewTable);
}
- if( !IN_RENAME_OBJECT ){
+ if( pParse->pNewTrigger && !IN_RENAME_OBJECT ){
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
}
+ if( pParse->pVList ) sqlite3DbNNFreeNN(db, pParse->pVList);
+ db->pParse = pParentParse;
+ assert( nErr==0 || pParse->rc!=SQLITE_OK );
+ return nErr;
+}
- if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
- sqlite3DbFree(db, pParse->pVList);
- while( pParse->pAinc ){
- AutoincInfo *p = pParse->pAinc;
- pParse->pAinc = p->pNext;
- sqlite3DbFreeNN(db, p);
+
+#ifdef SQLITE_ENABLE_NORMALIZE
+/*
+** Insert a single space character into pStr if the current string
+** ends with an identifier
+*/
+static void addSpaceSeparator(sqlite3_str *pStr){
+ if( pStr->nChar && sqlite3IsIdChar(pStr->zText[pStr->nChar-1]) ){
+ sqlite3_str_append(pStr, " ", 1);
}
- while( pParse->pZombieTab ){
- Table *p = pParse->pZombieTab;
- pParse->pZombieTab = p->pNextZombie;
- sqlite3DeleteTable(db, p);
+}
+
+/*
+** Compute a normalization of the SQL given by zSql[0..nSql-1]. Return
+** the normalization in space obtained from sqlite3DbMalloc(). Or return
+** NULL if anything goes wrong or if zSql is NULL.
+*/
+SQLITE_PRIVATE char *sqlite3Normalize(
+ Vdbe *pVdbe, /* VM being reprepared */
+ const char *zSql /* The original SQL string */
+){
+ sqlite3 *db; /* The database connection */
+ int i; /* Next unread byte of zSql[] */
+ int n; /* length of current token */
+ int tokenType; /* type of current token */
+ int prevType = 0; /* Previous non-whitespace token */
+ int nParen; /* Number of nested levels of parentheses */
+ int iStartIN; /* Start of RHS of IN operator in z[] */
+ int nParenAtIN; /* Value of nParent at start of RHS of IN operator */
+ u32 j; /* Bytes of normalized SQL generated so far */
+ sqlite3_str *pStr; /* The normalized SQL string under construction */
+
+ db = sqlite3VdbeDb(pVdbe);
+ tokenType = -1;
+ nParen = iStartIN = nParenAtIN = 0;
+ pStr = sqlite3_str_new(db);
+ assert( pStr!=0 ); /* sqlite3_str_new() never returns NULL */
+ for(i=0; zSql[i] && pStr->accError==0; i+=n){
+ if( tokenType!=TK_SPACE ){
+ prevType = tokenType;
+ }
+ n = sqlite3GetToken((unsigned char*)zSql+i, &tokenType);
+ if( NEVER(n<=0) ) break;
+ switch( tokenType ){
+ case TK_SPACE: {
+ break;
+ }
+ case TK_NULL: {
+ if( prevType==TK_IS || prevType==TK_NOT ){
+ sqlite3_str_append(pStr, " NULL", 5);
+ break;
+ }
+ /* Fall through */
+ }
+ case TK_STRING:
+ case TK_INTEGER:
+ case TK_FLOAT:
+ case TK_VARIABLE:
+ case TK_BLOB: {
+ sqlite3_str_append(pStr, "?", 1);
+ break;
+ }
+ case TK_LP: {
+ nParen++;
+ if( prevType==TK_IN ){
+ iStartIN = pStr->nChar;
+ nParenAtIN = nParen;
+ }
+ sqlite3_str_append(pStr, "(", 1);
+ break;
+ }
+ case TK_RP: {
+ if( iStartIN>0 && nParen==nParenAtIN ){
+ assert( pStr->nChar>=(u32)iStartIN );
+ pStr->nChar = iStartIN+1;
+ sqlite3_str_append(pStr, "?,?,?", 5);
+ iStartIN = 0;
+ }
+ nParen--;
+ sqlite3_str_append(pStr, ")", 1);
+ break;
+ }
+ case TK_ID: {
+ iStartIN = 0;
+ j = pStr->nChar;
+ if( sqlite3Isquote(zSql[i]) ){
+ char *zId = sqlite3DbStrNDup(db, zSql+i, n);
+ int nId;
+ int eType = 0;
+ if( zId==0 ) break;
+ sqlite3Dequote(zId);
+ if( zSql[i]=='"' && sqlite3VdbeUsesDoubleQuotedString(pVdbe, zId) ){
+ sqlite3_str_append(pStr, "?", 1);
+ sqlite3DbFree(db, zId);
+ break;
+ }
+ nId = sqlite3Strlen30(zId);
+ if( sqlite3GetToken((u8*)zId, &eType)==nId && eType==TK_ID ){
+ addSpaceSeparator(pStr);
+ sqlite3_str_append(pStr, zId, nId);
+ }else{
+ sqlite3_str_appendf(pStr, "\"%w\"", zId);
+ }
+ sqlite3DbFree(db, zId);
+ }else{
+ addSpaceSeparator(pStr);
+ sqlite3_str_append(pStr, zSql+i, n);
+ }
+ while( j<pStr->nChar ){
+ pStr->zText[j] = sqlite3Tolower(pStr->zText[j]);
+ j++;
+ }
+ break;
+ }
+ case TK_SELECT: {
+ iStartIN = 0;
+ /* fall through */
+ }
+ default: {
+ if( sqlite3IsIdChar(zSql[i]) ) addSpaceSeparator(pStr);
+ j = pStr->nChar;
+ sqlite3_str_append(pStr, zSql+i, n);
+ while( j<pStr->nChar ){
+ pStr->zText[j] = sqlite3Toupper(pStr->zText[j]);
+ j++;
+ }
+ break;
+ }
+ }
}
- assert( nErr==0 || pParse->rc!=SQLITE_OK );
- return nErr;
+ if( tokenType!=TK_SEMI ) sqlite3_str_append(pStr, ";", 1);
+ return sqlite3_str_finish(pStr);
}
+#endif /* SQLITE_ENABLE_NORMALIZE */
/************** End of tokenize.c ********************************************/
/************** Begin file complete.c ****************************************/
@@ -152402,7 +177779,7 @@ SQLITE_PRIVATE const char sqlite3IsEbcdicIdChar[];
** (2) NORMAL We are in the middle of statement which ends with a single
** semicolon.
**
-** (3) EXPLAIN The keyword EXPLAIN has been seen at the beginning of
+** (3) EXPLAIN The keyword EXPLAIN has been seen at the beginning of
** a statement.
**
** (4) CREATE The keyword CREATE has been seen at the beginning of a
@@ -152745,29 +178122,81 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db);
} /* extern "C" */
#endif /* __cplusplus */
-
/************** End of sqliteicu.h *******************************************/
/************** Continuing where we left off in main.c ***********************/
#endif
-#ifdef SQLITE_ENABLE_JSON1
-SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*);
+
+/*
+** This is an extension initializer that is a no-op and always
+** succeeds, except that it fails if the fault-simulation is set
+** to 500.
+*/
+static int sqlite3TestExtInit(sqlite3 *db){
+ (void)db;
+ return sqlite3FaultSim(500);
+}
+
+
+/*
+** Forward declarations of external module initializer functions
+** for modules that need them.
+*/
+#ifdef SQLITE_ENABLE_FTS5
+SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
#endif
#ifdef SQLITE_ENABLE_STMTVTAB
SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*);
#endif
+#ifdef SQLITE_EXTRA_AUTOEXT
+int SQLITE_EXTRA_AUTOEXT(sqlite3*);
+#endif
+/*
+** An array of pointers to extension initializer functions for
+** built-in extensions.
+*/
+static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = {
+#ifdef SQLITE_ENABLE_FTS3
+ sqlite3Fts3Init,
+#endif
#ifdef SQLITE_ENABLE_FTS5
-SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
+ sqlite3Fts5Init,
+#endif
+#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
+ sqlite3IcuInit,
+#endif
+#ifdef SQLITE_ENABLE_RTREE
+ sqlite3RtreeInit,
+#endif
+#ifdef SQLITE_ENABLE_DBPAGE_VTAB
+ sqlite3DbpageRegister,
+#endif
+#ifdef SQLITE_ENABLE_DBSTAT_VTAB
+ sqlite3DbstatRegister,
#endif
+ sqlite3TestExtInit,
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
+ sqlite3JsonTableFunctions,
+#endif
+#ifdef SQLITE_ENABLE_STMTVTAB
+ sqlite3StmtVtabInit,
+#endif
+#ifdef SQLITE_ENABLE_BYTECODE_VTAB
+ sqlite3VdbeBytecodeVtabInit,
+#endif
+#ifdef SQLITE_EXTRA_AUTOEXT
+ SQLITE_EXTRA_AUTOEXT,
+#endif
+};
#ifndef SQLITE_AMALGAMATION
/* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant
-** contains the text of SQLITE_VERSION macro.
+** contains the text of SQLITE_VERSION macro.
*/
SQLITE_API const char sqlite3_version[] = SQLITE_VERSION;
#endif
/* IMPLEMENTATION-OF: R-53536-42575 The sqlite3_libversion() function returns
-** a pointer to the to the sqlite3_version[] string constant.
+** a pointer to the to the sqlite3_version[] string constant.
*/
SQLITE_API const char *sqlite3_libversion(void){ return sqlite3_version; }
@@ -152831,13 +178260,39 @@ SQLITE_API char *sqlite3_temp_directory = 0;
SQLITE_API char *sqlite3_data_directory = 0;
/*
-** Initialize SQLite.
+** Determine whether or not high-precision (long double) floating point
+** math works correctly on CPU currently running.
+*/
+static SQLITE_NOINLINE int hasHighPrecisionDouble(int rc){
+ if( sizeof(LONGDOUBLE_TYPE)<=8 ){
+ /* If the size of "long double" is not more than 8, then
+ ** high-precision math is not possible. */
+ return 0;
+ }else{
+ /* Just because sizeof(long double)>8 does not mean that the underlying
+ ** hardware actually supports high-precision floating point. For example,
+ ** clearing the 0x100 bit in the floating-point control word on Intel
+ ** processors will make long double work like double, even though long
+ ** double takes up more space. The only way to determine if long double
+ ** actually works is to run an experiment. */
+ LONGDOUBLE_TYPE a, b, c;
+ rc++;
+ a = 1.0+rc*0.1;
+ b = 1.0e+18+rc*25.0;
+ c = a+b;
+ return b!=c;
+ }
+}
+
+
+/*
+** Initialize SQLite.
**
** This routine must be called to initialize the memory allocation,
** VFS, and mutex subsystems prior to doing any serious work with
** SQLite. But as long as you do not compile with SQLITE_OMIT_AUTOINIT
** this routine will be called automatically by key routines such as
-** sqlite3_open().
+** sqlite3_open().
**
** This routine is a no-op except on its very first call for the process,
** or for the first call after a call to sqlite3_shutdown.
@@ -152862,7 +178317,7 @@ SQLITE_API char *sqlite3_data_directory = 0;
** without blocking.
*/
SQLITE_API int sqlite3_initialize(void){
- MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
+ MUTEX_LOGIC( sqlite3_mutex *pMainMtx; ) /* The main static mutex */
int rc; /* Result code */
#ifdef SQLITE_EXTRA_INIT
int bRunExtraInit = 0; /* Extra initialization needed */
@@ -152885,9 +178340,12 @@ SQLITE_API int sqlite3_initialize(void){
** must be complete. So isInit must not be set until the very end
** of this routine.
*/
- if( sqlite3GlobalConfig.isInit ) return SQLITE_OK;
+ if( sqlite3GlobalConfig.isInit ){
+ sqlite3MemoryBarrier();
+ return SQLITE_OK;
+ }
- /* Make sure the mutex subsystem is initialized. If unable to
+ /* Make sure the mutex subsystem is initialized. If unable to
** initialize the mutex subsystem, return early with the error.
** If the system is so sick that we are unable to allocate a mutex,
** there is not much SQLite is going to be able to do.
@@ -152899,13 +178357,13 @@ SQLITE_API int sqlite3_initialize(void){
if( rc ) return rc;
/* Initialize the malloc() system and the recursive pInitMutex mutex.
- ** This operation is protected by the STATIC_MASTER mutex. Note that
+ ** This operation is protected by the STATIC_MAIN mutex. Note that
** MutexAlloc() is called for a static mutex prior to initializing the
** malloc subsystem - this implies that the allocation of a static
** mutex must not require support from the malloc subsystem.
*/
- MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
- sqlite3_mutex_enter(pMaster);
+ MUTEX_LOGIC( pMainMtx = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); )
+ sqlite3_mutex_enter(pMainMtx);
sqlite3GlobalConfig.isMutexInit = 1;
if( !sqlite3GlobalConfig.isMallocInit ){
rc = sqlite3MallocInit();
@@ -152923,7 +178381,7 @@ SQLITE_API int sqlite3_initialize(void){
if( rc==SQLITE_OK ){
sqlite3GlobalConfig.nRefInitMutex++;
}
- sqlite3_mutex_leave(pMaster);
+ sqlite3_mutex_leave(pMainMtx);
/* If rc is not SQLITE_OK at this point, then either the malloc
** subsystem could not be initialized or the system failed to allocate
@@ -152963,14 +178421,15 @@ SQLITE_API int sqlite3_initialize(void){
sqlite3GlobalConfig.isPCacheInit = 1;
rc = sqlite3OsInit();
}
-#ifdef SQLITE_ENABLE_DESERIALIZE
+#ifndef SQLITE_OMIT_DESERIALIZE
if( rc==SQLITE_OK ){
rc = sqlite3MemdbInit();
}
#endif
if( rc==SQLITE_OK ){
- sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
+ sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
+ sqlite3MemoryBarrier();
sqlite3GlobalConfig.isInit = 1;
#ifdef SQLITE_EXTRA_INIT
bRunExtraInit = 1;
@@ -152983,14 +178442,14 @@ SQLITE_API int sqlite3_initialize(void){
/* Go back under the static mutex and clean up the recursive
** mutex to prevent a resource leak.
*/
- sqlite3_mutex_enter(pMaster);
+ sqlite3_mutex_enter(pMainMtx);
sqlite3GlobalConfig.nRefInitMutex--;
if( sqlite3GlobalConfig.nRefInitMutex<=0 ){
assert( sqlite3GlobalConfig.nRefInitMutex==0 );
sqlite3_mutex_free(sqlite3GlobalConfig.pInitMutex);
sqlite3GlobalConfig.pInitMutex = 0;
}
- sqlite3_mutex_leave(pMaster);
+ sqlite3_mutex_leave(pMainMtx);
/* The following is just a sanity check to make sure SQLite has
** been compiled correctly. It is important to run this code, but
@@ -153021,6 +178480,12 @@ SQLITE_API int sqlite3_initialize(void){
}
#endif
+ /* Experimentally determine if high-precision floating point is
+ ** available. */
+#ifndef SQLITE_OMIT_WSD
+ sqlite3Config.bUseLongDouble = hasHighPrecisionDouble(rc);
+#endif
+
return rc;
}
@@ -153090,9 +178555,21 @@ SQLITE_API int sqlite3_config(int op, ...){
va_list ap;
int rc = SQLITE_OK;
- /* sqlite3_config() shall return SQLITE_MISUSE if it is invoked while
- ** the SQLite library is in use. */
- if( sqlite3GlobalConfig.isInit ) return SQLITE_MISUSE_BKPT;
+ /* sqlite3_config() normally returns SQLITE_MISUSE if it is invoked while
+ ** the SQLite library is in use. Except, a few selected opcodes
+ ** are allowed.
+ */
+ if( sqlite3GlobalConfig.isInit ){
+ static const u64 mAnytimeConfigOption = 0
+ | MASKBIT64( SQLITE_CONFIG_LOG )
+ | MASKBIT64( SQLITE_CONFIG_PCACHE_HDRSZ )
+ ;
+ if( op<0 || op>63 || (MASKBIT64(op) & mAnytimeConfigOption)==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+ testcase( op==SQLITE_CONFIG_LOG );
+ testcase( op==SQLITE_CONFIG_PCACHE_HDRSZ );
+ }
va_start(ap, op);
switch( op ){
@@ -153161,6 +178638,7 @@ SQLITE_API int sqlite3_config(int op, ...){
break;
}
case SQLITE_CONFIG_MEMSTATUS: {
+ assert( !sqlite3GlobalConfig.isInit ); /* Cannot change at runtime */
/* EVIDENCE-OF: R-61275-35157 The SQLITE_CONFIG_MEMSTATUS option takes
** single argument of type int, interpreted as a boolean, which enables
** or disables the collection of memory allocation statistics. */
@@ -153186,7 +178664,7 @@ SQLITE_API int sqlite3_config(int op, ...){
** a single parameter which is a pointer to an integer and writes into
** that integer the number of extra bytes per page required for each page
** in SQLITE_CONFIG_PAGECACHE. */
- *va_arg(ap, int*) =
+ *va_arg(ap, int*) =
sqlite3HeaderSizeBtree() +
sqlite3HeaderSizePcache() +
sqlite3HeaderSizePcache1();
@@ -153273,7 +178751,7 @@ SQLITE_API int sqlite3_config(int op, ...){
sqlite3GlobalConfig.nLookaside = va_arg(ap, int);
break;
}
-
+
/* Record a pointer to the logger function and its first argument.
** The default is NULL. Logging is disabled if the function pointer is
** NULL.
@@ -153284,8 +178762,10 @@ SQLITE_API int sqlite3_config(int op, ...){
** sqlite3GlobalConfig.xLog = va_arg(ap, void(*)(void*,int,const char*));
*/
typedef void(*LOGFUNC_t)(void*,int,const char*);
- sqlite3GlobalConfig.xLog = va_arg(ap, LOGFUNC_t);
- sqlite3GlobalConfig.pLogArg = va_arg(ap, void*);
+ LOGFUNC_t xLog = va_arg(ap, LOGFUNC_t);
+ void *pLogArg = va_arg(ap, void*);
+ AtomicStore(&sqlite3GlobalConfig.xLog, xLog);
+ AtomicStore(&sqlite3GlobalConfig.pLogArg, pLogArg);
break;
}
@@ -153299,7 +178779,8 @@ SQLITE_API int sqlite3_config(int op, ...){
** argument of type int. If non-zero, then URI handling is globally
** enabled. If the parameter is zero, then URI handling is globally
** disabled. */
- sqlite3GlobalConfig.bOpenUri = va_arg(ap, int);
+ int bOpenUri = va_arg(ap, int);
+ AtomicStore(&sqlite3GlobalConfig.bOpenUri, bOpenUri);
break;
}
@@ -153377,6 +178858,13 @@ SQLITE_API int sqlite3_config(int op, ...){
}
#endif /* SQLITE_ENABLE_SORTER_REFERENCES */
+#ifndef SQLITE_OMIT_DESERIALIZE
+ case SQLITE_CONFIG_MEMDB_MAXSIZE: {
+ sqlite3GlobalConfig.mxMemdbSize = va_arg(ap, sqlite3_int64);
+ break;
+ }
+#endif /* SQLITE_OMIT_DESERIALIZE */
+
default: {
rc = SQLITE_ERROR;
break;
@@ -153388,7 +178876,7 @@ SQLITE_API int sqlite3_config(int op, ...){
/*
** Set up the lookaside buffers for a database connection.
-** Return SQLITE_OK on success.
+** Return SQLITE_OK on success.
** If lookaside is already active, return SQLITE_BUSY.
**
** The sz parameter is the number of bytes in each lookaside slot.
@@ -153400,12 +178888,15 @@ SQLITE_API int sqlite3_config(int op, ...){
static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
#ifndef SQLITE_OMIT_LOOKASIDE
void *pStart;
-
+ sqlite3_int64 szAlloc = sz*(sqlite3_int64)cnt;
+ int nBig; /* Number of full-size slots */
+ int nSm; /* Number smaller LOOKASIDE_SMALL-byte slots */
+
if( sqlite3LookasideUsed(db,0)>0 ){
return SQLITE_BUSY;
}
/* Free any existing lookaside buffer for this handle before
- ** allocating a new one so we don't have to have space for
+ ** allocating a new one so we don't have to have space for
** both at the same time.
*/
if( db->lookaside.bMalloced ){
@@ -153422,37 +178913,72 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
pStart = 0;
}else if( pBuf==0 ){
sqlite3BeginBenignMalloc();
- pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */
+ pStart = sqlite3Malloc( szAlloc ); /* IMP: R-61949-35727 */
sqlite3EndBenignMalloc();
- if( pStart ) cnt = sqlite3MallocSize(pStart)/sz;
+ if( pStart ) szAlloc = sqlite3MallocSize(pStart);
}else{
pStart = pBuf;
}
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ if( sz>=LOOKASIDE_SMALL*3 ){
+ nBig = szAlloc/(3*LOOKASIDE_SMALL+sz);
+ nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL;
+ }else if( sz>=LOOKASIDE_SMALL*2 ){
+ nBig = szAlloc/(LOOKASIDE_SMALL+sz);
+ nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL;
+ }else
+#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
+ if( sz>0 ){
+ nBig = szAlloc/sz;
+ nSm = 0;
+ }else{
+ nBig = nSm = 0;
+ }
db->lookaside.pStart = pStart;
db->lookaside.pInit = 0;
db->lookaside.pFree = 0;
db->lookaside.sz = (u16)sz;
+ db->lookaside.szTrue = (u16)sz;
if( pStart ){
int i;
LookasideSlot *p;
assert( sz > (int)sizeof(LookasideSlot*) );
- db->lookaside.nSlot = cnt;
p = (LookasideSlot*)pStart;
- for(i=cnt-1; i>=0; i--){
+ for(i=0; i<nBig; i++){
p->pNext = db->lookaside.pInit;
db->lookaside.pInit = p;
p = (LookasideSlot*)&((u8*)p)[sz];
}
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ db->lookaside.pSmallInit = 0;
+ db->lookaside.pSmallFree = 0;
+ db->lookaside.pMiddle = p;
+ for(i=0; i<nSm; i++){
+ p->pNext = db->lookaside.pSmallInit;
+ db->lookaside.pSmallInit = p;
+ p = (LookasideSlot*)&((u8*)p)[LOOKASIDE_SMALL];
+ }
+#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
+ assert( ((uptr)p)<=szAlloc + (uptr)pStart );
db->lookaside.pEnd = p;
db->lookaside.bDisable = 0;
db->lookaside.bMalloced = pBuf==0 ?1:0;
- }else{
- db->lookaside.pStart = db;
- db->lookaside.pEnd = db;
+ db->lookaside.nSlot = nBig+nSm;
+ }else{
+ db->lookaside.pStart = 0;
+#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
+ db->lookaside.pSmallInit = 0;
+ db->lookaside.pSmallFree = 0;
+ db->lookaside.pMiddle = 0;
+#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
+ db->lookaside.pEnd = 0;
db->lookaside.bDisable = 1;
+ db->lookaside.sz = 0;
db->lookaside.bMalloced = 0;
db->lookaside.nSlot = 0;
}
+ db->lookaside.pTrueEnd = db->lookaside.pEnd;
+ assert( sqlite3LookasideUsed(db,0)==0 );
#endif /* SQLITE_OMIT_LOOKASIDE */
return SQLITE_OK;
}
@@ -153510,7 +179036,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3 *db){
sqlite3BtreeEnterAll(db);
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
- if( pBt && sqlite3BtreeIsInTrans(pBt) ){
+ if( pBt && sqlite3BtreeTxnState(pBt)==SQLITE_TXN_WRITE ){
Pager *pPager = sqlite3BtreePager(pBt);
rc = sqlite3PagerFlush(pPager);
if( rc==SQLITE_BUSY ){
@@ -153530,6 +179056,11 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3 *db){
SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
va_list ap;
int rc;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
+ sqlite3_mutex_enter(db->mutex);
va_start(ap, op);
switch( op ){
case SQLITE_DBCONFIG_MAINDBNAME: {
@@ -153553,6 +179084,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
} aFlagOp[] = {
{ SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
{ SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
+ { SQLITE_DBCONFIG_ENABLE_VIEW, SQLITE_EnableView },
{ SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer },
{ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension },
{ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_NoCkptOnClose },
@@ -153560,6 +179092,15 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
{ SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_TriggerEQP },
{ SQLITE_DBCONFIG_RESET_DATABASE, SQLITE_ResetDatabase },
{ SQLITE_DBCONFIG_DEFENSIVE, SQLITE_Defensive },
+ { SQLITE_DBCONFIG_WRITABLE_SCHEMA, SQLITE_WriteSchema|
+ SQLITE_NoSchemaError },
+ { SQLITE_DBCONFIG_LEGACY_ALTER_TABLE, SQLITE_LegacyAlter },
+ { SQLITE_DBCONFIG_DQS_DDL, SQLITE_DqsDDL },
+ { SQLITE_DBCONFIG_DQS_DML, SQLITE_DqsDML },
+ { SQLITE_DBCONFIG_LEGACY_FILE_FORMAT, SQLITE_LegacyFileFmt },
+ { SQLITE_DBCONFIG_TRUSTED_SCHEMA, SQLITE_TrustedSchema },
+ { SQLITE_DBCONFIG_STMT_SCANSTATUS, SQLITE_StmtScanStatus },
+ { SQLITE_DBCONFIG_REVERSE_SCANORDER, SQLITE_ReverseOrder },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
@@ -153567,11 +179108,11 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
if( aFlagOp[i].op==op ){
int onoff = va_arg(ap, int);
int *pRes = va_arg(ap, int*);
- u32 oldFlags = db->flags;
+ u64 oldFlags = db->flags;
if( onoff>0 ){
db->flags |= aFlagOp[i].mask;
}else if( onoff==0 ){
- db->flags &= ~aFlagOp[i].mask;
+ db->flags &= ~(u64)aFlagOp[i].mask;
}
if( oldFlags!=db->flags ){
sqlite3ExpirePreparedStatements(db, 0);
@@ -153587,31 +179128,21 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
}
}
va_end(ap);
+ sqlite3_mutex_leave(db->mutex);
return rc;
}
-
-/*
-** Return true if the buffer z[0..n-1] contains all spaces.
-*/
-static int allSpaces(const char *z, int n){
- while( n>0 && z[n-1]==' ' ){ n--; }
- return n==0;
-}
-
/*
** This is the default collating function named "BINARY" which is always
** available.
-**
-** If the padFlag argument is not NULL then space padding at the end
-** of strings is ignored. This implements the RTRIM collation.
*/
static int binCollFunc(
- void *padFlag,
+ void *NotUsed,
int nKey1, const void *pKey1,
int nKey2, const void *pKey2
){
int rc, n;
+ UNUSED_PARAMETER(NotUsed);
n = nKey1<nKey2 ? nKey1 : nKey2;
/* EVIDENCE-OF: R-65033-28449 The built-in BINARY collation compares
** strings byte by byte using the memcmp() function from the standard C
@@ -153619,33 +179150,37 @@ static int binCollFunc(
assert( pKey1 && pKey2 );
rc = memcmp(pKey1, pKey2, n);
if( rc==0 ){
- if( padFlag
- && allSpaces(((char*)pKey1)+n, nKey1-n)
- && allSpaces(((char*)pKey2)+n, nKey2-n)
- ){
- /* EVIDENCE-OF: R-31624-24737 RTRIM is like BINARY except that extra
- ** spaces at the end of either string do not change the result. In other
- ** words, strings will compare equal to one another as long as they
- ** differ only in the number of spaces at the end.
- */
- }else{
- rc = nKey1 - nKey2;
- }
+ rc = nKey1 - nKey2;
}
return rc;
}
/*
+** This is the collating function named "RTRIM" which is always
+** available. Ignore trailing spaces.
+*/
+static int rtrimCollFunc(
+ void *pUser,
+ int nKey1, const void *pKey1,
+ int nKey2, const void *pKey2
+){
+ const u8 *pK1 = (const u8*)pKey1;
+ const u8 *pK2 = (const u8*)pKey2;
+ while( nKey1 && pK1[nKey1-1]==' ' ) nKey1--;
+ while( nKey2 && pK2[nKey2-1]==' ' ) nKey2--;
+ return binCollFunc(pUser, nKey1, pKey1, nKey2, pKey2);
+}
+
+/*
** Return true if CollSeq is the default built-in BINARY.
*/
SQLITE_PRIVATE int sqlite3IsBinary(const CollSeq *p){
- assert( p==0 || p->xCmp!=binCollFunc || p->pUser!=0
- || strcmp(p->zName,"BINARY")==0 );
- return p==0 || (p->xCmp==binCollFunc && p->pUser==0);
+ assert( p==0 || p->xCmp!=binCollFunc || strcmp(p->zName,"BINARY")==0 );
+ return p==0 || p->xCmp==binCollFunc;
}
/*
-** Another built-in collating sequence: NOCASE.
+** Another built-in collating sequence: NOCASE.
**
** This collating sequence is intended to be used for "case independent
** comparison". SQLite's knowledge of upper and lower case equivalents
@@ -153698,7 +179233,7 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3 *db, sqlite3_int64 iRowid)
/*
** Return the number of changes in the most recent call to sqlite3_exec().
*/
-SQLITE_API int sqlite3_changes(sqlite3 *db){
+SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3 *db){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ){
(void)SQLITE_MISUSE_BKPT;
@@ -153707,11 +179242,14 @@ SQLITE_API int sqlite3_changes(sqlite3 *db){
#endif
return db->nChange;
}
+SQLITE_API int sqlite3_changes(sqlite3 *db){
+ return (int)sqlite3_changes64(db);
+}
/*
** Return the number of changes since the database handle was opened.
*/
-SQLITE_API int sqlite3_total_changes(sqlite3 *db){
+SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3 *db){
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ){
(void)SQLITE_MISUSE_BKPT;
@@ -153720,6 +179258,9 @@ SQLITE_API int sqlite3_total_changes(sqlite3 *db){
#endif
return db->nTotalChange;
}
+SQLITE_API int sqlite3_total_changes(sqlite3 *db){
+ return (int)sqlite3_total_changes64(db);
+}
/*
** Close all open savepoints. This function only manipulates fields of the
@@ -153744,7 +179285,9 @@ SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *db){
** with SQLITE_ANY as the encoding.
*/
static void functionDestroy(sqlite3 *db, FuncDef *p){
- FuncDestructor *pDestructor = p->u.pDestructor;
+ FuncDestructor *pDestructor;
+ assert( (p->funcFlags & SQLITE_FUNC_BUILTIN)==0 );
+ pDestructor = p->u.pDestructor;
if( pDestructor ){
pDestructor->nRef--;
if( pDestructor->nRef==0 ){
@@ -153787,7 +179330,7 @@ static void disconnectAllVtab(sqlite3 *db){
/*
** Return TRUE if database connection db has unfinalized prepared
-** statements or unfinished sqlite3_backup objects.
+** statements or unfinished sqlite3_backup objects.
*/
static int connectionIsBusy(sqlite3 *db){
int j;
@@ -153814,7 +179357,7 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
}
sqlite3_mutex_enter(db->mutex);
if( db->mTrace & SQLITE_TRACE_CLOSE ){
- db->xTrace(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0);
+ db->trace.xV2(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0);
}
/* Force xDisconnect calls on all virtual tables */
@@ -153846,17 +179389,55 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
}
#endif
+ while( db->pDbData ){
+ DbClientData *p = db->pDbData;
+ db->pDbData = p->pNext;
+ assert( p->pData!=0 );
+ if( p->xDestructor ) p->xDestructor(p->pData);
+ sqlite3_free(p);
+ }
+
/* Convert the connection into a zombie and then close it.
*/
- db->magic = SQLITE_MAGIC_ZOMBIE;
+ db->eOpenState = SQLITE_STATE_ZOMBIE;
sqlite3LeaveMutexAndCloseZombie(db);
return SQLITE_OK;
}
/*
+** Return the transaction state for a single databse, or the maximum
+** transaction state over all attached databases if zSchema is null.
+*/
+SQLITE_API int sqlite3_txn_state(sqlite3 *db, const char *zSchema){
+ int iDb, nDb;
+ int iTxn = -1;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return -1;
+ }
+#endif
+ sqlite3_mutex_enter(db->mutex);
+ if( zSchema ){
+ nDb = iDb = sqlite3FindDbName(db, zSchema);
+ if( iDb<0 ) nDb--;
+ }else{
+ iDb = 0;
+ nDb = db->nDb-1;
+ }
+ for(; iDb<=nDb; iDb++){
+ Btree *pBt = db->aDb[iDb].pBt;
+ int x = pBt!=0 ? sqlite3BtreeTxnState(pBt) : SQLITE_TXN_NONE;
+ if( x>iTxn ) iTxn = x;
+ }
+ sqlite3_mutex_leave(db->mutex);
+ return iTxn;
+}
+
+/*
** Two variations on the public interface for closing a database
** connection. The sqlite3_close() version returns SQLITE_BUSY and
-** leaves the connection option if there are unfinalized prepared
+** leaves the connection open if there are unfinalized prepared
** statements or unfinished sqlite3_backups. The sqlite3_close_v2()
** version forces the connection to become a zombie if there are
** unclosed resources, and arranges for deallocation when the last
@@ -153882,7 +179463,7 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
** or if the connection has not yet been closed by sqlite3_close_v2(),
** then just leave the mutex and return.
*/
- if( db->magic!=SQLITE_MAGIC_ZOMBIE || connectionIsBusy(db) ){
+ if( db->eOpenState!=SQLITE_STATE_ZOMBIE || connectionIsBusy(db) ){
sqlite3_mutex_leave(db->mutex);
return;
}
@@ -153954,11 +179535,8 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
#ifndef SQLITE_OMIT_VIRTUALTABLE
for(i=sqliteHashFirst(&db->aModule); i; i=sqliteHashNext(i)){
Module *pMod = (Module *)sqliteHashData(i);
- if( pMod->xDestroy ){
- pMod->xDestroy(pMod->pAux);
- }
sqlite3VtabEponymousTableClear(db, pMod);
- sqlite3DbFree(db, pMod);
+ sqlite3VtabModuleUnref(db, pMod);
}
sqlite3HashClear(&db->aModule);
#endif
@@ -153971,17 +179549,20 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
sqlite3_free(db->auth.zAuthPW);
#endif
- db->magic = SQLITE_MAGIC_ERROR;
+ db->eOpenState = SQLITE_STATE_ERROR;
/* The temp-database schema is allocated differently from the other schema
** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()).
** So it needs to be freed here. Todo: Why not roll the temp schema into
- ** the same sqliteMalloc() as the one that allocates the database
+ ** the same sqliteMalloc() as the one that allocates the database
** structure?
*/
sqlite3DbFree(db, db->aDb[1].pSchema);
+ if( db->xAutovacDestr ){
+ db->xAutovacDestr(db->pAutovacPagesArg);
+ }
sqlite3_mutex_leave(db->mutex);
- db->magic = SQLITE_MAGIC_CLOSED;
+ db->eOpenState = SQLITE_STATE_CLOSED;
sqlite3_mutex_free(db->mutex);
assert( sqlite3LookasideUsed(db,0)==0 );
if( db->lookaside.bMalloced ){
@@ -154004,7 +179585,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){
assert( sqlite3_mutex_held(db->mutex) );
sqlite3BeginBenignMalloc();
- /* Obtain all b-tree mutexes before making any calls to BtreeRollback().
+ /* Obtain all b-tree mutexes before making any calls to BtreeRollback().
** This is important in case the transaction being rolled back has
** modified the database schema. If the b-tree mutexes are not taken
** here, then another shared-cache connection might sneak in between
@@ -154016,7 +179597,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){
for(i=0; i<db->nDb; i++){
Btree *p = db->aDb[i].pBt;
if( p ){
- if( sqlite3BtreeIsInTrans(p) ){
+ if( sqlite3BtreeTxnState(p)==SQLITE_TXN_WRITE ){
inTrans = 1;
}
sqlite3BtreeRollback(p, tripCode, !schemaChange);
@@ -154034,7 +179615,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){
/* Any deferred constraint violations have now been resolved. */
db->nDeferredCons = 0;
db->nDeferredImmCons = 0;
- db->flags &= ~SQLITE_DeferFKs;
+ db->flags &= ~(u64)(SQLITE_DeferFKs|SQLITE_CorruptRdOnly);
/* If one has been configured, invoke the rollback-hook callback */
if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){
@@ -154108,6 +179689,7 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){
case SQLITE_CANTOPEN_ISDIR: zName = "SQLITE_CANTOPEN_ISDIR"; break;
case SQLITE_CANTOPEN_FULLPATH: zName = "SQLITE_CANTOPEN_FULLPATH"; break;
case SQLITE_CANTOPEN_CONVPATH: zName = "SQLITE_CANTOPEN_CONVPATH"; break;
+ case SQLITE_CANTOPEN_SYMLINK: zName = "SQLITE_CANTOPEN_SYMLINK"; break;
case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
@@ -154139,6 +179721,7 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int rc){
case SQLITE_NOTICE_RECOVER_WAL: zName = "SQLITE_NOTICE_RECOVER_WAL";break;
case SQLITE_NOTICE_RECOVER_ROLLBACK:
zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break;
+ case SQLITE_NOTICE_RBU: zName = "SQLITE_NOTICE_RBU"; break;
case SQLITE_WARNING: zName = "SQLITE_WARNING"; break;
case SQLITE_WARNING_AUTOINDEX: zName = "SQLITE_WARNING_AUTOINDEX"; break;
case SQLITE_DONE: zName = "SQLITE_DONE"; break;
@@ -154229,12 +179812,11 @@ SQLITE_PRIVATE const char *sqlite3ErrStr(int rc){
*/
static int sqliteDefaultBusyCallback(
void *ptr, /* Database connection */
- int count, /* Number of times table has been busy */
- sqlite3_file *pFile /* The file on which the lock occurred */
+ int count /* Number of times table has been busy */
){
-#if SQLITE_OS_WIN || HAVE_USLEEP
+#if SQLITE_OS_WIN || !defined(HAVE_NANOSLEEP) || HAVE_NANOSLEEP
/* This case is for systems that have support for sleeping for fractions of
- ** a second. Examples: All windows systems, unix systems with usleep() */
+ ** a second. Examples: All windows systems, unix systems with nanosleep() */
static const u8 delays[] =
{ 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 };
static const u8 totals[] =
@@ -154244,19 +179826,6 @@ static int sqliteDefaultBusyCallback(
int tmout = db->busyTimeout;
int delay, prior;
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){
- if( count ){
- tmout = 0;
- sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout);
- return 0;
- }else{
- return 1;
- }
- }
-#else
- UNUSED_PARAMETER(pFile);
-#endif
assert( count>=0 );
if( count < NDELAY ){
delay = delays[count];
@@ -154276,7 +179845,6 @@ static int sqliteDefaultBusyCallback(
** must be done in increments of whole seconds */
sqlite3 *db = (sqlite3 *)ptr;
int tmout = ((sqlite3 *)ptr)->busyTimeout;
- UNUSED_PARAMETER(pFile);
if( (count+1)*1000 > tmout ){
return 0;
}
@@ -154294,25 +179862,16 @@ static int sqliteDefaultBusyCallback(
** If this routine returns non-zero, the lock is retried. If it
** returns 0, the operation aborts with an SQLITE_BUSY error.
*/
-SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){
+SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p){
int rc;
if( p->xBusyHandler==0 || p->nBusy<0 ) return 0;
- if( p->bExtraFileArg ){
- /* Add an extra parameter with the pFile pointer to the end of the
- ** callback argument list */
- int (*xTra)(void*,int,sqlite3_file*);
- xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler;
- rc = xTra(p->pBusyArg, p->nBusy, pFile);
- }else{
- /* Legacy style busy handler callback */
- rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
- }
+ rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
if( rc==0 ){
p->nBusy = -1;
}else{
p->nBusy++;
}
- return rc;
+ return rc;
}
/*
@@ -154331,7 +179890,6 @@ SQLITE_API int sqlite3_busy_handler(
db->busyHandler.xBusyHandler = xBusy;
db->busyHandler.pBusyArg = pArg;
db->busyHandler.nBusy = 0;
- db->busyHandler.bExtraFileArg = 0;
db->busyTimeout = 0;
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
@@ -154344,9 +179902,9 @@ SQLITE_API int sqlite3_busy_handler(
** be invoked every nOps opcodes.
*/
SQLITE_API void sqlite3_progress_handler(
- sqlite3 *db,
+ sqlite3 *db,
int nOps,
- int (*xProgress)(void*),
+ int (*xProgress)(void*),
void *pArg
){
#ifdef SQLITE_ENABLE_API_ARMOR
@@ -154382,7 +179940,6 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){
sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
(void*)db);
db->busyTimeout = ms;
- db->busyHandler.bExtraFileArg = 1;
}else{
sqlite3_busy_handler(db, 0, 0);
}
@@ -154394,20 +179951,37 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){
*/
SQLITE_API void sqlite3_interrupt(sqlite3 *db){
#ifdef SQLITE_ENABLE_API_ARMOR
- if( !sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){
+ if( !sqlite3SafetyCheckOk(db)
+ && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE)
+ ){
(void)SQLITE_MISUSE_BKPT;
return;
}
#endif
- db->u1.isInterrupted = 1;
+ AtomicStore(&db->u1.isInterrupted, 1);
}
+/*
+** Return true or false depending on whether or not an interrupt is
+** pending on connection db.
+*/
+SQLITE_API int sqlite3_is_interrupted(sqlite3 *db){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db)
+ && (db==0 || db->eOpenState!=SQLITE_STATE_ZOMBIE)
+ ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+ return AtomicLoad(&db->u1.isInterrupted)!=0;
+}
/*
** This function is exactly the same as sqlite3_create_function(), except
** that it is designed to be called by internal code. The difference is
** that if a malloc() fails in sqlite3_create_function(), an error code
-** is returned and the mallocFailed flag cleared.
+** is returned and the mallocFailed flag cleared.
*/
SQLITE_PRIVATE int sqlite3CreateFunc(
sqlite3 *db,
@@ -154423,7 +179997,6 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
FuncDestructor *pDestructor
){
FuncDef *p;
- int nName;
int extraFlags;
assert( sqlite3_mutex_held(db->mutex) );
@@ -154433,15 +180006,23 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
|| ((xFinal==0)!=(xStep==0)) /* Both or neither of xFinal and xStep */
|| ((xValue==0)!=(xInverse==0)) /* Both or neither of xValue, xInverse */
|| (nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG)
- || (255<(nName = sqlite3Strlen30( zFunctionName)))
+ || (255<sqlite3Strlen30(zFunctionName))
){
return SQLITE_MISUSE_BKPT;
}
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
- extraFlags = enc & SQLITE_DETERMINISTIC;
+ assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
+ extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|
+ SQLITE_SUBTYPE|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE);
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
-
+
+ /* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But
+ ** the meaning is inverted. So flip the bit. */
+ assert( SQLITE_FUNC_UNSAFE==SQLITE_INNOCUOUS );
+ extraFlags ^= SQLITE_FUNC_UNSAFE; /* tag-20230109-1 */
+
+
#ifndef SQLITE_OMIT_UTF16
/* If SQLITE_UTF16 is specified as the encoding type, transform this
** to one of SQLITE_UTF16LE or SQLITE_UTF16BE using the
@@ -154450,25 +180031,38 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
** If SQLITE_ANY is specified, add three versions of the function
** to the hash table.
*/
- if( enc==SQLITE_UTF16 ){
- enc = SQLITE_UTF16NATIVE;
- }else if( enc==SQLITE_ANY ){
- int rc;
- rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8|extraFlags,
- pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
- if( rc==SQLITE_OK ){
- rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE|extraFlags,
- pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
- }
- if( rc!=SQLITE_OK ){
- return rc;
+ switch( enc ){
+ case SQLITE_UTF16:
+ enc = SQLITE_UTF16NATIVE;
+ break;
+ case SQLITE_ANY: {
+ int rc;
+ rc = sqlite3CreateFunc(db, zFunctionName, nArg,
+ (SQLITE_UTF8|extraFlags)^SQLITE_FUNC_UNSAFE, /* tag-20230109-1 */
+ pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3CreateFunc(db, zFunctionName, nArg,
+ (SQLITE_UTF16LE|extraFlags)^SQLITE_FUNC_UNSAFE, /* tag-20230109-1*/
+ pUserData, xSFunc, xStep, xFinal, xValue, xInverse, pDestructor);
+ }
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ enc = SQLITE_UTF16BE;
+ break;
}
- enc = SQLITE_UTF16BE;
+ case SQLITE_UTF8:
+ case SQLITE_UTF16LE:
+ case SQLITE_UTF16BE:
+ break;
+ default:
+ enc = SQLITE_UTF8;
+ break;
}
#else
enc = SQLITE_UTF8;
#endif
-
+
/* Check if an existing function is being overridden or deleted. If so,
** and there are active VMs, then return SQLITE_BUSY. If a function
** is being overridden/deleted but there are no active VMs, allow the
@@ -154477,13 +180071,17 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 0);
if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==(u32)enc && p->nArg==nArg ){
if( db->nVdbeActive ){
- sqlite3ErrorWithMsg(db, SQLITE_BUSY,
+ sqlite3ErrorWithMsg(db, SQLITE_BUSY,
"unable to delete/modify user-function due to active statements");
assert( !db->mallocFailed );
return SQLITE_BUSY;
}else{
sqlite3ExpirePreparedStatements(db, 0);
}
+ }else if( xSFunc==0 && xFinal==0 ){
+ /* Trying to delete a function that does not exist. This is a no-op.
+ ** https://sqlite.org/forum/forumpost/726219164b */
+ return SQLITE_OK;
}
p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 1);
@@ -154502,6 +180100,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
p->u.pDestructor = pDestructor;
p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags;
testcase( p->funcFlags & SQLITE_DETERMINISTIC );
+ testcase( p->funcFlags & SQLITE_DIRECTONLY );
p->xSFunc = xSFunc ? xSFunc : xStep;
p->xFinalize = xFinal;
p->xValue = xValue;
@@ -154551,11 +180150,11 @@ static int createFunctionApi(
pArg->xDestroy = xDestroy;
pArg->pUserData = p;
}
- rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p,
+ rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p,
xSFunc, xStep, xFinal, xValue, xInverse, pArg
);
if( pArg && pArg->nRef==0 ){
- assert( rc!=SQLITE_OK );
+ assert( rc!=SQLITE_OK || (xStep==0 && xFinal==0) );
xDestroy(p);
sqlite3_free(pArg);
}
@@ -154668,7 +180267,7 @@ static void sqlite3InvalidFunction(
**
** If the function already exists as a regular global function, then
** this routine is a no-op. If the function does not exist, then create
-** a new one that always throws a run-time error.
+** a new one that always throws a run-time error.
**
** When virtual tables intend to provide an overloaded function, they
** should call this routine to make sure the global function exists.
@@ -154692,7 +180291,7 @@ SQLITE_API int sqlite3_overload_function(
rc = sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)!=0;
sqlite3_mutex_leave(db->mutex);
if( rc ) return SQLITE_OK;
- zCopy = sqlite3_mprintf(zName);
+ zCopy = sqlite3_mprintf("%s", zName);
if( zCopy==0 ) return SQLITE_NOMEM;
return sqlite3_create_function_v2(db, zName, nArg, SQLITE_UTF8,
zCopy, sqlite3InvalidFunction, 0, 0, sqlite3_free);
@@ -154701,7 +180300,7 @@ SQLITE_API int sqlite3_overload_function(
#ifndef SQLITE_OMIT_TRACE
/*
** Register a trace function. The pArg from the previously registered trace
-** is returned.
+** is returned.
**
** A NULL trace function means that no tracing is executes. A non-NULL
** trace is a pointer to a function that is invoked at the start of each
@@ -154720,7 +180319,7 @@ SQLITE_API void *sqlite3_trace(sqlite3 *db, void(*xTrace)(void*,const char*), vo
sqlite3_mutex_enter(db->mutex);
pOld = db->pTraceArg;
db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0;
- db->xTrace = (int(*)(u32,void*,void*,void*))xTrace;
+ db->trace.xLegacy = xTrace;
db->pTraceArg = pArg;
sqlite3_mutex_leave(db->mutex);
return pOld;
@@ -154744,7 +180343,7 @@ SQLITE_API int sqlite3_trace_v2(
if( mTrace==0 ) xTrace = 0;
if( xTrace==0 ) mTrace = 0;
db->mTrace = mTrace;
- db->xTrace = xTrace;
+ db->trace.xV2 = xTrace;
db->pTraceArg = pArg;
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
@@ -154752,8 +180351,8 @@ SQLITE_API int sqlite3_trace_v2(
#ifndef SQLITE_OMIT_DEPRECATED
/*
-** Register a profile function. The pArg from the previously registered
-** profile function is returned.
+** Register a profile function. The pArg from the previously registered
+** profile function is returned.
**
** A NULL profile function means that no profiling is executes. A non-NULL
** profile is a pointer to a function that is invoked at the conclusion of
@@ -154776,6 +180375,8 @@ SQLITE_API void *sqlite3_profile(
pOld = db->pProfileArg;
db->xProfile = xProfile;
db->pProfileArg = pArg;
+ db->mTrace &= SQLITE_TRACE_NONLEGACY_MASK;
+ if( db->xProfile ) db->mTrace |= SQLITE_TRACE_XPROFILE;
sqlite3_mutex_leave(db->mutex);
return pOld;
}
@@ -154870,6 +180471,12 @@ SQLITE_API void *sqlite3_preupdate_hook(
void *pArg /* First callback argument */
){
void *pRet;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 ){
+ return 0;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
pRet = db->pPreUpdateArg;
db->xPreUpdateCallback = xCallback;
@@ -154879,13 +180486,41 @@ SQLITE_API void *sqlite3_preupdate_hook(
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+/*
+** Register a function to be invoked prior to each autovacuum that
+** determines the number of pages to vacuum.
+*/
+SQLITE_API int sqlite3_autovacuum_pages(
+ sqlite3 *db, /* Attach the hook to this database */
+ unsigned int (*xCallback)(void*,const char*,u32,u32,u32),
+ void *pArg, /* Argument to the function */
+ void (*xDestructor)(void*) /* Destructor for pArg */
+){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ if( xDestructor ) xDestructor(pArg);
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ sqlite3_mutex_enter(db->mutex);
+ if( db->xAutovacDestr ){
+ db->xAutovacDestr(db->pAutovacPagesArg);
+ }
+ db->xAutovacPages = xCallback;
+ db->pAutovacPagesArg = pArg;
+ db->xAutovacDestr = xDestructor;
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+}
+
+
#ifndef SQLITE_OMIT_WAL
/*
** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint().
** Invoke sqlite3_wal_checkpoint if the number of frames in the log file
** is greater than sqlite3.pWalArg cast to an integer (the value configured by
** wal_autocheckpoint()).
-*/
+*/
SQLITE_PRIVATE int sqlite3WalDefaultHook(
void *pClientData, /* Argument */
sqlite3 *db, /* Connection */
@@ -154971,7 +180606,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
return SQLITE_OK;
#else
int rc; /* Return code */
- int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */
+ int iDb; /* Schema to checkpoint */
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
@@ -154988,12 +180623,14 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_TRUNCATE ){
/* EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint
** mode: */
- return SQLITE_MISUSE;
+ return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(db->mutex);
if( zDb && zDb[0] ){
iDb = sqlite3FindDbName(db, zDb);
+ }else{
+ iDb = SQLITE_MAX_DB; /* This means process all schemas */
}
if( iDb<0 ){
rc = SQLITE_ERROR;
@@ -155008,7 +180645,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
/* If there are no active statements, clear the interrupt flag at this
** point. */
if( db->nVdbeActive==0 ){
- db->u1.isInterrupted = 0;
+ AtomicStore(&db->u1.isInterrupted, 0);
}
sqlite3_mutex_leave(db->mutex);
@@ -155019,7 +180656,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
/*
** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points
-** to contains a zero-length string, all attached databases are
+** to contains a zero-length string, all attached databases are
** checkpointed.
*/
SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
@@ -155033,16 +180670,16 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
** Run a checkpoint on database iDb. This is a no-op if database iDb is
** not currently open in WAL mode.
**
-** If a transaction is open on the database being checkpointed, this
-** function returns SQLITE_LOCKED and a checkpoint is not attempted. If
-** an error occurs while running the checkpoint, an SQLite error code is
+** If a transaction is open on the database being checkpointed, this
+** function returns SQLITE_LOCKED and a checkpoint is not attempted. If
+** an error occurs while running the checkpoint, an SQLite error code is
** returned (i.e. SQLITE_IOERR). Otherwise, SQLITE_OK.
**
** The mutex on database handle db should be held by the caller. The mutex
** associated with the specific b-tree being checkpointed is taken by
** this function while the checkpoint is running.
**
-** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are
+** If iDb is passed SQLITE_MAX_DB then all attached databases are
** checkpointed. If an error is encountered it is returned immediately -
** no attempt is made to checkpoint any remaining databases.
**
@@ -155057,9 +180694,11 @@ SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog
assert( sqlite3_mutex_held(db->mutex) );
assert( !pnLog || *pnLog==-1 );
assert( !pnCkpt || *pnCkpt==-1 );
+ testcase( iDb==SQLITE_MAX_ATTACHED ); /* See forum post a006d86f72 */
+ testcase( iDb==SQLITE_MAX_DB );
for(i=0; i<db->nDb && rc==SQLITE_OK; i++){
- if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){
+ if( i==iDb || iDb==SQLITE_MAX_DB ){
rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt);
pnLog = 0;
pnCkpt = 0;
@@ -155127,7 +180766,7 @@ SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){
z = sqlite3ErrStr(SQLITE_NOMEM_BKPT);
}else{
testcase( db->pErr==0 );
- z = (char*)sqlite3_value_text(db->pErr);
+ z = db->errCode ? (char*)sqlite3_value_text(db->pErr) : 0;
assert( !db->mallocFailed );
if( z==0 ){
z = sqlite3ErrStr(db->errCode);
@@ -155137,6 +180776,19 @@ SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){
return z;
}
+/*
+** Return the byte offset of the most recent error
+*/
+SQLITE_API int sqlite3_error_offset(sqlite3 *db){
+ int iOffset = -1;
+ if( db && sqlite3SafetyCheckSickOrOk(db) && db->errCode ){
+ sqlite3_mutex_enter(db->mutex);
+ iOffset = db->errByteOffset;
+ sqlite3_mutex_leave(db->mutex);
+ }
+ return iOffset;
+}
+
#ifndef SQLITE_OMIT_UTF16
/*
** Return UTF-16 encoded English language explanation of the most recent
@@ -155204,7 +180856,7 @@ SQLITE_API int sqlite3_extended_errcode(sqlite3 *db){
}
SQLITE_API int sqlite3_system_errno(sqlite3 *db){
return db ? db->iSysErrno : 0;
-}
+}
/*
** Return a string that describes the kind of error specified in the
@@ -155221,7 +180873,7 @@ SQLITE_API const char *sqlite3_errstr(int rc){
*/
static int createCollation(
sqlite3* db,
- const char *zName,
+ const char *zName,
u8 enc,
void* pCtx,
int(*xCompare)(void*,int,const void*,int,const void*),
@@ -155229,7 +180881,7 @@ static int createCollation(
){
CollSeq *pColl;
int enc2;
-
+
assert( sqlite3_mutex_held(db->mutex) );
/* If SQLITE_UTF16 is specified as the encoding type, transform this
@@ -155246,14 +180898,14 @@ static int createCollation(
return SQLITE_MISUSE_BKPT;
}
- /* Check if this call is removing or replacing an existing collation
+ /* Check if this call is removing or replacing an existing collation
** sequence. If so, and there are active VMs, return busy. If there
** are no active VMs, invalidate any pre-compiled statements.
*/
pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 0);
if( pColl && pColl->xCmp ){
if( db->nVdbeActive ){
- sqlite3ErrorWithMsg(db, SQLITE_BUSY,
+ sqlite3ErrorWithMsg(db, SQLITE_BUSY,
"unable to delete/modify collation sequence due to active statements");
return SQLITE_BUSY;
}
@@ -155264,7 +180916,7 @@ static int createCollation(
** then any copies made by synthCollSeq() need to be invalidated.
** Also, collation destructor - CollSeq.xDel() - function may need
** to be called.
- */
+ */
if( (pColl->enc & ~SQLITE_UTF16_ALIGNED)==enc2 ){
CollSeq *aColl = sqlite3HashFind(&db->aCollSeq, zName);
int j;
@@ -155397,6 +181049,8 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
if( newLimit>=0 ){ /* IMP: R-52476-28732 */
if( newLimit>aHardLimit[limitId] ){
newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */
+ }else if( newLimit<1 && limitId==SQLITE_LIMIT_LENGTH ){
+ newLimit = 1;
}
db->aLimit[limitId] = newLimit;
}
@@ -155413,17 +181067,19 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
** query parameter. The second argument contains the URI (or non-URI filename)
** itself. When this function is called the *pFlags variable should contain
** the default flags to open the database handle with. The value stored in
-** *pFlags may be updated before returning if the URI filename contains
+** *pFlags may be updated before returning if the URI filename contains
** "cache=xxx" or "mode=xxx" query parameters.
**
** If successful, SQLITE_OK is returned. In this case *ppVfs is set to point to
** the VFS that should be used to open the database file. *pzFile is set to
-** point to a buffer containing the name of the file to open. It is the
-** responsibility of the caller to eventually call sqlite3_free() to release
-** this buffer.
+** point to a buffer containing the name of the file to open. The value
+** stored in *pzFile is a database name acceptable to sqlite3_uri_parameter()
+** and is in the same format as names created using sqlite3_create_filename().
+** The caller must invoke sqlite3_free_filename() (not sqlite3_free()!) on
+** the value returned in *pzFile to avoid a memory leak.
**
** If an error occurs, then an SQLite error code is returned and *pzErrMsg
-** may be set to point to a buffer containing an English language error
+** may be set to point to a buffer containing an English language error
** message. It is the responsibility of the caller to eventually release
** this buffer by calling sqlite3_free().
*/
@@ -155431,7 +181087,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
const char *zDefaultVfs, /* VFS to use if no "vfs=xxx" query option */
const char *zUri, /* Nul-terminated URI to parse */
unsigned int *pFlags, /* IN/OUT: SQLITE_OPEN_XXX flags */
- sqlite3_vfs **ppVfs, /* OUT: VFS to use */
+ sqlite3_vfs **ppVfs, /* OUT: VFS to use */
char **pzFile, /* OUT: Filename component of URI */
char **pzErrMsg /* OUT: Error message (if rc!=SQLITE_OK) */
){
@@ -155444,17 +181100,17 @@ SQLITE_PRIVATE int sqlite3ParseUri(
assert( *pzErrMsg==0 );
- if( ((flags & SQLITE_OPEN_URI) /* IMP: R-48725-32206 */
- || sqlite3GlobalConfig.bOpenUri) /* IMP: R-51689-46548 */
- && nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */
+ if( ((flags & SQLITE_OPEN_URI) /* IMP: R-48725-32206 */
+ || AtomicLoad(&sqlite3GlobalConfig.bOpenUri)) /* IMP: R-51689-46548 */
+ && nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */
){
char *zOpt;
int eState; /* Parser state when parsing URI */
int iIn; /* Input character index */
int iOut = 0; /* Output character index */
- u64 nByte = nUri+2; /* Bytes of space to allocate */
+ u64 nByte = nUri+8; /* Bytes of space to allocate */
- /* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen
+ /* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen
** method that there may be extra parameters following the file-name. */
flags |= SQLITE_OPEN_URI;
@@ -155462,6 +181118,9 @@ SQLITE_PRIVATE int sqlite3ParseUri(
zFile = sqlite3_malloc64(nByte);
if( !zFile ) return SQLITE_NOMEM_BKPT;
+ memset(zFile, 0, 4); /* 4-byte of 0x00 is the start of DB name marker */
+ zFile += 4;
+
iIn = 5;
#ifdef SQLITE_ALLOW_URI_AUTHORITY
if( strncmp(zUri+5, "///", 3)==0 ){
@@ -155469,7 +181128,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
/* The following condition causes URIs with five leading / characters
** like file://///host/path to be converted into UNCs like //host/path.
** The correct URI for that UNC has only two or four leading / characters
- ** file://host/path or file:////host/path. But 5 leading slashes is a
+ ** file://host/path or file:////host/path. But 5 leading slashes is a
** common error, we are told, so we handle it as a special case. */
if( strncmp(zUri+7, "///", 3)==0 ){ iIn++; }
}else if( strncmp(zUri+5, "//localhost/", 12)==0 ){
@@ -155481,7 +181140,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
iIn = 7;
while( zUri[iIn] && zUri[iIn]!='/' ) iIn++;
if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){
- *pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s",
+ *pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s",
iIn-7, &zUri[7]);
rc = SQLITE_ERROR;
goto parse_uri_out;
@@ -155489,8 +181148,8 @@ SQLITE_PRIVATE int sqlite3ParseUri(
}
#endif
- /* Copy the filename and any query parameters into the zFile buffer.
- ** Decode %HH escape codes along the way.
+ /* Copy the filename and any query parameters into the zFile buffer.
+ ** Decode %HH escape codes along the way.
**
** Within this loop, variable eState may be set to 0, 1 or 2, depending
** on the parsing context. As follows:
@@ -155502,9 +181161,9 @@ SQLITE_PRIVATE int sqlite3ParseUri(
eState = 0;
while( (c = zUri[iIn])!=0 && c!='#' ){
iIn++;
- if( c=='%'
- && sqlite3Isxdigit(zUri[iIn])
- && sqlite3Isxdigit(zUri[iIn+1])
+ if( c=='%'
+ && sqlite3Isxdigit(zUri[iIn])
+ && sqlite3Isxdigit(zUri[iIn+1])
){
int octet = (sqlite3HexToInt(zUri[iIn++]) << 4);
octet += sqlite3HexToInt(zUri[iIn++]);
@@ -155516,7 +181175,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
** case we ignore all text in the remainder of the path, name or
** value currently being parsed. So ignore the current character
** and skip to the next "?", "=" or "&", as appropriate. */
- while( (c = zUri[iIn])!=0 && c!='#'
+ while( (c = zUri[iIn])!=0 && c!='#'
&& (eState!=0 || c!='?')
&& (eState!=1 || (c!='=' && c!='&'))
&& (eState!=2 || c!='&')
@@ -155551,10 +181210,9 @@ SQLITE_PRIVATE int sqlite3ParseUri(
zFile[iOut++] = c;
}
if( eState==1 ) zFile[iOut++] = '\0';
- zFile[iOut++] = '\0';
- zFile[iOut++] = '\0';
+ memset(zFile+iOut, 0, 4); /* end-of-options + empty journal filenames */
- /* Check if there were any options specified that should be interpreted
+ /* Check if there were any options specified that should be interpreted
** here. Options that are interpreted here include "vfs" and those that
** correspond to flags that may be passed to the sqlite3_open_v2()
** method. */
@@ -155590,7 +181248,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
if( nOpt==4 && memcmp("mode", zOpt, 4)==0 ){
static struct OpenMode aOpenMode[] = {
{ "ro", SQLITE_OPEN_READONLY },
- { "rw", SQLITE_OPEN_READWRITE },
+ { "rw", SQLITE_OPEN_READWRITE },
{ "rwc", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE },
{ "memory", SQLITE_OPEN_MEMORY },
{ 0, 0 }
@@ -155632,13 +181290,14 @@ SQLITE_PRIVATE int sqlite3ParseUri(
}
}else{
- zFile = sqlite3_malloc64(nUri+2);
+ zFile = sqlite3_malloc64(nUri+8);
if( !zFile ) return SQLITE_NOMEM_BKPT;
+ memset(zFile, 0, 4);
+ zFile += 4;
if( nUri ){
memcpy(zFile, zUri, nUri);
}
- zFile[nUri] = '\0';
- zFile[nUri+1] = '\0';
+ memset(zFile+nUri, 0, 4);
flags &= ~SQLITE_OPEN_URI;
}
@@ -155649,7 +181308,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
}
parse_uri_out:
if( rc!=SQLITE_OK ){
- sqlite3_free(zFile);
+ sqlite3_free_filename(zFile);
zFile = 0;
}
*pFlags = flags;
@@ -155657,10 +181316,26 @@ SQLITE_PRIVATE int sqlite3ParseUri(
return rc;
}
+/*
+** This routine does the core work of extracting URI parameters from a
+** database filename for the sqlite3_uri_parameter() interface.
+*/
+static const char *uriParameter(const char *zFilename, const char *zParam){
+ zFilename += sqlite3Strlen30(zFilename) + 1;
+ while( ALWAYS(zFilename!=0) && zFilename[0] ){
+ int x = strcmp(zFilename, zParam);
+ zFilename += sqlite3Strlen30(zFilename) + 1;
+ if( x==0 ) return zFilename;
+ zFilename += sqlite3Strlen30(zFilename) + 1;
+ }
+ return 0;
+}
+
+
/*
** This routine does the work of opening a database on behalf of
-** sqlite3_open() and sqlite3_open16(). The database filename "zFilename"
+** sqlite3_open() and sqlite3_open16(). The database filename "zFilename"
** is UTF-8 encoded.
*/
static int openDatabase(
@@ -155674,6 +181349,7 @@ static int openDatabase(
int isThreadsafe; /* True for threadsafe connections */
char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */
char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */
+ int i; /* Loop counter */
#ifdef SQLITE_ENABLE_API_ARMOR
if( ppDb==0 ) return SQLITE_MISUSE_BKPT;
@@ -155706,18 +181382,18 @@ static int openDatabase(
** dealt with in the previous code block. Besides these, the only
** valid input flags for sqlite3_open_v2() are SQLITE_OPEN_READONLY,
** SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, SQLITE_OPEN_SHAREDCACHE,
- ** SQLITE_OPEN_PRIVATECACHE, and some reserved bits. Silently mask
- ** off all other flags.
+ ** SQLITE_OPEN_PRIVATECACHE, SQLITE_OPEN_EXRESCODE, and some reserved
+ ** bits. Silently mask off all other flags.
*/
flags &= ~( SQLITE_OPEN_DELETEONCLOSE |
SQLITE_OPEN_EXCLUSIVE |
SQLITE_OPEN_MAIN_DB |
- SQLITE_OPEN_TEMP_DB |
- SQLITE_OPEN_TRANSIENT_DB |
- SQLITE_OPEN_MAIN_JOURNAL |
- SQLITE_OPEN_TEMP_JOURNAL |
- SQLITE_OPEN_SUBJOURNAL |
- SQLITE_OPEN_MASTER_JOURNAL |
+ SQLITE_OPEN_TEMP_DB |
+ SQLITE_OPEN_TRANSIENT_DB |
+ SQLITE_OPEN_MAIN_JOURNAL |
+ SQLITE_OPEN_TEMP_JOURNAL |
+ SQLITE_OPEN_SUBJOURNAL |
+ SQLITE_OPEN_SUPER_JOURNAL |
SQLITE_OPEN_NOMUTEX |
SQLITE_OPEN_FULLMUTEX |
SQLITE_OPEN_WAL
@@ -155726,7 +181402,7 @@ static int openDatabase(
/* Allocate the sqlite data structure */
db = sqlite3MallocZero( sizeof(sqlite3) );
if( db==0 ) goto opendb_out;
- if( isThreadsafe
+ if( isThreadsafe
#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS
|| sqlite3GlobalConfig.bCoreMutex
#endif
@@ -155742,11 +181418,12 @@ static int openDatabase(
}
}
sqlite3_mutex_enter(db->mutex);
- db->errMask = 0xff;
+ db->errMask = (flags & SQLITE_OPEN_EXRESCODE)!=0 ? 0xffffffff : 0xff;
db->nDb = 2;
- db->magic = SQLITE_MAGIC_BUSY;
+ db->eOpenState = SQLITE_STATE_BUSY;
db->aDb = db->aDbStatic;
db->lookaside.bDisable = 1;
+ db->lookaside.sz = 0;
assert( sizeof(db->aLimit)==sizeof(aHardLimit) );
memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit));
@@ -155755,8 +181432,47 @@ static int openDatabase(
db->nextAutovac = -1;
db->szMmap = sqlite3GlobalConfig.szMmap;
db->nextPagesize = 0;
+ db->init.azInit = sqlite3StdType; /* Any array of string ptrs will do */
+#ifdef SQLITE_ENABLE_SORTER_MMAP
+ /* Beginning with version 3.37.0, using the VFS xFetch() API to memory-map
+ ** the temporary files used to do external sorts (see code in vdbesort.c)
+ ** is disabled. It can still be used either by defining
+ ** SQLITE_ENABLE_SORTER_MMAP at compile time or by using the
+ ** SQLITE_TESTCTRL_SORTER_MMAP test-control at runtime. */
db->nMaxSorterMmap = 0x7FFFFFFF;
- db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_CacheSpill
+#endif
+ db->flags |= SQLITE_ShortColNames
+ | SQLITE_EnableTrigger
+ | SQLITE_EnableView
+ | SQLITE_CacheSpill
+#if !defined(SQLITE_TRUSTED_SCHEMA) || SQLITE_TRUSTED_SCHEMA+0!=0
+ | SQLITE_TrustedSchema
+#endif
+/* The SQLITE_DQS compile-time option determines the default settings
+** for SQLITE_DBCONFIG_DQS_DDL and SQLITE_DBCONFIG_DQS_DML.
+**
+** SQLITE_DQS SQLITE_DBCONFIG_DQS_DDL SQLITE_DBCONFIG_DQS_DML
+** ---------- ----------------------- -----------------------
+** undefined on on
+** 3 on on
+** 2 on off
+** 1 off on
+** 0 off off
+**
+** Legacy behavior is 3 (double-quoted string literals are allowed anywhere)
+** and so that is the default. But developers are encouraged to use
+** -DSQLITE_DQS=0 (best) or -DSQLITE_DQS=1 (second choice) if possible.
+*/
+#if !defined(SQLITE_DQS)
+# define SQLITE_DQS 3
+#endif
+#if (SQLITE_DQS&1)==1
+ | SQLITE_DqsDML
+#endif
+#if (SQLITE_DQS&2)==2
+ | SQLITE_DqsDDL
+#endif
+
#if !defined(SQLITE_DEFAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX
| SQLITE_AutoIndex
#endif
@@ -155790,6 +181506,12 @@ static int openDatabase(
#if defined(SQLITE_DEFAULT_DEFENSIVE)
| SQLITE_Defensive
#endif
+#if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE)
+ | SQLITE_LegacyAlter
+#endif
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+ | SQLITE_StmtScanStatus
+#endif
;
sqlite3HashInit(&db->aCollSeq);
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -155807,19 +181529,27 @@ static int openDatabase(
createCollation(db, sqlite3StrBINARY, SQLITE_UTF16BE, 0, binCollFunc, 0);
createCollation(db, sqlite3StrBINARY, SQLITE_UTF16LE, 0, binCollFunc, 0);
createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0);
- createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0);
+ createCollation(db, "RTRIM", SQLITE_UTF8, 0, rtrimCollFunc, 0);
if( db->mallocFailed ){
goto opendb_out;
}
- /* EVIDENCE-OF: R-08308-17224 The default collating function for all
- ** strings is BINARY.
- */
- db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, sqlite3StrBINARY, 0);
- assert( db->pDfltColl!=0 );
+
+#if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)
+ /* Process magic filenames ":localStorage:" and ":sessionStorage:" */
+ if( zFilename && zFilename[0]==':' ){
+ if( strcmp(zFilename, ":localStorage:")==0 ){
+ zFilename = "file:local?vfs=kvvfs";
+ flags |= SQLITE_OPEN_URI;
+ }else if( strcmp(zFilename, ":sessionStorage:")==0 ){
+ zFilename = "file:session?vfs=kvvfs";
+ flags |= SQLITE_OPEN_URI;
+ }
+ }
+#endif /* SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) */
/* Parse the filename/URI argument
**
- ** Only allow sensible combinations of bits in the flags argument.
+ ** Only allow sensible combinations of bits in the flags argument.
** Throw an error if any non-sense combination is used. If we
** do not block illegal combinations here, it could trigger
** assert() statements in deeper layers. Sensible combinations
@@ -155837,7 +181567,7 @@ static int openDatabase(
testcase( (1<<(flags&7))==0x04 ); /* READWRITE */
testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */
if( ((1<<(flags&7)) & 0x46)==0 ){
- rc = SQLITE_MISUSE_BKPT; /* IMP: R-65497-44594 */
+ rc = SQLITE_MISUSE_BKPT; /* IMP: R-18321-05872 */
}else{
rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
}
@@ -155847,6 +181577,12 @@ static int openDatabase(
sqlite3_free(zErrMsg);
goto opendb_out;
}
+ assert( db->pVfs!=0 );
+#if SQLITE_OS_KV || defined(SQLITE_OS_KV_OPTIONAL)
+ if( sqlite3_stricmp(db->pVfs->zName, "kvvfs")==0 ){
+ db->temp_store = 2;
+ }
+#endif
/* Open the backend database driver */
rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0,
@@ -155860,19 +181596,21 @@ static int openDatabase(
}
sqlite3BtreeEnter(db->aDb[0].pBt);
db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt);
- if( !db->mallocFailed ) ENC(db) = SCHEMA_ENC(db);
+ if( !db->mallocFailed ){
+ sqlite3SetTextEncoding(db, SCHEMA_ENC(db));
+ }
sqlite3BtreeLeave(db->aDb[0].pBt);
db->aDb[1].pSchema = sqlite3SchemaGet(db, 0);
/* The default safety_level for the main database is FULL; for the temp
- ** database it is OFF. This matches the pager layer defaults.
+ ** database it is OFF. This matches the pager layer defaults.
*/
db->aDb[0].zDbSName = "main";
db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
db->aDb[1].zDbSName = "temp";
db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF;
- db->magic = SQLITE_MAGIC_OPEN;
+ db->eOpenState = SQLITE_STATE_OPEN;
if( db->mallocFailed ){
goto opendb_out;
}
@@ -155885,14 +181623,11 @@ static int openDatabase(
sqlite3RegisterPerConnectionBuiltinFunctions(db);
rc = sqlite3_errcode(db);
-#ifdef SQLITE_ENABLE_FTS5
- /* Register any built-in FTS5 module before loading the automatic
- ** extensions. This allows automatic extensions to register FTS5
- ** tokenizers and auxiliary functions. */
- if( !db->mallocFailed && rc==SQLITE_OK ){
- rc = sqlite3Fts5Init(db);
+
+ /* Load compiled-in extensions */
+ for(i=0; rc==SQLITE_OK && i<ArraySize(sqlite3BuiltinExtensions); i++){
+ rc = sqlite3BuiltinExtensions[i](db);
}
-#endif
/* Load automatic extensions - extensions that have been registered
** using the sqlite3_automatic_extension() API.
@@ -155905,60 +181640,11 @@ static int openDatabase(
}
}
-#ifdef SQLITE_ENABLE_FTS1
- if( !db->mallocFailed ){
- extern int sqlite3Fts1Init(sqlite3*);
- rc = sqlite3Fts1Init(db);
- }
-#endif
-
-#ifdef SQLITE_ENABLE_FTS2
- if( !db->mallocFailed && rc==SQLITE_OK ){
- extern int sqlite3Fts2Init(sqlite3*);
- rc = sqlite3Fts2Init(db);
- }
-#endif
-
-#ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */
- if( !db->mallocFailed && rc==SQLITE_OK ){
- rc = sqlite3Fts3Init(db);
- }
-#endif
-
-#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
- if( !db->mallocFailed && rc==SQLITE_OK ){
- rc = sqlite3IcuInit(db);
- }
-#endif
-
-#ifdef SQLITE_ENABLE_RTREE
- if( !db->mallocFailed && rc==SQLITE_OK){
- rc = sqlite3RtreeInit(db);
- }
-#endif
-
-#ifdef SQLITE_ENABLE_DBPAGE_VTAB
- if( !db->mallocFailed && rc==SQLITE_OK){
- rc = sqlite3DbpageRegister(db);
- }
-#endif
-
-#ifdef SQLITE_ENABLE_DBSTAT_VTAB
- if( !db->mallocFailed && rc==SQLITE_OK){
- rc = sqlite3DbstatRegister(db);
- }
-#endif
-
-#ifdef SQLITE_ENABLE_JSON1
- if( !db->mallocFailed && rc==SQLITE_OK){
- rc = sqlite3Json1Init(db);
- }
-#endif
-
-#ifdef SQLITE_ENABLE_STMTVTAB
- if( !db->mallocFailed && rc==SQLITE_OK){
- rc = sqlite3StmtVtabInit(db);
- }
+#ifdef SQLITE_ENABLE_INTERNAL_FUNCTIONS
+ /* Testing use only!!! The -DSQLITE_ENABLE_INTERNAL_FUNCTIONS=1 compile-time
+ ** option gives access to internal functions by default.
+ ** Testing use only!!! */
+ db->mDbFlags |= DBFLAG_InternalFunc;
#endif
/* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking
@@ -155986,12 +181672,12 @@ opendb_out:
sqlite3_mutex_leave(db->mutex);
}
rc = sqlite3_errcode(db);
- assert( db!=0 || rc==SQLITE_NOMEM );
- if( rc==SQLITE_NOMEM ){
+ assert( db!=0 || (rc&0xff)==SQLITE_NOMEM );
+ if( (rc&0xff)==SQLITE_NOMEM ){
sqlite3_close(db);
db = 0;
}else if( rc!=SQLITE_OK ){
- db->magic = SQLITE_MAGIC_SICK;
+ db->eOpenState = SQLITE_STATE_SICK;
}
*ppDb = db;
#ifdef SQLITE_ENABLE_SQLLOG
@@ -156001,33 +181687,17 @@ opendb_out:
sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0);
}
#endif
-#if defined(SQLITE_HAS_CODEC)
- if( rc==SQLITE_OK ){
- const char *zKey;
- if( (zKey = sqlite3_uri_parameter(zOpen, "hexkey"))!=0 && zKey[0] ){
- u8 iByte;
- int i;
- char zDecoded[40];
- for(i=0, iByte=0; i<sizeof(zDecoded)*2 && sqlite3Isxdigit(zKey[i]); i++){
- iByte = (iByte<<4) + sqlite3HexToInt(zKey[i]);
- if( (i&1)!=0 ) zDecoded[i/2] = iByte;
- }
- sqlite3_key_v2(db, 0, zDecoded, i/2);
- }else if( (zKey = sqlite3_uri_parameter(zOpen, "key"))!=0 ){
- sqlite3_key_v2(db, 0, zKey, sqlite3Strlen30(zKey));
- }
- }
-#endif
- sqlite3_free(zOpen);
- return rc & 0xff;
+ sqlite3_free_filename(zOpen);
+ return rc;
}
+
/*
** Open a new database handle.
*/
SQLITE_API int sqlite3_open(
- const char *zFilename,
- sqlite3 **ppDb
+ const char *zFilename,
+ sqlite3 **ppDb
){
return openDatabase(zFilename, ppDb,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
@@ -156046,7 +181716,7 @@ SQLITE_API int sqlite3_open_v2(
** Open a new database handle.
*/
SQLITE_API int sqlite3_open16(
- const void *zFilename,
+ const void *zFilename,
sqlite3 **ppDb
){
char const *zFilename8; /* zFilename encoded in UTF-8 instead of UTF-16 */
@@ -156085,9 +181755,9 @@ SQLITE_API int sqlite3_open16(
** Register a new collation sequence with the database handle db.
*/
SQLITE_API int sqlite3_create_collation(
- sqlite3* db,
- const char *zName,
- int enc,
+ sqlite3* db,
+ const char *zName,
+ int enc,
void* pCtx,
int(*xCompare)(void*,int,const void*,int,const void*)
){
@@ -156098,9 +181768,9 @@ SQLITE_API int sqlite3_create_collation(
** Register a new collation sequence with the database handle db.
*/
SQLITE_API int sqlite3_create_collation_v2(
- sqlite3* db,
- const char *zName,
- int enc,
+ sqlite3* db,
+ const char *zName,
+ int enc,
void* pCtx,
int(*xCompare)(void*,int,const void*,int,const void*),
void(*xDel)(void*)
@@ -156123,9 +181793,9 @@ SQLITE_API int sqlite3_create_collation_v2(
** Register a new collation sequence with the database handle db.
*/
SQLITE_API int sqlite3_create_collation16(
- sqlite3* db,
+ sqlite3* db,
const void *zName,
- int enc,
+ int enc,
void* pCtx,
int(*xCompare)(void*,int,const void*,int,const void*)
){
@@ -156153,8 +181823,8 @@ SQLITE_API int sqlite3_create_collation16(
** db. Replace any previously installed collation sequence factory.
*/
SQLITE_API int sqlite3_collation_needed(
- sqlite3 *db,
- void *pCollNeededArg,
+ sqlite3 *db,
+ void *pCollNeededArg,
void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*)
){
#ifdef SQLITE_ENABLE_API_ARMOR
@@ -156174,8 +181844,8 @@ SQLITE_API int sqlite3_collation_needed(
** db. Replace any previously installed collation sequence factory.
*/
SQLITE_API int sqlite3_collation_needed16(
- sqlite3 *db,
- void *pCollNeededArg,
+ sqlite3 *db,
+ void *pCollNeededArg,
void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*)
){
#ifdef SQLITE_ENABLE_API_ARMOR
@@ -156190,6 +181860,69 @@ SQLITE_API int sqlite3_collation_needed16(
}
#endif /* SQLITE_OMIT_UTF16 */
+/*
+** Find existing client data.
+*/
+SQLITE_API void *sqlite3_get_clientdata(sqlite3 *db, const char *zName){
+ DbClientData *p;
+ sqlite3_mutex_enter(db->mutex);
+ for(p=db->pDbData; p; p=p->pNext){
+ if( strcmp(p->zName, zName)==0 ){
+ void *pResult = p->pData;
+ sqlite3_mutex_leave(db->mutex);
+ return pResult;
+ }
+ }
+ sqlite3_mutex_leave(db->mutex);
+ return 0;
+}
+
+/*
+** Add new client data to a database connection.
+*/
+SQLITE_API int sqlite3_set_clientdata(
+ sqlite3 *db, /* Attach client data to this connection */
+ const char *zName, /* Name of the client data */
+ void *pData, /* The client data itself */
+ void (*xDestructor)(void*) /* Destructor */
+){
+ DbClientData *p, **pp;
+ sqlite3_mutex_enter(db->mutex);
+ pp = &db->pDbData;
+ for(p=db->pDbData; p && strcmp(p->zName,zName); p=p->pNext){
+ pp = &p->pNext;
+ }
+ if( p ){
+ assert( p->pData!=0 );
+ if( p->xDestructor ) p->xDestructor(p->pData);
+ if( pData==0 ){
+ *pp = p->pNext;
+ sqlite3_free(p);
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+ }
+ }else if( pData==0 ){
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+ }else{
+ size_t n = strlen(zName);
+ p = sqlite3_malloc64( sizeof(DbClientData)+n+1 );
+ if( p==0 ){
+ if( xDestructor ) xDestructor(pData);
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_NOMEM;
+ }
+ memcpy(p->zName, zName, n+1);
+ p->pNext = db->pDbData;
+ db->pDbData = p;
+ }
+ p->pData = pData;
+ p->xDestructor = xDestructor;
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+}
+
+
#ifndef SQLITE_OMIT_DEPRECATED
/*
** This function is now an anachronism. It used to be used to recover from a
@@ -156244,13 +181977,15 @@ SQLITE_PRIVATE int sqlite3CantopenError(int lineno){
testcase( sqlite3GlobalConfig.xLog!=0 );
return sqlite3ReportError(SQLITE_CANTOPEN, lineno, "cannot open file");
}
-#ifdef SQLITE_DEBUG
+#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_CORRUPT_PGNO)
SQLITE_PRIVATE int sqlite3CorruptPgnoError(int lineno, Pgno pgno){
char zMsg[100];
sqlite3_snprintf(sizeof(zMsg), zMsg, "database corruption page %d", pgno);
testcase( sqlite3GlobalConfig.xLog!=0 );
return sqlite3ReportError(SQLITE_CORRUPT, lineno, zMsg);
}
+#endif
+#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3NomemError(int lineno){
testcase( sqlite3GlobalConfig.xLog!=0 );
return sqlite3ReportError(SQLITE_NOMEM, lineno, "OOM");
@@ -156316,18 +182051,18 @@ SQLITE_API int sqlite3_table_column_metadata(
/* Locate the table in question */
pTab = sqlite3FindTable(db, zTableName, zDbName);
- if( !pTab || pTab->pSelect ){
+ if( !pTab || IsView(pTab) ){
pTab = 0;
goto error_out;
}
/* Find the column for which info is requested */
if( zColumnName==0 ){
- /* Query for existance of table only */
+ /* Query for existence of table only */
}else{
for(iCol=0; iCol<pTab->nCol; iCol++){
pCol = &pTab->aCol[iCol];
- if( 0==sqlite3StrICmp(pCol->zName, zColumnName) ){
+ if( 0==sqlite3StrICmp(pCol->zCnName, zColumnName) ){
break;
}
}
@@ -156345,16 +182080,16 @@ SQLITE_API int sqlite3_table_column_metadata(
/* The following block stores the meta information that will be returned
** to the caller in local variables zDataType, zCollSeq, notnull, primarykey
** and autoinc. At this point there are two possibilities:
- **
- ** 1. The specified column name was rowid", "oid" or "_rowid_"
- ** and there is no explicitly declared IPK column.
**
- ** 2. The table is not a view and the column name identified an
+ ** 1. The specified column name was rowid", "oid" or "_rowid_"
+ ** and there is no explicitly declared IPK column.
+ **
+ ** 2. The table is not a view and the column name identified an
** explicitly declared column. Copy meta information from *pCol.
- */
+ */
if( pCol ){
zDataType = sqlite3ColumnType(pCol,0);
- zCollSeq = pCol->zColl;
+ zCollSeq = sqlite3ColumnColl(pCol);
notnull = pCol->notNull!=0;
primarykey = (pCol->colFlags & COLFLAG_PRIMKEY)!=0;
autoinc = pTab->iPKey==iCol && (pTab->tabFlags & TF_Autoincrement)!=0;
@@ -156401,10 +182136,10 @@ SQLITE_API int sqlite3_sleep(int ms){
pVfs = sqlite3_vfs_find(0);
if( pVfs==0 ) return 0;
- /* This function works in milliseconds, but the underlying OsSleep()
+ /* This function works in milliseconds, but the underlying OsSleep()
** API uses microseconds. Hence the 1000's.
*/
- rc = (sqlite3OsSleep(pVfs, 1000*ms)/1000);
+ rc = (sqlite3OsSleep(pVfs, ms<0 ? 0 : 1000*ms)/1000);
return rc;
}
@@ -156453,8 +182188,20 @@ SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, vo
}else if( op==SQLITE_FCNTL_DATA_VERSION ){
*(unsigned int*)pArg = sqlite3PagerDataVersion(pPager);
rc = SQLITE_OK;
+ }else if( op==SQLITE_FCNTL_RESERVE_BYTES ){
+ int iNew = *(int*)pArg;
+ *(int*)pArg = sqlite3BtreeGetRequestedReserve(pBtree);
+ if( iNew>=0 && iNew<=255 ){
+ sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0);
+ }
+ rc = SQLITE_OK;
+ }else if( op==SQLITE_FCNTL_RESET_CACHE ){
+ sqlite3BtreeClearCache(pBtree);
+ rc = SQLITE_OK;
}else{
+ int nSave = db->busyHandler.nBusy;
rc = sqlite3OsFileControl(fd, op, pArg);
+ db->busyHandler.nBusy = nSave;
}
sqlite3BtreeLeave(pBtree);
}
@@ -156492,15 +182239,60 @@ SQLITE_API int sqlite3_test_control(int op, ...){
break;
}
- /*
- ** Reset the PRNG back to its uninitialized state. The next call
- ** to sqlite3_randomness() will reseed the PRNG using a single call
- ** to the xRandomness method of the default VFS.
+ /* sqlite3_test_control(SQLITE_TESTCTRL_PRNG_SEED, int x, sqlite3 *db);
+ **
+ ** Control the seed for the pseudo-random number generator (PRNG) that
+ ** is built into SQLite. Cases:
+ **
+ ** x!=0 && db!=0 Seed the PRNG to the current value of the
+ ** schema cookie in the main database for db, or
+ ** x if the schema cookie is zero. This case
+ ** is convenient to use with database fuzzers
+ ** as it allows the fuzzer some control over the
+ ** the PRNG seed.
+ **
+ ** x!=0 && db==0 Seed the PRNG to the value of x.
+ **
+ ** x==0 && db==0 Revert to default behavior of using the
+ ** xRandomness method on the primary VFS.
+ **
+ ** This test-control also resets the PRNG so that the new seed will
+ ** be used for the next call to sqlite3_randomness().
*/
- case SQLITE_TESTCTRL_PRNG_RESET: {
+#ifndef SQLITE_OMIT_WSD
+ case SQLITE_TESTCTRL_PRNG_SEED: {
+ int x = va_arg(ap, int);
+ int y;
+ sqlite3 *db = va_arg(ap, sqlite3*);
+ assert( db==0 || db->aDb[0].pSchema!=0 );
+ if( db && (y = db->aDb[0].pSchema->schema_cookie)!=0 ){ x = y; }
+ sqlite3Config.iPrngSeed = x;
sqlite3_randomness(0,0);
break;
}
+#endif
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b);
+ **
+ ** If b is true, then activate the SQLITE_FkNoAction setting. If b is
+ ** false then clearn that setting. If the SQLITE_FkNoAction setting is
+ ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if
+ ** they were NO ACTION, regardless of how they are defined.
+ **
+ ** NB: One must usually run "PRAGMA writable_schema=RESET" after
+ ** using this test-control, before it will take full effect. failing
+ ** to reset the schema can result in some unexpected behavior.
+ */
+ case SQLITE_TESTCTRL_FK_NO_ACTION: {
+ sqlite3 *db = va_arg(ap, sqlite3*);
+ int b = va_arg(ap, int);
+ if( b ){
+ db->flags |= SQLITE_FkNoAction;
+ }else{
+ db->flags &= ~SQLITE_FkNoAction;
+ }
+ break;
+ }
/*
** sqlite3_test_control(BITVEC_TEST, size, program)
@@ -156529,12 +182321,16 @@ SQLITE_API int sqlite3_test_control(int op, ...){
** sqlite3_test_control().
*/
case SQLITE_TESTCTRL_FAULT_INSTALL: {
- /* MSVC is picky about pulling func ptrs from va lists.
- ** http://support.microsoft.com/kb/47961
+ /* A bug in MSVC prevents it from understanding pointers to functions
+ ** types in the second argument to va_arg(). Work around the problem
+ ** using a typedef.
+ ** http://support.microsoft.com/kb/47961 <-- dead hyperlink
+ ** Search at http://web.archive.org/ to find the 2015-03-16 archive
+ ** of the link above to see the original text.
** sqlite3GlobalConfig.xTestCallback = va_arg(ap, int(*)(int));
*/
- typedef int(*TESTCALLBACKFUNC_t)(int);
- sqlite3GlobalConfig.xTestCallback = va_arg(ap, TESTCALLBACKFUNC_t);
+ typedef int(*sqlite3FaultFuncType)(int);
+ sqlite3GlobalConfig.xTestCallback = va_arg(ap, sqlite3FaultFuncType);
rc = sqlite3FaultSim(0);
break;
}
@@ -156542,7 +182338,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){
/*
** sqlite3_test_control(BENIGN_MALLOC_HOOKS, xBegin, xEnd)
**
- ** Register hooks to call to indicate which malloc() failures
+ ** Register hooks to call to indicate which malloc() failures
** are benign.
*/
case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: {
@@ -156593,6 +182389,30 @@ SQLITE_API int sqlite3_test_control(int op, ...){
volatile int x = 0;
assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 );
rc = x;
+#if defined(SQLITE_DEBUG)
+ /* Invoke these debugging routines so that the compiler does not
+ ** issue "defined but not used" warnings. */
+ if( x==9999 ){
+ sqlite3ShowExpr(0);
+ sqlite3ShowExpr(0);
+ sqlite3ShowExprList(0);
+ sqlite3ShowIdList(0);
+ sqlite3ShowSrcList(0);
+ sqlite3ShowWith(0);
+ sqlite3ShowUpsert(0);
+#ifndef SQLITE_OMIT_TRIGGER
+ sqlite3ShowTriggerStep(0);
+ sqlite3ShowTriggerStepList(0);
+ sqlite3ShowTrigger(0);
+ sqlite3ShowTriggerList(0);
+#endif
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ sqlite3ShowWindow(0);
+ sqlite3ShowWinFunc(0);
+#endif
+ sqlite3ShowSelect(0);
+ }
+#endif
break;
}
@@ -156640,29 +182460,15 @@ SQLITE_API int sqlite3_test_control(int op, ...){
** 10 little-endian, determined at run-time
** 432101 big-endian, determined at compile-time
** 123410 little-endian, determined at compile-time
- */
+ */
case SQLITE_TESTCTRL_BYTEORDER: {
rc = SQLITE_BYTEORDER*100 + SQLITE_LITTLEENDIAN*10 + SQLITE_BIGENDIAN;
break;
}
- /* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N)
- **
- ** Set the nReserve size to N for the main database on the database
- ** connection db.
- */
- case SQLITE_TESTCTRL_RESERVE: {
- sqlite3 *db = va_arg(ap, sqlite3*);
- int x = va_arg(ap,int);
- sqlite3_mutex_enter(db->mutex);
- sqlite3BtreeSetPageSize(db->aDb[0].pBt, 0, x, 0);
- sqlite3_mutex_leave(db->mutex);
- break;
- }
-
/* sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N)
**
- ** Enable or disable various optimizations for testing purposes. The
+ ** Enable or disable various optimizations for testing purposes. The
** argument N is a bitmask of optimizations to be disabled. For normal
** operation N should be 0. The idea is that a test program (like the
** SQL Logic Test or SLT test module) can run the same SQL multiple times
@@ -156671,29 +182477,42 @@ SQLITE_API int sqlite3_test_control(int op, ...){
*/
case SQLITE_TESTCTRL_OPTIMIZATIONS: {
sqlite3 *db = va_arg(ap, sqlite3*);
- db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff);
+ db->dbOptFlags = va_arg(ap, u32);
break;
}
- /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff);
+ /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, onoff, xAlt);
+ **
+ ** If parameter onoff is 1, subsequent calls to localtime() fail.
+ ** If 2, then invoke xAlt() instead of localtime(). If 0, normal
+ ** processing.
**
- ** If parameter onoff is non-zero, subsequent calls to localtime()
- ** and its variants fail. If onoff is zero, undo this setting.
+ ** xAlt arguments are void pointers, but they really want to be:
+ **
+ ** int xAlt(const time_t*, struct tm*);
+ **
+ ** xAlt should write results in to struct tm object of its 2nd argument
+ ** and return zero on success, or return non-zero on failure.
*/
case SQLITE_TESTCTRL_LOCALTIME_FAULT: {
sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int);
+ if( sqlite3GlobalConfig.bLocaltimeFault==2 ){
+ typedef int(*sqlite3LocaltimeType)(const void*,void*);
+ sqlite3GlobalConfig.xAltLocaltime = va_arg(ap, sqlite3LocaltimeType);
+ }else{
+ sqlite3GlobalConfig.xAltLocaltime = 0;
+ }
break;
}
- /* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCS, int onoff);
+ /* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, sqlite3*);
**
- ** If parameter onoff is non-zero, internal-use-only SQL functions
- ** are visible to ordinary SQL. This is useful for testing but is
- ** unsafe because invalid parameters to those internal-use-only functions
- ** can result in crashes or segfaults.
+ ** Toggle the ability to use internal functions on or off for
+ ** the database connection given in the argument.
*/
case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: {
- sqlite3GlobalConfig.bInternalFunctions = va_arg(ap, int);
+ sqlite3 *db = va_arg(ap, sqlite3*);
+ db->mDbFlags ^= DBFLAG_InternalFunc;
break;
}
@@ -156703,13 +182522,30 @@ SQLITE_API int sqlite3_test_control(int op, ...){
** formed and never corrupt. This flag is clear by default, indicating that
** database files might have arbitrary corruption. Setting the flag during
** testing causes certain assert() statements in the code to be activated
- ** that demonstrat invariants on well-formed database files.
+ ** that demonstrate invariants on well-formed database files.
*/
case SQLITE_TESTCTRL_NEVER_CORRUPT: {
sqlite3GlobalConfig.neverCorrupt = va_arg(ap, int);
break;
}
+ /* sqlite3_test_control(SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS, int);
+ **
+ ** Set or clear a flag that causes SQLite to verify that type, name,
+ ** and tbl_name fields of the sqlite_schema table. This is normally
+ ** on, but it is sometimes useful to turn it off for testing.
+ **
+ ** 2020-07-22: Disabling EXTRA_SCHEMA_CHECKS also disables the
+ ** verification of rootpage numbers when parsing the schema. This
+ ** is useful to make it easier to reach strange internal error states
+ ** during testing. The EXTRA_SCHEMA_CHECKS setting is always enabled
+ ** in production.
+ */
+ case SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS: {
+ sqlite3GlobalConfig.bExtraSchemaChecks = va_arg(ap, int);
+ break;
+ }
+
/* Set the threshold at which OP_Once counters reset back to zero.
** By default this is 0x7ffffffe (over 2 billion), but that value is
** too big to test in a reasonable amount of time, so this control is
@@ -156722,7 +182558,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){
/* sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE, xCallback, ptr);
**
- ** Set the VDBE coverage callback function to xCallback with context
+ ** Set the VDBE coverage callback function to xCallback with context
** pointer ptr.
*/
case SQLITE_TESTCTRL_VDBE_COVERAGE: {
@@ -156770,12 +182606,16 @@ SQLITE_API int sqlite3_test_control(int op, ...){
*/
case SQLITE_TESTCTRL_IMPOSTER: {
sqlite3 *db = va_arg(ap, sqlite3*);
+ int iDb;
sqlite3_mutex_enter(db->mutex);
- db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*));
- db->init.busy = db->init.imposterTable = va_arg(ap,int);
- db->init.newTnum = va_arg(ap,int);
- if( db->init.busy==0 && db->init.newTnum>0 ){
- sqlite3ResetAllSchemasOfConnection(db);
+ iDb = sqlite3FindDbName(db, va_arg(ap,const char*));
+ if( iDb>=0 ){
+ db->init.iDb = iDb;
+ db->init.busy = db->init.imposterTable = va_arg(ap,int);
+ db->init.newTnum = va_arg(ap,int);
+ if( db->init.busy==0 && db->init.newTnum>0 ){
+ sqlite3ResetAllSchemasOfConnection(db);
+ }
}
sqlite3_mutex_leave(db->mutex);
break;
@@ -156796,6 +182636,151 @@ SQLITE_API int sqlite3_test_control(int op, ...){
break;
}
#endif /* defined(YYCOVERAGE) */
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_RESULT_INTREAL, sqlite3_context*);
+ **
+ ** This test-control causes the most recent sqlite3_result_int64() value
+ ** to be interpreted as a MEM_IntReal instead of as an MEM_Int. Normally,
+ ** MEM_IntReal values only arise during an INSERT operation of integer
+ ** values into a REAL column, so they can be challenging to test. This
+ ** test-control enables us to write an intreal() SQL function that can
+ ** inject an intreal() value at arbitrary places in an SQL statement,
+ ** for testing purposes.
+ */
+ case SQLITE_TESTCTRL_RESULT_INTREAL: {
+ sqlite3_context *pCtx = va_arg(ap, sqlite3_context*);
+ sqlite3ResultIntReal(pCtx);
+ break;
+ }
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_SEEK_COUNT,
+ ** sqlite3 *db, // Database connection
+ ** u64 *pnSeek // Write seek count here
+ ** );
+ **
+ ** This test-control queries the seek-counter on the "main" database
+ ** file. The seek-counter is written into *pnSeek and is then reset.
+ ** The seek-count is only available if compiled with SQLITE_DEBUG.
+ */
+ case SQLITE_TESTCTRL_SEEK_COUNT: {
+ sqlite3 *db = va_arg(ap, sqlite3*);
+ u64 *pn = va_arg(ap, sqlite3_uint64*);
+ *pn = sqlite3BtreeSeekCount(db->aDb->pBt);
+ (void)db; /* Silence harmless unused variable warning */
+ break;
+ }
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, op, ptr)
+ **
+ ** "ptr" is a pointer to a u32.
+ **
+ ** op==0 Store the current sqlite3TreeTrace in *ptr
+ ** op==1 Set sqlite3TreeTrace to the value *ptr
+ ** op==2 Store the current sqlite3WhereTrace in *ptr
+ ** op==3 Set sqlite3WhereTrace to the value *ptr
+ */
+ case SQLITE_TESTCTRL_TRACEFLAGS: {
+ int opTrace = va_arg(ap, int);
+ u32 *ptr = va_arg(ap, u32*);
+ switch( opTrace ){
+ case 0: *ptr = sqlite3TreeTrace; break;
+ case 1: sqlite3TreeTrace = *ptr; break;
+ case 2: *ptr = sqlite3WhereTrace; break;
+ case 3: sqlite3WhereTrace = *ptr; break;
+ }
+ break;
+ }
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_LOGEST,
+ ** double fIn, // Input value
+ ** int *pLogEst, // sqlite3LogEstFromDouble(fIn)
+ ** u64 *pInt, // sqlite3LogEstToInt(*pLogEst)
+ ** int *pLogEst2 // sqlite3LogEst(*pInt)
+ ** );
+ **
+ ** Test access for the LogEst conversion routines.
+ */
+ case SQLITE_TESTCTRL_LOGEST: {
+ double rIn = va_arg(ap, double);
+ LogEst rLogEst = sqlite3LogEstFromDouble(rIn);
+ int *pI1 = va_arg(ap,int*);
+ u64 *pU64 = va_arg(ap,u64*);
+ int *pI2 = va_arg(ap,int*);
+ *pI1 = rLogEst;
+ *pU64 = sqlite3LogEstToInt(rLogEst);
+ *pI2 = sqlite3LogEst(*pU64);
+ break;
+ }
+
+#if !defined(SQLITE_OMIT_WSD)
+ /* sqlite3_test_control(SQLITE_TESTCTRL_USELONGDOUBLE, int X);
+ **
+ ** X<0 Make no changes to the bUseLongDouble. Just report value.
+ ** X==0 Disable bUseLongDouble
+ ** X==1 Enable bUseLongDouble
+ ** X>=2 Set bUseLongDouble to its default value for this platform
+ */
+ case SQLITE_TESTCTRL_USELONGDOUBLE: {
+ int b = va_arg(ap, int);
+ if( b>=2 ) b = hasHighPrecisionDouble(b);
+ if( b>=0 ) sqlite3Config.bUseLongDouble = b>0;
+ rc = sqlite3Config.bUseLongDouble!=0;
+ break;
+ }
+#endif
+
+
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD)
+ /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue)
+ **
+ ** If "id" is an integer between 1 and SQLITE_NTUNE then set the value
+ ** of the id-th tuning parameter to *piValue. If "id" is between -1
+ ** and -SQLITE_NTUNE, then write the current value of the (-id)-th
+ ** tuning parameter into *piValue.
+ **
+ ** Tuning parameters are for use during transient development builds,
+ ** to help find the best values for constants in the query planner.
+ ** Access tuning parameters using the Tuning(ID) macro. Set the
+ ** parameters in the CLI using ".testctrl tune ID VALUE".
+ **
+ ** Transient use only. Tuning parameters should not be used in
+ ** checked-in code.
+ */
+ case SQLITE_TESTCTRL_TUNE: {
+ int id = va_arg(ap, int);
+ int *piValue = va_arg(ap, int*);
+ if( id>0 && id<=SQLITE_NTUNE ){
+ Tuning(id) = *piValue;
+ }else if( id<0 && id>=-SQLITE_NTUNE ){
+ *piValue = Tuning(-id);
+ }else{
+ rc = SQLITE_NOTFOUND;
+ }
+ break;
+ }
+#endif
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_JSON_SELFCHECK, &onOff);
+ **
+ ** Activate or deactivate validation of JSONB that is generated from
+ ** text. Off by default, as the validation is slow. Validation is
+ ** only available if compiled using SQLITE_DEBUG.
+ **
+ ** If onOff is initially 1, then turn it on. If onOff is initially
+ ** off, turn it off. If onOff is initially -1, then change onOff
+ ** to be the current setting.
+ */
+ case SQLITE_TESTCTRL_JSON_SELFCHECK: {
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD)
+ int *pOnOff = va_arg(ap, int*);
+ if( *pOnOff<0 ){
+ *pOnOff = sqlite3Config.bJsonSelfcheck;
+ }else{
+ sqlite3Config.bJsonSelfcheck = (u8)((*pOnOff)&0xff);
+ }
+#endif
+ break;
+ }
}
va_end(ap);
#endif /* SQLITE_UNTESTABLE */
@@ -156803,8 +182788,85 @@ SQLITE_API int sqlite3_test_control(int op, ...){
}
/*
+** The Pager stores the Database filename, Journal filename, and WAL filename
+** consecutively in memory, in that order. The database filename is prefixed
+** by four zero bytes. Locate the start of the database filename by searching
+** backwards for the first byte following four consecutive zero bytes.
+**
+** This only works if the filename passed in was obtained from the Pager.
+*/
+static const char *databaseName(const char *zName){
+ while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){
+ zName--;
+ }
+ return zName;
+}
+
+/*
+** Append text z[] to the end of p[]. Return a pointer to the first
+** character after then zero terminator on the new text in p[].
+*/
+static char *appendText(char *p, const char *z){
+ size_t n = strlen(z);
+ memcpy(p, z, n+1);
+ return p+n+1;
+}
+
+/*
+** Allocate memory to hold names for a database, journal file, WAL file,
+** and query parameters. The pointer returned is valid for use by
+** sqlite3_filename_database() and sqlite3_uri_parameter() and related
+** functions.
+**
+** Memory layout must be compatible with that generated by the pager
+** and expected by sqlite3_uri_parameter() and databaseName().
+*/
+SQLITE_API const char *sqlite3_create_filename(
+ const char *zDatabase,
+ const char *zJournal,
+ const char *zWal,
+ int nParam,
+ const char **azParam
+){
+ sqlite3_int64 nByte;
+ int i;
+ char *pResult, *p;
+ nByte = strlen(zDatabase) + strlen(zJournal) + strlen(zWal) + 10;
+ for(i=0; i<nParam*2; i++){
+ nByte += strlen(azParam[i])+1;
+ }
+ pResult = p = sqlite3_malloc64( nByte );
+ if( p==0 ) return 0;
+ memset(p, 0, 4);
+ p += 4;
+ p = appendText(p, zDatabase);
+ for(i=0; i<nParam*2; i++){
+ p = appendText(p, azParam[i]);
+ }
+ *(p++) = 0;
+ p = appendText(p, zJournal);
+ p = appendText(p, zWal);
+ *(p++) = 0;
+ *(p++) = 0;
+ assert( (sqlite3_int64)(p - pResult)==nByte );
+ return pResult + 4;
+}
+
+/*
+** Free memory obtained from sqlite3_create_filename(). It is a severe
+** error to call this routine with any parameter other than a pointer
+** previously obtained from sqlite3_create_filename() or a NULL pointer.
+*/
+SQLITE_API void sqlite3_free_filename(const char *p){
+ if( p==0 ) return;
+ p = databaseName(p);
+ sqlite3_free((char*)p - 4);
+}
+
+
+/*
** This is a utility routine, useful to VFS implementations, that checks
-** to see if a database file was a URI that contained a specific query
+** to see if a database file was a URI that contained a specific query
** parameter, and if so obtains the value of the query parameter.
**
** The zFilename argument is the filename pointer passed into the xOpen()
@@ -156815,14 +182877,22 @@ SQLITE_API int sqlite3_test_control(int op, ...){
*/
SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
if( zFilename==0 || zParam==0 ) return 0;
+ zFilename = databaseName(zFilename);
+ return uriParameter(zFilename, zParam);
+}
+
+/*
+** Return a pointer to the name of Nth query parameter of the filename.
+*/
+SQLITE_API const char *sqlite3_uri_key(const char *zFilename, int N){
+ if( zFilename==0 || N<0 ) return 0;
+ zFilename = databaseName(zFilename);
zFilename += sqlite3Strlen30(zFilename) + 1;
- while( zFilename[0] ){
- int x = strcmp(zFilename, zParam);
+ while( ALWAYS(zFilename) && zFilename[0] && (N--)>0 ){
zFilename += sqlite3Strlen30(zFilename) + 1;
- if( x==0 ) return zFilename;
zFilename += sqlite3Strlen30(zFilename) + 1;
}
- return 0;
+ return zFilename[0] ? zFilename : 0;
}
/*
@@ -156851,6 +182921,40 @@ SQLITE_API sqlite3_int64 sqlite3_uri_int64(
}
/*
+** Translate a filename that was handed to a VFS routine into the corresponding
+** database, journal, or WAL file.
+**
+** It is an error to pass this routine a filename string that was not
+** passed into the VFS from the SQLite core. Doing so is similar to
+** passing free() a pointer that was not obtained from malloc() - it is
+** an error that we cannot easily detect but that will likely cause memory
+** corruption.
+*/
+SQLITE_API const char *sqlite3_filename_database(const char *zFilename){
+ if( zFilename==0 ) return 0;
+ return databaseName(zFilename);
+}
+SQLITE_API const char *sqlite3_filename_journal(const char *zFilename){
+ if( zFilename==0 ) return 0;
+ zFilename = databaseName(zFilename);
+ zFilename += sqlite3Strlen30(zFilename) + 1;
+ while( ALWAYS(zFilename) && zFilename[0] ){
+ zFilename += sqlite3Strlen30(zFilename) + 1;
+ zFilename += sqlite3Strlen30(zFilename) + 1;
+ }
+ return zFilename + 1;
+}
+SQLITE_API const char *sqlite3_filename_wal(const char *zFilename){
+#ifdef SQLITE_OMIT_WAL
+ return 0;
+#else
+ zFilename = sqlite3_filename_journal(zFilename);
+ if( zFilename ) zFilename += sqlite3Strlen30(zFilename) + 1;
+ return zFilename;
+#endif
+}
+
+/*
** Return the Btree pointer identified by zDbName. Return NULL if not found.
*/
SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
@@ -156859,6 +182963,24 @@ SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
}
/*
+** Return the name of the N-th database schema. Return NULL if N is out
+** of range.
+*/
+SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+ if( N<0 || N>=db->nDb ){
+ return 0;
+ }else{
+ return db->aDb[N].zDbSName;
+ }
+}
+
+/*
** Return the filename of the database associated with a database
** connection.
*/
@@ -156892,11 +183014,11 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){
#ifdef SQLITE_ENABLE_SNAPSHOT
/*
-** Obtain a snapshot handle for the snapshot of database zDb currently
+** Obtain a snapshot handle for the snapshot of database zDb currently
** being read by handle db.
*/
SQLITE_API int sqlite3_snapshot_get(
- sqlite3 *db,
+ sqlite3 *db,
const char *zDb,
sqlite3_snapshot **ppSnapshot
){
@@ -156914,7 +183036,7 @@ SQLITE_API int sqlite3_snapshot_get(
int iDb = sqlite3FindDbName(db, zDb);
if( iDb==0 || iDb>1 ){
Btree *pBt = db->aDb[iDb].pBt;
- if( 0==sqlite3BtreeIsInTrans(pBt) ){
+ if( SQLITE_TXN_WRITE!=sqlite3BtreeTxnState(pBt) ){
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
@@ -156929,11 +183051,11 @@ SQLITE_API int sqlite3_snapshot_get(
}
/*
-** Open a read-transaction on the snapshot idendified by pSnapshot.
+** Open a read-transaction on the snapshot identified by pSnapshot.
*/
SQLITE_API int sqlite3_snapshot_open(
- sqlite3 *db,
- const char *zDb,
+ sqlite3 *db,
+ const char *zDb,
sqlite3_snapshot *pSnapshot
){
int rc = SQLITE_ERROR;
@@ -156950,10 +183072,10 @@ SQLITE_API int sqlite3_snapshot_open(
iDb = sqlite3FindDbName(db, zDb);
if( iDb==0 || iDb>1 ){
Btree *pBt = db->aDb[iDb].pBt;
- if( sqlite3BtreeIsInTrans(pBt)==0 ){
+ if( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE ){
Pager *pPager = sqlite3BtreePager(pBt);
int bUnlock = 0;
- if( sqlite3BtreeIsInReadTrans(pBt) ){
+ if( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_NONE ){
if( db->nVdbeActive==0 ){
rc = sqlite3PagerSnapshotCheck(pPager, pSnapshot);
if( rc==SQLITE_OK ){
@@ -156989,8 +183111,8 @@ SQLITE_API int sqlite3_snapshot_open(
*/
SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){
int rc = SQLITE_ERROR;
- int iDb;
#ifndef SQLITE_OMIT_WAL
+ int iDb;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) ){
@@ -157002,7 +183124,7 @@ SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){
iDb = sqlite3FindDbName(db, zDb);
if( iDb==0 || iDb>1 ){
Btree *pBt = db->aDb[iDb].pBt;
- if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
+ if( SQLITE_TXN_NONE==sqlite3BtreeTxnState(pBt) ){
rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerSnapshotRecover(sqlite3BtreePager(pBt));
@@ -157035,8 +183157,8 @@ SQLITE_API int sqlite3_compileoption_used(const char *zOptName){
int i, n;
int nOpt;
const char **azCompileOpt;
-
-#if SQLITE_ENABLE_API_ARMOR
+
+#ifdef SQLITE_ENABLE_API_ARMOR
if( zOptName==0 ){
(void)SQLITE_MISUSE_BKPT;
return 0;
@@ -157048,7 +183170,7 @@ SQLITE_API int sqlite3_compileoption_used(const char *zOptName){
if( sqlite3StrNICmp(zOptName, "SQLITE_", 7)==0 ) zOptName += 7;
n = sqlite3Strlen30(zOptName);
- /* Since nOpt is normally in single digits, a linear search is
+ /* Since nOpt is normally in single digits, a linear search is
** adequate. No need for a binary search. */
for(i=0; i<nOpt; i++){
if( sqlite3StrNICmp(zOptName, azCompileOpt[i], n)==0
@@ -157108,25 +183230,25 @@ SQLITE_API const char *sqlite3_compileoption_get(int N){
*/
#define assertMutexHeld() \
- assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) )
+ assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)) )
/*
** Head of a linked list of all sqlite3 objects created by this process
** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection
-** is not NULL. This variable may only accessed while the STATIC_MASTER
+** is not NULL. This variable may only accessed while the STATIC_MAIN
** mutex is held.
*/
static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0;
#ifndef NDEBUG
/*
-** This function is a complex assert() that verifies the following
+** This function is a complex assert() that verifies the following
** properties of the blocked connections list:
**
-** 1) Each entry in the list has a non-NULL value for either
+** 1) Each entry in the list has a non-NULL value for either
** pUnlockConnection or pBlockingConnection, or both.
**
-** 2) All entries in the list that share a common value for
+** 2) All entries in the list that share a common value for
** xUnlockNotify are grouped together.
**
** 3) If the argument db is not NULL, then none of the entries in the
@@ -157178,8 +183300,8 @@ static void addToBlockedList(sqlite3 *db){
sqlite3 **pp;
assertMutexHeld();
for(
- pp=&sqlite3BlockedList;
- *pp && (*pp)->xUnlockNotify!=db->xUnlockNotify;
+ pp=&sqlite3BlockedList;
+ *pp && (*pp)->xUnlockNotify!=db->xUnlockNotify;
pp=&(*pp)->pNextBlocked
);
db->pNextBlocked = *pp;
@@ -157187,20 +183309,20 @@ static void addToBlockedList(sqlite3 *db){
}
/*
-** Obtain the STATIC_MASTER mutex.
+** Obtain the STATIC_MAIN mutex.
*/
static void enterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
checkListProperties(0);
}
/*
-** Release the STATIC_MASTER mutex.
+** Release the STATIC_MAIN mutex.
*/
static void leaveMutex(void){
assertMutexHeld();
checkListProperties(0);
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN));
}
/*
@@ -157231,6 +183353,9 @@ SQLITE_API int sqlite3_unlock_notify(
){
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
enterMutex();
@@ -157241,9 +183366,9 @@ SQLITE_API int sqlite3_unlock_notify(
db->xUnlockNotify = 0;
db->pUnlockArg = 0;
}else if( 0==db->pBlockingConnection ){
- /* The blocking transaction has been concluded. Or there never was a
+ /* The blocking transaction has been concluded. Or there never was a
** blocking transaction. In either case, invoke the notify callback
- ** immediately.
+ ** immediately.
*/
xNotify(&pArg, 1);
}else{
@@ -157269,7 +183394,7 @@ SQLITE_API int sqlite3_unlock_notify(
}
/*
-** This function is called while stepping or preparing a statement
+** This function is called while stepping or preparing a statement
** associated with connection db. The operation will return SQLITE_LOCKED
** to the user because it requires a lock that will not be available
** until connection pBlocker concludes its current transaction.
@@ -157285,7 +183410,7 @@ SQLITE_PRIVATE void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){
/*
** This function is called when
-** the transaction opened by database db has just finished. Locks held
+** the transaction opened by database db has just finished. Locks held
** by database connection db have been released.
**
** This function loops through each entry in the blocked connections
@@ -157311,7 +183436,7 @@ SQLITE_PRIVATE void sqlite3ConnectionUnlocked(sqlite3 *db){
void *aStatic[16]; /* Starter space for aArg[]. No malloc required */
aArg = aStatic;
- enterMutex(); /* Enter STATIC_MASTER mutex */
+ enterMutex(); /* Enter STATIC_MAIN mutex */
/* This loop runs once for each entry in the blocked-connections list. */
for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
@@ -157345,7 +183470,7 @@ SQLITE_PRIVATE void sqlite3ConnectionUnlocked(sqlite3 *db){
}else{
/* This occurs when the array of context pointers that need to
** be passed to the unlock-notify callback is larger than the
- ** aStatic[] array allocated on the stack and the attempt to
+ ** aStatic[] array allocated on the stack and the attempt to
** allocate a larger array from the heap has failed.
**
** This is a difficult situation to handle. Returning an error
@@ -157353,17 +183478,17 @@ SQLITE_PRIVATE void sqlite3ConnectionUnlocked(sqlite3 *db){
** is returned the transaction on connection db will still be
** closed and the unlock-notify callbacks on blocked connections
** will go unissued. This might cause the application to wait
- ** indefinitely for an unlock-notify callback that will never
+ ** indefinitely for an unlock-notify callback that will never
** arrive.
**
** Instead, invoke the unlock-notify callback with the context
** array already accumulated. We can then clear the array and
- ** begin accumulating any further context pointers without
+ ** begin accumulating any further context pointers without
** requiring any dynamic allocation. This is sub-optimal because
** it means that instead of one callback with a large array of
** context pointers the application will receive two or more
** callbacks with smaller arrays of context pointers, which will
- ** reduce the applications ability to prioritize multiple
+ ** reduce the applications ability to prioritize multiple
** connections. But it is the best that can be done under the
** circumstances.
*/
@@ -157394,11 +183519,11 @@ SQLITE_PRIVATE void sqlite3ConnectionUnlocked(sqlite3 *db){
xUnlockNotify(aArg, nArg);
}
sqlite3_free(aDyn);
- leaveMutex(); /* Leave STATIC_MASTER mutex */
+ leaveMutex(); /* Leave STATIC_MAIN mutex */
}
/*
-** This is called when the database connection passed as an argument is
+** This is called when the database connection passed as an argument is
** being closed. The connection is removed from the blocked list.
*/
SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
@@ -157475,7 +183600,7 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
** A doclist (document list) holds a docid-sorted list of hits for a
** given term. Doclists hold docids and associated token positions.
** A docid is the unique integer identifier for a single document.
-** A position is the index of a word within the document. The first
+** A position is the index of a word within the document. The first
** word of the document has a position of 0.
**
** FTS3 used to optionally store character offsets using a compile-time
@@ -157500,7 +183625,7 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
**
** 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. Note that POS_END and POS_COLUMN occur
+** generated by the tokenizer. 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. POS_END is 0. POS_COLUMN is 1.
** The positions numbers are not stored literally but rather as two more
@@ -157524,7 +183649,7 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
** a document record consists of a docid followed by a position-list and
** a doclist consists of one or more document records.
**
-** A bare doclist omits the position information, becoming an
+** A bare doclist omits the position information, becoming an
** array of varint-encoded docids.
**
**** Segment leaf nodes ****
@@ -157720,7 +183845,7 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
#ifndef _FTSINT_H
#define _FTSINT_H
-#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
# define NDEBUG 1
#endif
@@ -157743,7 +183868,7 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
/* If not building as part of the core, include sqlite3ext.h. */
#ifndef SQLITE_CORE
-/* # include "sqlite3ext.h" */
+/* # include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT3
#endif
@@ -157787,7 +183912,7 @@ SQLITE_EXTENSION_INIT3
** When an fts3 table is created, it passes any arguments passed to
** the tokenizer clause of the CREATE VIRTUAL TABLE statement to the
** sqlite3_tokenizer_module.xCreate() function of the requested tokenizer
-** implementation. The xCreate() function in turn returns an
+** implementation. The xCreate() function in turn returns an
** sqlite3_tokenizer structure representing the specific tokenizer to
** be used for the fts3 table (customized by the tokenizer clause arguments).
**
@@ -157819,7 +183944,7 @@ struct sqlite3_tokenizer_module {
** then argc is set to 2, and the argv[] array contains pointers
** to the strings "arg1" and "arg2".
**
- ** This method should return either SQLITE_OK (0), or an SQLite error
+ ** This method should return either SQLITE_OK (0), or an SQLite error
** code. If SQLITE_OK is returned, then *ppTokenizer should be set
** to point at the newly created tokenizer structure. The generic
** sqlite3_tokenizer.pModule variable should not be initialized by
@@ -157840,7 +183965,7 @@ struct sqlite3_tokenizer_module {
/*
** Create a tokenizer cursor to tokenize an input buffer. The caller
** is responsible for ensuring that the input buffer remains valid
- ** until the cursor is closed (using the xClose() method).
+ ** until the cursor is closed (using the xClose() method).
*/
int (*xOpen)(
sqlite3_tokenizer *pTokenizer, /* Tokenizer object */
@@ -157849,7 +183974,7 @@ struct sqlite3_tokenizer_module {
);
/*
- ** Destroy an existing tokenizer cursor. The fts3 module calls this
+ ** Destroy an existing tokenizer cursor. The fts3 module calls this
** method exactly once for each successful call to xOpen().
*/
int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
@@ -157860,7 +183985,7 @@ struct sqlite3_tokenizer_module {
** "OUT" variables identified below, or SQLITE_DONE to indicate that
** the end of the buffer has been reached, or an SQLite error code.
**
- ** *ppToken should be set to point at a buffer containing the
+ ** *ppToken should be set to point at a buffer containing the
** normalized version of the token (i.e. after any case-folding and/or
** stemming has been performed). *pnBytes should be set to the length
** of this buffer in bytes. The input text that generated the token is
@@ -157872,7 +183997,7 @@ struct sqlite3_tokenizer_module {
**
** The buffer *ppToken is set to point at is managed by the tokenizer
** implementation. It is only required to be valid until the next call
- ** to xNext() or xClose().
+ ** to xNext() or xClose().
*/
/* TODO(shess) current implementation requires pInput to be
** nul-terminated. This should either be fixed, or pInput/nBytes
@@ -157890,7 +184015,7 @@ struct sqlite3_tokenizer_module {
** Methods below this point are only available if iVersion>=1.
*/
- /*
+ /*
** Configure the language id of a tokenizer cursor.
*/
int (*xLanguageid)(sqlite3_tokenizer_cursor *pCsr, int iLangid);
@@ -157959,7 +184084,7 @@ struct Fts3Hash {
} *ht;
};
-/* Each element in the hash table is an instance of the following
+/* 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
@@ -157978,10 +184103,10 @@ struct Fts3HashElem {
** (including the null-terminator, if any). Case
** is respected in comparisons.
**
-** FTS3_HASH_BINARY pKey points to binary data nKey bytes long.
+** FTS3_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 fts3HashInit is 1.
+** A copy of the key is made if the copyKey parameter to fts3HashInit is 1.
*/
#define FTS3_HASH_STRING 1
#define FTS3_HASH_BINARY 2
@@ -158034,7 +184159,7 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi
/*
** This constant determines the maximum depth of an FTS expression tree
-** that the library will create and use. FTS uses recursion to perform
+** that the library will create and use. FTS uses recursion to perform
** various operations on the query tree, so the disadvantage of a large
** limit is that it may allow very large queries to use large amounts
** of stack space (perhaps causing a stack overflow).
@@ -158052,11 +184177,11 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi
#define FTS3_MERGE_COUNT 16
/*
-** This is the maximum amount of data (in bytes) to store in the
+** This is the maximum amount of data (in bytes) to store in the
** Fts3Table.pendingTerms hash table. Normally, the hash table is
** populated as documents are inserted/updated/deleted in a transaction
** and used to create a new segment when the transaction is committed.
-** However if this limit is reached midway through a transaction, a new
+** However if this limit is reached midway through a transaction, a new
** segment is created and the hash table cleared immediately.
*/
#define FTS3_MAX_PENDING_DATA (1*1024*1024)
@@ -158082,10 +184207,12 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi
*/
#define FTS3_VARINT_MAX 10
+#define FTS3_BUFFER_PADDING 8
+
/*
** FTS4 virtual tables may maintain multiple indexes - one index of all terms
** in the document set and zero or more prefix indexes. All indexes are stored
-** as one or more b+-trees in the %_segments and %_segdir tables.
+** as one or more b+-trees in the %_segments and %_segdir tables.
**
** It is possible to determine which index a b+-tree belongs to based on the
** value stored in the "%_segdir.level" column. Given this value L, the index
@@ -158093,8 +184220,8 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi
** level values between 0 and 1023 (inclusive) belong to index 0, all levels
** between 1024 and 2047 to index 1, and so on.
**
-** It is considered impossible for an index to use more than 1024 levels. In
-** theory though this may happen, but only after at least
+** It is considered impossible for an index to use more than 1024 levels. In
+** theory though this may happen, but only after at least
** (FTS3_MERGE_COUNT^1024) separate flushes of the pending-terms tables.
*/
#define FTS3_SEGDIR_MAXLEVEL 1024
@@ -158112,11 +184239,23 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi
** Terminator values for position-lists and column-lists.
*/
#define POS_COLUMN (1) /* Column-list terminator */
-#define POS_END (0) /* Position-list terminator */
+#define POS_END (0) /* Position-list terminator */
+
+/*
+** The assert_fts3_nc() macro is similar to the assert() macro, except that it
+** is used for assert() conditions that are true only if it can be
+** guranteed that the database is not corrupt.
+*/
+#ifdef SQLITE_DEBUG
+SQLITE_API extern int sqlite3_fts3_may_be_corrupt;
+# define assert_fts3_nc(x) assert(sqlite3_fts3_may_be_corrupt || (x))
+#else
+# define assert_fts3_nc(x) assert(x)
+#endif
/*
** This section provides definitions to allow the
-** FTS3 extension to be compiled outside of the
+** FTS3 extension to be compiled outside of the
** amalgamation.
*/
#ifndef SQLITE_AMALGAMATION
@@ -158124,17 +184263,18 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi
** Macros indicating that conditional expressions are always true or
** false.
*/
-#ifdef SQLITE_COVERAGE_TEST
-# define ALWAYS(x) (1)
-# define NEVER(X) (0)
-#elif defined(SQLITE_DEBUG)
-# define ALWAYS(x) sqlite3Fts3Always((x)!=0)
-# define NEVER(x) sqlite3Fts3Never((x)!=0)
-SQLITE_PRIVATE int sqlite3Fts3Always(int b);
-SQLITE_PRIVATE int sqlite3Fts3Never(int b);
+#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)
+# define ALWAYS(X) (X)
+# define NEVER(X) (X)
#endif
/*
@@ -158154,7 +184294,7 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
/*
** Activate assert() only if SQLITE_TEST is enabled.
*/
-#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
# define NDEBUG 1
#endif
@@ -158169,6 +184309,11 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
# define TESTONLY(X)
#endif
+#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
+#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
+
+#define deliberate_fall_through
+
#endif /* SQLITE_AMALGAMATION */
#ifdef SQLITE_DEBUG
@@ -158212,9 +184357,10 @@ struct Fts3Table {
char *zLanguageid; /* languageid=xxx option, or NULL */
int nAutoincrmerge; /* Value configured by 'automerge' */
u32 nLeafAdd; /* Number of leaf blocks added this trans */
+ int bLock; /* Used to prevent recursive content= tbls */
- /* Precompiled statements used by the implementation. Each of these
- ** statements is run and reset within a single virtual table API call.
+ /* Precompiled statements used by the implementation. Each of these
+ ** statements is run and reset within a single virtual table API call.
*/
sqlite3_stmt *aStmt[40];
sqlite3_stmt *pSeekStmt; /* Cache for fts3CursorSeekStmt() */
@@ -158231,9 +184377,10 @@ struct Fts3Table {
int nPgsz; /* Page size for host database */
char *zSegmentsTbl; /* Name of %_segments table */
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
+ int iSavepoint;
- /*
- ** The following array of hash tables is used to buffer pending index
+ /*
+ ** The following array of hash tables is used to buffer pending index
** updates during transactions. All pending updates buffered at any one
** time must share a common language-id (see the FTS4 langid= feature).
** The current language id is stored in variable iPrevLangid.
@@ -158243,10 +184390,10 @@ struct Fts3Table {
** terms that appear in the document set. Each subsequent index in aIndex[]
** is an index of prefixes of a specific length.
**
- ** Variable nPendingData contains an estimate the memory consumed by the
+ ** Variable nPendingData contains an estimate the memory consumed by the
** pending data structures, including hash table overhead, but not including
** malloc overhead. When nPendingData exceeds nMaxPendingData, all hash
- ** tables are flushed to disk. Variable iPrevDocid is the docid of the most
+ ** tables are flushed to disk. Variable iPrevDocid is the docid of the most
** recently inserted record.
*/
int nIndex; /* Size of aIndex[] */
@@ -158270,13 +184417,23 @@ struct Fts3Table {
int mxSavepoint; /* Largest valid xSavepoint integer */
#endif
-#ifdef SQLITE_TEST
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/* True to disable the incremental doclist optimization. This is controled
** by special insert command 'test-no-incr-doclist'. */
int bNoIncrDoclist;
+
+ /* Number of segments in a level */
+ int nMergeCount;
#endif
};
+/* Macro to find the number of segments to merge */
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+# define MergeCount(P) ((P)->nMergeCount)
+#else
+# define MergeCount(P) FTS3_MERGE_COUNT
+#endif
+
/*
** When the core wants to read from the virtual table, it creates a
** virtual table cursor (an instance of the following structure) using
@@ -158319,10 +184476,10 @@ struct Fts3Cursor {
**
** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d);
** SELECT docid FROM ex1 WHERE b MATCH 'one two three';
-**
+**
** Because the LHS of the MATCH operator is 2nd column "b",
** Fts3Cursor.eSearch will be set to FTS3_FULLTEXT_SEARCH+1. (+0 for a,
-** +1 for b, +2 for c, +3 for d.) If the LHS of MATCH were "ex1"
+** +1 for b, +2 for c, +3 for d.) If the LHS of MATCH were "ex1"
** indicating that all columns should be searched,
** then eSearch would be set to FTS3_FULLTEXT_SEARCH+4.
*/
@@ -158381,8 +184538,8 @@ struct Fts3Phrase {
char *pOrPoslist;
i64 iOrDocid;
- /* Variables below this point are populated by fts3_expr.c when parsing
- ** a MATCH expression. Everything above is part of the evaluation phase.
+ /* Variables below this point are populated by fts3_expr.c when parsing
+ ** a MATCH expression. Everything above is part of the evaluation phase.
*/
int nToken; /* Number of tokens in the phrase */
int iColumn; /* Index of column this phrase must match */
@@ -158392,10 +184549,10 @@ struct Fts3Phrase {
/*
** A tree of these objects forms the RHS of a MATCH operator.
**
-** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist
-** points to a malloced buffer, size nDoclist bytes, containing the results
-** of this phrase query in FTS3 doclist format. As usual, the initial
-** "Length" field found in doclists stored on disk is omitted from this
+** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist
+** points to a malloced buffer, size nDoclist bytes, containing the results
+** of this phrase query in FTS3 doclist format. As usual, the initial
+** "Length" field found in doclists stored on disk is omitted from this
** buffer.
**
** Variable aMI is used only for FTSQUERY_NEAR nodes to store the global
@@ -158407,7 +184564,7 @@ struct Fts3Phrase {
** aMI[iCol*3 + 1] = Number of occurrences
** aMI[iCol*3 + 2] = Number of rows containing at least one instance
**
-** The aMI array is allocated using sqlite3_malloc(). It should be freed
+** The aMI array is allocated using sqlite3_malloc(). It should be freed
** when the expression node is.
*/
struct Fts3Expr {
@@ -158431,7 +184588,7 @@ struct Fts3Expr {
/*
** Candidate values for Fts3Query.eType. Note that the order of the first
-** four values is in order of precedence when parsing expressions. For
+** four values is in order of precedence when parsing expressions. For
** example, the following:
**
** "a OR b AND c NOT d NEAR e"
@@ -158488,7 +184645,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3MultiSegReader*, Ft
SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3MultiSegReader *);
SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(Fts3MultiSegReader *);
-SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(Fts3Table *,
+SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(Fts3Table *,
int, int, int, const char *, int, int, int, Fts3MultiSegReader *);
/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
@@ -158514,7 +184671,7 @@ struct Fts3MultiSegReader {
int nAdvance; /* How many seg-readers to advance */
Fts3SegFilter *pFilter; /* Pointer to filter object */
char *aBuffer; /* Buffer to merge doclists in */
- int nBuffer; /* Allocated size of aBuffer[] in bytes */
+ i64 nBuffer; /* Allocated size of aBuffer[] in bytes */
int iColFilter; /* If >=0, filter for this column */
int bRestart;
@@ -158540,6 +184697,8 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char**,const char*,...);
SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64);
SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
+SQLITE_PRIVATE int sqlite3Fts3GetVarintU(const char *, sqlite_uint64 *);
+SQLITE_PRIVATE int sqlite3Fts3GetVarintBounded(const char*,const char*,sqlite3_int64*);
SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *);
SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64);
SQLITE_PRIVATE void sqlite3Fts3Dequote(char *);
@@ -158548,11 +184707,12 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc);
+SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut);
/* fts3_tokenizer.c */
SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *);
-SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *,
+SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *,
sqlite3_tokenizer **, char **
);
SQLITE_PRIVATE int sqlite3Fts3IsIdChar(char);
@@ -158574,6 +184734,7 @@ SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *);
SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db, Fts3Hash*);
SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db);
#endif
+SQLITE_PRIVATE void *sqlite3Fts3MallocZero(i64 nByte);
SQLITE_PRIVATE int sqlite3Fts3OpenTokenizer(sqlite3_tokenizer *, int, const char *, int,
sqlite3_tokenizer_cursor **
@@ -158588,12 +184749,12 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrStart(
Fts3Table*, Fts3MultiSegReader*, int, const char*, int);
SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
Fts3Table *, Fts3MultiSegReader *, sqlite3_int64 *, char **, int *);
-SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol, char **);
+SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol, char **);
SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *);
SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);
/* fts3_tokenize_vtab.c */
-SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *);
+SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *, void(*xDestroy)(void*));
/* fts3_unicode2.c (functions generated by parsing unicode text files) */
#ifndef SQLITE_DISABLE_FTS3_UNICODE
@@ -158602,6 +184763,10 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int);
SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
#endif
+SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*);
+
+SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk);
+
#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
#endif /* _FTSINT_H */
@@ -158621,24 +184786,33 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
/* #include <stdarg.h> */
/* #include "fts3.h" */
-#ifndef SQLITE_CORE
+#ifndef SQLITE_CORE
/* # include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#endif
+typedef struct Fts3HashWrapper Fts3HashWrapper;
+struct Fts3HashWrapper {
+ Fts3Hash hash; /* Hash table */
+ int nRef; /* Number of pointers to this object */
+};
+
static int fts3EvalNext(Fts3Cursor *pCsr);
static int fts3EvalStart(Fts3Cursor *pCsr);
static int fts3TermSegReaderCursor(
Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **);
-#ifndef SQLITE_AMALGAMATION
-# if defined(SQLITE_DEBUG)
-SQLITE_PRIVATE int sqlite3Fts3Always(int b) { assert( b ); return b; }
-SQLITE_PRIVATE int sqlite3Fts3Never(int b) { assert( !b ); return b; }
-# endif
+/*
+** This variable is set to false when running tests for which the on disk
+** structures should not be corrupt. Otherwise, true. If it is false, extra
+** assert() conditions in the fts3 code are activated - conditions that are
+** only true if it is guaranteed that the fts3 database is not corrupt.
+*/
+#ifdef SQLITE_DEBUG
+SQLITE_API int sqlite3_fts3_may_be_corrupt = 1;
#endif
-/*
+/*
** Write a 64-bit variable-length integer to memory starting at p[0].
** The length of data written will be between 1 and FTS3_VARINT_MAX bytes.
** The number of bytes written is returned.
@@ -158656,18 +184830,13 @@ SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *p, sqlite_int64 v){
}
#define GETVARINT_STEP(v, ptr, shift, mask1, mask2, var, ret) \
- v = (v & mask1) | ( (*ptr++) << shift ); \
+ v = (v & mask1) | ( (*(const unsigned char*)(ptr++)) << shift ); \
if( (v & mask2)==0 ){ var = v; return ret; }
#define GETVARINT_INIT(v, ptr, shift, mask1, mask2, var, ret) \
v = (*ptr++); \
if( (v & mask2)==0 ){ var = v; return ret; }
-/*
-** 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.
-*/
-SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){
+SQLITE_PRIVATE int sqlite3Fts3GetVarintU(const char *pBuf, sqlite_uint64 *v){
const unsigned char *p = (const unsigned char*)pBuf;
const unsigned char *pStart = p;
u32 a;
@@ -158690,24 +184859,60 @@ SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){
}
/*
-** Similar to sqlite3Fts3GetVarint(), except that the output is truncated to
+** 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.
+*/
+SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *pBuf, sqlite_int64 *v){
+ return sqlite3Fts3GetVarintU(pBuf, (sqlite3_uint64*)v);
+}
+
+/*
+** Read a 64-bit variable-length integer from memory starting at p[0] and
+** not extending past pEnd[-1].
+** Return the number of bytes read, or 0 on error.
+** The value is stored in *v.
+*/
+SQLITE_PRIVATE int sqlite3Fts3GetVarintBounded(
+ const char *pBuf,
+ const char *pEnd,
+ sqlite_int64 *v
+){
+ const unsigned char *p = (const unsigned char*)pBuf;
+ const unsigned char *pStart = p;
+ const unsigned char *pX = (const unsigned char*)pEnd;
+ u64 b = 0;
+ int shift;
+ for(shift=0; shift<=63; shift+=7){
+ u64 c = p<pX ? *p : 0;
+ p++;
+ b += (c&0x7F) << shift;
+ if( (c & 0x80)==0 ) break;
+ }
+ *v = b;
+ return (int)(p - pStart);
+}
+
+/*
+** Similar to sqlite3Fts3GetVarint(), except that the output is truncated to
** a non-negative 32-bit integer before it is returned.
*/
SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *p, int *pi){
+ const unsigned char *ptr = (const unsigned char*)p;
u32 a;
#ifndef fts3GetVarint32
- GETVARINT_INIT(a, p, 0, 0x00, 0x80, *pi, 1);
+ GETVARINT_INIT(a, ptr, 0, 0x00, 0x80, *pi, 1);
#else
- a = (*p++);
+ a = (*ptr++);
assert( a & 0x80 );
#endif
- GETVARINT_STEP(a, p, 7, 0x7F, 0x4000, *pi, 2);
- GETVARINT_STEP(a, p, 14, 0x3FFF, 0x200000, *pi, 3);
- GETVARINT_STEP(a, p, 21, 0x1FFFFF, 0x10000000, *pi, 4);
+ GETVARINT_STEP(a, ptr, 7, 0x7F, 0x4000, *pi, 2);
+ GETVARINT_STEP(a, ptr, 14, 0x3FFF, 0x200000, *pi, 3);
+ GETVARINT_STEP(a, ptr, 21, 0x1FFFFF, 0x10000000, *pi, 4);
a = (a & 0x0FFFFFFF );
- *pi = (int)(a | ((u32)(*p & 0x07) << 28));
+ *pi = (int)(a | ((u32)(*ptr & 0x07) << 28));
assert( 0==(a & 0x80000000) );
assert( *pi>=0 );
return 5;
@@ -158748,7 +184953,7 @@ SQLITE_PRIVATE void sqlite3Fts3Dequote(char *z){
int iOut = 0; /* Index of next byte to write to output */
/* If the first byte was a '[', then the close-quote character is a ']' */
- if( quote=='[' ) quote = ']';
+ if( quote=='[' ) quote = ']';
while( z[iIn] ){
if( z[iIn]==quote ){
@@ -158784,14 +184989,14 @@ static void fts3GetDeltaVarint(char **pp, sqlite3_int64 *pVal){
** varint is part of.
*/
static void fts3GetReverseVarint(
- char **pp,
- char *pStart,
+ char **pp,
+ char *pStart,
sqlite3_int64 *pVal
){
sqlite3_int64 iVal;
char *p;
- /* Pointer p now points at the first byte past the varint we are
+ /* Pointer p now points at the first byte past the varint we are
** interested in. So, unless the doclist is corrupt, the 0x80 bit is
** clear on character p[-1]. */
for(p = (*pp)-2; p>=pStart && *p&0x80; p--);
@@ -158878,13 +185083,18 @@ static int fts3DestroyMethod(sqlite3_vtab *pVtab){
sqlite3 *db = p->db; /* Database handle */
/* Drop the shadow tables */
- if( p->zContentTbl==0 ){
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", zDb, p->zName);
- }
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", zDb,p->zName);
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", zDb, p->zName);
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", zDb, p->zName);
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", zDb, p->zName);
+ fts3DbExec(&rc, db,
+ "DROP TABLE IF EXISTS %Q.'%q_segments';"
+ "DROP TABLE IF EXISTS %Q.'%q_segdir';"
+ "DROP TABLE IF EXISTS %Q.'%q_docsize';"
+ "DROP TABLE IF EXISTS %Q.'%q_stat';"
+ "%s DROP TABLE IF EXISTS %Q.'%q_content';",
+ zDb, p->zName,
+ zDb, p->zName,
+ zDb, p->zName,
+ zDb, p->zName,
+ (p->zContentTbl ? "--" : ""), zDb,p->zName
+ );
/* If everything has worked, invoke fts3DisconnectMethod() to free the
** memory associated with the Fts3Table structure and return SQLITE_OK.
@@ -158899,7 +185109,7 @@ static int fts3DestroyMethod(sqlite3_vtab *pVtab){
** passed as the first argument. This is done as part of the xConnect()
** and xCreate() methods.
**
-** If *pRc is non-zero when this function is called, it is a no-op.
+** If *pRc is non-zero when this function is called, it is a no-op.
** Otherwise, if an error occurs, an SQLite error code is stored in *pRc
** before returning.
*/
@@ -158913,6 +185123,7 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid");
sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+ sqlite3_vtab_config(p->db, SQLITE_VTAB_INNOCUOUS);
/* Create a list of user columns for the virtual table */
zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]);
@@ -158922,7 +185133,7 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
/* Create the whole "CREATE TABLE" statement to pass to SQLite */
zSql = sqlite3_mprintf(
- "CREATE TABLE x(%s %Q HIDDEN, docid HIDDEN, %Q HIDDEN)",
+ "CREATE TABLE x(%s %Q HIDDEN, docid HIDDEN, %Q HIDDEN)",
zCols, p->zName, zLanguageid
);
if( !zCols || !zSql ){
@@ -158941,7 +185152,7 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
** Create the %_stat table if it does not already exist.
*/
SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int *pRc, Fts3Table *p){
- fts3DbExec(pRc, p->db,
+ fts3DbExec(pRc, p->db,
"CREATE TABLE IF NOT EXISTS %Q.'%q_stat'"
"(id INTEGER PRIMARY KEY, value BLOB);",
p->zDb, p->zName
@@ -158977,9 +185188,9 @@ static int fts3CreateTables(Fts3Table *p){
zContentCols = sqlite3_mprintf("%z, langid", zContentCols, zLanguageid);
}
if( zContentCols==0 ) rc = SQLITE_NOMEM;
-
+
/* Create the content table */
- fts3DbExec(&rc, db,
+ fts3DbExec(&rc, db,
"CREATE TABLE %Q.'%q_content'(%s)",
p->zDb, p->zName, zContentCols
);
@@ -158987,11 +185198,11 @@ static int fts3CreateTables(Fts3Table *p){
}
/* Create other tables */
- fts3DbExec(&rc, db,
+ fts3DbExec(&rc, db,
"CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);",
p->zDb, p->zName
);
- fts3DbExec(&rc, db,
+ fts3DbExec(&rc, db,
"CREATE TABLE %Q.'%q_segdir'("
"level INTEGER,"
"idx INTEGER,"
@@ -159004,7 +185215,7 @@ static int fts3CreateTables(Fts3Table *p){
p->zDb, p->zName
);
if( p->bHasDocsize ){
- fts3DbExec(&rc, db,
+ fts3DbExec(&rc, db,
"CREATE TABLE %Q.'%q_docsize'(docid INTEGER PRIMARY KEY, size BLOB);",
p->zDb, p->zName
);
@@ -159019,7 +185230,7 @@ static int fts3CreateTables(Fts3Table *p){
/*
** Store the current database page-size in bytes in p->nPgsz.
**
-** If *pRc is non-zero when this function is called, it is a no-op.
+** If *pRc is non-zero when this function is called, it is a no-op.
** Otherwise, if an error occurs, an SQLite error code is stored in *pRc
** before returning.
*/
@@ -159028,7 +185239,7 @@ static void fts3DatabasePageSize(int *pRc, Fts3Table *p){
int rc; /* Return code */
char *zSql; /* SQL text "PRAGMA %Q.page_size" */
sqlite3_stmt *pStmt; /* Compiled "PRAGMA %Q.page_size" statement */
-
+
zSql = sqlite3_mprintf("PRAGMA %Q.page_size", p->zDb);
if( !zSql ){
rc = SQLITE_NOMEM;
@@ -159054,11 +185265,11 @@ static void fts3DatabasePageSize(int *pRc, Fts3Table *p){
**
** <key> = <value>
**
-** There may not be whitespace surrounding the "=" character. The <value>
+** There may not be whitespace surrounding the "=" character. The <value>
** term may be quoted, but the <key> may not.
*/
static int fts3IsSpecialColumn(
- const char *z,
+ const char *z,
int *pnKey,
char **pzValue
){
@@ -159116,10 +185327,10 @@ static void fts3Appendf(
** memory.
*/
static char *fts3QuoteId(char const *zInput){
- int nRet;
+ sqlite3_int64 nRet;
char *zRet;
nRet = 2 + (int)strlen(zInput)*2 + 1;
- zRet = sqlite3_malloc(nRet);
+ zRet = sqlite3_malloc64(nRet);
if( zRet ){
int i;
char *z = zRet;
@@ -159135,7 +185346,7 @@ static char *fts3QuoteId(char const *zInput){
}
/*
-** Return a list of comma separated SQL expressions and a FROM clause that
+** Return a list of comma separated SQL expressions and a FROM clause that
** could be used in a SELECT statement such as the following:
**
** SELECT <list of expressions> FROM %_content AS x ...
@@ -159186,7 +185397,7 @@ static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){
fts3Appendf(pRc, &zRet, ", x.%Q", p->zLanguageid);
}
}
- fts3Appendf(pRc, &zRet, " FROM '%q'.'%q%s' AS x",
+ fts3Appendf(pRc, &zRet, " FROM '%q'.'%q%s' AS x",
p->zDb,
(p->zContentTbl ? p->zContentTbl : p->zName),
(p->zContentTbl ? "" : "_content")
@@ -159201,7 +185412,7 @@ static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){
**
** If argument zFunc is not NULL, then all but the first question mark
** is preceded by zFunc and an open bracket, and followed by a closed
-** bracket. For example, if zFunc is "zip" and the FTS3 table has three
+** bracket. For example, if zFunc is "zip" and the FTS3 table has three
** user-defined text columns, the following string is returned:
**
** "?, zip(?), zip(?), zip(?)"
@@ -159237,12 +185448,28 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
}
/*
+** Buffer z contains a positive integer value encoded as utf-8 text.
+** Decode this value and store it in *pnOut, returning the number of bytes
+** consumed. If an overflow error occurs return a negative value.
+*/
+SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut){
+ u64 iVal = 0;
+ int i;
+ for(i=0; z[i]>='0' && z[i]<='9'; i++){
+ iVal = iVal*10 + (z[i] - '0');
+ if( iVal>0x7FFFFFFF ) return -1;
+ }
+ *pnOut = (int)iVal;
+ return i;
+}
+
+/*
** This function interprets the string at (*pp) as a non-negative integer
-** value. It reads the integer and sets *pnOut to the value read, then
+** value. It reads the integer and sets *pnOut to the value read, then
** sets *pp to point to the byte immediately following the last byte of
** the integer value.
**
-** Only decimal digits ('0'..'9') may be part of an integer value.
+** Only decimal digits ('0'..'9') may be part of an integer value.
**
** If *pp does not being with a decimal digit SQLITE_ERROR is returned and
** the output value undefined. Otherwise SQLITE_OK is returned.
@@ -159251,19 +185478,17 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
*/
static int fts3GobbleInt(const char **pp, int *pnOut){
const int MAX_NPREFIX = 10000000;
- const char *p; /* Iterator pointer */
int nInt = 0; /* Output value */
-
- for(p=*pp; p[0]>='0' && p[0]<='9'; p++){
- nInt = nInt * 10 + (p[0] - '0');
- if( nInt>MAX_NPREFIX ){
- nInt = 0;
- break;
- }
+ int nByte;
+ nByte = sqlite3Fts3ReadInt(*pp, &nInt);
+ if( nInt>MAX_NPREFIX ){
+ nInt = 0;
+ }
+ if( nByte==0 ){
+ return SQLITE_ERROR;
}
- if( p==*pp ) return SQLITE_ERROR;
*pnOut = nInt;
- *pp = p;
+ *pp += nByte;
return SQLITE_OK;
}
@@ -159300,7 +185525,7 @@ static int fts3PrefixParameter(
}
}
- aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex);
+ aIndex = sqlite3_malloc64(sizeof(struct Fts3Index) * nIndex);
*apIndex = aIndex;
if( !aIndex ){
return SQLITE_NOMEM;
@@ -159363,7 +185588,7 @@ static int fts3ContentColumns(
char **pzErr /* OUT: error message */
){
int rc = SQLITE_OK; /* Return code */
- char *zSql; /* "SELECT *" statement on zTbl */
+ char *zSql; /* "SELECT *" statement on zTbl */
sqlite3_stmt *pStmt = 0; /* Compiled version of zSql */
zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zTbl);
@@ -159379,7 +185604,7 @@ static int fts3ContentColumns(
if( rc==SQLITE_OK ){
const char **azCol; /* Output array */
- int nStr = 0; /* Size of all column names (incl. 0x00) */
+ sqlite3_int64 nStr = 0; /* Size of all column names (incl. 0x00) */
int nCol; /* Number of table columns */
int i; /* Used to iterate through columns */
@@ -159389,11 +185614,11 @@ static int fts3ContentColumns(
nCol = sqlite3_column_count(pStmt);
for(i=0; i<nCol; i++){
const char *zCol = sqlite3_column_name(pStmt, i);
- nStr += (int)strlen(zCol) + 1;
+ nStr += strlen(zCol) + 1;
}
/* Allocate and populate the array to return. */
- azCol = (const char **)sqlite3_malloc(sizeof(char *) * nCol + nStr);
+ azCol = (const char **)sqlite3_malloc64(sizeof(char *) * nCol + nStr);
if( azCol==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -159437,11 +185662,11 @@ static int fts3InitVtab(
sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */
char **pzErr /* Write any error message here */
){
- Fts3Hash *pHash = (Fts3Hash *)pAux;
+ Fts3Hash *pHash = &((Fts3HashWrapper*)pAux)->hash;
Fts3Table *p = 0; /* Pointer to allocated vtab */
int rc = SQLITE_OK; /* Return code */
int i; /* Iterator variable */
- int nByte; /* Size of allocation used for *p */
+ sqlite3_int64 nByte; /* Size of allocation used for *p */
int iCol; /* Column index */
int nString = 0; /* Bytes required to hold all column names */
int nCol = 0; /* Number of columns in the FTS table */
@@ -159475,10 +185700,10 @@ static int fts3InitVtab(
nName = (int)strlen(argv[2]) + 1;
nByte = sizeof(const char *) * (argc-2);
- aCol = (const char **)sqlite3_malloc(nByte);
+ aCol = (const char **)sqlite3_malloc64(nByte);
if( aCol ){
memset((void*)aCol, 0, nByte);
- azNotindexed = (char **)sqlite3_malloc(nByte);
+ azNotindexed = (char **)sqlite3_malloc64(nByte);
}
if( azNotindexed ){
memset(azNotindexed, 0, nByte);
@@ -159505,9 +185730,9 @@ static int fts3InitVtab(
char *zVal;
/* Check if this is a tokenizer specification */
- if( !pTokenizer
+ if( !pTokenizer
&& strlen(z)>8
- && 0==sqlite3_strnicmp(z, "tokenize", 8)
+ && 0==sqlite3_strnicmp(z, "tokenize", 8)
&& 0==sqlite3Fts3IsIdChar(z[8])
){
rc = sqlite3Fts3InitTokenizer(pHash, &z[9], &pTokenizer, pzErr);
@@ -159567,8 +185792,8 @@ static int fts3InitVtab(
break;
case 4: /* ORDER */
- if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3))
- && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4))
+ if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3))
+ && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4))
){
sqlite3Fts3ErrMsg(pzErr, "unrecognized order: %s", zVal);
rc = SQLITE_ERROR;
@@ -159619,17 +185844,17 @@ static int fts3InitVtab(
** TABLE statement, use all columns from the content table.
*/
if( rc==SQLITE_OK && zContent ){
- sqlite3_free(zCompress);
- sqlite3_free(zUncompress);
+ sqlite3_free(zCompress);
+ sqlite3_free(zUncompress);
zCompress = 0;
zUncompress = 0;
if( nCol==0 ){
- sqlite3_free((void*)aCol);
+ sqlite3_free((void*)aCol);
aCol = 0;
rc = fts3ContentColumns(db, argv[1], zContent,&aCol,&nCol,&nString,pzErr);
/* If a languageid= option was specified, remove the language id
- ** column from the aCol[] array. */
+ ** column from the aCol[] array. */
if( rc==SQLITE_OK && zLanguageid ){
int j;
for(j=0; j<nCol; j++){
@@ -159673,7 +185898,7 @@ static int fts3InitVtab(
nName + /* zName */
nDb + /* zDb */
nString; /* Space for azColumn strings */
- p = (Fts3Table*)sqlite3_malloc(nByte);
+ p = (Fts3Table*)sqlite3_malloc64(nByte);
if( p==0 ){
rc = SQLITE_NOMEM;
goto fts3_init_out;
@@ -159716,7 +185941,7 @@ static int fts3InitVtab(
/* Fill in the azColumn array */
for(iCol=0; iCol<nCol; iCol++){
- char *z;
+ char *z;
int n = 0;
z = (char *)sqlite3Fts3NextToken(aCol[iCol], &n);
if( n>0 ){
@@ -159735,7 +185960,7 @@ static int fts3InitVtab(
for(i=0; i<nNotindexed; i++){
char *zNot = azNotindexed[i];
if( zNot && n==(int)strlen(zNot)
- && 0==sqlite3_strnicmp(p->azColumn[iCol], zNot, n)
+ && 0==sqlite3_strnicmp(p->azColumn[iCol], zNot, n)
){
p->abNotindexed[iCol] = 1;
sqlite3_free(zNot);
@@ -159759,7 +185984,7 @@ static int fts3InitVtab(
p->zWriteExprlist = fts3WriteExprList(p, zCompress, &rc);
if( rc!=SQLITE_OK ) goto fts3_init_out;
- /* If this is an xCreate call, create the underlying tables in the
+ /* If this is an xCreate call, create the underlying tables in the
** database. TODO: For xConnect(), it could verify that said tables exist.
*/
if( isCreate ){
@@ -159778,6 +186003,10 @@ static int fts3InitVtab(
fts3DatabasePageSize(&rc, p);
p->nNodeSize = p->nPgsz-35;
+#if defined(SQLITE_DEBUG)||defined(SQLITE_TEST)
+ p->nMergeCount = FTS3_MERGE_COUNT;
+#endif
+
/* Declare the table schema to SQLite. */
fts3DeclareVtab(&rc, p);
@@ -159855,11 +186084,11 @@ static void fts3SetUniqueFlag(sqlite3_index_info *pIdxInfo){
#endif
}
-/*
+/*
** Implementation of the xBestIndex method for FTS3 tables. There
** are three possible strategies, in order of preference:
**
-** 1. Direct lookup by rowid or docid.
+** 1. Direct lookup by rowid or docid.
** 2. Full-text search using a MATCH operator on a non-docid column.
** 3. Linear scan of %_content table.
*/
@@ -159873,8 +186102,12 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
int iDocidLe = -1; /* Index of docid<=x constraint, if present */
int iIdx;
+ if( p->bLock ){
+ return SQLITE_ERROR;
+ }
+
/* By default use a full table scan. This is an expensive option,
- ** so search through the constraints to see if a more efficient
+ ** so search through the constraints to see if a more efficient
** strategy is possible.
*/
pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
@@ -159910,12 +186143,12 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
**
** If there is more than one MATCH constraint available, use the first
** one encountered. If there is both a MATCH constraint and a direct
- ** rowid/docid lookup, prefer the MATCH strategy. This is done even
+ ** rowid/docid lookup, prefer the MATCH strategy. This is done even
** though the rowid/docid lookup is faster than a MATCH query, selecting
- ** it would lead to an "unable to use function MATCH in the requested
+ ** it would lead to an "unable to use function MATCH in the requested
** context" error.
*/
- if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH
+ if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH
&& pCons->iColumn>=0 && pCons->iColumn<=p->nColumn
){
pInfo->idxNum = FTS3_FULLTEXT_SEARCH + pCons->iColumn;
@@ -159924,7 +186157,7 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
}
/* Equality constraint on the langid column */
- if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ
+ if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ
&& pCons->iColumn==p->nColumn + 2
){
iLangidCons = i;
@@ -159952,22 +186185,22 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
if( iCons>=0 ){
pInfo->aConstraintUsage[iCons].argvIndex = iIdx++;
pInfo->aConstraintUsage[iCons].omit = 1;
- }
+ }
if( iLangidCons>=0 ){
pInfo->idxNum |= FTS3_HAVE_LANGID;
pInfo->aConstraintUsage[iLangidCons].argvIndex = iIdx++;
- }
+ }
if( iDocidGe>=0 ){
pInfo->idxNum |= FTS3_HAVE_DOCID_GE;
pInfo->aConstraintUsage[iDocidGe].argvIndex = iIdx++;
- }
+ }
if( iDocidLe>=0 ){
pInfo->idxNum |= FTS3_HAVE_DOCID_LE;
pInfo->aConstraintUsage[iDocidLe].argvIndex = iIdx++;
- }
+ }
/* Regardless of the strategy selected, FTS can deliver rows in rowid (or
- ** docid) order. Both ascending and descending are possible.
+ ** docid) order. Both ascending and descending are possible.
*/
if( pInfo->nOrderBy==1 ){
struct sqlite3_index_orderby *pOrder = &pInfo->aOrderBy[0];
@@ -159994,7 +186227,7 @@ static int fts3OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
UNUSED_PARAMETER(pVTab);
/* Allocate a buffer large enough for an Fts3Cursor structure. If the
- ** allocation succeeds, zero it and return SQLITE_OK. Otherwise,
+ ** allocation succeeds, zero it and return SQLITE_OK. Otherwise,
** if the allocation fails, return SQLITE_NOMEM.
*/
*ppCsr = pCsr = (sqlite3_vtab_cursor *)sqlite3_malloc(sizeof(Fts3Cursor));
@@ -160071,7 +186304,11 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){
}else{
zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
if( !zSql ) return SQLITE_NOMEM;
- rc = sqlite3_prepare_v3(p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0);
+ p->bLock++;
+ rc = sqlite3_prepare_v3(
+ p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
+ );
+ p->bLock--;
sqlite3_free(zSql);
}
if( rc==SQLITE_OK ) pCsr->bSeekStmt = 1;
@@ -160082,18 +186319,22 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){
/*
** Position the pCsr->pStmt statement so that it is on the row
** of the %_content table that contains the last match. Return
-** SQLITE_OK on success.
+** SQLITE_OK on success.
*/
static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){
int rc = SQLITE_OK;
if( pCsr->isRequireSeek ){
rc = fts3CursorSeekStmt(pCsr);
if( rc==SQLITE_OK ){
+ Fts3Table *pTab = (Fts3Table*)pCsr->base.pVtab;
+ pTab->bLock++;
sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId);
pCsr->isRequireSeek = 0;
if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){
+ pTab->bLock--;
return SQLITE_OK;
}else{
+ pTab->bLock--;
rc = sqlite3_reset(pCsr->pStmt);
if( rc==SQLITE_OK && ((Fts3Table *)pCsr->base.pVtab)->zContentTbl==0 ){
/* If no row was found and no error has occurred, then the %_content
@@ -160114,7 +186355,7 @@ static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){
/*
** This function is used to process a single interior node when searching
-** a b-tree for a term or term prefix. The node data is passed to this
+** a b-tree for a term or term prefix. The node data is passed to this
** function via the zNode/nNode parameters. The term to search for is
** passed in zTerm/nTerm.
**
@@ -160141,11 +186382,12 @@ static int fts3ScanInteriorNode(
char *zBuffer = 0; /* Buffer to load terms into */
i64 nAlloc = 0; /* Size of allocated buffer */
int isFirstTerm = 1; /* True when processing first term on page */
- sqlite3_int64 iChild; /* Block id of child node to descend to */
+ u64 iChild; /* Block id of child node to descend to */
+ int nBuffer = 0; /* Total term size */
- /* Skip over the 'height' varint that occurs at the start of every
+ /* Skip over the 'height' varint that occurs at the start of every
** interior node. Then load the blockid of the left-child of the b-tree
- ** node into variable iChild.
+ ** node into variable iChild.
**
** Even if the data structure on disk is corrupted, this (reading two
** varints from the buffer) does not risk an overread. If zNode is a
@@ -160156,28 +186398,31 @@ static int fts3ScanInteriorNode(
** table, then there are always 20 bytes of zeroed padding following the
** nNode bytes of content (see sqlite3Fts3ReadBlock() for details).
*/
- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild);
- zCsr += sqlite3Fts3GetVarint(zCsr, &iChild);
+ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild);
+ zCsr += sqlite3Fts3GetVarintU(zCsr, &iChild);
if( zCsr>zEnd ){
return FTS_CORRUPT_VTAB;
}
-
+
while( zCsr<zEnd && (piFirst || piLast) ){
int cmp; /* memcmp() result */
int nSuffix; /* Size of term suffix */
int nPrefix = 0; /* Size of term prefix */
- int nBuffer; /* Total term size */
-
+
/* Load the next term on the node into zBuffer. Use realloc() to expand
** the size of zBuffer if required. */
if( !isFirstTerm ){
zCsr += fts3GetVarint32(zCsr, &nPrefix);
+ if( nPrefix>nBuffer ){
+ rc = FTS_CORRUPT_VTAB;
+ goto finish_scan;
+ }
}
isFirstTerm = 0;
zCsr += fts3GetVarint32(zCsr, &nSuffix);
-
+
assert( nPrefix>=0 && nSuffix>=0 );
- if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr ){
+ if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr || nSuffix==0 ){
rc = FTS_CORRUPT_VTAB;
goto finish_scan;
}
@@ -160198,8 +186443,8 @@ static int fts3ScanInteriorNode(
/* Compare the term we are searching for with the term just loaded from
** the interior node. If the specified term is greater than or equal
- ** to the term from the interior node, then all terms on the sub-tree
- ** headed by node iChild are smaller than zTerm. No need to search
+ ** to the term from the interior node, then all terms on the sub-tree
+ ** headed by node iChild are smaller than zTerm. No need to search
** iChild.
**
** If the interior node term is larger than the specified term, then
@@ -160207,20 +186452,20 @@ static int fts3ScanInteriorNode(
*/
cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer));
if( piFirst && (cmp<0 || (cmp==0 && nBuffer>nTerm)) ){
- *piFirst = iChild;
+ *piFirst = (i64)iChild;
piFirst = 0;
}
if( piLast && cmp<0 ){
- *piLast = iChild;
+ *piLast = (i64)iChild;
piLast = 0;
}
iChild++;
};
- if( piFirst ) *piFirst = iChild;
- if( piLast ) *piLast = iChild;
+ if( piFirst ) *piFirst = (i64)iChild;
+ if( piLast ) *piLast = (i64)iChild;
finish_scan:
sqlite3_free(zBuffer);
@@ -160235,20 +186480,20 @@ static int fts3ScanInteriorNode(
** node for the range of leaf nodes that may contain the specified term
** or terms for which the specified term is a prefix.
**
-** If piLeaf is not NULL, then *piLeaf is set to the blockid of the
+** If piLeaf is not NULL, then *piLeaf is set to the blockid of the
** left-most leaf node in the tree that may contain the specified term.
** If piLeaf2 is not NULL, then *piLeaf2 is set to the blockid of the
** right-most leaf node that may contain a term for which the specified
** term is a prefix.
**
-** It is possible that the range of returned leaf nodes does not contain
-** the specified term or any terms for which it is a prefix. However, if the
+** It is possible that the range of returned leaf nodes does not contain
+** the specified term or any terms for which it is a prefix. However, if the
** segment does contain any such terms, they are stored within the identified
** range. Because this function only inspects interior segment nodes (and
** never loads leaf nodes into memory), it is not possible to be sure.
**
** If an error occurs, an error code other than SQLITE_OK is returned.
-*/
+*/
static int fts3SelectLeaf(
Fts3Table *p, /* Virtual table handle */
const char *zTerm, /* Term to select leaves for */
@@ -160265,7 +186510,7 @@ static int fts3SelectLeaf(
fts3GetVarint32(zNode, &iHeight);
rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
- assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
+ assert_fts3_nc( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
if( rc==SQLITE_OK && iHeight>1 ){
char *zBlob = 0; /* Blob read from %_segments table */
@@ -160285,7 +186530,13 @@ static int fts3SelectLeaf(
rc = sqlite3Fts3ReadBlock(p, piLeaf?*piLeaf:*piLeaf2, &zBlob, &nBlob, 0);
}
if( rc==SQLITE_OK ){
- rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2);
+ int iNewHeight = 0;
+ fts3GetVarint32(zBlob, &iNewHeight);
+ if( iNewHeight>=iHeight ){
+ rc = FTS_CORRUPT_VTAB;
+ }else{
+ rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2);
+ }
}
sqlite3_free(zBlob);
}
@@ -160294,7 +186545,7 @@ static int fts3SelectLeaf(
}
/*
-** This function is used to create delta-encoded serialized lists of FTS3
+** This function is used to create delta-encoded serialized lists of FTS3
** varints. Each call to this function appends a single varint to a list.
*/
static void fts3PutDeltaVarint(
@@ -160302,17 +186553,17 @@ static void fts3PutDeltaVarint(
sqlite3_int64 *piPrev, /* IN/OUT: Previous value written to list */
sqlite3_int64 iVal /* Write this value to the list */
){
- assert( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) );
+ assert_fts3_nc( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) );
*pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev);
*piPrev = iVal;
}
/*
-** When this function is called, *ppPoslist is assumed to point to the
+** When this function is called, *ppPoslist is assumed to point to the
** start of a position-list. After it returns, *ppPoslist points to the
** first byte after the position-list.
**
-** A position list is list of positions (delta encoded) and columns for
+** A position list is list of positions (delta encoded) and columns for
** a single document record of a doclist. So, in other words, this
** routine advances *ppPoslist so that it points to the next docid in
** the doclist, or to the first byte past the end of the doclist.
@@ -160325,12 +186576,12 @@ static void fts3PoslistCopy(char **pp, char **ppPoslist){
char *pEnd = *ppPoslist;
char c = 0;
- /* The end of a position list is marked by a zero encoded as an FTS3
+ /* The end of a position list is marked by a zero encoded as an FTS3
** varint. A single POS_END (0) byte. Except, if the 0 byte is preceded by
** a byte with the 0x80 bit set, then it is not a varint 0, but the tail
** of some other, multi-byte, value.
**
- ** The following while-loop moves pEnd to point to the first byte that is not
+ ** The following while-loop moves pEnd to point to the first byte that is not
** immediately preceded by a byte with the 0x80 bit set. Then increments
** pEnd once more so that it points to the byte immediately following the
** last byte in the position-list.
@@ -160352,7 +186603,7 @@ static void fts3PoslistCopy(char **pp, char **ppPoslist){
}
/*
-** When this function is called, *ppPoslist is assumed to point to the
+** When this function is called, *ppPoslist is assumed to point to the
** start of a column-list. After it returns, *ppPoslist points to the
** to the terminator (POS_COLUMN or POS_END) byte of the column-list.
**
@@ -160390,10 +186641,11 @@ static void fts3ColumnlistCopy(char **pp, char **ppPoslist){
}
/*
-** Value used to signify the end of an position-list. This is safe because
-** it is not possible to have a document with 2^31 terms.
+** Value used to signify the end of an position-list. This must be
+** as large or larger than any value that might appear on the
+** position-list, even a position list that has been corrupted.
*/
-#define POSITION_LIST_END 0x7fffffff
+#define POSITION_LIST_END LARGEST_INT64
/*
** This function is used to help parse position-lists. When this function is
@@ -160402,7 +186654,7 @@ static void fts3ColumnlistCopy(char **pp, char **ppPoslist){
** (in which case **pp will be a terminator bytes POS_END (0) or
** (1)).
**
-** If *pp points past the end of the current position-list, set *pi to
+** If *pp points past the end of the current position-list, set *pi to
** POSITION_LIST_END and return. Otherwise, read the next varint from *pp,
** increment the current value of *pi by the value read, and set *pp to
** point to the next value before returning.
@@ -160418,7 +186670,9 @@ static void fts3ReadNextPos(
sqlite3_int64 *pi /* IN/OUT: Value read from position-list */
){
if( (**pp)&0xFE ){
- fts3GetDeltaVarint(pp, pi);
+ int iVal;
+ *pp += fts3GetVarint32((*pp), &iVal);
+ *pi += iVal;
*pi -= 2;
}else{
*pi = POSITION_LIST_END;
@@ -160430,7 +186684,7 @@ static void fts3ReadNextPos(
** the value of iCol encoded as a varint to *pp. This will start a new
** column list.
**
-** Set *pp to point to the byte just after the last byte written before
+** Set *pp to point to the byte just after the last byte written before
** returning (do not modify it if iCol==0). Return the total number of bytes
** written (0 if iCol==0).
*/
@@ -160452,7 +186706,7 @@ static int fts3PutColNumber(char **pp, int iCol){
** updated appropriately. The caller is responsible for insuring
** that there is enough space in *pp to hold the complete output.
*/
-static void fts3PoslistMerge(
+static int fts3PoslistMerge(
char **pp, /* Output buffer */
char **pp1, /* Left input list */
char **pp2 /* Right input list */
@@ -160465,12 +186719,18 @@ static void fts3PoslistMerge(
int iCol1; /* The current column index in pp1 */
int iCol2; /* The current column index in pp2 */
- if( *p1==POS_COLUMN ) fts3GetVarint32(&p1[1], &iCol1);
- else if( *p1==POS_END ) iCol1 = POSITION_LIST_END;
+ if( *p1==POS_COLUMN ){
+ fts3GetVarint32(&p1[1], &iCol1);
+ if( iCol1==0 ) return FTS_CORRUPT_VTAB;
+ }
+ else if( *p1==POS_END ) iCol1 = 0x7fffffff;
else iCol1 = 0;
- if( *p2==POS_COLUMN ) fts3GetVarint32(&p2[1], &iCol2);
- else if( *p2==POS_END ) iCol2 = POSITION_LIST_END;
+ if( *p2==POS_COLUMN ){
+ fts3GetVarint32(&p2[1], &iCol2);
+ if( iCol2==0 ) return FTS_CORRUPT_VTAB;
+ }
+ else if( *p2==POS_END ) iCol2 = 0x7fffffff;
else iCol2 = 0;
if( iCol1==iCol2 ){
@@ -160483,7 +186743,7 @@ static void fts3PoslistMerge(
/* At this point, both p1 and p2 point to the start of column-lists
** for the same column (the column with index iCol1 and iCol2).
- ** A column-list is a list of non-negative delta-encoded varints, each
+ ** A column-list is a list of non-negative delta-encoded varints, each
** incremented by 2 before being stored. Each list is terminated by a
** POS_END (0) or POS_COLUMN (1). The following block merges the two lists
** and writes the results to buffer p. p is left pointing to the byte
@@ -160492,8 +186752,11 @@ static void fts3PoslistMerge(
*/
fts3GetDeltaVarint(&p1, &i1);
fts3GetDeltaVarint(&p2, &i2);
+ if( i1<2 || i2<2 ){
+ break;
+ }
do {
- fts3PutDeltaVarint(&p, &iPrev, (i1<i2) ? i1 : i2);
+ fts3PutDeltaVarint(&p, &iPrev, (i1<i2) ? i1 : i2);
iPrev -= 2;
if( i1==i2 ){
fts3ReadNextPos(&p1, &i1);
@@ -160517,6 +186780,7 @@ static void fts3PoslistMerge(
*pp = p;
*pp1 = p1 + 1;
*pp2 = p2 + 1;
+ return SQLITE_OK;
}
/*
@@ -160534,7 +186798,7 @@ static void fts3PoslistMerge(
** When this function returns, both *pp1 and *pp2 are left pointing to the
** byte following the 0x00 terminator of their respective position lists.
**
-** If isSaveLeft is 0, an entry is added to the output position list for
+** If isSaveLeft is 0, an entry is added to the output position list for
** each position in *pp2 for which there exists one or more positions in
** *pp1 so that (pos(*pp2)>pos(*pp1) && pos(*pp2)-pos(*pp1)<=nToken). i.e.
** when the *pp1 token appears before the *pp2 token, but not more than nToken
@@ -160559,12 +186823,12 @@ static int fts3PoslistPhraseMerge(
/* Never set both isSaveLeft and isExact for the same invocation. */
assert( isSaveLeft==0 || isExact==0 );
- assert( p!=0 && *p1!=0 && *p2!=0 );
- if( *p1==POS_COLUMN ){
+ assert_fts3_nc( p!=0 && *p1!=0 && *p2!=0 );
+ if( *p1==POS_COLUMN ){
p1++;
p1 += fts3GetVarint32(p1, &iCol1);
}
- if( *p2==POS_COLUMN ){
+ if( *p2==POS_COLUMN ){
p2++;
p2 += fts3GetVarint32(p2, &iCol2);
}
@@ -160581,14 +186845,13 @@ static int fts3PoslistPhraseMerge(
p += sqlite3Fts3PutVarint(p, iCol1);
}
- assert( *p1!=POS_END && *p1!=POS_COLUMN );
- assert( *p2!=POS_END && *p2!=POS_COLUMN );
fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2;
fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2;
+ if( iPos1<0 || iPos2<0 ) break;
while( 1 ){
- if( iPos2==iPos1+nToken
- || (isExact==0 && iPos2>iPos1 && iPos2<=iPos1+nToken)
+ if( iPos2==iPos1+nToken
+ || (isExact==0 && iPos2>iPos1 && iPos2<=iPos1+nToken)
){
sqlite3_int64 iSave;
iSave = isSaveLeft ? iPos1 : iPos2;
@@ -160623,8 +186886,8 @@ static int fts3PoslistPhraseMerge(
/* Advance pointer p1 or p2 (whichever corresponds to the smaller of
** iCol1 and iCol2) so that it points to either the 0x00 that marks the
- ** end of the position list, or the 0x01 that precedes the next
- ** column-number in the position list.
+ ** end of the position list, or the 0x01 that precedes the next
+ ** column-number in the position list.
*/
else if( iCol1<iCol2 ){
fts3ColumnlistCopy(0, &p1);
@@ -160653,14 +186916,14 @@ static int fts3PoslistPhraseMerge(
/*
** Merge two position-lists as required by the NEAR operator. The argument
-** position lists correspond to the left and right phrases of an expression
+** position lists correspond to the left and right phrases of an expression
** like:
**
** "phrase 1" NEAR "phrase number 2"
**
-** Position list *pp1 corresponds to the left-hand side of the NEAR
-** expression and *pp2 to the right. As usual, the indexes in the position
-** lists are the offsets of the last token in each phrase (tokens "1" and "2"
+** Position list *pp1 corresponds to the left-hand side of the NEAR
+** expression and *pp2 to the right. As usual, the indexes in the position
+** lists are the offsets of the last token in each phrase (tokens "1" and "2"
** in the example above).
**
** The output position list - written to *pp - is a copy of *pp2 with those
@@ -160700,7 +186963,7 @@ static int fts3PoslistNearMerge(
return res;
}
-/*
+/*
** An instance of this function is used to merge together the (potentially
** large number of) doclists for each term that matches a prefix query.
** See function fts3TermSelectMerge() for details.
@@ -160721,7 +186984,7 @@ struct TermSelect {
** from *pp. *pp is then set to point 1 byte past the end of the read varint.
**
** If bDescIdx is false, the value read is added to *pVal before returning.
-** If it is true, the value read is subtracted from *pVal before this
+** If it is true, the value read is subtracted from *pVal before this
** function returns.
*/
static void fts3GetDeltaVarint3(
@@ -160733,12 +186996,12 @@ static void fts3GetDeltaVarint3(
if( *pp>=pEnd ){
*pp = 0;
}else{
- sqlite3_int64 iVal;
- *pp += sqlite3Fts3GetVarint(*pp, &iVal);
+ u64 iVal;
+ *pp += sqlite3Fts3GetVarintU(*pp, &iVal);
if( bDescIdx ){
- *pVal -= iVal;
+ *pVal = (i64)((u64)*pVal - iVal);
}else{
- *pVal += iVal;
+ *pVal = (i64)((u64)*pVal + iVal);
}
}
}
@@ -160749,9 +187012,9 @@ static void fts3GetDeltaVarint3(
** end of the value written.
**
** If *pbFirst is zero when this function is called, the value written to
-** the buffer is that of parameter iVal.
+** the buffer is that of parameter iVal.
**
-** If *pbFirst is non-zero when this function is called, then the value
+** If *pbFirst is non-zero when this function is called, then the value
** written is either (iVal-*piPrev) (if bDescIdx is zero) or (*piPrev-iVal)
** (if bDescIdx is non-zero).
**
@@ -160765,14 +187028,16 @@ static void fts3PutDeltaVarint3(
int *pbFirst, /* IN/OUT: True after first int written */
sqlite3_int64 iVal /* Write this value to the list */
){
- sqlite3_int64 iWrite;
+ sqlite3_uint64 iWrite;
if( bDescIdx==0 || *pbFirst==0 ){
- iWrite = iVal - *piPrev;
+ assert_fts3_nc( *pbFirst==0 || iVal>=*piPrev );
+ iWrite = (u64)iVal - (u64)*piPrev;
}else{
- iWrite = *piPrev - iVal;
+ assert_fts3_nc( *piPrev>=iVal );
+ iWrite = (u64)*piPrev - (u64)iVal;
}
assert( *pbFirst || *piPrev==0 );
- assert( *pbFirst==0 || iWrite>0 );
+ assert_fts3_nc( *pbFirst==0 || iWrite>0 );
*pp += sqlite3Fts3PutVarint(*pp, iWrite);
*piPrev = iVal;
*pbFirst = 1;
@@ -160782,17 +187047,18 @@ static void fts3PutDeltaVarint3(
/*
** This macro is used by various functions that merge doclists. The two
** arguments are 64-bit docid values. If the value of the stack variable
-** bDescDoclist is 0 when this macro is invoked, then it returns (i1-i2).
+** bDescDoclist is 0 when this macro is invoked, then it returns (i1-i2).
** Otherwise, (i2-i1).
**
** Using this makes it easier to write code that can merge doclists that are
** sorted in either ascending or descending order.
*/
-#define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i1-i2))
+/* #define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i64)((u64)i1-i2)) */
+#define DOCID_CMP(i1, i2) ((bDescDoclist?-1:1) * (i1>i2?1:((i1==i2)?0:-1)))
/*
** This function does an "OR" merge of two doclists (output contains all
-** positions contained in either argument doclist). If the docids in the
+** positions contained in either argument doclist). If the docids in the
** input doclists are sorted in ascending order, parameter bDescDoclist
** should be false. If they are sorted in ascending order, it should be
** passed a non-zero value.
@@ -160810,6 +187076,7 @@ static int fts3DoclistOrMerge(
char *a2, int n2, /* Second doclist */
char **paOut, int *pnOut /* OUT: Malloc'd doclist */
){
+ int rc = SQLITE_OK;
sqlite3_int64 i1 = 0;
sqlite3_int64 i2 = 0;
sqlite3_int64 iPrev = 0;
@@ -160831,12 +187098,12 @@ static int fts3DoclistOrMerge(
** current and previous docid (a positive number - since the list is in
** ascending order).
**
- ** The first docid written to the output is therefore encoded using the
+ ** The first docid written to the output is therefore encoded using the
** same number of bytes as it is in whichever of the input lists it is
- ** read from. And each subsequent docid read from the same input list
+ ** read from. And each subsequent docid read from the same input list
** consumes either the same or less bytes as it did in the input (since
** the difference between it and the previous value in the output must
- ** be a positive value less than or equal to the delta value read from
+ ** be a positive value less than or equal to the delta value read from
** the input list). The same argument applies to all but the first docid
** read from the 'other' list. And to the contents of all position lists
** that will be copied and merged from the input to the output.
@@ -160848,12 +187115,12 @@ static int fts3DoclistOrMerge(
**
** The space required to store the output is therefore the sum of the
** sizes of the two inputs, plus enough space for exactly one of the input
- ** docids to grow.
+ ** docids to grow.
**
- ** A symetric argument may be made if the doclists are in descending
+ ** A symetric argument may be made if the doclists are in descending
** order.
*/
- aOut = sqlite3_malloc(n1+n2+FTS3_VARINT_MAX-1);
+ aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING);
if( !aOut ) return SQLITE_NOMEM;
p = aOut;
@@ -160864,7 +187131,8 @@ static int fts3DoclistOrMerge(
if( p2 && p1 && iDiff==0 ){
fts3PutDeltaVarint3(&p, bDescDoclist, &iPrev, &bFirstOut, i1);
- fts3PoslistMerge(&p, &p1, &p2);
+ rc = fts3PoslistMerge(&p, &p1, &p2);
+ if( rc ) break;
fts3GetDeltaVarint3(&p1, pEnd1, bDescDoclist, &i1);
fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2);
}else if( !p2 || (p1 && iDiff<0) ){
@@ -160876,12 +187144,20 @@ static int fts3DoclistOrMerge(
fts3PoslistCopy(&p, &p2);
fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2);
}
+
+ assert( (p-aOut)<=((p1?(p1-a1):n1)+(p2?(p2-a2):n2)+FTS3_VARINT_MAX-1) );
}
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(aOut);
+ p = aOut = 0;
+ }else{
+ assert( (p-aOut)<=n1+n2+FTS3_VARINT_MAX-1 );
+ memset(&aOut[(p-aOut)], 0, FTS3_BUFFER_PADDING);
+ }
*paOut = aOut;
*pnOut = (int)(p-aOut);
- assert( *pnOut<=n1+n2+FTS3_VARINT_MAX-1 );
- return SQLITE_OK;
+ return rc;
}
/*
@@ -160891,7 +187167,7 @@ static int fts3DoclistOrMerge(
** exactly nDist tokens before it.
**
** If the docids in the input doclists are sorted in ascending order,
-** parameter bDescDoclist should be false. If they are sorted in ascending
+** parameter bDescDoclist should be false. If they are sorted in ascending
** order, it should be passed a non-zero value.
**
** The right-hand input doclist is overwritten by this function.
@@ -160916,7 +187192,7 @@ static int fts3DoclistPhraseMerge(
assert( nDist>0 );
if( bDescDoclist ){
- aOut = sqlite3_malloc(*pnRight + FTS3_VARINT_MAX);
+ aOut = sqlite3_malloc64((sqlite3_int64)*pnRight + FTS3_VARINT_MAX);
if( aOut==0 ) return SQLITE_NOMEM;
}else{
aOut = aRight;
@@ -161037,7 +187313,7 @@ static int fts3TermSelectFinishMerge(Fts3Table *p, TermSelect *pTS){
int nNew;
char *aNew;
- int rc = fts3DoclistOrMerge(p->bDescIdx,
+ int rc = fts3DoclistOrMerge(p->bDescIdx,
pTS->aaOutput[i], pTS->anOutput[i], aOut, nOut, &aNew, &nNew
);
if( rc!=SQLITE_OK ){
@@ -161081,25 +187357,26 @@ static int fts3TermSelectMerge(
){
if( pTS->aaOutput[0]==0 ){
/* If this is the first term selected, copy the doclist to the output
- ** buffer using memcpy().
+ ** buffer using memcpy().
**
- ** Add FTS3_VARINT_MAX bytes of unused space to the end of the
+ ** Add FTS3_VARINT_MAX bytes of unused space to the end of the
** allocation. This is so as to ensure that the buffer is big enough
** to hold the current doclist AND'd with any other doclist. If the
** doclists are stored in order=ASC order, this padding would not be
** required (since the size of [doclistA AND doclistB] is always less
** than or equal to the size of [doclistA] in that case). But this is
- ** not true for order=DESC. For example, a doclist containing (1, -1)
+ ** not true for order=DESC. For example, a doclist containing (1, -1)
** may be smaller than (-1), as in the first example the -1 may be stored
** as a single-byte delta, whereas in the second it must be stored as a
** FTS3_VARINT_MAX byte varint.
**
** Similar padding is added in the fts3DoclistOrMerge() function.
*/
- pTS->aaOutput[0] = sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1);
+ pTS->aaOutput[0] = sqlite3_malloc64((i64)nDoclist + FTS3_VARINT_MAX + 1);
pTS->anOutput[0] = nDoclist;
if( pTS->aaOutput[0] ){
memcpy(pTS->aaOutput[0], aDoclist, nDoclist);
+ memset(&pTS->aaOutput[0][nDoclist], 0, FTS3_VARINT_MAX);
}else{
return SQLITE_NOMEM;
}
@@ -161118,7 +187395,7 @@ static int fts3TermSelectMerge(
char *aNew;
int nNew;
- int rc = fts3DoclistOrMerge(p->bDescIdx, aMerge, nMerge,
+ int rc = fts3DoclistOrMerge(p->bDescIdx, aMerge, nMerge,
pTS->aaOutput[iOut], pTS->anOutput[iOut], &aNew, &nNew
);
if( rc!=SQLITE_OK ){
@@ -161129,7 +187406,7 @@ static int fts3TermSelectMerge(
if( aMerge!=aDoclist ) sqlite3_free(aMerge);
sqlite3_free(pTS->aaOutput[iOut]);
pTS->aaOutput[iOut] = 0;
-
+
aMerge = aNew;
nMerge = nNew;
if( (iOut+1)==SizeofArray(pTS->aaOutput) ){
@@ -161146,13 +187423,13 @@ static int fts3TermSelectMerge(
** Append SegReader object pNew to the end of the pCsr->apSegment[] array.
*/
static int fts3SegReaderCursorAppend(
- Fts3MultiSegReader *pCsr,
+ Fts3MultiSegReader *pCsr,
Fts3SegReader *pNew
){
if( (pCsr->nSegment%16)==0 ){
Fts3SegReader **apNew;
- int nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*);
- apNew = (Fts3SegReader **)sqlite3_realloc(pCsr->apSegment, nByte);
+ sqlite3_int64 nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*);
+ apNew = (Fts3SegReader **)sqlite3_realloc64(pCsr->apSegment, nByte);
if( !apNew ){
sqlite3Fts3SegReaderFree(pNew);
return SQLITE_NOMEM;
@@ -161185,13 +187462,13 @@ static int fts3SegReaderCursor(
sqlite3_stmt *pStmt = 0; /* Statement to iterate through segments */
int rc2; /* Result of sqlite3_reset() */
- /* If iLevel is less than 0 and this is not a scan, include a seg-reader
+ /* If iLevel is less than 0 and this is not a scan, include a seg-reader
** for the pending-terms. If this is a scan, then this call must be being
** made by an fts4aux module, not an FTS table. In this case calling
- ** Fts3SegReaderPending might segfault, as the data structures used by
+ ** Fts3SegReaderPending might segfault, as the data structures used by
** fts4aux are not completely populated. So it's easiest to filter these
** calls out here. */
- if( iLevel<0 && p->aIndex ){
+ if( iLevel<0 && p->aIndex && p->iPrevLangid==iLangid ){
Fts3SegReader *pSeg = 0;
rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix||isScan, &pSeg);
if( rc==SQLITE_OK && pSeg ){
@@ -161216,16 +187493,16 @@ static int fts3SegReaderCursor(
/* If zTerm is not NULL, and this segment is not stored entirely on its
** root node, the range of leaves scanned can be reduced. Do this. */
- if( iStartBlock && zTerm ){
+ if( iStartBlock && zTerm && zRoot ){
sqlite3_int64 *pi = (isPrefix ? &iLeavesEndBlock : 0);
rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &iStartBlock, pi);
if( rc!=SQLITE_OK ) goto finished;
if( isPrefix==0 && isScan==0 ) iLeavesEndBlock = iStartBlock;
}
-
- rc = sqlite3Fts3SegReaderNew(pCsr->nSegment+1,
+
+ rc = sqlite3Fts3SegReaderNew(pCsr->nSegment+1,
(isPrefix==0 && isScan==0),
- iStartBlock, iLeavesEndBlock,
+ iStartBlock, iLeavesEndBlock,
iEndBlock, zRoot, nRoot, &pSeg
);
if( rc!=SQLITE_OK ) goto finished;
@@ -161241,7 +187518,7 @@ static int fts3SegReaderCursor(
}
/*
-** Set up a cursor object for iterating through a full-text index or a
+** Set up a cursor object for iterating through a full-text index or a
** single level therein.
*/
SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
@@ -161257,7 +187534,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
){
assert( iIndex>=0 && iIndex<p->nIndex );
assert( iLevel==FTS3_SEGCURSOR_ALL
- || iLevel==FTS3_SEGCURSOR_PENDING
+ || iLevel==FTS3_SEGCURSOR_PENDING
|| iLevel>=0
);
assert( iLevel<FTS3_SEGDIR_MAXLEVEL );
@@ -161283,20 +187560,20 @@ static int fts3SegReaderCursorAddZero(
int nTerm, /* Number of bytes in zTerm */
Fts3MultiSegReader *pCsr /* Fts3MultiSegReader to modify */
){
- return fts3SegReaderCursor(p,
+ return fts3SegReaderCursor(p,
iLangid, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0,pCsr
);
}
/*
** Open an Fts3MultiSegReader to scan the doclist for term zTerm/nTerm. Or,
-** if isPrefix is true, to scan the doclist for all terms for which
+** if isPrefix is true, to scan the doclist for all terms for which
** zTerm/nTerm is a prefix. If successful, return SQLITE_OK and write
** a pointer to the new Fts3MultiSegReader to *ppSegcsr. Otherwise, return
** an SQLite error code.
**
** It is the responsibility of the caller to free this object by eventually
-** passing it to fts3SegReaderCursorFree()
+** passing it to fts3SegReaderCursorFree()
**
** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
** Output parameter *ppSegcsr is set to 0 if an error occurs.
@@ -161321,7 +187598,7 @@ static int fts3TermSegReaderCursor(
for(i=1; bFound==0 && i<p->nIndex; i++){
if( p->aIndex[i].nPrefix==nTerm ){
bFound = 1;
- rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
+ rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr
);
pSegcsr->bLookup = 1;
@@ -161331,7 +187608,7 @@ static int fts3TermSegReaderCursor(
for(i=1; bFound==0 && i<p->nIndex; i++){
if( p->aIndex[i].nPrefix==nTerm+1 ){
bFound = 1;
- rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
+ rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 1, 0, pSegcsr
);
if( rc==SQLITE_OK ){
@@ -161344,7 +187621,7 @@ static int fts3TermSegReaderCursor(
}
if( bFound==0 ){
- rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
+ rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr
);
pSegcsr->bLookup = !isPrefix;
@@ -161392,7 +187669,7 @@ static int fts3TermSelect(
rc = sqlite3Fts3SegReaderStart(p, pSegcsr, &filter);
while( SQLITE_OK==rc
- && SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pSegcsr))
+ && SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pSegcsr))
){
rc = fts3TermSelectMerge(p, &tsc, pSegcsr->aDoclist, pSegcsr->nDoclist);
}
@@ -161421,7 +187698,7 @@ static int fts3TermSelect(
**
** If the isPoslist argument is true, then it is assumed that the doclist
** contains a position-list following each docid. Otherwise, it is assumed
-** that the doclist is simply a list of docids stored as delta encoded
+** that the doclist is simply a list of docids stored as delta encoded
** varints.
*/
static int fts3DoclistCountDocids(char *aList, int nList){
@@ -161454,6 +187731,8 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
int rc;
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
if( pCsr->eSearch==FTS3_DOCID_SEARCH || pCsr->eSearch==FTS3_FULLSCAN_SEARCH ){
+ Fts3Table *pTab = (Fts3Table*)pCursor->pVtab;
+ pTab->bLock++;
if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){
pCsr->isEof = 1;
rc = sqlite3_reset(pCsr->pStmt);
@@ -161461,6 +187740,7 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0);
rc = SQLITE_OK;
}
+ pTab->bLock--;
}else{
rc = fts3EvalNext((Fts3Cursor *)pCursor);
}
@@ -161469,18 +187749,6 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
}
/*
-** The following are copied from sqliteInt.h.
-**
-** Constants for the largest and smallest possible 64-bit signed integers.
-** These macros are designed to work correctly on both 32-bit and 64-bit
-** compilers.
-*/
-#ifndef SQLITE_AMALGAMATION
-# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
-# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
-#endif
-
-/*
** If the numeric type of argument pVal is "integer", then return it
** converted to a 64-bit signed integer. Otherwise, return a copy of
** the second parameter, iDefault.
@@ -161533,6 +187801,10 @@ static int fts3FilterMethod(
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(nVal);
+ if( p->bLock ){
+ return SQLITE_ERROR;
+ }
+
eSearch = (idxNum & 0x0000FFFF);
assert( eSearch>=0 && eSearch<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
assert( p->pSegments==0 );
@@ -161572,7 +187844,7 @@ static int fts3FilterMethod(
assert( p->base.zErrMsg==0 );
rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid,
- p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr,
+ p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr,
&p->base.zErrMsg
);
if( rc!=SQLITE_OK ){
@@ -161599,12 +187871,16 @@ static int fts3FilterMethod(
(pCsr->bDesc ? "DESC" : "ASC")
);
}else{
- zSql = sqlite3_mprintf("SELECT %s ORDER BY rowid %s",
+ zSql = sqlite3_mprintf("SELECT %s ORDER BY rowid %s",
p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC")
);
}
if( zSql ){
- rc = sqlite3_prepare_v3(p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0);
+ p->bLock++;
+ rc = sqlite3_prepare_v3(
+ p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0
+ );
+ p->bLock--;
sqlite3_free(zSql);
}else{
rc = SQLITE_NOMEM;
@@ -161620,8 +187896,8 @@ static int fts3FilterMethod(
return fts3NextMethod(pCursor);
}
-/*
-** This is the xEof method of the virtual table. SQLite calls this
+/*
+** This is the xEof method of the virtual table. SQLite calls this
** routine to find out if it has reached the end of a result set.
*/
static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){
@@ -161633,7 +187909,7 @@ static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){
return pCsr->isEof;
}
-/*
+/*
** This is the xRowid method. The SQLite core calls this routine to
** retrieve the rowid for the current row of the result set. fts3
** exposes %_content.docid as the rowid for the virtual table. The
@@ -161645,7 +187921,7 @@ static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
return SQLITE_OK;
}
-/*
+/*
** This is the xColumn method, called by SQLite to request a value from
** the row that the supplied cursor currently points to.
**
@@ -161688,7 +187964,7 @@ static int fts3ColumnMethod(
break;
}else{
iCol = p->nColumn;
- /* fall-through */
+ /* no break */ deliberate_fall_through
}
default:
@@ -161705,8 +187981,8 @@ static int fts3ColumnMethod(
return rc;
}
-/*
-** This function is the implementation of the xUpdate callback used by
+/*
+** This function is the implementation of the xUpdate callback used by
** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
** inserted, updated or deleted.
*/
@@ -161741,7 +188017,7 @@ static int fts3SyncMethod(sqlite3_vtab *pVtab){
**
** Of course, updating the input segments also involves deleting a bunch
** of blocks from the segments table. But this is not considered overhead
- ** as it would also be required by a crisis-merge that used the same input
+ ** as it would also be required by a crisis-merge that used the same input
** segments.
*/
const u32 nMinMerge = 64; /* Minimum amount of incr-merge work to do */
@@ -161751,8 +188027,8 @@ static int fts3SyncMethod(sqlite3_vtab *pVtab){
i64 iLastRowid = sqlite3_last_insert_rowid(p->db);
rc = sqlite3Fts3PendingTermsFlush(p);
- if( rc==SQLITE_OK
- && p->nLeafAdd>(nMinMerge/16)
+ if( rc==SQLITE_OK
+ && p->nLeafAdd>(nMinMerge/16)
&& p->nAutoincrmerge && p->nAutoincrmerge!=0xff
){
int mxLevel = 0; /* Maximum relative level value in db */
@@ -161791,18 +188067,24 @@ static int fts3SetHasStat(Fts3Table *p){
}
/*
-** Implementation of xBegin() method.
+** Implementation of xBegin() method.
*/
static int fts3BeginMethod(sqlite3_vtab *pVtab){
Fts3Table *p = (Fts3Table*)pVtab;
+ int rc;
UNUSED_PARAMETER(pVtab);
assert( p->pSegments==0 );
assert( p->nPendingData==0 );
assert( p->inTransaction!=1 );
- TESTONLY( p->inTransaction = 1 );
- TESTONLY( p->mxSavepoint = -1; );
p->nLeafAdd = 0;
- return fts3SetHasStat(p);
+ rc = fts3SetHasStat(p);
+#ifdef SQLITE_DEBUG
+ if( rc==SQLITE_OK ){
+ p->inTransaction = 1;
+ p->mxSavepoint = -1;
+ }
+#endif
+ return rc;
}
/*
@@ -161847,17 +188129,17 @@ static void fts3ReversePoslist(char *pStart, char **ppPoslist){
/* Skip backwards passed any trailing 0x00 bytes added by NearTrim() */
while( p>pStart && (c=*p--)==0 );
- /* Search backwards for a varint with value zero (the end of the previous
+ /* Search backwards for a varint with value zero (the end of the previous
** poslist). This is an 0x00 byte preceded by some byte that does not
** have the 0x80 bit set. */
- while( p>pStart && (*p & 0x80) | c ){
- c = *p--;
+ while( p>pStart && (*p & 0x80) | c ){
+ c = *p--;
}
assert( p==pStart || c==0 );
/* At this point p points to that preceding byte without the 0x80 bit
** set. So to find the start of the poslist, skip forward 2 bytes then
- ** over a varint.
+ ** over a varint.
**
** Normally. The other case is that p==pStart and the poslist to return
** is the first in the doclist. In this case do not skip forward 2 bytes.
@@ -161878,7 +188160,7 @@ static void fts3ReversePoslist(char *pStart, char **ppPoslist){
** offsets() and optimize() SQL functions.
**
** If the value passed as the third argument is a blob of size
-** sizeof(Fts3Cursor*), then the blob contents are copied to the
+** sizeof(Fts3Cursor*), then the blob contents are copied to the
** output variable *ppCsr and SQLITE_OK is returned. Otherwise, an error
** message is written to context pContext and SQLITE_ERROR returned. The
** string passed via zFunc is used as part of the error message.
@@ -161923,7 +188205,7 @@ static void fts3SnippetFunc(
assert( nVal>=1 );
if( nVal>6 ){
- sqlite3_result_error(pContext,
+ sqlite3_result_error(pContext,
"wrong number of arguments to function snippet()", -1);
return;
}
@@ -161931,9 +188213,13 @@ static void fts3SnippetFunc(
switch( nVal ){
case 6: nToken = sqlite3_value_int(apVal[5]);
+ /* no break */ deliberate_fall_through
case 5: iCol = sqlite3_value_int(apVal[4]);
+ /* no break */ deliberate_fall_through
case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]);
+ /* no break */ deliberate_fall_through
case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]);
+ /* no break */ deliberate_fall_through
case 2: zStart = (const char*)sqlite3_value_text(apVal[1]);
}
if( !zEllipsis || !zEnd || !zStart ){
@@ -161965,8 +188251,8 @@ static void fts3OffsetsFunc(
}
}
-/*
-** Implementation of the special optimize() function for FTS3. This
+/*
+** Implementation of the special optimize() function for FTS3. This
** function merges all segments in the database to a single segment.
** Example usage is:
**
@@ -162075,10 +188361,10 @@ static int fts3RenameMethod(
/* At this point it must be known if the %_stat table exists or not.
** So bHasStat may not be 2. */
rc = fts3SetHasStat(p);
-
+
/* As it happens, the pending terms table is always empty here. This is
- ** because an "ALTER TABLE RENAME TABLE" statement inside a transaction
- ** always opens a savepoint transaction. And the xSavepoint() method
+ ** because an "ALTER TABLE RENAME TABLE" statement inside a transaction
+ ** always opens a savepoint transaction. And the xSavepoint() method
** flushes the pending terms table. But leave the (no-op) call to
** PendingTermsFlush() in in case that changes.
*/
@@ -162087,6 +188373,8 @@ static int fts3RenameMethod(
rc = sqlite3Fts3PendingTermsFlush(p);
}
+ p->bIgnoreSavepoint = 1;
+
if( p->zContentTbl==0 ){
fts3DbExec(&rc, db,
"ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
@@ -162114,6 +188402,8 @@ static int fts3RenameMethod(
"ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';",
p->zDb, p->zName, zName
);
+
+ p->bIgnoreSavepoint = 0;
return rc;
}
@@ -162124,12 +188414,28 @@ static int fts3RenameMethod(
*/
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
int rc = SQLITE_OK;
- UNUSED_PARAMETER(iSavepoint);
- assert( ((Fts3Table *)pVtab)->inTransaction );
- assert( ((Fts3Table *)pVtab)->mxSavepoint <= iSavepoint );
- TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
- if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){
- rc = fts3SyncMethod(pVtab);
+ Fts3Table *pTab = (Fts3Table*)pVtab;
+ assert( pTab->inTransaction );
+ assert( pTab->mxSavepoint<=iSavepoint );
+ TESTONLY( pTab->mxSavepoint = iSavepoint );
+
+ if( pTab->bIgnoreSavepoint==0 ){
+ if( fts3HashCount(&pTab->aIndex[0].hPending)>0 ){
+ char *zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')",
+ pTab->zDb, pTab->zName, pTab->zName
+ );
+ if( zSql ){
+ pTab->bIgnoreSavepoint = 1;
+ rc = sqlite3_exec(pTab->db, zSql, 0, 0, 0);
+ pTab->bIgnoreSavepoint = 0;
+ sqlite3_free(zSql);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ pTab->iSavepoint = iSavepoint+1;
+ }
}
return rc;
}
@@ -162140,12 +188446,11 @@ static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
** This is a no-op.
*/
static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
- TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
- UNUSED_PARAMETER(iSavepoint);
- UNUSED_PARAMETER(pVtab);
- assert( p->inTransaction );
- assert( p->mxSavepoint >= iSavepoint );
- TESTONLY( p->mxSavepoint = iSavepoint-1 );
+ Fts3Table *pTab = (Fts3Table*)pVtab;
+ assert( pTab->inTransaction );
+ assert( pTab->mxSavepoint >= iSavepoint );
+ TESTONLY( pTab->mxSavepoint = iSavepoint-1 );
+ pTab->iSavepoint = iSavepoint;
return SQLITE_OK;
}
@@ -162155,12 +188460,13 @@ static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
** Discard the contents of the pending terms table.
*/
static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
- Fts3Table *p = (Fts3Table*)pVtab;
+ Fts3Table *pTab = (Fts3Table*)pVtab;
UNUSED_PARAMETER(iSavepoint);
- assert( p->inTransaction );
- assert( p->mxSavepoint >= iSavepoint );
- TESTONLY( p->mxSavepoint = iSavepoint );
- sqlite3Fts3PendingTermsClear(p);
+ assert( pTab->inTransaction );
+ TESTONLY( pTab->mxSavepoint = iSavepoint );
+ if( (iSavepoint+1)<=pTab->iSavepoint ){
+ sqlite3Fts3PendingTermsClear(pTab);
+ }
return SQLITE_OK;
}
@@ -162170,7 +188476,7 @@ static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
*/
static int fts3ShadowName(const char *zName){
static const char *azName[] = {
- "content", "docsize", "segdir", "segments", "stat",
+ "content", "docsize", "segdir", "segments", "stat",
};
unsigned int i;
for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
@@ -162179,8 +188485,40 @@ static int fts3ShadowName(const char *zName){
return 0;
}
+/*
+** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual
+** table.
+*/
+static int fts3IntegrityMethod(
+ sqlite3_vtab *pVtab, /* The virtual table to be checked */
+ const char *zSchema, /* Name of schema in which pVtab lives */
+ const char *zTabname, /* Name of the pVTab table */
+ int isQuick, /* True if this is a quick_check */
+ char **pzErr /* Write error message here */
+){
+ Fts3Table *p = (Fts3Table*)pVtab;
+ int rc;
+ int bOk = 0;
+
+ UNUSED_PARAMETER(isQuick);
+ rc = sqlite3Fts3IntegrityCheck(p, &bOk);
+ assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 );
+ if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){
+ *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
+ " FTS%d table %s.%s: %s",
+ p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc));
+ }else if( bOk==0 ){
+ *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
+ p->bFts4 ? 4 : 3, zSchema, zTabname);
+ }
+ sqlite3Fts3SegmentsClose(p);
+ return SQLITE_OK;
+}
+
+
+
static const sqlite3_module fts3Module = {
- /* iVersion */ 3,
+ /* iVersion */ 4,
/* xCreate */ fts3CreateMethod,
/* xConnect */ fts3ConnectMethod,
/* xBestIndex */ fts3BestIndexMethod,
@@ -162204,6 +188542,7 @@ static const sqlite3_module fts3Module = {
/* xRelease */ fts3ReleaseMethod,
/* xRollbackTo */ fts3RollbackToMethod,
/* xShadowName */ fts3ShadowName,
+ /* xIntegrity */ fts3IntegrityMethod,
};
/*
@@ -162212,13 +188551,16 @@ static const sqlite3_module fts3Module = {
** allocated for the tokenizer hash table.
*/
static void hashDestroy(void *p){
- Fts3Hash *pHash = (Fts3Hash *)p;
- sqlite3Fts3HashClear(pHash);
- sqlite3_free(pHash);
+ Fts3HashWrapper *pHash = (Fts3HashWrapper *)p;
+ pHash->nRef--;
+ if( pHash->nRef<=0 ){
+ sqlite3Fts3HashClear(&pHash->hash);
+ sqlite3_free(pHash);
+ }
}
/*
-** The fts3 built-in tokenizers - "simple", "porter" and "icu"- are
+** The fts3 built-in tokenizers - "simple", "porter" and "icu"- are
** implemented in files fts3_tokenizer1.c, fts3_porter.c and fts3_icu.c
** respectively. The following three forward declarations are for functions
** declared in these files used to retrieve the respective implementations.
@@ -162244,7 +188586,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const
*/
SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
int rc = SQLITE_OK;
- Fts3Hash *pHash = 0;
+ Fts3HashWrapper *pHash = 0;
const sqlite3_tokenizer_module *pSimple = 0;
const sqlite3_tokenizer_module *pPorter = 0;
#ifndef SQLITE_DISABLE_FTS3_UNICODE
@@ -162272,23 +188614,24 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
sqlite3Fts3PorterTokenizerModule(&pPorter);
/* Allocate and initialize the hash-table used to store tokenizers. */
- pHash = sqlite3_malloc(sizeof(Fts3Hash));
+ pHash = sqlite3_malloc(sizeof(Fts3HashWrapper));
if( !pHash ){
rc = SQLITE_NOMEM;
}else{
- sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1);
+ sqlite3Fts3HashInit(&pHash->hash, FTS3_HASH_STRING, 1);
+ pHash->nRef = 0;
}
/* Load the built-in tokenizers into the hash table */
if( rc==SQLITE_OK ){
- if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple)
- || sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter)
+ if( sqlite3Fts3HashInsert(&pHash->hash, "simple", 7, (void *)pSimple)
+ || sqlite3Fts3HashInsert(&pHash->hash, "porter", 7, (void *)pPorter)
#ifndef SQLITE_DISABLE_FTS3_UNICODE
- || sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode)
+ || sqlite3Fts3HashInsert(&pHash->hash, "unicode61", 10, (void *)pUnicode)
#endif
#ifdef SQLITE_ENABLE_ICU
- || (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu))
+ || (pIcu && sqlite3Fts3HashInsert(&pHash->hash, "icu", 4, (void *)pIcu))
#endif
){
rc = SQLITE_NOMEM;
@@ -162297,32 +188640,35 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
#ifdef SQLITE_TEST
if( rc==SQLITE_OK ){
- rc = sqlite3Fts3ExprInitTestInterface(db, pHash);
+ rc = sqlite3Fts3ExprInitTestInterface(db, &pHash->hash);
}
#endif
- /* Create the virtual table wrapper around the hash-table and overload
+ /* Create the virtual table wrapper around the hash-table and overload
** the four scalar functions. If this is successful, register the
** module with sqlite.
*/
- if( SQLITE_OK==rc
- && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer"))
+ if( SQLITE_OK==rc
+ && SQLITE_OK==(rc=sqlite3Fts3InitHashTable(db,&pHash->hash,"fts3_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, "matchinfo", 1))
&& SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 2))
&& SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", 1))
){
+ pHash->nRef++;
rc = sqlite3_create_module_v2(
db, "fts3", &fts3Module, (void *)pHash, hashDestroy
);
if( rc==SQLITE_OK ){
+ pHash->nRef++;
rc = sqlite3_create_module_v2(
- db, "fts4", &fts3Module, (void *)pHash, 0
+ db, "fts4", &fts3Module, (void *)pHash, hashDestroy
);
}
if( rc==SQLITE_OK ){
- rc = sqlite3Fts3InitTok(db, (void *)pHash);
+ pHash->nRef++;
+ rc = sqlite3Fts3InitTok(db, (void *)pHash, hashDestroy);
}
return rc;
}
@@ -162331,7 +188677,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
/* An error has occurred. Delete the hash table and return the error code. */
assert( rc!=SQLITE_OK );
if( pHash ){
- sqlite3Fts3HashClear(pHash);
+ sqlite3Fts3HashClear(&pHash->hash);
sqlite3_free(pHash);
}
return rc;
@@ -162339,7 +188685,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
/*
** Allocate an Fts3MultiSegReader for each token in the expression headed
-** by pExpr.
+** by pExpr.
**
** An Fts3SegReader object is a cursor that can seek or scan a range of
** entries within a single segment b-tree. An Fts3MultiSegReader uses multiple
@@ -162349,7 +188695,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
** If the allocated Fts3MultiSegReader just seeks to a single entry in a
** segment b-tree (if the term is not a prefix or it is a prefix for which
** there exists prefix b-tree of the right length) then it may be traversed
-** and merged incrementally. Otherwise, it has to be merged into an in-memory
+** and merged incrementally. Otherwise, it has to be merged into an in-memory
** doclist and then traversed.
*/
static void fts3EvalAllocateReaders(
@@ -162366,7 +188712,7 @@ static void fts3EvalAllocateReaders(
*pnToken += nToken;
for(i=0; i<nToken; i++){
Fts3PhraseToken *pToken = &pExpr->pPhrase->aToken[i];
- int rc = fts3TermSegReaderCursor(pCsr,
+ int rc = fts3TermSegReaderCursor(pCsr,
pToken->z, pToken->n, pToken->isPrefix, &pToken->pSegcsr
);
if( rc!=SQLITE_OK ){
@@ -162500,8 +188846,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
char *aPoslist = 0; /* Position list for deferred tokens */
int nPoslist = 0; /* Number of bytes in aPoslist */
int iPrev = -1; /* Token number of previous deferred token */
-
- assert( pPhrase->doclist.bFreeList==0 );
+ char *aFree = (pPhrase->doclist.bFreeList ? pPhrase->doclist.pList : 0);
for(iToken=0; iToken<pPhrase->nToken; iToken++){
Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
@@ -162515,6 +188860,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
if( pList==0 ){
sqlite3_free(aPoslist);
+ sqlite3_free(aFree);
pPhrase->doclist.pList = 0;
pPhrase->doclist.nList = 0;
return SQLITE_OK;
@@ -162535,6 +188881,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
nPoslist = (int)(aOut - aPoslist);
if( nPoslist==0 ){
sqlite3_free(aPoslist);
+ sqlite3_free(aFree);
pPhrase->doclist.pList = 0;
pPhrase->doclist.nList = 0;
return SQLITE_OK;
@@ -162567,13 +188914,14 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
nDistance = iPrev - nMaxUndeferred;
}
- aOut = (char *)sqlite3_malloc(nPoslist+8);
+ aOut = (char *)sqlite3Fts3MallocZero(nPoslist+FTS3_BUFFER_PADDING);
if( !aOut ){
sqlite3_free(aPoslist);
return SQLITE_NOMEM;
}
-
+
pPhrase->doclist.pList = aOut;
+ assert( p1 && p2 );
if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){
pPhrase->doclist.bFreeList = 1;
pPhrase->doclist.nList = (int)(aOut - pPhrase->doclist.pList);
@@ -162586,6 +188934,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
}
}
+ if( pPhrase->doclist.pList!=aFree ) sqlite3_free(aFree);
return SQLITE_OK;
}
#endif /* SQLITE_DISABLE_FTS4_DEFERRED */
@@ -162597,7 +188946,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
#define MAX_INCR_PHRASE_TOKENS 4
/*
-** This function is called for each Fts3Phrase in a full-text query
+** This function is called for each Fts3Phrase in a full-text query
** expression to initialize the mechanism for returning rows. Once this
** function has been called successfully on an Fts3Phrase, it may be
** used with fts3EvalPhraseNext() to iterate through the matching docids.
@@ -162615,14 +188964,14 @@ static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
/* Determine if doclists may be loaded from disk incrementally. This is
** possible if the bOptOk argument is true, the FTS doclists will be
- ** scanned in forward order, and the phrase consists of
+ ** scanned in forward order, and the phrase consists of
** MAX_INCR_PHRASE_TOKENS or fewer tokens, none of which are are "^first"
** tokens or prefix tokens that cannot use a prefix-index. */
int bHaveIncr = 0;
- int bIncrOk = (bOptOk
- && pCsr->bDesc==pTab->bDescIdx
+ int bIncrOk = (bOptOk
+ && pCsr->bDesc==pTab->bDescIdx
&& p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
-#ifdef SQLITE_TEST
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
&& pTab->bNoIncrDoclist==0
#endif
);
@@ -162656,12 +189005,12 @@ static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
}
/*
-** This function is used to iterate backwards (from the end to start)
+** This function is used to iterate backwards (from the end to start)
** through doclists. It is used by this module to iterate through phrase
** doclists in reverse and by the fts3_write.c module to iterate through
** pending-terms lists when writing to databases with "order=desc".
**
-** The doclist may be sorted in ascending (parameter bDescIdx==0) or
+** The doclist may be sorted in ascending (parameter bDescIdx==0) or
** descending (parameter bDescIdx==1) order of docid. Regardless, this
** function iterates from the end of the doclist to the beginning.
*/
@@ -162678,7 +189027,7 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(
assert( nDoclist>0 );
assert( *pbEof==0 );
- assert( p || *piDocid==0 );
+ assert_fts3_nc( p || *piDocid==0 );
assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) );
if( p==0 ){
@@ -162733,7 +189082,7 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistNext(
assert( nDoclist>0 );
assert( *pbEof==0 );
- assert( p || *piDocid==0 );
+ assert_fts3_nc( p || *piDocid==0 );
assert( !p || (p>=aDoclist && p<=&aDoclist[nDoclist]) );
if( p==0 ){
@@ -162741,7 +189090,7 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistNext(
p += sqlite3Fts3GetVarint(p, piDocid);
}else{
fts3PoslistCopy(0, &p);
- while( p<&aDoclist[nDoclist] && *p==0 ) p++;
+ while( p<&aDoclist[nDoclist] && *p==0 ) p++;
if( p>=&aDoclist[nDoclist] ){
*pbEof = 1;
}else{
@@ -162764,15 +189113,16 @@ static void fts3EvalDlPhraseNext(
u8 *pbEof
){
char *pIter; /* Used to iterate through aAll */
- char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
-
+ char *pEnd; /* 1 byte past end of aAll */
+
if( pDL->pNextDocid ){
pIter = pDL->pNextDocid;
+ assert( pDL->aAll!=0 || pIter==0 );
}else{
pIter = pDL->aAll;
}
- if( pIter>=pEnd ){
+ if( pIter==0 || pIter>=(pEnd = pDL->aAll + pDL->nAll) ){
/* We have already reached the end of this doclist. EOF. */
*pbEof = 1;
}else{
@@ -162813,12 +189163,12 @@ struct TokenDoclist {
};
/*
-** Token pToken is an incrementally loaded token that is part of a
+** Token pToken is an incrementally loaded token that is part of a
** multi-token phrase. Advance it to the next matching document in the
** database and populate output variable *p with the details of the new
** entry. Or, if the iterator has reached EOF, set *pbEof to true.
**
-** If an error occurs, return an SQLite error code. Otherwise, return
+** If an error occurs, return an SQLite error code. Otherwise, return
** SQLITE_OK.
*/
static int incrPhraseTokenNext(
@@ -162859,18 +189209,18 @@ static int incrPhraseTokenNext(
/*
** The phrase iterator passed as the second argument:
**
-** * features at least one token that uses an incremental doclist, and
+** * features at least one token that uses an incremental doclist, and
**
** * does not contain any deferred tokens.
**
** Advance it to the next matching documnent in the database and populate
-** the Fts3Doclist.pList and nList fields.
+** the Fts3Doclist.pList and nList fields.
**
** If there is no "next" entry and no error occurs, then *pbEof is set to
** 1 before returning. Otherwise, if no error occurs and the iterator is
** successfully advanced, *pbEof is set to 0.
**
-** If an error occurs, return an SQLite error code. Otherwise, return
+** If an error occurs, return an SQLite error code. Otherwise, return
** SQLITE_OK.
*/
static int fts3EvalIncrPhraseNext(
@@ -162888,7 +189238,7 @@ static int fts3EvalIncrPhraseNext(
assert( p->bIncr==1 );
if( p->nToken==1 ){
- rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
+ rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
&pDL->iDocid, &pDL->pList, &pDL->nList
);
if( pDL->pList==0 ) bEof = 1;
@@ -162918,8 +189268,8 @@ static int fts3EvalIncrPhraseNext(
/* Keep advancing iterators until they all point to the same document */
for(i=0; i<p->nToken; i++){
- while( rc==SQLITE_OK && bEof==0
- && a[i].bIgnore==0 && DOCID_CMP(a[i].iDocid, iMax)<0
+ while( rc==SQLITE_OK && bEof==0
+ && a[i].bIgnore==0 && DOCID_CMP(a[i].iDocid, iMax)<0
){
rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof);
if( DOCID_CMP(a[i].iDocid, iMax)>0 ){
@@ -162933,9 +189283,10 @@ static int fts3EvalIncrPhraseNext(
if( bEof==0 ){
int nList = 0;
int nByte = a[p->nToken-1].nList;
- char *aDoclist = sqlite3_malloc(nByte+1);
+ char *aDoclist = sqlite3_malloc64((i64)nByte+FTS3_BUFFER_PADDING);
if( !aDoclist ) return SQLITE_NOMEM;
memcpy(aDoclist, a[p->nToken-1].pList, nByte+1);
+ memset(&aDoclist[nByte], 0, FTS3_BUFFER_PADDING);
for(i=0; i<(p->nToken-1); i++){
if( a[i].bIgnore==0 ){
@@ -162965,8 +189316,8 @@ static int fts3EvalIncrPhraseNext(
}
/*
-** Attempt to move the phrase iterator to point to the next matching docid.
-** If an error occurs, return an SQLite error code. Otherwise, return
+** Attempt to move the phrase iterator to point to the next matching docid.
+** If an error occurs, return an SQLite error code. Otherwise, return
** SQLITE_OK.
**
** If there is no "next" entry and no error occurs, then *pbEof is set to
@@ -162985,7 +189336,7 @@ static int fts3EvalPhraseNext(
if( p->bIncr ){
rc = fts3EvalIncrPhraseNext(pCsr, p, pbEof);
}else if( pCsr->bDesc!=pTab->bDescIdx && pDL->nAll ){
- sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll,
+ sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll,
&pDL->pNextDocid, &pDL->iDocid, &pDL->nList, pbEof
);
pDL->pList = pDL->pNextDocid;
@@ -163045,7 +189396,7 @@ static void fts3EvalStartReaders(
** Tokens are divided into AND/NEAR clusters. All tokens in a cluster belong
** to phrases that are connected only by AND and NEAR operators (not OR or
** NOT). When determining tokens to defer, each AND/NEAR cluster is considered
-** separately. The root of a tokens AND/NEAR cluster is stored in
+** separately. The root of a tokens AND/NEAR cluster is stored in
** Fts3TokenAndCost.pRoot.
*/
typedef struct Fts3TokenAndCost Fts3TokenAndCost;
@@ -163113,7 +189464,7 @@ static void fts3EvalTokenCosts(
** write this value to *pnPage and return SQLITE_OK. Otherwise, return
** an SQLite error code.
**
-** The average document size in pages is calculated by first calculating
+** The average document size in pages is calculated by first calculating
** determining the average size in bytes, B. If B is less than the amount
** of data that will fit on a single leaf page of an intkey table in
** this database, then the average docsize is 1. Otherwise, it is 1 plus
@@ -163123,10 +189474,10 @@ static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
int rc = SQLITE_OK;
if( pCsr->nRowAvg==0 ){
/* The average document size, which is required to calculate the cost
- ** of each doclist, has not yet been determined. Read the required
+ ** of each doclist, has not yet been determined. Read the required
** data from the %_stat table to calculate it.
**
- ** Entry 0 of the %_stat table is a blob containing (nCol+1) FTS3
+ ** Entry 0 of the %_stat table is a blob containing (nCol+1) FTS3
** varints, where nCol is the number of columns in the FTS3 table.
** The first varint is the number of documents currently stored in
** the table. The following nCol varints contain the total amount of
@@ -163143,12 +189494,13 @@ static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
rc = sqlite3Fts3SelectDoctotal(p, &pStmt);
if( rc!=SQLITE_OK ) return rc;
a = sqlite3_column_blob(pStmt, 0);
- assert( a );
-
- pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
- a += sqlite3Fts3GetVarint(a, &nDoc);
- while( a<pEnd ){
- a += sqlite3Fts3GetVarint(a, &nByte);
+ testcase( a==0 ); /* If %_stat.value set to X'' */
+ if( a ){
+ pEnd = &a[sqlite3_column_bytes(pStmt, 0)];
+ a += sqlite3Fts3GetVarintBounded(a, pEnd, &nDoc);
+ while( a<pEnd ){
+ a += sqlite3Fts3GetVarintBounded(a, pEnd, &nByte);
+ }
}
if( nDoc==0 || nByte==0 ){
sqlite3_reset(pStmt);
@@ -163157,7 +189509,7 @@ static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
pCsr->nDoc = nDoc;
pCsr->nRowAvg = (int)(((nByte / nDoc) + p->nPgsz) / p->nPgsz);
- assert( pCsr->nRowAvg>0 );
+ assert( pCsr->nRowAvg>0 );
rc = sqlite3_reset(pStmt);
}
@@ -163166,11 +189518,11 @@ static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
}
/*
-** This function is called to select the tokens (if any) that will be
+** This function is called to select the tokens (if any) that will be
** deferred. The array aTC[] has already been populated when this is
** called.
**
-** This function is called once for each AND/NEAR cluster in the
+** This function is called once for each AND/NEAR cluster in the
** expression. Each invocation determines which tokens to defer within
** the cluster with root node pRoot. See comments above the definition
** of struct Fts3TokenAndCost for more details.
@@ -163220,8 +189572,8 @@ static int fts3EvalSelectDeferred(
assert( rc!=SQLITE_OK || nDocSize>0 );
- /* Iterate through all tokens in this AND/NEAR cluster, in ascending order
- ** of the number of overflow pages that will be loaded by the pager layer
+ /* Iterate through all tokens in this AND/NEAR cluster, in ascending order
+ ** of the number of overflow pages that will be loaded by the pager layer
** to retrieve the entire doclist for the token from the full-text index.
** Load the doclists for tokens that are either:
**
@@ -163232,7 +189584,7 @@ static int fts3EvalSelectDeferred(
**
** After each token doclist is loaded, merge it with the others from the
** same phrase and count the number of documents that the merged doclist
- ** contains. Set variable "nMinEst" to the smallest number of documents in
+ ** contains. Set variable "nMinEst" to the smallest number of documents in
** any phrase doclist for which 1 or more token doclists have been loaded.
** Let nOther be the number of other phrases for which it is certain that
** one or more tokens will not be deferred.
@@ -163248,8 +189600,8 @@ static int fts3EvalSelectDeferred(
/* Set pTC to point to the cheapest remaining token. */
for(iTC=0; iTC<nTC; iTC++){
- if( aTC[iTC].pToken && aTC[iTC].pRoot==pRoot
- && (!pTC || aTC[iTC].nOvfl<pTC->nOvfl)
+ if( aTC[iTC].pToken && aTC[iTC].pRoot==pRoot
+ && (!pTC || aTC[iTC].nOvfl<pTC->nOvfl)
){
pTC = &aTC[iTC];
}
@@ -163258,7 +189610,7 @@ static int fts3EvalSelectDeferred(
if( ii && pTC->nOvfl>=((nMinEst+(nLoad4/4)-1)/(nLoad4/4))*nDocSize ){
/* The number of overflow pages to load for this (and therefore all
- ** subsequent) tokens is greater than the estimated number of pages
+ ** subsequent) tokens is greater than the estimated number of pages
** that will be loaded if all subsequent tokens are deferred.
*/
Fts3PhraseToken *pToken = pTC->pToken;
@@ -163267,7 +189619,7 @@ static int fts3EvalSelectDeferred(
pToken->pSegcsr = 0;
}else{
/* Set nLoad4 to the value of (4^nOther) for the next iteration of the
- ** for-loop. Except, limit the value to 2^24 to prevent it from
+ ** for-loop. Except, limit the value to 2^24 to prevent it from
** overflowing the 32-bit integer it is stored in. */
if( ii<12 ) nLoad4 = nLoad4*4;
@@ -163325,16 +189677,15 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
#ifndef SQLITE_DISABLE_FTS4_DEFERRED
if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){
Fts3TokenAndCost *aTC;
- Fts3Expr **apOr;
- aTC = (Fts3TokenAndCost *)sqlite3_malloc(
+ aTC = (Fts3TokenAndCost *)sqlite3_malloc64(
sizeof(Fts3TokenAndCost) * nToken
+ sizeof(Fts3Expr *) * nOr * 2
);
- apOr = (Fts3Expr **)&aTC[nToken];
if( !aTC ){
rc = SQLITE_NOMEM;
}else{
+ Fts3Expr **apOr = (Fts3Expr **)&aTC[nToken];
int ii;
Fts3TokenAndCost *pTC = aTC;
Fts3Expr **ppOr = apOr;
@@ -163380,7 +189731,7 @@ static void fts3EvalInvalidatePoslist(Fts3Phrase *pPhrase){
**
** Parameter nNear is passed the NEAR distance of the expression (5 in
** the example above). When this function is called, *paPoslist points to
-** the position list, and *pnToken is the number of phrase tokens in, the
+** the position list, and *pnToken is the number of phrase tokens in the
** phrase on the other side of the NEAR operator to pPhrase. For example,
** if pPhrase refers to the "def ghi" phrase, then *paPoslist points to
** the position list associated with phrase "abc".
@@ -163389,7 +189740,7 @@ static void fts3EvalInvalidatePoslist(Fts3Phrase *pPhrase){
** close to a position in the *paPoslist position list are removed. If this
** leaves 0 positions, zero is returned. Otherwise, non-zero.
**
-** Before returning, *paPoslist is set to point to the position lsit
+** Before returning, *paPoslist is set to point to the position lsit
** associated with pPhrase. And *pnToken is set to the number of tokens in
** pPhrase.
*/
@@ -163403,8 +189754,8 @@ static int fts3EvalNearTrim(
int nParam1 = nNear + pPhrase->nToken;
int nParam2 = nNear + *pnToken;
int nNew;
- char *p2;
- char *pOut;
+ char *p2;
+ char *pOut;
int res;
assert( pPhrase->doclist.pList );
@@ -163415,10 +189766,12 @@ static int fts3EvalNearTrim(
);
if( res ){
nNew = (int)(pOut - pPhrase->doclist.pList) - 1;
- assert( pPhrase->doclist.pList[nNew]=='\0' );
- assert( nNew<=pPhrase->doclist.nList && nNew>0 );
- memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
- pPhrase->doclist.nList = nNew;
+ assert_fts3_nc( nNew<=pPhrase->doclist.nList && nNew>0 );
+ if( nNew>=0 && nNew<=pPhrase->doclist.nList ){
+ assert( pPhrase->doclist.pList[nNew]=='\0' );
+ memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
+ pPhrase->doclist.nList = nNew;
+ }
*paPoslist = pPhrase->doclist.pList;
*pnToken = pPhrase->nToken;
}
@@ -163451,19 +189804,19 @@ static int fts3EvalNearTrim(
**
** 1. Deferred tokens are not taken into account. If a phrase consists
** entirely of deferred tokens, it is assumed to match every row in
-** the db. In this case the position-list is not populated at all.
+** the db. In this case the position-list is not populated at all.
**
** Or, if a phrase contains one or more deferred tokens and one or
-** more non-deferred tokens, then the expression is advanced to the
+** more non-deferred tokens, then the expression is advanced to the
** next possible match, considering only non-deferred tokens. In other
** words, if the phrase is "A B C", and "B" is deferred, the expression
-** is advanced to the next row that contains an instance of "A * C",
+** is advanced to the next row that contains an instance of "A * C",
** where "*" may match any single token. The position list in this case
** is populated as for "A * C" before returning.
**
-** 2. NEAR is treated as AND. If the expression is "x NEAR y", it is
+** 2. NEAR is treated as AND. If the expression is "x NEAR y", it is
** advanced to point to the next row that matches "x AND y".
-**
+**
** See sqlite3Fts3EvalTestDeferred() for details on testing if a row is
** really a match, taking into account deferred tokens and NEAR operators.
*/
@@ -163472,9 +189825,8 @@ static void fts3EvalNextRow(
Fts3Expr *pExpr, /* Expr. to advance to next matching row */
int *pRc /* IN/OUT: Error code */
){
- if( *pRc==SQLITE_OK ){
+ if( *pRc==SQLITE_OK && pExpr->bEof==0 ){
int bDescDoclist = pCsr->bDesc; /* Used by DOCID_CMP() macro */
- assert( pExpr->bEof==0 );
pExpr->bStart = 1;
switch( pExpr->eType ){
@@ -163527,18 +189879,19 @@ static void fts3EvalNextRow(
fts3EvalNextRow(pCsr, pLeft, pRc);
}
}
+ pRight->bEof = pLeft->bEof = 1;
}
}
break;
}
-
+
case FTSQUERY_OR: {
Fts3Expr *pLeft = pExpr->pLeft;
Fts3Expr *pRight = pExpr->pRight;
sqlite3_int64 iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid);
- assert( pLeft->bStart || pLeft->iDocid==pRight->iDocid );
- assert( pRight->bStart || pLeft->iDocid==pRight->iDocid );
+ assert_fts3_nc( pLeft->bStart || pLeft->iDocid==pRight->iDocid );
+ assert_fts3_nc( pRight->bStart || pLeft->iDocid==pRight->iDocid );
if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){
fts3EvalNextRow(pCsr, pLeft, pRc);
@@ -163571,9 +189924,9 @@ static void fts3EvalNextRow(
fts3EvalNextRow(pCsr, pLeft, pRc);
if( pLeft->bEof==0 ){
- while( !*pRc
- && !pRight->bEof
- && DOCID_CMP(pLeft->iDocid, pRight->iDocid)>0
+ while( !*pRc
+ && !pRight->bEof
+ && DOCID_CMP(pLeft->iDocid, pRight->iDocid)>0
){
fts3EvalNextRow(pCsr, pRight, pRc);
}
@@ -163598,14 +189951,14 @@ static void fts3EvalNextRow(
** If *pRc is not SQLITE_OK, or if pExpr is not the root node of a NEAR
** cluster, then this function returns 1 immediately.
**
-** Otherwise, it checks if the current row really does match the NEAR
-** expression, using the data currently stored in the position lists
-** (Fts3Expr->pPhrase.doclist.pList/nList) for each phrase in the expression.
+** Otherwise, it checks if the current row really does match the NEAR
+** expression, using the data currently stored in the position lists
+** (Fts3Expr->pPhrase.doclist.pList/nList) for each phrase in the expression.
**
** If the current row is a match, the position list associated with each
** phrase in the NEAR expression is edited in place to contain only those
** phrase instances sufficiently close to their peers to satisfy all NEAR
-** constraints. In this case it returns 1. If the NEAR expression does not
+** constraints. In this case it returns 1. If the NEAR expression does not
** match the current row, 0 is returned. The position lists may or may not
** be edited if 0 is returned.
*/
@@ -163628,16 +189981,16 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){
** | |
** "w" "x"
**
- ** The right-hand child of a NEAR node is always a phrase. The
+ ** The right-hand child of a NEAR node is always a phrase. The
** left-hand child may be either a phrase or a NEAR node. There are
** no exceptions to this - it's the way the parser in fts3_expr.c works.
*/
- if( *pRc==SQLITE_OK
- && pExpr->eType==FTSQUERY_NEAR
+ if( *pRc==SQLITE_OK
+ && pExpr->eType==FTSQUERY_NEAR
&& (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR)
){
- Fts3Expr *p;
- int nTmp = 0; /* Bytes of temp space */
+ Fts3Expr *p;
+ sqlite3_int64 nTmp = 0; /* Bytes of temp space */
char *aTmp; /* Temp space for PoslistNearMerge() */
/* Allocate temporary working space. */
@@ -163646,7 +189999,7 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){
nTmp += p->pRight->pPhrase->doclist.nList;
}
nTmp += p->pPhrase->doclist.nList;
- aTmp = sqlite3_malloc(nTmp*2);
+ aTmp = sqlite3_malloc64(nTmp*2);
if( !aTmp ){
*pRc = SQLITE_NOMEM;
res = 0;
@@ -163683,12 +190036,12 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){
/*
** This function is a helper function for sqlite3Fts3EvalTestDeferred().
** Assuming no error occurs or has occurred, It returns non-zero if the
-** expression passed as the second argument matches the row that pCsr
+** expression passed as the second argument matches the row that pCsr
** currently points to, or zero if it does not.
**
** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
-** If an error occurs during execution of this function, *pRc is set to
-** the appropriate SQLite error code. In this case the returned value is
+** If an error occurs during execution of this function, *pRc is set to
+** the appropriate SQLite error code. In this case the returned value is
** undefined.
*/
static int fts3EvalTestExpr(
@@ -163707,10 +190060,10 @@ static int fts3EvalTestExpr(
&& fts3EvalNearTest(pExpr, pRc)
);
- /* If the NEAR expression does not match any rows, zero the doclist for
+ /* If the NEAR expression does not match any rows, zero the doclist for
** all phrases involved in the NEAR. This is because the snippet(),
- ** offsets() and matchinfo() functions are not supposed to recognize
- ** any instances of phrases that are part of unmatched NEAR queries.
+ ** offsets() and matchinfo() functions are not supposed to recognize
+ ** any instances of phrases that are part of unmatched NEAR queries.
** For example if this expression:
**
** ... MATCH 'a OR (b NEAR c)'
@@ -163722,8 +190075,8 @@ static int fts3EvalTestExpr(
** then any snippet() should ony highlight the "a" term, not the "b"
** (as "b" is part of a non-matching NEAR clause).
*/
- if( bHit==0
- && pExpr->eType==FTSQUERY_NEAR
+ if( bHit==0
+ && pExpr->eType==FTSQUERY_NEAR
&& (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR)
){
Fts3Expr *p;
@@ -163755,11 +190108,10 @@ static int fts3EvalTestExpr(
default: {
#ifndef SQLITE_DISABLE_FTS4_DEFERRED
- if( pCsr->pDeferred
- && (pExpr->iDocid==pCsr->iPrevId || pExpr->bDeferred)
- ){
+ if( pCsr->pDeferred && (pExpr->bDeferred || (
+ pExpr->iDocid==pCsr->iPrevId && pExpr->pPhrase->doclist.pList
+ ))){
Fts3Phrase *pPhrase = pExpr->pPhrase;
- assert( pExpr->bDeferred || pPhrase->doclist.bFreeList==0 );
if( pExpr->bDeferred ){
fts3EvalInvalidatePoslist(pPhrase);
}
@@ -163769,7 +190121,10 @@ static int fts3EvalTestExpr(
}else
#endif
{
- bHit = (pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId);
+ bHit = (
+ pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId
+ && pExpr->pPhrase->doclist.nList>0
+ );
}
break;
}
@@ -163811,7 +190166,7 @@ SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc){
** memory and scan it to determine the position list for each deferred
** token. Then, see if this row is really a match, considering deferred
** tokens and NEAR operators (neither of which were taken into account
- ** earlier, by fts3EvalNextRow()).
+ ** earlier, by fts3EvalNextRow()).
*/
if( pCsr->pDeferred ){
rc = fts3CursorSeek(0, pCsr);
@@ -163866,7 +190221,7 @@ static int fts3EvalNext(Fts3Cursor *pCsr){
/*
** Restart interation for expression pExpr so that the next call to
-** fts3EvalNext() visits the first row. Do not allow incremental
+** fts3EvalNext() visits the first row. Do not allow incremental
** loading or merging of phrase doclists for this iteration.
**
** If *pRc is other than SQLITE_OK when this function is called, it is
@@ -163909,22 +190264,21 @@ static void fts3EvalRestart(
}
/*
-** After allocating the Fts3Expr.aMI[] array for each phrase in the
+** After allocating the Fts3Expr.aMI[] array for each phrase in the
** expression rooted at pExpr, the cursor iterates through all rows matched
** by pExpr, calling this function for each row. This function increments
** the values in Fts3Expr.aMI[] according to the position-list currently
-** found in Fts3Expr.pPhrase->doclist.pList for each of the phrase
+** found in Fts3Expr.pPhrase->doclist.pList for each of the phrase
** expression nodes.
*/
-static void fts3EvalUpdateCounts(Fts3Expr *pExpr){
+static void fts3EvalUpdateCounts(Fts3Expr *pExpr, int nCol){
if( pExpr ){
Fts3Phrase *pPhrase = pExpr->pPhrase;
if( pPhrase && pPhrase->doclist.pList ){
int iCol = 0;
char *p = pPhrase->doclist.pList;
- assert( *p );
- while( 1 ){
+ do{
u8 c = 0;
int iCnt = 0;
while( 0xFE & (*p | c) ){
@@ -163940,12 +190294,28 @@ static void fts3EvalUpdateCounts(Fts3Expr *pExpr){
if( *p==0x00 ) break;
p++;
p += fts3GetVarint32(p, &iCol);
- }
+ }while( iCol<nCol );
}
- fts3EvalUpdateCounts(pExpr->pLeft);
- fts3EvalUpdateCounts(pExpr->pRight);
+ fts3EvalUpdateCounts(pExpr->pLeft, nCol);
+ fts3EvalUpdateCounts(pExpr->pRight, nCol);
+ }
+}
+
+/*
+** This is an sqlite3Fts3ExprIterate() callback. If the Fts3Expr.aMI[] array
+** has not yet been allocated, allocate and zero it. Otherwise, just zero
+** it.
+*/
+static int fts3AllocateMSI(Fts3Expr *pExpr, int iPhrase, void *pCtx){
+ Fts3Table *pTab = (Fts3Table*)pCtx;
+ UNUSED_PARAMETER(iPhrase);
+ if( pExpr->aMI==0 ){
+ pExpr->aMI = (u32 *)sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32));
+ if( pExpr->aMI==0 ) return SQLITE_NOMEM;
}
+ memset(pExpr->aMI, 0, pTab->nColumn * 3 * sizeof(u32));
+ return SQLITE_OK;
}
/*
@@ -163969,7 +190339,6 @@ static int fts3EvalGatherStats(
if( pExpr->aMI==0 ){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
Fts3Expr *pRoot; /* Root of NEAR expression */
- Fts3Expr *p; /* Iterator used for several purposes */
sqlite3_int64 iPrevId = pCsr->iPrevId;
sqlite3_int64 iDocid;
@@ -163977,7 +190346,9 @@ static int fts3EvalGatherStats(
/* Find the root of the NEAR expression */
pRoot = pExpr;
- while( pRoot->pParent && pRoot->pParent->eType==FTSQUERY_NEAR ){
+ while( pRoot->pParent
+ && (pRoot->pParent->eType==FTSQUERY_NEAR || pRoot->bDeferred)
+ ){
pRoot = pRoot->pParent;
}
iDocid = pRoot->iDocid;
@@ -163985,14 +190356,8 @@ static int fts3EvalGatherStats(
assert( pRoot->bStart );
/* Allocate space for the aMSI[] array of each FTSQUERY_PHRASE node */
- for(p=pRoot; p; p=p->pLeft){
- Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight);
- assert( pE->aMI==0 );
- pE->aMI = (u32 *)sqlite3_malloc(pTab->nColumn * 3 * sizeof(u32));
- if( !pE->aMI ) return SQLITE_NOMEM;
- memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32));
- }
-
+ rc = sqlite3Fts3ExprIterate(pRoot, fts3AllocateMSI, (void*)pTab);
+ if( rc!=SQLITE_OK ) return rc;
fts3EvalRestart(pCsr, pRoot, &rc);
while( pCsr->isEof==0 && rc==SQLITE_OK ){
@@ -164008,13 +190373,13 @@ static int fts3EvalGatherStats(
pCsr->isRequireSeek = 1;
pCsr->isMatchinfoNeeded = 1;
pCsr->iPrevId = pRoot->iDocid;
- }while( pCsr->isEof==0
- && pRoot->eType==FTSQUERY_NEAR
- && sqlite3Fts3EvalTestDeferred(pCsr, &rc)
+ }while( pCsr->isEof==0
+ && pRoot->eType==FTSQUERY_NEAR
+ && sqlite3Fts3EvalTestDeferred(pCsr, &rc)
);
if( rc==SQLITE_OK && pCsr->isEof==0 ){
- fts3EvalUpdateCounts(pRoot);
+ fts3EvalUpdateCounts(pRoot, pTab->nColumn);
}
}
@@ -164025,7 +190390,7 @@ static int fts3EvalGatherStats(
pRoot->bEof = bEof;
}else{
/* Caution: pRoot may iterate through docids in ascending or descending
- ** order. For this reason, even though it seems more defensive, the
+ ** order. For this reason, even though it seems more defensive, the
** do loop can not be written:
**
** do {...} while( pRoot->iDocid<iDocid && rc==SQLITE_OK );
@@ -164033,7 +190398,8 @@ static int fts3EvalGatherStats(
fts3EvalRestart(pCsr, pRoot, &rc);
do {
fts3EvalNextRow(pCsr, pRoot, &rc);
- assert( pRoot->bEof==0 );
+ assert_fts3_nc( pRoot->bEof==0 );
+ if( pRoot->bEof ) rc = FTS_CORRUPT_VTAB;
}while( pRoot->iDocid!=iDocid && rc==SQLITE_OK );
}
}
@@ -164041,10 +190407,10 @@ static int fts3EvalGatherStats(
}
/*
-** This function is used by the matchinfo() module to query a phrase
+** This function is used by the matchinfo() module to query a phrase
** expression node for the following information:
**
-** 1. The total number of occurrences of the phrase in each column of
+** 1. The total number of occurrences of the phrase in each column of
** the FTS table (considering all rows), and
**
** 2. For each column, the number of rows in the table for which the
@@ -164058,12 +190424,12 @@ static int fts3EvalGatherStats(
**
** Caveats:
**
-** * If a phrase consists entirely of deferred tokens, then all output
+** * If a phrase consists entirely of deferred tokens, then all output
** values are set to the number of documents in the table. In other
-** words we assume that very common tokens occur exactly once in each
+** words we assume that very common tokens occur exactly once in each
** column of each row of the table.
**
-** * If a phrase contains some deferred tokens (and some non-deferred
+** * If a phrase contains some deferred tokens (and some non-deferred
** tokens), count the potential occurrence identified by considering
** the non-deferred tokens instead of actual phrase occurrences.
**
@@ -164101,14 +190467,14 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(
/*
** The expression pExpr passed as the second argument to this function
-** must be of type FTSQUERY_PHRASE.
+** must be of type FTSQUERY_PHRASE.
**
** The returned value is either NULL or a pointer to a buffer containing
** a position-list indicating the occurrences of the phrase in column iCol
-** of the current row.
+** of the current row.
**
-** More specifically, the returned buffer contains 1 varint for each
-** occurrence of the phrase in the column, stored using the normal (delta+2)
+** More specifically, the returned buffer contains 1 varint for each
+** occurrence of the phrase in the column, stored using the normal (delta+2)
** compression and is terminated by either an 0x01 or 0x00 byte. For example,
** if the requested column contains "a b X c d X X" and the position-list
** for 'X' is requested, the buffer returned may contain:
@@ -164130,7 +190496,7 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
int iThis;
sqlite3_int64 iDocid;
- /* If this phrase is applies specifically to some column other than
+ /* If this phrase is applies specifically to some column other than
** column iCol, return a NULL pointer. */
*ppOut = 0;
assert( iCol>=0 && iCol<pTab->nColumn );
@@ -164147,10 +190513,11 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
u8 bTreeEof = 0;
Fts3Expr *p; /* Used to iterate from pExpr to root */
Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */
+ Fts3Expr *pRun; /* Closest non-deferred ancestor of pNear */
int bMatch;
- /* Check if this phrase descends from an OR expression node. If not,
- ** return NULL. Otherwise, the entry that corresponds to docid
+ /* Check if this phrase descends from an OR expression node. If not,
+ ** return NULL. Otherwise, the entry that corresponds to docid
** pCsr->iPrevId may lie earlier in the doclist buffer. Or, if the
** tree that the node is part of has been marked as EOF, but the node
** itself is not EOF, then it may point to an earlier entry. */
@@ -164161,22 +190528,30 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
if( p->bEof ) bTreeEof = 1;
}
if( bOr==0 ) return SQLITE_OK;
+ pRun = pNear;
+ while( pRun->bDeferred ){
+ assert( pRun->pParent );
+ pRun = pRun->pParent;
+ }
/* This is the descendent of an OR node. In this case we cannot use
** an incremental phrase. Load the entire doclist for the phrase
** into memory in this case. */
if( pPhrase->bIncr ){
- int bEofSave = pNear->bEof;
- fts3EvalRestart(pCsr, pNear, &rc);
- while( rc==SQLITE_OK && !pNear->bEof ){
- fts3EvalNextRow(pCsr, pNear, &rc);
- if( bEofSave==0 && pNear->iDocid==iDocid ) break;
+ int bEofSave = pRun->bEof;
+ fts3EvalRestart(pCsr, pRun, &rc);
+ while( rc==SQLITE_OK && !pRun->bEof ){
+ fts3EvalNextRow(pCsr, pRun, &rc);
+ if( bEofSave==0 && pRun->iDocid==iDocid ) break;
}
assert( rc!=SQLITE_OK || pPhrase->bIncr==0 );
+ if( rc==SQLITE_OK && pRun->bEof!=bEofSave ){
+ rc = FTS_CORRUPT_VTAB;
+ }
}
if( bTreeEof ){
- while( rc==SQLITE_OK && !pNear->bEof ){
- fts3EvalNextRow(pCsr, pNear, &rc);
+ while( rc==SQLITE_OK && !pRun->bEof ){
+ fts3EvalNextRow(pCsr, pRun, &rc);
}
}
if( rc!=SQLITE_OK ) return rc;
@@ -164198,7 +190573,7 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
(pIter >= (pPh->doclist.aAll + pPh->doclist.nAll));
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
sqlite3Fts3DoclistNext(
- bDescDoclist, pPh->doclist.aAll, pPh->doclist.nAll,
+ bDescDoclist, pPh->doclist.aAll, pPh->doclist.nAll,
&pIter, &iDocid, &bEof
);
}
@@ -164207,7 +190582,7 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
int dummy;
sqlite3Fts3DoclistPrev(
- bDescDoclist, pPh->doclist.aAll, pPh->doclist.nAll,
+ bDescDoclist, pPh->doclist.aAll, pPh->doclist.nAll,
&pIter, &iDocid, &dummy, &bEof
);
}
@@ -164283,7 +190658,7 @@ SQLITE_PRIVATE int sqlite3Fts3Corrupt(){
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_fts3_init(
- sqlite3 *db,
+ sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
@@ -164364,7 +190739,7 @@ static int fts3auxConnectMethod(
char const *zFts3; /* Name of fts3 table */
int nDb; /* Result of strlen(zDb) */
int nFts3; /* Result of strlen(zFts3) */
- int nByte; /* Bytes of space to allocate here */
+ sqlite3_int64 nByte; /* Bytes of space to allocate here */
int rc; /* value returned by declare_vtab() */
Fts3auxTable *p; /* Virtual table object to return */
@@ -164377,11 +190752,11 @@ static int fts3auxConnectMethod(
*/
if( argc!=4 && argc!=5 ) goto bad_args;
- zDb = argv[1];
+ zDb = argv[1];
nDb = (int)strlen(zDb);
if( argc==5 ){
if( nDb==4 && 0==sqlite3_strnicmp("temp", zDb, 4) ){
- zDb = argv[3];
+ zDb = argv[3];
nDb = (int)strlen(zDb);
zFts3 = argv[4];
}else{
@@ -164396,7 +190771,7 @@ static int fts3auxConnectMethod(
if( rc!=SQLITE_OK ) return rc;
nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
- p = (Fts3auxTable *)sqlite3_malloc(nByte);
+ p = (Fts3auxTable *)sqlite3_malloc64(nByte);
if( !p ) return SQLITE_NOMEM;
memset(p, 0, nByte);
@@ -164445,7 +190820,7 @@ static int fts3auxDisconnectMethod(sqlite3_vtab *pVtab){
** xBestIndex - Analyze a WHERE and ORDER BY clause.
*/
static int fts3auxBestIndexMethod(
- sqlite3_vtab *pVTab,
+ sqlite3_vtab *pVTab,
sqlite3_index_info *pInfo
){
int i;
@@ -164458,14 +190833,14 @@ static int fts3auxBestIndexMethod(
UNUSED_PARAMETER(pVTab);
/* This vtab delivers always results in "ORDER BY term ASC" order. */
- if( pInfo->nOrderBy==1
- && pInfo->aOrderBy[0].iColumn==0
+ if( pInfo->nOrderBy==1
+ && pInfo->aOrderBy[0].iColumn==0
&& pInfo->aOrderBy[0].desc==0
){
pInfo->orderByConsumed = 1;
}
- /* Search for equality and range constraints on the "term" column.
+ /* Search for equality and range constraints on the "term" column.
** And equality constraints on the hidden "languageid" column. */
for(i=0; i<pInfo->nConstraint; i++){
if( pInfo->aConstraint[i].usable ){
@@ -164546,11 +190921,11 @@ static int fts3auxCloseMethod(sqlite3_vtab_cursor *pCursor){
static int fts3auxGrowStatArray(Fts3auxCursor *pCsr, int nSize){
if( nSize>pCsr->nStat ){
struct Fts3auxColstats *aNew;
- aNew = (struct Fts3auxColstats *)sqlite3_realloc(pCsr->aStat,
+ aNew = (struct Fts3auxColstats *)sqlite3_realloc64(pCsr->aStat,
sizeof(struct Fts3auxColstats) * nSize
);
if( aNew==0 ) return SQLITE_NOMEM;
- memset(&aNew[pCsr->nStat], 0,
+ memset(&aNew[pCsr->nStat], 0,
sizeof(struct Fts3auxColstats) * (nSize - pCsr->nStat)
);
pCsr->aStat = aNew;
@@ -164595,6 +190970,7 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM;
memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat);
iCol = 0;
+ rc = SQLITE_OK;
while( i<nDoclist ){
sqlite3_int64 v = 0;
@@ -164610,8 +190986,8 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
/* State 1. In this state we are expecting either a 1, indicating
** that the following integer will be a column number, or the
- ** start of a position list for column 0.
- **
+ ** start of a position list for column 0.
+ **
** The only difference between state 1 and state 2 is that if the
** integer encountered in state 1 is not 0 or 1, then we need to
** increment the column 0 "nDoc" count for this term.
@@ -164638,6 +191014,10 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
/* State 3. The integer just read is a column number. */
default: assert( eState==3 );
iCol = (int)v;
+ if( iCol<1 ){
+ rc = SQLITE_CORRUPT_VTAB;
+ break;
+ }
if( fts3auxGrowStatArray(pCsr, iCol+2) ) return SQLITE_NOMEM;
pCsr->aStat[iCol+1].nDoc++;
eState = 2;
@@ -164646,7 +191026,6 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
}
pCsr->iCol = 0;
- rc = SQLITE_OK;
}else{
pCsr->isEof = 1;
}
@@ -164704,6 +191083,7 @@ static int fts3auxFilterMethod(
sqlite3Fts3SegReaderFinish(&pCsr->csr);
sqlite3_free((void *)pCsr->filter.zTerm);
sqlite3_free(pCsr->aStat);
+ sqlite3_free(pCsr->zStop);
memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr);
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
@@ -164714,17 +191094,17 @@ static int fts3auxFilterMethod(
assert( (iEq==0 && iGe==-1) || (iEq==-1 && iGe==0) );
if( zStr ){
pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr);
- pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]);
if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM;
+ pCsr->filter.nTerm = (int)strlen(pCsr->filter.zTerm);
}
}
if( iLe>=0 ){
pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iLe]));
- pCsr->nStop = sqlite3_value_bytes(apVal[iLe]);
if( pCsr->zStop==0 ) return SQLITE_NOMEM;
+ pCsr->nStop = (int)strlen(pCsr->zStop);
}
-
+
if( iLangid>=0 ){
iLangVal = sqlite3_value_int(apVal[iLangid]);
@@ -164838,7 +191218,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
int rc; /* Return code */
@@ -164863,15 +191244,15 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){
******************************************************************************
**
** This module contains code that implements a parser for fts3 query strings
-** (the right-hand argument to the MATCH operator). Because the supported
+** (the right-hand argument to the MATCH operator). Because the supported
** syntax is relatively simple, the whole tokenizer/parser system is
-** hand-coded.
+** hand-coded.
*/
/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/*
-** By default, this module parses the legacy syntax that has been
+** By default, this module parses the legacy syntax that has been
** traditionally used by fts3. Or, if SQLITE_ENABLE_FTS3_PARENTHESIS
** is defined, then it uses the new syntax. The differences between
** the new and the old syntaxes are:
@@ -164880,7 +191261,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){
**
** b) The new syntax supports the AND and NOT operators. The old does not.
**
-** c) The old syntax supports the "-" token qualifier. This is not
+** c) The old syntax supports the "-" token qualifier. This is not
** supported by the new syntax (it is replaced by the NOT operator).
**
** d) When using the old syntax, the OR operator has a greater precedence
@@ -164889,7 +191270,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){
**
** If compiled with SQLITE_TEST defined, then this module exports the
** symbol "int sqlite3_fts3_enable_parentheses". Setting this variable
-** to zero causes the module to use the old syntax. If it is set to
+** to zero causes the module to use the old syntax. If it is set to
** non-zero the new syntax is activated. This is so both syntaxes can
** be tested using a single build of testfixture.
**
@@ -164918,7 +191299,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){
#ifdef SQLITE_TEST
SQLITE_API int sqlite3_fts3_enable_parentheses = 0;
#else
-# ifdef SQLITE_ENABLE_FTS3_PARENTHESIS
+# ifdef SQLITE_ENABLE_FTS3_PARENTHESIS
# define sqlite3_fts3_enable_parentheses 1
# else
# define sqlite3_fts3_enable_parentheses 0
@@ -164936,7 +191317,7 @@ SQLITE_API int sqlite3_fts3_enable_parentheses = 0;
/*
** isNot:
** This variable is used by function getNextNode(). When getNextNode() is
-** called, it sets ParseContext.isNot to true if the 'next node' is a
+** called, it sets ParseContext.isNot to true if the 'next node' is a
** FTSQUERY_PHRASE with a unary "-" attached to it. i.e. "mysql" in the
** FTS3 query "sqlite -mysql". Otherwise, ParseContext.isNot is set to
** zero.
@@ -164955,7 +191336,7 @@ struct ParseContext {
};
/*
-** This function is equivalent to the standard isspace() function.
+** This function is equivalent to the standard isspace() function.
**
** The standard isspace() can be awkward to use safely, because although it
** is defined to accept an argument of type int, its behavior when passed
@@ -164971,11 +191352,11 @@ static int fts3isspace(char c){
/*
** Allocate nByte bytes of memory using sqlite3_malloc(). If successful,
-** zero the memory before returning a pointer to it. If unsuccessful,
+** zero the memory before returning a pointer to it. If unsuccessful,
** return NULL.
*/
-static void *fts3MallocZero(int nByte){
- void *pRet = sqlite3_malloc(nByte);
+SQLITE_PRIVATE void *sqlite3Fts3MallocZero(sqlite3_int64 nByte){
+ void *pRet = sqlite3_malloc64(nByte);
if( pRet ) memset(pRet, 0, nByte);
return pRet;
}
@@ -165019,7 +191400,7 @@ static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
** structure of type FTSQUERY_PHRASE containing a phrase consisting of this
** single token and set *ppExpr to point to it. If the end of the buffer is
** reached before a token is found, set *ppExpr to zero. It is the
-** responsibility of the caller to eventually deallocate the allocated
+** responsibility of the caller to eventually deallocate the allocated
** Fts3Expr structure (if any) by passing it to sqlite3_free().
**
** Return SQLITE_OK if successful, or SQLITE_NOMEM if a memory allocation
@@ -165050,12 +191431,12 @@ static int getNextToken(
if( rc==SQLITE_OK ){
const char *zToken;
int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0;
- int nByte; /* total space to allocate */
+ sqlite3_int64 nByte; /* total space to allocate */
rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
if( rc==SQLITE_OK ){
nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken;
- pRet = (Fts3Expr *)fts3MallocZero(nByte);
+ pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte);
if( !pRet ){
rc = SQLITE_NOMEM;
}else{
@@ -165073,8 +191454,8 @@ static int getNextToken(
}
while( 1 ){
- if( !sqlite3_fts3_enable_parentheses
- && iStart>0 && z[iStart-1]=='-'
+ if( !sqlite3_fts3_enable_parentheses
+ && iStart>0 && z[iStart-1]=='-'
){
pParse->isNot = 1;
iStart--;
@@ -165094,7 +191475,7 @@ static int getNextToken(
pModule->xClose(pCursor);
}
-
+
*ppExpr = pRet;
return rc;
}
@@ -165104,8 +191485,8 @@ static int getNextToken(
** Enlarge a memory allocation. If an out-of-memory allocation occurs,
** then free the old allocation.
*/
-static void *fts3ReallocOrFree(void *pOrig, int nNew){
- void *pRet = sqlite3_realloc(pOrig, nNew);
+static void *fts3ReallocOrFree(void *pOrig, sqlite3_int64 nNew){
+ void *pRet = sqlite3_realloc64(pOrig, nNew);
if( !pRet ){
sqlite3_free(pOrig);
}
@@ -165116,7 +191497,7 @@ static void *fts3ReallocOrFree(void *pOrig, int nNew){
** Buffer zInput, length nInput, contains the contents of a quoted string
** that appeared as part of an fts3 query expression. Neither quote character
** is included in the buffer. This function attempts to tokenize the entire
-** input buffer and create an Fts3Expr structure of type FTSQUERY_PHRASE
+** input buffer and create an Fts3Expr structure of type FTSQUERY_PHRASE
** containing the results.
**
** If successful, SQLITE_OK is returned and *ppExpr set to point at the
@@ -165141,7 +191522,7 @@ static int getNextString(
int nToken = 0;
/* The final Fts3Expr data structure, including the Fts3Phrase,
- ** Fts3PhraseToken structures token buffers are all stored as a single
+ ** Fts3PhraseToken structures token buffers are all stored as a single
** allocation so that the expression can be freed with a single call to
** sqlite3_free(). Setting this up requires a two pass approach.
**
@@ -165150,7 +191531,7 @@ static int getNextString(
** to assemble data in two dynamic buffers:
**
** Buffer p: Points to the Fts3Expr structure, followed by the Fts3Phrase
- ** structure, followed by the array of Fts3PhraseToken
+ ** structure, followed by the array of Fts3PhraseToken
** structures. This pass only populates the Fts3PhraseToken array.
**
** Buffer zTemp: Contains copies of all tokens.
@@ -165235,7 +191616,7 @@ no_mem:
}
/*
-** The output variable *ppExpr is populated with an allocated Fts3Expr
+** The output variable *ppExpr is populated with an allocated Fts3Expr
** structure, or set to 0 if the end of the input buffer is reached.
**
** Returns an SQLite error code. SQLITE_OK if everything works, SQLITE_NOMEM
@@ -165271,7 +191652,7 @@ static int getNextNode(
pParse->isNot = 0;
/* Skip over any whitespace before checking for a keyword, an open or
- ** close bracket, or a quoted string.
+ ** close bracket, or a quoted string.
*/
while( nInput>0 && fts3isspace(*zInput) ){
nInput--;
@@ -165298,22 +191679,19 @@ static int getNextNode(
if( pKey->eType==FTSQUERY_NEAR ){
assert( nKey==4 );
if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){
- nNear = 0;
- for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){
- nNear = nNear * 10 + (zInput[nKey] - '0');
- }
+ nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear);
}
}
/* At this point this is probably a keyword. But for that to be true,
** the next byte must contain either whitespace, an open or close
- ** parenthesis, a quote character, or EOF.
+ ** parenthesis, a quote character, or EOF.
*/
cNext = zInput[nKey];
- if( fts3isspace(cNext)
+ if( fts3isspace(cNext)
|| cNext=='"' || cNext=='(' || cNext==')' || cNext==0
){
- pRet = (Fts3Expr *)fts3MallocZero(sizeof(Fts3Expr));
+ pRet = (Fts3Expr *)sqlite3Fts3MallocZero(sizeof(Fts3Expr));
if( !pRet ){
return SQLITE_NOMEM;
}
@@ -165348,8 +191726,12 @@ static int getNextNode(
if( *zInput=='(' ){
int nConsumed = 0;
pParse->nNest++;
+#if !defined(SQLITE_MAX_EXPR_DEPTH)
+ if( pParse->nNest>1000 ) return SQLITE_ERROR;
+#elif SQLITE_MAX_EXPR_DEPTH>0
+ if( pParse->nNest>SQLITE_MAX_EXPR_DEPTH ) return SQLITE_ERROR;
+#endif
rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed);
- if( rc==SQLITE_OK && !*ppExpr ){ rc = SQLITE_DONE; }
*pnConsumed = (int)(zInput - z) + 1 + nConsumed;
return rc;
}else if( *zInput==')' ){
@@ -165360,15 +191742,15 @@ static int getNextNode(
}
}
- /* If control flows to this point, this must be a regular token, or
+ /* If control flows to this point, this must be a regular token, or
** the end of the input. Read a regular token using the sqlite3_tokenizer
** interface. Before doing so, figure out if there is an explicit
- ** column specifier for the token.
+ ** column specifier for the token.
**
** TODO: Strangely, it is not possible to associate a column specifier
** with a quoted phrase, only with a single token. Not sure if this was
** an implementation artifact or an intentional decision when fts3 was
- ** first implemented. Whichever it was, this module duplicates the
+ ** first implemented. Whichever it was, this module duplicates the
** limitation.
*/
iCol = pParse->iDefaultCol;
@@ -165376,8 +191758,8 @@ static int getNextNode(
for(ii=0; ii<pParse->nCol; ii++){
const char *zStr = pParse->azCol[ii];
int nStr = (int)strlen(zStr);
- if( nInput>nStr && zInput[nStr]==':'
- && sqlite3_strnicmp(zStr, zInput, nStr)==0
+ if( nInput>nStr && zInput[nStr]==':'
+ && sqlite3_strnicmp(zStr, zInput, nStr)==0
){
iCol = ii;
iColLen = (int)((zInput - z) + nStr + 1);
@@ -165422,7 +191804,7 @@ static int opPrecedence(Fts3Expr *p){
}
/*
-** Argument ppHead contains a pointer to the current head of a query
+** Argument ppHead contains a pointer to the current head of a query
** expression tree being parsed. pPrev is the expression node most recently
** inserted into the tree. This function adds pNew, which is always a binary
** operator node, into the expression tree based on the relative precedence
@@ -165452,7 +191834,7 @@ static void insertBinaryOperator(
/*
** Parse the fts3 query expression found in buffer z, length n. This function
-** returns either when the end of the buffer is reached or an unmatched
+** returns either when the end of the buffer is reached or an unmatched
** closing bracket - ')' - is encountered.
**
** If successful, SQLITE_OK is returned, *ppExpr is set to point to the
@@ -165484,11 +191866,11 @@ static int fts3ExprParse(
if( p ){
int isPhrase;
- if( !sqlite3_fts3_enable_parentheses
- && p->eType==FTSQUERY_PHRASE && pParse->isNot
+ if( !sqlite3_fts3_enable_parentheses
+ && p->eType==FTSQUERY_PHRASE && pParse->isNot
){
/* Create an implicit NOT operator. */
- Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr));
+ Fts3Expr *pNot = sqlite3Fts3MallocZero(sizeof(Fts3Expr));
if( !pNot ){
sqlite3Fts3ExprFree(p);
rc = SQLITE_NOMEM;
@@ -165522,7 +191904,7 @@ static int fts3ExprParse(
/* Insert an implicit AND operator. */
Fts3Expr *pAnd;
assert( pRet && pPrev );
- pAnd = fts3MallocZero(sizeof(Fts3Expr));
+ pAnd = sqlite3Fts3MallocZero(sizeof(Fts3Expr));
if( !pAnd ){
sqlite3Fts3ExprFree(p);
rc = SQLITE_NOMEM;
@@ -165606,13 +191988,13 @@ exprparse_out:
}
/*
-** Return SQLITE_ERROR if the maximum depth of the expression tree passed
+** Return SQLITE_ERROR if the maximum depth of the expression tree passed
** as the only argument is more than nMaxDepth.
*/
static int fts3ExprCheckDepth(Fts3Expr *p, int nMaxDepth){
int rc = SQLITE_OK;
if( p ){
- if( nMaxDepth<0 ){
+ if( nMaxDepth<0 ){
rc = SQLITE_TOOBIG;
}else{
rc = fts3ExprCheckDepth(p->pLeft, nMaxDepth-1);
@@ -165627,12 +192009,12 @@ static int fts3ExprCheckDepth(Fts3Expr *p, int nMaxDepth){
/*
** This function attempts to transform the expression tree at (*pp) to
** an equivalent but more balanced form. The tree is modified in place.
-** If successful, SQLITE_OK is returned and (*pp) set to point to the
-** new root expression node.
+** If successful, SQLITE_OK is returned and (*pp) set to point to the
+** new root expression node.
**
** nMaxDepth is the maximum allowable depth of the balanced sub-tree.
**
-** Otherwise, if an error occurs, an SQLite error code is returned and
+** Otherwise, if an error occurs, an SQLite error code is returned and
** expression (*pp) freed.
*/
static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){
@@ -165648,7 +192030,7 @@ static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){
if( rc==SQLITE_OK ){
if( (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){
Fts3Expr **apLeaf;
- apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth);
+ apLeaf = (Fts3Expr **)sqlite3_malloc64(sizeof(Fts3Expr *) * nMaxDepth);
if( 0==apLeaf ){
rc = SQLITE_NOMEM;
}else{
@@ -165747,7 +192129,7 @@ static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){
}
pRoot = p;
}else{
- /* An error occurred. Delete the contents of the apLeaf[] array
+ /* An error occurred. Delete the contents of the apLeaf[] array
** and pFree list. Everything else is cleaned up by the call to
** sqlite3Fts3ExprFree(pRoot) below. */
Fts3Expr *pDel;
@@ -165789,7 +192171,7 @@ static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){
}
}
}
-
+
if( rc!=SQLITE_OK ){
sqlite3Fts3ExprFree(pRoot);
pRoot = 0;
@@ -165803,9 +192185,9 @@ static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){
** differences:
**
** 1. It does not do expression rebalancing.
-** 2. It does not check that the expression does not exceed the
+** 2. It does not check that the expression does not exceed the
** maximum allowable depth.
-** 3. Even if it fails, *ppExpr may still be set to point to an
+** 3. Even if it fails, *ppExpr may still be set to point to an
** expression tree. It should be deleted using sqlite3Fts3ExprFree()
** in this case.
*/
@@ -165844,7 +192226,7 @@ static int fts3ExprParseUnbalanced(
if( rc==SQLITE_OK && sParse.nNest ){
rc = SQLITE_ERROR;
}
-
+
return rc;
}
@@ -165863,7 +192245,7 @@ static int fts3ExprParseUnbalanced(
** The first parameter, pTokenizer, is passed the fts3 tokenizer module to
** use to normalize query tokens while parsing the expression. The azCol[]
** array, which is assumed to contain nCol entries, should contain the names
-** of each column in the target fts3 table, in order from left to right.
+** of each column in the target fts3 table, in order from left to right.
** Column names must be nul-terminated strings.
**
** The iDefaultCol parameter should be passed the index of the table column
@@ -165886,7 +192268,7 @@ SQLITE_PRIVATE int sqlite3Fts3ExprParse(
int rc = fts3ExprParseUnbalanced(
pTokenizer, iLangid, azCol, bFts4, nCol, iDefaultCol, z, n, ppExpr
);
-
+
/* Rebalance the expression. And check that its depth does not exceed
** SQLITE_FTS3_MAX_EXPR_DEPTH. */
if( rc==SQLITE_OK && *ppExpr ){
@@ -165901,7 +192283,7 @@ SQLITE_PRIVATE int sqlite3Fts3ExprParse(
*ppExpr = 0;
if( rc==SQLITE_TOOBIG ){
sqlite3Fts3ErrMsg(pzErr,
- "FTS expression tree is too large (maximum depth %d)",
+ "FTS expression tree is too large (maximum depth %d)",
SQLITE_FTS3_MAX_EXPR_DEPTH
);
rc = SQLITE_ERROR;
@@ -165963,11 +192345,11 @@ SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *pDel){
/*
** Return a pointer to a buffer containing a text representation of the
** expression passed as the first argument. The buffer is obtained from
-** sqlite3_malloc(). It is the responsibility of the caller to use
+** sqlite3_malloc(). It is the responsibility of the caller to use
** sqlite3_free() to release the memory. If an OOM condition is encountered,
** NULL is returned.
**
-** If the second argument is not NULL, then its contents are prepended to
+** If the second argument is not NULL, then its contents are prepended to
** the returned expression text and then freed using sqlite3_free().
*/
static char *exprToString(Fts3Expr *pExpr, char *zBuf){
@@ -165981,7 +192363,7 @@ static char *exprToString(Fts3Expr *pExpr, char *zBuf){
zBuf = sqlite3_mprintf(
"%zPHRASE %d 0", zBuf, pPhrase->iColumn);
for(i=0; zBuf && i<pPhrase->nToken; i++){
- zBuf = sqlite3_mprintf("%z %.*s%s", zBuf,
+ zBuf = sqlite3_mprintf("%z %.*s%s", zBuf,
pPhrase->aToken[i].n, pPhrase->aToken[i].z,
(pPhrase->aToken[i].isPrefix?"+":"")
);
@@ -166014,7 +192396,7 @@ static char *exprToString(Fts3Expr *pExpr, char *zBuf){
}
/*
-** This is the implementation of a scalar SQL function used to test the
+** This is the implementation of a scalar SQL function used to test the
** expression parser. It should be called as follows:
**
** fts3_exprtest(<tokenizer>, <expr>, <column 1>, ...);
@@ -166047,7 +192429,7 @@ static void fts3ExprTestCommon(
char *zErr = 0;
if( argc<3 ){
- sqlite3_result_error(context,
+ sqlite3_result_error(context,
"Usage: fts3_exprtest(tokenizer, expr, col1, ...", -1
);
return;
@@ -166068,7 +192450,7 @@ static void fts3ExprTestCommon(
zExpr = (const char *)sqlite3_value_text(argv[1]);
nExpr = sqlite3_value_bytes(argv[1]);
nCol = argc-2;
- azCol = (char **)sqlite3_malloc(nCol*sizeof(char *));
+ azCol = (char **)sqlite3_malloc64(nCol*sizeof(char *));
if( !azCol ){
sqlite3_result_error_nomem(context);
goto exprtest_out;
@@ -166125,15 +192507,15 @@ static void fts3ExprTestRebalance(
}
/*
-** Register the query expression parser test function fts3_exprtest()
-** with database connection db.
+** Register the query expression parser test function fts3_exprtest()
+** with database connection db.
*/
SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db, Fts3Hash *pHash){
int rc = sqlite3_create_function(
db, "fts3_exprtest", -1, SQLITE_UTF8, (void*)pHash, fts3ExprTest, 0, 0
);
if( rc==SQLITE_OK ){
- rc = sqlite3_create_function(db, "fts3_exprtest_rebalance",
+ rc = sqlite3_create_function(db, "fts3_exprtest_rebalance",
-1, SQLITE_UTF8, (void*)pHash, fts3ExprTestRebalance, 0, 0
);
}
@@ -166182,8 +192564,8 @@ SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db, Fts3Hash *pHash
/*
** Malloc and Free functions
*/
-static void *fts3HashMalloc(int n){
- void *p = sqlite3_malloc(n);
+static void *fts3HashMalloc(sqlite3_int64 n){
+ void *p = sqlite3_malloc64(n);
if( p ){
memset(p, 0, n);
}
@@ -166197,8 +192579,8 @@ static void fts3HashFree(void *p){
** fields of the Hash structure.
**
** "pNew" is a pointer to the hash table that is to be initialized.
-** keyClass is one of the constants
-** FTS3_HASH_BINARY or FTS3_HASH_STRING. The value of keyClass
+** keyClass is one of the constants
+** FTS3_HASH_BINARY or FTS3_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.
@@ -166275,7 +192657,7 @@ static int fts3BinCompare(const void *pKey1, int n1, const void *pKey2, int n2){
/*
** Return a pointer to the appropriate hash function given the key class.
**
-** The C syntax in this function definition may be unfamilar to some
+** 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 "ftsHashFunction". The function takes a
@@ -166335,7 +192717,7 @@ static void fts3HashInsertElement(
/* Resize the hash table so that it cantains "new_size" buckets.
-** "new_size" must be a power of 2. The hash table might fail
+** "new_size" must be a power of 2. The hash table might fail
** to resize if sqliteMalloc() fails.
**
** Return non-zero if a memory allocation error occurs.
@@ -166380,7 +192762,7 @@ static Fts3HashElem *fts3FindElementByHash(
count = pEntry->count;
xCompare = ftsCompareFunction(pH->keyClass);
while( count-- && elem ){
- if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
+ if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
return elem;
}
elem = elem->next;
@@ -166399,7 +192781,7 @@ static void fts3RemoveElementByHash(
){
struct _fts3ht *pEntry;
if( elem->prev ){
- elem->prev->next = elem->next;
+ elem->prev->next = elem->next;
}else{
pH->first = elem->next;
}
@@ -166427,8 +192809,8 @@ static void fts3RemoveElementByHash(
}
SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(
- const Fts3Hash *pH,
- const void *pKey,
+ const Fts3Hash *pH,
+ const void *pKey,
int nKey
){
int h; /* A hash on key */
@@ -166442,7 +192824,7 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(
return fts3FindElementByHash(pH,pKey,nKey, h & (pH->htsize-1));
}
-/*
+/*
** 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.
@@ -166616,7 +192998,7 @@ static int porterDestroy(sqlite3_tokenizer *pTokenizer){
/*
** 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
+** used to incrementally tokenize this string is returned in
** *ppCursor.
*/
static int porterOpen(
@@ -166669,7 +193051,7 @@ static const char cType[] = {
/*
** isConsonant() and isVowel() determine if their first character in
** the string they point to is a consonant or a vowel, according
-** to Porter ruls.
+** 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,
@@ -166789,11 +193171,11 @@ static int star_oh(const char *z){
/*
** If the word ends with zFrom and xCond() is true for the stem
-** of the word that preceeds the zFrom ending, then change the
+** 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.
+** 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
@@ -166862,9 +193244,9 @@ static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
** word contains digits, 3 bytes are taken from the beginning and
** 3 bytes from the end. For long words without digits, 10 bytes
** are taken from each end. US-ASCII case folding still applies.
-**
-** If the input word contains not digits but does characters not
-** in [a-zA-Z] then no stemming is attempted and this routine just
+**
+** If the input word contains not digits but does characters not
+** in [a-zA-Z] then no stemming is attempted and this routine just
** copies the input into the input into the output with US-ASCII
** case folding.
**
@@ -166909,11 +193291,11 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
}
}
- /* Step 1b */
+ /* Step 1b */
z2 = z;
if( stem(&z, "dee", "ee", m_gt_0) ){
/* Do nothing. The work was all in the test */
- }else if(
+ }else if(
(stem(&z, "gni", "", hasVowel) || stem(&z, "de", "", hasVowel))
&& z!=z2
){
@@ -166952,7 +193334,7 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
stem(&z, "igol", "log", m_gt_0);
break;
case 'l':
- if( !stem(&z, "ilb", "ble", m_gt_0)
+ if( !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)
@@ -167154,7 +193536,7 @@ static int porterNext(
if( n>c->nAllocated ){
char *pNew;
c->nAllocated = n+20;
- pNew = sqlite3_realloc(c->zToken, c->nAllocated);
+ pNew = sqlite3_realloc64(c->zToken, c->nAllocated);
if( !pNew ) return SQLITE_NOMEM;
c->zToken = pNew;
}
@@ -167240,7 +193622,7 @@ static int fts3TokenizerEnabled(sqlite3_context *context){
}
/*
-** Implementation of the SQL scalar function for accessing the underlying
+** Implementation of the SQL scalar function for accessing the underlying
** hash table. This function may be called as follows:
**
** SELECT <function-name>(<key-name>);
@@ -167277,7 +193659,7 @@ static void fts3TokenizerFunc(
nName = sqlite3_value_bytes(argv[0])+1;
if( argc==2 ){
- if( fts3TokenizerEnabled(context) ){
+ if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[1]) ){
void *pOld;
int n = sqlite3_value_bytes(argv[1]);
if( zName==0 || n!=sizeof(pPtr) ){
@@ -167304,7 +193686,9 @@ static void fts3TokenizerFunc(
return;
}
}
- sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT);
+ if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[0]) ){
+ sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT);
+ }
}
SQLITE_PRIVATE int sqlite3Fts3IsIdChar(char c){
@@ -167392,8 +193776,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
int iArg = 0;
z = &z[n+1];
while( z<zEnd && (NULL!=(z = (char *)sqlite3Fts3NextToken(z, &n))) ){
- int nNew = sizeof(char *)*(iArg+1);
- char const **aNew = (const char **)sqlite3_realloc((void *)aArg, nNew);
+ sqlite3_int64 nNew = sizeof(char *)*(iArg+1);
+ char const **aNew = (const char **)sqlite3_realloc64((void *)aArg, nNew);
if( !aNew ){
sqlite3_free(zCopy);
sqlite3_free((void *)aArg);
@@ -167410,7 +193794,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
if( rc!=SQLITE_OK ){
sqlite3Fts3ErrMsg(pzErr, "unknown tokenizer");
}else{
- (*ppTok)->pModule = m;
+ (*ppTok)->pModule = m;
}
sqlite3_free((void *)aArg);
}
@@ -167430,7 +193814,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
/* #include <string.h> */
/*
-** Implementation of a special SQL scalar function for testing tokenizers
+** Implementation of a special SQL scalar function for testing tokenizers
** designed to be used in concert with the Tcl testing framework. This
** function must be called with two or more arguments:
**
@@ -167442,9 +193826,9 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
**
** The return value is a string that may be interpreted as a Tcl
** list. For each token in the <input-string>, three elements are
-** added to the returned list. The first is the token position, the
+** added to the returned list. The first is the token position, the
** second is the token text (folded, stemmed, etc.) and the third is the
-** substring of <input-string> associated with the token. For example,
+** substring of <input-string> associated with the token. For example,
** using the built-in "simple" tokenizer:
**
** SELECT fts_tokenizer_test('simple', 'I don't see how');
@@ -167452,7 +193836,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
** will return the string:
**
** "{0 i I 1 dont don't 2 see see 3 how how}"
-**
+**
*/
static void testFunc(
sqlite3_context *context,
@@ -167547,8 +193931,8 @@ finish:
static
int registerTokenizer(
- sqlite3 *db,
- char *zName,
+ sqlite3 *db,
+ char *zName,
const sqlite3_tokenizer_module *p
){
int rc;
@@ -167570,8 +193954,8 @@ int registerTokenizer(
static
int queryTokenizer(
- sqlite3 *db,
- char *zName,
+ sqlite3 *db,
+ char *zName,
const sqlite3_tokenizer_module **pp
){
int rc;
@@ -167586,7 +193970,9 @@ int queryTokenizer(
sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
if( SQLITE_ROW==sqlite3_step(pStmt) ){
- if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
+ if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB
+ && sqlite3_column_bytes(pStmt, 0)==sizeof(*pp)
+ ){
memcpy((void *)pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
}
}
@@ -167654,28 +194040,28 @@ static void intTestFunc(
/*
** Set up SQL objects in database db used to access the contents of
** the hash table pointed to by argument pHash. The hash table must
-** been initialized to use string keys, and to take a private copy
+** been initialized to use string keys, and to take a private copy
** of the key when a value is inserted. i.e. by a call similar to:
**
** sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1);
**
** This function adds a scalar function (see header comment above
** fts3TokenizerFunc() in this file for details) and, if ENABLE_TABLE is
-** defined at compilation time, a temporary virtual table (see header
-** comment above struct HashTableVtab) to the database schema. Both
+** defined at compilation time, a temporary virtual table (see header
+** comment above struct HashTableVtab) to the database schema. Both
** provide read/write access to the contents of *pHash.
**
** The third argument to this function, zName, is used as the name
** of both the scalar and, if created, the virtual table.
*/
SQLITE_PRIVATE int sqlite3Fts3InitHashTable(
- sqlite3 *db,
- Fts3Hash *pHash,
+ sqlite3 *db,
+ Fts3Hash *pHash,
const char *zName
){
int rc = SQLITE_OK;
void *p = (void *)pHash;
- const int any = SQLITE_ANY;
+ const int any = SQLITE_UTF8|SQLITE_DIRECTONLY;
#ifdef SQLITE_TEST
char *zTest = 0;
@@ -167824,7 +194210,7 @@ static int simpleDestroy(sqlite3_tokenizer *pTokenizer){
/*
** 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
+** used to incrementally tokenize this string is returned in
** *ppCursor.
*/
static int simpleOpen(
@@ -167902,7 +194288,7 @@ static int simpleNext(
if( n>c->nTokenAllocated ){
char *pNew;
c->nTokenAllocated = n+20;
- pNew = sqlite3_realloc(c->pToken, c->nTokenAllocated);
+ pNew = sqlite3_realloc64(c->pToken, c->nTokenAllocated);
if( !pNew ) return SQLITE_NOMEM;
c->pToken = pNew;
}
@@ -167979,8 +194365,8 @@ SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(
**
** input = <string>
**
-** The virtual table module tokenizes this <string>, using the FTS3
-** tokenizer specified by the arguments to the CREATE VIRTUAL TABLE
+** The virtual table module tokenizes this <string>, using the FTS3
+** tokenizer specified by the arguments to the CREATE VIRTUAL TABLE
** statement and returns one row for each token in the result. With
** fields set as follows:
**
@@ -168049,7 +194435,7 @@ static int fts3tokQueryTokenizer(
/*
** The second argument, argv[], is an array of pointers to nul-terminated
-** strings. This function makes a copy of the array and strings into a
+** strings. This function makes a copy of the array and strings into a
** single block of memory. It then dequotes any of the strings that appear
** to be quoted.
**
@@ -168076,7 +194462,7 @@ static int fts3tokDequoteArray(
nByte += (int)(strlen(argv[i]) + 1);
}
- *pazDequote = azDequote = sqlite3_malloc(sizeof(char *)*argc + nByte);
+ *pazDequote = azDequote = sqlite3_malloc64(sizeof(char *)*argc + nByte);
if( azDequote==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -168105,7 +194491,7 @@ static int fts3tokDequoteArray(
** and xCreate are identical operations.
**
** argv[0]: module name
-** argv[1]: database name
+** argv[1]: database name
** argv[2]: table name
** argv[3]: first argument (tokenizer name)
*/
@@ -168142,7 +194528,8 @@ static int fts3tokConnectMethod(
assert( (rc==SQLITE_OK)==(pMod!=0) );
if( rc==SQLITE_OK ){
- const char * const *azArg = (const char * const *)&azDequote[1];
+ const char * const *azArg = 0;
+ if( nDequote>1 ) azArg = (const char * const *)&azDequote[1];
rc = pMod->xCreate((nDequote>1 ? nDequote-1 : 0), azArg, &pTok);
}
@@ -168185,16 +194572,16 @@ static int fts3tokDisconnectMethod(sqlite3_vtab *pVtab){
** xBestIndex - Analyze a WHERE and ORDER BY clause.
*/
static int fts3tokBestIndexMethod(
- sqlite3_vtab *pVTab,
+ sqlite3_vtab *pVTab,
sqlite3_index_info *pInfo
){
int i;
UNUSED_PARAMETER(pVTab);
for(i=0; i<pInfo->nConstraint; i++){
- if( pInfo->aConstraint[i].usable
- && pInfo->aConstraint[i].iColumn==0
- && pInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ
+ if( pInfo->aConstraint[i].usable
+ && pInfo->aConstraint[i].iColumn==0
+ && pInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ
){
pInfo->idxNum = 1;
pInfo->aConstraintUsage[i].argvIndex = 1;
@@ -168300,11 +194687,11 @@ static int fts3tokFilterMethod(
if( idxNum==1 ){
const char *zByte = (const char *)sqlite3_value_text(apVal[0]);
int nByte = sqlite3_value_bytes(apVal[0]);
- pCsr->zInput = sqlite3_malloc(nByte+1);
+ pCsr->zInput = sqlite3_malloc64(nByte+1);
if( pCsr->zInput==0 ){
rc = SQLITE_NOMEM;
}else{
- memcpy(pCsr->zInput, zByte, nByte);
+ if( nByte>0 ) memcpy(pCsr->zInput, zByte, nByte);
pCsr->zInput[nByte] = 0;
rc = pTab->pMod->xOpen(pTab->pTok, pCsr->zInput, nByte, &pCsr->pCsr);
if( rc==SQLITE_OK ){
@@ -168373,7 +194760,7 @@ static int fts3tokRowidMethod(
** Register the fts3tok module with database connection db. Return SQLITE_OK
** if successful or an error code if sqlite3_create_module() fails.
*/
-SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){
+SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash, void(*xDestroy)(void*)){
static const sqlite3_module fts3tok_module = {
0, /* iVersion */
fts3tokConnectMethod, /* xCreate */
@@ -168398,11 +194785,14 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
int rc; /* Return code */
- rc = sqlite3_create_module(db, "fts3tokenize", &fts3tok_module, (void*)pHash);
+ rc = sqlite3_create_module_v2(
+ db, "fts3tokenize", &fts3tok_module, (void*)pHash, xDestroy
+ );
return rc;
}
@@ -168425,7 +194815,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){
** This file is part of the SQLite FTS3 extension module. Specifically,
** this file contains code to insert, update and delete rows from FTS3
** tables. It also contains code to merge FTS3 b-tree segments. Some
-** of the sub-routines used to merge segments are also used by the query
+** of the sub-routines used to merge segments are also used by the query
** code in fts3.c.
*/
@@ -168435,13 +194825,13 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){
/* #include <string.h> */
/* #include <assert.h> */
/* #include <stdlib.h> */
-
+/* #include <stdio.h> */
#define FTS_MAX_APPENDABLE_HEIGHT 16
/*
** When full-text index nodes are loaded from disk, the buffer that they
-** are loaded into has the following number of bytes of padding at the end
+** are loaded into has the following number of bytes of padding at the end
** of it. i.e. if a full-text index node is 900 bytes in size, then a buffer
** of 920 bytes is allocated for it.
**
@@ -168458,10 +194848,10 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){
** method before retrieving all query results (as may happen, for example,
** if a query has a LIMIT clause).
**
-** Incremental loading is used for b-tree nodes FTS3_NODE_CHUNK_THRESHOLD
+** Incremental loading is used for b-tree nodes FTS3_NODE_CHUNK_THRESHOLD
** bytes and larger. Nodes are loaded in chunks of FTS3_NODE_CHUNKSIZE bytes.
-** The code is written so that the hard lower-limit for each of these values
-** is 1. Clearly such small values would be inefficient, but can be useful
+** The code is written so that the hard lower-limit for each of these values
+** is 1. Clearly such small values would be inefficient, but can be useful
** for testing purposes.
**
** If this module is built with SQLITE_TEST defined, these constants may
@@ -168474,12 +194864,12 @@ int test_fts3_node_chunk_threshold = (4*1024)*4;
# define FTS3_NODE_CHUNKSIZE test_fts3_node_chunksize
# define FTS3_NODE_CHUNK_THRESHOLD test_fts3_node_chunk_threshold
#else
-# define FTS3_NODE_CHUNKSIZE (4*1024)
+# define FTS3_NODE_CHUNKSIZE (4*1024)
# define FTS3_NODE_CHUNK_THRESHOLD (FTS3_NODE_CHUNKSIZE*4)
#endif
/*
-** The two values that may be meaningfully bound to the :1 parameter in
+** The values that may be meaningfully bound to the :1 parameter in
** statements SQL_REPLACE_STAT and SQL_SELECT_STAT.
*/
#define FTS_STAT_DOCTOTAL 0
@@ -168488,7 +194878,7 @@ int test_fts3_node_chunk_threshold = (4*1024)*4;
/*
** If FTS_LOG_MERGES is defined, call sqlite3_log() to report each automatic
-** and incremental merge operation that takes place. This is used for
+** and incremental merge operation that takes place. This is used for
** debugging FTS only, it should not usually be turned on in production
** systems.
*/
@@ -168574,7 +194964,7 @@ struct Fts3SegReader {
char *aDoclist; /* Pointer to doclist of current entry */
int nDoclist; /* Size of doclist in current entry */
- /* The following variables are used by fts3SegReaderNextDocid() to iterate
+ /* The following variables are used by fts3SegReaderNextDocid() to iterate
** through the current doclist (aDoclist/nDoclist).
*/
char *pOffsetList;
@@ -168619,11 +195009,11 @@ struct SegmentWriter {
** fts3NodeFree()
**
** When a b+tree is written to the database (either as a result of a merge
-** or the pending-terms table being flushed), leaves are written into the
+** or the pending-terms table being flushed), leaves are written into the
** database file as soon as they are completely populated. The interior of
** the tree is assembled in memory and written out only once all leaves have
** been populated and stored. This is Ok, as the b+-tree fanout is usually
-** very large, meaning that the interior of the tree consumes relatively
+** very large, meaning that the interior of the tree consumes relatively
** little memory.
*/
struct SegmentNode {
@@ -168644,7 +195034,7 @@ struct SegmentNode {
*/
#define SQL_DELETE_CONTENT 0
#define SQL_IS_EMPTY 1
-#define SQL_DELETE_ALL_CONTENT 2
+#define SQL_DELETE_ALL_CONTENT 2
#define SQL_DELETE_ALL_SEGMENTS 3
#define SQL_DELETE_ALL_SEGDIR 4
#define SQL_DELETE_ALL_DOCSIZE 5
@@ -168692,7 +195082,7 @@ struct SegmentNode {
** Otherwise, an SQLite error code is returned and *pp is set to 0.
**
** If argument apVal is not NULL, then it must point to an array with
-** at least as many entries as the requested statement has bound
+** at least as many entries as the requested statement has bound
** parameters. The values are bound to the statements parameters before
** returning.
*/
@@ -168716,7 +195106,7 @@ static int fts3SqlStmt(
/* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)",
/* 11 */ "REPLACE INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)",
- /* Return segments in order from oldest to newest.*/
+ /* Return segments in order from oldest to newest.*/
/* 12 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
"FROM %Q.'%q_segdir' WHERE level = ? ORDER BY idx ASC",
/* 13 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
@@ -168747,13 +195137,15 @@ static int fts3SqlStmt(
** returns zero rows. */
/* 28 */ "SELECT level, count(*) AS cnt FROM %Q.'%q_segdir' "
" GROUP BY level HAVING cnt>=?"
- " ORDER BY (level %% 1024) ASC LIMIT 1",
+ " ORDER BY (level %% 1024) ASC, 2 DESC LIMIT 1",
/* Estimate the upper limit on the number of leaf nodes in a new segment
-** created by merging the oldest :2 segments from absolute level :1. See
+** created by merging the oldest :2 segments from absolute level :1. See
** function sqlite3Fts3Incrmerge() for details. */
/* 29 */ "SELECT 2 * total(1 + leaves_end_block - start_block) "
- " FROM %Q.'%q_segdir' WHERE level = ? AND idx < ?",
+ " FROM (SELECT * FROM %Q.'%q_segdir' "
+ " WHERE level = ? ORDER BY idx ASC LIMIT ?"
+ " )",
/* SQL_DELETE_SEGDIR_ENTRY
** Delete the %_segdir entry on absolute level :1 with index :2. */
@@ -168765,7 +195157,7 @@ static int fts3SqlStmt(
/* 31 */ "UPDATE %Q.'%q_segdir' SET idx = ? WHERE level=? AND idx=?",
/* SQL_SELECT_SEGDIR
-** Read a single entry from the %_segdir table. The entry from absolute
+** Read a single entry from the %_segdir table. The entry from absolute
** level :1 with index value :2. */
/* 32 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
"FROM %Q.'%q_segdir' WHERE level = ? AND idx = ?",
@@ -168789,7 +195181,7 @@ static int fts3SqlStmt(
** Return the largest relative level in the FTS index or indexes. */
/* 36 */ "SELECT max( level %% 1024 ) FROM %Q.'%q_segdir'",
- /* Return segments in order from oldest to newest.*/
+ /* Return segments in order from oldest to newest.*/
/* 37 */ "SELECT level, idx, end_block "
"FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ? "
"ORDER BY level DESC, idx ASC",
@@ -168805,13 +195197,15 @@ static int fts3SqlStmt(
assert( SizeofArray(azSql)==SizeofArray(p->aStmt) );
assert( eStmt<SizeofArray(azSql) && eStmt>=0 );
-
+
pStmt = p->aStmt[eStmt];
if( !pStmt ){
+ int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB;
char *zSql;
if( eStmt==SQL_CONTENT_INSERT ){
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
}else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
+ f &= ~SQLITE_PREPARE_NO_VTAB;
zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
}else{
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
@@ -168819,8 +195213,7 @@ static int fts3SqlStmt(
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
- rc = sqlite3_prepare_v3(p->db, zSql, -1, SQLITE_PREPARE_PERSISTENT,
- &pStmt, NULL);
+ rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL);
sqlite3_free(zSql);
assert( rc==SQLITE_OK || pStmt==0 );
p->aStmt[eStmt] = pStmt;
@@ -168909,7 +195302,7 @@ static void fts3SqlExec(
sqlite3_stmt *pStmt;
int rc;
if( *pRC ) return;
- rc = fts3SqlStmt(p, eStmt, &pStmt, apVal);
+ rc = fts3SqlStmt(p, eStmt, &pStmt, apVal);
if( rc==SQLITE_OK ){
sqlite3_step(pStmt);
rc = sqlite3_reset(pStmt);
@@ -168919,22 +195312,22 @@ static void fts3SqlExec(
/*
-** This function ensures that the caller has obtained an exclusive
-** shared-cache table-lock on the %_segdir table. This is required before
+** This function ensures that the caller has obtained an exclusive
+** shared-cache table-lock on the %_segdir table. This is required before
** writing data to the fts3 table. If this lock is not acquired first, then
** the caller may end up attempting to take this lock as part of committing
-** a transaction, causing SQLite to return SQLITE_LOCKED or
+** a transaction, causing SQLite to return SQLITE_LOCKED or
** LOCKED_SHAREDCACHEto a COMMIT command.
**
-** It is best to avoid this because if FTS3 returns any error when
-** committing a transaction, the whole transaction will be rolled back.
-** And this is not what users expect when they get SQLITE_LOCKED_SHAREDCACHE.
-** It can still happen if the user locks the underlying tables directly
+** It is best to avoid this because if FTS3 returns any error when
+** committing a transaction, the whole transaction will be rolled back.
+** And this is not what users expect when they get SQLITE_LOCKED_SHAREDCACHE.
+** It can still happen if the user locks the underlying tables directly
** instead of accessing them via FTS.
*/
static int fts3Writelock(Fts3Table *p){
int rc = SQLITE_OK;
-
+
if( p->nPendingData==0 ){
sqlite3_stmt *pStmt;
rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pStmt, 0);
@@ -168951,7 +195344,7 @@ static int fts3Writelock(Fts3Table *p){
/*
** FTS maintains a separate indexes for each language-id (a 32-bit integer).
** Within each language id, a separate index is maintained to store the
-** document terms, and each configured prefix size (configured the FTS
+** document terms, and each configured prefix size (configured the FTS
** "prefix=" option). And each index consists of multiple levels ("relative
** levels").
**
@@ -168961,14 +195354,14 @@ static int fts3Writelock(Fts3Table *p){
** separate component values into the single 64-bit integer value that
** can be used to query the %_segdir table.
**
-** Specifically, each language-id/index combination is allocated 1024
+** Specifically, each language-id/index combination is allocated 1024
** 64-bit integer level values ("absolute levels"). The main terms index
** for language-id 0 is allocate values 0-1023. The first prefix index
** (if any) for language-id 0 is allocated values 1024-2047. And so on.
** Language 1 indexes are allocated immediately following language 0.
**
** So, for a system with nPrefix prefix indexes configured, the block of
-** absolute levels that corresponds to language-id iLangid and index
+** absolute levels that corresponds to language-id iLangid and index
** iIndex starts at absolute level ((iLangid * (nPrefix+1) + iIndex) * 1024).
*/
static sqlite3_int64 getAbsoluteLevel(
@@ -168978,7 +195371,7 @@ static sqlite3_int64 getAbsoluteLevel(
int iLevel /* Level of segments */
){
sqlite3_int64 iBase; /* First absolute level for iLangid/iIndex */
- assert( iLangid>=0 );
+ assert_fts3_nc( iLangid>=0 );
assert( p->nIndex>0 );
assert( iIndex>=0 && iIndex<p->nIndex );
@@ -168989,7 +195382,7 @@ static sqlite3_int64 getAbsoluteLevel(
/*
** Set *ppStmt to a statement handle that may be used to iterate through
** all rows in the %_segdir table, from oldest to newest. If successful,
-** return SQLITE_OK. If an error occurs while preparing the statement,
+** return SQLITE_OK. If an error occurs while preparing the statement,
** return an SQLite error code.
**
** There is only ever one instance of this SQL statement compiled for
@@ -169020,16 +195413,16 @@ SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(
if( iLevel<0 ){
/* "SELECT * FROM %_segdir WHERE level BETWEEN ? AND ? ORDER BY ..." */
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE, &pStmt, 0);
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK ){
sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex, 0));
- sqlite3_bind_int64(pStmt, 2,
+ sqlite3_bind_int64(pStmt, 2,
getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1)
);
}
}else{
/* "SELECT * FROM %_segdir WHERE level = ? ORDER BY ..." */
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0);
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK ){
sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex,iLevel));
}
}
@@ -169058,7 +195451,7 @@ static int fts3PendingListAppendVarint(
/* Allocate or grow the PendingList as required. */
if( !p ){
- p = sqlite3_malloc(sizeof(*p) + 100);
+ p = sqlite3_malloc64(sizeof(*p) + 100);
if( !p ){
return SQLITE_NOMEM;
}
@@ -169067,14 +195460,14 @@ static int fts3PendingListAppendVarint(
p->nData = 0;
}
else if( p->nData+FTS3_VARINT_MAX+1>p->nSpace ){
- int nNew = p->nSpace * 2;
- p = sqlite3_realloc(p, sizeof(*p) + nNew);
+ i64 nNew = p->nSpace * 2;
+ p = sqlite3_realloc64(p, sizeof(*p) + nNew);
if( !p ){
sqlite3_free(*pp);
*pp = 0;
return SQLITE_NOMEM;
}
- p->nSpace = nNew;
+ p->nSpace = (int)nNew;
p->aData = (char *)&p[1];
}
@@ -169107,7 +195500,7 @@ static int fts3PendingListAppend(
assert( !p || p->iLastDocid<=iDocid );
if( !p || p->iLastDocid!=iDocid ){
- sqlite3_int64 iDelta = iDocid - (p ? p->iLastDocid : 0);
+ u64 iDelta = (u64)iDocid - (u64)(p ? p->iLastDocid : 0);
if( p ){
assert( p->nData<p->nSpace );
assert( p->aData[p->nData]==0 );
@@ -169173,7 +195566,7 @@ static int fts3PendingTermsAddOne(
}
if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){
if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){
- /* Malloc failed while inserting the new entry. This can only
+ /* Malloc failed while inserting the new entry. This can only
** happen if there was no previous entry for this token.
*/
assert( 0==fts3HashFind(pHash, zToken, nToken) );
@@ -169219,7 +195612,7 @@ static int fts3PendingTermsAdd(
assert( pTokenizer && pModule );
/* If the user has inserted a NULL value, this function may be called with
- ** zText==0. In this case, add zero token entries to the hash table and
+ ** zText==0. In this case, add zero token entries to the hash table and
** return early. */
if( zText==0 ){
*pnWord = 0;
@@ -169250,8 +195643,8 @@ static int fts3PendingTermsAdd(
rc = fts3PendingTermsAddOne(
p, iCol, iPos, &p->aIndex[0].hPending, zToken, nToken
);
-
- /* Add the term to each of the prefix indexes that it is not too
+
+ /* Add the term to each of the prefix indexes that it is not too
** short for. */
for(i=1; rc==SQLITE_OK && i<p->nIndex; i++){
struct Fts3Index *pIndex = &p->aIndex[i];
@@ -169267,8 +195660,8 @@ static int fts3PendingTermsAdd(
return (rc==SQLITE_DONE ? SQLITE_OK : rc);
}
-/*
-** Calling this function indicates that subsequent calls to
+/*
+** Calling this function indicates that subsequent calls to
** fts3PendingTermsAdd() are to add term/position-list pairs for the
** contents of the document with docid iDocid.
*/
@@ -169287,10 +195680,10 @@ static int fts3PendingTermsDocid(
** buffer was half empty, that would let the less frequent terms
** generate longer doclists.
*/
- if( iDocid<p->iPrevDocid
+ if( iDocid<p->iPrevDocid
|| (iDocid==p->iPrevDocid && p->bPrevDelete==0)
|| p->iPrevLangid!=iLangid
- || p->nPendingData>p->nMaxPendingData
+ || p->nPendingData>p->nMaxPendingData
){
int rc = sqlite3Fts3PendingTermsFlush(p);
if( rc!=SQLITE_OK ) return rc;
@@ -169302,7 +195695,7 @@ static int fts3PendingTermsDocid(
}
/*
-** Discard the contents of the pending-terms hash tables.
+** Discard the contents of the pending-terms hash tables.
*/
SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *p){
int i;
@@ -169327,9 +195720,9 @@ SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *p){
** fts3InsertData(). Parameter iDocid is the docid of the new row.
*/
static int fts3InsertTerms(
- Fts3Table *p,
- int iLangid,
- sqlite3_value **apVal,
+ Fts3Table *p,
+ int iLangid,
+ sqlite3_value **apVal,
u32 *aSz
){
int i; /* Iterator variable */
@@ -169392,7 +195785,7 @@ static int fts3InsertData(
rc = fts3SqlStmt(p, SQL_CONTENT_INSERT, &pContentInsert, &apVal[1]);
if( rc==SQLITE_OK && p->zLanguageid ){
rc = sqlite3_bind_int(
- pContentInsert, p->nColumn+2,
+ pContentInsert, p->nColumn+2,
sqlite3_value_int(apVal[p->nColumn+4])
);
}
@@ -169419,8 +195812,8 @@ static int fts3InsertData(
if( rc!=SQLITE_OK ) return rc;
}
- /* Execute the statement to insert the record. Set *piDocid to the
- ** new docid value.
+ /* Execute the statement to insert the record. Set *piDocid to the
+ ** new docid value.
*/
sqlite3_step(pContentInsert);
rc = sqlite3_reset(pContentInsert);
@@ -169470,7 +195863,7 @@ static int langidFromSelect(Fts3Table *p, sqlite3_stmt *pSelect){
** (an integer) of a row about to be deleted. Remove all terms from the
** full-text index.
*/
-static void fts3DeleteTerms(
+static void fts3DeleteTerms(
int *pRC, /* Result code */
Fts3Table *p, /* The FTS table to delete from */
sqlite3_value *pRowid, /* The docid to be deleted */
@@ -169517,7 +195910,7 @@ static void fts3DeleteTerms(
*/
static int fts3SegmentMerge(Fts3Table *, int, int, int);
-/*
+/*
** This function allocates a new level iLevel index in the segdir table.
** Usually, indexes are allocated within a level sequentially starting
** with 0, so the allocated index is one greater than the value returned
@@ -169526,17 +195919,17 @@ static int fts3SegmentMerge(Fts3Table *, int, int, int);
** SELECT max(idx) FROM %_segdir WHERE level = :iLevel
**
** However, if there are already FTS3_MERGE_COUNT indexes at the requested
-** level, they are merged into a single level (iLevel+1) segment and the
+** level, they are merged into a single level (iLevel+1) segment and the
** allocated index is 0.
**
** If successful, *piIdx is set to the allocated index slot and SQLITE_OK
** returned. Otherwise, an SQLite error code is returned.
*/
static int fts3AllocateSegdirIdx(
- Fts3Table *p,
+ Fts3Table *p,
int iLangid, /* Language id */
int iIndex, /* Index for p->aIndex */
- int iLevel,
+ int iLevel,
int *piIdx
){
int rc; /* Return Code */
@@ -169564,7 +195957,7 @@ static int fts3AllocateSegdirIdx(
** segment and allocate (newly freed) index 0 at level iLevel. Otherwise,
** if iNext is less than FTS3_MERGE_COUNT, allocate index iNext.
*/
- if( iNext>=FTS3_MERGE_COUNT ){
+ if( iNext>=MergeCount(p) ){
fts3LogMerge(16, getAbsoluteLevel(p, iLangid, iIndex, iLevel));
rc = fts3SegmentMerge(p, iLangid, iIndex, iLevel);
*piIdx = 0;
@@ -169584,7 +195977,7 @@ static int fts3AllocateSegdirIdx(
** This function reads data from a single row of the %_segments table. The
** specific row is identified by the iBlockid parameter. If paBlob is not
** NULL, then a buffer is allocated using sqlite3_malloc() and populated
-** with the contents of the blob stored in the "block" column of the
+** with the contents of the blob stored in the "block" column of the
** identified table row is. Whether or not paBlob is NULL, *pnBlob is set
** to the size of the blob in bytes before returning.
**
@@ -169631,7 +196024,7 @@ SQLITE_PRIVATE int sqlite3Fts3ReadBlock(
int nByte = sqlite3_blob_bytes(p->pSegments);
*pnBlob = nByte;
if( paBlob ){
- char *aByte = sqlite3_malloc(nByte + FTS3_NODE_PADDING);
+ char *aByte = sqlite3_malloc64((i64)nByte + FTS3_NODE_PADDING);
if( !aByte ){
rc = SQLITE_NOMEM;
}else{
@@ -169648,6 +196041,8 @@ SQLITE_PRIVATE int sqlite3Fts3ReadBlock(
}
*paBlob = aByte;
}
+ }else if( rc==SQLITE_ERROR ){
+ rc = FTS_CORRUPT_VTAB;
}
return rc;
@@ -169661,14 +196056,14 @@ SQLITE_PRIVATE void sqlite3Fts3SegmentsClose(Fts3Table *p){
sqlite3_blob_close(p->pSegments);
p->pSegments = 0;
}
-
+
static int fts3SegReaderIncrRead(Fts3SegReader *pReader){
int nRead; /* Number of bytes to read */
int rc; /* Return code */
nRead = MIN(pReader->nNode - pReader->nPopulate, FTS3_NODE_CHUNKSIZE);
rc = sqlite3_blob_read(
- pReader->pBlob,
+ pReader->pBlob,
&pReader->aNode[pReader->nPopulate],
nRead,
pReader->nPopulate
@@ -169688,10 +196083,10 @@ static int fts3SegReaderIncrRead(Fts3SegReader *pReader){
static int fts3SegReaderRequire(Fts3SegReader *pReader, char *pFrom, int nByte){
int rc = SQLITE_OK;
- assert( !pReader->pBlob
+ assert( !pReader->pBlob
|| (pFrom>=pReader->aNode && pFrom<&pReader->aNode[pReader->nNode])
);
- while( pReader->pBlob && rc==SQLITE_OK
+ while( pReader->pBlob && rc==SQLITE_OK
&& (pFrom - pReader->aNode + nByte)>pReader->nPopulate
){
rc = fts3SegReaderIncrRead(pReader);
@@ -169717,7 +196112,7 @@ static void fts3SegReaderSetEof(Fts3SegReader *pSeg){
** SQLITE_DONE. Otherwise, an SQLite error code.
*/
static int fts3SegReaderNext(
- Fts3Table *p,
+ Fts3Table *p,
Fts3SegReader *pReader,
int bIncr
){
@@ -169742,9 +196137,19 @@ static int fts3SegReaderNext(
char *aCopy;
PendingList *pList = (PendingList *)fts3HashData(pElem);
int nCopy = pList->nData+1;
- pReader->zTerm = (char *)fts3HashKey(pElem);
- pReader->nTerm = fts3HashKeysize(pElem);
- aCopy = (char*)sqlite3_malloc(nCopy);
+
+ int nTerm = fts3HashKeysize(pElem);
+ if( (nTerm+1)>pReader->nTermAlloc ){
+ sqlite3_free(pReader->zTerm);
+ pReader->zTerm = (char*)sqlite3_malloc64(((i64)nTerm+1)*2);
+ if( !pReader->zTerm ) return SQLITE_NOMEM;
+ pReader->nTermAlloc = (nTerm+1)*2;
+ }
+ memcpy(pReader->zTerm, fts3HashKey(pElem), nTerm);
+ pReader->zTerm[nTerm] = '\0';
+ pReader->nTerm = nTerm;
+
+ aCopy = (char*)sqlite3_malloc64(nCopy);
if( !aCopy ) return SQLITE_NOMEM;
memcpy(aCopy, pList->aData, nCopy);
pReader->nNode = pReader->nDoclist = nCopy;
@@ -169757,15 +196162,17 @@ static int fts3SegReaderNext(
fts3SegReaderSetEof(pReader);
- /* If iCurrentBlock>=iLeafEndBlock, this is an EOF condition. All leaf
+ /* If iCurrentBlock>=iLeafEndBlock, this is an EOF condition. All leaf
** blocks have already been traversed. */
- assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock );
+#ifdef CORRUPT_DB
+ assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock || CORRUPT_DB );
+#endif
if( pReader->iCurrentBlock>=pReader->iLeafEndBlock ){
return SQLITE_OK;
}
rc = sqlite3Fts3ReadBlock(
- p, ++pReader->iCurrentBlock, &pReader->aNode, &pReader->nNode,
+ p, ++pReader->iCurrentBlock, &pReader->aNode, &pReader->nNode,
(bIncr ? &pReader->nPopulate : 0)
);
if( rc!=SQLITE_OK ) return rc;
@@ -169781,14 +196188,14 @@ static int fts3SegReaderNext(
rc = fts3SegReaderRequire(pReader, pNext, FTS3_VARINT_MAX*2);
if( rc!=SQLITE_OK ) return rc;
-
- /* Because of the FTS3_NODE_PADDING bytes of padding, the following is
+
+ /* Because of the FTS3_NODE_PADDING bytes of padding, the following is
** safe (no risk of overread) even if the node data is corrupted. */
pNext += fts3GetVarint32(pNext, &nPrefix);
pNext += fts3GetVarint32(pNext, &nSuffix);
- if( nSuffix<=0
+ if( nSuffix<=0
|| (&pReader->aNode[pReader->nNode] - pNext)<nSuffix
- || nPrefix>pReader->nTermAlloc
+ || nPrefix>pReader->nTerm
){
return FTS_CORRUPT_VTAB;
}
@@ -169817,11 +196224,12 @@ static int fts3SegReaderNext(
pReader->pOffsetList = 0;
/* Check that the doclist does not appear to extend past the end of the
- ** b-tree node. And that the final byte of the doclist is 0x00. If either
+ ** b-tree node. And that the final byte of the doclist is 0x00. If either
** of these statements is untrue, then the data structure is corrupt.
*/
- if( (&pReader->aNode[pReader->nNode] - pReader->aDoclist)<pReader->nDoclist
+ if( pReader->nDoclist > pReader->nNode-(pReader->aDoclist-pReader->aNode)
|| (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
+ || pReader->nDoclist==0
){
return FTS_CORRUPT_VTAB;
}
@@ -169841,7 +196249,7 @@ static int fts3SegReaderFirstDocid(Fts3Table *pTab, Fts3SegReader *pReader){
pReader->iDocid = 0;
pReader->nOffsetList = 0;
sqlite3Fts3DoclistPrev(0,
- pReader->aDoclist, pReader->nDoclist, &pReader->pOffsetList,
+ pReader->aDoclist, pReader->nDoclist, &pReader->pOffsetList,
&pReader->iDocid, &pReader->nOffsetList, &bEof
);
}else{
@@ -169857,8 +196265,8 @@ static int fts3SegReaderFirstDocid(Fts3Table *pTab, Fts3SegReader *pReader){
/*
** Advance the SegReader to point to the next docid in the doclist
** associated with the current term.
-**
-** If arguments ppOffsetList and pnOffsetList are not NULL, then
+**
+** If arguments ppOffsetList and pnOffsetList are not NULL, then
** *ppOffsetList is set to point to the first column-offset list
** in the doclist entry (i.e. immediately past the docid varint).
** *pnOffsetList is set to the length of the set of column-offset
@@ -169901,22 +196309,22 @@ static int fts3SegReaderNextDocid(
** following block advances it to point one byte past the end of
** the same offset list. */
while( 1 ){
-
+
/* The following line of code (and the "p++" below the while() loop) is
- ** normally all that is required to move pointer p to the desired
+ ** normally all that is required to move pointer p to the desired
** position. The exception is if this node is being loaded from disk
** incrementally and pointer "p" now points to the first byte past
** the populated part of pReader->aNode[].
*/
while( *p | c ) c = *p++ & 0x80;
assert( *p==0 );
-
+
if( pReader->pBlob==0 || p<&pReader->aNode[pReader->nPopulate] ) break;
rc = fts3SegReaderIncrRead(pReader);
if( rc!=SQLITE_OK ) return rc;
}
p++;
-
+
/* If required, populate the output variables with a pointer to and the
** size of the previous offset-list.
*/
@@ -169927,7 +196335,7 @@ static int fts3SegReaderNextDocid(
/* List may have been edited in place by fts3EvalNearTrim() */
while( p<pEnd && *p==0 ) p++;
-
+
/* If there are no more entries in the doclist, set pOffsetList to
** NULL. Otherwise, set Fts3SegReader.iDocid to the next docid and
** Fts3SegReader.pOffsetList to point to the next offset list before
@@ -169938,23 +196346,23 @@ static int fts3SegReaderNextDocid(
}else{
rc = fts3SegReaderRequire(pReader, p, FTS3_VARINT_MAX);
if( rc==SQLITE_OK ){
- sqlite3_int64 iDelta;
- pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta);
+ u64 iDelta;
+ pReader->pOffsetList = p + sqlite3Fts3GetVarintU(p, &iDelta);
if( pTab->bDescIdx ){
- pReader->iDocid -= iDelta;
+ pReader->iDocid = (i64)((u64)pReader->iDocid - iDelta);
}else{
- pReader->iDocid += iDelta;
+ pReader->iDocid = (i64)((u64)pReader->iDocid + iDelta);
}
}
}
}
- return SQLITE_OK;
+ return rc;
}
SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(
- Fts3Cursor *pCsr,
+ Fts3Cursor *pCsr,
Fts3MultiSegReader *pMsr,
int *pnOvfl
){
@@ -169969,8 +196377,8 @@ SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(
for(ii=0; rc==SQLITE_OK && ii<pMsr->nSegment; ii++){
Fts3SegReader *pReader = pMsr->apSegment[ii];
- if( !fts3SegReaderIsPending(pReader)
- && !fts3SegReaderIsRootOnly(pReader)
+ if( !fts3SegReaderIsPending(pReader)
+ && !fts3SegReaderIsRootOnly(pReader)
){
sqlite3_int64 jj;
for(jj=pReader->iStartBlock; jj<=pReader->iLeafEndBlock; jj++){
@@ -169988,14 +196396,12 @@ SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(
}
/*
-** Free all allocations associated with the iterator passed as the
+** Free all allocations associated with the iterator passed as the
** second argument.
*/
SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){
if( pReader ){
- if( !fts3SegReaderIsPending(pReader) ){
- sqlite3_free(pReader->zTerm);
- }
+ sqlite3_free(pReader->zTerm);
if( !fts3SegReaderIsRootOnly(pReader) ){
sqlite3_free(pReader->aNode);
}
@@ -170020,12 +196426,17 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(
Fts3SegReader *pReader; /* Newly allocated SegReader object */
int nExtra = 0; /* Bytes to allocate segment root node */
- assert( iStartLeaf<=iEndLeaf );
+ assert( zRoot!=0 || nRoot==0 );
+#ifdef CORRUPT_DB
+ assert( zRoot!=0 || CORRUPT_DB );
+#endif
+
if( iStartLeaf==0 ){
+ if( iEndLeaf!=0 ) return FTS_CORRUPT_VTAB;
nExtra = nRoot + FTS3_NODE_PADDING;
}
- pReader = (Fts3SegReader *)sqlite3_malloc(sizeof(Fts3SegReader) + nExtra);
+ pReader = (Fts3SegReader *)sqlite3_malloc64(sizeof(Fts3SegReader) + nExtra);
if( !pReader ){
return SQLITE_NOMEM;
}
@@ -170041,7 +196452,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(
pReader->aNode = (char *)&pReader[1];
pReader->rootOnly = 1;
pReader->nNode = nRoot;
- memcpy(pReader->aNode, zRoot, nRoot);
+ if( nRoot ) memcpy(pReader->aNode, zRoot, nRoot);
memset(&pReader->aNode[nRoot], 0, FTS3_NODE_PADDING);
}else{
pReader->iCurrentBlock = iStartLeaf-1;
@@ -170117,7 +196528,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
if( nElem==nAlloc ){
Fts3HashElem **aElem2;
nAlloc += 16;
- aElem2 = (Fts3HashElem **)sqlite3_realloc(
+ aElem2 = (Fts3HashElem **)sqlite3_realloc64(
aElem, nAlloc*sizeof(Fts3HashElem *)
);
if( !aElem2 ){
@@ -170142,7 +196553,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
}else{
/* The query is a simple term lookup that matches at most one term in
- ** the index. All that is required is a straight hash-lookup.
+ ** the index. All that is required is a straight hash-lookup.
**
** Because the stack address of pE may be accessed via the aElem pointer
** below, the "Fts3HashElem *pE" must be declared so that it is valid
@@ -170156,8 +196567,9 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
}
if( nElem>0 ){
- int nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *);
- pReader = (Fts3SegReader *)sqlite3_malloc(nByte);
+ sqlite3_int64 nByte;
+ nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *);
+ pReader = (Fts3SegReader *)sqlite3_malloc64(nByte);
if( !pReader ){
rc = SQLITE_NOMEM;
}else{
@@ -170176,7 +196588,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
}
/*
-** Compare the entries pointed to by two Fts3SegReader structures.
+** Compare the entries pointed to by two Fts3SegReader structures.
** Comparison is as follows:
**
** 1) EOF is greater than not EOF.
@@ -170205,7 +196617,7 @@ static int fts3SegReaderCmp(Fts3SegReader *pLhs, Fts3SegReader *pRhs){
if( rc==0 ){
rc = pRhs->iIdx - pLhs->iIdx;
}
- assert( rc!=0 );
+ assert_fts3_nc( rc!=0 );
return rc;
}
@@ -170247,7 +196659,7 @@ static int fts3SegReaderDoclistCmpRev(Fts3SegReader *pLhs, Fts3SegReader *pRhs){
/*
** Compare the term that the Fts3SegReader object passed as the first argument
-** points to with the term specified by arguments zTerm and nTerm.
+** points to with the term specified by arguments zTerm and nTerm.
**
** If the pSeg iterator is already at EOF, return 0. Otherwise, return
** -ve if the pSeg term is less than zTerm/nTerm, 0 if the two terms are
@@ -170308,7 +196720,7 @@ static void fts3SegReaderSort(
#endif
}
-/*
+/*
** Insert a record into the %_segments table.
*/
static int fts3WriteSegment(
@@ -170350,7 +196762,7 @@ SQLITE_PRIVATE int sqlite3Fts3MaxLevel(Fts3Table *p, int *pnMax){
return rc;
}
-/*
+/*
** Insert a record into the %_segdir table.
*/
static int fts3WriteSegdir(
@@ -170388,7 +196800,7 @@ static int fts3WriteSegdir(
/*
** Return the size of the common prefix (if any) shared by zPrev and
-** zNext, in bytes. For example,
+** zNext, in bytes. For example,
**
** fts3PrefixCompress("abc", 3, "abcdef", 6) // returns 3
** fts3PrefixCompress("abX", 3, "abcdef", 6) // returns 2
@@ -170401,8 +196813,8 @@ static int fts3PrefixCompress(
int nNext /* Size of buffer zNext in bytes */
){
int n;
- UNUSED_PARAMETER(nNext);
- for(n=0; n<nPrev && zPrev[n]==zNext[n]; n++);
+ for(n=0; n<nPrev && n<nNext && zPrev[n]==zNext[n]; n++);
+ assert_fts3_nc( n<nNext );
return n;
}
@@ -170412,7 +196824,7 @@ static int fts3PrefixCompress(
*/
static int fts3NodeAddTerm(
Fts3Table *p, /* Virtual table handle */
- SegmentNode **ppTree, /* IN/OUT: SegmentNode handle */
+ SegmentNode **ppTree, /* IN/OUT: SegmentNode handle */
int isCopyTerm, /* True if zTerm/nTerm is transient */
const char *zTerm, /* Pointer to buffer containing term */
int nTerm /* Size of term in bytes */
@@ -170421,7 +196833,7 @@ static int fts3NodeAddTerm(
int rc;
SegmentNode *pNew;
- /* First try to append the term to the current node. Return early if
+ /* First try to append the term to the current node. Return early if
** this is possible.
*/
if( pTree ){
@@ -170433,6 +196845,11 @@ static int fts3NodeAddTerm(
nPrefix = fts3PrefixCompress(pTree->zTerm, pTree->nTerm, zTerm, nTerm);
nSuffix = nTerm-nPrefix;
+ /* If nSuffix is zero or less, then zTerm/nTerm must be a prefix of
+ ** pWriter->zTerm/pWriter->nTerm. i.e. must be equal to or less than when
+ ** compared with BINARY collation. This indicates corruption. */
+ if( nSuffix<=0 ) return FTS_CORRUPT_VTAB;
+
nReq += sqlite3Fts3VarintLen(nPrefix)+sqlite3Fts3VarintLen(nSuffix)+nSuffix;
if( nReq<=p->nNodeSize || !pTree->zTerm ){
@@ -170441,11 +196858,11 @@ static int fts3NodeAddTerm(
** and the static node buffer (p->nNodeSize bytes) is not large
** enough. Use a separately malloced buffer instead This wastes
** p->nNodeSize bytes, but since this scenario only comes about when
- ** the database contain two terms that share a prefix of almost 2KB,
- ** this is not expected to be a serious problem.
+ ** the database contain two terms that share a prefix of almost 2KB,
+ ** this is not expected to be a serious problem.
*/
assert( pTree->aData==(char *)&pTree[1] );
- pTree->aData = (char *)sqlite3_malloc(nReq);
+ pTree->aData = (char *)sqlite3_malloc64(nReq);
if( !pTree->aData ){
return SQLITE_NOMEM;
}
@@ -170463,7 +196880,7 @@ static int fts3NodeAddTerm(
if( isCopyTerm ){
if( pTree->nMalloc<nTerm ){
- char *zNew = sqlite3_realloc(pTree->zMalloc, nTerm*2);
+ char *zNew = sqlite3_realloc64(pTree->zMalloc, (i64)nTerm*2);
if( !zNew ){
return SQLITE_NOMEM;
}
@@ -170486,10 +196903,10 @@ static int fts3NodeAddTerm(
** If this is the first node in the tree, the term is added to it.
**
** Otherwise, the term is not added to the new node, it is left empty for
- ** now. Instead, the term is inserted into the parent of pTree. If pTree
+ ** now. Instead, the term is inserted into the parent of pTree. If pTree
** has no parent, one is created here.
*/
- pNew = (SegmentNode *)sqlite3_malloc(sizeof(SegmentNode) + p->nNodeSize);
+ pNew = (SegmentNode *)sqlite3_malloc64(sizeof(SegmentNode) + p->nNodeSize);
if( !pNew ){
return SQLITE_NOMEM;
}
@@ -170511,7 +196928,7 @@ static int fts3NodeAddTerm(
pTree->zMalloc = 0;
}else{
pNew->pLeftmost = pNew;
- rc = fts3NodeAddTerm(p, &pNew, isCopyTerm, zTerm, nTerm);
+ rc = fts3NodeAddTerm(p, &pNew, isCopyTerm, zTerm, nTerm);
}
*ppTree = pNew;
@@ -170522,8 +196939,8 @@ static int fts3NodeAddTerm(
** Helper function for fts3NodeWrite().
*/
static int fts3TreeFinishNode(
- SegmentNode *pTree,
- int iHeight,
+ SegmentNode *pTree,
+ int iHeight,
sqlite3_int64 iLeftChild
){
int nStart;
@@ -170536,15 +196953,15 @@ static int fts3TreeFinishNode(
/*
** Write the buffer for the segment node pTree and all of its peers to the
-** database. Then call this function recursively to write the parent of
-** pTree and its peers to the database.
+** database. Then call this function recursively to write the parent of
+** pTree and its peers to the database.
**
** Except, if pTree is a root node, do not write it to the database. Instead,
** set output variables *paRoot and *pnRoot to contain the root node.
**
** If successful, SQLITE_OK is returned and output variable *piLast is
** set to the largest blockid written to the database (or zero if no
-** blocks were written to the db). Otherwise, an SQLite error code is
+** blocks were written to the db). Otherwise, an SQLite error code is
** returned.
*/
static int fts3NodeWrite(
@@ -170572,7 +196989,7 @@ static int fts3NodeWrite(
for(pIter=pTree->pLeftmost; pIter && rc==SQLITE_OK; pIter=pIter->pRight){
int nStart = fts3TreeFinishNode(pIter, iHeight, iNextLeaf);
int nWrite = pIter->nData - nStart;
-
+
rc = fts3WriteSegment(p, iNextFree, &pIter->aData[nStart], nWrite);
iNextFree++;
iNextLeaf += (pIter->nEntry+1);
@@ -170618,7 +197035,7 @@ static void fts3NodeFree(SegmentNode *pTree){
*/
static int fts3SegWriterAdd(
Fts3Table *p, /* Virtual table handle */
- SegmentWriter **ppWriter, /* IN/OUT: SegmentWriter handle */
+ SegmentWriter **ppWriter, /* IN/OUT: SegmentWriter handle */
int isCopyTerm, /* True if buffer zTerm must be copied */
const char *zTerm, /* Pointer to buffer containing term */
int nTerm, /* Size of term in bytes */
@@ -170627,7 +197044,7 @@ static int fts3SegWriterAdd(
){
int nPrefix; /* Size of term prefix in bytes */
int nSuffix; /* Size of term suffix in bytes */
- int nReq; /* Number of bytes required on leaf page */
+ i64 nReq; /* Number of bytes required on leaf page */
int nData;
SegmentWriter *pWriter = *ppWriter;
@@ -170636,13 +197053,13 @@ static int fts3SegWriterAdd(
sqlite3_stmt *pStmt;
/* Allocate the SegmentWriter structure */
- pWriter = (SegmentWriter *)sqlite3_malloc(sizeof(SegmentWriter));
+ pWriter = (SegmentWriter *)sqlite3_malloc64(sizeof(SegmentWriter));
if( !pWriter ) return SQLITE_NOMEM;
memset(pWriter, 0, sizeof(SegmentWriter));
*ppWriter = pWriter;
/* Allocate a buffer in which to accumulate data */
- pWriter->aData = (char *)sqlite3_malloc(p->nNodeSize);
+ pWriter->aData = (char *)sqlite3_malloc64(p->nNodeSize);
if( !pWriter->aData ) return SQLITE_NOMEM;
pWriter->nSize = p->nNodeSize;
@@ -170661,6 +197078,11 @@ static int fts3SegWriterAdd(
nPrefix = fts3PrefixCompress(pWriter->zTerm, pWriter->nTerm, zTerm, nTerm);
nSuffix = nTerm-nPrefix;
+ /* If nSuffix is zero or less, then zTerm/nTerm must be a prefix of
+ ** pWriter->zTerm/pWriter->nTerm. i.e. must be equal to or less than when
+ ** compared with BINARY collation. This indicates corruption. */
+ if( nSuffix<=0 ) return FTS_CORRUPT_VTAB;
+
/* Figure out how many bytes are required by this new entry */
nReq = sqlite3Fts3VarintLen(nPrefix) + /* varint containing prefix size */
sqlite3Fts3VarintLen(nSuffix) + /* varint containing suffix size */
@@ -170672,6 +197094,7 @@ static int fts3SegWriterAdd(
int rc;
/* The current leaf node is full. Write it out to the database. */
+ if( pWriter->iFree==LARGEST_INT64 ) return FTS_CORRUPT_VTAB;
rc = fts3WriteSegment(p, pWriter->iFree++, pWriter->aData, nData);
if( rc!=SQLITE_OK ) return rc;
p->nLeafAdd++;
@@ -170711,7 +197134,7 @@ static int fts3SegWriterAdd(
** the buffer to make it large enough.
*/
if( nReq>pWriter->nSize ){
- char *aNew = sqlite3_realloc(pWriter->aData, nReq);
+ char *aNew = sqlite3_realloc64(pWriter->aData, nReq);
if( !aNew ) return SQLITE_NOMEM;
pWriter->aData = aNew;
pWriter->nSize = nReq;
@@ -170721,9 +197144,11 @@ static int fts3SegWriterAdd(
/* Append the prefix-compressed term and doclist to the buffer. */
nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nPrefix);
nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nSuffix);
+ assert( nSuffix>0 );
memcpy(&pWriter->aData[nData], &zTerm[nPrefix], nSuffix);
nData += nSuffix;
nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nDoclist);
+ assert( nDoclist>0 );
memcpy(&pWriter->aData[nData], aDoclist, nDoclist);
pWriter->nData = nData + nDoclist;
@@ -170734,7 +197159,7 @@ static int fts3SegWriterAdd(
*/
if( isCopyTerm ){
if( nTerm>pWriter->nMalloc ){
- char *zNew = sqlite3_realloc(pWriter->zMalloc, nTerm*2);
+ char *zNew = sqlite3_realloc64(pWriter->zMalloc, (i64)nTerm*2);
if( !zNew ){
return SQLITE_NOMEM;
}
@@ -170743,6 +197168,7 @@ static int fts3SegWriterAdd(
pWriter->zTerm = zNew;
}
assert( pWriter->zTerm==pWriter->zMalloc );
+ assert( nTerm>0 );
memcpy(pWriter->zTerm, zTerm, nTerm);
}else{
pWriter->zTerm = (char *)zTerm;
@@ -170778,12 +197204,12 @@ static int fts3SegWriterFlush(
pWriter->iFirst, pWriter->iFree, &iLast, &zRoot, &nRoot);
}
if( rc==SQLITE_OK ){
- rc = fts3WriteSegdir(p, iLevel, iIdx,
+ rc = fts3WriteSegdir(p, iLevel, iIdx,
pWriter->iFirst, iLastLeaf, iLast, pWriter->nLeafData, zRoot, nRoot);
}
}else{
/* The entire tree fits on the root node. Write it to the segdir table. */
- rc = fts3WriteSegdir(p, iLevel, iIdx,
+ rc = fts3WriteSegdir(p, iLevel, iIdx,
0, 0, 0, pWriter->nLeafData, pWriter->aData, pWriter->nData);
}
p->nLeafAdd++;
@@ -170791,7 +197217,7 @@ static int fts3SegWriterFlush(
}
/*
-** Release all memory held by the SegmentWriter object passed as the
+** Release all memory held by the SegmentWriter object passed as the
** first argument.
*/
static void fts3SegWriterFree(SegmentWriter *pWriter){
@@ -170841,9 +197267,9 @@ static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
** Return SQLITE_OK if successful, or an SQLite error code if not.
*/
static int fts3SegmentMaxLevel(
- Fts3Table *p,
+ Fts3Table *p,
int iLangid,
- int iIndex,
+ int iIndex,
sqlite3_int64 *pnMax
){
sqlite3_stmt *pStmt;
@@ -170859,7 +197285,7 @@ static int fts3SegmentMaxLevel(
rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0);
if( rc!=SQLITE_OK ) return rc;
sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex, 0));
- sqlite3_bind_int64(pStmt, 2,
+ sqlite3_bind_int64(pStmt, 2,
getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1)
);
if( SQLITE_ROW==sqlite3_step(pStmt) ){
@@ -170888,8 +197314,8 @@ static int fts3SegmentIsMaxLevel(Fts3Table *p, i64 iAbsLevel, int *pbMax){
int rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0);
if( rc!=SQLITE_OK ) return rc;
sqlite3_bind_int64(pStmt, 1, iAbsLevel+1);
- sqlite3_bind_int64(pStmt, 2,
- ((iAbsLevel/FTS3_SEGDIR_MAXLEVEL)+1) * FTS3_SEGDIR_MAXLEVEL
+ sqlite3_bind_int64(pStmt, 2,
+ (((u64)iAbsLevel/FTS3_SEGDIR_MAXLEVEL)+1) * FTS3_SEGDIR_MAXLEVEL
);
*pbMax = 0;
@@ -170926,9 +197352,9 @@ static int fts3DeleteSegment(
** This function is used after merging multiple segments into a single large
** segment to delete the old, now redundant, segment b-trees. Specifically,
** it:
-**
-** 1) Deletes all %_segments entries for the segments associated with
-** each of the SegReader objects in the array passed as the third
+**
+** 1) Deletes all %_segments entries for the segments associated with
+** each of the SegReader objects in the array passed as the third
** argument, and
**
** 2) deletes all %_segdir entries with level iLevel, or all %_segdir
@@ -170960,7 +197386,7 @@ static int fts3DeleteSegdir(
rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_RANGE, &pDelete, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pDelete, 1, getAbsoluteLevel(p, iLangid, iIndex, 0));
- sqlite3_bind_int64(pDelete, 2,
+ sqlite3_bind_int64(pDelete, 2,
getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1)
);
}
@@ -170982,7 +197408,7 @@ static int fts3DeleteSegdir(
}
/*
-** When this function is called, buffer *ppList (size *pnList bytes) contains
+** When this function is called, buffer *ppList (size *pnList bytes) contains
** a position list that may (or may not) feature multiple columns. This
** function adjusts the pointer *ppList and the length *pnList so that they
** identify the subset of the position list that corresponds to column iCol.
@@ -171009,7 +197435,7 @@ static void fts3ColumnFilter(
while( 1 ){
char c = 0;
while( p<pEnd && (c | *p)&0xFE ) c = *p++ & 0x80;
-
+
if( iCol==iCurrent ){
nList = (int)(p - pList);
break;
@@ -171017,14 +197443,14 @@ static void fts3ColumnFilter(
nList -= (int)(p - pList);
pList = p;
- if( nList==0 ){
+ if( nList<=0 ){
break;
}
p = &pList[1];
p += fts3GetVarint32(p, &iCurrent);
}
- if( bZero && &pList[nList]!=pEnd ){
+ if( bZero && (pEnd - &pList[nList])>0){
memset(&pList[nList], 0, pEnd - &pList[nList]);
}
*ppList = pList;
@@ -171041,17 +197467,20 @@ static void fts3ColumnFilter(
static int fts3MsrBufferData(
Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */
char *pList,
- int nList
+ i64 nList
){
- if( nList>pMsr->nBuffer ){
+ if( (nList+FTS3_NODE_PADDING)>pMsr->nBuffer ){
char *pNew;
- pMsr->nBuffer = nList*2;
- pNew = (char *)sqlite3_realloc(pMsr->aBuffer, pMsr->nBuffer);
+ int nNew = nList*2 + FTS3_NODE_PADDING;
+ pNew = (char *)sqlite3_realloc64(pMsr->aBuffer, nNew);
if( !pNew ) return SQLITE_NOMEM;
pMsr->aBuffer = pNew;
+ pMsr->nBuffer = nNew;
}
+ assert( nList>0 );
memcpy(pMsr->aBuffer, pList, nList);
+ memset(&pMsr->aBuffer[nList], 0, FTS3_NODE_PADDING);
return SQLITE_OK;
}
@@ -171089,7 +197518,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
rc = fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList);
j = 1;
- while( rc==SQLITE_OK
+ while( rc==SQLITE_OK
&& j<nMerge
&& apSegment[j]->pOffsetList
&& apSegment[j]->iDocid==iDocid
@@ -171101,7 +197530,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp);
if( nList>0 && fts3SegReaderIsPending(apSegment[0]) ){
- rc = fts3MsrBufferData(pMsr, pList, nList+1);
+ rc = fts3MsrBufferData(pMsr, pList, (i64)nList+1);
if( rc!=SQLITE_OK ) return rc;
assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 );
pList = pMsr->aBuffer;
@@ -171132,7 +197561,7 @@ static int fts3SegReaderStart(
int i;
int nSeg = pCsr->nSegment;
- /* If the Fts3SegFilter defines a specific term (or term prefix) to search
+ /* If the Fts3SegFilter defines a specific term (or term prefix) to search
** for, then advance each segment iterator until it points to a term of
** equal or greater value than the specified term. This prevents many
** unnecessary merge/sort operations for the case where single segment
@@ -171216,7 +197645,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrStart(
** sqlite3Fts3SegReaderStart()
** sqlite3Fts3SegReaderStep()
**
-** then the entire doclist for the term is available in
+** then the entire doclist for the term is available in
** MultiSegReader.aDoclist/nDoclist.
*/
SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){
@@ -171238,6 +197667,19 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){
return SQLITE_OK;
}
+static int fts3GrowSegReaderBuffer(Fts3MultiSegReader *pCsr, i64 nReq){
+ if( nReq>pCsr->nBuffer ){
+ char *aNew;
+ pCsr->nBuffer = nReq*2;
+ aNew = sqlite3_realloc64(pCsr->aBuffer, pCsr->nBuffer);
+ if( !aNew ){
+ return SQLITE_NOMEM;
+ }
+ pCsr->aBuffer = aNew;
+ }
+ return SQLITE_OK;
+}
+
SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
Fts3Table *p, /* Virtual table handle */
@@ -171264,9 +197706,9 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
do {
int nMerge;
int i;
-
+
/* Advance the first pCsr->nAdvance entries in the apSegment[] array
- ** forward. Then sort the list in order of current term again.
+ ** forward. Then sort the list in order of current term again.
*/
for(i=0; i<pCsr->nAdvance; i++){
Fts3SegReader *pSeg = apSegment[i];
@@ -171288,39 +197730,40 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
pCsr->zTerm = apSegment[0]->zTerm;
/* If this is a prefix-search, and if the term that apSegment[0] points
- ** to does not share a suffix with pFilter->zTerm/nTerm, then all
+ ** to does not share a suffix with pFilter->zTerm/nTerm, then all
** required callbacks have been made. In this case exit early.
**
** Similarly, if this is a search for an exact match, and the first term
** of segment apSegment[0] is not a match, exit early.
*/
if( pFilter->zTerm && !isScan ){
- if( pCsr->nTerm<pFilter->nTerm
+ if( pCsr->nTerm<pFilter->nTerm
|| (!isPrefix && pCsr->nTerm>pFilter->nTerm)
- || memcmp(pCsr->zTerm, pFilter->zTerm, pFilter->nTerm)
+ || memcmp(pCsr->zTerm, pFilter->zTerm, pFilter->nTerm)
){
break;
}
}
nMerge = 1;
- while( nMerge<nSegment
+ while( nMerge<nSegment
&& apSegment[nMerge]->aNode
- && apSegment[nMerge]->nTerm==pCsr->nTerm
+ && apSegment[nMerge]->nTerm==pCsr->nTerm
&& 0==memcmp(pCsr->zTerm, apSegment[nMerge]->zTerm, pCsr->nTerm)
){
nMerge++;
}
assert( isIgnoreEmpty || (isRequirePos && !isColFilter) );
- if( nMerge==1
- && !isIgnoreEmpty
- && !isFirst
+ if( nMerge==1
+ && !isIgnoreEmpty
+ && !isFirst
&& (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0)
){
pCsr->nDoclist = apSegment[0]->nDoclist;
if( fts3SegReaderIsPending(apSegment[0]) ){
- rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist);
+ rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist,
+ (i64)pCsr->nDoclist);
pCsr->aDoclist = pCsr->aBuffer;
}else{
pCsr->aDoclist = apSegment[0]->aDoclist;
@@ -171360,32 +197803,27 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
if( !isIgnoreEmpty || nList>0 ){
- /* Calculate the 'docid' delta value to write into the merged
+ /* Calculate the 'docid' delta value to write into the merged
** doclist. */
sqlite3_int64 iDelta;
if( p->bDescIdx && nDoclist>0 ){
- iDelta = iPrev - iDocid;
+ if( iPrev<=iDocid ) return FTS_CORRUPT_VTAB;
+ iDelta = (i64)((u64)iPrev - (u64)iDocid);
}else{
- iDelta = iDocid - iPrev;
+ if( nDoclist>0 && iPrev>=iDocid ) return FTS_CORRUPT_VTAB;
+ iDelta = (i64)((u64)iDocid - (u64)iPrev);
}
- assert( iDelta>0 || (nDoclist==0 && iDelta==iDocid) );
- assert( nDoclist>0 || iDelta==iDocid );
nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0);
- if( nDoclist+nByte>pCsr->nBuffer ){
- char *aNew;
- pCsr->nBuffer = (nDoclist+nByte)*2;
- aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer);
- if( !aNew ){
- return SQLITE_NOMEM;
- }
- pCsr->aBuffer = aNew;
- }
+
+ rc = fts3GrowSegReaderBuffer(pCsr,
+ (i64)nByte+nDoclist+FTS3_NODE_PADDING);
+ if( rc ) return rc;
if( isFirst ){
char *a = &pCsr->aBuffer[nDoclist];
int nWrite;
-
+
nWrite = sqlite3Fts3FirstFilter(iDelta, pList, nList, a);
if( nWrite ){
iPrev = iDocid;
@@ -171405,6 +197843,9 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
fts3SegReaderSort(apSegment, nMerge, j, xCmp);
}
if( nDoclist>0 ){
+ rc = fts3GrowSegReaderBuffer(pCsr, (i64)nDoclist+FTS3_NODE_PADDING);
+ if( rc ) return rc;
+ memset(&pCsr->aBuffer[nDoclist], 0, FTS3_NODE_PADDING);
pCsr->aDoclist = pCsr->aBuffer;
pCsr->nDoclist = nDoclist;
rc = SQLITE_ROW;
@@ -171435,18 +197876,18 @@ SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(
}
/*
-** Decode the "end_block" field, selected by column iCol of the SELECT
-** statement passed as the first argument.
+** Decode the "end_block" field, selected by column iCol of the SELECT
+** statement passed as the first argument.
**
** The "end_block" field may contain either an integer, or a text field
-** containing the text representation of two non-negative integers separated
-** by one or more space (0x20) characters. In the first case, set *piEndBlock
-** to the integer value and *pnByte to zero before returning. In the second,
+** containing the text representation of two non-negative integers separated
+** by one or more space (0x20) characters. In the first case, set *piEndBlock
+** to the integer value and *pnByte to zero before returning. In the second,
** set *piEndBlock to the first value and *pnByte to the second.
*/
static void fts3ReadEndBlockField(
- sqlite3_stmt *pStmt,
- int iCol,
+ sqlite3_stmt *pStmt,
+ int iCol,
i64 *piEndBlock,
i64 *pnByte
){
@@ -171454,11 +197895,11 @@ static void fts3ReadEndBlockField(
if( zText ){
int i;
int iMul = 1;
- i64 iVal = 0;
+ u64 iVal = 0;
for(i=0; zText[i]>='0' && zText[i]<='9'; i++){
iVal = iVal*10 + (zText[i] - '0');
}
- *piEndBlock = iVal;
+ *piEndBlock = (i64)iVal;
while( zText[i]==' ' ) i++;
iVal = 0;
if( zText[i]=='-' ){
@@ -171468,7 +197909,7 @@ static void fts3ReadEndBlockField(
for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
iVal = iVal*10 + (zText[i] - '0');
}
- *pnByte = (iVal * (i64)iMul);
+ *pnByte = ((i64)iVal * (i64)iMul);
}
}
@@ -171492,10 +197933,10 @@ static int fts3PromoteSegments(
i64 iLast = (iAbsLevel/FTS3_SEGDIR_MAXLEVEL + 1) * FTS3_SEGDIR_MAXLEVEL - 1;
i64 nLimit = (nByte*3)/2;
- /* Loop through all entries in the %_segdir table corresponding to
+ /* Loop through all entries in the %_segdir table corresponding to
** segments in this index on levels greater than iAbsLevel. If there is
- ** at least one such segment, and it is possible to determine that all
- ** such segments are smaller than nLimit bytes in size, they will be
+ ** at least one such segment, and it is possible to determine that all
+ ** such segments are smaller than nLimit bytes in size, they will be
** promoted to level iAbsLevel. */
sqlite3_bind_int64(pRange, 1, iAbsLevel+1);
sqlite3_bind_int64(pRange, 2, iLast);
@@ -171503,7 +197944,7 @@ static int fts3PromoteSegments(
i64 nSize = 0, dummy;
fts3ReadEndBlockField(pRange, 2, &dummy, &nSize);
if( nSize<=0 || nSize>nLimit ){
- /* If nSize==0, then the %_segdir.end_block field does not not
+ /* If nSize==0, then the %_segdir.end_block field does not not
** contain a size value. This happens if it was written by an
** old version of FTS. In this case it is not possible to determine
** the size of the segment, and so segment promotion does not
@@ -171569,18 +198010,18 @@ static int fts3PromoteSegments(
}
/*
-** Merge all level iLevel segments in the database into a single
+** Merge all level iLevel segments in the database into a single
** iLevel+1 segment. Or, if iLevel<0, merge all segments into a
-** single segment with a level equal to the numerically largest level
+** single segment with a level equal to the numerically largest level
** currently present in the database.
**
** If this function is called with iLevel<0, but there is only one
-** segment in the database, SQLITE_DONE is returned immediately.
-** Otherwise, if successful, SQLITE_OK is returned. If an error occurs,
+** segment in the database, SQLITE_DONE is returned immediately.
+** Otherwise, if successful, SQLITE_OK is returned. If an error occurs,
** an SQLite error code is returned.
*/
static int fts3SegmentMerge(
- Fts3Table *p,
+ Fts3Table *p,
int iLangid, /* Language id to merge */
int iIndex, /* Index in p->aIndex[] to merge */
int iLevel /* Level to merge */
@@ -171624,7 +198065,7 @@ static int fts3SegmentMerge(
}else{
/* This call is to merge all segments at level iLevel. find the next
** available segment index at level iLevel+1. The call to
- ** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to
+ ** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to
** a single iLevel+2 segment if necessary. */
assert( FTS3_SEGCURSOR_PENDING==-1 );
iNewLevel = getAbsoluteLevel(p, iLangid, iIndex, iLevel+1);
@@ -171634,8 +198075,10 @@ static int fts3SegmentMerge(
if( rc!=SQLITE_OK ) goto finished;
assert( csr.nSegment>0 );
- assert( iNewLevel>=getAbsoluteLevel(p, iLangid, iIndex, 0) );
- assert( iNewLevel<getAbsoluteLevel(p, iLangid, iIndex,FTS3_SEGDIR_MAXLEVEL) );
+ assert_fts3_nc( iNewLevel>=getAbsoluteLevel(p, iLangid, iIndex, 0) );
+ assert_fts3_nc(
+ iNewLevel<getAbsoluteLevel(p, iLangid, iIndex,FTS3_SEGDIR_MAXLEVEL)
+ );
memset(&filter, 0, sizeof(Fts3SegFilter));
filter.flags = FTS3_SEGMENT_REQUIRE_POS;
@@ -171645,11 +198088,11 @@ static int fts3SegmentMerge(
while( SQLITE_OK==rc ){
rc = sqlite3Fts3SegReaderStep(p, &csr);
if( rc!=SQLITE_ROW ) break;
- rc = fts3SegWriterAdd(p, &pWriter, 1,
+ rc = fts3SegWriterAdd(p, &pWriter, 1,
csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist);
}
if( rc!=SQLITE_OK ) goto finished;
- assert( pWriter || bIgnoreEmpty );
+ assert_fts3_nc( pWriter || bIgnoreEmpty );
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
rc = fts3DeleteSegdir(
@@ -171673,18 +198116,17 @@ static int fts3SegmentMerge(
}
-/*
-** Flush the contents of pendingTerms to level 0 segments.
+/*
+** Flush the contents of pendingTerms to level 0 segments.
*/
SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
int rc = SQLITE_OK;
int i;
-
+
for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){
rc = fts3SegmentMerge(p, p->iPrevLangid, i, FTS3_SEGCURSOR_PENDING);
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
- sqlite3Fts3PendingTermsClear(p);
/* Determine the auto-incr-merge setting if unknown. If enabled,
** estimate the number of leaf blocks of content to be written
@@ -171706,6 +198148,10 @@ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
rc = sqlite3_reset(pStmt);
}
}
+
+ if( rc==SQLITE_OK ){
+ sqlite3Fts3PendingTermsClear(p);
+ }
return rc;
}
@@ -171734,14 +198180,16 @@ static void fts3DecodeIntArray(
const char *zBuf, /* The BLOB containing the varints */
int nBuf /* size of the BLOB */
){
- int i, j;
- UNUSED_PARAMETER(nBuf);
- for(i=j=0; i<N; i++){
- sqlite3_int64 x;
- j += sqlite3Fts3GetVarint(&zBuf[j], &x);
- assert(j<=nBuf);
- a[i] = (u32)(x & 0xffffffff);
+ int i = 0;
+ if( nBuf && (zBuf[nBuf-1]&0x80)==0 ){
+ int j;
+ for(i=j=0; i<N && j<nBuf; i++){
+ sqlite3_int64 x;
+ j += sqlite3Fts3GetVarint(&zBuf[j], &x);
+ a[i] = (u32)(x & 0xffffffff);
+ }
}
+ while( i<N ) a[i++] = 0;
}
/*
@@ -171760,7 +198208,7 @@ static void fts3InsertDocsize(
int rc; /* Result code from subfunctions */
if( *pRC ) return;
- pBlob = sqlite3_malloc( 10*p->nColumn );
+ pBlob = sqlite3_malloc64( 10*(sqlite3_int64)p->nColumn );
if( pBlob==0 ){
*pRC = SQLITE_NOMEM;
return;
@@ -171781,7 +198229,7 @@ static void fts3InsertDocsize(
/*
** Record 0 of the %_stat table contains a blob consisting of N varints,
** where N is the number of user defined columns in the fts3 table plus
-** two. If nCol is the number of user defined columns, then values of the
+** two. If nCol is the number of user defined columns, then values of the
** varints are set as follows:
**
** Varint 0: Total number of rows in the table.
@@ -171810,7 +198258,7 @@ static void fts3UpdateDocTotals(
const int nStat = p->nColumn+2;
if( *pRC ) return;
- a = sqlite3_malloc( (sizeof(u32)+10)*nStat );
+ a = sqlite3_malloc64( (sizeof(u32)+10)*(sqlite3_int64)nStat );
if( a==0 ){
*pRC = SQLITE_NOMEM;
return;
@@ -171866,7 +198314,7 @@ static void fts3UpdateDocTotals(
}
/*
-** Merge the entire database so that there is one segment for each
+** Merge the entire database so that there is one segment for each
** iIndex/iLangid combination.
*/
static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
@@ -171874,7 +198322,10 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
int rc;
sqlite3_stmt *pAllLangid = 0;
- rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0);
+ rc = sqlite3Fts3PendingTermsFlush(p);
+ if( rc==SQLITE_OK ){
+ rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0);
+ }
if( rc==SQLITE_OK ){
int rc2;
sqlite3_bind_int(pAllLangid, 1, p->iPrevLangid);
@@ -171895,7 +198346,6 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
}
sqlite3Fts3SegmentsClose(p);
- sqlite3Fts3PendingTermsClear(p);
return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc;
}
@@ -171905,7 +198355,7 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
**
** INSERT INTO <tbl>(<tbl>) VALUES('rebuild');
**
-** The entire FTS index is discarded and rebuilt. If the table is one
+** The entire FTS index is discarded and rebuilt. If the table is one
** created using the content=xxx option, then the new index is based on
** the current contents of the xxx table. Otherwise, it is rebuilt based
** on the contents of the %_content table.
@@ -171931,8 +198381,8 @@ static int fts3DoRebuild(Fts3Table *p){
}
if( rc==SQLITE_OK ){
- int nByte = sizeof(u32) * (p->nColumn+1)*3;
- aSz = (u32 *)sqlite3_malloc(nByte);
+ sqlite3_int64 nByte = sizeof(u32) * ((sqlite3_int64)p->nColumn+1)*3;
+ aSz = (u32 *)sqlite3_malloc64(nByte);
if( aSz==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -171985,9 +198435,9 @@ static int fts3DoRebuild(Fts3Table *p){
/*
-** This function opens a cursor used to read the input data for an
+** This function opens a cursor used to read the input data for an
** incremental merge operation. Specifically, it opens a cursor to scan
-** the oldest nSeg segments (idx=0 through idx=(nSeg-1)) in absolute
+** the oldest nSeg segments (idx=0 through idx=(nSeg-1)) in absolute
** level iAbsLevel.
*/
static int fts3IncrmergeCsr(
@@ -171997,13 +198447,13 @@ static int fts3IncrmergeCsr(
Fts3MultiSegReader *pCsr /* Cursor object to populate */
){
int rc; /* Return Code */
- sqlite3_stmt *pStmt = 0; /* Statement used to read %_segdir entry */
- int nByte; /* Bytes allocated at pCsr->apSegment[] */
+ sqlite3_stmt *pStmt = 0; /* Statement used to read %_segdir entry */
+ sqlite3_int64 nByte; /* Bytes allocated at pCsr->apSegment[] */
/* Allocate space for the Fts3MultiSegReader.aCsr[] array */
memset(pCsr, 0, sizeof(*pCsr));
nByte = sizeof(Fts3SegReader *) * nSeg;
- pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc(nByte);
+ pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc64(nByte);
if( pCsr->apSegment==0 ){
rc = SQLITE_NOMEM;
@@ -172052,7 +198502,7 @@ struct Blob {
};
/*
-** This structure is used to build up buffers containing segment b-tree
+** This structure is used to build up buffers containing segment b-tree
** nodes (blocks).
*/
struct NodeWriter {
@@ -172109,7 +198559,7 @@ struct NodeReader {
static void blobGrowBuffer(Blob *pBlob, int nMin, int *pRc){
if( *pRc==SQLITE_OK && nMin>pBlob->nAlloc ){
int nAlloc = nMin;
- char *a = (char *)sqlite3_realloc(pBlob->a, nAlloc);
+ char *a = (char *)sqlite3_realloc64(pBlob->a, nAlloc);
if( a ){
pBlob->nAlloc = nAlloc;
pBlob->a = a;
@@ -172121,12 +198571,12 @@ static void blobGrowBuffer(Blob *pBlob, int nMin, int *pRc){
/*
** Attempt to advance the node-reader object passed as the first argument to
-** the next entry on the node.
+** the next entry on the node.
**
-** Return an error code if an error occurs (SQLITE_NOMEM is possible).
+** Return an error code if an error occurs (SQLITE_NOMEM is possible).
** Otherwise return SQLITE_OK. If there is no next entry on the node
** (e.g. because the current entry is the last) set NodeReader->aNode to
-** NULL to indicate EOF. Otherwise, populate the NodeReader structure output
+** NULL to indicate EOF. Otherwise, populate the NodeReader structure output
** variables for the new entry.
*/
static int nodeReaderNext(NodeReader *p){
@@ -172146,18 +198596,18 @@ static int nodeReaderNext(NodeReader *p){
}
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
- if( nPrefix>p->iOff || nSuffix>p->nNode-p->iOff ){
- return SQLITE_CORRUPT_VTAB;
+ if( nPrefix>p->term.n || nSuffix>p->nNode-p->iOff || nSuffix==0 ){
+ return FTS_CORRUPT_VTAB;
}
blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc);
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK && ALWAYS(p->term.a!=0) ){
memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix);
p->term.n = nPrefix+nSuffix;
p->iOff += nSuffix;
if( p->iChild==0 ){
p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist);
if( (p->nNode-p->iOff)<p->nDoclist ){
- return SQLITE_CORRUPT_VTAB;
+ return FTS_CORRUPT_VTAB;
}
p->aDoclist = &p->aNode[p->iOff];
p->iOff += p->nDoclist;
@@ -172165,7 +198615,7 @@ static int nodeReaderNext(NodeReader *p){
}
}
- assert( p->iOff<=p->nNode );
+ assert_fts3_nc( p->iOff<=p->nNode );
return rc;
}
@@ -172179,7 +198629,7 @@ static void nodeReaderRelease(NodeReader *p){
/*
** Initialize a node-reader object to read the node in buffer aNode/nNode.
**
-** If successful, SQLITE_OK is returned and the NodeReader object set to
+** If successful, SQLITE_OK is returned and the NodeReader object set to
** point to the first entry on the node (if any). Otherwise, an SQLite
** error code is returned.
*/
@@ -172189,14 +198639,14 @@ static int nodeReaderInit(NodeReader *p, const char *aNode, int nNode){
p->nNode = nNode;
/* Figure out if this is a leaf or an internal node. */
- if( p->aNode[0] ){
+ if( aNode && aNode[0] ){
/* An internal node. */
p->iOff = 1 + sqlite3Fts3GetVarint(&p->aNode[1], &p->iChild);
}else{
p->iOff = 1;
}
- return nodeReaderNext(p);
+ return aNode ? nodeReaderNext(p) : SQLITE_OK;
}
/*
@@ -172228,17 +198678,18 @@ static int fts3IncrmergePush(
int nSpace;
/* Figure out how much space the key will consume if it is written to
- ** the current node of layer iLayer. Due to the prefix compression,
+ ** the current node of layer iLayer. Due to the prefix compression,
** the space required changes depending on which node the key is to
** be added to. */
nPrefix = fts3PrefixCompress(pNode->key.a, pNode->key.n, zTerm, nTerm);
nSuffix = nTerm - nPrefix;
+ if(nSuffix<=0 ) return FTS_CORRUPT_VTAB;
nSpace = sqlite3Fts3VarintLen(nPrefix);
nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix;
- if( pNode->key.n==0 || (pNode->block.n + nSpace)<=p->nNodeSize ){
+ if( pNode->key.n==0 || (pNode->block.n + nSpace)<=p->nNodeSize ){
/* If the current node of layer iLayer contains zero keys, or if adding
- ** the key to it will not cause it to grow to larger than nNodeSize
+ ** the key to it will not cause it to grow to larger than nNodeSize
** bytes in size, write the key here. */
Blob *pBlk = &pNode->block;
@@ -172257,6 +198708,8 @@ static int fts3IncrmergePush(
pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nPrefix);
}
pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nSuffix);
+ assert( nPrefix+nSuffix<=nTerm );
+ assert( nPrefix>=0 );
memcpy(&pBlk->a[pBlk->n], &zTerm[nPrefix], nSuffix);
pBlk->n += nSuffix;
@@ -172294,12 +198747,12 @@ static int fts3IncrmergePush(
** A node header is a single 0x00 byte for a leaf node, or a height varint
** followed by the left-hand-child varint for an internal node.
**
-** The term to be appended is passed via arguments zTerm/nTerm. For a
+** The term to be appended is passed via arguments zTerm/nTerm. For a
** leaf node, the doclist is passed as aDoclist/nDoclist. For an internal
** node, both aDoclist and nDoclist must be passed 0.
**
** If the size of the value in blob pPrev is zero, then this is the first
-** term written to the node. Otherwise, pPrev contains a copy of the
+** term written to the node. Otherwise, pPrev contains a copy of the
** previous term. Before this function returns, it is updated to contain a
** copy of zTerm/nTerm.
**
@@ -172316,7 +198769,7 @@ static int fts3AppendToNode(
const char *zTerm, /* New term to write */
int nTerm, /* Size of zTerm in bytes */
const char *aDoclist, /* Doclist (or NULL) to write */
- int nDoclist /* Size of aDoclist in bytes */
+ int nDoclist /* Size of aDoclist in bytes */
){
int rc = SQLITE_OK; /* Return code */
int bFirst = (pPrev->n==0); /* True if this is the first term written */
@@ -172326,13 +198779,16 @@ static int fts3AppendToNode(
/* Node must have already been started. There must be a doclist for a
** leaf node, and there must not be a doclist for an internal node. */
assert( pNode->n>0 );
- assert( (pNode->a[0]=='\0')==(aDoclist!=0) );
+ assert_fts3_nc( (pNode->a[0]=='\0')==(aDoclist!=0) );
blobGrowBuffer(pPrev, nTerm, &rc);
if( rc!=SQLITE_OK ) return rc;
+ assert( pPrev!=0 );
+ assert( pPrev->a!=0 );
nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm);
nSuffix = nTerm - nPrefix;
+ if( nSuffix<=0 ) return FTS_CORRUPT_VTAB;
memcpy(pPrev->a, zTerm, nTerm);
pPrev->n = nTerm;
@@ -172378,19 +198834,24 @@ static int fts3IncrmergeAppend(
pLeaf = &pWriter->aNodeWriter[0];
nPrefix = fts3PrefixCompress(pLeaf->key.a, pLeaf->key.n, zTerm, nTerm);
nSuffix = nTerm - nPrefix;
+ if(nSuffix<=0 ) return FTS_CORRUPT_VTAB;
nSpace = sqlite3Fts3VarintLen(nPrefix);
nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix;
nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist;
/* If the current block is not empty, and if adding this term/doclist
- ** to the current block would make it larger than Fts3Table.nNodeSize
- ** bytes, write this block out to the database. */
- if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){
+ ** to the current block would make it larger than Fts3Table.nNodeSize bytes,
+ ** and if there is still room for another leaf page, write this block out to
+ ** the database. */
+ if( pLeaf->block.n>0
+ && (pLeaf->block.n + nSpace)>p->nNodeSize
+ && pLeaf->iBlock < (pWriter->iStart + pWriter->nLeafEst)
+ ){
rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n);
pWriter->nWork++;
- /* Add the current term to the parent node. The term added to the
+ /* Add the current term to the parent node. The term added to the
** parent must:
**
** a) be greater than the largest term on the leaf node just written
@@ -172455,7 +198916,7 @@ static void fts3IncrmergeRelease(
NodeWriter *pRoot; /* NodeWriter for root node */
int rc = *pRc; /* Error code */
- /* Set iRoot to the index in pWriter->aNodeWriter[] of the output segment
+ /* Set iRoot to the index in pWriter->aNodeWriter[] of the output segment
** root node. If the segment fits entirely on a single leaf node, iRoot
** will be set to 0. If the root node is the parent of the leaves, iRoot
** will be 1. And so on. */
@@ -172473,17 +198934,17 @@ static void fts3IncrmergeRelease(
/* The entire output segment fits on a single node. Normally, this means
** the node would be stored as a blob in the "root" column of the %_segdir
- ** table. However, this is not permitted in this case. The problem is that
- ** space has already been reserved in the %_segments table, and so the
- ** start_block and end_block fields of the %_segdir table must be populated.
- ** And, by design or by accident, released versions of FTS cannot handle
+ ** table. However, this is not permitted in this case. The problem is that
+ ** space has already been reserved in the %_segments table, and so the
+ ** start_block and end_block fields of the %_segdir table must be populated.
+ ** And, by design or by accident, released versions of FTS cannot handle
** segments that fit entirely on the root node with start_block!=0.
**
- ** Instead, create a synthetic root node that contains nothing but a
+ ** Instead, create a synthetic root node that contains nothing but a
** pointer to the single content node. So that the segment consists of a
** single leaf and a single interior (root) node.
**
- ** Todo: Better might be to defer allocating space in the %_segments
+ ** Todo: Better might be to defer allocating space in the %_segments
** table until we are sure it is needed.
*/
if( iRoot==0 ){
@@ -172511,7 +198972,7 @@ static void fts3IncrmergeRelease(
/* Write the %_segdir record. */
if( rc==SQLITE_OK ){
- rc = fts3WriteSegdir(p,
+ rc = fts3WriteSegdir(p,
pWriter->iAbsLevel+1, /* level */
pWriter->iIdx, /* idx */
pWriter->iStart, /* start_block */
@@ -172542,7 +199003,11 @@ static int fts3TermCmp(
int nCmp = MIN(nLhs, nRhs);
int res;
- res = memcmp(zLhs, zRhs, nCmp);
+ if( nCmp && ALWAYS(zLhs) && ALWAYS(zRhs) ){
+ res = memcmp(zLhs, zRhs, nCmp);
+ }else{
+ res = 0;
+ }
if( res==0 ) res = nLhs - nRhs;
return res;
@@ -172550,11 +199015,11 @@ static int fts3TermCmp(
/*
-** Query to see if the entry in the %_segments table with blockid iEnd is
+** Query to see if the entry in the %_segments table with blockid iEnd is
** NULL. If no error occurs and the entry is NULL, set *pbRes 1 before
-** returning. Otherwise, set *pbRes to 0.
+** returning. Otherwise, set *pbRes to 0.
**
-** Or, if an error occurs while querying the database, return an SQLite
+** Or, if an error occurs while querying the database, return an SQLite
** error code. The final value of *pbRes is undefined in this case.
**
** This is used to test if a segment is an "appendable" segment. If it
@@ -172572,14 +199037,14 @@ static int fts3IsAppendable(Fts3Table *p, sqlite3_int64 iEnd, int *pbRes){
if( SQLITE_ROW==sqlite3_step(pCheck) ) bRes = 1;
rc = sqlite3_reset(pCheck);
}
-
+
*pbRes = bRes;
return rc;
}
/*
** This function is called when initializing an incremental-merge operation.
-** It checks if the existing segment with index value iIdx at absolute level
+** It checks if the existing segment with index value iIdx at absolute level
** (iAbsLevel+1) can be appended to by the incremental merge. If it can, the
** merge-writer object *pWriter is initialized to write to it.
**
@@ -172588,7 +199053,7 @@ static int fts3IsAppendable(Fts3Table *p, sqlite3_int64 iEnd, int *pbRes){
** * It was initially created as an appendable segment (with all required
** space pre-allocated), and
**
-** * The first key read from the input (arguments zKey and nKey) is
+** * The first key read from the input (arguments zKey and nKey) is
** greater than the largest key currently stored in the potential
** output segment.
*/
@@ -172626,6 +199091,10 @@ static int fts3IncrmergeLoad(
pWriter->bNoLeafData = (pWriter->nLeafData==0);
nRoot = sqlite3_column_bytes(pSelect, 4);
aRoot = sqlite3_column_blob(pSelect, 4);
+ if( aRoot==0 ){
+ sqlite3_reset(pSelect);
+ return nRoot ? SQLITE_NOMEM : FTS_CORRUPT_VTAB;
+ }
}else{
return sqlite3_reset(pSelect);
}
@@ -172661,6 +199130,10 @@ static int fts3IncrmergeLoad(
int i;
int nHeight = (int)aRoot[0];
NodeWriter *pNode;
+ if( nHeight<1 || nHeight>=FTS_MAX_APPENDABLE_HEIGHT ){
+ sqlite3_reset(pSelect);
+ return FTS_CORRUPT_VTAB;
+ }
pWriter->nLeafEst = (int)((iEnd - iStart) + 1)/FTS_MAX_APPENDABLE_HEIGHT;
pWriter->iStart = iStart;
@@ -172674,34 +199147,46 @@ static int fts3IncrmergeLoad(
pNode = &pWriter->aNodeWriter[nHeight];
pNode->iBlock = pWriter->iStart + pWriter->nLeafEst*nHeight;
- blobGrowBuffer(&pNode->block, MAX(nRoot, p->nNodeSize), &rc);
+ blobGrowBuffer(&pNode->block,
+ MAX(nRoot, p->nNodeSize)+FTS3_NODE_PADDING, &rc
+ );
if( rc==SQLITE_OK ){
memcpy(pNode->block.a, aRoot, nRoot);
pNode->block.n = nRoot;
+ memset(&pNode->block.a[nRoot], 0, FTS3_NODE_PADDING);
}
for(i=nHeight; i>=0 && rc==SQLITE_OK; i--){
NodeReader reader;
+ memset(&reader, 0, sizeof(reader));
pNode = &pWriter->aNodeWriter[i];
- rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
- while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
- blobGrowBuffer(&pNode->key, reader.term.n, &rc);
- if( rc==SQLITE_OK ){
- memcpy(pNode->key.a, reader.term.a, reader.term.n);
- pNode->key.n = reader.term.n;
- if( i>0 ){
- char *aBlock = 0;
- int nBlock = 0;
- pNode = &pWriter->aNodeWriter[i-1];
- pNode->iBlock = reader.iChild;
- rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock, 0);
- blobGrowBuffer(&pNode->block, MAX(nBlock, p->nNodeSize), &rc);
- if( rc==SQLITE_OK ){
- memcpy(pNode->block.a, aBlock, nBlock);
- pNode->block.n = nBlock;
+ if( pNode->block.a){
+ rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
+ while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
+ blobGrowBuffer(&pNode->key, reader.term.n, &rc);
+ if( rc==SQLITE_OK ){
+ assert_fts3_nc( reader.term.n>0 || reader.aNode==0 );
+ if( reader.term.n>0 ){
+ memcpy(pNode->key.a, reader.term.a, reader.term.n);
+ }
+ pNode->key.n = reader.term.n;
+ if( i>0 ){
+ char *aBlock = 0;
+ int nBlock = 0;
+ pNode = &pWriter->aNodeWriter[i-1];
+ pNode->iBlock = reader.iChild;
+ rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
+ blobGrowBuffer(&pNode->block,
+ MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
+ );
+ if( rc==SQLITE_OK ){
+ memcpy(pNode->block.a, aBlock, nBlock);
+ pNode->block.n = nBlock;
+ memset(&pNode->block.a[nBlock], 0, FTS3_NODE_PADDING);
+ }
+ sqlite3_free(aBlock);
}
- sqlite3_free(aBlock);
}
}
nodeReaderRelease(&reader);
@@ -172718,13 +199203,13 @@ static int fts3IncrmergeLoad(
/*
** Determine the largest segment index value that exists within absolute
** level iAbsLevel+1. If no error occurs, set *piIdx to this value plus
-** one before returning SQLITE_OK. Or, if there are no segments at all
+** one before returning SQLITE_OK. Or, if there are no segments at all
** within level iAbsLevel, set *piIdx to zero.
**
** If an error occurs, return an SQLite error code. The final value of
** *piIdx is undefined in this case.
*/
-static int fts3IncrmergeOutputIdx(
+static int fts3IncrmergeOutputIdx(
Fts3Table *p, /* FTS Table handle */
sqlite3_int64 iAbsLevel, /* Absolute index of input segments */
int *piIdx /* OUT: Next free index at iAbsLevel+1 */
@@ -172743,7 +199228,7 @@ static int fts3IncrmergeOutputIdx(
return rc;
}
-/*
+/*
** Allocate an appendable output segment on absolute level iAbsLevel+1
** with idx value iIdx.
**
@@ -172757,7 +199242,7 @@ static int fts3IncrmergeOutputIdx(
** When an appendable segment is allocated, it is estimated that the
** maximum number of leaf blocks that may be required is the sum of the
** number of leaf blocks consumed by the input segments, plus the number
-** of input segments, multiplied by two. This value is stored in stack
+** of input segments, multiplied by two. This value is stored in stack
** variable nLeafEst.
**
** A total of 16*nLeafEst blocks are allocated when an appendable segment
@@ -172766,10 +199251,10 @@ static int fts3IncrmergeOutputIdx(
** of interior nodes that are parents of the leaf nodes start at block
** (start_block + (1 + end_block - start_block) / 16). And so on.
**
-** In the actual code below, the value "16" is replaced with the
+** In the actual code below, the value "16" is replaced with the
** pre-processor macro FTS_MAX_APPENDABLE_HEIGHT.
*/
-static int fts3IncrmergeWriter(
+static int fts3IncrmergeWriter(
Fts3Table *p, /* Fts3 table handle */
sqlite3_int64 iAbsLevel, /* Absolute level of input segments */
int iIdx, /* Index of new output segment */
@@ -172807,7 +199292,7 @@ static int fts3IncrmergeWriter(
if( rc!=SQLITE_OK ) return rc;
/* Insert the marker in the %_segments table to make sure nobody tries
- ** to steal the space just allocated. This is also used to identify
+ ** to steal the space just allocated. This is also used to identify
** appendable segments. */
rc = fts3WriteSegment(p, pWriter->iEnd, 0, 0);
if( rc!=SQLITE_OK ) return rc;
@@ -172824,13 +199309,13 @@ static int fts3IncrmergeWriter(
}
/*
-** Remove an entry from the %_segdir table. This involves running the
+** Remove an entry from the %_segdir table. This involves running the
** following two statements:
**
** DELETE FROM %_segdir WHERE level = :iAbsLevel AND idx = :iIdx
** UPDATE %_segdir SET idx = idx - 1 WHERE level = :iAbsLevel AND idx > :iIdx
**
-** The DELETE statement removes the specific %_segdir level. The UPDATE
+** The DELETE statement removes the specific %_segdir level. The UPDATE
** statement ensures that the remaining segments have contiguously allocated
** idx values.
*/
@@ -172878,7 +199363,7 @@ static int fts3RepackSegdirLevel(
if( nIdx>=nAlloc ){
int *aNew;
nAlloc += 16;
- aNew = sqlite3_realloc(aIdx, nAlloc*sizeof(int));
+ aNew = sqlite3_realloc64(aIdx, nAlloc*sizeof(int));
if( !aNew ){
rc = SQLITE_NOMEM;
break;
@@ -172944,7 +199429,10 @@ static int fts3TruncateNode(
NodeReader reader; /* Reader object */
Blob prev = {0, 0, 0}; /* Previous term written to new node */
int rc = SQLITE_OK; /* Return code */
- int bLeaf = aNode[0]=='\0'; /* True for a leaf node */
+ int bLeaf; /* True for a leaf node */
+
+ if( nNode<1 ) return FTS_CORRUPT_VTAB;
+ bLeaf = aNode[0]=='\0';
/* Allocate required output space */
blobGrowBuffer(pNew, nNode, &rc);
@@ -172952,8 +199440,8 @@ static int fts3TruncateNode(
pNew->n = 0;
/* Populate new node buffer */
- for(rc = nodeReaderInit(&reader, aNode, nNode);
- rc==SQLITE_OK && reader.aNode;
+ for(rc = nodeReaderInit(&reader, aNode, nNode);
+ rc==SQLITE_OK && reader.aNode;
rc = nodeReaderNext(&reader)
){
if( pNew->n==0 ){
@@ -172980,7 +199468,7 @@ static int fts3TruncateNode(
}
/*
-** Remove all terms smaller than zTerm/nTerm from segment iIdx in absolute
+** Remove all terms smaller than zTerm/nTerm from segment iIdx in absolute
** level iAbsLevel. This may involve deleting entries from the %_segments
** table, and modifying existing entries in both the %_segments and %_segdir
** tables.
@@ -173104,9 +199592,9 @@ static int fts3IncrmergeChomp(
}
*pnRem = 0;
}else{
- /* The incremental merge did not copy all the data from this
+ /* The incremental merge did not copy all the data from this
** segment to the upper level. The segment is modified in place
- ** so that it contains no keys smaller than zTerm/nTerm. */
+ ** so that it contains no keys smaller than zTerm/nTerm. */
const char *zTerm = pSeg->zTerm;
int nTerm = pSeg->nTerm;
rc = fts3TruncateSegment(p, iAbsLevel, pSeg->iIdx, zTerm, nTerm);
@@ -173142,7 +199630,7 @@ static int fts3IncrmergeHintStore(Fts3Table *p, Blob *pHint){
}
/*
-** Load an incr-merge hint from the database. The incr-merge hint, if one
+** Load an incr-merge hint from the database. The incr-merge hint, if one
** exists, is stored in the rowid==1 row of the %_stat table.
**
** If successful, populate blob *pHint with the value read from the %_stat
@@ -173164,7 +199652,7 @@ static int fts3IncrmergeHintLoad(Fts3Table *p, Blob *pHint){
if( aHint ){
blobGrowBuffer(pHint, nHint, &rc);
if( rc==SQLITE_OK ){
- memcpy(pHint->a, aHint, nHint);
+ if( ALWAYS(pHint->a!=0) ) memcpy(pHint->a, aHint, nHint);
pHint->n = nHint;
}
}
@@ -173179,7 +199667,7 @@ static int fts3IncrmergeHintLoad(Fts3Table *p, Blob *pHint){
/*
** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
** Otherwise, append an entry to the hint stored in blob *pHint. Each entry
-** consists of two varints, the absolute level number of the input segments
+** consists of two varints, the absolute level number of the input segments
** and the number of input segments.
**
** If successful, leave *pRc set to SQLITE_OK and return. If an error occurs,
@@ -173200,7 +199688,7 @@ static void fts3IncrmergeHintPush(
/*
** Read the last entry (most recently pushed) from the hint blob *pHint
-** and then remove the entry. Write the two values read to *piAbsLevel and
+** and then remove the entry. Write the two values read to *piAbsLevel and
** *pnInput before returning.
**
** If no error occurs, return SQLITE_OK. If the hint blob in *pHint does
@@ -173210,13 +199698,17 @@ static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){
const int nHint = pHint->n;
int i;
- i = pHint->n-2;
+ i = pHint->n-1;
+ if( (pHint->a[i] & 0x80) ) return FTS_CORRUPT_VTAB;
while( i>0 && (pHint->a[i-1] & 0x80) ) i--;
+ if( i==0 ) return FTS_CORRUPT_VTAB;
+ i--;
while( i>0 && (pHint->a[i-1] & 0x80) ) i--;
pHint->n = i;
i += sqlite3Fts3GetVarint(&pHint->a[i], piAbsLevel);
i += fts3GetVarint32(&pHint->a[i], pnInput);
+ assert( i<=nHint );
if( i!=nHint ) return FTS_CORRUPT_VTAB;
return SQLITE_OK;
@@ -173226,10 +199718,10 @@ static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){
/*
** Attempt an incremental merge that writes nMerge leaf blocks.
**
-** Incremental merges happen nMin segments at a time. The segments
-** to be merged are the nMin oldest segments (the ones with the smallest
-** values for the _segdir.idx field) in the highest level that contains
-** at least nMin segments. Multiple merges might occur in an attempt to
+** Incremental merges happen nMin segments at a time. The segments
+** to be merged are the nMin oldest segments (the ones with the smallest
+** values for the _segdir.idx field) in the highest level that contains
+** at least nMin segments. Multiple merges might occur in an attempt to
** write the quota of nMerge leaf blocks.
*/
SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
@@ -173245,7 +199737,7 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
/* Allocate space for the cursor, filter and writer objects */
const int nAlloc = sizeof(*pCsr) + sizeof(*pFilter) + sizeof(*pWriter);
- pWriter = (IncrmergeWriter *)sqlite3_malloc(nAlloc);
+ pWriter = (IncrmergeWriter *)sqlite3_malloc64(nAlloc);
if( !pWriter ) return SQLITE_NOMEM;
pFilter = (Fts3SegFilter *)&pWriter[1];
pCsr = (Fts3MultiSegReader *)&pFilter[1];
@@ -173260,7 +199752,7 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
/* Search the %_segdir table for the absolute level with the smallest
** relative level number that contains at least nMin segments, if any.
** If one is found, set iAbsLevel to the absolute level number and
- ** nSeg to nMin. If no level with at least nMin segments can be found,
+ ** nSeg to nMin. If no level with at least nMin segments can be found,
** set nSeg to -1.
*/
rc = fts3SqlStmt(p, SQL_FIND_MERGE_LEVEL, &pFindLevel, 0);
@@ -173276,7 +199768,7 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
/* If the hint read from the %_stat table is not empty, check if the
** last entry in it specifies a relative level smaller than or equal
- ** to the level identified by the block above (if any). If so, this
+ ** to the level identified by the block above (if any). If so, this
** iteration of the loop will work on merging at the hinted level.
*/
if( rc==SQLITE_OK && hint.n ){
@@ -173286,8 +199778,14 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
rc = fts3IncrmergeHintPop(&hint, &iHintAbsLevel, &nHintSeg);
if( nSeg<0 || (iAbsLevel % nMod) >= (iHintAbsLevel % nMod) ){
+ /* Based on the scan in the block above, it is known that there
+ ** are no levels with a relative level smaller than that of
+ ** iAbsLevel with more than nSeg segments, or if nSeg is -1,
+ ** no levels with more than nMin segments. Use this to limit the
+ ** value of nHintSeg to avoid a large memory allocation in case the
+ ** merge-hint is corrupt*/
iAbsLevel = iHintAbsLevel;
- nSeg = nHintSeg;
+ nSeg = MIN(MAX(nMin,nSeg), nHintSeg);
bUseHint = 1;
bDirtyHint = 1;
}else{
@@ -173300,13 +199798,19 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
/* If nSeg is less that zero, then there is no level with at least
** nMin segments and no hint in the %_stat table. No work to do.
** Exit early in this case. */
- if( nSeg<0 ) break;
+ if( nSeg<=0 ) break;
- /* Open a cursor to iterate through the contents of the oldest nSeg
- ** indexes of absolute level iAbsLevel. If this cursor is opened using
+ assert( nMod<=0x7FFFFFFF );
+ if( iAbsLevel<0 || iAbsLevel>(nMod<<32) ){
+ rc = FTS_CORRUPT_VTAB;
+ break;
+ }
+
+ /* Open a cursor to iterate through the contents of the oldest nSeg
+ ** indexes of absolute level iAbsLevel. If this cursor is opened using
** the 'hint' parameters, it is possible that there are less than nSeg
** segments available in level iAbsLevel. In this case, no work is
- ** done on iAbsLevel - fall through to the next iteration of the loop
+ ** done on iAbsLevel - fall through to the next iteration of the loop
** to start work on some other level. */
memset(pWriter, 0, nAlloc);
pFilter->flags = FTS3_SEGMENT_REQUIRE_POS;
@@ -173328,8 +199832,15 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
}
if( SQLITE_OK==rc && pCsr->nSegment==nSeg
&& SQLITE_OK==(rc = sqlite3Fts3SegReaderStart(p, pCsr, pFilter))
- && SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pCsr))
){
+ int bEmpty = 0;
+ rc = sqlite3Fts3SegReaderStep(p, pCsr);
+ if( rc==SQLITE_OK ){
+ bEmpty = 1;
+ }else if( rc!=SQLITE_ROW ){
+ sqlite3Fts3SegReaderFinish(pCsr);
+ break;
+ }
if( bUseHint && iIdx>0 ){
const char *zKey = pCsr->zTerm;
int nKey = pCsr->nTerm;
@@ -173340,11 +199851,13 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
if( rc==SQLITE_OK && pWriter->nLeafEst ){
fts3LogMerge(nSeg, iAbsLevel);
- do {
- rc = fts3IncrmergeAppend(p, pWriter, pCsr);
- if( rc==SQLITE_OK ) rc = sqlite3Fts3SegReaderStep(p, pCsr);
- if( pWriter->nWork>=nRem && rc==SQLITE_ROW ) rc = SQLITE_OK;
- }while( rc==SQLITE_ROW );
+ if( bEmpty==0 ){
+ do {
+ rc = fts3IncrmergeAppend(p, pWriter, pCsr);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts3SegReaderStep(p, pCsr);
+ if( pWriter->nWork>=nRem && rc==SQLITE_ROW ) rc = SQLITE_OK;
+ }while( rc==SQLITE_ROW );
+ }
/* Update or delete the input segments */
if( rc==SQLITE_OK ){
@@ -173385,7 +199898,7 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
** the integer.
**
** This function used for parameters to merge= and incrmerge=
-** commands.
+** commands.
*/
static int fts3Getint(const char **pz){
const char *z = *pz;
@@ -173409,7 +199922,7 @@ static int fts3DoIncrmerge(
const char *zParam /* Nul-terminated string containing "A,B" */
){
int rc;
- int nMin = (FTS3_MERGE_COUNT / 2);
+ int nMin = (MergeCount(p) / 2);
int nMerge = 0;
const char *z = zParam;
@@ -173454,7 +199967,7 @@ static int fts3DoAutoincrmerge(
int rc = SQLITE_OK;
sqlite3_stmt *pStmt = 0;
p->nAutoincrmerge = fts3Getint(&zParam);
- if( p->nAutoincrmerge==1 || p->nAutoincrmerge>FTS3_MERGE_COUNT ){
+ if( p->nAutoincrmerge==1 || p->nAutoincrmerge>MergeCount(p) ){
p->nAutoincrmerge = 8;
}
if( !p->bHasStat ){
@@ -173516,7 +200029,7 @@ static u64 fts3ChecksumIndex(
int rc;
u64 cksum = 0;
- assert( *pRc==SQLITE_OK );
+ if( *pRc ) return 0;
memset(&filter, 0, sizeof(filter));
memset(&csr, 0, sizeof(csr));
@@ -173537,12 +200050,12 @@ static u64 fts3ChecksumIndex(
i64 iDocid = 0;
i64 iCol = 0;
- i64 iPos = 0;
+ u64 iPos = 0;
pCsr += sqlite3Fts3GetVarint(pCsr, &iDocid);
while( pCsr<pEnd ){
- i64 iVal = 0;
- pCsr += sqlite3Fts3GetVarint(pCsr, &iVal);
+ u64 iVal = 0;
+ pCsr += sqlite3Fts3GetVarintU(pCsr, &iVal);
if( pCsr<pEnd ){
if( iVal==0 || iVal==1 ){
iCol = 0;
@@ -173550,8 +200063,12 @@ static u64 fts3ChecksumIndex(
if( iVal ){
pCsr += sqlite3Fts3GetVarint(pCsr, &iCol);
}else{
- pCsr += sqlite3Fts3GetVarint(pCsr, &iVal);
- iDocid += iVal;
+ pCsr += sqlite3Fts3GetVarintU(pCsr, &iVal);
+ if( p->bDescIdx ){
+ iDocid = (i64)((u64)iDocid - iVal);
+ }else{
+ iDocid = (i64)((u64)iDocid + iVal);
+ }
}
}else{
iPos += (iVal - 2);
@@ -173576,10 +200093,10 @@ static u64 fts3ChecksumIndex(
** to true and return SQLITE_OK. Or if the contents do not match, set *pbOk
** to false before returning.
**
-** If an error occurs (e.g. an OOM or IO error), return an SQLite error
+** If an error occurs (e.g. an OOM or IO error), return an SQLite error
** code. The final value of *pbOk is undefined in this case.
*/
-static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
+SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
int rc = SQLITE_OK; /* Return code */
u64 cksum1 = 0; /* Checksum based on FTS index contents */
u64 cksum2 = 0; /* Checksum based on %_content contents */
@@ -173607,7 +200124,7 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
sqlite3_tokenizer_module const *pModule = p->pTokenizer->pModule;
sqlite3_stmt *pStmt = 0;
char *zSql;
-
+
zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist);
if( !zSql ){
rc = SQLITE_NOMEM;
@@ -173624,10 +200141,9 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
if( p->abNotindexed[iCol]==0 ){
const char *zText = (const char *)sqlite3_column_text(pStmt, iCol+1);
- int nText = sqlite3_column_bytes(pStmt, iCol+1);
sqlite3_tokenizer_cursor *pT = 0;
- rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, nText,&pT);
+ rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, -1, &pT);
while( rc==SQLITE_OK ){
char const *zToken; /* Buffer containing token */
int nToken = 0; /* Number of bytes in token */
@@ -173658,7 +200174,7 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
sqlite3_finalize(pStmt);
}
- *pbOk = (cksum1==cksum2);
+ *pbOk = (rc==SQLITE_OK && cksum1==cksum2);
return rc;
}
@@ -173667,7 +200183,7 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
** the FTS index are correct, return SQLITE_OK. Or, if the contents of the
** FTS index are incorrect, return SQLITE_CORRUPT_VTAB.
**
-** Or, if an error (e.g. an OOM or IO error) occurs, return an SQLite
+** Or, if an error (e.g. an OOM or IO error) occurs, return an SQLite
** error code.
**
** The integrity-check works as follows. For each token and indexed token
@@ -173676,7 +200192,7 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
**
** + The index number (0 for the main index, 1 for the first prefix
** index etc.),
-** + The token (or token prefix) text itself,
+** + The token (or token prefix) text itself,
** + The language-id of the row it appears in,
** + The docid of the row it appears in,
** + The column it appears in, and
@@ -173687,7 +200203,7 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
**
** The integrity-check code calculates the same checksum in two ways:
**
-** 1. By scanning the contents of the FTS index, and
+** 1. By scanning the contents of the FTS index, and
** 2. By scanning and tokenizing the content table.
**
** If the two checksums are identical, the integrity-check is deemed to have
@@ -173698,7 +200214,7 @@ static int fts3DoIntegrityCheck(
){
int rc;
int bOk = 0;
- rc = fts3IntegrityCheck(p, &bOk);
+ rc = sqlite3Fts3IntegrityCheck(p, &bOk);
if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB;
return rc;
}
@@ -173708,11 +200224,11 @@ static int fts3DoIntegrityCheck(
**
** "INSERT INTO tbl(tbl) VALUES(<expr>)"
**
-** Argument pVal contains the result of <expr>. Currently the only
+** Argument pVal contains the result of <expr>. Currently the only
** meaningful value to insert is the text 'optimize'.
*/
static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
- int rc; /* Return Code */
+ int rc = SQLITE_ERROR; /* Return Code */
const char *zVal = (const char *)sqlite3_value_text(pVal);
int nVal = sqlite3_value_bytes(pVal);
@@ -173728,21 +200244,30 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
rc = fts3DoIncrmerge(p, &zVal[6]);
}else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){
rc = fts3DoAutoincrmerge(p, &zVal[10]);
-#ifdef SQLITE_TEST
- }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
- p->nNodeSize = atoi(&zVal[9]);
- rc = SQLITE_OK;
- }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
- p->nMaxPendingData = atoi(&zVal[11]);
- rc = SQLITE_OK;
- }else if( nVal>21 && 0==sqlite3_strnicmp(zVal, "test-no-incr-doclist=", 21) ){
- p->bNoIncrDoclist = atoi(&zVal[21]);
- rc = SQLITE_OK;
-#endif
- }else{
- rc = SQLITE_ERROR;
+ }else if( nVal==5 && 0==sqlite3_strnicmp(zVal, "flush", 5) ){
+ rc = sqlite3Fts3PendingTermsFlush(p);
}
-
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+ else{
+ int v;
+ if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
+ v = atoi(&zVal[9]);
+ if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v;
+ rc = SQLITE_OK;
+ }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
+ v = atoi(&zVal[11]);
+ if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v;
+ rc = SQLITE_OK;
+ }else if( nVal>21 && 0==sqlite3_strnicmp(zVal,"test-no-incr-doclist=",21) ){
+ p->bNoIncrDoclist = atoi(&zVal[21]);
+ rc = SQLITE_OK;
+ }else if( nVal>11 && 0==sqlite3_strnicmp(zVal,"mergecount=",11) ){
+ v = atoi(&zVal[11]);
+ if( v>=4 && v<=FTS3_MERGE_COUNT && (v&1)==0 ) p->nMergeCount = v;
+ rc = SQLITE_OK;
+ }
+ }
+#endif
return rc;
}
@@ -173760,7 +200285,7 @@ SQLITE_PRIVATE void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *pCsr){
}
/*
-** Free all entries in the pCsr->pDeffered list. Entries are added to
+** Free all entries in the pCsr->pDeffered list. Entries are added to
** this list using sqlite3Fts3DeferToken().
*/
SQLITE_PRIVATE void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *pCsr){
@@ -173788,14 +200313,14 @@ SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *pCsr){
int i; /* Used to iterate through table columns */
sqlite3_int64 iDocid; /* Docid of the row pCsr points to */
Fts3DeferredToken *pDef; /* Used to iterate through deferred tokens */
-
+
Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
sqlite3_tokenizer *pT = p->pTokenizer;
sqlite3_tokenizer_module const *pModule = pT->pModule;
-
+
assert( pCsr->isRequireSeek==0 );
iDocid = sqlite3_column_int64(pCsr->pStmt, 0);
-
+
for(i=0; i<p->nColumn && rc==SQLITE_OK; i++){
if( p->abNotindexed[i]==0 ){
const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1);
@@ -173836,8 +200361,8 @@ SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *pCsr){
}
SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(
- Fts3DeferredToken *p,
- char **ppData,
+ Fts3DeferredToken *p,
+ char **ppData,
int *pnData
){
char *pRet;
@@ -173851,13 +200376,13 @@ SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(
return SQLITE_OK;
}
- pRet = (char *)sqlite3_malloc(p->pList->nData);
+ pRet = (char *)sqlite3_malloc64(p->pList->nData);
if( !pRet ) return SQLITE_NOMEM;
nSkip = sqlite3Fts3GetVarint(p->pList->aData, &dummy);
*pnData = p->pList->nData - nSkip;
*ppData = pRet;
-
+
memcpy(pRet, &p->pList->aData[nSkip], *pnData);
return SQLITE_OK;
}
@@ -173871,13 +200396,13 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken(
int iCol /* Column that token must appear in (or -1) */
){
Fts3DeferredToken *pDeferred;
- pDeferred = sqlite3_malloc(sizeof(*pDeferred));
+ pDeferred = sqlite3_malloc64(sizeof(*pDeferred));
if( !pDeferred ){
return SQLITE_NOMEM;
}
memset(pDeferred, 0, sizeof(*pDeferred));
pDeferred->pToken = pToken;
- pDeferred->pNext = pCsr->pDeferred;
+ pDeferred->pNext = pCsr->pDeferred;
pDeferred->iCol = iCol;
pCsr->pDeferred = pDeferred;
@@ -173894,8 +200419,8 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken(
** of subsiduary data structures accordingly.
*/
static int fts3DeleteByRowid(
- Fts3Table *p,
- sqlite3_value *pRowid,
+ Fts3Table *p,
+ sqlite3_value *pRowid,
int *pnChng, /* IN/OUT: Decrement if row is deleted */
u32 *aSzDel
){
@@ -173933,14 +200458,14 @@ static int fts3DeleteByRowid(
** This function does the work for the xUpdate method of FTS3 virtual
** tables. The schema of the virtual table being:
**
-** CREATE TABLE <table name>(
+** CREATE TABLE <table name>(
** <user columns>,
-** <table name> HIDDEN,
-** docid HIDDEN,
+** <table name> HIDDEN,
+** docid HIDDEN,
** <langid> HIDDEN
** );
**
-**
+**
*/
SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
sqlite3_vtab *pVtab, /* FTS3 vtab object */
@@ -173960,7 +200485,7 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
assert( p->bHasStat==0 || p->bHasStat==1 );
assert( p->pSegments==0 );
- assert(
+ assert(
nArg==1 /* DELETE operations */
|| nArg==(2 + p->nColumn + 3) /* INSERT or UPDATE operations */
);
@@ -173969,9 +200494,9 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
**
** INSERT INTO xyz(xyz) VALUES('command');
*/
- if( nArg>1
- && sqlite3_value_type(apVal[0])==SQLITE_NULL
- && sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL
+ if( nArg>1
+ && sqlite3_value_type(apVal[0])==SQLITE_NULL
+ && sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL
){
rc = fts3SpecialInsert(p, apVal[p->nColumn+2]);
goto update_out;
@@ -173983,7 +200508,7 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
}
/* Allocate space to hold the change in document sizes */
- aSzDel = sqlite3_malloc( sizeof(aSzDel[0])*(p->nColumn+1)*2 );
+ aSzDel = sqlite3_malloc64(sizeof(aSzDel[0])*((sqlite3_int64)p->nColumn+1)*2);
if( aSzDel==0 ){
rc = SQLITE_NOMEM;
goto update_out;
@@ -174010,24 +200535,24 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
pNewRowid = apVal[1];
}
- if( sqlite3_value_type(pNewRowid)!=SQLITE_NULL && (
+ if( sqlite3_value_type(pNewRowid)!=SQLITE_NULL && (
sqlite3_value_type(apVal[0])==SQLITE_NULL
|| sqlite3_value_int64(apVal[0])!=sqlite3_value_int64(pNewRowid)
)){
/* The new rowid is not NULL (in this case the rowid will be
- ** automatically assigned and there is no chance of a conflict), and
+ ** automatically assigned and there is no chance of a conflict), and
** the statement is either an INSERT or an UPDATE that modifies the
** rowid column. So if the conflict mode is REPLACE, then delete any
- ** existing row with rowid=pNewRowid.
+ ** existing row with rowid=pNewRowid.
**
- ** Or, if the conflict mode is not REPLACE, insert the new record into
+ ** Or, if the conflict mode is not REPLACE, insert the new record into
** the %_content table. If we hit the duplicate rowid constraint (or any
** other error) while doing so, return immediately.
**
** This branch may also run if pNewRowid contains a value that cannot
- ** be losslessly converted to an integer. In this case, the eventual
+ ** be losslessly converted to an integer. In this case, the eventual
** call to fts3InsertData() (either just below or further on in this
- ** function) will return SQLITE_MISMATCH. If fts3DeleteByRowid is
+ ** function) will return SQLITE_MISMATCH. If fts3DeleteByRowid is
** invoked, it will delete zero rows (since no row will have
** docid=$pNewRowid if $pNewRowid is not an integer value).
*/
@@ -174048,7 +200573,7 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER );
rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel);
}
-
+
/* If this is an INSERT or UPDATE operation, insert the new record. */
if( nArg>1 && rc==SQLITE_OK ){
int iLangid = sqlite3_value_int(apVal[2 + p->nColumn + 2]);
@@ -174081,10 +200606,10 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
return rc;
}
-/*
+/*
** Flush any data in the pending-terms hash table to disk. If successful,
-** merge all segments in the database (including the new segment, if
-** there was any data to flush) into a single segment.
+** merge all segments in the database (including the new segment, if
+** there was any data to flush) into a single segment.
*/
SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){
int rc;
@@ -174126,6 +200651,10 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){
/* #include <string.h> */
/* #include <assert.h> */
+#ifndef SQLITE_AMALGAMATION
+typedef sqlite3_int64 i64;
+#endif
+
/*
** Characters that may appear in the second argument to matchinfo().
*/
@@ -174140,13 +200669,13 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){
#define FTS3_MATCHINFO_LHITS_BM 'b' /* nCol*nPhrase values */
/*
-** The default value for the second argument to matchinfo().
+** The default value for the second argument to matchinfo().
*/
#define FTS3_MATCHINFO_DEFAULT "pcx"
/*
-** Used as an fts3ExprIterate() context when loading phrase doclists to
+** Used as an sqlite3Fts3ExprIterate() context when loading phrase doclists to
** Fts3Expr.aDoclist[]/nDoclist.
*/
typedef struct LoadDoclistCtx LoadDoclistCtx;
@@ -174157,7 +200686,7 @@ struct LoadDoclistCtx {
};
/*
-** The following types are used as part of the implementation of the
+** The following types are used as part of the implementation of the
** fts3BestSnippet() routine.
*/
typedef struct SnippetIter SnippetIter;
@@ -174176,9 +200705,9 @@ struct SnippetIter {
struct SnippetPhrase {
int nToken; /* Number of tokens in phrase */
char *pList; /* Pointer to start of phrase position list */
- int iHead; /* Next value in position list */
+ i64 iHead; /* Next value in position list */
char *pHead; /* Position list data following iHead */
- int iTail; /* Next value in trailing position list */
+ i64 iTail; /* Next value in trailing position list */
char *pTail; /* Position list data following iTail */
};
@@ -174190,7 +200719,7 @@ struct SnippetFragment {
};
/*
-** This type is used as an fts3ExprIterate() context object while
+** This type is used as an sqlite3Fts3ExprIterate() context object while
** accumulating the data returned by the matchinfo() function.
*/
typedef struct MatchInfo MatchInfo;
@@ -174237,17 +200766,18 @@ struct StrBuffer {
/*
** Allocate a two-slot MatchinfoBuffer object.
*/
-static MatchinfoBuffer *fts3MIBufferNew(int nElem, const char *zMatchinfo){
+static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){
MatchinfoBuffer *pRet;
- int nByte = sizeof(u32) * (2*nElem + 1) + sizeof(MatchinfoBuffer);
- int nStr = (int)strlen(zMatchinfo);
+ sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1)
+ + sizeof(MatchinfoBuffer);
+ sqlite3_int64 nStr = strlen(zMatchinfo);
- pRet = sqlite3_malloc(nByte + nStr+1);
+ pRet = sqlite3Fts3MallocZero(nByte + nStr+1);
if( pRet ){
- memset(pRet, 0, nByte);
pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet;
- pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + sizeof(u32)*(nElem+1);
- pRet->nElem = nElem;
+ pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0]
+ + sizeof(u32)*((int)nElem+1);
+ pRet->nElem = (int)nElem;
pRet->zMatchinfo = ((char*)pRet) + nByte;
memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1);
pRet->aRef[0] = 1;
@@ -174259,8 +200789,8 @@ static MatchinfoBuffer *fts3MIBufferNew(int nElem, const char *zMatchinfo){
static void fts3MIBufferFree(void *p){
MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]);
- assert( (u32*)p==&pBuf->aMatchinfo[1]
- || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2]
+ assert( (u32*)p==&pBuf->aMatchinfo[1]
+ || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2]
);
if( (u32*)p==&pBuf->aMatchinfo[1] ){
pBuf->aRef[1] = 0;
@@ -174287,7 +200817,7 @@ static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){
aOut = &p->aMatchinfo[p->nElem+2];
xRet = fts3MIBufferFree;
}else{
- aOut = (u32*)sqlite3_malloc(p->nElem * sizeof(u32));
+ aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32));
if( aOut ){
xRet = sqlite3_free;
if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32));
@@ -174316,7 +200846,7 @@ SQLITE_PRIVATE void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p){
}
}
-/*
+/*
** End of MatchinfoBuffer code.
*************************************************************************/
@@ -174341,14 +200871,14 @@ SQLITE_PRIVATE void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p){
** After it returns, *piPos contains the value of the next element of the
** list and *pp is advanced to the following varint.
*/
-static void fts3GetDeltaPosition(char **pp, int *piPos){
+static void fts3GetDeltaPosition(char **pp, i64 *piPos){
int iVal;
*pp += fts3GetVarint32(*pp, &iVal);
*piPos += (iVal-2);
}
/*
-** Helper function for fts3ExprIterate() (see below).
+** Helper function for sqlite3Fts3ExprIterate() (see below).
*/
static int fts3ExprIterate2(
Fts3Expr *pExpr, /* Expression to iterate phrases of */
@@ -174377,12 +200907,12 @@ static int fts3ExprIterate2(
** are part of a sub-tree that is the right-hand-side of a NOT operator.
** For each phrase node found, the supplied callback function is invoked.
**
-** If the callback function returns anything other than SQLITE_OK,
+** If the callback function returns anything other than SQLITE_OK,
** the iteration is abandoned and the error code returned immediately.
** Otherwise, SQLITE_OK is returned after a callback has been made for
** all eligible phrase nodes.
*/
-static int fts3ExprIterate(
+SQLITE_PRIVATE int sqlite3Fts3ExprIterate(
Fts3Expr *pExpr, /* Expression to iterate phrases of */
int (*x)(Fts3Expr*,int,void*), /* Callback function to invoke for phrases */
void *pCtx /* Second argument to pass to callback */
@@ -174391,10 +200921,9 @@ static int fts3ExprIterate(
return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx);
}
-
/*
-** This is an fts3ExprIterate() callback used while loading the doclists
-** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
+** This is an sqlite3Fts3ExprIterate() callback used while loading the
+** doclists for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
** fts3ExprLoadDoclists().
*/
static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
@@ -174412,11 +200941,11 @@ static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
/*
** Load the doclists for each phrase in the query associated with FTS3 cursor
-** pCsr.
+** pCsr.
**
-** If pnPhrase is not NULL, then *pnPhrase is set to the number of matchable
-** phrases in the expression (all phrases except those directly or
-** indirectly descended from the right-hand-side of a NOT operator). If
+** If pnPhrase is not NULL, then *pnPhrase is set to the number of matchable
+** phrases in the expression (all phrases except those directly or
+** indirectly descended from the right-hand-side of a NOT operator). If
** pnToken is not NULL, then it is set to the number of tokens in all
** matchable phrases of the expression.
*/
@@ -174426,9 +200955,9 @@ static int fts3ExprLoadDoclists(
int *pnToken /* OUT: Number of tokens in query */
){
int rc; /* Return Code */
- LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */
+ LoadDoclistCtx sCtx = {0,0,0}; /* Context for sqlite3Fts3ExprIterate() */
sCtx.pCsr = pCsr;
- rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb, (void *)&sCtx);
+ rc = sqlite3Fts3ExprIterate(pCsr->pExpr,fts3ExprLoadDoclistsCb,(void*)&sCtx);
if( pnPhrase ) *pnPhrase = sCtx.nPhrase;
if( pnToken ) *pnToken = sCtx.nToken;
return rc;
@@ -174441,19 +200970,19 @@ static int fts3ExprPhraseCountCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
}
static int fts3ExprPhraseCount(Fts3Expr *pExpr){
int nPhrase = 0;
- (void)fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase);
+ (void)sqlite3Fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase);
return nPhrase;
}
/*
-** Advance the position list iterator specified by the first two
+** Advance the position list iterator specified by the first two
** arguments so that it points to the first element with a value greater
** than or equal to parameter iNext.
*/
-static void fts3SnippetAdvance(char **ppIter, int *piIter, int iNext){
+static void fts3SnippetAdvance(char **ppIter, i64 *piIter, int iNext){
char *pIter = *ppIter;
if( pIter ){
- int iIter = *piIter;
+ i64 iIter = *piIter;
while( iIter<iNext ){
if( 0==(*pIter & 0xFE) ){
@@ -174515,7 +201044,7 @@ static int fts3SnippetNextCandidate(SnippetIter *pIter){
}
/*
-** Retrieve information about the current candidate snippet of snippet
+** Retrieve information about the current candidate snippet of snippet
** iterator pIter.
*/
static void fts3SnippetDetails(
@@ -174536,13 +201065,14 @@ static void fts3SnippetDetails(
SnippetPhrase *pPhrase = &pIter->aPhrase[i];
if( pPhrase->pTail ){
char *pCsr = pPhrase->pTail;
- int iCsr = pPhrase->iTail;
+ i64 iCsr = pPhrase->iTail;
- while( iCsr<(iStart+pIter->nSnippet) ){
+ while( iCsr<(iStart+pIter->nSnippet) && iCsr>=iStart ){
int j;
- u64 mPhrase = (u64)1 << i;
+ u64 mPhrase = (u64)1 << (i%64);
u64 mPos = (u64)1 << (iCsr - iStart);
- assert( iCsr>=iStart );
+ assert( iCsr>=iStart && (iCsr - iStart)<=64 );
+ assert( i>=0 );
if( (mCover|mCovered)&mPhrase ){
iScore++;
}else{
@@ -174568,8 +201098,9 @@ static void fts3SnippetDetails(
}
/*
-** This function is an fts3ExprIterate() callback used by fts3BestSnippet().
-** Each invocation populates an element of the SnippetIter.aPhrase[] array.
+** This function is an sqlite3Fts3ExprIterate() callback used by
+** fts3BestSnippet(). Each invocation populates an element of the
+** SnippetIter.aPhrase[] array.
*/
static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
SnippetIter *p = (SnippetIter *)ctx;
@@ -174581,17 +201112,20 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
rc = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pCsr);
assert( rc==SQLITE_OK || pCsr==0 );
if( pCsr ){
- int iFirst = 0;
+ i64 iFirst = 0;
pPhrase->pList = pCsr;
fts3GetDeltaPosition(&pCsr, &iFirst);
- assert( iFirst>=0 );
- pPhrase->pHead = pCsr;
- pPhrase->pTail = pCsr;
- pPhrase->iHead = iFirst;
- pPhrase->iTail = iFirst;
+ if( iFirst<0 ){
+ rc = FTS_CORRUPT_VTAB;
+ }else{
+ pPhrase->pHead = pCsr;
+ pPhrase->pTail = pCsr;
+ pPhrase->iHead = iFirst;
+ pPhrase->iTail = iFirst;
+ }
}else{
assert( rc!=SQLITE_OK || (
- pPhrase->pList==0 && pPhrase->pHead==0 && pPhrase->pTail==0
+ pPhrase->pList==0 && pPhrase->pHead==0 && pPhrase->pTail==0
));
}
@@ -174599,14 +201133,14 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
}
/*
-** Select the fragment of text consisting of nFragment contiguous tokens
+** Select the fragment of text consisting of nFragment contiguous tokens
** from column iCol that represent the "best" snippet. The best snippet
** is the snippet with the highest score, where scores are calculated
** by adding:
**
** (a) +1 point for each occurrence of a matchable phrase in the snippet.
**
-** (b) +1000 points for the first occurrence of each matchable phrase in
+** (b) +1000 points for the first occurrence of each matchable phrase in
** the snippet for which the corresponding mCovered bit is not set.
**
** The selected snippet parameters are stored in structure *pFragment before
@@ -174625,7 +201159,7 @@ static int fts3BestSnippet(
int rc; /* Return Code */
int nList; /* Number of phrases in expression */
SnippetIter sIter; /* Iterates through snippet candidates */
- int nByte; /* Number of bytes of space to allocate */
+ sqlite3_int64 nByte; /* Number of bytes of space to allocate */
int iBestScore = -1; /* Best snippet score found so far */
int i; /* Loop counter */
@@ -174643,11 +201177,10 @@ static int fts3BestSnippet(
** the required space using malloc().
*/
nByte = sizeof(SnippetPhrase) * nList;
- sIter.aPhrase = (SnippetPhrase *)sqlite3_malloc(nByte);
+ sIter.aPhrase = (SnippetPhrase *)sqlite3Fts3MallocZero(nByte);
if( !sIter.aPhrase ){
return SQLITE_NOMEM;
}
- memset(sIter.aPhrase, 0, nByte);
/* Initialize the contents of the SnippetIter object. Then iterate through
** the set of phrases in the expression to populate the aPhrase[] array.
@@ -174657,17 +201190,19 @@ static int fts3BestSnippet(
sIter.nSnippet = nSnippet;
sIter.nPhrase = nList;
sIter.iCurrent = -1;
- rc = fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter);
+ rc = sqlite3Fts3ExprIterate(
+ pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter
+ );
if( rc==SQLITE_OK ){
/* Set the *pmSeen output variable. */
for(i=0; i<nList; i++){
if( sIter.aPhrase[i].pHead ){
- *pmSeen |= (u64)1 << i;
+ *pmSeen |= (u64)1 << (i%64);
}
}
- /* Loop through all candidate snippets. Store the best snippet in
+ /* Loop through all candidate snippets. Store the best snippet in
** *pFragment. Store its associated 'score' in iBestScore.
*/
pFragment->iCol = iCol;
@@ -174713,8 +201248,8 @@ static int fts3StringAppend(
** appended data.
*/
if( pStr->n+nAppend+1>=pStr->nAlloc ){
- int nAlloc = pStr->nAlloc+nAppend+100;
- char *zNew = sqlite3_realloc(pStr->z, nAlloc);
+ sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100;
+ char *zNew = sqlite3_realloc64(pStr->z, nAlloc);
if( !zNew ){
return SQLITE_NOMEM;
}
@@ -174739,8 +201274,8 @@ static int fts3StringAppend(
**
** ........X.....X
**
-** This function "shifts" the beginning of the snippet forward in the
-** document so that there are approximately the same number of
+** This function "shifts" the beginning of the snippet forward in the
+** document so that there are approximately the same number of
** non-highlighted terms to the right of the final highlighted term as there
** are to the left of the first highlighted term. For example, to this:
**
@@ -174748,8 +201283,8 @@ static int fts3StringAppend(
**
** This is done as part of extracting the snippet text, not when selecting
** the snippet. Snippet selection is done based on doclists only, so there
-** is no way for fts3BestSnippet() to know whether or not the document
-** actually contains terms that follow the final highlighted term.
+** is no way for fts3BestSnippet() to know whether or not the document
+** actually contains terms that follow the final highlighted term.
*/
static int fts3SnippetShift(
Fts3Table *pTab, /* FTS3 table snippet comes from */
@@ -174769,6 +201304,7 @@ static int fts3SnippetShift(
for(nLeft=0; !(hlmask & ((u64)1 << nLeft)); nLeft++);
for(nRight=0; !(hlmask & ((u64)1 << (nSnippet-1-nRight))); nRight++);
+ assert( (nSnippet-1-nRight)<=63 && (nSnippet-1-nRight)>=0 );
nDesired = (nLeft-nRight)/2;
/* Ideally, the start of the snippet should be pushed forward in the
@@ -174838,7 +201374,7 @@ static int fts3SnippetText(
int iCol = pFragment->iCol+1; /* Query column to extract text from */
sqlite3_tokenizer_module *pMod; /* Tokenizer module methods object */
sqlite3_tokenizer_cursor *pC; /* Tokenizer cursor open on zDoc/nDoc */
-
+
zDoc = (const char *)sqlite3_column_text(pCsr->pStmt, iCol);
if( zDoc==0 ){
if( sqlite3_column_type(pCsr->pStmt, iCol)!=SQLITE_NULL ){
@@ -174878,7 +201414,7 @@ static int fts3SnippetText(
if( rc==SQLITE_DONE ){
/* Special case - the last token of the snippet is also the last token
** of the column. Append any punctuation that occurred between the end
- ** of the previous token and the end of the document to the output.
+ ** of the previous token and the end of the document to the output.
** Then break out of the loop. */
rc = fts3StringAppend(pOut, &zDoc[iEnd], -1);
}
@@ -174895,7 +201431,7 @@ static int fts3SnippetText(
/* Now that the shift has been done, check if the initial "..." are
** required. They are required if (a) this is not the first fragment,
- ** or (b) this fragment does not begin at position 0 of its column.
+ ** or (b) this fragment does not begin at position 0 of its column.
*/
if( rc==SQLITE_OK ){
if( iPos>0 || iFragment>0 ){
@@ -174931,8 +201467,8 @@ static int fts3SnippetText(
/*
-** This function is used to count the entries in a column-list (a
-** delta-encoded list of term offsets within a single column of a single
+** This function is used to count the entries in a column-list (a
+** delta-encoded list of term offsets within a single column of a single
** row). When this function is called, *ppCollist should point to the
** beginning of the first varint in the column-list (the varint that
** contains the position of the first matching term in the column data).
@@ -174961,7 +201497,7 @@ static int fts3ColumnlistCount(char **ppCollist){
/*
** This function gathers 'y' or 'b' data for a single phrase.
*/
-static void fts3ExprLHits(
+static int fts3ExprLHits(
Fts3Expr *pExpr, /* Phrase expression node */
MatchInfo *p /* Matchinfo context */
){
@@ -174978,7 +201514,7 @@ static void fts3ExprLHits(
iStart = pExpr->iPhrase * ((p->nCol + 31) / 32);
}
- while( 1 ){
+ if( pIter ) while( 1 ){
int nHit = fts3ColumnlistCount(&pIter);
if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){
if( p->flag==FTS3_MATCHINFO_LHITS ){
@@ -174991,34 +201527,38 @@ static void fts3ExprLHits(
if( *pIter!=0x01 ) break;
pIter++;
pIter += fts3GetVarint32(pIter, &iCol);
+ if( iCol>=p->nCol ) return FTS_CORRUPT_VTAB;
}
+ return SQLITE_OK;
}
/*
** Gather the results for matchinfo directives 'y' and 'b'.
*/
-static void fts3ExprLHitGather(
+static int fts3ExprLHitGather(
Fts3Expr *pExpr,
MatchInfo *p
){
+ int rc = SQLITE_OK;
assert( (pExpr->pLeft==0)==(pExpr->pRight==0) );
if( pExpr->bEof==0 && pExpr->iDocid==p->pCursor->iPrevId ){
if( pExpr->pLeft ){
- fts3ExprLHitGather(pExpr->pLeft, p);
- fts3ExprLHitGather(pExpr->pRight, p);
+ rc = fts3ExprLHitGather(pExpr->pLeft, p);
+ if( rc==SQLITE_OK ) rc = fts3ExprLHitGather(pExpr->pRight, p);
}else{
- fts3ExprLHits(pExpr, p);
+ rc = fts3ExprLHits(pExpr, p);
}
}
+ return rc;
}
/*
-** fts3ExprIterate() callback used to collect the "global" matchinfo stats
-** for a single query.
+** sqlite3Fts3ExprIterate() callback used to collect the "global" matchinfo
+** stats for a single query.
**
-** fts3ExprIterate() callback to load the 'global' elements of a
-** FTS3_MATCHINFO_HITS matchinfo array. The global stats are those elements
-** of the matchinfo array that are constant for all rows returned by the
+** sqlite3Fts3ExprIterate() callback to load the 'global' elements of a
+** FTS3_MATCHINFO_HITS matchinfo array. The global stats are those elements
+** of the matchinfo array that are constant for all rows returned by the
** current query.
**
** Argument pCtx is actually a pointer to a struct of type MatchInfo. This
@@ -175034,7 +201574,7 @@ static void fts3ExprLHitGather(
** at least one instance of phrase iPhrase.
**
** If the phrase pExpr consists entirely of deferred tokens, then all X and
-** Y values are set to nDoc, where nDoc is the number of documents in the
+** Y values are set to nDoc, where nDoc is the number of documents in the
** file system. This is done because the full-text index doclist is required
** to calculate these values properly, and the full-text index doclist is
** not available for deferred tokens.
@@ -175051,8 +201591,8 @@ static int fts3ExprGlobalHitsCb(
}
/*
-** fts3ExprIterate() callback used to collect the "local" part of the
-** FTS3_MATCHINFO_HITS array. The local stats are those elements of the
+** sqlite3Fts3ExprIterate() callback used to collect the "local" part of the
+** FTS3_MATCHINFO_HITS array. The local stats are those elements of the
** array that are different for each row returned by the query.
*/
static int fts3ExprLocalHitsCb(
@@ -175079,7 +201619,7 @@ static int fts3ExprLocalHitsCb(
}
static int fts3MatchinfoCheck(
- Fts3Table *pTab,
+ Fts3Table *pTab,
char cArg,
char **pzErr
){
@@ -175099,13 +201639,13 @@ static int fts3MatchinfoCheck(
return SQLITE_ERROR;
}
-static int fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
- int nVal; /* Number of integers output by cArg */
+static size_t fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
+ size_t nVal; /* Number of integers output by cArg */
switch( cArg ){
case FTS3_MATCHINFO_NDOC:
- case FTS3_MATCHINFO_NPHRASE:
- case FTS3_MATCHINFO_NCOL:
+ case FTS3_MATCHINFO_NPHRASE:
+ case FTS3_MATCHINFO_NCOL:
nVal = 1;
break;
@@ -175136,11 +201676,15 @@ static int fts3MatchinfoSelectDoctotal(
Fts3Table *pTab,
sqlite3_stmt **ppStmt,
sqlite3_int64 *pnDoc,
- const char **paLen
+ const char **paLen,
+ const char **ppEnd
){
sqlite3_stmt *pStmt;
const char *a;
+ const char *pEnd;
sqlite3_int64 nDoc;
+ int n;
+
if( !*ppStmt ){
int rc = sqlite3Fts3SelectDoctotal(pTab, ppStmt);
@@ -175149,17 +201693,25 @@ static int fts3MatchinfoSelectDoctotal(
pStmt = *ppStmt;
assert( sqlite3_data_count(pStmt)==1 );
+ n = sqlite3_column_bytes(pStmt, 0);
a = sqlite3_column_blob(pStmt, 0);
- a += sqlite3Fts3GetVarint(a, &nDoc);
- if( nDoc==0 ) return FTS_CORRUPT_VTAB;
- *pnDoc = (u32)nDoc;
+ if( a==0 ){
+ return FTS_CORRUPT_VTAB;
+ }
+ pEnd = a + n;
+ a += sqlite3Fts3GetVarintBounded(a, pEnd, &nDoc);
+ if( nDoc<=0 || a>pEnd ){
+ return FTS_CORRUPT_VTAB;
+ }
+ *pnDoc = nDoc;
if( paLen ) *paLen = a;
+ if( ppEnd ) *ppEnd = pEnd;
return SQLITE_OK;
}
/*
-** An instance of the following structure is used to store state while
+** An instance of the following structure is used to store state while
** iterating through a multi-column position-list corresponding to the
** hits for a single phrase on a single row in order to calculate the
** values for a matchinfo() FTS3_MATCHINFO_LCS request.
@@ -175172,7 +201724,7 @@ struct LcsIterator {
int iPos; /* Current position */
};
-/*
+/*
** If LcsIterator.iCol is set to the following value, the iterator has
** finished iterating through all offsets for all columns.
*/
@@ -175194,10 +201746,12 @@ static int fts3MatchinfoLcsCb(
** position list for the next column.
*/
static int fts3LcsIteratorAdvance(LcsIterator *pIter){
- char *pRead = pIter->pRead;
+ char *pRead;
sqlite3_int64 iRead;
int rc = 0;
+ if( NEVER(pIter==0) ) return 1;
+ pRead = pIter->pRead;
pRead += sqlite3Fts3GetVarint(pRead, &iRead);
if( iRead==0 || iRead==1 ){
pRead = 0;
@@ -175209,16 +201763,16 @@ static int fts3LcsIteratorAdvance(LcsIterator *pIter){
pIter->pRead = pRead;
return rc;
}
-
+
/*
-** This function implements the FTS3_MATCHINFO_LCS matchinfo() flag.
+** This function implements the FTS3_MATCHINFO_LCS matchinfo() flag.
**
** If the call is successful, the longest-common-substring lengths for each
-** column are written into the first nCol elements of the pInfo->aMatchinfo[]
+** column are written into the first nCol elements of the pInfo->aMatchinfo[]
** array before returning. SQLITE_OK is returned in this case.
**
** Otherwise, if an error occurs, an SQLite error code is returned and the
-** data written to the first nCol elements of pInfo->aMatchinfo[] is
+** data written to the first nCol elements of pInfo->aMatchinfo[] is
** undefined.
*/
static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){
@@ -175226,14 +201780,14 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){
int i;
int iCol;
int nToken = 0;
+ int rc = SQLITE_OK;
/* Allocate and populate the array of LcsIterator objects. The array
** contains one element for each matchable phrase in the query.
**/
- aIter = sqlite3_malloc(sizeof(LcsIterator) * pCsr->nPhrase);
+ aIter = sqlite3Fts3MallocZero(sizeof(LcsIterator) * pCsr->nPhrase);
if( !aIter ) return SQLITE_NOMEM;
- memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase);
- (void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter);
+ (void)sqlite3Fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter);
for(i=0; i<pInfo->nPhrase; i++){
LcsIterator *pIter = &aIter[i];
@@ -175246,13 +201800,16 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){
int nLive = 0; /* Number of iterators in aIter not at EOF */
for(i=0; i<pInfo->nPhrase; i++){
- int rc;
LcsIterator *pIt = &aIter[i];
rc = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol, &pIt->pRead);
- if( rc!=SQLITE_OK ) return rc;
+ if( rc!=SQLITE_OK ) goto matchinfo_lcs_out;
if( pIt->pRead ){
pIt->iPos = pIt->iPosOffset;
- fts3LcsIteratorAdvance(&aIter[i]);
+ fts3LcsIteratorAdvance(pIt);
+ if( pIt->pRead==0 ){
+ rc = FTS_CORRUPT_VTAB;
+ goto matchinfo_lcs_out;
+ }
nLive++;
}
}
@@ -175284,13 +201841,14 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){
pInfo->aMatchinfo[iCol] = nLcs;
}
+ matchinfo_lcs_out:
sqlite3_free(aIter);
- return SQLITE_OK;
+ return rc;
}
/*
** Populate the buffer pInfo->aMatchinfo[] with an array of integers to
-** be returned by the matchinfo() function. Argument zArg contains the
+** be returned by the matchinfo() function. Argument zArg contains the
** format string passed as the second argument to matchinfo (or the
** default value "pcx" if no second argument was specified). The format
** string has already been validated and the pInfo->aMatchinfo[] array
@@ -175301,7 +201859,7 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){
** rows (i.e. FTS3_MATCHINFO_NPHRASE, NCOL, NDOC, AVGLENGTH and part of HITS)
** have already been populated.
**
-** Return SQLITE_OK if successful, or an SQLite error code if an error
+** Return SQLITE_OK if successful, or an SQLite error code if an error
** occurs. If a value other than SQLITE_OK is returned, the state the
** pInfo->aMatchinfo[] buffer is left in is undefined.
*/
@@ -175326,27 +201884,32 @@ static int fts3MatchinfoValues(
case FTS3_MATCHINFO_NCOL:
if( bGlobal ) pInfo->aMatchinfo[0] = pInfo->nCol;
break;
-
+
case FTS3_MATCHINFO_NDOC:
if( bGlobal ){
sqlite3_int64 nDoc = 0;
- rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0);
+ rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0, 0);
pInfo->aMatchinfo[0] = (u32)nDoc;
}
break;
- case FTS3_MATCHINFO_AVGLENGTH:
+ case FTS3_MATCHINFO_AVGLENGTH:
if( bGlobal ){
sqlite3_int64 nDoc; /* Number of rows in table */
const char *a; /* Aggregate column length array */
+ const char *pEnd; /* First byte past end of length array */
- rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a);
+ rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a, &pEnd);
if( rc==SQLITE_OK ){
int iCol;
for(iCol=0; iCol<pInfo->nCol; iCol++){
u32 iVal;
sqlite3_int64 nToken;
a += sqlite3Fts3GetVarint(a, &nToken);
+ if( a>pEnd ){
+ rc = SQLITE_CORRUPT_VTAB;
+ break;
+ }
iVal = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc);
pInfo->aMatchinfo[iCol] = iVal;
}
@@ -175360,9 +201923,14 @@ static int fts3MatchinfoValues(
if( rc==SQLITE_OK ){
int iCol;
const char *a = sqlite3_column_blob(pSelectDocsize, 0);
+ const char *pEnd = a + sqlite3_column_bytes(pSelectDocsize, 0);
for(iCol=0; iCol<pInfo->nCol; iCol++){
sqlite3_int64 nToken;
- a += sqlite3Fts3GetVarint(a, &nToken);
+ a += sqlite3Fts3GetVarintBounded(a, pEnd, &nToken);
+ if( a>pEnd ){
+ rc = SQLITE_CORRUPT_VTAB;
+ break;
+ }
pInfo->aMatchinfo[iCol] = (u32)nToken;
}
}
@@ -175379,9 +201947,9 @@ static int fts3MatchinfoValues(
case FTS3_MATCHINFO_LHITS_BM:
case FTS3_MATCHINFO_LHITS: {
- int nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32);
+ size_t nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32);
memset(pInfo->aMatchinfo, 0, nZero);
- fts3ExprLHitGather(pCsr->pExpr, pInfo);
+ rc = fts3ExprLHitGather(pCsr->pExpr, pInfo);
break;
}
@@ -175393,14 +201961,14 @@ static int fts3MatchinfoValues(
if( rc!=SQLITE_OK ) break;
if( bGlobal ){
if( pCsr->pDeferred ){
- rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc, 0);
+ rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc,0,0);
if( rc!=SQLITE_OK ) break;
}
- rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
+ rc = sqlite3Fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
sqlite3Fts3EvalTestDeferred(pCsr, &rc);
if( rc!=SQLITE_OK ) break;
}
- (void)fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo);
+ (void)sqlite3Fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo);
break;
}
}
@@ -175414,7 +201982,7 @@ static int fts3MatchinfoValues(
/*
-** Populate pCsr->aMatchinfo[] with data for the current row. The
+** Populate pCsr->aMatchinfo[] with data for the current row. The
** 'matchinfo' data is an array of 32-bit unsigned integers (C type u32).
*/
static void fts3GetMatchinfo(
@@ -175434,8 +202002,8 @@ static void fts3GetMatchinfo(
sInfo.pCursor = pCsr;
sInfo.nCol = pTab->nColumn;
- /* If there is cached matchinfo() data, but the format string for the
- ** cache does not match the format string for this request, discard
+ /* If there is cached matchinfo() data, but the format string for the
+ ** cache does not match the format string for this request, discard
** the cached data. */
if( pCsr->pMIBuffer && strcmp(pCsr->pMIBuffer->zMatchinfo, zArg) ){
sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
@@ -175443,12 +202011,12 @@ static void fts3GetMatchinfo(
}
/* If Fts3Cursor.pMIBuffer is NULL, then this is the first time the
- ** matchinfo function has been called for this query. In this case
+ ** matchinfo function has been called for this query. In this case
** allocate the array used to accumulate the matchinfo data and
** initialize those elements that are constant for every row.
*/
if( pCsr->pMIBuffer==0 ){
- int nMatchinfo = 0; /* Number of u32 elements in match-info */
+ size_t nMatchinfo = 0; /* Number of u32 elements in match-info */
int i; /* Used to iterate through zArg */
/* Determine the number of phrases in the query */
@@ -175518,7 +202086,7 @@ SQLITE_PRIVATE void sqlite3Fts3Snippet(
/* The returned text includes up to four fragments of text extracted from
** the data in the current row. The first iteration of the for(...) loop
- ** below attempts to locate a single fragment of text nToken tokens in
+ ** below attempts to locate a single fragment of text nToken tokens in
** size that contains at least one instance of all phrases in the query
** expression that appear in the current row. If such a fragment of text
** cannot be found, the second iteration of the loop attempts to locate
@@ -175533,6 +202101,10 @@ SQLITE_PRIVATE void sqlite3Fts3Snippet(
return;
}
+ /* Limit the snippet length to 64 tokens. */
+ if( nToken<-64 ) nToken = -64;
+ if( nToken>+64 ) nToken = +64;
+
for(nSnippet=1; 1; nSnippet++){
int iSnip; /* Loop counter 0..nSnippet-1 */
@@ -175585,7 +202157,7 @@ SQLITE_PRIVATE void sqlite3Fts3Snippet(
assert( nFToken>0 );
for(i=0; i<nSnippet && rc==SQLITE_OK; i++){
- rc = fts3SnippetText(pCsr, &aSnippet[i],
+ rc = fts3SnippetText(pCsr, &aSnippet[i],
i, (i==nSnippet-1), nFToken, zStart, zEnd, zEllipsis, &res
);
}
@@ -175606,8 +202178,8 @@ typedef struct TermOffsetCtx TermOffsetCtx;
struct TermOffset {
char *pList; /* Position-list */
- int iPos; /* Position just read from pList */
- int iOff; /* Offset of this term from read positions */
+ i64 iPos; /* Position just read from pList */
+ i64 iOff; /* Offset of this term from read positions */
};
struct TermOffsetCtx {
@@ -175619,14 +202191,14 @@ struct TermOffsetCtx {
};
/*
-** This function is an fts3ExprIterate() callback used by sqlite3Fts3Offsets().
+** This function is an sqlite3Fts3ExprIterate() callback used by sqlite3Fts3Offsets().
*/
static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
TermOffsetCtx *p = (TermOffsetCtx *)ctx;
int nTerm; /* Number of tokens in phrase */
int iTerm; /* For looping through nTerm phrase terms */
char *pList; /* Pointer to position list for phrase */
- int iPos = 0; /* First position in position-list */
+ i64 iPos = 0; /* First position in position-list */
int rc;
UNUSED_PARAMETER(iPhrase);
@@ -175634,7 +202206,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
nTerm = pExpr->pPhrase->nToken;
if( pList ){
fts3GetDeltaPosition(&pList, &iPos);
- assert( iPos>=0 );
+ assert_fts3_nc( iPos>=0 );
}
for(iTerm=0; iTerm<nTerm; iTerm++){
@@ -175675,7 +202247,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
if( rc!=SQLITE_OK ) goto offsets_out;
/* Allocate the array of TermOffset iterators. */
- sCtx.aTerm = (TermOffset *)sqlite3_malloc(sizeof(TermOffset)*nToken);
+ sCtx.aTerm = (TermOffset *)sqlite3Fts3MallocZero(sizeof(TermOffset)*nToken);
if( 0==sCtx.aTerm ){
rc = SQLITE_NOMEM;
goto offsets_out;
@@ -175683,7 +202255,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
sCtx.iDocid = pCsr->iPrevId;
sCtx.pCsr = pCsr;
- /* Loop through the table columns, appending offset information to
+ /* Loop through the table columns, appending offset information to
** string-buffer res for each column.
*/
for(iCol=0; iCol<pTab->nColumn; iCol++){
@@ -175696,19 +202268,21 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
const char *zDoc;
int nDoc;
- /* Initialize the contents of sCtx.aTerm[] for column iCol. There is
- ** no way that this operation can fail, so the return code from
- ** fts3ExprIterate() can be discarded.
+ /* Initialize the contents of sCtx.aTerm[] for column iCol. This
+ ** operation may fail if the database contains corrupt records.
*/
sCtx.iCol = iCol;
sCtx.iTerm = 0;
- (void)fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx);
+ rc = sqlite3Fts3ExprIterate(
+ pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx
+ );
+ if( rc!=SQLITE_OK ) goto offsets_out;
- /* Retreive the text stored in column iCol. If an SQL NULL is stored
+ /* Retreive the text stored in column iCol. If an SQL NULL is stored
** in column iCol, jump immediately to the next iteration of the loop.
** If an OOM occurs while retrieving the data (this can happen if SQLite
- ** needs to transform the data from utf-16 to utf-8), return SQLITE_NOMEM
- ** to the caller.
+ ** needs to transform the data from utf-16 to utf-8), return SQLITE_NOMEM
+ ** to the caller.
*/
zDoc = (const char *)sqlite3_column_text(pCsr->pStmt, iCol+1);
nDoc = sqlite3_column_bytes(pCsr->pStmt, iCol+1);
@@ -175744,7 +202318,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
/* All offsets for this column have been gathered. */
rc = SQLITE_DONE;
}else{
- assert( iCurrent<=iMinPos );
+ assert_fts3_nc( iCurrent<=iMinPos );
if( 0==(0xFE&*pTerm->pList) ){
pTerm->pList = 0;
}else{
@@ -175755,7 +202329,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
}
if( rc==SQLITE_OK ){
char aBuffer[64];
- sqlite3_snprintf(sizeof(aBuffer), aBuffer,
+ sqlite3_snprintf(sizeof(aBuffer), aBuffer,
"%d %d %d %d ", iCol, pTerm-sCtx.aTerm, iStart, iEnd-iStart
);
rc = fts3StringAppend(&res, aBuffer, -1);
@@ -175900,7 +202474,7 @@ typedef struct unicode_cursor unicode_cursor;
struct unicode_tokenizer {
sqlite3_tokenizer base;
- int bRemoveDiacritic;
+ int eRemoveDiacritic;
int nException;
int *aiException;
};
@@ -175936,7 +202510,7 @@ static int unicodeDestroy(sqlite3_tokenizer *pTokenizer){
**
** For each codepoint in the zIn/nIn string, this function checks if the
** sqlite3FtsUnicodeIsalnum() function already returns the desired result.
-** If so, no action is taken. Otherwise, the codepoint is added to the
+** If so, no action is taken. Otherwise, the codepoint is added to the
** unicode_tokenizer.aiException[] array. For the purposes of tokenization,
** the return value of sqlite3FtsUnicodeIsalnum() is inverted for all
** codepoints in the aiException[] array.
@@ -175962,8 +202536,8 @@ static int unicodeAddExceptions(
while( z<zTerm ){
READ_UTF8(z, zTerm, iCode);
assert( (sqlite3FtsUnicodeIsalnum((int)iCode) & 0xFFFFFFFE)==0 );
- if( sqlite3FtsUnicodeIsalnum((int)iCode)!=bAlnum
- && sqlite3FtsUnicodeIsdiacritic((int)iCode)==0
+ if( sqlite3FtsUnicodeIsalnum((int)iCode)!=bAlnum
+ && sqlite3FtsUnicodeIsdiacritic((int)iCode)==0
){
nEntry++;
}
@@ -175973,14 +202547,14 @@ static int unicodeAddExceptions(
int *aNew; /* New aiException[] array */
int nNew; /* Number of valid entries in array aNew[] */
- aNew = sqlite3_realloc(p->aiException, (p->nException+nEntry)*sizeof(int));
+ aNew = sqlite3_realloc64(p->aiException,(p->nException+nEntry)*sizeof(int));
if( aNew==0 ) return SQLITE_NOMEM;
nNew = p->nException;
z = (const unsigned char *)zIn;
while( z<zTerm ){
READ_UTF8(z, zTerm, iCode);
- if( sqlite3FtsUnicodeIsalnum((int)iCode)!=bAlnum
+ if( sqlite3FtsUnicodeIsalnum((int)iCode)!=bAlnum
&& sqlite3FtsUnicodeIsdiacritic((int)iCode)==0
){
int i, j;
@@ -176045,17 +202619,20 @@ static int unicodeCreate(
pNew = (unicode_tokenizer *) sqlite3_malloc(sizeof(unicode_tokenizer));
if( pNew==NULL ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(unicode_tokenizer));
- pNew->bRemoveDiacritic = 1;
+ pNew->eRemoveDiacritic = 1;
for(i=0; rc==SQLITE_OK && i<nArg; i++){
const char *z = azArg[i];
int n = (int)strlen(z);
if( n==19 && memcmp("remove_diacritics=1", z, 19)==0 ){
- pNew->bRemoveDiacritic = 1;
+ pNew->eRemoveDiacritic = 1;
}
else if( n==19 && memcmp("remove_diacritics=0", z, 19)==0 ){
- pNew->bRemoveDiacritic = 0;
+ pNew->eRemoveDiacritic = 0;
+ }
+ else if( n==19 && memcmp("remove_diacritics=2", z, 19)==0 ){
+ pNew->eRemoveDiacritic = 2;
}
else if( n>=11 && memcmp("tokenchars=", z, 11)==0 ){
rc = unicodeAddExceptions(pNew, 1, &z[11], n-11);
@@ -176080,7 +202657,7 @@ static int unicodeCreate(
/*
** 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
+** used to incrementally tokenize this string is returned in
** *ppCursor.
*/
static int unicodeOpen(
@@ -176100,6 +202677,7 @@ static int unicodeOpen(
pCsr->aInput = (const unsigned char *)aInput;
if( aInput==0 ){
pCsr->nInput = 0;
+ pCsr->aInput = (const unsigned char*)"";
}else if( nInput<0 ){
pCsr->nInput = (int)strlen(aInput);
}else{
@@ -176144,7 +202722,7 @@ static int unicodeNext(
const unsigned char *zTerm = &pCsr->aInput[pCsr->nInput];
/* Scan past any delimiter characters before the start of the next token.
- ** Return SQLITE_DONE early if this takes us all the way to the end of
+ ** Return SQLITE_DONE early if this takes us all the way to the end of
** the input. */
while( z<zTerm ){
READ_UTF8(z, zTerm, iCode);
@@ -176159,7 +202737,7 @@ static int unicodeNext(
/* Grow the output buffer if required. */
if( (zOut-pCsr->zToken)>=(pCsr->nAlloc-4) ){
- char *zNew = sqlite3_realloc(pCsr->zToken, pCsr->nAlloc+64);
+ char *zNew = sqlite3_realloc64(pCsr->zToken, pCsr->nAlloc+64);
if( !zNew ) return SQLITE_NOMEM;
zOut = &zNew[zOut - pCsr->zToken];
pCsr->zToken = zNew;
@@ -176168,7 +202746,7 @@ static int unicodeNext(
/* Write the folded case of the last character read to the output */
zEnd = z;
- iOut = sqlite3FtsUnicodeFold((int)iCode, p->bRemoveDiacritic);
+ iOut = sqlite3FtsUnicodeFold((int)iCode, p->eRemoveDiacritic);
if( iOut ){
WRITE_UTF8(zOut, iOut);
}
@@ -176176,7 +202754,7 @@ static int unicodeNext(
/* If the cursor is not at EOF, read the next character */
if( z>=zTerm ) break;
READ_UTF8(z, zTerm, iCode);
- }while( unicodeIsAlnum(p, (int)iCode)
+ }while( unicodeIsAlnum(p, (int)iCode)
|| sqlite3FtsUnicodeIsdiacritic((int)iCode)
);
@@ -176191,7 +202769,7 @@ static int unicodeNext(
}
/*
-** Set *ppModule to a pointer to the sqlite3_tokenizer_module
+** Set *ppModule to a pointer to the sqlite3_tokenizer_module
** structure for the unicode tokenizer.
*/
SQLITE_PRIVATE void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const **ppModule){
@@ -176213,7 +202791,7 @@ SQLITE_PRIVATE void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const *
/************** End of fts3_unicode.c ****************************************/
/************** Begin file fts3_unicode2.c ***********************************/
/*
-** 2012 May 25
+** 2012-05-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -176246,11 +202824,11 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int c){
** range of unicode codepoints that are not either letters or numbers (i.e.
** codepoints for which this function should return 0).
**
- ** The most significant 22 bits in each 32-bit value contain the first
+ ** The most significant 22 bits in each 32-bit value contain the first
** codepoint in the range. The least significant 10 bits are used to store
- ** the size of the range (always at least 1). In other words, the value
- ** ((C<<22) + N) represents a range of N codepoints starting with codepoint
- ** C. It is not possible to represent a range larger than 1023 codepoints
+ ** the size of the range (always at least 1). In other words, the value
+ ** ((C<<22) + N) represents a range of N codepoints starting with codepoint
+ ** C. It is not possible to represent a range larger than 1023 codepoints
** using this format.
*/
static const unsigned int aEntry[] = {
@@ -176373,32 +202951,48 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int c){
** E"). The resuls of passing a codepoint that corresponds to an
** uppercase letter are undefined.
*/
-static int remove_diacritic(int c){
+static int remove_diacritic(int c, int bComplex){
unsigned short aDia[] = {
- 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
- 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
- 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
- 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336,
- 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928,
- 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234,
- 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504,
- 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529,
- 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
- 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122,
- 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536,
- 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730,
- 62924, 63050, 63082, 63274, 63390,
+ 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
+ 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
+ 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
+ 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336,
+ 3456, 3696, 3712, 3728, 3744, 3766, 3832, 3896,
+ 3912, 3928, 3944, 3968, 4008, 4040, 4056, 4106,
+ 4138, 4170, 4202, 4234, 4266, 4296, 4312, 4344,
+ 4408, 4424, 4442, 4472, 4488, 4504, 6148, 6198,
+ 6264, 6280, 6360, 6429, 6505, 6529, 61448, 61468,
+ 61512, 61534, 61592, 61610, 61642, 61672, 61688, 61704,
+ 61726, 61784, 61800, 61816, 61836, 61880, 61896, 61914,
+ 61948, 61998, 62062, 62122, 62154, 62184, 62200, 62218,
+ 62252, 62302, 62364, 62410, 62442, 62478, 62536, 62554,
+ 62584, 62604, 62640, 62648, 62656, 62664, 62730, 62766,
+ 62830, 62890, 62924, 62974, 63032, 63050, 63082, 63118,
+ 63182, 63242, 63274, 63310, 63368, 63390,
};
- char aChar[] = {
- '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c',
- 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
- 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
- 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
- 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0',
- '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h',
- 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't',
- 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a',
- 'e', 'i', 'o', 'u', 'y',
+#define HIBIT ((unsigned char)0x80)
+ unsigned char aChar[] = {
+ '\0', 'a', 'c', 'e', 'i', 'n',
+ 'o', 'u', 'y', 'y', 'a', 'c',
+ 'd', 'e', 'e', 'g', 'h', 'i',
+ 'j', 'k', 'l', 'n', 'o', 'r',
+ 's', 't', 'u', 'u', 'w', 'y',
+ 'z', 'o', 'u', 'a', 'i', 'o',
+ 'u', 'u'|HIBIT, 'a'|HIBIT, 'g', 'k', 'o',
+ 'o'|HIBIT, 'j', 'g', 'n', 'a'|HIBIT, 'a',
+ 'e', 'i', 'o', 'r', 'u', 's',
+ 't', 'h', 'a', 'e', 'o'|HIBIT, 'o',
+ 'o'|HIBIT, 'y', '\0', '\0', '\0', '\0',
+ '\0', '\0', '\0', '\0', 'a', 'b',
+ 'c'|HIBIT, 'd', 'd', 'e'|HIBIT, 'e', 'e'|HIBIT,
+ 'f', 'g', 'h', 'h', 'i', 'i'|HIBIT,
+ 'k', 'l', 'l'|HIBIT, 'l', 'm', 'n',
+ 'o'|HIBIT, 'p', 'r', 'r'|HIBIT, 'r', 's',
+ 's'|HIBIT, 't', 'u', 'u'|HIBIT, 'v', 'w',
+ 'w', 'x', 'y', 'z', 'h', 't',
+ 'w', 'y', 'a', 'a'|HIBIT, 'a'|HIBIT, 'a'|HIBIT,
+ 'e', 'e'|HIBIT, 'e'|HIBIT, 'i', 'o', 'o'|HIBIT,
+ 'o'|HIBIT, 'o'|HIBIT, 'u', 'u'|HIBIT, 'u'|HIBIT, 'y',
};
unsigned int key = (((unsigned int)c)<<3) | 0x00000007;
@@ -176415,7 +203009,8 @@ static int remove_diacritic(int c){
}
}
assert( key>=aDia[iRes] );
- return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);
+ if( bComplex==0 && (aChar[iRes] & 0x80) ) return c;
+ return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : ((int)aChar[iRes] & 0x7F);
}
@@ -176428,8 +203023,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int c){
unsigned int mask1 = 0x000361F8;
if( c<768 || c>817 ) return 0;
return (c < 768+32) ?
- (mask0 & (1 << (c-768))) :
- (mask1 & (1 << (c-768-32)));
+ (mask0 & ((unsigned int)1 << (c-768))) :
+ (mask1 & ((unsigned int)1 << (c-768-32)));
}
@@ -176442,7 +203037,7 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int c){
** The results are undefined if the value passed to this function
** is less than zero.
*/
-SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){
+SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){
/* Each entry in the following array defines a rule for folding a range
** of codepoints to lower case. The rule applies to a range of nRange
** codepoints starting at codepoint iCode.
@@ -176519,19 +203114,19 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){
{42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1},
{42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1},
{42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1},
- {65313, 14, 26},
+ {65313, 14, 26},
};
static const unsigned short aiOff[] = {
- 1, 2, 8, 15, 16, 26, 28, 32,
- 37, 38, 40, 48, 63, 64, 69, 71,
- 79, 80, 116, 202, 203, 205, 206, 207,
- 209, 210, 211, 213, 214, 217, 218, 219,
- 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721,
- 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
- 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
- 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
- 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
- 65514, 65521, 65527, 65528, 65529,
+ 1, 2, 8, 15, 16, 26, 28, 32,
+ 37, 38, 40, 48, 63, 64, 69, 71,
+ 79, 80, 116, 202, 203, 205, 206, 207,
+ 209, 210, 211, 213, 214, 217, 218, 219,
+ 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721,
+ 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
+ 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
+ 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
+ 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
+ 65514, 65521, 65527, 65528, 65529,
};
int ret = c;
@@ -176565,9 +203160,11 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){
assert( ret>0 );
}
- if( bRemoveDiacritic ) ret = remove_diacritic(ret);
+ if( eRemoveDiacritic ){
+ ret = remove_diacritic(ret, eRemoveDiacritic==2);
+ }
}
-
+
else if( c>=66560 && c<66600 ){
ret = c + 40;
}
@@ -176578,7 +203175,7 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){
#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */
/************** End of fts3_unicode2.c ***************************************/
-/************** Begin file json1.c *******************************************/
+/************** Begin file json.c ********************************************/
/*
** 2015-08-12
**
@@ -176591,98 +203188,242 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){
**
******************************************************************************
**
-** This SQLite extension implements JSON functions. The interface is
-** modeled after MySQL JSON functions:
-**
-** https://dev.mysql.com/doc/refman/5.7/en/json.html
-**
-** For the time being, all JSON is stored as pure text. (We might add
-** a JSONB type in the future which stores a binary encoding of JSON in
-** a BLOB, but there is no support for JSONB in the current implementation.
-** This implementation parses JSON text at 250 MB/s, so it is hard to see
-** how JSONB might improve on that.)
-*/
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1)
-#if !defined(SQLITEINT_H)
-/* #include "sqlite3ext.h" */
-#endif
-SQLITE_EXTENSION_INIT1
-/* #include <assert.h> */
-/* #include <string.h> */
-/* #include <stdlib.h> */
-/* #include <stdarg.h> */
-
-/* Mark a function parameter as unused, to suppress nuisance compiler
-** warnings. */
-#ifndef UNUSED_PARAM
-# define UNUSED_PARAM(X) (void)(X)
-#endif
-
-#ifndef LARGEST_INT64
-# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
-# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
-#endif
+** SQLite JSON functions.
+**
+** This file began as an extension in ext/misc/json1.c in 2015. That
+** extension proved so useful that it has now been moved into the core.
+**
+** The original design stored all JSON as pure text, canonical RFC-8259.
+** Support for JSON-5 extensions was added with version 3.42.0 (2023-05-16).
+** All generated JSON text still conforms strictly to RFC-8259, but text
+** with JSON-5 extensions is accepted as input.
+**
+** Beginning with version 3.45.0 (circa 2024-01-01), these routines also
+** accept BLOB values that have JSON encoded using a binary representation
+** called "JSONB". The name JSONB comes from PostgreSQL, however the on-disk
+** format SQLite JSONB is completely different and incompatible with
+** PostgreSQL JSONB.
+**
+** Decoding and interpreting JSONB is still O(N) where N is the size of
+** the input, the same as text JSON. However, the constant of proportionality
+** for JSONB is much smaller due to faster parsing. The size of each
+** element in JSONB is encoded in its header, so there is no need to search
+** for delimiters using persnickety syntax rules. JSONB seems to be about
+** 3x faster than text JSON as a result. JSONB is also tends to be slightly
+** smaller than text JSON, by 5% or 10%, but there are corner cases where
+** JSONB can be slightly larger. So you are not far mistaken to say that
+** a JSONB blob is the same size as the equivalent RFC-8259 text.
+**
+**
+** THE JSONB ENCODING:
+**
+** Every JSON element is encoded in JSONB as a header and a payload.
+** The header is between 1 and 9 bytes in size. The payload is zero
+** or more bytes.
+**
+** The lower 4 bits of the first byte of the header determines the
+** element type:
+**
+** 0: NULL
+** 1: TRUE
+** 2: FALSE
+** 3: INT -- RFC-8259 integer literal
+** 4: INT5 -- JSON5 integer literal
+** 5: FLOAT -- RFC-8259 floating point literal
+** 6: FLOAT5 -- JSON5 floating point literal
+** 7: TEXT -- Text literal acceptable to both SQL and JSON
+** 8: TEXTJ -- Text containing RFC-8259 escapes
+** 9: TEXT5 -- Text containing JSON5 and/or RFC-8259 escapes
+** 10: TEXTRAW -- Text containing unescaped syntax characters
+** 11: ARRAY
+** 12: OBJECT
+**
+** The other three possible values (13-15) are reserved for future
+** enhancements.
+**
+** The upper 4 bits of the first byte determine the size of the header
+** and sometimes also the size of the payload. If X is the first byte
+** of the element and if X>>4 is between 0 and 11, then the payload
+** will be that many bytes in size and the header is exactly one byte
+** in size. Other four values for X>>4 (12-15) indicate that the header
+** is more than one byte in size and that the payload size is determined
+** by the remainder of the header, interpreted as a unsigned big-endian
+** integer.
+**
+** Value of X>>4 Size integer Total header size
+** ------------- -------------------- -----------------
+** 12 1 byte (0-255) 2
+** 13 2 byte (0-65535) 3
+** 14 4 byte (0-4294967295) 5
+** 15 8 byte (0-1.8e19) 9
+**
+** The payload size need not be expressed in its minimal form. For example,
+** if the payload size is 10, the size can be expressed in any of 5 different
+** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by on 0x0a byte,
+** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by
+** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and
+** a single byte of 0x0a. The shorter forms are preferred, of course, but
+** sometimes when generating JSONB, the payload size is not known in advance
+** and it is convenient to reserve sufficient header space to cover the
+** largest possible payload size and then come back later and patch up
+** the size when it becomes known, resulting in a non-minimal encoding.
+**
+** The value (X>>4)==15 is not actually used in the current implementation
+** (as SQLite is currently unable handle BLOBs larger than about 2GB)
+** but is included in the design to allow for future enhancements.
+**
+** The payload follows the header. NULL, TRUE, and FALSE have no payload and
+** their payload size must always be zero. The payload for INT, INT5,
+** FLOAT, FLOAT5, TEXT, TEXTJ, TEXT5, and TEXTROW is text. Note that the
+** "..." or '...' delimiters are omitted from the various text encodings.
+** The payload for ARRAY and OBJECT is a list of additional elements that
+** are the content for the array or object. The payload for an OBJECT
+** must be an even number of elements. The first element of each pair is
+** the label and must be of type TEXT, TEXTJ, TEXT5, or TEXTRAW.
+**
+** A valid JSONB blob consists of a single element, as described above.
+** Usually this will be an ARRAY or OBJECT element which has many more
+** elements as its content. But the overall blob is just a single element.
+**
+** Input validation for JSONB blobs simply checks that the element type
+** code is between 0 and 12 and that the total size of the element
+** (header plus payload) is the same as the size of the BLOB. If those
+** checks are true, the BLOB is assumed to be JSONB and processing continues.
+** Errors are only raised if some other miscoding is discovered during
+** processing.
+**
+** Additional information can be found in the doc/jsonb.md file of the
+** canonical SQLite source tree.
+*/
+#ifndef SQLITE_OMIT_JSON
+/* #include "sqliteInt.h" */
-/*
-** Versions of isspace(), isalnum() and isdigit() to which it is safe
-** to pass signed char values.
-*/
-#ifdef sqlite3Isdigit
- /* Use the SQLite core versions if this routine is part of the
- ** SQLite amalgamation */
-# define safe_isdigit(x) sqlite3Isdigit(x)
-# define safe_isalnum(x) sqlite3Isalnum(x)
-# define safe_isxdigit(x) sqlite3Isxdigit(x)
-#else
- /* Use the standard library for separate compilation */
-#include <ctype.h> /* amalgamator: keep */
-# define safe_isdigit(x) isdigit((unsigned char)(x))
-# define safe_isalnum(x) isalnum((unsigned char)(x))
-# define safe_isxdigit(x) isxdigit((unsigned char)(x))
-#endif
+/* JSONB element types
+*/
+#define JSONB_NULL 0 /* "null" */
+#define JSONB_TRUE 1 /* "true" */
+#define JSONB_FALSE 2 /* "false" */
+#define JSONB_INT 3 /* integer acceptable to JSON and SQL */
+#define JSONB_INT5 4 /* integer in 0x000 notation */
+#define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */
+#define JSONB_FLOAT5 6 /* float with JSON5 extensions */
+#define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */
+#define JSONB_TEXTJ 8 /* Text with JSON escapes */
+#define JSONB_TEXT5 9 /* Text with JSON-5 escape */
+#define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */
+#define JSONB_ARRAY 11 /* An array */
+#define JSONB_OBJECT 12 /* An object */
+
+/* Human-readable names for the JSONB values. The index for each
+** string must correspond to the JSONB_* integer above.
+*/
+static const char * const jsonbType[] = {
+ "null", "true", "false", "integer", "integer",
+ "real", "real", "text", "text", "text",
+ "text", "array", "object", "", "", "", ""
+};
/*
** Growing our own isspace() routine this way is twice as fast as
** the library isspace() function, resulting in a 7% overall performance
-** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
+** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
*/
static const char jsonIsSpace[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x])
+
+/*
+** The set of all space characters recognized by jsonIsspace().
+** Useful as the second argument to strspn().
+*/
+static const char jsonSpaces[] = "\011\012\015\040";
+
+/*
+** Characters that are special to JSON. Control characters,
+** '"' and '\\' and '\''. Actually, '\'' is not special to
+** canonical JSON, but it is special in JSON-5, so we include
+** it in the set of special characters.
+*/
+static const char jsonIsOk[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
-#define safe_isspace(x) (jsonIsSpace[(unsigned char)x])
-
-#ifndef SQLITE_AMALGAMATION
- /* Unsigned integer types. These are already defined in the sqliteInt.h,
- ** but the definitions need to be repeated for separate compilation. */
- typedef sqlite3_uint64 u64;
- typedef unsigned int u32;
- typedef unsigned short int u16;
- typedef unsigned char u8;
-#endif
/* Objects */
+typedef struct JsonCache JsonCache;
typedef struct JsonString JsonString;
-typedef struct JsonNode JsonNode;
typedef struct JsonParse JsonParse;
+/*
+** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
+*/
+#define JSON_CACHE_ID (-429938) /* Cache entry */
+#define JSON_CACHE_SIZE 4 /* Max number of cache entries */
+
+/*
+** jsonUnescapeOneChar() returns this invalid code point if it encounters
+** a syntax error.
+*/
+#define JSON_INVALID_CHAR 0x99999
+
+/* A cache mapping JSON text into JSONB blobs.
+**
+** Each cache entry is a JsonParse object with the following restrictions:
+**
+** * The bReadOnly flag must be set
+**
+** * The aBlob[] array must be owned by the JsonParse object. In other
+** words, nBlobAlloc must be non-zero.
+**
+** * eEdit and delta must be zero.
+**
+** * zJson must be an RCStr. In other words bJsonIsRCStr must be true.
+*/
+struct JsonCache {
+ sqlite3 *db; /* Database connection */
+ int nUsed; /* Number of active entries in the cache */
+ JsonParse *a[JSON_CACHE_SIZE]; /* One line for each cache entry */
+};
+
/* An instance of this object represents a JSON string
** under construction. Really, this is a generic string accumulator
** that can be and is used to create strings other than JSON.
+**
+** If the generated string is longer than will fit into the zSpace[] buffer,
+** then it will be an RCStr string. This aids with caching of large
+** JSON strings.
*/
struct JsonString {
sqlite3_context *pCtx; /* Function context - put error messages here */
@@ -176690,88 +203431,227 @@ struct JsonString {
u64 nAlloc; /* Bytes of storage available in zBuf[] */
u64 nUsed; /* Bytes of zBuf[] currently used */
u8 bStatic; /* True if zBuf is static space */
- u8 bErr; /* True if an error has been encountered */
+ u8 eErr; /* True if an error has been encountered */
char zSpace[100]; /* Initial static space */
};
-/* JSON type values
-*/
-#define JSON_NULL 0
-#define JSON_TRUE 1
-#define JSON_FALSE 2
-#define JSON_INT 3
-#define JSON_REAL 4
-#define JSON_STRING 5
-#define JSON_ARRAY 6
-#define JSON_OBJECT 7
+/* Allowed values for JsonString.eErr */
+#define JSTRING_OOM 0x01 /* Out of memory */
+#define JSTRING_MALFORMED 0x02 /* Malformed JSONB */
+#define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */
-/* The "subtype" set for JSON values */
+/* The "subtype" set for text JSON values passed through using
+** sqlite3_result_subtype() and sqlite3_value_subtype().
+*/
#define JSON_SUBTYPE 74 /* Ascii for "J" */
/*
-** Names of the various JSON types:
-*/
-static const char * const jsonType[] = {
- "null", "true", "false", "integer", "real", "text", "array", "object"
-};
-
-/* Bit values for the JsonNode.jnFlag field
+** Bit values for the flags passed into various SQL function implementations
+** via the sqlite3_user_data() value.
*/
-#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */
-#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */
-#define JNODE_REMOVE 0x04 /* Do not output */
-#define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */
-#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */
-#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */
-#define JNODE_LABEL 0x40 /* Is a label of an object */
+#define JSON_JSON 0x01 /* Result is always JSON */
+#define JSON_SQL 0x02 /* Result is always SQL */
+#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */
+#define JSON_ISSET 0x04 /* json_set(), not json_insert() */
+#define JSON_BLOB 0x08 /* Use the BLOB output format */
-/* A single node of parsed JSON
-*/
-struct JsonNode {
- u8 eType; /* One of the JSON_ type values */
- u8 jnFlags; /* JNODE flags */
- u32 n; /* Bytes of content, or number of sub-nodes */
- union {
- const char *zJContent; /* Content for INT, REAL, and STRING */
- u32 iAppend; /* More terms for ARRAY and OBJECT */
- u32 iKey; /* Key for ARRAY objects in json_tree() */
- u32 iReplace; /* Replacement content for JNODE_REPLACE */
- JsonNode *pPatch; /* Node chain of patch for JNODE_PATCH */
- } u;
-};
-
-/* A completely parsed JSON string
+/* A parsed JSON value. Lifecycle:
+**
+** 1. JSON comes in and is parsed into a JSONB value in aBlob. The
+** original text is stored in zJson. This step is skipped if the
+** input is JSONB instead of text JSON.
+**
+** 2. The aBlob[] array is searched using the JSON path notation, if needed.
+**
+** 3. Zero or more changes are made to aBlob[] (via json_remove() or
+** json_replace() or json_patch() or similar).
+**
+** 4. New JSON text is generated from the aBlob[] for output. This step
+** is skipped if the function is one of the jsonb_* functions that
+** returns JSONB instead of text JSON.
*/
struct JsonParse {
- u32 nNode; /* Number of slots of aNode[] used */
- u32 nAlloc; /* Number of slots of aNode[] allocated */
- JsonNode *aNode; /* Array of nodes containing the parse */
- const char *zJson; /* Original JSON string */
- u32 *aUp; /* Index of parent of each node */
- u8 oom; /* Set to true if out of memory */
- u8 nErr; /* Number of errors seen */
- u16 iDepth; /* Nesting depth */
+ u8 *aBlob; /* JSONB representation of JSON value */
+ u32 nBlob; /* Bytes of aBlob[] actually used */
+ u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */
+ char *zJson; /* Json text used for parsing */
+ sqlite3 *db; /* The database connection to which this object belongs */
int nJson; /* Length of the zJson string in bytes */
- u32 iHold; /* Replace cache line with the lowest iHold value */
+ u32 nJPRef; /* Number of references to this object */
+ u32 iErr; /* Error location in zJson[] */
+ u16 iDepth; /* Nesting depth */
+ u8 nErr; /* Number of errors seen */
+ u8 oom; /* Set to true if out of memory */
+ u8 bJsonIsRCStr; /* True if zJson is an RCStr */
+ u8 hasNonstd; /* True if input uses non-standard features like JSON5 */
+ u8 bReadOnly; /* Do not modify. */
+ /* Search and edit information. See jsonLookupStep() */
+ u8 eEdit; /* Edit operation to apply */
+ int delta; /* Size change due to the edit */
+ u32 nIns; /* Number of bytes to insert */
+ u32 iLabel; /* Location of label if search landed on an object value */
+ u8 *aIns; /* Content to be inserted */
};
+/* Allowed values for JsonParse.eEdit */
+#define JEDIT_DEL 1 /* Delete if exists */
+#define JEDIT_REPL 2 /* Overwrite if exists */
+#define JEDIT_INS 3 /* Insert if not exists */
+#define JEDIT_SET 4 /* Insert or overwrite */
+
/*
** Maximum nesting depth of JSON for this implementation.
**
** This limit is needed to avoid a stack overflow in the recursive
-** descent parser. A depth of 2000 is far deeper than any sane JSON
-** should go.
+** descent parser. A depth of 1000 is far deeper than any sane JSON
+** should go. Historical note: This limit was 2000 prior to version 3.42.0
+*/
+#ifndef SQLITE_JSON_MAX_DEPTH
+# define JSON_MAX_DEPTH 1000
+#else
+# define JSON_MAX_DEPTH SQLITE_JSON_MAX_DEPTH
+#endif
+
+/*
+** Allowed values for the flgs argument to jsonParseFuncArg();
+*/
+#define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */
+#define JSON_KEEPERROR 0x02 /* Return non-NULL even if there is an error */
+
+/**************************************************************************
+** Forward references
+**************************************************************************/
+static void jsonReturnStringAsBlob(JsonString*);
+static int jsonFuncArgMightBeBinary(sqlite3_value *pJson);
+static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
+static void jsonReturnParse(sqlite3_context*,JsonParse*);
+static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
+static void jsonParseFree(JsonParse*);
+static u32 jsonbPayloadSize(const JsonParse*, u32, u32*);
+static u32 jsonUnescapeOneChar(const char*, u32, u32*);
+
+/**************************************************************************
+** Utility routines for dealing with JsonCache objects
+**************************************************************************/
+
+/*
+** Free a JsonCache object.
*/
-#define JSON_MAX_DEPTH 2000
+static void jsonCacheDelete(JsonCache *p){
+ int i;
+ for(i=0; i<p->nUsed; i++){
+ jsonParseFree(p->a[i]);
+ }
+ sqlite3DbFree(p->db, p);
+}
+static void jsonCacheDeleteGeneric(void *p){
+ jsonCacheDelete((JsonCache*)p);
+}
+
+/*
+** Insert a new entry into the cache. If the cache is full, expel
+** the least recently used entry. Return SQLITE_OK on success or a
+** result code otherwise.
+**
+** Cache entries are stored in age order, oldest first.
+*/
+static int jsonCacheInsert(
+ sqlite3_context *ctx, /* The SQL statement context holding the cache */
+ JsonParse *pParse /* The parse object to be added to the cache */
+){
+ JsonCache *p;
+
+ assert( pParse->zJson!=0 );
+ assert( pParse->bJsonIsRCStr );
+ assert( pParse->delta==0 );
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ){
+ sqlite3 *db = sqlite3_context_db_handle(ctx);
+ p = sqlite3DbMallocZero(db, sizeof(*p));
+ if( p==0 ) return SQLITE_NOMEM;
+ p->db = db;
+ sqlite3_set_auxdata(ctx, JSON_CACHE_ID, p, jsonCacheDeleteGeneric);
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ) return SQLITE_NOMEM;
+ }
+ if( p->nUsed >= JSON_CACHE_SIZE ){
+ jsonParseFree(p->a[0]);
+ memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0]));
+ p->nUsed = JSON_CACHE_SIZE-1;
+ }
+ assert( pParse->nBlobAlloc>0 );
+ pParse->eEdit = 0;
+ pParse->nJPRef++;
+ pParse->bReadOnly = 1;
+ p->a[p->nUsed] = pParse;
+ p->nUsed++;
+ return SQLITE_OK;
+}
+
+/*
+** Search for a cached translation the json text supplied by pArg. Return
+** the JsonParse object if found. Return NULL if not found.
+**
+** When a match if found, the matching entry is moved to become the
+** most-recently used entry if it isn't so already.
+**
+** The JsonParse object returned still belongs to the Cache and might
+** be deleted at any moment. If the caller whants the JsonParse to
+** linger, it needs to increment the nPJRef reference counter.
+*/
+static JsonParse *jsonCacheSearch(
+ sqlite3_context *ctx, /* The SQL statement context holding the cache */
+ sqlite3_value *pArg /* Function argument containing SQL text */
+){
+ JsonCache *p;
+ int i;
+ const char *zJson;
+ int nJson;
+
+ if( sqlite3_value_type(pArg)!=SQLITE_TEXT ){
+ return 0;
+ }
+ zJson = (const char*)sqlite3_value_text(pArg);
+ if( zJson==0 ) return 0;
+ nJson = sqlite3_value_bytes(pArg);
+
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ){
+ return 0;
+ }
+ for(i=0; i<p->nUsed; i++){
+ if( p->a[i]->zJson==zJson ) break;
+ }
+ if( i>=p->nUsed ){
+ for(i=0; i<p->nUsed; i++){
+ if( p->a[i]->nJson!=nJson ) continue;
+ if( memcmp(p->a[i]->zJson, zJson, nJson)==0 ) break;
+ }
+ }
+ if( i<p->nUsed ){
+ if( i<p->nUsed-1 ){
+ /* Make the matching entry the most recently used entry */
+ JsonParse *tmp = p->a[i];
+ memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp));
+ p->a[p->nUsed-1] = tmp;
+ i = p->nUsed - 1;
+ }
+ assert( p->a[i]->delta==0 );
+ return p->a[i];
+ }else{
+ return 0;
+ }
+}
/**************************************************************************
** Utility routines for dealing with JsonString objects
**************************************************************************/
-/* Set the JsonString object to an empty string
+/* Turn uninitialized bulk memory into a valid JsonString object
+** holding a zero-length string.
*/
-static void jsonZero(JsonString *p){
+static void jsonStringZero(JsonString *p){
p->zBuf = p->zSpace;
p->nAlloc = sizeof(p->zSpace);
p->nUsed = 0;
@@ -176780,53 +203660,51 @@ static void jsonZero(JsonString *p){
/* Initialize the JsonString object
*/
-static void jsonInit(JsonString *p, sqlite3_context *pCtx){
+static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){
p->pCtx = pCtx;
- p->bErr = 0;
- jsonZero(p);
+ p->eErr = 0;
+ jsonStringZero(p);
}
-
/* Free all allocated memory and reset the JsonString object back to its
** initial state.
*/
-static void jsonReset(JsonString *p){
- if( !p->bStatic ) sqlite3_free(p->zBuf);
- jsonZero(p);
+static void jsonStringReset(JsonString *p){
+ if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf);
+ jsonStringZero(p);
}
-
-/* Report an out-of-memory (OOM) condition
+/* Report an out-of-memory (OOM) condition
*/
-static void jsonOom(JsonString *p){
- p->bErr = 1;
- sqlite3_result_error_nomem(p->pCtx);
- jsonReset(p);
+static void jsonStringOom(JsonString *p){
+ p->eErr |= JSTRING_OOM;
+ if( p->pCtx ) sqlite3_result_error_nomem(p->pCtx);
+ jsonStringReset(p);
}
/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
** Return zero on success. Return non-zero on an OOM error
*/
-static int jsonGrow(JsonString *p, u32 N){
+static int jsonStringGrow(JsonString *p, u32 N){
u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
char *zNew;
if( p->bStatic ){
- if( p->bErr ) return 1;
- zNew = sqlite3_malloc64(nTotal);
+ if( p->eErr ) return 1;
+ zNew = sqlite3RCStrNew(nTotal);
if( zNew==0 ){
- jsonOom(p);
+ jsonStringOom(p);
return SQLITE_NOMEM;
}
memcpy(zNew, p->zBuf, (size_t)p->nUsed);
p->zBuf = zNew;
p->bStatic = 0;
}else{
- zNew = sqlite3_realloc64(p->zBuf, nTotal);
- if( zNew==0 ){
- jsonOom(p);
+ p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal);
+ if( p->zBuf==0 ){
+ p->eErr |= JSTRING_OOM;
+ jsonStringZero(p);
return SQLITE_NOMEM;
}
- p->zBuf = zNew;
}
p->nAlloc = nTotal;
return SQLITE_OK;
@@ -176834,17 +203712,41 @@ static int jsonGrow(JsonString *p, u32 N){
/* Append N bytes from zIn onto the end of the JsonString string.
*/
-static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
- if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return;
+static SQLITE_NOINLINE void jsonStringExpandAndAppend(
+ JsonString *p,
+ const char *zIn,
+ u32 N
+){
+ assert( N>0 );
+ if( jsonStringGrow(p,N) ) return;
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
}
+static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
+ if( N==0 ) return;
+ if( N+p->nUsed >= p->nAlloc ){
+ jsonStringExpandAndAppend(p,zIn,N);
+ }else{
+ memcpy(p->zBuf+p->nUsed, zIn, N);
+ p->nUsed += N;
+ }
+}
+static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){
+ assert( N>0 );
+ if( N+p->nUsed >= p->nAlloc ){
+ jsonStringExpandAndAppend(p,zIn,N);
+ }else{
+ memcpy(p->zBuf+p->nUsed, zIn, N);
+ p->nUsed += N;
+ }
+}
+
/* Append formatted text (not to exceed N bytes) to the JsonString.
*/
static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
va_list ap;
- if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return;
+ if( (p->nUsed + N >= p->nAlloc) && jsonStringGrow(p, N) ) return;
va_start(ap, zFormat);
sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
va_end(ap);
@@ -176853,10 +203755,38 @@ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
/* Append a single character
*/
-static void jsonAppendChar(JsonString *p, char c){
- if( p->nUsed>=p->nAlloc && jsonGrow(p,1)!=0 ) return;
+static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){
+ if( jsonStringGrow(p,1) ) return;
p->zBuf[p->nUsed++] = c;
}
+static void jsonAppendChar(JsonString *p, char c){
+ if( p->nUsed>=p->nAlloc ){
+ jsonAppendCharExpand(p,c);
+ }else{
+ p->zBuf[p->nUsed++] = c;
+ }
+}
+
+/* Remove a single character from the end of the string
+*/
+static void jsonStringTrimOneChar(JsonString *p){
+ if( p->eErr==0 ){
+ assert( p->nUsed>0 );
+ p->nUsed--;
+ }
+}
+
+
+/* Make sure there is a zero terminator on p->zBuf[]
+**
+** Return true on success. Return false if an OOM prevents this
+** from happening.
+*/
+static int jsonStringTerminate(JsonString *p){
+ jsonAppendChar(p, 0);
+ jsonStringTrimOneChar(p);
+ return p->eErr==0;
+}
/* Append a comma separator to the output buffer, if the previous
** character is not '[' or '{'.
@@ -176865,25 +203795,76 @@ static void jsonAppendSeparator(JsonString *p){
char c;
if( p->nUsed==0 ) return;
c = p->zBuf[p->nUsed-1];
- if( c!='[' && c!='{' ) jsonAppendChar(p, ',');
+ if( c=='[' || c=='{' ) return;
+ jsonAppendChar(p, ',');
}
/* Append the N-byte string in zIn to the end of the JsonString string
-** under construction. Enclose the string in "..." and escape
-** any double-quotes or backslash characters contained within the
+** under construction. Enclose the string in double-quotes ("...") and
+** escape any double-quotes or backslash characters contained within the
** string.
+**
+** This routine is a high-runner. There is a measurable performance
+** increase associated with unwinding the jsonIsOk[] loop.
*/
static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
- u32 i;
- if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return;
+ u32 k;
+ u8 c;
+ const u8 *z = (const u8*)zIn;
+ if( z==0 ) return;
+ if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return;
p->zBuf[p->nUsed++] = '"';
- for(i=0; i<N; i++){
- unsigned char c = ((unsigned const char*)zIn)[i];
+ while( 1 /*exit-by-break*/ ){
+ k = 0;
+ /* The following while() is the 4-way unwound equivalent of
+ **
+ ** while( k<N && jsonIsOk[z[k]] ){ k++; }
+ */
+ while( 1 /* Exit by break */ ){
+ if( k+3>=N ){
+ while( k<N && jsonIsOk[z[k]] ){ k++; }
+ break;
+ }
+ if( !jsonIsOk[z[k]] ){
+ break;
+ }
+ if( !jsonIsOk[z[k+1]] ){
+ k += 1;
+ break;
+ }
+ if( !jsonIsOk[z[k+2]] ){
+ k += 2;
+ break;
+ }
+ if( !jsonIsOk[z[k+3]] ){
+ k += 3;
+ break;
+ }else{
+ k += 4;
+ }
+ }
+ if( k>=N ){
+ if( k>0 ){
+ memcpy(&p->zBuf[p->nUsed], z, k);
+ p->nUsed += k;
+ }
+ break;
+ }
+ if( k>0 ){
+ memcpy(&p->zBuf[p->nUsed], z, k);
+ p->nUsed += k;
+ z += k;
+ N -= k;
+ }
+ c = z[0];
if( c=='"' || c=='\\' ){
json_simple_escape:
- if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
+ if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return;
p->zBuf[p->nUsed++] = '\\';
- }else if( c<=0x1f ){
+ p->zBuf[p->nUsed++] = c;
+ }else if( c=='\'' ){
+ p->zBuf[p->nUsed++] = c;
+ }else{
static const char aSpecial[] = {
0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
@@ -176894,39 +203875,44 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
assert( aSpecial['\n']=='n' );
assert( aSpecial['\r']=='r' );
assert( aSpecial['\t']=='t' );
+ assert( c>=0 && c<sizeof(aSpecial) );
if( aSpecial[c] ){
c = aSpecial[c];
goto json_simple_escape;
}
- if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return;
+ if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return;
p->zBuf[p->nUsed++] = '\\';
p->zBuf[p->nUsed++] = 'u';
p->zBuf[p->nUsed++] = '0';
p->zBuf[p->nUsed++] = '0';
- p->zBuf[p->nUsed++] = '0' + (c>>4);
- c = "0123456789abcdef"[c&0xf];
+ p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4];
+ p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf];
}
- p->zBuf[p->nUsed++] = c;
+ z++;
+ N--;
}
p->zBuf[p->nUsed++] = '"';
assert( p->nUsed<p->nAlloc );
}
/*
-** Append a function parameter value to the JSON string under
-** construction.
+** Append an sqlite3_value (such as a function parameter) to the JSON
+** string under construction in p.
*/
-static void jsonAppendValue(
+static void jsonAppendSqlValue(
JsonString *p, /* Append to this JSON string */
sqlite3_value *pValue /* Value to append */
){
switch( sqlite3_value_type(pValue) ){
case SQLITE_NULL: {
- jsonAppendRaw(p, "null", 4);
+ jsonAppendRawNZ(p, "null", 4);
break;
}
- case SQLITE_INTEGER:
case SQLITE_FLOAT: {
+ jsonPrintf(100, p, "%!0.15g", sqlite3_value_double(pValue));
+ break;
+ }
+ case SQLITE_INTEGER: {
const char *z = (const char*)sqlite3_value_text(pValue);
u32 n = (u32)sqlite3_value_bytes(pValue);
jsonAppendRaw(p, z, n);
@@ -176943,561 +203929,1236 @@ static void jsonAppendValue(
break;
}
default: {
- if( p->bErr==0 ){
+ if( jsonFuncArgMightBeBinary(pValue) ){
+ JsonParse px;
+ memset(&px, 0, sizeof(px));
+ px.aBlob = (u8*)sqlite3_value_blob(pValue);
+ px.nBlob = sqlite3_value_bytes(pValue);
+ jsonTranslateBlobToText(&px, 0, p);
+ }else if( p->eErr==0 ){
sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
- p->bErr = 2;
- jsonReset(p);
+ p->eErr = JSTRING_ERR;
+ jsonStringReset(p);
}
break;
}
}
}
-
-/* Make the JSON in p the result of the SQL function.
+/* Make the text in p (which is probably a generated JSON text string)
+** the result of the SQL function.
+**
+** The JsonString is reset.
+**
+** If pParse and ctx are both non-NULL, then the SQL string in p is
+** loaded into the zJson field of the pParse object as a RCStr and the
+** pParse is added to the cache.
*/
-static void jsonResult(JsonString *p){
- if( p->bErr==0 ){
- sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
- p->bStatic ? SQLITE_TRANSIENT : sqlite3_free,
- SQLITE_UTF8);
- jsonZero(p);
+static void jsonReturnString(
+ JsonString *p, /* String to return */
+ JsonParse *pParse, /* JSONB source or NULL */
+ sqlite3_context *ctx /* Where to cache */
+){
+ assert( (pParse!=0)==(ctx!=0) );
+ assert( ctx==0 || ctx==p->pCtx );
+ if( p->eErr==0 ){
+ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx));
+ if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(p);
+ }else if( p->bStatic ){
+ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
+ SQLITE_TRANSIENT, SQLITE_UTF8);
+ }else if( jsonStringTerminate(p) ){
+ if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){
+ int rc;
+ pParse->zJson = sqlite3RCStrRef(p->zBuf);
+ pParse->nJson = p->nUsed;
+ pParse->bJsonIsRCStr = 1;
+ rc = jsonCacheInsert(ctx, pParse);
+ if( rc==SQLITE_NOMEM ){
+ sqlite3_result_error_nomem(ctx);
+ jsonStringReset(p);
+ return;
+ }
+ }
+ sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed,
+ sqlite3RCStrUnref,
+ SQLITE_UTF8);
+ }else{
+ sqlite3_result_error_nomem(p->pCtx);
+ }
+ }else if( p->eErr & JSTRING_OOM ){
+ sqlite3_result_error_nomem(p->pCtx);
+ }else if( p->eErr & JSTRING_MALFORMED ){
+ sqlite3_result_error(p->pCtx, "malformed JSON", -1);
}
- assert( p->bStatic );
+ jsonStringReset(p);
}
/**************************************************************************
-** Utility routines for dealing with JsonNode and JsonParse objects
+** Utility routines for dealing with JsonParse objects
**************************************************************************/
/*
-** Return the number of consecutive JsonNode slots need to represent
-** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and
-** OBJECT types, the number might be larger.
-**
-** Appended elements are not counted. The value returned is the number
-** by which the JsonNode counter should increment in order to go to the
-** next peer value.
-*/
-static u32 jsonNodeSize(JsonNode *pNode){
- return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1;
-}
-
-/*
** Reclaim all memory allocated by a JsonParse object. But do not
** delete the JsonParse object itself.
*/
static void jsonParseReset(JsonParse *pParse){
- sqlite3_free(pParse->aNode);
- pParse->aNode = 0;
- pParse->nNode = 0;
- pParse->nAlloc = 0;
- sqlite3_free(pParse->aUp);
- pParse->aUp = 0;
+ assert( pParse->nJPRef<=1 );
+ if( pParse->bJsonIsRCStr ){
+ sqlite3RCStrUnref(pParse->zJson);
+ pParse->zJson = 0;
+ pParse->nJson = 0;
+ pParse->bJsonIsRCStr = 0;
+ }
+ if( pParse->nBlobAlloc ){
+ sqlite3DbFree(pParse->db, pParse->aBlob);
+ pParse->aBlob = 0;
+ pParse->nBlob = 0;
+ pParse->nBlobAlloc = 0;
+ }
}
/*
-** Free a JsonParse object that was obtained from sqlite3_malloc().
+** Decrement the reference count on the JsonParse object. When the
+** count reaches zero, free the object.
*/
static void jsonParseFree(JsonParse *pParse){
- jsonParseReset(pParse);
- sqlite3_free(pParse);
+ if( pParse ){
+ if( pParse->nJPRef>1 ){
+ pParse->nJPRef--;
+ }else{
+ jsonParseReset(pParse);
+ sqlite3DbFree(pParse->db, pParse);
+ }
+ }
}
+/**************************************************************************
+** Utility routines for the JSON text parser
+**************************************************************************/
+
/*
-** Convert the JsonNode pNode into a pure JSON string and
-** append to pOut. Subsubstructure is also included. Return
-** the number of JsonNode objects that are encoded.
+** Translate a single byte of Hex into an integer.
+** This routine only gives a correct answer if h really is a valid hexadecimal
+** character: 0..9a..fA..F. But unlike sqlite3HexToInt(), it does not
+** assert() if the digit is not hex.
*/
-static void jsonRenderNode(
- JsonNode *pNode, /* The node to render */
- JsonString *pOut, /* Write JSON here */
- sqlite3_value **aReplace /* Replacement values */
-){
- if( pNode->jnFlags & (JNODE_REPLACE|JNODE_PATCH) ){
- if( pNode->jnFlags & JNODE_REPLACE ){
- jsonAppendValue(pOut, aReplace[pNode->u.iReplace]);
- return;
- }
- pNode = pNode->u.pPatch;
- }
- switch( pNode->eType ){
- default: {
- assert( pNode->eType==JSON_NULL );
- jsonAppendRaw(pOut, "null", 4);
- break;
- }
- case JSON_TRUE: {
- jsonAppendRaw(pOut, "true", 4);
- break;
- }
- case JSON_FALSE: {
- jsonAppendRaw(pOut, "false", 5);
- break;
- }
- case JSON_STRING: {
- if( pNode->jnFlags & JNODE_RAW ){
- jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
- break;
- }
- /* Fall through into the next case */
- }
- case JSON_REAL:
- case JSON_INT: {
- jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
- break;
- }
- case JSON_ARRAY: {
- u32 j = 1;
- jsonAppendChar(pOut, '[');
- for(;;){
- while( j<=pNode->n ){
- if( (pNode[j].jnFlags & JNODE_REMOVE)==0 ){
- jsonAppendSeparator(pOut);
- jsonRenderNode(&pNode[j], pOut, aReplace);
- }
- j += jsonNodeSize(&pNode[j]);
- }
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
- pNode = &pNode[pNode->u.iAppend];
- j = 1;
- }
- jsonAppendChar(pOut, ']');
- break;
- }
- case JSON_OBJECT: {
- u32 j = 1;
- jsonAppendChar(pOut, '{');
- for(;;){
- while( j<=pNode->n ){
- if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){
- jsonAppendSeparator(pOut);
- jsonRenderNode(&pNode[j], pOut, aReplace);
- jsonAppendChar(pOut, ':');
- jsonRenderNode(&pNode[j+1], pOut, aReplace);
- }
- j += 1 + jsonNodeSize(&pNode[j+1]);
- }
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
- pNode = &pNode[pNode->u.iAppend];
- j = 1;
- }
- jsonAppendChar(pOut, '}');
- break;
- }
- }
+static u8 jsonHexToInt(int h){
+#ifdef SQLITE_ASCII
+ h += 9*(1&(h>>6));
+#endif
+#ifdef SQLITE_EBCDIC
+ h += 9*(1&~(h>>4));
+#endif
+ return (u8)(h & 0xf);
}
/*
-** Return a JsonNode and all its descendents as a JSON string.
+** Convert a 4-byte hex string into an integer
*/
-static void jsonReturnJson(
- JsonNode *pNode, /* Node to return */
- sqlite3_context *pCtx, /* Return value for this function */
- sqlite3_value **aReplace /* Array of replacement values */
-){
- JsonString s;
- jsonInit(&s, pCtx);
- jsonRenderNode(pNode, &s, aReplace);
- jsonResult(&s);
- sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
+static u32 jsonHexToInt4(const char *z){
+ u32 v;
+ v = (jsonHexToInt(z[0])<<12)
+ + (jsonHexToInt(z[1])<<8)
+ + (jsonHexToInt(z[2])<<4)
+ + jsonHexToInt(z[3]);
+ return v;
}
/*
-** Make the JsonNode the return value of the function.
+** Return true if z[] begins with 2 (or more) hexadecimal digits
*/
-static void jsonReturn(
- JsonNode *pNode, /* Node to return */
- sqlite3_context *pCtx, /* Return value for this function */
- sqlite3_value **aReplace /* Array of replacement values */
-){
- switch( pNode->eType ){
- default: {
- assert( pNode->eType==JSON_NULL );
- sqlite3_result_null(pCtx);
- break;
- }
- case JSON_TRUE: {
- sqlite3_result_int(pCtx, 1);
- break;
- }
- case JSON_FALSE: {
- sqlite3_result_int(pCtx, 0);
- break;
- }
- case JSON_INT: {
- sqlite3_int64 i = 0;
- const char *z = pNode->u.zJContent;
- if( z[0]=='-' ){ z++; }
- while( z[0]>='0' && z[0]<='9' ){
- unsigned v = *(z++) - '0';
- if( i>=LARGEST_INT64/10 ){
- if( i>LARGEST_INT64/10 ) goto int_as_real;
- if( z[0]>='0' && z[0]<='9' ) goto int_as_real;
- if( v==9 ) goto int_as_real;
- if( v==8 ){
- if( pNode->u.zJContent[0]=='-' ){
- sqlite3_result_int64(pCtx, SMALLEST_INT64);
- goto int_done;
- }else{
- goto int_as_real;
+static int jsonIs2Hex(const char *z){
+ return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]);
+}
+
+/*
+** Return true if z[] begins with 4 (or more) hexadecimal digits
+*/
+static int jsonIs4Hex(const char *z){
+ return jsonIs2Hex(z) && jsonIs2Hex(&z[2]);
+}
+
+/*
+** Return the number of bytes of JSON5 whitespace at the beginning of
+** the input string z[].
+**
+** JSON5 whitespace consists of any of the following characters:
+**
+** Unicode UTF-8 Name
+** U+0009 09 horizontal tab
+** U+000a 0a line feed
+** U+000b 0b vertical tab
+** U+000c 0c form feed
+** U+000d 0d carriage return
+** U+0020 20 space
+** U+00a0 c2 a0 non-breaking space
+** U+1680 e1 9a 80 ogham space mark
+** U+2000 e2 80 80 en quad
+** U+2001 e2 80 81 em quad
+** U+2002 e2 80 82 en space
+** U+2003 e2 80 83 em space
+** U+2004 e2 80 84 three-per-em space
+** U+2005 e2 80 85 four-per-em space
+** U+2006 e2 80 86 six-per-em space
+** U+2007 e2 80 87 figure space
+** U+2008 e2 80 88 punctuation space
+** U+2009 e2 80 89 thin space
+** U+200a e2 80 8a hair space
+** U+2028 e2 80 a8 line separator
+** U+2029 e2 80 a9 paragraph separator
+** U+202f e2 80 af narrow no-break space (NNBSP)
+** U+205f e2 81 9f medium mathematical space (MMSP)
+** U+3000 e3 80 80 ideographical space
+** U+FEFF ef bb bf byte order mark
+**
+** In addition, comments between '/', '*' and '*', '/' and
+** from '/', '/' to end-of-line are also considered to be whitespace.
+*/
+static int json5Whitespace(const char *zIn){
+ int n = 0;
+ const u8 *z = (u8*)zIn;
+ while( 1 /*exit by "goto whitespace_done"*/ ){
+ switch( z[n] ){
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x20: {
+ n++;
+ break;
+ }
+ case '/': {
+ if( z[n+1]=='*' && z[n+2]!=0 ){
+ int j;
+ for(j=n+3; z[j]!='/' || z[j-1]!='*'; j++){
+ if( z[j]==0 ) goto whitespace_done;
+ }
+ n = j+1;
+ break;
+ }else if( z[n+1]=='/' ){
+ int j;
+ char c;
+ for(j=n+2; (c = z[j])!=0; j++){
+ if( c=='\n' || c=='\r' ) break;
+ if( 0xe2==(u8)c && 0x80==(u8)z[j+1]
+ && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])
+ ){
+ j += 2;
+ break;
}
}
+ n = j;
+ if( z[n] ) n++;
+ break;
}
- i = i*10 + v;
+ goto whitespace_done;
}
- if( pNode->u.zJContent[0]=='-' ){ i = -i; }
- sqlite3_result_int64(pCtx, i);
- int_done:
- break;
- int_as_real: /* fall through to real */;
- }
- case JSON_REAL: {
- double r;
-#ifdef SQLITE_AMALGAMATION
- const char *z = pNode->u.zJContent;
- sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
-#else
- r = strtod(pNode->u.zJContent, 0);
-#endif
- sqlite3_result_double(pCtx, r);
- break;
- }
- case JSON_STRING: {
-#if 0 /* Never happens because JNODE_RAW is only set by json_set(),
- ** json_insert() and json_replace() and those routines do not
- ** call jsonReturn() */
- if( pNode->jnFlags & JNODE_RAW ){
- sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
- SQLITE_TRANSIENT);
- }else
-#endif
- assert( (pNode->jnFlags & JNODE_RAW)==0 );
- if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
- /* JSON formatted without any backslash-escapes */
- sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
- SQLITE_TRANSIENT);
- }else{
- /* Translate JSON formatted string into raw text */
- u32 i;
- u32 n = pNode->n;
- const char *z = pNode->u.zJContent;
- char *zOut;
- u32 j;
- zOut = sqlite3_malloc( n+1 );
- if( zOut==0 ){
- sqlite3_result_error_nomem(pCtx);
+ case 0xc2: {
+ if( z[n+1]==0xa0 ){
+ n += 2;
break;
}
- for(i=1, j=0; i<n-1; i++){
- char c = z[i];
- if( c!='\\' ){
- zOut[j++] = c;
- }else{
- c = z[++i];
- if( c=='u' ){
- u32 v = 0, k;
- for(k=0; k<4; i++, k++){
- assert( i<n-2 );
- c = z[i+1];
- assert( safe_isxdigit(c) );
- if( c<='9' ) v = v*16 + c - '0';
- else if( c<='F' ) v = v*16 + c - 'A' + 10;
- else v = v*16 + c - 'a' + 10;
- }
- if( v==0 ) break;
- if( v<=0x7f ){
- zOut[j++] = (char)v;
- }else if( v<=0x7ff ){
- zOut[j++] = (char)(0xc0 | (v>>6));
- zOut[j++] = 0x80 | (v&0x3f);
- }else{
- zOut[j++] = (char)(0xe0 | (v>>12));
- zOut[j++] = 0x80 | ((v>>6)&0x3f);
- zOut[j++] = 0x80 | (v&0x3f);
- }
- }else{
- if( c=='b' ){
- c = '\b';
- }else if( c=='f' ){
- c = '\f';
- }else if( c=='n' ){
- c = '\n';
- }else if( c=='r' ){
- c = '\r';
- }else if( c=='t' ){
- c = '\t';
- }
- zOut[j++] = c;
- }
+ goto whitespace_done;
+ }
+ case 0xe1: {
+ if( z[n+1]==0x9a && z[n+2]==0x80 ){
+ n += 3;
+ break;
+ }
+ goto whitespace_done;
+ }
+ case 0xe2: {
+ if( z[n+1]==0x80 ){
+ u8 c = z[n+2];
+ if( c<0x80 ) goto whitespace_done;
+ if( c<=0x8a || c==0xa8 || c==0xa9 || c==0xaf ){
+ n += 3;
+ break;
}
+ }else if( z[n+1]==0x81 && z[n+2]==0x9f ){
+ n += 3;
+ break;
}
- zOut[j] = 0;
- sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
+ goto whitespace_done;
+ }
+ case 0xe3: {
+ if( z[n+1]==0x80 && z[n+2]==0x80 ){
+ n += 3;
+ break;
+ }
+ goto whitespace_done;
+ }
+ case 0xef: {
+ if( z[n+1]==0xbb && z[n+2]==0xbf ){
+ n += 3;
+ break;
+ }
+ goto whitespace_done;
+ }
+ default: {
+ goto whitespace_done;
}
- break;
- }
- case JSON_ARRAY:
- case JSON_OBJECT: {
- jsonReturnJson(pNode, pCtx, aReplace);
- break;
}
}
+ whitespace_done:
+ return n;
}
-/* Forward reference */
-static int jsonParseAddNode(JsonParse*,u32,u32,const char*);
-
/*
-** A macro to hint to the compiler that a function should not be
-** inlined.
+** Extra floating-point literals to allow in JSON.
*/
-#if defined(__GNUC__)
-# define JSON_NOINLINE __attribute__((noinline))
-#elif defined(_MSC_VER) && _MSC_VER>=1310
-# define JSON_NOINLINE __declspec(noinline)
-#else
-# define JSON_NOINLINE
-#endif
+static const struct NanInfName {
+ char c1;
+ char c2;
+ char n;
+ char eType;
+ char nRepl;
+ char *zMatch;
+ char *zRepl;
+} aNanInfName[] = {
+ { 'i', 'I', 3, JSONB_FLOAT, 7, "inf", "9.0e999" },
+ { 'i', 'I', 8, JSONB_FLOAT, 7, "infinity", "9.0e999" },
+ { 'n', 'N', 3, JSONB_NULL, 4, "NaN", "null" },
+ { 'q', 'Q', 4, JSONB_NULL, 4, "QNaN", "null" },
+ { 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" },
+};
-static JSON_NOINLINE int jsonParseAddNodeExpand(
- JsonParse *pParse, /* Append the node to this object */
- u32 eType, /* Node type */
- u32 n, /* Content size or sub-node count */
- const char *zContent /* Content */
+/*
+** Report the wrong number of arguments for json_insert(), json_replace()
+** or json_set().
+*/
+static void jsonWrongNumArgs(
+ sqlite3_context *pCtx,
+ const char *zFuncName
){
- u32 nNew;
- JsonNode *pNew;
- assert( pParse->nNode>=pParse->nAlloc );
- if( pParse->oom ) return -1;
- nNew = pParse->nAlloc*2 + 10;
- pNew = sqlite3_realloc(pParse->aNode, sizeof(JsonNode)*nNew);
- if( pNew==0 ){
- pParse->oom = 1;
- return -1;
+ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
+ zFuncName);
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
+}
+
+/****************************************************************************
+** Utility routines for dealing with the binary BLOB representation of JSON
+****************************************************************************/
+
+/*
+** Expand pParse->aBlob so that it holds at least N bytes.
+**
+** Return the number of errors.
+*/
+static int jsonBlobExpand(JsonParse *pParse, u32 N){
+ u8 *aNew;
+ u32 t;
+ assert( N>pParse->nBlobAlloc );
+ if( pParse->nBlobAlloc==0 ){
+ t = 100;
+ }else{
+ t = pParse->nBlobAlloc*2;
}
- pParse->nAlloc = nNew;
- pParse->aNode = pNew;
- assert( pParse->nNode<pParse->nAlloc );
- return jsonParseAddNode(pParse, eType, n, zContent);
+ if( t<N ) t = N+100;
+ aNew = sqlite3DbRealloc(pParse->db, pParse->aBlob, t);
+ if( aNew==0 ){ pParse->oom = 1; return 1; }
+ pParse->aBlob = aNew;
+ pParse->nBlobAlloc = t;
+ return 0;
}
/*
-** Create a new JsonNode instance based on the arguments and append that
-** instance to the JsonParse. Return the index in pParse->aNode[] of the
-** new node, or -1 if a memory allocation fails.
+** If pParse->aBlob is not previously editable (because it is taken
+** from sqlite3_value_blob(), as indicated by the fact that
+** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable
+** by making a copy into space obtained from malloc.
+**
+** Return true on success. Return false on OOM.
*/
-static int jsonParseAddNode(
- JsonParse *pParse, /* Append the node to this object */
- u32 eType, /* Node type */
- u32 n, /* Content size or sub-node count */
- const char *zContent /* Content */
+static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){
+ u8 *aOld;
+ u32 nSize;
+ assert( !pParse->bReadOnly );
+ if( pParse->oom ) return 0;
+ if( pParse->nBlobAlloc>0 ) return 1;
+ aOld = pParse->aBlob;
+ nSize = pParse->nBlob + nExtra;
+ pParse->aBlob = 0;
+ if( jsonBlobExpand(pParse, nSize) ){
+ return 0;
+ }
+ assert( pParse->nBlobAlloc >= pParse->nBlob + nExtra );
+ memcpy(pParse->aBlob, aOld, pParse->nBlob);
+ return 1;
+}
+
+/* Expand pParse->aBlob and append one bytes.
+*/
+static SQLITE_NOINLINE void jsonBlobExpandAndAppendOneByte(
+ JsonParse *pParse,
+ u8 c
){
- JsonNode *p;
- if( pParse->nNode>=pParse->nAlloc ){
- return jsonParseAddNodeExpand(pParse, eType, n, zContent);
+ jsonBlobExpand(pParse, pParse->nBlob+1);
+ if( pParse->oom==0 ){
+ assert( pParse->nBlob+1<=pParse->nBlobAlloc );
+ pParse->aBlob[pParse->nBlob++] = c;
}
- p = &pParse->aNode[pParse->nNode];
- p->eType = (u8)eType;
- p->jnFlags = 0;
- p->n = n;
- p->u.zJContent = zContent;
- return pParse->nNode++;
}
-/*
-** Return true if z[] begins with 4 (or more) hexadecimal digits
+/* Append a single character.
*/
-static int jsonIs4Hex(const char *z){
- int i;
- for(i=0; i<4; i++) if( !safe_isxdigit(z[i]) ) return 0;
+static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){
+ if( pParse->nBlob >= pParse->nBlobAlloc ){
+ jsonBlobExpandAndAppendOneByte(pParse, c);
+ }else{
+ pParse->aBlob[pParse->nBlob++] = c;
+ }
+}
+
+/* Slow version of jsonBlobAppendNode() that first resizes the
+** pParse->aBlob structure.
+*/
+static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*);
+static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode(
+ JsonParse *pParse,
+ u8 eType,
+ u32 szPayload,
+ const void *aPayload
+){
+ if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return;
+ jsonBlobAppendNode(pParse, eType, szPayload, aPayload);
+}
+
+
+/* Append an node type byte together with the payload size and
+** possibly also the payload.
+**
+** If aPayload is not NULL, then it is a pointer to the payload which
+** is also appended. If aPayload is NULL, the pParse->aBlob[] array
+** is resized (if necessary) so that it is big enough to hold the
+** payload, but the payload is not appended and pParse->nBlob is left
+** pointing to where the first byte of payload will eventually be.
+*/
+static void jsonBlobAppendNode(
+ JsonParse *pParse, /* The JsonParse object under construction */
+ u8 eType, /* Node type. One of JSONB_* */
+ u32 szPayload, /* Number of bytes of payload */
+ const void *aPayload /* The payload. Might be NULL */
+){
+ u8 *a;
+ if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){
+ jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload);
+ return;
+ }
+ assert( pParse->aBlob!=0 );
+ a = &pParse->aBlob[pParse->nBlob];
+ if( szPayload<=11 ){
+ a[0] = eType | (szPayload<<4);
+ pParse->nBlob += 1;
+ }else if( szPayload<=0xff ){
+ a[0] = eType | 0xc0;
+ a[1] = szPayload & 0xff;
+ pParse->nBlob += 2;
+ }else if( szPayload<=0xffff ){
+ a[0] = eType | 0xd0;
+ a[1] = (szPayload >> 8) & 0xff;
+ a[2] = szPayload & 0xff;
+ pParse->nBlob += 3;
+ }else{
+ a[0] = eType | 0xe0;
+ a[1] = (szPayload >> 24) & 0xff;
+ a[2] = (szPayload >> 16) & 0xff;
+ a[3] = (szPayload >> 8) & 0xff;
+ a[4] = szPayload & 0xff;
+ pParse->nBlob += 5;
+ }
+ if( aPayload ){
+ pParse->nBlob += szPayload;
+ memcpy(&pParse->aBlob[pParse->nBlob-szPayload], aPayload, szPayload);
+ }
+}
+
+/* Change the payload size for the node at index i to be szPayload.
+*/
+static int jsonBlobChangePayloadSize(
+ JsonParse *pParse,
+ u32 i,
+ u32 szPayload
+){
+ u8 *a;
+ u8 szType;
+ u8 nExtra;
+ u8 nNeeded;
+ int delta;
+ if( pParse->oom ) return 0;
+ a = &pParse->aBlob[i];
+ szType = a[0]>>4;
+ if( szType<=11 ){
+ nExtra = 0;
+ }else if( szType==12 ){
+ nExtra = 1;
+ }else if( szType==13 ){
+ nExtra = 2;
+ }else{
+ nExtra = 4;
+ }
+ if( szPayload<=11 ){
+ nNeeded = 0;
+ }else if( szPayload<=0xff ){
+ nNeeded = 1;
+ }else if( szPayload<=0xffff ){
+ nNeeded = 2;
+ }else{
+ nNeeded = 4;
+ }
+ delta = nNeeded - nExtra;
+ if( delta ){
+ u32 newSize = pParse->nBlob + delta;
+ if( delta>0 ){
+ if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){
+ return 0; /* OOM error. Error state recorded in pParse->oom. */
+ }
+ a = &pParse->aBlob[i];
+ memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1));
+ }else{
+ memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta));
+ }
+ pParse->nBlob = newSize;
+ }
+ if( nNeeded==0 ){
+ a[0] = (a[0] & 0x0f) | (szPayload<<4);
+ }else if( nNeeded==1 ){
+ a[0] = (a[0] & 0x0f) | 0xc0;
+ a[1] = szPayload & 0xff;
+ }else if( nNeeded==2 ){
+ a[0] = (a[0] & 0x0f) | 0xd0;
+ a[1] = (szPayload >> 8) & 0xff;
+ a[2] = szPayload & 0xff;
+ }else{
+ a[0] = (a[0] & 0x0f) | 0xe0;
+ a[1] = (szPayload >> 24) & 0xff;
+ a[2] = (szPayload >> 16) & 0xff;
+ a[3] = (szPayload >> 8) & 0xff;
+ a[4] = szPayload & 0xff;
+ }
+ return delta;
+}
+
+/*
+** If z[0] is 'u' and is followed by exactly 4 hexadecimal character,
+** then set *pOp to JSONB_TEXTJ and return true. If not, do not make
+** any changes to *pOp and return false.
+*/
+static int jsonIs4HexB(const char *z, int *pOp){
+ if( z[0]!='u' ) return 0;
+ if( !jsonIs4Hex(&z[1]) ) return 0;
+ *pOp = JSONB_TEXTJ;
return 1;
}
/*
-** Parse a single JSON value which begins at pParse->zJson[i]. Return the
-** index of the first character past the end of the value parsed.
+** Check a single element of the JSONB in pParse for validity.
+**
+** The element to be checked starts at offset i and must end at on the
+** last byte before iEnd.
+**
+** Return 0 if everything is correct. Return the 1-based byte offset of the
+** error if a problem is detected. (In other words, if the error is at offset
+** 0, return 1).
+*/
+static u32 jsonbValidityCheck(
+ const JsonParse *pParse, /* Input JSONB. Only aBlob and nBlob are used */
+ u32 i, /* Start of element as pParse->aBlob[i] */
+ u32 iEnd, /* One more than the last byte of the element */
+ u32 iDepth /* Current nesting depth */
+){
+ u32 n, sz, j, k;
+ const u8 *z;
+ u8 x;
+ if( iDepth>JSON_MAX_DEPTH ) return i+1;
+ sz = 0;
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( NEVER(n==0) ) return i+1; /* Checked by caller */
+ if( NEVER(i+n+sz!=iEnd) ) return i+1; /* Checked by caller */
+ z = pParse->aBlob;
+ x = z[i] & 0x0f;
+ switch( x ){
+ case JSONB_NULL:
+ case JSONB_TRUE:
+ case JSONB_FALSE: {
+ return n+sz==1 ? 0 : i+1;
+ }
+ case JSONB_INT: {
+ if( sz<1 ) return i+1;
+ j = i+n;
+ if( z[j]=='-' ){
+ j++;
+ if( sz<2 ) return i+1;
+ }
+ k = i+n+sz;
+ while( j<k ){
+ if( sqlite3Isdigit(z[j]) ){
+ j++;
+ }else{
+ return j+1;
+ }
+ }
+ return 0;
+ }
+ case JSONB_INT5: {
+ if( sz<3 ) return i+1;
+ j = i+n;
+ if( z[j]=='-' ){
+ if( sz<4 ) return i+1;
+ j++;
+ }
+ if( z[j]!='0' ) return i+1;
+ if( z[j+1]!='x' && z[j+1]!='X' ) return j+2;
+ j += 2;
+ k = i+n+sz;
+ while( j<k ){
+ if( sqlite3Isxdigit(z[j]) ){
+ j++;
+ }else{
+ return j+1;
+ }
+ }
+ return 0;
+ }
+ case JSONB_FLOAT:
+ case JSONB_FLOAT5: {
+ u8 seen = 0; /* 0: initial. 1: '.' seen 2: 'e' seen */
+ if( sz<2 ) return i+1;
+ j = i+n;
+ k = j+sz;
+ if( z[j]=='-' ){
+ j++;
+ if( sz<3 ) return i+1;
+ }
+ if( z[j]=='.' ){
+ if( x==JSONB_FLOAT ) return j+1;
+ if( !sqlite3Isdigit(z[j+1]) ) return j+1;
+ j += 2;
+ seen = 1;
+ }else if( z[j]=='0' && x==JSONB_FLOAT ){
+ if( j+3>k ) return j+1;
+ if( z[j+1]!='.' && z[j+1]!='e' && z[j+1]!='E' ) return j+1;
+ j++;
+ }
+ for(; j<k; j++){
+ if( sqlite3Isdigit(z[j]) ) continue;
+ if( z[j]=='.' ){
+ if( seen>0 ) return j+1;
+ if( x==JSONB_FLOAT && (j==k-1 || !sqlite3Isdigit(z[j+1])) ){
+ return j+1;
+ }
+ seen = 1;
+ continue;
+ }
+ if( z[j]=='e' || z[j]=='E' ){
+ if( seen==2 ) return j+1;
+ if( j==k-1 ) return j+1;
+ if( z[j+1]=='+' || z[j+1]=='-' ){
+ j++;
+ if( j==k-1 ) return j+1;
+ }
+ seen = 2;
+ continue;
+ }
+ return j+1;
+ }
+ if( seen==0 ) return i+1;
+ return 0;
+ }
+ case JSONB_TEXT: {
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ if( !jsonIsOk[z[j]] && z[j]!='\'' ) return j+1;
+ j++;
+ }
+ return 0;
+ }
+ case JSONB_TEXTJ:
+ case JSONB_TEXT5: {
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ if( !jsonIsOk[z[j]] && z[j]!='\'' ){
+ if( z[j]=='"' ){
+ if( x==JSONB_TEXTJ ) return j+1;
+ }else if( z[j]!='\\' || j+1>=k ){
+ return j+1;
+ }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){
+ j++;
+ }else if( z[j+1]=='u' ){
+ if( j+5>=k ) return j+1;
+ if( !jsonIs4Hex((const char*)&z[j+2]) ) return j+1;
+ j++;
+ }else if( x!=JSONB_TEXT5 ){
+ return j+1;
+ }else{
+ u32 c = 0;
+ u32 szC = jsonUnescapeOneChar((const char*)&z[j], k-j, &c);
+ if( c==JSON_INVALID_CHAR ) return j+1;
+ j += szC - 1;
+ }
+ }
+ j++;
+ }
+ return 0;
+ }
+ case JSONB_TEXTRAW: {
+ return 0;
+ }
+ case JSONB_ARRAY: {
+ u32 sub;
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ sz = 0;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return j+1;
+ if( j+n+sz>k ) return j+1;
+ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
+ if( sub ) return sub;
+ j += n + sz;
+ }
+ assert( j==k );
+ return 0;
+ }
+ case JSONB_OBJECT: {
+ u32 cnt = 0;
+ u32 sub;
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ sz = 0;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return j+1;
+ if( j+n+sz>k ) return j+1;
+ if( (cnt & 1)==0 ){
+ x = z[j] & 0x0f;
+ if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return j+1;
+ }
+ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
+ if( sub ) return sub;
+ cnt++;
+ j += n + sz;
+ }
+ assert( j==k );
+ if( (cnt & 1)!=0 ) return j+1;
+ return 0;
+ }
+ default: {
+ return i+1;
+ }
+ }
+}
+
+/*
+** Translate a single element of JSON text at pParse->zJson[i] into
+** its equivalent binary JSONB representation. Append the translation into
+** pParse->aBlob[] beginning at pParse->nBlob. The size of
+** pParse->aBlob[] is increased as necessary.
+**
+** Return the index of the first character past the end of the element parsed,
+** or one of the following special result codes:
**
-** Return negative for a syntax error. Special cases: return -2 if the
-** first non-whitespace character is '}' and return -3 if the first
-** non-whitespace character is ']'.
+** 0 End of input
+** -1 Syntax error or OOM
+** -2 '}' seen \
+** -3 ']' seen \___ For these returns, pParse->iErr is set to
+** -4 ',' seen / the index in zJson[] of the seen character
+** -5 ':' seen /
*/
-static int jsonParseValue(JsonParse *pParse, u32 i){
+static int jsonTranslateTextToBlob(JsonParse *pParse, u32 i){
char c;
u32 j;
- int iThis;
+ u32 iThis, iStart;
int x;
- JsonNode *pNode;
+ u8 t;
const char *z = pParse->zJson;
- while( safe_isspace(z[i]) ){ i++; }
- if( (c = z[i])=='{' ){
+json_parse_restart:
+ switch( (u8)z[i] ){
+ case '{': {
/* Parse object */
- iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- if( iThis<0 ) return -1;
+ iThis = pParse->nBlob;
+ jsonBlobAppendNode(pParse, JSONB_OBJECT, pParse->nJson-i, 0);
+ if( ++pParse->iDepth > JSON_MAX_DEPTH ){
+ pParse->iErr = i;
+ return -1;
+ }
+ iStart = pParse->nBlob;
for(j=i+1;;j++){
- while( safe_isspace(z[j]) ){ j++; }
- if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
- x = jsonParseValue(pParse, j);
- if( x<0 ){
- pParse->iDepth--;
- if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1;
- return -1;
+ u32 iBlob = pParse->nBlob;
+ x = jsonTranslateTextToBlob(pParse, j);
+ if( x<=0 ){
+ int op;
+ if( x==(-2) ){
+ j = pParse->iErr;
+ if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1;
+ break;
+ }
+ j += json5Whitespace(&z[j]);
+ op = JSONB_TEXT;
+ if( sqlite3JsonId1(z[j])
+ || (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op))
+ ){
+ int k = j+1;
+ while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0)
+ || (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op))
+ ){
+ k++;
+ }
+ assert( iBlob==pParse->nBlob );
+ jsonBlobAppendNode(pParse, op, k-j, &z[j]);
+ pParse->hasNonstd = 1;
+ x = k;
+ }else{
+ if( x!=-1 ) pParse->iErr = j;
+ return -1;
+ }
}
if( pParse->oom ) return -1;
- pNode = &pParse->aNode[pParse->nNode-1];
- if( pNode->eType!=JSON_STRING ) return -1;
- pNode->jnFlags |= JNODE_LABEL;
+ t = pParse->aBlob[iBlob] & 0x0f;
+ if( t<JSONB_TEXT || t>JSONB_TEXTRAW ){
+ pParse->iErr = j;
+ return -1;
+ }
j = x;
- while( safe_isspace(z[j]) ){ j++; }
- if( z[j]!=':' ) return -1;
- j++;
- x = jsonParseValue(pParse, j);
- pParse->iDepth--;
- if( x<0 ) return -1;
+ if( z[j]==':' ){
+ j++;
+ }else{
+ if( jsonIsspace(z[j]) ){
+ /* strspn() is not helpful here */
+ do{ j++; }while( jsonIsspace(z[j]) );
+ if( z[j]==':' ){
+ j++;
+ goto parse_object_value;
+ }
+ }
+ x = jsonTranslateTextToBlob(pParse, j);
+ if( x!=(-5) ){
+ if( x!=(-1) ) pParse->iErr = j;
+ return -1;
+ }
+ j = pParse->iErr+1;
+ }
+ parse_object_value:
+ x = jsonTranslateTextToBlob(pParse, j);
+ if( x<=0 ){
+ if( x!=(-1) ) pParse->iErr = j;
+ return -1;
+ }
j = x;
- while( safe_isspace(z[j]) ){ j++; }
- c = z[j];
- if( c==',' ) continue;
- if( c!='}' ) return -1;
- break;
+ if( z[j]==',' ){
+ continue;
+ }else if( z[j]=='}' ){
+ break;
+ }else{
+ if( jsonIsspace(z[j]) ){
+ j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
+ if( z[j]==',' ){
+ continue;
+ }else if( z[j]=='}' ){
+ break;
+ }
+ }
+ x = jsonTranslateTextToBlob(pParse, j);
+ if( x==(-4) ){
+ j = pParse->iErr;
+ continue;
+ }
+ if( x==(-2) ){
+ j = pParse->iErr;
+ break;
+ }
+ }
+ pParse->iErr = j;
+ return -1;
}
- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
+ pParse->iDepth--;
return j+1;
- }else if( c=='[' ){
+ }
+ case '[': {
/* Parse array */
- iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
- if( iThis<0 ) return -1;
+ iThis = pParse->nBlob;
+ jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0);
+ iStart = pParse->nBlob;
+ if( pParse->oom ) return -1;
+ if( ++pParse->iDepth > JSON_MAX_DEPTH ){
+ pParse->iErr = i;
+ return -1;
+ }
for(j=i+1;;j++){
- while( safe_isspace(z[j]) ){ j++; }
- if( ++pParse->iDepth > JSON_MAX_DEPTH ) return -1;
- x = jsonParseValue(pParse, j);
- pParse->iDepth--;
- if( x<0 ){
- if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1;
+ x = jsonTranslateTextToBlob(pParse, j);
+ if( x<=0 ){
+ if( x==(-3) ){
+ j = pParse->iErr;
+ if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1;
+ break;
+ }
+ if( x!=(-1) ) pParse->iErr = j;
return -1;
}
j = x;
- while( safe_isspace(z[j]) ){ j++; }
- c = z[j];
- if( c==',' ) continue;
- if( c!=']' ) return -1;
- break;
+ if( z[j]==',' ){
+ continue;
+ }else if( z[j]==']' ){
+ break;
+ }else{
+ if( jsonIsspace(z[j]) ){
+ j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
+ if( z[j]==',' ){
+ continue;
+ }else if( z[j]==']' ){
+ break;
+ }
+ }
+ x = jsonTranslateTextToBlob(pParse, j);
+ if( x==(-4) ){
+ j = pParse->iErr;
+ continue;
+ }
+ if( x==(-3) ){
+ j = pParse->iErr;
+ break;
+ }
+ }
+ pParse->iErr = j;
+ return -1;
}
- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
+ pParse->iDepth--;
return j+1;
- }else if( c=='"' ){
+ }
+ case '\'': {
+ u8 opcode;
+ char cDelim;
+ pParse->hasNonstd = 1;
+ opcode = JSONB_TEXT;
+ goto parse_string;
+ case '"':
/* Parse string */
- u8 jnFlags = 0;
+ opcode = JSONB_TEXT;
+ parse_string:
+ cDelim = z[i];
j = i+1;
- for(;;){
- c = z[j];
- if( (c & ~0x1f)==0 ){
- /* Control characters are not allowed in strings */
- return -1;
+ while( 1 /*exit-by-break*/ ){
+ if( jsonIsOk[(u8)z[j]] ){
+ if( !jsonIsOk[(u8)z[j+1]] ){
+ j += 1;
+ }else if( !jsonIsOk[(u8)z[j+2]] ){
+ j += 2;
+ }else{
+ j += 3;
+ continue;
+ }
}
- if( c=='\\' ){
+ c = z[j];
+ if( c==cDelim ){
+ break;
+ }else if( c=='\\' ){
c = z[++j];
if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
|| c=='n' || c=='r' || c=='t'
- || (c=='u' && jsonIs4Hex(z+j+1)) ){
- jnFlags = JNODE_ESCAPE;
+ || (c=='u' && jsonIs4Hex(&z[j+1])) ){
+ if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
+ }else if( c=='\'' || c=='0' || c=='v' || c=='\n'
+ || (0xe2==(u8)c && 0x80==(u8)z[j+1]
+ && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
+ || (c=='x' && jsonIs2Hex(&z[j+1])) ){
+ opcode = JSONB_TEXT5;
+ pParse->hasNonstd = 1;
+ }else if( c=='\r' ){
+ if( z[j+1]=='\n' ) j++;
+ opcode = JSONB_TEXT5;
+ pParse->hasNonstd = 1;
}else{
+ pParse->iErr = j;
return -1;
}
+ }else if( c<=0x1f ){
+ /* Control characters are not allowed in strings */
+ pParse->iErr = j;
+ return -1;
}else if( c=='"' ){
- break;
+ opcode = JSONB_TEXT5;
}
j++;
}
- jsonParseAddNode(pParse, JSON_STRING, j+1-i, &z[i]);
- if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags;
+ jsonBlobAppendNode(pParse, opcode, j-1-i, &z[i+1]);
return j+1;
- }else if( c=='n'
- && strncmp(z+i,"null",4)==0
- && !safe_isalnum(z[i+4]) ){
- jsonParseAddNode(pParse, JSON_NULL, 0, 0);
- return i+4;
- }else if( c=='t'
- && strncmp(z+i,"true",4)==0
- && !safe_isalnum(z[i+4]) ){
- jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
- return i+4;
- }else if( c=='f'
- && strncmp(z+i,"false",5)==0
- && !safe_isalnum(z[i+5]) ){
- jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
- return i+5;
- }else if( c=='-' || (c>='0' && c<='9') ){
+ }
+ case 't': {
+ if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){
+ jsonBlobAppendOneByte(pParse, JSONB_TRUE);
+ return i+4;
+ }
+ pParse->iErr = i;
+ return -1;
+ }
+ case 'f': {
+ if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){
+ jsonBlobAppendOneByte(pParse, JSONB_FALSE);
+ return i+5;
+ }
+ pParse->iErr = i;
+ return -1;
+ }
+ case '+': {
+ u8 seenE;
+ pParse->hasNonstd = 1;
+ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
+ goto parse_number;
+ case '.':
+ if( sqlite3Isdigit(z[i+1]) ){
+ pParse->hasNonstd = 1;
+ t = 0x03; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
+ seenE = 0;
+ goto parse_number_2;
+ }
+ pParse->iErr = i;
+ return -1;
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
/* Parse number */
- u8 seenDP = 0;
- u8 seenE = 0;
+ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
+ parse_number:
+ seenE = 0;
assert( '-' < '0' );
+ assert( '+' < '0' );
+ assert( '.' < '0' );
+ c = z[i];
+
if( c<='0' ){
- j = c=='-' ? i+1 : i;
- if( z[j]=='0' && z[j+1]>='0' && z[j+1]<='9' ) return -1;
+ if( c=='0' ){
+ if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){
+ assert( t==0x00 );
+ pParse->hasNonstd = 1;
+ t = 0x01;
+ for(j=i+3; sqlite3Isxdigit(z[j]); j++){}
+ goto parse_number_finish;
+ }else if( sqlite3Isdigit(z[i+1]) ){
+ pParse->iErr = i+1;
+ return -1;
+ }
+ }else{
+ if( !sqlite3Isdigit(z[i+1]) ){
+ /* JSON5 allows for "+Infinity" and "-Infinity" using exactly
+ ** that case. SQLite also allows these in any case and it allows
+ ** "+inf" and "-inf". */
+ if( (z[i+1]=='I' || z[i+1]=='i')
+ && sqlite3StrNICmp(&z[i+1], "inf",3)==0
+ ){
+ pParse->hasNonstd = 1;
+ if( z[i]=='-' ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
+ }else{
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
+ }
+ return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4);
+ }
+ if( z[i+1]=='.' ){
+ pParse->hasNonstd = 1;
+ t |= 0x01;
+ goto parse_number_2;
+ }
+ pParse->iErr = i;
+ return -1;
+ }
+ if( z[i+1]=='0' ){
+ if( sqlite3Isdigit(z[i+2]) ){
+ pParse->iErr = i+1;
+ return -1;
+ }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){
+ pParse->hasNonstd = 1;
+ t |= 0x01;
+ for(j=i+4; sqlite3Isxdigit(z[j]); j++){}
+ goto parse_number_finish;
+ }
+ }
+ }
}
- j = i+1;
- for(;; j++){
+
+ parse_number_2:
+ for(j=i+1;; j++){
c = z[j];
- if( c>='0' && c<='9' ) continue;
+ if( sqlite3Isdigit(c) ) continue;
if( c=='.' ){
- if( z[j-1]=='-' ) return -1;
- if( seenDP ) return -1;
- seenDP = 1;
+ if( (t & 0x02)!=0 ){
+ pParse->iErr = j;
+ return -1;
+ }
+ t |= 0x02;
continue;
}
if( c=='e' || c=='E' ){
- if( z[j-1]<'0' ) return -1;
- if( seenE ) return -1;
- seenDP = seenE = 1;
+ if( z[j-1]<'0' ){
+ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
+ pParse->hasNonstd = 1;
+ t |= 0x01;
+ }else{
+ pParse->iErr = j;
+ return -1;
+ }
+ }
+ if( seenE ){
+ pParse->iErr = j;
+ return -1;
+ }
+ t |= 0x02;
+ seenE = 1;
c = z[j+1];
if( c=='+' || c=='-' ){
j++;
c = z[j+1];
}
- if( c<'0' || c>'9' ) return -1;
+ if( c<'0' || c>'9' ){
+ pParse->iErr = j;
+ return -1;
+ }
continue;
}
break;
}
- if( z[j-1]<'0' ) return -1;
- jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT,
- j - i, &z[i]);
+ if( z[j-1]<'0' ){
+ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
+ pParse->hasNonstd = 1;
+ t |= 0x01;
+ }else{
+ pParse->iErr = j;
+ return -1;
+ }
+ }
+ parse_number_finish:
+ assert( JSONB_INT+0x01==JSONB_INT5 );
+ assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 );
+ assert( JSONB_INT+0x02==JSONB_FLOAT );
+ if( z[i]=='+' ) i++;
+ jsonBlobAppendNode(pParse, JSONB_INT+t, j-i, &z[i]);
return j;
- }else if( c=='}' ){
+ }
+ case '}': {
+ pParse->iErr = i;
return -2; /* End of {...} */
- }else if( c==']' ){
+ }
+ case ']': {
+ pParse->iErr = i;
return -3; /* End of [...] */
- }else if( c==0 ){
+ }
+ case ',': {
+ pParse->iErr = i;
+ return -4; /* List separator */
+ }
+ case ':': {
+ pParse->iErr = i;
+ return -5; /* Object label/value separator */
+ }
+ case 0: {
return 0; /* End of file */
- }else{
+ }
+ case 0x09:
+ case 0x0a:
+ case 0x0d:
+ case 0x20: {
+ i += 1 + (u32)strspn(&z[i+1], jsonSpaces);
+ goto json_parse_restart;
+ }
+ case 0x0b:
+ case 0x0c:
+ case '/':
+ case 0xc2:
+ case 0xe1:
+ case 0xe2:
+ case 0xe3:
+ case 0xef: {
+ j = json5Whitespace(&z[i]);
+ if( j>0 ){
+ i += j;
+ pParse->hasNonstd = 1;
+ goto json_parse_restart;
+ }
+ pParse->iErr = i;
+ return -1;
+ }
+ case 'n': {
+ if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
+ jsonBlobAppendOneByte(pParse, JSONB_NULL);
+ return i+4;
+ }
+ /* fall-through into the default case that checks for NaN */
+ }
+ default: {
+ u32 k;
+ int nn;
+ c = z[i];
+ for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){
+ if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue;
+ nn = aNanInfName[k].n;
+ if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){
+ continue;
+ }
+ if( sqlite3Isalnum(z[i+nn]) ) continue;
+ if( aNanInfName[k].eType==JSONB_FLOAT ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
+ }else{
+ jsonBlobAppendOneByte(pParse, JSONB_NULL);
+ }
+ pParse->hasNonstd = 1;
+ return i + nn;
+ }
+ pParse->iErr = i;
return -1; /* Syntax error */
}
+ } /* End switch(z[i]) */
}
+
/*
** Parse a complete JSON string. Return 0 on success or non-zero if there
-** are any errors. If an error occurs, free all memory associated with
-** pParse.
+** are any errors. If an error occurs, free all memory held by pParse,
+** but not pParse itself.
**
-** pParse is uninitialized when this routine is called.
+** pParse must be initialized to an empty parse object prior to calling
+** this routine.
*/
-static int jsonParse(
+static int jsonConvertTextToBlob(
JsonParse *pParse, /* Initialize and fill this JsonParse object */
- sqlite3_context *pCtx, /* Report errors here */
- const char *zJson /* Input JSON text to be parsed */
+ sqlite3_context *pCtx /* Report errors here */
){
int i;
- memset(pParse, 0, sizeof(*pParse));
- if( zJson==0 ) return 1;
- pParse->zJson = zJson;
- i = jsonParseValue(pParse, 0);
+ const char *zJson = pParse->zJson;
+ i = jsonTranslateTextToBlob(pParse, 0);
if( pParse->oom ) i = -1;
if( i>0 ){
+#ifdef SQLITE_DEBUG
assert( pParse->iDepth==0 );
- while( safe_isspace(zJson[i]) ) i++;
- if( zJson[i] ) i = -1;
+ if( sqlite3Config.bJsonSelfcheck ){
+ assert( jsonbValidityCheck(pParse, 0, pParse->nBlob, 0)==0 );
+ }
+#endif
+ while( jsonIsspace(zJson[i]) ) i++;
+ if( zJson[i] ){
+ i += json5Whitespace(&zJson[i]);
+ if( zJson[i] ){
+ if( pCtx ) sqlite3_result_error(pCtx, "malformed JSON", -1);
+ jsonParseReset(pParse);
+ return 1;
+ }
+ pParse->hasNonstd = 1;
+ }
}
if( i<=0 ){
if( pCtx!=0 ){
@@ -177513,159 +205174,715 @@ static int jsonParse(
return 0;
}
-/* Mark node i of pParse as being a child of iParent. Call recursively
-** to fill in all the descendants of node i.
+/*
+** The input string pStr is a well-formed JSON text string. Convert
+** this into the JSONB format and make it the return value of the
+** SQL function.
*/
-static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
- JsonNode *pNode = &pParse->aNode[i];
- u32 j;
- pParse->aUp[i] = iParent;
- switch( pNode->eType ){
- case JSON_ARRAY: {
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
- jsonParseFillInParentage(pParse, i+j, i);
+static void jsonReturnStringAsBlob(JsonString *pStr){
+ JsonParse px;
+ memset(&px, 0, sizeof(px));
+ jsonStringTerminate(pStr);
+ px.zJson = pStr->zBuf;
+ px.nJson = pStr->nUsed;
+ px.db = sqlite3_context_db_handle(pStr->pCtx);
+ (void)jsonTranslateTextToBlob(&px, 0);
+ if( px.oom ){
+ sqlite3DbFree(px.db, px.aBlob);
+ sqlite3_result_error_nomem(pStr->pCtx);
+ }else{
+ assert( px.nBlobAlloc>0 );
+ assert( !px.bReadOnly );
+ sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, SQLITE_DYNAMIC);
+ }
+}
+
+/* The byte at index i is a node type-code. This routine
+** determines the payload size for that node and writes that
+** payload size in to *pSz. It returns the offset from i to the
+** beginning of the payload. Return 0 on error.
+*/
+static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){
+ u8 x;
+ u32 sz;
+ u32 n;
+ if( NEVER(i>pParse->nBlob) ){
+ *pSz = 0;
+ return 0;
+ }
+ x = pParse->aBlob[i]>>4;
+ if( x<=11 ){
+ sz = x;
+ n = 1;
+ }else if( x==12 ){
+ if( i+1>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = pParse->aBlob[i+1];
+ n = 2;
+ }else if( x==13 ){
+ if( i+2>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2];
+ n = 3;
+ }else if( x==14 ){
+ if( i+4>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = ((u32)pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) +
+ (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4];
+ n = 5;
+ }else{
+ if( i+8>=pParse->nBlob
+ || pParse->aBlob[i+1]!=0
+ || pParse->aBlob[i+2]!=0
+ || pParse->aBlob[i+3]!=0
+ || pParse->aBlob[i+4]!=0
+ ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
+ (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
+ n = 9;
+ }
+ if( (i64)i+sz+n > pParse->nBlob
+ && (i64)i+sz+n > pParse->nBlob-pParse->delta
+ ){
+ sz = 0;
+ n = 0;
+ }
+ *pSz = sz;
+ return n;
+}
+
+
+/*
+** Translate the binary JSONB representation of JSON beginning at
+** pParse->aBlob[i] into a JSON text string. Append the JSON
+** text onto the end of pOut. Return the index in pParse->aBlob[]
+** of the first byte past the end of the element that is translated.
+**
+** If an error is detected in the BLOB input, the pOut->eErr flag
+** might get set to JSTRING_MALFORMED. But not all BLOB input errors
+** are detected. So a malformed JSONB input might either result
+** in an error, or in incorrect JSON.
+**
+** The pOut->eErr JSTRING_OOM flag is set on a OOM.
+*/
+static u32 jsonTranslateBlobToText(
+ const JsonParse *pParse, /* the complete parse of the JSON */
+ u32 i, /* Start rendering at this index */
+ JsonString *pOut /* Write JSON here */
+){
+ u32 sz, n, j, iEnd;
+
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( n==0 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ return pParse->nBlob+1;
+ }
+ switch( pParse->aBlob[i] & 0x0f ){
+ case JSONB_NULL: {
+ jsonAppendRawNZ(pOut, "null", 4);
+ return i+1;
+ }
+ case JSONB_TRUE: {
+ jsonAppendRawNZ(pOut, "true", 4);
+ return i+1;
+ }
+ case JSONB_FALSE: {
+ jsonAppendRawNZ(pOut, "false", 5);
+ return i+1;
+ }
+ case JSONB_INT:
+ case JSONB_FLOAT: {
+ if( sz==0 ) goto malformed_jsonb;
+ jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
+ break;
+ }
+ case JSONB_INT5: { /* Integer literal in hexadecimal notation */
+ u32 k = 2;
+ sqlite3_uint64 u = 0;
+ const char *zIn = (const char*)&pParse->aBlob[i+n];
+ int bOverflow = 0;
+ if( sz==0 ) goto malformed_jsonb;
+ if( zIn[0]=='-' ){
+ jsonAppendChar(pOut, '-');
+ k++;
+ }else if( zIn[0]=='+' ){
+ k++;
+ }
+ for(; k<sz; k++){
+ if( !sqlite3Isxdigit(zIn[k]) ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ break;
+ }else if( (u>>60)!=0 ){
+ bOverflow = 1;
+ }else{
+ u = u*16 + sqlite3HexToInt(zIn[k]);
+ }
+ }
+ jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u);
+ break;
+ }
+ case JSONB_FLOAT5: { /* Float literal missing digits beside "." */
+ u32 k = 0;
+ const char *zIn = (const char*)&pParse->aBlob[i+n];
+ if( sz==0 ) goto malformed_jsonb;
+ if( zIn[0]=='-' ){
+ jsonAppendChar(pOut, '-');
+ k++;
+ }
+ if( zIn[k]=='.' ){
+ jsonAppendChar(pOut, '0');
+ }
+ for(; k<sz; k++){
+ jsonAppendChar(pOut, zIn[k]);
+ if( zIn[k]=='.' && (k+1==sz || !sqlite3Isdigit(zIn[k+1])) ){
+ jsonAppendChar(pOut, '0');
+ }
}
break;
}
- case JSON_OBJECT: {
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){
- pParse->aUp[i+j] = i;
- jsonParseFillInParentage(pParse, i+j+1, i);
+ case JSONB_TEXT:
+ case JSONB_TEXTJ: {
+ jsonAppendChar(pOut, '"');
+ jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
+ jsonAppendChar(pOut, '"');
+ break;
+ }
+ case JSONB_TEXT5: {
+ const char *zIn;
+ u32 k;
+ u32 sz2 = sz;
+ zIn = (const char*)&pParse->aBlob[i+n];
+ jsonAppendChar(pOut, '"');
+ while( sz2>0 ){
+ for(k=0; k<sz2 && zIn[k]!='\\' && zIn[k]!='"'; k++){}
+ if( k>0 ){
+ jsonAppendRawNZ(pOut, zIn, k);
+ if( k>=sz2 ){
+ break;
+ }
+ zIn += k;
+ sz2 -= k;
+ }
+ if( zIn[0]=='"' ){
+ jsonAppendRawNZ(pOut, "\\\"", 2);
+ zIn++;
+ sz2--;
+ continue;
+ }
+ assert( zIn[0]=='\\' );
+ assert( sz2>=1 );
+ if( sz2<2 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ break;
+ }
+ switch( (u8)zIn[1] ){
+ case '\'':
+ jsonAppendChar(pOut, '\'');
+ break;
+ case 'v':
+ jsonAppendRawNZ(pOut, "\\u0009", 6);
+ break;
+ case 'x':
+ if( sz2<4 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ sz2 = 2;
+ break;
+ }
+ jsonAppendRawNZ(pOut, "\\u00", 4);
+ jsonAppendRawNZ(pOut, &zIn[2], 2);
+ zIn += 2;
+ sz2 -= 2;
+ break;
+ case '0':
+ jsonAppendRawNZ(pOut, "\\u0000", 6);
+ break;
+ case '\r':
+ if( sz2>2 && zIn[2]=='\n' ){
+ zIn++;
+ sz2--;
+ }
+ break;
+ case '\n':
+ break;
+ case 0xe2:
+ /* '\' followed by either U+2028 or U+2029 is ignored as
+ ** whitespace. Not that in UTF8, U+2028 is 0xe2 0x80 0x29.
+ ** U+2029 is the same except for the last byte */
+ if( sz2<4
+ || 0x80!=(u8)zIn[2]
+ || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3])
+ ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ sz2 = 2;
+ break;
+ }
+ zIn += 2;
+ sz2 -= 2;
+ break;
+ default:
+ jsonAppendRawNZ(pOut, zIn, 2);
+ break;
+ }
+ assert( sz2>=2 );
+ zIn += 2;
+ sz2 -= 2;
}
+ jsonAppendChar(pOut, '"');
+ break;
+ }
+ case JSONB_TEXTRAW: {
+ jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz);
+ break;
+ }
+ case JSONB_ARRAY: {
+ jsonAppendChar(pOut, '[');
+ j = i+n;
+ iEnd = j+sz;
+ while( j<iEnd && pOut->eErr==0 ){
+ j = jsonTranslateBlobToText(pParse, j, pOut);
+ jsonAppendChar(pOut, ',');
+ }
+ if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
+ if( sz>0 ) jsonStringTrimOneChar(pOut);
+ jsonAppendChar(pOut, ']');
break;
}
+ case JSONB_OBJECT: {
+ int x = 0;
+ jsonAppendChar(pOut, '{');
+ j = i+n;
+ iEnd = j+sz;
+ while( j<iEnd && pOut->eErr==0 ){
+ j = jsonTranslateBlobToText(pParse, j, pOut);
+ jsonAppendChar(pOut, (x++ & 1) ? ',' : ':');
+ }
+ if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
+ if( sz>0 ) jsonStringTrimOneChar(pOut);
+ jsonAppendChar(pOut, '}');
+ break;
+ }
+
default: {
+ malformed_jsonb:
+ pOut->eErr |= JSTRING_MALFORMED;
break;
}
}
+ return i+n+sz;
+}
+
+/* Return true if the input pJson
+**
+** For performance reasons, this routine does not do a detailed check of the
+** input BLOB to ensure that it is well-formed. Hence, false positives are
+** possible. False negatives should never occur, however.
+*/
+static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
+ u32 sz, n;
+ const u8 *aBlob;
+ int nBlob;
+ JsonParse s;
+ if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0;
+ aBlob = sqlite3_value_blob(pJson);
+ nBlob = sqlite3_value_bytes(pJson);
+ if( nBlob<1 ) return 0;
+ if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0;
+ memset(&s, 0, sizeof(s));
+ s.aBlob = (u8*)aBlob;
+ s.nBlob = nBlob;
+ n = jsonbPayloadSize(&s, 0, &sz);
+ if( n==0 ) return 0;
+ if( sz+n!=(u32)nBlob ) return 0;
+ if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0;
+ return sz+n==(u32)nBlob;
}
/*
-** Compute the parentage of all nodes in a completed parse.
+** Given that a JSONB_ARRAY object starts at offset i, return
+** the number of entries in that array.
*/
-static int jsonParseFindParents(JsonParse *pParse){
- u32 *aUp;
- assert( pParse->aUp==0 );
- aUp = pParse->aUp = sqlite3_malloc( sizeof(u32)*pParse->nNode );
- if( aUp==0 ){
- pParse->oom = 1;
- return SQLITE_NOMEM;
+static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){
+ u32 n, sz, i, iEnd;
+ u32 k = 0;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ iEnd = iRoot+n+sz;
+ for(i=iRoot+n; n>0 && i<iEnd; i+=sz+n, k++){
+ n = jsonbPayloadSize(pParse, i, &sz);
}
- jsonParseFillInParentage(pParse, 0, 0);
- return SQLITE_OK;
+ return k;
}
/*
-** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
+** Edit the payload size of the element at iRoot by the amount in
+** pParse->delta.
*/
-#define JSON_CACHE_ID (-429938) /* First cache entry */
-#define JSON_CACHE_SZ 4 /* Max number of cache entries */
+static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){
+ u32 sz = 0;
+ u32 nBlob;
+ assert( pParse->delta!=0 );
+ assert( pParse->nBlobAlloc >= pParse->nBlob );
+ nBlob = pParse->nBlob;
+ pParse->nBlob = pParse->nBlobAlloc;
+ (void)jsonbPayloadSize(pParse, iRoot, &sz);
+ pParse->nBlob = nBlob;
+ sz += pParse->delta;
+ pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz);
+}
/*
-** Obtain a complete parse of the JSON found in the first argument
-** of the argv array. Use the sqlite3_get_auxdata() cache for this
-** parse if it is available. If the cache is not available or if it
-** is no longer valid, parse the JSON again and return the new parse,
-** and also register the new parse so that it will be available for
-** future sqlite3_get_auxdata() calls.
+** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of
+** content beginning at iDel, and replacing them with nIns bytes of
+** content given by aIns.
+**
+** nDel may be zero, in which case no bytes are removed. But iDel is
+** still important as new bytes will be insert beginning at iDel.
+**
+** aIns may be zero, in which case space is created to hold nIns bytes
+** beginning at iDel, but that space is uninitialized.
+**
+** Set pParse->oom if an OOM occurs.
*/
-static JsonParse *jsonParseCached(
- sqlite3_context *pCtx,
- sqlite3_value **argv,
- sqlite3_context *pErrCtx
+static void jsonBlobEdit(
+ JsonParse *pParse, /* The JSONB to be modified is in pParse->aBlob */
+ u32 iDel, /* First byte to be removed */
+ u32 nDel, /* Number of bytes to remove */
+ const u8 *aIns, /* Content to insert */
+ u32 nIns /* Bytes of content to insert */
){
- const char *zJson = (const char*)sqlite3_value_text(argv[0]);
- int nJson = sqlite3_value_bytes(argv[0]);
- JsonParse *p;
- JsonParse *pMatch = 0;
- int iKey;
- int iMinKey = 0;
- u32 iMinHold = 0xffffffff;
- u32 iMaxHold = 0;
- if( zJson==0 ) return 0;
- for(iKey=0; iKey<JSON_CACHE_SZ; iKey++){
- p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iKey);
- if( p==0 ){
- iMinKey = iKey;
- break;
+ i64 d = (i64)nIns - (i64)nDel;
+ if( d!=0 ){
+ if( pParse->nBlob + d > pParse->nBlobAlloc ){
+ jsonBlobExpand(pParse, pParse->nBlob+d);
+ if( pParse->oom ) return;
}
- if( pMatch==0
- && p->nJson==nJson
- && memcmp(p->zJson,zJson,nJson)==0
- ){
- p->nErr = 0;
- pMatch = p;
- }else if( p->iHold<iMinHold ){
- iMinHold = p->iHold;
- iMinKey = iKey;
+ memmove(&pParse->aBlob[iDel+nIns],
+ &pParse->aBlob[iDel+nDel],
+ pParse->nBlob - (iDel+nDel));
+ pParse->nBlob += d;
+ pParse->delta += d;
+ }
+ if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns);
+}
+
+/*
+** Return the number of escaped newlines to be ignored.
+** An escaped newline is a one of the following byte sequences:
+**
+** 0x5c 0x0a
+** 0x5c 0x0d
+** 0x5c 0x0d 0x0a
+** 0x5c 0xe2 0x80 0xa8
+** 0x5c 0xe2 0x80 0xa9
+*/
+static u32 jsonBytesToBypass(const char *z, u32 n){
+ u32 i = 0;
+ while( i+1<n ){
+ if( z[i]!='\\' ) return i;
+ if( z[i+1]=='\n' ){
+ i += 2;
+ continue;
}
- if( p->iHold>iMaxHold ){
- iMaxHold = p->iHold;
+ if( z[i+1]=='\r' ){
+ if( i+2<n && z[i+2]=='\n' ){
+ i += 3;
+ }else{
+ i += 2;
+ }
+ continue;
}
+ if( 0xe2==(u8)z[i+1]
+ && i+3<n
+ && 0x80==(u8)z[i+2]
+ && (0xa8==(u8)z[i+3] || 0xa9==(u8)z[i+3])
+ ){
+ i += 4;
+ continue;
+ }
+ break;
}
- if( pMatch ){
- pMatch->nErr = 0;
- pMatch->iHold = iMaxHold+1;
- return pMatch;
+ return i;
+}
+
+/*
+** Input z[0..n] defines JSON escape sequence including the leading '\\'.
+** Decode that escape sequence into a single character. Write that
+** character into *piOut. Return the number of bytes in the escape sequence.
+**
+** If there is a syntax error of some kind (for example too few characters
+** after the '\\' to complete the encoding) then *piOut is set to
+** JSON_INVALID_CHAR.
+*/
+static u32 jsonUnescapeOneChar(const char *z, u32 n, u32 *piOut){
+ assert( n>0 );
+ assert( z[0]=='\\' );
+ if( n<2 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
}
- p = sqlite3_malloc( sizeof(*p) + nJson + 1 );
- if( p==0 ){
- sqlite3_result_error_nomem(pCtx);
- return 0;
+ switch( (u8)z[1] ){
+ case 'u': {
+ u32 v, vlo;
+ if( n<6 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }
+ v = jsonHexToInt4(&z[2]);
+ if( (v & 0xfc00)==0xd800
+ && n>=12
+ && z[6]=='\\'
+ && z[7]=='u'
+ && ((vlo = jsonHexToInt4(&z[8]))&0xfc00)==0xdc00
+ ){
+ *piOut = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
+ return 12;
+ }else{
+ *piOut = v;
+ return 6;
+ }
+ }
+ case 'b': { *piOut = '\b'; return 2; }
+ case 'f': { *piOut = '\f'; return 2; }
+ case 'n': { *piOut = '\n'; return 2; }
+ case 'r': { *piOut = '\r'; return 2; }
+ case 't': { *piOut = '\t'; return 2; }
+ case 'v': { *piOut = '\v'; return 2; }
+ case '0': { *piOut = 0; return 2; }
+ case '\'':
+ case '"':
+ case '/':
+ case '\\':{ *piOut = z[1]; return 2; }
+ case 'x': {
+ if( n<4 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }
+ *piOut = (jsonHexToInt(z[2])<<4) | jsonHexToInt(z[3]);
+ return 4;
+ }
+ case 0xe2:
+ case '\r':
+ case '\n': {
+ u32 nSkip = jsonBytesToBypass(z, n);
+ if( nSkip==0 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }else if( nSkip==n ){
+ *piOut = 0;
+ return n;
+ }else if( z[nSkip]=='\\' ){
+ return nSkip + jsonUnescapeOneChar(&z[nSkip], n-nSkip, piOut);
+ }else{
+ int sz = sqlite3Utf8ReadLimited((u8*)&z[nSkip], n-nSkip, piOut);
+ return nSkip + sz;
+ }
+ }
+ default: {
+ *piOut = JSON_INVALID_CHAR;
+ return 2;
+ }
}
- memset(p, 0, sizeof(*p));
- p->zJson = (char*)&p[1];
- memcpy((char*)p->zJson, zJson, nJson+1);
- if( jsonParse(p, pErrCtx, p->zJson) ){
- sqlite3_free(p);
- return 0;
+}
+
+
+/*
+** Compare two object labels. Return 1 if they are equal and
+** 0 if they differ.
+**
+** In this version, we know that one or the other or both of the
+** two comparands contains an escape sequence.
+*/
+static SQLITE_NOINLINE int jsonLabelCompareEscaped(
+ const char *zLeft, /* The left label */
+ u32 nLeft, /* Size of the left label in bytes */
+ int rawLeft, /* True if zLeft contains no escapes */
+ const char *zRight, /* The right label */
+ u32 nRight, /* Size of the right label in bytes */
+ int rawRight /* True if zRight is escape-free */
+){
+ u32 cLeft, cRight;
+ assert( rawLeft==0 || rawRight==0 );
+ while( 1 /*exit-by-return*/ ){
+ if( nLeft==0 ){
+ cLeft = 0;
+ }else if( rawLeft || zLeft[0]!='\\' ){
+ cLeft = ((u8*)zLeft)[0];
+ if( cLeft>=0xc0 ){
+ int sz = sqlite3Utf8ReadLimited((u8*)zLeft, nLeft, &cLeft);
+ zLeft += sz;
+ nLeft -= sz;
+ }else{
+ zLeft++;
+ nLeft--;
+ }
+ }else{
+ u32 n = jsonUnescapeOneChar(zLeft, nLeft, &cLeft);
+ zLeft += n;
+ assert( n<=nLeft );
+ nLeft -= n;
+ }
+ if( nRight==0 ){
+ cRight = 0;
+ }else if( rawRight || zRight[0]!='\\' ){
+ cRight = ((u8*)zRight)[0];
+ if( cRight>=0xc0 ){
+ int sz = sqlite3Utf8ReadLimited((u8*)zRight, nRight, &cRight);
+ zRight += sz;
+ nRight -= sz;
+ }else{
+ zRight++;
+ nRight--;
+ }
+ }else{
+ u32 n = jsonUnescapeOneChar(zRight, nRight, &cRight);
+ zRight += n;
+ assert( n<=nRight );
+ nRight -= n;
+ }
+ if( cLeft!=cRight ) return 0;
+ if( cLeft==0 ) return 1;
}
- p->nJson = nJson;
- p->iHold = iMaxHold+1;
- sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
- (void(*)(void*))jsonParseFree);
- return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
}
/*
-** Compare the OBJECT label at pNode against zKey,nKey. Return true on
-** a match.
+** Compare two object labels. Return 1 if they are equal and
+** 0 if they differ. Return -1 if an OOM occurs.
*/
-static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){
- if( pNode->jnFlags & JNODE_RAW ){
- if( pNode->n!=nKey ) return 0;
- return strncmp(pNode->u.zJContent, zKey, nKey)==0;
+static int jsonLabelCompare(
+ const char *zLeft, /* The left label */
+ u32 nLeft, /* Size of the left label in bytes */
+ int rawLeft, /* True if zLeft contains no escapes */
+ const char *zRight, /* The right label */
+ u32 nRight, /* Size of the right label in bytes */
+ int rawRight /* True if zRight is escape-free */
+){
+ if( rawLeft && rawRight ){
+ /* Simpliest case: Neither label contains escapes. A simple
+ ** memcmp() is sufficient. */
+ if( nLeft!=nRight ) return 0;
+ return memcmp(zLeft, zRight, nLeft)==0;
}else{
- if( pNode->n!=nKey+2 ) return 0;
- return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
+ return jsonLabelCompareEscaped(zLeft, nLeft, rawLeft,
+ zRight, nRight, rawRight);
}
}
-/* forward declaration */
-static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
+/*
+** Error returns from jsonLookupStep()
+*/
+#define JSON_LOOKUP_ERROR 0xffffffff
+#define JSON_LOOKUP_NOTFOUND 0xfffffffe
+#define JSON_LOOKUP_PATHERROR 0xfffffffd
+#define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR)
+
+/* Forward declaration */
+static u32 jsonLookupStep(JsonParse*,u32,const char*,u32);
+
+
+/* This helper routine for jsonLookupStep() populates pIns with
+** binary data that is to be inserted into pParse.
+**
+** In the common case, pIns just points to pParse->aIns and pParse->nIns.
+** But if the zPath of the original edit operation includes path elements
+** that go deeper, additional substructure must be created.
+**
+** For example:
+**
+** json_insert('{}', '$.a.b.c', 123);
+**
+** The search stops at '$.a' But additional substructure must be
+** created for the ".b.c" part of the patch so that the final result
+** is: {"a":{"b":{"c"::123}}}. This routine populates pIns with
+** the binary equivalent of {"b":{"c":123}} so that it can be inserted.
+**
+** The caller is responsible for resetting pIns when it has finished
+** using the substructure.
+*/
+static u32 jsonCreateEditSubstructure(
+ JsonParse *pParse, /* The original JSONB that is being edited */
+ JsonParse *pIns, /* Populate this with the blob data to insert */
+ const char *zTail /* Tail of the path that determins substructure */
+){
+ static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT };
+ int rc;
+ memset(pIns, 0, sizeof(*pIns));
+ pIns->db = pParse->db;
+ if( zTail[0]==0 ){
+ /* No substructure. Just insert what is given in pParse. */
+ pIns->aBlob = pParse->aIns;
+ pIns->nBlob = pParse->nIns;
+ rc = 0;
+ }else{
+ /* Construct the binary substructure */
+ pIns->nBlob = 1;
+ pIns->aBlob = (u8*)&emptyObject[zTail[0]=='.'];
+ pIns->eEdit = pParse->eEdit;
+ pIns->nIns = pParse->nIns;
+ pIns->aIns = pParse->aIns;
+ rc = jsonLookupStep(pIns, 0, zTail, 0);
+ pParse->oom |= pIns->oom;
+ }
+ return rc; /* Error code only */
+}
/*
-** Search along zPath to find the node specified. Return a pointer
-** to that node, or NULL if zPath is malformed or if there is no such
-** node.
+** Search along zPath to find the Json element specified. Return an
+** index into pParse->aBlob[] for the start of that element's value.
+**
+** If the value found by this routine is the value half of label/value pair
+** within an object, then set pPath->iLabel to the start of the corresponding
+** label, before returning.
+**
+** Return one of the JSON_LOOKUP error codes if problems are seen.
**
-** If pApnd!=0, then try to append new nodes to complete zPath if it is
-** possible to do so and if no existing node corresponds to zPath. If
-** new nodes are appended *pApnd is set to 1.
+** This routine will also modify the blob. If pParse->eEdit is one of
+** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be
+** made to the selected value. If an edit is performed, then the return
+** value does not necessarily point to the select element. If an edit
+** is performed, the return value is only useful for detecting error
+** conditions.
*/
-static JsonNode *jsonLookupStep(
+static u32 jsonLookupStep(
JsonParse *pParse, /* The JSON to search */
- u32 iRoot, /* Begin the search at this node */
+ u32 iRoot, /* Begin the search at this element of aBlob[] */
const char *zPath, /* The path to search */
- int *pApnd, /* Append nodes to complete path if not NULL */
- const char **pzErr /* Make *pzErr point to any syntax error in zPath */
+ u32 iLabel /* Label if iRoot is a value of in an object */
){
- u32 i, j, nKey;
+ u32 i, j, k, nKey, sz, n, iEnd, rc;
const char *zKey;
- JsonNode *pRoot = &pParse->aNode[iRoot];
- if( zPath[0]==0 ) return pRoot;
+ u8 x;
+
+ if( zPath[0]==0 ){
+ if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ sz += n;
+ if( pParse->eEdit==JEDIT_DEL ){
+ if( iLabel>0 ){
+ sz += iRoot - iLabel;
+ iRoot = iLabel;
+ }
+ jsonBlobEdit(pParse, iRoot, sz, 0, 0);
+ }else if( pParse->eEdit==JEDIT_INS ){
+ /* Already exists, so json_insert() is a no-op */
+ }else{
+ /* json_set() or json_replace() */
+ jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns);
+ }
+ }
+ pParse->iLabel = iLabel;
+ return iRoot;
+ }
if( zPath[0]=='.' ){
- if( pRoot->eType!=JSON_OBJECT ) return 0;
+ int rawKey = 1;
+ x = pParse->aBlob[iRoot];
zPath++;
if( zPath[0]=='"' ){
zKey = zPath + 1;
@@ -177674,260 +205891,849 @@ static JsonNode *jsonLookupStep(
if( zPath[i] ){
i++;
}else{
- *pzErr = zPath;
- return 0;
+ return JSON_LOOKUP_PATHERROR;
}
+ testcase( nKey==0 );
+ rawKey = memchr(zKey, '\\', nKey)==0;
}else{
zKey = zPath;
for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
nKey = i;
+ if( nKey==0 ){
+ return JSON_LOOKUP_PATHERROR;
+ }
+ }
+ if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_LOOKUP_NOTFOUND;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ j = iRoot + n; /* j is the index of a label */
+ iEnd = j+sz;
+ while( j<iEnd ){
+ int rawLabel;
+ const char *zLabel;
+ x = pParse->aBlob[j] & 0x0f;
+ if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ k = j+n; /* k is the index of the label text */
+ if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR;
+ zLabel = (const char*)&pParse->aBlob[k];
+ rawLabel = x==JSONB_TEXT || x==JSONB_TEXTRAW;
+ if( jsonLabelCompare(zKey, nKey, rawKey, zLabel, sz, rawLabel) ){
+ u32 v = k+sz; /* v is the index of the value */
+ if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, v, &sz);
+ if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR;
+ assert( j>0 );
+ rc = jsonLookupStep(pParse, v, &zPath[i], j);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
+ }
+ j = k+sz;
+ if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ j += n+sz;
+ }
+ if( j>iEnd ) return JSON_LOOKUP_ERROR;
+ if( pParse->eEdit>=JEDIT_INS ){
+ u32 nIns; /* Total bytes to insert (label+value) */
+ JsonParse v; /* BLOB encoding of the value to be inserted */
+ JsonParse ix; /* Header of the label to be inserted */
+ testcase( pParse->eEdit==JEDIT_INS );
+ testcase( pParse->eEdit==JEDIT_SET );
+ memset(&ix, 0, sizeof(ix));
+ ix.db = pParse->db;
+ jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0);
+ pParse->oom |= ix.oom;
+ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]);
+ if( !JSON_LOOKUP_ISERROR(rc)
+ && jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob)
+ ){
+ assert( !pParse->oom );
+ nIns = ix.nBlob + nKey + v.nBlob;
+ jsonBlobEdit(pParse, j, 0, 0, nIns);
+ if( !pParse->oom ){
+ assert( pParse->aBlob!=0 ); /* Because pParse->oom!=0 */
+ assert( ix.aBlob!=0 ); /* Because pPasre->oom!=0 */
+ memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob);
+ k = j + ix.nBlob;
+ memcpy(&pParse->aBlob[k], zKey, nKey);
+ k += nKey;
+ memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob);
+ if( ALWAYS(pParse->delta) ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ }
+ }
+ jsonParseReset(&v);
+ jsonParseReset(&ix);
+ return rc;
}
- if( nKey==0 ){
- *pzErr = zPath;
- return 0;
+ }else if( zPath[0]=='[' ){
+ x = pParse->aBlob[iRoot] & 0x0f;
+ if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ k = 0;
+ i = 1;
+ while( sqlite3Isdigit(zPath[i]) ){
+ k = k*10 + zPath[i] - '0';
+ i++;
}
- j = 1;
- for(;;){
- while( j<=pRoot->n ){
- if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
- return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
+ if( i<2 || zPath[i]!=']' ){
+ if( zPath[1]=='#' ){
+ k = jsonbArrayCount(pParse, iRoot);
+ i = 2;
+ if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){
+ unsigned int nn = 0;
+ i = 3;
+ do{
+ nn = nn*10 + zPath[i] - '0';
+ i++;
+ }while( sqlite3Isdigit(zPath[i]) );
+ if( nn>k ) return JSON_LOOKUP_NOTFOUND;
+ k -= nn;
}
- j++;
- j += jsonNodeSize(&pRoot[j]);
+ if( zPath[i]!=']' ){
+ return JSON_LOOKUP_PATHERROR;
+ }
+ }else{
+ return JSON_LOOKUP_PATHERROR;
}
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
- iRoot += pRoot->u.iAppend;
- pRoot = &pParse->aNode[iRoot];
- j = 1;
}
- if( pApnd ){
- u32 iStart, iLabel;
- JsonNode *pNode;
- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
- iLabel = jsonParseAddNode(pParse, JSON_STRING, i, zPath);
- zPath += i;
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
- if( pParse->oom ) return 0;
- if( pNode ){
- pRoot = &pParse->aNode[iRoot];
- pRoot->u.iAppend = iStart - iRoot;
- pRoot->jnFlags |= JNODE_APPEND;
- pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
- }
- return pNode;
- }
- }else if( zPath[0]=='[' && safe_isdigit(zPath[1]) ){
- if( pRoot->eType!=JSON_ARRAY ) return 0;
- i = 0;
- j = 1;
- while( safe_isdigit(zPath[j]) ){
- i = i*10 + zPath[j] - '0';
- j++;
+ j = iRoot+n;
+ iEnd = j+sz;
+ while( j<iEnd ){
+ if( k==0 ){
+ rc = jsonLookupStep(pParse, j, &zPath[i+1], 0);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
+ }
+ k--;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ j += n+sz;
+ }
+ if( j>iEnd ) return JSON_LOOKUP_ERROR;
+ if( k>0 ) return JSON_LOOKUP_NOTFOUND;
+ if( pParse->eEdit>=JEDIT_INS ){
+ JsonParse v;
+ testcase( pParse->eEdit==JEDIT_INS );
+ testcase( pParse->eEdit==JEDIT_SET );
+ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]);
+ if( !JSON_LOOKUP_ISERROR(rc)
+ && jsonBlobMakeEditable(pParse, v.nBlob)
+ ){
+ assert( !pParse->oom );
+ jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob);
+ }
+ jsonParseReset(&v);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
+ }
+ }else{
+ return JSON_LOOKUP_PATHERROR;
+ }
+ return JSON_LOOKUP_NOTFOUND;
+}
+
+/*
+** Convert a JSON BLOB into text and make that text the return value
+** of an SQL function.
+*/
+static void jsonReturnTextJsonFromBlob(
+ sqlite3_context *ctx,
+ const u8 *aBlob,
+ u32 nBlob
+){
+ JsonParse x;
+ JsonString s;
+
+ if( NEVER(aBlob==0) ) return;
+ memset(&x, 0, sizeof(x));
+ x.aBlob = (u8*)aBlob;
+ x.nBlob = nBlob;
+ jsonStringInit(&s, ctx);
+ jsonTranslateBlobToText(&x, 0, &s);
+ jsonReturnString(&s, 0, 0);
+}
+
+
+/*
+** Return the value of the BLOB node at index i.
+**
+** If the value is a primitive, return it as an SQL value.
+** If the value is an array or object, return it as either
+** JSON text or the BLOB encoding, depending on the JSON_B flag
+** on the userdata.
+*/
+static void jsonReturnFromBlob(
+ JsonParse *pParse, /* Complete JSON parse tree */
+ u32 i, /* Index of the node */
+ sqlite3_context *pCtx, /* Return value for this function */
+ int textOnly /* return text JSON. Disregard user-data */
+){
+ u32 n, sz;
+ int rc;
+ sqlite3 *db = sqlite3_context_db_handle(pCtx);
+
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( n==0 ){
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
+ return;
+ }
+ switch( pParse->aBlob[i] & 0x0f ){
+ case JSONB_NULL: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_null(pCtx);
+ break;
}
- if( zPath[j]!=']' ){
- *pzErr = zPath;
+ case JSONB_TRUE: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_int(pCtx, 1);
+ break;
+ }
+ case JSONB_FALSE: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_int(pCtx, 0);
+ break;
+ }
+ case JSONB_INT5:
+ case JSONB_INT: {
+ sqlite3_int64 iRes = 0;
+ char *z;
+ int bNeg = 0;
+ char x;
+ if( sz==0 ) goto returnfromblob_malformed;
+ x = (char)pParse->aBlob[i+n];
+ if( x=='-' ){
+ if( sz<2 ) goto returnfromblob_malformed;
+ n++;
+ sz--;
+ bNeg = 1;
+ }
+ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
+ if( z==0 ) goto returnfromblob_oom;
+ rc = sqlite3DecOrHexToI64(z, &iRes);
+ sqlite3DbFree(db, z);
+ if( rc==0 ){
+ sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
+ }else if( rc==3 && bNeg ){
+ sqlite3_result_int64(pCtx, SMALLEST_INT64);
+ }else if( rc==1 ){
+ goto returnfromblob_malformed;
+ }else{
+ if( bNeg ){ n--; sz++; }
+ goto to_double;
+ }
+ break;
+ }
+ case JSONB_FLOAT5:
+ case JSONB_FLOAT: {
+ double r;
+ char *z;
+ if( sz==0 ) goto returnfromblob_malformed;
+ to_double:
+ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
+ if( z==0 ) goto returnfromblob_oom;
+ rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
+ sqlite3DbFree(db, z);
+ if( rc<=0 ) goto returnfromblob_malformed;
+ sqlite3_result_double(pCtx, r);
+ break;
+ }
+ case JSONB_TEXTRAW:
+ case JSONB_TEXT: {
+ sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz,
+ SQLITE_TRANSIENT);
+ break;
+ }
+ case JSONB_TEXT5:
+ case JSONB_TEXTJ: {
+ /* Translate JSON formatted string into raw text */
+ u32 iIn, iOut;
+ const char *z;
+ char *zOut;
+ u32 nOut = sz;
+ z = (const char*)&pParse->aBlob[i+n];
+ zOut = sqlite3DbMallocRaw(db, nOut+1);
+ if( zOut==0 ) goto returnfromblob_oom;
+ for(iIn=iOut=0; iIn<sz; iIn++){
+ char c = z[iIn];
+ if( c=='\\' ){
+ u32 v;
+ u32 szEscape = jsonUnescapeOneChar(&z[iIn], sz-iIn, &v);
+ if( v<=0x7f ){
+ zOut[iOut++] = (char)v;
+ }else if( v<=0x7ff ){
+ assert( szEscape>=2 );
+ zOut[iOut++] = (char)(0xc0 | (v>>6));
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }else if( v<0x10000 ){
+ assert( szEscape>=3 );
+ zOut[iOut++] = 0xe0 | (v>>12);
+ zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }else if( v==JSON_INVALID_CHAR ){
+ /* Silently ignore illegal unicode */
+ }else{
+ assert( szEscape>=4 );
+ zOut[iOut++] = 0xf0 | (v>>18);
+ zOut[iOut++] = 0x80 | ((v>>12)&0x3f);
+ zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }
+ iIn += szEscape - 1;
+ }else{
+ zOut[iOut++] = c;
+ }
+ } /* end for() */
+ assert( iOut<=nOut );
+ zOut[iOut] = 0;
+ sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC);
+ break;
+ }
+ case JSONB_ARRAY:
+ case JSONB_OBJECT: {
+ int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx));
+ if( flags & JSON_BLOB ){
+ sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT);
+ }else{
+ jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n);
+ }
+ break;
+ }
+ default: {
+ goto returnfromblob_malformed;
+ }
+ }
+ return;
+
+returnfromblob_oom:
+ sqlite3_result_error_nomem(pCtx);
+ return;
+
+returnfromblob_malformed:
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
+ return;
+}
+
+/*
+** pArg is a function argument that might be an SQL value or a JSON
+** value. Figure out what it is and encode it as a JSONB blob.
+** Return the results in pParse.
+**
+** pParse is uninitialized upon entry. This routine will handle the
+** initialization of pParse. The result will be contained in
+** pParse->aBlob and pParse->nBlob. pParse->aBlob might be dynamically
+** allocated (if pParse->nBlobAlloc is greater than zero) in which case
+** the caller is responsible for freeing the space allocated to pParse->aBlob
+** when it has finished with it. Or pParse->aBlob might be a static string
+** or a value obtained from sqlite3_value_blob(pArg).
+**
+** If the argument is a BLOB that is clearly not a JSONB, then this
+** function might set an error message in ctx and return non-zero.
+** It might also set an error message and return non-zero on an OOM error.
+*/
+static int jsonFunctionArgToBlob(
+ sqlite3_context *ctx,
+ sqlite3_value *pArg,
+ JsonParse *pParse
+){
+ int eType = sqlite3_value_type(pArg);
+ static u8 aNull[] = { 0x00 };
+ memset(pParse, 0, sizeof(pParse[0]));
+ pParse->db = sqlite3_context_db_handle(ctx);
+ switch( eType ){
+ default: {
+ pParse->aBlob = aNull;
+ pParse->nBlob = 1;
return 0;
}
- zPath += j + 1;
- j = 1;
- for(;;){
- while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){
- if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--;
- j += jsonNodeSize(&pRoot[j]);
+ case SQLITE_BLOB: {
+ if( jsonFuncArgMightBeBinary(pArg) ){
+ pParse->aBlob = (u8*)sqlite3_value_blob(pArg);
+ pParse->nBlob = sqlite3_value_bytes(pArg);
+ }else{
+ sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1);
+ return 1;
}
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
- iRoot += pRoot->u.iAppend;
- pRoot = &pParse->aNode[iRoot];
- j = 1;
+ break;
}
- if( j<=pRoot->n ){
- return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
+ case SQLITE_TEXT: {
+ const char *zJson = (const char*)sqlite3_value_text(pArg);
+ int nJson = sqlite3_value_bytes(pArg);
+ if( zJson==0 ) return 1;
+ if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){
+ pParse->zJson = (char*)zJson;
+ pParse->nJson = nJson;
+ if( jsonConvertTextToBlob(pParse, ctx) ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ sqlite3DbFree(pParse->db, pParse->aBlob);
+ memset(pParse, 0, sizeof(pParse[0]));
+ return 1;
+ }
+ }else{
+ jsonBlobAppendNode(pParse, JSONB_TEXTRAW, nJson, zJson);
+ }
+ break;
}
- if( i==0 && pApnd ){
- u32 iStart;
- JsonNode *pNode;
- iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
- if( pParse->oom ) return 0;
- if( pNode ){
- pRoot = &pParse->aNode[iRoot];
- pRoot->u.iAppend = iStart - iRoot;
- pRoot->jnFlags |= JNODE_APPEND;
+ case SQLITE_FLOAT: {
+ double r = sqlite3_value_double(pArg);
+ if( NEVER(sqlite3IsNaN(r)) ){
+ jsonBlobAppendNode(pParse, JSONB_NULL, 0, 0);
+ }else{
+ int n = sqlite3_value_bytes(pArg);
+ const char *z = (const char*)sqlite3_value_text(pArg);
+ if( z==0 ) return 1;
+ if( z[0]=='I' ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
+ }else if( z[0]=='-' && z[1]=='I' ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
+ }else{
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, n, z);
+ }
}
- return pNode;
+ break;
+ }
+ case SQLITE_INTEGER: {
+ int n = sqlite3_value_bytes(pArg);
+ const char *z = (const char*)sqlite3_value_text(pArg);
+ if( z==0 ) return 1;
+ jsonBlobAppendNode(pParse, JSONB_INT, n, z);
+ break;
}
+ }
+ if( pParse->oom ){
+ sqlite3_result_error_nomem(ctx);
+ return 1;
}else{
- *pzErr = zPath;
+ return 0;
}
- return 0;
}
/*
-** Append content to pParse that will complete zPath. Return a pointer
-** to the inserted node, or return NULL if the append fails.
+** Generate a bad path error.
+**
+** If ctx is not NULL then push the error message into ctx and return NULL.
+** If ctx is NULL, then return the text of the error message.
*/
-static JsonNode *jsonLookupAppend(
- JsonParse *pParse, /* Append content to the JSON parse */
- const char *zPath, /* Description of content to append */
- int *pApnd, /* Set this flag to 1 */
- const char **pzErr /* Make this point to any syntax error */
+static char *jsonBadPathError(
+ sqlite3_context *ctx, /* The function call containing the error */
+ const char *zPath /* The path with the problem */
){
- *pApnd = 1;
- if( zPath[0]==0 ){
- jsonParseAddNode(pParse, JSON_NULL, 0, 0);
- return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
+ char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath);
+ if( ctx==0 ) return zMsg;
+ if( zMsg ){
+ sqlite3_result_error(ctx, zMsg, -1);
+ sqlite3_free(zMsg);
+ }else{
+ sqlite3_result_error_nomem(ctx);
}
- if( zPath[0]=='.' ){
- jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- }else if( strncmp(zPath,"[0]",3)==0 ){
- jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
+ return 0;
+}
+
+/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent
+** arguments come in parse where each pair contains a JSON path and
+** content to insert or set at that patch. Do the updates
+** and return the result.
+**
+** The specific operation is determined by eEdit, which can be one
+** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET.
+*/
+static void jsonInsertIntoBlob(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv,
+ int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */
+){
+ int i;
+ u32 rc = 0;
+ const char *zPath = 0;
+ int flgs;
+ JsonParse *p;
+ JsonParse ax;
+
+ assert( (argc&1)==1 );
+ flgs = argc==1 ? 0 : JSON_EDITABLE;
+ p = jsonParseFuncArg(ctx, argv[0], flgs);
+ if( p==0 ) return;
+ for(i=1; i<argc-1; i+=2){
+ if( sqlite3_value_type(argv[i])==SQLITE_NULL ) continue;
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ if( zPath==0 ){
+ sqlite3_result_error_nomem(ctx);
+ jsonParseFree(p);
+ return;
+ }
+ if( zPath[0]!='$' ) goto jsonInsertIntoBlob_patherror;
+ if( jsonFunctionArgToBlob(ctx, argv[i+1], &ax) ){
+ jsonParseReset(&ax);
+ jsonParseFree(p);
+ return;
+ }
+ if( zPath[1]==0 ){
+ if( eEdit==JEDIT_REPL || eEdit==JEDIT_SET ){
+ jsonBlobEdit(p, 0, p->nBlob, ax.aBlob, ax.nBlob);
+ }
+ rc = 0;
+ }else{
+ p->eEdit = eEdit;
+ p->nIns = ax.nBlob;
+ p->aIns = ax.aBlob;
+ p->delta = 0;
+ rc = jsonLookupStep(p, 0, zPath+1, 0);
+ }
+ jsonParseReset(&ax);
+ if( rc==JSON_LOOKUP_NOTFOUND ) continue;
+ if( JSON_LOOKUP_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror;
+ }
+ jsonReturnParse(ctx, p);
+ jsonParseFree(p);
+ return;
+
+jsonInsertIntoBlob_patherror:
+ jsonParseFree(p);
+ if( rc==JSON_LOOKUP_ERROR ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
}else{
- return 0;
+ jsonBadPathError(ctx, zPath);
}
- if( pParse->oom ) return 0;
- return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
+ return;
}
/*
-** Return the text of a syntax error message on a JSON path. Space is
-** obtained from sqlite3_malloc().
+** If pArg is a blob that seems like a JSONB blob, then initialize
+** p to point to that JSONB and return TRUE. If pArg does not seem like
+** a JSONB blob, then return FALSE;
+**
+** This routine is only called if it is already known that pArg is a
+** blob. The only open question is whether or not the blob appears
+** to be a JSONB blob.
*/
-static char *jsonPathSyntaxError(const char *zErr){
- return sqlite3_mprintf("JSON path error near '%q'", zErr);
+static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
+ u32 n, sz = 0;
+ p->aBlob = (u8*)sqlite3_value_blob(pArg);
+ p->nBlob = (u32)sqlite3_value_bytes(pArg);
+ if( p->nBlob==0 ){
+ p->aBlob = 0;
+ return 0;
+ }
+ if( NEVER(p->aBlob==0) ){
+ return 0;
+ }
+ if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
+ && (n = jsonbPayloadSize(p, 0, &sz))>0
+ && sz+n==p->nBlob
+ && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
+ ){
+ return 1;
+ }
+ p->aBlob = 0;
+ p->nBlob = 0;
+ return 0;
}
/*
-** Do a node lookup using zPath. Return a pointer to the node on success.
-** Return NULL if not found or if there is an error.
+** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob,
+** from the SQL function argument pArg. Return a pointer to the new
+** JsonParse object.
**
-** On an error, write an error message into pCtx and increment the
-** pParse->nErr counter.
+** Ownership of the new JsonParse object is passed to the caller. The
+** caller should invoke jsonParseFree() on the return value when it
+** has finished using it.
**
-** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
-** nodes are appended.
+** If any errors are detected, an appropriate error messages is set
+** using sqlite3_result_error() or the equivalent and this routine
+** returns NULL. This routine also returns NULL if the pArg argument
+** is an SQL NULL value, but no error message is set in that case. This
+** is so that SQL functions that are given NULL arguments will return
+** a NULL value.
*/
-static JsonNode *jsonLookup(
- JsonParse *pParse, /* The JSON to search */
- const char *zPath, /* The path to search */
- int *pApnd, /* Append nodes to complete path if not NULL */
- sqlite3_context *pCtx /* Report errors here, if not NULL */
+static JsonParse *jsonParseFuncArg(
+ sqlite3_context *ctx,
+ sqlite3_value *pArg,
+ u32 flgs
){
- const char *zErr = 0;
- JsonNode *pNode = 0;
- char *zMsg;
+ int eType; /* Datatype of pArg */
+ JsonParse *p = 0; /* Value to be returned */
+ JsonParse *pFromCache = 0; /* Value taken from cache */
+ sqlite3 *db; /* The database connection */
- if( zPath==0 ) return 0;
- if( zPath[0]!='$' ){
- zErr = zPath;
- goto lookup_err;
+ assert( ctx!=0 );
+ eType = sqlite3_value_type(pArg);
+ if( eType==SQLITE_NULL ){
+ return 0;
+ }
+ pFromCache = jsonCacheSearch(ctx, pArg);
+ if( pFromCache ){
+ pFromCache->nJPRef++;
+ if( (flgs & JSON_EDITABLE)==0 ){
+ return pFromCache;
+ }
}
- zPath++;
- pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
- if( zErr==0 ) return pNode;
+ db = sqlite3_context_db_handle(ctx);
+rebuild_from_cache:
+ p = sqlite3DbMallocZero(db, sizeof(*p));
+ if( p==0 ) goto json_pfa_oom;
+ memset(p, 0, sizeof(*p));
+ p->db = db;
+ p->nJPRef = 1;
+ if( pFromCache!=0 ){
+ u32 nBlob = pFromCache->nBlob;
+ p->aBlob = sqlite3DbMallocRaw(db, nBlob);
+ if( p->aBlob==0 ) goto json_pfa_oom;
+ memcpy(p->aBlob, pFromCache->aBlob, nBlob);
+ p->nBlobAlloc = p->nBlob = nBlob;
+ p->hasNonstd = pFromCache->hasNonstd;
+ jsonParseFree(pFromCache);
+ return p;
+ }
+ if( eType==SQLITE_BLOB ){
+ if( jsonArgIsJsonb(pArg,p) ){
+ if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){
+ goto json_pfa_oom;
+ }
+ return p;
+ }
+ /* If the blob is not valid JSONB, fall through into trying to cast
+ ** the blob into text which is then interpreted as JSON. (tag-20240123-a)
+ **
+ ** This goes against all historical documentation about how the SQLite
+ ** JSON functions were suppose to work. From the beginning, blob was
+ ** reserved for expansion and a blob value should have raised an error.
+ ** But it did not, due to a bug. And many applications came to depend
+ ** upon this buggy behavior, espeically when using the CLI and reading
+ ** JSON text using readfile(), which returns a blob. For this reason
+ ** we will continue to support the bug moving forward.
+ ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
+ */
+ }
+ p->zJson = (char*)sqlite3_value_text(pArg);
+ p->nJson = sqlite3_value_bytes(pArg);
+ if( p->nJson==0 ) goto json_pfa_malformed;
+ if( NEVER(p->zJson==0) ) goto json_pfa_oom;
+ if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){
+ if( flgs & JSON_KEEPERROR ){
+ p->nErr = 1;
+ return p;
+ }else{
+ jsonParseFree(p);
+ return 0;
+ }
+ }else{
+ int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref);
+ int rc;
+ if( !isRCStr ){
+ char *zNew = sqlite3RCStrNew( p->nJson );
+ if( zNew==0 ) goto json_pfa_oom;
+ memcpy(zNew, p->zJson, p->nJson);
+ p->zJson = zNew;
+ p->zJson[p->nJson] = 0;
+ }else{
+ sqlite3RCStrRef(p->zJson);
+ }
+ p->bJsonIsRCStr = 1;
+ rc = jsonCacheInsert(ctx, p);
+ if( rc==SQLITE_NOMEM ) goto json_pfa_oom;
+ if( flgs & JSON_EDITABLE ){
+ pFromCache = p;
+ p = 0;
+ goto rebuild_from_cache;
+ }
+ }
+ return p;
-lookup_err:
- pParse->nErr++;
- assert( zErr!=0 && pCtx!=0 );
- zMsg = jsonPathSyntaxError(zErr);
- if( zMsg ){
- sqlite3_result_error(pCtx, zMsg, -1);
- sqlite3_free(zMsg);
+json_pfa_malformed:
+ if( flgs & JSON_KEEPERROR ){
+ p->nErr = 1;
+ return p;
}else{
- sqlite3_result_error_nomem(pCtx);
+ jsonParseFree(p);
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ return 0;
}
+
+json_pfa_oom:
+ jsonParseFree(pFromCache);
+ jsonParseFree(p);
+ sqlite3_result_error_nomem(ctx);
return 0;
}
-
/*
-** Report the wrong number of arguments for json_insert(), json_replace()
-** or json_set().
+** Make the return value of a JSON function either the raw JSONB blob
+** or make it JSON text, depending on whether the JSON_BLOB flag is
+** set on the function.
*/
-static void jsonWrongNumArgs(
- sqlite3_context *pCtx,
- const char *zFuncName
+static void jsonReturnParse(
+ sqlite3_context *ctx,
+ JsonParse *p
){
- char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
- zFuncName);
- sqlite3_result_error(pCtx, zMsg, -1);
- sqlite3_free(zMsg);
-}
-
-/*
-** Mark all NULL entries in the Object passed in as JNODE_REMOVE.
-*/
-static void jsonRemoveAllNulls(JsonNode *pNode){
- int i, n;
- assert( pNode->eType==JSON_OBJECT );
- n = pNode->n;
- for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){
- switch( pNode[i].eType ){
- case JSON_NULL:
- pNode[i].jnFlags |= JNODE_REMOVE;
- break;
- case JSON_OBJECT:
- jsonRemoveAllNulls(&pNode[i]);
- break;
+ int flgs;
+ if( p->oom ){
+ sqlite3_result_error_nomem(ctx);
+ return;
+ }
+ flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( flgs & JSON_BLOB ){
+ if( p->nBlobAlloc>0 && !p->bReadOnly ){
+ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_DYNAMIC);
+ p->nBlobAlloc = 0;
+ }else{
+ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_TRANSIENT);
}
+ }else{
+ JsonString s;
+ jsonStringInit(&s, ctx);
+ p->delta = 0;
+ jsonTranslateBlobToText(p, 0, &s);
+ jsonReturnString(&s, p, ctx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
}
-
/****************************************************************************
** SQL functions used for testing and debugging
****************************************************************************/
-#ifdef SQLITE_DEBUG
+#if SQLITE_DEBUG
/*
-** The json_parse(JSON) function returns a string which describes
-** a parse of the JSON provided. Or it returns NULL if JSON is not
-** well-formed.
-*/
-static void jsonParseFunc(
- sqlite3_context *ctx,
- int argc,
- sqlite3_value **argv
-){
- JsonString s; /* Output string - not real JSON */
- JsonParse x; /* The parse */
- u32 i;
-
- assert( argc==1 );
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
- jsonParseFindParents(&x);
- jsonInit(&s, ctx);
- for(i=0; i<x.nNode; i++){
- const char *zType;
- if( x.aNode[i].jnFlags & JNODE_LABEL ){
- assert( x.aNode[i].eType==JSON_STRING );
- zType = "label";
- }else{
- zType = jsonType[x.aNode[i].eType];
+** Decode JSONB bytes in aBlob[] starting at iStart through but not
+** including iEnd. Indent the
+** content by nIndent spaces.
+*/
+static void jsonDebugPrintBlob(
+ JsonParse *pParse, /* JSON content */
+ u32 iStart, /* Start rendering here */
+ u32 iEnd, /* Do not render this byte or any byte after this one */
+ int nIndent, /* Indent by this many spaces */
+ sqlite3_str *pOut /* Generate output into this sqlite3_str object */
+){
+ while( iStart<iEnd ){
+ u32 i, n, nn, sz = 0;
+ int showContent = 1;
+ u8 x = pParse->aBlob[iStart] & 0x0f;
+ u32 savedNBlob = pParse->nBlob;
+ sqlite3_str_appendf(pOut, "%5d:%*s", iStart, nIndent, "");
+ if( pParse->nBlobAlloc>pParse->nBlob ){
+ pParse->nBlob = pParse->nBlobAlloc;
+ }
+ nn = n = jsonbPayloadSize(pParse, iStart, &sz);
+ if( nn==0 ) nn = 1;
+ if( sz>0 && x<JSONB_ARRAY ){
+ nn += sz;
+ }
+ for(i=0; i<nn; i++){
+ sqlite3_str_appendf(pOut, " %02x", pParse->aBlob[iStart+i]);
+ }
+ if( n==0 ){
+ sqlite3_str_appendf(pOut, " ERROR invalid node size\n");
+ iStart = n==0 ? iStart+1 : iEnd;
+ continue;
+ }
+ pParse->nBlob = savedNBlob;
+ if( iStart+n+sz>iEnd ){
+ iEnd = iStart+n+sz;
+ if( iEnd>pParse->nBlob ){
+ if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){
+ iEnd = pParse->nBlobAlloc;
+ }else{
+ iEnd = pParse->nBlob;
+ }
+ }
+ }
+ sqlite3_str_appendall(pOut," <-- ");
+ switch( x ){
+ case JSONB_NULL: sqlite3_str_appendall(pOut,"null"); break;
+ case JSONB_TRUE: sqlite3_str_appendall(pOut,"true"); break;
+ case JSONB_FALSE: sqlite3_str_appendall(pOut,"false"); break;
+ case JSONB_INT: sqlite3_str_appendall(pOut,"int"); break;
+ case JSONB_INT5: sqlite3_str_appendall(pOut,"int5"); break;
+ case JSONB_FLOAT: sqlite3_str_appendall(pOut,"float"); break;
+ case JSONB_FLOAT5: sqlite3_str_appendall(pOut,"float5"); break;
+ case JSONB_TEXT: sqlite3_str_appendall(pOut,"text"); break;
+ case JSONB_TEXTJ: sqlite3_str_appendall(pOut,"textj"); break;
+ case JSONB_TEXT5: sqlite3_str_appendall(pOut,"text5"); break;
+ case JSONB_TEXTRAW: sqlite3_str_appendall(pOut,"textraw"); break;
+ case JSONB_ARRAY: {
+ sqlite3_str_appendf(pOut,"array, %u bytes\n", sz);
+ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
+ showContent = 0;
+ break;
+ }
+ case JSONB_OBJECT: {
+ sqlite3_str_appendf(pOut, "object, %u bytes\n", sz);
+ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
+ showContent = 0;
+ break;
+ }
+ default: {
+ sqlite3_str_appendall(pOut, "ERROR: unknown node type\n");
+ showContent = 0;
+ break;
+ }
}
- jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d",
- i, zType, x.aNode[i].n, x.aUp[i]);
- if( x.aNode[i].u.zJContent!=0 ){
- jsonAppendRaw(&s, " ", 1);
- jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n);
+ if( showContent ){
+ if( sz==0 && x<=JSONB_FALSE ){
+ sqlite3_str_append(pOut, "\n", 1);
+ }else{
+ u32 i;
+ sqlite3_str_appendall(pOut, ": \"");
+ for(i=iStart+n; i<iStart+n+sz; i++){
+ u8 c = pParse->aBlob[i];
+ if( c<0x20 || c>=0x7f ) c = '.';
+ sqlite3_str_append(pOut, (char*)&c, 1);
+ }
+ sqlite3_str_append(pOut, "\"\n", 2);
+ }
}
- jsonAppendRaw(&s, "\n", 1);
+ iStart += n + sz;
}
- jsonParseReset(&x);
- jsonResult(&s);
}
+static void jsonShowParse(JsonParse *pParse){
+ sqlite3_str out;
+ char zBuf[1000];
+ if( pParse==0 ){
+ printf("NULL pointer\n");
+ return;
+ }else{
+ printf("nBlobAlloc = %u\n", pParse->nBlobAlloc);
+ printf("nBlob = %u\n", pParse->nBlob);
+ printf("delta = %d\n", pParse->delta);
+ if( pParse->nBlob==0 ) return;
+ printf("content (bytes 0..%u):\n", pParse->nBlob-1);
+ }
+ sqlite3StrAccumInit(&out, 0, zBuf, sizeof(zBuf), 1000000);
+ jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0, &out);
+ printf("%s", sqlite3_str_value(&out));
+ sqlite3_str_reset(&out);
+}
+#endif /* SQLITE_DEBUG */
+#ifdef SQLITE_DEBUG
/*
-** The json_test1(JSON) function return true (1) if the input is JSON
-** text generated by another json function. It returns (0) if the input
-** is not known to be JSON.
+** SQL function: json_parse(JSON)
+**
+** Parse JSON using jsonParseFuncArg(). Return text that is a
+** human-readable dump of the binary JSONB for the input parameter.
*/
-static void jsonTest1Func(
+static void jsonParseFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
- UNUSED_PARAM(argc);
- sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
+ JsonParse *p; /* The parse */
+ sqlite3_str out;
+
+ assert( argc>=1 );
+ sqlite3StrAccumInit(&out, 0, 0, 0, 1000000);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
+ if( p==0 ) return;
+ if( argc==1 ){
+ jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out);
+ sqlite3_result_text64(ctx, out.zText, out.nChar, SQLITE_DYNAMIC, SQLITE_UTF8);
+ }else{
+ jsonShowParse(p);
+ }
+ jsonParseFree(p);
}
#endif /* SQLITE_DEBUG */
@@ -177936,8 +206742,8 @@ static void jsonTest1Func(
****************************************************************************/
/*
-** Implementation of the json_QUOTE(VALUE) function. Return a JSON value
-** corresponding to the SQL value input. Mostly this means putting
+** Implementation of the json_quote(VALUE) function. Return a JSON value
+** corresponding to the SQL value input. Mostly this means putting
** double-quotes around strings and returning the unquoted string "null"
** when given a NULL input.
*/
@@ -177947,11 +206753,11 @@ static void jsonQuoteFunc(
sqlite3_value **argv
){
JsonString jx;
- UNUSED_PARAM(argc);
+ UNUSED_PARAMETER(argc);
- jsonInit(&jx, ctx);
- jsonAppendValue(&jx, argv[0]);
- jsonResult(&jx);
+ jsonStringInit(&jx, ctx);
+ jsonAppendSqlValue(&jx, argv[0]);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
@@ -177968,23 +206774,22 @@ static void jsonArrayFunc(
int i;
JsonString jx;
- jsonInit(&jx, ctx);
+ jsonStringInit(&jx, ctx);
jsonAppendChar(&jx, '[');
for(i=0; i<argc; i++){
jsonAppendSeparator(&jx);
- jsonAppendValue(&jx, argv[i]);
+ jsonAppendSqlValue(&jx, argv[i]);
}
jsonAppendChar(&jx, ']');
- jsonResult(&jx);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
-
/*
** json_array_length(JSON)
** json_array_length(JSON, PATH)
**
-** Return the number of elements in the top-level JSON array.
+** Return the number of elements in the top-level JSON array.
** Return 0 if the input is not a well-formed JSON array.
*/
static void jsonArrayLengthFunc(
@@ -177993,145 +206798,380 @@ static void jsonArrayLengthFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- sqlite3_int64 n = 0;
+ sqlite3_int64 cnt = 0;
u32 i;
- JsonNode *pNode;
+ u8 eErr = 0;
- p = jsonParseCached(ctx, argv, ctx);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
- assert( p->nNode );
if( argc==2 ){
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(p, zPath, 0, ctx);
+ if( zPath==0 ){
+ jsonParseFree(p);
+ return;
+ }
+ i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ /* no-op */
+ }else if( i==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ eErr = 1;
+ i = 0;
+ }
}else{
- pNode = p->aNode;
- }
- if( pNode==0 ){
- return;
+ i = 0;
}
- if( pNode->eType==JSON_ARRAY ){
- assert( (pNode->jnFlags & JNODE_APPEND)==0 );
- for(i=1; i<=pNode->n; n++){
- i += jsonNodeSize(&pNode[i]);
- }
+ if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){
+ cnt = jsonbArrayCount(p, i);
}
- sqlite3_result_int64(ctx, n);
+ if( !eErr ) sqlite3_result_int64(ctx, cnt);
+ jsonParseFree(p);
+}
+
+/* True if the string is all digits */
+static int jsonAllDigits(const char *z, int n){
+ int i;
+ for(i=0; i<n && sqlite3Isdigit(z[i]); i++){}
+ return i==n;
+}
+
+/* True if the string is all alphanumerics and underscores */
+static int jsonAllAlphanum(const char *z, int n){
+ int i;
+ for(i=0; i<n && (sqlite3Isalnum(z[i]) || z[i]=='_'); i++){}
+ return i==n;
}
/*
** json_extract(JSON, PATH, ...)
+** "->"(JSON,PATH)
+** "->>"(JSON,PATH)
+**
+** Return the element described by PATH. Return NULL if that PATH element
+** is not found.
+**
+** If JSON_JSON is set or if more that one PATH argument is supplied then
+** always return a JSON representation of the result. If JSON_SQL is set,
+** then always return an SQL representation of the result. If neither flag
+** is present and argc==2, then return JSON for objects and arrays and SQL
+** for all other values.
**
-** Return the element described by PATH. Return NULL if there is no
-** PATH element. If there are multiple PATHs, then return a JSON array
-** with the result from each path. Throw an error if the JSON or any PATH
-** is malformed.
+** When multiple PATH arguments are supplied, the result is a JSON array
+** containing the result of each PATH.
+**
+** Abbreviated JSON path expressions are allows if JSON_ABPATH, for
+** compatibility with PG.
*/
static void jsonExtractFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
- JsonParse *p; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- JsonString jx;
- int i;
+ JsonParse *p = 0; /* The parse */
+ int flags; /* Flags associated with the function */
+ int i; /* Loop counter */
+ JsonString jx; /* String for array result */
if( argc<2 ) return;
- p = jsonParseCached(ctx, argv, ctx);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
- jsonInit(&jx, ctx);
- jsonAppendChar(&jx, '[');
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ jsonStringInit(&jx, ctx);
+ if( argc>2 ){
+ jsonAppendChar(&jx, '[');
+ }
for(i=1; i<argc; i++){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- pNode = jsonLookup(p, zPath, 0, ctx);
- if( p->nErr ) break;
- if( argc>2 ){
- jsonAppendSeparator(&jx);
- if( pNode ){
- jsonRenderNode(pNode, &jx, 0);
+ /* With a single PATH argument */
+ const char *zPath = (const char*)sqlite3_value_text(argv[i]);
+ int nPath;
+ u32 j;
+ if( zPath==0 ) goto json_extract_error;
+ nPath = sqlite3Strlen30(zPath);
+ if( zPath[0]=='$' ){
+ j = jsonLookupStep(p, 0, zPath+1, 0);
+ }else if( (flags & JSON_ABPATH) ){
+ /* The -> and ->> operators accept abbreviated PATH arguments. This
+ ** is mostly for compatibility with PostgreSQL, but also for
+ ** convenience.
+ **
+ ** NUMBER ==> $[NUMBER] // PG compatible
+ ** LABEL ==> $.LABEL // PG compatible
+ ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience
+ */
+ jsonStringInit(&jx, ctx);
+ if( jsonAllDigits(zPath, nPath) ){
+ jsonAppendRawNZ(&jx, "[", 1);
+ jsonAppendRaw(&jx, zPath, nPath);
+ jsonAppendRawNZ(&jx, "]", 2);
+ }else if( jsonAllAlphanum(zPath, nPath) ){
+ jsonAppendRawNZ(&jx, ".", 1);
+ jsonAppendRaw(&jx, zPath, nPath);
+ }else if( zPath[0]=='[' && nPath>=3 && zPath[nPath-1]==']' ){
+ jsonAppendRaw(&jx, zPath, nPath);
}else{
- jsonAppendRaw(&jx, "null", 4);
+ jsonAppendRawNZ(&jx, ".\"", 2);
+ jsonAppendRaw(&jx, zPath, nPath);
+ jsonAppendRawNZ(&jx, "\"", 1);
}
- }else if( pNode ){
- jsonReturn(pNode, ctx, 0);
+ jsonStringTerminate(&jx);
+ j = jsonLookupStep(p, 0, jx.zBuf, 0);
+ jsonStringReset(&jx);
+ }else{
+ jsonBadPathError(ctx, zPath);
+ goto json_extract_error;
+ }
+ if( j<p->nBlob ){
+ if( argc==2 ){
+ if( flags & JSON_JSON ){
+ jsonStringInit(&jx, ctx);
+ jsonTranslateBlobToText(p, j, &jx);
+ jsonReturnString(&jx, 0, 0);
+ jsonStringReset(&jx);
+ assert( (flags & JSON_BLOB)==0 );
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }else{
+ jsonReturnFromBlob(p, j, ctx, 0);
+ if( (flags & (JSON_SQL|JSON_BLOB))==0
+ && (p->aBlob[j]&0x0f)>=JSONB_ARRAY
+ ){
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
+ }
+ }else{
+ jsonAppendSeparator(&jx);
+ jsonTranslateBlobToText(p, j, &jx);
+ }
+ }else if( j==JSON_LOOKUP_NOTFOUND ){
+ if( argc==2 ){
+ goto json_extract_error; /* Return NULL if not found */
+ }else{
+ jsonAppendSeparator(&jx);
+ jsonAppendRawNZ(&jx, "null", 4);
+ }
+ }else if( j==JSON_LOOKUP_ERROR ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ goto json_extract_error;
+ }else{
+ jsonBadPathError(ctx, zPath);
+ goto json_extract_error;
}
}
- if( argc>2 && i==argc ){
+ if( argc>2 ){
jsonAppendChar(&jx, ']');
- jsonResult(&jx);
- sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ jsonReturnString(&jx, 0, 0);
+ if( (flags & JSON_BLOB)==0 ){
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
}
- jsonReset(&jx);
+json_extract_error:
+ jsonStringReset(&jx);
+ jsonParseFree(p);
+ return;
}
-/* This is the RFC 7396 MergePatch algorithm.
-*/
-static JsonNode *jsonMergePatch(
- JsonParse *pParse, /* The JSON parser that contains the TARGET */
- u32 iTarget, /* Node of the TARGET in pParse */
- JsonNode *pPatch /* The PATCH */
-){
- u32 i, j;
- u32 iRoot;
- JsonNode *pTarget;
- if( pPatch->eType!=JSON_OBJECT ){
- return pPatch;
- }
- assert( iTarget>=0 && iTarget<pParse->nNode );
- pTarget = &pParse->aNode[iTarget];
- assert( (pPatch->jnFlags & JNODE_APPEND)==0 );
- if( pTarget->eType!=JSON_OBJECT ){
- jsonRemoveAllNulls(pPatch);
- return pPatch;
- }
- iRoot = iTarget;
- for(i=1; i<pPatch->n; i += jsonNodeSize(&pPatch[i+1])+1){
- u32 nKey;
- const char *zKey;
- assert( pPatch[i].eType==JSON_STRING );
- assert( pPatch[i].jnFlags & JNODE_LABEL );
- nKey = pPatch[i].n;
- zKey = pPatch[i].u.zJContent;
- assert( (pPatch[i].jnFlags & JNODE_RAW)==0 );
- for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){
- assert( pTarget[j].eType==JSON_STRING );
- assert( pTarget[j].jnFlags & JNODE_LABEL );
- assert( (pPatch[i].jnFlags & JNODE_RAW)==0 );
- if( pTarget[j].n==nKey && strncmp(pTarget[j].u.zJContent,zKey,nKey)==0 ){
- if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break;
- if( pPatch[i+1].eType==JSON_NULL ){
- pTarget[j+1].jnFlags |= JNODE_REMOVE;
- }else{
- JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
- if( pNew==0 ) return 0;
- pTarget = &pParse->aNode[iTarget];
- if( pNew!=&pTarget[j+1] ){
- pTarget[j+1].u.pPatch = pNew;
- pTarget[j+1].jnFlags |= JNODE_PATCH;
- }
- }
- break;
+/*
+** Return codes for jsonMergePatch()
+*/
+#define JSON_MERGE_OK 0 /* Success */
+#define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */
+#define JSON_MERGE_BADPATCH 2 /* Malformed PATCH blob */
+#define JSON_MERGE_OOM 3 /* Out-of-memory condition */
+
+/*
+** RFC-7396 MergePatch for two JSONB blobs.
+**
+** pTarget is the target. pPatch is the patch. The target is updated
+** in place. The patch is read-only.
+**
+** The original RFC-7396 algorithm is this:
+**
+** define MergePatch(Target, Patch):
+** if Patch is an Object:
+** if Target is not an Object:
+** Target = {} # Ignore the contents and set it to an empty Object
+** for each Name/Value pair in Patch:
+** if Value is null:
+** if Name exists in Target:
+** remove the Name/Value pair from Target
+** else:
+** Target[Name] = MergePatch(Target[Name], Value)
+** return Target
+** else:
+** return Patch
+**
+** Here is an equivalent algorithm restructured to show the actual
+** implementation:
+**
+** 01 define MergePatch(Target, Patch):
+** 02 if Patch is not an Object:
+** 03 return Patch
+** 04 else: // if Patch is an Object
+** 05 if Target is not an Object:
+** 06 Target = {}
+** 07 for each Name/Value pair in Patch:
+** 08 if Name exists in Target:
+** 09 if Value is null:
+** 10 remove the Name/Value pair from Target
+** 11 else
+** 12 Target[name] = MergePatch(Target[Name], Value)
+** 13 else if Value is not NULL:
+** 14 if Value is not an Object:
+** 15 Target[name] = Value
+** 16 else:
+** 17 Target[name] = MergePatch('{}',value)
+** 18 return Target
+** |
+** ^---- Line numbers referenced in comments in the implementation
+*/
+static int jsonMergePatch(
+ JsonParse *pTarget, /* The JSON parser that contains the TARGET */
+ u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */
+ const JsonParse *pPatch, /* The PATCH */
+ u32 iPatch /* Index of PATCH in pPatch->aBlob[] */
+){
+ u8 x; /* Type of a single node */
+ u32 n, sz=0; /* Return values from jsonbPayloadSize() */
+ u32 iTCursor; /* Cursor position while scanning the target object */
+ u32 iTStart; /* First label in the target object */
+ u32 iTEndBE; /* Original first byte past end of target, before edit */
+ u32 iTEnd; /* Current first byte past end of target */
+ u8 eTLabel; /* Node type of the target label */
+ u32 iTLabel = 0; /* Index of the label */
+ u32 nTLabel = 0; /* Header size in bytes for the target label */
+ u32 szTLabel = 0; /* Size of the target label payload */
+ u32 iTValue = 0; /* Index of the target value */
+ u32 nTValue = 0; /* Header size of the target value */
+ u32 szTValue = 0; /* Payload size for the target value */
+
+ u32 iPCursor; /* Cursor position while scanning the patch */
+ u32 iPEnd; /* First byte past the end of the patch */
+ u8 ePLabel; /* Node type of the patch label */
+ u32 iPLabel; /* Start of patch label */
+ u32 nPLabel; /* Size of header on the patch label */
+ u32 szPLabel; /* Payload size of the patch label */
+ u32 iPValue; /* Start of patch value */
+ u32 nPValue; /* Header size for the patch value */
+ u32 szPValue; /* Payload size of the patch value */
+
+ assert( iTarget>=0 && iTarget<pTarget->nBlob );
+ assert( iPatch>=0 && iPatch<pPatch->nBlob );
+ x = pPatch->aBlob[iPatch] & 0x0f;
+ if( x!=JSONB_OBJECT ){ /* Algorithm line 02 */
+ u32 szPatch; /* Total size of the patch, header+payload */
+ u32 szTarget; /* Total size of the target, header+payload */
+ n = jsonbPayloadSize(pPatch, iPatch, &sz);
+ szPatch = n+sz;
+ sz = 0;
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ szTarget = n+sz;
+ jsonBlobEdit(pTarget, iTarget, szTarget, pPatch->aBlob+iPatch, szPatch);
+ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; /* Line 03 */
+ }
+ x = pTarget->aBlob[iTarget] & 0x0f;
+ if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0);
+ x = pTarget->aBlob[iTarget];
+ pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT;
+ }
+ n = jsonbPayloadSize(pPatch, iPatch, &sz);
+ if( NEVER(n==0) ) return JSON_MERGE_BADPATCH;
+ iPCursor = iPatch+n;
+ iPEnd = iPCursor+sz;
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ if( NEVER(n==0) ) return JSON_MERGE_BADTARGET;
+ iTStart = iTarget+n;
+ iTEndBE = iTStart+sz;
+
+ while( iPCursor<iPEnd ){ /* Algorithm line 07 */
+ iPLabel = iPCursor;
+ ePLabel = pPatch->aBlob[iPCursor] & 0x0f;
+ if( ePLabel<JSONB_TEXT || ePLabel>JSONB_TEXTRAW ){
+ return JSON_MERGE_BADPATCH;
+ }
+ nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel);
+ if( nPLabel==0 ) return JSON_MERGE_BADPATCH;
+ iPValue = iPCursor + nPLabel + szPLabel;
+ if( iPValue>=iPEnd ) return JSON_MERGE_BADPATCH;
+ nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue);
+ if( nPValue==0 ) return JSON_MERGE_BADPATCH;
+ iPCursor = iPValue + nPValue + szPValue;
+ if( iPCursor>iPEnd ) return JSON_MERGE_BADPATCH;
+
+ iTCursor = iTStart;
+ iTEnd = iTEndBE + pTarget->delta;
+ while( iTCursor<iTEnd ){
+ int isEqual; /* true if the patch and target labels match */
+ iTLabel = iTCursor;
+ eTLabel = pTarget->aBlob[iTCursor] & 0x0f;
+ if( eTLabel<JSONB_TEXT || eTLabel>JSONB_TEXTRAW ){
+ return JSON_MERGE_BADTARGET;
+ }
+ nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel);
+ if( nTLabel==0 ) return JSON_MERGE_BADTARGET;
+ iTValue = iTLabel + nTLabel + szTLabel;
+ if( iTValue>=iTEnd ) return JSON_MERGE_BADTARGET;
+ nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue);
+ if( nTValue==0 ) return JSON_MERGE_BADTARGET;
+ if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET;
+ isEqual = jsonLabelCompare(
+ (const char*)&pPatch->aBlob[iPLabel+nPLabel],
+ szPLabel,
+ (ePLabel==JSONB_TEXT || ePLabel==JSONB_TEXTRAW),
+ (const char*)&pTarget->aBlob[iTLabel+nTLabel],
+ szTLabel,
+ (eTLabel==JSONB_TEXT || eTLabel==JSONB_TEXTRAW));
+ if( isEqual ) break;
+ iTCursor = iTValue + nTValue + szTValue;
+ }
+ x = pPatch->aBlob[iPValue] & 0x0f;
+ if( iTCursor<iTEnd ){
+ /* A match was found. Algorithm line 08 */
+ if( x==0 ){
+ /* Patch value is NULL. Algorithm line 09 */
+ jsonBlobEdit(pTarget, iTLabel, nTLabel+szTLabel+nTValue+szTValue, 0,0);
+ /* vvvvvv----- No OOM on a delete-only edit */
+ if( NEVER(pTarget->oom) ) return JSON_MERGE_OOM;
+ }else{
+ /* Algorithm line 12 */
+ int rc, savedDelta = pTarget->delta;
+ pTarget->delta = 0;
+ rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue);
+ if( rc ) return rc;
+ pTarget->delta += savedDelta;
+ }
+ }else if( x>0 ){ /* Algorithm line 13 */
+ /* No match and patch value is not NULL */
+ u32 szNew = szPLabel+nPLabel;
+ if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ /* Line 14 */
+ jsonBlobEdit(pTarget, iTEnd, 0, 0, szPValue+nPValue+szNew);
+ if( pTarget->oom ) return JSON_MERGE_OOM;
+ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
+ memcpy(&pTarget->aBlob[iTEnd+szNew],
+ &pPatch->aBlob[iPValue], szPValue+nPValue);
+ }else{
+ int rc, savedDelta;
+ jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1);
+ if( pTarget->oom ) return JSON_MERGE_OOM;
+ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
+ pTarget->aBlob[iTEnd+szNew] = 0x00;
+ savedDelta = pTarget->delta;
+ pTarget->delta = 0;
+ rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue);
+ if( rc ) return rc;
+ pTarget->delta += savedDelta;
}
}
- if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){
- int iStart, iPatch;
- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
- jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
- iPatch = jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
- if( pParse->oom ) return 0;
- jsonRemoveAllNulls(pPatch);
- pTarget = &pParse->aNode[iTarget];
- pParse->aNode[iRoot].jnFlags |= JNODE_APPEND;
- pParse->aNode[iRoot].u.iAppend = iStart - iRoot;
- iRoot = iStart;
- pParse->aNode[iPatch].jnFlags |= JNODE_PATCH;
- pParse->aNode[iPatch].u.pPatch = &pPatch[i+1];
- }
}
- return pTarget;
+ if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget);
+ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK;
}
+
/*
** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON
** object that is the result of running the RFC 7396 MergePatch() algorithm
@@ -178142,25 +207182,27 @@ static void jsonPatchFunc(
int argc,
sqlite3_value **argv
){
- JsonParse x; /* The JSON that is being patched */
- JsonParse y; /* The patch */
- JsonNode *pResult; /* The result of the merge */
+ JsonParse *pTarget; /* The TARGET */
+ JsonParse *pPatch; /* The PATCH */
+ int rc; /* Result code */
- UNUSED_PARAM(argc);
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
- if( jsonParse(&y, ctx, (const char*)sqlite3_value_text(argv[1])) ){
- jsonParseReset(&x);
- return;
- }
- pResult = jsonMergePatch(&x, 0, y.aNode);
- assert( pResult!=0 || x.oom );
- if( pResult ){
- jsonReturnJson(pResult, ctx, 0);
- }else{
- sqlite3_result_error_nomem(ctx);
+ UNUSED_PARAMETER(argc);
+ assert( argc==2 );
+ pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE);
+ if( pTarget==0 ) return;
+ pPatch = jsonParseFuncArg(ctx, argv[1], 0);
+ if( pPatch ){
+ rc = jsonMergePatch(pTarget, 0, pPatch, 0);
+ if( rc==JSON_MERGE_OK ){
+ jsonReturnParse(ctx, pTarget);
+ }else if( rc==JSON_MERGE_OOM ){
+ sqlite3_result_error_nomem(ctx);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ jsonParseFree(pPatch);
}
- jsonParseReset(&x);
- jsonParseReset(&y);
+ jsonParseFree(pTarget);
}
@@ -178184,23 +207226,23 @@ static void jsonObjectFunc(
"of arguments", -1);
return;
}
- jsonInit(&jx, ctx);
+ jsonStringInit(&jx, ctx);
jsonAppendChar(&jx, '{');
for(i=0; i<argc; i+=2){
if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
- jsonReset(&jx);
+ jsonStringReset(&jx);
return;
}
jsonAppendSeparator(&jx);
z = (const char*)sqlite3_value_text(argv[i]);
- n = (u32)sqlite3_value_bytes(argv[i]);
+ n = sqlite3_value_bytes(argv[i]);
jsonAppendString(&jx, z, n);
jsonAppendChar(&jx, ':');
- jsonAppendValue(&jx, argv[i+1]);
+ jsonAppendSqlValue(&jx, argv[i+1]);
}
jsonAppendChar(&jx, '}');
- jsonResult(&jx);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
@@ -178216,26 +207258,50 @@ static void jsonRemoveFunc(
int argc,
sqlite3_value **argv
){
- JsonParse x; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
+ JsonParse *p; /* The parse */
+ const char *zPath = 0; /* Path of element to be removed */
+ int i; /* Loop counter */
+ u32 rc; /* Subroutine return code */
if( argc<1 ) return;
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
- assert( x.nNode );
- for(i=1; i<(u32)argc; i++){
+ p = jsonParseFuncArg(ctx, argv[0], argc>1 ? JSON_EDITABLE : 0);
+ if( p==0 ) return;
+ for(i=1; i<argc; i++){
zPath = (const char*)sqlite3_value_text(argv[i]);
- if( zPath==0 ) goto remove_done;
- pNode = jsonLookup(&x, zPath, 0, ctx);
- if( x.nErr ) goto remove_done;
- if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
- }
- if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
- jsonReturnJson(x.aNode, ctx, 0);
+ if( zPath==0 ){
+ goto json_remove_done;
+ }
+ if( zPath[0]!='$' ){
+ goto json_remove_patherror;
+ }
+ if( zPath[1]==0 ){
+ /* json_remove(j,'$') returns NULL */
+ goto json_remove_done;
+ }
+ p->eEdit = JEDIT_DEL;
+ p->delta = 0;
+ rc = jsonLookupStep(p, 0, zPath+1, 0);
+ if( JSON_LOOKUP_ISERROR(rc) ){
+ if( rc==JSON_LOOKUP_NOTFOUND ){
+ continue; /* No-op */
+ }else if( rc==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ goto json_remove_done;
+ }
}
-remove_done:
- jsonParseReset(&x);
+ jsonReturnParse(ctx, p);
+ jsonParseFree(p);
+ return;
+
+json_remove_patherror:
+ jsonBadPathError(ctx, zPath);
+
+json_remove_done:
+ jsonParseFree(p);
+ return;
}
/*
@@ -178249,36 +207315,15 @@ static void jsonReplaceFunc(
int argc,
sqlite3_value **argv
){
- JsonParse x; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
-
if( argc<1 ) return;
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, "replace");
return;
}
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
- assert( x.nNode );
- for(i=1; i<(u32)argc; i+=2){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- pNode = jsonLookup(&x, zPath, 0, ctx);
- if( x.nErr ) goto replace_err;
- if( pNode ){
- pNode->jnFlags |= (u8)JNODE_REPLACE;
- pNode->u.iReplace = i + 1;
- }
- }
- if( x.aNode[0].jnFlags & JNODE_REPLACE ){
- sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
- }else{
- jsonReturnJson(x.aNode, ctx, argv);
- }
-replace_err:
- jsonParseReset(&x);
+ jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL);
}
+
/*
** json_set(JSON, PATH, VALUE, ...)
**
@@ -178296,49 +207341,24 @@ static void jsonSetFunc(
int argc,
sqlite3_value **argv
){
- JsonParse x; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
- int bApnd;
- int bIsSet = *(int*)sqlite3_user_data(ctx);
+
+ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ int bIsSet = (flags&JSON_ISSET)!=0;
if( argc<1 ) return;
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
return;
}
- if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
- assert( x.nNode );
- for(i=1; i<(u32)argc; i+=2){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- bApnd = 0;
- pNode = jsonLookup(&x, zPath, &bApnd, ctx);
- if( x.oom ){
- sqlite3_result_error_nomem(ctx);
- goto jsonSetDone;
- }else if( x.nErr ){
- goto jsonSetDone;
- }else if( pNode && (bApnd || bIsSet) ){
- pNode->jnFlags |= (u8)JNODE_REPLACE;
- pNode->u.iReplace = i + 1;
- }
- }
- if( x.aNode[0].jnFlags & JNODE_REPLACE ){
- sqlite3_result_value(ctx, argv[x.aNode[0].u.iReplace]);
- }else{
- jsonReturnJson(x.aNode, ctx, argv);
- }
-jsonSetDone:
- jsonParseReset(&x);
+ jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS);
}
/*
** json_type(JSON)
** json_type(JSON, PATH)
**
-** Return the top-level "type" of a JSON string. Throw an error if
-** either the JSON or PATH inputs are not well-formed.
+** Return the top-level "type" of a JSON string. json_type() raises an
+** error if either the JSON or PATH inputs are not well-formed.
*/
static void jsonTypeFunc(
sqlite3_context *ctx,
@@ -178346,27 +207366,93 @@ static void jsonTypeFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- const char *zPath;
- JsonNode *pNode;
+ const char *zPath = 0;
+ u32 i;
- p = jsonParseCached(ctx, argv, ctx);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
if( argc==2 ){
zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(p, zPath, 0, ctx);
+ if( zPath==0 ) goto json_type_done;
+ if( zPath[0]!='$' ){
+ jsonBadPathError(ctx, zPath);
+ goto json_type_done;
+ }
+ i = jsonLookupStep(p, 0, zPath+1, 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ /* no-op */
+ }else if( i==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ goto json_type_done;
+ }
}else{
- pNode = p->aNode;
- }
- if( pNode ){
- sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
+ i = 0;
}
+ sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC);
+json_type_done:
+ jsonParseFree(p);
}
/*
** json_valid(JSON)
-**
-** Return 1 if JSON is a well-formed JSON string according to RFC-7159.
-** Return 0 otherwise.
+** json_valid(JSON, FLAGS)
+**
+** Check the JSON argument to see if it is well-formed. The FLAGS argument
+** encodes the various constraints on what is meant by "well-formed":
+**
+** 0x01 Canonical RFC-8259 JSON text
+** 0x02 JSON text with optional JSON-5 extensions
+** 0x04 Superficially appears to be JSONB
+** 0x08 Strictly well-formed JSONB
+**
+** If the FLAGS argument is omitted, it defaults to 1. Useful values for
+** FLAGS include:
+**
+** 1 Strict canonical JSON text
+** 2 JSON text perhaps with JSON-5 extensions
+** 4 Superficially appears to be JSONB
+** 5 Canonical JSON text or superficial JSONB
+** 6 JSON-5 text or superficial JSONB
+** 8 Strict JSONB
+** 9 Canonical JSON text or strict JSONB
+** 10 JSON-5 text or strict JSONB
+**
+** Other flag combinations are redundant. For example, every canonical
+** JSON text is also well-formed JSON-5 text, so FLAG values 2 and 3
+** are the same. Similarly, any input that passes a strict JSONB validation
+** will also pass the superficial validation so 12 through 15 are the same
+** as 8 through 11 respectively.
+**
+** This routine runs in linear time to validate text and when doing strict
+** JSONB validation. Superficial JSONB validation is constant time,
+** assuming the BLOB is already in memory. The performance advantage
+** of superficial JSONB validation is why that option is provided.
+** Application developers can choose to do fast superficial validation or
+** slower strict validation, according to their specific needs.
+**
+** Only the lower four bits of the FLAGS argument are currently used.
+** Higher bits are reserved for future expansion. To facilitate
+** compatibility, the current implementation raises an error if any bit
+** in FLAGS is set other than the lower four bits.
+**
+** The original circa 2015 implementation of the JSON routines in
+** SQLite only supported canonical RFC-8259 JSON text and the json_valid()
+** function only accepted one argument. That is why the default value
+** for the FLAGS argument is 1, since FLAGS=1 causes this routine to only
+** recognize canonical RFC-8259 JSON text as valid. The extra FLAGS
+** argument was added when the JSON routines were extended to support
+** JSON5-like extensions and binary JSONB stored in BLOBs.
+**
+** Return Values:
+**
+** * Raise an error if FLAGS is outside the range of 1 to 15.
+** * Return NULL if the input is NULL
+** * Return 1 if the input is well-formed.
+** * Return 0 if the input is not well-formed.
*/
static void jsonValidFunc(
sqlite3_context *ctx,
@@ -178374,11 +207460,127 @@ static void jsonValidFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- UNUSED_PARAM(argc);
- p = jsonParseCached(ctx, argv, 0);
- sqlite3_result_int(ctx, p!=0);
+ u8 flags = 1;
+ u8 res = 0;
+ if( argc==2 ){
+ i64 f = sqlite3_value_int64(argv[1]);
+ if( f<1 || f>15 ){
+ sqlite3_result_error(ctx, "FLAGS parameter to json_valid() must be"
+ " between 1 and 15", -1);
+ return;
+ }
+ flags = f & 0x0f;
+ }
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_NULL: {
+#ifdef SQLITE_LEGACY_JSON_VALID
+ /* Incorrect legacy behavior was to return FALSE for a NULL input */
+ sqlite3_result_int(ctx, 0);
+#endif
+ return;
+ }
+ case SQLITE_BLOB: {
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ if( flags & 0x04 ){
+ /* Superficial checking only - accomplished by the
+ ** jsonFuncArgMightBeBinary() call above. */
+ res = 1;
+ }else if( flags & 0x08 ){
+ /* Strict checking. Check by translating BLOB->TEXT->BLOB. If
+ ** no errors occur, call that a "strict check". */
+ JsonParse px;
+ u32 iErr;
+ memset(&px, 0, sizeof(px));
+ px.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ px.nBlob = sqlite3_value_bytes(argv[0]);
+ iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
+ res = iErr==0;
+ }
+ break;
+ }
+ /* Fall through into interpreting the input as text. See note
+ ** above at tag-20240123-a. */
+ /* no break */ deliberate_fall_through
+ }
+ default: {
+ JsonParse px;
+ if( (flags & 0x3)==0 ) break;
+ memset(&px, 0, sizeof(px));
+
+ p = jsonParseFuncArg(ctx, argv[0], JSON_KEEPERROR);
+ if( p ){
+ if( p->oom ){
+ sqlite3_result_error_nomem(ctx);
+ }else if( p->nErr ){
+ /* no-op */
+ }else if( (flags & 0x02)!=0 || p->hasNonstd==0 ){
+ res = 1;
+ }
+ jsonParseFree(p);
+ }else{
+ sqlite3_result_error_nomem(ctx);
+ }
+ break;
+ }
+ }
+ sqlite3_result_int(ctx, res);
}
+/*
+** json_error_position(JSON)
+**
+** If the argument is NULL, return NULL
+**
+** If the argument is BLOB, do a full validity check and return non-zero
+** if the check fails. The return value is the approximate 1-based offset
+** to the byte of the element that contains the first error.
+**
+** Otherwise interpret the argument is TEXT (even if it is numeric) and
+** return the 1-based character position for where the parser first recognized
+** that the input was not valid JSON, or return 0 if the input text looks
+** ok. JSON-5 extensions are accepted.
+*/
+static void jsonErrorFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ i64 iErrPos = 0; /* Error position to be returned */
+ JsonParse s;
+
+ assert( argc==1 );
+ UNUSED_PARAMETER(argc);
+ memset(&s, 0, sizeof(s));
+ s.db = sqlite3_context_db_handle(ctx);
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ s.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ s.nBlob = sqlite3_value_bytes(argv[0]);
+ iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1);
+ }else{
+ s.zJson = (char*)sqlite3_value_text(argv[0]);
+ if( s.zJson==0 ) return; /* NULL input or OOM */
+ s.nJson = sqlite3_value_bytes(argv[0]);
+ if( jsonConvertTextToBlob(&s,0) ){
+ if( s.oom ){
+ iErrPos = -1;
+ }else{
+ /* Convert byte-offset s.iErr into a character offset */
+ u32 k;
+ assert( s.zJson!=0 ); /* Because s.oom is false */
+ for(k=0; k<s.iErr && ALWAYS(s.zJson[k]); k++){
+ if( (s.zJson[k] & 0xc0)!=0x80 ) iErrPos++;
+ }
+ iErrPos++;
+ }
+ }
+ }
+ jsonParseReset(&s);
+ if( iErrPos<0 ){
+ sqlite3_result_error_nomem(ctx);
+ }else{
+ sqlite3_result_int64(ctx, iErrPos);
+ }
+}
/****************************************************************************
** Aggregate SQL function implementations
@@ -178394,35 +207596,46 @@ static void jsonArrayStep(
sqlite3_value **argv
){
JsonString *pStr;
- UNUSED_PARAM(argc);
+ UNUSED_PARAMETER(argc);
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
- jsonInit(pStr, ctx);
+ jsonStringInit(pStr, ctx);
jsonAppendChar(pStr, '[');
- }else{
+ }else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
- pStr->pCtx = ctx;
}
- jsonAppendValue(pStr, argv[0]);
+ pStr->pCtx = ctx;
+ jsonAppendSqlValue(pStr, argv[0]);
}
}
static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
+ int flags;
pStr->pCtx = ctx;
jsonAppendChar(pStr, ']');
- if( pStr->bErr ){
- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
- assert( pStr->bStatic );
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( pStr->eErr ){
+ jsonReturnString(pStr, 0, 0);
+ return;
+ }else if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(pStr);
+ if( isFinal ){
+ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
+ }else{
+ jsonStringTrimOneChar(pStr);
+ }
+ return;
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
- pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
+ pStr->bStatic ? SQLITE_TRANSIENT :
+ sqlite3RCStrUnref);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
- pStr->nUsed--;
+ jsonStringTrimOneChar(pStr);
}
}else{
sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
@@ -178448,29 +207661,38 @@ static void jsonGroupInverse(
int argc,
sqlite3_value **argv
){
- int i;
+ unsigned int i;
int inStr = 0;
+ int nNest = 0;
char *z;
+ char c;
JsonString *pStr;
- UNUSED_PARAM(argc);
- UNUSED_PARAM(argv);
+ UNUSED_PARAMETER(argc);
+ UNUSED_PARAMETER(argv);
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
#ifdef NEVER
/* pStr is always non-NULL since jsonArrayStep() or jsonObjectStep() will
- ** always have been called to initalize it */
+ ** always have been called to initialize it */
if( NEVER(!pStr) ) return;
#endif
z = pStr->zBuf;
- for(i=1; z[i]!=',' || inStr; i++){
- assert( i<pStr->nUsed );
- if( z[i]=='"' ){
+ for(i=1; i<pStr->nUsed && ((c = z[i])!=',' || inStr || nNest); i++){
+ if( c=='"' ){
inStr = !inStr;
- }else if( z[i]=='\\' ){
+ }else if( c=='\\' ){
i++;
+ }else if( !inStr ){
+ if( c=='{' || c=='[' ) nNest++;
+ if( c=='}' || c==']' ) nNest--;
}
}
- pStr->nUsed -= i;
- memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1);
+ if( i<pStr->nUsed ){
+ pStr->nUsed -= i;
+ memmove(&z[1], &z[i+1], (size_t)pStr->nUsed-1);
+ z[pStr->nUsed] = 0;
+ }else{
+ pStr->nUsed = 1;
+ }
}
#else
# define jsonGroupInverse 0
@@ -178490,38 +207712,50 @@ static void jsonObjectStep(
JsonString *pStr;
const char *z;
u32 n;
- UNUSED_PARAM(argc);
+ UNUSED_PARAMETER(argc);
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
- jsonInit(pStr, ctx);
+ jsonStringInit(pStr, ctx);
jsonAppendChar(pStr, '{');
- }else{
+ }else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
- pStr->pCtx = ctx;
}
+ pStr->pCtx = ctx;
z = (const char*)sqlite3_value_text(argv[0]);
- n = (u32)sqlite3_value_bytes(argv[0]);
+ n = sqlite3Strlen30(z);
jsonAppendString(pStr, z, n);
jsonAppendChar(pStr, ':');
- jsonAppendValue(pStr, argv[1]);
+ jsonAppendSqlValue(pStr, argv[1]);
}
}
static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
+ int flags;
jsonAppendChar(pStr, '}');
- if( pStr->bErr ){
- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
- assert( pStr->bStatic );
+ pStr->pCtx = ctx;
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( pStr->eErr ){
+ jsonReturnString(pStr, 0, 0);
+ return;
+ }else if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(pStr);
+ if( isFinal ){
+ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
+ }else{
+ jsonStringTrimOneChar(pStr);
+ }
+ return;
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
- pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
+ pStr->bStatic ? SQLITE_TRANSIENT :
+ sqlite3RCStrUnref);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
- pStr->nUsed--;
+ jsonStringTrimOneChar(pStr);
}
}else{
sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
@@ -178541,19 +207775,37 @@ static void jsonObjectFinal(sqlite3_context *ctx){
/****************************************************************************
** The json_each virtual table
****************************************************************************/
+typedef struct JsonParent JsonParent;
+struct JsonParent {
+ u32 iHead; /* Start of object or array */
+ u32 iValue; /* Start of the value */
+ u32 iEnd; /* First byte past the end */
+ u32 nPath; /* Length of path */
+ i64 iKey; /* Key for JSONB_ARRAY */
+};
+
typedef struct JsonEachCursor JsonEachCursor;
struct JsonEachCursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
u32 iRowid; /* The rowid */
- u32 iBegin; /* The first node of the scan */
- u32 i; /* Index in sParse.aNode[] of current row */
+ u32 i; /* Index in sParse.aBlob[] of current row */
u32 iEnd; /* EOF when i equals or exceeds this value */
- u8 eType; /* Type of top-level element */
+ u32 nRoot; /* Size of the root path in bytes */
+ u8 eType; /* Type of the container for element i */
u8 bRecursive; /* True for json_tree(). False for json_each() */
- char *zJson; /* Input JSON */
- char *zRoot; /* Path by which to filter zJson */
+ u32 nParent; /* Current nesting depth */
+ u32 nParentAlloc; /* Space allocated for aParent[] */
+ JsonParent *aParent; /* Parent elements of i */
+ sqlite3 *db; /* Database connection */
+ JsonString path; /* Current path */
JsonParse sParse; /* Parse of the input JSON */
};
+typedef struct JsonEachConnection JsonEachConnection;
+struct JsonEachConnection {
+ sqlite3_vtab base; /* Base class - must be first */
+ sqlite3 *db; /* Database connection */
+};
+
/* Constructor for the json_each virtual table */
static int jsonEachConnect(
@@ -178563,7 +207815,7 @@ static int jsonEachConnect(
sqlite3_vtab **ppVtab,
char **pzErr
){
- sqlite3_vtab *pNew;
+ JsonEachConnection *pNew;
int rc;
/* Column numbers */
@@ -178581,35 +207833,40 @@ static int jsonEachConnect(
#define JEACH_JSON 8
#define JEACH_ROOT 9
- UNUSED_PARAM(pzErr);
- UNUSED_PARAM(argv);
- UNUSED_PARAM(argc);
- UNUSED_PARAM(pAux);
- rc = sqlite3_declare_vtab(db,
+ UNUSED_PARAMETER(pzErr);
+ UNUSED_PARAMETER(argv);
+ UNUSED_PARAMETER(argc);
+ UNUSED_PARAMETER(pAux);
+ rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
"json HIDDEN,root HIDDEN)");
if( rc==SQLITE_OK ){
- pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+ pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew));
+ *ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
- memset(pNew, 0, sizeof(*pNew));
+ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
+ pNew->db = db;
}
return rc;
}
/* destructor for json_each virtual table */
static int jsonEachDisconnect(sqlite3_vtab *pVtab){
- sqlite3_free(pVtab);
+ JsonEachConnection *p = (JsonEachConnection*)pVtab;
+ sqlite3DbFree(p->db, pVtab);
return SQLITE_OK;
}
/* constructor for a JsonEachCursor object for json_each(). */
static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ JsonEachConnection *pVtab = (JsonEachConnection*)p;
JsonEachCursor *pCur;
- UNUSED_PARAM(p);
- pCur = sqlite3_malloc( sizeof(*pCur) );
+ UNUSED_PARAMETER(p);
+ pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur));
if( pCur==0 ) return SQLITE_NOMEM;
- memset(pCur, 0, sizeof(*pCur));
+ pCur->db = pVtab->db;
+ jsonStringZero(&pCur->path);
*ppCursor = &pCur->base;
return SQLITE_OK;
}
@@ -178627,22 +207884,24 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
/* Reset a JsonEachCursor back to its original state. Free any memory
** held. */
static void jsonEachCursorReset(JsonEachCursor *p){
- sqlite3_free(p->zJson);
- sqlite3_free(p->zRoot);
jsonParseReset(&p->sParse);
+ jsonStringReset(&p->path);
+ sqlite3DbFree(p->db, p->aParent);
p->iRowid = 0;
p->i = 0;
+ p->aParent = 0;
+ p->nParent = 0;
+ p->nParentAlloc = 0;
p->iEnd = 0;
p->eType = 0;
- p->zJson = 0;
- p->zRoot = 0;
}
/* Destructor for a jsonEachCursor object */
static int jsonEachClose(sqlite3_vtab_cursor *cur){
JsonEachCursor *p = (JsonEachCursor*)cur;
jsonEachCursorReset(p);
- sqlite3_free(cur);
+
+ sqlite3DbFree(p->db, cur);
return SQLITE_OK;
}
@@ -178653,166 +207912,230 @@ static int jsonEachEof(sqlite3_vtab_cursor *cur){
return p->i >= p->iEnd;
}
+/*
+** If the cursor is currently pointing at the label of a object entry,
+** then return the index of the value. For all other cases, return the
+** current pointer position, which is the value.
+*/
+static int jsonSkipLabel(JsonEachCursor *p){
+ if( p->eType==JSONB_OBJECT ){
+ u32 sz = 0;
+ u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz);
+ return p->i + n + sz;
+ }else{
+ return p->i;
+ }
+}
+
+/*
+** Append the path name for the current element.
+*/
+static void jsonAppendPathName(JsonEachCursor *p){
+ assert( p->nParent>0 );
+ assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT );
+ if( p->eType==JSONB_ARRAY ){
+ jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey);
+ }else{
+ u32 n, sz = 0, k, i;
+ const char *z;
+ int needQuote = 0;
+ n = jsonbPayloadSize(&p->sParse, p->i, &sz);
+ k = p->i + n;
+ z = (const char*)&p->sParse.aBlob[k];
+ if( sz==0 || !sqlite3Isalpha(z[0]) ){
+ needQuote = 1;
+ }else{
+ for(i=0; i<sz; i++){
+ if( !sqlite3Isalnum(z[i]) ){
+ needQuote = 1;
+ break;
+ }
+ }
+ }
+ if( needQuote ){
+ jsonPrintf(sz+4,&p->path,".\"%.*s\"", sz, z);
+ }else{
+ jsonPrintf(sz+2,&p->path,".%.*s", sz, z);
+ }
+ }
+}
+
/* Advance the cursor to the next element for json_tree() */
static int jsonEachNext(sqlite3_vtab_cursor *cur){
JsonEachCursor *p = (JsonEachCursor*)cur;
+ int rc = SQLITE_OK;
if( p->bRecursive ){
- if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
- p->i++;
- p->iRowid++;
- if( p->i<p->iEnd ){
- u32 iUp = p->sParse.aUp[p->i];
- JsonNode *pUp = &p->sParse.aNode[iUp];
- p->eType = pUp->eType;
- if( pUp->eType==JSON_ARRAY ){
- if( iUp==p->i-1 ){
- pUp->u.iKey = 0;
- }else{
- pUp->u.iKey++;
- }
+ u8 x;
+ u8 levelChange = 0;
+ u32 n, sz = 0;
+ u32 i = jsonSkipLabel(p);
+ x = p->sParse.aBlob[i] & 0x0f;
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ if( x==JSONB_OBJECT || x==JSONB_ARRAY ){
+ JsonParent *pParent;
+ if( p->nParent>=p->nParentAlloc ){
+ JsonParent *pNew;
+ u64 nNew;
+ nNew = p->nParentAlloc*2 + 3;
+ pNew = sqlite3DbRealloc(p->db, p->aParent, sizeof(JsonParent)*nNew);
+ if( pNew==0 ) return SQLITE_NOMEM;
+ p->nParentAlloc = (u32)nNew;
+ p->aParent = pNew;
+ }
+ levelChange = 1;
+ pParent = &p->aParent[p->nParent];
+ pParent->iHead = p->i;
+ pParent->iValue = i;
+ pParent->iEnd = i + n + sz;
+ pParent->iKey = -1;
+ pParent->nPath = (u32)p->path.nUsed;
+ if( p->eType && p->nParent ){
+ jsonAppendPathName(p);
+ if( p->path.eErr ) rc = SQLITE_NOMEM;
+ }
+ p->nParent++;
+ p->i = i + n;
+ }else{
+ p->i = i + n + sz;
+ }
+ while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){
+ p->nParent--;
+ p->path.nUsed = p->aParent[p->nParent].nPath;
+ levelChange = 1;
+ }
+ if( levelChange ){
+ if( p->nParent>0 ){
+ JsonParent *pParent = &p->aParent[p->nParent-1];
+ u32 iVal = pParent->iValue;
+ p->eType = p->sParse.aBlob[iVal] & 0x0f;
+ }else{
+ p->eType = 0;
}
}
}else{
- switch( p->eType ){
- case JSON_ARRAY: {
- p->i += jsonNodeSize(&p->sParse.aNode[p->i]);
- p->iRowid++;
- break;
- }
- case JSON_OBJECT: {
- p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]);
- p->iRowid++;
- break;
- }
- default: {
- p->i = p->iEnd;
- break;
- }
- }
+ u32 n, sz = 0;
+ u32 i = jsonSkipLabel(p);
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ p->i = i + n + sz;
}
- return SQLITE_OK;
+ if( p->eType==JSONB_ARRAY && p->nParent ){
+ p->aParent[p->nParent-1].iKey++;
+ }
+ p->iRowid++;
+ return rc;
}
-/* Append the name of the path for element i to pStr
+/* Length of the path for rowid==0 in bRecursive mode.
*/
-static void jsonEachComputePath(
- JsonEachCursor *p, /* The cursor */
- JsonString *pStr, /* Write the path here */
- u32 i /* Path to this element */
-){
- JsonNode *pNode, *pUp;
- u32 iUp;
- if( i==0 ){
- jsonAppendChar(pStr, '$');
- return;
- }
- iUp = p->sParse.aUp[i];
- jsonEachComputePath(p, pStr, iUp);
- pNode = &p->sParse.aNode[i];
- pUp = &p->sParse.aNode[iUp];
- if( pUp->eType==JSON_ARRAY ){
- jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
- }else{
- assert( pUp->eType==JSON_OBJECT );
- if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
- assert( pNode->eType==JSON_STRING );
- assert( pNode->jnFlags & JNODE_LABEL );
- jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
+static int jsonEachPathLength(JsonEachCursor *p){
+ u32 n = p->path.nUsed;
+ char *z = p->path.zBuf;
+ if( p->iRowid==0 && p->bRecursive && n>=2 ){
+ while( n>1 ){
+ n--;
+ if( z[n]=='[' || z[n]=='.' ){
+ u32 x, sz = 0;
+ char cSaved = z[n];
+ z[n] = 0;
+ assert( p->sParse.eEdit==0 );
+ x = jsonLookupStep(&p->sParse, 0, z+1, 0);
+ z[n] = cSaved;
+ if( JSON_LOOKUP_ISERROR(x) ) continue;
+ if( x + jsonbPayloadSize(&p->sParse, x, &sz) == p->i ) break;
+ }
+ }
}
+ return n;
}
/* Return the value of a column */
static int jsonEachColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
- int i /* Which column to return */
+ int iColumn /* Which column to return */
){
JsonEachCursor *p = (JsonEachCursor*)cur;
- JsonNode *pThis = &p->sParse.aNode[p->i];
- switch( i ){
+ switch( iColumn ){
case JEACH_KEY: {
- if( p->i==0 ) break;
- if( p->eType==JSON_OBJECT ){
- jsonReturn(pThis, ctx, 0);
- }else if( p->eType==JSON_ARRAY ){
- u32 iKey;
- if( p->bRecursive ){
- if( p->iRowid==0 ) break;
- iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey;
+ if( p->nParent==0 ){
+ u32 n, j;
+ if( p->nRoot==1 ) break;
+ j = jsonEachPathLength(p);
+ n = p->nRoot - j;
+ if( n==0 ){
+ break;
+ }else if( p->path.zBuf[j]=='[' ){
+ i64 x;
+ sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8);
+ sqlite3_result_int64(ctx, x);
+ }else if( p->path.zBuf[j+1]=='"' ){
+ sqlite3_result_text(ctx, &p->path.zBuf[j+2], n-3, SQLITE_TRANSIENT);
}else{
- iKey = p->iRowid;
+ sqlite3_result_text(ctx, &p->path.zBuf[j+1], n-1, SQLITE_TRANSIENT);
}
- sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
+ break;
+ }
+ if( p->eType==JSONB_OBJECT ){
+ jsonReturnFromBlob(&p->sParse, p->i, ctx, 1);
+ }else{
+ assert( p->eType==JSONB_ARRAY );
+ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey);
}
break;
}
case JEACH_VALUE: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- jsonReturn(pThis, ctx, 0);
+ u32 i = jsonSkipLabel(p);
+ jsonReturnFromBlob(&p->sParse, i, ctx, 1);
break;
}
case JEACH_TYPE: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
+ u32 i = jsonSkipLabel(p);
+ u8 eType = p->sParse.aBlob[i] & 0x0f;
+ sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC);
break;
}
case JEACH_ATOM: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- if( pThis->eType>=JSON_ARRAY ) break;
- jsonReturn(pThis, ctx, 0);
+ u32 i = jsonSkipLabel(p);
+ if( (p->sParse.aBlob[i] & 0x0f)<JSONB_ARRAY ){
+ jsonReturnFromBlob(&p->sParse, i, ctx, 1);
+ }
break;
}
case JEACH_ID: {
- sqlite3_result_int64(ctx,
- (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
+ sqlite3_result_int64(ctx, (sqlite3_int64)p->i);
break;
}
case JEACH_PARENT: {
- if( p->i>p->iBegin && p->bRecursive ){
- sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
+ if( p->nParent>0 && p->bRecursive ){
+ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead);
}
break;
}
case JEACH_FULLKEY: {
- JsonString x;
- jsonInit(&x, ctx);
- if( p->bRecursive ){
- jsonEachComputePath(p, &x, p->i);
- }else{
- if( p->zRoot ){
- jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
- }else{
- jsonAppendChar(&x, '$');
- }
- if( p->eType==JSON_ARRAY ){
- jsonPrintf(30, &x, "[%d]", p->iRowid);
- }else if( p->eType==JSON_OBJECT ){
- jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
- }
- }
- jsonResult(&x);
+ u64 nBase = p->path.nUsed;
+ if( p->nParent ) jsonAppendPathName(p);
+ sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed,
+ SQLITE_TRANSIENT, SQLITE_UTF8);
+ p->path.nUsed = nBase;
break;
}
case JEACH_PATH: {
- if( p->bRecursive ){
- JsonString x;
- jsonInit(&x, ctx);
- jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
- jsonResult(&x);
- break;
- }
- /* For json_each() path and root are the same so fall through
- ** into the root case */
+ u32 n = jsonEachPathLength(p);
+ sqlite3_result_text64(ctx, p->path.zBuf, n,
+ SQLITE_TRANSIENT, SQLITE_UTF8);
+ break;
}
default: {
- const char *zRoot = p->zRoot;
- if( zRoot==0 ) zRoot = "$";
- sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
+ sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC);
break;
}
case JEACH_JSON: {
- assert( i==JEACH_JSON );
- sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+ if( p->sParse.zJson==0 ){
+ sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob,
+ SQLITE_STATIC);
+ }else{
+ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+ }
break;
}
}
@@ -178844,7 +208167,7 @@ static int jsonEachBestIndex(
/* This implementation assumes that JSON and ROOT are the last two
** columns in the table */
assert( JEACH_ROOT == JEACH_JSON+1 );
- UNUSED_PARAM(tab);
+ UNUSED_PARAMETER(tab);
aIdx[0] = aIdx[1] = -1;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
@@ -178853,6 +208176,7 @@ static int jsonEachBestIndex(
if( pConstraint->iColumn < JEACH_JSON ) continue;
iCol = pConstraint->iColumn - JEACH_JSON;
assert( iCol==0 || iCol==1 );
+ testcase( iCol==0 );
iMask = 1 << iCol;
if( pConstraint->usable==0 ){
unusableMask |= iMask;
@@ -178861,6 +208185,13 @@ static int jsonEachBestIndex(
idxMask |= iMask;
}
}
+ if( pIdxInfo->nOrderBy>0
+ && pIdxInfo->aOrderBy[0].iColumn<0
+ && pIdxInfo->aOrderBy[0].desc==0
+ ){
+ pIdxInfo->orderByConsumed = 1;
+ }
+
if( (unusableMask & ~idxMask)!=0 ){
/* If there are any unusable constraints on JSON or ROOT, then reject
** this entire plan */
@@ -178895,76 +208226,97 @@ static int jsonEachFilter(
int argc, sqlite3_value **argv
){
JsonEachCursor *p = (JsonEachCursor*)cur;
- const char *z;
const char *zRoot = 0;
- sqlite3_int64 n;
+ u32 i, n, sz;
- UNUSED_PARAM(idxStr);
- UNUSED_PARAM(argc);
+ UNUSED_PARAMETER(idxStr);
+ UNUSED_PARAMETER(argc);
jsonEachCursorReset(p);
if( idxNum==0 ) return SQLITE_OK;
- z = (const char*)sqlite3_value_text(argv[0]);
- if( z==0 ) return SQLITE_OK;
- n = sqlite3_value_bytes(argv[0]);
- p->zJson = sqlite3_malloc64( n+1 );
- if( p->zJson==0 ) return SQLITE_NOMEM;
- memcpy(p->zJson, z, (size_t)n+1);
- if( jsonParse(&p->sParse, 0, p->zJson) ){
- int rc = SQLITE_NOMEM;
- if( p->sParse.oom==0 ){
- sqlite3_free(cur->pVtab->zErrMsg);
- cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
- if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
+ memset(&p->sParse, 0, sizeof(p->sParse));
+ p->sParse.nJPRef = 1;
+ p->sParse.db = p->db;
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
+ p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ }else{
+ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
+ p->sParse.nJson = sqlite3_value_bytes(argv[0]);
+ if( p->sParse.zJson==0 ){
+ p->i = p->iEnd = 0;
+ return SQLITE_OK;
}
- jsonEachCursorReset(p);
- return rc;
- }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
- jsonEachCursorReset(p);
- return SQLITE_NOMEM;
- }else{
- JsonNode *pNode = 0;
- if( idxNum==3 ){
- const char *zErr = 0;
- zRoot = (const char*)sqlite3_value_text(argv[1]);
- if( zRoot==0 ) return SQLITE_OK;
- n = sqlite3_value_bytes(argv[1]);
- p->zRoot = sqlite3_malloc64( n+1 );
- if( p->zRoot==0 ) return SQLITE_NOMEM;
- memcpy(p->zRoot, zRoot, (size_t)n+1);
- if( zRoot[0]!='$' ){
- zErr = zRoot;
- }else{
- pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
+ if( jsonConvertTextToBlob(&p->sParse, 0) ){
+ if( p->sParse.oom ){
+ return SQLITE_NOMEM;
}
- if( zErr ){
+ goto json_each_malformed_input;
+ }
+ }
+ if( idxNum==3 ){
+ zRoot = (const char*)sqlite3_value_text(argv[1]);
+ if( zRoot==0 ) return SQLITE_OK;
+ if( zRoot[0]!='$' ){
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
+ jsonEachCursorReset(p);
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
+ }
+ p->nRoot = sqlite3Strlen30(zRoot);
+ if( zRoot[1]==0 ){
+ i = p->i = 0;
+ p->eType = 0;
+ }else{
+ i = jsonLookupStep(&p->sParse, 0, zRoot+1, 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ p->i = 0;
+ p->eType = 0;
+ p->iEnd = 0;
+ return SQLITE_OK;
+ }
sqlite3_free(cur->pVtab->zErrMsg);
- cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
+ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
jsonEachCursorReset(p);
return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
- }else if( pNode==0 ){
- return SQLITE_OK;
}
- }else{
- pNode = p->sParse.aNode;
- }
- p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
- p->eType = pNode->eType;
- if( p->eType>=JSON_ARRAY ){
- pNode->u.iKey = 0;
- p->iEnd = p->i + pNode->n + 1;
- if( p->bRecursive ){
- p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
- if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
- p->i--;
- }
+ if( p->sParse.iLabel ){
+ p->i = p->sParse.iLabel;
+ p->eType = JSONB_OBJECT;
}else{
- p->i++;
- }
- }else{
- p->iEnd = p->i+1;
- }
+ p->i = i;
+ p->eType = JSONB_ARRAY;
+ }
+ }
+ jsonAppendRaw(&p->path, zRoot, p->nRoot);
+ }else{
+ i = p->i = 0;
+ p->eType = 0;
+ p->nRoot = 1;
+ jsonAppendRaw(&p->path, "$", 1);
+ }
+ p->nParent = 0;
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ p->iEnd = i+n+sz;
+ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){
+ p->i = i + n;
+ p->eType = p->sParse.aBlob[i] & 0x0f;
+ p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent));
+ if( p->aParent==0 ) return SQLITE_NOMEM;
+ p->nParent = 1;
+ p->nParentAlloc = 1;
+ p->aParent[0].iKey = 0;
+ p->aParent[0].iEnd = p->iEnd;
+ p->aParent[0].iHead = p->i;
+ p->aParent[0].iValue = i;
}
return SQLITE_OK;
+
+json_each_malformed_input:
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
+ jsonEachCursorReset(p);
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
}
/* The methods of the json_each virtual table */
@@ -178992,7 +208344,8 @@ static sqlite3_module jsonEachModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/* The methods of the json_tree virtual table. */
@@ -179020,108 +208373,96 @@ static sqlite3_module jsonTreeModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
-
-/****************************************************************************
-** The following routines are the only publically visible identifiers in this
-** file. Call the following routines in order to register the various SQL
-** functions and the virtual table implemented by this file.
-****************************************************************************/
-
-SQLITE_PRIVATE int sqlite3Json1Init(sqlite3 *db){
- int rc = SQLITE_OK;
- unsigned int i;
- static const struct {
- const char *zName;
- int nArg;
- int flag;
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
- } aFunc[] = {
- { "json", 1, 0, jsonRemoveFunc },
- { "json_array", -1, 0, jsonArrayFunc },
- { "json_array_length", 1, 0, jsonArrayLengthFunc },
- { "json_array_length", 2, 0, jsonArrayLengthFunc },
- { "json_extract", -1, 0, jsonExtractFunc },
- { "json_insert", -1, 0, jsonSetFunc },
- { "json_object", -1, 0, jsonObjectFunc },
- { "json_patch", 2, 0, jsonPatchFunc },
- { "json_quote", 1, 0, jsonQuoteFunc },
- { "json_remove", -1, 0, jsonRemoveFunc },
- { "json_replace", -1, 0, jsonReplaceFunc },
- { "json_set", -1, 1, jsonSetFunc },
- { "json_type", 1, 0, jsonTypeFunc },
- { "json_type", 2, 0, jsonTypeFunc },
- { "json_valid", 1, 0, jsonValidFunc },
-
+#endif /* !defined(SQLITE_OMIT_JSON) */
+
+/*
+** Register JSON functions.
+*/
+SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
+#ifndef SQLITE_OMIT_JSON
+ static FuncDef aJsonFunc[] = {
+ /* sqlite3_result_subtype() ----, ,--- sqlite3_value_subtype() */
+ /* | | */
+ /* Uses cache ------, | | ,---- Returns JSONB */
+ /* | | | | */
+ /* Number of arguments ---, | | | | ,--- Flags */
+ /* | | | | | | */
+ JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc),
+ JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc),
+ JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc),
+ JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc),
+ JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc),
+ JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc),
+ JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc),
+ JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc),
+ JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc),
+ JFUNCTION(->, 2,1,1, 0,0,JSON_JSON, jsonExtractFunc),
+ JFUNCTION(->>, 2,1,0, 0,0,JSON_SQL, jsonExtractFunc),
+ JFUNCTION(json_insert, -1,1,1, 1,0,0, jsonSetFunc),
+ JFUNCTION(jsonb_insert, -1,1,0, 1,1,0, jsonSetFunc),
+ JFUNCTION(json_object, -1,0,1, 1,0,0, jsonObjectFunc),
+ JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc),
+ JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc),
+ JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc),
+ JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc),
+ JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc),
+ JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc),
+ JFUNCTION(json_replace, -1,1,1, 1,0,0, jsonReplaceFunc),
+ JFUNCTION(jsonb_replace, -1,1,0, 1,1,0, jsonReplaceFunc),
+ JFUNCTION(json_set, -1,1,1, 1,0,JSON_ISSET, jsonSetFunc),
+ JFUNCTION(jsonb_set, -1,1,0, 1,1,JSON_ISSET, jsonSetFunc),
+ JFUNCTION(json_type, 1,1,0, 0,0,0, jsonTypeFunc),
+ JFUNCTION(json_type, 2,1,0, 0,0,0, jsonTypeFunc),
+ JFUNCTION(json_valid, 1,1,0, 0,0,0, jsonValidFunc),
+ JFUNCTION(json_valid, 2,1,0, 0,0,0, jsonValidFunc),
#if SQLITE_DEBUG
- /* DEBUG and TESTING functions */
- { "json_parse", 1, 0, jsonParseFunc },
- { "json_test1", 1, 0, jsonTest1Func },
-#endif
- };
- static const struct {
- const char *zName;
- int nArg;
- void (*xStep)(sqlite3_context*,int,sqlite3_value**);
- void (*xFinal)(sqlite3_context*);
- void (*xValue)(sqlite3_context*);
- } aAgg[] = {
- { "json_group_array", 1,
- jsonArrayStep, jsonArrayFinal, jsonArrayValue },
- { "json_group_object", 2,
- jsonObjectStep, jsonObjectFinal, jsonObjectValue },
+ JFUNCTION(json_parse, 1,1,0, 0,0,0, jsonParseFunc),
+#endif
+ WAGGREGATE(json_group_array, 1, 0, 0,
+ jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
+ SQLITE_DETERMINISTIC),
+ WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0,
+ jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
+ WAGGREGATE(json_group_object, 2, 0, 0,
+ jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
+ WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0,
+ jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
+ SQLITE_DETERMINISTIC)
};
-#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
+#endif
+}
+
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
+/*
+** Register the JSON table-valued functions
+*/
+SQLITE_PRIVATE int sqlite3JsonTableFunctions(sqlite3 *db){
+ int rc = SQLITE_OK;
static const struct {
- const char *zName;
- sqlite3_module *pModule;
+ const char *zName;
+ sqlite3_module *pModule;
} aMod[] = {
{ "json_each", &jsonEachModule },
{ "json_tree", &jsonTreeModule },
};
-#endif
- for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
- rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
- SQLITE_UTF8 | SQLITE_DETERMINISTIC,
- (void*)&aFunc[i].flag,
- aFunc[i].xFunc, 0, 0);
- }
-#ifndef SQLITE_OMIT_WINDOWFUNC
- for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
- rc = sqlite3_create_window_function(db, aAgg[i].zName, aAgg[i].nArg,
- SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
- aAgg[i].xStep, aAgg[i].xFinal,
- aAgg[i].xValue, jsonGroupInverse, 0);
- }
-#endif
-#ifndef SQLITE_OMIT_VIRTUALTABLE
+ unsigned int i;
for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
}
-#endif
return rc;
}
+#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) */
-
-#ifndef SQLITE_CORE
-#ifdef _WIN32
-__declspec(dllexport)
-#endif
-SQLITE_API int sqlite3_json_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
-){
- SQLITE_EXTENSION_INIT2(pApi);
- (void)pzErrMsg; /* Unused parameter */
- return sqlite3Json1Init(db);
-}
-#endif
-#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) */
-
-/************** End of json1.c ***********************************************/
+/************** End of json.c ************************************************/
/************** Begin file rtree.c *******************************************/
/*
** 2001 September 15
@@ -179142,7 +208483,7 @@ SQLITE_API int sqlite3_json_init(
** Database Format of R-Tree Tables
** --------------------------------
**
-** The data structure for a single virtual r-tree table is stored in three
+** The data structure for a single virtual r-tree table is stored in three
** native SQLite tables declared as follows. In each case, the '%' character
** in the table name is replaced with the user-supplied name of the r-tree
** table.
@@ -179168,7 +208509,7 @@ SQLITE_API int sqlite3_json_init(
** of the node contain the tree depth as a big-endian integer.
** For non-root nodes, the first 2 bytes are left unused.
**
-** 2. The next 2 bytes contain the number of entries currently
+** 2. The next 2 bytes contain the number of entries currently
** stored in the node.
**
** 3. The remainder of the node contains the node entries. Each entry
@@ -179187,20 +208528,50 @@ SQLITE_API int sqlite3_json_init(
#else
/* #include "sqlite3.h" */
#endif
+SQLITE_PRIVATE int sqlite3GetToken(const unsigned char*,int*); /* In the SQLite core */
-/* #include <string.h> */
-/* #include <assert.h> */
-/* #include <stdio.h> */
-
-#ifndef SQLITE_AMALGAMATION
+/*
+** If building separately, we will need some setup that is normally
+** found in sqliteInt.h
+*/
+#if !defined(SQLITE_AMALGAMATION)
#include "sqlite3rtree.h"
typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+# define NDEBUG 1
+#endif
+#if defined(NDEBUG) && defined(SQLITE_DEBUG)
+# undef NDEBUG
+#endif
+#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) */
+
+/* Macro to check for 4-byte alignment. Only used inside of assert() */
+#ifdef SQLITE_DEBUG
+# define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0)
#endif
+/* #include <string.h> */
+/* #include <stdio.h> */
+/* #include <assert.h> */
+/* #include <stdlib.h> */
+
/* The following macro is used to suppress compiler warnings.
*/
#ifndef UNUSED_PARAMETER
@@ -179224,7 +208595,7 @@ typedef struct RtreeSearchPoint RtreeSearchPoint;
#define RTREE_MAX_AUX_COLUMN 100
/* Size of hash table Rtree.aHash. This hash table is not expected to
-** ever contain very many entries, so a fixed number of buckets is
+** ever contain very many entries, so a fixed number of buckets is
** used.
*/
#define HASHSIZE 97
@@ -179233,13 +208604,13 @@ typedef struct RtreeSearchPoint RtreeSearchPoint;
** the number of rows in the virtual table to calculate the costs of
** various strategies. If possible, this estimate is loaded from the
** sqlite_stat1 table (with RTREE_MIN_ROWEST as a hard-coded minimum).
-** Otherwise, if no sqlite_stat1 entry is available, use
+** Otherwise, if no sqlite_stat1 entry is available, use
** RTREE_DEFAULT_ROWEST.
*/
#define RTREE_DEFAULT_ROWEST 1048576
#define RTREE_MIN_ROWEST 100
-/*
+/*
** An rtree virtual-table object.
*/
struct Rtree {
@@ -179252,10 +208623,16 @@ struct Rtree {
u8 nBytesPerCell; /* Bytes consumed per cell */
u8 inWrTrans; /* True if inside write transaction */
u8 nAux; /* # of auxiliary columns in %_rowid */
+#ifdef SQLITE_ENABLE_GEOPOLY
u8 nAuxNotNull; /* Number of initial not-null aux columns */
+#endif
+#ifdef SQLITE_DEBUG
+ u8 bCorrupt; /* Shadow table corruption detected */
+#endif
int iDepth; /* Current depth of the r-tree structure */
char *zDb; /* Name of database containing r-tree table */
- char *zName; /* Name of r-tree table */
+ char *zName; /* Name of r-tree table */
+ char *zNodeName; /* Name of the %_node table */
u32 nBusy; /* Current number of users of this structure */
i64 nRowEst; /* Estimated number of rows in this table */
u32 nCursor; /* Number of open cursors */
@@ -179264,11 +208641,10 @@ struct Rtree {
/* List of nodes removed during a CondenseTree operation. List is
** linked together via the pointer normally used for hash chains -
- ** RtreeNode.pNext. RtreeNode.iNode stores the depth of the sub-tree
+ ** RtreeNode.pNext. RtreeNode.iNode stores the depth of the sub-tree
** headed by the node (leaf nodes have RtreeNode.iNode==0).
*/
RtreeNode *pDeleted;
- int iReinsertHeight; /* Height of sub-trees Reinsert() has run on */
/* Blob I/O on xxx_node */
sqlite3_blob *pNodeBlob;
@@ -179290,7 +208666,7 @@ struct Rtree {
/* Statement for writing to the "aux:" fields, if there are any */
sqlite3_stmt *pWriteAux;
- RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
+ RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
};
/* Possible values for Rtree.eCoordType: */
@@ -179313,6 +208689,15 @@ struct Rtree {
#endif
/*
+** Set the Rtree.bCorrupt flag
+*/
+#ifdef SQLITE_DEBUG
+# define RTREE_IS_CORRUPT(X) ((X)->bCorrupt = 1)
+#else
+# define RTREE_IS_CORRUPT(X)
+#endif
+
+/*
** When doing a search of an r-tree, instances of the following structure
** record intermediate results from the tree walk.
**
@@ -179330,7 +208715,7 @@ struct RtreeSearchPoint {
};
/*
-** The minimum number of cells allowed for a node is a third of the
+** The minimum number of cells allowed for a node is a third of the
** maximum. In Gutman's notation:
**
** m = M/3
@@ -179345,7 +208730,7 @@ struct RtreeSearchPoint {
/*
** The smallest possible node-size is (512-64)==448 bytes. And the largest
** supported cell size is 48 bytes (8 byte rowid + ten 4 byte coordinates).
-** Therefore all non-root nodes must contain at least 3 entries. Since
+** Therefore all non-root nodes must contain at least 3 entries. Since
** 3^40 is greater than 2^64, an r-tree structure always has a depth of
** 40 or less.
*/
@@ -179359,7 +208744,7 @@ struct RtreeSearchPoint {
*/
#define RTREE_CACHE_SZ 5
-/*
+/*
** An rtree cursor object.
*/
struct RtreeCursor {
@@ -179432,8 +208817,14 @@ struct RtreeConstraint {
#define RTREE_MATCH 0x46 /* F: Old-style sqlite3_rtree_geometry_callback() */
#define RTREE_QUERY 0x47 /* G: New-style sqlite3_rtree_query_callback() */
+/* Special operators available only on cursors. Needs to be consecutive
+** with the normal values above, but must be less than RTREE_MATCH. These
+** are used in the cursor for contraints such as x=NULL (RTREE_FALSE) or
+** x<'xyz' (RTREE_TRUE) */
+#define RTREE_TRUE 0x3f /* ? */
+#define RTREE_FALSE 0x40 /* @ */
-/*
+/*
** An rtree structure node.
*/
struct RtreeNode {
@@ -179448,7 +208839,7 @@ struct RtreeNode {
/* Return the number of cells in a node */
#define NCELL(pNode) readInt16(&(pNode)->zData[2])
-/*
+/*
** A single cell from a node, deserialized
*/
struct RtreeCell {
@@ -179463,11 +208854,11 @@ struct RtreeCell {
** sqlite3_rtree_query_callback() and which appear on the right of MATCH
** operators in order to constrain a search.
**
-** xGeom and xQueryFunc are the callback functions. Exactly one of
+** xGeom and xQueryFunc are the callback functions. Exactly one of
** xGeom and xQueryFunc fields is non-NULL, depending on whether the
** SQL function was created using sqlite3_rtree_geometry_callback() or
** sqlite3_rtree_query_callback().
-**
+**
** This object is deleted automatically by the destructor mechanism in
** sqlite3_create_function_v2().
*/
@@ -179516,7 +208907,29 @@ struct RtreeMatchArg {
** it is not, make it a no-op.
*/
#ifndef SQLITE_AMALGAMATION
-# define testcase(X)
+# if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG)
+ unsigned int sqlite3RtreeTestcase = 0;
+# define testcase(X) if( X ){ sqlite3RtreeTestcase += __LINE__; }
+# else
+# define testcase(X)
+# endif
+#endif
+
+/*
+** Make sure that the compiler intrinsics we desire are enabled when
+** compiling with an appropriate version of MSVC unless prevented by
+** the SQLITE_DISABLE_INTRINSIC define.
+*/
+#if !defined(SQLITE_DISABLE_INTRINSIC)
+# if defined(_MSC_VER) && _MSC_VER>=1400
+# if !defined(_WIN32_WCE)
+/* # include <intrin.h> */
+# pragma intrinsic(_byteswap_ulong)
+# pragma intrinsic(_byteswap_uint64)
+# else
+/* # include <cmnintrin.h> */
+# endif
+# endif
#endif
/*
@@ -179528,17 +208941,23 @@ struct RtreeMatchArg {
** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined
** at run-time.
*/
-#ifndef SQLITE_BYTEORDER
-#if defined(i386) || defined(__i386__) || defined(_M_IX86) || \
- defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
- defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
- defined(__arm__)
-# define SQLITE_BYTEORDER 1234
-#elif defined(sparc) || defined(__ppc__)
-# define SQLITE_BYTEORDER 4321
-#else
-# define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */
-#endif
+#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */
+# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
+# define SQLITE_BYTEORDER 4321
+# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
+# define SQLITE_BYTEORDER 1234
+# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1
+# define SQLITE_BYTEORDER 4321
+# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
+ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
+ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
+ defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64)
+# define SQLITE_BYTEORDER 1234
+# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__)
+# define SQLITE_BYTEORDER 4321
+# else
+# define SQLITE_BYTEORDER 0
+# endif
#endif
@@ -179559,7 +208978,7 @@ static int readInt16(u8 *p){
return (p[0]<<8) + p[1];
}
static void readCoord(u8 *p, RtreeCoord *pCoord){
- assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */
+ assert( FOUR_BYTE_ALIGNED(p) );
#if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
pCoord->u = _byteswap_ulong(*(u32*)p);
#elif SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000
@@ -179568,9 +208987,9 @@ static void readCoord(u8 *p, RtreeCoord *pCoord){
pCoord->u = *(u32*)p;
#else
pCoord->u = (
- (((u32)p[0]) << 24) +
- (((u32)p[1]) << 16) +
- (((u32)p[2]) << 8) +
+ (((u32)p[0]) << 24) +
+ (((u32)p[1]) << 16) +
+ (((u32)p[2]) << 8) +
(((u32)p[3]) << 0)
);
#endif
@@ -179590,13 +209009,13 @@ static i64 readInt64(u8 *p){
return x;
#else
return (i64)(
- (((u64)p[0]) << 56) +
- (((u64)p[1]) << 48) +
- (((u64)p[2]) << 40) +
- (((u64)p[3]) << 32) +
- (((u64)p[4]) << 24) +
- (((u64)p[5]) << 16) +
- (((u64)p[6]) << 8) +
+ (((u64)p[0]) << 56) +
+ (((u64)p[1]) << 48) +
+ (((u64)p[2]) << 40) +
+ (((u64)p[3]) << 32) +
+ (((u64)p[4]) << 24) +
+ (((u64)p[5]) << 16) +
+ (((u64)p[6]) << 8) +
(((u64)p[7]) << 0)
);
#endif
@@ -179613,7 +209032,7 @@ static void writeInt16(u8 *p, int i){
}
static int writeCoord(u8 *p, RtreeCoord *pCoord){
u32 i;
- assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */
+ assert( FOUR_BYTE_ALIGNED(p) );
assert( sizeof(RtreeCoord)==4 );
assert( sizeof(u32)==4 );
#if SQLITE_BYTEORDER==1234 && GCC_VERSION>=4003000
@@ -179678,8 +209097,8 @@ static void nodeZero(Rtree *pRtree, RtreeNode *p){
** Given a node number iNode, return the corresponding key to use
** in the Rtree.aHash table.
*/
-static int nodeHash(i64 iNode){
- return iNode % HASHSIZE;
+static unsigned int nodeHash(i64 iNode){
+ return ((unsigned)iNode) % HASHSIZE;
}
/*
@@ -179724,7 +209143,7 @@ static void nodeHashDelete(Rtree *pRtree, RtreeNode *pNode){
*/
static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
RtreeNode *pNode;
- pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode) + pRtree->iNodeSize);
+ pNode = (RtreeNode *)sqlite3_malloc64(sizeof(RtreeNode) + pRtree->iNodeSize);
if( pNode ){
memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize);
pNode->zData = (u8 *)&pNode[1];
@@ -179764,10 +209183,9 @@ static int nodeAcquire(
** increase its reference count and return it.
*/
if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
- assert( !pParent || !pNode->pParent || pNode->pParent==pParent );
- if( pParent && !pNode->pParent ){
- pParent->nRef++;
- pNode->pParent = pParent;
+ if( pParent && ALWAYS(pParent!=pNode->pParent) ){
+ RTREE_IS_CORRUPT(pRtree);
+ return SQLITE_CORRUPT_VTAB;
}
pNode->nRef++;
*ppNode = pNode;
@@ -179785,20 +209203,21 @@ static int nodeAcquire(
}
}
if( pRtree->pNodeBlob==0 ){
- char *zTab = sqlite3_mprintf("%s_node", pRtree->zName);
- if( zTab==0 ) return SQLITE_NOMEM;
- rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, zTab, "data", iNode, 0,
+ rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, pRtree->zNodeName,
+ "data", iNode, 0,
&pRtree->pNodeBlob);
- sqlite3_free(zTab);
}
if( rc ){
nodeBlobReset(pRtree);
*ppNode = 0;
/* If unable to open an sqlite3_blob on the desired row, that can only
** be because the shadow tables hold erroneous data. */
- if( rc==SQLITE_ERROR ) rc = SQLITE_CORRUPT_VTAB;
+ if( rc==SQLITE_ERROR ){
+ rc = SQLITE_CORRUPT_VTAB;
+ RTREE_IS_CORRUPT(pRtree);
+ }
}else if( pRtree->iNodeSize==sqlite3_blob_bytes(pRtree->pNodeBlob) ){
- pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize);
+ pNode = (RtreeNode *)sqlite3_malloc64(sizeof(RtreeNode)+pRtree->iNodeSize);
if( !pNode ){
rc = SQLITE_NOMEM;
}else{
@@ -179811,7 +209230,6 @@ static int nodeAcquire(
pNode->pNext = 0;
rc = sqlite3_blob_read(pRtree->pNodeBlob, pNode->zData,
pRtree->iNodeSize, 0);
- nodeReference(pParent);
}
}
@@ -179821,28 +209239,32 @@ static int nodeAcquire(
** are the leaves, and so on. If the depth as specified on the root node
** is greater than RTREE_MAX_DEPTH, the r-tree structure must be corrupt.
*/
- if( pNode && iNode==1 ){
+ if( rc==SQLITE_OK && pNode && iNode==1 ){
pRtree->iDepth = readInt16(pNode->zData);
if( pRtree->iDepth>RTREE_MAX_DEPTH ){
rc = SQLITE_CORRUPT_VTAB;
+ RTREE_IS_CORRUPT(pRtree);
}
}
/* If no error has occurred so far, check if the "number of entries"
- ** field on the node is too large. If so, set the return code to
+ ** field on the node is too large. If so, set the return code to
** SQLITE_CORRUPT_VTAB.
*/
if( pNode && rc==SQLITE_OK ){
if( NCELL(pNode)>((pRtree->iNodeSize-4)/pRtree->nBytesPerCell) ){
rc = SQLITE_CORRUPT_VTAB;
+ RTREE_IS_CORRUPT(pRtree);
}
}
if( rc==SQLITE_OK ){
if( pNode!=0 ){
+ nodeReference(pParent);
nodeHashInsert(pRtree, pNode);
}else{
rc = SQLITE_CORRUPT_VTAB;
+ RTREE_IS_CORRUPT(pRtree);
}
*ppNode = pNode;
}else{
@@ -180025,7 +209447,7 @@ static int rtreeInit(
sqlite3 *, void *, int, const char *const*, sqlite3_vtab **, char **, int
);
-/*
+/*
** Rtree virtual table module xCreate method.
*/
static int rtreeCreate(
@@ -180038,7 +209460,7 @@ static int rtreeCreate(
return rtreeInit(db, pAux, argc, argv, ppVtab, pzErr, 1);
}
-/*
+/*
** Rtree virtual table module xConnect method.
*/
static int rtreeConnect(
@@ -180068,7 +209490,7 @@ static void rtreeRelease(Rtree *pRtree){
pRtree->inWrTrans = 0;
assert( pRtree->nCursor==0 );
nodeBlobReset(pRtree);
- assert( pRtree->nNodeRef==0 );
+ assert( pRtree->nNodeRef==0 || pRtree->bCorrupt );
sqlite3_finalize(pRtree->pWriteNode);
sqlite3_finalize(pRtree->pDeleteNode);
sqlite3_finalize(pRtree->pReadRowid);
@@ -180083,7 +209505,7 @@ static void rtreeRelease(Rtree *pRtree){
}
}
-/*
+/*
** Rtree virtual table module xDisconnect method.
*/
static int rtreeDisconnect(sqlite3_vtab *pVtab){
@@ -180091,7 +209513,7 @@ static int rtreeDisconnect(sqlite3_vtab *pVtab){
return SQLITE_OK;
}
-/*
+/*
** Rtree virtual table module xDestroy method.
*/
static int rtreeDestroy(sqlite3_vtab *pVtab){
@@ -180101,7 +209523,7 @@ static int rtreeDestroy(sqlite3_vtab *pVtab){
"DROP TABLE '%q'.'%q_node';"
"DROP TABLE '%q'.'%q_rowid';"
"DROP TABLE '%q'.'%q_parent';",
- pRtree->zDb, pRtree->zName,
+ pRtree->zDb, pRtree->zName,
pRtree->zDb, pRtree->zName,
pRtree->zDb, pRtree->zName
);
@@ -180119,7 +209541,7 @@ static int rtreeDestroy(sqlite3_vtab *pVtab){
return rc;
}
-/*
+/*
** Rtree virtual table module xOpen method.
*/
static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
@@ -180127,7 +209549,7 @@ static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
Rtree *pRtree = (Rtree *)pVTab;
RtreeCursor *pCsr;
- pCsr = (RtreeCursor *)sqlite3_malloc(sizeof(RtreeCursor));
+ pCsr = (RtreeCursor *)sqlite3_malloc64(sizeof(RtreeCursor));
if( pCsr ){
memset(pCsr, 0, sizeof(RtreeCursor));
pCsr->base.pVtab = pVTab;
@@ -180141,9 +209563,12 @@ static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
/*
-** Free the RtreeCursor.aConstraint[] array and its contents.
+** Reset a cursor back to its initial state.
*/
-static void freeCursorConstraints(RtreeCursor *pCsr){
+static void resetCursor(RtreeCursor *pCsr){
+ Rtree *pRtree = (Rtree *)(pCsr->base.pVtab);
+ int ii;
+ sqlite3_stmt *pStmt;
if( pCsr->aConstraint ){
int i; /* Used to iterate through constraint array */
for(i=0; i<pCsr->nConstraint; i++){
@@ -180156,20 +209581,24 @@ static void freeCursorConstraints(RtreeCursor *pCsr){
sqlite3_free(pCsr->aConstraint);
pCsr->aConstraint = 0;
}
+ for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
+ sqlite3_free(pCsr->aPoint);
+ pStmt = pCsr->pReadAux;
+ memset(pCsr, 0, sizeof(RtreeCursor));
+ pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
+ pCsr->pReadAux = pStmt;
+
}
-/*
+/*
** Rtree virtual table module xClose method.
*/
static int rtreeClose(sqlite3_vtab_cursor *cur){
Rtree *pRtree = (Rtree *)(cur->pVtab);
- int ii;
RtreeCursor *pCsr = (RtreeCursor *)cur;
assert( pRtree->nCursor>0 );
- freeCursorConstraints(pCsr);
+ resetCursor(pCsr);
sqlite3_finalize(pCsr->pReadAux);
- sqlite3_free(pCsr->aPoint);
- for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
sqlite3_free(pCsr);
pRtree->nCursor--;
nodeBlobReset(pRtree);
@@ -180179,7 +209608,7 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
/*
** Rtree virtual table module xEof method.
**
-** Return non-zero if the cursor does not currently point to a valid
+** 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 rtreeEof(sqlite3_vtab_cursor *cur){
@@ -180235,7 +209664,7 @@ static int rtreeEof(sqlite3_vtab_cursor *cur){
/*
** Check the RTree node or entry given by pCellData and p against the MATCH
-** constraint pConstraint.
+** constraint pConstraint.
*/
static int rtreeCallbackConstraint(
RtreeConstraint *pConstraint, /* The constraint to test */
@@ -180308,7 +209737,7 @@ static int rtreeCallbackConstraint(
return rc;
}
-/*
+/*
** Check the internal RTree node given by pCellData against constraint p.
** If this constraint cannot be satisfied by any child within the node,
** set *peWithin to NOT_WITHIN.
@@ -180326,24 +209755,36 @@ static void rtreeNonleafConstraint(
*/
pCellData += 8 + 4*(p->iCoord&0xfe);
- assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
- || p->op==RTREE_GT || p->op==RTREE_EQ );
- assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */
+ assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
+ || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE
+ || p->op==RTREE_FALSE );
+ assert( FOUR_BYTE_ALIGNED(pCellData) );
switch( p->op ){
+ case RTREE_TRUE: return; /* Always satisfied */
+ case RTREE_FALSE: break; /* Never satisfied */
+ case RTREE_EQ:
+ RTREE_DECODE_COORD(eInt, pCellData, val);
+ /* val now holds the lower bound of the coordinate pair */
+ if( p->u.rValue>=val ){
+ pCellData += 4;
+ RTREE_DECODE_COORD(eInt, pCellData, val);
+ /* val now holds the upper bound of the coordinate pair */
+ if( p->u.rValue<=val ) return;
+ }
+ break;
case RTREE_LE:
case RTREE_LT:
- case RTREE_EQ:
RTREE_DECODE_COORD(eInt, pCellData, val);
/* val now holds the lower bound of the coordinate pair */
if( p->u.rValue>=val ) return;
- if( p->op!=RTREE_EQ ) break; /* RTREE_LE and RTREE_LT end here */
- /* Fall through for the RTREE_EQ case */
+ break;
- default: /* RTREE_GT or RTREE_GE, or fallthrough of RTREE_EQ */
+ default:
pCellData += 4;
RTREE_DECODE_COORD(eInt, pCellData, val);
/* val now holds the upper bound of the coordinate pair */
if( p->u.rValue<=val ) return;
+ break;
}
*peWithin = NOT_WITHIN;
}
@@ -180366,28 +209807,31 @@ static void rtreeLeafConstraint(
){
RtreeDValue xN; /* Coordinate value converted to a double */
- assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
- || p->op==RTREE_GT || p->op==RTREE_EQ );
+ assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
+ || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_TRUE
+ || p->op==RTREE_FALSE );
pCellData += 8 + p->iCoord*4;
- assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */
+ assert( FOUR_BYTE_ALIGNED(pCellData) );
RTREE_DECODE_COORD(eInt, pCellData, xN);
switch( p->op ){
- case RTREE_LE: if( xN <= p->u.rValue ) return; break;
- case RTREE_LT: if( xN < p->u.rValue ) return; break;
- case RTREE_GE: if( xN >= p->u.rValue ) return; break;
- case RTREE_GT: if( xN > p->u.rValue ) return; break;
- default: if( xN == p->u.rValue ) return; break;
+ case RTREE_TRUE: return; /* Always satisfied */
+ case RTREE_FALSE: break; /* Never satisfied */
+ case RTREE_LE: if( xN <= p->u.rValue ) return; break;
+ case RTREE_LT: if( xN < p->u.rValue ) return; break;
+ case RTREE_GE: if( xN >= p->u.rValue ) return; break;
+ case RTREE_GT: if( xN > p->u.rValue ) return; break;
+ default: if( xN == p->u.rValue ) return; break;
}
*peWithin = NOT_WITHIN;
}
/*
-** One of the cells in node pNode is guaranteed to have a 64-bit
+** One of the cells in node pNode is guaranteed to have a 64-bit
** integer value equal to iRowid. Return the index of this cell.
*/
static int nodeRowidIndex(
- Rtree *pRtree,
- RtreeNode *pNode,
+ Rtree *pRtree,
+ RtreeNode *pNode,
i64 iRowid,
int *piIndex
){
@@ -180400,6 +209844,7 @@ static int nodeRowidIndex(
return SQLITE_OK;
}
}
+ RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
}
@@ -180409,11 +209854,12 @@ static int nodeRowidIndex(
*/
static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){
RtreeNode *pParent = pNode->pParent;
- if( pParent ){
+ if( ALWAYS(pParent) ){
return nodeRowidIndex(pRtree, pParent, pNode->iNode, piIndex);
+ }else{
+ *piIndex = -1;
+ return SQLITE_OK;
}
- *piIndex = -1;
- return SQLITE_OK;
}
/*
@@ -180493,7 +209939,7 @@ static RtreeSearchPoint *rtreeEnqueue(
RtreeSearchPoint *pNew;
if( pCur->nPoint>=pCur->nPointAlloc ){
int nNew = pCur->nPointAlloc*2 + 8;
- pNew = sqlite3_realloc(pCur->aPoint, nNew*sizeof(pCur->aPoint[0]));
+ pNew = sqlite3_realloc64(pCur->aPoint, nNew*sizeof(pCur->aPoint[0]));
if( pNew==0 ) return 0;
pCur->aPoint = pNew;
pCur->nPointAlloc = nNew;
@@ -180528,7 +209974,7 @@ static RtreeSearchPoint *rtreeSearchPointNew(
pFirst = rtreeSearchPointFirst(pCur);
pCur->anQueue[iLevel]++;
if( pFirst==0
- || pFirst->rScore>rScore
+ || pFirst->rScore>rScore
|| (pFirst->rScore==rScore && pFirst->iLevel>iLevel)
){
if( pCur->bPoint ){
@@ -180536,7 +209982,8 @@ static RtreeSearchPoint *rtreeSearchPointNew(
pNew = rtreeEnqueue(pCur, rScore, iLevel);
if( pNew==0 ) return 0;
ii = (int)(pNew - pCur->aPoint) + 1;
- if( ii<RTREE_CACHE_SZ ){
+ assert( ii==1 );
+ if( ALWAYS(ii<RTREE_CACHE_SZ) ){
assert( pCur->aNode[ii]==0 );
pCur->aNode[ii] = pCur->aNode[0];
}else{
@@ -180597,7 +210044,7 @@ static void rtreeSearchPointPop(RtreeCursor *p){
if( p->bPoint ){
p->anQueue[p->sPoint.iLevel]--;
p->bPoint = 0;
- }else if( p->nPoint ){
+ }else if( ALWAYS(p->nPoint) ){
p->anQueue[p->aPoint[0].iLevel]--;
n = --p->nPoint;
p->aPoint[0] = p->aPoint[n];
@@ -180648,13 +210095,14 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
eInt = pRtree->eCoordType==RTREE_COORD_INT32;
while( (p = rtreeSearchPointFirst(pCur))!=0 && p->iLevel>0 ){
+ u8 *pCellData;
pNode = rtreeNodeOfFirstSearchPoint(pCur, &rc);
if( rc ) return rc;
nCell = NCELL(pNode);
assert( nCell<200 );
+ pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
while( p->iCell<nCell ){
sqlite3_rtree_dbl rScore = (sqlite3_rtree_dbl)-1;
- u8 *pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
eWithin = FULLY_WITHIN;
for(ii=0; ii<nConstraint; ii++){
RtreeConstraint *pConstraint = pCur->aConstraint + ii;
@@ -180667,13 +210115,23 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
}else{
rtreeNonleafConstraint(pConstraint, eInt, pCellData, &eWithin);
}
- if( eWithin==NOT_WITHIN ) break;
+ if( eWithin==NOT_WITHIN ){
+ p->iCell++;
+ pCellData += pRtree->nBytesPerCell;
+ break;
+ }
}
- p->iCell++;
if( eWithin==NOT_WITHIN ) continue;
+ p->iCell++;
x.iLevel = p->iLevel - 1;
if( x.iLevel ){
x.id = readInt64(pCellData);
+ for(ii=0; ii<pCur->nPoint; ii++){
+ if( pCur->aPoint[ii].id==x.id ){
+ RTREE_IS_CORRUPT(pRtree);
+ return SQLITE_CORRUPT_VTAB;
+ }
+ }
x.iCell = 0;
}else{
x.id = p->id;
@@ -180701,7 +210159,7 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
return SQLITE_OK;
}
-/*
+/*
** Rtree virtual table module xNext method.
*/
static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
@@ -180719,7 +210177,7 @@ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
return rc;
}
-/*
+/*
** Rtree virtual table module xRowid method.
*/
static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
@@ -180727,13 +210185,13 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
int rc = SQLITE_OK;
RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
- if( rc==SQLITE_OK && p ){
+ if( rc==SQLITE_OK && ALWAYS(p) ){
*pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
}
return rc;
}
-/*
+/*
** Rtree virtual table module xColumn method.
*/
static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
@@ -180745,7 +210203,7 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
if( rc ) return rc;
- if( p==0 ) return SQLITE_OK;
+ if( NEVER(p==0) ) return SQLITE_OK;
if( i==0 ){
sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
}else if( i<=pRtree->nDim2 ){
@@ -180766,7 +210224,7 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
&pCsr->pReadAux, 0);
if( rc ) return rc;
}
- sqlite3_bind_int64(pCsr->pReadAux, 1,
+ sqlite3_bind_int64(pCsr->pReadAux, 1,
nodeGetRowid(pRtree, pNode, p->iCell));
rc = sqlite3_step(pCsr->pReadAux);
if( rc==SQLITE_ROW ){
@@ -180779,12 +210237,12 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
}
sqlite3_result_value(ctx,
sqlite3_column_value(pCsr->pReadAux, i - pRtree->nDim2 + 1));
- }
+ }
return SQLITE_OK;
}
-/*
-** Use nodeAcquire() to obtain the leaf node containing the record with
+/*
+** Use nodeAcquire() to obtain the leaf node containing the record with
** rowid iRowid. If successful, set *ppLeaf to point to the node and
** return SQLITE_OK. If there is no such record in the table, set
** *ppLeaf to 0 and return SQLITE_OK. If an error occurs, set *ppLeaf
@@ -180843,11 +210301,11 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
return SQLITE_OK;
}
-/*
+/*
** Rtree virtual table module xFilter method.
*/
static int rtreeFilter(
- sqlite3_vtab_cursor *pVtabCursor,
+ sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
@@ -180857,17 +210315,11 @@ static int rtreeFilter(
int ii;
int rc = SQLITE_OK;
int iCell = 0;
- sqlite3_stmt *pStmt;
rtreeReference(pRtree);
/* Reset the cursor to the same state as rtreeOpen() leaves it in. */
- freeCursorConstraints(pCsr);
- sqlite3_free(pCsr->aPoint);
- pStmt = pCsr->pReadAux;
- memset(pCsr, 0, sizeof(RtreeCursor));
- pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
- pCsr->pReadAux = pStmt;
+ resetCursor(pCsr);
pCsr->iStrategy = idxNum;
if( idxNum==1 ){
@@ -180876,7 +210328,15 @@ static int rtreeFilter(
RtreeSearchPoint *p; /* Search point for the leaf */
i64 iRowid = sqlite3_value_int64(argv[0]);
i64 iNode = 0;
- rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
+ int eType = sqlite3_value_numeric_type(argv[0]);
+ if( eType==SQLITE_INTEGER
+ || (eType==SQLITE_FLOAT && sqlite3_value_double(argv[0])==iRowid)
+ ){
+ rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
+ }else{
+ rc = SQLITE_OK;
+ pLeaf = 0;
+ }
if( rc==SQLITE_OK && pLeaf!=0 ){
p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0);
assert( p!=0 ); /* Always returns pCsr->sPoint */
@@ -180890,12 +210350,12 @@ static int rtreeFilter(
pCsr->atEOF = 1;
}
}else{
- /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
- ** with the configured constraints.
+ /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
+ ** with the configured constraints.
*/
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
if( rc==SQLITE_OK && argc>0 ){
- pCsr->aConstraint = sqlite3_malloc(sizeof(RtreeConstraint)*argc);
+ pCsr->aConstraint = sqlite3_malloc64(sizeof(RtreeConstraint)*argc);
pCsr->nConstraint = argc;
if( !pCsr->aConstraint ){
rc = SQLITE_NOMEM;
@@ -180906,6 +210366,7 @@ static int rtreeFilter(
|| (idxStr && (int)strlen(idxStr)==argc*2) );
for(ii=0; ii<argc; ii++){
RtreeConstraint *p = &pCsr->aConstraint[ii];
+ int eType = sqlite3_value_numeric_type(argv[ii]);
p->op = idxStr[ii*2];
p->iCoord = idxStr[ii*2+1]-'0';
if( p->op>=RTREE_MATCH ){
@@ -180920,20 +210381,45 @@ static int rtreeFilter(
p->pInfo->nCoord = pRtree->nDim2;
p->pInfo->anQueue = pCsr->anQueue;
p->pInfo->mxLevel = pRtree->iDepth + 1;
- }else{
+ }else if( eType==SQLITE_INTEGER ){
+ sqlite3_int64 iVal = sqlite3_value_int64(argv[ii]);
+#ifdef SQLITE_RTREE_INT_ONLY
+ p->u.rValue = iVal;
+#else
+ p->u.rValue = (double)iVal;
+ if( iVal>=((sqlite3_int64)1)<<48
+ || iVal<=-(((sqlite3_int64)1)<<48)
+ ){
+ if( p->op==RTREE_LT ) p->op = RTREE_LE;
+ if( p->op==RTREE_GT ) p->op = RTREE_GE;
+ }
+#endif
+ }else if( eType==SQLITE_FLOAT ){
#ifdef SQLITE_RTREE_INT_ONLY
p->u.rValue = sqlite3_value_int64(argv[ii]);
#else
p->u.rValue = sqlite3_value_double(argv[ii]);
#endif
+ }else{
+ p->u.rValue = RTREE_ZERO;
+ if( eType==SQLITE_NULL ){
+ p->op = RTREE_FALSE;
+ }else if( p->op==RTREE_LT || p->op==RTREE_LE ){
+ p->op = RTREE_TRUE;
+ }else{
+ p->op = RTREE_FALSE;
+ }
}
}
}
}
if( rc==SQLITE_OK ){
RtreeSearchPoint *pNew;
+ assert( pCsr->bPoint==0 ); /* Due to the resetCursor() call above */
pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1));
- if( pNew==0 ) return SQLITE_NOMEM;
+ if( NEVER(pNew==0) ){ /* Because pCsr->bPoint was FALSE */
+ return SQLITE_NOMEM;
+ }
pNew->id = 1;
pNew->iCell = 0;
pNew->eWithin = PARTLY_WITHIN;
@@ -180952,7 +210438,7 @@ static int rtreeFilter(
/*
** Rtree virtual table module xBestIndex method. There are three
-** table scan strategies to choose from (in order from most to
+** table scan strategies to choose from (in order from most to
** least desirable):
**
** idxNum idxStr Strategy
@@ -180962,8 +210448,8 @@ static int rtreeFilter(
** ------------------------------------------------
**
** If strategy 1 is used, then idxStr is not meaningful. If strategy
-** 2 is used, idxStr is formatted to contain 2 bytes for each
-** constraint used. The first two bytes of idxStr correspond to
+** 2 is used, idxStr is formatted to contain 2 bytes for each
+** constraint used. The first two bytes of idxStr correspond to
** the constraint in sqlite3_index_info.aConstraintUsage[] with
** (argvIndex==1) etc.
**
@@ -181009,8 +210495,8 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
- if( bMatch==0 && p->usable
- && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ
+ if( bMatch==0 && p->usable
+ && p->iColumn<=0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ
){
/* We have an equality constraint on the rowid. Use strategy 1. */
int jj;
@@ -181023,11 +210509,11 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
pIdxInfo->aConstraintUsage[jj].omit = 1;
/* This strategy involves a two rowid lookups on an B-Tree structures
- ** and then a linear search of an R-Tree node. This should be
- ** considered almost as quick as a direct rowid lookup (for which
+ ** and then a linear search of an R-Tree node. This should be
+ ** considered almost as quick as a direct rowid lookup (for which
** sqlite uses an internal cost of 0.0). It is expected to return
** a single row.
- */
+ */
pIdxInfo->estimatedCost = 30.0;
pIdxInfo->estimatedRows = 1;
pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
@@ -181039,28 +210525,33 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|| p->op==SQLITE_INDEX_CONSTRAINT_MATCH)
){
u8 op;
+ u8 doOmit = 1;
switch( p->op ){
- case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break;
- case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break;
- case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break;
- case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break;
- case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break;
- default:
- assert( p->op==SQLITE_INDEX_CONSTRAINT_MATCH );
- op = RTREE_MATCH;
- break;
+ case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; doOmit = 0; break;
+ case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; doOmit = 0; break;
+ case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break;
+ case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; doOmit = 0; break;
+ case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break;
+ case SQLITE_INDEX_CONSTRAINT_MATCH: op = RTREE_MATCH; break;
+ default: op = 0; break;
+ }
+ if( op ){
+ zIdxStr[iIdx++] = op;
+ zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0');
+ pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2);
+ pIdxInfo->aConstraintUsage[ii].omit = doOmit;
}
- zIdxStr[iIdx++] = op;
- zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0');
- pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2);
- pIdxInfo->aConstraintUsage[ii].omit = 1;
}
}
pIdxInfo->idxNum = 2;
pIdxInfo->needToFreeIdxStr = 1;
- if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){
- return SQLITE_NOMEM;
+ if( iIdx>0 ){
+ pIdxInfo->idxStr = sqlite3_malloc( iIdx+1 );
+ if( pIdxInfo->idxStr==0 ){
+ return SQLITE_NOMEM;
+ }
+ memcpy(pIdxInfo->idxStr, zIdxStr, iIdx+1);
}
nRow = pRtree->nRowEst >> (iIdx/2);
@@ -181089,11 +210580,11 @@ static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
#endif
{
switch( pRtree->nDim ){
- case 5: area = p->aCoord[9].i - p->aCoord[8].i;
- case 4: area *= p->aCoord[7].i - p->aCoord[6].i;
- case 3: area *= p->aCoord[5].i - p->aCoord[4].i;
- case 2: area *= p->aCoord[3].i - p->aCoord[2].i;
- default: area *= p->aCoord[1].i - p->aCoord[0].i;
+ case 5: area = (i64)p->aCoord[9].i - (i64)p->aCoord[8].i;
+ case 4: area *= (i64)p->aCoord[7].i - (i64)p->aCoord[6].i;
+ case 3: area *= (i64)p->aCoord[5].i - (i64)p->aCoord[4].i;
+ case 2: area *= (i64)p->aCoord[3].i - (i64)p->aCoord[2].i;
+ default: area *= (i64)p->aCoord[1].i - (i64)p->aCoord[0].i;
}
}
return area;
@@ -181139,35 +210630,26 @@ static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
*/
static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
int ii;
- int isInt = (pRtree->eCoordType==RTREE_COORD_INT32);
- for(ii=0; ii<pRtree->nDim2; ii+=2){
- RtreeCoord *a1 = &p1->aCoord[ii];
- RtreeCoord *a2 = &p2->aCoord[ii];
- if( (!isInt && (a2[0].f<a1[0].f || a2[1].f>a1[1].f))
- || ( isInt && (a2[0].i<a1[0].i || a2[1].i>a1[1].i))
- ){
- return 0;
+ if( pRtree->eCoordType==RTREE_COORD_INT32 ){
+ for(ii=0; ii<pRtree->nDim2; ii+=2){
+ RtreeCoord *a1 = &p1->aCoord[ii];
+ RtreeCoord *a2 = &p2->aCoord[ii];
+ if( a2[0].i<a1[0].i || a2[1].i>a1[1].i ) return 0;
+ }
+ }else{
+ for(ii=0; ii<pRtree->nDim2; ii+=2){
+ RtreeCoord *a1 = &p1->aCoord[ii];
+ RtreeCoord *a2 = &p2->aCoord[ii];
+ if( a2[0].f<a1[0].f || a2[1].f>a1[1].f ) return 0;
}
}
return 1;
}
-/*
-** Return the amount cell p would grow by if it were unioned with pCell.
-*/
-static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
- RtreeDValue area;
- RtreeCell cell;
- memcpy(&cell, p, sizeof(RtreeCell));
- area = cellArea(pRtree, &cell);
- cellUnion(pRtree, &cell, pCell);
- return (cellArea(pRtree, &cell)-area);
-}
-
static RtreeDValue cellOverlap(
- Rtree *pRtree,
- RtreeCell *p,
- RtreeCell *aCell,
+ Rtree *pRtree,
+ RtreeCell *p,
+ RtreeCell *aCell,
int nCell
){
int ii;
@@ -181210,38 +210692,52 @@ static int ChooseLeaf(
for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){
int iCell;
sqlite3_int64 iBest = 0;
-
+ int bFound = 0;
RtreeDValue fMinGrowth = RTREE_ZERO;
RtreeDValue fMinArea = RTREE_ZERO;
-
int nCell = NCELL(pNode);
- RtreeCell cell;
- RtreeNode *pChild;
-
- RtreeCell *aCell = 0;
+ RtreeNode *pChild = 0;
- /* Select the child node which will be enlarged the least if pCell
- ** is inserted into it. Resolve ties by choosing the entry with
- ** the smallest area.
+ /* First check to see if there is are any cells in pNode that completely
+ ** contains pCell. If two or more cells in pNode completely contain pCell
+ ** then pick the smallest.
*/
for(iCell=0; iCell<nCell; iCell++){
- int bBest = 0;
- RtreeDValue growth;
- RtreeDValue area;
+ RtreeCell cell;
nodeGetCell(pRtree, pNode, iCell, &cell);
- growth = cellGrowth(pRtree, &cell, pCell);
- area = cellArea(pRtree, &cell);
- if( iCell==0||growth<fMinGrowth||(growth==fMinGrowth && area<fMinArea) ){
- bBest = 1;
+ if( cellContains(pRtree, &cell, pCell) ){
+ RtreeDValue area = cellArea(pRtree, &cell);
+ if( bFound==0 || area<fMinArea ){
+ iBest = cell.iRowid;
+ fMinArea = area;
+ bFound = 1;
+ }
}
- if( bBest ){
- fMinGrowth = growth;
- fMinArea = area;
- iBest = cell.iRowid;
+ }
+ if( !bFound ){
+ /* No cells of pNode will completely contain pCell. So pick the
+ ** cell of pNode that grows by the least amount when pCell is added.
+ ** Break ties by selecting the smaller cell.
+ */
+ for(iCell=0; iCell<nCell; iCell++){
+ RtreeCell cell;
+ RtreeDValue growth;
+ RtreeDValue area;
+ nodeGetCell(pRtree, pNode, iCell, &cell);
+ area = cellArea(pRtree, &cell);
+ cellUnion(pRtree, &cell, pCell);
+ growth = cellArea(pRtree, &cell)-area;
+ if( iCell==0
+ || growth<fMinGrowth
+ || (growth==fMinGrowth && area<fMinArea)
+ ){
+ fMinGrowth = growth;
+ fMinArea = area;
+ iBest = cell.iRowid;
+ }
}
}
- sqlite3_free(aCell);
rc = nodeAcquire(pRtree, iBest, pNode, &pChild);
nodeRelease(pRtree, pNode);
pNode = pChild;
@@ -181262,12 +210758,21 @@ static int AdjustTree(
RtreeCell *pCell /* This cell was just inserted */
){
RtreeNode *p = pNode;
+ int cnt = 0;
+ int rc;
while( p->pParent ){
RtreeNode *pParent = p->pParent;
RtreeCell cell;
int iCell;
- if( nodeParentIndex(pRtree, p, &iCell) ){
+ cnt++;
+ if( NEVER(cnt>100) ){
+ RTREE_IS_CORRUPT(pRtree);
+ return SQLITE_CORRUPT_VTAB;
+ }
+ rc = nodeParentIndex(pRtree, p, &iCell);
+ if( NEVER(rc!=SQLITE_OK) ){
+ RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
}
@@ -181276,7 +210781,7 @@ static int AdjustTree(
cellUnion(pRtree, &cell, pCell);
nodeOverwriteCell(pRtree, pParent, &cell, iCell);
}
-
+
p = pParent;
}
return SQLITE_OK;
@@ -181305,81 +210810,10 @@ static int parentWrite(Rtree *pRtree, sqlite3_int64 iNode, sqlite3_int64 iPar){
static int rtreeInsertCell(Rtree *, RtreeNode *, RtreeCell *, int);
-/*
-** Arguments aIdx, aDistance and aSpare all point to arrays of size
-** nIdx. The aIdx array contains the set of integers from 0 to
-** (nIdx-1) in no particular order. This function sorts the values
-** in aIdx according to the indexed values in aDistance. For
-** example, assuming the inputs:
-**
-** aIdx = { 0, 1, 2, 3 }
-** aDistance = { 5.0, 2.0, 7.0, 6.0 }
-**
-** this function sets the aIdx array to contain:
-**
-** aIdx = { 0, 1, 2, 3 }
-**
-** The aSpare array is used as temporary working space by the
-** sorting algorithm.
-*/
-static void SortByDistance(
- int *aIdx,
- int nIdx,
- RtreeDValue *aDistance,
- int *aSpare
-){
- if( nIdx>1 ){
- int iLeft = 0;
- int iRight = 0;
-
- int nLeft = nIdx/2;
- int nRight = nIdx-nLeft;
- int *aLeft = aIdx;
- int *aRight = &aIdx[nLeft];
-
- SortByDistance(aLeft, nLeft, aDistance, aSpare);
- SortByDistance(aRight, nRight, aDistance, aSpare);
-
- memcpy(aSpare, aLeft, sizeof(int)*nLeft);
- aLeft = aSpare;
-
- while( iLeft<nLeft || iRight<nRight ){
- if( iLeft==nLeft ){
- aIdx[iLeft+iRight] = aRight[iRight];
- iRight++;
- }else if( iRight==nRight ){
- aIdx[iLeft+iRight] = aLeft[iLeft];
- iLeft++;
- }else{
- RtreeDValue fLeft = aDistance[aLeft[iLeft]];
- RtreeDValue fRight = aDistance[aRight[iRight]];
- if( fLeft<fRight ){
- aIdx[iLeft+iRight] = aLeft[iLeft];
- iLeft++;
- }else{
- aIdx[iLeft+iRight] = aRight[iRight];
- iRight++;
- }
- }
- }
-
-#if 0
- /* Check that the sort worked */
- {
- int jj;
- for(jj=1; jj<nIdx; jj++){
- RtreeDValue left = aDistance[aIdx[jj-1]];
- RtreeDValue right = aDistance[aIdx[jj]];
- assert( left<=right );
- }
- }
-#endif
- }
-}
/*
** Arguments aIdx, aCell and aSpare all point to arrays of size
-** nIdx. The aIdx array contains the set of integers from 0 to
+** nIdx. The aIdx array contains the set of integers from 0 to
** (nIdx-1) in no particular order. This function sorts the values
** in aIdx according to dimension iDim of the cells in aCell. The
** minimum value of dimension iDim is considered first, the
@@ -181390,10 +210824,10 @@ static void SortByDistance(
*/
static void SortByDimension(
Rtree *pRtree,
- int *aIdx,
- int nIdx,
- int iDim,
- RtreeCell *aCell,
+ int *aIdx,
+ int nIdx,
+ int iDim,
+ RtreeCell *aCell,
int *aSpare
){
if( nIdx>1 ){
@@ -181464,9 +210898,9 @@ static int splitNodeStartree(
int iBestSplit = 0;
RtreeDValue fBestMargin = RTREE_ZERO;
- int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
+ sqlite3_int64 nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
- aaSorted = (int **)sqlite3_malloc(nByte);
+ aaSorted = (int **)sqlite3_malloc64(nByte);
if( !aaSorted ){
return SQLITE_NOMEM;
}
@@ -181490,8 +210924,8 @@ static int splitNodeStartree(
int nLeft;
for(
- nLeft=RTREE_MINCELLS(pRtree);
- nLeft<=(nCell-RTREE_MINCELLS(pRtree));
+ nLeft=RTREE_MINCELLS(pRtree);
+ nLeft<=(nCell-RTREE_MINCELLS(pRtree));
nLeft++
){
RtreeCell left;
@@ -181546,21 +210980,26 @@ static int splitNodeStartree(
static int updateMapping(
- Rtree *pRtree,
- i64 iRowid,
- RtreeNode *pNode,
+ Rtree *pRtree,
+ i64 iRowid,
+ RtreeNode *pNode,
int iHeight
){
int (*xSetMapping)(Rtree *, sqlite3_int64, sqlite3_int64);
xSetMapping = ((iHeight==0)?rowidWrite:parentWrite);
if( iHeight>0 ){
RtreeNode *pChild = nodeHashLookup(pRtree, iRowid);
+ RtreeNode *p;
+ for(p=pNode; p; p=p->pParent){
+ if( p==pChild ) return SQLITE_CORRUPT_VTAB;
+ }
if( pChild ){
nodeRelease(pRtree, pChild->pParent);
nodeReference(pNode);
pChild->pParent = pNode;
}
}
+ if( NEVER(pNode==0) ) return SQLITE_ERROR;
return xSetMapping(pRtree, iRowid, pNode->iNode);
}
@@ -181584,10 +211023,10 @@ static int SplitNode(
RtreeCell leftbbox;
RtreeCell rightbbox;
- /* Allocate an array and populate it with a copy of pCell and
+ /* Allocate an array and populate it with a copy of pCell and
** all cells from node pLeft. Then zero the original node.
*/
- aCell = sqlite3_malloc((sizeof(RtreeCell)+sizeof(int))*(nCell+1));
+ aCell = sqlite3_malloc64((sizeof(RtreeCell)+sizeof(int))*(nCell+1));
if( !aCell ){
rc = SQLITE_NOMEM;
goto splitnode_out;
@@ -181650,11 +211089,12 @@ static int SplitNode(
RtreeNode *pParent = pLeft->pParent;
int iCell;
rc = nodeParentIndex(pRtree, pLeft, &iCell);
- if( rc==SQLITE_OK ){
+ if( ALWAYS(rc==SQLITE_OK) ){
nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell);
rc = AdjustTree(pRtree, pParent, &leftbbox);
+ assert( rc==SQLITE_OK );
}
- if( rc!=SQLITE_OK ){
+ if( NEVER(rc!=SQLITE_OK) ){
goto splitnode_out;
}
}
@@ -181701,14 +211141,14 @@ splitnode_out:
}
/*
-** If node pLeaf is not the root of the r-tree and its pParent pointer is
+** If node pLeaf is not the root of the r-tree and its pParent pointer is
** still NULL, load all ancestor nodes of pLeaf into memory and populate
** the pLeaf->pParent chain all the way up to the root node.
**
** This operation is required when a row is deleted (or updated - an update
** is implemented as a delete followed by an insert). SQLite provides the
** rowid of the row to delete, which can be used to find the leaf on which
-** the entry resides (argument pLeaf). Once the leaf is located, this
+** the entry resides (argument pLeaf). Once the leaf is located, this
** function is called to determine its ancestry.
*/
static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){
@@ -181729,13 +211169,16 @@ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){
*/
iNode = sqlite3_column_int64(pRtree->pReadParent, 0);
for(pTest=pLeaf; pTest && pTest->iNode!=iNode; pTest=pTest->pParent);
- if( !pTest ){
+ if( pTest==0 ){
rc2 = nodeAcquire(pRtree, iNode, 0, &pChild->pParent);
}
}
rc = sqlite3_reset(pRtree->pReadParent);
if( rc==SQLITE_OK ) rc = rc2;
- if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT_VTAB;
+ if( rc==SQLITE_OK && !pChild->pParent ){
+ RTREE_IS_CORRUPT(pRtree);
+ rc = SQLITE_CORRUPT_VTAB;
+ }
pChild = pChild->pParent;
}
return rc;
@@ -181757,6 +211200,7 @@ static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){
pParent = pNode->pParent;
pNode->pParent = 0;
rc = deleteCell(pRtree, pParent, iCell, iHeight+1);
+ testcase( rc!=SQLITE_OK );
}
rc2 = nodeRelease(pRtree, pParent);
if( rc==SQLITE_OK ){
@@ -181779,7 +211223,7 @@ static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){
if( SQLITE_OK!=(rc = sqlite3_reset(pRtree->pDeleteParent)) ){
return rc;
}
-
+
/* Remove the node from the in-memory hash table and link it into
** the Rtree.pDeleted list. Its contents will be re-inserted later on.
*/
@@ -181794,9 +211238,9 @@ static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){
static int fixBoundingBox(Rtree *pRtree, RtreeNode *pNode){
RtreeNode *pParent = pNode->pParent;
- int rc = SQLITE_OK;
+ int rc = SQLITE_OK;
if( pParent ){
- int ii;
+ int ii;
int nCell = NCELL(pNode);
RtreeCell box; /* Bounding box for pNode */
nodeGetCell(pRtree, pNode, 0, &box);
@@ -181850,109 +211294,8 @@ static int deleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell, int iHeight){
return rc;
}
-static int Reinsert(
- Rtree *pRtree,
- RtreeNode *pNode,
- RtreeCell *pCell,
- int iHeight
-){
- int *aOrder;
- int *aSpare;
- RtreeCell *aCell;
- RtreeDValue *aDistance;
- int nCell;
- RtreeDValue aCenterCoord[RTREE_MAX_DIMENSIONS];
- int iDim;
- int ii;
- int rc = SQLITE_OK;
- int n;
-
- memset(aCenterCoord, 0, sizeof(RtreeDValue)*RTREE_MAX_DIMENSIONS);
-
- nCell = NCELL(pNode)+1;
- n = (nCell+1)&(~1);
-
- /* Allocate the buffers used by this operation. The allocation is
- ** relinquished before this function returns.
- */
- aCell = (RtreeCell *)sqlite3_malloc(n * (
- sizeof(RtreeCell) + /* aCell array */
- sizeof(int) + /* aOrder array */
- sizeof(int) + /* aSpare array */
- sizeof(RtreeDValue) /* aDistance array */
- ));
- if( !aCell ){
- return SQLITE_NOMEM;
- }
- aOrder = (int *)&aCell[n];
- aSpare = (int *)&aOrder[n];
- aDistance = (RtreeDValue *)&aSpare[n];
-
- for(ii=0; ii<nCell; ii++){
- if( ii==(nCell-1) ){
- memcpy(&aCell[ii], pCell, sizeof(RtreeCell));
- }else{
- nodeGetCell(pRtree, pNode, ii, &aCell[ii]);
- }
- aOrder[ii] = ii;
- for(iDim=0; iDim<pRtree->nDim; iDim++){
- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]);
- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]);
- }
- }
- for(iDim=0; iDim<pRtree->nDim; iDim++){
- aCenterCoord[iDim] = (aCenterCoord[iDim]/(nCell*(RtreeDValue)2));
- }
-
- for(ii=0; ii<nCell; ii++){
- aDistance[ii] = RTREE_ZERO;
- for(iDim=0; iDim<pRtree->nDim; iDim++){
- RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) -
- DCOORD(aCell[ii].aCoord[iDim*2]));
- aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]);
- }
- }
-
- SortByDistance(aOrder, nCell, aDistance, aSpare);
- nodeZero(pRtree, pNode);
-
- for(ii=0; rc==SQLITE_OK && ii<(nCell-(RTREE_MINCELLS(pRtree)+1)); ii++){
- RtreeCell *p = &aCell[aOrder[ii]];
- nodeInsertCell(pRtree, pNode, p);
- if( p->iRowid==pCell->iRowid ){
- if( iHeight==0 ){
- rc = rowidWrite(pRtree, p->iRowid, pNode->iNode);
- }else{
- rc = parentWrite(pRtree, p->iRowid, pNode->iNode);
- }
- }
- }
- if( rc==SQLITE_OK ){
- rc = fixBoundingBox(pRtree, pNode);
- }
- for(; rc==SQLITE_OK && ii<nCell; ii++){
- /* Find a node to store this cell in. pNode->iNode currently contains
- ** the height of the sub-tree headed by the cell.
- */
- RtreeNode *pInsert;
- RtreeCell *p = &aCell[aOrder[ii]];
- rc = ChooseLeaf(pRtree, p, iHeight, &pInsert);
- if( rc==SQLITE_OK ){
- int rc2;
- rc = rtreeInsertCell(pRtree, pInsert, p, iHeight);
- rc2 = nodeRelease(pRtree, pInsert);
- if( rc==SQLITE_OK ){
- rc = rc2;
- }
- }
- }
-
- sqlite3_free(aCell);
- return rc;
-}
-
/*
-** Insert cell pCell into node pNode. Node pNode is the head of a
+** Insert cell pCell into node pNode. Node pNode is the head of a
** subtree iHeight high (leaf nodes have iHeight==0).
*/
static int rtreeInsertCell(
@@ -181971,15 +211314,10 @@ static int rtreeInsertCell(
}
}
if( nodeInsertCell(pRtree, pNode, pCell) ){
- if( iHeight<=pRtree->iReinsertHeight || pNode->iNode==1){
- rc = SplitNode(pRtree, pNode, pCell, iHeight);
- }else{
- pRtree->iReinsertHeight = iHeight;
- rc = Reinsert(pRtree, pNode, pCell, iHeight);
- }
+ rc = SplitNode(pRtree, pNode, pCell, iHeight);
}else{
rc = AdjustTree(pRtree, pNode, pCell);
- if( rc==SQLITE_OK ){
+ if( ALWAYS(rc==SQLITE_OK) ){
if( iHeight==0 ){
rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode);
}else{
@@ -182042,15 +211380,19 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
/* Obtain a reference to the root node to initialize Rtree.iDepth */
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
- /* Obtain a reference to the leaf node that contains the entry
- ** about to be deleted.
+ /* Obtain a reference to the leaf node that contains the entry
+ ** about to be deleted.
*/
if( rc==SQLITE_OK ){
rc = findLeafNode(pRtree, iDelete, &pLeaf, 0);
}
+#ifdef CORRUPT_DB
+ assert( pLeaf!=0 || rc!=SQLITE_OK || CORRUPT_DB );
+#endif
+
/* Delete the cell in question from the leaf node. */
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK && pLeaf ){
int rc2;
rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell);
if( rc==SQLITE_OK ){
@@ -182070,18 +211412,18 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
}
/* Check if the root node now has exactly one child. If so, remove
- ** it, schedule the contents of the child for reinsertion and
+ ** it, schedule the contents of the child for reinsertion and
** reduce the tree height by one.
**
** This is equivalent to copying the contents of the child into
- ** the root node (the operation that Gutman's paper says to perform
+ ** the root node (the operation that Gutman's paper says to perform
** in this scenario).
*/
if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){
int rc2;
RtreeNode *pChild = 0;
i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
- rc = nodeAcquire(pRtree, iChild, pRoot, &pChild);
+ rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); /* tag-20210916a */
if( rc==SQLITE_OK ){
rc = removeNode(pRtree, pChild, pRtree->iDepth-1);
}
@@ -182144,8 +211486,8 @@ static RtreeValue rtreeValueUp(sqlite3_value *v){
#endif /* !defined(SQLITE_RTREE_INT_ONLY) */
/*
-** A constraint has failed while inserting a row into an rtree table.
-** Assuming no OOM error occurs, this function sets the error message
+** A constraint has failed while inserting a row into an rtree table.
+** Assuming no OOM error occurs, this function sets the error message
** (at pRtree->base.zErrMsg) to an appropriate value and returns
** SQLITE_CONSTRAINT.
**
@@ -182158,7 +211500,7 @@ static RtreeValue rtreeValueUp(sqlite3_value *v){
*/
static int rtreeConstraintError(Rtree *pRtree, int iCol){
sqlite3_stmt *pStmt = 0;
- char *zSql;
+ char *zSql;
int rc;
assert( iCol==0 || iCol%2 );
@@ -182195,9 +211537,9 @@ static int rtreeConstraintError(Rtree *pRtree, int iCol){
** The xUpdate method for rtree module virtual tables.
*/
static int rtreeUpdate(
- sqlite3_vtab *pVtab,
- int nData,
- sqlite3_value **aData,
+ sqlite3_vtab *pVtab,
+ int nData,
+ sqlite3_value **aData,
sqlite_int64 *pRowid
){
Rtree *pRtree = (Rtree *)pVtab;
@@ -182214,7 +211556,7 @@ static int rtreeUpdate(
rtreeReference(pRtree);
assert(nData>=1);
- cell.iRowid = 0; /* Used only to suppress a compiler warning */
+ memset(&cell, 0, sizeof(cell));
/* Constraint handling. A write operation on an r-tree table may return
** SQLITE_CONSTRAINT for two reasons:
@@ -182264,7 +211606,7 @@ static int rtreeUpdate(
}
}
- /* If a rowid value was supplied, check if it is already present in
+ /* If a rowid value was supplied, check if it is already present in
** the table. If so, the constraint has failed. */
if( sqlite3_value_type(aData[2])!=SQLITE_NULL ){
cell.iRowid = sqlite3_value_int64(aData[2]);
@@ -182315,14 +211657,13 @@ static int rtreeUpdate(
}
if( rc==SQLITE_OK ){
int rc2;
- pRtree->iReinsertHeight = -1;
rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0);
rc2 = nodeRelease(pRtree, pLeaf);
if( rc==SQLITE_OK ){
rc = rc2;
}
}
- if( pRtree->nAux ){
+ if( rc==SQLITE_OK && pRtree->nAux ){
sqlite3_stmt *pUp = pRtree->pWriteAux;
int jj;
sqlite3_bind_int64(pUp, 1, *pRowid);
@@ -182370,8 +211711,8 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
"ALTER TABLE %Q.'%q_node' RENAME TO \"%w_node\";"
"ALTER TABLE %Q.'%q_parent' RENAME TO \"%w_parent\";"
"ALTER TABLE %Q.'%q_rowid' RENAME TO \"%w_rowid\";"
- , pRtree->zDb, pRtree->zName, zNewName
- , pRtree->zDb, pRtree->zName, zNewName
+ , pRtree->zDb, pRtree->zName, zNewName
+ , pRtree->zDb, pRtree->zName, zNewName
, pRtree->zDb, pRtree->zName, zNewName
);
if( zSql ){
@@ -182386,8 +211727,8 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
** The xSavepoint method.
**
** This module does not need to do anything to support savepoints. However,
-** it uses this hook to close any open blob handle. This is done because a
-** DROP TABLE command - which fortunately always opens a savepoint - cannot
+** it uses this hook to close any open blob handle. This is done because a
+** DROP TABLE command - which fortunately always opens a savepoint - cannot
** succeed if there are any open blob handles. i.e. if the blob handle were
** not closed here, the following would fail:
**
@@ -182416,7 +211757,7 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
char *zSql;
sqlite3_stmt *p;
int rc;
- i64 nRow = 0;
+ i64 nRow = RTREE_MIN_ROWEST;
rc = sqlite3_table_column_metadata(
db, pRtree->zDb, "sqlite_stat1",0,0,0,0,0,0
@@ -182433,20 +211774,10 @@ static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
if( rc==SQLITE_OK ){
if( sqlite3_step(p)==SQLITE_ROW ) nRow = sqlite3_column_int64(p, 0);
rc = sqlite3_finalize(p);
- }else if( rc!=SQLITE_NOMEM ){
- rc = SQLITE_OK;
- }
-
- if( rc==SQLITE_OK ){
- if( nRow==0 ){
- pRtree->nRowEst = RTREE_DEFAULT_ROWEST;
- }else{
- pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST);
- }
}
sqlite3_free(zSql);
}
-
+ pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST);
return rc;
}
@@ -182466,8 +211797,11 @@ static int rtreeShadowName(const char *zName){
return 0;
}
+/* Forward declaration */
+static int rtreeIntegrity(sqlite3_vtab*, const char*, const char*, int, char**);
+
static sqlite3_module rtreeModule = {
- 3, /* iVersion */
+ 4, /* iVersion */
rtreeCreate, /* xCreate - create a table */
rtreeConnect, /* xConnect - connect to an existing table */
rtreeBestIndex, /* xBestIndex - Determine search strategy */
@@ -182490,14 +211824,15 @@ static sqlite3_module rtreeModule = {
rtreeSavepoint, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- rtreeShadowName /* xShadowName */
+ rtreeShadowName, /* xShadowName */
+ rtreeIntegrity /* xIntegrity */
};
static int rtreeSqlInit(
- Rtree *pRtree,
- sqlite3 *db,
- const char *zDb,
- const char *zPrefix,
+ Rtree *pRtree,
+ sqlite3 *db,
+ const char *zDb,
+ const char *zPrefix,
int isCreate
){
int rc = SQLITE_OK;
@@ -182520,6 +211855,7 @@ static int rtreeSqlInit(
};
sqlite3_stmt **appStmt[N_STATEMENT];
int i;
+ const int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB;
pRtree->db = db;
@@ -182576,14 +211912,13 @@ static int rtreeSqlInit(
}
zSql = sqlite3_mprintf(zFormat, zDb, zPrefix);
if( zSql ){
- rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT,
- appStmt[i], 0);
+ rc = sqlite3_prepare_v3(db, zSql, -1, f, appStmt[i], 0);
}else{
rc = SQLITE_NOMEM;
}
sqlite3_free(zSql);
}
- if( pRtree->nAux ){
+ if( pRtree->nAux && rc!=SQLITE_NOMEM ){
pRtree->zReadAuxSql = sqlite3_mprintf(
"SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1",
zDb, zPrefix);
@@ -182596,9 +211931,12 @@ static int rtreeSqlInit(
sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix);
for(ii=0; ii<pRtree->nAux; ii++){
if( ii ) sqlite3_str_append(p, ",", 1);
+#ifdef SQLITE_ENABLE_GEOPOLY
if( ii<pRtree->nAuxNotNull ){
sqlite3_str_appendf(p,"a%d=coalesce(?%d,a%d)",ii,ii+2,ii);
- }else{
+ }else
+#endif
+ {
sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2);
}
}
@@ -182607,8 +211945,7 @@ static int rtreeSqlInit(
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
- rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT,
- &pRtree->pWriteAux, 0);
+ rc = sqlite3_prepare_v3(db, zSql, -1, f, &pRtree->pWriteAux, 0);
sqlite3_free(zSql);
}
}
@@ -182649,9 +211986,9 @@ static int getIntFromStmt(sqlite3 *db, const char *zSql, int *piVal){
** table already exists. In this case the node-size is determined by inspecting
** the root node of the tree.
**
-** Otherwise, for an xCreate(), use 64 bytes less than the database page-size.
-** This ensures that each node is stored on a single database page. If the
-** database page-size is so large that more than RTREE_MAXCELLS entries
+** Otherwise, for an xCreate(), use 64 bytes less than the database page-size.
+** This ensures that each node is stored on a single database page. If the
+** database page-size is so large that more than RTREE_MAXCELLS entries
** would fit in a single node, use a smaller node-size.
*/
static int getNodeSize(
@@ -182684,6 +212021,7 @@ static int getNodeSize(
*pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
}else if( pRtree->iNodeSize<(512-64) ){
rc = SQLITE_CORRUPT_VTAB;
+ RTREE_IS_CORRUPT(pRtree);
*pzErr = sqlite3_mprintf("undersize RTree blobs in \"%q_node\"",
pRtree->zName);
}
@@ -182693,7 +212031,15 @@ static int getNodeSize(
return rc;
}
-/*
+/*
+** Return the length of a token
+*/
+static int rtreeTokenLength(const char *z){
+ int dummy = 0;
+ return sqlite3GetToken((const unsigned char*)z,&dummy);
+}
+
+/*
** This function is the implementation of both the xConnect and xCreate
** methods of the r-tree virtual table.
**
@@ -182729,28 +212075,33 @@ static int rtreeInit(
};
assert( RTREE_MAX_AUX_COLUMN<256 ); /* Aux columns counted by a u8 */
- if( argc>RTREE_MAX_AUX_COLUMN+3 ){
- *pzErr = sqlite3_mprintf("%s", aErrMsg[3]);
+ if( argc<6 || argc>RTREE_MAX_AUX_COLUMN+3 ){
+ *pzErr = sqlite3_mprintf("%s", aErrMsg[2 + (argc>=6)]);
return SQLITE_ERROR;
}
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
+
/* Allocate the sqlite3_vtab structure */
nDb = (int)strlen(argv[1]);
nName = (int)strlen(argv[2]);
- pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
+ pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8);
if( !pRtree ){
return SQLITE_NOMEM;
}
- memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2);
+ memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8);
pRtree->nBusy = 1;
pRtree->base.pModule = &rtreeModule;
pRtree->zDb = (char *)&pRtree[1];
pRtree->zName = &pRtree->zDb[nDb+1];
+ pRtree->zNodeName = &pRtree->zName[nName+1];
pRtree->eCoordType = (u8)eCoordType;
memcpy(pRtree->zDb, argv[1], nDb);
memcpy(pRtree->zName, argv[2], nName);
+ memcpy(pRtree->zNodeName, argv[2], nName);
+ memcpy(&pRtree->zNodeName[nName], "_node", 6);
/* Create/Connect to the underlying relational database schema. If
@@ -182758,16 +212109,20 @@ static int rtreeInit(
** the r-tree table schema.
*/
pSql = sqlite3_str_new(db);
- sqlite3_str_appendf(pSql, "CREATE TABLE x(%s", argv[3]);
+ sqlite3_str_appendf(pSql, "CREATE TABLE x(%.*s INT",
+ rtreeTokenLength(argv[3]), argv[3]);
for(ii=4; ii<argc; ii++){
- if( argv[ii][0]=='+' ){
+ const char *zArg = argv[ii];
+ if( zArg[0]=='+' ){
pRtree->nAux++;
- sqlite3_str_appendf(pSql, ",%s", argv[ii]+1);
+ sqlite3_str_appendf(pSql, ",%.*s", rtreeTokenLength(zArg+1), zArg+1);
}else if( pRtree->nAux>0 ){
break;
}else{
+ static const char *azFormat[] = {",%.*s REAL", ",%.*s INT"};
pRtree->nDim2++;
- sqlite3_str_appendf(pSql, ",%s", argv[ii]);
+ sqlite3_str_appendf(pSql, azFormat[eCoordType],
+ rtreeTokenLength(zArg), zArg);
}
}
sqlite3_str_appendf(pSql, ");");
@@ -182832,53 +212187,50 @@ rtreeInit_fail:
**
** The human readable string takes the form of a Tcl list with one
** entry for each cell in the r-tree node. Each entry is itself a
-** list, containing the 8-byte rowid/pageno followed by the
+** list, containing the 8-byte rowid/pageno followed by the
** <num-dimension>*2 coordinates.
*/
static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
- char *zText = 0;
RtreeNode node;
Rtree tree;
int ii;
+ int nData;
+ int errCode;
+ sqlite3_str *pOut;
UNUSED_PARAMETER(nArg);
memset(&node, 0, sizeof(RtreeNode));
memset(&tree, 0, sizeof(Rtree));
tree.nDim = (u8)sqlite3_value_int(apArg[0]);
+ if( tree.nDim<1 || tree.nDim>5 ) return;
tree.nDim2 = tree.nDim*2;
tree.nBytesPerCell = 8 + 8 * tree.nDim;
node.zData = (u8 *)sqlite3_value_blob(apArg[1]);
+ if( node.zData==0 ) return;
+ nData = sqlite3_value_bytes(apArg[1]);
+ if( nData<4 ) return;
+ if( nData<NCELL(&node)*tree.nBytesPerCell ) return;
+ pOut = sqlite3_str_new(0);
for(ii=0; ii<NCELL(&node); ii++){
- char zCell[512];
- int nCell = 0;
RtreeCell cell;
int jj;
nodeGetCell(&tree, &node, ii, &cell);
- sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid);
- nCell = (int)strlen(zCell);
+ if( ii>0 ) sqlite3_str_append(pOut, " ", 1);
+ sqlite3_str_appendf(pOut, "{%lld", cell.iRowid);
for(jj=0; jj<tree.nDim2; jj++){
#ifndef SQLITE_RTREE_INT_ONLY
- sqlite3_snprintf(512-nCell,&zCell[nCell], " %g",
- (double)cell.aCoord[jj].f);
+ sqlite3_str_appendf(pOut, " %g", (double)cell.aCoord[jj].f);
#else
- sqlite3_snprintf(512-nCell,&zCell[nCell], " %d",
- cell.aCoord[jj].i);
+ sqlite3_str_appendf(pOut, " %d", cell.aCoord[jj].i);
#endif
- nCell = (int)strlen(zCell);
- }
-
- if( zText ){
- char *zTextNew = sqlite3_mprintf("%s {%s}", zText, zCell);
- sqlite3_free(zText);
- zText = zTextNew;
- }else{
- zText = sqlite3_mprintf("{%s}", zCell);
}
+ sqlite3_str_append(pOut, "}", 1);
}
-
- sqlite3_result_text(ctx, zText, -1, sqlite3_free);
+ errCode = sqlite3_str_errcode(pOut);
+ sqlite3_result_text(ctx, sqlite3_str_finish(pOut), -1, sqlite3_free);
+ sqlite3_result_error_code(ctx, errCode);
}
/* This routine implements an SQL function that returns the "depth" parameter
@@ -182892,13 +212244,18 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
*/
static void rtreedepth(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
UNUSED_PARAMETER(nArg);
- if( sqlite3_value_type(apArg[0])!=SQLITE_BLOB
+ if( sqlite3_value_type(apArg[0])!=SQLITE_BLOB
|| sqlite3_value_bytes(apArg[0])<2
+
){
- sqlite3_result_error(ctx, "Invalid argument to rtreedepth()", -1);
+ sqlite3_result_error(ctx, "Invalid argument to rtreedepth()", -1);
}else{
u8 *zBlob = (u8 *)sqlite3_value_blob(apArg[0]);
- sqlite3_result_int(ctx, readInt16(zBlob));
+ if( zBlob ){
+ sqlite3_result_int(ctx, readInt16(zBlob));
+ }else{
+ sqlite3_result_error_nomem(ctx);
+ }
}
}
@@ -182978,7 +212335,7 @@ static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){
if( z==0 ){
pCheck->rc = SQLITE_NOMEM;
}else{
- pCheck->zReport = sqlite3_mprintf("%z%s%z",
+ pCheck->zReport = sqlite3_mprintf("%z%s%z",
pCheck->zReport, (pCheck->zReport ? "\n" : ""), z
);
if( pCheck->zReport==0 ){
@@ -183007,10 +212364,9 @@ static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){
static u8 *rtreeCheckGetNode(RtreeCheck *pCheck, i64 iNode, int *pnNode){
u8 *pRet = 0; /* Return value */
- assert( pCheck->rc==SQLITE_OK );
- if( pCheck->pGetNode==0 ){
+ if( pCheck->rc==SQLITE_OK && pCheck->pGetNode==0 ){
pCheck->pGetNode = rtreeCheckPrepare(pCheck,
- "SELECT data FROM %Q.'%q_node' WHERE nodeno=?",
+ "SELECT data FROM %Q.'%q_node' WHERE nodeno=?",
pCheck->zDb, pCheck->zTab
);
}
@@ -183020,7 +212376,7 @@ static u8 *rtreeCheckGetNode(RtreeCheck *pCheck, i64 iNode, int *pnNode){
if( sqlite3_step(pCheck->pGetNode)==SQLITE_ROW ){
int nNode = sqlite3_column_bytes(pCheck->pGetNode, 0);
const u8 *pNode = (const u8*)sqlite3_column_blob(pCheck->pGetNode, 0);
- pRet = sqlite3_malloc(nNode);
+ pRet = sqlite3_malloc64(nNode);
if( pRet==0 ){
pCheck->rc = SQLITE_NOMEM;
}else{
@@ -183080,7 +212436,7 @@ static void rtreeCheckMapping(
}else if( rc==SQLITE_ROW ){
i64 ii = sqlite3_column_int64(pStmt, 0);
if( ii!=iVal ){
- rtreeCheckAppendMsg(pCheck,
+ rtreeCheckAppendMsg(pCheck,
"Found (%lld -> %lld) in %s table, expected (%lld -> %lld)",
iKey, ii, (bLeaf ? "%_rowid" : "%_parent"), iKey, iVal
);
@@ -183096,13 +212452,13 @@ static void rtreeCheckMapping(
** if they are not.
**
** Additionally, if pParent is not NULL, then it is assumed to point to
-** the array of coordinates on the parent page that bound the page
+** the array of coordinates on the parent page that bound the page
** containing pCell. In this case it is also verified that the two
** sets of coordinates are mutually consistent and an error message added
** to the RtreeCheck object if they are not.
*/
static void rtreeCheckCellCoord(
- RtreeCheck *pCheck,
+ RtreeCheck *pCheck,
i64 iNode, /* Node id to use in error messages */
int iCell, /* Cell number to use in error messages */
u8 *pCell, /* Pointer to cell coordinates */
@@ -183118,7 +212474,7 @@ static void rtreeCheckCellCoord(
/* printf("%e, %e\n", c1.u.f, c2.u.f); */
if( pCheck->bInt ? c1.i>c2.i : c1.f>c2.f ){
- rtreeCheckAppendMsg(pCheck,
+ rtreeCheckAppendMsg(pCheck,
"Dimension %d of cell %d on node %lld is corrupt", i, iCell, iNode
);
}
@@ -183127,10 +212483,10 @@ static void rtreeCheckCellCoord(
readCoord(&pParent[4*2*i], &p1);
readCoord(&pParent[4*(2*i + 1)], &p2);
- if( (pCheck->bInt ? c1.i<p1.i : c1.f<p1.f)
+ if( (pCheck->bInt ? c1.i<p1.i : c1.f<p1.f)
|| (pCheck->bInt ? c2.i>p2.i : c2.f>p2.f)
){
- rtreeCheckAppendMsg(pCheck,
+ rtreeCheckAppendMsg(pCheck,
"Dimension %d of cell %d on node %lld is corrupt relative to parent"
, i, iCell, iNode
);
@@ -183162,7 +212518,7 @@ static void rtreeCheckNode(
aNode = rtreeCheckGetNode(pCheck, iNode, &nNode);
if( aNode ){
if( nNode<4 ){
- rtreeCheckAppendMsg(pCheck,
+ rtreeCheckAppendMsg(pCheck,
"Node %lld is too small (%d bytes)", iNode, nNode
);
}else{
@@ -183178,8 +212534,8 @@ static void rtreeCheckNode(
}
nCell = readInt16(&aNode[2]);
if( (4 + nCell*(8 + pCheck->nDim*2*4))>nNode ){
- rtreeCheckAppendMsg(pCheck,
- "Node %lld is too small for cell count of %d (%d bytes)",
+ rtreeCheckAppendMsg(pCheck,
+ "Node %lld is too small for cell count of %d (%d bytes)",
iNode, nCell, nNode
);
}else{
@@ -183242,7 +212598,6 @@ static int rtreeCheckTable(
){
RtreeCheck check; /* Common context for various routines */
sqlite3_stmt *pStmt = 0; /* Used to find column count of rtree table */
- int bEnd = 0; /* True if transaction should be closed */
int nAux = 0; /* Number of extra columns. */
/* Initialize the context object */
@@ -183251,21 +212606,13 @@ static int rtreeCheckTable(
check.zDb = zDb;
check.zTab = zTab;
- /* If there is not already an open transaction, open one now. This is
- ** to ensure that the queries run as part of this integrity-check operate
- ** on a consistent snapshot. */
- if( sqlite3_get_autocommit(db) ){
- check.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0);
- bEnd = 1;
- }
-
/* Find the number of auxiliary columns */
- if( check.rc==SQLITE_OK ){
- pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
- if( pStmt ){
- nAux = sqlite3_column_count(pStmt) - 2;
- sqlite3_finalize(pStmt);
- }
+ pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
+ if( pStmt ){
+ nAux = sqlite3_column_count(pStmt) - 2;
+ sqlite3_finalize(pStmt);
+ }else
+ if( check.rc!=SQLITE_NOMEM ){
check.rc = SQLITE_OK;
}
@@ -183297,16 +212644,36 @@ static int rtreeCheckTable(
sqlite3_finalize(check.aCheckMapping[0]);
sqlite3_finalize(check.aCheckMapping[1]);
- /* If one was opened, close the transaction */
- if( bEnd ){
- int rc = sqlite3_exec(db, "END", 0, 0, 0);
- if( check.rc==SQLITE_OK ) check.rc = rc;
- }
*pzReport = check.zReport;
return check.rc;
}
/*
+** Implementation of the xIntegrity method for Rtree.
+*/
+static int rtreeIntegrity(
+ sqlite3_vtab *pVtab, /* The virtual table to check */
+ const char *zSchema, /* Schema in which the virtual table lives */
+ const char *zName, /* Name of the virtual table */
+ int isQuick, /* True for a quick_check */
+ char **pzErr /* Write results here */
+){
+ Rtree *pRtree = (Rtree*)pVtab;
+ int rc;
+ assert( pzErr!=0 && *pzErr==0 );
+ UNUSED_PARAMETER(zSchema);
+ UNUSED_PARAMETER(zName);
+ UNUSED_PARAMETER(isQuick);
+ rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr);
+ if( rc==SQLITE_OK && *pzErr ){
+ *pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z",
+ pRtree->zDb, pRtree->zName, *pzErr);
+ if( (*pzErr)==0 ) rc = SQLITE_NOMEM;
+ }
+ return rc;
+}
+
+/*
** Usage:
**
** rtreecheck(<rtree-table>);
@@ -183322,11 +212689,11 @@ static int rtreeCheckTable(
** b) unless the cell is on the root node, that the cell is bounded
** by the parent cell on the parent node.
**
-** c) for leaf nodes, that there is an entry in the %_rowid
-** table corresponding to the cell's rowid value that
+** c) for leaf nodes, that there is an entry in the %_rowid
+** table corresponding to the cell's rowid value that
** points to the correct node.
**
-** d) for cells on non-leaf nodes, that there is an entry in the
+** d) for cells on non-leaf nodes, that there is an entry in the
** %_parent table mapping from the cell's child node to the
** node that it resides on.
**
@@ -183335,17 +212702,17 @@ static int rtreeCheckTable(
** is a leaf cell that corresponds to each entry in the %_rowid table.
**
** 3. That there are the same number of entries in the %_parent table
-** as there are non-leaf cells in the r-tree structure, and that
-** there is a non-leaf cell that corresponds to each entry in the
+** as there are non-leaf cells in the r-tree structure, and that
+** there is a non-leaf cell that corresponds to each entry in the
** %_parent table.
*/
static void rtreecheck(
- sqlite3_context *ctx,
- int nArg,
+ sqlite3_context *ctx,
+ int nArg,
sqlite3_value **apArg
){
if( nArg!=1 && nArg!=2 ){
- sqlite3_result_error(ctx,
+ sqlite3_result_error(ctx,
"wrong number of arguments to function rtreecheck()", -1
);
}else{
@@ -183401,11 +212768,7 @@ static void rtreecheck(
# define GEODEBUG(X)
#endif
-#ifndef JSON_NULL /* The following stuff repeats things found in json1 */
-/*
-** Versions of isspace(), isalnum() and isdigit() to which it is safe
-** to pass signed char values.
-*/
+/* Character class routines */
#ifdef sqlite3Isdigit
/* Use the SQLite core versions if this routine is part of the
** SQLite amalgamation */
@@ -183420,6 +212783,7 @@ static void rtreecheck(
# define safe_isxdigit(x) isxdigit((unsigned char)(x))
#endif
+#ifndef JSON_NULL /* The following stuff repeats things found in json1 */
/*
** Growing our own isspace() routine this way is twice as fast as
** the library isspace() function.
@@ -183442,7 +212806,7 @@ static const char geopolyIsSpace[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
-#define safe_isspace(x) (geopolyIsSpace[(unsigned char)x])
+#define fast_isspace(x) (geopolyIsSpace[(unsigned char)x])
#endif /* JSON NULL - back to original code */
/* Compiler and version */
@@ -183499,6 +212863,14 @@ struct GeoPoly {
*/
#define GEOPOLY_SZ(N) (sizeof(GeoPoly) + sizeof(GeoCoord)*2*((N)-4))
+/* Macros to access coordinates of a GeoPoly.
+** We have to use these macros, rather than just say p->a[i] in order
+** to silence (incorrect) UBSAN warnings if the array index is too large.
+*/
+#define GeoX(P,I) (((GeoCoord*)(P)->a)[(I)*2])
+#define GeoY(P,I) (((GeoCoord*)(P)->a)[(I)*2+1])
+
+
/*
** State of a parse of a GeoJSON input.
*/
@@ -183523,7 +212895,7 @@ static void geopolySwab32(unsigned char *a){
/* Skip whitespace. Return the next non-whitespace character. */
static char geopolySkipSpace(GeoParse *p){
- while( safe_isspace(p->z[0]) ) p->z++;
+ while( fast_isspace(p->z[0]) ) p->z++;
return p->z[0];
}
@@ -183636,7 +213008,7 @@ static GeoPoly *geopolyParseJson(const unsigned char *z, int *pRc){
GeoPoly *pOut;
int x = 1;
s.nVertex--; /* Remove the redundant vertex at the end */
- pOut = sqlite3_malloc64( GEOPOLY_SZ(s.nVertex) );
+ pOut = sqlite3_malloc64( GEOPOLY_SZ((sqlite3_int64)s.nVertex) );
x = 1;
if( pOut==0 ) goto parse_json_err;
pOut->nVertex = s.nVertex;
@@ -183672,11 +213044,16 @@ static GeoPoly *geopolyFuncParam(
){
GeoPoly *p = 0;
int nByte;
+ testcase( pCtx==0 );
if( sqlite3_value_type(pVal)==SQLITE_BLOB
- && (nByte = sqlite3_value_bytes(pVal))>=(4+6*sizeof(GeoCoord))
+ && (nByte = sqlite3_value_bytes(pVal))>=(int)(4+6*sizeof(GeoCoord))
){
const unsigned char *a = sqlite3_value_blob(pVal);
int nVertex;
+ if( a==0 ){
+ if( pCtx ) sqlite3_result_error_nomem(pCtx);
+ return 0;
+ }
nVertex = (a[1]<<16) + (a[2]<<8) + a[3];
if( (a[0]==0 || a[0]==1)
&& (nVertex*2*sizeof(GeoCoord) + 4)==(unsigned int)nByte
@@ -183691,8 +213068,9 @@ static GeoPoly *geopolyFuncParam(
memcpy(p->hdr, a, nByte);
if( a[0] != *(unsigned char*)&x ){
int ii;
- for(ii=0; ii<nVertex*2; ii++){
- geopolySwab32((unsigned char*)&p->a[ii]);
+ for(ii=0; ii<nVertex; ii++){
+ geopolySwab32((unsigned char*)&GeoX(p,ii));
+ geopolySwab32((unsigned char*)&GeoY(p,ii));
}
p->hdr[0] ^= 1;
}
@@ -183726,8 +213104,9 @@ static void geopolyBlobFunc(
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ (void)argc;
if( p ){
- sqlite3_result_blob(context, p->hdr,
+ sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
sqlite3_free(p);
}
@@ -183745,15 +213124,16 @@ static void geopolyJsonFunc(
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ (void)argc;
if( p ){
sqlite3 *db = sqlite3_context_db_handle(context);
sqlite3_str *x = sqlite3_str_new(db);
int i;
sqlite3_str_append(x, "[", 1);
for(i=0; i<p->nVertex; i++){
- sqlite3_str_appendf(x, "[%!g,%!g],", p->a[i*2], p->a[i*2+1]);
+ sqlite3_str_appendf(x, "[%!g,%!g],", GeoX(p,i), GeoY(p,i));
}
- sqlite3_str_appendf(x, "[%!g,%!g]]", p->a[0], p->a[1]);
+ sqlite3_str_appendf(x, "[%!g,%!g]]", GeoX(p,0), GeoY(p,0));
sqlite3_result_text(context, sqlite3_str_finish(x), -1, sqlite3_free);
sqlite3_free(p);
}
@@ -183770,7 +213150,9 @@ static void geopolySvgFunc(
int argc,
sqlite3_value **argv
){
- GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ GeoPoly *p;
+ if( argc<1 ) return;
+ p = geopolyFuncParam(context, argv[0], 0);
if( p ){
sqlite3 *db = sqlite3_context_db_handle(context);
sqlite3_str *x = sqlite3_str_new(db);
@@ -183778,10 +213160,10 @@ static void geopolySvgFunc(
char cSep = '\'';
sqlite3_str_appendf(x, "<polyline points=");
for(i=0; i<p->nVertex; i++){
- sqlite3_str_appendf(x, "%c%g,%g", cSep, p->a[i*2], p->a[i*2+1]);
+ sqlite3_str_appendf(x, "%c%g,%g", cSep, GeoX(p,i), GeoY(p,i));
cSep = ' ';
}
- sqlite3_str_appendf(x, " %g,%g'", p->a[0], p->a[1]);
+ sqlite3_str_appendf(x, " %g,%g'", GeoX(p,0), GeoY(p,0));
for(i=1; i<argc; i++){
const char *z = (const char*)sqlite3_value_text(argv[i]);
if( z && z[0] ){
@@ -183824,16 +213206,17 @@ static void geopolyXformFunc(
double F = sqlite3_value_double(argv[6]);
GeoCoord x1, y1, x0, y0;
int ii;
+ (void)argc;
if( p ){
for(ii=0; ii<p->nVertex; ii++){
- x0 = p->a[ii*2];
- y0 = p->a[ii*2+1];
+ x0 = GeoX(p,ii);
+ y0 = GeoY(p,ii);
x1 = (GeoCoord)(A*x0 + B*y0 + E);
y1 = (GeoCoord)(C*x0 + D*y0 + F);
- p->a[ii*2] = x1;
- p->a[ii*2+1] = y1;
+ GeoX(p,ii) = x1;
+ GeoY(p,ii) = y1;
}
- sqlite3_result_blob(context, p->hdr,
+ sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
sqlite3_free(p);
}
@@ -183850,12 +213233,12 @@ static double geopolyArea(GeoPoly *p){
double rArea = 0.0;
int ii;
for(ii=0; ii<p->nVertex-1; ii++){
- rArea += (p->a[ii*2] - p->a[ii*2+2]) /* (x0 - x1) */
- * (p->a[ii*2+1] + p->a[ii*2+3]) /* (y0 + y1) */
+ rArea += (GeoX(p,ii) - GeoX(p,ii+1)) /* (x0 - x1) */
+ * (GeoY(p,ii) + GeoY(p,ii+1)) /* (y0 + y1) */
* 0.5;
}
- rArea += (p->a[ii*2] - p->a[0]) /* (xN - x0) */
- * (p->a[ii*2+1] + p->a[1]) /* (yN + y0) */
+ rArea += (GeoX(p,ii) - GeoX(p,0)) /* (xN - x0) */
+ * (GeoY(p,ii) + GeoY(p,0)) /* (yN + y0) */
* 0.5;
return rArea;
}
@@ -183874,10 +213257,11 @@ static void geopolyAreaFunc(
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ (void)argc;
if( p ){
sqlite3_result_double(context, geopolyArea(p));
sqlite3_free(p);
- }
+ }
}
/*
@@ -183885,7 +213269,7 @@ static void geopolyAreaFunc(
**
** If the rotation of polygon X is clockwise (incorrect) instead of
** counter-clockwise (the correct winding order according to RFC7946)
-** then reverse the order of the vertexes in polygon X.
+** then reverse the order of the vertexes in polygon X.
**
** In other words, this routine returns a CCW polygon regardless of the
** winding order of its input.
@@ -183899,22 +213283,23 @@ static void geopolyCcwFunc(
sqlite3_value **argv
){
GeoPoly *p = geopolyFuncParam(context, argv[0], 0);
+ (void)argc;
if( p ){
if( geopolyArea(p)<0.0 ){
int ii, jj;
- for(ii=2, jj=p->nVertex*2 - 2; ii<jj; ii+=2, jj-=2){
- GeoCoord t = p->a[ii];
- p->a[ii] = p->a[jj];
- p->a[jj] = t;
- t = p->a[ii+1];
- p->a[ii+1] = p->a[jj+1];
- p->a[jj+1] = t;
+ for(ii=1, jj=p->nVertex-1; ii<jj; ii++, jj--){
+ GeoCoord t = GeoX(p,ii);
+ GeoX(p,ii) = GeoX(p,jj);
+ GeoX(p,jj) = t;
+ t = GeoY(p,ii);
+ GeoY(p,ii) = GeoY(p,jj);
+ GeoY(p,jj) = t;
}
}
- sqlite3_result_blob(context, p->hdr,
+ sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
sqlite3_free(p);
- }
+ }
}
#define GEOPOLY_PI 3.1415926535897932385
@@ -183953,6 +213338,7 @@ static void geopolyRegularFunc(
int n = sqlite3_value_int(argv[3]);
int i;
GeoPoly *p;
+ (void)argc;
if( n<3 || r<=0.0 ) return;
if( n>1000 ) n = 1000;
@@ -183968,8 +213354,8 @@ static void geopolyRegularFunc(
p->hdr[3] = n&0xff;
for(i=0; i<n; i++){
double rAngle = 2.0*GEOPOLY_PI*i/n;
- p->a[i*2] = x - r*geopolySine(rAngle-0.5*GEOPOLY_PI);
- p->a[i*2+1] = y + r*geopolySine(rAngle);
+ GeoX(p,i) = x - r*geopolySine(rAngle-0.5*GEOPOLY_PI);
+ GeoY(p,i) = y + r*geopolySine(rAngle);
}
sqlite3_result_blob(context, p->hdr, 4+8*n, SQLITE_TRANSIENT);
sqlite3_free(p);
@@ -184006,20 +213392,20 @@ static GeoPoly *geopolyBBox(
}
if( p ){
int ii;
- mnX = mxX = p->a[0];
- mnY = mxY = p->a[1];
+ mnX = mxX = GeoX(p,0);
+ mnY = mxY = GeoY(p,0);
for(ii=1; ii<p->nVertex; ii++){
- double r = p->a[ii*2];
+ double r = GeoX(p,ii);
if( r<mnX ) mnX = (float)r;
else if( r>mxX ) mxX = (float)r;
- r = p->a[ii*2+1];
+ r = GeoY(p,ii);
if( r<mnY ) mnY = (float)r;
else if( r>mxY ) mxY = (float)r;
}
if( pRc ) *pRc = SQLITE_OK;
if( aCoord==0 ){
geopolyBboxFill:
- pOut = sqlite3_realloc(p, GEOPOLY_SZ(4));
+ pOut = sqlite3_realloc64(p, GEOPOLY_SZ(4));
if( pOut==0 ){
sqlite3_free(p);
if( context ) sqlite3_result_error_nomem(context);
@@ -184032,14 +213418,14 @@ static GeoPoly *geopolyBBox(
pOut->hdr[1] = 0;
pOut->hdr[2] = 0;
pOut->hdr[3] = 4;
- pOut->a[0] = mnX;
- pOut->a[1] = mnY;
- pOut->a[2] = mxX;
- pOut->a[3] = mnY;
- pOut->a[4] = mxX;
- pOut->a[5] = mxY;
- pOut->a[6] = mnX;
- pOut->a[7] = mxY;
+ GeoX(pOut,0) = mnX;
+ GeoY(pOut,0) = mnY;
+ GeoX(pOut,1) = mxX;
+ GeoY(pOut,1) = mnY;
+ GeoX(pOut,2) = mxX;
+ GeoY(pOut,2) = mxY;
+ GeoX(pOut,3) = mnX;
+ GeoY(pOut,3) = mxY;
}else{
sqlite3_free(p);
aCoord[0].f = mnX;
@@ -184047,6 +213433,8 @@ static GeoPoly *geopolyBBox(
aCoord[2].f = mnY;
aCoord[3].f = mxY;
}
+ }else if( aCoord ){
+ memset(aCoord, 0, sizeof(RtreeCoord)*4);
}
return pOut;
}
@@ -184060,8 +213448,9 @@ static void geopolyBBoxFunc(
sqlite3_value **argv
){
GeoPoly *p = geopolyBBox(context, argv[0], 0, 0);
+ (void)argc;
if( p ){
- sqlite3_result_blob(context, p->hdr,
+ sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
sqlite3_free(p);
}
@@ -184087,6 +213476,7 @@ static void geopolyBBoxStep(
){
RtreeCoord a[4];
int rc = SQLITE_OK;
+ (void)argc;
(void)geopolyBBox(context, argv[0], a, &rc);
if( rc==SQLITE_OK ){
GeoBBox *pBBox;
@@ -184112,7 +213502,7 @@ static void geopolyBBoxFinal(
if( pBBox==0 ) return;
p = geopolyBBox(context, 0, pBBox->a, 0);
if( p ){
- sqlite3_result_blob(context, p->hdr,
+ sqlite3_result_blob(context, p->hdr,
4+8*p->nVertex, SQLITE_TRANSIENT);
sqlite3_free(p);
}
@@ -184175,16 +213565,18 @@ static void geopolyContainsPointFunc(
int v = 0;
int cnt = 0;
int ii;
+ (void)argc;
+
if( p1==0 ) return;
for(ii=0; ii<p1->nVertex-1; ii++){
- v = pointBeneathLine(x0,y0,p1->a[ii*2],p1->a[ii*2+1],
- p1->a[ii*2+2],p1->a[ii*2+3]);
+ v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii),
+ GeoX(p1,ii+1),GeoY(p1,ii+1));
if( v==2 ) break;
cnt += v;
}
if( v!=2 ){
- v = pointBeneathLine(x0,y0,p1->a[ii*2],p1->a[ii*2+1],
- p1->a[0],p1->a[1]);
+ v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii),
+ GeoX(p1,0), GeoY(p1,0));
}
if( v==2 ){
sqlite3_result_int(context, 1);
@@ -184214,6 +213606,7 @@ static void geopolyWithinFunc(
){
GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0);
+ (void)argc;
if( p1 && p2 ){
int x = geopolyOverlap(p1, p2);
if( x<0 ){
@@ -184292,7 +213685,7 @@ static void geopolyAddOneSegment(
pEvent->eType = 1;
pEvent->pSeg = pSeg;
}
-
+
/*
@@ -184306,10 +213699,10 @@ static void geopolyAddSegments(
unsigned int i;
GeoCoord *x;
for(i=0; i<(unsigned)pPoly->nVertex-1; i++){
- x = pPoly->a + (i*2);
+ x = &GeoX(pPoly,i);
geopolyAddOneSegment(p, x[0], x[1], x[2], x[3], side, i);
}
- x = pPoly->a + (i*2);
+ x = &GeoX(pPoly,i);
geopolyAddOneSegment(p, x[0], x[1], pPoly->a[0], pPoly->a[1], side, i);
}
@@ -184332,7 +213725,7 @@ static GeoEvent *geopolyEventMerge(GeoEvent *pLeft, GeoEvent *pRight){
}
}
pLast->pNext = pRight ? pRight : pLeft;
- return head.pNext;
+ return head.pNext;
}
/*
@@ -184381,7 +213774,7 @@ static GeoSegment *geopolySegmentMerge(GeoSegment *pLeft, GeoSegment *pRight){
}
}
pLast->pNext = pRight ? pRight : pLeft;
- return head.pNext;
+ return head.pNext;
}
/*
@@ -184415,9 +213808,9 @@ static GeoSegment *geopolySortSegmentsByYAndC(GeoSegment *pList){
** Determine the overlap between two polygons
*/
static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){
- int nVertex = p1->nVertex + p2->nVertex + 2;
+ sqlite3_int64 nVertex = p1->nVertex + p2->nVertex + 2;
GeoOverlap *p;
- int nByte;
+ sqlite3_int64 nByte;
GeoEvent *pThisEvent;
double rX;
int rc = 0;
@@ -184426,10 +213819,10 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){
GeoSegment *pSeg;
unsigned char aOverlap[4];
- nByte = sizeof(GeoEvent)*nVertex*2
- + sizeof(GeoSegment)*nVertex
+ nByte = sizeof(GeoEvent)*nVertex*2
+ + sizeof(GeoSegment)*nVertex
+ sizeof(GeoOverlap);
- p = sqlite3_malloc( nByte );
+ p = sqlite3_malloc64( nByte );
if( p==0 ) return -1;
p->aEvent = (GeoEvent*)&p[1];
p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2];
@@ -184437,7 +213830,7 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){
geopolyAddSegments(p, p1, 1);
geopolyAddSegments(p, p2, 2);
pThisEvent = geopolySortEventsByX(p->aEvent, p->nEvent);
- rX = pThisEvent->x==0.0 ? -1.0 : 0.0;
+ rX = pThisEvent && pThisEvent->x==0.0 ? -1.0 : 0.0;
memset(aOverlap, 0, sizeof(aOverlap));
while( pThisEvent ){
if( pThisEvent->x!=rX ){
@@ -184496,11 +213889,11 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){
}else{
/* Remove a segment */
if( pActive==pThisEvent->pSeg ){
- pActive = pActive->pNext;
+ pActive = ALWAYS(pActive) ? pActive->pNext : 0;
}else{
for(pSeg=pActive; pSeg; pSeg=pSeg->pNext){
if( pSeg->pNext==pThisEvent->pSeg ){
- pSeg->pNext = pSeg->pNext->pNext;
+ pSeg->pNext = ALWAYS(pSeg->pNext) ? pSeg->pNext->pNext : 0;
break;
}
}
@@ -184544,6 +213937,7 @@ static void geopolyOverlapFunc(
){
GeoPoly *p1 = geopolyFuncParam(context, argv[0], 0);
GeoPoly *p2 = geopolyFuncParam(context, argv[1], 0);
+ (void)argc;
if( p1 && p2 ){
int x = geopolyOverlap(p1, p2);
if( x<0 ){
@@ -184564,12 +213958,16 @@ static void geopolyDebugFunc(
int argc,
sqlite3_value **argv
){
+ (void)context;
+ (void)argc;
#ifdef GEOPOLY_ENABLE_DEBUG
geo_debug = sqlite3_value_int(argv[0]);
+#else
+ (void)argv;
#endif
}
-/*
+/*
** This function is the implementation of both the xConnect and xCreate
** methods of the geopoly virtual table.
**
@@ -184588,31 +213986,36 @@ static int geopolyInit(
){
int rc = SQLITE_OK;
Rtree *pRtree;
- int nDb; /* Length of string argv[1] */
- int nName; /* Length of string argv[2] */
+ sqlite3_int64 nDb; /* Length of string argv[1] */
+ sqlite3_int64 nName; /* Length of string argv[2] */
sqlite3_str *pSql;
char *zSql;
int ii;
+ (void)pAux;
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
/* Allocate the sqlite3_vtab structure */
- nDb = (int)strlen(argv[1]);
- nName = (int)strlen(argv[2]);
- pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
+ nDb = strlen(argv[1]);
+ nName = strlen(argv[2]);
+ pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8);
if( !pRtree ){
return SQLITE_NOMEM;
}
- memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2);
+ memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8);
pRtree->nBusy = 1;
pRtree->base.pModule = &rtreeModule;
pRtree->zDb = (char *)&pRtree[1];
pRtree->zName = &pRtree->zDb[nDb+1];
+ pRtree->zNodeName = &pRtree->zName[nName+1];
pRtree->eCoordType = RTREE_COORD_REAL32;
pRtree->nDim = 2;
pRtree->nDim2 = 4;
memcpy(pRtree->zDb, argv[1], nDb);
memcpy(pRtree->zName, argv[2], nName);
+ memcpy(pRtree->zNodeName, argv[2], nName);
+ memcpy(&pRtree->zNodeName[nName], "_node", 6);
/* Create/Connect to the underlying relational database schema. If
@@ -184659,7 +214062,7 @@ geopolyInit_fail:
}
-/*
+/*
** GEOPOLY virtual table module xCreate method.
*/
static int geopolyCreate(
@@ -184672,7 +214075,7 @@ static int geopolyCreate(
return geopolyInit(db, pAux, argc, argv, ppVtab, pzErr, 1);
}
-/*
+/*
** GEOPOLY virtual table module xConnect method.
*/
static int geopolyConnect(
@@ -184686,7 +214089,7 @@ static int geopolyConnect(
}
-/*
+/*
** GEOPOLY virtual table module xFilter method.
**
** Query plans:
@@ -184709,17 +214112,12 @@ static int geopolyFilter(
RtreeNode *pRoot = 0;
int rc = SQLITE_OK;
int iCell = 0;
- sqlite3_stmt *pStmt;
+ (void)idxStr;
rtreeReference(pRtree);
/* Reset the cursor to the same state as rtreeOpen() leaves it in. */
- freeCursorConstraints(pCsr);
- sqlite3_free(pCsr->aPoint);
- pStmt = pCsr->pReadAux;
- memset(pCsr, 0, sizeof(RtreeCursor));
- pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
- pCsr->pReadAux = pStmt;
+ resetCursor(pCsr);
pCsr->iStrategy = idxNum;
if( idxNum==1 ){
@@ -184742,14 +214140,15 @@ static int geopolyFilter(
pCsr->atEOF = 1;
}
}else{
- /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
- ** with the configured constraints.
+ /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
+ ** with the configured constraints.
*/
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
if( rc==SQLITE_OK && idxNum<=3 ){
RtreeCoord bbox[4];
RtreeConstraint *p;
assert( argc==1 );
+ assert( argv[0]!=0 );
geopolyBBox(0, argv[0], bbox, &rc);
if( rc ){
goto geopoly_filter_end;
@@ -184824,7 +214223,7 @@ geopoly_filter_end:
/*
** Rtree virtual table module xBestIndex method. There are three
-** table scan strategies to choose from (in order from most to
+** table scan strategies to choose from (in order from most to
** least desirable):
**
** idxNum idxStr Strategy
@@ -184840,6 +214239,7 @@ static int geopolyBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int iRowidTerm = -1;
int iFuncTerm = -1;
int idxNum = 0;
+ (void)tab;
for(ii=0; ii<pIdxInfo->nConstraint; ii++){
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
@@ -184884,7 +214284,7 @@ static int geopolyBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
}
-/*
+/*
** GEOPOLY virtual table module xColumn method.
*/
static int geopolyColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
@@ -184904,7 +214304,7 @@ static int geopolyColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
&pCsr->pReadAux, 0);
if( rc ) return rc;
}
- sqlite3_bind_int64(pCsr->pReadAux, 1,
+ sqlite3_bind_int64(pCsr->pReadAux, 1,
nodeGetRowid(pRtree, pNode, p->iCell));
rc = sqlite3_step(pCsr->pReadAux);
if( rc==SQLITE_ROW ){
@@ -184943,9 +214343,9 @@ static int geopolyColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
** argv[3] = new value for first application-defined column....
*/
static int geopolyUpdate(
- sqlite3_vtab *pVtab,
- int nData,
- sqlite3_value **aData,
+ sqlite3_vtab *pVtab,
+ int nData,
+ sqlite3_value **aData,
sqlite_int64 *pRowid
){
Rtree *pRtree = (Rtree *)pVtab;
@@ -184977,6 +214377,7 @@ static int geopolyUpdate(
|| !sqlite3_value_nochange(aData[2]) /* UPDATE _shape */
|| oldRowid!=newRowid) /* Rowid change */
){
+ assert( aData[2]!=0 );
geopolyBBox(0, aData[2], cell.aCoord, &rc);
if( rc ){
if( rc==SQLITE_ERROR ){
@@ -184987,7 +214388,7 @@ static int geopolyUpdate(
}
coordChange = 1;
- /* If a rowid value was supplied, check if it is already present in
+ /* If a rowid value was supplied, check if it is already present in
** the table. If so, the constraint has failed. */
if( newRowidValid && (!oldRowidValid || oldRowid!=newRowid) ){
int steprc;
@@ -185028,7 +214429,6 @@ static int geopolyUpdate(
}
if( rc==SQLITE_OK ){
int rc2;
- pRtree->iReinsertHeight = -1;
rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0);
rc2 = nodeRelease(pRtree, pLeaf);
if( rc==SQLITE_OK ){
@@ -185059,7 +214459,7 @@ static int geopolyUpdate(
sqlite3_free(p);
nChange = 1;
}
- for(jj=1; jj<pRtree->nAux; jj++){
+ for(jj=1; jj<nData-2; jj++){
nChange++;
sqlite3_bind_value(pUp, jj+2, aData[jj+2]);
}
@@ -185085,6 +214485,8 @@ static int geopolyFindFunction(
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
void **ppArg
){
+ (void)pVtab;
+ (void)nArg;
if( sqlite3_stricmp(zName, "geopoly_overlap")==0 ){
*pxFunc = geopolyOverlapFunc;
*ppArg = 0;
@@ -185123,7 +214525,8 @@ static sqlite3_module geopolyModule = {
rtreeSavepoint, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- rtreeShadowName /* xShadowName */
+ rtreeShadowName, /* xShadowName */
+ rtreeIntegrity /* xIntegrity */
};
static int sqlite3_geopoly_init(sqlite3 *db){
@@ -185154,16 +214557,22 @@ static int sqlite3_geopoly_init(sqlite3 *db){
} aAgg[] = {
{ geopolyBBoxStep, geopolyBBoxFinal, "geopoly_group_bbox" },
};
- int i;
+ unsigned int i;
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
- int enc = aFunc[i].bPure ? SQLITE_UTF8|SQLITE_DETERMINISTIC : SQLITE_UTF8;
+ int enc;
+ if( aFunc[i].bPure ){
+ enc = SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS;
+ }else{
+ enc = SQLITE_UTF8|SQLITE_DIRECTONLY;
+ }
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
enc, 0,
aFunc[i].xFunc, 0, 0);
}
for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
- rc = sqlite3_create_function(db, aAgg[i].zName, 1, SQLITE_UTF8, 0,
- 0, aAgg[i].xStep, aAgg[i].xFinal);
+ rc = sqlite3_create_function(db, aAgg[i].zName, 1,
+ SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS, 0,
+ 0, aAgg[i].xStep, aAgg[i].xFinal);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_module_v2(db, "geopoly", &geopolyModule, 0, 0);
@@ -185177,7 +214586,7 @@ static int sqlite3_geopoly_init(sqlite3 *db){
/*
** Register the r-tree module with database handle db. This creates the
-** virtual table module "rtree" and the debugging/analysis scalar
+** virtual table module "rtree" and the debugging/analysis scalar
** function "rtreenode".
*/
SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db){
@@ -185254,12 +214663,12 @@ static void rtreeMatchArgFree(void *pArg){
static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
RtreeMatchArg *pBlob;
- int nBlob;
+ sqlite3_int64 nBlob;
int memErr = 0;
nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue)
+ nArg*sizeof(sqlite3_value*);
- pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob);
+ pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob);
if( !pBlob ){
sqlite3_result_error_nomem(ctx);
}else{
@@ -185304,7 +214713,7 @@ SQLITE_API int sqlite3_rtree_geometry_callback(
pGeomCtx->xQueryFunc = 0;
pGeomCtx->xDestructor = 0;
pGeomCtx->pContext = pContext;
- return sqlite3_create_function_v2(db, zGeom, -1, SQLITE_ANY,
+ return sqlite3_create_function_v2(db, zGeom, -1, SQLITE_ANY,
(void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
);
}
@@ -185324,12 +214733,15 @@ SQLITE_API int sqlite3_rtree_query_callback(
/* Allocate and populate the context object. */
pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback));
- if( !pGeomCtx ) return SQLITE_NOMEM;
+ if( !pGeomCtx ){
+ if( xDestructor ) xDestructor(pContext);
+ return SQLITE_NOMEM;
+ }
pGeomCtx->xGeom = 0;
pGeomCtx->xQueryFunc = xQueryFunc;
pGeomCtx->xDestructor = xDestructor;
pGeomCtx->pContext = pContext;
- return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY,
+ return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY,
(void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
);
}
@@ -185365,9 +214777,9 @@ SQLITE_API int sqlite3_rtree_init(
*************************************************************************
** $Id: icu.c,v 1.7 2007/12/13 21:54:11 drh Exp $
**
-** This file implements an integration between the ICU library
-** ("International Components for Unicode", an open-source library
-** for handling unicode data) and SQLite. The integration uses
+** This file implements an integration between the ICU library
+** ("International Components for Unicode", an open-source library
+** for handling unicode data) and SQLite. The integration uses
** ICU to provide the following to SQLite:
**
** * An implementation of the SQL regexp() function (and hence REGEXP
@@ -185378,7 +214790,7 @@ SQLITE_API int sqlite3_rtree_init(
**
** * Integration of ICU and SQLite collation sequences.
**
-** * An implementation of the LIKE operator that uses ICU to
+** * An implementation of the LIKE operator that uses ICU to
** provide case-independent matching.
*/
@@ -185405,7 +214817,7 @@ SQLITE_API int sqlite3_rtree_init(
** This function is called when an ICU function called from within
** the implementation of an SQL scalar function returns an error.
**
-** The scalar function context passed as the first argument is
+** The scalar function context passed as the first argument is
** loaded with an error message based on the following two args.
*/
static void icuFunctionError(
@@ -185470,7 +214882,7 @@ static const unsigned char icuUtf8Trans1[] = {
/*
** Compare two UTF-8 strings for equality where the first string is
-** a "LIKE" expression. Return true (1) if they are the same and
+** a "LIKE" expression. Return true (1) if they are the same and
** false (0) if they are different.
*/
static int icuLikeCompare(
@@ -185497,12 +214909,12 @@ static int icuLikeCompare(
** 3. uPattern is an unescaped escape character, or
** 4. uPattern is to be handled as an ordinary character
*/
- if( !prevEscape && uPattern==MATCH_ALL ){
+ if( uPattern==MATCH_ALL && !prevEscape && uPattern!=(uint32_t)uEsc ){
/* Case 1. */
uint8_t c;
/* Skip any MATCH_ALL or MATCH_ONE characters that follow a
- ** MATCH_ALL. For each MATCH_ONE, skip one character in the
+ ** MATCH_ALL. For each MATCH_ONE, skip one character in the
** test string.
*/
while( (c=*zPattern) == MATCH_ALL || c == MATCH_ONE ){
@@ -185523,12 +214935,12 @@ static int icuLikeCompare(
}
return 0;
- }else if( !prevEscape && uPattern==MATCH_ONE ){
+ }else if( uPattern==MATCH_ONE && !prevEscape && uPattern!=(uint32_t)uEsc ){
/* Case 2. */
if( *zString==0 ) return 0;
SQLITE_ICU_SKIP_UTF8(zString);
- }else if( !prevEscape && uPattern==(uint32_t)uEsc){
+ }else if( uPattern==(uint32_t)uEsc && !prevEscape ){
/* Case 3. */
prevEscape = 1;
@@ -185555,15 +214967,15 @@ static int icuLikeCompare(
**
** A LIKE B
**
-** is implemented as like(B, A). If there is an escape character E,
+** is implemented as like(B, A). If there is an escape character E,
**
** A LIKE B ESCAPE E
**
** is mapped to like(B, A, E).
*/
static void icuLikeFunc(
- sqlite3_context *context,
- int argc,
+ sqlite3_context *context,
+ int argc,
sqlite3_value **argv
){
const unsigned char *zA = sqlite3_value_text(argv[0]);
@@ -185589,7 +215001,7 @@ static void icuLikeFunc(
if( zE==0 ) return;
U8_NEXT(zE, i, nE, uEsc);
if( i!=nE){
- sqlite3_result_error(context,
+ sqlite3_result_error(context,
"ESCAPE expression must be a single character", -1);
return;
}
@@ -185612,7 +215024,7 @@ static void icuRegexpDelete(void *p){
/*
** Implementation of SQLite REGEXP operator. This scalar function takes
** two arguments. The first is a regular expression pattern to compile
-** the second is a string to match against that pattern. If either
+** the second is a string to match against that pattern. If either
** argument is an SQL NULL, then NULL Is returned. Otherwise, the result
** is 1 if the string matches the pattern, or 0 otherwise.
**
@@ -185636,8 +215048,8 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
(void)nArg; /* Unused parameter */
- /* If the left hand side of the regexp operator is NULL,
- ** then the result is also NULL.
+ /* If the left hand side of the regexp operator is NULL,
+ ** then the result is also NULL.
*/
if( !zString ){
return;
@@ -185653,8 +215065,9 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
if( U_SUCCESS(status) ){
sqlite3_set_auxdata(p, 0, pExpr, icuRegexpDelete);
- }else{
- assert(!pExpr);
+ pExpr = sqlite3_get_auxdata(p, 0);
+ }
+ if( !pExpr ){
icuFunctionError(p, "uregex_open", status);
return;
}
@@ -185675,7 +215088,7 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
}
/* Set the text that the regular expression operates on to a NULL
- ** pointer. This is not really necessary, but it is tidier than
+ ** pointer. This is not really necessary, but it is tidier than
** leaving the regular expression object configured with an invalid
** pointer after this function returns.
*/
@@ -185686,7 +215099,7 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
}
/*
-** Implementations of scalar functions for case mapping - upper() and
+** Implementations of scalar functions for case mapping - upper() and
** lower(). Function upper() converts its input to upper-case (ABC).
** Function lower() converts to lower-case (abc).
**
@@ -185694,7 +215107,7 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
** "language specific". Refer to ICU documentation for the differences
** between the two.
**
-** To utilise "general" case mapping, the upper() or lower() scalar
+** To utilise "general" case mapping, the upper() or lower() scalar
** functions are invoked with one argument:
**
** upper('ABC') -> 'abc'
@@ -185802,7 +215215,7 @@ static int icuCollationColl(
/*
** Implementation of the scalar function icu_load_collation().
**
-** This scalar function is used to add ICU collation based collation
+** This scalar function is used to add ICU collation based collation
** types to an SQLite database connection. It is intended to be called
** as follows:
**
@@ -185813,8 +215226,8 @@ static int icuCollationColl(
** collation sequence to create.
*/
static void icuLoadCollation(
- sqlite3_context *p,
- int nArg,
+ sqlite3_context *p,
+ int nArg,
sqlite3_value **apArg
){
sqlite3 *db = (sqlite3 *)sqlite3_user_data(p);
@@ -185840,7 +215253,7 @@ static void icuLoadCollation(
}
assert(p);
- rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator,
+ rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator,
icuCollationColl, icuCollationDel
);
if( rc!=SQLITE_OK ){
@@ -185853,35 +215266,36 @@ static void icuLoadCollation(
** Register the ICU extension functions with database db.
*/
SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){
+# define SQLITEICU_EXTRAFLAGS (SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS)
static const struct IcuScalar {
const char *zName; /* Function name */
unsigned char nArg; /* Number of arguments */
- unsigned short enc; /* Optimal text encoding */
+ unsigned int enc; /* Optimal text encoding */
unsigned char iContext; /* sqlite3_user_data() context */
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} scalars[] = {
- {"icu_load_collation", 2, SQLITE_UTF8, 1, icuLoadCollation},
+ {"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
- {"regexp", 2, SQLITE_ANY|SQLITE_DETERMINISTIC, 0, icuRegexpFunc},
- {"lower", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
- {"lower", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
- {"upper", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
- {"upper", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
- {"lower", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
- {"lower", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
- {"upper", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
- {"upper", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
- {"like", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
- {"like", 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
+ {"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc},
+ {"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
+ {"lower", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
+ {"upper", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
+ {"upper", 2, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
+ {"lower", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
+ {"lower", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
+ {"upper", 1, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
+ {"upper", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 1, icuCaseFunc16},
+ {"like", 2, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
+ {"like", 3, SQLITE_UTF8|SQLITEICU_EXTRAFLAGS, 0, icuLikeFunc},
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU) */
};
int rc = SQLITE_OK;
int i;
-
+
for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
const struct IcuScalar *p = &scalars[i];
rc = sqlite3_create_function(
- db, p->zName, p->nArg, p->enc,
+ db, p->zName, p->nArg, p->enc,
p->iContext ? (void*)db : (void*)0,
p->xFunc, 0, 0
);
@@ -185895,7 +215309,7 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_icu_init(
- sqlite3 *db,
+ sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
@@ -185970,7 +215384,7 @@ static int icuCreate(
if( argc>0 ){
n = strlen(argv[0])+1;
}
- p = (IcuTokenizer *)sqlite3_malloc(sizeof(IcuTokenizer)+n);
+ p = (IcuTokenizer *)sqlite3_malloc64(sizeof(IcuTokenizer)+n);
if( !p ){
return SQLITE_NOMEM;
}
@@ -185998,7 +215412,7 @@ static int icuDestroy(sqlite3_tokenizer *pTokenizer){
/*
** 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
+** used to incrementally tokenize this string is returned in
** *ppCursor.
*/
static int icuOpen(
@@ -186027,7 +215441,7 @@ static int icuOpen(
nInput = strlen(zInput);
}
nChar = nInput+1;
- pCsr = (IcuCursor *)sqlite3_malloc(
+ pCsr = (IcuCursor *)sqlite3_malloc64(
sizeof(IcuCursor) + /* IcuCursor */
((nChar+3)&~3) * sizeof(UChar) + /* IcuCursor.aChar[] */
(nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */
@@ -186040,7 +215454,7 @@ static int icuOpen(
pCsr->aOffset = (int *)&pCsr->aChar[(nChar+3)&~3];
pCsr->aOffset[iOut] = iInput;
- U8_NEXT(zInput, iInput, nInput, c);
+ U8_NEXT(zInput, iInput, nInput, c);
while( c>0 ){
int isError = 0;
c = u_foldCase(c, opt);
@@ -186186,7 +215600,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
*************************************************************************
**
**
-** OVERVIEW
+** OVERVIEW
**
** The RBU extension requires that the RBU update be packaged as an
** SQLite database. The tables it expects to find are described in
@@ -186194,34 +215608,34 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
** that the user wishes to write to, a corresponding data_xyz table is
** created in the RBU database and populated with one row for each row to
** update, insert or delete from the target table.
-**
+**
** The update proceeds in three stages:
-**
+**
** 1) The database is updated. The modified database pages are written
** to a *-oal file. A *-oal file is just like a *-wal file, except
** that it is named "<database>-oal" instead of "<database>-wal".
** Because regular SQLite clients do not look for file named
** "<database>-oal", they go on using the original database in
** rollback mode while the *-oal file is being generated.
-**
+**
** During this stage RBU does not update the database by writing
** directly to the target tables. Instead it creates "imposter"
** tables using the SQLITE_TESTCTRL_IMPOSTER interface that it uses
** to update each b-tree individually. All updates required by each
** b-tree are completed before moving on to the next, and all
** updates are done in sorted key order.
-**
+**
** 2) The "<database>-oal" file is moved to the equivalent "<database>-wal"
** location using a call to rename(2). Before doing this the RBU
** module takes an EXCLUSIVE lock on the database file, ensuring
** that there are no other active readers.
-**
+**
** Once the EXCLUSIVE lock is released, any other database readers
** detect the new *-wal file and read the database in wal mode. At
** this point they see the new version of the database - including
** the updates made as part of the RBU update.
-**
-** 3) The new *-wal file is checkpointed. This proceeds in the same way
+**
+** 3) The new *-wal file is checkpointed. This proceeds in the same way
** as a regular database checkpoint, except that a single frame is
** checkpointed each time sqlite3rbu_step() is called. If the RBU
** handle is closed before the entire *-wal file is checkpointed,
@@ -186230,7 +215644,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
** the future.
**
** POTENTIAL PROBLEMS
-**
+**
** The rename() call might not be portable. And RBU is not currently
** syncing the directory after renaming the file.
**
@@ -186252,7 +215666,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
** fields are collected. This means we're probably writing a lot more
** data to disk when saving the state of an ongoing update to the RBU
** update database than is strictly necessary.
-**
+**
*/
/* #include <assert.h> */
@@ -186276,42 +215690,42 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
**
*************************************************************************
**
-** This file contains the public interface for the RBU extension.
+** This file contains the public interface for the RBU extension.
*/
/*
** SUMMARY
**
-** Writing a transaction containing a large number of operations on
+** Writing a transaction containing a large number of operations on
** b-tree indexes that are collectively larger than the available cache
-** memory can be very inefficient.
+** memory can be very inefficient.
**
** The problem is that in order to update a b-tree, the leaf page (at least)
** containing the entry being inserted or deleted must be modified. If the
-** working set of leaves is larger than the available cache memory, then a
-** single leaf that is modified more than once as part of the transaction
+** working set of leaves is larger than the available cache memory, then a
+** single leaf that is modified more than once as part of the transaction
** may be loaded from or written to the persistent media multiple times.
** Additionally, because the index updates are likely to be applied in
-** random order, access to pages within the database is also likely to be in
+** random order, access to pages within the database is also likely to be in
** random order, which is itself quite inefficient.
**
** One way to improve the situation is to sort the operations on each index
** by index key before applying them to the b-tree. This leads to an IO
** pattern that resembles a single linear scan through the index b-tree,
-** and all but guarantees each modified leaf page is loaded and stored
+** and all but guarantees each modified leaf page is loaded and stored
** exactly once. SQLite uses this trick to improve the performance of
** CREATE INDEX commands. This extension allows it to be used to improve
** the performance of large transactions on existing databases.
**
-** Additionally, this extension allows the work involved in writing the
-** large transaction to be broken down into sub-transactions performed
-** sequentially by separate processes. This is useful if the system cannot
-** guarantee that a single update process will run for long enough to apply
-** the entire update, for example because the update is being applied on a
-** mobile device that is frequently rebooted. Even after the writer process
+** Additionally, this extension allows the work involved in writing the
+** large transaction to be broken down into sub-transactions performed
+** sequentially by separate processes. This is useful if the system cannot
+** guarantee that a single update process will run for long enough to apply
+** the entire update, for example because the update is being applied on a
+** mobile device that is frequently rebooted. Even after the writer process
** has committed one or more sub-transactions, other database clients continue
-** to read from the original database snapshot. In other words, partially
-** applied transactions are not visible to other clients.
+** to read from the original database snapshot. In other words, partially
+** applied transactions are not visible to other clients.
**
** "RBU" stands for "Resumable Bulk Update". As in a large database update
** transmitted via a wireless network to a mobile device. A transaction
@@ -186327,9 +215741,9 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
**
** * INSERT statements may not use any default values.
**
-** * UPDATE and DELETE statements must identify their target rows by
+** * UPDATE and DELETE statements must identify their target rows by
** non-NULL PRIMARY KEY values. Rows with NULL values stored in PRIMARY
-** KEY fields may not be updated or deleted. If the table being written
+** KEY fields may not be updated or deleted. If the table being written
** has no PRIMARY KEY, affected rows must be identified by rowid.
**
** * UPDATE statements may not modify PRIMARY KEY columns.
@@ -186346,10 +215760,10 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
** PREPARATION
**
** An "RBU update" is stored as a separate SQLite database. A database
-** containing an RBU update is an "RBU database". For each table in the
+** containing an RBU update is an "RBU database". For each table in the
** target database to be updated, the RBU database should contain a table
** named "data_<target name>" containing the same set of columns as the
-** target table, and one more - "rbu_control". The data_% table should
+** target table, and one more - "rbu_control". The data_% table should
** have no PRIMARY KEY or UNIQUE constraints, but each column should have
** the same type as the corresponding column in the target database.
** The "rbu_control" column should have no type at all. For example, if
@@ -186364,22 +215778,22 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
** The order of the columns in the data_% table does not matter.
**
** Instead of a regular table, the RBU database may also contain virtual
-** tables or view named using the data_<target> naming scheme.
+** tables or views named using the data_<target> naming scheme.
**
-** Instead of the plain data_<target> naming scheme, RBU database tables
+** Instead of the plain data_<target> naming scheme, RBU database tables
** may also be named data<integer>_<target>, where <integer> is any sequence
** of zero or more numeric characters (0-9). This can be significant because
-** tables within the RBU database are always processed in order sorted by
+** tables within the RBU database are always processed in order sorted by
** name. By judicious selection of the <integer> portion of the names
** of the RBU tables the user can therefore control the order in which they
** are processed. This can be useful, for example, to ensure that "external
** content" FTS4 tables are updated before their underlying content tables.
**
** If the target database table is a virtual table or a table that has no
-** PRIMARY KEY declaration, the data_% table must also contain a column
-** named "rbu_rowid". This column is mapped to the tables implicit primary
-** key column - "rowid". Virtual tables for which the "rowid" column does
-** not function like a primary key value cannot be updated using RBU. For
+** PRIMARY KEY declaration, the data_% table must also contain a column
+** named "rbu_rowid". This column is mapped to the table's implicit primary
+** key column - "rowid". Virtual tables for which the "rowid" column does
+** not function like a primary key value cannot be updated using RBU. For
** example, if the target db contains either of the following:
**
** CREATE VIRTUAL TABLE x1 USING fts3(a, b);
@@ -186402,35 +215816,35 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
** CREATE TABLE data_ft1(a, b, langid, rbu_rowid, rbu_control);
** CREATE TABLE data_ft1(a, b, rbu_rowid, rbu_control);
**
-** For each row to INSERT into the target database as part of the RBU
+** For each row to INSERT into the target database as part of the RBU
** update, the corresponding data_% table should contain a single record
** with the "rbu_control" column set to contain integer value 0. The
-** other columns should be set to the values that make up the new record
-** to insert.
+** other columns should be set to the values that make up the new record
+** to insert.
**
-** If the target database table has an INTEGER PRIMARY KEY, it is not
-** possible to insert a NULL value into the IPK column. Attempting to
+** If the target database table has an INTEGER PRIMARY KEY, it is not
+** possible to insert a NULL value into the IPK column. Attempting to
** do so results in an SQLITE_MISMATCH error.
**
-** For each row to DELETE from the target database as part of the RBU
+** For each row to DELETE from the target database as part of the RBU
** update, the corresponding data_% table should contain a single record
** with the "rbu_control" column set to contain integer value 1. The
** real primary key values of the row to delete should be stored in the
** corresponding columns of the data_% table. The values stored in the
** other columns are not used.
**
-** For each row to UPDATE from the target database as part of the RBU
+** For each row to UPDATE from the target database as part of the RBU
** update, the corresponding data_% table should contain a single record
** with the "rbu_control" column set to contain a value of type text.
-** The real primary key values identifying the row to update should be
+** The real primary key values identifying the row to update should be
** stored in the corresponding columns of the data_% table row, as should
-** the new values of all columns being update. The text value in the
+** the new values of all columns being update. The text value in the
** "rbu_control" column must contain the same number of characters as
** there are columns in the target database table, and must consist entirely
-** of 'x' and '.' characters (or in some special cases 'd' - see below). For
+** of 'x' and '.' characters (or in some special cases 'd' - see below). For
** each column that is being updated, the corresponding character is set to
** 'x'. For those that remain as they are, the corresponding character of the
-** rbu_control value should be set to '.'. For example, given the tables
+** rbu_control value should be set to '.'. For example, given the tables
** above, the update statement:
**
** UPDATE t1 SET c = 'usa' WHERE a = 4;
@@ -186444,30 +215858,30 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
** target table with the value stored in the corresponding data_% column, the
** user-defined SQL function "rbu_delta()" is invoked and the result stored in
** the target table column. rbu_delta() is invoked with two arguments - the
-** original value currently stored in the target table column and the
+** original value currently stored in the target table column and the
** value specified in the data_xxx table.
**
** For example, this row:
**
** INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..d');
**
-** is similar to an UPDATE statement such as:
+** is similar to an UPDATE statement such as:
**
** UPDATE t1 SET c = rbu_delta(c, 'usa') WHERE a = 4;
**
-** Finally, if an 'f' character appears in place of a 'd' or 's' in an
+** Finally, if an 'f' character appears in place of a 'd' or 's' in an
** ota_control string, the contents of the data_xxx table column is assumed
** to be a "fossil delta" - a patch to be applied to a blob value in the
** format used by the fossil source-code management system. In this case
-** the existing value within the target database table must be of type BLOB.
+** the existing value within the target database table must be of type BLOB.
** It is replaced by the result of applying the specified fossil delta to
** itself.
**
** If the target database table is a virtual table or a table with no PRIMARY
-** KEY, the rbu_control value should not include a character corresponding
+** KEY, the rbu_control value should not include a character corresponding
** to the rbu_rowid value. For example, this:
**
-** INSERT INTO data_ft1(a, b, rbu_rowid, rbu_control)
+** INSERT INTO data_ft1(a, b, rbu_rowid, rbu_control)
** VALUES(NULL, 'usa', 12, '.x');
**
** causes a result similar to:
@@ -186477,14 +215891,14 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
** The data_xxx tables themselves should have no PRIMARY KEY declarations.
** However, RBU is more efficient if reading the rows in from each data_xxx
** table in "rowid" order is roughly the same as reading them sorted by
-** the PRIMARY KEY of the corresponding target database table. In other
-** words, rows should be sorted using the destination table PRIMARY KEY
+** the PRIMARY KEY of the corresponding target database table. In other
+** words, rows should be sorted using the destination table PRIMARY KEY
** fields before they are inserted into the data_xxx tables.
**
** USAGE
**
-** The API declared below allows an application to apply an RBU update
-** stored on disk to an existing target database. Essentially, the
+** The API declared below allows an application to apply an RBU update
+** stored on disk to an existing target database. Essentially, the
** application:
**
** 1) Opens an RBU handle using the sqlite3rbu_open() function.
@@ -186495,24 +215909,24 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
**
** 3) Calls the sqlite3rbu_step() function one or more times on
** the new handle. Each call to sqlite3rbu_step() performs a single
-** b-tree operation, so thousands of calls may be required to apply
+** b-tree operation, so thousands of calls may be required to apply
** a complete update.
**
** 4) Calls sqlite3rbu_close() to close the RBU update handle. If
** sqlite3rbu_step() has been called enough times to completely
** apply the update to the target database, then the RBU database
-** is marked as fully applied. Otherwise, the state of the RBU
-** update application is saved in the RBU database for later
+** is marked as fully applied. Otherwise, the state of the RBU
+** update application is saved in the RBU database for later
** resumption.
**
** See comments below for more detail on APIs.
**
** If an update is only partially applied to the target database by the
-** time sqlite3rbu_close() is called, various state information is saved
+** time sqlite3rbu_close() is called, various state information is saved
** within the RBU database. This allows subsequent processes to automatically
** resume the RBU update from where it left off.
**
-** To remove all RBU extension state information, returning an RBU database
+** To remove all RBU extension state information, returning an RBU database
** to its original contents, it is sufficient to drop all tables that begin
** with the prefix "rbu_"
**
@@ -186548,21 +215962,21 @@ typedef struct sqlite3rbu sqlite3rbu;
** the path to the RBU database. Each call to this function must be matched
** by a call to sqlite3rbu_close(). When opening the databases, RBU passes
** the SQLITE_CONFIG_URI flag to sqlite3_open_v2(). So if either zTarget
-** or zRbu begin with "file:", it will be interpreted as an SQLite
+** or zRbu begin with "file:", it will be interpreted as an SQLite
** database URI, not a regular file name.
**
-** If the zState argument is passed a NULL value, the RBU extension stores
-** the current state of the update (how many rows have been updated, which
+** If the zState argument is passed a NULL value, the RBU extension stores
+** the current state of the update (how many rows have been updated, which
** indexes are yet to be updated etc.) within the RBU database itself. This
** can be convenient, as it means that the RBU application does not need to
-** organize removing a separate state file after the update is concluded.
-** Or, if zState is non-NULL, it must be a path to a database file in which
+** organize removing a separate state file after the update is concluded.
+** Or, if zState is non-NULL, it must be a path to a database file in which
** the RBU extension can store the state of the update.
**
** When resuming an RBU update, the zState argument must be passed the same
** value as when the RBU update was started.
**
-** Once the RBU update is finished, the RBU extension does not
+** Once the RBU update is finished, the RBU extension does not
** automatically remove any zState database file, even if it created it.
**
** By default, RBU uses the default VFS to access the files on disk. To
@@ -186575,7 +215989,7 @@ typedef struct sqlite3rbu sqlite3rbu;
** the zipvfs_create_vfs() API below for details on using RBU with zipvfs.
*/
SQLITE_API sqlite3rbu *sqlite3rbu_open(
- const char *zTarget,
+ const char *zTarget,
const char *zRbu,
const char *zState
);
@@ -186585,13 +215999,13 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open(
** An RBU vacuum is similar to SQLite's built-in VACUUM command, except
** that it can be suspended and resumed like an RBU update.
**
-** The second argument to this function identifies a database in which
-** to store the state of the RBU vacuum operation if it is suspended. The
+** The second argument to this function identifies a database in which
+** to store the state of the RBU vacuum operation if it is suspended. The
** first time sqlite3rbu_vacuum() is called, to start an RBU vacuum
** operation, the state database should either not exist or be empty
-** (contain no tables). If an RBU vacuum is suspended by calling
+** (contain no tables). If an RBU vacuum is suspended by calling
** sqlite3rbu_close() on the RBU handle before sqlite3rbu_step() has
-** returned SQLITE_DONE, the vacuum state is stored in the state database.
+** returned SQLITE_DONE, the vacuum state is stored in the state database.
** The vacuum can be resumed by calling this function to open a new RBU
** handle specifying the same target and state databases.
**
@@ -186601,20 +216015,24 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open(
** state database is not already present in the file-system, it is created
** with the same permissions as the target db is made.
**
+** With an RBU vacuum, it is an SQLITE_MISUSE error if the name of the
+** state database ends with "-vactmp". This name is reserved for internal
+** use.
+**
** This function does not delete the state database after an RBU vacuum
** is completed, even if it created it. However, if the call to
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
** of the state tables within the state database are zeroed. This way,
-** the next call to sqlite3rbu_vacuum() opens a handle that starts a
+** the next call to sqlite3rbu_vacuum() opens a handle that starts a
** new RBU vacuum operation.
**
** As with sqlite3rbu_open(), Zipvfs users should rever to the comment
-** describing the sqlite3rbu_create_vfs() API function below for
-** a description of the complications associated with using RBU with
+** describing the sqlite3rbu_create_vfs() API function below for
+** a description of the complications associated with using RBU with
** zipvfs databases.
*/
SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
- const char *zTarget,
+ const char *zTarget,
const char *zState
);
@@ -186626,7 +216044,7 @@ SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
** is removed entirely. If the second parameter is negative, the limit is
** not modified (this is useful for querying the current limit).
**
-** In all cases the returned value is the current limit in bytes (zero
+** In all cases the returned value is the current limit in bytes (zero
** indicates unlimited).
**
** If the temp space limit is exceeded during operation, an SQLITE_FULL
@@ -186635,13 +216053,13 @@ SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
SQLITE_API sqlite3_int64 sqlite3rbu_temp_size_limit(sqlite3rbu*, sqlite3_int64);
/*
-** Return the current amount of temp file space, in bytes, currently used by
+** Return the current amount of temp file space, in bytes, currently used by
** the RBU handle passed as the only argument.
*/
SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu*);
/*
-** Internally, each RBU connection uses a separate SQLite database
+** Internally, each RBU connection uses a separate SQLite database
** connection to access the target and rbu update databases. This
** API allows the application direct access to these database handles.
**
@@ -186652,10 +216070,10 @@ SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu*);
** following scenarios:
**
** * If any target tables are virtual tables, it may be necessary to
-** call sqlite3_create_module() on the target database handle to
+** call sqlite3_create_module() on the target database handle to
** register the required virtual table implementations.
**
-** * If the data_xxx tables in the RBU source database are virtual
+** * If the data_xxx tables in the RBU source database are virtual
** tables, the application may need to call sqlite3_create_module() on
** the rbu update db handle to any required virtual table
** implementations.
@@ -186674,12 +216092,12 @@ SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu*);
SQLITE_API sqlite3 *sqlite3rbu_db(sqlite3rbu*, int bRbu);
/*
-** Do some work towards applying the RBU update to the target db.
+** Do some work towards applying the RBU update to the target db.
**
-** Return SQLITE_DONE if the update has been completely applied, or
+** Return SQLITE_DONE if the update has been completely applied, or
** SQLITE_OK if no error occurs but there remains work to do to apply
-** the RBU update. If an error does occur, some other error code is
-** returned.
+** the RBU update. If an error does occur, some other error code is
+** returned.
**
** Once a call to sqlite3rbu_step() has returned a value other than
** SQLITE_OK, all subsequent calls on the same RBU handle are no-ops
@@ -186692,7 +216110,7 @@ SQLITE_API int sqlite3rbu_step(sqlite3rbu *pRbu);
**
** If a power failure or application crash occurs during an update, following
** system recovery RBU may resume the update from the point at which the state
-** was last saved. In other words, from the most recent successful call to
+** was last saved. In other words, from the most recent successful call to
** sqlite3rbu_close() or this function.
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
@@ -186700,7 +216118,7 @@ SQLITE_API int sqlite3rbu_step(sqlite3rbu *pRbu);
SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu);
/*
-** Close an RBU handle.
+** Close an RBU handle.
**
** If the RBU update has been completely applied, mark the RBU database
** as fully applied. Otherwise, assuming no error has occurred, save the
@@ -186714,20 +216132,20 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu);
** eventually free any such buffer using sqlite3_free().
**
** Otherwise, if no error occurs, this function returns SQLITE_OK if the
-** update has been partially applied, or SQLITE_DONE if it has been
+** update has been partially applied, or SQLITE_DONE if it has been
** completely applied.
*/
SQLITE_API int sqlite3rbu_close(sqlite3rbu *pRbu, char **pzErrmsg);
/*
-** Return the total number of key-value operations (inserts, deletes or
+** Return the total number of key-value operations (inserts, deletes or
** updates) that have been performed on the target database since the
** current RBU update was started.
*/
SQLITE_API sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu);
/*
-** Obtain permyriadage (permyriadage is to 10000 as percentage is to 100)
+** Obtain permyriadage (permyriadage is to 10000 as percentage is to 100)
** progress indications for the two stages of an RBU update. This API may
** be useful for driving GUI progress indicators and similar.
**
@@ -186740,16 +216158,16 @@ SQLITE_API sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu);
** The update is visible to non-RBU clients during stage 2. During stage 1
** non-RBU reader clients may see the original database.
**
-** If this API is called during stage 2 of the update, output variable
+** If this API is called during stage 2 of the update, output variable
** (*pnOne) is set to 10000 to indicate that stage 1 has finished and (*pnTwo)
** to a value between 0 and 10000 to indicate the permyriadage progress of
-** stage 2. A value of 5000 indicates that stage 2 is half finished,
+** stage 2. A value of 5000 indicates that stage 2 is half finished,
** 9000 indicates that it is 90% finished, and so on.
**
-** If this API is called during stage 1 of the update, output variable
+** If this API is called during stage 1 of the update, output variable
** (*pnTwo) is set to 0 to indicate that stage 2 has not yet started. The
-** value to which (*pnOne) is set depends on whether or not the RBU
-** database contains an "rbu_count" table. The rbu_count table, if it
+** value to which (*pnOne) is set depends on whether or not the RBU
+** database contains an "rbu_count" table. The rbu_count table, if it
** exists, must contain the same columns as the following:
**
** CREATE TABLE rbu_count(tbl TEXT PRIMARY KEY, cnt INTEGER) WITHOUT ROWID;
@@ -186807,21 +216225,49 @@ SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int*pnTwo);
SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu);
/*
+** As part of applying an RBU update or performing an RBU vacuum operation,
+** the system must at one point move the *-oal file to the equivalent *-wal
+** path. Normally, it does this by invoking POSIX function rename(2) directly.
+** Except on WINCE platforms, where it uses win32 API MoveFileW(). This
+** function may be used to register a callback that the RBU module will invoke
+** instead of one of these APIs.
+**
+** If a callback is registered with an RBU handle, it invokes it instead
+** of rename(2) when it needs to move a file within the file-system. The
+** first argument passed to the xRename() callback is a copy of the second
+** argument (pArg) passed to this function. The second is the full path
+** to the file to move and the third the full path to which it should be
+** moved. The callback function should return SQLITE_OK to indicate
+** success. If an error occurs, it should return an SQLite error code.
+** In this case the RBU operation will be abandoned and the error returned
+** to the RBU user.
+**
+** Passing a NULL pointer in place of the xRename argument to this function
+** restores the default behaviour.
+*/
+SQLITE_API void sqlite3rbu_rename_handler(
+ sqlite3rbu *pRbu,
+ void *pArg,
+ int (*xRename)(void *pArg, const char *zOld, const char *zNew)
+);
+
+
+/*
** Create an RBU VFS named zName that accesses the underlying file-system
-** via existing VFS zParent. Or, if the zParent parameter is passed NULL,
+** via existing VFS zParent. Or, if the zParent parameter is passed NULL,
** then the new RBU VFS uses the default system VFS to access the file-system.
-** The new object is registered as a non-default VFS with SQLite before
+** The new object is registered as a non-default VFS with SQLite before
** returning.
**
** Part of the RBU implementation uses a custom VFS object. Usually, this
-** object is created and deleted automatically by RBU.
+** object is created and deleted automatically by RBU.
**
** The exception is for applications that also use zipvfs. In this case,
** the custom VFS must be explicitly created by the user before the RBU
** handle is opened. The RBU VFS should be installed so that the zipvfs
-** VFS uses the RBU VFS, which in turn uses any other VFS layers in use
+** VFS uses the RBU VFS, which in turn uses any other VFS layers in use
** (for example multiplexor) to access the file-system. For example,
-** to assemble an RBU enabled VFS stack that uses both zipvfs and
+** to assemble an RBU enabled VFS stack that uses both zipvfs and
** multiplexor (error checking omitted):
**
** // Create a VFS named "multiplex" (not the default).
@@ -186843,9 +216289,9 @@ SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu);
** may be used by RBU clients. Attempting to use RBU with a zipvfs VFS stack
** that does not include the RBU layer results in an error.
**
-** The overhead of adding the "rbu" VFS to the system is negligible for
-** non-RBU users. There is no harm in an application accessing the
-** file-system via "rbu" all the time, even if it only uses RBU functionality
+** The overhead of adding the "rbu" VFS to the system is negligible for
+** non-RBU users. There is no harm in an application accessing the
+** file-system via "rbu" all the time, even if it only uses RBU functionality
** occasionally.
*/
SQLITE_API int sqlite3rbu_create_vfs(const char *zName, const char *zParent);
@@ -186891,6 +216337,13 @@ SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName);
#endif
/*
+** Name of the URI option that causes RBU to take an exclusive lock as
+** part of the incremental checkpoint operation.
+*/
+#define RBU_EXCLUSIVE_CHECKPOINT "rbu_exclusive_checkpoint"
+
+
+/*
** The rbu_state table is used to save the state of a partially applied
** update so that it can be resumed later. The table consists of integer
** keys mapped to values as follows:
@@ -186898,17 +216351,17 @@ SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName);
** RBU_STATE_STAGE:
** May be set to integer values 1, 2, 4 or 5. As follows:
** 1: the *-rbu file is currently under construction.
-** 2: the *-rbu file has been constructed, but not yet moved
+** 2: the *-rbu file has been constructed, but not yet moved
** to the *-wal path.
** 4: the checkpoint is underway.
** 5: the rbu update has been checkpointed.
**
** RBU_STATE_TBL:
-** Only valid if STAGE==1. The target database name of the table
+** Only valid if STAGE==1. The target database name of the table
** currently being written.
**
** RBU_STATE_IDX:
-** Only valid if STAGE==1. The target database name of the index
+** Only valid if STAGE==1. The target database name of the index
** currently being written, or NULL if the main table is currently being
** updated.
**
@@ -186928,14 +216381,14 @@ SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName);
** be continued if this happens).
**
** RBU_STATE_COOKIE:
-** Valid if STAGE==1. The current change-counter cookie value in the
+** Valid if STAGE==1. The current change-counter cookie value in the
** target db file.
**
** RBU_STATE_OALSZ:
** Valid if STAGE==1. The size in bytes of the *-oal file.
**
** RBU_STATE_DATATBL:
-** Only valid if STAGE==1. The RBU database name of the table
+** Only valid if STAGE==1. The RBU database name of the table
** currently being read.
*/
#define RBU_STATE_STAGE 1
@@ -186962,6 +216415,7 @@ SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName);
typedef struct RbuFrame RbuFrame;
typedef struct RbuObjIter RbuObjIter;
typedef struct RbuState RbuState;
+typedef struct RbuSpan RbuSpan;
typedef struct rbu_vfs rbu_vfs;
typedef struct rbu_file rbu_file;
typedef struct RbuUpdateStmt RbuUpdateStmt;
@@ -187006,12 +216460,17 @@ struct RbuUpdateStmt {
RbuUpdateStmt *pNext;
};
+struct RbuSpan {
+ const char *zSpan;
+ int nSpan;
+};
+
/*
** An iterator of this type is used to iterate through all objects in
** the target database that require updating. For each such table, the
** iterator visits, in order:
**
-** * the table itself,
+** * the table itself,
** * each index of the table (zero or more points to visit), and
** * a special "cleanup table" state.
**
@@ -187020,7 +216479,12 @@ struct RbuUpdateStmt {
** it points to an array of flags nTblCol elements in size. The flag is
** set for each column that is either a part of the PK or a part of an
** index. Or clear otherwise.
-**
+**
+** If there are one or more partial indexes on the table, all fields of
+** this array set set to 1. This is because in that case, the module has
+** no way to tell which fields will be required to add and remove entries
+** from the partial indexes.
+**
*/
struct RbuObjIter {
sqlite3_stmt *pTblIter; /* Iterate through tables */
@@ -187050,6 +216514,9 @@ struct RbuObjIter {
sqlite3_stmt *pInsert; /* Statement for INSERT operations */
sqlite3_stmt *pDelete; /* Statement for DELETE ops */
sqlite3_stmt *pTmpInsert; /* Insert into rbu_tmp_$zDataTbl */
+ int nIdxCol;
+ RbuSpan *aIdxCol;
+ char *zIdxSql;
/* Last UPDATE used (for PK b-tree updates only), or NULL. */
RbuUpdateStmt *pRbuUpdate;
@@ -187099,7 +216566,7 @@ struct RbuFrame {
**
** nPhaseOneStep:
** If the RBU database contains an rbu_count table, this value is set to
-** a running estimate of the number of b-tree operations required to
+** a running estimate of the number of b-tree operations required to
** finish populating the *-oal file. This allows the sqlite3_bp_progress()
** API to calculate the permyriadage progress of populating the *-oal file
** using the formula:
@@ -187119,7 +216586,7 @@ struct RbuFrame {
**
** * the RBU update contains any UPDATE operations. If the PK specified
** for an UPDATE operation does not exist in the target table, then
-** no b-tree operations are required on index b-trees. Or if the
+** no b-tree operations are required on index b-trees. Or if the
** specified PK does exist, then (nIndex*2) such operations are
** required (one delete and one insert on each index b-tree).
**
@@ -187152,6 +216619,8 @@ struct sqlite3rbu {
int nPagePerSector; /* Pages per sector for pTargetFd */
i64 iOalSz;
i64 nPhaseOneStep;
+ void *pRenameArg;
+ int (*xRename)(void*, const char*, const char*);
/* The following state variables are used as part of the incremental
** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding
@@ -187464,6 +216933,7 @@ static void rbuFossilDeltaFunc(
}else{
nOut2 = rbuDeltaApply(aOrig, nOrig, aDelta, nDelta, aOut);
if( nOut2!=nOut ){
+ sqlite3_free(aOut);
sqlite3_result_error(context, "corrupt fossil delta", -1);
}else{
sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
@@ -187475,7 +216945,7 @@ static void rbuFossilDeltaFunc(
/*
** Prepare the SQL statement in buffer zSql against database handle db.
** If successful, set *ppStmt to point to the new statement and return
-** SQLITE_OK.
+** SQLITE_OK.
**
** Otherwise, if an error does occur, set *ppStmt to NULL and return
** an SQLite error code. Additionally, set output variable *pzErrmsg to
@@ -187483,7 +216953,7 @@ static void rbuFossilDeltaFunc(
** of the caller to (eventually) free this buffer using sqlite3_free().
*/
static int prepareAndCollectError(
- sqlite3 *db,
+ sqlite3 *db,
sqlite3_stmt **ppStmt,
char **pzErrmsg,
const char *zSql
@@ -187515,9 +216985,9 @@ static int resetAndCollectError(sqlite3_stmt *pStmt, char **pzErrmsg){
/*
** Unless it is NULL, argument zSql points to a buffer allocated using
** sqlite3_malloc containing an SQL statement. This function prepares the SQL
-** statement against database db and frees the buffer. If statement
-** compilation is successful, *ppStmt is set to point to the new statement
-** handle and SQLITE_OK is returned.
+** statement against database db and frees the buffer. If statement
+** compilation is successful, *ppStmt is set to point to the new statement
+** handle and SQLITE_OK is returned.
**
** Otherwise, if an error occurs, *ppStmt is set to NULL and an error code
** returned. In this case, *pzErrmsg may also be set to point to an error
@@ -187528,7 +216998,7 @@ static int resetAndCollectError(sqlite3_stmt *pStmt, char **pzErrmsg){
** In this case SQLITE_NOMEM is returned and *ppStmt set to NULL.
*/
static int prepareFreeAndCollectError(
- sqlite3 *db,
+ sqlite3 *db,
sqlite3_stmt **ppStmt,
char **pzErrmsg,
char *zSql
@@ -187583,13 +217053,18 @@ static void rbuObjIterClearStatements(RbuObjIter *pIter){
sqlite3_free(pUp);
pUp = pTmp;
}
-
+ sqlite3_free(pIter->aIdxCol);
+ sqlite3_free(pIter->zIdxSql);
+
pIter->pSelect = 0;
pIter->pInsert = 0;
pIter->pDelete = 0;
pIter->pRbuUpdate = 0;
pIter->pTmpInsert = 0;
pIter->nCol = 0;
+ pIter->nIdxCol = 0;
+ pIter->aIdxCol = 0;
+ pIter->zIdxSql = 0;
}
/*
@@ -187607,16 +217082,16 @@ static void rbuObjIterFinalize(RbuObjIter *pIter){
/*
** Advance the iterator to the next position.
**
-** If no error occurs, SQLITE_OK is returned and the iterator is left
-** pointing to the next entry. Otherwise, an error code and message is
-** left in the RBU handle passed as the first argument. A copy of the
+** If no error occurs, SQLITE_OK is returned and the iterator is left
+** pointing to the next entry. Otherwise, an error code and message is
+** left in the RBU handle passed as the first argument. A copy of the
** error code is returned.
*/
static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){
int rc = p->rc;
if( rc==SQLITE_OK ){
- /* Free any SQLite statements used while processing the previous object */
+ /* Free any SQLite statements used while processing the previous object */
rbuObjIterClearStatements(pIter);
if( pIter->zIdx==0 ){
rc = sqlite3_exec(p->dbMain,
@@ -187675,7 +217150,7 @@ static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){
** The implementation of the rbu_target_name() SQL function. This function
** accepts one or two arguments. The first argument is the name of a table -
** the name of a table in the RBU database. The second, if it is present, is 1
-** for a view or 0 for a table.
+** for a view or 0 for a table.
**
** For a non-vacuum RBU handle, if the table name matches the pattern:
**
@@ -187704,6 +217179,7 @@ static void rbuTargetNameFunc(
zIn = (const char*)sqlite3_value_text(argv[0]);
if( zIn ){
if( rbuIsVacuum(p) ){
+ assert( argc==2 || argc==1 );
if( argc==1 || 0==sqlite3_value_int(argv[1]) ){
sqlite3_result_text(pCtx, zIn, -1, SQLITE_STATIC);
}
@@ -187722,19 +217198,19 @@ static void rbuTargetNameFunc(
/*
** Initialize the iterator structure passed as the second argument.
**
-** If no error occurs, SQLITE_OK is returned and the iterator is left
-** pointing to the first entry. Otherwise, an error code and message is
-** left in the RBU handle passed as the first argument. A copy of the
+** If no error occurs, SQLITE_OK is returned and the iterator is left
+** pointing to the first entry. Otherwise, an error code and message is
+** left in the RBU handle passed as the first argument. A copy of the
** error code is returned.
*/
static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){
int rc;
memset(pIter, 0, sizeof(RbuObjIter));
- rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg,
+ rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg,
sqlite3_mprintf(
"SELECT rbu_target_name(name, type='view') AS target, name "
- "FROM sqlite_master "
+ "FROM sqlite_schema "
"WHERE type IN ('table', 'view') AND target IS NOT NULL "
" %s "
"ORDER BY name"
@@ -187743,7 +217219,7 @@ static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){
if( rc==SQLITE_OK ){
rc = prepareAndCollectError(p->dbMain, &pIter->pIdxIter, &p->zErrmsg,
"SELECT name, rootpage, sql IS NULL OR substr(8, 6)=='UNIQUE' "
- " FROM main.sqlite_master "
+ " FROM main.sqlite_schema "
" WHERE type='index' AND tbl_name = ?"
);
}
@@ -187759,7 +217235,7 @@ static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){
**
** If an error has already occurred (p->rc is already set to something other
** than SQLITE_OK), then this function returns NULL without modifying the
-** stored error code. In this case it still calls sqlite3_free() on any
+** stored error code. In this case it still calls sqlite3_free() on any
** printf() parameters associated with %z conversions.
*/
static char *rbuMPrintf(sqlite3rbu *p, const char *zFmt, ...){
@@ -187805,16 +217281,16 @@ static int rbuMPrintfExec(sqlite3rbu *p, sqlite3 *db, const char *zFmt, ...){
}
/*
-** Attempt to allocate and return a pointer to a zeroed block of nByte
-** bytes.
+** Attempt to allocate and return a pointer to a zeroed block of nByte
+** bytes.
**
-** If an error (i.e. an OOM condition) occurs, return NULL and leave an
-** error code in the rbu handle passed as the first argument. Or, if an
-** error has already occurred when this function is called, return NULL
+** If an error (i.e. an OOM condition) occurs, return NULL and leave an
+** error code in the rbu handle passed as the first argument. Or, if an
+** error has already occurred when this function is called, return NULL
** immediately without attempting the allocation or modifying the stored
** error code.
*/
-static void *rbuMalloc(sqlite3rbu *p, int nByte){
+static void *rbuMalloc(sqlite3rbu *p, sqlite3_int64 nByte){
void *pRet = 0;
if( p->rc==SQLITE_OK ){
assert( nByte>0 );
@@ -187835,7 +217311,7 @@ static void *rbuMalloc(sqlite3rbu *p, int nByte){
** error code in the RBU handle passed as the first argument.
*/
static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){
- int nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol;
+ sqlite3_int64 nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol;
char **azNew;
azNew = (char**)rbuMalloc(p, nByte);
@@ -187862,14 +217338,15 @@ static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){
static char *rbuStrndup(const char *zStr, int *pRc){
char *zRet = 0;
- assert( *pRc==SQLITE_OK );
- if( zStr ){
- size_t nCopy = strlen(zStr) + 1;
- zRet = (char*)sqlite3_malloc64(nCopy);
- if( zRet ){
- memcpy(zRet, zStr, nCopy);
- }else{
- *pRc = SQLITE_NOMEM;
+ if( *pRc==SQLITE_OK ){
+ if( zStr ){
+ size_t nCopy = strlen(zStr) + 1;
+ zRet = (char*)sqlite3_malloc64(nCopy);
+ if( zRet ){
+ memcpy(zRet, zStr, nCopy);
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
}
}
@@ -187906,7 +217383,7 @@ static void rbuFinalize(sqlite3rbu *p, sqlite3_stmt *pStmt){
** RBU_PK_VTAB: Table is a virtual table.
**
** Argument *piPk is also of type (int*), and also points to an output
-** parameter. Unless the table has an external primary key index
+** parameter. Unless the table has an external primary key index
** (i.e. unless *peType is set to 3), then *piPk is set to zero. Or,
** if the table does have an external primary key index, then *piPk
** is set to the root page number of the primary key index before
@@ -187914,12 +217391,12 @@ static void rbuFinalize(sqlite3rbu *p, sqlite3_stmt *pStmt){
**
** ALGORITHM:
**
-** if( no entry exists in sqlite_master ){
+** if( no entry exists in sqlite_schema ){
** return RBU_PK_NOTABLE
** }else if( sql for the entry starts with "CREATE VIRTUAL" ){
** return RBU_PK_VTAB
** }else if( "PRAGMA index_list()" for the table contains a "pk" index ){
-** if( the index that is the pk exists in sqlite_master ){
+** if( the index that is the pk exists in sqlite_schema ){
** *piPK = rootpage of that index.
** return RBU_PK_EXTERNAL
** }else{
@@ -187939,9 +217416,9 @@ static void rbuTableType(
int *piPk
){
/*
- ** 0) SELECT count(*) FROM sqlite_master where name=%Q AND IsVirtual(%Q)
+ ** 0) SELECT count(*) FROM sqlite_schema where name=%Q AND IsVirtual(%Q)
** 1) PRAGMA index_list = ?
- ** 2) SELECT count(*) FROM sqlite_master where name=%Q
+ ** 2) SELECT count(*) FROM sqlite_schema where name=%Q
** 3) PRAGMA table_info = ?
*/
sqlite3_stmt *aStmt[4] = {0, 0, 0, 0};
@@ -187950,10 +217427,12 @@ static void rbuTableType(
*piPk = 0;
assert( p->rc==SQLITE_OK );
- p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[0], &p->zErrmsg,
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[0], &p->zErrmsg,
sqlite3_mprintf(
- "SELECT (sql LIKE 'create virtual%%'), rootpage"
- " FROM sqlite_master"
+ "SELECT "
+ " (sql COLLATE nocase BETWEEN 'CREATE VIRTUAL' AND 'CREATE VIRTUAM'),"
+ " rootpage"
+ " FROM sqlite_schema"
" WHERE name=%Q", zTab
));
if( p->rc!=SQLITE_OK || sqlite3_step(aStmt[0])!=SQLITE_ROW ){
@@ -187966,7 +217445,7 @@ static void rbuTableType(
}
*piTnum = sqlite3_column_int(aStmt[0], 1);
- p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[1], &p->zErrmsg,
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[1], &p->zErrmsg,
sqlite3_mprintf("PRAGMA index_list=%Q",zTab)
);
if( p->rc ) goto rbuTableType_end;
@@ -187974,9 +217453,9 @@ static void rbuTableType(
const u8 *zOrig = sqlite3_column_text(aStmt[1], 3);
const u8 *zIdx = sqlite3_column_text(aStmt[1], 1);
if( zOrig && zIdx && zOrig[0]=='p' ){
- p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[2], &p->zErrmsg,
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[2], &p->zErrmsg,
sqlite3_mprintf(
- "SELECT rootpage FROM sqlite_master WHERE name = %Q", zIdx
+ "SELECT rootpage FROM sqlite_schema WHERE name = %Q", zIdx
));
if( p->rc==SQLITE_OK ){
if( sqlite3_step(aStmt[2])==SQLITE_ROW ){
@@ -187990,7 +217469,7 @@ static void rbuTableType(
}
}
- p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[3], &p->zErrmsg,
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[3], &p->zErrmsg,
sqlite3_mprintf("PRAGMA table_info=%Q",zTab)
);
if( p->rc==SQLITE_OK ){
@@ -188029,14 +217508,21 @@ static void rbuObjIterCacheIndexedCols(sqlite3rbu *p, RbuObjIter *pIter){
pIter->nIndex = 0;
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pList) ){
const char *zIdx = (const char*)sqlite3_column_text(pList, 1);
+ int bPartial = sqlite3_column_int(pList, 4);
sqlite3_stmt *pXInfo = 0;
if( zIdx==0 ) break;
+ if( bPartial ){
+ memset(pIter->abIndexed, 0x01, sizeof(u8)*pIter->nTblCol);
+ }
p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx)
);
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
int iCid = sqlite3_column_int(pXInfo, 1);
if( iCid>=0 ) pIter->abIndexed[iCid] = 1;
+ if( iCid==-2 ){
+ memset(pIter->abIndexed, 0x01, sizeof(u8)*pIter->nTblCol);
+ }
}
rbuFinalize(p, pXInfo);
bIndex = 1;
@@ -188059,7 +217545,7 @@ static void rbuObjIterCacheIndexedCols(sqlite3rbu *p, RbuObjIter *pIter){
** the table (not index) that the iterator currently points to.
**
** Return SQLITE_OK if successful, or an SQLite error code otherwise. If
-** an error does occur, an error code and error message are also left in
+** an error does occur, an error code and error message are also left in
** the RBU handle.
*/
static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
@@ -188081,7 +217567,7 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
if( p->rc ) return p->rc;
if( pIter->zIdx==0 ) pIter->iTnum = iTnum;
- assert( pIter->eType==RBU_PK_NONE || pIter->eType==RBU_PK_IPK
+ assert( pIter->eType==RBU_PK_NONE || pIter->eType==RBU_PK_IPK
|| pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_WITHOUT_ROWID
|| pIter->eType==RBU_PK_VTAB
);
@@ -188089,7 +217575,7 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
/* Populate the azTblCol[] and nTblCol variables based on the columns
** of the input table. Ignore any input table columns that begin with
** "rbu_". */
- p->rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
sqlite3_mprintf("SELECT * FROM '%q'", pIter->zDataTbl)
);
if( p->rc==SQLITE_OK ){
@@ -188125,7 +217611,7 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
** present in the input table. Populate the abTblPk[], azTblType[] and
** aiTblOrder[] arrays at the same time. */
if( p->rc==SQLITE_OK ){
- p->rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
sqlite3_mprintf("PRAGMA table_info(%Q)", pIter->zTbl)
);
}
@@ -188151,7 +217637,8 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
}
pIter->azTblType[iOrder] = rbuStrndup(zType, &p->rc);
- pIter->abTblPk[iOrder] = (iPk!=0);
+ assert( iPk>=0 );
+ pIter->abTblPk[iOrder] = (u8)iPk;
pIter->abNotNull[iOrder] = (u8)bNotNull || (iPk!=0);
iOrder++;
}
@@ -188167,8 +217654,8 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
}
/*
-** This function constructs and returns a pointer to a nul-terminated
-** string containing some SQL clause or list based on one or more of the
+** This function constructs and returns a pointer to a nul-terminated
+** string containing some SQL clause or list based on one or more of the
** column names currently stored in the pIter->azTblCol[] array.
*/
static char *rbuObjIterGetCollist(
@@ -188187,23 +217674,232 @@ static char *rbuObjIterGetCollist(
}
/*
-** This function is used to create a SELECT list (the list of SQL
-** expressions that follows a SELECT keyword) for a SELECT statement
-** used to read from an data_xxx or rbu_tmp_xxx table while updating the
-** index object currently indicated by the iterator object passed as the
-** second argument. A "PRAGMA index_xinfo = <idxname>" statement is used
+** Return a comma separated list of the quoted PRIMARY KEY column names,
+** in order, for the current table. Before each column name, add the text
+** zPre. After each column name, add the zPost text. Use zSeparator as
+** the separator text (usually ", ").
+*/
+static char *rbuObjIterGetPkList(
+ sqlite3rbu *p, /* RBU object */
+ RbuObjIter *pIter, /* Object iterator for column names */
+ const char *zPre, /* Before each quoted column name */
+ const char *zSeparator, /* Separator to use between columns */
+ const char *zPost /* After each quoted column name */
+){
+ int iPk = 1;
+ char *zRet = 0;
+ const char *zSep = "";
+ while( 1 ){
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ if( (int)pIter->abTblPk[i]==iPk ){
+ const char *zCol = pIter->azTblCol[i];
+ zRet = rbuMPrintf(p, "%z%s%s\"%w\"%s", zRet, zSep, zPre, zCol, zPost);
+ zSep = zSeparator;
+ break;
+ }
+ }
+ if( i==pIter->nTblCol ) break;
+ iPk++;
+ }
+ return zRet;
+}
+
+/*
+** This function is called as part of restarting an RBU vacuum within
+** stage 1 of the process (while the *-oal file is being built) while
+** updating a table (not an index). The table may be a rowid table or
+** a WITHOUT ROWID table. It queries the target database to find the
+** largest key that has already been written to the target table and
+** constructs a WHERE clause that can be used to extract the remaining
+** rows from the source table. For a rowid table, the WHERE clause
+** is of the form:
+**
+** "WHERE _rowid_ > ?"
+**
+** and for WITHOUT ROWID tables:
+**
+** "WHERE (key1, key2) > (?, ?)"
+**
+** Instead of "?" placeholders, the actual WHERE clauses created by
+** this function contain literal SQL values.
+*/
+static char *rbuVacuumTableStart(
+ sqlite3rbu *p, /* RBU handle */
+ RbuObjIter *pIter, /* RBU iterator object */
+ int bRowid, /* True for a rowid table */
+ const char *zWrite /* Target table name prefix */
+){
+ sqlite3_stmt *pMax = 0;
+ char *zRet = 0;
+ if( bRowid ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pMax, &p->zErrmsg,
+ sqlite3_mprintf(
+ "SELECT max(_rowid_) FROM \"%s%w\"", zWrite, pIter->zTbl
+ )
+ );
+ if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pMax) ){
+ sqlite3_int64 iMax = sqlite3_column_int64(pMax, 0);
+ zRet = rbuMPrintf(p, " WHERE _rowid_ > %lld ", iMax);
+ }
+ rbuFinalize(p, pMax);
+ }else{
+ char *zOrder = rbuObjIterGetPkList(p, pIter, "", ", ", " DESC");
+ char *zSelect = rbuObjIterGetPkList(p, pIter, "quote(", "||','||", ")");
+ char *zList = rbuObjIterGetPkList(p, pIter, "", ", ", "");
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pMax, &p->zErrmsg,
+ sqlite3_mprintf(
+ "SELECT %s FROM \"%s%w\" ORDER BY %s LIMIT 1",
+ zSelect, zWrite, pIter->zTbl, zOrder
+ )
+ );
+ if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pMax) ){
+ const char *zVal = (const char*)sqlite3_column_text(pMax, 0);
+ zRet = rbuMPrintf(p, " WHERE (%s) > (%s) ", zList, zVal);
+ }
+ rbuFinalize(p, pMax);
+ }
+
+ sqlite3_free(zOrder);
+ sqlite3_free(zSelect);
+ sqlite3_free(zList);
+ }
+ return zRet;
+}
+
+/*
+** This function is called as part of restating an RBU vacuum when the
+** current operation is writing content to an index. If possible, it
+** queries the target index b-tree for the largest key already written to
+** it, then composes and returns an expression that can be used in a WHERE
+** clause to select the remaining required rows from the source table.
+** It is only possible to return such an expression if:
+**
+** * The index contains no DESC columns, and
+** * The last key written to the index before the operation was
+** suspended does not contain any NULL values.
+**
+** The expression is of the form:
+**
+** (index-field1, index-field2, ...) > (?, ?, ...)
+**
+** except that the "?" placeholders are replaced with literal values.
+**
+** If the expression cannot be created, NULL is returned. In this case,
+** the caller has to use an OFFSET clause to extract only the required
+** rows from the sourct table, just as it does for an RBU update operation.
+*/
+static char *rbuVacuumIndexStart(
+ sqlite3rbu *p, /* RBU handle */
+ RbuObjIter *pIter /* RBU iterator object */
+){
+ char *zOrder = 0;
+ char *zLhs = 0;
+ char *zSelect = 0;
+ char *zVector = 0;
+ char *zRet = 0;
+ int bFailed = 0;
+ const char *zSep = "";
+ int iCol = 0;
+ sqlite3_stmt *pXInfo = 0;
+
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", pIter->zIdx)
+ );
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
+ int iCid = sqlite3_column_int(pXInfo, 1);
+ const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
+ const char *zCol;
+ if( sqlite3_column_int(pXInfo, 3) ){
+ bFailed = 1;
+ break;
+ }
+
+ if( iCid<0 ){
+ if( pIter->eType==RBU_PK_IPK ){
+ int i;
+ for(i=0; pIter->abTblPk[i]==0; i++);
+ assert( i<pIter->nTblCol );
+ zCol = pIter->azTblCol[i];
+ }else{
+ zCol = "_rowid_";
+ }
+ }else{
+ zCol = pIter->azTblCol[iCid];
+ }
+
+ zLhs = rbuMPrintf(p, "%z%s \"%w\" COLLATE %Q",
+ zLhs, zSep, zCol, zCollate
+ );
+ zOrder = rbuMPrintf(p, "%z%s \"rbu_imp_%d%w\" COLLATE %Q DESC",
+ zOrder, zSep, iCol, zCol, zCollate
+ );
+ zSelect = rbuMPrintf(p, "%z%s quote(\"rbu_imp_%d%w\")",
+ zSelect, zSep, iCol, zCol
+ );
+ zSep = ", ";
+ iCol++;
+ }
+ rbuFinalize(p, pXInfo);
+ if( bFailed ) goto index_start_out;
+
+ if( p->rc==SQLITE_OK ){
+ sqlite3_stmt *pSel = 0;
+
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pSel, &p->zErrmsg,
+ sqlite3_mprintf("SELECT %s FROM \"rbu_imp_%w\" ORDER BY %s LIMIT 1",
+ zSelect, pIter->zTbl, zOrder
+ )
+ );
+ if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSel) ){
+ zSep = "";
+ for(iCol=0; iCol<pIter->nCol; iCol++){
+ const char *zQuoted = (const char*)sqlite3_column_text(pSel, iCol);
+ if( zQuoted==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else if( zQuoted[0]=='N' ){
+ bFailed = 1;
+ break;
+ }
+ zVector = rbuMPrintf(p, "%z%s%s", zVector, zSep, zQuoted);
+ zSep = ", ";
+ }
+
+ if( !bFailed ){
+ zRet = rbuMPrintf(p, "(%s) > (%s)", zLhs, zVector);
+ }
+ }
+ rbuFinalize(p, pSel);
+ }
+
+ index_start_out:
+ sqlite3_free(zOrder);
+ sqlite3_free(zSelect);
+ sqlite3_free(zVector);
+ sqlite3_free(zLhs);
+ return zRet;
+}
+
+/*
+** This function is used to create a SELECT list (the list of SQL
+** expressions that follows a SELECT keyword) for a SELECT statement
+** used to read from an data_xxx or rbu_tmp_xxx table while updating the
+** index object currently indicated by the iterator object passed as the
+** second argument. A "PRAGMA index_xinfo = <idxname>" statement is used
** to obtain the required information.
**
** If the index is of the following form:
**
** CREATE INDEX i1 ON t1(c, b COLLATE nocase);
**
-** and "t1" is a table with an explicit INTEGER PRIMARY KEY column
+** and "t1" is a table with an explicit INTEGER PRIMARY KEY column
** "ipk", the returned string is:
**
** "`c` COLLATE 'BINARY', `b` COLLATE 'NOCASE', `ipk` COLLATE 'BINARY'"
**
-** As well as the returned string, three other malloc'd strings are
+** As well as the returned string, three other malloc'd strings are
** returned via output parameters. As follows:
**
** pzImposterCols: ...
@@ -188240,36 +217936,44 @@ static char *rbuObjIterGetIndexCols(
int iCid = sqlite3_column_int(pXInfo, 1);
int bDesc = sqlite3_column_int(pXInfo, 3);
const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
- const char *zCol;
+ const char *zCol = 0;
const char *zType;
- if( iCid<0 ){
- /* An integer primary key. If the table has an explicit IPK, use
- ** its name. Otherwise, use "rbu_rowid". */
- if( pIter->eType==RBU_PK_IPK ){
- int i;
- for(i=0; pIter->abTblPk[i]==0; i++);
- assert( i<pIter->nTblCol );
- zCol = pIter->azTblCol[i];
- }else if( rbuIsVacuum(p) ){
- zCol = "_rowid_";
+ if( iCid==-2 ){
+ int iSeq = sqlite3_column_int(pXInfo, 0);
+ zRet = sqlite3_mprintf("%z%s(%.*s) COLLATE %Q", zRet, zCom,
+ pIter->aIdxCol[iSeq].nSpan, pIter->aIdxCol[iSeq].zSpan, zCollate
+ );
+ zType = "";
+ }else {
+ if( iCid<0 ){
+ /* An integer primary key. If the table has an explicit IPK, use
+ ** its name. Otherwise, use "rbu_rowid". */
+ if( pIter->eType==RBU_PK_IPK ){
+ int i;
+ for(i=0; pIter->abTblPk[i]==0; i++);
+ assert( i<pIter->nTblCol );
+ zCol = pIter->azTblCol[i];
+ }else if( rbuIsVacuum(p) ){
+ zCol = "_rowid_";
+ }else{
+ zCol = "rbu_rowid";
+ }
+ zType = "INTEGER";
}else{
- zCol = "rbu_rowid";
+ zCol = pIter->azTblCol[iCid];
+ zType = pIter->azTblType[iCid];
}
- zType = "INTEGER";
- }else{
- zCol = pIter->azTblCol[iCid];
- zType = pIter->azTblType[iCid];
+ zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom,zCol,zCollate);
}
- zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom, zCol, zCollate);
if( pIter->bUnique==0 || sqlite3_column_int(pXInfo, 5) ){
const char *zOrder = (bDesc ? " DESC" : "");
- zImpPK = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\"%s",
+ zImpPK = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\"%s",
zImpPK, zCom, nBind, zCol, zOrder
);
}
- zImpCols = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\" %s COLLATE %Q",
+ zImpCols = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\" %s COLLATE %Q",
zImpCols, zCom, nBind, zCol, zType, zCollate
);
zWhere = sqlite3_mprintf(
@@ -188315,7 +218019,7 @@ static char *rbuObjIterGetIndexCols(
** the text ", old._rowid_" to the returned value.
*/
static char *rbuObjIterGetOldlist(
- sqlite3rbu *p,
+ sqlite3rbu *p,
RbuObjIter *pIter,
const char *zObj
){
@@ -188356,7 +218060,7 @@ static char *rbuObjIterGetOldlist(
** "b = ?1 AND c = ?2"
*/
static char *rbuObjIterGetWhere(
- sqlite3rbu *p,
+ sqlite3rbu *p,
RbuObjIter *pIter
){
char *zList = 0;
@@ -188371,7 +218075,7 @@ static char *rbuObjIterGetWhere(
zSep = " AND ";
}
}
- zList = rbuMPrintf(p,
+ zList = rbuMPrintf(p,
"_rowid_ = (SELECT id FROM rbu_imposter2 WHERE %z)", zList
);
@@ -188411,7 +218115,7 @@ static void rbuBadControlError(sqlite3rbu *p){
**
** The memory for the returned string is obtained from sqlite3_malloc().
** It is the responsibility of the caller to eventually free it using
-** sqlite3_free().
+** sqlite3_free().
**
** If an OOM error is encountered when allocating space for the new
** string, an error code is left in the rbu handle passed as the first
@@ -188435,19 +218139,19 @@ static char *rbuObjIterGetSetlist(
for(i=0; i<pIter->nTblCol; i++){
char c = zMask[pIter->aiSrcOrder[i]];
if( c=='x' ){
- zList = rbuMPrintf(p, "%z%s\"%w\"=?%d",
+ zList = rbuMPrintf(p, "%z%s\"%w\"=?%d",
zList, zSep, pIter->azTblCol[i], i+1
);
zSep = ", ";
}
else if( c=='d' ){
- zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_delta(\"%w\", ?%d)",
+ zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_delta(\"%w\", ?%d)",
zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1
);
zSep = ", ";
}
else if( c=='f' ){
- zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_fossil_delta(\"%w\", ?%d)",
+ zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_fossil_delta(\"%w\", ?%d)",
zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1
);
zSep = ", ";
@@ -188465,7 +218169,7 @@ static char *rbuObjIterGetSetlist(
**
** The memory for the returned string is obtained from sqlite3_malloc().
** It is the responsibility of the caller to eventually free it using
-** sqlite3_free().
+** sqlite3_free().
**
** If an OOM error is encountered when allocating space for the new
** string, an error code is left in the rbu handle passed as the first
@@ -188475,7 +218179,7 @@ static char *rbuObjIterGetSetlist(
*/
static char *rbuObjIterGetBindlist(sqlite3rbu *p, int nBind){
char *zRet = 0;
- int nByte = nBind*2 + 1;
+ sqlite3_int64 nByte = 2*(sqlite3_int64)nBind + 1;
zRet = (char*)rbuMalloc(p, nByte);
if( zRet ){
@@ -188489,8 +218193,8 @@ static char *rbuObjIterGetBindlist(sqlite3rbu *p, int nBind){
}
/*
-** The iterator currently points to a table (not index) of type
-** RBU_PK_WITHOUT_ROWID. This function creates the PRIMARY KEY
+** The iterator currently points to a table (not index) of type
+** RBU_PK_WITHOUT_ROWID. This function creates the PRIMARY KEY
** declaration for the corresponding imposter table. For example,
** if the iterator points to a table created as:
**
@@ -188507,7 +218211,7 @@ static char *rbuWithoutRowidPK(sqlite3rbu *p, RbuObjIter *pIter){
const char *zSep = "PRIMARY KEY(";
sqlite3_stmt *pXList = 0; /* PRAGMA index_list = (pIter->zTbl) */
sqlite3_stmt *pXInfo = 0; /* PRAGMA index_xinfo = <pk-index> */
-
+
p->rc = prepareFreeAndCollectError(p->dbMain, &pXList, &p->zErrmsg,
sqlite3_mprintf("PRAGMA main.index_list = %Q", pIter->zTbl)
);
@@ -188545,7 +218249,7 @@ static char *rbuWithoutRowidPK(sqlite3rbu *p, RbuObjIter *pIter){
** a table b-tree where the table has an external primary key. If the
** iterator passed as the second argument does not currently point to
** a table (not index) with an external primary key, this function is a
-** no-op.
+** no-op.
**
** Assuming the iterator does point to a table with an external PK, this
** function creates a WITHOUT ROWID imposter table named "rbu_imposter2"
@@ -188572,8 +218276,8 @@ static void rbuCreateImposterTable2(sqlite3rbu *p, RbuObjIter *pIter){
/* Figure out the name of the primary key index for the current table.
** This is needed for the argument to "PRAGMA index_xinfo". Set
** zIdx to point to a nul-terminated string containing this name. */
- p->rc = prepareAndCollectError(p->dbMain, &pQuery, &p->zErrmsg,
- "SELECT name FROM sqlite_master WHERE rootpage = ?"
+ p->rc = prepareAndCollectError(p->dbMain, &pQuery, &p->zErrmsg,
+ "SELECT name FROM sqlite_schema WHERE rootpage = ?"
);
if( p->rc==SQLITE_OK ){
sqlite3_bind_int(pQuery, 1, tnum);
@@ -188594,7 +218298,7 @@ static void rbuCreateImposterTable2(sqlite3rbu *p, RbuObjIter *pIter){
int iCid = sqlite3_column_int(pXInfo, 1);
int bDesc = sqlite3_column_int(pXInfo, 3);
const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
- zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %Q", zCols, zComma,
+ zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %Q", zCols, zComma,
iCid, pIter->azTblType[iCid], zCollate
);
zPk = rbuMPrintf(p, "%z%sc%d%s", zPk, zComma, iCid, bDesc?" DESC":"");
@@ -188606,7 +218310,7 @@ static void rbuCreateImposterTable2(sqlite3rbu *p, RbuObjIter *pIter){
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1, tnum);
rbuMPrintfExec(p, p->dbMain,
- "CREATE TABLE rbu_imposter2(%z, PRIMARY KEY(%z)) WITHOUT ROWID",
+ "CREATE TABLE rbu_imposter2(%z, PRIMARY KEY(%z)) WITHOUT ROWID",
zCols, zPk
);
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 0);
@@ -188614,7 +218318,7 @@ static void rbuCreateImposterTable2(sqlite3rbu *p, RbuObjIter *pIter){
}
/*
-** If an error has already occurred when this function is called, it
+** If an error has already occurred when this function is called, it
** immediately returns zero (without doing any work). Or, if an error
** occurs during the execution of this function, it sets the error code
** in the sqlite3rbu object indicated by the first argument and returns
@@ -188627,9 +218331,9 @@ static void rbuCreateImposterTable2(sqlite3rbu *p, RbuObjIter *pIter){
** an imposter table are created, or zero otherwise.
**
** An imposter table is required in all cases except RBU_PK_VTAB. Only
-** virtual tables are written to directly. The imposter table has the
-** same schema as the actual target table (less any UNIQUE constraints).
-** More precisely, the "same schema" means the same columns, types,
+** virtual tables are written to directly. The imposter table has the
+** same schema as the actual target table (less any UNIQUE constraints).
+** More precisely, the "same schema" means the same columns, types,
** collation sequences. For tables that do not have an external PRIMARY
** KEY, it also means the same PRIMARY KEY declaration.
*/
@@ -188655,7 +218359,7 @@ static void rbuCreateImposterTable(sqlite3rbu *p, RbuObjIter *pIter){
** "PRIMARY KEY" to the imposter table column declaration. */
zPk = "PRIMARY KEY ";
}
- zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %Q%s",
+ zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %Q%s",
zSql, zComma, zCol, pIter->azTblType[iCol], zPk, zColl,
(pIter->abNotNull[iCol] ? " NOT NULL" : "")
);
@@ -188670,8 +218374,8 @@ static void rbuCreateImposterTable(sqlite3rbu *p, RbuObjIter *pIter){
}
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1, tnum);
- rbuMPrintfExec(p, p->dbMain, "CREATE TABLE \"rbu_imp_%w\"(%z)%s",
- pIter->zTbl, zSql,
+ rbuMPrintfExec(p, p->dbMain, "CREATE TABLE \"rbu_imp_%w\"(%z)%s",
+ pIter->zTbl, zSql,
(pIter->eType==RBU_PK_WITHOUT_ROWID ? " WITHOUT ROWID" : "")
);
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 0);
@@ -188685,12 +218389,12 @@ static void rbuCreateImposterTable(sqlite3rbu *p, RbuObjIter *pIter){
** INSERT INTO rbu_tmp_xxx VALUES(?, ?, ? ...);
**
** The number of bound variables is equal to the number of columns in
-** the target table, plus one (for the rbu_control column), plus one more
-** (for the rbu_rowid column) if the target table is an implicit IPK or
+** the target table, plus one (for the rbu_control column), plus one more
+** (for the rbu_rowid column) if the target table is an implicit IPK or
** virtual table.
*/
static void rbuObjIterPrepareTmpInsert(
- sqlite3rbu *p,
+ sqlite3rbu *p,
RbuObjIter *pIter,
const char *zCollist,
const char *zRbuRowid
@@ -188701,14 +218405,14 @@ static void rbuObjIterPrepareTmpInsert(
assert( pIter->pTmpInsert==0 );
p->rc = prepareFreeAndCollectError(
p->dbRbu, &pIter->pTmpInsert, &p->zErrmsg, sqlite3_mprintf(
- "INSERT INTO %s.'rbu_tmp_%q'(rbu_control,%s%s) VALUES(%z)",
+ "INSERT INTO %s.'rbu_tmp_%q'(rbu_control,%s%s) VALUES(%z)",
p->zStateDb, pIter->zDataTbl, zCollist, zRbuRowid, zBind
));
}
}
static void rbuTmpInsertFunc(
- sqlite3_context *pCtx,
+ sqlite3_context *pCtx,
int nVal,
sqlite3_value **apVal
){
@@ -188717,8 +218421,8 @@ static void rbuTmpInsertFunc(
int i;
assert( sqlite3_value_int(apVal[0])!=0
- || p->objiter.eType==RBU_PK_EXTERNAL
- || p->objiter.eType==RBU_PK_NONE
+ || p->objiter.eType==RBU_PK_EXTERNAL
+ || p->objiter.eType==RBU_PK_NONE
);
if( sqlite3_value_int(apVal[0])!=0 ){
p->nPhaseOneStep += p->objiter.nIndex;
@@ -188737,13 +218441,108 @@ static void rbuTmpInsertFunc(
}
}
+static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
+ sqlite3_stmt *pStmt = 0;
+ int rc = p->rc;
+ char *zRet = 0;
+
+ assert( pIter->zIdxSql==0 && pIter->nIdxCol==0 && pIter->aIdxCol==0 );
+
+ if( rc==SQLITE_OK ){
+ rc = prepareAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
+ "SELECT trim(sql) FROM sqlite_schema WHERE type='index' AND name=?"
+ );
+ }
+ if( rc==SQLITE_OK ){
+ int rc2;
+ rc = sqlite3_bind_text(pStmt, 1, pIter->zIdx, -1, SQLITE_STATIC);
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ char *zSql = (char*)sqlite3_column_text(pStmt, 0);
+ if( zSql ){
+ pIter->zIdxSql = zSql = rbuStrndup(zSql, &rc);
+ }
+ if( zSql ){
+ int nParen = 0; /* Number of open parenthesis */
+ int i;
+ int iIdxCol = 0;
+ int nIdxAlloc = 0;
+ for(i=0; zSql[i]; i++){
+ char c = zSql[i];
+
+ /* If necessary, grow the pIter->aIdxCol[] array */
+ if( iIdxCol==nIdxAlloc ){
+ RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc(
+ pIter->aIdxCol, (nIdxAlloc+16)*sizeof(RbuSpan)
+ );
+ if( aIdxCol==0 ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ pIter->aIdxCol = aIdxCol;
+ nIdxAlloc += 16;
+ }
+
+ if( c=='(' ){
+ if( nParen==0 ){
+ assert( iIdxCol==0 );
+ pIter->aIdxCol[0].zSpan = &zSql[i+1];
+ }
+ nParen++;
+ }
+ else if( c==')' ){
+ nParen--;
+ if( nParen==0 ){
+ int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan;
+ pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
+ i++;
+ break;
+ }
+ }else if( c==',' && nParen==1 ){
+ int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan;
+ pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
+ pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1];
+ }else if( c=='"' || c=='\'' || c=='`' ){
+ for(i++; 1; i++){
+ if( zSql[i]==c ){
+ if( zSql[i+1]!=c ) break;
+ i++;
+ }
+ }
+ }else if( c=='[' ){
+ for(i++; 1; i++){
+ if( zSql[i]==']' ) break;
+ }
+ }else if( c=='-' && zSql[i+1]=='-' ){
+ for(i=i+2; zSql[i] && zSql[i]!='\n'; i++);
+ if( zSql[i]=='\0' ) break;
+ }else if( c=='/' && zSql[i+1]=='*' ){
+ for(i=i+2; zSql[i] && (zSql[i]!='*' || zSql[i+1]!='/'); i++);
+ if( zSql[i]=='\0' ) break;
+ i++;
+ }
+ }
+ if( zSql[i] ){
+ zRet = rbuStrndup(&zSql[i], &rc);
+ }
+ pIter->nIdxCol = iIdxCol;
+ }
+ }
+
+ rc2 = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ p->rc = rc;
+ return zRet;
+}
+
/*
-** Ensure that the SQLite statement handles required to update the
-** target database object currently indicated by the iterator passed
+** Ensure that the SQLite statement handles required to update the
+** target database object currently indicated by the iterator passed
** as the second argument are available.
*/
static int rbuObjIterPrepareAll(
- sqlite3rbu *p,
+ sqlite3rbu *p,
RbuObjIter *pIter,
int nOffset /* Add "LIMIT -1 OFFSET $nOffset" to SELECT */
){
@@ -188766,9 +218565,11 @@ static int rbuObjIterPrepareAll(
char *zImposterPK = 0; /* Primary key declaration for imposter */
char *zWhere = 0; /* WHERE clause on PK columns */
char *zBind = 0;
+ char *zPart = 0;
int nBind = 0;
assert( pIter->eType!=RBU_PK_VTAB );
+ zPart = rbuObjIterGetIndexWhere(p, pIter);
zCollist = rbuObjIterGetIndexCols(
p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind
);
@@ -188804,39 +218605,58 @@ static int rbuObjIterPrepareAll(
if( p->rc==SQLITE_OK ){
char *zSql;
if( rbuIsVacuum(p) ){
+ char *zStart = 0;
+ if( nOffset ){
+ zStart = rbuVacuumIndexStart(p, pIter);
+ if( zStart ){
+ sqlite3_free(zLimit);
+ zLimit = 0;
+ }
+ }
+
zSql = sqlite3_mprintf(
- "SELECT %s, 0 AS rbu_control FROM '%q' ORDER BY %s%s",
- zCollist,
+ "SELECT %s, 0 AS rbu_control FROM '%q' %s %s %s ORDER BY %s%s",
+ zCollist,
pIter->zDataTbl,
+ zPart,
+ (zStart ? (zPart ? "AND" : "WHERE") : ""), zStart,
zCollist, zLimit
);
+ sqlite3_free(zStart);
}else
if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
zSql = sqlite3_mprintf(
- "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s",
+ "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' %s ORDER BY %s%s",
zCollist, p->zStateDb, pIter->zDataTbl,
- zCollist, zLimit
+ zPart, zCollist, zLimit
);
}else{
zSql = sqlite3_mprintf(
- "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "
+ "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' %s "
"UNION ALL "
"SELECT %s, rbu_control FROM '%q' "
- "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 "
+ "%s %s typeof(rbu_control)='integer' AND rbu_control!=1 "
"ORDER BY %s%s",
- zCollist, p->zStateDb, pIter->zDataTbl,
- zCollist, pIter->zDataTbl,
+ zCollist, p->zStateDb, pIter->zDataTbl, zPart,
+ zCollist, pIter->zDataTbl,
+ zPart,
+ (zPart ? "AND" : "WHERE"),
zCollist, zLimit
);
}
- p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, zSql);
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(p->dbRbu,&pIter->pSelect,pz,zSql);
+ }else{
+ sqlite3_free(zSql);
+ }
}
sqlite3_free(zImposterCols);
sqlite3_free(zImposterPK);
sqlite3_free(zWhere);
sqlite3_free(zBind);
+ sqlite3_free(zPart);
}else{
int bRbuRowid = (pIter->eType==RBU_PK_VTAB)
||(pIter->eType==RBU_PK_NONE)
@@ -188861,7 +218681,7 @@ static int rbuObjIterPrepareAll(
if( p->rc==SQLITE_OK ){
p->rc = prepareFreeAndCollectError(p->dbMain, &pIter->pInsert, pz,
sqlite3_mprintf(
- "INSERT INTO \"%s%w\"(%s%s) VALUES(%s)",
+ "INSERT INTO \"%s%w\"(%s%s) VALUES(%s)",
zWrite, zTbl, zCollist, (bRbuRowid ? ", _rowid_" : ""), zBindings
)
);
@@ -188929,18 +218749,42 @@ static int rbuObjIterPrepareAll(
/* Create the SELECT statement to read keys from data_xxx */
if( p->rc==SQLITE_OK ){
const char *zRbuRowid = "";
+ char *zStart = 0;
+ char *zOrder = 0;
if( bRbuRowid ){
zRbuRowid = rbuIsVacuum(p) ? ",_rowid_ " : ",rbu_rowid";
}
- p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz,
- sqlite3_mprintf(
- "SELECT %s,%s rbu_control%s FROM '%q'%s",
- zCollist,
- (rbuIsVacuum(p) ? "0 AS " : ""),
- zRbuRowid,
- pIter->zDataTbl, zLimit
- )
- );
+
+ if( rbuIsVacuum(p) ){
+ if( nOffset ){
+ zStart = rbuVacuumTableStart(p, pIter, bRbuRowid, zWrite);
+ if( zStart ){
+ sqlite3_free(zLimit);
+ zLimit = 0;
+ }
+ }
+ if( bRbuRowid ){
+ zOrder = rbuMPrintf(p, "_rowid_");
+ }else{
+ zOrder = rbuObjIterGetPkList(p, pIter, "", ", ", "");
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz,
+ sqlite3_mprintf(
+ "SELECT %s,%s rbu_control%s FROM '%q'%s %s %s %s",
+ zCollist,
+ (rbuIsVacuum(p) ? "0 AS " : ""),
+ zRbuRowid,
+ pIter->zDataTbl, (zStart ? zStart : ""),
+ (zOrder ? "ORDER BY" : ""), zOrder,
+ zLimit
+ )
+ );
+ }
+ sqlite3_free(zStart);
+ sqlite3_free(zOrder);
}
sqlite3_free(zWhere);
@@ -188951,16 +218795,16 @@ static int rbuObjIterPrepareAll(
sqlite3_free(zCollist);
sqlite3_free(zLimit);
}
-
+
return p->rc;
}
/*
** Set output variable *ppStmt to point to an UPDATE statement that may
** be used to update the imposter table for the main table b-tree of the
-** table object that pIter currently points to, assuming that the
+** table object that pIter currently points to, assuming that the
** rbu_control column of the data_xyz table contains zMask.
-**
+**
** If the zMask string does not specify any columns to update, then this
** is not an error. Output variable *ppStmt is set to NULL in this case.
*/
@@ -188987,7 +218831,7 @@ static int rbuGetUpdateStmt(
*pp = pUp->pNext;
pUp->pNext = pIter->pRbuUpdate;
pIter->pRbuUpdate = pUp;
- *ppStmt = pUp->pUpdate;
+ *ppStmt = pUp->pUpdate;
return SQLITE_OK;
}
nUp++;
@@ -189017,7 +218861,7 @@ static int rbuGetUpdateStmt(
const char *zPrefix = "";
if( pIter->eType!=RBU_PK_VTAB ) zPrefix = "rbu_imp_";
- zUpdate = sqlite3_mprintf("UPDATE \"%s%w\" SET %s WHERE %s",
+ zUpdate = sqlite3_mprintf("UPDATE \"%s%w\" SET %s WHERE %s",
zPrefix, pIter->zTbl, zSet, zWhere
);
p->rc = prepareFreeAndCollectError(
@@ -189033,8 +218877,8 @@ static int rbuGetUpdateStmt(
}
static sqlite3 *rbuOpenDbhandle(
- sqlite3rbu *p,
- const char *zName,
+ sqlite3rbu *p,
+ const char *zName,
int bUseVfs
){
sqlite3 *db = 0;
@@ -189063,8 +218907,8 @@ static void rbuFreeState(RbuState *p){
}
/*
-** Allocate an RbuState object and load the contents of the rbu_state
-** table into it. Return a pointer to the new object. It is the
+** Allocate an RbuState object and load the contents of the rbu_state
+** table into it. Return a pointer to the new object. It is the
** responsibility of the caller to eventually free the object using
** sqlite3_free().
**
@@ -189080,7 +218924,7 @@ static RbuState *rbuLoadState(sqlite3rbu *p){
pRet = (RbuState*)rbuMalloc(p, sizeof(RbuState));
if( pRet==0 ) return 0;
- rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
+ rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
sqlite3_mprintf("SELECT k, v FROM %s.rbu_state", p->zStateDb)
);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
@@ -189120,7 +218964,7 @@ static RbuState *rbuLoadState(sqlite3rbu *p){
break;
case RBU_STATE_OALSZ:
- pRet->iOalSz = (u32)sqlite3_column_int64(pStmt, 1);
+ pRet->iOalSz = sqlite3_column_int64(pStmt, 1);
break;
case RBU_STATE_PHASEONESTEP:
@@ -189147,19 +218991,25 @@ static RbuState *rbuLoadState(sqlite3rbu *p){
/*
** Open the database handle and attach the RBU database as "rbu". If an
** error occurs, leave an error code and message in the RBU handle.
+**
+** If argument dbMain is not NULL, then it is a database handle already
+** open on the target database. Use this handle instead of opening a new
+** one.
*/
-static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
+static void rbuOpenDatabase(sqlite3rbu *p, sqlite3 *dbMain, int *pbRetry){
assert( p->rc || (p->dbMain==0 && p->dbRbu==0) );
assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 );
+ assert( dbMain==0 || rbuIsVacuum(p)==0 );
/* Open the RBU database */
p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1);
+ p->dbMain = dbMain;
if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
if( p->zState==0 ){
const char *zFile = sqlite3_db_filename(p->dbRbu, "main");
- p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile);
+ p->zState = rbuMPrintf(p, "file:///%s-vacuum?modeof=%s", zFile, zFile);
}
}
@@ -189188,9 +219038,9 @@ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
int bOk = 0;
sqlite3_stmt *pCnt = 0;
p->rc = prepareAndCollectError(p->dbRbu, &pCnt, &p->zErrmsg,
- "SELECT count(*) FROM stat.sqlite_master"
+ "SELECT count(*) FROM stat.sqlite_schema"
);
- if( p->rc==SQLITE_OK
+ if( p->rc==SQLITE_OK
&& sqlite3_step(pCnt)==SQLITE_ROW
&& 1==sqlite3_column_int(pCnt, 0)
){
@@ -189203,7 +219053,7 @@ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
p->rc = SQLITE_ERROR;
p->zErrmsg = sqlite3_mprintf("invalid state database");
}
-
+
if( p->rc==SQLITE_OK ){
p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, 0);
}
@@ -189257,7 +219107,7 @@ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
if( *zExtra=='\0' ) zExtra = 0;
}
- zTarget = sqlite3_mprintf("file:%s-vacuum?rbu_memory=1%s%s",
+ zTarget = sqlite3_mprintf("file:%s-vactmp?rbu_memory=1%s%s",
sqlite3_db_filename(p->dbRbu, "main"),
(zExtra==0 ? "" : "&"), (zExtra==0 ? "" : zExtra)
);
@@ -189272,19 +219122,19 @@ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
}
if( p->rc==SQLITE_OK ){
- p->rc = sqlite3_create_function(p->dbMain,
+ p->rc = sqlite3_create_function(p->dbMain,
"rbu_tmp_insert", -1, SQLITE_UTF8, (void*)p, rbuTmpInsertFunc, 0, 0
);
}
if( p->rc==SQLITE_OK ){
- p->rc = sqlite3_create_function(p->dbMain,
+ p->rc = sqlite3_create_function(p->dbMain,
"rbu_fossil_delta", 2, SQLITE_UTF8, 0, rbuFossilDeltaFunc, 0, 0
);
}
if( p->rc==SQLITE_OK ){
- p->rc = sqlite3_create_function(p->dbRbu,
+ p->rc = sqlite3_create_function(p->dbRbu,
"rbu_target_name", -1, SQLITE_UTF8, (void*)p, rbuTargetNameFunc, 0, 0
);
}
@@ -189292,9 +219142,9 @@ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
if( p->rc==SQLITE_OK ){
p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p);
}
- rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_master");
+ rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_schema");
- /* Mark the database file just opened as an RBU target database. If
+ /* Mark the database file just opened as an RBU target database. If
** this call returns SQLITE_NOTFOUND, then the RBU vfs is not in use.
** This is an error. */
if( p->rc==SQLITE_OK ){
@@ -189342,10 +219192,10 @@ static void rbuFileSuffix3(const char *zBase, char *z){
}
/*
-** Return the current wal-index header checksum for the target database
+** Return the current wal-index header checksum for the target database
** as a 64-bit integer.
**
-** The checksum is store in the first page of xShmMap memory as an 8-byte
+** The checksum is store in the first page of xShmMap memory as an 8-byte
** blob starting at byte offset 40.
*/
static i64 rbuShmChecksum(sqlite3rbu *p){
@@ -189363,11 +219213,11 @@ static i64 rbuShmChecksum(sqlite3rbu *p){
/*
** This function is called as part of initializing or reinitializing an
-** incremental checkpoint.
+** incremental checkpoint.
**
-** It populates the sqlite3rbu.aFrame[] array with the set of
-** (wal frame -> db page) copy operations required to checkpoint the
-** current wal file, and obtains the set of shm locks required to safely
+** It populates the sqlite3rbu.aFrame[] array with the set of
+** (wal frame -> db page) copy operations required to checkpoint the
+** current wal file, and obtains the set of shm locks required to safely
** perform the copy operations directly on the file-system.
**
** If argument pState is not NULL, then the incremental checkpoint is
@@ -189385,7 +219235,7 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
if( pState==0 ){
p->eStage = 0;
if( p->rc==SQLITE_OK ){
- p->rc = sqlite3_exec(p->dbMain, "SELECT * FROM sqlite_master", 0, 0, 0);
+ p->rc = sqlite3_exec(p->dbMain, "SELECT * FROM sqlite_schema", 0, 0, 0);
}
}
@@ -189402,26 +219252,26 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
** would be read/written are recorded in the sqlite3rbu.aFrame[]
** array.
**
- ** * Calls to xShmLock(UNLOCK) to release the exclusive shm WRITER,
+ ** * Calls to xShmLock(UNLOCK) to release the exclusive shm WRITER,
** READ0 and CHECKPOINT locks taken as part of the checkpoint are
** no-ops. These locks will not be released until the connection
** is closed.
**
- ** * Attempting to xSync() the database file causes an SQLITE_INTERNAL
+ ** * Attempting to xSync() the database file causes an SQLITE_NOTICE
** error.
**
** As a result, unless an error (i.e. OOM or SQLITE_BUSY) occurs, the
- ** checkpoint below fails with SQLITE_INTERNAL, and leaves the aFrame[]
- ** array populated with a set of (frame -> page) mappings. Because the
- ** WRITER, CHECKPOINT and READ0 locks are still held, it is safe to copy
- ** data from the wal file into the database file according to the
+ ** checkpoint below fails with SQLITE_NOTICE, and leaves the aFrame[]
+ ** array populated with a set of (frame -> page) mappings. Because the
+ ** WRITER, CHECKPOINT and READ0 locks are still held, it is safe to copy
+ ** data from the wal file into the database file according to the
** contents of aFrame[].
*/
if( p->rc==SQLITE_OK ){
int rc2;
p->eStage = RBU_STAGE_CAPTURE;
rc2 = sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0);
- if( rc2!=SQLITE_INTERNAL ) p->rc = rc2;
+ if( rc2!=SQLITE_NOTICE ) p->rc = rc2;
}
if( p->rc==SQLITE_OK && p->nFrame>0 ){
@@ -189447,9 +219297,9 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
p->nPagePerSector = 1;
}
- /* Call xSync() on the wal file. This causes SQLite to sync the
- ** directory in which the target database and the wal file reside, in
- ** case it has not been synced since the rename() call in
+ /* Call xSync() on the wal file. This causes SQLite to sync the
+ ** directory in which the target database and the wal file reside, in
+ ** case it has not been synced since the rename() call in
** rbuMoveOalFile(). */
p->rc = pWal->pMethods->xSync(pWal, SQLITE_SYNC_NORMAL);
}
@@ -189467,7 +219317,7 @@ static int rbuCaptureWalRead(sqlite3rbu *pRbu, i64 iOff, int iAmt){
if( pRbu->mLock!=mReq ){
pRbu->rc = SQLITE_BUSY;
- return SQLITE_INTERNAL;
+ return SQLITE_NOTICE_RBU;
}
pRbu->pgsz = iAmt;
@@ -189490,7 +219340,7 @@ static int rbuCaptureWalRead(sqlite3rbu *pRbu, i64 iOff, int iAmt){
/*
** Called when a page of data is written to offset iOff of the database
-** file while the rbu handle is in capture mode. Record the page number
+** file while the rbu handle is in capture mode. Record the page number
** of the page being written in the aFrame[] array.
*/
static int rbuCaptureDbWrite(sqlite3rbu *pRbu, i64 iOff){
@@ -189517,17 +219367,49 @@ static void rbuCheckpointFrame(sqlite3rbu *p, RbuFrame *pFrame){
p->rc = pDb->pMethods->xWrite(pDb, p->aBuf, p->pgsz, iOff);
}
+/*
+** This value is copied from the definition of ZIPVFS_CTRL_FILE_POINTER
+** in zipvfs.h.
+*/
+#define RBU_ZIPVFS_CTRL_FILE_POINTER 230439
/*
-** Take an EXCLUSIVE lock on the database file.
+** Take an EXCLUSIVE lock on the database file. Return SQLITE_OK if
+** successful, or an SQLite error code otherwise.
*/
-static void rbuLockDatabase(sqlite3rbu *p){
- sqlite3_file *pReal = p->pTargetFd->pReal;
- assert( p->rc==SQLITE_OK );
- p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_SHARED);
- if( p->rc==SQLITE_OK ){
- p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_EXCLUSIVE);
+static int rbuLockDatabase(sqlite3 *db){
+ int rc = SQLITE_OK;
+ sqlite3_file *fd = 0;
+
+ sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd);
+ if( fd ){
+ sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd);
+ rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED);
+ if( rc==SQLITE_OK ){
+ rc = fd->pMethods->xUnlock(fd, SQLITE_LOCK_NONE);
+ }
+ sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd);
+ }else{
+ sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd);
+ }
+
+ if( rc==SQLITE_OK && fd->pMethods ){
+ rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED);
+ if( rc==SQLITE_OK ){
+ rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE);
+ }
}
+ return rc;
+}
+
+/*
+** Return true if the database handle passed as the only argument
+** was opened with the rbu_exclusive_checkpoint=1 URI parameter
+** specified. Or false otherwise.
+*/
+static int rbuExclusiveCheckpoint(sqlite3 *db){
+ const char *zUri = sqlite3_db_filename(db, 0);
+ return sqlite3_uri_boolean(zUri, RBU_EXCLUSIVE_CHECKPOINT, 0);
}
#if defined(_WIN32_WCE)
@@ -189558,7 +219440,7 @@ static LPWSTR rbuWinUtf8ToUnicode(const char *zFilename){
** The RBU handle is currently in RBU_STAGE_OAL state, with a SHARED lock
** on the database file. This proc moves the *-oal file to the *-wal path,
** then reopens the database file (this time in vanilla, non-oal, WAL mode).
-** If an error occurs, leave an error code and error message in the rbu
+** If an error occurs, leave an error code and error message in the rbu
** handle.
*/
static void rbuMoveOalFile(sqlite3rbu *p){
@@ -189580,54 +219462,43 @@ static void rbuMoveOalFile(sqlite3rbu *p){
}else{
/* Move the *-oal file to *-wal. At this point connection p->db is
** holding a SHARED lock on the target database file (because it is
- ** in WAL mode). So no other connection may be writing the db.
+ ** in WAL mode). So no other connection may be writing the db.
**
** In order to ensure that there are no database readers, an EXCLUSIVE
** lock is obtained here before the *-oal is moved to *-wal.
*/
- rbuLockDatabase(p);
- if( p->rc==SQLITE_OK ){
- rbuFileSuffix3(zBase, zWal);
- rbuFileSuffix3(zBase, zOal);
+ sqlite3 *dbMain = 0;
+ rbuFileSuffix3(zBase, zWal);
+ rbuFileSuffix3(zBase, zOal);
- /* Re-open the databases. */
- rbuObjIterFinalize(&p->objiter);
- sqlite3_close(p->dbRbu);
- sqlite3_close(p->dbMain);
- p->dbMain = 0;
- p->dbRbu = 0;
+ /* Re-open the databases. */
+ rbuObjIterFinalize(&p->objiter);
+ sqlite3_close(p->dbRbu);
+ sqlite3_close(p->dbMain);
+ p->dbMain = 0;
+ p->dbRbu = 0;
-#if defined(_WIN32_WCE)
- {
- LPWSTR zWideOal;
- LPWSTR zWideWal;
-
- zWideOal = rbuWinUtf8ToUnicode(zOal);
- if( zWideOal ){
- zWideWal = rbuWinUtf8ToUnicode(zWal);
- if( zWideWal ){
- if( MoveFileW(zWideOal, zWideWal) ){
- p->rc = SQLITE_OK;
- }else{
- p->rc = SQLITE_IOERR;
- }
- sqlite3_free(zWideWal);
- }else{
- p->rc = SQLITE_IOERR_NOMEM;
- }
- sqlite3_free(zWideOal);
- }else{
- p->rc = SQLITE_IOERR_NOMEM;
- }
- }
-#else
- p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK;
-#endif
+ dbMain = rbuOpenDbhandle(p, p->zTarget, 1);
+ if( dbMain ){
+ assert( p->rc==SQLITE_OK );
+ p->rc = rbuLockDatabase(dbMain);
+ }
- if( p->rc==SQLITE_OK ){
- rbuOpenDatabase(p, 0);
- rbuSetupCheckpoint(p, 0);
- }
+ if( p->rc==SQLITE_OK ){
+ p->rc = p->xRename(p->pRenameArg, zOal, zWal);
+ }
+
+ if( p->rc!=SQLITE_OK
+ || rbuIsVacuum(p)
+ || rbuExclusiveCheckpoint(dbMain)==0
+ ){
+ sqlite3_close(dbMain);
+ dbMain = 0;
+ }
+
+ if( p->rc==SQLITE_OK ){
+ rbuOpenDatabase(p, dbMain, 0);
+ rbuSetupCheckpoint(p, 0);
}
}
@@ -189738,8 +219609,8 @@ static void rbuStepOneOp(sqlite3rbu *p, int eType){
/* If this is an INSERT into a table b-tree and the table has an
** explicit INTEGER PRIMARY KEY, check that this is not an attempt
** to write a NULL into the IPK column. That is not permitted. */
- if( eType==RBU_INSERT
- && pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i]
+ if( eType==RBU_INSERT
+ && pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i]
&& sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
){
p->rc = SQLITE_MISMATCH;
@@ -189756,18 +219627,18 @@ static void rbuStepOneOp(sqlite3rbu *p, int eType){
if( p->rc ) return;
}
if( pIter->zIdx==0 ){
- if( pIter->eType==RBU_PK_VTAB
- || pIter->eType==RBU_PK_NONE
- || (pIter->eType==RBU_PK_EXTERNAL && rbuIsVacuum(p))
+ if( pIter->eType==RBU_PK_VTAB
+ || pIter->eType==RBU_PK_NONE
+ || (pIter->eType==RBU_PK_EXTERNAL && rbuIsVacuum(p))
){
- /* For a virtual table, or a table with no primary key, the
+ /* For a virtual table, or a table with no primary key, the
** SELECT statement is:
**
** SELECT <cols>, rbu_control, rbu_rowid FROM ....
**
** Hence column_value(pIter->nCol+1).
*/
- assertColumnName(pIter->pSelect, pIter->nCol+1,
+ assertColumnName(pIter->pSelect, pIter->nCol+1,
rbuIsVacuum(p) ? "rowid" : "rbu_rowid"
);
pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
@@ -189831,8 +219702,8 @@ static int rbuStep(sqlite3rbu *p){
p->rc = sqlite3_bind_value(pUpdate, i+1, pVal);
}
}
- if( p->rc==SQLITE_OK
- && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE)
+ if( p->rc==SQLITE_OK
+ && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE)
){
/* Bind the rbu_rowid value to column _rowid_ */
assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid");
@@ -189862,7 +219733,7 @@ static void rbuIncrSchemaCookie(sqlite3rbu *p){
int iCookie = 1000000;
sqlite3_stmt *pStmt;
- p->rc = prepareAndCollectError(dbread, &pStmt, &p->zErrmsg,
+ p->rc = prepareAndCollectError(dbread, &pStmt, &p->zErrmsg,
"PRAGMA schema_version"
);
if( p->rc==SQLITE_OK ){
@@ -189894,7 +219765,7 @@ static void rbuSaveState(sqlite3rbu *p, int eStage){
int rc;
assert( p->zErrmsg==0 );
- rc = prepareFreeAndCollectError(p->dbRbu, &pInsert, &p->zErrmsg,
+ rc = prepareFreeAndCollectError(p->dbRbu, &pInsert, &p->zErrmsg,
sqlite3_mprintf(
"INSERT OR REPLACE INTO %s.rbu_state(k, v) VALUES "
"(%d, %d), "
@@ -189909,9 +219780,9 @@ static void rbuSaveState(sqlite3rbu *p, int eStage){
"(%d, %Q) ",
p->zStateDb,
RBU_STATE_STAGE, eStage,
- RBU_STATE_TBL, p->objiter.zTbl,
- RBU_STATE_IDX, p->objiter.zIdx,
- RBU_STATE_ROW, p->nStep,
+ RBU_STATE_TBL, p->objiter.zTbl,
+ RBU_STATE_IDX, p->objiter.zIdx,
+ RBU_STATE_ROW, p->nStep,
RBU_STATE_PROGRESS, p->nProgress,
RBU_STATE_CKPT, p->iWalCksum,
RBU_STATE_COOKIE, (i64)pFd->iCookie,
@@ -189932,7 +219803,7 @@ static void rbuSaveState(sqlite3rbu *p, int eStage){
/*
-** The second argument passed to this function is the name of a PRAGMA
+** The second argument passed to this function is the name of a PRAGMA
** setting - "page_size", "auto_vacuum", "user_version" or "application_id".
** This function executes the following on sqlite3rbu.dbRbu:
**
@@ -189951,7 +219822,7 @@ static void rbuSaveState(sqlite3rbu *p, int eStage){
static void rbuCopyPragma(sqlite3rbu *p, const char *zPragma){
if( p->rc==SQLITE_OK ){
sqlite3_stmt *pPragma = 0;
- p->rc = prepareFreeAndCollectError(p->dbRbu, &pPragma, &p->zErrmsg,
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pPragma, &p->zErrmsg,
sqlite3_mprintf("PRAGMA main.%s", zPragma)
);
if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPragma) ){
@@ -189964,7 +219835,7 @@ static void rbuCopyPragma(sqlite3rbu *p, const char *zPragma){
}
/*
-** The RBU handle passed as the only argument has just been opened and
+** The RBU handle passed as the only argument has just been opened and
** the state database is empty. If this RBU handle was opened for an
** RBU vacuum operation, create the schema in the target db.
*/
@@ -189975,8 +219846,8 @@ static void rbuCreateTargetSchema(sqlite3rbu *p){
assert( rbuIsVacuum(p) );
p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=1", 0,0, &p->zErrmsg);
if( p->rc==SQLITE_OK ){
- p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
- "SELECT sql FROM sqlite_master WHERE sql!='' AND rootpage!=0"
+ p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
+ "SELECT sql FROM sqlite_schema WHERE sql!='' AND rootpage!=0"
" AND name!='sqlite_sequence' "
" ORDER BY type DESC"
);
@@ -189990,14 +219861,14 @@ static void rbuCreateTargetSchema(sqlite3rbu *p){
if( p->rc!=SQLITE_OK ) return;
if( p->rc==SQLITE_OK ){
- p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
- "SELECT * FROM sqlite_master WHERE rootpage=0 OR rootpage IS NULL"
+ p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
+ "SELECT * FROM sqlite_schema WHERE rootpage=0 OR rootpage IS NULL"
);
}
if( p->rc==SQLITE_OK ){
- p->rc = prepareAndCollectError(p->dbMain, &pInsert, &p->zErrmsg,
- "INSERT INTO sqlite_master VALUES(?,?,?,?,?)"
+ p->rc = prepareAndCollectError(p->dbMain, &pInsert, &p->zErrmsg,
+ "INSERT INTO sqlite_schema VALUES(?,?,?,?,?)"
);
}
@@ -190037,11 +219908,11 @@ SQLITE_API int sqlite3rbu_step(sqlite3rbu *p){
while( p->rc==SQLITE_OK && pIter->zTbl ){
if( pIter->bCleanup ){
- /* Clean up the rbu_tmp_xxx table for the previous table. It
+ /* Clean up the rbu_tmp_xxx table for the previous table. It
** cannot be dropped as there are currently active SQL statements.
** But the contents can be deleted. */
if( rbuIsVacuum(p)==0 && pIter->abIndexed ){
- rbuMPrintfExec(p, p->dbRbu,
+ rbuMPrintfExec(p, p->dbRbu,
"DELETE FROM %s.'rbu_tmp_%q'", p->zStateDb, pIter->zDataTbl
);
}
@@ -190091,10 +219962,10 @@ SQLITE_API int sqlite3rbu_step(sqlite3rbu *p){
if( p->rc==SQLITE_OK ){
if( p->nStep>=p->nFrame ){
sqlite3_file *pDb = p->pTargetFd->pReal;
-
+
/* Sync the db file */
p->rc = pDb->pMethods->xSync(pDb, SQLITE_SYNC_NORMAL);
-
+
/* Update nBackfill */
if( p->rc==SQLITE_OK ){
void volatile *ptr;
@@ -190103,7 +219974,7 @@ SQLITE_API int sqlite3rbu_step(sqlite3rbu *p){
((u32 volatile*)ptr)[24] = p->iMaxFrame;
}
}
-
+
if( p->rc==SQLITE_OK ){
p->eStage = RBU_STAGE_DONE;
p->rc = SQLITE_DONE;
@@ -190111,7 +219982,7 @@ SQLITE_API int sqlite3rbu_step(sqlite3rbu *p){
}else{
/* At one point the following block copied a single frame from the
** wal file to the database file. So that one call to sqlite3rbu_step()
- ** checkpointed a single frame.
+ ** checkpointed a single frame.
**
** However, if the sector-size is larger than the page-size, and the
** application calls sqlite3rbu_savestate() or close() immediately
@@ -190125,7 +219996,7 @@ SQLITE_API int sqlite3rbu_step(sqlite3rbu *p){
iSector = (pFrame->iDbPage-1) / p->nPagePerSector;
rbuCheckpointFrame(p, pFrame);
p->nStep++;
- }while( p->nStep<p->nFrame
+ }while( p->nStep<p->nFrame
&& iSector==((p->aFrame[p->nStep].iDbPage-1) / p->nPagePerSector)
&& p->rc==SQLITE_OK
);
@@ -190171,7 +220042,7 @@ static void rbuSetupOal(sqlite3rbu *p, RbuState *pState){
RbuObjIter *pIter = &p->objiter;
int rc = SQLITE_OK;
- while( rc==SQLITE_OK && pIter->zTbl && (pIter->bCleanup
+ while( rc==SQLITE_OK && pIter->zTbl && (pIter->bCleanup
|| rbuStrCompare(pIter->zIdx, pState->zIdx)
|| (pState->zDataTbl==0 && rbuStrCompare(pIter->zTbl, pState->zTbl))
|| (pState->zDataTbl && rbuStrCompare(pIter->zDataTbl, pState->zDataTbl))
@@ -190201,7 +220072,8 @@ static void rbuSetupOal(sqlite3rbu *p, RbuState *pState){
static void rbuDeleteOalFile(sqlite3rbu *p){
char *zOal = rbuMPrintf(p, "%s-oal", p->zTarget);
if( zOal ){
- sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
+ sqlite3_vfs *pVfs = 0;
+ sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_VFS_POINTER, &pVfs);
assert( pVfs && p->rc==SQLITE_OK && p->zErrmsg==0 );
pVfs->xDelete(pVfs, zOal, 0);
sqlite3_free(zOal);
@@ -190247,7 +220119,7 @@ static void rbuDeleteVfs(sqlite3rbu *p){
** the number of auxilliary indexes on the table.
*/
static void rbuIndexCntFunc(
- sqlite3_context *pCtx,
+ sqlite3_context *pCtx,
int nVal,
sqlite3_value **apVal
){
@@ -190255,11 +220127,12 @@ static void rbuIndexCntFunc(
sqlite3_stmt *pStmt = 0;
char *zErrmsg = 0;
int rc;
+ sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain);
assert( nVal==1 );
-
- rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &zErrmsg,
- sqlite3_mprintf("SELECT count(*) FROM sqlite_master "
+
+ rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg,
+ sqlite3_mprintf("SELECT count(*) FROM sqlite_schema "
"WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0]))
);
if( rc!=SQLITE_OK ){
@@ -190273,7 +220146,7 @@ static void rbuIndexCntFunc(
if( rc==SQLITE_OK ){
sqlite3_result_int(pCtx, nIndex);
}else{
- sqlite3_result_error(pCtx, sqlite3_errmsg(p->dbMain), -1);
+ sqlite3_result_error(pCtx, sqlite3_errmsg(db), -1);
}
}
@@ -190292,7 +220165,7 @@ static void rbuIndexCntFunc(
** and the cnt column the number of rows it contains.
**
** sqlite3rbu.nPhaseOneStep is initialized to the sum of (1 + nIndex) * cnt
-** for all rows in the rbu_count table, where nIndex is the number of
+** for all rows in the rbu_count table, where nIndex is the number of
** indexes on the corresponding target database table.
*/
static void rbuInitPhaseOneSteps(sqlite3rbu *p){
@@ -190302,15 +220175,15 @@ static void rbuInitPhaseOneSteps(sqlite3rbu *p){
p->nPhaseOneStep = -1;
- p->rc = sqlite3_create_function(p->dbRbu,
+ p->rc = sqlite3_create_function(p->dbRbu,
"rbu_index_cnt", 1, SQLITE_UTF8, (void*)p, rbuIndexCntFunc, 0, 0
);
-
+
/* Check for the rbu_count table. If it does not exist, or if an error
** occurs, nPhaseOneStep will be left set to -1. */
if( p->rc==SQLITE_OK ){
p->rc = prepareAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
- "SELECT 1 FROM sqlite_master WHERE tbl_name = 'rbu_count'"
+ "SELECT 1 FROM sqlite_schema WHERE tbl_name = 'rbu_count'"
);
}
if( p->rc==SQLITE_OK ){
@@ -190319,7 +220192,7 @@ static void rbuInitPhaseOneSteps(sqlite3rbu *p){
}
p->rc = sqlite3_finalize(pStmt);
}
-
+
if( p->rc==SQLITE_OK && bExists ){
p->rc = prepareAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
"SELECT sum(cnt * (1 + rbu_index_cnt(rbu_target_name(tbl))))"
@@ -190337,7 +220210,7 @@ static void rbuInitPhaseOneSteps(sqlite3rbu *p){
static sqlite3rbu *openRbuHandle(
- const char *zTarget,
+ const char *zTarget,
const char *zRbu,
const char *zState
){
@@ -190352,6 +220225,7 @@ static sqlite3rbu *openRbuHandle(
/* Create the custom VFS. */
memset(p, 0, sizeof(sqlite3rbu));
+ sqlite3rbu_rename_handler(p, 0, 0);
rbuCreateVfs(p);
/* Open the target, RBU and state databases */
@@ -190375,11 +220249,11 @@ static sqlite3rbu *openRbuHandle(
** to be a wal-mode db. But, this may have happened due to an earlier
** RBU vacuum operation leaving an old wal file in the directory.
** If this is the case, it will have been checkpointed and deleted
- ** when the handle was closed and a second attempt to open the
+ ** when the handle was closed and a second attempt to open the
** database may succeed. */
- rbuOpenDatabase(p, &bRetry);
+ rbuOpenDatabase(p, 0, &bRetry);
if( bRetry ){
- rbuOpenDatabase(p, 0);
+ rbuOpenDatabase(p, 0, 0);
}
}
@@ -190388,7 +220262,7 @@ static sqlite3rbu *openRbuHandle(
assert( pState || p->rc!=SQLITE_OK );
if( p->rc==SQLITE_OK ){
- if( pState->eStage==0 ){
+ if( pState->eStage==0 ){
rbuDeleteOalFile(p);
rbuInitPhaseOneSteps(p);
p->eStage = RBU_STAGE_OAL;
@@ -190412,15 +220286,15 @@ static sqlite3rbu *openRbuHandle(
}
}
- if( p->rc==SQLITE_OK
+ if( p->rc==SQLITE_OK
&& (p->eStage==RBU_STAGE_OAL || p->eStage==RBU_STAGE_MOVE)
&& pState->eStage!=0
){
rbu_file *pFd = (rbuIsVacuum(p) ? p->pRbuFd : p->pTargetFd);
- if( pFd->iCookie!=pState->iCookie ){
+ if( pFd->iCookie!=pState->iCookie ){
/* At this point (pTargetFd->iCookie) contains the value of the
- ** change-counter cookie (the thing that gets incremented when a
- ** transaction is committed in rollback mode) currently stored on
+ ** change-counter cookie (the thing that gets incremented when a
+ ** transaction is committed in rollback mode) currently stored on
** page 1 of the database file. */
p->rc = SQLITE_BUSY;
p->zErrmsg = sqlite3_mprintf("database modified during rbu %s",
@@ -190457,7 +220331,7 @@ static sqlite3rbu *openRbuHandle(
}
/* Check if the main database is a zipvfs db. If it is, set the upper
- ** level pager to use "journal_mode=off". This prevents it from
+ ** level pager to use "journal_mode=off". This prevents it from
** generating a large journal using a temp file. */
if( p->rc==SQLITE_OK ){
int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0);
@@ -190474,6 +220348,14 @@ static sqlite3rbu *openRbuHandle(
}else if( p->eStage==RBU_STAGE_MOVE ){
/* no-op */
}else if( p->eStage==RBU_STAGE_CKPT ){
+ if( !rbuIsVacuum(p) && rbuExclusiveCheckpoint(p->dbMain) ){
+ /* If the rbu_exclusive_checkpoint=1 URI parameter was specified
+ ** and an incremental checkpoint is being resumed, attempt an
+ ** exclusive lock on the db file. If this fails, so be it. */
+ p->eStage = RBU_STAGE_DONE;
+ rbuLockDatabase(p->dbMain);
+ p->eStage = RBU_STAGE_CKPT;
+ }
rbuSetupCheckpoint(p, pState);
}else if( p->eStage==RBU_STAGE_DONE ){
p->rc = SQLITE_DONE;
@@ -190503,15 +220385,14 @@ static sqlite3rbu *rbuMisuseError(void){
}
/*
-** Open and return a new RBU handle.
+** Open and return a new RBU handle.
*/
SQLITE_API sqlite3rbu *sqlite3rbu_open(
- const char *zTarget,
+ const char *zTarget,
const char *zRbu,
const char *zState
){
if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); }
- /* TODO: Check that zTarget and zRbu are non-NULL */
return openRbuHandle(zTarget, zRbu, zState);
}
@@ -190519,10 +220400,16 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open(
** Open a handle to begin or resume an RBU VACUUM operation.
*/
SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
- const char *zTarget,
+ const char *zTarget,
const char *zState
){
if( zTarget==0 ){ return rbuMisuseError(); }
+ if( zState ){
+ int n = strlen(zState);
+ if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){
+ return rbuMisuseError();
+ }
+ }
/* TODO: Check that both arguments are non-NULL */
return openRbuHandle(0, zTarget, zState);
}
@@ -190587,8 +220474,8 @@ SQLITE_API int sqlite3rbu_close(sqlite3rbu *p, char **pzErrmsg){
rbuObjIterFinalize(&p->objiter);
/* If this is an RBU vacuum handle and the vacuum has either finished
- ** successfully or encountered an error, delete the contents of the
- ** state table. This causes the next call to sqlite3rbu_vacuum()
+ ** successfully or encountered an error, delete the contents of the
+ ** state table. This causes the next call to sqlite3rbu_vacuum()
** specifying the current target and state databases to start a new
** vacuum from scratch. */
if( rbuIsVacuum(p) && p->rc!=SQLITE_OK && p->dbRbu ){
@@ -190621,7 +220508,7 @@ SQLITE_API int sqlite3rbu_close(sqlite3rbu *p, char **pzErrmsg){
}
/*
-** Return the total number of key-value operations (inserts, deletes or
+** Return the total number of key-value operations (inserts, deletes or
** updates) that have been performed on the target database since the
** current RBU update was started.
*/
@@ -190719,7 +220606,10 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){
if( p->eStage==RBU_STAGE_OAL ){
assert( rc!=SQLITE_DONE );
if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, 0);
- if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "BEGIN IMMEDIATE", 0, 0, 0);
+ if( rc==SQLITE_OK ){
+ const char *zBegin = rbuIsVacuum(p) ? "BEGIN" : "BEGIN IMMEDIATE";
+ rc = sqlite3_exec(p->dbRbu, zBegin, 0, 0, 0);
+ }
if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "BEGIN IMMEDIATE", 0, 0,0);
}
@@ -190727,11 +220617,59 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){
return rc;
}
+/*
+** Default xRename callback for RBU.
+*/
+static int xDefaultRename(void *pArg, const char *zOld, const char *zNew){
+ int rc = SQLITE_OK;
+#if defined(_WIN32_WCE)
+ {
+ LPWSTR zWideOld;
+ LPWSTR zWideNew;
+
+ zWideOld = rbuWinUtf8ToUnicode(zOld);
+ if( zWideOld ){
+ zWideNew = rbuWinUtf8ToUnicode(zNew);
+ if( zWideNew ){
+ if( MoveFileW(zWideOld, zWideNew) ){
+ rc = SQLITE_OK;
+ }else{
+ rc = SQLITE_IOERR;
+ }
+ sqlite3_free(zWideNew);
+ }else{
+ rc = SQLITE_IOERR_NOMEM;
+ }
+ sqlite3_free(zWideOld);
+ }else{
+ rc = SQLITE_IOERR_NOMEM;
+ }
+ }
+#else
+ rc = rename(zOld, zNew) ? SQLITE_IOERR : SQLITE_OK;
+#endif
+ return rc;
+}
+
+SQLITE_API void sqlite3rbu_rename_handler(
+ sqlite3rbu *pRbu,
+ void *pArg,
+ int (*xRename)(void *pArg, const char *zOld, const char *zNew)
+){
+ if( xRename ){
+ pRbu->xRename = xRename;
+ pRbu->pRenameArg = pArg;
+ }else{
+ pRbu->xRename = xDefaultRename;
+ pRbu->pRenameArg = 0;
+ }
+}
+
/**************************************************************************
** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour
** of a standard VFS in the following ways:
**
-** 1. Whenever the first page of a main database file is read or
+** 1. Whenever the first page of a main database file is read or
** written, the value of the change-counter cookie is stored in
** rbu_file.iCookie. Similarly, the value of the "write-version"
** database header field is stored in rbu_file.iWriteVer. This ensures
@@ -190739,15 +220677,15 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){
**
** 2. Whenever an SQLITE_OPEN_WAL file is opened, the (rbu_file.pWalFd)
** member variable of the associated database file descriptor is set
-** to point to the new file. A mutex protected linked list of all main
-** db fds opened using a particular RBU VFS is maintained at
+** to point to the new file. A mutex protected linked list of all main
+** db fds opened using a particular RBU VFS is maintained at
** rbu_vfs.pMain to facilitate this.
**
-** 3. Using a new file-control "SQLITE_FCNTL_RBU", a main db rbu_file
+** 3. Using a new file-control "SQLITE_FCNTL_RBU", a main db rbu_file
** object can be marked as the target database of an RBU update. This
** turns on the following extra special behaviour:
**
-** 3a. If xAccess() is called to check if there exists a *-wal file
+** 3a. If xAccess() is called to check if there exists a *-wal file
** associated with an RBU target database currently in RBU_STAGE_OAL
** stage (preparing the *-oal file), the following special handling
** applies:
@@ -190760,30 +220698,30 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){
**
** Then, when xOpen() is called to open the *-wal file associated with
** the RBU target in RBU_STAGE_OAL stage, instead of opening the *-wal
-** file, the rbu vfs opens the corresponding *-oal file instead.
+** file, the rbu vfs opens the corresponding *-oal file instead.
**
** 3b. The *-shm pages returned by xShmMap() for a target db file in
** RBU_STAGE_OAL mode are actually stored in heap memory. This is to
** avoid creating a *-shm file on disk. Additionally, xShmLock() calls
** are no-ops on target database files in RBU_STAGE_OAL mode. This is
-** because assert() statements in some VFS implementations fail if
+** because assert() statements in some VFS implementations fail if
** xShmLock() is called before xShmMap().
**
** 3c. If an EXCLUSIVE lock is attempted on a target database file in any
-** mode except RBU_STAGE_DONE (all work completed and checkpointed), it
+** mode except RBU_STAGE_DONE (all work completed and checkpointed), it
** fails with an SQLITE_BUSY error. This is to stop RBU connections
** from automatically checkpointing a *-wal (or *-oal) file from within
** sqlite3_close().
**
** 3d. In RBU_STAGE_CAPTURE mode, all xRead() calls on the wal file, and
-** all xWrite() calls on the target database file perform no IO.
+** all xWrite() calls on the target database file perform no IO.
** Instead the frame and page numbers that would be read and written
** are recorded. Additionally, successful attempts to obtain exclusive
-** xShmLock() WRITER, CHECKPOINTER and READ0 locks on the target
+** xShmLock() WRITER, CHECKPOINTER and READ0 locks on the target
** database file are recorded. xShmLock() calls to unlock the same
** locks are no-ops (so that once obtained, these locks are never
** relinquished). Finally, calls to xSync() on the target database
-** file fail with SQLITE_INTERNAL errors.
+** file fail with SQLITE_NOTICE errors.
*/
static void rbuUnlockShm(rbu_file *p){
@@ -190855,7 +220793,7 @@ static void rbuMainlistRemove(rbu_file *p){
}
/*
-** Given that zWal points to a buffer containing a wal file name passed to
+** Given that zWal points to a buffer containing a wal file name passed to
** either the xOpen() or xAccess() VFS method, search the main-db list for
** a file-handle opened by the same database connection on the corresponding
** database file.
@@ -190892,9 +220830,12 @@ static int rbuVfsClose(sqlite3_file *pFile){
sqlite3_free(p->zDel);
if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
+ const sqlite3_io_methods *pMeth = p->pReal->pMethods;
rbuMainlistRemove(p);
rbuUnlockShm(p);
- p->pReal->pMethods->xShmUnmap(p->pReal, 0);
+ if( pMeth->iVersion>1 && pMeth->xShmUnmap ){
+ pMeth->xShmUnmap(p->pReal, 0);
+ }
}
else if( (p->openFlags & SQLITE_OPEN_DELETEONCLOSE) && p->pRbu ){
rbuUpdateTempSize(p, 0);
@@ -190908,7 +220849,7 @@ static int rbuVfsClose(sqlite3_file *pFile){
/*
-** Read and return an unsigned 32-bit big-endian integer from the buffer
+** Read and return an unsigned 32-bit big-endian integer from the buffer
** passed as the only argument.
*/
static u32 rbuGetU32(u8 *aBuf){
@@ -190938,9 +220879,9 @@ static void rbuPutU16(u8 *aBuf, u16 iVal){
** Read data from an rbuVfs-file.
*/
static int rbuVfsRead(
- sqlite3_file *pFile,
- void *zBuf,
- int iAmt,
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
sqlite_int64 iOfst
){
rbu_file *p = (rbu_file*)pFile;
@@ -190951,20 +220892,20 @@ static int rbuVfsRead(
assert( p->openFlags & SQLITE_OPEN_WAL );
rc = rbuCaptureWalRead(p->pRbu, iOfst, iAmt);
}else{
- if( pRbu && pRbu->eStage==RBU_STAGE_OAL
- && (p->openFlags & SQLITE_OPEN_WAL)
- && iOfst>=pRbu->iOalSz
+ if( pRbu && pRbu->eStage==RBU_STAGE_OAL
+ && (p->openFlags & SQLITE_OPEN_WAL)
+ && iOfst>=pRbu->iOalSz
){
rc = SQLITE_OK;
memset(zBuf, 0, iAmt);
}else{
rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
#if 1
- /* If this is being called to read the first page of the target
- ** database as part of an rbu vacuum operation, synthesize the
+ /* If this is being called to read the first page of the target
+ ** database as part of an rbu vacuum operation, synthesize the
** contents of the first page if it does not yet exist. Otherwise,
** SQLite will not check for a *-wal file. */
- if( pRbu && rbuIsVacuum(pRbu)
+ if( pRbu && rbuIsVacuum(pRbu)
&& rc==SQLITE_IOERR_SHORT_READ && iOfst==0
&& (p->openFlags & SQLITE_OPEN_MAIN_DB)
&& pRbu->rc==SQLITE_OK
@@ -191004,9 +220945,9 @@ static int rbuVfsRead(
** Write data to an rbuVfs-file.
*/
static int rbuVfsWrite(
- sqlite3_file *pFile,
- const void *zBuf,
- int iAmt,
+ sqlite3_file *pFile,
+ const void *zBuf,
+ int iAmt,
sqlite_int64 iOfst
){
rbu_file *p = (rbu_file*)pFile;
@@ -191018,8 +220959,8 @@ static int rbuVfsWrite(
rc = rbuCaptureDbWrite(p->pRbu, iOfst);
}else{
if( pRbu ){
- if( pRbu->eStage==RBU_STAGE_OAL
- && (p->openFlags & SQLITE_OPEN_WAL)
+ if( pRbu->eStage==RBU_STAGE_OAL
+ && (p->openFlags & SQLITE_OPEN_WAL)
&& iOfst>=pRbu->iOalSz
){
pRbu->iOalSz = iAmt + iOfst;
@@ -191062,7 +221003,7 @@ static int rbuVfsSync(sqlite3_file *pFile, int flags){
rbu_file *p = (rbu_file *)pFile;
if( p->pRbu && p->pRbu->eStage==RBU_STAGE_CAPTURE ){
if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
- return SQLITE_INTERNAL;
+ return SQLITE_NOTICE_RBU;
}
return SQLITE_OK;
}
@@ -191079,10 +221020,10 @@ static int rbuVfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
/* If this is an RBU vacuum operation and this is the target database,
** pretend that it has at least one page. Otherwise, SQLite will not
- ** check for the existance of a *-wal file. rbuVfsRead() contains
+ ** check for the existance of a *-wal file. rbuVfsRead() contains
** similar logic. */
- if( rc==SQLITE_OK && *pSize==0
- && p->pRbu && rbuIsVacuum(p->pRbu)
+ if( rc==SQLITE_OK && *pSize==0
+ && p->pRbu && rbuIsVacuum(p->pRbu)
&& (p->openFlags & SQLITE_OPEN_MAIN_DB)
){
*pSize = 1024;
@@ -191099,10 +221040,10 @@ static int rbuVfsLock(sqlite3_file *pFile, int eLock){
int rc = SQLITE_OK;
assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
- if( eLock==SQLITE_LOCK_EXCLUSIVE
+ if( eLock==SQLITE_LOCK_EXCLUSIVE
&& (p->bNolock || (pRbu && pRbu->eStage!=RBU_STAGE_DONE))
){
- /* Do not allow EXCLUSIVE locks. Preventing SQLite from taking this
+ /* Do not allow EXCLUSIVE locks. Preventing SQLite from taking this
** prevents it from checkpointing the database from sqlite3_close(). */
rc = SQLITE_BUSY;
}else{
@@ -191158,9 +221099,7 @@ static int rbuVfsFileControl(sqlite3_file *pFile, int op, void *pArg){
}else if( rc==SQLITE_NOTFOUND ){
pRbu->pTargetFd = p;
p->pRbu = pRbu;
- if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
- rbuMainlistAdd(p);
- }
+ rbuMainlistAdd(p);
if( p->pWalFd ) p->pWalFd->pRbu = pRbu;
rc = SQLITE_OK;
}
@@ -191215,25 +221154,24 @@ static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
#endif
assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
- if( pRbu && (pRbu->eStage==RBU_STAGE_OAL || pRbu->eStage==RBU_STAGE_MOVE) ){
- /* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from
- ** taking this lock also prevents any checkpoints from occurring.
- ** todo: really, it's not clear why this might occur, as
- ** wal_autocheckpoint ought to be turned off. */
+ if( pRbu && (
+ pRbu->eStage==RBU_STAGE_OAL
+ || pRbu->eStage==RBU_STAGE_MOVE
+ || pRbu->eStage==RBU_STAGE_DONE
+ )){
+ /* Prevent SQLite from taking a shm-lock on the target file when it
+ ** is supplying heap memory to the upper layer in place of *-shm
+ ** segments. */
if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY;
}else{
int bCapture = 0;
- if( n==1 && (flags & SQLITE_SHM_EXCLUSIVE)
- && pRbu && pRbu->eStage==RBU_STAGE_CAPTURE
- && (ofst==WAL_LOCK_WRITE || ofst==WAL_LOCK_CKPT || ofst==WAL_LOCK_READ0)
- ){
+ if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){
bCapture = 1;
}
-
if( bCapture==0 || 0==(flags & SQLITE_SHM_UNLOCK) ){
rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
if( bCapture && rc==SQLITE_OK ){
- pRbu->mLock |= (1 << ofst);
+ pRbu->mLock |= ((1<<n) - 1) << ofst;
}
}
}
@@ -191245,10 +221183,10 @@ static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
** Obtain a pointer to a mapping of a single 32KiB page of the *-shm file.
*/
static int rbuVfsShmMap(
- sqlite3_file *pFile,
- int iRegion,
- int szRegion,
- int isWrite,
+ sqlite3_file *pFile,
+ int iRegion,
+ int szRegion,
+ int isWrite,
void volatile **pp
){
rbu_file *p = (rbu_file*)pFile;
@@ -191256,23 +221194,27 @@ static int rbuVfsShmMap(
int eStage = (p->pRbu ? p->pRbu->eStage : 0);
/* If not in RBU_STAGE_OAL, allow this call to pass through. Or, if this
- ** rbu is in the RBU_STAGE_OAL state, use heap memory for *-shm space
+ ** rbu is in the RBU_STAGE_OAL state, use heap memory for *-shm space
** instead of a file on disk. */
assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
- if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){
- if( iRegion<=p->nShm ){
- int nByte = (iRegion+1) * sizeof(char*);
- char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte);
- if( apNew==0 ){
- rc = SQLITE_NOMEM;
- }else{
- memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm));
- p->apShm = apNew;
- p->nShm = iRegion+1;
- }
+ if( eStage==RBU_STAGE_OAL ){
+ sqlite3_int64 nByte = (iRegion+1) * sizeof(char*);
+ char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte);
+
+ /* This is an RBU connection that uses its own heap memory for the
+ ** pages of the *-shm file. Since no other process can have run
+ ** recovery, the connection must request *-shm pages in order
+ ** from start to finish. */
+ assert( iRegion==p->nShm );
+ if( apNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm));
+ p->apShm = apNew;
+ p->nShm = iRegion+1;
}
- if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){
+ if( rc==SQLITE_OK ){
char *pNew = (char*)sqlite3_malloc64(szRegion);
if( pNew==0 ){
rc = SQLITE_NOMEM;
@@ -191322,33 +221264,6 @@ static int rbuVfsShmUnmap(sqlite3_file *pFile, int delFlag){
return rc;
}
-/*
-** A main database named zName has just been opened. The following
-** function returns a pointer to a buffer owned by SQLite that contains
-** the name of the *-wal file this db connection will use. SQLite
-** happens to pass a pointer to this buffer when using xAccess()
-** or xOpen() to operate on the *-wal file.
-*/
-static const char *rbuMainToWal(const char *zName, int flags){
- int n = (int)strlen(zName);
- const char *z = &zName[n];
- if( flags & SQLITE_OPEN_URI ){
- int odd = 0;
- while( 1 ){
- if( z[0]==0 ){
- odd = 1 - odd;
- if( odd && z[1]==0 ) break;
- }
- z++;
- }
- z += 2;
- }else{
- while( *z==0 ) z++;
- }
- z += (n + 8 + 1);
- return z;
-}
-
/*
** Open an rbu file handle.
*/
@@ -191379,6 +221294,25 @@ static int rbuVfsOpen(
rbuVfsShmUnmap, /* xShmUnmap */
0, 0 /* xFetch, xUnfetch */
};
+ static sqlite3_io_methods rbuvfs_io_methods1 = {
+ 1, /* iVersion */
+ rbuVfsClose, /* xClose */
+ rbuVfsRead, /* xRead */
+ rbuVfsWrite, /* xWrite */
+ rbuVfsTruncate, /* xTruncate */
+ rbuVfsSync, /* xSync */
+ rbuVfsFileSize, /* xFileSize */
+ rbuVfsLock, /* xLock */
+ rbuVfsUnlock, /* xUnlock */
+ rbuVfsCheckReservedLock, /* xCheckReservedLock */
+ rbuVfsFileControl, /* xFileControl */
+ rbuVfsSectorSize, /* xSectorSize */
+ rbuVfsDeviceCharacteristics, /* xDeviceCharacteristics */
+ 0, 0, 0, 0, 0, 0
+ };
+
+
+
rbu_vfs *pRbuVfs = (rbu_vfs*)pVfs;
sqlite3_vfs *pRealVfs = pRbuVfs->pRealVfs;
rbu_file *pFd = (rbu_file *)pFile;
@@ -191397,34 +221331,20 @@ static int rbuVfsOpen(
** the name of the *-wal file this db connection will use. SQLite
** happens to pass a pointer to this buffer when using xAccess()
** or xOpen() to operate on the *-wal file. */
- pFd->zWal = rbuMainToWal(zName, flags);
+ pFd->zWal = sqlite3_filename_wal(zName);
}
else if( flags & SQLITE_OPEN_WAL ){
rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName, 0);
if( pDb ){
if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
- /* This call is to open a *-wal file. Intead, open the *-oal. This
- ** code ensures that the string passed to xOpen() is terminated by a
- ** pair of '\0' bytes in case the VFS attempts to extract a URI
- ** parameter from it. */
- const char *zBase = zName;
- size_t nCopy;
- char *zCopy;
+ /* This call is to open a *-wal file. Intead, open the *-oal. */
+ size_t nOpen;
if( rbuIsVacuum(pDb->pRbu) ){
- zBase = sqlite3_db_filename(pDb->pRbu->dbRbu, "main");
- zBase = rbuMainToWal(zBase, SQLITE_OPEN_URI);
- }
- nCopy = strlen(zBase);
- zCopy = sqlite3_malloc64(nCopy+2);
- if( zCopy ){
- memcpy(zCopy, zBase, nCopy);
- zCopy[nCopy-3] = 'o';
- zCopy[nCopy] = '\0';
- zCopy[nCopy+1] = '\0';
- zOpen = (const char*)(pFd->zDel = zCopy);
- }else{
- rc = SQLITE_NOMEM;
+ zOpen = sqlite3_db_filename(pDb->pRbu->dbRbu, "main");
+ zOpen = sqlite3_filename_wal(zOpen);
}
+ nOpen = strlen(zOpen);
+ ((char*)zOpen)[nOpen-3] = 'o';
pFd->pRbu = pDb->pRbu;
}
pDb->pWalFd = pFd;
@@ -191434,8 +221354,8 @@ static int rbuVfsOpen(
pFd->pRbu = pRbuVfs->pRbu;
}
- if( oflags & SQLITE_OPEN_MAIN_DB
- && sqlite3_uri_boolean(zName, "rbu_memory", 0)
+ if( oflags & SQLITE_OPEN_MAIN_DB
+ && sqlite3_uri_boolean(zName, "rbu_memory", 0)
){
assert( oflags & SQLITE_OPEN_MAIN_DB );
oflags = SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
@@ -191447,10 +221367,15 @@ static int rbuVfsOpen(
rc = pRealVfs->xOpen(pRealVfs, zOpen, pFd->pReal, oflags, pOutFlags);
}
if( pFd->pReal->pMethods ){
+ const sqlite3_io_methods *pMeth = pFd->pReal->pMethods;
/* The xOpen() operation has succeeded. Set the sqlite3_file.pMethods
** pointer and, if the file is a main database file, link it into the
** mutex protected linked list of all such files. */
- pFile->pMethods = &rbuvfs_io_methods;
+ if( pMeth->iVersion<2 || pMeth->xShmLock==0 ){
+ pFile->pMethods = &rbuvfs_io_methods1;
+ }else{
+ pFile->pMethods = &rbuvfs_io_methods;
+ }
if( flags & SQLITE_OPEN_MAIN_DB ){
rbuMainlistAdd(pFd);
}
@@ -191474,9 +221399,9 @@ static int rbuVfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
** is available, or false otherwise.
*/
static int rbuVfsAccess(
- sqlite3_vfs *pVfs,
- const char *zPath,
- int flags,
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
int *pResOut
){
rbu_vfs *pRbuVfs = (rbu_vfs*)pVfs;
@@ -191492,7 +221417,7 @@ static int rbuVfsAccess(
** a) if the *-wal file does exist, return SQLITE_CANTOPEN. This
** ensures that the RBU extension never tries to update a database
** in wal mode, even if the first page of the database file has
- ** been damaged.
+ ** been damaged.
**
** b) if the *-wal file does not exist, claim that it does anyway,
** causing SQLite to call xOpen() to open it. This call will also
@@ -191501,7 +221426,8 @@ static int rbuVfsAccess(
*/
if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS ){
rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath, 1);
- if( pDb && pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
+ if( pDb && pDb->pRbu->eStage==RBU_STAGE_OAL ){
+ assert( pDb->pRbu );
if( *pResOut ){
rc = SQLITE_CANTOPEN;
}else{
@@ -191521,9 +221447,9 @@ static int rbuVfsAccess(
** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
*/
static int rbuVfsFullPathname(
- sqlite3_vfs *pVfs,
- const char *zPath,
- int nOut,
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
char *zOut
){
sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
@@ -191541,7 +221467,7 @@ static void *rbuVfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
/*
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
-** utf-8 string describing the most recent error encountered associated
+** utf-8 string describing the most recent error encountered associated
** with dynamic libraries.
*/
static void rbuVfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
@@ -191553,8 +221479,8 @@ static void rbuVfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
*/
static void (*rbuVfsDlSym(
- sqlite3_vfs *pVfs,
- void *pArg,
+ sqlite3_vfs *pVfs,
+ void *pArg,
const char *zSym
))(void){
sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
@@ -191571,7 +221497,7 @@ static void rbuVfsDlClose(sqlite3_vfs *pVfs, void *pHandle){
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
/*
-** Populate the buffer pointed to by zBufOut with nByte bytes of
+** Populate the buffer pointed to by zBufOut with nByte bytes of
** random data.
*/
static int rbuVfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
@@ -191580,7 +221506,7 @@ static int rbuVfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
}
/*
-** Sleep for nMicro microseconds. Return the number of microseconds
+** Sleep for nMicro microseconds. Return the number of microseconds
** actually slept.
*/
static int rbuVfsSleep(sqlite3_vfs *pVfs, int nMicro){
@@ -191731,7 +221657,7 @@ SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu *pRbu){
**
** This file contains an implementation of the "dbstat" virtual table.
**
-** The dbstat virtual table is used to extract low-level formatting
+** The dbstat virtual table is used to extract low-level storage
** information from an SQLite database in order to implement the
** "sqlite3_analyzer" utility. See the ../tool/spaceanal.tcl script
** for an example implementation.
@@ -191745,23 +221671,32 @@ SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu *pRbu){
&& !defined(SQLITE_OMIT_VIRTUALTABLE)
/*
+** The pager and btree modules arrange objects in memory so that there are
+** always approximately 200 bytes of addressable memory following each page
+** buffer. This way small buffer overreads caused by corrupt database pages
+** do not cause undefined behaviour. This module pads each page buffer
+** by the following number of bytes for the same purpose.
+*/
+#define DBSTAT_PAGE_PADDING_BYTES 256
+
+/*
** Page paths:
-**
-** The value of the 'path' column describes the path taken from the
-** root-node of the b-tree structure to each page. The value of the
+**
+** The value of the 'path' column describes the path taken from the
+** root-node of the b-tree structure to each page. The value of the
** root-node path is '/'.
**
** The value of the path for the left-most child page of the root of
** a b-tree is '/000/'. (Btrees store content ordered from left to right
** so the pages to the left have smaller keys than the pages to the right.)
** The next to left-most child of the root page is
-** '/001', and so on, each sibling page identified by a 3-digit hex
+** '/001', and so on, each sibling page identified by a 3-digit hex
** value. The children of the 451st left-most sibling have paths such
** as '/1c2/000/, '/1c2/001/' etc.
**
-** Overflow pages are specified by appending a '+' character and a
+** Overflow pages are specified by appending a '+' character and a
** six-digit hexadecimal value to the path to the cell they are linked
-** from. For example, the three overflow pages in a chain linked from
+** from. For example, the three overflow pages in a chain linked from
** the left-most cell of the 450th child of the root page are identified
** by the paths:
**
@@ -191775,27 +221710,30 @@ SQLITE_API sqlite3_int64 sqlite3rbu_temp_size(sqlite3rbu *pRbu){
**
** '/1c2/000/' // Left-most child of 451st child of root
*/
-#define VTAB_SCHEMA \
- "CREATE TABLE xx( " \
- " name TEXT, /* Name of table or index */" \
- " path TEXT, /* Path to page from root */" \
- " pageno INTEGER, /* Page number */" \
- " pagetype TEXT, /* 'internal', 'leaf' or 'overflow' */" \
- " ncell INTEGER, /* Cells on page (0 for overflow) */" \
- " payload INTEGER, /* Bytes of payload on this page */" \
- " unused INTEGER, /* Bytes of unused space on this page */" \
- " mx_payload INTEGER, /* Largest payload size of all cells */" \
- " pgoffset INTEGER, /* Offset of page in file */" \
- " pgsize INTEGER, /* Size of the page */" \
- " schema TEXT HIDDEN /* Database schema being analyzed */" \
- ");"
-
-
+static const char zDbstatSchema[] =
+ "CREATE TABLE x("
+ " name TEXT," /* 0 Name of table or index */
+ " path TEXT," /* 1 Path to page from root (NULL for agg) */
+ " pageno INTEGER," /* 2 Page number (page count for aggregates) */
+ " pagetype TEXT," /* 3 'internal', 'leaf', 'overflow', or NULL */
+ " ncell INTEGER," /* 4 Cells on page (0 for overflow) */
+ " payload INTEGER," /* 5 Bytes of payload on this page */
+ " unused INTEGER," /* 6 Bytes of unused space on this page */
+ " mx_payload INTEGER," /* 7 Largest payload size of all cells */
+ " pgoffset INTEGER," /* 8 Offset of page in file (NULL for agg) */
+ " pgsize INTEGER," /* 9 Size of the page (sum for aggregate) */
+ " schema TEXT HIDDEN," /* 10 Database schema being analyzed */
+ " aggregate BOOLEAN HIDDEN" /* 11 aggregate info for each table */
+ ")"
+;
+
+/* Forward reference to data structured used in this module */
typedef struct StatTable StatTable;
typedef struct StatCursor StatCursor;
typedef struct StatPage StatPage;
typedef struct StatCell StatCell;
+/* Size information for a single cell within a btree page */
struct StatCell {
int nLocal; /* Bytes of local payload */
u32 iChildPg; /* Child node (or 0 if this is a leaf) */
@@ -191805,11 +221743,11 @@ struct StatCell {
int iOvfl; /* Iterates through aOvfl[] */
};
+/* Size information for a single btree page */
struct StatPage {
- u32 iPgno;
- DbPage *pPg;
- int iCell;
-
+ u32 iPgno; /* Page number */
+ u8 *aPg; /* Page buffer from sqlite3_malloc() */
+ int iCell; /* Current cell */
char *zPath; /* Path to this page */
/* Variables populated by statDecodePage(): */
@@ -191818,34 +221756,38 @@ struct StatPage {
int nUnused; /* Number of unused bytes on page */
StatCell *aCell; /* Array of parsed cells */
u32 iRightChildPg; /* Right-child page number (or 0) */
- int nMxPayload; /* Largest payload of any cell on this page */
+ int nMxPayload; /* Largest payload of any cell on the page */
};
+/* The cursor for scanning the dbstat virtual table */
struct StatCursor {
- sqlite3_vtab_cursor base;
+ sqlite3_vtab_cursor base; /* base class. MUST BE FIRST! */
sqlite3_stmt *pStmt; /* Iterates through set of root pages */
- int isEof; /* After pStmt has returned SQLITE_DONE */
+ u8 isEof; /* After pStmt has returned SQLITE_DONE */
+ u8 isAgg; /* Aggregate results for each table */
int iDb; /* Schema used for this query */
- StatPage aPage[32];
+ StatPage aPage[32]; /* Pages in path to current page */
int iPage; /* Current entry in aPage[] */
/* Values to return. */
+ u32 iPageno; /* Value of 'pageno' column */
char *zName; /* Value of 'name' column */
char *zPath; /* Value of 'path' column */
- u32 iPageno; /* Value of 'pageno' column */
char *zPagetype; /* Value of 'pagetype' column */
+ int nPage; /* Number of pages in current btree */
int nCell; /* Value of 'ncell' column */
- int nPayload; /* Value of 'payload' column */
- int nUnused; /* Value of 'unused' column */
int nMxPayload; /* Value of 'mx_payload' column */
+ i64 nUnused; /* Value of 'unused' column */
+ i64 nPayload; /* Value of 'payload' column */
i64 iOffset; /* Value of 'pgOffset' column */
- int szPage; /* Value of 'pgSize' column */
+ i64 szPage; /* Value of 'pgSize' column */
};
+/* An instance of the DBSTAT virtual table */
struct StatTable {
- sqlite3_vtab base;
- sqlite3 *db;
+ sqlite3_vtab base; /* base class. MUST BE FIRST! */
+ sqlite3 *db; /* Database connection that owns this vtab */
int iDb; /* Index of database to analyze */
};
@@ -191854,7 +221796,7 @@ struct StatTable {
#endif
/*
-** Connect to or create a statvfs virtual table.
+** Connect to or create a new DBSTAT virtual table.
*/
static int statConnect(
sqlite3 *db,
@@ -191866,6 +221808,7 @@ static int statConnect(
StatTable *pTab = 0;
int rc = SQLITE_OK;
int iDb;
+ (void)pAux;
if( argc>=4 ){
Token nm;
@@ -191878,7 +221821,8 @@ static int statConnect(
}else{
iDb = 0;
}
- rc = sqlite3_declare_vtab(db, VTAB_SCHEMA);
+ sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
+ rc = sqlite3_declare_vtab(db, zDbstatSchema);
if( rc==SQLITE_OK ){
pTab = (StatTable *)sqlite3_malloc64(sizeof(StatTable));
if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
@@ -191896,7 +221840,7 @@ static int statConnect(
}
/*
-** Disconnect from or destroy a statvfs virtual table.
+** Disconnect from or destroy the DBSTAT virtual table.
*/
static int statDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
@@ -191904,14 +221848,21 @@ static int statDisconnect(sqlite3_vtab *pVtab){
}
/*
-** There is no "best-index". This virtual table always does a linear
-** scan. However, a schema=? constraint should cause this table to
-** operate on a different database schema, so check for it.
+** Compute the best query strategy and return the result in idxNum.
**
-** idxNum is normally 0, but will be 1 if a schema=? constraint exists.
+** idxNum-Bit Meaning
+** ---------- ----------------------------------------------
+** 0x01 There is a schema=? term in the WHERE clause
+** 0x02 There is a name=? term in the WHERE clause
+** 0x04 There is an aggregate=? term in the WHERE clause
+** 0x08 Output should be ordered by name and path
*/
static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int i;
+ int iSchema = -1;
+ int iName = -1;
+ int iAgg = -1;
+ (void)tab;
/* Look for a valid schema=? constraint. If found, change the idxNum to
** 1 and request the value of that constraint be sent to xFilter. And
@@ -191919,19 +221870,44 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
** used.
*/
for(i=0; i<pIdxInfo->nConstraint; i++){
- if( pIdxInfo->aConstraint[i].iColumn!=10 ) continue;
- if( pIdxInfo->aConstraint[i].usable==0 ) return SQLITE_CONSTRAINT;
if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
- pIdxInfo->idxNum = 1;
- pIdxInfo->estimatedCost = 1.0;
- pIdxInfo->aConstraintUsage[i].argvIndex = 1;
- pIdxInfo->aConstraintUsage[i].omit = 1;
- break;
+ if( pIdxInfo->aConstraint[i].usable==0 ){
+ /* Force DBSTAT table should always be the right-most table in a join */
+ return SQLITE_CONSTRAINT;
+ }
+ switch( pIdxInfo->aConstraint[i].iColumn ){
+ case 0: { /* name */
+ iName = i;
+ break;
+ }
+ case 10: { /* schema */
+ iSchema = i;
+ break;
+ }
+ case 11: { /* aggregate */
+ iAgg = i;
+ break;
+ }
+ }
}
+ i = 0;
+ if( iSchema>=0 ){
+ pIdxInfo->aConstraintUsage[iSchema].argvIndex = ++i;
+ pIdxInfo->aConstraintUsage[iSchema].omit = 1;
+ pIdxInfo->idxNum |= 0x01;
+ }
+ if( iName>=0 ){
+ pIdxInfo->aConstraintUsage[iName].argvIndex = ++i;
+ pIdxInfo->idxNum |= 0x02;
+ }
+ if( iAgg>=0 ){
+ pIdxInfo->aConstraintUsage[iAgg].argvIndex = ++i;
+ pIdxInfo->idxNum |= 0x04;
+ }
+ pIdxInfo->estimatedCost = 1.0;
-
- /* Records are always returned in ascending order of (name, path).
- ** If this will satisfy the client, set the orderByConsumed flag so that
+ /* Records are always returned in ascending order of (name, path).
+ ** If this will satisfy the client, set the orderByConsumed flag so that
** SQLite does not do an external sort.
*/
if( ( pIdxInfo->nOrderBy==1
@@ -191946,13 +221922,14 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
)
){
pIdxInfo->orderByConsumed = 1;
+ pIdxInfo->idxNum |= 0x08;
}
return SQLITE_OK;
}
/*
-** Open a new statvfs cursor.
+** Open a new DBSTAT cursor.
*/
static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
StatTable *pTab = (StatTable *)pVTab;
@@ -191984,26 +221961,43 @@ static void statClearCells(StatPage *p){
}
static void statClearPage(StatPage *p){
+ u8 *aPg = p->aPg;
statClearCells(p);
- sqlite3PagerUnref(p->pPg);
sqlite3_free(p->zPath);
memset(p, 0, sizeof(StatPage));
+ p->aPg = aPg;
}
static void statResetCsr(StatCursor *pCsr){
int i;
- sqlite3_reset(pCsr->pStmt);
+ /* In some circumstances, specifically if an OOM has occurred, the call
+ ** to sqlite3_reset() may cause the pager to be reset (emptied). It is
+ ** important that statClearPage() is called to free any page refs before
+ ** this happens. dbsqlfuzz 9ed3e4e3816219d3509d711636c38542bf3f40b1. */
for(i=0; i<ArraySize(pCsr->aPage); i++){
statClearPage(&pCsr->aPage[i]);
+ sqlite3_free(pCsr->aPage[i].aPg);
+ pCsr->aPage[i].aPg = 0;
}
+ sqlite3_reset(pCsr->pStmt);
pCsr->iPage = 0;
sqlite3_free(pCsr->zPath);
pCsr->zPath = 0;
pCsr->isEof = 0;
}
+/* Resize the space-used counters inside of the cursor */
+static void statResetCounts(StatCursor *pCsr){
+ pCsr->nCell = 0;
+ pCsr->nMxPayload = 0;
+ pCsr->nUnused = 0;
+ pCsr->nPayload = 0;
+ pCsr->szPage = 0;
+ pCsr->nPage = 0;
+}
+
/*
-** Close a statvfs cursor.
+** Close a DBSTAT cursor.
*/
static int statClose(sqlite3_vtab_cursor *pCursor){
StatCursor *pCsr = (StatCursor *)pCursor;
@@ -192013,16 +222007,20 @@ static int statClose(sqlite3_vtab_cursor *pCursor){
return SQLITE_OK;
}
-static void getLocalPayload(
+/*
+** For a single cell on a btree page, compute the number of bytes of
+** content (payload) stored on that page. That is to say, compute the
+** number of bytes of content not found on overflow pages.
+*/
+static int getLocalPayload(
int nUsable, /* Usable bytes per page */
u8 flags, /* Page flags */
- int nTotal, /* Total record (payload) size */
- int *pnLocal /* OUT: Bytes stored locally */
+ int nTotal /* Total record (payload) size */
){
int nLocal;
int nMinLocal;
int nMaxLocal;
-
+
if( flags==0x0D ){ /* Table leaf node */
nMinLocal = (nUsable - 12) * 32 / 255 - 23;
nMaxLocal = nUsable - 35;
@@ -192033,9 +222031,12 @@ static void getLocalPayload(
nLocal = nMinLocal + (nTotal - nMinLocal) % (nUsable - 4);
if( nLocal>nMaxLocal ) nLocal = nMinLocal;
- *pnLocal = nLocal;
+ return nLocal;
}
+/* Populate the StatPage object with information about the all
+** cells found on the page currently under analysis.
+*/
static int statDecodePage(Btree *pBt, StatPage *p){
int nUnused;
int iOff;
@@ -192043,7 +222044,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
int isLeaf;
int szPage;
- u8 *aData = sqlite3PagerGetData(p->pPg);
+ u8 *aData = p->aPg;
u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
p->flags = aHdr[0];
@@ -192106,7 +222107,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
iOff += sqlite3GetVarint(&aData[iOff], &dummy);
}
if( nPayload>(u32)p->nMxPayload ) p->nMxPayload = nPayload;
- getLocalPayload(nUsable, p->flags, nPayload, &nLocal);
+ nLocal = getLocalPayload(nUsable, p->flags, nPayload);
if( nLocal<0 ) goto statPageIsCorrupt;
pCell->nLocal = nLocal;
assert( nPayload>=(u32)nLocal );
@@ -192114,7 +222115,9 @@ static int statDecodePage(Btree *pBt, StatPage *p){
if( nPayload>(u32)nLocal ){
int j;
int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
- if( iOff+nLocal>nUsable ) goto statPageIsCorrupt;
+ if( iOff+nLocal+4>nUsable || nPayload>0x7fffffff ){
+ goto statPageIsCorrupt;
+ }
pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
pCell->nOvfl = nOvfl;
pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl);
@@ -192128,7 +222131,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
if( rc!=SQLITE_OK ){
assert( pPg==0 );
return rc;
- }
+ }
pCell->aOvfl[j] = sqlite3Get4byte(sqlite3PagerGetData(pPg));
sqlite3PagerUnref(pPg);
}
@@ -192156,23 +222159,57 @@ static void statSizeAndOffset(StatCursor *pCsr){
sqlite3_file *fd;
sqlite3_int64 x[2];
- /* The default page size and offset */
- pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
- pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1);
-
- /* If connected to a ZIPVFS backend, override the page size and
- ** offset with actual values obtained from ZIPVFS.
+ /* If connected to a ZIPVFS backend, find the page size and
+ ** offset from ZIPVFS.
*/
fd = sqlite3PagerFile(pPager);
x[0] = pCsr->iPageno;
if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
pCsr->iOffset = x[0];
- pCsr->szPage = (int)x[1];
+ pCsr->szPage += x[1];
+ }else{
+ /* Not ZIPVFS: The default page size and offset */
+ pCsr->szPage += sqlite3BtreeGetPageSize(pBt);
+ pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1);
+ }
+}
+
+/*
+** Load a copy of the page data for page iPg into the buffer belonging
+** to page object pPg. Allocate the buffer if necessary. Return SQLITE_OK
+** if successful, or an SQLite error code otherwise.
+*/
+static int statGetPage(
+ Btree *pBt, /* Load page from this b-tree */
+ u32 iPg, /* Page number to load */
+ StatPage *pPg /* Load page into this object */
+){
+ int pgsz = sqlite3BtreeGetPageSize(pBt);
+ DbPage *pDbPage = 0;
+ int rc;
+
+ if( pPg->aPg==0 ){
+ pPg->aPg = (u8*)sqlite3_malloc(pgsz + DBSTAT_PAGE_PADDING_BYTES);
+ if( pPg->aPg==0 ){
+ return SQLITE_NOMEM_BKPT;
+ }
+ memset(&pPg->aPg[pgsz], 0, DBSTAT_PAGE_PADDING_BYTES);
+ }
+
+ rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPg, &pDbPage, 0);
+ if( rc==SQLITE_OK ){
+ const u8 *a = sqlite3PagerGetData(pDbPage);
+ memcpy(pPg->aPg, a, pgsz);
+ sqlite3PagerUnref(pDbPage);
}
+
+ return rc;
}
/*
-** Move a statvfs cursor to the next entry in the file.
+** Move a DBSTAT cursor to the next entry. Normally, the next
+** entry will be the next page, but in aggregated mode (pCsr->isAgg!=0),
+** the next entry is the next btree.
*/
static int statNext(sqlite3_vtab_cursor *pCursor){
int rc;
@@ -192187,7 +222224,9 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
pCsr->zPath = 0;
statNextRestart:
- if( pCsr->aPage[0].pPg==0 ){
+ if( pCsr->iPage<0 ){
+ /* Start measuring space on the next btree */
+ statResetCounts(pCsr);
rc = sqlite3_step(pCsr->pStmt);
if( rc==SQLITE_ROW ){
int nPage;
@@ -192197,47 +222236,50 @@ statNextRestart:
pCsr->isEof = 1;
return sqlite3_reset(pCsr->pStmt);
}
- rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0);
+ rc = statGetPage(pBt, iRoot, &pCsr->aPage[0]);
pCsr->aPage[0].iPgno = iRoot;
pCsr->aPage[0].iCell = 0;
- pCsr->aPage[0].zPath = z = sqlite3_mprintf("/");
+ if( !pCsr->isAgg ){
+ pCsr->aPage[0].zPath = z = sqlite3_mprintf("/");
+ if( z==0 ) rc = SQLITE_NOMEM_BKPT;
+ }
pCsr->iPage = 0;
- if( z==0 ) rc = SQLITE_NOMEM_BKPT;
+ pCsr->nPage = 1;
}else{
pCsr->isEof = 1;
return sqlite3_reset(pCsr->pStmt);
}
}else{
-
- /* Page p itself has already been visited. */
+ /* Continue analyzing the btree previously started */
StatPage *p = &pCsr->aPage[pCsr->iPage];
-
+ if( !pCsr->isAgg ) statResetCounts(pCsr);
while( p->iCell<p->nCell ){
StatCell *pCell = &p->aCell[p->iCell];
- if( pCell->iOvfl<pCell->nOvfl ){
- int nUsable;
+ while( pCell->iOvfl<pCell->nOvfl ){
+ int nUsable, iOvfl;
sqlite3BtreeEnter(pBt);
- nUsable = sqlite3BtreeGetPageSize(pBt) -
+ nUsable = sqlite3BtreeGetPageSize(pBt) -
sqlite3BtreeGetReserveNoMutex(pBt);
sqlite3BtreeLeave(pBt);
- pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0);
- pCsr->iPageno = pCell->aOvfl[pCell->iOvfl];
- pCsr->zPagetype = "overflow";
- pCsr->nCell = 0;
- pCsr->nMxPayload = 0;
- pCsr->zPath = z = sqlite3_mprintf(
- "%s%.3x+%.6x", p->zPath, p->iCell, pCell->iOvfl
- );
+ pCsr->nPage++;
+ statSizeAndOffset(pCsr);
if( pCell->iOvfl<pCell->nOvfl-1 ){
- pCsr->nUnused = 0;
- pCsr->nPayload = nUsable - 4;
+ pCsr->nPayload += nUsable - 4;
}else{
- pCsr->nPayload = pCell->nLastOvfl;
- pCsr->nUnused = nUsable - 4 - pCsr->nPayload;
+ pCsr->nPayload += pCell->nLastOvfl;
+ pCsr->nUnused += nUsable - 4 - pCell->nLastOvfl;
}
+ iOvfl = pCell->iOvfl;
pCell->iOvfl++;
- statSizeAndOffset(pCsr);
- return z==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK;
+ if( !pCsr->isAgg ){
+ pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0);
+ pCsr->iPageno = pCell->aOvfl[iOvfl];
+ pCsr->zPagetype = "overflow";
+ pCsr->zPath = z = sqlite3_mprintf(
+ "%s%.3x+%.6x", p->zPath, p->iCell, iOvfl
+ );
+ return z==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK;
+ }
}
if( p->iRightChildPg ) break;
p->iCell++;
@@ -192245,11 +222287,19 @@ statNextRestart:
if( !p->iRightChildPg || p->iCell>p->nCell ){
statClearPage(p);
- if( pCsr->iPage==0 ) return statNext(pCursor);
pCsr->iPage--;
+ if( pCsr->isAgg && pCsr->iPage<0 ){
+ /* label-statNext-done: When computing aggregate space usage over
+ ** an entire btree, this is the exit point from this function */
+ return SQLITE_OK;
+ }
goto statNextRestart; /* Tail recursion */
}
pCsr->iPage++;
+ if( pCsr->iPage>=ArraySize(pCsr->aPage) ){
+ statResetCsr(pCsr);
+ return SQLITE_CORRUPT_BKPT;
+ }
assert( p==&pCsr->aPage[pCsr->iPage-1] );
if( p->iCell==p->nCell ){
@@ -192257,11 +222307,14 @@ statNextRestart:
}else{
p[1].iPgno = p->aCell[p->iCell].iChildPg;
}
- rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0);
+ rc = statGetPage(pBt, p[1].iPgno, &p[1]);
+ pCsr->nPage++;
p[1].iCell = 0;
- p[1].zPath = z = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell);
+ if( !pCsr->isAgg ){
+ p[1].zPath = z = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell);
+ if( z==0 ) rc = SQLITE_NOMEM_BKPT;
+ }
p->iCell++;
- if( z==0 ) rc = SQLITE_NOMEM_BKPT;
}
@@ -192291,16 +222344,23 @@ statNextRestart:
pCsr->zPagetype = "corrupted";
break;
}
- pCsr->nCell = p->nCell;
- pCsr->nUnused = p->nUnused;
- pCsr->nMxPayload = p->nMxPayload;
- pCsr->zPath = z = sqlite3_mprintf("%s", p->zPath);
- if( z==0 ) rc = SQLITE_NOMEM_BKPT;
+ pCsr->nCell += p->nCell;
+ pCsr->nUnused += p->nUnused;
+ if( p->nMxPayload>pCsr->nMxPayload ) pCsr->nMxPayload = p->nMxPayload;
+ if( !pCsr->isAgg ){
+ pCsr->zPath = z = sqlite3_mprintf("%s", p->zPath);
+ if( z==0 ) rc = SQLITE_NOMEM_BKPT;
+ }
nPayload = 0;
for(i=0; i<p->nCell; i++){
nPayload += p->aCell[i].nLocal;
}
- pCsr->nPayload = nPayload;
+ pCsr->nPayload += nPayload;
+
+ /* If computing aggregate space usage by btree, continue with the
+ ** next page. The loop will exit via the return at label-statNext-done
+ */
+ if( pCsr->isAgg ) goto statNextRestart;
}
}
@@ -192312,38 +222372,65 @@ static int statEof(sqlite3_vtab_cursor *pCursor){
return pCsr->isEof;
}
+/* Initialize a cursor according to the query plan idxNum using the
+** arguments in argv[0]. See statBestIndex() for a description of the
+** meaning of the bits in idxNum.
+*/
static int statFilter(
- sqlite3_vtab_cursor *pCursor,
+ sqlite3_vtab_cursor *pCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
StatCursor *pCsr = (StatCursor *)pCursor;
StatTable *pTab = (StatTable*)(pCursor->pVtab);
- char *zSql;
- int rc = SQLITE_OK;
- char *zMaster;
+ sqlite3_str *pSql; /* Query of btrees to analyze */
+ char *zSql; /* String value of pSql */
+ int iArg = 0; /* Count of argv[] parameters used so far */
+ int rc = SQLITE_OK; /* Result of this operation */
+ const char *zName = 0; /* Only provide analysis of this table */
+ (void)argc;
+ (void)idxStr;
- if( idxNum==1 ){
- const char *zDbase = (const char*)sqlite3_value_text(argv[0]);
+ statResetCsr(pCsr);
+ sqlite3_finalize(pCsr->pStmt);
+ pCsr->pStmt = 0;
+ if( idxNum & 0x01 ){
+ /* schema=? constraint is present. Get its value */
+ const char *zDbase = (const char*)sqlite3_value_text(argv[iArg++]);
pCsr->iDb = sqlite3FindDbName(pTab->db, zDbase);
if( pCsr->iDb<0 ){
- sqlite3_free(pCursor->pVtab->zErrMsg);
- pCursor->pVtab->zErrMsg = sqlite3_mprintf("no such schema: %s", zDbase);
- return pCursor->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM_BKPT;
+ pCsr->iDb = 0;
+ pCsr->isEof = 1;
+ return SQLITE_OK;
}
}else{
pCsr->iDb = pTab->iDb;
}
- statResetCsr(pCsr);
- sqlite3_finalize(pCsr->pStmt);
- pCsr->pStmt = 0;
- zMaster = pCsr->iDb==1 ? "sqlite_temp_master" : "sqlite_master";
- zSql = sqlite3_mprintf(
- "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type"
- " UNION ALL "
- "SELECT name, rootpage, type"
- " FROM \"%w\".%s WHERE rootpage!=0"
- " ORDER BY name", pTab->db->aDb[pCsr->iDb].zDbSName, zMaster);
+ if( idxNum & 0x02 ){
+ /* name=? constraint is present */
+ zName = (const char*)sqlite3_value_text(argv[iArg++]);
+ }
+ if( idxNum & 0x04 ){
+ /* aggregate=? constraint is present */
+ pCsr->isAgg = sqlite3_value_double(argv[iArg++])!=0.0;
+ }else{
+ pCsr->isAgg = 0;
+ }
+ pSql = sqlite3_str_new(pTab->db);
+ sqlite3_str_appendf(pSql,
+ "SELECT * FROM ("
+ "SELECT 'sqlite_schema' AS name,1 AS rootpage,'table' AS type"
+ " UNION ALL "
+ "SELECT name,rootpage,type"
+ " FROM \"%w\".sqlite_schema WHERE rootpage!=0)",
+ pTab->db->aDb[pCsr->iDb].zDbSName);
+ if( zName ){
+ sqlite3_str_appendf(pSql, "WHERE name=%Q", zName);
+ }
+ if( idxNum & 0x08 ){
+ sqlite3_str_appendf(pSql, " ORDER BY name");
+ }
+ zSql = sqlite3_str_finish(pSql);
if( zSql==0 ){
return SQLITE_NOMEM_BKPT;
}else{
@@ -192352,14 +222439,15 @@ static int statFilter(
}
if( rc==SQLITE_OK ){
+ pCsr->iPage = -1;
rc = statNext(pCursor);
}
return rc;
}
static int statColumn(
- sqlite3_vtab_cursor *pCursor,
- sqlite3_context *ctx,
+ sqlite3_vtab_cursor *pCursor,
+ sqlite3_context *ctx,
int i
){
StatCursor *pCsr = (StatCursor *)pCursor;
@@ -192368,38 +222456,52 @@ static int statColumn(
sqlite3_result_text(ctx, pCsr->zName, -1, SQLITE_TRANSIENT);
break;
case 1: /* path */
- sqlite3_result_text(ctx, pCsr->zPath, -1, SQLITE_TRANSIENT);
+ if( !pCsr->isAgg ){
+ sqlite3_result_text(ctx, pCsr->zPath, -1, SQLITE_TRANSIENT);
+ }
break;
case 2: /* pageno */
- sqlite3_result_int64(ctx, pCsr->iPageno);
+ if( pCsr->isAgg ){
+ sqlite3_result_int64(ctx, pCsr->nPage);
+ }else{
+ sqlite3_result_int64(ctx, pCsr->iPageno);
+ }
break;
case 3: /* pagetype */
- sqlite3_result_text(ctx, pCsr->zPagetype, -1, SQLITE_STATIC);
+ if( !pCsr->isAgg ){
+ sqlite3_result_text(ctx, pCsr->zPagetype, -1, SQLITE_STATIC);
+ }
break;
case 4: /* ncell */
- sqlite3_result_int(ctx, pCsr->nCell);
+ sqlite3_result_int64(ctx, pCsr->nCell);
break;
case 5: /* payload */
- sqlite3_result_int(ctx, pCsr->nPayload);
+ sqlite3_result_int64(ctx, pCsr->nPayload);
break;
case 6: /* unused */
- sqlite3_result_int(ctx, pCsr->nUnused);
+ sqlite3_result_int64(ctx, pCsr->nUnused);
break;
case 7: /* mx_payload */
- sqlite3_result_int(ctx, pCsr->nMxPayload);
+ sqlite3_result_int64(ctx, pCsr->nMxPayload);
break;
case 8: /* pgoffset */
- sqlite3_result_int64(ctx, pCsr->iOffset);
+ if( !pCsr->isAgg ){
+ sqlite3_result_int64(ctx, pCsr->iOffset);
+ }
break;
case 9: /* pgsize */
- sqlite3_result_int(ctx, pCsr->szPage);
+ sqlite3_result_int64(ctx, pCsr->szPage);
break;
- default: { /* schema */
+ case 10: { /* schema */
sqlite3 *db = sqlite3_context_db_handle(ctx);
int iDb = pCsr->iDb;
sqlite3_result_text(ctx, db->aDb[iDb].zDbSName, -1, SQLITE_STATIC);
break;
}
+ default: { /* aggregate */
+ sqlite3_result_int(ctx, pCsr->isAgg);
+ break;
+ }
}
return SQLITE_OK;
}
@@ -192438,7 +222540,8 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
return sqlite3_create_module(db, "dbstat", &dbstat_module, 0);
}
@@ -192463,7 +222566,7 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){ return SQLITE_OK; }
** This file contains an implementation of the "sqlite_dbpage" virtual table.
**
** The sqlite_dbpage virtual table is used to read or write whole raw
-** pages of the database file. The pager interface is used so that
+** pages of the database file. The pager interface is used so that
** uncommitted changes and changes recorded in the WAL file are correctly
** retrieved.
**
@@ -192522,8 +222625,14 @@ static int dbpageConnect(
){
DbpageTable *pTab = 0;
int rc = SQLITE_OK;
+ (void)pAux;
+ (void)argc;
+ (void)argv;
+ (void)pzErr;
- rc = sqlite3_declare_vtab(db,
+ sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
+ sqlite3_vtab_config(db, SQLITE_VTAB_USES_ALL_SCHEMAS);
+ rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
if( rc==SQLITE_OK ){
pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable));
@@ -192559,6 +222668,7 @@ static int dbpageDisconnect(sqlite3_vtab *pVtab){
static int dbpageBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int i;
int iPlan = 0;
+ (void)tab;
/* If there is a schema= constraint, it must be honored. Report a
** ridiculously large estimated cost if the schema= constraint is
@@ -192663,7 +222773,7 @@ static int dbpageEof(sqlite3_vtab_cursor *pCursor){
** idxStr is not used
*/
static int dbpageFilter(
- sqlite3_vtab_cursor *pCursor,
+ sqlite3_vtab_cursor *pCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
@@ -192673,8 +222783,10 @@ static int dbpageFilter(
sqlite3 *db = pTab->db;
Btree *pBt;
+ (void)idxStr;
+
/* Default setting is no rows of result */
- pCsr->pgno = 1;
+ pCsr->pgno = 1;
pCsr->mxPgno = 0;
if( idxNum & 2 ){
@@ -192687,7 +222799,7 @@ static int dbpageFilter(
pCsr->iDb = 0;
}
pBt = db->aDb[pCsr->iDb].pBt;
- if( pBt==0 ) return SQLITE_OK;
+ if( NEVER(pBt==0) ) return SQLITE_OK;
pCsr->pPager = sqlite3BtreePager(pBt);
pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
pCsr->mxPgno = sqlite3BtreeLastPage(pBt);
@@ -192709,8 +222821,8 @@ static int dbpageFilter(
}
static int dbpageColumn(
- sqlite3_vtab_cursor *pCursor,
- sqlite3_context *ctx,
+ sqlite3_vtab_cursor *pCursor,
+ sqlite3_context *ctx,
int i
){
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
@@ -192722,12 +222834,18 @@ static int dbpageColumn(
}
case 1: { /* data */
DbPage *pDbPage = 0;
- rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
- if( rc==SQLITE_OK ){
- sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
- SQLITE_TRANSIENT);
+ if( pCsr->pgno==((PENDING_BYTE/pCsr->szPage)+1) ){
+ /* The pending byte page. Assume it is zeroed out. Attempting to
+ ** request this page from the page is an SQLITE_CORRUPT error. */
+ sqlite3_result_zeroblob(ctx, pCsr->szPage);
+ }else{
+ rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
+ SQLITE_TRANSIENT);
+ }
+ sqlite3PagerUnref(pDbPage);
}
- sqlite3PagerUnref(pDbPage);
break;
}
default: { /* schema */
@@ -192736,7 +222854,7 @@ static int dbpageColumn(
break;
}
}
- return SQLITE_OK;
+ return rc;
}
static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
@@ -192762,6 +222880,7 @@ static int dbpageUpdate(
Pager *pPager;
int szPage;
+ (void)pRowid;
if( pTab->db->flags & SQLITE_Defensive ){
zErr = "read-only";
goto update_fail;
@@ -192771,23 +222890,25 @@ static int dbpageUpdate(
goto update_fail;
}
pgno = sqlite3_value_int(argv[0]);
- if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL
+ || (Pgno)sqlite3_value_int(argv[1])!=pgno
+ ){
zErr = "cannot insert";
goto update_fail;
}
zSchema = (const char*)sqlite3_value_text(argv[4]);
- iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1;
- if( iDb<0 ){
+ iDb = ALWAYS(zSchema) ? sqlite3FindDbName(pTab->db, zSchema) : -1;
+ if( NEVER(iDb<0) ){
zErr = "no such schema";
goto update_fail;
}
pBt = pTab->db->aDb[iDb].pBt;
- if( pgno<1 || pBt==0 || pgno>(int)sqlite3BtreeLastPage(pBt) ){
+ if( NEVER(pgno<1) || NEVER(pBt==0) || NEVER(pgno>sqlite3BtreeLastPage(pBt)) ){
zErr = "bad page number";
goto update_fail;
}
szPage = sqlite3BtreeGetPageSize(pBt);
- if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
+ if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
|| sqlite3_value_bytes(argv[3])!=szPage
){
zErr = "bad page value";
@@ -192796,11 +222917,12 @@ static int dbpageUpdate(
pPager = sqlite3BtreePager(pBt);
rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
if( rc==SQLITE_OK ){
- rc = sqlite3PagerWrite(pDbPage);
- if( rc==SQLITE_OK ){
- memcpy(sqlite3PagerGetData(pDbPage),
- sqlite3_value_blob(argv[3]),
- szPage);
+ const void *pData = sqlite3_value_blob(argv[3]);
+ assert( pData!=0 || pTab->db->mallocFailed );
+ if( pData
+ && (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK
+ ){
+ memcpy(sqlite3PagerGetData(pDbPage), pData, szPage);
}
}
sqlite3PagerUnref(pDbPage);
@@ -192822,7 +222944,7 @@ static int dbpageBegin(sqlite3_vtab *pVtab){
int i;
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
- if( pBt ) sqlite3BtreeBeginTrans(pBt, 1, 0);
+ if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0);
}
return SQLITE_OK;
}
@@ -192856,7 +222978,8 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
}
@@ -192893,6 +223016,8 @@ typedef struct SessionInput SessionInput;
# endif
#endif
+#define SESSIONS_ROWID "_rowid_"
+
static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE;
typedef struct SessionHook SessionHook;
@@ -192910,12 +223035,16 @@ struct SessionHook {
struct sqlite3_session {
sqlite3 *db; /* Database handle session is attached to */
char *zDb; /* Name of database session is attached to */
+ int bEnableSize; /* True if changeset_size() enabled */
int bEnable; /* True if currently recording */
int bIndirect; /* True if all changes are indirect */
int bAutoAttach; /* True to auto-attach tables */
+ int bImplicitPK; /* True to handle tables with implicit PK */
int rc; /* Non-zero if an error has occurred */
void *pFilterCtx; /* First argument to pass to xTableFilter */
int (*xTableFilter)(void *pCtx, const char *zTab);
+ i64 nMalloc; /* Number of bytes of data allocated */
+ i64 nMaxChangesetSize;
sqlite3_value *pZeroBlob; /* Value containing X'' */
sqlite3_session *pNext; /* Next session object on same db. */
SessionTable *pTable; /* List of attached tables */
@@ -192932,7 +223061,7 @@ struct SessionBuffer {
};
/*
-** An object of this type is used internally as an abstraction for
+** An object of this type is used internally as an abstraction for
** input data. Input data may be supplied either as a single large buffer
** (e.g. sqlite3changeset_start()) or using a stream function (e.g.
** sqlite3changeset_start_strm()).
@@ -192958,6 +223087,7 @@ struct sqlite3_changeset_iter {
SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */
int bPatchset; /* True if this is a patchset */
int bInvert; /* True to invert changeset */
+ int bSkipEmpty; /* Skip noop UPDATE changes */
int rc; /* Iterator error code */
sqlite3_stmt *pConflict; /* Points to conflicting row, if any */
char *zTab; /* Current table */
@@ -192980,24 +223110,39 @@ struct sqlite3_changeset_iter {
** The data associated with each hash-table entry is a structure containing
** a subset of the initial values that the modified row contained at the
** start of the session. Or no initial values if the row was inserted.
+**
+** pDfltStmt:
+** This is only used by the sqlite3changegroup_xxx() APIs, not by
+** regular sqlite3_session objects. It is a SELECT statement that
+** selects the default value for each table column. For example,
+** if the table is
+**
+** CREATE TABLE xx(a DEFAULT 1, b, c DEFAULT 'abc')
+**
+** then this variable is the compiled version of:
+**
+** SELECT 1, NULL, 'abc'
*/
struct SessionTable {
SessionTable *pNext;
char *zName; /* Local name of table */
int nCol; /* Number of columns in table zName */
int bStat1; /* True if this is sqlite_stat1 */
+ int bRowid; /* True if this table uses rowid for PK */
const char **azCol; /* Column names */
+ const char **azDflt; /* Default value expressions */
u8 *abPK; /* Array of primary key flags */
int nEntry; /* Total number of entries in hash table */
int nChange; /* Size of apChange[] array */
SessionChange **apChange; /* Hash table buckets */
+ sqlite3_stmt *pDfltStmt;
};
-/*
+/*
** RECORD FORMAT:
**
-** The following record format is similar to (but not compatible with) that
-** used in SQLite database files. This format is used as part of the
+** The following record format is similar to (but not compatible with) that
+** used in SQLite database files. This format is used as part of the
** change-set binary format, and so must be architecture independent.
**
** Unlike the SQLite database record format, each field is self-contained -
@@ -193031,7 +223176,7 @@ struct SessionTable {
** Real values:
** An 8-byte big-endian IEEE 754-2008 real value.
**
-** Varint values are encoded in the same way as varints in the SQLite
+** Varint values are encoded in the same way as varints in the SQLite
** record format.
**
** CHANGESET FORMAT:
@@ -193063,7 +223208,7 @@ struct SessionTable {
**
** The new.* record that is part of each INSERT change contains the values
** that make up the new row. Similarly, the old.* record that is part of each
-** DELETE change contains the values that made up the row that was deleted
+** DELETE change contains the values that made up the row that was deleted
** from the database. In the changeset format, the records that are part
** of INSERT or DELETE changes never contain any undefined (type byte 0x00)
** fields.
@@ -193072,8 +223217,8 @@ struct SessionTable {
** associated with table columns that are not PRIMARY KEY columns and are
** not modified by the UPDATE change are set to "undefined". Other fields
** are set to the values that made up the row before the UPDATE that the
-** change records took place. Within the new.* record, fields associated
-** with table columns modified by the UPDATE change contain the new
+** change records took place. Within the new.* record, fields associated
+** with table columns modified by the UPDATE change contain the new
** values. Fields associated with table columns that are not modified
** are set to "undefined".
**
@@ -193099,7 +223244,7 @@ struct SessionTable {
**
** As in the changeset format, each field of the single record that is part
** of a patchset change is associated with the correspondingly positioned
-** table column, counting from left to right within the CREATE TABLE
+** table column, counting from left to right within the CREATE TABLE
** statement.
**
** For a DELETE change, all fields within the record except those associated
@@ -193117,7 +223262,7 @@ struct SessionTable {
**
** REBASE BLOB FORMAT:
**
-** A rebase blob may be output by sqlite3changeset_apply_v2() and its
+** A rebase blob may be output by sqlite3changeset_apply_v2() and its
** streaming equivalent for use with the sqlite3_rebaser APIs to rebase
** existing changesets. A rebase blob contains one entry for each conflict
** resolved using either the OMIT or REPLACE strategies within the apply_v2()
@@ -193141,7 +223286,7 @@ struct SessionTable {
**
** In a rebase blob, the first field is set to SQLITE_INSERT if the change
** that caused the conflict was an INSERT or UPDATE, or to SQLITE_DELETE if
-** it was a DELETE. The second field is set to 0x01 if the conflict
+** it was a DELETE. The second field is set to 0x01 if the conflict
** resolution strategy was REPLACE, or 0x00 if it was OMIT.
**
** If the change that caused the conflict was a DELETE, then the single
@@ -193157,15 +223302,17 @@ struct SessionTable {
** this structure stored in a SessionTable.aChange[] hash table.
*/
struct SessionChange {
- int op; /* One of UPDATE, DELETE, INSERT */
- int bIndirect; /* True if this change is "indirect" */
+ u8 op; /* One of UPDATE, DELETE, INSERT */
+ u8 bIndirect; /* True if this change is "indirect" */
+ u16 nRecordField; /* Number of fields in aRecord[] */
+ int nMaxSize; /* Max size of eventual changeset record */
int nRecord; /* Number of bytes in buffer aRecord[] */
u8 *aRecord; /* Buffer containing old.* record */
SessionChange *pNext; /* For hash-table collisions */
};
/*
-** Write a varint with value iVal into the buffer at aBuf. Return the
+** Write a varint with value iVal into the buffer at aBuf. Return the
** number of bytes written.
*/
static int sessionVarintPut(u8 *aBuf, int iVal){
@@ -193180,10 +223327,10 @@ static int sessionVarintLen(int iVal){
}
/*
-** Read a varint value from aBuf[] into *piVal. Return the number of
+** Read a varint value from aBuf[] into *piVal. Return the number of
** bytes read.
*/
-static int sessionVarintGet(u8 *aBuf, int *piVal){
+static int sessionVarintGet(const u8 *aBuf, int *piVal){
return getVarint32(aBuf, *piVal);
}
@@ -193219,34 +223366,34 @@ static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){
** This function is used to serialize the contents of value pValue (see
** comment titled "RECORD FORMAT" above).
**
-** If it is non-NULL, the serialized form of the value is written to
+** If it is non-NULL, the serialized form of the value is written to
** buffer aBuf. *pnWrite is set to the number of bytes written before
** returning. Or, if aBuf is NULL, the only thing this function does is
** set *pnWrite.
**
** If no error occurs, SQLITE_OK is returned. Or, if an OOM error occurs
-** within a call to sqlite3_value_text() (may fail if the db is utf-16))
+** within a call to sqlite3_value_text() (may fail if the db is utf-16))
** SQLITE_NOMEM is returned.
*/
static int sessionSerializeValue(
u8 *aBuf, /* If non-NULL, write serialized value here */
sqlite3_value *pValue, /* Value to serialize */
- int *pnWrite /* IN/OUT: Increment by bytes written */
+ sqlite3_int64 *pnWrite /* IN/OUT: Increment by bytes written */
){
int nByte; /* Size of serialized value in bytes */
if( pValue ){
int eType; /* Value type (SQLITE_NULL, TEXT etc.) */
-
+
eType = sqlite3_value_type(pValue);
if( aBuf ) aBuf[0] = eType;
-
+
switch( eType ){
- case SQLITE_NULL:
+ case SQLITE_NULL:
nByte = 1;
break;
-
- case SQLITE_INTEGER:
+
+ case SQLITE_INTEGER:
case SQLITE_FLOAT:
if( aBuf ){
/* TODO: SQLite does something special to deal with mixed-endian
@@ -193263,14 +223410,14 @@ static int sessionSerializeValue(
}
sessionPutI64(&aBuf[1], i);
}
- nByte = 9;
+ nByte = 9;
break;
-
+
default: {
u8 *z;
int n;
int nVarint;
-
+
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
if( eType==SQLITE_TEXT ){
z = (u8 *)sqlite3_value_text(pValue);
@@ -193280,12 +223427,12 @@ static int sessionSerializeValue(
n = sqlite3_value_bytes(pValue);
if( z==0 && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
nVarint = sessionVarintLen(n);
-
+
if( aBuf ){
sessionVarintPut(&aBuf[1], n);
- if( n ) memcpy(&aBuf[nVarint + 1], z, n);
+ if( n>0 ) memcpy(&aBuf[nVarint + 1], z, n);
}
-
+
nByte = 1 + nVarint + n;
break;
}
@@ -193299,6 +223446,26 @@ static int sessionSerializeValue(
return SQLITE_OK;
}
+/*
+** Allocate and return a pointer to a buffer nByte bytes in size. If
+** pSession is not NULL, increase the sqlite3_session.nMalloc variable
+** by the number of bytes allocated.
+*/
+static void *sessionMalloc64(sqlite3_session *pSession, i64 nByte){
+ void *pRet = sqlite3_malloc64(nByte);
+ if( pSession ) pSession->nMalloc += sqlite3_msize(pRet);
+ return pRet;
+}
+
+/*
+** Free buffer pFree, which must have been allocated by an earlier
+** call to sessionMalloc64(). If pSession is not NULL, decrease the
+** sqlite3_session.nMalloc counter by the number of bytes freed.
+*/
+static void sessionFree(sqlite3_session *pSession, void *pFree){
+ if( pSession ) pSession->nMalloc -= sqlite3_msize(pFree);
+ sqlite3_free(pFree);
+}
/*
** This macro is used to calculate hash key values for data structures. In
@@ -193327,7 +223494,7 @@ static unsigned int sessionHashAppendI64(unsigned int h, i64 i){
}
/*
-** Append the hash of the blob passed via the second and third arguments to
+** Append the hash of the blob passed via the second and third arguments to
** the hash-key value passed as the first. Return the new hash-key value.
*/
static unsigned int sessionHashAppendBlob(unsigned int h, int n, const u8 *z){
@@ -193346,7 +223513,7 @@ static unsigned int sessionHashAppendType(unsigned int h, int eType){
/*
** This function may only be called from within a pre-update callback.
-** It calculates a hash based on the primary key values of the old.* or
+** It calculates a hash based on the primary key values of the old.* or
** new.* row currently available and, assuming no error occurs, writes it to
** *piHash before returning. If the primary key contains one or more NULL
** values, *pbNullPK is set to true before returning.
@@ -193357,6 +223524,7 @@ static unsigned int sessionHashAppendType(unsigned int h, int eType){
*/
static int sessionPreupdateHash(
sqlite3_session *pSession, /* Session object that owns pTab */
+ i64 iRowid,
SessionTable *pTab, /* Session table handle */
int bNew, /* True to hash the new.* PK */
int *piHash, /* OUT: Hash value */
@@ -193365,48 +223533,53 @@ static int sessionPreupdateHash(
unsigned int h = 0; /* Hash value to return */
int i; /* Used to iterate through columns */
- assert( *pbNullPK==0 );
- assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) );
- for(i=0; i<pTab->nCol; i++){
- if( pTab->abPK[i] ){
- int rc;
- int eType;
- sqlite3_value *pVal;
-
- if( bNew ){
- rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal);
- }else{
- rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal);
- }
- if( rc!=SQLITE_OK ) return rc;
+ if( pTab->bRowid ){
+ assert( pTab->nCol-1==pSession->hook.xCount(pSession->hook.pCtx) );
+ h = sessionHashAppendI64(h, iRowid);
+ }else{
+ assert( *pbNullPK==0 );
+ assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) );
+ for(i=0; i<pTab->nCol; i++){
+ if( pTab->abPK[i] ){
+ int rc;
+ int eType;
+ sqlite3_value *pVal;
- eType = sqlite3_value_type(pVal);
- h = sessionHashAppendType(h, eType);
- if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
- i64 iVal;
- if( eType==SQLITE_INTEGER ){
- iVal = sqlite3_value_int64(pVal);
+ if( bNew ){
+ rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal);
}else{
- double rVal = sqlite3_value_double(pVal);
- assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
- memcpy(&iVal, &rVal, 8);
+ rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal);
}
- h = sessionHashAppendI64(h, iVal);
- }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
- const u8 *z;
- int n;
- if( eType==SQLITE_TEXT ){
- z = (const u8 *)sqlite3_value_text(pVal);
+ if( rc!=SQLITE_OK ) return rc;
+
+ eType = sqlite3_value_type(pVal);
+ h = sessionHashAppendType(h, eType);
+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ i64 iVal;
+ if( eType==SQLITE_INTEGER ){
+ iVal = sqlite3_value_int64(pVal);
+ }else{
+ double rVal = sqlite3_value_double(pVal);
+ assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
+ memcpy(&iVal, &rVal, 8);
+ }
+ h = sessionHashAppendI64(h, iVal);
+ }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
+ const u8 *z;
+ int n;
+ if( eType==SQLITE_TEXT ){
+ z = (const u8 *)sqlite3_value_text(pVal);
+ }else{
+ z = (const u8 *)sqlite3_value_blob(pVal);
+ }
+ n = sqlite3_value_bytes(pVal);
+ if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
+ h = sessionHashAppendBlob(h, n, z);
}else{
- z = (const u8 *)sqlite3_value_blob(pVal);
+ assert( eType==SQLITE_NULL );
+ assert( pTab->bStat1==0 || i!=1 );
+ *pbNullPK = 1;
}
- n = sqlite3_value_bytes(pVal);
- if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
- h = sessionHashAppendBlob(h, n, z);
- }else{
- assert( eType==SQLITE_NULL );
- assert( pTab->bStat1==0 || i!=1 );
- *pbNullPK = 1;
}
}
}
@@ -193420,9 +223593,11 @@ static int sessionPreupdateHash(
** Return the number of bytes of space occupied by the value (including
** the type byte).
*/
-static int sessionSerialLen(u8 *a){
- int e = *a;
+static int sessionSerialLen(const u8 *a){
+ int e;
int n;
+ assert( a!=0 );
+ e = *a;
if( e==0 || e==0xFF ) return 1;
if( e==SQLITE_NULL ) return 1;
if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
@@ -193453,12 +223628,12 @@ static unsigned int sessionChangeHash(
int isPK = pTab->abPK[i];
if( bPkOnly && isPK==0 ) continue;
- /* It is not possible for eType to be SQLITE_NULL here. The session
+ /* It is not possible for eType to be SQLITE_NULL here. The session
** module does not record changes for rows with NULL values stored in
** primary key columns. */
- assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
- || eType==SQLITE_TEXT || eType==SQLITE_BLOB
- || eType==SQLITE_NULL || eType==0
+ assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
+ || eType==SQLITE_TEXT || eType==SQLITE_BLOB
+ || eType==SQLITE_NULL || eType==0
);
assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) );
@@ -193469,7 +223644,7 @@ static unsigned int sessionChangeHash(
h = sessionHashAppendI64(h, sessionGetI64(a));
a += 8;
}else{
- int n;
+ int n;
a += sessionVarintGet(a, &n);
h = sessionHashAppendBlob(h, n, a);
a += n;
@@ -193484,7 +223659,7 @@ static unsigned int sessionChangeHash(
/*
** Arguments aLeft and aRight are pointers to change records for table pTab.
** This function returns true if the two records apply to the same row (i.e.
-** have the same values stored in the primary key columns), or false
+** have the same values stored in the primary key columns), or false
** otherwise.
*/
static int sessionChangeEqual(
@@ -193521,17 +223696,17 @@ static int sessionChangeEqual(
** Arguments aLeft and aRight both point to buffers containing change
** records with nCol columns. This function "merges" the two records into
** a single records which is written to the buffer at *paOut. *paOut is
-** then set to point to one byte after the last byte written before
+** then set to point to one byte after the last byte written before
** returning.
**
-** The merging of records is done as follows: For each column, if the
+** The merging of records is done as follows: For each column, if the
** aRight record contains a value for the column, copy the value from
** their. Otherwise, if aLeft contains a value, copy it. If neither
** record contains a value for a given column, then neither does the
** output record.
*/
static void sessionMergeRecord(
- u8 **paOut,
+ u8 **paOut,
int nCol,
u8 *aLeft,
u8 *aRight
@@ -193561,13 +223736,13 @@ static void sessionMergeRecord(
/*
** This is a helper function used by sessionMergeUpdate().
**
-** When this function is called, both *paOne and *paTwo point to a value
-** within a change record. Before it returns, both have been advanced so
+** When this function is called, both *paOne and *paTwo point to a value
+** within a change record. Before it returns, both have been advanced so
** as to point to the next value in the record.
**
** If, when this function is called, *paTwo points to a valid value (i.e.
** *paTwo[0] is not 0x00 - the "no value" placeholder), a copy of the *paTwo
-** pointer is returned and *pnVal is set to the number of bytes in the
+** pointer is returned and *pnVal is set to the number of bytes in the
** serialized value. Otherwise, a copy of *paOne is returned and *pnVal
** set to the number of bytes in the value at *paOne. If *paOne points
** to the "no value" placeholder, *pnVal is set to 1. In other words:
@@ -193666,8 +223841,8 @@ static int sessionMergeUpdate(
aOld = sessionMergeValue(&aOld1, &aOld2, &nOld);
aNew = sessionMergeValue(&aNew1, &aNew2, &nNew);
- if( bPatchset==0
- && (pTab->abPK[i] || (nOld==nNew && 0==memcmp(aOld, aNew, nNew)))
+ if( bPatchset==0
+ && (pTab->abPK[i] || (nOld==nNew && 0==memcmp(aOld, aNew, nNew)))
){
*(aOut++) = '\0';
}else{
@@ -193689,6 +223864,7 @@ static int sessionMergeUpdate(
*/
static int sessionPreupdateEqual(
sqlite3_session *pSession, /* Session object that owns SessionTable */
+ i64 iRowid, /* Rowid value if pTab->bRowid */
SessionTable *pTab, /* Table associated with change */
SessionChange *pChange, /* Change to compare to */
int op /* Current pre-update operation */
@@ -193696,6 +223872,11 @@ static int sessionPreupdateEqual(
int iCol; /* Used to iterate through columns */
u8 *a = pChange->aRecord; /* Cursor used to scan change record */
+ if( pTab->bRowid ){
+ if( a[0]!=SQLITE_INTEGER ) return 0;
+ return sessionGetI64(&a[1])==iRowid;
+ }
+
assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
for(iCol=0; iCol<pTab->nCol; iCol++){
if( !pTab->abPK[iCol] ){
@@ -193718,6 +223899,7 @@ static int sessionPreupdateEqual(
rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal);
}
assert( rc==SQLITE_OK );
+ (void)rc; /* Suppress warning about unused variable */
if( sqlite3_value_type(pVal)!=eType ) return 0;
/* A SessionChange object never has a NULL value in a PK column */
@@ -193756,7 +223938,7 @@ static int sessionPreupdateEqual(
}
/*
-** If required, grow the hash table used to store changes on table pTab
+** If required, grow the hash table used to store changes on table pTab
** (part of the session pSession). If a fatal OOM error occurs, set the
** session object to failed and return SQLITE_ERROR. Otherwise, return
** SQLITE_OK.
@@ -193766,13 +223948,19 @@ static int sessionPreupdateEqual(
** Growing the hash table in this case is a performance optimization only,
** it is not required for correct operation.
*/
-static int sessionGrowHash(int bPatchset, SessionTable *pTab){
+static int sessionGrowHash(
+ sqlite3_session *pSession, /* For memory accounting. May be NULL */
+ int bPatchset,
+ SessionTable *pTab
+){
if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
int i;
SessionChange **apNew;
- int nNew = (pTab->nChange ? pTab->nChange : 128) * 2;
+ sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128);
- apNew = (SessionChange **)sqlite3_malloc(sizeof(SessionChange *) * nNew);
+ apNew = (SessionChange**)sessionMalloc64(
+ pSession, sizeof(SessionChange*) * nNew
+ );
if( apNew==0 ){
if( pTab->nChange==0 ){
return SQLITE_ERROR;
@@ -193793,7 +223981,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){
}
}
- sqlite3_free(pTab->apChange);
+ sessionFree(pSession, pTab->apChange);
pTab->nChange = nNew;
pTab->apChange = apNew;
}
@@ -193814,40 +224002,52 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){
**
** For example, if the table is declared as:
**
-** CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z));
+** CREATE TABLE tbl1(w, x DEFAULT 'abc', y, z, PRIMARY KEY(w, z));
**
-** Then the four output variables are populated as follows:
+** Then the five output variables are populated as follows:
**
** *pnCol = 4
** *pzTab = "tbl1"
** *pazCol = {"w", "x", "y", "z"}
+** *pazDflt = {NULL, 'abc', NULL, NULL}
** *pabPK = {1, 0, 0, 1}
**
** All returned buffers are part of the same single allocation, which must
** be freed using sqlite3_free() by the caller
*/
static int sessionTableInfo(
+ sqlite3_session *pSession, /* For memory accounting. May be NULL */
sqlite3 *db, /* Database connection */
const char *zDb, /* Name of attached database (e.g. "main") */
const char *zThis, /* Table name */
int *pnCol, /* OUT: number of columns */
const char **pzTab, /* OUT: Copy of zThis */
const char ***pazCol, /* OUT: Array of column names for table */
- u8 **pabPK /* OUT: Array of booleans - true for PK col */
+ const char ***pazDflt, /* OUT: Array of default value expressions */
+ u8 **pabPK, /* OUT: Array of booleans - true for PK col */
+ int *pbRowid /* OUT: True if only PK is a rowid */
){
char *zPragma;
sqlite3_stmt *pStmt;
int rc;
- int nByte;
+ sqlite3_int64 nByte;
int nDbCol = 0;
int nThis;
int i;
u8 *pAlloc = 0;
char **azCol = 0;
+ char **azDflt = 0;
u8 *abPK = 0;
+ int bRowid = 0; /* Set to true to use rowid as PK */
assert( pazCol && pabPK );
+ *pazCol = 0;
+ *pabPK = 0;
+ *pnCol = 0;
+ if( pzTab ) *pzTab = 0;
+ if( pazDflt ) *pazDflt = 0;
+
nThis = sqlite3Strlen30(zThis);
if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){
rc = sqlite3_table_column_metadata(db, zDb, zThis, 0, 0, 0, 0, 0, 0);
@@ -193866,29 +224066,42 @@ static int sessionTableInfo(
}else{
zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
}
- if( !zPragma ) return SQLITE_NOMEM;
+ if( !zPragma ){
+ return SQLITE_NOMEM;
+ }
rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
sqlite3_free(zPragma);
- if( rc!=SQLITE_OK ) return rc;
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
nByte = nThis + 1;
+ bRowid = (pbRowid!=0);
while( SQLITE_ROW==sqlite3_step(pStmt) ){
- nByte += sqlite3_column_bytes(pStmt, 1);
+ nByte += sqlite3_column_bytes(pStmt, 1); /* name */
+ nByte += sqlite3_column_bytes(pStmt, 4); /* dflt_value */
nDbCol++;
+ if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; /* pk */
}
+ if( nDbCol==0 ) bRowid = 0;
+ nDbCol += bRowid;
+ nByte += strlen(SESSIONS_ROWID);
rc = sqlite3_reset(pStmt);
if( rc==SQLITE_OK ){
- nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
- pAlloc = sqlite3_malloc(nByte);
+ nByte += nDbCol * (sizeof(const char *)*2 + sizeof(u8) + 1 + 1);
+ pAlloc = sessionMalloc64(pSession, nByte);
if( pAlloc==0 ){
rc = SQLITE_NOMEM;
+ }else{
+ memset(pAlloc, 0, nByte);
}
}
if( rc==SQLITE_OK ){
azCol = (char **)pAlloc;
- pAlloc = (u8 *)&azCol[nDbCol];
+ azDflt = (char**)&azCol[nDbCol];
+ pAlloc = (u8 *)&azDflt[nDbCol];
abPK = (u8 *)pAlloc;
pAlloc = &abPK[nDbCol];
if( pzTab ){
@@ -193896,60 +224109,82 @@ static int sessionTableInfo(
*pzTab = (char *)pAlloc;
pAlloc += nThis+1;
}
-
+
i = 0;
+ if( bRowid ){
+ size_t nName = strlen(SESSIONS_ROWID);
+ memcpy(pAlloc, SESSIONS_ROWID, nName+1);
+ azCol[i] = (char*)pAlloc;
+ pAlloc += nName+1;
+ abPK[i] = 1;
+ i++;
+ }
while( SQLITE_ROW==sqlite3_step(pStmt) ){
int nName = sqlite3_column_bytes(pStmt, 1);
+ int nDflt = sqlite3_column_bytes(pStmt, 4);
const unsigned char *zName = sqlite3_column_text(pStmt, 1);
+ const unsigned char *zDflt = sqlite3_column_text(pStmt, 4);
+
if( zName==0 ) break;
memcpy(pAlloc, zName, nName+1);
azCol[i] = (char *)pAlloc;
pAlloc += nName+1;
+ if( zDflt ){
+ memcpy(pAlloc, zDflt, nDflt+1);
+ azDflt[i] = (char *)pAlloc;
+ pAlloc += nDflt+1;
+ }else{
+ azDflt[i] = 0;
+ }
abPK[i] = sqlite3_column_int(pStmt, 5);
i++;
}
rc = sqlite3_reset(pStmt);
-
}
/* If successful, populate the output variables. Otherwise, zero them and
** free any allocation made. An error code will be returned in this case.
*/
if( rc==SQLITE_OK ){
- *pazCol = (const char **)azCol;
+ *pazCol = (const char**)azCol;
+ if( pazDflt ) *pazDflt = (const char**)azDflt;
*pabPK = abPK;
*pnCol = nDbCol;
}else{
- *pazCol = 0;
- *pabPK = 0;
- *pnCol = 0;
- if( pzTab ) *pzTab = 0;
- sqlite3_free(azCol);
+ sessionFree(pSession, azCol);
}
+ if( pbRowid ) *pbRowid = bRowid;
sqlite3_finalize(pStmt);
return rc;
}
/*
-** This function is only called from within a pre-update handler for a
-** write to table pTab, part of session pSession. If this is the first
-** write to this table, initalize the SessionTable.nCol, azCol[] and
-** abPK[] arrays accordingly.
+** This function is called to initialize the SessionTable.nCol, azCol[]
+** abPK[] and azDflt[] members of SessionTable object pTab. If these
+** fields are already initilialized, this function is a no-op.
**
** If an error occurs, an error code is stored in sqlite3_session.rc and
** non-zero returned. Or, if no error occurs but the table has no primary
** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to
-** indicate that updates on this table should be ignored. SessionTable.abPK
+** indicate that updates on this table should be ignored. SessionTable.abPK
** is set to NULL in this case.
*/
-static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
+static int sessionInitTable(
+ sqlite3_session *pSession, /* Optional session handle */
+ SessionTable *pTab, /* Table object to initialize */
+ sqlite3 *db, /* Database handle to read schema from */
+ const char *zDb /* Name of db - "main", "temp" etc. */
+){
+ int rc = SQLITE_OK;
+
if( pTab->nCol==0 ){
u8 *abPK;
assert( pTab->azCol==0 || pTab->abPK==0 );
- pSession->rc = sessionTableInfo(pSession->db, pSession->zDb,
- pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK
+ rc = sessionTableInfo(pSession, db, zDb,
+ pTab->zName, &pTab->nCol, 0, &pTab->azCol, &pTab->azDflt, &abPK,
+ ((pSession==0 || pSession->bImplicitPK) ? &pTab->bRowid : 0)
);
- if( pSession->rc==SQLITE_OK ){
+ if( rc==SQLITE_OK ){
int i;
for(i=0; i<pTab->nCol; i++){
if( abPK[i] ){
@@ -193960,9 +224195,322 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
if( 0==sqlite3_stricmp("sqlite_stat1", pTab->zName) ){
pTab->bStat1 = 1;
}
+
+ if( pSession && pSession->bEnableSize ){
+ pSession->nMaxChangesetSize += (
+ 1 + sessionVarintLen(pTab->nCol) + pTab->nCol + strlen(pTab->zName)+1
+ );
+ }
+ }
+ }
+
+ if( pSession ){
+ pSession->rc = rc;
+ return (rc || pTab->abPK==0);
+ }
+ return rc;
+}
+
+/*
+** Re-initialize table object pTab.
+*/
+static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){
+ int nCol = 0;
+ const char **azCol = 0;
+ const char **azDflt = 0;
+ u8 *abPK = 0;
+ int bRowid = 0;
+
+ assert( pSession->rc==SQLITE_OK );
+
+ pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
+ pTab->zName, &nCol, 0, &azCol, &azDflt, &abPK,
+ (pSession->bImplicitPK ? &bRowid : 0)
+ );
+ if( pSession->rc==SQLITE_OK ){
+ if( pTab->nCol>nCol || pTab->bRowid!=bRowid ){
+ pSession->rc = SQLITE_SCHEMA;
+ }else{
+ int ii;
+ int nOldCol = pTab->nCol;
+ for(ii=0; ii<nCol; ii++){
+ if( ii<pTab->nCol ){
+ if( pTab->abPK[ii]!=abPK[ii] ){
+ pSession->rc = SQLITE_SCHEMA;
+ }
+ }else if( abPK[ii] ){
+ pSession->rc = SQLITE_SCHEMA;
+ }
+ }
+
+ if( pSession->rc==SQLITE_OK ){
+ const char **a = pTab->azCol;
+ pTab->azCol = azCol;
+ pTab->nCol = nCol;
+ pTab->azDflt = azDflt;
+ pTab->abPK = abPK;
+ azCol = a;
+ }
+ if( pSession->bEnableSize ){
+ pSession->nMaxChangesetSize += (nCol - nOldCol);
+ pSession->nMaxChangesetSize += sessionVarintLen(nCol);
+ pSession->nMaxChangesetSize -= sessionVarintLen(nOldCol);
+ }
}
}
- return (pSession->rc || pTab->abPK==0);
+
+ sqlite3_free((char*)azCol);
+ return pSession->rc;
+}
+
+/*
+** Session-change object (*pp) contains an old.* record with fewer than
+** nCol fields. This function updates it with the default values for
+** the missing fields.
+*/
+static void sessionUpdateOneChange(
+ sqlite3_session *pSession, /* For memory accounting */
+ int *pRc, /* IN/OUT: Error code */
+ SessionChange **pp, /* IN/OUT: Change object to update */
+ int nCol, /* Number of columns now in table */
+ sqlite3_stmt *pDflt /* SELECT <default-values...> */
+){
+ SessionChange *pOld = *pp;
+
+ while( pOld->nRecordField<nCol ){
+ SessionChange *pNew = 0;
+ int nByte = 0;
+ int nIncr = 0;
+ int iField = pOld->nRecordField;
+ int eType = sqlite3_column_type(pDflt, iField);
+ switch( eType ){
+ case SQLITE_NULL:
+ nIncr = 1;
+ break;
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT:
+ nIncr = 9;
+ break;
+ default: {
+ int n = sqlite3_column_bytes(pDflt, iField);
+ nIncr = 1 + sessionVarintLen(n) + n;
+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
+ break;
+ }
+ }
+
+ nByte = nIncr + (sizeof(SessionChange) + pOld->nRecord);
+ pNew = sessionMalloc64(pSession, nByte);
+ if( pNew==0 ){
+ *pRc = SQLITE_NOMEM;
+ return;
+ }else{
+ memcpy(pNew, pOld, sizeof(SessionChange));
+ pNew->aRecord = (u8*)&pNew[1];
+ memcpy(pNew->aRecord, pOld->aRecord, pOld->nRecord);
+ pNew->aRecord[pNew->nRecord++] = (u8)eType;
+ switch( eType ){
+ case SQLITE_INTEGER: {
+ i64 iVal = sqlite3_column_int64(pDflt, iField);
+ sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
+ pNew->nRecord += 8;
+ break;
+ }
+
+ case SQLITE_FLOAT: {
+ double rVal = sqlite3_column_double(pDflt, iField);
+ i64 iVal = 0;
+ memcpy(&iVal, &rVal, sizeof(rVal));
+ sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
+ pNew->nRecord += 8;
+ break;
+ }
+
+ case SQLITE_TEXT: {
+ int n = sqlite3_column_bytes(pDflt, iField);
+ const char *z = (const char*)sqlite3_column_text(pDflt, iField);
+ pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n);
+ memcpy(&pNew->aRecord[pNew->nRecord], z, n);
+ pNew->nRecord += n;
+ break;
+ }
+
+ case SQLITE_BLOB: {
+ int n = sqlite3_column_bytes(pDflt, iField);
+ const u8 *z = (const u8*)sqlite3_column_blob(pDflt, iField);
+ pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n);
+ memcpy(&pNew->aRecord[pNew->nRecord], z, n);
+ pNew->nRecord += n;
+ break;
+ }
+
+ default:
+ assert( eType==SQLITE_NULL );
+ break;
+ }
+
+ sessionFree(pSession, pOld);
+ *pp = pOld = pNew;
+ pNew->nRecordField++;
+ pNew->nMaxSize += nIncr;
+ if( pSession ){
+ pSession->nMaxChangesetSize += nIncr;
+ }
+ }
+ }
+}
+
+/*
+** Ensure that there is room in the buffer to append nByte bytes of data.
+** If not, use sqlite3_realloc() to grow the buffer so that there is.
+**
+** If successful, return zero. Otherwise, if an OOM condition is encountered,
+** set *pRc to SQLITE_NOMEM and return non-zero.
+*/
+static int sessionBufferGrow(SessionBuffer *p, i64 nByte, int *pRc){
+#define SESSION_MAX_BUFFER_SZ (0x7FFFFF00 - 1)
+ i64 nReq = p->nBuf + nByte;
+ if( *pRc==SQLITE_OK && nReq>p->nAlloc ){
+ u8 *aNew;
+ i64 nNew = p->nAlloc ? p->nAlloc : 128;
+
+ do {
+ nNew = nNew*2;
+ }while( nNew<nReq );
+
+ /* The value of SESSION_MAX_BUFFER_SZ is copied from the implementation
+ ** of sqlite3_realloc64(). Allocations greater than this size in bytes
+ ** always fail. It is used here to ensure that this routine can always
+ ** allocate up to this limit - instead of up to the largest power of
+ ** two smaller than the limit. */
+ if( nNew>SESSION_MAX_BUFFER_SZ ){
+ nNew = SESSION_MAX_BUFFER_SZ;
+ if( nNew<nReq ){
+ *pRc = SQLITE_NOMEM;
+ return 1;
+ }
+ }
+
+ aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
+ if( 0==aNew ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ p->aBuf = aNew;
+ p->nAlloc = nNew;
+ }
+ }
+ return (*pRc!=SQLITE_OK);
+}
+
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a string to the buffer. All bytes in the string
+** up to (but not including) the nul-terminator are written to the buffer.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendStr(
+ SessionBuffer *p,
+ const char *zStr,
+ int *pRc
+){
+ int nStr = sqlite3Strlen30(zStr);
+ if( 0==sessionBufferGrow(p, nStr+1, pRc) ){
+ memcpy(&p->aBuf[p->nBuf], zStr, nStr);
+ p->nBuf += nStr;
+ p->aBuf[p->nBuf] = 0x00;
+ }
+}
+
+/*
+** Format a string using printf() style formatting and then append it to the
+** buffer using sessionAppendString().
+*/
+static void sessionAppendPrintf(
+ SessionBuffer *p, /* Buffer to append to */
+ int *pRc,
+ const char *zFmt,
+ ...
+){
+ if( *pRc==SQLITE_OK ){
+ char *zApp = 0;
+ va_list ap;
+ va_start(ap, zFmt);
+ zApp = sqlite3_vmprintf(zFmt, ap);
+ if( zApp==0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ sessionAppendStr(p, zApp, pRc);
+ }
+ va_end(ap);
+ sqlite3_free(zApp);
+ }
+}
+
+/*
+** Prepare a statement against database handle db that SELECTs a single
+** row containing the default values for each column in table pTab. For
+** example, if pTab is declared as:
+**
+** CREATE TABLE pTab(a PRIMARY KEY, b DEFAULT 123, c DEFAULT 'abcd');
+**
+** Then this function prepares and returns the SQL statement:
+**
+** SELECT NULL, 123, 'abcd';
+*/
+static int sessionPrepareDfltStmt(
+ sqlite3 *db, /* Database handle */
+ SessionTable *pTab, /* Table to prepare statement for */
+ sqlite3_stmt **ppStmt /* OUT: Statement handle */
+){
+ SessionBuffer sql = {0,0,0};
+ int rc = SQLITE_OK;
+ const char *zSep = " ";
+ int ii = 0;
+
+ *ppStmt = 0;
+ sessionAppendPrintf(&sql, &rc, "SELECT");
+ for(ii=0; ii<pTab->nCol; ii++){
+ const char *zDflt = pTab->azDflt[ii] ? pTab->azDflt[ii] : "NULL";
+ sessionAppendPrintf(&sql, &rc, "%s%s", zSep, zDflt);
+ zSep = ", ";
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare_v2(db, (const char*)sql.aBuf, -1, ppStmt, 0);
+ }
+ sqlite3_free(sql.aBuf);
+
+ return rc;
+}
+
+/*
+** Table pTab has one or more existing change-records with old.* records
+** with fewer than pTab->nCol columns. This function updates all such
+** change-records with the default values for the missing columns.
+*/
+static int sessionUpdateChanges(sqlite3_session *pSession, SessionTable *pTab){
+ sqlite3_stmt *pStmt = 0;
+ int rc = pSession->rc;
+
+ rc = sessionPrepareDfltStmt(pSession->db, pTab, &pStmt);
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ int ii = 0;
+ SessionChange **pp = 0;
+ for(ii=0; ii<pTab->nChange; ii++){
+ for(pp=&pTab->apChange[ii]; *pp; pp=&((*pp)->pNext)){
+ if( (*pp)->nRecordField!=pTab->nCol ){
+ sessionUpdateOneChange(pSession, &rc, pp, pTab->nCol, pStmt);
+ }
+ }
+ }
+ }
+
+ pSession->rc = rc;
+ rc = sqlite3_finalize(pStmt);
+ if( pSession->rc==SQLITE_OK ) pSession->rc = rc;
+ return pSession->rc;
}
/*
@@ -194005,9 +224553,111 @@ static int sessionStat1Depth(void *pCtx){
return p->hook.xDepth(p->hook.pCtx);
}
+static int sessionUpdateMaxSize(
+ int op,
+ sqlite3_session *pSession, /* Session object pTab is attached to */
+ SessionTable *pTab, /* Table that change applies to */
+ SessionChange *pC /* Update pC->nMaxSize */
+){
+ i64 nNew = 2;
+ if( pC->op==SQLITE_INSERT ){
+ if( pTab->bRowid ) nNew += 9;
+ if( op!=SQLITE_DELETE ){
+ int ii;
+ for(ii=0; ii<pTab->nCol; ii++){
+ sqlite3_value *p = 0;
+ pSession->hook.xNew(pSession->hook.pCtx, ii, &p);
+ sessionSerializeValue(0, p, &nNew);
+ }
+ }
+ }else if( op==SQLITE_DELETE ){
+ nNew += pC->nRecord;
+ if( sqlite3_preupdate_blobwrite(pSession->db)>=0 ){
+ nNew += pC->nRecord;
+ }
+ }else{
+ int ii;
+ u8 *pCsr = pC->aRecord;
+ if( pTab->bRowid ){
+ nNew += 9 + 1;
+ pCsr += 9;
+ }
+ for(ii=pTab->bRowid; ii<pTab->nCol; ii++){
+ int bChanged = 1;
+ int nOld = 0;
+ int eType;
+ sqlite3_value *p = 0;
+ pSession->hook.xNew(pSession->hook.pCtx, ii-pTab->bRowid, &p);
+ if( p==0 ){
+ return SQLITE_NOMEM;
+ }
+
+ eType = *pCsr++;
+ switch( eType ){
+ case SQLITE_NULL:
+ bChanged = sqlite3_value_type(p)!=SQLITE_NULL;
+ break;
+
+ case SQLITE_FLOAT:
+ case SQLITE_INTEGER: {
+ if( eType==sqlite3_value_type(p) ){
+ sqlite3_int64 iVal = sessionGetI64(pCsr);
+ if( eType==SQLITE_INTEGER ){
+ bChanged = (iVal!=sqlite3_value_int64(p));
+ }else{
+ double dVal;
+ memcpy(&dVal, &iVal, 8);
+ bChanged = (dVal!=sqlite3_value_double(p));
+ }
+ }
+ nOld = 8;
+ pCsr += 8;
+ break;
+ }
+
+ default: {
+ int nByte;
+ nOld = sessionVarintGet(pCsr, &nByte);
+ pCsr += nOld;
+ nOld += nByte;
+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
+ if( eType==sqlite3_value_type(p)
+ && nByte==sqlite3_value_bytes(p)
+ && (nByte==0 || 0==memcmp(pCsr, sqlite3_value_blob(p), nByte))
+ ){
+ bChanged = 0;
+ }
+ pCsr += nByte;
+ break;
+ }
+ }
+
+ if( bChanged && pTab->abPK[ii] ){
+ nNew = pC->nRecord + 2;
+ break;
+ }
+
+ if( bChanged ){
+ nNew += 1 + nOld;
+ sessionSerializeValue(0, p, &nNew);
+ }else if( pTab->abPK[ii] ){
+ nNew += 2 + nOld;
+ }else{
+ nNew += 2;
+ }
+ }
+ }
+
+ if( nNew>pC->nMaxSize ){
+ int nIncr = nNew - pC->nMaxSize;
+ pC->nMaxSize = nNew;
+ pSession->nMaxChangesetSize += nIncr;
+ }
+ return SQLITE_OK;
+}
/*
-** This function is only called from with a pre-update-hook reporting a
+** This function is only called from with a pre-update-hook reporting a
** change on table pTab (attached to session pSession). The type of change
** (UPDATE, INSERT, DELETE) is specified by the first argument.
**
@@ -194016,28 +224666,35 @@ static int sessionStat1Depth(void *pCtx){
*/
static void sessionPreupdateOneChange(
int op, /* One of SQLITE_UPDATE, INSERT, DELETE */
+ i64 iRowid,
sqlite3_session *pSession, /* Session object pTab is attached to */
SessionTable *pTab /* Table that change applies to */
){
- int iHash;
- int bNull = 0;
+ int iHash;
+ int bNull = 0;
int rc = SQLITE_OK;
- SessionStat1Ctx stat1 = {0};
+ int nExpect = 0;
+ SessionStat1Ctx stat1 = {{0,0,0,0,0},0};
if( pSession->rc ) return;
/* Load table details if required */
- if( sessionInitTable(pSession, pTab) ) return;
+ if( sessionInitTable(pSession, pTab, pSession->db, pSession->zDb) ) return;
- /* Check the number of columns in this xPreUpdate call matches the
+ /* Check the number of columns in this xPreUpdate call matches the
** number of columns in the table. */
- if( pTab->nCol!=pSession->hook.xCount(pSession->hook.pCtx) ){
+ nExpect = pSession->hook.xCount(pSession->hook.pCtx);
+ if( (pTab->nCol-pTab->bRowid)<nExpect ){
+ if( sessionReinitTable(pSession, pTab) ) return;
+ if( sessionUpdateChanges(pSession, pTab) ) return;
+ }
+ if( (pTab->nCol-pTab->bRowid)!=nExpect ){
pSession->rc = SQLITE_SCHEMA;
return;
}
/* Grow the hash table if required */
- if( sessionGrowHash(0, pTab) ){
+ if( sessionGrowHash(pSession, 0, pTab) ){
pSession->rc = SQLITE_NOMEM;
return;
}
@@ -194064,30 +224721,31 @@ static void sessionPreupdateOneChange(
/* Calculate the hash-key for this change. If the primary key of the row
** includes a NULL value, exit early. Such changes are ignored by the
** session module. */
- rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull);
+ rc = sessionPreupdateHash(
+ pSession, iRowid, pTab, op==SQLITE_INSERT, &iHash, &bNull
+ );
if( rc!=SQLITE_OK ) goto error_out;
if( bNull==0 ){
/* Search the hash table for an existing record for this row. */
SessionChange *pC;
for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){
- if( sessionPreupdateEqual(pSession, pTab, pC, op) ) break;
+ if( sessionPreupdateEqual(pSession, iRowid, pTab, pC, op) ) break;
}
if( pC==0 ){
/* Create a new change object containing all the old values (if
** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK
** values (if this is an INSERT). */
- SessionChange *pChange; /* New change object */
- int nByte; /* Number of bytes to allocate */
+ sqlite3_int64 nByte; /* Number of bytes to allocate */
int i; /* Used to iterate through columns */
-
+
assert( rc==SQLITE_OK );
pTab->nEntry++;
-
+
/* Figure out how large an allocation is required */
nByte = sizeof(SessionChange);
- for(i=0; i<pTab->nCol; i++){
+ for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
sqlite3_value *p = 0;
if( op!=SQLITE_INSERT ){
TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p);
@@ -194102,52 +224760,67 @@ static void sessionPreupdateOneChange(
rc = sessionSerializeValue(0, p, &nByte);
if( rc!=SQLITE_OK ) goto error_out;
}
-
+ if( pTab->bRowid ){
+ nByte += 9; /* Size of rowid field - an integer */
+ }
+
/* Allocate the change object */
- pChange = (SessionChange *)sqlite3_malloc(nByte);
- if( !pChange ){
+ pC = (SessionChange*)sessionMalloc64(pSession, nByte);
+ if( !pC ){
rc = SQLITE_NOMEM;
goto error_out;
}else{
- memset(pChange, 0, sizeof(SessionChange));
- pChange->aRecord = (u8 *)&pChange[1];
+ memset(pC, 0, sizeof(SessionChange));
+ pC->aRecord = (u8 *)&pC[1];
}
-
+
/* Populate the change object. None of the preupdate_old(),
** preupdate_new() or SerializeValue() calls below may fail as all
** required values and encodings have already been cached in memory.
** It is not possible for an OOM to occur in this block. */
nByte = 0;
- for(i=0; i<pTab->nCol; i++){
+ if( pTab->bRowid ){
+ pC->aRecord[0] = SQLITE_INTEGER;
+ sessionPutI64(&pC->aRecord[1], iRowid);
+ nByte = 9;
+ }
+ for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
sqlite3_value *p = 0;
if( op!=SQLITE_INSERT ){
pSession->hook.xOld(pSession->hook.pCtx, i, &p);
}else if( pTab->abPK[i] ){
pSession->hook.xNew(pSession->hook.pCtx, i, &p);
}
- sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
+ sessionSerializeValue(&pC->aRecord[nByte], p, &nByte);
}
/* Add the change to the hash-table */
if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){
- pChange->bIndirect = 1;
+ pC->bIndirect = 1;
}
- pChange->nRecord = nByte;
- pChange->op = op;
- pChange->pNext = pTab->apChange[iHash];
- pTab->apChange[iHash] = pChange;
+ pC->nRecordField = pTab->nCol;
+ pC->nRecord = nByte;
+ pC->op = op;
+ pC->pNext = pTab->apChange[iHash];
+ pTab->apChange[iHash] = pC;
}else if( pC->bIndirect ){
/* If the existing change is considered "indirect", but this current
** change is "direct", mark the change object as direct. */
- if( pSession->hook.xDepth(pSession->hook.pCtx)==0
- && pSession->bIndirect==0
+ if( pSession->hook.xDepth(pSession->hook.pCtx)==0
+ && pSession->bIndirect==0
){
pC->bIndirect = 0;
}
}
+
+ assert( rc==SQLITE_OK );
+ if( pSession->bEnableSize ){
+ rc = sessionUpdateMaxSize(op, pSession, pTab, pC);
+ }
}
+
/* If an error has occurred, mark the session object as failed. */
error_out:
if( pTab->bStat1 ){
@@ -194159,7 +224832,7 @@ static void sessionPreupdateOneChange(
}
static int sessionFindTable(
- sqlite3_session *pSession,
+ sqlite3_session *pSession,
const char *zName,
SessionTable **ppTab
){
@@ -194176,11 +224849,15 @@ static int sessionFindTable(
/* If there is a table-filter configured, invoke it. If it returns 0,
** do not automatically add the new table. */
if( pSession->xTableFilter==0
- || pSession->xTableFilter(pSession->pFilterCtx, zName)
+ || pSession->xTableFilter(pSession->pFilterCtx, zName)
){
rc = sqlite3session_attach(pSession, zName);
if( rc==SQLITE_OK ){
- for(pRet=pSession->pTable; pRet->pNext; pRet=pRet->pNext);
+ pRet = pSession->pTable;
+ while( ALWAYS(pRet) && pRet->pNext ){
+ pRet = pRet->pNext;
+ }
+ assert( pRet!=0 );
assert( 0==sqlite3_strnicmp(pRet->zName, zName, nName+1) );
}
}
@@ -194207,12 +224884,14 @@ static void xPreUpdate(
int nDb = sqlite3Strlen30(zDb);
assert( sqlite3_mutex_held(db->mutex) );
+ (void)iKey1;
+ (void)iKey2;
for(pSession=(sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){
SessionTable *pTab;
- /* If this session is attached to a different database ("main", "temp"
- ** etc.), or if it is not currently enabled, there is nothing to do. Skip
+ /* If this session is attached to a different database ("main", "temp"
+ ** etc.), or if it is not currently enabled, there is nothing to do. Skip
** to the next session object attached to this database. */
if( pSession->bEnable==0 ) continue;
if( pSession->rc ) continue;
@@ -194221,9 +224900,10 @@ static void xPreUpdate(
pSession->rc = sessionFindTable(pSession, zName, &pTab);
if( pTab ){
assert( pSession->rc==SQLITE_OK );
- sessionPreupdateOneChange(op, pSession, pTab);
+ assert( op==SQLITE_UPDATE || iKey1==iKey2 );
+ sessionPreupdateOneChange(op, iKey1, pSession, pTab);
if( op==SQLITE_UPDATE ){
- sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab);
+ sessionPreupdateOneChange(SQLITE_INSERT, iKey2, pSession, pTab);
}
}
}
@@ -194262,6 +224942,7 @@ static void sessionPreupdateHooks(
typedef struct SessionDiffCtx SessionDiffCtx;
struct SessionDiffCtx {
sqlite3_stmt *pStmt;
+ int bRowid;
int nOldOff;
};
@@ -194270,19 +224951,20 @@ struct SessionDiffCtx {
*/
static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){
SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
- *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff);
+ *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff+p->bRowid);
return SQLITE_OK;
}
static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){
SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
- *ppVal = sqlite3_column_value(p->pStmt, iVal);
+ *ppVal = sqlite3_column_value(p->pStmt, iVal+p->bRowid);
return SQLITE_OK;
}
static int sessionDiffCount(void *pCtx){
SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
- return p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt);
+ return (p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt)) - p->bRowid;
}
static int sessionDiffDepth(void *pCtx){
+ (void)pCtx;
return 0;
}
@@ -194303,7 +224985,7 @@ static void sessionDiffHooks(
static char *sessionExprComparePK(
int nCol,
- const char *zDb1, const char *zDb2,
+ const char *zDb1, const char *zDb2,
const char *zTab,
const char **azCol, u8 *abPK
){
@@ -194326,7 +225008,7 @@ static char *sessionExprComparePK(
static char *sessionExprCompareOther(
int nCol,
- const char *zDb1, const char *zDb2,
+ const char *zDb1, const char *zDb2,
const char *zTab,
const char **azCol, u8 *abPK
){
@@ -194356,17 +225038,18 @@ static char *sessionExprCompareOther(
}
static char *sessionSelectFindNew(
- int nCol,
const char *zDb1, /* Pick rows in this db only */
const char *zDb2, /* But not in this one */
+ int bRowid,
const char *zTbl, /* Table name */
const char *zExpr
){
+ const char *zSel = (bRowid ? SESSIONS_ROWID ", *" : "*");
char *zRet = sqlite3_mprintf(
- "SELECT * FROM \"%w\".\"%w\" WHERE NOT EXISTS ("
+ "SELECT %s FROM \"%w\".\"%w\" WHERE NOT EXISTS ("
" SELECT 1 FROM \"%w\".\"%w\" WHERE %s"
")",
- zDb1, zTbl, zDb2, zTbl, zExpr
+ zSel, zDb1, zTbl, zDb2, zTbl, zExpr
);
return zRet;
}
@@ -194380,7 +225063,9 @@ static int sessionDiffFindNew(
char *zExpr
){
int rc = SQLITE_OK;
- char *zStmt = sessionSelectFindNew(pTab->nCol, zDb1, zDb2, pTab->zName,zExpr);
+ char *zStmt = sessionSelectFindNew(
+ zDb1, zDb2, pTab->bRowid, pTab->zName, zExpr
+ );
if( zStmt==0 ){
rc = SQLITE_NOMEM;
@@ -194391,8 +225076,10 @@ static int sessionDiffFindNew(
SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
pDiffCtx->pStmt = pStmt;
pDiffCtx->nOldOff = 0;
+ pDiffCtx->bRowid = pTab->bRowid;
while( SQLITE_ROW==sqlite3_step(pStmt) ){
- sessionPreupdateOneChange(op, pSession, pTab);
+ i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0);
+ sessionPreupdateOneChange(op, iRowid, pSession, pTab);
}
rc = sqlite3_finalize(pStmt);
}
@@ -194402,10 +225089,31 @@ static int sessionDiffFindNew(
return rc;
}
+/*
+** Return a comma-separated list of the fully-qualified (with both database
+** and table name) column names from table pTab. e.g.
+**
+** "main"."t1"."a", "main"."t1"."b", "main"."t1"."c"
+*/
+static char *sessionAllCols(
+ const char *zDb,
+ SessionTable *pTab
+){
+ int ii;
+ char *zRet = 0;
+ for(ii=0; ii<pTab->nCol; ii++){
+ zRet = sqlite3_mprintf("%z%s\"%w\".\"%w\".\"%w\"",
+ zRet, (zRet ? ", " : ""), zDb, pTab->zName, pTab->azCol[ii]
+ );
+ if( !zRet ) break;
+ }
+ return zRet;
+}
+
static int sessionDiffFindModified(
- sqlite3_session *pSession,
- SessionTable *pTab,
- const char *zFrom,
+ sqlite3_session *pSession,
+ SessionTable *pTab,
+ const char *zFrom,
const char *zExpr
){
int rc = SQLITE_OK;
@@ -194416,11 +225124,13 @@ static int sessionDiffFindModified(
if( zExpr2==0 ){
rc = SQLITE_NOMEM;
}else{
+ char *z1 = sessionAllCols(pSession->zDb, pTab);
+ char *z2 = sessionAllCols(zFrom, pTab);
char *zStmt = sqlite3_mprintf(
- "SELECT * FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)",
- pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2
+ "SELECT %s,%s FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)",
+ z1, z2, pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2
);
- if( zStmt==0 ){
+ if( zStmt==0 || z1==0 || z2==0 ){
rc = SQLITE_NOMEM;
}else{
sqlite3_stmt *pStmt;
@@ -194431,12 +225141,15 @@ static int sessionDiffFindModified(
pDiffCtx->pStmt = pStmt;
pDiffCtx->nOldOff = pTab->nCol;
while( SQLITE_ROW==sqlite3_step(pStmt) ){
- sessionPreupdateOneChange(SQLITE_UPDATE, pSession, pTab);
+ i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0);
+ sessionPreupdateOneChange(SQLITE_UPDATE, iRowid, pSession, pTab);
}
rc = sqlite3_finalize(pStmt);
}
- sqlite3_free(zStmt);
}
+ sqlite3_free(zStmt);
+ sqlite3_free(z1);
+ sqlite3_free(z2);
}
return rc;
@@ -194465,7 +225178,7 @@ SQLITE_API int sqlite3session_diff(
/* Locate and if necessary initialize the target table object */
rc = sessionFindTable(pSession, zTbl, &pTo);
if( pTo==0 ) goto diff_out;
- if( sessionInitTable(pSession, pTo) ){
+ if( sessionInitTable(pSession, pTo, pSession->db, pSession->zDb) ){
rc = pSession->rc;
goto diff_out;
}
@@ -194475,9 +225188,12 @@ SQLITE_API int sqlite3session_diff(
int bHasPk = 0;
int bMismatch = 0;
int nCol; /* Columns in zFrom.zTbl */
+ int bRowid = 0;
u8 *abPK;
const char **azCol = 0;
- rc = sessionTableInfo(db, zFrom, zTbl, &nCol, 0, &azCol, &abPK);
+ rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, 0, &abPK,
+ pSession->bImplicitPK ? &bRowid : 0
+ );
if( rc==SQLITE_OK ){
if( pTo->nCol!=nCol ){
bMismatch = 1;
@@ -194492,7 +225208,9 @@ SQLITE_API int sqlite3session_diff(
}
sqlite3_free((char*)azCol);
if( bMismatch ){
- *pzErrMsg = sqlite3_mprintf("table schemas do not match");
+ if( pzErrMsg ){
+ *pzErrMsg = sqlite3_mprintf("table schemas do not match");
+ }
rc = SQLITE_SCHEMA;
}
if( bHasPk==0 ){
@@ -194502,7 +225220,7 @@ SQLITE_API int sqlite3session_diff(
}
if( rc==SQLITE_OK ){
- zExpr = sessionExprComparePK(pTo->nCol,
+ zExpr = sessionExprComparePK(pTo->nCol,
zDb, zFrom, pTo->zName, pTo->azCol, pTo->abPK
);
}
@@ -194548,7 +225266,7 @@ SQLITE_API int sqlite3session_create(
*ppSession = 0;
/* Allocate and populate the new session object. */
- pNew = (sqlite3_session *)sqlite3_malloc(sizeof(sqlite3_session) + nDb + 1);
+ pNew = (sqlite3_session *)sqlite3_malloc64(sizeof(sqlite3_session) + nDb + 1);
if( !pNew ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(sqlite3_session));
pNew->db = db;
@@ -194557,7 +225275,7 @@ SQLITE_API int sqlite3session_create(
memcpy(pNew->zDb, zDb, nDb+1);
sessionPreupdateHooks(pNew);
- /* Add the new session object to the linked list of session objects
+ /* Add the new session object to the linked list of session objects
** attached to database handle $db. Do this under the cover of the db
** handle mutex. */
sqlite3_mutex_enter(sqlite3_db_mutex(db));
@@ -194573,7 +225291,7 @@ SQLITE_API int sqlite3session_create(
** Free the list of table objects passed as the first argument. The contents
** of the changed-rows hash tables are also deleted.
*/
-static void sessionDeleteTable(SessionTable *pList){
+static void sessionDeleteTable(sqlite3_session *pSession, SessionTable *pList){
SessionTable *pNext;
SessionTable *pTab;
@@ -194585,12 +225303,13 @@ static void sessionDeleteTable(SessionTable *pList){
SessionChange *pNextChange;
for(p=pTab->apChange[i]; p; p=pNextChange){
pNextChange = p->pNext;
- sqlite3_free(p);
+ sessionFree(pSession, p);
}
}
- sqlite3_free((char*)pTab->azCol); /* cast works around VC++ bug */
- sqlite3_free(pTab->apChange);
- sqlite3_free(pTab);
+ sqlite3_finalize(pTab->pDfltStmt);
+ sessionFree(pSession, (char*)pTab->azCol); /* cast works around VC++ bug */
+ sessionFree(pSession, pTab->apChange);
+ sessionFree(pSession, pTab);
}
}
@@ -194616,11 +225335,11 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession){
sqlite3_mutex_leave(sqlite3_db_mutex(db));
sqlite3ValueFree(pSession->pZeroBlob);
- /* Delete all attached table objects. And the contents of their
+ /* Delete all attached table objects. And the contents of their
** associated hash-tables. */
- sessionDeleteTable(pSession->pTable);
+ sessionDeleteTable(pSession, pSession->pTable);
- /* Free the session object itself. */
+ /* Free the session object. */
sqlite3_free(pSession);
}
@@ -194628,7 +225347,7 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession){
** Set a table filter on a Session Object.
*/
SQLITE_API void sqlite3session_table_filter(
- sqlite3_session *pSession,
+ sqlite3_session *pSession,
int(*xFilter)(void*, const char*),
void *pCtx /* First argument passed to xFilter */
){
@@ -194667,12 +225386,13 @@ SQLITE_API int sqlite3session_attach(
if( !pTab ){
/* Allocate new SessionTable object. */
- pTab = (SessionTable *)sqlite3_malloc(sizeof(SessionTable) + nName + 1);
+ int nByte = sizeof(SessionTable) + nName + 1;
+ pTab = (SessionTable*)sessionMalloc64(pSession, nByte);
if( !pTab ){
rc = SQLITE_NOMEM;
}else{
/* Populate the new SessionTable object and link it into the list.
- ** The new object must be linked onto the end of the list, not
+ ** The new object must be linked onto the end of the list, not
** simply added to the start of it in order to ensure that tables
** appear in the correct order when a changeset or patchset is
** eventually generated. */
@@ -194691,32 +225411,6 @@ SQLITE_API int sqlite3session_attach(
}
/*
-** Ensure that there is room in the buffer to append nByte bytes of data.
-** If not, use sqlite3_realloc() to grow the buffer so that there is.
-**
-** If successful, return zero. Otherwise, if an OOM condition is encountered,
-** set *pRc to SQLITE_NOMEM and return non-zero.
-*/
-static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){
- if( *pRc==SQLITE_OK && p->nAlloc-p->nBuf<nByte ){
- u8 *aNew;
- i64 nNew = p->nAlloc ? p->nAlloc : 128;
- do {
- nNew = nNew*2;
- }while( (nNew-p->nBuf)<nByte );
-
- aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
- if( 0==aNew ){
- *pRc = SQLITE_NOMEM;
- }else{
- p->aBuf = aNew;
- p->nAlloc = nNew;
- }
- }
- return (*pRc!=SQLITE_OK);
-}
-
-/*
** Append the value passed as the second argument to the buffer passed
** as the first.
**
@@ -194727,7 +225421,7 @@ static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){
static void sessionAppendValue(SessionBuffer *p, sqlite3_value *pVal, int *pRc){
int rc = *pRc;
if( rc==SQLITE_OK ){
- int nByte = 0;
+ sqlite3_int64 nByte = 0;
rc = sessionSerializeValue(0, pVal, &nByte);
sessionBufferGrow(p, nByte, &rc);
if( rc==SQLITE_OK ){
@@ -194740,8 +225434,8 @@ static void sessionAppendValue(SessionBuffer *p, sqlite3_value *pVal, int *pRc){
}
/*
-** This function is a no-op if *pRc is other than SQLITE_OK when it is
-** called. Otherwise, append a single byte to the buffer.
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a single byte to the buffer.
**
** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
** returning.
@@ -194753,8 +225447,8 @@ static void sessionAppendByte(SessionBuffer *p, u8 v, int *pRc){
}
/*
-** This function is a no-op if *pRc is other than SQLITE_OK when it is
-** called. Otherwise, append a single varint to the buffer.
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a single varint to the buffer.
**
** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
** returning.
@@ -194766,16 +225460,16 @@ static void sessionAppendVarint(SessionBuffer *p, int v, int *pRc){
}
/*
-** This function is a no-op if *pRc is other than SQLITE_OK when it is
-** called. Otherwise, append a blob of data to the buffer.
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a blob of data to the buffer.
**
** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
** returning.
*/
static void sessionAppendBlob(
- SessionBuffer *p,
- const u8 *aBlob,
- int nBlob,
+ SessionBuffer *p,
+ const u8 *aBlob,
+ int nBlob,
int *pRc
){
if( nBlob>0 && 0==sessionBufferGrow(p, nBlob, pRc) ){
@@ -194785,27 +225479,7 @@ static void sessionAppendBlob(
}
/*
-** This function is a no-op if *pRc is other than SQLITE_OK when it is
-** called. Otherwise, append a string to the buffer. All bytes in the string
-** up to (but not including) the nul-terminator are written to the buffer.
-**
-** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
-** returning.
-*/
-static void sessionAppendStr(
- SessionBuffer *p,
- const char *zStr,
- int *pRc
-){
- int nStr = sqlite3Strlen30(zStr);
- if( 0==sessionBufferGrow(p, nStr, pRc) ){
- memcpy(&p->aBuf[p->nBuf], zStr, nStr);
- p->nBuf += nStr;
- }
-}
-
-/*
-** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. Otherwise, append the string representation of integer iVal
** to the buffer. No nul-terminator is written.
**
@@ -194823,9 +225497,9 @@ static void sessionAppendInteger(
}
/*
-** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. Otherwise, append the string zStr enclosed in quotes (") and
-** with any embedded quote characters escaped to the buffer. No
+** with any embedded quote characters escaped to the buffer. No
** nul-terminator byte is written.
**
** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
@@ -194836,7 +225510,7 @@ static void sessionAppendIdent(
const char *zStr, /* String to quote, escape and append */
int *pRc /* IN/OUT: Error code */
){
- int nStr = sqlite3Strlen30(zStr)*2 + 2 + 1;
+ int nStr = sqlite3Strlen30(zStr)*2 + 2 + 2;
if( 0==sessionBufferGrow(p, nStr, pRc) ){
char *zOut = (char *)&p->aBuf[p->nBuf];
const char *zIn = zStr;
@@ -194847,6 +225521,7 @@ static void sessionAppendIdent(
}
*zOut++ = '"';
p->nBuf = (int)((u8 *)zOut - p->aBuf);
+ p->aBuf[p->nBuf] = 0x00;
}
}
@@ -194898,8 +225573,8 @@ static void sessionAppendCol(
/*
**
-** This function appends an update change to the buffer (see the comments
-** under "CHANGESET FORMAT" at the top of the file). An update change
+** This function appends an update change to the buffer (see the comments
+** under "CHANGESET FORMAT" at the top of the file). An update change
** consists of:
**
** 1 byte: SQLITE_UPDATE (0x17)
@@ -194914,10 +225589,10 @@ static void sessionAppendCol(
** If all of the old.* values are equal to their corresponding new.* value
** (i.e. nothing has changed), then no data at all is appended to the buffer.
**
-** Otherwise, the old.* record contains all primary key values and the
-** original values of any fields that have been modified. The new.* record
+** Otherwise, the old.* record contains all primary key values and the
+** original values of any fields that have been modified. The new.* record
** contains the new values of only those fields that have been modified.
-*/
+*/
static int sessionAppendUpdate(
SessionBuffer *pBuf, /* Buffer to append to */
int bPatchset, /* True for "patchset", 0 for "changeset" */
@@ -194932,6 +225607,7 @@ static int sessionAppendUpdate(
int i; /* Used to iterate through columns */
u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */
+ assert( abPK!=0 );
sessionAppendByte(pBuf, SQLITE_UPDATE, &rc);
sessionAppendByte(pBuf, p->bIndirect, &rc);
for(i=0; i<sqlite3_column_count(pStmt); i++){
@@ -194968,8 +225644,8 @@ static int sessionAppendUpdate(
int nHdr = 1 + sessionVarintGet(&pCsr[1], &n);
assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
nAdvance = nHdr + n;
- if( eType==sqlite3_column_type(pStmt, i)
- && n==sqlite3_column_bytes(pStmt, i)
+ if( eType==sqlite3_column_type(pStmt, i)
+ && n==sqlite3_column_bytes(pStmt, i)
&& (n==0 || 0==memcmp(&pCsr[nHdr], sqlite3_column_blob(pStmt, i), n))
){
break;
@@ -194981,7 +225657,7 @@ static int sessionAppendUpdate(
/* If at least one field has been modified, this is not a no-op. */
if( bChanged ) bNoop = 0;
- /* Add a field to the old.* record. This is omitted if this modules is
+ /* Add a field to the old.* record. This is omitted if this module is
** currently generating a patchset. */
if( bPatchset==0 ){
if( bChanged || abPK[i] ){
@@ -195070,12 +225746,20 @@ static int sessionAppendDelete(
** Formulate and prepare a SELECT statement to retrieve a row from table
** zTab in database zDb based on its primary key. i.e.
**
-** SELECT * FROM zDb.zTab WHERE pk1 = ? AND pk2 = ? AND ...
+** SELECT *, <noop-test> FROM zDb.zTab WHERE (pk1, pk2,...) IS (?1, ?2,...)
+**
+** where <noop-test> is:
+**
+** 1 AND (?A OR ?1 IS <column>) AND ...
+**
+** for each non-pk <column>.
*/
static int sessionSelectStmt(
sqlite3 *db, /* Database handle */
+ int bIgnoreNoop,
const char *zDb, /* Database name */
const char *zTab, /* Table name */
+ int bRowid,
int nCol, /* Number of columns in table */
const char **azCol, /* Names of table columns */
u8 *abPK, /* PRIMARY KEY array */
@@ -195083,16 +225767,57 @@ static int sessionSelectStmt(
){
int rc = SQLITE_OK;
char *zSql = 0;
+ const char *zSep = "";
+ const char *zCols = bRowid ? SESSIONS_ROWID ", *" : "*";
int nSql = -1;
+ int i;
+
+ SessionBuffer nooptest = {0, 0, 0};
+ SessionBuffer pkfield = {0, 0, 0};
+ SessionBuffer pkvar = {0, 0, 0};
+
+ sessionAppendStr(&nooptest, ", 1", &rc);
if( 0==sqlite3_stricmp("sqlite_stat1", zTab) ){
+ sessionAppendStr(&nooptest, " AND (?6 OR ?3 IS stat)", &rc);
+ sessionAppendStr(&pkfield, "tbl, idx", &rc);
+ sessionAppendStr(&pkvar,
+ "?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc
+ );
+ zCols = "tbl, ?2, stat";
+ }else{
+ for(i=0; i<nCol; i++){
+ if( abPK[i] ){
+ sessionAppendStr(&pkfield, zSep, &rc);
+ sessionAppendStr(&pkvar, zSep, &rc);
+ zSep = ", ";
+ sessionAppendIdent(&pkfield, azCol[i], &rc);
+ sessionAppendPrintf(&pkvar, &rc, "?%d", i+1);
+ }else{
+ sessionAppendPrintf(&nooptest, &rc,
+ " AND (?%d OR ?%d IS %w.%w)", i+1+nCol, i+1, zTab, azCol[i]
+ );
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ zSql = sqlite3_mprintf(
+ "SELECT %s%s FROM %Q.%Q WHERE (%s) IS (%s)",
+ zCols, (bIgnoreNoop ? (char*)nooptest.aBuf : ""),
+ zDb, zTab, (char*)pkfield.aBuf, (char*)pkvar.aBuf
+ );
+ if( zSql==0 ) rc = SQLITE_NOMEM;
+ }
+
+#if 0
+ if( 0==sqlite3_stricmp("sqlite_stat1", zTab) ){
zSql = sqlite3_mprintf(
"SELECT tbl, ?2, stat FROM %Q.sqlite_stat1 WHERE tbl IS ?1 AND "
"idx IS (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", zDb
);
if( zSql==0 ) rc = SQLITE_NOMEM;
}else{
- int i;
const char *zSep = "";
SessionBuffer buf = {0, 0, 0};
@@ -195113,11 +225838,15 @@ static int sessionSelectStmt(
zSql = (char*)buf.aBuf;
nSql = buf.nBuf;
}
+#endif
if( rc==SQLITE_OK ){
rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, 0);
}
sqlite3_free(zSql);
+ sqlite3_free(nooptest.aBuf);
+ sqlite3_free(pkfield.aBuf);
+ sqlite3_free(pkvar.aBuf);
return rc;
}
@@ -195196,7 +225925,7 @@ static int sessionSelectBind(
/*
** This function is a no-op if *pRc is set to other than SQLITE_OK when it
-** is called. Otherwise, append a serialized table header (part of the binary
+** is called. Otherwise, append a serialized table header (part of the binary
** changeset format) to buffer *pBuf. If an error occurs, set *pRc to an
** SQLite error code before returning.
*/
@@ -195220,7 +225949,7 @@ static void sessionAppendTableHdr(
**
** If no error occurs, SQLITE_OK is returned and the new changeset/patchset
** stored in output variables *pnChangeset and *ppChangeset. Or, if an error
-** occurs, an SQLite error code is returned and both output variables set
+** occurs, an SQLite error code is returned and both output variables set
** to 0.
*/
static int sessionGenerateChangeset(
@@ -195236,12 +225965,14 @@ static int sessionGenerateChangeset(
SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */
int rc; /* Return code */
- assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0 ) );
+ assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) );
+ assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) );
/* Zero the output variables in case an error occurs. If this session
** object is already in the error state (sqlite3_session.rc != SQLITE_OK),
** this call will be a no-op. */
if( xOutput==0 ){
+ assert( pnChangeset!=0 && ppChangeset!=0 );
*pnChangeset = 0;
*ppChangeset = 0;
}
@@ -195255,18 +225986,16 @@ static int sessionGenerateChangeset(
for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
if( pTab->nEntry ){
const char *zName = pTab->zName;
- int nCol; /* Number of columns in table */
- u8 *abPK; /* Primary key array */
- const char **azCol = 0; /* Table columns */
int i; /* Used to iterate through hash buckets */
sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */
int nRewind = buf.nBuf; /* Initial size of write buffer */
int nNoop; /* Size of buffer after writing tbl header */
+ int nOldCol = pTab->nCol;
/* Check the table schema is still Ok. */
- rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK);
- if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){
- rc = SQLITE_SCHEMA;
+ rc = sessionReinitTable(pSession, pTab);
+ if( rc==SQLITE_OK && pTab->nCol!=nOldCol ){
+ rc = sessionUpdateChanges(pSession, pTab);
}
/* Write a table header */
@@ -195274,8 +226003,9 @@ static int sessionGenerateChangeset(
/* Build and compile a statement to execute: */
if( rc==SQLITE_OK ){
- rc = sessionSelectStmt(
- db, pSession->zDb, zName, nCol, azCol, abPK, &pSel);
+ rc = sessionSelectStmt(db, 0, pSession->zDb,
+ zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel
+ );
}
nNoop = buf.nBuf;
@@ -195283,21 +226013,22 @@ static int sessionGenerateChangeset(
SessionChange *p; /* Used to iterate through changes */
for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
- rc = sessionSelectBind(pSel, nCol, abPK, p);
+ rc = sessionSelectBind(pSel, pTab->nCol, pTab->abPK, p);
if( rc!=SQLITE_OK ) continue;
if( sqlite3_step(pSel)==SQLITE_ROW ){
if( p->op==SQLITE_INSERT ){
int iCol;
sessionAppendByte(&buf, SQLITE_INSERT, &rc);
sessionAppendByte(&buf, p->bIndirect, &rc);
- for(iCol=0; iCol<nCol; iCol++){
+ for(iCol=0; iCol<pTab->nCol; iCol++){
sessionAppendCol(&buf, pSel, iCol, &rc);
}
}else{
- rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK);
+ assert( pTab->abPK!=0 );
+ rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, pTab->abPK);
}
}else if( p->op!=SQLITE_INSERT ){
- rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK);
+ rc = sessionAppendDelete(&buf, bPatchset, p, pTab->nCol,pTab->abPK);
}
if( rc==SQLITE_OK ){
rc = sqlite3_reset(pSel);
@@ -195305,10 +226036,10 @@ static int sessionGenerateChangeset(
/* If the buffer is now larger than sessions_strm_chunk_size, pass
** its contents to the xOutput() callback. */
- if( xOutput
- && rc==SQLITE_OK
- && buf.nBuf>nNoop
- && buf.nBuf>sessions_strm_chunk_size
+ if( xOutput
+ && rc==SQLITE_OK
+ && buf.nBuf>nNoop
+ && buf.nBuf>sessions_strm_chunk_size
){
rc = xOutput(pOut, (void*)buf.aBuf, buf.nBuf);
nNoop = -1;
@@ -195322,7 +226053,6 @@ static int sessionGenerateChangeset(
if( buf.nBuf==nNoop ){
buf.nBuf = nRewind;
}
- sqlite3_free((char*)azCol); /* cast works around VC++ bug */
}
}
@@ -195343,10 +226073,10 @@ static int sessionGenerateChangeset(
}
/*
-** Obtain a changeset object containing all changes recorded by the
+** Obtain a changeset object containing all changes recorded by the
** session object passed as the first argument.
**
-** It is the responsibility of the caller to eventually free the buffer
+** It is the responsibility of the caller to eventually free the buffer
** using sqlite3_free().
*/
SQLITE_API int sqlite3session_changeset(
@@ -195354,7 +226084,14 @@ SQLITE_API int sqlite3session_changeset(
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
void **ppChangeset /* OUT: Buffer containing changeset */
){
- return sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset);
+ int rc;
+
+ if( pnChangeset==0 || ppChangeset==0 ) return SQLITE_MISUSE;
+ rc = sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset);
+ assert( rc || pnChangeset==0
+ || pSession->bEnableSize==0 || *pnChangeset<=pSession->nMaxChangesetSize
+ );
+ return rc;
}
/*
@@ -195365,6 +226102,7 @@ SQLITE_API int sqlite3session_changeset_strm(
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
){
+ if( xOutput==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0);
}
@@ -195376,14 +226114,15 @@ SQLITE_API int sqlite3session_patchset_strm(
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
){
+ if( xOutput==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0);
}
/*
-** Obtain a patchset object containing all changes recorded by the
+** Obtain a patchset object containing all changes recorded by the
** session object passed as the first argument.
**
-** It is the responsibility of the caller to eventually free the buffer
+** It is the responsibility of the caller to eventually free the buffer
** using sqlite3_free().
*/
SQLITE_API int sqlite3session_patchset(
@@ -195391,6 +226130,7 @@ SQLITE_API int sqlite3session_patchset(
int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */
void **ppPatchset /* OUT: Buffer containing changeset */
){
+ if( pnPatchset==0 || ppPatchset==0 ) return SQLITE_MISUSE;
return sessionGenerateChangeset(pSession, 1, 0, 0, pnPatchset, ppPatchset);
}
@@ -195440,6 +226180,59 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession){
}
/*
+** Return the amount of heap memory in use.
+*/
+SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession){
+ return pSession->nMalloc;
+}
+
+/*
+** Configure the session object passed as the first argument.
+*/
+SQLITE_API int sqlite3session_object_config(sqlite3_session *pSession, int op, void *pArg){
+ int rc = SQLITE_OK;
+ switch( op ){
+ case SQLITE_SESSION_OBJCONFIG_SIZE: {
+ int iArg = *(int*)pArg;
+ if( iArg>=0 ){
+ if( pSession->pTable ){
+ rc = SQLITE_MISUSE;
+ }else{
+ pSession->bEnableSize = (iArg!=0);
+ }
+ }
+ *(int*)pArg = pSession->bEnableSize;
+ break;
+ }
+
+ case SQLITE_SESSION_OBJCONFIG_ROWID: {
+ int iArg = *(int*)pArg;
+ if( iArg>=0 ){
+ if( pSession->pTable ){
+ rc = SQLITE_MISUSE;
+ }else{
+ pSession->bImplicitPK = (iArg!=0);
+ }
+ }
+ *(int*)pArg = pSession->bImplicitPK;
+ break;
+ }
+
+ default:
+ rc = SQLITE_MISUSE;
+ }
+
+ return rc;
+}
+
+/*
+** Return the maximum size of sqlite3session_changeset() output.
+*/
+SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession){
+ return pSession->nMaxChangesetSize;
+}
+
+/*
** Do the work for either sqlite3changeset_start() or start_strm().
*/
static int sessionChangesetStart(
@@ -195448,7 +226241,8 @@ static int sessionChangesetStart(
void *pIn,
int nChangeset, /* Size of buffer pChangeset in bytes */
void *pChangeset, /* Pointer to buffer containing changeset */
- int bInvert /* True to invert changeset */
+ int bInvert, /* True to invert changeset */
+ int bSkipEmpty /* True to skip empty UPDATE changes */
){
sqlite3_changeset_iter *pRet; /* Iterator to return */
int nByte; /* Number of bytes to allocate for iterator */
@@ -195469,6 +226263,7 @@ static int sessionChangesetStart(
pRet->in.pIn = pIn;
pRet->in.bEof = (xInput ? 0 : 1);
pRet->bInvert = bInvert;
+ pRet->bSkipEmpty = bSkipEmpty;
/* Populate the output variable and return success. */
*pp = pRet;
@@ -195483,7 +226278,7 @@ SQLITE_API int sqlite3changeset_start(
int nChangeset, /* Size of buffer pChangeset in bytes */
void *pChangeset /* Pointer to buffer containing changeset */
){
- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0);
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, 0, 0);
}
SQLITE_API int sqlite3changeset_start_v2(
sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
@@ -195492,7 +226287,7 @@ SQLITE_API int sqlite3changeset_start_v2(
int flags
){
int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
- return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert);
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset, bInvert, 0);
}
/*
@@ -195503,7 +226298,7 @@ SQLITE_API int sqlite3changeset_start_strm(
int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn
){
- return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0);
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, 0, 0);
}
SQLITE_API int sqlite3changeset_start_v2_strm(
sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
@@ -195512,7 +226307,7 @@ SQLITE_API int sqlite3changeset_start_v2_strm(
int flags
){
int bInvert = !!(flags & SQLITE_CHANGESETSTART_INVERT);
- return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert);
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0, bInvert, 0);
}
/*
@@ -195589,7 +226384,7 @@ static void sessionSkipRecord(
/*
** This function sets the value of the sqlite3_value object passed as the
-** first argument to a copy of the string or blob held in the aData[]
+** first argument to a copy of the string or blob held in the aData[]
** buffer. SQLITE_OK is returned if successful, or SQLITE_NOMEM if an OOM
** error occurs.
*/
@@ -195600,10 +226395,10 @@ static int sessionValueSetStr(
u8 enc /* String encoding (0 for blobs) */
){
/* In theory this code could just pass SQLITE_TRANSIENT as the final
- ** argument to sqlite3ValueSetStr() and have the copy created
+ ** argument to sqlite3ValueSetStr() and have the copy created
** automatically. But doing so makes it difficult to detect any OOM
** error. Hence the code to create the copy externally. */
- u8 *aCopy = sqlite3_malloc(nData+1);
+ u8 *aCopy = sqlite3_malloc64((sqlite3_int64)nData+1);
if( aCopy==0 ) return SQLITE_NOMEM;
memcpy(aCopy, aData, nData);
sqlite3ValueSetStr(pVal, nData, (char*)aCopy, enc, sqlite3_free);
@@ -195638,11 +226433,14 @@ static int sessionReadRecord(
SessionInput *pIn, /* Input data */
int nCol, /* Number of values in record */
u8 *abPK, /* Array of primary key flags, or NULL */
- sqlite3_value **apOut /* Write values to this array */
+ sqlite3_value **apOut, /* Write values to this array */
+ int *pbEmpty
){
int i; /* Used to iterate through columns */
int rc = SQLITE_OK;
+ assert( pbEmpty==0 || *pbEmpty==0 );
+ if( pbEmpty ) *pbEmpty = 1;
for(i=0; i<nCol && rc==SQLITE_OK; i++){
int eType = 0; /* Type of value (SQLITE_NULL, TEXT etc.) */
if( abPK && abPK[i]==0 ) continue;
@@ -195654,6 +226452,7 @@ static int sessionReadRecord(
eType = pIn->aData[pIn->iNext++];
assert( apOut[i]==0 );
if( eType ){
+ if( pbEmpty ) *pbEmpty = 0;
apOut[i] = sqlite3ValueNew(0);
if( !apOut[i] ) rc = SQLITE_NOMEM;
}
@@ -195677,15 +226476,19 @@ static int sessionReadRecord(
}
}
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
- sqlite3_int64 v = sessionGetI64(aVal);
- if( eType==SQLITE_INTEGER ){
- sqlite3VdbeMemSetInt64(apOut[i], v);
+ if( (pIn->nData-pIn->iNext)<8 ){
+ rc = SQLITE_CORRUPT_BKPT;
}else{
- double d;
- memcpy(&d, &v, 8);
- sqlite3VdbeMemSetDouble(apOut[i], d);
+ sqlite3_int64 v = sessionGetI64(aVal);
+ if( eType==SQLITE_INTEGER ){
+ sqlite3VdbeMemSetInt64(apOut[i], v);
+ }else{
+ double d;
+ memcpy(&d, &v, 8);
+ sqlite3VdbeMemSetDouble(apOut[i], d);
+ }
+ pIn->iNext += 8;
}
- pIn->iNext += 8;
}
}
}
@@ -195701,7 +226504,7 @@ static int sessionReadRecord(
** + array of PK flags (1 byte per column),
** + table name (nul terminated).
**
-** This function ensures that all of the above is present in the input
+** This function ensures that all of the above is present in the input
** buffer (i.e. that it can be accessed without any calls to xInput()).
** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code.
** The input pointer is not moved.
@@ -195715,11 +226518,11 @@ static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){
if( rc==SQLITE_OK ){
nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol);
/* The hard upper limit for the number of columns in an SQLite
- ** database table is, according to sqliteLimit.h, 32676. So
- ** consider any table-header that purports to have more than 65536
- ** columns to be corrupt. This is convenient because otherwise,
- ** if the (nCol>65536) condition below were omitted, a sufficiently
- ** large value for nCol may cause nRead to wrap around and become
+ ** database table is, according to sqliteLimit.h, 32676. So
+ ** consider any table-header that purports to have more than 65536
+ ** columns to be corrupt. This is convenient because otherwise,
+ ** if the (nCol>65536) condition below were omitted, a sufficiently
+ ** large value for nCol may cause nRead to wrap around and become
** negative. Leading to a crash. */
if( nCol<0 || nCol>65536 ){
rc = SQLITE_CORRUPT_BKPT;
@@ -195784,8 +226587,8 @@ static int sessionChangesetBufferRecord(
** + array of PK flags (1 byte per column),
** + table name (nul terminated).
**
-** This function decodes the table-header and populates the p->nCol,
-** p->zTab and p->abPK[] variables accordingly. The p->apValue[] array is
+** This function decodes the table-header and populates the p->nCol,
+** p->zTab and p->abPK[] variables accordingly. The p->apValue[] array is
** also allocated or resized according to the new value of p->nCol. The
** input pointer is left pointing to the byte following the table header.
**
@@ -195815,44 +226618,45 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
}
if( rc==SQLITE_OK ){
- int iPK = sizeof(sqlite3_value*)*p->nCol*2;
+ size_t iPK = sizeof(sqlite3_value*)*p->nCol*2;
memset(p->tblhdr.aBuf, 0, iPK);
memcpy(&p->tblhdr.aBuf[iPK], &p->in.aData[p->in.iNext], nCopy);
p->in.iNext += nCopy;
}
p->apValue = (sqlite3_value**)p->tblhdr.aBuf;
- p->abPK = (u8*)&p->apValue[p->nCol*2];
- p->zTab = (char*)&p->abPK[p->nCol];
+ if( p->apValue==0 ){
+ p->abPK = 0;
+ p->zTab = 0;
+ }else{
+ p->abPK = (u8*)&p->apValue[p->nCol*2];
+ p->zTab = p->abPK ? (char*)&p->abPK[p->nCol] : 0;
+ }
return (p->rc = rc);
}
/*
-** Advance the changeset iterator to the next change.
+** Advance the changeset iterator to the next change. The differences between
+** this function and sessionChangesetNext() are that
**
-** If both paRec and pnRec are NULL, then this function works like the public
-** API sqlite3changeset_next(). If SQLITE_ROW is returned, then the
-** sqlite3changeset_new() and old() APIs may be used to query for values.
+** * If pbEmpty is not NULL and the change is a no-op UPDATE (an UPDATE
+** that modifies no columns), this function sets (*pbEmpty) to 1.
**
-** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change
-** record is written to *paRec before returning and the number of bytes in
-** the record to *pnRec.
-**
-** Either way, this function returns SQLITE_ROW if the iterator is
-** successfully advanced to the next change in the changeset, an SQLite
-** error code if an error occurs, or SQLITE_DONE if there are no further
-** changes in the changeset.
+** * If the iterator is configured to skip no-op UPDATEs,
+** sessionChangesetNext() does that. This function does not.
*/
-static int sessionChangesetNext(
+static int sessionChangesetNextOne(
sqlite3_changeset_iter *p, /* Changeset iterator */
u8 **paRec, /* If non-NULL, store record pointer here */
int *pnRec, /* If non-NULL, store size of record here */
- int *pbNew /* If non-NULL, true if new table */
+ int *pbNew, /* If non-NULL, true if new table */
+ int *pbEmpty
){
int i;
u8 op;
assert( (paRec==0 && pnRec==0) || (paRec && pnRec) );
+ assert( pbEmpty==0 || *pbEmpty==0 );
/* If the iterator is in the error-state, return immediately. */
if( p->rc!=SQLITE_OK ) return p->rc;
@@ -195904,7 +226708,7 @@ static int sessionChangesetNext(
return (p->rc = SQLITE_CORRUPT_BKPT);
}
- if( paRec ){
+ if( paRec ){
int nVal; /* Number of values to buffer */
if( p->bPatchset==0 && op==SQLITE_UPDATE ){
nVal = p->nCol * 2;
@@ -195925,13 +226729,13 @@ static int sessionChangesetNext(
/* If this is an UPDATE or DELETE, read the old.* record. */
if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){
u8 *abPK = p->bPatchset ? p->abPK : 0;
- p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld);
+ p->rc = sessionReadRecord(&p->in, p->nCol, abPK, apOld, 0);
if( p->rc!=SQLITE_OK ) return p->rc;
}
/* If this is an INSERT or UPDATE, read the new.* record. */
if( p->op!=SQLITE_DELETE ){
- p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew);
+ p->rc = sessionReadRecord(&p->in, p->nCol, 0, apNew, pbEmpty);
if( p->rc!=SQLITE_OK ) return p->rc;
}
@@ -195953,12 +226757,59 @@ static int sessionChangesetNext(
if( p->op==SQLITE_INSERT ) p->op = SQLITE_DELETE;
else if( p->op==SQLITE_DELETE ) p->op = SQLITE_INSERT;
}
+
+ /* If this is an UPDATE that is part of a changeset, then check that
+ ** there are no fields in the old.* record that are not (a) PK fields,
+ ** or (b) also present in the new.* record.
+ **
+ ** Such records are technically corrupt, but the rebaser was at one
+ ** point generating them. Under most circumstances this is benign, but
+ ** can cause spurious SQLITE_RANGE errors when applying the changeset. */
+ if( p->bPatchset==0 && p->op==SQLITE_UPDATE){
+ for(i=0; i<p->nCol; i++){
+ if( p->abPK[i]==0 && p->apValue[i+p->nCol]==0 ){
+ sqlite3ValueFree(p->apValue[i]);
+ p->apValue[i] = 0;
+ }
+ }
+ }
}
return SQLITE_ROW;
}
/*
+** Advance the changeset iterator to the next change.
+**
+** If both paRec and pnRec are NULL, then this function works like the public
+** API sqlite3changeset_next(). If SQLITE_ROW is returned, then the
+** sqlite3changeset_new() and old() APIs may be used to query for values.
+**
+** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change
+** record is written to *paRec before returning and the number of bytes in
+** the record to *pnRec.
+**
+** Either way, this function returns SQLITE_ROW if the iterator is
+** successfully advanced to the next change in the changeset, an SQLite
+** error code if an error occurs, or SQLITE_DONE if there are no further
+** changes in the changeset.
+*/
+static int sessionChangesetNext(
+ sqlite3_changeset_iter *p, /* Changeset iterator */
+ u8 **paRec, /* If non-NULL, store record pointer here */
+ int *pnRec, /* If non-NULL, store size of record here */
+ int *pbNew /* If non-NULL, true if new table */
+){
+ int bEmpty;
+ int rc;
+ do {
+ bEmpty = 0;
+ rc = sessionChangesetNextOne(p, paRec, pnRec, pbNew, &bEmpty);
+ }while( rc==SQLITE_ROW && p->bSkipEmpty && bEmpty);
+ return rc;
+}
+
+/*
** Advance an iterator created by sqlite3changeset_start() to the next
** change in the changeset. This function may return SQLITE_ROW, SQLITE_DONE
** or SQLITE_CORRUPT.
@@ -196071,7 +226922,7 @@ SQLITE_API int sqlite3changeset_new(
/*
** This function may only be called with a changeset iterator that has been
-** passed to an SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT
+** passed to an SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT
** conflict-handler function. Otherwise, SQLITE_MISUSE is returned.
**
** If successful, *ppValue is set to point to an sqlite3_value structure
@@ -196216,7 +227067,7 @@ static int sessionChangesetInvert(
int iCol;
if( 0==apVal ){
- apVal = (sqlite3_value **)sqlite3_malloc(sizeof(apVal[0])*nCol*2);
+ apVal = (sqlite3_value **)sqlite3_malloc64(sizeof(apVal[0])*nCol*2);
if( 0==apVal ){
rc = SQLITE_NOMEM;
goto finished_invert;
@@ -196230,9 +227081,9 @@ static int sessionChangesetInvert(
/* Read the old.* and new.* records for the update change. */
pInput->iNext += 2;
- rc = sessionReadRecord(pInput, nCol, 0, &apVal[0]);
+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[0], 0);
if( rc==SQLITE_OK ){
- rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol]);
+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol], 0);
}
/* Write the new old.* record. Consists of the PK columns from the
@@ -196276,11 +227127,11 @@ static int sessionChangesetInvert(
}
assert( rc==SQLITE_OK );
- if( pnInverted ){
+ if( pnInverted && ALWAYS(ppInverted) ){
*pnInverted = sOut.nBuf;
*ppInverted = sOut.aBuf;
sOut.aBuf = 0;
- }else if( sOut.nBuf>0 ){
+ }else if( sOut.nBuf>0 && ALWAYS(xOutput!=0) ){
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
}
@@ -196333,24 +227184,197 @@ SQLITE_API int sqlite3changeset_invert_strm(
return rc;
}
+
+typedef struct SessionUpdate SessionUpdate;
+struct SessionUpdate {
+ sqlite3_stmt *pStmt;
+ u32 *aMask;
+ SessionUpdate *pNext;
+};
+
typedef struct SessionApplyCtx SessionApplyCtx;
struct SessionApplyCtx {
sqlite3 *db;
sqlite3_stmt *pDelete; /* DELETE statement */
- sqlite3_stmt *pUpdate; /* UPDATE statement */
sqlite3_stmt *pInsert; /* INSERT statement */
sqlite3_stmt *pSelect; /* SELECT statement */
int nCol; /* Size of azCol[] and abPK[] arrays */
const char **azCol; /* Array of column names */
u8 *abPK; /* Boolean array - true if column is in PK */
+ u32 *aUpdateMask; /* Used by sessionUpdateFind */
+ SessionUpdate *pUp;
int bStat1; /* True if table is sqlite_stat1 */
int bDeferConstraints; /* True to defer constraints */
+ int bInvertConstraints; /* Invert when iterating constraints buffer */
SessionBuffer constraints; /* Deferred constraints are stored here */
SessionBuffer rebase; /* Rebase information (if any) here */
u8 bRebaseStarted; /* If table header is already in rebase */
u8 bRebase; /* True to collect rebase information */
+ u8 bIgnoreNoop; /* True to ignore no-op conflicts */
+ int bRowid;
};
+/* Number of prepared UPDATE statements to cache. */
+#define SESSION_UPDATE_CACHE_SZ 12
+
+/*
+** Find a prepared UPDATE statement suitable for the UPDATE step currently
+** being visited by the iterator. The UPDATE is of the form:
+**
+** UPDATE tbl SET col = ?, col2 = ? WHERE pk1 IS ? AND pk2 IS ?
+*/
+static int sessionUpdateFind(
+ sqlite3_changeset_iter *pIter,
+ SessionApplyCtx *p,
+ int bPatchset,
+ sqlite3_stmt **ppStmt
+){
+ int rc = SQLITE_OK;
+ SessionUpdate *pUp = 0;
+ int nCol = pIter->nCol;
+ int nU32 = (pIter->nCol+33)/32;
+ int ii;
+
+ if( p->aUpdateMask==0 ){
+ p->aUpdateMask = sqlite3_malloc(nU32*sizeof(u32));
+ if( p->aUpdateMask==0 ){
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ memset(p->aUpdateMask, 0, nU32*sizeof(u32));
+ rc = SQLITE_CORRUPT;
+ for(ii=0; ii<pIter->nCol; ii++){
+ if( sessionChangesetNew(pIter, ii) ){
+ p->aUpdateMask[ii/32] |= (1<<(ii%32));
+ rc = SQLITE_OK;
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ if( bPatchset ) p->aUpdateMask[nCol/32] |= (1<<(nCol%32));
+
+ if( p->pUp ){
+ int nUp = 0;
+ SessionUpdate **pp = &p->pUp;
+ while( 1 ){
+ nUp++;
+ if( 0==memcmp(p->aUpdateMask, (*pp)->aMask, nU32*sizeof(u32)) ){
+ pUp = *pp;
+ *pp = pUp->pNext;
+ pUp->pNext = p->pUp;
+ p->pUp = pUp;
+ break;
+ }
+
+ if( (*pp)->pNext ){
+ pp = &(*pp)->pNext;
+ }else{
+ if( nUp>=SESSION_UPDATE_CACHE_SZ ){
+ sqlite3_finalize((*pp)->pStmt);
+ sqlite3_free(*pp);
+ *pp = 0;
+ }
+ break;
+ }
+ }
+ }
+
+ if( pUp==0 ){
+ int nByte = sizeof(SessionUpdate) * nU32*sizeof(u32);
+ int bStat1 = (sqlite3_stricmp(pIter->zTab, "sqlite_stat1")==0);
+ pUp = (SessionUpdate*)sqlite3_malloc(nByte);
+ if( pUp==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ const char *zSep = "";
+ SessionBuffer buf;
+
+ memset(&buf, 0, sizeof(buf));
+ pUp->aMask = (u32*)&pUp[1];
+ memcpy(pUp->aMask, p->aUpdateMask, nU32*sizeof(u32));
+
+ sessionAppendStr(&buf, "UPDATE main.", &rc);
+ sessionAppendIdent(&buf, pIter->zTab, &rc);
+ sessionAppendStr(&buf, " SET ", &rc);
+
+ /* Create the assignments part of the UPDATE */
+ for(ii=0; ii<pIter->nCol; ii++){
+ if( p->abPK[ii]==0 && sessionChangesetNew(pIter, ii) ){
+ sessionAppendStr(&buf, zSep, &rc);
+ sessionAppendIdent(&buf, p->azCol[ii], &rc);
+ sessionAppendStr(&buf, " = ?", &rc);
+ sessionAppendInteger(&buf, ii*2+1, &rc);
+ zSep = ", ";
+ }
+ }
+
+ /* Create the WHERE clause part of the UPDATE */
+ zSep = "";
+ sessionAppendStr(&buf, " WHERE ", &rc);
+ for(ii=0; ii<pIter->nCol; ii++){
+ if( p->abPK[ii] || (bPatchset==0 && sessionChangesetOld(pIter, ii)) ){
+ sessionAppendStr(&buf, zSep, &rc);
+ if( bStat1 && ii==1 ){
+ assert( sqlite3_stricmp(p->azCol[ii], "idx")==0 );
+ sessionAppendStr(&buf,
+ "idx IS CASE "
+ "WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL "
+ "ELSE ?4 END ", &rc
+ );
+ }else{
+ sessionAppendIdent(&buf, p->azCol[ii], &rc);
+ sessionAppendStr(&buf, " IS ?", &rc);
+ sessionAppendInteger(&buf, ii*2+2, &rc);
+ }
+ zSep = " AND ";
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ char *zSql = (char*)buf.aBuf;
+ rc = sqlite3_prepare_v2(p->db, zSql, buf.nBuf, &pUp->pStmt, 0);
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pUp);
+ pUp = 0;
+ }else{
+ pUp->pNext = p->pUp;
+ p->pUp = pUp;
+ }
+ sqlite3_free(buf.aBuf);
+ }
+ }
+ }
+
+ assert( (rc==SQLITE_OK)==(pUp!=0) );
+ if( pUp ){
+ *ppStmt = pUp->pStmt;
+ }else{
+ *ppStmt = 0;
+ }
+ return rc;
+}
+
+/*
+** Free all cached UPDATE statements.
+*/
+static void sessionUpdateFree(SessionApplyCtx *p){
+ SessionUpdate *pUp;
+ SessionUpdate *pNext;
+ for(pUp=p->pUp; pUp; pUp=pNext){
+ pNext = pUp->pNext;
+ sqlite3_finalize(pUp->pStmt);
+ sqlite3_free(pUp);
+ }
+ p->pUp = 0;
+ sqlite3_free(p->aUpdateMask);
+ p->aUpdateMask = 0;
+}
+
/*
** Formulate a statement to DELETE a row from database db. Assuming a table
** structure like this:
@@ -196379,7 +227403,7 @@ static int sessionDeleteRow(
SessionBuffer buf = {0, 0, 0};
int nPk = 0;
- sessionAppendStr(&buf, "DELETE FROM ", &rc);
+ sessionAppendStr(&buf, "DELETE FROM main.", &rc);
sessionAppendIdent(&buf, zTab, &rc);
sessionAppendStr(&buf, " WHERE ", &rc);
@@ -196421,103 +227445,6 @@ static int sessionDeleteRow(
}
/*
-** Formulate and prepare a statement to UPDATE a row from database db.
-** Assuming a table structure like this:
-**
-** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
-**
-** The UPDATE statement looks like this:
-**
-** UPDATE x SET
-** a = CASE WHEN ?2 THEN ?3 ELSE a END,
-** b = CASE WHEN ?5 THEN ?6 ELSE b END,
-** c = CASE WHEN ?8 THEN ?9 ELSE c END,
-** d = CASE WHEN ?11 THEN ?12 ELSE d END
-** WHERE a = ?1 AND c = ?7 AND (?13 OR
-** (?5==0 OR b IS ?4) AND (?11==0 OR d IS ?10) AND
-** )
-**
-** For each column in the table, there are three variables to bind:
-**
-** ?(i*3+1) The old.* value of the column, if any.
-** ?(i*3+2) A boolean flag indicating that the value is being modified.
-** ?(i*3+3) The new.* value of the column, if any.
-**
-** Also, a boolean flag that, if set to true, causes the statement to update
-** a row even if the non-PK values do not match. This is required if the
-** conflict-handler is invoked with CHANGESET_DATA and returns
-** CHANGESET_REPLACE. This is variable "?(nCol*3+1)".
-**
-** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left
-** pointing to the prepared version of the SQL statement.
-*/
-static int sessionUpdateRow(
- sqlite3 *db, /* Database handle */
- const char *zTab, /* Table name */
- SessionApplyCtx *p /* Session changeset-apply context */
-){
- int rc = SQLITE_OK;
- int i;
- const char *zSep = "";
- SessionBuffer buf = {0, 0, 0};
-
- /* Append "UPDATE tbl SET " */
- sessionAppendStr(&buf, "UPDATE ", &rc);
- sessionAppendIdent(&buf, zTab, &rc);
- sessionAppendStr(&buf, " SET ", &rc);
-
- /* Append the assignments */
- for(i=0; i<p->nCol; i++){
- sessionAppendStr(&buf, zSep, &rc);
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " = CASE WHEN ?", &rc);
- sessionAppendInteger(&buf, i*3+2, &rc);
- sessionAppendStr(&buf, " THEN ?", &rc);
- sessionAppendInteger(&buf, i*3+3, &rc);
- sessionAppendStr(&buf, " ELSE ", &rc);
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " END", &rc);
- zSep = ", ";
- }
-
- /* Append the PK part of the WHERE clause */
- sessionAppendStr(&buf, " WHERE ", &rc);
- for(i=0; i<p->nCol; i++){
- if( p->abPK[i] ){
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " = ?", &rc);
- sessionAppendInteger(&buf, i*3+1, &rc);
- sessionAppendStr(&buf, " AND ", &rc);
- }
- }
-
- /* Append the non-PK part of the WHERE clause */
- sessionAppendStr(&buf, " (?", &rc);
- sessionAppendInteger(&buf, p->nCol*3+1, &rc);
- sessionAppendStr(&buf, " OR 1", &rc);
- for(i=0; i<p->nCol; i++){
- if( !p->abPK[i] ){
- sessionAppendStr(&buf, " AND (?", &rc);
- sessionAppendInteger(&buf, i*3+2, &rc);
- sessionAppendStr(&buf, "=0 OR ", &rc);
- sessionAppendIdent(&buf, p->azCol[i], &rc);
- sessionAppendStr(&buf, " IS ?", &rc);
- sessionAppendInteger(&buf, i*3+1, &rc);
- sessionAppendStr(&buf, ")", &rc);
- }
- }
- sessionAppendStr(&buf, ")", &rc);
-
- if( rc==SQLITE_OK ){
- rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0);
- }
- sqlite3_free(buf.aBuf);
-
- return rc;
-}
-
-
-/*
** Formulate and prepare an SQL statement to query table zTab by primary
** key. Assuming the following table structure:
**
@@ -196535,8 +227462,10 @@ static int sessionSelectRow(
const char *zTab, /* Table name */
SessionApplyCtx *p /* Session changeset-apply context */
){
- return sessionSelectStmt(
- db, "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect);
+ /* TODO */
+ return sessionSelectStmt(db, p->bIgnoreNoop,
+ "main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect
+ );
}
/*
@@ -196585,7 +227514,7 @@ static int sessionPrepare(sqlite3 *db, sqlite3_stmt **pp, const char *zSql){
/*
** Prepare statements for applying changes to the sqlite_stat1 table.
** These are similar to those created by sessionSelectRow(),
-** sessionInsertRow(), sessionUpdateRow() and sessionDeleteRow() for
+** sessionInsertRow(), sessionUpdateRow() and sessionDeleteRow() for
** other tables.
*/
static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
@@ -196598,17 +227527,6 @@ static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
);
}
if( rc==SQLITE_OK ){
- rc = sessionPrepare(db, &p->pUpdate,
- "UPDATE main.sqlite_stat1 SET "
- "tbl = CASE WHEN ?2 THEN ?3 ELSE tbl END, "
- "idx = CASE WHEN ?5 THEN ?6 ELSE idx END, "
- "stat = CASE WHEN ?8 THEN ?9 ELSE stat END "
- "WHERE tbl=?1 AND idx IS "
- "CASE WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL ELSE ?4 END "
- "AND (?10 OR ?8=0 OR stat IS ?7)"
- );
- }
- if( rc==SQLITE_OK ){
rc = sessionPrepare(db, &p->pDelete,
"DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS "
"CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END "
@@ -196619,7 +227537,7 @@ static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
}
/*
-** A wrapper around sqlite3_bind_value() that detects an extra problem.
+** A wrapper around sqlite3_bind_value() that detects an extra problem.
** See comments in the body of this function for details.
*/
static int sessionBindValue(
@@ -196642,15 +227560,15 @@ static int sessionBindValue(
}
/*
-** Iterator pIter must point to an SQLITE_INSERT entry. This function
+** Iterator pIter must point to an SQLITE_INSERT entry. This function
** transfers new.* values from the current iterator entry to statement
** pStmt. The table being inserted into has nCol columns.
**
-** New.* value $i from the iterator is bound to variable ($i+1) of
+** New.* value $i from the iterator is bound to variable ($i+1) of
** statement pStmt. If parameter abPK is NULL, all values from 0 to (nCol-1)
** are transfered to the statement. Otherwise, if abPK is not NULL, it points
-** to an array nCol elements in size. In this case only those values for
-** which abPK[$i] is true are read from the iterator and bound to the
+** to an array nCol elements in size. In this case only those values for
+** which abPK[$i] is true are read from the iterator and bound to the
** statement.
**
** An SQLite error code is returned if an error occurs. Otherwise, SQLITE_OK.
@@ -196666,14 +227584,14 @@ static int sessionBindRow(
int rc = SQLITE_OK;
/* Neither sqlite3changeset_old or sqlite3changeset_new can fail if the
- ** argument iterator points to a suitable entry. Make sure that xValue
- ** is one of these to guarantee that it is safe to ignore the return
+ ** argument iterator points to a suitable entry. Make sure that xValue
+ ** is one of these to guarantee that it is safe to ignore the return
** in the code below. */
assert( xValue==sqlite3changeset_old || xValue==sqlite3changeset_new );
for(i=0; rc==SQLITE_OK && i<nCol; i++){
if( !abPK || abPK[i] ){
- sqlite3_value *pVal;
+ sqlite3_value *pVal = 0;
(void)xValue(pIter, i, &pVal);
if( pVal==0 ){
/* The value in the changeset was "undefined". This indicates a
@@ -196691,36 +227609,48 @@ static int sessionBindRow(
** SQL statement pSelect is as generated by the sessionSelectRow() function.
** This function binds the primary key values from the change that changeset
** iterator pIter points to to the SELECT and attempts to seek to the table
-** entry. If a row is found, the SELECT statement left pointing at the row
+** entry. If a row is found, the SELECT statement left pointing at the row
** and SQLITE_ROW is returned. Otherwise, if no row is found and no error
** has occured, the statement is reset and SQLITE_OK is returned. If an
** error occurs, the statement is reset and an SQLite error code is returned.
**
-** If this function returns SQLITE_ROW, the caller must eventually reset()
+** If this function returns SQLITE_ROW, the caller must eventually reset()
** statement pSelect. If any other value is returned, the statement does
** not require a reset().
**
** If the iterator currently points to an INSERT record, bind values from the
** new.* record to the SELECT statement. Or, if it points to a DELETE or
-** UPDATE, bind values from the old.* record.
+** UPDATE, bind values from the old.* record.
*/
static int sessionSeekToRow(
- sqlite3 *db, /* Database handle */
sqlite3_changeset_iter *pIter, /* Changeset iterator */
- u8 *abPK, /* Primary key flags array */
- sqlite3_stmt *pSelect /* SELECT statement from sessionSelectRow() */
+ SessionApplyCtx *p
){
+ sqlite3_stmt *pSelect = p->pSelect;
int rc; /* Return code */
int nCol; /* Number of columns in table */
int op; /* Changset operation (SQLITE_UPDATE etc.) */
const char *zDummy; /* Unused */
+ sqlite3_clear_bindings(pSelect);
sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
- rc = sessionBindRow(pIter,
+ rc = sessionBindRow(pIter,
op==SQLITE_INSERT ? sqlite3changeset_new : sqlite3changeset_old,
- nCol, abPK, pSelect
+ nCol, p->abPK, pSelect
);
+ if( op!=SQLITE_DELETE && p->bIgnoreNoop ){
+ int ii;
+ for(ii=0; rc==SQLITE_OK && ii<nCol; ii++){
+ if( p->abPK[ii]==0 ){
+ sqlite3_value *pVal = 0;
+ sqlite3changeset_new(pIter, ii, &pVal);
+ sqlite3_bind_int(pSelect, ii+1+nCol, (pVal==0));
+ if( pVal ) rc = sessionBindValue(pSelect, ii+1, pVal);
+ }
+ }
+ }
+
if( rc==SQLITE_OK ){
rc = sqlite3_step(pSelect);
if( rc!=SQLITE_ROW ) rc = sqlite3_reset(pSelect);
@@ -196730,10 +227660,10 @@ static int sessionSeekToRow(
}
/*
-** This function is called from within sqlite3changset_apply_v2() when
+** This function is called from within sqlite3changeset_apply_v2() when
** a conflict is encountered and resolved using conflict resolution
** mode eType (either SQLITE_CHANGESET_OMIT or SQLITE_CHANGESET_REPLACE)..
-** It adds a conflict resolution record to the buffer in
+** It adds a conflict resolution record to the buffer in
** SessionApplyCtx.rebase, which will eventually be returned to the caller
** of apply_v2() as the "rebase" buffer.
**
@@ -196761,7 +227691,7 @@ static int sessionRebaseAdd(
assert( eType==SQLITE_CHANGESET_REPLACE||eType==SQLITE_CHANGESET_OMIT );
assert( eOp==SQLITE_DELETE || eOp==SQLITE_INSERT || eOp==SQLITE_UPDATE );
- sessionAppendByte(&p->rebase,
+ sessionAppendByte(&p->rebase,
(eOp==SQLITE_DELETE ? SQLITE_DELETE : SQLITE_INSERT), &rc
);
sessionAppendByte(&p->rebase, (eType==SQLITE_CHANGESET_REPLACE), &rc);
@@ -196809,7 +227739,7 @@ static int sessionRebaseAdd(
** is set to non-zero before returning SQLITE_OK.
**
** If the conflict handler returns SQLITE_CHANGESET_ABORT, SQLITE_ABORT is
-** returned. Or, if the conflict handler returns an invalid value,
+** returned. Or, if the conflict handler returns an invalid value,
** SQLITE_MISUSE. If the conflict handler returns SQLITE_CHANGESET_OMIT,
** this function returns SQLITE_OK.
*/
@@ -196835,16 +227765,22 @@ static int sessionConflictHandler(
/* Bind the new.* PRIMARY KEY values to the SELECT statement. */
if( pbReplace ){
- rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect);
+ rc = sessionSeekToRow(pIter, p);
}else{
rc = SQLITE_OK;
}
if( rc==SQLITE_ROW ){
/* There exists another row with the new.* primary key. */
- pIter->pConflict = p->pSelect;
- res = xConflict(pCtx, eType, pIter);
- pIter->pConflict = 0;
+ if( p->bIgnoreNoop
+ && sqlite3_column_int(p->pSelect, sqlite3_column_count(p->pSelect)-1)
+ ){
+ res = SQLITE_CHANGESET_OMIT;
+ }else{
+ pIter->pConflict = p->pSelect;
+ res = xConflict(pCtx, eType, pIter);
+ pIter->pConflict = 0;
+ }
rc = sqlite3_reset(p->pSelect);
}else if( rc==SQLITE_OK ){
if( p->bDeferConstraints && eType==SQLITE_CHANGESET_CONFLICT ){
@@ -196899,16 +227835,16 @@ static int sessionConflictHandler(
** to true before returning. In this case the caller will invoke this function
** again, this time with pbRetry set to NULL.
**
-** If argument pbReplace is NULL and a CHANGESET_CONFLICT conflict is
+** If argument pbReplace is NULL and a CHANGESET_CONFLICT conflict is
** encountered invoke the conflict handler with CHANGESET_CONSTRAINT instead.
** Or, if pbReplace is not NULL, invoke it with CHANGESET_CONFLICT. If such
** an invocation returns SQLITE_CHANGESET_REPLACE, set *pbReplace to true
** before retrying. In this case the caller attempts to remove the conflicting
-** row before invoking this function again, this time with pbReplace set
+** row before invoking this function again, this time with pbReplace set
** to NULL.
**
** If any conflict handler returns SQLITE_CHANGESET_ABORT, this function
-** returns SQLITE_ABORT. Otherwise, if no error occurs, SQLITE_OK is
+** returns SQLITE_ABORT. Otherwise, if no error occurs, SQLITE_OK is
** returned.
*/
static int sessionApplyOneOp(
@@ -196924,7 +227860,7 @@ static int sessionApplyOneOp(
int nCol;
int rc = SQLITE_OK;
- assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect );
+ assert( p->pDelete && p->pInsert && p->pSelect );
assert( p->azCol && p->abPK );
assert( !pbReplace || *pbReplace==0 );
@@ -196952,7 +227888,7 @@ static int sessionApplyOneOp(
sqlite3_step(p->pDelete);
rc = sqlite3_reset(p->pDelete);
- if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
+ if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 && p->bIgnoreNoop==0 ){
rc = sessionConflictHandler(
SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
);
@@ -196964,29 +227900,28 @@ static int sessionApplyOneOp(
}else if( op==SQLITE_UPDATE ){
int i;
+ sqlite3_stmt *pUp = 0;
+ int bPatchset = (pbRetry==0 || pIter->bPatchset);
+
+ rc = sessionUpdateFind(pIter, p, bPatchset, &pUp);
/* Bind values to the UPDATE statement. */
for(i=0; rc==SQLITE_OK && i<nCol; i++){
sqlite3_value *pOld = sessionChangesetOld(pIter, i);
sqlite3_value *pNew = sessionChangesetNew(pIter, i);
-
- sqlite3_bind_int(p->pUpdate, i*3+2, !!pNew);
- if( pOld ){
- rc = sessionBindValue(p->pUpdate, i*3+1, pOld);
+ if( p->abPK[i] || (bPatchset==0 && pOld) ){
+ rc = sessionBindValue(pUp, i*2+2, pOld);
}
if( rc==SQLITE_OK && pNew ){
- rc = sessionBindValue(p->pUpdate, i*3+3, pNew);
+ rc = sessionBindValue(pUp, i*2+1, pNew);
}
}
- if( rc==SQLITE_OK ){
- sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0 || pIter->bPatchset);
- }
if( rc!=SQLITE_OK ) return rc;
/* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict,
** the result will be SQLITE_OK with 0 rows modified. */
- sqlite3_step(p->pUpdate);
- rc = sqlite3_reset(p->pUpdate);
+ sqlite3_step(pUp);
+ rc = sqlite3_reset(pUp);
if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
/* A NOTFOUND or DATA error. Search the table to see if it contains
@@ -197008,9 +227943,9 @@ static int sessionApplyOneOp(
assert( op==SQLITE_INSERT );
if( p->bStat1 ){
/* Check if there is a conflicting row. For sqlite_stat1, this needs
- ** to be done using a SELECT, as there is no PRIMARY KEY in the
+ ** to be done using a SELECT, as there is no PRIMARY KEY in the
** database schema to throw an exception if a duplicate is inserted. */
- rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect);
+ rc = sessionSeekToRow(pIter, p);
if( rc==SQLITE_ROW ){
rc = SQLITE_CONSTRAINT;
sqlite3_reset(p->pSelect);
@@ -197041,7 +227976,7 @@ static int sessionApplyOneOp(
** the conflict handler callback.
**
** The difference between this function and sessionApplyOne() is that this
-** function handles the case where the conflict-handler is invoked and
+** function handles the case where the conflict-handler is invoked and
** returns SQLITE_CHANGESET_REPLACE - indicating that the change should be
** retried in some manner.
*/
@@ -197061,7 +227996,7 @@ static int sessionApplyOneWithRetry(
/* If the bRetry flag is set, the change has not been applied due to an
** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and
** a row with the correct PK is present in the db, but one or more other
- ** fields do not contain the expected values) and the conflict handler
+ ** fields do not contain the expected values) and the conflict handler
** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation,
** but pass NULL as the final argument so that sessionApplyOneOp() ignores
** the SQLITE_CHANGESET_DATA problem. */
@@ -197079,7 +228014,7 @@ static int sessionApplyOneWithRetry(
assert( pIter->op==SQLITE_INSERT );
rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
if( rc==SQLITE_OK ){
- rc = sessionBindRow(pIter,
+ rc = sessionBindRow(pIter,
sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete);
sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1);
}
@@ -197103,7 +228038,7 @@ static int sessionApplyOneWithRetry(
** Retry the changes accumulated in the pApply->constraints buffer.
*/
static int sessionRetryConstraints(
- sqlite3 *db,
+ sqlite3 *db,
int bPatchset,
const char *zTab,
SessionApplyCtx *pApply,
@@ -197117,9 +228052,11 @@ static int sessionRetryConstraints(
SessionBuffer cons = pApply->constraints;
memset(&pApply->constraints, 0, sizeof(SessionBuffer));
- rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf, 0);
+ rc = sessionChangesetStart(
+ &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints, 1
+ );
if( rc==SQLITE_OK ){
- int nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
+ size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
int rc2;
pIter2->bPatchset = bPatchset;
pIter2->zTab = (char*)zTab;
@@ -197151,7 +228088,7 @@ static int sessionRetryConstraints(
/*
** Argument pIter is a changeset iterator that has been initialized, but
-** not yet passed to sqlite3changeset_next(). This function applies the
+** not yet passed to sqlite3changeset_next(). This function applies the
** changeset to the main database attached to handle "db". The supplied
** conflict handler callback is invoked to resolve any conflicts encountered
** while applying the change.
@@ -197184,6 +228121,8 @@ static int sessionChangesetApply(
pIter->in.bNoDiscard = 1;
memset(&sApply, 0, sizeof(sApply));
sApply.bRebase = (ppRebase && pnRebase);
+ sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
+ sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP);
sqlite3_mutex_enter(sqlite3_db_mutex(db));
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
@@ -197195,7 +228134,7 @@ static int sessionChangesetApply(
int nCol;
int op;
const char *zNew;
-
+
sqlite3changeset_op(pIter, &zNew, &nCol, &op, 0);
if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){
@@ -197206,14 +228145,13 @@ static int sessionChangesetApply(
);
if( rc!=SQLITE_OK ) break;
+ sessionUpdateFree(&sApply);
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
sqlite3_finalize(sApply.pDelete);
- sqlite3_finalize(sApply.pUpdate);
sqlite3_finalize(sApply.pInsert);
sqlite3_finalize(sApply.pSelect);
sApply.db = db;
sApply.pDelete = 0;
- sApply.pUpdate = 0;
sApply.pInsert = 0;
sApply.pSelect = 0;
sApply.nCol = 0;
@@ -197222,9 +228160,10 @@ static int sessionChangesetApply(
sApply.bStat1 = 0;
sApply.bDeferConstraints = 1;
sApply.bRebaseStarted = 0;
+ sApply.bRowid = 0;
memset(&sApply.constraints, 0, sizeof(SessionBuffer));
- /* If an xFilter() callback was specified, invoke it now. If the
+ /* If an xFilter() callback was specified, invoke it now. If the
** xFilter callback returns zero, skip this table. If it returns
** non-zero, proceed. */
schemaMismatch = (xFilter && (0==xFilter(pCtx, zNew)));
@@ -197241,25 +228180,25 @@ static int sessionChangesetApply(
int i;
sqlite3changeset_pk(pIter, &abPK, 0);
- rc = sessionTableInfo(
- db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
+ rc = sessionTableInfo(0, db, "main", zNew,
+ &sApply.nCol, &zTab, &sApply.azCol, 0, &sApply.abPK, &sApply.bRowid
);
if( rc!=SQLITE_OK ) break;
for(i=0; i<sApply.nCol; i++){
if( sApply.abPK[i] ) nMinCol = i+1;
}
-
+
if( sApply.nCol==0 ){
schemaMismatch = 1;
- sqlite3_log(SQLITE_SCHEMA,
+ sqlite3_log(SQLITE_SCHEMA,
"sqlite3changeset_apply(): no such table: %s", zTab
);
}
else if( sApply.nCol<nCol ){
schemaMismatch = 1;
- sqlite3_log(SQLITE_SCHEMA,
+ sqlite3_log(SQLITE_SCHEMA,
"sqlite3changeset_apply(): table %s has %d columns, "
- "expected %d or more",
+ "expected %d or more",
zTab, sApply.nCol, nCol
);
}
@@ -197277,11 +228216,10 @@ static int sessionChangesetApply(
}
sApply.bStat1 = 1;
}else{
- if((rc = sessionSelectRow(db, zTab, &sApply))
- || (rc = sessionUpdateRow(db, zTab, &sApply))
- || (rc = sessionDeleteRow(db, zTab, &sApply))
- || (rc = sessionInsertRow(db, zTab, &sApply))
- ){
+ if( (rc = sessionSelectRow(db, zTab, &sApply))
+ || (rc = sessionDeleteRow(db, zTab, &sApply))
+ || (rc = sessionInsertRow(db, zTab, &sApply))
+ ){
break;
}
sApply.bStat1 = 0;
@@ -197340,9 +228278,9 @@ static int sessionChangesetApply(
*pnRebase = sApply.rebase.nBuf;
sApply.rebase.aBuf = 0;
}
+ sessionUpdateFree(&sApply);
sqlite3_finalize(sApply.pInsert);
sqlite3_finalize(sApply.pDelete);
- sqlite3_finalize(sApply.pUpdate);
sqlite3_finalize(sApply.pSelect);
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
sqlite3_free((char*)sApply.constraints.aBuf);
@@ -197352,7 +228290,7 @@ static int sessionChangesetApply(
}
/*
-** Apply the changeset passed via pChangeset/nChangeset to the main
+** Apply the changeset passed via pChangeset/nChangeset to the main
** database attached to handle "db".
*/
SQLITE_API int sqlite3changeset_apply_v2(
@@ -197372,14 +228310,27 @@ SQLITE_API int sqlite3changeset_apply_v2(
void **ppRebase, int *pnRebase,
int flags
){
- sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
- int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
- int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset,bInverse);
+ sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
+ int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
+ int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1);
+ u64 savedFlag = db->flags & SQLITE_FkNoAction;
+
+ if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){
+ db->flags |= ((u64)SQLITE_FkNoAction);
+ db->aDb[0].pSchema->schema_cookie -= 32;
+ }
+
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
);
}
+
+ if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
+ assert( db->flags & SQLITE_FkNoAction );
+ db->flags &= ~((u64)SQLITE_FkNoAction);
+ db->aDb[0].pSchema->schema_cookie -= 32;
+ }
return rc;
}
@@ -197430,9 +228381,9 @@ SQLITE_API int sqlite3changeset_apply_v2_strm(
void **ppRebase, int *pnRebase,
int flags
){
- sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
+ sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
- int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse);
+ int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse, 1);
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
@@ -197467,6 +228418,9 @@ struct sqlite3_changegroup {
int rc; /* Error code */
int bPatch; /* True to accumulate patchsets */
SessionTable *pList; /* List of tables in current patch */
+
+ sqlite3 *db; /* Configured by changegroup_schema() */
+ char *zDb; /* Configured by changegroup_schema() */
};
/*
@@ -197487,9 +228441,10 @@ static int sessionChangeMerge(
){
SessionChange *pNew = 0;
int rc = SQLITE_OK;
+ assert( aRec!=0 );
if( !pExist ){
- pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange) + nRec);
+ pNew = (SessionChange *)sqlite3_malloc64(sizeof(SessionChange) + nRec);
if( !pNew ){
return SQLITE_NOMEM;
}
@@ -197522,8 +228477,8 @@ static int sessionChangeMerge(
if( pExist->op==SQLITE_DELETE && pExist->bIndirect ){
*ppNew = pExist;
}else{
- int nByte = nRec + pExist->nRecord + sizeof(SessionChange);
- pNew = (SessionChange*)sqlite3_malloc(nByte);
+ sqlite3_int64 nByte = nRec + pExist->nRecord + sizeof(SessionChange);
+ pNew = (SessionChange*)sqlite3_malloc64(nByte);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -197559,7 +228514,7 @@ static int sessionChangeMerge(
}else{
int op1 = pExist->op;
- /*
+ /*
** op1=INSERT, op2=INSERT -> Unsupported. Discard op2.
** op1=INSERT, op2=UPDATE -> INSERT.
** op1=INSERT, op2=DELETE -> (none)
@@ -197571,7 +228526,7 @@ static int sessionChangeMerge(
** op1=DELETE, op2=INSERT -> UPDATE.
** op1=DELETE, op2=UPDATE -> Unsupported. Discard op2.
** op1=DELETE, op2=DELETE -> Unsupported. Discard op2.
- */
+ */
if( (op1==SQLITE_INSERT && op2==SQLITE_INSERT)
|| (op1==SQLITE_UPDATE && op2==SQLITE_INSERT)
|| (op1==SQLITE_DELETE && op2==SQLITE_UPDATE)
@@ -197583,14 +228538,14 @@ static int sessionChangeMerge(
assert( pNew==0 );
}else{
u8 *aExist = pExist->aRecord;
- int nByte;
+ sqlite3_int64 nByte;
u8 *aCsr;
/* Allocate a new SessionChange object. Ensure that the aRecord[]
** buffer of the new object is large enough to hold any record that
** may be generated by combining the input records. */
nByte = sizeof(SessionChange) + pExist->nRecord + nRec;
- pNew = (SessionChange *)sqlite3_malloc(nByte);
+ pNew = (SessionChange *)sqlite3_malloc64(nByte);
if( !pNew ){
sqlite3_free(pExist);
return SQLITE_NOMEM;
@@ -197653,6 +228608,114 @@ static int sessionChangeMerge(
}
/*
+** Check if a changeset entry with nCol columns and the PK array passed
+** as the final argument to this function is compatible with SessionTable
+** pTab. If so, return 1. Otherwise, if they are incompatible in some way,
+** return 0.
+*/
+static int sessionChangesetCheckCompat(
+ SessionTable *pTab,
+ int nCol,
+ u8 *abPK
+){
+ if( pTab->azCol && nCol<pTab->nCol ){
+ int ii;
+ for(ii=0; ii<pTab->nCol; ii++){
+ u8 bPK = (ii < nCol) ? abPK[ii] : 0;
+ if( pTab->abPK[ii]!=bPK ) return 0;
+ }
+ return 1;
+ }
+ return (pTab->nCol==nCol && 0==memcmp(abPK, pTab->abPK, nCol));
+}
+
+static int sessionChangesetExtendRecord(
+ sqlite3_changegroup *pGrp,
+ SessionTable *pTab,
+ int nCol,
+ int op,
+ const u8 *aRec,
+ int nRec,
+ SessionBuffer *pOut
+){
+ int rc = SQLITE_OK;
+ int ii = 0;
+
+ assert( pTab->azCol );
+ assert( nCol<pTab->nCol );
+
+ pOut->nBuf = 0;
+ if( op==SQLITE_INSERT || (op==SQLITE_DELETE && pGrp->bPatch==0) ){
+ /* Append the missing default column values to the record. */
+ sessionAppendBlob(pOut, aRec, nRec, &rc);
+ if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){
+ rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt);
+ }
+ for(ii=nCol; rc==SQLITE_OK && ii<pTab->nCol; ii++){
+ int eType = sqlite3_column_type(pTab->pDfltStmt, ii);
+ sessionAppendByte(pOut, eType, &rc);
+ switch( eType ){
+ case SQLITE_FLOAT:
+ case SQLITE_INTEGER: {
+ i64 iVal;
+ if( eType==SQLITE_INTEGER ){
+ iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
+ }else{
+ double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
+ memcpy(&iVal, &rVal, sizeof(i64));
+ }
+ if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){
+ sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
+ }
+ break;
+ }
+
+ case SQLITE_BLOB:
+ case SQLITE_TEXT: {
+ int n = sqlite3_column_bytes(pTab->pDfltStmt, ii);
+ sessionAppendVarint(pOut, n, &rc);
+ if( eType==SQLITE_TEXT ){
+ const u8 *z = (const u8*)sqlite3_column_text(pTab->pDfltStmt, ii);
+ sessionAppendBlob(pOut, z, n, &rc);
+ }else{
+ const u8 *z = (const u8*)sqlite3_column_blob(pTab->pDfltStmt, ii);
+ sessionAppendBlob(pOut, z, n, &rc);
+ }
+ break;
+ }
+
+ default:
+ assert( eType==SQLITE_NULL );
+ break;
+ }
+ }
+ }else if( op==SQLITE_UPDATE ){
+ /* Append missing "undefined" entries to the old.* record. And, if this
+ ** is an UPDATE, to the new.* record as well. */
+ int iOff = 0;
+ if( pGrp->bPatch==0 ){
+ for(ii=0; ii<nCol; ii++){
+ iOff += sessionSerialLen(&aRec[iOff]);
+ }
+ sessionAppendBlob(pOut, aRec, iOff, &rc);
+ for(ii=0; ii<(pTab->nCol-nCol); ii++){
+ sessionAppendByte(pOut, 0x00, &rc);
+ }
+ }
+
+ sessionAppendBlob(pOut, &aRec[iOff], nRec-iOff, &rc);
+ for(ii=0; ii<(pTab->nCol-nCol); ii++){
+ sessionAppendByte(pOut, 0x00, &rc);
+ }
+ }else{
+ assert( op==SQLITE_DELETE && pGrp->bPatch );
+ sessionAppendBlob(pOut, aRec, nRec, &rc);
+ }
+
+ return rc;
+}
+
+/*
** Add all changes in the changeset traversed by the iterator passed as
** the first argument to the changegroup hash tables.
*/
@@ -197665,6 +228728,7 @@ static int sessionChangesetToHash(
int nRec;
int rc = SQLITE_OK;
SessionTable *pTab = 0;
+ SessionBuffer rec = {0, 0, 0};
while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){
const char *zNew;
@@ -197676,6 +228740,9 @@ static int sessionChangesetToHash(
SessionChange *pExist = 0;
SessionChange **pp;
+ /* Ensure that only changesets, or only patchsets, but not a mixture
+ ** of both, are being combined. It is an error to try to combine a
+ ** changeset and a patchset. */
if( pGrp->pList==0 ){
pGrp->bPatch = pIter->bPatchset;
}else if( pIter->bPatchset!=pGrp->bPatch ){
@@ -197696,7 +228763,7 @@ static int sessionChangesetToHash(
if( !pTab ){
SessionTable **ppTab;
- pTab = sqlite3_malloc(sizeof(SessionTable) + nCol + nNew+1);
+ pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1);
if( !pTab ){
rc = SQLITE_NOMEM;
break;
@@ -197708,19 +228775,39 @@ static int sessionChangesetToHash(
pTab->zName = (char*)&pTab->abPK[nCol];
memcpy(pTab->zName, zNew, nNew+1);
+ if( pGrp->db ){
+ pTab->nCol = 0;
+ rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
+ if( rc ){
+ assert( pTab->azCol==0 );
+ sqlite3_free(pTab);
+ break;
+ }
+ }
+
/* The new object must be linked on to the end of the list, not
** simply added to the start of it. This is to ensure that the
- ** tables within the output of sqlite3changegroup_output() are in
+ ** tables within the output of sqlite3changegroup_output() are in
** the right order. */
for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext);
*ppTab = pTab;
- }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){
+ }
+
+ if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
rc = SQLITE_SCHEMA;
break;
}
}
- if( sessionGrowHash(pIter->bPatchset, pTab) ){
+ if( nCol<pTab->nCol ){
+ assert( pGrp->db );
+ rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, &rec);
+ if( rc ) break;
+ aRec = rec.aBuf;
+ nRec = rec.nBuf;
+ }
+
+ if( sessionGrowHash(0, pIter->bPatchset, pTab) ){
rc = SQLITE_NOMEM;
break;
}
@@ -197728,7 +228815,7 @@ static int sessionChangesetToHash(
pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
);
- /* Search for existing entry. If found, remove it from the hash table.
+ /* Search for existing entry. If found, remove it from the hash table.
** Code below may link it back in.
*/
for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
@@ -197746,7 +228833,7 @@ static int sessionChangesetToHash(
}
}
- rc = sessionChangeMerge(pTab, bRebase,
+ rc = sessionChangeMerge(pTab, bRebase,
pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
);
if( rc ) break;
@@ -197757,6 +228844,7 @@ static int sessionChangesetToHash(
}
}
+ sqlite3_free(rec.aBuf);
if( rc==SQLITE_OK ) rc = pIter->rc;
return rc;
}
@@ -197767,7 +228855,7 @@ static int sessionChangesetToHash(
**
** If xOutput is not NULL, then the changeset/patchset is returned to the
** user via one or more calls to xOutput, as with the other streaming
-** interfaces.
+** interfaces.
**
** Or, if xOutput is NULL, then (*ppOut) is populated with a pointer to a
** buffer containing the output changeset before this function returns. In
@@ -197792,7 +228880,7 @@ static int sessionChangegroupOutput(
assert( xOutput==0 || (ppOut==0 && pnOut==0) );
/* Create the serialized output changeset based on the contents of the
- ** hash tables attached to the SessionTable objects in list p->pList.
+ ** hash tables attached to the SessionTable objects in list p->pList.
*/
for(pTab=pGrp->pList; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
int i;
@@ -197816,9 +228904,9 @@ static int sessionChangegroupOutput(
if( rc==SQLITE_OK ){
if( xOutput ){
if( buf.nBuf>0 ) rc = xOutput(pOut, buf.aBuf, buf.nBuf);
- }else{
+ }else if( ppOut ){
*ppOut = buf.aBuf;
- *pnOut = buf.nBuf;
+ if( pnOut ) *pnOut = buf.nBuf;
buf.aBuf = 0;
}
}
@@ -197844,6 +228932,31 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp){
}
/*
+** Provide a database schema to the changegroup object.
+*/
+SQLITE_API int sqlite3changegroup_schema(
+ sqlite3_changegroup *pGrp,
+ sqlite3 *db,
+ const char *zDb
+){
+ int rc = SQLITE_OK;
+
+ if( pGrp->pList || pGrp->db ){
+ /* Cannot add a schema after one or more calls to sqlite3changegroup_add(),
+ ** or after sqlite3changegroup_schema() has already been called. */
+ rc = SQLITE_MISUSE;
+ }else{
+ pGrp->zDb = sqlite3_mprintf("%s", zDb);
+ if( pGrp->zDb==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pGrp->db = db;
+ }
+ }
+ return rc;
+}
+
+/*
** Add the changeset currently stored in buffer pData, size nData bytes,
** to changeset-group p.
*/
@@ -197895,7 +229008,7 @@ SQLITE_API int sqlite3changegroup_add_strm(
*/
SQLITE_API int sqlite3changegroup_output_strm(
sqlite3_changegroup *pGrp,
- int (*xOutput)(void *pOut, const void *pData, int nData),
+ int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
){
return sessionChangegroupOutput(pGrp, xOutput, pOut, 0, 0);
@@ -197906,12 +229019,13 @@ SQLITE_API int sqlite3changegroup_output_strm(
*/
SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
if( pGrp ){
- sessionDeleteTable(pGrp->pList);
+ sqlite3_free(pGrp->zDb);
+ sessionDeleteTable(0, pGrp->pList);
sqlite3_free(pGrp);
}
}
-/*
+/*
** Combine two changesets together.
*/
SQLITE_API int sqlite3changeset_concat(
@@ -197978,7 +229092,7 @@ struct sqlite3_rebaser {
/*
** Buffers a1 and a2 must both contain a sessions module record nCol
-** fields in size. This function appends an nCol sessions module
+** fields in size. This function appends an nCol sessions module
** record to buffer pBuf that is a copy of a1, except that for
** each field that is undefined in a1[], swap in the field from a2[].
*/
@@ -198013,20 +229127,20 @@ static void sessionAppendRecordMerge(
}
/*
-** This function is called when rebasing a local UPDATE change against one
+** This function is called when rebasing a local UPDATE change against one
** or more remote UPDATE changes. The aRec/nRec buffer contains the current
** old.* and new.* records for the change. The rebase buffer (a single
** record) is in aChange/nChange. The rebased change is appended to buffer
** pBuf.
**
-** Rebasing the UPDATE involves:
+** Rebasing the UPDATE involves:
**
** * Removing any changes to fields for which the corresponding field
** in the rebase buffer is set to "replaced" (type 0xFF). If this
** means the UPDATE change updates no fields, nothing is appended
** to the output buffer.
**
-** * For each field modified by the local change for which the
+** * For each field modified by the local change for which the
** corresponding field in the rebase buffer is not "undefined" (0x00)
** or "replaced" (0xFF), the old.* value is replaced by the value
** in the rebase buffer.
@@ -198052,10 +229166,10 @@ static void sessionAppendPartialUpdate(
int n1 = sessionSerialLen(a1);
int n2 = sessionSerialLen(a2);
if( pIter->abPK[i] || a2[0]==0 ){
- if( !pIter->abPK[i] ) bData = 1;
+ if( !pIter->abPK[i] && a1[0] ) bData = 1;
memcpy(pOut, a1, n1);
pOut += n1;
- }else if( a2[0]!=0xFF ){
+ }else if( a2[0]!=0xFF && a1[0] ){
bData = 1;
memcpy(pOut, a2, n2);
pOut += n2;
@@ -198085,15 +229199,15 @@ static void sessionAppendPartialUpdate(
}
/*
-** pIter is configured to iterate through a changeset. This function rebases
-** that changeset according to the current configuration of the rebaser
+** pIter is configured to iterate through a changeset. This function rebases
+** that changeset according to the current configuration of the rebaser
** object passed as the first argument. If no error occurs and argument xOutput
** is not NULL, then the changeset is returned to the caller by invoking
** xOutput zero or more times and SQLITE_OK returned. Or, if xOutput is NULL,
** then (*ppOut) is set to point to a buffer containing the rebased changeset
** before this function returns. In this case (*pnOut) is set to the size of
** the buffer in bytes. It is the responsibility of the caller to eventually
-** free the (*ppOut) buffer using sqlite3_free().
+** free the (*ppOut) buffer using sqlite3_free().
**
** If an error occurs, an SQLite error code is returned. If ppOut and
** pnOut are not NULL, then the two output parameters are set to 0 before
@@ -198171,7 +229285,7 @@ static int sessionRebase(
sessionAppendByte(&sOut, SQLITE_INSERT, &rc);
sessionAppendByte(&sOut, pIter->bIndirect, &rc);
sessionAppendRecordMerge(&sOut, pIter->nCol,
- pCsr, nRec-(pCsr-aRec),
+ pCsr, nRec-(pCsr-aRec),
pChange->aRecord, pChange->nRecord, &rc
);
}
@@ -198218,7 +229332,7 @@ static int sessionRebase(
if( sOut.nBuf>0 ){
rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
}
- }else{
+ }else if( ppOut ){
*ppOut = (void*)sOut.aBuf;
*pnOut = sOut.nBuf;
sOut.aBuf = 0;
@@ -198228,7 +229342,7 @@ static int sessionRebase(
return rc;
}
-/*
+/*
** Create a new rebaser object.
*/
SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew){
@@ -198245,11 +229359,11 @@ SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew){
return rc;
}
-/*
+/*
** Call this one or more times to configure a rebaser.
*/
SQLITE_API int sqlite3rebaser_configure(
- sqlite3_rebaser *p,
+ sqlite3_rebaser *p,
int nRebase, const void *pRebase
){
sqlite3_changeset_iter *pIter = 0; /* Iterator opened on pData/nData */
@@ -198262,15 +229376,15 @@ SQLITE_API int sqlite3rebaser_configure(
return rc;
}
-/*
-** Rebase a changeset according to current rebaser configuration
+/*
+** Rebase a changeset according to current rebaser configuration
*/
SQLITE_API int sqlite3rebaser_rebase(
sqlite3_rebaser *p,
- int nIn, const void *pIn,
- int *pnOut, void **ppOut
+ int nIn, const void *pIn,
+ int *pnOut, void **ppOut
){
- sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */
+ sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */
int rc = sqlite3changeset_start(&pIter, nIn, (void*)pIn);
if( rc==SQLITE_OK ){
@@ -198281,8 +229395,8 @@ SQLITE_API int sqlite3rebaser_rebase(
return rc;
}
-/*
-** Rebase a changeset according to current rebaser configuration
+/*
+** Rebase a changeset according to current rebaser configuration
*/
SQLITE_API int sqlite3rebaser_rebase_strm(
sqlite3_rebaser *p,
@@ -198291,7 +229405,7 @@ SQLITE_API int sqlite3rebaser_rebase_strm(
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
){
- sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */
+ sqlite3_changeset_iter *pIter = 0; /* Iterator to skip through input */
int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
if( rc==SQLITE_OK ){
@@ -198302,17 +229416,17 @@ SQLITE_API int sqlite3rebaser_rebase_strm(
return rc;
}
-/*
-** Destroy a rebaser object
+/*
+** Destroy a rebaser object
*/
SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){
if( p ){
- sessionDeleteTable(p->grp.pList);
+ sessionDeleteTable(0, p->grp.pList);
sqlite3_free(p);
}
}
-/*
+/*
** Global configuration
*/
SQLITE_API int sqlite3session_config(int op, void *pArg){
@@ -198339,9 +229453,9 @@ SQLITE_API int sqlite3session_config(int op, void *pArg){
/************** Begin file fts5.c ********************************************/
-#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5)
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5)
-#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
# define NDEBUG 1
#endif
#if defined(NDEBUG) && defined(SQLITE_DEBUG)
@@ -198360,7 +229474,7 @@ SQLITE_API int sqlite3session_config(int op, void *pArg){
**
******************************************************************************
**
-** Interfaces to extend FTS5. Using the interfaces defined in this file,
+** Interfaces to extend FTS5. Using the interfaces defined in this file,
** FTS5 may be extended with:
**
** * custom tokenizers, and
@@ -198405,19 +229519,19 @@ struct Fts5PhraseIter {
** EXTENSION API FUNCTIONS
**
** xUserData(pFts):
-** Return a copy of the context pointer the extension function was
+** Return a copy of the context pointer the extension function was
** registered with.
**
** xColumnTotalSize(pFts, iCol, pnToken):
** If parameter iCol is less than zero, set output variable *pnToken
** to the total number of tokens in the FTS5 table. Or, if iCol is
** non-negative but less than the number of columns in the table, return
-** the total number of tokens in column iCol, considering all rows in
+** the total number of tokens in column iCol, considering all rows in
** the FTS5 table.
**
** If parameter iCol is greater than or equal to the number of columns
** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
-** an OOM condition or IO error), an appropriate SQLite error code is
+** an OOM condition or IO error), an appropriate SQLite error code is
** returned.
**
** xColumnCount(pFts):
@@ -198431,15 +229545,18 @@ struct Fts5PhraseIter {
**
** If parameter iCol is greater than or equal to the number of columns
** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
-** an OOM condition or IO error), an appropriate SQLite error code is
+** an OOM condition or IO error), an appropriate SQLite error code is
** returned.
**
** This function may be quite inefficient if used with an FTS5 table
** created with the "columnsize=0" option.
**
** xColumnText:
-** This function attempts to retrieve the text of column iCol of the
-** current document. If successful, (*pz) is set to point to a buffer
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of columns in the table, SQLITE_RANGE is returned.
+**
+** Otherwise, this function attempts to retrieve the text of column iCol of
+** the current document. If successful, (*pz) is set to point to a buffer
** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
** if an error occurs, an SQLite error code is returned and the final values
@@ -198449,8 +229566,10 @@ struct Fts5PhraseIter {
** Returns the number of phrases in the current query expression.
**
** xPhraseSize:
-** Returns the number of tokens in phrase iPhrase of the query. Phrases
-** are numbered starting from zero.
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of phrases in the current query, as returned by xPhraseCount,
+** 0 is returned. Otherwise, this function returns the number of tokens in
+** phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
** Set *pnInst to the total number of occurrences of all phrases within
@@ -198458,27 +229577,24 @@ struct Fts5PhraseIter {
** an error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" or "detail=column" option. If the FTS5 table is created
-** with either "detail=none" or "detail=column" and "content=" option
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
** (i.e. if it is a contentless table), then this API always returns 0.
**
** xInst:
** Query for the details of phrase match iIdx within the current row.
** Phrase matches are numbered starting from zero, so the iIdx argument
** should be greater than or equal to zero and smaller than the value
-** output by xInstCount().
+** output by xInstCount(). If iIdx is less than zero or greater than
+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
-** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the
-** first token of the phrase. The exception is if the table was created
-** with the offsets=0 option specified. In this case *piOff is always
-** set to -1.
-**
-** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
-** if an error occurs.
+** first token of the phrase. SQLITE_OK is returned if successful, or an
+** error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" or "detail=column" option.
+** "detail=none" or "detail=column" option.
**
** xRowid:
** Returns the rowid of the current row.
@@ -198494,13 +229610,17 @@ struct Fts5PhraseIter {
**
** with $p set to a phrase equivalent to the phrase iPhrase of the
** current query is executed. Any column filter that applies to
-** phrase iPhrase of the current query is included in $p. For each
-** row visited, the callback function passed as the fourth argument
-** is invoked. The context and API objects passed to the callback
+** phrase iPhrase of the current query is included in $p. For each
+** row visited, the callback function passed as the fourth argument
+** is invoked. The context and API objects passed to the callback
** function may be used to access the properties of each matched row.
-** Invoking Api.xUserData() returns a copy of the pointer passed as
+** Invoking Api.xUserData() returns a copy of the pointer passed as
** the third argument to pUserData.
**
+** If parameter iPhrase is less than zero, or greater than or equal to
+** the number of phrases in the query, as returned by xPhraseCount(),
+** this function returns SQLITE_RANGE.
+**
** If the callback function returns any value other than SQLITE_OK, the
** query is abandoned and the xQueryPhrase function returns immediately.
** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
@@ -198513,14 +229633,14 @@ struct Fts5PhraseIter {
**
** xSetAuxdata(pFts5, pAux, xDelete)
**
-** Save the pointer passed as the second argument as the extension functions
+** Save the pointer passed as the second argument as the extension function's
** "auxiliary data". The pointer may then be retrieved by the current or any
** future invocation of the same fts5 extension function made as part of
-** of the same MATCH query using the xGetAuxdata() API.
+** the same MATCH query using the xGetAuxdata() API.
**
** Each extension function is allocated a single auxiliary data slot for
-** each FTS query (MATCH expression). If the extension function is invoked
-** more than once for a single FTS query, then all invocations share a
+** each FTS query (MATCH expression). If the extension function is invoked
+** more than once for a single FTS query, then all invocations share a
** single auxiliary data context.
**
** If there is already an auxiliary data pointer when this function is
@@ -198531,7 +229651,7 @@ struct Fts5PhraseIter {
** The xDelete callback, if one is specified, is also invoked on the
** auxiliary data pointer after the FTS5 query has finished.
**
-** If an error (e.g. an OOM condition) occurs within this function, an
+** If an error (e.g. an OOM condition) occurs within this function,
** the auxiliary data is set to NULL and an error code returned. If the
** xDelete parameter was not NULL, it is invoked on the auxiliary data
** pointer before returning.
@@ -198539,7 +229659,7 @@ struct Fts5PhraseIter {
**
** xGetAuxdata(pFts5, bClear)
**
-** Returns the current auxiliary data pointer for the fts5 extension
+** Returns the current auxiliary data pointer for the fts5 extension
** function. See the xSetAuxdata() method for details.
**
** If the bClear argument is non-zero, then the auxiliary data is cleared
@@ -198559,7 +229679,7 @@ struct Fts5PhraseIter {
** method, to iterate through all instances of a single query phrase within
** the current row. This is the same information as is accessible via the
** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient
-** to use, this API may be faster under some circumstances. To iterate
+** to use, this API may be faster under some circumstances. To iterate
** through instances of phrase iPhrase, use the following code:
**
** Fts5PhraseIter iter;
@@ -198577,8 +229697,8 @@ struct Fts5PhraseIter {
** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below).
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" or "detail=column" option. If the FTS5 table is created
-** with either "detail=none" or "detail=column" and "content=" option
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
** (i.e. if it is a contentless table), then this API always iterates
** through an empty set (all calls to xPhraseFirst() set iCol to -1).
**
@@ -198602,19 +229722,52 @@ struct Fts5PhraseIter {
** }
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" option. If the FTS5 table is created with either
-** "detail=none" "content=" option (i.e. if it is a contentless table),
-** then this API always iterates through an empty set (all calls to
+** "detail=none" option. If the FTS5 table is created with either
+** "detail=none" "content=" option (i.e. if it is a contentless table),
+** then this API always iterates through an empty set (all calls to
** xPhraseFirstColumn() set iCol to -1).
**
** The information accessed using this API and its companion
** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
** (or xInst/xInstCount). The chief advantage of this API is that it is
** significantly more efficient than those alternatives when used with
-** "detail=column" tables.
+** "detail=column" tables.
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
+**
+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase iPhrase of the current
+** query. Before returning, output parameter *ppToken is set to point
+** to a buffer containing the requested token, and *pnToken to the
+** size of this buffer in bytes.
+**
+** If iPhrase or iToken are less than zero, or if iPhrase is greater than
+** or equal to the number of phrases in the query as reported by
+** xPhraseCount(), or if iToken is equal to or greater than the number of
+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
+ are both zeroed.
+**
+** The output text is not a copy of the query text that specified the
+** token. It is the output of the tokenizer module. For tokendata=1
+** tables, this includes any embedded 0x00 and trailing data.
+**
+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase hit iIdx within the
+** current row. If iIdx is less than zero or greater than or equal to the
+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
+** output variable (*ppToken) is set to point to a buffer containing the
+** matching document token, and (*pnToken) to the size of that buffer in
+** bytes. This API is not available if the specified token matches a
+** prefix query term. In that case both output variables are always set
+** to 0.
+**
+** The output text is not a copy of the document text that was tokenized.
+** It is the output of the tokenizer module. For tokendata=1 tables, this
+** includes any embedded 0x00 and trailing data.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
int iVersion; /* Currently always set to 3 */
@@ -198625,7 +229778,7 @@ struct Fts5ExtensionApi {
int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
- int (*xTokenize)(Fts5Context*,
+ int (*xTokenize)(Fts5Context*,
const char *pText, int nText, /* Text to tokenize */
void *pCtx, /* Context passed to xToken() */
int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
@@ -198652,17 +229805,24 @@ struct Fts5ExtensionApi {
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+
+ /* Below this point are iVersion>=3 only */
+ int (*xQueryToken)(Fts5Context*,
+ int iPhrase, int iToken,
+ const char **ppToken, int *pnToken
+ );
+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
-/*
+/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/
/*************************************************************************
** CUSTOM TOKENIZERS
**
-** Applications may also register custom tokenizer types. A tokenizer
-** is registered by providing fts5 with a populated instance of the
+** Applications may also register custom tokenizer types. A tokenizer
+** is registered by providing fts5 with a populated instance of the
** following structure. All structure methods must be defined, setting
** any member of the fts5_tokenizer struct to NULL leads to undefined
** behaviour. The structure methods are expected to function as follows:
@@ -198673,16 +229833,16 @@ struct Fts5ExtensionApi {
**
** The first argument passed to this function is a copy of the (void*)
** pointer provided by the application when the fts5_tokenizer object
-** was registered with FTS5 (the third argument to xCreateTokenizer()).
+** was registered with FTS5 (the third argument to xCreateTokenizer()).
** The second and third arguments are an array of nul-terminated strings
** containing the tokenizer arguments, if any, specified following the
** tokenizer name as part of the CREATE VIRTUAL TABLE statement used
** to create the FTS5 table.
**
-** The final argument is an output variable. If successful, (*ppOut)
+** The final argument is an output variable. If successful, (*ppOut)
** should be set to point to the new tokenizer handle and SQLITE_OK
** returned. If an error occurs, some value other than SQLITE_OK should
-** be returned. In this case, fts5 assumes that the final value of *ppOut
+** be returned. In this case, fts5 assumes that the final value of *ppOut
** is undefined.
**
** xDelete:
@@ -198691,7 +229851,7 @@ struct Fts5ExtensionApi {
** be invoked exactly once for each successful call to xCreate().
**
** xTokenize:
-** This function is expected to tokenize the nText byte string indicated
+** This function is expected to tokenize the nText byte string indicated
** by argument pText. pText may or may not be nul-terminated. The first
** argument passed to this function is a pointer to an Fts5Tokenizer object
** returned by an earlier call to xCreate().
@@ -198705,8 +229865,8 @@ struct Fts5ExtensionApi {
** determine the set of tokens to add to (or delete from) the
** FTS index.
**
-** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
-** against the FTS index. The tokenizer is being called to tokenize
+** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
+** against the FTS index. The tokenizer is being called to tokenize
** a bareword or quoted string specified as part of the query.
**
** <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as
@@ -198714,10 +229874,10 @@ struct Fts5ExtensionApi {
** followed by a "*" character, indicating that the last token
** returned by the tokenizer will be treated as a token prefix.
**
-** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
+** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
** satisfy an fts5_api.xTokenize() request made by an auxiliary
** function. Or an fts5_api.xColumnSize() request made by the same
-** on a columnsize=0 database.
+** on a columnsize=0 database.
** </ul>
**
** For each token in the input string, the supplied callback xToken() must
@@ -198729,10 +229889,10 @@ struct Fts5ExtensionApi {
** which the token is derived within the input.
**
** The second argument passed to the xToken() callback ("tflags") should
-** normally be set to 0. The exception is if the tokenizer supports
+** normally be set to 0. The exception is if the tokenizer supports
** synonyms. In this case see the discussion below for details.
**
-** FTS5 assumes the xToken() callback is invoked for each token in the
+** FTS5 assumes the xToken() callback is invoked for each token in the
** order that they occur within the input text.
**
** If an xToken() callback returns any value other than SQLITE_OK, then
@@ -198746,7 +229906,7 @@ struct Fts5ExtensionApi {
** SYNONYM SUPPORT
**
** Custom tokenizers may also support synonyms. Consider a case in which a
-** user wishes to query for a phrase such as "first place". Using the
+** user wishes to query for a phrase such as "first place". Using the
** built-in tokenizers, the FTS5 query 'first + place' will match instances
** of "first place" within the document set, but not alternative forms
** such as "1st place". In some applications, it would be better to match
@@ -198755,8 +229915,8 @@ struct Fts5ExtensionApi {
**
** There are several ways to approach this in FTS5:
**
-** <ol><li> By mapping all synonyms to a single token. In this case, the
-** In the above example, this means that the tokenizer returns the
+** <ol><li> By mapping all synonyms to a single token. In this case, using
+** the above example, this means that the tokenizer returns the
** same token for inputs "first" and "1st". Say that token is in
** fact "first", so that when the user inserts the document "I won
** 1st place" entries are added to the index for tokens "i", "won",
@@ -198764,36 +229924,36 @@ struct Fts5ExtensionApi {
** the tokenizer substitutes "first" for "1st" and the query works
** as expected.
**
-** <li> By adding multiple synonyms for a single term to the FTS index.
-** In this case, when tokenizing query text, the tokenizer may
-** provide multiple synonyms for a single term within the document.
-** FTS5 then queries the index for each synonym individually. For
-** example, faced with the query:
+** <li> By querying the index for all synonyms of each query term
+** separately. In this case, when tokenizing query text, the
+** tokenizer may provide multiple synonyms for a single term
+** within the document. FTS5 then queries the index for each
+** synonym individually. For example, faced with the query:
**
** <codeblock>
** ... MATCH 'first place'</codeblock>
**
** the tokenizer offers both "1st" and "first" as synonyms for the
-** first token in the MATCH query and FTS5 effectively runs a query
+** first token in the MATCH query and FTS5 effectively runs a query
** similar to:
**
** <codeblock>
** ... MATCH '(first OR 1st) place'</codeblock>
**
** except that, for the purposes of auxiliary functions, the query
-** still appears to contain just two phrases - "(first OR 1st)"
+** still appears to contain just two phrases - "(first OR 1st)"
** being treated as a single phrase.
**
** <li> By adding multiple synonyms for a single term to the FTS index.
** Using this method, when tokenizing document text, the tokenizer
-** provides multiple synonyms for each token. So that when a
+** provides multiple synonyms for each token. So that when a
** document such as "I won first place" is tokenized, entries are
** added to the FTS index for "i", "won", "first", "1st" and
** "place".
**
** This way, even if the tokenizer does not provide synonyms
-** when tokenizing query text (it should not - to do would be
-** inefficient), it doesn't matter if the user queries for
+** when tokenizing query text (it should not - to do so would be
+** inefficient), it doesn't matter if the user queries for
** 'first + place' or '1st + place', as there are entries in the
** FTS index corresponding to both forms of the first token.
** </ol>
@@ -198814,11 +229974,11 @@ struct Fts5ExtensionApi {
**
** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time
** xToken() is called. Multiple synonyms may be specified for a single token
-** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
+** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
** There is no limit to the number of synonyms that may be provided for a
** single token.
**
-** In many cases, method (1) above is the best approach. It does not add
+** In many cases, method (1) above is the best approach. It does not add
** extra data to the FTS index or require FTS5 to query for multiple terms,
** so it is efficient in terms of disk space and query speed. However, it
** does not support prefix queries very well. If, as suggested above, the
@@ -198830,24 +229990,24 @@ struct Fts5ExtensionApi {
** will not match documents that contain the token "1st" (as the tokenizer
** will probably not map "1s" to any prefix of "first").
**
-** For full prefix support, method (3) may be preferred. In this case,
+** For full prefix support, method (3) may be preferred. In this case,
** because the index contains entries for both "first" and "1st", prefix
** queries such as 'fi*' or '1s*' will match correctly. However, because
** extra entries are added to the FTS index, this method uses more space
** within the database.
**
** Method (2) offers a midpoint between (1) and (3). Using this method,
-** a query such as '1s*' will match documents that contain the literal
+** a query such as '1s*' will match documents that contain the literal
** token "1st", but not "first" (assuming the tokenizer is not able to
** provide synonyms for prefixes). However, a non-prefix query like '1st'
** will match against "1st" and "first". This method does not require
-** extra disk space, as no extra entries are added to the FTS index.
+** extra disk space, as no extra entries are added to the FTS index.
** On the other hand, it may require more CPU cycles to run MATCH queries,
** as separate queries of the FTS index are required for each synonym.
**
** When using methods (2) or (3), it is important that the tokenizer only
-** provide synonyms when tokenizing document text (method (2)) or query
-** text (method (3)), not both. Doing so will not cause any errors, but is
+** provide synonyms when tokenizing document text (method (3)) or query
+** text (method (2)), not both. Doing so will not cause any errors, but is
** inefficient.
*/
typedef struct Fts5Tokenizer Fts5Tokenizer;
@@ -198855,10 +230015,10 @@ typedef struct fts5_tokenizer fts5_tokenizer;
struct fts5_tokenizer {
int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
void (*xDelete)(Fts5Tokenizer*);
- int (*xTokenize)(Fts5Tokenizer*,
+ int (*xTokenize)(Fts5Tokenizer*,
void *pCtx,
int flags, /* Mask of FTS5_TOKENIZE_* flags */
- const char *pText, int nText,
+ const char *pText, int nText,
int (*xToken)(
void *pCtx, /* Copy of 2nd argument to xTokenize() */
int tflags, /* Mask of FTS5_TOKEN_* flags */
@@ -198895,7 +230055,7 @@ struct fts5_api {
int (*xCreateTokenizer)(
fts5_api *pApi,
const char *zName,
- void *pContext,
+ void *pUserData,
fts5_tokenizer *pTokenizer,
void (*xDestroy)(void*)
);
@@ -198904,7 +230064,7 @@ struct fts5_api {
int (*xFindTokenizer)(
fts5_api *pApi,
const char *zName,
- void **ppContext,
+ void **ppUserData,
fts5_tokenizer *pTokenizer
);
@@ -198912,7 +230072,7 @@ struct fts5_api {
int (*xCreateFunction)(
fts5_api *pApi,
const char *zName,
- void *pContext,
+ void *pUserData,
fts5_extension_function xFunction,
void (*xDestroy)(void*)
);
@@ -198965,8 +230125,20 @@ typedef sqlite3_uint64 u64;
#endif
#define testcase(x)
-#define ALWAYS(x) 1
-#define NEVER(x) 0
+
+#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
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
@@ -198979,7 +230151,7 @@ typedef sqlite3_uint64 u64;
#endif
-/* Truncate very long tokens to this many bytes. Hard limit is
+/* Truncate very long tokens to this many bytes. Hard limit is
** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
** field that occurs at the start of each leaf page (see fts5_index.c). */
#define FTS5_MAX_TOKEN_SIZE 32768
@@ -198991,6 +230163,11 @@ typedef sqlite3_uint64 u64;
*/
#define FTS5_MAX_PREFIX_INDEXES 31
+/*
+** Maximum segments permitted in a single index
+*/
+#define FTS5_MAX_SEGMENT 2000
+
#define FTS5_DEFAULT_NEARDIST 10
#define FTS5_DEFAULT_RANK "bm25"
@@ -199007,7 +230184,7 @@ static int sqlite3Fts5Corrupt(void);
/*
** The assert_nc() macro is similar to the assert() macro, except that it
-** is used for assert() conditions that are true only if it can be
+** is used for assert() conditions that are true only if it can be
** guranteed that the database is not corrupt.
*/
#ifdef SQLITE_DEBUG
@@ -199017,6 +230194,12 @@ SQLITE_API extern int sqlite3_fts5_may_be_corrupt;
# define assert_nc(x) assert(x)
#endif
+/*
+** A version of memcmp() that does not cause asan errors if one of the pointer
+** parameters is NULL and the number of bytes to compare is zero.
+*/
+#define fts5Memcmp(s1, s2, n) ((n)<=0 ? 0 : memcmp((s1), (s2), (n)))
+
/* Mark a function parameter as unused, to suppress nuisance compiler
** warnings. */
#ifndef UNUSED_PARAM
@@ -199030,7 +230213,7 @@ SQLITE_API extern int sqlite3_fts5_may_be_corrupt;
typedef struct Fts5Global Fts5Global;
typedef struct Fts5Colset Fts5Colset;
-/* If a NEAR() clump or phrase may only match a specific set of columns,
+/* If a NEAR() clump or phrase may only match a specific set of columns,
** then an object of the following type is used to record the set of columns.
** Each entry in the aiCol[] array is a column that may be matched.
**
@@ -199058,20 +230241,24 @@ typedef struct Fts5Config Fts5Config;
**
** nAutomerge:
** The minimum number of segments that an auto-merge operation should
-** attempt to merge together. A value of 1 sets the object to use the
+** attempt to merge together. A value of 1 sets the object to use the
** compile time default. Zero disables auto-merge altogether.
**
+** bContentlessDelete:
+** True if the contentless_delete option was present in the CREATE
+** VIRTUAL TABLE statement.
+**
** zContent:
**
** zContentRowid:
-** The value of the content_rowid= option, if one was specified. Or
+** The value of the content_rowid= option, if one was specified. Or
** the string "rowid" otherwise. This text is not quoted - if it is
** used as part of an SQL statement it needs to be quoted appropriately.
**
** zContentExprlist:
**
** pzErrmsg:
-** This exists in order to allow the fts5_index.c module to return a
+** This exists in order to allow the fts5_index.c module to return a
** decent error message if it encounters a file-format version it does
** not understand.
**
@@ -199095,15 +230282,20 @@ struct Fts5Config {
int nPrefix; /* Number of prefix indexes */
int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */
int eContent; /* An FTS5_CONTENT value */
- char *zContent; /* content table */
- char *zContentRowid; /* "content_rowid=" option value */
+ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */
+ char *zContent; /* content table */
+ char *zContentRowid; /* "content_rowid=" option value */
int bColumnsize; /* "columnsize=" option value (dflt==1) */
+ int bTokendata; /* "tokendata=" option value (dflt==0) */
int eDetail; /* FTS5_DETAIL_XXX value */
char *zContentExprlist;
Fts5Tokenizer *pTok;
fts5_tokenizer *pTokApi;
+ int bLock; /* True when table is preparing statement */
+ int ePattern; /* FTS_PATTERN_XXX constant */
/* Values loaded from the %_config table */
+ int iVersion; /* fts5 file format 'version' */
int iCookie; /* Incremented when %_config is modified */
int pgsz; /* Approximate page size used in %_data */
int nAutomerge; /* 'automerge' setting */
@@ -199112,6 +230304,8 @@ struct Fts5Config {
int nHashSize; /* Bytes of memory for in-memory hash */
char *zRank; /* Name of rank function */
char *zRankArgs; /* Arguments to rank function */
+ int bSecureDelete; /* 'secure-delete' */
+ int nDeleteMerge; /* 'deletemerge' */
/* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
char **pzErrmsg;
@@ -199121,18 +230315,23 @@ struct Fts5Config {
#endif
};
-/* Current expected value of %_config table 'version' field */
-#define FTS5_CURRENT_VERSION 4
+/* Current expected value of %_config table 'version' field. And
+** the expected version if the 'secure-delete' option has ever been
+** set on the table. */
+#define FTS5_CURRENT_VERSION 4
+#define FTS5_CURRENT_VERSION_SECUREDELETE 5
#define FTS5_CONTENT_NORMAL 0
#define FTS5_CONTENT_NONE 1
#define FTS5_CONTENT_EXTERNAL 2
-#define FTS5_DETAIL_FULL 0
-#define FTS5_DETAIL_NONE 1
-#define FTS5_DETAIL_COLUMNS 2
-
+#define FTS5_DETAIL_FULL 0
+#define FTS5_DETAIL_NONE 1
+#define FTS5_DETAIL_COLUMNS 2
+#define FTS5_PATTERN_NONE 0
+#define FTS5_PATTERN_LIKE 65 /* matches SQLITE_INDEX_CONSTRAINT_LIKE */
+#define FTS5_PATTERN_GLOB 66 /* matches SQLITE_INDEX_CONSTRAINT_GLOB */
static int sqlite3Fts5ConfigParse(
Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char**
@@ -199189,7 +230388,7 @@ static void sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, char *zFmt, ...);
static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...);
#define fts5BufferZero(x) sqlite3Fts5BufferZero(x)
-#define fts5BufferAppendVarint(a,b,c) sqlite3Fts5BufferAppendVarint(a,b,c)
+#define fts5BufferAppendVarint(a,b,c) sqlite3Fts5BufferAppendVarint(a,b,(i64)c)
#define fts5BufferFree(a) sqlite3Fts5BufferFree(a)
#define fts5BufferAppendBlob(a,b,c,d) sqlite3Fts5BufferAppendBlob(a,b,c,d)
#define fts5BufferSet(a,b,c,d) sqlite3Fts5BufferSet(a,b,c,d)
@@ -199204,7 +230403,7 @@ static void sqlite3Fts5Put32(u8*, int);
static int sqlite3Fts5Get32(const u8*);
#define FTS5_POS2COLUMN(iPos) (int)(iPos >> 32)
-#define FTS5_POS2OFFSET(iPos) (int)(iPos & 0xFFFFFFFF)
+#define FTS5_POS2OFFSET(iPos) (int)(iPos & 0x7FFFFFFF)
typedef struct Fts5PoslistReader Fts5PoslistReader;
struct Fts5PoslistReader {
@@ -199239,7 +230438,7 @@ static int sqlite3Fts5PoslistNext64(
);
/* Malloc utility */
-static void *sqlite3Fts5MallocZero(int *pRc, int nByte);
+static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte);
static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn);
/* Character set tests (like isspace(), isalpha() etc.) */
@@ -199276,16 +230475,19 @@ struct Fts5IndexIter {
/*
** Values used as part of the flags argument passed to IndexQuery().
*/
-#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */
-#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */
-#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */
-#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */
+#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */
+#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */
+#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */
+#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */
/* The following are used internally by the fts5_index.c module. They are
** defined here only to make it easier to avoid clashes with the flags
** above. */
-#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010
-#define FTS5INDEX_QUERY_NOOUTPUT 0x0020
+#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010
+#define FTS5INDEX_QUERY_NOOUTPUT 0x0020
+#define FTS5INDEX_QUERY_SKIPHASH 0x0040
+#define FTS5INDEX_QUERY_NOTOKENDATA 0x0080
+#define FTS5INDEX_QUERY_SCANONETERM 0x0100
/*
** Create/destroy an Fts5Index object.
@@ -199297,27 +230499,27 @@ static int sqlite3Fts5IndexClose(Fts5Index *p);
** Return a simple checksum value based on the arguments.
*/
static u64 sqlite3Fts5IndexEntryCksum(
- i64 iRowid,
- int iCol,
- int iPos,
+ i64 iRowid,
+ int iCol,
+ int iPos,
int iIdx,
const char *pTerm,
int nTerm
);
/*
-** Argument p points to a buffer containing utf-8 text that is n bytes in
+** Argument p points to a buffer containing utf-8 text that is n bytes in
** size. Return the number of bytes in the nChar character prefix of the
** buffer, or 0 if there are less than nChar characters in total.
*/
static int sqlite3Fts5IndexCharlenToBytelen(
- const char *p,
- int nByte,
+ const char *p,
+ int nByte,
int nChar
);
/*
-** Open a new iterator to iterate though all rowids that match the
+** Open a new iterator to iterate though all rowids that match the
** specified token or token prefix.
*/
static int sqlite3Fts5IndexQuery(
@@ -199341,14 +230543,26 @@ static int sqlite3Fts5IterNextFrom(Fts5IndexIter*, i64 iMatch);
static void sqlite3Fts5IterClose(Fts5IndexIter*);
/*
+** Close the reader blob handle, if it is open.
+*/
+static void sqlite3Fts5IndexCloseReader(Fts5Index*);
+
+/*
** This interface is used by the fts5vocab module.
*/
static const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*);
static int sqlite3Fts5IterNextScan(Fts5IndexIter*);
+static void *sqlite3Fts5StructureRef(Fts5Index*);
+static void sqlite3Fts5StructureRelease(void*);
+static int sqlite3Fts5StructureTest(Fts5Index*, void*);
+/*
+** Used by xInstToken():
+*/
+static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*);
/*
-** Insert or remove data to or from the index. Each time a document is
+** Insert or remove data to or from the index. Each time a document is
** added to or removed from the index, this function is called one or more
** times.
**
@@ -199383,7 +230597,7 @@ static int sqlite3Fts5IndexSync(Fts5Index *p);
/*
** Discard any data stored in the in-memory hash tables. Do not write it
** to the database. Additionally, assume that the contents of the %_data
-** table may have changed on disk. So any in-memory caches of %_data
+** table may have changed on disk. So any in-memory caches of %_data
** records must be invalidated.
*/
static int sqlite3Fts5IndexRollback(Fts5Index *p);
@@ -199397,18 +230611,18 @@ static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int);
/*
** Functions called by the storage module as part of integrity-check.
*/
-static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum);
+static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum, int bUseCksum);
-/*
-** Called during virtual module initialization to register UDF
-** fts5_decode() with SQLite
+/*
+** Called during virtual module initialization to register UDF
+** fts5_decode() with SQLite
*/
static int sqlite3Fts5IndexInit(sqlite3*);
static int sqlite3Fts5IndexSetCookie(Fts5Index*, int);
/*
-** Return the total number of entries read from the %_data table by
+** Return the total number of entries read from the %_data table by
** this connection since it was created.
*/
static int sqlite3Fts5IndexReads(Fts5Index *p);
@@ -199420,19 +230634,29 @@ static int sqlite3Fts5IndexReset(Fts5Index *p);
static int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
+static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin);
+static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid);
+
+static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*);
+
+/* Used to populate hash tables for xInstToken in detail=none/column mode. */
+static int sqlite3Fts5IndexIterWriteTokendata(
+ Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff
+);
+
/*
** End of interface to code in fts5_index.c.
**************************************************************************/
/**************************************************************************
-** Interface to code in fts5_varint.c.
+** Interface to code in fts5_varint.c.
*/
static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v);
static int sqlite3Fts5GetVarintLen(u32 iVal);
static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*);
static int sqlite3Fts5PutVarint(unsigned char *p, u64 v);
-#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&b)
+#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&(b))
#define fts5GetVarint sqlite3Fts5GetVarint
#define fts5FastGetVarint32(a, iOff, nVal) { \
@@ -199450,26 +230674,37 @@ static int sqlite3Fts5PutVarint(unsigned char *p, u64 v);
/**************************************************************************
-** Interface to code in fts5.c.
+** Interface to code in fts5_main.c.
*/
+/*
+** Virtual-table object.
+*/
+typedef struct Fts5Table Fts5Table;
+struct Fts5Table {
+ sqlite3_vtab base; /* Base class used by SQLite core */
+ Fts5Config *pConfig; /* Virtual table configuration */
+ Fts5Index *pIndex; /* Full-text index */
+};
+
static int sqlite3Fts5GetTokenizer(
- Fts5Global*,
+ Fts5Global*,
const char **azArg,
int nArg,
- Fts5Tokenizer**,
- fts5_tokenizer**,
+ Fts5Config*,
char **pzErr
);
-static Fts5Index *sqlite3Fts5IndexFromCsrid(Fts5Global*, i64, Fts5Config **);
+static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64);
+
+static int sqlite3Fts5FlushToDisk(Fts5Table*);
/*
** End of interface to code in fts5.c.
**************************************************************************/
/**************************************************************************
-** Interface to code in fts5_hash.c.
+** Interface to code in fts5_hash.c.
*/
typedef struct Fts5Hash Fts5Hash;
@@ -199493,10 +230728,16 @@ static int sqlite3Fts5HashWrite(
*/
static void sqlite3Fts5HashClear(Fts5Hash*);
+/*
+** Return true if the hash is empty, false otherwise.
+*/
+static int sqlite3Fts5HashIsEmpty(Fts5Hash*);
+
static int sqlite3Fts5HashQuery(
Fts5Hash*, /* Hash table to query */
+ int nPre,
const char *pTerm, int nTerm, /* Query term */
- const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */
+ void **ppObj, /* OUT: Pointer to doclist for pTerm */
int *pnDoclist /* OUT: Size of doclist in bytes */
);
@@ -199508,17 +230749,19 @@ static void sqlite3Fts5HashScanNext(Fts5Hash*);
static int sqlite3Fts5HashScanEof(Fts5Hash*);
static void sqlite3Fts5HashScanEntry(Fts5Hash *,
const char **pzTerm, /* OUT: term (nul-terminated) */
+ int *pnTerm, /* OUT: Size of term in bytes */
const u8 **ppDoclist, /* OUT: pointer to doclist */
int *pnDoclist /* OUT: size of doclist in bytes */
);
+
/*
** End of interface to code in fts5_hash.c.
**************************************************************************/
/**************************************************************************
-** Interface to code in fts5_storage.c. fts5_storage.c contains contains
+** Interface to code in fts5_storage.c. fts5_storage.c contains contains
** code to access the data stored in the %_content and %_docsize tables.
*/
@@ -199539,7 +230782,7 @@ static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**);
static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*);
static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64);
-static int sqlite3Fts5StorageIntegrity(Fts5Storage *p);
+static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg);
static int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**);
static void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*);
@@ -199567,7 +230810,7 @@ static int sqlite3Fts5StorageReset(Fts5Storage *p);
/**************************************************************************
-** Interface to code in fts5_expr.c.
+** Interface to code in fts5_expr.c.
*/
typedef struct Fts5Expr Fts5Expr;
typedef struct Fts5ExprNode Fts5ExprNode;
@@ -199583,12 +230826,20 @@ struct Fts5Token {
/* Parse a MATCH expression. */
static int sqlite3Fts5ExprNew(
- Fts5Config *pConfig,
+ Fts5Config *pConfig,
+ int bPhraseToAnd,
int iCol, /* Column on LHS of MATCH operator */
const char *zExpr,
- Fts5Expr **ppNew,
+ Fts5Expr **ppNew,
char **pzErr
);
+static int sqlite3Fts5ExprPattern(
+ Fts5Config *pConfig,
+ int bGlob,
+ int iCol,
+ const char *zText,
+ Fts5Expr **pp
+);
/*
** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bDesc);
@@ -199605,6 +230856,7 @@ static int sqlite3Fts5ExprEof(Fts5Expr*);
static i64 sqlite3Fts5ExprRowid(Fts5Expr*);
static void sqlite3Fts5ExprFree(Fts5Expr*);
+static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2);
/* Called during startup to register a UDF with SQLite */
static int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*);
@@ -199624,6 +230876,10 @@ static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**);
static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *);
+static int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*);
+static int sqlite3Fts5ExprInstToken(Fts5Expr*, i64, int, int, int, int, const char**, int*);
+static void sqlite3Fts5ExprClearTokens(Fts5Expr*);
+
/*******************************************
** The fts5_expr.c API above this point is used by the other hand-written
** C code in this module. The interfaces below this point are called by
@@ -199646,8 +230902,8 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
);
static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
- Fts5Parse *pParse,
- Fts5ExprPhrase *pPhrase,
+ Fts5Parse *pParse,
+ Fts5ExprPhrase *pPhrase,
Fts5Token *pToken,
int bPrefix
);
@@ -199655,14 +230911,14 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
static void sqlite3Fts5ParseSetCaret(Fts5ExprPhrase*);
static Fts5ExprNearset *sqlite3Fts5ParseNearset(
- Fts5Parse*,
+ Fts5Parse*,
Fts5ExprNearset*,
- Fts5ExprPhrase*
+ Fts5ExprPhrase*
);
static Fts5Colset *sqlite3Fts5ParseColset(
- Fts5Parse*,
- Fts5Colset*,
+ Fts5Parse*,
+ Fts5Colset*,
Fts5Token *
);
@@ -199683,7 +230939,7 @@ static void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*);
/**************************************************************************
-** Interface to code in fts5_aux.c.
+** Interface to code in fts5_aux.c.
*/
static int sqlite3Fts5AuxInit(fts5_api*);
@@ -199692,16 +230948,20 @@ static int sqlite3Fts5AuxInit(fts5_api*);
**************************************************************************/
/**************************************************************************
-** Interface to code in fts5_tokenizer.c.
+** Interface to code in fts5_tokenizer.c.
*/
static int sqlite3Fts5TokenizerInit(fts5_api*);
+static int sqlite3Fts5TokenizerPattern(
+ int (*xCreate)(void*, const char**, int, Fts5Tokenizer**),
+ Fts5Tokenizer *pTok
+);
/*
** End of interface to code in fts5_tokenizer.c.
**************************************************************************/
/**************************************************************************
-** Interface to code in fts5_vocab.c.
+** Interface to code in fts5_vocab.c.
*/
static int sqlite3Fts5VocabInit(Fts5Global*, sqlite3*);
@@ -199712,13 +230972,13 @@ static int sqlite3Fts5VocabInit(Fts5Global*, sqlite3*);
/**************************************************************************
-** Interface to automatically generated code in fts5_unicode2.c.
+** Interface to automatically generated code in fts5_unicode2.c.
*/
static int sqlite3Fts5UnicodeIsdiacritic(int c);
static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic);
static int sqlite3Fts5UnicodeCatParse(const char*, u8*);
-static int sqlite3Fts5UnicodeCategory(int iCode);
+static int sqlite3Fts5UnicodeCategory(u32 iCode);
static void sqlite3Fts5UnicodeAscii(u8*, u8*);
/*
** End of interface to code in fts5_unicode2.c.
@@ -199742,6 +231002,9 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*);
#define FTS5_PLUS 14
#define FTS5_STAR 15
+/* This file is automatically generated by Lemon from input grammar
+** source file "fts5parse.y".
+*/
/*
** 2000-05-29
**
@@ -199766,8 +231029,6 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*);
** The following is the concatenation of all %include directives from the
** input grammar file:
*/
-/* #include <stdio.h> */
-/* #include <assert.h> */
/************ Begin %include sections from the grammar ************************/
/* #include "fts5Int.h" */
@@ -199797,11 +231058,26 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*);
#define fts5YYMALLOCARGTYPE u64
/**************** End of %include directives **********************************/
-/* These constants specify the various numeric values for terminal symbols
-** in a format understandable to "makeheaders". This section is blank unless
-** "lemon" is run with the "-m" command-line option.
-***************** Begin makeheaders token definitions *************************/
-/**************** End makeheaders token definitions ***************************/
+/* These constants specify the various numeric values for terminal symbols.
+***************** Begin token definitions *************************************/
+#ifndef FTS5_OR
+#define FTS5_OR 1
+#define FTS5_AND 2
+#define FTS5_NOT 3
+#define FTS5_TERM 4
+#define FTS5_COLON 5
+#define FTS5_MINUS 6
+#define FTS5_LCP 7
+#define FTS5_RCP 8
+#define FTS5_STRING 9
+#define FTS5_LP 10
+#define FTS5_RP 11
+#define FTS5_CARET 12
+#define FTS5_COMMA 13
+#define FTS5_PLUS 14
+#define FTS5_STAR 15
+#endif
+/**************** End token definitions ***************************************/
/* The next sections is a series of control #defines.
** various aspects of the generated parser.
@@ -199826,7 +231102,7 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*);
** the minor type might be the name of the identifier.
** Each non-terminal can have a different minor type.
** Terminal symbols all have the same minor type, though.
-** This macros defines the minor type for terminal
+** This macros defines the minor type for terminal
** symbols.
** fts5YYMINORTYPE is the data type used for all minor types.
** This is typically a union of many types, one of
@@ -199886,6 +231162,7 @@ typedef union {
#define sqlite3Fts5ParserCTX_STORE
#define fts5YYNSTATE 35
#define fts5YYNRULE 28
+#define fts5YYNRULE_WITH_ACTION 28
#define fts5YYNFTS5TOKEN 16
#define fts5YY_MAX_SHIFT 34
#define fts5YY_MIN_SHIFTREDUCE 52
@@ -199914,7 +231191,7 @@ typedef union {
/* Next are the tables used to determine what action to take based on the
** current state and lookahead token. These tables are used to implement
** functions that take a state number and lookahead value and return an
-** action integer.
+** action integer.
**
** Suppose the action integer is N. Then the action is determined as
** follows
@@ -200014,9 +231291,9 @@ static const fts5YYACTIONTYPE fts5yy_default[] = {
};
/********** End of lemon-generated parsing tables *****************************/
-/* The next table maps tokens (terminal symbols) into fallback tokens.
+/* The next table maps tokens (terminal symbols) into fallback tokens.
** If a construct like the following:
-**
+**
** %fallback ID X Y Z.
**
** appears in the grammar, then ID becomes a fallback token for X, Y,
@@ -200081,6 +231358,7 @@ struct fts5yyParser {
};
typedef struct fts5yyParser fts5yyParser;
+/* #include <assert.h> */
#ifndef NDEBUG
/* #include <stdio.h> */
static FILE *fts5yyTraceFILE = 0;
@@ -200088,10 +231366,10 @@ static char *fts5yyTracePrompt = 0;
#endif /* NDEBUG */
#ifndef NDEBUG
-/*
+/*
** Turn parser tracing on by giving a stream to which to write the trace
** and a prompt to preface each trace message. Tracing is turned off
-** by making either argument NULL
+** by making either argument NULL
**
** Inputs:
** <ul>
@@ -200116,7 +231394,7 @@ static void sqlite3Fts5ParserTrace(FILE *TraceFILE, char *zTracePrompt){
#if defined(fts5YYCOVERAGE) || !defined(NDEBUG)
/* For tracing shifts, the names of all terminals and nonterminals
** are required. The following table supplies these names */
-static const char *const fts5yyTokenName[] = {
+static const char *const fts5yyTokenName[] = {
/* 0 */ "$",
/* 1 */ "OR",
/* 2 */ "AND",
@@ -200212,7 +231490,7 @@ static int fts5yyGrowStack(fts5yyParser *p){
#endif
p->fts5yystksz = newSize;
}
- return pNew==0;
+ return pNew==0;
}
#endif
@@ -200254,7 +231532,7 @@ static void sqlite3Fts5ParserInit(void *fts5yypRawParser sqlite3Fts5ParserCTX_PD
}
#ifndef sqlite3Fts5Parser_ENGINEALWAYSONSTACK
-/*
+/*
** This function allocates a new parser.
** The only argument is a pointer to a function which works like
** malloc.
@@ -200281,7 +231559,7 @@ static void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(fts5YYMALLOCARGTYPE) sql
/* The following function deletes the "minor type" or semantic value
** associated with a symbol. The symbol can be either a terminal
** or nonterminal. "fts5yymajor" is the symbol code, and "fts5yypminor" is
-** a pointer to the value to be deleted. The code used to do the
+** a pointer to the value to be deleted. The code used to do the
** deletions is derived from the %destructor and/or %token_destructor
** directives of the input grammar.
*/
@@ -200296,7 +231574,7 @@ static void fts5yy_destructor(
/* Here is inserted the actions which take place when a
** terminal or non-terminal is destroyed. This can happen
** when the symbol is popped from the stack during a
- ** reduce or during error processing or when a parser is
+ ** reduce or during error processing or when a parser is
** being destroyed before it is finished parsing.
**
** Note: during a reduce, the only symbols destroyed are those
@@ -200306,31 +231584,31 @@ static void fts5yy_destructor(
/********* Begin destructor definitions ***************************************/
case 16: /* input */
{
- (void)pParse;
+ (void)pParse;
}
break;
case 17: /* expr */
case 18: /* cnearset */
case 19: /* exprlist */
{
- sqlite3Fts5ParseNodeFree((fts5yypminor->fts5yy24));
+ sqlite3Fts5ParseNodeFree((fts5yypminor->fts5yy24));
}
break;
case 20: /* colset */
case 21: /* colsetlist */
{
- sqlite3_free((fts5yypminor->fts5yy11));
+ sqlite3_free((fts5yypminor->fts5yy11));
}
break;
case 22: /* nearset */
case 23: /* nearphrases */
{
- sqlite3Fts5ParseNearsetFree((fts5yypminor->fts5yy46));
+ sqlite3Fts5ParseNearsetFree((fts5yypminor->fts5yy46));
}
break;
case 24: /* phrase */
{
- sqlite3Fts5ParsePhraseFree((fts5yypminor->fts5yy53));
+ sqlite3Fts5ParsePhraseFree((fts5yypminor->fts5yy53));
}
break;
/********* End destructor definitions *****************************************/
@@ -200371,7 +231649,7 @@ static void sqlite3Fts5ParserFinalize(void *p){
}
#ifndef sqlite3Fts5Parser_ENGINEALWAYSONSTACK
-/*
+/*
** Deallocate and destroy a parser. Destructors are called for
** all stack elements before shutting the parser down.
**
@@ -200456,15 +231734,18 @@ static fts5YYACTIONTYPE fts5yy_find_shift_action(
do{
i = fts5yy_shift_ofst[stateno];
assert( i>=0 );
- /* assert( i+fts5YYNFTS5TOKEN<=(int)fts5YY_NLOOKAHEAD ); */
+ assert( i<=fts5YY_ACTTAB_COUNT );
+ assert( i+fts5YYNFTS5TOKEN<=(int)fts5YY_NLOOKAHEAD );
assert( iLookAhead!=fts5YYNOCODE );
assert( iLookAhead < fts5YYNFTS5TOKEN );
i += iLookAhead;
- if( i>=fts5YY_NLOOKAHEAD || fts5yy_lookahead[i]!=iLookAhead ){
+ assert( i<(int)fts5YY_NLOOKAHEAD );
+ if( fts5yy_lookahead[i]!=iLookAhead ){
#ifdef fts5YYFALLBACK
fts5YYCODETYPE iFallback; /* Fallback token */
- if( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])
- && (iFallback = fts5yyFallback[iLookAhead])!=0 ){
+ assert( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0]) );
+ iFallback = fts5yyFallback[iLookAhead];
+ if( iFallback!=0 ){
#ifndef NDEBUG
if( fts5yyTraceFILE ){
fprintf(fts5yyTraceFILE, "%sFALLBACK %s => %s\n",
@@ -200479,16 +231760,8 @@ static fts5YYACTIONTYPE fts5yy_find_shift_action(
#ifdef fts5YYWILDCARD
{
int j = i - iLookAhead + fts5YYWILDCARD;
- if(
-#if fts5YY_SHIFT_MIN+fts5YYWILDCARD<0
- j>=0 &&
-#endif
-#if fts5YY_SHIFT_MAX+fts5YYWILDCARD>=fts5YY_ACTTAB_COUNT
- j<fts5YY_ACTTAB_COUNT &&
-#endif
- j<(int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0])) &&
- fts5yy_lookahead[j]==fts5YYWILDCARD && iLookAhead>0
- ){
+ assert( j<(int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0])) );
+ if( fts5yy_lookahead[j]==fts5YYWILDCARD && iLookAhead>0 ){
#ifndef NDEBUG
if( fts5yyTraceFILE ){
fprintf(fts5yyTraceFILE, "%sWILDCARD %s => %s\n",
@@ -200502,6 +231775,7 @@ static fts5YYACTIONTYPE fts5yy_find_shift_action(
#endif /* fts5YYWILDCARD */
return fts5yy_default[stateno];
}else{
+ assert( i>=0 && i<(int)(sizeof(fts5yy_action)/sizeof(fts5yy_action[0])) );
return fts5yy_action[i];
}
}while(1);
@@ -200597,7 +231871,7 @@ static void fts5yy_shift(
assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack) );
}
#endif
-#if fts5YYSTACKDEPTH>0
+#if fts5YYSTACKDEPTH>0
if( fts5yypParser->fts5yytos>fts5yypParser->fts5yystackEnd ){
fts5yypParser->fts5yytos--;
fts5yyStackOverflow(fts5yypParser);
@@ -200622,41 +231896,70 @@ static void fts5yy_shift(
fts5yyTraceShift(fts5yypParser, fts5yyNewState, "Shift");
}
-/* The following table contains information about every rule that
-** is used during the reduce.
-*/
-static const struct {
- fts5YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
- signed char nrhs; /* Negative of the number of RHS symbols in the rule */
-} fts5yyRuleInfo[] = {
- { 16, -1 }, /* (0) input ::= expr */
- { 20, -4 }, /* (1) colset ::= MINUS LCP colsetlist RCP */
- { 20, -3 }, /* (2) colset ::= LCP colsetlist RCP */
- { 20, -1 }, /* (3) colset ::= STRING */
- { 20, -2 }, /* (4) colset ::= MINUS STRING */
- { 21, -2 }, /* (5) colsetlist ::= colsetlist STRING */
- { 21, -1 }, /* (6) colsetlist ::= STRING */
- { 17, -3 }, /* (7) expr ::= expr AND expr */
- { 17, -3 }, /* (8) expr ::= expr OR expr */
- { 17, -3 }, /* (9) expr ::= expr NOT expr */
- { 17, -5 }, /* (10) expr ::= colset COLON LP expr RP */
- { 17, -3 }, /* (11) expr ::= LP expr RP */
- { 17, -1 }, /* (12) expr ::= exprlist */
- { 19, -1 }, /* (13) exprlist ::= cnearset */
- { 19, -2 }, /* (14) exprlist ::= exprlist cnearset */
- { 18, -1 }, /* (15) cnearset ::= nearset */
- { 18, -3 }, /* (16) cnearset ::= colset COLON nearset */
- { 22, -1 }, /* (17) nearset ::= phrase */
- { 22, -2 }, /* (18) nearset ::= CARET phrase */
- { 22, -5 }, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */
- { 23, -1 }, /* (20) nearphrases ::= phrase */
- { 23, -2 }, /* (21) nearphrases ::= nearphrases phrase */
- { 25, 0 }, /* (22) neardist_opt ::= */
- { 25, -2 }, /* (23) neardist_opt ::= COMMA STRING */
- { 24, -4 }, /* (24) phrase ::= phrase PLUS STRING star_opt */
- { 24, -2 }, /* (25) phrase ::= STRING star_opt */
- { 26, -1 }, /* (26) star_opt ::= STAR */
- { 26, 0 }, /* (27) star_opt ::= */
+/* For rule J, fts5yyRuleInfoLhs[J] contains the symbol on the left-hand side
+** of that rule */
+static const fts5YYCODETYPE fts5yyRuleInfoLhs[] = {
+ 16, /* (0) input ::= expr */
+ 20, /* (1) colset ::= MINUS LCP colsetlist RCP */
+ 20, /* (2) colset ::= LCP colsetlist RCP */
+ 20, /* (3) colset ::= STRING */
+ 20, /* (4) colset ::= MINUS STRING */
+ 21, /* (5) colsetlist ::= colsetlist STRING */
+ 21, /* (6) colsetlist ::= STRING */
+ 17, /* (7) expr ::= expr AND expr */
+ 17, /* (8) expr ::= expr OR expr */
+ 17, /* (9) expr ::= expr NOT expr */
+ 17, /* (10) expr ::= colset COLON LP expr RP */
+ 17, /* (11) expr ::= LP expr RP */
+ 17, /* (12) expr ::= exprlist */
+ 19, /* (13) exprlist ::= cnearset */
+ 19, /* (14) exprlist ::= exprlist cnearset */
+ 18, /* (15) cnearset ::= nearset */
+ 18, /* (16) cnearset ::= colset COLON nearset */
+ 22, /* (17) nearset ::= phrase */
+ 22, /* (18) nearset ::= CARET phrase */
+ 22, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */
+ 23, /* (20) nearphrases ::= phrase */
+ 23, /* (21) nearphrases ::= nearphrases phrase */
+ 25, /* (22) neardist_opt ::= */
+ 25, /* (23) neardist_opt ::= COMMA STRING */
+ 24, /* (24) phrase ::= phrase PLUS STRING star_opt */
+ 24, /* (25) phrase ::= STRING star_opt */
+ 26, /* (26) star_opt ::= STAR */
+ 26, /* (27) star_opt ::= */
+};
+
+/* For rule J, fts5yyRuleInfoNRhs[J] contains the negative of the number
+** of symbols on the right-hand side of that rule. */
+static const signed char fts5yyRuleInfoNRhs[] = {
+ -1, /* (0) input ::= expr */
+ -4, /* (1) colset ::= MINUS LCP colsetlist RCP */
+ -3, /* (2) colset ::= LCP colsetlist RCP */
+ -1, /* (3) colset ::= STRING */
+ -2, /* (4) colset ::= MINUS STRING */
+ -2, /* (5) colsetlist ::= colsetlist STRING */
+ -1, /* (6) colsetlist ::= STRING */
+ -3, /* (7) expr ::= expr AND expr */
+ -3, /* (8) expr ::= expr OR expr */
+ -3, /* (9) expr ::= expr NOT expr */
+ -5, /* (10) expr ::= colset COLON LP expr RP */
+ -3, /* (11) expr ::= LP expr RP */
+ -1, /* (12) expr ::= exprlist */
+ -1, /* (13) exprlist ::= cnearset */
+ -2, /* (14) exprlist ::= exprlist cnearset */
+ -1, /* (15) cnearset ::= nearset */
+ -3, /* (16) cnearset ::= colset COLON nearset */
+ -1, /* (17) nearset ::= phrase */
+ -2, /* (18) nearset ::= CARET phrase */
+ -5, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */
+ -1, /* (20) nearphrases ::= phrase */
+ -2, /* (21) nearphrases ::= nearphrases phrase */
+ 0, /* (22) neardist_opt ::= */
+ -2, /* (23) neardist_opt ::= COMMA STRING */
+ -4, /* (24) phrase ::= phrase PLUS STRING star_opt */
+ -2, /* (25) phrase ::= STRING star_opt */
+ -1, /* (26) star_opt ::= STAR */
+ 0, /* (27) star_opt ::= */
};
static void fts5yy_accept(fts5yyParser*); /* Forward Declaration */
@@ -200686,51 +231989,6 @@ static fts5YYACTIONTYPE fts5yy_reduce(
(void)fts5yyLookahead;
(void)fts5yyLookaheadToken;
fts5yymsp = fts5yypParser->fts5yytos;
-#ifndef NDEBUG
- if( fts5yyTraceFILE && fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ){
- fts5yysize = fts5yyRuleInfo[fts5yyruleno].nrhs;
- if( fts5yysize ){
- fprintf(fts5yyTraceFILE, "%sReduce %d [%s], go to state %d.\n",
- fts5yyTracePrompt,
- fts5yyruleno, fts5yyRuleName[fts5yyruleno], fts5yymsp[fts5yysize].stateno);
- }else{
- fprintf(fts5yyTraceFILE, "%sReduce %d [%s].\n",
- fts5yyTracePrompt, fts5yyruleno, fts5yyRuleName[fts5yyruleno]);
- }
- }
-#endif /* NDEBUG */
-
- /* Check that the stack is large enough to grow by a single entry
- ** if the RHS of the rule is empty. This ensures that there is room
- ** enough on the stack to push the LHS value */
- if( fts5yyRuleInfo[fts5yyruleno].nrhs==0 ){
-#ifdef fts5YYTRACKMAXSTACKDEPTH
- if( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){
- fts5yypParser->fts5yyhwm++;
- assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack));
- }
-#endif
-#if fts5YYSTACKDEPTH>0
- if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){
- fts5yyStackOverflow(fts5yypParser);
- /* The call to fts5yyStackOverflow() above pops the stack until it is
- ** empty, causing the main parser loop to exit. So the return value
- ** is never used and does not matter. */
- return 0;
- }
-#else
- if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){
- if( fts5yyGrowStack(fts5yypParser) ){
- fts5yyStackOverflow(fts5yypParser);
- /* The call to fts5yyStackOverflow() above pops the stack until it is
- ** empty, causing the main parser loop to exit. So the return value
- ** is never used and does not matter. */
- return 0;
- }
- fts5yymsp = fts5yypParser->fts5yytos;
- }
-#endif
- }
switch( fts5yyruleno ){
/* Beginning here are the reduction cases. A typical example
@@ -200747,7 +232005,7 @@ static fts5YYACTIONTYPE fts5yy_reduce(
{ sqlite3Fts5ParseFinished(pParse, fts5yymsp[0].minor.fts5yy24); }
break;
case 1: /* colset ::= MINUS LCP colsetlist RCP */
-{
+{
fts5yymsp[-3].minor.fts5yy11 = sqlite3Fts5ParseColsetInvert(pParse, fts5yymsp[-1].minor.fts5yy11);
}
break;
@@ -200767,13 +232025,13 @@ static fts5YYACTIONTYPE fts5yy_reduce(
}
break;
case 5: /* colsetlist ::= colsetlist STRING */
-{
+{
fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, fts5yymsp[-1].minor.fts5yy11, &fts5yymsp[0].minor.fts5yy0); }
fts5yymsp[-1].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
break;
case 6: /* colsetlist ::= STRING */
-{
- fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
+{
+ fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
}
fts5yymsp[0].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
break;
@@ -200817,14 +232075,14 @@ static fts5YYACTIONTYPE fts5yy_reduce(
fts5yymsp[-1].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
break;
case 15: /* cnearset ::= nearset */
-{
- fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy46);
+{
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy46);
}
fts5yymsp[0].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
break;
case 16: /* cnearset ::= colset COLON nearset */
-{
- fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy46);
+{
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy46);
sqlite3Fts5ParseSetColset(pParse, fts5yylhsminor.fts5yy24, fts5yymsp[-2].minor.fts5yy11);
}
fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
@@ -200834,9 +232092,9 @@ static fts5YYACTIONTYPE fts5yy_reduce(
fts5yymsp[0].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
break;
case 18: /* nearset ::= CARET phrase */
-{
+{
sqlite3Fts5ParseSetCaret(fts5yymsp[0].minor.fts5yy53);
- fts5yymsp[-1].minor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53);
+ fts5yymsp[-1].minor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53);
}
break;
case 19: /* nearset ::= STRING LP nearphrases neardist_opt RP */
@@ -200848,8 +232106,8 @@ static fts5YYACTIONTYPE fts5yy_reduce(
fts5yymsp[-4].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
break;
case 20: /* nearphrases ::= phrase */
-{
- fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53);
+{
+ fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53);
}
fts5yymsp[0].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
break;
@@ -200866,13 +232124,13 @@ static fts5YYACTIONTYPE fts5yy_reduce(
{ fts5yymsp[-1].minor.fts5yy0 = fts5yymsp[0].minor.fts5yy0; }
break;
case 24: /* phrase ::= phrase PLUS STRING star_opt */
-{
+{
fts5yylhsminor.fts5yy53 = sqlite3Fts5ParseTerm(pParse, fts5yymsp[-3].minor.fts5yy53, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy4);
}
fts5yymsp[-3].minor.fts5yy53 = fts5yylhsminor.fts5yy53;
break;
case 25: /* phrase ::= STRING star_opt */
-{
+{
fts5yylhsminor.fts5yy53 = sqlite3Fts5ParseTerm(pParse, 0, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy4);
}
fts5yymsp[-1].minor.fts5yy53 = fts5yylhsminor.fts5yy53;
@@ -200887,9 +232145,9 @@ static fts5YYACTIONTYPE fts5yy_reduce(
break;
/********** End reduce actions ************************************************/
};
- assert( fts5yyruleno<sizeof(fts5yyRuleInfo)/sizeof(fts5yyRuleInfo[0]) );
- fts5yygoto = fts5yyRuleInfo[fts5yyruleno].lhs;
- fts5yysize = fts5yyRuleInfo[fts5yyruleno].nrhs;
+ assert( fts5yyruleno<sizeof(fts5yyRuleInfoLhs)/sizeof(fts5yyRuleInfoLhs[0]) );
+ fts5yygoto = fts5yyRuleInfoLhs[fts5yyruleno];
+ fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno];
fts5yyact = fts5yy_find_reduce_action(fts5yymsp[fts5yysize].stateno,(fts5YYCODETYPE)fts5yygoto);
/* There are no SHIFTREDUCE actions on nonterminals because the table
@@ -201033,12 +232291,56 @@ static void sqlite3Fts5Parser(
}
#endif
- do{
+ while(1){ /* Exit by "break" */
+ assert( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystack );
assert( fts5yyact==fts5yypParser->fts5yytos->stateno );
fts5yyact = fts5yy_find_shift_action((fts5YYCODETYPE)fts5yymajor,fts5yyact);
if( fts5yyact >= fts5YY_MIN_REDUCE ){
- fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyact-fts5YY_MIN_REDUCE,fts5yymajor,
- fts5yyminor sqlite3Fts5ParserCTX_PARAM);
+ unsigned int fts5yyruleno = fts5yyact - fts5YY_MIN_REDUCE; /* Reduce by this rule */
+#ifndef NDEBUG
+ assert( fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) );
+ if( fts5yyTraceFILE ){
+ int fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno];
+ if( fts5yysize ){
+ fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
+ fts5yyTracePrompt,
+ fts5yyruleno, fts5yyRuleName[fts5yyruleno],
+ fts5yyruleno<fts5YYNRULE_WITH_ACTION ? "" : " without external action",
+ fts5yypParser->fts5yytos[fts5yysize].stateno);
+ }else{
+ fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s.\n",
+ fts5yyTracePrompt, fts5yyruleno, fts5yyRuleName[fts5yyruleno],
+ fts5yyruleno<fts5YYNRULE_WITH_ACTION ? "" : " without external action");
+ }
+ }
+#endif /* NDEBUG */
+
+ /* Check that the stack is large enough to grow by a single entry
+ ** if the RHS of the rule is empty. This ensures that there is room
+ ** enough on the stack to push the LHS value */
+ if( fts5yyRuleInfoNRhs[fts5yyruleno]==0 ){
+#ifdef fts5YYTRACKMAXSTACKDEPTH
+ if( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){
+ fts5yypParser->fts5yyhwm++;
+ assert( fts5yypParser->fts5yyhwm ==
+ (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack));
+ }
+#endif
+#if fts5YYSTACKDEPTH>0
+ if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){
+ fts5yyStackOverflow(fts5yypParser);
+ break;
+ }
+#else
+ if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){
+ if( fts5yyGrowStack(fts5yypParser) ){
+ fts5yyStackOverflow(fts5yypParser);
+ break;
+ }
+ }
+#endif
+ }
+ fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyruleno,fts5yymajor,fts5yyminor sqlite3Fts5ParserCTX_PARAM);
}else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){
fts5yy_shift(fts5yypParser,fts5yyact,(fts5YYCODETYPE)fts5yymajor,fts5yyminor);
#ifndef fts5YYNOERRORRECOVERY
@@ -201063,7 +232365,7 @@ static void sqlite3Fts5Parser(
#ifdef fts5YYERRORSYMBOL
/* A syntax error has occurred.
** The response to an error depends upon whether or not the
- ** grammar defines an error token "ERROR".
+ ** grammar defines an error token "ERROR".
**
** This is what we do if the grammar does define ERROR:
**
@@ -201094,14 +232396,13 @@ static void sqlite3Fts5Parser(
fts5yy_destructor(fts5yypParser, (fts5YYCODETYPE)fts5yymajor, &fts5yyminorunion);
fts5yymajor = fts5YYNOCODE;
}else{
- while( fts5yypParser->fts5yytos >= fts5yypParser->fts5yystack
- && (fts5yyact = fts5yy_find_reduce_action(
- fts5yypParser->fts5yytos->stateno,
- fts5YYERRORSYMBOL)) > fts5YY_MAX_SHIFTREDUCE
- ){
+ while( fts5yypParser->fts5yytos > fts5yypParser->fts5yystack ){
+ fts5yyact = fts5yy_find_reduce_action(fts5yypParser->fts5yytos->stateno,
+ fts5YYERRORSYMBOL);
+ if( fts5yyact<=fts5YY_MAX_SHIFTREDUCE ) break;
fts5yy_pop_parser_stack(fts5yypParser);
}
- if( fts5yypParser->fts5yytos < fts5yypParser->fts5yystack || fts5yymajor==0 ){
+ if( fts5yypParser->fts5yytos <= fts5yypParser->fts5yystack || fts5yymajor==0 ){
fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion);
fts5yy_parse_failed(fts5yypParser);
#ifndef fts5YYNOERRORRECOVERY
@@ -201151,7 +232452,7 @@ static void sqlite3Fts5Parser(
break;
#endif
}
- }while( fts5yypParser->fts5yytos>fts5yypParser->fts5yystack );
+ }
#ifndef NDEBUG
if( fts5yyTraceFILE ){
fts5yyStackEntry *i;
@@ -201173,13 +232474,12 @@ static void sqlite3Fts5Parser(
*/
static int sqlite3Fts5ParserFallback(int iToken){
#ifdef fts5YYFALLBACK
- if( iToken<(int)(sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])) ){
- return fts5yyFallback[iToken];
- }
+ assert( iToken<(int)(sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])) );
+ return fts5yyFallback[iToken];
#else
(void)iToken;
-#endif
return 0;
+#endif
}
/*
@@ -201200,7 +232500,7 @@ static int sqlite3Fts5ParserFallback(int iToken){
#include <math.h> /* amalgamator: keep */
/*
-** Object used to iterate through all "coalesced phrase instances" in
+** Object used to iterate through all "coalesced phrase instances" in
** a single column of the current row. If the phrase instances in the
** column being considered do not overlap, this object simply iterates
** through them. Or, if they do overlap (share one or more tokens in
@@ -201263,7 +232563,7 @@ static int fts5CInstIterNext(CInstIter *pIter){
}
/*
-** Initialize the iterator object indicated by the final parameter to
+** Initialize the iterator object indicated by the final parameter to
** iterate through coalesced phrase instances in column iCol.
*/
static int fts5CInstIterInit(
@@ -201294,33 +232594,37 @@ static int fts5CInstIterInit(
*/
typedef struct HighlightContext HighlightContext;
struct HighlightContext {
- CInstIter iter; /* Coalesced Instance Iterator */
- int iPos; /* Current token offset in zIn[] */
+ /* Constant parameters to fts5HighlightCb() */
int iRangeStart; /* First token to include */
int iRangeEnd; /* If non-zero, last token to include */
const char *zOpen; /* Opening highlight */
const char *zClose; /* Closing highlight */
const char *zIn; /* Input text */
int nIn; /* Size of input text in bytes */
- int iOff; /* Current offset within zIn[] */
+
+ /* Variables modified by fts5HighlightCb() */
+ CInstIter iter; /* Coalesced Instance Iterator */
+ int iPos; /* Current token offset in zIn[] */
+ int iOff; /* Have copied up to this offset in zIn[] */
+ int bOpen; /* True if highlight is open */
char *zOut; /* Output value */
};
/*
** Append text to the HighlightContext output string - p->zOut. Argument
-** z points to a buffer containing n bytes of text to append. If n is
+** z points to a buffer containing n bytes of text to append. If n is
** negative, everything up until the first '\0' is appended to the output.
**
-** If *pRc is set to any value other than SQLITE_OK when this function is
-** called, it is a no-op. If an error (i.e. an OOM condition) is encountered,
-** *pRc is set to an error code before returning.
+** If *pRc is set to any value other than SQLITE_OK when this function is
+** called, it is a no-op. If an error (i.e. an OOM condition) is encountered,
+** *pRc is set to an error code before returning.
*/
static void fts5HighlightAppend(
- int *pRc,
- HighlightContext *p,
+ int *pRc,
+ HighlightContext *p,
const char *z, int n
){
- if( *pRc==SQLITE_OK ){
+ if( *pRc==SQLITE_OK && z ){
if( n<0 ) n = (int)strlen(z);
p->zOut = sqlite3_mprintf("%z%.*s", p->zOut, n, z);
if( p->zOut==0 ) *pRc = SQLITE_NOMEM;
@@ -201335,8 +232639,8 @@ static int fts5HighlightCb(
int tflags, /* Mask of FTS5_TOKEN_* flags */
const char *pToken, /* Buffer containing token */
int nToken, /* Size of token in bytes */
- int iStartOff, /* Start offset of token */
- int iEndOff /* End offset of token */
+ int iStartOff, /* Start byte offset of token */
+ int iEndOff /* End byte offset of token */
){
HighlightContext *p = (HighlightContext*)pContext;
int rc = SQLITE_OK;
@@ -201347,35 +232651,60 @@ static int fts5HighlightCb(
if( tflags & FTS5_TOKEN_COLOCATED ) return SQLITE_OK;
iPos = p->iPos++;
- if( p->iRangeEnd>0 ){
+ if( p->iRangeEnd>=0 ){
if( iPos<p->iRangeStart || iPos>p->iRangeEnd ) return SQLITE_OK;
if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff;
}
- if( iPos==p->iter.iStart ){
+ /* If the parenthesis is open, and this token is not part of the current
+ ** phrase, and the starting byte offset of this token is past the point
+ ** that has currently been copied into the output buffer, close the
+ ** parenthesis. */
+ if( p->bOpen
+ && (iPos<=p->iter.iStart || p->iter.iStart<0)
+ && iStartOff>p->iOff
+ ){
+ fts5HighlightAppend(&rc, p, p->zClose, -1);
+ p->bOpen = 0;
+ }
+
+ /* If this is the start of a new phrase, and the highlight is not open:
+ **
+ ** * copy text from the input up to the start of the phrase, and
+ ** * open the highlight.
+ */
+ if( iPos==p->iter.iStart && p->bOpen==0 ){
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff);
fts5HighlightAppend(&rc, p, p->zOpen, -1);
p->iOff = iStartOff;
+ p->bOpen = 1;
}
if( iPos==p->iter.iEnd ){
- if( p->iRangeEnd && p->iter.iStart<p->iRangeStart ){
+ if( p->bOpen==0 ){
+ assert( p->iRangeEnd>=0 );
fts5HighlightAppend(&rc, p, p->zOpen, -1);
+ p->bOpen = 1;
}
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
- fts5HighlightAppend(&rc, p, p->zClose, -1);
p->iOff = iEndOff;
+
if( rc==SQLITE_OK ){
rc = fts5CInstIterNext(&p->iter);
}
}
- if( p->iRangeEnd>0 && iPos==p->iRangeEnd ){
- fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
- p->iOff = iEndOff;
- if( iPos>=p->iter.iStart && iPos<p->iter.iEnd ){
+ if( iPos==p->iRangeEnd ){
+ if( p->bOpen ){
+ if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
+ p->iOff = iEndOff;
+ }
fts5HighlightAppend(&rc, p, p->zClose, -1);
+ p->bOpen = 0;
}
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
+ p->iOff = iEndOff;
}
return rc;
@@ -201405,9 +232734,12 @@ static void fts5HighlightFunction(
memset(&ctx, 0, sizeof(HighlightContext));
ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
+ ctx.iRangeEnd = -1;
rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn);
-
- if( ctx.zIn ){
+ if( rc==SQLITE_RANGE ){
+ sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC);
+ rc = SQLITE_OK;
+ }else if( ctx.zIn ){
if( rc==SQLITE_OK ){
rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
}
@@ -201415,6 +232747,9 @@ static void fts5HighlightFunction(
if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
}
+ if( ctx.bOpen ){
+ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
+ }
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
if( rc==SQLITE_OK ){
@@ -201452,7 +232787,7 @@ static int fts5SentenceFinderAdd(Fts5SFinder *p, int iAdd){
int nNew = p->nFirstAlloc ? p->nFirstAlloc*2 : 64;
int *aNew;
- aNew = (int*)sqlite3_realloc(p->aFirst, nNew*sizeof(int));
+ aNew = (int*)sqlite3_realloc64(p->aFirst, nNew*sizeof(int));
if( aNew==0 ) return SQLITE_NOMEM;
p->aFirst = aNew;
p->nFirstAlloc = nNew;
@@ -201519,11 +232854,12 @@ static int fts5SnippetScore(
int nInst;
int nScore = 0;
int iLast = 0;
+ sqlite3_int64 iEnd = (sqlite3_int64)iPos + nToken;
rc = pApi->xInstCount(pFts, &nInst);
for(i=0; i<nInst && rc==SQLITE_OK; i++){
rc = pApi->xInst(pFts, i, &ip, &ic, &iOff);
- if( rc==SQLITE_OK && ic==iCol && iOff>=iPos && iOff<(iPos+nToken) ){
+ if( rc==SQLITE_OK && ic==iCol && iOff>=iPos && iOff<iEnd ){
nScore += (aSeen[ip] ? 1 : 1000);
aSeen[ip] = 1;
if( iFirst<0 ) iFirst = iOff;
@@ -201533,17 +232869,17 @@ static int fts5SnippetScore(
*pnScore = nScore;
if( piPos ){
- int iAdj = iFirst - (nToken - (iLast-iFirst)) / 2;
+ sqlite3_int64 iAdj = iFirst - (nToken - (iLast-iFirst)) / 2;
if( (iAdj+nToken)>nDocsize ) iAdj = nDocsize - nToken;
if( iAdj<0 ) iAdj = 0;
- *piPos = iAdj;
+ *piPos = (int)iAdj;
}
return rc;
}
/*
-** Return the value in pVal interpreted as utf-8 text. Except, if pVal
+** Return the value in pVal interpreted as utf-8 text. Except, if pVal
** contains a NULL value, return a pointer to a static string zero
** bytes in length instead of a NULL pointer.
*/
@@ -201589,6 +232925,7 @@ static void fts5SnippetFunction(
iCol = sqlite3_value_int(apVal[0]);
ctx.zOpen = fts5ValueToText(apVal[1]);
ctx.zClose = fts5ValueToText(apVal[2]);
+ ctx.iRangeEnd = -1;
zEllips = fts5ValueToText(apVal[3]);
nToken = sqlite3_value_int(apVal[4]);
@@ -201612,7 +232949,7 @@ static void fts5SnippetFunction(
sFinder.nFirst = 0;
rc = pApi->xColumnText(pFts, i, &sFinder.zDoc, &nDoc);
if( rc!=SQLITE_OK ) break;
- rc = pApi->xTokenize(pFts,
+ rc = pApi->xTokenize(pFts,
sFinder.zDoc, nDoc, (void*)&sFinder,fts5SentenceFinderCb
);
if( rc!=SQLITE_OK ) break;
@@ -201626,7 +232963,9 @@ static void fts5SnippetFunction(
int jj;
rc = pApi->xInst(pFts, ii, &ip, &ic, &io);
- if( ic!=i || rc!=SQLITE_OK ) continue;
+ if( ic!=i ) continue;
+ if( io>nDocsize ) rc = FTS5_CORRUPT;
+ if( rc!=SQLITE_OK ) continue;
memset(aSeen, 0, nPhrase);
rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
io, nToken, &nScore, &iAdj
@@ -201645,7 +232984,7 @@ static void fts5SnippetFunction(
if( sFinder.aFirst[jj]<io ){
memset(aSeen, 0, nPhrase);
- rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
+ rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
sFinder.aFirst[jj], nToken, &nScore, 0
);
@@ -201689,6 +233028,9 @@ static void fts5SnippetFunction(
if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
}
+ if( ctx.bOpen ){
+ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
+ }
if( ctx.iRangeEnd>=(nColSize-1) ){
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
}else{
@@ -201724,7 +233066,7 @@ struct Fts5Bm25Data {
** table matched by each individual phrase within the query.
*/
static int fts5CountCb(
- const Fts5ExtensionApi *pApi,
+ const Fts5ExtensionApi *pApi,
Fts5Context *pFts,
void *pUserData /* Pointer to sqlite3_int64 variable */
){
@@ -201735,34 +233077,34 @@ static int fts5CountCb(
}
/*
-** Set *ppData to point to the Fts5Bm25Data object for the current query.
+** Set *ppData to point to the Fts5Bm25Data object for the current query.
** If the object has not already been allocated, allocate and populate it
** now.
*/
static int fts5Bm25GetData(
- const Fts5ExtensionApi *pApi,
+ const Fts5ExtensionApi *pApi,
Fts5Context *pFts,
Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */
){
int rc = SQLITE_OK; /* Return code */
Fts5Bm25Data *p; /* Object to return */
- p = pApi->xGetAuxdata(pFts, 0);
+ p = (Fts5Bm25Data*)pApi->xGetAuxdata(pFts, 0);
if( p==0 ){
int nPhrase; /* Number of phrases in query */
sqlite3_int64 nRow = 0; /* Number of rows in table */
sqlite3_int64 nToken = 0; /* Number of tokens in table */
- int nByte; /* Bytes of space to allocate */
+ sqlite3_int64 nByte; /* Bytes of space to allocate */
int i;
/* Allocate the Fts5Bm25Data object */
nPhrase = pApi->xPhraseCount(pFts);
nByte = sizeof(Fts5Bm25Data) + nPhrase*2*sizeof(double);
- p = (Fts5Bm25Data*)sqlite3_malloc(nByte);
+ p = (Fts5Bm25Data*)sqlite3_malloc64(nByte);
if( p==0 ){
rc = SQLITE_NOMEM;
}else{
- memset(p, 0, nByte);
+ memset(p, 0, (size_t)nByte);
p->nPhrase = nPhrase;
p->aIDF = (double*)&p[1];
p->aFreq = &p->aIDF[nPhrase];
@@ -201770,6 +233112,7 @@ static int fts5Bm25GetData(
/* Calculate the average document length for this FTS5 table */
if( rc==SQLITE_OK ) rc = pApi->xRowCount(pFts, &nRow);
+ assert( rc!=SQLITE_OK || nRow>0 );
if( rc==SQLITE_OK ) rc = pApi->xColumnTotalSize(pFts, -1, &nToken);
if( rc==SQLITE_OK ) p->avgdl = (double)nToken / (double)nRow;
@@ -201787,7 +233130,7 @@ static int fts5Bm25GetData(
** is the number that contain at least one instance of the phrase
** under consideration.
**
- ** The problem with this is that if (N < 2*nHit), the IDF is
+ ** The problem with this is that if (N < 2*nHit), the IDF is
** negative. Which is undesirable. So the mimimum allowable IDF is
** (1e-6) - roughly the same as a term that appears in just over
** half of set of 5,000,000 documents. */
@@ -201820,7 +233163,7 @@ static void fts5Bm25Function(
){
const double k1 = 1.2; /* Constant "k1" from BM25 formula */
const double b = 0.75; /* Constant "b" from BM25 formula */
- int rc = SQLITE_OK; /* Error code */
+ int rc; /* Error code */
double score = 0.0; /* SQL function return value */
Fts5Bm25Data *pData; /* Values allocated/calculated once only */
int i; /* Iterator variable */
@@ -201852,17 +233195,15 @@ static void fts5Bm25Function(
D = (double)nTok;
}
- /* Determine the BM25 score for the current row. */
- for(i=0; rc==SQLITE_OK && i<pData->nPhrase; i++){
- score += pData->aIDF[i] * (
- ( aFreq[i] * (k1 + 1.0) ) /
- ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
- );
- }
-
- /* If no error has occurred, return the calculated score. Otherwise,
- ** throw an SQL exception. */
+ /* Determine and return the BM25 score for the current row. Or, if an
+ ** error has occurred, throw an exception. */
if( rc==SQLITE_OK ){
+ for(i=0; i<pData->nPhrase; i++){
+ score += pData->aIDF[i] * (
+ ( aFreq[i] * (k1 + 1.0) ) /
+ ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
+ );
+ }
sqlite3_result_double(pCtx, -1.0 * score);
}else{
sqlite3_result_error_code(pCtx, rc);
@@ -201895,8 +233236,6 @@ static int sqlite3Fts5AuxInit(fts5_api *pApi){
return rc;
}
-
-
/*
** 2014 May 31
**
@@ -201916,17 +233255,17 @@ static int sqlite3Fts5AuxInit(fts5_api *pApi){
static int sqlite3Fts5BufferSize(int *pRc, Fts5Buffer *pBuf, u32 nByte){
if( (u32)pBuf->nSpace<nByte ){
- u32 nNew = pBuf->nSpace ? pBuf->nSpace : 64;
+ u64 nNew = pBuf->nSpace ? pBuf->nSpace : 64;
u8 *pNew;
while( nNew<nByte ){
nNew = nNew * 2;
}
- pNew = sqlite3_realloc(pBuf->p, nNew);
+ pNew = sqlite3_realloc64(pBuf->p, nNew);
if( pNew==0 ){
*pRc = SQLITE_NOMEM;
return 1;
}else{
- pBuf->nSpace = nNew;
+ pBuf->nSpace = (int)nNew;
pBuf->p = pNew;
}
}
@@ -201951,23 +233290,23 @@ static void sqlite3Fts5Put32(u8 *aBuf, int iVal){
}
static int sqlite3Fts5Get32(const u8 *aBuf){
- return (aBuf[0] << 24) + (aBuf[1] << 16) + (aBuf[2] << 8) + aBuf[3];
+ return (int)((((u32)aBuf[0])<<24) + (aBuf[1]<<16) + (aBuf[2]<<8) + aBuf[3]);
}
/*
-** Append buffer nData/pData to buffer pBuf. If an OOM error occurs, set
+** Append buffer nData/pData to buffer pBuf. If an OOM error occurs, set
** the error code in p. If an error has already occurred when this function
** is called, it is a no-op.
*/
static void sqlite3Fts5BufferAppendBlob(
int *pRc,
- Fts5Buffer *pBuf,
- u32 nData,
+ Fts5Buffer *pBuf,
+ u32 nData,
const u8 *pData
){
- assert_nc( *pRc || nData>=0 );
if( nData ){
if( fts5BufferGrow(pRc, pBuf, nData) ) return;
+ assert( pBuf->p!=0 );
memcpy(&pBuf->p[pBuf->n], pData, nData);
pBuf->n += nData;
}
@@ -201975,12 +233314,12 @@ static void sqlite3Fts5BufferAppendBlob(
/*
** Append the nul-terminated string zStr to the buffer pBuf. This function
-** ensures that the byte following the buffer data is set to 0x00, even
+** ensures that the byte following the buffer data is set to 0x00, even
** though this byte is not included in the pBuf->n count.
*/
static void sqlite3Fts5BufferAppendString(
int *pRc,
- Fts5Buffer *pBuf,
+ Fts5Buffer *pBuf,
const char *zStr
){
int nStr = (int)strlen(zStr);
@@ -201992,13 +233331,13 @@ static void sqlite3Fts5BufferAppendString(
** Argument zFmt is a printf() style format string. This function performs
** the printf() style processing, then appends the results to buffer pBuf.
**
-** Like sqlite3Fts5BufferAppendString(), this function ensures that the byte
+** Like sqlite3Fts5BufferAppendString(), this function ensures that the byte
** following the buffer data is set to 0x00, even though this byte is not
** included in the pBuf->n count.
-*/
+*/
static void sqlite3Fts5BufferAppendPrintf(
int *pRc,
- Fts5Buffer *pBuf,
+ Fts5Buffer *pBuf,
char *zFmt, ...
){
if( *pRc==SQLITE_OK ){
@@ -202025,12 +233364,12 @@ static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...){
zRet = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
if( zRet==0 ){
- *pRc = SQLITE_NOMEM;
+ *pRc = SQLITE_NOMEM;
}
}
return zRet;
}
-
+
/*
** Free any buffer allocated by pBuf. Zero the structure before returning.
@@ -202041,7 +233380,7 @@ static void sqlite3Fts5BufferFree(Fts5Buffer *pBuf){
}
/*
-** Zero the contents of the buffer object. But do not free the associated
+** Zero the contents of the buffer object. But do not free the associated
** memory allocation.
*/
static void sqlite3Fts5BufferZero(Fts5Buffer *pBuf){
@@ -202055,8 +233394,8 @@ static void sqlite3Fts5BufferZero(Fts5Buffer *pBuf){
*/
static void sqlite3Fts5BufferSet(
int *pRc,
- Fts5Buffer *pBuf,
- int nData,
+ Fts5Buffer *pBuf,
+ int nData,
const u8 *pData
){
pBuf->n = 0;
@@ -202069,21 +233408,36 @@ static int sqlite3Fts5PoslistNext64(
i64 *piOff /* IN/OUT: Current offset */
){
int i = *pi;
+ assert( a!=0 || i==0 );
if( i>=n ){
/* EOF */
*piOff = -1;
- return 1;
+ return 1;
}else{
i64 iOff = *piOff;
- int iVal;
+ u32 iVal;
+ assert( a!=0 );
fts5FastGetVarint32(a, i, iVal);
- if( iVal==1 ){
+ if( iVal<=1 ){
+ if( iVal==0 ){
+ *pi = i;
+ return 0;
+ }
fts5FastGetVarint32(a, i, iVal);
iOff = ((i64)iVal) << 32;
+ assert( iOff>=0 );
fts5FastGetVarint32(a, i, iVal);
+ if( iVal<2 ){
+ /* This is a corrupt record. So stop parsing it here. */
+ *piOff = -1;
+ return 1;
+ }
+ *piOff = iOff + ((iVal-2) & 0x7FFFFFFF);
+ }else{
+ *piOff = (iOff & (i64)0x7FFFFFFF<<32)+((iOff + (iVal-2)) & 0x7FFFFFFF);
}
- *piOff = iOff + (iVal-2);
*pi = i;
+ assert_nc( *piOff>=iOff );
return 0;
}
}
@@ -202118,22 +233472,24 @@ static int sqlite3Fts5PoslistReaderInit(
** to iPos before returning.
*/
static void sqlite3Fts5PoslistSafeAppend(
- Fts5Buffer *pBuf,
- i64 *piPrev,
+ Fts5Buffer *pBuf,
+ i64 *piPrev,
i64 iPos
){
- static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32;
- if( (iPos & colmask) != (*piPrev & colmask) ){
- pBuf->p[pBuf->n++] = 1;
- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32));
- *piPrev = (iPos & colmask);
+ if( iPos>=*piPrev ){
+ static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32;
+ if( (iPos & colmask) != (*piPrev & colmask) ){
+ pBuf->p[pBuf->n++] = 1;
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32));
+ *piPrev = (iPos & colmask);
+ }
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2);
+ *piPrev = iPos;
}
- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2);
- *piPrev = iPos;
}
static int sqlite3Fts5PoslistWriterAppend(
- Fts5Buffer *pBuf,
+ Fts5Buffer *pBuf,
Fts5PoslistWriter *pWriter,
i64 iPos
){
@@ -202143,14 +233499,14 @@ static int sqlite3Fts5PoslistWriterAppend(
return SQLITE_OK;
}
-static void *sqlite3Fts5MallocZero(int *pRc, int nByte){
+static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte){
void *pRet = 0;
if( *pRc==SQLITE_OK ){
- pRet = sqlite3_malloc(nByte);
+ pRet = sqlite3_malloc64(nByte);
if( pRet==0 ){
if( nByte>0 ) *pRc = SQLITE_NOMEM;
}else{
- memset(pRet, 0, nByte);
+ memset(pRet, 0, (size_t)nByte);
}
}
return pRet;
@@ -202162,7 +233518,7 @@ static void *sqlite3Fts5MallocZero(int *pRc, int nByte){
** the length of the string is determined using strlen().
**
** It is the responsibility of the caller to eventually free the returned
-** buffer using sqlite3_free(). If an OOM error occurs, NULL is returned.
+** buffer using sqlite3_free(). If an OOM error occurs, NULL is returned.
*/
static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){
char *zRet = 0;
@@ -202229,9 +233585,9 @@ static int sqlite3Fts5TermsetNew(Fts5Termset **pp){
}
static int sqlite3Fts5TermsetAdd(
- Fts5Termset *p,
+ Fts5Termset *p,
int iIdx,
- const char *pTerm, int nTerm,
+ const char *pTerm, int nTerm,
int *pbPresent
){
int rc = SQLITE_OK;
@@ -202252,9 +233608,9 @@ static int sqlite3Fts5TermsetAdd(
hash = hash % ArraySize(p->apHash);
for(pEntry=p->apHash[hash]; pEntry; pEntry=pEntry->pNext){
- if( pEntry->iIdx==iIdx
- && pEntry->nTerm==nTerm
- && memcmp(pEntry->pTerm, pTerm, nTerm)==0
+ if( pEntry->iIdx==iIdx
+ && pEntry->nTerm==nTerm
+ && memcmp(pEntry->pTerm, pTerm, nTerm)==0
){
*pbPresent = 1;
break;
@@ -202316,8 +233672,10 @@ static void sqlite3Fts5TermsetFree(Fts5Termset *p){
#define FTS5_DEFAULT_CRISISMERGE 16
#define FTS5_DEFAULT_HASHSIZE (1024*1024)
+#define FTS5_DEFAULT_DELETE_AUTOMERGE 10 /* default 10% */
+
/* Maximum allowed page size */
-#define FTS5_MAX_PAGE_SIZE (128*1024)
+#define FTS5_MAX_PAGE_SIZE (64*1024)
static int fts5_iswhitespace(char x){
return (x==' ');
@@ -202328,8 +233686,8 @@ static int fts5_isopenquote(char x){
}
/*
-** Argument pIn points to a character that is part of a nul-terminated
-** string. Return a pointer to the first character following *pIn in
+** Argument pIn points to a character that is part of a nul-terminated
+** string. Return a pointer to the first character following *pIn in
** the string that is not a white-space character.
*/
static const char *fts5ConfigSkipWhitespace(const char *pIn){
@@ -202341,8 +233699,8 @@ static const char *fts5ConfigSkipWhitespace(const char *pIn){
}
/*
-** Argument pIn points to a character that is part of a nul-terminated
-** string. Return a pointer to the first character following *pIn in
+** Argument pIn points to a character that is part of a nul-terminated
+** string. Return a pointer to the first character following *pIn in
** the string that is not a "bareword" character.
*/
static const char *fts5ConfigSkipBareword(const char *pIn){
@@ -202373,9 +233731,9 @@ static const char *fts5ConfigSkipLiteral(const char *pIn){
p++;
if( *p=='\'' ){
p++;
- while( (*p>='a' && *p<='f')
- || (*p>='A' && *p<='F')
- || (*p>='0' && *p<='9')
+ while( (*p>='a' && *p<='f')
+ || (*p>='A' && *p<='F')
+ || (*p>='0' && *p<='9')
){
p++;
}
@@ -202406,7 +233764,7 @@ static const char *fts5ConfigSkipLiteral(const char *pIn){
if( *p=='+' || *p=='-' ) p++;
while( fts5_isdigit(*p) ) p++;
- /* At this point, if the literal was an integer, the parse is
+ /* At this point, if the literal was an integer, the parse is
** finished. Or, if it is a floating point value, it may continue
** with either a decimal point or an 'E' character. */
if( *p=='.' && fts5_isdigit(p[1]) ){
@@ -202430,8 +233788,8 @@ static const char *fts5ConfigSkipLiteral(const char *pIn){
** nul-terminator byte.
**
** If the close-quote is found, the value returned is the byte offset of
-** the character immediately following it. Or, if the close-quote is not
-** found, -1 is returned. If -1 is returned, the buffer is left in an
+** the character immediately following it. Or, if the close-quote is not
+** found, -1 is returned. If -1 is returned, the buffer is left in an
** undefined state.
*/
static int fts5Dequote(char *z){
@@ -202442,9 +233800,9 @@ static int fts5Dequote(char *z){
/* Set stack variable q to the close-quote character */
assert( q=='[' || q=='\'' || q=='"' || q=='`' );
- if( q=='[' ) q = ']';
+ if( q=='[' ) q = ']';
- while( ALWAYS(z[iIn]) ){
+ while( z[iIn] ){
if( z[iIn]==q ){
if( z[iIn+1]!=q ){
/* Character iIn was the close quote. */
@@ -202452,7 +233810,7 @@ static int fts5Dequote(char *z){
break;
}else{
/* Character iIn and iIn+1 form an escaped quote character. Skip
- ** the input cursor past both and copy a single quote character
+ ** the input cursor past both and copy a single quote character
** to the output buffer. */
iIn += 2;
z[iOut++] = q;
@@ -202497,8 +233855,8 @@ struct Fts5Enum {
typedef struct Fts5Enum Fts5Enum;
static int fts5ConfigSetEnum(
- const Fts5Enum *aEnum,
- const char *zEnum,
+ const Fts5Enum *aEnum,
+ const char *zEnum,
int *peVal
){
int nEnum = (int)strlen(zEnum);
@@ -202589,7 +233947,7 @@ static int fts5ConfigParseSpecial(
if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){
const char *p = (const char*)zArg;
- int nArg = (int)strlen(zArg) + 1;
+ sqlite3_int64 nArg = strlen(zArg) + 1;
char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg);
char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2);
char *pSpace = pDel;
@@ -202618,8 +233976,8 @@ static int fts5ConfigParseSpecial(
*pzErr = sqlite3_mprintf("parse error in tokenize directive");
rc = SQLITE_ERROR;
}else{
- rc = sqlite3Fts5GetTokenizer(pGlobal,
- (const char**)azArg, nArg, &pConfig->pTok, &pConfig->pTokApi,
+ rc = sqlite3Fts5GetTokenizer(pGlobal,
+ (const char**)azArg, (int)nArg, pConfig,
pzErr
);
}
@@ -202646,6 +234004,16 @@ static int fts5ConfigParseSpecial(
return rc;
}
+ if( sqlite3_strnicmp("contentless_delete", zCmd, nCmd)==0 ){
+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
+ *pzErr = sqlite3_mprintf("malformed contentless_delete=... directive");
+ rc = SQLITE_ERROR;
+ }else{
+ pConfig->bContentlessDelete = (zArg[0]=='1');
+ }
+ return rc;
+ }
+
if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
if( pConfig->zContentRowid ){
*pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
@@ -202680,20 +234048,28 @@ static int fts5ConfigParseSpecial(
return rc;
}
+ if( sqlite3_strnicmp("tokendata", zCmd, nCmd)==0 ){
+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
+ *pzErr = sqlite3_mprintf("malformed tokendata=... directive");
+ rc = SQLITE_ERROR;
+ }else{
+ pConfig->bTokendata = (zArg[0]=='1');
+ }
+ return rc;
+ }
+
*pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
return SQLITE_ERROR;
}
/*
-** Allocate an instance of the default tokenizer ("simple") at
+** Allocate an instance of the default tokenizer ("simple") at
** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error
** code if an error occurs.
*/
static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){
assert( pConfig->pTok==0 && pConfig->pTokApi==0 );
- return sqlite3Fts5GetTokenizer(
- pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0
- );
+ return sqlite3Fts5GetTokenizer(pGlobal, 0, 0, pConfig, 0);
}
/*
@@ -202719,8 +234095,8 @@ static const char *fts5ConfigGobbleWord(
){
const char *zRet = 0;
- int nIn = (int)strlen(zIn);
- char *zOut = sqlite3_malloc(nIn+1);
+ sqlite3_int64 nIn = strlen(zIn);
+ char *zOut = sqlite3_malloc64(nIn+1);
assert( *pRc==SQLITE_OK );
*pbQuoted = 0;
@@ -202729,7 +234105,7 @@ static const char *fts5ConfigGobbleWord(
if( zOut==0 ){
*pRc = SQLITE_NOMEM;
}else{
- memcpy(zOut, zIn, nIn+1);
+ memcpy(zOut, zIn, (size_t)(nIn+1));
if( fts5_isopenquote(zOut[0]) ){
int ii = fts5Dequote(zOut);
zRet = &zIn[ii];
@@ -202752,14 +234128,14 @@ static const char *fts5ConfigGobbleWord(
}
static int fts5ConfigParseColumn(
- Fts5Config *p,
- char *zCol,
- char *zArg,
+ Fts5Config *p,
+ char *zCol,
+ char *zArg,
char **pzErr
){
int rc = SQLITE_OK;
- if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME)
- || 0==sqlite3_stricmp(zCol, FTS5_ROWID_NAME)
+ if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME)
+ || 0==sqlite3_stricmp(zCol, FTS5_ROWID_NAME)
){
*pzErr = sqlite3_mprintf("reserved fts5 column name: %s", zCol);
rc = SQLITE_ERROR;
@@ -202802,14 +234178,14 @@ static int fts5ConfigMakeExprlist(Fts5Config *p){
/*
** Arguments nArg/azArg contain the string arguments passed to the xCreate
-** or xConnect method of the virtual table. This function attempts to
+** or xConnect method of the virtual table. This function attempts to
** allocate an instance of Fts5Config containing the results of parsing
** those arguments.
**
** If successful, SQLITE_OK is returned and *ppOut is set to point to the
-** new Fts5Config object. If an error occurs, an SQLite error code is
+** new Fts5Config object. If an error occurs, an SQLite error code is
** returned, *ppOut is set to NULL and an error message may be left in
-** *pzErr. It is the responsibility of the caller to eventually free any
+** *pzErr. It is the responsibility of the caller to eventually free any
** such error message using sqlite3_free().
*/
static int sqlite3Fts5ConfigParse(
@@ -202823,7 +234199,7 @@ static int sqlite3Fts5ConfigParse(
int rc = SQLITE_OK; /* Return code */
Fts5Config *pRet; /* New object to return */
int i;
- int nByte;
+ sqlite3_int64 nByte;
*ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
if( pRet==0 ) return SQLITE_NOMEM;
@@ -202833,7 +234209,7 @@ static int sqlite3Fts5ConfigParse(
nByte = nArg * (sizeof(char*) + sizeof(u8));
pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte);
- pRet->abUnindexed = (u8*)&pRet->azCol[nArg];
+ pRet->abUnindexed = pRet->azCol ? (u8*)&pRet->azCol[nArg] : 0;
pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1);
pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1);
pRet->bColumnsize = 1;
@@ -202846,6 +234222,7 @@ static int sqlite3Fts5ConfigParse(
rc = SQLITE_ERROR;
}
+ assert( (pRet->abUnindexed && pRet->azCol) || rc!=SQLITE_OK );
for(i=3; rc==SQLITE_OK && i<nArg; i++){
const char *zOrig = azArg[i];
const char *z;
@@ -202858,6 +234235,7 @@ static int sqlite3Fts5ConfigParse(
z = fts5ConfigSkipWhitespace(z);
if( z && *z=='=' ){
bOption = 1;
+ assert( zOne!=0 );
z++;
if( bMustBeCol ) z = 0;
}
@@ -202874,7 +234252,11 @@ static int sqlite3Fts5ConfigParse(
rc = SQLITE_ERROR;
}else{
if( bOption ){
- rc = fts5ConfigParseSpecial(pGlobal, pRet, zOne, zTwo?zTwo:"", pzErr);
+ rc = fts5ConfigParseSpecial(pGlobal, pRet,
+ ALWAYS(zOne)?zOne:"",
+ zTwo?zTwo:"",
+ pzErr
+ );
}else{
rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr);
zOne = 0;
@@ -202886,6 +234268,28 @@ static int sqlite3Fts5ConfigParse(
sqlite3_free(zTwo);
}
+ /* We only allow contentless_delete=1 if the table is indeed contentless. */
+ if( rc==SQLITE_OK
+ && pRet->bContentlessDelete
+ && pRet->eContent!=FTS5_CONTENT_NONE
+ ){
+ *pzErr = sqlite3_mprintf(
+ "contentless_delete=1 requires a contentless table"
+ );
+ rc = SQLITE_ERROR;
+ }
+
+ /* We only allow contentless_delete=1 if columnsize=0 is not present.
+ **
+ ** This restriction may be removed at some point.
+ */
+ if( rc==SQLITE_OK && pRet->bContentlessDelete && pRet->bColumnsize==0 ){
+ *pzErr = sqlite3_mprintf(
+ "contentless_delete=1 is incompatible with columnsize=0"
+ );
+ rc = SQLITE_ERROR;
+ }
+
/* If a tokenizer= option was successfully parsed, the tokenizer has
** already been allocated. Otherwise, allocate an instance of the default
** tokenizer (unicode61) now. */
@@ -202896,8 +234300,8 @@ static int sqlite3Fts5ConfigParse(
/* If no zContent option was specified, fill in the default values. */
if( rc==SQLITE_OK && pRet->zContent==0 ){
const char *zTail = 0;
- assert( pRet->eContent==FTS5_CONTENT_NORMAL
- || pRet->eContent==FTS5_CONTENT_NONE
+ assert( pRet->eContent==FTS5_CONTENT_NORMAL
+ || pRet->eContent==FTS5_CONTENT_NONE
);
if( pRet->eContent==FTS5_CONTENT_NORMAL ){
zTail = "content";
@@ -202968,7 +234372,7 @@ static int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
const char *zSep = (i==0?"":", ");
zSql = sqlite3Fts5Mprintf(&rc, "%z%s%Q", zSql, zSep, pConfig->azCol[i]);
}
- zSql = sqlite3Fts5Mprintf(&rc, "%z, %Q HIDDEN, %s HIDDEN)",
+ zSql = sqlite3Fts5Mprintf(&rc, "%z, %Q HIDDEN, %s HIDDEN)",
zSql, pConfig->zName, FTS5_RANK_NAME
);
@@ -202977,7 +234381,7 @@ static int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
rc = sqlite3_declare_vtab(pConfig->db, zSql);
sqlite3_free(zSql);
}
-
+
return rc;
}
@@ -202995,7 +234399,7 @@ static int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
** int iPos // Position of token in input (first token is 0)
**
** If the callback returns a non-zero value the tokenization is abandoned
-** and no further callbacks are issued.
+** and no further callbacks are issued.
**
** This function returns SQLITE_OK if successful or an SQLite error code
** if an error occurs. If the tokenization was abandoned early because
@@ -203025,7 +234429,7 @@ static int sqlite3Fts5Tokenize(
*/
static const char *fts5ConfigSkipArgs(const char *pIn){
const char *p = pIn;
-
+
while( 1 ){
p = fts5ConfigSkipWhitespace(p);
p = fts5ConfigSkipLiteral(p);
@@ -203042,7 +234446,7 @@ static const char *fts5ConfigSkipArgs(const char *pIn){
}
/*
-** Parameter zIn contains a rank() function specification. The format of
+** Parameter zIn contains a rank() function specification. The format of
** this is:
**
** + Bareword (function name)
@@ -203084,7 +234488,7 @@ static int sqlite3Fts5ConfigParseRank(
p++;
}
if( rc==SQLITE_OK ){
- const char *pArgs;
+ const char *pArgs;
p = fts5ConfigSkipWhitespace(p);
pArgs = p;
if( *p!=')' ){
@@ -203110,8 +234514,8 @@ static int sqlite3Fts5ConfigParseRank(
}
static int sqlite3Fts5ConfigSetValue(
- Fts5Config *pConfig,
- const char *zKey,
+ Fts5Config *pConfig,
+ const char *zKey,
sqlite3_value *pVal,
int *pbBadkey
){
@@ -203122,7 +234526,7 @@ static int sqlite3Fts5ConfigSetValue(
if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
pgsz = sqlite3_value_int(pVal);
}
- if( pgsz<=0 || pgsz>FTS5_MAX_PAGE_SIZE ){
+ if( pgsz<32 || pgsz>FTS5_MAX_PAGE_SIZE ){
*pbBadkey = 1;
}else{
pConfig->pgsz = pgsz;
@@ -203175,10 +234579,23 @@ static int sqlite3Fts5ConfigSetValue(
*pbBadkey = 1;
}else{
if( nCrisisMerge<=1 ) nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
+ if( nCrisisMerge>=FTS5_MAX_SEGMENT ) nCrisisMerge = FTS5_MAX_SEGMENT-1;
pConfig->nCrisisMerge = nCrisisMerge;
}
}
+ else if( 0==sqlite3_stricmp(zKey, "deletemerge") ){
+ int nVal = -1;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ nVal = sqlite3_value_int(pVal);
+ }else{
+ *pbBadkey = 1;
+ }
+ if( nVal<0 ) nVal = FTS5_DEFAULT_DELETE_AUTOMERGE;
+ if( nVal>100 ) nVal = 0;
+ pConfig->nDeleteMerge = nVal;
+ }
+
else if( 0==sqlite3_stricmp(zKey, "rank") ){
const char *zIn = (const char*)sqlite3_value_text(pVal);
char *zRank;
@@ -203193,6 +234610,18 @@ static int sqlite3Fts5ConfigSetValue(
rc = SQLITE_OK;
*pbBadkey = 1;
}
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "secure-delete") ){
+ int bVal = -1;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ bVal = sqlite3_value_int(pVal);
+ }
+ if( bVal<0 ){
+ *pbBadkey = 1;
+ }else{
+ pConfig->bSecureDelete = (bVal ? 1 : 0);
+ }
}else{
*pbBadkey = 1;
}
@@ -203215,6 +234644,7 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE;
pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE;
+ pConfig->nDeleteMerge = FTS5_DEFAULT_DELETE_AUTOMERGE;
zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName);
if( zSql ){
@@ -203236,16 +234666,21 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
}
rc = sqlite3_finalize(p);
}
-
- if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION ){
+
+ if( rc==SQLITE_OK
+ && iVersion!=FTS5_CURRENT_VERSION
+ && iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE
+ ){
rc = SQLITE_ERROR;
if( pConfig->pzErrmsg ){
assert( 0==*pConfig->pzErrmsg );
- *pConfig->pzErrmsg = sqlite3_mprintf(
- "invalid fts5 file format (found %d, expected %d) - run 'rebuild'",
- iVersion, FTS5_CURRENT_VERSION
+ *pConfig->pzErrmsg = sqlite3_mprintf("invalid fts5 file format "
+ "(found %d, expected %d or %d) - run 'rebuild'",
+ iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE
);
}
+ }else{
+ pConfig->iVersion = iVersion;
}
if( rc==SQLITE_OK ){
@@ -203273,6 +234708,10 @@ static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
/* #include "fts5Int.h" */
/* #include "fts5parse.h" */
+#ifndef SQLITE_FTS5_MAX_EXPR_DEPTH
+# define SQLITE_FTS5_MAX_EXPR_DEPTH 256
+#endif
+
/*
** All token types in the generated fts5parse.h file are greater than 0.
*/
@@ -203313,11 +234752,17 @@ struct Fts5Expr {
** FTS5_NOT (nChild, apChild valid)
** FTS5_STRING (pNear valid)
** FTS5_TERM (pNear valid)
+**
+** iHeight:
+** Distance from this node to furthest leaf. This is always 0 for nodes
+** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one
+** greater than the largest child value.
*/
struct Fts5ExprNode {
int eType; /* Node type */
int bEof; /* True at EOF */
int bNomatch; /* True if entry is not a match */
+ int iHeight; /* Distance to tree leaf nodes */
/* Next method for this node. */
int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64);
@@ -203325,7 +234770,7 @@ struct Fts5ExprNode {
i64 iRowid; /* Current rowid */
Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */
- /* Child nodes. For a NOT node, this array always contains 2 entries. For
+ /* Child nodes. For a NOT node, this array always contains 2 entries. For
** AND or OR nodes, it contains 2 or more entries. */
int nChild; /* Number of child nodes */
Fts5ExprNode *apChild[1]; /* Array of child nodes */
@@ -203346,7 +234791,9 @@ struct Fts5ExprNode {
struct Fts5ExprTerm {
u8 bPrefix; /* True for a prefix term */
u8 bFirst; /* True if token must be first in column */
- char *zTerm; /* nul-terminated term */
+ char *pTerm; /* Term data */
+ int nQueryTerm; /* Effective size of term in bytes */
+ int nFullTerm; /* Size of term in bytes incl. tokendata */
Fts5IndexIter *pIter; /* Iterator for this term */
Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */
};
@@ -203384,12 +234831,39 @@ struct Fts5Parse {
int nPhrase; /* Size of apPhrase array */
Fts5ExprPhrase **apPhrase; /* Array of all phrases */
Fts5ExprNode *pExpr; /* Result of a successful parse */
+ int bPhraseToAnd; /* Convert "a+b" to "a AND b" */
};
+/*
+** Check that the Fts5ExprNode.iHeight variables are set correctly in
+** the expression tree passed as the only argument.
+*/
+#ifndef NDEBUG
+static void assert_expr_depth_ok(int rc, Fts5ExprNode *p){
+ if( rc==SQLITE_OK ){
+ if( p->eType==FTS5_TERM || p->eType==FTS5_STRING || p->eType==0 ){
+ assert( p->iHeight==0 );
+ }else{
+ int ii;
+ int iMaxChild = 0;
+ for(ii=0; ii<p->nChild; ii++){
+ Fts5ExprNode *pChild = p->apChild[ii];
+ iMaxChild = MAX(iMaxChild, pChild->iHeight);
+ assert_expr_depth_ok(SQLITE_OK, pChild);
+ }
+ assert( p->iHeight==iMaxChild+1 );
+ }
+ }
+}
+#else
+# define assert_expr_depth_ok(rc, p)
+#endif
+
static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){
va_list ap;
va_start(ap, zFmt);
if( pParse->rc==SQLITE_OK ){
+ assert( pParse->zErr==0 );
pParse->zErr = sqlite3_vmprintf(zFmt, ap);
pParse->rc = SQLITE_ERROR;
}
@@ -203404,7 +234878,7 @@ static int fts5ExprIsspace(char t){
** Read the first token from the nul-terminated string at *pz.
*/
static int fts5ExprGetToken(
- Fts5Parse *pParse,
+ Fts5Parse *pParse,
const char **pz, /* IN/OUT: Pointer into buffer */
Fts5Token *pToken
){
@@ -203467,14 +234941,15 @@ static int fts5ExprGetToken(
return tok;
}
-static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc((int)t); }
+static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc64((sqlite3_int64)t);}
static void fts5ParseFree(void *p){ sqlite3_free(p); }
static int sqlite3Fts5ExprNew(
Fts5Config *pConfig, /* FTS5 Configuration */
+ int bPhraseToAnd,
int iCol,
const char *zExpr, /* Expression text */
- Fts5Expr **ppNew,
+ Fts5Expr **ppNew,
char **pzErr
){
Fts5Parse sParse;
@@ -203487,6 +234962,7 @@ static int sqlite3Fts5ExprNew(
*ppNew = 0;
*pzErr = 0;
memset(&sParse, 0, sizeof(sParse));
+ sParse.bPhraseToAnd = bPhraseToAnd;
pEngine = sqlite3Fts5ParserAlloc(fts5ParseAlloc);
if( pEngine==0 ){ return SQLITE_NOMEM; }
sParse.pConfig = pConfig;
@@ -203497,6 +234973,8 @@ static int sqlite3Fts5ExprNew(
}while( sParse.rc==SQLITE_OK && t!=FTS5_EOF );
sqlite3Fts5ParserFree(pEngine, fts5ParseFree);
+ assert_expr_depth_ok(sParse.rc, sParse.pExpr);
+
/* If the LHS of the MATCH expression was a user column, apply the
** implicit column-filter. */
if( iCol<pConfig->nCol && sParse.pExpr && sParse.rc==SQLITE_OK ){
@@ -203529,6 +235007,7 @@ static int sqlite3Fts5ExprNew(
pNew->pConfig = pConfig;
pNew->apExprPhrase = sParse.apPhrase;
pNew->nPhrase = sParse.nPhrase;
+ pNew->bDesc = 0;
sParse.apPhrase = 0;
}
}else{
@@ -203541,6 +235020,95 @@ static int sqlite3Fts5ExprNew(
}
/*
+** Assuming that buffer z is at least nByte bytes in size and contains a
+** valid utf-8 string, return the number of characters in the string.
+*/
+static int fts5ExprCountChar(const char *z, int nByte){
+ int nRet = 0;
+ int ii;
+ for(ii=0; ii<nByte; ii++){
+ if( (z[ii] & 0xC0)!=0x80 ) nRet++;
+ }
+ return nRet;
+}
+
+/*
+** This function is only called when using the special 'trigram' tokenizer.
+** Argument zText contains the text of a LIKE or GLOB pattern matched
+** against column iCol. This function creates and compiles an FTS5 MATCH
+** expression that will match a superset of the rows matched by the LIKE or
+** GLOB. If successful, SQLITE_OK is returned. Otherwise, an SQLite error
+** code.
+*/
+static int sqlite3Fts5ExprPattern(
+ Fts5Config *pConfig, int bGlob, int iCol, const char *zText, Fts5Expr **pp
+){
+ i64 nText = strlen(zText);
+ char *zExpr = (char*)sqlite3_malloc64(nText*4 + 1);
+ int rc = SQLITE_OK;
+
+ if( zExpr==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ char aSpec[3];
+ int iOut = 0;
+ int i = 0;
+ int iFirst = 0;
+
+ if( bGlob==0 ){
+ aSpec[0] = '_';
+ aSpec[1] = '%';
+ aSpec[2] = 0;
+ }else{
+ aSpec[0] = '*';
+ aSpec[1] = '?';
+ aSpec[2] = '[';
+ }
+
+ while( i<=nText ){
+ if( i==nText
+ || zText[i]==aSpec[0] || zText[i]==aSpec[1] || zText[i]==aSpec[2]
+ ){
+
+ if( fts5ExprCountChar(&zText[iFirst], i-iFirst)>=3 ){
+ int jj;
+ zExpr[iOut++] = '"';
+ for(jj=iFirst; jj<i; jj++){
+ zExpr[iOut++] = zText[jj];
+ if( zText[jj]=='"' ) zExpr[iOut++] = '"';
+ }
+ zExpr[iOut++] = '"';
+ zExpr[iOut++] = ' ';
+ }
+ if( zText[i]==aSpec[2] ){
+ i += 2;
+ if( zText[i-1]=='^' ) i++;
+ while( i<nText && zText[i]!=']' ) i++;
+ }
+ iFirst = i+1;
+ }
+ i++;
+ }
+ if( iOut>0 ){
+ int bAnd = 0;
+ if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
+ bAnd = 1;
+ if( pConfig->eDetail==FTS5_DETAIL_NONE ){
+ iCol = pConfig->nCol;
+ }
+ }
+ zExpr[iOut] = '\0';
+ rc = sqlite3Fts5ExprNew(pConfig, bAnd, iCol, zExpr, pp,pConfig->pzErrmsg);
+ }else{
+ *pp = 0;
+ }
+ sqlite3_free(zExpr);
+ }
+
+ return rc;
+}
+
+/*
** Free the expression node object passed as the only argument.
*/
static void sqlite3Fts5ParseNodeFree(Fts5ExprNode *p){
@@ -203565,6 +235133,42 @@ static void sqlite3Fts5ExprFree(Fts5Expr *p){
}
}
+static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
+ Fts5Parse sParse;
+ memset(&sParse, 0, sizeof(sParse));
+
+ if( *pp1 && p2 ){
+ Fts5Expr *p1 = *pp1;
+ int nPhrase = p1->nPhrase + p2->nPhrase;
+
+ p1->pRoot = sqlite3Fts5ParseNode(&sParse, FTS5_AND, p1->pRoot, p2->pRoot,0);
+ p2->pRoot = 0;
+
+ if( sParse.rc==SQLITE_OK ){
+ Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc(
+ p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*)
+ );
+ if( ap==0 ){
+ sParse.rc = SQLITE_NOMEM;
+ }else{
+ int i;
+ memmove(&ap[p2->nPhrase], ap, p1->nPhrase*sizeof(Fts5ExprPhrase*));
+ for(i=0; i<p2->nPhrase; i++){
+ ap[i] = p2->apExprPhrase[i];
+ }
+ p1->nPhrase = nPhrase;
+ p1->apExprPhrase = ap;
+ }
+ }
+ sqlite3_free(p2->apExprPhrase);
+ sqlite3_free(p2);
+ }else if( p2 ){
+ *pp1 = p2;
+ }
+
+ return sParse.rc;
+}
+
/*
** Argument pTerm must be a synonym iterator. Return the current rowid
** that it points to.
@@ -203574,6 +235178,7 @@ static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){
int bRetValid = 0;
Fts5ExprTerm *p;
+ assert( pTerm );
assert( pTerm->pSynonym );
assert( bDesc==0 || bDesc==1 );
for(p=pTerm; p; p=p->pSynonym){
@@ -203594,7 +235199,7 @@ static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){
** Argument pTerm must be a synonym iterator.
*/
static int fts5ExprSynonymList(
- Fts5ExprTerm *pTerm,
+ Fts5ExprTerm *pTerm,
i64 iRowid,
Fts5Buffer *pBuf, /* Use this buffer for space if required */
u8 **pa, int *pn
@@ -203612,8 +235217,8 @@ static int fts5ExprSynonymList(
if( sqlite3Fts5IterEof(pIter)==0 && pIter->iRowid==iRowid ){
if( pIter->nData==0 ) continue;
if( nIter==nAlloc ){
- int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2;
- Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte);
+ sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * nAlloc * 2;
+ Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc64(nByte);
if( aNew==0 ){
rc = SQLITE_NOMEM;
goto synonym_poslist_out;
@@ -203667,13 +235272,13 @@ static int fts5ExprSynonymList(
/*
** All individual term iterators in pPhrase are guaranteed to be valid and
-** pointing to the same rowid when this function is called. This function
+** pointing to the same rowid when this function is called. This function
** checks if the current rowid really is a match, and if so populates
** the pPhrase->poslist buffer accordingly. Output parameter *pbMatch
** is set to true if this is really a match, or false otherwise.
**
-** SQLITE_OK is returned if an error occurs, or an SQLite error code
-** otherwise. It is not considered an error code if the current rowid is
+** SQLITE_OK is returned if an error occurs, or an SQLite error code
+** otherwise. It is not considered an error code if the current rowid is
** not a match.
*/
static int fts5ExprPhraseIsMatch(
@@ -203687,14 +235292,14 @@ static int fts5ExprPhraseIsMatch(
int i;
int rc = SQLITE_OK;
int bFirst = pPhrase->aTerm[0].bFirst;
-
+
fts5BufferZero(&pPhrase->poslist);
/* If the aStatic[] array is not large enough, allocate a large array
** using sqlite3_malloc(). This approach could be improved upon. */
if( pPhrase->nTerm>ArraySize(aStatic) ){
- int nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm;
- aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte);
+ sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm;
+ aIter = (Fts5PoslistReader*)sqlite3_malloc64(nByte);
if( !aIter ) return SQLITE_NOMEM;
}
memset(aIter, 0, sizeof(Fts5PoslistReader) * pPhrase->nTerm);
@@ -203809,7 +235414,7 @@ struct Fts5NearTrimmer {
** function is called, it is a no-op. Or, if an error (e.g. SQLITE_NOMEM)
** occurs within this function (*pRc) is set accordingly before returning.
** The return value is undefined in both these cases.
-**
+**
** If no error occurs and non-zero (a match) is returned, the position-list
** of each phrase object is edited to contain only those entries that
** meet the constraint before returning.
@@ -203828,7 +235433,7 @@ static int fts5ExprNearIsMatch(int *pRc, Fts5ExprNearset *pNear){
/* If the aStatic[] array is not large enough, allocate a large array
** using sqlite3_malloc(). This approach could be improved upon. */
if( pNear->nPhrase>ArraySize(aStatic) ){
- int nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase;
+ sqlite3_int64 nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase;
a = (Fts5NearTrimmer*)sqlite3Fts5MallocZero(&rc, nByte);
}else{
memset(aStatic, 0, sizeof(aStatic));
@@ -203841,7 +235446,7 @@ static int fts5ExprNearIsMatch(int *pRc, Fts5ExprNearset *pNear){
/* Initialize a lookahead iterator for each phrase. After passing the
** buffer and buffer size to the lookaside-reader init function, zero
** the phrase poslist buffer. The new poslist for the phrase (containing
- ** the same entries as the original with some entries removed on account
+ ** the same entries as the original with some entries removed on account
** of the NEAR constraint) is written over the original even as it is
** being read. This is safe as the entries for the new poslist are a
** subset of the old, so it is not possible for data yet to be read to
@@ -203998,7 +235603,7 @@ static int fts5ExprNearTest(
** phrase is not a match, break out of the loop early. */
for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
- if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym
+ if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym
|| pNear->pColset || pPhrase->aTerm[0].bFirst
){
int bMatch = 0;
@@ -204055,7 +235660,7 @@ static int fts5ExprNearInitAll(
p->pIter = 0;
}
rc = sqlite3Fts5IndexQuery(
- pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm),
+ pExpr->pIndex, p->pTerm, p->nQueryTerm,
(pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
(pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
pNear->pColset,
@@ -204146,7 +235751,7 @@ static void fts5ExprNodeZeroPoslist(Fts5ExprNode *pNode){
*/
static int fts5NodeCompare(
Fts5Expr *pExpr,
- Fts5ExprNode *p1,
+ Fts5ExprNode *p1,
Fts5ExprNode *p2
){
if( p2->bEof ) return -1;
@@ -204161,7 +235766,7 @@ static int fts5NodeCompare(
** If an EOF is reached before this happens, *pbEof is set to true before
** returning.
**
-** SQLITE_OK is returned if an error occurs, or an SQLite error code
+** SQLITE_OK is returned if an error occurs, or an SQLite error code
** otherwise. It is not considered an error code if an iterator reaches
** EOF.
*/
@@ -204178,8 +235783,8 @@ static int fts5ExprNodeTest_STRING(
const int bDesc = pExpr->bDesc;
/* Check that this node should not be FTS5_TERM */
- assert( pNear->nPhrase>1
- || pNear->apPhrase[0]->nTerm>1
+ assert( pNear->nPhrase>1
+ || pNear->apPhrase[0]->nTerm>1
|| pNear->apPhrase[0]->aTerm[0].pSynonym
|| pNear->apPhrase[0]->aTerm[0].bFirst
);
@@ -204239,7 +235844,7 @@ static int fts5ExprNodeNext_STRING(
Fts5Expr *pExpr, /* Expression pPhrase belongs to */
Fts5ExprNode *pNode, /* FTS5_STRING or FTS5_TERM node */
int bFromValid,
- i64 iFrom
+ i64 iFrom
){
Fts5ExprTerm *pTerm = &pNode->pNear->apPhrase[0]->aTerm[0];
int rc = SQLITE_OK;
@@ -204257,8 +235862,8 @@ static int fts5ExprNodeNext_STRING(
for(p=pTerm; p; p=p->pSynonym){
if( sqlite3Fts5IterEof(p->pIter)==0 ){
i64 ii = p->pIter->iRowid;
- if( ii==iRowid
- || (bFromValid && ii!=iFrom && (ii>iFrom)==pExpr->bDesc)
+ if( ii==iRowid
+ || (bFromValid && ii!=iFrom && (ii>iFrom)==pExpr->bDesc)
){
if( bFromValid ){
rc = sqlite3Fts5IterNextFrom(p->pIter, iFrom);
@@ -204304,9 +235909,9 @@ static int fts5ExprNodeTest_TERM(
Fts5Expr *pExpr, /* Expression that pNear is a part of */
Fts5ExprNode *pNode /* The "NEAR" node (FTS5_TERM) */
){
- /* As this "NEAR" object is actually a single phrase that consists
+ /* As this "NEAR" object is actually a single phrase that consists
** of a single term only, grab pointers into the poslist managed by the
- ** fts5_index.c iterator object. This is much faster than synthesizing
+ ** fts5_index.c iterator object. This is much faster than synthesizing
** a new poslist the way we have to for more complicated phrase or NEAR
** expressions. */
Fts5ExprPhrase *pPhrase = pNode->pNear->apPhrase[0];
@@ -204329,7 +235934,7 @@ static int fts5ExprNodeTest_TERM(
** xNext() method for a node of type FTS5_TERM.
*/
static int fts5ExprNodeNext_TERM(
- Fts5Expr *pExpr,
+ Fts5Expr *pExpr,
Fts5ExprNode *pNode,
int bFromValid,
i64 iFrom
@@ -204372,7 +235977,7 @@ static void fts5ExprNodeTest_OR(
}
static int fts5ExprNodeNext_OR(
- Fts5Expr *pExpr,
+ Fts5Expr *pExpr,
Fts5ExprNode *pNode,
int bFromValid,
i64 iFrom
@@ -204384,7 +235989,7 @@ static int fts5ExprNodeNext_OR(
Fts5ExprNode *p1 = pNode->apChild[i];
assert( p1->bEof || fts5RowidCmp(pExpr, p1->iRowid, iLast)>=0 );
if( p1->bEof==0 ){
- if( (p1->iRowid==iLast)
+ if( (p1->iRowid==iLast)
|| (bFromValid && fts5RowidCmp(pExpr, p1->iRowid, iFrom)<0)
){
int rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom);
@@ -204456,7 +236061,7 @@ static int fts5ExprNodeTest_AND(
}
static int fts5ExprNodeNext_AND(
- Fts5Expr *pExpr,
+ Fts5Expr *pExpr,
Fts5ExprNode *pNode,
int bFromValid,
i64 iFrom
@@ -204499,7 +236104,7 @@ static int fts5ExprNodeTest_NOT(
}
static int fts5ExprNodeNext_NOT(
- Fts5Expr *pExpr,
+ Fts5Expr *pExpr,
Fts5ExprNode *pNode,
int bFromValid,
i64 iFrom
@@ -204556,7 +236161,7 @@ static int fts5ExprNodeTest(
return rc;
}
-
+
/*
** Set node pNode, which is part of expression pExpr, to point to the first
** match. If there are no matches, set the Node.bEof flag to indicate EOF.
@@ -204610,8 +236215,8 @@ static int fts5ExprNodeFirst(Fts5Expr *pExpr, Fts5ExprNode *pNode){
/*
** Begin iterating through the set of documents in index pIdx matched by
-** the MATCH expression passed as the first argument. If the "bDesc"
-** parameter is passed a non-zero value, iteration is in descending rowid
+** the MATCH expression passed as the first argument. If the "bDesc"
+** parameter is passed a non-zero value, iteration is in descending rowid
** order. Or, if it is zero, in ascending order.
**
** If iterating in ascending rowid order (bDesc==0), the first document
@@ -204633,23 +236238,23 @@ static int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bD
/* If not at EOF but the current rowid occurs earlier than iFirst in
** the iteration order, move to document iFirst or later. */
- if( rc==SQLITE_OK
- && 0==pRoot->bEof
- && fts5RowidCmp(p, pRoot->iRowid, iFirst)<0
+ if( rc==SQLITE_OK
+ && 0==pRoot->bEof
+ && fts5RowidCmp(p, pRoot->iRowid, iFirst)<0
){
rc = fts5ExprNodeNext(p, pRoot, 1, iFirst);
}
/* If the iterator is not at a real match, skip forward until it is. */
- while( pRoot->bNomatch ){
- assert( pRoot->bEof==0 && rc==SQLITE_OK );
+ while( pRoot->bNomatch && rc==SQLITE_OK ){
+ assert( pRoot->bEof==0 );
rc = fts5ExprNodeNext(p, pRoot, 0, 0);
}
return rc;
}
/*
-** Move to the next document
+** Move to the next document
**
** Return SQLITE_OK if successful, or an SQLite error code otherwise. It
** is not considered an error if the query does not match any documents.
@@ -204692,7 +236297,7 @@ static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){
Fts5ExprTerm *pSyn;
Fts5ExprTerm *pNext;
Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
- sqlite3_free(pTerm->zTerm);
+ sqlite3_free(pTerm->pTerm);
sqlite3Fts5IterClose(pTerm->pIter);
for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){
pNext = pSyn->pSynonym;
@@ -204737,18 +236342,20 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset(
return pNear;
}
if( pNear==0 ){
- int nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*);
- pRet = sqlite3_malloc(nByte);
+ sqlite3_int64 nByte;
+ nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*);
+ pRet = sqlite3_malloc64(nByte);
if( pRet==0 ){
pParse->rc = SQLITE_NOMEM;
}else{
- memset(pRet, 0, nByte);
+ memset(pRet, 0, (size_t)nByte);
}
}else if( (pNear->nPhrase % SZALLOC)==0 ){
int nNew = pNear->nPhrase + SZALLOC;
- int nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*);
+ sqlite3_int64 nByte;
- pRet = (Fts5ExprNearset*)sqlite3_realloc(pNear, nByte);
+ nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*);
+ pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte);
if( pRet==0 ){
pParse->rc = SQLITE_NOMEM;
}
@@ -204764,6 +236371,9 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset(
}else{
if( pRet->nPhrase>0 ){
Fts5ExprPhrase *pLast = pRet->apPhrase[pRet->nPhrase-1];
+ assert( pParse!=0 );
+ assert( pParse->apPhrase!=0 );
+ assert( pParse->nPhrase>=2 );
assert( pLast==pParse->apPhrase[pParse->nPhrase-2] );
if( pPhrase->nTerm==0 ){
fts5ExprPhraseFree(pPhrase);
@@ -204785,6 +236395,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset(
typedef struct TokenCtx TokenCtx;
struct TokenCtx {
Fts5ExprPhrase *pPhrase;
+ Fts5Config *pConfig;
int rc;
};
@@ -204812,14 +236423,18 @@ static int fts5ParseTokenize(
if( pPhrase && pPhrase->nTerm>0 && (tflags & FTS5_TOKEN_COLOCATED) ){
Fts5ExprTerm *pSyn;
- int nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1;
- pSyn = (Fts5ExprTerm*)sqlite3_malloc(nByte);
+ sqlite3_int64 nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1;
+ pSyn = (Fts5ExprTerm*)sqlite3_malloc64(nByte);
if( pSyn==0 ){
rc = SQLITE_NOMEM;
}else{
- memset(pSyn, 0, nByte);
- pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
- memcpy(pSyn->zTerm, pToken, nToken);
+ memset(pSyn, 0, (size_t)nByte);
+ pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
+ pSyn->nFullTerm = pSyn->nQueryTerm = nToken;
+ if( pCtx->pConfig->bTokendata ){
+ pSyn->nQueryTerm = (int)strlen(pSyn->pTerm);
+ }
+ memcpy(pSyn->pTerm, pToken, nToken);
pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn;
}
@@ -204829,7 +236444,7 @@ static int fts5ParseTokenize(
Fts5ExprPhrase *pNew;
int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);
- pNew = (Fts5ExprPhrase*)sqlite3_realloc(pPhrase,
+ pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase,
sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew
);
if( pNew==0 ){
@@ -204844,7 +236459,11 @@ static int fts5ParseTokenize(
if( rc==SQLITE_OK ){
pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
memset(pTerm, 0, sizeof(Fts5ExprTerm));
- pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
+ pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
+ pTerm->nFullTerm = pTerm->nQueryTerm = nToken;
+ if( pCtx->pConfig->bTokendata && rc==SQLITE_OK ){
+ pTerm->nQueryTerm = (int)strlen(pTerm->pTerm);
+ }
}
}
@@ -204879,6 +236498,20 @@ static void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p){
pParse->pExpr = p;
}
+static int parseGrowPhraseArray(Fts5Parse *pParse){
+ if( (pParse->nPhrase % 8)==0 ){
+ sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8);
+ Fts5ExprPhrase **apNew;
+ apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte);
+ if( apNew==0 ){
+ pParse->rc = SQLITE_NOMEM;
+ return SQLITE_NOMEM;
+ }
+ pParse->apPhrase = apNew;
+ }
+ return SQLITE_OK;
+}
+
/*
** This function is called by the parser to process a string token. The
** string may or may not be quoted. In any case it is tokenized and a
@@ -204897,6 +236530,7 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
memset(&sCtx, 0, sizeof(TokenCtx));
sCtx.pPhrase = pAppend;
+ sCtx.pConfig = pConfig;
rc = fts5ParseStringFromToken(pToken, &z);
if( rc==SQLITE_OK ){
@@ -204914,16 +236548,9 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
}else{
if( pAppend==0 ){
- if( (pParse->nPhrase % 8)==0 ){
- int nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8);
- Fts5ExprPhrase **apNew;
- apNew = (Fts5ExprPhrase**)sqlite3_realloc(pParse->apPhrase, nByte);
- if( apNew==0 ){
- pParse->rc = SQLITE_NOMEM;
- fts5ExprPhraseFree(sCtx.pPhrase);
- return 0;
- }
- pParse->apPhrase = apNew;
+ if( parseGrowPhraseArray(pParse) ){
+ fts5ExprPhraseFree(sCtx.pPhrase);
+ return 0;
}
pParse->nPhrase++;
}
@@ -204946,64 +236573,70 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
** expression passed as the second argument.
*/
static int sqlite3Fts5ExprClonePhrase(
- Fts5Expr *pExpr,
- int iPhrase,
+ Fts5Expr *pExpr,
+ int iPhrase,
Fts5Expr **ppNew
){
int rc = SQLITE_OK; /* Return code */
- Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */
+ Fts5ExprPhrase *pOrig = 0; /* The phrase extracted from pExpr */
Fts5Expr *pNew = 0; /* Expression to return via *ppNew */
- TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */
-
- pOrig = pExpr->apExprPhrase[iPhrase];
- pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
+ TokenCtx sCtx = {0,0,0}; /* Context object for fts5ParseTokenize */
+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
+ rc = SQLITE_RANGE;
+ }else{
+ pOrig = pExpr->apExprPhrase[iPhrase];
+ pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
+ }
if( rc==SQLITE_OK ){
- pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
+ pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
sizeof(Fts5ExprPhrase*));
}
if( rc==SQLITE_OK ){
- pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
+ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
sizeof(Fts5ExprNode));
}
if( rc==SQLITE_OK ){
- pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
+ pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
}
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
if( pColsetOrig ){
- int nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
- Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
- if( pColset ){
- memcpy(pColset, pColsetOrig, nByte);
+ sqlite3_int64 nByte;
+ Fts5Colset *pColset;
+ nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
+ pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
+ if( pColset ){
+ memcpy(pColset, pColsetOrig, (size_t)nByte);
}
pNew->pRoot->pNear->pColset = pColset;
}
}
- if( pOrig->nTerm ){
- int i; /* Used to iterate through phrase terms */
- for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
- int tflags = 0;
- Fts5ExprTerm *p;
- for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
- const char *zTerm = p->zTerm;
- rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm),
- 0, 0);
- tflags = FTS5_TOKEN_COLOCATED;
- }
- if( rc==SQLITE_OK ){
- sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
- sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
+ if( rc==SQLITE_OK ){
+ if( pOrig->nTerm ){
+ int i; /* Used to iterate through phrase terms */
+ sCtx.pConfig = pExpr->pConfig;
+ for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
+ int tflags = 0;
+ Fts5ExprTerm *p;
+ for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
+ rc = fts5ParseTokenize((void*)&sCtx,tflags,p->pTerm,p->nFullTerm,0,0);
+ tflags = FTS5_TOKEN_COLOCATED;
+ }
+ if( rc==SQLITE_OK ){
+ sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
+ sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
+ }
}
+ }else{
+ /* This happens when parsing a token or quoted phrase that contains
+ ** no token characters at all. (e.g ... MATCH '""'). */
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
}
- }else{
- /* This happens when parsing a token or quoted phrase that contains
- ** no token characters at all. (e.g ... MATCH '""'). */
- sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
}
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
/* All the allocations succeeded. Put the expression object together. */
pNew->pIndex = pExpr->pIndex;
pNew->pConfig = pExpr->pConfig;
@@ -205013,9 +236646,9 @@ static int sqlite3Fts5ExprClonePhrase(
pNew->pRoot->pNear->nPhrase = 1;
sCtx.pPhrase->pNode = pNew->pRoot;
- if( pOrig->nTerm==1
- && pOrig->aTerm[0].pSynonym==0
- && pOrig->aTerm[0].bFirst==0
+ if( pOrig->nTerm==1
+ && pOrig->aTerm[0].pSynonym==0
+ && pOrig->aTerm[0].bFirst==0
){
pNew->pRoot->eType = FTS5_TERM;
pNew->pRoot->xNext = fts5ExprNodeNext_TERM;
@@ -205048,7 +236681,7 @@ static void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token *pTok){
}
static void sqlite3Fts5ParseSetDistance(
- Fts5Parse *pParse,
+ Fts5Parse *pParse,
Fts5ExprNearset *pNear,
Fts5Token *p
){
@@ -205077,7 +236710,7 @@ static void sqlite3Fts5ParseSetDistance(
** The second argument passed to this function may be NULL, or it may be
** an existing Fts5Colset object. This function returns a pointer to
** a new colset object containing the contents of (p) with new value column
-** number iCol appended.
+** number iCol appended.
**
** If an OOM error occurs, store an error code in pParse and return NULL.
** The old colset object (if any) is not freed in this case.
@@ -205093,7 +236726,7 @@ static Fts5Colset *fts5ParseColset(
assert( pParse->rc==SQLITE_OK );
assert( iCol>=0 && iCol<pParse->pConfig->nCol );
- pNew = sqlite3_realloc(p, sizeof(Fts5Colset) + sizeof(int)*nCol);
+ pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol);
if( pNew==0 ){
pParse->rc = SQLITE_NOMEM;
}else{
@@ -205127,7 +236760,7 @@ static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p
Fts5Colset *pRet;
int nCol = pParse->pConfig->nCol;
- pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
+ pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
sizeof(Fts5Colset) + sizeof(int)*nCol
);
if( pRet ){
@@ -205180,7 +236813,7 @@ static Fts5Colset *sqlite3Fts5ParseColset(
/*
** If argument pOrig is NULL, or if (*pRc) is set to anything other than
-** SQLITE_OK when this function is called, NULL is returned.
+** SQLITE_OK when this function is called, NULL is returned.
**
** Otherwise, a copy of (*pOrig) is made into memory obtained from
** sqlite3Fts5MallocZero() and a pointer to it returned. If the allocation
@@ -205189,10 +236822,10 @@ static Fts5Colset *sqlite3Fts5ParseColset(
static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){
Fts5Colset *pRet;
if( pOrig ){
- int nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int);
+ sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int);
pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte);
- if( pRet ){
- memcpy(pRet, pOrig, nByte);
+ if( pRet ){
+ memcpy(pRet, pOrig, (size_t)nByte);
}
}else{
pRet = 0;
@@ -205230,13 +236863,13 @@ static void fts5MergeColset(Fts5Colset *pColset, Fts5Colset *pMerge){
** zero, or it may create copies of pColset using fts5CloneColset().
*/
static void fts5ParseSetColset(
- Fts5Parse *pParse,
- Fts5ExprNode *pNode,
+ Fts5Parse *pParse,
+ Fts5ExprNode *pNode,
Fts5Colset *pColset,
Fts5Colset **ppFree
){
if( pParse->rc==SQLITE_OK ){
- assert( pNode->eType==FTS5_TERM || pNode->eType==FTS5_STRING
+ assert( pNode->eType==FTS5_TERM || pNode->eType==FTS5_STRING
|| pNode->eType==FTS5_AND || pNode->eType==FTS5_OR
|| pNode->eType==FTS5_NOT || pNode->eType==FTS5_EOF
);
@@ -205268,15 +236901,14 @@ static void fts5ParseSetColset(
** Apply colset pColset to expression node pExpr and all of its descendents.
*/
static void sqlite3Fts5ParseSetColset(
- Fts5Parse *pParse,
- Fts5ExprNode *pExpr,
- Fts5Colset *pColset
+ Fts5Parse *pParse,
+ Fts5ExprNode *pExpr,
+ Fts5Colset *pColset
){
Fts5Colset *pFree = pColset;
if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){
- pParse->rc = SQLITE_ERROR;
- pParse->zErr = sqlite3_mprintf(
- "fts5: column queries are not supported (detail=none)"
+ sqlite3Fts5ParseError(pParse,
+ "fts5: column queries are not supported (detail=none)"
);
}else{
fts5ParseSetColset(pParse, pExpr, pColset, &pFree);
@@ -205288,7 +236920,7 @@ static void fts5ExprAssignXNext(Fts5ExprNode *pNode){
switch( pNode->eType ){
case FTS5_STRING: {
Fts5ExprNearset *pNear = pNode->pNear;
- if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1
+ if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1
&& pNear->apPhrase[0]->aTerm[0].pSynonym==0
&& pNear->apPhrase[0]->aTerm[0].bFirst==0
){
@@ -205318,6 +236950,7 @@ static void fts5ExprAssignXNext(Fts5ExprNode *pNode){
}
static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
+ int ii = p->nChild;
if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
int nByte = sizeof(Fts5ExprNode*) * pSub->nChild;
memcpy(&p->apChild[p->nChild], pSub->apChild, nByte);
@@ -205326,6 +236959,73 @@ static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
}else{
p->apChild[p->nChild++] = pSub;
}
+ for( ; ii<p->nChild; ii++){
+ p->iHeight = MAX(p->iHeight, p->apChild[ii]->iHeight + 1);
+ }
+}
+
+/*
+** This function is used when parsing LIKE or GLOB patterns against
+** trigram indexes that specify either detail=column or detail=none.
+** It converts a phrase:
+**
+** abc + def + ghi
+**
+** into an AND tree:
+**
+** abc AND def AND ghi
+*/
+static Fts5ExprNode *fts5ParsePhraseToAnd(
+ Fts5Parse *pParse,
+ Fts5ExprNearset *pNear
+){
+ int nTerm = pNear->apPhrase[0]->nTerm;
+ int ii;
+ int nByte;
+ Fts5ExprNode *pRet;
+
+ assert( pNear->nPhrase==1 );
+ assert( pParse->bPhraseToAnd );
+
+ nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*);
+ pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
+ if( pRet ){
+ pRet->eType = FTS5_AND;
+ pRet->nChild = nTerm;
+ pRet->iHeight = 1;
+ fts5ExprAssignXNext(pRet);
+ pParse->nPhrase--;
+ for(ii=0; ii<nTerm; ii++){
+ Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
+ &pParse->rc, sizeof(Fts5ExprPhrase)
+ );
+ if( pPhrase ){
+ if( parseGrowPhraseArray(pParse) ){
+ fts5ExprPhraseFree(pPhrase);
+ }else{
+ Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii];
+ Fts5ExprTerm *pTo = &pPhrase->aTerm[0];
+ pParse->apPhrase[pParse->nPhrase++] = pPhrase;
+ pPhrase->nTerm = 1;
+ pTo->pTerm = sqlite3Fts5Strndup(&pParse->rc, p->pTerm, p->nFullTerm);
+ pTo->nQueryTerm = p->nQueryTerm;
+ pTo->nFullTerm = p->nFullTerm;
+ pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING,
+ 0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase)
+ );
+ }
+ }
+ }
+
+ if( pParse->rc ){
+ sqlite3Fts5ParseNodeFree(pRet);
+ pRet = 0;
+ }else{
+ sqlite3Fts5ParseNearsetFree(pNear);
+ }
+ }
+
+ return pRet;
}
/*
@@ -205343,8 +237043,8 @@ static Fts5ExprNode *sqlite3Fts5ParseNode(
if( pParse->rc==SQLITE_OK ){
int nChild = 0; /* Number of children of returned node */
- int nByte; /* Bytes of space to allocate for this node */
-
+ sqlite3_int64 nByte; /* Bytes of space to allocate for this node */
+
assert( (eType!=FTS5_STRING && !pNear)
|| (eType==FTS5_STRING && !pLeft && !pRight)
);
@@ -205352,51 +237052,63 @@ static Fts5ExprNode *sqlite3Fts5ParseNode(
if( eType!=FTS5_STRING && pLeft==0 ) return pRight;
if( eType!=FTS5_STRING && pRight==0 ) return pLeft;
- if( eType==FTS5_NOT ){
- nChild = 2;
- }else if( eType==FTS5_AND || eType==FTS5_OR ){
- nChild = 2;
- if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
- if( pRight->eType==eType ) nChild += pRight->nChild-1;
- }
+ if( eType==FTS5_STRING
+ && pParse->bPhraseToAnd
+ && pNear->apPhrase[0]->nTerm>1
+ ){
+ pRet = fts5ParsePhraseToAnd(pParse, pNear);
+ }else{
+ if( eType==FTS5_NOT ){
+ nChild = 2;
+ }else if( eType==FTS5_AND || eType==FTS5_OR ){
+ nChild = 2;
+ if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
+ if( pRight->eType==eType ) nChild += pRight->nChild-1;
+ }
- nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
- pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
+ nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
+ pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
- if( pRet ){
- pRet->eType = eType;
- pRet->pNear = pNear;
- fts5ExprAssignXNext(pRet);
- if( eType==FTS5_STRING ){
- int iPhrase;
- for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
- pNear->apPhrase[iPhrase]->pNode = pRet;
- if( pNear->apPhrase[iPhrase]->nTerm==0 ){
- pRet->xNext = 0;
- pRet->eType = FTS5_EOF;
+ if( pRet ){
+ pRet->eType = eType;
+ pRet->pNear = pNear;
+ fts5ExprAssignXNext(pRet);
+ if( eType==FTS5_STRING ){
+ int iPhrase;
+ for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
+ pNear->apPhrase[iPhrase]->pNode = pRet;
+ if( pNear->apPhrase[iPhrase]->nTerm==0 ){
+ pRet->xNext = 0;
+ pRet->eType = FTS5_EOF;
+ }
}
- }
- if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){
- Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
- if( pNear->nPhrase!=1
- || pPhrase->nTerm>1
- || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst)
- ){
- assert( pParse->rc==SQLITE_OK );
- pParse->rc = SQLITE_ERROR;
- assert( pParse->zErr==0 );
- pParse->zErr = sqlite3_mprintf(
- "fts5: %s queries are not supported (detail!=full)",
- pNear->nPhrase==1 ? "phrase": "NEAR"
- );
+ if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
+ if( pNear->nPhrase!=1
+ || pPhrase->nTerm>1
+ || (pPhrase->nTerm>0 && pPhrase->aTerm[0].bFirst)
+ ){
+ sqlite3Fts5ParseError(pParse,
+ "fts5: %s queries are not supported (detail!=full)",
+ pNear->nPhrase==1 ? "phrase": "NEAR"
+ );
+ sqlite3_free(pRet);
+ pRet = 0;
+ }
+ }
+ }else{
+ fts5ExprAddChildren(pRet, pLeft);
+ fts5ExprAddChildren(pRet, pRight);
+ if( pRet->iHeight>SQLITE_FTS5_MAX_EXPR_DEPTH ){
+ sqlite3Fts5ParseError(pParse,
+ "fts5 expression tree is too large (maximum depth %d)",
+ SQLITE_FTS5_MAX_EXPR_DEPTH
+ );
sqlite3_free(pRet);
pRet = 0;
}
}
- }else{
- fts5ExprAddChildren(pRet, pLeft);
- fts5ExprAddChildren(pRet, pRight);
}
}
}
@@ -205423,14 +237135,14 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
sqlite3Fts5ParseNodeFree(pRight);
}else{
- assert( pLeft->eType==FTS5_STRING
+ assert( pLeft->eType==FTS5_STRING
|| pLeft->eType==FTS5_TERM
|| pLeft->eType==FTS5_EOF
|| pLeft->eType==FTS5_AND
);
- assert( pRight->eType==FTS5_STRING
- || pRight->eType==FTS5_TERM
- || pRight->eType==FTS5_EOF
+ assert( pRight->eType==FTS5_STRING
+ || pRight->eType==FTS5_TERM
+ || pRight->eType==FTS5_EOF
);
if( pLeft->eType==FTS5_AND ){
@@ -205438,9 +237150,9 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
}else{
pPrev = pLeft;
}
- assert( pPrev->eType==FTS5_STRING
- || pPrev->eType==FTS5_TERM
- || pPrev->eType==FTS5_EOF
+ assert( pPrev->eType==FTS5_STRING
+ || pPrev->eType==FTS5_TERM
+ || pPrev->eType==FTS5_EOF
);
if( pRight->eType==FTS5_EOF ){
@@ -205474,23 +237186,25 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
return pRet;
}
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
- int nByte = 0;
+ sqlite3_int64 nByte = 0;
Fts5ExprTerm *p;
char *zQuoted;
/* Determine the maximum amount of space required. */
for(p=pTerm; p; p=p->pSynonym){
- nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2;
+ nByte += pTerm->nQueryTerm * 2 + 3 + 2;
}
- zQuoted = sqlite3_malloc(nByte);
+ zQuoted = sqlite3_malloc64(nByte);
if( zQuoted ){
int i = 0;
for(p=pTerm; p; p=p->pSynonym){
- char *zIn = p->zTerm;
+ char *zIn = p->pTerm;
+ char *zEnd = &zIn[p->nQueryTerm];
zQuoted[i++] = '"';
- while( *zIn ){
+ while( zIn<zEnd ){
if( *zIn=='"' ) zQuoted[i++] = '"';
zQuoted[i++] = *zIn++;
}
@@ -205522,20 +237236,20 @@ static char *fts5PrintfAppend(char *zApp, const char *zFmt, ...){
}
/*
-** Compose a tcl-readable representation of expression pExpr. Return a
-** pointer to a buffer containing that representation. It is the
-** responsibility of the caller to at some point free the buffer using
+** Compose a tcl-readable representation of expression pExpr. Return a
+** pointer to a buffer containing that representation. It is the
+** responsibility of the caller to at some point free the buffer using
** sqlite3_free().
*/
static char *fts5ExprPrintTcl(
- Fts5Config *pConfig,
+ Fts5Config *pConfig,
const char *zNearsetCmd,
Fts5ExprNode *pExpr
){
char *zRet = 0;
if( pExpr->eType==FTS5_STRING || pExpr->eType==FTS5_TERM ){
Fts5ExprNearset *pNear = pExpr->pNear;
- int i;
+ int i;
int iTerm;
zRet = fts5PrintfAppend(zRet, "%s ", zNearsetCmd);
@@ -205568,8 +237282,10 @@ static char *fts5ExprPrintTcl(
zRet = fts5PrintfAppend(zRet, " {");
for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){
- char *zTerm = pPhrase->aTerm[iTerm].zTerm;
- zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm);
+ Fts5ExprTerm *p = &pPhrase->aTerm[iTerm];
+ zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ",
+ p->nQueryTerm, p->pTerm
+ );
if( pPhrase->aTerm[iTerm].bPrefix ){
zRet = fts5PrintfAppend(zRet, "*");
}
@@ -205579,15 +237295,17 @@ static char *fts5ExprPrintTcl(
if( zRet==0 ) return 0;
}
+ }else if( pExpr->eType==0 ){
+ zRet = sqlite3_mprintf("{}");
}else{
char const *zOp = 0;
int i;
switch( pExpr->eType ){
case FTS5_AND: zOp = "AND"; break;
case FTS5_NOT: zOp = "NOT"; break;
- default:
+ default:
assert( pExpr->eType==FTS5_OR );
- zOp = "OR";
+ zOp = "OR";
break;
}
@@ -205613,12 +237331,21 @@ static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){
}else
if( pExpr->eType==FTS5_STRING || pExpr->eType==FTS5_TERM ){
Fts5ExprNearset *pNear = pExpr->pNear;
- int i;
+ int i;
int iTerm;
if( pNear->pColset ){
- int iCol = pNear->pColset->aiCol[0];
- zRet = fts5PrintfAppend(zRet, "%s : ", pConfig->azCol[iCol]);
+ int ii;
+ Fts5Colset *pColset = pNear->pColset;
+ if( pColset->nCol>1 ) zRet = fts5PrintfAppend(zRet, "{");
+ for(ii=0; ii<pColset->nCol; ii++){
+ zRet = fts5PrintfAppend(zRet, "%s%s",
+ pConfig->azCol[pColset->aiCol[ii]], ii==pColset->nCol-1 ? "" : " "
+ );
+ }
+ if( zRet ){
+ zRet = fts5PrintfAppend(zRet, "%s : ", pColset->nCol>1 ? "}" : "");
+ }
if( zRet==0 ) return 0;
}
@@ -205658,9 +237385,9 @@ static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){
switch( pExpr->eType ){
case FTS5_AND: zOp = " AND "; break;
case FTS5_NOT: zOp = " NOT "; break;
- default:
+ default:
assert( pExpr->eType==FTS5_OR );
- zOp = " OR ";
+ zOp = " OR ";
break;
}
@@ -205672,7 +237399,7 @@ static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){
}else{
int e = pExpr->apChild[i]->eType;
int b = (e!=FTS5_STRING && e!=FTS5_TERM && e!=FTS5_EOF);
- zRet = fts5PrintfAppend(zRet, "%s%s%z%s",
+ zRet = fts5PrintfAppend(zRet, "%s%s%z%s",
(i==0 ? "" : zOp),
(b?"(":""), z, (b?")":"")
);
@@ -205723,7 +237450,7 @@ static void fts5ExprFunction(
}
nConfig = 3 + (nArg-iArg);
- azConfig = (const char**)sqlite3_malloc(sizeof(char*) * nConfig);
+ azConfig = (const char**)sqlite3_malloc64(sizeof(char*) * nConfig);
if( azConfig==0 ){
sqlite3_result_error_nomem(pCtx);
return;
@@ -205732,14 +237459,16 @@ static void fts5ExprFunction(
azConfig[1] = "main";
azConfig[2] = "tbl";
for(i=3; iArg<nArg; iArg++){
- azConfig[i++] = (const char*)sqlite3_value_text(apVal[iArg]);
+ const char *z = (const char*)sqlite3_value_text(apVal[iArg]);
+ azConfig[i++] = (z ? z : "");
}
zExpr = (const char*)sqlite3_value_text(apVal[0]);
+ if( zExpr==0 ) zExpr = "";
rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr);
if( rc==SQLITE_OK ){
- rc = sqlite3Fts5ExprNew(pConfig, pConfig->nCol, zExpr, &pExpr, &zErr);
+ rc = sqlite3Fts5ExprNew(pConfig, 0, pConfig->nCol, zExpr, &pExpr, &zErr);
}
if( rc==SQLITE_OK ){
char *zText;
@@ -205788,7 +237517,7 @@ static void fts5ExprFunctionTcl(
/*
** The implementation of an SQLite user-defined-function that accepts a
-** single integer as an argument. If the integer is an alpha-numeric
+** single integer as an argument. If the integer is an alpha-numeric
** unicode code point, 1 is returned. Otherwise 0.
*/
static void fts5ExprIsAlnum(
@@ -205799,7 +237528,7 @@ static void fts5ExprIsAlnum(
int iCode;
u8 aArr[32];
if( nArg!=1 ){
- sqlite3_result_error(pCtx,
+ sqlite3_result_error(pCtx,
"wrong number of arguments to function fts5_isalnum", -1
);
return;
@@ -205809,7 +237538,7 @@ static void fts5ExprIsAlnum(
sqlite3Fts5UnicodeCatParse("N*", aArr);
sqlite3Fts5UnicodeCatParse("Co", aArr);
iCode = sqlite3_value_int(apVal[0]);
- sqlite3_result_int(pCtx, aArr[sqlite3Fts5UnicodeCategory(iCode)]);
+ sqlite3_result_int(pCtx, aArr[sqlite3Fts5UnicodeCategory((u32)iCode)]);
}
static void fts5ExprFold(
@@ -205818,7 +237547,7 @@ static void fts5ExprFold(
sqlite3_value **apVal /* Function arguments */
){
if( nArg!=1 && nArg!=2 ){
- sqlite3_result_error(pCtx,
+ sqlite3_result_error(pCtx,
"wrong number of arguments to function fts5_fold", -1
);
}else{
@@ -205829,12 +237558,14 @@ static void fts5ExprFold(
sqlite3_result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics));
}
}
+#endif /* if SQLITE_TEST || SQLITE_FTS5_DEBUG */
/*
** This is called during initialization to register the fts5_expr() scalar
** UDF with the SQLite handle passed as the only argument.
*/
static int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
struct Fts5ExprFunc {
const char *z;
void (*x)(sqlite3_context*,int,sqlite3_value**);
@@ -205852,6 +237583,10 @@ static int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){
struct Fts5ExprFunc *p = &aFunc[i];
rc = sqlite3_create_function(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0);
}
+#else
+ int rc = SQLITE_OK;
+ UNUSED_PARAM2(pGlobal,db);
+#endif
/* Avoid warnings indicating that sqlite3Fts5ParserTrace() and
** sqlite3Fts5ParserFallback() are unused */
@@ -205902,17 +237637,26 @@ struct Fts5PoslistPopulator {
int bMiss;
};
+/*
+** Clear the position lists associated with all phrases in the expression
+** passed as the first argument. Argument bLive is true if the expression
+** might be pointing to a real entry, otherwise it has just been reset.
+**
+** At present this function is only used for detail=col and detail=none
+** fts5 tables. This implies that all phrases must be at most 1 token
+** in size, as phrase matches are not supported without detail=full.
+*/
static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){
Fts5PoslistPopulator *pRet;
- pRet = sqlite3_malloc(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
+ pRet = sqlite3_malloc64(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
if( pRet ){
int i;
memset(pRet, 0, sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
for(i=0; i<pExpr->nPhrase; i++){
Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist;
Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode;
- assert( pExpr->apExprPhrase[i]->nTerm==1 );
- if( bLive &&
+ assert( pExpr->apExprPhrase[i]->nTerm<=1 );
+ if( bLive &&
(pBuf->n==0 || pNode->iRowid!=pExpr->pRoot->iRowid || pNode->bEof)
){
pRet[i].bMiss = 1;
@@ -205942,6 +237686,17 @@ static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){
return 0;
}
+/*
+** pToken is a buffer nToken bytes in size that may or may not contain
+** an embedded 0x00 byte. If it does, return the number of bytes in
+** the buffer before the 0x00. If it does not, return nToken.
+*/
+static int fts5QueryTerm(const char *pToken, int nToken){
+ int ii;
+ for(ii=0; ii<nToken && pToken[ii]; ii++){}
+ return ii;
+}
+
static int fts5ExprPopulatePoslistsCb(
void *pCtx, /* Copy of 2nd argument to xTokenize() */
int tflags, /* Mask of FTS5_TOKEN_* flags */
@@ -205953,22 +237708,33 @@ static int fts5ExprPopulatePoslistsCb(
Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx;
Fts5Expr *pExpr = p->pExpr;
int i;
+ int nQuery = nToken;
+ i64 iRowid = pExpr->pRoot->iRowid;
UNUSED_PARAM2(iUnused1, iUnused2);
- if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
+ if( nQuery>FTS5_MAX_TOKEN_SIZE ) nQuery = FTS5_MAX_TOKEN_SIZE;
+ if( pExpr->pConfig->bTokendata ){
+ nQuery = fts5QueryTerm(pToken, nQuery);
+ }
if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++;
for(i=0; i<pExpr->nPhrase; i++){
- Fts5ExprTerm *pTerm;
+ Fts5ExprTerm *pT;
if( p->aPopulator[i].bOk==0 ) continue;
- for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){
- int nTerm = (int)strlen(pTerm->zTerm);
- if( (nTerm==nToken || (nTerm<nToken && pTerm->bPrefix))
- && memcmp(pTerm->zTerm, pToken, nTerm)==0
+ for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){
+ if( (pT->nQueryTerm==nQuery || (pT->nQueryTerm<nQuery && pT->bPrefix))
+ && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0
){
int rc = sqlite3Fts5PoslistWriterAppend(
&pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
);
+ if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){
+ int iCol = p->iOff>>32;
+ int iTokOff = p->iOff & 0x7FFFFFFF;
+ rc = sqlite3Fts5IndexIterWriteTokendata(
+ pT->pIter, pToken, nToken, iRowid, iCol, iTokOff
+ );
+ }
if( rc ) return rc;
break;
}
@@ -205979,9 +237745,9 @@ static int fts5ExprPopulatePoslistsCb(
static int sqlite3Fts5ExprPopulatePoslists(
Fts5Config *pConfig,
- Fts5Expr *pExpr,
+ Fts5Expr *pExpr,
Fts5PoslistPopulator *aPopulator,
- int iCol,
+ int iCol,
const char *z, int n
){
int i;
@@ -205993,7 +237759,7 @@ static int sqlite3Fts5ExprPopulatePoslists(
for(i=0; i<pExpr->nPhrase; i++){
Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode;
Fts5Colset *pColset = pNode->pNear->pColset;
- if( (pColset && 0==fts5ExprColsetTest(pColset, iCol))
+ if( (pColset && 0==fts5ExprColsetTest(pColset, iCol))
|| aPopulator[i].bMiss
){
aPopulator[i].bOk = 0;
@@ -206002,7 +237768,7 @@ static int sqlite3Fts5ExprPopulatePoslists(
}
}
- return sqlite3Fts5Tokenize(pConfig,
+ return sqlite3Fts5Tokenize(pConfig,
FTS5_TOKENIZE_DOCUMENT, z, n, (void*)&sCtx, fts5ExprPopulatePoslistsCb
);
}
@@ -206067,12 +237833,12 @@ static void sqlite3Fts5ExprCheckPoslists(Fts5Expr *pExpr, i64 iRowid){
}
/*
-** This function is only called for detail=columns tables.
+** This function is only called for detail=columns tables.
*/
static int sqlite3Fts5ExprPhraseCollist(
- Fts5Expr *pExpr,
- int iPhrase,
- const u8 **ppCollist,
+ Fts5Expr *pExpr,
+ int iPhrase,
+ const u8 **ppCollist,
int *pnCollist
){
Fts5ExprPhrase *pPhrase = pExpr->apExprPhrase[iPhrase];
@@ -206082,8 +237848,8 @@ static int sqlite3Fts5ExprPhraseCollist(
assert( iPhrase>=0 && iPhrase<pExpr->nPhrase );
assert( pExpr->pConfig->eDetail==FTS5_DETAIL_COLUMNS );
- if( pNode->bEof==0
- && pNode->iRowid==pExpr->pRoot->iRowid
+ if( pNode->bEof==0
+ && pNode->iRowid==pExpr->pRoot->iRowid
&& pPhrase->poslist.n>0
){
Fts5ExprTerm *pTerm = &pPhrase->aTerm[0];
@@ -206104,6 +237870,82 @@ static int sqlite3Fts5ExprPhraseCollist(
return rc;
}
+/*
+** Does the work of the fts5_api.xQueryToken() API method.
+*/
+static int sqlite3Fts5ExprQueryToken(
+ Fts5Expr *pExpr,
+ int iPhrase,
+ int iToken,
+ const char **ppOut,
+ int *pnOut
+){
+ Fts5ExprPhrase *pPhrase = 0;
+
+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
+ return SQLITE_RANGE;
+ }
+ pPhrase = pExpr->apExprPhrase[iPhrase];
+ if( iToken<0 || iToken>=pPhrase->nTerm ){
+ return SQLITE_RANGE;
+ }
+
+ *ppOut = pPhrase->aTerm[iToken].pTerm;
+ *pnOut = pPhrase->aTerm[iToken].nFullTerm;
+ return SQLITE_OK;
+}
+
+/*
+** Does the work of the fts5_api.xInstToken() API method.
+*/
+static int sqlite3Fts5ExprInstToken(
+ Fts5Expr *pExpr,
+ i64 iRowid,
+ int iPhrase,
+ int iCol,
+ int iOff,
+ int iToken,
+ const char **ppOut,
+ int *pnOut
+){
+ Fts5ExprPhrase *pPhrase = 0;
+ Fts5ExprTerm *pTerm = 0;
+ int rc = SQLITE_OK;
+
+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
+ return SQLITE_RANGE;
+ }
+ pPhrase = pExpr->apExprPhrase[iPhrase];
+ if( iToken<0 || iToken>=pPhrase->nTerm ){
+ return SQLITE_RANGE;
+ }
+ pTerm = &pPhrase->aTerm[iToken];
+ if( pTerm->bPrefix==0 ){
+ if( pExpr->pConfig->bTokendata ){
+ rc = sqlite3Fts5IterToken(
+ pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut
+ );
+ }else{
+ *ppOut = pTerm->pTerm;
+ *pnOut = pTerm->nFullTerm;
+ }
+ }
+ return rc;
+}
+
+/*
+** Clear the token mappings for all Fts5IndexIter objects mannaged by
+** the expression passed as the only argument.
+*/
+static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
+ int ii;
+ for(ii=0; ii<pExpr->nPhrase; ii++){
+ Fts5ExprTerm *pT;
+ for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){
+ sqlite3Fts5IndexIterClearTokendata(pT->pIter);
+ }
+ }
+}
/*
** 2014 August 11
@@ -206142,11 +237984,16 @@ struct Fts5Hash {
};
/*
-** Each entry in the hash table is represented by an object of the
-** following type. Each object, its key (a nul-terminated string) and
-** its current data are stored in a single memory allocation. The
-** key immediately follows the object in memory. The position list
-** data immediately follows the key data in memory.
+** Each entry in the hash table is represented by an object of the
+** following type. Each object, its key, and its current data are stored
+** in a single memory allocation. The key immediately follows the object
+** in memory. The position list data immediately follows the key data
+** in memory.
+**
+** The key is Fts5HashEntry.nKey bytes in size. It consists of a single
+** byte identifying the index (either the main term index or a prefix-index),
+** followed by the term data. For example: "0token". There is no
+** nul-terminator - in this case nKey=6.
**
** The data that follows the key is in a similar, but not identical format
** to the doclist data stored in the database. It is:
@@ -206166,7 +238013,7 @@ struct Fts5Hash {
struct Fts5HashEntry {
Fts5HashEntry *pHashNext; /* Next hash entry with same hash-key */
Fts5HashEntry *pScanNext; /* Next entry in sorted order */
-
+
int nAlloc; /* Total size of allocation */
int iSzPoslist; /* Offset of space for 4-byte poslist size */
int nData; /* Total bytes of data (incl. structure) */
@@ -206197,20 +238044,20 @@ static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
- int nByte;
+ sqlite3_int64 nByte;
memset(pNew, 0, sizeof(Fts5Hash));
pNew->pnByte = pnByte;
pNew->eDetail = pConfig->eDetail;
pNew->nSlot = 1024;
nByte = sizeof(Fts5HashEntry*) * pNew->nSlot;
- pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc(nByte);
+ pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc64(nByte);
if( pNew->aSlot==0 ){
sqlite3_free(pNew);
*ppNew = 0;
rc = SQLITE_NOMEM;
}else{
- memset(pNew->aSlot, 0, nByte);
+ memset(pNew->aSlot, 0, (size_t)nByte);
}
}
return rc;
@@ -206272,7 +238119,7 @@ static int fts5HashResize(Fts5Hash *pHash){
Fts5HashEntry **apNew;
Fts5HashEntry **apOld = pHash->aSlot;
- apNew = (Fts5HashEntry**)sqlite3_malloc(nNew*sizeof(Fts5HashEntry*));
+ apNew = (Fts5HashEntry**)sqlite3_malloc64(nNew*sizeof(Fts5HashEntry*));
if( !apNew ) return SQLITE_NOMEM;
memset(apNew, 0, nNew*sizeof(Fts5HashEntry*));
@@ -206281,8 +238128,7 @@ static int fts5HashResize(Fts5Hash *pHash){
unsigned int iHash;
Fts5HashEntry *p = apOld[i];
apOld[i] = p->pHashNext;
- iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p),
- (int)strlen(fts5EntryKey(p)));
+ iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), p->nKey);
p->pHashNext = apNew[iHash];
apNew[iHash] = p;
}
@@ -206294,19 +238140,25 @@ static int fts5HashResize(Fts5Hash *pHash){
return SQLITE_OK;
}
-static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){
+static int fts5HashAddPoslistSize(
+ Fts5Hash *pHash,
+ Fts5HashEntry *p,
+ Fts5HashEntry *p2
+){
+ int nRet = 0;
if( p->iSzPoslist ){
- u8 *pPtr = (u8*)p;
+ u8 *pPtr = p2 ? (u8*)p2 : (u8*)p;
+ int nData = p->nData;
if( pHash->eDetail==FTS5_DETAIL_NONE ){
- assert( p->nData==p->iSzPoslist );
+ assert( nData==p->iSzPoslist );
if( p->bDel ){
- pPtr[p->nData++] = 0x00;
+ pPtr[nData++] = 0x00;
if( p->bContent ){
- pPtr[p->nData++] = 0x00;
+ pPtr[nData++] = 0x00;
}
}
}else{
- int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */
+ int nSz = (nData - p->iSzPoslist - 1); /* Size in bytes */
int nPos = nSz*2 + p->bDel; /* Value of nPos field */
assert( p->bDel==0 || p->bDel==1 );
@@ -206316,14 +238168,19 @@ static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){
int nByte = sqlite3Fts5GetVarintLen((u32)nPos);
memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz);
sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos);
- p->nData += (nByte-1);
+ nData += (nByte-1);
}
}
- p->iSzPoslist = 0;
- p->bDel = 0;
- p->bContent = 0;
+ nRet = nData - p->nData;
+ if( p2==0 ){
+ p->iSzPoslist = 0;
+ p->bDel = 0;
+ p->bContent = 0;
+ p->nData = nData;
+ }
}
+ return nRet;
}
/*
@@ -206347,16 +238204,16 @@ static int sqlite3Fts5HashWrite(
u8 *pPtr;
int nIncr = 0; /* Amount to increment (*pHash->pnByte) by */
int bNew; /* If non-delete entry should be written */
-
+
bNew = (pHash->eDetail==FTS5_DETAIL_FULL);
/* Attempt to locate an existing hash entry */
iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
char *zKey = fts5EntryKey(p);
- if( zKey[0]==bByte
- && p->nKey==nToken
- && memcmp(&zKey[1], pToken, nToken)==0
+ if( zKey[0]==bByte
+ && p->nKey==nToken+1
+ && memcmp(&zKey[1], pToken, nToken)==0
){
break;
}
@@ -206366,7 +238223,7 @@ static int sqlite3Fts5HashWrite(
if( p==0 ){
/* Figure out how much space to allocate */
char *zKey;
- int nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64;
+ sqlite3_int64 nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64;
if( nByte<128 ) nByte = 128;
/* Grow the Fts5Hash.aSlot[] array if necessary. */
@@ -206377,17 +238234,17 @@ static int sqlite3Fts5HashWrite(
}
/* Allocate new Fts5HashEntry and add it to the hash table. */
- p = (Fts5HashEntry*)sqlite3_malloc(nByte);
+ p = (Fts5HashEntry*)sqlite3_malloc64(nByte);
if( !p ) return SQLITE_NOMEM;
memset(p, 0, sizeof(Fts5HashEntry));
- p->nAlloc = nByte;
+ p->nAlloc = (int)nByte;
zKey = fts5EntryKey(p);
zKey[0] = bByte;
memcpy(&zKey[1], pToken, nToken);
assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) );
- p->nKey = nToken;
+ p->nKey = nToken+1;
zKey[nToken+1] = '\0';
- p->nData = nToken+1 + 1 + sizeof(Fts5HashEntry);
+ p->nData = nToken+1 + sizeof(Fts5HashEntry);
p->pHashNext = pHash->aSlot[iHash];
pHash->aSlot[iHash] = p;
pHash->nEntry++;
@@ -206402,11 +238259,10 @@ static int sqlite3Fts5HashWrite(
p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1);
}
- nIncr += p->nData;
}else{
- /* Appending to an existing hash-entry. Check that there is enough
- ** space to append the largest possible new entry. Worst case scenario
+ /* Appending to an existing hash-entry. Check that there is enough
+ ** space to append the largest possible new entry. Worst case scenario
** is:
**
** + 9 bytes for a new rowid,
@@ -206416,12 +238272,12 @@ static int sqlite3Fts5HashWrite(
** + 5 bytes for the new position offset (32-bit max).
*/
if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){
- int nNew = p->nAlloc * 2;
+ sqlite3_int64 nNew = p->nAlloc * 2;
Fts5HashEntry *pNew;
Fts5HashEntry **pp;
- pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew);
+ pNew = (Fts5HashEntry*)sqlite3_realloc64(p, nNew);
if( pNew==0 ) return SQLITE_NOMEM;
- pNew->nAlloc = nNew;
+ pNew->nAlloc = (int)nNew;
for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext);
*pp = pNew;
p = pNew;
@@ -206435,8 +238291,9 @@ static int sqlite3Fts5HashWrite(
/* If this is a new rowid, append the 4-byte size field for the previous
** entry, and the new rowid for this entry. */
if( iRowid!=p->iRowid ){
- fts5HashAddPoslistSize(pHash, p);
- p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid);
+ u64 iDiff = (u64)iRowid - (u64)p->iRowid;
+ fts5HashAddPoslistSize(pHash, p, 0);
+ p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iDiff);
p->iRowid = iRowid;
bNew = 1;
p->iSzPoslist = p->nData;
@@ -206452,7 +238309,7 @@ static int sqlite3Fts5HashWrite(
p->bContent = 1;
}else{
/* Append a new column value, if necessary */
- assert( iCol>=p->iCol );
+ assert_nc( iCol>=p->iCol );
if( iCol!=p->iCol ){
if( pHash->eDetail==FTS5_DETAIL_FULL ){
pPtr[p->nData++] = 0x01;
@@ -206504,12 +238361,17 @@ static Fts5HashEntry *fts5HashEntryMerge(
*ppOut = p1;
p1 = 0;
}else{
- int i = 0;
char *zKey1 = fts5EntryKey(p1);
char *zKey2 = fts5EntryKey(p2);
- while( zKey1[i]==zKey2[i] ) i++;
+ int nMin = MIN(p1->nKey, p2->nKey);
+
+ int cmp = memcmp(zKey1, zKey2, nMin);
+ if( cmp==0 ){
+ cmp = p1->nKey - p2->nKey;
+ }
+ assert( cmp!=0 );
- if( ((u8)zKey1[i])>((u8)zKey2[i]) ){
+ if( cmp>0 ){
/* p2 is smaller */
*ppOut = p2;
ppOut = &p2->pScanNext;
@@ -206528,13 +238390,11 @@ static Fts5HashEntry *fts5HashEntryMerge(
}
/*
-** Extract all tokens from hash table iHash and link them into a list
-** in sorted order. The hash table is cleared before returning. It is
-** the responsibility of the caller to free the elements of the returned
-** list.
+** Link all tokens from hash table iHash into a list in sorted order. The
+** tokens are not removed from the hash table.
*/
static int fts5HashEntrySort(
- Fts5Hash *pHash,
+ Fts5Hash *pHash,
const char *pTerm, int nTerm, /* Query prefix, if any */
Fts5HashEntry **ppSorted
){
@@ -206545,14 +238405,16 @@ static int fts5HashEntrySort(
int i;
*ppSorted = 0;
- ap = sqlite3_malloc(sizeof(Fts5HashEntry*) * nMergeSlot);
+ ap = sqlite3_malloc64(sizeof(Fts5HashEntry*) * nMergeSlot);
if( !ap ) return SQLITE_NOMEM;
memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot);
for(iSlot=0; iSlot<pHash->nSlot; iSlot++){
Fts5HashEntry *pIter;
for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){
- if( pTerm==0 || 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm) ){
+ if( pTerm==0
+ || (pIter->nKey>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm))
+ ){
Fts5HashEntry *pEntry = pIter;
pEntry->pScanNext = 0;
for(i=0; ap[i]; i++){
@@ -206569,7 +238431,6 @@ static int fts5HashEntrySort(
pList = fts5HashEntryMerge(pList, ap[i]);
}
- pHash->nEntry = 0;
sqlite3_free(ap);
*ppSorted = pList;
return SQLITE_OK;
@@ -206580,8 +238441,9 @@ static int fts5HashEntrySort(
*/
static int sqlite3Fts5HashQuery(
Fts5Hash *pHash, /* Hash table to query */
+ int nPre,
const char *pTerm, int nTerm, /* Query term */
- const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */
+ void **ppOut, /* OUT: Pointer to new object */
int *pnDoclist /* OUT: Size of doclist in bytes */
){
unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm);
@@ -206590,15 +238452,24 @@ static int sqlite3Fts5HashQuery(
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
zKey = fts5EntryKey(p);
- if( memcmp(zKey, pTerm, nTerm)==0 && zKey[nTerm]==0 ) break;
+ if( nTerm==p->nKey && memcmp(zKey, pTerm, nTerm)==0 ) break;
}
if( p ){
- fts5HashAddPoslistSize(pHash, p);
- *ppDoclist = (const u8*)&zKey[nTerm+1];
- *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1);
+ int nHashPre = sizeof(Fts5HashEntry) + nTerm;
+ int nList = p->nData - nHashPre;
+ u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10));
+ if( pRet ){
+ Fts5HashEntry *pFaux = (Fts5HashEntry*)&pRet[nPre-nHashPre];
+ memcpy(&pRet[nPre], &((u8*)p)[nHashPre], nList);
+ nList += fts5HashAddPoslistSize(pHash, p, pFaux);
+ *pnDoclist = nList;
+ }else{
+ *pnDoclist = 0;
+ return SQLITE_NOMEM;
+ }
}else{
- *ppDoclist = 0;
+ *ppOut = 0;
*pnDoclist = 0;
}
@@ -206612,6 +238483,28 @@ static int sqlite3Fts5HashScanInit(
return fts5HashEntrySort(p, pTerm, nTerm, &p->pScan);
}
+#ifdef SQLITE_DEBUG
+static int fts5HashCount(Fts5Hash *pHash){
+ int nEntry = 0;
+ int ii;
+ for(ii=0; ii<pHash->nSlot; ii++){
+ Fts5HashEntry *p = 0;
+ for(p=pHash->aSlot[ii]; p; p=p->pHashNext){
+ nEntry++;
+ }
+ }
+ return nEntry;
+}
+#endif
+
+/*
+** Return true if the hash table is empty, false otherwise.
+*/
+static int sqlite3Fts5HashIsEmpty(Fts5Hash *pHash){
+ assert( pHash->nEntry==fts5HashCount(pHash) );
+ return pHash->nEntry==0;
+}
+
static void sqlite3Fts5HashScanNext(Fts5Hash *p){
assert( !sqlite3Fts5HashScanEof(p) );
p->pScan = p->pScan->pScanNext;
@@ -206624,25 +238517,27 @@ static int sqlite3Fts5HashScanEof(Fts5Hash *p){
static void sqlite3Fts5HashScanEntry(
Fts5Hash *pHash,
const char **pzTerm, /* OUT: term (nul-terminated) */
+ int *pnTerm, /* OUT: Size of term in bytes */
const u8 **ppDoclist, /* OUT: pointer to doclist */
int *pnDoclist /* OUT: size of doclist in bytes */
){
Fts5HashEntry *p;
if( (p = pHash->pScan) ){
char *zKey = fts5EntryKey(p);
- int nTerm = (int)strlen(zKey);
- fts5HashAddPoslistSize(pHash, p);
+ int nTerm = p->nKey;
+ fts5HashAddPoslistSize(pHash, p, 0);
*pzTerm = zKey;
- *ppDoclist = (const u8*)&zKey[nTerm+1];
- *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1);
+ *pnTerm = nTerm;
+ *ppDoclist = (const u8*)&zKey[nTerm];
+ *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm);
}else{
*pzTerm = 0;
+ *pnTerm = 0;
*ppDoclist = 0;
*pnDoclist = 0;
}
}
-
/*
** 2014 May 31
**
@@ -206655,7 +238550,7 @@ static void sqlite3Fts5HashScanEntry(
**
******************************************************************************
**
-** Low level access to the FTS index stored in the database file. The
+** Low level access to the FTS index stored in the database file. The
** routines in this file file implement all read and write access to the
** %_data table. Other parts of the system access this functionality via
** the interface defined in fts5Int.h.
@@ -206671,10 +238566,10 @@ static void sqlite3Fts5HashScanEntry(
** As well as the main term index, there may be up to 31 prefix indexes.
** The format is similar to FTS3/4, except that:
**
-** * all segment b-tree leaf data is stored in fixed size page records
-** (e.g. 1000 bytes). A single doclist may span multiple pages. Care is
-** taken to ensure it is possible to iterate in either direction through
-** the entries in a doclist, or to seek to a specific entry within a
+** * all segment b-tree leaf data is stored in fixed size page records
+** (e.g. 1000 bytes). A single doclist may span multiple pages. Care is
+** taken to ensure it is possible to iterate in either direction through
+** the entries in a doclist, or to seek to a specific entry within a
** doclist, without loading it into memory.
**
** * large doclists that span many pages have associated "doclist index"
@@ -206699,6 +238594,26 @@ static void sqlite3Fts5HashScanEntry(
# error "FTS5_MAX_PREFIX_INDEXES is too large"
#endif
+#define FTS5_MAX_LEVEL 64
+
+/*
+** There are two versions of the format used for the structure record:
+**
+** 1. the legacy format, that may be read by all fts5 versions, and
+**
+** 2. the V2 format, which is used by contentless_delete=1 databases.
+**
+** Both begin with a 4-byte "configuration cookie" value. Then, a legacy
+** format structure record contains a varint - the number of levels in
+** the structure. Whereas a V2 structure record contains the constant
+** 4 bytes [0xff 0x00 0x00 0x01]. This is unambiguous as the value of a
+** varint has to be at least 16256 to begin with "0xFF". And the default
+** maximum number of levels is 64.
+**
+** See below for more on structure record formats.
+*/
+#define FTS5_STRUCTURE_V2 "\xFF\x00\x00\x01"
+
/*
** Details:
**
@@ -206706,21 +238621,21 @@ static void sqlite3Fts5HashScanEntry(
**
** CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB);
**
-** , contains the following 5 types of records. See the comments surrounding
-** the FTS5_*_ROWID macros below for a description of how %_data rowids are
+** , contains the following 6 types of records. See the comments surrounding
+** the FTS5_*_ROWID macros below for a description of how %_data rowids are
** assigned to each fo them.
**
** 1. Structure Records:
**
** The set of segments that make up an index - the index structure - are
** recorded in a single record within the %_data table. The record consists
-** of a single 32-bit configuration cookie value followed by a list of
-** SQLite varints. If the FTS table features more than one index (because
-** there are one or more prefix indexes), it is guaranteed that all share
-** the same cookie value.
+** of a single 32-bit configuration cookie value followed by a list of
+** SQLite varints.
**
-** Immediately following the configuration cookie, the record begins with
-** three varints:
+** If the structure record is a V2 record, the configuration cookie is
+** followed by the following 4 bytes: [0xFF 0x00 0x00 0x01].
+**
+** Next, the record continues with three varints:
**
** + number of levels,
** + total number of segments on all levels,
@@ -206735,6 +238650,12 @@ static void sqlite3Fts5HashScanEntry(
** + first leaf page number (often 1, always greater than 0)
** + final leaf page number
**
+** Then, for V2 structures only:
+**
+** + lower origin counter value,
+** + upper origin counter value,
+** + the number of tombstone hash pages.
+**
** 2. The Averages Record:
**
** A single record within the %_data table. The data is a list of varints.
@@ -206746,7 +238667,7 @@ static void sqlite3Fts5HashScanEntry(
**
** TERM/DOCLIST FORMAT:
**
-** Most of each segment leaf is taken up by term/doclist data. The
+** Most of each segment leaf is taken up by term/doclist data. The
** general format of term/doclist, starting with the first term
** on the leaf page, is:
**
@@ -206789,7 +238710,7 @@ static void sqlite3Fts5HashScanEntry(
**
** PAGE FORMAT
**
-** Each leaf page begins with a 4-byte header containing 2 16-bit
+** Each leaf page begins with a 4-byte header containing 2 16-bit
** unsigned integer fields in big-endian format. They are:
**
** * The byte offset of the first rowid on the page, if it exists
@@ -206824,7 +238745,7 @@ static void sqlite3Fts5HashScanEntry(
** 5. Segment doclist indexes:
**
** Doclist indexes are themselves b-trees, however they usually consist of
-** a single leaf record only. The format of each doclist index leaf page
+** a single leaf record only. The format of each doclist index leaf page
** is:
**
** * Flags byte. Bits are:
@@ -206834,8 +238755,8 @@ static void sqlite3Fts5HashScanEntry(
**
** * First rowid on page indicated by previous field. As a varint.
**
-** * A list of varints, one for each subsequent termless page. A
-** positive delta if the termless page contains at least one rowid,
+** * A list of varints, one for each subsequent termless page. A
+** positive delta if the termless page contains at least one rowid,
** or an 0x00 byte otherwise.
**
** Internal doclist index nodes are:
@@ -206848,8 +238769,40 @@ static void sqlite3Fts5HashScanEntry(
** * Copy of first rowid on page indicated by previous field. As a varint.
**
** * A list of delta-encoded varints - the first rowid on each subsequent
-** child page.
+** child page.
**
+** 6. Tombstone Hash Page
+**
+** These records are only ever present in contentless_delete=1 tables.
+** There are zero or more of these associated with each segment. They
+** are used to store the tombstone rowids for rows contained in the
+** associated segments.
+**
+** The set of nHashPg tombstone hash pages associated with a single
+** segment together form a single hash table containing tombstone rowids.
+** To find the page of the hash on which a key might be stored:
+**
+** iPg = (rowid % nHashPg)
+**
+** Then, within page iPg, which has nSlot slots:
+**
+** iSlot = (rowid / nHashPg) % nSlot
+**
+** Each tombstone hash page begins with an 8 byte header:
+**
+** 1-byte: Key-size (the size in bytes of each slot). Either 4 or 8.
+** 1-byte: rowid-0-tombstone flag. This flag is only valid on the
+** first tombstone hash page for each segment (iPg=0). If set,
+** the hash table contains rowid 0. If clear, it does not.
+** Rowid 0 is handled specially.
+** 2-bytes: unused.
+** 4-bytes: Big-endian integer containing number of entries on page.
+**
+** Following this are nSlot 4 or 8 byte slots (depending on the key-size
+** in the first byte of the page header). The number of slots may be
+** determined based on the size of the page record and the key-size:
+**
+** nSlot = (nByte - 8) / key-size
*/
/*
@@ -206865,7 +238818,7 @@ static void sqlite3Fts5HashScanEntry(
**
** Each segment has a unique non-zero 16-bit id.
**
-** The rowid for each segment leaf is found by passing the segment id and
+** The rowid for each segment leaf is found by passing the segment id and
** the leaf page number to the FTS5_SEGMENT_ROWID macro. Leaves are numbered
** sequentially starting from 1.
*/
@@ -206883,11 +238836,7 @@ static void sqlite3Fts5HashScanEntry(
#define FTS5_SEGMENT_ROWID(segid, pgno) fts5_dri(segid, 0, 0, pgno)
#define FTS5_DLIDX_ROWID(segid, height, pgno) fts5_dri(segid, 1, height, pgno)
-
-/*
-** Maximum segments permitted in a single index
-*/
-#define FTS5_MAX_SEGMENT 2000
+#define FTS5_TOMBSTONE_ROWID(segid,ipg) fts5_dri(segid+(1<<16), 0, 0, ipg)
#ifdef SQLITE_DEBUG
static int sqlite3Fts5Corrupt() { return SQLITE_CORRUPT_VTAB; }
@@ -206914,6 +238863,9 @@ typedef struct Fts5SegWriter Fts5SegWriter;
typedef struct Fts5Structure Fts5Structure;
typedef struct Fts5StructureLevel Fts5StructureLevel;
typedef struct Fts5StructureSegment Fts5StructureSegment;
+typedef struct Fts5TokenDataIter Fts5TokenDataIter;
+typedef struct Fts5TokenDataMap Fts5TokenDataMap;
+typedef struct Fts5TombstoneArray Fts5TombstoneArray;
struct Fts5Data {
u8 *p; /* Pointer to buffer containing record */
@@ -206923,6 +238875,12 @@ struct Fts5Data {
/*
** One object per %_data table.
+**
+** nContentlessDelete:
+** The number of contentless delete operations since the most recent
+** call to fts5IndexFlush() or fts5IndexDiscardData(). This is tracked
+** so that extra auto-merge work can be done by fts5IndexFlush() to
+** account for the delete operations.
*/
struct Fts5Index {
Fts5Config *pConfig; /* Virtual table configuration */
@@ -206937,19 +238895,25 @@ struct Fts5Index {
int nPendingData; /* Current bytes of pending data */
i64 iWriteRowid; /* Rowid for current doc being written */
int bDelete; /* Current write is a delete */
+ int nContentlessDelete; /* Number of contentless delete ops */
+ int nPendingRow; /* Number of INSERT in hash table */
/* Error state. */
int rc; /* Current error code */
+ int flushRc;
/* State used by the fts5DataXXX() functions. */
sqlite3_blob *pReader; /* RO incr-blob open on %_data table */
sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */
sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */
sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */
- sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=? */
+ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */
sqlite3_stmt *pIdxSelect;
+ sqlite3_stmt *pIdxNextSelect;
int nRead; /* Total number of blocks read */
+ sqlite3_stmt *pDeleteFromIdx;
+
sqlite3_stmt *pDataVersion;
i64 iStructVersion; /* data_version when pStruct read */
Fts5Structure *pStruct; /* Current db structure (or NULL) */
@@ -206967,13 +238931,25 @@ struct Fts5DoclistIter {
/*
** The contents of the "structure" record for each index are represented
-** using an Fts5Structure record in memory. Which uses instances of the
+** using an Fts5Structure record in memory. Which uses instances of the
** other Fts5StructureXXX types as components.
+**
+** nOriginCntr:
+** This value is set to non-zero for structure records created for
+** contentlessdelete=1 tables only. In that case it represents the
+** origin value to apply to the next top-level segment created.
*/
struct Fts5StructureSegment {
int iSegid; /* Segment id */
int pgnoFirst; /* First leaf page number in segment */
int pgnoLast; /* Last leaf page number in segment */
+
+ /* contentlessdelete=1 tables only: */
+ u64 iOrigin1;
+ u64 iOrigin2;
+ int nPgTombstone; /* Number of tombstone hash table pages */
+ u64 nEntryTombstone; /* Number of tombstone entries that "count" */
+ u64 nEntry; /* Number of rows in this segment */
};
struct Fts5StructureLevel {
int nMerge; /* Number of segments in incr-merge */
@@ -206983,6 +238959,7 @@ struct Fts5StructureLevel {
struct Fts5Structure {
int nRef; /* Object reference count */
u64 nWriteCounter; /* Total leaves written to level 0 */
+ u64 nOriginCntr; /* Origin value for next top-level segment */
int nSegment; /* Total segments in this structure */
int nLevel; /* Number of levels in this index */
Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */
@@ -207040,11 +239017,8 @@ struct Fts5CResult {
** Current leaf page number within segment.
**
** iLeafOffset:
-** Byte offset within the current leaf that is the first byte of the
+** Byte offset within the current leaf that is the first byte of the
** position list data (one byte passed the position-list size field).
-** rowid field of the current entry. Usually this is the size field of the
-** position list data. The exception is if the rowid for the current entry
-** is the last thing on the leaf page.
**
** pLeaf:
** Buffer containing current leaf page data. Set to NULL at EOF.
@@ -207057,7 +239031,7 @@ struct Fts5CResult {
** Mask of FTS5_SEGITER_XXX values. Interpreted as follows:
**
** FTS5_SEGITER_ONETERM:
-** If set, set the iterator to point to EOF after the current doclist
+** If set, set the iterator to point to EOF after the current doclist
** has been exhausted. Do not proceed to the next term in the segment.
**
** FTS5_SEGITER_REVERSE:
@@ -207074,6 +239048,13 @@ struct Fts5CResult {
**
** iTermIdx:
** Index of current term on iTermLeafPgno.
+**
+** apTombstone/nTombstone:
+** These are used for contentless_delete=1 tables only. When the cursor
+** is first allocated, the apTombstone[] array is allocated so that it
+** is large enough for all tombstones hash pages associated with the
+** segment. The pages themselves are loaded lazily from the database as
+** they are required.
*/
struct Fts5SegIter {
Fts5StructureSegment *pSeg; /* Segment to iterate through */
@@ -207081,12 +239062,13 @@ struct Fts5SegIter {
int iLeafPgno; /* Current leaf page number */
Fts5Data *pLeaf; /* Current leaf data */
Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */
- int iLeafOffset; /* Byte offset within current leaf */
+ i64 iLeafOffset; /* Byte offset within current leaf */
+ Fts5TombstoneArray *pTombArray; /* Array of tombstone pages */
/* Next method */
void (*xNext)(Fts5Index*, Fts5SegIter*, int*);
- /* The page and offset from which the current term was read. The offset
+ /* The page and offset from which the current term was read. The offset
** is the offset of the first rowid in the current doclist. */
int iTermLeafPgno;
int iTermLeafOffset;
@@ -207109,7 +239091,16 @@ struct Fts5SegIter {
};
/*
-** Argument is a pointer to an Fts5Data structure that contains a
+** Array of tombstone pages. Reference counted.
+*/
+struct Fts5TombstoneArray {
+ int nRef; /* Number of pointers to this object */
+ int nTombstone;
+ Fts5Data *apTombstone[1]; /* Array of tombstone pages */
+};
+
+/*
+** Argument is a pointer to an Fts5Data structure that contains a
** leaf page.
*/
#define ASSERT_SZLEAF_OK(x) assert( \
@@ -207119,7 +239110,7 @@ struct Fts5SegIter {
#define FTS5_SEGITER_ONETERM 0x01
#define FTS5_SEGITER_REVERSE 0x02
-/*
+/*
** Argument is a pointer to an Fts5Data structure that contains a leaf
** page. This macro evaluates to true if the leaf contains no terms, or
** false if it contains at least one term.
@@ -207141,23 +239132,29 @@ struct Fts5SegIter {
** on empty segments.
**
** The results of comparing segments aSeg[N] and aSeg[N+1], where N is an
-** even number, is stored in aFirst[(nSeg+N)/2]. The "result" of the
+** even number, is stored in aFirst[(nSeg+N)/2]. The "result" of the
** comparison in this context is the index of the iterator that currently
** points to the smaller term/rowid combination. Iterators at EOF are
** considered to be greater than all other iterators.
**
** aFirst[1] contains the index in aSeg[] of the iterator that points to
-** the smallest key overall. aFirst[0] is unused.
+** the smallest key overall. aFirst[0] is unused.
**
** poslist:
** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
** There is no way to tell if this is populated or not.
+**
+** pColset:
+** If not NULL, points to an object containing a set of column indices.
+** Only matches that occur in one of these columns will be returned.
+** The Fts5Iter does not own the Fts5Colset object, and so it is not
+** freed when the iterator is closed - it is owned by the upper layer.
*/
struct Fts5Iter {
Fts5IndexIter base; /* Base class containing output vars */
+ Fts5TokenDataIter *pTokenDataIter;
Fts5Index *pIndex; /* Index that owns this iterator */
- Fts5Structure *pStruct; /* Database structure for this iterator */
Fts5Buffer poslist; /* Buffer containing current poslist */
Fts5Colset *pColset; /* Restrict matches to these columns */
@@ -207173,7 +239170,6 @@ struct Fts5Iter {
Fts5SegIter aSeg[1]; /* Array of segment iterators */
};
-
/*
** An instance of the following type is used to iterate through the contents
** of a doclist-index record.
@@ -207210,7 +239206,61 @@ static void fts5PutU16(u8 *aOut, u16 iVal){
static u16 fts5GetU16(const u8 *aIn){
return ((u16)aIn[0] << 8) + aIn[1];
-}
+}
+
+/*
+** The only argument points to a buffer at least 8 bytes in size. This
+** function interprets the first 8 bytes of the buffer as a 64-bit big-endian
+** unsigned integer and returns the result.
+*/
+static u64 fts5GetU64(u8 *a){
+ return ((u64)a[0] << 56)
+ + ((u64)a[1] << 48)
+ + ((u64)a[2] << 40)
+ + ((u64)a[3] << 32)
+ + ((u64)a[4] << 24)
+ + ((u64)a[5] << 16)
+ + ((u64)a[6] << 8)
+ + ((u64)a[7] << 0);
+}
+
+/*
+** The only argument points to a buffer at least 4 bytes in size. This
+** function interprets the first 4 bytes of the buffer as a 32-bit big-endian
+** unsigned integer and returns the result.
+*/
+static u32 fts5GetU32(const u8 *a){
+ return ((u32)a[0] << 24)
+ + ((u32)a[1] << 16)
+ + ((u32)a[2] << 8)
+ + ((u32)a[3] << 0);
+}
+
+/*
+** Write iVal, formated as a 64-bit big-endian unsigned integer, to the
+** buffer indicated by the first argument.
+*/
+static void fts5PutU64(u8 *a, u64 iVal){
+ a[0] = ((iVal >> 56) & 0xFF);
+ a[1] = ((iVal >> 48) & 0xFF);
+ a[2] = ((iVal >> 40) & 0xFF);
+ a[3] = ((iVal >> 32) & 0xFF);
+ a[4] = ((iVal >> 24) & 0xFF);
+ a[5] = ((iVal >> 16) & 0xFF);
+ a[6] = ((iVal >> 8) & 0xFF);
+ a[7] = ((iVal >> 0) & 0xFF);
+}
+
+/*
+** Write iVal, formated as a 32-bit big-endian unsigned integer, to the
+** buffer indicated by the first argument.
+*/
+static void fts5PutU32(u8 *a, u32 iVal){
+ a[0] = ((iVal >> 24) & 0xFF);
+ a[1] = ((iVal >> 16) & 0xFF);
+ a[2] = ((iVal >> 8) & 0xFF);
+ a[3] = ((iVal >> 0) & 0xFF);
+}
/*
** Allocate and return a buffer at least nByte bytes in size.
@@ -207218,7 +239268,7 @@ static u16 fts5GetU16(const u8 *aIn){
** If an OOM error is encountered, return NULL and set the error code in
** the Fts5Index handle passed as the first argument.
*/
-static void *fts5IdxMalloc(Fts5Index *p, int nByte){
+static void *fts5IdxMalloc(Fts5Index *p, sqlite3_int64 nByte){
return sqlite3Fts5MallocZero(&p->rc, nByte);
}
@@ -207251,8 +239301,11 @@ static int fts5BufferCompareBlob(
** res = *pLeft - *pRight
*/
static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){
- int nCmp = MIN(pLeft->n, pRight->n);
- int res = memcmp(pLeft->p, pRight->p, nCmp);
+ int nCmp, res;
+ nCmp = MIN(pLeft->n, pRight->n);
+ assert( nCmp<=0 || pLeft->p!=0 );
+ assert( nCmp<=0 || pRight->p!=0 );
+ res = fts5Memcmp(pLeft->p, pRight->p, nCmp);
return (res==0 ? (pLeft->n - pRight->n) : res);
}
@@ -207265,7 +239318,7 @@ static int fts5LeafFirstTermOff(Fts5Data *pLeaf){
/*
** Close the read-only blob handle, if it is open.
*/
-static void fts5CloseReader(Fts5Index *p){
+static void sqlite3Fts5IndexCloseReader(Fts5Index *p){
if( p->pReader ){
sqlite3_blob *pReader = p->pReader;
p->pReader = 0;
@@ -207276,7 +239329,7 @@ static void fts5CloseReader(Fts5Index *p){
/*
** Retrieve a record from the %_data table.
**
-** If an error occurs, NULL is returned and an error left in the
+** If an error occurs, NULL is returned and an error left in the
** Fts5Index object.
*/
static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
@@ -207294,16 +239347,16 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
assert( p->pReader==0 );
p->pReader = pBlob;
if( rc!=SQLITE_OK ){
- fts5CloseReader(p);
+ sqlite3Fts5IndexCloseReader(p);
}
if( rc==SQLITE_ABORT ) rc = SQLITE_OK;
}
- /* If the blob handle is not open at this point, open it and seek
+ /* If the blob handle is not open at this point, open it and seek
** to the requested entry. */
if( p->pReader==0 && rc==SQLITE_OK ){
Fts5Config *pConfig = p->pConfig;
- rc = sqlite3_blob_open(pConfig->db,
+ rc = sqlite3_blob_open(pConfig->db,
pConfig->zDb, p->zDataTbl, "block", iRowid, 0, &p->pReader
);
}
@@ -207311,15 +239364,15 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
/* If either of the sqlite3_blob_open() or sqlite3_blob_reopen() calls
** above returned SQLITE_ERROR, return SQLITE_CORRUPT_VTAB instead.
** All the reasons those functions might return SQLITE_ERROR - missing
- ** table, missing row, non-blob/text in block column - indicate
+ ** table, missing row, non-blob/text in block column - indicate
** backing store corruption. */
if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT;
if( rc==SQLITE_OK ){
u8 *aOut = 0; /* Read blob data into this buffer */
int nByte = sqlite3_blob_bytes(p->pReader);
- int nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING;
- pRet = (Fts5Data*)sqlite3_malloc(nAlloc);
+ sqlite3_int64 nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING;
+ pRet = (Fts5Data*)sqlite3_malloc64(nAlloc);
if( pRet ){
pRet->nn = nByte;
aOut = pRet->p = (u8*)&pRet[1];
@@ -207335,6 +239388,8 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
pRet = 0;
}else{
/* TODO1: Fix this */
+ pRet->p[nByte] = 0x00;
+ pRet->p[nByte+1] = 0x00;
pRet->szLeaf = fts5GetU16(&pRet->p[2]);
}
}
@@ -207346,6 +239401,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
return pRet;
}
+
/*
** Release a reference to data record returned by an earlier call to
** fts5DataRead().
@@ -207357,7 +239413,7 @@ static void fts5DataRelease(Fts5Data *pData){
static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
Fts5Data *pRet = fts5DataRead(p, iRowid);
if( pRet ){
- if( pRet->szLeaf>pRet->nn ){
+ if( pRet->nn<4 || pRet->szLeaf>pRet->nn ){
p->rc = FTS5_CORRUPT;
fts5DataRelease(pRet);
pRet = 0;
@@ -207374,7 +239430,8 @@ static int fts5IndexPrepareStmt(
if( p->rc==SQLITE_OK ){
if( zSql ){
p->rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1,
- SQLITE_PREPARE_PERSISTENT, ppStmt, 0);
+ SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB,
+ ppStmt, 0);
}else{
p->rc = SQLITE_NOMEM;
}
@@ -207393,7 +239450,7 @@ static void fts5DataWrite(Fts5Index *p, i64 iRowid, const u8 *pData, int nData){
if( p->pWriter==0 ){
Fts5Config *pConfig = p->pConfig;
fts5IndexPrepareStmt(p, &p->pWriter, sqlite3_mprintf(
- "REPLACE INTO '%q'.'%q_data'(id, block) VALUES(?,?)",
+ "REPLACE INTO '%q'.'%q_data'(id, block) VALUES(?,?)",
pConfig->zDb, pConfig->zName
));
if( p->rc ) return;
@@ -207415,23 +239472,12 @@ static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){
if( p->rc!=SQLITE_OK ) return;
if( p->pDeleter==0 ){
- int rc;
Fts5Config *pConfig = p->pConfig;
char *zSql = sqlite3_mprintf(
- "DELETE FROM '%q'.'%q_data' WHERE id>=? AND id<=?",
+ "DELETE FROM '%q'.'%q_data' WHERE id>=? AND id<=?",
pConfig->zDb, pConfig->zName
);
- if( zSql==0 ){
- rc = SQLITE_NOMEM;
- }else{
- rc = sqlite3_prepare_v3(pConfig->db, zSql, -1,
- SQLITE_PREPARE_PERSISTENT, &p->pDeleter, 0);
- sqlite3_free(zSql);
- }
- if( rc!=SQLITE_OK ){
- p->rc = rc;
- return;
- }
+ if( fts5IndexPrepareStmt(p, &p->pDeleter, zSql) ) return;
}
sqlite3_bind_int64(p->pDeleter, 1, iFirst);
@@ -207443,10 +239489,17 @@ static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){
/*
** Remove all records associated with segment iSegid.
*/
-static void fts5DataRemoveSegment(Fts5Index *p, int iSegid){
+static void fts5DataRemoveSegment(Fts5Index *p, Fts5StructureSegment *pSeg){
+ int iSegid = pSeg->iSegid;
i64 iFirst = FTS5_SEGMENT_ROWID(iSegid, 0);
i64 iLast = FTS5_SEGMENT_ROWID(iSegid+1, 0)-1;
fts5DataDelete(p, iFirst, iLast);
+
+ if( pSeg->nPgTombstone ){
+ i64 iTomb1 = FTS5_TOMBSTONE_ROWID(iSegid, 0);
+ i64 iTomb2 = FTS5_TOMBSTONE_ROWID(iSegid, pSeg->nPgTombstone-1);
+ fts5DataDelete(p, iTomb1, iTomb2);
+ }
if( p->pIdxDeleter==0 ){
Fts5Config *pConfig = p->pConfig;
fts5IndexPrepareStmt(p, &p->pIdxDeleter, sqlite3_mprintf(
@@ -207462,7 +239515,7 @@ static void fts5DataRemoveSegment(Fts5Index *p, int iSegid){
}
/*
-** Release a reference to an Fts5Structure object returned by an earlier
+** Release a reference to an Fts5Structure object returned by an earlier
** call to fts5StructureRead() or fts5StructureDecode().
*/
static void fts5StructureRelease(Fts5Structure *pStruct){
@@ -207480,6 +239533,58 @@ static void fts5StructureRef(Fts5Structure *pStruct){
pStruct->nRef++;
}
+static void *sqlite3Fts5StructureRef(Fts5Index *p){
+ fts5StructureRef(p->pStruct);
+ return (void*)p->pStruct;
+}
+static void sqlite3Fts5StructureRelease(void *p){
+ if( p ){
+ fts5StructureRelease((Fts5Structure*)p);
+ }
+}
+static int sqlite3Fts5StructureTest(Fts5Index *p, void *pStruct){
+ if( p->pStruct!=(Fts5Structure*)pStruct ){
+ return SQLITE_ABORT;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Ensure that structure object (*pp) is writable.
+**
+** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. If
+** an error occurs, (*pRc) is set to an SQLite error code before returning.
+*/
+static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){
+ Fts5Structure *p = *pp;
+ if( *pRc==SQLITE_OK && p->nRef>1 ){
+ i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel);
+ Fts5Structure *pNew;
+ pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte);
+ if( pNew ){
+ int i;
+ memcpy(pNew, p, nByte);
+ for(i=0; i<p->nLevel; i++) pNew->aLevel[i].aSeg = 0;
+ for(i=0; i<p->nLevel; i++){
+ Fts5StructureLevel *pLvl = &pNew->aLevel[i];
+ nByte = sizeof(Fts5StructureSegment) * pNew->aLevel[i].nSeg;
+ pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(pRc, nByte);
+ if( pLvl->aSeg==0 ){
+ for(i=0; i<p->nLevel; i++){
+ sqlite3_free(pNew->aLevel[i].aSeg);
+ }
+ sqlite3_free(pNew);
+ return;
+ }
+ memcpy(pLvl->aSeg, p->aLevel[i].aSeg, nByte);
+ }
+ p->nRef--;
+ pNew->nRef = 1;
+ }
+ *pp = pNew;
+ }
+}
+
/*
** Deserialize and return the structure record currently stored in serialized
** form within buffer pData/nData.
@@ -207503,17 +239608,30 @@ static int fts5StructureDecode(
int iLvl;
int nLevel = 0;
int nSegment = 0;
- int nByte; /* Bytes of space to allocate at pRet */
+ sqlite3_int64 nByte; /* Bytes of space to allocate at pRet */
Fts5Structure *pRet = 0; /* Structure object to return */
+ int bStructureV2 = 0; /* True for FTS5_STRUCTURE_V2 */
+ u64 nOriginCntr = 0; /* Largest origin value seen so far */
/* Grab the cookie value */
if( piCookie ) *piCookie = sqlite3Fts5Get32(pData);
i = 4;
+ /* Check if this is a V2 structure record. Set bStructureV2 if it is. */
+ if( 0==memcmp(&pData[i], FTS5_STRUCTURE_V2, 4) ){
+ i += 4;
+ bStructureV2 = 1;
+ }
+
/* Read the total number of levels and segments from the start of the
** structure record. */
i += fts5GetVarint32(&pData[i], nLevel);
i += fts5GetVarint32(&pData[i], nSegment);
+ if( nLevel>FTS5_MAX_SEGMENT || nLevel<0
+ || nSegment>FTS5_MAX_SEGMENT || nSegment<0
+ ){
+ return FTS5_CORRUPT;
+ }
nByte = (
sizeof(Fts5Structure) + /* Main structure */
sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */
@@ -207536,25 +239654,47 @@ static int fts5StructureDecode(
}else{
i += fts5GetVarint32(&pData[i], pLvl->nMerge);
i += fts5GetVarint32(&pData[i], nTotal);
- assert( nTotal>=pLvl->nMerge );
- pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc,
+ if( nTotal<pLvl->nMerge ) rc = FTS5_CORRUPT;
+ pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc,
nTotal * sizeof(Fts5StructureSegment)
);
+ nSegment -= nTotal;
}
if( rc==SQLITE_OK ){
pLvl->nSeg = nTotal;
for(iSeg=0; iSeg<nTotal; iSeg++){
+ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
if( i>=nData ){
rc = FTS5_CORRUPT;
break;
}
- i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].iSegid);
- i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoFirst);
- i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoLast);
+ assert( pSeg!=0 );
+ i += fts5GetVarint32(&pData[i], pSeg->iSegid);
+ i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst);
+ i += fts5GetVarint32(&pData[i], pSeg->pgnoLast);
+ if( bStructureV2 ){
+ i += fts5GetVarint(&pData[i], &pSeg->iOrigin1);
+ i += fts5GetVarint(&pData[i], &pSeg->iOrigin2);
+ i += fts5GetVarint32(&pData[i], pSeg->nPgTombstone);
+ i += fts5GetVarint(&pData[i], &pSeg->nEntryTombstone);
+ i += fts5GetVarint(&pData[i], &pSeg->nEntry);
+ nOriginCntr = MAX(nOriginCntr, pSeg->iOrigin2);
+ }
+ if( pSeg->pgnoLast<pSeg->pgnoFirst ){
+ rc = FTS5_CORRUPT;
+ break;
+ }
}
+ if( iLvl>0 && pLvl[-1].nMerge && nTotal==0 ) rc = FTS5_CORRUPT;
+ if( iLvl==nLevel-1 && pLvl->nMerge ) rc = FTS5_CORRUPT;
}
}
+ if( nSegment!=0 && rc==SQLITE_OK ) rc = FTS5_CORRUPT;
+ if( bStructureV2 ){
+ pRet->nOriginCntr = nOriginCntr+1;
+ }
+
if( rc!=SQLITE_OK ){
fts5StructureRelease(pRet);
pRet = 0;
@@ -207566,18 +239706,21 @@ static int fts5StructureDecode(
}
/*
-**
+** Add a level to the Fts5Structure.aLevel[] array of structure object
+** (*ppStruct).
*/
static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){
+ fts5StructureMakeWritable(pRc, ppStruct);
+ assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK );
if( *pRc==SQLITE_OK ){
Fts5Structure *pStruct = *ppStruct;
int nLevel = pStruct->nLevel;
- int nByte = (
+ sqlite3_int64 nByte = (
sizeof(Fts5Structure) + /* Main structure */
sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */
);
- pStruct = sqlite3_realloc(pStruct, nByte);
+ pStruct = sqlite3_realloc64(pStruct, nByte);
if( pStruct ){
memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel));
pStruct->nLevel++;
@@ -207593,19 +239736,19 @@ static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){
** segments.
*/
static void fts5StructureExtendLevel(
- int *pRc,
- Fts5Structure *pStruct,
- int iLvl,
- int nExtra,
+ int *pRc,
+ Fts5Structure *pStruct,
+ int iLvl,
+ int nExtra,
int bInsert
){
if( *pRc==SQLITE_OK ){
Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
Fts5StructureSegment *aNew;
- int nByte;
+ sqlite3_int64 nByte;
nByte = (pLvl->nSeg + nExtra) * sizeof(Fts5StructureSegment);
- aNew = sqlite3_realloc(pLvl->aSeg, nByte);
+ aNew = sqlite3_realloc64(pLvl->aSeg, nByte);
if( aNew ){
if( bInsert==0 ){
memset(&aNew[pLvl->nSeg], 0, sizeof(Fts5StructureSegment) * nExtra);
@@ -207632,7 +239775,7 @@ static Fts5Structure *fts5StructureReadUncached(Fts5Index *p){
/* TODO: Do we need this if the leaf-index is appended? Probably... */
memset(&pData->p[pData->nn], 0, FTS5_DATA_PADDING);
p->rc = fts5StructureDecode(pData->p, pData->nn, &iCookie, &pRet);
- if( p->rc==SQLITE_OK && pConfig->iCookie!=iCookie ){
+ if( p->rc==SQLITE_OK && (pConfig->pgsz==0 || pConfig->iCookie!=iCookie) ){
p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
}
fts5DataRelease(pData);
@@ -207650,7 +239793,7 @@ static i64 fts5IndexDataVersion(Fts5Index *p){
if( p->rc==SQLITE_OK ){
if( p->pDataVersion==0 ){
- p->rc = fts5IndexPrepareStmt(p, &p->pDataVersion,
+ p->rc = fts5IndexPrepareStmt(p, &p->pDataVersion,
sqlite3_mprintf("PRAGMA %Q.data_version", p->pConfig->zDb)
);
if( p->rc ) return 0;
@@ -207669,7 +239812,7 @@ static i64 fts5IndexDataVersion(Fts5Index *p){
** Read, deserialize and return the structure record.
**
** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array
-** are over-allocated as described for function fts5StructureDecode()
+** are over-allocated as described for function fts5StructureDecode()
** above.
**
** If an error occurs, NULL is returned and an error code left in the
@@ -207763,6 +239906,7 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){
Fts5Buffer buf; /* Buffer to serialize record into */
int iLvl; /* Used to iterate through levels */
int iCookie; /* Cookie value to store */
+ int nHdr = (pStruct->nOriginCntr>0 ? (4+4+9+9+9) : (4+9+9));
assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) );
memset(&buf, 0, sizeof(Fts5Buffer));
@@ -207771,9 +239915,12 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){
iCookie = p->pConfig->iCookie;
if( iCookie<0 ) iCookie = 0;
- if( 0==sqlite3Fts5BufferSize(&p->rc, &buf, 4+9+9+9) ){
+ if( 0==sqlite3Fts5BufferSize(&p->rc, &buf, nHdr) ){
sqlite3Fts5Put32(buf.p, iCookie);
buf.n = 4;
+ if( pStruct->nOriginCntr>0 ){
+ fts5BufferSafeAppendBlob(&buf, FTS5_STRUCTURE_V2, 4);
+ }
fts5BufferSafeAppendVarint(&buf, pStruct->nLevel);
fts5BufferSafeAppendVarint(&buf, pStruct->nSegment);
fts5BufferSafeAppendVarint(&buf, (i64)pStruct->nWriteCounter);
@@ -207787,9 +239934,17 @@ static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){
assert( pLvl->nMerge<=pLvl->nSeg );
for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){
- fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].iSegid);
- fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoFirst);
- fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoLast);
+ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->iSegid);
+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->pgnoFirst);
+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->pgnoLast);
+ if( pStruct->nOriginCntr>0 ){
+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->iOrigin1);
+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->iOrigin2);
+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->nPgTombstone);
+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->nEntryTombstone);
+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->nEntry);
+ }
}
}
@@ -207818,8 +239973,8 @@ static int fts5SegmentSize(Fts5StructureSegment *pSeg){
}
/*
-** Return a copy of index structure pStruct. Except, promote as many
-** segments as possible to level iPromote. If an OOM occurs, NULL is
+** Return a copy of index structure pStruct. Except, promote as many
+** segments as possible to level iPromote. If an OOM occurs, NULL is
** returned.
*/
static void fts5StructurePromoteTo(
@@ -207859,8 +240014,8 @@ static void fts5StructurePromoteTo(
**
** b) If the segment just written is larger than the newest segment on
** the next populated level, then that segment, and any other adjacent
-** segments that are also smaller than the one just written, are
-** promoted.
+** segments that are also smaller than the one just written, are
+** promoted.
**
** If one or more segments are promoted, the structure object is updated
** to reflect this.
@@ -207894,7 +240049,7 @@ static void fts5StructurePromote(
if( sz>szMax ) szMax = sz;
}
if( szMax>=szSeg ){
- /* Condition (a) is true. Promote the newest segment on level
+ /* Condition (a) is true. Promote the newest segment on level
** iLvl to level iTst. */
iPromote = iTst;
szPromote = szMax;
@@ -207913,7 +240068,7 @@ static void fts5StructurePromote(
/*
-** Advance the iterator passed as the only argument. If the end of the
+** Advance the iterator passed as the only argument. If the end of the
** doclist-index page is reached, return non-zero.
*/
static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){
@@ -207928,13 +240083,13 @@ static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){
}else{
int iOff;
for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){
- if( pData->p[iOff] ) break;
+ if( pData->p[iOff] ) break;
}
if( iOff<pData->nn ){
- i64 iVal;
+ u64 iVal;
pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1;
- iOff += fts5GetVarint(&pData->p[iOff], (u64*)&iVal);
+ iOff += fts5GetVarint(&pData->p[iOff], &iVal);
pLvl->iRowid += iVal;
pLvl->iOff = iOff;
}else{
@@ -207958,7 +240113,7 @@ static int fts5DlidxIterNextR(Fts5Index *p, Fts5DlidxIter *pIter, int iLvl){
if( pLvl[1].bEof==0 ){
fts5DataRelease(pLvl->pData);
memset(pLvl, 0, sizeof(Fts5DlidxLvl));
- pLvl->pData = fts5DataRead(p,
+ pLvl->pData = fts5DataRead(p,
FTS5_DLIDX_ROWID(pIter->iSegid, iLvl, pLvl[1].iLeafPgno)
);
if( pLvl->pData ) fts5DlidxLvlNext(pLvl);
@@ -207978,7 +240133,7 @@ static int fts5DlidxIterNext(Fts5Index *p, Fts5DlidxIter *pIter){
** points to the first rowid in the doclist-index.
**
** pData:
-** pointer to doclist-index record,
+** pointer to doclist-index record,
**
** When this function is called pIter->iLeafPgno is the page number the
** doclist is associated with (the one featuring the term).
@@ -208009,7 +240164,7 @@ static void fts5DlidxIterLast(Fts5Index *p, Fts5DlidxIter *pIter){
Fts5DlidxLvl *pChild = &pLvl[-1];
fts5DataRelease(pChild->pData);
memset(pChild, 0, sizeof(Fts5DlidxLvl));
- pChild->pData = fts5DataRead(p,
+ pChild->pData = fts5DataRead(p,
FTS5_DLIDX_ROWID(pIter->iSegid, i-1, pLvl->iLeafPgno)
);
}
@@ -208027,42 +240182,25 @@ static int fts5DlidxLvlPrev(Fts5DlidxLvl *pLvl){
pLvl->bEof = 1;
}else{
u8 *a = pLvl->pData->p;
- i64 iVal;
- int iLimit;
- int ii;
- int nZero = 0;
-
- /* Currently iOff points to the first byte of a varint. This block
- ** decrements iOff until it points to the first byte of the previous
- ** varint. Taking care not to read any memory locations that occur
- ** before the buffer in memory. */
- iLimit = (iOff>9 ? iOff-9 : 0);
- for(iOff--; iOff>iLimit; iOff--){
- if( (a[iOff-1] & 0x80)==0 ) break;
- }
-
- fts5GetVarint(&a[iOff], (u64*)&iVal);
- pLvl->iRowid -= iVal;
- pLvl->iLeafPgno--;
-
- /* Skip backwards past any 0x00 varints. */
- for(ii=iOff-1; ii>=pLvl->iFirstOff && a[ii]==0x00; ii--){
- nZero++;
- }
- if( ii>=pLvl->iFirstOff && (a[ii] & 0x80) ){
- /* The byte immediately before the last 0x00 byte has the 0x80 bit
- ** set. So the last 0x00 is only a varint 0 if there are 8 more 0x80
- ** bytes before a[ii]. */
- int bZero = 0; /* True if last 0x00 counts */
- if( (ii-8)>=pLvl->iFirstOff ){
- int j;
- for(j=1; j<=8 && (a[ii-j] & 0x80); j++);
- bZero = (j>8);
+
+ pLvl->iOff = 0;
+ fts5DlidxLvlNext(pLvl);
+ while( 1 ){
+ int nZero = 0;
+ int ii = pLvl->iOff;
+ u64 delta = 0;
+
+ while( a[ii]==0 ){
+ nZero++;
+ ii++;
}
- if( bZero==0 ) nZero--;
+ ii += sqlite3Fts5GetVarint(&a[ii], &delta);
+
+ if( ii>=iOff ) break;
+ pLvl->iLeafPgno += nZero+1;
+ pLvl->iRowid += delta;
+ pLvl->iOff = ii;
}
- pLvl->iLeafPgno -= nZero;
- pLvl->iOff = iOff - nZero;
}
return pLvl->bEof;
@@ -208078,7 +240216,7 @@ static int fts5DlidxIterPrevR(Fts5Index *p, Fts5DlidxIter *pIter, int iLvl){
if( pLvl[1].bEof==0 ){
fts5DataRelease(pLvl->pData);
memset(pLvl, 0, sizeof(Fts5DlidxLvl));
- pLvl->pData = fts5DataRead(p,
+ pLvl->pData = fts5DataRead(p,
FTS5_DLIDX_ROWID(pIter->iSegid, iLvl, pLvl[1].iLeafPgno)
);
if( pLvl->pData ){
@@ -208119,10 +240257,10 @@ static Fts5DlidxIter *fts5DlidxIterInit(
int bDone = 0;
for(i=0; p->rc==SQLITE_OK && bDone==0; i++){
- int nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl);
+ sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl);
Fts5DlidxIter *pNew;
- pNew = (Fts5DlidxIter*)sqlite3_realloc(pIter, nByte);
+ pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte);
if( pNew==0 ){
p->rc = SQLITE_NOMEM;
}else{
@@ -208177,7 +240315,7 @@ static void fts5SegIterNextPage(
pIter->pLeaf = pIter->pNextLeaf;
pIter->pNextLeaf = 0;
}else if( pIter->iLeafPgno<=pSeg->pgnoLast ){
- pIter->pLeaf = fts5LeafRead(p,
+ pIter->pLeaf = fts5LeafRead(p,
FTS5_SEGMENT_ROWID(pSeg->iSegid, pIter->iLeafPgno)
);
}else{
@@ -208221,7 +240359,7 @@ static int fts5GetPoslistSize(const u8 *p, int *pnSz, int *pbDel){
** Fts5SegIter.nPos
** Fts5SegIter.bDel
**
-** Leave Fts5SegIter.iLeafOffset pointing to the first byte of the
+** Leave Fts5SegIter.iLeafOffset pointing to the first byte of the
** position list content (if any).
*/
static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){
@@ -208255,10 +240393,10 @@ static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){
static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
u8 *a = pIter->pLeaf->p; /* Buffer to read data from */
- int iOff = pIter->iLeafOffset;
+ i64 iOff = pIter->iLeafOffset;
ASSERT_SZLEAF_OK(pIter->pLeaf);
- if( iOff>=pIter->pLeaf->szLeaf ){
+ while( iOff>=pIter->pLeaf->szLeaf ){
fts5SegIterNextPage(p, pIter);
if( pIter->pLeaf==0 ){
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
@@ -208272,7 +240410,7 @@ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
}
/*
-** Fts5SegIter.iLeafOffset currently points to the first byte of the
+** Fts5SegIter.iLeafOffset currently points to the first byte of the
** "nSuffix" field of a term. Function parameter nKeep contains the value
** of the "nPrefix" field (if there was one - it is passed 0 if this is
** the first term in the segment).
@@ -208283,21 +240421,22 @@ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
** Fts5SegIter.rowid
**
** accordingly and leaves (Fts5SegIter.iLeafOffset) set to the content of
-** the first position list. The position list belonging to document
+** the first position list. The position list belonging to document
** (Fts5SegIter.iRowid).
*/
static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){
u8 *a = pIter->pLeaf->p; /* Buffer to read data from */
- int iOff = pIter->iLeafOffset; /* Offset to read at */
+ i64 iOff = pIter->iLeafOffset; /* Offset to read at */
int nNew; /* Bytes of new data */
iOff += fts5GetVarint32(&a[iOff], nNew);
- if( iOff+nNew>pIter->pLeaf->nn ){
+ if( iOff+nNew>pIter->pLeaf->szLeaf || nKeep>pIter->term.n || nNew==0 ){
p->rc = FTS5_CORRUPT;
return;
}
pIter->term.n = nKeep;
fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff]);
+ assert( pIter->term.n<=pIter->term.nSpace );
iOff += nNew;
pIter->iTermLeafOffset = iOff;
pIter->iTermLeafPgno = pIter->iLeafPgno;
@@ -208329,11 +240468,30 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){
}
/*
+** Allocate a tombstone hash page array object (pIter->pTombArray) for
+** the iterator passed as the second argument. If an OOM error occurs,
+** leave an error in the Fts5Index object.
+*/
+static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
+ const int nTomb = pIter->pSeg->nPgTombstone;
+ if( nTomb>0 ){
+ int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray);
+ Fts5TombstoneArray *pNew;
+ pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
+ if( pNew ){
+ pNew->nTombstone = nTomb;
+ pNew->nRef = 1;
+ pIter->pTombArray = pNew;
+ }
+ }
+}
+
+/*
** Initialize the iterator object pIter to iterate through the entries in
-** segment pSeg. The iterator is left pointing to the first entry when
+** segment pSeg. The iterator is left pointing to the first entry when
** this function returns.
**
-** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
** an error has already occurred when this function is called, it is a no-op.
*/
static void fts5SegIterInit(
@@ -208356,16 +240514,20 @@ static void fts5SegIterInit(
fts5SegIterSetNext(p, pIter);
pIter->pSeg = pSeg;
pIter->iLeafPgno = pSeg->pgnoFirst-1;
- fts5SegIterNextPage(p, pIter);
+ do {
+ fts5SegIterNextPage(p, pIter);
+ }while( p->rc==SQLITE_OK && pIter->pLeaf && pIter->pLeaf->nn==4 );
}
- if( p->rc==SQLITE_OK ){
+ if( p->rc==SQLITE_OK && pIter->pLeaf ){
pIter->iLeafOffset = 4;
+ assert( pIter->pLeaf!=0 );
assert_nc( pIter->pLeaf->nn>4 );
- assert( fts5LeafFirstTermOff(pIter->pLeaf)==4 );
+ assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 );
pIter->iPgidxOff = pIter->pLeaf->szLeaf+1;
fts5SegIterLoadTerm(p, pIter, 0);
fts5SegIterLoadNPos(p, pIter);
+ fts5SegIterAllocTombstone(p, pIter);
}
}
@@ -208378,8 +240540,8 @@ static void fts5SegIterInit(
** the position-list size field for the first relevant rowid on the page.
** Fts5SegIter.rowid is set, but nPos and bDel are not.
**
-** This function advances the iterator so that it points to the last
-** relevant rowid on the page and, if necessary, initializes the
+** This function advances the iterator so that it points to the last
+** relevant rowid on the page and, if necessary, initializes the
** aRowidOffset[] and iRowidOffset variables. At this point the iterator
** is in its regular state - Fts5SegIter.iLeafOffset points to the first
** byte of the position list content associated with said rowid.
@@ -208397,7 +240559,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
ASSERT_SZLEAF_OK(pIter->pLeaf);
while( 1 ){
- i64 iDelta = 0;
+ u64 iDelta = 0;
if( eDetail==FTS5_DETAIL_NONE ){
/* todo */
@@ -208412,13 +240574,13 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
i += nPos;
}
if( i>=n ) break;
- i += fts5GetVarint(&a[i], (u64*)&iDelta);
+ i += fts5GetVarint(&a[i], &iDelta);
pIter->iRowid += iDelta;
/* If necessary, grow the pIter->aRowidOffset[] array. */
if( iRowidOffset>=pIter->nRowidOffset ){
int nNew = pIter->nRowidOffset + 8;
- int *aNew = (int*)sqlite3_realloc(pIter->aRowidOffset, nNew*sizeof(int));
+ int *aNew = (int*)sqlite3_realloc64(pIter->aRowidOffset,nNew*sizeof(int));
if( aNew==0 ){
p->rc = SQLITE_NOMEM;
break;
@@ -208463,8 +240625,12 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){
int iRowidOff;
iRowidOff = fts5LeafFirstRowidOff(pNew);
if( iRowidOff ){
- pIter->pLeaf = pNew;
- pIter->iLeafOffset = iRowidOff;
+ if( iRowidOff>=pNew->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ pIter->pLeaf = pNew;
+ pIter->iLeafOffset = iRowidOff;
+ }
}
}
@@ -208511,7 +240677,7 @@ static void fts5SegIterNext_Reverse(
if( pIter->iRowidOffset>0 ){
u8 *a = pIter->pLeaf->p;
int iOff;
- i64 iDelta;
+ u64 iDelta;
pIter->iRowidOffset--;
pIter->iLeafOffset = pIter->aRowidOffset[pIter->iRowidOffset];
@@ -208520,7 +240686,7 @@ static void fts5SegIterNext_Reverse(
if( p->pConfig->eDetail!=FTS5_DETAIL_NONE ){
iOff += pIter->nPos;
}
- fts5GetVarint(&a[iOff], (u64*)&iDelta);
+ fts5GetVarint(&a[iOff], &iDelta);
pIter->iRowid -= iDelta;
}else{
fts5SegIterReverseNewPage(p, pIter);
@@ -208548,7 +240714,7 @@ static void fts5SegIterNext_None(
iOff = pIter->iLeafOffset;
/* Next entry is on the next page */
- if( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){
+ while( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){
fts5SegIterNextPage(p, pIter);
if( p->rc || pIter->pLeaf==0 ) return;
pIter->iRowid = 0;
@@ -208572,15 +240738,16 @@ static void fts5SegIterNext_None(
}else{
const u8 *pList = 0;
const char *zTerm = 0;
+ int nTerm = 0;
int nList;
sqlite3Fts5HashScanNext(p->pHash);
- sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList);
if( pList==0 ) goto next_none_eof;
pIter->pLeaf->p = (u8*)pList;
pIter->pLeaf->nn = nList;
pIter->pLeaf->szLeaf = nList;
pIter->iEndofDoclist = nList;
- sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm);
+ sqlite3Fts5BufferSet(&p->rc,&pIter->term, nTerm, (u8*)zTerm);
pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
}
@@ -208599,10 +240766,10 @@ static void fts5SegIterNext_None(
/*
-** Advance iterator pIter to the next entry.
+** Advance iterator pIter to the next entry.
**
-** If an error occurs, Fts5Index.rc is set to an appropriate error code. It
-** is not considered an error if the iterator reaches EOF. If an error has
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. It
+** is not considered an error if the iterator reaches EOF. If an error has
** already occurred when this function is called, it is a no-op.
*/
static void fts5SegIterNext(
@@ -208646,11 +240813,12 @@ static void fts5SegIterNext(
}else if( pIter->pSeg==0 ){
const u8 *pList = 0;
const char *zTerm = 0;
+ int nTerm = 0;
int nList = 0;
assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm );
if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){
sqlite3Fts5HashScanNext(p->pHash);
- sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList);
}
if( pList==0 ){
fts5DataRelease(pIter->pLeaf);
@@ -208660,8 +240828,7 @@ static void fts5SegIterNext(
pIter->pLeaf->nn = nList;
pIter->pLeaf->szLeaf = nList;
pIter->iEndofDoclist = nList+1;
- sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm),
- (u8*)zTerm);
+ sqlite3Fts5BufferSet(&p->rc, &pIter->term, nTerm, (u8*)zTerm);
pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
*pbNewTerm = 1;
}
@@ -208713,14 +240880,9 @@ static void fts5SegIterNext(
}else{
/* The following could be done by calling fts5SegIterLoadNPos(). But
** this block is particularly performance critical, so equivalent
- ** code is inlined.
- **
- ** Later: Switched back to fts5SegIterLoadNPos() because it supports
- ** detail=none mode. Not ideal.
- */
+ ** code is inlined. */
int nSz;
- assert( p->rc==SQLITE_OK );
- assert( pIter->iLeafOffset<=pIter->pLeaf->nn );
+ assert_nc( pIter->iLeafOffset<=pIter->pLeaf->nn );
fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz);
pIter->bDel = (nSz & 0x0001);
pIter->nPos = nSz>>1;
@@ -208746,10 +240908,10 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
Fts5Data *pLast = 0;
int pgnoLast = 0;
- if( pDlidx ){
+ if( pDlidx && p->pConfig->iVersion==FTS5_CURRENT_VERSION ){
int iSegid = pIter->pSeg->iSegid;
pgnoLast = fts5DlidxIterPgno(pDlidx);
- pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast));
+ pLast = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast));
}else{
Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */
@@ -208776,7 +240938,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
** forward to find the page containing the last rowid. */
for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){
i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno);
- Fts5Data *pNew = fts5DataRead(p, iAbs);
+ Fts5Data *pNew = fts5LeafRead(p, iAbs);
if( pNew ){
int iRowid, bTermless;
iRowid = fts5LeafFirstRowidOff(pNew);
@@ -208793,7 +240955,7 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
}
/* If pLast is NULL at this point, then the last rowid for this doclist
- ** lies on the page currently indicated by the iterator. In this case
+ ** lies on the page currently indicated by the iterator. In this case
** pIter->iLeafOffset is already set to point to the position-list size
** field associated with the first relevant rowid on the page.
**
@@ -208807,6 +240969,10 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
pIter->pLeaf = pLast;
pIter->iLeafPgno = pgnoLast;
iOff = fts5LeafFirstRowidOff(pLast);
+ if( iOff>pLast->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ return;
+ }
iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
pIter->iLeafOffset = iOff;
@@ -208815,7 +240981,6 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
}else{
pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
}
-
}
fts5SegIterReverseInitPage(p, pIter);
@@ -208823,8 +240988,8 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
/*
** Iterator pIter currently points to the first rowid of a doclist.
-** There is a doclist-index associated with the final term on the current
-** page. If the current term is the last term on the page, load the
+** There is a doclist-index associated with the final term on the current
+** page. If the current term is the last term on the page, load the
** doclist-index from disk and initialize an iterator at (pIter->pDlidx).
*/
static void fts5SegIterLoadDlidx(Fts5Index *p, Fts5SegIter *pIter){
@@ -208838,8 +241003,8 @@ static void fts5SegIterLoadDlidx(Fts5Index *p, Fts5SegIter *pIter){
/* Check if the current doclist ends on this page. If it does, return
** early without loading the doclist-index (as it belongs to a different
** term. */
- if( pIter->iTermLeafPgno==pIter->iLeafPgno
- && pIter->iEndofDoclist<pLeaf->szLeaf
+ if( pIter->iTermLeafPgno==pIter->iLeafPgno
+ && pIter->iEndofDoclist<pLeaf->szLeaf
){
return;
}
@@ -208867,21 +241032,20 @@ static void fts5LeafSeek(
Fts5SegIter *pIter, /* Iterator to seek */
const u8 *pTerm, int nTerm /* Term to search for */
){
- int iOff;
+ u32 iOff;
const u8 *a = pIter->pLeaf->p;
- int szLeaf = pIter->pLeaf->szLeaf;
- int n = pIter->pLeaf->nn;
+ u32 n = (u32)pIter->pLeaf->nn;
- int nMatch = 0;
- int nKeep = 0;
- int nNew = 0;
- int iTermOff;
- int iPgidx; /* Current offset in pgidx */
+ u32 nMatch = 0;
+ u32 nKeep = 0;
+ u32 nNew = 0;
+ u32 iTermOff;
+ u32 iPgidx; /* Current offset in pgidx */
int bEndOfPage = 0;
assert( p->rc==SQLITE_OK );
- iPgidx = szLeaf;
+ iPgidx = (u32)pIter->pLeaf->szLeaf;
iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff);
iOff = iTermOff;
if( iOff>n ){
@@ -208899,15 +241063,15 @@ static void fts5LeafSeek(
assert( nKeep>=nMatch );
if( nKeep==nMatch ){
- int nCmp;
- int i;
- nCmp = MIN(nNew, nTerm-nMatch);
+ u32 nCmp;
+ u32 i;
+ nCmp = (u32)MIN(nNew, nTerm-nMatch);
for(i=0; i<nCmp; i++){
if( a[iOff+i]!=pTerm[nMatch+i] ) break;
}
nMatch += i;
- if( nTerm==nMatch ){
+ if( (u32)nTerm==nMatch ){
if( i==nNew ){
goto search_success;
}else{
@@ -208947,14 +241111,15 @@ static void fts5LeafSeek(
if( pIter->pLeaf==0 ) return;
a = pIter->pLeaf->p;
if( fts5LeafIsTermless(pIter->pLeaf)==0 ){
- iPgidx = pIter->pLeaf->szLeaf;
+ iPgidx = (u32)pIter->pLeaf->szLeaf;
iPgidx += fts5GetVarint32(&pIter->pLeaf->p[iPgidx], iOff);
- if( iOff<4 || iOff>=pIter->pLeaf->szLeaf ){
+ if( iOff<4 || (i64)iOff>=pIter->pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
+ return;
}else{
nKeep = 0;
iTermOff = iOff;
- n = pIter->pLeaf->nn;
+ n = (u32)pIter->pLeaf->nn;
iOff += fts5GetVarint32(&a[iOff], nNew);
break;
}
@@ -208963,7 +241128,10 @@ static void fts5LeafSeek(
}
search_success:
-
+ if( (i64)iOff+nNew>n || nNew<1 ){
+ p->rc = FTS5_CORRUPT;
+ return;
+ }
pIter->iLeafOffset = iOff + nNew;
pIter->iTermLeafOffset = pIter->iLeafOffset;
pIter->iTermLeafPgno = pIter->iLeafPgno;
@@ -209000,7 +241168,7 @@ static sqlite3_stmt *fts5IdxSelectStmt(Fts5Index *p){
** Initialize the object pIter to point to term pTerm/nTerm within segment
** pSeg. If there is no such term in the index, the iterator is set to EOF.
**
-** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
** an error has already occurred when this function is called, it is a no-op.
*/
static void fts5SegIterSeekInit(
@@ -209046,7 +241214,7 @@ static void fts5SegIterSeekInit(
fts5LeafSeek(p, bGe, pIter, pTerm, nTerm);
}
- if( p->rc==SQLITE_OK && bGe==0 ){
+ if( p->rc==SQLITE_OK && (bGe==0 || (flags & FTS5INDEX_QUERY_SCANONETERM)) ){
pIter->flags |= FTS5_SEGITER_ONETERM;
if( pIter->pLeaf ){
if( flags & FTS5INDEX_QUERY_DESC ){
@@ -209062,6 +241230,9 @@ static void fts5SegIterSeekInit(
}
fts5SegIterSetNext(p, pIter);
+ if( 0==(flags & FTS5INDEX_QUERY_SCANONETERM) ){
+ fts5SegIterAllocTombstone(p, pIter);
+ }
/* Either:
**
@@ -209071,19 +241242,92 @@ static void fts5SegIterSeekInit(
** 4) the FTS5INDEX_QUERY_SCAN flag was set and the iterator points
** to an entry with a term greater than or equal to (pTerm/nTerm).
*/
- assert( p->rc!=SQLITE_OK /* 1 */
+ assert_nc( p->rc!=SQLITE_OK /* 1 */
|| pIter->pLeaf==0 /* 2 */
|| fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)==0 /* 3 */
|| (bGe && fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0) /* 4 */
);
}
+
+/*
+** SQL used by fts5SegIterNextInit() to find the page to open.
+*/
+static sqlite3_stmt *fts5IdxNextStmt(Fts5Index *p){
+ if( p->pIdxNextSelect==0 ){
+ Fts5Config *pConfig = p->pConfig;
+ fts5IndexPrepareStmt(p, &p->pIdxNextSelect, sqlite3_mprintf(
+ "SELECT pgno FROM '%q'.'%q_idx' WHERE "
+ "segid=? AND term>? ORDER BY term ASC LIMIT 1",
+ pConfig->zDb, pConfig->zName
+ ));
+
+ }
+ return p->pIdxNextSelect;
+}
+
+/*
+** This is similar to fts5SegIterSeekInit(), except that it initializes
+** the segment iterator to point to the first term following the page
+** with pToken/nToken on it.
+*/
+static void fts5SegIterNextInit(
+ Fts5Index *p,
+ const char *pTerm, int nTerm,
+ Fts5StructureSegment *pSeg, /* Description of segment */
+ Fts5SegIter *pIter /* Object to populate */
+){
+ int iPg = -1; /* Page of segment to open */
+ int bDlidx = 0;
+ sqlite3_stmt *pSel = 0; /* SELECT to find iPg */
+
+ pSel = fts5IdxNextStmt(p);
+ if( pSel ){
+ assert( p->rc==SQLITE_OK );
+ sqlite3_bind_int(pSel, 1, pSeg->iSegid);
+ sqlite3_bind_blob(pSel, 2, pTerm, nTerm, SQLITE_STATIC);
+
+ if( sqlite3_step(pSel)==SQLITE_ROW ){
+ i64 val = sqlite3_column_int64(pSel, 0);
+ iPg = (int)(val>>1);
+ bDlidx = (val & 0x0001);
+ }
+ p->rc = sqlite3_reset(pSel);
+ sqlite3_bind_null(pSel, 2);
+ if( p->rc ) return;
+ }
+
+ memset(pIter, 0, sizeof(*pIter));
+ pIter->pSeg = pSeg;
+ pIter->flags |= FTS5_SEGITER_ONETERM;
+ if( iPg>=0 ){
+ pIter->iLeafPgno = iPg - 1;
+ fts5SegIterNextPage(p, pIter);
+ fts5SegIterSetNext(p, pIter);
+ }
+ if( pIter->pLeaf ){
+ const u8 *a = pIter->pLeaf->p;
+ int iTermOff = 0;
+
+ pIter->iPgidxOff = pIter->pLeaf->szLeaf;
+ pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff);
+ pIter->iLeafOffset = iTermOff;
+ fts5SegIterLoadTerm(p, pIter, 0);
+ fts5SegIterLoadNPos(p, pIter);
+ if( bDlidx ) fts5SegIterLoadDlidx(p, pIter);
+
+ assert( p->rc!=SQLITE_OK ||
+ fts5BufferCompareBlob(&pIter->term, (const u8*)pTerm, nTerm)>0
+ );
+ }
+}
+
/*
** Initialize the object pIter to point to term pTerm/nTerm within the
-** in-memory hash table. If there is no such term in the hash-table, the
+** in-memory hash table. If there is no such term in the hash-table, the
** iterator is set to EOF.
**
-** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
** an error has already occurred when this function is called, it is a no-op.
*/
static void fts5SegIterHashInit(
@@ -209092,31 +241336,47 @@ static void fts5SegIterHashInit(
int flags, /* Mask of FTS5INDEX_XXX flags */
Fts5SegIter *pIter /* Object to populate */
){
- const u8 *pList = 0;
int nList = 0;
const u8 *z = 0;
int n = 0;
+ Fts5Data *pLeaf = 0;
assert( p->pHash );
assert( p->rc==SQLITE_OK );
if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){
+ const u8 *pList = 0;
+
p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm);
- sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList);
- n = (z ? (int)strlen((const char*)z) : 0);
+ sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &n, &pList, &nList);
+ if( pList ){
+ pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
+ if( pLeaf ){
+ pLeaf->p = (u8*)pList;
+ }
+ }
+
+ /* The call to sqlite3Fts5HashScanInit() causes the hash table to
+ ** fill the size field of all existing position lists. This means they
+ ** can no longer be appended to. Since the only scenario in which they
+ ** can be appended to is if the previous operation on this table was
+ ** a DELETE, by clearing the Fts5Index.bDelete flag we can avoid this
+ ** possibility altogether. */
+ p->bDelete = 0;
}else{
- pIter->flags |= FTS5_SEGITER_ONETERM;
- sqlite3Fts5HashQuery(p->pHash, (const char*)pTerm, nTerm, &pList, &nList);
+ p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data),
+ (const char*)pTerm, nTerm, (void**)&pLeaf, &nList
+ );
+ if( pLeaf ){
+ pLeaf->p = (u8*)&pLeaf[1];
+ }
z = pTerm;
n = nTerm;
+ pIter->flags |= FTS5_SEGITER_ONETERM;
}
- if( pList ){
- Fts5Data *pLeaf;
+ if( pLeaf ){
sqlite3Fts5BufferSet(&p->rc, &pIter->term, n, z);
- pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
- if( pLeaf==0 ) return;
- pLeaf->p = (u8*)pList;
pLeaf->nn = pLeaf->szLeaf = nList;
pIter->pLeaf = pLeaf;
pIter->iLeafOffset = fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid);
@@ -209134,12 +241394,44 @@ static void fts5SegIterHashInit(
}
/*
+** Array ap[] contains n elements. Release each of these elements using
+** fts5DataRelease(). Then free the array itself using sqlite3_free().
+*/
+static void fts5IndexFreeArray(Fts5Data **ap, int n){
+ if( ap ){
+ int ii;
+ for(ii=0; ii<n; ii++){
+ fts5DataRelease(ap[ii]);
+ }
+ sqlite3_free(ap);
+ }
+}
+
+/*
+** Decrement the ref-count of the object passed as the only argument. If it
+** reaches 0, free it and its contents.
+*/
+static void fts5TombstoneArrayDelete(Fts5TombstoneArray *p){
+ if( p ){
+ p->nRef--;
+ if( p->nRef<=0 ){
+ int ii;
+ for(ii=0; ii<p->nTombstone; ii++){
+ fts5DataRelease(p->apTombstone[ii]);
+ }
+ sqlite3_free(p);
+ }
+ }
+}
+
+/*
** Zero the iterator passed as the only argument.
*/
static void fts5SegIterClear(Fts5SegIter *pIter){
fts5BufferFree(&pIter->term);
fts5DataRelease(pIter->pLeaf);
fts5DataRelease(pIter->pNextLeaf);
+ fts5TombstoneArrayDelete(pIter->pTombArray);
fts5DlidxIterFree(pIter->pDlidx);
sqlite3_free(pIter->aRowidOffset);
memset(pIter, 0, sizeof(Fts5SegIter));
@@ -209154,7 +241446,7 @@ static void fts5SegIterClear(Fts5SegIter *pIter){
** two iterators.
*/
static void fts5AssertComparisonResult(
- Fts5Iter *pIter,
+ Fts5Iter *pIter,
Fts5SegIter *p1,
Fts5SegIter *p2,
Fts5CResult *pRes
@@ -209169,7 +241461,7 @@ static void fts5AssertComparisonResult(
assert( pRes->iFirst==i1 );
}else{
int nMin = MIN(p1->term.n, p2->term.n);
- int res = memcmp(p1->term.p, p2->term.p, nMin);
+ int res = fts5Memcmp(p1->term.p, p2->term.p, nMin);
if( res==0 ) res = p1->term.n - p2->term.n;
if( res==0 ){
@@ -209191,7 +241483,7 @@ static void fts5AssertComparisonResult(
/*
** This function is a no-op unless SQLITE_DEBUG is defined when this module
-** is compiled. In that case, this function is essentially an assert()
+** is compiled. In that case, this function is essentially an assert()
** statement used to verify that the contents of the pIter->aFirst[] array
** are correct.
*/
@@ -209205,9 +241497,9 @@ static void fts5AssertMultiIterSetup(Fts5Index *p, Fts5Iter *pIter){
/* Check that pIter->iSwitchRowid is set correctly. */
for(i=0; i<pIter->nSeg; i++){
Fts5SegIter *p1 = &pIter->aSeg[i];
- assert( p1==pFirst
- || p1->pLeaf==0
- || fts5BufferCompare(&pFirst->term, &p1->term)
+ assert( p1==pFirst
+ || p1->pLeaf==0
+ || fts5BufferCompare(&pFirst->term, &p1->term)
|| p1->iRowid==pIter->iSwitchRowid
|| (p1->iRowid<pIter->iSwitchRowid)==pIter->bRev
);
@@ -209237,7 +241529,7 @@ static void fts5AssertMultiIterSetup(Fts5Index *p, Fts5Iter *pIter){
**
** If the returned value is non-zero, then it is the index of an entry
** in the pIter->aSeg[] array that is (a) not at EOF, and (b) pointing
-** to a key that is a duplicate of another, higher priority,
+** to a key that is a duplicate of another, higher priority,
** segment-iterator in the pSeg->aSeg[] array.
*/
static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
@@ -209269,11 +241561,10 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
}else{
int res = fts5BufferCompare(&p1->term, &p2->term);
if( res==0 ){
- assert( i2>i1 );
- assert( i2!=0 );
+ assert_nc( i2>i1 );
+ assert_nc( i2!=0 );
pRes->bTermEq = 1;
if( p1->iRowid==p2->iRowid ){
- p1->bDel = p2->bDel;
return i2;
}
res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1;
@@ -209292,7 +241583,8 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
/*
** Move the seg-iter so that it points to the first rowid on page iLeafPgno.
-** It is an error if leaf iLeafPgno does not exist or contains no rowids.
+** It is an error if leaf iLeafPgno does not exist. Unless the db is
+** a 'secure-delete' db, if it contains no rowids then this is also an error.
*/
static void fts5SegIterGotoPage(
Fts5Index *p, /* FTS5 backend object */
@@ -209307,28 +241599,30 @@ static void fts5SegIterGotoPage(
fts5DataRelease(pIter->pNextLeaf);
pIter->pNextLeaf = 0;
pIter->iLeafPgno = iLeafPgno-1;
- fts5SegIterNextPage(p, pIter);
- assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno );
- if( p->rc==SQLITE_OK ){
+ while( p->rc==SQLITE_OK ){
int iOff;
- u8 *a = pIter->pLeaf->p;
- int n = pIter->pLeaf->szLeaf;
-
+ fts5SegIterNextPage(p, pIter);
+ if( pIter->pLeaf==0 ) break;
iOff = fts5LeafFirstRowidOff(pIter->pLeaf);
- if( iOff<4 || iOff>=n ){
- p->rc = FTS5_CORRUPT;
- }else{
- iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid);
- pIter->iLeafOffset = iOff;
- fts5SegIterLoadNPos(p, pIter);
+ if( iOff>0 ){
+ u8 *a = pIter->pLeaf->p;
+ int n = pIter->pLeaf->szLeaf;
+ if( iOff<4 || iOff>=n ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid);
+ pIter->iLeafOffset = iOff;
+ fts5SegIterLoadNPos(p, pIter);
+ }
+ break;
}
}
}
}
/*
-** Advance the iterator passed as the second argument until it is at or
+** Advance the iterator passed as the second argument until it is at or
** past rowid iFrom. Regardless of the value of iFrom, the iterator is
** always advanced at least once.
*/
@@ -209382,7 +241676,6 @@ static void fts5SegIterNextFrom(
}while( p->rc==SQLITE_OK );
}
-
/*
** Free the iterator object passed as the second argument.
*/
@@ -209392,7 +241685,6 @@ static void fts5MultiIterFree(Fts5Iter *pIter){
for(i=0; i<pIter->nSeg; i++){
fts5SegIterClear(&pIter->aSeg[i]);
}
- fts5StructureRelease(pIter->pStruct);
fts5BufferFree(&pIter->poslist);
sqlite3_free(pIter);
}
@@ -209425,7 +241717,7 @@ static void fts5MultiIterAdvanced(
** If non-zero is returned, the caller should call fts5MultiIterAdvanced()
** on the iterator instead. That function does the same as this one, except
** that it deals with more complicated cases as well.
-*/
+*/
static int fts5MultiIterAdvanceRowid(
Fts5Iter *pIter, /* Iterator to update aFirst[] array for */
int iChanged, /* Index of sub-iterator just advanced */
@@ -209476,14 +241768,93 @@ static void fts5MultiIterSetEof(Fts5Iter *pIter){
}
/*
-** Move the iterator to the next entry.
+** The argument to this macro must be an Fts5Data structure containing a
+** tombstone hash page. This macro returns the key-size of the hash-page.
+*/
+#define TOMBSTONE_KEYSIZE(pPg) (pPg->p[0]==4 ? 4 : 8)
+
+#define TOMBSTONE_NSLOT(pPg) \
+ ((pPg->nn > 16) ? ((pPg->nn-8) / TOMBSTONE_KEYSIZE(pPg)) : 1)
+
+/*
+** Query a single tombstone hash table for rowid iRowid. Return true if
+** it is found or false otherwise. The tombstone hash table is one of
+** nHashTable tables.
+*/
+static int fts5IndexTombstoneQuery(
+ Fts5Data *pHash, /* Hash table page to query */
+ int nHashTable, /* Number of pages attached to segment */
+ u64 iRowid /* Rowid to query hash for */
+){
+ const int szKey = TOMBSTONE_KEYSIZE(pHash);
+ const int nSlot = TOMBSTONE_NSLOT(pHash);
+ int iSlot = (iRowid / nHashTable) % nSlot;
+ int nCollide = nSlot;
+
+ if( iRowid==0 ){
+ return pHash->p[1];
+ }else if( szKey==4 ){
+ u32 *aSlot = (u32*)&pHash->p[8];
+ while( aSlot[iSlot] ){
+ if( fts5GetU32((u8*)&aSlot[iSlot])==iRowid ) return 1;
+ if( nCollide--==0 ) break;
+ iSlot = (iSlot+1)%nSlot;
+ }
+ }else{
+ u64 *aSlot = (u64*)&pHash->p[8];
+ while( aSlot[iSlot] ){
+ if( fts5GetU64((u8*)&aSlot[iSlot])==iRowid ) return 1;
+ if( nCollide--==0 ) break;
+ iSlot = (iSlot+1)%nSlot;
+ }
+ }
+
+ return 0;
+}
+
+/*
+** Return true if the iterator passed as the only argument points
+** to an segment entry for which there is a tombstone. Return false
+** if there is no tombstone or if the iterator is already at EOF.
+*/
+static int fts5MultiIterIsDeleted(Fts5Iter *pIter){
+ int iFirst = pIter->aFirst[1].iFirst;
+ Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
+ Fts5TombstoneArray *pArray = pSeg->pTombArray;
+
+ if( pSeg->pLeaf && pArray ){
+ /* Figure out which page the rowid might be present on. */
+ int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone;
+ assert( iPg>=0 );
+
+ /* If tombstone hash page iPg has not yet been loaded from the
+ ** database, load it now. */
+ if( pArray->apTombstone[iPg]==0 ){
+ pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex,
+ FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg)
+ );
+ if( pArray->apTombstone[iPg]==0 ) return 0;
+ }
+
+ return fts5IndexTombstoneQuery(
+ pArray->apTombstone[iPg],
+ pArray->nTombstone,
+ pSeg->iRowid
+ );
+ }
+
+ return 0;
+}
+
+/*
+** Move the iterator to the next entry.
**
-** If an error occurs, an error code is left in Fts5Index.rc. It is not
-** considered an error if the iterator reaches EOF, or if it is already at
+** If an error occurs, an error code is left in Fts5Index.rc. It is not
+** considered an error if the iterator reaches EOF, or if it is already at
** EOF when this function is called.
*/
static void fts5MultiIterNext(
- Fts5Index *p,
+ Fts5Index *p,
Fts5Iter *pIter,
int bFrom, /* True if argument iFrom is valid */
i64 iFrom /* Advance at least as far as this */
@@ -209501,7 +241872,7 @@ static void fts5MultiIterNext(
pSeg->xNext(p, pSeg, &bNewTerm);
}
- if( pSeg->pLeaf==0 || bNewTerm
+ if( pSeg->pLeaf==0 || bNewTerm
|| fts5MultiIterAdvanceRowid(pIter, iFirst, &pSeg)
){
fts5MultiIterAdvanced(p, pIter, iFirst, 1);
@@ -209512,7 +241883,9 @@ static void fts5MultiIterNext(
fts5AssertMultiIterSetup(p, pIter);
assert( pSeg==&pIter->aSeg[pIter->aFirst[1].iFirst] && pSeg->pLeaf );
- if( pIter->bSkipEmpty==0 || pSeg->nPos ){
+ if( (pIter->bSkipEmpty==0 || pSeg->nPos)
+ && 0==fts5MultiIterIsDeleted(pIter)
+ ){
pIter->xSetOutputs(pIter, pSeg);
return;
}
@@ -209521,7 +241894,7 @@ static void fts5MultiIterNext(
}
static void fts5MultiIterNext2(
- Fts5Index *p,
+ Fts5Index *p,
Fts5Iter *pIter,
int *pbNewTerm /* OUT: True if *might* be new term */
){
@@ -209535,7 +241908,7 @@ static void fts5MultiIterNext2(
assert( p->rc==SQLITE_OK );
pSeg->xNext(p, pSeg, &bNewTerm);
- if( pSeg->pLeaf==0 || bNewTerm
+ if( pSeg->pLeaf==0 || bNewTerm
|| fts5MultiIterAdvanceRowid(pIter, iFirst, &pSeg)
){
fts5MultiIterAdvanced(p, pIter, iFirst, 1);
@@ -209544,7 +241917,9 @@ static void fts5MultiIterNext2(
}
fts5AssertMultiIterSetup(p, pIter);
- }while( fts5MultiIterIsEmpty(p, pIter) );
+ }while( (fts5MultiIterIsEmpty(p, pIter) || fts5MultiIterIsDeleted(pIter))
+ && (p->rc==SQLITE_OK)
+ );
}
}
@@ -209557,10 +241932,10 @@ static Fts5Iter *fts5MultiIterAlloc(
int nSeg
){
Fts5Iter *pNew;
- int nSlot; /* Power of two >= nSeg */
+ i64 nSlot; /* Power of two >= nSeg */
for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
- pNew = fts5IdxMalloc(p,
+ pNew = fts5IdxMalloc(p,
sizeof(Fts5Iter) + /* pNew */
sizeof(Fts5SegIter) * (nSlot-1) + /* pNew->aSeg[] */
sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */
@@ -209575,8 +241950,8 @@ static Fts5Iter *fts5MultiIterAlloc(
}
static void fts5PoslistCallback(
- Fts5Index *pUnused,
- void *pContext,
+ Fts5Index *pUnused,
+ void *pContext,
const u8 *pChunk, int nChunk
){
UNUSED_PARAM(pUnused);
@@ -209613,8 +241988,8 @@ static int fts5IndexColsetTest(Fts5Colset *pColset, int iCol){
}
static void fts5PoslistOffsetsCallback(
- Fts5Index *pUnused,
- void *pContext,
+ Fts5Index *pUnused,
+ void *pContext,
const u8 *pChunk, int nChunk
){
PoslistOffsetsCtx *pCtx = (PoslistOffsetsCtx*)pContext;
@@ -209637,7 +242012,7 @@ static void fts5PoslistOffsetsCallback(
static void fts5PoslistFilterCallback(
Fts5Index *pUnused,
- void *pContext,
+ void *pContext,
const u8 *pChunk, int nChunk
){
PoslistCallbackCtx *pCtx = (PoslistCallbackCtx*)pContext;
@@ -209700,7 +242075,7 @@ static void fts5ChunkIterate(
int pgno = pSeg->iLeafPgno;
int pgnoSave = 0;
- /* This function does notmwork with detail=none databases. */
+ /* This function does not work with detail=none databases. */
assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE );
if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){
@@ -209713,6 +242088,9 @@ static void fts5ChunkIterate(
fts5DataRelease(pData);
if( nRem<=0 ){
break;
+ }else if( pSeg->pSeg==0 ){
+ p->rc = FTS5_CORRUPT;
+ return;
}else{
pgno++;
pData = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno));
@@ -209740,7 +242118,12 @@ static void fts5SegiterPoslist(
Fts5Colset *pColset,
Fts5Buffer *pBuf
){
- if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos) ){
+ assert( pBuf!=0 );
+ assert( pSeg!=0 );
+ if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+FTS5_DATA_ZERO_PADDING) ){
+ assert( pBuf->p!=0 );
+ assert( pBuf->nSpace >= pBuf->n+pSeg->nPos+FTS5_DATA_ZERO_PADDING );
+ memset(&pBuf->p[pBuf->n+pSeg->nPos], 0, FTS5_DATA_ZERO_PADDING);
if( pColset==0 ){
fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback);
}else{
@@ -209763,66 +242146,72 @@ static void fts5SegiterPoslist(
}
/*
-** IN/OUT parameter (*pa) points to a position list n bytes in size. If
-** the position list contains entries for column iCol, then (*pa) is set
-** to point to the sub-position-list for that column and the number of
-** bytes in it returned. Or, if the argument position list does not
-** contain any entries for column iCol, return 0.
+** Parameter pPos points to a buffer containing a position list, size nPos.
+** This function filters it according to pColset (which must be non-NULL)
+** and sets pIter->base.pData/nData to point to the new position list.
+** If memory is required for the new position list, use buffer pIter->poslist.
+** Or, if the new position list is a contiguous subset of the input, set
+** pIter->base.pData/nData to point directly to it.
+**
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. If an OOM error is encountered, *pRc is set to SQLITE_NOMEM
+** before returning.
*/
-static int fts5IndexExtractCol(
- const u8 **pa, /* IN/OUT: Pointer to poslist */
- int n, /* IN: Size of poslist in bytes */
- int iCol /* Column to extract from poslist */
-){
- int iCurrent = 0; /* Anything before the first 0x01 is col 0 */
- const u8 *p = *pa;
- const u8 *pEnd = &p[n]; /* One byte past end of position list */
-
- while( iCol>iCurrent ){
- /* Advance pointer p until it points to pEnd or an 0x01 byte that is
- ** not part of a varint. Note that it is not possible for a negative
- ** or extremely large varint to occur within an uncorrupted position
- ** list. So the last byte of each varint may be assumed to have a clear
- ** 0x80 bit. */
- while( *p!=0x01 ){
- while( *p++ & 0x80 );
- if( p>=pEnd ) return 0;
- }
- *pa = p++;
- iCurrent = *p++;
- if( iCurrent & 0x80 ){
- p--;
- p += fts5GetVarint32(p, iCurrent);
- }
- }
- if( iCol!=iCurrent ) return 0;
-
- /* Advance pointer p until it points to pEnd or an 0x01 byte that is
- ** not part of a varint */
- while( p<pEnd && *p!=0x01 ){
- while( *p++ & 0x80 );
- }
-
- return p - (*pa);
-}
-
static void fts5IndexExtractColset(
int *pRc,
Fts5Colset *pColset, /* Colset to filter on */
const u8 *pPos, int nPos, /* Position list */
- Fts5Buffer *pBuf /* Output buffer */
+ Fts5Iter *pIter
){
if( *pRc==SQLITE_OK ){
- int i;
- fts5BufferZero(pBuf);
- for(i=0; i<pColset->nCol; i++){
- const u8 *pSub = pPos;
- int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]);
- if( nSub ){
- fts5BufferAppendBlob(pRc, pBuf, nSub, pSub);
+ const u8 *p = pPos;
+ const u8 *aCopy = p;
+ const u8 *pEnd = &p[nPos]; /* One byte past end of position list */
+ int i = 0;
+ int iCurrent = 0;
+
+ if( pColset->nCol>1 && sqlite3Fts5BufferSize(pRc, &pIter->poslist, nPos) ){
+ return;
+ }
+
+ while( 1 ){
+ while( pColset->aiCol[i]<iCurrent ){
+ i++;
+ if( i==pColset->nCol ){
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ return;
+ }
+ }
+
+ /* Advance pointer p until it points to pEnd or an 0x01 byte that is
+ ** not part of a varint */
+ while( p<pEnd && *p!=0x01 ){
+ while( *p++ & 0x80 );
+ }
+
+ if( pColset->aiCol[i]==iCurrent ){
+ if( pColset->nCol==1 ){
+ pIter->base.pData = aCopy;
+ pIter->base.nData = p-aCopy;
+ return;
+ }
+ fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy);
+ }
+ if( p>=pEnd ){
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ return;
+ }
+ aCopy = p++;
+ iCurrent = *p++;
+ if( iCurrent & 0x80 ){
+ p--;
+ p += fts5GetVarint32(p, iCurrent);
}
}
}
+
}
/*
@@ -209846,7 +242235,7 @@ static void fts5IterSetOutputs_Nocolset(Fts5Iter *pIter, Fts5SegIter *pSeg){
assert( pIter->pColset==0 );
if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){
- /* All data is stored on the current page. Populate the output
+ /* All data is stored on the current page. Populate the output
** variables to point into the body of the page object. */
pIter->base.pData = &pSeg->pLeaf->p[pSeg->iLeafOffset];
}else{
@@ -209882,13 +242271,13 @@ static void fts5IterSetOutputs_Col(Fts5Iter *pIter, Fts5SegIter *pSeg){
}
/*
-** xSetOutputs callback used when:
+** xSetOutputs callback used when:
**
** * detail=col,
** * there is a column filter, and
-** * the table contains 100 or fewer columns.
+** * the table contains 100 or fewer columns.
**
-** The last point is to ensure all column numbers are stored as
+** The last point is to ensure all column numbers are stored as
** single-byte varints.
*/
static void fts5IterSetOutputs_Col100(Fts5Iter *pIter, Fts5SegIter *pSeg){
@@ -209900,7 +242289,7 @@ static void fts5IterSetOutputs_Col100(Fts5Iter *pIter, Fts5SegIter *pSeg){
fts5IterSetOutputs_Col(pIter, pSeg);
}else{
u8 *a = (u8*)&pSeg->pLeaf->p[pSeg->iLeafOffset];
- u8 *pEnd = (u8*)&a[pSeg->nPos];
+ u8 *pEnd = (u8*)&a[pSeg->nPos];
int iPrev = 0;
int *aiCol = pIter->pColset->aiCol;
int *aiColEnd = &aiCol[pIter->pColset->nCol];
@@ -209939,19 +242328,12 @@ static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){
assert( pColset );
if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){
- /* All data is stored on the current page. Populate the output
+ /* All data is stored on the current page. Populate the output
** variables to point into the body of the page object. */
const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset];
- if( pColset->nCol==1 ){
- pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]);
- pIter->base.pData = a;
- }else{
- int *pRc = &pIter->pIndex->rc;
- fts5BufferZero(&pIter->poslist);
- fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, &pIter->poslist);
- pIter->base.pData = pIter->poslist.p;
- pIter->base.nData = pIter->poslist.n;
- }
+ int *pRc = &pIter->pIndex->rc;
+ fts5BufferZero(&pIter->poslist);
+ fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, pIter);
}else{
/* The data is distributed over two or more pages. Copy it into the
** Fts5Iter.poslist buffer and then set the output pointer to point
@@ -209964,6 +242346,7 @@ static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){
}
static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
+ assert( pIter!=0 || (*pRc)!=SQLITE_OK );
if( *pRc==SQLITE_OK ){
Fts5Config *pConfig = pIter->pIndex->pConfig;
if( pConfig->eDetail==FTS5_DETAIL_NONE ){
@@ -209994,6 +242377,32 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
}
}
+/*
+** All the component segment-iterators of pIter have been set up. This
+** functions finishes setup for iterator pIter itself.
+*/
+static void fts5MultiIterFinishSetup(Fts5Index *p, Fts5Iter *pIter){
+ int iIter;
+ for(iIter=pIter->nSeg-1; iIter>0; iIter--){
+ int iEq;
+ if( (iEq = fts5MultiIterDoCompare(pIter, iIter)) ){
+ Fts5SegIter *pSeg = &pIter->aSeg[iEq];
+ if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
+ fts5MultiIterAdvanced(p, pIter, iEq, iIter);
+ }
+ }
+ fts5MultiIterSetEof(pIter);
+ fts5AssertMultiIterSetup(p, pIter);
+
+ if( (pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter))
+ || fts5MultiIterIsDeleted(pIter)
+ ){
+ fts5MultiIterNext(p, pIter, 0, 0);
+ }else if( pIter->base.bEof==0 ){
+ Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
+ pIter->xSetOutputs(pIter, pSeg);
+ }
+}
/*
** Allocate a new Fts5Iter object.
@@ -210003,7 +242412,7 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
** is zero or greater, data from the first nSegment segments on level iLevel
** is merged.
**
-** The iterator initially points to the first term/rowid entry in the
+** The iterator initially points to the first term/rowid entry in the
** iterated data.
*/
static void fts5MultiIterNew(
@@ -210029,18 +242438,19 @@ static void fts5MultiIterNew(
if( iLevel<0 ){
assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) );
nSeg = pStruct->nSegment;
- nSeg += (p->pHash ? 1 : 0);
+ nSeg += (p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH));
}else{
nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment);
}
}
*ppOut = pNew = fts5MultiIterAlloc(p, nSeg);
- if( pNew==0 ) return;
+ if( pNew==0 ){
+ assert( p->rc!=SQLITE_OK );
+ goto fts5MultiIterNew_post_check;
+ }
pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC));
pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY));
- pNew->pStruct = pStruct;
pNew->pColset = pColset;
- fts5StructureRef(pStruct);
if( (flags & FTS5INDEX_QUERY_NOOUTPUT)==0 ){
fts5IterSetOutputCb(&p->rc, pNew);
}
@@ -210049,7 +242459,7 @@ static void fts5MultiIterNew(
if( p->rc==SQLITE_OK ){
if( iLevel<0 ){
Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel];
- if( p->pHash ){
+ if( p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH) ){
/* Add a segment iterator for the current contents of the hash table. */
Fts5SegIter *pIter = &pNew->aSeg[iIter++];
fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter);
@@ -210074,33 +242484,20 @@ static void fts5MultiIterNew(
assert( iIter==nSeg );
}
- /* If the above was successful, each component iterators now points
- ** to the first entry in its segment. In this case initialize the
+ /* If the above was successful, each component iterator now points
+ ** to the first entry in its segment. In this case initialize the
** aFirst[] array. Or, if an error has occurred, free the iterator
** object and set the output variable to NULL. */
if( p->rc==SQLITE_OK ){
- for(iIter=pNew->nSeg-1; iIter>0; iIter--){
- int iEq;
- if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){
- Fts5SegIter *pSeg = &pNew->aSeg[iEq];
- if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
- fts5MultiIterAdvanced(p, pNew, iEq, iIter);
- }
- }
- fts5MultiIterSetEof(pNew);
- fts5AssertMultiIterSetup(p, pNew);
-
- if( pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew) ){
- fts5MultiIterNext(p, pNew, 0, 0);
- }else if( pNew->base.bEof==0 ){
- Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst];
- pNew->xSetOutputs(pNew, pSeg);
- }
-
+ fts5MultiIterFinishSetup(p, pNew);
}else{
fts5MultiIterFree(pNew);
*ppOut = 0;
}
+
+fts5MultiIterNew_post_check:
+ assert( (*ppOut)!=0 || p->rc!=SQLITE_OK );
+ return;
}
/*
@@ -210117,7 +242514,6 @@ static void fts5MultiIterNew2(
pNew = fts5MultiIterAlloc(p, 2);
if( pNew ){
Fts5SegIter *pIter = &pNew->aSeg[1];
-
pIter->flags = FTS5_SEGITER_ONETERM;
if( pData->szLeaf>0 ){
pIter->pLeaf = pData;
@@ -210144,12 +242540,13 @@ static void fts5MultiIterNew2(
}
/*
-** Return true if the iterator is at EOF or if an error has occurred.
+** Return true if the iterator is at EOF or if an error has occurred.
** False otherwise.
*/
static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){
- assert( p->rc
- || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof
+ assert( pIter!=0 || p->rc!=SQLITE_OK );
+ assert( p->rc!=SQLITE_OK
+ || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof
);
return (p->rc || pIter->base.bEof);
}
@@ -210168,8 +242565,8 @@ static i64 fts5MultiIterRowid(Fts5Iter *pIter){
** Move the iterator to the next entry at or following iMatch.
*/
static void fts5MultiIterNextFrom(
- Fts5Index *p,
- Fts5Iter *pIter,
+ Fts5Index *p,
+ Fts5Iter *pIter,
i64 iMatch
){
while( 1 ){
@@ -210183,7 +242580,7 @@ static void fts5MultiIterNextFrom(
}
/*
-** Return a pointer to a buffer containing the term associated with the
+** Return a pointer to a buffer containing the term associated with the
** entry that the iterator currently points to.
*/
static const u8 *fts5MultiIterTerm(Fts5Iter *pIter, int *pn){
@@ -210194,11 +242591,11 @@ static const u8 *fts5MultiIterTerm(Fts5Iter *pIter, int *pn){
/*
** Allocate a new segment-id for the structure pStruct. The new segment
-** id must be between 1 and 65335 inclusive, and must not be used by
+** id must be between 1 and 65335 inclusive, and must not be used by
** any currently existing segment. If a free segment id cannot be found,
** SQLITE_FULL is returned.
**
-** If an error has already occurred, this function is a no-op. 0 is
+** If an error has already occurred, this function is a no-op. 0 is
** returned in this case.
*/
static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){
@@ -210218,24 +242615,24 @@ static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){
for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
int iId = pStruct->aLevel[iLvl].aSeg[iSeg].iSegid;
- if( iId<=FTS5_MAX_SEGMENT ){
- aUsed[(iId-1) / 32] |= 1 << ((iId-1) % 32);
+ if( iId<=FTS5_MAX_SEGMENT && iId>0 ){
+ aUsed[(iId-1) / 32] |= (u32)1 << ((iId-1) % 32);
}
}
}
for(i=0; aUsed[i]==0xFFFFFFFF; i++);
mask = aUsed[i];
- for(iSegid=0; mask & (1 << iSegid); iSegid++);
+ for(iSegid=0; mask & ((u32)1 << iSegid); iSegid++);
iSegid += 1 + i*32;
#ifdef SQLITE_DEBUG
for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
- assert( iSegid!=pStruct->aLevel[iLvl].aSeg[iSeg].iSegid );
+ assert_nc( iSegid!=pStruct->aLevel[iLvl].aSeg[iSeg].iSegid );
}
}
- assert( iSegid>0 && iSegid<=FTS5_MAX_SEGMENT );
+ assert_nc( iSegid>0 && iSegid<=FTS5_MAX_SEGMENT );
{
sqlite3_stmt *pIdxSelect = fts5IdxSelectStmt(p);
@@ -210243,7 +242640,7 @@ static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){
u8 aBlob[2] = {0xff, 0xff};
sqlite3_bind_int(pIdxSelect, 1, iSegid);
sqlite3_bind_blob(pIdxSelect, 2, aBlob, 2, SQLITE_STATIC);
- assert( sqlite3_step(pIdxSelect)!=SQLITE_ROW );
+ assert_nc( sqlite3_step(pIdxSelect)!=SQLITE_ROW );
p->rc = sqlite3_reset(pIdxSelect);
sqlite3_bind_null(pIdxSelect, 2);
}
@@ -210263,14 +242660,17 @@ static void fts5IndexDiscardData(Fts5Index *p){
if( p->pHash ){
sqlite3Fts5HashClear(p->pHash);
p->nPendingData = 0;
+ p->nPendingRow = 0;
+ p->flushRc = SQLITE_OK;
}
+ p->nContentlessDelete = 0;
}
/*
-** Return the size of the prefix, in bytes, that buffer
+** Return the size of the prefix, in bytes, that buffer
** (pNew/<length-unknown>) shares with buffer (pOld/nOld).
**
-** Buffer (pNew/<length-unknown>) is guaranteed to be greater
+** Buffer (pNew/<length-unknown>) is guaranteed to be greater
** than buffer (pOld/nOld).
*/
static int fts5PrefixCompress(int nOld, const u8 *pOld, const u8 *pNew){
@@ -210282,7 +242682,7 @@ static int fts5PrefixCompress(int nOld, const u8 *pOld, const u8 *pNew){
}
static void fts5WriteDlidxClear(
- Fts5Index *p,
+ Fts5Index *p,
Fts5SegWriter *pWriter,
int bFlush /* If true, write dlidx to disk */
){
@@ -210293,7 +242693,7 @@ static void fts5WriteDlidxClear(
if( pDlidx->buf.n==0 ) break;
if( bFlush ){
assert( pDlidx->pgno!=0 );
- fts5DataWrite(p,
+ fts5DataWrite(p,
FTS5_DLIDX_ROWID(pWriter->iSegid, i, pDlidx->pgno),
pDlidx->buf.p, pDlidx->buf.n
);
@@ -210313,13 +242713,13 @@ static int fts5WriteDlidxGrow(
int nLvl
){
if( p->rc==SQLITE_OK && nLvl>=pWriter->nDlidx ){
- Fts5DlidxWriter *aDlidx = (Fts5DlidxWriter*)sqlite3_realloc(
+ Fts5DlidxWriter *aDlidx = (Fts5DlidxWriter*)sqlite3_realloc64(
pWriter->aDlidx, sizeof(Fts5DlidxWriter) * nLvl
);
if( aDlidx==0 ){
p->rc = SQLITE_NOMEM;
}else{
- int nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx);
+ size_t nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx);
memset(&aDlidx[pWriter->nDlidx], 0, nByte);
pWriter->aDlidx = aDlidx;
pWriter->nDlidx = nLvl;
@@ -210347,8 +242747,8 @@ static int fts5WriteFlushDlidx(Fts5Index *p, Fts5SegWriter *pWriter){
}
/*
-** This function is called whenever processing of the doclist for the
-** last term on leaf page (pWriter->iBtPage) is completed.
+** This function is called whenever processing of the doclist for the
+** last term on leaf page (pWriter->iBtPage) is completed.
**
** The doclist-index for that term is currently stored in-memory within the
** Fts5SegWriter.aDlidx[] array. If it is large enough, this function
@@ -210392,8 +242792,10 @@ static void fts5WriteBtreeTerm(
int nTerm, const u8 *pTerm /* First term on new page */
){
fts5WriteFlushBtree(p, pWriter);
- fts5BufferSet(&p->rc, &pWriter->btterm, nTerm, pTerm);
- pWriter->iBtPage = pWriter->writer.pgno;
+ if( p->rc==SQLITE_OK ){
+ fts5BufferSet(&p->rc, &pWriter->btterm, nTerm, pTerm);
+ pWriter->iBtPage = pWriter->writer.pgno;
+ }
}
/*
@@ -210431,8 +242833,8 @@ static i64 fts5DlidxExtractFirstRowid(Fts5Buffer *pBuf){
** doclist-index.
*/
static void fts5WriteDlidxAppend(
- Fts5Index *p,
- Fts5SegWriter *pWriter,
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
i64 iRowid
){
int i;
@@ -210445,11 +242847,11 @@ static void fts5WriteDlidxAppend(
if( pDlidx->buf.n>=p->pConfig->pgsz ){
/* The current doclist-index page is full. Write it to disk and push
** a copy of iRowid (which will become the first rowid on the next
- ** doclist-index leaf page) up into the next level of the b-tree
+ ** doclist-index leaf page) up into the next level of the b-tree
** hierarchy. If the node being flushed is currently the root node,
** also push its first rowid upwards. */
pDlidx->buf.p[0] = 0x01; /* Not the root node */
- fts5DataWrite(p,
+ fts5DataWrite(p,
FTS5_DLIDX_ROWID(pWriter->iSegid, i, pDlidx->pgno),
pDlidx->buf.p, pDlidx->buf.n
);
@@ -210475,7 +242877,7 @@ static void fts5WriteDlidxAppend(
}
if( pDlidx->bPrevValid ){
- iVal = iRowid - pDlidx->iPrev;
+ iVal = (u64)iRowid - (u64)pDlidx->iPrev;
}else{
i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno);
assert( pDlidx->buf.n==0 );
@@ -210533,17 +242935,18 @@ static void fts5WriteFlushLeaf(Fts5Index *p, Fts5SegWriter *pWriter){
** Append term pTerm/nTerm to the segment being written by the writer passed
** as the second argument.
**
-** If an error occurs, set the Fts5Index.rc error code. If an error has
+** If an error occurs, set the Fts5Index.rc error code. If an error has
** already occurred, this function is a no-op.
*/
static void fts5WriteAppendTerm(
- Fts5Index *p,
+ Fts5Index *p,
Fts5SegWriter *pWriter,
- int nTerm, const u8 *pTerm
+ int nTerm, const u8 *pTerm
){
int nPrefix; /* Bytes of prefix compression for term */
Fts5PageWriter *pPage = &pWriter->writer;
Fts5Buffer *pPgidx = &pWriter->writer.pgidx;
+ int nMin = MIN(pPage->term.n, nTerm);
assert( p->rc==SQLITE_OK );
assert( pPage->buf.n>=4 );
@@ -210553,10 +242956,11 @@ static void fts5WriteAppendTerm(
if( (pPage->buf.n + pPgidx->n + nTerm + 2)>=p->pConfig->pgsz ){
if( pPage->buf.n>4 ){
fts5WriteFlushLeaf(p, pWriter);
+ if( p->rc!=SQLITE_OK ) return;
}
fts5BufferGrow(&p->rc, &pPage->buf, nTerm+FTS5_DATA_PADDING);
}
-
+
/* TODO1: Updating pgidx here. */
pPgidx->n += sqlite3Fts5PutVarint(
&pPgidx->p[pPgidx->n], pPage->buf.n - pPage->iPrevPgidx
@@ -210572,11 +242976,11 @@ static void fts5WriteAppendTerm(
if( pPage->pgno!=1 ){
/* This is the first term on a leaf that is not the leftmost leaf in
** the segment b-tree. In this case it is necessary to add a term to
- ** the b-tree hierarchy that is (a) larger than the largest term
+ ** the b-tree hierarchy that is (a) larger than the largest term
** already written to the segment and (b) smaller than or equal to
** this term. In other words, a prefix of (pTerm/nTerm) that is one
** byte longer than the longest prefix (pTerm/nTerm) shares with the
- ** previous term.
+ ** previous term.
**
** Usually, the previous term is available in pPage->term. The exception
** is if this is the first term written in an incremental-merge step.
@@ -210585,13 +242989,14 @@ static void fts5WriteAppendTerm(
** inefficient, but still correct. */
int n = nTerm;
if( pPage->term.n ){
- n = 1 + fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm);
+ n = 1 + fts5PrefixCompress(nMin, pPage->term.p, pTerm);
}
fts5WriteBtreeTerm(p, pWriter, n, pTerm);
+ if( p->rc!=SQLITE_OK ) return;
pPage = &pWriter->writer;
}
}else{
- nPrefix = fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm);
+ nPrefix = fts5PrefixCompress(nMin, pPage->term.p, pTerm);
fts5BufferAppendVarint(&p->rc, &pPage->buf, nPrefix);
}
@@ -210612,10 +243017,10 @@ static void fts5WriteAppendTerm(
}
/*
-** Append a rowid and position-list size field to the writers output.
+** Append a rowid and position-list size field to the writers output.
*/
static void fts5WriteAppendRowid(
- Fts5Index *p,
+ Fts5Index *p,
Fts5SegWriter *pWriter,
i64 iRowid
){
@@ -210626,7 +243031,7 @@ static void fts5WriteAppendRowid(
fts5WriteFlushLeaf(p, pWriter);
}
- /* If this is to be the first rowid written to the page, set the
+ /* If this is to be the first rowid written to the page, set the
** rowid-pointer in the page-header. Also append a value to the dlidx
** buffer, in case a doclist-index is required. */
if( pWriter->bFirstRowidInPage ){
@@ -210638,8 +243043,10 @@ static void fts5WriteAppendRowid(
if( pWriter->bFirstRowidInDoclist || pWriter->bFirstRowidInPage ){
fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid);
}else{
- assert( p->rc || iRowid>pWriter->iPrevRowid );
- fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid);
+ assert_nc( p->rc || iRowid>pWriter->iPrevRowid );
+ fts5BufferAppendVarint(&p->rc, &pPage->buf,
+ (u64)iRowid - (u64)pWriter->iPrevRowid
+ );
}
pWriter->iPrevRowid = iRowid;
pWriter->bFirstRowidInDoclist = 0;
@@ -210648,18 +243055,18 @@ static void fts5WriteAppendRowid(
}
static void fts5WriteAppendPoslistData(
- Fts5Index *p,
- Fts5SegWriter *pWriter,
- const u8 *aData,
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ const u8 *aData,
int nData
){
Fts5PageWriter *pPage = &pWriter->writer;
const u8 *a = aData;
int n = nData;
-
- assert( p->pConfig->pgsz>0 );
- while( p->rc==SQLITE_OK
- && (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz
+
+ assert( p->pConfig->pgsz>0 || p->rc!=SQLITE_OK );
+ while( p->rc==SQLITE_OK
+ && (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz
){
int nReq = p->pConfig->pgsz - pPage->buf.n - pPage->pgidx.n;
int nCopy = 0;
@@ -210682,7 +243089,7 @@ static void fts5WriteAppendPoslistData(
** allocations associated with the writer.
*/
static void fts5WriteFinish(
- Fts5Index *p,
+ Fts5Index *p,
Fts5SegWriter *pWriter, /* Writer object */
int *pnLeaf /* OUT: Number of leaf pages in b-tree */
){
@@ -210710,8 +243117,8 @@ static void fts5WriteFinish(
}
static void fts5WriteInit(
- Fts5Index *p,
- Fts5SegWriter *pWriter,
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
int iSegid
){
const int nBuffer = p->pConfig->pgsz + FTS5_DATA_PADDING;
@@ -210734,7 +243141,7 @@ static void fts5WriteInit(
if( p->pIdxWriter==0 ){
Fts5Config *pConfig = p->pConfig;
fts5IndexPrepareStmt(p, &p->pIdxWriter, sqlite3_mprintf(
- "INSERT INTO '%q'.'%q_idx'(segid,term,pgno) VALUES(?,?,?)",
+ "INSERT INTO '%q'.'%q_idx'(segid,term,pgno) VALUES(?,?,?)",
pConfig->zDb, pConfig->zName
));
}
@@ -210760,7 +243167,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
int i;
Fts5Buffer buf;
memset(&buf, 0, sizeof(Fts5Buffer));
- for(i=0; i<pIter->nSeg; i++){
+ for(i=0; i<pIter->nSeg && p->rc==SQLITE_OK; i++){
Fts5SegIter *pSeg = &pIter->aSeg[i];
if( pSeg->pSeg==0 ){
/* no-op */
@@ -210778,35 +243185,44 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
u8 aHdr[4] = {0x00, 0x00, 0x00, 0x00};
iLeafRowid = FTS5_SEGMENT_ROWID(iId, pSeg->iTermLeafPgno);
- pData = fts5DataRead(p, iLeafRowid);
+ pData = fts5LeafRead(p, iLeafRowid);
if( pData ){
- fts5BufferZero(&buf);
- fts5BufferGrow(&p->rc, &buf, pData->nn);
- fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr);
- fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n);
- fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p);
- fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff, &pData->p[iOff]);
- if( p->rc==SQLITE_OK ){
- /* Set the szLeaf field */
- fts5PutU16(&buf.p[2], (u16)buf.n);
- }
+ if( iOff>pData->szLeaf ){
+ /* This can occur if the pages that the segments occupy overlap - if
+ ** a single page has been assigned to more than one segment. In
+ ** this case a prior iteration of this loop may have corrupted the
+ ** segment currently being trimmed. */
+ p->rc = FTS5_CORRUPT;
+ }else{
+ fts5BufferZero(&buf);
+ fts5BufferGrow(&p->rc, &buf, pData->nn);
+ fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr);
+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n);
+ fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p);
+ fts5BufferAppendBlob(&p->rc, &buf,pData->szLeaf-iOff,&pData->p[iOff]);
+ if( p->rc==SQLITE_OK ){
+ /* Set the szLeaf field */
+ fts5PutU16(&buf.p[2], (u16)buf.n);
+ }
- /* Set up the new page-index array */
- fts5BufferAppendVarint(&p->rc, &buf, 4);
- if( pSeg->iLeafPgno==pSeg->iTermLeafPgno
- && pSeg->iEndofDoclist<pData->szLeaf
- ){
- int nDiff = pData->szLeaf - pSeg->iEndofDoclist;
- fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4);
- fts5BufferAppendBlob(&p->rc, &buf,
- pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff]
- );
- }
+ /* Set up the new page-index array */
+ fts5BufferAppendVarint(&p->rc, &buf, 4);
+ if( pSeg->iLeafPgno==pSeg->iTermLeafPgno
+ && pSeg->iEndofDoclist<pData->szLeaf
+ && pSeg->iPgidxOff<=pData->nn
+ ){
+ int nDiff = pData->szLeaf - pSeg->iEndofDoclist;
+ fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4);
+ fts5BufferAppendBlob(&p->rc, &buf,
+ pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff]
+ );
+ }
+ pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno;
+ fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 1), iLeafRowid);
+ fts5DataWrite(p, iLeafRowid, buf.p, buf.n);
+ }
fts5DataRelease(pData);
- pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno;
- fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 1), iLeafRowid);
- fts5DataWrite(p, iLeafRowid, buf.p, buf.n);
}
}
}
@@ -210814,8 +243230,8 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
}
static void fts5MergeChunkCallback(
- Fts5Index *p,
- void *pCtx,
+ Fts5Index *p,
+ void *pCtx,
const u8 *pChunk, int nChunk
){
Fts5SegWriter *pWriter = (Fts5SegWriter*)pCtx;
@@ -210884,6 +243300,12 @@ static void fts5IndexMergeLevel(
/* Read input from all segments in the input level */
nInput = pLvl->nSeg;
+
+ /* Set the range of origins that will go into the output segment. */
+ if( pStruct->nOriginCntr>0 ){
+ pSeg->iOrigin1 = pLvl->aSeg[0].iOrigin1;
+ pSeg->iOrigin2 = pLvl->aSeg[pLvl->nSeg-1].iOrigin2;
+ }
}
bOldest = (pLvlOut->nSeg==1 && pStruct->nLevel==iLvl+2);
@@ -210898,7 +243320,7 @@ static void fts5IndexMergeLevel(
const u8 *pTerm;
pTerm = fts5MultiIterTerm(pIter, &nTerm);
- if( nTerm!=term.n || memcmp(pTerm, term.p, nTerm) ){
+ if( nTerm!=term.n || fts5Memcmp(pTerm, term.p, nTerm) ){
if( pnRem && writer.nLeafWritten>nRem ){
break;
}
@@ -210938,12 +243360,16 @@ static void fts5IndexMergeLevel(
** and last leaf page number at the same time. */
fts5WriteFinish(p, &writer, &pSeg->pgnoLast);
+ assert( pIter!=0 || p->rc!=SQLITE_OK );
if( fts5MultiIterEof(p, pIter) ){
int i;
/* Remove the redundant segments from the %_data table */
+ assert( pSeg->nEntry==0 );
for(i=0; i<nInput; i++){
- fts5DataRemoveSegment(p, pLvl->aSeg[i].iSegid);
+ Fts5StructureSegment *pOld = &pLvl->aSeg[i];
+ pSeg->nEntry += (pOld->nEntry - pOld->nEntryTombstone);
+ fts5DataRemoveSegment(p, pOld);
}
/* Remove the redundant segments from the input level */
@@ -210970,6 +243396,43 @@ static void fts5IndexMergeLevel(
}
/*
+** If this is not a contentless_delete=1 table, or if the 'deletemerge'
+** configuration option is set to 0, then this function always returns -1.
+** Otherwise, it searches the structure object passed as the second argument
+** for a level suitable for merging due to having a large number of
+** tombstones in the tombstone hash. If one is found, its index is returned.
+** Otherwise, if there is no suitable level, -1.
+*/
+static int fts5IndexFindDeleteMerge(Fts5Index *p, Fts5Structure *pStruct){
+ Fts5Config *pConfig = p->pConfig;
+ int iRet = -1;
+ if( pConfig->bContentlessDelete && pConfig->nDeleteMerge>0 ){
+ int ii;
+ int nBest = 0;
+
+ for(ii=0; ii<pStruct->nLevel; ii++){
+ Fts5StructureLevel *pLvl = &pStruct->aLevel[ii];
+ i64 nEntry = 0;
+ i64 nTomb = 0;
+ int iSeg;
+ for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){
+ nEntry += pLvl->aSeg[iSeg].nEntry;
+ nTomb += pLvl->aSeg[iSeg].nEntryTombstone;
+ }
+ assert_nc( nEntry>0 || pLvl->nSeg==0 );
+ if( nEntry>0 ){
+ int nPercent = (nTomb * 100) / nEntry;
+ if( nPercent>=pConfig->nDeleteMerge && nPercent>nBest ){
+ iRet = ii;
+ nBest = nPercent;
+ }
+ }
+ }
+ }
+ return iRet;
+}
+
+/*
** Do up to nPg pages of automerge work on the index.
**
** Return true if any changes were actually made, or false otherwise.
@@ -210988,14 +243451,15 @@ static int fts5IndexMerge(
int iBestLvl = 0; /* Level offering the most input segments */
int nBest = 0; /* Number of input segments on best level */
- /* Set iBestLvl to the level to read input segments from. */
+ /* Set iBestLvl to the level to read input segments from. Or to -1 if
+ ** there is no level suitable to merge segments from. */
assert( pStruct->nLevel>0 );
for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
if( pLvl->nMerge ){
if( pLvl->nMerge>nBest ){
iBestLvl = iLvl;
- nBest = pLvl->nMerge;
+ nBest = nMin;
}
break;
}
@@ -211004,22 +243468,18 @@ static int fts5IndexMerge(
iBestLvl = iLvl;
}
}
-
- /* If nBest is still 0, then the index must be empty. */
-#ifdef SQLITE_DEBUG
- for(iLvl=0; nBest==0 && iLvl<pStruct->nLevel; iLvl++){
- assert( pStruct->aLevel[iLvl].nSeg==0 );
+ if( nBest<nMin ){
+ iBestLvl = fts5IndexFindDeleteMerge(p, pStruct);
}
-#endif
- if( nBest<nMin && pStruct->aLevel[iBestLvl].nMerge==0 ){
- break;
- }
+ if( iBestLvl<0 ) break;
bRet = 1;
fts5IndexMergeLevel(p, &pStruct, iBestLvl, &nRem);
if( p->rc==SQLITE_OK && pStruct->aLevel[iBestLvl].nMerge==0 ){
fts5StructurePromote(p, iBestLvl+1, pStruct);
}
+
+ if( nMin==1 ) nMin = 2;
}
*ppStruct = pStruct;
return bRet;
@@ -211030,7 +243490,7 @@ static int fts5IndexMerge(
** segment. This function updates the write-counter accordingly and, if
** necessary, performs incremental merge work.
**
-** If an error occurs, set the Fts5Index.rc error code. If an error has
+** If an error occurs, set the Fts5Index.rc error code. If an error has
** already occurred, this function is a no-op.
*/
static void fts5IndexAutomerge(
@@ -211038,7 +243498,7 @@ static void fts5IndexAutomerge(
Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */
int nLeaf /* Number of output leaves just written */
){
- if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 ){
+ if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 && ALWAYS((*ppStruct)!=0) ){
Fts5Structure *pStruct = *ppStruct;
u64 nWrite; /* Initial value of write-counter */
int nWork; /* Number of work-quanta to perform */
@@ -211060,16 +243520,16 @@ static void fts5IndexCrisismerge(
){
const int nCrisis = p->pConfig->nCrisisMerge;
Fts5Structure *pStruct = *ppStruct;
- int iLvl = 0;
-
- assert( p->rc!=SQLITE_OK || pStruct->nLevel>0 );
- while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){
- fts5IndexMergeLevel(p, &pStruct, iLvl, 0);
- assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) );
- fts5StructurePromote(p, iLvl+1, pStruct);
- iLvl++;
+ if( pStruct && pStruct->nLevel>0 ){
+ int iLvl = 0;
+ while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){
+ fts5IndexMergeLevel(p, &pStruct, iLvl, 0);
+ assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) );
+ fts5StructurePromote(p, iLvl+1, pStruct);
+ iLvl++;
+ }
+ *ppStruct = pStruct;
}
- *ppStruct = pStruct;
}
static int fts5IndexReturn(Fts5Index *p){
@@ -211081,12 +243541,12 @@ static int fts5IndexReturn(Fts5Index *p){
typedef struct Fts5FlushCtx Fts5FlushCtx;
struct Fts5FlushCtx {
Fts5Index *pIdx;
- Fts5SegWriter writer;
+ Fts5SegWriter writer;
};
/*
** Buffer aBuf[] contains a list of varints, all small enough to fit
-** in a 32-bit integer. Return the size of the largest prefix of this
+** in a 32-bit integer. Return the size of the largest prefix of this
** list nMax bytes or less in size.
*/
static int fts5PoslistPrefix(const u8 *aBuf, int nMax){
@@ -211104,10 +243564,473 @@ static int fts5PoslistPrefix(const u8 *aBuf, int nMax){
}
/*
-** Flush the contents of in-memory hash table iHash to a new level-0
+** Execute the SQL statement:
+**
+** DELETE FROM %_idx WHERE (segid, (pgno/2)) = ($iSegid, $iPgno);
+**
+** This is used when a secure-delete operation removes the last term
+** from a segment leaf page. In that case the %_idx entry is removed
+** too. This is done to ensure that if all instances of a token are
+** removed from an fts5 database in secure-delete mode, no trace of
+** the token itself remains in the database.
+*/
+static void fts5SecureDeleteIdxEntry(
+ Fts5Index *p, /* FTS5 backend object */
+ int iSegid, /* Id of segment to delete entry for */
+ int iPgno /* Page number within segment */
+){
+ if( iPgno!=1 ){
+ assert( p->pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE );
+ if( p->pDeleteFromIdx==0 ){
+ fts5IndexPrepareStmt(p, &p->pDeleteFromIdx, sqlite3_mprintf(
+ "DELETE FROM '%q'.'%q_idx' WHERE (segid, (pgno/2)) = (?1, ?2)",
+ p->pConfig->zDb, p->pConfig->zName
+ ));
+ }
+ if( p->rc==SQLITE_OK ){
+ sqlite3_bind_int(p->pDeleteFromIdx, 1, iSegid);
+ sqlite3_bind_int(p->pDeleteFromIdx, 2, iPgno);
+ sqlite3_step(p->pDeleteFromIdx);
+ p->rc = sqlite3_reset(p->pDeleteFromIdx);
+ }
+ }
+}
+
+/*
+** This is called when a secure-delete operation removes a position-list
+** that overflows onto segment page iPgno of segment pSeg. This function
+** rewrites node iPgno, and possibly one or more of its right-hand peers,
+** to remove this portion of the position list.
+**
+** Output variable (*pbLastInDoclist) is set to true if the position-list
+** removed is followed by a new term or the end-of-segment, or false if
+** it is followed by another rowid/position list.
+*/
+static void fts5SecureDeleteOverflow(
+ Fts5Index *p,
+ Fts5StructureSegment *pSeg,
+ int iPgno,
+ int *pbLastInDoclist
+){
+ const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE);
+ int pgno;
+ Fts5Data *pLeaf = 0;
+ assert( iPgno!=1 );
+
+ *pbLastInDoclist = 1;
+ for(pgno=iPgno; p->rc==SQLITE_OK && pgno<=pSeg->pgnoLast; pgno++){
+ i64 iRowid = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno);
+ int iNext = 0;
+ u8 *aPg = 0;
+
+ pLeaf = fts5DataRead(p, iRowid);
+ if( pLeaf==0 ) break;
+ aPg = pLeaf->p;
+
+ iNext = fts5GetU16(&aPg[0]);
+ if( iNext!=0 ){
+ *pbLastInDoclist = 0;
+ }
+ if( iNext==0 && pLeaf->szLeaf!=pLeaf->nn ){
+ fts5GetVarint32(&aPg[pLeaf->szLeaf], iNext);
+ }
+
+ if( iNext==0 ){
+ /* The page contains no terms or rowids. Replace it with an empty
+ ** page and move on to the right-hand peer. */
+ const u8 aEmpty[] = {0x00, 0x00, 0x00, 0x04};
+ assert_nc( bDetailNone==0 || pLeaf->nn==4 );
+ if( bDetailNone==0 ) fts5DataWrite(p, iRowid, aEmpty, sizeof(aEmpty));
+ fts5DataRelease(pLeaf);
+ pLeaf = 0;
+ }else if( bDetailNone ){
+ break;
+ }else if( iNext>=pLeaf->szLeaf || pLeaf->nn<pLeaf->szLeaf || iNext<4 ){
+ p->rc = FTS5_CORRUPT;
+ break;
+ }else{
+ int nShift = iNext - 4;
+ int nPg;
+
+ int nIdx = 0;
+ u8 *aIdx = 0;
+
+ /* Unless the current page footer is 0 bytes in size (in which case
+ ** the new page footer will be as well), allocate and populate a
+ ** buffer containing the new page footer. Set stack variables aIdx
+ ** and nIdx accordingly. */
+ if( pLeaf->nn>pLeaf->szLeaf ){
+ int iFirst = 0;
+ int i1 = pLeaf->szLeaf;
+ int i2 = 0;
+
+ i1 += fts5GetVarint32(&aPg[i1], iFirst);
+ if( iFirst<iNext ){
+ p->rc = FTS5_CORRUPT;
+ break;
+ }
+ aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2);
+ if( aIdx==0 ) break;
+ i2 = sqlite3Fts5PutVarint(aIdx, iFirst-nShift);
+ if( i1<pLeaf->nn ){
+ memcpy(&aIdx[i2], &aPg[i1], pLeaf->nn-i1);
+ i2 += (pLeaf->nn-i1);
+ }
+ nIdx = i2;
+ }
+
+ /* Modify the contents of buffer aPg[]. Set nPg to the new size
+ ** in bytes. The new page is always smaller than the old. */
+ nPg = pLeaf->szLeaf - nShift;
+ memmove(&aPg[4], &aPg[4+nShift], nPg-4);
+ fts5PutU16(&aPg[2], nPg);
+ if( fts5GetU16(&aPg[0]) ) fts5PutU16(&aPg[0], 4);
+ if( nIdx>0 ){
+ memcpy(&aPg[nPg], aIdx, nIdx);
+ nPg += nIdx;
+ }
+ sqlite3_free(aIdx);
+
+ /* Write the new page to disk and exit the loop */
+ assert( nPg>4 || fts5GetU16(aPg)==0 );
+ fts5DataWrite(p, iRowid, aPg, nPg);
+ break;
+ }
+ }
+ fts5DataRelease(pLeaf);
+}
+
+/*
+** Completely remove the entry that pSeg currently points to from
+** the database.
+*/
+static void fts5DoSecureDelete(
+ Fts5Index *p,
+ Fts5SegIter *pSeg
+){
+ const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE);
+ int iSegid = pSeg->pSeg->iSegid;
+ u8 *aPg = pSeg->pLeaf->p;
+ int nPg = pSeg->pLeaf->nn;
+ int iPgIdx = pSeg->pLeaf->szLeaf;
+
+ u64 iDelta = 0;
+ int iNextOff = 0;
+ int iOff = 0;
+ int nIdx = 0;
+ u8 *aIdx = 0;
+ int bLastInDoclist = 0;
+ int iIdx = 0;
+ int iStart = 0;
+ int iDelKeyOff = 0; /* Offset of deleted key, if any */
+
+ nIdx = nPg-iPgIdx;
+ aIdx = sqlite3Fts5MallocZero(&p->rc, nIdx+16);
+ if( p->rc ) return;
+ memcpy(aIdx, &aPg[iPgIdx], nIdx);
+
+ /* At this point segment iterator pSeg points to the entry
+ ** this function should remove from the b-tree segment.
+ **
+ ** In detail=full or detail=column mode, pSeg->iLeafOffset is the
+ ** offset of the first byte in the position-list for the entry to
+ ** remove. Immediately before this comes two varints that will also
+ ** need to be removed:
+ **
+ ** + the rowid or delta rowid value for the entry, and
+ ** + the size of the position list in bytes.
+ **
+ ** Or, in detail=none mode, there is a single varint prior to
+ ** pSeg->iLeafOffset - the rowid or delta rowid value.
+ **
+ ** This block sets the following variables:
+ **
+ ** iStart:
+ ** The offset of the first byte of the rowid or delta-rowid
+ ** value for the doclist entry being removed.
+ **
+ ** iDelta:
+ ** The value of the rowid or delta-rowid value for the doclist
+ ** entry being removed.
+ **
+ ** iNextOff:
+ ** The offset of the next entry following the position list
+ ** for the one being removed. If the position list for this
+ ** entry overflows onto the next leaf page, this value will be
+ ** greater than pLeaf->szLeaf.
+ */
+ {
+ int iSOP; /* Start-Of-Position-list */
+ if( pSeg->iLeafPgno==pSeg->iTermLeafPgno ){
+ iStart = pSeg->iTermLeafOffset;
+ }else{
+ iStart = fts5GetU16(&aPg[0]);
+ }
+
+ iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta);
+ assert_nc( iSOP<=pSeg->iLeafOffset );
+
+ if( bDetailNone ){
+ while( iSOP<pSeg->iLeafOffset ){
+ if( aPg[iSOP]==0x00 ) iSOP++;
+ if( aPg[iSOP]==0x00 ) iSOP++;
+ iStart = iSOP;
+ iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta);
+ }
+
+ iNextOff = iSOP;
+ if( iNextOff<pSeg->iEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++;
+ if( iNextOff<pSeg->iEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++;
+
+ }else{
+ int nPos = 0;
+ iSOP += fts5GetVarint32(&aPg[iSOP], nPos);
+ while( iSOP<pSeg->iLeafOffset ){
+ iStart = iSOP + (nPos/2);
+ iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta);
+ iSOP += fts5GetVarint32(&aPg[iSOP], nPos);
+ }
+ assert_nc( iSOP==pSeg->iLeafOffset );
+ iNextOff = pSeg->iLeafOffset + pSeg->nPos;
+ }
+ }
+
+ iOff = iStart;
+
+ /* If the position-list for the entry being removed flows over past
+ ** the end of this page, delete the portion of the position-list on the
+ ** next page and beyond.
+ **
+ ** Set variable bLastInDoclist to true if this entry happens
+ ** to be the last rowid in the doclist for its term. */
+ if( iNextOff>=iPgIdx ){
+ int pgno = pSeg->iLeafPgno+1;
+ fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist);
+ iNextOff = iPgIdx;
+ }
+
+ if( pSeg->bDel==0 ){
+ if( iNextOff!=iPgIdx ){
+ /* Loop through the page-footer. If iNextOff (offset of the
+ ** entry following the one we are removing) is equal to the
+ ** offset of a key on this page, then the entry is the last
+ ** in its doclist. */
+ int iKeyOff = 0;
+ for(iIdx=0; iIdx<nIdx; /* no-op */){
+ u32 iVal = 0;
+ iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
+ iKeyOff += iVal;
+ if( iKeyOff==iNextOff ){
+ bLastInDoclist = 1;
+ }
+ }
+ }
+
+ /* If this is (a) the first rowid on a page and (b) is not followed by
+ ** another position list on the same page, set the "first-rowid" field
+ ** of the header to 0. */
+ if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist || iNextOff==iPgIdx) ){
+ fts5PutU16(&aPg[0], 0);
+ }
+ }
+
+ if( pSeg->bDel ){
+ iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta);
+ aPg[iOff++] = 0x01;
+ }else if( bLastInDoclist==0 ){
+ if( iNextOff!=iPgIdx ){
+ u64 iNextDelta = 0;
+ iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta);
+ iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta + iNextDelta);
+ }
+ }else if(
+ pSeg->iLeafPgno==pSeg->iTermLeafPgno
+ && iStart==pSeg->iTermLeafOffset
+ ){
+ /* The entry being removed was the only position list in its
+ ** doclist. Therefore the term needs to be removed as well. */
+ int iKey = 0;
+ int iKeyOff = 0;
+
+ /* Set iKeyOff to the offset of the term that will be removed - the
+ ** last offset in the footer that is not greater than iStart. */
+ for(iIdx=0; iIdx<nIdx; iKey++){
+ u32 iVal = 0;
+ iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
+ if( (iKeyOff+iVal)>(u32)iStart ) break;
+ iKeyOff += iVal;
+ }
+ assert_nc( iKey>=1 );
+
+ /* Set iDelKeyOff to the value of the footer entry to remove from
+ ** the page. */
+ iDelKeyOff = iOff = iKeyOff;
+
+ if( iNextOff!=iPgIdx ){
+ /* This is the only position-list associated with the term, and there
+ ** is another term following it on this page. So the subsequent term
+ ** needs to be moved to replace the term associated with the entry
+ ** being removed. */
+ int nPrefix = 0;
+ int nSuffix = 0;
+ int nPrefix2 = 0;
+ int nSuffix2 = 0;
+
+ iDelKeyOff = iNextOff;
+ iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2);
+ iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2);
+
+ if( iKey!=1 ){
+ iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix);
+ }
+ iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix);
+
+ nPrefix = MIN(nPrefix, nPrefix2);
+ nSuffix = (nPrefix2 + nSuffix2) - nPrefix;
+
+ if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ if( iKey!=1 ){
+ iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix);
+ }
+ iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix);
+ if( nPrefix2>pSeg->term.n ){
+ p->rc = FTS5_CORRUPT;
+ }else if( nPrefix2>nPrefix ){
+ memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix);
+ iOff += (nPrefix2-nPrefix);
+ }
+ memmove(&aPg[iOff], &aPg[iNextOff], nSuffix2);
+ iOff += nSuffix2;
+ iNextOff += nSuffix2;
+ }
+ }
+ }else if( iStart==4 ){
+ int iPgno;
+
+ assert_nc( pSeg->iLeafPgno>pSeg->iTermLeafPgno );
+ /* The entry being removed may be the only position list in
+ ** its doclist. */
+ for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){
+ Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno));
+ int bEmpty = (pPg && pPg->nn==4);
+ fts5DataRelease(pPg);
+ if( bEmpty==0 ) break;
+ }
+
+ if( iPgno==pSeg->iTermLeafPgno ){
+ i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno);
+ Fts5Data *pTerm = fts5DataRead(p, iId);
+ if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){
+ u8 *aTermIdx = &pTerm->p[pTerm->szLeaf];
+ int nTermIdx = pTerm->nn - pTerm->szLeaf;
+ int iTermIdx = 0;
+ int iTermOff = 0;
+
+ while( 1 ){
+ u32 iVal = 0;
+ int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal);
+ iTermOff += iVal;
+ if( (iTermIdx+nByte)>=nTermIdx ) break;
+ iTermIdx += nByte;
+ }
+ nTermIdx = iTermIdx;
+
+ memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx);
+ fts5PutU16(&pTerm->p[2], iTermOff);
+
+ fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx);
+ if( nTermIdx==0 ){
+ fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno);
+ }
+ }
+ fts5DataRelease(pTerm);
+ }
+ }
+
+ /* Assuming no error has occurred, this block does final edits to the
+ ** leaf page before writing it back to disk. Input variables are:
+ **
+ ** nPg: Total initial size of leaf page.
+ ** iPgIdx: Initial offset of page footer.
+ **
+ ** iOff: Offset to move data to
+ ** iNextOff: Offset to move data from
+ */
+ if( p->rc==SQLITE_OK ){
+ const int nMove = nPg - iNextOff; /* Number of bytes to move */
+ int nShift = iNextOff - iOff; /* Distance to move them */
+
+ int iPrevKeyOut = 0;
+ int iKeyIn = 0;
+
+ memmove(&aPg[iOff], &aPg[iNextOff], nMove);
+ iPgIdx -= nShift;
+ nPg = iPgIdx;
+ fts5PutU16(&aPg[2], iPgIdx);
+
+ for(iIdx=0; iIdx<nIdx; /* no-op */){
+ u32 iVal = 0;
+ iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
+ iKeyIn += iVal;
+ if( iKeyIn!=iDelKeyOff ){
+ int iKeyOut = (iKeyIn - (iKeyIn>iOff ? nShift : 0));
+ nPg += sqlite3Fts5PutVarint(&aPg[nPg], iKeyOut - iPrevKeyOut);
+ iPrevKeyOut = iKeyOut;
+ }
+ }
+
+ if( iPgIdx==nPg && nIdx>0 && pSeg->iLeafPgno!=1 ){
+ fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iLeafPgno);
+ }
+
+ assert_nc( nPg>4 || fts5GetU16(aPg)==0 );
+ fts5DataWrite(p, FTS5_SEGMENT_ROWID(iSegid,pSeg->iLeafPgno), aPg, nPg);
+ }
+ sqlite3_free(aIdx);
+}
+
+/*
+** This is called as part of flushing a delete to disk in 'secure-delete'
+** mode. It edits the segments within the database described by argument
+** pStruct to remove the entries for term zTerm, rowid iRowid.
+*/
+static void fts5FlushSecureDelete(
+ Fts5Index *p,
+ Fts5Structure *pStruct,
+ const char *zTerm,
+ int nTerm,
+ i64 iRowid
+){
+ const int f = FTS5INDEX_QUERY_SKIPHASH;
+ Fts5Iter *pIter = 0; /* Used to find term instance */
+
+ fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter);
+ if( fts5MultiIterEof(p, pIter)==0 ){
+ i64 iThis = fts5MultiIterRowid(pIter);
+ if( iThis<iRowid ){
+ fts5MultiIterNextFrom(p, pIter, iRowid);
+ }
+
+ if( p->rc==SQLITE_OK
+ && fts5MultiIterEof(p, pIter)==0
+ && iRowid==fts5MultiIterRowid(pIter)
+ ){
+ Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
+ fts5DoSecureDelete(p, pSeg);
+ }
+ }
+
+ fts5MultiIterFree(pIter);
+}
+
+
+/*
+** Flush the contents of in-memory hash table iHash to a new level-0
** segment on disk. Also update the corresponding structure record.
**
-** If an error occurs, set the Fts5Index.rc error code. If an error has
+** If an error occurs, set the Fts5Index.rc error code. If an error has
** already occurred, this function is a no-op.
*/
static void fts5FlushOneHash(Fts5Index *p){
@@ -211119,141 +244042,197 @@ static void fts5FlushOneHash(Fts5Index *p){
/* Obtain a reference to the index structure and allocate a new segment-id
** for the new level-0 segment. */
pStruct = fts5StructureRead(p);
- iSegid = fts5AllocateSegid(p, pStruct);
fts5StructureInvalidate(p);
- if( iSegid ){
- const int pgsz = p->pConfig->pgsz;
- int eDetail = p->pConfig->eDetail;
- Fts5StructureSegment *pSeg; /* New segment within pStruct */
- Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */
- Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */
+ if( sqlite3Fts5HashIsEmpty(pHash)==0 ){
+ iSegid = fts5AllocateSegid(p, pStruct);
+ if( iSegid ){
+ const int pgsz = p->pConfig->pgsz;
+ int eDetail = p->pConfig->eDetail;
+ int bSecureDelete = p->pConfig->bSecureDelete;
+ Fts5StructureSegment *pSeg; /* New segment within pStruct */
+ Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */
+ Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */
+
+ Fts5SegWriter writer;
+ fts5WriteInit(p, &writer, iSegid);
+
+ pBuf = &writer.writer.buf;
+ pPgidx = &writer.writer.pgidx;
+
+ /* fts5WriteInit() should have initialized the buffers to (most likely)
+ ** the maximum space required. */
+ assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) );
+ assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) );
+
+ /* Begin scanning through hash table entries. This loop runs once for each
+ ** term/doclist currently stored within the hash table. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0);
+ }
+ while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
+ const char *zTerm; /* Buffer containing term */
+ int nTerm; /* Size of zTerm in bytes */
+ const u8 *pDoclist; /* Pointer to doclist for this term */
+ int nDoclist; /* Size of doclist in bytes */
- Fts5SegWriter writer;
- fts5WriteInit(p, &writer, iSegid);
+ /* Get the term and doclist for this entry. */
+ sqlite3Fts5HashScanEntry(pHash, &zTerm, &nTerm, &pDoclist, &nDoclist);
+ if( bSecureDelete==0 ){
+ fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm);
+ if( p->rc!=SQLITE_OK ) break;
+ assert( writer.bFirstRowidInPage==0 );
+ }
- pBuf = &writer.writer.buf;
- pPgidx = &writer.writer.pgidx;
+ if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
+ /* The entire doclist will fit on the current leaf. */
+ fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist);
+ }else{
+ int bTermWritten = !bSecureDelete;
+ i64 iRowid = 0;
+ i64 iPrev = 0;
+ int iOff = 0;
+
+ /* The entire doclist will not fit on this leaf. The following
+ ** loop iterates through the poslists that make up the current
+ ** doclist. */
+ while( p->rc==SQLITE_OK && iOff<nDoclist ){
+ u64 iDelta = 0;
+ iOff += fts5GetVarint(&pDoclist[iOff], &iDelta);
+ iRowid += iDelta;
+
+ /* If in secure delete mode, and if this entry in the poslist is
+ ** in fact a delete, then edit the existing segments directly
+ ** using fts5FlushSecureDelete(). */
+ if( bSecureDelete ){
+ if( eDetail==FTS5_DETAIL_NONE ){
+ if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
+ fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
+ iOff++;
+ if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
+ iOff++;
+ nDoclist = 0;
+ }else{
+ continue;
+ }
+ }
+ }else if( (pDoclist[iOff] & 0x01) ){
+ fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
+ if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){
+ iOff++;
+ continue;
+ }
+ }
+ }
- /* fts5WriteInit() should have initialized the buffers to (most likely)
- ** the maximum space required. */
- assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) );
- assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) );
+ if( p->rc==SQLITE_OK && bTermWritten==0 ){
+ fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm);
+ bTermWritten = 1;
+ assert( p->rc!=SQLITE_OK || writer.bFirstRowidInPage==0 );
+ }
- /* Begin scanning through hash table entries. This loop runs once for each
- ** term/doclist currently stored within the hash table. */
- if( p->rc==SQLITE_OK ){
- p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0);
- }
- while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
- const char *zTerm; /* Buffer containing term */
- const u8 *pDoclist; /* Pointer to doclist for this term */
- int nDoclist; /* Size of doclist in bytes */
-
- /* Write the term for this entry to disk. */
- sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist);
- fts5WriteAppendTerm(p, &writer, (int)strlen(zTerm), (const u8*)zTerm);
-
- assert( writer.bFirstRowidInPage==0 );
- if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
- /* The entire doclist will fit on the current leaf. */
- fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist);
- }else{
- i64 iRowid = 0;
- i64 iDelta = 0;
- int iOff = 0;
-
- /* The entire doclist will not fit on this leaf. The following
- ** loop iterates through the poslists that make up the current
- ** doclist. */
- while( p->rc==SQLITE_OK && iOff<nDoclist ){
- iOff += fts5GetVarint(&pDoclist[iOff], (u64*)&iDelta);
- iRowid += iDelta;
-
- if( writer.bFirstRowidInPage ){
- fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */
- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid);
- writer.bFirstRowidInPage = 0;
- fts5WriteDlidxAppend(p, &writer, iRowid);
- }else{
- pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta);
- }
- assert( pBuf->n<=pBuf->nSpace );
+ if( writer.bFirstRowidInPage ){
+ fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid);
+ writer.bFirstRowidInPage = 0;
+ fts5WriteDlidxAppend(p, &writer, iRowid);
+ }else{
+ u64 iRowidDelta = (u64)iRowid - (u64)iPrev;
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowidDelta);
+ }
+ if( p->rc!=SQLITE_OK ) break;
+ assert( pBuf->n<=pBuf->nSpace );
+ iPrev = iRowid;
- if( eDetail==FTS5_DETAIL_NONE ){
- if( iOff<nDoclist && pDoclist[iOff]==0 ){
- pBuf->p[pBuf->n++] = 0;
- iOff++;
+ if( eDetail==FTS5_DETAIL_NONE ){
if( iOff<nDoclist && pDoclist[iOff]==0 ){
pBuf->p[pBuf->n++] = 0;
iOff++;
+ if( iOff<nDoclist && pDoclist[iOff]==0 ){
+ pBuf->p[pBuf->n++] = 0;
+ iOff++;
+ }
+ }
+ if( (pBuf->n + pPgidx->n)>=pgsz ){
+ fts5WriteFlushLeaf(p, &writer);
}
- }
- if( (pBuf->n + pPgidx->n)>=pgsz ){
- fts5WriteFlushLeaf(p, &writer);
- }
- }else{
- int bDummy;
- int nPos;
- int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy);
- nCopy += nPos;
- if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){
- /* The entire poslist will fit on the current leaf. So copy
- ** it in one go. */
- fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy);
}else{
- /* The entire poslist will not fit on this leaf. So it needs
- ** to be broken into sections. The only qualification being
- ** that each varint must be stored contiguously. */
- const u8 *pPoslist = &pDoclist[iOff];
- int iPos = 0;
- while( p->rc==SQLITE_OK ){
- int nSpace = pgsz - pBuf->n - pPgidx->n;
- int n = 0;
- if( (nCopy - iPos)<=nSpace ){
- n = nCopy - iPos;
- }else{
- n = fts5PoslistPrefix(&pPoslist[iPos], nSpace);
- }
- assert( n>0 );
- fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n);
- iPos += n;
- if( (pBuf->n + pPgidx->n)>=pgsz ){
- fts5WriteFlushLeaf(p, &writer);
+ int bDel = 0;
+ int nPos = 0;
+ int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDel);
+ if( bDel && bSecureDelete ){
+ fts5BufferAppendVarint(&p->rc, pBuf, nPos*2);
+ iOff += nCopy;
+ nCopy = nPos;
+ }else{
+ nCopy += nPos;
+ }
+ if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){
+ /* The entire poslist will fit on the current leaf. So copy
+ ** it in one go. */
+ fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy);
+ }else{
+ /* The entire poslist will not fit on this leaf. So it needs
+ ** to be broken into sections. The only qualification being
+ ** that each varint must be stored contiguously. */
+ const u8 *pPoslist = &pDoclist[iOff];
+ int iPos = 0;
+ while( p->rc==SQLITE_OK ){
+ int nSpace = pgsz - pBuf->n - pPgidx->n;
+ int n = 0;
+ if( (nCopy - iPos)<=nSpace ){
+ n = nCopy - iPos;
+ }else{
+ n = fts5PoslistPrefix(&pPoslist[iPos], nSpace);
+ }
+ assert( n>0 );
+ fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n);
+ iPos += n;
+ if( (pBuf->n + pPgidx->n)>=pgsz ){
+ fts5WriteFlushLeaf(p, &writer);
+ }
+ if( iPos>=nCopy ) break;
}
- if( iPos>=nCopy ) break;
}
+ iOff += nCopy;
}
- iOff += nCopy;
}
}
- }
- /* TODO2: Doclist terminator written here. */
- /* pBuf->p[pBuf->n++] = '\0'; */
- assert( pBuf->n<=pBuf->nSpace );
- sqlite3Fts5HashScanNext(pHash);
- }
- sqlite3Fts5HashClear(pHash);
- fts5WriteFinish(p, &writer, &pgnoLast);
+ /* TODO2: Doclist terminator written here. */
+ /* pBuf->p[pBuf->n++] = '\0'; */
+ assert( pBuf->n<=pBuf->nSpace );
+ if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash);
+ }
+ fts5WriteFinish(p, &writer, &pgnoLast);
- /* Update the Fts5Structure. It is written back to the database by the
- ** fts5StructureRelease() call below. */
- if( pStruct->nLevel==0 ){
- fts5StructureAddLevel(&p->rc, &pStruct);
- }
- fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0);
- if( p->rc==SQLITE_OK ){
- pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ];
- pSeg->iSegid = iSegid;
- pSeg->pgnoFirst = 1;
- pSeg->pgnoLast = pgnoLast;
- pStruct->nSegment++;
+ assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 );
+ if( pgnoLast>0 ){
+ /* Update the Fts5Structure. It is written back to the database by the
+ ** fts5StructureRelease() call below. */
+ if( pStruct->nLevel==0 ){
+ fts5StructureAddLevel(&p->rc, &pStruct);
+ }
+ fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0);
+ if( p->rc==SQLITE_OK ){
+ pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ];
+ pSeg->iSegid = iSegid;
+ pSeg->pgnoFirst = 1;
+ pSeg->pgnoLast = pgnoLast;
+ if( pStruct->nOriginCntr>0 ){
+ pSeg->iOrigin1 = pStruct->nOriginCntr;
+ pSeg->iOrigin2 = pStruct->nOriginCntr;
+ pSeg->nEntry = p->nPendingRow;
+ pStruct->nOriginCntr++;
+ }
+ pStruct->nSegment++;
+ }
+ fts5StructurePromote(p, 0, pStruct);
+ }
}
- fts5StructurePromote(p, 0, pStruct);
}
- fts5IndexAutomerge(p, &pStruct, pgnoLast);
+ fts5IndexAutomerge(p, &pStruct, pgnoLast + p->nContentlessDelete);
fts5IndexCrisismerge(p, &pStruct);
fts5StructureWrite(p, pStruct);
fts5StructureRelease(pStruct);
@@ -211264,36 +244243,52 @@ static void fts5FlushOneHash(Fts5Index *p){
*/
static void fts5IndexFlush(Fts5Index *p){
/* Unless it is empty, flush the hash table to disk */
- if( p->nPendingData ){
+ if( p->flushRc ){
+ p->rc = p->flushRc;
+ return;
+ }
+ if( p->nPendingData || p->nContentlessDelete ){
assert( p->pHash );
- p->nPendingData = 0;
fts5FlushOneHash(p);
+ if( p->rc==SQLITE_OK ){
+ sqlite3Fts5HashClear(p->pHash);
+ p->nPendingData = 0;
+ p->nPendingRow = 0;
+ p->nContentlessDelete = 0;
+ }else if( p->nPendingData || p->nContentlessDelete ){
+ p->flushRc = p->rc;
+ }
}
}
static Fts5Structure *fts5IndexOptimizeStruct(
- Fts5Index *p,
+ Fts5Index *p,
Fts5Structure *pStruct
){
Fts5Structure *pNew = 0;
- int nByte = sizeof(Fts5Structure);
+ sqlite3_int64 nByte = sizeof(Fts5Structure);
int nSeg = pStruct->nSegment;
int i;
/* Figure out if this structure requires optimization. A structure does
** not require optimization if either:
**
- ** + it consists of fewer than two segments, or
- ** + all segments are on the same level, or
- ** + all segments except one are currently inputs to a merge operation.
+ ** 1. it consists of fewer than two segments, or
+ ** 2. all segments are on the same level, or
+ ** 3. all segments except one are currently inputs to a merge operation.
**
- ** In the first case, return NULL. In the second, increment the ref-count
- ** on *pStruct and return a copy of the pointer to it.
+ ** In the first case, if there are no tombstone hash pages, return NULL. In
+ ** the second, increment the ref-count on *pStruct and return a copy of the
+ ** pointer to it.
*/
- if( nSeg<2 ) return 0;
+ if( nSeg==0 ) return 0;
for(i=0; i<pStruct->nLevel; i++){
int nThis = pStruct->aLevel[i].nSeg;
- if( nThis==nSeg || (nThis==nSeg-1 && pStruct->aLevel[i].nMerge==nThis) ){
+ int nMerge = pStruct->aLevel[i].nMerge;
+ if( nThis>0 && (nThis==nSeg || (nThis==nSeg-1 && nMerge==nThis)) ){
+ if( nSeg==1 && nThis==1 && pStruct->aLevel[i].aSeg[0].nPgTombstone==0 ){
+ return 0;
+ }
fts5StructureRef(pStruct);
return pStruct;
}
@@ -211306,10 +244301,11 @@ static Fts5Structure *fts5IndexOptimizeStruct(
if( pNew ){
Fts5StructureLevel *pLvl;
nByte = nSeg * sizeof(Fts5StructureSegment);
- pNew->nLevel = pStruct->nLevel+1;
+ pNew->nLevel = MIN(pStruct->nLevel+1, FTS5_MAX_LEVEL);
pNew->nRef = 1;
pNew->nWriteCounter = pStruct->nWriteCounter;
- pLvl = &pNew->aLevel[pStruct->nLevel];
+ pNew->nOriginCntr = pStruct->nOriginCntr;
+ pLvl = &pNew->aLevel[pNew->nLevel-1];
pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( pLvl->aSeg ){
int iLvl, iSeg;
@@ -211339,7 +244335,9 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){
assert( p->rc==SQLITE_OK );
fts5IndexFlush(p);
+ assert( p->rc!=SQLITE_OK || p->nContentlessDelete==0 );
pStruct = fts5StructureRead(p);
+ assert( p->rc!=SQLITE_OK || pStruct!=0 );
fts5StructureInvalidate(p);
if( pStruct ){
@@ -211360,7 +244358,7 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){
fts5StructureRelease(pNew);
}
- return fts5IndexReturn(p);
+ return fts5IndexReturn(p);
}
/*
@@ -211368,7 +244366,10 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){
** INSERT command.
*/
static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
- Fts5Structure *pStruct = fts5StructureRead(p);
+ Fts5Structure *pStruct = 0;
+
+ fts5IndexFlush(p);
+ pStruct = fts5StructureRead(p);
if( pStruct ){
int nMin = p->pConfig->nUsermerge;
fts5StructureInvalidate(p);
@@ -211376,7 +244377,7 @@ static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
Fts5Structure *pNew = fts5IndexOptimizeStruct(p, pStruct);
fts5StructureRelease(pStruct);
pStruct = pNew;
- nMin = 2;
+ nMin = 1;
nMerge = nMerge*-1;
}
if( pStruct && pStruct->nLevel ){
@@ -211391,7 +244392,7 @@ static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
static void fts5AppendRowid(
Fts5Index *p,
- i64 iDelta,
+ u64 iDelta,
Fts5Iter *pUnused,
Fts5Buffer *pBuf
){
@@ -211401,16 +244402,18 @@ static void fts5AppendRowid(
static void fts5AppendPoslist(
Fts5Index *p,
- i64 iDelta,
+ u64 iDelta,
Fts5Iter *pMulti,
Fts5Buffer *pBuf
){
int nData = pMulti->base.nData;
+ int nByte = nData + 9 + 9 + FTS5_DATA_ZERO_PADDING;
assert( nData>0 );
- if( p->rc==SQLITE_OK && 0==fts5BufferGrow(&p->rc, pBuf, nData+9+9) ){
+ if( p->rc==SQLITE_OK && 0==fts5BufferGrow(&p->rc, pBuf, nByte) ){
fts5BufferSafeAppendVarint(pBuf, iDelta);
fts5BufferSafeAppendVarint(pBuf, nData*2);
fts5BufferSafeAppendBlob(pBuf, pMulti->base.pData, nData);
+ memset(&pBuf->p[pBuf->n], 0, FTS5_DATA_ZERO_PADDING);
}
}
@@ -211418,7 +244421,7 @@ static void fts5AppendPoslist(
static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist;
- assert( pIter->aPoslist );
+ assert( pIter->aPoslist || (p==0 && pIter->aPoslist==0) );
if( p>=pIter->aEof ){
pIter->aPoslist = 0;
}else{
@@ -211438,17 +244441,22 @@ static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
}
pIter->aPoslist = p;
+ if( &pIter->aPoslist[pIter->nPoslist]>pIter->aEof ){
+ pIter->aPoslist = 0;
+ }
}
}
static void fts5DoclistIterInit(
- Fts5Buffer *pBuf,
+ Fts5Buffer *pBuf,
Fts5DoclistIter *pIter
){
memset(pIter, 0, sizeof(*pIter));
- pIter->aPoslist = pBuf->p;
- pIter->aEof = &pBuf->p[pBuf->n];
- fts5DoclistIterNext(pIter);
+ if( pBuf->n>0 ){
+ pIter->aPoslist = pBuf->p;
+ pIter->aEof = &pBuf->p[pBuf->n];
+ fts5DoclistIterNext(pIter);
+ }
}
#if 0
@@ -211469,10 +244477,10 @@ static void fts5MergeAppendDocid(
}
#endif
-#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \
- assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \
- fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \
- (iLastRowid) = (iRowid); \
+#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \
+ assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \
+ fts5BufferSafeAppendVarint((pBuf), (u64)(iRowid) - (u64)(iLastRowid)); \
+ (iLastRowid) = (iRowid); \
}
/*
@@ -211502,16 +244510,20 @@ static void fts5NextRowid(Fts5Buffer *pBuf, int *piOff, i64 *piRowid){
static void fts5MergeRowidLists(
Fts5Index *p, /* FTS5 backend object */
Fts5Buffer *p1, /* First list to merge */
- Fts5Buffer *p2 /* Second list to merge */
+ int nBuf, /* Number of entries in apBuf[] */
+ Fts5Buffer *aBuf /* Array of other lists to merge into p1 */
){
int i1 = 0;
int i2 = 0;
i64 iRowid1 = 0;
i64 iRowid2 = 0;
i64 iOut = 0;
-
+ Fts5Buffer *p2 = &aBuf[0];
Fts5Buffer out;
+
+ (void)nBuf;
memset(&out, 0, sizeof(out));
+ assert( nBuf==1 );
sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n);
if( p->rc ) return;
@@ -211538,162 +244550,225 @@ static void fts5MergeRowidLists(
fts5BufferFree(&out);
}
+typedef struct PrefixMerger PrefixMerger;
+struct PrefixMerger {
+ Fts5DoclistIter iter; /* Doclist iterator */
+ i64 iPos; /* For iterating through a position list */
+ int iOff;
+ u8 *aPos;
+ PrefixMerger *pNext; /* Next in docid/poslist order */
+};
+
+static void fts5PrefixMergerInsertByRowid(
+ PrefixMerger **ppHead,
+ PrefixMerger *p
+){
+ if( p->iter.aPoslist ){
+ PrefixMerger **pp = ppHead;
+ while( *pp && p->iter.iRowid>(*pp)->iter.iRowid ){
+ pp = &(*pp)->pNext;
+ }
+ p->pNext = *pp;
+ *pp = p;
+ }
+}
+
+static void fts5PrefixMergerInsertByPosition(
+ PrefixMerger **ppHead,
+ PrefixMerger *p
+){
+ if( p->iPos>=0 ){
+ PrefixMerger **pp = ppHead;
+ while( *pp && p->iPos>(*pp)->iPos ){
+ pp = &(*pp)->pNext;
+ }
+ p->pNext = *pp;
+ *pp = p;
+ }
+}
+
+
/*
-** Buffers p1 and p2 contain doclists. This function merges the content
-** of the two doclists together and sets buffer p1 to the result before
-** returning.
-**
-** If an error occurs, an error code is left in p->rc. If an error has
-** already occurred, this function is a no-op.
+** Array aBuf[] contains nBuf doclists. These are all merged in with the
+** doclist in buffer p1.
*/
static void fts5MergePrefixLists(
Fts5Index *p, /* FTS5 backend object */
Fts5Buffer *p1, /* First list to merge */
- Fts5Buffer *p2 /* Second list to merge */
-){
- if( p2->n ){
- i64 iLastRowid = 0;
- Fts5DoclistIter i1;
- Fts5DoclistIter i2;
- Fts5Buffer out = {0, 0, 0};
- Fts5Buffer tmp = {0, 0, 0};
-
- /* The maximum size of the output is equal to the sum of the two
- ** input sizes + 1 varint (9 bytes). The extra varint is because if the
- ** first rowid in one input is a large negative number, and the first in
- ** the other a non-negative number, the delta for the non-negative
- ** number will be larger on disk than the literal integer value
- ** was. */
- if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n + 9) ) return;
- fts5DoclistIterInit(p1, &i1);
- fts5DoclistIterInit(p2, &i2);
+ int nBuf, /* Number of buffers in array aBuf[] */
+ Fts5Buffer *aBuf /* Other lists to merge in */
+){
+#define fts5PrefixMergerNextPosition(p) \
+ sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos)
+#define FTS5_MERGE_NLIST 16
+ PrefixMerger aMerger[FTS5_MERGE_NLIST];
+ PrefixMerger *pHead = 0;
+ int i;
+ int nOut = 0;
+ Fts5Buffer out = {0, 0, 0};
+ Fts5Buffer tmp = {0, 0, 0};
+ i64 iLastRowid = 0;
+
+ /* Initialize a doclist-iterator for each input buffer. Arrange them in
+ ** a linked-list starting at pHead in ascending order of rowid. Avoid
+ ** linking any iterators already at EOF into the linked list at all. */
+ assert( nBuf+1<=(int)(sizeof(aMerger)/sizeof(aMerger[0])) );
+ memset(aMerger, 0, sizeof(PrefixMerger)*(nBuf+1));
+ pHead = &aMerger[nBuf];
+ fts5DoclistIterInit(p1, &pHead->iter);
+ for(i=0; i<nBuf; i++){
+ fts5DoclistIterInit(&aBuf[i], &aMerger[i].iter);
+ fts5PrefixMergerInsertByRowid(&pHead, &aMerger[i]);
+ nOut += aBuf[i].n;
+ }
+ if( nOut==0 ) return;
+ nOut += p1->n + 9 + 10*nBuf;
+
+ /* The maximum size of the output is equal to the sum of the
+ ** input sizes + 1 varint (9 bytes). The extra varint is because if the
+ ** first rowid in one input is a large negative number, and the first in
+ ** the other a non-negative number, the delta for the non-negative
+ ** number will be larger on disk than the literal integer value
+ ** was.
+ **
+ ** Or, if the input position-lists are corrupt, then the output might
+ ** include up to (nBuf+1) extra 10-byte positions created by interpreting -1
+ ** (the value PoslistNext64() uses for EOF) as a position and appending
+ ** it to the output. This can happen at most once for each input
+ ** position-list, hence (nBuf+1) 10 byte paddings. */
+ if( sqlite3Fts5BufferSize(&p->rc, &out, nOut) ) return;
+
+ while( pHead ){
+ fts5MergeAppendDocid(&out, iLastRowid, pHead->iter.iRowid);
+
+ if( pHead->pNext && iLastRowid==pHead->pNext->iter.iRowid ){
+ /* Merge data from two or more poslists */
+ i64 iPrev = 0;
+ int nTmp = FTS5_DATA_ZERO_PADDING;
+ int nMerge = 0;
+ PrefixMerger *pSave = pHead;
+ PrefixMerger *pThis = 0;
+ int nTail = 0;
+
+ pHead = 0;
+ while( pSave && pSave->iter.iRowid==iLastRowid ){
+ PrefixMerger *pNext = pSave->pNext;
+ pSave->iOff = 0;
+ pSave->iPos = 0;
+ pSave->aPos = &pSave->iter.aPoslist[pSave->iter.nSize];
+ fts5PrefixMergerNextPosition(pSave);
+ nTmp += pSave->iter.nPoslist + 10;
+ nMerge++;
+ fts5PrefixMergerInsertByPosition(&pHead, pSave);
+ pSave = pNext;
+ }
+
+ if( pHead==0 || pHead->pNext==0 ){
+ p->rc = FTS5_CORRUPT;
+ break;
+ }
- while( 1 ){
- if( i1.iRowid<i2.iRowid ){
- /* Copy entry from i1 */
- fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
- fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.nPoslist+i1.nSize);
- fts5DoclistIterNext(&i1);
- if( i1.aPoslist==0 ) break;
- }
- else if( i2.iRowid!=i1.iRowid ){
- /* Copy entry from i2 */
- fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
- fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize);
- fts5DoclistIterNext(&i2);
- if( i2.aPoslist==0 ) break;
+ /* See the earlier comment in this function for an explanation of why
+ ** corrupt input position lists might cause the output to consume
+ ** at most nMerge*10 bytes of unexpected space. */
+ if( sqlite3Fts5BufferSize(&p->rc, &tmp, nTmp+nMerge*10) ){
+ break;
}
- else{
- /* Merge the two position lists. */
- i64 iPos1 = 0;
- i64 iPos2 = 0;
- int iOff1 = 0;
- int iOff2 = 0;
- u8 *a1 = &i1.aPoslist[i1.nSize];
- u8 *a2 = &i2.aPoslist[i2.nSize];
-
- i64 iPrev = 0;
- Fts5PoslistWriter writer;
- memset(&writer, 0, sizeof(writer));
-
- fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
- fts5BufferZero(&tmp);
- sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist);
- if( p->rc ) break;
+ fts5BufferZero(&tmp);
- sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
- sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
- assert( iPos1>=0 && iPos2>=0 );
+ pThis = pHead;
+ pHead = pThis->pNext;
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos);
+ fts5PrefixMergerNextPosition(pThis);
+ fts5PrefixMergerInsertByPosition(&pHead, pThis);
- if( iPos1<iPos2 ){
- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
- sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
- }else{
- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
- sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
+ while( pHead->pNext ){
+ pThis = pHead;
+ if( pThis->iPos!=iPrev ){
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos);
}
+ fts5PrefixMergerNextPosition(pThis);
+ pHead = pThis->pNext;
+ fts5PrefixMergerInsertByPosition(&pHead, pThis);
+ }
- if( iPos1>=0 && iPos2>=0 ){
- while( 1 ){
- if( iPos1<iPos2 ){
- if( iPos1!=iPrev ){
- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
- }
- sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
- if( iPos1<0 ) break;
- }else{
- assert( iPos2!=iPrev );
- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
- sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
- if( iPos2<0 ) break;
- }
- }
- }
+ if( pHead->iPos!=iPrev ){
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pHead->iPos);
+ }
+ nTail = pHead->iter.nPoslist - pHead->iOff;
- if( iPos1>=0 ){
- if( iPos1!=iPrev ){
- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
- }
- fts5BufferSafeAppendBlob(&tmp, &a1[iOff1], i1.nPoslist-iOff1);
- }else{
- assert( iPos2>=0 && iPos2!=iPrev );
- sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
- fts5BufferSafeAppendBlob(&tmp, &a2[iOff2], i2.nPoslist-iOff2);
- }
+ /* WRITEPOSLISTSIZE */
+ assert_nc( tmp.n+nTail<=nTmp );
+ assert( tmp.n+nTail<=nTmp+nMerge*10 );
+ if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING ){
+ if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
+ break;
+ }
+ fts5BufferSafeAppendVarint(&out, (tmp.n+nTail) * 2);
+ fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
+ if( nTail>0 ){
+ fts5BufferSafeAppendBlob(&out, &pHead->aPos[pHead->iOff], nTail);
+ }
- /* WRITEPOSLISTSIZE */
- fts5BufferSafeAppendVarint(&out, tmp.n * 2);
- fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
- fts5DoclistIterNext(&i1);
- fts5DoclistIterNext(&i2);
- if( i1.aPoslist==0 || i2.aPoslist==0 ) break;
+ pHead = pSave;
+ for(i=0; i<nBuf+1; i++){
+ PrefixMerger *pX = &aMerger[i];
+ if( pX->iter.aPoslist && pX->iter.iRowid==iLastRowid ){
+ fts5DoclistIterNext(&pX->iter);
+ fts5PrefixMergerInsertByRowid(&pHead, pX);
+ }
}
- }
- if( i1.aPoslist ){
- fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
- fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.aEof - i1.aPoslist);
- }
- else if( i2.aPoslist ){
- fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
- fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.aEof - i2.aPoslist);
+ }else{
+ /* Copy poslist from pHead to output */
+ PrefixMerger *pThis = pHead;
+ Fts5DoclistIter *pI = &pThis->iter;
+ fts5BufferSafeAppendBlob(&out, pI->aPoslist, pI->nPoslist+pI->nSize);
+ fts5DoclistIterNext(pI);
+ pHead = pThis->pNext;
+ fts5PrefixMergerInsertByRowid(&pHead, pThis);
}
- assert( out.n<=(p1->n+p2->n+9) );
-
- fts5BufferSet(&p->rc, p1, out.n, out.p);
- fts5BufferFree(&tmp);
- fts5BufferFree(&out);
}
+
+ fts5BufferFree(p1);
+ fts5BufferFree(&tmp);
+ memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
+ *p1 = out;
}
static void fts5SetupPrefixIter(
Fts5Index *p, /* Index to read from */
int bDesc, /* True for "ORDER BY rowid DESC" */
- const u8 *pToken, /* Buffer containing prefix to match */
+ int iIdx, /* Index to scan for data */
+ u8 *pToken, /* Buffer containing prefix to match */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset, /* Restrict matches to these columns */
- Fts5Iter **ppIter /* OUT: New iterator */
+ Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
Fts5Buffer *aBuf;
- const int nBuf = 32;
+ int nBuf = 32;
+ int nMerge = 1;
- void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*);
- void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*);
+ void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
+ void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
xMerge = fts5MergeRowidLists;
xAppend = fts5AppendRowid;
}else{
+ nMerge = FTS5_MERGE_NLIST-1;
+ nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
xMerge = fts5MergePrefixLists;
xAppend = fts5AppendPoslist;
}
aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
pStruct = fts5StructureRead(p);
+ assert( p->rc!=SQLITE_OK || (aBuf && pStruct) );
- if( aBuf && pStruct ){
- const int flags = FTS5INDEX_QUERY_SCAN
- | FTS5INDEX_QUERY_SKIPEMPTY
+ if( p->rc==SQLITE_OK ){
+ const int flags = FTS5INDEX_QUERY_SCAN
+ | FTS5INDEX_QUERY_SKIPEMPTY
| FTS5INDEX_QUERY_NOOUTPUT;
int i;
i64 iLastRowid = 0;
@@ -211703,8 +244778,36 @@ static void fts5SetupPrefixIter(
int bNewTerm = 1;
memset(&doclist, 0, sizeof(doclist));
+
+ /* If iIdx is non-zero, then it is the number of a prefix-index for
+ ** prefixes 1 character longer than the prefix being queried for. That
+ ** index contains all the doclists required, except for the one
+ ** corresponding to the prefix itself. That one is extracted from the
+ ** main term index here. */
+ if( iIdx!=0 ){
+ int dummy = 0;
+ const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT;
+ pToken[0] = FTS5_MAIN_PREFIX;
+ fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1);
+ fts5IterSetOutputCb(&p->rc, p1);
+ for(;
+ fts5MultiIterEof(p, p1)==0;
+ fts5MultiIterNext2(p, p1, &dummy)
+ ){
+ Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
+ p1->xSetOutputs(p1, pSeg);
+ if( p1->base.nData ){
+ xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
+ iLastRowid = p1->base.iRowid;
+ }
+ }
+ fts5MultiIterFree(p1);
+ }
+
+ pToken[0] = FTS5_MAIN_PREFIX + iIdx;
fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
fts5IterSetOutputCb(&p->rc, p1);
+
for( /* no-op */ ;
fts5MultiIterEof(p, p1)==0;
fts5MultiIterNext2(p, p1, &bNewTerm)
@@ -211720,34 +244823,45 @@ static void fts5SetupPrefixIter(
}
if( p1->base.nData==0 ) continue;
-
if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
- assert( i<nBuf );
- if( aBuf[i].n==0 ){
- fts5BufferSwap(&doclist, &aBuf[i]);
- fts5BufferZero(&doclist);
- }else{
- xMerge(p, &doclist, &aBuf[i]);
- fts5BufferZero(&aBuf[i]);
+ int i1 = i*nMerge;
+ int iStore;
+ assert( i1+nMerge<=nBuf );
+ for(iStore=i1; iStore<i1+nMerge; iStore++){
+ if( aBuf[iStore].n==0 ){
+ fts5BufferSwap(&doclist, &aBuf[iStore]);
+ fts5BufferZero(&doclist);
+ break;
+ }
+ }
+ if( iStore==i1+nMerge ){
+ xMerge(p, &doclist, nMerge, &aBuf[i1]);
+ for(iStore=i1; iStore<i1+nMerge; iStore++){
+ fts5BufferZero(&aBuf[iStore]);
+ }
}
}
iLastRowid = 0;
}
- xAppend(p, p1->base.iRowid-iLastRowid, p1, &doclist);
+ xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
iLastRowid = p1->base.iRowid;
}
- for(i=0; i<nBuf; i++){
+ assert( (nBuf%nMerge)==0 );
+ for(i=0; i<nBuf; i+=nMerge){
+ int iFree;
if( p->rc==SQLITE_OK ){
- xMerge(p, &doclist, &aBuf[i]);
+ xMerge(p, &doclist, nMerge, &aBuf[i]);
+ }
+ for(iFree=i; iFree<i+nMerge; iFree++){
+ fts5BufferFree(&aBuf[iFree]);
}
- fts5BufferFree(&aBuf[i]);
}
fts5MultiIterFree(p1);
- pData = fts5IdxMalloc(p, sizeof(Fts5Data) + doclist.n);
+ pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING);
if( pData ){
pData->p = (u8*)&pData[1];
pData->nn = pData->szLeaf = doclist.n;
@@ -211775,15 +244889,18 @@ static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){
}
/* Flush the hash table to disk if required */
- if( iRowid<p->iWriteRowid
+ if( iRowid<p->iWriteRowid
|| (iRowid==p->iWriteRowid && p->bDelete==0)
- || (p->nPendingData > p->pConfig->nHashSize)
+ || (p->nPendingData > p->pConfig->nHashSize)
){
fts5IndexFlush(p);
}
p->iWriteRowid = iRowid;
p->bDelete = bDelete;
+ if( bDelete==0 ){
+ p->nPendingRow++;
+ }
return fts5IndexReturn(p);
}
@@ -211793,18 +244910,18 @@ static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){
static int sqlite3Fts5IndexSync(Fts5Index *p){
assert( p->rc==SQLITE_OK );
fts5IndexFlush(p);
- fts5CloseReader(p);
+ sqlite3Fts5IndexCloseReader(p);
return fts5IndexReturn(p);
}
/*
** Discard any data stored in the in-memory hash tables. Do not write it
** to the database. Additionally, assume that the contents of the %_data
-** table may have changed on disk. So any in-memory caches of %_data
+** table may have changed on disk. So any in-memory caches of %_data
** records must be invalidated.
*/
static int sqlite3Fts5IndexRollback(Fts5Index *p){
- fts5CloseReader(p);
+ sqlite3Fts5IndexCloseReader(p);
fts5IndexDiscardData(p);
fts5StructureInvalidate(p);
/* assert( p->rc==SQLITE_OK ); */
@@ -211819,7 +244936,11 @@ static int sqlite3Fts5IndexRollback(Fts5Index *p){
static int sqlite3Fts5IndexReinit(Fts5Index *p){
Fts5Structure s;
fts5StructureInvalidate(p);
+ fts5IndexDiscardData(p);
memset(&s, 0, sizeof(Fts5Structure));
+ if( p->pConfig->bContentlessDelete ){
+ s.nOriginCntr = 1;
+ }
fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
fts5StructureWrite(p, &s);
return fts5IndexReturn(p);
@@ -211833,8 +244954,8 @@ static int sqlite3Fts5IndexReinit(Fts5Index *p){
** Otherwise, set *pp to NULL and return an SQLite error code.
*/
static int sqlite3Fts5IndexOpen(
- Fts5Config *pConfig,
- int bCreate,
+ Fts5Config *pConfig,
+ int bCreate,
Fts5Index **pp,
char **pzErr
){
@@ -211851,8 +244972,8 @@ static int sqlite3Fts5IndexOpen(
pConfig, "data", "id INTEGER PRIMARY KEY, block BLOB", 0, pzErr
);
if( rc==SQLITE_OK ){
- rc = sqlite3Fts5CreateTable(pConfig, "idx",
- "segid, term, pgno, PRIMARY KEY(segid, term)",
+ rc = sqlite3Fts5CreateTable(pConfig, "idx",
+ "segid, term, pgno, PRIMARY KEY(segid, term)",
1, pzErr
);
}
@@ -211883,7 +245004,9 @@ static int sqlite3Fts5IndexClose(Fts5Index *p){
sqlite3_finalize(p->pIdxWriter);
sqlite3_finalize(p->pIdxDeleter);
sqlite3_finalize(p->pIdxSelect);
+ sqlite3_finalize(p->pIdxNextSelect);
sqlite3_finalize(p->pDataVersion);
+ sqlite3_finalize(p->pDeleteFromIdx);
sqlite3Fts5HashFree(p->pHash);
sqlite3_free(p->zDataTbl);
sqlite3_free(p);
@@ -211892,13 +245015,13 @@ static int sqlite3Fts5IndexClose(Fts5Index *p){
}
/*
-** Argument p points to a buffer containing utf-8 text that is n bytes in
+** Argument p points to a buffer containing utf-8 text that is n bytes in
** size. Return the number of bytes in the nChar character prefix of the
** buffer, or 0 if there are less than nChar characters in total.
*/
static int sqlite3Fts5IndexCharlenToBytelen(
- const char *p,
- int nByte,
+ const char *p,
+ int nByte,
int nChar
){
int n = 0;
@@ -211906,9 +245029,13 @@ static int sqlite3Fts5IndexCharlenToBytelen(
for(i=0; i<nChar; i++){
if( n>=nByte ) return 0; /* Input contains fewer than nChar chars */
if( (unsigned char)p[n++]>=0xc0 ){
+ if( n>=nByte ) return 0;
while( (p[n] & 0xc0)==0x80 ){
n++;
- if( n>=nByte ) break;
+ if( n>=nByte ){
+ if( i+1==nChar ) break;
+ return 0;
+ }
}
}
}
@@ -211920,7 +245047,7 @@ static int sqlite3Fts5IndexCharlenToBytelen(
** unicode characters in the string.
*/
static int fts5IndexCharlen(const char *pIn, int nIn){
- int nChar = 0;
+ int nChar = 0;
int i = 0;
while( i<nIn ){
if( (unsigned char)pIn[i++]>=0xc0 ){
@@ -211932,7 +245059,7 @@ static int fts5IndexCharlen(const char *pIn, int nIn){
}
/*
-** Insert or remove data to or from the index. Each time a document is
+** Insert or remove data to or from the index. Each time a document is
** added to or removed from the index, this function is called one or more
** times.
**
@@ -211963,7 +245090,7 @@ static int sqlite3Fts5IndexWrite(
const int nChar = pConfig->aPrefix[i];
int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar);
if( nByte ){
- rc = sqlite3Fts5HashWrite(p->pHash,
+ rc = sqlite3Fts5HashWrite(p->pHash,
p->iWriteRowid, iCol, iPos, (char)(FTS5_MAIN_PREFIX+i+1), pToken,
nByte
);
@@ -211974,7 +245101,455 @@ static int sqlite3Fts5IndexWrite(
}
/*
-** Open a new iterator to iterate though all rowid that match the
+** pToken points to a buffer of size nToken bytes containing a search
+** term, including the index number at the start, used on a tokendata=1
+** table. This function returns true if the term in buffer pBuf matches
+** token pToken/nToken.
+*/
+static int fts5IsTokendataPrefix(
+ Fts5Buffer *pBuf,
+ const u8 *pToken,
+ int nToken
+){
+ return (
+ pBuf->n>=nToken
+ && 0==memcmp(pBuf->p, pToken, nToken)
+ && (pBuf->n==nToken || pBuf->p[nToken]==0x00)
+ );
+}
+
+/*
+** Ensure the segment-iterator passed as the only argument points to EOF.
+*/
+static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
+ fts5DataRelease(pSeg->pLeaf);
+ pSeg->pLeaf = 0;
+}
+
+/*
+** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
+** array of these for each row it visits. Or, for an iterator used by an
+** "ORDER BY rank" query, it accumulates an array of these for the entire
+** query.
+**
+** Each instance in the array indicates the iterator (and therefore term)
+** associated with position iPos of rowid iRowid. This is used by the
+** xInstToken() API.
+*/
+struct Fts5TokenDataMap {
+ i64 iRowid; /* Row this token is located in */
+ i64 iPos; /* Position of token */
+ int iIter; /* Iterator token was read from */
+};
+
+/*
+** An object used to supplement Fts5Iter for tokendata=1 iterators.
+*/
+struct Fts5TokenDataIter {
+ int nIter;
+ int nIterAlloc;
+
+ int nMap;
+ int nMapAlloc;
+ Fts5TokenDataMap *aMap;
+
+ Fts5PoslistReader *aPoslistReader;
+ int *aPoslistToIter;
+ Fts5Iter *apIter[1];
+};
+
+/*
+** This function appends iterator pAppend to Fts5TokenDataIter pIn and
+** returns the result.
+*/
+static Fts5TokenDataIter *fts5AppendTokendataIter(
+ Fts5Index *p, /* Index object (for error code) */
+ Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */
+ Fts5Iter *pAppend /* Append this iterator */
+){
+ Fts5TokenDataIter *pRet = pIn;
+
+ if( p->rc==SQLITE_OK ){
+ if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
+ int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
+ int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter);
+ Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
+
+ if( pNew==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ if( pIn==0 ) memset(pNew, 0, nByte);
+ pRet = pNew;
+ pNew->nIterAlloc = nAlloc;
+ }
+ }
+ }
+ if( p->rc ){
+ sqlite3Fts5IterClose((Fts5IndexIter*)pAppend);
+ }else{
+ pRet->apIter[pRet->nIter++] = pAppend;
+ }
+ assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );
+
+ return pRet;
+}
+
+/*
+** Delete an Fts5TokenDataIter structure and its contents.
+*/
+static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
+ if( pSet ){
+ int ii;
+ for(ii=0; ii<pSet->nIter; ii++){
+ fts5MultiIterFree(pSet->apIter[ii]);
+ }
+ sqlite3_free(pSet->aPoslistReader);
+ sqlite3_free(pSet->aMap);
+ sqlite3_free(pSet);
+ }
+}
+
+/*
+** Append a mapping to the token-map belonging to object pT.
+*/
+static void fts5TokendataIterAppendMap(
+ Fts5Index *p,
+ Fts5TokenDataIter *pT,
+ int iIter,
+ i64 iRowid,
+ i64 iPos
+){
+ if( p->rc==SQLITE_OK ){
+ if( pT->nMap==pT->nMapAlloc ){
+ int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
+ int nByte = nNew * sizeof(Fts5TokenDataMap);
+ Fts5TokenDataMap *aNew;
+
+ aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte);
+ if( aNew==0 ){
+ p->rc = SQLITE_NOMEM;
+ return;
+ }
+
+ pT->aMap = aNew;
+ pT->nMapAlloc = nNew;
+ }
+
+ pT->aMap[pT->nMap].iRowid = iRowid;
+ pT->aMap[pT->nMap].iPos = iPos;
+ pT->aMap[pT->nMap].iIter = iIter;
+ pT->nMap++;
+ }
+}
+
+/*
+** The iterator passed as the only argument must be a tokendata=1 iterator
+** (pIter->pTokenDataIter!=0). This function sets the iterator output
+** variables (pIter->base.*) according to the contents of the current
+** row.
+*/
+static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
+ int ii;
+ int nHit = 0;
+ i64 iRowid = SMALLEST_INT64;
+ int iMin = 0;
+
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+
+ pIter->base.nData = 0;
+ pIter->base.pData = 0;
+
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Iter *p = pT->apIter[ii];
+ if( p->base.bEof==0 ){
+ if( nHit==0 || p->base.iRowid<iRowid ){
+ iRowid = p->base.iRowid;
+ nHit = 1;
+ pIter->base.pData = p->base.pData;
+ pIter->base.nData = p->base.nData;
+ iMin = ii;
+ }else if( p->base.iRowid==iRowid ){
+ nHit++;
+ }
+ }
+ }
+
+ if( nHit==0 ){
+ pIter->base.bEof = 1;
+ }else{
+ int eDetail = pIter->pIndex->pConfig->eDetail;
+ pIter->base.bEof = 0;
+ pIter->base.iRowid = iRowid;
+
+ if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
+ fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1);
+ }else
+ if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){
+ int nReader = 0;
+ int nByte = 0;
+ i64 iPrev = 0;
+
+ /* Allocate array of iterators if they are not already allocated. */
+ if( pT->aPoslistReader==0 ){
+ pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero(
+ &pIter->pIndex->rc,
+ pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int))
+ );
+ if( pT->aPoslistReader==0 ) return;
+ pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter];
+ }
+
+ /* Populate an iterator for each poslist that will be merged */
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Iter *p = pT->apIter[ii];
+ if( iRowid==p->base.iRowid ){
+ pT->aPoslistToIter[nReader] = ii;
+ sqlite3Fts5PoslistReaderInit(
+ p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++]
+ );
+ nByte += p->base.nData;
+ }
+ }
+
+ /* Ensure the output buffer is large enough */
+ if( fts5BufferGrow(&pIter->pIndex->rc, &pIter->poslist, nByte+nHit*10) ){
+ return;
+ }
+
+ /* Ensure the token-mapping is large enough */
+ if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){
+ int nNew = (pT->nMapAlloc + nByte) * 2;
+ Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc(
+ pT->aMap, nNew*sizeof(Fts5TokenDataMap)
+ );
+ if( aNew==0 ){
+ pIter->pIndex->rc = SQLITE_NOMEM;
+ return;
+ }
+ pT->aMap = aNew;
+ pT->nMapAlloc = nNew;
+ }
+
+ pIter->poslist.n = 0;
+
+ while( 1 ){
+ i64 iMinPos = LARGEST_INT64;
+
+ /* Find smallest position */
+ iMin = 0;
+ for(ii=0; ii<nReader; ii++){
+ Fts5PoslistReader *pReader = &pT->aPoslistReader[ii];
+ if( pReader->bEof==0 ){
+ if( pReader->iPos<iMinPos ){
+ iMinPos = pReader->iPos;
+ iMin = ii;
+ }
+ }
+ }
+
+ /* If all readers were at EOF, break out of the loop. */
+ if( iMinPos==LARGEST_INT64 ) break;
+
+ sqlite3Fts5PoslistSafeAppend(&pIter->poslist, &iPrev, iMinPos);
+ sqlite3Fts5PoslistReaderNext(&pT->aPoslistReader[iMin]);
+
+ if( eDetail==FTS5_DETAIL_FULL ){
+ pT->aMap[pT->nMap].iPos = iMinPos;
+ pT->aMap[pT->nMap].iIter = pT->aPoslistToIter[iMin];
+ pT->aMap[pT->nMap].iRowid = iRowid;
+ pT->nMap++;
+ }
+ }
+
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ }
+ }
+}
+
+/*
+** The iterator passed as the only argument must be a tokendata=1 iterator
+** (pIter->pTokenDataIter!=0). This function advances the iterator. If
+** argument bFrom is false, then the iterator is advanced to the next
+** entry. Or, if bFrom is true, it is advanced to the first entry with
+** a rowid of iFrom or greater.
+*/
+static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){
+ int ii;
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Iter *p = pT->apIter[ii];
+ if( p->base.bEof==0
+ && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom))
+ ){
+ fts5MultiIterNext(p->pIndex, p, bFrom, iFrom);
+ while( bFrom && p->base.bEof==0
+ && p->base.iRowid<iFrom
+ && p->pIndex->rc==SQLITE_OK
+ ){
+ fts5MultiIterNext(p->pIndex, p, 0, 0);
+ }
+ }
+ }
+
+ fts5IterSetOutputsTokendata(pIter);
+}
+
+/*
+** If the segment-iterator passed as the first argument is at EOF, then
+** set pIter->term to a copy of buffer pTerm.
+*/
+static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){
+ if( pIter && pIter->aSeg[0].pLeaf==0 ){
+ fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p);
+ }
+}
+
+/*
+** This function sets up an iterator to use for a non-prefix query on a
+** tokendata=1 table.
+*/
+static Fts5Iter *fts5SetupTokendataIter(
+ Fts5Index *p, /* FTS index to query */
+ const u8 *pToken, /* Buffer containing query term */
+ int nToken, /* Size of buffer pToken in bytes */
+ Fts5Colset *pColset /* Colset to filter on */
+){
+ Fts5Iter *pRet = 0;
+ Fts5TokenDataIter *pSet = 0;
+ Fts5Structure *pStruct = 0;
+ const int flags = FTS5INDEX_QUERY_SCANONETERM | FTS5INDEX_QUERY_SCAN;
+
+ Fts5Buffer bSeek = {0, 0, 0};
+ Fts5Buffer *pSmall = 0;
+
+ fts5IndexFlush(p);
+ pStruct = fts5StructureRead(p);
+
+ while( p->rc==SQLITE_OK ){
+ Fts5Iter *pPrev = pSet ? pSet->apIter[pSet->nIter-1] : 0;
+ Fts5Iter *pNew = 0;
+ Fts5SegIter *pNewIter = 0;
+ Fts5SegIter *pPrevIter = 0;
+
+ int iLvl, iSeg, ii;
+
+ pNew = fts5MultiIterAlloc(p, pStruct->nSegment);
+ if( pSmall ){
+ fts5BufferSet(&p->rc, &bSeek, pSmall->n, pSmall->p);
+ fts5BufferAppendBlob(&p->rc, &bSeek, 1, (const u8*)"\0");
+ }else{
+ fts5BufferSet(&p->rc, &bSeek, nToken, pToken);
+ }
+ if( p->rc ){
+ sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
+ break;
+ }
+
+ pNewIter = &pNew->aSeg[0];
+ pPrevIter = (pPrev ? &pPrev->aSeg[0] : 0);
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){
+ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg];
+ int bDone = 0;
+
+ if( pPrevIter ){
+ if( fts5BufferCompare(pSmall, &pPrevIter->term) ){
+ memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter));
+ memset(pPrevIter, 0, sizeof(Fts5SegIter));
+ bDone = 1;
+ }else if( pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf ){
+ fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter);
+ bDone = 1;
+ }
+ }
+
+ if( bDone==0 ){
+ fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter);
+ }
+
+ if( pPrevIter ){
+ if( pPrevIter->pTombArray ){
+ pNewIter->pTombArray = pPrevIter->pTombArray;
+ pNewIter->pTombArray->nRef++;
+ }
+ }else{
+ fts5SegIterAllocTombstone(p, pNewIter);
+ }
+
+ pNewIter++;
+ if( pPrevIter ) pPrevIter++;
+ if( p->rc ) break;
+ }
+ }
+ fts5TokendataSetTermIfEof(pPrev, pSmall);
+
+ pNew->bSkipEmpty = 1;
+ pNew->pColset = pColset;
+ fts5IterSetOutputCb(&p->rc, pNew);
+
+ /* Loop through all segments in the new iterator. Find the smallest
+ ** term that any segment-iterator points to. Iterator pNew will be
+ ** used for this term. Also, set any iterator that points to a term that
+ ** does not match pToken/nToken to point to EOF */
+ pSmall = 0;
+ for(ii=0; ii<pNew->nSeg; ii++){
+ Fts5SegIter *pII = &pNew->aSeg[ii];
+ if( 0==fts5IsTokendataPrefix(&pII->term, pToken, nToken) ){
+ fts5SegIterSetEOF(pII);
+ }
+ if( pII->pLeaf && (!pSmall || fts5BufferCompare(pSmall, &pII->term)>0) ){
+ pSmall = &pII->term;
+ }
+ }
+
+ /* If pSmall is still NULL at this point, then the new iterator does
+ ** not point to any terms that match the query. So delete it and break
+ ** out of the loop - all required iterators have been collected. */
+ if( pSmall==0 ){
+ sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
+ break;
+ }
+
+ /* Append this iterator to the set and continue. */
+ pSet = fts5AppendTokendataIter(p, pSet, pNew);
+ }
+
+ if( p->rc==SQLITE_OK && pSet ){
+ int ii;
+ for(ii=0; ii<pSet->nIter; ii++){
+ Fts5Iter *pIter = pSet->apIter[ii];
+ int iSeg;
+ for(iSeg=0; iSeg<pIter->nSeg; iSeg++){
+ pIter->aSeg[iSeg].flags |= FTS5_SEGITER_ONETERM;
+ }
+ fts5MultiIterFinishSetup(p, pIter);
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ pRet = fts5MultiIterAlloc(p, 0);
+ }
+ if( pRet ){
+ pRet->pTokenDataIter = pSet;
+ if( pSet ){
+ fts5IterSetOutputsTokendata(pRet);
+ }else{
+ pRet->base.bEof = 1;
+ }
+ }else{
+ fts5TokendataIterDelete(pSet);
+ }
+
+ fts5StructureRelease(pStruct);
+ fts5BufferFree(&bSeek);
+ return pRet;
+}
+
+
+/*
+** Open a new iterator to iterate though all rowid that match the
** specified token or token prefix.
*/
static int sqlite3Fts5IndexQuery(
@@ -211993,7 +245568,13 @@ static int sqlite3Fts5IndexQuery(
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */
- if( nToken ) memcpy(&buf.p[1], pToken, nToken);
+ int iPrefixIdx = 0; /* +1 prefix index */
+ int bTokendata = pConfig->bTokendata;
+ if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
+
+ if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
+ bTokendata = 0;
+ }
/* Figure out which index to search and set iIdx accordingly. If this
** is a prefix query for which there is no prefix index, set iIdx to
@@ -212001,9 +245582,9 @@ static int sqlite3Fts5IndexQuery(
** satisfied by scanning multiple terms in the main index.
**
** If the QUERY_TEST_NOIDX flag was specified, then this must be a
- ** prefix-query. Instead of using a prefix-index (if one exists),
+ ** prefix-query. Instead of using a prefix-index (if one exists),
** evaluate the prefix query using the main FTS index. This is used
- ** for internal sanity checking by the integrity-check in debug
+ ** for internal sanity checking by the integrity-check in debug
** mode only. */
#ifdef SQLITE_DEBUG
if( pConfig->bPrefixIndex==0 || (flags & FTS5INDEX_QUERY_TEST_NOIDX) ){
@@ -212014,16 +245595,21 @@ static int sqlite3Fts5IndexQuery(
if( flags & FTS5INDEX_QUERY_PREFIX ){
int nChar = fts5IndexCharlen(pToken, nToken);
for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
- if( pConfig->aPrefix[iIdx-1]==nChar ) break;
+ int nIdxChar = pConfig->aPrefix[iIdx-1];
+ if( nIdxChar==nChar ) break;
+ if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
}
}
- if( iIdx<=pConfig->nPrefix ){
+ if( bTokendata && iIdx==0 ){
+ buf.p[0] = '0';
+ pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset);
+ }else if( iIdx<=pConfig->nPrefix ){
/* Straight index lookup */
Fts5Structure *pStruct = fts5StructureRead(p);
buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
if( pStruct ){
- fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY,
+ fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY,
pColset, buf.p, nToken+1, -1, 0, &pRet
);
fts5StructureRelease(pStruct);
@@ -212031,20 +245617,23 @@ static int sqlite3Fts5IndexQuery(
}else{
/* Scan multiple terms in the main index */
int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
- buf.p[0] = FTS5_MAIN_PREFIX;
- fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet);
- assert( p->rc!=SQLITE_OK || pRet->pColset==0 );
- fts5IterSetOutputCb(&p->rc, pRet);
- if( p->rc==SQLITE_OK ){
- Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst];
- if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg);
+ fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
+ if( pRet==0 ){
+ assert( p->rc!=SQLITE_OK );
+ }else{
+ assert( pRet->pColset==0 );
+ fts5IterSetOutputCb(&p->rc, pRet);
+ if( p->rc==SQLITE_OK ){
+ Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst];
+ if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg);
+ }
}
}
if( p->rc ){
sqlite3Fts5IterClose((Fts5IndexIter*)pRet);
pRet = 0;
- fts5CloseReader(p);
+ sqlite3Fts5IndexCloseReader(p);
}
*ppIter = (Fts5IndexIter*)pRet;
@@ -212057,12 +245646,16 @@ static int sqlite3Fts5IndexQuery(
** Return true if the iterator passed as the only argument is at EOF.
*/
/*
-** Move to the next matching rowid.
+** Move to the next matching rowid.
*/
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
assert( pIter->pIndex->rc==SQLITE_OK );
- fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
+ if( pIter->pTokenDataIter ){
+ fts5TokendataIterNext(pIter, 0, 0);
+ }else{
+ fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
+ }
return fts5IndexReturn(pIter->pIndex);
}
@@ -212095,7 +245688,11 @@ static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){
*/
static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
- fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
+ if( pIter->pTokenDataIter ){
+ fts5TokendataIterNext(pIter, 1, iMatch);
+ }else{
+ fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
+ }
return fts5IndexReturn(pIter->pIndex);
}
@@ -212105,8 +245702,102 @@ static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
int n;
const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
+ assert_nc( z || n<=1 );
*pn = n-1;
- return &z[1];
+ return (z ? &z[1] : 0);
+}
+
+/*
+** This is used by xInstToken() to access the token at offset iOff, column
+** iCol of row iRowid. The token is returned via output variables *ppOut
+** and *pnOut. The iterator passed as the first argument must be a tokendata=1
+** iterator (pIter->pTokenDataIter!=0).
+*/
+static int sqlite3Fts5IterToken(
+ Fts5IndexIter *pIndexIter,
+ i64 iRowid,
+ int iCol,
+ int iOff,
+ const char **ppOut, int *pnOut
+){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+ Fts5TokenDataMap *aMap = pT->aMap;
+ i64 iPos = (((i64)iCol)<<32) + iOff;
+
+ int i1 = 0;
+ int i2 = pT->nMap;
+ int iTest = 0;
+
+ while( i2>i1 ){
+ iTest = (i1 + i2) / 2;
+
+ if( aMap[iTest].iRowid<iRowid ){
+ i1 = iTest+1;
+ }else if( aMap[iTest].iRowid>iRowid ){
+ i2 = iTest;
+ }else{
+ if( aMap[iTest].iPos<iPos ){
+ if( aMap[iTest].iPos<0 ){
+ break;
+ }
+ i1 = iTest+1;
+ }else if( aMap[iTest].iPos>iPos ){
+ i2 = iTest;
+ }else{
+ break;
+ }
+ }
+ }
+
+ if( i2>i1 ){
+ Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
+ *ppOut = (const char*)pMap->aSeg[0].term.p+1;
+ *pnOut = pMap->aSeg[0].term.n-1;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Clear any existing entries from the token-map associated with the
+** iterator passed as the only argument.
+*/
+static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ if( pIter && pIter->pTokenDataIter ){
+ pIter->pTokenDataIter->nMap = 0;
+ }
+}
+
+/*
+** Set a token-mapping for the iterator passed as the first argument. This
+** is used in detail=column or detail=none mode when a token is requested
+** using the xInstToken() API. In this case the caller tokenizers the
+** current row and configures the token-mapping via multiple calls to this
+** function.
+*/
+static int sqlite3Fts5IndexIterWriteTokendata(
+ Fts5IndexIter *pIndexIter,
+ const char *pToken, int nToken,
+ i64 iRowid, int iCol, int iOff
+){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+ Fts5Index *p = pIter->pIndex;
+ int ii;
+
+ assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
+ assert( pIter->pTokenDataIter );
+
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
+ if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
+ }
+ if( ii<pT->nIter ){
+ fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff);
+ }
+ return fts5IndexReturn(p);
}
/*
@@ -212116,13 +245807,14 @@ static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
if( pIndexIter ){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5Index *pIndex = pIter->pIndex;
+ fts5TokendataIterDelete(pIter->pTokenDataIter);
fts5MultiIterFree(pIter);
- fts5CloseReader(pIndex);
+ sqlite3Fts5IndexCloseReader(pIndex);
}
}
/*
-** Read and decode the "averages" record from the database.
+** Read and decode the "averages" record from the database.
**
** Parameter anSize must point to an array of size nCol, where nCol is
** the number of user defined columns in the FTS table.
@@ -212148,7 +245840,7 @@ static int sqlite3Fts5IndexGetAverages(Fts5Index *p, i64 *pnRow, i64 *anSize){
}
/*
-** Replace the current "averages" record with the contents of the buffer
+** Replace the current "averages" record with the contents of the buffer
** supplied as the second argument.
*/
static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8 *pData, int nData){
@@ -212166,7 +245858,7 @@ static int sqlite3Fts5IndexReads(Fts5Index *p){
}
/*
-** Set the 32-bit cookie value stored at the start of all structure
+** Set the 32-bit cookie value stored at the start of all structure
** records to the value passed as the second argument.
**
** Return SQLITE_OK if successful, or an SQLite error code if an error
@@ -212181,7 +245873,7 @@ static int sqlite3Fts5IndexSetCookie(Fts5Index *p, int iNew){
assert( p->rc==SQLITE_OK );
sqlite3Fts5Put32(aCookie, iNew);
- rc = sqlite3_blob_open(pConfig->db, pConfig->zDb, p->zDataTbl,
+ rc = sqlite3_blob_open(pConfig->db, pConfig->zDb, p->zDataTbl,
"block", FTS5_STRUCTURE_ROWID, 1, &pBlob
);
if( rc==SQLITE_OK ){
@@ -212199,10 +245891,351 @@ static int sqlite3Fts5IndexLoadConfig(Fts5Index *p){
return fts5IndexReturn(p);
}
+/*
+** Retrieve the origin value that will be used for the segment currently
+** being accumulated in the in-memory hash table when it is flushed to
+** disk. If successful, SQLITE_OK is returned and (*piOrigin) set to
+** the queried value. Or, if an error occurs, an error code is returned
+** and the final value of (*piOrigin) is undefined.
+*/
+static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin){
+ Fts5Structure *pStruct;
+ pStruct = fts5StructureRead(p);
+ if( pStruct ){
+ *piOrigin = pStruct->nOriginCntr;
+ fts5StructureRelease(pStruct);
+ }
+ return fts5IndexReturn(p);
+}
+
+/*
+** Buffer pPg contains a page of a tombstone hash table - one of nPg pages
+** associated with the same segment. This function adds rowid iRowid to
+** the hash table. The caller is required to guarantee that there is at
+** least one free slot on the page.
+**
+** If parameter bForce is false and the hash table is deemed to be full
+** (more than half of the slots are occupied), then non-zero is returned
+** and iRowid not inserted. Or, if bForce is true or if the hash table page
+** is not full, iRowid is inserted and zero returned.
+*/
+static int fts5IndexTombstoneAddToPage(
+ Fts5Data *pPg,
+ int bForce,
+ int nPg,
+ u64 iRowid
+){
+ const int szKey = TOMBSTONE_KEYSIZE(pPg);
+ const int nSlot = TOMBSTONE_NSLOT(pPg);
+ const int nElem = fts5GetU32(&pPg->p[4]);
+ int iSlot = (iRowid / nPg) % nSlot;
+ int nCollide = nSlot;
+
+ if( szKey==4 && iRowid>0xFFFFFFFF ) return 2;
+ if( iRowid==0 ){
+ pPg->p[1] = 0x01;
+ return 0;
+ }
+
+ if( bForce==0 && nElem>=(nSlot/2) ){
+ return 1;
+ }
+
+ fts5PutU32(&pPg->p[4], nElem+1);
+ if( szKey==4 ){
+ u32 *aSlot = (u32*)&pPg->p[8];
+ while( aSlot[iSlot] ){
+ iSlot = (iSlot + 1) % nSlot;
+ if( nCollide--==0 ) return 0;
+ }
+ fts5PutU32((u8*)&aSlot[iSlot], (u32)iRowid);
+ }else{
+ u64 *aSlot = (u64*)&pPg->p[8];
+ while( aSlot[iSlot] ){
+ iSlot = (iSlot + 1) % nSlot;
+ if( nCollide--==0 ) return 0;
+ }
+ fts5PutU64((u8*)&aSlot[iSlot], iRowid);
+ }
+
+ return 0;
+}
+
+/*
+** This function attempts to build a new hash containing all the keys
+** currently in the tombstone hash table for segment pSeg. The new
+** hash will be stored in the nOut buffers passed in array apOut[].
+** All pages of the new hash use key-size szKey (4 or 8).
+**
+** Return 0 if the hash is successfully rebuilt into the nOut pages.
+** Or non-zero if it is not (because one page became overfull). In this
+** case the caller should retry with a larger nOut parameter.
+**
+** Parameter pData1 is page iPg1 of the hash table being rebuilt.
+*/
+static int fts5IndexTombstoneRehash(
+ Fts5Index *p,
+ Fts5StructureSegment *pSeg, /* Segment to rebuild hash of */
+ Fts5Data *pData1, /* One page of current hash - or NULL */
+ int iPg1, /* Which page of the current hash is pData1 */
+ int szKey, /* 4 or 8, the keysize */
+ int nOut, /* Number of output pages */
+ Fts5Data **apOut /* Array of output hash pages */
+){
+ int ii;
+ int res = 0;
+
+ /* Initialize the headers of all the output pages */
+ for(ii=0; ii<nOut; ii++){
+ apOut[ii]->p[0] = szKey;
+ fts5PutU32(&apOut[ii]->p[4], 0);
+ }
+
+ /* Loop through the current pages of the hash table. */
+ for(ii=0; res==0 && ii<pSeg->nPgTombstone; ii++){
+ Fts5Data *pData = 0; /* Page ii of the current hash table */
+ Fts5Data *pFree = 0; /* Free this at the end of the loop */
+
+ if( iPg1==ii ){
+ pData = pData1;
+ }else{
+ pFree = pData = fts5DataRead(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid, ii));
+ }
+
+ if( pData ){
+ int szKeyIn = TOMBSTONE_KEYSIZE(pData);
+ int nSlotIn = (pData->nn - 8) / szKeyIn;
+ int iIn;
+ for(iIn=0; iIn<nSlotIn; iIn++){
+ u64 iVal = 0;
+
+ /* Read the value from slot iIn of the input page into iVal. */
+ if( szKeyIn==4 ){
+ u32 *aSlot = (u32*)&pData->p[8];
+ if( aSlot[iIn] ) iVal = fts5GetU32((u8*)&aSlot[iIn]);
+ }else{
+ u64 *aSlot = (u64*)&pData->p[8];
+ if( aSlot[iIn] ) iVal = fts5GetU64((u8*)&aSlot[iIn]);
+ }
+
+ /* If iVal is not 0 at this point, insert it into the new hash table */
+ if( iVal ){
+ Fts5Data *pPg = apOut[(iVal % nOut)];
+ res = fts5IndexTombstoneAddToPage(pPg, 0, nOut, iVal);
+ if( res ) break;
+ }
+ }
+
+ /* If this is page 0 of the old hash, copy the rowid-0-flag from the
+ ** old hash to the new. */
+ if( ii==0 ){
+ apOut[0]->p[1] = pData->p[1];
+ }
+ }
+ fts5DataRelease(pFree);
+ }
+
+ return res;
+}
+
+/*
+** This is called to rebuild the hash table belonging to segment pSeg.
+** If parameter pData1 is not NULL, then one page of the existing hash table
+** has already been loaded - pData1, which is page iPg1. The key-size for
+** the new hash table is szKey (4 or 8).
+**
+** If successful, the new hash table is not written to disk. Instead,
+** output parameter (*pnOut) is set to the number of pages in the new
+** hash table, and (*papOut) to point to an array of buffers containing
+** the new page data.
+**
+** If an error occurs, an error code is left in the Fts5Index object and
+** both output parameters set to 0 before returning.
+*/
+static void fts5IndexTombstoneRebuild(
+ Fts5Index *p,
+ Fts5StructureSegment *pSeg, /* Segment to rebuild hash of */
+ Fts5Data *pData1, /* One page of current hash - or NULL */
+ int iPg1, /* Which page of the current hash is pData1 */
+ int szKey, /* 4 or 8, the keysize */
+ int *pnOut, /* OUT: Number of output pages */
+ Fts5Data ***papOut /* OUT: Output hash pages */
+){
+ const int MINSLOT = 32;
+ int nSlotPerPage = MAX(MINSLOT, (p->pConfig->pgsz - 8) / szKey);
+ int nSlot = 0; /* Number of slots in each output page */
+ int nOut = 0;
+
+ /* Figure out how many output pages (nOut) and how many slots per
+ ** page (nSlot). There are three possibilities:
+ **
+ ** 1. The hash table does not yet exist. In this case the new hash
+ ** table will consist of a single page with MINSLOT slots.
+ **
+ ** 2. The hash table exists but is currently a single page. In this
+ ** case an attempt is made to grow the page to accommodate the new
+ ** entry. The page is allowed to grow up to nSlotPerPage (see above)
+ ** slots.
+ **
+ ** 3. The hash table already consists of more than one page, or of
+ ** a single page already so large that it cannot be grown. In this
+ ** case the new hash consists of (nPg*2+1) pages of nSlotPerPage
+ ** slots each, where nPg is the current number of pages in the
+ ** hash table.
+ */
+ if( pSeg->nPgTombstone==0 ){
+ /* Case 1. */
+ nOut = 1;
+ nSlot = MINSLOT;
+ }else if( pSeg->nPgTombstone==1 ){
+ /* Case 2. */
+ int nElem = (int)fts5GetU32(&pData1->p[4]);
+ assert( pData1 && iPg1==0 );
+ nOut = 1;
+ nSlot = MAX(nElem*4, MINSLOT);
+ if( nSlot>nSlotPerPage ) nOut = 0;
+ }
+ if( nOut==0 ){
+ /* Case 3. */
+ nOut = (pSeg->nPgTombstone * 2 + 1);
+ nSlot = nSlotPerPage;
+ }
+
+ /* Allocate the required array and output pages */
+ while( 1 ){
+ int res = 0;
+ int ii = 0;
+ int szPage = 0;
+ Fts5Data **apOut = 0;
+
+ /* Allocate space for the new hash table */
+ assert( nSlot>=MINSLOT );
+ apOut = (Fts5Data**)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5Data*) * nOut);
+ szPage = 8 + nSlot*szKey;
+ for(ii=0; ii<nOut; ii++){
+ Fts5Data *pNew = (Fts5Data*)sqlite3Fts5MallocZero(&p->rc,
+ sizeof(Fts5Data)+szPage
+ );
+ if( pNew ){
+ pNew->nn = szPage;
+ pNew->p = (u8*)&pNew[1];
+ apOut[ii] = pNew;
+ }
+ }
+
+ /* Rebuild the hash table. */
+ if( p->rc==SQLITE_OK ){
+ res = fts5IndexTombstoneRehash(p, pSeg, pData1, iPg1, szKey, nOut, apOut);
+ }
+ if( res==0 ){
+ if( p->rc ){
+ fts5IndexFreeArray(apOut, nOut);
+ apOut = 0;
+ nOut = 0;
+ }
+ *pnOut = nOut;
+ *papOut = apOut;
+ break;
+ }
+
+ /* If control flows to here, it was not possible to rebuild the hash
+ ** table. Free all buffers and then try again with more pages. */
+ assert( p->rc==SQLITE_OK );
+ fts5IndexFreeArray(apOut, nOut);
+ nSlot = nSlotPerPage;
+ nOut = nOut*2 + 1;
+ }
+}
+
+
+/*
+** Add a tombstone for rowid iRowid to segment pSeg.
+*/
+static void fts5IndexTombstoneAdd(
+ Fts5Index *p,
+ Fts5StructureSegment *pSeg,
+ u64 iRowid
+){
+ Fts5Data *pPg = 0;
+ int iPg = -1;
+ int szKey = 0;
+ int nHash = 0;
+ Fts5Data **apHash = 0;
+
+ p->nContentlessDelete++;
+
+ if( pSeg->nPgTombstone>0 ){
+ iPg = iRowid % pSeg->nPgTombstone;
+ pPg = fts5DataRead(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid,iPg));
+ if( pPg==0 ){
+ assert( p->rc!=SQLITE_OK );
+ return;
+ }
+
+ if( 0==fts5IndexTombstoneAddToPage(pPg, 0, pSeg->nPgTombstone, iRowid) ){
+ fts5DataWrite(p, FTS5_TOMBSTONE_ROWID(pSeg->iSegid,iPg), pPg->p, pPg->nn);
+ fts5DataRelease(pPg);
+ return;
+ }
+ }
+
+ /* Have to rebuild the hash table. First figure out the key-size (4 or 8). */
+ szKey = pPg ? TOMBSTONE_KEYSIZE(pPg) : 4;
+ if( iRowid>0xFFFFFFFF ) szKey = 8;
+
+ /* Rebuild the hash table */
+ fts5IndexTombstoneRebuild(p, pSeg, pPg, iPg, szKey, &nHash, &apHash);
+ assert( p->rc==SQLITE_OK || (nHash==0 && apHash==0) );
+
+ /* If all has succeeded, write the new rowid into one of the new hash
+ ** table pages, then write them all out to disk. */
+ if( nHash ){
+ int ii = 0;
+ fts5IndexTombstoneAddToPage(apHash[iRowid % nHash], 1, nHash, iRowid);
+ for(ii=0; ii<nHash; ii++){
+ i64 iTombstoneRowid = FTS5_TOMBSTONE_ROWID(pSeg->iSegid, ii);
+ fts5DataWrite(p, iTombstoneRowid, apHash[ii]->p, apHash[ii]->nn);
+ }
+ pSeg->nPgTombstone = nHash;
+ fts5StructureWrite(p, p->pStruct);
+ }
+
+ fts5DataRelease(pPg);
+ fts5IndexFreeArray(apHash, nHash);
+}
+
+/*
+** Add iRowid to the tombstone list of the segment or segments that contain
+** rows from origin iOrigin. Return SQLITE_OK if successful, or an SQLite
+** error code otherwise.
+*/
+static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid){
+ Fts5Structure *pStruct;
+ pStruct = fts5StructureRead(p);
+ if( pStruct ){
+ int bFound = 0; /* True after pSeg->nEntryTombstone incr. */
+ int iLvl;
+ for(iLvl=pStruct->nLevel-1; iLvl>=0; iLvl--){
+ int iSeg;
+ for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){
+ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg];
+ if( pSeg->iOrigin1<=(u64)iOrigin && pSeg->iOrigin2>=(u64)iOrigin ){
+ if( bFound==0 ){
+ pSeg->nEntryTombstone++;
+ bFound = 1;
+ }
+ fts5IndexTombstoneAdd(p, pSeg, iRowid);
+ }
+ }
+ }
+ fts5StructureRelease(pStruct);
+ }
+ return fts5IndexReturn(p);
+}
/*************************************************************************
**************************************************************************
-** Below this point is the implementation of the integrity-check
+** Below this point is the implementation of the integrity-check
** functionality.
*/
@@ -212210,9 +246243,9 @@ static int sqlite3Fts5IndexLoadConfig(Fts5Index *p){
** Return a simple checksum value based on the arguments.
*/
static u64 sqlite3Fts5IndexEntryCksum(
- i64 iRowid,
- int iCol,
- int iPos,
+ i64 iRowid,
+ int iCol,
+ int iPos,
int iIdx,
const char *pTerm,
int nTerm
@@ -212228,15 +246261,15 @@ static u64 sqlite3Fts5IndexEntryCksum(
#ifdef SQLITE_DEBUG
/*
-** This function is purely an internal test. It does not contribute to
+** This function is purely an internal test. It does not contribute to
** FTS functionality, or even the integrity-check, in any way.
**
-** Instead, it tests that the same set of pgno/rowid combinations are
+** Instead, it tests that the same set of pgno/rowid combinations are
** visited regardless of whether the doclist-index identified by parameters
** iSegid/iLeaf is iterated in forwards or reverse order.
*/
static void fts5TestDlidxReverse(
- Fts5Index *p,
+ Fts5Index *p,
int iSegid, /* Segment id to load from */
int iLeaf /* Load doclist-index for this leaf */
){
@@ -212282,9 +246315,11 @@ static int fts5QueryCksum(
int eDetail = p->pConfig->eDetail;
u64 cksum = *pCksum;
Fts5IndexIter *pIter = 0;
- int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter);
+ int rc = sqlite3Fts5IndexQuery(
+ p, z, n, (flags | FTS5INDEX_QUERY_NOTOKENDATA), 0, &pIter
+ );
- while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIter) ){
+ while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){
i64 rowid = pIter->iRowid;
if( eDetail==FTS5_DETAIL_NONE ){
@@ -212310,13 +246345,44 @@ static int fts5QueryCksum(
return rc;
}
+/*
+** Check if buffer z[], size n bytes, contains as series of valid utf-8
+** encoded codepoints. If so, return 0. Otherwise, if the buffer does not
+** contain valid utf-8, return non-zero.
+*/
+static int fts5TestUtf8(const char *z, int n){
+ int i = 0;
+ assert_nc( n>0 );
+ while( i<n ){
+ if( (z[i] & 0x80)==0x00 ){
+ i++;
+ }else
+ if( (z[i] & 0xE0)==0xC0 ){
+ if( i+1>=n || (z[i+1] & 0xC0)!=0x80 ) return 1;
+ i += 2;
+ }else
+ if( (z[i] & 0xF0)==0xE0 ){
+ if( i+2>=n || (z[i+1] & 0xC0)!=0x80 || (z[i+2] & 0xC0)!=0x80 ) return 1;
+ i += 3;
+ }else
+ if( (z[i] & 0xF8)==0xF0 ){
+ if( i+3>=n || (z[i+1] & 0xC0)!=0x80 || (z[i+2] & 0xC0)!=0x80 ) return 1;
+ if( (z[i+2] & 0xC0)!=0x80 ) return 1;
+ i += 3;
+ }else{
+ return 1;
+ }
+ }
+
+ return 0;
+}
/*
-** This function is also purely an internal test. It does not contribute to
+** This function is also purely an internal test. It does not contribute to
** FTS functionality, or even the integrity-check, in any way.
*/
static void fts5TestTerm(
- Fts5Index *p,
+ Fts5Index *p,
Fts5Buffer *pPrev, /* Previous term */
const char *z, int n, /* Possibly new term to test */
u64 expected,
@@ -212345,13 +246411,19 @@ static void fts5TestTerm(
if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
/* If this is a prefix query, check that the results returned if the
- ** the index is disabled are the same. In both ASC and DESC order.
+ ** the index is disabled are the same. In both ASC and DESC order.
**
** This check may only be performed if the hash table is empty. This
** is because the hash table only supports a single scan query at
** a time, and the multi-iter loop from which this function is called
- ** is already performing such a scan. */
- if( p->nPendingData==0 ){
+ ** is already performing such a scan.
+ **
+ ** Also only do this if buffer zTerm contains nTerm bytes of valid
+ ** utf-8. Otherwise, the last part of the buffer contents might contain
+ ** a non-utf-8 sequence that happens to be a prefix of a valid utf-8
+ ** character stored in the main fts index, which will cause the
+ ** test to fail. */
+ if( p->nPendingData==0 && 0==fts5TestUtf8(zTerm, nTerm) ){
if( iIdx>0 && rc==SQLITE_OK ){
int f = flags|FTS5INDEX_QUERY_TEST_NOIDX;
ck2 = 0;
@@ -212376,7 +246448,7 @@ static void fts5TestTerm(
}
p->rc = rc;
}
-
+
#else
# define fts5TestDlidxReverse(x,y,z)
# define fts5TestTerm(u,v,w,x,y,z)
@@ -212412,7 +246484,7 @@ static void fts5IndexIntegrityCheckEmpty(
}
static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
- int iTermOff = 0;
+ i64 iTermOff = 0;
int ii;
Fts5Buffer buf1 = {0,0,0};
@@ -212421,7 +246493,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
ii = pLeaf->szLeaf;
while( ii<pLeaf->nn && p->rc==SQLITE_OK ){
int res;
- int iOff;
+ i64 iOff;
int nIncr;
ii += fts5GetVarint32(&pLeaf->p[ii], nIncr);
@@ -212466,6 +246538,7 @@ static void fts5IndexIntegrityCheckSegment(
Fts5StructureSegment *pSeg /* Segment to check internal consistency */
){
Fts5Config *pConfig = p->pConfig;
+ int bSecureDelete = (pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE);
sqlite3_stmt *pStmt = 0;
int rc2;
int iIdxPrevLeaf = pSeg->pgnoFirst-1;
@@ -212474,7 +246547,8 @@ static void fts5IndexIntegrityCheckSegment(
if( pSeg->pgnoFirst==0 ) return;
fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintf(
- "SELECT segid, term, (pgno>>1), (pgno&1) FROM %Q.'%q_idx' WHERE segid=%d",
+ "SELECT segid, term, (pgno>>1), (pgno&1) FROM %Q.'%q_idx' WHERE segid=%d "
+ "ORDER BY 1, 2",
pConfig->zDb, pConfig->zName, pSeg->iSegid
));
@@ -212483,12 +246557,12 @@ static void fts5IndexIntegrityCheckSegment(
i64 iRow; /* Rowid for this leaf */
Fts5Data *pLeaf; /* Data for this leaf */
+ const char *zIdxTerm = (const char*)sqlite3_column_blob(pStmt, 1);
int nIdxTerm = sqlite3_column_bytes(pStmt, 1);
- const char *zIdxTerm = (const char*)sqlite3_column_text(pStmt, 1);
int iIdxLeaf = sqlite3_column_int(pStmt, 2);
int bIdxDlidx = sqlite3_column_int(pStmt, 3);
- /* If the leaf in question has already been trimmed from the segment,
+ /* If the leaf in question has already been trimmed from the segment,
** ignore this b-tree entry. Otherwise, load it into memory. */
if( iIdxLeaf<pSeg->pgnoFirst ) continue;
iRow = FTS5_SEGMENT_ROWID(pSeg->iSegid, iIdxLeaf);
@@ -212500,7 +246574,19 @@ static void fts5IndexIntegrityCheckSegment(
** is also a rowid pointer within the leaf page header, it points to a
** location before the term. */
if( pLeaf->nn<=pLeaf->szLeaf ){
- p->rc = FTS5_CORRUPT;
+
+ if( nIdxTerm==0
+ && pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE
+ && pLeaf->nn==pLeaf->szLeaf
+ && pLeaf->nn==4
+ ){
+ /* special case - the very first page in a segment keeps its %_idx
+ ** entry even if all the terms are removed from it by secure-delete
+ ** operations. */
+ }else{
+ p->rc = FTS5_CORRUPT;
+ }
+
}else{
int iOff; /* Offset of first term on leaf */
int iRowidOff; /* Offset of first rowid on leaf */
@@ -212509,11 +246595,11 @@ static void fts5IndexIntegrityCheckSegment(
iOff = fts5LeafFirstTermOff(pLeaf);
iRowidOff = fts5LeafFirstRowidOff(pLeaf);
- if( iRowidOff>=iOff ){
+ if( iRowidOff>=iOff || iOff>=pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
}else{
iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm);
- res = memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm));
+ res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm));
if( res==0 ) res = nTerm - nIdxTerm;
if( res<0 ) p->rc = FTS5_CORRUPT;
}
@@ -212564,9 +246650,12 @@ static void fts5IndexIntegrityCheckSegment(
ASSERT_SZLEAF_OK(pLeaf);
if( iRowidOff>=pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
- }else{
+ }else if( bSecureDelete==0 || iRowidOff>0 ){
+ i64 iDlRowid = fts5DlidxIterRowid(pDlidx);
fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid);
- if( iRowid!=fts5DlidxIterRowid(pDlidx) ) p->rc = FTS5_CORRUPT;
+ if( iRowid<iDlRowid || (bSecureDelete==0 && iRowid!=iDlRowid) ){
+ p->rc = FTS5_CORRUPT;
+ }
}
fts5DataRelease(pLeaf);
}
@@ -212596,7 +246685,7 @@ static void fts5IndexIntegrityCheckSegment(
/*
-** Run internal checks to ensure that the FTS index (a) is internally
+** Run internal checks to ensure that the FTS index (a) is internally
** consistent and (b) contains entries for which the XOR of the checksums
** as calculated by sqlite3Fts5IndexEntryCksum() is cksum.
**
@@ -212605,12 +246694,13 @@ static void fts5IndexIntegrityCheckSegment(
** error, or some other SQLite error code if another error (e.g. OOM)
** occurs.
*/
-static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
+static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
int eDetail = p->pConfig->eDetail;
u64 cksum2 = 0; /* Checksum based on contents of indexes */
Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */
Fts5Iter *pIter; /* Used to iterate through entire index */
Fts5Structure *pStruct; /* Index structure */
+ int iLvl, iSeg;
#ifdef SQLITE_DEBUG
/* Used by extra internal tests only run if NDEBUG is not defined */
@@ -212618,18 +246708,19 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
Fts5Buffer term = {0,0,0}; /* Buffer used to hold most recent term */
#endif
const int flags = FTS5INDEX_QUERY_NOOUTPUT;
-
+
/* Load the FTS index structure */
pStruct = fts5StructureRead(p);
+ if( pStruct==0 ){
+ assert( p->rc!=SQLITE_OK );
+ return fts5IndexReturn(p);
+ }
/* Check that the internal nodes of each segment match the leaves */
- if( pStruct ){
- int iLvl, iSeg;
- for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
- for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
- Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg];
- fts5IndexIntegrityCheckSegment(p, pSeg);
- }
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
+ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg];
+ fts5IndexIntegrityCheckSegment(p, pSeg);
}
}
@@ -212640,7 +246731,7 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
**
** Two versions of the same checksum are calculated. The first (stack
** variable cksum2) based on entries extracted from the full-text index
- ** while doing a linear scan of each individual index in turn.
+ ** while doing a linear scan of each individual index in turn.
**
** As each term visited by the linear scans, a separate query for the
** same term is performed. cksum3 is calculated based on the entries
@@ -212658,6 +246749,7 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
/* If this is a new term, query for it. Update cksum3 with the results. */
fts5TestTerm(p, &term, z, n, cksum2, &cksum3);
+ if( p->rc ) break;
if( eDetail==FTS5_DETAIL_NONE ){
if( 0==fts5MultiIterIsEmpty(p, pIter) ){
@@ -212666,6 +246758,7 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
}else{
poslist.n = 0;
fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist);
+ fts5BufferAppendBlob(&p->rc, &poslist, 4, (const u8*)"\0\0\0\0");
while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){
int iCol = FTS5_POS2COLUMN(iPos);
int iTokOff = FTS5_POS2OFFSET(iPos);
@@ -212676,7 +246769,7 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3);
fts5MultiIterFree(pIter);
- if( p->rc==SQLITE_OK && cksum!=cksum2 ) p->rc = FTS5_CORRUPT;
+ if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ) p->rc = FTS5_CORRUPT;
fts5StructureRelease(pStruct);
#ifdef SQLITE_DEBUG
@@ -212692,12 +246785,14 @@ static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
** function only.
*/
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** Decode a segment-data rowid from the %_data table. This function is
** the opposite of macro FTS5_SEGMENT_ROWID().
*/
static void fts5DecodeRowid(
i64 iRowid, /* Rowid from %_data table */
+ int *pbTombstone, /* OUT: Tombstone hash flag */
int *piSegid, /* OUT: Segment id */
int *pbDlidx, /* OUT: Dlidx flag */
int *piHeight, /* OUT: Height */
@@ -212713,11 +246808,16 @@ static void fts5DecodeRowid(
iRowid >>= FTS5_DATA_DLI_B;
*piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B) - 1));
+ iRowid >>= FTS5_DATA_ID_B;
+
+ *pbTombstone = (int)(iRowid & 0x0001);
}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){
- int iSegid, iHeight, iPgno, bDlidx; /* Rowid compenents */
- fts5DecodeRowid(iKey, &iSegid, &bDlidx, &iHeight, &iPgno);
+ int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid compenents */
+ fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno);
if( iSegid==0 ){
if( iKey==FTS5_AVERAGES_ROWID ){
@@ -212727,12 +246827,16 @@ static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){
}
}
else{
- sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%ssegid=%d h=%d pgno=%d}",
- bDlidx ? "dlidx " : "", iSegid, iHeight, iPgno
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%s%ssegid=%d h=%d pgno=%d}",
+ bDlidx ? "dlidx " : "",
+ bTomb ? "tombstone " : "",
+ iSegid, iHeight, iPgno
);
}
}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
static void fts5DebugStructure(
int *pRc, /* IN/OUT: error code */
Fts5Buffer *pBuf,
@@ -212742,25 +246846,33 @@ static void fts5DebugStructure(
for(iLvl=0; iLvl<p->nLevel; iLvl++){
Fts5StructureLevel *pLvl = &p->aLevel[iLvl];
- sqlite3Fts5BufferAppendPrintf(pRc, pBuf,
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf,
" {lvl=%d nMerge=%d nSeg=%d", iLvl, pLvl->nMerge, pLvl->nSeg
);
for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){
Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
- sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d}",
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d",
pSeg->iSegid, pSeg->pgnoFirst, pSeg->pgnoLast
);
+ if( pSeg->iOrigin1>0 ){
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " origin=%lld..%lld",
+ pSeg->iOrigin1, pSeg->iOrigin2
+ );
+ }
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}");
}
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}");
}
}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** This is part of the fts5_decode() debugging aid.
**
** Arguments pBlob/nBlob contain a serialized Fts5Structure object. This
** function appends a human-readable representation of the same object
-** to the buffer passed as the second argument.
+** to the buffer passed as the second argument.
*/
static void fts5DecodeStructure(
int *pRc, /* IN/OUT: error code */
@@ -212779,13 +246891,15 @@ static void fts5DecodeStructure(
fts5DebugStructure(pRc, pBuf, p);
fts5StructureRelease(p);
}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** This is part of the fts5_decode() debugging aid.
**
-** Arguments pBlob/nBlob contain an "averages" record. This function
-** appends a human-readable representation of record to the buffer passed
-** as the second argument.
+** Arguments pBlob/nBlob contain an "averages" record. This function
+** appends a human-readable representation of record to the buffer passed
+** as the second argument.
*/
static void fts5DecodeAverages(
int *pRc, /* IN/OUT: error code */
@@ -212802,7 +246916,9 @@ static void fts5DecodeAverages(
zSpace = " ";
}
}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** Buffer (a/n) is assumed to contain a list of serialized varints. Read
** each varint and append its string representation to buffer pBuf. Return
@@ -212819,12 +246935,14 @@ static int fts5DecodePoslist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){
}
return iOff;
}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** The start of buffer (a/n) contains the start of a doclist. The doclist
** may or may not finish within the buffer. This function appends a text
** representation of the part of the doclist that is present to buffer
-** pBuf.
+** pBuf.
**
** The return value is the number of bytes read from the input buffer.
*/
@@ -212852,9 +246970,11 @@ static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){
return iOff;
}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
-** This function is part of the fts5_decode() debugging function. It is
+** This function is part of the fts5_decode() debugging function. It is
** only ever used with detail=none tables.
**
** Buffer (pData/nData) contains a doclist in the format used by detail=none
@@ -212893,7 +247013,27 @@ static void fts5DecodeRowidList(
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp);
}
}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
+static void fts5BufferAppendTerm(int *pRc, Fts5Buffer *pBuf, Fts5Buffer *pTerm){
+ int ii;
+ fts5BufferGrow(pRc, pBuf, pTerm->n*2 + 1);
+ if( *pRc==SQLITE_OK ){
+ for(ii=0; ii<pTerm->n; ii++){
+ if( pTerm->p[ii]==0x00 ){
+ pBuf->p[pBuf->n++] = '\\';
+ pBuf->p[pBuf->n++] = '0';
+ }else{
+ pBuf->p[pBuf->n++] = pTerm->p[ii];
+ }
+ }
+ pBuf->p[pBuf->n] = 0x00;
+ }
+}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** The implementation of user-defined scalar function fts5_decode().
*/
@@ -212904,11 +247044,12 @@ static void fts5DecodeFunction(
){
i64 iRowid; /* Rowid for record being decoded */
int iSegid,iHeight,iPgno,bDlidx;/* Rowid components */
+ int bTomb;
const u8 *aBlob; int n; /* Record to decode */
u8 *a = 0;
Fts5Buffer s; /* Build up text to return here */
int rc = SQLITE_OK; /* Return code */
- int nSpace = 0;
+ sqlite3_int64 nSpace = 0;
int eDetailNone = (sqlite3_user_data(pCtx)!=0);
assert( nArg==2 );
@@ -212924,10 +247065,9 @@ static void fts5DecodeFunction(
nSpace = n + FTS5_DATA_ZERO_PADDING;
a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace);
if( a==0 ) goto decode_out;
- memcpy(a, aBlob, n);
+ if( n>0 ) memcpy(a, aBlob, n);
-
- fts5DecodeRowid(iRowid, &iSegid, &bDlidx, &iHeight, &iPgno);
+ fts5DecodeRowid(iRowid, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno);
fts5DebugRowid(&rc, &s, iRowid);
if( bDlidx ){
@@ -212942,10 +247082,32 @@ static void fts5DecodeFunction(
lvl.iLeafPgno = iPgno;
for(fts5DlidxLvlNext(&lvl); lvl.bEof==0; fts5DlidxLvlNext(&lvl)){
- sqlite3Fts5BufferAppendPrintf(&rc, &s,
+ sqlite3Fts5BufferAppendPrintf(&rc, &s,
" %d(%lld)", lvl.iLeafPgno, lvl.iRowid
);
}
+ }else if( bTomb ){
+ u32 nElem = fts5GetU32(&a[4]);
+ int szKey = (aBlob[0]==4 || aBlob[0]==8) ? aBlob[0] : 8;
+ int nSlot = (n - 8) / szKey;
+ int ii;
+ sqlite3Fts5BufferAppendPrintf(&rc, &s, " nElem=%d", (int)nElem);
+ if( aBlob[1] ){
+ sqlite3Fts5BufferAppendPrintf(&rc, &s, " 0");
+ }
+ for(ii=0; ii<nSlot; ii++){
+ u64 iVal = 0;
+ if( szKey==4 ){
+ u32 *aSlot = (u32*)&aBlob[8];
+ if( aSlot[ii] ) iVal = fts5GetU32((u8*)&aSlot[ii]);
+ }else{
+ u64 *aSlot = (u64*)&aBlob[8];
+ if( aSlot[ii] ) iVal = fts5GetU64((u8*)&aSlot[ii]);
+ }
+ if( iVal!=0 ){
+ sqlite3Fts5BufferAppendPrintf(&rc, &s, " %lld", (i64)iVal);
+ }
+ }
}else if( iSegid==0 ){
if( iRowid==FTS5_AVERAGES_ROWID ){
fts5DecodeAverages(&rc, &s, a, n);
@@ -212971,16 +247133,15 @@ static void fts5DecodeFunction(
fts5DecodeRowidList(&rc, &s, &a[4], iTermOff-4);
iOff = iTermOff;
- while( iOff<szLeaf ){
+ while( iOff<szLeaf && rc==SQLITE_OK ){
int nAppend;
/* Read the term data for the next term*/
iOff += fts5GetVarint32(&a[iOff], nAppend);
term.n = nKeep;
fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]);
- sqlite3Fts5BufferAppendPrintf(
- &rc, &s, " term=%.*s", term.n, (const char*)term.p
- );
+ sqlite3Fts5BufferAppendPrintf(&rc, &s, " term=");
+ fts5BufferAppendTerm(&rc, &s, &term);
iOff += nAppend;
/* Figure out where the doclist for this term ends */
@@ -212991,8 +247152,11 @@ static void fts5DecodeFunction(
}else{
iTermOff = szLeaf;
}
-
- fts5DecodeRowidList(&rc, &s, &a[iOff], iTermOff-iOff);
+ if( iTermOff>szLeaf ){
+ rc = FTS5_CORRUPT;
+ }else{
+ fts5DecodeRowidList(&rc, &s, &a[iOff], iTermOff-iOff);
+ }
iOff = iTermOff;
if( iOff<szLeaf ){
iOff += fts5GetVarint32(&a[iOff], nKeep);
@@ -213020,6 +247184,9 @@ static void fts5DecodeFunction(
iPgidxOff = szLeaf = fts5GetU16(&a[2]);
if( iPgidxOff<n ){
fts5GetVarint32(&a[iPgidxOff], iTermOff);
+ }else if( iPgidxOff>n ){
+ rc = FTS5_CORRUPT;
+ goto decode_out;
}
}
@@ -213031,18 +247198,26 @@ static void fts5DecodeFunction(
}else{
iOff = szLeaf;
}
+ if( iOff>n ){
+ rc = FTS5_CORRUPT;
+ goto decode_out;
+ }
fts5DecodePoslist(&rc, &s, &a[4], iOff-4);
/* Decode any more doclist data that appears on the page before the
** first term. */
nDoclist = (iTermOff ? iTermOff : szLeaf) - iOff;
+ if( nDoclist+iOff>n ){
+ rc = FTS5_CORRUPT;
+ goto decode_out;
+ }
fts5DecodeDoclist(&rc, &s, &a[iOff], nDoclist);
- while( iPgidxOff<n ){
+ while( iPgidxOff<n && rc==SQLITE_OK ){
int bFirst = (iPgidxOff==szLeaf); /* True for first term on page */
int nByte; /* Bytes of data */
int iEnd;
-
+
iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nByte);
iPgidxPrev += nByte;
iOff = iPgidxPrev;
@@ -213053,24 +247228,35 @@ static void fts5DecodeFunction(
}else{
iEnd = szLeaf;
}
+ if( iEnd>szLeaf ){
+ rc = FTS5_CORRUPT;
+ break;
+ }
if( bFirst==0 ){
iOff += fts5GetVarint32(&a[iOff], nByte);
+ if( nByte>term.n ){
+ rc = FTS5_CORRUPT;
+ break;
+ }
term.n = nByte;
}
iOff += fts5GetVarint32(&a[iOff], nByte);
+ if( iOff+nByte>n ){
+ rc = FTS5_CORRUPT;
+ break;
+ }
fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]);
iOff += nByte;
- sqlite3Fts5BufferAppendPrintf(
- &rc, &s, " term=%.*s", term.n, (const char*)term.p
- );
+ sqlite3Fts5BufferAppendPrintf(&rc, &s, " term=");
+ fts5BufferAppendTerm(&rc, &s, &term);
iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff);
}
fts5BufferFree(&term);
}
-
+
decode_out:
sqlite3_free(a);
if( rc==SQLITE_OK ){
@@ -213080,7 +247266,9 @@ static void fts5DecodeFunction(
}
fts5BufferFree(&s);
}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** The implementation of user-defined scalar function fts5_rowid().
*/
@@ -213098,7 +247286,7 @@ static void fts5RowidFunction(
i64 iRowid;
int segid, pgno;
if( nArg!=3 ){
- sqlite3_result_error(pCtx,
+ sqlite3_result_error(pCtx,
"should be: fts5_rowid('segment', segid, pgno))", -1
);
}else{
@@ -213108,12 +247296,241 @@ static void fts5RowidFunction(
sqlite3_result_int64(pCtx, iRowid);
}
}else{
- sqlite3_result_error(pCtx,
+ sqlite3_result_error(pCtx,
"first arg to fts5_rowid() must be 'segment'" , -1
);
}
}
}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
+
+typedef struct Fts5StructVtab Fts5StructVtab;
+struct Fts5StructVtab {
+ sqlite3_vtab base;
+};
+
+typedef struct Fts5StructVcsr Fts5StructVcsr;
+struct Fts5StructVcsr {
+ sqlite3_vtab_cursor base;
+ Fts5Structure *pStruct;
+ int iLevel;
+ int iSeg;
+ int iRowid;
+};
+
+/*
+** Create a new fts5_structure() table-valued function.
+*/
+static int fts5structConnectMethod(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ Fts5StructVtab *pNew = 0;
+ int rc = SQLITE_OK;
+
+ rc = sqlite3_declare_vtab(db,
+ "CREATE TABLE xyz("
+ "level, segment, merge, segid, leaf1, leaf2, loc1, loc2, "
+ "npgtombstone, nentrytombstone, nentry, struct HIDDEN);"
+ );
+ if( rc==SQLITE_OK ){
+ pNew = sqlite3Fts5MallocZero(&rc, sizeof(*pNew));
+ }
+
+ *ppVtab = (sqlite3_vtab*)pNew;
+ return rc;
+}
+
+/*
+** We must have a single struct=? constraint that will be passed through
+** into the xFilter method. If there is no valid stmt=? constraint,
+** then return an SQLITE_CONSTRAINT error.
+*/
+static int fts5structBestIndexMethod(
+ sqlite3_vtab *tab,
+ sqlite3_index_info *pIdxInfo
+){
+ int i;
+ int rc = SQLITE_CONSTRAINT;
+ struct sqlite3_index_constraint *p;
+ pIdxInfo->estimatedCost = (double)100;
+ pIdxInfo->estimatedRows = 100;
+ pIdxInfo->idxNum = 0;
+ for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){
+ if( p->usable==0 ) continue;
+ if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==11 ){
+ rc = SQLITE_OK;
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
+ break;
+ }
+ }
+ return rc;
+}
+
+/*
+** This method is the destructor for bytecodevtab objects.
+*/
+static int fts5structDisconnectMethod(sqlite3_vtab *pVtab){
+ Fts5StructVtab *p = (Fts5StructVtab*)pVtab;
+ sqlite3_free(p);
+ return SQLITE_OK;
+}
+
+/*
+** Constructor for a new bytecodevtab_cursor object.
+*/
+static int fts5structOpenMethod(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
+ int rc = SQLITE_OK;
+ Fts5StructVcsr *pNew = 0;
+
+ pNew = sqlite3Fts5MallocZero(&rc, sizeof(*pNew));
+ *ppCsr = (sqlite3_vtab_cursor*)pNew;
+
+ return SQLITE_OK;
+}
+
+/*
+** Destructor for a bytecodevtab_cursor.
+*/
+static int fts5structCloseMethod(sqlite3_vtab_cursor *cur){
+ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur;
+ fts5StructureRelease(pCsr->pStruct);
+ sqlite3_free(pCsr);
+ return SQLITE_OK;
+}
+
+
+/*
+** Advance a bytecodevtab_cursor to its next row of output.
+*/
+static int fts5structNextMethod(sqlite3_vtab_cursor *cur){
+ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur;
+ Fts5Structure *p = pCsr->pStruct;
+
+ assert( pCsr->pStruct );
+ pCsr->iSeg++;
+ pCsr->iRowid++;
+ while( pCsr->iLevel<p->nLevel && pCsr->iSeg>=p->aLevel[pCsr->iLevel].nSeg ){
+ pCsr->iLevel++;
+ pCsr->iSeg = 0;
+ }
+ if( pCsr->iLevel>=p->nLevel ){
+ fts5StructureRelease(pCsr->pStruct);
+ pCsr->pStruct = 0;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the cursor has been moved off of the last
+** row of output.
+*/
+static int fts5structEofMethod(sqlite3_vtab_cursor *cur){
+ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur;
+ return pCsr->pStruct==0;
+}
+
+static int fts5structRowidMethod(
+ sqlite3_vtab_cursor *cur,
+ sqlite_int64 *piRowid
+){
+ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur;
+ *piRowid = pCsr->iRowid;
+ return SQLITE_OK;
+}
+
+/*
+** Return values of columns for the row at which the bytecodevtab_cursor
+** is currently pointing.
+*/
+static int fts5structColumnMethod(
+ sqlite3_vtab_cursor *cur, /* The cursor */
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
+ int i /* Which column to return */
+){
+ Fts5StructVcsr *pCsr = (Fts5StructVcsr*)cur;
+ Fts5Structure *p = pCsr->pStruct;
+ Fts5StructureSegment *pSeg = &p->aLevel[pCsr->iLevel].aSeg[pCsr->iSeg];
+
+ switch( i ){
+ case 0: /* level */
+ sqlite3_result_int(ctx, pCsr->iLevel);
+ break;
+ case 1: /* segment */
+ sqlite3_result_int(ctx, pCsr->iSeg);
+ break;
+ case 2: /* merge */
+ sqlite3_result_int(ctx, pCsr->iSeg < p->aLevel[pCsr->iLevel].nMerge);
+ break;
+ case 3: /* segid */
+ sqlite3_result_int(ctx, pSeg->iSegid);
+ break;
+ case 4: /* leaf1 */
+ sqlite3_result_int(ctx, pSeg->pgnoFirst);
+ break;
+ case 5: /* leaf2 */
+ sqlite3_result_int(ctx, pSeg->pgnoLast);
+ break;
+ case 6: /* origin1 */
+ sqlite3_result_int64(ctx, pSeg->iOrigin1);
+ break;
+ case 7: /* origin2 */
+ sqlite3_result_int64(ctx, pSeg->iOrigin2);
+ break;
+ case 8: /* npgtombstone */
+ sqlite3_result_int(ctx, pSeg->nPgTombstone);
+ break;
+ case 9: /* nentrytombstone */
+ sqlite3_result_int64(ctx, pSeg->nEntryTombstone);
+ break;
+ case 10: /* nentry */
+ sqlite3_result_int64(ctx, pSeg->nEntry);
+ break;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Initialize a cursor.
+**
+** idxNum==0 means show all subprograms
+** idxNum==1 means show only the main bytecode and omit subprograms.
+*/
+static int fts5structFilterMethod(
+ sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ Fts5StructVcsr *pCsr = (Fts5StructVcsr *)pVtabCursor;
+ int rc = SQLITE_OK;
+
+ const u8 *aBlob = 0;
+ int nBlob = 0;
+
+ assert( argc==1 );
+ fts5StructureRelease(pCsr->pStruct);
+ pCsr->pStruct = 0;
+
+ nBlob = sqlite3_value_bytes(argv[0]);
+ aBlob = (const u8*)sqlite3_value_blob(argv[0]);
+ rc = fts5StructureDecode(aBlob, nBlob, 0, &pCsr->pStruct);
+ if( rc==SQLITE_OK ){
+ pCsr->iLevel = 0;
+ pCsr->iRowid = 0;
+ pCsr->iSeg = -1;
+ rc = fts5structNextMethod(pVtabCursor);
+ }
+
+ return rc;
+}
+
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
/*
** This is called as part of registering the FTS5 module with database
@@ -213124,13 +247541,14 @@ static void fts5RowidFunction(
** SQLite error code is returned instead.
*/
static int sqlite3Fts5IndexInit(sqlite3 *db){
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
int rc = sqlite3_create_function(
db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0
);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
- db, "fts5_decode_none", 2,
+ db, "fts5_decode_none", 2,
SQLITE_UTF8, (void*)db, fts5DecodeFunction, 0, 0
);
}
@@ -213140,7 +247558,42 @@ static int sqlite3Fts5IndexInit(sqlite3 *db){
db, "fts5_rowid", -1, SQLITE_UTF8, 0, fts5RowidFunction, 0, 0
);
}
+
+ if( rc==SQLITE_OK ){
+ static const sqlite3_module fts5structure_module = {
+ 0, /* iVersion */
+ 0, /* xCreate */
+ fts5structConnectMethod, /* xConnect */
+ fts5structBestIndexMethod, /* xBestIndex */
+ fts5structDisconnectMethod, /* xDisconnect */
+ 0, /* xDestroy */
+ fts5structOpenMethod, /* xOpen */
+ fts5structCloseMethod, /* xClose */
+ fts5structFilterMethod, /* xFilter */
+ fts5structNextMethod, /* xNext */
+ fts5structEofMethod, /* xEof */
+ fts5structColumnMethod, /* xColumn */
+ fts5structRowidMethod, /* xRowid */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindFunction */
+ 0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
+ };
+ rc = sqlite3_create_module(db, "fts5_structure", &fts5structure_module, 0);
+ }
return rc;
+#else
+ return SQLITE_OK;
+ UNUSED_PARAM(db);
+#endif
}
@@ -213176,20 +247629,22 @@ static int sqlite3Fts5IndexReset(Fts5Index *p){
** assert() conditions in the fts5 code are activated - conditions that are
** only true if it is guaranteed that the fts5 database is not corrupt.
*/
+#ifdef SQLITE_DEBUG
SQLITE_API int sqlite3_fts5_may_be_corrupt = 1;
+#endif
typedef struct Fts5Auxdata Fts5Auxdata;
typedef struct Fts5Auxiliary Fts5Auxiliary;
typedef struct Fts5Cursor Fts5Cursor;
+typedef struct Fts5FullTable Fts5FullTable;
typedef struct Fts5Sorter Fts5Sorter;
-typedef struct Fts5Table Fts5Table;
typedef struct Fts5TokenizerModule Fts5TokenizerModule;
/*
-** NOTES ON TRANSACTIONS:
+** NOTES ON TRANSACTIONS:
**
-** SQLite invokes the following virtual table methods as transactions are
+** SQLite invokes the following virtual table methods as transactions are
** opened and closed by the user:
**
** xBegin(): Start of a new transaction.
@@ -213198,7 +247653,7 @@ typedef struct Fts5TokenizerModule Fts5TokenizerModule;
** xRollback(): Rollback the transaction.
**
** Anything that is required as part of a commit that may fail is performed
-** in the xSync() callback. Current versions of SQLite ignore any errors
+** in the xSync() callback. Current versions of SQLite ignore any errors
** returned by xCommit().
**
** And as sub-transactions are opened/closed:
@@ -213207,9 +247662,9 @@ typedef struct Fts5TokenizerModule Fts5TokenizerModule;
** xRelease(int S): Commit and close savepoint S.
** xRollbackTo(int S): Rollback to start of savepoint S.
**
-** During a write-transaction the fts5_index.c module may cache some data
+** During a write-transaction the fts5_index.c module may cache some data
** in-memory. It is flushed to disk whenever xSync(), xRelease() or
-** xSavepoint() is called. And discarded whenever xRollback() or xRollbackTo()
+** xSavepoint() is called. And discarded whenever xRollback() or xRollbackTo()
** is called.
**
** Additionally, if SQLITE_DEBUG is defined, an instance of the following
@@ -213223,13 +247678,13 @@ struct Fts5TransactionState {
};
/*
-** A single object of this type is allocated when the FTS5 module is
+** A single object of this type is allocated when the FTS5 module is
** registered with a database handle. It is used to store pointers to
** all registered FTS5 extensions - tokenizers and auxiliary functions.
*/
struct Fts5Global {
fts5_api api; /* User visible part of object (see fts5.h) */
- sqlite3 *db; /* Associated database connection */
+ sqlite3 *db; /* Associated database connection */
i64 iNextId; /* Used to allocate unique cursor ids */
Fts5Auxiliary *pAux; /* First in list of all aux. functions */
Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */
@@ -213264,16 +247719,13 @@ struct Fts5TokenizerModule {
Fts5TokenizerModule *pNext; /* Next registered tokenizer module */
};
-/*
-** Virtual-table object.
-*/
-struct Fts5Table {
- sqlite3_vtab base; /* Base class used by SQLite core */
- Fts5Config *pConfig; /* Virtual table configuration */
- Fts5Index *pIndex; /* Full-text index */
+struct Fts5FullTable {
+ Fts5Table p; /* Public class members from fts5Int.h */
Fts5Storage *pStorage; /* Document store */
Fts5Global *pGlobal; /* Global (connection wide) data */
Fts5Cursor *pSortCsr; /* Sort data from this cursor */
+ int iSavepoint; /* Successful xSavepoint()+1 */
+
#ifdef SQLITE_DEBUG
struct Fts5TransactionState ts;
#endif
@@ -213290,7 +247742,7 @@ struct Fts5MatchPhrase {
**
** aIdx[]:
** There is one entry in the aIdx[] array for each phrase in the query,
-** the value of which is the offset within aPoslist[] following the last
+** the value of which is the offset within aPoslist[] following the last
** byte of the position list for the corresponding phrase.
*/
struct Fts5Sorter {
@@ -213306,8 +247758,8 @@ struct Fts5Sorter {
** Virtual-table cursor object.
**
** iSpecial:
-** If this is a 'special' query (refer to function fts5SpecialMatch()),
-** then this variable contains the result of the query.
+** If this is a 'special' query (refer to function fts5SpecialMatch()),
+** then this variable contains the result of the query.
**
** iFirstRowid, iLastRowid:
** These variables are only used for FTS5_PLAN_MATCH cursors. Assuming the
@@ -213358,7 +247810,7 @@ struct Fts5Cursor {
};
/*
-** Bits that make up the "idxNum" parameter passed indirectly by
+** Bits that make up the "idxNum" parameter passed indirectly by
** xBestIndex() to xFilter().
*/
#define FTS5_BI_MATCH 0x0001 /* <tbl> MATCH ? */
@@ -213408,7 +247860,7 @@ struct Fts5Auxdata {
#define FTS5_SAVEPOINT 5
#define FTS5_RELEASE 6
#define FTS5_ROLLBACKTO 7
-static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){
+static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){
switch( op ){
case FTS5_BEGIN:
assert( p->ts.eState==0 );
@@ -213417,7 +247869,7 @@ static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){
break;
case FTS5_SYNC:
- assert( p->ts.eState==1 );
+ assert( p->ts.eState==1 || p->ts.eState==2 );
p->ts.eState = 2;
break;
@@ -213432,23 +247884,26 @@ static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){
break;
case FTS5_SAVEPOINT:
- assert( p->ts.eState==1 );
+ assert( p->ts.eState>=1 );
assert( iSavepoint>=0 );
assert( iSavepoint>=p->ts.iSavepoint );
p->ts.iSavepoint = iSavepoint;
break;
-
+
case FTS5_RELEASE:
- assert( p->ts.eState==1 );
+ assert( p->ts.eState>=1 );
assert( iSavepoint>=0 );
assert( iSavepoint<=p->ts.iSavepoint );
p->ts.iSavepoint = iSavepoint-1;
break;
case FTS5_ROLLBACKTO:
- assert( p->ts.eState==1 );
- assert( iSavepoint>=0 );
- assert( iSavepoint<=p->ts.iSavepoint );
+ assert( p->ts.eState>=1 );
+ assert( iSavepoint>=-1 );
+ /* The following assert() can fail if another vtab strikes an error
+ ** within an xSavepoint() call then SQLite calls xRollbackTo() - without
+ ** having called xSavepoint() on this vtab. */
+ /* assert( iSavepoint<=p->ts.iSavepoint ); */
p->ts.iSavepoint = iSavepoint;
break;
}
@@ -213460,18 +247915,18 @@ static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){
/*
** Return true if pTab is a contentless table.
*/
-static int fts5IsContentless(Fts5Table *pTab){
- return pTab->pConfig->eContent==FTS5_CONTENT_NONE;
+static int fts5IsContentless(Fts5FullTable *pTab){
+ return pTab->p.pConfig->eContent==FTS5_CONTENT_NONE;
}
/*
-** Delete a virtual table handle allocated by fts5InitVtab().
+** Delete a virtual table handle allocated by fts5InitVtab().
*/
-static void fts5FreeVtab(Fts5Table *pTab){
+static void fts5FreeVtab(Fts5FullTable *pTab){
if( pTab ){
- sqlite3Fts5IndexClose(pTab->pIndex);
+ sqlite3Fts5IndexClose(pTab->p.pIndex);
sqlite3Fts5StorageClose(pTab->pStorage);
- sqlite3Fts5ConfigFree(pTab->pConfig);
+ sqlite3Fts5ConfigFree(pTab->p.pConfig);
sqlite3_free(pTab);
}
}
@@ -213480,7 +247935,7 @@ static void fts5FreeVtab(Fts5Table *pTab){
** The xDisconnect() virtual table method.
*/
static int fts5DisconnectMethod(sqlite3_vtab *pVtab){
- fts5FreeVtab((Fts5Table*)pVtab);
+ fts5FreeVtab((Fts5FullTable*)pVtab);
return SQLITE_OK;
}
@@ -213491,7 +247946,7 @@ static int fts5DestroyMethod(sqlite3_vtab *pVtab){
Fts5Table *pTab = (Fts5Table*)pVtab;
int rc = sqlite3Fts5DropAll(pTab->pConfig);
if( rc==SQLITE_OK ){
- fts5FreeVtab((Fts5Table*)pVtab);
+ fts5FreeVtab((Fts5FullTable*)pVtab);
}
return rc;
}
@@ -213520,28 +247975,28 @@ static int fts5InitVtab(
const char **azConfig = (const char**)argv;
int rc = SQLITE_OK; /* Return code */
Fts5Config *pConfig = 0; /* Results of parsing argc/argv */
- Fts5Table *pTab = 0; /* New virtual table object */
+ Fts5FullTable *pTab = 0; /* New virtual table object */
/* Allocate the new vtab object and parse the configuration */
- pTab = (Fts5Table*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Table));
+ pTab = (Fts5FullTable*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5FullTable));
if( rc==SQLITE_OK ){
rc = sqlite3Fts5ConfigParse(pGlobal, db, argc, azConfig, &pConfig, pzErr);
assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 );
}
if( rc==SQLITE_OK ){
- pTab->pConfig = pConfig;
+ pTab->p.pConfig = pConfig;
pTab->pGlobal = pGlobal;
}
/* Open the index sub-system */
if( rc==SQLITE_OK ){
- rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->pIndex, pzErr);
+ rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->p.pIndex, pzErr);
}
/* Open the storage sub-system */
if( rc==SQLITE_OK ){
rc = sqlite3Fts5StorageOpen(
- pConfig, pTab->pIndex, bCreate, &pTab->pStorage, pzErr
+ pConfig, pTab->p.pIndex, bCreate, &pTab->pStorage, pzErr
);
}
@@ -213554,11 +248009,18 @@ static int fts5InitVtab(
if( rc==SQLITE_OK ){
assert( pConfig->pzErrmsg==0 );
pConfig->pzErrmsg = pzErr;
- rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex);
- sqlite3Fts5IndexRollback(pTab->pIndex);
+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ sqlite3Fts5IndexRollback(pTab->p.pIndex);
pConfig->pzErrmsg = 0;
}
+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
+ }
+
if( rc!=SQLITE_OK ){
fts5FreeVtab(pTab);
pTab = 0;
@@ -213620,21 +248082,62 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
#endif
}
+static int fts5UsePatternMatch(
+ Fts5Config *pConfig,
+ struct sqlite3_index_constraint *p
+){
+ assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB );
+ assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE );
+ if( pConfig->ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){
+ return 1;
+ }
+ if( pConfig->ePattern==FTS5_PATTERN_LIKE
+ && (p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB)
+ ){
+ return 1;
+ }
+ return 0;
+}
+
/*
-** Implementation of the xBestIndex method for FTS5 tables. Within the
+** Implementation of the xBestIndex method for FTS5 tables. Within the
** WHERE constraint, it searches for the following:
**
-** 1. A MATCH constraint against the special column.
+** 1. A MATCH constraint against the table column.
** 2. A MATCH constraint against the "rank" column.
-** 3. An == constraint against the rowid column.
-** 4. A < or <= constraint against the rowid column.
-** 5. A > or >= constraint against the rowid column.
+** 3. A MATCH constraint against some other column.
+** 4. An == constraint against the rowid column.
+** 5. A < or <= constraint against the rowid column.
+** 6. A > or >= constraint against the rowid column.
**
-** Within the ORDER BY, either:
+** Within the ORDER BY, the following are supported:
**
** 5. ORDER BY rank [ASC|DESC]
** 6. ORDER BY rowid [ASC|DESC]
**
+** Information for the xFilter call is passed via both the idxNum and
+** idxStr variables. Specifically, idxNum is a bitmask of the following
+** flags used to encode the ORDER BY clause:
+**
+** FTS5_BI_ORDER_RANK
+** FTS5_BI_ORDER_ROWID
+** FTS5_BI_ORDER_DESC
+**
+** idxStr is used to encode data from the WHERE clause. For each argument
+** passed to the xFilter method, the following is appended to idxStr:
+**
+** Match against table column: "m"
+** Match against rank column: "r"
+** Match against other column: "M<column-number>"
+** LIKE against other column: "L<column-number>"
+** GLOB against other column: "G<column-number>"
+** Equality constraint against the rowid: "="
+** A < or <= against the rowid: "<"
+** A > or >= against the rowid: ">"
+**
+** This function ensures that there is at most one "r" or "=". And that if
+** there exists an "=" then there is no "<" or ">".
+**
** Costs are assigned as follows:
**
** a) If an unusable MATCH operator is present in the WHERE clause, the
@@ -213662,32 +248165,18 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
Fts5Config *pConfig = pTab->pConfig;
const int nCol = pConfig->nCol;
int idxFlags = 0; /* Parameter passed through to xFilter() */
- int bHasMatch;
- int iNext;
int i;
- struct Constraint {
- int op; /* Mask against sqlite3_index_constraint.op */
- int fts5op; /* FTS5 mask for idxFlags */
- int iCol; /* 0==rowid, 1==tbl, 2==rank */
- int omit; /* True to omit this if found */
- int iConsIndex; /* Index in pInfo->aConstraint[] */
- } aConstraint[] = {
- {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
- FTS5_BI_MATCH, 1, 1, -1},
- {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
- FTS5_BI_RANK, 2, 1, -1},
- {SQLITE_INDEX_CONSTRAINT_EQ, FTS5_BI_ROWID_EQ, 0, 0, -1},
- {SQLITE_INDEX_CONSTRAINT_LT|SQLITE_INDEX_CONSTRAINT_LE,
- FTS5_BI_ROWID_LE, 0, 0, -1},
- {SQLITE_INDEX_CONSTRAINT_GT|SQLITE_INDEX_CONSTRAINT_GE,
- FTS5_BI_ROWID_GE, 0, 0, -1},
- };
+ char *idxStr;
+ int iIdxStr = 0;
+ int iCons = 0;
+
+ int bSeenEq = 0;
+ int bSeenGt = 0;
+ int bSeenLt = 0;
+ int bSeenMatch = 0;
+ int bSeenRank = 0;
- int aColMap[3];
- aColMap[0] = -1;
- aColMap[1] = nCol;
- aColMap[2] = nCol+1;
assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH );
assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH );
@@ -213695,42 +248184,94 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
assert( SQLITE_INDEX_CONSTRAINT_GE<SQLITE_INDEX_CONSTRAINT_MATCH );
assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH );
- /* Set idxFlags flags for all WHERE clause terms that will be used. */
+ if( pConfig->bLock ){
+ pTab->base.zErrMsg = sqlite3_mprintf(
+ "recursively defined fts5 content table"
+ );
+ return SQLITE_ERROR;
+ }
+
+ idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1);
+ if( idxStr==0 ) return SQLITE_NOMEM;
+ pInfo->idxStr = idxStr;
+ pInfo->needToFreeIdxStr = 1;
+
for(i=0; i<pInfo->nConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
int iCol = p->iColumn;
-
- if( (p->op==SQLITE_INDEX_CONSTRAINT_MATCH && iCol>=0 && iCol<=nCol)
- || (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol==nCol)
+ if( p->op==SQLITE_INDEX_CONSTRAINT_MATCH
+ || (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol>=nCol)
){
/* A MATCH operator or equivalent */
- if( p->usable ){
- idxFlags = (idxFlags & 0xFFFF) | FTS5_BI_MATCH | (iCol << 16);
- aConstraint[0].iConsIndex = i;
- }else{
- /* As there exists an unusable MATCH constraint this is an
+ if( p->usable==0 || iCol<0 ){
+ /* As there exists an unusable MATCH constraint this is an
** unusable plan. Set a prohibitively high cost. */
pInfo->estimatedCost = 1e50;
+ assert( iIdxStr < pInfo->nConstraint*6 + 1 );
+ idxStr[iIdxStr] = 0;
return SQLITE_OK;
- }
- }else if( p->op<=SQLITE_INDEX_CONSTRAINT_MATCH ){
- int j;
- for(j=1; j<ArraySize(aConstraint); j++){
- struct Constraint *pC = &aConstraint[j];
- if( iCol==aColMap[pC->iCol] && (p->op & pC->op) && p->usable ){
- pC->iConsIndex = i;
- idxFlags |= pC->fts5op;
+ }else{
+ if( iCol==nCol+1 ){
+ if( bSeenRank ) continue;
+ idxStr[iIdxStr++] = 'r';
+ bSeenRank = 1;
+ }else if( iCol>=0 ){
+ bSeenMatch = 1;
+ idxStr[iIdxStr++] = 'M';
+ sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
+ idxStr += strlen(&idxStr[iIdxStr]);
+ assert( idxStr[iIdxStr]=='\0' );
+ }
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ pInfo->aConstraintUsage[i].omit = 1;
+ }
+ }else if( p->usable ){
+ if( iCol>=0 && iCol<nCol && fts5UsePatternMatch(pConfig, p) ){
+ assert( p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB );
+ idxStr[iIdxStr++] = p->op==FTS5_PATTERN_LIKE ? 'L' : 'G';
+ sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
+ idxStr += strlen(&idxStr[iIdxStr]);
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ assert( idxStr[iIdxStr]=='\0' );
+ }else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){
+ idxStr[iIdxStr++] = '=';
+ bSeenEq = 1;
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ }
+ }
+ }
+
+ if( bSeenEq==0 ){
+ for(i=0; i<pInfo->nConstraint; i++){
+ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
+ if( p->iColumn<0 && p->usable ){
+ int op = p->op;
+ if( op==SQLITE_INDEX_CONSTRAINT_LT || op==SQLITE_INDEX_CONSTRAINT_LE ){
+ if( bSeenLt ) continue;
+ idxStr[iIdxStr++] = '<';
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ bSeenLt = 1;
+ }else
+ if( op==SQLITE_INDEX_CONSTRAINT_GT || op==SQLITE_INDEX_CONSTRAINT_GE ){
+ if( bSeenGt ) continue;
+ idxStr[iIdxStr++] = '>';
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ bSeenGt = 1;
}
}
}
}
+ idxStr[iIdxStr] = '\0';
- /* Set idxFlags flags for the ORDER BY clause */
+ /* Set idxFlags flags for the ORDER BY clause
+ **
+ ** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC".
+ */
if( pInfo->nOrderBy==1 ){
int iSort = pInfo->aOrderBy[0].iColumn;
- if( iSort==(pConfig->nCol+1) && BitFlagTest(idxFlags, FTS5_BI_MATCH) ){
+ if( iSort==(pConfig->nCol+1) && bSeenMatch ){
idxFlags |= FTS5_BI_ORDER_RANK;
- }else if( iSort==-1 ){
+ }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){
idxFlags |= FTS5_BI_ORDER_ROWID;
}
if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){
@@ -213742,33 +248283,22 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
}
/* Calculate the estimated cost based on the flags set in idxFlags. */
- bHasMatch = BitFlagTest(idxFlags, FTS5_BI_MATCH);
- if( BitFlagTest(idxFlags, FTS5_BI_ROWID_EQ) ){
- pInfo->estimatedCost = bHasMatch ? 100.0 : 10.0;
- if( bHasMatch==0 ) fts5SetUniqueFlag(pInfo);
- }else if( BitFlagAllTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
- pInfo->estimatedCost = bHasMatch ? 500.0 : 250000.0;
- }else if( BitFlagTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
- pInfo->estimatedCost = bHasMatch ? 750.0 : 750000.0;
+ if( bSeenEq ){
+ pInfo->estimatedCost = bSeenMatch ? 100.0 : 10.0;
+ if( bSeenMatch==0 ) fts5SetUniqueFlag(pInfo);
+ }else if( bSeenLt && bSeenGt ){
+ pInfo->estimatedCost = bSeenMatch ? 500.0 : 250000.0;
+ }else if( bSeenLt || bSeenGt ){
+ pInfo->estimatedCost = bSeenMatch ? 750.0 : 750000.0;
}else{
- pInfo->estimatedCost = bHasMatch ? 1000.0 : 1000000.0;
- }
-
- /* Assign argvIndex values to each constraint in use. */
- iNext = 1;
- for(i=0; i<ArraySize(aConstraint); i++){
- struct Constraint *pC = &aConstraint[i];
- if( pC->iConsIndex>=0 ){
- pInfo->aConstraintUsage[pC->iConsIndex].argvIndex = iNext++;
- pInfo->aConstraintUsage[pC->iConsIndex].omit = (unsigned char)pC->omit;
- }
+ pInfo->estimatedCost = bSeenMatch ? 1000.0 : 1000000.0;
}
pInfo->idxNum = idxFlags;
return SQLITE_OK;
}
-static int fts5NewTransaction(Fts5Table *pTab){
+static int fts5NewTransaction(Fts5FullTable *pTab){
Fts5Cursor *pCsr;
for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
if( pCsr->base.pVtab==(sqlite3_vtab*)pTab ) return SQLITE_OK;
@@ -213780,19 +248310,19 @@ static int fts5NewTransaction(Fts5Table *pTab){
** Implementation of xOpen method.
*/
static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
- Fts5Table *pTab = (Fts5Table*)pVTab;
- Fts5Config *pConfig = pTab->pConfig;
+ Fts5FullTable *pTab = (Fts5FullTable*)pVTab;
+ Fts5Config *pConfig = pTab->p.pConfig;
Fts5Cursor *pCsr = 0; /* New cursor object */
- int nByte; /* Bytes of space to allocate */
+ sqlite3_int64 nByte; /* Bytes of space to allocate */
int rc; /* Return code */
rc = fts5NewTransaction(pTab);
if( rc==SQLITE_OK ){
nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int);
- pCsr = (Fts5Cursor*)sqlite3_malloc(nByte);
+ pCsr = (Fts5Cursor*)sqlite3_malloc64(nByte);
if( pCsr ){
Fts5Global *pGlobal = pTab->pGlobal;
- memset(pCsr, 0, nByte);
+ memset(pCsr, 0, (size_t)nByte);
pCsr->aColumnSize = (int*)&pCsr[1];
pCsr->pNext = pGlobal->pCsr;
pGlobal->pCsr = pCsr;
@@ -213814,20 +248344,20 @@ static int fts5StmtType(Fts5Cursor *pCsr){
/*
** This function is called after the cursor passed as the only argument
-** is moved to point at a different row. It clears all cached data
+** is moved to point at a different row. It clears all cached data
** specific to the previous row stored by the cursor object.
*/
static void fts5CsrNewrow(Fts5Cursor *pCsr){
- CsrFlagSet(pCsr,
- FTS5CSR_REQUIRE_CONTENT
- | FTS5CSR_REQUIRE_DOCSIZE
- | FTS5CSR_REQUIRE_INST
- | FTS5CSR_REQUIRE_POSLIST
+ CsrFlagSet(pCsr,
+ FTS5CSR_REQUIRE_CONTENT
+ | FTS5CSR_REQUIRE_DOCSIZE
+ | FTS5CSR_REQUIRE_INST
+ | FTS5CSR_REQUIRE_POSLIST
);
}
static void fts5FreeCursorComponents(Fts5Cursor *pCsr){
- Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
Fts5Auxdata *pData;
Fts5Auxdata *pNext;
@@ -213861,6 +248391,7 @@ static void fts5FreeCursorComponents(Fts5Cursor *pCsr){
sqlite3_free(pCsr->zRankArgs);
}
+ sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan - (u8*)pCsr));
}
@@ -213871,7 +248402,7 @@ static void fts5FreeCursorComponents(Fts5Cursor *pCsr){
*/
static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){
if( pCursor ){
- Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
+ Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab);
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
Fts5Cursor **pp;
@@ -213892,7 +248423,7 @@ static int fts5SorterNext(Fts5Cursor *pCsr){
rc = sqlite3_step(pSorter->pStmt);
if( rc==SQLITE_DONE ){
rc = SQLITE_OK;
- CsrFlagSet(pCsr, FTS5CSR_EOF);
+ CsrFlagSet(pCsr, FTS5CSR_EOF|FTS5CSR_REQUIRE_CONTENT);
}else if( rc==SQLITE_ROW ){
const u8 *a;
const u8 *aBlob;
@@ -213925,14 +248456,14 @@ static int fts5SorterNext(Fts5Cursor *pCsr){
/*
-** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors
+** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors
** open on table pTab.
*/
-static void fts5TripCursors(Fts5Table *pTab){
+static void fts5TripCursors(Fts5FullTable *pTab){
Fts5Cursor *pCsr;
for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
if( pCsr->ePlan==FTS5_PLAN_MATCH
- && pCsr->base.pVtab==(sqlite3_vtab*)pTab
+ && pCsr->base.pVtab==(sqlite3_vtab*)pTab
){
CsrFlagSet(pCsr, FTS5CSR_REQUIRE_RESEEK);
}
@@ -213941,25 +248472,25 @@ static void fts5TripCursors(Fts5Table *pTab){
/*
** If the REQUIRE_RESEEK flag is set on the cursor passed as the first
-** argument, close and reopen all Fts5IndexIter iterators that the cursor
+** argument, close and reopen all Fts5IndexIter iterators that the cursor
** is using. Then attempt to move the cursor to a rowid equal to or laster
-** (in the cursors sort order - ASC or DESC) than the current rowid.
+** (in the cursors sort order - ASC or DESC) than the current rowid.
**
** If the new rowid is not equal to the old, set output parameter *pbSkip
** to 1 before returning. Otherwise, leave it unchanged.
**
-** Return SQLITE_OK if successful or if no reseek was required, or an
+** Return SQLITE_OK if successful or if no reseek was required, or an
** error code if an error occurred.
*/
static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){
int rc = SQLITE_OK;
assert( *pbSkip==0 );
if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){
- Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
int bDesc = pCsr->bDesc;
i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
- rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, iRowid, bDesc);
+ rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->p.pIndex, iRowid, bDesc);
if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){
*pbSkip = 1;
}
@@ -213976,7 +248507,7 @@ static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){
/*
-** Advance the cursor to the next row in the table that matches the
+** Advance the cursor to the next row in the table that matches the
** search criteria.
**
** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned
@@ -213988,10 +248519,20 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
int rc;
assert( (pCsr->ePlan<3)==
- (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE)
+ (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE)
);
assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) );
+ /* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table,
+ ** clear any token mappings accumulated at the fts5_index.c level. In
+ ** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH,
+ ** we need to retain the mappings for the entire query. */
+ if( pCsr->ePlan==FTS5_PLAN_MATCH
+ && ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata
+ ){
+ sqlite3Fts5ExprClearTokens(pCsr->pExpr);
+ }
+
if( pCsr->ePlan<3 ){
int bSkip = 0;
if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
@@ -214005,31 +248546,40 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
rc = SQLITE_OK;
break;
}
-
+
case FTS5_PLAN_SORTED_MATCH: {
rc = fts5SorterNext(pCsr);
break;
}
-
- default:
+
+ default: {
+ Fts5Config *pConfig = ((Fts5Table*)pCursor->pVtab)->pConfig;
+ pConfig->bLock++;
rc = sqlite3_step(pCsr->pStmt);
+ pConfig->bLock--;
if( rc!=SQLITE_ROW ){
CsrFlagSet(pCsr, FTS5CSR_EOF);
rc = sqlite3_reset(pCsr->pStmt);
+ if( rc!=SQLITE_OK ){
+ pCursor->pVtab->zErrMsg = sqlite3_mprintf(
+ "%s", sqlite3_errmsg(pConfig->db)
+ );
+ }
}else{
rc = SQLITE_OK;
}
break;
+ }
}
}
-
+
return rc;
}
static int fts5PrepareStatement(
sqlite3_stmt **ppStmt,
- Fts5Config *pConfig,
+ Fts5Config *pConfig,
const char *zFmt,
...
){
@@ -214041,9 +248591,9 @@ static int fts5PrepareStatement(
va_start(ap, zFmt);
zSql = sqlite3_vmprintf(zFmt, ap);
if( zSql==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM;
}else{
- rc = sqlite3_prepare_v3(pConfig->db, zSql, -1,
+ rc = sqlite3_prepare_v3(pConfig->db, zSql, -1,
SQLITE_PREPARE_PERSISTENT, &pRet, 0);
if( rc!=SQLITE_OK ){
*pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db));
@@ -214054,33 +248604,37 @@ static int fts5PrepareStatement(
va_end(ap);
*ppStmt = pRet;
return rc;
-}
+}
-static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
- Fts5Config *pConfig = pTab->pConfig;
+static int fts5CursorFirstSorted(
+ Fts5FullTable *pTab,
+ Fts5Cursor *pCsr,
+ int bDesc
+){
+ Fts5Config *pConfig = pTab->p.pConfig;
Fts5Sorter *pSorter;
int nPhrase;
- int nByte;
+ sqlite3_int64 nByte;
int rc;
const char *zRank = pCsr->zRank;
const char *zRankArgs = pCsr->zRankArgs;
-
+
nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1);
- pSorter = (Fts5Sorter*)sqlite3_malloc(nByte);
+ pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte);
if( pSorter==0 ) return SQLITE_NOMEM;
- memset(pSorter, 0, nByte);
+ memset(pSorter, 0, (size_t)nByte);
pSorter->nIdx = nPhrase;
/* TODO: It would be better to have some system for reusing statement
** handles here, rather than preparing a new one for each query. But that
** is not possible as SQLite reference counts the virtual table objects.
- ** And since the statement required here reads from this very virtual
+ ** And since the statement required here reads from this very virtual
** table, saving it creates a circular reference.
**
** If SQLite a built-in statement cache, this wouldn't be a problem. */
rc = fts5PrepareStatement(&pSorter->pStmt, pConfig,
- "SELECT rowid, rank FROM %Q.%Q ORDER BY %s(%s%s%s) %s",
+ "SELECT rowid, rank FROM %Q.%Q ORDER BY %s(\"%w\"%s%s) %s",
pConfig->zDb, pConfig->zName, zRank, pConfig->zName,
(zRankArgs ? ", " : ""),
(zRankArgs ? zRankArgs : ""),
@@ -214104,10 +248658,10 @@ static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
return rc;
}
-static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
+static int fts5CursorFirst(Fts5FullTable *pTab, Fts5Cursor *pCsr, int bDesc){
int rc;
Fts5Expr *pExpr = pCsr->pExpr;
- rc = sqlite3Fts5ExprFirst(pExpr, pTab->pIndex, pCsr->iFirstRowid, bDesc);
+ rc = sqlite3Fts5ExprFirst(pExpr, pTab->p.pIndex, pCsr->iFirstRowid, bDesc);
if( sqlite3Fts5ExprEof(pExpr) ){
CsrFlagSet(pCsr, FTS5CSR_EOF);
}
@@ -214122,8 +248676,8 @@ static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
** parameters.
*/
static int fts5SpecialMatch(
- Fts5Table *pTab,
- Fts5Cursor *pCsr,
+ Fts5FullTable *pTab,
+ Fts5Cursor *pCsr,
const char *zQuery
){
int rc = SQLITE_OK; /* Return code */
@@ -214133,18 +248687,18 @@ static int fts5SpecialMatch(
while( z[0]==' ' ) z++;
for(n=0; z[n] && z[n]!=' '; n++);
- assert( pTab->base.zErrMsg==0 );
+ assert( pTab->p.base.zErrMsg==0 );
pCsr->ePlan = FTS5_PLAN_SPECIAL;
- if( 0==sqlite3_strnicmp("reads", z, n) ){
- pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->pIndex);
+ if( n==5 && 0==sqlite3_strnicmp("reads", z, n) ){
+ pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->p.pIndex);
}
- else if( 0==sqlite3_strnicmp("id", z, n) ){
+ else if( n==2 && 0==sqlite3_strnicmp("id", z, n) ){
pCsr->iSpecial = pCsr->iCsrId;
}
else{
/* An unrecognized directive. Return an error message. */
- pTab->base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z);
+ pTab->p.base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z);
rc = SQLITE_ERROR;
}
@@ -214156,7 +248710,7 @@ static int fts5SpecialMatch(
** pTab. If one is found, return a pointer to the corresponding Fts5Auxiliary
** structure. Otherwise, if no such function exists, return NULL.
*/
-static Fts5Auxiliary *fts5FindAuxiliary(Fts5Table *pTab, const char *zName){
+static Fts5Auxiliary *fts5FindAuxiliary(Fts5FullTable *pTab, const char *zName){
Fts5Auxiliary *pAux;
for(pAux=pTab->pGlobal->pAux; pAux; pAux=pAux->pNext){
@@ -214169,8 +248723,8 @@ static Fts5Auxiliary *fts5FindAuxiliary(Fts5Table *pTab, const char *zName){
static int fts5FindRankFunction(Fts5Cursor *pCsr){
- Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
- Fts5Config *pConfig = pTab->pConfig;
+ Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
+ Fts5Config *pConfig = pTab->p.pConfig;
int rc = SQLITE_OK;
Fts5Auxiliary *pAux = 0;
const char *zRank = pCsr->zRank;
@@ -214186,7 +248740,7 @@ static int fts5FindRankFunction(Fts5Cursor *pCsr){
assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 );
if( rc==SQLITE_OK ){
if( SQLITE_ROW==sqlite3_step(pStmt) ){
- int nByte;
+ sqlite3_int64 nByte;
pCsr->nRankArg = sqlite3_column_count(pStmt);
nByte = sizeof(sqlite3_value*)*pCsr->nRankArg;
pCsr->apRankArg = (sqlite3_value**)sqlite3Fts5MallocZero(&rc, nByte);
@@ -214208,8 +248762,8 @@ static int fts5FindRankFunction(Fts5Cursor *pCsr){
if( rc==SQLITE_OK ){
pAux = fts5FindAuxiliary(pTab, zRank);
if( pAux==0 ){
- assert( pTab->base.zErrMsg==0 );
- pTab->base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank);
+ assert( pTab->p.base.zErrMsg==0 );
+ pTab->p.base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank);
rc = SQLITE_ERROR;
}
}
@@ -214221,7 +248775,7 @@ static int fts5FindRankFunction(Fts5Cursor *pCsr){
static int fts5CursorParseRank(
Fts5Config *pConfig,
- Fts5Cursor *pCsr,
+ Fts5Cursor *pCsr,
sqlite3_value *pRank
){
int rc = SQLITE_OK;
@@ -214270,7 +248824,7 @@ static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){
** This is the xFilter interface for the virtual table. See
** the virtual table xFilter method documentation for additional
** information.
-**
+**
** There are three possible query strategies:
**
** 1. Full-text search using a MATCH operator.
@@ -214280,27 +248834,32 @@ static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){
static int fts5FilterMethod(
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
int idxNum, /* Strategy index */
- const char *zUnused, /* Unused */
+ const char *idxStr, /* Unused */
int nVal, /* Number of elements in apVal */
sqlite3_value **apVal /* Arguments for the indexing scheme */
){
- Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
- Fts5Config *pConfig = pTab->pConfig;
+ Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab);
+ Fts5Config *pConfig = pTab->p.pConfig;
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
int rc = SQLITE_OK; /* Error code */
- int iVal = 0; /* Counter for apVal[] */
int bDesc; /* True if ORDER BY [rank|rowid] DESC */
int bOrderByRank; /* True if ORDER BY rank */
- sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */
sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */
sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
int iCol; /* Column on LHS of MATCH operator */
char **pzErrmsg = pConfig->pzErrmsg;
+ int i;
+ int iIdxStr = 0;
+ Fts5Expr *pExpr = 0;
- UNUSED_PARAM(zUnused);
- UNUSED_PARAM(nVal);
+ if( pConfig->bLock ){
+ pTab->p.base.zErrMsg = sqlite3_mprintf(
+ "recursively defined fts5 content table"
+ );
+ return SQLITE_ERROR;
+ }
if( pCsr->ePlan ){
fts5FreeCursorComponents(pCsr);
@@ -214313,27 +248872,78 @@ static int fts5FilterMethod(
assert( pCsr->pRank==0 );
assert( pCsr->zRank==0 );
assert( pCsr->zRankArgs==0 );
+ assert( pTab->pSortCsr==0 || nVal==0 );
- assert( pzErrmsg==0 || pzErrmsg==&pTab->base.zErrMsg );
- pConfig->pzErrmsg = &pTab->base.zErrMsg;
+ assert( pzErrmsg==0 || pzErrmsg==&pTab->p.base.zErrMsg );
+ pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
- /* Decode the arguments passed through to this function.
- **
- ** Note: The following set of if(...) statements must be in the same
- ** order as the corresponding entries in the struct at the top of
- ** fts5BestIndexMethod(). */
- if( BitFlagTest(idxNum, FTS5_BI_MATCH) ) pMatch = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_RANK) ) pRank = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_ROWID_EQ) ) pRowidEq = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_ROWID_LE) ) pRowidLe = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_ROWID_GE) ) pRowidGe = apVal[iVal++];
- iCol = (idxNum>>16);
- assert( iCol>=0 && iCol<=pConfig->nCol );
- assert( iVal==nVal );
+ /* Decode the arguments passed through to this function. */
+ for(i=0; i<nVal; i++){
+ switch( idxStr[iIdxStr++] ){
+ case 'r':
+ pRank = apVal[i];
+ break;
+ case 'M': {
+ const char *zText = (const char*)sqlite3_value_text(apVal[i]);
+ if( zText==0 ) zText = "";
+ iCol = 0;
+ do{
+ iCol = iCol*10 + (idxStr[iIdxStr]-'0');
+ iIdxStr++;
+ }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
+
+ if( zText[0]=='*' ){
+ /* The user has issued a query of the form "MATCH '*...'". This
+ ** indicates that the MATCH expression is not a full text query,
+ ** but a request for an internal parameter. */
+ rc = fts5SpecialMatch(pTab, pCsr, &zText[1]);
+ goto filter_out;
+ }else{
+ char **pzErr = &pTab->p.base.zErrMsg;
+ rc = sqlite3Fts5ExprNew(pConfig, 0, iCol, zText, &pExpr, pzErr);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
+ pExpr = 0;
+ }
+ if( rc!=SQLITE_OK ) goto filter_out;
+ }
+
+ break;
+ }
+ case 'L':
+ case 'G': {
+ int bGlob = (idxStr[iIdxStr-1]=='G');
+ const char *zText = (const char*)sqlite3_value_text(apVal[i]);
+ iCol = 0;
+ do{
+ iCol = iCol*10 + (idxStr[iIdxStr]-'0');
+ iIdxStr++;
+ }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
+ if( zText ){
+ rc = sqlite3Fts5ExprPattern(pConfig, bGlob, iCol, zText, &pExpr);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
+ pExpr = 0;
+ }
+ if( rc!=SQLITE_OK ) goto filter_out;
+ break;
+ }
+ case '=':
+ pRowidEq = apVal[i];
+ break;
+ case '<':
+ pRowidLe = apVal[i];
+ break;
+ default: assert( idxStr[iIdxStr-1]=='>' );
+ pRowidGe = apVal[i];
+ break;
+ }
+ }
bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0);
pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0);
- /* Set the cursor upper and lower rowid limits. Only some strategies
+ /* Set the cursor upper and lower rowid limits. Only some strategies
** actually use them. This is ok, as the xBestIndex() method leaves the
** sqlite3_index_constraint.omit flag clear for range constraints
** on the rowid field. */
@@ -214348,15 +248958,18 @@ static int fts5FilterMethod(
pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64);
}
+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ if( rc!=SQLITE_OK ) goto filter_out;
+
if( pTab->pSortCsr ){
- /* If pSortCsr is non-NULL, then this call is being made as part of
+ /* If pSortCsr is non-NULL, then this call is being made as part of
** processing for a "... MATCH <expr> ORDER BY rank" query (ePlan is
** set to FTS5_PLAN_SORTED_MATCH). pSortCsr is the cursor that will
- ** return results to the user for this query. The current cursor
- ** (pCursor) is used to execute the query issued by function
+ ** return results to the user for this query. The current cursor
+ ** (pCursor) is used to execute the query issued by function
** fts5CursorFirstSorted() above. */
assert( pRowidEq==0 && pRowidLe==0 && pRowidGe==0 && pRank==0 );
- assert( nVal==0 && pMatch==0 && bOrderByRank==0 && bDesc==0 );
+ assert( nVal==0 && bOrderByRank==0 && bDesc==0 );
assert( pCsr->iLastRowid==LARGEST_INT64 );
assert( pCsr->iFirstRowid==SMALLEST_INT64 );
if( pTab->pSortCsr->bDesc ){
@@ -214369,29 +248982,16 @@ static int fts5FilterMethod(
pCsr->ePlan = FTS5_PLAN_SOURCE;
pCsr->pExpr = pTab->pSortCsr->pExpr;
rc = fts5CursorFirst(pTab, pCsr, bDesc);
- }else if( pMatch ){
- const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
- if( zExpr==0 ) zExpr = "";
-
+ }else if( pCsr->pExpr ){
+ assert( rc==SQLITE_OK );
rc = fts5CursorParseRank(pConfig, pCsr, pRank);
if( rc==SQLITE_OK ){
- if( zExpr[0]=='*' ){
- /* The user has issued a query of the form "MATCH '*...'". This
- ** indicates that the MATCH expression is not a full text query,
- ** but a request for an internal parameter. */
- rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]);
- }else{
- char **pzErr = &pTab->base.zErrMsg;
- rc = sqlite3Fts5ExprNew(pConfig, iCol, zExpr, &pCsr->pExpr, pzErr);
- if( rc==SQLITE_OK ){
- if( bOrderByRank ){
- pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
- rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
- }else{
- pCsr->ePlan = FTS5_PLAN_MATCH;
- rc = fts5CursorFirst(pTab, pCsr, bDesc);
- }
- }
+ if( bOrderByRank ){
+ pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
+ rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
+ }else{
+ pCsr->ePlan = FTS5_PLAN_MATCH;
+ rc = fts5CursorFirst(pTab, pCsr, bDesc);
}
}
}else if( pConfig->zContent==0 ){
@@ -214404,11 +249004,12 @@ static int fts5FilterMethod(
** by rowid (ePlan==FTS5_PLAN_ROWID). */
pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN);
rc = sqlite3Fts5StorageStmt(
- pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->base.zErrMsg
+ pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg
);
if( rc==SQLITE_OK ){
- if( pCsr->ePlan==FTS5_PLAN_ROWID ){
- sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
+ if( pRowidEq!=0 ){
+ assert( pCsr->ePlan==FTS5_PLAN_ROWID );
+ sqlite3_bind_value(pCsr->pStmt, 1, pRowidEq);
}else{
sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid);
sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid);
@@ -214417,12 +249018,14 @@ static int fts5FilterMethod(
}
}
+ filter_out:
+ sqlite3Fts5ExprFree(pExpr);
pConfig->pzErrmsg = pzErrmsg;
return rc;
}
-/*
-** This is the xEof method of the virtual table. SQLite calls this
+/*
+** This is the xEof method of the virtual table. SQLite calls this
** routine to find out if it has reached the end of a result set.
*/
static int fts5EofMethod(sqlite3_vtab_cursor *pCursor){
@@ -214434,9 +249037,9 @@ static int fts5EofMethod(sqlite3_vtab_cursor *pCursor){
** Return the rowid that the cursor currently points to.
*/
static i64 fts5CursorRowid(Fts5Cursor *pCsr){
- assert( pCsr->ePlan==FTS5_PLAN_MATCH
- || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH
- || pCsr->ePlan==FTS5_PLAN_SOURCE
+ assert( pCsr->ePlan==FTS5_PLAN_MATCH
+ || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH
+ || pCsr->ePlan==FTS5_PLAN_SOURCE
);
if( pCsr->pSorter ){
return pCsr->pSorter->iRowid;
@@ -214445,7 +249048,7 @@ static i64 fts5CursorRowid(Fts5Cursor *pCsr){
}
}
-/*
+/*
** This is the xRowid method. The SQLite core calls this routine to
** retrieve the rowid for the current row of the result set. fts5
** exposes %_content.rowid as the rowid for the virtual table. The
@@ -214454,7 +249057,7 @@ static i64 fts5CursorRowid(Fts5Cursor *pCsr){
static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
int ePlan = pCsr->ePlan;
-
+
assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 );
switch( ePlan ){
case FTS5_PLAN_SPECIAL:
@@ -214485,22 +249088,25 @@ static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){
int rc = SQLITE_OK;
- /* If the cursor does not yet have a statement handle, obtain one now. */
+ /* If the cursor does not yet have a statement handle, obtain one now. */
if( pCsr->pStmt==0 ){
- Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
int eStmt = fts5StmtType(pCsr);
rc = sqlite3Fts5StorageStmt(
- pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->base.zErrMsg:0)
+ pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->p.base.zErrMsg:0)
);
- assert( rc!=SQLITE_OK || pTab->base.zErrMsg==0 );
+ assert( rc!=SQLITE_OK || pTab->p.base.zErrMsg==0 );
assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) );
}
if( rc==SQLITE_OK && CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ){
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
assert( pCsr->pExpr );
sqlite3_reset(pCsr->pStmt);
sqlite3_bind_int64(pCsr->pStmt, 1, fts5CursorRowid(pCsr));
+ pTab->pConfig->bLock++;
rc = sqlite3_step(pCsr->pStmt);
+ pTab->pConfig->bLock--;
if( rc==SQLITE_ROW ){
rc = SQLITE_OK;
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT);
@@ -214508,17 +249114,21 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){
rc = sqlite3_reset(pCsr->pStmt);
if( rc==SQLITE_OK ){
rc = FTS5_CORRUPT;
+ }else if( pTab->pConfig->pzErrmsg ){
+ *pTab->pConfig->pzErrmsg = sqlite3_mprintf(
+ "%s", sqlite3_errmsg(pTab->pConfig->db)
+ );
}
}
}
return rc;
}
-static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){
+static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){
va_list ap; /* ... printf arguments */
va_start(ap, zFormat);
- assert( p->base.zErrMsg==0 );
- p->base.zErrMsg = sqlite3_vmprintf(zFormat, ap);
+ assert( p->p.base.zErrMsg==0 );
+ p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
}
@@ -214529,7 +249139,7 @@ static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){
** INSERT INTO fts(fts) VALUES($pCmd)
** INSERT INTO fts(fts, rank) VALUES($pCmd, $pVal)
**
-** Argument pVal is the value assigned to column "fts" by the INSERT
+** Argument pVal is the value assigned to column "fts" by the INSERT
** statement. This function returns SQLITE_OK if successful, or an SQLite
** error code if an error occurs.
**
@@ -214538,17 +249148,18 @@ static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){
** more commands are added to this function.
*/
static int fts5SpecialInsert(
- Fts5Table *pTab, /* Fts5 table object */
+ Fts5FullTable *pTab, /* Fts5 table object */
const char *zCmd, /* Text inserted into table-name column */
sqlite3_value *pVal /* Value inserted into rank column */
){
- Fts5Config *pConfig = pTab->pConfig;
+ Fts5Config *pConfig = pTab->p.pConfig;
int rc = SQLITE_OK;
int bError = 0;
+ int bLoadConfig = 0;
if( 0==sqlite3_stricmp("delete-all", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
- fts5SetVtabError(pTab,
+ fts5SetVtabError(pTab,
"'delete-all' may only be used with a "
"contentless or external content fts5 table"
);
@@ -214556,30 +249167,38 @@ static int fts5SpecialInsert(
}else{
rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
}
+ bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NONE ){
- fts5SetVtabError(pTab,
+ fts5SetVtabError(pTab,
"'rebuild' may not be used with a contentless fts5 table"
);
rc = SQLITE_ERROR;
}else{
rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
}
+ bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("optimize", zCmd) ){
rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
}else if( 0==sqlite3_stricmp("merge", zCmd) ){
int nMerge = sqlite3_value_int(pVal);
rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge);
}else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){
- rc = sqlite3Fts5StorageIntegrity(pTab->pStorage);
+ int iArg = sqlite3_value_int(pVal);
+ rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, iArg);
#ifdef SQLITE_DEBUG
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
#endif
+ }else if( 0==sqlite3_stricmp("flush", zCmd) ){
+ rc = sqlite3Fts5FlushToDisk(&pTab->p);
}else{
- rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex);
+ rc = sqlite3Fts5FlushToDisk(&pTab->p);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ }
if( rc==SQLITE_OK ){
- rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, zCmd, pVal, &bError);
+ rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError);
}
if( rc==SQLITE_OK ){
if( bError ){
@@ -214589,11 +249208,17 @@ static int fts5SpecialInsert(
}
}
}
+
+ if( rc==SQLITE_OK && bLoadConfig ){
+ pTab->p.pConfig->iCookie--;
+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ }
+
return rc;
}
static int fts5SpecialDelete(
- Fts5Table *pTab,
+ Fts5FullTable *pTab,
sqlite3_value **apVal
){
int rc = SQLITE_OK;
@@ -214606,9 +249231,9 @@ static int fts5SpecialDelete(
}
static void fts5StorageInsert(
- int *pRc,
- Fts5Table *pTab,
- sqlite3_value **apVal,
+ int *pRc,
+ Fts5FullTable *pTab,
+ sqlite3_value **apVal,
i64 *piRowid
){
int rc = *pRc;
@@ -214621,13 +249246,13 @@ static void fts5StorageInsert(
*pRc = rc;
}
-/*
-** This function is the implementation of the xUpdate callback used by
+/*
+** This function is the implementation of the xUpdate callback used by
** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
** inserted, updated or deleted.
**
** A delete specifies a single argument - the rowid of the row to remove.
-**
+**
** Update and insert operations pass:
**
** 1. The "old" rowid, or NULL.
@@ -214641,42 +249266,54 @@ static int fts5UpdateMethod(
sqlite3_value **apVal, /* Array of arguments */
sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */
){
- Fts5Table *pTab = (Fts5Table*)pVtab;
- Fts5Config *pConfig = pTab->pConfig;
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ Fts5Config *pConfig = pTab->p.pConfig;
int eType0; /* value_type() of apVal[0] */
int rc = SQLITE_OK; /* Return code */
+ int bUpdateOrDelete = 0;
/* A transaction must be open when this is called. */
- assert( pTab->ts.eState==1 );
+ assert( pTab->ts.eState==1 || pTab->ts.eState==2 );
assert( pVtab->zErrMsg==0 );
assert( nArg==1 || nArg==(2+pConfig->nCol+2) );
- assert( nArg==1
- || sqlite3_value_type(apVal[1])==SQLITE_INTEGER
- || sqlite3_value_type(apVal[1])==SQLITE_NULL
+ assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER
+ || sqlite3_value_type(apVal[0])==SQLITE_NULL
);
- assert( pTab->pConfig->pzErrmsg==0 );
- pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg;
+ assert( pTab->p.pConfig->pzErrmsg==0 );
+ if( pConfig->pgsz==0 ){
+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
/* Put any active cursors into REQUIRE_SEEK state. */
fts5TripCursors(pTab);
eType0 = sqlite3_value_type(apVal[0]);
- if( eType0==SQLITE_NULL
- && sqlite3_value_type(apVal[2+pConfig->nCol])!=SQLITE_NULL
+ if( eType0==SQLITE_NULL
+ && sqlite3_value_type(apVal[2+pConfig->nCol])!=SQLITE_NULL
){
/* A "special" INSERT op. These are handled separately. */
const char *z = (const char*)sqlite3_value_text(apVal[2+pConfig->nCol]);
- if( pConfig->eContent!=FTS5_CONTENT_NORMAL
- && 0==sqlite3_stricmp("delete", z)
+ if( pConfig->eContent!=FTS5_CONTENT_NORMAL
+ && 0==sqlite3_stricmp("delete", z)
){
- rc = fts5SpecialDelete(pTab, apVal);
+ if( pConfig->bContentlessDelete ){
+ fts5SetVtabError(pTab,
+ "'delete' may not be used with a contentless_delete=1 table"
+ );
+ rc = SQLITE_ERROR;
+ }else{
+ rc = fts5SpecialDelete(pTab, apVal);
+ }
}else{
rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
}
}else{
/* A regular INSERT, UPDATE or DELETE statement. The trick here is that
- ** any conflict on the rowid value must be detected before any
+ ** any conflict on the rowid value must be detected before any
** modifications are made to the database file. There are 4 cases:
**
** 1) DELETE
@@ -214687,7 +249324,7 @@ static int fts5UpdateMethod(
** Cases 3 and 4 may violate the rowid constraint.
*/
int eConflict = SQLITE_ABORT;
- if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->bContentlessDelete ){
eConflict = sqlite3_vtab_on_conflict(pConfig->db);
}
@@ -214695,10 +249332,14 @@ static int fts5UpdateMethod(
assert( nArg!=1 || eType0==SQLITE_INTEGER );
/* Filter out attempts to run UPDATE or DELETE on contentless tables.
- ** This is not suported. */
- if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){
- pTab->base.zErrMsg = sqlite3_mprintf(
- "cannot %s contentless fts5 table: %s",
+ ** This is not suported. Except - they are both supported if the CREATE
+ ** VIRTUAL TABLE statement contained "contentless_delete=1". */
+ if( eType0==SQLITE_INTEGER
+ && pConfig->eContent==FTS5_CONTENT_NONE
+ && pConfig->bContentlessDelete==0
+ ){
+ pTab->p.base.zErrMsg = sqlite3_mprintf(
+ "cannot %s contentless fts5 table: %s",
(nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
);
rc = SQLITE_ERROR;
@@ -214708,71 +249349,93 @@ static int fts5UpdateMethod(
else if( nArg==1 ){
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0);
+ bUpdateOrDelete = 1;
}
- /* INSERT */
- else if( eType0!=SQLITE_INTEGER ){
- /* If this is a REPLACE, first remove the current entry (if any) */
- if( eConflict==SQLITE_REPLACE
- && sqlite3_value_type(apVal[1])==SQLITE_INTEGER
- ){
- i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
+ /* INSERT or UPDATE */
+ else{
+ int eType1 = sqlite3_value_numeric_type(apVal[1]);
+
+ if( eType1!=SQLITE_INTEGER && eType1!=SQLITE_NULL ){
+ rc = SQLITE_MISMATCH;
}
- fts5StorageInsert(&rc, pTab, apVal, pRowid);
- }
- /* UPDATE */
- else{
- i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */
- i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */
- if( iOld!=iNew ){
- if( eConflict==SQLITE_REPLACE ){
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
- }
- fts5StorageInsert(&rc, pTab, apVal, pRowid);
- }else{
- rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid);
- if( rc==SQLITE_OK ){
+ else if( eType0!=SQLITE_INTEGER ){
+ /* An INSERT statement. If the conflict-mode is REPLACE, first remove
+ ** the current entry (if any). */
+ if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
+ i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
+ bUpdateOrDelete = 1;
+ }
+ fts5StorageInsert(&rc, pTab, apVal, pRowid);
+ }
+
+ /* UPDATE */
+ else{
+ i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */
+ i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */
+ if( eType1==SQLITE_INTEGER && iOld!=iNew ){
+ if( eConflict==SQLITE_REPLACE ){
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
+ }
+ fts5StorageInsert(&rc, pTab, apVal, pRowid);
+ }else{
+ rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid);
+ }
}
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *pRowid);
- }
+ }else{
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
+ fts5StorageInsert(&rc, pTab, apVal, pRowid);
}
- }else{
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
- fts5StorageInsert(&rc, pTab, apVal, pRowid);
+ bUpdateOrDelete = 1;
}
}
}
- pTab->pConfig->pzErrmsg = 0;
+ if( rc==SQLITE_OK
+ && bUpdateOrDelete
+ && pConfig->bSecureDelete
+ && pConfig->iVersion==FTS5_CURRENT_VERSION
+ ){
+ rc = sqlite3Fts5StorageConfigValue(
+ pTab->pStorage, "version", 0, FTS5_CURRENT_VERSION_SECUREDELETE
+ );
+ if( rc==SQLITE_OK ){
+ pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE;
+ }
+ }
+
+ pTab->p.pConfig->pzErrmsg = 0;
return rc;
}
/*
-** Implementation of xSync() method.
+** Implementation of xSync() method.
*/
static int fts5SyncMethod(sqlite3_vtab *pVtab){
int rc;
- Fts5Table *pTab = (Fts5Table*)pVtab;
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
fts5CheckTransactionState(pTab, FTS5_SYNC, 0);
- pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg;
- fts5TripCursors(pTab);
- rc = sqlite3Fts5StorageSync(pTab->pStorage);
- pTab->pConfig->pzErrmsg = 0;
+ pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
+ rc = sqlite3Fts5FlushToDisk(&pTab->p);
+ pTab->p.pConfig->pzErrmsg = 0;
return rc;
}
/*
-** Implementation of xBegin() method.
+** Implementation of xBegin() method.
*/
static int fts5BeginMethod(sqlite3_vtab *pVtab){
- fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_BEGIN, 0);
- fts5NewTransaction((Fts5Table*)pVtab);
+ fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0);
+ fts5NewTransaction((Fts5FullTable*)pVtab);
return SQLITE_OK;
}
@@ -214783,7 +249446,7 @@ static int fts5BeginMethod(sqlite3_vtab *pVtab){
*/
static int fts5CommitMethod(sqlite3_vtab *pVtab){
UNUSED_PARAM(pVtab); /* Call below is a no-op for NDEBUG builds */
- fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_COMMIT, 0);
+ fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_COMMIT, 0);
return SQLITE_OK;
}
@@ -214793,7 +249456,7 @@ static int fts5CommitMethod(sqlite3_vtab *pVtab){
*/
static int fts5RollbackMethod(sqlite3_vtab *pVtab){
int rc;
- Fts5Table *pTab = (Fts5Table*)pVtab;
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0);
rc = sqlite3Fts5StorageRollback(pTab->pStorage);
return rc;
@@ -214812,24 +249475,24 @@ static int fts5ApiColumnCount(Fts5Context *pCtx){
}
static int fts5ApiColumnTotalSize(
- Fts5Context *pCtx,
- int iCol,
+ Fts5Context *pCtx,
+ int iCol,
sqlite3_int64 *pnToken
){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
return sqlite3Fts5StorageSize(pTab->pStorage, iCol, pnToken);
}
static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow);
}
static int fts5ApiTokenize(
- Fts5Context *pCtx,
- const char *pText, int nText,
+ Fts5Context *pCtx,
+ const char *pText, int nText,
void *pUserData,
int (*xToken)(void*, int, const char*, int, int, int)
){
@@ -214851,14 +249514,19 @@ static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){
}
static int fts5ApiColumnText(
- Fts5Context *pCtx,
- int iCol,
- const char **pz,
+ Fts5Context *pCtx,
+ int iCol,
+ const char **pz,
int *pn
){
int rc = SQLITE_OK;
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ if( iCol<0 || iCol>=pTab->pConfig->nCol ){
+ rc = SQLITE_RANGE;
+ }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab))
+ || pCsr->ePlan==FTS5_PLAN_SPECIAL
+ ){
*pz = 0;
*pn = 0;
}else{
@@ -214872,8 +249540,8 @@ static int fts5ApiColumnText(
}
static int fts5CsrPoslist(
- Fts5Cursor *pCsr,
- int iPhrase,
+ Fts5Cursor *pCsr,
+ int iPhrase,
const u8 **pa,
int *pn
){
@@ -214881,8 +249549,9 @@ static int fts5CsrPoslist(
int rc = SQLITE_OK;
int bLive = (pCsr->pSorter==0);
- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
-
+ if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){
+ rc = SQLITE_RANGE;
+ }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
Fts5PoslistPopulator *aPopulator;
int i;
@@ -214906,15 +249575,21 @@ static int fts5CsrPoslist(
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST);
}
- if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
- Fts5Sorter *pSorter = pCsr->pSorter;
- int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
- *pn = pSorter->aIdx[iPhrase] - i1;
- *pa = &pSorter->aPoslist[i1];
+ if( rc==SQLITE_OK ){
+ if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
+ *pn = pSorter->aIdx[iPhrase] - i1;
+ *pa = &pSorter->aPoslist[i1];
+ }else{
+ *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
+ }
}else{
- *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
+ *pa = 0;
+ *pn = 0;
}
+
return rc;
}
@@ -214927,10 +249602,11 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){
int rc = SQLITE_OK;
Fts5PoslistReader *aIter; /* One iterator for each phrase */
int nIter; /* Number of iterators/phrases */
-
+ int nCol = ((Fts5Table*)pCsr->base.pVtab)->pConfig->nCol;
+
nIter = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
if( pCsr->aInstIter==0 ){
- int nByte = sizeof(Fts5PoslistReader) * nIter;
+ sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * nIter;
pCsr->aInstIter = (Fts5PoslistReader*)sqlite3Fts5MallocZero(&rc, nByte);
}
aIter = pCsr->aInstIter;
@@ -214942,7 +249618,7 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){
/* Initialize all iterators */
for(i=0; i<nIter && rc==SQLITE_OK; i++){
const u8 *a;
- int n;
+ int n;
rc = fts5CsrPoslist(pCsr, i, &a, &n);
if( rc==SQLITE_OK ){
sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]);
@@ -214954,8 +249630,8 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){
int *aInst;
int iBest = -1;
for(i=0; i<nIter; i++){
- if( (aIter[i].bEof==0)
- && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos)
+ if( (aIter[i].bEof==0)
+ && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos)
){
iBest = i;
}
@@ -214964,13 +249640,15 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){
nInst++;
if( nInst>=pCsr->nInstAlloc ){
- pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32;
- aInst = (int*)sqlite3_realloc(
- pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3
+ int nNewSize = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32;
+ aInst = (int*)sqlite3_realloc64(
+ pCsr->aInst, nNewSize*sizeof(int)*3
);
if( aInst ){
pCsr->aInst = aInst;
+ pCsr->nInstAlloc = nNewSize;
}else{
+ nInst--;
rc = SQLITE_NOMEM;
break;
}
@@ -214980,6 +249658,10 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){
aInst[0] = iBest;
aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos);
aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos);
+ if( aInst[1]<0 || aInst[1]>=nCol ){
+ rc = FTS5_CORRUPT;
+ break;
+ }
sqlite3Fts5PoslistReaderNext(&aIter[iBest]);
}
}
@@ -214993,7 +249675,7 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){
static int fts5ApiInstCount(Fts5Context *pCtx, int *pnInst){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
int rc = SQLITE_OK;
- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
|| SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){
*pnInst = pCsr->nInstCount;
}
@@ -215001,25 +249683,19 @@ static int fts5ApiInstCount(Fts5Context *pCtx, int *pnInst){
}
static int fts5ApiInst(
- Fts5Context *pCtx,
- int iIdx,
- int *piPhrase,
- int *piCol,
+ Fts5Context *pCtx,
+ int iIdx,
+ int *piPhrase,
+ int *piCol,
int *piOff
){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
int rc = SQLITE_OK;
- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
- || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
+ || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
){
if( iIdx<0 || iIdx>=pCsr->nInstCount ){
rc = SQLITE_RANGE;
-#if 0
- }else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){
- *piPhrase = pCsr->aInst[iIdx*3];
- *piCol = pCsr->aInst[iIdx*3 + 2];
- *piOff = -1;
-#endif
}else{
*piPhrase = pCsr->aInst[iIdx*3];
*piCol = pCsr->aInst[iIdx*3 + 1];
@@ -215052,8 +249728,8 @@ static int fts5ColumnSizeCb(
static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
- Fts5Config *pConfig = pTab->pConfig;
+ Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
+ Fts5Config *pConfig = pTab->p.pConfig;
int rc = SQLITE_OK;
if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){
@@ -215159,8 +249835,8 @@ static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){
}
static void fts5ApiPhraseNext(
- Fts5Context *pUnused,
- Fts5PhraseIter *pIter,
+ Fts5Context *pUnused,
+ Fts5PhraseIter *pIter,
int *piCol, int *piOff
){
UNUSED_PARAM(pUnused);
@@ -215181,16 +249857,17 @@ static void fts5ApiPhraseNext(
}
static int fts5ApiPhraseFirst(
- Fts5Context *pCtx,
- int iPhrase,
- Fts5PhraseIter *pIter,
+ Fts5Context *pCtx,
+ int iPhrase,
+ Fts5PhraseIter *pIter,
int *piCol, int *piOff
){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
int n;
int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
if( rc==SQLITE_OK ){
- pIter->b = &pIter->a[n];
+ assert( pIter->a || n==0 );
+ pIter->b = (pIter->a ? &pIter->a[n] : 0);
*piCol = 0;
*piOff = 0;
fts5ApiPhraseNext(pCtx, pIter, piCol, piOff);
@@ -215199,8 +249876,8 @@ static int fts5ApiPhraseFirst(
}
static void fts5ApiPhraseNextColumn(
- Fts5Context *pCtx,
- Fts5PhraseIter *pIter,
+ Fts5Context *pCtx,
+ Fts5PhraseIter *pIter,
int *piCol
){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
@@ -215229,9 +249906,9 @@ static void fts5ApiPhraseNextColumn(
}
static int fts5ApiPhraseFirstColumn(
- Fts5Context *pCtx,
- int iPhrase,
- Fts5PhraseIter *pIter,
+ Fts5Context *pCtx,
+ int iPhrase,
+ Fts5PhraseIter *pIter,
int *piCol
){
int rc = SQLITE_OK;
@@ -215249,7 +249926,8 @@ static int fts5ApiPhraseFirstColumn(
rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n);
}
if( rc==SQLITE_OK ){
- pIter->b = &pIter->a[n];
+ assert( pIter->a || n==0 );
+ pIter->b = (pIter->a ? &pIter->a[n] : 0);
*piCol = 0;
fts5ApiPhraseNextColumn(pCtx, pIter, piCol);
}
@@ -215257,7 +249935,8 @@ static int fts5ApiPhraseFirstColumn(
int n;
rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
if( rc==SQLITE_OK ){
- pIter->b = &pIter->a[n];
+ assert( pIter->a || n==0 );
+ pIter->b = (pIter->a ? &pIter->a[n] : 0);
if( n<=0 ){
*piCol = -1;
}else if( pIter->a[0]==0x01 ){
@@ -215271,13 +249950,56 @@ static int fts5ApiPhraseFirstColumn(
return rc;
}
+/*
+** xQueryToken() API implemenetation.
+*/
+static int fts5ApiQueryToken(
+ Fts5Context* pCtx,
+ int iPhrase,
+ int iToken,
+ const char **ppOut,
+ int *pnOut
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut);
+}
-static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
+/*
+** xInstToken() API implemenetation.
+*/
+static int fts5ApiInstToken(
+ Fts5Context *pCtx,
+ int iIdx,
+ int iToken,
+ const char **ppOut, int *pnOut
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ int rc = SQLITE_OK;
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
+ || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
+ ){
+ if( iIdx<0 || iIdx>=pCsr->nInstCount ){
+ rc = SQLITE_RANGE;
+ }else{
+ int iPhrase = pCsr->aInst[iIdx*3];
+ int iCol = pCsr->aInst[iIdx*3 + 1];
+ int iOff = pCsr->aInst[iIdx*3 + 2];
+ i64 iRowid = fts5CursorRowid(pCsr);
+ rc = sqlite3Fts5ExprInstToken(
+ pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut
+ );
+ }
+ }
+ return rc;
+}
+
+
+static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
int(*)(const Fts5ExtensionApi*, Fts5Context*, void*)
);
static const Fts5ExtensionApi sFts5Api = {
- 2, /* iVersion */
+ 3, /* iVersion */
fts5ApiUserData,
fts5ApiColumnCount,
fts5ApiRowCount,
@@ -215297,19 +250019,21 @@ static const Fts5ExtensionApi sFts5Api = {
fts5ApiPhraseNext,
fts5ApiPhraseFirstColumn,
fts5ApiPhraseNextColumn,
+ fts5ApiQueryToken,
+ fts5ApiInstToken
};
/*
** Implementation of API function xQueryPhrase().
*/
static int fts5ApiQueryPhrase(
- Fts5Context *pCtx,
- int iPhrase,
+ Fts5Context *pCtx,
+ int iPhrase,
void *pUserData,
int(*xCallback)(const Fts5ExtensionApi*, Fts5Context*, void*)
){
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
int rc;
Fts5Cursor *pNew = 0;
@@ -215375,7 +250099,7 @@ static void fts5ApiCallback(
iCsrId = sqlite3_value_int64(argv[0]);
pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId);
- if( pCsr==0 ){
+ if( pCsr==0 || pCsr->ePlan==0 ){
char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId);
sqlite3_result_error(context, zErr, -1);
sqlite3_free(zErr);
@@ -215386,25 +250110,19 @@ static void fts5ApiCallback(
/*
-** Given cursor id iId, return a pointer to the corresponding Fts5Index
+** Given cursor id iId, return a pointer to the corresponding Fts5Table
** object. Or NULL If the cursor id does not exist.
-**
-** If successful, set *ppConfig to point to the associated config object
-** before returning.
*/
-static Fts5Index *sqlite3Fts5IndexFromCsrid(
+static Fts5Table *sqlite3Fts5TableFromCsrid(
Fts5Global *pGlobal, /* FTS5 global context for db handle */
- i64 iCsrId, /* Id of cursor to find */
- Fts5Config **ppConfig /* OUT: Configuration object */
+ i64 iCsrId /* Id of cursor to find */
){
Fts5Cursor *pCsr;
- Fts5Table *pTab;
-
pCsr = fts5CursorFromCsrid(pGlobal, iCsrId);
- pTab = (Fts5Table*)pCsr->base.pVtab;
- *ppConfig = pTab->pConfig;
-
- return pTab->pIndex;
+ if( pCsr ){
+ return (Fts5Table*)pCsr->base.pVtab;
+ }
+ return 0;
}
/*
@@ -215475,7 +250193,7 @@ static int fts5PoslistBlob(sqlite3_context *pCtx, Fts5Cursor *pCsr){
return rc;
}
-/*
+/*
** This is the xColumn method, called by SQLite to request a value from
** the row that the supplied cursor currently points to.
*/
@@ -215484,11 +250202,11 @@ static int fts5ColumnMethod(
sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
int iCol /* Index of column to read value from */
){
- Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
- Fts5Config *pConfig = pTab->pConfig;
+ Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab);
+ Fts5Config *pConfig = pTab->p.pConfig;
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
int rc = SQLITE_OK;
-
+
assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 );
if( pCsr->ePlan==FTS5_PLAN_SPECIAL ){
@@ -215508,7 +250226,7 @@ static int fts5ColumnMethod(
/* The value of the "rank" column. */
if( pCsr->ePlan==FTS5_PLAN_SOURCE ){
fts5PoslistBlob(pCtx, pCsr);
- }else if(
+ }else if(
pCsr->ePlan==FTS5_PLAN_MATCH
|| pCsr->ePlan==FTS5_PLAN_SORTED_MATCH
){
@@ -215517,10 +250235,18 @@ static int fts5ColumnMethod(
}
}
}else if( !fts5IsContentless(pTab) ){
+ pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
rc = fts5SeekCursor(pCsr, 1);
if( rc==SQLITE_OK ){
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
}
+ pConfig->pzErrmsg = 0;
+ }else if( pConfig->bContentlessDelete && sqlite3_vtab_nochange(pCtx) ){
+ char *zErr = sqlite3_mprintf("cannot UPDATE a subset of "
+ "columns on fts5 contentless-delete table: %s", pConfig->zName
+ );
+ sqlite3_result_error(pCtx, zErr, -1);
+ sqlite3_free(zErr);
}
return rc;
}
@@ -215537,7 +250263,7 @@ static int fts5FindFunctionMethod(
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */
void **ppArg /* OUT: User data for *pxFunc */
){
- Fts5Table *pTab = (Fts5Table*)pVtab;
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
Fts5Auxiliary *pAux;
UNUSED_PARAM(nUnused);
@@ -215559,8 +250285,15 @@ static int fts5RenameMethod(
sqlite3_vtab *pVtab, /* Virtual table handle */
const char *zName /* New name of table */
){
- Fts5Table *pTab = (Fts5Table*)pVtab;
- return sqlite3Fts5StorageRename(pTab->pStorage, zName);
+ int rc;
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ rc = sqlite3Fts5StorageRename(pTab->pStorage, zName);
+ return rc;
+}
+
+static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
+ fts5TripCursors((Fts5FullTable*)pTab);
+ return sqlite3Fts5StorageSync(((Fts5FullTable*)pTab)->pStorage);
}
/*
@@ -215569,11 +250302,15 @@ static int fts5RenameMethod(
** Flush the contents of the pending-terms table to disk.
*/
static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
- Fts5Table *pTab = (Fts5Table*)pVtab;
- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ int rc = SQLITE_OK;
+
fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
- fts5TripCursors(pTab);
- return sqlite3Fts5StorageSync(pTab->pStorage);
+ rc = sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
+ if( rc==SQLITE_OK ){
+ pTab->iSavepoint = iSavepoint+1;
+ }
+ return rc;
}
/*
@@ -215582,11 +250319,16 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
** This is a no-op.
*/
static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
- Fts5Table *pTab = (Fts5Table*)pVtab;
- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ int rc = SQLITE_OK;
fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
- fts5TripCursors(pTab);
- return sqlite3Fts5StorageSync(pTab->pStorage);
+ if( (iSavepoint+1)<pTab->iSavepoint ){
+ rc = sqlite3Fts5FlushToDisk(&pTab->p);
+ if( rc==SQLITE_OK ){
+ pTab->iSavepoint = iSavepoint;
+ }
+ }
+ return rc;
}
/*
@@ -215595,11 +250337,15 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
** Discard the contents of the pending terms table.
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
- Fts5Table *pTab = (Fts5Table*)pVtab;
- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ int rc = SQLITE_OK;
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
fts5TripCursors(pTab);
- return sqlite3Fts5StorageRollback(pTab->pStorage);
+ if( (iSavepoint+1)<=pTab->iSavepoint ){
+ pTab->p.pConfig->pgsz = 0;
+ rc = sqlite3Fts5StorageRollback(pTab->pStorage);
+ }
+ return rc;
}
/*
@@ -215616,14 +250362,14 @@ static int fts5CreateAux(
int rc = sqlite3_overload_function(pGlobal->db, zName, -1);
if( rc==SQLITE_OK ){
Fts5Auxiliary *pAux;
- int nName; /* Size of zName in bytes, including \0 */
- int nByte; /* Bytes of space to allocate */
+ sqlite3_int64 nName; /* Size of zName in bytes, including \0 */
+ sqlite3_int64 nByte; /* Bytes of space to allocate */
- nName = (int)strlen(zName) + 1;
+ nName = strlen(zName) + 1;
nByte = sizeof(Fts5Auxiliary) + nName;
- pAux = (Fts5Auxiliary*)sqlite3_malloc(nByte);
+ pAux = (Fts5Auxiliary*)sqlite3_malloc64(nByte);
if( pAux ){
- memset(pAux, 0, nByte);
+ memset(pAux, 0, (size_t)nByte);
pAux->zFunc = (char*)&pAux[1];
memcpy(pAux->zFunc, zName, nName);
pAux->pGlobal = pGlobal;
@@ -215641,7 +250387,7 @@ static int fts5CreateAux(
}
/*
-** Register a new tokenizer. This is the implementation of the
+** Register a new tokenizer. This is the implementation of the
** fts5_api.xCreateTokenizer() method.
*/
static int fts5CreateTokenizer(
@@ -215653,15 +250399,15 @@ static int fts5CreateTokenizer(
){
Fts5Global *pGlobal = (Fts5Global*)pApi;
Fts5TokenizerModule *pNew;
- int nName; /* Size of zName and its \0 terminator */
- int nByte; /* Bytes of space to allocate */
+ sqlite3_int64 nName; /* Size of zName and its \0 terminator */
+ sqlite3_int64 nByte; /* Bytes of space to allocate */
int rc = SQLITE_OK;
- nName = (int)strlen(zName) + 1;
+ nName = strlen(zName) + 1;
nByte = sizeof(Fts5TokenizerModule) + nName;
- pNew = (Fts5TokenizerModule*)sqlite3_malloc(nByte);
+ pNew = (Fts5TokenizerModule*)sqlite3_malloc64(nByte);
if( pNew ){
- memset(pNew, 0, nByte);
+ memset(pNew, 0, (size_t)nByte);
pNew->zName = (char*)&pNew[1];
memcpy(pNew->zName, zName, nName);
pNew->pUserData = pUserData;
@@ -215680,7 +250426,7 @@ static int fts5CreateTokenizer(
}
static Fts5TokenizerModule *fts5LocateTokenizer(
- Fts5Global *pGlobal,
+ Fts5Global *pGlobal,
const char *zName
){
Fts5TokenizerModule *pMod = 0;
@@ -215697,7 +250443,7 @@ static Fts5TokenizerModule *fts5LocateTokenizer(
}
/*
-** Find a tokenizer. This is the implementation of the
+** Find a tokenizer. This is the implementation of the
** fts5_api.xFindTokenizer() method.
*/
static int fts5FindTokenizer(
@@ -215722,11 +250468,10 @@ static int fts5FindTokenizer(
}
static int sqlite3Fts5GetTokenizer(
- Fts5Global *pGlobal,
+ Fts5Global *pGlobal,
const char **azArg,
int nArg,
- Fts5Tokenizer **ppTok,
- fts5_tokenizer **ppTokApi,
+ Fts5Config *pConfig,
char **pzErr
){
Fts5TokenizerModule *pMod;
@@ -215738,16 +250483,22 @@ static int sqlite3Fts5GetTokenizer(
rc = SQLITE_ERROR;
*pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
}else{
- rc = pMod->x.xCreate(pMod->pUserData, &azArg[1], (nArg?nArg-1:0), ppTok);
- *ppTokApi = &pMod->x;
- if( rc!=SQLITE_OK && pzErr ){
- *pzErr = sqlite3_mprintf("error in tokenizer constructor");
+ rc = pMod->x.xCreate(
+ pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->pTok
+ );
+ pConfig->pTokApi = &pMod->x;
+ if( rc!=SQLITE_OK ){
+ if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor");
+ }else{
+ pConfig->ePattern = sqlite3Fts5TokenizerPattern(
+ pMod->x.xCreate, pConfig->pTok
+ );
}
}
if( rc!=SQLITE_OK ){
- *ppTokApi = 0;
- *ppTok = 0;
+ pConfig->pTokApi = 0;
+ pConfig->pTok = 0;
}
return rc;
@@ -215796,7 +250547,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a", -1, SQLITE_TRANSIENT);
}
/*
@@ -215814,9 +250565,40 @@ static int fts5ShadowName(const char *zName){
return 0;
}
+/*
+** Run an integrity check on the FTS5 data structures. Return a string
+** if anything is found amiss. Return a NULL pointer if everything is
+** OK.
+*/
+static int fts5IntegrityMethod(
+ sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */
+ const char *zSchema, /* Name of schema in which this table lives */
+ const char *zTabname, /* Name of the table itself */
+ int isQuick, /* True if this is a quick-check */
+ char **pzErr /* Write error message here */
+){
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ int rc;
+
+ assert( pzErr!=0 && *pzErr==0 );
+ UNUSED_PARAM(isQuick);
+ rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0);
+ if( (rc&0xff)==SQLITE_CORRUPT ){
+ *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
+ zSchema, zTabname);
+ }else if( rc!=SQLITE_OK ){
+ *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
+ " FTS5 table %s.%s: %s",
+ zSchema, zTabname, sqlite3_errstr(rc));
+ }
+ sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
+
+ return SQLITE_OK;
+}
+
static int fts5Init(sqlite3 *db){
static const sqlite3_module fts5Mod = {
- /* iVersion */ 3,
+ /* iVersion */ 4,
/* xCreate */ fts5CreateMethod,
/* xConnect */ fts5ConnectMethod,
/* xBestIndex */ fts5BestIndexMethod,
@@ -215839,7 +250621,8 @@ static int fts5Init(sqlite3 *db){
/* xSavepoint */ fts5SavepointMethod,
/* xRelease */ fts5ReleaseMethod,
/* xRollbackTo */ fts5RollbackToMethod,
- /* xShadowName */ fts5ShadowName
+ /* xShadowName */ fts5ShadowName,
+ /* xIntegrity */ fts5IntegrityMethod
};
int rc;
@@ -215869,7 +250652,9 @@ static int fts5Init(sqlite3 *db){
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
- db, "fts5_source_id", 0, SQLITE_UTF8, p, fts5SourceIdFunc, 0, 0
+ db, "fts5_source_id", 0,
+ SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
+ p, fts5SourceIdFunc, 0, 0
);
}
}
@@ -215892,7 +250677,7 @@ static int fts5Init(sqlite3 *db){
** this module is being built as part of the SQLite core (SQLITE_CORE is
** defined), then sqlite3_open() will call sqlite3Fts5Init() directly.
**
-** Or, if this module is being built as a loadable extension,
+** Or, if this module is being built as a loadable extension,
** sqlite3Fts5Init() is omitted and the two standard entry points
** sqlite3_fts_init() and sqlite3_fts5_init() defined instead.
*/
@@ -215951,19 +250736,19 @@ struct Fts5Storage {
Fts5Index *pIndex;
int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */
i64 nTotalRow; /* Total number of rows in FTS table */
- i64 *aTotalSize; /* Total sizes of each column */
+ i64 *aTotalSize; /* Total sizes of each column */
sqlite3_stmt *aStmt[11];
};
-#if FTS5_STMT_SCAN_ASC!=0
-# error "FTS5_STMT_SCAN_ASC mismatch"
+#if FTS5_STMT_SCAN_ASC!=0
+# error "FTS5_STMT_SCAN_ASC mismatch"
#endif
-#if FTS5_STMT_SCAN_DESC!=1
-# error "FTS5_STMT_SCAN_DESC mismatch"
+#if FTS5_STMT_SCAN_DESC!=1
+# error "FTS5_STMT_SCAN_DESC mismatch"
#endif
#if FTS5_STMT_LOOKUP!=2
-# error "FTS5_STMT_LOOKUP mismatch"
+# error "FTS5_STMT_LOOKUP mismatch"
#endif
#define FTS5_STMT_INSERT_CONTENT 3
@@ -215989,12 +250774,12 @@ static int fts5StorageGetStmt(
){
int rc = SQLITE_OK;
- /* If there is no %_docsize table, there should be no requests for
+ /* If there is no %_docsize table, there should be no requests for
** statements to operate on it. */
assert( p->pConfig->bColumnsize || (
- eStmt!=FTS5_STMT_REPLACE_DOCSIZE
- && eStmt!=FTS5_STMT_DELETE_DOCSIZE
- && eStmt!=FTS5_STMT_LOOKUP_DOCSIZE
+ eStmt!=FTS5_STMT_REPLACE_DOCSIZE
+ && eStmt!=FTS5_STMT_DELETE_DOCSIZE
+ && eStmt!=FTS5_STMT_LOOKUP_DOCSIZE
));
assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) );
@@ -216007,10 +250792,10 @@ static int fts5StorageGetStmt(
"INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */
"REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */
"DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */
- "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */
+ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?%s)", /* REPLACE_DOCSIZE */
"DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */
- "SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
+ "SELECT sz%s FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
"REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */
"SELECT %s FROM %s AS T", /* SCAN */
@@ -216020,32 +250805,32 @@ static int fts5StorageGetStmt(
switch( eStmt ){
case FTS5_STMT_SCAN:
- zSql = sqlite3_mprintf(azStmt[eStmt],
+ zSql = sqlite3_mprintf(azStmt[eStmt],
pC->zContentExprlist, pC->zContent
);
break;
case FTS5_STMT_SCAN_ASC:
case FTS5_STMT_SCAN_DESC:
- zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist,
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist,
pC->zContent, pC->zContentRowid, pC->zContentRowid,
pC->zContentRowid
);
break;
case FTS5_STMT_LOOKUP:
- zSql = sqlite3_mprintf(azStmt[eStmt],
+ zSql = sqlite3_mprintf(azStmt[eStmt],
pC->zContentExprlist, pC->zContent, pC->zContentRowid
);
break;
- case FTS5_STMT_INSERT_CONTENT:
+ case FTS5_STMT_INSERT_CONTENT:
case FTS5_STMT_REPLACE_CONTENT: {
int nCol = pC->nCol + 1;
char *zBind;
int i;
- zBind = sqlite3_malloc(1 + nCol*2);
+ zBind = sqlite3_malloc64(1 + nCol*2);
if( zBind ){
for(i=0; i<nCol; i++){
zBind[i*2] = '?';
@@ -216058,6 +250843,19 @@ static int fts5StorageGetStmt(
break;
}
+ case FTS5_STMT_REPLACE_DOCSIZE:
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName,
+ (pC->bContentlessDelete ? ",?" : "")
+ );
+ break;
+
+ case FTS5_STMT_LOOKUP_DOCSIZE:
+ zSql = sqlite3_mprintf(azStmt[eStmt],
+ (pC->bContentlessDelete ? ",origin" : ""),
+ pC->zDb, pC->zName
+ );
+ break;
+
default:
zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
break;
@@ -216066,8 +250864,11 @@ static int fts5StorageGetStmt(
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
- rc = sqlite3_prepare_v3(pC->db, zSql, -1,
- SQLITE_PREPARE_PERSISTENT, &p->aStmt[eStmt], 0);
+ int f = SQLITE_PREPARE_PERSISTENT;
+ if( eStmt>FTS5_STMT_LOOKUP ) f |= SQLITE_PREPARE_NO_VTAB;
+ p->pConfig->bLock++;
+ rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0);
+ p->pConfig->bLock--;
sqlite3_free(zSql);
if( rc!=SQLITE_OK && pzErrMsg ){
*pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));
@@ -216110,7 +250911,7 @@ static int fts5ExecPrintf(
** code otherwise.
*/
static int sqlite3Fts5DropAll(Fts5Config *pConfig){
- int rc = fts5ExecPrintf(pConfig->db, 0,
+ int rc = fts5ExecPrintf(pConfig->db, 0,
"DROP TABLE IF EXISTS %Q.'%q_data';"
"DROP TABLE IF EXISTS %Q.'%q_idx';"
"DROP TABLE IF EXISTS %Q.'%q_config';",
@@ -216119,13 +250920,13 @@ static int sqlite3Fts5DropAll(Fts5Config *pConfig){
pConfig->zDb, pConfig->zName
);
if( rc==SQLITE_OK && pConfig->bColumnsize ){
- rc = fts5ExecPrintf(pConfig->db, 0,
+ rc = fts5ExecPrintf(pConfig->db, 0,
"DROP TABLE IF EXISTS %Q.'%q_docsize';",
pConfig->zDb, pConfig->zName
);
}
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
- rc = fts5ExecPrintf(pConfig->db, 0,
+ rc = fts5ExecPrintf(pConfig->db, 0,
"DROP TABLE IF EXISTS %Q.'%q_content';",
pConfig->zDb, pConfig->zName
);
@@ -216140,7 +250941,7 @@ static void fts5StorageRenameOne(
const char *zName /* New name of FTS5 table */
){
if( *pRc==SQLITE_OK ){
- *pRc = fts5ExecPrintf(pConfig->db, 0,
+ *pRc = fts5ExecPrintf(pConfig->db, 0,
"ALTER TABLE %Q.'%q_%s' RENAME TO '%q_%s';",
pConfig->zDb, pConfig->zName, zTail, zName, zTail
);
@@ -216178,7 +250979,7 @@ static int sqlite3Fts5CreateTable(
char *zErr = 0;
rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s",
- pConfig->zDb, pConfig->zName, zPost, zDefn,
+ pConfig->zDb, pConfig->zName, zPost, zDefn,
#ifndef SQLITE_FTS5_NO_WITHOUT_ROWID
bWithout?" WITHOUT ROWID":
#endif
@@ -216186,7 +250987,7 @@ static int sqlite3Fts5CreateTable(
);
if( zErr ){
*pzErr = sqlite3_mprintf(
- "fts5: error creating shadow table %q_%s: %s",
+ "fts5: error creating shadow table %q_%s: %s",
pConfig->zName, zPost, zErr
);
sqlite3_free(zErr);
@@ -216197,28 +250998,28 @@ static int sqlite3Fts5CreateTable(
/*
** Open a new Fts5Index handle. If the bCreate argument is true, create
-** and initialize the underlying tables
+** and initialize the underlying tables
**
** If successful, set *pp to point to the new object and return SQLITE_OK.
** Otherwise, set *pp to NULL and return an SQLite error code.
*/
static int sqlite3Fts5StorageOpen(
- Fts5Config *pConfig,
- Fts5Index *pIndex,
- int bCreate,
+ Fts5Config *pConfig,
+ Fts5Index *pIndex,
+ int bCreate,
Fts5Storage **pp,
char **pzErr /* OUT: Error message */
){
int rc = SQLITE_OK;
Fts5Storage *p; /* New object */
- int nByte; /* Bytes of space to allocate */
+ sqlite3_int64 nByte; /* Bytes of space to allocate */
nByte = sizeof(Fts5Storage) /* Fts5Storage object */
+ pConfig->nCol * sizeof(i64); /* Fts5Storage.aTotalSize[] */
- *pp = p = (Fts5Storage*)sqlite3_malloc(nByte);
+ *pp = p = (Fts5Storage*)sqlite3_malloc64(nByte);
if( !p ) return SQLITE_NOMEM;
- memset(p, 0, nByte);
+ memset(p, 0, (size_t)nByte);
p->aTotalSize = (i64*)&p[1];
p->pConfig = pConfig;
p->pIndex = pIndex;
@@ -216226,7 +251027,7 @@ static int sqlite3Fts5StorageOpen(
if( bCreate ){
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
int nDefn = 32 + pConfig->nCol*10;
- char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10);
+ char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10);
if( zDefn==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -216244,9 +251045,11 @@ static int sqlite3Fts5StorageOpen(
}
if( rc==SQLITE_OK && pConfig->bColumnsize ){
- rc = sqlite3Fts5CreateTable(
- pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
- );
+ const char *zCols = "id INTEGER PRIMARY KEY, sz BLOB";
+ if( pConfig->bContentlessDelete ){
+ zCols = "id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER";
+ }
+ rc = sqlite3Fts5CreateTable(pConfig, "docsize", zCols, 0, pzErr);
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5CreateTable(
@@ -216317,13 +251120,13 @@ static int fts5StorageInsertCallback(
** remove the %_content row at this time though.
*/
static int fts5StorageDeleteFromIndex(
- Fts5Storage *p,
- i64 iDel,
+ Fts5Storage *p,
+ i64 iDel,
sqlite3_value **apVal
){
Fts5Config *pConfig = p->pConfig;
sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */
- int rc; /* Return code */
+ int rc = SQLITE_OK; /* Return code */
int rc2; /* sqlite3_reset() return code */
int iCol;
Fts5InsertCtx ctx;
@@ -216339,32 +251142,73 @@ static int fts5StorageDeleteFromIndex(
ctx.pStorage = p;
ctx.iCol = -1;
- rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
if( pConfig->abUnindexed[iCol-1]==0 ){
const char *zText;
int nText;
+ assert( pSeek==0 || apVal==0 );
+ assert( pSeek!=0 || apVal!=0 );
if( pSeek ){
zText = (const char*)sqlite3_column_text(pSeek, iCol);
nText = sqlite3_column_bytes(pSeek, iCol);
- }else{
+ }else if( ALWAYS(apVal) ){
zText = (const char*)sqlite3_value_text(apVal[iCol-1]);
nText = sqlite3_value_bytes(apVal[iCol-1]);
+ }else{
+ continue;
}
ctx.szCol = 0;
- rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT,
+ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT,
zText, nText, (void*)&ctx, fts5StorageInsertCallback
);
p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
+ if( p->aTotalSize[iCol-1]<0 ){
+ rc = FTS5_CORRUPT;
+ }
}
}
- p->nTotalRow--;
+ if( rc==SQLITE_OK && p->nTotalRow<1 ){
+ rc = FTS5_CORRUPT;
+ }else{
+ p->nTotalRow--;
+ }
rc2 = sqlite3_reset(pSeek);
if( rc==SQLITE_OK ) rc = rc2;
return rc;
}
+/*
+** This function is called to process a DELETE on a contentless_delete=1
+** table. It adds the tombstone required to delete the entry with rowid
+** iDel. If successful, SQLITE_OK is returned. Or, if an error occurs,
+** an SQLite error code.
+*/
+static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){
+ i64 iOrigin = 0;
+ sqlite3_stmt *pLookup = 0;
+ int rc = SQLITE_OK;
+
+ assert( p->pConfig->bContentlessDelete );
+ assert( p->pConfig->eContent==FTS5_CONTENT_NONE );
+
+ /* Look up the origin of the document in the %_docsize table. Store
+ ** this in stack variable iOrigin. */
+ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pLookup, 1, iDel);
+ if( SQLITE_ROW==sqlite3_step(pLookup) ){
+ iOrigin = sqlite3_column_int64(pLookup, 1);
+ }
+ rc = sqlite3_reset(pLookup);
+ }
+
+ if( rc==SQLITE_OK && iOrigin!=0 ){
+ rc = sqlite3Fts5IndexContentlessDelete(p->pIndex, iOrigin, iDel);
+ }
+
+ return rc;
+}
/*
** Insert a record into the %_docsize table. Specifically, do:
@@ -216385,17 +251229,24 @@ static int fts5StorageInsertDocsize(
rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pReplace, 1, iRowid);
- sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
- sqlite3_step(pReplace);
- rc = sqlite3_reset(pReplace);
- sqlite3_bind_null(pReplace, 2);
+ if( p->pConfig->bContentlessDelete ){
+ i64 iOrigin = 0;
+ rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin);
+ sqlite3_bind_int64(pReplace, 3, iOrigin);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
+ sqlite3_step(pReplace);
+ rc = sqlite3_reset(pReplace);
+ sqlite3_bind_null(pReplace, 2);
+ }
}
}
return rc;
}
/*
-** Load the contents of the "averages" record from disk into the
+** Load the contents of the "averages" record from disk into the
** p->nTotalRow and p->aTotalSize[] variables. If successful, and if
** argument bCache is true, set the p->bTotalsValid flag to indicate
** that the contents of aTotalSize[] and nTotalRow are valid until
@@ -216414,7 +251265,7 @@ static int fts5StorageLoadTotals(Fts5Storage *p, int bCache){
}
/*
-** Store the current contents of the p->nTotalRow and p->aTotalSize[]
+** Store the current contents of the p->nTotalRow and p->aTotalSize[]
** variables in the "averages" record on disk.
**
** Return SQLITE_OK if successful, or an SQLite error code if an error
@@ -216452,7 +251303,15 @@ static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **ap
/* Delete the index records */
if( rc==SQLITE_OK ){
- rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
+ }
+
+ if( rc==SQLITE_OK ){
+ if( p->pConfig->bContentlessDelete ){
+ rc = fts5StorageContentlessDelete(p, iDel);
+ }else{
+ rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
+ }
}
/* Delete the %_docsize record */
@@ -216487,9 +251346,11 @@ static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){
Fts5Config *pConfig = p->pConfig;
int rc;
+ p->bTotalsValid = 0;
+
/* Delete the contents of the %_data and %_docsize tables. */
rc = fts5ExecPrintf(pConfig->db, 0,
- "DELETE FROM %Q.'%q_data';"
+ "DELETE FROM %Q.'%q_data';"
"DELETE FROM %Q.'%q_idx';",
pConfig->zDb, pConfig->zName,
pConfig->zDb, pConfig->zName
@@ -216517,7 +251378,7 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){
Fts5Config *pConfig = p->pConfig;
sqlite3_stmt *pScan = 0;
Fts5InsertCtx ctx;
- int rc;
+ int rc, rc2;
memset(&ctx, 0, sizeof(Fts5InsertCtx));
ctx.pStorage = p;
@@ -216527,7 +251388,7 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){
}
if( rc==SQLITE_OK ){
- rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, pConfig->pzErrmsg);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
@@ -216538,10 +251399,11 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
ctx.szCol = 0;
if( pConfig->abUnindexed[ctx.iCol]==0 ){
- rc = sqlite3Fts5Tokenize(pConfig,
+ const char *zText = (const char*)sqlite3_column_text(pScan, ctx.iCol+1);
+ int nText = sqlite3_column_bytes(pScan, ctx.iCol+1);
+ rc = sqlite3Fts5Tokenize(pConfig,
FTS5_TOKENIZE_DOCUMENT,
- (const char*)sqlite3_column_text(pScan, ctx.iCol+1),
- sqlite3_column_bytes(pScan, ctx.iCol+1),
+ zText, nText,
(void*)&ctx,
fts5StorageInsertCallback
);
@@ -216556,6 +251418,8 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){
}
}
sqlite3_free(buf.p);
+ rc2 = sqlite3_reset(pScan);
+ if( rc==SQLITE_OK ) rc = rc2;
/* Write the averages record */
if( rc==SQLITE_OK ){
@@ -216607,8 +251471,8 @@ static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){
** Insert a new row into the FTS content table.
*/
static int sqlite3Fts5StorageContentInsert(
- Fts5Storage *p,
- sqlite3_value **apVal,
+ Fts5Storage *p,
+ sqlite3_value **apVal,
i64 *piRowid
){
Fts5Config *pConfig = p->pConfig;
@@ -216642,8 +251506,8 @@ static int sqlite3Fts5StorageContentInsert(
** Insert new entries into the FTS index and %_docsize table.
*/
static int sqlite3Fts5StorageIndexInsert(
- Fts5Storage *p,
- sqlite3_value **apVal,
+ Fts5Storage *p,
+ sqlite3_value **apVal,
i64 iRowid
){
Fts5Config *pConfig = p->pConfig;
@@ -216661,10 +251525,11 @@ static int sqlite3Fts5StorageIndexInsert(
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
ctx.szCol = 0;
if( pConfig->abUnindexed[ctx.iCol]==0 ){
- rc = sqlite3Fts5Tokenize(pConfig,
+ const char *zText = (const char*)sqlite3_value_text(apVal[ctx.iCol+2]);
+ int nText = sqlite3_value_bytes(apVal[ctx.iCol+2]);
+ rc = sqlite3Fts5Tokenize(pConfig,
FTS5_TOKENIZE_DOCUMENT,
- (const char*)sqlite3_value_text(apVal[ctx.iCol+2]),
- sqlite3_value_bytes(apVal[ctx.iCol+2]),
+ zText, nText,
(void*)&ctx,
fts5StorageInsertCallback
);
@@ -216688,7 +251553,7 @@ static int fts5StorageCount(Fts5Storage *p, const char *zSuffix, i64 *pnRow){
char *zSql;
int rc;
- zSql = sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'",
+ zSql = sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'",
pConfig->zDb, pConfig->zName, zSuffix
);
if( zSql==0 ){
@@ -216795,97 +251660,104 @@ static int fts5StorageIntegrityCallback(
** some other SQLite error code if an error occurs while attempting to
** determine this.
*/
-static int sqlite3Fts5StorageIntegrity(Fts5Storage *p){
+static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg){
Fts5Config *pConfig = p->pConfig;
- int rc; /* Return code */
+ int rc = SQLITE_OK; /* Return code */
int *aColSize; /* Array of size pConfig->nCol */
i64 *aTotalSize; /* Array of size pConfig->nCol */
Fts5IntegrityCtx ctx;
sqlite3_stmt *pScan;
+ int bUseCksum;
memset(&ctx, 0, sizeof(Fts5IntegrityCtx));
ctx.pConfig = p->pConfig;
- aTotalSize = (i64*)sqlite3_malloc(pConfig->nCol * (sizeof(int)+sizeof(i64)));
+ aTotalSize = (i64*)sqlite3_malloc64(pConfig->nCol*(sizeof(int)+sizeof(i64)));
if( !aTotalSize ) return SQLITE_NOMEM;
aColSize = (int*)&aTotalSize[pConfig->nCol];
memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol);
- /* Generate the expected index checksum based on the contents of the
- ** %_content table. This block stores the checksum in ctx.cksum. */
- rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
- if( rc==SQLITE_OK ){
- int rc2;
- while( SQLITE_ROW==sqlite3_step(pScan) ){
- int i;
- ctx.iRowid = sqlite3_column_int64(pScan, 0);
- ctx.szCol = 0;
- if( pConfig->bColumnsize ){
- rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
- }
- if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
- rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
- }
- for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
- if( pConfig->abUnindexed[i] ) continue;
- ctx.iCol = i;
+ bUseCksum = (pConfig->eContent==FTS5_CONTENT_NORMAL
+ || (pConfig->eContent==FTS5_CONTENT_EXTERNAL && iArg)
+ );
+ if( bUseCksum ){
+ /* Generate the expected index checksum based on the contents of the
+ ** %_content table. This block stores the checksum in ctx.cksum. */
+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
+ if( rc==SQLITE_OK ){
+ int rc2;
+ while( SQLITE_ROW==sqlite3_step(pScan) ){
+ int i;
+ ctx.iRowid = sqlite3_column_int64(pScan, 0);
ctx.szCol = 0;
- if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
- rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5Tokenize(pConfig,
- FTS5_TOKENIZE_DOCUMENT,
- (const char*)sqlite3_column_text(pScan, i+1),
- sqlite3_column_bytes(pScan, i+1),
- (void*)&ctx,
- fts5StorageIntegrityCallback
- );
+ if( pConfig->bColumnsize ){
+ rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
}
- if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
- rc = FTS5_CORRUPT;
+ if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
+ rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
}
- aTotalSize[i] += ctx.szCol;
- if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
- sqlite3Fts5TermsetFree(ctx.pTermset);
- ctx.pTermset = 0;
+ for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
+ if( pConfig->abUnindexed[i] ) continue;
+ ctx.iCol = i;
+ ctx.szCol = 0;
+ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
+ rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
+ }
+ if( rc==SQLITE_OK ){
+ const char *zText = (const char*)sqlite3_column_text(pScan, i+1);
+ int nText = sqlite3_column_bytes(pScan, i+1);
+ rc = sqlite3Fts5Tokenize(pConfig,
+ FTS5_TOKENIZE_DOCUMENT,
+ zText, nText,
+ (void*)&ctx,
+ fts5StorageIntegrityCallback
+ );
+ }
+ if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
+ rc = FTS5_CORRUPT;
+ }
+ aTotalSize[i] += ctx.szCol;
+ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
+ sqlite3Fts5TermsetFree(ctx.pTermset);
+ ctx.pTermset = 0;
+ }
}
- }
- sqlite3Fts5TermsetFree(ctx.pTermset);
- ctx.pTermset = 0;
+ sqlite3Fts5TermsetFree(ctx.pTermset);
+ ctx.pTermset = 0;
- if( rc!=SQLITE_OK ) break;
+ if( rc!=SQLITE_OK ) break;
+ }
+ rc2 = sqlite3_reset(pScan);
+ if( rc==SQLITE_OK ) rc = rc2;
}
- rc2 = sqlite3_reset(pScan);
- if( rc==SQLITE_OK ) rc = rc2;
- }
- /* Test that the "totals" (sometimes called "averages") record looks Ok */
- if( rc==SQLITE_OK ){
- int i;
- rc = fts5StorageLoadTotals(p, 0);
- for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
- if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT;
+ /* Test that the "totals" (sometimes called "averages") record looks Ok */
+ if( rc==SQLITE_OK ){
+ int i;
+ rc = fts5StorageLoadTotals(p, 0);
+ for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
+ if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT;
+ }
}
- }
- /* Check that the %_docsize and %_content tables contain the expected
- ** number of rows. */
- if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
- i64 nRow = 0;
- rc = fts5StorageCount(p, "content", &nRow);
- if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
- }
- if( rc==SQLITE_OK && pConfig->bColumnsize ){
- i64 nRow = 0;
- rc = fts5StorageCount(p, "docsize", &nRow);
- if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
+ /* Check that the %_docsize and %_content tables contain the expected
+ ** number of rows. */
+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ i64 nRow = 0;
+ rc = fts5StorageCount(p, "content", &nRow);
+ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
+ }
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ i64 nRow = 0;
+ rc = fts5StorageCount(p, "docsize", &nRow);
+ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
+ }
}
/* Pass the expected checksum down to the FTS index module. It will
** verify, amongst other things, that it matches the checksum generated by
** inspecting the index itself. */
if( rc==SQLITE_OK ){
- rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum);
+ rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum, bUseCksum);
}
sqlite3_free(aTotalSize);
@@ -216897,13 +251769,13 @@ static int sqlite3Fts5StorageIntegrity(Fts5Storage *p){
** %_content table.
*/
static int sqlite3Fts5StorageStmt(
- Fts5Storage *p,
- int eStmt,
- sqlite3_stmt **pp,
+ Fts5Storage *p,
+ int eStmt,
+ sqlite3_stmt **pp,
char **pzErrMsg
){
int rc;
- assert( eStmt==FTS5_STMT_SCAN_ASC
+ assert( eStmt==FTS5_STMT_SCAN_ASC
|| eStmt==FTS5_STMT_SCAN_DESC
|| eStmt==FTS5_STMT_LOOKUP
);
@@ -216921,8 +251793,8 @@ static int sqlite3Fts5StorageStmt(
** must match that passed to the sqlite3Fts5StorageStmt() call.
*/
static void sqlite3Fts5StorageStmtRelease(
- Fts5Storage *p,
- int eStmt,
+ Fts5Storage *p,
+ int eStmt,
sqlite3_stmt *pStmt
){
assert( eStmt==FTS5_STMT_SCAN_ASC
@@ -216965,8 +251837,9 @@ static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){
assert( p->pConfig->bColumnsize );
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
- if( rc==SQLITE_OK ){
+ if( pLookup ){
int bCorrupt = 1;
+ assert( rc==SQLITE_OK );
sqlite3_bind_int64(pLookup, 1, iRowid);
if( SQLITE_ROW==sqlite3_step(pLookup) ){
const u8 *aBlob = sqlite3_column_blob(pLookup, 0);
@@ -216979,6 +251852,8 @@ static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){
if( bCorrupt && rc==SQLITE_OK ){
rc = FTS5_CORRUPT;
}
+ }else{
+ assert( rc!=SQLITE_OK );
}
return rc;
@@ -217005,7 +251880,13 @@ static int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnToken){
static int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow){
int rc = fts5StorageLoadTotals(p, 0);
if( rc==SQLITE_OK ){
+ /* nTotalRow being zero does not necessarily indicate a corrupt
+ ** database - it might be that the FTS5 table really does contain zero
+ ** rows. However this function is only called from the xRowCount() API,
+ ** and there is no way for that API to be invoked if the table contains
+ ** no rows. Hence the FTS5_CORRUPT return. */
*pnRow = p->nTotalRow;
+ if( p->nTotalRow<=0 ) rc = FTS5_CORRUPT;
}
return rc;
}
@@ -217018,7 +251899,9 @@ static int sqlite3Fts5StorageSync(Fts5Storage *p){
i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db);
if( p->bTotalsValid ){
rc = fts5StorageSaveTotals(p);
- p->bTotalsValid = 0;
+ if( rc==SQLITE_OK ){
+ p->bTotalsValid = 0;
+ }
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexSync(p->pIndex);
@@ -217033,7 +251916,7 @@ static int sqlite3Fts5StorageRollback(Fts5Storage *p){
}
static int sqlite3Fts5StorageConfigValue(
- Fts5Storage *p,
+ Fts5Storage *p,
const char *z,
sqlite3_value *pVal,
int iVal
@@ -217083,7 +251966,7 @@ static int sqlite3Fts5StorageConfigValue(
/*
** For tokenizers with no "unicode" modifier, the set of token characters
-** is the same as the set of ASCII range alphanumeric characters.
+** is the same as the set of ASCII range alphanumeric characters.
*/
static unsigned char aAsciiTokenChar[128] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00..0x0F */
@@ -217102,8 +251985,8 @@ struct AsciiTokenizer {
};
static void fts5AsciiAddExceptions(
- AsciiTokenizer *p,
- const char *zArg,
+ AsciiTokenizer *p,
+ const char *zArg,
int bTokenChars
){
int i;
@@ -217125,7 +252008,7 @@ static void fts5AsciiDelete(Fts5Tokenizer *p){
** Create an "ascii" tokenizer.
*/
static int fts5AsciiCreate(
- void *pUnused,
+ void *pUnused,
const char **azArg, int nArg,
Fts5Tokenizer **ppOut
){
@@ -217215,7 +252098,7 @@ static int fts5AsciiTokenize(
nByte = ie-is;
if( nByte>nFold ){
if( pFold!=aFold ) sqlite3_free(pFold);
- pFold = sqlite3_malloc(nByte*2);
+ pFold = sqlite3_malloc64((sqlite3_int64)nByte*2);
if( pFold==0 ){
rc = SQLITE_NOMEM;
break;
@@ -217228,7 +252111,7 @@ static int fts5AsciiTokenize(
rc = xToken(pCtx, 0, pFold, nByte, is, ie);
is = ie+1;
}
-
+
if( pFold!=aFold ) sqlite3_free(pFold);
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
return rc;
@@ -217292,18 +252175,29 @@ static const unsigned char sqlite3Utf8Trans1[] = {
#endif /* ifndef SQLITE_AMALGAMATION */
+#define FTS5_SKIP_UTF8(zIn) { \
+ if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \
+ while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \
+ } \
+}
+
typedef struct Unicode61Tokenizer Unicode61Tokenizer;
struct Unicode61Tokenizer {
unsigned char aTokenChar[128]; /* ASCII range token characters */
char *aFold; /* Buffer to fold text into */
int nFold; /* Size of aFold[] in bytes */
- int bRemoveDiacritic; /* True if remove_diacritics=1 is set */
+ int eRemoveDiacritic; /* True if remove_diacritics=1 is set */
int nException;
int *aiException;
unsigned char aCategory[32]; /* True for token char categories */
};
+/* Values for eRemoveDiacritic (must match internals of fts5_unicode2.c) */
+#define FTS5_REMOVE_DIACRITICS_NONE 0
+#define FTS5_REMOVE_DIACRITICS_SIMPLE 1
+#define FTS5_REMOVE_DIACRITICS_COMPLEX 2
+
static int fts5UnicodeAddExceptions(
Unicode61Tokenizer *p, /* Tokenizer object */
const char *z, /* Characters to treat as exceptions */
@@ -217314,25 +252208,26 @@ static int fts5UnicodeAddExceptions(
int *aNew;
if( n>0 ){
- aNew = (int*)sqlite3_realloc(p->aiException, (n+p->nException)*sizeof(int));
+ aNew = (int*)sqlite3_realloc64(p->aiException,
+ (n+p->nException)*sizeof(int));
if( aNew ){
int nNew = p->nException;
const unsigned char *zCsr = (const unsigned char*)z;
const unsigned char *zTerm = (const unsigned char*)&z[n];
while( zCsr<zTerm ){
- int iCode;
+ u32 iCode;
int bToken;
READ_UTF8(zCsr, zTerm, iCode);
if( iCode<128 ){
p->aTokenChar[iCode] = (unsigned char)bTokenChars;
}else{
bToken = p->aCategory[sqlite3Fts5UnicodeCategory(iCode)];
- assert( (bToken==0 || bToken==1) );
+ assert( (bToken==0 || bToken==1) );
assert( (bTokenChars==0 || bTokenChars==1) );
if( bToken!=bTokenChars && sqlite3Fts5UnicodeIsdiacritic(iCode)==0 ){
int i;
for(i=0; i<nNew; i++){
- if( aNew[i]>iCode ) break;
+ if( (u32)aNew[i]>iCode ) break;
}
memmove(&aNew[i+1], &aNew[i], (nNew-i)*sizeof(int));
aNew[i] = iCode;
@@ -217406,12 +252301,12 @@ static int unicodeSetCategories(Unicode61Tokenizer *p, const char *zCat){
** Create a "unicode61" tokenizer.
*/
static int fts5UnicodeCreate(
- void *pUnused,
+ void *pUnused,
const char **azArg, int nArg,
Fts5Tokenizer **ppOut
){
int rc = SQLITE_OK; /* Return code */
- Unicode61Tokenizer *p = 0; /* New tokenizer object */
+ Unicode61Tokenizer *p = 0; /* New tokenizer object */
UNUSED_PARAM(pUnused);
@@ -217424,9 +252319,9 @@ static int fts5UnicodeCreate(
int i;
memset(p, 0, sizeof(Unicode61Tokenizer));
- p->bRemoveDiacritic = 1;
+ p->eRemoveDiacritic = FTS5_REMOVE_DIACRITICS_SIMPLE;
p->nFold = 64;
- p->aFold = sqlite3_malloc(p->nFold * sizeof(char));
+ p->aFold = sqlite3_malloc64(p->nFold * sizeof(char));
if( p->aFold==0 ){
rc = SQLITE_NOMEM;
}
@@ -217445,10 +252340,15 @@ static int fts5UnicodeCreate(
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
const char *zArg = azArg[i+1];
if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
- if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
+ if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
rc = SQLITE_ERROR;
+ }else{
+ p->eRemoveDiacritic = (zArg[0] - '0');
+ assert( p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_NONE
+ || p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_SIMPLE
+ || p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_COMPLEX
+ );
}
- p->bRemoveDiacritic = (zArg[0]=='1');
}else
if( 0==sqlite3_stricmp(azArg[i], "tokenchars") ){
rc = fts5UnicodeAddExceptions(p, zArg, 1);
@@ -217477,12 +252377,12 @@ static int fts5UnicodeCreate(
/*
** Return true if, for the purposes of tokenizing with the tokenizer
-** passed as the first argument, codepoint iCode is considered a token
+** passed as the first argument, codepoint iCode is considered a token
** character (not a separator).
*/
static int fts5UnicodeIsAlnum(Unicode61Tokenizer *p, int iCode){
return (
- p->aCategory[sqlite3Fts5UnicodeCategory(iCode)]
+ p->aCategory[sqlite3Fts5UnicodeCategory((u32)iCode)]
^ fts5UnicodeIsException(p, iCode)
);
}
@@ -217511,7 +252411,7 @@ static int fts5UnicodeTokenize(
/* Each iteration of this loop gobbles up a contiguous run of separators,
** then the next token. */
while( rc==SQLITE_OK ){
- int iCode; /* non-ASCII codepoint read from input */
+ u32 iCode; /* non-ASCII codepoint read from input */
char *zOut = aFold;
int is;
int ie;
@@ -217543,7 +252443,7 @@ static int fts5UnicodeTokenize(
/* Grow the output buffer so that there is sufficient space to fit the
** largest possible utf-8 character. */
if( zOut>pEnd ){
- aFold = sqlite3_malloc(nFold*2);
+ aFold = sqlite3_malloc64((sqlite3_int64)nFold*2);
if( aFold==0 ){
rc = SQLITE_NOMEM;
goto tokenize_done;
@@ -217562,14 +252462,14 @@ static int fts5UnicodeTokenize(
READ_UTF8(zCsr, zTerm, iCode);
if( fts5UnicodeIsAlnum(p,iCode)||sqlite3Fts5UnicodeIsdiacritic(iCode) ){
non_ascii_tokenchar:
- iCode = sqlite3Fts5UnicodeFold(iCode, p->bRemoveDiacritic);
+ iCode = sqlite3Fts5UnicodeFold(iCode, p->eRemoveDiacritic);
if( iCode ) WRITE_UTF8(zOut, iCode);
}else{
break;
}
}else if( a[*zCsr]==0 ){
/* An ascii-range separator character. End of token. */
- break;
+ break;
}else{
ascii_tokenchar:
if( *zCsr>='A' && *zCsr<='Z' ){
@@ -217583,9 +252483,9 @@ static int fts5UnicodeTokenize(
}
/* Invoke the token callback */
- rc = xToken(pCtx, 0, aFold, zOut-aFold, is, ie);
+ rc = xToken(pCtx, 0, aFold, zOut-aFold, is, ie);
}
-
+
tokenize_done:
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
return rc;
@@ -217623,7 +252523,7 @@ static void fts5PorterDelete(Fts5Tokenizer *pTok){
** Create a "porter" tokenizer.
*/
static int fts5PorterCreate(
- void *pCtx,
+ void *pCtx,
const char **azArg, int nArg,
Fts5Tokenizer **ppOut
){
@@ -217767,7 +252667,7 @@ static int fts5Porter_Ostar(char *zStem, int nStem){
/* porter rule condition: (m > 1 and (*S or *T)) */
static int fts5Porter_MGt1_and_S_or_T(char *zStem, int nStem){
assert( nStem>0 );
- return (zStem[nStem-1]=='s' || zStem[nStem-1]=='t')
+ return (zStem[nStem-1]=='s' || zStem[nStem-1]=='t')
&& fts5Porter_MGt1(zStem, nStem);
}
@@ -217792,16 +252692,16 @@ static int fts5PorterStep4(char *aBuf, int *pnBuf){
int ret = 0;
int nBuf = *pnBuf;
switch( aBuf[nBuf-2] ){
-
- case 'a':
+
+ case 'a':
if( nBuf>2 && 0==memcmp("al", &aBuf[nBuf-2], 2) ){
if( fts5Porter_MGt1(aBuf, nBuf-2) ){
*pnBuf = nBuf - 2;
}
}
break;
-
- case 'c':
+
+ case 'c':
if( nBuf>4 && 0==memcmp("ance", &aBuf[nBuf-4], 4) ){
if( fts5Porter_MGt1(aBuf, nBuf-4) ){
*pnBuf = nBuf - 4;
@@ -217812,24 +252712,24 @@ static int fts5PorterStep4(char *aBuf, int *pnBuf){
}
}
break;
-
- case 'e':
+
+ case 'e':
if( nBuf>2 && 0==memcmp("er", &aBuf[nBuf-2], 2) ){
if( fts5Porter_MGt1(aBuf, nBuf-2) ){
*pnBuf = nBuf - 2;
}
}
break;
-
- case 'i':
+
+ case 'i':
if( nBuf>2 && 0==memcmp("ic", &aBuf[nBuf-2], 2) ){
if( fts5Porter_MGt1(aBuf, nBuf-2) ){
*pnBuf = nBuf - 2;
}
}
break;
-
- case 'l':
+
+ case 'l':
if( nBuf>4 && 0==memcmp("able", &aBuf[nBuf-4], 4) ){
if( fts5Porter_MGt1(aBuf, nBuf-4) ){
*pnBuf = nBuf - 4;
@@ -217840,8 +252740,8 @@ static int fts5PorterStep4(char *aBuf, int *pnBuf){
}
}
break;
-
- case 'n':
+
+ case 'n':
if( nBuf>3 && 0==memcmp("ant", &aBuf[nBuf-3], 3) ){
if( fts5Porter_MGt1(aBuf, nBuf-3) ){
*pnBuf = nBuf - 3;
@@ -217860,8 +252760,8 @@ static int fts5PorterStep4(char *aBuf, int *pnBuf){
}
}
break;
-
- case 'o':
+
+ case 'o':
if( nBuf>3 && 0==memcmp("ion", &aBuf[nBuf-3], 3) ){
if( fts5Porter_MGt1_and_S_or_T(aBuf, nBuf-3) ){
*pnBuf = nBuf - 3;
@@ -217872,16 +252772,16 @@ static int fts5PorterStep4(char *aBuf, int *pnBuf){
}
}
break;
-
- case 's':
+
+ case 's':
if( nBuf>3 && 0==memcmp("ism", &aBuf[nBuf-3], 3) ){
if( fts5Porter_MGt1(aBuf, nBuf-3) ){
*pnBuf = nBuf - 3;
}
}
break;
-
- case 't':
+
+ case 't':
if( nBuf>3 && 0==memcmp("ate", &aBuf[nBuf-3], 3) ){
if( fts5Porter_MGt1(aBuf, nBuf-3) ){
*pnBuf = nBuf - 3;
@@ -217892,76 +252792,76 @@ static int fts5PorterStep4(char *aBuf, int *pnBuf){
}
}
break;
-
- case 'u':
+
+ case 'u':
if( nBuf>3 && 0==memcmp("ous", &aBuf[nBuf-3], 3) ){
if( fts5Porter_MGt1(aBuf, nBuf-3) ){
*pnBuf = nBuf - 3;
}
}
break;
-
- case 'v':
+
+ case 'v':
if( nBuf>3 && 0==memcmp("ive", &aBuf[nBuf-3], 3) ){
if( fts5Porter_MGt1(aBuf, nBuf-3) ){
*pnBuf = nBuf - 3;
}
}
break;
-
- case 'z':
+
+ case 'z':
if( nBuf>3 && 0==memcmp("ize", &aBuf[nBuf-3], 3) ){
if( fts5Porter_MGt1(aBuf, nBuf-3) ){
*pnBuf = nBuf - 3;
}
}
break;
-
+
}
return ret;
}
-
+
static int fts5PorterStep1B2(char *aBuf, int *pnBuf){
int ret = 0;
int nBuf = *pnBuf;
switch( aBuf[nBuf-2] ){
-
- case 'a':
+
+ case 'a':
if( nBuf>2 && 0==memcmp("at", &aBuf[nBuf-2], 2) ){
memcpy(&aBuf[nBuf-2], "ate", 3);
*pnBuf = nBuf - 2 + 3;
ret = 1;
}
break;
-
- case 'b':
+
+ case 'b':
if( nBuf>2 && 0==memcmp("bl", &aBuf[nBuf-2], 2) ){
memcpy(&aBuf[nBuf-2], "ble", 3);
*pnBuf = nBuf - 2 + 3;
ret = 1;
}
break;
-
- case 'i':
+
+ case 'i':
if( nBuf>2 && 0==memcmp("iz", &aBuf[nBuf-2], 2) ){
memcpy(&aBuf[nBuf-2], "ize", 3);
*pnBuf = nBuf - 2 + 3;
ret = 1;
}
break;
-
+
}
return ret;
}
-
+
static int fts5PorterStep2(char *aBuf, int *pnBuf){
int ret = 0;
int nBuf = *pnBuf;
switch( aBuf[nBuf-2] ){
-
- case 'a':
+
+ case 'a':
if( nBuf>7 && 0==memcmp("ational", &aBuf[nBuf-7], 7) ){
if( fts5Porter_MGt0(aBuf, nBuf-7) ){
memcpy(&aBuf[nBuf-7], "ate", 3);
@@ -217974,8 +252874,8 @@ static int fts5PorterStep2(char *aBuf, int *pnBuf){
}
}
break;
-
- case 'c':
+
+ case 'c':
if( nBuf>4 && 0==memcmp("enci", &aBuf[nBuf-4], 4) ){
if( fts5Porter_MGt0(aBuf, nBuf-4) ){
memcpy(&aBuf[nBuf-4], "ence", 4);
@@ -217988,8 +252888,8 @@ static int fts5PorterStep2(char *aBuf, int *pnBuf){
}
}
break;
-
- case 'e':
+
+ case 'e':
if( nBuf>4 && 0==memcmp("izer", &aBuf[nBuf-4], 4) ){
if( fts5Porter_MGt0(aBuf, nBuf-4) ){
memcpy(&aBuf[nBuf-4], "ize", 3);
@@ -217997,8 +252897,8 @@ static int fts5PorterStep2(char *aBuf, int *pnBuf){
}
}
break;
-
- case 'g':
+
+ case 'g':
if( nBuf>4 && 0==memcmp("logi", &aBuf[nBuf-4], 4) ){
if( fts5Porter_MGt0(aBuf, nBuf-4) ){
memcpy(&aBuf[nBuf-4], "log", 3);
@@ -218006,8 +252906,8 @@ static int fts5PorterStep2(char *aBuf, int *pnBuf){
}
}
break;
-
- case 'l':
+
+ case 'l':
if( nBuf>3 && 0==memcmp("bli", &aBuf[nBuf-3], 3) ){
if( fts5Porter_MGt0(aBuf, nBuf-3) ){
memcpy(&aBuf[nBuf-3], "ble", 3);
@@ -218035,8 +252935,8 @@ static int fts5PorterStep2(char *aBuf, int *pnBuf){
}
}
break;
-
- case 'o':
+
+ case 'o':
if( nBuf>7 && 0==memcmp("ization", &aBuf[nBuf-7], 7) ){
if( fts5Porter_MGt0(aBuf, nBuf-7) ){
memcpy(&aBuf[nBuf-7], "ize", 3);
@@ -218054,8 +252954,8 @@ static int fts5PorterStep2(char *aBuf, int *pnBuf){
}
}
break;
-
- case 's':
+
+ case 's':
if( nBuf>5 && 0==memcmp("alism", &aBuf[nBuf-5], 5) ){
if( fts5Porter_MGt0(aBuf, nBuf-5) ){
memcpy(&aBuf[nBuf-5], "al", 2);
@@ -218078,8 +252978,8 @@ static int fts5PorterStep2(char *aBuf, int *pnBuf){
}
}
break;
-
- case 't':
+
+ case 't':
if( nBuf>5 && 0==memcmp("aliti", &aBuf[nBuf-5], 5) ){
if( fts5Porter_MGt0(aBuf, nBuf-5) ){
memcpy(&aBuf[nBuf-5], "al", 2);
@@ -218097,18 +252997,18 @@ static int fts5PorterStep2(char *aBuf, int *pnBuf){
}
}
break;
-
+
}
return ret;
}
-
+
static int fts5PorterStep3(char *aBuf, int *pnBuf){
int ret = 0;
int nBuf = *pnBuf;
switch( aBuf[nBuf-2] ){
-
- case 'a':
+
+ case 'a':
if( nBuf>4 && 0==memcmp("ical", &aBuf[nBuf-4], 4) ){
if( fts5Porter_MGt0(aBuf, nBuf-4) ){
memcpy(&aBuf[nBuf-4], "ic", 2);
@@ -218116,16 +253016,16 @@ static int fts5PorterStep3(char *aBuf, int *pnBuf){
}
}
break;
-
- case 's':
+
+ case 's':
if( nBuf>4 && 0==memcmp("ness", &aBuf[nBuf-4], 4) ){
if( fts5Porter_MGt0(aBuf, nBuf-4) ){
*pnBuf = nBuf - 4;
}
}
break;
-
- case 't':
+
+ case 't':
if( nBuf>5 && 0==memcmp("icate", &aBuf[nBuf-5], 5) ){
if( fts5Porter_MGt0(aBuf, nBuf-5) ){
memcpy(&aBuf[nBuf-5], "ic", 2);
@@ -218138,24 +253038,24 @@ static int fts5PorterStep3(char *aBuf, int *pnBuf){
}
}
break;
-
- case 'u':
+
+ case 'u':
if( nBuf>3 && 0==memcmp("ful", &aBuf[nBuf-3], 3) ){
if( fts5Porter_MGt0(aBuf, nBuf-3) ){
*pnBuf = nBuf - 3;
}
}
break;
-
- case 'v':
+
+ case 'v':
if( nBuf>5 && 0==memcmp("ative", &aBuf[nBuf-5], 5) ){
if( fts5Porter_MGt0(aBuf, nBuf-5) ){
*pnBuf = nBuf - 5;
}
}
break;
-
- case 'z':
+
+ case 'z':
if( nBuf>5 && 0==memcmp("alize", &aBuf[nBuf-5], 5) ){
if( fts5Porter_MGt0(aBuf, nBuf-5) ){
memcpy(&aBuf[nBuf-5], "al", 2);
@@ -218163,18 +253063,18 @@ static int fts5PorterStep3(char *aBuf, int *pnBuf){
}
}
break;
-
+
}
return ret;
}
-
+
static int fts5PorterStep1B(char *aBuf, int *pnBuf){
int ret = 0;
int nBuf = *pnBuf;
switch( aBuf[nBuf-2] ){
-
- case 'e':
+
+ case 'e':
if( nBuf>3 && 0==memcmp("eed", &aBuf[nBuf-3], 3) ){
if( fts5Porter_MGt0(aBuf, nBuf-3) ){
memcpy(&aBuf[nBuf-3], "ee", 2);
@@ -218187,8 +253087,8 @@ static int fts5PorterStep1B(char *aBuf, int *pnBuf){
}
}
break;
-
- case 'n':
+
+ case 'n':
if( nBuf>3 && 0==memcmp("ing", &aBuf[nBuf-3], 3) ){
if( fts5Porter_Vowel(aBuf, nBuf-3) ){
*pnBuf = nBuf - 3;
@@ -218196,12 +253096,12 @@ static int fts5PorterStep1B(char *aBuf, int *pnBuf){
}
}
break;
-
+
}
return ret;
}
-
-/*
+
+/*
** GENERATED CODE ENDS HERE (mkportersteps.tcl)
***************************************************************************
**************************************************************************/
@@ -218210,7 +253110,7 @@ static void fts5PorterStep1A(char *aBuf, int *pnBuf){
int nBuf = *pnBuf;
if( aBuf[nBuf-1]=='s' ){
if( aBuf[nBuf-2]=='e' ){
- if( (nBuf>4 && aBuf[nBuf-4]=='s' && aBuf[nBuf-3]=='s')
+ if( (nBuf>4 && aBuf[nBuf-4]=='s' && aBuf[nBuf-3]=='s')
|| (nBuf>3 && aBuf[nBuf-3]=='i' )
){
*pnBuf = nBuf-2;
@@ -218225,11 +253125,11 @@ static void fts5PorterStep1A(char *aBuf, int *pnBuf){
}
static int fts5PorterCb(
- void *pCtx,
+ void *pCtx,
int tflags,
- const char *pToken,
- int nToken,
- int iStart,
+ const char *pToken,
+ int nToken,
+ int iStart,
int iEnd
){
PorterContext *p = (PorterContext*)pCtx;
@@ -218247,8 +253147,8 @@ static int fts5PorterCb(
if( fts5PorterStep1B(aBuf, &nBuf) ){
if( fts5PorterStep1B2(aBuf, &nBuf)==0 ){
char c = aBuf[nBuf-1];
- if( fts5PorterIsVowel(c, 0)==0
- && c!='l' && c!='s' && c!='z' && c==aBuf[nBuf-2]
+ if( fts5PorterIsVowel(c, 0)==0
+ && c!='l' && c!='s' && c!='z' && c==aBuf[nBuf-2]
){
nBuf--;
}else if( fts5Porter_MEq1(aBuf, nBuf) && fts5Porter_Ostar(aBuf, nBuf) ){
@@ -218270,7 +253170,7 @@ static int fts5PorterCb(
/* Step 5a. */
assert( nBuf>0 );
if( aBuf[nBuf-1]=='e' ){
- if( fts5Porter_MGt1(aBuf, nBuf-1)
+ if( fts5Porter_MGt1(aBuf, nBuf-1)
|| (fts5Porter_MEq1(aBuf, nBuf-1) && !fts5Porter_Ostar(aBuf, nBuf-1))
){
nBuf--;
@@ -218278,8 +253178,8 @@ static int fts5PorterCb(
}
/* Step 5b. */
- if( nBuf>1 && aBuf[nBuf-1]=='l'
- && aBuf[nBuf-2]=='l' && fts5Porter_MGt1(aBuf, nBuf-1)
+ if( nBuf>1 && aBuf[nBuf-1]=='l'
+ && aBuf[nBuf-2]=='l' && fts5Porter_MGt1(aBuf, nBuf-1)
){
nBuf--;
}
@@ -218310,6 +253210,170 @@ static int fts5PorterTokenize(
);
}
+/**************************************************************************
+** Start of trigram implementation.
+*/
+typedef struct TrigramTokenizer TrigramTokenizer;
+struct TrigramTokenizer {
+ int bFold; /* True to fold to lower-case */
+ int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */
+};
+
+/*
+** Free a trigram tokenizer.
+*/
+static void fts5TriDelete(Fts5Tokenizer *p){
+ sqlite3_free(p);
+}
+
+/*
+** Allocate a trigram tokenizer.
+*/
+static int fts5TriCreate(
+ void *pUnused,
+ const char **azArg,
+ int nArg,
+ Fts5Tokenizer **ppOut
+){
+ int rc = SQLITE_OK;
+ TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
+ UNUSED_PARAM(pUnused);
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int i;
+ pNew->bFold = 1;
+ pNew->iFoldParam = 0;
+ for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
+ const char *zArg = azArg[i+1];
+ if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
+ rc = SQLITE_ERROR;
+ }else{
+ pNew->bFold = (zArg[0]=='0');
+ }
+ }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
+ if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
+ rc = SQLITE_ERROR;
+ }else{
+ pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0;
+ }
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ }
+
+ if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
+ rc = SQLITE_ERROR;
+ }
+
+ if( rc!=SQLITE_OK ){
+ fts5TriDelete((Fts5Tokenizer*)pNew);
+ pNew = 0;
+ }
+ }
+ *ppOut = (Fts5Tokenizer*)pNew;
+ return rc;
+}
+
+/*
+** Trigram tokenizer tokenize routine.
+*/
+static int fts5TriTokenize(
+ Fts5Tokenizer *pTok,
+ void *pCtx,
+ int unusedFlags,
+ const char *pText, int nText,
+ int (*xToken)(void*, int, const char*, int, int, int)
+){
+ TrigramTokenizer *p = (TrigramTokenizer*)pTok;
+ int rc = SQLITE_OK;
+ char aBuf[32];
+ char *zOut = aBuf;
+ int ii;
+ const unsigned char *zIn = (const unsigned char*)pText;
+ const unsigned char *zEof = &zIn[nText];
+ u32 iCode;
+ int aStart[3]; /* Input offset of each character in aBuf[] */
+
+ UNUSED_PARAM(unusedFlags);
+
+ /* Populate aBuf[] with the characters for the first trigram. */
+ for(ii=0; ii<3; ii++){
+ do {
+ aStart[ii] = zIn - (const unsigned char*)pText;
+ READ_UTF8(zIn, zEof, iCode);
+ if( iCode==0 ) return SQLITE_OK;
+ if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
+ }while( iCode==0 );
+ WRITE_UTF8(zOut, iCode);
+ }
+
+ /* At the start of each iteration of this loop:
+ **
+ ** aBuf: Contains 3 characters. The 3 characters of the next trigram.
+ ** zOut: Points to the byte following the last character in aBuf.
+ ** aStart[3]: Contains the byte offset in the input text corresponding
+ ** to the start of each of the three characters in the buffer.
+ */
+ assert( zIn<=zEof );
+ while( 1 ){
+ int iNext; /* Start of character following current tri */
+ const char *z1;
+
+ /* Read characters from the input up until the first non-diacritic */
+ do {
+ iNext = zIn - (const unsigned char*)pText;
+ READ_UTF8(zIn, zEof, iCode);
+ if( iCode==0 ) break;
+ if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
+ }while( iCode==0 );
+
+ /* Pass the current trigram back to fts5 */
+ rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext);
+ if( iCode==0 || rc!=SQLITE_OK ) break;
+
+ /* Remove the first character from buffer aBuf[]. Append the character
+ ** with codepoint iCode. */
+ z1 = aBuf;
+ FTS5_SKIP_UTF8(z1);
+ memmove(aBuf, z1, zOut - z1);
+ zOut -= (z1 - aBuf);
+ WRITE_UTF8(zOut, iCode);
+
+ /* Update the aStart[] array */
+ aStart[0] = aStart[1];
+ aStart[1] = aStart[2];
+ aStart[2] = iNext;
+ }
+
+ return rc;
+}
+
+/*
+** Argument xCreate is a pointer to a constructor function for a tokenizer.
+** pTok is a tokenizer previously created using the same method. This function
+** returns one of FTS5_PATTERN_NONE, FTS5_PATTERN_LIKE or FTS5_PATTERN_GLOB
+** indicating the style of pattern matching that the tokenizer can support.
+** In practice, this is:
+**
+** "trigram" tokenizer, case_sensitive=1 - FTS5_PATTERN_GLOB
+** "trigram" tokenizer, case_sensitive=0 (the default) - FTS5_PATTERN_LIKE
+** all other tokenizers - FTS5_PATTERN_NONE
+*/
+static int sqlite3Fts5TokenizerPattern(
+ int (*xCreate)(void*, const char**, int, Fts5Tokenizer**),
+ Fts5Tokenizer *pTok
+){
+ if( xCreate==fts5TriCreate ){
+ TrigramTokenizer *p = (TrigramTokenizer*)pTok;
+ if( p->iFoldParam==0 ){
+ return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
+ }
+ }
+ return FTS5_PATTERN_NONE;
+}
+
/*
** Register all built-in tokenizers with FTS5.
*/
@@ -218321,8 +253385,9 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){
{ "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}},
{ "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }},
{ "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }},
+ { "trigram", {fts5TriCreate, fts5TriDelete, fts5TriTokenize}},
};
-
+
int rc = SQLITE_OK; /* Return code */
int i; /* To iterate through builtin functions */
@@ -218338,10 +253403,8 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){
return rc;
}
-
-
/*
-** 2012 May 25
+** 2012-05-25
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -218370,32 +253433,48 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){
** E"). The resuls of passing a codepoint that corresponds to an
** uppercase letter are undefined.
*/
-static int fts5_remove_diacritic(int c){
+static int fts5_remove_diacritic(int c, int bComplex){
unsigned short aDia[] = {
- 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
- 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
- 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
- 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336,
- 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928,
- 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234,
- 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504,
- 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529,
- 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
- 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122,
- 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536,
- 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730,
- 62924, 63050, 63082, 63274, 63390,
+ 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
+ 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
+ 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
+ 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336,
+ 3456, 3696, 3712, 3728, 3744, 3766, 3832, 3896,
+ 3912, 3928, 3944, 3968, 4008, 4040, 4056, 4106,
+ 4138, 4170, 4202, 4234, 4266, 4296, 4312, 4344,
+ 4408, 4424, 4442, 4472, 4488, 4504, 6148, 6198,
+ 6264, 6280, 6360, 6429, 6505, 6529, 61448, 61468,
+ 61512, 61534, 61592, 61610, 61642, 61672, 61688, 61704,
+ 61726, 61784, 61800, 61816, 61836, 61880, 61896, 61914,
+ 61948, 61998, 62062, 62122, 62154, 62184, 62200, 62218,
+ 62252, 62302, 62364, 62410, 62442, 62478, 62536, 62554,
+ 62584, 62604, 62640, 62648, 62656, 62664, 62730, 62766,
+ 62830, 62890, 62924, 62974, 63032, 63050, 63082, 63118,
+ 63182, 63242, 63274, 63310, 63368, 63390,
};
- char aChar[] = {
- '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c',
- 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
- 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
- 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
- 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0',
- '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h',
- 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't',
- 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a',
- 'e', 'i', 'o', 'u', 'y',
+#define HIBIT ((unsigned char)0x80)
+ unsigned char aChar[] = {
+ '\0', 'a', 'c', 'e', 'i', 'n',
+ 'o', 'u', 'y', 'y', 'a', 'c',
+ 'd', 'e', 'e', 'g', 'h', 'i',
+ 'j', 'k', 'l', 'n', 'o', 'r',
+ 's', 't', 'u', 'u', 'w', 'y',
+ 'z', 'o', 'u', 'a', 'i', 'o',
+ 'u', 'u'|HIBIT, 'a'|HIBIT, 'g', 'k', 'o',
+ 'o'|HIBIT, 'j', 'g', 'n', 'a'|HIBIT, 'a',
+ 'e', 'i', 'o', 'r', 'u', 's',
+ 't', 'h', 'a', 'e', 'o'|HIBIT, 'o',
+ 'o'|HIBIT, 'y', '\0', '\0', '\0', '\0',
+ '\0', '\0', '\0', '\0', 'a', 'b',
+ 'c'|HIBIT, 'd', 'd', 'e'|HIBIT, 'e', 'e'|HIBIT,
+ 'f', 'g', 'h', 'h', 'i', 'i'|HIBIT,
+ 'k', 'l', 'l'|HIBIT, 'l', 'm', 'n',
+ 'o'|HIBIT, 'p', 'r', 'r'|HIBIT, 'r', 's',
+ 's'|HIBIT, 't', 'u', 'u'|HIBIT, 'v', 'w',
+ 'w', 'x', 'y', 'z', 'h', 't',
+ 'w', 'y', 'a', 'a'|HIBIT, 'a'|HIBIT, 'a'|HIBIT,
+ 'e', 'e'|HIBIT, 'e'|HIBIT, 'i', 'o', 'o'|HIBIT,
+ 'o'|HIBIT, 'o'|HIBIT, 'u', 'u'|HIBIT, 'u'|HIBIT, 'y',
};
unsigned int key = (((unsigned int)c)<<3) | 0x00000007;
@@ -218412,7 +253491,8 @@ static int fts5_remove_diacritic(int c){
}
}
assert( key>=aDia[iRes] );
- return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);
+ if( bComplex==0 && (aChar[iRes] & 0x80) ) return c;
+ return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : ((int)aChar[iRes] & 0x7F);
}
@@ -218425,8 +253505,8 @@ static int sqlite3Fts5UnicodeIsdiacritic(int c){
unsigned int mask1 = 0x000361F8;
if( c<768 || c>817 ) return 0;
return (c < 768+32) ?
- (mask0 & (1 << (c-768))) :
- (mask1 & (1 << (c-768-32)));
+ (mask0 & ((unsigned int)1 << (c-768))) :
+ (mask1 & ((unsigned int)1 << (c-768-32)));
}
@@ -218439,7 +253519,7 @@ static int sqlite3Fts5UnicodeIsdiacritic(int c){
** The results are undefined if the value passed to this function
** is less than zero.
*/
-static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){
+static int sqlite3Fts5UnicodeFold(int c, int eRemoveDiacritic){
/* Each entry in the following array defines a rule for folding a range
** of codepoints to lower case. The rule applies to a range of nRange
** codepoints starting at codepoint iCode.
@@ -218516,19 +253596,19 @@ static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){
{42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1},
{42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1},
{42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1},
- {65313, 14, 26},
+ {65313, 14, 26},
};
static const unsigned short aiOff[] = {
- 1, 2, 8, 15, 16, 26, 28, 32,
- 37, 38, 40, 48, 63, 64, 69, 71,
- 79, 80, 116, 202, 203, 205, 206, 207,
- 209, 210, 211, 213, 214, 217, 218, 219,
- 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721,
- 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
- 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
- 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
- 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
- 65514, 65521, 65527, 65528, 65529,
+ 1, 2, 8, 15, 16, 26, 28, 32,
+ 37, 38, 40, 48, 63, 64, 69, 71,
+ 79, 80, 116, 202, 203, 205, 206, 207,
+ 209, 210, 211, 213, 214, 217, 218, 219,
+ 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721,
+ 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
+ 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
+ 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
+ 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
+ 65514, 65521, 65527, 65528, 65529,
};
int ret = c;
@@ -218562,9 +253642,11 @@ static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){
assert( ret>0 );
}
- if( bRemoveDiacritic ) ret = fts5_remove_diacritic(ret);
+ if( eRemoveDiacritic ){
+ ret = fts5_remove_diacritic(ret, eRemoveDiacritic==2);
+ }
}
-
+
else if( c>=66560 && c<66600 ){
ret = c + 40;
}
@@ -218573,13 +253655,7 @@ static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){
}
-#if 0
-static int sqlite3Fts5UnicodeNCat(void) {
- return 32;
-}
-#endif
-
-static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
+static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
aArray[0] = 1;
switch( zCat[0] ){
case 'C':
@@ -218589,7 +253665,7 @@ static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
case 'n': aArray[3] = 1; break;
case 's': aArray[4] = 1; break;
case 'o': aArray[31] = 1; break;
- case '*':
+ case '*':
aArray[1] = 1;
aArray[2] = 1;
aArray[3] = 1;
@@ -218607,7 +253683,7 @@ static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
case 't': aArray[8] = 1; break;
case 'u': aArray[9] = 1; break;
case 'C': aArray[30] = 1; break;
- case '*':
+ case '*':
aArray[5] = 1;
aArray[6] = 1;
aArray[7] = 1;
@@ -218623,7 +253699,7 @@ static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
case 'c': aArray[10] = 1; break;
case 'e': aArray[11] = 1; break;
case 'n': aArray[12] = 1; break;
- case '*':
+ case '*':
aArray[10] = 1;
aArray[11] = 1;
aArray[12] = 1;
@@ -218636,7 +253712,7 @@ static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
case 'd': aArray[13] = 1; break;
case 'l': aArray[14] = 1; break;
case 'o': aArray[15] = 1; break;
- case '*':
+ case '*':
aArray[13] = 1;
aArray[14] = 1;
aArray[15] = 1;
@@ -218653,7 +253729,7 @@ static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
case 'i': aArray[20] = 1; break;
case 'o': aArray[21] = 1; break;
case 's': aArray[22] = 1; break;
- case '*':
+ case '*':
aArray[16] = 1;
aArray[17] = 1;
aArray[18] = 1;
@@ -218671,7 +253747,7 @@ static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
case 'k': aArray[24] = 1; break;
case 'm': aArray[25] = 1; break;
case 'o': aArray[26] = 1; break;
- case '*':
+ case '*':
aArray[23] = 1;
aArray[24] = 1;
aArray[25] = 1;
@@ -218685,7 +253761,7 @@ static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
case 'l': aArray[27] = 1; break;
case 'p': aArray[28] = 1; break;
case 's': aArray[29] = 1; break;
- case '*':
+ case '*':
aArray[27] = 1;
aArray[28] = 1;
aArray[29] = 1;
@@ -218698,369 +253774,369 @@ static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
}
static u16 aFts5UnicodeBlock[] = {
- 0, 1471, 1753, 1760, 1760, 1760, 1760, 1760, 1760, 1760,
- 1760, 1760, 1760, 1760, 1760, 1763, 1765,
+ 0, 1471, 1753, 1760, 1760, 1760, 1760, 1760, 1760, 1760,
+ 1760, 1760, 1760, 1760, 1760, 1763, 1765,
};
static u16 aFts5UnicodeMap[] = {
- 0, 32, 33, 36, 37, 40, 41, 42, 43, 44,
- 45, 46, 48, 58, 60, 63, 65, 91, 92, 93,
- 94, 95, 96, 97, 123, 124, 125, 126, 127, 160,
- 161, 162, 166, 167, 168, 169, 170, 171, 172, 173,
- 174, 175, 176, 177, 178, 180, 181, 182, 184, 185,
- 186, 187, 188, 191, 192, 215, 216, 223, 247, 248,
- 256, 312, 313, 329, 330, 377, 383, 385, 387, 388,
- 391, 394, 396, 398, 402, 403, 405, 406, 409, 412,
- 414, 415, 417, 418, 423, 427, 428, 431, 434, 436,
- 437, 440, 442, 443, 444, 446, 448, 452, 453, 454,
- 455, 456, 457, 458, 459, 460, 461, 477, 478, 496,
- 497, 498, 499, 500, 503, 505, 506, 564, 570, 572,
- 573, 575, 577, 580, 583, 584, 592, 660, 661, 688,
- 706, 710, 722, 736, 741, 748, 749, 750, 751, 768,
- 880, 884, 885, 886, 890, 891, 894, 900, 902, 903,
- 904, 908, 910, 912, 913, 931, 940, 975, 977, 978,
- 981, 984, 1008, 1012, 1014, 1015, 1018, 1020, 1021, 1072,
- 1120, 1154, 1155, 1160, 1162, 1217, 1231, 1232, 1329, 1369,
- 1370, 1377, 1417, 1418, 1423, 1425, 1470, 1471, 1472, 1473,
- 1475, 1476, 1478, 1479, 1488, 1520, 1523, 1536, 1542, 1545,
- 1547, 1548, 1550, 1552, 1563, 1566, 1568, 1600, 1601, 1611,
- 1632, 1642, 1646, 1648, 1649, 1748, 1749, 1750, 1757, 1758,
- 1759, 1765, 1767, 1769, 1770, 1774, 1776, 1786, 1789, 1791,
- 1792, 1807, 1808, 1809, 1810, 1840, 1869, 1958, 1969, 1984,
- 1994, 2027, 2036, 2038, 2039, 2042, 2048, 2070, 2074, 2075,
- 2084, 2085, 2088, 2089, 2096, 2112, 2137, 2142, 2208, 2210,
- 2276, 2304, 2307, 2308, 2362, 2363, 2364, 2365, 2366, 2369,
- 2377, 2381, 2382, 2384, 2385, 2392, 2402, 2404, 2406, 2416,
- 2417, 2418, 2425, 2433, 2434, 2437, 2447, 2451, 2474, 2482,
- 2486, 2492, 2493, 2494, 2497, 2503, 2507, 2509, 2510, 2519,
- 2524, 2527, 2530, 2534, 2544, 2546, 2548, 2554, 2555, 2561,
- 2563, 2565, 2575, 2579, 2602, 2610, 2613, 2616, 2620, 2622,
- 2625, 2631, 2635, 2641, 2649, 2654, 2662, 2672, 2674, 2677,
- 2689, 2691, 2693, 2703, 2707, 2730, 2738, 2741, 2748, 2749,
- 2750, 2753, 2759, 2761, 2763, 2765, 2768, 2784, 2786, 2790,
- 2800, 2801, 2817, 2818, 2821, 2831, 2835, 2858, 2866, 2869,
- 2876, 2877, 2878, 2879, 2880, 2881, 2887, 2891, 2893, 2902,
- 2903, 2908, 2911, 2914, 2918, 2928, 2929, 2930, 2946, 2947,
- 2949, 2958, 2962, 2969, 2972, 2974, 2979, 2984, 2990, 3006,
- 3008, 3009, 3014, 3018, 3021, 3024, 3031, 3046, 3056, 3059,
- 3065, 3066, 3073, 3077, 3086, 3090, 3114, 3125, 3133, 3134,
- 3137, 3142, 3146, 3157, 3160, 3168, 3170, 3174, 3192, 3199,
- 3202, 3205, 3214, 3218, 3242, 3253, 3260, 3261, 3262, 3263,
- 3264, 3270, 3271, 3274, 3276, 3285, 3294, 3296, 3298, 3302,
- 3313, 3330, 3333, 3342, 3346, 3389, 3390, 3393, 3398, 3402,
- 3405, 3406, 3415, 3424, 3426, 3430, 3440, 3449, 3450, 3458,
- 3461, 3482, 3507, 3517, 3520, 3530, 3535, 3538, 3542, 3544,
- 3570, 3572, 3585, 3633, 3634, 3636, 3647, 3648, 3654, 3655,
- 3663, 3664, 3674, 3713, 3716, 3719, 3722, 3725, 3732, 3737,
- 3745, 3749, 3751, 3754, 3757, 3761, 3762, 3764, 3771, 3773,
- 3776, 3782, 3784, 3792, 3804, 3840, 3841, 3844, 3859, 3860,
- 3861, 3864, 3866, 3872, 3882, 3892, 3893, 3894, 3895, 3896,
- 3897, 3898, 3899, 3900, 3901, 3902, 3904, 3913, 3953, 3967,
- 3968, 3973, 3974, 3976, 3981, 3993, 4030, 4038, 4039, 4046,
- 4048, 4053, 4057, 4096, 4139, 4141, 4145, 4146, 4152, 4153,
- 4155, 4157, 4159, 4160, 4170, 4176, 4182, 4184, 4186, 4190,
- 4193, 4194, 4197, 4199, 4206, 4209, 4213, 4226, 4227, 4229,
- 4231, 4237, 4238, 4239, 4240, 4250, 4253, 4254, 4256, 4295,
- 4301, 4304, 4347, 4348, 4349, 4682, 4688, 4696, 4698, 4704,
- 4746, 4752, 4786, 4792, 4800, 4802, 4808, 4824, 4882, 4888,
- 4957, 4960, 4969, 4992, 5008, 5024, 5120, 5121, 5741, 5743,
- 5760, 5761, 5787, 5788, 5792, 5867, 5870, 5888, 5902, 5906,
- 5920, 5938, 5941, 5952, 5970, 5984, 5998, 6002, 6016, 6068,
- 6070, 6071, 6078, 6086, 6087, 6089, 6100, 6103, 6104, 6107,
- 6108, 6109, 6112, 6128, 6144, 6150, 6151, 6155, 6158, 6160,
- 6176, 6211, 6212, 6272, 6313, 6314, 6320, 6400, 6432, 6435,
- 6439, 6441, 6448, 6450, 6451, 6457, 6464, 6468, 6470, 6480,
- 6512, 6528, 6576, 6593, 6600, 6608, 6618, 6622, 6656, 6679,
- 6681, 6686, 6688, 6741, 6742, 6743, 6744, 6752, 6753, 6754,
- 6755, 6757, 6765, 6771, 6783, 6784, 6800, 6816, 6823, 6824,
- 6912, 6916, 6917, 6964, 6965, 6966, 6971, 6972, 6973, 6978,
- 6979, 6981, 6992, 7002, 7009, 7019, 7028, 7040, 7042, 7043,
- 7073, 7074, 7078, 7080, 7082, 7083, 7084, 7086, 7088, 7098,
- 7142, 7143, 7144, 7146, 7149, 7150, 7151, 7154, 7164, 7168,
- 7204, 7212, 7220, 7222, 7227, 7232, 7245, 7248, 7258, 7288,
- 7294, 7360, 7376, 7379, 7380, 7393, 7394, 7401, 7405, 7406,
- 7410, 7412, 7413, 7424, 7468, 7531, 7544, 7545, 7579, 7616,
- 7676, 7680, 7830, 7838, 7936, 7944, 7952, 7960, 7968, 7976,
- 7984, 7992, 8000, 8008, 8016, 8025, 8027, 8029, 8031, 8033,
- 8040, 8048, 8064, 8072, 8080, 8088, 8096, 8104, 8112, 8118,
- 8120, 8124, 8125, 8126, 8127, 8130, 8134, 8136, 8140, 8141,
- 8144, 8150, 8152, 8157, 8160, 8168, 8173, 8178, 8182, 8184,
- 8188, 8189, 8192, 8203, 8208, 8214, 8216, 8217, 8218, 8219,
- 8221, 8222, 8223, 8224, 8232, 8233, 8234, 8239, 8240, 8249,
- 8250, 8251, 8255, 8257, 8260, 8261, 8262, 8263, 8274, 8275,
- 8276, 8277, 8287, 8288, 8298, 8304, 8305, 8308, 8314, 8317,
- 8318, 8319, 8320, 8330, 8333, 8334, 8336, 8352, 8400, 8413,
- 8417, 8418, 8421, 8448, 8450, 8451, 8455, 8456, 8458, 8459,
- 8462, 8464, 8467, 8468, 8469, 8470, 8472, 8473, 8478, 8484,
- 8485, 8486, 8487, 8488, 8489, 8490, 8494, 8495, 8496, 8500,
- 8501, 8505, 8506, 8508, 8510, 8512, 8517, 8519, 8522, 8523,
- 8524, 8526, 8527, 8528, 8544, 8579, 8581, 8585, 8592, 8597,
- 8602, 8604, 8608, 8609, 8611, 8612, 8614, 8615, 8622, 8623,
- 8654, 8656, 8658, 8659, 8660, 8661, 8692, 8960, 8968, 8972,
- 8992, 8994, 9001, 9002, 9003, 9084, 9085, 9115, 9140, 9180,
- 9186, 9216, 9280, 9312, 9372, 9450, 9472, 9655, 9656, 9665,
- 9666, 9720, 9728, 9839, 9840, 9985, 10088, 10089, 10090, 10091,
- 10092, 10093, 10094, 10095, 10096, 10097, 10098, 10099, 10100, 10101,
- 10102, 10132, 10176, 10181, 10182, 10183, 10214, 10215, 10216, 10217,
- 10218, 10219, 10220, 10221, 10222, 10223, 10224, 10240, 10496, 10627,
- 10628, 10629, 10630, 10631, 10632, 10633, 10634, 10635, 10636, 10637,
- 10638, 10639, 10640, 10641, 10642, 10643, 10644, 10645, 10646, 10647,
- 10648, 10649, 10712, 10713, 10714, 10715, 10716, 10748, 10749, 10750,
- 11008, 11056, 11077, 11079, 11088, 11264, 11312, 11360, 11363, 11365,
- 11367, 11374, 11377, 11378, 11380, 11381, 11383, 11388, 11390, 11393,
- 11394, 11492, 11493, 11499, 11503, 11506, 11513, 11517, 11518, 11520,
- 11559, 11565, 11568, 11631, 11632, 11647, 11648, 11680, 11688, 11696,
- 11704, 11712, 11720, 11728, 11736, 11744, 11776, 11778, 11779, 11780,
- 11781, 11782, 11785, 11786, 11787, 11788, 11789, 11790, 11799, 11800,
- 11802, 11803, 11804, 11805, 11806, 11808, 11809, 11810, 11811, 11812,
- 11813, 11814, 11815, 11816, 11817, 11818, 11823, 11824, 11834, 11904,
- 11931, 12032, 12272, 12288, 12289, 12292, 12293, 12294, 12295, 12296,
- 12297, 12298, 12299, 12300, 12301, 12302, 12303, 12304, 12305, 12306,
- 12308, 12309, 12310, 12311, 12312, 12313, 12314, 12315, 12316, 12317,
- 12318, 12320, 12321, 12330, 12334, 12336, 12337, 12342, 12344, 12347,
- 12348, 12349, 12350, 12353, 12441, 12443, 12445, 12447, 12448, 12449,
- 12539, 12540, 12543, 12549, 12593, 12688, 12690, 12694, 12704, 12736,
- 12784, 12800, 12832, 12842, 12872, 12880, 12881, 12896, 12928, 12938,
- 12977, 12992, 13056, 13312, 19893, 19904, 19968, 40908, 40960, 40981,
- 40982, 42128, 42192, 42232, 42238, 42240, 42508, 42509, 42512, 42528,
- 42538, 42560, 42606, 42607, 42608, 42611, 42612, 42622, 42623, 42624,
- 42655, 42656, 42726, 42736, 42738, 42752, 42775, 42784, 42786, 42800,
- 42802, 42864, 42865, 42873, 42878, 42888, 42889, 42891, 42896, 42912,
- 43000, 43002, 43003, 43010, 43011, 43014, 43015, 43019, 43020, 43043,
- 43045, 43047, 43048, 43056, 43062, 43064, 43065, 43072, 43124, 43136,
- 43138, 43188, 43204, 43214, 43216, 43232, 43250, 43256, 43259, 43264,
- 43274, 43302, 43310, 43312, 43335, 43346, 43359, 43360, 43392, 43395,
- 43396, 43443, 43444, 43446, 43450, 43452, 43453, 43457, 43471, 43472,
- 43486, 43520, 43561, 43567, 43569, 43571, 43573, 43584, 43587, 43588,
- 43596, 43597, 43600, 43612, 43616, 43632, 43633, 43639, 43642, 43643,
- 43648, 43696, 43697, 43698, 43701, 43703, 43705, 43710, 43712, 43713,
- 43714, 43739, 43741, 43742, 43744, 43755, 43756, 43758, 43760, 43762,
- 43763, 43765, 43766, 43777, 43785, 43793, 43808, 43816, 43968, 44003,
- 44005, 44006, 44008, 44009, 44011, 44012, 44013, 44016, 44032, 55203,
- 55216, 55243, 55296, 56191, 56319, 57343, 57344, 63743, 63744, 64112,
- 64256, 64275, 64285, 64286, 64287, 64297, 64298, 64312, 64318, 64320,
- 64323, 64326, 64434, 64467, 64830, 64831, 64848, 64914, 65008, 65020,
- 65021, 65024, 65040, 65047, 65048, 65049, 65056, 65072, 65073, 65075,
- 65077, 65078, 65079, 65080, 65081, 65082, 65083, 65084, 65085, 65086,
- 65087, 65088, 65089, 65090, 65091, 65092, 65093, 65095, 65096, 65097,
- 65101, 65104, 65108, 65112, 65113, 65114, 65115, 65116, 65117, 65118,
- 65119, 65122, 65123, 65124, 65128, 65129, 65130, 65136, 65142, 65279,
- 65281, 65284, 65285, 65288, 65289, 65290, 65291, 65292, 65293, 65294,
- 65296, 65306, 65308, 65311, 65313, 65339, 65340, 65341, 65342, 65343,
- 65344, 65345, 65371, 65372, 65373, 65374, 65375, 65376, 65377, 65378,
- 65379, 65380, 65382, 65392, 65393, 65438, 65440, 65474, 65482, 65490,
- 65498, 65504, 65506, 65507, 65508, 65509, 65512, 65513, 65517, 65529,
- 65532, 0, 13, 40, 60, 63, 80, 128, 256, 263,
- 311, 320, 373, 377, 394, 400, 464, 509, 640, 672,
- 768, 800, 816, 833, 834, 842, 896, 927, 928, 968,
- 976, 977, 1024, 1064, 1104, 1184, 2048, 2056, 2058, 2103,
- 2108, 2111, 2135, 2136, 2304, 2326, 2335, 2336, 2367, 2432,
- 2494, 2560, 2561, 2565, 2572, 2576, 2581, 2585, 2616, 2623,
- 2624, 2640, 2656, 2685, 2687, 2816, 2873, 2880, 2904, 2912,
- 2936, 3072, 3680, 4096, 4097, 4098, 4099, 4152, 4167, 4178,
- 4198, 4224, 4226, 4227, 4272, 4275, 4279, 4281, 4283, 4285,
- 4286, 4304, 4336, 4352, 4355, 4391, 4396, 4397, 4406, 4416,
- 4480, 4482, 4483, 4531, 4534, 4543, 4545, 4549, 4560, 5760,
- 5803, 5804, 5805, 5806, 5808, 5814, 5815, 5824, 8192, 9216,
- 9328, 12288, 26624, 28416, 28496, 28497, 28559, 28563, 45056, 53248,
- 53504, 53545, 53605, 53607, 53610, 53613, 53619, 53627, 53635, 53637,
- 53644, 53674, 53678, 53760, 53826, 53829, 54016, 54112, 54272, 54298,
- 54324, 54350, 54358, 54376, 54402, 54428, 54430, 54434, 54437, 54441,
- 54446, 54454, 54459, 54461, 54469, 54480, 54506, 54532, 54535, 54541,
- 54550, 54558, 54584, 54587, 54592, 54598, 54602, 54610, 54636, 54662,
- 54688, 54714, 54740, 54766, 54792, 54818, 54844, 54870, 54896, 54922,
- 54952, 54977, 54978, 55003, 55004, 55010, 55035, 55036, 55061, 55062,
- 55068, 55093, 55094, 55119, 55120, 55126, 55151, 55152, 55177, 55178,
- 55184, 55209, 55210, 55235, 55236, 55242, 55246, 60928, 60933, 60961,
- 60964, 60967, 60969, 60980, 60985, 60987, 60994, 60999, 61001, 61003,
- 61005, 61009, 61012, 61015, 61017, 61019, 61021, 61023, 61025, 61028,
- 61031, 61036, 61044, 61049, 61054, 61056, 61067, 61089, 61093, 61099,
- 61168, 61440, 61488, 61600, 61617, 61633, 61649, 61696, 61712, 61744,
- 61808, 61926, 61968, 62016, 62032, 62208, 62256, 62263, 62336, 62368,
- 62406, 62432, 62464, 62528, 62530, 62713, 62720, 62784, 62800, 62971,
- 63045, 63104, 63232, 0, 42710, 42752, 46900, 46912, 47133, 63488,
- 1, 32, 256, 0, 65533,
+ 0, 32, 33, 36, 37, 40, 41, 42, 43, 44,
+ 45, 46, 48, 58, 60, 63, 65, 91, 92, 93,
+ 94, 95, 96, 97, 123, 124, 125, 126, 127, 160,
+ 161, 162, 166, 167, 168, 169, 170, 171, 172, 173,
+ 174, 175, 176, 177, 178, 180, 181, 182, 184, 185,
+ 186, 187, 188, 191, 192, 215, 216, 223, 247, 248,
+ 256, 312, 313, 329, 330, 377, 383, 385, 387, 388,
+ 391, 394, 396, 398, 402, 403, 405, 406, 409, 412,
+ 414, 415, 417, 418, 423, 427, 428, 431, 434, 436,
+ 437, 440, 442, 443, 444, 446, 448, 452, 453, 454,
+ 455, 456, 457, 458, 459, 460, 461, 477, 478, 496,
+ 497, 498, 499, 500, 503, 505, 506, 564, 570, 572,
+ 573, 575, 577, 580, 583, 584, 592, 660, 661, 688,
+ 706, 710, 722, 736, 741, 748, 749, 750, 751, 768,
+ 880, 884, 885, 886, 890, 891, 894, 900, 902, 903,
+ 904, 908, 910, 912, 913, 931, 940, 975, 977, 978,
+ 981, 984, 1008, 1012, 1014, 1015, 1018, 1020, 1021, 1072,
+ 1120, 1154, 1155, 1160, 1162, 1217, 1231, 1232, 1329, 1369,
+ 1370, 1377, 1417, 1418, 1423, 1425, 1470, 1471, 1472, 1473,
+ 1475, 1476, 1478, 1479, 1488, 1520, 1523, 1536, 1542, 1545,
+ 1547, 1548, 1550, 1552, 1563, 1566, 1568, 1600, 1601, 1611,
+ 1632, 1642, 1646, 1648, 1649, 1748, 1749, 1750, 1757, 1758,
+ 1759, 1765, 1767, 1769, 1770, 1774, 1776, 1786, 1789, 1791,
+ 1792, 1807, 1808, 1809, 1810, 1840, 1869, 1958, 1969, 1984,
+ 1994, 2027, 2036, 2038, 2039, 2042, 2048, 2070, 2074, 2075,
+ 2084, 2085, 2088, 2089, 2096, 2112, 2137, 2142, 2208, 2210,
+ 2276, 2304, 2307, 2308, 2362, 2363, 2364, 2365, 2366, 2369,
+ 2377, 2381, 2382, 2384, 2385, 2392, 2402, 2404, 2406, 2416,
+ 2417, 2418, 2425, 2433, 2434, 2437, 2447, 2451, 2474, 2482,
+ 2486, 2492, 2493, 2494, 2497, 2503, 2507, 2509, 2510, 2519,
+ 2524, 2527, 2530, 2534, 2544, 2546, 2548, 2554, 2555, 2561,
+ 2563, 2565, 2575, 2579, 2602, 2610, 2613, 2616, 2620, 2622,
+ 2625, 2631, 2635, 2641, 2649, 2654, 2662, 2672, 2674, 2677,
+ 2689, 2691, 2693, 2703, 2707, 2730, 2738, 2741, 2748, 2749,
+ 2750, 2753, 2759, 2761, 2763, 2765, 2768, 2784, 2786, 2790,
+ 2800, 2801, 2817, 2818, 2821, 2831, 2835, 2858, 2866, 2869,
+ 2876, 2877, 2878, 2879, 2880, 2881, 2887, 2891, 2893, 2902,
+ 2903, 2908, 2911, 2914, 2918, 2928, 2929, 2930, 2946, 2947,
+ 2949, 2958, 2962, 2969, 2972, 2974, 2979, 2984, 2990, 3006,
+ 3008, 3009, 3014, 3018, 3021, 3024, 3031, 3046, 3056, 3059,
+ 3065, 3066, 3073, 3077, 3086, 3090, 3114, 3125, 3133, 3134,
+ 3137, 3142, 3146, 3157, 3160, 3168, 3170, 3174, 3192, 3199,
+ 3202, 3205, 3214, 3218, 3242, 3253, 3260, 3261, 3262, 3263,
+ 3264, 3270, 3271, 3274, 3276, 3285, 3294, 3296, 3298, 3302,
+ 3313, 3330, 3333, 3342, 3346, 3389, 3390, 3393, 3398, 3402,
+ 3405, 3406, 3415, 3424, 3426, 3430, 3440, 3449, 3450, 3458,
+ 3461, 3482, 3507, 3517, 3520, 3530, 3535, 3538, 3542, 3544,
+ 3570, 3572, 3585, 3633, 3634, 3636, 3647, 3648, 3654, 3655,
+ 3663, 3664, 3674, 3713, 3716, 3719, 3722, 3725, 3732, 3737,
+ 3745, 3749, 3751, 3754, 3757, 3761, 3762, 3764, 3771, 3773,
+ 3776, 3782, 3784, 3792, 3804, 3840, 3841, 3844, 3859, 3860,
+ 3861, 3864, 3866, 3872, 3882, 3892, 3893, 3894, 3895, 3896,
+ 3897, 3898, 3899, 3900, 3901, 3902, 3904, 3913, 3953, 3967,
+ 3968, 3973, 3974, 3976, 3981, 3993, 4030, 4038, 4039, 4046,
+ 4048, 4053, 4057, 4096, 4139, 4141, 4145, 4146, 4152, 4153,
+ 4155, 4157, 4159, 4160, 4170, 4176, 4182, 4184, 4186, 4190,
+ 4193, 4194, 4197, 4199, 4206, 4209, 4213, 4226, 4227, 4229,
+ 4231, 4237, 4238, 4239, 4240, 4250, 4253, 4254, 4256, 4295,
+ 4301, 4304, 4347, 4348, 4349, 4682, 4688, 4696, 4698, 4704,
+ 4746, 4752, 4786, 4792, 4800, 4802, 4808, 4824, 4882, 4888,
+ 4957, 4960, 4969, 4992, 5008, 5024, 5120, 5121, 5741, 5743,
+ 5760, 5761, 5787, 5788, 5792, 5867, 5870, 5888, 5902, 5906,
+ 5920, 5938, 5941, 5952, 5970, 5984, 5998, 6002, 6016, 6068,
+ 6070, 6071, 6078, 6086, 6087, 6089, 6100, 6103, 6104, 6107,
+ 6108, 6109, 6112, 6128, 6144, 6150, 6151, 6155, 6158, 6160,
+ 6176, 6211, 6212, 6272, 6313, 6314, 6320, 6400, 6432, 6435,
+ 6439, 6441, 6448, 6450, 6451, 6457, 6464, 6468, 6470, 6480,
+ 6512, 6528, 6576, 6593, 6600, 6608, 6618, 6622, 6656, 6679,
+ 6681, 6686, 6688, 6741, 6742, 6743, 6744, 6752, 6753, 6754,
+ 6755, 6757, 6765, 6771, 6783, 6784, 6800, 6816, 6823, 6824,
+ 6912, 6916, 6917, 6964, 6965, 6966, 6971, 6972, 6973, 6978,
+ 6979, 6981, 6992, 7002, 7009, 7019, 7028, 7040, 7042, 7043,
+ 7073, 7074, 7078, 7080, 7082, 7083, 7084, 7086, 7088, 7098,
+ 7142, 7143, 7144, 7146, 7149, 7150, 7151, 7154, 7164, 7168,
+ 7204, 7212, 7220, 7222, 7227, 7232, 7245, 7248, 7258, 7288,
+ 7294, 7360, 7376, 7379, 7380, 7393, 7394, 7401, 7405, 7406,
+ 7410, 7412, 7413, 7424, 7468, 7531, 7544, 7545, 7579, 7616,
+ 7676, 7680, 7830, 7838, 7936, 7944, 7952, 7960, 7968, 7976,
+ 7984, 7992, 8000, 8008, 8016, 8025, 8027, 8029, 8031, 8033,
+ 8040, 8048, 8064, 8072, 8080, 8088, 8096, 8104, 8112, 8118,
+ 8120, 8124, 8125, 8126, 8127, 8130, 8134, 8136, 8140, 8141,
+ 8144, 8150, 8152, 8157, 8160, 8168, 8173, 8178, 8182, 8184,
+ 8188, 8189, 8192, 8203, 8208, 8214, 8216, 8217, 8218, 8219,
+ 8221, 8222, 8223, 8224, 8232, 8233, 8234, 8239, 8240, 8249,
+ 8250, 8251, 8255, 8257, 8260, 8261, 8262, 8263, 8274, 8275,
+ 8276, 8277, 8287, 8288, 8298, 8304, 8305, 8308, 8314, 8317,
+ 8318, 8319, 8320, 8330, 8333, 8334, 8336, 8352, 8400, 8413,
+ 8417, 8418, 8421, 8448, 8450, 8451, 8455, 8456, 8458, 8459,
+ 8462, 8464, 8467, 8468, 8469, 8470, 8472, 8473, 8478, 8484,
+ 8485, 8486, 8487, 8488, 8489, 8490, 8494, 8495, 8496, 8500,
+ 8501, 8505, 8506, 8508, 8510, 8512, 8517, 8519, 8522, 8523,
+ 8524, 8526, 8527, 8528, 8544, 8579, 8581, 8585, 8592, 8597,
+ 8602, 8604, 8608, 8609, 8611, 8612, 8614, 8615, 8622, 8623,
+ 8654, 8656, 8658, 8659, 8660, 8661, 8692, 8960, 8968, 8972,
+ 8992, 8994, 9001, 9002, 9003, 9084, 9085, 9115, 9140, 9180,
+ 9186, 9216, 9280, 9312, 9372, 9450, 9472, 9655, 9656, 9665,
+ 9666, 9720, 9728, 9839, 9840, 9985, 10088, 10089, 10090, 10091,
+ 10092, 10093, 10094, 10095, 10096, 10097, 10098, 10099, 10100, 10101,
+ 10102, 10132, 10176, 10181, 10182, 10183, 10214, 10215, 10216, 10217,
+ 10218, 10219, 10220, 10221, 10222, 10223, 10224, 10240, 10496, 10627,
+ 10628, 10629, 10630, 10631, 10632, 10633, 10634, 10635, 10636, 10637,
+ 10638, 10639, 10640, 10641, 10642, 10643, 10644, 10645, 10646, 10647,
+ 10648, 10649, 10712, 10713, 10714, 10715, 10716, 10748, 10749, 10750,
+ 11008, 11056, 11077, 11079, 11088, 11264, 11312, 11360, 11363, 11365,
+ 11367, 11374, 11377, 11378, 11380, 11381, 11383, 11388, 11390, 11393,
+ 11394, 11492, 11493, 11499, 11503, 11506, 11513, 11517, 11518, 11520,
+ 11559, 11565, 11568, 11631, 11632, 11647, 11648, 11680, 11688, 11696,
+ 11704, 11712, 11720, 11728, 11736, 11744, 11776, 11778, 11779, 11780,
+ 11781, 11782, 11785, 11786, 11787, 11788, 11789, 11790, 11799, 11800,
+ 11802, 11803, 11804, 11805, 11806, 11808, 11809, 11810, 11811, 11812,
+ 11813, 11814, 11815, 11816, 11817, 11818, 11823, 11824, 11834, 11904,
+ 11931, 12032, 12272, 12288, 12289, 12292, 12293, 12294, 12295, 12296,
+ 12297, 12298, 12299, 12300, 12301, 12302, 12303, 12304, 12305, 12306,
+ 12308, 12309, 12310, 12311, 12312, 12313, 12314, 12315, 12316, 12317,
+ 12318, 12320, 12321, 12330, 12334, 12336, 12337, 12342, 12344, 12347,
+ 12348, 12349, 12350, 12353, 12441, 12443, 12445, 12447, 12448, 12449,
+ 12539, 12540, 12543, 12549, 12593, 12688, 12690, 12694, 12704, 12736,
+ 12784, 12800, 12832, 12842, 12872, 12880, 12881, 12896, 12928, 12938,
+ 12977, 12992, 13056, 13312, 19893, 19904, 19968, 40908, 40960, 40981,
+ 40982, 42128, 42192, 42232, 42238, 42240, 42508, 42509, 42512, 42528,
+ 42538, 42560, 42606, 42607, 42608, 42611, 42612, 42622, 42623, 42624,
+ 42655, 42656, 42726, 42736, 42738, 42752, 42775, 42784, 42786, 42800,
+ 42802, 42864, 42865, 42873, 42878, 42888, 42889, 42891, 42896, 42912,
+ 43000, 43002, 43003, 43010, 43011, 43014, 43015, 43019, 43020, 43043,
+ 43045, 43047, 43048, 43056, 43062, 43064, 43065, 43072, 43124, 43136,
+ 43138, 43188, 43204, 43214, 43216, 43232, 43250, 43256, 43259, 43264,
+ 43274, 43302, 43310, 43312, 43335, 43346, 43359, 43360, 43392, 43395,
+ 43396, 43443, 43444, 43446, 43450, 43452, 43453, 43457, 43471, 43472,
+ 43486, 43520, 43561, 43567, 43569, 43571, 43573, 43584, 43587, 43588,
+ 43596, 43597, 43600, 43612, 43616, 43632, 43633, 43639, 43642, 43643,
+ 43648, 43696, 43697, 43698, 43701, 43703, 43705, 43710, 43712, 43713,
+ 43714, 43739, 43741, 43742, 43744, 43755, 43756, 43758, 43760, 43762,
+ 43763, 43765, 43766, 43777, 43785, 43793, 43808, 43816, 43968, 44003,
+ 44005, 44006, 44008, 44009, 44011, 44012, 44013, 44016, 44032, 55203,
+ 55216, 55243, 55296, 56191, 56319, 57343, 57344, 63743, 63744, 64112,
+ 64256, 64275, 64285, 64286, 64287, 64297, 64298, 64312, 64318, 64320,
+ 64323, 64326, 64434, 64467, 64830, 64831, 64848, 64914, 65008, 65020,
+ 65021, 65024, 65040, 65047, 65048, 65049, 65056, 65072, 65073, 65075,
+ 65077, 65078, 65079, 65080, 65081, 65082, 65083, 65084, 65085, 65086,
+ 65087, 65088, 65089, 65090, 65091, 65092, 65093, 65095, 65096, 65097,
+ 65101, 65104, 65108, 65112, 65113, 65114, 65115, 65116, 65117, 65118,
+ 65119, 65122, 65123, 65124, 65128, 65129, 65130, 65136, 65142, 65279,
+ 65281, 65284, 65285, 65288, 65289, 65290, 65291, 65292, 65293, 65294,
+ 65296, 65306, 65308, 65311, 65313, 65339, 65340, 65341, 65342, 65343,
+ 65344, 65345, 65371, 65372, 65373, 65374, 65375, 65376, 65377, 65378,
+ 65379, 65380, 65382, 65392, 65393, 65438, 65440, 65474, 65482, 65490,
+ 65498, 65504, 65506, 65507, 65508, 65509, 65512, 65513, 65517, 65529,
+ 65532, 0, 13, 40, 60, 63, 80, 128, 256, 263,
+ 311, 320, 373, 377, 394, 400, 464, 509, 640, 672,
+ 768, 800, 816, 833, 834, 842, 896, 927, 928, 968,
+ 976, 977, 1024, 1064, 1104, 1184, 2048, 2056, 2058, 2103,
+ 2108, 2111, 2135, 2136, 2304, 2326, 2335, 2336, 2367, 2432,
+ 2494, 2560, 2561, 2565, 2572, 2576, 2581, 2585, 2616, 2623,
+ 2624, 2640, 2656, 2685, 2687, 2816, 2873, 2880, 2904, 2912,
+ 2936, 3072, 3680, 4096, 4097, 4098, 4099, 4152, 4167, 4178,
+ 4198, 4224, 4226, 4227, 4272, 4275, 4279, 4281, 4283, 4285,
+ 4286, 4304, 4336, 4352, 4355, 4391, 4396, 4397, 4406, 4416,
+ 4480, 4482, 4483, 4531, 4534, 4543, 4545, 4549, 4560, 5760,
+ 5803, 5804, 5805, 5806, 5808, 5814, 5815, 5824, 8192, 9216,
+ 9328, 12288, 26624, 28416, 28496, 28497, 28559, 28563, 45056, 53248,
+ 53504, 53545, 53605, 53607, 53610, 53613, 53619, 53627, 53635, 53637,
+ 53644, 53674, 53678, 53760, 53826, 53829, 54016, 54112, 54272, 54298,
+ 54324, 54350, 54358, 54376, 54402, 54428, 54430, 54434, 54437, 54441,
+ 54446, 54454, 54459, 54461, 54469, 54480, 54506, 54532, 54535, 54541,
+ 54550, 54558, 54584, 54587, 54592, 54598, 54602, 54610, 54636, 54662,
+ 54688, 54714, 54740, 54766, 54792, 54818, 54844, 54870, 54896, 54922,
+ 54952, 54977, 54978, 55003, 55004, 55010, 55035, 55036, 55061, 55062,
+ 55068, 55093, 55094, 55119, 55120, 55126, 55151, 55152, 55177, 55178,
+ 55184, 55209, 55210, 55235, 55236, 55242, 55246, 60928, 60933, 60961,
+ 60964, 60967, 60969, 60980, 60985, 60987, 60994, 60999, 61001, 61003,
+ 61005, 61009, 61012, 61015, 61017, 61019, 61021, 61023, 61025, 61028,
+ 61031, 61036, 61044, 61049, 61054, 61056, 61067, 61089, 61093, 61099,
+ 61168, 61440, 61488, 61600, 61617, 61633, 61649, 61696, 61712, 61744,
+ 61808, 61926, 61968, 62016, 62032, 62208, 62256, 62263, 62336, 62368,
+ 62406, 62432, 62464, 62528, 62530, 62713, 62720, 62784, 62800, 62971,
+ 63045, 63104, 63232, 0, 42710, 42752, 46900, 46912, 47133, 63488,
+ 1, 32, 256, 0, 65533,
};
static u16 aFts5UnicodeData[] = {
- 1025, 61, 117, 55, 117, 54, 50, 53, 57, 53,
- 49, 85, 333, 85, 121, 85, 841, 54, 53, 50,
- 56, 48, 56, 837, 54, 57, 50, 57, 1057, 61,
- 53, 151, 58, 53, 56, 58, 39, 52, 57, 34,
- 58, 56, 58, 57, 79, 56, 37, 85, 56, 47,
- 39, 51, 111, 53, 745, 57, 233, 773, 57, 261,
- 1822, 37, 542, 37, 1534, 222, 69, 73, 37, 126,
- 126, 73, 69, 137, 37, 73, 37, 105, 101, 73,
- 37, 73, 37, 190, 158, 37, 126, 126, 73, 37,
- 126, 94, 37, 39, 94, 69, 135, 41, 40, 37,
- 41, 40, 37, 41, 40, 37, 542, 37, 606, 37,
- 41, 40, 37, 126, 73, 37, 1886, 197, 73, 37,
- 73, 69, 126, 105, 37, 286, 2181, 39, 869, 582,
- 152, 390, 472, 166, 248, 38, 56, 38, 568, 3596,
- 158, 38, 56, 94, 38, 101, 53, 88, 41, 53,
- 105, 41, 73, 37, 553, 297, 1125, 94, 37, 105,
- 101, 798, 133, 94, 57, 126, 94, 37, 1641, 1541,
- 1118, 58, 172, 75, 1790, 478, 37, 2846, 1225, 38,
- 213, 1253, 53, 49, 55, 1452, 49, 44, 53, 76,
- 53, 76, 53, 44, 871, 103, 85, 162, 121, 85,
- 55, 85, 90, 364, 53, 85, 1031, 38, 327, 684,
- 333, 149, 71, 44, 3175, 53, 39, 236, 34, 58,
- 204, 70, 76, 58, 140, 71, 333, 103, 90, 39,
- 469, 34, 39, 44, 967, 876, 2855, 364, 39, 333,
- 1063, 300, 70, 58, 117, 38, 711, 140, 38, 300,
- 38, 108, 38, 172, 501, 807, 108, 53, 39, 359,
- 876, 108, 42, 1735, 44, 42, 44, 39, 106, 268,
- 138, 44, 74, 39, 236, 327, 76, 85, 333, 53,
- 38, 199, 231, 44, 74, 263, 71, 711, 231, 39,
- 135, 44, 39, 106, 140, 74, 74, 44, 39, 42,
- 71, 103, 76, 333, 71, 87, 207, 58, 55, 76,
- 42, 199, 71, 711, 231, 71, 71, 71, 44, 106,
- 76, 76, 108, 44, 135, 39, 333, 76, 103, 44,
- 76, 42, 295, 103, 711, 231, 71, 167, 44, 39,
- 106, 172, 76, 42, 74, 44, 39, 71, 76, 333,
- 53, 55, 44, 74, 263, 71, 711, 231, 71, 167,
- 44, 39, 42, 44, 42, 140, 74, 74, 44, 44,
- 42, 71, 103, 76, 333, 58, 39, 207, 44, 39,
- 199, 103, 135, 71, 39, 71, 71, 103, 391, 74,
- 44, 74, 106, 106, 44, 39, 42, 333, 111, 218,
- 55, 58, 106, 263, 103, 743, 327, 167, 39, 108,
- 138, 108, 140, 76, 71, 71, 76, 333, 239, 58,
- 74, 263, 103, 743, 327, 167, 44, 39, 42, 44,
- 170, 44, 74, 74, 76, 74, 39, 71, 76, 333,
- 71, 74, 263, 103, 1319, 39, 106, 140, 106, 106,
- 44, 39, 42, 71, 76, 333, 207, 58, 199, 74,
- 583, 775, 295, 39, 231, 44, 106, 108, 44, 266,
- 74, 53, 1543, 44, 71, 236, 55, 199, 38, 268,
- 53, 333, 85, 71, 39, 71, 39, 39, 135, 231,
- 103, 39, 39, 71, 135, 44, 71, 204, 76, 39,
- 167, 38, 204, 333, 135, 39, 122, 501, 58, 53,
- 122, 76, 218, 333, 335, 58, 44, 58, 44, 58,
- 44, 54, 50, 54, 50, 74, 263, 1159, 460, 42,
- 172, 53, 76, 167, 364, 1164, 282, 44, 218, 90,
- 181, 154, 85, 1383, 74, 140, 42, 204, 42, 76,
- 74, 76, 39, 333, 213, 199, 74, 76, 135, 108,
- 39, 106, 71, 234, 103, 140, 423, 44, 74, 76,
- 202, 44, 39, 42, 333, 106, 44, 90, 1225, 41,
- 41, 1383, 53, 38, 10631, 135, 231, 39, 135, 1319,
- 135, 1063, 135, 231, 39, 135, 487, 1831, 135, 2151,
- 108, 309, 655, 519, 346, 2727, 49, 19847, 85, 551,
- 61, 839, 54, 50, 2407, 117, 110, 423, 135, 108,
- 583, 108, 85, 583, 76, 423, 103, 76, 1671, 76,
- 42, 236, 266, 44, 74, 364, 117, 38, 117, 55,
- 39, 44, 333, 335, 213, 49, 149, 108, 61, 333,
- 1127, 38, 1671, 1319, 44, 39, 2247, 935, 108, 138,
- 76, 106, 74, 44, 202, 108, 58, 85, 333, 967,
- 167, 1415, 554, 231, 74, 333, 47, 1114, 743, 76,
- 106, 85, 1703, 42, 44, 42, 236, 44, 42, 44,
- 74, 268, 202, 332, 44, 333, 333, 245, 38, 213,
- 140, 42, 1511, 44, 42, 172, 42, 44, 170, 44,
- 74, 231, 333, 245, 346, 300, 314, 76, 42, 967,
- 42, 140, 74, 76, 42, 44, 74, 71, 333, 1415,
- 44, 42, 76, 106, 44, 42, 108, 74, 149, 1159,
- 266, 268, 74, 76, 181, 333, 103, 333, 967, 198,
- 85, 277, 108, 53, 428, 42, 236, 135, 44, 135,
- 74, 44, 71, 1413, 2022, 421, 38, 1093, 1190, 1260,
- 140, 4830, 261, 3166, 261, 265, 197, 201, 261, 265,
- 261, 265, 197, 201, 261, 41, 41, 41, 94, 229,
- 265, 453, 261, 264, 261, 264, 261, 264, 165, 69,
- 137, 40, 56, 37, 120, 101, 69, 137, 40, 120,
- 133, 69, 137, 120, 261, 169, 120, 101, 69, 137,
- 40, 88, 381, 162, 209, 85, 52, 51, 54, 84,
- 51, 54, 52, 277, 59, 60, 162, 61, 309, 52,
- 51, 149, 80, 117, 57, 54, 50, 373, 57, 53,
- 48, 341, 61, 162, 194, 47, 38, 207, 121, 54,
- 50, 38, 335, 121, 54, 50, 422, 855, 428, 139,
- 44, 107, 396, 90, 41, 154, 41, 90, 37, 105,
- 69, 105, 37, 58, 41, 90, 57, 169, 218, 41,
- 58, 41, 58, 41, 58, 137, 58, 37, 137, 37,
- 135, 37, 90, 69, 73, 185, 94, 101, 58, 57,
- 90, 37, 58, 527, 1134, 94, 142, 47, 185, 186,
- 89, 154, 57, 90, 57, 90, 57, 250, 57, 1018,
- 89, 90, 57, 58, 57, 1018, 8601, 282, 153, 666,
- 89, 250, 54, 50, 2618, 57, 986, 825, 1306, 217,
- 602, 1274, 378, 1935, 2522, 719, 5882, 57, 314, 57,
- 1754, 281, 3578, 57, 4634, 3322, 54, 50, 54, 50,
- 54, 50, 54, 50, 54, 50, 54, 50, 54, 50,
- 975, 1434, 185, 54, 50, 1017, 54, 50, 54, 50,
- 54, 50, 54, 50, 54, 50, 537, 8218, 4217, 54,
- 50, 54, 50, 54, 50, 54, 50, 54, 50, 54,
- 50, 54, 50, 54, 50, 54, 50, 54, 50, 54,
- 50, 2041, 54, 50, 54, 50, 1049, 54, 50, 8281,
- 1562, 697, 90, 217, 346, 1513, 1509, 126, 73, 69,
- 254, 105, 37, 94, 37, 94, 165, 70, 105, 37,
- 3166, 37, 218, 158, 108, 94, 149, 47, 85, 1221,
- 37, 37, 1799, 38, 53, 44, 743, 231, 231, 231,
- 231, 231, 231, 231, 231, 1036, 85, 52, 51, 52,
- 51, 117, 52, 51, 53, 52, 51, 309, 49, 85,
- 49, 53, 52, 51, 85, 52, 51, 54, 50, 54,
- 50, 54, 50, 54, 50, 181, 38, 341, 81, 858,
- 2874, 6874, 410, 61, 117, 58, 38, 39, 46, 54,
- 50, 54, 50, 54, 50, 54, 50, 54, 50, 90,
- 54, 50, 54, 50, 54, 50, 54, 50, 49, 54,
- 82, 58, 302, 140, 74, 49, 166, 90, 110, 38,
- 39, 53, 90, 2759, 76, 88, 70, 39, 49, 2887,
- 53, 102, 39, 1319, 3015, 90, 143, 346, 871, 1178,
- 519, 1018, 335, 986, 271, 58, 495, 1050, 335, 1274,
- 495, 2042, 8218, 39, 39, 2074, 39, 39, 679, 38,
- 36583, 1786, 1287, 198, 85, 8583, 38, 117, 519, 333,
- 71, 1502, 39, 44, 107, 53, 332, 53, 38, 798,
- 44, 2247, 334, 76, 213, 760, 294, 88, 478, 69,
- 2014, 38, 261, 190, 350, 38, 88, 158, 158, 382,
- 70, 37, 231, 44, 103, 44, 135, 44, 743, 74,
- 76, 42, 154, 207, 90, 55, 58, 1671, 149, 74,
- 1607, 522, 44, 85, 333, 588, 199, 117, 39, 333,
- 903, 268, 85, 743, 364, 74, 53, 935, 108, 42,
- 1511, 44, 74, 140, 74, 44, 138, 437, 38, 333,
- 85, 1319, 204, 74, 76, 74, 76, 103, 44, 263,
- 44, 42, 333, 149, 519, 38, 199, 122, 39, 42,
- 1543, 44, 39, 108, 71, 76, 167, 76, 39, 44,
- 39, 71, 38, 85, 359, 42, 76, 74, 85, 39,
- 70, 42, 44, 199, 199, 199, 231, 231, 1127, 74,
- 44, 74, 44, 74, 53, 42, 44, 333, 39, 39,
- 743, 1575, 36, 68, 68, 36, 63, 63, 11719, 3399,
- 229, 165, 39, 44, 327, 57, 423, 167, 39, 71,
- 71, 3463, 536, 11623, 54, 50, 2055, 1735, 391, 55,
- 58, 524, 245, 54, 50, 53, 236, 53, 81, 80,
- 54, 50, 54, 50, 54, 50, 54, 50, 54, 50,
- 54, 50, 54, 50, 54, 50, 85, 54, 50, 149,
- 112, 117, 149, 49, 54, 50, 54, 50, 54, 50,
- 117, 57, 49, 121, 53, 55, 85, 167, 4327, 34,
- 117, 55, 117, 54, 50, 53, 57, 53, 49, 85,
- 333, 85, 121, 85, 841, 54, 53, 50, 56, 48,
- 56, 837, 54, 57, 50, 57, 54, 50, 53, 54,
- 50, 85, 327, 38, 1447, 70, 999, 199, 199, 199,
- 103, 87, 57, 56, 58, 87, 58, 153, 90, 98,
- 90, 391, 839, 615, 71, 487, 455, 3943, 117, 1455,
- 314, 1710, 143, 570, 47, 410, 1466, 44, 935, 1575,
- 999, 143, 551, 46, 263, 46, 967, 53, 1159, 263,
- 53, 174, 1289, 1285, 2503, 333, 199, 39, 1415, 71,
- 39, 743, 53, 271, 711, 207, 53, 839, 53, 1799,
- 71, 39, 108, 76, 140, 135, 103, 871, 108, 44,
- 271, 309, 935, 79, 53, 1735, 245, 711, 271, 615,
- 271, 2343, 1007, 42, 44, 42, 1703, 492, 245, 655,
- 333, 76, 42, 1447, 106, 140, 74, 76, 85, 34,
- 149, 807, 333, 108, 1159, 172, 42, 268, 333, 149,
- 76, 42, 1543, 106, 300, 74, 135, 149, 333, 1383,
- 44, 42, 44, 74, 204, 42, 44, 333, 28135, 3182,
- 149, 34279, 18215, 2215, 39, 1482, 140, 422, 71, 7898,
- 1274, 1946, 74, 108, 122, 202, 258, 268, 90, 236,
- 986, 140, 1562, 2138, 108, 58, 2810, 591, 841, 837,
- 841, 229, 581, 841, 837, 41, 73, 41, 73, 137,
- 265, 133, 37, 229, 357, 841, 837, 73, 137, 265,
- 233, 837, 73, 137, 169, 41, 233, 837, 841, 837,
- 841, 837, 841, 837, 841, 837, 841, 837, 841, 901,
- 809, 57, 805, 57, 197, 809, 57, 805, 57, 197,
- 809, 57, 805, 57, 197, 809, 57, 805, 57, 197,
- 809, 57, 805, 57, 197, 94, 1613, 135, 871, 71,
- 39, 39, 327, 135, 39, 39, 39, 39, 39, 39,
- 103, 71, 39, 39, 39, 39, 39, 39, 71, 39,
- 135, 231, 135, 135, 39, 327, 551, 103, 167, 551,
- 89, 1434, 3226, 506, 474, 506, 506, 367, 1018, 1946,
- 1402, 954, 1402, 314, 90, 1082, 218, 2266, 666, 1210,
- 186, 570, 2042, 58, 5850, 154, 2010, 154, 794, 2266,
- 378, 2266, 3738, 39, 39, 39, 39, 39, 39, 17351,
- 34, 3074, 7692, 63, 63,
+ 1025, 61, 117, 55, 117, 54, 50, 53, 57, 53,
+ 49, 85, 333, 85, 121, 85, 841, 54, 53, 50,
+ 56, 48, 56, 837, 54, 57, 50, 57, 1057, 61,
+ 53, 151, 58, 53, 56, 58, 39, 52, 57, 34,
+ 58, 56, 58, 57, 79, 56, 37, 85, 56, 47,
+ 39, 51, 111, 53, 745, 57, 233, 773, 57, 261,
+ 1822, 37, 542, 37, 1534, 222, 69, 73, 37, 126,
+ 126, 73, 69, 137, 37, 73, 37, 105, 101, 73,
+ 37, 73, 37, 190, 158, 37, 126, 126, 73, 37,
+ 126, 94, 37, 39, 94, 69, 135, 41, 40, 37,
+ 41, 40, 37, 41, 40, 37, 542, 37, 606, 37,
+ 41, 40, 37, 126, 73, 37, 1886, 197, 73, 37,
+ 73, 69, 126, 105, 37, 286, 2181, 39, 869, 582,
+ 152, 390, 472, 166, 248, 38, 56, 38, 568, 3596,
+ 158, 38, 56, 94, 38, 101, 53, 88, 41, 53,
+ 105, 41, 73, 37, 553, 297, 1125, 94, 37, 105,
+ 101, 798, 133, 94, 57, 126, 94, 37, 1641, 1541,
+ 1118, 58, 172, 75, 1790, 478, 37, 2846, 1225, 38,
+ 213, 1253, 53, 49, 55, 1452, 49, 44, 53, 76,
+ 53, 76, 53, 44, 871, 103, 85, 162, 121, 85,
+ 55, 85, 90, 364, 53, 85, 1031, 38, 327, 684,
+ 333, 149, 71, 44, 3175, 53, 39, 236, 34, 58,
+ 204, 70, 76, 58, 140, 71, 333, 103, 90, 39,
+ 469, 34, 39, 44, 967, 876, 2855, 364, 39, 333,
+ 1063, 300, 70, 58, 117, 38, 711, 140, 38, 300,
+ 38, 108, 38, 172, 501, 807, 108, 53, 39, 359,
+ 876, 108, 42, 1735, 44, 42, 44, 39, 106, 268,
+ 138, 44, 74, 39, 236, 327, 76, 85, 333, 53,
+ 38, 199, 231, 44, 74, 263, 71, 711, 231, 39,
+ 135, 44, 39, 106, 140, 74, 74, 44, 39, 42,
+ 71, 103, 76, 333, 71, 87, 207, 58, 55, 76,
+ 42, 199, 71, 711, 231, 71, 71, 71, 44, 106,
+ 76, 76, 108, 44, 135, 39, 333, 76, 103, 44,
+ 76, 42, 295, 103, 711, 231, 71, 167, 44, 39,
+ 106, 172, 76, 42, 74, 44, 39, 71, 76, 333,
+ 53, 55, 44, 74, 263, 71, 711, 231, 71, 167,
+ 44, 39, 42, 44, 42, 140, 74, 74, 44, 44,
+ 42, 71, 103, 76, 333, 58, 39, 207, 44, 39,
+ 199, 103, 135, 71, 39, 71, 71, 103, 391, 74,
+ 44, 74, 106, 106, 44, 39, 42, 333, 111, 218,
+ 55, 58, 106, 263, 103, 743, 327, 167, 39, 108,
+ 138, 108, 140, 76, 71, 71, 76, 333, 239, 58,
+ 74, 263, 103, 743, 327, 167, 44, 39, 42, 44,
+ 170, 44, 74, 74, 76, 74, 39, 71, 76, 333,
+ 71, 74, 263, 103, 1319, 39, 106, 140, 106, 106,
+ 44, 39, 42, 71, 76, 333, 207, 58, 199, 74,
+ 583, 775, 295, 39, 231, 44, 106, 108, 44, 266,
+ 74, 53, 1543, 44, 71, 236, 55, 199, 38, 268,
+ 53, 333, 85, 71, 39, 71, 39, 39, 135, 231,
+ 103, 39, 39, 71, 135, 44, 71, 204, 76, 39,
+ 167, 38, 204, 333, 135, 39, 122, 501, 58, 53,
+ 122, 76, 218, 333, 335, 58, 44, 58, 44, 58,
+ 44, 54, 50, 54, 50, 74, 263, 1159, 460, 42,
+ 172, 53, 76, 167, 364, 1164, 282, 44, 218, 90,
+ 181, 154, 85, 1383, 74, 140, 42, 204, 42, 76,
+ 74, 76, 39, 333, 213, 199, 74, 76, 135, 108,
+ 39, 106, 71, 234, 103, 140, 423, 44, 74, 76,
+ 202, 44, 39, 42, 333, 106, 44, 90, 1225, 41,
+ 41, 1383, 53, 38, 10631, 135, 231, 39, 135, 1319,
+ 135, 1063, 135, 231, 39, 135, 487, 1831, 135, 2151,
+ 108, 309, 655, 519, 346, 2727, 49, 19847, 85, 551,
+ 61, 839, 54, 50, 2407, 117, 110, 423, 135, 108,
+ 583, 108, 85, 583, 76, 423, 103, 76, 1671, 76,
+ 42, 236, 266, 44, 74, 364, 117, 38, 117, 55,
+ 39, 44, 333, 335, 213, 49, 149, 108, 61, 333,
+ 1127, 38, 1671, 1319, 44, 39, 2247, 935, 108, 138,
+ 76, 106, 74, 44, 202, 108, 58, 85, 333, 967,
+ 167, 1415, 554, 231, 74, 333, 47, 1114, 743, 76,
+ 106, 85, 1703, 42, 44, 42, 236, 44, 42, 44,
+ 74, 268, 202, 332, 44, 333, 333, 245, 38, 213,
+ 140, 42, 1511, 44, 42, 172, 42, 44, 170, 44,
+ 74, 231, 333, 245, 346, 300, 314, 76, 42, 967,
+ 42, 140, 74, 76, 42, 44, 74, 71, 333, 1415,
+ 44, 42, 76, 106, 44, 42, 108, 74, 149, 1159,
+ 266, 268, 74, 76, 181, 333, 103, 333, 967, 198,
+ 85, 277, 108, 53, 428, 42, 236, 135, 44, 135,
+ 74, 44, 71, 1413, 2022, 421, 38, 1093, 1190, 1260,
+ 140, 4830, 261, 3166, 261, 265, 197, 201, 261, 265,
+ 261, 265, 197, 201, 261, 41, 41, 41, 94, 229,
+ 265, 453, 261, 264, 261, 264, 261, 264, 165, 69,
+ 137, 40, 56, 37, 120, 101, 69, 137, 40, 120,
+ 133, 69, 137, 120, 261, 169, 120, 101, 69, 137,
+ 40, 88, 381, 162, 209, 85, 52, 51, 54, 84,
+ 51, 54, 52, 277, 59, 60, 162, 61, 309, 52,
+ 51, 149, 80, 117, 57, 54, 50, 373, 57, 53,
+ 48, 341, 61, 162, 194, 47, 38, 207, 121, 54,
+ 50, 38, 335, 121, 54, 50, 422, 855, 428, 139,
+ 44, 107, 396, 90, 41, 154, 41, 90, 37, 105,
+ 69, 105, 37, 58, 41, 90, 57, 169, 218, 41,
+ 58, 41, 58, 41, 58, 137, 58, 37, 137, 37,
+ 135, 37, 90, 69, 73, 185, 94, 101, 58, 57,
+ 90, 37, 58, 527, 1134, 94, 142, 47, 185, 186,
+ 89, 154, 57, 90, 57, 90, 57, 250, 57, 1018,
+ 89, 90, 57, 58, 57, 1018, 8601, 282, 153, 666,
+ 89, 250, 54, 50, 2618, 57, 986, 825, 1306, 217,
+ 602, 1274, 378, 1935, 2522, 719, 5882, 57, 314, 57,
+ 1754, 281, 3578, 57, 4634, 3322, 54, 50, 54, 50,
+ 54, 50, 54, 50, 54, 50, 54, 50, 54, 50,
+ 975, 1434, 185, 54, 50, 1017, 54, 50, 54, 50,
+ 54, 50, 54, 50, 54, 50, 537, 8218, 4217, 54,
+ 50, 54, 50, 54, 50, 54, 50, 54, 50, 54,
+ 50, 54, 50, 54, 50, 54, 50, 54, 50, 54,
+ 50, 2041, 54, 50, 54, 50, 1049, 54, 50, 8281,
+ 1562, 697, 90, 217, 346, 1513, 1509, 126, 73, 69,
+ 254, 105, 37, 94, 37, 94, 165, 70, 105, 37,
+ 3166, 37, 218, 158, 108, 94, 149, 47, 85, 1221,
+ 37, 37, 1799, 38, 53, 44, 743, 231, 231, 231,
+ 231, 231, 231, 231, 231, 1036, 85, 52, 51, 52,
+ 51, 117, 52, 51, 53, 52, 51, 309, 49, 85,
+ 49, 53, 52, 51, 85, 52, 51, 54, 50, 54,
+ 50, 54, 50, 54, 50, 181, 38, 341, 81, 858,
+ 2874, 6874, 410, 61, 117, 58, 38, 39, 46, 54,
+ 50, 54, 50, 54, 50, 54, 50, 54, 50, 90,
+ 54, 50, 54, 50, 54, 50, 54, 50, 49, 54,
+ 82, 58, 302, 140, 74, 49, 166, 90, 110, 38,
+ 39, 53, 90, 2759, 76, 88, 70, 39, 49, 2887,
+ 53, 102, 39, 1319, 3015, 90, 143, 346, 871, 1178,
+ 519, 1018, 335, 986, 271, 58, 495, 1050, 335, 1274,
+ 495, 2042, 8218, 39, 39, 2074, 39, 39, 679, 38,
+ 36583, 1786, 1287, 198, 85, 8583, 38, 117, 519, 333,
+ 71, 1502, 39, 44, 107, 53, 332, 53, 38, 798,
+ 44, 2247, 334, 76, 213, 760, 294, 88, 478, 69,
+ 2014, 38, 261, 190, 350, 38, 88, 158, 158, 382,
+ 70, 37, 231, 44, 103, 44, 135, 44, 743, 74,
+ 76, 42, 154, 207, 90, 55, 58, 1671, 149, 74,
+ 1607, 522, 44, 85, 333, 588, 199, 117, 39, 333,
+ 903, 268, 85, 743, 364, 74, 53, 935, 108, 42,
+ 1511, 44, 74, 140, 74, 44, 138, 437, 38, 333,
+ 85, 1319, 204, 74, 76, 74, 76, 103, 44, 263,
+ 44, 42, 333, 149, 519, 38, 199, 122, 39, 42,
+ 1543, 44, 39, 108, 71, 76, 167, 76, 39, 44,
+ 39, 71, 38, 85, 359, 42, 76, 74, 85, 39,
+ 70, 42, 44, 199, 199, 199, 231, 231, 1127, 74,
+ 44, 74, 44, 74, 53, 42, 44, 333, 39, 39,
+ 743, 1575, 36, 68, 68, 36, 63, 63, 11719, 3399,
+ 229, 165, 39, 44, 327, 57, 423, 167, 39, 71,
+ 71, 3463, 536, 11623, 54, 50, 2055, 1735, 391, 55,
+ 58, 524, 245, 54, 50, 53, 236, 53, 81, 80,
+ 54, 50, 54, 50, 54, 50, 54, 50, 54, 50,
+ 54, 50, 54, 50, 54, 50, 85, 54, 50, 149,
+ 112, 117, 149, 49, 54, 50, 54, 50, 54, 50,
+ 117, 57, 49, 121, 53, 55, 85, 167, 4327, 34,
+ 117, 55, 117, 54, 50, 53, 57, 53, 49, 85,
+ 333, 85, 121, 85, 841, 54, 53, 50, 56, 48,
+ 56, 837, 54, 57, 50, 57, 54, 50, 53, 54,
+ 50, 85, 327, 38, 1447, 70, 999, 199, 199, 199,
+ 103, 87, 57, 56, 58, 87, 58, 153, 90, 98,
+ 90, 391, 839, 615, 71, 487, 455, 3943, 117, 1455,
+ 314, 1710, 143, 570, 47, 410, 1466, 44, 935, 1575,
+ 999, 143, 551, 46, 263, 46, 967, 53, 1159, 263,
+ 53, 174, 1289, 1285, 2503, 333, 199, 39, 1415, 71,
+ 39, 743, 53, 271, 711, 207, 53, 839, 53, 1799,
+ 71, 39, 108, 76, 140, 135, 103, 871, 108, 44,
+ 271, 309, 935, 79, 53, 1735, 245, 711, 271, 615,
+ 271, 2343, 1007, 42, 44, 42, 1703, 492, 245, 655,
+ 333, 76, 42, 1447, 106, 140, 74, 76, 85, 34,
+ 149, 807, 333, 108, 1159, 172, 42, 268, 333, 149,
+ 76, 42, 1543, 106, 300, 74, 135, 149, 333, 1383,
+ 44, 42, 44, 74, 204, 42, 44, 333, 28135, 3182,
+ 149, 34279, 18215, 2215, 39, 1482, 140, 422, 71, 7898,
+ 1274, 1946, 74, 108, 122, 202, 258, 268, 90, 236,
+ 986, 140, 1562, 2138, 108, 58, 2810, 591, 841, 837,
+ 841, 229, 581, 841, 837, 41, 73, 41, 73, 137,
+ 265, 133, 37, 229, 357, 841, 837, 73, 137, 265,
+ 233, 837, 73, 137, 169, 41, 233, 837, 841, 837,
+ 841, 837, 841, 837, 841, 837, 841, 837, 841, 901,
+ 809, 57, 805, 57, 197, 809, 57, 805, 57, 197,
+ 809, 57, 805, 57, 197, 809, 57, 805, 57, 197,
+ 809, 57, 805, 57, 197, 94, 1613, 135, 871, 71,
+ 39, 39, 327, 135, 39, 39, 39, 39, 39, 39,
+ 103, 71, 39, 39, 39, 39, 39, 39, 71, 39,
+ 135, 231, 135, 135, 39, 327, 551, 103, 167, 551,
+ 89, 1434, 3226, 506, 474, 506, 506, 367, 1018, 1946,
+ 1402, 954, 1402, 314, 90, 1082, 218, 2266, 666, 1210,
+ 186, 570, 2042, 58, 5850, 154, 2010, 154, 794, 2266,
+ 378, 2266, 3738, 39, 39, 39, 39, 39, 39, 17351,
+ 34, 3074, 7692, 63, 63,
};
-static int sqlite3Fts5UnicodeCategory(int iCode) {
+static int sqlite3Fts5UnicodeCategory(u32 iCode) {
int iRes = -1;
int iHi;
int iLo;
@@ -219102,6 +254178,7 @@ static void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){
}
iTbl++;
}
+ aAscii[0] = 0; /* 0x00 is never a token character */
}
@@ -219183,7 +254260,7 @@ static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v){
u8 n;
p -= 2;
n = sqlite3Fts5GetVarint(p, &v64);
- *v = (u32)v64;
+ *v = ((u32)v64) & 0x7FFFFFFF;
assert( n>3 && n<=9 );
return n;
}
@@ -219411,7 +254488,7 @@ static int FTS5_NOINLINE fts5PutVarint64(unsigned char *p, u64 v){
v >>= 7;
}
return 9;
- }
+ }
n = 0;
do{
buf[n++] = (u8)((v & 0x7f) | 0x80);
@@ -219450,7 +254527,6 @@ static int sqlite3Fts5GetVarintLen(u32 iVal){
return 5;
}
-
/*
** 2015 May 08
**
@@ -219464,7 +254540,7 @@ static int sqlite3Fts5GetVarintLen(u32 iVal){
******************************************************************************
**
** This is an SQLite virtual table module implementing direct access to an
-** existing FTS5 index. The module may create several different types of
+** existing FTS5 index. The module may create several different types of
** tables:
**
** col:
@@ -219472,21 +254548,21 @@ static int sqlite3Fts5GetVarintLen(u32 iVal){
**
** One row for each term/column combination. The value of $doc is set to
** the number of fts5 rows that contain at least one instance of term
-** $term within column $col. Field $cnt is set to the total number of
-** instances of term $term in column $col (in any row of the fts5 table).
+** $term within column $col. Field $cnt is set to the total number of
+** instances of term $term in column $col (in any row of the fts5 table).
**
** row:
** CREATE TABLE vocab(term, doc, cnt, PRIMARY KEY(term));
**
** One row for each term in the database. The value of $doc is set to
** the number of fts5 rows that contain at least one instance of term
-** $term. Field $cnt is set to the total number of instances of term
+** $term. Field $cnt is set to the total number of instances of term
** $term in the database.
**
** instance:
** CREATE TABLE vocab(term, doc, col, offset, PRIMARY KEY(<all-fields>));
**
-** One row for each term instance in the database.
+** One row for each term instance in the database.
*/
@@ -219503,21 +254579,22 @@ struct Fts5VocabTable {
sqlite3 *db; /* Database handle */
Fts5Global *pGlobal; /* FTS5 global object for this database */
int eType; /* FTS5_VOCAB_COL, ROW or INSTANCE */
+ unsigned bBusy; /* True if busy */
};
struct Fts5VocabCursor {
sqlite3_vtab_cursor base;
sqlite3_stmt *pStmt; /* Statement holding lock on pIndex */
- Fts5Index *pIndex; /* Associated FTS5 index */
+ Fts5Table *pFts5; /* Associated FTS5 table */
int bEof; /* True if this cursor is at EOF */
Fts5IndexIter *pIter; /* Term/rowid iterator object */
+ void *pStruct; /* From sqlite3Fts5StructureRef() */
int nLeTerm; /* Size of zLeTerm in bytes */
char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */
/* These are used by 'col' tables only */
- Fts5Config *pConfig; /* Fts5 table configuration */
int iCol;
i64 *aCnt;
i64 *aDoc;
@@ -219548,7 +254625,7 @@ struct Fts5VocabCursor {
/*
-** Translate a string containing an fts5vocab table type to an
+** Translate a string containing an fts5vocab table type to an
** FTS5_VOCAB_XXX constant. If successful, set *peType to the output
** value and return SQLITE_OK. Otherwise, set *pzErr to an error message
** and return SQLITE_ERROR.
@@ -219626,8 +254703,8 @@ static int fts5VocabInitVtab(
sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */
char **pzErr /* Write any error message here */
){
- const char *azSchema[] = {
- "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")",
+ const char *azSchema[] = {
+ "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")",
"CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")",
"CREATE TABlE vocab(" FTS5_VOCAB_INST_SCHEMA ")"
};
@@ -219646,10 +254723,10 @@ static int fts5VocabInitVtab(
const char *zDb = bDb ? argv[3] : argv[1];
const char *zTab = bDb ? argv[4] : argv[3];
const char *zType = bDb ? argv[5] : argv[4];
- int nDb = (int)strlen(zDb)+1;
+ int nDb = (int)strlen(zDb)+1;
int nTab = (int)strlen(zTab)+1;
int eType = 0;
-
+
rc = fts5VocabTableType(zType, pzErr, &eType);
if( rc==SQLITE_OK ){
assert( eType>=0 && eType<ArraySize(azSchema) );
@@ -219701,7 +254778,7 @@ static int fts5VocabCreateMethod(
return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr);
}
-/*
+/*
** Implementation of the xBestIndex method.
**
** Only constraints of the form:
@@ -219710,7 +254787,7 @@ static int fts5VocabCreateMethod(
** term == ?
** term >= ?
**
-** are interpreted. Less-than and less-than-or-equal are treated
+** are interpreted. Less-than and less-than-or-equal are treated
** identically, as are greater-than and greater-than-or-equal.
*/
static int fts5VocabBestIndexMethod(
@@ -219761,8 +254838,8 @@ static int fts5VocabBestIndexMethod(
** specifically - "ORDER BY term" or "ORDER BY term ASC" - set the
** sqlite3_index_info.orderByConsumed flag to tell the core the results
** are already in sorted order. */
- if( pInfo->nOrderBy==1
- && pInfo->aOrderBy[0].iColumn==0
+ if( pInfo->nOrderBy==1
+ && pInfo->aOrderBy[0].iColumn==0
&& pInfo->aOrderBy[0].desc==0
){
pInfo->orderByConsumed = 1;
@@ -219776,17 +254853,22 @@ static int fts5VocabBestIndexMethod(
** Implementation of xOpen method.
*/
static int fts5VocabOpenMethod(
- sqlite3_vtab *pVTab,
+ sqlite3_vtab *pVTab,
sqlite3_vtab_cursor **ppCsr
){
Fts5VocabTable *pTab = (Fts5VocabTable*)pVTab;
- Fts5Index *pIndex = 0;
- Fts5Config *pConfig = 0;
+ Fts5Table *pFts5 = 0;
Fts5VocabCursor *pCsr = 0;
int rc = SQLITE_OK;
sqlite3_stmt *pStmt = 0;
char *zSql = 0;
+ if( pTab->bBusy ){
+ pVTab->zErrMsg = sqlite3_mprintf(
+ "recursive definition for %s.%s", pTab->zFts5Db, pTab->zFts5Tbl
+ );
+ return SQLITE_ERROR;
+ }
zSql = sqlite3Fts5Mprintf(&rc,
"SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'",
pTab->zFts5Tbl, pTab->zFts5Db, pTab->zFts5Tbl, pTab->zFts5Tbl
@@ -219798,33 +254880,38 @@ static int fts5VocabOpenMethod(
assert( rc==SQLITE_OK || pStmt==0 );
if( rc==SQLITE_ERROR ) rc = SQLITE_OK;
+ pTab->bBusy = 1;
if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
i64 iId = sqlite3_column_int64(pStmt, 0);
- pIndex = sqlite3Fts5IndexFromCsrid(pTab->pGlobal, iId, &pConfig);
+ pFts5 = sqlite3Fts5TableFromCsrid(pTab->pGlobal, iId);
}
+ pTab->bBusy = 0;
- if( rc==SQLITE_OK && pIndex==0 ){
- rc = sqlite3_finalize(pStmt);
- pStmt = 0;
- if( rc==SQLITE_OK ){
- pVTab->zErrMsg = sqlite3_mprintf(
- "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl
- );
- rc = SQLITE_ERROR;
+ if( rc==SQLITE_OK ){
+ if( pFts5==0 ){
+ rc = sqlite3_finalize(pStmt);
+ pStmt = 0;
+ if( rc==SQLITE_OK ){
+ pVTab->zErrMsg = sqlite3_mprintf(
+ "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl
+ );
+ rc = SQLITE_ERROR;
+ }
+ }else{
+ rc = sqlite3Fts5FlushToDisk(pFts5);
}
}
if( rc==SQLITE_OK ){
- int nByte = pConfig->nCol * sizeof(i64) * 2 + sizeof(Fts5VocabCursor);
+ i64 nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor);
pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte);
}
if( pCsr ){
- pCsr->pIndex = pIndex;
+ pCsr->pFts5 = pFts5;
pCsr->pStmt = pStmt;
- pCsr->pConfig = pConfig;
pCsr->aCnt = (i64*)&pCsr[1];
- pCsr->aDoc = &pCsr->aCnt[pConfig->nCol];
+ pCsr->aDoc = &pCsr->aCnt[pFts5->pConfig->nCol];
}else{
sqlite3_finalize(pStmt);
}
@@ -219836,10 +254923,13 @@ static int fts5VocabOpenMethod(
static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
pCsr->rowid = 0;
sqlite3Fts5IterClose(pCsr->pIter);
+ sqlite3Fts5StructureRelease(pCsr->pStruct);
+ pCsr->pStruct = 0;
pCsr->pIter = 0;
sqlite3_free(pCsr->zLeTerm);
pCsr->nLeTerm = -1;
pCsr->zLeTerm = 0;
+ pCsr->bEof = 0;
}
/*
@@ -219857,7 +254947,7 @@ static int fts5VocabCloseMethod(sqlite3_vtab_cursor *pCursor){
static int fts5VocabInstanceNewTerm(Fts5VocabCursor *pCsr){
int rc = SQLITE_OK;
-
+
if( sqlite3Fts5IterEof(pCsr->pIter) ){
pCsr->bEof = 1;
}else{
@@ -219878,16 +254968,16 @@ static int fts5VocabInstanceNewTerm(Fts5VocabCursor *pCsr){
}
static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){
- int eDetail = pCsr->pConfig->eDetail;
+ int eDetail = pCsr->pFts5->pConfig->eDetail;
int rc = SQLITE_OK;
Fts5IndexIter *pIter = pCsr->pIter;
i64 *pp = &pCsr->iInstPos;
int *po = &pCsr->iInstOff;
-
+
assert( sqlite3Fts5IterEof(pIter)==0 );
assert( pCsr->bEof==0 );
while( eDetail==FTS5_DETAIL_NONE
- || sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp)
+ || sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp)
){
pCsr->iInstPos = 0;
pCsr->iInstOff = 0;
@@ -219912,9 +255002,11 @@ static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){
static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab;
- int rc = SQLITE_OK;
- int nCol = pCsr->pConfig->nCol;
+ int nCol = pCsr->pFts5->pConfig->nCol;
+ int rc;
+ rc = sqlite3Fts5StructureTest(pCsr->pFts5->pIndex, pCsr->pStruct);
+ if( rc!=SQLITE_OK ) return rc;
pCsr->rowid++;
if( pTab->eType==FTS5_VOCAB_INSTANCE ){
@@ -219935,6 +255027,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
int nTerm;
zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
+ assert( nTerm>=0 );
if( pCsr->nLeTerm>=0 ){
int nCmp = MIN(nTerm, pCsr->nLeTerm);
int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp);
@@ -219951,7 +255044,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW );
while( rc==SQLITE_OK ){
- int eDetail = pCsr->pConfig->eDetail;
+ int eDetail = pCsr->pFts5->pConfig->eDetail;
const u8 *pPos; int nPos; /* Position list */
i64 iPos = 0; /* 64-bit position read from poslist */
int iOff = 0; /* Current offset within position list */
@@ -219974,7 +255067,6 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
int iCol = -1;
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
int ii = FTS5_POS2COLUMN(iPos);
- pCsr->aCnt[ii]++;
if( iCol!=ii ){
if( ii>=nCol ){
rc = FTS5_CORRUPT;
@@ -219983,6 +255075,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
pCsr->aDoc[ii]++;
iCol = ii;
}
+ pCsr->aCnt[ii]++;
}
}else if( eDetail==FTS5_DETAIL_COLUMNS ){
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){
@@ -220011,7 +255104,9 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
if( rc==SQLITE_OK ){
zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
- if( nTerm!=pCsr->term.n || memcmp(zTerm, pCsr->term.p, nTerm) ){
+ if( nTerm!=pCsr->term.n
+ || (nTerm>0 && memcmp(zTerm, pCsr->term.p, nTerm))
+ ){
break;
}
if( sqlite3Fts5IterEof(pCsr->pIter) ) break;
@@ -220021,8 +255116,10 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
}
if( rc==SQLITE_OK && pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){
- while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++;
- assert( pCsr->iCol<pCsr->pConfig->nCol );
+ for(/* noop */; pCsr->iCol<nCol && pCsr->aDoc[pCsr->iCol]==0; pCsr->iCol++);
+ if( pCsr->iCol==nCol ){
+ rc = FTS5_CORRUPT;
+ }
}
return rc;
}
@@ -220061,7 +255158,7 @@ static int fts5VocabFilterMethod(
if( pEq ){
zTerm = (const char *)sqlite3_value_text(pEq);
nTerm = sqlite3_value_bytes(pEq);
- f = 0;
+ f = FTS5INDEX_QUERY_NOTOKENDATA;
}else{
if( pGe ){
zTerm = (const char *)sqlite3_value_text(pGe);
@@ -220069,6 +255166,7 @@ static int fts5VocabFilterMethod(
}
if( pLe ){
const char *zCopy = (const char *)sqlite3_value_text(pLe);
+ if( zCopy==0 ) zCopy = "";
pCsr->nLeTerm = sqlite3_value_bytes(pLe);
pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1);
if( pCsr->zLeTerm==0 ){
@@ -220080,14 +255178,18 @@ static int fts5VocabFilterMethod(
}
if( rc==SQLITE_OK ){
- rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter);
+ Fts5Index *pIndex = pCsr->pFts5->pIndex;
+ rc = sqlite3Fts5IndexQuery(pIndex, zTerm, nTerm, f, 0, &pCsr->pIter);
+ if( rc==SQLITE_OK ){
+ pCsr->pStruct = sqlite3Fts5StructureRef(pIndex);
+ }
}
if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){
rc = fts5VocabInstanceNewTerm(pCsr);
}
- if( rc==SQLITE_OK
- && !pCsr->bEof
- && (eType!=FTS5_VOCAB_INSTANCE || pCsr->pConfig->eDetail!=FTS5_DETAIL_NONE)
+ if( rc==SQLITE_OK && !pCsr->bEof
+ && (eType!=FTS5_VOCAB_INSTANCE
+ || pCsr->pFts5->pConfig->eDetail!=FTS5_DETAIL_NONE)
){
rc = fts5VocabNextMethod(pCursor);
}
@@ -220095,8 +255197,8 @@ static int fts5VocabFilterMethod(
return rc;
}
-/*
-** This is the xEof method of the virtual table. SQLite calls this
+/*
+** This is the xEof method of the virtual table. SQLite calls this
** routine to find out if it has reached the end of a result set.
*/
static int fts5VocabEofMethod(sqlite3_vtab_cursor *pCursor){
@@ -220110,7 +255212,7 @@ static int fts5VocabColumnMethod(
int iCol /* Index of column to read value from */
){
Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
- int eDetail = pCsr->pConfig->eDetail;
+ int eDetail = pCsr->pFts5->pConfig->eDetail;
int eType = ((Fts5VocabTable*)(pCursor->pVtab))->eType;
i64 iVal = 0;
@@ -220122,7 +255224,7 @@ static int fts5VocabColumnMethod(
assert( iCol==1 || iCol==2 || iCol==3 );
if( iCol==1 ){
if( eDetail!=FTS5_DETAIL_NONE ){
- const char *z = pCsr->pConfig->azCol[pCsr->iCol];
+ const char *z = pCsr->pFts5->pConfig->azCol[pCsr->iCol];
sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
}
}else if( iCol==2 ){
@@ -220150,8 +255252,8 @@ static int fts5VocabColumnMethod(
}else if( eDetail==FTS5_DETAIL_COLUMNS ){
ii = (int)pCsr->iInstPos;
}
- if( ii>=0 && ii<pCsr->pConfig->nCol ){
- const char *z = pCsr->pConfig->azCol[ii];
+ if( ii>=0 && ii<pCsr->pFts5->pConfig->nCol ){
+ const char *z = pCsr->pFts5->pConfig->azCol[ii];
sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
}
break;
@@ -220171,13 +255273,13 @@ static int fts5VocabColumnMethod(
return SQLITE_OK;
}
-/*
+/*
** This is the xRowid method. The SQLite core calls this routine to
** retrieve the rowid for the current row of the result set. The
** rowid should be written to *pRowid.
*/
static int fts5VocabRowidMethod(
- sqlite3_vtab_cursor *pCursor,
+ sqlite3_vtab_cursor *pCursor,
sqlite_int64 *pRowid
){
Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
@@ -220210,7 +255312,8 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
- /* xShadowName */ 0
+ /* xShadowName */ 0,
+ /* xIntegrity */ 0
};
void *p = (void*)pGlobal;
@@ -220218,7 +255321,7 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
}
-
+
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */
/************** End of fts5.c ************************************************/
@@ -220255,6 +255358,16 @@ SQLITE_EXTENSION_INIT1
#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+#define STMT_NUM_INTEGER_COLUMN 10
+typedef struct StmtRow StmtRow;
+struct StmtRow {
+ sqlite3_int64 iRowid; /* Rowid value */
+ char *zSql; /* column "sql" */
+ int aCol[STMT_NUM_INTEGER_COLUMN+1]; /* all other column values */
+ StmtRow *pNext; /* Next row to return */
+};
+
/* stmt_vtab is a subclass of sqlite3_vtab which will
** serve as the underlying representation of a stmt virtual table
*/
@@ -220272,8 +255385,7 @@ typedef struct stmt_cursor stmt_cursor;
struct stmt_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3 *db; /* Database connection for this cursor */
- sqlite3_stmt *pStmt; /* Statement cursor is currently pointing at */
- sqlite3_int64 iRowid; /* The rowid */
+ StmtRow *pRow; /* Current row */
};
/*
@@ -220313,11 +255425,15 @@ static int stmtConnect(
#define STMT_COLUMN_MEM 10 /* SQLITE_STMTSTATUS_MEMUSED */
+ (void)pAux;
+ (void)argc;
+ (void)argv;
+ (void)pzErr;
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(sql,ncol,ro,busy,nscan,nsort,naidx,nstep,"
"reprep,run,mem)");
if( rc==SQLITE_OK ){
- pNew = sqlite3_malloc( sizeof(*pNew) );
+ pNew = sqlite3_malloc64( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -220339,7 +255455,7 @@ static int stmtDisconnect(sqlite3_vtab *pVtab){
*/
static int stmtOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
stmt_cursor *pCur;
- pCur = sqlite3_malloc( sizeof(*pCur) );
+ pCur = sqlite3_malloc64( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->db = ((stmt_vtab*)p)->db;
@@ -220347,10 +255463,21 @@ static int stmtOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
return SQLITE_OK;
}
+static void stmtCsrReset(stmt_cursor *pCur){
+ StmtRow *pRow = 0;
+ StmtRow *pNext = 0;
+ for(pRow=pCur->pRow; pRow; pRow=pNext){
+ pNext = pRow->pNext;
+ sqlite3_free(pRow);
+ }
+ pCur->pRow = 0;
+}
+
/*
** Destructor for a stmt_cursor.
*/
static int stmtClose(sqlite3_vtab_cursor *cur){
+ stmtCsrReset((stmt_cursor*)cur);
sqlite3_free(cur);
return SQLITE_OK;
}
@@ -220361,8 +255488,9 @@ static int stmtClose(sqlite3_vtab_cursor *cur){
*/
static int stmtNext(sqlite3_vtab_cursor *cur){
stmt_cursor *pCur = (stmt_cursor*)cur;
- pCur->iRowid++;
- pCur->pStmt = sqlite3_next_stmt(pCur->db, pCur->pStmt);
+ StmtRow *pNext = pCur->pRow->pNext;
+ sqlite3_free(pCur->pRow);
+ pCur->pRow = pNext;
return SQLITE_OK;
}
@@ -220376,38 +255504,11 @@ static int stmtColumn(
int i /* Which column to return */
){
stmt_cursor *pCur = (stmt_cursor*)cur;
- switch( i ){
- case STMT_COLUMN_SQL: {
- sqlite3_result_text(ctx, sqlite3_sql(pCur->pStmt), -1, SQLITE_TRANSIENT);
- break;
- }
- case STMT_COLUMN_NCOL: {
- sqlite3_result_int(ctx, sqlite3_column_count(pCur->pStmt));
- break;
- }
- case STMT_COLUMN_RO: {
- sqlite3_result_int(ctx, sqlite3_stmt_readonly(pCur->pStmt));
- break;
- }
- case STMT_COLUMN_BUSY: {
- sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt));
- break;
- }
- case STMT_COLUMN_MEM: {
- i = SQLITE_STMTSTATUS_MEMUSED +
- STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP;
- /* Fall thru */
- }
- case STMT_COLUMN_NSCAN:
- case STMT_COLUMN_NSORT:
- case STMT_COLUMN_NAIDX:
- case STMT_COLUMN_NSTEP:
- case STMT_COLUMN_REPREP:
- case STMT_COLUMN_RUN: {
- sqlite3_result_int(ctx, sqlite3_stmt_status(pCur->pStmt,
- i-STMT_COLUMN_NSCAN+SQLITE_STMTSTATUS_FULLSCAN_STEP, 0));
- break;
- }
+ StmtRow *pRow = pCur->pRow;
+ if( i==STMT_COLUMN_SQL ){
+ sqlite3_result_text(ctx, pRow->zSql, -1, SQLITE_TRANSIENT);
+ }else{
+ sqlite3_result_int(ctx, pRow->aCol[i]);
}
return SQLITE_OK;
}
@@ -220418,7 +255519,7 @@ static int stmtColumn(
*/
static int stmtRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
stmt_cursor *pCur = (stmt_cursor*)cur;
- *pRowid = pCur->iRowid;
+ *pRowid = pCur->pRow->iRowid;
return SQLITE_OK;
}
@@ -220428,24 +255529,72 @@ static int stmtRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
*/
static int stmtEof(sqlite3_vtab_cursor *cur){
stmt_cursor *pCur = (stmt_cursor*)cur;
- return pCur->pStmt==0;
+ return pCur->pRow==0;
}
/*
** This method is called to "rewind" the stmt_cursor object back
** to the first row of output. This method is always called at least
-** once prior to any call to stmtColumn() or stmtRowid() or
+** once prior to any call to stmtColumn() or stmtRowid() or
** stmtEof().
*/
static int stmtFilter(
- sqlite3_vtab_cursor *pVtabCursor,
+ sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
stmt_cursor *pCur = (stmt_cursor *)pVtabCursor;
- pCur->pStmt = 0;
- pCur->iRowid = 0;
- return stmtNext(pVtabCursor);
+ sqlite3_stmt *p = 0;
+ sqlite3_int64 iRowid = 1;
+ StmtRow **ppRow = 0;
+
+ (void)idxNum;
+ (void)idxStr;
+ (void)argc;
+ (void)argv;
+ stmtCsrReset(pCur);
+ ppRow = &pCur->pRow;
+ for(p=sqlite3_next_stmt(pCur->db, 0); p; p=sqlite3_next_stmt(pCur->db, p)){
+ const char *zSql = sqlite3_sql(p);
+ sqlite3_int64 nSql = zSql ? strlen(zSql)+1 : 0;
+ StmtRow *pNew = (StmtRow*)sqlite3_malloc64(sizeof(StmtRow) + nSql);
+
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(StmtRow));
+ if( zSql ){
+ pNew->zSql = (char*)&pNew[1];
+ memcpy(pNew->zSql, zSql, nSql);
+ }
+ pNew->aCol[STMT_COLUMN_NCOL] = sqlite3_column_count(p);
+ pNew->aCol[STMT_COLUMN_RO] = sqlite3_stmt_readonly(p);
+ pNew->aCol[STMT_COLUMN_BUSY] = sqlite3_stmt_busy(p);
+ pNew->aCol[STMT_COLUMN_NSCAN] = sqlite3_stmt_status(
+ p, SQLITE_STMTSTATUS_FULLSCAN_STEP, 0
+ );
+ pNew->aCol[STMT_COLUMN_NSORT] = sqlite3_stmt_status(
+ p, SQLITE_STMTSTATUS_SORT, 0
+ );
+ pNew->aCol[STMT_COLUMN_NAIDX] = sqlite3_stmt_status(
+ p, SQLITE_STMTSTATUS_AUTOINDEX, 0
+ );
+ pNew->aCol[STMT_COLUMN_NSTEP] = sqlite3_stmt_status(
+ p, SQLITE_STMTSTATUS_VM_STEP, 0
+ );
+ pNew->aCol[STMT_COLUMN_REPREP] = sqlite3_stmt_status(
+ p, SQLITE_STMTSTATUS_REPREPARE, 0
+ );
+ pNew->aCol[STMT_COLUMN_RUN] = sqlite3_stmt_status(
+ p, SQLITE_STMTSTATUS_RUN, 0
+ );
+ pNew->aCol[STMT_COLUMN_MEM] = sqlite3_stmt_status(
+ p, SQLITE_STMTSTATUS_MEMUSED, 0
+ );
+ pNew->iRowid = iRowid++;
+ *ppRow = pNew;
+ ppRow = &pNew->pNext;
+ }
+
+ return SQLITE_OK;
}
/*
@@ -220458,13 +255607,14 @@ static int stmtBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
+ (void)tab;
pIdxInfo->estimatedCost = (double)500;
pIdxInfo->estimatedRows = 500;
return SQLITE_OK;
}
/*
-** This following structure defines all the methods for the
+** This following structure defines all the methods for the
** stmt virtual table.
*/
static sqlite3_module stmtModule = {
@@ -220492,6 +255642,7 @@ static sqlite3_module stmtModule = {
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
+ 0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -220509,8 +255660,8 @@ SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3 *db){
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_stmt_init(
- sqlite3 *db,
- char **pzErrMsg,
+ sqlite3 *db,
+ char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
@@ -220524,10 +255675,6 @@ SQLITE_API int sqlite3_stmt_init(
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
/************** End of stmt.c ************************************************/
-#if __LINE__!=220527
-#undef SQLITE_SOURCE_ID
-#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238alt2"
-#endif
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
/************************** End of sqlite3.c ******************************/
diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h
index f36ae57a64..4fdfde004e 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.h
+++ b/src/libs/3rdparty/sqlite/sqlite3.h
@@ -43,7 +43,30 @@ extern "C" {
/*
-** Provide the ability to override linkage features of the interface.
+** Facilitate override of interface linkage and calling conventions.
+** Be aware that these macros may not be used within this particular
+** translation of the amalgamation and its associated header file.
+**
+** The SQLITE_EXTERN and SQLITE_API macros are used to instruct the
+** compiler that the target identifier should have external linkage.
+**
+** The SQLITE_CDECL macro is used to set the calling convention for
+** public functions that accept a variable number of arguments.
+**
+** The SQLITE_APICALL macro is used to set the calling convention for
+** public functions that accept a fixed number of arguments.
+**
+** The SQLITE_STDCALL macro is no longer used and is now deprecated.
+**
+** The SQLITE_CALLBACK macro is used to set the calling convention for
+** function pointers.
+**
+** The SQLITE_SYSAPI macro is used to set the calling convention for
+** functions provided by the operating system.
+**
+** Currently, the SQLITE_CDECL, SQLITE_APICALL, SQLITE_CALLBACK, and
+** SQLITE_SYSAPI macros are used only when building for environments
+** that require non-default calling conventions.
*/
#ifndef SQLITE_EXTERN
# define SQLITE_EXTERN extern
@@ -108,7 +131,7 @@ extern "C" {
** be held constant and Z will be incremented or else Y will be incremented
** and Z will be reset to zero.
**
-** Since [version 3.6.18] ([dateof:3.6.18]),
+** Since [version 3.6.18] ([dateof:3.6.18]),
** SQLite source code has been stored in the
** <a href="http://www.fossil-scm.org/">Fossil configuration management
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
@@ -123,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.26.0"
-#define SQLITE_VERSION_NUMBER 3026000
-#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9"
+#define SQLITE_VERSION "3.45.1"
+#define SQLITE_VERSION_NUMBER 3045001
+#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -151,8 +174,8 @@ extern "C" {
** function is provided for use in DLLs since DLL users usually do not have
** direct access to string constants within the DLL. ^The
** sqlite3_libversion_number() function returns an integer equal to
-** [SQLITE_VERSION_NUMBER]. ^(The sqlite3_sourceid() function returns
-** a pointer to a string constant whose value is the same as the
+** [SQLITE_VERSION_NUMBER]. ^(The sqlite3_sourceid() function returns
+** a pointer to a string constant whose value is the same as the
** [SQLITE_SOURCE_ID] C preprocessor macro. Except if SQLite is built
** using an edited copy of [the amalgamation], then the last four characters
** of the hash might be different from [SQLITE_SOURCE_ID].)^
@@ -167,20 +190,20 @@ SQLITE_API int sqlite3_libversion_number(void);
/*
** CAPI3REF: Run-Time Library Compilation Options Diagnostics
**
-** ^The sqlite3_compileoption_used() function returns 0 or 1
-** indicating whether the specified option was defined at
-** compile time. ^The SQLITE_ prefix may be omitted from the
-** option name passed to sqlite3_compileoption_used().
+** ^The sqlite3_compileoption_used() function returns 0 or 1
+** indicating whether the specified option was defined at
+** compile time. ^The SQLITE_ prefix may be omitted from the
+** option name passed to sqlite3_compileoption_used().
**
** ^The sqlite3_compileoption_get() function allows iterating
** over the list of options that were defined at compile time by
** returning the N-th compile time option string. ^If N is out of range,
-** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_
-** prefix is omitted from any strings returned by
+** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_
+** prefix is omitted from any strings returned by
** sqlite3_compileoption_get().
**
** ^Support for the diagnostic functions sqlite3_compileoption_used()
-** and sqlite3_compileoption_get() may be omitted by specifying the
+** and sqlite3_compileoption_get() may be omitted by specifying the
** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time.
**
** See also: SQL functions [sqlite_compileoption_used()] and
@@ -189,6 +212,9 @@ SQLITE_API int sqlite3_libversion_number(void);
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
SQLITE_API int sqlite3_compileoption_used(const char *zOptName);
SQLITE_API const char *sqlite3_compileoption_get(int N);
+#else
+# define sqlite3_compileoption_used(X) 0
+# define sqlite3_compileoption_get(X) ((void*)0)
#endif
/*
@@ -201,7 +227,7 @@ SQLITE_API const char *sqlite3_compileoption_get(int N);
** SQLite can be compiled with or without mutexes. When
** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes
** are enabled and SQLite is threadsafe. When the
-** [SQLITE_THREADSAFE] macro is 0,
+** [SQLITE_THREADSAFE] macro is 0,
** the mutexes are omitted. Without the mutexes, it is not safe
** to use SQLite concurrently from more than one thread.
**
@@ -258,14 +284,14 @@ typedef struct sqlite3 sqlite3;
**
** ^The sqlite3_int64 and sqlite_int64 types can store integer values
** between -9223372036854775808 and +9223372036854775807 inclusive. ^The
-** sqlite3_uint64 and sqlite_uint64 types can store integer values
+** sqlite3_uint64 and sqlite_uint64 types can store integer values
** between 0 and +18446744073709551615 inclusive.
*/
#ifdef SQLITE_INT64_TYPE
typedef SQLITE_INT64_TYPE sqlite_int64;
# ifdef SQLITE_UINT64_TYPE
typedef SQLITE_UINT64_TYPE sqlite_uint64;
-# else
+# else
typedef unsigned SQLITE_INT64_TYPE sqlite_uint64;
# endif
#elif defined(_MSC_VER) || defined(__BORLANDC__)
@@ -296,26 +322,22 @@ typedef sqlite_uint64 sqlite3_uint64;
** the [sqlite3] object is successfully destroyed and all associated
** resources are deallocated.
**
-** ^If the database connection is associated with unfinalized prepared
-** statements or unfinished sqlite3_backup objects then sqlite3_close()
-** will leave the database connection open and return [SQLITE_BUSY].
-** ^If sqlite3_close_v2() is called with unfinalized prepared statements
-** and/or unfinished sqlite3_backups, then the database connection becomes
-** an unusable "zombie" which will automatically be deallocated when the
-** last prepared statement is finalized or the last sqlite3_backup is
-** finished. The sqlite3_close_v2() interface is intended for use with
-** host languages that are garbage collected, and where the order in which
-** destructors are called is arbitrary.
-**
-** Applications should [sqlite3_finalize | finalize] all [prepared statements],
-** [sqlite3_blob_close | close] all [BLOB handles], and
+** Ideally, applications should [sqlite3_finalize | finalize] all
+** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and
** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
-** with the [sqlite3] object prior to attempting to close the object. ^If
-** sqlite3_close_v2() is called on a [database connection] that still has
-** outstanding [prepared statements], [BLOB handles], and/or
-** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation
-** of resources is deferred until all [prepared statements], [BLOB handles],
-** and [sqlite3_backup] objects are also destroyed.
+** with the [sqlite3] object prior to attempting to close the object.
+** ^If the database connection is associated with unfinalized prepared
+** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then
+** sqlite3_close() will leave the database connection open and return
+** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared
+** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups,
+** it returns [SQLITE_OK] regardless, but instead of deallocating the database
+** connection immediately, it marks the database connection as an unusable
+** "zombie" and makes arrangements to automatically deallocate the database
+** connection after all prepared statements are finalized, all BLOB handles
+** are closed, and all backups have finished. The sqlite3_close_v2() interface
+** is intended for use with host languages that are garbage collected, and
+** where the order in which destructors are called is arbitrary.
**
** ^If an [sqlite3] object is destroyed while a transaction is open,
** the transaction is automatically rolled back.
@@ -345,7 +367,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** The sqlite3_exec() interface is a convenience wrapper around
** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()],
** that allows an application to run multiple statements of SQL
-** without having to use a lot of C code.
+** without having to use a lot of C code.
**
** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded,
** semicolon-separate SQL statements passed into its 2nd argument,
@@ -385,7 +407,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** from [sqlite3_column_name()].
**
** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer
-** to an empty string, or a pointer that contains only whitespace and/or
+** to an empty string, or a pointer that contains only whitespace and/or
** SQL comments, then no SQL statements are evaluated and the database
** is not changed.
**
@@ -504,17 +526,23 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
+#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
+#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
+#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
+#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
+#define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8))
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
+#define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3<<8))
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
@@ -532,11 +560,15 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8))
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8))
+#define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT |(11<<8))
+#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8))
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
+#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8))
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
+#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */
/*
** CAPI3REF: Flags For File Open Operations
@@ -544,6 +576,19 @@ SQLITE_API int sqlite3_exec(
** These bit values are intended for use in the
** 3rd parameter to the [sqlite3_open_v2()] interface and
** in the 4th parameter to the [sqlite3_vfs.xOpen] method.
+**
+** Only those flags marked as "Ok for sqlite3_open_v2()" may be
+** used as the third argument to the [sqlite3_open_v2()] interface.
+** The other flags have historically been ignored by sqlite3_open_v2(),
+** though future versions of SQLite might change so that an error is
+** raised if any of the disallowed bits are passed into sqlite3_open_v2().
+** Applications should not depend on the historical behavior.
+**
+** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into
+** [sqlite3_open_v2()] does *not* cause the underlying database file
+** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into
+** [sqlite3_open_v2()] has historically be a no-op and might become an
+** error in future versions of SQLite.
*/
#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
@@ -559,14 +604,19 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_OPEN_MAIN_JOURNAL 0x00000800 /* VFS only */
#define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 /* VFS only */
#define SQLITE_OPEN_SUBJOURNAL 0x00002000 /* VFS only */
-#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */
+#define SQLITE_OPEN_SUPER_JOURNAL 0x00004000 /* VFS only */
#define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_WAL 0x00080000 /* VFS only */
+#define SQLITE_OPEN_NOFOLLOW 0x01000000 /* Ok for sqlite3_open_v2() */
+#define SQLITE_OPEN_EXRESCODE 0x02000000 /* Extended result codes */
/* Reserved: 0x00F00000 */
+/* Legacy compatibility: */
+#define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */
+
/*
** CAPI3REF: Device Characteristics
@@ -622,13 +672,17 @@ SQLITE_API int sqlite3_exec(
**
** SQLite uses one of these integer values as the second
** argument to calls it makes to the xLock() and xUnlock() methods
-** of an [sqlite3_io_methods] object.
+** of an [sqlite3_io_methods] object. These values are ordered from
+** lest restrictive to most restrictive.
+**
+** The argument to xLock() is always SHARED or higher. The argument to
+** xUnlock is either SHARED or NONE.
*/
-#define SQLITE_LOCK_NONE 0
-#define SQLITE_LOCK_SHARED 1
-#define SQLITE_LOCK_RESERVED 2
-#define SQLITE_LOCK_PENDING 3
-#define SQLITE_LOCK_EXCLUSIVE 4
+#define SQLITE_LOCK_NONE 0 /* xUnlock() only */
+#define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */
+#define SQLITE_LOCK_RESERVED 2 /* xLock() only */
+#define SQLITE_LOCK_PENDING 3 /* xLock() only */
+#define SQLITE_LOCK_EXCLUSIVE 4 /* xLock() only */
/*
** CAPI3REF: Synchronization Type Flags
@@ -663,7 +717,7 @@ SQLITE_API int sqlite3_exec(
/*
** CAPI3REF: OS Interface Open File Handle
**
-** An [sqlite3_file] object represents an open file in the
+** An [sqlite3_file] object represents an open file in the
** [sqlite3_vfs | OS interface layer]. Individual OS interface
** implementations will
** want to subclass this object by appending additional fields
@@ -685,7 +739,7 @@ struct sqlite3_file {
** This object defines the methods used to perform various operations
** against the open file represented by the [sqlite3_file] object.
**
-** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element
+** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element
** to a non-NULL pointer, then the sqlite3_io_methods.xClose method
** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The
** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen]
@@ -706,7 +760,14 @@ struct sqlite3_file {
** <li> [SQLITE_LOCK_PENDING], or
** <li> [SQLITE_LOCK_EXCLUSIVE].
** </ul>
-** xLock() increases the lock. xUnlock() decreases the lock.
+** xLock() upgrades the database file lock. In other words, xLock() moves the
+** database file lock in the direction NONE toward EXCLUSIVE. The argument to
+** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
+** SQLITE_LOCK_NONE. If the database file lock is already at or above the
+** requested lock, then the call to xLock() is a no-op.
+** xUnlock() downgrades the database file lock to either SHARED or NONE.
+* If the lock is already at or below the requested lock state, then the call
+** to xUnlock() is a no-op.
** The xCheckReservedLock() method checks whether any database connection,
** either in this process or in some other process, is holding a RESERVED,
** PENDING, or EXCLUSIVE lock on the file. It returns true
@@ -811,9 +872,8 @@ struct sqlite3_io_methods {
** opcode causes the xFileControl method to write the current state of
** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
-** into an integer that the pArg argument points to. This capability
-** is used during testing and is only available when the SQLITE_TEST
-** compile-time option is used.
+** into an integer that the pArg argument points to.
+** This capability is only available if SQLite is compiled with [SQLITE_DEBUG].
**
** <li>[[SQLITE_FCNTL_SIZE_HINT]]
** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
@@ -823,10 +883,19 @@ struct sqlite3_io_methods {
** file space based on this hint in order to help writes to the database
** file run faster.
**
+** <li>[[SQLITE_FCNTL_SIZE_LIMIT]]
+** The [SQLITE_FCNTL_SIZE_LIMIT] opcode is used by in-memory VFS that
+** implements [sqlite3_deserialize()] to set an upper bound on the size
+** of the in-memory database. The argument is a pointer to a [sqlite3_int64].
+** If the integer pointed to is negative, then it is filled in with the
+** current limit. Otherwise the limit is set to the larger of the value
+** of the integer pointed to and the current database size. The integer
+** pointed to is set to the new limit.
+**
** <li>[[SQLITE_FCNTL_CHUNK_SIZE]]
** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
** extends and truncates the database file in chunks of a size specified
-** by the user. The fourth argument to [sqlite3_file_control()] should
+** by the user. The fourth argument to [sqlite3_file_control()] should
** point to an integer (type int) containing the new chunk-size to use
** for the nominated database. Allocating database file space in large
** chunks (say 1MB at a time), may reduce file-system fragmentation and
@@ -849,24 +918,24 @@ struct sqlite3_io_methods {
** <li>[[SQLITE_FCNTL_SYNC]]
** The [SQLITE_FCNTL_SYNC] opcode is generated internally by SQLite and
** sent to the VFS immediately before the xSync method is invoked on a
-** database file descriptor. Or, if the xSync method is not invoked
-** because the user has configured SQLite with
-** [PRAGMA synchronous | PRAGMA synchronous=OFF] it is invoked in place
+** database file descriptor. Or, if the xSync method is not invoked
+** because the user has configured SQLite with
+** [PRAGMA synchronous | PRAGMA synchronous=OFF] it is invoked in place
** of the xSync method. In most cases, the pointer argument passed with
** this file-control is NULL. However, if the database file is being synced
** as part of a multi-database commit, the argument points to a nul-terminated
-** string containing the transactions master-journal file name. VFSes that
-** do not need this signal should silently ignore this opcode. Applications
-** should not call [sqlite3_file_control()] with this opcode as doing so may
-** disrupt the operation of the specialized VFSes that do require it.
+** string containing the transactions super-journal file name. VFSes that
+** do not need this signal should silently ignore this opcode. Applications
+** should not call [sqlite3_file_control()] with this opcode as doing so may
+** disrupt the operation of the specialized VFSes that do require it.
**
** <li>[[SQLITE_FCNTL_COMMIT_PHASETWO]]
** The [SQLITE_FCNTL_COMMIT_PHASETWO] opcode is generated internally by SQLite
** and sent to the VFS after a transaction has been committed immediately
** but before the database is unlocked. VFSes that do not need this signal
** should silently ignore this opcode. Applications should not call
-** [sqlite3_file_control()] with this opcode as doing so may disrupt the
-** operation of the specialized VFSes that do require it.
+** [sqlite3_file_control()] with this opcode as doing so may disrupt the
+** operation of the specialized VFSes that do require it.
**
** <li>[[SQLITE_FCNTL_WIN32_AV_RETRY]]
** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
@@ -914,13 +983,13 @@ struct sqlite3_io_methods {
** <li>[[SQLITE_FCNTL_OVERWRITE]]
** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
** a write transaction to indicate that, unless it is rolled back for some
-** reason, the entire database file will be overwritten by the current
+** reason, the entire database file will be overwritten by the current
** transaction. This is used by VACUUM operations.
**
** <li>[[SQLITE_FCNTL_VFSNAME]]
** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
** all [VFSes] in the VFS stack. The names are of all VFS shims and the
-** final bottom-level VFS are written into memory obtained from
+** final bottom-level VFS are written into memory obtained from
** [sqlite3_malloc()] and the result is stored in the char* variable
** that the fourth parameter of [sqlite3_file_control()] points to.
** The caller is responsible for freeing the memory when done. As with
@@ -939,7 +1008,7 @@ struct sqlite3_io_methods {
** upper-most shim only.
**
** <li>[[SQLITE_FCNTL_PRAGMA]]
-** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA]
+** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA]
** file control is sent to the open [sqlite3_file] object corresponding
** to the database file to which the pragma statement refers. ^The argument
** to the [SQLITE_FCNTL_PRAGMA] file control is an array of
@@ -950,7 +1019,7 @@ struct sqlite3_io_methods {
** of the char** argument point to a string obtained from [sqlite3_mprintf()]
** or the equivalent and that string will become the result of the pragma or
** the error message if the pragma fails. ^If the
-** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal
+** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal
** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA]
** file control returns [SQLITE_OK], then the parser assumes that the
** VFS has handled the PRAGMA itself and the parser generates a no-op
@@ -967,16 +1036,16 @@ struct sqlite3_io_methods {
** ^The [SQLITE_FCNTL_BUSYHANDLER]
** file-control may be invoked by SQLite on the database file handle
** shortly after it is opened in order to provide a custom VFS with access
-** to the connections busy-handler callback. The argument is of type (void **)
+** to the connection's busy-handler callback. The argument is of type (void**)
** - an array of two (void *) values. The first (void *) actually points
-** to a function of type (int (*)(void *)). In order to invoke the connections
+** to a function of type (int (*)(void *)). In order to invoke the connection's
** busy-handler, this function should be invoked with the second (void *) in
** the array as the only argument. If it returns non-zero, then the operation
** should be retried. If it returns zero, the custom VFS should abandon the
** current operation.
**
** <li>[[SQLITE_FCNTL_TEMPFILENAME]]
-** ^Application can invoke the [SQLITE_FCNTL_TEMPFILENAME] file-control
+** ^Applications can invoke the [SQLITE_FCNTL_TEMPFILENAME] file-control
** to have SQLite generate a
** temporary filename using the same algorithm that is followed to generate
** temporary filenames for TEMP tables and other internal uses. The
@@ -990,7 +1059,7 @@ struct sqlite3_io_methods {
** The argument is a pointer to a value of type sqlite3_int64 that
** is an advisory maximum number of bytes in the file to memory map. The
** pointer is overwritten with the old value. The limit is not changed if
-** the value originally pointed to is negative, and so the current limit
+** the value originally pointed to is negative, and so the current limit
** can be queried by passing in a pointer to a negative number. This
** file-control is used internally to implement [PRAGMA mmap_size].
**
@@ -1034,7 +1103,7 @@ struct sqlite3_io_methods {
** <li>[[SQLITE_FCNTL_RBU]]
** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by
** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for
-** this opcode.
+** this opcode.
**
** <li>[[SQLITE_FCNTL_BEGIN_ATOMIC_WRITE]]
** If the [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] opcode returns SQLITE_OK, then
@@ -1051,7 +1120,7 @@ struct sqlite3_io_methods {
**
** <li>[[SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]]
** The [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE] opcode causes all write
-** operations since the previous successful call to
+** operations since the previous successful call to
** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be performed atomically.
** This file control returns [SQLITE_OK] if and only if the writes were
** all performed successfully and have been committed to persistent storage.
@@ -1063,7 +1132,7 @@ struct sqlite3_io_methods {
**
** <li>[[SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE]]
** The [SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE] opcode causes all write
-** operations since the previous successful call to
+** operations since the previous successful call to
** [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] to be rolled back.
** ^This file control takes the file descriptor out of batch write mode
** so that all subsequent write operations are independent.
@@ -1071,10 +1140,12 @@ struct sqlite3_io_methods {
** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
**
** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
-** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain
-** a file lock using the xLock or xShmLock methods of the VFS to wait
-** for up to M milliseconds before failing, where M is the single
-** unsigned integer parameter.
+** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS
+** to block for up to M milliseconds before failing when attempting to
+** obtain a file lock using the xLock or xShmLock methods of the VFS.
+** The parameter is a pointer to a 32-bit signed integer that contains
+** the value that M is to be set to. Before returning, the 32-bit signed
+** integer is overwritten with the previous value of M.
**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
@@ -1089,12 +1160,45 @@ struct sqlite3_io_methods {
** not provide a mechanism to detect changes to MAIN only. Also, the
** [sqlite3_total_changes()] interface responds to internal changes only and
** omits changes made by other database connections. The
-** [PRAGMA data_version] command provide a mechanism to detect changes to
+** [PRAGMA data_version] command provides a mechanism to detect changes to
** a single attached database that occur due to other database connections,
** but omits changes implemented by the database connection on which it is
** called. This file control is the only mechanism to detect changes that
** happen either internally or externally and that are associated with
** a particular attached database.
+**
+** <li>[[SQLITE_FCNTL_CKPT_START]]
+** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint
+** in wal mode before the client starts to copy pages from the wal
+** file to the database file.
+**
+** <li>[[SQLITE_FCNTL_CKPT_DONE]]
+** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint
+** in wal mode after the client has finished copying pages from the wal
+** file to the database file, but before the *-shm file is updated to
+** record the fact that the pages have been checkpointed.
+**
+** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
+** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
+** whether or not there is a database client in another process with a wal-mode
+** transaction open on the database or not. It is only available on unix.The
+** (void*) argument passed with this file-control should be a pointer to a
+** value of type (int). The integer value is set to 1 if the database is a wal
+** mode database and there exists at least one client in another process that
+** currently has an SQL transaction open on the database. It is set to 0 if
+** the database is not a wal-mode db, or if there is no such connection in any
+** other process. This opcode cannot be used to detect transactions opened
+** by clients within the current process, only within other processes.
+**
+** <li>[[SQLITE_FCNTL_CKSM_FILE]]
+** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use internally by the
+** [checksum VFS shim] only.
+**
+** <li>[[SQLITE_FCNTL_RESET_CACHE]]
+** If there is currently no transaction open on the database, and the
+** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control
+** purges the contents of the in-memory page cache. If there is an open
+** transaction, or if the db is a temp-db, this opcode is a no-op, not an error.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE 1
@@ -1131,6 +1235,13 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
#define SQLITE_FCNTL_LOCK_TIMEOUT 34
#define SQLITE_FCNTL_DATA_VERSION 35
+#define SQLITE_FCNTL_SIZE_LIMIT 36
+#define SQLITE_FCNTL_CKPT_DONE 37
+#define SQLITE_FCNTL_RESERVE_BYTES 38
+#define SQLITE_FCNTL_CKPT_START 39
+#define SQLITE_FCNTL_EXTERNAL_READER 40
+#define SQLITE_FCNTL_CKSM_FILE 41
+#define SQLITE_FCNTL_RESET_CACHE 42
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -1161,6 +1272,26 @@ typedef struct sqlite3_mutex sqlite3_mutex;
typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
+** CAPI3REF: File Name
+**
+** Type [sqlite3_filename] is used by SQLite to pass filenames to the
+** xOpen method of a [VFS]. It may be cast to (const char*) and treated
+** as a normal, nul-terminated, UTF-8 buffer containing the filename, but
+** may also be passed to special APIs such as:
+**
+** <ul>
+** <li> sqlite3_filename_database()
+** <li> sqlite3_filename_journal()
+** <li> sqlite3_filename_wal()
+** <li> sqlite3_uri_parameter()
+** <li> sqlite3_uri_boolean()
+** <li> sqlite3_uri_int64()
+** <li> sqlite3_uri_key()
+** </ul>
+*/
+typedef const char *sqlite3_filename;
+
+/*
** CAPI3REF: OS Interface Object
**
** An instance of the sqlite3_vfs object defines the interface between
@@ -1176,10 +1307,10 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** to 3 with SQLite [version 3.7.6] on [dateof:3.7.6]. Additional fields
** may be appended to the sqlite3_vfs object and the iVersion value
** may increase again in future versions of SQLite.
-** Note that the structure
-** of the sqlite3_vfs object changes in the transition from
+** Note that due to an oversight, the structure
+** of the sqlite3_vfs object changed in the transition from
** SQLite [version 3.5.9] to [version 3.6.0] on [dateof:3.6.0]
-** and yet the iVersion field was not modified.
+** and yet the iVersion field was not increased.
**
** The szOsFile field is the size of the subclassed [sqlite3_file]
** structure used by this VFS. mxPathname is the maximum length of
@@ -1214,14 +1345,14 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** the [sqlite3_file] can safely store a pointer to the
** filename if it needs to remember the filename for some reason.
** If the zFilename parameter to xOpen is a NULL pointer then xOpen
-** must invent its own temporary name for the file. ^Whenever the
+** must invent its own temporary name for the file. ^Whenever the
** xFilename parameter is NULL it will also be the case that the
** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE].
**
** The flags argument to xOpen() includes all bits set in
** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()]
** or [sqlite3_open16()] is used, then flags includes at least
-** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE].
+** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE].
** If xOpen() opens a file read-only then it sets *pOutFlags to
** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set.
**
@@ -1235,7 +1366,7 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** <li> [SQLITE_OPEN_TEMP_JOURNAL]
** <li> [SQLITE_OPEN_TRANSIENT_DB]
** <li> [SQLITE_OPEN_SUBJOURNAL]
-** <li> [SQLITE_OPEN_MASTER_JOURNAL]
+** <li> [SQLITE_OPEN_SUPER_JOURNAL]
** <li> [SQLITE_OPEN_WAL]
** </ul>)^
**
@@ -1263,14 +1394,14 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction
** with the [SQLITE_OPEN_CREATE] flag, which are both directly
** analogous to the O_EXCL and O_CREAT flags of the POSIX open()
-** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the
+** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the
** SQLITE_OPEN_CREATE, is used to indicate that file should always
** be created, and that it is an error if it already exists.
-** It is <i>not</i> used to indicate the file should be opened
+** It is <i>not</i> used to indicate the file should be opened
** for exclusive access.
**
** ^At least szOsFile bytes of memory are allocated by SQLite
-** to hold the [sqlite3_file] structure passed as the third
+** to hold the [sqlite3_file] structure passed as the third
** argument to xOpen. The xOpen method does not have to
** allocate the structure; it should just fill it in. Note that
** the xOpen method must set the sqlite3_file.pMethods to either
@@ -1283,8 +1414,14 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS]
** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to
** test whether a file is readable and writable, or [SQLITE_ACCESS_READ]
-** to test whether a file is at least readable. The file can be a
-** directory.
+** to test whether a file is at least readable. The SQLITE_ACCESS_READ
+** flag is never actually used and is not implemented in the built-in
+** VFSes of SQLite. The file is named by the second argument and can be a
+** directory. The xAccess method returns [SQLITE_OK] on success or some
+** non-zero error code if there is an I/O error or if the name of
+** the file given in the second argument is illegal. If SQLITE_OK
+** is returned, then non-zero or zero is written into *pResOut to indicate
+** whether or not the file is accessible.
**
** ^SQLite will always allocate at least mxPathname+1 bytes for the
** output buffer xFullPathname. The exact size of the output buffer
@@ -1304,16 +1441,16 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** method returns a Julian Day Number for the current date and time as
** a floating point value.
** ^The xCurrentTimeInt64() method returns, as an integer, the Julian
-** Day Number multiplied by 86400000 (the number of milliseconds in
-** a 24-hour day).
+** Day Number multiplied by 86400000 (the number of milliseconds in
+** a 24-hour day).
** ^SQLite will use the xCurrentTimeInt64() method to get the current
-** date and time if that method is available (if iVersion is 2 or
+** date and time if that method is available (if iVersion is 2 or
** greater and the function pointer is not NULL) and will fall back
** to xCurrentTime() if xCurrentTimeInt64() is unavailable.
**
** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces
** are not used by the SQLite core. These optional interfaces are provided
-** by some VFSes to facilitate testing of the VFS code. By overriding
+** by some VFSes to facilitate testing of the VFS code. By overriding
** system calls with functions under its control, a test program can
** simulate faults and error conditions that would otherwise be difficult
** or impossible to induce. The set of system calls that can be overridden
@@ -1332,7 +1469,7 @@ struct sqlite3_vfs {
sqlite3_vfs *pNext; /* Next registered VFS */
const char *zName; /* Name of this virtual file system */
void *pAppData; /* Pointer to application-specific data */
- int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*,
+ int (*xOpen)(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*,
int flags, int *pOutFlags);
int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir);
int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut);
@@ -1360,7 +1497,7 @@ struct sqlite3_vfs {
/*
** The methods above are in versions 1 through 3 of the sqlite_vfs object.
** New fields may be appended in future versions. The iVersion
- ** value will increment whenever this happens.
+ ** value will increment whenever this happens.
*/
};
@@ -1404,7 +1541,7 @@ struct sqlite3_vfs {
** </ul>
**
** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as
-** was given on the corresponding lock.
+** was given on the corresponding lock.
**
** The xShmLock method can transition between unlocked and SHARED or
** between unlocked and EXCLUSIVE. It cannot transition between SHARED
@@ -1519,20 +1656,23 @@ SQLITE_API int sqlite3_os_end(void);
** must ensure that no other SQLite interfaces are invoked by other
** threads while sqlite3_config() is running.</b>
**
-** The sqlite3_config() interface
-** may only be invoked prior to library initialization using
-** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
-** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
-** [sqlite3_shutdown()] then it will return SQLITE_MISUSE.
-** Note, however, that ^sqlite3_config() can be called as part of the
-** implementation of an application-defined [sqlite3_os_init()].
-**
** The first argument to sqlite3_config() is an integer
** [configuration option] that determines
** what property of SQLite is to be configured. Subsequent arguments
** vary depending on the [configuration option]
** in the first argument.
**
+** For most configuration options, the sqlite3_config() interface
+** may only be invoked prior to library initialization using
+** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
+** The exceptional configuration options that may be invoked at any time
+** are called "anytime configuration options".
+** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
+** [sqlite3_shutdown()] with a first argument that is not an anytime
+** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE.
+** Note, however, that ^sqlite3_config() can be called as part of the
+** implementation of an application-defined [sqlite3_os_init()].
+**
** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK].
** ^If the option is unknown or SQLite is unable to set the option
** then this routine returns a non-zero [error code].
@@ -1549,7 +1689,7 @@ SQLITE_API int sqlite3_config(int, ...);
** [database connection] (specified in the first argument).
**
** The second argument to sqlite3_db_config(D,V,...) is the
-** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code
+** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code
** that indicates what aspect of the [database connection] is being configured.
** Subsequent arguments vary depending on the configuration verb.
**
@@ -1567,7 +1707,7 @@ SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...);
** This object is used in only one place in the SQLite interface.
** A pointer to an instance of this object is the argument to
** [sqlite3_config()] when the configuration option is
-** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC].
+** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC].
** By creating an instance of this object
** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC])
** during configuration, an application can specify an alternative
@@ -1597,17 +1737,17 @@ SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...);
** allocators round up memory allocations at least to the next multiple
** of 8. Some allocators round up to a larger multiple or to a power of 2.
** Every memory allocation request coming in through [sqlite3_malloc()]
-** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0,
+** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0,
** that causes the corresponding memory allocation to fail.
**
** The xInit method initializes the memory allocator. For example,
-** it might allocate any require mutexes or initialize internal data
+** it might allocate any required mutexes or initialize internal data
** structures. The xShutdown method is invoked (indirectly) by
** [sqlite3_shutdown()] and should deallocate any resources acquired
** by xInit. The pAppData pointer is used as the only parameter to
** xInit and xShutdown.
**
-** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes
+** SQLite holds the [SQLITE_MUTEX_STATIC_MAIN] mutex when it invokes
** the xInit method, so the xInit method need not be threadsafe. The
** xShutdown method is only called from [sqlite3_shutdown()] so it does
** not need to be threadsafe either. For all other methods, SQLite
@@ -1640,6 +1780,23 @@ struct sqlite3_mem_methods {
** These constants are the available integer configuration options that
** can be passed as the first argument to the [sqlite3_config()] interface.
**
+** Most of the configuration options for sqlite3_config()
+** will only work if invoked prior to [sqlite3_initialize()] or after
+** [sqlite3_shutdown()]. The few exceptions to this rule are called
+** "anytime configuration options".
+** ^Calling [sqlite3_config()] with a first argument that is not an
+** anytime configuration option in between calls to [sqlite3_initialize()] and
+** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE.
+**
+** The set of anytime configuration options can change (by insertions
+** and/or deletions) from one release of SQLite to the next.
+** As of SQLite version 3.42.0, the complete set of anytime configuration
+** options is:
+** <ul>
+** <li> SQLITE_CONFIG_LOG
+** <li> SQLITE_CONFIG_PCACHE_HDRSZ
+** </ul>
+**
** New configuration options may be added in future releases of SQLite.
** Existing configuration options might be discontinued. Applications
** should check the return code from [sqlite3_config()] to make sure that
@@ -1655,7 +1812,7 @@ struct sqlite3_mem_methods {
** by a single thread. ^If SQLite is compiled with
** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
** it is not possible to change the [threading mode] from its default
-** value of Single-thread and so [sqlite3_config()] will return
+** value of Single-thread and so [sqlite3_config()] will return
** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD
** configuration option.</dd>
**
@@ -1690,7 +1847,7 @@ struct sqlite3_mem_methods {
** SQLITE_CONFIG_SERIALIZED configuration option.</dd>
**
** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt>
-** <dd> ^(The SQLITE_CONFIG_MALLOC option takes a single argument which is
+** <dd> ^(The SQLITE_CONFIG_MALLOC option takes a single argument which is
** a pointer to an instance of the [sqlite3_mem_methods] structure.
** The argument specifies
** alternative low-level memory allocation routines to be used in place of
@@ -1723,6 +1880,7 @@ struct sqlite3_mem_methods {
** memory allocation statistics. ^(When memory allocation statistics are
** disabled, the following SQLite interfaces become non-operational:
** <ul>
+** <li> [sqlite3_hard_heap_limit64()]
** <li> [sqlite3_memory_used()]
** <li> [sqlite3_memory_highwater()]
** <li> [sqlite3_soft_heap_limit64()]
@@ -1740,8 +1898,8 @@ struct sqlite3_mem_methods {
** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a memory pool
** that SQLite can use for the database page cache with the default page
-** cache implementation.
-** This configuration option is a no-op if an application-define page
+** cache implementation.
+** This configuration option is a no-op if an application-defined page
** cache implementation is loaded using the [SQLITE_CONFIG_PCACHE2].
** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to
** 8-byte aligned memory (pMem), the size of each page cache line (sz),
@@ -1768,7 +1926,7 @@ struct sqlite3_mem_methods {
** additional cache line. </dd>
**
** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
-** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer
+** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer
** that SQLite will use for all of its dynamic memory allocation needs
** beyond those provided for by [SQLITE_CONFIG_PAGECACHE].
** ^The SQLITE_CONFIG_HEAP option is only available if SQLite is compiled
@@ -1823,7 +1981,7 @@ struct sqlite3_mem_methods {
** configuration on individual connections.)^ </dd>
**
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
-** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
+** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
** a pointer to an [sqlite3_pcache_methods2] object. This object specifies
** the interface to a custom page cache implementation.)^
** ^SQLite makes a copy of the [sqlite3_pcache_methods2] object.</dd>
@@ -1837,7 +1995,7 @@ struct sqlite3_mem_methods {
** <dd> The SQLITE_CONFIG_LOG option is used to configure the SQLite
** global [error log].
** (^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
-** function with a call signature of void(*)(void*,int,const char*),
+** function with a call signature of void(*)(void*,int,const char*),
** and a pointer to void. ^If the function pointer is not NULL, it is
** invoked by [sqlite3_log()] to process each logging event. ^If the
** function pointer is NULL, the [sqlite3_log()] interface becomes a no-op.
@@ -1946,7 +2104,7 @@ struct sqlite3_mem_methods {
** [[SQLITE_CONFIG_STMTJRNL_SPILL]]
** <dt>SQLITE_CONFIG_STMTJRNL_SPILL
** <dd>^The SQLITE_CONFIG_STMTJRNL_SPILL option takes a single parameter which
-** becomes the [statement journal] spill-to-disk threshold.
+** becomes the [statement journal] spill-to-disk threshold.
** [Statement journals] are held in memory until their size (in bytes)
** exceeds this threshold, at which point they are written to disk.
** Or if the threshold is -1, statement journals are always held
@@ -1968,40 +2126,52 @@ struct sqlite3_mem_methods {
** than the configured sorter-reference size threshold - then a reference
** is stored in each sorted record and the required column values loaded
** from the database as records are returned in sorted order. The default
-** value for this option is to never use this optimization. Specifying a
-** negative value for this option restores the default behaviour.
+** value for this option is to never use this optimization. Specifying a
+** negative value for this option restores the default behavior.
** This option is only available if SQLite is compiled with the
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
+**
+** [[SQLITE_CONFIG_MEMDB_MAXSIZE]]
+** <dt>SQLITE_CONFIG_MEMDB_MAXSIZE
+** <dd>The SQLITE_CONFIG_MEMDB_MAXSIZE option accepts a single parameter
+** [sqlite3_int64] parameter which is the default maximum size for an in-memory
+** database created using [sqlite3_deserialize()]. This default maximum
+** size can be adjusted up or down for individual databases using the
+** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this
+** configuration setting is never used, then the default maximum is determined
+** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
+** compile-time option is not set, then the default maximum is 1073741824.
** </dl>
*/
-#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
-#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */
-#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
-#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
-#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
-#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */
-#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
-#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
-#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
-#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */
-#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
-/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
-#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
-#define SQLITE_CONFIG_PCACHE 14 /* no-op */
-#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */
-#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
-#define SQLITE_CONFIG_URI 17 /* int */
-#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */
-#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
+#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
+#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */
+#define SQLITE_CONFIG_SERIALIZED 3 /* nil */
+#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */
+#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */
+#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */
+#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */
+#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */
+#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */
+#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */
+#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
+/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
+#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
+#define SQLITE_CONFIG_PCACHE 14 /* no-op */
+#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */
+#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
+#define SQLITE_CONFIG_URI 17 /* int */
+#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */
+#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */
-#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
-#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
+#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
+#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
+#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -2019,7 +2189,7 @@ struct sqlite3_mem_methods {
** <dl>
** [[SQLITE_DBCONFIG_LOOKASIDE]]
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
-** <dd> ^This option takes three additional arguments that determine the
+** <dd> ^This option takes three additional arguments that determine the
** [lookaside memory allocator] configuration for the [database connection].
** ^The first argument (the third parameter to [sqlite3_db_config()] is a
** pointer to a memory buffer to use for lookaside memory.
@@ -2035,9 +2205,9 @@ struct sqlite3_mem_methods {
** configuration for a database connection can only be changed when that
** connection is not currently using lookaside memory, or in other words
** when the "current value" returned by
-** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero.
+** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
** Any attempt to change the lookaside memory configuration when lookaside
-** memory is in use leaves the configuration unchanged and returns
+** memory is in use leaves the configuration unchanged and returns
** [SQLITE_BUSY].)^</dd>
**
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
@@ -2060,12 +2230,35 @@ struct sqlite3_mem_methods {
** The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether triggers are disabled or enabled
** following this call. The second parameter may be a NULL pointer, in
-** which case the trigger setting is not reported back. </dd>
+** which case the trigger setting is not reported back.
+**
+** <p>Originally this option disabled all triggers. ^(However, since
+** SQLite version 3.35.0, TEMP triggers are still allowed even if
+** this option is off. So, in other words, this option now only disables
+** triggers in the main database schema or in the schemas of ATTACH-ed
+** databases.)^ </dd>
+**
+** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
+** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
+** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable views,
+** positive to enable views or negative to leave the setting unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether views are disabled or enabled
+** following this call. The second parameter may be a NULL pointer, in
+** which case the view setting is not reported back.
+**
+** <p>Originally this option disabled all views. ^(However, since
+** SQLite version 3.35.0, TEMP views are still allowed even if
+** this option is off. So, in other words, this option now only disables
+** views in the main database schema or in the schemas of ATTACH-ed
+** databases.)^ </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
-** <dd> ^This option is used to enable or disable the two-argument
-** version of the [fts3_tokenizer()] function which is part of the
+** <dd> ^This option is used to enable or disable the
+** [fts3_tokenizer()] function which is part of the
** [FTS3] full-text search engine extension.
** There should be two additional arguments.
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
@@ -2103,13 +2296,13 @@ struct sqlite3_mem_methods {
** until after the database connection closes.
** </dd>
**
-** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
+** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
-** <dd> Usually, when a database in wal mode is closed or detached from a
-** database handle, SQLite checks if this will mean that there are now no
-** connections at all to the database. If so, it performs a checkpoint
+** <dd> Usually, when a database in wal mode is closed or detached from a
+** database handle, SQLite checks if this will mean that there are now no
+** connections at all to the database. If so, it performs a checkpoint
** operation before closing the connection. This option may be used to
-** override this behaviour. The first parameter passed to this operation
+** override this behavior. The first parameter passed to this operation
** is an integer - positive to disable checkpoints-on-close, or zero (the
** default) to enable them, and negative to leave the setting unchanged.
** The second parameter is a pointer to an integer
@@ -2126,7 +2319,7 @@ struct sqlite3_mem_methods {
** slower. But the QPSG has the advantage of more predictable behavior. With
** the QPSG active, SQLite will always use the same query plan in the field as
** was used during testing in the lab.
-** The first argument to this setting is an integer which is 0 to disable
+** The first argument to this setting is an integer which is 0 to disable
** the QPSG, positive to enable QPSG, or negative to leave the setting
** unchanged. The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether the QPSG is disabled or enabled
@@ -2134,15 +2327,15 @@ struct sqlite3_mem_methods {
** </dd>
**
** [[SQLITE_DBCONFIG_TRIGGER_EQP]] <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
-** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not
+** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not
** include output for any operations performed by trigger programs. This
** option is used to set or clear (the default) a flag that governs this
** behavior. The first parameter passed to this operation is an integer -
** positive to enable output for trigger programs, or zero to disable it,
** or negative to leave the setting unchanged.
-** The second parameter is a pointer to an integer into which is written
-** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if
-** it is not disabled, 1 if it is.
+** The second parameter is a pointer to an integer into which is written
+** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if
+** it is not disabled, 1 if it is.
** </dd>
**
** [[SQLITE_DBCONFIG_RESET_DATABASE]] <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
@@ -2156,27 +2349,146 @@ struct sqlite3_mem_methods {
** database, or calling sqlite3_table_column_metadata(), ignoring any
** errors. This step is only necessary if the application desires to keep
** the database in WAL mode after the reset if it was in WAL mode before
-** the reset.
+** the reset.
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
** <li> [sqlite3_exec](db, "[VACUUM]", 0, 0, 0);
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
** </ol>
** Because resetting a database is destructive and irreversible, the
-** process requires the use of this obscure API and multiple steps to help
-** ensure that it does not happen by accident.
+** process requires the use of this obscure API and multiple steps to
+** help ensure that it does not happen by accident. Because this
+** feature must be capable of resetting corrupt databases, and
+** shutting down virtual tables may require access to that corrupt
+** storage, the library must abandon any installed virtual tables
+** without calling their xDestroy() methods.
**
** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
** "defensive" flag for a database connection. When the defensive
-** flag is enabled, language features that allow ordinary SQL to
+** flag is enabled, language features that allow ordinary SQL to
** deliberately corrupt the database file are disabled. The disabled
** features include but are not limited to the following:
** <ul>
** <li> The [PRAGMA writable_schema=ON] statement.
+** <li> The [PRAGMA journal_mode=OFF] statement.
+** <li> The [PRAGMA schema_version=N] statement.
** <li> Writes to the [sqlite_dbpage] virtual table.
** <li> Direct writes to [shadow tables].
** </ul>
** </dd>
+**
+** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]] <dt>SQLITE_DBCONFIG_WRITABLE_SCHEMA</dt>
+** <dd>The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the
+** "writable_schema" flag. This has the same effect and is logically equivalent
+** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF].
+** The first argument to this setting is an integer which is 0 to disable
+** the writable_schema, positive to enable writable_schema, or negative to
+** leave the setting unchanged. The second parameter is a pointer to an
+** integer into which is written 0 or 1 to indicate whether the writable_schema
+** is enabled or disabled following this call.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_LEGACY_ALTER_TABLE]]
+** <dt>SQLITE_DBCONFIG_LEGACY_ALTER_TABLE</dt>
+** <dd>The SQLITE_DBCONFIG_LEGACY_ALTER_TABLE option activates or deactivates
+** the legacy behavior of the [ALTER TABLE RENAME] command such it
+** behaves as it did prior to [version 3.24.0] (2018-06-04). See the
+** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for
+** additional information. This feature can also be turned on and off
+** using the [PRAGMA legacy_alter_table] statement.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_DQS_DML]]
+** <dt>SQLITE_DBCONFIG_DQS_DML</dt>
+** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates
+** the legacy [double-quoted string literal] misfeature for DML statements
+** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The
+** default value of this setting is determined by the [-DSQLITE_DQS]
+** compile-time option.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_DQS_DDL]]
+** <dt>SQLITE_DBCONFIG_DQS_DDL</dt>
+** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates
+** the legacy [double-quoted string literal] misfeature for DDL statements,
+** such as CREATE TABLE and CREATE INDEX. The
+** default value of this setting is determined by the [-DSQLITE_DQS]
+** compile-time option.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]]
+** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt>
+** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to
+** assume that database schemas are untainted by malicious content.
+** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite
+** takes additional defensive steps to protect the application from harm
+** including:
+** <ul>
+** <li> Prohibit the use of SQL functions inside triggers, views,
+** CHECK constraints, DEFAULT clauses, expression indexes,
+** partial indexes, or generated columns
+** unless those functions are tagged with [SQLITE_INNOCUOUS].
+** <li> Prohibit the use of virtual tables inside of triggers or views
+** unless those virtual tables are tagged with [SQLITE_VTAB_INNOCUOUS].
+** </ul>
+** This setting defaults to "on" for legacy compatibility, however
+** all applications are advised to turn it off if possible. This setting
+** can also be controlled using the [PRAGMA trusted_schema] statement.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]]
+** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt>
+** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates
+** the legacy file format flag. When activated, this flag causes all newly
+** created database file to have a schema format version number (the 4-byte
+** integer found at offset 44 into the database header) of 1. This in turn
+** means that the resulting database file will be readable and writable by
+** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting,
+** newly created databases are generally not understandable by SQLite versions
+** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there
+** is now scarcely any need to generate database files that are compatible
+** all the way back to version 3.0.0, and so this setting is of little
+** practical use, but is provided so that SQLite can continue to claim the
+** ability to generate new database files that are compatible with version
+** 3.0.0.
+** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on,
+** the [VACUUM] command will fail with an obscure error when attempting to
+** process a table with generated columns and a descending index. This is
+** not considered a bug since SQLite versions 3.3.0 and earlier do not support
+** either generated columns or descending indexes.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]]
+** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt>
+** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in
+** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears
+** a flag that enables collection of the sqlite3_stmt_scanstatus_v2()
+** statistics. For statistics to be collected, the flag must be set on
+** the database handle both when the SQL statement is prepared and when it
+** is stepped. The flag is set (collection of statistics is enabled)
+** by default. This option takes two arguments: an integer and a pointer to
+** an integer.. The first argument is 1, 0, or -1 to enable, disable, or
+** leave unchanged the statement scanstatus option. If the second argument
+** is not NULL, then the value of the statement scanstatus setting after
+** processing the first argument is written into the integer that the second
+** argument points to.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]]
+** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</dt>
+** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order
+** in which tables and indexes are scanned so that the scans start at the end
+** and work toward the beginning rather than starting at the beginning and
+** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the
+** same as setting [PRAGMA reverse_unordered_selects]. This option takes
+** two arguments which are an integer and a pointer to an integer. The first
+** argument is 1, 0, or -1 to enable, disable, or leave unchanged the
+** reverse scan order flag, respectively. If the second argument is not NULL,
+** then 0 or 1 is written into the integer that the second argument points to
+** depending on if the reverse scan order flag is set after processing the
+** first argument.
+** </dd>
+**
** </dl>
*/
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
@@ -2190,7 +2502,16 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
-#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */
+#define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */
+#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */
+#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
+#define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */
+#define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */
+#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */
+#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -2217,8 +2538,8 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff);
** ^The sqlite3_last_insert_rowid(D) interface usually returns the [rowid] of
** the most recent successful [INSERT] into a rowid table or [virtual table]
** on database connection D. ^Inserts into [WITHOUT ROWID] tables are not
-** recorded. ^If no successful [INSERT]s into rowid tables have ever occurred
-** on the database connection D, then sqlite3_last_insert_rowid(D) returns
+** recorded. ^If no successful [INSERT]s into rowid tables have ever occurred
+** on the database connection D, then sqlite3_last_insert_rowid(D) returns
** zero.
**
** As well as being set automatically as rows are inserted into database
@@ -2228,15 +2549,15 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff);
** Some virtual table implementations may INSERT rows into rowid tables as
** part of committing a transaction (e.g. to flush data accumulated in memory
** to disk). In this case subsequent calls to this function return the rowid
-** associated with these internal INSERT operations, which leads to
+** associated with these internal INSERT operations, which leads to
** unintuitive results. Virtual table implementations that do write to rowid
-** tables in this way can avoid this problem by restoring the original
-** rowid value using [sqlite3_set_last_insert_rowid()] before returning
+** tables in this way can avoid this problem by restoring the original
+** rowid value using [sqlite3_set_last_insert_rowid()] before returning
** control to the user.
**
-** ^(If an [INSERT] occurs within a trigger then this routine will
-** return the [rowid] of the inserted row as long as the trigger is
-** running. Once the trigger program ends, the value returned
+** ^(If an [INSERT] occurs within a trigger then this routine will
+** return the [rowid] of the inserted row as long as the trigger is
+** running. Once the trigger program ends, the value returned
** by this routine reverts to what it was before the trigger was fired.)^
**
** ^An [INSERT] that fails due to a constraint violation is not a
@@ -2269,7 +2590,7 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
** METHOD: sqlite3
**
** The sqlite3_set_last_insert_rowid(D, R) method allows the application to
-** set the value returned by calling sqlite3_last_insert_rowid(D) to R
+** set the value returned by calling sqlite3_last_insert_rowid(D) to R
** without inserting a row into the database.
*/
SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
@@ -2278,44 +2599,47 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
** CAPI3REF: Count The Number Of Rows Modified
** METHOD: sqlite3
**
-** ^This function returns the number of rows modified, inserted or
+** ^These functions return the number of rows modified, inserted or
** deleted by the most recently completed INSERT, UPDATE or DELETE
** statement on the database connection specified by the only parameter.
-** ^Executing any other type of SQL statement does not modify the value
-** returned by this function.
+** The two functions are identical except for the type of the return value
+** and that if the number of rows modified by the most recent INSERT, UPDATE
+** or DELETE is greater than the maximum value supported by type "int", then
+** the return value of sqlite3_changes() is undefined. ^Executing any other
+** type of SQL statement does not modify the value returned by these functions.
**
** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are
-** considered - auxiliary changes caused by [CREATE TRIGGER | triggers],
+** considered - auxiliary changes caused by [CREATE TRIGGER | triggers],
** [foreign key actions] or [REPLACE] constraint resolution are not counted.
-**
-** Changes to a view that are intercepted by
-** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value
-** returned by sqlite3_changes() immediately after an INSERT, UPDATE or
-** DELETE statement run on a view is always zero. Only changes made to real
+**
+** Changes to a view that are intercepted by
+** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value
+** returned by sqlite3_changes() immediately after an INSERT, UPDATE or
+** DELETE statement run on a view is always zero. Only changes made to real
** tables are counted.
**
** Things are more complicated if the sqlite3_changes() function is
** executed while a trigger program is running. This may happen if the
** program uses the [changes() SQL function], or if some other callback
** function invokes sqlite3_changes() directly. Essentially:
-**
+**
** <ul>
** <li> ^(Before entering a trigger program the value returned by
-** sqlite3_changes() function is saved. After the trigger program
+** sqlite3_changes() function is saved. After the trigger program
** has finished, the original value is restored.)^
-**
-** <li> ^(Within a trigger program each INSERT, UPDATE and DELETE
-** statement sets the value returned by sqlite3_changes()
-** upon completion as normal. Of course, this value will not include
-** any changes performed by sub-triggers, as the sqlite3_changes()
+**
+** <li> ^(Within a trigger program each INSERT, UPDATE and DELETE
+** statement sets the value returned by sqlite3_changes()
+** upon completion as normal. Of course, this value will not include
+** any changes performed by sub-triggers, as the sqlite3_changes()
** value will be saved and restored after each sub-trigger has run.)^
** </ul>
-**
+**
** ^This means that if the changes() SQL function (or similar) is used
-** by the first INSERT, UPDATE or DELETE statement within a trigger, it
+** by the first INSERT, UPDATE or DELETE statement within a trigger, it
** returns the value as set when the calling statement began executing.
-** ^If it is used by the second or subsequent such statement within a trigger
-** program, the value returned reflects the number of rows modified by the
+** ^If it is used by the second or subsequent such statement within a trigger
+** program, the value returned reflects the number of rows modified by the
** previous INSERT, UPDATE or DELETE statement within the same trigger.
**
** If a separate thread makes changes on the same database connection
@@ -2331,29 +2655,34 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
** </ul>
*/
SQLITE_API int sqlite3_changes(sqlite3*);
+SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3*);
/*
** CAPI3REF: Total Number Of Rows Modified
** METHOD: sqlite3
**
-** ^This function returns the total number of rows inserted, modified or
+** ^These functions return the total number of rows inserted, modified or
** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed
** since the database connection was opened, including those executed as
-** part of trigger programs. ^Executing any other type of SQL statement
-** does not affect the value returned by sqlite3_total_changes().
-**
+** part of trigger programs. The two functions are identical except for the
+** type of the return value and that if the number of rows modified by the
+** connection exceeds the maximum value supported by type "int", then
+** the return value of sqlite3_total_changes() is undefined. ^Executing
+** any other type of SQL statement does not affect the value returned by
+** sqlite3_total_changes().
+**
** ^Changes made as part of [foreign key actions] are included in the
** count, but those made as part of REPLACE constraint resolution are
-** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
+** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
** are not counted.
**
-** This the [sqlite3_total_changes(D)] interface only reports the number
+** The [sqlite3_total_changes(D)] interface only reports the number
** of rows that changed due to SQL statement run against database
** connection D. Any changes by other database connections are ignored.
** To detect changes against a database file from other database
** connections use the [PRAGMA data_version] command or the
** [SQLITE_FCNTL_DATA_VERSION] [file control].
-**
+**
** If a separate thread makes changes on the same database connection
** while [sqlite3_total_changes()] is running then the value
** returned is unpredictable and not meaningful.
@@ -2368,6 +2697,7 @@ SQLITE_API int sqlite3_changes(sqlite3*);
** </ul>
*/
SQLITE_API int sqlite3_total_changes(sqlite3*);
+SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*);
/*
** CAPI3REF: Interrupt A Long-Running Query
@@ -2395,16 +2725,21 @@ SQLITE_API int sqlite3_total_changes(sqlite3*);
**
** ^The sqlite3_interrupt(D) call is in effect until all currently running
** SQL statements on [database connection] D complete. ^Any new SQL statements
-** that are started after the sqlite3_interrupt() call and before the
-** running statements reaches zero are interrupted as if they had been
+** that are started after the sqlite3_interrupt() call and before the
+** running statement count reaches zero are interrupted as if they had been
** running prior to the sqlite3_interrupt() call. ^New SQL statements
** that are started after the running statement count reaches zero are
** not effected by the sqlite3_interrupt().
** ^A call to sqlite3_interrupt(D) that occurs when there are no running
** SQL statements is a no-op and has no effect on SQL statements
** that are started after the sqlite3_interrupt() call returns.
+**
+** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether
+** or not an interrupt is currently in effect for [database connection] D.
+** It returns 1 if an interrupt is currently in effect, or 0 otherwise.
*/
SQLITE_API void sqlite3_interrupt(sqlite3*);
+SQLITE_API int sqlite3_is_interrupted(sqlite3*);
/*
** CAPI3REF: Determine If An SQL Statement Is Complete
@@ -2427,7 +2762,7 @@ SQLITE_API void sqlite3_interrupt(sqlite3*);
** ^These routines do not parse the SQL statements thus
** will not detect syntactically incorrect SQL.
**
-** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior
+** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior
** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked
** automatically by sqlite3_complete16(). If that initialization fails,
** then the return value from sqlite3_complete16() will be non-zero
@@ -2472,7 +2807,7 @@ SQLITE_API int sqlite3_complete16(const void *sql);
** The presence of a busy handler does not guarantee that it will be invoked
** when there is lock contention. ^If SQLite determines that invoking the busy
** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY]
-** to the application instead of invoking the
+** to the application instead of invoking the
** busy handler.
** Consider a scenario where one process is holding a read lock that
** it is trying to promote to a reserved lock and
@@ -2497,7 +2832,7 @@ SQLITE_API int sqlite3_complete16(const void *sql);
** database connection that invoked the busy handler. In other words,
** the busy handler is not reentrant. Any such actions
** result in undefined behavior.
-**
+**
** A busy handler must not close the database connection
** or [prepared statement] that invoked the busy handler.
*/
@@ -2564,9 +2899,9 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
** Cindy | 21
** </pre></blockquote>
**
-** There are two column (M==2) and three rows (N==3). Thus the
+** There are two columns (M==2) and three rows (N==3). Thus the
** result table has 8 entries. Suppose the result table is stored
-** in an array names azResult. Then azResult holds this content:
+** in an array named azResult. Then azResult holds this content:
**
** <blockquote><pre>
** azResult&#91;0] = "Name";
@@ -2615,7 +2950,7 @@ SQLITE_API void sqlite3_free_table(char **result);
** These routines are work-alikes of the "printf()" family of functions
** from the standard C library.
** These routines understand most of the common formatting options from
-** the standard library printf()
+** the standard library printf()
** plus some additional non-standard formats ([%q], [%Q], [%w], and [%z]).
** See the [built-in printf()] documentation for details.
**
@@ -2659,7 +2994,7 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
**
** The SQLite core uses these three routines for all of its own
** internal memory allocation needs. "Core" in the previous sentence
-** does not include operating-system specific VFS implementation. The
+** does not include operating-system specific [VFS] implementation. The
** Windows VFS uses native malloc() and free() for some operations.
**
** ^The sqlite3_malloc() routine returns a pointer to a block
@@ -2720,19 +3055,6 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time
** option is used.
**
-** In SQLite version 3.5.0 and 3.5.1, it was possible to define
-** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in
-** implementation of these routines to be omitted. That capability
-** is no longer provided. Only built-in memory allocators can be used.
-**
-** Prior to SQLite version 3.7.10, the Windows OS interface layer called
-** the system malloc() and free() directly when converting
-** filenames between the UTF-8 encoding used by SQLite
-** and whatever filename encoding is used by the particular Windows
-** installation. Memory allocation errors were detected, but
-** they were reported back as [SQLITE_CANTOPEN] or
-** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
-**
** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
** must be either NULL or else pointers obtained from a prior
** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have
@@ -2781,7 +3103,7 @@ SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
** SQLite contains a high-quality pseudo-random number generator (PRNG) used to
** select random [ROWID | ROWIDs] when inserting new records into a table that
** already uses the largest possible [ROWID]. The PRNG is also used for
-** the build-in random() and randomblob() SQL functions. This interface allows
+** the built-in random() and randomblob() SQL functions. This interface allows
** applications to access the same PRNG for other purposes.
**
** ^A call to this routine stores N bytes of randomness into buffer P.
@@ -2824,7 +3146,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P);
** requested is ok. ^When the callback returns [SQLITE_DENY], the
** [sqlite3_prepare_v2()] or equivalent call that triggered the
** authorizer will fail with an error message explaining that
-** access is denied.
+** access is denied.
**
** ^The first parameter to the authorizer callback is a copy of the third
** parameter to the sqlite3_set_authorizer() interface. ^The second parameter
@@ -2877,7 +3199,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P);
** database connections for the meaning of "modify" in this paragraph.
**
** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the
-** statement might be re-prepared during [sqlite3_step()] due to a
+** statement might be re-prepared during [sqlite3_step()] due to a
** schema change. Hence, the application should ensure that the
** correct authorizer callback remains in place during the [sqlite3_step()].
**
@@ -2991,9 +3313,9 @@ SQLITE_API int sqlite3_set_authorizer(
** time is in units of nanoseconds, however the current implementation
** is only capable of millisecond resolution so the six least significant
** digits in the time are meaningless. Future versions of SQLite
-** might provide greater resolution on the profiler callback. The
-** sqlite3_profile() function is considered experimental and is
-** subject to change in future versions of SQLite.
+** might provide greater resolution on the profiler callback. Invoking
+** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the
+** profile callback.
*/
SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*,
void(*xTrace)(void*,const char*), void*);
@@ -3025,7 +3347,7 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
** execution of the prepared statement, such as at the start of each
** trigger subprogram. ^The P argument is a pointer to the
** [prepared statement]. ^The X argument is a pointer to a string which
-** is the unexpanded SQL text of the prepared statement or an SQL comment
+** is the unexpanded SQL text of the prepared statement or an SQL comment
** that indicates the invocation of a trigger. ^The callback can compute
** the same text that would have been returned by the legacy [sqlite3_trace()]
** interface by using the X argument when X begins with "--" and invoking
@@ -3035,13 +3357,13 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
** <dd>^An SQLITE_TRACE_PROFILE callback provides approximately the same
** information as is provided by the [sqlite3_profile()] callback.
** ^The P argument is a pointer to the [prepared statement] and the
-** X argument points to a 64-bit integer which is the estimated of
-** the number of nanosecond that the prepared statement took to run.
+** X argument points to a 64-bit integer which is approximately
+** the number of nanoseconds that the prepared statement took to run.
** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes.
**
** [[SQLITE_TRACE_ROW]] <dt>SQLITE_TRACE_ROW</dt>
** <dd>^An SQLITE_TRACE_ROW callback is invoked whenever a prepared
-** statement generates a single row of result.
+** statement generates a single row of result.
** ^The P argument is a pointer to the [prepared statement] and the
** X argument is unused.
**
@@ -3068,10 +3390,12 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
** M argument should be the bitwise OR-ed combination of
** zero or more [SQLITE_TRACE] constants.
**
-** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides
-** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2().
+** ^Each call to either sqlite3_trace(D,X,P) or sqlite3_trace_v2(D,M,X,P)
+** overrides (cancels) all prior calls to sqlite3_trace(D,X,P) or
+** sqlite3_trace_v2(D,M,X,P) for the [database connection] D. Each
+** database connection may have at most one trace callback.
**
-** ^The X callback is invoked whenever any of the events identified by
+** ^The X callback is invoked whenever any of the events identified by
** mask M occur. ^The integer return value from the callback is currently
** ignored, though this may change in future releases. Callback
** implementations should return zero to ensure future compatibility.
@@ -3099,12 +3423,12 @@ SQLITE_API int sqlite3_trace_v2(
**
** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback
** function X to be invoked periodically during long running calls to
-** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for
+** [sqlite3_step()] and [sqlite3_prepare()] and similar for
** database connection D. An example use for this
** interface is to keep a GUI updated during a large query.
**
-** ^The parameter P is passed through as the only parameter to the
-** callback function X. ^The parameter N is the approximate number of
+** ^The parameter P is passed through as the only parameter to the
+** callback function X. ^The parameter N is the approximate number of
** [virtual machine instructions] that are evaluated between successive
** invocations of the callback X. ^If N is less than one then the progress
** handler is disabled.
@@ -3124,6 +3448,13 @@ SQLITE_API int sqlite3_trace_v2(
** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
** database connections for the meaning of "modify" in this paragraph.
**
+** The progress handler callback would originally only be invoked from the
+** bytecode engine. It still might be invoked during [sqlite3_prepare()]
+** and similar because those routines might force a reparse of the schema
+** which involves running the bytecode engine. However, beginning with
+** SQLite version 3.41.0, the progress handler callback might also be
+** invoked directly from [sqlite3_prepare()] while analyzing and generating
+** code for complex queries.
*/
SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
@@ -3131,7 +3462,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** CAPI3REF: Opening A New Database Connection
** CONSTRUCTOR: sqlite3
**
-** ^These routines open an SQLite database file as specified by the
+** ^These routines open an SQLite database file as specified by the
** filename argument. ^The filename argument is interpreted as UTF-8 for
** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
** order for sqlite3_open16(). ^(A [database connection] handle is usually
@@ -3155,20 +3486,23 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** The sqlite3_open_v2() interface works like sqlite3_open()
** except that it accepts two additional parameters for additional control
** over the new database connection. ^(The flags parameter to
-** sqlite3_open_v2() can take one of
-** the following three values, optionally combined with the
-** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE],
-** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^
+** sqlite3_open_v2() must include, at a minimum, one of the following
+** three flag combinations:)^
**
** <dl>
** ^(<dt>[SQLITE_OPEN_READONLY]</dt>
-** <dd>The database is opened in read-only mode. If the database does not
-** already exist, an error is returned.</dd>)^
+** <dd>The database is opened in read-only mode. If the database does
+** not already exist, an error is returned.</dd>)^
**
** ^(<dt>[SQLITE_OPEN_READWRITE]</dt>
-** <dd>The database is opened for reading and writing if possible, or reading
-** only if the file is write protected by the operating system. In either
-** case the database must already exist, otherwise an error is returned.</dd>)^
+** <dd>The database is opened for reading and writing if possible, or
+** reading only if the file is write protected by the operating
+** system. In either case the database must already exist, otherwise
+** an error is returned. For historical reasons, if opening in
+** read-write mode fails due to OS-level permissions, an attempt is
+** made to open it in read-only mode. [sqlite3_db_readonly()] can be
+** used to determine whether the database is actually
+** read-write.</dd>)^
**
** ^(<dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt>
** <dd>The database is opened for reading and writing, and is created if
@@ -3176,22 +3510,69 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** sqlite3_open() and sqlite3_open16().</dd>)^
** </dl>
**
+** In addition to the required flags, the following optional flags are
+** also supported:
+**
+** <dl>
+** ^(<dt>[SQLITE_OPEN_URI]</dt>
+** <dd>The filename can be interpreted as a URI if this flag is set.</dd>)^
+**
+** ^(<dt>[SQLITE_OPEN_MEMORY]</dt>
+** <dd>The database will be opened as an in-memory database. The database
+** is named by the "filename" argument for the purposes of cache-sharing,
+** if shared cache mode is enabled, but the "filename" is otherwise ignored.
+** </dd>)^
+**
+** ^(<dt>[SQLITE_OPEN_NOMUTEX]</dt>
+** <dd>The new database connection will use the "multi-thread"
+** [threading mode].)^ This means that separate threads are allowed
+** to use SQLite at the same time, as long as each thread is using
+** a different [database connection].
+**
+** ^(<dt>[SQLITE_OPEN_FULLMUTEX]</dt>
+** <dd>The new database connection will use the "serialized"
+** [threading mode].)^ This means the multiple threads can safely
+** attempt to use the same database connection at the same time.
+** (Mutexes will block any actual concurrency, but in this mode
+** there is no harm in trying.)
+**
+** ^(<dt>[SQLITE_OPEN_SHAREDCACHE]</dt>
+** <dd>The database is opened [shared cache] enabled, overriding
+** the default shared cache setting provided by
+** [sqlite3_enable_shared_cache()].)^
+** The [use of shared cache mode is discouraged] and hence shared cache
+** capabilities may be omitted from many builds of SQLite. In such cases,
+** this option is a no-op.
+**
+** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
+** <dd>The database is opened [shared cache] disabled, overriding
+** the default shared cache setting provided by
+** [sqlite3_enable_shared_cache()].)^
+**
+** [[OPEN_EXRESCODE]] ^(<dt>[SQLITE_OPEN_EXRESCODE]</dt>
+** <dd>The database connection comes up in "extended result code mode".
+** In other words, the database behaves has if
+** [sqlite3_extended_result_codes(db,1)] where called on the database
+** connection as soon as the connection is created. In addition to setting
+** the extended result code mode, this flag also causes [sqlite3_open_v2()]
+** to return an extended result code.</dd>
+**
+** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt>
+** <dd>The database filename is not allowed to contain a symbolic link</dd>
+** </dl>)^
+**
** If the 3rd parameter to sqlite3_open_v2() is not one of the
-** combinations shown above optionally combined with other
+** required combinations shown above optionally combined with other
** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits]
-** then the behavior is undefined.
-**
-** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection
-** opens in the multi-thread [threading mode] as long as the single-thread
-** mode has not been set at compile-time or start-time. ^If the
-** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens
-** in the serialized [threading mode] unless single-thread was
-** previously selected at compile-time or start-time.
-** ^The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be
-** eligible to use [shared cache mode], regardless of whether or not shared
-** cache is enabled using [sqlite3_enable_shared_cache()]. ^The
-** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not
-** participate in [shared cache mode] even if it is enabled.
+** then the behavior is undefined. Historic versions of SQLite
+** have silently ignored surplus bits in the flags parameter to
+** sqlite3_open_v2(), however that behavior might not be carried through
+** into future versions of SQLite and so applications should not rely
+** upon it. Note in particular that the SQLITE_OPEN_EXCLUSIVE flag is a no-op
+** for sqlite3_open_v2(). The SQLITE_OPEN_EXCLUSIVE does *not* cause
+** the open to fail if the database already exists. The SQLITE_OPEN_EXCLUSIVE
+** flag is intended for use by the [sqlite3_vfs|VFS interface] only, and not
+** by sqlite3_open_v2().
**
** ^The fourth parameter to sqlite3_open_v2() is the name of the
** [sqlite3_vfs] object that defines the operating system interface that
@@ -3224,17 +3605,17 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** information.
**
** URI filenames are parsed according to RFC 3986. ^If the URI contains an
-** authority, then it must be either an empty string or the string
-** "localhost". ^If the authority is not an empty string or "localhost", an
-** error is returned to the caller. ^The fragment component of a URI, if
+** authority, then it must be either an empty string or the string
+** "localhost". ^If the authority is not an empty string or "localhost", an
+** error is returned to the caller. ^The fragment component of a URI, if
** present, is ignored.
**
** ^SQLite uses the path component of the URI as the name of the disk file
-** which contains the database. ^If the path begins with a '/' character,
-** then it is interpreted as an absolute path. ^If the path does not begin
+** which contains the database. ^If the path begins with a '/' character,
+** then it is interpreted as an absolute path. ^If the path does not begin
** with a '/' (meaning that the authority section is omitted from the URI)
-** then the path is interpreted as a relative path.
-** ^(On windows, the first component of an absolute path
+** then the path is interpreted as a relative path.
+** ^(On windows, the first component of an absolute path
** is a drive specification (e.g. "C:").)^
**
** [[core URI query parameters]]
@@ -3254,13 +3635,13 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
**
** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw",
** "rwc", or "memory". Attempting to set it to any other value is
-** an error)^.
-** ^If "ro" is specified, then the database is opened for read-only
-** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the
-** third argument to sqlite3_open_v2(). ^If the mode option is set to
-** "rw", then the database is opened for read-write (but not create)
-** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had
-** been set. ^Value "rwc" is equivalent to setting both
+** an error)^.
+** ^If "ro" is specified, then the database is opened for read-only
+** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the
+** third argument to sqlite3_open_v2(). ^If the mode option is set to
+** "rw", then the database is opened for read-write (but not create)
+** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had
+** been set. ^Value "rwc" is equivalent to setting both
** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is
** set to "memory" then a pure [in-memory database] that never reads
** or writes from disk is used. ^It is an error to specify a value for
@@ -3270,7 +3651,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or
** "private". ^Setting it to "shared" is equivalent to setting the
** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to
-** sqlite3_open_v2(). ^Setting the cache parameter to "private" is
+** sqlite3_open_v2(). ^Setting the cache parameter to "private" is
** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
** ^If sqlite3_open_v2() is used and the "cache" parameter is present in
** a URI filename, its value overrides any behavior requested by setting
@@ -3296,7 +3677,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** property on a database file that does in fact change can result
** in incorrect query results and/or [SQLITE_CORRUPT] errors.
** See also: [SQLITE_IOCAP_IMMUTABLE].
-**
+**
** </ul>
**
** ^Specifying an unknown parameter in the query component of a URI is not an
@@ -3308,36 +3689,37 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
**
** <table border="1" align=center cellpadding=5>
** <tr><th> URI filenames <th> Results
-** <tr><td> file:data.db <td>
+** <tr><td> file:data.db <td>
** Open the file "data.db" in the current directory.
** <tr><td> file:/home/fred/data.db<br>
-** file:///home/fred/data.db <br>
-** file://localhost/home/fred/data.db <br> <td>
+** file:///home/fred/data.db <br>
+** file://localhost/home/fred/data.db <br> <td>
** Open the database file "/home/fred/data.db".
-** <tr><td> file://darkstar/home/fred/data.db <td>
+** <tr><td> file://darkstar/home/fred/data.db <td>
** An error. "darkstar" is not a recognized authority.
-** <tr><td style="white-space:nowrap">
+** <tr><td style="white-space:nowrap">
** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db
** <td> Windows only: Open the file "data.db" on fred's desktop on drive
-** C:. Note that the %20 escaping in this example is not strictly
+** C:. Note that the %20 escaping in this example is not strictly
** necessary - space characters can be used literally
** in URI filenames.
-** <tr><td> file:data.db?mode=ro&cache=private <td>
+** <tr><td> file:data.db?mode=ro&cache=private <td>
** Open file "data.db" in the current directory for read-only access.
** Regardless of whether or not shared-cache mode is enabled by
** default, use a private cache.
** <tr><td> file:/home/fred/data.db?vfs=unix-dotfile <td>
** Open file "/home/fred/data.db". Use the special VFS "unix-dotfile"
** that uses dot-files in place of posix advisory locking.
-** <tr><td> file:data.db?mode=readonly <td>
+** <tr><td> file:data.db?mode=readonly <td>
** An error. "readonly" is not a valid option for the "mode" parameter.
+** Use "ro" instead: "file:data.db?mode=ro".
** </table>
**
** ^URI hexadecimal escape sequences (%HH) are supported within the path and
** query components of a URI. A hexadecimal escape sequence consists of a
-** percent sign - "%" - followed by exactly two hexadecimal digits
+** percent sign - "%" - followed by exactly two hexadecimal digits
** specifying an octet value. ^Before the path or query components of a
-** URI filename are interpreted, they are encoded using UTF-8 and all
+** URI filename are interpreted, they are encoded using UTF-8 and all
** hexadecimal escape sequences replaced by a single byte containing the
** corresponding octet. If this process generates an invalid UTF-8 encoding,
** the results are undefined.
@@ -3372,17 +3754,27 @@ SQLITE_API int sqlite3_open_v2(
/*
** CAPI3REF: Obtain Values For URI Parameters
**
-** These are utility routines, useful to VFS implementations, that check
-** to see if a database file was a URI that contained a specific query
+** These are utility routines, useful to [VFS|custom VFS implementations],
+** that check if a database file was a URI that contained a specific query
** parameter, and if so obtains the value of that query parameter.
**
-** If F is the database filename pointer passed into the xOpen() method of
-** a VFS implementation when the flags parameter to xOpen() has one or
-** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and
-** P is the name of the query parameter, then
+** The first parameter to these interfaces (hereafter referred to
+** as F) must be one of:
+** <ul>
+** <li> A database filename pointer created by the SQLite core and
+** passed into the xOpen() method of a VFS implementation, or
+** <li> A filename obtained from [sqlite3_db_filename()], or
+** <li> A new filename constructed using [sqlite3_create_filename()].
+** </ul>
+** If the F parameter is not one of the above, then the behavior is
+** undefined and probably undesirable. Older versions of SQLite were
+** more tolerant of invalid F parameters than newer versions.
+**
+** If F is a suitable filename (as described in the previous paragraph)
+** and if P is the name of the query parameter, then
** sqlite3_uri_parameter(F,P) returns the value of the P
-** parameter if it exists or a NULL pointer if P does not appear as a
-** query parameter on F. If P is a query parameter of F
+** parameter if it exists or a NULL pointer if P does not appear as a
+** query parameter on F. If P is a query parameter of F and it
** has no explicit value, then sqlite3_uri_parameter(F,P) returns
** a pointer to an empty string.
**
@@ -3390,39 +3782,160 @@ SQLITE_API int sqlite3_open_v2(
** parameter and returns true (1) or false (0) according to the value
** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the
** value of query parameter P is one of "yes", "true", or "on" in any
-** case or if the value begins with a non-zero number. The
+** case or if the value begins with a non-zero number. The
** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of
** query parameter P is one of "no", "false", or "off" in any case or
** if the value begins with a numeric zero. If P is not a query
-** parameter on F or if the value of P is does not match any of the
+** parameter on F or if the value of P does not match any of the
** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0).
**
** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a
** 64-bit signed integer and returns that integer, or D if P does not
** exist. If the value of P is something other than an integer, then
** zero is returned.
-**
+**
+** The sqlite3_uri_key(F,N) returns a pointer to the name (not
+** the value) of the N-th query parameter for filename F, or a NULL
+** pointer if N is less than zero or greater than the number of query
+** parameters minus 1. The N value is zero-based so N should be 0 to obtain
+** the name of the first query parameter, 1 for the second parameter, and
+** so forth.
+**
** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and
** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and
-** is not a database file pathname pointer that SQLite passed into the xOpen
-** VFS method, then the behavior of this routine is undefined and probably
-** undesirable.
+** is not a database file pathname pointer that the SQLite core passed
+** into the xOpen VFS method, then the behavior of this routine is undefined
+** and probably undesirable.
+**
+** Beginning with SQLite [version 3.31.0] ([dateof:3.31.0]) the input F
+** parameter can also be the name of a rollback journal file or WAL file
+** in addition to the main database file. Prior to version 3.31.0, these
+** routines would only work if F was the name of the main database file.
+** When the F parameter is the name of the rollback journal or WAL file,
+** it has access to all the same query parameters as were found on the
+** main database file.
+**
+** See the [URI filename] documentation for additional information.
+*/
+SQLITE_API const char *sqlite3_uri_parameter(sqlite3_filename z, const char *zParam);
+SQLITE_API int sqlite3_uri_boolean(sqlite3_filename z, const char *zParam, int bDefault);
+SQLITE_API sqlite3_int64 sqlite3_uri_int64(sqlite3_filename, const char*, sqlite3_int64);
+SQLITE_API const char *sqlite3_uri_key(sqlite3_filename z, int N);
+
+/*
+** CAPI3REF: Translate filenames
+**
+** These routines are available to [VFS|custom VFS implementations] for
+** translating filenames between the main database file, the journal file,
+** and the WAL file.
+**
+** If F is the name of an sqlite database file, journal file, or WAL file
+** passed by the SQLite core into the VFS, then sqlite3_filename_database(F)
+** returns the name of the corresponding database file.
+**
+** If F is the name of an sqlite database file, journal file, or WAL file
+** passed by the SQLite core into the VFS, or if F is a database filename
+** obtained from [sqlite3_db_filename()], then sqlite3_filename_journal(F)
+** returns the name of the corresponding rollback journal file.
+**
+** If F is the name of an sqlite database file, journal file, or WAL file
+** that was passed by the SQLite core into the VFS, or if F is a database
+** filename obtained from [sqlite3_db_filename()], then
+** sqlite3_filename_wal(F) returns the name of the corresponding
+** WAL file.
+**
+** In all of the above, if F is not the name of a database, journal or WAL
+** filename passed into the VFS from the SQLite core and F is not the
+** return value from [sqlite3_db_filename()], then the result is
+** undefined and is likely a memory access violation.
+*/
+SQLITE_API const char *sqlite3_filename_database(sqlite3_filename);
+SQLITE_API const char *sqlite3_filename_journal(sqlite3_filename);
+SQLITE_API const char *sqlite3_filename_wal(sqlite3_filename);
+
+/*
+** CAPI3REF: Database File Corresponding To A Journal
+**
+** ^If X is the name of a rollback or WAL-mode journal file that is
+** passed into the xOpen method of [sqlite3_vfs], then
+** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file]
+** object that represents the main database file.
+**
+** This routine is intended for use in custom [VFS] implementations
+** only. It is not a general-purpose interface.
+** The argument sqlite3_file_object(X) must be a filename pointer that
+** has been passed into [sqlite3_vfs].xOpen method where the
+** flags parameter to xOpen contains one of the bits
+** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use
+** of this routine results in undefined and probably undesirable
+** behavior.
*/
-SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
-SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
-SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
+SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
+/*
+** CAPI3REF: Create and Destroy VFS Filenames
+**
+** These interfaces are provided for use by [VFS shim] implementations and
+** are not useful outside of that context.
+**
+** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of
+** database filename D with corresponding journal file J and WAL file W and
+** with N URI parameters key/values pairs in the array P. The result from
+** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that
+** is safe to pass to routines like:
+** <ul>
+** <li> [sqlite3_uri_parameter()],
+** <li> [sqlite3_uri_boolean()],
+** <li> [sqlite3_uri_int64()],
+** <li> [sqlite3_uri_key()],
+** <li> [sqlite3_filename_database()],
+** <li> [sqlite3_filename_journal()], or
+** <li> [sqlite3_filename_wal()].
+** </ul>
+** If a memory allocation error occurs, sqlite3_create_filename() might
+** return a NULL pointer. The memory obtained from sqlite3_create_filename(X)
+** must be released by a corresponding call to sqlite3_free_filename(Y).
+**
+** The P parameter in sqlite3_create_filename(D,J,W,N,P) should be an array
+** of 2*N pointers to strings. Each pair of pointers in this array corresponds
+** to a key and value for a query parameter. The P parameter may be a NULL
+** pointer if N is zero. None of the 2*N pointers in the P array may be
+** NULL pointers and key pointers should not be empty strings.
+** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may
+** be NULL pointers, though they can be empty strings.
+**
+** The sqlite3_free_filename(Y) routine releases a memory allocation
+** previously obtained from sqlite3_create_filename(). Invoking
+** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op.
+**
+** If the Y parameter to sqlite3_free_filename(Y) is anything other
+** than a NULL pointer or a pointer previously acquired from
+** sqlite3_create_filename(), then bad things such as heap
+** corruption or segfaults may occur. The value Y should not be
+** used again after sqlite3_free_filename(Y) has been called. This means
+** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y,
+** then the corresponding [sqlite3_module.xClose() method should also be
+** invoked prior to calling sqlite3_free_filename(Y).
+*/
+SQLITE_API sqlite3_filename sqlite3_create_filename(
+ const char *zDatabase,
+ const char *zJournal,
+ const char *zWal,
+ int nParam,
+ const char **azParam
+);
+SQLITE_API void sqlite3_free_filename(sqlite3_filename);
/*
** CAPI3REF: Error Codes And Messages
** METHOD: sqlite3
**
-** ^If the most recent sqlite3_* API call associated with
+** ^If the most recent sqlite3_* API call associated with
** [database connection] D failed, then the sqlite3_errcode(D) interface
** returns the numeric [result code] or [extended result code] for that
** API call.
** ^The sqlite3_extended_errcode()
-** interface is the same except that it always returns the
+** interface is the same except that it always returns the
** [extended result code] even when extended result codes are
** disabled.
**
@@ -3430,27 +3943,38 @@ SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int
** sqlite3_extended_errcode() might change with each API call.
** Except, there are some interfaces that are guaranteed to never
** change the value of the error code. The error-code preserving
-** interfaces are:
+** interfaces include the following:
**
** <ul>
** <li> sqlite3_errcode()
** <li> sqlite3_extended_errcode()
** <li> sqlite3_errmsg()
** <li> sqlite3_errmsg16()
+** <li> sqlite3_error_offset()
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
-** text that describes the error, as either UTF-8 or UTF-16 respectively.
+** text that describes the error, as either UTF-8 or UTF-16 respectively,
+** or NULL if no error message is available.
+** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
-** ^The sqlite3_errstr() interface returns the English-language text
-** that describes the [result code], as UTF-8.
+** ^The sqlite3_errstr(E) interface returns the English-language text
+** that describes the [result code] E, as UTF-8, or NULL if E is not an
+** result code for which a text error message is available.
** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
+** ^If the most recent error references a specific token in the input
+** SQL, the sqlite3_error_offset() interface returns the byte offset
+** of the start of that token. ^The byte offset returned by
+** sqlite3_error_offset() assumes that the input SQL is UTF8.
+** ^If the most recent error does not reference a specific token in the input
+** SQL, then the sqlite3_error_offset() function returns -1.
+**
** When the serialized [threading mode] is in use, it might be the
** case that a second error occurs on a separate thread in between
** the time of the first error and the call to these interfaces.
@@ -3470,6 +3994,7 @@ SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
SQLITE_API const char *sqlite3_errstr(int);
+SQLITE_API int sqlite3_error_offset(sqlite3 *db);
/*
** CAPI3REF: Prepared Statement Object
@@ -3479,7 +4004,7 @@ SQLITE_API const char *sqlite3_errstr(int);
** has been compiled into binary form and is ready to be evaluated.
**
** Think of each SQL statement as a separate computer program. The
-** original SQL text is source code. A prepared statement object
+** original SQL text is source code. A prepared statement object
** is the compiled object code. All SQL must be converted into a
** prepared statement before it can be run.
**
@@ -3509,7 +4034,7 @@ typedef struct sqlite3_stmt sqlite3_stmt;
** new limit for that construct.)^
**
** ^If the new limit is a negative number, the limit is unchanged.
-** ^(For each limit category SQLITE_LIMIT_<i>NAME</i> there is a
+** ^(For each limit category SQLITE_LIMIT_<i>NAME</i> there is a
** [limits | hard upper bound]
** set at compile-time by a C preprocessor macro called
** [limits | SQLITE_MAX_<i>NAME</i>].
@@ -3517,7 +4042,7 @@ typedef struct sqlite3_stmt sqlite3_stmt;
** ^Attempts to increase a limit above its hard upper bound are
** silently truncated to the hard upper bound.
**
-** ^Regardless of whether or not the limit was changed, the
+** ^Regardless of whether or not the limit was changed, the
** [sqlite3_limit()] interface returns the prior value of the limit.
** ^Hence, to find the current value of a limit without changing it,
** simply invoke this interface with the third parameter set to -1.
@@ -3622,25 +4147,30 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** <dd>The SQLITE_PREPARE_PERSISTENT flag is a hint to the query planner
** that the prepared statement will be retained for a long time and
** probably reused many times.)^ ^Without this flag, [sqlite3_prepare_v3()]
-** and [sqlite3_prepare16_v3()] assume that the prepared statement will
+** and [sqlite3_prepare16_v3()] assume that the prepared statement will
** be used just once or at most a few times and then destroyed using
** [sqlite3_finalize()] relatively soon. The current implementation acts
** on this hint by avoiding the use of [lookaside memory] so as not to
** deplete the limited store of lookaside memory. Future versions of
** SQLite may act on this hint differently.
**
-** [[SQLITE_PREPARE_NORMALIZE]] ^(<dt>SQLITE_PREPARE_NORMALIZE</dt>
-** <dd>The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized
-** representation of the SQL statement should be calculated and then
-** associated with the prepared statement, which can be obtained via
-** the [sqlite3_normalized_sql()] interface.)^ The semantics used to
-** normalize a SQL statement are unspecified and subject to change.
-** At a minimum, literal values will be replaced with suitable
-** placeholders.
+** [[SQLITE_PREPARE_NORMALIZE]] <dt>SQLITE_PREPARE_NORMALIZE</dt>
+** <dd>The SQLITE_PREPARE_NORMALIZE flag is a no-op. This flag used
+** to be required for any prepared statement that wanted to use the
+** [sqlite3_normalized_sql()] interface. However, the
+** [sqlite3_normalized_sql()] interface is now available to all
+** prepared statements, regardless of whether or not they use this
+** flag.
+**
+** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
+** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
+** to return an error (error code SQLITE_ERROR) if the statement uses
+** any virtual tables.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
+#define SQLITE_PREPARE_NO_VTAB 0x04
/*
** CAPI3REF: Compiling An SQL Statement
@@ -3724,15 +4254,15 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** </li>
**
** <li>
-** ^If the specific value bound to [parameter | host parameter] in the
+** ^If the specific value bound to a [parameter | host parameter] in the
** WHERE clause might influence the choice of query plan for a statement,
-** then the statement will be automatically recompiled, as if there had been
-** a schema change, on the first [sqlite3_step()] call following any change
-** to the [sqlite3_bind_text | bindings] of that [parameter].
-** ^The specific value of WHERE-clause [parameter] might influence the
+** then the statement will be automatically recompiled, as if there had been
+** a schema change, on the first [sqlite3_step()] call following any change
+** to the [sqlite3_bind_text | bindings] of that [parameter].
+** ^The specific value of a WHERE-clause [parameter] might influence the
** choice of query plan if the parameter is the left-hand side of a [LIKE]
** or [GLOB] operator or if the parameter is compared to an indexed column
-** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
+** and the [SQLITE_ENABLE_STAT4] compile-time option is enabled.
** </li>
** </ol>
**
@@ -3822,12 +4352,17 @@ SQLITE_API int sqlite3_prepare16_v3(
** are managed by SQLite and are automatically freed when the prepared
** statement is finalized.
** ^The string returned by sqlite3_expanded_sql(P), on the other hand,
-** is obtained from [sqlite3_malloc()] and must be free by the application
+** is obtained from [sqlite3_malloc()] and must be freed by the application
** by passing it to [sqlite3_free()].
+**
+** ^The sqlite3_normalized_sql() interface is only available if
+** the [SQLITE_ENABLE_NORMALIZE] compile-time option is defined.
*/
SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
+#ifdef SQLITE_ENABLE_NORMALIZE
SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
+#endif
/*
** CAPI3REF: Determine If An SQL Statement Writes The Database
@@ -3838,8 +4373,8 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
** the content of the database file.
**
** Note that [application-defined SQL functions] or
-** [virtual tables] might change the database indirectly as a side effect.
-** ^(For example, if an application defines a function "eval()" that
+** [virtual tables] might change the database indirectly as a side effect.
+** ^(For example, if an application defines a function "eval()" that
** calls [sqlite3_exec()], then the following SQL statement would
** change the database file through side-effects:
**
@@ -3853,35 +4388,95 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK],
** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true,
** since the statements themselves do not actually modify the database but
-** rather they control the timing of when other statements modify the
+** rather they control the timing of when other statements modify the
** database. ^The [ATTACH] and [DETACH] statements also cause
** sqlite3_stmt_readonly() to return true since, while those statements
-** change the configuration of a database connection, they do not make
+** change the configuration of a database connection, they do not make
** changes to the content of the database files on disk.
** ^The sqlite3_stmt_readonly() interface returns true for [BEGIN] since
** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and
** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so
** sqlite3_stmt_readonly() returns false for those commands.
+**
+** ^This routine returns false if there is any possibility that the
+** statement might change the database file. ^A false return does
+** not guarantee that the statement will change the database file.
+** ^For example, an UPDATE statement might have a WHERE clause that
+** makes it a no-op, but the sqlite3_stmt_readonly() result would still
+** be false. ^Similarly, a CREATE TABLE IF NOT EXISTS statement is a
+** read-only no-op if the table already exists, but
+** sqlite3_stmt_readonly() still returns false for such a statement.
+**
+** ^If prepared statement X is an [EXPLAIN] or [EXPLAIN QUERY PLAN]
+** statement, then sqlite3_stmt_readonly(X) returns the same value as
+** if the EXPLAIN or EXPLAIN QUERY PLAN prefix were omitted.
*/
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
/*
+** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement
+** METHOD: sqlite3_stmt
+**
+** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the
+** prepared statement S is an EXPLAIN statement, or 2 if the
+** statement S is an EXPLAIN QUERY PLAN.
+** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is
+** an ordinary statement or a NULL pointer.
+*/
+SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt);
+
+/*
+** CAPI3REF: Change The EXPLAIN Setting For A Prepared Statement
+** METHOD: sqlite3_stmt
+**
+** The sqlite3_stmt_explain(S,E) interface changes the EXPLAIN
+** setting for [prepared statement] S. If E is zero, then S becomes
+** a normal prepared statement. If E is 1, then S behaves as if
+** its SQL text began with "[EXPLAIN]". If E is 2, then S behaves as if
+** its SQL text began with "[EXPLAIN QUERY PLAN]".
+**
+** Calling sqlite3_stmt_explain(S,E) might cause S to be reprepared.
+** SQLite tries to avoid a reprepare, but a reprepare might be necessary
+** on the first transition into EXPLAIN or EXPLAIN QUERY PLAN mode.
+**
+** Because of the potential need to reprepare, a call to
+** sqlite3_stmt_explain(S,E) will fail with SQLITE_ERROR if S cannot be
+** reprepared because it was created using [sqlite3_prepare()] instead of
+** the newer [sqlite3_prepare_v2()] or [sqlite3_prepare_v3()] interfaces and
+** hence has no saved SQL text with which to reprepare.
+**
+** Changing the explain setting for a prepared statement does not change
+** the original SQL text for the statement. Hence, if the SQL text originally
+** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0)
+** is called to convert the statement into an ordinary statement, the EXPLAIN
+** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S)
+** output, even though the statement now acts like a normal SQL statement.
+**
+** This routine returns SQLITE_OK if the explain mode is successfully
+** changed, or an error code if the explain mode could not be changed.
+** The explain mode cannot be changed while a statement is active.
+** Hence, it is good practice to call [sqlite3_reset(S)]
+** immediately prior to calling sqlite3_stmt_explain(S,E).
+*/
+SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode);
+
+/*
** CAPI3REF: Determine If A Prepared Statement Has Been Reset
** METHOD: sqlite3_stmt
**
** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the
-** [prepared statement] S has been stepped at least once using
+** [prepared statement] S has been stepped at least once using
** [sqlite3_step(S)] but has neither run to completion (returned
** [SQLITE_DONE] from [sqlite3_step(S)]) nor
** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S)
-** interface returns false if S is a NULL pointer. If S is not a
+** interface returns false if S is a NULL pointer. If S is not a
** NULL pointer and is not a pointer to a valid [prepared statement]
** object, then the behavior is undefined and probably undesirable.
**
** This interface can be used in combination [sqlite3_next_stmt()]
-** to locate all prepared statements associated with a database
+** to locate all prepared statements associated with a database
** connection that are in need of being reset. This can be used,
-** for example, in diagnostic routines to search for prepared
+** for example, in diagnostic routines to search for prepared
** statements that are holding a transaction open.
*/
SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
@@ -3900,7 +4495,7 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
** will accept either a protected or an unprotected sqlite3_value.
** Every interface that accepts sqlite3_value arguments specifies
** whether or not it requires a protected sqlite3_value. The
-** [sqlite3_value_dup()] interface can be used to construct a new
+** [sqlite3_value_dup()] interface can be used to construct a new
** protected sqlite3_value from an unprotected sqlite3_value.
**
** The terms "protected" and "unprotected" refer to whether or not
@@ -3908,7 +4503,7 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
** sqlite3_value object but no mutex is held for an unprotected
** sqlite3_value object. If SQLite is compiled to be single-threaded
** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0)
-** or if SQLite is run in one of reduced mutex modes
+** or if SQLite is run in one of reduced mutex modes
** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD]
** then there is no distinction between protected and unprotected
** sqlite3_value objects and they can be used interchangeably. However,
@@ -3918,6 +4513,8 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
**
** ^The sqlite3_value objects that are passed as parameters into the
** implementation of [application-defined SQL functions] are protected.
+** ^The sqlite3_value objects returned by [sqlite3_vtab_rhs_value()]
+** are protected.
** ^The sqlite3_value object returned by
** [sqlite3_column_value()] is unprotected.
** Unprotected sqlite3_value objects may only be used as arguments
@@ -3977,12 +4574,30 @@ typedef struct sqlite3_context sqlite3_context;
** [sqlite3_bind_parameter_index()] API if desired. ^The index
** for "?NNN" parameters is the value of NNN.
** ^The NNN value must be between 1 and the [sqlite3_limit()]
-** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999).
+** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 32766).
**
** ^The third argument is the value to bind to the parameter.
** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
** is ignored and the end result is the same as sqlite3_bind_null().
+** ^If the third parameter to sqlite3_bind_text() is not NULL, then
+** it should be a pointer to well-formed UTF8 text.
+** ^If the third parameter to sqlite3_bind_text16() is not NULL, then
+** it should be a pointer to well-formed UTF16 text.
+** ^If the third parameter to sqlite3_bind_text64() is not NULL, then
+** it should be a pointer to a well-formed unicode string that is
+** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16
+** otherwise.
+**
+** [[byte-order determination rules]] ^The byte-order of
+** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF)
+** found in first character, which is removed, or in the absence of a BOM
+** the byte order is the native byte order of the host
+** machine for sqlite3_bind_text16() or the byte order specified in
+** the 6th parameter for sqlite3_bind_text64().)^
+** ^If UTF16 input text contains invalid unicode
+** characters, then SQLite might change those invalid characters
+** into the unicode replacement character: U+FFFD.
**
** ^(In those routines that have a fourth argument, its value is the
** number of bytes in the parameter. To be clear: the value is the
@@ -3996,21 +4611,27 @@ typedef struct sqlite3_context sqlite3_context;
** or sqlite3_bind_text16() or sqlite3_bind_text64() then
** that parameter must be the byte offset
** where the NUL terminator would occur assuming the string were NUL
-** terminated. If any NUL characters occur at byte offsets less than
+** terminated. If any NUL characters occurs at byte offsets less than
** the value of the fourth parameter then the resulting string value will
** contain embedded NULs. The result of expressions involving strings
** with embedded NULs is undefined.
**
-** ^The fifth argument to the BLOB and string binding interfaces
-** is a destructor used to dispose of the BLOB or
-** string after SQLite has finished with it. ^The destructor is called
-** to dispose of the BLOB or string even if the call to bind API fails.
-** ^If the fifth argument is
-** the special value [SQLITE_STATIC], then SQLite assumes that the
-** information is in static, unmanaged space and does not need to be freed.
-** ^If the fifth argument has the value [SQLITE_TRANSIENT], then
-** SQLite makes its own private copy of the data immediately, before
-** the sqlite3_bind_*() routine returns.
+** ^The fifth argument to the BLOB and string binding interfaces controls
+** or indicates the lifetime of the object referenced by the third parameter.
+** These three options exist:
+** ^ (1) A destructor to dispose of the BLOB or string after SQLite has finished
+** with it may be passed. ^It is called to dispose of the BLOB or string even
+** if the call to the bind API fails, except the destructor is not called if
+** the third parameter is a NULL pointer or the fourth parameter is negative.
+** ^ (2) The special constant, [SQLITE_STATIC], may be passed to indicate that
+** the application remains responsible for disposing of the object. ^In this
+** case, the object and the provided pointer to it must remain valid until
+** either the prepared statement is finalized or the same SQL parameter is
+** bound to something else, whichever occurs sooner.
+** ^ (3) The constant, [SQLITE_TRANSIENT], may be passed to indicate that the
+** object is to be copied prior to the return from sqlite3_bind_*(). ^The
+** object and pointer to it must remain valid until then. ^SQLite will then
+** manage the lifetime of its private copy.
**
** ^The sixth argument to sqlite3_bind_text64() must be one of
** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]
@@ -4156,7 +4777,7 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*);
** METHOD: sqlite3_stmt
**
** ^Return the number of columns in the result set returned by the
-** [prepared statement]. ^If this routine returns 0, that means the
+** [prepared statement]. ^If this routine returns 0, that means the
** [prepared statement] returns no data (for example an [UPDATE]).
** ^However, just because this routine returns a positive number does not
** mean that one or more rows of data will be returned. ^A SELECT statement
@@ -4224,7 +4845,7 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N);
**
** ^If the Nth column returned by the statement is an expression or
** subquery and is not a column value, then all of these functions return
-** NULL. ^These routine might also return NULL if a memory allocation error
+** NULL. ^These routines might also return NULL if a memory allocation error
** occurs. ^Otherwise, they return the name of the attached database, table,
** or column that query result column was extracted from.
**
@@ -4234,10 +4855,6 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N);
** ^These APIs are only available if the library was compiled with the
** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol.
**
-** If two or more threads call one or more of these routines against the same
-** prepared statement and column at the same time then the results are
-** undefined.
-**
** If two or more threads call one or more
** [sqlite3_column_database_name | column metadata interfaces]
** for the same [prepared statement] and result column
@@ -4342,7 +4959,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
** For all versions of SQLite up to and including 3.6.23.1, a call to
** [sqlite3_reset()] was required after sqlite3_step() returned anything
** other than [SQLITE_ROW] before any subsequent invocation of
-** sqlite3_step(). Failure to reset the prepared statement using
+** sqlite3_step(). Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
** sqlite3_step() began
@@ -4374,7 +4991,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*);
** ^The sqlite3_data_count(P) interface returns the number of columns in the
** current row of the result set of [prepared statement] P.
** ^If prepared statement P does not have results ready to return
-** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of
+** (via calls to the [sqlite3_column_int | sqlite3_column()] family of
** interfaces) then sqlite3_data_count(P) returns 0.
** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer.
** ^The sqlite3_data_count(P) routine returns 0 if the previous call to
@@ -4433,7 +5050,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** <tr><td><b>sqlite3_column_int64</b><td>&rarr;<td>64-bit INTEGER result
** <tr><td><b>sqlite3_column_text</b><td>&rarr;<td>UTF-8 TEXT result
** <tr><td><b>sqlite3_column_text16</b><td>&rarr;<td>UTF-16 TEXT result
-** <tr><td><b>sqlite3_column_value</b><td>&rarr;<td>The result as an
+** <tr><td><b>sqlite3_column_value</b><td>&rarr;<td>The result as an
** [sqlite3_value|unprotected sqlite3_value] object.
** <tr><td>&nbsp;<td>&nbsp;<td>&nbsp;
** <tr><td><b>sqlite3_column_bytes</b><td>&rarr;<td>Size of a BLOB
@@ -4481,7 +5098,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** The return value of sqlite3_column_type() can be used to decide which
** of the first six interface should be used to extract the column value.
** The value returned by sqlite3_column_type() is only meaningful if no
-** automatic type conversions have occurred for the value in question.
+** automatic type conversions have occurred for the value in question.
** After a type conversion, the result of calling sqlite3_column_type()
** is undefined, though harmless. Future
** versions of SQLite may change the behavior of sqlite3_column_type()
@@ -4509,7 +5126,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** the number of bytes in that string.
** ^If the result is NULL, then sqlite3_column_bytes16() returns zero.
**
-** ^The values returned by [sqlite3_column_bytes()] and
+** ^The values returned by [sqlite3_column_bytes()] and
** [sqlite3_column_bytes16()] do not include the zero terminators at the end
** of the string. ^For clarity: the values returned by
** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of
@@ -4519,6 +5136,10 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** even empty strings, are always zero-terminated. ^The return
** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
**
+** ^Strings returned by sqlite3_column_text16() always have the endianness
+** which is native to the platform, regardless of the text encoding set
+** for the database.
+**
** <b>Warning:</b> ^The object returned by [sqlite3_column_value()] is an
** [unprotected sqlite3_value] object. In a multithreaded environment,
** an unprotected sqlite3_value object may only be used safely with
@@ -4528,11 +5149,11 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** to routines like [sqlite3_value_int()], [sqlite3_value_text()],
** or [sqlite3_value_bytes()], the behavior is not threadsafe.
** Hence, the sqlite3_column_value() interface
-** is normally only useful within the implementation of
+** is normally only useful within the implementation of
** [application-defined SQL functions] or [virtual tables], not within
** top-level application code.
**
-** The these routines may attempt to convert the datatype of the result.
+** These routines may attempt to convert the datatype of the result.
** ^For example, if the internal representation is FLOAT and a text result
** is requested, [sqlite3_snprintf()] is used internally to perform the
** conversion automatically. ^(The following table details the conversions
@@ -4557,7 +5178,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** <tr><td> TEXT <td> BLOB <td> No change
** <tr><td> BLOB <td> INTEGER <td> [CAST] to INTEGER
** <tr><td> BLOB <td> FLOAT <td> [CAST] to REAL
-** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed
+** <tr><td> BLOB <td> TEXT <td> [CAST] to TEXT, ensure zero terminator
** </table>
** </blockquote>)^
**
@@ -4681,32 +5302,43 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S
** back to the beginning of its program.
**
-** ^If the most recent call to [sqlite3_step(S)] for the
-** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE],
-** or if [sqlite3_step(S)] has never before been called on S,
-** then [sqlite3_reset(S)] returns [SQLITE_OK].
+** ^The return code from [sqlite3_reset(S)] indicates whether or not
+** the previous evaluation of prepared statement S completed successfully.
+** ^If [sqlite3_step(S)] has never before been called on S or if
+** [sqlite3_step(S)] has not been called since the previous call
+** to [sqlite3_reset(S)], then [sqlite3_reset(S)] will return
+** [SQLITE_OK].
**
** ^If the most recent call to [sqlite3_step(S)] for the
** [prepared statement] S indicated an error, then
** [sqlite3_reset(S)] returns an appropriate [error code].
+** ^The [sqlite3_reset(S)] interface might also return an [error code]
+** if there were no prior errors but the process of resetting
+** the prepared statement caused a new error. ^For example, if an
+** [INSERT] statement with a [RETURNING] clause is only stepped one time,
+** that one call to [sqlite3_step(S)] might return SQLITE_ROW but
+** the overall statement might still fail and the [sqlite3_reset(S)] call
+** might return SQLITE_BUSY if locking constraints prevent the
+** database change from committing. Therefore, it is important that
+** applications check the return code from [sqlite3_reset(S)] even if
+** no prior call to [sqlite3_step(S)] indicated a problem.
**
** ^The [sqlite3_reset(S)] interface does not change the values
** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S.
*/
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
+
/*
** CAPI3REF: Create Or Redefine SQL Functions
** KEYWORDS: {function creation routines}
-** KEYWORDS: {application-defined SQL function}
-** KEYWORDS: {application-defined SQL functions}
** METHOD: sqlite3
**
** ^These functions (collectively known as "function creation routines")
** are used to add SQL functions or aggregates or to redefine the behavior
** of existing SQL functions or aggregates. The only differences between
-** the three "sqlite3_create_function*" routines are the text encoding
-** expected for the second parameter (the name of the function being
+** the three "sqlite3_create_function*" routines are the text encoding
+** expected for the second parameter (the name of the function being
** created) and the presence or absence of a destructor callback for
** the application data pointer. Function sqlite3_create_window_function()
** is similar, but allows the user to supply the extra callback functions
@@ -4720,7 +5352,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** ^The second parameter is the name of the SQL function to be created or
** redefined. ^The length of the name is limited to 255 bytes in a UTF-8
** representation, exclusive of the zero-terminator. ^Note that the name
-** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes.
+** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes.
** ^Any attempt to create a function with a longer name
** will result in [SQLITE_MISUSE] being returned.
**
@@ -4735,7 +5367,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** ^The fourth parameter, eTextRep, specifies what
** [SQLITE_UTF8 | text encoding] this SQL function prefers for
** its parameters. The application should set this parameter to
-** [SQLITE_UTF16LE] if the function implementation invokes
+** [SQLITE_UTF16LE] if the function implementation invokes
** [sqlite3_value_text16le()] on an input, or [SQLITE_UTF16BE] if the
** implementation invokes [sqlite3_value_text16be()] on an input, or
** [SQLITE_UTF16] if [sqlite3_value_text16()] is used, or [SQLITE_UTF8]
@@ -4753,6 +5385,21 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** perform additional optimizations on deterministic functions, so use
** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
**
+** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY]
+** flag, which if present prevents the function from being invoked from
+** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions,
+** index expressions, or the WHERE clause of partial indexes.
+**
+** For best security, the [SQLITE_DIRECTONLY] flag is recommended for
+** all application-defined SQL functions that do not need to be
+** used inside of triggers, view, CHECK constraints, or other elements of
+** the database schema. This flags is especially recommended for SQL
+** functions that have side effects or reveal internal application state.
+** Without this flag, an attacker might be able to modify the schema of
+** a database file to include invocations of the function with parameters
+** chosen by the attacker, which the application will then execute when
+** the database file is opened and read.
+**
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
** function can gain access to this pointer using [sqlite3_user_data()].)^
**
@@ -4766,21 +5413,21 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** SQL function or aggregate, pass NULL pointers for all three function
** callbacks.
**
-** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue
+** ^The sixth, seventh, eighth and ninth parameters (xStep, xFinal, xValue
** and xInverse) passed to sqlite3_create_window_function are pointers to
** C-language callbacks that implement the new function. xStep and xFinal
** must both be non-NULL. xValue and xInverse may either both be NULL, in
-** which case a regular aggregate function is created, or must both be
+** which case a regular aggregate function is created, or must both be
** non-NULL, in which case the new function may be used as either an aggregate
** or aggregate window function. More details regarding the implementation
-** of aggregate window functions are
+** of aggregate window functions are
** [user-defined window functions|available here].
**
** ^(If the final parameter to sqlite3_create_function_v2() or
** sqlite3_create_window_function() is not NULL, then it is destructor for
-** the application data pointer. The destructor is invoked when the function
-** is deleted, either by being overloaded or when the database connection
-** closes.)^ ^The destructor is also invoked if the call to
+** the application data pointer. The destructor is invoked when the function
+** is deleted, either by being overloaded or when the database connection
+** closes.)^ ^The destructor is also invoked if the call to
** sqlite3_create_function_v2() fails. ^When the destructor callback is
** invoked, it is passed a single argument which is a copy of the application
** data pointer which was the fifth parameter to sqlite3_create_function_v2().
@@ -4793,7 +5440,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** nArg parameter is a better match than a function implementation with
** a negative nArg. ^A function where the preferred text encoding
** matches the database encoding is a better
-** match than a function where the encoding is different.
+** match than a function where the encoding is different.
** ^A function where the encoding difference is between UTF16le and UTF16be
** is a closer match than a function where the encoding difference is
** between UTF8 and UTF16.
@@ -4865,19 +5512,105 @@ SQLITE_API int sqlite3_create_window_function(
/*
** CAPI3REF: Function Flags
**
-** These constants may be ORed together with the
+** These constants may be ORed together with the
** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
** to [sqlite3_create_function()], [sqlite3_create_function16()], or
** [sqlite3_create_function_v2()].
+**
+** <dl>
+** [[SQLITE_DETERMINISTIC]] <dt>SQLITE_DETERMINISTIC</dt><dd>
+** The SQLITE_DETERMINISTIC flag means that the new function always gives
+** the same output when the input parameters are the same.
+** The [abs|abs() function] is deterministic, for example, but
+** [randomblob|randomblob()] is not. Functions must
+** be deterministic in order to be used in certain contexts such as
+** with the WHERE clause of [partial indexes] or in [generated columns].
+** SQLite might also optimize deterministic functions by factoring them
+** out of inner loops.
+** </dd>
+**
+** [[SQLITE_DIRECTONLY]] <dt>SQLITE_DIRECTONLY</dt><dd>
+** The SQLITE_DIRECTONLY flag means that the function may only be invoked
+** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in
+** schema structures such as [CHECK constraints], [DEFAULT clauses],
+** [expression indexes], [partial indexes], or [generated columns].
+** <p>
+** The SQLITE_DIRECTONLY flag is recommended for any
+** [application-defined SQL function]
+** that has side-effects or that could potentially leak sensitive information.
+** This will prevent attacks in which an application is tricked
+** into using a database file that has had its schema surreptitiously
+** modified to invoke the application-defined function in ways that are
+** harmful.
+** <p>
+** Some people say it is good practice to set SQLITE_DIRECTONLY on all
+** [application-defined SQL functions], regardless of whether or not they
+** are security sensitive, as doing so prevents those functions from being used
+** inside of the database schema, and thus ensures that the database
+** can be inspected and modified using generic tools (such as the [CLI])
+** that do not have access to the application-defined functions.
+** </dd>
+**
+** [[SQLITE_INNOCUOUS]] <dt>SQLITE_INNOCUOUS</dt><dd>
+** The SQLITE_INNOCUOUS flag means that the function is unlikely
+** to cause problems even if misused. An innocuous function should have
+** no side effects and should not depend on any values other than its
+** input parameters. The [abs|abs() function] is an example of an
+** innocuous function.
+** The [load_extension() SQL function] is not innocuous because of its
+** side effects.
+** <p> SQLITE_INNOCUOUS is similar to SQLITE_DETERMINISTIC, but is not
+** exactly the same. The [random|random() function] is an example of a
+** function that is innocuous but not deterministic.
+** <p>Some heightened security settings
+** ([SQLITE_DBCONFIG_TRUSTED_SCHEMA] and [PRAGMA trusted_schema=OFF])
+** disable the use of SQL functions inside views and triggers and in
+** schema structures such as [CHECK constraints], [DEFAULT clauses],
+** [expression indexes], [partial indexes], and [generated columns] unless
+** the function is tagged with SQLITE_INNOCUOUS. Most built-in functions
+** are innocuous. Developers are advised to avoid using the
+** SQLITE_INNOCUOUS flag for application-defined functions unless the
+** function has been carefully audited and found to be free of potentially
+** security-adverse side-effects and information-leaks.
+** </dd>
+**
+** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd>
+** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call
+** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
+** This flag instructs SQLite to omit some corner-case optimizations that
+** might disrupt the operation of the [sqlite3_value_subtype()] function,
+** causing it to return zero rather than the correct subtype().
+** SQL functions that invokes [sqlite3_value_subtype()] should have this
+** property. If the SQLITE_SUBTYPE property is omitted, then the return
+** value from [sqlite3_value_subtype()] might sometimes be zero even though
+** a non-zero subtype was specified by the function argument expression.
+**
+** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd>
+** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call
+** [sqlite3_result_subtype()] to cause a sub-type to be associated with its
+** result.
+** Every function that invokes [sqlite3_result_subtype()] should have this
+** property. If it does not, then the call to [sqlite3_result_subtype()]
+** might become a no-op if the function is used as term in an
+** [expression index]. On the other hand, SQL functions that never invoke
+** [sqlite3_result_subtype()] should avoid setting this property, as the
+** purpose of this property is to disable certain optimizations that are
+** incompatible with subtypes.
+** </dd>
+** </dl>
*/
-#define SQLITE_DETERMINISTIC 0x800
+#define SQLITE_DETERMINISTIC 0x000000800
+#define SQLITE_DIRECTONLY 0x000080000
+#define SQLITE_SUBTYPE 0x000100000
+#define SQLITE_INNOCUOUS 0x000200000
+#define SQLITE_RESULT_SUBTYPE 0x001000000
/*
** CAPI3REF: Deprecated Functions
** DEPRECATED
**
** These functions are [deprecated]. In order to maintain
-** backwards compatibility with older code, these functions continue
+** backwards compatibility with older code, these functions continue
** to be supported. However, new applications should avoid
** the use of these functions. To encourage programmers to avoid
** these functions, we will not explain what they do.
@@ -4921,14 +5654,16 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** <tr><td><b>sqlite3_value_nochange&nbsp;&nbsp;</b>
** <td>&rarr;&nbsp;&nbsp;<td>True if the column is unchanged in an UPDATE
** against a virtual table.
+** <tr><td><b>sqlite3_value_frombind&nbsp;&nbsp;</b>
+** <td>&rarr;&nbsp;&nbsp;<td>True if value originated from a [bound parameter]
** </table></blockquote>
**
** <b>Details:</b>
**
** These routines extract type, size, and content information from
** [protected sqlite3_value] objects. Protected sqlite3_value objects
-** are used to pass parameter information into implementation of
-** [application-defined SQL functions] and [virtual tables].
+** are used to pass parameter information into the functions that
+** implement [application-defined SQL functions] and [virtual tables].
**
** These routines work only with [protected sqlite3_value] objects.
** Any attempt to use these routines on an [unprotected sqlite3_value]
@@ -4943,11 +5678,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces
** extract UTF-16 strings as big-endian and little-endian respectively.
**
-** ^If [sqlite3_value] object V was initialized
+** ^If [sqlite3_value] object V was initialized
** using [sqlite3_bind_pointer(S,I,P,X,D)] or [sqlite3_result_pointer(C,P,X,D)]
** and if X and Y are strings that compare equal according to strcmp(X,Y),
** then sqlite3_value_pointer(V,Y) will return the pointer P. ^Otherwise,
-** sqlite3_value_pointer(V,Y) returns a NULL. The sqlite3_bind_pointer()
+** sqlite3_value_pointer(V,Y) returns a NULL. The sqlite3_bind_pointer()
** routine is part of the [pointer passing interface] added for SQLite 3.20.0.
**
** ^(The sqlite3_value_type(V) interface returns the
@@ -4982,6 +5717,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** than within an [xUpdate] method call for an UPDATE statement, then
** the return value is arbitrary and meaningless.
**
+** ^The sqlite3_value_frombind(X) interface returns non-zero if the
+** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()]
+** interfaces. ^If X comes from an SQL literal value, or a table column,
+** or an expression, then sqlite3_value_frombind(X) returns zero.
+**
** Please pay particular attention to the fact that the pointer returned
** from [sqlite3_value_blob()], [sqlite3_value_text()], or
** [sqlite3_value_text16()] can be invalidated by a subsequent call to
@@ -5027,6 +5767,29 @@ SQLITE_API int sqlite3_value_bytes16(sqlite3_value*);
SQLITE_API int sqlite3_value_type(sqlite3_value*);
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
+SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
+
+/*
+** CAPI3REF: Report the internal text encoding state of an sqlite3_value object
+** METHOD: sqlite3_value
+**
+** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8],
+** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding
+** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X)
+** returns something other than SQLITE_TEXT, then the return value from
+** sqlite3_value_encoding(X) is meaningless. ^Calls to
+** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)],
+** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or
+** [sqlite3_value_bytes16(X)] might change the encoding of the value X and
+** thus change the return from subsequent calls to sqlite3_value_encoding(X).
+**
+** This routine is intended for used by applications that test and validate
+** the SQLite implementation. This routine is inquiring about the opaque
+** internal state of an [sqlite3_value] object. Ordinary applications should
+** not need to know what the internal state of an sqlite3_value object is and
+** hence should not need to use this interface.
+*/
+SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
/*
** CAPI3REF: Finding The Subtype Of SQL Values
@@ -5037,6 +5800,12 @@ SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
** information can be used to pass a limited amount of context from
** one SQL function to another. Use the [sqlite3_result_subtype()]
** routine to set the subtype for the return value of an SQL function.
+**
+** Every [application-defined SQL function] that invoke this interface
+** should include the [SQLITE_SUBTYPE] property in the text
+** encoding argument when the function is [sqlite3_create_function|registered].
+** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype()
+** might return zero instead of the upstream subtype in some corner cases.
*/
SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
@@ -5048,7 +5817,8 @@ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
** object D and returns a pointer to that copy. ^The [sqlite3_value] returned
** is a [protected sqlite3_value] object even if the input is not.
** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
-** memory allocation fails.
+** memory allocation fails. ^If V is a [pointer value], then the result
+** of sqlite3_value_dup(V) is a NULL value.
**
** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object
** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
@@ -5064,9 +5834,9 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*);
** Implementations of aggregate SQL functions use this
** routine to allocate memory for storing their state.
**
-** ^The first time the sqlite3_aggregate_context(C,N) routine is called
-** for a particular aggregate function, SQLite
-** allocates N of memory, zeroes out that memory, and returns a pointer
+** ^The first time the sqlite3_aggregate_context(C,N) routine is called
+** for a particular aggregate function, SQLite allocates
+** N bytes of memory, zeroes out that memory, and returns a pointer
** to the new memory. ^On second and subsequent calls to
** sqlite3_aggregate_context() for the same aggregate function instance,
** the same buffer is returned. Sqlite3_aggregate_context() is normally
@@ -5077,19 +5847,19 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*);
** In those cases, sqlite3_aggregate_context() might be called for the
** first time from within xFinal().)^
**
-** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
+** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
** when first called if N is less than or equal to zero or if a memory
-** allocate error occurs.
+** allocation error occurs.
**
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
** determined by the N parameter on first successful call. Changing the
-** value of N in subsequent call to sqlite3_aggregate_context() within
+** value of N in any subsequent call to sqlite3_aggregate_context() within
** the same aggregate function instance will not resize the memory
** allocation.)^ Within the xFinal callback, it is customary to set
-** N=0 in calls to sqlite3_aggregate_context(C,N) so that no
+** N=0 in calls to sqlite3_aggregate_context(C,N) so that no
** pointless memory allocations occur.
**
-** ^SQLite automatically frees the memory allocated by
+** ^SQLite automatically frees the memory allocated by
** sqlite3_aggregate_context() when the aggregate query concludes.
**
** The first parameter must be a copy of the
@@ -5134,48 +5904,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** METHOD: sqlite3_context
**
** These functions may be used by (non-aggregate) SQL functions to
-** associate metadata with argument values. If the same value is passed to
-** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated metadata may be preserved. An example
-** of where this might be useful is in a regular-expression matching
-** function. The compiled version of the regular expression can be stored as
-** metadata associated with the pattern string.
+** associate auxiliary data with argument values. If the same argument
+** value is passed to multiple invocations of the same SQL function during
+** query execution, under some circumstances the associated auxiliary data
+** might be preserved. An example of where this might be useful is in a
+** regular-expression matching function. The compiled version of the regular
+** expression can be stored as auxiliary data associated with the pattern string.
** Then as long as the pattern string remains the same,
** the compiled regular expression can be reused on multiple
** invocations of the same function.
**
-** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata
+** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data
** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument
** value to the application-defined function. ^N is zero for the left-most
-** function argument. ^If there is no metadata
+** function argument. ^If there is no auxiliary data
** associated with the function argument, the sqlite3_get_auxdata(C,N) interface
** returns a NULL pointer.
**
-** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
-** argument of the application-defined function. ^Subsequent
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the
+** N-th argument of the application-defined function. ^Subsequent
** calls to sqlite3_get_auxdata(C,N) return P from the most recent
-** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
-** NULL if the metadata has been discarded.
+** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or
+** NULL if the auxiliary data has been discarded.
** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
** SQLite will invoke the destructor function X with parameter P exactly
-** once, when the metadata is discarded.
-** SQLite is free to discard the metadata at any time, including: <ul>
+** once, when the auxiliary data is discarded.
+** SQLite is free to discard the auxiliary data at any time, including: <ul>
** <li> ^(when the corresponding function parameter changes)^, or
** <li> ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
** SQL statement)^, or
** <li> ^(when sqlite3_set_auxdata() is invoked again on the same
** parameter)^, or
-** <li> ^(during the original sqlite3_set_auxdata() call when a memory
-** allocation error occurs.)^ </ul>
+** <li> ^(during the original sqlite3_set_auxdata() call when a memory
+** allocation error occurs.)^
+** <li> ^(during the original sqlite3_set_auxdata() call if the function
+** is evaluated during query planning instead of during query execution,
+** as sometimes happens with [SQLITE_ENABLE_STAT4].)^ </ul>
**
-** Note the last bullet in particular. The destructor X in
+** Note the last two bullets in particular. The destructor X in
** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
** should be called near the end of the function implementation and the
** function implementation should not make any use of P after
-** sqlite3_set_auxdata() has been called.
-**
-** ^(In practice, metadata is preserved between function calls for
+** sqlite3_set_auxdata() has been called. Furthermore, a call to
+** sqlite3_get_auxdata() that occurs immediately after a corresponding call
+** to sqlite3_set_auxdata() might still return NULL if an out-of-memory
+** condition occurred during the sqlite3_set_auxdata() call or if the
+** function is being evaluated during query planning rather than during
+** query execution.
+**
+** ^(In practice, auxiliary data is preserved between function calls for
** function parameters that are compile-time constants, including literal
** values and [parameters] and expressions composed from the same.)^
**
@@ -5185,10 +5963,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
**
** These routines must be called from the same thread in which
** the SQL function is running.
+**
+** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()].
*/
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N);
SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
+/*
+** CAPI3REF: Database Connection Client Data
+** METHOD: sqlite3
+**
+** These functions are used to associate one or more named pointers
+** with a [database connection].
+** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P
+** to be attached to [database connection] D using name N. Subsequent
+** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
+** or a NULL pointer if there were no prior calls to
+** sqlite3_set_clientdata() with the same values of D and N.
+** Names are compared using strcmp() and are thus case sensitive.
+**
+** If P and X are both non-NULL, then the destructor X is invoked with
+** argument P on the first of the following occurrences:
+** <ul>
+** <li> An out-of-memory error occurs during the call to
+** sqlite3_set_clientdata() which attempts to register pointer P.
+** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made
+** with the same D and N parameters.
+** <li> The database connection closes. SQLite does not make any guarantees
+** about the order in which destructors are called, only that all
+** destructors will be called exactly once at some point during the
+** database connection closing process.
+** </ul>
+**
+** SQLite does not do anything with client data other than invoke
+** destructors on the client data at the appropriate time. The intended
+** use for client data is to provide a mechanism for wrapper libraries
+** to store additional information about an SQLite database connection.
+**
+** There is no limit (other than available memory) on the number of different
+** client data pointers (with different names) that can be attached to a
+** single database connection. However, the implementation is optimized
+** for the case of having only one or two different client data names.
+** Applications and wrapper libraries are discouraged from using more than
+** one client data name each.
+**
+** There is no way to enumerate the client data pointers
+** associated with a database connection. The N parameter can be thought
+** of as a secret key such that only code that knows the secret key is able
+** to access the associated data.
+**
+** Security Warning: These interfaces should not be exposed in scripting
+** languages or in other circumstances where it might be possible for an
+** an attacker to invoke them. Any agent that can invoke these interfaces
+** can probably also take control of the process.
+**
+** Database connection client data is only available for SQLite
+** version 3.44.0 ([dateof:3.44.0]) and later.
+**
+** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()].
+*/
+SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*);
+SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*));
/*
** CAPI3REF: Constants Defining Special Destructor Behavior
@@ -5240,8 +6075,9 @@ typedef void (*sqlite3_destructor_type)(void*);
** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16()
** as the text of an error message. ^SQLite interprets the error
** message string from sqlite3_result_error() as UTF-8. ^SQLite
-** interprets the string from sqlite3_result_error16() as UTF-16 in native
-** byte order. ^If the third parameter to sqlite3_result_error()
+** interprets the string from sqlite3_result_error16() as UTF-16 using
+** the same [byte-order determination rules] as [sqlite3_bind_text16()].
+** ^If the third parameter to sqlite3_result_error()
** or sqlite3_result_error16() is negative then SQLite takes as the error
** message all text up through the first zero character.
** ^If the third parameter to sqlite3_result_error() or
@@ -5283,9 +6119,10 @@ typedef void (*sqlite3_destructor_type)(void*);
** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
** ^SQLite takes the text result from the application from
** the 2nd parameter of the sqlite3_result_text* interfaces.
-** ^If the 3rd parameter to the sqlite3_result_text* interfaces
-** is negative, then SQLite takes result text from the 2nd parameter
-** through the first zero character.
+** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces
+** other than sqlite3_result_text64() is negative, then SQLite computes
+** the string length itself by searching the 2nd parameter for the first
+** zero character.
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
** is non-negative, then as many bytes (not characters) of the text
** pointed to by the 2nd parameter are taken as the application-defined
@@ -5309,6 +6146,25 @@ typedef void (*sqlite3_destructor_type)(void*);
** then SQLite makes a copy of the result into space obtained
** from [sqlite3_malloc()] before it returns.
**
+** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and
+** sqlite3_result_text16be() routines, and for sqlite3_result_text64()
+** when the encoding is not UTF8, if the input UTF16 begins with a
+** byte-order mark (BOM, U+FEFF) then the BOM is removed from the
+** string and the rest of the string is interpreted according to the
+** byte-order specified by the BOM. ^The byte-order specified by
+** the BOM at the beginning of the text overrides the byte-order
+** specified by the interface procedure. ^So, for example, if
+** sqlite3_result_text16le() is invoked with text that begins
+** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the
+** first two bytes of input are skipped and the remaining input
+** is interpreted as UTF16BE text.
+**
+** ^For UTF16 input text to the sqlite3_result_text16(),
+** sqlite3_result_text16be(), sqlite3_result_text16le(), and
+** sqlite3_result_text64() routines, if the text contains invalid
+** UTF16 characters, the invalid characters might be converted
+** into the unicode replacement character, U+FFFD.
+**
** ^The sqlite3_result_value() interface sets the result of
** the application-defined function to be a copy of the
** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
@@ -5321,7 +6177,7 @@ typedef void (*sqlite3_destructor_type)(void*);
**
** ^The sqlite3_result_pointer(C,P,T,D) interface sets the result to an
** SQL NULL value, just like [sqlite3_result_null(C)], except that it
-** also associates the host-language pointer P or type T with that
+** also associates the host-language pointer P or type T with that
** NULL value such that the pointer can be retrieved within an
** [application-defined SQL function] using [sqlite3_value_pointer()].
** ^If the D parameter is not NULL, then it is a pointer to a destructor
@@ -5363,12 +6219,26 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
** METHOD: sqlite3_context
**
** The sqlite3_result_subtype(C,T) function causes the subtype of
-** the result from the [application-defined SQL function] with
-** [sqlite3_context] C to be the value T. Only the lower 8 bits
+** the result from the [application-defined SQL function] with
+** [sqlite3_context] C to be the value T. Only the lower 8 bits
** of the subtype T are preserved in current versions of SQLite;
** higher order bits are discarded.
** The number of subtype bytes preserved by SQLite might increase
** in future releases of SQLite.
+**
+** Every [application-defined SQL function] that invokes this interface
+** should include the [SQLITE_RESULT_SUBTYPE] property in its
+** text encoding argument when the SQL function is
+** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE]
+** property is omitted from the function that invokes sqlite3_result_subtype(),
+** then in some cases the sqlite3_result_subtype() might fail to set
+** the result subtype.
+**
+** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any
+** SQL function that invokes the sqlite3_result_subtype() interface
+** and that does not have the SQLITE_RESULT_SUBTYPE property will raise
+** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1
+** by default.
*/
SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
@@ -5394,7 +6264,7 @@ SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
** <li> [SQLITE_UTF16_ALIGNED].
** </ul>)^
** ^The eTextRep argument determines the encoding of strings passed
-** to the collating function callback, xCallback.
+** to the collating function callback, xCompare.
** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep
** force strings to be UTF16 with native byte order.
** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin
@@ -5403,18 +6273,19 @@ SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
** ^The fourth argument, pArg, is an application data pointer that is passed
** through as the first argument to the collating function callback.
**
-** ^The fifth argument, xCallback, is a pointer to the collating function.
+** ^The fifth argument, xCompare, is a pointer to the collating function.
** ^Multiple collating functions can be registered using the same name but
** with different eTextRep parameters and SQLite will use whichever
** function requires the least amount of data transformation.
-** ^If the xCallback argument is NULL then the collating function is
+** ^If the xCompare argument is NULL then the collating function is
** deleted. ^When all collating functions having the same name are deleted,
** that collation is no longer usable.
**
-** ^The collating function callback is invoked with a copy of the pArg
+** ^The collating function callback is invoked with a copy of the pArg
** application data pointer and with two strings in the encoding specified
-** by the eTextRep argument. The collating function must return an
-** integer that is negative, zero, or positive
+** by the eTextRep argument. The two integer parameters to the collating
+** function callback are the length of the two strings, in bytes. The collating
+** function must return an integer that is negative, zero, or positive
** if the first string is less than, equal to, or greater than the second,
** respectively. A collating function must always return the same answer
** given the same inputs. If two or more collating functions are registered
@@ -5431,7 +6302,7 @@ SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
** </ol>
**
** If a collating function fails any of the above constraints and that
-** collating function is registered and used, then the behavior of SQLite
+** collating function is registered and used, then the behavior of SQLite
** is undefined.
**
** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation()
@@ -5441,36 +6312,36 @@ SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
** calls to the collation creation functions or when the
** [database connection] is closed using [sqlite3_close()].
**
-** ^The xDestroy callback is <u>not</u> called if the
+** ^The xDestroy callback is <u>not</u> called if the
** sqlite3_create_collation_v2() function fails. Applications that invoke
-** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should
+** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should
** check the return code and dispose of the application data pointer
** themselves rather than expecting SQLite to deal with it for them.
-** This is different from every other SQLite interface. The inconsistency
-** is unfortunate but cannot be changed without breaking backwards
+** This is different from every other SQLite interface. The inconsistency
+** is unfortunate but cannot be changed without breaking backwards
** compatibility.
**
** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()].
*/
SQLITE_API int sqlite3_create_collation(
- sqlite3*,
- const char *zName,
- int eTextRep,
+ sqlite3*,
+ const char *zName,
+ int eTextRep,
void *pArg,
int(*xCompare)(void*,int,const void*,int,const void*)
);
SQLITE_API int sqlite3_create_collation_v2(
- sqlite3*,
- const char *zName,
- int eTextRep,
+ sqlite3*,
+ const char *zName,
+ int eTextRep,
void *pArg,
int(*xCompare)(void*,int,const void*,int,const void*),
void(*xDestroy)(void*)
);
SQLITE_API int sqlite3_create_collation16(
- sqlite3*,
+ sqlite3*,
const void *zName,
- int eTextRep,
+ int eTextRep,
void *pArg,
int(*xCompare)(void*,int,const void*,int,const void*)
);
@@ -5503,64 +6374,19 @@ SQLITE_API int sqlite3_create_collation16(
** [sqlite3_create_collation_v2()].
*/
SQLITE_API int sqlite3_collation_needed(
- sqlite3*,
- void*,
+ sqlite3*,
+ void*,
void(*)(void*,sqlite3*,int eTextRep,const char*)
);
SQLITE_API int sqlite3_collation_needed16(
- sqlite3*,
+ sqlite3*,
void*,
void(*)(void*,sqlite3*,int eTextRep,const void*)
);
-#ifdef SQLITE_HAS_CODEC
-/*
-** Specify the key for an encrypted database. This routine should be
-** called right after sqlite3_open().
-**
-** The code to implement this API is not available in the public release
-** of SQLite.
-*/
-SQLITE_API int sqlite3_key(
- sqlite3 *db, /* Database to be rekeyed */
- const void *pKey, int nKey /* The key */
-);
-SQLITE_API int sqlite3_key_v2(
- sqlite3 *db, /* Database to be rekeyed */
- const char *zDbName, /* Name of the database */
- const void *pKey, int nKey /* The key */
-);
-
-/*
-** Change the key on an open database. If the current database is not
-** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the
-** database is decrypted.
-**
-** The code to implement this API is not available in the public release
-** of SQLite.
-*/
-SQLITE_API int sqlite3_rekey(
- sqlite3 *db, /* Database to be rekeyed */
- const void *pKey, int nKey /* The new key */
-);
-SQLITE_API int sqlite3_rekey_v2(
- sqlite3 *db, /* Database to be rekeyed */
- const char *zDbName, /* Name of the database */
- const void *pKey, int nKey /* The new key */
-);
-
-/*
-** Specify the activation key for a SEE database. Unless
-** activated, none of the SEE routines will work.
-*/
-SQLITE_API void sqlite3_activate_see(
- const char *zPassPhrase /* Activation phrase */
-);
-#endif
-
#ifdef SQLITE_ENABLE_CEROD
/*
-** Specify the activation key for a CEROD database. Unless
+** Specify the activation key for a CEROD database. Unless
** activated, none of the CEROD routines will work.
*/
SQLITE_API void sqlite3_activate_cerod(
@@ -5584,6 +6410,13 @@ SQLITE_API void sqlite3_activate_cerod(
** of the default VFS is not implemented correctly, or not implemented at
** all, then the behavior of sqlite3_sleep() may deviate from the description
** in the previous paragraphs.
+**
+** If a negative argument is passed to sqlite3_sleep() the results vary by
+** VFS and operating system. Some system treat a negative argument as an
+** instruction to sleep forever. Others understand it to mean do not sleep
+** at all. ^In SQLite version 3.42.0 and later, a negative
+** argument passed into sqlite3_sleep() is changed to zero before it is relayed
+** down into the xSleep method of the VFS.
*/
SQLITE_API int sqlite3_sleep(int);
@@ -5616,7 +6449,7 @@ SQLITE_API int sqlite3_sleep(int);
** ^The [temp_store_directory pragma] may modify this variable and cause
** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore,
** the [temp_store_directory pragma] always assumes that any string
-** that this variable points to is held in memory obtained from
+** that this variable points to is held in memory obtained from
** [sqlite3_malloc] and the pragma may attempt to free that memory
** using [sqlite3_free].
** Hence, if this variable is modified directly, either it should be
@@ -5673,7 +6506,7 @@ SQLITE_API SQLITE_EXTERN char *sqlite3_temp_directory;
** ^The [data_store_directory pragma] may modify this variable and cause
** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore,
** the [data_store_directory pragma] always assumes that any string
-** that this variable points to is held in memory obtained from
+** that this variable points to is held in memory obtained from
** [sqlite3_malloc] and the pragma may attempt to free that memory
** using [sqlite3_free].
** Hence, if this variable is modified directly, either it should be
@@ -5755,21 +6588,58 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*);
SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
/*
+** CAPI3REF: Return The Schema Name For A Database Connection
+** METHOD: sqlite3
+**
+** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name
+** for the N-th database on database connection D, or a NULL pointer of N is
+** out of range. An N value of 0 means the main database file. An N of 1 is
+** the "temp" schema. Larger values of N correspond to various ATTACH-ed
+** databases.
+**
+** Space to hold the string that is returned by sqlite3_db_name() is managed
+** by SQLite itself. The string might be deallocated by any operation that
+** changes the schema, including [ATTACH] or [DETACH] or calls to
+** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that
+** occur on a different thread. Applications that need to
+** remember the string long-term should make their own copy. Applications that
+** are accessing the same database connection simultaneously on multiple
+** threads should mutex-protect calls to this API and should make their own
+** private copy of the result prior to releasing the mutex.
+*/
+SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N);
+
+/*
** CAPI3REF: Return The Filename For A Database Connection
** METHOD: sqlite3
**
-** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename
-** associated with database N of connection D. ^The main database file
-** has the name "main". If there is no attached database N on the database
+** ^The sqlite3_db_filename(D,N) interface returns a pointer to the filename
+** associated with database N of connection D.
+** ^If there is no attached database N on the database
** connection D, or if database N is a temporary or in-memory database, then
-** a NULL pointer is returned.
+** this function will return either a NULL pointer or an empty string.
+**
+** ^The string value returned by this routine is owned and managed by
+** the database connection. ^The value will be valid until the database N
+** is [DETACH]-ed or until the database connection closes.
**
** ^The filename returned by this function is the output of the
** xFullPathname method of the [VFS]. ^In other words, the filename
** will be an absolute pathname, even if the filename used
** to open the database originally was a URI or relative pathname.
+**
+** If the filename pointer returned by this routine is not NULL, then it
+** can be used as the filename input parameter to these routines:
+** <ul>
+** <li> [sqlite3_uri_parameter()]
+** <li> [sqlite3_uri_boolean()]
+** <li> [sqlite3_uri_int64()]
+** <li> [sqlite3_filename_database()]
+** <li> [sqlite3_filename_journal()]
+** <li> [sqlite3_filename_wal()]
+** </ul>
*/
-SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
+SQLITE_API sqlite3_filename sqlite3_db_filename(sqlite3 *db, const char *zDbName);
/*
** CAPI3REF: Determine if a database is read-only
@@ -5782,6 +6652,57 @@ SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
/*
+** CAPI3REF: Determine the transaction state of a database
+** METHOD: sqlite3
+**
+** ^The sqlite3_txn_state(D,S) interface returns the current
+** [transaction state] of schema S in database connection D. ^If S is NULL,
+** then the highest transaction state of any schema on database connection D
+** is returned. Transaction states are (in order of lowest to highest):
+** <ol>
+** <li value="0"> SQLITE_TXN_NONE
+** <li value="1"> SQLITE_TXN_READ
+** <li value="2"> SQLITE_TXN_WRITE
+** </ol>
+** ^If the S argument to sqlite3_txn_state(D,S) is not the name of
+** a valid schema, then -1 is returned.
+*/
+SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema);
+
+/*
+** CAPI3REF: Allowed return values from sqlite3_txn_state()
+** KEYWORDS: {transaction state}
+**
+** These constants define the current transaction state of a database file.
+** ^The [sqlite3_txn_state(D,S)] interface returns one of these
+** constants in order to describe the transaction state of schema S
+** in [database connection] D.
+**
+** <dl>
+** [[SQLITE_TXN_NONE]] <dt>SQLITE_TXN_NONE</dt>
+** <dd>The SQLITE_TXN_NONE state means that no transaction is currently
+** pending.</dd>
+**
+** [[SQLITE_TXN_READ]] <dt>SQLITE_TXN_READ</dt>
+** <dd>The SQLITE_TXN_READ state means that the database is currently
+** in a read transaction. Content has been read from the database file
+** but nothing in the database file has changed. The transaction state
+** will advanced to SQLITE_TXN_WRITE if any changes occur and there are
+** no other conflicting concurrent write transactions. The transaction
+** state will revert to SQLITE_TXN_NONE following a [ROLLBACK] or
+** [COMMIT].</dd>
+**
+** [[SQLITE_TXN_WRITE]] <dt>SQLITE_TXN_WRITE</dt>
+** <dd>The SQLITE_TXN_WRITE state means that the database is currently
+** in a write transaction. Content has been written to the database file
+** but has not yet committed. The transaction state will change to
+** to SQLITE_TXN_NONE at the next [ROLLBACK] or [COMMIT].</dd>
+*/
+#define SQLITE_TXN_NONE 0
+#define SQLITE_TXN_READ 1
+#define SQLITE_TXN_WRITE 2
+
+/*
** CAPI3REF: Find the next prepared statement
** METHOD: sqlite3
**
@@ -5848,6 +6769,72 @@ SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
/*
+** CAPI3REF: Autovacuum Compaction Amount Callback
+** METHOD: sqlite3
+**
+** ^The sqlite3_autovacuum_pages(D,C,P,X) interface registers a callback
+** function C that is invoked prior to each autovacuum of the database
+** file. ^The callback is passed a copy of the generic data pointer (P),
+** the schema-name of the attached database that is being autovacuumed,
+** the size of the database file in pages, the number of free pages,
+** and the number of bytes per page, respectively. The callback should
+** return the number of free pages that should be removed by the
+** autovacuum. ^If the callback returns zero, then no autovacuum happens.
+** ^If the value returned is greater than or equal to the number of
+** free pages, then a complete autovacuum happens.
+**
+** <p>^If there are multiple ATTACH-ed database files that are being
+** modified as part of a transaction commit, then the autovacuum pages
+** callback is invoked separately for each file.
+**
+** <p><b>The callback is not reentrant.</b> The callback function should
+** not attempt to invoke any other SQLite interface. If it does, bad
+** things may happen, including segmentation faults and corrupt database
+** files. The callback function should be a simple function that
+** does some arithmetic on its input parameters and returns a result.
+**
+** ^The X parameter to sqlite3_autovacuum_pages(D,C,P,X) is an optional
+** destructor for the P parameter. ^If X is not NULL, then X(P) is
+** invoked whenever the database connection closes or when the callback
+** is overwritten by another invocation of sqlite3_autovacuum_pages().
+**
+** <p>^There is only one autovacuum pages callback per database connection.
+** ^Each call to the sqlite3_autovacuum_pages() interface overrides all
+** previous invocations for that database connection. ^If the callback
+** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer,
+** then the autovacuum steps callback is canceled. The return value
+** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might
+** be some other error code if something goes wrong. The current
+** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other
+** return codes might be added in future releases.
+**
+** <p>If no autovacuum pages callback is specified (the usual case) or
+** a NULL pointer is provided for the callback,
+** then the default behavior is to vacuum all free pages. So, in other
+** words, the default behavior is the same as if the callback function
+** were something like this:
+**
+** <blockquote><pre>
+** &nbsp; unsigned int demonstration_autovac_pages_callback(
+** &nbsp; void *pClientData,
+** &nbsp; const char *zSchema,
+** &nbsp; unsigned int nDbPage,
+** &nbsp; unsigned int nFreePage,
+** &nbsp; unsigned int nBytePerPage
+** &nbsp; ){
+** &nbsp; return nFreePage;
+** &nbsp; }
+** </pre></blockquote>
+*/
+SQLITE_API int sqlite3_autovacuum_pages(
+ sqlite3 *db,
+ unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
+ void*,
+ void(*)(void*)
+);
+
+
+/*
** CAPI3REF: Data Change Notification Callbacks
** METHOD: sqlite3
**
@@ -5871,7 +6858,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** ^In the case of an update, this is the [rowid] after the update takes place.
**
** ^(The update hook is not invoked when internal system tables are
-** modified (i.e. sqlite_master and sqlite_sequence).)^
+** modified (i.e. sqlite_sequence).)^
** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified.
**
** ^In the current implementation, the update hook
@@ -5897,7 +6884,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** and [sqlite3_preupdate_hook()] interfaces.
*/
SQLITE_API void *sqlite3_update_hook(
- sqlite3*,
+ sqlite3*,
void(*)(void *,int ,char const *,char const *,sqlite3_int64),
void*
);
@@ -5910,26 +6897,35 @@ SQLITE_API void *sqlite3_update_hook(
** to the same database. Sharing is enabled if the argument is true
** and disabled if the argument is false.)^
**
+** This interface is omitted if SQLite is compiled with
+** [-DSQLITE_OMIT_SHARED_CACHE]. The [-DSQLITE_OMIT_SHARED_CACHE]
+** compile-time option is recommended because the
+** [use of shared cache mode is discouraged].
+**
** ^Cache sharing is enabled and disabled for an entire process.
-** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
+** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
** In prior versions of SQLite,
** sharing was enabled or disabled for each thread separately.
**
** ^(The cache sharing mode set by this interface effects all subsequent
** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()].
-** Existing database connections continue use the sharing mode
+** Existing database connections continue to use the sharing mode
** that was in effect at the time they were opened.)^
**
** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled
** successfully. An [error code] is returned otherwise.)^
**
-** ^Shared cache is disabled by default. But this might change in
-** future releases of SQLite. Applications that care about shared
-** cache setting should set it explicitly.
+** ^Shared cache is disabled by default. It is recommended that it stay
+** that way. In other words, do not use this routine. This interface
+** continues to be provided for historical compatibility, but its use is
+** discouraged. Any use of shared cache is discouraged. If shared cache
+** must be used, it is recommended that shared cache only be enabled for
+** individual database connections using the [sqlite3_open_v2()] interface
+** with the [SQLITE_OPEN_SHAREDCACHE] flag.
**
** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0
-** and will always return SQLITE_MISUSE. On those systems,
-** shared cache mode should be enabled per-database connection via
+** and will always return SQLITE_MISUSE. On those systems,
+** shared cache mode should be enabled per-database connection via
** [sqlite3_open_v2()] with [SQLITE_OPEN_SHAREDCACHE].
**
** This interface is threadsafe on processors where writing a
@@ -5972,6 +6968,9 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
/*
** CAPI3REF: Impose A Limit On Heap Size
**
+** These interfaces impose limits on the amount of heap memory that will be
+** by all database connections within a single process.
+**
** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the
** soft limit on the amount of heap memory that may be allocated by SQLite.
** ^SQLite strives to keep heap memory utilization below the soft heap
@@ -5979,23 +6978,44 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
** as heap memory usages approaches the limit.
** ^The soft heap limit is "soft" because even though SQLite strives to stay
** below the limit, it will exceed the limit rather than generate
-** an [SQLITE_NOMEM] error. In other words, the soft heap limit
+** an [SQLITE_NOMEM] error. In other words, the soft heap limit
** is advisory only.
**
-** ^The return value from sqlite3_soft_heap_limit64() is the size of
-** the soft heap limit prior to the call, or negative in the case of an
-** error. ^If the argument N is negative
-** then no change is made to the soft heap limit. Hence, the current
-** size of the soft heap limit can be determined by invoking
-** sqlite3_soft_heap_limit64() with a negative argument.
-**
-** ^If the argument N is zero then the soft heap limit is disabled.
+** ^The sqlite3_hard_heap_limit64(N) interface sets a hard upper bound of
+** N bytes on the amount of memory that will be allocated. ^The
+** sqlite3_hard_heap_limit64(N) interface is similar to
+** sqlite3_soft_heap_limit64(N) except that memory allocations will fail
+** when the hard heap limit is reached.
**
-** ^(The soft heap limit is not enforced in the current implementation
+** ^The return value from both sqlite3_soft_heap_limit64() and
+** sqlite3_hard_heap_limit64() is the size of
+** the heap limit prior to the call, or negative in the case of an
+** error. ^If the argument N is negative
+** then no change is made to the heap limit. Hence, the current
+** size of heap limits can be determined by invoking
+** sqlite3_soft_heap_limit64(-1) or sqlite3_hard_heap_limit(-1).
+**
+** ^Setting the heap limits to zero disables the heap limiter mechanism.
+**
+** ^The soft heap limit may not be greater than the hard heap limit.
+** ^If the hard heap limit is enabled and if sqlite3_soft_heap_limit(N)
+** is invoked with a value of N that is greater than the hard heap limit,
+** the soft heap limit is set to the value of the hard heap limit.
+** ^The soft heap limit is automatically enabled whenever the hard heap
+** limit is enabled. ^When sqlite3_hard_heap_limit64(N) is invoked and
+** the soft heap limit is outside the range of 1..N, then the soft heap
+** limit is set to N. ^Invoking sqlite3_soft_heap_limit64(0) when the
+** hard heap limit is enabled makes the soft heap limit equal to the
+** hard heap limit.
+**
+** The memory allocation limits can also be adjusted using
+** [PRAGMA soft_heap_limit] and [PRAGMA hard_heap_limit].
+**
+** ^(The heap limits are not enforced in the current implementation
** if one or more of following conditions are true:
**
** <ul>
-** <li> The soft heap limit is set to zero.
+** <li> The limit value is set to zero.
** <li> Memory accounting is disabled using a combination of the
** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and
** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option.
@@ -6006,21 +7026,11 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
** from the heap.
** </ul>)^
**
-** Beginning with SQLite [version 3.7.3] ([dateof:3.7.3]),
-** the soft heap limit is enforced
-** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT]
-** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT],
-** the soft heap limit is enforced on every memory allocation. Without
-** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced
-** when memory is allocated by the page cache. Testing suggests that because
-** the page cache is the predominate memory user in SQLite, most
-** applications will achieve adequate soft heap limit enforcement without
-** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT].
-**
-** The circumstances under which SQLite will enforce the soft heap limit may
+** The circumstances under which SQLite will enforce the heap limits may
** changes in future releases of SQLite.
*/
SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N);
+SQLITE_API sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 N);
/*
** CAPI3REF: Deprecated Soft Heap Limit Interface
@@ -6044,7 +7054,7 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
** interface returns SQLITE_OK and fills in the non-NULL pointers in
** the final five arguments with appropriate values if the specified
** column exists. ^The sqlite3_table_column_metadata() interface returns
-** SQLITE_ERROR and if the specified column does not exist.
+** SQLITE_ERROR if the specified column does not exist.
** ^If the column-name parameter to sqlite3_table_column_metadata() is a
** NULL pointer, then this routine simply checks for the existence of the
** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it
@@ -6084,7 +7094,7 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
**
** ^If the specified table is actually a view, an [error code] is returned.
**
-** ^If the specified column is "rowid", "oid" or "_rowid_" and the table
+** ^If the specified column is "rowid", "oid" or "_rowid_" and the table
** is not a [WITHOUT ROWID] table and an
** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output
** parameters are set for the explicitly declared column. ^(If there is no
@@ -6150,7 +7160,7 @@ SQLITE_API int sqlite3_table_column_metadata(
** prior to calling this API,
** otherwise an error will be returned.
**
-** <b>Security warning:</b> It is recommended that the
+** <b>Security warning:</b> It is recommended that the
** [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method be used to enable only this
** interface. The use of the [sqlite3_enable_load_extension()] interface
** should be avoided. This will keep the SQL function [load_extension()]
@@ -6186,7 +7196,7 @@ SQLITE_API int sqlite3_load_extension(
** to enable or disable only the C-API.)^
**
** <b>Security warning:</b> It is recommended that extension loading
-** be disabled using the [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method
+** be enabled using the [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method
** rather than this interface, so the [load_extension()] SQL function
** remains disabled. This will prevent SQL injections from giving attackers
** access to extension loading capabilities.
@@ -6237,7 +7247,7 @@ SQLITE_API int sqlite3_auto_extension(void(*xEntryPoint)(void));
** ^The [sqlite3_cancel_auto_extension(X)] interface unregisters the
** initialization routine X that was registered using a prior call to
** [sqlite3_auto_extension(X)]. ^The [sqlite3_cancel_auto_extension(X)]
-** routine returns 1 if initialization routine X was successfully
+** routine returns 1 if initialization routine X was successfully
** unregistered and it returns 0 if X was not on the list of initialization
** routines.
*/
@@ -6252,15 +7262,6 @@ SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
SQLITE_API void sqlite3_reset_auto_extension(void);
/*
-** The interface to the virtual-table mechanism is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stabilizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-*/
-
-/*
** Structures used by the virtual table interface
*/
typedef struct sqlite3_vtab sqlite3_vtab;
@@ -6272,8 +7273,8 @@ typedef struct sqlite3_module sqlite3_module;
** CAPI3REF: Virtual Table Object
** KEYWORDS: sqlite3_module {virtual table module}
**
-** This structure, sometimes called a "virtual table module",
-** defines the implementation of a [virtual tables].
+** This structure, sometimes called a "virtual table module",
+** defines the implementation of a [virtual table].
** This structure consists mostly of methods for the module.
**
** ^A virtual table module is created by filling in a persistent
@@ -6312,7 +7313,7 @@ struct sqlite3_module {
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
void **ppArg);
int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
- /* The methods above are in version 1 of the sqlite_module object. Those
+ /* The methods above are in version 1 of the sqlite_module object. Those
** below are for version 2 and greater. */
int (*xSavepoint)(sqlite3_vtab *pVTab, int);
int (*xRelease)(sqlite3_vtab *pVTab, int);
@@ -6320,6 +7321,10 @@ struct sqlite3_module {
/* The methods above are in versions 1 and 2 of the sqlite_module object.
** Those below are for version 3 and greater. */
int (*xShadowName)(const char*);
+ /* The methods above are in versions 1 through 3 of the sqlite_module object.
+ ** Those below are for version 4 and greater. */
+ int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
+ const char *zTabName, int mFlags, char **pzErr);
};
/*
@@ -6362,7 +7367,7 @@ struct sqlite3_module {
** required by SQLite. If the table has at least 64 columns and any column
** to the right of the first 63 is required, then bit 63 of colUsed is also
** set. In other words, column iCol may be required if the expression
-** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to
+** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to
** non-zero.
**
** The [xBestIndex] method must fill aConstraintUsage[] with information
@@ -6370,12 +7375,18 @@ struct sqlite3_module {
** the right-hand side of the corresponding aConstraint[] is evaluated
** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit
** is true, then the constraint is assumed to be fully handled by the
-** virtual table and is not checked again by SQLite.)^
-**
-** ^The idxNum and idxPtr values are recorded and passed into the
+** virtual table and might not be checked again by the byte code.)^ ^(The
+** aConstraintUsage[].omit flag is an optimization hint. When the omit flag
+** is left in its default setting of false, the constraint will always be
+** checked separately in byte code. If the omit flag is change to true, then
+** the constraint may or may not be checked in byte code. In other words,
+** when the omit flag is true there is no guarantee that the constraint will
+** not be checked again using byte code.)^
+**
+** ^The idxNum and idxStr values are recorded and passed into the
** [xFilter] method.
-** ^[sqlite3_free()] is used to free idxPtr if and only if
-** needToFreeIdxPtr is true.
+** ^[sqlite3_free()] is used to free idxStr if and only if
+** needToFreeIdxStr is true.
**
** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in
** the correct order to satisfy the ORDER BY clause so that no separate
@@ -6383,17 +7394,17 @@ struct sqlite3_module {
**
** ^The estimatedCost value is an estimate of the cost of a particular
** strategy. A cost of N indicates that the cost of the strategy is similar
-** to a linear scan of an SQLite table with N rows. A cost of log(N)
+** to a linear scan of an SQLite table with N rows. A cost of log(N)
** indicates that the expense of the operation is similar to that of a
** binary search on a unique indexed field of an SQLite table with N rows.
**
** ^The estimatedRows value is an estimate of the number of rows that
** will be returned by the strategy.
**
-** The xBestIndex method may optionally populate the idxFlags field with a
+** The xBestIndex method may optionally populate the idxFlags field with a
** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag -
** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
-** assumes that the strategy may visit at most one row.
+** assumes that the strategy may visit at most one row.
**
** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then
** SQLite also assumes that if a call to the xUpdate() method is made as
@@ -6406,14 +7417,14 @@ struct sqlite3_module {
** the xUpdate method are automatically rolled back by SQLite.
**
** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
-** structure for SQLite [version 3.8.2] ([dateof:3.8.2]).
+** structure for SQLite [version 3.8.2] ([dateof:3.8.2]).
** If a virtual table extension is
-** used with an SQLite version earlier than 3.8.2, the results of attempting
-** to read or write the estimatedRows field are undefined (but are likely
-** to included crashing the application). The estimatedRows field should
+** used with an SQLite version earlier than 3.8.2, the results of attempting
+** to read or write the estimatedRows field are undefined (but are likely
+** to include crashing the application). The estimatedRows field should
** therefore only be used if [sqlite3_libversion_number()] returns a
** value greater than or equal to 3008002. Similarly, the idxFlags field
-** was added for [version 3.9.0] ([dateof:3.9.0]).
+** was added for [version 3.9.0] ([dateof:3.9.0]).
** It may therefore only be used if
** sqlite3_libversion_number() returns a value greater than or equal to
** 3009000.
@@ -6453,7 +7464,7 @@ struct sqlite3_index_info {
/*
** CAPI3REF: Virtual Table Scan Flags
**
-** Virtual table implementations are allowed to set the
+** Virtual table implementations are allowed to set the
** [sqlite3_index_info].idxFlags field to some combination of
** these bits.
*/
@@ -6462,26 +7473,58 @@ struct sqlite3_index_info {
/*
** CAPI3REF: Virtual Table Constraint Operator Codes
**
-** These macros defined the allowed values for the
+** These macros define the allowed values for the
** [sqlite3_index_info].aConstraint[].op field. Each value represents
-** an operator that is part of a constraint term in the wHERE clause of
+** an operator that is part of a constraint term in the WHERE clause of
** a query that uses a [virtual table].
-*/
-#define SQLITE_INDEX_CONSTRAINT_EQ 2
-#define SQLITE_INDEX_CONSTRAINT_GT 4
-#define SQLITE_INDEX_CONSTRAINT_LE 8
-#define SQLITE_INDEX_CONSTRAINT_LT 16
-#define SQLITE_INDEX_CONSTRAINT_GE 32
-#define SQLITE_INDEX_CONSTRAINT_MATCH 64
-#define SQLITE_INDEX_CONSTRAINT_LIKE 65
-#define SQLITE_INDEX_CONSTRAINT_GLOB 66
-#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
-#define SQLITE_INDEX_CONSTRAINT_NE 68
-#define SQLITE_INDEX_CONSTRAINT_ISNOT 69
-#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
-#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
-#define SQLITE_INDEX_CONSTRAINT_IS 72
-#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
+**
+** ^The left-hand operand of the operator is given by the corresponding
+** aConstraint[].iColumn field. ^An iColumn of -1 indicates the left-hand
+** operand is the rowid.
+** The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET
+** operators have no left-hand operand, and so for those operators the
+** corresponding aConstraint[].iColumn is meaningless and should not be
+** used.
+**
+** All operator values from SQLITE_INDEX_CONSTRAINT_FUNCTION through
+** value 255 are reserved to represent functions that are overloaded
+** by the [xFindFunction|xFindFunction method] of the virtual table
+** implementation.
+**
+** The right-hand operands for each constraint might be accessible using
+** the [sqlite3_vtab_rhs_value()] interface. Usually the right-hand
+** operand is only available if it appears as a single constant literal
+** in the input SQL. If the right-hand operand is another column or an
+** expression (even a constant expression) or a parameter, then the
+** sqlite3_vtab_rhs_value() probably will not be able to extract it.
+** ^The SQLITE_INDEX_CONSTRAINT_ISNULL and
+** SQLITE_INDEX_CONSTRAINT_ISNOTNULL operators have no right-hand operand
+** and hence calls to sqlite3_vtab_rhs_value() for those operators will
+** always return SQLITE_NOTFOUND.
+**
+** The collating sequence to be used for comparison can be found using
+** the [sqlite3_vtab_collation()] interface. For most real-world virtual
+** tables, the collating sequence of constraints does not matter (for example
+** because the constraints are numeric) and so the sqlite3_vtab_collation()
+** interface is not commonly needed.
+*/
+#define SQLITE_INDEX_CONSTRAINT_EQ 2
+#define SQLITE_INDEX_CONSTRAINT_GT 4
+#define SQLITE_INDEX_CONSTRAINT_LE 8
+#define SQLITE_INDEX_CONSTRAINT_LT 16
+#define SQLITE_INDEX_CONSTRAINT_GE 32
+#define SQLITE_INDEX_CONSTRAINT_MATCH 64
+#define SQLITE_INDEX_CONSTRAINT_LIKE 65
+#define SQLITE_INDEX_CONSTRAINT_GLOB 66
+#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
+#define SQLITE_INDEX_CONSTRAINT_NE 68
+#define SQLITE_INDEX_CONSTRAINT_ISNOT 69
+#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70
+#define SQLITE_INDEX_CONSTRAINT_ISNULL 71
+#define SQLITE_INDEX_CONSTRAINT_IS 72
+#define SQLITE_INDEX_CONSTRAINT_LIMIT 73
+#define SQLITE_INDEX_CONSTRAINT_OFFSET 74
+#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150
/*
** CAPI3REF: Register A Virtual Table Implementation
@@ -6493,7 +7536,7 @@ struct sqlite3_index_info {
** preexisting [virtual table] for the module.
**
** ^The module name is registered on the [database connection] specified
-** by the first parameter. ^The name of the module is given by the
+** by the first parameter. ^The name of the module is given by the
** second parameter. ^The third parameter is a pointer to
** the implementation of the [virtual table module]. ^The fourth
** parameter is an arbitrary client data pointer that is passed through
@@ -6508,6 +7551,12 @@ struct sqlite3_index_info {
** ^The sqlite3_create_module()
** interface is equivalent to sqlite3_create_module_v2() with a NULL
** destructor.
+**
+** ^If the third parameter (the pointer to the sqlite3_module object) is
+** NULL then no new module is created and any existing modules with the
+** same name are dropped.
+**
+** See also: [sqlite3_drop_modules()]
*/
SQLITE_API int sqlite3_create_module(
sqlite3 *db, /* SQLite connection to register module with */
@@ -6524,6 +7573,23 @@ SQLITE_API int sqlite3_create_module_v2(
);
/*
+** CAPI3REF: Remove Unnecessary Virtual Table Implementations
+** METHOD: sqlite3
+**
+** ^The sqlite3_drop_modules(D,L) interface removes all virtual
+** table modules from database connection D except those named on list L.
+** The L parameter must be either NULL or a pointer to an array of pointers
+** to strings where the array is terminated by a single NULL pointer.
+** ^If the L parameter is NULL, then all virtual table modules are removed.
+**
+** See also: [sqlite3_create_module()]
+*/
+SQLITE_API int sqlite3_drop_modules(
+ sqlite3 *db, /* Remove modules from this connection */
+ const char **azKeep /* Except, do not remove the ones named here */
+);
+
+/*
** CAPI3REF: Virtual Table Instance Object
** KEYWORDS: sqlite3_vtab
**
@@ -6585,7 +7651,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
** METHOD: sqlite3
**
** ^(Virtual tables can provide alternative implementations of functions
-** using the [xFindFunction] method of the [virtual table module].
+** using the [xFindFunction] method of the [virtual table module].
** But global versions of those functions
** must exist in order to be overloaded.)^
**
@@ -6600,16 +7666,6 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
/*
-** The interface to the virtual-table mechanism defined above (back up
-** to a comment remarkably similar to this one) is currently considered
-** to be experimental. The interface might change in incompatible ways.
-** If this is a problem for you, do not use the interface at this time.
-**
-** When the virtual-table mechanism stabilizes, we will declare the
-** interface fixed, support it indefinitely, and remove this comment.
-*/
-
-/*
** CAPI3REF: A Handle To An Open BLOB
** KEYWORDS: {BLOB handle} {BLOB handles}
**
@@ -6636,7 +7692,7 @@ typedef struct sqlite3_blob sqlite3_blob;
** SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
** </pre>)^
**
-** ^(Parameter zDb is not the filename that contains the database, but
+** ^(Parameter zDb is not the filename that contains the database, but
** rather the symbolic name of the database. For attached databases, this is
** the name that appears after the AS keyword in the [ATTACH] statement.
** For the main database file, the database name is "main". For TEMP
@@ -6649,28 +7705,28 @@ typedef struct sqlite3_blob sqlite3_blob;
** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored
** in *ppBlob. Otherwise an [error code] is returned and, unless the error
** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
-** the API is not misused, it is always safe to call [sqlite3_blob_close()]
+** the API is not misused, it is always safe to call [sqlite3_blob_close()]
** on *ppBlob after this function it returns.
**
** This function fails with SQLITE_ERROR if any of the following are true:
** <ul>
-** <li> ^(Database zDb does not exist)^,
-** <li> ^(Table zTable does not exist within database zDb)^,
-** <li> ^(Table zTable is a WITHOUT ROWID table)^,
+** <li> ^(Database zDb does not exist)^,
+** <li> ^(Table zTable does not exist within database zDb)^,
+** <li> ^(Table zTable is a WITHOUT ROWID table)^,
** <li> ^(Column zColumn does not exist)^,
** <li> ^(Row iRow is not present in the table)^,
** <li> ^(The specified column of row iRow contains a value that is not
** a TEXT or BLOB value)^,
-** <li> ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE
+** <li> ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE
** constraint and the blob is being opened for read/write access)^,
-** <li> ^([foreign key constraints | Foreign key constraints] are enabled,
+** <li> ^([foreign key constraints | Foreign key constraints] are enabled,
** column zColumn is part of a [child key] definition and the blob is
** being opened for read/write access)^.
** </ul>
**
-** ^Unless it returns SQLITE_MISUSE, this function sets the
-** [database connection] error code and message accessible via
-** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
+** ^Unless it returns SQLITE_MISUSE, this function sets the
+** [database connection] error code and message accessible via
+** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
**
** A BLOB referenced by sqlite3_blob_open() may be read using the
** [sqlite3_blob_read()] interface and modified by using
@@ -6696,7 +7752,7 @@ typedef struct sqlite3_blob sqlite3_blob;
** blob.
**
** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces
-** and the built-in [zeroblob] SQL function may be used to create a
+** and the built-in [zeroblob] SQL function may be used to create a
** zero-filled blob to read or write using the incremental-blob interface.
**
** To avoid a resource leak, every open [BLOB handle] should eventually
@@ -6746,7 +7802,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
** DESTRUCTOR: sqlite3_blob
**
** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed
-** unconditionally. Even if this routine returns an error code, the
+** unconditionally. Even if this routine returns an error code, the
** handle is still closed.)^
**
** ^If the blob handle being closed was opened for read-write access, and if
@@ -6756,10 +7812,10 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
** code is returned and the transaction rolled back.
**
** Calling this function with an argument that is not a NULL pointer or an
-** open blob handle results in undefined behaviour. ^Calling this routine
-** with a null pointer (such as would be returned by a failed call to
+** open blob handle results in undefined behavior. ^Calling this routine
+** with a null pointer (such as would be returned by a failed call to
** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
-** is passed a valid open blob handle, the values returned by the
+** is passed a valid open blob handle, the values returned by the
** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning.
*/
SQLITE_API int sqlite3_blob_close(sqlite3_blob *);
@@ -6768,7 +7824,7 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *);
** CAPI3REF: Return The Size Of An Open BLOB
** METHOD: sqlite3_blob
**
-** ^Returns the size in bytes of the BLOB accessible via the
+** ^Returns the size in bytes of the BLOB accessible via the
** successfully opened [BLOB handle] in its only argument. ^The
** incremental blob I/O routines can only read or overwriting existing
** blob content; they cannot change the size of a blob.
@@ -6819,9 +7875,9 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
**
** ^(On success, sqlite3_blob_write() returns SQLITE_OK.
** Otherwise, an [error code] or an [extended error code] is returned.)^
-** ^Unless SQLITE_MISUSE is returned, this function sets the
-** [database connection] error code and message accessible via
-** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
+** ^Unless SQLITE_MISUSE is returned, this function sets the
+** [database connection] error code and message accessible via
+** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
**
** ^If the [BLOB handle] passed as the first argument was not opened for
** writing (the flags parameter to [sqlite3_blob_open()] was zero),
@@ -6830,9 +7886,9 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
** This function may only modify the contents of the BLOB; it is
** not possible to increase the size of a BLOB using this API.
** ^If offset iOffset is less than N bytes from the end of the BLOB,
-** [SQLITE_ERROR] is returned and no data is written. The size of the
-** BLOB (and hence the maximum value of N+iOffset) can be determined
-** using the [sqlite3_blob_bytes()] interface. ^If N or iOffset are less
+** [SQLITE_ERROR] is returned and no data is written. The size of the
+** BLOB (and hence the maximum value of N+iOffset) can be determined
+** using the [sqlite3_blob_bytes()] interface. ^If N or iOffset are less
** than zero [SQLITE_ERROR] is returned and no data is written.
**
** ^An attempt to write to an expired [BLOB handle] fails with an
@@ -6926,7 +7982,7 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
** <ul>
** <li> SQLITE_MUTEX_FAST
** <li> SQLITE_MUTEX_RECURSIVE
-** <li> SQLITE_MUTEX_STATIC_MASTER
+** <li> SQLITE_MUTEX_STATIC_MAIN
** <li> SQLITE_MUTEX_STATIC_MEM
** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
@@ -6983,18 +8039,20 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
-** will always return SQLITE_BUSY. The SQLite core only ever uses
-** sqlite3_mutex_try() as an optimization so this is acceptable
-** behavior.)^
+** will always return SQLITE_BUSY. In most cases the SQLite core only uses
+** sqlite3_mutex_try() as an optimization, so this is acceptable
+** behavior. The exceptions are unix builds that set the
+** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working
+** sqlite3_mutex_try() is required.)^
**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread. The behavior
** is undefined if the mutex is not currently entered by the
** calling thread or is not currently allocated.
**
-** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or
-** sqlite3_mutex_leave() is a NULL pointer, then all three routines
-** behave as no-ops.
+** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(),
+** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer,
+** then any of the four routines behaves as a no-op.
**
** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()].
*/
@@ -7049,7 +8107,7 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*);
** The only difference is that the public sqlite3_XXX functions enumerated
** above silently ignore any invocations that pass a NULL pointer instead
** of a valid mutex handle. The implementations of the methods defined
-** by this structure are not required to handle this case, the results
+** by this structure are not required to handle this case. The results
** of passing a NULL pointer instead of a valid mutex handle are undefined
** (i.e. it is acceptable to provide an implementation that segfaults if
** it is passed a NULL pointer).
@@ -7128,7 +8186,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*);
*/
#define SQLITE_MUTEX_FAST 0
#define SQLITE_MUTEX_RECURSIVE 1
-#define SQLITE_MUTEX_STATIC_MASTER 2
+#define SQLITE_MUTEX_STATIC_MAIN 2
#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */
#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */
#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */
@@ -7143,11 +8201,15 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */
#define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */
+/* Legacy compatibility: */
+#define SQLITE_MUTEX_STATIC_MASTER 2
+
+
/*
** CAPI3REF: Retrieve the mutex for a database connection
** METHOD: sqlite3
**
-** ^This interface returns a pointer the [sqlite3_mutex] object that
+** ^This interface returns a pointer the [sqlite3_mutex] object that
** serializes access to the [database connection] given in the argument
** when the [threading mode] is Serialized.
** ^If the [threading mode] is Single-thread or Multi-thread then this
@@ -7174,7 +8236,7 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*);
** method becomes the return value of this routine.
**
** A few opcodes for [sqlite3_file_control()] are handled directly
-** by the SQLite core and never invoke the
+** by the SQLite core and never invoke the
** sqlite3_io_methods.xFileControl method.
** ^The [SQLITE_FCNTL_FILE_POINTER] value for the op parameter causes
** a pointer to the underlying [sqlite3_file] object to be written into
@@ -7231,14 +8293,16 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_FIRST 5
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
-#define SQLITE_TESTCTRL_PRNG_RESET 7
+#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
+#define SQLITE_TESTCTRL_FK_NO_ACTION 7
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
#define SQLITE_TESTCTRL_PENDING_BYTE 11
#define SQLITE_TESTCTRL_ASSERT 12
#define SQLITE_TESTCTRL_ALWAYS 13
-#define SQLITE_TESTCTRL_RESERVE 14
+#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
+#define SQLITE_TESTCTRL_JSON_SELFCHECK 14
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
@@ -7253,12 +8317,20 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_SORTER_MMAP 24
#define SQLITE_TESTCTRL_IMPOSTER 25
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
-#define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */
+#define SQLITE_TESTCTRL_RESULT_INTREAL 27
+#define SQLITE_TESTCTRL_PRNG_SEED 28
+#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
+#define SQLITE_TESTCTRL_SEEK_COUNT 30
+#define SQLITE_TESTCTRL_TRACEFLAGS 31
+#define SQLITE_TESTCTRL_TUNE 32
+#define SQLITE_TESTCTRL_LOGEST 33
+#define SQLITE_TESTCTRL_USELONGDOUBLE 34
+#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */
/*
** CAPI3REF: SQL Keyword Checking
**
-** These routines provide access to the set of SQL language keywords
+** These routines provide access to the set of SQL language keywords
** recognized by SQLite. Applications can uses these routines to determine
** whether or not a specific identifier needs to be escaped (for example,
** by enclosing in double-quotes) so as not to confuse the parser.
@@ -7330,14 +8402,14 @@ typedef struct sqlite3_str sqlite3_str;
**
** ^The [sqlite3_str_new(D)] interface allocates and initializes
** a new [sqlite3_str] object. To avoid memory leaks, the object returned by
-** [sqlite3_str_new()] must be freed by a subsequent call to
+** [sqlite3_str_new()] must be freed by a subsequent call to
** [sqlite3_str_finish(X)].
**
** ^The [sqlite3_str_new(D)] interface always returns a pointer to a
** valid [sqlite3_str] object, though in the event of an out-of-memory
** error the returned object might be a special singleton that will
-** silently reject new text, always return SQLITE_NOMEM from
-** [sqlite3_str_errcode()], always return 0 for
+** silently reject new text, always return SQLITE_NOMEM from
+** [sqlite3_str_errcode()], always return 0 for
** [sqlite3_str_length()], and always return NULL from
** [sqlite3_str_finish(X)]. It is always safe to use the value
** returned by [sqlite3_str_new(D)] as the sqlite3_str parameter
@@ -7373,9 +8445,9 @@ SQLITE_API char *sqlite3_str_finish(sqlite3_str*);
** These interfaces add content to an sqlite3_str object previously obtained
** from [sqlite3_str_new()].
**
-** ^The [sqlite3_str_appendf(X,F,...)] and
+** ^The [sqlite3_str_appendf(X,F,...)] and
** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf]
-** functionality of SQLite to append formatted text onto the end of
+** functionality of SQLite to append formatted text onto the end of
** [sqlite3_str] object X.
**
** ^The [sqlite3_str_append(X,S,N)] method appends exactly N bytes from string S
@@ -7392,7 +8464,7 @@ SQLITE_API char *sqlite3_str_finish(sqlite3_str*);
** ^This method can be used, for example, to add whitespace indentation.
**
** ^The [sqlite3_str_reset(X)] method resets the string under construction
-** inside [sqlite3_str] object X back to zero bytes in length.
+** inside [sqlite3_str] object X back to zero bytes in length.
**
** These methods do not return a result code. ^If an error occurs, that fact
** is recorded in the [sqlite3_str] object and can be recovered by a
@@ -7494,7 +8566,7 @@ SQLITE_API int sqlite3_status64(
** <dd>This parameter records the largest memory allocation request
** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their
** internal equivalents). Only the value returned in the
-** *pHighwater parameter to [sqlite3_status()] is of interest.
+** *pHighwater parameter to [sqlite3_status()] is of interest.
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
** [[SQLITE_STATUS_MALLOC_COUNT]] ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt>
@@ -7503,11 +8575,11 @@ SQLITE_API int sqlite3_status64(
**
** [[SQLITE_STATUS_PAGECACHE_USED]] ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt>
** <dd>This parameter returns the number of pages used out of the
-** [pagecache memory allocator] that was configured using
+** [pagecache memory allocator] that was configured using
** [SQLITE_CONFIG_PAGECACHE]. The
** value returned is in pages, not in bytes.</dd>)^
**
-** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]]
+** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]]
** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt>
** <dd>This parameter returns the number of bytes of page cache
** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE]
@@ -7519,8 +8591,8 @@ SQLITE_API int sqlite3_status64(
**
** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt>
** <dd>This parameter records the largest memory allocation request
-** handed to [pagecache memory allocator]. Only the value returned in the
-** *pHighwater parameter to [sqlite3_status()] is of interest.
+** handed to the [pagecache memory allocator]. Only the value returned in the
+** *pHighwater parameter to [sqlite3_status()] is of interest.
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
** [[SQLITE_STATUS_SCRATCH_USED]] <dt>SQLITE_STATUS_SCRATCH_USED</dt>
@@ -7533,7 +8605,7 @@ SQLITE_API int sqlite3_status64(
** <dd>No longer used.</dd>
**
** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
-** <dd>The *pHighwater parameter records the deepest parser stack.
+** <dd>The *pHighwater parameter records the deepest parser stack.
** The *pCurrent value is undefined. The *pHighwater value is only
** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
** </dl>
@@ -7555,12 +8627,12 @@ SQLITE_API int sqlite3_status64(
** CAPI3REF: Database Connection Status
** METHOD: sqlite3
**
-** ^This interface is used to retrieve runtime status information
+** ^This interface is used to retrieve runtime status information
** about a single [database connection]. ^The first argument is the
** database connection object to be interrogated. ^The second argument
** is an integer constant, taken from the set of
** [SQLITE_DBSTATUS options], that
-** determines the parameter to interrogate. The set of
+** determines the parameter to interrogate. The set of
** [SQLITE_DBSTATUS options] is likely
** to grow in future releases of SQLite.
**
@@ -7595,7 +8667,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** checked out.</dd>)^
**
** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>
-** <dd>This parameter returns the number malloc attempts that were
+** <dd>This parameter returns the number of malloc attempts that were
** satisfied using lookaside memory. Only the high-water value is meaningful;
** the current value is always zero.)^
**
@@ -7620,7 +8692,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** memory used by all pager caches associated with the database connection.)^
** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
**
-** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]]
+** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]]
** ^(<dt>SQLITE_DBSTATUS_CACHE_USED_SHARED</dt>
** <dd>This parameter is similar to DBSTATUS_CACHE_USED, except that if a
** pager cache is shared between two or more connections the bytes of heap
@@ -7635,7 +8707,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
** <dd>This parameter returns the approximate number of bytes of heap
** memory used to store the schema for all databases associated
-** with the connection - main, temp, and any [ATTACH]-ed databases.)^
+** with the connection - main, temp, and any [ATTACH]-ed databases.)^
** ^The full amount of memory used by the schemas is reported, even if the
** schema memory is shared with other database connections due to
** [shared cache mode] being enabled.
@@ -7650,13 +8722,13 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
**
** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(<dt>SQLITE_DBSTATUS_CACHE_HIT</dt>
** <dd>This parameter returns the number of pager cache hits that have
-** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT
+** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT
** is always 0.
** </dd>
**
** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(<dt>SQLITE_DBSTATUS_CACHE_MISS</dt>
** <dd>This parameter returns the number of pager cache misses that have
-** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS
+** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS
** is always 0.
** </dd>
**
@@ -7677,7 +8749,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** cache overflowing. Transactions are more efficient if they are written
** to disk all at once. When pages spill mid-transaction, that introduces
** additional overhead. This parameter can be used help identify
-** inefficiencies that can be resolve by increasing the cache size.
+** inefficiencies that can be resolved by increasing the cache size.
** </dd>
**
** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
@@ -7714,7 +8786,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** statements. For example, if the number of table steps greatly exceeds
** the number of table searches or result rows, that would tend to indicate
** that the prepared statement is using a full table scan rather than
-** an index.
+** an index.
**
** ^(This interface is used to retrieve and reset counter values from
** a [prepared statement]. The first argument is the prepared statement
@@ -7741,7 +8813,7 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]] <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt>
** <dd>^This is the number of times that SQLite has stepped forward in
** a table as part of a full table scan. Large numbers for this counter
-** may indicate opportunities for performance improvement through
+** may indicate opportunities for performance improvement through
** careful use of indices.</dd>
**
** [[SQLITE_STMTSTATUS_SORT]] <dt>SQLITE_STMTSTATUS_SORT</dt>
@@ -7759,14 +8831,14 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** [[SQLITE_STMTSTATUS_VM_STEP]] <dt>SQLITE_STMTSTATUS_VM_STEP</dt>
** <dd>^This is the number of virtual machine operations executed
** by the prepared statement if that number is less than or equal
-** to 2147483647. The number of virtual machine operations can be
+** to 2147483647. The number of virtual machine operations can be
** used as a proxy for the total work done by the prepared statement.
** If the number of virtual machine operations exceeds 2147483647
** then the value returned by this statement status code is undefined.
**
** [[SQLITE_STMTSTATUS_REPREPARE]] <dt>SQLITE_STMTSTATUS_REPREPARE</dt>
** <dd>^This is the number of times that the prepare statement has been
-** automatically regenerated due to schema changes or change to
+** automatically regenerated due to schema changes or changes to
** [bound parameters] that might affect the query plan.
**
** [[SQLITE_STMTSTATUS_RUN]] <dt>SQLITE_STMTSTATUS_RUN</dt>
@@ -7776,6 +8848,16 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** The counter is incremented on the first [sqlite3_step()] call of each
** cycle.
**
+** [[SQLITE_STMTSTATUS_FILTER_MISS]]
+** [[SQLITE_STMTSTATUS_FILTER HIT]]
+** <dt>SQLITE_STMTSTATUS_FILTER_HIT<br>
+** SQLITE_STMTSTATUS_FILTER_MISS</dt>
+** <dd>^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join
+** step was bypassed because a Bloom filter returned not-found. The
+** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of
+** times that the Bloom filter returned a find, and thus the join step
+** had to be processed as normal.
+**
** [[SQLITE_STMTSTATUS_MEMUSED]] <dt>SQLITE_STMTSTATUS_MEMUSED</dt>
** <dd>^This is the approximate number of bytes of heap memory
** used to store the prepared statement. ^This value is not actually
@@ -7790,6 +8872,8 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
#define SQLITE_STMTSTATUS_VM_STEP 4
#define SQLITE_STMTSTATUS_REPREPARE 5
#define SQLITE_STMTSTATUS_RUN 6
+#define SQLITE_STMTSTATUS_FILTER_MISS 7
+#define SQLITE_STMTSTATUS_FILTER_HIT 8
#define SQLITE_STMTSTATUS_MEMUSED 99
/*
@@ -7826,15 +8910,15 @@ struct sqlite3_pcache_page {
** KEYWORDS: {page cache}
**
** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can
-** register an alternative page cache implementation by passing in an
+** register an alternative page cache implementation by passing in an
** instance of the sqlite3_pcache_methods2 structure.)^
-** In many applications, most of the heap memory allocated by
+** In many applications, most of the heap memory allocated by
** SQLite is used for the page cache.
-** By implementing a
+** By implementing a
** custom page cache using this API, an application can better control
-** the amount of memory consumed by SQLite, the way in which
-** that memory is allocated and released, and the policies used to
-** determine exactly which parts of a database file are cached and for
+** the amount of memory consumed by SQLite, the way in which
+** that memory is allocated and released, and the policies used to
+** determine exactly which parts of a database file are cached and for
** how long.
**
** The alternative page cache mechanism is an
@@ -7847,19 +8931,19 @@ struct sqlite3_pcache_page {
** [sqlite3_config()] returns.)^
**
** [[the xInit() page cache method]]
-** ^(The xInit() method is called once for each effective
+** ^(The xInit() method is called once for each effective
** call to [sqlite3_initialize()])^
** (usually only once during the lifetime of the process). ^(The xInit()
** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^
-** The intent of the xInit() method is to set up global data structures
-** required by the custom page cache implementation.
-** ^(If the xInit() method is NULL, then the
+** The intent of the xInit() method is to set up global data structures
+** required by the custom page cache implementation.
+** ^(If the xInit() method is NULL, then the
** built-in default page cache is used instead of the application defined
** page cache.)^
**
** [[the xShutdown() page cache method]]
** ^The xShutdown() method is called by [sqlite3_shutdown()].
-** It can be used to clean up
+** It can be used to clean up
** any outstanding resources before process shutdown, if required.
** ^The xShutdown() method may be NULL.
**
@@ -7878,7 +8962,7 @@ struct sqlite3_pcache_page {
** though this is not guaranteed. ^The
** first parameter, szPage, is the size in bytes of the pages that must
** be allocated by the cache. ^szPage will always a power of two. ^The
-** second parameter szExtra is a number of bytes of extra storage
+** second parameter szExtra is a number of bytes of extra storage
** associated with each page cache entry. ^The szExtra parameter will
** a number less than 250. SQLite will use the
** extra szExtra bytes on each page to store metadata about the underlying
@@ -7891,7 +8975,7 @@ struct sqlite3_pcache_page {
** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will
** never invoke xUnpin() except to deliberately delete a page.
** ^In other words, calls to xUnpin() on a cache with bPurgeable set to
-** false will always have the "discard" flag set to true.
+** false will always have the "discard" flag set to true.
** ^Hence, a cache created with bPurgeable false will
** never contain any unpinned pages.
**
@@ -7906,12 +8990,12 @@ struct sqlite3_pcache_page {
** [[the xPagecount() page cache methods]]
** The xPagecount() method must return the number of pages currently
** stored in the cache, both pinned and unpinned.
-**
+**
** [[the xFetch() page cache methods]]
-** The xFetch() method locates a page in the cache and returns a pointer to
+** The xFetch() method locates a page in the cache and returns a pointer to
** an sqlite3_pcache_page object associated with that page, or a NULL pointer.
** The pBuf element of the returned sqlite3_pcache_page object will be a
-** pointer to a buffer of szPage bytes used to store the content of a
+** pointer to a buffer of szPage bytes used to store the content of a
** single database page. The pExtra element of sqlite3_pcache_page will be
** a pointer to the szExtra bytes of extra storage that SQLite has requested
** for each entry in the page cache.
@@ -7937,7 +9021,7 @@ struct sqlite3_pcache_page {
**
** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite
** will only use a createFlag of 2 after a prior call with a createFlag of 1
-** failed.)^ In between the to xFetch() calls, SQLite may
+** failed.)^ In between the xFetch() calls, SQLite may
** attempt to unpin one or more cache pages by spilling the content of
** pinned pages to disk and synching the operating system disk cache.
**
@@ -7950,8 +9034,8 @@ struct sqlite3_pcache_page {
** page cache implementation. ^The page cache implementation
** may choose to evict unpinned pages at any time.
**
-** The cache must not perform any reference counting. A single
-** call to xUnpin() unpins the page regardless of the number of prior calls
+** The cache must not perform any reference counting. A single
+** call to xUnpin() unpins the page regardless of the number of prior calls
** to xFetch().
**
** [[the xRekey() page cache methods]]
@@ -7991,7 +9075,7 @@ struct sqlite3_pcache_methods2 {
int (*xPagecount)(sqlite3_pcache*);
sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);
void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard);
- void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*,
+ void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*,
unsigned oldKey, unsigned newKey);
void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);
void (*xDestroy)(sqlite3_pcache*);
@@ -8036,7 +9120,7 @@ typedef struct sqlite3_backup sqlite3_backup;
**
** The backup API copies the content of one database into another.
** It is useful either for creating backups of databases or
-** for copying in-memory databases to or from persistent files.
+** for copying in-memory databases to or from persistent files.
**
** See Also: [Using the SQLite Online Backup API]
**
@@ -8047,36 +9131,36 @@ typedef struct sqlite3_backup sqlite3_backup;
** ^Thus, the backup may be performed on a live source database without
** preventing other database connections from
** reading or writing to the source database while the backup is underway.
-**
-** ^(To perform a backup operation:
+**
+** ^(To perform a backup operation:
** <ol>
** <li><b>sqlite3_backup_init()</b> is called once to initialize the
-** backup,
-** <li><b>sqlite3_backup_step()</b> is called one or more times to transfer
+** backup,
+** <li><b>sqlite3_backup_step()</b> is called one or more times to transfer
** the data between the two databases, and finally
-** <li><b>sqlite3_backup_finish()</b> is called to release all resources
-** associated with the backup operation.
+** <li><b>sqlite3_backup_finish()</b> is called to release all resources
+** associated with the backup operation.
** </ol>)^
** There should be exactly one call to sqlite3_backup_finish() for each
** successful call to sqlite3_backup_init().
**
** [[sqlite3_backup_init()]] <b>sqlite3_backup_init()</b>
**
-** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the
-** [database connection] associated with the destination database
+** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the
+** [database connection] associated with the destination database
** and the database name, respectively.
** ^The database name is "main" for the main database, "temp" for the
** temporary database, or the name specified after the AS keyword in
** an [ATTACH] statement for an attached database.
-** ^The S and M arguments passed to
+** ^The S and M arguments passed to
** sqlite3_backup_init(D,N,S,M) identify the [database connection]
** and database name of the source database, respectively.
** ^The source and destination [database connections] (parameters S and D)
** must be different or else sqlite3_backup_init(D,N,S,M) will fail with
** an error.
**
-** ^A call to sqlite3_backup_init() will fail, returning NULL, if
-** there is already a read or read-write transaction open on the
+** ^A call to sqlite3_backup_init() will fail, returning NULL, if
+** there is already a read or read-write transaction open on the
** destination database.
**
** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is
@@ -8088,14 +9172,14 @@ typedef struct sqlite3_backup sqlite3_backup;
** ^A successful call to sqlite3_backup_init() returns a pointer to an
** [sqlite3_backup] object.
** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and
-** sqlite3_backup_finish() functions to perform the specified backup
+** sqlite3_backup_finish() functions to perform the specified backup
** operation.
**
** [[sqlite3_backup_step()]] <b>sqlite3_backup_step()</b>
**
-** ^Function sqlite3_backup_step(B,N) will copy up to N pages between
+** ^Function sqlite3_backup_step(B,N) will copy up to N pages between
** the source and destination databases specified by [sqlite3_backup] object B.
-** ^If N is negative, all remaining source pages are copied.
+** ^If N is negative, all remaining source pages are copied.
** ^If sqlite3_backup_step(B,N) successfully copies N pages and there
** are still more pages to be copied, then the function returns [SQLITE_OK].
** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages
@@ -8117,8 +9201,8 @@ typedef struct sqlite3_backup sqlite3_backup;
**
** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then
** the [sqlite3_busy_handler | busy-handler function]
-** is invoked (if one is specified). ^If the
-** busy-handler returns non-zero before the lock is available, then
+** is invoked (if one is specified). ^If the
+** busy-handler returns non-zero before the lock is available, then
** [SQLITE_BUSY] is returned to the caller. ^In this case the call to
** sqlite3_backup_step() can be retried later. ^If the source
** [database connection]
@@ -8126,15 +9210,15 @@ typedef struct sqlite3_backup sqlite3_backup;
** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this
** case the call to sqlite3_backup_step() can be retried later on. ^(If
** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or
-** [SQLITE_READONLY] is returned, then
-** there is no point in retrying the call to sqlite3_backup_step(). These
-** errors are considered fatal.)^ The application must accept
-** that the backup operation has failed and pass the backup operation handle
+** [SQLITE_READONLY] is returned, then
+** there is no point in retrying the call to sqlite3_backup_step(). These
+** errors are considered fatal.)^ The application must accept
+** that the backup operation has failed and pass the backup operation handle
** to the sqlite3_backup_finish() to release associated resources.
**
** ^The first call to sqlite3_backup_step() obtains an exclusive lock
-** on the destination file. ^The exclusive lock is not released until either
-** sqlite3_backup_finish() is called or the backup operation is complete
+** on the destination file. ^The exclusive lock is not released until either
+** sqlite3_backup_finish() is called or the backup operation is complete
** and sqlite3_backup_step() returns [SQLITE_DONE]. ^Every call to
** sqlite3_backup_step() obtains a [shared lock] on the source database that
** lasts for the duration of the sqlite3_backup_step() call.
@@ -8143,18 +9227,18 @@ typedef struct sqlite3_backup sqlite3_backup;
** through the backup process. ^If the source database is modified by an
** external process or via a database connection other than the one being
** used by the backup operation, then the backup will be automatically
-** restarted by the next call to sqlite3_backup_step(). ^If the source
+** restarted by the next call to sqlite3_backup_step(). ^If the source
** database is modified by the using the same database connection as is used
** by the backup operation, then the backup database is automatically
** updated at the same time.
**
** [[sqlite3_backup_finish()]] <b>sqlite3_backup_finish()</b>
**
-** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the
+** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the
** application wishes to abandon the backup operation, the application
** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish().
** ^The sqlite3_backup_finish() interfaces releases all
-** resources associated with the [sqlite3_backup] object.
+** resources associated with the [sqlite3_backup] object.
** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any
** active write-transaction on the destination database is rolled back.
** The [sqlite3_backup] object is invalid
@@ -8194,23 +9278,23 @@ typedef struct sqlite3_backup sqlite3_backup;
** connections, then the source database connection may be used concurrently
** from within other threads.
**
-** However, the application must guarantee that the destination
-** [database connection] is not passed to any other API (by any thread) after
+** However, the application must guarantee that the destination
+** [database connection] is not passed to any other API (by any thread) after
** sqlite3_backup_init() is called and before the corresponding call to
** sqlite3_backup_finish(). SQLite does not currently check to see
** if the application incorrectly accesses the destination [database connection]
** and so no error code is reported, but the operations may malfunction
** nevertheless. Use of the destination database connection while a
-** backup is in progress might also also cause a mutex deadlock.
+** backup is in progress might also cause a mutex deadlock.
**
** If running in [shared cache mode], the application must
** guarantee that the shared cache used by the destination database
** is not accessed while the backup is running. In practice this means
-** that the application must guarantee that the disk file being
+** that the application must guarantee that the disk file being
** backed up to is not accessed by any connection within the process,
** not just the specific connection that was passed to sqlite3_backup_init().
**
-** The [sqlite3_backup] object itself is partially threadsafe. Multiple
+** The [sqlite3_backup] object itself is partially threadsafe. Multiple
** threads may safely make multiple concurrent calls to sqlite3_backup_step().
** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount()
** APIs are not strictly speaking threadsafe. If they are invoked at the
@@ -8235,8 +9319,8 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
** ^When running in shared-cache mode, a database operation may fail with
** an [SQLITE_LOCKED] error if the required locks on the shared-cache or
** individual tables within the shared-cache cannot be obtained. See
-** [SQLite Shared-Cache Mode] for a description of shared-cache locking.
-** ^This API may be used to register a callback that SQLite will invoke
+** [SQLite Shared-Cache Mode] for a description of shared-cache locking.
+** ^This API may be used to register a callback that SQLite will invoke
** when the connection currently holding the required lock relinquishes it.
** ^This API is only available if the library was compiled with the
** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined.
@@ -8244,18 +9328,18 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
** See Also: [Using the SQLite Unlock Notification Feature].
**
** ^Shared-cache locks are released when a database connection concludes
-** its current transaction, either by committing it or rolling it back.
+** its current transaction, either by committing it or rolling it back.
**
** ^When a connection (known as the blocked connection) fails to obtain a
** shared-cache lock and SQLITE_LOCKED is returned to the caller, the
** identity of the database connection (the blocking connection) that
-** has locked the required resource is stored internally. ^After an
+** has locked the required resource is stored internally. ^After an
** application receives an SQLITE_LOCKED error, it may call the
-** sqlite3_unlock_notify() method with the blocked connection handle as
+** sqlite3_unlock_notify() method with the blocked connection handle as
** the first argument to register for a callback that will be invoked
** when the blocking connections current transaction is concluded. ^The
** callback is invoked from within the [sqlite3_step] or [sqlite3_close]
-** call that concludes the blocking connections transaction.
+** call that concludes the blocking connection's transaction.
**
** ^(If sqlite3_unlock_notify() is called in a multi-threaded application,
** there is a chance that the blocking connection will have already
@@ -8265,15 +9349,15 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
**
** ^If the blocked connection is attempting to obtain a write-lock on a
** shared-cache table, and more than one other connection currently holds
-** a read-lock on the same table, then SQLite arbitrarily selects one of
+** a read-lock on the same table, then SQLite arbitrarily selects one of
** the other connections to use as the blocking connection.
**
-** ^(There may be at most one unlock-notify callback registered by a
+** ^(There may be at most one unlock-notify callback registered by a
** blocked connection. If sqlite3_unlock_notify() is called when the
** blocked connection already has a registered unlock-notify callback,
** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
** called with a NULL pointer as its second argument, then any existing
-** unlock-notify callback is canceled. ^The blocked connections
+** unlock-notify callback is canceled. ^The blocked connections
** unlock-notify callback may also be canceled by closing the blocked
** connection using [sqlite3_close()].
**
@@ -8286,25 +9370,25 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
**
** <b>Callback Invocation Details</b>
**
-** When an unlock-notify callback is registered, the application provides a
+** When an unlock-notify callback is registered, the application provides a
** single void* pointer that is passed to the callback when it is invoked.
** However, the signature of the callback function allows SQLite to pass
** it an array of void* context pointers. The first argument passed to
** an unlock-notify callback is a pointer to an array of void* pointers,
** and the second is the number of entries in the array.
**
-** When a blocking connections transaction is concluded, there may be
+** When a blocking connection's transaction is concluded, there may be
** more than one blocked connection that has registered for an unlock-notify
** callback. ^If two or more such blocked connections have specified the
** same callback function, then instead of invoking the callback function
** multiple times, it is invoked once with the set of void* context pointers
** specified by the blocked connections bundled together into an array.
-** This gives the application an opportunity to prioritize any actions
+** This gives the application an opportunity to prioritize any actions
** related to the set of unblocked database connections.
**
** <b>Deadlock Detection</b>
**
-** Assuming that after registering for an unlock-notify callback a
+** Assuming that after registering for an unlock-notify callback a
** database waits for the callback to be issued before taking any further
** action (a reasonable assumption), then using this API may cause the
** application to deadlock. For example, if connection X is waiting for
@@ -8327,7 +9411,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
**
** <b>The "DROP TABLE" Exception</b>
**
-** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost
+** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost
** always appropriate to call sqlite3_unlock_notify(). There is however,
** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement,
** SQLite checks if there are any currently executing SELECT statements
@@ -8340,7 +9424,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
** One way around this problem is to check the extended error code returned
** by an sqlite3_step() call. ^(If there is a blocking connection, then the
** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in
-** the special "DROP TABLE/INDEX" case, the extended error code is just
+** the special "DROP TABLE/INDEX" case, the extended error code is just
** SQLITE_LOCKED.)^
*/
SQLITE_API int sqlite3_unlock_notify(
@@ -8431,8 +9515,8 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
** ^The [sqlite3_wal_hook()] function is used to register a callback that
** is invoked each time data is committed to a database in wal mode.
**
-** ^(The callback is invoked by SQLite after the commit has taken place and
-** the associated write-lock on the database released)^, so the implementation
+** ^(The callback is invoked by SQLite after the commit has taken place and
+** the associated write-lock on the database released)^, so the implementation
** may read, write or [checkpoint] the database as required.
**
** ^The first parameter passed to the callback function when it is invoked
@@ -8451,15 +9535,16 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
** that does not correspond to any valid SQLite error code, the results
** are undefined.
**
-** A single database handle may have at most a single write-ahead log callback
+** A single database handle may have at most a single write-ahead log callback
** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any
-** previously registered write-ahead log callback. ^Note that the
-** [sqlite3_wal_autocheckpoint()] interface and the
+** previously registered write-ahead log callback. ^The return value is
+** a copy of the third parameter from the previous call, if any, or 0.
+** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the
** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
** overwrite any prior [sqlite3_wal_hook()] settings.
*/
SQLITE_API void *sqlite3_wal_hook(
- sqlite3*,
+ sqlite3*,
int(*)(void *,sqlite3*,const char*,int),
void*
);
@@ -8472,7 +9557,7 @@ SQLITE_API void *sqlite3_wal_hook(
** [sqlite3_wal_hook()] that causes any database on [database connection] D
** to automatically [checkpoint]
** after committing a transaction if there are N or
-** more frames in the [write-ahead log] file. ^Passing zero or
+** more frames in the [write-ahead log] file. ^Passing zero or
** a negative value as the nFrame parameter disables automatic
** checkpoints entirely.
**
@@ -8502,7 +9587,7 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to
** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^
**
-** In brief, sqlite3_wal_checkpoint(D,X) causes the content in the
+** In brief, sqlite3_wal_checkpoint(D,X) causes the content in the
** [write-ahead log] for database X on [database connection] D to be
** transferred into the database file and for the write-ahead log to
** be reset. See the [checkpointing] documentation for addition
@@ -8528,10 +9613,10 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
**
** <dl>
** <dt>SQLITE_CHECKPOINT_PASSIVE<dd>
-** ^Checkpoint as many frames as possible without waiting for any database
-** readers or writers to finish, then sync the database file if all frames
+** ^Checkpoint as many frames as possible without waiting for any database
+** readers or writers to finish, then sync the database file if all frames
** in the log were checkpointed. ^The [busy-handler callback]
-** is never invoked in the SQLITE_CHECKPOINT_PASSIVE mode.
+** is never invoked in the SQLITE_CHECKPOINT_PASSIVE mode.
** ^On the other hand, passive mode might leave the checkpoint unfinished
** if there are concurrent readers or writers.
**
@@ -8545,9 +9630,9 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
**
** <dt>SQLITE_CHECKPOINT_RESTART<dd>
** ^This mode works the same way as SQLITE_CHECKPOINT_FULL with the addition
-** that after checkpointing the log file it blocks (calls the
+** that after checkpointing the log file it blocks (calls the
** [busy-handler callback])
-** until all readers are reading from the database file only. ^This ensures
+** until all readers are reading from the database file only. ^This ensures
** that the next writer will restart the log file from the beginning.
** ^Like SQLITE_CHECKPOINT_FULL, this mode blocks new
** database writer attempts while it is pending, but does not impede readers.
@@ -8569,31 +9654,31 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
** truncated to zero bytes and so both *pnLog and *pnCkpt will be set to zero.
**
** ^All calls obtain an exclusive "checkpoint" lock on the database file. ^If
-** any other process is running a checkpoint operation at the same time, the
-** lock cannot be obtained and SQLITE_BUSY is returned. ^Even if there is a
+** any other process is running a checkpoint operation at the same time, the
+** lock cannot be obtained and SQLITE_BUSY is returned. ^Even if there is a
** busy-handler configured, it will not be invoked in this case.
**
-** ^The SQLITE_CHECKPOINT_FULL, RESTART and TRUNCATE modes also obtain the
+** ^The SQLITE_CHECKPOINT_FULL, RESTART and TRUNCATE modes also obtain the
** exclusive "writer" lock on the database file. ^If the writer lock cannot be
** obtained immediately, and a busy-handler is configured, it is invoked and
** the writer lock retried until either the busy-handler returns 0 or the lock
** is successfully obtained. ^The busy-handler is also invoked while waiting for
** database readers as described above. ^If the busy-handler returns 0 before
** the writer lock is obtained or while waiting for database readers, the
-** checkpoint operation proceeds from that point in the same way as
-** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible
+** checkpoint operation proceeds from that point in the same way as
+** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible
** without blocking any further. ^SQLITE_BUSY is returned in this case.
**
** ^If parameter zDb is NULL or points to a zero length string, then the
-** specified operation is attempted on all WAL databases [attached] to
+** specified operation is attempted on all WAL databases [attached] to
** [database connection] db. In this case the
-** values written to output parameters *pnLog and *pnCkpt are undefined. ^If
-** an SQLITE_BUSY error is encountered when processing one or more of the
-** attached WAL databases, the operation is still attempted on any remaining
-** attached databases and SQLITE_BUSY is returned at the end. ^If any other
-** error occurs while processing an attached database, processing is abandoned
-** and the error code is returned to the caller immediately. ^If no error
-** (SQLITE_BUSY or otherwise) is encountered while processing the attached
+** values written to output parameters *pnLog and *pnCkpt are undefined. ^If
+** an SQLITE_BUSY error is encountered when processing one or more of the
+** attached WAL databases, the operation is still attempted on any remaining
+** attached databases and SQLITE_BUSY is returned at the end. ^If any other
+** error occurs while processing an attached database, processing is abandoned
+** and the error code is returned to the caller immediately. ^If no error
+** (SQLITE_BUSY or otherwise) is encountered while processing the attached
** databases, SQLITE_OK is returned.
**
** ^If database zDb is the name of an attached database that is not in WAL
@@ -8628,7 +9713,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
*/
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
-#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */
+#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
/*
@@ -8641,14 +9726,20 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
** If this interface is invoked outside the context of an xConnect or
** xCreate virtual table method then the behavior is undefined.
**
-** At present, there is only one option that may be configured using
-** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options
-** may be added in the future.
+** In the call sqlite3_vtab_config(D,C,...) the D parameter is the
+** [database connection] in which the virtual table is being created and
+** which is passed in as the first argument to the [xConnect] or [xCreate]
+** method that is invoking sqlite3_vtab_config(). The C parameter is one
+** of the [virtual table configuration options]. The presence and meaning
+** of parameters after C depend on which [virtual table configuration option]
+** is used.
*/
SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
/*
** CAPI3REF: Virtual Table Configuration Options
+** KEYWORDS: {virtual table configuration options}
+** KEYWORDS: {virtual table configuration option}
**
** These macros define the various options to the
** [sqlite3_vtab_config()] interface that [virtual table] implementations
@@ -8656,7 +9747,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
**
** <dl>
** [[SQLITE_VTAB_CONSTRAINT_SUPPORT]]
-** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT
+** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT</dt>
** <dd>Calls of the form
** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported,
** where X is an integer. If X is zero, then the [virtual table] whose
@@ -8670,24 +9761,56 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
** If X is non-zero, then the virtual table implementation guarantees
** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before
** any modifications to internal or persistent data structures have been made.
-** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite
+** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite
** is able to roll back a statement or database transaction, and abandon
-** or continue processing the current SQL statement as appropriate.
+** or continue processing the current SQL statement as appropriate.
** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns
** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode
** had been ABORT.
**
** Virtual table implementations that are required to handle OR REPLACE
-** must do so within the [xUpdate] method. If a call to the
-** [sqlite3_vtab_on_conflict()] function indicates that the current ON
-** CONFLICT policy is REPLACE, the virtual table implementation should
+** must do so within the [xUpdate] method. If a call to the
+** [sqlite3_vtab_on_conflict()] function indicates that the current ON
+** CONFLICT policy is REPLACE, the virtual table implementation should
** silently replace the appropriate rows within the xUpdate callback and
** return SQLITE_OK. Or, if this is not possible, it may return
-** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT
+** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT
** constraint handling.
+** </dd>
+**
+** [[SQLITE_VTAB_DIRECTONLY]]<dt>SQLITE_VTAB_DIRECTONLY</dt>
+** <dd>Calls of the form
+** [sqlite3_vtab_config](db,SQLITE_VTAB_DIRECTONLY) from within the
+** the [xConnect] or [xCreate] methods of a [virtual table] implementation
+** prohibits that virtual table from being used from within triggers and
+** views.
+** </dd>
+**
+** [[SQLITE_VTAB_INNOCUOUS]]<dt>SQLITE_VTAB_INNOCUOUS</dt>
+** <dd>Calls of the form
+** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the
+** the [xConnect] or [xCreate] methods of a [virtual table] implementation
+** identify that virtual table as being safe to use from within triggers
+** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the
+** virtual table can do no serious harm even if it is controlled by a
+** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS
+** flag unless absolutely necessary.
+** </dd>
+**
+** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]<dt>SQLITE_VTAB_USES_ALL_SCHEMAS</dt>
+** <dd>Calls of the form
+** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the
+** the [xConnect] or [xCreate] methods of a [virtual table] implementation
+** instruct the query planner to begin at least a read transaction on
+** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the
+** virtual table is used.
+** </dd>
** </dl>
*/
#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
+#define SQLITE_VTAB_INNOCUOUS 2
+#define SQLITE_VTAB_DIRECTONLY 3
+#define SQLITE_VTAB_USES_ALL_SCHEMAS 4
/*
** CAPI3REF: Determine The Virtual Table Conflict Policy
@@ -8705,10 +9828,11 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE
**
** If the sqlite3_vtab_nochange(X) routine is called within the [xColumn]
-** method of a [virtual table], then it returns true if and only if the
+** method of a [virtual table], then it might return true if the
** column is being fetched as part of an UPDATE operation during which the
-** column value will not change. Applications might use this to substitute
-** a return value that is less expensive to compute and that the corresponding
+** column value will not change. The virtual table implementation can use
+** this hint as permission to substitute a return value that is less
+** expensive to compute and that the corresponding
** [xUpdate] method understands as a "no-change" value.
**
** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that
@@ -8717,23 +9841,285 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces].
** In that case, [sqlite3_value_nochange(X)] will return true for the
** same column in the [xUpdate] method.
+**
+** The sqlite3_vtab_nochange() routine is an optimization. Virtual table
+** implementations should continue to give a correct answer even if the
+** sqlite3_vtab_nochange() interface were to always return false. In the
+** current implementation, the sqlite3_vtab_nochange() interface does always
+** returns false for the enhanced [UPDATE FROM] statement.
*/
SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*);
/*
** CAPI3REF: Determine The Collation For a Virtual Table Constraint
+** METHOD: sqlite3_index_info
**
** This function may only be called from within a call to the [xBestIndex]
-** method of a [virtual table].
+** method of a [virtual table]. This function returns a pointer to a string
+** that is the name of the appropriate collation sequence to use for text
+** comparisons on the constraint identified by its arguments.
+**
+** The first argument must be the pointer to the [sqlite3_index_info] object
+** that is the first parameter to the xBestIndex() method. The second argument
+** must be an index into the aConstraint[] array belonging to the
+** sqlite3_index_info structure passed to xBestIndex.
**
-** The first argument must be the sqlite3_index_info object that is the
-** first parameter to the xBestIndex() method. The second argument must be
-** an index into the aConstraint[] array belonging to the sqlite3_index_info
-** structure passed to xBestIndex. This function returns a pointer to a buffer
-** containing the name of the collation sequence for the corresponding
-** constraint.
+** Important:
+** The first parameter must be the same pointer that is passed into the
+** xBestMethod() method. The first parameter may not be a pointer to a
+** different [sqlite3_index_info] object, even an exact copy.
+**
+** The return value is computed as follows:
+**
+** <ol>
+** <li><p> If the constraint comes from a WHERE clause expression that contains
+** a [COLLATE operator], then the name of the collation specified by
+** that COLLATE operator is returned.
+** <li><p> If there is no COLLATE operator, but the column that is the subject
+** of the constraint specifies an alternative collating sequence via
+** a [COLLATE clause] on the column definition within the CREATE TABLE
+** statement that was passed into [sqlite3_declare_vtab()], then the
+** name of that alternative collating sequence is returned.
+** <li><p> Otherwise, "BINARY" is returned.
+** </ol>
*/
-SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
+SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
+
+/*
+** CAPI3REF: Determine if a virtual table query is DISTINCT
+** METHOD: sqlite3_index_info
+**
+** This API may only be used from within an [xBestIndex|xBestIndex method]
+** of a [virtual table] implementation. The result of calling this
+** interface from outside of xBestIndex() is undefined and probably harmful.
+**
+** ^The sqlite3_vtab_distinct() interface returns an integer between 0 and
+** 3. The integer returned by sqlite3_vtab_distinct()
+** gives the virtual table additional information about how the query
+** planner wants the output to be ordered. As long as the virtual table
+** can meet the ordering requirements of the query planner, it may set
+** the "orderByConsumed" flag.
+**
+** <ol><li value="0"><p>
+** ^If the sqlite3_vtab_distinct() interface returns 0, that means
+** that the query planner needs the virtual table to return all rows in the
+** sort order defined by the "nOrderBy" and "aOrderBy" fields of the
+** [sqlite3_index_info] object. This is the default expectation. If the
+** virtual table outputs all rows in sorted order, then it is always safe for
+** the xBestIndex method to set the "orderByConsumed" flag, regardless of
+** the return value from sqlite3_vtab_distinct().
+** <li value="1"><p>
+** ^(If the sqlite3_vtab_distinct() interface returns 1, that means
+** that the query planner does not need the rows to be returned in sorted order
+** as long as all rows with the same values in all columns identified by the
+** "aOrderBy" field are adjacent.)^ This mode is used when the query planner
+** is doing a GROUP BY.
+** <li value="2"><p>
+** ^(If the sqlite3_vtab_distinct() interface returns 2, that means
+** that the query planner does not need the rows returned in any particular
+** order, as long as rows with the same values in all "aOrderBy" columns
+** are adjacent.)^ ^(Furthermore, only a single row for each particular
+** combination of values in the columns identified by the "aOrderBy" field
+** needs to be returned.)^ ^It is always ok for two or more rows with the same
+** values in all "aOrderBy" columns to be returned, as long as all such rows
+** are adjacent. ^The virtual table may, if it chooses, omit extra rows
+** that have the same value for all columns identified by "aOrderBy".
+** ^However omitting the extra rows is optional.
+** This mode is used for a DISTINCT query.
+** <li value="3"><p>
+** ^(If the sqlite3_vtab_distinct() interface returns 3, that means
+** that the query planner needs only distinct rows but it does need the
+** rows to be sorted.)^ ^The virtual table implementation is free to omit
+** rows that are identical in all aOrderBy columns, if it wants to, but
+** it is not required to omit any rows. This mode is used for queries
+** that have both DISTINCT and ORDER BY clauses.
+** </ol>
+**
+** ^For the purposes of comparing virtual table output values to see if the
+** values are same value for sorting purposes, two NULL values are considered
+** to be the same. In other words, the comparison operator is "IS"
+** (or "IS NOT DISTINCT FROM") and not "==".
+**
+** If a virtual table implementation is unable to meet the requirements
+** specified above, then it must not set the "orderByConsumed" flag in the
+** [sqlite3_index_info] object or an incorrect answer may result.
+**
+** ^A virtual table implementation is always free to return rows in any order
+** it wants, as long as the "orderByConsumed" flag is not set. ^When the
+** the "orderByConsumed" flag is unset, the query planner will add extra
+** [bytecode] to ensure that the final results returned by the SQL query are
+** ordered correctly. The use of the "orderByConsumed" flag and the
+** sqlite3_vtab_distinct() interface is merely an optimization. ^Careful
+** use of the sqlite3_vtab_distinct() interface and the "orderByConsumed"
+** flag might help queries against a virtual table to run faster. Being
+** overly aggressive and setting the "orderByConsumed" flag when it is not
+** valid to do so, on the other hand, might cause SQLite to return incorrect
+** results.
+*/
+SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*);
+
+/*
+** CAPI3REF: Identify and handle IN constraints in xBestIndex
+**
+** This interface may only be used from within an
+** [xBestIndex|xBestIndex() method] of a [virtual table] implementation.
+** The result of invoking this interface from any other context is
+** undefined and probably harmful.
+**
+** ^(A constraint on a virtual table of the form
+** "[IN operator|column IN (...)]" is
+** communicated to the xBestIndex method as a
+** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use
+** this constraint, it must set the corresponding
+** aConstraintUsage[].argvIndex to a positive integer. ^(Then, under
+** the usual mode of handling IN operators, SQLite generates [bytecode]
+** that invokes the [xFilter|xFilter() method] once for each value
+** on the right-hand side of the IN operator.)^ Thus the virtual table
+** only sees a single value from the right-hand side of the IN operator
+** at a time.
+**
+** In some cases, however, it would be advantageous for the virtual
+** table to see all values on the right-hand of the IN operator all at
+** once. The sqlite3_vtab_in() interfaces facilitates this in two ways:
+**
+** <ol>
+** <li><p>
+** ^A call to sqlite3_vtab_in(P,N,-1) will return true (non-zero)
+** if and only if the [sqlite3_index_info|P->aConstraint][N] constraint
+** is an [IN operator] that can be processed all at once. ^In other words,
+** sqlite3_vtab_in() with -1 in the third argument is a mechanism
+** by which the virtual table can ask SQLite if all-at-once processing
+** of the IN operator is even possible.
+**
+** <li><p>
+** ^A call to sqlite3_vtab_in(P,N,F) with F==1 or F==0 indicates
+** to SQLite that the virtual table does or does not want to process
+** the IN operator all-at-once, respectively. ^Thus when the third
+** parameter (F) is non-negative, this interface is the mechanism by
+** which the virtual table tells SQLite how it wants to process the
+** IN operator.
+** </ol>
+**
+** ^The sqlite3_vtab_in(P,N,F) interface can be invoked multiple times
+** within the same xBestIndex method call. ^For any given P,N pair,
+** the return value from sqlite3_vtab_in(P,N,F) will always be the same
+** within the same xBestIndex call. ^If the interface returns true
+** (non-zero), that means that the constraint is an IN operator
+** that can be processed all-at-once. ^If the constraint is not an IN
+** operator or cannot be processed all-at-once, then the interface returns
+** false.
+**
+** ^(All-at-once processing of the IN operator is selected if both of the
+** following conditions are met:
+**
+** <ol>
+** <li><p> The P->aConstraintUsage[N].argvIndex value is set to a positive
+** integer. This is how the virtual table tells SQLite that it wants to
+** use the N-th constraint.
+**
+** <li><p> The last call to sqlite3_vtab_in(P,N,F) for which F was
+** non-negative had F>=1.
+** </ol>)^
+**
+** ^If either or both of the conditions above are false, then SQLite uses
+** the traditional one-at-a-time processing strategy for the IN constraint.
+** ^If both conditions are true, then the argvIndex-th parameter to the
+** xFilter method will be an [sqlite3_value] that appears to be NULL,
+** but which can be passed to [sqlite3_vtab_in_first()] and
+** [sqlite3_vtab_in_next()] to find all values on the right-hand side
+** of the IN constraint.
+*/
+SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle);
+
+/*
+** CAPI3REF: Find all elements on the right-hand side of an IN constraint.
+**
+** These interfaces are only useful from within the
+** [xFilter|xFilter() method] of a [virtual table] implementation.
+** The result of invoking these interfaces from any other context
+** is undefined and probably harmful.
+**
+** The X parameter in a call to sqlite3_vtab_in_first(X,P) or
+** sqlite3_vtab_in_next(X,P) should be one of the parameters to the
+** xFilter method which invokes these routines, and specifically
+** a parameter that was previously selected for all-at-once IN constraint
+** processing use the [sqlite3_vtab_in()] interface in the
+** [xBestIndex|xBestIndex method]. ^(If the X parameter is not
+** an xFilter argument that was selected for all-at-once IN constraint
+** processing, then these routines return [SQLITE_ERROR].)^
+**
+** ^(Use these routines to access all values on the right-hand side
+** of the IN constraint using code like the following:
+**
+** <blockquote><pre>
+** &nbsp; for(rc=sqlite3_vtab_in_first(pList, &pVal);
+** &nbsp; rc==SQLITE_OK && pVal;
+** &nbsp; rc=sqlite3_vtab_in_next(pList, &pVal)
+** &nbsp; ){
+** &nbsp; // do something with pVal
+** &nbsp; }
+** &nbsp; if( rc!=SQLITE_OK ){
+** &nbsp; // an error has occurred
+** &nbsp; }
+** </pre></blockquote>)^
+**
+** ^On success, the sqlite3_vtab_in_first(X,P) and sqlite3_vtab_in_next(X,P)
+** routines return SQLITE_OK and set *P to point to the first or next value
+** on the RHS of the IN constraint. ^If there are no more values on the
+** right hand side of the IN constraint, then *P is set to NULL and these
+** routines return [SQLITE_DONE]. ^The return value might be
+** some other value, such as SQLITE_NOMEM, in the event of a malfunction.
+**
+** The *ppOut values returned by these routines are only valid until the
+** next call to either of these routines or until the end of the xFilter
+** method from which these routines were called. If the virtual table
+** implementation needs to retain the *ppOut values for longer, it must make
+** copies. The *ppOut values are [protected sqlite3_value|protected].
+*/
+SQLITE_API int sqlite3_vtab_in_first(sqlite3_value *pVal, sqlite3_value **ppOut);
+SQLITE_API int sqlite3_vtab_in_next(sqlite3_value *pVal, sqlite3_value **ppOut);
+
+/*
+** CAPI3REF: Constraint values in xBestIndex()
+** METHOD: sqlite3_index_info
+**
+** This API may only be used from within the [xBestIndex|xBestIndex method]
+** of a [virtual table] implementation. The result of calling this interface
+** from outside of an xBestIndex method are undefined and probably harmful.
+**
+** ^When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within
+** the [xBestIndex] method of a [virtual table] implementation, with P being
+** a copy of the [sqlite3_index_info] object pointer passed into xBestIndex and
+** J being a 0-based index into P->aConstraint[], then this routine
+** attempts to set *V to the value of the right-hand operand of
+** that constraint if the right-hand operand is known. ^If the
+** right-hand operand is not known, then *V is set to a NULL pointer.
+** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if
+** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V)
+** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th
+** constraint is not available. ^The sqlite3_vtab_rhs_value() interface
+** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if
+** something goes wrong.
+**
+** The sqlite3_vtab_rhs_value() interface is usually only successful if
+** the right-hand operand of a constraint is a literal value in the original
+** SQL statement. If the right-hand operand is an expression or a reference
+** to some other column or a [host parameter], then sqlite3_vtab_rhs_value()
+** will probably return [SQLITE_NOTFOUND].
+**
+** ^(Some constraints, such as [SQLITE_INDEX_CONSTRAINT_ISNULL] and
+** [SQLITE_INDEX_CONSTRAINT_ISNOTNULL], have no right-hand operand. For such
+** constraints, sqlite3_vtab_rhs_value() always returns SQLITE_NOTFOUND.)^
+**
+** ^The [sqlite3_value] object returned in *V is a protected sqlite3_value
+** and remains valid for the duration of the xBestIndex method call.
+** ^When xBestIndex returns, the sqlite3_value object returned by
+** sqlite3_vtab_rhs_value() is automatically deallocated.
+**
+** The "_rhs_" in the name of this routine is an abbreviation for
+** "Right-Hand Side".
+*/
+SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
/*
** CAPI3REF: Conflict resolution modes
@@ -8765,17 +10151,21 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
** managed by the prepared statement S and will be automatically freed when
** S is finalized.
**
+** Not all values are available for all query elements. When a value is
+** not available, the output variable is set to -1 if the value is numeric,
+** or to NULL if it is a string (SQLITE_SCANSTAT_NAME).
+**
** <dl>
** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt>
-** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be
+** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be
** set to the total number of times that the X-th loop has run.</dd>
**
** [[SQLITE_SCANSTAT_NVISIT]] <dt>SQLITE_SCANSTAT_NVISIT</dt>
-** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be set
+** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be set
** to the total number of rows examined by all iterations of the X-th loop.</dd>
**
** [[SQLITE_SCANSTAT_EST]] <dt>SQLITE_SCANSTAT_EST</dt>
-** <dd>^The "double" variable pointed to by the T parameter will be set to the
+** <dd>^The "double" variable pointed to by the V parameter will be set to the
** query planner's estimate for the average number of rows output from each
** iteration of the X-th loop. If the query planner's estimates was accurate,
** then this value will approximate the quotient NVISIT/NLOOP and the
@@ -8783,21 +10173,33 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
** be the NLOOP value for the current loop.
**
** [[SQLITE_SCANSTAT_NAME]] <dt>SQLITE_SCANSTAT_NAME</dt>
-** <dd>^The "const char *" variable pointed to by the T parameter will be set
+** <dd>^The "const char *" variable pointed to by the V parameter will be set
** to a zero-terminated UTF-8 string containing the name of the index or table
** used for the X-th loop.
**
** [[SQLITE_SCANSTAT_EXPLAIN]] <dt>SQLITE_SCANSTAT_EXPLAIN</dt>
-** <dd>^The "const char *" variable pointed to by the T parameter will be set
+** <dd>^The "const char *" variable pointed to by the V parameter will be set
** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN]
** description for the X-th loop.
**
-** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECT</dt>
-** <dd>^The "int" variable pointed to by the T parameter will be set to the
-** "select-id" for the X-th loop. The select-id identifies which query or
-** subquery the loop is part of. The main query has a select-id of zero.
-** The select-id is the same value as is output in the first column
-** of an [EXPLAIN QUERY PLAN] query.
+** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECTID</dt>
+** <dd>^The "int" variable pointed to by the V parameter will be set to the
+** id for the X-th query plan element. The id value is unique within the
+** statement. The select-id is the same value as is output in the first
+** column of an [EXPLAIN QUERY PLAN] query.
+**
+** [[SQLITE_SCANSTAT_PARENTID]] <dt>SQLITE_SCANSTAT_PARENTID</dt>
+** <dd>The "int" variable pointed to by the V parameter will be set to the
+** the id of the parent of the current query element, if applicable, or
+** to zero if the query element has no parent. This is the same value as
+** returned in the second column of an [EXPLAIN QUERY PLAN] query.
+**
+** [[SQLITE_SCANSTAT_NCYCLE]] <dt>SQLITE_SCANSTAT_NCYCLE</dt>
+** <dd>The sqlite3_int64 output value is set to the number of cycles,
+** according to the processor time-stamp counter, that elapsed while the
+** query element was being processed. This value is not available for
+** all query elements - if it is unavailable the output variable is
+** set to -1.
** </dl>
*/
#define SQLITE_SCANSTAT_NLOOP 0
@@ -8806,12 +10208,14 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
#define SQLITE_SCANSTAT_NAME 3
#define SQLITE_SCANSTAT_EXPLAIN 4
#define SQLITE_SCANSTAT_SELECTID 5
+#define SQLITE_SCANSTAT_PARENTID 6
+#define SQLITE_SCANSTAT_NCYCLE 7
/*
** CAPI3REF: Prepared Statement Scan Status
** METHOD: sqlite3_stmt
**
-** This interface returns information about the predicted and measured
+** These interfaces return information about the predicted and measured
** performance for pStmt. Advanced applications can use this
** interface to compare the predicted and the measured performance and
** issue warnings and/or rerun [ANALYZE] if discrepancies are found.
@@ -8822,19 +10226,25 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
**
** The "iScanStatusOp" parameter determines which status information to return.
** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior
-** of this interface is undefined.
-** ^The requested measurement is written into a variable pointed to by
-** the "pOut" parameter.
-** Parameter "idx" identifies the specific loop to retrieve statistics for.
-** Loops are numbered starting from zero. ^If idx is out of range - less than
-** zero or greater than or equal to the total number of loops used to implement
-** the statement - a non-zero value is returned and the variable that pOut
-** points to is unchanged.
-**
-** ^Statistics might not be available for all loops in all statements. ^In cases
-** where there exist loops with no available statistics, this function behaves
-** as if the loop did not exist - it returns non-zero and leave the variable
-** that pOut points to unchanged.
+** of this interface is undefined. ^The requested measurement is written into
+** a variable pointed to by the "pOut" parameter.
+**
+** The "flags" parameter must be passed a mask of flags. At present only
+** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX
+** is specified, then status information is available for all elements
+** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If
+** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements
+** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of
+** the EXPLAIN QUERY PLAN output) are available. Invoking API
+** sqlite3_stmt_scanstatus() is equivalent to calling
+** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter.
+**
+** Parameter "idx" identifies the specific query element to retrieve statistics
+** for. Query elements are numbered starting from zero. A value of -1 may be
+** to query for statistics regarding the entire query. ^If idx is out of range
+** - less than -1 or greater than or equal to the total number of query
+** elements used to implement the statement - a non-zero value is returned and
+** the variable that pOut points to is unchanged.
**
** See also: [sqlite3_stmt_scanstatus_reset()]
*/
@@ -8843,7 +10253,20 @@ SQLITE_API int sqlite3_stmt_scanstatus(
int idx, /* Index of loop to report on */
int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
void *pOut /* Result written here */
-);
+);
+SQLITE_API int sqlite3_stmt_scanstatus_v2(
+ sqlite3_stmt *pStmt, /* Prepared statement for which info desired */
+ int idx, /* Index of loop to report on */
+ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
+ int flags, /* Mask of flags defined below */
+ void *pOut /* Result written here */
+);
+
+/*
+** CAPI3REF: Prepared Statement Scan Status
+** KEYWORDS: {scan status flags}
+*/
+#define SQLITE_SCANSTAT_COMPLEX 0x0001
/*
** CAPI3REF: Zero Scan-Status Counters
@@ -8858,18 +10281,19 @@ SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
/*
** CAPI3REF: Flush caches to disk mid-transaction
+** METHOD: sqlite3
**
** ^If a write-transaction is open on [database connection] D when the
** [sqlite3_db_cacheflush(D)] interface invoked, any dirty
-** pages in the pager-cache that are not currently in use are written out
+** pages in the pager-cache that are not currently in use are written out
** to disk. A dirty page may be in use if a database cursor created by an
** active SQL statement is reading from it, or if it is page 1 of a database
** file (page 1 is always "in use"). ^The [sqlite3_db_cacheflush(D)]
** interface flushes caches for all schemas - "main", "temp", and
** any [attached] databases.
**
-** ^If this function needs to obtain extra database locks before dirty pages
-** can be flushed to disk, it does so. ^If those locks cannot be obtained
+** ^If this function needs to obtain extra database locks before dirty pages
+** can be flushed to disk, it does so. ^If those locks cannot be obtained
** immediately and there is a busy-handler callback configured, it is invoked
** in the usual manner. ^If the required lock still cannot be obtained, then
** the database is skipped and an attempt made to flush any dirty pages
@@ -8890,6 +10314,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
/*
** CAPI3REF: The pre-update hook.
+** METHOD: sqlite3
**
** ^These interfaces are only available if SQLite is compiled using the
** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option.
@@ -8907,7 +10332,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
**
** ^The preupdate hook only fires for changes to real database tables; the
** preupdate hook is not invoked for changes to [virtual tables] or to
-** system tables like sqlite_master or sqlite_stat1.
+** system tables like sqlite_sequence or sqlite_stat1.
**
** ^The second parameter to the preupdate callback is a pointer to
** the [database connection] that registered the preupdate hook.
@@ -8916,21 +10341,25 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
** kind of update operation that is about to occur.
** ^(The fourth parameter to the preupdate callback is the name of the
** database within the database connection that is being modified. This
-** will be "main" for the main database or "temp" for TEMP tables or
+** will be "main" for the main database or "temp" for TEMP tables or
** the name given after the AS keyword in the [ATTACH] statement for attached
** databases.)^
** ^The fifth parameter to the preupdate callback is the name of the
** table that is being modified.
**
** For an UPDATE or DELETE operation on a [rowid table], the sixth
-** parameter passed to the preupdate callback is the initial [rowid] of the
+** parameter passed to the preupdate callback is the initial [rowid] of the
** row being modified or deleted. For an INSERT operation on a rowid table,
-** or any operation on a WITHOUT ROWID table, the value of the sixth
+** or any operation on a WITHOUT ROWID table, the value of the sixth
** parameter is undefined. For an INSERT or UPDATE on a rowid table the
** seventh parameter is the final rowid value of the row being inserted
** or updated. The value of the seventh parameter passed to the callback
** function is not defined for operations on WITHOUT ROWID tables, or for
-** INSERT operations on rowid tables.
+** DELETE operations on rowid tables.
+**
+** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from
+** the previous call on the same [database connection] D, or NULL for
+** the first call on D.
**
** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
@@ -8964,10 +10393,19 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
**
** ^The [sqlite3_preupdate_depth(D)] interface returns 0 if the preupdate
** callback was invoked as a result of a direct insert, update, or delete
-** operation; or 1 for inserts, updates, or deletes invoked by top-level
+** operation; or 1 for inserts, updates, or deletes invoked by top-level
** triggers; or 2 for changes resulting from triggers called by top-level
** triggers; and so forth.
**
+** When the [sqlite3_blob_write()] API is used to update a blob column,
+** the pre-update hook is invoked with SQLITE_DELETE. This is because the
+** in this case the new values are not available. In this case, when a
+** callback made with op==SQLITE_DELETE is actually a write using the
+** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns
+** the index of the column being written. In other cases, where the
+** pre-update hook is being invoked for some other reason, including a
+** regular DELETE, sqlite3_preupdate_blobwrite() returns -1.
+**
** See also: [sqlite3_update_hook()]
*/
#if defined(SQLITE_ENABLE_PREUPDATE_HOOK)
@@ -8988,17 +10426,19 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
SQLITE_API int sqlite3_preupdate_count(sqlite3 *);
SQLITE_API int sqlite3_preupdate_depth(sqlite3 *);
SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
+SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *);
#endif
/*
** CAPI3REF: Low-level system error code
+** METHOD: sqlite3
**
** ^Attempt to return the underlying operating system error code or error
** number that caused the most recent I/O error or failure to open a file.
** The return value is OS-dependent. For example, on unix systems, after
** [sqlite3_open_v2()] returns [SQLITE_CANTOPEN], this interface could be
** called to get back the underlying "errno" that caused the problem, such
-** as ENOSPC, EAUTH, EISDIR, and so forth.
+** as ENOSPC, EAUTH, EISDIR, and so forth.
*/
SQLITE_API int sqlite3_system_errno(sqlite3*);
@@ -9036,12 +10476,12 @@ typedef struct sqlite3_snapshot {
** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly
** created [sqlite3_snapshot] object into *P and returns SQLITE_OK.
** If there is not already a read-transaction open on schema S when
-** this function is called, one is opened automatically.
+** this function is called, one is opened automatically.
**
** The following must be true for this function to succeed. If any of
** the following statements are false when sqlite3_snapshot_get() is
** called, SQLITE_ERROR is returned. The final value of *P is undefined
-** in this case.
+** in this case.
**
** <ul>
** <li> The database handle must not be in [autocommit mode].
@@ -9053,13 +10493,13 @@ typedef struct sqlite3_snapshot {
**
** <li> One or more transactions must have been written to the current wal
** file since it was created on disk (by any connection). This means
-** that a snapshot cannot be taken on a wal mode database with no wal
+** that a snapshot cannot be taken on a wal mode database with no wal
** file immediately after it is first opened. At least one transaction
** must be written to it first.
** </ul>
**
** This function may also return SQLITE_NOMEM. If it is called with the
-** database handle in autocommit mode but fails for some other reason,
+** database handle in autocommit mode but fails for some other reason,
** whether or not a read transaction is opened on schema S is undefined.
**
** The [sqlite3_snapshot] object returned from a successful call to
@@ -9079,38 +10519,38 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
** CAPI3REF: Start a read transaction on an historical snapshot
** METHOD: sqlite3_snapshot
**
-** ^The [sqlite3_snapshot_open(D,S,P)] interface either starts a new read
-** transaction or upgrades an existing one for schema S of
-** [database connection] D such that the read transaction refers to
-** historical [snapshot] P, rather than the most recent change to the
-** database. ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK
+** ^The [sqlite3_snapshot_open(D,S,P)] interface either starts a new read
+** transaction or upgrades an existing one for schema S of
+** [database connection] D such that the read transaction refers to
+** historical [snapshot] P, rather than the most recent change to the
+** database. ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK
** on success or an appropriate [error code] if it fails.
**
-** ^In order to succeed, the database connection must not be in
+** ^In order to succeed, the database connection must not be in
** [autocommit mode] when [sqlite3_snapshot_open(D,S,P)] is called. If there
** is already a read transaction open on schema S, then the database handle
** must have no active statements (SELECT statements that have been passed
-** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()).
+** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()).
** SQLITE_ERROR is returned if either of these conditions is violated, or
** if schema S does not exist, or if the snapshot object is invalid.
**
** ^A call to sqlite3_snapshot_open() will fail to open if the specified
-** snapshot has been overwritten by a [checkpoint]. In this case
+** snapshot has been overwritten by a [checkpoint]. In this case
** SQLITE_ERROR_SNAPSHOT is returned.
**
-** If there is already a read transaction open when this function is
+** If there is already a read transaction open when this function is
** invoked, then the same read transaction remains open (on the same
** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_ERROR_SNAPSHOT
** is returned. If another error code - for example SQLITE_PROTOCOL or an
** SQLITE_IOERR error code - is returned, then the final state of the
-** read transaction is undefined. If SQLITE_OK is returned, then the
+** read transaction is undefined. If SQLITE_OK is returned, then the
** read transaction is now open on database snapshot P.
**
** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
** database connection D does not know that the database file for
** schema S is in [WAL mode]. A database connection might not know
** that the database file is in [WAL mode] if there has been no prior
-** I/O on that database connection, or if the database entered [WAL mode]
+** I/O on that database connection, or if the database entered [WAL mode]
** after the most recent I/O on the database connection.)^
** (Hint: Run "[PRAGMA application_id]" against a newly opened
** database connection in order to make it ready to use snapshots.)
@@ -9142,17 +10582,17 @@ SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
** METHOD: sqlite3_snapshot
**
** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages
-** of two valid snapshot handles.
+** of two valid snapshot handles.
**
-** If the two snapshot handles are not associated with the same database
-** file, the result of the comparison is undefined.
+** If the two snapshot handles are not associated with the same database
+** file, the result of the comparison is undefined.
**
** Additionally, the result of the comparison is only valid if both of the
** snapshot handles were obtained by calling sqlite3_snapshot_get() since the
** last time the wal file was deleted. The wal file is deleted when the
** database is changed back to rollback mode or when the number of database
-** clients drops to zero. If either snapshot handle was obtained before the
-** wal file was last deleted, the value returned by this function
+** clients drops to zero. If either snapshot handle was obtained before the
+** wal file was last deleted, the value returned by this function
** is undefined.
**
** Otherwise, this API returns a negative value if P1 refers to an older
@@ -9217,16 +10657,23 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
** representation of the database will usually only exist if there has
** been a prior call to [sqlite3_deserialize(D,S,...)] with the same
** values of D and S.
-** The size of the database is written into *P even if the
+** The size of the database is written into *P even if the
** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
** of the database exists.
**
+** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set,
+** the returned buffer content will remain accessible and unchanged
+** until either the next write operation on the connection or when
+** the connection is closed, and applications must not modify the
+** buffer. If the bit had been clear, the returned buffer will not
+** be accessed by SQLite after the call.
+**
** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
** allocation error occurs.
**
-** This interface is only available if SQLite is compiled with the
-** [SQLITE_ENABLE_DESERIALIZE] option.
+** This interface is omitted if SQLite is compiled with the
+** [SQLITE_OMIT_DESERIALIZE] option.
*/
SQLITE_API unsigned char *sqlite3_serialize(
sqlite3 *db, /* The database connection */
@@ -9254,7 +10701,7 @@ SQLITE_API unsigned char *sqlite3_serialize(
/*
** CAPI3REF: Deserialize a database
**
-** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
+** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
** [database connection] D to disconnect from database S and then
** reopen S as an in-memory database based on the serialization contained
** in P. The serialized database P is N bytes in size. M is the size of
@@ -9269,16 +10716,30 @@ SQLITE_API unsigned char *sqlite3_serialize(
** SQLite will try to increase the buffer size using sqlite3_realloc64()
** if writes on the database cause it to grow larger than M bytes.
**
+** Applications must not modify the buffer P or invalidate it before
+** the database connection D is closed.
+**
** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the
** database is currently in a read transaction or is involved in a backup
** operation.
**
-** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
+** It is not possible to deserialized into the TEMP database. If the
+** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the
+** function returns SQLITE_ERROR.
+**
+** The deserialized database should not be in [WAL mode]. If the database
+** is in WAL mode, then any attempt to use the database file will result
+** in an [SQLITE_CANTOPEN] error. The application can set the
+** [file format version numbers] (bytes 18 and 19) of the input database P
+** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the
+** database file into rollback mode and work around this limitation.
+**
+** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
** [sqlite3_free()] is invoked on argument P prior to returning.
**
-** This interface is only available if SQLite is compiled with the
-** [SQLITE_ENABLE_DESERIALIZE] option.
+** This interface is omitted if SQLite is compiled with the
+** [SQLITE_OMIT_DESERIALIZE] option.
*/
SQLITE_API int sqlite3_deserialize(
sqlite3 *db, /* The database connection */
@@ -9322,6 +10783,19 @@ SQLITE_API int sqlite3_deserialize(
# undef double
#endif
+#if defined(__wasi__)
+# undef SQLITE_WASI
+# define SQLITE_WASI 1
+# undef SQLITE_OMIT_WAL
+# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */
+# ifndef SQLITE_OMIT_LOAD_EXTENSION
+# define SQLITE_OMIT_LOAD_EXTENSION
+# endif
+# ifndef SQLITE_THREADSAFE
+# define SQLITE_THREADSAFE 0
+# endif
+#endif
+
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif
@@ -9388,7 +10862,7 @@ struct sqlite3_rtree_geometry {
};
/*
-** Register a 2nd-generation geometry callback named zScore that can be
+** Register a 2nd-generation geometry callback named zScore that can be
** used as part of an R-Tree geometry query as follows:
**
** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...)
@@ -9403,7 +10877,7 @@ SQLITE_API int sqlite3_rtree_query_callback(
/*
-** A pointer to a structure of the following type is passed as the
+** A pointer to a structure of the following type is passed as the
** argument to scored geometry callback registered using
** sqlite3_rtree_query_callback().
**
@@ -9498,7 +10972,7 @@ typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;
** is not possible for an application to register a pre-update hook on a
** database handle that has one or more session objects attached. Nor is
** it possible to create a session object attached to a database handle for
-** which a pre-update hook is already defined. The results of attempting
+** which a pre-update hook is already defined. The results of attempting
** either of these things are undefined.
**
** The session object will be used to create changesets for tables in
@@ -9516,17 +10990,62 @@ SQLITE_API int sqlite3session_create(
** CAPI3REF: Delete A Session Object
** DESTRUCTOR: sqlite3_session
**
-** Delete a session object previously allocated using
+** Delete a session object previously allocated using
** [sqlite3session_create()]. Once a session object has been deleted, the
** results of attempting to use pSession with any other session module
** function are undefined.
**
** Session objects must be deleted before the database handle to which they
-** are attached is closed. Refer to the documentation for
+** are attached is closed. Refer to the documentation for
** [sqlite3session_create()] for details.
*/
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
+/*
+** CAPI3REF: Configure a Session Object
+** METHOD: sqlite3_session
+**
+** This method is used to configure a session object after it has been
+** created. At present the only valid values for the second parameter are
+** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID].
+**
+*/
+SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);
+
+/*
+** CAPI3REF: Options for sqlite3session_object_config
+**
+** The following values may passed as the the 2nd parameter to
+** sqlite3session_object_config().
+**
+** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd>
+** This option is used to set, clear or query the flag that enables
+** the [sqlite3session_changeset_size()] API. Because it imposes some
+** computational overhead, this API is disabled by default. Argument
+** pArg must point to a value of type (int). If the value is initially
+** 0, then the sqlite3session_changeset_size() API is disabled. If it
+** is greater than 0, then the same API is enabled. Or, if the initial
+** value is less than zero, no change is made. In all cases the (int)
+** variable is set to 1 if the sqlite3session_changeset_size() API is
+** enabled following the current call, or 0 otherwise.
+**
+** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
+** the first table has been attached to the session object.
+**
+** <dt>SQLITE_SESSION_OBJCONFIG_ROWID <dd>
+** This option is used to set, clear or query the flag that enables
+** collection of data for tables with no explicit PRIMARY KEY.
+**
+** Normally, tables with no explicit PRIMARY KEY are simply ignored
+** by the sessions module. However, if this flag is set, it behaves
+** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted
+** as their leftmost columns.
+**
+** It is an error (SQLITE_MISUSE) to attempt to modify this setting after
+** the first table has been attached to the session object.
+*/
+#define SQLITE_SESSION_OBJCONFIG_SIZE 1
+#define SQLITE_SESSION_OBJCONFIG_ROWID 2
/*
** CAPI3REF: Enable Or Disable A Session Object
@@ -9540,10 +11059,10 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession);
** the eventual changesets.
**
** Passing zero to this function disables the session. Passing a value
-** greater than zero enables it. Passing a value less than zero is a
+** greater than zero enables it. Passing a value less than zero is a
** no-op, and may be used to query the current state of the session.
**
-** The return value indicates the final state of the session object: 0 if
+** The return value indicates the final state of the session object: 0 if
** the session is disabled, or 1 if it is enabled.
*/
SQLITE_API int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
@@ -9558,7 +11077,7 @@ SQLITE_API int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
** <ul>
** <li> The session object "indirect" flag is set when the change is
** made, or
-** <li> The change is made by an SQL trigger or foreign key action
+** <li> The change is made by an SQL trigger or foreign key action
** instead of directly as a result of a users SQL statement.
** </ul>
**
@@ -9570,10 +11089,10 @@ SQLITE_API int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
** flag. If the second argument passed to this function is zero, then the
** indirect flag is cleared. If it is greater than zero, the indirect flag
** is set. Passing a value less than zero does not modify the current value
-** of the indirect flag, and may be used to query the current state of the
+** of the indirect flag, and may be used to query the current state of the
** indirect flag for the specified session object.
**
-** The return value indicates the final state of the indirect flag: 0 if
+** The return value indicates the final state of the indirect flag: 0 if
** it is clear, or 1 if it is set.
*/
SQLITE_API int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect);
@@ -9583,20 +11102,20 @@ SQLITE_API int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect)
** METHOD: sqlite3_session
**
** If argument zTab is not NULL, then it is the name of a table to attach
-** to the session object passed as the first argument. All subsequent changes
-** made to the table while the session object is enabled will be recorded. See
+** to the session object passed as the first argument. All subsequent changes
+** made to the table while the session object is enabled will be recorded. See
** documentation for [sqlite3session_changeset()] for further details.
**
** Or, if argument zTab is NULL, then changes are recorded for all tables
-** in the database. If additional tables are added to the database (by
-** executing "CREATE TABLE" statements) after this call is made, changes for
+** in the database. If additional tables are added to the database (by
+** executing "CREATE TABLE" statements) after this call is made, changes for
** the new tables are also recorded.
**
** Changes can only be recorded for tables that have a PRIMARY KEY explicitly
-** defined as part of their CREATE TABLE statement. It does not matter if the
+** defined as part of their CREATE TABLE statement. It does not matter if the
** PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias) or not. The PRIMARY
** KEY may consist of a single column, or may be a composite key.
-**
+**
** It is not an error if the named table does not exist in the database. Nor
** is it an error if the named table does not have a PRIMARY KEY. However,
** no changes will be recorded in either of these scenarios.
@@ -9604,29 +11123,29 @@ SQLITE_API int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect)
** Changes are not recorded for individual rows that have NULL values stored
** in one or more of their PRIMARY KEY columns.
**
-** SQLITE_OK is returned if the call completes without error. Or, if an error
+** SQLITE_OK is returned if the call completes without error. Or, if an error
** occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
**
** <h3>Special sqlite_stat1 Handling</h3>
**
-** As of SQLite version 3.22.0, the "sqlite_stat1" table is an exception to
+** As of SQLite version 3.22.0, the "sqlite_stat1" table is an exception to
** some of the rules above. In SQLite, the schema of sqlite_stat1 is:
** <pre>
-** &nbsp; CREATE TABLE sqlite_stat1(tbl,idx,stat)
+** &nbsp; CREATE TABLE sqlite_stat1(tbl,idx,stat)
** </pre>
**
-** Even though sqlite_stat1 does not have a PRIMARY KEY, changes are
-** recorded for it as if the PRIMARY KEY is (tbl,idx). Additionally, changes
+** Even though sqlite_stat1 does not have a PRIMARY KEY, changes are
+** recorded for it as if the PRIMARY KEY is (tbl,idx). Additionally, changes
** are recorded for rows for which (idx IS NULL) is true. However, for such
** rows a zero-length blob (SQL value X'') is stored in the changeset or
** patchset instead of a NULL value. This allows such changesets to be
** manipulated by legacy implementations of sqlite3changeset_invert(),
** concat() and similar.
**
-** The sqlite3changeset_apply() function automatically converts the
+** The sqlite3changeset_apply() function automatically converts the
** zero-length blob back to a NULL value when updating the sqlite_stat1
** table. However, if the application calls sqlite3changeset_new(),
-** sqlite3changeset_old() or sqlite3changeset_conflict on a changeset
+** sqlite3changeset_old() or sqlite3changeset_conflict on a changeset
** iterator directly (including on a changeset iterator passed to a
** conflict-handler callback) then the X'' value is returned. The application
** must translate X'' to NULL itself if required.
@@ -9645,10 +11164,10 @@ SQLITE_API int sqlite3session_attach(
** CAPI3REF: Set a table filter on a Session Object.
** METHOD: sqlite3_session
**
-** The second argument (xFilter) is the "filter callback". For changes to rows
+** The second argument (xFilter) is the "filter callback". For changes to rows
** in tables that are not attached to the Session object, the filter is called
-** to determine whether changes to the table's rows should be tracked or not.
-** If xFilter returns 0, changes is not tracked. Note that once a table is
+** to determine whether changes to the table's rows should be tracked or not.
+** If xFilter returns 0, changes are not tracked. Note that once a table is
** attached, xFilter will not be called again.
*/
SQLITE_API void sqlite3session_table_filter(
@@ -9664,9 +11183,9 @@ SQLITE_API void sqlite3session_table_filter(
** CAPI3REF: Generate A Changeset From A Session Object
** METHOD: sqlite3_session
**
-** Obtain a changeset containing changes to the tables attached to the
-** session object passed as the first argument. If successful,
-** set *ppChangeset to point to a buffer containing the changeset
+** Obtain a changeset containing changes to the tables attached to the
+** session object passed as the first argument. If successful,
+** set *ppChangeset to point to a buffer containing the changeset
** and *pnChangeset to the size of the changeset in bytes before returning
** SQLITE_OK. If an error occurs, set both *ppChangeset and *pnChangeset to
** zero and return an SQLite error code.
@@ -9681,7 +11200,7 @@ SQLITE_API void sqlite3session_table_filter(
** modifies the values of primary key columns. If such a change is made, it
** is represented in a changeset as a DELETE followed by an INSERT.
**
-** Changes are not recorded for rows that have NULL values stored in one or
+** Changes are not recorded for rows that have NULL values stored in one or
** more of their PRIMARY KEY columns. If such a row is inserted or deleted,
** no corresponding change is present in the changesets returned by this
** function. If an existing row with one or more NULL values stored in
@@ -9734,14 +11253,14 @@ SQLITE_API void sqlite3session_table_filter(
** <ul>
** <li> For each record generated by an insert, the database is queried
** for a row with a matching primary key. If one is found, an INSERT
-** change is added to the changeset. If no such row is found, no change
+** change is added to the changeset. If no such row is found, no change
** is added to the changeset.
**
-** <li> For each record generated by an update or delete, the database is
+** <li> For each record generated by an update or delete, the database is
** queried for a row with a matching primary key. If such a row is
** found and one or more of the non-primary key fields have been
-** modified from their original values, an UPDATE change is added to
-** the changeset. Or, if no such row is found in the table, a DELETE
+** modified from their original values, an UPDATE change is added to
+** the changeset. Or, if no such row is found in the table, a DELETE
** change is added to the changeset. If there is a row with a matching
** primary key in the database, but all fields contain their original
** values, no change is added to the changeset.
@@ -9749,7 +11268,7 @@ SQLITE_API void sqlite3session_table_filter(
**
** This means, amongst other things, that if a row is inserted and then later
** deleted while a session object is active, neither the insert nor the delete
-** will be present in the changeset. Or if a row is deleted and then later a
+** will be present in the changeset. Or if a row is deleted and then later a
** row with the same primary key values inserted while a session object is
** active, the resulting changeset will contain an UPDATE change instead of
** a DELETE and an INSERT.
@@ -9758,10 +11277,10 @@ SQLITE_API void sqlite3session_table_filter(
** it does not accumulate records when rows are inserted, updated or deleted.
** This may appear to have some counter-intuitive effects if a single row
** is written to more than once during a session. For example, if a row
-** is inserted while a session object is enabled, then later deleted while
+** is inserted while a session object is enabled, then later deleted while
** the same session object is disabled, no INSERT record will appear in the
** changeset, even though the delete took place while the session was disabled.
-** Or, if one field of a row is updated while a session is disabled, and
+** Or, if one field of a row is updated while a session is disabled, and
** another field of the same row is updated while the session is enabled, the
** resulting changeset will contain an UPDATE change that updates both fields.
*/
@@ -9772,6 +11291,22 @@ SQLITE_API int sqlite3session_changeset(
);
/*
+** CAPI3REF: Return An Upper-limit For The Size Of The Changeset
+** METHOD: sqlite3_session
+**
+** By default, this function always returns 0. For it to return
+** a useful result, the sqlite3_session object must have been configured
+** to enable this API using sqlite3session_object_config() with the
+** SQLITE_SESSION_OBJCONFIG_SIZE verb.
+**
+** When enabled, this function returns an upper limit, in bytes, for the size
+** of the changeset that might be produced if sqlite3session_changeset() were
+** called. The final changeset size might be equal to or smaller than the
+** size in bytes returned by this function.
+*/
+SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession);
+
+/*
** CAPI3REF: Load The Difference Between Tables Into A Session
** METHOD: sqlite3_session
**
@@ -9782,7 +11317,7 @@ SQLITE_API int sqlite3session_changeset(
** an error).
**
** Argument zFromDb must be the name of a database ("main", "temp" etc.)
-** attached to the same database handle as the session object that contains
+** attached to the same database handle as the session object that contains
** a table compatible with the table attached to the session by this function.
** A table is considered compatible if it:
**
@@ -9798,33 +11333,33 @@ SQLITE_API int sqlite3session_changeset(
** APIs, tables without PRIMARY KEYs are simply ignored.
**
** This function adds a set of changes to the session object that could be
-** used to update the table in database zFrom (call this the "from-table")
-** so that its content is the same as the table attached to the session
+** used to update the table in database zFrom (call this the "from-table")
+** so that its content is the same as the table attached to the session
** object (call this the "to-table"). Specifically:
**
** <ul>
-** <li> For each row (primary key) that exists in the to-table but not in
+** <li> For each row (primary key) that exists in the to-table but not in
** the from-table, an INSERT record is added to the session object.
**
-** <li> For each row (primary key) that exists in the to-table but not in
+** <li> For each row (primary key) that exists in the to-table but not in
** the from-table, a DELETE record is added to the session object.
**
-** <li> For each row (primary key) that exists in both tables, but features
+** <li> For each row (primary key) that exists in both tables, but features
** different non-PK values in each, an UPDATE record is added to the
-** session.
+** session.
** </ul>
**
** To clarify, if this function is called and then a changeset constructed
-** using [sqlite3session_changeset()], then after applying that changeset to
-** database zFrom the contents of the two compatible tables would be
+** using [sqlite3session_changeset()], then after applying that changeset to
+** database zFrom the contents of the two compatible tables would be
** identical.
**
** It an error if database zFrom does not exist or does not contain the
** required compatible table.
**
-** If the operation successful, SQLITE_OK is returned. Otherwise, an SQLite
+** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
-** may be set to point to a buffer containing an English language error
+** may be set to point to a buffer containing an English language error
** message. It is the responsibility of the caller to free this buffer using
** sqlite3_free().
*/
@@ -9843,19 +11378,19 @@ SQLITE_API int sqlite3session_diff(
** The differences between a patchset and a changeset are that:
**
** <ul>
-** <li> DELETE records consist of the primary key fields only. The
+** <li> DELETE records consist of the primary key fields only. The
** original values of other fields are omitted.
-** <li> The original values of any modified fields are omitted from
+** <li> The original values of any modified fields are omitted from
** UPDATE records.
** </ul>
**
-** A patchset blob may be used with up to date versions of all
-** sqlite3changeset_xxx API functions except for sqlite3changeset_invert(),
+** A patchset blob may be used with up to date versions of all
+** sqlite3changeset_xxx API functions except for sqlite3changeset_invert(),
** which returns SQLITE_CORRUPT if it is passed a patchset. Similarly,
** attempting to use a patchset blob with old versions of the
-** sqlite3changeset_xxx APIs also provokes an SQLITE_CORRUPT error.
+** sqlite3changeset_xxx APIs also provokes an SQLITE_CORRUPT error.
**
-** Because the non-primary key "old.*" fields are omitted, no
+** Because the non-primary key "old.*" fields are omitted, no
** SQLITE_CHANGESET_DATA conflicts can be detected or reported if a patchset
** is passed to the sqlite3changeset_apply() API. Other conflict types work
** in the same way as for changesets.
@@ -9874,22 +11409,30 @@ SQLITE_API int sqlite3session_patchset(
/*
** CAPI3REF: Test if a changeset has recorded any changes.
**
-** Return non-zero if no changes to attached tables have been recorded by
-** the session object passed as the first argument. Otherwise, if one or
+** Return non-zero if no changes to attached tables have been recorded by
+** the session object passed as the first argument. Otherwise, if one or
** more changes have been recorded, return zero.
**
** Even if this function returns zero, it is possible that calling
** [sqlite3session_changeset()] on the session handle may still return a
-** changeset that contains no changes. This can happen when a row in
-** an attached table is modified and then later on the original values
+** changeset that contains no changes. This can happen when a row in
+** an attached table is modified and then later on the original values
** are restored. However, if this function returns non-zero, then it is
-** guaranteed that a call to sqlite3session_changeset() will return a
+** guaranteed that a call to sqlite3session_changeset() will return a
** changeset containing zero changes.
*/
SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);
/*
-** CAPI3REF: Create An Iterator To Traverse A Changeset
+** CAPI3REF: Query for the amount of heap memory used by a session object.
+**
+** This API returns the total amount of heap memory in bytes currently
+** used by the session object passed as the only argument.
+*/
+SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession);
+
+/*
+** CAPI3REF: Create An Iterator To Traverse A Changeset
** CONSTRUCTOR: sqlite3_changeset_iter
**
** Create an iterator used to iterate through the contents of a changeset.
@@ -9897,7 +11440,7 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);
** is returned. Otherwise, if an error occurs, *pp is set to zero and an
** SQLite error code is returned.
**
-** The following functions can be used to advance and query a changeset
+** The following functions can be used to advance and query a changeset
** iterator created by this function:
**
** <ul>
@@ -9914,12 +11457,12 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);
**
** Assuming the changeset blob was created by one of the
** [sqlite3session_changeset()], [sqlite3changeset_concat()] or
-** [sqlite3changeset_invert()] functions, all changes within the changeset
-** that apply to a single table are grouped together. This means that when
-** an application iterates through a changeset using an iterator created by
-** this function, all changes that relate to a single table are visited
-** consecutively. There is no chance that the iterator will visit a change
-** the applies to table X, then one for table Y, and then later on visit
+** [sqlite3changeset_invert()] functions, all changes within the changeset
+** that apply to a single table are grouped together. This means that when
+** an application iterates through a changeset using an iterator created by
+** this function, all changes that relate to a single table are visited
+** consecutively. There is no chance that the iterator will visit a change
+** the applies to table X, then one for table Y, and then later on visit
** another change for table X.
**
** The behavior of sqlite3changeset_start_v2() and its streaming equivalent
@@ -9959,7 +11502,7 @@ SQLITE_API int sqlite3changeset_start_v2(
** CAPI3REF: Advance A Changeset Iterator
** METHOD: sqlite3_changeset_iter
**
-** This function may only be used with iterators created by function
+** This function may only be used with iterators created by the function
** [sqlite3changeset_start()]. If it is called on an iterator passed to
** a conflict-handler callback by [sqlite3changeset_apply()], SQLITE_MISUSE
** is returned and the call has no effect.
@@ -9970,12 +11513,12 @@ SQLITE_API int sqlite3changeset_start_v2(
** point to the first change in the changeset. Each subsequent call advances
** the iterator to point to the next change in the changeset (if any). If
** no error occurs and the iterator points to a valid change after a call
-** to sqlite3changeset_next() has advanced it, SQLITE_ROW is returned.
+** to sqlite3changeset_next() has advanced it, SQLITE_ROW is returned.
** Otherwise, if all changes in the changeset have already been visited,
** SQLITE_DONE is returned.
**
-** If an error occurs, an SQLite error code is returned. Possible error
-** codes include SQLITE_CORRUPT (if the changeset buffer is corrupt) or
+** If an error occurs, an SQLite error code is returned. Possible error
+** codes include SQLITE_CORRUPT (if the changeset buffer is corrupt) or
** SQLITE_NOMEM.
*/
SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
@@ -9990,18 +11533,23 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
** is not the case, this function returns [SQLITE_MISUSE].
**
-** If argument pzTab is not NULL, then *pzTab is set to point to a
-** nul-terminated utf-8 encoded string containing the name of the table
-** affected by the current change. The buffer remains valid until either
-** sqlite3changeset_next() is called on the iterator or until the
-** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
-** set to the number of columns in the table affected by the change. If
-** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change
+** Arguments pOp, pnCol and pzTab may not be NULL. Upon return, three
+** outputs are set through these pointers:
+**
+** *pOp is set to one of [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE],
+** depending on the type of change that the iterator currently points to;
+**
+** *pnCol is set to the number of columns in the table affected by the change; and
+**
+** *pzTab is set to point to a nul-terminated utf-8 encoded string containing
+** the name of the table affected by the current change. The buffer remains
+** valid until either sqlite3changeset_next() is called on the iterator
+** or until the conflict-handler function returns.
+**
+** If pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
** is an indirect change, or false (0) otherwise. See the documentation for
** [sqlite3session_indirect()] for a description of direct and indirect
-** changes. Finally, if pOp is not NULL, then *pOp is set to one of
-** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the
-** type of change that the iterator currently points to.
+** changes.
**
** If no error occurs, SQLITE_OK is returned. If an error does occur, an
** SQLite error code is returned. The values of the output variables may not
@@ -10054,7 +11602,7 @@ SQLITE_API int sqlite3changeset_pk(
** The pIter argument passed to this function may either be an iterator
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
** created by [sqlite3changeset_start()]. In the latter case, the most recent
-** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
+** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
** Furthermore, it may only be called if the type of change that the iterator
** currently points to is either [SQLITE_DELETE] or [SQLITE_UPDATE]. Otherwise,
** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL.
@@ -10064,9 +11612,9 @@ SQLITE_API int sqlite3changeset_pk(
** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
**
** If successful, this function sets *ppValue to point to a protected
-** sqlite3_value object containing the iVal'th value from the vector of
+** sqlite3_value object containing the iVal'th value from the vector of
** original row values stored as part of the UPDATE or DELETE change and
-** returns SQLITE_OK. The name of the function comes from the fact that this
+** returns SQLITE_OK. The name of the function comes from the fact that this
** is similar to the "old.*" columns available to update or delete triggers.
**
** If some other error occurs (e.g. an OOM condition), an SQLite error code
@@ -10085,7 +11633,7 @@ SQLITE_API int sqlite3changeset_old(
** The pIter argument passed to this function may either be an iterator
** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
** created by [sqlite3changeset_start()]. In the latter case, the most recent
-** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
+** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
** Furthermore, it may only be called if the type of change that the iterator
** currently points to is either [SQLITE_UPDATE] or [SQLITE_INSERT]. Otherwise,
** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL.
@@ -10095,12 +11643,12 @@ SQLITE_API int sqlite3changeset_old(
** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
**
** If successful, this function sets *ppValue to point to a protected
-** sqlite3_value object containing the iVal'th value from the vector of
+** sqlite3_value object containing the iVal'th value from the vector of
** new row values stored as part of the UPDATE or INSERT change and
** returns SQLITE_OK. If the change is an UPDATE and does not include
-** a new value for the requested column, *ppValue is set to NULL and
-** SQLITE_OK returned. The name of the function comes from the fact that
-** this is similar to the "new.*" columns available to update or delete
+** a new value for the requested column, *ppValue is set to NULL and
+** SQLITE_OK returned. The name of the function comes from the fact that
+** this is similar to the "new.*" columns available to update or delete
** triggers.
**
** If some other error occurs (e.g. an OOM condition), an SQLite error code
@@ -10127,7 +11675,7 @@ SQLITE_API int sqlite3changeset_new(
** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
**
** If successful, this function sets *ppValue to point to a protected
-** sqlite3_value object containing the iVal'th value from the
+** sqlite3_value object containing the iVal'th value from the
** "conflicting row" associated with the current conflict-handler callback
** and returns SQLITE_OK.
**
@@ -10171,7 +11719,7 @@ SQLITE_API int sqlite3changeset_fk_conflicts(
** call has no effect.
**
** If an error was encountered within a call to an sqlite3changeset_xxx()
-** function (for example an [SQLITE_CORRUPT] in [sqlite3changeset_next()] or an
+** function (for example an [SQLITE_CORRUPT] in [sqlite3changeset_next()] or an
** [SQLITE_NOMEM] in [sqlite3changeset_new()]) then an error code corresponding
** to that error is returned by this function. Otherwise, SQLITE_OK is
** returned. This is to allow the following pattern (pseudo-code):
@@ -10183,7 +11731,7 @@ SQLITE_API int sqlite3changeset_fk_conflicts(
** }
** rc = sqlite3changeset_finalize();
** if( rc!=SQLITE_OK ){
-** // An error has occurred
+** // An error has occurred
** }
** </pre>
*/
@@ -10211,7 +11759,7 @@ SQLITE_API int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);
** zeroed and an SQLite error code returned.
**
** It is the responsibility of the caller to eventually call sqlite3_free()
-** on the *ppOut pointer to free the buffer allocation following a successful
+** on the *ppOut pointer to free the buffer allocation following a successful
** call to this function.
**
** WARNING/TODO: This function currently assumes that the input is a valid
@@ -10225,11 +11773,11 @@ SQLITE_API int sqlite3changeset_invert(
/*
** CAPI3REF: Concatenate Two Changeset Objects
**
-** This function is used to concatenate two changesets, A and B, into a
+** This function is used to concatenate two changesets, A and B, into a
** single changeset. The result is a changeset equivalent to applying
-** changeset A followed by changeset B.
+** changeset A followed by changeset B.
**
-** This function combines the two input changesets using an
+** This function combines the two input changesets using an
** sqlite3_changegroup object. Calling it produces similar results as the
** following code fragment:
**
@@ -10259,9 +11807,21 @@ SQLITE_API int sqlite3changeset_concat(
/*
+** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
+*/
+SQLITE_API int sqlite3changeset_upgrade(
+ sqlite3 *db,
+ const char *zDb,
+ int nIn, const void *pIn, /* Input changeset */
+ int *pnOut, void **ppOut /* OUT: Inverse of input */
+);
+
+
+
+/*
** CAPI3REF: Changegroup Handle
**
-** A changegroup is an object used to combine two or more
+** A changegroup is an object used to combine two or more
** [changesets] or [patchsets]
*/
typedef struct sqlite3_changegroup sqlite3_changegroup;
@@ -10277,7 +11837,7 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
**
** If successful, this function returns SQLITE_OK and populates (*pp) with
** a pointer to a new sqlite3_changegroup object before returning. The caller
-** should eventually free the returned object using a call to
+** should eventually free the returned object using a call to
** sqlite3changegroup_delete(). If an error occurs, an SQLite error code
** (i.e. SQLITE_NOMEM) is returned and *pp is set to NULL.
**
@@ -10289,7 +11849,7 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
** <li> Zero or more changesets (or patchsets) are added to the object
** by calling sqlite3changegroup_add().
**
-** <li> The result of combining all input changesets together is obtained
+** <li> The result of combining all input changesets together is obtained
** by the application via a call to sqlite3changegroup_output().
**
** <li> The object is deleted using a call to sqlite3changegroup_delete().
@@ -10298,18 +11858,50 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
** Any number of calls to add() and output() may be made between the calls to
** new() and delete(), and in any order.
**
-** As well as the regular sqlite3changegroup_add() and
+** As well as the regular sqlite3changegroup_add() and
** sqlite3changegroup_output() functions, also available are the streaming
** versions sqlite3changegroup_add_strm() and sqlite3changegroup_output_strm().
*/
SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
/*
+** CAPI3REF: Add a Schema to a Changegroup
+** METHOD: sqlite3_changegroup_schema
+**
+** This method may be used to optionally enforce the rule that the changesets
+** added to the changegroup handle must match the schema of database zDb
+** ("main", "temp", or the name of an attached database). If
+** sqlite3changegroup_add() is called to add a changeset that is not compatible
+** with the configured schema, SQLITE_SCHEMA is returned and the changegroup
+** object is left in an undefined state.
+**
+** A changeset schema is considered compatible with the database schema in
+** the same way as for sqlite3changeset_apply(). Specifically, for each
+** table in the changeset, there exists a database table with:
+**
+** <ul>
+** <li> The name identified by the changeset, and
+** <li> at least as many columns as recorded in the changeset, and
+** <li> the primary key columns in the same position as recorded in
+** the changeset.
+** </ul>
+**
+** The output of the changegroup object always has the same schema as the
+** database nominated using this function. In cases where changesets passed
+** to sqlite3changegroup_add() have fewer columns than the corresponding table
+** in the database schema, these are filled in using the default column
+** values from the database schema. This makes it possible to combined
+** changesets that have different numbers of columns for a single table
+** within a changegroup, provided that they are otherwise compatible.
+*/
+SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);
+
+/*
** CAPI3REF: Add A Changeset To A Changegroup
** METHOD: sqlite3_changegroup
**
** Add all changes within the changeset (or patchset) in buffer pData (size
-** nData bytes) to the changegroup.
+** nData bytes) to the changegroup.
**
** If the buffer contains a patchset, then all prior calls to this function
** on the same changegroup object must also have specified patchsets. Or, if
@@ -10336,7 +11928,7 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
** changeset was recorded immediately after the changesets already
** added to the changegroup.
** <tr><td>INSERT <td>UPDATE <td>
-** The INSERT change remains in the changegroup. The values in the
+** The INSERT change remains in the changegroup. The values in the
** INSERT change are modified as if the row was inserted by the
** existing change and then updated according to the new change.
** <tr><td>INSERT <td>DELETE <td>
@@ -10347,17 +11939,17 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
** changeset was recorded immediately after the changesets already
** added to the changegroup.
** <tr><td>UPDATE <td>UPDATE <td>
-** The existing UPDATE remains within the changegroup. It is amended
-** so that the accompanying values are as if the row was updated once
+** The existing UPDATE remains within the changegroup. It is amended
+** so that the accompanying values are as if the row was updated once
** by the existing change and then again by the new change.
** <tr><td>UPDATE <td>DELETE <td>
** The existing UPDATE is replaced by the new DELETE within the
** changegroup.
** <tr><td>DELETE <td>INSERT <td>
** If one or more of the column values in the row inserted by the
-** new change differ from those in the row deleted by the existing
+** new change differ from those in the row deleted by the existing
** change, the existing DELETE is replaced by an UPDATE within the
-** changegroup. Otherwise, if the inserted row is exactly the same
+** changegroup. Otherwise, if the inserted row is exactly the same
** as the deleted row, the existing DELETE is simply discarded.
** <tr><td>DELETE <td>UPDATE <td>
** The new change is ignored. This case does not occur if the new
@@ -10372,13 +11964,18 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
** If the new changeset contains changes to a table that is already present
** in the changegroup, then the number of columns and the position of the
** primary key columns for the table must be consistent. If this is not the
-** case, this function fails with SQLITE_SCHEMA. If the input changeset
-** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
-** returned. Or, if an out-of-memory condition occurs during processing, this
-** function returns SQLITE_NOMEM. In all cases, if an error occurs the
-** final contents of the changegroup is undefined.
+** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup
+** object has been configured with a database schema using the
+** sqlite3changegroup_schema() API, then it is possible to combine changesets
+** with different numbers of columns for a single table, provided that
+** they are otherwise compatible.
+**
+** If the input changeset appears to be corrupt and the corruption is
+** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition
+** occurs during processing, this function returns SQLITE_NOMEM.
**
-** If no error occurs, SQLITE_OK is returned.
+** In all cases, if an error occurs the state of the final contents of the
+** changegroup is undefined. If no error occurs, SQLITE_OK is returned.
*/
SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
@@ -10402,7 +11999,7 @@ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pDa
**
** If an error occurs, an SQLite error code is returned and the output
** variables (*pnData) and (*ppData) are set to 0. Otherwise, SQLITE_OK
-** is returned and the output variables are set to the size of and a
+** is returned and the output variables are set to the size of and a
** pointer to the output buffer, respectively. In this case it is the
** responsibility of the caller to eventually free the buffer using a
** call to sqlite3_free().
@@ -10424,7 +12021,7 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
**
** Apply a changeset or patchset to a database. These functions attempt to
** update the "main" database attached to handle db with the changes found in
-** the changeset passed via the second and third arguments.
+** the changeset passed via the second and third arguments.
**
** The fourth argument (xFilter) passed to these functions is the "filter
** callback". If it is not NULL, then for each table affected by at least one
@@ -10435,16 +12032,16 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
** Otherwise, if the return value is non-zero or the xFilter argument to
** is NULL, all changes related to the table are attempted.
**
-** For each table that is not excluded by the filter callback, this function
-** tests that the target database contains a compatible table. A table is
+** For each table that is not excluded by the filter callback, this function
+** tests that the target database contains a compatible table. A table is
** considered compatible if all of the following are true:
**
** <ul>
-** <li> The table has the same name as the name recorded in the
+** <li> The table has the same name as the name recorded in the
** changeset, and
-** <li> The table has at least as many columns as recorded in the
+** <li> The table has at least as many columns as recorded in the
** changeset, and
-** <li> The table has primary key columns in the same position as
+** <li> The table has primary key columns in the same position as
** recorded in the changeset.
** </ul>
**
@@ -10453,11 +12050,11 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
** via the sqlite3_log() mechanism with the error code SQLITE_SCHEMA. At most
** one such warning is issued for each table in the changeset.
**
-** For each change for which there is a compatible table, an attempt is made
-** to modify the table contents according to the UPDATE, INSERT or DELETE
-** change. If a change cannot be applied cleanly, the conflict handler
-** function passed as the fifth argument to sqlite3changeset_apply() may be
-** invoked. A description of exactly when the conflict handler is invoked for
+** For each change for which there is a compatible table, an attempt is made
+** to modify the table contents according to the UPDATE, INSERT or DELETE
+** change. If a change cannot be applied cleanly, the conflict handler
+** function passed as the fifth argument to sqlite3changeset_apply() may be
+** invoked. A description of exactly when the conflict handler is invoked for
** each type of change is below.
**
** Unlike the xFilter argument, xConflict may not be passed NULL. The results
@@ -10465,23 +12062,23 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
** argument are undefined.
**
** Each time the conflict handler function is invoked, it must return one
-** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or
+** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or
** [SQLITE_CHANGESET_REPLACE]. SQLITE_CHANGESET_REPLACE may only be returned
** if the second argument passed to the conflict handler is either
** SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If the conflict-handler
** returns an illegal value, any changes already made are rolled back and
-** the call to sqlite3changeset_apply() returns SQLITE_MISUSE. Different
+** the call to sqlite3changeset_apply() returns SQLITE_MISUSE. Different
** actions are taken by sqlite3changeset_apply() depending on the value
** returned by each invocation of the conflict-handler function. Refer to
-** the documentation for the three
+** the documentation for the three
** [SQLITE_CHANGESET_OMIT|available return values] for details.
**
** <dl>
** <dt>DELETE Changes<dd>
-** For each DELETE change, the function checks if the target database
-** contains a row with the same primary key value (or values) as the
-** original row values stored in the changeset. If it does, and the values
-** stored in all non-primary key columns also match the values stored in
+** For each DELETE change, the function checks if the target database
+** contains a row with the same primary key value (or values) as the
+** original row values stored in the changeset. If it does, and the values
+** stored in all non-primary key columns also match the values stored in
** the changeset the row is deleted from the target database.
**
** If a row with matching primary key values is found, but one or more of
@@ -10510,22 +12107,22 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
** database table, the trailing fields are populated with their default
** values.
**
-** If the attempt to insert the row fails because the database already
+** If the attempt to insert the row fails because the database already
** contains a row with the same primary key values, the conflict handler
-** function is invoked with the second argument set to
+** function is invoked with the second argument set to
** [SQLITE_CHANGESET_CONFLICT].
**
** If the attempt to insert the row fails because of some other constraint
-** violation (e.g. NOT NULL or UNIQUE), the conflict handler function is
+** violation (e.g. NOT NULL or UNIQUE), the conflict handler function is
** invoked with the second argument set to [SQLITE_CHANGESET_CONSTRAINT].
-** This includes the case where the INSERT operation is re-attempted because
-** an earlier call to the conflict handler function returned
+** This includes the case where the INSERT operation is re-attempted because
+** an earlier call to the conflict handler function returned
** [SQLITE_CHANGESET_REPLACE].
**
** <dt>UPDATE Changes<dd>
-** For each UPDATE change, the function checks if the target database
-** contains a row with the same primary key value (or values) as the
-** original row values stored in the changeset. If it does, and the values
+** For each UPDATE change, the function checks if the target database
+** contains a row with the same primary key value (or values) as the
+** original row values stored in the changeset. If it does, and the values
** stored in all modified non-primary key columns also match the values
** stored in the changeset the row is updated within the target database.
**
@@ -10541,28 +12138,28 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND]
** passed as the second argument.
**
-** If the UPDATE operation is attempted, but SQLite returns
-** SQLITE_CONSTRAINT, the conflict-handler function is invoked with
+** If the UPDATE operation is attempted, but SQLite returns
+** SQLITE_CONSTRAINT, the conflict-handler function is invoked with
** [SQLITE_CHANGESET_CONSTRAINT] passed as the second argument.
-** This includes the case where the UPDATE operation is attempted after
+** This includes the case where the UPDATE operation is attempted after
** an earlier call to the conflict handler function returned
-** [SQLITE_CHANGESET_REPLACE].
+** [SQLITE_CHANGESET_REPLACE].
** </dl>
**
** It is safe to execute SQL statements, including those that write to the
** table that the callback related to, from within the xConflict callback.
-** This can be used to further customize the applications conflict
+** This can be used to further customize the application's conflict
** resolution strategy.
**
** All changes made by these functions are enclosed in a savepoint transaction.
** If any other error (aside from a constraint failure when attempting to
** write to the target database) occurs, then the savepoint transaction is
-** rolled back, restoring the target database to its original state, and an
+** rolled back, restoring the target database to its original state, and an
** SQLite error code returned.
**
** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
-** may set (*ppRebase) to point to a "rebase" that may be used with the
+** may set (*ppRebase) to point to a "rebase" that may be used with the
** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase)
** is set to the size of the buffer in bytes. It is the responsibility of the
** caller to eventually free any such buffer using sqlite3_free(). The buffer
@@ -10623,18 +12220,39 @@ SQLITE_API int sqlite3changeset_apply_v2(
** SAVEPOINT is committed if the changeset or patchset is successfully
** applied, or rolled back if an error occurs. Specifying this flag
** causes the sessions module to omit this savepoint. In this case, if the
-** caller has an open transaction or savepoint when apply_v2() is called,
+** caller has an open transaction or savepoint when apply_v2() is called,
** it may revert the partially applied changeset by rolling it back.
**
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
** Invert the changeset before applying it. This is equivalent to inverting
** a changeset using sqlite3changeset_invert() before applying it. It is
** an error to specify this flag with a patchset.
+**
+** <dt>SQLITE_CHANGESETAPPLY_IGNORENOOP <dd>
+** Do not invoke the conflict handler callback for any changes that
+** would not actually modify the database even if they were applied.
+** Specifically, this means that the conflict handler is not invoked
+** for:
+** <ul>
+** <li>a delete change if the row being deleted cannot be found,
+** <li>an update change if the modified fields are already set to
+** their new values in the conflicting row, or
+** <li>an insert change if all fields of the conflicting row match
+** the row being inserted.
+** </ul>
+**
+** <dt>SQLITE_CHANGESETAPPLY_FKNOACTION <dd>
+** If this flag it set, then all foreign key constraints in the target
+** database behave as if they were declared with "ON UPDATE NO ACTION ON
+** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL
+** or SET DEFAULT.
*/
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
+#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
+#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
-/*
+/*
** CAPI3REF: Constants Passed To The Conflict Handler
**
** Values that may be passed as the second argument to a conflict-handler.
@@ -10643,32 +12261,32 @@ SQLITE_API int sqlite3changeset_apply_v2(
** <dt>SQLITE_CHANGESET_DATA<dd>
** The conflict handler is invoked with CHANGESET_DATA as the second argument
** when processing a DELETE or UPDATE change if a row with the required
-** PRIMARY KEY fields is present in the database, but one or more other
-** (non primary-key) fields modified by the update do not contain the
+** PRIMARY KEY fields is present in the database, but one or more other
+** (non primary-key) fields modified by the update do not contain the
** expected "before" values.
-**
+**
** The conflicting row, in this case, is the database row with the matching
** primary key.
-**
+**
** <dt>SQLITE_CHANGESET_NOTFOUND<dd>
** The conflict handler is invoked with CHANGESET_NOTFOUND as the second
** argument when processing a DELETE or UPDATE change if a row with the
** required PRIMARY KEY fields is not present in the database.
-**
+**
** There is no conflicting row in this case. The results of invoking the
** sqlite3changeset_conflict() API are undefined.
-**
+**
** <dt>SQLITE_CHANGESET_CONFLICT<dd>
** CHANGESET_CONFLICT is passed as the second argument to the conflict
-** handler while processing an INSERT change if the operation would result
+** handler while processing an INSERT change if the operation would result
** in duplicate primary key values.
-**
+**
** The conflicting row in this case is the database row with the matching
** primary key.
**
** <dt>SQLITE_CHANGESET_FOREIGN_KEY<dd>
** If foreign key handling is enabled, and applying a changeset leaves the
-** database in a state containing foreign key violations, the conflict
+** database in a state containing foreign key violations, the conflict
** handler is invoked with CHANGESET_FOREIGN_KEY as the second argument
** exactly once before the changeset is committed. If the conflict handler
** returns CHANGESET_OMIT, the changes, including those that caused the
@@ -10678,12 +12296,12 @@ SQLITE_API int sqlite3changeset_apply_v2(
** No current or conflicting row information is provided. The only function
** it is possible to call on the supplied sqlite3_changeset_iter handle
** is sqlite3changeset_fk_conflicts().
-**
+**
** <dt>SQLITE_CHANGESET_CONSTRAINT<dd>
-** If any other constraint violation occurs while applying a change (i.e.
-** a UNIQUE, CHECK or NOT NULL constraint), the conflict handler is
+** If any other constraint violation occurs while applying a change (i.e.
+** a UNIQUE, CHECK or NOT NULL constraint), the conflict handler is
** invoked with CHANGESET_CONSTRAINT as the second argument.
-**
+**
** There is no conflicting row in this case. The results of invoking the
** sqlite3changeset_conflict() API are undefined.
**
@@ -10695,7 +12313,7 @@ SQLITE_API int sqlite3changeset_apply_v2(
#define SQLITE_CHANGESET_CONSTRAINT 4
#define SQLITE_CHANGESET_FOREIGN_KEY 5
-/*
+/*
** CAPI3REF: Constants Returned By The Conflict Handler
**
** A conflict handler callback must return one of the following three values.
@@ -10703,13 +12321,13 @@ SQLITE_API int sqlite3changeset_apply_v2(
** <dl>
** <dt>SQLITE_CHANGESET_OMIT<dd>
** If a conflict handler returns this value no special action is taken. The
-** change that caused the conflict is not applied. The session module
+** change that caused the conflict is not applied. The session module
** continues to the next change in the changeset.
**
** <dt>SQLITE_CHANGESET_REPLACE<dd>
** This value may only be returned if the second argument to the conflict
** handler was SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If this
-** is not the case, any changes applied so far are rolled back and the
+** is not the case, any changes applied so far are rolled back and the
** call to sqlite3changeset_apply() returns SQLITE_MISUSE.
**
** If CHANGESET_REPLACE is returned by an SQLITE_CHANGESET_DATA conflict
@@ -10722,7 +12340,7 @@ SQLITE_API int sqlite3changeset_apply_v2(
** the original row is restored to the database before continuing.
**
** <dt>SQLITE_CHANGESET_ABORT<dd>
-** If this value is returned, any changes applied so far are rolled back
+** If this value is returned, any changes applied so far are rolled back
** and the call to sqlite3changeset_apply() returns SQLITE_ABORT.
** </dl>
*/
@@ -10730,20 +12348,20 @@ SQLITE_API int sqlite3changeset_apply_v2(
#define SQLITE_CHANGESET_REPLACE 1
#define SQLITE_CHANGESET_ABORT 2
-/*
+/*
** CAPI3REF: Rebasing changesets
** EXPERIMENTAL
**
** Suppose there is a site hosting a database in state S0. And that
** modifications are made that move that database to state S1 and a
** changeset recorded (the "local" changeset). Then, a changeset based
-** on S0 is received from another site (the "remote" changeset) and
-** applied to the database. The database is then in state
+** on S0 is received from another site (the "remote" changeset) and
+** applied to the database. The database is then in state
** (S1+"remote"), where the exact state depends on any conflict
** resolution decisions (OMIT or REPLACE) made while applying "remote".
-** Rebasing a changeset is to update it to take those conflict
+** Rebasing a changeset is to update it to take those conflict
** resolution decisions into account, so that the same conflicts
-** do not have to be resolved elsewhere in the network.
+** do not have to be resolved elsewhere in the network.
**
** For example, if both the local and remote changesets contain an
** INSERT of the same key on "CREATE TABLE t1(a PRIMARY KEY, b)":
@@ -10762,7 +12380,7 @@ SQLITE_API int sqlite3changeset_apply_v2(
**
** <dl>
** <dt>Local INSERT<dd>
-** This may only conflict with a remote INSERT. If the conflict
+** This may only conflict with a remote INSERT. If the conflict
** resolution was OMIT, then add an UPDATE change to the rebased
** changeset. Or, if the conflict resolution was REPLACE, add
** nothing to the rebased changeset.
@@ -10786,12 +12404,12 @@ SQLITE_API int sqlite3changeset_apply_v2(
** the old.* values are rebased using the new.* values in the remote
** change. Or, if the resolution is REPLACE, then the change is copied
** into the rebased changeset with updates to columns also updated by
-** the conflicting remote UPDATE removed. If this means no columns would
+** the conflicting remote UPDATE removed. If this means no columns would
** be updated, the change is omitted.
** </dl>
**
-** A local change may be rebased against multiple remote changes
-** simultaneously. If a single key is modified by multiple remote
+** A local change may be rebased against multiple remote changes
+** simultaneously. If a single key is modified by multiple remote
** changesets, they are combined as follows before the local changeset
** is rebased:
**
@@ -10804,10 +12422,10 @@ SQLITE_API int sqlite3changeset_apply_v2(
** of the OMIT resolutions.
** </ul>
**
-** Note that conflict resolutions from multiple remote changesets are
-** combined on a per-field basis, not per-row. This means that in the
-** case of multiple remote UPDATE operations, some fields of a single
-** local change may be rebased for REPLACE while others are rebased for
+** Note that conflict resolutions from multiple remote changesets are
+** combined on a per-field basis, not per-row. This means that in the
+** case of multiple remote UPDATE operations, some fields of a single
+** local change may be rebased for REPLACE while others are rebased for
** OMIT.
**
** In order to rebase a local changeset, the remote changeset must first
@@ -10815,7 +12433,7 @@ SQLITE_API int sqlite3changeset_apply_v2(
** the buffer of rebase information captured. Then:
**
** <ol>
-** <li> An sqlite3_rebaser object is created by calling
+** <li> An sqlite3_rebaser object is created by calling
** sqlite3rebaser_create().
** <li> The new object is configured with the rebase buffer obtained from
** sqlite3changeset_apply_v2() by calling sqlite3rebaser_configure().
@@ -10836,8 +12454,8 @@ typedef struct sqlite3_rebaser sqlite3_rebaser;
**
** Allocate a new changeset rebaser object. If successful, set (*ppNew) to
** point to the new object and return SQLITE_OK. Otherwise, if an error
-** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew)
-** to NULL.
+** occurs, return an SQLite error code (e.g. SQLITE_NOMEM) and set (*ppNew)
+** to NULL.
*/
SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew);
@@ -10851,9 +12469,9 @@ SQLITE_API int sqlite3rebaser_create(sqlite3_rebaser **ppNew);
** sqlite3changeset_apply_v2().
*/
SQLITE_API int sqlite3rebaser_configure(
- sqlite3_rebaser*,
+ sqlite3_rebaser*,
int nRebase, const void *pRebase
-);
+);
/*
** CAPI3REF: Rebase a changeset
@@ -10861,9 +12479,9 @@ SQLITE_API int sqlite3rebaser_configure(
**
** Argument pIn must point to a buffer containing a changeset nIn bytes
** in size. This function allocates and populates a buffer with a copy
-** of the changeset rebased rebased according to the configuration of the
+** of the changeset rebased according to the configuration of the
** rebaser object passed as the first argument. If successful, (*ppOut)
-** is set to point to the new buffer containing the rebased changset and
+** is set to point to the new buffer containing the rebased changeset and
** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
** responsibility of the caller to eventually free the new buffer using
** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut)
@@ -10871,8 +12489,8 @@ SQLITE_API int sqlite3rebaser_configure(
*/
SQLITE_API int sqlite3rebaser_rebase(
sqlite3_rebaser*,
- int nIn, const void *pIn,
- int *pnOut, void **ppOut
+ int nIn, const void *pIn,
+ int *pnOut, void **ppOut
);
/*
@@ -10883,30 +12501,30 @@ SQLITE_API int sqlite3rebaser_rebase(
** should be one call to this function for each successful invocation
** of sqlite3rebaser_create().
*/
-SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
+SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
/*
** CAPI3REF: Streaming Versions of API functions.
**
-** The six streaming API xxx_strm() functions serve similar purposes to the
+** The six streaming API xxx_strm() functions serve similar purposes to the
** corresponding non-streaming API functions:
**
** <table border=1 style="margin-left:8ex;margin-right:8ex">
** <tr><th>Streaming function<th>Non-streaming equivalent</th>
-** <tr><td>sqlite3changeset_apply_strm<td>[sqlite3changeset_apply]
-** <tr><td>sqlite3changeset_apply_strm_v2<td>[sqlite3changeset_apply_v2]
-** <tr><td>sqlite3changeset_concat_strm<td>[sqlite3changeset_concat]
-** <tr><td>sqlite3changeset_invert_strm<td>[sqlite3changeset_invert]
-** <tr><td>sqlite3changeset_start_strm<td>[sqlite3changeset_start]
-** <tr><td>sqlite3session_changeset_strm<td>[sqlite3session_changeset]
-** <tr><td>sqlite3session_patchset_strm<td>[sqlite3session_patchset]
+** <tr><td>sqlite3changeset_apply_strm<td>[sqlite3changeset_apply]
+** <tr><td>sqlite3changeset_apply_strm_v2<td>[sqlite3changeset_apply_v2]
+** <tr><td>sqlite3changeset_concat_strm<td>[sqlite3changeset_concat]
+** <tr><td>sqlite3changeset_invert_strm<td>[sqlite3changeset_invert]
+** <tr><td>sqlite3changeset_start_strm<td>[sqlite3changeset_start]
+** <tr><td>sqlite3session_changeset_strm<td>[sqlite3session_changeset]
+** <tr><td>sqlite3session_patchset_strm<td>[sqlite3session_patchset]
** </table>
**
** Non-streaming functions that accept changesets (or patchsets) as input
-** require that the entire changeset be stored in a single buffer in memory.
-** Similarly, those that return a changeset or patchset do so by returning
-** a pointer to a single large buffer allocated using sqlite3_malloc().
-** Normally this is convenient. However, if an application running in a
+** require that the entire changeset be stored in a single buffer in memory.
+** Similarly, those that return a changeset or patchset do so by returning
+** a pointer to a single large buffer allocated using sqlite3_malloc().
+** Normally this is convenient. However, if an application running in a
** low-memory environment is required to handle very large changesets, the
** large contiguous memory allocations required can become onerous.
**
@@ -10928,12 +12546,12 @@ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
** </pre>
**
** Each time the xInput callback is invoked by the sessions module, the first
-** argument passed is a copy of the supplied pIn context pointer. The second
-** argument, pData, points to a buffer (*pnData) bytes in size. Assuming no
-** error occurs the xInput method should copy up to (*pnData) bytes of data
-** into the buffer and set (*pnData) to the actual number of bytes copied
-** before returning SQLITE_OK. If the input is completely exhausted, (*pnData)
-** should be set to zero to indicate this. Or, if an error occurs, an SQLite
+** argument passed is a copy of the supplied pIn context pointer. The second
+** argument, pData, points to a buffer (*pnData) bytes in size. Assuming no
+** error occurs the xInput method should copy up to (*pnData) bytes of data
+** into the buffer and set (*pnData) to the actual number of bytes copied
+** before returning SQLITE_OK. If the input is completely exhausted, (*pnData)
+** should be set to zero to indicate this. Or, if an error occurs, an SQLite
** error code should be returned. In all cases, if an xInput callback returns
** an error, all processing is abandoned and the streaming API function
** returns a copy of the error code to the caller.
@@ -10941,7 +12559,7 @@ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
** In the case of sqlite3changeset_start_strm(), the xInput callback may be
** invoked by the sessions module at any point during the lifetime of the
** iterator. If such an xInput callback returns an error, the iterator enters
-** an error state, whereby all subsequent calls to iterator functions
+** an error state, whereby all subsequent calls to iterator functions
** immediately fail with the same error code as returned by xInput.
**
** Similarly, streaming API functions that return changesets (or patchsets)
@@ -10971,7 +12589,7 @@ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p);
** is immediately abandoned and the streaming API function returns a copy
** of the xOutput error code to the application.
**
-** The sessions module never invokes an xOutput callback with the third
+** The sessions module never invokes an xOutput callback with the third
** parameter set to a value less than or equal to zero. Other than this,
** no guarantees are made as to the size of the chunks of data returned.
*/
@@ -11042,12 +12660,12 @@ SQLITE_API int sqlite3session_patchset_strm(
int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
);
-SQLITE_API int sqlite3changegroup_add_strm(sqlite3_changegroup*,
+SQLITE_API int sqlite3changegroup_add_strm(sqlite3_changegroup*,
int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn
);
SQLITE_API int sqlite3changegroup_output_strm(sqlite3_changegroup*,
- int (*xOutput)(void *pOut, const void *pData, int nData),
+ int (*xOutput)(void *pOut, const void *pData, int nData),
void *pOut
);
SQLITE_API int sqlite3rebaser_rebase_strm(
@@ -11062,16 +12680,16 @@ SQLITE_API int sqlite3rebaser_rebase_strm(
** CAPI3REF: Configure global parameters
**
** The sqlite3session_config() interface is used to make global configuration
-** changes to the sessions module in order to tune it to the specific needs
+** changes to the sessions module in order to tune it to the specific needs
** of the application.
**
** The sqlite3session_config() interface is not threadsafe. If it is invoked
** while any other thread is inside any other sessions method then the
** results are undefined. Furthermore, if it is invoked after any sessions
-** related objects have been created, the results are also undefined.
+** related objects have been created, the results are also undefined.
**
** The first argument to the sqlite3session_config() function must be one
-** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The
+** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The
** interpretation of the (void*) value passed as the second parameter and
** the effect of calling this function depends on the value of the first
** parameter.
@@ -11121,7 +12739,7 @@ SQLITE_API int sqlite3session_config(int op, void *pArg);
**
******************************************************************************
**
-** Interfaces to extend FTS5. Using the interfaces defined in this file,
+** Interfaces to extend FTS5. Using the interfaces defined in this file,
** FTS5 may be extended with:
**
** * custom tokenizers, and
@@ -11165,19 +12783,19 @@ struct Fts5PhraseIter {
** EXTENSION API FUNCTIONS
**
** xUserData(pFts):
-** Return a copy of the context pointer the extension function was
+** Return a copy of the context pointer the extension function was
** registered with.
**
** xColumnTotalSize(pFts, iCol, pnToken):
** If parameter iCol is less than zero, set output variable *pnToken
** to the total number of tokens in the FTS5 table. Or, if iCol is
** non-negative but less than the number of columns in the table, return
-** the total number of tokens in column iCol, considering all rows in
+** the total number of tokens in column iCol, considering all rows in
** the FTS5 table.
**
** If parameter iCol is greater than or equal to the number of columns
** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
-** an OOM condition or IO error), an appropriate SQLite error code is
+** an OOM condition or IO error), an appropriate SQLite error code is
** returned.
**
** xColumnCount(pFts):
@@ -11191,15 +12809,18 @@ struct Fts5PhraseIter {
**
** If parameter iCol is greater than or equal to the number of columns
** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
-** an OOM condition or IO error), an appropriate SQLite error code is
+** an OOM condition or IO error), an appropriate SQLite error code is
** returned.
**
** This function may be quite inefficient if used with an FTS5 table
** created with the "columnsize=0" option.
**
** xColumnText:
-** This function attempts to retrieve the text of column iCol of the
-** current document. If successful, (*pz) is set to point to a buffer
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of columns in the table, SQLITE_RANGE is returned.
+**
+** Otherwise, this function attempts to retrieve the text of column iCol of
+** the current document. If successful, (*pz) is set to point to a buffer
** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
** if an error occurs, an SQLite error code is returned and the final values
@@ -11209,8 +12830,10 @@ struct Fts5PhraseIter {
** Returns the number of phrases in the current query expression.
**
** xPhraseSize:
-** Returns the number of tokens in phrase iPhrase of the query. Phrases
-** are numbered starting from zero.
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of phrases in the current query, as returned by xPhraseCount,
+** 0 is returned. Otherwise, this function returns the number of tokens in
+** phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
** Set *pnInst to the total number of occurrences of all phrases within
@@ -11218,27 +12841,24 @@ struct Fts5PhraseIter {
** an error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" or "detail=column" option. If the FTS5 table is created
-** with either "detail=none" or "detail=column" and "content=" option
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
** (i.e. if it is a contentless table), then this API always returns 0.
**
** xInst:
** Query for the details of phrase match iIdx within the current row.
** Phrase matches are numbered starting from zero, so the iIdx argument
** should be greater than or equal to zero and smaller than the value
-** output by xInstCount().
+** output by xInstCount(). If iIdx is less than zero or greater than
+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
-** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the
-** first token of the phrase. The exception is if the table was created
-** with the offsets=0 option specified. In this case *piOff is always
-** set to -1.
-**
-** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
-** if an error occurs.
+** first token of the phrase. SQLITE_OK is returned if successful, or an
+** error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" or "detail=column" option.
+** "detail=none" or "detail=column" option.
**
** xRowid:
** Returns the rowid of the current row.
@@ -11254,13 +12874,17 @@ struct Fts5PhraseIter {
**
** with $p set to a phrase equivalent to the phrase iPhrase of the
** current query is executed. Any column filter that applies to
-** phrase iPhrase of the current query is included in $p. For each
-** row visited, the callback function passed as the fourth argument
-** is invoked. The context and API objects passed to the callback
+** phrase iPhrase of the current query is included in $p. For each
+** row visited, the callback function passed as the fourth argument
+** is invoked. The context and API objects passed to the callback
** function may be used to access the properties of each matched row.
-** Invoking Api.xUserData() returns a copy of the pointer passed as
+** Invoking Api.xUserData() returns a copy of the pointer passed as
** the third argument to pUserData.
**
+** If parameter iPhrase is less than zero, or greater than or equal to
+** the number of phrases in the query, as returned by xPhraseCount(),
+** this function returns SQLITE_RANGE.
+**
** If the callback function returns any value other than SQLITE_OK, the
** query is abandoned and the xQueryPhrase function returns immediately.
** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
@@ -11273,14 +12897,14 @@ struct Fts5PhraseIter {
**
** xSetAuxdata(pFts5, pAux, xDelete)
**
-** Save the pointer passed as the second argument as the extension functions
+** Save the pointer passed as the second argument as the extension function's
** "auxiliary data". The pointer may then be retrieved by the current or any
** future invocation of the same fts5 extension function made as part of
-** of the same MATCH query using the xGetAuxdata() API.
+** the same MATCH query using the xGetAuxdata() API.
**
** Each extension function is allocated a single auxiliary data slot for
-** each FTS query (MATCH expression). If the extension function is invoked
-** more than once for a single FTS query, then all invocations share a
+** each FTS query (MATCH expression). If the extension function is invoked
+** more than once for a single FTS query, then all invocations share a
** single auxiliary data context.
**
** If there is already an auxiliary data pointer when this function is
@@ -11291,7 +12915,7 @@ struct Fts5PhraseIter {
** The xDelete callback, if one is specified, is also invoked on the
** auxiliary data pointer after the FTS5 query has finished.
**
-** If an error (e.g. an OOM condition) occurs within this function, an
+** If an error (e.g. an OOM condition) occurs within this function,
** the auxiliary data is set to NULL and an error code returned. If the
** xDelete parameter was not NULL, it is invoked on the auxiliary data
** pointer before returning.
@@ -11299,7 +12923,7 @@ struct Fts5PhraseIter {
**
** xGetAuxdata(pFts5, bClear)
**
-** Returns the current auxiliary data pointer for the fts5 extension
+** Returns the current auxiliary data pointer for the fts5 extension
** function. See the xSetAuxdata() method for details.
**
** If the bClear argument is non-zero, then the auxiliary data is cleared
@@ -11319,7 +12943,7 @@ struct Fts5PhraseIter {
** method, to iterate through all instances of a single query phrase within
** the current row. This is the same information as is accessible via the
** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient
-** to use, this API may be faster under some circumstances. To iterate
+** to use, this API may be faster under some circumstances. To iterate
** through instances of phrase iPhrase, use the following code:
**
** Fts5PhraseIter iter;
@@ -11337,8 +12961,8 @@ struct Fts5PhraseIter {
** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below).
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" or "detail=column" option. If the FTS5 table is created
-** with either "detail=none" or "detail=column" and "content=" option
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
** (i.e. if it is a contentless table), then this API always iterates
** through an empty set (all calls to xPhraseFirst() set iCol to -1).
**
@@ -11362,19 +12986,52 @@ struct Fts5PhraseIter {
** }
**
** This API can be quite slow if used with an FTS5 table created with the
-** "detail=none" option. If the FTS5 table is created with either
-** "detail=none" "content=" option (i.e. if it is a contentless table),
-** then this API always iterates through an empty set (all calls to
+** "detail=none" option. If the FTS5 table is created with either
+** "detail=none" "content=" option (i.e. if it is a contentless table),
+** then this API always iterates through an empty set (all calls to
** xPhraseFirstColumn() set iCol to -1).
**
** The information accessed using this API and its companion
** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
** (or xInst/xInstCount). The chief advantage of this API is that it is
** significantly more efficient than those alternatives when used with
-** "detail=column" tables.
+** "detail=column" tables.
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
+**
+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase iPhrase of the current
+** query. Before returning, output parameter *ppToken is set to point
+** to a buffer containing the requested token, and *pnToken to the
+** size of this buffer in bytes.
+**
+** If iPhrase or iToken are less than zero, or if iPhrase is greater than
+** or equal to the number of phrases in the query as reported by
+** xPhraseCount(), or if iToken is equal to or greater than the number of
+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
+ are both zeroed.
+**
+** The output text is not a copy of the query text that specified the
+** token. It is the output of the tokenizer module. For tokendata=1
+** tables, this includes any embedded 0x00 and trailing data.
+**
+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase hit iIdx within the
+** current row. If iIdx is less than zero or greater than or equal to the
+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
+** output variable (*ppToken) is set to point to a buffer containing the
+** matching document token, and (*pnToken) to the size of that buffer in
+** bytes. This API is not available if the specified token matches a
+** prefix query term. In that case both output variables are always set
+** to 0.
+**
+** The output text is not a copy of the document text that was tokenized.
+** It is the output of the tokenizer module. For tokendata=1 tables, this
+** includes any embedded 0x00 and trailing data.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
int iVersion; /* Currently always set to 3 */
@@ -11385,7 +13042,7 @@ struct Fts5ExtensionApi {
int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
- int (*xTokenize)(Fts5Context*,
+ int (*xTokenize)(Fts5Context*,
const char *pText, int nText, /* Text to tokenize */
void *pCtx, /* Context passed to xToken() */
int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
@@ -11412,17 +13069,24 @@ struct Fts5ExtensionApi {
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+
+ /* Below this point are iVersion>=3 only */
+ int (*xQueryToken)(Fts5Context*,
+ int iPhrase, int iToken,
+ const char **ppToken, int *pnToken
+ );
+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
-/*
+/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/
/*************************************************************************
** CUSTOM TOKENIZERS
**
-** Applications may also register custom tokenizer types. A tokenizer
-** is registered by providing fts5 with a populated instance of the
+** Applications may also register custom tokenizer types. A tokenizer
+** is registered by providing fts5 with a populated instance of the
** following structure. All structure methods must be defined, setting
** any member of the fts5_tokenizer struct to NULL leads to undefined
** behaviour. The structure methods are expected to function as follows:
@@ -11433,16 +13097,16 @@ struct Fts5ExtensionApi {
**
** The first argument passed to this function is a copy of the (void*)
** pointer provided by the application when the fts5_tokenizer object
-** was registered with FTS5 (the third argument to xCreateTokenizer()).
+** was registered with FTS5 (the third argument to xCreateTokenizer()).
** The second and third arguments are an array of nul-terminated strings
** containing the tokenizer arguments, if any, specified following the
** tokenizer name as part of the CREATE VIRTUAL TABLE statement used
** to create the FTS5 table.
**
-** The final argument is an output variable. If successful, (*ppOut)
+** The final argument is an output variable. If successful, (*ppOut)
** should be set to point to the new tokenizer handle and SQLITE_OK
** returned. If an error occurs, some value other than SQLITE_OK should
-** be returned. In this case, fts5 assumes that the final value of *ppOut
+** be returned. In this case, fts5 assumes that the final value of *ppOut
** is undefined.
**
** xDelete:
@@ -11451,7 +13115,7 @@ struct Fts5ExtensionApi {
** be invoked exactly once for each successful call to xCreate().
**
** xTokenize:
-** This function is expected to tokenize the nText byte string indicated
+** This function is expected to tokenize the nText byte string indicated
** by argument pText. pText may or may not be nul-terminated. The first
** argument passed to this function is a pointer to an Fts5Tokenizer object
** returned by an earlier call to xCreate().
@@ -11465,8 +13129,8 @@ struct Fts5ExtensionApi {
** determine the set of tokens to add to (or delete from) the
** FTS index.
**
-** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
-** against the FTS index. The tokenizer is being called to tokenize
+** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
+** against the FTS index. The tokenizer is being called to tokenize
** a bareword or quoted string specified as part of the query.
**
** <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as
@@ -11474,10 +13138,10 @@ struct Fts5ExtensionApi {
** followed by a "*" character, indicating that the last token
** returned by the tokenizer will be treated as a token prefix.
**
-** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
+** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
** satisfy an fts5_api.xTokenize() request made by an auxiliary
** function. Or an fts5_api.xColumnSize() request made by the same
-** on a columnsize=0 database.
+** on a columnsize=0 database.
** </ul>
**
** For each token in the input string, the supplied callback xToken() must
@@ -11489,10 +13153,10 @@ struct Fts5ExtensionApi {
** which the token is derived within the input.
**
** The second argument passed to the xToken() callback ("tflags") should
-** normally be set to 0. The exception is if the tokenizer supports
+** normally be set to 0. The exception is if the tokenizer supports
** synonyms. In this case see the discussion below for details.
**
-** FTS5 assumes the xToken() callback is invoked for each token in the
+** FTS5 assumes the xToken() callback is invoked for each token in the
** order that they occur within the input text.
**
** If an xToken() callback returns any value other than SQLITE_OK, then
@@ -11506,7 +13170,7 @@ struct Fts5ExtensionApi {
** SYNONYM SUPPORT
**
** Custom tokenizers may also support synonyms. Consider a case in which a
-** user wishes to query for a phrase such as "first place". Using the
+** user wishes to query for a phrase such as "first place". Using the
** built-in tokenizers, the FTS5 query 'first + place' will match instances
** of "first place" within the document set, but not alternative forms
** such as "1st place". In some applications, it would be better to match
@@ -11515,8 +13179,8 @@ struct Fts5ExtensionApi {
**
** There are several ways to approach this in FTS5:
**
-** <ol><li> By mapping all synonyms to a single token. In this case, the
-** In the above example, this means that the tokenizer returns the
+** <ol><li> By mapping all synonyms to a single token. In this case, using
+** the above example, this means that the tokenizer returns the
** same token for inputs "first" and "1st". Say that token is in
** fact "first", so that when the user inserts the document "I won
** 1st place" entries are added to the index for tokens "i", "won",
@@ -11524,36 +13188,36 @@ struct Fts5ExtensionApi {
** the tokenizer substitutes "first" for "1st" and the query works
** as expected.
**
-** <li> By adding multiple synonyms for a single term to the FTS index.
-** In this case, when tokenizing query text, the tokenizer may
-** provide multiple synonyms for a single term within the document.
-** FTS5 then queries the index for each synonym individually. For
-** example, faced with the query:
+** <li> By querying the index for all synonyms of each query term
+** separately. In this case, when tokenizing query text, the
+** tokenizer may provide multiple synonyms for a single term
+** within the document. FTS5 then queries the index for each
+** synonym individually. For example, faced with the query:
**
** <codeblock>
** ... MATCH 'first place'</codeblock>
**
** the tokenizer offers both "1st" and "first" as synonyms for the
-** first token in the MATCH query and FTS5 effectively runs a query
+** first token in the MATCH query and FTS5 effectively runs a query
** similar to:
**
** <codeblock>
** ... MATCH '(first OR 1st) place'</codeblock>
**
** except that, for the purposes of auxiliary functions, the query
-** still appears to contain just two phrases - "(first OR 1st)"
+** still appears to contain just two phrases - "(first OR 1st)"
** being treated as a single phrase.
**
** <li> By adding multiple synonyms for a single term to the FTS index.
** Using this method, when tokenizing document text, the tokenizer
-** provides multiple synonyms for each token. So that when a
+** provides multiple synonyms for each token. So that when a
** document such as "I won first place" is tokenized, entries are
** added to the FTS index for "i", "won", "first", "1st" and
** "place".
**
** This way, even if the tokenizer does not provide synonyms
-** when tokenizing query text (it should not - to do would be
-** inefficient), it doesn't matter if the user queries for
+** when tokenizing query text (it should not - to do so would be
+** inefficient), it doesn't matter if the user queries for
** 'first + place' or '1st + place', as there are entries in the
** FTS index corresponding to both forms of the first token.
** </ol>
@@ -11574,11 +13238,11 @@ struct Fts5ExtensionApi {
**
** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time
** xToken() is called. Multiple synonyms may be specified for a single token
-** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
+** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
** There is no limit to the number of synonyms that may be provided for a
** single token.
**
-** In many cases, method (1) above is the best approach. It does not add
+** In many cases, method (1) above is the best approach. It does not add
** extra data to the FTS index or require FTS5 to query for multiple terms,
** so it is efficient in terms of disk space and query speed. However, it
** does not support prefix queries very well. If, as suggested above, the
@@ -11590,24 +13254,24 @@ struct Fts5ExtensionApi {
** will not match documents that contain the token "1st" (as the tokenizer
** will probably not map "1s" to any prefix of "first").
**
-** For full prefix support, method (3) may be preferred. In this case,
+** For full prefix support, method (3) may be preferred. In this case,
** because the index contains entries for both "first" and "1st", prefix
** queries such as 'fi*' or '1s*' will match correctly. However, because
** extra entries are added to the FTS index, this method uses more space
** within the database.
**
** Method (2) offers a midpoint between (1) and (3). Using this method,
-** a query such as '1s*' will match documents that contain the literal
+** a query such as '1s*' will match documents that contain the literal
** token "1st", but not "first" (assuming the tokenizer is not able to
** provide synonyms for prefixes). However, a non-prefix query like '1st'
** will match against "1st" and "first". This method does not require
-** extra disk space, as no extra entries are added to the FTS index.
+** extra disk space, as no extra entries are added to the FTS index.
** On the other hand, it may require more CPU cycles to run MATCH queries,
** as separate queries of the FTS index are required for each synonym.
**
** When using methods (2) or (3), it is important that the tokenizer only
-** provide synonyms when tokenizing document text (method (2)) or query
-** text (method (3)), not both. Doing so will not cause any errors, but is
+** provide synonyms when tokenizing document text (method (3)) or query
+** text (method (2)), not both. Doing so will not cause any errors, but is
** inefficient.
*/
typedef struct Fts5Tokenizer Fts5Tokenizer;
@@ -11615,10 +13279,10 @@ typedef struct fts5_tokenizer fts5_tokenizer;
struct fts5_tokenizer {
int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
void (*xDelete)(Fts5Tokenizer*);
- int (*xTokenize)(Fts5Tokenizer*,
+ int (*xTokenize)(Fts5Tokenizer*,
void *pCtx,
int flags, /* Mask of FTS5_TOKENIZE_* flags */
- const char *pText, int nText,
+ const char *pText, int nText,
int (*xToken)(
void *pCtx, /* Copy of 2nd argument to xTokenize() */
int tflags, /* Mask of FTS5_TOKEN_* flags */
@@ -11655,7 +13319,7 @@ struct fts5_api {
int (*xCreateTokenizer)(
fts5_api *pApi,
const char *zName,
- void *pContext,
+ void *pUserData,
fts5_tokenizer *pTokenizer,
void (*xDestroy)(void*)
);
@@ -11664,7 +13328,7 @@ struct fts5_api {
int (*xFindTokenizer)(
fts5_api *pApi,
const char *zName,
- void **ppContext,
+ void **ppUserData,
fts5_tokenizer *pTokenizer
);
@@ -11672,7 +13336,7 @@ struct fts5_api {
int (*xCreateFunction)(
fts5_api *pApi,
const char *zName,
- void *pContext,
+ void *pUserData,
fts5_extension_function xFunction,
void (*xDestroy)(void*)
);
diff --git a/src/libs/3rdparty/sqlite/sqlite3ext.h b/src/libs/3rdparty/sqlite/sqlite3ext.h
index 34c41fd5a9..ae0949baf7 100644
--- a/src/libs/3rdparty/sqlite/sqlite3ext.h
+++ b/src/libs/3rdparty/sqlite/sqlite3ext.h
@@ -319,6 +319,53 @@ struct sqlite3_api_routines {
void(*xDestroy)(void*));
/* Version 3.26.0 and later */
const char *(*normalized_sql)(sqlite3_stmt*);
+ /* Version 3.28.0 and later */
+ int (*stmt_isexplain)(sqlite3_stmt*);
+ int (*value_frombind)(sqlite3_value*);
+ /* Version 3.30.0 and later */
+ int (*drop_modules)(sqlite3*,const char**);
+ /* Version 3.31.0 and later */
+ sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
+ const char *(*uri_key)(const char*,int);
+ const char *(*filename_database)(const char*);
+ const char *(*filename_journal)(const char*);
+ const char *(*filename_wal)(const char*);
+ /* Version 3.32.0 and later */
+ const char *(*create_filename)(const char*,const char*,const char*,
+ int,const char**);
+ void (*free_filename)(const char*);
+ sqlite3_file *(*database_file_object)(const char*);
+ /* Version 3.34.0 and later */
+ int (*txn_state)(sqlite3*,const char*);
+ /* Version 3.36.1 and later */
+ sqlite3_int64 (*changes64)(sqlite3*);
+ sqlite3_int64 (*total_changes64)(sqlite3*);
+ /* Version 3.37.0 and later */
+ int (*autovacuum_pages)(sqlite3*,
+ unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
+ void*, void(*)(void*));
+ /* Version 3.38.0 and later */
+ int (*error_offset)(sqlite3*);
+ int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
+ int (*vtab_distinct)(sqlite3_index_info*);
+ int (*vtab_in)(sqlite3_index_info*,int,int);
+ int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
+ int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
+ /* Version 3.39.0 and later */
+ int (*deserialize)(sqlite3*,const char*,unsigned char*,
+ sqlite3_int64,sqlite3_int64,unsigned);
+ unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
+ unsigned int);
+ const char *(*db_name)(sqlite3*,int);
+ /* Version 3.40.0 and later */
+ int (*value_encoding)(sqlite3_value*);
+ /* Version 3.41.0 and later */
+ int (*is_interrupted)(sqlite3*);
+ /* Version 3.43.0 and later */
+ int (*stmt_explain)(sqlite3_stmt*,int);
+ /* Version 3.44.0 and later */
+ void *(*get_clientdata)(sqlite3*,const char*);
+ int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
};
/*
@@ -608,6 +655,50 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_create_window_function sqlite3_api->create_window_function
/* Version 3.26.0 and later */
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
+/* Version 3.28.0 and later */
+#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
+#define sqlite3_value_frombind sqlite3_api->value_frombind
+/* Version 3.30.0 and later */
+#define sqlite3_drop_modules sqlite3_api->drop_modules
+/* Version 3.31.0 and later */
+#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
+#define sqlite3_uri_key sqlite3_api->uri_key
+#define sqlite3_filename_database sqlite3_api->filename_database
+#define sqlite3_filename_journal sqlite3_api->filename_journal
+#define sqlite3_filename_wal sqlite3_api->filename_wal
+/* Version 3.32.0 and later */
+#define sqlite3_create_filename sqlite3_api->create_filename
+#define sqlite3_free_filename sqlite3_api->free_filename
+#define sqlite3_database_file_object sqlite3_api->database_file_object
+/* Version 3.34.0 and later */
+#define sqlite3_txn_state sqlite3_api->txn_state
+/* Version 3.36.1 and later */
+#define sqlite3_changes64 sqlite3_api->changes64
+#define sqlite3_total_changes64 sqlite3_api->total_changes64
+/* Version 3.37.0 and later */
+#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
+/* Version 3.38.0 and later */
+#define sqlite3_error_offset sqlite3_api->error_offset
+#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
+#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
+#define sqlite3_vtab_in sqlite3_api->vtab_in
+#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
+#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
+/* Version 3.39.0 and later */
+#ifndef SQLITE_OMIT_DESERIALIZE
+#define sqlite3_deserialize sqlite3_api->deserialize
+#define sqlite3_serialize sqlite3_api->serialize
+#endif
+#define sqlite3_db_name sqlite3_api->db_name
+/* Version 3.40.0 and later */
+#define sqlite3_value_encoding sqlite3_api->value_encoding
+/* Version 3.41.0 and later */
+#define sqlite3_is_interrupted sqlite3_api->is_interrupted
+/* Version 3.43.0 and later */
+#define sqlite3_stmt_explain sqlite3_api->stmt_explain
+/* Version 3.44.0 and later */
+#define sqlite3_get_clientdata sqlite3_api->get_clientdata
+#define sqlite3_set_clientdata sqlite3_api->set_clientdata
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
diff --git a/src/libs/3rdparty/sqlite/sqlite_static_config.h b/src/libs/3rdparty/sqlite/sqlite_static_config.h
new file mode 100644
index 0000000000..011fd25657
--- /dev/null
+++ b/src/libs/3rdparty/sqlite/sqlite_static_config.h
@@ -0,0 +1,305 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+// Make sure to avoid any other sqlite3 static libraries symbols present in the project
+#define sqlite3_aggregate_context qtc_sqlite3_aggregate_context
+#define sqlite3_auto_extension qtc_sqlite3_auto_extension
+#define sqlite3_autovacuum_pages qtc_sqlite3_autovacuum_pages
+#define sqlite3_backup_finish qtc_sqlite3_backup_finish
+#define sqlite3_backup_init qtc_sqlite3_backup_init
+#define sqlite3_backup_pagecount qtc_sqlite3_backup_pagecount
+#define sqlite3_backup_remaining qtc_sqlite3_backup_remaining
+#define sqlite3_backup_step qtc_sqlite3_backup_step
+#define sqlite3_bind_blob qtc_sqlite3_bind_blob
+#define sqlite3_bind_blob64 qtc_sqlite3_bind_blob64
+#define sqlite3_bind_double qtc_sqlite3_bind_double
+#define sqlite3_bind_int qtc_sqlite3_bind_int
+#define sqlite3_bind_int64 qtc_sqlite3_bind_int64
+#define sqlite3_bind_null qtc_sqlite3_bind_null
+#define sqlite3_bind_parameter_count qtc_sqlite3_bind_parameter_count
+#define sqlite3_bind_parameter_index qtc_sqlite3_bind_parameter_index
+#define sqlite3_bind_parameter_name qtc_sqlite3_bind_parameter_name
+#define sqlite3_bind_pointer qtc_sqlite3_bind_pointer
+#define sqlite3_bind_text qtc_sqlite3_bind_text
+#define sqlite3_bind_text64 qtc_sqlite3_bind_text64
+#define sqlite3_bind_value qtc_sqlite3_bind_value
+#define sqlite3_bind_zeroblob qtc_sqlite3_bind_zeroblob
+#define sqlite3_bind_zeroblob64 qtc_sqlite3_bind_zeroblob64
+#define sqlite3_blob_bytes qtc_sqlite3_blob_bytes
+#define sqlite3_blob_close qtc_sqlite3_blob_close
+#define sqlite3_blob_open qtc_sqlite3_blob_open
+#define sqlite3_blob_read qtc_sqlite3_blob_read
+#define sqlite3_blob_reopen qtc_sqlite3_blob_reopen
+#define sqlite3_blob_write qtc_sqlite3_blob_write
+#define sqlite3_busy_handler qtc_sqlite3_busy_handler
+#define sqlite3_busy_timeout qtc_sqlite3_busy_timeout
+#define sqlite3_cancel_auto_extension qtc_sqlite3_cancel_auto_extension
+#define sqlite3_changes qtc_sqlite3_changes
+#define sqlite3_changes64 qtc_sqlite3_changes64
+#define sqlite3_clear_bindings qtc_sqlite3_clear_bindings
+#define sqlite3_close qtc_sqlite3_close
+#define sqlite3_close_v2 qtc_sqlite3_close_v2
+#define sqlite3_collation_needed qtc_sqlite3_collation_needed
+#define sqlite3_column_blob qtc_sqlite3_column_blob
+#define sqlite3_column_bytes qtc_sqlite3_column_bytes
+#define sqlite3_column_bytes16 qtc_sqlite3_column_bytes16
+#define sqlite3_column_count qtc_sqlite3_column_count
+#define sqlite3_column_double qtc_sqlite3_column_double
+#define sqlite3_column_int qtc_sqlite3_column_int
+#define sqlite3_column_int64 qtc_sqlite3_column_int64
+#define sqlite3_column_name qtc_sqlite3_column_name
+#define sqlite3_column_text qtc_sqlite3_column_text
+#define sqlite3_column_type qtc_sqlite3_column_type
+#define sqlite3_column_value qtc_sqlite3_column_value
+#define sqlite3_commit_hook qtc_sqlite3_commit_hook
+#define sqlite3_compileoption_get qtc_sqlite3_compileoption_get
+#define sqlite3_compileoption_used qtc_sqlite3_compileoption_used
+#define sqlite3_config qtc_sqlite3_config
+#define sqlite3_context_db_handle qtc_sqlite3_context_db_handle
+#define sqlite3_create_collation qtc_sqlite3_create_collation
+#define sqlite3_create_collation_v2 qtc_sqlite3_create_collation_v2
+#define sqlite3_create_filename qtc_sqlite3_create_filename
+#define sqlite3_create_function qtc_sqlite3_create_function
+#define sqlite3_create_function_v2 qtc_sqlite3_create_function_v2
+#define sqlite3_create_module qtc_sqlite3_create_module
+#define sqlite3_create_module_v2 qtc_sqlite3_create_module_v2
+#define sqlite3_create_window_function qtc_sqlite3_create_window_function
+#define sqlite3_data_count qtc_sqlite3_data_count
+#define sqlite3_database_file_object qtc_sqlite3_database_file_object
+#define sqlite3_db_cacheflush qtc_sqlite3_db_cacheflush
+#define sqlite3_db_config qtc_sqlite3_db_config
+#define sqlite3_db_filename qtc_sqlite3_db_filename
+#define sqlite3_db_handle qtc_sqlite3_db_handle
+#define sqlite3_db_mutex qtc_sqlite3_db_mutex
+#define sqlite3_db_readonly qtc_sqlite3_db_readonly
+#define sqlite3_db_release_memory qtc_sqlite3_db_release_memory
+#define sqlite3_db_status qtc_sqlite3_db_status
+#define sqlite3_declare_vtab qtc_sqlite3_declare_vtab
+#define sqlite3_deserialize qtc_sqlite3_deserialize
+#define sqlite3_drop_modules qtc_sqlite3_drop_modules
+#define sqlite3_errcode qtc_sqlite3_errcode
+#define sqlite3_errmsg qtc_sqlite3_errmsg
+#define sqlite3_errstr qtc_sqlite3_errstr
+#define sqlite3_exec qtc_sqlite3_exec
+#define sqlite3_expanded_sql qtc_sqlite3_expanded_sql
+#define sqlite3_extended_errcode qtc_sqlite3_extended_errcode
+#define sqlite3_extended_result_codes qtc_sqlite3_extended_result_codes
+#define sqlite3_file_control qtc_sqlite3_file_control
+#define sqlite3_filename_database qtc_sqlite3_filename_database
+#define sqlite3_filename_journal qtc_sqlite3_filename_journal
+#define sqlite3_filename_wal qtc_sqlite3_filename_wal
+#define sqlite3_finalize qtc_sqlite3_finalize
+#define sqlite3_free qtc_sqlite3_free
+#define sqlite3_free_filename qtc_sqlite3_free_filename
+#define sqlite3_free_table qtc_sqlite3_free_table
+#define sqlite3_get_autocommit qtc_sqlite3_get_autocommit
+#define sqlite3_get_auxdata qtc_sqlite3_get_auxdata
+#define sqlite3_get_table qtc_sqlite3_get_table
+#define sqlite3_hard_heap_limit64 qtc_sqlite3_hard_heap_limit64
+#define sqlite3_initialize qtc_sqlite3_initialize
+#define sqlite3_interrupt qtc_sqlite3_interrupt
+#define sqlite3_keyword_check qtc_sqlite3_keyword_check
+#define sqlite3_keyword_count qtc_sqlite3_keyword_count
+#define sqlite3_keyword_name qtc_sqlite3_keyword_name
+#define sqlite3_last_insert_rowid qtc_sqlite3_last_insert_rowid
+#define sqlite3_libversion qtc_sqlite3_libversion
+#define sqlite3_libversion_number qtc_sqlite3_libversion_number
+#define sqlite3_limit qtc_sqlite3_limit
+#define sqlite3_log qtc_sqlite3_log
+#define sqlite3_malloc qtc_sqlite3_malloc
+#define sqlite3_malloc64 qtc_sqlite3_malloc64
+#define sqlite3_memory_highwater qtc_sqlite3_memory_highwater
+#define sqlite3_memory_used qtc_sqlite3_memory_used
+#define sqlite3_mprintf qtc_sqlite3_mprintf
+#define sqlite3_msize qtc_sqlite3_msize
+#define sqlite3_mutex_alloc qtc_sqlite3_mutex_alloc
+#define sqlite3_mutex_enter qtc_sqlite3_mutex_enter
+#define sqlite3_mutex_free qtc_sqlite3_mutex_free
+#define sqlite3_mutex_leave qtc_sqlite3_mutex_leave
+#define sqlite3_mutex_try qtc_sqlite3_mutex_try
+#define sqlite3_next_stmt qtc_sqlite3_next_stmt
+#define sqlite3_open qtc_sqlite3_open
+#define sqlite3_open_v2 qtc_sqlite3_open_v2
+#define sqlite3_os_end qtc_sqlite3_os_end
+#define sqlite3_os_init qtc_sqlite3_os_init
+#define sqlite3_overload_function qtc_sqlite3_overload_function
+#define sqlite3_prepare qtc_sqlite3_prepare
+#define sqlite3_prepare_v2 qtc_sqlite3_prepare_v2
+#define sqlite3_prepare_v3 qtc_sqlite3_prepare_v3
+#define sqlite3_progress_handler qtc_sqlite3_progress_handler
+#define sqlite3_randomness qtc_sqlite3_randomness
+#define sqlite3_realloc qtc_sqlite3_realloc
+#define sqlite3_realloc64 qtc_sqlite3_realloc64
+#define sqlite3_release_memory qtc_sqlite3_release_memory
+#define sqlite3_reset qtc_sqlite3_reset
+#define sqlite3_reset_auto_extension qtc_sqlite3_reset_auto_extension
+#define sqlite3_result_blob qtc_sqlite3_result_blob
+#define sqlite3_result_blob64 qtc_sqlite3_result_blob64
+#define sqlite3_result_double qtc_sqlite3_result_double
+#define sqlite3_result_error qtc_sqlite3_result_error
+#define sqlite3_result_error_code qtc_sqlite3_result_error_code
+#define sqlite3_result_error_nomem qtc_sqlite3_result_error_nomem
+#define sqlite3_result_error_toobig qtc_sqlite3_result_error_toobig
+#define sqlite3_result_int qtc_sqlite3_result_int
+#define sqlite3_result_int64 qtc_sqlite3_result_int64
+#define sqlite3_result_null qtc_sqlite3_result_null
+#define sqlite3_result_pointer qtc_sqlite3_result_pointer
+#define sqlite3_result_subtype qtc_sqlite3_result_subtype
+#define sqlite3_result_text qtc_sqlite3_result_text
+#define sqlite3_result_text64 qtc_sqlite3_result_text64
+#define sqlite3_result_value qtc_sqlite3_result_value
+#define sqlite3_result_zeroblob qtc_sqlite3_result_zeroblob
+#define sqlite3_result_zeroblob64 qtc_sqlite3_result_zeroblob64
+#define sqlite3_rollback_hook qtc_sqlite3_rollback_hook
+#define sqlite3_serialize qtc_sqlite3_serialize
+#define sqlite3_set_authorizer qtc_sqlite3_set_authorizer
+#define sqlite3_set_auxdata qtc_sqlite3_set_auxdata
+#define sqlite3_set_last_insert_rowid qtc_sqlite3_set_last_insert_rowid
+#define sqlite3_shutdown qtc_sqlite3_shutdown
+#define sqlite3_sleep qtc_sqlite3_sleep
+#define sqlite3_snprintf qtc_sqlite3_snprintf
+#define sqlite3_soft_heap_limit qtc_sqlite3_soft_heap_limit
+#define sqlite3_soft_heap_limit64 qtc_sqlite3_soft_heap_limit64
+#define sqlite3_sourceid qtc_sqlite3_sourceid
+#define sqlite3_sql qtc_sqlite3_sql
+#define sqlite3_status qtc_sqlite3_status
+#define sqlite3_status64 qtc_sqlite3_status64
+#define sqlite3_step qtc_sqlite3_step
+#define sqlite3_stmt_busy qtc_sqlite3_stmt_busy
+#define sqlite3_stmt_isexplain qtc_sqlite3_stmt_isexplain
+#define sqlite3_stmt_readonly qtc_sqlite3_stmt_readonly
+#define sqlite3_stmt_status qtc_sqlite3_stmt_status
+#define sqlite3_str_append qtc_sqlite3_str_append
+#define sqlite3_str_appendall qtc_sqlite3_str_appendall
+#define sqlite3_str_appendchar qtc_sqlite3_str_appendchar
+#define sqlite3_str_appendf qtc_sqlite3_str_appendf
+#define sqlite3_str_errcode qtc_sqlite3_str_errcode
+#define sqlite3_str_finish qtc_sqlite3_str_finish
+#define sqlite3_str_length qtc_sqlite3_str_length
+#define sqlite3_str_new qtc_sqlite3_str_new
+#define sqlite3_str_reset qtc_sqlite3_str_reset
+#define sqlite3_str_value qtc_sqlite3_str_value
+#define sqlite3_str_vappendf qtc_sqlite3_str_vappendf
+#define sqlite3_strglob qtc_sqlite3_strglob
+#define sqlite3_stricmp qtc_sqlite3_stricmp
+#define sqlite3_strlike qtc_sqlite3_strlike
+#define sqlite3_strnicmp qtc_sqlite3_strnicmp
+#define sqlite3_system_errno qtc_sqlite3_system_errno
+#define sqlite3_table_column_metadata qtc_sqlite3_table_column_metadata
+#define sqlite3_test_control qtc_sqlite3_test_control
+#define sqlite3_threadsafe qtc_sqlite3_threadsafe
+#define sqlite3_total_changes qtc_sqlite3_total_changes
+#define sqlite3_total_changes64 qtc_sqlite3_total_changes64
+#define sqlite3_txn_state qtc_sqlite3_txn_state
+#define sqlite3_unlock_notify qtc_sqlite3_unlock_notify
+#define sqlite3_update_hook qtc_sqlite3_update_hook
+#define sqlite3_uri_boolean qtc_sqlite3_uri_boolean
+#define sqlite3_uri_int64 qtc_sqlite3_uri_int64
+#define sqlite3_uri_key qtc_sqlite3_uri_key
+#define sqlite3_uri_parameter qtc_sqlite3_uri_parameter
+#define sqlite3_user_data qtc_sqlite3_user_data
+#define sqlite3_value_blob qtc_sqlite3_value_blob
+#define sqlite3_value_bytes qtc_sqlite3_value_bytes
+#define sqlite3_value_bytes16 qtc_sqlite3_value_bytes16
+#define sqlite3_value_double qtc_sqlite3_value_double
+#define sqlite3_value_dup qtc_sqlite3_value_dup
+#define sqlite3_value_free qtc_sqlite3_value_free
+#define sqlite3_value_frombind qtc_sqlite3_value_frombind
+#define sqlite3_value_int qtc_sqlite3_value_int
+#define sqlite3_value_int64 qtc_sqlite3_value_int64
+#define sqlite3_value_nochange qtc_sqlite3_value_nochange
+#define sqlite3_value_numeric_type qtc_sqlite3_value_numeric_type
+#define sqlite3_value_pointer qtc_sqlite3_value_pointer
+#define sqlite3_value_subtype qtc_sqlite3_value_subtype
+#define sqlite3_value_text qtc_sqlite3_value_text
+#define sqlite3_value_type qtc_sqlite3_value_type
+#define sqlite3_vfs_find qtc_sqlite3_vfs_find
+#define sqlite3_vfs_register qtc_sqlite3_vfs_register
+#define sqlite3_vfs_unregister qtc_sqlite3_vfs_unregister
+#define sqlite3_vmprintf qtc_sqlite3_vmprintf
+#define sqlite3_vsnprintf qtc_sqlite3_vsnprintf
+#define sqlite3_vtab_collation qtc_sqlite3_vtab_collation
+#define sqlite3_vtab_config qtc_sqlite3_vtab_config
+#define sqlite3_vtab_nochange qtc_sqlite3_vtab_nochange
+#define sqlite3_vtab_on_conflict qtc_sqlite3_vtab_on_conflict
+#define sqlite3_wal_autocheckpoint qtc_sqlite3_wal_autocheckpoint
+#define sqlite3_wal_checkpoint qtc_sqlite3_wal_checkpoint
+#define sqlite3_wal_checkpoint_v2 qtc_sqlite3_wal_checkpoint_v2
+#define sqlite3_wal_hook qtc_sqlite3_wal_hook
+#define sqlite3_win32_is_nt qtc_sqlite3_win32_is_nt
+#define sqlite3_win32_mbcs_to_utf8 qtc_sqlite3_win32_mbcs_to_utf8
+#define sqlite3_win32_mbcs_to_utf8_v2 qtc_sqlite3_win32_mbcs_to_utf8_v2
+#define sqlite3_win32_set_directory qtc_sqlite3_win32_set_directory
+#define sqlite3_win32_set_directory16 qtc_sqlite3_win32_set_directory16
+#define sqlite3_win32_set_directory8 qtc_sqlite3_win32_set_directory8
+#define sqlite3_win32_sleep qtc_sqlite3_win32_sleep
+#define sqlite3_win32_unicode_to_utf8 qtc_sqlite3_win32_unicode_to_utf8
+#define sqlite3_win32_utf8_to_mbcs qtc_sqlite3_win32_utf8_to_mbcs
+#define sqlite3_win32_utf8_to_mbcs_v2 qtc_sqlite3_win32_utf8_to_mbcs_v2
+#define sqlite3_win32_utf8_to_unicode qtc_sqlite3_win32_utf8_to_unicode
+#define sqlite3_win32_write_debug qtc_sqlite3_win32_write_debug
+#define sqlite3_version qtc_sqlite3_version
+#define sqlite3_temp_directory qtc_sqlite3_temp_directory
+#define sqlite3_data_directory qtc_sqlite3_data_directory
+#define sqlite3changeset_finalize qtc_sqlite3changeset_finalize
+#define sqlite3changeset_op qtc_sqlite3changeset_op
+
+// Redefine structures in order to avoid ODR violations
+#define sqlite3 qtc_sqlite3
+#define sqlite3_file qtc_sqlite3_file
+#define sqlite3_io_methods qtc_sqlite3_io_methods
+#define sqlite3_mutex qtc_sqlite3_mutex
+#define sqlite3_api_routines qtc_sqlite3_api_routines
+#define sqlite3_vfs qtc_sqlite3_vfs
+#define sqlite3_mem_methods qtc_sqlite3_mem_methods
+#define sqlite3_stmt qtc_sqlite3_stmt
+#define sqlite3_value qtc_sqlite3_value
+#define sqlite3_context qtc_sqlite3_context
+#define sqlite3_vtab qtc_sqlite3_vtab
+#define sqlite3_index_info qtc_sqlite3_index_info
+#define sqlite3_vtab_cursor qtc_sqlite3_vtab_cursor
+#define sqlite3_module qtc_sqlite3_module
+#define sqlite3_mutex_methods qtc_sqlite3_mutex_methods
+#define sqlite3_str qtc_sqlite3_str
+#define sqlite3_pcache qtc_sqlite3_pcache
+#define sqlite3_pcache_page qtc_sqlite3_pcache_page
+#define sqlite3_pcache_methods2 qtc_sqlite3_pcache_methods2
+#define sqlite3_pcache_methods qtc_sqlite3_pcache_methods
+#define sqlite3_backup qtc_sqlite3_backup
+#define sqlite3_snapshot qtc_sqlite3_snapshot
+#define sqlite3_rtree_geometry qtc_sqlite3_rtree_geometry
+#define sqlite3_rtree_query_info qtc_sqlite3_rtree_query_info
+#define sqlite3_session qtc_sqlite3_session
+#define sqlite3_changeset_iter qtc_sqlite3_changeset_iter
+#define sqlite3_changegroup qtc_sqlite3_changegroup
+#define sqlite3_rebaser qtc_sqlite3_rebaser
+#define Fts5ExtensionApi qtc_Fts5ExtensionApi
+#define Fts5Context qtc_Fts5Context
+#define Fts5PhraseIter qtc_Fts5PhraseIter
+#define Fts5Tokenizer qtc_Fts5Tokenizer
+#define fts5_tokenizer qtc_fts5_tokenizer
+#define fts5_api qtc_fts5_api
diff --git a/src/libs/3rdparty/syntax-highlighting/.git-blame-ignore-revs b/src/libs/3rdparty/syntax-highlighting/.git-blame-ignore-revs
new file mode 100644
index 0000000000..eba140cf12
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/.git-blame-ignore-revs
@@ -0,0 +1,5 @@
+# clang-format
+56ed6f3f5f505eb0dbffc630729d67c3fb510546
+ba947348217c468a0cea117289f3a3fc43bb66e6
+#clang-tidy
+0960472cc3f57831a97697a4ae0cd139e2cc5551
diff --git a/src/libs/3rdparty/syntax-highlighting/.gitignore b/src/libs/3rdparty/syntax-highlighting/.gitignore
index 52f10e28a5..a29428fe5e 100644
--- a/src/libs/3rdparty/syntax-highlighting/.gitignore
+++ b/src/libs/3rdparty/syntax-highlighting/.gitignore
@@ -9,4 +9,11 @@ callgrind.*
heaptrack.*
/build*/
*.unc-backup*
-
+/.clang-format
+/.cmake/
+/*.code-workspace
+/compile_commands.json
+.clangd
+.idea
+/cmake-build*
+.cache
diff --git a/src/libs/3rdparty/syntax-highlighting/.gitlab-ci.yml b/src/libs/3rdparty/syntax-highlighting/.gitlab-ci.yml
new file mode 100644
index 0000000000..a5df0fb27a
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/.gitlab-ci.yml
@@ -0,0 +1,10 @@
+# SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
+# SPDX-License-Identifier: CC0-1.0
+
+include:
+ - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
+ - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml
+ - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml
+ - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml
+ - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
+ - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml
diff --git a/src/libs/3rdparty/syntax-highlighting/.kde-ci.yml b/src/libs/3rdparty/syntax-highlighting/.kde-ci.yml
new file mode 100644
index 0000000000..51e4f4e72f
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/.kde-ci.yml
@@ -0,0 +1,12 @@
+Dependencies:
+- 'on': ['@all']
+ 'require':
+ 'frameworks/extra-cmake-modules': '@same'
+
+Options:
+ test-before-installing: True
+ require-passing-tests-on: [ 'Linux', 'FreeBSD', 'Windows' ]
+
+ # exclude stuff from cppcheck that takes too long
+ cppcheck-ignore-files:
+ - 'src/lib/abstracthighlighter.cpp'
diff --git a/src/libs/3rdparty/syntax-highlighting/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/CMakeLists.txt
index 27cc408223..ce22946d84 100644
--- a/src/libs/3rdparty/syntax-highlighting/CMakeLists.txt
+++ b/src/libs/3rdparty/syntax-highlighting/CMakeLists.txt
@@ -1,10 +1,21 @@
-add_qtc_library(KSyntaxHighlighting STATIC
- PUBLIC_INCLUDES autogenerated/ autogenerated/src/lib src/lib
- PUBLIC_DEFINES KSYNTAXHIGHLIGHTING_LIBRARY
- DEPENDS Qt5::Network Qt5::Gui
+if(TARGET KF5::SyntaxHighlighting)
+ set(HIGHLIGHTING_BUILD_DEFAULT OFF)
+else()
+ set(HIGHLIGHTING_BUILD_DEFAULT ON)
+endif()
+
+add_qtc_library(KSyntaxHighlighting
+ BUILD_DEFAULT ${HIGHLIGHTING_BUILD_DEFAULT}
+ INCLUDES autogenerated/
+ PUBLIC_INCLUDES
+ src/lib
+ autogenerated/include
+ autogenerated/src/lib
+ DEPENDS Qt::Network Qt::Widgets
SOURCES
autogenerated/src/lib/ksyntaxhighlighting_logging.cpp autogenerated/src/lib/ksyntaxhighlighting_logging.h
autogenerated/ksyntaxhighlighting_version.h
+ autogenerated/src/lib/ksyntaxhighlighting_export.h
data/themes/theme-data.qrc
@@ -15,11 +26,12 @@ add_qtc_library(KSyntaxHighlighting STATIC
src/lib/definitiondownloader.cpp src/lib/definitiondownloader.h
src/lib/definitionref_p.h
src/lib/definition_p.h
+ src/lib/dynamicregexpcache_p.h
src/lib/foldingregion.cpp src/lib/foldingregion.h
src/lib/format.cpp src/lib/format.h src/lib/format_p.h
src/lib/htmlhighlighter.cpp src/lib/htmlhighlighter.h
+ src/lib/highlightingdata.cpp src/lib/highlightingdata_p.hpp
src/lib/keywordlist.cpp src/lib/keywordlist_p.h
- src/lib/ksyntaxhighlighting_export.h
src/lib/matchresult_p.h
src/lib/repository.cpp src/lib/repository.h src/lib/repository_p.h
src/lib/rule.cpp src/lib/rule_p.h
@@ -28,20 +40,28 @@ add_qtc_library(KSyntaxHighlighting STATIC
src/lib/textstyledata_p.h
src/lib/theme.cpp src/lib/theme.h
src/lib/themedata.cpp src/lib/themedata_p.h
- src/lib/wildcardmatcher.cpp src/lib/wildcardmatcher_p.h
+ src/lib/wildcardmatcher.cpp
+ src/lib/worddelimiters.cpp src/lib/worddelimiters_p.h
src/lib/xml_p.h
)
-install(
- DIRECTORY data/syntax
- DESTINATION "${IDE_DATA_PATH}/generic-highlighter/"
-)
+set(export_symbol_declaration DEFINES KF6SyntaxHighlighting_EXPORTS)
+if (QTC_STATIC_BUILD)
+ set(export_symbol_declaration PUBLIC_DEFINES KSYNTAXHIGHLIGHTING_STATIC_DEFINE)
+endif()
+extend_qtc_library(KSyntaxHighlighting ${export_symbol_declaration})
-add_custom_target(copy_generic_highligher_to_builddir ALL VERBATIM)
-add_custom_command(TARGET copy_generic_highligher_to_builddir POST_BUILD
- COMMAND "${CMAKE_COMMAND}" -E copy_directory data/syntax
- "${PROJECT_BINARY_DIR}/${IDE_DATA_PATH}/generic-highlighter/syntax"
- WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
- COMMENT Copy files into build directory
- VERBATIM
-)
+qtc_add_public_header(autogenerated/include/KSyntaxHighlighting/State)
+
+if(TARGET KSyntaxHighlighting)
+ install(
+ DIRECTORY data/syntax
+ DESTINATION "${IDE_DATA_PATH}/generic-highlighter/"
+ )
+
+ # copy resource directories during build
+ qtc_copy_to_builddir(copy_generic_highligher_to_builddir
+ DIRECTORIES data/syntax
+ DESTINATION "${IDE_DATA_PATH}/generic-highlighter/syntax"
+ )
+endif()
diff --git a/src/libs/3rdparty/syntax-highlighting/CMakeLists.txt.kde b/src/libs/3rdparty/syntax-highlighting/CMakeLists.txt.kde
index 4f88fcf84a..cf21b3ac73 100644
--- a/src/libs/3rdparty/syntax-highlighting/CMakeLists.txt.kde
+++ b/src/libs/3rdparty/syntax-highlighting/CMakeLists.txt.kde
@@ -1,14 +1,19 @@
cmake_minimum_required(VERSION 3.5)
-set(KF5_VERSION "5.59.0")
+set(KF5_VERSION "5.73.0")
project(KSyntaxHighlighting VERSION ${KF5_VERSION})
-find_package(ECM 5.59.0 REQUIRED NO_MODULE)
-set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
+find_package(ECM 5.73.0 REQUIRED NO_MODULE)
+set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
if(POLICY CMP0063)
cmake_policy(SET CMP0063 NEW)
endif()
+include(KDEInstallDirs)
+include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
+include(KDECMakeSettings)
+include(KDEClangFormat)
+
include(FeatureSummary)
include(GenerateExportHeader)
include(ECMSetupVersion)
@@ -17,9 +22,6 @@ include(ECMGeneratePriFile)
include(CMakePackageConfigHelpers)
include(ECMPoQmTools)
include(ECMQtDeclareLoggingCategory)
-include(KDEInstallDirs)
-include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
-include(KDECMakeSettings)
include(ECMMarkNonGuiExecutable)
include(ECMAddQch)
include(ECMOptionalAddSubdirectory)
@@ -34,7 +36,7 @@ ecm_setup_version(PROJECT
#
# Dependencies
#
-set(REQUIRED_QT_VERSION 5.10.0)
+set(REQUIRED_QT_VERSION 5.12.0)
find_package(Qt5 ${REQUIRED_QT_VERSION} NO_MODULE REQUIRED COMPONENTS Core Network Test)
option(KSYNTAXHIGHLIGHTING_USE_GUI "Build components depending on Qt5Gui" ON)
if(KSYNTAXHIGHLIGHTING_USE_GUI)
@@ -84,6 +86,7 @@ if (NO_STANDARD_PATHS)
add_definitions(-DNO_STANDARD_PATHS)
endif()
add_definitions(-DQT_NO_FOREACH)
+add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050d00)
#
# Actually build the stuff
@@ -135,6 +138,10 @@ endif()
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ksyntaxhighlighting_version.h"
DESTINATION "${KDE_INSTALL_INCLUDEDIR_KF5}"
COMPONENT Devel)
-install(FILES org_kde_ksyntaxhighlighting.categories DESTINATION ${KDE_INSTALL_CONFDIR})
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
+
+# add clang-format target for all our real source files
+file(GLOB ALL_CLANG_FORMAT_TEST_FILES autotests/*.cpp autotests/*.h)
+file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES examples/*.cpp examples/*.h src/*.cpp src/*.h)
+kde_clang_format(${ALL_CLANG_FORMAT_TEST_FILES} ${ALL_CLANG_FORMAT_SOURCE_FILES})
diff --git a/src/libs/3rdparty/syntax-highlighting/KF5SyntaxHighlightingConfig.cmake.in b/src/libs/3rdparty/syntax-highlighting/KF5SyntaxHighlightingConfig.cmake.in
index 0fd1778fb8..b576ae1b48 100644
--- a/src/libs/3rdparty/syntax-highlighting/KF5SyntaxHighlightingConfig.cmake.in
+++ b/src/libs/3rdparty/syntax-highlighting/KF5SyntaxHighlightingConfig.cmake.in
@@ -1,6 +1,8 @@
@PACKAGE_INIT@
-find_package(Qt5 @Qt5Core_VERSION_MAJOR@.@Qt5Core_VERSION_MINOR@ NO_MODULE REQUIRED COMPONENTS Core Gui)
+include(CMakeFindDependencyMacro)
+find_dependency(Qt@QT_MAJOR_VERSION@Core @REQUIRED_QT_VERSION@)
+find_dependency(Qt@QT_MAJOR_VERSION@Gui @REQUIRED_QT_VERSION@)
include("${CMAKE_CURRENT_LIST_DIR}/KF5SyntaxHighlightingTargets.cmake")
@PACKAGE_INCLUDE_QCHTARGETS@
diff --git a/src/libs/3rdparty/syntax-highlighting/QC_README.txt b/src/libs/3rdparty/syntax-highlighting/QC_README.txt
deleted file mode 100644
index e69de29bb2..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/QC_README.txt
+++ /dev/null
diff --git a/src/libs/3rdparty/syntax-highlighting/README.md b/src/libs/3rdparty/syntax-highlighting/README.md
index c1b82c63d1..2e668dd3da 100644
--- a/src/libs/3rdparty/syntax-highlighting/README.md
+++ b/src/libs/3rdparty/syntax-highlighting/README.md
@@ -2,6 +2,20 @@
Syntax highlighting engine for Kate syntax definitions
+## Table of contents
+
+1. [Introduction](#introduction)
+2. [Out of scope](#out-of-scope)
+3. [Syntax definition files](#syntax-definition-files)
+4. [Color theme files](#color-theme-files)
+5. [Build it](#build-it)
+6. [How to contribute](#how-to-contribute)
+ * [Licensing](#licensing)
+ * [Tips for contributing to syntax definition files](#tips-for-contributing-to-syntax-definition-files)
+ * [Adding unit tests for a syntax definition](#adding-unit-tests-for-a-syntax-definition)
+7. [Report bug or help to fix them](#report-bug-or-help-to-fix-them)
+8. [Updating the syntax & themes pages of the kate-editor.org website](#updating-the-kate-editororgsyntax-website)
+
## Introduction
This is a stand-alone implementation of the Kate syntax highlighting engine.
@@ -9,14 +23,7 @@ It's meant as a building block for text editors as well as for simple highlighte
text rendering (e.g. as HTML), supporting both integration with a custom editor
as well as a ready-to-use QSyntaxHighlighter sub-class.
-## Syntax Definition Files
-
-This library uses Kate syntax definition files for the actual highlighting,
-the file format is documented [here](https://docs.kde.org/stable5/en/applications/katepart/highlight.html).
-
-More than 250 syntax definition files are included, additional ones are
-picked up from the file system if present, so you can easily extend this
-by application-specific syntax definitions for example.
+Besides a C++ API, a [QML API](@ref qml_api) is also provided.
## Out of scope
@@ -31,19 +38,214 @@ out of scope:
If you need any of this, check out [KTextEditor](https://api.kde.org/frameworks/ktexteditor/html/).
-## Adding unit tests for a syntax definition
+## Syntax definition files
+
+This library uses Kate syntax definition files for the actual highlighting,
+the file format is documented [here](https://docs.kde.org/?application=katepart&branch=trunk5&path=highlight.html).
+
+More than 300 syntax definition files are included, that are located
+in **data/syntax/** and have the **.xml** extension. Additional ones are
+picked up from the file system if present, so you can easily extend this
+by application-specific syntax definitions for example.
+
+To install or test a syntax definition file locally, place it in
+**org.kde.syntax-highlighting/syntax/**, which is located in your user directory.
+Usually it is:
+
+<table>
+ <tr>
+ <td>For local user</td>
+ <td>$HOME/.local/share/org.kde.syntax-highlighting/syntax/</td>
+ </tr>
+ <tr>
+ <td>For Flatpak packages</td>
+ <td>$HOME/.var/app/<em>package-name</em>/data/org.kde.syntax-highlighting/syntax/</td>
+ </tr>
+ <tr>
+ <td>For Snap packages</a></td>
+ <td>$HOME/snap/<em>package-name</em>/current/.local/share/org.kde.syntax-highlighting/syntax/</td>
+ </tr>
+ <tr>
+ <td>On Windows®</td>
+ <td>&#37;USERPROFILE&#37;&#92;AppData&#92;Local&#92;org.kde.syntax-highlighting&#92;syntax&#92;</td>
+ </tr>
+ <tr>
+ <td>On macOS®</td>
+ <td>$HOME/Library/Application Support/org.kde.syntax-highlighting/syntax/</td>
+ </tr>
+</table>
+
+For more details, see ["The Highlight Definition XML Format" (Working with Syntax Highlighting, KDE Documentation)](https://docs.kde.org/?application=katepart&branch=trunk5&path=highlight.html#katehighlight-xml-format).
+
+Also, in **data/schema/** there is a script to validate the syntax definition XML
+files. Use the command `validatehl.sh mySyntax.xml`.
+
+## Color theme files
+
+This library includes the color themes, which are documented
+[here](https://docs.kde.org/?application=katepart&branch=trunk5&path=color-themes.html).
+
+The color theme files use the JSON format and are located in **data/themes/**
+with the **.theme** extension.
+Additional ones are also picked up from the file system if present,
+in the **org.kde.syntax-highlighting/themes/** folder of your user directory,
+allowing you to easily add custom color theme files. This location is the same
+as shown in the table of the [previous section](#syntax-definition-files),
+replacing the **syntax** folder with **themes**.
+For more details, see ["The Color Themes JSON Format" (Working with Color Themes, KDE Documentation)](https://docs.kde.org/?application=katepart&branch=trunk5&path=color-themes.html#color-themes-json).
+
+The [KTextEditor](https://api.kde.org/frameworks/ktexteditor/html/) library
+(used by Kate, Kile and KDevelop, for example) provides a
+[user interface](https://docs.kde.org/?application=katepart&branch=trunk5&path=color-themes.html#color-themes-gui)
+for editing and creating KSyntaxHighlighting color themes, including
+a tool for exporting and importing the JSON theme files.
+
+Note that in KDE text editors, the KSyntaxHighlighting color themes are used
+[since KDE Frameworks 5.75](https://kate-editor.org/post/2020/2020-09-13-kate-color-themes-5.75/),
+released on October 10, 2020. Previously, Kate's color schemes
+(KConfig based schema config) were used and are now deprecated.
+The tool **utils/schema-converter/** and the script **utils/kateschema_to_theme_converter.py**
+convert the old Kate schemas to KSyntaxHighlighting themes.
+
+Also see ["Submit a KSyntaxHighlighting Color Theme" (Kate Editor Website)](https://kate-editor.org/post/2020/2020-09-18-submit-a-ksyntaxhighlighting-color-theme/).
+
+## Build it
+
+1. Create and change into a build directory. Usually, a folder called **build**
+ is created inside the **syntax-highlighting** source directory.
+
+ ```bash
+ mkdir <build-directory>
+ cd <build-directory>
+ ```
+
+2. Run the configure process with *cmake* and compile:
+
+ ```bash
+ cmake <source-directory>
+ make
+ ```
+
+ For example:
+
+ ```bash
+ git clone git@invent.kde.org:frameworks/syntax-highlighting.git
+ mkdir ./syntax-highlighting/build
+ cd ./syntax-highlighting/build
+ cmake ../
+ make
+ ```
+
+ For more details see ["Building Kate from Sources on Linux" (Kate Editor Website)](https://kate-editor.org/build-it/).
+
+ **NOTE:** If running *cmake* shows an error related to your version of KDE
+ Frameworks, you edit the **CMakeLists.txt** file in the line
+ `find_package(ECM 5.XX.X ...)`.
+
+3. To run tests:
+
+ ```bash
+ make test
+ ```
+
+ The tests are located in the **autotests** directory.
+ This command can be used to check changes to units test after modifying some
+ syntax definition file. To add a unit test or update the references, see the
+ section ["Adding unit tests for a syntax definition"](#adding-unit-tests-for-a-syntax-definition).
+
+## How to contribute
+
+KDE uses a GitLab instance at **invent.kde.org** for code review. The official
+repository of the KSyntaxHighlighting framework is [here](https://invent.kde.org/frameworks/syntax-highlighting).
+
+All the necessary information to send contributions is [here](https://community.kde.org/Infrastructure/GitLab).
+
+### Licensing
+
+Contributions to KSyntaxHighlighting shall be licensed under [MIT](LICENSES/MIT.txt).
+
+All files shall contain a proper "SPDX-License-Identifier: MIT" identifier inside a header like:
+
+```cpp
+/*
+ SPDX-FileCopyrightText: 2020 Christoph Cullmann <cullmann@kde.org>
+
+ SPDX-License-Identifier: MIT
+*/
+```
+
+### Tips for contributing to syntax definition files
+
+* If you are modifying an existing syntax definition XML file, you must increase
+ the version number of the language.
+
+* Do not use hard-coded colors, as they may not look good or be illegible in some color
+ themes. Prefer to use the default color styles.
+
+ For more information, see:
+
+ * [Available Default Styles (Working with Syntax Highlighting, KDE Documentation)](https://docs.kde.org/?application=katepart&branch=trunk5&path=highlight.html#kate-highlight-default-styles)
+ * [Kate Part (KF5): New Default Styles for better Color Schemes (Kate Editor Website)](https://kate-editor.org/2014/03/07/kate-part-kf5-new-default-styles-for-better-color-schemes/)
+
+* Add test files, these are found in **autotests/input/**.
+ If you are going to add a new syntax XML file, create a new test file; if you
+ are going to modify a XML file, adds examples to existing test files.
+
+ Then, it is necessary to generate and update the files in **autotests/folding/**,
+ **autotests/html/** and **autotests/reference/**, which must be included in the
+ patches. Instructions are [below](#adding-unit-tests-for-a-syntax-definition).
+
+### Adding unit tests for a syntax definition
+
+1. Add an input file into the **autotests/input/** folder, lets call it
+ **test.&lt;language-extension&gt;**.
+
+2. If the file extension is not sufficient to trigger the right syntax definition, you can add an
+ second file **testname.&lt;language-extension&gt;.syntax** that contains the syntax definition name
+ to enforce the use of the right extension.
+
+3. Do `make && make test`.
+
+ Note that after adding or modifying something in
+ **&lt;source-directory&gt;/autotests/input/**, an error will be showed when
+ running `make test`, because the references in the source directory do not
+ match the ones now generated.
+
+4. Inspect the outputs found in your binary directory **autotests/folding.out/**,
+ **autotests/html.output/** and **autotests/output/**.
+
+5. If OK, run in the binary folder `./autotests/update-reference-data.sh`
+ to copy the results to the right location.
+ That script updates the references in the source directory in
+ **autotest/folding/**, **autotest/html/** and **autotest/reference/**.
+
+6. Add the result references after the copying to the git.
+
+## Report bug or help to fix them
+
+KDE uses Bugzilla to management of bugs at **bugs.kde.org**. You can see the bugs
+reported of **frameworks-syntax-highlighting** [here](https://bugs.kde.org/describecomponents.cgi?product=frameworks-syntax-highlighting).
+
+Also, you can report a bug [here](https://bugs.kde.org/enter_bug.cgi?product=frameworks-syntax-highlighting).
-* add an input file into the autotests/input/ folder, lets call it test.<language-extension>
+However, some users often report bugs related to syntax highlighting in
+[kate/syntax](https://bugs.kde.org/buglist.cgi?component=syntax&product=kate&resolution=---)
+and [kile/editor](https://bugs.kde.org/buglist.cgi?component=editor&product=kile&resolution=---).
-* if the file extension is not sufficient to trigger the right syntax definition, you can add an
- second file testname.<language-extension>.syntax that contains the syntax definition name
- to enforce the use of the right extension
+## Updating the syntax & themes pages of the kate-editor.org website
-* do "make && make test"
+To update the [kate-editor.org/syntax](https://kate-editor.org/syntax/) and
+[kate-editor.org/themes](https://kate-editor.org/themes/) websites
+including the update site & all linked examples/files,
+please run after successful **build** & **test** the following make target:
-* inspect the outputs found in your binary directory autotests/folding.out, autotests/html.output and autotests/output
+```bash
+make update_kate_editor_org
+```
-* if ok, run in the binary folder "./autotests/update-reference-data.sh" to copy the results to the right location
+This will clone the [kate-editor.org git](https://invent.kde.org/websites/kate-editor-org)
+from *invent.kde.org* into **kate-editor-org** inside the build directory and update the needed things.
-* add the result references after the copying to the git
+You can afterwards step into **kate-editor-org** and commit & push the change after review.
+The [kate-editor.org](https://kate-editor.org) webserver will update itself periodically from the repository on *invent.kde.org*.
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/autogenerated.pri b/src/libs/3rdparty/syntax-highlighting/autogenerated/autogenerated.pri
deleted file mode 100644
index aee620add9..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/autogenerated.pri
+++ /dev/null
@@ -1,9 +0,0 @@
-INCLUDEPATH *= $$PWD/src/lib
-INCLUDEPATH *= $$PWD
-
-SOURCES += \
- $$PWD/src/lib/ksyntaxhighlighting_logging.cpp
-
-HEADERS += \
- $$PWD/ksyntaxhighlighting_version.h \
- $$PWD/src/lib/ksyntaxhighlighting_logging.h
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/AbstractHighlighter b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/AbstractHighlighter
index b787873771..b787873771 100644
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/AbstractHighlighter
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/AbstractHighlighter
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Definition b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Definition
index 2c3241f841..2c3241f841 100644
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Definition
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Definition
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/DefinitionDownloader b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/DefinitionDownloader
index 2a82740d19..2a82740d19 100644
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/DefinitionDownloader
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/DefinitionDownloader
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/FoldingRegion b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/FoldingRegion
index 005b829d53..005b829d53 100644
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/FoldingRegion
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/FoldingRegion
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Format b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Format
index b0d6a10246..b0d6a10246 100644
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Format
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Format
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Repository b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Repository
index 189dbc2f69..189dbc2f69 100644
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Repository
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Repository
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/State b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/State
index e148d70391..e148d70391 100644
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/State
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/State
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/SyntaxHighlighter b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/SyntaxHighlighter
index b42982473b..b42982473b 100644
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/SyntaxHighlighter
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/SyntaxHighlighter
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Theme b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Theme
index 34a3e98e60..34a3e98e60 100644
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/Theme
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/Theme
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/WildcardMatcher b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/WildcardMatcher
new file mode 100644
index 0000000000..ecea1b09bd
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/include/KSyntaxHighlighting/WildcardMatcher
@@ -0,0 +1 @@
+#include "wildcardmatcher.h"
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/ksyntaxhighlighting_version.h b/src/libs/3rdparty/syntax-highlighting/autogenerated/ksyntaxhighlighting_version.h
index 70795908d6..d568d1813d 100644
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/ksyntaxhighlighting_version.h
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/ksyntaxhighlighting_version.h
@@ -1,12 +1,12 @@
// This file was generated by ecm_setup_version(): DO NOT EDIT!
-#ifndef SyntaxHighlighting_VERSION_H
-#define SyntaxHighlighting_VERSION_H
+#ifndef KSYNTAXHIGHLIGHTING_VERSION_H
+#define KSYNTAXHIGHLIGHTING_VERSION_H
-#define SyntaxHighlighting_VERSION_STRING "5.59.0"
-#define SyntaxHighlighting_VERSION_MAJOR 5
-#define SyntaxHighlighting_VERSION_MINOR 59
-#define SyntaxHighlighting_VERSION_PATCH 0
-#define SyntaxHighlighting_VERSION ((5<<16)|(59<<8)|(0))
+#define KSYNTAXHIGHLIGHTING_VERSION_STRING "5.249.0"
+#define KSYNTAXHIGHLIGHTING_VERSION_MAJOR 5
+#define KSYNTAXHIGHLIGHTING_VERSION_MINOR 249
+#define KSYNTAXHIGHLIGHTING_VERSION_PATCH 0
+#define KSYNTAXHIGHLIGHTING_VERSION ((5<<16)|(249<<8)|(0))
#endif
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/ksyntaxhighlighting_export.h b/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/ksyntaxhighlighting_export.h
new file mode 100644
index 0000000000..a7b9e09afa
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/ksyntaxhighlighting_export.h
@@ -0,0 +1,111 @@
+
+#ifndef KSYNTAXHIGHLIGHTING_EXPORT_H
+#define KSYNTAXHIGHLIGHTING_EXPORT_H
+
+#ifdef KSYNTAXHIGHLIGHTING_STATIC_DEFINE
+# define KSYNTAXHIGHLIGHTING_EXPORT
+# define KSYNTAXHIGHLIGHTING_NO_EXPORT
+#else
+# ifndef KSYNTAXHIGHLIGHTING_EXPORT
+# ifdef KF6SyntaxHighlighting_EXPORTS
+ /* We are building this library */
+# define KSYNTAXHIGHLIGHTING_EXPORT Q_DECL_EXPORT
+# else
+ /* We are using this library */
+# define KSYNTAXHIGHLIGHTING_EXPORT Q_DECL_IMPORT
+# endif
+# endif
+
+# ifndef KSYNTAXHIGHLIGHTING_NO_EXPORT
+# define KSYNTAXHIGHLIGHTING_NO_EXPORT
+# endif
+#endif
+
+#ifndef KSYNTAXHIGHLIGHTING_DECL_DEPRECATED
+# define KSYNTAXHIGHLIGHTING_DECL_DEPRECATED __declspec(deprecated)
+#endif
+
+#ifndef KSYNTAXHIGHLIGHTING_DECL_DEPRECATED_EXPORT
+# define KSYNTAXHIGHLIGHTING_DECL_DEPRECATED_EXPORT KSYNTAXHIGHLIGHTING_EXPORT KSYNTAXHIGHLIGHTING_DECL_DEPRECATED
+#endif
+
+#ifndef KSYNTAXHIGHLIGHTING_DECL_DEPRECATED_NO_EXPORT
+# define KSYNTAXHIGHLIGHTING_DECL_DEPRECATED_NO_EXPORT KSYNTAXHIGHLIGHTING_NO_EXPORT KSYNTAXHIGHLIGHTING_DECL_DEPRECATED
+#endif
+
+#if 0 /* DEFINE_NO_DEPRECATED */
+# ifndef KSYNTAXHIGHLIGHTING_NO_DEPRECATED
+# define KSYNTAXHIGHLIGHTING_NO_DEPRECATED
+# endif
+#endif
+
+#define KSYNTAXHIGHLIGHTING_DECL_DEPRECATED_TEXT(text) __declspec(deprecated(text))
+
+/* Take any defaults from group settings */
+#if !defined(KSYNTAXHIGHLIGHTING_NO_DEPRECATED) && !defined(KSYNTAXHIGHLIGHTING_DISABLE_DEPRECATED_BEFORE_AND_AT)
+# ifdef KF_NO_DEPRECATED
+# define KSYNTAXHIGHLIGHTING_NO_DEPRECATED
+# elif defined(KF_DISABLE_DEPRECATED_BEFORE_AND_AT)
+# define KSYNTAXHIGHLIGHTING_DISABLE_DEPRECATED_BEFORE_AND_AT KF_DISABLE_DEPRECATED_BEFORE_AND_AT
+# endif
+#endif
+#if !defined(KSYNTAXHIGHLIGHTING_DISABLE_DEPRECATED_BEFORE_AND_AT) && defined(KF_DISABLE_DEPRECATED_BEFORE_AND_AT)
+# define KSYNTAXHIGHLIGHTING_DISABLE_DEPRECATED_BEFORE_AND_AT KF_DISABLE_DEPRECATED_BEFORE_AND_AT
+#endif
+
+#if !defined(KSYNTAXHIGHLIGHTING_NO_DEPRECATED_WARNINGS) && !defined(KSYNTAXHIGHLIGHTING_DEPRECATED_WARNINGS_SINCE)
+# ifdef KF_NO_DEPRECATED_WARNINGS
+# define KSYNTAXHIGHLIGHTING_NO_DEPRECATED_WARNINGS
+# elif defined(KF_DEPRECATED_WARNINGS_SINCE)
+# define KSYNTAXHIGHLIGHTING_DEPRECATED_WARNINGS_SINCE KF_DEPRECATED_WARNINGS_SINCE
+# endif
+#endif
+#if !defined(KSYNTAXHIGHLIGHTING_DEPRECATED_WARNINGS_SINCE) && defined(KF_DEPRECATED_WARNINGS_SINCE)
+# define KSYNTAXHIGHLIGHTING_DEPRECATED_WARNINGS_SINCE KF_DEPRECATED_WARNINGS_SINCE
+#endif
+
+#if defined(KSYNTAXHIGHLIGHTING_NO_DEPRECATED)
+# undef KSYNTAXHIGHLIGHTING_DEPRECATED
+# define KSYNTAXHIGHLIGHTING_DEPRECATED_EXPORT KSYNTAXHIGHLIGHTING_EXPORT
+# define KSYNTAXHIGHLIGHTING_DEPRECATED_NO_EXPORT KSYNTAXHIGHLIGHTING_NO_EXPORT
+#elif defined(KSYNTAXHIGHLIGHTING_NO_DEPRECATED_WARNINGS)
+# define KSYNTAXHIGHLIGHTING_DEPRECATED
+# define KSYNTAXHIGHLIGHTING_DEPRECATED_EXPORT KSYNTAXHIGHLIGHTING_EXPORT
+# define KSYNTAXHIGHLIGHTING_DEPRECATED_NO_EXPORT KSYNTAXHIGHLIGHTING_NO_EXPORT
+#else
+# define KSYNTAXHIGHLIGHTING_DEPRECATED KSYNTAXHIGHLIGHTING_DECL_DEPRECATED
+# define KSYNTAXHIGHLIGHTING_DEPRECATED_EXPORT KSYNTAXHIGHLIGHTING_DECL_DEPRECATED_EXPORT
+# define KSYNTAXHIGHLIGHTING_DEPRECATED_NO_EXPORT KSYNTAXHIGHLIGHTING_DECL_DEPRECATED_NO_EXPORT
+#endif
+
+/* No deprecated API had been removed from build */
+#define KSYNTAXHIGHLIGHTING_EXCLUDE_DEPRECATED_BEFORE_AND_AT 0
+
+#define KSYNTAXHIGHLIGHTING_BUILD_DEPRECATED_SINCE(major, minor) 1
+
+#ifdef KSYNTAXHIGHLIGHTING_NO_DEPRECATED
+# define KSYNTAXHIGHLIGHTING_DISABLE_DEPRECATED_BEFORE_AND_AT KSYNTAXHIGHLIGHTING_VERSION
+#endif
+#ifdef KSYNTAXHIGHLIGHTING_NO_DEPRECATED_WARNINGS
+# define KSYNTAXHIGHLIGHTING_DEPRECATED_WARNINGS_SINCE 0
+#endif
+
+#ifndef KSYNTAXHIGHLIGHTING_DEPRECATED_WARNINGS_SINCE
+# ifdef KSYNTAXHIGHLIGHTING_DISABLE_DEPRECATED_BEFORE_AND_AT
+# define KSYNTAXHIGHLIGHTING_DEPRECATED_WARNINGS_SINCE KSYNTAXHIGHLIGHTING_DISABLE_DEPRECATED_BEFORE_AND_AT
+# else
+# define KSYNTAXHIGHLIGHTING_DEPRECATED_WARNINGS_SINCE KSYNTAXHIGHLIGHTING_VERSION
+# endif
+#endif
+
+#ifndef KSYNTAXHIGHLIGHTING_DISABLE_DEPRECATED_BEFORE_AND_AT
+# define KSYNTAXHIGHLIGHTING_DISABLE_DEPRECATED_BEFORE_AND_AT 0
+#endif
+
+#ifdef KSYNTAXHIGHLIGHTING_DEPRECATED
+# define KSYNTAXHIGHLIGHTING_ENABLE_DEPRECATED_SINCE(major, minor) (((major<<16)|(minor<<8)) > KSYNTAXHIGHLIGHTING_DISABLE_DEPRECATED_BEFORE_AND_AT)
+#else
+# define KSYNTAXHIGHLIGHTING_ENABLE_DEPRECATED_SINCE(major, minor) 0
+#endif
+
+#endif /* KSYNTAXHIGHLIGHTING_EXPORT_H */
diff --git a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/ksyntaxhighlighting_logging.cpp b/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/ksyntaxhighlighting_logging.cpp
index 4082ac4aff..af482ef3db 100644
--- a/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/ksyntaxhighlighting_logging.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/autogenerated/src/lib/ksyntaxhighlighting_logging.cpp
@@ -3,9 +3,5 @@
#include "ksyntaxhighlighting_logging.h"
namespace KSyntaxHighlighting {
-#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
-Q_LOGGING_CATEGORY(Log, "org.kde.ksyntaxhighlighting", QtInfoMsg)
-#else
-Q_LOGGING_CATEGORY(Log, "org.kde.ksyntaxhighlighting")
-#endif
+Q_LOGGING_CATEGORY(Log, "kf.syntaxhighlighting", QtInfoMsg)
}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/data/CMakeLists.txt
index 73adbe6d20..26a110d31b 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/CMakeLists.txt
+++ b/src/libs/3rdparty/syntax-highlighting/data/CMakeLists.txt
@@ -1,61 +1,85 @@
+# create a directory for generated definitions
+execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/generated/syntax)
+
+macro(generate_syntax_definition generator targetFile srcFile)
+ add_custom_target(
+ ${targetFile} ALL
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/generated/syntax/${targetFile}
+ )
+ add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated/syntax/${targetFile}
+ COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generators/${generator}
+ ${CMAKE_CURRENT_SOURCE_DIR}/syntax/${srcFile}
+ ${CMAKE_CURRENT_BINARY_DIR}/generated/syntax/${targetFile}
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generators/${generator}
+ ${CMAKE_CURRENT_SOURCE_DIR}/syntax/${srcFile}
+ ${ARGN}
+ VERBATIM
+ )
+ set(gen_defs ${gen_defs} ${CMAKE_CURRENT_BINARY_DIR}/generated/syntax/${targetFile})
+endmacro()
+
# generate PHP definitions
macro(generate_php_syntax_definition targetFile srcFile)
- execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/syntax)
- execute_process(COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generators/generate-php.pl
- INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/syntax/${srcFile}
- OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/syntax/${targetFile})
+ generate_syntax_definition(generate-php.pl ${targetFile} ${srcFile} ${CMAKE_CURRENT_SOURCE_DIR}/syntax/php.xml)
endmacro()
generate_php_syntax_definition(javascript-php.xml javascript.xml)
generate_php_syntax_definition(css-php.xml css.xml)
generate_php_syntax_definition(html-php.xml html.xml)
+generate_php_syntax_definition(javascript-react-php.xml javascript-react.xml)
+generate_php_syntax_definition(typescript-php.xml typescript.xml)
+generate_php_syntax_definition(mustache-php.xml mustache.xml)
+
+# generate DoxygenLua definition
+generate_syntax_definition(generate-doxygenlua.pl doxygenlua.xml doxygen.xml)
# find all definitions
file(GLOB src_defs "${CMAKE_CURRENT_SOURCE_DIR}/syntax/*.xml")
-set(defs
- ${src_defs}
- ${CMAKE_CURRENT_BINARY_DIR}/syntax/html-php.xml
- ${CMAKE_CURRENT_BINARY_DIR}/syntax/css-php.xml
- ${CMAKE_CURRENT_BINARY_DIR}/syntax/javascript-php.xml
-)
+set(defs ${src_defs} ${gen_defs})
+
+# object library to make cross-folder dependencies work
+add_library(SyntaxHighlightingData OBJECT)
# theme data resource
-qt5_add_resources(themes_QRC ${CMAKE_CURRENT_SOURCE_DIR}/themes/theme-data.qrc)
+target_sources(SyntaxHighlightingData PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/themes/theme-data.qrc)
-# do we want syntax files bundled in the library?
-if (QRC_SYNTAX)
- # generate the resource file
- set(qrc_file ${CMAKE_CURRENT_BINARY_DIR}/syntax-data.qrc)
- set(qrc_body "")
- foreach(def ${defs})
- get_filename_component(def_name ${def} NAME)
- string(APPEND qrc_body "<file alias=\"${def_name}\">${def}</file>\n")
- endforeach()
- set(SYNTAX_DATA_QRC_FILES_STRING ${qrc_body})
- configure_file(syntax-data.qrc.in ${qrc_file} @ONLY)
-
- # generate the index file
- add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/index.katesyntax"
+# generate the resource file
+set(qrc_file ${CMAKE_CURRENT_BINARY_DIR}/syntax-data.qrc)
+set(qrc_body "")
+foreach(def ${defs})
+ get_filename_component(def_name ${def} NAME)
+ string(APPEND qrc_body "<file alias=\"${def_name}\">${def}</file>\n")
+endforeach()
+set(SYNTAX_DATA_QRC_FILES_STRING ${qrc_body})
+configure_file(syntax-data.qrc.in ${qrc_file} @ONLY)
+
+# generate the index file
+add_custom_target(katesyntax DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/index.katesyntax")
+add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/index.katesyntax"
COMMAND katehighlightingindexer "${CMAKE_CURRENT_BINARY_DIR}/index.katesyntax" "${CMAKE_CURRENT_SOURCE_DIR}/schema/language.xsd" "${CMAKE_CURRENT_BINARY_DIR}/syntax-data.qrc"
- DEPENDS ${defs} ${CMAKE_CURRENT_SOURCE_DIR}/schema/language.xsd ${CMAKE_CURRENT_BINARY_DIR}/syntax-data.qrc
- )
+ DEPENDS katehighlightingindexer ${defs} ${CMAKE_CURRENT_SOURCE_DIR}/schema/language.xsd ${CMAKE_CURRENT_BINARY_DIR}/syntax-data.qrc
+)
+# do we want syntax files bundled in the library?
+if (QRC_SYNTAX)
# generate the qrc file manually, to make dependencies on generated files work...
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/qrc_syntax-data.cpp"
- COMMAND ${Qt5Core_RCC_EXECUTABLE} --name syntax_data -o "${CMAKE_CURRENT_BINARY_DIR}/qrc_syntax-data.cpp" "${CMAKE_CURRENT_BINARY_DIR}/syntax-data.qrc"
+ COMMAND Qt${QT_MAJOR_VERSION}::rcc --name syntax_data -o "${CMAKE_CURRENT_BINARY_DIR}/qrc_syntax-data.cpp" "${CMAKE_CURRENT_BINARY_DIR}/syntax-data.qrc"
DEPENDS ${defs} ${CMAKE_CURRENT_BINARY_DIR}/index.katesyntax
)
set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/qrc_syntax-data.cpp" PROPERTIES SKIP_AUTOMOC ON)
- # object library to make cross-folder dependencies work, themes + syntax files
- add_library(SyntaxHighlightingData OBJECT ${themes_QRC} ${CMAKE_CURRENT_BINARY_DIR}/qrc_syntax-data.cpp)
+ target_sources(SyntaxHighlightingData PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/qrc_syntax-data.cpp)
else()
+ add_dependencies(SyntaxHighlightingData katesyntax)
# install the syntax files as normal files into the prefix
- install (FILES ${defs} DESTINATION share/org.kde.syntax-highlighting/syntax)
-
- # object library to make cross-folder dependencies work, only themes
- add_library(SyntaxHighlightingData OBJECT ${themes_QRC})
+ install (FILES "${CMAKE_CURRENT_BINARY_DIR}/index.katesyntax" ${defs} DESTINATION ${KDE_INSTALL_DATADIR}/org.kde.syntax-highlighting/syntax-bundled)
endif()
# set PIC to allow use in static and shared libs
+# this needs some more recent CMake than generally required
set_property(TARGET SyntaxHighlightingData PROPERTY POSITION_INDEPENDENT_CODE 1)
+if(NOT ${CMAKE_VERSION} VERSION_LESS "3.13.0")
+ target_link_libraries(SyntaxHighlightingData PRIVATE Qt${QT_MAJOR_VERSION}::Core)
+endif()
diff --git a/src/libs/3rdparty/syntax-highlighting/data/data.pro b/src/libs/3rdparty/syntax-highlighting/data/data.pro
deleted file mode 100644
index 1028ea1579..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/data/data.pro
+++ /dev/null
@@ -1,11 +0,0 @@
-TEMPLATE = aux
-
-include(../../../../../qtcreator.pri)
-
-STATIC_BASE = $$PWD
-STATIC_OUTPUT_BASE = $$IDE_DATA_PATH/generic-highlighter
-STATIC_INSTALL_BASE = $$INSTALL_DATA_PATH/generic-highlighter
-
-STATIC_FILES += $$files($$PWD/syntax/*, true)
-
-include(../../../../../qtcreatordata.pri)
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/.gitignore b/src/libs/3rdparty/syntax-highlighting/data/generators/.gitignore
new file mode 100644
index 0000000000..8f4dc5cfcf
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/.gitignore
@@ -0,0 +1 @@
+Pipfile.lock
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/Pipfile b/src/libs/3rdparty/syntax-highlighting/data/generators/Pipfile
new file mode 100644
index 0000000000..227d4cd2cc
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/Pipfile
@@ -0,0 +1,12 @@
+# kate: hl toml;
+
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+click = "~= 8.0"
+jinja2 = "~= 3.0"
+lxml = "*"
+PyYAML = "*"
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/cmake.xml.tpl b/src/libs/3rdparty/syntax-highlighting/data/generators/cmake.xml.tpl
index 33cc7511aa..48f56f7b60 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/generators/cmake.xml.tpl
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/cmake.xml.tpl
@@ -1,38 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd"
+<!DOCTYPE language
[
- <!ENTITY id_re "[_A-Za-z][\-_0-9A-Za-z]*">
+ <!-- NOTE See https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#variable-references -->
+ <!ENTITY var_ref_re "[/\.\+\-_0-9A-Za-z]+">
+ <!-- NOTE See `cmGeneratorExpression::IsValidTargetName` -->
+ <!ENTITY tgt_name_re "[A-Za-z0-9_\.\+\-]+">
]>
<!--
- This file is part of KDE's kate project.
-
- Copyright 2004 Alexander Neundorf (neundorf@kde.org)
- Copyright 2005 Dominik Haumann (dhdev@gmx.de)
- Copyright 2007,2008,2013,2014 Matthew Woehlke (mw_triad@users.sourceforge.net)
- Copyright 2013-2015,2017-2018 Alex Turbov (i.zaufi@gmail.com)
-
- **********************************************************************
- * This library is free software; you can redistribute it and/or *
- * modify it under the terms of the GNU Lesser General Public *
- * License as published by the Free Software Foundation; either *
- * version 2 of the License, or (at your option) any later version. *
- * *
- * This library 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 *
- * Lesser General Public License for more details. *
- * *
- * You should have received a copy of the GNU Lesser General Public *
- * License along with this library; if not, write to the *
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
- * Boston, MA 02110-1301, USA. *
- **********************************************************************
+ This file is part of KDE's kate project.
+
+ SPDX-FileCopyrightText: 2004 Alexander Neundorf <neundorf@kde.org>
+ SPDX-FileCopyrightText: 2005 Dominik Haumann <dhdev@gmx.de>
+ SPDX-FileCopyrightText: 2007, 2008, 2013, 2014 Matthew Woehlke <mw_triad@users.sourceforge.net>
+ SPDX-FileCopyrightText: 2013-2015, 2017-2023 Alex Turbov <i.zaufi@gmail.com>
+
+ SPDX-License-Identifier: LGPL-2.0-or-later
-->
+<!-- ***** THIS FILE WAS GENERATED BY A SCRIPT - DO NOT EDIT *****
+ cd data/generators
+ # increase version of cmake.xml.tpl then
+ ./generate-cmake-syntax.py cmake.yaml > ../syntax/cmake.xml
+-->
+
<language
name="CMake"
- version="11"
- kateversion="2.4"
+ version="<!--{version}-->"
+ kateversion="5.62"
section="Other"
extensions="CMakeLists.txt;*.cmake;*.cmake.in"
style="CMake"
@@ -43,169 +37,286 @@
<highlighting>
<list name="commands">
- {%- for command in commands %}
- <item>{{command.name}}</item>
- {%- endfor %}
+ <!--[- for command in commands ]-->
+ <item><!--{command.name}--></item>
+ <!--[- endfor ]-->
</list>
- {% for command in commands -%}
- {%- if command.named_args and command.named_args.kw %}
- <list name="{{command.name}}_nargs">
- {%- for arg in command.named_args.kw %}
- <item>{{arg}}</item>
- {%- endfor %}
+
+ <!--[- macro render_command_arg_lists(commands) ]-->
+ <!--[- for command in commands -]-->
+ <!--[- if command.named_args and command.named_args.kw ]-->
+ <list name="<!--{command.name}-->_nargs">
+ <!--[- for arg in command.named_args.kw ]-->
+ <item><!--{arg}--></item>
+ <!--[- endfor ]-->
</list>
- {%- endif %}
- {%- if command.special_args and command.special_args.kw %}
- <list name="{{command.name}}_sargs">
- {%- for arg in command.special_args.kw %}
- <item>{{arg}}</item>
- {%- endfor %}
+ <!--[- endif ]-->
+ <!--[- if command.special_args and command.special_args.kw ]-->
+ <list name="<!--{command.name}-->_sargs">
+ <!--[- for arg in command.special_args.kw ]-->
+ <item><!--{arg}--></item>
+ <!--[- endfor ]-->
</list>
- {%- endif %}
- {%- endfor %}
+ <!--[- endif ]-->
+ <!--[- endfor ]-->
+ <!--[- endmacro ]-->
+ <!--{- render_command_arg_lists(commands) }-->
+ <!--{- render_command_arg_lists(standard_module_commands) }-->
<list name="variables">
- {%- for var in variables.kw %}
- <item>{{var}}</item>
- {%- endfor %}
+ <!--[- for var in variables.kw ]-->
+ <item><!--{var}--></item>
+ <!--[- endfor ]-->
+ </list>
+
+ <list name="deprecated-or-internal-variables">
+ <!--[- for var in deprecated_or_internal_variables.kw ]-->
+ <item><!--{var}--></item>
+ <!--[- endfor ]-->
+ </list>
+
+ <list name="environment-variables">
+ <!--[- for var in environment_variables.kw ]-->
+ <item><!--{var}--></item>
+ <!--[- endfor ]-->
</list>
- {%- for kind in properties.kinds %}
- <list name="{{ kind|replace('_', '-') }}">
- {%- for prop in properties[kind].kw %}
- <item>{{prop}}</item>
- {%- endfor %}
+ <!--[- for kind in properties.kinds ]-->
+ <list name="<!--{ kind|replace('_', '-') }-->">
+ <!--[- for prop in properties[kind].kw ]-->
+ <item><!--{prop}--></item>
+ <!--[- endfor ]-->
</list>
- {%- endfor %}
+ <!--[- endfor ]-->
<list name="generator-expressions">
- {%- for expr in generator_expressions %}
- <item>{{ expr }}</item>
- {%- endfor %}
+ <!--[- for expr in generator_expressions ]-->
+ <item><!--{ expr }--></item>
+ <!--[- endfor ]-->
+ </list>
+ <!--[- for expr in complex_generator_expressions ]-->
+ <list name="genex-<!--{expr.name}-->-subcommands">
+ <!--[- for cmd in expr.subcommands ]-->
+ <item><!--{ cmd }--></item>
+ <!--[- endfor ]-->
+ </list>
+ <!--[- endfor ]-->
+
+ <list name="standard-modules">
+ <!--[- for module in modules.utility ]-->
+ <item><!--{ module }--></item>
+ <!--[- endfor ]-->
+ </list>
+
+ <list name="standard-finder-modules">
+ <!--[- for module in modules.finder ]-->
+ <item><!--{ module | replace('Find', '') }--></item>
+ <!--[- endfor ]-->
+ </list>
+
+ <list name="deprecated-modules">
+ <!--[- for module in modules.deprecated ]-->
+ <item><!--{ module }--></item>
+ <!--[- endfor ]-->
+ </list>
+
+ <!-- Source/cmStringAlgorithms.cxx: bool cmIsOff(cm::string_view val) -->
+ <list name="true_special_arg">
+ <item>TRUE</item>
+ <item>ON</item>
+ <item>YES</item>
+ <item>Y</item>
+ <item>0</item>
+ </list>
+
+ <!-- Source/cmStringAlgorithms.cxx: bool cmIsOff(cm::string_view val) -->
+ <list name="false_special_arg">
+ <item>FALSE</item>
+ <item>OFF</item>
+ <item>NO</item>
+ <item>IGNORE</item>
+ <item>N</item>
+ <item>0</item>
</list>
<contexts>
<context attribute="Normal Text" lineEndContext="#stay" name="Normal Text">
- <DetectSpaces/>
- {% for command in commands -%}
- <WordDetect String="{{command.name}}" insensitive="true" attribute="Command" context="{{command.name}}_ctx" />
- {% endfor -%}
- <RegExpr attribute="Region Marker" context="RST Documentation" String="^#\[(=*)\[\.rst:" column="0" />
- <RegExpr attribute="Comment" context="Bracketed Comment" String="#\[(=*)\[" />
- <DetectChar attribute="Comment" context="Comment" char="#" />
+ <DetectSpaces />
+ <!--[ for command in commands -]-->
+ <WordDetect String="<!--{command.name}-->" insensitive="true" attribute="<!--{command.attribute}-->" context="<!--{command.name}-->_ctx"<!--[ if command.start_region ]--> beginRegion="<!--{command.start_region}-->"<!--[ endif -]--> <!--[- if command.end_region ]--> endRegion="<!--{command.end_region}-->"<!--[ endif ]--> />
+ <!--[ endfor -]-->
+ <!--[ for command in standard_module_commands -]-->
+ <WordDetect String="<!--{command.name}-->" insensitive="true" attribute="CMake Provided Function/Macro" context="<!--{command.name}-->_ctx" />
+ <!--[ endfor -]-->
+ <DetectChar attribute="Comment" context="Match Comments and Docs" char="#" lookAhead="true" />
<DetectIdentifier attribute="User Function/Macro" context="User Function" />
- <RegExpr attribute="@Variable Substitution" context="@VarSubst" String="@&id_re;@" lookAhead="true" />
- <!-- Include keywords matching for language autocompleter work -->
- <keyword attribute="Command" context="#stay" String="commands" />
- </context>
-
- {% for command in commands -%}
- {#
- <!--
- {{ command|pprint }}
- -->
- -#}
- <context attribute="Normal Text" lineEndContext="#stay" name="{{command.name}}_ctx">
- <DetectChar attribute="Normal Text" context="{{command.name}}_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="{{command.name}}_ctx_op">
- {%- if command.nested_parentheses %}
- <DetectChar attribute="Normal Text" context="{{command.name}}_ctx_op_nested" char="(" />
- {%- endif %}
- <IncludeRules context="EndCmdPop2" />
- {%- if command.named_args and command.named_args.kw %}
- <keyword attribute="Named Args" context="#stay" String="{{command.name}}_nargs" />
- {%- endif %}
- {%- if command.special_args and command.special_args.kw %}
- <keyword attribute="Special Args" context="#stay" String="{{command.name}}_sargs" />
- {%- endif %}
- {%- if command.property_args and command.property_args.kw %}
- {%- for kind in command.property_args.kw %}
- <keyword attribute="Property" context="#stay" String="{{kind}}" />
- {%- if properties[kind|replace('-', '_')].re %}
- <IncludeRules context="Detect More {{kind}}" />
- {%- endif %}
- {%- endfor %}
- {%- endif %}
- {%- if command is not nulary %}
+ <RegExpr attribute="@Variable Substitution" context="@VarSubst" String="@&var_ref_re;@" lookAhead="true" />
+ <IncludeRules context="LineError" />
+ </context>
+ <!--[- macro render_command_parsers(commands) ]-->
+ <!--[ for command in commands -]-->
+ <context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_ctx">
+ <DetectChar attribute="Normal Text" context="<!--{command.name}-->_ctx_op<!--{'_tgt_first' if command.first_arg_is_target else '_tgts_first' if command.first_args_are_targets else ''}-->" char="(" />
+ <DetectChar attribute="Normal Text" context="#pop" char=")" />
+ </context>
+ <!--[- if command.first_arg_is_target ]-->
+ <context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_ctx_op_tgt_first">
+ <DetectSpaces />
+ <RegExpr attribute="Aliased Targets" context="<!--{command.name}-->_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*" />
+ <RegExpr attribute="Targets" context="<!--{command.name}-->_ctx_op" String="&tgt_name_re;" />
+ <IncludeRules context="User Function Opened" />
+ <IncludeRules context="LineError" />
+ </context>
+ <!--[- endif ]-->
+ <!--[- if command.first_args_are_targets ]-->
+ <context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_ctx_op_tgts_first">
+ <DetectSpaces />
+ <!--[- if command.named_args and command.named_args.kw ]-->
+ <!-- NOTE Handle the only case in CMake nowadays:
+ 1. `set_target_properties` have a named keyword (`PROPERTIES`) after targets list
+ -->
+ <keyword context="<!--{command.name}-->_ctx_op" String="<!--{command.name}-->_nargs" lookAhead="true" />
+ <!--[- endif ]-->
+ <IncludeRules context="Detect Aliased Targets" />
+ <IncludeRules context="Detect Targets" />
+ <IncludeRules context="User Function Opened" />
+ <IncludeRules context="LineError" />
+ </context>
+ <!--[- endif ]-->
+ <!--[- if not command.first_args_are_targets or (command.named_args and command.named_args.kw) ]-->
+ <context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_ctx_op">
+ <DetectSpaces />
+ <!--[- if command.nested_parentheses ]-->
+ <DetectChar attribute="Normal Text" context="<!--{command.name}-->_ctx_op_nested" char="(" />
+ <!--[- endif ]-->
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true" />
+ <!--[- if command.named_args and command.named_args.kw ]-->
+ <!--[- if command.has_target_name_after_kw ]-->
+ <WordDetect String="<!--{command.has_target_name_after_kw}-->" attribute="Named Args" context="Target Name" />
+ <!--[- endif ]-->
+ <!--[- if command.has_target_names_after_kw ]-->
+ <WordDetect String="<!--{command.has_target_names_after_kw}-->" attribute="Named Args" context="<!--{command.name}-->_tgts" />
+ <!--[- endif ]-->
+ <keyword attribute="Named Args" context="#stay" String="<!--{command.name}-->_nargs" />
+ <!--[- endif ]-->
+ <!--[- if command.name == 'include' ]-->
+ <keyword attribute="Standard Module" context="#stay" String="standard-modules" />
+ <keyword attribute="Deprecated Module" context="#stay" String="deprecated-modules" />
+ <!--[- endif ]-->
+ <!--[- if command.name == 'find_package' ]-->
+ <keyword attribute="Standard Module" context="#stay" String="standard-finder-modules" />
+ <!--[- endif ]-->
+ <!--[- if command.special_args and command.special_args.kw ]-->
+ <keyword attribute="Special Args" context="#stay" String="<!--{command.name}-->_sargs" />
+ <!--[- endif ]-->
+ <!--[- if command.property_args and command.property_args.kw ]-->
+ <!--[- for kind in command.property_args.kw ]-->
+ <keyword attribute="Property" context="#stay" String="<!--{kind}-->" />
+ <!--[- if properties[kind|replace('-', '_')].re ]-->
+ <IncludeRules context="Detect More <!--{kind}-->" />
+ <!--[- endif ]-->
+ <!--[- endfor ]-->
+ <!--[- endif ]-->
+ <!--[- if command is not nulary ]-->
<IncludeRules context="User Function Args" />
- {%- if command.name == 'cmake_policy' %}
+ <!--[- if command.name == 'cmake_policy' ]-->
<!-- NOTE Handle CMP<NNN> as a special arg of `cmake_policy` command -->
<RegExpr attribute="Special Args" context="#stay" String="\bCMP[0-9]+\b" />
- {%- endif %}
- {%- endif %}
- </context>
- {%- if command.nested_parentheses %}
- <context attribute="Normal Text" lineEndContext="#stay" name="{{command.name}}_ctx_op_nested">
- <IncludeRules context="EndCmdPop" />
- {%- if command.named_args and command.named_args.kw %}
- <keyword attribute="Named Args" context="#stay" String="{{command.name}}_nargs" />
- {%- endif %}
- {%- if command.special_args and command.special_args.kw %}
- <keyword attribute="Special Args" context="#stay" String="{{command.name}}_sargs" />
- {%- endif %}
- {%- if command.property_args and command.property_args.kw %}
- {%- for kind in command.property_args.kw %}
- <keyword attribute="Property" context="#stay" String="{{kind}}" />
- {%- if properties[kind|replace('-', '_')].re %}
- <IncludeRules context="Detect More {{kind}}" />
- {%- endif %}
- {%- endfor %}
- {%- endif %}
+ <!--[- endif ]-->
+ <!--[- endif ]-->
+ </context>
+ <!--[- endif ]-->
+ <!--[- if command.has_target_names_after_kw ]-->
+ <context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_tgts">
+ <DetectSpaces />
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true" />
+ <keyword attribute="Named Args" context="#pop" String="<!--{command.name}-->_nargs" />
+ <IncludeRules context="Detect Aliased Targets" />
+ <IncludeRules context="Detect Targets" />
<IncludeRules context="User Function Args" />
+ <IncludeRules context="LineError" />
</context>
- {%- endif %}
- {% endfor -%}
-
- {% for kind in properties.kinds if properties[kind].re -%}
- <context attribute="Normal Text" lineEndContext="#stay" name="Detect More {{ kind|replace('_', '-') }}">
- {%- for prop in properties[kind].re %}
- <RegExpr attribute="Property" context="#stay" String="{{prop}}" />
- {%- endfor %}
- </context>{{ '\n' }}
- {% endfor -%}
-
- <context attribute="Normal Text" lineEndContext="#stay" name="EndCmdPop">
+ <!--[- endif ]-->
+ <!--[- if command.nested_parentheses ]-->
+ <context attribute="Normal Text" lineEndContext="#stay" name="<!--{command.name}-->_ctx_op_nested">
+ <DetectSpaces />
<DetectChar attribute="Normal Text" context="#pop" char=")" />
+ <DetectChar attribute="Normal Text" context="<!--{command.name}-->_ctx_op_nested" char="(" />
+ <!--[- if command.named_args and command.named_args.kw ]-->
+ <keyword attribute="Named Args" context="#stay" String="<!--{command.name}-->_nargs" />
+ <!--[- endif ]-->
+ <!--[- if command.special_args and command.special_args.kw ]-->
+ <keyword attribute="Special Args" context="#stay" String="<!--{command.name}-->_sargs" />
+ <!--[- endif ]-->
+ <!--[- if command.property_args and command.property_args.kw ]-->
+ <!--[- for kind in command.property_args.kw ]-->
+ <keyword attribute="Property" context="#stay" String="<!--{kind}-->" />
+ <!--[- if properties[kind|replace('-', '_')].re ]-->
+ <IncludeRules context="Detect More <!--{kind}-->" />
+ <!--[- endif ]-->
+ <!--[- endfor ]-->
+ <!--[- endif ]-->
+ <IncludeRules context="User Function Args" />
</context>
-
- <context attribute="Normal Text" lineEndContext="#stay" name="EndCmdPop2">
- <DetectChar attribute="Normal Text" context="#pop#pop" char=")" />
- </context>
+ <!--[- endif ]-->
+ <!--[ endfor -]-->
+ <!--[- endmacro -]-->
+ <!--{- render_command_parsers(commands) -}-->
+ <!--{- render_command_parsers(standard_module_commands) -}-->
+ <!--[ for kind in properties.kinds if properties[kind].re -]-->
+ <context attribute="Normal Text" lineEndContext="#stay" name="Detect More <!--{ kind|replace('_', '-') }-->">
+ <RegExpr attribute="Property" context="#stay" String="<!--{properties[kind].re}-->" />
+ </context><!--{ '\n' }-->
+ <!--[ endfor -]-->
<context attribute="User Function/Macro" lineEndContext="#stay" name="User Function">
<DetectChar attribute="Normal Text" context="User Function Opened" char="(" />
- <IncludeRules context="EndCmdPop2" />
+ <DetectChar attribute="Normal Text" context="#pop" char=")" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="User Function Opened">
- <IncludeRules context="EndCmdPop2" />
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true" />
<IncludeRules context="User Function Args" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Builtin Variables">
+ <RegExpr attribute="Internal Name" context="#stay" String="\b_&var_ref_re;\b" />
+ <keyword attribute="CMake Internal Variable" context="#stay" String="deprecated-or-internal-variables" insensitive="false" />
<keyword attribute="Builtin Variable" context="#stay" String="variables" insensitive="false" />
<IncludeRules context="Detect More Builtin Variables" />
- <RegExpr attribute="Internal Name" context="#stay" String="\b_&id_re;\b" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect More Builtin Variables">
- {%- for var in variables.re %}
- <RegExpr attribute="Builtin Variable" context="#stay" String="{{var}}" />
- {%- endfor %}
+ <!--[- if deprecated_or_internal_variables.re ]-->
+ <RegExpr attribute="CMake Internal Variable" context="#stay" String="<!--{deprecated_or_internal_variables.re}-->" />
+ <!--[- endif ]-->
+ <!--[- if variables.re ]-->
+ <RegExpr attribute="Builtin Variable" context="#stay" String="<!--{variables.re}-->" />
+ <!--[- endif ]-->
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Variable Substitutions">
- <RegExpr attribute="Environment Variable Substitution" context="#stay" String="\$ENV\{\s*[\w-]+\s*\}" />
+ <RegExpr attribute="Cache Variable Substitution" context="#stay" String="\$CACHE\{\s*[\w-]+\s*\}" />
+ <RegExpr attribute="Environment Variable Substitution" context="EnvVarSubst" String="\$?ENV\{" />
<Detect2Chars attribute="Variable Substitution" context="VarSubst" char="$" char1="{" />
- <RegExpr attribute="@Variable Substitution" context="@VarSubst" String="@&id_re;@" lookAhead="true" />
+ <RegExpr attribute="@Variable Substitution" context="@VarSubst" String="@&var_ref_re;@" lookAhead="true" />
+ </context>
+
+ <context attribute="Environment Variable Substitution" lineEndContext="#pop" name="EnvVarSubst">
+ <DetectChar attribute="Environment Variable Substitution" context="#pop" char="}" />
+ <keyword attribute="Standard Environment Variable" context="#stay" String="environment-variables" insensitive="false" />
+ <!--[- if environment_variables.re ]-->
+ <RegExpr attribute="Standard Environment Variable" context="#stay" String="<!--{environment_variables.re}-->" />
+ <!--[- endif ]-->
+ <DetectIdentifier />
+ <IncludeRules context="Detect Variable Substitutions" />
</context>
<context attribute="Variable Substitution" lineEndContext="#pop" name="VarSubst">
+ <DetectChar attribute="Variable Substitution" context="#pop" char="}" />
<IncludeRules context="Detect Builtin Variables" />
<DetectIdentifier />
- <DetectChar attribute="Variable Substitution" context="#pop" char="}" />
<IncludeRules context="Detect Variable Substitutions" />
</context>
@@ -214,62 +325,104 @@
</context>
<context attribute="@Variable Substitution" lineEndContext="#pop#pop" name="VarSubst@">
+ <DetectChar attribute="@Variable Substitution" context="#pop#pop" char="@" />
<IncludeRules context="Detect Builtin Variables" />
<DetectIdentifier />
- <DetectChar attribute="@Variable Substitution" context="#pop#pop" char="@" />
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="Target Name">
+ <DetectSpaces />
+ <RegExpr attribute="Aliased Targets" context="#pop" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*" />
+ <IncludeRules context="Detect Targets" />
+ <IncludeRules context="User Function Opened" />
+ <IncludeRules context="LineError" />
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="Detect Targets">
+ <RegExpr attribute="Targets" context="#stay" String="&tgt_name_re;" />
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="LineError">
+ <RegExpr attribute="Error" context="#stay" String=".*" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="User Function Args">
<Detect2Chars attribute="Normal Text" context="#stay" char="\" char1="(" />
<Detect2Chars attribute="Normal Text" context="#stay" char="\" char1=")" />
- <RegExpr attribute="Escapes" context="#stay" String="\\[&quot;$n\\]" />
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="&quot;" />
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="$" />
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="n" />
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="\" />
<DetectChar attribute="Strings" context="String" char="&quot;" />
- <RegExpr attribute="Strings" context="Bracketed String" String="\[(=*)\[" />
- <RegExpr attribute="Comment" context="Bracketed Comment" String="#\[(=*)\[" />
- <DetectChar attribute="Comment" context="Comment" char="#" />
+ <RegExpr attribute="Strings" context="Bracketed String" String="\[(=*)\[" beginRegion="BracketedString" />
+ <DetectChar attribute="Comment" context="Match Comments" char="#" lookAhead="true" />
<IncludeRules context="Detect Builtin Variables" />
<IncludeRules context="Detect Variable Substitutions" />
<IncludeRules context="Detect Special Values" />
<IncludeRules context="Detect Aliased Targets" />
<IncludeRules context="Detect Generator Expressions" />
+ <DetectIdentifier />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Special Values">
- <RegExpr attribute="True Special Arg" context="#stay" String="\b(TRUE|ON)\b" />
- <RegExpr attribute="False Special Arg" context="#stay" String="\b(FALSE|OFF|(&id_re;-)?NOTFOUND)\b" />
- <RegExpr attribute="Special Args" context="#stay" String="\bCMP[0-9][0-9][0-9]\b" />
+ <RegExpr attribute="Version Arg" context="#stay" String="\b[0-9]++(.[0-9]++)+\b" />
+ <keyword attribute="True Special Arg" context="#stay" String="true_special_arg" insensitive="true" />
+ <keyword attribute="False Special Arg" context="#stay" String="false_special_arg" insensitive="true" />
+ <RegExpr attribute="False Special Arg" context="#stay" String="\b(?:&var_ref_re;-)?NOTFOUND\b" />
+ <RegExpr attribute="Special Args" context="#stay" String="\bCMP[0-9][0-9][0-9][0-9]\b" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Aliased Targets">
- <RegExpr attribute="Aliased Targets" context="#stay" String="\b&id_re;::&id_re;(::&id_re;)*\b" />
+ <RegExpr attribute="Aliased Targets" context="#stay" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*" />
+ </context>
+
+ <context attribute="Comment" lineEndContext="#pop" name="Match Comments">
+ <DetectSpaces />
+ <RegExpr attribute="Comment" context="#pop!Bracketed Comment" String="#\[(=*)\[" beginRegion="BracketedComment" />
+ <DetectChar attribute="Comment" context="#pop!Comment" char="#" />
+ <DetectIdentifier />
+ </context>
+
+ <context attribute="Comment" lineEndContext="#pop" name="Match Comments and Docs">
+ <RegExpr attribute="Region Marker" context="#pop!RST Documentation" String="^#\[(=*)\[\.rst:" column="0" beginRegion="RSTDocumentation" />
+ <IncludeRules context="Match Comments" />
</context>
<context attribute="Comment" lineEndContext="#pop" name="Comment">
+ <DetectSpaces />
<LineContinue attribute="Comment" context="#pop" />
- <IncludeRules context="##Alerts" />
- <IncludeRules context="##Modelines" />
+ <IncludeRules context="##Comments" />
+ <DetectIdentifier />
</context>
<context attribute="Comment" lineEndContext="#stay" name="RST Documentation" dynamic="true">
- <RegExpr attribute="Region Marker" context="#pop" String="^#?\]%1\]" dynamic="true" column="0" />
+ <RegExpr attribute="Region Marker" context="#pop" String="^#?\]%1\]" dynamic="true" column="0" endRegion="RSTDocumentation" />
<IncludeRules context="##reStructuredText" />
</context>
<context attribute="Comment" lineEndContext="#stay" name="Bracketed Comment" dynamic="true">
- <RegExpr attribute="Comment" context="#pop" String=".*\]%1\]" dynamic="true" />
- <IncludeRules context="##Alerts" />
- <IncludeRules context="##Modelines" />
+ <LineContinue attribute="Comment" context="#stay" />
+ <DetectSpaces />
+ <StringDetect attribute="Comment" context="#pop" String="]%1]" dynamic="true" endRegion="BracketedComment" />
+ <IncludeRules context="##Comments" />
</context>
<context attribute="Strings" lineEndContext="#stay" name="String">
+ <DetectSpaces />
+ <DetectIdentifier />
<RegExpr attribute="Strings" context="#pop" String="&quot;(?=[ );]|$)" />
- <RegExpr attribute="Escapes" context="#stay" String="\\[&quot;$nrt\\]" />
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="&quot;" />
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="$" />
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="n" />
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="r" />
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="t" />
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="\" />
<IncludeRules context="Detect Variable Substitutions" />
<IncludeRules context="Detect Generator Expressions" />
</context>
<context attribute="Strings" lineEndContext="#stay" name="Bracketed String" dynamic="true">
- <RegExpr attribute="Strings" context="#pop" String="\]%1\]" dynamic="true" />
+ <StringDetect attribute="Strings" context="#pop" String="]%1]" dynamic="true" endRegion="BracketedString" />
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Generator Expressions">
@@ -281,43 +434,67 @@
<DetectChar attribute="Comment" context="Comment" char="#" />
<DetectChar attribute="Generator Expression" context="#pop" char="&gt;" />
<keyword attribute="Generator Expression Keyword" context="#stay" String="generator-expressions" insensitive="false" />
+ <!--[- for expr in complex_generator_expressions ]-->
+ <WordDetect String="<!--{expr.name}-->" attribute="Generator Expression Keyword" context="genex_<!--{expr.name}-->_ctx" />
+ <!--[- endfor ]-->
<IncludeRules context="Detect Aliased Targets" />
<IncludeRules context="Detect Variable Substitutions" />
+ <DetectIdentifier />
</context>
+ <!--[- for expr in complex_generator_expressions ]-->
+ <context attribute="Generator Expression" lineEndContext="#stay" name="genex_<!--{expr.name}-->_ctx" fallthroughContext="#pop">
+ <DetectChar char=":" context="#stay" />
+ <DetectSpaces />
+ <keyword attribute="Generator Expression Sub-Command" context="#pop" String="genex-<!--{expr.name}-->-subcommands" insensitive="false" />
+ </context>
+ <!--[- endfor ]-->
+
</contexts>
<itemDatas>
<itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false" />
+ <itemData name="Comment" defStyleNum="dsComment" spellChecking="true" />
<itemData name="Command" defStyleNum="dsKeyword" spellChecking="false" />
+ <itemData name="Control Flow" defStyleNum="dsControlFlow" spellChecking="false" />
+ <itemData name="CMake Provided Function/Macro" defStyleNum="dsFunction" bold="true" spellChecking="false" />
<itemData name="User Function/Macro" defStyleNum="dsFunction" spellChecking="false" />
<itemData name="Property" defStyleNum="dsOthers" spellChecking="false" />
+ <itemData name="Targets" defStyleNum="dsBaseN" spellChecking="false" />
<itemData name="Aliased Targets" defStyleNum="dsBaseN" spellChecking="false" />
<itemData name="Named Args" defStyleNum="dsOthers" spellChecking="false" />
<itemData name="Special Args" defStyleNum="dsOthers" spellChecking="false" />
<itemData name="True Special Arg" defStyleNum="dsOthers" color="#30a030" selColor="#30a030" spellChecking="false" />
<itemData name="False Special Arg" defStyleNum="dsOthers" color="#e05050" selColor="#e05050" spellChecking="false" />
+ <itemData name="Version Arg" defStyleNum="dsDataType" spellChecking="false" />
<itemData name="Strings" defStyleNum="dsString" spellChecking="true" />
- <itemData name="Escapes" defStyleNum="dsChar" spellChecking="false" />
+ <itemData name="Escapes" defStyleNum="dsSpecialChar" spellChecking="false" />
<itemData name="Builtin Variable" defStyleNum="dsDecVal" color="#c09050" selColor="#c09050" spellChecking="false" />
+ <itemData name="CMake Internal Variable" defStyleNum="dsVariable" spellChecking="false" />
+ <itemData name="Internal Name" defStyleNum="dsVariable" spellChecking="false" />
<itemData name="Variable Substitution" defStyleNum="dsDecVal" spellChecking="false" />
<itemData name="@Variable Substitution" defStyleNum="dsBaseN" spellChecking="false" />
- <itemData name="Internal Name" defStyleNum="dsDecVal" color="#303030" selColor="#303030" spellChecking="false" />
+ <itemData name="Cache Variable Substitution" defStyleNum="dsFloat" spellChecking="false" />
<itemData name="Environment Variable Substitution" defStyleNum="dsFloat" spellChecking="false" />
+ <itemData name="Standard Environment Variable" defStyleNum="dsFloat" spellChecking="false" />
<itemData name="Generator Expression Keyword" defStyleNum="dsKeyword" color="#b84040" selColor="#b84040" spellChecking="false" />
+ <itemData name="Generator Expression Sub-Command" defStyleNum="dsKeyword" color="#c05050" selColor="#c05050" spellChecking="false" />
<itemData name="Generator Expression" defStyleNum="dsOthers" color="#b86050" selColor="#b86050" spellChecking="false" />
- <itemData name="Comment" defStyleNum="dsComment" spellChecking="true" />
+ <itemData name="Standard Module" defStyleNum="dsImport" spellChecking="false" />
+ <itemData name="Deprecated Module" defStyleNum="dsImport" spellChecking="false" />
<itemData name="Region Marker" defStyleNum="dsRegionMarker" spellChecking="false" />
+ <itemData name="Error" defStyleNum="dsError" spellChecking="false" />
</itemDatas>
</highlighting>
<general>
<comments>
- <comment name="singleLine" start="#" />
+ <comment name="singleLine" start="#" position="afterwhitespace" />
+ <comment name="multiLine" start="#[[" end="]]" region="BracketedComment" />
</comments>
- <keywords casesensitive="1" />
+ <keywords casesensitive="1" weakDeliminator="." />
</general>
</language>
-<!-- kate: indent-width 2; tab-width 2; -->
+<!-- kate: replace-tabs on; indent-width 2; tab-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/cmake.yaml b/src/libs/3rdparty/syntax-highlighting/data/generators/cmake.yaml
index 1174c35215..e247237dda 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/generators/cmake.yaml
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/cmake.yaml
@@ -1,3 +1,5 @@
+version: 50
+
global-properties:
- ALLOW_DUPLICATE_CUSTOM_TARGETS
- AUTOGEN_SOURCE_GROUP
@@ -6,7 +8,9 @@ global-properties:
- AUTOMOC_TARGETS_FOLDER
- AUTORCC_SOURCE_GROUP
- CMAKE_C_KNOWN_FEATURES
+ - CMAKE_CUDA_KNOWN_FEATURES # Since 3.17
- CMAKE_CXX_KNOWN_FEATURES
+ - CMAKE_ROLE # Since 3.14
- DEBUG_CONFIGURATIONS
- DISABLED_FEATURES
- ENABLED_FEATURES
@@ -42,6 +46,7 @@ global-properties:
- FeatureSummary_<TYPE>_DESCRIPTION
directory-properties:
+ - ADDITIONAL_CLEAN_FILES # Since 3.15
- ADDITIONAL_MAKE_CLEAN_FILES
- BINARY_DIR
- BUILDSYSTEM_TARGETS
@@ -53,6 +58,7 @@ directory-properties:
- DEFINITIONS
- EXCLUDE_FROM_ALL
- IMPLICIT_DEPENDS_INCLUDE_TRANSFORM
+ - IMPORTED_TARGETS # Since 3.21
- INCLUDE_DIRECTORIES
- INCLUDE_REGULAR_EXPRESSION
- INTERPROCEDURAL_OPTIMIZATION_<CONFIG>
@@ -68,6 +74,7 @@ directory-properties:
- RULE_LAUNCH_LINK
- SOURCE_DIR
- SUBDIRECTORIES
+ - SYSTEM # Since 3.25
- TESTS # Since 3.12
- TEST_INCLUDE_FILE
- TEST_INCLUDE_FILES # Since 3.10
@@ -78,6 +85,8 @@ directory-properties:
# NOTE Copy-n-pasting this list from official docs may contain a redudant item `Example`! Check it!
target-properties:
+ - ADDITIONAL_CLEAN_FILES # Since 3.15
+ - AIX_EXPORT_ALL_SYMBOLS # Since 3.17
- ALIASED_TARGET
- ANDROID_ANT_ADDITIONAL_OPTIONS
- ANDROID_API
@@ -101,17 +110,26 @@ target-properties:
- ARCHIVE_OUTPUT_NAME_<CONFIG>
- ARCHIVE_OUTPUT_NAME
- AUTOGEN_BUILD_DIR
+ - AUTOGEN_ORIGIN_DEPENDS # Since 3.14
- AUTOGEN_TARGET_DEPENDS
+ - AUTOGEN_USE_SYSTEM_INCLUDE # Since 3.27
+ - AUTOMOC_COMPILER_PREDEFINES # Since 3.10
- AUTOMOC_DEPEND_FILTERS
+ - AUTOMOC_EXECUTABLE # Since 3.14
+ - AUTOMOC_MACRO_NAMES
- AUTOMOC_MOC_OPTIONS
+ - AUTOMOC_PATH_PREFIX # Since 3.16
- AUTOMOC
- AUTOUIC
+ - AUTOUIC_EXECUTABLE # Since 3.14
- AUTOUIC_OPTIONS
- AUTOUIC_SEARCH_PATHS
- AUTORCC
+ - AUTORCC_EXECUTABLE # Since 3.14
- AUTORCC_OPTIONS
- BINARY_DIR
- BUILD_RPATH
+ - BUILD_RPATH_USE_ORIGIN # Since 3.14
- BUILD_WITH_INSTALL_NAME_DIR
- BUILD_WITH_INSTALL_RPATH
- BUNDLE_EXTENSION
@@ -119,7 +137,7 @@ target-properties:
- C_EXTENSIONS
- C_STANDARD
- C_STANDARD_REQUIRED
- - COMMON_LANGUAGE_RUNTIME # Sine 3.12
+ - COMMON_LANGUAGE_RUNTIME # Since 3.12
- COMPATIBLE_INTERFACE_BOOL
- COMPATIBLE_INTERFACE_NUMBER_MAX
- COMPATIBLE_INTERFACE_NUMBER_MIN
@@ -132,38 +150,68 @@ target-properties:
- COMPILE_PDB_NAME_<CONFIG>
- COMPILE_PDB_OUTPUT_DIRECTORY
- COMPILE_PDB_OUTPUT_DIRECTORY_<CONFIG>
+ - COMPILE_WARNING_AS_ERROR # Since 3.24
- <CONFIG>_OUTPUT_NAME
- <CONFIG>_POSTFIX
- CROSSCOMPILING_EMULATOR
+ - CUDA_ARCHITECTURES # Since 3.18
+ - CUDA_CUBIN_COMPILATION # Since 3.27
+ - CUDA_EXTENSIONS
+ - CUDA_FATBIN_COMPILATION # Since 3.27
+ - CUDA_OPTIX_COMPILATION # Since 3.27
- CUDA_PTX_COMPILATION
- CUDA_SEPARABLE_COMPILATION
- CUDA_RESOLVE_DEVICE_SYMBOLS
- - CUDA_EXTENSIONS
+ - CUDA_RUNTIME_LIBRARY # Since 3.17
- CUDA_STANDARD
- CUDA_STANDARD_REQUIRED
- CXX_EXTENSIONS
+ - CXX_MODULE_DIRS # Since 3.28
+ - CXX_MODULE_DIRS_<NAME> # Since 3.28
+ - CXX_MODULE_SET # Since 3.28
+ - CXX_MODULE_SET_<NAME> # Since 3.28
+ - CXX_MODULE_SETS # Since 3.28
+ - CXX_SCAN_FOR_MODULES # Since 3.28
- CXX_STANDARD
- CXX_STANDARD_REQUIRED
# - DEBUG_POSTFIX # NOTE: Handled by `<CONFIG>_POSTFIX`
- DEFINE_SYMBOL
- DEPLOYMENT_ADDITIONAL_FILES # Since 3.13
- DEPLOYMENT_REMOTE_DIRECTORY
+ - DEPRECATION # Since 3.17
+ - DISABLE_PRECOMPILE_HEADERS # Since 3.16
+ - DLL_NAME_WITH_SOVERSION # Since 3.27
+ - DOTNET_SDK # Since 3.23
+ - DOTNET_TARGET_FRAMEWORK # Since 3.17
- DOTNET_TARGET_FRAMEWORK_VERSION # Since 3.12
- EchoString
- ENABLE_EXPORTS
- EXCLUDE_FROM_ALL
- EXCLUDE_FROM_DEFAULT_BUILD_<CONFIG>
- EXCLUDE_FROM_DEFAULT_BUILD
+ - EXPORT_COMPILE_COMMANDS # Since 3.20
- EXPORT_NAME
+ - EXPORT_NO_SYSTEM # Since 3.25
- EXPORT_PROPERTIES # Since 3.12
- FOLDER
- Fortran_FORMAT
- Fortran_MODULE_DIRECTORY
+ - Fortran_PREPROCESS # Since 3.18
- FRAMEWORK
+ - FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG> # Since 3.18
- FRAMEWORK_VERSION
- GENERATOR_FILE_NAME
- GNUtoMS
- HAS_CXX
+ - HEADER_DIRS # Since 3.23
+ - HEADER_DIRS_<NAME> # Since 3.23
+ - HEADER_SET # Since 3.23
+ - HEADER_SET_<NAME> # Since 3.23
+ - HEADER_SETS # Since 3.23
+ - HIP_ARCHITECTURES # Since 3.21
+ - HIP_EXTENSIONS # Since 3.21
+ - HIP_STANDARD # Since 3.21
+ - HIP_STANDARD_REQUIRED # Since 3.21
- IMPLICIT_DEPENDS_INCLUDE_TRANSFORM
- IMPORTED_COMMON_LANGUAGE_RUNTIME # Since 3.12
- IMPORTED_CONFIGURATIONS
@@ -184,6 +232,7 @@ target-properties:
- IMPORTED_LOCATION
- IMPORTED_NO_SONAME_<CONFIG>
- IMPORTED_NO_SONAME
+ - IMPORTED_NO_SYSTEM # Since 3.23
- IMPORTED_OBJECTS_<CONFIG>
- IMPORTED_OBJECTS
- IMPORTED
@@ -193,31 +242,44 @@ target-properties:
- IMPORT_SUFFIX
- INCLUDE_DIRECTORIES
- INSTALL_NAME_DIR
+ - INSTALL_REMOVE_ENVIRONMENT_RPATH # Since 3.16
- INSTALL_RPATH
- INSTALL_RPATH_USE_LINK_PATH
+ - INTERFACE_AUTOMOC_MACRO_NAMES # Since 3.27
- INTERFACE_AUTOUIC_OPTIONS
- INTERFACE_COMPILE_DEFINITIONS
- INTERFACE_COMPILE_FEATURES
- INTERFACE_COMPILE_OPTIONS
+ - INTERFACE_CXX_MODULE_SETS # Since 3.28
+ - INTERFACE_HEADER_SETS # Since 3.23
+ - INTERFACE_HEADER_SETS_TO_VERIFY # Since 3.24
- INTERFACE_INCLUDE_DIRECTORIES
- INTERFACE_LINK_DEPENDS # Since 3.13
- INTERFACE_LINK_DIRECTORIES # Since 3.13
- INTERFACE_LINK_LIBRARIES
+ - INTERFACE_LINK_LIBRARIES_DIRECT # Since 3.24
+ - INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE # Since 3.24
- INTERFACE_LINK_OPTIONS # Since 3.13
+ - INTERFACE_PRECOMPILE_HEADERS # Since 3.16
- INTERFACE_POSITION_INDEPENDENT_CODE
- INTERFACE_SOURCES
- INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
- INTERPROCEDURAL_OPTIMIZATION_<CONFIG>
- INTERPROCEDURAL_OPTIMIZATION
- IOS_INSTALL_COMBINED
+ - ISPC_HEADER_DIRECTORY # Since 3.19
+ - ISPC_HEADER_SUFFIX # Since 3.19.2
+ - ISPC_INSTRUCTION_SETS # Since 3.19
- JOB_POOL_COMPILE
- JOB_POOL_LINK
- LABELS
- <LANG>_CLANG_TIDY
+ - <LANG>_CLANG_TIDY_EXPORT_FIXES_DIR # Since 3.26
- <LANG>_COMPILER_LAUNCHER
- <LANG>_CPPCHECK # Since 3.10
- <LANG>_CPPLINT
- <LANG>_INCLUDE_WHAT_YOU_USE
+ - <LANG>_LINKER_LAUNCHER # Since 3.21
- <LANG>_VISIBILITY_PRESET
- LIBRARY_OUTPUT_DIRECTORY_<CONFIG>
- LIBRARY_OUTPUT_DIRECTORY
@@ -234,30 +296,48 @@ target-properties:
- LINK_INTERFACE_MULTIPLICITY_<CONFIG>
- LINK_INTERFACE_MULTIPLICITY
- LINK_LIBRARIES
+ - LINK_LIBRARIES_ONLY_TARGETS # Since 3.23
- LINK_OPTIONS # Since 3.13
- LINK_SEARCH_END_STATIC
- LINK_SEARCH_START_STATIC
- LINK_WHAT_YOU_USE
- LOCATION_<CONFIG>
- LOCATION
+ - MACHO_COMPATIBILITY_VERSION # Since 3.17
+ - MACHO_CURRENT_VERSION # Since 3.17
- MACOSX_BUNDLE_INFO_PLIST
- MACOSX_BUNDLE
- MACOSX_FRAMEWORK_INFO_PLIST
- MACOSX_RPATH
- MANUALLY_ADDED_DEPENDENCIES
- MAP_IMPORTED_CONFIG_<CONFIG>
+ - MSVC_DEBUG_INFORMATION_FORMAT # Since 3.25
+ - MSVC_RUNTIME_LIBRARY # Since 3.15
- NAME
- NO_SONAME
- NO_SYSTEM_FROM_IMPORTED
+ - OBJC_EXTENSIONS # Since 3.16
+ - OBJC_STANDARD # Since 3.16
+ - OBJC_STANDARD_REQUIRED # Since 3.16
+ - OBJCXX_EXTENSIONS # Since 3.16
+ - OBJCXX_STANDARD # Since 3.16
+ - OBJCXX_STANDARD_REQUIRED # Since 3.16
+ - OPTIMIZE_DEPENDENCIES # Since 3.19
- OSX_ARCHITECTURES_<CONFIG>
- OSX_ARCHITECTURES
+ - OSX_CURRENT_VERSION # Since 3.17
+ - OSX_COMPATIBILITY_VERSION # Since 3.17
- OUTPUT_NAME_<CONFIG>
- OUTPUT_NAME
+ - PCH_WARN_INVALID # Since 3.18
+ - PCH_INSTANTIATE_TEMPLATES # Since 3.19
- PDB_NAME_<CONFIG>
- PDB_NAME
- PDB_OUTPUT_DIRECTORY_<CONFIG>
- PDB_OUTPUT_DIRECTORY
- POSITION_INDEPENDENT_CODE
+ - PRECOMPILE_HEADERS # Since 3.16
+ - PRECOMPILE_HEADERS_REUSE_FROM # Since 3.16
- PREFIX
- PRIVATE_HEADER
- PROJECT_LABEL
@@ -278,7 +358,18 @@ target-properties:
- STATIC_LIBRARY_FLAGS
- STATIC_LIBRARY_OPTIONS # Since 3.13
- SUFFIX
+ - Swift_DEPENDENCIES_FILE # Since 3.15
+ - Swift_MODULE_DIRECTORY # Since 3.15
+ - Swift_MODULE_NAME # Since 3.15
+ - SYSTEM # Since 3.25
- TYPE
+ - UNITY_BUILD # Since 3.16
+ - UNITY_BUILD_BATCH_SIZE # Since 3.16
+ - UNITY_BUILD_CODE_AFTER_INCLUDE # Since 3.16
+ - UNITY_BUILD_CODE_BEFORE_INCLUDE # Since 3.16
+ - UNITY_BUILD_MODE # Since 3.18
+ - UNITY_BUILD_UNIQUE_ID # Since 3.20
+ - VERIFY_INTERFACE_HEADER_SETS # Since 3.24
- VERSION
- VISIBILITY_INLINES_HIDDEN
- VS_CONFIGURATION_TYPE
@@ -291,48 +382,75 @@ target-properties:
- VS_DOTNET_REFERENCEPROP_<refname>_TAG_<tagname> # Since 3.10
- VS_DOTNET_REFERENCES
- VS_DOTNET_REFERENCES_COPY_LOCAL
+ - VS_DOTNET_STARTUP_OBJECT # Since 3.24
- VS_DOTNET_TARGET_FRAMEWORK_VERSION
+ - VS_DOTNET_DOCUMENTATION_FILE # Since 3.17
+ - VS_DPI_AWARE # Since 3.16
- VS_GLOBAL_KEYWORD
- VS_GLOBAL_PROJECT_TYPES
- VS_GLOBAL_ROOTNAMESPACE
- VS_GLOBAL_<variable>
- VS_IOT_EXTENSIONS_VERSION
- VS_IOT_STARTUP_TASK
+ - VS_JUST_MY_CODE_DEBUGGING # Since 3.15
- VS_KEYWORD
- VS_MOBILE_EXTENSIONS_VERSION
+ - VS_NO_COMPILE_BATCHING
+ - VS_NO_SOLUTION_DEPLOY # Since 3.15
+ - VS_PACKAGE_REFERENCES # Since 3.15
+ - VS_PLATFORM_TOOLSET # Since 3.18
+ - VS_PROJECT_IMPORT # Since 3.15
- VS_SCC_AUXPATH
- VS_SCC_LOCALPATH
- VS_SCC_PROJECTNAME
- VS_SCC_PROVIDER
- VS_SDK_REFERENCES
+ - VS_SOLUTION_DEPLOY # Since 3.18
+ - VS_SOURCE_SETTINGS_<tool> # Since 3.18
- VS_USER_PROPS
- VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION
- VS_WINRT_COMPONENT
- VS_WINRT_EXTENSIONS
- VS_WINRT_REFERENCES
+ - WATCOM_RUNTIME_LIBRARY # Since 3.24
- WIN32_EXECUTABLE
- WINDOWS_EXPORT_ALL_SYMBOLS
- XCODE_ATTRIBUTE_<an-attribute>
+ - XCODE_EMBED_<type>_CODE_SIGN_ON_COPY # Since 3.20
+ - XCODE_EMBED_<type>_PATH # Since 3.20
+ - XCODE_EMBED_<type>_REMOVE_HEADERS_ON_COPY # Since 3.20
+ - XCODE_EMBED_<type> # Since 3.20
- XCODE_EXPLICIT_FILE_TYPE
+ - XCODE_GENERATE_SCHEME # Since 3.15
+ - XCODE_LINK_BUILD_PHASE_MODE # Since 3.19
- XCODE_PRODUCT_TYPE
- XCODE_SCHEME_ADDRESS_SANITIZER # Since 3.13
- XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN # Since 3.13
+ - XCODE_SCHEME_ARGUMENTS # Since 3.13
+ - XCODE_SCHEME_DEBUG_AS_ROOT # Since 3.15
+ - XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING # Since 3.16
+ - XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER # Since 3.13
+ - XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS # Since 3.13
+ - XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE # Since 3.13
+ - XCODE_SCHEME_ENABLE_GPU_API_VALIDATION # Since 3.25
+ - XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE # Since 3.23
+ - XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION # Since 3.25
+ - XCODE_SCHEME_EXECUTABLE # Since 3.13
+ - XCODE_SCHEME_ENVIRONMENT # Since 3.13
+ - XCODE_SCHEME_GUARD_MALLOC # Since 3.13
+ - XCODE_SCHEME_LAUNCH_CONFIGURATION # Since 3.25
+ - XCODE_SCHEME_LAUNCH_MODE # Since 3.25
+ - XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP # Since 3.13
+ - XCODE_SCHEME_MALLOC_GUARD_EDGES # Since 3.13
+ - XCODE_SCHEME_MALLOC_SCRIBBLE # Since 3.13
+ - XCODE_SCHEME_MALLOC_STACK # Since 3.13
- XCODE_SCHEME_THREAD_SANITIZER # Since 3.13
- XCODE_SCHEME_THREAD_SANITIZER_STOP # Since 3.13
- XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER # Since 3.13
- XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP # Since 3.13
- - XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER # Since 3.13
- - XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP # Since 3.13
- - XCODE_SCHEME_MALLOC_SCRIBBLE # Since 3.13
- - XCODE_SCHEME_MALLOC_GUARD_EDGES # Since 3.13
- - XCODE_SCHEME_GUARD_MALLOC # Since 3.13
+ - XCODE_SCHEME_WORKING_DIRECTORY # Since 3.1?
- XCODE_SCHEME_ZOMBIE_OBJECTS # Since 3.13
- - XCODE_SCHEME_MALLOC_STACK # Since 3.13
- - XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE # Since 3.13
- - XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS # Since 3.13
- - XCODE_SCHEME_EXECUTABLE # Since 3.13
- - XCODE_SCHEME_ARGUMENTS # Since 3.13
- - XCODE_SCHEME_ENVIRONMENT # Since 3.13
+ - XCODE_XCCONFIG # Since 3.24
- XCTEST
test-properties:
@@ -342,21 +460,27 @@ test-properties:
- DEPENDS
- DISABLED
- ENVIRONMENT
+ - ENVIRONMENT_MODIFICATION # Since 3.22
- FAIL_REGULAR_EXPRESSION
- FIXTURES_CLEANUP
- FIXTURES_REQUIRED
- FIXTURES_SETUP
+ - GENERATED_RESOURCE_SPEC_FILE # Since 3.28
- LABELS
- MEASUREMENT
- PASS_REGULAR_EXPRESSION
- PROCESSOR_AFFINITY # Since 3.12
- PROCESSORS
- REQUIRED_FILES
+ - RESOURCE_GROUPS # Since 3.16
- RESOURCE_LOCK
- RUN_SERIAL
+ - SKIP_REGULAR_EXPRESSION # Since 3.16
- SKIP_RETURN_CODE
- TIMEOUT
- TIMEOUT_AFTER_MATCH
+ - TIMEOUT_SIGNAL_GRACE_PERIOD # Since 3.27
+ - TIMEOUT_SIGNAL_NAME # Since 3.27
- WILL_FAIL
- WORKING_DIRECTORY
@@ -367,8 +491,10 @@ source-properties:
- COMPILE_DEFINITIONS
- COMPILE_FLAGS
- COMPILE_OPTIONS # Since 3.11
+ - CXX_SCAN_FOR_MODULES # Since 3.28
- EXTERNAL_OBJECT
- Fortran_FORMAT
+ - Fortran_PREPROCESS # Since 3.18
- GENERATED
- HEADER_FILE_ONLY
- INCLUDE_DIRECTORIES # Since 3.11
@@ -383,6 +509,11 @@ source-properties:
- SKIP_AUTOMOC
- SKIP_AUTORCC
- SKIP_AUTOUIC
+ - SKIP_LINTING # Since 3.27
+ - SKIP_PRECOMPILE_HEADERS # Since 3.16
+ - SKIP_UNITY_BUILD_INCLUSION # Since 3.16
+ - Swift_DEPENDENCIES_FILE # Since 3.15
+ - Swift_DIAGNOSTICS_FILE # Since 3.15
- SYMBOLIC
- VS_COPY_TO_OUT_DIR
- VS_CSHARP_<tagname>
@@ -390,6 +521,7 @@ source-properties:
- VS_DEPLOYMENT_LOCATION
- VS_INCLUDE_IN_VSIX
- VS_RESOURCE_GENERATOR
+ - VS_SETTINGS # Since 3.18
- VS_SHADER_DISABLE_OPTIMIZATIONS # Since 3.11
- VS_SHADER_ENABLE_DEBUG # Since 3.11
- VS_SHADER_ENTRYPOINT
@@ -423,69 +555,193 @@ install-properties:
- CPACK_WIX_ACL
generator-expressions:
+ # Conditional Expressions
+ - IF
- 0
- 1
- - AND
- - ANGLE-R
- BOOL
- - BUILD_INTERFACE
- - COMMA
- - COMPILE_FEATURES
- - COMPILE_LANGUAGE
- - CONFIG
- - CXX_COMPILER_ID
- - CXX_COMPILER_VERSION
- - C_COMPILER_ID
- - C_COMPILER_VERSION
+ # Logical Operators
+ - AND
+ - OR
+ - NOT
+ # String Comparisons
+ - STREQUAL
- EQUAL
- - GENEX_EVAL # Since 3.12
- - IF
- - INSTALL_INTERFACE
- - INSTALL_PREFIX
- - IN_LIST # Since 3.12
- - JOIN
- - LINK_ONLY
+ # Version Comparisons
+ - VERSION_LESS
+ - VERSION_GREATER
+ - VERSION_EQUAL
+ - VERSION_LESS_EQUAL
+ - VERSION_GREATER_EQUAL
+ # String Transformations
- LOWER_CASE
+ - UPPER_CASE
- MAKE_C_IDENTIFIER
- - NOT
- - OR
- - PLATFORM_ID
- - SEMICOLON
+ # List Expressions
+ # * List Comparisons
+ - IN_LIST # Since 3.12
+ - name: LIST # Since 3.27
+ subcommands:
+ # * List Queries
+ - LENGTH
+ - GET
+ - SUBLIST
+ - FIND
+ # * List Transformations
+ - JOIN
+ - APPEND
+ - PREPEND
+ - INSERT
+ - POP_BACK
+ - POP_FRONT
+ - REMOVE_ITEM
+ - REMOVE_AT
+ - REMOVE_DUPLICATES
+ - FILTER
+ - TRANSFORM
+ - FRANSFORM
+ # * List Ordering
+ - REVERSE
+ - SORT
+ - JOIN
+ - REMOVE_DUPLICATES # Since 3.15
+ - FILTER # Since 3.15
+ # Path Expressions
+ # * Path Comparisons
+ - PATH_EQUAL # Since 3.24
+ - name: PATH # Since 3.24
+ subcommands:
+ # * Path Queries
+ - HAS_ROOT_NAME
+ - HAS_ROOT_DIRECTORY
+ - HAS_ROOT_PATH
+ - HAS_FILENAME
+ - HAS_EXTENSION
+ - HAS_STEM
+ - HAS_RELATIVE_PART
+ - HAS_PARENT_PATH
+ - IS_ABSOLUTE
+ - IS_RELATIVE
+ - IS_PREFIX
+ # * Path Decomposition
+ - GET_ROOT_NAME
+ - GET_ROOT_DIRECTORY
+ - GET_ROOT_PATH
+ - GET_FILENAME
+ - GET_EXTENSION
+ - GET_STEM
+ - GET_RELATIVE_PART
+ - GET_PARENT_PATH
+ # * Path Transformations
+ - CMAKE_PATH
+ - APPEND
+ - REMOVE_FILENAME
+ - REPLACE_FILENAME
+ - REMOVE_EXTENSION
+ - REPLACE_EXTENSION
+ - NORMAL_PATH
+ - RELATIVE_PATH
+ - ABSOLUTE_PATH
+ # Shell Paths
- SHELL_PATH
- - STREQUAL
- - TARGET_BUNDLE_CONTENT_DIR
- - TARGET_BUNDLE_DIR
+ # Configuration Expressions
+ - CONFIG
+ - OUTPUT_CONFIG # Since 3.20
+ - COMMAND_CONFIG # Since 3.20
+ # Toolchain And Language Expressions
+ # * Platform
+ - PLATFORM_ID
+ # * Compiler Version
+ - C_COMPILER_VERSION
+ - CXX_COMPILER_VERSION
+ - CUDA_COMPILER_VERSION # Since 3.15
+ - OBJC_COMPILER_VERSION # Since 3.16
+ - OBJCXX_COMPILER_VERSION # Since 3.16
+ - Fortran_COMPILER_VERSION
+ - HIP_COMPILER_VERSION # Since 3.21
+ - ISPC_COMPILER_VERSION # Since 3.19
+ # * Compiler Language And ID
+ - C_COMPILER_ID
+ - CXX_COMPILER_ID
+ - CUDA_COMPILER_ID # Since 3.15
+ - OBJC_COMPILER_ID # Since 3.16
+ - OBJCXX_COMPILER_ID # Since 3.16
+ - Fortran_COMPILER_ID
+ - HIP_COMPILER_ID # Since 3.21
+ - ISPC_COMPILER_ID # Since 3.19
+ - COMPILE_LANGUAGE # Since 3.3
+ - COMPILE_LANG_AND_ID # Since 3.15
+ # * Compile Features
+ - COMPILE_FEATURES
+ # * Compile Context
+ - COMPILE_ONLY # Since 3.27
+ # * Linker Language And ID
+ - LINK_LANGUAGE # Since 3.18
+ - LINK_LANG_AND_ID # Since 3.18
+ # * Link Features
+ - LINK_LIBRARY # Since 3.24
+ - LINK_GROUP # Since 3.24
+ # * Link Context
+ - LINK_ONLY
+ - DEVICE_LINK # Since 3.18
+ - HOST_LINK # Since 3.18
+ # Target-Dependent Expressions
- TARGET_EXISTS # Since 3.12
+ - TARGET_NAME_IF_EXISTS # Since 3.12
+ - TARGET_NAME
+ - TARGET_PROPERTY
+ - TARGET_OBJECTS
+ - TARGET_POLICY
- TARGET_FILE
- - TARGET_FILE_DIR
+ - TARGET_FILE_BASE_NAME # Since 3.15
+ - TARGET_FILE_PREFIX # Since 3.15
+ - TARGET_FILE_SUFFIX # Since 3.15
- TARGET_FILE_NAME
- - TARGET_GENEX_EVAL # Since 3.12
+ - TARGET_FILE_DIR
+ - TARGET_IMPORT_FILE # Since 3.27
+ - TARGET_IMPORT_FILE_BASE_NAME # Since 3.27
+ - TARGET_IMPORT_FILE_PREFIX # Since 3.27
+ - TARGET_IMPORT_FILE_SUFFIX # Since 3.27
+ - TARGET_IMPORT_FILE_NAME # Since 3.27
+ - TARGET_IMPORT_FILE_DIR # Since 3.27
- TARGET_LINKER_FILE
- - TARGET_LINKER_FILE_DIR
+ - TARGET_LINKER_FILE_BASE_NAME # Since 3.15
+ - TARGET_LINKER_FILE_PREFIX # Since 3.15
+ - TARGET_LINKER_FILE_SUFFIX # Since 3.15
- TARGET_LINKER_FILE_NAME
- - TARGET_NAME
- - TARGET_NAME_IF_EXISTS # Since 3.12
- - TARGET_OBJECTS
- - TARGET_PDB_FILE
- - TARGET_PDB_FILE_DIR
- - TARGET_PDB_FILE_NAME
- - TARGET_POLICY
- - TARGET_PROPERTY
+ - TARGET_LINKER_FILE_DIR
- TARGET_SONAME_FILE
- - TARGET_SONAME_FILE_DIR
- TARGET_SONAME_FILE_NAME
- - UPPER_CASE
- - VERSION_EQUAL
- - VERSION_GREATER
- - VERSION_GREATER_EQUAL
- - VERSION_LESS
- - VERSION_LESS_EQUAL
+ - TARGET_SONAME_FILE_DIR
+ - TARGET_PDB_FILE
+ - TARGET_PDB_FILE_BASE_NAME # Since 3.15
+ - TARGET_PDB_FILE_NAME
+ - TARGET_PDB_FILE_DIR
+ - TARGET_BUNDLE_DIR_NAME # Since 3.24
+ - TARGET_BUNDLE_DIR
+ - TARGET_BUNDLE_CONTENT_DIR
+ - TARGET_RUNTIME_DLLS # Since 3.21
+ - TARGET_RUNTIME_DLL_DIRS # Since 3.27
+ # Export And Install Expressions
+ - INSTALL_INTERFACE
+ - BUILD_INTERFACE
+ - BUILD_LOCAL_INTERFACE # Since 3.26
+ - INSTALL_PREFIX
+ # Multi-level Expression Evaluation
+ - GENEX_EVAL # Since 3.12
+ - TARGET_GENEX_EVAL # Since 3.12
+ # Escaped Characters
+ - ANGLE-R
+ - COMMA
+ - SEMICOLON
+ # Deprecated Expressions
+ # - CONFIGURATION
variables:
- # Variables that Provide Information
+ # Variables that Provide Information
- CMAKE_AR
- CMAKE_ARGC
- - CMAKE_ARGV0
+ - CMAKE_ARGV<n>
- CMAKE_BINARY_DIR
- CMAKE_BUILD_TOOL
- CMAKE_CACHEFILE_DIR
@@ -498,31 +754,40 @@ variables:
- CMAKE_CROSSCOMPILING_EMULATOR
- CMAKE_CTEST_COMMAND
- CMAKE_CURRENT_BINARY_DIR
+ - CMAKE_CURRENT_FUNCTION # Since 3.17
+ - CMAKE_CURRENT_FUNCTION_LIST_DIR # Since 3.17
+ - CMAKE_CURRENT_FUNCTION_LIST_FILE # Since 3.17
+ - CMAKE_CURRENT_FUNCTION_LIST_LINE # Since 3.17
- CMAKE_CURRENT_LIST_DIR
- CMAKE_CURRENT_LIST_FILE
- CMAKE_CURRENT_LIST_LINE
- CMAKE_CURRENT_SOURCE_DIR
- CMAKE_DIRECTORY_LABELS # Since 3.10
- CMAKE_DL_LIBS
+ - CMAKE_DOTNET_SDK # Since 3.23
+ - CMAKE_DOTNET_TARGET_FRAMEWORK # Since 3.17
- CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION # Since 3.12
- CMAKE_EDIT_COMMAND
- CMAKE_EXECUTABLE_SUFFIX
- CMAKE_EXTRA_GENERATOR
- CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES
+ - CMAKE_FIND_DEBUG_MODE # Since 3.17
- CMAKE_FIND_PACKAGE_NAME
+ - CMAKE_FIND_PACKAGE_REDIRECTS_DIR # Since 3.24
- CMAKE_FIND_PACKAGE_SORT_DIRECTION
- CMAKE_FIND_PACKAGE_SORT_ORDER
- CMAKE_GENERATOR
- CMAKE_GENERATOR_INSTANCE # Since 3.11
+ - CMAKE_GENERATOR_NO_COMPILER_ENV # "Professional CMake" §17.4
- CMAKE_GENERATOR_PLATFORM
- CMAKE_GENERATOR_TOOLSET
- - CMAKE_HOME_DIRECTORY
- CMAKE_IMPORT_LIBRARY_PREFIX
- CMAKE_IMPORT_LIBRARY_SUFFIX
- CMAKE_JOB_POOL_COMPILE
- CMAKE_JOB_POOL_LINK
- CMAKE_JOB_POOLS # Since 3.11
- CMAKE_<LANG>_COMPILER_AR
+ - CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT # Since 3.14
- CMAKE_<LANG>_COMPILER_RANLIB
- CMAKE_LINK_LIBRARY_SUFFIX
- CMAKE_LINK_SEARCH_END_STATIC
@@ -531,6 +796,10 @@ variables:
- CMAKE_MAKE_PROGRAM
- CMAKE_MATCH_COUNT
- CMAKE_MATCH_<n>
+ - CMAKE_MESSAGE_CONTEXT # Since 3.17
+ - CMAKE_MESSAGE_CONTEXT_SHOW # Since 3.17
+ - CMAKE_MESSAGE_INDENT # Since 3.16
+ - CMAKE_MESSAGE_LOG_LEVEL # Since 3.17
- CMAKE_MINIMUM_REQUIRED_VERSION
- CMAKE_MINOR_VERSION
- CMAKE_NETRC # Since 3.11
@@ -558,22 +827,35 @@ variables:
- CMAKE_SOURCE_DIR
- CMAKE_STATIC_LIBRARY_PREFIX
- CMAKE_STATIC_LIBRARY_SUFFIX
+ - CMAKE_Swift_MODULE_DIRECTORY # Since 3.15
- CMAKE_TOOLCHAIN_FILE
- CMAKE_TWEAK_VERSION
- CMAKE_VERBOSE_MAKEFILE
- CMAKE_VERSION
- CMAKE_VS_DEVENV_COMMAND
- - CMAKE_VS_INTEL_Fortran_PROJECT_VERSION
- CMAKE_VS_MSBUILD_COMMAND
- CMAKE_VS_NsightTegra_VERSION
+ - CMAKE_VS_NUGET_PACKAGE_RESTORE # Since 3.23
- CMAKE_VS_PLATFORM_NAME
+ - CMAKE_VS_PLATFORM_NAME_DEFAULT # Since 3.14.3
- CMAKE_VS_PLATFORM_TOOLSET
- CMAKE_VS_PLATFORM_TOOLSET_CUDA
- - CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE
+ - CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR # Since 3.16
+ - CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE # Since 3.8
+ - CMAKE_VS_PLATFORM_TOOLSET_VERSION # Since 3.12
+ - CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER # Since 3.22
+ - CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION # Since 3.22
+ - CMAKE_VS_TARGET_FRAMEWORK_VERSION # Since 3.22
+ - CMAKE_VS_VERSION_BUILD_NUMBER # Since 3.26
+ - CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION # Since 3.27
- CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION
- - CMAKE_XCODE_GENERATE_SCHEME
+ - CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM # Since 3.19
+ - CMAKE_XCODE_BUILD_SYSTEM # Since 3.19
- CMAKE_XCODE_PLATFORM_TOOLSET
- <PROJECT-NAME>_BINARY_DIR
+ - <PROJECT-NAME>_DESCRIPTION # Since 3.12
+ - <PROJECT-NAME>_IS_TOP_LEVEL # Since 3.21
+ - <PROJECT-NAME>_HOMEPAGE_URL # Since 3.12
- <PROJECT-NAME>_SOURCE_DIR
- <PROJECT-NAME>_VERSION
- <PROJECT-NAME>_VERSION_MAJOR
@@ -581,7 +863,8 @@ variables:
- <PROJECT-NAME>_VERSION_PATCH
- <PROJECT-NAME>_VERSION_TWEAK
- PROJECT_BINARY_DIR
- - PROJECT_DESCRIPTION
+ - PROJECT_DESCRIPTION # Since 3.9
+ - PROJECT_IS_TOP_LEVEL # Since 3.21
- PROJECT_HOMEPAGE_URL # Since 3.12
- PROJECT_NAME
- PROJECT_SOURCE_DIR
@@ -593,13 +876,16 @@ variables:
# Variables that Change Behavior
- BUILD_SHARED_LIBS
- CMAKE_ABSOLUTE_DESTINATION_FILES
+ - CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY # Since 3.27
- CMAKE_APPBUNDLE_PATH
- CMAKE_AUTOMOC_RELAXED_MODE
- CMAKE_BACKWARDS_COMPATIBILITY
- CMAKE_BUILD_TYPE
+ - CMAKE_CLANG_VFS_OVERLAY # Since 3.19
- CMAKE_CODEBLOCKS_COMPILER_ID # Since 3.11
- CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES # Since 3.10
- CMAKE_CODELITE_USE_TARGETS
+ - CMAKE_COLOR_DIAGNOSTICS # Since 3.24
- CMAKE_COLOR_MAKEFILE
- CMAKE_CONFIGURATION_TYPES
- CMAKE_DEBUG_TARGET_PROPERTIES
@@ -608,10 +894,13 @@ variables:
- CMAKE_ECLIPSE_GENERATE_LINKED_RESOURCES
- CMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT
- CMAKE_ECLIPSE_MAKE_ARGUMENTS
+ - CMAKE_ECLIPSE_RESOURCE_ENCODING # Since 3.16
- CMAKE_ECLIPSE_VERSION
- CMAKE_ERROR_DEPRECATED
- CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION
+ - CMAKE_EXECUTE_PROCESS_COMMAND_ECHO # Since 3.15
- CMAKE_EXPORT_COMPILE_COMMANDS
+ - CMAKE_EXPORT_PACKAGE_REGISTRY # Since 3.15
- CMAKE_EXPORT_NO_PACKAGE_REGISTRY
- CMAKE_FIND_APPBUNDLE
- CMAKE_FIND_FRAMEWORK
@@ -619,16 +908,28 @@ variables:
- CMAKE_FIND_LIBRARY_PREFIXES
- CMAKE_FIND_LIBRARY_SUFFIXES
- CMAKE_FIND_NO_INSTALL_PREFIX
- - CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
+ - CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY # Deprecated since 3.16
- CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY
+ - CMAKE_FIND_PACKAGE_PREFER_CONFIG # Since 3.15
+ - CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS # Since 3.14
+ - CMAKE_FIND_PACKAGE_TARGETS_GLOBAL # Since 3.24
- CMAKE_FIND_PACKAGE_WARN_NO_MODULE
- CMAKE_FIND_ROOT_PATH
- CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
- CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
- CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
- CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
+ - CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH # Since 3.16
+ - CMAKE_FIND_USE_CMAKE_PATH # Since 3.16
+ - CMAKE_FIND_USE_CMAKE_SYSTEM_PATH # Since 3.16
+ - CMAKE_FIND_USE_INSTALL_PREFIX # Since 3.24
+ - CMAKE_FIND_USE_PACKAGE_ROOT_PATH # Since 3.16
+ - CMAKE_FIND_USE_PACKAGE_REGISTRY # Since 3.16
+ - CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH # Since 3.16
+ - CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY # Since 3.16
- CMAKE_FRAMEWORK_PATH
- CMAKE_IGNORE_PATH
+ - CMAKE_IGNORE_PREFIX_PATH # Since 3.23
- CMAKE_INCLUDE_DIRECTORIES_BEFORE
- CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE
- CMAKE_INCLUDE_PATH
@@ -639,14 +940,20 @@ variables:
- CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT
- CMAKE_LIBRARY_PATH
- CMAKE_LINK_DIRECTORIES_BEFORE # Since 3.13
+ - CMAKE_LINK_LIBRARIES_ONLY_TARGETS # Since 3.23
+ - CMAKE_MAXIMUM_RECURSION_DEPTH # Since 3.14
- CMAKE_MFC_FLAG
- CMAKE_MODULE_PATH
- - CMAKE_NOT_USING_CONFIG_FLAGS
- CMAKE_POLICY_DEFAULT_CMP<NNNN>
- CMAKE_POLICY_WARNING_CMP<NNNN>
- CMAKE_PREFIX_PATH
- CMAKE_PROGRAM_PATH
+ - CMAKE_PROJECT_INCLUDE # Since 3.15
+ - CMAKE_PROJECT_INCLUDE_BEFORE # Since 3.15
- CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE
+ - CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE # Since 3.17
+ - CMAKE_PROJECT_TOP_LEVEL_INCLUDES # Since 3.24
+ - CMAKE_REQUIRE_FIND_PACKAGE_<PackageName> # Since 3.22
- CMAKE_SKIP_INSTALL_ALL_DEPENDENCY
- CMAKE_STAGING_PREFIX
- CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS
@@ -658,6 +965,7 @@ variables:
- CMAKE_SYSTEM_APPBUNDLE_PATH
- CMAKE_SYSTEM_FRAMEWORK_PATH
- CMAKE_SYSTEM_IGNORE_PATH
+ - CMAKE_SYSTEM_IGNORE_PREFIX_PATH # Since 3.23
- CMAKE_SYSTEM_INCLUDE_PATH
- CMAKE_SYSTEM_LIBRARY_PATH
- CMAKE_SYSTEM_PREFIX_PATH
@@ -665,31 +973,44 @@ variables:
- CMAKE_USER_MAKE_RULES_OVERRIDE
- CMAKE_WARN_DEPRECATED
- CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION
+ - CMAKE_XCODE_GENERATE_SCHEME
- CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY # Since 3.13
+ - CMAKE_XCODE_LINK_BUILD_PHASE_MODE # Since 3.19
- CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER # Since 3.13
- CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN # Since 3.13
+ - CMAKE_XCODE_SCHEME_WORKING_DIRECTORY # Since 3.17
+ - CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING # Since 3.16
+ - CMAKE_XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER # Since 3.13
+ - CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE # Since 3.13
+ - CMAKE_XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS # Since 3.13
+ - CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION # Since 3.25
+ - CMAKE_XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE # Since 3.23
+ - CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION # Since 3.25
+ - CMAKE_XCODE_SCHEME_ENVIRONMENT # Since 3.17
+ - CMAKE_XCODE_SCHEME_GUARD_MALLOC # Since 3.13
+ - CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION # Since 3.25
+ - CMAKE_XCODE_SCHEME_LAUNCH_MODE # Since 3.25
+ - CMAKE_XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP # Since 3.13
+ - CMAKE_XCODE_SCHEME_MALLOC_GUARD_EDGES # Since 3.13
+ - CMAKE_XCODE_SCHEME_MALLOC_SCRIBBLE # Since 3.13
+ - CMAKE_XCODE_SCHEME_MALLOC_STACK # Since 3.13
- CMAKE_XCODE_SCHEME_THREAD_SANITIZER # Since 3.13
- CMAKE_XCODE_SCHEME_THREAD_SANITIZER_STOP # Since 3.13
- CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER # Since 3.13
- CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP # Since 3.13
- - CMAKE_XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER # Since 3.13
- - CMAKE_XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP # Since 3.13
- - CMAKE_XCODE_SCHEME_MALLOC_SCRIBBLE # Since 3.13
- - CMAKE_XCODE_SCHEME_MALLOC_GUARD_EDGES # Since 3.13
- - CMAKE_XCODE_SCHEME_GUARD_MALLOC # Since 3.13
- CMAKE_XCODE_SCHEME_ZOMBIE_OBJECTS # Since 3.13
- - CMAKE_XCODE_SCHEME_MALLOC_STACK # Since 3.13
- - CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE # Since 3.13
- - CMAKE_XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS # Since 3.13
- - CMAKE_SUPPRESS_DEVELOPER_WARNINGS # Undocumented yet (CMake <= 3.10)
- - CMAKE_SUPPRESS_DEVELOPER_ERRORS # Undocumented yet (CMake <= 3.10)
+ - CMAKE_XCODE_XCCONFIG # Since 3.24
# Variables that Describe the System
- ANDROID
- APPLE
- BORLAND
+ - BSD # Since 3.25
+ - CMAKE_ANDROID_NDK_VERSION # Since 3.20
- CMAKE_CL_64
- CMAKE_COMPILER_2005
- CMAKE_HOST_APPLE
+ - CMAKE_HOST_BSD # Since 3.25
+ - CMAKE_HOST_LINUX # Since 3.25
- CMAKE_HOST_SOLARIS
- CMAKE_HOST_SYSTEM
- CMAKE_HOST_SYSTEM_NAME
@@ -707,7 +1028,9 @@ variables:
- CYGWIN
# NOTE `ENV` not a variable in fact!
# - ENV
- - GHS-MULTI
+ - GHSMULTI
+ - IOS # Since 3.14
+ - LINUX # Since 3.25
- MINGW
- MSVC
- MSVC10
@@ -730,6 +1053,7 @@ variables:
- XCODE
- XCODE_VERSION
# Variables that Control the Build
+ - CMAKE_AIX_EXPORT_ALL_SYMBOLS # Since 3.17
- CMAKE_ANDROID_ANT_ADDITIONAL_OPTIONS
- CMAKE_ANDROID_API
- CMAKE_ANDROID_API_MIN
@@ -755,58 +1079,99 @@ variables:
- CMAKE_ANDROID_SKIP_ANT_STEP
- CMAKE_ANDROID_STANDALONE_TOOLCHAIN
- CMAKE_ANDROID_STL_TYPE
+ - CMAKE_APPLE_SILICON_PROCESSOR # Since 3.19.2
- CMAKE_ARCHIVE_OUTPUT_DIRECTORY
- CMAKE_ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>
+ - CMAKE_AUTOGEN_ORIGIN_DEPENDS # Since 3.14
- CMAKE_AUTOGEN_PARALLEL
+ - CMAKE_AUTOGEN_USE_SYSTEM_INCLUDE # Since 3.27
- CMAKE_AUTOGEN_VERBOSE # Since 3.13
- CMAKE_AUTOMOC
- CMAKE_AUTOMOC_DEPEND_FILTERS
- CMAKE_AUTOMOC_MOC_OPTIONS
+ - CMAKE_AUTOMOC_PATH_PREFIX # Since 3.16
+ - CMAKE_AUTOMOC_EXECUTABLE # Since 3.27
- CMAKE_AUTORCC
- CMAKE_AUTORCC_OPTIONS
+ - CMAKE_AUTORCC_EXECUTABLE # Since 3.27
- CMAKE_AUTOUIC
- CMAKE_AUTOUIC_OPTIONS
- CMAKE_AUTOUIC_SEARCH_PATHS
+ - CMAKE_AUTOUIC_EXECUTABLE # Since 3.27
- CMAKE_BUILD_RPATH
+ - CMAKE_BUILD_RPATH_USE_ORIGIN # Since 3.14
- CMAKE_BUILD_WITH_INSTALL_NAME_DIR
- CMAKE_BUILD_WITH_INSTALL_RPATH
- CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY
- CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY_<CONFIG>
+ - CMAKE_COMPILE_WARNING_AS_ERROR # Since 3.24
- CMAKE_<CONFIG>_POSTFIX
+ - CMAKE_CROSS_CONFIGS # Since 3.17
+ - CMAKE_CTEST_ARGUMENTS # Since 3.17
+ - CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS # Since 3.16
+ - CMAKE_CUDA_RUNTIME_LIBRARY # Since 3.17
- CMAKE_CUDA_SEPARABLE_COMPILATION # Since 3.11
+ - CMAKE_CXX_SCAN_FOR_MODULES # Since 3.28
- CMAKE_DEBUG_POSTFIX
- - CMAKE_ENABLE_EXPORTS
+ - CMAKE_DEFAULT_BUILD_TYPE # Since 3.17
+ - CMAKE_DEFAULT_CONFIGS # Since 3.17
+ - CMAKE_DISABLE_PRECOMPILE_HEADERS # Since 3.17
+ - CMAKE_DLL_NAME_WITH_SOVERSION # Since 3.27
+ # `CMAKE_ENABLE_EXPORTS` has been moved to deprecated section
+ - CMAKE_EXECUTABLE_ENABLE_EXPORTS # Since 3.27
- CMAKE_EXE_LINKER_FLAGS
- CMAKE_EXE_LINKER_FLAGS_<CONFIG>
- CMAKE_EXE_LINKER_FLAGS_<CONFIG>_INIT
- CMAKE_EXE_LINKER_FLAGS_INIT
- CMAKE_FOLDER # Since 3.12
+ - CMAKE_FRAMEWORK # Since 3.15
+ - CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG> # Since 3.18
- CMAKE_Fortran_FORMAT
- CMAKE_Fortran_MODULE_DIRECTORY
+ - CMAKE_Fortran_PREPROCESS # Since 3.18
+ - CMAKE_GLOBAL_AUTOGEN_TARGET # Since 3.14
+ - CMAKE_GLOBAL_AUTOGEN_TARGET_NAME # Since 3.14
+ - CMAKE_GLOBAL_AUTORCC_TARGET # Since 3.14
+ - CMAKE_GLOBAL_AUTORCC_TARGET_NAME # Since 3.14
- CMAKE_GNUtoMS
- CMAKE_INCLUDE_CURRENT_DIR
- CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE
- CMAKE_INSTALL_NAME_DIR
+ - CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH # Since 3.16
- CMAKE_INSTALL_RPATH
- CMAKE_INSTALL_RPATH_USE_LINK_PATH
- CMAKE_INTERPROCEDURAL_OPTIMIZATION
- CMAKE_INTERPROCEDURAL_OPTIMIZATION_<CONFIG>
- - CMAKE_IOS_INSTALL_COMBINED
- CMAKE_<LANG>_CLANG_TIDY
+ - CMAKE_<LANG>_CLANG_TIDY_EXPORT_FIXES_DIR # Since 3.26
- CMAKE_<LANG>_COMPILER_LAUNCHER
- CMAKE_<LANG>_CPPCHECK # Since 3.10
- CMAKE_<LANG>_CPPLINT
- CMAKE_<LANG>_INCLUDE_WHAT_YOU_USE
+ - CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE> # Since 3.24
+ - CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED # Since 3.24
+ - CMAKE_<LANG>_LINKER_LAUNCHER # Since 3.21
+ - CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE> # Since 3.24
+ - CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED # Since 3.24
+ - CMAKE_<LANG>_LINK_LIBRARY_FILE_FLAG # Since 3.16
+ - CMAKE_<LANG>_LINK_LIBRARY_FLAG # Since 3.16
+ - CMAKE_<LANG>_LINK_WHAT_YOU_USE_FLAG # Since 3.22
- CMAKE_<LANG>_VISIBILITY_PRESET
- CMAKE_LIBRARY_OUTPUT_DIRECTORY
- CMAKE_LIBRARY_OUTPUT_DIRECTORY_<CONFIG>
- CMAKE_LIBRARY_PATH_FLAG
- CMAKE_LINK_DEF_FILE_FLAG
- CMAKE_LINK_DEPENDS_NO_SHARED
+ - CMAKE_LINK_DEPENDS_USE_LINKER # Since 3.27
+ - CMAKE_LINK_GROUP_USING_<FEATURE> # Since 3.24
+ - CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED # Since 3.24
- CMAKE_LINK_INTERFACE_LIBRARIES
- CMAKE_LINK_LIBRARY_FILE_FLAG
- CMAKE_LINK_LIBRARY_FLAG
+ - CMAKE_LINK_LIBRARY_USING_<FEATURE> # Since 3.24
+ - CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED # Since 3.24
- CMAKE_LINK_WHAT_YOU_USE
+ - CMAKE_LINK_WHAT_YOU_USE_CHECK # Since 3.22
- CMAKE_MACOSX_BUNDLE
- CMAKE_MACOSX_RPATH
- CMAKE_MAP_IMPORTED_CONFIG_<CONFIG>
@@ -814,17 +1179,25 @@ variables:
- CMAKE_MODULE_LINKER_FLAGS_<CONFIG>
- CMAKE_MODULE_LINKER_FLAGS_<CONFIG>_INIT
- CMAKE_MODULE_LINKER_FLAGS_INIT
+ - CMAKE_MSVC_DEBUG_INFORMATION_FORMAT # Since 3.25
+ - CMAKE_MSVC_RUNTIME_LIBRARY # Since 3.15
+ - CMAKE_MSVCIDE_RUN_PATH # Since 3.15
- CMAKE_NINJA_OUTPUT_PATH_PREFIX
- CMAKE_NO_BUILTIN_CHRPATH
- CMAKE_NO_SYSTEM_FROM_IMPORTED
+ - CMAKE_OPTIMIZE_DEPENDENCIES # Since 3.19
- CMAKE_OSX_ARCHITECTURES
- CMAKE_OSX_DEPLOYMENT_TARGET
- CMAKE_OSX_SYSROOT
+ - CMAKE_PCH_WARN_INVALID # Since 3.18
+ - CMAKE_PCH_INSTANTIATE_TEMPLATES # Since 3.19
- CMAKE_PDB_OUTPUT_DIRECTORY
- CMAKE_PDB_OUTPUT_DIRECTORY_<CONFIG>
+ - CMAKE_PLATFORM_NO_VERSIONED_SONAME # Since 3.1
- CMAKE_POSITION_INDEPENDENT_CODE
- CMAKE_RUNTIME_OUTPUT_DIRECTORY
- CMAKE_RUNTIME_OUTPUT_DIRECTORY_<CONFIG>
+ - CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS # Since 3.27
- CMAKE_SHARED_LINKER_FLAGS
- CMAKE_SHARED_LINKER_FLAGS_<CONFIG>
- CMAKE_SHARED_LINKER_FLAGS_<CONFIG>_INIT
@@ -835,14 +1208,24 @@ variables:
- CMAKE_STATIC_LINKER_FLAGS_<CONFIG>
- CMAKE_STATIC_LINKER_FLAGS_<CONFIG>_INIT
- CMAKE_STATIC_LINKER_FLAGS_INIT
+ - CMAKE_TASKING_TOOLSET # Since 3.25
- CMAKE_TRY_COMPILE_CONFIGURATION
+ - CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES # Since 3.24
- CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
- CMAKE_TRY_COMPILE_TARGET_TYPE
+ - CMAKE_UNITY_BUILD # Since 3.16
+ - CMAKE_UNITY_BUILD_BATCH_SIZE # Since 3.16
- CMAKE_USE_RELATIVE_PATHS
+ - CMAKE_VERIFY_INTERFACE_HEADER_SETS # Since 3.24
- CMAKE_VISIBILITY_INLINES_HIDDEN
+ - CMAKE_VS_DEBUGGER_COMMAND # Since 3.27
+ - CMAKE_VS_DEBUGGER_COMMAND_ARGUMENTS # Since 3.27
+ - CMAKE_VS_DEBUGGER_ENVIRONMENT # Since 3.27
+ - CMAKE_VS_DEBUGGER_WORKING_DIRECTORY # Since 3.27
- CMAKE_VS_GLOBALS # Since 3.13
- CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD
- CMAKE_VS_INCLUDE_PACKAGE_TO_DEFAULT_BUILD
+ - CMAKE_VS_JUST_MY_CODE_DEBUGGING # Since 3.15
- CMAKE_VS_SDK_EXCLUDE_DIRECTORIES # Since 3.12
- CMAKE_VS_SDK_EXECUTABLE_DIRECTORIES # Since 3.12
- CMAKE_VS_SDK_INCLUDE_DIRECTORIES # Since 3.12
@@ -850,6 +1233,8 @@ variables:
- CMAKE_VS_SDK_LIBRARY_WINRT_DIRECTORIES # Since 3.12
- CMAKE_VS_SDK_REFERENCE_DIRECTORIES # Since 3.12
- CMAKE_VS_SDK_SOURCE_DIRECTORIES # Since 3.12
+ - CMAKE_VS_WINRT_BY_DEFAULT # Since 3.13
+ - CMAKE_WATCOM_RUNTIME_LIBRARY # Since 3.24
- CMAKE_WIN32_EXECUTABLE
- CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
- CMAKE_XCODE_ATTRIBUTE_<an-attribute>
@@ -859,6 +1244,9 @@ variables:
- CMAKE_COMPILER_IS_GNUCC
- CMAKE_COMPILER_IS_GNUCXX
- CMAKE_COMPILER_IS_GNUG77
+ - CMAKE_CUDA_ARCHITECTURES # Since 3.18
+ - CMAKE_CUDA_COMPILE_FEATURES # Since 3.17
+ - CMAKE_CUDA_HOST_COMPILER # Since 3.17
- CMAKE_CUDA_EXTENSIONS
- CMAKE_CUDA_STANDARD
- CMAKE_CUDA_STANDARD_REQUIRED
@@ -874,15 +1262,22 @@ variables:
- CMAKE_Fortran_MODDIR_DEFAULT
- CMAKE_Fortran_MODDIR_FLAG
- CMAKE_Fortran_MODOUT_FLAG
- - CMAKE_INTERNAL_PLATFORM_ABI
+ - CMAKE_HIP_ARCHITECTURES # Since 3.21
+ - CMAKE_HIP_EXTENSIONS # Since 3.21
+ - CMAKE_HIP_PLATFORM # Since 3.28
+ - CMAKE_HIP_STANDARD # Since 3.21
+ - CMAKE_HIP_STANDARD_REQUIRED # Since 3.21
+ - CMAKE_ISPC_HEADER_DIRECTORY # Since 3.19
+ - CMAKE_ISPC_HEADER_SUFFIX # Since 3.19.2
+ - CMAKE_ISPC_INSTRUCTION_SETS # Since 3.19
- CMAKE_<LANG>_ANDROID_TOOLCHAIN_MACHINE
- CMAKE_<LANG>_ANDROID_TOOLCHAIN_PREFIX
- CMAKE_<LANG>_ANDROID_TOOLCHAIN_SUFFIX
- CMAKE_<LANG>_ARCHIVE_APPEND
- CMAKE_<LANG>_ARCHIVE_CREATE
- CMAKE_<LANG>_ARCHIVE_FINISH
+ - CMAKE_<LANG>_BYTE_ORDER # Since 3.20
- CMAKE_<LANG>_COMPILER
- - CMAKE_<LANG>_COMPILER_ABI
- CMAKE_<LANG>_COMPILER_EXTERNAL_TOOLCHAIN
- CMAKE_<LANG>_COMPILER_ID
- CMAKE_<LANG>_COMPILER_LOADED
@@ -892,7 +1287,11 @@ variables:
- CMAKE_<LANG>_CREATE_SHARED_LIBRARY
- CMAKE_<LANG>_CREATE_SHARED_MODULE
- CMAKE_<LANG>_CREATE_STATIC_LIBRARY
+ - CMAKE_<LANG>_EXTENSIONS
+ - CMAKE_<LANG>_EXTENSIONS_DEFAULT # Since 3.22
- CMAKE_<LANG>_FLAGS
+ - CMAKE_<LANG>_FLAGS_<CONFIG>
+ - CMAKE_<LANG>_FLAGS_<CONFIG>_INIT
- CMAKE_<LANG>_FLAGS_DEBUG
- CMAKE_<LANG>_FLAGS_DEBUG_INIT
- CMAKE_<LANG>_FLAGS_INIT
@@ -918,13 +1317,18 @@ variables:
- CMAKE_<LANG>_LINKER_WRAPPER_FLAG_SEP # Since 3.13
- CMAKE_<LANG>_LINK_EXECUTABLE
- CMAKE_<LANG>_OUTPUT_EXTENSION
- - CMAKE_<LANG>_PLATFORM_ID
- CMAKE_<LANG>_SIMULATE_ID
- CMAKE_<LANG>_SIMULATE_VERSION
- CMAKE_<LANG>_SIZEOF_DATA_PTR
- CMAKE_<LANG>_SOURCE_FILE_EXTENSIONS
- CMAKE_<LANG>_STANDARD_INCLUDE_DIRECTORIES
- CMAKE_<LANG>_STANDARD_LIBRARIES
+ - CMAKE_OBJC_EXTENSIONS # Since 3.16
+ - CMAKE_OBJC_STANDARD # Since 3.16
+ - CMAKE_OBJC_STANDARD_REQUIRED # Since 3.16
+ - CMAKE_OBJCXX_EXTENSIONS # Since 3.16
+ - CMAKE_OBJCXX_STANDARD # Since 3.16
+ - CMAKE_OBJCXX_STANDARD_REQUIRED # Since 3.16
- CMAKE_Swift_LANGUAGE_VERSION
- CMAKE_USER_MAKE_RULES_OVERRIDE_<LANG>
# Variables for CTest
@@ -955,6 +1359,7 @@ variables:
- CTEST_CUSTOM_PRE_MEMCHECK
- CTEST_CUSTOM_PRE_TEST
- CTEST_CUSTOM_TEST_IGNORE
+ - CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION # Since 3.24
- CTEST_CUSTOM_WARNING_EXCEPTION
- CTEST_CUSTOM_WARNING_MATCH
- CTEST_CVS_CHECKOUT
@@ -984,8 +1389,12 @@ variables:
- CTEST_P4_COMMAND
- CTEST_P4_OPTIONS
- CTEST_P4_UPDATE_OPTIONS
+ - CTEST_RESOURCE_SPEC_FILE # Since 3.18
+ - CTEST_RUN_CURRENT_SCRIPT # Since 3.11
- CTEST_SCP_COMMAND
- CTEST_SITE
+ - CTEST_SUBMIT_INACTIVITY_TIMEOUT # Since 3.23
+ - CTEST_SUBMIT_URL # Since 3.14
- CTEST_SOURCE_DIRECTORY
- CTEST_SVN_COMMAND
- CTEST_SVN_OPTIONS
@@ -996,52 +1405,101 @@ variables:
- CTEST_UPDATE_COMMAND
- CTEST_UPDATE_OPTIONS
- CTEST_UPDATE_VERSION_ONLY
+ - CTEST_UPDATE_VERSION_OVERRIDE # Since 3.15
- CTEST_USE_LAUNCHERS
# Variables for CPack
- CPACK_ABSOLUTE_DESTINATION_FILES
- CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY
+ - CPACK_CUSTOM_INSTALL_VARIABLES # Since 3.21
- CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION
- CPACK_INCLUDE_TOPLEVEL_DIRECTORY
- CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS # Since 3.11
- - CPACK_INSTALL_SCRIPT
+ - CPACK_INSTALL_SCRIPTS # Since 3.16
- CPACK_PACKAGING_INSTALL_PREFIX
- CPACK_SET_DESTDIR
- CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION
# Variables for `find_package()`
- PACKAGE_FIND_NAME
- - PACKAGE_FIND_VERSION
- - PACKAGE_FIND_VERSION_MAJOR
- - PACKAGE_FIND_VERSION_MINOR
- - PACKAGE_FIND_VERSION_PATCH
- - PACKAGE_FIND_VERSION_TWEAK
+ # NOTE <SMTH>_VERSION and components already defined above, so skipped here
- PACKAGE_FIND_VERSION_COUNT
+ - PACKAGE_FIND_VERSION_RANGE
+ - PACKAGE_FIND_VERSION_RANGE_MIN
+ - PACKAGE_FIND_VERSION_RANGE_MAX
+ - PACKAGE_FIND_VERSION_MIN
+ - PACKAGE_FIND_VERSION_MIN_MAJOR
+ - PACKAGE_FIND_VERSION_MIN_MINOR
+ - PACKAGE_FIND_VERSION_MIN_PATCH
+ - PACKAGE_FIND_VERSION_MIN_TWEAK
+ - PACKAGE_FIND_VERSION_MIN_COUNT
+ - PACKAGE_FIND_VERSION_MAX
+ - PACKAGE_FIND_VERSION_MAX_MAJOR
+ - PACKAGE_FIND_VERSION_MAX_MINOR
+ - PACKAGE_FIND_VERSION_MAX_PATCH
+ - PACKAGE_FIND_VERSION_MAX_TWEAK
+ - PACKAGE_FIND_VERSION_MAX_COUNT
+ - PACKAGE_FIND_VERSION_COMPLETE
- PACKAGE_VERSION
- PACKAGE_VERSION_EXACT
- PACKAGE_VERSION_COMPATIBLE
- PACKAGE_VERSION_UNSUITABLE
- # NOTE <SMTH>_VERSION and components already defined above, so skipped here
+ # Package File Interface Variables
- <package>_FOUND
- - <package>_VERSION_COUNT
- <package>_FIND_REQUIRED
- <package>_FIND_QUIETLY
- - <package>_FIND_VERSION
- - <package>_FIND_VERSION_MAJOR
- - <package>_FIND_VERSION_MINOR
- - <package>_FIND_VERSION_PATCH
- - <package>_FIND_VERSION_TWEAK
- - <package>_FIND_VERSION_COUNT
- - <package>_FIND_VERSION_EXACT
- - <package>_FIND_COMPONENTS
- - <package>_FIND_REQUIRED_<c>
- - <package>_CONSIDERED_CONFIGS
- - <package>_CONSIDERED_VERSIONS
+ - <package>_VERSION_COUNT
+ # NOTE <SMTH>_VERSION and components already defined above, so skipped here
+ - <PackageName>_FIND_VERSION_COUNT
+ - <PackageName>_FIND_VERSION_EXACT
+ - <PackageName>_FIND_COMPONENTS
+ - <PackageName>_FIND_REQUIRED_<c>
+ - <PackageName>_FIND_VERSION_RANGE
+ - <PackageName>_FIND_VERSION_RANGE_MIN
+ - <PackageName>_FIND_VERSION_RANGE_MAX
+ - <PackageName>_FIND_VERSION_MIN
+ - <PackageName>_FIND_VERSION_MIN_MAJOR
+ - <PackageName>_FIND_VERSION_MIN_MINOR
+ - <PackageName>_FIND_VERSION_MIN_PATCH
+ - <PackageName>_FIND_VERSION_MIN_TWEAK
+ - <PackageName>_FIND_VERSION_MIN_COUNT
+ - <PackageName>_FIND_VERSION_MAX
+ - <PackageName>_FIND_VERSION_MAX_MAJOR
+ - <PackageName>_FIND_VERSION_MAX_MINOR
+ - <PackageName>_FIND_VERSION_MAX_PATCH
+ - <PackageName>_FIND_VERSION_MAX_TWEAK
+ - <PackageName>_FIND_VERSION_MAX_COUNT
+ - <PackageName>_FIND_VERSION_COMPLETE
+ - <PackageName>_CONFIG
+ - <PackageName>_CONSIDERED_CONFIGS
+ - <PackageName>_CONSIDERED_VERSIONS
- <PackageName>_ROOT # Since 3.12
# Other standard variables/patterns
# - `try_run`
- <RUN_RESULT_VAR>__TRYRUN_OUTPUT
+ # - `function`
+ - ARGC
+ - ARGN
+ - ARGV
+ - ARGV<n>
+ # - `cmake_parse_arguments`
+ - <pfx>_UNPARSED_ARGUMENTS
+ - <pfx>_KEYWORDS_MISSING_VALUES
+ # Variables that control `file(GET_RUNTIME_DEPENDENCIES)` behavior
+ - CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM
+ - CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL
+ - CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND
+ # Fallback Interface Variables for `cmake_host_system_information`
+ # Since CMake 3.22
+ - CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS
+ - CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_<varname>
+ - CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
# Well known CMake's official module's variables
+ # - BundleUtilities
+ - BU_CHMOD_BUNDLE_ITEMS
+ # - CheckCompilerFlag
# - CheckCCompilerFlag
+ # - CheckSourceCompiles
# - CheckCSourceCompiles
+ # - CheckSourceRuns
# - CheckCSourceRuns
# - CheckCxxCompilerFlag
# - CheckCxxSourceCompiles
@@ -1056,6 +1514,22 @@ variables:
# - CheckIncludeFile
# - CheckIncludeFiles
# - CheckLibraryExists
+ # - CheckPIESupported
+ - CMAKE_C_LINK_PIE_SUPPORTED
+ - CMAKE_CXX_LINK_PIE_SUPPORTED
+ - CMAKE_Fortran_LINK_PIE_SUPPORTED
+ - CMAKE_C_LINK_NO_PIE_SUPPORTED
+ - CMAKE_CXX_LINK_NO_PIE_SUPPORTED
+ - CMAKE_Fortran_LINK_NO_PIE_SUPPORTED
+ # Since 3.23
+ - CMAKE_OBJC_LINK_PIE_SUPPORTED
+ - CMAKE_OBJC_LINK_NO_PIE_SUPPORTED
+ - CMAKE_OBJCXX_LINK_PIE_SUPPORTED
+ - CMAKE_OBJCXX_LINK_NO_PIE_SUPPORTED
+ - CMAKE_CUDA_LINK_PIE_SUPPORTED
+ - CMAKE_CUDA_LINK_NO_PIE_SUPPORTED
+ - CMAKE_HIP_LINK_PIE_SUPPORTED
+ - CMAKE_HIP_LINK_NO_PIE_SUPPORTED
# - CheckPrototypeDefinition
# - CheckStructHasMember
# - CheckSymbolExists
@@ -1064,6 +1538,7 @@ variables:
- CMAKE_REQUIRED_DEFINITIONS
- CMAKE_REQUIRED_FLAGS
- CMAKE_REQUIRED_INCLUDES
+ - CMAKE_REQUIRED_LINK_OPTIONS # Since 3.14
- CMAKE_REQUIRED_LIBRARIES
- CMAKE_REQUIRED_QUIET
# - CheckTypeSize
@@ -1089,7 +1564,9 @@ variables:
# - CPackArchive
- CPACK_ARCHIVE_FILE_NAME
- CPACK_ARCHIVE_<COMPONENT>_FILE_NAME
+ - CPACK_ARCHIVE_FILE_EXTENSION # Since 3.25
- CPACK_ARCHIVE_COMPONENT_INSTALL
+ - CPACK_ARCHIVE_THREADS # Since 3.18
# - CPackBundle
- CPACK_BUNDLE_NAME
- CPACK_BUNDLE_PLIST
@@ -1131,7 +1608,7 @@ variables:
- CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS
- CPACK_DEBIAN_PACKAGE_MAINTAINER
- CPACK_DEBIAN_PACKAGE_DESCRIPTION
- - CPACK_COMPONENT_<COMPONENT>_DESCRIPTION
+ - CPACK_DEBIAN_<COMPONENT>_DESCRIPTION # Since 3.16
- CPACK_DEBIAN_PACKAGE_SECTION
- CPACK_DEBIAN_<COMPONENT>_PACKAGE_SECTION
- CPACK_DEBIAN_ARCHIVE_TYPE
@@ -1141,6 +1618,7 @@ variables:
- CPACK_DEBIAN_PACKAGE_HOMEPAGE
- CPACK_DEBIAN_PACKAGE_SHLIBDEPS
- CPACK_DEBIAN_<COMPONENT>_PACKAGE_SHLIBDEPS
+ - CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS # Since 3.20
- CPACK_DEBIAN_PACKAGE_DEBUG
- CPACK_DEBIAN_PACKAGE_PREDEPENDS
- CPACK_DEBIAN_<COMPONENT>_PACKAGE_PREDEPENDS
@@ -1175,15 +1653,54 @@ variables:
- CPACK_DMG_DS_STORE_SETUP_SCRIPT
- CPACK_DMG_BACKGROUND_IMAGE
- CPACK_DMG_DISABLE_APPLICATIONS_SYMLINK
+ - CPACK_DMG_SLA_USE_RESOURCE_FILE_LICENSE # Since 3.23
- CPACK_DMG_SLA_DIR
- CPACK_DMG_SLA_LANGUAGES
+ - CPACK_DMG_<component>_FILE_NAME # Since 3.17
+ - CPACK_DMG_FILESYSTEM # Since 3.21
- CPACK_COMMAND_HDIUTIL
- CPACK_COMMAND_SETFILE
- CPACK_COMMAND_REZ
+ # [built-in]: CPack Inno Setup Generator (Since 3.27)
+ - CPACK_INNOSETUP_USE_CMAKE_BOOL_FORMAT
+ - CPACK_INNOSETUP_ARCHITECTURE
+ - CPACK_INNOSETUP_INSTALL_ROOT
+ - CPACK_INNOSETUP_ALLOW_CUSTOM_DIRECTORY
+ - CPACK_INNOSETUP_PROGRAM_MENU_FOLDER
+ - CPACK_INNOSETUP_LANGUAGES
+ - CPACK_INNOSETUP_IGNORE_LICENSE_PAGE
+ - CPACK_INNOSETUP_IGNORE_README_PAGE
+ - CPACK_INNOSETUP_PASSWORD
+ - CPACK_INNOSETUP_USE_MODERN_WIZARD
+ - CPACK_INNOSETUP_ICON_FILE
+ - CPACK_INNOSETUP_SETUP_<directive>
+ - CPACK_INNOSETUP_CUSTOM_INSTALL_INSTRUCTIONS
+ - CPACK_INNOSETUP_MENU_LINKS
+ - CPACK_INNOSETUP_CREATE_UNINSTALL_LINK
+ - CPACK_INNOSETUP_RUN_EXECUTABLES
+ - CPACK_INNOSETUP_<compName>_INSTALL_DIRECTORY
+ - CPACK_INNOSETUP_VERIFY_DOWNLOADS
+ - CPACK_INNOSETUP_EXECUTABLE
+ - CPACK_INNOSETUP_EXECUTABLE_ARGUMENTS
+ - CPACK_INNOSETUP_DEFINE_<macro>
+ - CPACK_INNOSETUP_EXTRA_SCRIPTS
+ - CPACK_INNOSETUP_CODE_FILES
+ # [built-in]: CPack FreeBSD Generator (Since 3.10)
+ - CPACK_FREEBSD_PACKAGE_NAME
+ - CPACK_FREEBSD_PACKAGE_COMMENT
+ - CPACK_FREEBSD_PACKAGE_DESCRIPTION
+ - CPACK_FREEBSD_PACKAGE_WWW
+ - CPACK_FREEBSD_PACKAGE_LICENSE
+ - CPACK_FREEBSD_PACKAGE_LICENSE_LOGIC
+ - CPACK_FREEBSD_PACKAGE_MAINTAINER
+ - CPACK_FREEBSD_PACKAGE_ORIGIN
+ - CPACK_FREEBSD_PACKAGE_CATEGORIES
+ - CPACK_FREEBSD_PACKAGE_DEPS
# -CPackExt (Since 3.13)
- - CPACK_EXT_REQUESTED_VERSIONS
- - CPACK_EXT_ENABLE_STAGING
- - CPACK_EXT_PACKAGE_SCRIPT
+ - CPACK_EXTERNAL_REQUESTED_VERSIONS
+ - CPACK_EXTERNAL_ENABLE_STAGING
+ - CPACK_EXTERNAL_PACKAGE_SCRIPT
+ - CPACK_EXTERNAL_BUILT_PACKAGES # Since 3.19
# - CPackIFW
- CPACK_IFW_ROOT
- QTIFWDIR
@@ -1198,8 +1715,10 @@ variables:
- CPACK_IFW_PACKAGE_BANNER
- CPACK_IFW_PACKAGE_BACKGROUND
- CPACK_IFW_PACKAGE_WIZARD_STYLE
+ - CPACK_IFW_PACKAGE_STYLE_SHEET # Since 3.15
- CPACK_IFW_PACKAGE_WIZARD_DEFAULT_WIDTH
- CPACK_IFW_PACKAGE_WIZARD_DEFAULT_HEIGHT
+ - CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST # Since 3.20
- CPACK_IFW_PACKAGE_TITLE_COLOR
- CPACK_IFW_PACKAGE_START_MENU_DIRECTORY
- CPACK_IFW_TARGET_DIRECTORY
@@ -1210,11 +1729,19 @@ variables:
- CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_INI_FILE
- CPACK_IFW_PACKAGE_ALLOW_NON_ASCII_CHARACTERS
- CPACK_IFW_PACKAGE_ALLOW_SPACE_IN_PATH
+ - CPACK_IFW_PACKAGE_DISABLE_COMMAND_LINE_INTERFACE # Since 3.23
- CPACK_IFW_PACKAGE_CONTROL_SCRIPT
- CPACK_IFW_PACKAGE_REMOVE_TARGET_DIR # Since 3.11
- CPACK_IFW_PACKAGE_RESOURCES
- CPACK_IFW_REPOSITORIES_ALL
- CPACK_IFW_DOWNLOAD_ALL
+ - CPACK_IFW_PACKAGE_PRODUCT_IMAGES # Since 3.23
+ - CPACK_IFW_PACKAGE_RUN_PROGRAM # Since 3.23
+ - CPACK_IFW_PACKAGE_RUN_PROGRAM_ARGUMENTS # Since 3.23
+ - CPACK_IFW_PACKAGE_RUN_PROGRAM_DESCRIPTION # Since 3.23
+ - CPACK_IFW_PACKAGE_SIGNING_IDENTITY # Since 3.23
+ - CPACK_IFW_ARCHIVE_FORMAT # Since 3.23
+ - CPACK_IFW_ARCHIVE_COMPRESSION # Since 3.23
- CPACK_IFW_RESOLVE_DUPLICATE_NAMES
- CPACK_IFW_PACKAGES_DIRECTORIES
- CPACK_IFW_FRAMEWORK_VERSION
@@ -1247,6 +1774,19 @@ variables:
- CPACK_NSIS_EXECUTABLES_DIRECTORY
- CPACK_NSIS_MUI_FINISHPAGE_RUN
- CPACK_NSIS_MENU_LINKS
+ - CPACK_NSIS_UNINSTALL_NAME # Since 3.17
+ - CPACK_NSIS_WELCOME_TITLE # Since 3.17
+ - CPACK_NSIS_WELCOME_TITLE_3LINES # Since 3.17
+ - CPACK_NSIS_FINISH_TITLE # Since 3.17
+ - CPACK_NSIS_FINISH_TITLE_3LINES # Since 3.17
+ - CPACK_NSIS_MUI_HEADERIMAGE # Since 3.17
+ - CPACK_NSIS_MANIFEST_DPI_AWARE # Since 3.18
+ - CPACK_NSIS_BRANDING_TEXT # Since 3.20
+ - CPACK_NSIS_BRANDING_TEXT_TRIM_POSITION # Since 3.20
+ - CPACK_NSIS_EXECUTABLE # Since 3.21
+ - CPACK_NSIS_IGNORE_LICENSE_PAGE # Since 3.22
+ - CPACK_NSIS_EXECUTABLE_PRE_ARGUMENTS # Since 3.25
+ - CPACK_NSIS_EXECUTABLE_POST_ARGUMENTS # Since 3.25
# - CPackNuGet (since 3.12)
- CPACK_NUGET_COMPONENT_INSTALL
- CPACK_NUGET_PACKAGE_NAME
@@ -1265,14 +1805,22 @@ variables:
- CPACK_NUGET_<compName>_PACKAGE_HOMEPAGE_URL
- CPACK_NUGET_PACKAGE_LICENSEURL
- CPACK_NUGET_<compName>_PACKAGE_LICENSEURL
+ - CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION # Since 3.20
+ - CPACK_NUGET_<compName>_PACKAGE_LICENSE_EXPRESSION # Since 3.20
+ - CPACK_NUGET_PACKAGE_LICENSE_FILE_NAME # Since 3.20
+ - CPACK_NUGET_<compName>_PACKAGE_LICENSE_FILE_NAME # Since 3.20
- CPACK_NUGET_PACKAGE_ICONURL
- CPACK_NUGET_<compName>_PACKAGE_ICONURL
+ - CPACK_NUGET_PACKAGE_ICON # Since 3.20
+ - CPACK_NUGET_<compName>_PACKAGE_ICON # Since 3.20
- CPACK_NUGET_PACKAGE_DESCRIPTION_SUMMARY
- CPACK_NUGET_<compName>_PACKAGE_DESCRIPTION_SUMMARY
- CPACK_NUGET_PACKAGE_RELEASE_NOTES
- CPACK_NUGET_<compName>_PACKAGE_RELEASE_NOTES
- CPACK_NUGET_PACKAGE_COPYRIGHT
- CPACK_NUGET_<compName>_PACKAGE_COPYRIGHT
+ - CPACK_NUGET_PACKAGE_LANGUAGE # Since 3.20
+ - CPACK_NUGET_<compName>_PACKAGE_LANGUAGE # Since 3.20
- CPACK_NUGET_PACKAGE_TAGS
- CPACK_NUGET_<compName>_PACKAGE_TAGS
- CPACK_NUGET_PACKAGE_DEPENDENCIES
@@ -1280,16 +1828,33 @@ variables:
- CPACK_NUGET_PACKAGE_DEPENDENCIES_<dependency>_VERSION
- CPACK_NUGET_<compName>_PACKAGE_DEPENDENCIES_<dependency>_VERSION
- CPACK_NUGET_PACKAGE_DEBUG
- # - CPackPackageMaker
- - CPACK_OSX_PACKAGE_VERSION
+ # - CPackPackageMaker is deprecated and gonna be removed in next versions of CPack
+ # NOTE CPackPackageMaker has been removed in 3.24
# - CPackProductBuild
- CPACK_COMMAND_PRODUCTBUILD
+ - CPACK_PRODUCTBUILD_IDENTIFIER # Since 3.23
- CPACK_PRODUCTBUILD_IDENTITY_NAME
- CPACK_PRODUCTBUILD_KEYCHAIN_PATH
- CPACK_COMMAND_PKGBUILD
- CPACK_PKGBUILD_IDENTITY_NAME
- CPACK_PKGBUILD_KEYCHAIN_PATH
+ - CPACK_PREFLIGHT_<COMP>_SCRIPT # Since 3.1?
+ - CPACK_POSTFLIGHT_<COMP>_SCRIPT # Since 3.1?
- CPACK_PRODUCTBUILD_RESOURCES_DIR
+ - CPACK_PRODUCTBUILD_DOMAINS # Since 3.23
+ - CPACK_PRODUCTBUILD_DOMAINS_ANYWHERE # Since 3.23
+ - CPACK_PRODUCTBUILD_DOMAINS_USER # Since 3.23
+ - CPACK_PRODUCTBUILD_DOMAINS_ROOT # Since 3.23
+ - CPACK_PRODUCTBUILD_BACKGROUND # Since 3.17
+ - CPACK_PRODUCTBUILD_BACKGROUND_ALIGNMENT # Since 3.17
+ - CPACK_PRODUCTBUILD_BACKGROUND_SCALING # Since 3.17
+ - CPACK_PRODUCTBUILD_BACKGROUND_MIME_TYPE # Since 3.17
+ - CPACK_PRODUCTBUILD_BACKGROUND_UTI # Since 3.17
+ - CPACK_PRODUCTBUILD_BACKGROUND_DARKAQUA # Since 3.17
+ - CPACK_PRODUCTBUILD_BACKGROUND_DARKAQUA_ALIGNMENT # Since 3.17
+ - CPACK_PRODUCTBUILD_BACKGROUND_DARKAQUA_SCALING # Since 3.17
+ - CPACK_PRODUCTBUILD_BACKGROUND_DARKAQUA_MIME_TYPE # Since 3.17
+ - CPACK_PRODUCTBUILD_BACKGROUND_DARKAQUA_UTI # Since 3.17
# - CPackRPM
- CPACK_RPM_COMPONENT_INSTALL
- CPACK_RPM_PACKAGE_SUMMARY
@@ -1347,8 +1912,10 @@ variables:
- CPACK_RPM_GENERATE_USER_BINARY_SPECFILE_TEMPLATE
- CPACK_RPM_PRE_INSTALL_SCRIPT_FILE
- CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE
+ - CPACK_RPM_PRE_TRANS_SCRIPT_FILE # Since 3.18
- CPACK_RPM_POST_INSTALL_SCRIPT_FILE
- CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE
+ - CPACK_RPM_POST_TRANS_SCRIPT_FILE # Since 3.18
- CPACK_RPM_USER_FILELIST
- CPACK_RPM_<COMPONENT>_USER_FILELIST
- CPACK_RPM_CHANGELOG_FILE
@@ -1383,12 +1950,10 @@ variables:
- CPACK_RPM_SOURCE_PKG_BUILD_PARAMS
- CPACK_RPM_SOURCE_PKG_PACKAGING_INSTALL_PREFIX
- CPACK_RPM_BUILDREQUIRES
+ - CPACK_RPM_REQUIRES_EXCLUDE_FROM # Since 3.22
# - CPack
- CPACK_PACKAGE_NAME
- CPACK_PACKAGE_VENDOR
- # `CPACK_PACKAGE_CONTACT` used by some modules (like Deb and NSIS),
- # but not documented yet...
- - CPACK_PACKAGE_CONTACT
- CPACK_PACKAGE_DIRECTORY
- CPACK_PACKAGE_VERSION_MAJOR
- CPACK_PACKAGE_VERSION_MINOR
@@ -1411,6 +1976,7 @@ variables:
- CPACK_PACKAGE_EXECUTABLES
- CPACK_STRIP_FILES
- CPACK_VERBATIM_VARIABLES
+ - CPACK_THREADS # Since 3.20
- CPACK_SOURCE_PACKAGE_FILE_NAME
- CPACK_SOURCE_STRIP_FILES
- CPACK_SOURCE_GENERATOR
@@ -1426,6 +1992,12 @@ variables:
- CPACK_PACKAGE_INSTALL_REGISTRY_KEY
- CPACK_CREATE_DESKTOP_LINKS
- CPACK_BINARY_<GENNAME>
+ - CPACK_READELF_EXECUTABLE # Since 3.25
+ - CPACK_OBJCOPY_EXECUTABLE # Since 3.25
+ - CPACK_OBJDUMP_EXECUTABLE # Since 3.25
+ - CPACK_PRE_BUILD_SCRIPTS # Since 3.19
+ - CPACK_POST_BUILD_SCRIPTS # Since 3.19
+ - CPACK_PACKAGE_FILES # Since 3.19
# - CPackWIX
- CPACK_WIX_UPGRADE_GUID
- CPACK_WIX_PRODUCT_GUID
@@ -1450,6 +2022,9 @@ variables:
- CPACK_WIX_SKIP_PROGRAM_FOLDER
- CPACK_WIX_ROOT_FOLDER_ID
- CPACK_WIX_ROOT
+ - CPACK_WIX_CUSTOM_XMLNS # Since 3.19
+ - CPACK_WIX_SKIP_WIX_UI_EXTENSION # Since 3.23
+ - CPACK_WIX_ARCHITECTURE # Since 3.24
# - CTest
# - Dart
- BUILD_TESTING
@@ -1472,17 +2047,560 @@ variables:
- ExternalData_CUSTOM_LOCATION
- ExternalData_CUSTOM_FILE
- ExternalData_CUSTOM_ERROR
+ # - FetchContent
+ - FETCHCONTENT_QUIET
+ - FETCHCONTENT_FULLY_DISCONNECTED
+ - FETCHCONTENT_UPDATES_DISCONNECTED
+ - FETCHCONTENT_TRY_FIND_PACKAGE_MODE # Since 3.24
+ - FETCHCONTENT_SOURCE_DIR_<ucName>
+ - FETCHCONTENT_UPDATES_DISCONNECTED_<ucName>
# - FindXXX module "standard" variables
- <package>_INCLUDE_DIRS
- <package>_LIBRARIES
- <package>_LIBRARY_DIRS
- <package>_VERSION_STRING
+ # NOTE For the other stanfard finders below variables matched
+ # the regular expressions above gonna be omitted.
+ # - FindALSA
+ - ALSA_LIBRARY
+ # - FindArmadillo
+ - ALSA_LIBRARY
+ # - FindASPELL
+ - ASPELL_EXECUTABLE
+ - ASPELL_DEFINITIONS
+ # - FindAVIFile
+ - AVIFILE_DEFINITIONS
+ # - FindBacktrace
+ - Backtrace_HEADER
+ - Backtrace_LIBRARY
+ # - FindBISON
+ - BISON_EXECUTABLE
+ - BISON_<Name>_DEFINED
+ - BISON_<Name>_INPUT
+ - BISON_<Name>_OUTPUT_SOURCE
+ - BISON_<Name>_OUTPUT_HEADER
+ - BISON_<Name>_OUTPUTS
+ - BISON_<Name>_COMPILE_FLAGS
+ # - FindBLAS
+ - BLA_STATIC
+ - BLA_VENDOR
+ - BLA_F95
+ - BLA_PREFER_PKGCONFIG
+ - BLAS_LINKER_FLAGS
+ - BLA_SIZEOF_INTEGER # Since 3.22
+ # - FindBoost
+ - Boost_<COMPONENT>_LIBRARY
+ - Boost_VERSION_MACRO
+ - Boost_VERSION_COUNT
+ - Boost_INCLUDE_DIR
+ - Boost_LIBRARY_DIR_DEBUG
+ - Boost_LIBRARY_DIR_RELEASE
+ - Boost_<COMPONENT>_LIBRARY_DEBUG
+ - Boost_<COMPONENT>_LIBRARY_RELEASE
+ - BOOSTROOT
+ - BOOST_INCLUDEDIR
+ - BOOST_LIBRARYDIR
+ - Boost_NO_SYSTEM_PATHS
+ - Boost_ADDITIONAL_VERSIONS
+ - Boost_USE_DEBUG_LIBS
+ - Boost_USE_RELEASE_LIBS
+ - Boost_USE_MULTITHREADED
+ - Boost_USE_STATIC_LIBS
+ - Boost_USE_DEBUG_RUNTIME
+ - Boost_USE_DEBUG_PYTHON
+ - Boost_USE_STLPORT
+ - Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS
+ - Boost_COMPILER
+ - Boost_LIB_PREFIX
+ - Boost_ARCHITECTURE
+ - Boost_THREADAPI
+ - Boost_NAMESPACE
+ - Boost_DEBUG
+ - Boost_REALPATH
+ - Boost_NO_WARN_NEW_VERSIONS
+ # - FindBullet
+ # - FindBZip2
+ - BZIP2_NEED_PREFIX
+ - BZIP2_INCLUDE_DIR
+ # - FindCABLE
+ - CABLE
+ - CABLE_TCL_LIBRARY
+ - CABLE_INCLUDE_DIR
+ # - FindCoin3D
+ # - FindCUDAToolkit
+ - CUDAToolkit_NVCC_EXECUTABLE
+ # - FindCups
+ - CUPS_INCLUDE_DIR
+ # - FindCURL
+ - CURL_NO_CURL_CMAKE
+ - CURL_USE_STATIC_LIBS # Since 3.28
+ # - FindCurses
+ - CURSES_CFLAGS
+ - CURSES_HAVE_CURSES_H
+ - CURSES_HAVE_NCURSES_H
+ - CURSES_HAVE_NCURSES_NCURSES_H
+ - CURSES_HAVE_NCURSES_CURSES_H
+ - CURSES_NEED_NCURSES
+ - CURSES_NEED_WIDE
+ # - CURSES_LIBRARY deprecated
+ # - FindCVS
+ - CVS_EXECUTABLE
+ # - FindCxxTest
+ - CXXTEST_USE_PYTHON
+ - CXXTEST_TESTGEN_ARGS
+ - CXXTEST_PERL_TESTGEN_EXECUTABLE
+ - CXXTEST_PYTHON_TESTGEN_EXECUTABLE
+ - CXXTEST_TESTGEN_EXECUTABLE
+ - CXXTEST_TESTGEN_INTERPRETER
+ # - FindCygwin
+ # - FindDCMTK
+ # - FindDevIL
# - FindDoxygen
- DOXYGEN_<TAG>
+ # - FindEnvModules
+ - EnvModules_COMMAND
+ # - FindEXPAT
+ - EXPAT_USE_STATIC_LIBS # Since 3.28
+ # - FindFLEX
+ - FLEX_EXECUTABLE
+ # - FindFLTK
+ - FLTK_SKIP_OPENGL
+ - FLTK_SKIP_FORMS
+ - FLTK_SKIP_IMAGES
+ - FLTK_SKIP_FLUID
+ - FLTK_FLUID_EXECUTABLE
+ - FLTK_WRAP_UI
+ - FLTK_BASE_LIBRARY_RELEASE
+ - FLTK_BASE_LIBRARY_DEBUG
+ - FLTK_GL_LIBRARY_RELEASE
+ - FLTK_GL_LIBRARY_DEBUG
+ - FLTK_FORMS_LIBRARY_RELEASE
+ - FLTK_FORMS_LIBRARY_DEBUG
+ - FLTK_IMAGES_LIBRARY_RELEASE
+ - FLTK_IMAGES_LIBRARY_DEBUG
+ # - FindFLTK2
+ - FLTK2_FLUID_EXECUTABLE
+ - FLTK2_WRAP_UI
+ - FLTK2_BASE_LIBRARY
+ - FLTK2_GL_LIBRARY
+ - FLTK2_IMAGES_LIBRARY
+ # - FindFontconfig
+ - Fontconfig_COMPILE_OPTIONS
+ # - FindFreetype
+ - FREETYPE_INCLUDE_DIR_ft2build
+ - FREETYPE_INCLUDE_DIR_freetype2
+ # - FindGCCXML
+ - GCCXML
+ # - FindGDAL
+ - GDAL_LIBRARY
+ - FindGDAL_SKIP_GDAL_CONFIG
+ - GDAL_ADDITIONAL_LIBRARY_VERSIONS
+ # - FindGettext
+ - GETTEXT_MSGMERGE_EXECUTABLE
+ - GETTEXT_MSGFMT_EXECUTABLE
+ # - FindGIF
+ - GIF_LIBRARY
+ # - FindGit
+ - GIT_EXECUTABLE
+ # - FindGLEW
+ - GLEW_USE_STATIC_LIBS
+ - GLEW_VERBOSE
+ # - FindGLUT
+ - GLUT_glut_LIBRARY
+ - GLUT_Xmu_LIBRARY
+ - GLUT_Xi_LIBRARY
+ # - FindGnuplot
+ - GNUPLOT_EXECUTABLE
+ # - FindGnuTLS
+ - GNUTLS_DEFINITIONS
+ # - FindGSL
+ - GSL_CBLAS_LIBRARY
+ - GSL_CBLAS_LIBRARY_DEBUG
+ - GSL_CONFIG_EXECUTABLE
+ - GSL_LIBRARY
+ - GSL_LIBRARY_DEBUG
+ # - FindGTest
+ - GTEST_MSVC_SEARCH
+ # - FindGTK
+ # - FindGTK2
+ - GTK2_TARGETS
+ - GTK2_DEFINITIONS
+ - GTK2_USE_IMPORTED_TARGETS
+ - GTK2_DEBUG
+ - GTK2_ADDITIONAL_SUFFIXES
+ # - FindHDF5
+ - HDF5_DEFINITIONS
+ - HDF5_C_DEFINITIONS
+ - HDF5_CXX_DEFINITIONS
+ - HDF5_Fortran_DEFINITIONS
+ - HDF5_IS_PARALLEL
+ - HDF5_C_COMPILER_EXECUTABLE
+ - HDF5_CXX_COMPILER_EXECUTABLE
+ - HDF5_Fortran_COMPILER_EXECUTABLE
+ - HDF5_C_COMPILER_EXECUTABLE_NO_INTERROGATE
+ - HDF5_CXX_COMPILER_EXECUTABLE_NO_INTERROGATE
+ - HDF5_Fortran_COMPILER_EXECUTABLE_NO_INTERROGATE
+ - HDF5_DIFF_EXECUTABLE
+ - HDF5_PREFER_PARALLEL
+ - HDF5_FIND_DEBUG
+ - HDF5_NO_FIND_PACKAGE_CONFIG_FILE
+ # - FindHg
+ - HG_EXECUTABLE
+ #- <var-prefix>_WC_CHANGESET
+ #- <var-prefix>_WC_REVISION
+ # - FindHSPELL
+ # - FindHTMLHelp
+ - HTML_HELP_COMPILER
+ - HTML_HELP_LIBRARY
+ # - FindIce
+ # - FindIconv
+ - Iconv_IS_BUILT_IN
+ - Iconv_LIBRARY
+ # - FindIcotool
+ - ICOTOOL_EXECUTABLE
+ # - FindICU
+ - ICU_MAKEFILE_INC
+ - ICU_PKGDATA_INC
+ - ICU_<P>_EXECUTABLE
+ - ICU_<C>_LIBRARY
+ - ICU_DEBUG
+ # - FindImageMagick
+ # - FindIntl
+ - Intl_LIBRARY
+ - Intl_IS_BUILT_IN
+ # - FindITK
+ # - FindJasper
+ - JASPER_LIBRARY_RELEASE
+ - JASPER_LIBARRY_DEBUG
+ # - FindJava
+ - Java_JAVA_EXECUTABLE
+ - Java_JAVAC_EXECUTABLE
+ - Java_JAVAH_EXECUTABLE
+ - Java_JAVADOC_EXECUTABLE
+ - Java_IDLJ_EXECUTABLE
+ - Java_JAR_EXECUTABLE
+ - Java_JARSIGNER_EXECUTABLE
+ # - FindJNI
+ - JAVA_AWT_LIBRARY
+ - JAVA_JVM_LIBRARY
+ - JAVA_INCLUDE_PATH
+ - JAVA_INCLUDE_PATH2
+ - JAVA_AWT_INCLUDE_PATH
+ # - FindJPEG
+ - JPEG_LIBRARY_RELEASE
+ - JPEG_LIBRARY_DEBUG
+ - JPEG_LIBRARY
+ # - FindKDE3
+ # - FindKDE4
+ # - FindLAPACK
+ - LAPACK_LINKER_FLAGS
+ # - FindLATEX
+ - LATEX_COMPILER
+ - PDFLATEX_COMPILER
+ - XELATEX_COMPILER
+ - LUALATEX_COMPILER
+ - BIBTEX_COMPILER
+ - BIBER_COMPILER
+ - MAKEINDEX_COMPILER
+ - XINDY_COMPILER
+ - DVIPS_CONVERTER
+ - DVIPDF_CONVERTER
+ - PS2PDF_CONVERTER
+ - PDFTOPS_CONVERTER
+ - LATEX2HTML_CONVERTER
+ - HTLATEX_COMPILER
+ # - FindLibArchive
+ # - FindLibinput
+ - Libinput_COMPILE_OPTIONS
+ # - FindLibLZMA
+ - LIBLZMA_HAS_AUTO_DECODER
+ - LIBLZMA_HAS_EASY_ENCODER
+ - LIBLZMA_HAS_LZMA_PRESET
+ # - FindLibXml2
+ - LIBXML2_DEFINITIONS
+ - LIBXML2_XMLLINT_EXECUTABLE
+ - LIBXML2_LIBRARY
+ # - FindLibXslt
+ - LIBXSLT_DEFINITIONS
+ - LIBXSLT_XSLTPROC_EXECUTABLE
+ # - FindLTTngUST
+ - LTTNGUST_HAS_TRACEF
+ - LTTNGUST_HAS_TRACELOG
+ # - FindLua
+ # - FindLua50
+ # - FindLua51
+ # - FindMatlab
+ - MATLAB_FIND_DEBUG
+ - MATLAB_ADDITIONAL_VERSIONS
+ - Matlab_MAIN_PROGRAM
+ - Matlab_MEX_LIBRARY
+ - Matlab_MX_LIBRARY
+ - Matlab_ENG_LIBRARY
+ - Matlab_MAT_LIBRARY
+ - Matlab_ENGINE_LIBRARY
+ - Matlab_DATAARRAY_LIBRARY
+ - Matlab_MEX_COMPILER
+ - Matlab_MCC_COMPILER
+ - Matlab_MEX_EXTENSION
+ # - FindMFC
+ # - FindMotif
+ # - FindMPEG
+ - MPEG_mpeg2_LIBRARY
+ - MPEG_vo_LIBRARY
+ # - FindMPEG2
+ - MPEG2_mpeg2_LIBRARY
+ - MPEG2_vo_LIBRARY
+ # - FindMPI
+ - MPI_<lang>_COMPILER
+ - MPI_<lang>_COMPILE_OPTIONS
+ - MPI_<lang>_COMPILE_DEFINITIONS
+ - MPI_Fortran_HAVE_F77_HEADER
+ - MPI_Fortran_HAVE_F90_MODULE
+ - MPI_Fortran_HAVE_F08_MODULE
+ - MPIEXEC_EXECUTABLE
+ - MPIEXEC_NUMPROC_FLAG
+ - MPIEXEC_MAX_NUMPROCS
+ - MPIEXEC_PREFLAGS
+ - MPIEXEC_POSTFLAGS
+ - MPIEXEC_EXECUTABLE
+ - MPI_HOME
+ - MPI_COMPILER_FLAGS
+ - MPI_EXECUTABLE_SUFFIX
+ - MPI_GUESS_LIBRARY_NAME
+ - MPI_ASSUME_NO_BUILTIN_MPI
+ - MPI_SKIP_COMPILER_WRAPPER
+ - MPI_SKIP_GUESSING
+ - MPI_CXX_SKIP_MPICXX
+ - MPI_<lang>_ADDITIONAL_INCLUDE_VARS
+ - MPI_<lib_name>_LIBRARY
+ - MPI_<lang>_LIB_NAMES
+ - MPI_DETERMINE_Fortran_CAPABILITIES
+ - MPI_SUBARRAYS_SUPPORTED
+ - MPI_ASYNC_PROTECTS_NONBLOCKING
+ - MPI_Fortran_F77_HEADER_SUBARRAYS
+ - MPI_Fortran_F77_HEADER_ASYNCPROT
+ - MPI_Fortran_F90_MODULE_SUBARRAYS
+ - MPI_Fortran_F90_MODULE_ASYNCPROT
+ - MPI_Fortran_F08_MODULE_SUBARRAYS
+ - MPI_Fortran_F08_MODULE_ASYNCPROT
+ # - FindMsys
+ # - FindODBC
+ - ODBC_CONFIG
+ - ODBC_LIBRARY
+ # - FindOpenACC
+ - OpenACC_<lang>_FLAGS
+ - OpenACC_<lang>_OPTIONS
+ - OpenACC_<lang>_SPEC_DATE
+ - OpenACC_ACCEL_TARGET
+ # - FindOpenAL
+ - OPENAL_LIBRARY
+ # - FindOpenCL
+ - OpenCL_LIBRARY
+ # - FindOpenGL
+ - OPENGL_egl_LIBRARY
+ - OPENGL_glu_LIBRARY
+ - OPENGL_glx_LIBRARY
+ - OPENGL_opengl_LIBRARY
+ - OPENGL_gl_LIBRARY
+ - OpenGL_GL_PREFERENCE
+ # - FindOpenMP
+ - OpenMP_<lang>_FLAGS
+ - OpenMP_<lang>_LIB_NAMES
+ - OpenMP_<libname>_LIBRARY
+ - OpenMP_Fortran_HAVE_OMPLIB_HEADER
+ - OpenMP_Fortran_HAVE_OMPLIB_MODULE
+ - OpenMP_<lang>_SPEC_DATE
+ # - FindOpenSceneGraph
+ # - FindOpenSSL
+ - OPENSSL_CRYPTO_LIBRARY
+ - OPENSSL_SSL_LIBRARY
+ - OPENSSL_APPLINK_SOURCE
+ - OPENSSL_USE_STATIC_LIBS
+ - OPENSSL_MSVC_STATIC_RT
+ # - FindOpenThreads
+ # - Findosg
+ # - Findosg_functions
+ # - FindosgAnimation
+ # - FindosgDB
+ # - FindosgFX
+ # - FindosgGA
+ # - FindosgIntrospection
+ # - FindosgManipulator
+ # - FindosgParticle
+ # - FindosgPresentation
+ # - FindosgProducer
+ # - FindosgQt
+ # - FindosgShadow
+ # - FindosgSim
+ # - FindosgTerrain
+ # - FindosgText
+ # - FindosgUtil
+ # - FindosgViewer
+ # - FindosgVolume
+ # - FindosgWidget
+ # - FindPatch
+ - Patch_EXECUTABLE
+ # - FindPerl
+ - PERL_EXECUTABLE
+ # - FindPerlLibs
+ - PERL_SITESEARCH
+ - PERL_SITEARCH
+ - PERL_SITELIB
+ - PERL_VENDORARCH
+ - PERL_VENDORLIB
+ - PERL_ARCHLIB
+ - PERL_PRIVLIB
+ - PERL_UPDATE_ARCHLIB
+ - PERL_UPDATE_PRIVLIB
+ - PERL_EXTRA_C_FLAGS
+ # - FindPHP4
+ - PHP4_INCLUDE_PATH
+ - PHP4_EXECUTABLE
+ # - FindPhysFS
+ - PHYSFS_LIBRARY
+ # - FindPike
+ - PIKE_INCLUDE_PATH
+ - PIKE_EXECUTABLE
# - FindPkgConfig
- PKG_CONFIG_EXECUTABLE
- PKG_CONFIG_VERSION_STRING
- PKG_CONFIG_USE_CMAKE_PREFIX_PATH
+ - <prefix>_MODULE_NAME # Since 3.16
+ - <XXX>_LINK_LIBRARIES
+ - <XXX>_STATIC_LINK_LIBRARIES # Since 3.24
+ - <XXX>_LDFLAGS
+ - <XXX>_LDFLAGS_OTHER
+ - <XXX>_CFLAGS
+ - <XXX>_CFLAGS_OTHER
+ # - FindPNG
+ - PNG_DEFINITIONS
+ - PNG_LIBRARY
+ # - FindPostgreSQL
+ # - FindProducer
+ # - FindProtobuf
+ - Protobuf_SRC_ROOT_FOLDER
+ - Protobuf_IMPORT_DIRS
+ - Protobuf_DEBUG
+ - Protobuf_USE_STATIC_LIBS
+ - Protobuf_LIBRARY
+ - Protobuf_PROTOC_LIBRARY
+ - Protobuf_PROTOC_EXECUTABLE
+ - Protobuf_LIBRARY_DEBUG
+ - Protobuf_PROTOC_LIBRARY_DEBUG
+ - Protobuf_LITE_LIBRARY
+ - Protobuf_LITE_LIBRARY_DEBUG
+ # - FindPython
+ - Python_EXECUTABLE
+ - Python_INTERPRETER_ID
+ - Python_STDLIB
+ - Python_STDARCH
+ - Python_SITELIB
+ - Python_SITEARCH
+ - Python_SOABI
+ - Python_COMPILER
+ - Python_COMPILER_ID
+ - Python_DOTNET_LAUNCHER
+ - Python_LINK_OPTIONS
+ - Python_USE_STATIC_LIBS
+ - Python_FIND_ABI
+ - Python_FIND_STRATEGY
+ - Python_FIND_REGISTRY
+ - Python_FIND_FRAMEWORK
+ - Python_FIND_VIRTUALENV
+ - Python_FIND_IMPLEMENTATIONS
+ - Python_FIND_UNVERSIONED_NAMES
+ - Python_ARTIFACTS_INTERACTIVE
+ # - FindPython2
+ - Python2_EXECUTABLE
+ - Python2_INTERPRETER_ID
+ - Python2_STDLIB
+ - Python2_STDARCH
+ - Python2_SITELIB
+ - Python2_SITEARCH
+ - Python2_SOABI
+ - Python2_COMPILER
+ - Python2_COMPILER_ID
+ - Python2_DOTNET_LAUNCHER
+ - Python2_LINK_OPTIONS
+ - Python2_USE_STATIC_LIBS
+ - Python2_FIND_ABI
+ - Python2_FIND_STRATEGY
+ - Python2_FIND_REGISTRY
+ - Python2_FIND_FRAMEWORK
+ - Python2_FIND_VIRTUALENV
+ - Python2_FIND_IMPLEMENTATIONS
+ - Python2_FIND_UNVERSIONED_NAMES
+ - Python2_ARTIFACTS_INTERACTIVE
+ # - FindPython3
+ - Python3_EXECUTABLE
+ - Python3_INTERPRETER_ID
+ - Python3_STDLIB
+ - Python3_STDARCH
+ - Python3_SITELIB
+ - Python3_SITEARCH
+ - Python3_SOABI
+ - Python3_COMPILER
+ - Python3_COMPILER_ID
+ - Python3_DOTNET_LAUNCHER
+ - Python3_LINK_OPTIONS
+ - Python3_USE_STATIC_LIBS
+ - Python3_FIND_ABI
+ - Python3_FIND_STRATEGY
+ - Python3_FIND_REGISTRY
+ - Python3_FIND_FRAMEWORK
+ - Python3_FIND_VIRTUALENV
+ - Python3_FIND_IMPLEMENTATIONS
+ - Python3_FIND_UNVERSIONED_NAMES
+ - Python3_ARTIFACTS_INTERACTIVE
+ # - FindQt3
+ # - FindQt4
+ # - FindQuickTime
+ # - FindRTI
+ - RTI_DEFINITIONS
+ # - FindRuby
+ - Ruby_EXECUTABLE
+ - Ruby_FIND_VIRTUALENV
+ # - FindSDL
+ - SDL_LIBRARY
+ - SDL_BUILDING_LIBRARY
+ # - FindSDL_image
+ - SDLIMAGE_LIBRARY
+ # - FindSDL_mixer
+ - SDLMIXER_LIBRARY
+ # - FindSDL_net
+ - SDLNET_LIBRARY
+ # - FindSDL_sound
+ - SDL_SOUND_LIBRARY
+ # - FindSDL_ttf
+ - SDLTTF_LIBRARY
+ # - FindSelfPackers
+ # - FindSquish
+ - SQUISH_SERVER_EXECUTABLE
+ - SQUISH_CLIENT_EXECUTABLE
+ # - FindSQLite3
+ # - FindSubversion
+ - Subversion_SVN_EXECUTABLE
+ - Subversion_VERSION_SVN
+ # <var-prefix>_WC_URL
+ # <var-prefix>_WC_ROOT
+ # <var-prefix>_WC_REVISION
+ # <var-prefix>_WC_LAST_CHANGED_AUTHOR
+ # <var-prefix>_WC_LAST_CHANGED_DATE
+ # <var-prefix>_WC_LAST_CHANGED_REV
+ # <var-prefix>_WC_INFO
+ # - FindSWIG
+ - SWIG_EXECUTABLE
+ # - FindTCL
+ - TCL_INCLUDE_PATH
+ - TCL_TCLSH
+ - TK_LIBRARY
+ - TK_INCLUDE_PATH
+ - TK_WISH
+ # - FindTclsh
+ # - FindTclStub
+ - TCL_STUB_LIBRARY
+ - TK_STUB_LIBRARY
+ - TTK_STUB_LIBRARY
# - FindThreads
- CMAKE_THREAD_LIBS_INIT
- CMAKE_USE_SPROC_INIT
@@ -1491,6 +2609,63 @@ variables:
- CMAKE_HP_PTHREADS_INIT
- CMAKE_THREAD_PREFER_PTHREAD
- THREADS_PREFER_PTHREAD_FLAG
+ # - FindTIFF
+ - TIFF_LIBRARY_RELEASE
+ - TIFF_LIBRARY_DEBUG
+ - TIFFXX_LIBRARY_RELEASE
+ - TIFFXX_LIBRARY_DEBUG
+ # - FindUnixCommands
+ # - FindVTK
+ # - FindVulkan
+ - Vulkan_LIBRARY
+ - Vulkan_GLSLC_EXECUTABLE
+ - Vulkan_GLSLANG_VALIDATOR_EXECUTABLE
+ # - FindWget
+ - WGET_EXECUTABLE
+ # - FindWish
+ # - FindwxWidgets
+ - wxWidgets_CONFIGURATION
+ - wxWidgets_EXCLUDE_COMMON_LIBRARIES
+ - wxWidgets_USE_DEBUG
+ - wxWidgets_USE_UNICODE
+ - wxWidgets_USE_UNIVERSAL
+ - wxWidgets_USE_STATIC
+ - wxWidgets_DEFINITIONS
+ - wxWidgets_DEFINITIONS_DEBUG
+ - wxWidgets_CXX_FLAGS
+ - wxWidgets_USE_FILE
+ # - FindX11
+ # - FindXalanC
+ - XalanC_LIBRARY
+ # - FindXCTest
+ - XCTest_EXECUTABLE
+ # - FindXercesC
+ - XercesC_LIBRARY
+ # - FindXMLRPC
+ # - FindZLIB
+ - ZLIB_USE_STATIC_LIBS # Since 3.24
+ # - FortranCInterface
+ # The following vars gonna match by regex
+ # - FortranCInterface_GLOBAL_FOUND
+ # - FortranCInterface_MODULE_FOUND
+ - FortranCInterface_GLOBAL_PREFIX
+ - FortranCInterface_GLOBAL_SUFFIX
+ - FortranCInterface_GLOBAL_CASE
+ - FortranCInterface_GLOBAL__PREFIX
+ - FortranCInterface_GLOBAL__SUFFIX
+ - FortranCInterface_GLOBAL__CASE
+ - FortranCInterface_MODULE_PREFIX
+ - FortranCInterface_MODULE_MIDDLE
+ - FortranCInterface_MODULE_SUFFIX
+ - FortranCInterface_MODULE_CASE
+ - FortranCInterface_MODULE__PREFIX
+ - FortranCInterface_MODULE__MIDDLE
+ - FortranCInterface_MODULE__SUFFIX
+ - FortranCInterface_MODULE__CASE
+ - FortranCInterface_VERIFIED_C
+ - FortranCInterface_VERIFIED_CXX
+ - FortranCInterface_GLOBAL_SYMBOLS
+ - FortranCInterface_MODULE_SYMBOLS
# - GNUInstallDirs
- CMAKE_INSTALL_BINDIR
- CMAKE_INSTALL_FULL_BINDIR
@@ -1524,6 +2699,8 @@ variables:
- CMAKE_INSTALL_FULL_MANDIR
- CMAKE_INSTALL_DOCDIR
- CMAKE_INSTALL_FULL_DOCDIR
+ # - GoogleTest
+ - CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE
# - InstallRequiredSystemLibraries
- CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS
- CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP
@@ -1543,18 +2720,160 @@ variables:
- CMAKE_NO_ANSI_STRING_STREAM
# - TestForSTDNamespace
- CMAKE_NO_STD_NAMESPACE
+ # - UseJava
+ - CMAKE_JAVA_COMPILE_FLAGS
+ - CMAKE_JAVA_INCLUDE_PATH
+ - CMAKE_JNI_TARGET
+ - CMAKE_JAR_CLASSES_PREFIX
# - UseSWIG
+ - UseSWIG_MODULE_VERSION # Since 3.12
- CMAKE_SWIG_FLAGS
- CMAKE_SWIG_OUTDIR
+ - SWIG_OUTFILE_DIR
- SWIG_MODULE_<name>_EXTRA_DEPS
+ - SWIG_SOURCE_FILE_EXTENSIONS # Since 3.14
+ - SWIG_USE_SWIG_DEPENDENCIES # Since 3.20
+
+deprecated-or-internal-variables:
+ - CMAKE_HOME_DIRECTORY
+ - CMAKE_INTERNAL_PLATFORM_ABI
+ - CMAKE_<LANG>_COMPILER_ABI
+ - CMAKE_<LANG>_COMPILER_ARCHITECTURE_ID
+ - CMAKE_<LANG>_COMPILER_VERSION_INTERNAL
+ - CMAKE_<LANG>_PLATFORM_ID
+ - CMAKE_NOT_USING_CONFIG_FLAGS
+ - CMAKE_VS_INTEL_Fortran_PROJECT_VERSION
+ - CPACK_INSTALL_SCRIPT # Deprecated since 3.16
+ # Various undocumented variables (yet)
+ - CMAKE_SUPPRESS_DEVELOPER_WARNINGS # CMake <= 3.10
+ - CMAKE_SUPPRESS_DEVELOPER_ERRORS # CMake <= 3.10
+ - CMAKE_FILES_DIRECTORY
+ # The following variables used by CPack and some CMake modules,
+ # but not documented (yet):
+ # used by some modules like Deb and NSIS
+ - CPACK_PACKAGE_CONTACT
+ # used in CPack.cmake as default value for `CPACK_RPM_PACKAGE_RELOCATABLE`
+ - CPACK_PACKAGE_RELOCATABLE
+ # mentioned in CPack External generator but not documented (yet)
+ - CPACK_TEMPORARY_DIRECTORY
+ - CPACK_TOPLEVEL_DIRECTORY
+ - CPACK_INSTALL_PREFIX
+ # Mentioned in `file(GET_RUNTIME_DEPENDENCIES)` docs
+ - CMAKE_OBJDUMP
+ # Mentioned in "Deprecated and Removed Features" of release notes 3.21
+ - CMAKE_SYSTEM_ARCH
+ # Superseded by `CMAKE_EXECUTABLE_ENABLE_EXPORTS`
+ - CMAKE_ENABLE_EXPORTS
+ - CMAKE_IOS_INSTALL_COMBINED # Since 3.28
+
+
+
+# https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html
+# NOTE Added to syntax file version 14 at 3.15.0 version of CMake
+environment-variables:
+ # Environment Variables that Change Behavior
+ - CMAKE_PREFIX_PATH
+ - SSL_CERT_DIR # Since 3.25
+ - SSL_CERT_FILE # Since 3.25
+ # Environment Variables that Control the Build
+ - ADSP_ROOT # Since 3.24
+ - CMAKE_APPLE_SILICON_PROCESSOR # Since 3.19.2
+ - CMAKE_BUILD_PARALLEL_LEVEL
+ - CMAKE_BUILD_TYPE # Since 3.22
+ - CMAKE_COLOR_DIAGNOSTICS # Since 3.24
+ - CMAKE_CONFIGURATION_TYPES # Since 3.22
+ - CMAKE_CONFIG_TYPE
+ - CMAKE_CROSSCOMPILING_EMULATOR # Since 3.28
+ - CMAKE_EXPORT_COMPILE_COMMANDS # Since 3.17
+ - CMAKE_GENERATOR
+ - CMAKE_GENERATOR_INSTANCE
+ - CMAKE_GENERATOR_PLATFORM
+ - CMAKE_GENERATOR_TOOLSET
+ - CMAKE_INSTALL_MODE # Since 3.22
+ - CMAKE_<LANG>_COMPILER_LAUNCHER # Since 3.17
+ - CMAKE_<LANG>_LINKER_LAUNCHER # Since 3.21
+ - CMAKE_MSVCIDE_RUN_PATH
+ - CMAKE_NO_VERBOSE
+ - CMAKE_OSX_ARCHITECTURES
+ - CMAKE_TOOLCHAIN_FILE # Since 3.21
+ - DESTDIR
+ - LDFLAGS
+ - MACOSX_DEPLOYMENT_TARGET
+ - <PackageName>_ROOT
+ - VERBOSE
+ # Environment Variables for Languages
+ - ASM<DIALECT>
+ - ASM<DIALECT>FLAGS
+ - CC
+ - CFLAGS
+ - CSFLAGS
+ - CUDAARCHS # Since 3.20
+ - CUDACXX
+ - CUDAFLAGS
+ - CUDAHOSTCXX
+ - CXX
+ - CXXFLAGS
+ - FC
+ - FFLAGS
+ - HIPCXX # Since 3.21
+ - HIPFLAGS # Since 3.21
+ - ISPC # Since 3.19
+ - ISPCFLAGS # Since 3.19
+ - OBJC # Since 3.16.7
+ - OBJCXX # Since 3.16.7
+ - RC
+ - RCFLAGS
+ - SWIFTC
+ # Environment Variables for CTest
+ - CMAKE_CONFIG_TYPE
+ - CTEST_INTERACTIVE_DEBUG_MODE
+ - CTEST_NO_TESTS_ACTION # Since 3.26
+ - CTEST_OUTPUT_ON_FAILURE
+ - CTEST_PARALLEL_LEVEL
+ - CTEST_PROGRESS_OUTPUT
+ - CTEST_USE_LAUNCHERS_DEFAULT
+ - DASHBOARD_TEST_FROM_CTEST
+ # Environment Variables for the CMake curses interface
+ - CCMAKE_COLORS
+ # Here are the `find_package` specific variables described at the
+ # https://cmake.org/cmake/help/latest/command/find_package.html
+ - <PackageName>_DIR
+ - CMAKE_FRAMEWORK_PATH
+ - CMAKE_APPBUNDLE_PATH
+ - PATH
+ # Environment Variables recognized by some standard modules
+ - QTIFWDIR
+ - CUDA_PATH # FindCUDAToolkit
scripting-commands:
-
+ name: block # Since 3.25
+ named-args: [
+ SCOPE_FOR
+ , PROPAGATE
+ ]
+ special-args: [
+ POLICIES
+ , VARIABLES
+ ]
+ start-region: block
+ -
name: break
nulary?: true
-
name: cmake_host_system_information
- named-args: [RESULT, QUERY]
+ named-args: [
+ RESULT
+ , QUERY
+ # Since 3.24
+ , WINDOWS_REGISTRY
+ , VALUE_NAMES
+ , SUBKEYS
+ , VALUE
+ , VIEW
+ , SEPARATOR
+ , ERROR_VARIABLE
+ ]
special-args: [
NUMBER_OF_LOGICAL_CORES
, NUMBER_OF_PHYSICAL_CORES
@@ -1584,7 +2903,41 @@ scripting-commands:
, OS_RELEASE
, OS_VERSION
, OS_PLATFORM
+ , MSYSTEM_PREFIX # Since 3.28
+ # Since 3.22
+ , DISTRIB_INFO
+ , DISTRIB_<name>
+ # Since 3.24
+ , '64'
+ , '32'
+ , '64_32'
+ , '32_64'
+ , HOST
+ , TARGET
+ , BOTH
+ ]
+ -
+ name: cmake_language
+ named-args: [
+ CALL
+ , EVAL
+ , CODE
+ # Since 3.19
+ , DEFER
+ , DIRECTORY
+ , ID
+ , ID_VAR
+ , GET_CALL_IDS
+ , GET_CALL
+ , CANCEL_CALL
+ # Since 3.24
+ , SET_DEPENDENCY_PROVIDER
+ , SUPPORTED_METHODS
+ # Since 3.25
+ , GET_MESSAGE_LOG_LEVEL
]
+ # Since 3.24
+ special-args: [FIND_PACKAGE, FETCHCONTENT_MAKEAVAILABLE_SERIAL]
-
name: cmake_minimum_required
named-args: [VERSION, FATAL_ERROR]
@@ -1592,12 +2945,72 @@ scripting-commands:
name: cmake_parse_arguments
named-args: [PARSE_ARGV]
-
+ name: cmake_path # Since 3.20
+ named-args: [
+ # Decomposition
+ GET
+ , ROOT_NAME
+ , ROOT_DIRECTORY
+ , ROOT_PATH
+ , FILENAME
+ , EXTENSION
+ , LAST_ONLY
+ , STEM
+ , RELATIVE_PART
+ , PARENT_PATH
+ # Query
+ , HAS_ROOT_NAME
+ , HAS_ROOT_DIRECTORY
+ , HAS_ROOT_PATH
+ , HAS_FILENAME
+ , HAS_EXTENSION
+ , HAS_STEM
+ , HAS_RELATIVE_PART
+ , HAS_PARENT_PATH
+ , IS_ABSOLUTE
+ , IS_RELATIVE
+ , IS_PREFIX
+ , NORMALIZE
+ , COMPARE # Since 3.??
+ , EQUAL # Since 3.??
+ , NOT_EQUAL # Since 3.??
+ # Modification
+ , SET
+ , APPEND
+ , OUTPUT_VARIABLE
+ , APPEND_STRING
+ , REMOVE_FILENAME
+ , REPLACE_FILENAME
+ , REMOVE_EXTENSION
+ , REPLACE_EXTENSION
+ # Generation
+ , NORMAL_PATH
+ , RELATIVE_PATH
+ , BASE_DIRECTORY
+ , ABSOLUTE_PATH
+ # Native Conversion
+ , NATIVE_PATH
+ , CONVERT
+ , TO_CMAKE_PATH_LIST
+ , TO_NATIVE_PATH_LIST
+ # Hashing
+ , HASH
+ ]
+ -
name: cmake_policy
named-args: [GET, SET, PUSH, POP, VERSION]
special-args: [OLD, NEW]
-
name: configure_file
- named-args: [COPYONLY, ESCAPE_QUOTES, "@ONLY", NEWLINE_STYLE]
+ named-args: [
+ COPYONLY
+ , ESCAPE_QUOTES
+ , NO_SOURCE_PERMISSIONS # Since 3.19
+ , "@ONLY"
+ , NEWLINE_STYLE
+ , USE_SOURCE_PERMISSIONS # Since 3.20
+ , FILE_PERMISSIONS # Since 3.20
+ ]
special-args: [UNIX, DOS, WIN32, LF, CRLF]
-
name: continue
@@ -1608,12 +3021,12 @@ scripting-commands:
NOT
, AND
, OR
+ , COMMAND
, POLICY
, TARGET
, TEST
, EXISTS
, IS_NEWER_THAN
- , IS_NEWER_THAN
, IS_DIRECTORY
, IS_SYMLINK
, IS_ABSOLUTE
@@ -1635,26 +3048,37 @@ scripting-commands:
, VERSION_GREATER_EQUAL
, IN_LIST
, DEFINED
+ , PATH_EQUAL # Since 3.24
]
+ has-target-name-after-kw: TARGET
nested-parentheses?: true
-
name: else
nulary?: true
-
+ name: endblock
+ nulary?: true
+ end-region: block
+ -
name: endforeach
nulary?: true
+ end-region: foreach
-
name: endfunction
nulary?: true
+ end-region: function
-
name: endif
nulary?: true
+ end-region: if
-
name: endmacro
nulary?: true
+ end-region: macro
-
name: endwhile
nulary?: true
+ end-region: while
-
name: execute_process
named-args: [
@@ -1670,11 +3094,26 @@ scripting-commands:
, ERROR_FILE
, OUTPUT_QUIET
, ERROR_QUIET
+ , COMMAND_ECHO # Since 3.15
, OUTPUT_STRIP_TRAILING_WHITESPACE
, ERROR_STRIP_TRAILING_WHITESPACE
, ENCODING
+ , ECHO_OUTPUT_VARIABLE # Since 3.18
+ , ECHO_ERROR_VARIABLE # Since 3.18
+ , COMMAND_ERROR_IS_FATAL # Since 3.19
]
- special-args: [NONE, AUTO, ANSI, OEM, UTF8]
+ special-args: [
+ NONE
+ , AUTO
+ , ANSI
+ , OEM
+ , UTF8
+ , STDERR # Since 3.15: the value for `COMMAND_ECHO`
+ , STDOUT # Since 3.15: the value for `COMMAND_ECHO`
+ , ANY # Since 3.19: the value for `COMMAND_ERROR_IS_FATAL`
+ , LAST # Since 3.19: the value for `COMMAND_ERROR_IS_FATAL`
+ ]
+ nested-parentheses?: true
-
name: file
named-args: [
@@ -1730,6 +3169,8 @@ scripting-commands:
, HTTPHEADER
, EXPECTED_HASH
, EXPECTED_MD5
+ , RANGE_START # Since 3.24
+ , RANGE_END # Since 3.24
, TLS_VERIFY
, TLS_CAINFO
, TIMESTAMP
@@ -1739,6 +3180,7 @@ scripting-commands:
, INPUT
, CONTENT
, CONDITION
+ , TARGET # Since 3.19
, COPY
, INSTALL
, DESTINATION
@@ -1746,6 +3188,7 @@ scripting-commands:
, DIRECTORY_PERMISSIONS
, NO_SOURCE_PERMISSIONS
, USE_SOURCE_PERMISSIONS
+ , FOLLOW_SYMLINK_CHAIN # Since 3.15
, FILES_MATCHING
, PATTERN
, REGEX
@@ -1760,6 +3203,49 @@ scripting-commands:
# Sub-options of UPLOAD/DOWNLOAD since 3.11
, NETRC
, NETRC_FILE
+ # New sub-options since 3.14
+ , READ_SYMLINK
+ , SIZE
+ # New sub-options since 3.16
+ , GET_RUNTIME_DEPENDENCIES
+ , RESOLVED_DEPENDENCIES_VAR
+ , UNRESOLVED_DEPENDENCIES_VAR
+ , EXECUTABLES
+ , LIBRARIES
+ , MODULES
+ , DIRECTORIES
+ , BUNDLE_EXECUTABLE
+ , PRE_INCLUDE_REGEXES
+ , PRE_EXCLUDE_REGEXES
+ , POST_INCLUDE_REGEXES
+ , POST_EXCLUDE_REGEXES
+ , POST_INCLUDE_FILES # Since 3.21
+ , POST_EXCLUDE_FILES # Since 3.21
+ # New sub-options since 3.18
+ , ARCHIVE_CREATE
+ , FILES
+ , FORMAT
+ , COMPRESSION
+ , COMPRESSION_LEVEL # Since 3.19
+ , MTIME
+ , VERBOSE
+ , ARCHIVE_EXTRACT
+ , LIST_ONLY
+ , CONFIGURE
+ , ESCAPE_QUOTES
+ , "@ONLY"
+ , NEWLINE_STYLE
+ # New sub-options since 3.19
+ , CHMOD
+ , CHMOD_RECURSE
+ , REAL_PATH
+ , BASE_DIRECTORY
+ # New sub-options since 3.21
+ , COPY_FILE
+ , RESULT
+ , ONLY_IF_DIFFERENT
+ , EXPAND_TILDE
+ , NO_REPLACE
]
special-args: [
UTF-8
@@ -1781,9 +3267,29 @@ scripting-commands:
, WORLD_EXECUTE
, SETUID
, SETGID
- , IGNORED # NETRC options since 3.11
+ # Special args for NETRC options since 3.11
+ , IGNORED
, OPTIONAL
, REQUIRED
+ # Special args for `FORMAT` named argument since 3.18
+ , 7zip
+ , gnutar
+ , pax
+ , paxr
+ , raw
+ , zip
+ # Special args for `TYPE` named argument since 3.18
+ , None
+ , BZip2
+ , GZip
+ , XZ
+ , Zstd
+ # Special args for `NEWLINE_STYLE` named argument since 3.18
+ , UNIX
+ , DOS
+ , WIN32
+ , LF
+ , CRLF
]
-
name: find_file
@@ -1793,6 +3299,8 @@ scripting-commands:
, PATHS
, PATH_SUFFIXES
, DOC
+ , NO_CACHE # Since 3.21
+ , REQUIRED # Since 3.18
, NO_DEFAULT_PATH
, NO_PACKAGE_ROOT_PATH
, NO_CMAKE_PATH
@@ -1802,7 +3310,12 @@ scripting-commands:
, CMAKE_FIND_ROOT_PATH_BOTH
, ONLY_CMAKE_FIND_ROOT_PATH
, NO_CMAKE_FIND_ROOT_PATH
+ , NO_CMAKE_INSTALL_PREFIX # Since 3.24
+ , REGISTRY_VIEW # Since 3.24
+ , VALIDATOR # Since 3.25
]
+ # Since 3.24
+ special-args: &reg_view_special ['64', '32', '64_32', '32_64', HOST, TARGET, BOTH]
-
name: find_library
named-args: &find_library [
@@ -1812,6 +3325,8 @@ scripting-commands:
, PATHS
, PATH_SUFFIXES
, DOC
+ , NO_CACHE # Since 3.21
+ , REQUIRED # Since 3.18
, NO_DEFAULT_PATH
, NO_PACKAGE_ROOT_PATH
, NO_CMAKE_PATH
@@ -1821,7 +3336,11 @@ scripting-commands:
, CMAKE_FIND_ROOT_PATH_BOTH
, ONLY_CMAKE_FIND_ROOT_PATH
, NO_CMAKE_FIND_ROOT_PATH
+ , NO_CMAKE_INSTALL_PREFIX # Since 3.24
+ , REGISTRY_VIEW # Since 3.24
+ , VALIDATOR # Since 3.25
]
+ special-args: *reg_view_special # Since 3.24
-
name: find_package
named-args: [
@@ -1850,31 +3369,83 @@ scripting-commands:
, CMAKE_FIND_ROOT_PATH_BOTH
, ONLY_CMAKE_FIND_ROOT_PATH
, NO_CMAKE_FIND_ROOT_PATH
+ , NO_CMAKE_INSTALL_PREFIX # Since 3.24
+ , BYPASS_PROVIDER # Since 3.24
+ , REGISTRY_VIEW # Since 3.24
+ , GLOBAL # Since 3.24
]
+ special-args: *reg_view_special # Since 3.24
-
name: find_path
named-args: *find_file
+ special-args: *reg_view_special # Since 3.24
-
name: find_program
named-args: *find_library
+ special-args: *reg_view_special # Since 3.24
-
name: foreach
- named-args: [RANGE, IN, LISTS, ITEMS]
+ named-args: [
+ RANGE
+ , IN
+ , LISTS
+ , ITEMS
+ , ZIP_LISTS # Since 3.17
+ ]
+ start-region: foreach
-
name: function
+ start-region: function
-
name: get_cmake_property
property-args: [global-properties]
+ special-args: [
+ COMMANDS
+ , COMPONENTS
+ , MACROS
+ , VARIABLES
+ , CACHE_VARIABLES
+ ]
-
name: get_directory_property
named-args: [DIRECTORY, DEFINITION]
property-args: &get_directory_property [directory-properties]
-
name: get_filename_component
- named-args: [DIRECTORY, NAME, EXT, NAME_WE, PATH, CACHE, BASE_DIR, ABSOLUTE, REALPATH, PROGRAM, PROGRAM_ARGS]
+ named-args: [
+ DIRECTORY
+ , NAME
+ , EXT
+ , NAME_WE
+ , PATH
+ , CACHE
+ , BASE_DIR
+ , ABSOLUTE
+ , REALPATH
+ , PROGRAM
+ , PROGRAM_ARGS
+ , LAST_EXT # Since 3.14
+ , NAME_WLE # Since 3.14
+ ]
-
name: get_property
- named-args: [GLOBAL, DIRECTORY, TARGET, SOURCE, INSTALL, TEST, CACHE, VARIABLE, PROPERTY, SET, DEFINED, BRIEF_DOCS, FULL_DOCS]
+ named-args: [
+ GLOBAL
+ , DIRECTORY
+ , TARGET
+ , SOURCE
+ , TARGET_DIRECTORY # `SOURCE` sub-option since 3.18
+ , DIRECTORY # `SOURCE` sub-option since 3.18
+ , INSTALL
+ , TEST
+ , CACHE
+ , VARIABLE
+ , PROPERTY
+ , SET
+ , DEFINED
+ , BRIEF_DOCS
+ , FULL_DOCS
+ ]
property-args: &get-property [
global-properties
, directory-properties
@@ -1887,7 +3458,9 @@ scripting-commands:
-
name: if
named-args: *if
+ has-target-name-after-kw: TARGET
nested-parentheses?: true
+ start-region: if
-
name: include
named-args: [OPTIONAL, RESULT_VARIABLE, NO_POLICY_SCOPE]
@@ -1933,11 +3506,24 @@ scripting-commands:
, FOR
# NOTE Another `REGEX` named arg has already added
, OUTPUT_VARIABLE
+ # Since 3.15
+ # NOTE Another `PREPEND` named arg has already added
+ , POP_FRONT
+ , POP_BACK
]
# Since 3.13
- special-args: [STRING, FILE_BASENAME, SENSITIVE, INSENSITIVE, ASCENDING, DESCENDING]
+ special-args: [
+ STRING
+ , FILE_BASENAME
+ , SENSITIVE
+ , INSENSITIVE
+ , ASCENDING
+ , DESCENDING
+ , NATURAL # Since 3.18
+ ]
-
name: macro
+ start-region: macro
-
name: mark_as_advanced
named-args: [CLEAR, FORCE]
@@ -1951,22 +3537,61 @@ scripting-commands:
special-args: [DECIMAL, HEXADECIMAL]
-
name: message
- named-args: [STATUS, WARNING, AUTHOR_WARNING, SEND_ERROR, FATAL_ERROR, DEPRECATION]
+ named-args: [
+ STATUS
+ , WARNING
+ , AUTHOR_WARNING
+ , SEND_ERROR
+ , FATAL_ERROR
+ , DEPRECATION
+ # Since 3.15
+ , NOTICE
+ , VERBOSE
+ , DEBUG
+ , TRACE
+ # Since 3.17
+ , CHECK_START
+ , CHECK_PASS
+ , CHECK_FAIL
+ # Since 3.26
+ , CONFIGURE_LOG
+ ]
-
name: option
-
name: return
- nulary?: true
+ named-args: [PROPAGATE] # Since 3.25
-
name: separate_arguments
- named-args: [NATIVE_COMMAND, UNIX_COMMAND, WINDOWS_COMMAND]
+ named-args: [
+ NATIVE_COMMAND
+ , UNIX_COMMAND
+ , WINDOWS_COMMAND
+ , PROGRAM # Since 3.19
+ , SEPARATE_ARGS # Since 3.19
+ ]
-
name: set_directory_properties
named-args: [PROPERTIES]
property-args: *get-property
-
name: set_property
- named-args: [GLOBAL, DIRECTORY, TARGET, DIRECTORY, SOURCE, INSTALL, TEST, CACHE, VARIABLE, APPEND, APPEND_STRING, PROPERTY]
+ named-args: [
+ GLOBAL
+ , DIRECTORY
+ , TARGET
+ , DIRECTORY
+ , SOURCE
+ , TARGET_DIRECTORY # `SOURCE` sub-option since 3.18
+ , DIRECTORY # `SOURCE` sub-option since 3.18
+ , INSTALL
+ , TEST
+ , CACHE
+ , VARIABLE
+ , APPEND
+ , APPEND_STRING
+ , PROPERTY
+ ]
property-args: *get-property
-
name: set
@@ -1993,6 +3618,7 @@ scripting-commands:
, SUBSTRING
, STRIP
, GENEX_STRIP
+ , REPEAT # Since 3.15
, COMPARE
, LESS
, GREATER
@@ -2011,6 +3637,7 @@ scripting-commands:
, SHA3_384
, SHA3_512
, ASCII
+ , HEX # Since 3.18
, CONFIGURE
, "@ONLY"
, ESCAPE_QUOTES
@@ -2026,6 +3653,16 @@ scripting-commands:
, NAME
, TYPE
, UPPER
+ # Since 3.19
+ , JSON
+ , ERROR_VARIABLE
+ , GET
+ , TYPE
+ , MEMBER
+ , LENGTH
+ , REMOVE
+ , SET
+ , EQUAL
]
-
name: unset
@@ -2035,7 +3672,9 @@ scripting-commands:
-
name: while
named-args: *if
+ has-target-name-after-kw: TARGET
nested-parentheses?: true
+ start-region: while
project-commands:
-
@@ -2055,15 +3694,20 @@ project-commands:
, WORKING_DIRECTORY
, COMMENT
, DEPFILE
+ , JOB_POOL # Since 3.15
+ , JOB_SERVER_AWARE # Since 3.28
, VERBATIM
, APPEND
, USES_TERMINAL
, COMMAND_EXPAND_LISTS
+ , DEPENDS_EXPLICIT_ONLY # Since 3.27
, TARGET
, PRE_BUILD
, PRE_LINK
, POST_BUILD
]
+ nested-parentheses?: true
+ has-target-name-after-kw: TARGET
-
name: add_custom_target
named-args: [
@@ -2075,61 +3719,120 @@ project-commands:
, WORKING_DIRECTORY
, COMMENT
, DEPFILE
+ , JOB_POOL # Since 3.15
+ , JOB_SERVER_AWARE # Since 3.28
, VERBATIM
, APPEND
, USES_TERMINAL
, COMMAND_EXPAND_LISTS
, SOURCES
]
+ first-arg-is-target?: true
+ nested-parentheses?: true
-
name: add_definitions
-
name: add_dependencies
+ first-args-are-targets?: true
-
name: add_executable
named-args: [WIN32, MACOSX_BUNDLE, EXCLUDE_FROM_ALL, IMPORTED, GLOBAL, ALIAS]
+ first-arg-is-target?: true
-
name: add_library
named-args: [STATIC, SHARED, MODULE, OBJECT, EXCLUDE_FROM_ALL, IMPORTED, UNKNOWN, GLOBAL, ALIAS, INTERFACE]
+ first-arg-is-target?: true
+ has-target-name-after-kw: ALIAS
-
name: add_link_options
-
name: add_subdirectory
- named-args: [EXCLUDE_FROM_ALL]
+ named-args: [
+ EXCLUDE_FROM_ALL
+ , SYSTEM # Since 3.25
+ ]
-
name: add_test
- named-args: [NAME, COMMAND, CONFIGURATIONS, WORKING_DIRECTORY]
+ named-args: [NAME, COMMAND, COMMAND_EXPAND_LISTS, CONFIGURATIONS, WORKING_DIRECTORY]
+ nested-parentheses?: true
-
name: aux_source_directory
-
name: build_command
- named-args: [CONFIGURATION, TARGET]
+ named-args: [
+ CONFIGURATION
+ , PARALLEL_LEVEL # Since 3.21
+ , TARGET
+ ]
+ has-target-name-after-kw: TARGET
+ -
+ name: cmake_file_api # Since 3.27
+ named-args: [QUERY, API_VERSION, CODEMODEL, CACHE, CMAKEFILES, TOOLCHAINS]
-
name: create_test_sourcelist
named-args: [EXTRA_INCLUDE, FUNCTION]
-
name: define_property
- named-args: [GLOBAL, DIRECTORY, DIRECTORY, SOURCE, TEST, VARIABLE, CACHED_VARIABLE, PROPERTY, INHERITED, BRIEF_DOCS, FULL_DOCS]
+ named-args: [
+ GLOBAL
+ , DIRECTORY
+ , DIRECTORY
+ , SOURCE
+ , TEST
+ , VARIABLE
+ , CACHED_VARIABLE
+ , PROPERTY
+ , INHERITED
+ , BRIEF_DOCS
+ , FULL_DOCS
+ , INITIALIZE_FROM_VARIABLE # Since 3.23
+ ]
property-args: *get-property
-
name: enable_language
named-args: [OPTIONAL]
- special-args: [C, CXX, RC, Fortran]
+ special-args: &langs [
+ ASM
+ , ASM-ATT
+ , ASM_NASM
+ , ASM_MARMASM # Since 3.26
+ , ASM_MASM
+ , C
+ , CSharp
+ , CXX
+ , CUDA
+ , HIP # Since 3.21
+ , ISPC # Since 3.19
+ , Java
+ , OBJC # Since 3.16
+ , OBJCXX # Since 3.16
+ , RC
+ , Fortran
+ , Swift
+ ]
-
name: enable_testing
-
name: export
named-args: [EXPORT, NAMESPACE, FILE, TARGETS, APPEND, EXPORT_LINK_INTERFACE_LIBRARIES, ANDROID_MK]
+ has-target-names-after-kw: TARGETS
-
name: fltk_wrap_ui
-
name: get_source_file_property
+ named-args: [
+ # Sice 3.18
+ TARGET_DIRECTORY
+ , DIRECTORY
+ ]
property-args: &get_source_file_property [source-properties]
-
name: get_target_property
+ second-arg-is-target?: true
property-args: &get_target_property [target-properties]
-
name: get_test_property
+ named-args: [DIRECTORY] # Since 3.28
property-args: &get_test_property [test-properties]
-
name: include_directories
@@ -2151,6 +3854,7 @@ project-commands:
, EXCLUDE_FROM_ALL
, RENAME
, OPTIONAL
+ , TYPE # Since 3.20
# Installing Targets
, TARGETS
, EXPORT
@@ -2163,9 +3867,11 @@ project-commands:
, PRIVATE_HEADER
, PUBLIC_HEADER
, RESOURCE
+ , FILE_SET # Since 3.23
, INCLUDES
, NAMELINK_ONLY
, NAMELINK_SKIP
+ , RUNTIME_DEPENDENCIES # Since 3.21
# Installing Files
, FILES
, PROGRAMS
@@ -2187,6 +3893,17 @@ project-commands:
, FILE
, EXPORT_ANDROID_MK
, EXPORT_LINK_INTERFACE_LIBRARIES
+ # Installing Imported Runtime Artifacts (since 3.21)
+ , IMPORTED_RUNTIME_ARTIFACTS
+ , RUNTIME_DEPENDENCY_SET
+ # Installing Runtime Dependencies (since 3.21)
+ , PRE_INCLUDE_REGEXES
+ , PRE_EXCLUDE_REGEXES
+ , POST_INCLUDE_REGEXES
+ , POST_EXCLUDE_REGEXES
+ , POST_INCLUDE_FILES
+ , POST_EXCLUDE_FILES
+ , DIRECTORIES
]
special-args: &valid_permissions [
OWNER_READ
@@ -2201,12 +3918,13 @@ project-commands:
, SETUID
, SETGID
]
+ has-target-names-after-kw: TARGETS
-
name: link_directories
named-args: [AFTER, BEFORE]
-
name: link_libraries
- named-args: [debug, optimized, general]
+ special-args: &link_libraries_sa [debug, optimized, general]
-
name: load_cache
named-args: [READ_WITH_PREFIX, EXCLUDE, INCLUDE_INTERNALS]
@@ -2218,7 +3936,25 @@ project-commands:
, HOMEPAGE_URL # Since 3.12
, LANGUAGES
]
- special-args: [NONE, C, CXX, RC, CUDA, Fortran, ASM]
+ special-args: [
+ ASM
+ , ASM-ATT
+ , ASM_NASM
+ , ASM_MASM
+ , C
+ , CSharp
+ , CXX
+ , CUDA
+ , HIP # Since 3.21
+ , ISPC # Since 3.19
+ , Java
+ , OBJC # Since 3.16
+ , OBJCXX # Since 3.16
+ , RC
+ , Fortran
+ , Swift
+ , NONE # This one is different from `enable_language`
+ ]
-
name: qt_wrap_cpp
-
@@ -2227,15 +3963,23 @@ project-commands:
name: remove_definitions
-
name: set_source_files_properties
- named-args: [PROPERTIES]
+ named-args: [
+ PROPERTIES
+ , TARGET_DIRECTORY # Since 3.18
+ , DIRECTORY # Since 3.18
+ ]
property-args: *get_source_file_property
-
name: set_target_properties
named-args: [PROPERTIES]
property-args: *get_target_property
+ first-args-are-targets?: true # NOTE Multiple target args
-
name: set_tests_properties
- named-args: [PROPERTIES]
+ named-args: [
+ DIRECTORY # Since 3.28
+ , PROPERTIES
+ ]
property-args: *get_test_property
-
name: source_group
@@ -2243,6 +3987,7 @@ project-commands:
-
name: target_compile_definitions
named-args: &target_compile_definitions [INTERFACE, PUBLIC, PRIVATE]
+ first-arg-is-target?: true
-
name: target_compile_features
named-args: *target_compile_definitions
@@ -2253,6 +3998,8 @@ project-commands:
, cxx_std_14
, cxx_std_17
, cxx_std_20 # Since 3.12
+ , cxx_std_23 # Since 3.20
+ , cxx_std_26 # Since 3.25
, cxx_aggregate_default_initializers
, cxx_alias_templates
, cxx_alignas
@@ -2314,44 +4061,99 @@ project-commands:
, c_std_90
, c_std_99
, c_std_11
+ , c_std_17 # Since 3.21
+ , c_std_23 # Since 3.21
, c_function_prototypes
, c_restrict
, c_static_assert
, c_variadic_macros
+ # CMAKE_CUDA_KNOWN_FEATURES (since 3.17)
+ , cuda_std_03
+ , cuda_std_11
+ , cuda_std_14
+ , cuda_std_17
+ , cuda_std_20
+ , cuda_std_23 # Since 3.21
+ , cuda_std_26 # Since 3.25
]
+ first-arg-is-target?: true
-
name: target_compile_options
named-args: &target_compile_options [BEFORE, INTERFACE, PUBLIC, PRIVATE]
+ first-arg-is-target?: true
-
name: target_include_directories
- named-args: [BEFORE, SYSTEM, INTERFACE, PUBLIC, PRIVATE]
+ named-args: [
+ AFTER # Since 3.20
+ , BEFORE
+ , SYSTEM
+ , INTERFACE
+ , PUBLIC
+ , PRIVATE
+ ]
+ first-arg-is-target?: true
-
# Since 3.13
name: target_link_directories
named-args: *target_compile_options
+ first-arg-is-target?: true
-
name: target_link_libraries
named-args: *target_compile_definitions
+ special-args: *link_libraries_sa
+ first-arg-is-target?: true
-
name: target_link_options
named-args: *target_compile_definitions
+ first-arg-is-target?: true
+ -
+ # Since 3.16
+ name: target_precompile_headers
+ named-args: [INTERFACE, PUBLIC, PRIVATE, REUSE_FROM]
+ first-arg-is-target?: true
-
name: target_sources
- named-args: *target_compile_definitions
+ named-args: [
+ INTERFACE
+ , PUBLIC
+ , PRIVATE
+ # Since 3.23
+ , FILE_SET
+ , TYPE
+ , BASE_DIRS
+ , FILES
+ ]
+ special-args: [
+ HEADERS
+ , CXX_MODULES # Since 3.28
+ ]
+ first-arg-is-target?: true
-
name: try_compile
named-args: [
- RESULT_VAR
- , CMAKE_FLAGS
+ CMAKE_FLAGS
, OUTPUT_VARIABLE
, SOURCES
, COMPILE_DEFINITIONS
, LINK_LIBRARIES
+ , LINK_OPTIONS # Since 3.14
, COPY_FILE
, COPY_FILE_ERROR
, <LANG>_STANDARD
, <LANG>_STANDARD_REQUIRED
, <LANG>_EXTENSIONS
+ # Since 3.25
+ , PROJECT
+ , SOURCE_DIR
+ , BINARY_DIR
+ , TARGET
+ , NO_CACHE
+ , SOURCE_FROM_CONTENT
+ , SOURCE_FROM_VAR
+ , SOURCE_FROM_FILE
+ # Since 3.26
+ , LOG_DESCRIPTION
+ , NO_LOG
]
-
name: try_run
@@ -2361,10 +4163,28 @@ project-commands:
, CMAKE_FLAGS
, COMPILE_DEFINITIONS
, LINK_LIBRARIES
+ , LINK_OPTIONS # Since 3.14
, COMPILE_OUTPUT_VARIABLE
+ , COPY_FILE
+ , COPY_FILE_ERROR
+ , <LANG>_STANDARD
+ , <LANG>_STANDARD_REQUIRED
+ , <LANG>_EXTENSIONS
, RUN_OUTPUT_VARIABLE
, OUTPUT_VARIABLE
+ , WORKING_DIRECTORY # Since 3.20
, ARGS
+ # Since 3.25
+ , SOURCES
+ , SOURCE_FROM_CONTENT
+ , SOURCE_FROM_VAR
+ , SOURCE_FROM_FILE
+ , NO_CACHE
+ , RUN_OUTPUT_STDOUT_VARIABLE
+ , RUN_OUTPUT_STDERR_VARIABLE
+ # Since 3.26
+ , LOG_DESCRIPTION
+ , NO_LOG
]
ctest-commands:
@@ -2414,7 +4234,19 @@ ctest-commands:
named-args: [TRACK, APPEND, QUIET]
-
name: ctest_submit
- named-args: [PARTS, FILES, HTTPHEADER, RETRY_COUNT, RETRY_DELAY, RETURN_VALUE, QUIET, CDASH_UPLOAD, CDASH_UPLOAD_TYPE]
+ named-args: [
+ PARTS
+ , FILES
+ , BUILD_ID # Since 3.15
+ , SUBMIT_URL # Since 3.14
+ , HTTPHEADER
+ , RETRY_COUNT
+ , RETRY_DELAY
+ , RETURN_VALUE
+ , QUIET
+ , CDASH_UPLOAD
+ , CDASH_UPLOAD_TYPE
+ ]
-
name: ctest_test
named-args: [
@@ -2433,14 +4265,1215 @@ ctest-commands:
, PARALLEL_LEVEL
, TEST_LOAD
, SCHEDULE_RANDOM
+ , STOP_ON_FAILURE # Since 3.18
, STOP_TIME
, RETURN_VALUE
, CAPTURE_CMAKE_ERROR
+ , REPEAT # Since 3.17
, QUIET
]
+ special-args: [
+ # Since 3.17
+ UNTIL_FAIL
+ , UNTIL_PASS
+ , AFTER_TIMEOUT
+ ]
-
name: ctest_update
named-args: [SOURCE, RETURN_VALUE, QUIET]
-
name: ctest_upload
named-args: [FILES, CAPTURE_CMAKE_ERROR, QUIET]
+
+modules:
+ # The modules listed here are part of the CMake distribution:
+ # https://cmake.org/cmake/help/latest/manual/cmake-modules.7.html
+ utility:
+ - AndroidTestUtilities
+ - BundleUtilities
+ - CheckCCompilerFlag
+ - CheckCompilerFlag
+ - CheckCSourceCompiles
+ - CheckCSourceRuns
+ - CheckCXXCompilerFlag
+ - CheckCXXSourceCompiles
+ - CheckCXXSourceRuns
+ - CheckCXXSymbolExists
+ - CheckFortranCompilerFlag
+ - CheckFortranFunctionExists
+ - CheckFortranSourceCompiles
+ - CheckFortranSourceRuns
+ - CheckFunctionExists
+ - CheckIncludeFileCXX
+ - CheckIncludeFile
+ - CheckIncludeFiles
+ - CheckIPOSupported
+ - CheckLanguage
+ - CheckLibraryExists
+ - CheckLinkerFlag
+ - CheckOBJCCompilerFlag
+ - CheckOBJCSourceCompiles
+ - CheckOBJCSourceRuns
+ - CheckOBJCXXCompilerFlag
+ - CheckOBJCXXSourceCompiles
+ - CheckOBJCXXSourceRuns
+ - CheckPIESupported
+ - CheckPrototypeDefinition
+ - CheckSourceCompiles
+ - CheckSourceRuns
+ - CheckStructHasMember
+ - CheckSymbolExists
+ - CheckTypeSize
+ - CheckVariableExists
+ - CMakeAddFortranSubdirectory
+ - CMakeBackwardCompatibilityCXX
+ - CMakeDependentOption
+ - CMakeFindDependencyMacro
+ - CMakeFindFrameworks
+ # NOTE The commented modules below are not for `include()`
+ # - CMakeFindPackageMode
+ # - CMakeGraphVizOptions
+ - CMakePackageConfigHelpers
+ - CMakePrintHelpers
+ - CMakePrintSystemInformation
+ - CMakePushCheckState
+ # NOTE The commented module below is not for `include()`
+ # - CMakeVerifyManifest
+ - CPack
+ - CPackComponent
+ - CPackIFW
+ - CPackIFWConfigureFile
+ - CSharpUtilities
+ - CTest
+ - CTestCoverageCollectGCOV
+ - CTestScriptMode
+ - CTestUseLaunchers
+ - DeployQt4
+ - ExternalData
+ - ExternalProject
+ - FeatureSummary
+ - FetchContent
+ - FindPackageHandleStandardArgs
+ - FindPackageMessage
+ - FortranCInterface
+ - GenerateExportHeader
+ - GNUInstallDirs
+ - GoogleTest
+ - InstallRequiredSystemLibraries
+ - ProcessorCount
+ - SelectLibraryConfigurations
+ # NOTE The commented module below is not for `include()`
+ # - SquishTestScript
+ - TestForANSIForScope
+ - TestForANSIStreamHeaders
+ - TestForSSTREAM
+ - TestForSTDNamespace
+ - UseEcos
+ - UseJava
+ - UseSWIG
+ - UsewxWidgets
+
+ finder:
+ - FindALSA
+ - FindArmadillo
+ - FindASPELL
+ - FindAVIFile
+ - FindBacktrace
+ - FindBISON
+ - FindBLAS
+ - FindBoost
+ - FindBullet
+ - FindBZip2
+ - FindCABLE
+ - FindCoin3D
+ - FindCUDAToolkit
+ - FindCups
+ - FindCURL
+ - FindCurses
+ - FindCVS
+ - FindCxxTest
+ - FindCygwin
+ - FindDCMTK
+ - FindDevIL
+ - FindDoxygen
+ - FindEnvModules
+ - FindEXPAT
+ - FindFLEX
+ - FindFLTK
+ - FindFLTK2
+ - FindFontconfig
+ - FindFreetype
+ - FindGCCXML
+ - FindGDAL
+ - FindGettext
+ - FindGIF
+ - FindGit
+ - FindGLEW
+ - FindGLUT
+ - FindGnuplot
+ - FindGnuTLS
+ - FindGSL
+ - FindGTest
+ - FindGTK
+ - FindGTK2
+ - FindHDF5
+ - FindHg
+ - FindHSPELL
+ - FindHTMLHelp
+ - FindIce
+ - FindIconv
+ - FindIcotool
+ - FindICU
+ - FindImageMagick
+ - FindIntl
+ - FindITK
+ - FindJasper
+ - FindJava
+ - FindJNI
+ - FindJPEG
+ - FindKDE3
+ - FindKDE4
+ - FindLAPACK
+ - FindLATEX
+ - FindLibArchive
+ - FindLibinput
+ - FindLibLZMA
+ - FindLibXml2
+ - FindLibXslt
+ - FindLTTngUST
+ - FindLua
+ - FindLua50
+ - FindLua51
+ - FindMatlab
+ - FindMFC
+ - FindMotif
+ - FindMPEG
+ - FindMPEG2
+ - FindMPI
+ - FindMsys
+ - FindODBC
+ - FindOpenACC
+ - FindOpenAL
+ - FindOpenCL
+ - FindOpenGL
+ - FindOpenMP
+ - FindOpenSceneGraph
+ - FindOpenSSL
+ - FindOpenThreads
+ - Findosg
+ - Findosg_functions
+ - FindosgAnimation
+ - FindosgDB
+ - FindosgFX
+ - FindosgGA
+ - FindosgIntrospection
+ - FindosgManipulator
+ - FindosgParticle
+ - FindosgPresentation
+ - FindosgProducer
+ - FindosgQt
+ - FindosgShadow
+ - FindosgSim
+ - FindosgTerrain
+ - FindosgText
+ - FindosgUtil
+ - FindosgViewer
+ - FindosgVolume
+ - FindosgWidget
+ - FindPatch
+ - FindPerl
+ - FindPerlLibs
+ - FindPHP4
+ - FindPhysFS
+ - FindPike
+ - FindPkgConfig
+ - FindPNG
+ - FindPostgreSQL
+ - FindProducer
+ - FindProtobuf
+ - FindPython
+ - FindPython2
+ - FindPython3
+ - FindQt3
+ - FindQt4
+ - FindQuickTime
+ - FindRTI
+ - FindRuby
+ - FindSDL
+ - FindSDL_image
+ - FindSDL_mixer
+ - FindSDL_net
+ - FindSDL_sound
+ - FindSDL_ttf
+ - FindSelfPackers
+ - FindSquish
+ - FindSQLite3
+ - FindSubversion
+ - FindSWIG
+ - FindTCL
+ - FindTclsh
+ - FindTclStub
+ - FindThreads
+ - FindTIFF
+ - FindUnixCommands
+ - FindVTK
+ - FindVulkan
+ - FindWget
+ - FindWish
+ - FindwxWidgets
+ - FindX11
+ - FindXalanC
+ - FindXCTest
+ - FindXercesC
+ - FindXMLRPC
+ - FindZLIB
+
+ deprecated:
+ # Deprecated Utility Modules
+ - AddFileDependencies
+ - CMakeDetermineVSServicePack
+ - CMakeExpandImportedTargets
+ - CMakeForceCompiler
+ - CMakeParseArguments
+ - Dart # Since 3.27
+ - Documentation
+ - MacroAddFileDependencies
+ - TestCXXAcceptsFlag
+ - UseJavaClassFilelist
+ - UseJavaSymlinks
+ - UsePkgConfig
+ - Use_wxWindows
+ - WriteBasicConfigVersionFile
+ - WriteCompilerDetectionHeader
+ # Deprecated Find Modules
+ - FindCUDA
+ - FindDart # Since 3.27
+ - FindPythonInterp
+ - FindPythonLibs
+ - FindQt
+ - FindwxWindows
+ # Legacy CPack Modules
+ - CPackArchive
+ - CPackBundle
+ - CPackCygwin
+ - CPackDeb
+ - CPackDMG
+ - CPackFreeBSD
+ - CPackNSIS
+ - CPackNuGet
+ - CPackPackageMaker
+ - CPackProductBuild
+ - CPackRPM
+ - CPackWIX
+ # ATTENTION The following modules still in the list of the standard modules
+ # however, the docs claims that they have been deprecated
+ - GetPrerequisites
+ - TestBigEndian
+
+standard-module-commands:
+ # AndroidTestUtilities
+ -
+ name: android_add_test_data
+ named-args:
+ - FILES
+ - FILES_DEST
+ - LIBS
+ - LIBS_DEST
+ - DEVICE_OBJECT_STORE
+ - DEVICE_TEST_DIR
+ - NO_LINK_REGEX
+ # BundleUtilities
+ - name: fixup_bundle
+ - name: copy_and_fixup_bundle
+ - name: verify_app
+ - name: get_bundle_main_executable
+ - name: get_dotapp_dir
+ - name: get_bundle_and_executable
+ - name: get_bundle_all_executables
+ - name: get_item_key
+ - name: get_item_rpaths
+ - name: clear_bundle_keys
+ - name: set_bundle_key_values
+ - name: get_bundle_keys
+ - name: copy_resolved_item_into_bundle
+ - name: copy_resolved_framework_into_bundle
+ - name: fixup_bundle_item
+ - name: verify_bundle_prerequisites
+ - name: verify_bundle_symlinks
+ # CheckCCompilerFlag
+ - name: check_c_compiler_flag
+ # CheckCompilerFlag
+ - name: check_compiler_flag
+ # CheckCSourceCompiles
+ -
+ name: check_c_source_compiles
+ named-args: &ccsc [FAIL_REGEX]
+ # CheckCSourceRuns
+ - name: check_c_source_runs
+ # CheckCXXCompilerFlag
+ - name: check_cxx_compiler_flag
+ # CheckCXXSourceCompiles
+ -
+ name: check_cxx_source_compiles
+ named-args: *ccsc
+ # CheckCXXSourceRuns
+ - name: check_cxx_source_runs
+ # CheckCXXSymbolExists
+ - name: check_cxx_symbol_exists
+ # CheckFortranCompilerFlag
+ - name: check_fortran_compiler_flag
+ # CheckFortranFunctionExists
+ - name: check_fortran_function_exists
+ # CheckFortranSourceCompiles
+ -
+ name: check_fortran_source_compiles
+ named-args: &frse [FAIL_REGEX, SRC_EXT]
+ # CheckFortranSourceRuns
+ -
+ name: check_fortran_source_runs
+ named-args: &se [SRC_EXT]
+ # CheckFunctionExists
+ - name: check_function_exists
+ # CheckIncludeFileCXX
+ - name: check_include_file_cxx
+ # CheckIncludeFile
+ - name: check_include_file
+ # CheckIncludeFiles
+ -
+ name: check_include_files
+ named-args: &l [LANGUAGE]
+ special-args: &ccxx [C, CXX]
+ # CheckIPOSupported
+ -
+ name: check_ipo_supported
+ named-args: [RESULT, OUTPUT, LANGUAGES]
+ special-args: &ccxxf [C, CXX, Fortran]
+ # CheckLanguage
+ -
+ name: check_language
+ special-args: *langs
+ # CheckLibraryExists
+ - name: check_library_exists
+ # CheckLinkerFlag
+ - name: check_linker_flag
+ # CheckOBJCCompilerFlag
+ - name: check_objc_compiler_flag
+ # CheckOBJCSourceCompiles
+ -
+ name: check_objc_source_compiles
+ named-args: *ccsc
+ # CheckOBJCSourceRuns
+ - name: check_objc_source_runs
+ # CheckOBJCXXCompilerFlag
+ - name: check_objcxx_compiler_flag
+ # CheckOBJCXXSourceCompiles
+ -
+ name: check_objcxx_source_compiles
+ named-args: *ccsc
+ # CheckOBJCXXSourceRuns
+ - name: check_objcxx_source_runs
+ # CheckPIESupported
+ -
+ name: check_pie_supported
+ named-args: [OUTPUT_VARIABLE, LANGUAGES]
+ special-args: [
+ C, CXX, Fortran,
+ # Since 3.23
+ OBJC, OBJCXX, CUDA, HIP
+ ]
+ # CheckPrototypeDefinition
+ - name: check_prototype_definition
+ # CheckSourceCompiles
+ -
+ name: check_source_compiles
+ named-args: *frse
+ # CheckSourceRuns
+ -
+ name: check_source_runs
+ named-args: *se
+ # CheckStructHasMember
+ -
+ name: check_struct_has_member
+ named-args: *l
+ special-args: *ccxx
+ # CheckSymbolExists
+ - name: check_symbol_exists
+ # CheckTypeSize
+ -
+ name: check_type_size
+ named-args: [BUILTIN_TYPES_ONLY, LANGUAGE]
+ special-args: *ccxx
+ # CheckVariableExists
+ - name: check_variable_exists
+ # CMakeAddFortranSubdirectory
+ -
+ name: cmake_add_fortran_subdirectory
+ named-args:
+ - PROJECT
+ - ARCHIVE_DIR
+ - RUNTIME_DIR
+ - LIBRARIES
+ - LINK_LIBRARIES
+ - LINK_LIBS
+ - CMAKE_COMMAND_LINE
+ - NO_EXTERNAL_INSTALL
+ # CMakeBackwardCompatibilityCXX
+ # CMakeDependentOption
+ - name: cmake_dependent_option
+ # CMakeFindDependencyMacro
+ - name: find_dependency
+ # CMakeFindFrameworks
+ # CMakeFindPackageMode
+ # CMakeGraphVizOptions
+ # CMakePackageConfigHelpers
+ -
+ name: configure_package_config_file
+ named-args:
+ - INSTALL_DESTINATION
+ - PATH_VARS
+ - NO_SET_AND_CHECK_MACRO
+ - NO_CHECK_REQUIRED_COMPONENTS_MACRO
+ - INSTALL_PREFIX
+ -
+ name: write_basic_package_version_file
+ named-args:
+ - VERSION
+ - COMPATIBILITY
+ - ARCH_INDEPENDENT
+ special-args: [AnyNewerVersion, SameMajorVersion, SameMinorVersion, ExactVersion]
+ # CMakePrintHelpers
+ -
+ name: cmake_print_properties
+ named-args:
+ - TARGETS
+ - SOURCES
+ - DIRECTORIES
+ - TESTS
+ - CACHE_ENTRIES
+ - PROPERTIES
+ has-target-names-after-kw: TARGETS
+ # CMakePrintSystemInformation
+ # CMakePushCheckState
+ -
+ name: cmake_push_check_state
+ named-args: [RESET]
+ - name: cmake_pop_check_state
+ - name: cmake_reset_check_state
+ # CMakeVerifyManifest
+ # CPack
+ # CPackComponent
+ -
+ name: cpack_add_component
+ named-args:
+ - DISPLAY_NAME
+ - DESCRIPTION
+ - HIDDEN
+ - REQUIRED
+ - DISABLED
+ - GROUP
+ - DEPENDS
+ - INSTALL_TYPES
+ - DOWNLOADED
+ - ARCHIVE_FILE
+ - PLIST
+ -
+ name: cpack_add_component_group
+ named-args:
+ - DISPLAY_NAME
+ - DESCRIPTION
+ - PARENT_GROUP
+ - EXPANDED
+ - BOLD_TITLE
+ -
+ name: cpack_add_install_type
+ named-args: [DISPLAY_NAME]
+ -
+ name: cpack_configure_downloads
+ named-args:
+ - UPLOAD_DIRECTORY
+ - ALL
+ - ADD_REMOVE
+ - NO_ADD_REMOVE
+ # CPackIFW
+ -
+ name: cpack_ifw_configure_component
+ named-args:
+ - COMMON
+ - ESSENTIAL
+ - VIRTUAL
+ - FORCED_INSTALLATION
+ - REQUIRES_ADMIN_RIGHTS]
+ - NAME
+ - DISPLAY_NAME
+ - DESCRIPTION
+ - UPDATE_TEXT
+ - VERSION
+ - RELEASE_DATE
+ - SCRIPT
+ - PRIORITY
+ - SORTING_PRIORITY
+ - DEPENDS
+ - DEPENDENCIES
+ - AUTO_DEPEND_ON
+ - LICENSES
+ - DEFAULT
+ - USER_INTERFACES
+ - TRANSLATIONS
+ - REPLACES
+ - CHECKABLE
+ -
+ name: cpack_ifw_configure_component_group
+ named-args:
+ - VIRTUAL
+ - FORCED_INSTALLATION
+ - REQUIRES_ADMIN_RIGHTS
+ - NAME
+ - DISPLAY_NAME
+ - DESCRIPTION
+ - UPDATE_TEXT
+ - VERSION
+ - RELEASE_DATE
+ - SCRIPT
+ - PRIORITY
+ - SORTING_PRIORITY
+ - DEPENDS
+ - DEPENDENCIES
+ - AUTO_DEPEND_ON
+ - LICENSES
+ - DEFAULT
+ - USER_INTERFACES
+ - TRANSLATIONS
+ - REPLACES
+ - CHECKABLE
+ -
+ name: cpack_ifw_add_repository
+ named-args:
+ - DISABLED
+ - URL
+ - USERNAME
+ - PASSWORD
+ - DISPLAY_NAME
+ -
+ name: cpack_ifw_update_repository
+ named-args:
+ - ADD
+ - REMOVE
+ - URL
+ - REPLACE
+ - OLD_URL
+ - NEW_URL
+ - USERNAME
+ - PASSWORD
+ - DISPLAY_NAME
+ - name: cpack_ifw_add_package_resources
+ # CPackIFWConfigureFile
+ - name: cpack_ifw_configure_file
+ # CSharpUtilities
+ - name: csharp_set_windows_forms_properties
+ - name: csharp_set_designer_cs_properties
+ - name: csharp_set_xaml_cs_properties
+ - name: csharp_get_filename_keys
+ - name: csharp_get_filename_key_base
+ - name: csharp_get_dependentupon_name
+ # CTest
+ # CTestCoverageCollectGCOV
+ -
+ name: ctest_coverage_collect_gcov
+ named-args:
+ - TARBALL
+ - TARBALL_COMPRESSION
+ - SOURCE
+ - BUILD
+ - GCOV_COMMAND
+ - GCOV_OPTIONS
+ - GLOB
+ - DELETE
+ - QUIET
+ # CTestScriptMode
+ # CTestUseLaunchers
+ # Dart
+ # DeployQt4
+ - name: write_qt4_conf
+ - name: resolve_qt4_paths
+ - name: fixup_qt4_executable
+ - name: install_qt4_plugin_path
+ - name: install_qt4_plugin
+ - name: install_qt4_executable
+ # ExternalData
+ - name: ExternalData_Expand_Arguments
+ - name: ExternalData_Add_Test
+ -
+ name: ExternalData_Add_Target
+ named-args: [SHOW_PROGRESS]
+ # ExternalProject
+ -
+ name: ExternalProject_Add
+ named-args:
+ - PREFIX
+ - TMP_DIR
+ - STAMP_DIR
+ - LOG_DIR
+ - DOWNLOAD_DIR
+ - SOURCE_DIR
+ - BINARY_DIR
+ - INSTALL_DIR
+ - DOWNLOAD_COMMAND
+ - URL
+ - URL_HASH
+ - URL_MD5
+ - DOWNLOAD_NAME
+ - DOWNLOAD_EXTRACT_TIMESTAMP # Since 3.24
+ - DOWNLOAD_NO_EXTRACT
+ - DOWNLOAD_NO_PROGRESS
+ - TIMEOUT
+ - INACTIVITY_TIMEOUT
+ - HTTP_USERNAME
+ - HTTP_PASSWORD
+ - HTTP_HEADER
+ - TLS_VERIFY
+ - TLS_CAINFO
+ - NETRC
+ - NETRC_FILE
+ - GIT_REPOSITORY
+ - GIT_TAG
+ - GIT_REMOTE_NAME
+ - GIT_SUBMODULES
+ - GIT_SUBMODULES_RECURSE
+ - GIT_SHALLOW
+ - GIT_PROGRESS
+ - GIT_CONFIG
+ - GIT_REMOTE_UPDATE_STRATEGY
+ - SVN_REPOSITORY
+ - SVN_REVISION
+ - SVN_USERNAME
+ - SVN_PASSWORD
+ - SVN_TRUST_CERT
+ - HG_REPOSITORY
+ - HG_TAG
+ - CVS_REPOSITORY
+ - CVS_MODULE
+ - CVS_TAG
+ - UPDATE_COMMAND
+ - UPDATE_DISCONNECTED
+ - PATCH_COMMAND
+ - CONFIGURE_COMMAND
+ - CMAKE_COMMAND
+ - CMAKE_GENERATOR
+ - CMAKE_GENERATOR_PLATFORM
+ - CMAKE_GENERATOR_TOOLSET
+ - CMAKE_GENERATOR_INSTANCE
+ - CMAKE_ARGS
+ - CMAKE_CACHE_ARGS
+ - CMAKE_CACHE_DEFAULT_ARGS
+ - SOURCE_SUBDIR
+ - CONFIGURE_HANDLED_BY_BUILD
+ - BUILD_COMMAND
+ - BUILD_IN_SOURCE
+ - BUILD_ALWAYS
+ - BUILD_BYPRODUCTS
+ - BUILD_JOB_SERVER_AWARE # Since 3.28
+ - INSTALL_COMMAND
+ - TEST_COMMAND
+ - TEST_BEFORE_INSTALL
+ - TEST_AFTER_INSTALL
+ - TEST_EXCLUDE_FROM_MAIN
+ - LOG_DOWNLOAD
+ - LOG_UPDATE
+ - LOG_PATCH
+ - LOG_CONFIGURE
+ - LOG_BUILD
+ - LOG_INSTALL
+ - LOG_TEST
+ - LOG_MERGED_STDOUTERR
+ - LOG_OUTPUT_ON_FAILURE
+ - USES_TERMINAL_DOWNLOAD
+ - USES_TERMINAL_UPDATE
+ - USES_TERMINAL_PATCH # Since 3.23
+ - USES_TERMINAL_CONFIGURE
+ - USES_TERMINAL_BUILD
+ - USES_TERMINAL_INSTALL
+ - USES_TERMINAL_TEST
+ - DEPENDS
+ - EXCLUDE_FROM_ALL
+ - STEP_TARGETS
+ - INDEPENDENT_STEP_TARGETS
+ - LIST_SEPARATOR
+ - COMMAND
+ - INSTALL_BYPRODUCTS # Since 3.26
+ special-args: [IGNORED, OPTIONAL, REQUIRED, CHECKOUT, REBASE, REBASE_CHECKOUT]
+ property-args: *get_target_property
+ - name: ExternalProject_Get_Property
+ -
+ name: ExternalProject_Add_Step
+ named-args:
+ - COMMAND
+ - COMMENT
+ - DEPENDEES
+ - DEPENDERS
+ - DEPENDS
+ - INDEPENDENT
+ - BYPRODUCTS
+ - ALWAYS
+ - JOB_SERVER_AWARE # Since 3.28
+ - EXCLUDE_FROM_MAIN
+ - WORKING_DIRECTORY
+ - LOG
+ - USES_TERMINAL
+ -
+ name: ExternalProject_Add_StepTargets
+ named-args:
+ - NO_DEPENDS
+ - name: ExternalProject_Add_StepDependencies
+ # FeatureSummary
+ -
+ name: feature_summary
+ named-args:
+ - FILENAME
+ - APPEND
+ - VAR
+ - INCLUDE_QUIET_PACKAGES
+ - FATAL_ON_MISSING_REQUIRED_PACKAGES
+ - DESCRIPTION
+ - DEFAULT_DESCRIPTION
+ - QUIET_ON_EMPTY
+ - WHAT
+ - ALL
+ - PACKAGES_FOUND
+ - PACKAGES_NOT_FOUND
+ - ENABLED_FEATURES
+ - DISABLED_FEATURES
+ -
+ name: set_package_properties
+ named-args:
+ - PROPERTIES
+ - URL
+ - DESCRIPTION
+ - TYPE
+ - RUNTIME
+ - OPTIONAL
+ - RECOMMENDED
+ - REQUIRED
+ - PURPOSE
+ - name: add_feature_info
+ - name: set_package_info
+ - name: set_feature_info
+ - name: print_enabled_features
+ - name: print_disabled_features
+ # FetchContent
+ -
+ name: FetchContent_Declare
+ named-args:
+ - DOWNLOAD_COMMAND
+ - URL
+ - URL_HASH
+ - URL_MD5
+ - DOWNLOAD_NAME
+ - DOWNLOAD_NO_EXTRACT
+ - DOWNLOAD_NO_PROGRESS
+ - TIMEOUT
+ - INACTIVITY_TIMEOUT
+ - HTTP_USERNAME
+ - HTTP_PASSWORD
+ - HTTP_HEADER
+ - TLS_VERIFY
+ - TLS_CAINFO
+ - NETRC
+ - NETRC_FILE
+ - GIT_REPOSITORY
+ - GIT_TAG
+ - GIT_REMOTE_NAME
+ - GIT_SUBMODULES
+ - GIT_SUBMODULES_RECURSE
+ - GIT_SHALLOW
+ - GIT_PROGRESS
+ - GIT_CONFIG
+ - GIT_REMOTE_UPDATE_STRATEGY
+ - SVN_REPOSITORY
+ - SVN_REVISION
+ - SVN_USERNAME
+ - SVN_PASSWORD
+ - SVN_TRUST_CERT
+ - HG_REPOSITORY
+ - HG_TAG
+ - CVS_REPOSITORY
+ - CVS_MODULE
+ - CVS_TAG
+ - UPDATE_COMMAND
+ - UPDATE_DISCONNECTED
+ - PATCH_COMMAND
+ - SOURCE_SUBDIR
+ - OVERRIDE_FIND_PACKAGE
+ - FIND_PACKAGE_ARGS
+ - SYSTEM
+ - EXCLUDE_FROM_ALL # Since 3.28
+ -
+ name: FetchContent_Populate
+ named-args:
+ - QUIET
+ - SUBBUILD_DIR
+ - SOURCE_DIR
+ - BINARY_DIR
+ - DOWNLOAD_COMMAND
+ - URL
+ - URL_HASH
+ - URL_MD5
+ - DOWNLOAD_NAME
+ - DOWNLOAD_NO_EXTRACT
+ - DOWNLOAD_NO_PROGRESS
+ - TIMEOUT
+ - INACTIVITY_TIMEOUT
+ - HTTP_USERNAME
+ - HTTP_PASSWORD
+ - HTTP_HEADER
+ - TLS_VERIFY
+ - TLS_CAINFO
+ - NETRC
+ - NETRC_FILE
+ - GIT_REPOSITORY
+ - GIT_TAG
+ - GIT_REMOTE_NAME
+ - GIT_SUBMODULES
+ - GIT_SUBMODULES_RECURSE
+ - GIT_SHALLOW
+ - GIT_PROGRESS
+ - GIT_CONFIG
+ - GIT_REMOTE_UPDATE_STRATEGY
+ - SVN_REPOSITORY
+ - SVN_REVISION
+ - SVN_USERNAME
+ - SVN_PASSWORD
+ - SVN_TRUST_CERT
+ - HG_REPOSITORY
+ - HG_TAG
+ - CVS_REPOSITORY
+ - CVS_MODULE
+ - CVS_TAG
+ - UPDATE_COMMAND
+ - UPDATE_DISCONNECTED
+ - PATCH_COMMAND
+ - SOURCE_SUBDIR
+ -
+ name: FetchContent_GetProperties
+ named-args:
+ - SOURCE_DIR
+ - BINARY_DIR
+ - POPULATED
+ - name: FetchContent_MakeAvailable
+ # FindPackageHandleStandardArgs
+ -
+ name: find_package_handle_standard_args
+ named-args:
+ - DEFAULT_MSG
+ - FOUND_VAR
+ - REQUIRED_VARS
+ - VERSION_VAR
+ - HANDLE_VERSION_RANGE
+ - HANDLE_COMPONENTS
+ - CONFIG_MODE
+ - NAME_MISMATCHED
+ - REASON_FAILURE_MESSAGE
+ - FAIL_MESSAGE
+ -
+ name: find_package_check_version
+ named-args:
+ - HANDLE_VERSION_RANGE
+ - RESULT_MESSAGE_VARIABLE
+ # FindPackageMessage
+ - name: find_package_message
+ # FortranCInterface
+ -
+ name: FortranCInterface_HEADER
+ named-args:
+ - MACRO_NAMESPACE
+ - SYMBOL_NAMESPACE
+ - SYMBOLS
+ -
+ name: FortranCInterface_VERIFY
+ named-args: [CXX, QUIET]
+ # GenerateExportHeader
+ -
+ name: generate_export_header
+ named-args:
+ - BASE_NAME
+ - EXPORT_MACRO_NAME
+ - EXPORT_FILE_NAME
+ - DEPRECATED_MACRO_NAME
+ - NO_EXPORT_MACRO_NAME
+ - INCLUDE_GUARD_NAME
+ - STATIC_DEFINE
+ - NO_DEPRECATED_MACRO_NAME
+ - DEFINE_NO_DEPRECATED
+ - PREFIX_NAME
+ - CUSTOM_CONTENT_FROM_VARIABLE
+ first-arg-is-target?: true
+ # GetPrerequisites (NOTE The module has functions but has been deprecated)
+ # GNUInstallDirs
+ - name: GNUInstallDirs_get_absolute_install_dir
+ # GoogleTest
+ -
+ name: gtest_add_tests
+ named-args:
+ - TARGET
+ - SOURCES
+ - EXTRA_ARGS
+ - WORKING_DIRECTORY
+ - TEST_PREFIX
+ - TEST_SUFFIX
+ - SKIP_DEPENDENCY
+ - TEST_LIST
+ has-target-name-after-kw: TARGET
+ -
+ name: gtest_discover_tests # Since 3.10
+ named-args:
+ - EXTRA_ARGS
+ - WORKING_DIRECTORY
+ - TEST_PREFIX
+ - TEST_SUFFIX
+ - TEST_FILTER # Since 3.22
+ - NO_PRETTY_TYPES
+ - NO_PRETTY_VALUES
+ - PROPERTIES
+ - TEST_LIST
+ - DISCOVERY_TIMEOUT
+ - XML_OUTPUT_DIR
+ - DISCOVERY_MODE
+ special-args: [POST_BUILD, PRE_TEST]
+ first-arg-is-target?: true
+ # InstallRequiredSystemLibraries
+ # ProcessorCount
+ - name: processorcount
+ # SelectLibraryConfigurations
+ - name: select_library_configurations
+ # SquishTestScript
+ # TestBigEndian
+ - name: test_big_endian
+ # TestForANSIForScope
+ # TestForANSIStreamHeaders
+ # TestForSSTREAM
+ # TestForSTDNamespace
+ # UseEcos
+ - name: ecos_add_include_directories
+ - name: ecos_add_executable
+ - name: ecos_add_target_lib
+ - name: ecos_adjust_directory
+ - name: ecos_use_arm_elf_tools
+ - name: ecos_use_i386_elf_tools
+ - name: ecos_use_ppc_eabi_tools
+ # UseJava
+ -
+ name: add_jar
+ named-args:
+ - SOURCES
+ - RESOURCES
+ - NAMESPACE
+ - INCLUDE_JARS
+ - ENTRY_POINT
+ - VERSION
+ - MANIFEST
+ - OUTPUT_NAME
+ - OUTPUT_DIR
+ - GENERATE_NATIVE_HEADERS
+ - DESTINATION
+ - INSTALL
+ - BUILD
+ first-arg-is-target?: true
+ -
+ name: install_jar
+ named-args: &dc [DESTINATION, COMPONENT]
+ first-arg-is-target?: true
+ -
+ name: install_jni_symlink
+ named-args: *dc
+ first-arg-is-target?: true
+ -
+ name: create_javah
+ named-args:
+ - TARGET
+ - GENERATED_FILES
+ - CLASSES
+ - CLASSPATH
+ - DEPENDS
+ - OUTPUT_NAME
+ - OUTPUT_DIR
+ has-target-name-after-kw: TARGET
+ -
+ name: install_jar_exports
+ named-args:
+ - TARGETS
+ - NAMESPACE
+ - FILE
+ - DESTINATION
+ - COMPONENT
+ has-target-names-after-kw: TARGETS
+ -
+ name: export_jars
+ named-args:
+ - TARGETS
+ - NAMESPACE
+ - FILE
+ has-target-names-after-kw: TARGETS
+ -
+ name: find_jar
+ named-args:
+ - NAMES
+ - PATHS
+ - ENV
+ - VERSIONS
+ - DOC
+ -
+ name: create_javadoc
+ named-args:
+ - PACKAGES
+ - FILES
+ - SOURCEPATH
+ - CLASSPATH
+ - INSTALLPATH
+ - DOCTITLE
+ - WINDOWTITLE
+ - AUTHOR
+ - USE
+ - VERSION
+ # UseSWIG
+ -
+ name: swig_add_library
+ named-args:
+ - TYPE
+ - SHARED
+ - MODULE
+ - STATIC
+ - USE_BUILD_SHARED_LIBS
+ - LANGUAGE
+ - NO_PROXY
+ - OUTPUT_DIR
+ - OUTFILE_DIR
+ - SOURCES
+ - name: swig_link_libraries
+ # UsewxWidgets
+ # NOTE Some standard finder modules also provide commands
+ # FindSquish
+ -
+ name: squish_add_test
+ named-args:
+ - AUT
+ - SUITE
+ - TEST
+ - SETTINGSGROUP
+ - PRE_COMMAND
+ - POST_COMMAND
+ # FindBISON
+ -
+ name: bison_target
+ named-args:
+ - COMPILE_FLAGS
+ - DEFINES_FILE
+ - VERBOSE
+ - REPORT_FILE
+ # FindCxxTest
+ - name: cxxtest_add_test
+ # FindDoxygen
+ -
+ name: doxygen_add_docs
+ named-args:
+ - ALL
+ - USE_STAMP_FILE
+ - WORKING_DIRECTORY
+ - COMMENT
+ - CONFIG_FILE # Since 3.27
+ # FindEnvModules
+ -
+ name: env_module
+ named-args: [COMMAND, OUTPUT_VARIABLE, RESULT_VARIABLE]
+ -
+ name: env_module_swap
+ named-args: [OUTPUT_VARIABLE, RESULT_VARIABLE]
+ - name: env_module_list
+ - name: env_module_avail
+ # FindFLEX
+ -
+ name: flex_target
+ named-args:
+ - COMPILE_FLAGS
+ - DEFINES_FILE
+ - name: add_flex_bison_dependency
+ # FindGettext
+ -
+ name: gettext_create_translations
+ named-args: [ALL]
+ -
+ name: gettext_process_pot_file
+ named-args: [ALL, INSTALL_DESTINATION, LANGUAGES]
+ -
+ name: gettext_process_po_files
+ named-args: [ALL, INSTALL_DESTINATION, PO_FILES]
+ # FindHg
+ - name: hg_wc_info
+ # FindMatlab
+ - name: matlab_get_version_from_release_name
+ - name: matlab_get_release_name_from_version
+ - name: matlab_extract_all_installed_versions_from_registry
+ - name: matlab_get_all_valid_matlab_roots_from_registry
+ - name: matlab_get_mex_suffix
+ - name: matlab_get_version_from_matlab_run
+ -
+ name: matlab_add_unit_test
+ named-args:
+ - NAME
+ - UNITTEST_FILE
+ - CUSTOM_TEST_COMMAND
+ - UNITTEST_PRECOMMAND
+ - TIMEOUT
+ - ADDITIONAL_PATH
+ - MATLAB_ADDITIONAL_STARTUP_OPTIONS
+ - TEST_ARGS
+ - NO_UNITTEST_FRAMEWORK
+ -
+ name: matlab_add_mex
+ named-args:
+ - NAME
+ - EXECUTABLE
+ - MODULE
+ - SHARED
+ - SRC
+ - OUTPUT_NAME
+ - DOCUMENTATION
+ - LINK_TO
+ - R2017b
+ - R2018a
+ - EXCLUDE_FROM_ALL
+ - NO_IMPLICIT_LINK_TO_MATLAB_LIBRARIES # Since 3.24
+ # FindPkgConfig
+ -
+ name: pkg_check_modules
+ named-args: &pkgcm
+ - REQUIRED
+ - QUIET
+ - NO_CMAKE_PATH
+ - NO_CMAKE_ENVIRONMENT_PATH
+ - IMPORTED_TARGET
+ - GLOBAL
+ - STATIC_TARGET # Since 3.24
+ -
+ name: pkg_search_module
+ named-args: *pkgcm
+ -
+ name: pkg_get_variable
+ named-args: [DEFINE_VARIABLES] # Since 3.28
+ # FindProtobuf
+ -
+ name: protobuf_generate_cpp
+ named-args: [DESCRIPTORS, EXPORT_MACRO]
+ -
+ name: protobuf_generate_python
+ # FindPython
+ -
+ name: Python_add_library
+ named-args: [STATIC, SHARED, MODULE, WITH_SOABI]
+ # FindSubversion
+ -
+ name: Subversion_WC_INFO
+ named-args: [IGNORE_SVN_FAILURE]
+ -
+ name: Subversion_WC_LOG
+ # FindXCTest
+ - name: xctest_add_bundle
+ - name: xctest_add_test
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/generate-cmake-syntax.py b/src/libs/3rdparty/syntax-highlighting/data/generators/generate-cmake-syntax.py
index 61f60a682d..0d9597320e 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/generators/generate-cmake-syntax.py
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/generate-cmake-syntax.py
@@ -3,26 +3,31 @@
#
# Generate Kate syntax file for CMake
#
-# Copyright (c) 2017, Alex Turbov <i.zaufi@gmail.com>
+# SPDX-FileCopyrightText: 2017-2023 Alex Turbov <i.zaufi@gmail.com>
#
# To install prerequisites:
#
-# $ pip install --user click jinja2 yaml
+# $ pip install --user click jinja2 lxml pyyaml
#
# To use:
#
# $ ./generate-cmake-syntax.py cmake.yaml > ../syntax/cmake.xml
#
+
+from __future__ import annotations
+
+import functools
+import re
+from dataclasses import dataclass, field
+
import click
import jinja2
-import pathlib
-import re
import yaml
-
-import pprint
+import sys
+from lxml import etree
-_TEMPLATED_NAME = re.compile('<[^>]+>')
+_TEMPLATED_NAME = re.compile(r'(?:<[^>]+>)')
_PROPERTY_KEYS = [
'global-properties'
, 'directory-properties'
@@ -33,18 +38,239 @@ _PROPERTY_KEYS = [
, 'install-properties'
]
_KW_RE_LIST = ['kw', 're']
-
-
-def try_transform_placeholder_string_to_regex(name):
+_VAR_KIND_LIST = ['variables', 'deprecated-or-internal-variables', 'environment-variables']
+_CONTROL_FLOW_LIST = {
+ 'break'
+ , 'continue'
+ , 'elseif'
+ , 'else'
+ , 'endforeach'
+ , 'endif'
+ , 'endwhile'
+ , 'foreach'
+ , 'if'
+ , 'return'
+ , 'while'
+ }
+_VAR_REF_ENTITY = '&var_ref_re;'
+
+_HEURISTICS = [
+ (
+ {'MAX(_(COUNT|MAJOR|MINOR|PATCH|TWEAK))?', 'MIN(_(COUNT|MAJOR|MINOR|PATCH|TWEAK))?'}
+ , 'M(AX|IN)(_(COUNT|MAJOR|MINOR|PATCH|TWEAK))?'
+ )
+ , ({'OUTPUTS', 'OUTPUT_(HEADER|SOURCE)'}, 'OUTPUT(S|_(HEADER|SOURCE))')
+ , ({'PREFIX', 'SUFFIX'}, '(PRE|SUF)FIX')
+ , ({'CPPCHECK', 'CPPLINT'}, 'CPP(CHECK|LINT)')
+ , ({'DEPENDS', 'PREDEPENDS'}, '(PRE)?DEPENDS')
+ , ({'ICON', 'ICONURL'}, 'ICON(URL)?')
+ , (
+ {
+ '&var%ref%re;(_INIT)?'
+ , 'DEBUG(_INIT)?'
+ , 'MINSIZEREL(_INIT)?'
+ , 'RELEASE(_INIT)?'
+ , 'RELWITHDEBINFO(_INIT)?'
+ }
+ , '(DEBUG|MINSIZEREL|REL(EASE|WITHDEBINFO)|&var%ref%re;)(_INIT)?'
+ )
+ , ({'RELEASE', 'RELWITHDEBINFO'}, 'REL(EASE|WITHDEBINFO)')
+ , ({'POST', 'POSTUN', 'PRE', 'PREUN'}, 'P(RE|OST)(UN)?')
+ , ({'AUTOPROV', 'AUTOREQ', 'AUTOREQPROV'}, 'AUTO(PROV|REQ(PROV)?)')
+ , ({'DEFINITIONS', 'OPTIONS'}, '(DEFINI|OP)TIONS')
+ , ({'LIB_NAMES', 'LIBRARY'}, 'LIB(_NAMES|RARY)')
+ , ({'EXTENSIONS', 'EXTRA_FLAGS'}, 'EXT(ENSIONS|RA_FLAGS)')
+ , ({'DISABLED', 'DISPLAY_NAME'}, 'DIS(ABLED|PLAY_NAME)')
+ , ({'LIBRARIES', 'LINK_LIBRARIES', 'STATIC_LINK_LIBRARIES'}, '((STATIC_)?LINK_)?LIBRARIES')
+ , ({'INCLUDE_DIRS', 'LIBRARY_DIRS'}, '(INCLUDE|LIBRARY)_DIRS')
+ , ({'BINARY_DIR', 'SOURCE_DIR'}, '(BINARY|SOURCE)_DIR')
+ , ({'CFLAGS(_OTHER)?', 'LDFLAGS(_OTHER)?'}, '(C|LD)FLAGS(_OTHER)?')
+ , ({'INCLUDE_DIRECTORIES', 'LIBRARIES'}, '(INCLUDE_DIRECTO|LIBRA)RIES')
+ , ({'POSTFLIGHT_&var%ref%re;_SCRIPT', 'PREFLIGHT_&var%ref%re;_SCRIPT'}, 'P(RE|OST)FLIGHT_&var%ref%re;_SCRIPT')
+ , ({'DIRECTORIES', 'FRAMEWORK_DIRECTORIES'}, '(FRAMEWORK_)?DIRECTORIES')
+ , ({'FILE_FLAG', 'FILE'}, 'FILE(_FLAG)?')
+ , ({'DIR_PERMISSIONS', 'FILE_PERMISSIONS'}, '(DIR|FILE)_PERMISSIONS')
+ , ({'COMPILER_LAUNCHER', 'LINKER_LAUNCHER'}, '(COMPIL|LINK)ER_LAUNCHER')
+ , ({'COMPILER', 'COMPILE_(DEFINI|OP)TIONS'}, 'COMPILE(R|_(DEFINI|OP)TIONS)')
+ , ({'LICENSEURL', 'LICENSE_(EXPRESSION|FILE_NAME)'}, 'LICENSE(URL|_(EXPRESSION|FILE_NAME))')
+ , ({'NO_SONAME', 'SONAME'}, '(NO_)?SONAME')
+ , ({'CODE_SIGN_ON_COPY', 'REMOVE_HEADERS_ON_COPY'}, '(CODE_SIGN|REMOVE_HEADERS)_ON_COPY')
+ , ({'(REFERENCE|REFERENCEPROP_&var%ref%re;_TAG)_&var%ref%re;'}, 'REFERENCE(PROP_&var%ref%re;_TAG)?_&var%ref%re;')
+ , ({'DISABLE_FIND_PACKAGE', 'REQUIRE_FIND_PACKAGE'}, '(DISABLE|REQUIRE)_FIND_PACKAGE')
+ , (
+ {'GROUP_USING_&var%ref%re;(_SUPPORTED)?', 'LIBRARY_USING_&var%ref%re;(_SUPPORTED)?'}
+ , '(GROUP|LIBRARY)_USING_&var%ref%re;(_SUPPORTED)?'
+ )
+ , (
+ {
+ 'EXE_LINKER_FLAGS_&var%ref%re;(_INIT)?'
+ , 'MODULE_LINKER_FLAGS_&var%ref%re;(_INIT)?'
+ , 'SHARED_LINKER_FLAGS_&var%ref%re;(_INIT)?'
+ , 'STATIC_LINKER_FLAGS_&var%ref%re;(_INIT)?'
+ }
+ , '(EXE|MODULE|SHARED|STATIC)_LINKER_FLAGS_&var%ref%re;(_INIT)?'
+ )
+ , (
+ {
+ 'ARCHIVE_OUTPUT_DIRECTORY'
+ , 'COMPILE_PDB_OUTPUT_DIRECTORY'
+ , 'LIBRARY_OUTPUT_DIRECTORY'
+ , 'PDB_OUTPUT_DIRECTORY'
+ , 'RUNTIME_OUTPUT_DIRECTORY'
+ }
+ , '(ARCHIVE|(COMPILE_)?PDB|LIBRARY|RUNTIME)_OUTPUT_DIRECTORY'
+ )
+ , (
+ {
+ 'ARCHIVE_OUTPUT_(DIRECTORY|NAME)'
+ , 'LIBRARY_OUTPUT_(DIRECTORY|NAME)'
+ , 'RUNTIME_OUTPUT_(DIRECTORY|NAME)'
+ }
+ , '(ARCHIVE|LIBRARY|RUNTIME)_OUTPUT_(DIRECTORY|NAME)'
+ )
+ , ({'ASM&var_ref_re;', 'ASM&var_ref_re;FLAGS'}, 'ASM&var_ref_re;(FLAGS)?')
+ , (
+ {
+ 'CMAKE_POLICY_DEFAULT_CMP[0-9]{4}'
+ , 'CMAKE_POLICY_WARNING_CMP[0-9]{4}'
+ }
+ , 'CMAKE_POLICY_(DEFAULT|WARNING)_CMP[0-9]{4}'
+ )
+ , ({'CMAKE_ARGV[0-9]+', 'CMAKE_MATCH_[0-9]+'}, 'CMAKE_(ARGV|MATCH_)[0-9]+')
+ ]
+
+@dataclass
+class RePartNode:
+ children: dict[str, RePartNode] = field(default_factory=dict, hash=False)
+ is_leaf: bool = False
+
+
+@dataclass
+class RegexCollection:
+ special_cases: list[str] = field(default_factory=list, hash=False)
+ re_tree: dict[str, RePartNode] = field(default_factory=dict, hash=False)
+
+ def add_case(self, regex: str) -> RegexCollection:
+ self.special_cases.append(regex)
+ return self
+
+ def update_tree(self, name_parts: list[str]) -> RegexCollection:
+ safe_var_ref = _VAR_REF_ENTITY.replace('_', '%')
+ current = functools.reduce(
+ lambda current, part: (
+ self.re_tree if current is None else current.children
+ ).setdefault(part, RePartNode())
+ , safe_var_ref.join(name_parts).replace(f'{safe_var_ref}_{safe_var_ref}', safe_var_ref).split('_')
+ , None
+ )
+ current.is_leaf = True
+ return self
+
+
+def try_transform_placeholder_string_to_regex(state: RegexCollection, name: str):
'''
NOTE Some placeholders are not IDs, but numbers...
`CMAKE_MATCH_<N>` 4 example
'''
- m = _TEMPLATED_NAME.split(name)
- if 'CMAKE_MATCH_' in m:
- return '\\bCMAKE_MATCH_[0-9]+\\b'
+ name_parts = _TEMPLATED_NAME.split(name)
+ match name_parts:
+ case ['CMAKE_MATCH_' as head, ''] | ['CMAKE_ARGV' as head, ''] | ['ARGV' as head, '']:
+ return state.add_case(head + '[0-9]+')
+
+ case ['CMAKE_POLICY_DEFAULT_CMP' as head, ''] | ['CMAKE_POLICY_WARNING_CMP' as head, '']:
+ return state.add_case(head + '[0-9]{4}')
+
+ case ['', '__TRYRUN_OUTPUT']:
+ return state.add_case(f'{_VAR_REF_ENTITY}__TRYRUN_OUTPUT')
+
+ case (['ASM', ''] | ['ASM', 'FLAGS']) as asm_env:
+ return state.add_case(f'{asm_env[0]}{_VAR_REF_ENTITY}{asm_env[1]}')
+
+ return state.update_tree(name_parts)
+
+
+def is_first_subset_of_second(first, second):
+ subset = set(first)
+ fullset = set(second)
+ return subset.issubset(fullset)
+
+
+def try_optimize_known_alt_groups(groups: list[str]) -> list[str]:
+ for case in _HEURISTICS:
+ if is_first_subset_of_second(case[0], groups):
+ groups = sorted([*filter(lambda item: item not in case[0], groups), case[1]])
+ return groups
+
+
+def try_optimize_trailing_var_ref_regex(groups: list[str]) -> list[str]:
+ tail_var_ref_re = '_' + _VAR_REF_ENTITY.replace('_', '%')
+ candidates = [*filter(lambda s: s.endswith(tail_var_ref_re), groups)]
+ return sorted([
+ *filter(lambda item: item not in candidates, groups)
+ , f'({"|".join(try_optimize_known_alt_groups([s[:-len(tail_var_ref_re)] for s in candidates]))}){tail_var_ref_re}'
+ ]) if len(candidates) > 1 else groups
+
+
+def build_regex(state: list[str], kv: tuple[str, RePartNode]) -> list[str]:
+ name, value = kv
+ match (value, len(value.children)):
+ case (RePartNode(children={}, is_leaf=True), 0):
+ return [*state, name]
+
+ case (node, sz) if sz > 0:
+ alt_group = try_optimize_known_alt_groups(
+ try_optimize_trailing_var_ref_regex(
+ functools.reduce(build_regex, node.children.items(), [])
+ )
+ )
+
+ match (len(alt_group), node.is_leaf):
+ case (1, False):
+ return [*state, f'{name}_{alt_group[0]}']
+
+ case (1, True):
+ return [*state, f'{name}(_{alt_group[0]})?']
+
+ case (sz, False) if sz > 0:
+ return [*state, f'{name}_({"|".join(alt_group)})']
+
+ case (sz, True) if sz > 0:
+ return [*state, f'{name}(_({"|".join(alt_group)}))?']
+
+ case _:
+ raise AssertionError('Zero children?')
+
+ case _:
+ raise AssertionError(f'NOT MATCHED: {name=}→{value=}')
- return '\\b{}\\b'.format('&id_re;'.join(list(m))) if 1 < len(m) else name
+ return state
+
+
+def try_placeholders_to_regex(names):
+ if not names:
+ return None
+
+ data = functools.reduce(
+ try_transform_placeholder_string_to_regex
+ , names
+ , RegexCollection()
+ )
+
+ return (
+ '\\b(?:'
+ + '|'.join(
+ try_optimize_known_alt_groups(
+ try_optimize_trailing_var_ref_regex(
+ functools.reduce(
+ build_regex
+ , data.re_tree.items()
+ , data.special_cases
+ )
+ )
+ )
+ ).replace('%', '_')
+ + ')\\b'
+ )
def partition_iterable(fn, iterable):
@@ -60,7 +286,7 @@ def _transform_command_set(cmd, list_name):
list_name = list_name.replace('-', '_')
cmd[list_name] = {k: sorted(set(v)) for k, v in zip(_KW_RE_LIST, [args, args_re])}
- cmd[list_name]['re'] = [*map(lambda x: try_transform_placeholder_string_to_regex(x), args_re)]
+ cmd[list_name]['re'] = try_placeholders_to_regex(args_re)
return cmd
@@ -86,19 +312,124 @@ def transform_command(cmd):
assert new_cmd == cmd
can_be_nulary = False
- cmd['nested_parentheses'] = cmd['nested-parentheses?'] if 'nested-parentheses?' in cmd else False
+ cmd['nested_parentheses'] = cmd.get('nested-parentheses?', False)
+
+ if 'first-arg-is-target?' in cmd:
+ cmd['first_arg_is_target'] = cmd['first-arg-is-target?']
+ can_be_nulary = False
+
+ if 'first-args-are-targets?' in cmd:
+ cmd['first_args_are_targets'] = cmd['first-args-are-targets?']
+ can_be_nulary = False
+
+ if 'has-target-name-after-kw' in cmd:
+ cmd['has_target_name_after_kw'] = cmd['has-target-name-after-kw']
+ can_be_nulary = False
+
+ if 'has-target-names-after-kw' in cmd:
+ cmd['has_target_names_after_kw'] = cmd['has-target-names-after-kw']
+ can_be_nulary = False
+
+ if 'second-arg-is-target?' in cmd:
+ cmd['second_arg_is_target'] = cmd['second-arg-is-target?']
+ can_be_nulary = False
if 'nulary?' in cmd and cmd['nulary?'] and not can_be_nulary:
raise RuntimeError('Command `{}` w/ args declared nulary!?'.format(cmd['name']))
+ if 'start-region' in cmd:
+ cmd['start_region'] = cmd['start-region']
+
+ if 'end-region' in cmd:
+ cmd['end_region'] = cmd['end-region']
+
+ cmd['attribute'] = 'Control Flow' if cmd['name'] in _CONTROL_FLOW_LIST else 'Command'
+
return cmd
+def remove_duplicate_list_nodes(contexts, highlighting):
+ remap = {}
+
+ items_by_kws = {}
+ # extract duplicate keyword list
+ for items in highlighting:
+ if items.tag != 'list':
+ break
+ k = '<'.join(item.text for item in items)
+ name = items.attrib['name']
+ rename = items_by_kws.get(k)
+ if rename:
+ remap[name] = rename
+ highlighting.remove(items)
+ else:
+ items_by_kws[k] = name
+
+ # update keyword list name referenced by each rule
+ for context in contexts:
+ for rule in context:
+ if rule.tag == 'keyword':
+ name = rule.attrib['String']
+ rule.attrib['String'] = remap.get(name, name)
+
+
+def remove_duplicate_context_nodes(contexts):
+ # 3 levels: ctx, ctx_op and ctx_op_nested
+ for _ in range(3):
+ remap = {}
+ duplicated = {}
+
+ # remove duplicate nodes
+ for context in contexts:
+ name = context.attrib['name']
+ context.attrib['name'] = 'dummy'
+ ref = duplicated.setdefault(etree.tostring(context), [])
+ if ref:
+ contexts.remove(context)
+ else:
+ context.attrib['name'] = name
+ ref.append(name)
+ remap[name] = ref[0]
+
+ # update context name referenced by each rule
+ for context in contexts:
+ for rule in context:
+ ref = remap.get(rule.attrib.get('context'))
+ if ref:
+ rule.attrib['context'] = ref
+
+
+def remove_duplicate_nodes(xml_string):
+ parser = etree.XMLParser(resolve_entities=False, collect_ids=False)
+ root = etree.fromstring(xml_string.encode(), parser=parser)
+ highlighting = root[0]
+
+ contexts = highlighting.find('contexts')
+
+ remove_duplicate_list_nodes(contexts, highlighting)
+ remove_duplicate_context_nodes(contexts)
+
+ # reformat comments
+ xml = etree.tostring(root)
+ xml = re.sub(b'(?=[^\n ])<!--', b'\n<!--', xml)
+ xml = re.sub(b'-->(?=[^ \n])', b'-->\n', xml)
+
+ # extract DOCTYPE removed by etree.fromstring and reformat <language>
+ doctype = xml_string[:xml_string.find('<highlighting')]
+
+ # remove unformatted <language>
+ xml = xml[xml.find(b'<highlighting'):]
+
+ # last comment removed by etree.fromstring
+ last_comment = '\n<!-- kate: replace-tabs on; indent-width 2; tab-width 2; -->'
+
+ return f'{doctype}{xml.decode()}{last_comment}'
+
+
#BEGIN Jinja filters
def cmd_is_nulary(cmd):
- assert not ('named-args' in cmd or 'special-args' in cmd or 'property-args' in cmd)
- return 'nulary?' in cmd and cmd['nulary?']
+ return cmd.setdefault('nulary?', False)
#END Jinja filters
@@ -107,13 +438,17 @@ def cmd_is_nulary(cmd):
@click.argument('input_yaml', type=click.File('r'))
@click.argument('template', type=click.File('r'), default='./cmake.xml.tpl')
def cli(input_yaml, template):
- data = yaml.load(input_yaml)
-
- # Partition `variables` list into "pure" words and regexes to match
- data['variables'] = {
- k: sorted(set(v)) for k, v in zip(_KW_RE_LIST, [*partition_iterable(lambda x: _TEMPLATED_NAME.search(x) is None, data['variables'])])
- }
- data['variables']['re'] = [*map(lambda x: try_transform_placeholder_string_to_regex(x), data['variables']['re'])]
+ data = yaml.load(input_yaml, Loader=yaml.BaseLoader)
+
+ # Partition `variables` and `environment-variables` lists into "pure" (key)words and regexes to match
+ for var_key in _VAR_KIND_LIST:
+ data[var_key] = {
+ k: sorted(set(v)) for k, v in zip(
+ _KW_RE_LIST
+ , [*partition_iterable(lambda x: _TEMPLATED_NAME.search(x) is None, data[var_key])]
+ )
+ }
+ data[var_key]['re'] = try_placeholders_to_regex(data[var_key]['re'])
# Transform properties and make all-properties list
data['properties'] = {}
@@ -122,27 +457,54 @@ def cli(input_yaml, template):
props, props_re = partition_iterable(lambda x: _TEMPLATED_NAME.search(x) is None, data[prop])
del data[prop]
- data['properties'][python_prop_list_name] = {k: sorted(set(v)) for k, v in zip(_KW_RE_LIST, [props, props_re])}
- data['properties'][python_prop_list_name]['re'] = [*map(lambda x: try_transform_placeholder_string_to_regex(x), props_re)]
-
+ data['properties'][python_prop_list_name] = {
+ k: sorted(set(v)) for k, v in zip(_KW_RE_LIST, [props, props_re])
+ }
+ data['properties'][python_prop_list_name]['re'] = try_placeholders_to_regex(props_re)
- data['properties']['kinds'] = [*map(lambda name: name.replace('-', '_'), _PROPERTY_KEYS)]
+ data['properties']['kinds'] = list(map(lambda name: name.replace('-', '_'), _PROPERTY_KEYS))
# Make all commands list
- data['commands'] = [*map(lambda cmd: transform_command(cmd), data['scripting-commands'] + data['project-commands'] + data['ctest-commands'])]
-
- data['generator_expressions'] = data['generator-expressions']
+ data['commands'] = list(
+ map(
+ transform_command
+ , data['scripting-commands'] + data['project-commands'] + data['ctest-commands']
+ )
+ )
+ data['standard_module_commands'] = list(
+ map(
+ transform_command
+ , data['standard-module-commands']
+ )
+ )
+ del data['standard-module-commands']
+ # Fix node names to be accessible from Jinja template
+ data['generator_expressions'] = (ex for ex in data['generator-expressions'] if isinstance(ex, str))
+ data['complex_generator_expressions'] = [ex for ex in data['generator-expressions'] if not isinstance(ex, str)]
+ data['deprecated_or_internal_variables'] = data['deprecated-or-internal-variables']
+ data['environment_variables'] = data['environment-variables']
+ del data['generator-expressions']
+ del data['deprecated-or-internal-variables']
+ del data['environment-variables']
env = jinja2.Environment(
keep_trailing_newline=True
)
+ env.block_start_string = '<!--['
+ env.block_end_string = ']-->'
+ env.variable_start_string = '<!--{'
+ env.variable_end_string = '}-->'
+ env.comment_start_string = '<!--#'
+ env.comment_end_string = '#-->'
# Register convenience filters
env.tests['nulary'] = cmd_is_nulary
tpl = env.from_string(template.read())
result = tpl.render(data)
+ result = remove_duplicate_nodes(result)
+
print(result)
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/generate-doxygenlua.pl b/src/libs/3rdparty/syntax-highlighting/data/generators/generate-doxygenlua.pl
new file mode 100644
index 0000000000..ffc38ea17e
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/generate-doxygenlua.pl
@@ -0,0 +1,42 @@
+#!/usr/bin/env perl
+# SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+# SPDX-License-Identifier: MIT
+
+my $file = "";
+
+open(my $input, '<:encoding(UTF-8)', $ARGV[0])
+ or die "Could not open file '$ARGV[0]': $!";
+
+open(my $output, '>:encoding(UTF-8)', $ARGV[1])
+ or die "Could not open file '$ARGV[1]': $!";
+
+while (<$input>)
+{
+ $file .= $_;
+}
+
+$warning = "\n\n<!-- ***** THIS FILE WAS GENERATED BY A SCRIPT - DO NOT EDIT ***** -->\n";
+$first_context = " <context attribute=\"Normal Text\" lineEndContext=\"#stay\" name=\"Normal\">
+ <RegExpr attribute=\"Comment\" context=\"LineComment\" String=\"--(?:!|(?:-(?=[^-]|\$)))\"/>
+ <RegExpr attribute=\"Region\" context=\"#stay\" String=\"--\\s*\@\\{\\s*\$\" beginRegion=\"MemberGroup\" />
+ <RegExpr attribute=\"Region\" context=\"#stay\" String=\"--\\s*\@\\}\\s*\$\" endRegion=\"MemberGroup\" />
+ </context>";
+
+$file =~ s/\n\s*<context [^\n]*?(?:name="ML_|name="BlockComment").*?<\/context>//gs;
+$file =~ s/\n\s*<context [^\n]*?name="Normal".*?<\/context>/\n$first_context/s;
+$file =~ s/\n[^\n]*?(?: ml_word|LineContinue)[^\n]+//gs;
+$file =~ s/\/\/\//---/gs;
+$file =~ s/\/\/!/--!/gs;
+
+$language = $file =~ s/.*?(<language[^>]+?>).*/$1/sr;
+$language =~ s/ name="[^"]+/ name="DoxygenLua/s;
+$language =~ s/ extensions="[^"]+/ extensions="/s;
+$language =~ s/ mimetype="[^"]+/ mimetype="/s;
+$language =~ s/ priority="[^"]+"//s;
+$version = $language =~ s/.*? version="([^"]+).*/$1/sr;
+$version = $version+2;
+$language =~ s/ version="[^"]+/ version="$version/s;
+$file =~ s/<language[^>]+?>/$warning\n$language/s;
+
+print $output $file;
+print $output $warning;
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/generate-html.pl b/src/libs/3rdparty/syntax-highlighting/data/generators/generate-html.pl
new file mode 100644
index 0000000000..a5b9bc9d05
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/generate-html.pl
@@ -0,0 +1,143 @@
+#!/usr/bin/perl
+
+# This perl script read stdin and write on stdout. It shall be an XML language file.
+#
+# * If the name of the language is 'HTML', then it creates the language 'PHP (HTML)'
+# which shall be used for PHP hl.
+#
+# * If the name of the language is something else (say '*'), it creates the language '*/PHP'.
+# This new language is the same as the old one, but is able to detect PHP everywhere.
+#
+# This script will correctly set extensions & mimetype, and will replace
+# <IncludeRules context="##*"> by <IncludeRules context="##*/PHP">
+#
+# Generated languages need a language named 'PHP/PHP', which shall take care of PHP hl itself
+# and which will be called every time something like <?php is encountred.
+#
+# This script also supports Twig and does the same as for PHP.
+#
+# SPDX-FileCopyrightText: Jan Villat <jan.villat@net2000.ch>
+# License: LGPL
+
+my $file = "";
+
+open(my $input, '<:encoding(UTF-8)', $ARGV[0])
+ or die "Could not open file '$ARGV[0]': $!";
+
+open(my $output, '>:encoding(UTF-8)', $ARGV[1])
+ or die "Could not open file '$ARGV[1]': $!";
+
+my $language = $ARGV[1];
+if ($language =~ /-php\.xml$/)
+{
+ $language = "PHP";
+}
+else
+{
+ $language = "Twig";
+}
+
+while (<$input>)
+{
+ $file .= $_;
+}
+
+$warning = "\n\n<!-- ***** THIS FILE WAS GENERATED BY A SCRIPT - DO NOT EDIT ***** -->\n";
+
+$file =~ s/(?=<language)/$warning\n\n\n/;
+
+$file =~ /<language.*?name="([^"]+)"/;
+my $syntaxName = $1;
+
+if ($syntaxName eq "HTML")
+{
+ $root = 1;
+}
+
+if ($language eq "Twig")
+{
+ $file =~ s/<language([^>]+)priority="[^"]*"/<language$1/s;
+}
+
+if ($root == 1)
+{
+ $file =~ s/<language([^>]+)name="[^"]*"/<language$1name="$language (HTML)"/s;
+ $file =~ s/<language([^>]+)section="[^"]*"/<language$1section="Scripts"/s;
+ if ($language eq "PHP")
+ {
+ $file =~ s/<language([^>]+)extensions="[^"]*"/<language$1extensions="*.php;*.php3;*.wml;*.phtml;*.phtm;*.inc;*.ctp"/s;
+ $file =~ s/<language([^>]+)mimetype="[^"]*"/<language$1mimetype="text\/x-php4-src;text\/x-php3-src;text\/vnd.wap.wml;application\/x-php"/s;
+ $file =~ s/<language([^>]+)*/<language$1 indenter="cstyle"/s;
+ }
+ # Twig
+ else
+ {
+ $file =~ s/<language([^>]+)extensions="[^"]*"/<language$1extensions="*.twig;*.html.twig;*.htm.twig"/s;
+ $file =~ s/<language([^>]+)mimetype="[^"]*"/<language$1mimetype="text\/x-twig"/s;
+ }
+}
+else
+{
+ $file =~ s/<language([^>]+)hidden="[^"]*"/<language$1/s;
+ $file =~ s/<language([^>]+)section="[^"]*"/<language$1section="Other"/s;
+ my $extra = " hidden=\"true\"";
+ my $mimetype = "";
+ my $extensions = "";
+ if ($language eq "Twig")
+ {
+ $mimetype = "text/x-twig";
+ if ($syntaxName eq "JavaScript")
+ {
+ $extra = " priority=\"1\"";
+ $extensions = "*.js.twig;*.mjs.twig;*.cjs.twig";
+ }
+ elsif ($syntaxName eq "TypeScript")
+ {
+ $extra = " priority=\"1\"";
+ $extensions = "*.ts.twig;*.mts.twig;*.cts.twig";
+ }
+ }
+ $file =~ s/<language([^>]+)mimetype="[^"]*"/<language$1mimetype="$mimetype"/s;
+ $file =~ s/<language([^>]+)name="([^"]*)"/<language$1name="$2\/$language"$extra/s;
+ $file =~ s/<language([^>]+)extensions="[^"]*"/<language$1extensions="$extensions"/s;
+}
+
+# replace list with a include
+$file =~ s/<list name="([^"]+)">.*?<\/list>/<list name="$1"><include>$1##$syntaxName<\/include><\/list>/gs;
+
+$file =~ s/<language([^>]+)kateversion="[^"]*"/<language$1kateversion="5.79"/s;
+$file =~ s/ fallthrough="(true|1)"//gs;
+
+if ($language eq "Twig")
+{
+ # remove Mustache syntax
+ if ($root == 1)
+ {
+ $file =~ s/<context name="MustacheJS.*?<\/context>//gs;
+ $file =~ s/<StringDetect attribute="Value" context="#pop#pop!MustacheJS" String="[^"]+[^\/]+\/>//gs;
+ }
+}
+elsif ($root == 1 || $ARGV[0] =~ /mustache.xml$/)
+{
+ $file =~ s/<(?:RegExpr (attribute="Processing Instruction" context="PI"|context="PI" attribute="Processing Instruction")|itemData name="Processing Instruction")[^\/]+\/>|<context name="PI".*?<\/context>//gs;
+}
+
+my $find_language = "##$language/$language";
+my $language_suffix = "/$language";
+if ($language eq "PHP")
+{
+ $find_language = "FindPHP";
+}
+
+$file =~ s/<IncludeRules\s([^>]*)context="([^"#]*)##(?!Alerts|Comments|Doxygen|Modelines)([^"]+)"/<IncludeRules $1context="$2##$3$language_suffix"/g;
+$file =~ s/(<context\s[^>]*[^>\/]>)/$1\n<IncludeRules context="$find_language" \/>/g;
+$file =~ s/(<context\s[^>]*[^>\/])\s*\/>/$1>\n<IncludeRules context="$find_language" \/>\n<\/context>/g;
+
+if ($language eq "PHP")
+{
+ $findphp = "<context name=\"FindPHP\" attribute=\"Normal Text\" lineEndContext=\"#stay\">\n<Detect2Chars context=\"##PHP/PHP\" char=\"&lt;\" char1=\"?\" lookAhead=\"true\" />\n</context>\n";
+ $file =~ s/(?=<\/contexts\s*>)/$findphp/;
+}
+
+print $output $file;
+print $output $warning;
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/generate-nginx-lists.rb b/src/libs/3rdparty/syntax-highlighting/data/generators/generate-nginx-lists.rb
new file mode 100644
index 0000000000..577dea47c4
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/generate-nginx-lists.rb
@@ -0,0 +1,55 @@
+#!/usr/bin/env ruby
+#
+# Generates the keyword lists for directives and variables used in nginx and
+# prints them to stdout, ready to be copy & pasted into nginx.xml.
+#
+# SPDX-FileCopyrightText: 2023 Georg Gadinger <nilsding@nilsding.org>
+# SPDX-License-Identifier: MIT
+#
+# Usage:
+# % ./generate-nginx-lists.rb
+#
+# if you want to install the required dependencies provide `INSTALL_GEMS` in
+# your ENV:
+# % INSTALL_GEMS=1 ./generate-nginx-lists.rb
+
+require "bundler/inline"
+
+gemfile(ENV["INSTALL_GEMS"]) do
+ source "https://rubygems.org"
+
+ gem "nokogiri", "~> 1.14"
+ gem "faraday", "~> 2.7"
+ gem "builder", "~> 3.2"
+end
+
+def fetch_vars(url)
+ Faraday.get(url)
+ .then { Nokogiri::HTML.parse _1.body }
+ .css("div#content a[href]")
+ .map(&:text)
+ .reject { _1.end_with?("_") } # some vars are just a prefix, ignore those
+ .sort
+ .uniq
+end
+
+def build_xml_list(name, url: nil, items: [])
+ builder = Builder::XmlMarkup.new(indent: 2)
+
+ builder.comment! "see #{url} for a full list of #{name}"
+ builder.list(name:) do |b|
+ items.each do |item|
+ b.item item
+ end
+ end
+end
+
+{
+ directives: "https://nginx.org/en/docs/dirindex.html",
+ variables: "https://nginx.org/en/docs/varindex.html",
+}.each do |name, url|
+ items = fetch_vars(url)
+
+ puts build_xml_list(name, url:, items:).gsub(/^/, ' ' * 4)
+ puts
+end
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/generate-php.pl b/src/libs/3rdparty/syntax-highlighting/data/generators/generate-php.pl
deleted file mode 100644
index c8274aab58..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/data/generators/generate-php.pl
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/perl
-
-# This perl script read stdin and write on stdout. It shall be an XML language file.
-#
-# * If the name of the language is 'HTML', then it creates the language 'PHP (HTML)'
-# which shall be used for PHP hl.
-#
-# * If the name of the language is something else (say '*'), it creates the language '*/PHP'.
-# This new language is the same as the old one, but is able to detect PHP everywhere.
-#
-# This script will correctly set extensions & mimetype, and will replace
-# <IncludeRules context="##*"> by <IncludeRules context="##*/PHP">
-#
-# Generated languages need a language named 'PHP/PHP', which shall take care of PHP hl itself
-# and which will be called every time something like <?php is encountred.
-#
-# Author: Jan Villat <jan.villat@net2000.ch>
-# License: LGPL
-
-my $file = "";
-
-while (<>)
-{
- $file .= $_;
-}
-
-$warning = "\n\n<!-- ***** THIS FILE WAS GENERATED BY A SCRIPT - DO NOT EDIT ***** -->\n";
-
-$file =~ s/(?=<language)/$warning\n\n\n/;
-
-if ($file =~ /<language[^>]+name="HTML"/)
-{
- $root = 1;
-}
-
-if ($root == 1)
-{
- $file =~ s/<language([^>]+)name="[^"]*"/<language$1name="PHP (HTML)"/s;
- $file =~ s/<language([^>]+)section="[^"]*"/<language$1section="Scripts"/s;
- $file =~ s/<language([^>]+)extensions="[^"]*"/<language$1extensions="*.php;*.php3;*.wml;*.phtml;*.phtm;*.inc;*.ctp"/s;
- $file =~ s/<language([^>]+)mimetype="[^"]*"/<language$1mimetype="text\/x-php4-src;text\/x-php3-src;text\/vnd.wap.wml;application\/x-php"/s;
-}
-else
-{
- $file =~ s/<language([^>]+)name="([^"]*)"/<language$1name="$2\/PHP" hidden="true"/s;
- $file =~ s/<language([^>]+)section="[^"]*"/<language$1section="Other"/s;
- $file =~ s/<language([^>]+)extensions="[^"]*"/<language$1extensions=""/s;
- $file =~ s/<language([^>]+)mimetype="[^"]*"/<language$1mimetype=""/s;
-}
-
-$findphp = "<context name=\"FindPHP\" attribute=\"Normal Text\" lineEndContext=\"#stay\">\n<RegExpr context=\"##PHP/PHP\" String=\"&lt;\\?(?:=|php)?\" lookAhead=\"true\" />\n</context>\n";
-
-$file =~ s/<IncludeRules\s([^>]*)context="([^"#]*)##(?!Alerts|Doxygen|Modelines)([^"]+)"/<IncludeRules $1context="$2##$3\/PHP"/g;
-$file =~ s/(<context\s[^>]*>)/$1\n<IncludeRules context="FindPHP" \/>/g;
-$file =~ s/(?=<\/contexts\s*>)/$findphp/;
-
-print $file;
-print $warning;
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/generate-spdx-syntax.py b/src/libs/3rdparty/syntax-highlighting/data/generators/generate-spdx-syntax.py
new file mode 100644
index 0000000000..73ff29092c
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/generate-spdx-syntax.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Generate SPDX-Comments syntax file
+#
+# SPDX-FileCopyrightText: 2020 Alex Turbov <i.zaufi@gmail.com>
+# SPDX-License-Identifier: MIT
+#
+# To install prerequisites:
+#
+# $ pip install --user click jinja2
+#
+# To use:
+#
+# $ ./generate-spdx-syntax.py > ../syntax/spdx-comments.xml
+#
+
+import json
+import pathlib
+import urllib.request
+
+import click
+import jinja2
+
+
+def get_json(url):
+ with urllib.request.urlopen(url=url) as body:
+ return json.load(body)
+
+
+@click.command()
+@click.argument('template', type=click.File('r'), default='./spdx-comments.xml.tpl')
+def cli(template):
+
+ data = {
+ 'licenses': [
+ *filter(
+ lambda l: not l['licenseId'].endswith('+')
+ , get_json(url='https://spdx.org/licenses/licenses.json')['licenses']
+ )
+ ]
+ , 'exceptions': [
+ *filter(
+ lambda l: not l['licenseExceptionId'].endswith('+')
+ , get_json(url='https://spdx.org/licenses/exceptions.json')['exceptions']
+ )
+ ]
+ }
+
+ env = jinja2.Environment(
+ keep_trailing_newline=True
+ )
+ env.block_start_string = '<!--['
+ env.block_end_string = ']-->'
+ env.variable_start_string = '<!--{'
+ env.variable_end_string = '}-->'
+ env.comment_start_string = '<!--#'
+ env.comment_end_string = '#-->'
+
+ tpl = env.from_string(template.read())
+ result = tpl.render(data)
+ print(result.strip(), end=None)
+
+
+if __name__ == '__main__':
+ cli()
+ # TODO Handle execptions and show errors
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/get-Qt-classes.sh b/src/libs/3rdparty/syntax-highlighting/data/generators/get-Qt-classes.sh
index f2735c88a5..fcfddd1a9d 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/generators/get-Qt-classes.sh
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/get-Qt-classes.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright (c) 2012-2013 by Alex Turbov
+# SPDX-FileCopyrightText: 2012-2013 Alex Turbov
#
# Grab a documented (officially) class list from Qt project web site:
# http://qt-project.org/doc/qt-${version}/classes.html
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/get-Qt-macros.sh b/src/libs/3rdparty/syntax-highlighting/data/generators/get-Qt-macros.sh
index db2a32d7a3..c6275e52cb 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/generators/get-Qt-macros.sh
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/get-Qt-macros.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright (c) 2011-2012 by Alex Turbov
+# SPDX-FileCopyrightText: 2011-2012 Alex Turbov
#
# Simplest (and stupid) way to get #defines from a header file(s)
#
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/qmake-gen.py b/src/libs/3rdparty/syntax-highlighting/data/generators/qmake-gen.py
index 3b20361708..a16d5fd5d4 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/generators/qmake-gen.py
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/qmake-gen.py
@@ -1,19 +1,8 @@
#!/usr/bin/env python
-# Copyright (C) 2016 Kevin Funk <kfunk@kde.org>
+# SPDX-FileCopyrightText: 2016 Kevin Funk <kfunk@kde.org>
#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Library General Public License as published by
-# the Free Software Foundation; either version 2 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 Library General Public
-# License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# SPDX-License-Identifier: LGPL-2.0-or-later
# This script will print XML-like code you can put into the qmake.xml
# syntax definition file
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/spdx-comments.xml.tpl b/src/libs/3rdparty/syntax-highlighting/data/generators/spdx-comments.xml.tpl
new file mode 100644
index 0000000000..ad13296c1d
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/spdx-comments.xml.tpl
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language>
+<!-- ***** THIS FILE WAS GENERATED BY A SCRIPT - DO NOT EDIT *****
+ cd data/generators
+ # increase version of spdx-comments.xml.tpl then
+ ./generate-spdx-syntax.py > ../syntax/spdx-comments.xml
+-->
+<language
+ version="6"
+ kateversion="3.1"
+ name="SPDX-Comments"
+ section="Other"
+ extensions=""
+ mimetype=""
+ author="Alex Turbov (i.zaufi@gmail.com)"
+ license="MIT"
+ hidden="true"
+ >
+ <highlighting>
+ <list name="tags">
+ <item>SPDX-License-Identifier:</item>
+ <item>SPDX-FileContributor:</item>
+ <item>SPDX-FileCopyrightText:</item>
+ <item>SPDX-LicenseInfoInFile:</item>
+ </list>
+
+ <list name="operators">
+ <item>AND</item>
+ <item>OR</item>
+ <item>WITH</item>
+ </list>
+
+ <list name="licenses">
+ <!--[- for license in licenses if not license.isDeprecatedLicenseId ]-->
+ <item><!--{ license.licenseId }--></item>
+ <!--[- endfor ]-->
+ </list>
+
+ <list name="deprecated-licenses">
+ <!--[- for license in licenses if license.isDeprecatedLicenseId ]-->
+ <item><!--{ license.licenseId }--></item>
+ <!--[- endfor ]-->
+ </list>
+
+ <list name="exceptions">
+ <!--[- for exception in exceptions if not exception.isDeprecatedLicenseId ]-->
+ <item><!--{ exception.licenseExceptionId }--></item>
+ <!--[- endfor ]-->
+ </list>
+
+ <list name="deprecated-exceptions">
+ <!--[- for exception in exceptions if exception.isDeprecatedLicenseId ]-->
+ <item><!--{ exception.licenseExceptionId }--></item>
+ <!--[- endfor ]-->
+ </list>
+
+ <contexts>
+
+ <context name="Normal" attribute="SPDX Tag" lineEndContext="#pop">
+ <WordDetect String="SPDX-License-Identifier:" attribute="SPDX Tag" context="license-expression" />
+ <keyword String="tags" attribute="SPDX Tag" />
+ </context>
+
+ <context name="license-expression" attribute="SPDX Value" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
+ <DetectSpaces/>
+ <AnyChar String="()+" context="#stay" attribute="SPDX License Expression Operator" />
+ <keyword String="licenses" context="#stay" attribute="SPDX License" />
+ <keyword String="deprecated-licenses" context="#stay" attribute="SPDX Deprecated License" />
+ <keyword String="exceptions" context="#stay" attribute="SPDX License Exception" />
+ <keyword String="deprecated-exceptions" context="#stay" attribute="SPDX Deprecated License Exception" />
+ <keyword String="operators" context="#stay" attribute="SPDX License Expression Operator" />
+ <RegExpr attribute="SPDX License" context="#stay" String="\bLicenseRef-[^\s]+" />
+ </context>
+
+ </contexts>
+
+ <itemDatas>
+ <itemData name="SPDX Tag" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX Value" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX License" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX License Exception" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX Deprecated License" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX Deprecated License Exception" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX License Expression Operator" defStyleNum="dsOperator" italic="true" spellChecking="false" />
+ </itemDatas>
+
+ </highlighting>
+
+ <general>
+ <keywords casesensitive="1" weakDeliminator=":-." />
+ </general>
+
+</language>
+<!-- kate: indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/update_css_and_scss.py b/src/libs/3rdparty/syntax-highlighting/data/generators/update_css_and_scss.py
new file mode 100644
index 0000000000..a4edaa4227
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/update_css_and_scss.py
@@ -0,0 +1,498 @@
+#!/usr/bin/env python3
+# SPDX-FileCopyrightText: 2023 Jonathan Poelen <jonathan.poelen@gmail.com>
+# SPDX-License-Identifier: MIT
+
+from pathlib import Path
+from collections import defaultdict
+from typing import TextIO
+import re
+import sys
+
+
+exclude_line = {
+ ' - non-standard\n',
+ ' - experimental\n',
+ ' - deprecated\n',
+ 'page-type: css-combinator\n',
+ 'page-type: css-selector\n',
+ 'page-type: css-module\n',
+ 'page-type: landing-page\n',
+ 'page-type: guide\n',
+}
+
+page_type_accepted = {
+ 'page-type: css-type\n',
+ 'page-type: css-function\n',
+ 'page-type: css-property\n',
+ 'page-type: css-keyword\n',
+ 'page-type: css-shorthand-property\n',
+ 'page-type: css-pseudo-element\n',
+ 'page-type: css-pseudo-class\n',
+ 'page-type: css-at-rule-descriptor\n',
+ 'page-type: css-at-rule\n',
+ 'page-type: css-media-feature\n',
+ 'page-type: svg-attribute\n',
+}
+
+exclude_title = {
+ '<alpha-value>',
+ '<angle>',
+ '<angle-percentage>',
+ '<basic-shape>',
+ '<calc-constant>',
+ '<calc-sum>',
+ '<color-interpolation-method>',
+ '<color>',
+ '<custom-ident>',
+ '<dashed-ident>',
+ '<display-listitem>',
+ '<display-inside>',
+ '<dimension>',
+ '<easing-function>'
+ '<filter-function>',
+ '<flex>',
+ '<frequency-percentage>',
+ '<frequency>',
+ '<gradient>',
+ '<hex-color>',
+ '<hue>',
+ '<hue-interpolation-method>',
+ '<ident>',
+ '<image>',
+ '<integer>',
+ '<length>',
+ '<length-percentage>',
+ '<number>',
+ '<percentage>',
+ '<position>',
+ '<ratio>',
+ '<resolution>',
+ '<string>',
+ '<time-percentage>',
+ '<time>',
+ '<transform-function>',
+ '"!important"',
+}
+
+properties_ignore_value = (
+ 'counter-increment',
+ 'counter-reset',
+ 'counter-set',
+ 'text-rendering',
+ 'page',
+)
+
+
+units: list[str] = []
+colors: set[str] = set()
+system_colors: set[str] = set()
+deprecated_system_colors: set[str] = set()
+values: set[str] = set()
+properties: set[str] = set()
+svg_values: set[str] = set()
+svg_properties: set[str] = set()
+functions: set[str] = set()
+pseudo_classes: set[str] = set()
+pseudo_elements: set[str] = set()
+experimental_pseudo_classes: set[str] = set()
+experimental_pseudo_elements: set[str] = set()
+at_rules: set[str] = set()
+media_features: set[str] = set()
+media_feature_values: set[str] = set()
+
+
+_update_version_extractor = re.compile(r' version="(\d+)" ')
+
+def update_version(s: str) -> str:
+ return _update_version_extractor.sub(lambda m: f' version="{int(m[1])+1}" ', s, count=1)
+
+
+_md_value_extractor = re.compile(r'(?<=[^\w][ /])`([-\w][-\w\d]+(?:<[^>]+>[?+*])?)`')
+_html_value_extractor = re.compile(r'<code>([-\w][-\w\d]+)</code>')
+_is_md_value = re.compile(r'^\s*- `')
+_is_html_table_desc = re.compile(r'^\s+<td><code>')
+
+def css_parse_values(f: TextIO, prop: str, values: set[str]) -> None:
+ line:str = ''
+ # Format:
+ # ## Syntax or ### Syntax
+ #
+ # ```css
+ # (optional)
+ # ```
+ # ## Values or ### Values or not...
+ #
+ # - `ident` or html table <td><code>....</code></td>
+ #
+ # ## SVG only ... (optional)
+ # ## other title
+ for line in f:
+ if line.endswith('## Syntax\n') or line.endswith('## Values\n') or '## SVG only' in line:
+ for line in f:
+ if _is_md_value.match(line):
+ if 'deprecated' not in line:
+ values.update(_md_value_extractor.findall(line))
+ elif line.startswith('#'):
+ if not (line.endswith('## Values\n') or '## SVG only' in line
+ or (prop == 'display'
+ and (line.endswith('## Grouped values\n')
+ or line.endswith('## Outside\n')
+ or line.endswith('## Inside\n')
+ or line.endswith('## List Item\n')
+ or line.endswith('## Internal\n')
+ or line.endswith('## Box\n')
+ or line.endswith('## Precomposed\n')
+ ))
+ ):
+ return
+ elif line == '```css\n':
+ for line in f:
+ if line.startswith('```\n'):
+ break
+ elif _is_html_table_desc.match(line):
+ values.update(_html_value_extractor.findall(line))
+
+
+def css_parse_named_colors(f: TextIO) -> set[str]:
+ return set(re.findall('\n <td>(?:\n )?<code>([a-z]+)</code>', f.read()))
+
+
+def css_parse_units(f: TextIO) -> list[str]:
+ return re.findall(r'`([^`]+)`', ''.join(re.findall(r'\n\| (`[^|]+)', f.read())))
+
+
+_svg_values_extractor = re.compile(r'<th scope="row">Value</th>\n\s*<td>(.*?)</td>', re.DOTALL)
+_svg_value_extractor = re.compile(r'<code>([-\w\d]+)</code>')
+
+def css_parse_svg_attribute(f: TextIO, prop: str, properties: set[str], values: set[str]) -> None:
+ contents = f.read()
+ if 'can be used as a CSS property' in contents:
+ properties.add(prop)
+ m = _svg_values_extractor.search(contents)
+ if m:
+ values.update(_svg_value_extractor.findall(m[1]))
+
+
+_experimental_selector_extractor = re.compile(r'\n- {{CSSxRef([^}]+)}} {{Experimental_Inline}}')
+_selector_extractor = re.compile(r'":+([-\w\d]+)[()]*"')
+
+def css_parse_pseudo_classes_or_elements(f: TextIO) -> tuple[
+ set[str], # experimental
+ list[str]
+]:
+ s = f.read()
+ experimental_str = ''.join(_experimental_selector_extractor.findall(s))
+ return (set(_selector_extractor.findall(experimental_str)), _selector_extractor.findall(s))
+
+
+if len(sys.argv) < 5:
+ print(f'''{Path(sys.argv[0]).name} content-main-directory syntax/css.xml sass-site-directory syntax/scss.xml
+
+content-main-directory is https://github.com/mdn/content/ (https://github.com/mdn/content/archive/refs/heads/main.zip)
+sass-site-directory is https://github.com/sass/sass-site/tree/main (https://github.com/sass/sass-site/archive/refs/heads/main.zip)
+''', file=sys.stderr)
+ exit(1)
+
+css_dir = Path(sys.argv[1])
+css_filename = Path(sys.argv[2])
+scss_dir = Path(sys.argv[3])
+scss_filename = Path(sys.argv[4])
+
+
+tmp_pseudo_classes = (set(), ())
+tmp_pseudo_elements = (set(), ())
+
+for pattern in (
+ 'files/en-us/web/svg/attribute/**/',
+ 'files/en-us/web/css/**/',
+):
+ for md in css_dir.glob(pattern):
+ with open(md/'index.md', encoding='utf8') as f:
+ if f.readline() != '---\n':
+ continue
+
+ title = f.readline()[7:-1]
+ if title in exclude_title:
+ continue
+
+ if title.startswith('"'):
+ title = title[1:-1]
+
+ page_type = ''
+ for line in f:
+ if line in exclude_line:
+ page_type = ''
+ break
+
+ if line.startswith('page-type: '):
+ if line not in page_type_accepted:
+ raise Exception(f'Unknown {line[:-1]}')
+ page_type = line[11:-1]
+
+ if line == '---\n':
+ break
+
+ if page_type == 'css-property' or page_type == 'css-at-rule-descriptor':
+ properties.add(title)
+ if not title.endswith('-name') and title not in properties_ignore_value:
+ css_parse_values(f, title, values)
+ elif page_type == 'css-shorthand-property':
+ properties.add(title)
+ elif page_type == 'css-pseudo-class':
+ pseudo_classes.add(title[1:].removesuffix('()'))
+ elif page_type == 'css-pseudo-element':
+ pseudo_elements.add(title[2:].removesuffix('()'))
+ elif page_type == 'css-type':
+ if title == '<named-color>':
+ colors = css_parse_named_colors(f)
+ if title == '<system-color>':
+ css_parse_values(f, '', system_colors)
+ deprecated_system_colors = set(re.findall('\n- `([^`]+)` {{deprecated_inline}}', f.read()))
+ else:
+ css_parse_values(f, '', values)
+ elif page_type == 'css-function':
+ functions.add(title[:-2])
+ elif page_type == 'css-at-rule':
+ at_rules.add(title)
+ elif page_type == 'css-media-feature':
+ media_features.add(title)
+ css_parse_values(f, title, media_feature_values)
+ elif page_type == 'css-keyword':
+ values.add(title)
+ elif title == 'CSS values and units':
+ units = css_parse_units(f)
+ elif title == 'Pseudo-classes':
+ tmp_pseudo_classes = css_parse_pseudo_classes_or_elements(f)
+ elif title == 'Pseudo-elements':
+ tmp_pseudo_elements = css_parse_pseudo_classes_or_elements(f)
+ elif page_type == 'svg-attribute':
+ css_parse_svg_attribute(f, title, svg_properties, svg_values)
+ elif title == 'CSS value functions':
+ functions.update(re.findall(r'\n- {{CSSxRef\("[^"]+", "([-\w\d]+)\(\)"\)}}\n', f.read()))
+
+
+experimental_pseudo_classes = tmp_pseudo_classes[0]
+experimental_pseudo_classes -= pseudo_classes
+pseudo_classes.update(tmp_pseudo_classes[1])
+
+experimental_pseudo_elements = tmp_pseudo_elements[0]
+experimental_pseudo_elements -= pseudo_elements
+pseudo_elements.update(tmp_pseudo_elements[1])
+
+
+global_values = {
+ 'auto',
+ 'inherit',
+ 'initial',
+ 'revert',
+ 'revert-layer',
+ 'unset',
+}
+values -= global_values
+svg_values -= global_values
+pseudo_classes -= experimental_pseudo_classes
+pseudo_elements -= experimental_pseudo_elements
+
+# add values of functions
+values.update((
+ # repeat()
+ 'auto-fill',
+ 'auto-fit',
+))
+
+# move some keyword colors in values
+for special_color in ('transparent', 'currentcolor'):
+ values.add(special_color)
+ colors.discard(special_color)
+
+# fix not specified value in mdn file
+if 'user-invalid' in experimental_pseudo_classes:
+ pseudo_classes.discard('user-valid')
+ experimental_pseudo_classes.add('user-valid')
+media_features.update((
+ 'min-width',
+ 'max-width',
+ 'min-height',
+ 'max-height',
+))
+
+# fix errors in mdn file
+for e in ('has', 'host-context'):
+ pseudo_classes.add(e)
+ experimental_pseudo_classes.discard(e)
+
+# @font-format functions
+functions.update((
+ 'format',
+ 'local',
+ 'tech',
+))
+
+
+# def show(name, values):
+# print(f'{name} ({len(values)}):')
+# print('\n'.join(sorted(values)), end='\n\n')
+#
+# show('properties', properties)
+# show('svg properties', svg_properties)
+# show('values', values)
+# show('svg values', svg_values)
+# show('global values', global_values)
+# show('functions', functions)
+# show('pseudo-classes', pseudo_classes)
+# show('pseudo-elements', pseudo_elements)
+# show('experimental pseudo-classes', experimental_pseudo_classes)
+# show('experimental pseudo-elements', experimental_pseudo_elements)
+# show('at-rules', at_rules)
+# show('media-features', media_features)
+# show('media-features values', media_feature_values)
+# show('colors', colors)
+# show('system colors', system_colors)
+# show('deprecated system colors', deprecated_system_colors)
+# show('units', units)
+# print('units reg:', '|'.join(units))
+
+
+#
+# Update CSS
+#
+
+sep = '\n '
+css_replacements = {
+ prop: f'</item>{sep}<item>'.join(sorted(seq))
+ for prop, seq in (
+ ('properties', properties),
+ ('values', values),
+ ('value keywords', global_values),
+ ('functions', functions),
+ ('pseudo-classes', pseudo_classes),
+ ('pseudo-elements', pseudo_elements),
+ ('media features', media_features)
+ )
+}
+for prop, seq in (('properties', svg_properties - properties), ('values', svg_values - values)):
+ if seq:
+ items = f'</item>{sep}<item>'.join(sorted(seq))
+ css_replacements[prop] += f'</item>\n{sep}<!-- SVG only -->\n{sep}<item>{items}'
+
+rep1 = f'</item>{sep}<item>'.join(sorted(colors))
+rep2 = f'</item>{sep}<item>'.join(sorted(system_colors))
+css_replacements['colors'] = f'{rep1}</item>{sep}{sep}<!-- System colors -->{sep}<item>{rep2}'
+
+item_extractor = re.compile('<item>([^-<][^<]*)')
+
+current_at_rules = set()
+
+def _css_update_and_extract_items(m) -> str:
+ seq = css_replacements.get(m[1])
+ if seq:
+ end = ' ' if m[3] == '</list>' else sep
+ return f'<list name="{m[1]}">{sep}<item>{seq}</item>\n{end}{m[3]}'
+
+ current_at_rules.update(item_extractor.findall(m[2]))
+ return m[0]
+
+
+css_content = css_filename.read_text()
+original_css_content = css_content
+
+names = f"{'|'.join(css_replacements)}|at-rules(?: definitions)?"
+css_content = re.sub(rf'<list name="({names})">(.*?)(</list>|<!-- manual list -->)',
+ _css_update_and_extract_items, css_content, flags=re.DOTALL)
+
+_regexpr_unit_prefix = r'(<RegExpr attribute="Unit".*?String="\(%\|\()'
+regexpr_unit_extractor = re.compile(fr'{_regexpr_unit_prefix}([^)]+)')
+
+css_content = regexpr_unit_extractor.sub('\\1' + "|".join(units), css_content, 1)
+
+if original_css_content != css_content:
+ css_content = update_version(css_content)
+ css_filename.write_text(css_content)
+
+
+def show_at_rule_difference(language: str, old_at_rules: set[str], new_at_rules: set[str]) -> None:
+ at_rule_added = new_at_rules - old_at_rules
+ at_rule_removed = old_at_rules - new_at_rules
+ nl = '\n '
+ if at_rule_added or at_rule_removed:
+ print(f"""\x1b[31m{language} At-rules requires a manual update
+New ({len(at_rule_added)}):\x1b[0m
+ {nl.join(at_rule_added)}
+\x1b[31mRemoved ({len(at_rule_removed)}):\x1b[0m
+ {nl.join(at_rule_removed)}""")
+
+show_at_rule_difference('CSS', current_at_rules, at_rules)
+
+#
+# Extract SCSS data
+#
+
+scss_functions:list[str] = []
+scss_at_rules:set[str] = {'@content', '@return'}
+
+_function_list_extractor = re.compile(r'{% function (.*?) %}')
+_function_extractor = re.compile(r"'([-._a-zA-Z0-9]+)\(")
+_at_rule_extractor = re.compile(r'@[-a-z0-9]+')
+
+for md in sorted(scss_dir.glob('source/documentation/modules/**/*.md')):
+ func_list = _function_list_extractor.findall(md.read_text())
+ func_items = set(_function_extractor.findall(''.join(func_list)))
+ scss_functions.append(f'\n{sep}<!-- {md.stem} -->')
+ scss_functions.extend(f'{sep}<item>{func}</item>' for func in sorted(func_items - functions))
+
+for md in scss_dir.glob('source/documentation/at-rules/**/*.md'):
+ with open(md) as f:
+ f.readline()
+ scss_at_rules.update(_at_rule_extractor.findall(f.readline()))
+
+subproperties = set(
+ '-'.join(splitted[i:n])
+ for prop in properties
+ for splitted in (prop.rsplit('-', prop.count('-') - 1) # '-aaa-bbb' -> ['-aaa', 'bbb']
+ if prop.startswith('-')
+ else prop.split('-'), ) # 'aaa-bbb' -> ['aaa', 'bbb']
+ for i in range(len(splitted))
+ for n in range(i+1, len(splitted)+1)
+)
+
+#
+# Update SCSS
+#
+
+scss_current_at_rules = set()
+
+def _scss_update_and_extract_items(m) -> str:
+ name = m[1]
+
+ if name == 'functions':
+ return f"""<list name="functions">
+ <include>functions##CSS</include>
+
+ <!-- https://sass-lang.com/documentation/modules/ -->{f''.join(scss_functions)}
+ </list>"""
+
+ if name == 'at-rules':
+ scss_current_at_rules.update(_at_rule_extractor.findall(m[2]))
+ return m[0]
+
+ # sub-properties
+ items = f'</item>{sep}<item>'.join(sorted(subproperties - properties))
+ return f'<list name="{name}">{sep}<item>{items}</item>\n </list>'
+
+scss_content = scss_filename.read_text()
+original_scss_content = scss_content
+
+scss_content = re.sub(r'<list name="(sub-properties|functions|at-rules)">(.*?)</list>',
+ _scss_update_and_extract_items, scss_content, count=3, flags=re.DOTALL)
+
+scss_content = re.sub(r'<!ENTITY pseudoclasses "[^"]*">',
+ f'<!ENTITY pseudoclasses "{"|".join(sorted(pseudo_classes))}">',
+ scss_content, count=1)
+
+scss_content = regexpr_unit_extractor.sub('\\1' + "|".join(units), scss_content, 1)
+
+if original_scss_content != scss_content:
+ scss_content = update_version(scss_content)
+ scss_filename.write_text(scss_content)
+
+show_at_rule_difference('SCSS', scss_current_at_rules, scss_at_rules)
diff --git a/src/libs/3rdparty/syntax-highlighting/data/generators/update_less.py b/src/libs/3rdparty/syntax-highlighting/data/generators/update_less.py
new file mode 100644
index 0000000000..d509aeabe7
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/generators/update_less.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+# SPDX-FileCopyrightText: 2023 Jonathan Poelen <jonathan.poelen@gmail.com>
+# SPDX-License-Identifier: MIT
+
+from pathlib import Path
+from urllib import request
+import re
+import sys
+
+
+if len(sys.argv) < 1:
+ print(f'{sys.argv[0]} syntax/less.xml', file=sys.stderr)
+ exit(1)
+
+#
+# Extract functions
+#
+
+data = request.urlopen('https://lesscss.org/functions/').read().decode()
+
+functions = re.findall(r'</a>([-_\w\d]+)</h3>', data, flags=re.DOTALL)
+functions.append('%')
+
+#
+# Update syntax
+#
+
+sep = '\n '
+new_list = f"""<list name="functions">
+ <include>functions##CSS</include>
+
+ <!-- Less functions, @see http://lesscss.org/functions/ -->
+ <item>{f'</item>{sep}<item>'.join(sorted(functions))}</item>
+ </list>"""
+
+less_filename = Path(sys.argv[1])
+less_content = less_filename.read_text()
+original_less_content = less_content
+less_content = re.sub(r'<list name="functions">.*?</list>',
+ new_list, less_content, count=1, flags=re.DOTALL)
+
+if original_less_content != less_content:
+ less_content = re.sub(' version="(\d+)" ', lambda m: f' version="{int(m[1])+1}" ',
+ less_content, count=1)
+ less_filename.write_text(less_content)
diff --git a/src/libs/3rdparty/syntax-highlighting/data/schema/language.xsd b/src/libs/3rdparty/syntax-highlighting/data/schema/language.xsd
index 6f05aad7b5..b640219414 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/schema/language.xsd
+++ b/src/libs/3rdparty/syntax-highlighting/data/schema/language.xsd
@@ -1,44 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (c) 2001 Joseph Wenninger <jowenn@kde.org>
- modified (c) 2002 Anders Lund <anders@alweb.dk>
- modified (c) 2003 Simon Huerlimann <simon.huerlimann@access.unizh.ch>
- modified (c) 2005 Dominik Haumann <dhdev@gmx.de>
- modified (c) 2008 Wilbert Berendsen <info@wilbertberendsen.nl>
+ SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
+ SPDX-FileCopyrightText: 2002 Anders Lund <anders@alweb.dk>
+ SPDX-FileCopyrightText: 2003 Simon Huerlimann <simon.huerlimann@access.unizh.ch>
+ SPDX-FileCopyrightText: 2005 Dominik Haumann <dhdev@gmx.de>
+ SPDX-FileCopyrightText: 2008 Wilbert Berendsen <info@wilbertberendsen.nl>
- This file describes the XML format used for syntax highlight descriptions
- for the Kate text editor (http://kate.kde.org), which is part of the KDE
- desktop environment (http://www.kde.org).
- You'll find the "Writing a Kate Highlighting XML File HOWTO" at
- http://kate.kde.org/doc/hlhowto.php
+ This file describes the XML format used for syntax highlight descriptions
+ for the Kate text editor (https://kate-editor.org), which is part of the
+ KDE desktop environment (https://kde.org).
+ You'll find the "Working with Syntax Highlighting" at
+ https://docs.kde.org/stable5/en/kate/katepart/highlight.html
- This format is identified using the SYSTEM identifier
- SYSTEM "language.dtd"
+ You can validate your syntax files using "validatehl.sh yourSyntax.xml".
+ This needs xmllint from the libxml2 XML library.
- Files using this format should include a DOCTYPE declaration like this:
- <!DOCTYPE language SYSTEM "language.dtd">
-
- You can validate your syntax files using "validatehl.sh yourSyntax.xml".
- This needs xmllint from the libxml2 XML library.
-
- In any case, the katehighlightingindexer will validate all files bundled
- with KTextEditor during compile time and fail on errors.
-
- To use your syntax file, copy it to ~/.local/share/katepart5/syntax/ in
- your home directory. You have to open a new instance of kwrite/kate to use
- the new syntax file.
+ In any case, the katehighlightingindexer will validate all files bundled
+ with KTextEditor during compile time and fail on errors.
TODO
- find a more readable way for the - -dtdvalid stuff, it's just annoying
xml comments don't allow it.
-->
-<!--
- Entity declarations
- You can use '&per;' instead of '.'. This seems to be useful in <item> elements.
-
- TODO
- - Are there any more such pre-defined entities?
--->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<!--
Default Styles
@@ -115,27 +98,29 @@
</xs:restriction>
</xs:simpleType>
<!--
+ Char type
+ A sequence of exactly 1 character
+ -->
+ <xs:simpleType name="char">
+ <xs:restriction base="xs:string">
+ <xs:length value="1"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <!--
Language specification
name: The name of this syntax description. Used in the Highlightning Mode menu
section: The logical group to which this syntax description belongs. Used for sub menus
extensions: A file glob or pattern to decide for which documents to use this syntax description
style: The style that this highlighter provides. It is used through required-syntax-style by the indenters. [optional]
mimetype: A list of mimetypes to decide for which documents to use this syntax description [optional]
- version: Version number of this syntax description [optional]
- kateversion: Kate version required for using this file [optional]
- casesensitive: Whether text is matched case sensitive. [boolean, optional, default=true] FIXME: This is not implemented yet
+ version: Version number of this syntax description
+ kateversion: Kate version required for using this file
+ casesensitive: Ignored but preserved to maintain compatibility in older versions of KF5.
priority: Priority of this language, if more than one are usable for the file [optional]
author: Name of author of this hl file [optional]
license: License for this hl file [optional]
indenter: Name of the Indenter to use for this highlighting mode per default, like "cstyle" [optional]
hidden: Should it be hidden in menu [boolean, optional, default=false]
-
- TODO
- - Which matches are affected by casesensitive? keyword, RegExpr, StringDetect, WordDetect...?
-
- WARNING: due to helper scripts, the language opening tag must be on a
- *single line* and *cannot* be split in multiple lines.
-
-->
<xs:element name="language">
<xs:complexType>
@@ -151,6 +136,7 @@
<xs:attribute name="kateversion" use="required" type="xs:decimal"/>
<xs:attribute name="style"/>
<xs:attribute name="mimetype"/>
+ <!-- always ignored, <keywords casesensitive> must be used -->
<xs:attribute name="casesensitive" type="xs:boolean"/>
<xs:attribute name="priority" type="xs:integer"/>
<xs:attribute name="author"/>
@@ -279,9 +265,10 @@
-->
<xs:element name="list">
<xs:complexType>
- <xs:sequence>
- <xs:element minOccurs="0" maxOccurs="unbounded" ref="item"/>
- </xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element ref="item"/>
+ <xs:element ref="include"/>
+ </xs:choice>
<xs:attribute name="name" use="required"/>
</xs:complexType>
</xs:element>
@@ -290,6 +277,11 @@
contains string used in <keyword>
-->
<xs:element name="item" type="xs:string"/>
+ <!--
+ List include
+ contains a name of <keyword>
+ -->
+ <xs:element name="include" type="xs:string"/>
<!-- List of contexts -->
<xs:element name="contexts">
<xs:complexType>
@@ -302,20 +294,18 @@
context specification
name: The name of this context specification. Used in '*Context' attributes [optional]
attribute: The name of the ItemData to be used for matching text
- lineEndContext: Next context if end of line is encountered
- lineEmptyContext: Next context if an empty line is encountered [optional]
+ lineEndContext: Next context if end of line is encountered [optional, default='#stay']
+ lineEmptyContext: Next context if an empty line is encountered [optional, default=value of lineEndContext]
fallthrough: Use a fallthrough context [optional]
- fallthroughContext: Fall through to this context [optional]
+ deprecated since 5.62 but preserved to maintain compatibility in older versions of KF5
+ fallthroughContext: Fall through to this context [optional, default='#stay']
dynamic: Dynamic context [boolean, optional]
+ deprecated since always but preserved to maintain compatibility in older versions of KF5
noIndentationBasedFolding: Python uses indentation based folding. However, Python has parts where
- it does not use indentation based folding (e.g. for """ strings). In this case
- switch to an own context and set this attribute to true. Then the indentation
- based folding will ignore this parts and not change folding markers. [optional]
-
- TODO:
- - Explain fallthrough.
- - Do we need fallthrough at all? It could be true, if fallthroughContext is set, false otherwise.
- - Make lineEndContext optional, defaults to '#stay'. Reasonable?
+ it does not use indentation based folding (e.g. for """ strings). In this case
+ switch to an own context and set this attribute to true. Then the indentation
+ based folding will ignore this parts and not change folding markers. [optional]
+ stopEmptyLineContextSwitchLoop: Do not continue the context switching if an empty line is encountered. [boolean, optional, default=false]
-->
<xs:element name="context">
<xs:complexType>
@@ -324,7 +314,6 @@
<xs:element ref="Float"/>
<xs:element ref="HlCOct"/>
<xs:element ref="HlCHex"/>
- <xs:element ref="HlCFloat"/>
<xs:element ref="Int"/>
<xs:element ref="DetectChar"/>
<xs:element ref="Detect2Chars"/>
@@ -342,12 +331,23 @@
</xs:choice>
<xs:attribute name="name"/>
<xs:attribute name="attribute" use="required"/>
- <xs:attribute name="lineEndContext" use="required"/>
+ <xs:attribute name="lineEndContext"/>
<xs:attribute name="lineEmptyContext"/>
- <xs:attribute name="fallthrough" type="xs:boolean"/>
+ <xs:attribute name="fallthrough">
+ <xs:simpleType>
+ <!-- always true since 5.62 -->
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="1"/>
+ <xs:enumeration value="true"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
<xs:attribute name="fallthroughContext"/>
+ <!-- always ignored -->
<xs:attribute name="dynamic" type="xs:boolean"/>
<xs:attribute name="noIndentationBasedFolding" type="xs:boolean"/>
+ <!-- since 5.103 -->
+ <xs:attribute name="stopEmptyLineContextSwitchLoop" type="xs:boolean"/>
</xs:complexType>
</xs:element>
<!--
@@ -373,66 +373,66 @@
commonAttributes: Common attributes
insensitive: Is this list case-insensitive? [boolean, optional, see note]
String: Name of the list
- weakDelimiter: Use weak deliminator
+ weakDeliminator: Add weak deliminators [optional, see note]
+ additionalDeliminator: Add deliminators [optional, see note]
By default, case sensitivity is determined from <keywords casesensitive> in
<general> (default=true), but can be overridden per-list with 'insensitive'.
- TODO:
- - Should be weakDeliminator
- - Explain deliminator
- - Doesn't seem to be supported in highligh.cpp
+ weakDeliminator and additionalDeliminator are accumulated to those defined in
+ <keywords weakDeliminator additionalDeliminator> in <general>.
-->
<xs:element name="keyword">
<xs:complexType>
<xs:attributeGroup ref="commonAttributes"/>
<xs:attribute name="insensitive" type="xs:boolean"/>
<xs:attribute name="String" use="required"/>
- <xs:attribute name="weakDelimiter"/>
+ <xs:attribute name="weakDeliminator"/>
+ <xs:attribute name="additionalDeliminator"/>
</xs:complexType>
</xs:element>
<!--
- Detect a floating point number
+ Detect a floating point number (as the regular expression: (\b[0-9]+\.[0-9]*|\.[0-9]+)([eE][-+]?[0-9]+)?).
commonAttributes: Common attributes
+ weakDeliminator: Add weak deliminators [optional]
+ additionalDeliminator: Add deliminators [optional]
-->
<xs:element name="Float">
<xs:complexType>
<xs:attributeGroup ref="commonAttributes"/>
+ <xs:attribute name="weakDeliminator"/>
+ <xs:attribute name="additionalDeliminator"/>
</xs:complexType>
</xs:element>
<!--
- Detect an octal number
+ Detect an octal number (as the regular expression: \b0[0-7]+).
commonAttributes: Common attributes
+ weakDeliminator: Add weak deliminators [optional]
+ additionalDeliminator: Add deliminators [optional]
-->
<xs:element name="HlCOct">
<xs:complexType>
<xs:attributeGroup ref="commonAttributes"/>
+ <xs:attribute name="weakDeliminator"/>
+ <xs:attribute name="additionalDeliminator"/>
</xs:complexType>
</xs:element>
<!--
- Detect a hexadecimal number
+ Detect a hexadecimal number (as a regular expression: \b0[xX][0-9a-fA-F]+).
commonAttributes: Common attributes
+ weakDeliminator: Add weak deliminators [optional]
+ additionalDeliminator: Add deliminators [optional]
-->
<xs:element name="HlCHex">
<xs:complexType>
<xs:attributeGroup ref="commonAttributes"/>
+ <xs:attribute name="weakDeliminator"/>
+ <xs:attribute name="additionalDeliminator"/>
</xs:complexType>
</xs:element>
<!--
- Detect a C-style floating point number
- commonAttributes: Common attributes
- -->
- <xs:element name="HlCFloat">
- <xs:complexType>
- <xs:attributeGroup ref="commonAttributes"/>
- </xs:complexType>
- </xs:element>
- <!--
- Detect C-style character
+ Detect C-style character. Characters enclosed in a tick (Example: 'c') which may be a simple character or an escaped character (as a regular expression: '(\\([abefnrtv"'?\\.]|x[0-9a-fA-F]{1,2}|[0-7]{1,3})|[^'])')
commonAttributes: Common attributes
-
- TODO
- - Did I get this right?
-->
<xs:element name="HlCChar">
<xs:complexType>
@@ -442,10 +442,14 @@
<!--
Detect an integer number
commonAttributes: Common attributes
+ weakDeliminator: Add weak deliminators [optional]
+ additionalDeliminator: Add deliminators [optional]
-->
<xs:element name="Int">
<xs:complexType>
<xs:attributeGroup ref="commonAttributes"/>
+ <xs:attribute name="weakDeliminator"/>
+ <xs:attribute name="additionalDeliminator"/>
</xs:complexType>
</xs:element>
<!--
@@ -457,7 +461,7 @@
<xs:element name="DetectChar">
<xs:complexType>
<xs:attributeGroup ref="commonAttributes"/>
- <xs:attribute name="char" use="required"/>
+ <xs:attribute name="char" type="char" use="required"/>
<xs:attribute name="dynamic" type="xs:boolean"/>
</xs:complexType>
</xs:element>
@@ -470,17 +474,14 @@
<xs:element name="Detect2Chars">
<xs:complexType>
<xs:attributeGroup ref="commonAttributes"/>
- <xs:attribute name="char" use="required"/>
- <xs:attribute name="char1" use="required"/>
+ <xs:attribute name="char" type="char" use="required"/>
+ <xs:attribute name="char1" type="char" use="required"/>
</xs:complexType>
</xs:element>
<!--
- Detect any group of characters
+ Detect one character of a set of specified characters.
commonAttributes: Common attributes
- String: A string representing the characters to look for
-
- TODO
- - Description is not descriptive enough, I'm not sure what it exactly does:-(
+ String: The set of characters
-->
<xs:element name="AnyChar">
<xs:complexType>
@@ -494,9 +495,6 @@
String: The string to look for
insensitive: Whether the string is matched case INsensitive. [boolean, optional, default=false]
dynamic: Uses %1 .. %9 as placeholders for dynamic arguments [boolean, optional, default=false]
-
- TODO
- - What's default of insensitive? I'm not sure...
-->
<xs:element name="StringDetect">
<xs:complexType>
@@ -511,15 +509,16 @@
commonAttributes: Common attributes
String: The string to look for
insensitive: Whether the string is matched case INsensitive. [boolean, optional, default=false]
-
- TODO
- - What's default of insensitive? I'm not sure...
+ weakDeliminator: Add weak deliminators [optional]
+ additionalDeliminator: Add deliminators [optional]
-->
<xs:element name="WordDetect">
<xs:complexType>
<xs:attributeGroup ref="commonAttributes"/>
<xs:attribute name="String" use="required"/>
<xs:attribute name="insensitive" type="xs:boolean"/>
+ <xs:attribute name="weakDeliminator"/>
+ <xs:attribute name="additionalDeliminator"/>
</xs:complexType>
</xs:element>
<!--
@@ -527,7 +526,7 @@
commonAttributes: Common attributes
String: The regular expression pattern
insensitive: Whether the text is matched case INsensitive. [boolean, optional, default=false]
- minimal: Wheather to use minimal matching for wild cards in the pattern [boolean, optional, default='false']
+ minimal: Wheather to use minimal matching for wild cards in the pattern [boolean, optional, default=false]
dynamic: Uses %1 .. %9 as placeholders for dynamic arguments [boolean, optional, default=false]
-->
<xs:element name="RegExpr">
@@ -547,15 +546,12 @@
<xs:element name="LineContinue">
<xs:complexType>
<xs:attributeGroup ref="commonAttributes"/>
- <xs:attribute name="char"/>
+ <xs:attribute name="char" type="char"/>
</xs:complexType>
</xs:element>
<!--
- Detect a C-style escaped character
+ Detect a C-style escaped character (as a regular expression: \\([abefnrtv"'?\\.]|x[0-9a-fA-F]{1,2}|[0-7]{1,3})).
commonAttributes: Common attributes
-
- TODO:
- - Did I get this right? Only one character, or a string?
-->
<xs:element name="HlCStringChar">
<xs:complexType>
@@ -563,7 +559,7 @@
</xs:complexType>
</xs:element>
<!--
- Detect a range of characters
+ Detect a string with defined start and end characters.
commonAttributes: Common attributes
char: The character starting the range
char1: The character terminating the range
@@ -571,8 +567,8 @@
<xs:element name="RangeDetect">
<xs:complexType>
<xs:attributeGroup ref="commonAttributes"/>
- <xs:attribute name="char" use="required"/>
- <xs:attribute name="char1" use="required"/>
+ <xs:attribute name="char" type="char" use="required"/>
+ <xs:attribute name="char1" type="char" use="required"/>
</xs:complexType>
</xs:element>
<!--
@@ -661,7 +657,7 @@
<xs:element name="encoding">
<xs:complexType>
<xs:attribute name="string" use="required"/>
- <xs:attribute name="char"/>
+ <xs:attribute name="char" type="char"/>
<xs:attribute name="ignored" type="xs:boolean"/>
</xs:complexType>
</xs:element>
diff --git a/src/libs/3rdparty/syntax-highlighting/data/schema/validatehl.sh b/src/libs/3rdparty/syntax-highlighting/data/schema/validatehl.sh
index 06bf60ce35..49be00d96e 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/schema/validatehl.sh
+++ b/src/libs/3rdparty/syntax-highlighting/data/schema/validatehl.sh
@@ -1,2 +1,3 @@
#!/bin/sh
-xmllint --noout --schema language.xsd $@
+schemadir=$(dirname "$0")
+xmllint --noout --schema "$schemadir"/language.xsd "$@"
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/alert.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/alert.xml
index ec881f1503..b6c290b0ba 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/alert.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/alert.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd">
-<language version="5" kateversion="3.1" name="Alerts" section="Other" extensions="" mimetype="" author="Dominik Haumann (dhaumann@kde.org)" license="MIT" hidden="true">
+<!DOCTYPE language>
+<language version="8" kateversion="3.1" name="Alerts" section="Other" extensions="" mimetype="" author="Dominik Haumann (dhaumann@kde.org)" license="MIT" hidden="true">
<highlighting>
<list name="alerts_hi">
<item>ALERT</item>
@@ -19,6 +19,7 @@
<item>WARNING</item>
<item>CAUTION</item>
<item>NOLINT</item>
+ <item>NOQA</item>
</list>
<list name="alerts_lo">
<item>###</item>
@@ -35,6 +36,8 @@
<WordDetect attribute="Region Marker" context="#stay" String="END" endRegion="AlertRegion2" />
<keyword attribute="Alert Level 1" context="#stay" String="alerts_hi" />
<keyword attribute="Alert Level 2" context="#stay" String="alerts_mid" />
+ <!-- Some (Python?) linters use `noqa:` (small case w/ colon) -->
+ <WordDetect attribute="Alert Level 2" context="#stay" String="noqa:" />
<keyword attribute="Alert Level 3" context="#stay" String="alerts_lo" />
</context>
</contexts>
@@ -47,7 +50,8 @@
</itemDatas>
</highlighting>
<general>
- <keywords casesensitive="1"/>
+ <!-- NO-BREAK SPACE (nbsp) as deliminator -->
+ <keywords casesensitive="1" additionalDeliminator="&#160;"/>
</general>
</language>
<!-- kate: indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/autoconf.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/autoconf.xml
new file mode 100644
index 0000000000..c8a5ca2e6b
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/autoconf.xml
@@ -0,0 +1,396 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "/opt/kde3/share/apps/katepart/syntax/language.dtd">
+<!-- (c) 2008-2011 by Jürgen Heinemann http://www.hjcms.de
+ @see http://www.gnu.org/software/automake/manual/autoconf/
+-->
+<language name="Autoconf Language" version="2" kateversion="2.4" section="Other"
+ extensions="configure.ac;configure.in;configure.in.in;*.m4;*.M4"
+ mimetype="text/x-m4;text/x-autoconf"
+ author="Juergen Heinemann (nospam@hjcms.de)" license="LGPL">
+ <highlighting>
+ <!-- http://www.gnu.org/software/automake/manual/autoconf/Program-_0026-Function-Index.html -->
+ <list name="keywords">
+ <item> if </item>
+ <item> then </item>
+ <item> elif </item>
+ <item> else </item>
+ <item> fi </item>
+ <item> for </item>
+ <item> in </item>
+ <item> do </item>
+ <item> don </item>
+ <item> function </item>
+ <item> select </item>
+ <item> until </item>
+ <item> while </item>
+ <item> set </item>
+ <item> ifelse </item>
+ <item> case </item>
+ <item> esac </item>
+ </list>
+
+ <!-- http://www.gnu.org/software/automake/manual/autoconf/Limitations-of-Builtins.html -->
+ <list name="builtins">
+ <item> : </item>
+ <item> source </item>
+ <item> alias </item>
+ <item> bg </item>
+ <item> bind </item>
+ <item> break </item>
+ <item> builtin </item>
+ <item> cd </item>
+ <item> caller </item>
+ <item> command </item>
+ <item> compgen </item>
+ <item> complete </item>
+ <item> continue </item>
+ <item> dirs </item>
+ <item> disown </item>
+ <item> echo </item>
+ <item> enable </item>
+ <item> eval </item>
+ <item> exec </item>
+ <item> exit </item>
+ <item> fc </item>
+ <item> fg </item>
+ <item> getopts </item>
+ <item> hash </item>
+ <item> help </item>
+ <item> history </item>
+ <item> jobs </item>
+ <item> kill </item>
+ <item> let </item>
+ <item> logout </item>
+ <item> popd </item>
+ <item> printf </item>
+ <item> pushd </item>
+ <item> pwd </item>
+ <item> return </item>
+ <item> set </item>
+ <item> shift </item>
+ <item> shopt </item>
+ <item> suspend </item>
+ <item> test </item>
+ <item> time </item>
+ <item> times </item>
+ <item> trap </item>
+ <item> type </item>
+ <item> ulimit </item>
+ <item> umask </item>
+ <item> unalias </item>
+ <item> wait </item>
+ </list>
+
+ <list name="bools">
+ <item> no </item>
+ <item> yes </item>
+ <item> false </item>
+ <item> true </item>
+ </list>
+
+ <!--
+ This is an alphabetical list of the M4, M4sugar, and M4sh macros.
+ http://www.gnu.org/software/automake/manual/autoconf/M4-Macro-Index.html
+ -->
+ <list name="m4sugar">
+ <item> AS_BOURNE_COMPATIBLE </item>
+ <item> AS_BOX </item>
+ <item> AS_CASE </item>
+ <item> AS_DIRNAME </item>
+ <item> AS_ECHO </item>
+ <item> AS_ECHO_N </item>
+ <item> AS_ESCAPE </item>
+ <item> AS_EXIT </item>
+ <item> AS_HELP_STRING </item>
+ <item> AS_IF </item>
+ <item> AS_INIT </item>
+ <item> AS_INIT_GENERATED </item>
+ <item> AS_LINENO_PREPARE </item>
+ <item> AS_LITERAL_IF </item>
+ <item> AS_LITERAL_WORD_IF </item>
+ <item> AS_ME_PREPARE </item>
+ <item> AS_MESSAGE_FD </item>
+ <item> AS_MESSAGE_LOG_FD </item>
+ <item> AS_MKDIR_P </item>
+ <item> AS_ORIGINAL_STDIN_FD </item>
+ <item> AS_SET_CATFILE </item>
+ <item> AS_SET_STATUS </item>
+ <item> AS_SHELL_SANITIZE </item>
+ <item> AS_TMPDIR </item>
+ <item> AS_TR_CPP </item>
+ <item> AS_TR_SH </item>
+ <item> AS_UNSET </item>
+ <item> AS_VAR_APPEND </item>
+ <item> AS_VAR_ARITH </item>
+ <item> AS_VAR_COPY </item>
+ <item> AS_VAR_IF </item>
+ <item> AS_VAR_POPDEF </item>
+ <item> AS_VAR_PUSHDEF </item>
+ <item> AS_VAR_SET </item>
+ <item> AS_VAR_SET_IF </item>
+ <item> AS_VAR_TEST_SET </item>
+ <item> AS_VERSION_COMPARE </item>
+ <item> m4_append </item>
+ <item> m4_append_uniq </item>
+ <item> m4_append_uniq_w </item>
+ <item> m4_apply </item>
+ <item> m4_argn </item>
+ <item> m4_assert </item>
+ <item> m4_bmatch </item>
+ <item> m4_bpatsubst </item>
+ <item> m4_bpatsubsts </item>
+ <item> m4_bregexp </item>
+ <item> m4_builtin </item>
+ <item> m4_car </item>
+ <item> m4_case </item>
+ <item> m4_cdr </item>
+ <item> m4_changecom </item>
+ <item> m4_changequote </item>
+ <item> m4_chomp </item>
+ <item> m4_chomp_all </item>
+ <item> m4_cleardivert </item>
+ <item> m4_cmp </item>
+ <item> m4_combine </item>
+ <item> m4_cond </item>
+ <item> m4_copy </item>
+ <item> m4_copy_force </item>
+ <item> m4_count </item>
+ <item> m4_curry </item>
+ <item> m4_debugfile </item>
+ <item> m4_debugmode </item>
+ <item> m4_decr </item>
+ <item> m4_default </item>
+ <item> m4_default_nblank </item>
+ <item> m4_default_nblank_quoted </item>
+ <item> m4_default_quoted </item>
+ <item> m4_define </item>
+ <item> m4_define_default </item>
+ <item> m4_defn </item>
+ <item> m4_divert </item>
+ <item> m4_divert_once </item>
+ <item> m4_divert_pop </item>
+ <item> m4_divert_push </item>
+ <item> m4_divert_text </item>
+ <item> m4_divnum </item>
+ <item> m4_do </item>
+ <item> m4_dquote </item>
+ <item> m4_dquote_elt </item>
+ <item> m4_dumpdef </item>
+ <item> m4_dumpdefs </item>
+ <item> m4_echo </item>
+ <item> m4_errprint </item>
+ <item> m4_errprintn </item>
+ <item> m4_escape </item>
+ <item> m4_esyscmd </item>
+ <item> m4_esyscmd_s </item>
+ <item> m4_eval </item>
+ <item> m4_exit </item>
+ <item> m4_expand </item>
+ <item> m4_fatal </item>
+ <item> m4_flatten </item>
+ <item> m4_for </item>
+ <item> m4_foreach </item>
+ <item> m4_foreach_w </item>
+ <item> m4_format </item>
+ <item> m4_if </item>
+ <item> m4_ifblank </item>
+ <item> m4_ifdef </item>
+ <item> m4_ifnblank </item>
+ <item> m4_ifndef </item>
+ <item> m4_ifset </item>
+ <item> m4_ifval </item>
+ <item> m4_ifvaln </item>
+ <item> m4_ignore </item>
+ <item> m4_include </item>
+ <item> m4_incr </item>
+ <item> m4_index </item>
+ <item> m4_indir </item>
+ <item> m4_init </item>
+ <item> m4_join </item>
+ <item> m4_joinall </item>
+ <item> m4_len </item>
+ <item> m4_list_cmp </item>
+ <item> m4_location </item>
+ <item> m4_make_list </item>
+ <item> m4_maketemp </item>
+ <item> m4_map </item>
+ <item> m4_map_args </item>
+ <item> m4_map_args_pair </item>
+ <item> m4_map_args_sep </item>
+ <item> m4_map_args_w </item>
+ <item> m4_map_sep </item>
+ <item> m4_mapall </item>
+ <item> m4_mapall_sep </item>
+ <item> m4_max </item>
+ <item> m4_min </item>
+ <item> m4_mkstemp </item>
+ <item> m4_n </item>
+ <item> m4_newline </item>
+ <item> m4_normalize </item>
+ <item> m4_pattern_allow </item>
+ <item> m4_pattern_forbid </item>
+ <item> m4_popdef </item>
+ <item> m4_pushdef </item>
+ <item> m4_quote </item>
+ <item> m4_re_escape </item>
+ <item> m4_rename </item>
+ <item> m4_rename_force </item>
+ <item> m4_reverse </item>
+ <item> m4_set_add </item>
+ <item> m4_set_add_all </item>
+ <item> m4_set_contains </item>
+ <item> m4_set_contents </item>
+ <item> m4_set_delete </item>
+ <item> m4_set_difference </item>
+ <item> m4_set_dump </item>
+ <item> m4_set_empty </item>
+ <item> m4_set_foreach </item>
+ <item> m4_set_intersection </item>
+ <item> m4_set_list </item>
+ <item> m4_set_listc </item>
+ <item> m4_set_map </item>
+ <item> m4_set_map_sep </item>
+ <item> m4_set_remove </item>
+ <item> m4_set_size </item>
+ <item> m4_set_union </item>
+ <item> m4_shift </item>
+ <item> m4_shift2 </item>
+ <item> m4_shift3 </item>
+ <item> m4_shiftn </item>
+ <item> m4_sign </item>
+ <item> m4_sinclude </item>
+ <item> m4_split </item>
+ <item> m4_stack_foreach </item>
+ <item> m4_stack_foreach_lifo </item>
+ <item> m4_stack_foreach_sep </item>
+ <item> m4_stack_foreach_sep_lifo </item>
+ <item> m4_strip </item>
+ <item> m4_substr </item>
+ <item> m4_syscmd </item>
+ <item> m4_sysval </item>
+ <item> m4_text_box </item>
+ <item> m4_text_wrap </item>
+ <item> m4_tolower </item>
+ <item> m4_toupper </item>
+ <item> m4_traceoff </item>
+ <item> m4_traceon </item>
+ <item> m4_translit </item>
+ <item> m4_undefine </item>
+ <item> m4_undivert </item>
+ <item> m4_unquote </item>
+ <item> m4_version_compare </item>
+ <item> m4_version_prereq </item>
+ <item> m4_warn </item>
+ <item> m4_wrap </item>
+ <item> m4_wrap_lifo </item>
+ </list>
+
+ <!-- Autotest Macro Index -->
+ <list name="autotest_macro">
+ <item> AT_ARG_OPTION </item>
+ <item> AT_ARG_OPTION_ARG </item>
+ <item> AT_BANNER </item>
+ <item> AT_CAPTURE_FILE </item>
+ <item> AT_CHECK </item>
+ <item> AT_CHECK_EUNIT </item>
+ <item> AT_CHECK_UNQUOTED </item>
+ <item> AT_CLEANUP </item>
+ <item> AT_COLOR_TESTS </item>
+ <item> AT_COPYRIGHT </item>
+ <item> AT_DATA </item>
+ <item> AT_FAIL_IF </item>
+ <item> AT_INIT </item>
+ <item> AT_KEYWORDS </item>
+ <item> AT_PACKAGE_BUGREPORT </item>
+ <item> AT_PACKAGE_NAME </item>
+ <item> AT_PACKAGE_STRING </item>
+ <item> AT_PACKAGE_TARNAME </item>
+ <item> AT_PACKAGE_URL </item>
+ <item> AT_PACKAGE_VERSION </item>
+ <item> AT_SETUP </item>
+ <item> AT_SKIP_IF </item>
+ <item> AT_TESTED </item>
+ <item> AT_XFAIL_IF </item>
+ </list>
+
+ <list name="libtool">
+ <item> LT_PREREQ </item>
+ <item> LT_LANG </item>
+ <item> LT_INIT </item>
+ <item> LTDL_INIT </item>
+ <item> LT_CONFIG_LTDL_DIR </item>
+ </list>
+
+ <list name="pkgconfig">
+ <item> PKG_CHECK_MODULES </item>
+ <item> PKG_PROG_PKG_CONFIG </item>
+ <item> PKG_CHECK_EXISTS </item>
+ </list>
+
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Default">
+ <!-- <IncludeRules context="##Bash" /> -->
+ <keyword attribute="Keyword" context="#stay" String="keywords" />
+ <RegExpr attribute="Builtin" context="#stay" String="\.(?=\s)" />
+ <keyword attribute="Builtin" context="#stay" String="builtins" />
+ <keyword attribute="Boolean" context="#stay" String="bools" />
+ <!-- Autoconf Macros -->
+ <keyword attribute="M4 Sugar Macros" context="#stay" String="m4sugar" />
+ <!-- Autotest Macro Index -->
+ <keyword attribute="Autotest Macros" context="#stay" String="autotest_macro" />
+ <!-- Other Macros -->
+ <keyword attribute="pkg-config Macros" context="#stay" String="pkgconfig" />
+ <!-- libtool Macros -->
+ <keyword attribute="Libtool Macros" context="#stay" String="libtool" />
+ <!-- Autoconf Macros -->
+ <RegExpr attribute="Autoconf Macros" context="#stay" String="\bAC_[A-Z0-9_]+\b" insensitive="false" endRegion="BeginRegion" />
+ <!-- Automake Macros -->
+ <RegExpr attribute="Automake Macros" context="#stay" String="\bAM_[A-Z0-9_]+\b" insensitive="false" endRegion="BeginRegion" />
+ <!-- Script temp Defined Macros -->
+ <RegExpr attribute="Inline Macros" context="#stay" String="\bac_[a-z_]+\b" insensitive="false" endRegion="BeginRegion" />
+ <RegExpr attribute="Char" context="#stay" String="'.'"/>
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <AnyChar attribute="Symbol" context="#stay" String=":!%&amp;()+,-/.*&lt;=&gt;|"/>
+ <RegExpr attribute="Variable" context="#stay" String="\$[a-z_]+" insensitive="true" endRegion="BeginRegion" />
+ <Float attribute="Float" context="#stay"/>
+ <Int attribute="Decimal" context="#stay"/>
+ <RegExpr attribute="Pragmas" context="#stay" String="([\s\t]+#).*$" insensitive="true" endRegion="BeginRegion" />
+ <RegExpr attribute="Comment" context="#stay" String="(^#).*$" insensitive="true" endRegion="BeginRegion" />
+ <RegExpr attribute="Comment" context="#stay" String="(\bdnl\s).*$" insensitive="true" endRegion="BeginRegion" />
+ </context>
+ <context attribute="Region Marker" lineEndContext="#pop" name="Region Marker"/>
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal" />
+ <itemData name="Keyword" defStyleNum="dsKeyword" />
+ <itemData name="Builtin" defStyleNum="dsKeyword" color="#808" />
+ <itemData name="M4 Sugar Macros" defStyleNum="dsKeyword" />
+ <itemData name="Autotest Macros" defStyleNum="dsKeyword" />
+ <itemData name="Autoconf Macros" defStyleNum="dsKeyword" color="#0095ff" selColor="#ffffff" bold="1" />
+ <itemData name="Automake Macros" defStyleNum="dsKeyword" color="#6666cc" selColor="#ffffff" bold="1" />
+ <itemData name="Libtool Macros" defStyleNum="dsKeyword" color="#6666cc" selColor="#ffffff" bold="1" />
+ <itemData name="Inline Macros" defStyleNum="dsKeyword" color="#6666cc" selColor="#ffffff" />
+ <itemData name="pkg-config Macros" defStyleNum="dsKeyword" />
+ <itemData name="Boolean" defStyleNum="dsOthers" />
+ <itemData name="Variable" defStyleNum="dsOthers" />
+ <itemData name="Decimal" defStyleNum="dsDecVal" />
+ <itemData name="Float" defStyleNum="dsFloat" />
+ <itemData name="Char" defStyleNum="dsChar" />
+ <itemData name="String" defStyleNum="dsString" />
+ <itemData name="Pragmas" defStyleNum="dsOthers" color="#00C000" />
+ <itemData name="Comment" defStyleNum="dsComment" />
+ <itemData name="Symbol" defStyleNum="dsNormal"/>
+ <itemData name="Region Marker" defStyleNum="dsRegionMarker" />
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="dnl" />
+ <comment name="multiLine" start="/*" end="*/" />
+ </comments>
+ <keywords casesensitive="0" />
+ </general>
+</language>
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/bash.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/bash.xml
index cdfdf95844..b3e8334fda 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/bash.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/bash.xml
@@ -1,14 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd"
+<!DOCTYPE language
[
- <!ENTITY funcname "[A-Za-z_:][A-Za-z0-9_:#&#37;@-]*">
- <!ENTITY varname "[A-Za-z_][A-Za-z0-9_]*">
- <!ENTITY word "[^|&amp;;()&lt;&gt;\s]+"> <!-- see man bash -->
- <!ENTITY eos "(?=($|\s))"> <!-- eol or space following -->
- <!ENTITY noword "(?![\w$+-])"> <!-- no word, $, + or - following -->
- <!ENTITY pathpart "([\w_@.&#37;*?+-]|\\ )"> <!-- valid character in a file name -->
+ <!ENTITY tab "&#009;">
+ <!ENTITY funcname "([^&_fragpathseps;}=#$]|[+!@](?!\())([^&_fragpathseps;}=$]*+([+!@](?!\())?+)*+">
+ <!ENTITY varname "[A-Za-z_][A-Za-z0-9_]*">
+ <!ENTITY eos "(?=$|[ &tab;])"> <!-- eol or space following -->
+ <!ENTITY eoexpr "(?=$|[ &tab;&lt;>|&amp;;)])">
+
+ <!ENTITY substseps "${'&quot;`\\">
+ <!ENTITY symbolseps "&lt;>|&amp;;()"> <!-- see man bash -->
+ <!ENTITY wordseps " &tab;&symbolseps;"> <!-- see man bash -->
+
+ <!ENTITY bq_string "`[^`]*+`">
+ <!ENTITY sq_string "'[^']*+'">
+ <!ENTITY dq_string "&quot;(?:[^&quot;\\`]*+|&bq_string;|\\.)*+&quot;">
+ <!ENTITY strings "(?:&sq_string;|&dq_string;|&bq_string;)">
+
+ <!ENTITY globany "\[(?:[^&wordseps;&quot;'`\\\[\]]+|\\.|&strings;|\[:\w+:\]|\[)*\]">
+
+ <!ENTITY next_is_globany "(?:\^?\]\]|(?:\^?\]?)?(?:\\.|[^'&quot; &tab;&lt;>|&amp;;()\\[\]]+|&strings;|\[:\w+:\])+\])">
+ <!ENTITY bracket_noglobany "\[(?!&next_is_globany;)">
+
+ <!ENTITY _fragpathseps "*?+!@&wordseps;&substseps;">
+ <!ENTITY _fragpathnosep "(?:[+!@](?!\()|\\.|&_brace_noexpansion;)?+">
+ <!ENTITY path "(?:[^&_fragpathseps;\[]*+&_fragpathnosep;)*+">
+ <!ENTITY fragpath "(?:[^&_fragpathseps;\[/]*+&_fragpathnosep;)*+">
+ <!ENTITY opt "(?:[^&_fragpathseps;\[=/]*+&_fragpathnosep;)*+">
+ <!ENTITY pathpart "(?:~|\.\.?|&fragpath;)(?:/&path;|(?=[*?]|[+!@]\(|&fragpath;(?:[/*?]|\[&next_is_globany;|[+!@]\()))|(?:~|\.\.?)(?=[&wordseps;]|$)">
+
+ <!ENTITY _fragpathseps_alt "*?+!@&substseps;}">
+ <!ENTITY _fragpathnosep_alt "(?:[+!@](?!\()|\\.)?+">
+ <!ENTITY path_alt "(?:[^&_fragpathseps_alt;]*+&_fragpathnosep_alt;)*+">
+ <!ENTITY fragpath_alt "(?:[^&_fragpathseps_alt;/]*+&_fragpathnosep_alt;)*+">
+ <!ENTITY opt_alt "(?:[^&_fragpathseps_alt;=/]*+&_fragpathnosep_alt;)*+">
+ <!ENTITY pathpart_alt "(?:~|\.\.?|&fragpath_alt;)(?:/&path_alt;|(?=[*?]|[+!@]\(|&fragpath_alt;(?:[/*?]|[+!@]\()))|(?:~|\.\.?)(?=\}|$)">
+
+ <!-- Path only with / -->
+ <!ENTITY path_with_sep_text "[^?*+!@&wordseps;'&quot;`\\/]">
+ <!ENTITY path_with_sep_text2 "[^?*+!@()'&quot;`\\/]">
+ <!ENTITY path_with_sep_expr "\\.|&strings;|&globany;|[?*+](?!\()">
+ <!ENTITY path_with_sep_spe "(~|\.\.?)($|[/ &tab;&lt;>|&amp;;)])">
+ <!ENTITY path_with_sep_sub "[?*+!@]\((&path_with_sep_text2;++|&path_with_sep_expr;)*+(\)|(?=/))">
+ <!ENTITY path_with_sep "/|&path_with_sep_spe;|(&path_with_sep_text;++|&path_with_sep_expr;|&path_with_sep_sub;)*+/">
+
+ <!ENTITY _braceexpansion_spe " &tab;&lt;>|&amp;;{}\\`'&quot;$">
+ <!ENTITY _brace_noexpansion "\{[^&_braceexpansion_spe;,]*+\}">
+ <!ENTITY _braceexpansion_var "\$(?:\{[^\[\]&_braceexpansion_spe;]*+(?:\[[*@a-zA-Z0-9]\])\})?">
+ <!ENTITY _braceexpansion_string "'[^']*'|&quot;([^&quot;\`]*+|`[^`]*`)*+&quot;">
+ <!ENTITY _braceexpansion_elems "\\.|`[^`]*`|&_braceexpansion_string;|&_braceexpansion_var;|&_brace_noexpansion;">
+ <!ENTITY _braceexpansion_consume "&_braceexpansion_elems;|{(?:[^&_braceexpansion_spe;,]++|&_braceexpansion_elems;)*?}|(?R)?+">
+ <!ENTITY _braceexpansion "(?:[^&_braceexpansion_spe;,]++|&_braceexpansion_consume;)*?,(?:[^&_braceexpansion_spe;]++|&_braceexpansion_consume;)*?}">
+ <!ENTITY braceexpansion "{&_braceexpansion;">
+
+ <!ENTITY heredocq "(?|&quot;([^&quot;]+)&quot;|'([^']+)'|\\(.[^&wordseps;&substseps;]*))">
+
+ <!ENTITY arithmetic_as_subshell "\(((?:[^`'&quot;()$]++|\$\{[^`'&quot;(){}$]+\}|\$(?=[^{`'&quot;()])|`[^`]*+`|\((?1)(?:[)]|(?=['&quot;])))++)(?:[)](?=$|[^)])|[&quot;'])">
+
+ <!ENTITY unary_operators "-[abcdefghkprstuwxGLNOSovRnz]&eos;">
+ <!ENTITY binary_operators "(?:-(?:e[fq]|[nolg]t|[nlg]e)|==?|!=)&eos;">
+
+ <!ENTITY dblbracket_close "\]\](?=($|[ &tab;;|&amp;)]))">
+
+ <!ENTITY weakDeliminatorSymbols "^&#37;#[]$.{}:-/">
]>
-<language name="Bash" version="9" kateversion="5.0" section="Scripts" extensions="*.sh;*.bash;*.ebuild;*.eclass;*.nix;.bashrc;.bash_profile;.bash_login;.profile;PKGBUILD;APKBUILD" mimetype="application/x-shellscript" casesensitive="1" author="Wilbert Berendsen (wilbert@kde.nl)" license="LGPL">
+
+<language
+ name="Bash"
+ version="51"
+ kateversion="5.79"
+ section="Scripts"
+ extensions="*.sh;*.bash;*.ebuild;*.eclass;*.exlib;*.exheres-0;.bashrc;.bash_profile;.bash_login;.profile;PKGBUILD;APKBUILD"
+ mimetype="application/x-shellscript"
+ casesensitive="1"
+ author="Wilbert Berendsen (wilbert@kde.nl)"
+ license="LGPL"
+ >
<!-- (c) 2004 by Wilbert Berendsen (wilbert@kde.nl)
Changes by Matthew Woehlke (mw_triad@users.sourceforge.net)
@@ -18,32 +84,40 @@
<highlighting>
<list name="keywords">
+ <item>break</item>
+ <item>case</item>
+ <item>continue</item>
+ <item>do</item>
+ <item>done</item>
+ <item>elif</item>
<item>else</item>
+ <item>esac</item>
+ <item>fi</item>
<item>for</item>
<item>function</item>
+ <item>if</item>
<item>in</item>
+ <item>return</item>
<item>select</item>
+ <item>then</item>
<item>until</item>
<item>while</item>
- <item>elif</item>
- <item>then</item>
- <item>set</item>
</list>
<list name="builtins">
+ <item>.</item>
<item>:</item>
<item>source</item>
<item>alias</item>
<item>bg</item>
<item>bind</item>
- <item>break</item>
<item>builtin</item>
<item>cd</item>
<item>caller</item>
<item>command</item>
<item>compgen</item>
<item>complete</item>
- <item>continue</item>
+ <item>coproc</item>
<item>dirs</item>
<item>disown</item>
<item>echo</item>
@@ -53,19 +127,16 @@
<item>exit</item>
<item>fc</item>
<item>fg</item>
- <item>getopts</item>
<item>hash</item>
<item>help</item>
<item>history</item>
<item>jobs</item>
<item>kill</item>
- <item>let</item>
<item>logout</item>
<item>popd</item>
<item>printf</item>
<item>pushd</item>
<item>pwd</item>
- <item>return</item>
<item>set</item>
<item>shift</item>
<item>shopt</item>
@@ -83,18 +154,23 @@
<list name="builtins_var">
<item>export</item>
- <item>unset</item>
<item>declare</item>
- <item>typeset</item>
+ <item>getopts</item>
+ <item>let</item>
<item>local</item>
<item>read</item>
<item>readonly</item>
+ <item>typeset</item>
+ <item>unset</item>
</list>
<list name="unixcommands">
<!-- /bin -->
<item>arch</item>
<item>awk</item>
+ <item>b2sum</item>
+ <item>base32</item>
+ <item>base64</item>
<item>bash</item>
<item>bunzip2</item>
<item>bzcat</item>
@@ -109,11 +185,15 @@
<item>bzmore</item>
<item>cat</item>
<item>chattr</item>
+ <item>chcon</item>
<item>chgrp</item>
<item>chmod</item>
<item>chown</item>
<item>chvt</item>
+ <item>cksum</item>
<item>cp</item>
+ <item>crontab</item>
+ <item>csplit</item>
<item>date</item>
<item>dd</item>
<item>deallocvt</item>
@@ -125,12 +205,13 @@
<item>domainname</item>
<item>du</item>
<item>dumpkeys</item>
- <item>echo</item>
<item>ed</item>
<item>egrep</item>
+ <item>expand</item>
<item>false</item>
<item>fgconsole</item>
<item>fgrep</item>
+ <item>fold</item>
<item>fuser</item>
<item>gawk</item>
<item>getkeycodes</item>
@@ -141,7 +222,9 @@
<item>gunzip</item>
<item>gzexe</item>
<item>gzip</item>
+ <item>hostid</item>
<item>hostname</item>
+ <item>iconv</item>
<item>igawk</item>
<item>install</item>
<item>kbd_mode</item>
@@ -165,11 +248,11 @@
<item>lzfgrep</item>
<item>lzgrep</item>
<item>lzless</item>
- <item>lzcat</item>
<item>lzma</item>
<item>lzmainfo</item>
<item>lzmore</item>
<item>mapscrn</item>
+ <item>md5sum</item>
<item>mesg</item>
<item>mkdir</item>
<item>mkfifo</item>
@@ -181,21 +264,29 @@
<item>nano</item>
<item>netstat</item>
<item>nisdomainname</item>
+ <item>nproc</item>
<item>nroff</item>
+ <item>numfmt</item>
<item>openvt</item>
+ <item>paste</item>
+ <item>pathchk</item>
<item>pgawk</item>
<item>pidof</item>
<item>ping</item>
+ <item>pinky</item>
+ <item>printenv</item>
<item>ps</item>
<item>pstree</item>
- <item>pwd</item>
+ <item>ptx</item>
<item>rbash</item>
<item>readlink</item>
+ <item>realpath</item>
<item>red</item>
<item>resizecons</item>
<item>rm</item>
<item>rmdir</item>
<item>run-parts</item>
+ <item>runcon</item>
<item>sash</item>
<item>sed</item>
<item>setfont</item>
@@ -204,21 +295,33 @@
<item>setmetamode</item>
<item>setserial</item>
<item>sh</item>
+ <item>sha1sum</item>
+ <item>sha224sum</item>
+ <item>sha256sum</item>
+ <item>sha384sum</item>
+ <item>sha512sum</item>
<item>showkey</item>
<item>shred</item>
+ <item>shuf</item>
<item>sleep</item>
<item>ssed</item>
<item>stat</item>
+ <item>stdbuf</item>
<item>stty</item>
<item>su</item>
<item>sync</item>
<item>tar</item>
<item>tempfile</item>
+ <item>timeout</item>
<item>touch</item>
+ <item>tput</item>
<item>troff</item>
<item>true</item>
+ <item>truncate</item>
+ <item>tty</item>
<item>umount</item>
<item>uname</item>
+ <item>unexpand</item>
<item>unicode_start</item>
<item>unicode_stop</item>
<item>unlink</item>
@@ -227,6 +330,7 @@
<item>utmpdump</item>
<item>uuidgen</item>
<item>vdir</item>
+ <item>vi</item>
<item>wall</item>
<item>wc</item>
<item>xz</item>
@@ -258,13 +362,11 @@
<item>autoconf</item>
<item>autoheader</item>
<item>automake</item>
- <item>awk</item>
<item>basename</item>
<item>bc</item>
<item>bison</item>
<item>c++</item>
<item>cal</item>
- <item>cat</item>
<item>cc</item>
<item>cdda2wav</item>
<item>cdparanoia</item>
@@ -272,9 +374,6 @@
<item>cd-read</item>
<item>cdrecord</item>
<item>chfn</item>
- <item>chgrp</item>
- <item>chmod</item>
- <item>chown</item>
<item>chroot</item>
<item>chsh</item>
<item>clear</item>
@@ -282,20 +381,14 @@
<item>co</item>
<item>col</item>
<item>comm</item>
- <item>cp</item>
<item>cpio</item>
<item>cpp</item>
<item>cut</item>
<item>dc</item>
- <item>dd</item>
- <item>df</item>
<item>diff</item>
<item>diff3</item>
- <item>dir</item>
- <item>dircolors</item>
<item>directomatic</item>
<item>dirname</item>
- <item>du</item>
<item>env</item>
<item>expr</item>
<item>fbset</item>
@@ -307,12 +400,11 @@
<item>free</item>
<item>ftp</item>
<item>funzip</item>
- <item>fuser</item>
<item>g++</item>
- <item>gawk</item>
<item>gc</item>
<item>gcc</item>
<item>clang</item>
+ <item>clang++</item>
<item>valgrind</item>
<item>xdg-open</item>
<item>cmake</item>
@@ -333,30 +425,22 @@
<item>head</item>
<item>hexdump</item>
<item>id</item>
- <item>install</item>
<item>join</item>
- <item>kill</item>
- <item>killall</item>
<item>ld</item>
<item>ld86</item>
<item>ldd</item>
<item>less</item>
<item>lex</item>
- <item>ln</item>
<item>locate</item>
<item>lockfile</item>
<item>logname</item>
<item>lp</item>
<item>lpr</item>
- <item>ls</item>
<item>lynx</item>
<item>m4</item>
<item>make</item>
<item>man</item>
- <item>mkdir</item>
- <item>mknod</item>
<item>msgfmt</item>
- <item>mv</item>
<item>namei</item>
<item>nasm</item>
<item>nawk</item>
@@ -374,9 +458,7 @@
<item>pcretest</item>
<item>perl</item>
<item>perror</item>
- <item>pidof</item>
<item>pr</item>
- <item>printf</item>
<item>procmail</item>
<item>prune</item>
<item>ps2ascii</item>
@@ -392,12 +474,9 @@
<item>pstops</item>
<item>rcs</item>
<item>rev</item>
- <item>rm</item>
<item>scp</item>
- <item>sed</item>
<item>seq</item>
<item>setterm</item>
- <item>shred</item>
<item>size</item>
<item>size86</item>
<item>skill</item>
@@ -411,7 +490,6 @@
<item>ssh-agent</item>
<item>ssh-keygen</item>
<item>ssh-keyscan</item>
- <item>stat</item>
<item>strings</item>
<item>strip</item>
<item>sudo</item>
@@ -420,10 +498,9 @@
<item>tac</item>
<item>tail</item>
<item>tee</item>
- <item>test</item>
<item>tr</item>
+ <item>tsort</item>
<item>uniq</item>
- <item>unlink</item>
<item>unzip</item>
<item>updatedb</item>
<item>updmap</item>
@@ -431,7 +508,6 @@
<item>users</item>
<item>vmstat</item>
<item>w</item>
- <item>wc</item>
<item>wget</item>
<item>whatis</item>
<item>whereis</item>
@@ -456,525 +532,1377 @@
<contexts>
- <context attribute="Normal Text" lineEndContext="#stay" name="Start">
- <IncludeRules context="FindAll" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="Start" fallthroughContext="Command">
+ <IncludeRules context="BashOneLine"/>
</context>
-<!-- ====== The following rulessets are meant to be included ======== -->
- <!-- FindAll tries to interpret everything -->
- <context attribute="Normal Text" lineEndContext="#stay" name="FindAll">
- <IncludeRules context="FindComments" />
- <IncludeRules context="FindCommands" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
- <IncludeRules context="FindOthers" />
+ <!-- used by other syntaxes -->
+ <context attribute="Normal Text" lineEndContext="#pop" name="FindOneLineBackq" fallthroughContext="#pop">
+ <DetectChar attribute="Backquote" context="OneLineBackq" char="`"/>
</context>
-
- <!-- FindMost tries to interpret anything except commands -->
- <context attribute="Normal Text" lineEndContext="#stay" name="FindMost">
- <IncludeRules context="FindComments" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
- <IncludeRules context="FindOthers" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="OneLineBackq" fallthroughContext="Command">
+ <DetectChar attribute="Backquote" context="#pop" char="`"/>
+ <IncludeRules context="Start"/>
</context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="BashOneLine" fallthroughContext="Command">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <DetectChar attribute="Comment" context="Comment" char="#"/>
+ <!-- start expression in double parentheses -->
+ <Detect2Chars context="ExprDblParenOrSubShell" char="(" char1="(" lookAhead="1"/>
+ <!-- start a subshell -->
+ <DetectChar attribute="Keyword" context="SubShell" char="(" beginRegion="subshell"/>
+ <!-- start expression in single/double brackets -->
+ <DetectChar context="MaybeBracketExpression" char="[" lookAhead="1"/>
+ <!-- start a group command or BraceExpansion with { -->
+ <DetectChar context="MaybeGroup" char="{" lookAhead="1"/>
+
+ <!-- handle ` -->
+ <DetectChar attribute="Backquote" context="CommandBackq" char="`"/>
+
+ <!-- &> and &>> redirection -->
+ <StringDetect attribute="Redirection" context="WordRedirection" String="&amp;>>"/>
+ <Detect2Chars attribute="Redirection" context="FdRedirection" char="&amp;" char1=">"/>
+
+ <!-- handle branche conditions -->
+ <Detect2Chars attribute="Control" context="#stay" char="&amp;" char1="&amp;"/>
+ <Detect2Chars attribute="Control" context="#stay" char="|" char1="|"/>
+
+ <!-- handle &, |, ; -->
+ <AnyChar attribute="Control" context="#stay" String="&amp;|;"/>
+
+ <!-- handle variable assignments -->
+ <RegExpr attribute="Variable" context="VarAssign" String="&varname;(?=\+?=|\[(?:$|[^]]))"/>
+ <!-- handle keywords -->
+ <keyword context="DispatchKeyword" String="keywords" lookAhead="1"/>
+ <!-- handle commands that have variable names as argument -->
+ <keyword attribute="Builtin" context="VarName" String="builtins_var" lookAhead="1"/>
+ <!-- mark function definitions without function keyword -->
+ <RegExpr attribute="Function" context="#stay" String="&funcname;[ &tab;]*\(\)"/>
+ <!-- Special case for `: << '#BLOCK-COMMENT' form of "multiline" comments-->
+ <RegExpr attribute="Normal Text" context="PreHereDocMLComment" String="^:[ &tab;]+&lt;&lt;[ &tab;]*'#([^']+)'$" lookAhead="1" column="0"/>
+ <WordDetect attribute="Builtin" context="Coproc" String="coproc"/>
+ <keyword attribute="Builtin" context="CommandArgs" String="builtins"/>
+ <keyword attribute="Command" context="CommandArgs" String="unixcommands"/>
+
+ <!-- handle redirection -->
+ <AnyChar context="CommandMaybeRedirection" String="&lt;&gt;0123456789" lookAhead="1"/>
- <!-- FindComments consumes shell comments till EOL -->
- <context attribute="Normal Text" lineEndContext="#pop" name="FindComments">
- <DetectChar attribute="Comment" context="Comment" char="#" firstNonSpace="true"/>
- <RegExpr attribute="Normal Text" context="Comment" String="[\s;](?=#)" />
+ <AnyChar attribute="Error" context="#stay" String=")}"/>
+
+ <LineContinue attribute="Escape" context="#stay"/>
+
+ <Detect2Chars attribute="Expression" context="#stay" char="!" char1=" "/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="MaybeBracketExpression" fallthroughContext="#pop!Command">
+ <!-- start expression in double brackets -->
+ <RegExpr attribute="Keyword" context="#pop!ExprDblBracket" String="\[\[(?=$|[ &tab;(])" beginRegion="expression"/>
+ <!-- start expression in single brackets -->
+ <RegExpr attribute="Builtin" context="#pop!ExprBracket" String="\[&eos;" beginRegion="expression"/>
</context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="MaybeGroup">
+ <!-- start a group command with { -->
+ <RegExpr attribute="Keyword" context="#pop!Group" String="\{&eos;" beginRegion="group"/>
+ <DetectChar context="#pop!Command" char="{" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="CommandMaybeRedirection" fallthroughContext="#pop!Command">
+ <IncludeRules context="FindRedirection"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Return" fallthroughContext="#pop">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <Int attribute="Decimal" context="#stay"/>
+ </context>
+ <!-- Comment consumes shell comments till EOL -->
<context attribute="Comment" lineEndContext="#pop" name="Comment">
- <IncludeRules context="##Alerts" />
- <IncludeRules context="##Modelines" />
+ <DetectSpaces attribute="Comment"/>
+ <IncludeRules context="##Comments"/>
+ <DetectIdentifier attribute="Comment" context="#stay"/>
</context>
- <!-- FindCommentsParen consumes shell comments till EOL or a closing parenthese -->
- <context attribute="Normal Text" lineEndContext="#pop" name="FindCommentsParen">
- <DetectChar attribute="Comment" context="CommentParen" char="#" firstNonSpace="true"/>
- <RegExpr attribute="Normal Text" context="CommentParen" String="[\s;](?=#)" />
+ <!-- Group is called after a { is encountered -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="Group" fallthroughContext="Command">
+ <DetectChar context="MaybeGroupEnd" char="}" lookAhead="1"/>
+ <IncludeRules context="Start"/>
</context>
- <context attribute="Comment" lineEndContext="#pop" name="CommentParen">
- <RegExpr attribute="Comment" context="#pop" String="[^)](?=\))" />
- <IncludeRules context="##Alerts" />
+ <context attribute="Normal Text" lineEndContext="#pop" name="MaybeGroupEnd">
+ <!-- stop a group command with } -->
+ <RegExpr attribute="Keyword" context="#pop#pop" String="\}&eoexpr;" endRegion="group"/>
+ <DetectChar context="#pop!Command" char="}" lookAhead="1"/>
</context>
- <!-- FindCommentsBackq consumes shell comments till EOL or a backquote -->
- <context attribute="Normal Text" lineEndContext="#pop" name="FindCommentsBackq">
- <DetectChar attribute="Comment" context="CommentBackq" char="#" firstNonSpace="true"/>
- <RegExpr attribute="Normal Text" context="CommentBackq" String="[\s;](?=#)" />
+ <context attribute="Normal Text" lineEndContext="#pop" name="Coproc" fallthroughContext="#pop">
+ <DetectSpaces attribute="Normal Text"/>
+ <LineContinue attribute="Escape" context="#stay"/>
+ <RegExpr context="CoprocName" String="(\$?&varname;|\$\{&varname;\}|'&varname;'|&quot;(\$?&varname;|\$\{&varname;\}|[a-zA-Z0-9_])+&quot;)+(?=\s+\{($|\s))" lookAhead="1"/>
</context>
- <context attribute="Comment" lineEndContext="#pop" name="CommentBackq">
- <RegExpr attribute="Comment" context="#pop" String="[^`](?=`)" />
- <IncludeRules context="##Alerts" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="CoprocName">
+ <DetectChar attribute="Keyword" context="#pop#pop!Group" char="{" beginRegion="group"/>
+ <DetectSpaces attribute="Normal Text"/>
+ <DetectIdentifier attribute="Normal Text"/>
+ <IncludeRules context="FindWord"/>
</context>
-
- <!-- FindCommands matches many items that can be expected outside strings, substitutions etc. -->
- <context attribute="Normal Text" lineEndContext="#stay" name="FindCommands">
- <IncludeRules context="FindSpecialCommands" />
- <IncludeRules context="FindNormalCommands" />
+ <context attribute="Command" lineEndContext="#pop" name="Command" fallthroughContext="#pop">
+ <DetectSpaces attribute="Normal Text" context="#pop!CommandArgs"/>
+ <DetectChar context="CommandVariables" char="$" lookAhead="1"/>
+ <IncludeRules context="FindStrings"/>
+ <DetectChar context="#pop" char="`" lookAhead="1"/>
+ <RegExpr attribute="OtherCommand" context="CommandName" String="(?:[^&wordseps;&substseps;]++|\\.)+"/>
+ <LineContinue attribute="Escape" context="#stay"/>
+ <DetectChar context="CommandMaybeBraceExpansion" char="{" lookAhead="1"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="FindSpecialCommands">
+ <context attribute="Command" lineEndContext="#pop#pop" name="CommandVariables">
+ <IncludeRules context="DispatchVariables"/>
+ <DetectChar attribute="OtherCommand" context="#pop" char="$"/>
+ </context>
+ <context attribute="Command" lineEndContext="#pop#pop" name="DispatchVariables">
+ <IncludeRules context="DispatchSubstVariables"/>
+ <IncludeRules context="DispatchStringVariables"/>
+ <IncludeRules context="DispatchVarnameVariables"/>
+ </context>
+ <context attribute="Command" lineEndContext="#pop#pop" name="DispatchSubstVariables">
+ <Detect2Chars attribute="Parameter Expansion" context="#pop!VarBraceStart" char="$" char1="{"/>
+ <StringDetect context="#pop!ExprDblParenSubstOrSubstCommand" String="$((" lookAhead="1"/>
+ <Detect2Chars attribute="Parameter Expansion" context="#pop!SubstCommand" char="$" char1="(" beginRegion="subshell"/>
+ </context>
+ <context attribute="Command" lineEndContext="#pop#pop" name="DispatchStringVariables">
+ <Detect2Chars attribute="String SingleQ" context="#pop!StringEsc" char="$" char1="'"/>
+ <Detect2Chars attribute="String Transl." context="#pop!StringDQ" char="$" char1="&quot;"/>
+ </context>
+ <context attribute="Command" lineEndContext="#pop#pop" name="DispatchVarnameVariables">
+ <RegExpr attribute="Dollar Prefix" context="#pop!VarNamePrefixedWithDollar" String="\$(?=&varname;|[*@#?$!0-9-])"/>
+ </context>
+ <context attribute="Variable" lineEndContext="#pop" name="VarNamePrefixedWithDollar">
+ <DetectIdentifier attribute="Variable" context="#pop"/>
+ <AnyChar attribute="Variable" context="#pop" String="*@#?$!0123456789-"/>
+ </context>
+ <context attribute="OtherCommand" lineEndContext="#pop#pop" name="CommandName">
+ <DetectSpaces attribute="Normal Text" context="#pop#pop!CommandArgs"/>
+ <DetectIdentifier attribute="OtherCommand" context="#stay"/>
+ <DetectChar context="CommandVariables" char="$" lookAhead="1"/>
+ <IncludeRules context="FindStrings"/>
+ <Detect2Chars attribute="Control" context="#pop#pop" char="&amp;" char1="&amp;"/>
+ <Detect2Chars attribute="Control" context="#pop#pop" char="|" char1="|"/>
+ <DetectChar attribute="Control" context="#pop#pop" char="|"/>
+ <AnyChar context="#pop#pop" String=";)`" lookAhead="1"/>
+ <AnyChar context="#pop#pop!CommandArgs" String="&amp;&lt;>" lookAhead="1"/>
<!-- start expression in double parentheses -->
- <Detect2Chars attribute="Keyword" context="ExprDblParen" char="(" char1="(" beginRegion="expression" />
- <!-- start expression in double brackets -->
- <RegExpr attribute="Keyword" context="ExprDblBracket" String="\[\[&eos;" beginRegion="expression" column="0"/>
- <RegExpr attribute="Keyword" context="ExprDblBracket" String="\s\[\[&eos;" beginRegion="expression" />
- <!-- start expression in single brackets -->
- <RegExpr attribute="Builtin" context="ExprBracket" String="\[&eos;" beginRegion="expression" column="0"/>
- <RegExpr attribute="Builtin" context="ExprBracket" String="\s\[&eos;" beginRegion="expression" />
- <!-- start a group command with { -->
- <RegExpr attribute="Keyword" context="Group" String="\{&eos;" beginRegion="group" />
+ <Detect2Chars attribute="Error" context="#pop#pop!ExprDblParen" char="(" char1="(" beginRegion="expression"/>
<!-- start a subshell -->
- <DetectChar attribute="Keyword" context="SubShell" char="(" beginRegion="subshell" />
+ <DetectChar attribute="Error" context="#pop#pop!SubShell" char="(" beginRegion="subshell"/>
+ <LineContinue attribute="Escape" context="#stay"/>
+ <DetectChar context="#pop!CommandMaybeBraceExpansion" char="{" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="CommandMaybeBraceExpansion">
+ <IncludeRules context="DispatchBraceExpansion"/>
+ <DetectChar attribute="OtherCommand" context="#pop" char="{"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="CommandBackq" fallthroughContext="Command">
+ <DetectChar attribute="Backquote" context="#pop!CommandArgs" char="`"/>
+ <DetectChar attribute="Comment" context="CommentBackq" char="#"/>
+ <IncludeRules context="Start"/>
+ </context>
+ <!-- CommentBackq consumes shell comments till EOL or a backquote -->
+ <context attribute="Comment" lineEndContext="#pop" name="CommentBackq">
+ <DetectChar context="#pop" char="`" lookAhead="1"/>
+ <IncludeRules context="Comment"/>
+ </context>
+
+ <!-- CommandArgs matches the items after a command -->
+ <context attribute="Normal Text" lineEndContext="#pop" name="CommandArgs" fallthroughContext="CommandArg">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+
+ <!-- &> and &>> redirection -->
+ <StringDetect attribute="Redirection" context="WordRedirection" String="&amp;>>"/>
+ <Detect2Chars attribute="Redirection" context="FdRedirection" char="&amp;" char1=">"/>
+
+ <!-- handle &, |, ;, ` -->
+ <AnyChar context="#pop" String="&amp;|;`" lookAhead="1"/>
+
+ <!-- handle redirection -->
+ <AnyChar context="CommandArgMaybeRedirection" String="&lt;&gt;0123456789" lookAhead="1"/>
+
+ <DetectChar context="#pop" char=")" lookAhead="1"/>
+ <DetectChar attribute="Error" context="#pop!SubShell" char="("/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop#pop" name="CommandArg" fallthroughContext="#pop!NormalOption">
+ <!-- In command arguments, do not allow comments after escaped characters.
+ This avoids highlighting comments within paths or other text. Ex: pathtext\ #no\ comment -->
+ <DetectChar context="#pop#pop" char="#" lookAhead="1"/>
+ <Detect2Chars attribute="Option" context="#pop!LongOption" char="-" char1="-"/>
+ <DetectChar attribute="Option" context="#pop!ShortOption" char="-"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="CommandArgMaybeRedirection" fallthroughContext="#pop!NormalOption">
+ <IncludeRules context="FindRedirection"/>
+ </context>
+
+ <context attribute="Option" lineEndContext="#pop" name="ShortOption" fallthroughContext="#pop">
+ <DetectChar attribute="Path" context="PathThenPop" char="/"/>
+ <IncludeRules context="LongOption"/>
+ </context>
+ <context attribute="Option" lineEndContext="#pop" name="LongOption" fallthroughContext="#pop">
+ <AnyChar context="#pop" String="&wordseps;`" lookAhead="1"/>
+ <DetectChar attribute="Operator" context="#pop!NormalOption" char="="/>
+ <IncludeRules context="FindWord"/>
+ <IncludeRules context="FindGlobAndPop"/>
+ <DetectChar context="OptionMaybeBraceExpansion" char="{" lookAhead="1"/>
+ <DetectChar context="LongOptionMaybeGlobAny" char="[" lookAhead="1"/>
+ <RegExpr attribute="Option" context="#stay" String="&opt;"/>
+ </context>
+ <context attribute="Option" lineEndContext="#pop#pop" name="LongOptionMaybeGlobAny" fallthroughContext="#pop">
+ <IncludeRules context="FindGlobAny"/>
+ <DetectChar attribute="Option" context="#pop" char="["/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="NormalOption" fallthroughContext="#pop">
+ <AnyChar context="#pop" String="&wordseps;`" lookAhead="1"/>
+ <IncludeRules context="FindWord"/>
+ <DetectChar context="NormalMaybeBraceExpansion" char="{" lookAhead="1"/>
+ <IncludeRules context="FindNormalText"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FindNormalText">
+ <IncludeRules context="FindGlobAndPop"/>
+ <RegExpr attribute="Path" context="PathThenPop" String="&pathpart;"/>
+ <DetectChar context="FindPathThenPopMaybeGlobAny" char="[" lookAhead="1"/>
+ <RegExpr attribute="Normal Text" context="#stay" String="[^&wordseps;&substseps;\[]+"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="OptionMaybeBraceExpansion">
+ <IncludeRules context="DispatchBraceExpansion"/>
+ <DetectChar attribute="Option" context="#pop" char="{"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="NormalMaybeBraceExpansion">
+ <IncludeRules context="DispatchBraceExpansion"/>
+ <DetectChar attribute="Normal Text" context="#pop" char="{"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="AssumeEscape">
+ <LineContinue attribute="Escape" context="#pop"/>
+ <RegExpr attribute="Escape" context="#pop" String="\\."/>
+ </context>
+
+<!-- ====== The following rulessets are meant to be included ======== -->
+
+ <!-- FindRedirection consumes shell redirection -->
+ <context attribute="Normal Text" lineEndContext="#pop" name="FindRedirection">
+ <RegExpr attribute="File Descriptor" context="#pop!AssumeRedirection" String="[0-9]++(?=[&lt;>])"/>
+ <IncludeRules context="AssumeRedirection"/>
+ </context>
+
+ <!-- DispatchBraceExpansion consumes brace expansions -->
+ <context attribute="Normal Text" lineEndContext="#pop" name="DispatchBraceExpansion">
+ <!-- Only highlighting closed braces. The "BraceExpansion" context corrects
+ the closure of braces and allows recursive braces (bug #387915). -->
+ <RegExpr context="#pop!BraceExpansion" String="&braceexpansion;" lookAhead="1"/>
+ <RegExpr attribute="Escape" context="#pop!SequenceExpression" String="{(?=([-+]?[0-9]+\.\.[-+]?[0-9]+|[a-zA-Z]\.\.[a-zA-Z])(\.\.[-+]?[0-9]+)?\})"/>
+ </context>
+
+ <!-- FindPathThenPopMaybeGlobAny consumes path -->
+ <context attribute="Normal Text" lineEndContext="#pop#pop" name="FindPathThenPopMaybeGlobAny" fallthroughContext="#pop!PathThenPop">
+ <IncludeRules context="FindGlobAny"/>
+ <DetectChar attribute="Normal Text" context="#pop" char="["/>
+ </context>
+ <context attribute="Path" lineEndContext="#pop" name="FindGlobAndPop">
+ <Detect2Chars attribute="Glob" context="ExtGlobAndPop" char="?" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExtGlobAndPop" char="*" char1="("/>
+ <AnyChar attribute="Glob" context="PathThenPop" String="?*"/>
+ <Detect2Chars attribute="Glob" context="ExtGlobAndPop" char="+" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExtGlobAndPop" char="@" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExtGlobAndPop" char="!" char1="("/>
+ </context>
+ <context attribute="Path" lineEndContext="#stay" name="ExtGlobAndPop">
+ <DetectChar attribute="Glob" context="#pop!PathThenPop" char=")"/>
+ <IncludeRules context="FindWord"/>
+ <IncludeRules context="IncExtGlob"/>
+ </context>
+ <context attribute="Path" lineEndContext="#stay" name="FindExtGlob">
+ <Detect2Chars attribute="Glob" context="RecursiveExtGlob" char="?" char1="("/>
+ <Detect2Chars attribute="Glob" context="RecursiveExtGlob" char="*" char1="("/>
+ <Detect2Chars attribute="Glob" context="RecursiveExtGlob" char="+" char1="("/>
+ <Detect2Chars attribute="Glob" context="RecursiveExtGlob" char="@" char1="("/>
+ <Detect2Chars attribute="Glob" context="RecursiveExtGlob" char="!" char1="("/>
+ <AnyChar attribute="Glob" context="#stay" String="|?*"/>
+ </context>
+ <context attribute="Path" lineEndContext="#stay" name="RecursiveExtGlob">
+ <DetectChar attribute="Glob" context="#pop" char=")"/>
+ <IncludeRules context="FindWord"/>
+ <IncludeRules context="IncExtGlob"/>
+ <IncludeRules context="PathThenPopMaybeGlobAny"/>
+ </context>
+ <context attribute="Path" lineEndContext="#stay" name="IncExtGlob">
+ <DetectIdentifier attribute="Path"/>
+ <IncludeRules context="FindExtGlob"/>
+ <DetectChar context="PathMaybeBraceExpansion" char="{" lookAhead="1"/>
+ </context>
+ <context attribute="Path" lineEndContext="#pop#pop" name="PathThenPop">
+ <AnyChar context="#pop#pop" String="&wordseps;`" lookAhead="1"/>
+ <IncludeRules context="FindWord"/>
+ <IncludeRules context="FindExtGlob"/>
+ <DetectChar context="PathMaybeBraceExpansion" char="{" lookAhead="1"/>
+ <DetectChar context="PathThenPopMaybeGlobAny" char="[" lookAhead="1"/>
+ <RegExpr attribute="Path" context="#stay" String="&path;"/>
+ </context>
+ <context attribute="Path" lineEndContext="#pop#pop" name="PathThenPopMaybeGlobAny" fallthroughContext="#pop">
+ <IncludeRules context="FindGlobAny"/>
+ <DetectChar attribute="Path" context="#pop" char="["/>
+ </context>
+ <context attribute="Path" lineEndContext="#pop" name="PathMaybeBraceExpansion" fallthroughContext="#pop">
+ <IncludeRules context="DispatchBraceExpansion"/>
+ <DetectChar attribute="Path" context="#pop" char="{"/>
+ </context>
+
+ <context attribute="Glob" lineEndContext="#stay" name="FindGlobAny">
+ <RegExpr attribute="Glob" context="GlobAnyFlag" String="\[(?=&next_is_globany;)"/>
+ </context>
+ <context attribute="Pattern" lineEndContext="#pop" name="GlobAnyFlag" fallthroughContext="#pop!GlobAny">
+ <DetectChar attribute="Glob" context="#pop!GlobAny" char="^"/>
+ </context>
+ <context attribute="Pattern" lineEndContext="#pop" name="GlobAny">
+ <DetectIdentifier attribute="Pattern"/>
+ <DetectChar context="AssumeEscape" char="\" lookAhead="1"/>
+ <DetectChar attribute="Glob" context="#stay" char="-"/>
+ <IncludeRules context="FindStrings"/>
+ <DetectChar attribute="Glob" context="#pop" char="]"/>
+ <Detect2Chars attribute="Glob" context="GlobClass" char="[" char1=":"/>
+ </context>
+ <context attribute="Glob" lineEndContext="#pop#pop" name="GlobClass">
+ <DetectIdentifier attribute="Pattern"/>
+ <Detect2Chars attribute="Glob" context="#pop" char=":" char1="]"/>
+ <DetectChar attribute="Error" context="#pop" char="]"/>
+ </context>
+
+ <!-- FindPathThenPopInAlternateValue consumes path in ${xx:here}-->
+ <context attribute="Normal Text" lineEndContext="#pop" name="FindPathThenPopInAlternateValue">
+ <Detect2Chars context="#pop!PathThenPopInAlternateValue" char="?" char1="(" lookAhead="1"/>
+ <Detect2Chars context="#pop!PathThenPopInAlternateValue" char="*" char1="(" lookAhead="1"/>
+ <Detect2Chars context="#pop!PathThenPopInAlternateValue" char="+" char1="(" lookAhead="1"/>
+ <Detect2Chars context="#pop!PathThenPopInAlternateValue" char="@" char1="(" lookAhead="1"/>
+ <Detect2Chars context="#pop!PathThenPopInAlternateValue" char="!" char1="(" lookAhead="1"/>
+ <AnyChar attribute="Glob" context="#pop!PathThenPopInAlternateValue" String="?*"/>
+ <RegExpr attribute="Path" context="#pop!PathThenPopInAlternateValue" String="&pathpart_alt;"/>
+ </context>
+ <context attribute="Path" lineEndContext="#pop#pop" name="PathThenPopInAlternateValue">
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ <IncludeRules context="FindWord"/>
+ <Detect2Chars attribute="Glob" context="ExtGlobAndPopInAlternateValue" char="?" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExtGlobAndPopInAlternateValue" char="*" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExtGlobAndPopInAlternateValue" char="+" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExtGlobAndPopInAlternateValue" char="@" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExtGlobAndPopInAlternateValue" char="!" char1="("/>
+ <AnyChar attribute="Glob" context="#stay" String="?*"/>
+ <RegExpr attribute="Path" context="#stay" String="&path_alt;"/>
+ </context>
+ <context attribute="Path" lineEndContext="#stay" name="ExtGlobAndPopInAlternateValue">
+ <DetectChar attribute="Glob" context="#pop" char=")"/>
+ <DetectChar attribute="Error" context="#pop#pop" char="}"/>
+ <IncludeRules context="FindWord"/>
+ <IncludeRules context="FindExtGlobInAlternateValue"/>
+ </context>
+ <context attribute="Path" lineEndContext="#stay" name="FindExtGlobInAlternateValue">
+ <DetectIdentifier attribute="Path"/>
+ <Detect2Chars attribute="Glob" context="RecursiveExtGlobInAlternateValue" char="?" char1="("/>
+ <Detect2Chars attribute="Glob" context="RecursiveExtGlobInAlternateValue" char="*" char1="("/>
+ <Detect2Chars attribute="Glob" context="RecursiveExtGlobInAlternateValue" char="+" char1="("/>
+ <Detect2Chars attribute="Glob" context="RecursiveExtGlobInAlternateValue" char="@" char1="("/>
+ <Detect2Chars attribute="Glob" context="RecursiveExtGlobInAlternateValue" char="!" char1="("/>
+ <AnyChar attribute="Glob" context="#stay" String="|?*"/>
+ </context>
+ <context attribute="Path" lineEndContext="#stay" name="RecursiveExtGlobInAlternateValue">
+ <DetectChar attribute="Glob" context="#pop" char=")"/>
+ <DetectChar context="#pop" char="}" lookAhead="1"/>
+ <IncludeRules context="FindWord"/>
+ <IncludeRules context="FindExtGlobInAlternateValue"/>
+ </context>
+
+ <context attribute="Pattern" lineEndContext="#stay" name="FindPattern">
+ <Detect2Chars attribute="Glob" context="ExtPattern" char="?" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExtPattern" char="*" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExtPattern" char="+" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExtPattern" char="@" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExtPattern" char="!" char1="("/>
+ <AnyChar attribute="Glob" context="#stay" String="?*"/>
+ </context>
+ <context attribute="Pattern" lineEndContext="#stay" name="ExtPattern">
+ <DetectIdentifier attribute="Pattern"/>
+ <DetectChar attribute="Glob" context="#stay" char="|"/>
+ <IncludeRules context="FindWord"/>
+ <DetectChar attribute="Glob" context="#pop" char=")"/>
+ <IncludeRules context="FindPattern"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="VarAssign" fallthroughContext="#pop">
+ <DetectChar attribute="Parameter Expansion Operator" context="Subscript" char="["/>
+ <DetectChar attribute="Operator" context="#pop!Assign" char="="/>
+ <Detect2Chars attribute="Operator" context="#pop!Assign" char="+" char1="="/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="DispatchKeyword">
<!-- match do and if blocks -->
- <RegExpr attribute="Keyword" context="#stay" String="\bdo&noword;" beginRegion="do" />
- <RegExpr attribute="Keyword" context="#stay" String="\bdone&noword;" endRegion="do" />
- <RegExpr attribute="Keyword" context="#stay" String="\bif&eos;" beginRegion="if" />
- <RegExpr attribute="Keyword" context="#stay" String="\bfi&noword;" endRegion="if" />
+ <Detect2Chars attribute="Control Flow" context="#pop!NotCond" char="i" char1="f" beginRegion="if"/>
+ <Detect2Chars attribute="Control Flow" context="#pop" char="f" char1="i" endRegion="if"/>
+ <StringDetect attribute="Control Flow" context="#pop" String="done" endRegion="do"/>
+ <Detect2Chars attribute="Control Flow" context="#pop" char="d" char1="o" beginRegion="do"/>
+ <!-- handle while/until as a special case -->
+ <StringDetect attribute="Control Flow" context="#pop!NotCond" String="while"/>
+ <StringDetect attribute="Control Flow" context="#pop!NotCond" String="until"/>
+ <!-- handle for as a special case -->
+ <StringDetect attribute="Control Flow" context="#pop!For" String="for"/>
+ <!-- handle select as a special case -->
+ <StringDetect attribute="Control Flow" context="#pop!For" String="select"/>
<!-- handle case as a special case -->
- <RegExpr attribute="Keyword" context="Case" String="\bcase&noword;" beginRegion="case" />
- <!-- handle variable assignments -->
- <RegExpr attribute="Variable" context="Assign" String="\b&varname;\+?=" />
- <RegExpr attribute="Variable" context="AssignSubscr" String="\b&varname;(?=\[.+\]\+?=)" />
+ <StringDetect attribute="Control Flow" context="#pop!Case" String="case" beginRegion="case"/>
<!-- handle functions with function keyword before keywords -->
- <StringDetect attribute="Function" context="#stay" String=":()" />
- <WordDetect attribute="Keyword" context="FunctionDef" String="function" />
- <!-- mark function definitions without function keyword -->
- <RegExpr attribute="Function" context="#stay" String="&funcname;\s*\(\)" />
+ <StringDetect attribute="Keyword" context="#pop!FunctionDef" String="function"/>
+ <StringDetect attribute="Control Flow" context="#pop!Return" String="return"/>
+ <!-- not a keyword in this context -->
+ <Detect2Chars attribute="Error" context="#pop" char="i" char1="n"/>
+ <StringDetect attribute="Error" context="#pop" String="esac"/>
<!-- handle keywords -->
- <keyword attribute="Keyword" context="#stay" String="keywords" />
- <RegExpr attribute="Builtin" context="#stay" String="\.(?=\s)" />
- <!-- handle commands that have variable names as argument -->
- <keyword attribute="Builtin" context="VarName" String="builtins_var" />
- <!-- handle here-string -->
- <RegExpr attribute="Redirection" context="#stay" String="\d*&lt;&lt;&lt;" />
- <!-- handle here document -->
- <Detect2Chars attribute="Redirection" context="HereDoc" char="&lt;" char1="&lt;" lookAhead="true" />
- <!-- handle process subst -->
- <RegExpr attribute="Redirection" context="ProcessSubst" String="[&lt;&gt;]\(" />
- <!-- handle redirection -->
- <RegExpr attribute="Redirection" context="#stay" String="([0-9]*(&gt;{1,2}|&lt;)(&amp;[0-9]+-?)?|&amp;&gt;|&gt;&amp;|[0-9]*&lt;&gt;)" />
- <!-- handle &, &&, | and || -->
- <RegExpr attribute="Control" context="#stay" String="([|&amp;])\1?" />
+ <DetectIdentifier attribute="Control Flow" context="#pop"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="FindNormalCommands">
- <keyword attribute="Builtin" context="CommandArgs" String="builtins" />
- <keyword attribute="Command" context="CommandArgs" String="unixcommands" />
- <RegExpr attribute="OtherCommand" context="#stay" String="&pathpart;*(?=/)" />
- <RegExpr attribute="OtherCommand" context="#stay" String="~\w*" />
- <RegExpr attribute="OtherCommand" context="#stay" String="/&pathpart;*(?=([/);$`'&quot;]|$))" />
- <RegExpr attribute="OtherCommand" context="CommandArgs" String="/&pathpart;*(?=([\s);$`'&quot;]|$))" />
- <!-- This list is not complete. ie, ":" is missing but as it is in bash completition. -->
- <RegExpr attribute="OtherCommand" context="CommandArgs" String="&pathpart;*" />
+
+ <!-- if ! ..., while ! ... and until ! ... -->
+ <context attribute="Normal Text" lineEndContext="#pop" name="NotCond" fallthroughContext="#pop">
+ <DetectSpaces attribute="Normal Text" context="#pop!NotCond2"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="NotCond2" fallthroughContext="#pop">
+ <Detect2Chars attribute="Expression" context="#pop" char="!" char1="&tab;"/>
+ <Detect2Chars attribute="Expression" context="#pop" char="!" char1=" "/>
+ <LineContinue attribute="Expression" context="#pop" char="!"/>
</context>
- <!-- CommandArgs matches the items after a command -->
- <context attribute="Normal Text" lineEndContext="#pop" name="CommandArgs">
- <LineContinue />
- <IncludeRules context="FindMost" />
- <RegExpr attribute="Keyword" context="#stay" String="\\$" />
- <!-- handle keywords -->
- <RegExpr attribute="Option" context="#stay" String="\.(?=\s)" />
- <!-- handle here-string -->
- <RegExpr attribute="Redirection" context="#stay" String="\d*&lt;&lt;&lt;" />
+ <context attribute="Normal Text" lineEndContext="#pop" name="For" fallthroughContext="#pop">
+ <LineContinue attribute="Escape" context="#stay"/>
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <DetectIdentifier attribute="Normal Text" context="#pop!ForIn"/>
+ <Detect2Chars attribute="Keyword" context="#pop!ForArithmeticExpr" char="(" char1="("/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ForIn" fallthroughContext="#pop">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <DetectChar attribute="Control" context="#pop" char=";"/>
+ <WordDetect attribute="Keyword" context="#pop!CommandArgs" String="in"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ForArithmeticExpr">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <DetectChar attribute="Control" context="#stay" char=";"/>
+ <Detect2Chars attribute="Keyword" context="#pop" char=")" char1=")"/>
+ <IncludeRules context="FindExprDblParen"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="AssumeRedirection">
+ <!-- handle output redirection -->
+ <Detect2Chars attribute="Redirection" context="#pop!WordRedirection" char=">" char1=">"/>
+ <Detect2Chars attribute="Redirection" context="#pop!WordRedirection" char=">" char1="|"/>
+ <Detect2Chars attribute="Redirection" context="#pop!FdRedirection" char=">" char1="&amp;"/>
+ <Detect2Chars attribute="Redirection" context="#pop!ProcessSubst" char=">" char1="("/>
+ <DetectChar attribute="Redirection" context="#pop!WordRedirection" char=">"/>
+ <StringDetect attribute="Redirection" context="#pop!StringRedirection" String="&lt;&lt;&lt;"/>
<!-- handle here document -->
- <Detect2Chars attribute="Redirection" context="HereDoc" char="&lt;" char1="&lt;" lookAhead="true" />
- <!-- handle process subst -->
- <RegExpr attribute="Redirection" context="ProcessSubst" String="[&lt;&gt;]\(" />
- <!-- handle redirection -->
- <RegExpr attribute="Redirection" context="#stay" String="([0-9]*(&gt;{1,2}|&lt;)(&amp;[0-9]+-?)?|&amp;&gt;|&gt;&amp;|[0-9]*&lt;&gt;)" />
- <!-- handle &, &&, | and || -->
- <RegExpr attribute="Control" context="#pop" String="([|&amp;;])\1?" />
- <RegExpr attribute="Normal Text" context="#stay" String="[a-zA-Z_]+-[A-Za-z0-9_-]*" />
- <RegExpr attribute="Option" context="#stay" String="-?-[a-zA-Z_][A-Za-z0-9_-]*" />
- <keyword attribute="Option" context="#stay" String="keywords" />
- <AnyChar String=")}" context="#pop" lookAhead="true"/>
- </context>
-
- <!-- FindCommands matches many items that can be expected outside strings, substitutions etc, when inside a Backquote -->
- <context attribute="Normal Text" lineEndContext="#stay" name="FindCommandsBackq">
- <IncludeRules context="FindSpecialCommands" />
- <IncludeRules context="FindNormalCommandsBackq" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="FindNormalCommandsBackq">
- <keyword attribute="Builtin" context="CommandArgsBackq" String="builtins" />
- <keyword attribute="Command" context="CommandArgsBackq" String="unixcommands" />
- <RegExpr attribute="OtherCommand" context="#stay" String="&pathpart;*(?=/)" />
- <RegExpr attribute="OtherCommand" context="#stay" String="~\w*" />
- <RegExpr attribute="OtherCommand" context="#stay" String="/&pathpart;*(?=([/);$`'&quot;]|$))" />
- <RegExpr attribute="OtherCommand" context="CommandArgsBackq" String="/&pathpart;*(?=([\s);$`'&quot;]|$))" />
- <RegExpr attribute="OtherCommand" context="CommandArgsBackq" String="&pathpart;*" />
- </context>
- <context attribute="Normal Text" lineEndContext="#pop" name="CommandArgsBackq">
- <LineContinue />
- <DetectChar attribute="Keyword" context="#pop" char="`" lookAhead="true"/>
- <IncludeRules context="CommandArgs" />
- </context>
-
- <!-- FindOthers contains various rules to mark different shell input -->
- <context attribute="Normal Text" lineEndContext="#stay" name="FindOthers">
- <IncludeRules context="Escapes" />
- <RegExpr attribute="Keyword" context="#stay" String="\\$" />
- <!-- Only highlighting closed braces. The "BraceExpansion" context corrects
- the closure of braces and allows recursive braces (bug #387915). -->
- <RegExpr attribute="Escape" context="BraceExpansion" String="\{(?=(\\[ \{\}]|[^\s\{\}]|\{(\\[ ]|\S)*\})+\})" />
- <RegExpr attribute="Path" context="#stay" String="&pathpart;*(?=/)" />
- <RegExpr attribute="Path" context="#stay" String="~\w*" />
- <RegExpr attribute="Path" context="#stay" String="/&pathpart;*(?=([\s/):;$`'&quot;]|$))" />
- <!-- TODO: shell globs beside * and ? (in Path's) -->
+ <Detect2Chars context="#pop!HereDoc" char="&lt;" char1="&lt;" lookAhead="1"/>
+ <!-- handle input redirection -->
+ <Detect2Chars attribute="Redirection" context="#pop!FdRedirection" char="&lt;" char1="&amp;"/>
+ <Detect2Chars attribute="Redirection" context="#pop!WordRedirection" char="&lt;" char1=">"/>
+ <Detect2Chars attribute="Redirection" context="#pop!ProcessSubst" char="&lt;" char1="("/>
+ <DetectChar attribute="Redirection" context="#pop!WordRedirection" char="&lt;"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="Escapes">
- <RegExpr attribute="Escape" context="#stay" String="\\[][;\\$`{}()|&amp;&lt;&gt;* ]" />
+ <context attribute="Normal Text" lineEndContext="#pop" name="FdRedirection" fallthroughContext="#pop!FdRedirection2">
+ <DetectSpaces attribute="Normal Text"/>
+ <DetectChar attribute="Comment" context="#pop!Comment" char="#"/>
</context>
- <context attribute="Escape" lineEndContext="#pop" name="BraceExpansion">
- <DetectSpaces context="#pop" lookAhead="true" />
- <DetectChar attribute="Escape" context="#pop" char="}" />
- <DetectChar attribute="Escape" context="BraceExpansion" char="{" />
- <RegExpr attribute="Error" context="#stay" String="[^\s\{\}\\](?=\s|$)" />
- <IncludeRules context="Escapes" />
- <IncludeRules context="FindSubstitutions" />
- </context>
-
- <!-- FindStrings looks for single and double quoted strings, also with $-prefix -->
- <context attribute="Normal Text" lineEndContext="#stay" name="FindStrings">
- <Detect2Chars attribute="Escape" context="#stay" char="\" char1="'" />
- <Detect2Chars attribute="Escape" context="#stay" char="\" char1="&quot;" />
- <DetectChar attribute="String SingleQ" context="StringSQ" char="'" />
- <DetectChar attribute="String DoubleQ" context="StringDQ" char="&quot;" />
- <Detect2Chars attribute="String SingleQ" context="StringEsc" char="$" char1="'" />
- <Detect2Chars attribute="String Transl." context="StringDQ" char="$" char1="&quot;" />
- </context>
-
- <!-- FindSubstitutions goes after anything starting with $ and ` and their escapes -->
- <context attribute="Normal Text" lineEndContext="#stay" name="FindSubstitutions">
- <RegExpr attribute="Variable" context="Subscript" String="\$&varname;\[" />
- <RegExpr attribute="Variable" context="#stay" String="\$&varname;" />
- <RegExpr attribute="Variable" context="#stay" String="\$[*@#?$!_0-9-]" />
- <RegExpr attribute="Variable" context="#stay" String="\$\{[*@#?$!_0-9-]\}" />
- <RegExpr attribute="Variable" context="#stay" String="\$\{#&varname;(\[[*@]\])?\}" />
- <RegExpr attribute="Variable" context="#stay" String="\$\{!&varname;(\[[*@]\]|[*@])?\}" />
- <RegExpr attribute="Variable" context="#stay" String="\$\{#[0-9]+\}" />
- <RegExpr attribute="Variable" context="VarBrace" String="\$\{&varname;" />
- <RegExpr attribute="Variable" context="VarBrace" String="\$\{[*@#?$!_0-9-](?=[:#%/=?+-])" />
- <StringDetect attribute="Variable" context="ExprDblParenSubst" String="$((" beginRegion="expression" />
- <StringDetect attribute="Redirection" context="SubstFile" String="$(&lt;" />
- <Detect2Chars attribute="Variable" context="SubstCommand" char="$" char1="(" />
- <DetectChar attribute="Backquote" context="SubstBackq" char="`" />
- <RegExpr attribute="Escape" context="#stay" String="\\[`$\\]" />
- </context>
-
- <!-- FindTests finds operators valid in tests -->
- <context attribute="Normal Text" lineEndContext="#stay" name="FindTests">
- <RegExpr attribute="Expression" context="#stay" String="-[abcdefghkprstuwxOGLSNozn](?=\s)"/>
- <RegExpr attribute="Expression" context="#stay" String="-([no]t|ef)(?=\s)"/>
- <RegExpr attribute="Expression" context="#stay" String="([!=]=?|[&gt;&lt;])(?=\s)"/>
- <RegExpr attribute="Expression" context="#stay" String="-(eq|ne|[gl][te])(?=\s)"/>
+ <context attribute="Normal Text" lineEndContext="#pop" name="FdRedirection2" fallthroughContext="#pop!WordRedirection2">
+ <RegExpr attribute="File Descriptor" context="#pop!CloseFile" String="[0-9]+(?=-?&eoexpr;)"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="WordRedirection" fallthroughContext="#pop!WordRedirection2">
+ <DetectSpaces attribute="Normal Text"/>
+ <DetectChar attribute="Comment" context="#pop!Comment" char="#"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="WordRedirection2" fallthroughContext="#pop">
+ <AnyChar context="#pop" String="&wordseps;`" lookAhead="1"/>
+ <IncludeRules context="FindWord"/>
+ <RegExpr attribute="Path" context="PathThenPop" String="&path;"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="StringRedirection" fallthroughContext="#pop!StringRedirection2">
+ <DetectSpaces attribute="Normal Text"/>
+ <DetectChar attribute="Comment" context="#pop!Comment" char="#"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="StringRedirection2">
+ <AnyChar context="#pop" String="&wordseps;`" lookAhead="1"/>
+ <IncludeRules context="FindWord"/>
+ <DetectIdentifier attribute="Normal Text"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="CloseFile" fallthroughContext="#pop">
+ <DetectChar attribute="Keyword" context="#pop" char="-"/>
</context>
+ <!-- HereDoc consumes Here-documents. It is called at the beginning of the "<<" construct. -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="HereDoc">
+ <RegExpr attribute="Redirection" context="HereDocMLComment" String="^[ &tab;]*&lt;&lt;[ &tab;]*'#([^']+)'$" column="0"/>
+ <RegExpr attribute="Redirection" context="HereDocIQ" String="&lt;&lt;-[ &tab;]*&heredocq;(?=[ &tab;]*$)"/>
+ <RegExpr attribute="Redirection" context="HereDocINQ" String="&lt;&lt;-[ &tab;]*([^&wordseps;]+)(?=[ &tab;]*$)"/>
+ <RegExpr attribute="Redirection" context="HereDocQ" String="&lt;&lt;[ &tab;]*&heredocq;(?=[ &tab;]*$)"/>
+ <RegExpr attribute="Redirection" context="HereDocNQ" String="&lt;&lt;[ &tab;]*([^&wordseps;]+)(?=[ &tab;]*$)"/>
-<!-- ====== These are the contexts that can be branched to ======= -->
+ <RegExpr context="HereDocIQCmd" String="(&lt;&lt;-[ &tab;]*&heredocq;)" lookAhead="1"/>
+ <RegExpr context="HereDocINQCmd" String="(&lt;&lt;-[ &tab;]*([^&wordseps;]+))" lookAhead="1"/>
+ <RegExpr context="HereDocQCmd" String="(&lt;&lt;[ &tab;]*&heredocq;)" lookAhead="1"/>
+ <RegExpr context="HereDocNQCmd" String="(&lt;&lt;[ &tab;]*([^&wordseps;]+))" lookAhead="1"/>
- <!-- ExprDblParen consumes an expression started in command mode till )) -->
- <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblParen">
- <Detect2Chars attribute="Keyword" context="#pop" char=")" char1=")" endRegion="expression" />
- <DetectChar attribute="Normal Text" context="ExprSubParen" char="(" />
- <IncludeRules context="FindMost" />
+ <Detect2Chars attribute="Redirection" context="#pop" char="&lt;" char1="&lt;"/><!-- always met -->
</context>
- <!-- ExprDblParenSubst like ExprDblParen but matches )) as Variable -->
- <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblParenSubst">
- <Detect2Chars attribute="Variable" context="#pop" char=")" char1=")" endRegion="expression" />
- <DetectChar attribute="Normal Text" context="ExprSubParen" char="(" />
- <IncludeRules context="FindMost" />
+ <context attribute="Normal Text" lineEndContext="#pop" name="HereDocRemainder" fallthroughContext="CommandArg">
+ <AnyChar context="BashOneLine" String="&amp;|;`" lookAhead="1"/>
+ <IncludeRules context="CommandArgs"/>
</context>
- <!-- ExprSubParen consumes an expression till ) -->
- <context attribute="Normal Text" lineEndContext="#stay" name="ExprSubParen">
- <DetectChar attribute="Normal Text" context="#pop" char=")" />
- <DetectChar attribute="Normal Text" context="ExprSubParen" char="(" />
- <IncludeRules context="FindMost" />
+ <!-- Highlight the builtin `:` (true) and the followed redirection, then fall into `HereDocMLComment` -->
+ <context attribute="Comment" lineEndContext="#stay" name="PreHereDocMLComment">
+ <DetectChar attribute="Builtin" context="#stay" char=":" column="0"/>
+ <RegExpr attribute="Redirection" context="HereDocMLComment" String="&lt;&lt;[ &tab;]*'#([^']+)'$"/>
</context>
- <!-- ExprBracket consumes an expression till ] -->
- <context attribute="Normal Text" lineEndContext="#stay" name="ExprBracket">
- <RegExpr attribute="Builtin" context="#pop" String="\s\](?=($|[\s;|&amp;]))" endRegion="expression" />
- <RegExpr attribute="Builtin" context="#pop" String="\](?=($|[\s;|&amp;]))" endRegion="expression" column="0"/>
- <DetectChar attribute="Normal Text" context="ExprSubParen" char="(" />
- <IncludeRules context="FindTests" />
- <IncludeRules context="FindMost" />
+ <context attribute="Comment" lineEndContext="#stay" name="HereDocMLComment" dynamic="true" fallthroughContext="Comment">
+ <RegExpr attribute="Redirection" context="#pop#pop" String="^#%1$" column="0" dynamic="true"/>
+ <RegExpr attribute="Region Marker" context="RST Documentation" String="^#?\[(=*)\[\.rst:" column="0" beginRegion="RSTDocumentation" />
+ <IncludeRules context="Comment"/>
</context>
- <!-- ExprDblBracket consumes an expression till ]] -->
- <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblBracket">
- <RegExpr attribute="Keyword" context="#pop" String="\s\]\](?=($|[\s;|&amp;]))" endRegion="expression" />
- <RegExpr attribute="Keyword" context="#pop" String="\]\](?=($|[\s;|&amp;]))" endRegion="expression" column="0"/>
- <DetectChar attribute="Normal Text" context="ExprSubParen" char="(" />
- <IncludeRules context="FindTests" />
- <IncludeRules context="FindMost" />
+ <context attribute="Comment" lineEndContext="#stay" name="RST Documentation" dynamic="true">
+ <RegExpr attribute="Redirection" context="#pop#pop#pop" String="^#BLOCK-COMMENT$" column="0"/>
+ <RegExpr attribute="Region Marker" context="#pop" String="^#?\]%1\]" dynamic="true" column="0" endRegion="RSTDocumentation" />
+ <IncludeRules context="##reStructuredText" />
</context>
- <!-- Group consumes shell input till } -->
- <context attribute="Normal Text" lineEndContext="#stay" name="Group">
- <DetectChar attribute="Keyword" context="#pop" char="}" endRegion="group" />
- <IncludeRules context="FindAll" />
+ <context attribute="Here Doc" lineEndContext="#stay" name="HereDocQ" dynamic="true" fallthroughContext="HereDocText">
+ <RegExpr attribute="Redirection" context="#pop#pop" String="^%1$" dynamic="true" column="0"/>
</context>
- <!-- SubShell consumes shell input till ) -->
- <context attribute="Normal Text" lineEndContext="#stay" name="SubShell">
- <DetectChar attribute="Keyword" context="#pop" char=")" endRegion="subshell" />
- <IncludeRules context="FindAll" />
+ <context attribute="Here Doc" lineEndContext="#stay" name="HereDocNQ" dynamic="true" fallthroughContext="HereDocSubstitutions">
+ <IncludeRules context="HereDocQ" />
</context>
- <!-- Assign consumes an expression till EOL or whitespace -->
- <context attribute="Normal Text" lineEndContext="#pop" name="Assign" fallthrough="true" fallthroughContext="#pop">
- <DetectChar attribute="Variable" context="AssignArray" char="(" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
- <IncludeRules context="FindOthers" />
- <RegExpr attribute="Normal Text" context="#stay" String="[\w:,+_./-]" />
+ <context attribute="Here Doc" lineEndContext="#stay" name="HereDocIQ" dynamic="true" fallthroughContext="HereDocText">
+ <RegExpr attribute="Redirection" context="#pop#pop" String="^\t*%1$" dynamic="true" column="0"/>
</context>
- <!-- AssignArray consumes everything till ), marking assignments -->
- <context attribute="Normal Text" lineEndContext="#pop" name="AssignArray">
- <DetectChar attribute="Variable" context="#pop" char=")" />
- <DetectChar attribute="Variable" context="Subscript" char="[" />
- <DetectChar attribute="Variable" context="Assign" char="=" />
- <IncludeRules context="FindMost" />
+ <context attribute="Here Doc" lineEndContext="#stay" name="HereDocINQ" dynamic="true" fallthroughContext="HereDocSubstitutions">
+ <IncludeRules context="HereDocIQ" />
</context>
- <!-- AssignSubscr first expects a [ then parses subscript and continues with '=value' -->
- <context attribute="Normal Text" lineEndContext="#pop" name="AssignSubscr" fallthrough="true" fallthroughContext="#pop">
- <DetectChar attribute="Variable" context="Subscript" char="[" />
- <Detect2Chars attribute="Variable" context="Assign" char="+" char1="=" />
- <DetectChar attribute="Variable" context="Assign" char="=" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
- <IncludeRules context="FindOthers" />
+ <context attribute="Here Doc" lineEndContext="#stay" name="HereDocCmd">
+ <!-- Only if the redirect is before the command, but as this is too complicated,
+ check if the redirect is at the beginning of the line. -->
+ <StringDetect attribute="Redirection" context="BashOneLine" String="%1" dynamic="true" firstNonSpace="1"/>
+ <StringDetect attribute="Redirection" context="HereDocRemainder" String="%1" dynamic="true"/>
</context>
- <!-- Subscript consumes anything till ], marks as Variable -->
- <context attribute="Variable" lineEndContext="#stay" name="Subscript">
- <DetectChar attribute="Variable" context="#pop" char="]" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
- <IncludeRules context="FindOthers" />
+ <context attribute="Here Doc" lineEndContext="#stay" name="HereDocQCmd" dynamic="true" fallthroughContext="HereDocText">
+ <IncludeRules context="HereDocCmd"/>
+ <RegExpr attribute="Redirection" context="#pop#pop" String="^%2$" dynamic="true" column="0"/>
</context>
- <!-- FunctionDef consumes a name, possibly with (), marks as Function -->
- <context attribute="Function" lineEndContext="#pop" name="FunctionDef" fallthrough="true" fallthroughContext="#pop">
- <RegExpr attribute="Function" context="#pop" String="\s+&funcname;(\s*\(\))?" />
+ <context attribute="Here Doc" lineEndContext="#stay" name="HereDocNQCmd" dynamic="true" fallthroughContext="HereDocSubstitutions">
+ <IncludeRules context="HereDocQCmd"/>
+ </context>
+
+ <context attribute="Here Doc" lineEndContext="#stay" name="HereDocIQCmd" dynamic="true" fallthroughContext="HereDocText">
+ <IncludeRules context="HereDocCmd"/>
+ <RegExpr attribute="Redirection" context="#pop#pop" String="^\t*%2$" dynamic="true" column="0"/>
+ </context>
+
+ <context attribute="Here Doc" lineEndContext="#stay" name="HereDocINQCmd" dynamic="true" fallthroughContext="HereDocSubstitutions">
+ <IncludeRules context="HereDocIQCmd"/>
+ </context>
+
+ <context attribute="Here Doc" lineEndContext="#pop" name="HereDocText">
+ </context>
+
+ <context attribute="Here Doc" lineEndContext="#pop" name="HereDocSubstitutions">
+ <DetectSpaces attribute="Here Doc"/>
+ <DetectIdentifier attribute="Here Doc"/>
+ <DetectChar context="HereDocVariables" char="$" lookAhead="1"/>
+ <DetectChar attribute="Backquote" context="CommandBackq" char="`"/>
+ <DetectChar context="AssumeEscape" char="\" lookAhead="1"/>
+ </context>
+ <context attribute="Here Doc" lineEndContext="#pop" name="HereDocVariables">
+ <IncludeRules context="DispatchSubstVariables"/>
+ <IncludeRules context="DispatchVarnameVariables"/>
+ <DetectChar attribute="Here Doc" context="#pop" char="$"/>
</context>
<!-- VarName consumes spare variable names and assignments -->
- <context attribute="Normal Text" lineEndContext="#pop" name="VarName" fallthrough="true" fallthroughContext="#pop">
- <!-- handle command line options -->
- <RegExpr attribute="Option" context="#stay" String="-[A-Za-z0-9]+" />
- <RegExpr attribute="Option" context="#stay" String="--[a-z][A-Za-z0-9_-]*" />
- <RegExpr attribute="Variable" context="#stay" String="\b&varname;" />
- <DetectChar attribute="Variable" context="Subscript" char="[" />
- <DetectChar attribute="Variable" context="Assign" char="=" />
- <IncludeRules context="FindMost" />
- <!-- stay here in spaces and other safe characters -->
- <RegExpr attribute="Normal Text" context="#stay" String="[^]})|;`&amp;&gt;&lt;]" />
+ <context attribute="Normal Text" lineEndContext="#pop" name="VarName">
+ <StringDetect attribute="Builtin" context="#pop!BuiltinGetopts" String="getopts"/>
+ <StringDetect attribute="Builtin" context="#pop!BuiltinLet" String="let"/>
+ <DetectIdentifier attribute="Builtin" context="#pop!VarNameArgs"/>
+ <AnyChar attribute="Builtin" context="#pop!VarNameArgs" String=".:"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="VarNameArgs" fallthroughContext="#pop!CommandArgs">
+ <DetectSpaces attribute="Normal Text" context="VarNameArg"/>
+ <LineContinue attribute="Escape" context="#stay"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop#pop" name="VarNameArg" fallthroughContext="#pop!VarNameArg2">
+ <!-- In command arguments, do not allow comments after escaped characters.
+ This avoids highlighting comments within paths or other text. Ex: pathtext\ #no\ comment -->
+ <DetectChar context="#pop#pop" char="#" lookAhead="1"/>
+ <DetectChar attribute="Option" context="#pop!ShortOption" char="-"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="VarNameArg2" fallthroughContext="#pop!NormalOption">
+ <DetectChar attribute="Variable" context="Subscript" char="["/>
+ <DetectChar attribute="Operator" context="Assign" char="="/>
+ <DetectChar attribute="Variable" context="AssignArray" char="("/>
+ <DetectIdentifier attribute="Variable" context="#stay"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="BuiltinGetopts" fallthroughContext="#pop!CommandArgs">
+ <DetectSpaces attribute="Normal Text" context="#pop!BuiltinGetoptsOpt"/>
+ <LineContinue attribute="Escape" context="#stay"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop#pop" name="BuiltinGetoptsOpt" fallthroughContext="#pop!BuiltinGetoptsOpt2">
+ <DetectChar context="#pop#pop" char="#" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="BuiltinGetoptsOpt2" fallthroughContext="#pop!NormalOption">
+ <DetectChar attribute="Operator" context="#stay" char=":"/>
+ <DetectIdentifier attribute="Normal Text" context="#stay" />
+ <DetectSpaces attribute="Normal Text" context="#pop!BuiltinGetoptsVar"/>
+ <AnyChar context="#pop" String="&wordseps;`" lookAhead="1"/>
+ <IncludeRules context="FindWord"/>
+ <DetectChar context="NormalMaybeBraceExpansion" char="{" lookAhead="1"/>
+ <AnyChar attribute="Normal Text" context="#stay" String="/%.0123456789"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="BuiltinGetoptsVar" fallthroughContext="#pop!CommandArgs">
+ <DetectIdentifier attribute="Variable" context="#pop!CommandArgs"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="BuiltinLet" fallthroughContext="#pop!CommandArgs">
+ <DetectSpaces attribute="Normal Text" context="#pop!BuiltinLetArgs"/>
+ <LineContinue attribute="Escape" context="#stay"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="BuiltinLetArgs" fallthroughContext="BuiltinLetArg">
+ <AnyChar context="BuiltinLetArgsNumber" String="0123456789" lookAhead="1"/>
+ <IncludeRules context="CommandArgs"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="BuiltinLetArgsNumber" fallthroughContext="#pop!BuiltinLetArg">
+ <IncludeRules context="FindRedirection"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop#pop" name="BuiltinLetArg" fallthroughContext="#pop!BuiltinLetExpr">
+ <DetectChar context="#pop#pop" char="#" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="BuiltinLetExpr" fallthroughContext="#pop!NormalOption">
+ <DetectIdentifier attribute="Variable" context="#stay" />
+ <AnyChar context="#pop" String="&wordseps;`" lookAhead="1"/>
+ <AnyChar attribute="Operator" context="#stay" String="+-/%=^:"/>
+ <AnyChar context="Number" String="0123456789" lookAhead="1"/>
+ <DetectChar attribute="Parameter Expansion Operator" context="Subscript" char="["/>
+ <IncludeRules context="FindWord"/>
+ <DetectChar attribute="Error" context="#stay" char="#"/>
+ <DetectChar context="NormalMaybeBraceExpansion" char="{" lookAhead="1"/>
</context>
<!-- ProcessSubst handles <(command) and >(command) -->
- <context attribute="Normal Text" lineEndContext="#stay" name="ProcessSubst">
- <DetectChar attribute="Redirection" context="#pop" char=")" />
- <IncludeRules context="FindCommentsParen" />
- <IncludeRules context="FindCommands" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
- <IncludeRules context="FindOthers" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="ProcessSubst" fallthroughContext="Command">
+ <DetectChar attribute="Redirection" context="#pop" char=")"/>
+ <IncludeRules context="Start"/>
</context>
<!-- StringSQ consumes anything till ' -->
<context attribute="String SingleQ" lineEndContext="#stay" name="StringSQ">
- <DetectChar attribute="String SingleQ" context="#pop" char="'" />
+ <DetectSpaces attribute="String SingleQ"/>
+ <DetectIdentifier attribute="String SingleQ"/>
+ <DetectChar attribute="String SingleQ" context="#pop" char="'"/>
</context>
<!-- StringDQ consumes anything till ", substitutes vars and expressions -->
<context attribute="String DoubleQ" lineEndContext="#stay" name="StringDQ">
- <DetectChar attribute="String DoubleQ" context="#pop" char="&quot;" />
- <RegExpr attribute="String Escape" context="#stay" String="\\[`&quot;\\$\n]" />
- <IncludeRules context="FindSubstitutions" />
+ <DetectSpaces attribute="String DoubleQ"/>
+ <DetectIdentifier attribute="String DoubleQ"/>
+ <DetectChar attribute="String DoubleQ" context="#pop" char="&quot;"/>
+ <DetectChar context="StringDQEscape" char="\" lookAhead="1"/>
+ <DetectChar context="StringDQDispatchVariables" char="$" lookAhead="1"/>
+ <DetectChar attribute="Backquote" context="RegularBackq" char="`"/>
+ </context>
+ <context attribute="String DoubleQ" lineEndContext="#stay" name="StringDQDispatchVariables">
+ <IncludeRules context="DispatchSubstVariables"/>
+ <IncludeRules context="DispatchVarnameVariables"/>
+ <DetectChar attribute="String DoubleQ" context="#pop" char="$"/>
+ </context>
+ <context attribute="String DoubleQ" lineEndContext="#pop" name="StringDQEscape">
+ <Detect2Chars attribute="String Escape" context="#pop" char="\" char1="&quot;"/>
+ <Detect2Chars attribute="String Escape" context="#pop" char="\" char1="\"/>
+ <Detect2Chars attribute="String Escape" context="#pop" char="\" char1="`"/>
+ <Detect2Chars attribute="String Escape" context="#pop" char="\" char1="$"/>
+ <LineContinue attribute="String Escape" context="#pop"/>
+ <DetectChar attribute="String DoubleQ" context="#pop" char="\"/>
+ </context>
+
+ <!-- RegularBackq consumes anything till ` -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="RegularBackq" fallthroughContext="Command">
+ <DetectChar attribute="Backquote" context="#pop" char="`"/>
+ <DetectChar attribute="Comment" context="CommentBackq" char="#"/>
+ <IncludeRules context="Start"/>
</context>
<!-- StringEsc eats till ', but escaping many characters -->
<context attribute="String SingleQ" lineEndContext="#stay" name="StringEsc">
- <DetectChar attribute="String SingleQ" context="#pop" char="'" />
- <RegExpr attribute="String Escape" context="#stay" String="\\[abefnrtv\\']" />
- <RegExpr attribute="String Escape" context="#stay" String="\\([0-7]{1,3}|x[A-Fa-f0-9]{1,2}|c.)" />
+ <DetectSpaces attribute="String SingleQ"/>
+ <DetectIdentifier attribute="String SingleQ"/>
+ <DetectChar attribute="String SingleQ" context="#pop" char="'"/>
+ <RegExpr attribute="String Escape" context="#stay" String="\\(?:[abefnrtv\\']|[0-7]{1,3}|x[A-Fa-f0-9]{1,2}|c.)"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="FindWord">
+ <IncludeRules context="FindStrings"/>
+ <DetectChar context="RegularVariable" char="$" lookAhead="1"/>
+ <DetectChar attribute="Backquote" context="RegularBackq" char="`"/>
+ <DetectChar context="AssumeEscape" char="\" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="RegularVariable">
+ <IncludeRules context="DispatchVariables"/>
+ <DetectChar attribute="Normal Text" context="#pop" char="$"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="FindStrings">
+ <DetectChar attribute="String SingleQ" context="StringSQ" char="'"/>
+ <DetectChar attribute="String DoubleQ" context="StringDQ" char="&quot;"/>
+ </context>
+
+ <!-- SubstCommand is called after a $( is encountered -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="SubstCommand" fallthroughContext="Command">
+ <DetectChar attribute="Parameter Expansion" context="#pop" char=")" endRegion="subshell"/>
+ <IncludeRules context="Start"/>
</context>
- <!-- VarBrace is called as soon as ${xxx is encoutered -->
- <context attribute="Error" lineEndContext="#stay" name="VarBrace">
- <DetectChar attribute="Variable" context="#pop" char="}" />
- <DetectChar attribute="Variable" context="Subscript" char="[" />
- <RegExpr attribute="Variable" context="VarAlt" String="(:?[-=?+@]|##?|%%?|\^\^?|,,?)" />
- <RegExpr attribute="Variable" context="VarSubst" String="//?" />
- <DetectChar attribute="Variable" context="VarSub" char=":" />
+ <!-- VarBraceStart is called as soon as ${ is encoutered -->
+ <context attribute="Variable" lineEndContext="#pop!VarBrace" name="VarBraceStart" fallthroughContext="#pop!VarBrace">
+ <StringDetect context="#pop!VarBrace" String="!}" lookAhead="1"/>
+ <DetectChar attribute="Parameter Expansion Operator" context="#pop!VarBracePrefix" char="!"/>
+ <DetectChar attribute="Parameter Expansion Operator" context="#pop!VarBrace" char="#"/>
</context>
- <!-- VarAlt is to handle default/alternate/etc values of variables -->
- <context attribute="Normal Text" lineEndContext="#stay" name="VarAlt">
- <DetectChar attribute="Variable" context="#pop#pop" char="}" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
+ <!-- VarBracePrefix called as soon as ${! is encoutered -->
+ <context attribute="Variable" lineEndContext="#pop" name="VarBracePrefix">
+ <DetectIdentifier attribute="Variable" context="#pop!VarBracePrefixSuffix"/>
+ <Int attribute="Variable" context="#pop!VarBracePrefixSuffix"/>
+ <AnyChar attribute="Parameter Expansion Operator" context="#pop!VarBracePrefixSharp" String="@*#"/>
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ </context>
+ <context attribute="Variable" lineEndContext="#pop" name="VarBracePrefixSuffix" fallthroughContext="#pop!VarError">
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ <AnyChar attribute="Parameter Expansion Operator" context="#stay" String="@*"/>
+ <StringDetect attribute="Parameter Expansion Operator" context="#stay" String="[@]"/>
+ <StringDetect attribute="Parameter Expansion Operator" context="#stay" String="[*]"/>
+ </context>
+ <context attribute="Variable" lineEndContext="#pop" name="VarBracePrefixSharp" fallthroughContext="#pop!VarError">
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ <DetectChar attribute="Parameter Expansion Operator" char="#"/>
</context>
- <!-- VarSubst is to handle substitutions on variables -->
- <context attribute="Normal Text" lineEndContext="#stay" name="VarSubst">
- <DetectChar attribute="Variable" context="#pop#pop" char="}" />
- <DetectChar attribute="Variable" context="VarSubst2" char="/" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
+ <!-- VarBrace is called as soon as ${ or ${# are encoutered -->
+ <context attribute="Variable" lineEndContext="#stay" name="VarBrace" fallthroughContext="#pop!VarError">
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ <DetectIdentifier attribute="Variable" context="#pop!CheckVarAlt"/>
+ <AnyChar attribute="Variable" context="#pop!CheckVarAlt" String="*@?$!-"/>
+ <Int attribute="Variable" context="#pop!CheckVarAlt" additionalDeliminator=":#%^,/@{}"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="VarSubst2">
- <DetectChar attribute="Variable" context="#pop#pop#pop" char="}" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
+ <context attribute="Error" lineEndContext="#stay" name="VarError">
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="CheckVarAlt" fallthroughContext="#pop!VarError">
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ <StringDetect attribute="Parameter Expansion Operator" context="#stay" String="[@]"/>
+ <StringDetect attribute="Parameter Expansion Operator" context="#stay" String="[*]"/>
+ <DetectChar attribute="Parameter Expansion Operator" context="Subscript" char="["/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop!AlternateValue" char=":" char1="-"/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop!AlternateValue" char=":" char1="="/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop!AlternateValue" char=":" char1="?"/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop!AlternateValue" char=":" char1="+"/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop!AlternateValue" char="#" char1="#"/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop!AlternateValue" char="%" char1="%"/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop!AlternatePatternValue" char="^" char1="^"/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop!AlternatePatternValue" char="," char1=","/>
+ <DetectChar attribute="Parameter Expansion Operator" context="#pop!VarSub" char=":"/>
+ <DetectChar attribute="Parameter Expansion Operator" context="#pop!VarSubst" char="/"/>
+ <AnyChar attribute="Parameter Expansion Operator" context="#pop!AlternateValue" String="-+=?#%"/>
+ <AnyChar attribute="Parameter Expansion Operator" context="#pop!AlternatePatternValue" String="^,"/>
+ <DetectChar attribute="Parameter Expansion Operator" context="#pop!VarTransformation" char="@"/>
</context>
- <!-- VarSub is to substrings of variables -->
- <context attribute="Error" lineEndContext="#stay" name="VarSub">
- <DetectSpaces attribute="Variable" context="#stay" />
- <DetectChar attribute="Variable" context="VarSub2" char=":" />
- <DetectChar attribute="Variable" context="#pop#pop" char="}" />
- <RegExpr attribute="Variable" context="#stay" String="&varname;" />
- <RegExpr attribute="Variable" context="#stay" String="([\-\+]\s*)?[0-9]+\s*(?=[:}])" />
- <IncludeRules context="FindSubstitutions" />
+ <!-- called as soon as ${xxx: is encoutered -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="VarSub">
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ <!-- <Int> doesn't match :3 because : is a weakDeliminator -->
+ <AnyChar attribute="Decimal" String="0123456789"/>
+ <DetectChar attribute="Parameter Expansion Operator" context="#stay" char=":"/>
+ <DetectChar context="VarVariables" char="$" lookAhead="1"/>
+ <IncludeRules context="FindStrings"/>
+ <DetectChar attribute="Backquote" context="RegularBackq" char="`"/>
+ <DetectChar context="AssumeEscape" char="\" lookAhead="1"/>
</context>
- <context attribute="Error" lineEndContext="#stay" name="VarSub2">
- <DetectSpaces attribute="Variable" context="#stay" />
- <DetectChar attribute="Variable" context="#pop#pop#pop" char="}" />
- <RegExpr attribute="Variable" context="#stay" String="&varname;" />
- <RegExpr attribute="Variable" context="#stay" String="([\-\+]\s*)?[0-9]+\s*(?=[:}])" />
- <IncludeRules context="FindSubstitutions" />
+ <context attribute="Command" lineEndContext="#pop" name="VarVariables">
+ <IncludeRules context="DispatchVariables"/>
+ <DetectChar attribute="Error" context="#pop" char="$"/>
</context>
+ <!-- called as soon as ${xxx:- ${xxx:= ${xxx:? ${xxx:+ ${xxx# ${xxx% and ${xxx are encoutered -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="AlternateValue">
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ <IncludeRules context="FindWord"/>
+ <IncludeRules context="FindPathThenPopInAlternateValue"/>
+ <DetectIdentifier attribute="Normal Text"/>
+ </context>
- <!-- SubstFile is called after a <( or >( is encoutered -->
- <context attribute="Normal Text" lineEndContext="#stay" name="SubstFile">
- <DetectChar attribute="Redirection" context="#pop" char=")" />
- <IncludeRules context="FindCommentsParen" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
- <IncludeRules context="FindOthers" />
+ <!-- called as soon as ${xxx^ are ${xxx, are encoutered -->
+ <context attribute="Pattern" lineEndContext="#stay" name="AlternatePatternValue">
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ <IncludeRules context="FindWord"/>
+ <IncludeRules context="FindPattern"/>
+ <DetectIdentifier attribute="Pattern"/>
</context>
- <!-- SubstCommand is called after a $( is encountered -->
- <context attribute="Normal Text" lineEndContext="#stay" name="SubstCommand">
- <DetectChar attribute="Variable" context="#pop" char=")" />
- <IncludeRules context="FindCommentsParen" />
- <IncludeRules context="FindCommands" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
- <IncludeRules context="FindOthers" />
+ <!-- called as soon as ${xxx/ is encoutered -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="VarSubst" fallthroughContext="#pop!VarSubstPat">
+ <AnyChar attribute="Parameter Expansion Operator" context="#pop!VarSubstPat" String="/#%"/>
+ </context>
+ <context attribute="Pattern" lineEndContext="#stay" name="VarSubstPat">
+ <DetectChar attribute="Parameter Expansion Operator" context="#pop!AlternateValue" char="/"/>
+ <IncludeRules context="AlternatePatternValue"/>
</context>
- <!-- SubstBackq is called when a backquote is encountered -->
- <context attribute="Normal Text" lineEndContext="#stay" name="SubstBackq">
- <DetectChar attribute="Backquote" context="#pop" char="`" />
- <IncludeRules context="FindCommentsBackq" />
- <IncludeRules context="FindCommandsBackq" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindSubstitutions" />
- <IncludeRules context="FindOthers" />
+ <!-- called as soon as ${xxx@ is encoutered -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="VarTransformation" fallthroughContext="#pop!VarError">
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ <AnyChar attribute="Parameter Expansion" context="#stay" String="UuLQEPAKa"/>
</context>
- <!-- Case is called after the case keyword is encoutered. We handle this because of
- the lonely closing parentheses that would otherwise disturb the expr matching -->
- <context attribute="Normal Text" lineEndContext="#stay" name="Case">
- <RegExpr attribute="Keyword" context="CaseIn" String="\sin\b" />
- <IncludeRules context="FindMost" />
+ <context attribute="Escape" lineEndContext="#pop" name="BraceExpansion">
+ <DetectChar attribute="Escape" context="#pop!BraceExpansion2" char="{"/>
+ </context>
+ <context attribute="Escape" lineEndContext="#pop" name="BraceExpansion2">
+ <DetectChar attribute="Operator" context="#stay" char=","/>
+ <DetectChar attribute="Escape" context="#pop" char="}"/>
+ <DetectChar context="EscapeMaybeBraceExpansion" char="{" lookAhead="1"/>
+ <DetectChar context="AssumeEscape" char="\" lookAhead="1"/>
+ <DetectChar attribute="Backquote" context="CommandBackq" char="`"/>
+ <DetectChar context="BraceExpansionVariables" char="$" lookAhead="1"/>
+ <IncludeRules context="FindStrings"/>
+ <IncludeRules context="FindPattern"/>
+ <DetectIdentifier attribute="Escape"/>
+ </context>
+ <context attribute="Escape" lineEndContext="#pop" name="EscapeMaybeBraceExpansion">
+ <IncludeRules context="DispatchBraceExpansion"/>
+ <DetectChar attribute="Escape" context="#pop!BraceExpansion2" char="{"/>
+ </context>
+ <context attribute="Escape" lineEndContext="#pop" name="BraceExpansionVariables">
+ <IncludeRules context="DispatchVariables"/>
+ <DetectChar attribute="Escape" context="#pop" char="$"/>
</context>
- <!-- CaseIn is called when the construct 'case ... in' has been found. -->
- <context attribute="Normal Text" lineEndContext="#stay" name="CaseIn">
- <RegExpr attribute="Keyword" context="#pop#pop" String="\besac(?=$|[\s;)])" endRegion="case" />
- <DetectChar attribute="Keyword" context="CaseExpr" char=")" beginRegion="caseexpr" />
- <AnyChar attribute="Keyword" context="#stay" String="(|" />
- <IncludeRules context="FindMost" />
+ <context attribute="Normal Text" lineEndContext="#pop" name="SequenceExpression">
+ <AnyChar attribute="Decimal" context="#stay" String="0123456789-+"/>
+ <Detect2Chars attribute="Escape" context="#stay" char="." char1="."/>
+ <DetectChar attribute="Escape" context="#pop" char="}"/>
+ </context>
+
+<!-- ====== These are the contexts that can be branched to ======= -->
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblParenOrSubShell">
+ <RegExpr attribute="Keyword" context="#pop!SubShell" String="\((?=&arithmetic_as_subshell;)|" beginRegion="subshell"/>
+ <Detect2Chars attribute="Keyword" context="#pop!ExprDblParen" char="(" char1="(" beginRegion="expression"/>
+ </context>
+ <!-- ExprDblParen consumes an expression started in command mode till )) -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblParen">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <Detect2Chars attribute="Keyword" context="#pop" char=")" char1=")" endRegion="expression"/>
+ <IncludeRules context="FindExprDblParen"/>
+ <!-- ((cmd
+ ) # jump to SubShell context -->
+ <DetectChar attribute="Keyword" context="#pop!SubShell" char=")" endRegion="expression" beginRegion="subshell"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FindExprDblParen">
+ <Detect2Chars attribute="Control" context="#stay" char="&amp;" char1="&amp;"/>
+ <Detect2Chars attribute="Control" context="#stay" char="|" char1="|"/>
+ <AnyChar attribute="Operator" context="#stay" String="+-!~*/%&lt;>=&amp;^|?:"/>
+ <AnyChar context="Number" String="0123456789" lookAhead="1"/>
+ <DetectChar attribute="Control" context="#stay" char=","/>
+ <DetectChar attribute="Normal Text" context="ExprSubDblParen" char="("/>
+ <DetectChar attribute="Parameter Expansion Operator" context="Subscript" char="["/>
+ <IncludeRules context="FindWord"/>
+ <DetectChar attribute="Error" context="#stay" char="#"/>
+ <DetectChar context="MaybeArithmeticBrace" char="{" lookAhead="1"/>
+ <DetectIdentifier attribute="Variable" context="#stay"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprSubDblParen">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ <IncludeRules context="FindExprDblParen"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="MaybeArithmeticBrace">
+ <IncludeRules context="DispatchBraceExpansion"/>
+ <DetectChar attribute="Error" context="#pop" char="{"/>
</context>
- <!-- CaseExpr eats shell input till ;; -->
- <context attribute="Normal Text" lineEndContext="#stay" name="CaseExpr">
- <Detect2Chars attribute="Keyword" context="#pop" char=";" char1=";" endRegion="caseexpr" />
- <RegExpr attribute="Keyword" context="#pop" String="esac(?=$|[\s;)])" lookAhead="true" firstNonSpace="true" endRegion="caseexpr"/>
- <IncludeRules context="FindAll" />
+ <context attribute="Decimal" lineEndContext="#pop" name="Number">
+ <HlCHex attribute="Hex" context="#pop" additionalDeliminator="&weakDeliminatorSymbols;"/>
+ <HlCOct attribute="Octal" context="#pop!NumberError" additionalDeliminator="&weakDeliminatorSymbols;"/>
+ <RegExpr attribute="Base" context="#pop!BaseN" String="[1-9][0-9]*#"/>
+ <DetectChar attribute="Decimal" context="#pop!NumberError" char="0"/>
+ <Int attribute="Decimal" context="#pop" additionalDeliminator="&weakDeliminatorSymbols;"/>
+ </context>
+ <context attribute="BaseN" lineEndContext="#pop" name="BaseN" fallthroughContext="#pop">
+ <RegExpr attribute="BaseN" context="#pop" String="[0-9a-zA-Z@_]+"/>
+ </context>
+ <context attribute="Error" lineEndContext="#pop" name="NumberError" fallthroughContext="#pop">
+ <Int attribute="Error" context="#pop" additionalDeliminator="0123456789"/>
</context>
- <!-- HereDoc consumes Here-documents. It is called at the beginning of the "<<" construct. -->
- <context attribute="Normal Text" lineEndContext="#stay" name="HereDoc">
- <RegExpr attribute="Redirection" context="HereDocIQ" String="(&lt;&lt;-\s*&quot;(&word;)&quot;)" lookAhead="true" />
- <RegExpr attribute="Redirection" context="HereDocIQ" String="(&lt;&lt;-\s*'(&word;)')" lookAhead="true" />
- <RegExpr attribute="Redirection" context="HereDocIQ" String="(&lt;&lt;-\s*\\(&word;))" lookAhead="true" />
- <RegExpr attribute="Redirection" context="HereDocINQ" String="(&lt;&lt;-\s*(&word;))" lookAhead="true" />
- <RegExpr attribute="Redirection" context="HereDocQ" String="(&lt;&lt;\s*&quot;(&word;)&quot;)" lookAhead="true" />
- <RegExpr attribute="Redirection" context="HereDocQ" String="(&lt;&lt;\s*'(&word;)')" lookAhead="true" />
- <RegExpr attribute="Redirection" context="HereDocQ" String="(&lt;&lt;\s*\\(&word;))" lookAhead="true" />
- <RegExpr attribute="Redirection" context="HereDocNQ" String="(&lt;&lt;\s*(&word;))" lookAhead="true" />
- <Detect2Chars attribute="Redirection" context="#pop" char="&lt;" char1="&lt;" /><!-- always met -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblParenSubstOrSubstCommand">
+ <RegExpr attribute="Parameter Expansion" context="#pop!SubstCommand" String="\$\((?=&arithmetic_as_subshell;)|" beginRegion="subshell"/>
+ <StringDetect attribute="Parameter Expansion" context="#pop!ExprDblParenSubst" String="$((" beginRegion="expression"/>
+ </context>
+ <!-- ExprDblParenSubst like ExprDblParen but matches )) as Variable -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblParenSubst">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <Detect2Chars attribute="Parameter Expansion" context="#pop" char=")" char1=")" endRegion="expression"/>
+ <IncludeRules context="FindExprDblParen"/>
+ <!-- $((cmd
+ ) # jump to SubstCommand context -->
+ <DetectChar attribute="Parameter Expansion" context="#pop!SubstCommand" char=")" endRegion="expression" beginRegion="subshell"/>
</context>
- <context attribute="Normal Text" lineEndContext="#pop" name="HereDocRemainder">
- <IncludeRules context="FindAll" />
+ <!-- ExprBracket consumes an expression till ] -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprBracket" fallthroughContext="#pop!ExprBracketNot">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <IncludeRules context="FindExprBracketEnd"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="HereDocQ" dynamic="true">
- <RegExpr attribute="Redirection" context="HereDocRemainder" String="%1" dynamic="true" />
- <RegExpr attribute="Redirection" context="#pop#pop" String="^%2\b" dynamic="true" column="0"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprBracketNot" fallthroughContext="#pop!ExprBracketParam1">
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprBracketParam1"/>
+ <Detect2Chars attribute="Expression" context="ExprBracketTestMaybeNot" char="!" char1=" " lookAhead="1"/>
+ <Detect2Chars attribute="Expression" context="ExprBracketTestMaybeNot" char="!" char1="&tab;" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprBracketTestMaybeNot">
+ <DetectChar attribute="Expression" context="#pop" char="!"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="HereDocNQ" dynamic="true">
- <RegExpr attribute="Redirection" context="HereDocRemainder" String="%1" dynamic="true" />
- <RegExpr attribute="Redirection" context="#pop#pop" String="^%2\b" dynamic="true" column="0"/>
- <IncludeRules context="FindSubstitutions" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprBracketParam1" fallthroughContext="ExprBracketValue">
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprBracketParam2"/>
+ <DetectChar context="TestMaybeUnary" char="-" lookAhead="1"/>
+ <IncludeRules context="FindExprBracketEnd"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="HereDocIQ" dynamic="true">
- <RegExpr attribute="Redirection" context="HereDocRemainder" String="%1" dynamic="true" />
- <RegExpr attribute="Redirection" context="#pop#pop" String="^\t*%2\b" dynamic="true" column="0"/>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprBracketValue">
+ <AnyChar attribute="Error" context="#stay" String="&symbolseps;"/>
+ <AnyChar context="#pop" String=" &tab;" lookAhead="1"/>
+ <IncludeRules context="FindWord"/>
+ <DetectChar context="NormalMaybeBraceExpansion" char="{" lookAhead="1"/>
+ <IncludeRules context="FindNormalText"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprBracketParam2" fallthroughContext="ExprBracketValue">
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprBracketParam3"/>
+ <AnyChar context="TestMaybeBinary" String="-=!" lookAhead="1"/>
+ <IncludeRules context="FindExprBracketEnd"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="ExprBracketFinal" name="ExprBracketParam3" fallthroughContext="ExprBracketValue">
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprBracketFinal"/>
+ <IncludeRules context="FindExprBracketEnd"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprBracketFinal" fallthroughContext="ExprBracketValue">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <IncludeRules context="FindExprBracketEnd"/>
+ <RegExpr attribute="Expression" context="#pop!ExprBracket" String="-[ao]&eos;"/>
+ <RegExpr attribute="Error" context="#pop" String="(?:[^] &tab;]++|\][^ &tab;])++" endRegion="expression"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="FindExprBracketEnd">
+ <LineContinue attribute="Escape" context="#stay"/>
+ <RegExpr attribute="Builtin" context="#pop" String="\](?=($|[ &tab;;|&amp;&lt;>)]))" endRegion="expression"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="TestMaybeUnary" fallthroughContext="#pop!ExprBracketValue">
+ <RegExpr attribute="Expression" context="#pop#pop!ExprBracketParam2" String="&unary_operators;"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="TestMaybeBinary" fallthroughContext="#pop!ExprBracketValue">
+ <RegExpr attribute="Expression" context="#pop" String="&binary_operators;"/>
+ </context>
+
+
+ <!-- ExprDblBracket consumes an expression till ]] -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblBracket" fallthroughContext="#pop!ExprDblBracketNot">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <DetectChar attribute="Comment" context="Comment" char="#"/>
+ <IncludeRules context="FindExprDblBracketEnd"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblBracketNot" fallthroughContext="#pop!ExprDblBracketParam1">
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprDblBracketParam1"/>
+ <DetectChar context="ExprDblBracketTestMaybeNot" char="!" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprDblBracketTestMaybeNot" fallthroughContext="#pop#pop!ExprDblBracketParam1">
+ <RegExpr attribute="Expression" context="#pop" String="!(?=$|[ &tab;(])"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblBracketParam1" fallthroughContext="ExprDblBracketValueText">
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprDblBracketParam2"/>
+ <DetectChar context="TestMaybeUnary2" char="-" lookAhead="1"/>
+ <AnyChar attribute="Expression" context="#pop!ExprDblBracketParam3Spe" String="&lt;>"/>
+ <IncludeRules context="FindExprDblBracketEnd"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="TestMaybeUnary2" fallthroughContext="#pop!ExprDblBracketValueText">
+ <RegExpr attribute="Expression" context="#pop!ExprDblBracketUnary" String="&unary_operators;(?!\s+(?:=~|&binary_operators;))"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprDblBracketUnary">
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprDblBracketValueText"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprDblBracketValueText" fallthroughContext="#pop!ExprDblBracketValueText2">
+ <Detect2Chars context="#pop!ExprDblBracketValueTextMaybeEnd" char="]" char1="]" lookAhead="1"/>
+ <DetectChar attribute="Error" context="#pop!Comment" char="#"/>
+ <IncludeRules context="FindExprDblBracketValueTextPath"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="FindExprDblBracketValueTextPath">
+ <RegExpr context="#pop!ExprDblBracketValueTextPath" String="&path_with_sep;|" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprDblBracketValueTextMaybeEnd">
+ <RegExpr attribute="Keyword" context="#pop#pop" String="&dblbracket_close;" endRegion="expression"/>
+ <IncludeRules context="FindExprDblBracketValueTextPath"/>
+ <Detect2Chars context="#pop!ExprDblBracketValueText2" char="]" char1="]" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprDblBracketValueText2">
+ <DetectIdentifier/>
+ <AnyChar String="*?+!@~^:%+-/,"/>
+ <IncludeRules context="ExprDblBracketValueCommon"/>
+ </context>
+ <context attribute="Path" lineEndContext="#pop" name="ExprDblBracketValueTextPath">
+ <IncludeRules context="ExprDblBracketValueText2"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprDblBracketValuePattern" fallthroughContext="#pop!ExprDblBracketValuePattern2">
+ <RegExpr context="PathThenPop" String="&path_with_sep;|" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprDblBracketValuePattern2">
+ <DetectIdentifier attribute="Normal Text"/>
+ <IncludeRules context="ExprDblBracketValueCommon"/>
+ <IncludeRules context="FindExprDblBracketValueExtGlob"/>
+ <AnyChar attribute="Glob" String="?*"/>
+ </context>
+ <context attribute="Glob" lineEndContext="#stay" name="FindExprDblBracketValueExtGlob">
+ <Detect2Chars attribute="Glob" context="ExprDblBracketValueExtGlob" char="?" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExprDblBracketValueExtGlob" char="*" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExprDblBracketValueExtGlob" char="+" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExprDblBracketValueExtGlob" char="@" char1="("/>
+ <Detect2Chars attribute="Glob" context="ExprDblBracketValueExtGlob" char="!" char1="("/>
+ <DetectChar context="ExprDblBracketValueMaybeGlobAny" char="[" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblBracketValueMaybeGlobAny" fallthroughContext="#pop">
+ <IncludeRules context="FindGlobAny"/>
+ <DetectChar attribute="Normal Text" context="#pop" char="["/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop#pop" name="ExprDblBracketValueExtGlob">
+ <DetectIdentifier attribute="Normal Text"/>
+ <DetectChar attribute="Glob" context="#pop" char=")"/>
+ <DetectChar attribute="Normal Text" context="ExprDblBracketValueExtGlobNormal" char="("/>
+ <IncludeRules context="FindExprDblBracketValueExtGlob"/>
+ <AnyChar attribute="Glob" String="|?*"/>
+ <IncludeRules context="FindWord"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop#pop" name="ExprDblBracketValueExtGlobNormal">
+ <DetectIdentifier attribute="Normal Text"/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ <DetectChar attribute="Normal Text" context="ExprDblBracketValueExtGlobNormal" char="("/>
+ <IncludeRules context="FindExprDblBracketValueExtGlob"/>
+ <AnyChar attribute="Glob" String="?*"/>
+ <IncludeRules context="FindWord"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprDblBracketValueCommon">
+ <Detect2Chars context="ExprDblBracketDblParentOrSubValue" char="(" char1="(" lookAhead="1"/>
+ <DetectChar context="ExprDblBracketSubValue" char="(" lookAhead="1"/>
+ <DetectChar attribute="Operator" context="#pop#pop" char=")"/>
+ <Detect2Chars attribute="Control" context="#pop#pop!ExprDblBracket" char="&amp;" char1="&amp;"/>
+ <Detect2Chars attribute="Control" context="#pop#pop!ExprDblBracket" char="|" char1="|"/>
+ <AnyChar attribute="Error" context="#stay" String="|&amp;;"/>
+ <AnyChar context="#pop" String=" &tab;&lt;>" lookAhead="1"/>
+ <IncludeRules context="FindWord"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprDblBracketSubValue" fallthroughContext="#pop">
+ <DetectChar attribute="Operator" context="ExprDblBracketNot" char="("/>
+ <Detect2Chars context="#pop#pop" char="]" char1="]" lookAhead="1"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprDblBracketDblParentOrSubValue">
+ <RegExpr context="#pop!ExprDblBracketSubValue" String="\((?=&arithmetic_as_subshell;)|" lookAhead="1"/>
+ <Detect2Chars attribute="Keyword" context="#pop!ExprDblBracketExprDblParen" char="(" char1="(" beginRegion="expression"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblBracketExprDblParen">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <Detect2Chars attribute="Keyword" context="#pop" char=")" char1=")" endRegion="expression"/>
+ <IncludeRules context="FindExprDblParen"/>
+ <!-- ((cmd
+ ) # jump to ExprDblBracketValuePattern context -->
+ <DetectChar attribute="Operator" context="ExprDblBracketValuePattern" char=")" endRegion="expression" beginRegion="subshell"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblBracketParam2" fallthroughContext="ExprDblBracketValuePattern">
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprDblBracketParam3"/>
+ <AnyChar context="TestMaybeBinary2" String="-=!" lookAhead="1"/>
+ <AnyChar attribute="Expression" context="#pop!ExprDblBracketParam3Spe" String="&lt;>"/>
+ <IncludeRules context="FindExprDblBracketEnd"/>
+ <DetectChar attribute="Comment" context="Comment" char="#"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="TestMaybeBinary2" fallthroughContext="#pop!ExprDblBracketValuePattern">
+ <IncludeRules context="TestMaybeBinary"/>
+ <RegExpr attribute="Expression" context="#pop!ExprDblBracketRegex" String="=~(?=[ &tab;(])"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblBracketParam3Spe" fallthroughContext="#pop!ExprDblBracketParam3">
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprDblBracketParam3"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblBracketParam3" fallthroughContext="ExprDblBracketValuePattern">
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprDblBracketFinal"/>
+ <IncludeRules context="FindExprDblBracketEnd"/>
+ <AnyChar attribute="Error" context="#stay" String="&lt;>"/>
+ <DetectChar attribute="Comment" context="Comment" char="#"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblBracketFinal" fallthroughContext="ExprDblBracketValuePattern">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <IncludeRules context="FindExprDblBracketEnd"/>
+ <DetectChar attribute="Comment" context="Comment" char="#"/>
+ <DetectChar attribute="Operator" context="#pop" char=")"/>
+ <RegExpr attribute="Error" context="#pop" String="(?:[^] &tab;]++|\](?:[^]]|\][^ &tab;]))++" endRegion="expression"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="FindExprDblBracketEnd">
+ <LineContinue attribute="Escape" context="#stay"/>
+ <Detect2Chars attribute="Control" context="#pop!ExprDblBracket" char="&amp;" char1="&amp;"/>
+ <Detect2Chars attribute="Control" context="#pop!ExprDblBracket" char="|" char1="|"/>
+ <RegExpr attribute="Keyword" context="#pop" String="&dblbracket_close;" endRegion="expression"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExprDblBracketRegex" fallthroughContext="#pop!ExprDblBracketRegexCheck">
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprDblBracketRegexCheck"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="ExprDblBracketRegexCheck" fallthroughContext="#pop#pop!Regex">
+ <DetectChar attribute="Error" context="Comment" char="#"/>
+ </context>
+ <context attribute="Pattern" lineEndContext="#stay" name="Regex">
+ <DetectIdentifier attribute="Pattern"/>
+ <DetectSpaces attribute="Normal Text" context="#pop!ExprDblBracketFinal"/>
+ <DetectChar attribute="Operator" context="#pop" char=")"/>
+ <Detect2Chars attribute="Operator" context="RegexChar" char="[" char1="^"/>
+ <DetectChar attribute="Operator" context="RegexChar" char="["/>
+ <IncludeRules context="FindRegex"/>
+ </context>
+ <context attribute="Pattern" lineEndContext="#stay" name="ExprDblBracketSubRegex">
+ <DetectIdentifier attribute="Pattern"/>
+ <DetectSpaces attribute="Pattern" context="#stay"/>
+ <DetectChar attribute="Operator" context="#pop" char=")"/>
+ <Detect2Chars attribute="Operator" context="RegexSubChar" char="[" char1="^"/>
+ <DetectChar attribute="Operator" context="RegexSubChar" char="["/>
+ <IncludeRules context="FindRegex"/>
+ </context>
+
+ <context attribute="Pattern" lineEndContext="#stay" name="FindRegex">
+ <DetectChar attribute="Operator" context="ExprDblBracketSubRegex" char="("/>
+ <DetectChar attribute="Escape" context="RegexEscape" char="\"/>
+ <DetectChar attribute="Parameter Expansion" context="RegexDup" char="{"/>
+ <AnyChar attribute="Glob" context="#stay" String="^?+*.|"/>
+ <IncludeRules context="FindStrings"/>
+ <DetectChar context="RegexDispatchVariables" char="$" lookAhead="1"/>
+ <DetectChar attribute="Backquote" context="RegularBackq" char="`"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="RegexDispatchVariables">
+ <IncludeRules context="DispatchVariables"/>
+ <DetectChar attribute="Operator" context="#pop" char="$"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="RegexEscape">
+ <RegExpr attribute="Escape" context="#pop" String="x[0-9a-fA-F]{1,2}|[0-7]{1,3}|."/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="RegexDup">
+ <Int attribute="Decimal" additionalDeliminator="{"/>
+ <DetectChar attribute="Parameter Expansion Operator" context="#stay" char=","/>
+ <DetectChar attribute="Parameter Expansion" context="#pop" char="}"/>
+ </context>
+
+ <context attribute="Pattern" lineEndContext="#pop" name="RegexSubChar" fallthroughContext="#pop!RegexSubInChar">
+ <AnyChar attribute="Pattern" context="#pop!RegexSubInChar" String="-]"/>
+ </context>
+ <context attribute="Pattern" lineEndContext="#pop" name="RegexSubInChar">
+ <DetectSpaces attribute="Pattern" context="#stay"/>
+ <IncludeRules context="RegexInChar"/>
+ </context>
+
+ <context attribute="Pattern" lineEndContext="#pop" name="RegexChar" fallthroughContext="#pop!RegexInChar">
+ <AnyChar attribute="Pattern" context="#pop!RegexInChar" String="-]"/>
+ </context>
+ <context attribute="Pattern" lineEndContext="#pop" name="RegexInChar">
+ <Detect2Chars context="RegexInCharEnd" char="-" char1="]" lookAhead="1"/>
+ <DetectChar attribute="Operator" context="#stay" char="-"/>
+ <DetectChar attribute="Escape" context="RegexEscape" char="\"/>
+ <DetectChar context="RegexCharClassSelect" char="[" lookAhead="1"/>
+ <DetectChar attribute="Operator" context="#pop" char="]"/>
+ <AnyChar context="#pop" String="() &tab;" lookAhead="1"/>
+ <IncludeRules context="FindStrings"/>
+ </context>
+ <context attribute="Operator" lineEndContext="#stay" name="RegexInCharEnd">
+ <DetectChar attribute="Pattern" context="#stay" char="-"/>
+ <DetectChar attribute="Operator" context="#pop#pop" char="]"/>
+ </context>
+ <context attribute="Parameter Expansion" lineEndContext="#pop#pop#pop" name="RegexCharClassSelect">
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop!RegexCharClass" char="[" char1=":"/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop!RegexCollatingSymbols" char="[" char1="."/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop!RegexEquivalenceClass" char="[" char1="="/>
+ <DetectChar attribute="Pattern" context="#pop" char="["/>
+ </context>
+
+ <context attribute="Parameter Expansion" lineEndContext="#pop#pop#pop" name="RegexCharClass">
+ <DetectIdentifier attribute="Parameter Expansion"/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop" char=":" char1="]"/>
+ <DetectChar attribute="Error" context="#pop" char="]"/>
+ </context>
+ <context attribute="Parameter Expansion" lineEndContext="#pop#pop#pop" name="RegexCollatingSymbols">
+ <DetectIdentifier attribute="Parameter Expansion"/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop" char="." char1="]"/>
+ <DetectChar attribute="Error" context="#pop" char="]"/>
+ </context>
+ <context attribute="Parameter Expansion" lineEndContext="#pop#pop#pop" name="RegexEquivalenceClass">
+ <DetectIdentifier attribute="Parameter Expansion"/>
+ <Detect2Chars attribute="Parameter Expansion Operator" context="#pop" char="=" char1="]"/>
+ <DetectChar attribute="Error" context="#pop" char="]"/>
+ </context>
+
+ <!-- SubShell consumes shell input till ) -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="SubShell" fallthroughContext="Command">
+ <DetectChar attribute="Keyword" context="#pop" char=")" endRegion="subshell"/>
+ <IncludeRules context="Start"/>
+ </context>
+
+ <!-- Assign consumes an expression till EOL or whitespace -->
+ <context attribute="Normal Text" lineEndContext="#pop" name="Assign" fallthroughContext="#pop!Assign2">
+ <DetectChar attribute="Variable" context="#pop!AssignArray" char="("/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Assign2" fallthroughContext="#pop">
+ <DetectChar attribute="Backquote" context="RegularBackq" char="`"/>
+ <IncludeRules context="NormalOption"/>
+ </context>
+
+ <!-- AssignArray consumes everything till ), marking assignments -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="AssignArray" fallthroughContext="NormalOption">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <DetectChar attribute="Comment" context="Comment" char="#"/>
+ <DetectChar attribute="Variable" context="#pop!Assign2" char=")"/>
+ <DetectChar context="AssignArrayKey" char="[" lookAhead="1"/>
+ <DetectChar attribute="Backquote" context="AssignArrayBackq" char="`"/>
+ <AnyChar attribute="Error" context="#stay" String="&symbolseps;"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="AssignArrayKey" fallthroughContext="#pop">
+ <DetectChar attribute="Parameter Expansion Operator" context="Subscript" char="["/>
+ <DetectChar attribute="Variable" context="#pop" char="="/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="AssignArrayBackq" fallthroughContext="Command">
+ <DetectChar attribute="Backquote" context="#pop!NormalOption" char="`"/>
+ <DetectChar attribute="Comment" context="CommentBackq" char="#"/>
+ <IncludeRules context="Start"/>
+ </context>
+
+ <!-- Subscript consumes anything till ], marks as Variable -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="Subscript" fallthroughContext="#pop!Subscript2">
+ <DetectChar attribute="Parameter Expansion Operator" context="#pop" char="]"/>
+ <AnyChar attribute="Decimal" context="#stay" String="0123456789"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Subscript2">
+ <DetectIdentifier attribute="Normal Text"/>
+ <DetectChar attribute="Parameter Expansion Operator" context="#pop" char="]"/>
+ <IncludeRules context="FindWord"/>
+ <IncludeRules context="FindPattern"/>
+ </context>
+
+ <!-- FunctionDef consumes a name, possibly with (), marks as Function -->
+ <context attribute="Function" lineEndContext="#pop" name="FunctionDef" fallthroughContext="#pop">
+ <RegExpr attribute="Function" context="#pop" String="[ &tab;]+&funcname;(?:[ &tab;]*\(\))?"/>
+ </context>
+
+ <!-- Case is called after the case keyword is encoutered. We handle this because of
+ the lonely closing parentheses that would otherwise disturb the expr matching -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="Case">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <WordDetect attribute="Keyword" context="#pop!CaseIn" String="in"/>
+ <IncludeRules context="FindWord"/>
+ <DetectIdentifier attribute="Normal Text" context="#stay"/>
+ </context>
+
+ <!-- CaseIn is called when the construct 'case ... in' has been found. -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="CaseIn" fallthroughContext="CasePattern">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <DetectChar attribute="Keyword" context="CasePattern" char="("/>
+ <DetectChar attribute="Comment" context="Comment" char="#"/>
+ </context>
+ <context attribute="Pattern" lineEndContext="#stay" name="CasePattern">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <DetectChar attribute="Keyword" context="#pop!CaseExpr" char=")" beginRegion="caseexpr"/>
+ <DetectChar context="AssumeEscape" char="\" lookAhead="1"/>
+ <DetectChar attribute="Keyword" context="#stay" char="|"/>
+ <IncludeRules context="FindWord"/>
+ <IncludeRules context="FindPattern"/>
+ <WordDetect attribute="Control Flow" context="#pop#pop" String="esac" endRegion="case"/>
+ <DetectIdentifier attribute="Pattern" context="#stay"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="HereDocINQ" dynamic="true">
- <RegExpr attribute="Redirection" context="HereDocRemainder" String="%1" dynamic="true" />
- <RegExpr attribute="Redirection" context="#pop#pop" String="^\t*%2\b" dynamic="true" column="0"/>
- <IncludeRules context="FindSubstitutions" />
+ <!-- CaseExpr eats shell input till ;; / ;& / ;;& -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="CaseExpr" fallthroughContext="Command">
+ <StringDetect attribute="Control Flow" context="#pop" String=";;&amp;" endRegion="caseexpr"/>
+ <Detect2Chars attribute="Control Flow" context="#pop" char=";" char1=";" endRegion="caseexpr"/>
+ <Detect2Chars attribute="Control Flow" context="#pop" char=";" char1="&amp;" endRegion="caseexpr"/>
+ <WordDetect context="#pop" String="esac" endRegion="caseexpr" lookAhead="1"/>
+ <IncludeRules context="Start"/>
</context>
</contexts>
<itemDatas>
- <itemData name="Normal Text" defStyleNum="dsNormal" />
- <itemData name="Comment" defStyleNum="dsComment" />
- <itemData name="Keyword" defStyleNum="dsKeyword" />
- <itemData name="Control" defStyleNum="dsKeyword" />
- <itemData name="Builtin" defStyleNum="dsBuiltIn" />
- <itemData name="Command" defStyleNum="dsFunction" />
- <itemData name="OtherCommand" defStyleNum="dsExtension" />
- <itemData name="Redirection" defStyleNum="dsOperator" />
- <itemData name="Escape" defStyleNum="dsDataType" />
- <itemData name="String SingleQ" defStyleNum="dsString" />
- <itemData name="String DoubleQ" defStyleNum="dsString" />
- <itemData name="Backquote" defStyleNum="dsKeyword" />
- <itemData name="String Transl." defStyleNum="dsString" />
- <itemData name="String Escape" defStyleNum="dsDataType" />
- <itemData name="Variable" defStyleNum="dsVariable" />
- <itemData name="Expression" defStyleNum="dsOthers" />
- <itemData name="Function" defStyleNum="dsFunction" />
- <itemData name="Path" defStyleNum="dsNormal" />
- <itemData name="Option" defStyleNum="dsNormal" />
- <itemData name="Error" defStyleNum="dsError" />
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="Control" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="Control Flow" defStyleNum="dsControlFlow" spellChecking="false"/>
+ <itemData name="Builtin" defStyleNum="dsBuiltIn" spellChecking="false"/>
+ <itemData name="Command" defStyleNum="dsFunction" spellChecking="false"/>
+ <itemData name="OtherCommand" defStyleNum="dsExtension" spellChecking="false"/>
+ <itemData name="Redirection" defStyleNum="dsOperator" spellChecking="false"/>
+ <itemData name="Escape" defStyleNum="dsDataType" spellChecking="false"/>
+ <itemData name="String SingleQ" defStyleNum="dsString"/>
+ <itemData name="String DoubleQ" defStyleNum="dsString"/>
+ <itemData name="Here Doc" defStyleNum="dsString"/>
+ <itemData name="Backquote" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="String Transl." defStyleNum="dsString"/>
+ <itemData name="String Escape" defStyleNum="dsDataType"/>
+ <itemData name="Variable" defStyleNum="dsVariable" spellChecking="false"/>
+ <itemData name="Dollar Prefix" defStyleNum="dsVariable" spellChecking="false"/>
+ <itemData name="Expression" defStyleNum="dsOthers" spellChecking="false"/>
+ <itemData name="Function" defStyleNum="dsFunction" spellChecking="false"/>
+ <itemData name="Pattern" defStyleNum="dsSpecialString" spellChecking="false"/>
+ <itemData name="Path" defStyleNum="dsNormal" spellChecking="false"/>
+ <itemData name="Glob" defStyleNum="dsPreprocessor" spellChecking="false"/>
+ <itemData name="Option" defStyleNum="dsAttribute" spellChecking="false"/>
+ <itemData name="Hex" defStyleNum="dsBaseN" spellChecking="false"/>
+ <itemData name="Octal" defStyleNum="dsBaseN" spellChecking="false"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal" spellChecking="false"/>
+ <itemData name="Base" defStyleNum="dsDataType" spellChecking="false"/>
+ <itemData name="BaseN" defStyleNum="dsBaseN" spellChecking="false"/>
+ <itemData name="File Descriptor" defStyleNum="dsDecVal" spellChecking="false"/>
+ <itemData name="Parameter Expansion" defStyleNum="dsVariable" spellChecking="false"/>
+ <itemData name="Parameter Expansion Operator" defStyleNum="dsOperator" spellChecking="false"/>
+ <itemData name="Operator" defStyleNum="dsOperator" spellChecking="false"/>
+ <itemData name="Error" defStyleNum="dsError" spellChecking="false"/>
+ <itemData name="Region Marker" defStyleNum="dsRegionMarker" spellChecking="false" />
</itemDatas>
</highlighting>
<general>
<comments>
<comment name="singleLine" start="#"/>
</comments>
- <keywords casesensitive="1" weakDeliminator="^%#[]$._{}:-/" additionalDeliminator="`"/>
+ <keywords casesensitive="1" weakDeliminator="_&weakDeliminatorSymbols;" additionalDeliminator="`"/>
</general>
</language>
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/cmake.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/cmake.xml
index 6d170e4eaa..256619d3ff 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/cmake.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/cmake.xml
@@ -1,38 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd"
+<!DOCTYPE language
[
- <!ENTITY id_re "[_A-Za-z][\-_0-9A-Za-z]*">
+ <!-- NOTE See https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#variable-references -->
+ <!ENTITY var_ref_re "[/\.\+\-_0-9A-Za-z]+">
+ <!-- NOTE See `cmGeneratorExpression::IsValidTargetName` -->
+ <!ENTITY tgt_name_re "[A-Za-z0-9_\.\+\-]+">
]>
<!--
- This file is part of KDE's kate project.
+ This file is part of KDE's kate project.
- Copyright 2004 Alexander Neundorf (neundorf@kde.org)
- Copyright 2005 Dominik Haumann (dhdev@gmx.de)
- Copyright 2007,2008,2013,2014 Matthew Woehlke (mw_triad@users.sourceforge.net)
- Copyright 2013-2015,2017-2018 Alex Turbov (i.zaufi@gmail.com)
+ SPDX-FileCopyrightText: 2004 Alexander Neundorf <neundorf@kde.org>
+ SPDX-FileCopyrightText: 2005 Dominik Haumann <dhdev@gmx.de>
+ SPDX-FileCopyrightText: 2007, 2008, 2013, 2014 Matthew Woehlke <mw_triad@users.sourceforge.net>
+ SPDX-FileCopyrightText: 2013-2015, 2017-2023 Alex Turbov <i.zaufi@gmail.com>
- **********************************************************************
- * This library is free software; you can redistribute it and/or *
- * modify it under the terms of the GNU Lesser General Public *
- * License as published by the Free Software Foundation; either *
- * version 2 of the License, or (at your option) any later version. *
- * *
- * This library 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 *
- * Lesser General Public License for more details. *
- * *
- * You should have received a copy of the GNU Lesser General Public *
- * License along with this library; if not, write to the *
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
- * Boston, MA 02110-1301, USA. *
- **********************************************************************
+ SPDX-License-Identifier: LGPL-2.0-or-later
-->
+<!-- ***** THIS FILE WAS GENERATED BY A SCRIPT - DO NOT EDIT *****
+ cd data/generators
+ # increase version of cmake.xml.tpl then
+ ./generate-cmake-syntax.py cmake.yaml > ../syntax/cmake.xml
+-->
+
<language
name="CMake"
- version="12"
- kateversion="2.4"
+ version="50"
+ kateversion="5.62"
section="Other"
extensions="CMakeLists.txt;*.cmake;*.cmake.in"
style="CMake"
@@ -43,15 +37,19 @@
<highlighting>
<list name="commands">
+ <item>block</item>
<item>break</item>
<item>cmake_host_system_information</item>
+ <item>cmake_language</item>
<item>cmake_minimum_required</item>
<item>cmake_parse_arguments</item>
+ <item>cmake_path</item>
<item>cmake_policy</item>
<item>configure_file</item>
<item>continue</item>
<item>elseif</item>
<item>else</item>
+ <item>endblock</item>
<item>endforeach</item>
<item>endfunction</item>
<item>endif</item>
@@ -102,6 +100,7 @@
<item>add_test</item>
<item>aux_source_directory</item>
<item>build_command</item>
+ <item>cmake_file_api</item>
<item>create_test_sourcelist</item>
<item>define_property</item>
<item>enable_language</item>
@@ -133,6 +132,7 @@
<item>target_link_directories</item>
<item>target_link_libraries</item>
<item>target_link_options</item>
+ <item>target_precompile_headers</item>
<item>target_sources</item>
<item>try_compile</item>
<item>try_run</item>
@@ -150,14 +150,34 @@
<item>ctest_update</item>
<item>ctest_upload</item>
</list>
-
+ <list name="block_nargs">
+ <item>PROPAGATE</item>
+ <item>SCOPE_FOR</item>
+ </list>
+ <list name="block_sargs">
+ <item>POLICIES</item>
+ <item>VARIABLES</item>
+ </list>
<list name="cmake_host_system_information_nargs">
+ <item>ERROR_VARIABLE</item>
<item>QUERY</item>
<item>RESULT</item>
+ <item>SEPARATOR</item>
+ <item>SUBKEYS</item>
+ <item>VALUE</item>
+ <item>VALUE_NAMES</item>
+ <item>VIEW</item>
+ <item>WINDOWS_REGISTRY</item>
</list>
<list name="cmake_host_system_information_sargs">
+ <item>32</item>
+ <item>32_64</item>
+ <item>64</item>
+ <item>64_32</item>
<item>AVAILABLE_PHYSICAL_MEMORY</item>
<item>AVAILABLE_VIRTUAL_MEMORY</item>
+ <item>BOTH</item>
+ <item>DISTRIB_INFO</item>
<item>FQDN</item>
<item>HAS_AMD_3DNOW</item>
<item>HAS_AMD_3DNOW_PLUS</item>
@@ -170,8 +190,10 @@
<item>HAS_SSE2</item>
<item>HAS_SSE_FP</item>
<item>HAS_SSE_MMX</item>
+ <item>HOST</item>
<item>HOSTNAME</item>
<item>IS_64BIT</item>
+ <item>MSYSTEM_PREFIX</item>
<item>NUMBER_OF_LOGICAL_CORES</item>
<item>NUMBER_OF_PHYSICAL_CORES</item>
<item>OS_NAME</item>
@@ -181,9 +203,29 @@
<item>PROCESSOR_DESCRIPTION</item>
<item>PROCESSOR_NAME</item>
<item>PROCESSOR_SERIAL_NUMBER</item>
+ <item>TARGET</item>
<item>TOTAL_PHYSICAL_MEMORY</item>
<item>TOTAL_VIRTUAL_MEMORY</item>
</list>
+ <list name="cmake_language_nargs">
+ <item>CALL</item>
+ <item>CANCEL_CALL</item>
+ <item>CODE</item>
+ <item>DEFER</item>
+ <item>DIRECTORY</item>
+ <item>EVAL</item>
+ <item>GET_CALL</item>
+ <item>GET_CALL_IDS</item>
+ <item>GET_MESSAGE_LOG_LEVEL</item>
+ <item>ID</item>
+ <item>ID_VAR</item>
+ <item>SET_DEPENDENCY_PROVIDER</item>
+ <item>SUPPORTED_METHODS</item>
+ </list>
+ <list name="cmake_language_sargs">
+ <item>FETCHCONTENT_MAKEAVAILABLE_SERIAL</item>
+ <item>FIND_PACKAGE</item>
+ </list>
<list name="cmake_minimum_required_nargs">
<item>FATAL_ERROR</item>
<item>VERSION</item>
@@ -191,6 +233,50 @@
<list name="cmake_parse_arguments_nargs">
<item>PARSE_ARGV</item>
</list>
+ <list name="cmake_path_nargs">
+ <item>ABSOLUTE_PATH</item>
+ <item>APPEND</item>
+ <item>APPEND_STRING</item>
+ <item>BASE_DIRECTORY</item>
+ <item>COMPARE</item>
+ <item>CONVERT</item>
+ <item>EQUAL</item>
+ <item>EXTENSION</item>
+ <item>FILENAME</item>
+ <item>GET</item>
+ <item>HASH</item>
+ <item>HAS_EXTENSION</item>
+ <item>HAS_FILENAME</item>
+ <item>HAS_PARENT_PATH</item>
+ <item>HAS_RELATIVE_PART</item>
+ <item>HAS_ROOT_DIRECTORY</item>
+ <item>HAS_ROOT_NAME</item>
+ <item>HAS_ROOT_PATH</item>
+ <item>HAS_STEM</item>
+ <item>IS_ABSOLUTE</item>
+ <item>IS_PREFIX</item>
+ <item>IS_RELATIVE</item>
+ <item>LAST_ONLY</item>
+ <item>NATIVE_PATH</item>
+ <item>NORMALIZE</item>
+ <item>NORMAL_PATH</item>
+ <item>NOT_EQUAL</item>
+ <item>OUTPUT_VARIABLE</item>
+ <item>PARENT_PATH</item>
+ <item>RELATIVE_PART</item>
+ <item>RELATIVE_PATH</item>
+ <item>REMOVE_EXTENSION</item>
+ <item>REMOVE_FILENAME</item>
+ <item>REPLACE_EXTENSION</item>
+ <item>REPLACE_FILENAME</item>
+ <item>ROOT_DIRECTORY</item>
+ <item>ROOT_NAME</item>
+ <item>ROOT_PATH</item>
+ <item>SET</item>
+ <item>STEM</item>
+ <item>TO_CMAKE_PATH_LIST</item>
+ <item>TO_NATIVE_PATH_LIST</item>
+ </list>
<list name="cmake_policy_nargs">
<item>GET</item>
<item>POP</item>
@@ -206,7 +292,10 @@
<item>@ONLY</item>
<item>COPYONLY</item>
<item>ESCAPE_QUOTES</item>
+ <item>FILE_PERMISSIONS</item>
<item>NEWLINE_STYLE</item>
+ <item>NO_SOURCE_PERMISSIONS</item>
+ <item>USE_SOURCE_PERMISSIONS</item>
</list>
<list name="configure_file_sargs">
<item>CRLF</item>
@@ -217,6 +306,7 @@
</list>
<list name="elseif_nargs">
<item>AND</item>
+ <item>COMMAND</item>
<item>DEFINED</item>
<item>EQUAL</item>
<item>EXISTS</item>
@@ -232,6 +322,7 @@
<item>MATCHES</item>
<item>NOT</item>
<item>OR</item>
+ <item>PATH_EQUAL</item>
<item>POLICY</item>
<item>STREQUAL</item>
<item>STRGREATER</item>
@@ -248,6 +339,10 @@
</list>
<list name="execute_process_nargs">
<item>COMMAND</item>
+ <item>COMMAND_ECHO</item>
+ <item>COMMAND_ERROR_IS_FATAL</item>
+ <item>ECHO_ERROR_VARIABLE</item>
+ <item>ECHO_OUTPUT_VARIABLE</item>
<item>ENCODING</item>
<item>ERROR_FILE</item>
<item>ERROR_QUIET</item>
@@ -265,29 +360,52 @@
</list>
<list name="execute_process_sargs">
<item>ANSI</item>
+ <item>ANY</item>
<item>AUTO</item>
+ <item>LAST</item>
<item>NONE</item>
<item>OEM</item>
+ <item>STDERR</item>
+ <item>STDOUT</item>
<item>UTF8</item>
</list>
<list name="file_nargs">
+ <item>@ONLY</item>
<item>APPEND</item>
+ <item>ARCHIVE_CREATE</item>
+ <item>ARCHIVE_EXTRACT</item>
+ <item>BASE_DIRECTORY</item>
+ <item>BUNDLE_EXECUTABLE</item>
+ <item>CHMOD</item>
+ <item>CHMOD_RECURSE</item>
+ <item>COMPRESSION</item>
+ <item>COMPRESSION_LEVEL</item>
<item>CONDITION</item>
+ <item>CONFIGURE</item>
<item>CONFIGURE_DEPENDS</item>
<item>CONTENT</item>
<item>COPY</item>
+ <item>COPY_FILE</item>
<item>DESTINATION</item>
+ <item>DIRECTORIES</item>
<item>DIRECTORY</item>
<item>DIRECTORY_PERMISSIONS</item>
<item>DOWNLOAD</item>
<item>ENCODING</item>
+ <item>ESCAPE_QUOTES</item>
<item>EXCLUDE</item>
+ <item>EXECUTABLES</item>
+ <item>EXPAND_TILDE</item>
<item>EXPECTED_HASH</item>
<item>EXPECTED_MD5</item>
+ <item>FILES</item>
<item>FILES_MATCHING</item>
<item>FILE_PERMISSIONS</item>
<item>FOLLOW_SYMLINKS</item>
+ <item>FOLLOW_SYMLINK_CHAIN</item>
+ <item>FORMAT</item>
<item>GENERATE</item>
+ <item>GET_RUNTIME_DEPENDENCIES</item>
<item>GLOB</item>
<item>GLOB_RECURSE</item>
<item>GUARD</item>
@@ -298,26 +416,42 @@
<item>INSTALL</item>
<item>LENGTH_MAXIMUM</item>
<item>LENGTH_MINIMUM</item>
+ <item>LIBRARIES</item>
<item>LIMIT</item>
<item>LIMIT_COUNT</item>
<item>LIMIT_INPUT</item>
<item>LIMIT_OUTPUT</item>
<item>LIST_DIRECTORIES</item>
+ <item>LIST_ONLY</item>
<item>LOCK</item>
<item>LOG</item>
<item>MAKE_DIRECTORY</item>
<item>MD5</item>
+ <item>MODULES</item>
+ <item>MTIME</item>
<item>NETRC</item>
<item>NETRC_FILE</item>
<item>NEWLINE_CONSUME</item>
+ <item>NEWLINE_STYLE</item>
<item>NO_HEX_CONVERSION</item>
+ <item>NO_REPLACE</item>
<item>NO_SOURCE_PERMISSIONS</item>
<item>OFFSET</item>
+ <item>ONLY_IF_DIFFERENT</item>
<item>OUTPUT</item>
<item>PATTERN</item>
<item>PERMISSIONS</item>
+ <item>POST_EXCLUDE_FILES</item>
+ <item>POST_EXCLUDE_REGEXES</item>
+ <item>POST_INCLUDE_FILES</item>
+ <item>POST_INCLUDE_REGEXES</item>
+ <item>PRE_EXCLUDE_REGEXES</item>
+ <item>PRE_INCLUDE_REGEXES</item>
+ <item>RANGE_END</item>
+ <item>RANGE_START</item>
<item>READ</item>
<item>READ_SYMLINK</item>
+ <item>REAL_PATH</item>
<item>REGEX</item>
<item>RELATIVE</item>
<item>RELATIVE_PATH</item>
@@ -325,6 +459,8 @@
<item>REMOVE</item>
<item>REMOVE_RECURSE</item>
<item>RENAME</item>
+ <item>RESOLVED_DEPENDENCIES_VAR</item>
+ <item>RESULT</item>
<item>RESULT_VARIABLE</item>
<item>SHA1</item>
<item>SHA224</item>
@@ -339,6 +475,7 @@
<item>SIZE</item>
<item>STATUS</item>
<item>STRINGS</item>
+ <item>TARGET</item>
<item>TIMEOUT</item>
<item>TIMESTAMP</item>
<item>TLS_CAINFO</item>
@@ -347,19 +484,28 @@
<item>TOUCH_NOCREATE</item>
<item>TO_CMAKE_PATH</item>
<item>TO_NATIVE_PATH</item>
+ <item>UNRESOLVED_DEPENDENCIES_VAR</item>
<item>UPLOAD</item>
<item>USERPWD</item>
<item>USE_SOURCE_PERMISSIONS</item>
<item>UTC</item>
+ <item>VERBOSE</item>
<item>WRITE</item>
</list>
<list name="file_sargs">
+ <item>7zip</item>
+ <item>BZip2</item>
+ <item>CRLF</item>
+ <item>DOS</item>
<item>FILE</item>
<item>FUNCTION</item>
<item>GROUP_EXECUTE</item>
<item>GROUP_READ</item>
<item>GROUP_WRITE</item>
+ <item>GZip</item>
<item>IGNORED</item>
+ <item>LF</item>
+ <item>None</item>
<item>OPTIONAL</item>
<item>OWNER_EXECUTE</item>
<item>OWNER_READ</item>
@@ -368,22 +514,33 @@
<item>REQUIRED</item>
<item>SETGID</item>
<item>SETUID</item>
+ <item>UNIX</item>
<item>UTF-16BE</item>
<item>UTF-16LE</item>
<item>UTF-32B</item>
<item>UTF-32LE</item>
<item>UTF-8</item>
+ <item>WIN32</item>
<item>WORLD_EXECUTE</item>
<item>WORLD_READ</item>
<item>WORLD_WRITE</item>
+ <item>XZ</item>
+ <item>Zstd</item>
+ <item>gnutar</item>
+ <item>pax</item>
+ <item>paxr</item>
+ <item>raw</item>
+ <item>zip</item>
</list>
<list name="find_file_nargs">
<item>CMAKE_FIND_ROOT_PATH_BOTH</item>
<item>DOC</item>
<item>HINTS</item>
<item>NAMES</item>
+ <item>NO_CACHE</item>
<item>NO_CMAKE_ENVIRONMENT_PATH</item>
<item>NO_CMAKE_FIND_ROOT_PATH</item>
+ <item>NO_CMAKE_INSTALL_PREFIX</item>
<item>NO_CMAKE_PATH</item>
<item>NO_CMAKE_SYSTEM_PATH</item>
<item>NO_DEFAULT_PATH</item>
@@ -392,6 +549,18 @@
<item>ONLY_CMAKE_FIND_ROOT_PATH</item>
<item>PATHS</item>
<item>PATH_SUFFIXES</item>
+ <item>REGISTRY_VIEW</item>
+ <item>REQUIRED</item>
+ <item>VALIDATOR</item>
+ </list>
+ <list name="find_file_sargs">
+ <item>32</item>
+ <item>32_64</item>
+ <item>64</item>
+ <item>64_32</item>
+ <item>BOTH</item>
+ <item>HOST</item>
+ <item>TARGET</item>
</list>
<list name="find_library_nargs">
<item>CMAKE_FIND_ROOT_PATH_BOTH</item>
@@ -399,8 +568,10 @@
<item>HINTS</item>
<item>NAMES</item>
<item>NAMES_PER_DIR</item>
+ <item>NO_CACHE</item>
<item>NO_CMAKE_ENVIRONMENT_PATH</item>
<item>NO_CMAKE_FIND_ROOT_PATH</item>
+ <item>NO_CMAKE_INSTALL_PREFIX</item>
<item>NO_CMAKE_PATH</item>
<item>NO_CMAKE_SYSTEM_PATH</item>
<item>NO_DEFAULT_PATH</item>
@@ -409,18 +580,24 @@
<item>ONLY_CMAKE_FIND_ROOT_PATH</item>
<item>PATHS</item>
<item>PATH_SUFFIXES</item>
+ <item>REGISTRY_VIEW</item>
+ <item>REQUIRED</item>
+ <item>VALIDATOR</item>
</list>
<list name="find_package_nargs">
+ <item>BYPASS_PROVIDER</item>
<item>CMAKE_FIND_ROOT_PATH_BOTH</item>
<item>COMPONENTS</item>
<item>CONFIG</item>
<item>CONFIGS</item>
<item>EXACT</item>
+ <item>GLOBAL</item>
<item>HINTS</item>
<item>MODULE</item>
<item>NAMES</item>
<item>NO_CMAKE_ENVIRONMENT_PATH</item>
<item>NO_CMAKE_FIND_ROOT_PATH</item>
+ <item>NO_CMAKE_INSTALL_PREFIX</item>
<item>NO_CMAKE_PACKAGE_REGISTRY</item>
<item>NO_CMAKE_PATH</item>
<item>NO_CMAKE_SYSTEM_PACKAGE_REGISTRY</item>
@@ -435,46 +612,22 @@
<item>PATHS</item>
<item>PATH_SUFFIXES</item>
<item>QUIET</item>
+ <item>REGISTRY_VIEW</item>
<item>REQUIRED</item>
</list>
- <list name="find_path_nargs">
- <item>CMAKE_FIND_ROOT_PATH_BOTH</item>
- <item>DOC</item>
- <item>HINTS</item>
- <item>NAMES</item>
- <item>NO_CMAKE_ENVIRONMENT_PATH</item>
- <item>NO_CMAKE_FIND_ROOT_PATH</item>
- <item>NO_CMAKE_PATH</item>
- <item>NO_CMAKE_SYSTEM_PATH</item>
- <item>NO_DEFAULT_PATH</item>
- <item>NO_PACKAGE_ROOT_PATH</item>
- <item>NO_SYSTEM_ENVIRONMENT_PATH</item>
- <item>ONLY_CMAKE_FIND_ROOT_PATH</item>
- <item>PATHS</item>
- <item>PATH_SUFFIXES</item>
- </list>
- <list name="find_program_nargs">
- <item>CMAKE_FIND_ROOT_PATH_BOTH</item>
- <item>DOC</item>
- <item>HINTS</item>
- <item>NAMES</item>
- <item>NAMES_PER_DIR</item>
- <item>NO_CMAKE_ENVIRONMENT_PATH</item>
- <item>NO_CMAKE_FIND_ROOT_PATH</item>
- <item>NO_CMAKE_PATH</item>
- <item>NO_CMAKE_SYSTEM_PATH</item>
- <item>NO_DEFAULT_PATH</item>
- <item>NO_PACKAGE_ROOT_PATH</item>
- <item>NO_SYSTEM_ENVIRONMENT_PATH</item>
- <item>ONLY_CMAKE_FIND_ROOT_PATH</item>
- <item>PATHS</item>
- <item>PATH_SUFFIXES</item>
- </list>
<list name="foreach_nargs">
<item>IN</item>
<item>ITEMS</item>
<item>LISTS</item>
<item>RANGE</item>
+ <item>ZIP_LISTS</item>
+ </list>
+ <list name="get_cmake_property_sargs">
+ <item>CACHE_VARIABLES</item>
+ <item>COMMANDS</item>
+ <item>COMPONENTS</item>
+ <item>MACROS</item>
+ <item>VARIABLES</item>
</list>
<list name="get_directory_property_nargs">
<item>DEFINITION</item>
@@ -507,40 +660,10 @@
<item>SET</item>
<item>SOURCE</item>
<item>TARGET</item>
+ <item>TARGET_DIRECTORY</item>
<item>TEST</item>
<item>VARIABLE</item>
</list>
- <list name="if_nargs">
- <item>AND</item>
- <item>DEFINED</item>
- <item>EQUAL</item>
- <item>EXISTS</item>
- <item>GREATER</item>
- <item>GREATER_EQUAL</item>
- <item>IN_LIST</item>
- <item>IS_ABSOLUTE</item>
- <item>IS_DIRECTORY</item>
- <item>IS_NEWER_THAN</item>
- <item>IS_SYMLINK</item>
- <item>LESS</item>
- <item>LESS_EQUAL</item>
- <item>MATCHES</item>
- <item>NOT</item>
- <item>OR</item>
- <item>POLICY</item>
- <item>STREQUAL</item>
- <item>STRGREATER</item>
- <item>STRGREATER_EQUAL</item>
- <item>STRLESS</item>
- <item>STRLESS_EQUAL</item>
- <item>TARGET</item>
- <item>TEST</item>
- <item>VERSION_EQUAL</item>
- <item>VERSION_GREATER</item>
- <item>VERSION_GREATER_EQUAL</item>
- <item>VERSION_LESS</item>
- <item>VERSION_LESS_EQUAL</item>
- </list>
<list name="include_nargs">
<item>NO_POLICY_SCOPE</item>
<item>OPTIONAL</item>
@@ -567,6 +690,8 @@
<item>LENGTH</item>
<item>ORDER</item>
<item>OUTPUT_VARIABLE</item>
+ <item>POP_BACK</item>
+ <item>POP_FRONT</item>
<item>PREPEND</item>
<item>REGEX</item>
<item>REMOVE_AT</item>
@@ -585,6 +710,7 @@
<item>DESCENDING</item>
<item>FILE_BASENAME</item>
<item>INSENSITIVE</item>
+ <item>NATURAL</item>
<item>SENSITIVE</item>
<item>STRING</item>
</list>
@@ -602,14 +728,27 @@
</list>
<list name="message_nargs">
<item>AUTHOR_WARNING</item>
+ <item>CHECK_FAIL</item>
+ <item>CHECK_PASS</item>
+ <item>CHECK_START</item>
+ <item>CONFIGURE_LOG</item>
+ <item>DEBUG</item>
<item>DEPRECATION</item>
<item>FATAL_ERROR</item>
+ <item>NOTICE</item>
<item>SEND_ERROR</item>
<item>STATUS</item>
+ <item>TRACE</item>
+ <item>VERBOSE</item>
<item>WARNING</item>
</list>
+ <list name="return_nargs">
+ <item>PROPAGATE</item>
+ </list>
<list name="separate_arguments_nargs">
<item>NATIVE_COMMAND</item>
+ <item>PROGRAM</item>
+ <item>SEPARATE_ARGS</item>
<item>UNIX_COMMAND</item>
<item>WINDOWS_COMMAND</item>
</list>
@@ -626,6 +765,7 @@
<item>PROPERTY</item>
<item>SOURCE</item>
<item>TARGET</item>
+ <item>TARGET_DIRECTORY</item>
<item>TEST</item>
<item>VARIABLE</item>
</list>
@@ -651,12 +791,16 @@
<item>CONCAT</item>
<item>CONFIGURE</item>
<item>EQUAL</item>
+ <item>ERROR_VARIABLE</item>
<item>ESCAPE_QUOTES</item>
<item>FIND</item>
<item>GENEX_STRIP</item>
+ <item>GET</item>
<item>GREATER</item>
<item>GREATER_EQUAL</item>
+ <item>HEX</item>
<item>JOIN</item>
+ <item>JSON</item>
<item>LENGTH</item>
<item>LESS</item>
<item>LESS_EQUAL</item>
@@ -664,6 +808,7 @@
<item>MATCH</item>
<item>MATCHALL</item>
<item>MD5</item>
+ <item>MEMBER</item>
<item>NAME</item>
<item>NAMESPACE</item>
<item>NOTEQUAL</item>
@@ -671,8 +816,11 @@
<item>RANDOM</item>
<item>RANDOM_SEED</item>
<item>REGEX</item>
+ <item>REMOVE</item>
+ <item>REPEAT</item>
<item>REPLACE</item>
<item>REVERSE</item>
+ <item>SET</item>
<item>SHA1</item>
<item>SHA224</item>
<item>SHA256</item>
@@ -696,37 +844,6 @@
<item>CACHE</item>
<item>PARENT_SCOPE</item>
</list>
- <list name="while_nargs">
- <item>AND</item>
- <item>DEFINED</item>
- <item>EQUAL</item>
- <item>EXISTS</item>
- <item>GREATER</item>
- <item>GREATER_EQUAL</item>
- <item>IN_LIST</item>
- <item>IS_ABSOLUTE</item>
- <item>IS_DIRECTORY</item>
- <item>IS_NEWER_THAN</item>
- <item>IS_SYMLINK</item>
- <item>LESS</item>
- <item>LESS_EQUAL</item>
- <item>MATCHES</item>
- <item>NOT</item>
- <item>OR</item>
- <item>POLICY</item>
- <item>STREQUAL</item>
- <item>STRGREATER</item>
- <item>STRGREATER_EQUAL</item>
- <item>STRLESS</item>
- <item>STRLESS_EQUAL</item>
- <item>TARGET</item>
- <item>TEST</item>
- <item>VERSION_EQUAL</item>
- <item>VERSION_GREATER</item>
- <item>VERSION_GREATER_EQUAL</item>
- <item>VERSION_LESS</item>
- <item>VERSION_LESS_EQUAL</item>
- </list>
<list name="add_custom_command_nargs">
<item>APPEND</item>
<item>ARGS</item>
@@ -735,8 +852,11 @@
<item>COMMAND_EXPAND_LISTS</item>
<item>COMMENT</item>
<item>DEPENDS</item>
+ <item>DEPENDS_EXPLICIT_ONLY</item>
<item>DEPFILE</item>
<item>IMPLICIT_DEPENDS</item>
+ <item>JOB_POOL</item>
+ <item>JOB_SERVER_AWARE</item>
<item>MAIN_DEPENDENCY</item>
<item>OUTPUT</item>
<item>POST_BUILD</item>
@@ -757,6 +877,8 @@
<item>DEPENDS</item>
<item>DEPFILE</item>
<item>IMPLICIT_DEPENDS</item>
+ <item>JOB_POOL</item>
+ <item>JOB_SERVER_AWARE</item>
<item>SOURCES</item>
<item>USES_TERMINAL</item>
<item>VERBATIM</item>
@@ -784,17 +906,28 @@
</list>
<list name="add_subdirectory_nargs">
<item>EXCLUDE_FROM_ALL</item>
+ <item>SYSTEM</item>
</list>
<list name="add_test_nargs">
<item>COMMAND</item>
+ <item>COMMAND_EXPAND_LISTS</item>
<item>CONFIGURATIONS</item>
<item>NAME</item>
<item>WORKING_DIRECTORY</item>
</list>
<list name="build_command_nargs">
<item>CONFIGURATION</item>
+ <item>PARALLEL_LEVEL</item>
<item>TARGET</item>
</list>
+ <list name="cmake_file_api_nargs">
+ <item>API_VERSION</item>
+ <item>CACHE</item>
+ <item>CMAKEFILES</item>
+ <item>CODEMODEL</item>
+ <item>QUERY</item>
+ <item>TOOLCHAINS</item>
+ </list>
<list name="create_test_sourcelist_nargs">
<item>EXTRA_INCLUDE</item>
<item>FUNCTION</item>
@@ -806,6 +939,7 @@
<item>FULL_DOCS</item>
<item>GLOBAL</item>
<item>INHERITED</item>
+ <item>INITIALIZE_FROM_VARIABLE</item>
<item>PROPERTY</item>
<item>SOURCE</item>
<item>TEST</item>
@@ -815,10 +949,23 @@
<item>OPTIONAL</item>
</list>
<list name="enable_language_sargs">
+ <item>ASM</item>
+ <item>ASM-ATT</item>
+ <item>ASM_MARMASM</item>
+ <item>ASM_MASM</item>
+ <item>ASM_NASM</item>
<item>C</item>
+ <item>CSharp</item>
+ <item>CUDA</item>
<item>CXX</item>
<item>Fortran</item>
+ <item>HIP</item>
+ <item>ISPC</item>
+ <item>Java</item>
+ <item>OBJC</item>
+ <item>OBJCXX</item>
<item>RC</item>
+ <item>Swift</item>
</list>
<list name="export_nargs">
<item>ANDROID_MK</item>
@@ -829,6 +976,13 @@
<item>NAMESPACE</item>
<item>TARGETS</item>
</list>
+ <list name="get_source_file_property_nargs">
+ <item>DIRECTORY</item>
+ <item>TARGET_DIRECTORY</item>
+ </list>
+ <list name="get_test_property_nargs">
+ <item>DIRECTORY</item>
+ </list>
<list name="include_directories_nargs">
<item>AFTER</item>
<item>BEFORE</item>
@@ -846,6 +1000,7 @@
<item>COMPONENT</item>
<item>CONFIGURATIONS</item>
<item>DESTINATION</item>
+ <item>DIRECTORIES</item>
<item>DIRECTORY</item>
<item>DIRECTORY_PERMISSIONS</item>
<item>EXCLUDE</item>
@@ -857,7 +1012,9 @@
<item>FILES</item>
<item>FILES_MATCHING</item>
<item>FILE_PERMISSIONS</item>
+ <item>FILE_SET</item>
<item>FRAMEWORK</item>
+ <item>IMPORTED_RUNTIME_ARTIFACTS</item>
<item>INCLUDES</item>
<item>LIBRARY</item>
<item>MESSAGE_NEVER</item>
@@ -869,6 +1026,12 @@
<item>OPTIONAL</item>
<item>PATTERN</item>
<item>PERMISSIONS</item>
+ <item>POST_EXCLUDE_FILES</item>
+ <item>POST_EXCLUDE_REGEXES</item>
+ <item>POST_INCLUDE_FILES</item>
+ <item>POST_INCLUDE_REGEXES</item>
+ <item>PRE_EXCLUDE_REGEXES</item>
+ <item>PRE_INCLUDE_REGEXES</item>
<item>PRIVATE_HEADER</item>
<item>PROGRAMS</item>
<item>PUBLIC_HEADER</item>
@@ -876,8 +1039,11 @@
<item>RENAME</item>
<item>RESOURCE</item>
<item>RUNTIME</item>
+ <item>RUNTIME_DEPENDENCIES</item>
+ <item>RUNTIME_DEPENDENCY_SET</item>
<item>SCRIPT</item>
<item>TARGETS</item>
+ <item>TYPE</item>
<item>USE_SOURCE_PERMISSIONS</item>
</list>
<list name="install_sargs">
@@ -897,7 +1063,7 @@
<item>AFTER</item>
<item>BEFORE</item>
</list>
- <list name="link_libraries_nargs">
+ <list name="link_libraries_sargs">
<item>debug</item>
<item>general</item>
<item>optimized</item>
@@ -915,20 +1081,30 @@
</list>
<list name="project_sargs">
<item>ASM</item>
+ <item>ASM-ATT</item>
+ <item>ASM_MASM</item>
+ <item>ASM_NASM</item>
<item>C</item>
+ <item>CSharp</item>
<item>CUDA</item>
<item>CXX</item>
<item>Fortran</item>
+ <item>HIP</item>
+ <item>ISPC</item>
+ <item>Java</item>
<item>NONE</item>
+ <item>OBJC</item>
+ <item>OBJCXX</item>
<item>RC</item>
+ <item>Swift</item>
</list>
<list name="set_source_files_properties_nargs">
+ <item>DIRECTORY</item>
<item>PROPERTIES</item>
- </list>
- <list name="set_target_properties_nargs">
- <item>PROPERTIES</item>
+ <item>TARGET_DIRECTORY</item>
</list>
<list name="set_tests_properties_nargs">
+ <item>DIRECTORY</item>
<item>PROPERTIES</item>
</list>
<list name="source_group_nargs">
@@ -942,19 +1118,23 @@
<item>PRIVATE</item>
<item>PUBLIC</item>
</list>
- <list name="target_compile_features_nargs">
- <item>INTERFACE</item>
- <item>PRIVATE</item>
- <item>PUBLIC</item>
- </list>
<list name="target_compile_features_sargs">
<item>c_function_prototypes</item>
<item>c_restrict</item>
<item>c_static_assert</item>
<item>c_std_11</item>
+ <item>c_std_17</item>
+ <item>c_std_23</item>
<item>c_std_90</item>
<item>c_std_99</item>
<item>c_variadic_macros</item>
+ <item>cuda_std_03</item>
+ <item>cuda_std_11</item>
+ <item>cuda_std_14</item>
+ <item>cuda_std_17</item>
+ <item>cuda_std_20</item>
+ <item>cuda_std_23</item>
+ <item>cuda_std_26</item>
<item>cxx_aggregate_default_initializers</item>
<item>cxx_alias_templates</item>
<item>cxx_alignas</item>
@@ -1005,6 +1185,8 @@
<item>cxx_std_14</item>
<item>cxx_std_17</item>
<item>cxx_std_20</item>
+ <item>cxx_std_23</item>
+ <item>cxx_std_26</item>
<item>cxx_std_98</item>
<item>cxx_strong_enums</item>
<item>cxx_template_template_parameters</item>
@@ -1025,43 +1207,51 @@
<item>PUBLIC</item>
</list>
<list name="target_include_directories_nargs">
+ <item>AFTER</item>
<item>BEFORE</item>
<item>INTERFACE</item>
<item>PRIVATE</item>
<item>PUBLIC</item>
<item>SYSTEM</item>
</list>
- <list name="target_link_directories_nargs">
- <item>BEFORE</item>
- <item>INTERFACE</item>
- <item>PRIVATE</item>
- <item>PUBLIC</item>
- </list>
- <list name="target_link_libraries_nargs">
- <item>INTERFACE</item>
- <item>PRIVATE</item>
- <item>PUBLIC</item>
- </list>
- <list name="target_link_options_nargs">
+ <list name="target_precompile_headers_nargs">
<item>INTERFACE</item>
<item>PRIVATE</item>
<item>PUBLIC</item>
+ <item>REUSE_FROM</item>
</list>
<list name="target_sources_nargs">
+ <item>BASE_DIRS</item>
+ <item>FILES</item>
+ <item>FILE_SET</item>
<item>INTERFACE</item>
<item>PRIVATE</item>
<item>PUBLIC</item>
+ <item>TYPE</item>
+ </list>
+ <list name="target_sources_sargs">
+ <item>CXX_MODULES</item>
+ <item>HEADERS</item>
</list>
<list name="try_compile_nargs">
+ <item>BINARY_DIR</item>
<item>CMAKE_FLAGS</item>
<item>COMPILE_DEFINITIONS</item>
<item>COPY_FILE</item>
<item>COPY_FILE_ERROR</item>
<item>LINK_LIBRARIES</item>
<item>LINK_OPTIONS</item>
+ <item>LOG_DESCRIPTION</item>
+ <item>NO_CACHE</item>
+ <item>NO_LOG</item>
<item>OUTPUT_VARIABLE</item>
- <item>RESULT_VAR</item>
+ <item>PROJECT</item>
<item>SOURCES</item>
+ <item>SOURCE_DIR</item>
+ <item>SOURCE_FROM_CONTENT</item>
+ <item>SOURCE_FROM_FILE</item>
+ <item>SOURCE_FROM_VAR</item>
+ <item>TARGET</item>
</list>
<list name="try_run_nargs">
<item>ARGS</item>
@@ -1069,11 +1259,23 @@
<item>COMPILE_DEFINITIONS</item>
<item>COMPILE_OUTPUT_VARIABLE</item>
<item>COMPILE_RESULT_VAR</item>
+ <item>COPY_FILE</item>
+ <item>COPY_FILE_ERROR</item>
<item>LINK_LIBRARIES</item>
<item>LINK_OPTIONS</item>
+ <item>LOG_DESCRIPTION</item>
+ <item>NO_CACHE</item>
+ <item>NO_LOG</item>
<item>OUTPUT_VARIABLE</item>
+ <item>RUN_OUTPUT_STDERR_VARIABLE</item>
+ <item>RUN_OUTPUT_STDOUT_VARIABLE</item>
<item>RUN_OUTPUT_VARIABLE</item>
<item>RUN_RESULT_VAR</item>
+ <item>SOURCES</item>
+ <item>SOURCE_FROM_CONTENT</item>
+ <item>SOURCE_FROM_FILE</item>
+ <item>SOURCE_FROM_VAR</item>
+ <item>WORKING_DIRECTORY</item>
</list>
<list name="ctest_build_nargs">
<item>APPEND</item>
@@ -1135,6 +1337,7 @@
<item>TRACK</item>
</list>
<list name="ctest_submit_nargs">
+ <item>BUILD_ID</item>
<item>CDASH_UPLOAD</item>
<item>CDASH_UPLOAD_TYPE</item>
<item>FILES</item>
@@ -1144,6 +1347,7 @@
<item>RETRY_COUNT</item>
<item>RETRY_DELAY</item>
<item>RETURN_VALUE</item>
+ <item>SUBMIT_URL</item>
</list>
<list name="ctest_test_nargs">
<item>APPEND</item>
@@ -1159,13 +1363,20 @@
<item>INCLUDE_LABEL</item>
<item>PARALLEL_LEVEL</item>
<item>QUIET</item>
+ <item>REPEAT</item>
<item>RETURN_VALUE</item>
<item>SCHEDULE_RANDOM</item>
<item>START</item>
+ <item>STOP_ON_FAILURE</item>
<item>STOP_TIME</item>
<item>STRIDE</item>
<item>TEST_LOAD</item>
</list>
+ <list name="ctest_test_sargs">
+ <item>AFTER_TIMEOUT</item>
+ <item>UNTIL_FAIL</item>
+ <item>UNTIL_PASS</item>
+ </list>
<list name="ctest_update_nargs">
<item>QUIET</item>
<item>RETURN_VALUE</item>
@@ -1176,14 +1387,743 @@
<item>FILES</item>
<item>QUIET</item>
</list>
+ <list name="android_add_test_data_nargs">
+ <item>DEVICE_OBJECT_STORE</item>
+ <item>DEVICE_TEST_DIR</item>
+ <item>FILES</item>
+ <item>FILES_DEST</item>
+ <item>LIBS</item>
+ <item>LIBS_DEST</item>
+ <item>NO_LINK_REGEX</item>
+ </list>
+ <list name="check_c_source_compiles_nargs">
+ <item>FAIL_REGEX</item>
+ </list>
+ <list name="check_fortran_source_compiles_nargs">
+ <item>FAIL_REGEX</item>
+ <item>SRC_EXT</item>
+ </list>
+ <list name="check_fortran_source_runs_nargs">
+ <item>SRC_EXT</item>
+ </list>
+ <list name="check_include_files_nargs">
+ <item>LANGUAGE</item>
+ </list>
+ <list name="check_include_files_sargs">
+ <item>C</item>
+ <item>CXX</item>
+ </list>
+ <list name="check_ipo_supported_nargs">
+ <item>LANGUAGES</item>
+ <item>OUTPUT</item>
+ <item>RESULT</item>
+ </list>
+ <list name="check_ipo_supported_sargs">
+ <item>C</item>
+ <item>CXX</item>
+ <item>Fortran</item>
+ </list>
+ <list name="check_pie_supported_nargs">
+ <item>LANGUAGES</item>
+ <item>OUTPUT_VARIABLE</item>
+ </list>
+ <list name="check_pie_supported_sargs">
+ <item>C</item>
+ <item>CUDA</item>
+ <item>CXX</item>
+ <item>Fortran</item>
+ <item>HIP</item>
+ <item>OBJC</item>
+ <item>OBJCXX</item>
+ </list>
+ <list name="check_type_size_nargs">
+ <item>BUILTIN_TYPES_ONLY</item>
+ <item>LANGUAGE</item>
+ </list>
+ <list name="cmake_add_fortran_subdirectory_nargs">
+ <item>ARCHIVE_DIR</item>
+ <item>CMAKE_COMMAND_LINE</item>
+ <item>LIBRARIES</item>
+ <item>LINK_LIBRARIES</item>
+ <item>LINK_LIBS</item>
+ <item>NO_EXTERNAL_INSTALL</item>
+ <item>PROJECT</item>
+ <item>RUNTIME_DIR</item>
+ </list>
+ <list name="configure_package_config_file_nargs">
+ <item>INSTALL_DESTINATION</item>
+ <item>INSTALL_PREFIX</item>
+ <item>NO_CHECK_REQUIRED_COMPONENTS_MACRO</item>
+ <item>NO_SET_AND_CHECK_MACRO</item>
+ <item>PATH_VARS</item>
+ </list>
+ <list name="write_basic_package_version_file_nargs">
+ <item>ARCH_INDEPENDENT</item>
+ <item>COMPATIBILITY</item>
+ <item>VERSION</item>
+ </list>
+ <list name="write_basic_package_version_file_sargs">
+ <item>AnyNewerVersion</item>
+ <item>ExactVersion</item>
+ <item>SameMajorVersion</item>
+ <item>SameMinorVersion</item>
+ </list>
+ <list name="cmake_print_properties_nargs">
+ <item>CACHE_ENTRIES</item>
+ <item>DIRECTORIES</item>
+ <item>PROPERTIES</item>
+ <item>SOURCES</item>
+ <item>TARGETS</item>
+ <item>TESTS</item>
+ </list>
+ <list name="cmake_push_check_state_nargs">
+ <item>RESET</item>
+ </list>
+ <list name="cpack_add_component_nargs">
+ <item>ARCHIVE_FILE</item>
+ <item>DEPENDS</item>
+ <item>DESCRIPTION</item>
+ <item>DISABLED</item>
+ <item>DISPLAY_NAME</item>
+ <item>DOWNLOADED</item>
+ <item>GROUP</item>
+ <item>HIDDEN</item>
+ <item>INSTALL_TYPES</item>
+ <item>PLIST</item>
+ <item>REQUIRED</item>
+ </list>
+ <list name="cpack_add_component_group_nargs">
+ <item>BOLD_TITLE</item>
+ <item>DESCRIPTION</item>
+ <item>DISPLAY_NAME</item>
+ <item>EXPANDED</item>
+ <item>PARENT_GROUP</item>
+ </list>
+ <list name="cpack_add_install_type_nargs">
+ <item>DISPLAY_NAME</item>
+ </list>
+ <list name="cpack_configure_downloads_nargs">
+ <item>ADD_REMOVE</item>
+ <item>ALL</item>
+ <item>NO_ADD_REMOVE</item>
+ <item>UPLOAD_DIRECTORY</item>
+ </list>
+ <list name="cpack_ifw_configure_component_nargs">
+ <item>AUTO_DEPEND_ON</item>
+ <item>CHECKABLE</item>
+ <item>COMMON</item>
+ <item>DEFAULT</item>
+ <item>DEPENDENCIES</item>
+ <item>DEPENDS</item>
+ <item>DESCRIPTION</item>
+ <item>DISPLAY_NAME</item>
+ <item>ESSENTIAL</item>
+ <item>FORCED_INSTALLATION</item>
+ <item>LICENSES</item>
+ <item>NAME</item>
+ <item>PRIORITY</item>
+ <item>RELEASE_DATE</item>
+ <item>REPLACES</item>
+ <item>REQUIRES_ADMIN_RIGHTS]</item>
+ <item>SCRIPT</item>
+ <item>SORTING_PRIORITY</item>
+ <item>TRANSLATIONS</item>
+ <item>UPDATE_TEXT</item>
+ <item>USER_INTERFACES</item>
+ <item>VERSION</item>
+ <item>VIRTUAL</item>
+ </list>
+ <list name="cpack_ifw_configure_component_group_nargs">
+ <item>AUTO_DEPEND_ON</item>
+ <item>CHECKABLE</item>
+ <item>DEFAULT</item>
+ <item>DEPENDENCIES</item>
+ <item>DEPENDS</item>
+ <item>DESCRIPTION</item>
+ <item>DISPLAY_NAME</item>
+ <item>FORCED_INSTALLATION</item>
+ <item>LICENSES</item>
+ <item>NAME</item>
+ <item>PRIORITY</item>
+ <item>RELEASE_DATE</item>
+ <item>REPLACES</item>
+ <item>REQUIRES_ADMIN_RIGHTS</item>
+ <item>SCRIPT</item>
+ <item>SORTING_PRIORITY</item>
+ <item>TRANSLATIONS</item>
+ <item>UPDATE_TEXT</item>
+ <item>USER_INTERFACES</item>
+ <item>VERSION</item>
+ <item>VIRTUAL</item>
+ </list>
+ <list name="cpack_ifw_add_repository_nargs">
+ <item>DISABLED</item>
+ <item>DISPLAY_NAME</item>
+ <item>PASSWORD</item>
+ <item>URL</item>
+ <item>USERNAME</item>
+ </list>
+ <list name="cpack_ifw_update_repository_nargs">
+ <item>ADD</item>
+ <item>DISPLAY_NAME</item>
+ <item>NEW_URL</item>
+ <item>OLD_URL</item>
+ <item>PASSWORD</item>
+ <item>REMOVE</item>
+ <item>REPLACE</item>
+ <item>URL</item>
+ <item>USERNAME</item>
+ </list>
+ <list name="ctest_coverage_collect_gcov_nargs">
+ <item>BUILD</item>
+ <item>DELETE</item>
+ <item>GCOV_COMMAND</item>
+ <item>GCOV_OPTIONS</item>
+ <item>GLOB</item>
+ <item>QUIET</item>
+ <item>SOURCE</item>
+ <item>TARBALL</item>
+ <item>TARBALL_COMPRESSION</item>
+ </list>
+ <list name="ExternalData_Add_Target_nargs">
+ <item>SHOW_PROGRESS</item>
+ </list>
+ <list name="ExternalProject_Add_nargs">
+ <item>BINARY_DIR</item>
+ <item>BUILD_ALWAYS</item>
+ <item>BUILD_BYPRODUCTS</item>
+ <item>BUILD_COMMAND</item>
+ <item>BUILD_IN_SOURCE</item>
+ <item>BUILD_JOB_SERVER_AWARE</item>
+ <item>CMAKE_ARGS</item>
+ <item>CMAKE_CACHE_ARGS</item>
+ <item>CMAKE_CACHE_DEFAULT_ARGS</item>
+ <item>CMAKE_COMMAND</item>
+ <item>CMAKE_GENERATOR</item>
+ <item>CMAKE_GENERATOR_INSTANCE</item>
+ <item>CMAKE_GENERATOR_PLATFORM</item>
+ <item>CMAKE_GENERATOR_TOOLSET</item>
+ <item>COMMAND</item>
+ <item>CONFIGURE_COMMAND</item>
+ <item>CONFIGURE_HANDLED_BY_BUILD</item>
+ <item>CVS_MODULE</item>
+ <item>CVS_REPOSITORY</item>
+ <item>CVS_TAG</item>
+ <item>DEPENDS</item>
+ <item>DOWNLOAD_COMMAND</item>
+ <item>DOWNLOAD_DIR</item>
+ <item>DOWNLOAD_EXTRACT_TIMESTAMP</item>
+ <item>DOWNLOAD_NAME</item>
+ <item>DOWNLOAD_NO_EXTRACT</item>
+ <item>DOWNLOAD_NO_PROGRESS</item>
+ <item>EXCLUDE_FROM_ALL</item>
+ <item>GIT_CONFIG</item>
+ <item>GIT_PROGRESS</item>
+ <item>GIT_REMOTE_NAME</item>
+ <item>GIT_REMOTE_UPDATE_STRATEGY</item>
+ <item>GIT_REPOSITORY</item>
+ <item>GIT_SHALLOW</item>
+ <item>GIT_SUBMODULES</item>
+ <item>GIT_SUBMODULES_RECURSE</item>
+ <item>GIT_TAG</item>
+ <item>HG_REPOSITORY</item>
+ <item>HG_TAG</item>
+ <item>HTTP_HEADER</item>
+ <item>HTTP_PASSWORD</item>
+ <item>HTTP_USERNAME</item>
+ <item>INACTIVITY_TIMEOUT</item>
+ <item>INDEPENDENT_STEP_TARGETS</item>
+ <item>INSTALL_BYPRODUCTS</item>
+ <item>INSTALL_COMMAND</item>
+ <item>INSTALL_DIR</item>
+ <item>LIST_SEPARATOR</item>
+ <item>LOG_BUILD</item>
+ <item>LOG_CONFIGURE</item>
+ <item>LOG_DIR</item>
+ <item>LOG_DOWNLOAD</item>
+ <item>LOG_INSTALL</item>
+ <item>LOG_MERGED_STDOUTERR</item>
+ <item>LOG_OUTPUT_ON_FAILURE</item>
+ <item>LOG_PATCH</item>
+ <item>LOG_TEST</item>
+ <item>LOG_UPDATE</item>
+ <item>NETRC</item>
+ <item>NETRC_FILE</item>
+ <item>PATCH_COMMAND</item>
+ <item>PREFIX</item>
+ <item>SOURCE_DIR</item>
+ <item>SOURCE_SUBDIR</item>
+ <item>STAMP_DIR</item>
+ <item>STEP_TARGETS</item>
+ <item>SVN_PASSWORD</item>
+ <item>SVN_REPOSITORY</item>
+ <item>SVN_REVISION</item>
+ <item>SVN_TRUST_CERT</item>
+ <item>SVN_USERNAME</item>
+ <item>TEST_AFTER_INSTALL</item>
+ <item>TEST_BEFORE_INSTALL</item>
+ <item>TEST_COMMAND</item>
+ <item>TEST_EXCLUDE_FROM_MAIN</item>
+ <item>TIMEOUT</item>
+ <item>TLS_CAINFO</item>
+ <item>TLS_VERIFY</item>
+ <item>TMP_DIR</item>
+ <item>UPDATE_COMMAND</item>
+ <item>UPDATE_DISCONNECTED</item>
+ <item>URL</item>
+ <item>URL_HASH</item>
+ <item>URL_MD5</item>
+ <item>USES_TERMINAL_BUILD</item>
+ <item>USES_TERMINAL_CONFIGURE</item>
+ <item>USES_TERMINAL_DOWNLOAD</item>
+ <item>USES_TERMINAL_INSTALL</item>
+ <item>USES_TERMINAL_PATCH</item>
+ <item>USES_TERMINAL_TEST</item>
+ <item>USES_TERMINAL_UPDATE</item>
+ </list>
+ <list name="ExternalProject_Add_sargs">
+ <item>CHECKOUT</item>
+ <item>IGNORED</item>
+ <item>OPTIONAL</item>
+ <item>REBASE</item>
+ <item>REBASE_CHECKOUT</item>
+ <item>REQUIRED</item>
+ </list>
+ <list name="ExternalProject_Add_Step_nargs">
+ <item>ALWAYS</item>
+ <item>BYPRODUCTS</item>
+ <item>COMMAND</item>
+ <item>COMMENT</item>
+ <item>DEPENDEES</item>
+ <item>DEPENDERS</item>
+ <item>DEPENDS</item>
+ <item>EXCLUDE_FROM_MAIN</item>
+ <item>INDEPENDENT</item>
+ <item>JOB_SERVER_AWARE</item>
+ <item>LOG</item>
+ <item>USES_TERMINAL</item>
+ <item>WORKING_DIRECTORY</item>
+ </list>
+ <list name="ExternalProject_Add_StepTargets_nargs">
+ <item>NO_DEPENDS</item>
+ </list>
+ <list name="feature_summary_nargs">
+ <item>ALL</item>
+ <item>APPEND</item>
+ <item>DEFAULT_DESCRIPTION</item>
+ <item>DESCRIPTION</item>
+ <item>DISABLED_FEATURES</item>
+ <item>ENABLED_FEATURES</item>
+ <item>FATAL_ON_MISSING_REQUIRED_PACKAGES</item>
+ <item>FILENAME</item>
+ <item>INCLUDE_QUIET_PACKAGES</item>
+ <item>PACKAGES_FOUND</item>
+ <item>PACKAGES_NOT_FOUND</item>
+ <item>QUIET_ON_EMPTY</item>
+ <item>VAR</item>
+ <item>WHAT</item>
+ </list>
+ <list name="set_package_properties_nargs">
+ <item>DESCRIPTION</item>
+ <item>OPTIONAL</item>
+ <item>PROPERTIES</item>
+ <item>PURPOSE</item>
+ <item>RECOMMENDED</item>
+ <item>REQUIRED</item>
+ <item>RUNTIME</item>
+ <item>TYPE</item>
+ <item>URL</item>
+ </list>
+ <list name="FetchContent_Declare_nargs">
+ <item>CVS_MODULE</item>
+ <item>CVS_REPOSITORY</item>
+ <item>CVS_TAG</item>
+ <item>DOWNLOAD_COMMAND</item>
+ <item>DOWNLOAD_NAME</item>
+ <item>DOWNLOAD_NO_EXTRACT</item>
+ <item>DOWNLOAD_NO_PROGRESS</item>
+ <item>EXCLUDE_FROM_ALL</item>
+ <item>FIND_PACKAGE_ARGS</item>
+ <item>GIT_CONFIG</item>
+ <item>GIT_PROGRESS</item>
+ <item>GIT_REMOTE_NAME</item>
+ <item>GIT_REMOTE_UPDATE_STRATEGY</item>
+ <item>GIT_REPOSITORY</item>
+ <item>GIT_SHALLOW</item>
+ <item>GIT_SUBMODULES</item>
+ <item>GIT_SUBMODULES_RECURSE</item>
+ <item>GIT_TAG</item>
+ <item>HG_REPOSITORY</item>
+ <item>HG_TAG</item>
+ <item>HTTP_HEADER</item>
+ <item>HTTP_PASSWORD</item>
+ <item>HTTP_USERNAME</item>
+ <item>INACTIVITY_TIMEOUT</item>
+ <item>NETRC</item>
+ <item>NETRC_FILE</item>
+ <item>OVERRIDE_FIND_PACKAGE</item>
+ <item>PATCH_COMMAND</item>
+ <item>SOURCE_SUBDIR</item>
+ <item>SVN_PASSWORD</item>
+ <item>SVN_REPOSITORY</item>
+ <item>SVN_REVISION</item>
+ <item>SVN_TRUST_CERT</item>
+ <item>SVN_USERNAME</item>
+ <item>SYSTEM</item>
+ <item>TIMEOUT</item>
+ <item>TLS_CAINFO</item>
+ <item>TLS_VERIFY</item>
+ <item>UPDATE_COMMAND</item>
+ <item>UPDATE_DISCONNECTED</item>
+ <item>URL</item>
+ <item>URL_HASH</item>
+ <item>URL_MD5</item>
+ </list>
+ <list name="FetchContent_Populate_nargs">
+ <item>BINARY_DIR</item>
+ <item>CVS_MODULE</item>
+ <item>CVS_REPOSITORY</item>
+ <item>CVS_TAG</item>
+ <item>DOWNLOAD_COMMAND</item>
+ <item>DOWNLOAD_NAME</item>
+ <item>DOWNLOAD_NO_EXTRACT</item>
+ <item>DOWNLOAD_NO_PROGRESS</item>
+ <item>GIT_CONFIG</item>
+ <item>GIT_PROGRESS</item>
+ <item>GIT_REMOTE_NAME</item>
+ <item>GIT_REMOTE_UPDATE_STRATEGY</item>
+ <item>GIT_REPOSITORY</item>
+ <item>GIT_SHALLOW</item>
+ <item>GIT_SUBMODULES</item>
+ <item>GIT_SUBMODULES_RECURSE</item>
+ <item>GIT_TAG</item>
+ <item>HG_REPOSITORY</item>
+ <item>HG_TAG</item>
+ <item>HTTP_HEADER</item>
+ <item>HTTP_PASSWORD</item>
+ <item>HTTP_USERNAME</item>
+ <item>INACTIVITY_TIMEOUT</item>
+ <item>NETRC</item>
+ <item>NETRC_FILE</item>
+ <item>PATCH_COMMAND</item>
+ <item>QUIET</item>
+ <item>SOURCE_DIR</item>
+ <item>SOURCE_SUBDIR</item>
+ <item>SUBBUILD_DIR</item>
+ <item>SVN_PASSWORD</item>
+ <item>SVN_REPOSITORY</item>
+ <item>SVN_REVISION</item>
+ <item>SVN_TRUST_CERT</item>
+ <item>SVN_USERNAME</item>
+ <item>TIMEOUT</item>
+ <item>TLS_CAINFO</item>
+ <item>TLS_VERIFY</item>
+ <item>UPDATE_COMMAND</item>
+ <item>UPDATE_DISCONNECTED</item>
+ <item>URL</item>
+ <item>URL_HASH</item>
+ <item>URL_MD5</item>
+ </list>
+ <list name="FetchContent_GetProperties_nargs">
+ <item>BINARY_DIR</item>
+ <item>POPULATED</item>
+ <item>SOURCE_DIR</item>
+ </list>
+ <list name="find_package_handle_standard_args_nargs">
+ <item>CONFIG_MODE</item>
+ <item>DEFAULT_MSG</item>
+ <item>FAIL_MESSAGE</item>
+ <item>FOUND_VAR</item>
+ <item>HANDLE_COMPONENTS</item>
+ <item>HANDLE_VERSION_RANGE</item>
+ <item>NAME_MISMATCHED</item>
+ <item>REASON_FAILURE_MESSAGE</item>
+ <item>REQUIRED_VARS</item>
+ <item>VERSION_VAR</item>
+ </list>
+ <list name="find_package_check_version_nargs">
+ <item>HANDLE_VERSION_RANGE</item>
+ <item>RESULT_MESSAGE_VARIABLE</item>
+ </list>
+ <list name="FortranCInterface_HEADER_nargs">
+ <item>MACRO_NAMESPACE</item>
+ <item>SYMBOLS</item>
+ <item>SYMBOL_NAMESPACE</item>
+ </list>
+ <list name="FortranCInterface_VERIFY_nargs">
+ <item>CXX</item>
+ <item>QUIET</item>
+ </list>
+ <list name="generate_export_header_nargs">
+ <item>BASE_NAME</item>
+ <item>CUSTOM_CONTENT_FROM_VARIABLE</item>
+ <item>DEFINE_NO_DEPRECATED</item>
+ <item>DEPRECATED_MACRO_NAME</item>
+ <item>EXPORT_FILE_NAME</item>
+ <item>EXPORT_MACRO_NAME</item>
+ <item>INCLUDE_GUARD_NAME</item>
+ <item>NO_DEPRECATED_MACRO_NAME</item>
+ <item>NO_EXPORT_MACRO_NAME</item>
+ <item>PREFIX_NAME</item>
+ <item>STATIC_DEFINE</item>
+ </list>
+ <list name="gtest_add_tests_nargs">
+ <item>EXTRA_ARGS</item>
+ <item>SKIP_DEPENDENCY</item>
+ <item>SOURCES</item>
+ <item>TARGET</item>
+ <item>TEST_LIST</item>
+ <item>TEST_PREFIX</item>
+ <item>TEST_SUFFIX</item>
+ <item>WORKING_DIRECTORY</item>
+ </list>
+ <list name="gtest_discover_tests_nargs">
+ <item>DISCOVERY_MODE</item>
+ <item>DISCOVERY_TIMEOUT</item>
+ <item>EXTRA_ARGS</item>
+ <item>NO_PRETTY_TYPES</item>
+ <item>NO_PRETTY_VALUES</item>
+ <item>PROPERTIES</item>
+ <item>TEST_FILTER</item>
+ <item>TEST_LIST</item>
+ <item>TEST_PREFIX</item>
+ <item>TEST_SUFFIX</item>
+ <item>WORKING_DIRECTORY</item>
+ <item>XML_OUTPUT_DIR</item>
+ </list>
+ <list name="gtest_discover_tests_sargs">
+ <item>POST_BUILD</item>
+ <item>PRE_TEST</item>
+ </list>
+ <list name="add_jar_nargs">
+ <item>BUILD</item>
+ <item>DESTINATION</item>
+ <item>ENTRY_POINT</item>
+ <item>GENERATE_NATIVE_HEADERS</item>
+ <item>INCLUDE_JARS</item>
+ <item>INSTALL</item>
+ <item>MANIFEST</item>
+ <item>NAMESPACE</item>
+ <item>OUTPUT_DIR</item>
+ <item>OUTPUT_NAME</item>
+ <item>RESOURCES</item>
+ <item>SOURCES</item>
+ <item>VERSION</item>
+ </list>
+ <list name="install_jar_nargs">
+ <item>COMPONENT</item>
+ <item>DESTINATION</item>
+ </list>
+ <list name="create_javah_nargs">
+ <item>CLASSES</item>
+ <item>CLASSPATH</item>
+ <item>DEPENDS</item>
+ <item>GENERATED_FILES</item>
+ <item>OUTPUT_DIR</item>
+ <item>OUTPUT_NAME</item>
+ <item>TARGET</item>
+ </list>
+ <list name="install_jar_exports_nargs">
+ <item>COMPONENT</item>
+ <item>DESTINATION</item>
+ <item>FILE</item>
+ <item>NAMESPACE</item>
+ <item>TARGETS</item>
+ </list>
+ <list name="export_jars_nargs">
+ <item>FILE</item>
+ <item>NAMESPACE</item>
+ <item>TARGETS</item>
+ </list>
+ <list name="find_jar_nargs">
+ <item>DOC</item>
+ <item>ENV</item>
+ <item>NAMES</item>
+ <item>PATHS</item>
+ <item>VERSIONS</item>
+ </list>
+ <list name="create_javadoc_nargs">
+ <item>AUTHOR</item>
+ <item>CLASSPATH</item>
+ <item>DOCTITLE</item>
+ <item>FILES</item>
+ <item>INSTALLPATH</item>
+ <item>PACKAGES</item>
+ <item>SOURCEPATH</item>
+ <item>USE</item>
+ <item>VERSION</item>
+ <item>WINDOWTITLE</item>
+ </list>
+ <list name="swig_add_library_nargs">
+ <item>LANGUAGE</item>
+ <item>MODULE</item>
+ <item>NO_PROXY</item>
+ <item>OUTFILE_DIR</item>
+ <item>OUTPUT_DIR</item>
+ <item>SHARED</item>
+ <item>SOURCES</item>
+ <item>STATIC</item>
+ <item>TYPE</item>
+ <item>USE_BUILD_SHARED_LIBS</item>
+ </list>
+ <list name="squish_add_test_nargs">
+ <item>AUT</item>
+ <item>POST_COMMAND</item>
+ <item>PRE_COMMAND</item>
+ <item>SETTINGSGROUP</item>
+ <item>SUITE</item>
+ <item>TEST</item>
+ </list>
+ <list name="bison_target_nargs">
+ <item>COMPILE_FLAGS</item>
+ <item>DEFINES_FILE</item>
+ <item>REPORT_FILE</item>
+ <item>VERBOSE</item>
+ </list>
+ <list name="doxygen_add_docs_nargs">
+ <item>ALL</item>
+ <item>COMMENT</item>
+ <item>CONFIG_FILE</item>
+ <item>USE_STAMP_FILE</item>
+ <item>WORKING_DIRECTORY</item>
+ </list>
+ <list name="env_module_nargs">
+ <item>COMMAND</item>
+ <item>OUTPUT_VARIABLE</item>
+ <item>RESULT_VARIABLE</item>
+ </list>
+ <list name="env_module_swap_nargs">
+ <item>OUTPUT_VARIABLE</item>
+ <item>RESULT_VARIABLE</item>
+ </list>
+ <list name="flex_target_nargs">
+ <item>COMPILE_FLAGS</item>
+ <item>DEFINES_FILE</item>
+ </list>
+ <list name="gettext_create_translations_nargs">
+ <item>ALL</item>
+ </list>
+ <list name="gettext_process_pot_file_nargs">
+ <item>ALL</item>
+ <item>INSTALL_DESTINATION</item>
+ <item>LANGUAGES</item>
+ </list>
+ <list name="gettext_process_po_files_nargs">
+ <item>ALL</item>
+ <item>INSTALL_DESTINATION</item>
+ <item>PO_FILES</item>
+ </list>
+ <list name="matlab_add_unit_test_nargs">
+ <item>ADDITIONAL_PATH</item>
+ <item>CUSTOM_TEST_COMMAND</item>
+ <item>MATLAB_ADDITIONAL_STARTUP_OPTIONS</item>
+ <item>NAME</item>
+ <item>NO_UNITTEST_FRAMEWORK</item>
+ <item>TEST_ARGS</item>
+ <item>TIMEOUT</item>
+ <item>UNITTEST_FILE</item>
+ <item>UNITTEST_PRECOMMAND</item>
+ </list>
+ <list name="matlab_add_mex_nargs">
+ <item>DOCUMENTATION</item>
+ <item>EXCLUDE_FROM_ALL</item>
+ <item>EXECUTABLE</item>
+ <item>LINK_TO</item>
+ <item>MODULE</item>
+ <item>NAME</item>
+ <item>NO_IMPLICIT_LINK_TO_MATLAB_LIBRARIES</item>
+ <item>OUTPUT_NAME</item>
+ <item>R2017b</item>
+ <item>R2018a</item>
+ <item>SHARED</item>
+ <item>SRC</item>
+ </list>
+ <list name="pkg_check_modules_nargs">
+ <item>GLOBAL</item>
+ <item>IMPORTED_TARGET</item>
+ <item>NO_CMAKE_ENVIRONMENT_PATH</item>
+ <item>NO_CMAKE_PATH</item>
+ <item>QUIET</item>
+ <item>REQUIRED</item>
+ <item>STATIC_TARGET</item>
+ </list>
+ <list name="pkg_get_variable_nargs">
+ <item>DEFINE_VARIABLES</item>
+ </list>
+ <list name="protobuf_generate_cpp_nargs">
+ <item>DESCRIPTORS</item>
+ <item>EXPORT_MACRO</item>
+ </list>
+ <list name="Python_add_library_nargs">
+ <item>MODULE</item>
+ <item>SHARED</item>
+ <item>STATIC</item>
+ <item>WITH_SOABI</item>
+ </list>
+ <list name="Subversion_WC_INFO_nargs">
+ <item>IGNORE_SVN_FAILURE</item>
+ </list>
<list name="variables">
+ <item>ALSA_LIBRARY</item>
<item>ANDROID</item>
<item>APPLE</item>
+ <item>ARGC</item>
+ <item>ARGN</item>
+ <item>ARGV</item>
+ <item>ASPELL_DEFINITIONS</item>
+ <item>ASPELL_EXECUTABLE</item>
+ <item>AVIFILE_DEFINITIONS</item>
+ <item>BIBER_COMPILER</item>
+ <item>BIBTEX_COMPILER</item>
+ <item>BISON_EXECUTABLE</item>
+ <item>BLAS_LINKER_FLAGS</item>
+ <item>BLA_F95</item>
+ <item>BLA_PREFER_PKGCONFIG</item>
+ <item>BLA_SIZEOF_INTEGER</item>
+ <item>BLA_STATIC</item>
+ <item>BLA_VENDOR</item>
+ <item>BOOSTROOT</item>
+ <item>BOOST_INCLUDEDIR</item>
+ <item>BOOST_LIBRARYDIR</item>
<item>BORLAND</item>
+ <item>BSD</item>
<item>BUILD_SHARED_LIBS</item>
<item>BUILD_TESTING</item>
+ <item>BU_CHMOD_BUNDLE_ITEMS</item>
+ <item>BZIP2_INCLUDE_DIR</item>
+ <item>BZIP2_NEED_PREFIX</item>
+ <item>Backtrace_HEADER</item>
+ <item>Backtrace_LIBRARY</item>
+ <item>Boost_ADDITIONAL_VERSIONS</item>
+ <item>Boost_ARCHITECTURE</item>
+ <item>Boost_COMPILER</item>
+ <item>Boost_DEBUG</item>
+ <item>Boost_INCLUDE_DIR</item>
+ <item>Boost_LIBRARY_DIR_DEBUG</item>
+ <item>Boost_LIBRARY_DIR_RELEASE</item>
+ <item>Boost_LIB_PREFIX</item>
+ <item>Boost_NAMESPACE</item>
+ <item>Boost_NO_SYSTEM_PATHS</item>
+ <item>Boost_NO_WARN_NEW_VERSIONS</item>
+ <item>Boost_REALPATH</item>
+ <item>Boost_THREADAPI</item>
+ <item>Boost_USE_DEBUG_LIBS</item>
+ <item>Boost_USE_DEBUG_PYTHON</item>
+ <item>Boost_USE_DEBUG_RUNTIME</item>
+ <item>Boost_USE_MULTITHREADED</item>
+ <item>Boost_USE_RELEASE_LIBS</item>
+ <item>Boost_USE_STATIC_LIBS</item>
+ <item>Boost_USE_STLPORT</item>
+ <item>Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS</item>
+ <item>Boost_VERSION_COUNT</item>
+ <item>Boost_VERSION_MACRO</item>
+ <item>CABLE</item>
+ <item>CABLE_INCLUDE_DIR</item>
+ <item>CABLE_TCL_LIBRARY</item>
<item>CMAKE_ABSOLUTE_DESTINATION_FILES</item>
+ <item>CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY</item>
+ <item>CMAKE_AIX_EXPORT_ALL_SYMBOLS</item>
<item>CMAKE_ANDROID_ANT_ADDITIONAL_OPTIONS</item>
<item>CMAKE_ANDROID_API</item>
<item>CMAKE_ANDROID_API_MIN</item>
@@ -1202,6 +2142,7 @@
<item>CMAKE_ANDROID_NDK_DEPRECATED_HEADERS</item>
<item>CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG</item>
<item>CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION</item>
+ <item>CMAKE_ANDROID_NDK_VERSION</item>
<item>CMAKE_ANDROID_PROCESS_MAX</item>
<item>CMAKE_ANDROID_PROGUARD</item>
<item>CMAKE_ANDROID_PROGUARD_CONFIG_PATH</item>
@@ -1211,20 +2152,25 @@
<item>CMAKE_ANDROID_STL_TYPE</item>
<item>CMAKE_ANSI_CXXFLAGS</item>
<item>CMAKE_APPBUNDLE_PATH</item>
+ <item>CMAKE_APPLE_SILICON_PROCESSOR</item>
<item>CMAKE_AR</item>
<item>CMAKE_ARCHIVE_OUTPUT_DIRECTORY</item>
<item>CMAKE_ARGC</item>
- <item>CMAKE_ARGV0</item>
<item>CMAKE_AUTOGEN_ORIGIN_DEPENDS</item>
<item>CMAKE_AUTOGEN_PARALLEL</item>
+ <item>CMAKE_AUTOGEN_USE_SYSTEM_INCLUDE</item>
<item>CMAKE_AUTOGEN_VERBOSE</item>
<item>CMAKE_AUTOMOC</item>
<item>CMAKE_AUTOMOC_DEPEND_FILTERS</item>
+ <item>CMAKE_AUTOMOC_EXECUTABLE</item>
<item>CMAKE_AUTOMOC_MOC_OPTIONS</item>
+ <item>CMAKE_AUTOMOC_PATH_PREFIX</item>
<item>CMAKE_AUTOMOC_RELAXED_MODE</item>
<item>CMAKE_AUTORCC</item>
+ <item>CMAKE_AUTORCC_EXECUTABLE</item>
<item>CMAKE_AUTORCC_OPTIONS</item>
<item>CMAKE_AUTOUIC</item>
+ <item>CMAKE_AUTOUIC_EXECUTABLE</item>
<item>CMAKE_AUTOUIC_OPTIONS</item>
<item>CMAKE_AUTOUIC_SEARCH_PATHS</item>
<item>CMAKE_BACKWARDS_COMPATIBILITY</item>
@@ -1240,10 +2186,12 @@
<item>CMAKE_CACHE_MINOR_VERSION</item>
<item>CMAKE_CACHE_PATCH_VERSION</item>
<item>CMAKE_CFG_INTDIR</item>
+ <item>CMAKE_CLANG_VFS_OVERLAY</item>
<item>CMAKE_CL_64</item>
<item>CMAKE_CODEBLOCKS_COMPILER_ID</item>
<item>CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES</item>
<item>CMAKE_CODELITE_USE_TARGETS</item>
+ <item>CMAKE_COLOR_DIAGNOSTICS</item>
<item>CMAKE_COLOR_MAKEFILE</item>
<item>CMAKE_COMMAND</item>
<item>CMAKE_COMPILER_2005</item>
@@ -1251,51 +2199,80 @@
<item>CMAKE_COMPILER_IS_GNUCXX</item>
<item>CMAKE_COMPILER_IS_GNUG77</item>
<item>CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY</item>
+ <item>CMAKE_COMPILE_WARNING_AS_ERROR</item>
<item>CMAKE_CONFIGURATION_TYPES</item>
<item>CMAKE_CROSSCOMPILING</item>
<item>CMAKE_CROSSCOMPILING_EMULATOR</item>
+ <item>CMAKE_CROSS_CONFIGS</item>
+ <item>CMAKE_CTEST_ARGUMENTS</item>
<item>CMAKE_CTEST_COMMAND</item>
+ <item>CMAKE_CUDA_ARCHITECTURES</item>
+ <item>CMAKE_CUDA_COMPILE_FEATURES</item>
<item>CMAKE_CUDA_EXTENSIONS</item>
+ <item>CMAKE_CUDA_HOST_COMPILER</item>
+ <item>CMAKE_CUDA_LINK_NO_PIE_SUPPORTED</item>
+ <item>CMAKE_CUDA_LINK_PIE_SUPPORTED</item>
+ <item>CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS</item>
+ <item>CMAKE_CUDA_RUNTIME_LIBRARY</item>
<item>CMAKE_CUDA_SEPARABLE_COMPILATION</item>
<item>CMAKE_CUDA_STANDARD</item>
<item>CMAKE_CUDA_STANDARD_REQUIRED</item>
<item>CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES</item>
<item>CMAKE_CURRENT_BINARY_DIR</item>
+ <item>CMAKE_CURRENT_FUNCTION</item>
+ <item>CMAKE_CURRENT_FUNCTION_LIST_DIR</item>
+ <item>CMAKE_CURRENT_FUNCTION_LIST_FILE</item>
+ <item>CMAKE_CURRENT_FUNCTION_LIST_LINE</item>
<item>CMAKE_CURRENT_LIST_DIR</item>
<item>CMAKE_CURRENT_LIST_FILE</item>
<item>CMAKE_CURRENT_LIST_LINE</item>
<item>CMAKE_CURRENT_SOURCE_DIR</item>
<item>CMAKE_CXX_COMPILE_FEATURES</item>
<item>CMAKE_CXX_EXTENSIONS</item>
+ <item>CMAKE_CXX_LINK_NO_PIE_SUPPORTED</item>
+ <item>CMAKE_CXX_LINK_PIE_SUPPORTED</item>
+ <item>CMAKE_CXX_SCAN_FOR_MODULES</item>
<item>CMAKE_CXX_STANDARD</item>
<item>CMAKE_CXX_STANDARD_REQUIRED</item>
<item>CMAKE_C_COMPILE_FEATURES</item>
<item>CMAKE_C_EXTENSIONS</item>
+ <item>CMAKE_C_LINK_NO_PIE_SUPPORTED</item>
+ <item>CMAKE_C_LINK_PIE_SUPPORTED</item>
<item>CMAKE_C_STANDARD</item>
<item>CMAKE_C_STANDARD_REQUIRED</item>
<item>CMAKE_DEBUG_POSTFIX</item>
<item>CMAKE_DEBUG_TARGET_PROPERTIES</item>
+ <item>CMAKE_DEFAULT_BUILD_TYPE</item>
+ <item>CMAKE_DEFAULT_CONFIGS</item>
<item>CMAKE_DEPENDS_IN_PROJECT_ONLY</item>
<item>CMAKE_DIRECTORY_LABELS</item>
+ <item>CMAKE_DISABLE_PRECOMPILE_HEADERS</item>
+ <item>CMAKE_DLL_NAME_WITH_SOVERSION</item>
<item>CMAKE_DL_LIBS</item>
+ <item>CMAKE_DOTNET_SDK</item>
+ <item>CMAKE_DOTNET_TARGET_FRAMEWORK</item>
<item>CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION</item>
<item>CMAKE_ECLIPSE_GENERATE_LINKED_RESOURCES</item>
<item>CMAKE_ECLIPSE_GENERATE_SOURCE_PROJECT</item>
<item>CMAKE_ECLIPSE_MAKE_ARGUMENTS</item>
+ <item>CMAKE_ECLIPSE_RESOURCE_ENCODING</item>
<item>CMAKE_ECLIPSE_VERSION</item>
<item>CMAKE_EDIT_COMMAND</item>
- <item>CMAKE_ENABLE_EXPORTS</item>
<item>CMAKE_ERROR_DEPRECATED</item>
<item>CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION</item>
+ <item>CMAKE_EXECUTABLE_ENABLE_EXPORTS</item>
<item>CMAKE_EXECUTABLE_SUFFIX</item>
+ <item>CMAKE_EXECUTE_PROCESS_COMMAND_ECHO</item>
<item>CMAKE_EXE_LINKER_FLAGS</item>
<item>CMAKE_EXE_LINKER_FLAGS_INIT</item>
<item>CMAKE_EXPORT_COMPILE_COMMANDS</item>
<item>CMAKE_EXPORT_NO_PACKAGE_REGISTRY</item>
+ <item>CMAKE_EXPORT_PACKAGE_REGISTRY</item>
<item>CMAKE_EXTRA_GENERATOR</item>
<item>CMAKE_EXTRA_INCLUDE_FILES</item>
<item>CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES</item>
<item>CMAKE_FIND_APPBUNDLE</item>
+ <item>CMAKE_FIND_DEBUG_MODE</item>
<item>CMAKE_FIND_FRAMEWORK</item>
<item>CMAKE_FIND_FRAMEWORK_EXTRA_LOCATIONS</item>
<item>CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX</item>
@@ -1305,34 +2282,64 @@
<item>CMAKE_FIND_PACKAGE_NAME</item>
<item>CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY</item>
<item>CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY</item>
+ <item>CMAKE_FIND_PACKAGE_PREFER_CONFIG</item>
+ <item>CMAKE_FIND_PACKAGE_REDIRECTS_DIR</item>
<item>CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS</item>
<item>CMAKE_FIND_PACKAGE_SORT_DIRECTION</item>
<item>CMAKE_FIND_PACKAGE_SORT_ORDER</item>
+ <item>CMAKE_FIND_PACKAGE_TARGETS_GLOBAL</item>
<item>CMAKE_FIND_PACKAGE_WARN_NO_MODULE</item>
<item>CMAKE_FIND_ROOT_PATH</item>
<item>CMAKE_FIND_ROOT_PATH_MODE_INCLUDE</item>
<item>CMAKE_FIND_ROOT_PATH_MODE_LIBRARY</item>
<item>CMAKE_FIND_ROOT_PATH_MODE_PACKAGE</item>
<item>CMAKE_FIND_ROOT_PATH_MODE_PROGRAM</item>
+ <item>CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH</item>
+ <item>CMAKE_FIND_USE_CMAKE_PATH</item>
+ <item>CMAKE_FIND_USE_CMAKE_SYSTEM_PATH</item>
+ <item>CMAKE_FIND_USE_INSTALL_PREFIX</item>
+ <item>CMAKE_FIND_USE_PACKAGE_REGISTRY</item>
+ <item>CMAKE_FIND_USE_PACKAGE_ROOT_PATH</item>
+ <item>CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH</item>
+ <item>CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY</item>
<item>CMAKE_FOLDER</item>
+ <item>CMAKE_FRAMEWORK</item>
<item>CMAKE_FRAMEWORK_PATH</item>
<item>CMAKE_Fortran_FORMAT</item>
+ <item>CMAKE_Fortran_LINK_NO_PIE_SUPPORTED</item>
+ <item>CMAKE_Fortran_LINK_PIE_SUPPORTED</item>
<item>CMAKE_Fortran_MODDIR_DEFAULT</item>
<item>CMAKE_Fortran_MODDIR_FLAG</item>
<item>CMAKE_Fortran_MODOUT_FLAG</item>
<item>CMAKE_Fortran_MODULE_DIRECTORY</item>
+ <item>CMAKE_Fortran_PREPROCESS</item>
<item>CMAKE_GENERATOR</item>
<item>CMAKE_GENERATOR_INSTANCE</item>
+ <item>CMAKE_GENERATOR_NO_COMPILER_ENV</item>
<item>CMAKE_GENERATOR_PLATFORM</item>
<item>CMAKE_GENERATOR_TOOLSET</item>
+ <item>CMAKE_GET_OS_RELEASE_FALLBACK_RESULT</item>
+ <item>CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS</item>
+ <item>CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND</item>
+ <item>CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM</item>
+ <item>CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL</item>
<item>CMAKE_GLOBAL_AUTOGEN_TARGET</item>
<item>CMAKE_GLOBAL_AUTOGEN_TARGET_NAME</item>
<item>CMAKE_GLOBAL_AUTORCC_TARGET</item>
<item>CMAKE_GLOBAL_AUTORCC_TARGET_NAME</item>
<item>CMAKE_GNUtoMS</item>
+ <item>CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE</item>
<item>CMAKE_HAS_ANSI_STRING_STREAM</item>
- <item>CMAKE_HOME_DIRECTORY</item>
+ <item>CMAKE_HIP_ARCHITECTURES</item>
+ <item>CMAKE_HIP_EXTENSIONS</item>
+ <item>CMAKE_HIP_LINK_NO_PIE_SUPPORTED</item>
+ <item>CMAKE_HIP_LINK_PIE_SUPPORTED</item>
+ <item>CMAKE_HIP_PLATFORM</item>
+ <item>CMAKE_HIP_STANDARD</item>
+ <item>CMAKE_HIP_STANDARD_REQUIRED</item>
<item>CMAKE_HOST_APPLE</item>
+ <item>CMAKE_HOST_BSD</item>
+ <item>CMAKE_HOST_LINUX</item>
<item>CMAKE_HOST_SOLARIS</item>
<item>CMAKE_HOST_SYSTEM</item>
<item>CMAKE_HOST_SYSTEM_NAME</item>
@@ -1342,6 +2349,7 @@
<item>CMAKE_HOST_WIN32</item>
<item>CMAKE_HP_PTHREADS_INIT</item>
<item>CMAKE_IGNORE_PATH</item>
+ <item>CMAKE_IGNORE_PREFIX_PATH</item>
<item>CMAKE_IMPORT_LIBRARY_PREFIX</item>
<item>CMAKE_IMPORT_LIBRARY_SUFFIX</item>
<item>CMAKE_INCLUDE_CURRENT_DIR</item>
@@ -1387,6 +2395,7 @@
<item>CMAKE_INSTALL_OPENMP_LIBRARIES</item>
<item>CMAKE_INSTALL_PREFIX</item>
<item>CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT</item>
+ <item>CMAKE_INSTALL_REMOVE_ENVIRONMENT_RPATH</item>
<item>CMAKE_INSTALL_RPATH</item>
<item>CMAKE_INSTALL_RPATH_USE_LINK_PATH</item>
<item>CMAKE_INSTALL_RUNSTATEDIR</item>
@@ -1399,9 +2408,14 @@
<item>CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS</item>
<item>CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP</item>
<item>CMAKE_INSTALL_UCRT_LIBRARIES</item>
- <item>CMAKE_INTERNAL_PLATFORM_ABI</item>
<item>CMAKE_INTERPROCEDURAL_OPTIMIZATION</item>
- <item>CMAKE_IOS_INSTALL_COMBINED</item>
+ <item>CMAKE_ISPC_HEADER_DIRECTORY</item>
+ <item>CMAKE_ISPC_HEADER_SUFFIX</item>
+ <item>CMAKE_ISPC_INSTRUCTION_SETS</item>
+ <item>CMAKE_JAR_CLASSES_PREFIX</item>
+ <item>CMAKE_JAVA_COMPILE_FLAGS</item>
+ <item>CMAKE_JAVA_INCLUDE_PATH</item>
+ <item>CMAKE_JNI_TARGET</item>
<item>CMAKE_JOB_POOLS</item>
<item>CMAKE_JOB_POOL_COMPILE</item>
<item>CMAKE_JOB_POOL_LINK</item>
@@ -1412,49 +2426,75 @@
<item>CMAKE_LIBRARY_PATH_FLAG</item>
<item>CMAKE_LINK_DEF_FILE_FLAG</item>
<item>CMAKE_LINK_DEPENDS_NO_SHARED</item>
+ <item>CMAKE_LINK_DEPENDS_USE_LINKER</item>
<item>CMAKE_LINK_DIRECTORIES_BEFORE</item>
<item>CMAKE_LINK_INTERFACE_LIBRARIES</item>
+ <item>CMAKE_LINK_LIBRARIES_ONLY_TARGETS</item>
<item>CMAKE_LINK_LIBRARY_FILE_FLAG</item>
<item>CMAKE_LINK_LIBRARY_FLAG</item>
<item>CMAKE_LINK_LIBRARY_SUFFIX</item>
<item>CMAKE_LINK_SEARCH_END_STATIC</item>
<item>CMAKE_LINK_SEARCH_START_STATIC</item>
<item>CMAKE_LINK_WHAT_YOU_USE</item>
+ <item>CMAKE_LINK_WHAT_YOU_USE_CHECK</item>
<item>CMAKE_MACOSX_BUNDLE</item>
<item>CMAKE_MACOSX_RPATH</item>
<item>CMAKE_MAJOR_VERSION</item>
<item>CMAKE_MAKE_PROGRAM</item>
<item>CMAKE_MATCH_COUNT</item>
<item>CMAKE_MAXIMUM_RECURSION_DEPTH</item>
+ <item>CMAKE_MESSAGE_CONTEXT</item>
+ <item>CMAKE_MESSAGE_CONTEXT_SHOW</item>
+ <item>CMAKE_MESSAGE_INDENT</item>
+ <item>CMAKE_MESSAGE_LOG_LEVEL</item>
<item>CMAKE_MFC_FLAG</item>
<item>CMAKE_MINIMUM_REQUIRED_VERSION</item>
<item>CMAKE_MINOR_VERSION</item>
<item>CMAKE_MODULE_LINKER_FLAGS</item>
<item>CMAKE_MODULE_LINKER_FLAGS_INIT</item>
<item>CMAKE_MODULE_PATH</item>
+ <item>CMAKE_MSVCIDE_RUN_PATH</item>
+ <item>CMAKE_MSVC_DEBUG_INFORMATION_FORMAT</item>
+ <item>CMAKE_MSVC_RUNTIME_LIBRARY</item>
<item>CMAKE_NETRC</item>
<item>CMAKE_NETRC_FILE</item>
<item>CMAKE_NINJA_OUTPUT_PATH_PREFIX</item>
- <item>CMAKE_NOT_USING_CONFIG_FLAGS</item>
<item>CMAKE_NO_ANSI_FOR_SCOPE</item>
<item>CMAKE_NO_ANSI_STREAM_HEADERS</item>
<item>CMAKE_NO_ANSI_STRING_STREAM</item>
<item>CMAKE_NO_BUILTIN_CHRPATH</item>
<item>CMAKE_NO_STD_NAMESPACE</item>
<item>CMAKE_NO_SYSTEM_FROM_IMPORTED</item>
+ <item>CMAKE_OBJCXX_EXTENSIONS</item>
+ <item>CMAKE_OBJCXX_LINK_NO_PIE_SUPPORTED</item>
+ <item>CMAKE_OBJCXX_LINK_PIE_SUPPORTED</item>
+ <item>CMAKE_OBJCXX_STANDARD</item>
+ <item>CMAKE_OBJCXX_STANDARD_REQUIRED</item>
+ <item>CMAKE_OBJC_EXTENSIONS</item>
+ <item>CMAKE_OBJC_LINK_NO_PIE_SUPPORTED</item>
+ <item>CMAKE_OBJC_LINK_PIE_SUPPORTED</item>
+ <item>CMAKE_OBJC_STANDARD</item>
+ <item>CMAKE_OBJC_STANDARD_REQUIRED</item>
<item>CMAKE_OBJECT_PATH_MAX</item>
+ <item>CMAKE_OPTIMIZE_DEPENDENCIES</item>
<item>CMAKE_OSX_ARCHITECTURES</item>
<item>CMAKE_OSX_DEPLOYMENT_TARGET</item>
<item>CMAKE_OSX_SYSROOT</item>
<item>CMAKE_PARENT_LIST_FILE</item>
<item>CMAKE_PATCH_VERSION</item>
+ <item>CMAKE_PCH_INSTANTIATE_TEMPLATES</item>
+ <item>CMAKE_PCH_WARN_INVALID</item>
<item>CMAKE_PDB_OUTPUT_DIRECTORY</item>
+ <item>CMAKE_PLATFORM_NO_VERSIONED_SONAME</item>
<item>CMAKE_POSITION_INDEPENDENT_CODE</item>
<item>CMAKE_PREFIX_PATH</item>
<item>CMAKE_PROGRAM_PATH</item>
<item>CMAKE_PROJECT_DESCRIPTION</item>
<item>CMAKE_PROJECT_HOMEPAGE_URL</item>
+ <item>CMAKE_PROJECT_INCLUDE</item>
+ <item>CMAKE_PROJECT_INCLUDE_BEFORE</item>
<item>CMAKE_PROJECT_NAME</item>
+ <item>CMAKE_PROJECT_TOP_LEVEL_INCLUDES</item>
<item>CMAKE_PROJECT_VERSION</item>
<item>CMAKE_PROJECT_VERSION_MAJOR</item>
<item>CMAKE_PROJECT_VERSION_MINOR</item>
@@ -1465,10 +2505,12 @@
<item>CMAKE_REQUIRED_FLAGS</item>
<item>CMAKE_REQUIRED_INCLUDES</item>
<item>CMAKE_REQUIRED_LIBRARIES</item>
+ <item>CMAKE_REQUIRED_LINK_OPTIONS</item>
<item>CMAKE_REQUIRED_QUIET</item>
<item>CMAKE_ROOT</item>
<item>CMAKE_RUNTIME_OUTPUT_DIRECTORY</item>
<item>CMAKE_SCRIPT_MODE_FILE</item>
+ <item>CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS</item>
<item>CMAKE_SHARED_LIBRARY_PREFIX</item>
<item>CMAKE_SHARED_LIBRARY_SUFFIX</item>
<item>CMAKE_SHARED_LINKER_FLAGS</item>
@@ -1489,8 +2531,6 @@
<item>CMAKE_STATIC_LINKER_FLAGS_INIT</item>
<item>CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS</item>
<item>CMAKE_SUBLIME_TEXT_2_EXCLUDE_BUILD_TREE</item>
- <item>CMAKE_SUPPRESS_DEVELOPER_ERRORS</item>
- <item>CMAKE_SUPPRESS_DEVELOPER_WARNINGS</item>
<item>CMAKE_SUPPRESS_REGENERATION</item>
<item>CMAKE_SWIG_FLAGS</item>
<item>CMAKE_SWIG_OUTDIR</item>
@@ -1501,6 +2541,7 @@
<item>CMAKE_SYSTEM_APPBUNDLE_PATH</item>
<item>CMAKE_SYSTEM_FRAMEWORK_PATH</item>
<item>CMAKE_SYSTEM_IGNORE_PATH</item>
+ <item>CMAKE_SYSTEM_IGNORE_PREFIX_PATH</item>
<item>CMAKE_SYSTEM_INCLUDE_PATH</item>
<item>CMAKE_SYSTEM_LIBRARY_PATH</item>
<item>CMAKE_SYSTEM_NAME</item>
@@ -1509,32 +2550,46 @@
<item>CMAKE_SYSTEM_PROGRAM_PATH</item>
<item>CMAKE_SYSTEM_VERSION</item>
<item>CMAKE_Swift_LANGUAGE_VERSION</item>
+ <item>CMAKE_Swift_MODULE_DIRECTORY</item>
+ <item>CMAKE_TASKING_TOOLSET</item>
<item>CMAKE_THREAD_LIBS_INIT</item>
<item>CMAKE_THREAD_PREFER_PTHREAD</item>
<item>CMAKE_TOOLCHAIN_FILE</item>
<item>CMAKE_TRY_COMPILE_CONFIGURATION</item>
+ <item>CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES</item>
<item>CMAKE_TRY_COMPILE_PLATFORM_VARIABLES</item>
<item>CMAKE_TRY_COMPILE_TARGET_TYPE</item>
<item>CMAKE_TWEAK_VERSION</item>
+ <item>CMAKE_UNITY_BUILD</item>
+ <item>CMAKE_UNITY_BUILD_BATCH_SIZE</item>
<item>CMAKE_USER_MAKE_RULES_OVERRIDE</item>
<item>CMAKE_USE_PTHREADS_INIT</item>
<item>CMAKE_USE_RELATIVE_PATHS</item>
<item>CMAKE_USE_SPROC_INIT</item>
<item>CMAKE_USE_WIN32_THREADS_INIT</item>
<item>CMAKE_VERBOSE_MAKEFILE</item>
+ <item>CMAKE_VERIFY_INTERFACE_HEADER_SETS</item>
<item>CMAKE_VERSION</item>
<item>CMAKE_VISIBILITY_INLINES_HIDDEN</item>
+ <item>CMAKE_VS_DEBUGGER_COMMAND</item>
+ <item>CMAKE_VS_DEBUGGER_COMMAND_ARGUMENTS</item>
+ <item>CMAKE_VS_DEBUGGER_ENVIRONMENT</item>
+ <item>CMAKE_VS_DEBUGGER_WORKING_DIRECTORY</item>
<item>CMAKE_VS_DEVENV_COMMAND</item>
<item>CMAKE_VS_GLOBALS</item>
<item>CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD</item>
<item>CMAKE_VS_INCLUDE_PACKAGE_TO_DEFAULT_BUILD</item>
- <item>CMAKE_VS_INTEL_Fortran_PROJECT_VERSION</item>
+ <item>CMAKE_VS_JUST_MY_CODE_DEBUGGING</item>
<item>CMAKE_VS_MSBUILD_COMMAND</item>
+ <item>CMAKE_VS_NUGET_PACKAGE_RESTORE</item>
<item>CMAKE_VS_NsightTegra_VERSION</item>
<item>CMAKE_VS_PLATFORM_NAME</item>
+ <item>CMAKE_VS_PLATFORM_NAME_DEFAULT</item>
<item>CMAKE_VS_PLATFORM_TOOLSET</item>
<item>CMAKE_VS_PLATFORM_TOOLSET_CUDA</item>
+ <item>CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR</item>
<item>CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE</item>
+ <item>CMAKE_VS_PLATFORM_TOOLSET_VERSION</item>
<item>CMAKE_VS_SDK_EXCLUDE_DIRECTORIES</item>
<item>CMAKE_VS_SDK_EXECUTABLE_DIRECTORIES</item>
<item>CMAKE_VS_SDK_INCLUDE_DIRECTORIES</item>
@@ -1542,20 +2597,37 @@
<item>CMAKE_VS_SDK_LIBRARY_WINRT_DIRECTORIES</item>
<item>CMAKE_VS_SDK_REFERENCE_DIRECTORIES</item>
<item>CMAKE_VS_SDK_SOURCE_DIRECTORIES</item>
+ <item>CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER</item>
+ <item>CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION</item>
+ <item>CMAKE_VS_TARGET_FRAMEWORK_VERSION</item>
+ <item>CMAKE_VS_VERSION_BUILD_NUMBER</item>
+ <item>CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION</item>
<item>CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION</item>
+ <item>CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM</item>
+ <item>CMAKE_VS_WINRT_BY_DEFAULT</item>
<item>CMAKE_WARN_DEPRECATED</item>
<item>CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION</item>
+ <item>CMAKE_WATCOM_RUNTIME_LIBRARY</item>
<item>CMAKE_WIN32_EXECUTABLE</item>
<item>CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS</item>
+ <item>CMAKE_XCODE_BUILD_SYSTEM</item>
<item>CMAKE_XCODE_GENERATE_SCHEME</item>
<item>CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY</item>
+ <item>CMAKE_XCODE_LINK_BUILD_PHASE_MODE</item>
<item>CMAKE_XCODE_PLATFORM_TOOLSET</item>
<item>CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER</item>
<item>CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN</item>
+ <item>CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING</item>
<item>CMAKE_XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER</item>
<item>CMAKE_XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS</item>
<item>CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE</item>
+ <item>CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION</item>
+ <item>CMAKE_XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE</item>
+ <item>CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION</item>
+ <item>CMAKE_XCODE_SCHEME_ENVIRONMENT</item>
<item>CMAKE_XCODE_SCHEME_GUARD_MALLOC</item>
+ <item>CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION</item>
+ <item>CMAKE_XCODE_SCHEME_LAUNCH_MODE</item>
<item>CMAKE_XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP</item>
<item>CMAKE_XCODE_SCHEME_MALLOC_GUARD_EDGES</item>
<item>CMAKE_XCODE_SCHEME_MALLOC_SCRIBBLE</item>
@@ -1564,10 +2636,14 @@
<item>CMAKE_XCODE_SCHEME_THREAD_SANITIZER_STOP</item>
<item>CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER</item>
<item>CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP</item>
+ <item>CMAKE_XCODE_SCHEME_WORKING_DIRECTORY</item>
<item>CMAKE_XCODE_SCHEME_ZOMBIE_OBJECTS</item>
+ <item>CMAKE_XCODE_XCCONFIG</item>
<item>CPACK_ABSOLUTE_DESTINATION_FILES</item>
<item>CPACK_ARCHIVE_COMPONENT_INSTALL</item>
+ <item>CPACK_ARCHIVE_FILE_EXTENSION</item>
<item>CPACK_ARCHIVE_FILE_NAME</item>
+ <item>CPACK_ARCHIVE_THREADS</item>
<item>CPACK_BUILD_SOURCE_DIRS</item>
<item>CPACK_BUNDLE_APPLE_CERT_APP</item>
<item>CPACK_BUNDLE_APPLE_CODESIGN_FILES</item>
@@ -1588,6 +2664,7 @@
<item>CPACK_COMPONENTS_GROUPING</item>
<item>CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY</item>
<item>CPACK_CREATE_DESKTOP_LINKS</item>
+ <item>CPACK_CUSTOM_INSTALL_VARIABLES</item>
<item>CPACK_CYGWIN_BUILD_SCRIPT</item>
<item>CPACK_CYGWIN_PATCH_FILE</item>
<item>CPACK_CYGWIN_PATCH_NUMBER</item>
@@ -1619,6 +2696,7 @@
<item>CPACK_DEBIAN_PACKAGE_REPLACES</item>
<item>CPACK_DEBIAN_PACKAGE_SECTION</item>
<item>CPACK_DEBIAN_PACKAGE_SHLIBDEPS</item>
+ <item>CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS</item>
<item>CPACK_DEBIAN_PACKAGE_SOURCE</item>
<item>CPACK_DEBIAN_PACKAGE_SUGGESTS</item>
<item>CPACK_DEBIAN_PACKAGE_VERSION</item>
@@ -1627,16 +2705,31 @@
<item>CPACK_DMG_DISABLE_APPLICATIONS_SYMLINK</item>
<item>CPACK_DMG_DS_STORE</item>
<item>CPACK_DMG_DS_STORE_SETUP_SCRIPT</item>
+ <item>CPACK_DMG_FILESYSTEM</item>
<item>CPACK_DMG_FORMAT</item>
<item>CPACK_DMG_SLA_DIR</item>
<item>CPACK_DMG_SLA_LANGUAGES</item>
+ <item>CPACK_DMG_SLA_USE_RESOURCE_FILE_LICENSE</item>
<item>CPACK_DMG_VOLUME_NAME</item>
<item>CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION</item>
- <item>CPACK_EXT_ENABLE_STAGING</item>
- <item>CPACK_EXT_PACKAGE_SCRIPT</item>
- <item>CPACK_EXT_REQUESTED_VERSIONS</item>
+ <item>CPACK_EXTERNAL_BUILT_PACKAGES</item>
+ <item>CPACK_EXTERNAL_ENABLE_STAGING</item>
+ <item>CPACK_EXTERNAL_PACKAGE_SCRIPT</item>
+ <item>CPACK_EXTERNAL_REQUESTED_VERSIONS</item>
+ <item>CPACK_FREEBSD_PACKAGE_CATEGORIES</item>
+ <item>CPACK_FREEBSD_PACKAGE_COMMENT</item>
+ <item>CPACK_FREEBSD_PACKAGE_DEPS</item>
+ <item>CPACK_FREEBSD_PACKAGE_DESCRIPTION</item>
+ <item>CPACK_FREEBSD_PACKAGE_LICENSE</item>
+ <item>CPACK_FREEBSD_PACKAGE_LICENSE_LOGIC</item>
+ <item>CPACK_FREEBSD_PACKAGE_MAINTAINER</item>
+ <item>CPACK_FREEBSD_PACKAGE_NAME</item>
+ <item>CPACK_FREEBSD_PACKAGE_ORIGIN</item>
+ <item>CPACK_FREEBSD_PACKAGE_WWW</item>
<item>CPACK_GENERATOR</item>
<item>CPACK_IFW_ADMIN_TARGET_DIRECTORY</item>
+ <item>CPACK_IFW_ARCHIVE_COMPRESSION</item>
+ <item>CPACK_IFW_ARCHIVE_FORMAT</item>
<item>CPACK_IFW_BINARYCREATOR_EXECUTABLE</item>
<item>CPACK_IFW_DEVTOOL_EXECUTABLE</item>
<item>CPACK_IFW_DOWNLOAD_ALL</item>
@@ -1648,22 +2741,30 @@
<item>CPACK_IFW_PACKAGE_BACKGROUND</item>
<item>CPACK_IFW_PACKAGE_BANNER</item>
<item>CPACK_IFW_PACKAGE_CONTROL_SCRIPT</item>
+ <item>CPACK_IFW_PACKAGE_DISABLE_COMMAND_LINE_INTERFACE</item>
<item>CPACK_IFW_PACKAGE_GROUP</item>
<item>CPACK_IFW_PACKAGE_ICON</item>
<item>CPACK_IFW_PACKAGE_LOGO</item>
<item>CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_INI_FILE</item>
<item>CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_NAME</item>
<item>CPACK_IFW_PACKAGE_NAME</item>
+ <item>CPACK_IFW_PACKAGE_PRODUCT_IMAGES</item>
<item>CPACK_IFW_PACKAGE_PUBLISHER</item>
<item>CPACK_IFW_PACKAGE_REMOVE_TARGET_DIR</item>
<item>CPACK_IFW_PACKAGE_RESOURCES</item>
+ <item>CPACK_IFW_PACKAGE_RUN_PROGRAM</item>
+ <item>CPACK_IFW_PACKAGE_RUN_PROGRAM_ARGUMENTS</item>
+ <item>CPACK_IFW_PACKAGE_RUN_PROGRAM_DESCRIPTION</item>
+ <item>CPACK_IFW_PACKAGE_SIGNING_IDENTITY</item>
<item>CPACK_IFW_PACKAGE_START_MENU_DIRECTORY</item>
+ <item>CPACK_IFW_PACKAGE_STYLE_SHEET</item>
<item>CPACK_IFW_PACKAGE_TITLE</item>
<item>CPACK_IFW_PACKAGE_TITLE_COLOR</item>
<item>CPACK_IFW_PACKAGE_WATERMARK</item>
<item>CPACK_IFW_PACKAGE_WINDOW_ICON</item>
<item>CPACK_IFW_PACKAGE_WIZARD_DEFAULT_HEIGHT</item>
<item>CPACK_IFW_PACKAGE_WIZARD_DEFAULT_WIDTH</item>
+ <item>CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST</item>
<item>CPACK_IFW_PACKAGE_WIZARD_STYLE</item>
<item>CPACK_IFW_PRODUCT_URL</item>
<item>CPACK_IFW_REPOGEN_EXECUTABLE</item>
@@ -1673,35 +2774,68 @@
<item>CPACK_IFW_TARGET_DIRECTORY</item>
<item>CPACK_IFW_VERBOSE</item>
<item>CPACK_INCLUDE_TOPLEVEL_DIRECTORY</item>
+ <item>CPACK_INNOSETUP_ALLOW_CUSTOM_DIRECTORY</item>
+ <item>CPACK_INNOSETUP_ARCHITECTURE</item>
+ <item>CPACK_INNOSETUP_CODE_FILES</item>
+ <item>CPACK_INNOSETUP_CREATE_UNINSTALL_LINK</item>
+ <item>CPACK_INNOSETUP_CUSTOM_INSTALL_INSTRUCTIONS</item>
+ <item>CPACK_INNOSETUP_EXECUTABLE</item>
+ <item>CPACK_INNOSETUP_EXECUTABLE_ARGUMENTS</item>
+ <item>CPACK_INNOSETUP_EXTRA_SCRIPTS</item>
+ <item>CPACK_INNOSETUP_ICON_FILE</item>
+ <item>CPACK_INNOSETUP_IGNORE_LICENSE_PAGE</item>
+ <item>CPACK_INNOSETUP_IGNORE_README_PAGE</item>
+ <item>CPACK_INNOSETUP_INSTALL_ROOT</item>
+ <item>CPACK_INNOSETUP_LANGUAGES</item>
+ <item>CPACK_INNOSETUP_MENU_LINKS</item>
+ <item>CPACK_INNOSETUP_PASSWORD</item>
+ <item>CPACK_INNOSETUP_PROGRAM_MENU_FOLDER</item>
+ <item>CPACK_INNOSETUP_RUN_EXECUTABLES</item>
+ <item>CPACK_INNOSETUP_USE_CMAKE_BOOL_FORMAT</item>
+ <item>CPACK_INNOSETUP_USE_MODERN_WIZARD</item>
+ <item>CPACK_INNOSETUP_VERIFY_DOWNLOADS</item>
<item>CPACK_INSTALLED_DIRECTORIES</item>
<item>CPACK_INSTALL_CMAKE_PROJECTS</item>
<item>CPACK_INSTALL_COMMANDS</item>
<item>CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS</item>
- <item>CPACK_INSTALL_SCRIPT</item>
+ <item>CPACK_INSTALL_SCRIPTS</item>
<item>CPACK_MONOLITHIC_INSTALL</item>
+ <item>CPACK_NSIS_BRANDING_TEXT</item>
+ <item>CPACK_NSIS_BRANDING_TEXT_TRIM_POSITION</item>
<item>CPACK_NSIS_COMPRESSOR</item>
<item>CPACK_NSIS_CONTACT</item>
<item>CPACK_NSIS_CREATE_ICONS_EXTRA</item>
<item>CPACK_NSIS_DELETE_ICONS_EXTRA</item>
<item>CPACK_NSIS_DISPLAY_NAME</item>
<item>CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL</item>
+ <item>CPACK_NSIS_EXECUTABLE</item>
<item>CPACK_NSIS_EXECUTABLES_DIRECTORY</item>
+ <item>CPACK_NSIS_EXECUTABLE_POST_ARGUMENTS</item>
+ <item>CPACK_NSIS_EXECUTABLE_PRE_ARGUMENTS</item>
<item>CPACK_NSIS_EXTRA_INSTALL_COMMANDS</item>
<item>CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS</item>
<item>CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS</item>
+ <item>CPACK_NSIS_FINISH_TITLE</item>
+ <item>CPACK_NSIS_FINISH_TITLE_3LINES</item>
<item>CPACK_NSIS_HELP_LINK</item>
+ <item>CPACK_NSIS_IGNORE_LICENSE_PAGE</item>
<item>CPACK_NSIS_INSTALLED_ICON_NAME</item>
<item>CPACK_NSIS_INSTALLER_MUI_ICON_CODE</item>
<item>CPACK_NSIS_INSTALL_ROOT</item>
+ <item>CPACK_NSIS_MANIFEST_DPI_AWARE</item>
<item>CPACK_NSIS_MENU_LINKS</item>
<item>CPACK_NSIS_MODIFY_PATH</item>
<item>CPACK_NSIS_MUI_FINISHPAGE_RUN</item>
+ <item>CPACK_NSIS_MUI_HEADERIMAGE</item>
<item>CPACK_NSIS_MUI_ICON</item>
<item>CPACK_NSIS_MUI_UNIICON</item>
<item>CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP</item>
<item>CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP</item>
<item>CPACK_NSIS_PACKAGE_NAME</item>
+ <item>CPACK_NSIS_UNINSTALL_NAME</item>
<item>CPACK_NSIS_URL_INFO_ABOUT</item>
+ <item>CPACK_NSIS_WELCOME_TITLE</item>
+ <item>CPACK_NSIS_WELCOME_TITLE_3LINES</item>
<item>CPACK_NUGET_COMPONENT_INSTALL</item>
<item>CPACK_NUGET_PACKAGE_AUTHORS</item>
<item>CPACK_NUGET_PACKAGE_COPYRIGHT</item>
@@ -1710,23 +2844,28 @@
<item>CPACK_NUGET_PACKAGE_DESCRIPTION</item>
<item>CPACK_NUGET_PACKAGE_DESCRIPTION_SUMMARY</item>
<item>CPACK_NUGET_PACKAGE_HOMEPAGE_URL</item>
+ <item>CPACK_NUGET_PACKAGE_ICON</item>
<item>CPACK_NUGET_PACKAGE_ICONURL</item>
+ <item>CPACK_NUGET_PACKAGE_LANGUAGE</item>
<item>CPACK_NUGET_PACKAGE_LICENSEURL</item>
+ <item>CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION</item>
+ <item>CPACK_NUGET_PACKAGE_LICENSE_FILE_NAME</item>
<item>CPACK_NUGET_PACKAGE_NAME</item>
<item>CPACK_NUGET_PACKAGE_OWNERS</item>
<item>CPACK_NUGET_PACKAGE_RELEASE_NOTES</item>
<item>CPACK_NUGET_PACKAGE_TAGS</item>
<item>CPACK_NUGET_PACKAGE_TITLE</item>
<item>CPACK_NUGET_PACKAGE_VERSION</item>
- <item>CPACK_OSX_PACKAGE_VERSION</item>
+ <item>CPACK_OBJCOPY_EXECUTABLE</item>
+ <item>CPACK_OBJDUMP_EXECUTABLE</item>
<item>CPACK_OUTPUT_CONFIG_FILE</item>
<item>CPACK_PACKAGE_CHECKSUM</item>
- <item>CPACK_PACKAGE_CONTACT</item>
<item>CPACK_PACKAGE_DESCRIPTION</item>
<item>CPACK_PACKAGE_DESCRIPTION_FILE</item>
<item>CPACK_PACKAGE_DESCRIPTION_SUMMARY</item>
<item>CPACK_PACKAGE_DIRECTORY</item>
<item>CPACK_PACKAGE_EXECUTABLES</item>
+ <item>CPACK_PACKAGE_FILES</item>
<item>CPACK_PACKAGE_FILE_NAME</item>
<item>CPACK_PACKAGE_HOMEPAGE_URL</item>
<item>CPACK_PACKAGE_ICON</item>
@@ -1741,10 +2880,28 @@
<item>CPACK_PACKAGING_INSTALL_PREFIX</item>
<item>CPACK_PKGBUILD_IDENTITY_NAME</item>
<item>CPACK_PKGBUILD_KEYCHAIN_PATH</item>
+ <item>CPACK_POST_BUILD_SCRIPTS</item>
+ <item>CPACK_PRE_BUILD_SCRIPTS</item>
+ <item>CPACK_PRODUCTBUILD_BACKGROUND</item>
+ <item>CPACK_PRODUCTBUILD_BACKGROUND_ALIGNMENT</item>
+ <item>CPACK_PRODUCTBUILD_BACKGROUND_DARKAQUA</item>
+ <item>CPACK_PRODUCTBUILD_BACKGROUND_DARKAQUA_ALIGNMENT</item>
+ <item>CPACK_PRODUCTBUILD_BACKGROUND_DARKAQUA_MIME_TYPE</item>
+ <item>CPACK_PRODUCTBUILD_BACKGROUND_DARKAQUA_SCALING</item>
+ <item>CPACK_PRODUCTBUILD_BACKGROUND_DARKAQUA_UTI</item>
+ <item>CPACK_PRODUCTBUILD_BACKGROUND_MIME_TYPE</item>
+ <item>CPACK_PRODUCTBUILD_BACKGROUND_SCALING</item>
+ <item>CPACK_PRODUCTBUILD_BACKGROUND_UTI</item>
+ <item>CPACK_PRODUCTBUILD_DOMAINS</item>
+ <item>CPACK_PRODUCTBUILD_DOMAINS_ANYWHERE</item>
+ <item>CPACK_PRODUCTBUILD_DOMAINS_ROOT</item>
+ <item>CPACK_PRODUCTBUILD_DOMAINS_USER</item>
+ <item>CPACK_PRODUCTBUILD_IDENTIFIER</item>
<item>CPACK_PRODUCTBUILD_IDENTITY_NAME</item>
<item>CPACK_PRODUCTBUILD_KEYCHAIN_PATH</item>
<item>CPACK_PRODUCTBUILD_RESOURCES_DIR</item>
<item>CPACK_PROJECT_CONFIG_FILE</item>
+ <item>CPACK_READELF_EXECUTABLE</item>
<item>CPACK_RESOURCE_FILE_LICENSE</item>
<item>CPACK_RESOURCE_FILE_README</item>
<item>CPACK_RESOURCE_FILE_WELCOME</item>
@@ -1798,10 +2955,13 @@
<item>CPACK_RPM_PACKAGE_VENDOR</item>
<item>CPACK_RPM_PACKAGE_VERSION</item>
<item>CPACK_RPM_POST_INSTALL_SCRIPT_FILE</item>
+ <item>CPACK_RPM_POST_TRANS_SCRIPT_FILE</item>
<item>CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE</item>
<item>CPACK_RPM_PRE_INSTALL_SCRIPT_FILE</item>
+ <item>CPACK_RPM_PRE_TRANS_SCRIPT_FILE</item>
<item>CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE</item>
<item>CPACK_RPM_RELOCATION_PATHS</item>
+ <item>CPACK_RPM_REQUIRES_EXCLUDE_FROM</item>
<item>CPACK_RPM_SOURCE_PKG_BUILD_PARAMS</item>
<item>CPACK_RPM_SOURCE_PKG_PACKAGING_INSTALL_PREFIX</item>
<item>CPACK_RPM_SPEC_INSTALL_POST</item>
@@ -1816,11 +2976,14 @@
<item>CPACK_SOURCE_STRIP_FILES</item>
<item>CPACK_STRIP_FILES</item>
<item>CPACK_SYSTEM_NAME</item>
+ <item>CPACK_THREADS</item>
<item>CPACK_TOPLEVEL_TAG</item>
<item>CPACK_VERBATIM_VARIABLES</item>
<item>CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION</item>
+ <item>CPACK_WIX_ARCHITECTURE</item>
<item>CPACK_WIX_CMAKE_PACKAGE_REGISTRY</item>
<item>CPACK_WIX_CULTURES</item>
+ <item>CPACK_WIX_CUSTOM_XMLNS</item>
<item>CPACK_WIX_EXTENSIONS</item>
<item>CPACK_WIX_EXTRA_OBJECTS</item>
<item>CPACK_WIX_EXTRA_SOURCES</item>
@@ -1834,6 +2997,7 @@
<item>CPACK_WIX_ROOT_FEATURE_TITLE</item>
<item>CPACK_WIX_ROOT_FOLDER_ID</item>
<item>CPACK_WIX_SKIP_PROGRAM_FOLDER</item>
+ <item>CPACK_WIX_SKIP_WIX_UI_EXTENSION</item>
<item>CPACK_WIX_TEMPLATE</item>
<item>CPACK_WIX_UI_BANNER</item>
<item>CPACK_WIX_UI_DIALOG</item>
@@ -1866,6 +3030,7 @@
<item>CTEST_CUSTOM_PRE_MEMCHECK</item>
<item>CTEST_CUSTOM_PRE_TEST</item>
<item>CTEST_CUSTOM_TEST_IGNORE</item>
+ <item>CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION</item>
<item>CTEST_CUSTOM_WARNING_EXCEPTION</item>
<item>CTEST_CUSTOM_WARNING_MATCH</item>
<item>CTEST_CVS_CHECKOUT</item>
@@ -1895,9 +3060,12 @@
<item>CTEST_P4_COMMAND</item>
<item>CTEST_P4_OPTIONS</item>
<item>CTEST_P4_UPDATE_OPTIONS</item>
+ <item>CTEST_RESOURCE_SPEC_FILE</item>
+ <item>CTEST_RUN_CURRENT_SCRIPT</item>
<item>CTEST_SCP_COMMAND</item>
<item>CTEST_SITE</item>
<item>CTEST_SOURCE_DIRECTORY</item>
+ <item>CTEST_SUBMIT_INACTIVITY_TIMEOUT</item>
<item>CTEST_SUBMIT_URL</item>
<item>CTEST_SVN_COMMAND</item>
<item>CTEST_SVN_OPTIONS</item>
@@ -1908,9 +3076,32 @@
<item>CTEST_UPDATE_COMMAND</item>
<item>CTEST_UPDATE_OPTIONS</item>
<item>CTEST_UPDATE_VERSION_ONLY</item>
+ <item>CTEST_UPDATE_VERSION_OVERRIDE</item>
<item>CTEST_USE_LAUNCHERS</item>
+ <item>CUDAToolkit_NVCC_EXECUTABLE</item>
+ <item>CUPS_INCLUDE_DIR</item>
+ <item>CURL_NO_CURL_CMAKE</item>
+ <item>CURL_USE_STATIC_LIBS</item>
+ <item>CURSES_CFLAGS</item>
+ <item>CURSES_HAVE_CURSES_H</item>
+ <item>CURSES_HAVE_NCURSES_CURSES_H</item>
+ <item>CURSES_HAVE_NCURSES_H</item>
+ <item>CURSES_HAVE_NCURSES_NCURSES_H</item>
+ <item>CURSES_NEED_NCURSES</item>
+ <item>CURSES_NEED_WIDE</item>
+ <item>CVS_EXECUTABLE</item>
+ <item>CXXTEST_PERL_TESTGEN_EXECUTABLE</item>
+ <item>CXXTEST_PYTHON_TESTGEN_EXECUTABLE</item>
+ <item>CXXTEST_TESTGEN_ARGS</item>
+ <item>CXXTEST_TESTGEN_EXECUTABLE</item>
+ <item>CXXTEST_TESTGEN_INTERPRETER</item>
+ <item>CXXTEST_USE_PYTHON</item>
<item>CYGWIN</item>
+ <item>DVIPDF_CONVERTER</item>
+ <item>DVIPS_CONVERTER</item>
<item>EXECUTABLE_OUTPUT_PATH</item>
+ <item>EXPAT_USE_STATIC_LIBS</item>
+ <item>EnvModules_COMMAND</item>
<item>ExternalData_BINARY_ROOT</item>
<item>ExternalData_CUSTOM_ERROR</item>
<item>ExternalData_CUSTOM_FILE</item>
@@ -1927,7 +3118,67 @@
<item>ExternalData_TIMEOUT_ABSOLUTE</item>
<item>ExternalData_TIMEOUT_INACTIVITY</item>
<item>ExternalData_URL_TEMPLATES</item>
- <item>GHS-MULTI</item>
+ <item>FETCHCONTENT_FULLY_DISCONNECTED</item>
+ <item>FETCHCONTENT_QUIET</item>
+ <item>FETCHCONTENT_TRY_FIND_PACKAGE_MODE</item>
+ <item>FETCHCONTENT_UPDATES_DISCONNECTED</item>
+ <item>FLEX_EXECUTABLE</item>
+ <item>FLTK2_BASE_LIBRARY</item>
+ <item>FLTK2_FLUID_EXECUTABLE</item>
+ <item>FLTK2_GL_LIBRARY</item>
+ <item>FLTK2_IMAGES_LIBRARY</item>
+ <item>FLTK2_WRAP_UI</item>
+ <item>FLTK_BASE_LIBRARY_DEBUG</item>
+ <item>FLTK_BASE_LIBRARY_RELEASE</item>
+ <item>FLTK_FLUID_EXECUTABLE</item>
+ <item>FLTK_FORMS_LIBRARY_DEBUG</item>
+ <item>FLTK_FORMS_LIBRARY_RELEASE</item>
+ <item>FLTK_GL_LIBRARY_DEBUG</item>
+ <item>FLTK_GL_LIBRARY_RELEASE</item>
+ <item>FLTK_IMAGES_LIBRARY_DEBUG</item>
+ <item>FLTK_IMAGES_LIBRARY_RELEASE</item>
+ <item>FLTK_SKIP_FLUID</item>
+ <item>FLTK_SKIP_FORMS</item>
+ <item>FLTK_SKIP_IMAGES</item>
+ <item>FLTK_SKIP_OPENGL</item>
+ <item>FLTK_WRAP_UI</item>
+ <item>FREETYPE_INCLUDE_DIR_freetype2</item>
+ <item>FREETYPE_INCLUDE_DIR_ft2build</item>
+ <item>FindGDAL_SKIP_GDAL_CONFIG</item>
+ <item>Fontconfig_COMPILE_OPTIONS</item>
+ <item>FortranCInterface_GLOBAL_CASE</item>
+ <item>FortranCInterface_GLOBAL_PREFIX</item>
+ <item>FortranCInterface_GLOBAL_SUFFIX</item>
+ <item>FortranCInterface_GLOBAL_SYMBOLS</item>
+ <item>FortranCInterface_GLOBAL__CASE</item>
+ <item>FortranCInterface_GLOBAL__PREFIX</item>
+ <item>FortranCInterface_GLOBAL__SUFFIX</item>
+ <item>FortranCInterface_MODULE_CASE</item>
+ <item>FortranCInterface_MODULE_MIDDLE</item>
+ <item>FortranCInterface_MODULE_PREFIX</item>
+ <item>FortranCInterface_MODULE_SUFFIX</item>
+ <item>FortranCInterface_MODULE_SYMBOLS</item>
+ <item>FortranCInterface_MODULE__CASE</item>
+ <item>FortranCInterface_MODULE__MIDDLE</item>
+ <item>FortranCInterface_MODULE__PREFIX</item>
+ <item>FortranCInterface_MODULE__SUFFIX</item>
+ <item>FortranCInterface_VERIFIED_C</item>
+ <item>FortranCInterface_VERIFIED_CXX</item>
+ <item>GCCXML</item>
+ <item>GDAL_ADDITIONAL_LIBRARY_VERSIONS</item>
+ <item>GDAL_LIBRARY</item>
+ <item>GETTEXT_MSGFMT_EXECUTABLE</item>
+ <item>GETTEXT_MSGMERGE_EXECUTABLE</item>
+ <item>GHSMULTI</item>
+ <item>GIF_LIBRARY</item>
+ <item>GIT_EXECUTABLE</item>
+ <item>GLEW_USE_STATIC_LIBS</item>
+ <item>GLEW_VERBOSE</item>
+ <item>GLUT_Xi_LIBRARY</item>
+ <item>GLUT_Xmu_LIBRARY</item>
+ <item>GLUT_glut_LIBRARY</item>
+ <item>GNUPLOT_EXECUTABLE</item>
+ <item>GNUTLS_DEFINITIONS</item>
<item>GRAPHVIZ_EXECUTABLES</item>
<item>GRAPHVIZ_EXTERNAL_LIBS</item>
<item>GRAPHVIZ_GENERATE_PER_TARGET</item>
@@ -1939,8 +3190,112 @@
<item>GRAPHVIZ_NODE_PREFIX</item>
<item>GRAPHVIZ_SHARED_LIBS</item>
<item>GRAPHVIZ_STATIC_LIBS</item>
+ <item>GSL_CBLAS_LIBRARY</item>
+ <item>GSL_CBLAS_LIBRARY_DEBUG</item>
+ <item>GSL_CONFIG_EXECUTABLE</item>
+ <item>GSL_LIBRARY</item>
+ <item>GSL_LIBRARY_DEBUG</item>
+ <item>GTEST_MSVC_SEARCH</item>
+ <item>GTK2_ADDITIONAL_SUFFIXES</item>
+ <item>GTK2_DEBUG</item>
+ <item>GTK2_DEFINITIONS</item>
+ <item>GTK2_TARGETS</item>
+ <item>GTK2_USE_IMPORTED_TARGETS</item>
+ <item>HDF5_CXX_COMPILER_EXECUTABLE</item>
+ <item>HDF5_CXX_COMPILER_EXECUTABLE_NO_INTERROGATE</item>
+ <item>HDF5_CXX_DEFINITIONS</item>
+ <item>HDF5_C_COMPILER_EXECUTABLE</item>
+ <item>HDF5_C_COMPILER_EXECUTABLE_NO_INTERROGATE</item>
+ <item>HDF5_C_DEFINITIONS</item>
+ <item>HDF5_DEFINITIONS</item>
+ <item>HDF5_DIFF_EXECUTABLE</item>
+ <item>HDF5_FIND_DEBUG</item>
+ <item>HDF5_Fortran_COMPILER_EXECUTABLE</item>
+ <item>HDF5_Fortran_COMPILER_EXECUTABLE_NO_INTERROGATE</item>
+ <item>HDF5_Fortran_DEFINITIONS</item>
+ <item>HDF5_IS_PARALLEL</item>
+ <item>HDF5_NO_FIND_PACKAGE_CONFIG_FILE</item>
+ <item>HDF5_PREFER_PARALLEL</item>
+ <item>HG_EXECUTABLE</item>
+ <item>HTLATEX_COMPILER</item>
+ <item>HTML_HELP_COMPILER</item>
+ <item>HTML_HELP_LIBRARY</item>
+ <item>ICOTOOL_EXECUTABLE</item>
+ <item>ICU_DEBUG</item>
+ <item>ICU_MAKEFILE_INC</item>
+ <item>ICU_PKGDATA_INC</item>
+ <item>IOS</item>
+ <item>Iconv_IS_BUILT_IN</item>
+ <item>Iconv_LIBRARY</item>
+ <item>Intl_IS_BUILT_IN</item>
+ <item>Intl_LIBRARY</item>
+ <item>JASPER_LIBARRY_DEBUG</item>
+ <item>JASPER_LIBRARY_RELEASE</item>
+ <item>JAVA_AWT_INCLUDE_PATH</item>
+ <item>JAVA_AWT_LIBRARY</item>
+ <item>JAVA_INCLUDE_PATH</item>
+ <item>JAVA_INCLUDE_PATH2</item>
+ <item>JAVA_JVM_LIBRARY</item>
+ <item>JPEG_LIBRARY</item>
+ <item>JPEG_LIBRARY_DEBUG</item>
+ <item>JPEG_LIBRARY_RELEASE</item>
+ <item>Java_IDLJ_EXECUTABLE</item>
+ <item>Java_JARSIGNER_EXECUTABLE</item>
+ <item>Java_JAR_EXECUTABLE</item>
+ <item>Java_JAVAC_EXECUTABLE</item>
+ <item>Java_JAVADOC_EXECUTABLE</item>
+ <item>Java_JAVAH_EXECUTABLE</item>
+ <item>Java_JAVA_EXECUTABLE</item>
+ <item>LAPACK_LINKER_FLAGS</item>
+ <item>LATEX2HTML_CONVERTER</item>
+ <item>LATEX_COMPILER</item>
+ <item>LIBLZMA_HAS_AUTO_DECODER</item>
+ <item>LIBLZMA_HAS_EASY_ENCODER</item>
+ <item>LIBLZMA_HAS_LZMA_PRESET</item>
<item>LIBRARY_OUTPUT_PATH</item>
+ <item>LIBXML2_DEFINITIONS</item>
+ <item>LIBXML2_LIBRARY</item>
+ <item>LIBXML2_XMLLINT_EXECUTABLE</item>
+ <item>LIBXSLT_DEFINITIONS</item>
+ <item>LIBXSLT_XSLTPROC_EXECUTABLE</item>
+ <item>LINUX</item>
+ <item>LTTNGUST_HAS_TRACEF</item>
+ <item>LTTNGUST_HAS_TRACELOG</item>
+ <item>LUALATEX_COMPILER</item>
+ <item>Libinput_COMPILE_OPTIONS</item>
+ <item>MAKEINDEX_COMPILER</item>
+ <item>MATLAB_ADDITIONAL_VERSIONS</item>
+ <item>MATLAB_FIND_DEBUG</item>
<item>MINGW</item>
+ <item>MPEG2_mpeg2_LIBRARY</item>
+ <item>MPEG2_vo_LIBRARY</item>
+ <item>MPEG_mpeg2_LIBRARY</item>
+ <item>MPEG_vo_LIBRARY</item>
+ <item>MPIEXEC_EXECUTABLE</item>
+ <item>MPIEXEC_MAX_NUMPROCS</item>
+ <item>MPIEXEC_NUMPROC_FLAG</item>
+ <item>MPIEXEC_POSTFLAGS</item>
+ <item>MPIEXEC_PREFLAGS</item>
+ <item>MPI_ASSUME_NO_BUILTIN_MPI</item>
+ <item>MPI_ASYNC_PROTECTS_NONBLOCKING</item>
+ <item>MPI_COMPILER_FLAGS</item>
+ <item>MPI_CXX_SKIP_MPICXX</item>
+ <item>MPI_DETERMINE_Fortran_CAPABILITIES</item>
+ <item>MPI_EXECUTABLE_SUFFIX</item>
+ <item>MPI_Fortran_F08_MODULE_ASYNCPROT</item>
+ <item>MPI_Fortran_F08_MODULE_SUBARRAYS</item>
+ <item>MPI_Fortran_F77_HEADER_ASYNCPROT</item>
+ <item>MPI_Fortran_F77_HEADER_SUBARRAYS</item>
+ <item>MPI_Fortran_F90_MODULE_ASYNCPROT</item>
+ <item>MPI_Fortran_F90_MODULE_SUBARRAYS</item>
+ <item>MPI_Fortran_HAVE_F08_MODULE</item>
+ <item>MPI_Fortran_HAVE_F77_HEADER</item>
+ <item>MPI_Fortran_HAVE_F90_MODULE</item>
+ <item>MPI_GUESS_LIBRARY_NAME</item>
+ <item>MPI_HOME</item>
+ <item>MPI_SKIP_COMPILER_WRAPPER</item>
+ <item>MPI_SKIP_GUESSING</item>
+ <item>MPI_SUBARRAYS_SUPPORTED</item>
<item>MSVC</item>
<item>MSVC10</item>
<item>MSVC11</item>
@@ -1954,23 +3309,83 @@
<item>MSVC_IDE</item>
<item>MSVC_TOOLSET_VERSION</item>
<item>MSVC_VERSION</item>
+ <item>Matlab_DATAARRAY_LIBRARY</item>
+ <item>Matlab_ENGINE_LIBRARY</item>
+ <item>Matlab_ENG_LIBRARY</item>
+ <item>Matlab_MAIN_PROGRAM</item>
+ <item>Matlab_MAT_LIBRARY</item>
+ <item>Matlab_MCC_COMPILER</item>
+ <item>Matlab_MEX_COMPILER</item>
+ <item>Matlab_MEX_EXTENSION</item>
+ <item>Matlab_MEX_LIBRARY</item>
+ <item>Matlab_MX_LIBRARY</item>
+ <item>ODBC_CONFIG</item>
+ <item>ODBC_LIBRARY</item>
+ <item>OPENAL_LIBRARY</item>
+ <item>OPENGL_egl_LIBRARY</item>
+ <item>OPENGL_gl_LIBRARY</item>
+ <item>OPENGL_glu_LIBRARY</item>
+ <item>OPENGL_glx_LIBRARY</item>
+ <item>OPENGL_opengl_LIBRARY</item>
+ <item>OPENSSL_APPLINK_SOURCE</item>
+ <item>OPENSSL_CRYPTO_LIBRARY</item>
+ <item>OPENSSL_MSVC_STATIC_RT</item>
+ <item>OPENSSL_SSL_LIBRARY</item>
+ <item>OPENSSL_USE_STATIC_LIBS</item>
+ <item>OpenACC_ACCEL_TARGET</item>
+ <item>OpenCL_LIBRARY</item>
+ <item>OpenGL_GL_PREFERENCE</item>
+ <item>OpenMP_Fortran_HAVE_OMPLIB_HEADER</item>
+ <item>OpenMP_Fortran_HAVE_OMPLIB_MODULE</item>
<item>PACKAGE_FIND_NAME</item>
- <item>PACKAGE_FIND_VERSION</item>
+ <item>PACKAGE_FIND_VERSION_COMPLETE</item>
<item>PACKAGE_FIND_VERSION_COUNT</item>
- <item>PACKAGE_FIND_VERSION_MAJOR</item>
- <item>PACKAGE_FIND_VERSION_MINOR</item>
- <item>PACKAGE_FIND_VERSION_PATCH</item>
- <item>PACKAGE_FIND_VERSION_TWEAK</item>
+ <item>PACKAGE_FIND_VERSION_MAX</item>
+ <item>PACKAGE_FIND_VERSION_MAX_COUNT</item>
+ <item>PACKAGE_FIND_VERSION_MAX_MAJOR</item>
+ <item>PACKAGE_FIND_VERSION_MAX_MINOR</item>
+ <item>PACKAGE_FIND_VERSION_MAX_PATCH</item>
+ <item>PACKAGE_FIND_VERSION_MAX_TWEAK</item>
+ <item>PACKAGE_FIND_VERSION_MIN</item>
+ <item>PACKAGE_FIND_VERSION_MIN_COUNT</item>
+ <item>PACKAGE_FIND_VERSION_MIN_MAJOR</item>
+ <item>PACKAGE_FIND_VERSION_MIN_MINOR</item>
+ <item>PACKAGE_FIND_VERSION_MIN_PATCH</item>
+ <item>PACKAGE_FIND_VERSION_MIN_TWEAK</item>
+ <item>PACKAGE_FIND_VERSION_RANGE</item>
+ <item>PACKAGE_FIND_VERSION_RANGE_MAX</item>
+ <item>PACKAGE_FIND_VERSION_RANGE_MIN</item>
<item>PACKAGE_VERSION</item>
<item>PACKAGE_VERSION_COMPATIBLE</item>
<item>PACKAGE_VERSION_EXACT</item>
<item>PACKAGE_VERSION_UNSUITABLE</item>
+ <item>PDFLATEX_COMPILER</item>
+ <item>PDFTOPS_CONVERTER</item>
+ <item>PERL_ARCHLIB</item>
+ <item>PERL_EXECUTABLE</item>
+ <item>PERL_EXTRA_C_FLAGS</item>
+ <item>PERL_PRIVLIB</item>
+ <item>PERL_SITEARCH</item>
+ <item>PERL_SITELIB</item>
+ <item>PERL_SITESEARCH</item>
+ <item>PERL_UPDATE_ARCHLIB</item>
+ <item>PERL_UPDATE_PRIVLIB</item>
+ <item>PERL_VENDORARCH</item>
+ <item>PERL_VENDORLIB</item>
+ <item>PHP4_EXECUTABLE</item>
+ <item>PHP4_INCLUDE_PATH</item>
+ <item>PHYSFS_LIBRARY</item>
+ <item>PIKE_EXECUTABLE</item>
+ <item>PIKE_INCLUDE_PATH</item>
<item>PKG_CONFIG_EXECUTABLE</item>
<item>PKG_CONFIG_USE_CMAKE_PREFIX_PATH</item>
<item>PKG_CONFIG_VERSION_STRING</item>
+ <item>PNG_DEFINITIONS</item>
+ <item>PNG_LIBRARY</item>
<item>PROJECT_BINARY_DIR</item>
<item>PROJECT_DESCRIPTION</item>
<item>PROJECT_HOMEPAGE_URL</item>
+ <item>PROJECT_IS_TOP_LEVEL</item>
<item>PROJECT_NAME</item>
<item>PROJECT_SOURCE_DIR</item>
<item>PROJECT_VERSION</item>
@@ -1978,17 +3393,220 @@
<item>PROJECT_VERSION_MINOR</item>
<item>PROJECT_VERSION_PATCH</item>
<item>PROJECT_VERSION_TWEAK</item>
+ <item>PS2PDF_CONVERTER</item>
+ <item>Patch_EXECUTABLE</item>
+ <item>Protobuf_DEBUG</item>
+ <item>Protobuf_IMPORT_DIRS</item>
+ <item>Protobuf_LIBRARY</item>
+ <item>Protobuf_LIBRARY_DEBUG</item>
+ <item>Protobuf_LITE_LIBRARY</item>
+ <item>Protobuf_LITE_LIBRARY_DEBUG</item>
+ <item>Protobuf_PROTOC_EXECUTABLE</item>
+ <item>Protobuf_PROTOC_LIBRARY</item>
+ <item>Protobuf_PROTOC_LIBRARY_DEBUG</item>
+ <item>Protobuf_SRC_ROOT_FOLDER</item>
+ <item>Protobuf_USE_STATIC_LIBS</item>
+ <item>Python2_ARTIFACTS_INTERACTIVE</item>
+ <item>Python2_COMPILER</item>
+ <item>Python2_COMPILER_ID</item>
+ <item>Python2_DOTNET_LAUNCHER</item>
+ <item>Python2_EXECUTABLE</item>
+ <item>Python2_FIND_ABI</item>
+ <item>Python2_FIND_FRAMEWORK</item>
+ <item>Python2_FIND_IMPLEMENTATIONS</item>
+ <item>Python2_FIND_REGISTRY</item>
+ <item>Python2_FIND_STRATEGY</item>
+ <item>Python2_FIND_UNVERSIONED_NAMES</item>
+ <item>Python2_FIND_VIRTUALENV</item>
+ <item>Python2_INTERPRETER_ID</item>
+ <item>Python2_LINK_OPTIONS</item>
+ <item>Python2_SITEARCH</item>
+ <item>Python2_SITELIB</item>
+ <item>Python2_SOABI</item>
+ <item>Python2_STDARCH</item>
+ <item>Python2_STDLIB</item>
+ <item>Python2_USE_STATIC_LIBS</item>
+ <item>Python3_ARTIFACTS_INTERACTIVE</item>
+ <item>Python3_COMPILER</item>
+ <item>Python3_COMPILER_ID</item>
+ <item>Python3_DOTNET_LAUNCHER</item>
+ <item>Python3_EXECUTABLE</item>
+ <item>Python3_FIND_ABI</item>
+ <item>Python3_FIND_FRAMEWORK</item>
+ <item>Python3_FIND_IMPLEMENTATIONS</item>
+ <item>Python3_FIND_REGISTRY</item>
+ <item>Python3_FIND_STRATEGY</item>
+ <item>Python3_FIND_UNVERSIONED_NAMES</item>
+ <item>Python3_FIND_VIRTUALENV</item>
+ <item>Python3_INTERPRETER_ID</item>
+ <item>Python3_LINK_OPTIONS</item>
+ <item>Python3_SITEARCH</item>
+ <item>Python3_SITELIB</item>
+ <item>Python3_SOABI</item>
+ <item>Python3_STDARCH</item>
+ <item>Python3_STDLIB</item>
+ <item>Python3_USE_STATIC_LIBS</item>
+ <item>Python_ARTIFACTS_INTERACTIVE</item>
+ <item>Python_COMPILER</item>
+ <item>Python_COMPILER_ID</item>
+ <item>Python_DOTNET_LAUNCHER</item>
+ <item>Python_EXECUTABLE</item>
+ <item>Python_FIND_ABI</item>
+ <item>Python_FIND_FRAMEWORK</item>
+ <item>Python_FIND_IMPLEMENTATIONS</item>
+ <item>Python_FIND_REGISTRY</item>
+ <item>Python_FIND_STRATEGY</item>
+ <item>Python_FIND_UNVERSIONED_NAMES</item>
+ <item>Python_FIND_VIRTUALENV</item>
+ <item>Python_INTERPRETER_ID</item>
+ <item>Python_LINK_OPTIONS</item>
+ <item>Python_SITEARCH</item>
+ <item>Python_SITELIB</item>
+ <item>Python_SOABI</item>
+ <item>Python_STDARCH</item>
+ <item>Python_STDLIB</item>
+ <item>Python_USE_STATIC_LIBS</item>
<item>QTIFWDIR</item>
+ <item>RTI_DEFINITIONS</item>
+ <item>Ruby_EXECUTABLE</item>
+ <item>Ruby_FIND_VIRTUALENV</item>
+ <item>SDLIMAGE_LIBRARY</item>
+ <item>SDLMIXER_LIBRARY</item>
+ <item>SDLNET_LIBRARY</item>
+ <item>SDLTTF_LIBRARY</item>
+ <item>SDL_BUILDING_LIBRARY</item>
+ <item>SDL_LIBRARY</item>
+ <item>SDL_SOUND_LIBRARY</item>
+ <item>SQUISH_CLIENT_EXECUTABLE</item>
+ <item>SQUISH_SERVER_EXECUTABLE</item>
+ <item>SWIG_EXECUTABLE</item>
<item>SWIG_OUTFILE_DIR</item>
<item>SWIG_SOURCE_FILE_EXTENSIONS</item>
+ <item>SWIG_USE_SWIG_DEPENDENCIES</item>
+ <item>Subversion_SVN_EXECUTABLE</item>
+ <item>Subversion_VERSION_SVN</item>
+ <item>TCL_INCLUDE_PATH</item>
+ <item>TCL_STUB_LIBRARY</item>
+ <item>TCL_TCLSH</item>
<item>THREADS_PREFER_PTHREAD_FLAG</item>
+ <item>TIFFXX_LIBRARY_DEBUG</item>
+ <item>TIFFXX_LIBRARY_RELEASE</item>
+ <item>TIFF_LIBRARY_DEBUG</item>
+ <item>TIFF_LIBRARY_RELEASE</item>
+ <item>TK_INCLUDE_PATH</item>
+ <item>TK_LIBRARY</item>
+ <item>TK_STUB_LIBRARY</item>
+ <item>TK_WISH</item>
+ <item>TTK_STUB_LIBRARY</item>
<item>UNIX</item>
+ <item>UseSWIG_MODULE_VERSION</item>
+ <item>Vulkan_GLSLANG_VALIDATOR_EXECUTABLE</item>
+ <item>Vulkan_GLSLC_EXECUTABLE</item>
+ <item>Vulkan_LIBRARY</item>
+ <item>WGET_EXECUTABLE</item>
<item>WIN32</item>
<item>WINCE</item>
<item>WINDOWS_PHONE</item>
<item>WINDOWS_STORE</item>
<item>XCODE</item>
<item>XCODE_VERSION</item>
+ <item>XCTest_EXECUTABLE</item>
+ <item>XELATEX_COMPILER</item>
+ <item>XINDY_COMPILER</item>
+ <item>XalanC_LIBRARY</item>
+ <item>XercesC_LIBRARY</item>
+ <item>ZLIB_USE_STATIC_LIBS</item>
+ <item>wxWidgets_CONFIGURATION</item>
+ <item>wxWidgets_CXX_FLAGS</item>
+ <item>wxWidgets_DEFINITIONS</item>
+ <item>wxWidgets_DEFINITIONS_DEBUG</item>
+ <item>wxWidgets_EXCLUDE_COMMON_LIBRARIES</item>
+ <item>wxWidgets_USE_DEBUG</item>
+ <item>wxWidgets_USE_FILE</item>
+ <item>wxWidgets_USE_STATIC</item>
+ <item>wxWidgets_USE_UNICODE</item>
+ <item>wxWidgets_USE_UNIVERSAL</item>
+ </list>
+
+ <list name="deprecated-or-internal-variables">
+ <item>CMAKE_ENABLE_EXPORTS</item>
+ <item>CMAKE_FILES_DIRECTORY</item>
+ <item>CMAKE_HOME_DIRECTORY</item>
+ <item>CMAKE_INTERNAL_PLATFORM_ABI</item>
+ <item>CMAKE_IOS_INSTALL_COMBINED</item>
+ <item>CMAKE_NOT_USING_CONFIG_FLAGS</item>
+ <item>CMAKE_OBJDUMP</item>
+ <item>CMAKE_SUPPRESS_DEVELOPER_ERRORS</item>
+ <item>CMAKE_SUPPRESS_DEVELOPER_WARNINGS</item>
+ <item>CMAKE_SYSTEM_ARCH</item>
+ <item>CMAKE_VS_INTEL_Fortran_PROJECT_VERSION</item>
+ <item>CPACK_INSTALL_PREFIX</item>
+ <item>CPACK_INSTALL_SCRIPT</item>
+ <item>CPACK_PACKAGE_CONTACT</item>
+ <item>CPACK_PACKAGE_RELOCATABLE</item>
+ <item>CPACK_TEMPORARY_DIRECTORY</item>
+ <item>CPACK_TOPLEVEL_DIRECTORY</item>
+ </list>
+
+ <list name="environment-variables">
+ <item>ADSP_ROOT</item>
+ <item>CC</item>
+ <item>CCMAKE_COLORS</item>
+ <item>CFLAGS</item>
+ <item>CMAKE_APPBUNDLE_PATH</item>
+ <item>CMAKE_APPLE_SILICON_PROCESSOR</item>
+ <item>CMAKE_BUILD_PARALLEL_LEVEL</item>
+ <item>CMAKE_BUILD_TYPE</item>
+ <item>CMAKE_COLOR_DIAGNOSTICS</item>
+ <item>CMAKE_CONFIGURATION_TYPES</item>
+ <item>CMAKE_CONFIG_TYPE</item>
+ <item>CMAKE_CROSSCOMPILING_EMULATOR</item>
+ <item>CMAKE_EXPORT_COMPILE_COMMANDS</item>
+ <item>CMAKE_FRAMEWORK_PATH</item>
+ <item>CMAKE_GENERATOR</item>
+ <item>CMAKE_GENERATOR_INSTANCE</item>
+ <item>CMAKE_GENERATOR_PLATFORM</item>
+ <item>CMAKE_GENERATOR_TOOLSET</item>
+ <item>CMAKE_INSTALL_MODE</item>
+ <item>CMAKE_MSVCIDE_RUN_PATH</item>
+ <item>CMAKE_NO_VERBOSE</item>
+ <item>CMAKE_OSX_ARCHITECTURES</item>
+ <item>CMAKE_PREFIX_PATH</item>
+ <item>CMAKE_TOOLCHAIN_FILE</item>
+ <item>CSFLAGS</item>
+ <item>CTEST_INTERACTIVE_DEBUG_MODE</item>
+ <item>CTEST_NO_TESTS_ACTION</item>
+ <item>CTEST_OUTPUT_ON_FAILURE</item>
+ <item>CTEST_PARALLEL_LEVEL</item>
+ <item>CTEST_PROGRESS_OUTPUT</item>
+ <item>CTEST_USE_LAUNCHERS_DEFAULT</item>
+ <item>CUDAARCHS</item>
+ <item>CUDACXX</item>
+ <item>CUDAFLAGS</item>
+ <item>CUDAHOSTCXX</item>
+ <item>CUDA_PATH</item>
+ <item>CXX</item>
+ <item>CXXFLAGS</item>
+ <item>DASHBOARD_TEST_FROM_CTEST</item>
+ <item>DESTDIR</item>
+ <item>FC</item>
+ <item>FFLAGS</item>
+ <item>HIPCXX</item>
+ <item>HIPFLAGS</item>
+ <item>ISPC</item>
+ <item>ISPCFLAGS</item>
+ <item>LDFLAGS</item>
+ <item>MACOSX_DEPLOYMENT_TARGET</item>
+ <item>OBJC</item>
+ <item>OBJCXX</item>
+ <item>PATH</item>
+ <item>QTIFWDIR</item>
+ <item>RC</item>
+ <item>RCFLAGS</item>
+ <item>SSL_CERT_DIR</item>
+ <item>SSL_CERT_FILE</item>
+ <item>SWIFTC</item>
+ <item>VERBOSE</item>
</list>
<list name="global-properties">
<item>ALLOW_DUPLICATE_CUSTOM_TARGETS</item>
@@ -1997,6 +3615,7 @@
<item>AUTOMOC_SOURCE_GROUP</item>
<item>AUTOMOC_TARGETS_FOLDER</item>
<item>AUTORCC_SOURCE_GROUP</item>
+ <item>CMAKE_CUDA_KNOWN_FEATURES</item>
<item>CMAKE_CXX_KNOWN_FEATURES</item>
<item>CMAKE_C_KNOWN_FEATURES</item>
<item>CMAKE_ROLE</item>
@@ -2032,6 +3651,7 @@
<item>XCODE_EMIT_EFFECTIVE_PLATFORM_NAME</item>
</list>
<list name="directory-properties">
+ <item>ADDITIONAL_CLEAN_FILES</item>
<item>ADDITIONAL_MAKE_CLEAN_FILES</item>
<item>BINARY_DIR</item>
<item>BUILDSYSTEM_TARGETS</item>
@@ -2043,6 +3663,7 @@
<item>DEFINITIONS</item>
<item>EXCLUDE_FROM_ALL</item>
<item>IMPLICIT_DEPENDS_INCLUDE_TRANSFORM</item>
+ <item>IMPORTED_TARGETS</item>
<item>INCLUDE_DIRECTORIES</item>
<item>INCLUDE_REGULAR_EXPRESSION</item>
<item>INTERPROCEDURAL_OPTIMIZATION</item>
@@ -2057,6 +3678,7 @@
<item>RULE_LAUNCH_LINK</item>
<item>SOURCE_DIR</item>
<item>SUBDIRECTORIES</item>
+ <item>SYSTEM</item>
<item>TESTS</item>
<item>TEST_INCLUDE_FILE</item>
<item>TEST_INCLUDE_FILES</item>
@@ -2064,6 +3686,8 @@
<item>VS_STARTUP_PROJECT</item>
</list>
<list name="target-properties">
+ <item>ADDITIONAL_CLEAN_FILES</item>
+ <item>AIX_EXPORT_ALL_SYMBOLS</item>
<item>ALIASED_TARGET</item>
<item>ANDROID_ANT_ADDITIONAL_OPTIONS</item>
<item>ANDROID_API</item>
@@ -2087,12 +3711,14 @@
<item>AUTOGEN_BUILD_DIR</item>
<item>AUTOGEN_ORIGIN_DEPENDS</item>
<item>AUTOGEN_TARGET_DEPENDS</item>
+ <item>AUTOGEN_USE_SYSTEM_INCLUDE</item>
<item>AUTOMOC</item>
<item>AUTOMOC_COMPILER_PREDEFINES</item>
<item>AUTOMOC_DEPEND_FILTERS</item>
<item>AUTOMOC_EXECUTABLE</item>
<item>AUTOMOC_MACRO_NAMES</item>
<item>AUTOMOC_MOC_OPTIONS</item>
+ <item>AUTOMOC_PATH_PREFIX</item>
<item>AUTORCC</item>
<item>AUTORCC_EXECUTABLE</item>
<item>AUTORCC_OPTIONS</item>
@@ -2118,14 +3744,24 @@
<item>COMPILE_OPTIONS</item>
<item>COMPILE_PDB_NAME</item>
<item>COMPILE_PDB_OUTPUT_DIRECTORY</item>
+ <item>COMPILE_WARNING_AS_ERROR</item>
<item>CROSSCOMPILING_EMULATOR</item>
+ <item>CUDA_ARCHITECTURES</item>
+ <item>CUDA_CUBIN_COMPILATION</item>
<item>CUDA_EXTENSIONS</item>
+ <item>CUDA_FATBIN_COMPILATION</item>
+ <item>CUDA_OPTIX_COMPILATION</item>
<item>CUDA_PTX_COMPILATION</item>
<item>CUDA_RESOLVE_DEVICE_SYMBOLS</item>
+ <item>CUDA_RUNTIME_LIBRARY</item>
<item>CUDA_SEPARABLE_COMPILATION</item>
<item>CUDA_STANDARD</item>
<item>CUDA_STANDARD_REQUIRED</item>
<item>CXX_EXTENSIONS</item>
+ <item>CXX_MODULE_DIRS</item>
+ <item>CXX_MODULE_SET</item>
+ <item>CXX_MODULE_SETS</item>
+ <item>CXX_SCAN_FOR_MODULES</item>
<item>CXX_STANDARD</item>
<item>CXX_STANDARD_REQUIRED</item>
<item>C_EXTENSIONS</item>
@@ -2134,11 +3770,18 @@
<item>DEFINE_SYMBOL</item>
<item>DEPLOYMENT_ADDITIONAL_FILES</item>
<item>DEPLOYMENT_REMOTE_DIRECTORY</item>
+ <item>DEPRECATION</item>
+ <item>DISABLE_PRECOMPILE_HEADERS</item>
+ <item>DLL_NAME_WITH_SOVERSION</item>
+ <item>DOTNET_SDK</item>
+ <item>DOTNET_TARGET_FRAMEWORK</item>
<item>DOTNET_TARGET_FRAMEWORK_VERSION</item>
<item>ENABLE_EXPORTS</item>
<item>EXCLUDE_FROM_ALL</item>
<item>EXCLUDE_FROM_DEFAULT_BUILD</item>
+ <item>EXPORT_COMPILE_COMMANDS</item>
<item>EXPORT_NAME</item>
+ <item>EXPORT_NO_SYSTEM</item>
<item>EXPORT_PROPERTIES</item>
<item>EchoString</item>
<item>FOLDER</item>
@@ -2146,9 +3789,17 @@
<item>FRAMEWORK_VERSION</item>
<item>Fortran_FORMAT</item>
<item>Fortran_MODULE_DIRECTORY</item>
+ <item>Fortran_PREPROCESS</item>
<item>GENERATOR_FILE_NAME</item>
<item>GNUtoMS</item>
<item>HAS_CXX</item>
+ <item>HEADER_DIRS</item>
+ <item>HEADER_SET</item>
+ <item>HEADER_SETS</item>
+ <item>HIP_ARCHITECTURES</item>
+ <item>HIP_EXTENSIONS</item>
+ <item>HIP_STANDARD</item>
+ <item>HIP_STANDARD_REQUIRED</item>
<item>IMPLICIT_DEPENDS_INCLUDE_TRANSFORM</item>
<item>IMPORTED</item>
<item>IMPORTED_COMMON_LANGUAGE_RUNTIME</item>
@@ -2162,28 +3813,40 @@
<item>IMPORTED_LINK_INTERFACE_MULTIPLICITY</item>
<item>IMPORTED_LOCATION</item>
<item>IMPORTED_NO_SONAME</item>
+ <item>IMPORTED_NO_SYSTEM</item>
<item>IMPORTED_OBJECTS</item>
<item>IMPORTED_SONAME</item>
<item>IMPORT_PREFIX</item>
<item>IMPORT_SUFFIX</item>
<item>INCLUDE_DIRECTORIES</item>
<item>INSTALL_NAME_DIR</item>
+ <item>INSTALL_REMOVE_ENVIRONMENT_RPATH</item>
<item>INSTALL_RPATH</item>
<item>INSTALL_RPATH_USE_LINK_PATH</item>
+ <item>INTERFACE_AUTOMOC_MACRO_NAMES</item>
<item>INTERFACE_AUTOUIC_OPTIONS</item>
<item>INTERFACE_COMPILE_DEFINITIONS</item>
<item>INTERFACE_COMPILE_FEATURES</item>
<item>INTERFACE_COMPILE_OPTIONS</item>
+ <item>INTERFACE_CXX_MODULE_SETS</item>
+ <item>INTERFACE_HEADER_SETS</item>
+ <item>INTERFACE_HEADER_SETS_TO_VERIFY</item>
<item>INTERFACE_INCLUDE_DIRECTORIES</item>
<item>INTERFACE_LINK_DEPENDS</item>
<item>INTERFACE_LINK_DIRECTORIES</item>
<item>INTERFACE_LINK_LIBRARIES</item>
+ <item>INTERFACE_LINK_LIBRARIES_DIRECT</item>
+ <item>INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE</item>
<item>INTERFACE_LINK_OPTIONS</item>
<item>INTERFACE_POSITION_INDEPENDENT_CODE</item>
+ <item>INTERFACE_PRECOMPILE_HEADERS</item>
<item>INTERFACE_SOURCES</item>
<item>INTERFACE_SYSTEM_INCLUDE_DIRECTORIES</item>
<item>INTERPROCEDURAL_OPTIMIZATION</item>
<item>IOS_INSTALL_COMBINED</item>
+ <item>ISPC_HEADER_DIRECTORY</item>
+ <item>ISPC_HEADER_SUFFIX</item>
+ <item>ISPC_INSTRUCTION_SETS</item>
<item>JOB_POOL_COMPILE</item>
<item>JOB_POOL_LINK</item>
<item>LABELS</item>
@@ -2197,24 +3860,42 @@
<item>LINK_INTERFACE_LIBRARIES</item>
<item>LINK_INTERFACE_MULTIPLICITY</item>
<item>LINK_LIBRARIES</item>
+ <item>LINK_LIBRARIES_ONLY_TARGETS</item>
<item>LINK_OPTIONS</item>
<item>LINK_SEARCH_END_STATIC</item>
<item>LINK_SEARCH_START_STATIC</item>
<item>LINK_WHAT_YOU_USE</item>
<item>LOCATION</item>
+ <item>MACHO_COMPATIBILITY_VERSION</item>
+ <item>MACHO_CURRENT_VERSION</item>
<item>MACOSX_BUNDLE</item>
<item>MACOSX_BUNDLE_INFO_PLIST</item>
<item>MACOSX_FRAMEWORK_INFO_PLIST</item>
<item>MACOSX_RPATH</item>
<item>MANUALLY_ADDED_DEPENDENCIES</item>
+ <item>MSVC_DEBUG_INFORMATION_FORMAT</item>
+ <item>MSVC_RUNTIME_LIBRARY</item>
<item>NAME</item>
<item>NO_SONAME</item>
<item>NO_SYSTEM_FROM_IMPORTED</item>
+ <item>OBJCXX_EXTENSIONS</item>
+ <item>OBJCXX_STANDARD</item>
+ <item>OBJCXX_STANDARD_REQUIRED</item>
+ <item>OBJC_EXTENSIONS</item>
+ <item>OBJC_STANDARD</item>
+ <item>OBJC_STANDARD_REQUIRED</item>
+ <item>OPTIMIZE_DEPENDENCIES</item>
<item>OSX_ARCHITECTURES</item>
+ <item>OSX_COMPATIBILITY_VERSION</item>
+ <item>OSX_CURRENT_VERSION</item>
<item>OUTPUT_NAME</item>
+ <item>PCH_INSTANTIATE_TEMPLATES</item>
+ <item>PCH_WARN_INVALID</item>
<item>PDB_NAME</item>
<item>PDB_OUTPUT_DIRECTORY</item>
<item>POSITION_INDEPENDENT_CODE</item>
+ <item>PRECOMPILE_HEADERS</item>
+ <item>PRECOMPILE_HEADERS_REUSE_FROM</item>
<item>PREFIX</item>
<item>PRIVATE_HEADER</item>
<item>PROJECT_LABEL</item>
@@ -2232,7 +3913,18 @@
<item>STATIC_LIBRARY_FLAGS</item>
<item>STATIC_LIBRARY_OPTIONS</item>
<item>SUFFIX</item>
+ <item>SYSTEM</item>
+ <item>Swift_DEPENDENCIES_FILE</item>
+ <item>Swift_MODULE_DIRECTORY</item>
+ <item>Swift_MODULE_NAME</item>
<item>TYPE</item>
+ <item>UNITY_BUILD</item>
+ <item>UNITY_BUILD_BATCH_SIZE</item>
+ <item>UNITY_BUILD_CODE_AFTER_INCLUDE</item>
+ <item>UNITY_BUILD_CODE_BEFORE_INCLUDE</item>
+ <item>UNITY_BUILD_MODE</item>
+ <item>UNITY_BUILD_UNIQUE_ID</item>
+ <item>VERIFY_INTERFACE_HEADER_SETS</item>
<item>VERSION</item>
<item>VISIBILITY_INLINES_HIDDEN</item>
<item>VS_CONFIGURATION_TYPE</item>
@@ -2241,39 +3933,59 @@
<item>VS_DEBUGGER_ENVIRONMENT</item>
<item>VS_DEBUGGER_WORKING_DIRECTORY</item>
<item>VS_DESKTOP_EXTENSIONS_VERSION</item>
+ <item>VS_DOTNET_DOCUMENTATION_FILE</item>
<item>VS_DOTNET_REFERENCES</item>
<item>VS_DOTNET_REFERENCES_COPY_LOCAL</item>
+ <item>VS_DOTNET_STARTUP_OBJECT</item>
<item>VS_DOTNET_TARGET_FRAMEWORK_VERSION</item>
+ <item>VS_DPI_AWARE</item>
<item>VS_GLOBAL_KEYWORD</item>
<item>VS_GLOBAL_PROJECT_TYPES</item>
<item>VS_GLOBAL_ROOTNAMESPACE</item>
<item>VS_IOT_EXTENSIONS_VERSION</item>
<item>VS_IOT_STARTUP_TASK</item>
+ <item>VS_JUST_MY_CODE_DEBUGGING</item>
<item>VS_KEYWORD</item>
<item>VS_MOBILE_EXTENSIONS_VERSION</item>
+ <item>VS_NO_COMPILE_BATCHING</item>
+ <item>VS_NO_SOLUTION_DEPLOY</item>
+ <item>VS_PACKAGE_REFERENCES</item>
+ <item>VS_PLATFORM_TOOLSET</item>
+ <item>VS_PROJECT_IMPORT</item>
<item>VS_SCC_AUXPATH</item>
<item>VS_SCC_LOCALPATH</item>
<item>VS_SCC_PROJECTNAME</item>
<item>VS_SCC_PROVIDER</item>
<item>VS_SDK_REFERENCES</item>
+ <item>VS_SOLUTION_DEPLOY</item>
<item>VS_USER_PROPS</item>
<item>VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION</item>
<item>VS_WINRT_COMPONENT</item>
<item>VS_WINRT_EXTENSIONS</item>
<item>VS_WINRT_REFERENCES</item>
+ <item>WATCOM_RUNTIME_LIBRARY</item>
<item>WIN32_EXECUTABLE</item>
<item>WINDOWS_EXPORT_ALL_SYMBOLS</item>
<item>XCODE_EXPLICIT_FILE_TYPE</item>
+ <item>XCODE_GENERATE_SCHEME</item>
+ <item>XCODE_LINK_BUILD_PHASE_MODE</item>
<item>XCODE_PRODUCT_TYPE</item>
<item>XCODE_SCHEME_ADDRESS_SANITIZER</item>
<item>XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN</item>
<item>XCODE_SCHEME_ARGUMENTS</item>
+ <item>XCODE_SCHEME_DEBUG_AS_ROOT</item>
+ <item>XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING</item>
<item>XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER</item>
<item>XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS</item>
<item>XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE</item>
+ <item>XCODE_SCHEME_ENABLE_GPU_API_VALIDATION</item>
+ <item>XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE</item>
+ <item>XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION</item>
<item>XCODE_SCHEME_ENVIRONMENT</item>
<item>XCODE_SCHEME_EXECUTABLE</item>
<item>XCODE_SCHEME_GUARD_MALLOC</item>
+ <item>XCODE_SCHEME_LAUNCH_CONFIGURATION</item>
+ <item>XCODE_SCHEME_LAUNCH_MODE</item>
<item>XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP</item>
<item>XCODE_SCHEME_MALLOC_GUARD_EDGES</item>
<item>XCODE_SCHEME_MALLOC_SCRIBBLE</item>
@@ -2282,7 +3994,9 @@
<item>XCODE_SCHEME_THREAD_SANITIZER_STOP</item>
<item>XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER</item>
<item>XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP</item>
+ <item>XCODE_SCHEME_WORKING_DIRECTORY</item>
<item>XCODE_SCHEME_ZOMBIE_OBJECTS</item>
+ <item>XCODE_XCCONFIG</item>
<item>XCTEST</item>
</list>
<list name="source-properties">
@@ -2292,8 +4006,10 @@
<item>COMPILE_DEFINITIONS</item>
<item>COMPILE_FLAGS</item>
<item>COMPILE_OPTIONS</item>
+ <item>CXX_SCAN_FOR_MODULES</item>
<item>EXTERNAL_OBJECT</item>
<item>Fortran_FORMAT</item>
+ <item>Fortran_PREPROCESS</item>
<item>GENERATED</item>
<item>HEADER_FILE_ONLY</item>
<item>INCLUDE_DIRECTORIES</item>
@@ -2308,12 +4024,18 @@
<item>SKIP_AUTOMOC</item>
<item>SKIP_AUTORCC</item>
<item>SKIP_AUTOUIC</item>
+ <item>SKIP_LINTING</item>
+ <item>SKIP_PRECOMPILE_HEADERS</item>
+ <item>SKIP_UNITY_BUILD_INCLUSION</item>
<item>SYMBOLIC</item>
+ <item>Swift_DEPENDENCIES_FILE</item>
+ <item>Swift_DIAGNOSTICS_FILE</item>
<item>VS_COPY_TO_OUT_DIR</item>
<item>VS_DEPLOYMENT_CONTENT</item>
<item>VS_DEPLOYMENT_LOCATION</item>
<item>VS_INCLUDE_IN_VSIX</item>
<item>VS_RESOURCE_GENERATOR</item>
+ <item>VS_SETTINGS</item>
<item>VS_SHADER_DISABLE_OPTIMIZATIONS</item>
<item>VS_SHADER_ENABLE_DEBUG</item>
<item>VS_SHADER_ENTRYPOINT</item>
@@ -2337,21 +4059,27 @@
<item>DEPENDS</item>
<item>DISABLED</item>
<item>ENVIRONMENT</item>
+ <item>ENVIRONMENT_MODIFICATION</item>
<item>FAIL_REGULAR_EXPRESSION</item>
<item>FIXTURES_CLEANUP</item>
<item>FIXTURES_REQUIRED</item>
<item>FIXTURES_SETUP</item>
+ <item>GENERATED_RESOURCE_SPEC_FILE</item>
<item>LABELS</item>
<item>MEASUREMENT</item>
<item>PASS_REGULAR_EXPRESSION</item>
<item>PROCESSORS</item>
<item>PROCESSOR_AFFINITY</item>
<item>REQUIRED_FILES</item>
+ <item>RESOURCE_GROUPS</item>
<item>RESOURCE_LOCK</item>
<item>RUN_SERIAL</item>
+ <item>SKIP_REGULAR_EXPRESSION</item>
<item>SKIP_RETURN_CODE</item>
<item>TIMEOUT</item>
<item>TIMEOUT_AFTER_MATCH</item>
+ <item>TIMEOUT_SIGNAL_GRACE_PERIOD</item>
+ <item>TIMEOUT_SIGNAL_NAME</item>
<item>WILL_FAIL</item>
<item>WORKING_DIRECTORY</item>
</list>
@@ -2373,1482 +4101,2737 @@
</list>
<list name="generator-expressions">
+ <item>IF</item>
<item>0</item>
<item>1</item>
- <item>AND</item>
- <item>ANGLE-R</item>
<item>BOOL</item>
- <item>BUILD_INTERFACE</item>
- <item>COMMA</item>
- <item>COMPILE_FEATURES</item>
- <item>COMPILE_LANGUAGE</item>
- <item>CONFIG</item>
- <item>CXX_COMPILER_ID</item>
- <item>CXX_COMPILER_VERSION</item>
- <item>C_COMPILER_ID</item>
- <item>C_COMPILER_VERSION</item>
+ <item>AND</item>
+ <item>OR</item>
+ <item>NOT</item>
+ <item>STREQUAL</item>
<item>EQUAL</item>
- <item>GENEX_EVAL</item>
- <item>IF</item>
- <item>INSTALL_INTERFACE</item>
- <item>INSTALL_PREFIX</item>
- <item>IN_LIST</item>
- <item>JOIN</item>
- <item>LINK_ONLY</item>
+ <item>VERSION_LESS</item>
+ <item>VERSION_GREATER</item>
+ <item>VERSION_EQUAL</item>
+ <item>VERSION_LESS_EQUAL</item>
+ <item>VERSION_GREATER_EQUAL</item>
<item>LOWER_CASE</item>
+ <item>UPPER_CASE</item>
<item>MAKE_C_IDENTIFIER</item>
- <item>NOT</item>
- <item>OR</item>
- <item>PLATFORM_ID</item>
- <item>SEMICOLON</item>
+ <item>IN_LIST</item>
+ <item>JOIN</item>
+ <item>REMOVE_DUPLICATES</item>
+ <item>FILTER</item>
+ <item>PATH_EQUAL</item>
<item>SHELL_PATH</item>
- <item>STREQUAL</item>
- <item>TARGET_BUNDLE_CONTENT_DIR</item>
- <item>TARGET_BUNDLE_DIR</item>
+ <item>CONFIG</item>
+ <item>OUTPUT_CONFIG</item>
+ <item>COMMAND_CONFIG</item>
+ <item>PLATFORM_ID</item>
+ <item>C_COMPILER_VERSION</item>
+ <item>CXX_COMPILER_VERSION</item>
+ <item>CUDA_COMPILER_VERSION</item>
+ <item>OBJC_COMPILER_VERSION</item>
+ <item>OBJCXX_COMPILER_VERSION</item>
+ <item>Fortran_COMPILER_VERSION</item>
+ <item>HIP_COMPILER_VERSION</item>
+ <item>ISPC_COMPILER_VERSION</item>
+ <item>C_COMPILER_ID</item>
+ <item>CXX_COMPILER_ID</item>
+ <item>CUDA_COMPILER_ID</item>
+ <item>OBJC_COMPILER_ID</item>
+ <item>OBJCXX_COMPILER_ID</item>
+ <item>Fortran_COMPILER_ID</item>
+ <item>HIP_COMPILER_ID</item>
+ <item>ISPC_COMPILER_ID</item>
+ <item>COMPILE_LANGUAGE</item>
+ <item>COMPILE_LANG_AND_ID</item>
+ <item>COMPILE_FEATURES</item>
+ <item>COMPILE_ONLY</item>
+ <item>LINK_LANGUAGE</item>
+ <item>LINK_LANG_AND_ID</item>
+ <item>LINK_LIBRARY</item>
+ <item>LINK_GROUP</item>
+ <item>LINK_ONLY</item>
+ <item>DEVICE_LINK</item>
+ <item>HOST_LINK</item>
<item>TARGET_EXISTS</item>
+ <item>TARGET_NAME_IF_EXISTS</item>
+ <item>TARGET_NAME</item>
+ <item>TARGET_PROPERTY</item>
+ <item>TARGET_OBJECTS</item>
+ <item>TARGET_POLICY</item>
<item>TARGET_FILE</item>
- <item>TARGET_FILE_DIR</item>
+ <item>TARGET_FILE_BASE_NAME</item>
+ <item>TARGET_FILE_PREFIX</item>
+ <item>TARGET_FILE_SUFFIX</item>
<item>TARGET_FILE_NAME</item>
- <item>TARGET_GENEX_EVAL</item>
+ <item>TARGET_FILE_DIR</item>
+ <item>TARGET_IMPORT_FILE</item>
+ <item>TARGET_IMPORT_FILE_BASE_NAME</item>
+ <item>TARGET_IMPORT_FILE_PREFIX</item>
+ <item>TARGET_IMPORT_FILE_SUFFIX</item>
+ <item>TARGET_IMPORT_FILE_NAME</item>
+ <item>TARGET_IMPORT_FILE_DIR</item>
<item>TARGET_LINKER_FILE</item>
- <item>TARGET_LINKER_FILE_DIR</item>
+ <item>TARGET_LINKER_FILE_BASE_NAME</item>
+ <item>TARGET_LINKER_FILE_PREFIX</item>
+ <item>TARGET_LINKER_FILE_SUFFIX</item>
<item>TARGET_LINKER_FILE_NAME</item>
- <item>TARGET_NAME</item>
- <item>TARGET_NAME_IF_EXISTS</item>
- <item>TARGET_OBJECTS</item>
- <item>TARGET_PDB_FILE</item>
- <item>TARGET_PDB_FILE_DIR</item>
- <item>TARGET_PDB_FILE_NAME</item>
- <item>TARGET_POLICY</item>
- <item>TARGET_PROPERTY</item>
+ <item>TARGET_LINKER_FILE_DIR</item>
<item>TARGET_SONAME_FILE</item>
- <item>TARGET_SONAME_FILE_DIR</item>
<item>TARGET_SONAME_FILE_NAME</item>
- <item>UPPER_CASE</item>
- <item>VERSION_EQUAL</item>
- <item>VERSION_GREATER</item>
- <item>VERSION_GREATER_EQUAL</item>
- <item>VERSION_LESS</item>
- <item>VERSION_LESS_EQUAL</item>
+ <item>TARGET_SONAME_FILE_DIR</item>
+ <item>TARGET_PDB_FILE</item>
+ <item>TARGET_PDB_FILE_BASE_NAME</item>
+ <item>TARGET_PDB_FILE_NAME</item>
+ <item>TARGET_PDB_FILE_DIR</item>
+ <item>TARGET_BUNDLE_DIR_NAME</item>
+ <item>TARGET_BUNDLE_DIR</item>
+ <item>TARGET_BUNDLE_CONTENT_DIR</item>
+ <item>TARGET_RUNTIME_DLLS</item>
+ <item>TARGET_RUNTIME_DLL_DIRS</item>
+ <item>INSTALL_INTERFACE</item>
+ <item>BUILD_INTERFACE</item>
+ <item>BUILD_LOCAL_INTERFACE</item>
+ <item>INSTALL_PREFIX</item>
+ <item>GENEX_EVAL</item>
+ <item>TARGET_GENEX_EVAL</item>
+ <item>ANGLE-R</item>
+ <item>COMMA</item>
+ <item>SEMICOLON</item>
+ </list>
+ <list name="genex-LIST-subcommands">
+ <item>LENGTH</item>
+ <item>GET</item>
+ <item>SUBLIST</item>
+ <item>FIND</item>
+ <item>JOIN</item>
+ <item>APPEND</item>
+ <item>PREPEND</item>
+ <item>INSERT</item>
+ <item>POP_BACK</item>
+ <item>POP_FRONT</item>
+ <item>REMOVE_ITEM</item>
+ <item>REMOVE_AT</item>
+ <item>REMOVE_DUPLICATES</item>
+ <item>FILTER</item>
+ <item>TRANSFORM</item>
+ <item>FRANSFORM</item>
+ <item>REVERSE</item>
+ <item>SORT</item>
+ </list>
+ <list name="genex-PATH-subcommands">
+ <item>HAS_ROOT_NAME</item>
+ <item>HAS_ROOT_DIRECTORY</item>
+ <item>HAS_ROOT_PATH</item>
+ <item>HAS_FILENAME</item>
+ <item>HAS_EXTENSION</item>
+ <item>HAS_STEM</item>
+ <item>HAS_RELATIVE_PART</item>
+ <item>HAS_PARENT_PATH</item>
+ <item>IS_ABSOLUTE</item>
+ <item>IS_RELATIVE</item>
+ <item>IS_PREFIX</item>
+ <item>GET_ROOT_NAME</item>
+ <item>GET_ROOT_DIRECTORY</item>
+ <item>GET_ROOT_PATH</item>
+ <item>GET_FILENAME</item>
+ <item>GET_EXTENSION</item>
+ <item>GET_STEM</item>
+ <item>GET_RELATIVE_PART</item>
+ <item>GET_PARENT_PATH</item>
+ <item>CMAKE_PATH</item>
+ <item>APPEND</item>
+ <item>REMOVE_FILENAME</item>
+ <item>REPLACE_FILENAME</item>
+ <item>REMOVE_EXTENSION</item>
+ <item>REPLACE_EXTENSION</item>
+ <item>NORMAL_PATH</item>
+ <item>RELATIVE_PATH</item>
+ <item>ABSOLUTE_PATH</item>
+ </list>
+
+ <list name="standard-modules">
+ <item>AndroidTestUtilities</item>
+ <item>BundleUtilities</item>
+ <item>CheckCCompilerFlag</item>
+ <item>CheckCompilerFlag</item>
+ <item>CheckCSourceCompiles</item>
+ <item>CheckCSourceRuns</item>
+ <item>CheckCXXCompilerFlag</item>
+ <item>CheckCXXSourceCompiles</item>
+ <item>CheckCXXSourceRuns</item>
+ <item>CheckCXXSymbolExists</item>
+ <item>CheckFortranCompilerFlag</item>
+ <item>CheckFortranFunctionExists</item>
+ <item>CheckFortranSourceCompiles</item>
+ <item>CheckFortranSourceRuns</item>
+ <item>CheckFunctionExists</item>
+ <item>CheckIncludeFileCXX</item>
+ <item>CheckIncludeFile</item>
+ <item>CheckIncludeFiles</item>
+ <item>CheckIPOSupported</item>
+ <item>CheckLanguage</item>
+ <item>CheckLibraryExists</item>
+ <item>CheckLinkerFlag</item>
+ <item>CheckOBJCCompilerFlag</item>
+ <item>CheckOBJCSourceCompiles</item>
+ <item>CheckOBJCSourceRuns</item>
+ <item>CheckOBJCXXCompilerFlag</item>
+ <item>CheckOBJCXXSourceCompiles</item>
+ <item>CheckOBJCXXSourceRuns</item>
+ <item>CheckPIESupported</item>
+ <item>CheckPrototypeDefinition</item>
+ <item>CheckSourceCompiles</item>
+ <item>CheckSourceRuns</item>
+ <item>CheckStructHasMember</item>
+ <item>CheckSymbolExists</item>
+ <item>CheckTypeSize</item>
+ <item>CheckVariableExists</item>
+ <item>CMakeAddFortranSubdirectory</item>
+ <item>CMakeBackwardCompatibilityCXX</item>
+ <item>CMakeDependentOption</item>
+ <item>CMakeFindDependencyMacro</item>
+ <item>CMakeFindFrameworks</item>
+ <item>CMakePackageConfigHelpers</item>
+ <item>CMakePrintHelpers</item>
+ <item>CMakePrintSystemInformation</item>
+ <item>CMakePushCheckState</item>
+ <item>CPack</item>
+ <item>CPackComponent</item>
+ <item>CPackIFW</item>
+ <item>CPackIFWConfigureFile</item>
+ <item>CSharpUtilities</item>
+ <item>CTest</item>
+ <item>CTestCoverageCollectGCOV</item>
+ <item>CTestScriptMode</item>
+ <item>CTestUseLaunchers</item>
+ <item>DeployQt4</item>
+ <item>ExternalData</item>
+ <item>ExternalProject</item>
+ <item>FeatureSummary</item>
+ <item>FetchContent</item>
+ <item>FindPackageHandleStandardArgs</item>
+ <item>FindPackageMessage</item>
+ <item>FortranCInterface</item>
+ <item>GenerateExportHeader</item>
+ <item>GNUInstallDirs</item>
+ <item>GoogleTest</item>
+ <item>InstallRequiredSystemLibraries</item>
+ <item>ProcessorCount</item>
+ <item>SelectLibraryConfigurations</item>
+ <item>TestForANSIForScope</item>
+ <item>TestForANSIStreamHeaders</item>
+ <item>TestForSSTREAM</item>
+ <item>TestForSTDNamespace</item>
+ <item>UseEcos</item>
+ <item>UseJava</item>
+ <item>UseSWIG</item>
+ <item>UsewxWidgets</item>
+ </list>
+
+ <list name="standard-finder-modules">
+ <item>ALSA</item>
+ <item>Armadillo</item>
+ <item>ASPELL</item>
+ <item>AVIFile</item>
+ <item>Backtrace</item>
+ <item>BISON</item>
+ <item>BLAS</item>
+ <item>Boost</item>
+ <item>Bullet</item>
+ <item>BZip2</item>
+ <item>CABLE</item>
+ <item>Coin3D</item>
+ <item>CUDAToolkit</item>
+ <item>Cups</item>
+ <item>CURL</item>
+ <item>Curses</item>
+ <item>CVS</item>
+ <item>CxxTest</item>
+ <item>Cygwin</item>
+ <item>DCMTK</item>
+ <item>DevIL</item>
+ <item>Doxygen</item>
+ <item>EnvModules</item>
+ <item>EXPAT</item>
+ <item>FLEX</item>
+ <item>FLTK</item>
+ <item>FLTK2</item>
+ <item>Fontconfig</item>
+ <item>Freetype</item>
+ <item>GCCXML</item>
+ <item>GDAL</item>
+ <item>Gettext</item>
+ <item>GIF</item>
+ <item>Git</item>
+ <item>GLEW</item>
+ <item>GLUT</item>
+ <item>Gnuplot</item>
+ <item>GnuTLS</item>
+ <item>GSL</item>
+ <item>GTest</item>
+ <item>GTK</item>
+ <item>GTK2</item>
+ <item>HDF5</item>
+ <item>Hg</item>
+ <item>HSPELL</item>
+ <item>HTMLHelp</item>
+ <item>Ice</item>
+ <item>Iconv</item>
+ <item>Icotool</item>
+ <item>ICU</item>
+ <item>ImageMagick</item>
+ <item>Intl</item>
+ <item>ITK</item>
+ <item>Jasper</item>
+ <item>Java</item>
+ <item>JNI</item>
+ <item>JPEG</item>
+ <item>KDE3</item>
+ <item>KDE4</item>
+ <item>LAPACK</item>
+ <item>LATEX</item>
+ <item>LibArchive</item>
+ <item>Libinput</item>
+ <item>LibLZMA</item>
+ <item>LibXml2</item>
+ <item>LibXslt</item>
+ <item>LTTngUST</item>
+ <item>Lua</item>
+ <item>Lua50</item>
+ <item>Lua51</item>
+ <item>Matlab</item>
+ <item>MFC</item>
+ <item>Motif</item>
+ <item>MPEG</item>
+ <item>MPEG2</item>
+ <item>MPI</item>
+ <item>Msys</item>
+ <item>ODBC</item>
+ <item>OpenACC</item>
+ <item>OpenAL</item>
+ <item>OpenCL</item>
+ <item>OpenGL</item>
+ <item>OpenMP</item>
+ <item>OpenSceneGraph</item>
+ <item>OpenSSL</item>
+ <item>OpenThreads</item>
+ <item>osg</item>
+ <item>osg_functions</item>
+ <item>osgAnimation</item>
+ <item>osgDB</item>
+ <item>osgFX</item>
+ <item>osgGA</item>
+ <item>osgIntrospection</item>
+ <item>osgManipulator</item>
+ <item>osgParticle</item>
+ <item>osgPresentation</item>
+ <item>osgProducer</item>
+ <item>osgQt</item>
+ <item>osgShadow</item>
+ <item>osgSim</item>
+ <item>osgTerrain</item>
+ <item>osgText</item>
+ <item>osgUtil</item>
+ <item>osgViewer</item>
+ <item>osgVolume</item>
+ <item>osgWidget</item>
+ <item>Patch</item>
+ <item>Perl</item>
+ <item>PerlLibs</item>
+ <item>PHP4</item>
+ <item>PhysFS</item>
+ <item>Pike</item>
+ <item>PkgConfig</item>
+ <item>PNG</item>
+ <item>PostgreSQL</item>
+ <item>Producer</item>
+ <item>Protobuf</item>
+ <item>Python</item>
+ <item>Python2</item>
+ <item>Python3</item>
+ <item>Qt3</item>
+ <item>Qt4</item>
+ <item>QuickTime</item>
+ <item>RTI</item>
+ <item>Ruby</item>
+ <item>SDL</item>
+ <item>SDL_image</item>
+ <item>SDL_mixer</item>
+ <item>SDL_net</item>
+ <item>SDL_sound</item>
+ <item>SDL_ttf</item>
+ <item>SelfPackers</item>
+ <item>Squish</item>
+ <item>SQLite3</item>
+ <item>Subversion</item>
+ <item>SWIG</item>
+ <item>TCL</item>
+ <item>Tclsh</item>
+ <item>TclStub</item>
+ <item>Threads</item>
+ <item>TIFF</item>
+ <item>UnixCommands</item>
+ <item>VTK</item>
+ <item>Vulkan</item>
+ <item>Wget</item>
+ <item>Wish</item>
+ <item>wxWidgets</item>
+ <item>X11</item>
+ <item>XalanC</item>
+ <item>XCTest</item>
+ <item>XercesC</item>
+ <item>XMLRPC</item>
+ <item>ZLIB</item>
+ </list>
+
+ <list name="deprecated-modules">
+ <item>AddFileDependencies</item>
+ <item>CMakeDetermineVSServicePack</item>
+ <item>CMakeExpandImportedTargets</item>
+ <item>CMakeForceCompiler</item>
+ <item>CMakeParseArguments</item>
+ <item>Dart</item>
+ <item>Documentation</item>
+ <item>MacroAddFileDependencies</item>
+ <item>TestCXXAcceptsFlag</item>
+ <item>UseJavaClassFilelist</item>
+ <item>UseJavaSymlinks</item>
+ <item>UsePkgConfig</item>
+ <item>Use_wxWindows</item>
+ <item>WriteBasicConfigVersionFile</item>
+ <item>WriteCompilerDetectionHeader</item>
+ <item>FindCUDA</item>
+ <item>FindDart</item>
+ <item>FindPythonInterp</item>
+ <item>FindPythonLibs</item>
+ <item>FindQt</item>
+ <item>FindwxWindows</item>
+ <item>CPackArchive</item>
+ <item>CPackBundle</item>
+ <item>CPackCygwin</item>
+ <item>CPackDeb</item>
+ <item>CPackDMG</item>
+ <item>CPackFreeBSD</item>
+ <item>CPackNSIS</item>
+ <item>CPackNuGet</item>
+ <item>CPackPackageMaker</item>
+ <item>CPackProductBuild</item>
+ <item>CPackRPM</item>
+ <item>CPackWIX</item>
+ <item>GetPrerequisites</item>
+ <item>TestBigEndian</item>
+ </list>
+
+
+<!-- Source/cmStringAlgorithms.cxx: bool cmIsOff(cm::string_view val) -->
+ <list name="true_special_arg">
+ <item>TRUE</item>
+ <item>ON</item>
+ <item>YES</item>
+ <item>Y</item>
+ <item>0</item>
+ </list>
+
+
+<!-- Source/cmStringAlgorithms.cxx: bool cmIsOff(cm::string_view val) -->
+ <list name="false_special_arg">
+ <item>FALSE</item>
+ <item>OFF</item>
+ <item>NO</item>
+ <item>IGNORE</item>
+ <item>N</item>
+ <item>0</item>
</list>
<contexts>
<context attribute="Normal Text" lineEndContext="#stay" name="Normal Text">
<DetectSpaces/>
- <WordDetect String="break" insensitive="true" attribute="Command" context="break_ctx" />
- <WordDetect String="cmake_host_system_information" insensitive="true" attribute="Command" context="cmake_host_system_information_ctx" />
- <WordDetect String="cmake_minimum_required" insensitive="true" attribute="Command" context="cmake_minimum_required_ctx" />
- <WordDetect String="cmake_parse_arguments" insensitive="true" attribute="Command" context="cmake_parse_arguments_ctx" />
- <WordDetect String="cmake_policy" insensitive="true" attribute="Command" context="cmake_policy_ctx" />
- <WordDetect String="configure_file" insensitive="true" attribute="Command" context="configure_file_ctx" />
- <WordDetect String="continue" insensitive="true" attribute="Command" context="continue_ctx" />
- <WordDetect String="elseif" insensitive="true" attribute="Command" context="elseif_ctx" />
- <WordDetect String="else" insensitive="true" attribute="Command" context="else_ctx" />
- <WordDetect String="endforeach" insensitive="true" attribute="Command" context="endforeach_ctx" />
- <WordDetect String="endfunction" insensitive="true" attribute="Command" context="endfunction_ctx" />
- <WordDetect String="endif" insensitive="true" attribute="Command" context="endif_ctx" />
- <WordDetect String="endmacro" insensitive="true" attribute="Command" context="endmacro_ctx" />
- <WordDetect String="endwhile" insensitive="true" attribute="Command" context="endwhile_ctx" />
- <WordDetect String="execute_process" insensitive="true" attribute="Command" context="execute_process_ctx" />
- <WordDetect String="file" insensitive="true" attribute="Command" context="file_ctx" />
- <WordDetect String="find_file" insensitive="true" attribute="Command" context="find_file_ctx" />
- <WordDetect String="find_library" insensitive="true" attribute="Command" context="find_library_ctx" />
- <WordDetect String="find_package" insensitive="true" attribute="Command" context="find_package_ctx" />
- <WordDetect String="find_path" insensitive="true" attribute="Command" context="find_path_ctx" />
- <WordDetect String="find_program" insensitive="true" attribute="Command" context="find_program_ctx" />
- <WordDetect String="foreach" insensitive="true" attribute="Command" context="foreach_ctx" />
- <WordDetect String="function" insensitive="true" attribute="Command" context="function_ctx" />
- <WordDetect String="get_cmake_property" insensitive="true" attribute="Command" context="get_cmake_property_ctx" />
- <WordDetect String="get_directory_property" insensitive="true" attribute="Command" context="get_directory_property_ctx" />
- <WordDetect String="get_filename_component" insensitive="true" attribute="Command" context="get_filename_component_ctx" />
- <WordDetect String="get_property" insensitive="true" attribute="Command" context="get_property_ctx" />
- <WordDetect String="if" insensitive="true" attribute="Command" context="if_ctx" />
- <WordDetect String="include" insensitive="true" attribute="Command" context="include_ctx" />
- <WordDetect String="include_guard" insensitive="true" attribute="Command" context="include_guard_ctx" />
- <WordDetect String="list" insensitive="true" attribute="Command" context="list_ctx" />
- <WordDetect String="macro" insensitive="true" attribute="Command" context="macro_ctx" />
- <WordDetect String="mark_as_advanced" insensitive="true" attribute="Command" context="mark_as_advanced_ctx" />
- <WordDetect String="math" insensitive="true" attribute="Command" context="math_ctx" />
- <WordDetect String="message" insensitive="true" attribute="Command" context="message_ctx" />
- <WordDetect String="option" insensitive="true" attribute="Command" context="option_ctx" />
- <WordDetect String="return" insensitive="true" attribute="Command" context="return_ctx" />
- <WordDetect String="separate_arguments" insensitive="true" attribute="Command" context="separate_arguments_ctx" />
- <WordDetect String="set_directory_properties" insensitive="true" attribute="Command" context="set_directory_properties_ctx" />
- <WordDetect String="set_property" insensitive="true" attribute="Command" context="set_property_ctx" />
- <WordDetect String="set" insensitive="true" attribute="Command" context="set_ctx" />
- <WordDetect String="site_name" insensitive="true" attribute="Command" context="site_name_ctx" />
- <WordDetect String="string" insensitive="true" attribute="Command" context="string_ctx" />
- <WordDetect String="unset" insensitive="true" attribute="Command" context="unset_ctx" />
- <WordDetect String="variable_watch" insensitive="true" attribute="Command" context="variable_watch_ctx" />
- <WordDetect String="while" insensitive="true" attribute="Command" context="while_ctx" />
- <WordDetect String="add_compile_definitions" insensitive="true" attribute="Command" context="add_compile_definitions_ctx" />
- <WordDetect String="add_compile_options" insensitive="true" attribute="Command" context="add_compile_options_ctx" />
- <WordDetect String="add_custom_command" insensitive="true" attribute="Command" context="add_custom_command_ctx" />
- <WordDetect String="add_custom_target" insensitive="true" attribute="Command" context="add_custom_target_ctx" />
- <WordDetect String="add_definitions" insensitive="true" attribute="Command" context="add_definitions_ctx" />
- <WordDetect String="add_dependencies" insensitive="true" attribute="Command" context="add_dependencies_ctx" />
- <WordDetect String="add_executable" insensitive="true" attribute="Command" context="add_executable_ctx" />
- <WordDetect String="add_library" insensitive="true" attribute="Command" context="add_library_ctx" />
- <WordDetect String="add_link_options" insensitive="true" attribute="Command" context="add_link_options_ctx" />
- <WordDetect String="add_subdirectory" insensitive="true" attribute="Command" context="add_subdirectory_ctx" />
- <WordDetect String="add_test" insensitive="true" attribute="Command" context="add_test_ctx" />
- <WordDetect String="aux_source_directory" insensitive="true" attribute="Command" context="aux_source_directory_ctx" />
- <WordDetect String="build_command" insensitive="true" attribute="Command" context="build_command_ctx" />
- <WordDetect String="create_test_sourcelist" insensitive="true" attribute="Command" context="create_test_sourcelist_ctx" />
- <WordDetect String="define_property" insensitive="true" attribute="Command" context="define_property_ctx" />
- <WordDetect String="enable_language" insensitive="true" attribute="Command" context="enable_language_ctx" />
- <WordDetect String="enable_testing" insensitive="true" attribute="Command" context="enable_testing_ctx" />
- <WordDetect String="export" insensitive="true" attribute="Command" context="export_ctx" />
- <WordDetect String="fltk_wrap_ui" insensitive="true" attribute="Command" context="fltk_wrap_ui_ctx" />
- <WordDetect String="get_source_file_property" insensitive="true" attribute="Command" context="get_source_file_property_ctx" />
- <WordDetect String="get_target_property" insensitive="true" attribute="Command" context="get_target_property_ctx" />
- <WordDetect String="get_test_property" insensitive="true" attribute="Command" context="get_test_property_ctx" />
- <WordDetect String="include_directories" insensitive="true" attribute="Command" context="include_directories_ctx" />
- <WordDetect String="include_external_msproject" insensitive="true" attribute="Command" context="include_external_msproject_ctx" />
- <WordDetect String="include_regular_expression" insensitive="true" attribute="Command" context="include_regular_expression_ctx" />
- <WordDetect String="install" insensitive="true" attribute="Command" context="install_ctx" />
- <WordDetect String="link_directories" insensitive="true" attribute="Command" context="link_directories_ctx" />
- <WordDetect String="link_libraries" insensitive="true" attribute="Command" context="link_libraries_ctx" />
- <WordDetect String="load_cache" insensitive="true" attribute="Command" context="load_cache_ctx" />
- <WordDetect String="project" insensitive="true" attribute="Command" context="project_ctx" />
- <WordDetect String="qt_wrap_cpp" insensitive="true" attribute="Command" context="qt_wrap_cpp_ctx" />
- <WordDetect String="qt_wrap_ui" insensitive="true" attribute="Command" context="qt_wrap_ui_ctx" />
- <WordDetect String="remove_definitions" insensitive="true" attribute="Command" context="remove_definitions_ctx" />
- <WordDetect String="set_source_files_properties" insensitive="true" attribute="Command" context="set_source_files_properties_ctx" />
- <WordDetect String="set_target_properties" insensitive="true" attribute="Command" context="set_target_properties_ctx" />
- <WordDetect String="set_tests_properties" insensitive="true" attribute="Command" context="set_tests_properties_ctx" />
- <WordDetect String="source_group" insensitive="true" attribute="Command" context="source_group_ctx" />
- <WordDetect String="target_compile_definitions" insensitive="true" attribute="Command" context="target_compile_definitions_ctx" />
- <WordDetect String="target_compile_features" insensitive="true" attribute="Command" context="target_compile_features_ctx" />
- <WordDetect String="target_compile_options" insensitive="true" attribute="Command" context="target_compile_options_ctx" />
- <WordDetect String="target_include_directories" insensitive="true" attribute="Command" context="target_include_directories_ctx" />
- <WordDetect String="target_link_directories" insensitive="true" attribute="Command" context="target_link_directories_ctx" />
- <WordDetect String="target_link_libraries" insensitive="true" attribute="Command" context="target_link_libraries_ctx" />
- <WordDetect String="target_link_options" insensitive="true" attribute="Command" context="target_link_options_ctx" />
- <WordDetect String="target_sources" insensitive="true" attribute="Command" context="target_sources_ctx" />
- <WordDetect String="try_compile" insensitive="true" attribute="Command" context="try_compile_ctx" />
- <WordDetect String="try_run" insensitive="true" attribute="Command" context="try_run_ctx" />
- <WordDetect String="ctest_build" insensitive="true" attribute="Command" context="ctest_build_ctx" />
- <WordDetect String="ctest_configure" insensitive="true" attribute="Command" context="ctest_configure_ctx" />
- <WordDetect String="ctest_coverage" insensitive="true" attribute="Command" context="ctest_coverage_ctx" />
- <WordDetect String="ctest_empty_binary_directory" insensitive="true" attribute="Command" context="ctest_empty_binary_directory_ctx" />
- <WordDetect String="ctest_memcheck" insensitive="true" attribute="Command" context="ctest_memcheck_ctx" />
- <WordDetect String="ctest_read_custom_files" insensitive="true" attribute="Command" context="ctest_read_custom_files_ctx" />
- <WordDetect String="ctest_run_script" insensitive="true" attribute="Command" context="ctest_run_script_ctx" />
- <WordDetect String="ctest_sleep" insensitive="true" attribute="Command" context="ctest_sleep_ctx" />
- <WordDetect String="ctest_start" insensitive="true" attribute="Command" context="ctest_start_ctx" />
- <WordDetect String="ctest_submit" insensitive="true" attribute="Command" context="ctest_submit_ctx" />
- <WordDetect String="ctest_test" insensitive="true" attribute="Command" context="ctest_test_ctx" />
- <WordDetect String="ctest_update" insensitive="true" attribute="Command" context="ctest_update_ctx" />
- <WordDetect String="ctest_upload" insensitive="true" attribute="Command" context="ctest_upload_ctx" />
- <RegExpr attribute="Region Marker" context="RST Documentation" String="^#\[(=*)\[\.rst:" column="0" />
- <RegExpr attribute="Comment" context="Bracketed Comment" String="#\[(=*)\[" />
- <DetectChar attribute="Comment" context="Comment" char="#" />
- <DetectIdentifier attribute="User Function/Macro" context="User Function" />
- <RegExpr attribute="@Variable Substitution" context="@VarSubst" String="@&id_re;@" lookAhead="true" />
- <!-- Include keywords matching for language autocompleter work -->
- <keyword attribute="Command" context="#stay" String="commands" />
+ <WordDetect String="block" insensitive="true" attribute="Command" context="block_ctx" beginRegion="block"/>
+ <WordDetect String="break" insensitive="true" attribute="Control Flow" context="break_ctx"/>
+ <WordDetect String="cmake_host_system_information" insensitive="true" attribute="Command" context="cmake_host_system_information_ctx"/>
+ <WordDetect String="cmake_language" insensitive="true" attribute="Command" context="cmake_language_ctx"/>
+ <WordDetect String="cmake_minimum_required" insensitive="true" attribute="Command" context="cmake_minimum_required_ctx"/>
+ <WordDetect String="cmake_parse_arguments" insensitive="true" attribute="Command" context="cmake_parse_arguments_ctx"/>
+ <WordDetect String="cmake_path" insensitive="true" attribute="Command" context="cmake_path_ctx"/>
+ <WordDetect String="cmake_policy" insensitive="true" attribute="Command" context="cmake_policy_ctx"/>
+ <WordDetect String="configure_file" insensitive="true" attribute="Command" context="configure_file_ctx"/>
+ <WordDetect String="continue" insensitive="true" attribute="Control Flow" context="break_ctx"/>
+ <WordDetect String="elseif" insensitive="true" attribute="Control Flow" context="elseif_ctx"/>
+ <WordDetect String="else" insensitive="true" attribute="Control Flow" context="break_ctx"/>
+ <WordDetect String="endblock" insensitive="true" attribute="Command" context="break_ctx" endRegion="block"/>
+ <WordDetect String="endforeach" insensitive="true" attribute="Control Flow" context="break_ctx" endRegion="foreach"/>
+ <WordDetect String="endfunction" insensitive="true" attribute="Command" context="break_ctx" endRegion="function"/>
+ <WordDetect String="endif" insensitive="true" attribute="Control Flow" context="break_ctx" endRegion="if"/>
+ <WordDetect String="endmacro" insensitive="true" attribute="Command" context="break_ctx" endRegion="macro"/>
+ <WordDetect String="endwhile" insensitive="true" attribute="Control Flow" context="break_ctx" endRegion="while"/>
+ <WordDetect String="execute_process" insensitive="true" attribute="Command" context="execute_process_ctx"/>
+ <WordDetect String="file" insensitive="true" attribute="Command" context="file_ctx"/>
+ <WordDetect String="find_file" insensitive="true" attribute="Command" context="find_file_ctx"/>
+ <WordDetect String="find_library" insensitive="true" attribute="Command" context="find_library_ctx"/>
+ <WordDetect String="find_package" insensitive="true" attribute="Command" context="find_package_ctx"/>
+ <WordDetect String="find_path" insensitive="true" attribute="Command" context="find_file_ctx"/>
+ <WordDetect String="find_program" insensitive="true" attribute="Command" context="find_library_ctx"/>
+ <WordDetect String="foreach" insensitive="true" attribute="Control Flow" context="foreach_ctx" beginRegion="foreach"/>
+ <WordDetect String="function" insensitive="true" attribute="Command" context="function_ctx" beginRegion="function"/>
+ <WordDetect String="get_cmake_property" insensitive="true" attribute="Command" context="get_cmake_property_ctx"/>
+ <WordDetect String="get_directory_property" insensitive="true" attribute="Command" context="get_directory_property_ctx"/>
+ <WordDetect String="get_filename_component" insensitive="true" attribute="Command" context="get_filename_component_ctx"/>
+ <WordDetect String="get_property" insensitive="true" attribute="Command" context="get_property_ctx"/>
+ <WordDetect String="if" insensitive="true" attribute="Control Flow" context="if_ctx" beginRegion="if"/>
+ <WordDetect String="include" insensitive="true" attribute="Command" context="include_ctx"/>
+ <WordDetect String="include_guard" insensitive="true" attribute="Command" context="include_guard_ctx"/>
+ <WordDetect String="list" insensitive="true" attribute="Command" context="list_ctx"/>
+ <WordDetect String="macro" insensitive="true" attribute="Command" context="function_ctx" beginRegion="macro"/>
+ <WordDetect String="mark_as_advanced" insensitive="true" attribute="Command" context="mark_as_advanced_ctx"/>
+ <WordDetect String="math" insensitive="true" attribute="Command" context="math_ctx"/>
+ <WordDetect String="message" insensitive="true" attribute="Command" context="message_ctx"/>
+ <WordDetect String="option" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="return" insensitive="true" attribute="Control Flow" context="return_ctx"/>
+ <WordDetect String="separate_arguments" insensitive="true" attribute="Command" context="separate_arguments_ctx"/>
+ <WordDetect String="set_directory_properties" insensitive="true" attribute="Command" context="set_directory_properties_ctx"/>
+ <WordDetect String="set_property" insensitive="true" attribute="Command" context="set_property_ctx"/>
+ <WordDetect String="set" insensitive="true" attribute="Command" context="set_ctx"/>
+ <WordDetect String="site_name" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="string" insensitive="true" attribute="Command" context="string_ctx"/>
+ <WordDetect String="unset" insensitive="true" attribute="Command" context="unset_ctx"/>
+ <WordDetect String="variable_watch" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="while" insensitive="true" attribute="Control Flow" context="while_ctx" beginRegion="while"/>
+ <WordDetect String="add_compile_definitions" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="add_compile_options" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="add_custom_command" insensitive="true" attribute="Command" context="add_custom_command_ctx"/>
+ <WordDetect String="add_custom_target" insensitive="true" attribute="Command" context="add_custom_target_ctx"/>
+ <WordDetect String="add_definitions" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="add_dependencies" insensitive="true" attribute="Command" context="add_dependencies_ctx"/>
+ <WordDetect String="add_executable" insensitive="true" attribute="Command" context="add_executable_ctx"/>
+ <WordDetect String="add_library" insensitive="true" attribute="Command" context="add_library_ctx"/>
+ <WordDetect String="add_link_options" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="add_subdirectory" insensitive="true" attribute="Command" context="add_subdirectory_ctx"/>
+ <WordDetect String="add_test" insensitive="true" attribute="Command" context="add_test_ctx"/>
+ <WordDetect String="aux_source_directory" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="build_command" insensitive="true" attribute="Command" context="build_command_ctx"/>
+ <WordDetect String="cmake_file_api" insensitive="true" attribute="Command" context="cmake_file_api_ctx"/>
+ <WordDetect String="create_test_sourcelist" insensitive="true" attribute="Command" context="create_test_sourcelist_ctx"/>
+ <WordDetect String="define_property" insensitive="true" attribute="Command" context="define_property_ctx"/>
+ <WordDetect String="enable_language" insensitive="true" attribute="Command" context="enable_language_ctx"/>
+ <WordDetect String="enable_testing" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="export" insensitive="true" attribute="Command" context="export_ctx"/>
+ <WordDetect String="fltk_wrap_ui" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="get_source_file_property" insensitive="true" attribute="Command" context="get_source_file_property_ctx"/>
+ <WordDetect String="get_target_property" insensitive="true" attribute="Command" context="get_target_property_ctx"/>
+ <WordDetect String="get_test_property" insensitive="true" attribute="Command" context="get_test_property_ctx"/>
+ <WordDetect String="include_directories" insensitive="true" attribute="Command" context="include_directories_ctx"/>
+ <WordDetect String="include_external_msproject" insensitive="true" attribute="Command" context="include_external_msproject_ctx"/>
+ <WordDetect String="include_regular_expression" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="install" insensitive="true" attribute="Command" context="install_ctx"/>
+ <WordDetect String="link_directories" insensitive="true" attribute="Command" context="link_directories_ctx"/>
+ <WordDetect String="link_libraries" insensitive="true" attribute="Command" context="link_libraries_ctx"/>
+ <WordDetect String="load_cache" insensitive="true" attribute="Command" context="load_cache_ctx"/>
+ <WordDetect String="project" insensitive="true" attribute="Command" context="project_ctx"/>
+ <WordDetect String="qt_wrap_cpp" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="qt_wrap_ui" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="remove_definitions" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="set_source_files_properties" insensitive="true" attribute="Command" context="set_source_files_properties_ctx"/>
+ <WordDetect String="set_target_properties" insensitive="true" attribute="Command" context="set_target_properties_ctx"/>
+ <WordDetect String="set_tests_properties" insensitive="true" attribute="Command" context="set_tests_properties_ctx"/>
+ <WordDetect String="source_group" insensitive="true" attribute="Command" context="source_group_ctx"/>
+ <WordDetect String="target_compile_definitions" insensitive="true" attribute="Command" context="target_compile_definitions_ctx"/>
+ <WordDetect String="target_compile_features" insensitive="true" attribute="Command" context="target_compile_features_ctx"/>
+ <WordDetect String="target_compile_options" insensitive="true" attribute="Command" context="target_compile_options_ctx"/>
+ <WordDetect String="target_include_directories" insensitive="true" attribute="Command" context="target_include_directories_ctx"/>
+ <WordDetect String="target_link_directories" insensitive="true" attribute="Command" context="target_compile_options_ctx"/>
+ <WordDetect String="target_link_libraries" insensitive="true" attribute="Command" context="target_link_libraries_ctx"/>
+ <WordDetect String="target_link_options" insensitive="true" attribute="Command" context="target_compile_definitions_ctx"/>
+ <WordDetect String="target_precompile_headers" insensitive="true" attribute="Command" context="target_precompile_headers_ctx"/>
+ <WordDetect String="target_sources" insensitive="true" attribute="Command" context="target_sources_ctx"/>
+ <WordDetect String="try_compile" insensitive="true" attribute="Command" context="try_compile_ctx"/>
+ <WordDetect String="try_run" insensitive="true" attribute="Command" context="try_run_ctx"/>
+ <WordDetect String="ctest_build" insensitive="true" attribute="Command" context="ctest_build_ctx"/>
+ <WordDetect String="ctest_configure" insensitive="true" attribute="Command" context="ctest_configure_ctx"/>
+ <WordDetect String="ctest_coverage" insensitive="true" attribute="Command" context="ctest_coverage_ctx"/>
+ <WordDetect String="ctest_empty_binary_directory" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="ctest_memcheck" insensitive="true" attribute="Command" context="ctest_memcheck_ctx"/>
+ <WordDetect String="ctest_read_custom_files" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="ctest_run_script" insensitive="true" attribute="Command" context="ctest_run_script_ctx"/>
+ <WordDetect String="ctest_sleep" insensitive="true" attribute="Command" context="function_ctx"/>
+ <WordDetect String="ctest_start" insensitive="true" attribute="Command" context="ctest_start_ctx"/>
+ <WordDetect String="ctest_submit" insensitive="true" attribute="Command" context="ctest_submit_ctx"/>
+ <WordDetect String="ctest_test" insensitive="true" attribute="Command" context="ctest_test_ctx"/>
+ <WordDetect String="ctest_update" insensitive="true" attribute="Command" context="ctest_update_ctx"/>
+ <WordDetect String="ctest_upload" insensitive="true" attribute="Command" context="ctest_upload_ctx"/>
+ <WordDetect String="android_add_test_data" insensitive="true" attribute="CMake Provided Function/Macro" context="android_add_test_data_ctx"/>
+ <WordDetect String="fixup_bundle" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="copy_and_fixup_bundle" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="verify_app" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="get_bundle_main_executable" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="get_dotapp_dir" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="get_bundle_and_executable" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="get_bundle_all_executables" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="get_item_key" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="get_item_rpaths" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="clear_bundle_keys" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="set_bundle_key_values" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="get_bundle_keys" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="copy_resolved_item_into_bundle" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="copy_resolved_framework_into_bundle" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="fixup_bundle_item" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="verify_bundle_prerequisites" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="verify_bundle_symlinks" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_c_compiler_flag" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_compiler_flag" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_c_source_compiles" insensitive="true" attribute="CMake Provided Function/Macro" context="check_c_source_compiles_ctx"/>
+ <WordDetect String="check_c_source_runs" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_cxx_compiler_flag" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_cxx_source_compiles" insensitive="true" attribute="CMake Provided Function/Macro" context="check_c_source_compiles_ctx"/>
+ <WordDetect String="check_cxx_source_runs" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_cxx_symbol_exists" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_fortran_compiler_flag" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_fortran_function_exists" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_fortran_source_compiles" insensitive="true" attribute="CMake Provided Function/Macro" context="check_fortran_source_compiles_ctx"/>
+ <WordDetect String="check_fortran_source_runs" insensitive="true" attribute="CMake Provided Function/Macro" context="check_fortran_source_runs_ctx"/>
+ <WordDetect String="check_function_exists" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_include_file_cxx" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_include_file" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_include_files" insensitive="true" attribute="CMake Provided Function/Macro" context="check_include_files_ctx"/>
+ <WordDetect String="check_ipo_supported" insensitive="true" attribute="CMake Provided Function/Macro" context="check_ipo_supported_ctx"/>
+ <WordDetect String="check_language" insensitive="true" attribute="CMake Provided Function/Macro" context="check_language_ctx"/>
+ <WordDetect String="check_library_exists" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_linker_flag" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_objc_compiler_flag" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_objc_source_compiles" insensitive="true" attribute="CMake Provided Function/Macro" context="check_c_source_compiles_ctx"/>
+ <WordDetect String="check_objc_source_runs" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_objcxx_compiler_flag" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_objcxx_source_compiles" insensitive="true" attribute="CMake Provided Function/Macro" context="check_c_source_compiles_ctx"/>
+ <WordDetect String="check_objcxx_source_runs" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_pie_supported" insensitive="true" attribute="CMake Provided Function/Macro" context="check_pie_supported_ctx"/>
+ <WordDetect String="check_prototype_definition" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_source_compiles" insensitive="true" attribute="CMake Provided Function/Macro" context="check_fortran_source_compiles_ctx"/>
+ <WordDetect String="check_source_runs" insensitive="true" attribute="CMake Provided Function/Macro" context="check_fortran_source_runs_ctx"/>
+ <WordDetect String="check_struct_has_member" insensitive="true" attribute="CMake Provided Function/Macro" context="check_include_files_ctx"/>
+ <WordDetect String="check_symbol_exists" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="check_type_size" insensitive="true" attribute="CMake Provided Function/Macro" context="check_type_size_ctx"/>
+ <WordDetect String="check_variable_exists" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="cmake_add_fortran_subdirectory" insensitive="true" attribute="CMake Provided Function/Macro" context="cmake_add_fortran_subdirectory_ctx"/>
+ <WordDetect String="cmake_dependent_option" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="find_dependency" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="configure_package_config_file" insensitive="true" attribute="CMake Provided Function/Macro" context="configure_package_config_file_ctx"/>
+ <WordDetect String="write_basic_package_version_file" insensitive="true" attribute="CMake Provided Function/Macro" context="write_basic_package_version_file_ctx"/>
+ <WordDetect String="cmake_print_properties" insensitive="true" attribute="CMake Provided Function/Macro" context="cmake_print_properties_ctx"/>
+ <WordDetect String="cmake_push_check_state" insensitive="true" attribute="CMake Provided Function/Macro" context="cmake_push_check_state_ctx"/>
+ <WordDetect String="cmake_pop_check_state" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="cmake_reset_check_state" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="cpack_add_component" insensitive="true" attribute="CMake Provided Function/Macro" context="cpack_add_component_ctx"/>
+ <WordDetect String="cpack_add_component_group" insensitive="true" attribute="CMake Provided Function/Macro" context="cpack_add_component_group_ctx"/>
+ <WordDetect String="cpack_add_install_type" insensitive="true" attribute="CMake Provided Function/Macro" context="cpack_add_install_type_ctx"/>
+ <WordDetect String="cpack_configure_downloads" insensitive="true" attribute="CMake Provided Function/Macro" context="cpack_configure_downloads_ctx"/>
+ <WordDetect String="cpack_ifw_configure_component" insensitive="true" attribute="CMake Provided Function/Macro" context="cpack_ifw_configure_component_ctx"/>
+ <WordDetect String="cpack_ifw_configure_component_group" insensitive="true" attribute="CMake Provided Function/Macro" context="cpack_ifw_configure_component_group_ctx"/>
+ <WordDetect String="cpack_ifw_add_repository" insensitive="true" attribute="CMake Provided Function/Macro" context="cpack_ifw_add_repository_ctx"/>
+ <WordDetect String="cpack_ifw_update_repository" insensitive="true" attribute="CMake Provided Function/Macro" context="cpack_ifw_update_repository_ctx"/>
+ <WordDetect String="cpack_ifw_add_package_resources" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="cpack_ifw_configure_file" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="csharp_set_windows_forms_properties" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="csharp_set_designer_cs_properties" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="csharp_set_xaml_cs_properties" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="csharp_get_filename_keys" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="csharp_get_filename_key_base" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="csharp_get_dependentupon_name" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ctest_coverage_collect_gcov" insensitive="true" attribute="CMake Provided Function/Macro" context="ctest_coverage_collect_gcov_ctx"/>
+ <WordDetect String="write_qt4_conf" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="resolve_qt4_paths" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="fixup_qt4_executable" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="install_qt4_plugin_path" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="install_qt4_plugin" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="install_qt4_executable" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ExternalData_Expand_Arguments" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ExternalData_Add_Test" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ExternalData_Add_Target" insensitive="true" attribute="CMake Provided Function/Macro" context="ExternalData_Add_Target_ctx"/>
+ <WordDetect String="ExternalProject_Add" insensitive="true" attribute="CMake Provided Function/Macro" context="ExternalProject_Add_ctx"/>
+ <WordDetect String="ExternalProject_Get_Property" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ExternalProject_Add_Step" insensitive="true" attribute="CMake Provided Function/Macro" context="ExternalProject_Add_Step_ctx"/>
+ <WordDetect String="ExternalProject_Add_StepTargets" insensitive="true" attribute="CMake Provided Function/Macro" context="ExternalProject_Add_StepTargets_ctx"/>
+ <WordDetect String="ExternalProject_Add_StepDependencies" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="feature_summary" insensitive="true" attribute="CMake Provided Function/Macro" context="feature_summary_ctx"/>
+ <WordDetect String="set_package_properties" insensitive="true" attribute="CMake Provided Function/Macro" context="set_package_properties_ctx"/>
+ <WordDetect String="add_feature_info" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="set_package_info" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="set_feature_info" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="print_enabled_features" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="print_disabled_features" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="FetchContent_Declare" insensitive="true" attribute="CMake Provided Function/Macro" context="FetchContent_Declare_ctx"/>
+ <WordDetect String="FetchContent_Populate" insensitive="true" attribute="CMake Provided Function/Macro" context="FetchContent_Populate_ctx"/>
+ <WordDetect String="FetchContent_GetProperties" insensitive="true" attribute="CMake Provided Function/Macro" context="FetchContent_GetProperties_ctx"/>
+ <WordDetect String="FetchContent_MakeAvailable" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="find_package_handle_standard_args" insensitive="true" attribute="CMake Provided Function/Macro" context="find_package_handle_standard_args_ctx"/>
+ <WordDetect String="find_package_check_version" insensitive="true" attribute="CMake Provided Function/Macro" context="find_package_check_version_ctx"/>
+ <WordDetect String="find_package_message" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="FortranCInterface_HEADER" insensitive="true" attribute="CMake Provided Function/Macro" context="FortranCInterface_HEADER_ctx"/>
+ <WordDetect String="FortranCInterface_VERIFY" insensitive="true" attribute="CMake Provided Function/Macro" context="FortranCInterface_VERIFY_ctx"/>
+ <WordDetect String="generate_export_header" insensitive="true" attribute="CMake Provided Function/Macro" context="generate_export_header_ctx"/>
+ <WordDetect String="GNUInstallDirs_get_absolute_install_dir" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="gtest_add_tests" insensitive="true" attribute="CMake Provided Function/Macro" context="gtest_add_tests_ctx"/>
+ <WordDetect String="gtest_discover_tests" insensitive="true" attribute="CMake Provided Function/Macro" context="gtest_discover_tests_ctx"/>
+ <WordDetect String="processorcount" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="select_library_configurations" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="test_big_endian" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ecos_add_include_directories" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ecos_add_executable" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ecos_add_target_lib" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ecos_adjust_directory" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ecos_use_arm_elf_tools" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ecos_use_i386_elf_tools" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="ecos_use_ppc_eabi_tools" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="add_jar" insensitive="true" attribute="CMake Provided Function/Macro" context="add_jar_ctx"/>
+ <WordDetect String="install_jar" insensitive="true" attribute="CMake Provided Function/Macro" context="install_jar_ctx"/>
+ <WordDetect String="install_jni_symlink" insensitive="true" attribute="CMake Provided Function/Macro" context="install_jar_ctx"/>
+ <WordDetect String="create_javah" insensitive="true" attribute="CMake Provided Function/Macro" context="create_javah_ctx"/>
+ <WordDetect String="install_jar_exports" insensitive="true" attribute="CMake Provided Function/Macro" context="install_jar_exports_ctx"/>
+ <WordDetect String="export_jars" insensitive="true" attribute="CMake Provided Function/Macro" context="export_jars_ctx"/>
+ <WordDetect String="find_jar" insensitive="true" attribute="CMake Provided Function/Macro" context="find_jar_ctx"/>
+ <WordDetect String="create_javadoc" insensitive="true" attribute="CMake Provided Function/Macro" context="create_javadoc_ctx"/>
+ <WordDetect String="swig_add_library" insensitive="true" attribute="CMake Provided Function/Macro" context="swig_add_library_ctx"/>
+ <WordDetect String="swig_link_libraries" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="squish_add_test" insensitive="true" attribute="CMake Provided Function/Macro" context="squish_add_test_ctx"/>
+ <WordDetect String="bison_target" insensitive="true" attribute="CMake Provided Function/Macro" context="bison_target_ctx"/>
+ <WordDetect String="cxxtest_add_test" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="doxygen_add_docs" insensitive="true" attribute="CMake Provided Function/Macro" context="doxygen_add_docs_ctx"/>
+ <WordDetect String="env_module" insensitive="true" attribute="CMake Provided Function/Macro" context="env_module_ctx"/>
+ <WordDetect String="env_module_swap" insensitive="true" attribute="CMake Provided Function/Macro" context="env_module_swap_ctx"/>
+ <WordDetect String="env_module_list" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="env_module_avail" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="flex_target" insensitive="true" attribute="CMake Provided Function/Macro" context="flex_target_ctx"/>
+ <WordDetect String="add_flex_bison_dependency" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="gettext_create_translations" insensitive="true" attribute="CMake Provided Function/Macro" context="gettext_create_translations_ctx"/>
+ <WordDetect String="gettext_process_pot_file" insensitive="true" attribute="CMake Provided Function/Macro" context="gettext_process_pot_file_ctx"/>
+ <WordDetect String="gettext_process_po_files" insensitive="true" attribute="CMake Provided Function/Macro" context="gettext_process_po_files_ctx"/>
+ <WordDetect String="hg_wc_info" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="matlab_get_version_from_release_name" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="matlab_get_release_name_from_version" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="matlab_extract_all_installed_versions_from_registry" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="matlab_get_all_valid_matlab_roots_from_registry" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="matlab_get_mex_suffix" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="matlab_get_version_from_matlab_run" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="matlab_add_unit_test" insensitive="true" attribute="CMake Provided Function/Macro" context="matlab_add_unit_test_ctx"/>
+ <WordDetect String="matlab_add_mex" insensitive="true" attribute="CMake Provided Function/Macro" context="matlab_add_mex_ctx"/>
+ <WordDetect String="pkg_check_modules" insensitive="true" attribute="CMake Provided Function/Macro" context="pkg_check_modules_ctx"/>
+ <WordDetect String="pkg_search_module" insensitive="true" attribute="CMake Provided Function/Macro" context="pkg_check_modules_ctx"/>
+ <WordDetect String="pkg_get_variable" insensitive="true" attribute="CMake Provided Function/Macro" context="pkg_get_variable_ctx"/>
+ <WordDetect String="protobuf_generate_cpp" insensitive="true" attribute="CMake Provided Function/Macro" context="protobuf_generate_cpp_ctx"/>
+ <WordDetect String="protobuf_generate_python" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="Python_add_library" insensitive="true" attribute="CMake Provided Function/Macro" context="Python_add_library_ctx"/>
+ <WordDetect String="Subversion_WC_INFO" insensitive="true" attribute="CMake Provided Function/Macro" context="Subversion_WC_INFO_ctx"/>
+ <WordDetect String="Subversion_WC_LOG" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="xctest_add_bundle" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <WordDetect String="xctest_add_test" insensitive="true" attribute="CMake Provided Function/Macro" context="function_ctx"/>
+ <DetectChar attribute="Comment" context="Match Comments and Docs" char="#" lookAhead="true"/>
+ <DetectIdentifier attribute="User Function/Macro" context="User Function"/>
+ <RegExpr attribute="@Variable Substitution" context="@VarSubst" String="@&var_ref_re;@" lookAhead="true"/>
+ <IncludeRules context="LineError"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="block_ctx">
+ <DetectChar attribute="Normal Text" context="block_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="block_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="block_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="block_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
-
<context attribute="Normal Text" lineEndContext="#stay" name="break_ctx">
- <DetectChar attribute="Normal Text" context="break_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="break_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="break_ctx_op">
- <IncludeRules context="EndCmdPop2" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="cmake_host_system_information_ctx">
- <DetectChar attribute="Normal Text" context="cmake_host_system_information_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="cmake_host_system_information_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="cmake_host_system_information_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="cmake_host_system_information_nargs" />
- <keyword attribute="Special Args" context="#stay" String="cmake_host_system_information_sargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cmake_host_system_information_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="cmake_host_system_information_sargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_language_ctx">
+ <DetectChar attribute="Normal Text" context="cmake_language_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_language_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cmake_language_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="cmake_language_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="cmake_minimum_required_ctx">
- <DetectChar attribute="Normal Text" context="cmake_minimum_required_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="cmake_minimum_required_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="cmake_minimum_required_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="cmake_minimum_required_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cmake_minimum_required_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="cmake_parse_arguments_ctx">
- <DetectChar attribute="Normal Text" context="cmake_parse_arguments_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="cmake_parse_arguments_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="cmake_parse_arguments_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="cmake_parse_arguments_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cmake_parse_arguments_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_path_ctx">
+ <DetectChar attribute="Normal Text" context="cmake_path_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_path_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cmake_path_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="cmake_policy_ctx">
- <DetectChar attribute="Normal Text" context="cmake_policy_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="cmake_policy_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="cmake_policy_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="cmake_policy_nargs" />
- <keyword attribute="Special Args" context="#stay" String="cmake_policy_sargs" />
- <IncludeRules context="User Function Args" />
- <!-- NOTE Handle CMP<NNN> as a special arg of `cmake_policy` command -->
- <RegExpr attribute="Special Args" context="#stay" String="\bCMP[0-9]+\b" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cmake_policy_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="cmake_policy_sargs"/>
+ <IncludeRules context="User Function Args"/>
+
+<!-- NOTE Handle CMP<NNN> as a special arg of `cmake_policy` command -->
+ <RegExpr attribute="Special Args" context="#stay" String="\bCMP[0-9]+\b"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="configure_file_ctx">
- <DetectChar attribute="Normal Text" context="configure_file_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="configure_file_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="configure_file_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="configure_file_nargs" />
- <keyword attribute="Special Args" context="#stay" String="configure_file_sargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="continue_ctx">
- <DetectChar attribute="Normal Text" context="continue_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="continue_ctx_op">
- <IncludeRules context="EndCmdPop2" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="configure_file_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="configure_file_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="elseif_ctx">
- <DetectChar attribute="Normal Text" context="elseif_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="elseif_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="elseif_ctx_op">
- <DetectChar attribute="Normal Text" context="elseif_ctx_op_nested" char="(" />
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="elseif_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="elseif_ctx_op_nested" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGET" attribute="Named Args" context="Target Name"/>
+ <keyword attribute="Named Args" context="#stay" String="elseif_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="elseif_ctx_op_nested">
- <IncludeRules context="EndCmdPop" />
- <keyword attribute="Named Args" context="#stay" String="elseif_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="else_ctx">
- <DetectChar attribute="Normal Text" context="else_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="else_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="endforeach_ctx">
- <DetectChar attribute="Normal Text" context="endforeach_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="endforeach_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="endfunction_ctx">
- <DetectChar attribute="Normal Text" context="endfunction_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="endfunction_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="endif_ctx">
- <DetectChar attribute="Normal Text" context="endif_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="endif_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="endmacro_ctx">
- <DetectChar attribute="Normal Text" context="endmacro_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="endmacro_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="endwhile_ctx">
- <DetectChar attribute="Normal Text" context="endwhile_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="endwhile_ctx_op">
- <IncludeRules context="EndCmdPop2" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ <DetectChar attribute="Normal Text" context="elseif_ctx_op_nested" char="("/>
+ <keyword attribute="Named Args" context="#stay" String="elseif_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="execute_process_ctx">
- <DetectChar attribute="Normal Text" context="execute_process_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="execute_process_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="execute_process_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="execute_process_nargs" />
- <keyword attribute="Special Args" context="#stay" String="execute_process_sargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="execute_process_ctx_op_nested" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="execute_process_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="execute_process_sargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="execute_process_ctx_op_nested">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ <DetectChar attribute="Normal Text" context="execute_process_ctx_op_nested" char="("/>
+ <keyword attribute="Named Args" context="#stay" String="execute_process_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="execute_process_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="file_ctx">
- <DetectChar attribute="Normal Text" context="file_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="file_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="file_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="file_nargs" />
- <keyword attribute="Special Args" context="#stay" String="file_sargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="file_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="file_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="find_file_ctx">
- <DetectChar attribute="Normal Text" context="find_file_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="find_file_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="find_file_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="find_file_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="find_file_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="find_file_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="find_library_ctx">
- <DetectChar attribute="Normal Text" context="find_library_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="find_library_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="find_library_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="find_library_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="find_library_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="find_file_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="find_package_ctx">
- <DetectChar attribute="Normal Text" context="find_package_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="find_package_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="find_package_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="find_package_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="find_path_ctx">
- <DetectChar attribute="Normal Text" context="find_path_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="find_path_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="find_path_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="find_program_ctx">
- <DetectChar attribute="Normal Text" context="find_program_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="find_program_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="find_program_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="find_package_nargs"/>
+ <keyword attribute="Standard Module" context="#stay" String="standard-finder-modules"/>
+ <keyword attribute="Special Args" context="#stay" String="find_file_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="foreach_ctx">
- <DetectChar attribute="Normal Text" context="foreach_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="foreach_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="foreach_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="foreach_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="foreach_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="function_ctx">
- <DetectChar attribute="Normal Text" context="function_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="function_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="function_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_cmake_property_ctx">
- <DetectChar attribute="Normal Text" context="get_cmake_property_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="get_cmake_property_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_cmake_property_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Property" context="#stay" String="global-properties" />
- <IncludeRules context="Detect More global-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Special Args" context="#stay" String="get_cmake_property_sargs"/>
+ <keyword attribute="Property" context="#stay" String="global-properties"/>
+ <IncludeRules context="Detect More global-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_directory_property_ctx">
- <DetectChar attribute="Normal Text" context="get_directory_property_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="get_directory_property_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_directory_property_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="get_directory_property_nargs" />
- <keyword attribute="Property" context="#stay" String="directory-properties" />
- <IncludeRules context="Detect More directory-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="get_directory_property_nargs"/>
+ <keyword attribute="Property" context="#stay" String="directory-properties"/>
+ <IncludeRules context="Detect More directory-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_filename_component_ctx">
- <DetectChar attribute="Normal Text" context="get_filename_component_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="get_filename_component_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_filename_component_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="get_filename_component_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="get_filename_component_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_property_ctx">
- <DetectChar attribute="Normal Text" context="get_property_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="get_property_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_property_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="get_property_nargs" />
- <keyword attribute="Property" context="#stay" String="cache-properties" />
- <keyword attribute="Property" context="#stay" String="directory-properties" />
- <IncludeRules context="Detect More directory-properties" />
- <keyword attribute="Property" context="#stay" String="global-properties" />
- <IncludeRules context="Detect More global-properties" />
- <keyword attribute="Property" context="#stay" String="install-properties" />
- <keyword attribute="Property" context="#stay" String="source-properties" />
- <IncludeRules context="Detect More source-properties" />
- <keyword attribute="Property" context="#stay" String="target-properties" />
- <IncludeRules context="Detect More target-properties" />
- <keyword attribute="Property" context="#stay" String="test-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="get_property_nargs"/>
+ <keyword attribute="Property" context="#stay" String="cache-properties"/>
+ <keyword attribute="Property" context="#stay" String="directory-properties"/>
+ <IncludeRules context="Detect More directory-properties"/>
+ <keyword attribute="Property" context="#stay" String="global-properties"/>
+ <IncludeRules context="Detect More global-properties"/>
+ <keyword attribute="Property" context="#stay" String="install-properties"/>
+ <keyword attribute="Property" context="#stay" String="source-properties"/>
+ <IncludeRules context="Detect More source-properties"/>
+ <keyword attribute="Property" context="#stay" String="target-properties"/>
+ <IncludeRules context="Detect More target-properties"/>
+ <keyword attribute="Property" context="#stay" String="test-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="if_ctx">
- <DetectChar attribute="Normal Text" context="if_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="if_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="if_ctx_op">
- <DetectChar attribute="Normal Text" context="if_ctx_op_nested" char="(" />
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="if_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="if_ctx_op_nested" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGET" attribute="Named Args" context="Target Name"/>
+ <keyword attribute="Named Args" context="#stay" String="elseif_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="if_ctx_op_nested">
- <IncludeRules context="EndCmdPop" />
- <keyword attribute="Named Args" context="#stay" String="if_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ <DetectChar attribute="Normal Text" context="if_ctx_op_nested" char="("/>
+ <keyword attribute="Named Args" context="#stay" String="elseif_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="include_ctx">
- <DetectChar attribute="Normal Text" context="include_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="include_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="include_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="include_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="include_nargs"/>
+ <keyword attribute="Standard Module" context="#stay" String="standard-modules"/>
+ <keyword attribute="Deprecated Module" context="#stay" String="deprecated-modules"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="include_guard_ctx">
- <DetectChar attribute="Normal Text" context="include_guard_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="include_guard_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="include_guard_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="include_guard_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="include_guard_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="list_ctx">
- <DetectChar attribute="Normal Text" context="list_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="list_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="list_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="list_nargs" />
- <keyword attribute="Special Args" context="#stay" String="list_sargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="macro_ctx">
- <DetectChar attribute="Normal Text" context="macro_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="macro_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="list_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="list_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="mark_as_advanced_ctx">
- <DetectChar attribute="Normal Text" context="mark_as_advanced_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="mark_as_advanced_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="mark_as_advanced_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="mark_as_advanced_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="mark_as_advanced_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="math_ctx">
- <DetectChar attribute="Normal Text" context="math_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="math_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="math_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="math_nargs" />
- <keyword attribute="Special Args" context="#stay" String="math_sargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="math_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="math_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="message_ctx">
- <DetectChar attribute="Normal Text" context="message_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="message_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="message_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="message_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="option_ctx">
- <DetectChar attribute="Normal Text" context="option_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="option_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="message_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="return_ctx">
- <DetectChar attribute="Normal Text" context="return_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="return_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="return_ctx_op">
- <IncludeRules context="EndCmdPop2" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="return_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="separate_arguments_ctx">
- <DetectChar attribute="Normal Text" context="separate_arguments_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="separate_arguments_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="separate_arguments_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="separate_arguments_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="separate_arguments_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_directory_properties_ctx">
- <DetectChar attribute="Normal Text" context="set_directory_properties_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="set_directory_properties_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_directory_properties_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="set_directory_properties_nargs" />
- <keyword attribute="Property" context="#stay" String="cache-properties" />
- <keyword attribute="Property" context="#stay" String="directory-properties" />
- <IncludeRules context="Detect More directory-properties" />
- <keyword attribute="Property" context="#stay" String="global-properties" />
- <IncludeRules context="Detect More global-properties" />
- <keyword attribute="Property" context="#stay" String="install-properties" />
- <keyword attribute="Property" context="#stay" String="source-properties" />
- <IncludeRules context="Detect More source-properties" />
- <keyword attribute="Property" context="#stay" String="target-properties" />
- <IncludeRules context="Detect More target-properties" />
- <keyword attribute="Property" context="#stay" String="test-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="set_directory_properties_nargs"/>
+ <keyword attribute="Property" context="#stay" String="cache-properties"/>
+ <keyword attribute="Property" context="#stay" String="directory-properties"/>
+ <IncludeRules context="Detect More directory-properties"/>
+ <keyword attribute="Property" context="#stay" String="global-properties"/>
+ <IncludeRules context="Detect More global-properties"/>
+ <keyword attribute="Property" context="#stay" String="install-properties"/>
+ <keyword attribute="Property" context="#stay" String="source-properties"/>
+ <IncludeRules context="Detect More source-properties"/>
+ <keyword attribute="Property" context="#stay" String="target-properties"/>
+ <IncludeRules context="Detect More target-properties"/>
+ <keyword attribute="Property" context="#stay" String="test-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_property_ctx">
- <DetectChar attribute="Normal Text" context="set_property_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="set_property_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_property_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="set_property_nargs" />
- <keyword attribute="Property" context="#stay" String="cache-properties" />
- <keyword attribute="Property" context="#stay" String="directory-properties" />
- <IncludeRules context="Detect More directory-properties" />
- <keyword attribute="Property" context="#stay" String="global-properties" />
- <IncludeRules context="Detect More global-properties" />
- <keyword attribute="Property" context="#stay" String="install-properties" />
- <keyword attribute="Property" context="#stay" String="source-properties" />
- <IncludeRules context="Detect More source-properties" />
- <keyword attribute="Property" context="#stay" String="target-properties" />
- <IncludeRules context="Detect More target-properties" />
- <keyword attribute="Property" context="#stay" String="test-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="set_property_nargs"/>
+ <keyword attribute="Property" context="#stay" String="cache-properties"/>
+ <keyword attribute="Property" context="#stay" String="directory-properties"/>
+ <IncludeRules context="Detect More directory-properties"/>
+ <keyword attribute="Property" context="#stay" String="global-properties"/>
+ <IncludeRules context="Detect More global-properties"/>
+ <keyword attribute="Property" context="#stay" String="install-properties"/>
+ <keyword attribute="Property" context="#stay" String="source-properties"/>
+ <IncludeRules context="Detect More source-properties"/>
+ <keyword attribute="Property" context="#stay" String="target-properties"/>
+ <IncludeRules context="Detect More target-properties"/>
+ <keyword attribute="Property" context="#stay" String="test-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_ctx">
- <DetectChar attribute="Normal Text" context="set_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="set_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="set_nargs" />
- <keyword attribute="Special Args" context="#stay" String="set_sargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="site_name_ctx">
- <DetectChar attribute="Normal Text" context="site_name_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="site_name_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="set_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="set_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="string_ctx">
- <DetectChar attribute="Normal Text" context="string_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="string_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="string_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="string_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="string_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="unset_ctx">
- <DetectChar attribute="Normal Text" context="unset_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="unset_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="unset_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="unset_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="variable_watch_ctx">
- <DetectChar attribute="Normal Text" context="variable_watch_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="variable_watch_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="unset_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="while_ctx">
- <DetectChar attribute="Normal Text" context="while_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="while_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="while_ctx_op">
- <DetectChar attribute="Normal Text" context="while_ctx_op_nested" char="(" />
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="while_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="while_ctx_op_nested" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGET" attribute="Named Args" context="Target Name"/>
+ <keyword attribute="Named Args" context="#stay" String="elseif_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="while_ctx_op_nested">
- <IncludeRules context="EndCmdPop" />
- <keyword attribute="Named Args" context="#stay" String="while_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="add_compile_definitions_ctx">
- <DetectChar attribute="Normal Text" context="add_compile_definitions_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="add_compile_definitions_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="add_compile_options_ctx">
- <DetectChar attribute="Normal Text" context="add_compile_options_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="add_compile_options_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ <DetectChar attribute="Normal Text" context="while_ctx_op_nested" char="("/>
+ <keyword attribute="Named Args" context="#stay" String="elseif_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="add_custom_command_ctx">
- <DetectChar attribute="Normal Text" context="add_custom_command_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="add_custom_command_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="add_custom_command_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="add_custom_command_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="add_custom_command_ctx_op_nested" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGET" attribute="Named Args" context="Target Name"/>
+ <keyword attribute="Named Args" context="#stay" String="add_custom_command_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_custom_command_ctx_op_nested">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ <DetectChar attribute="Normal Text" context="add_custom_command_ctx_op_nested" char="("/>
+ <keyword attribute="Named Args" context="#stay" String="add_custom_command_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="add_custom_target_ctx">
- <DetectChar attribute="Normal Text" context="add_custom_target_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="add_custom_target_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="add_custom_target_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="add_custom_target_nargs" />
- <IncludeRules context="User Function Args" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_custom_target_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="add_custom_target_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="add_custom_target_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="add_definitions_ctx">
- <DetectChar attribute="Normal Text" context="add_definitions_ctx_op" char="(" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_custom_target_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="add_custom_target_ctx_op_nested" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="add_custom_target_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="add_definitions_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_custom_target_ctx_op_nested">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ <DetectChar attribute="Normal Text" context="add_custom_target_ctx_op_nested" char="("/>
+ <keyword attribute="Named Args" context="#stay" String="add_custom_target_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="add_dependencies_ctx">
- <DetectChar attribute="Normal Text" context="add_dependencies_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="add_dependencies_ctx_op_tgts_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="add_dependencies_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_dependencies_ctx_op_tgts_first">
+ <DetectSpaces/>
+ <IncludeRules context="Detect Aliased Targets"/>
+ <IncludeRules context="Detect Targets"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="add_executable_ctx">
- <DetectChar attribute="Normal Text" context="add_executable_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="add_executable_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_executable_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="add_executable_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="add_executable_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="add_executable_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="add_executable_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="add_executable_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="add_library_ctx">
- <DetectChar attribute="Normal Text" context="add_library_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="add_library_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="add_library_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectChar attribute="Normal Text" context="add_library_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="add_link_options_ctx">
- <DetectChar attribute="Normal Text" context="add_link_options_ctx_op" char="(" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_library_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="add_library_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="add_library_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="add_link_options_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_library_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="ALIAS" attribute="Named Args" context="Target Name"/>
+ <keyword attribute="Named Args" context="#stay" String="add_library_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="add_subdirectory_ctx">
- <DetectChar attribute="Normal Text" context="add_subdirectory_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="add_subdirectory_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="add_subdirectory_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="add_subdirectory_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="add_subdirectory_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="add_test_ctx">
- <DetectChar attribute="Normal Text" context="add_test_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="add_test_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="add_test_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="add_test_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="aux_source_directory_ctx">
- <DetectChar attribute="Normal Text" context="aux_source_directory_ctx_op" char="(" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="add_test_ctx_op_nested" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="add_test_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="aux_source_directory_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_test_ctx_op_nested">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ <DetectChar attribute="Normal Text" context="add_test_ctx_op_nested" char="("/>
+ <keyword attribute="Named Args" context="#stay" String="add_test_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="build_command_ctx">
- <DetectChar attribute="Normal Text" context="build_command_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="build_command_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="build_command_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="build_command_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGET" attribute="Named Args" context="Target Name"/>
+ <keyword attribute="Named Args" context="#stay" String="build_command_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_file_api_ctx">
+ <DetectChar attribute="Normal Text" context="cmake_file_api_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_file_api_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cmake_file_api_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="create_test_sourcelist_ctx">
- <DetectChar attribute="Normal Text" context="create_test_sourcelist_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="create_test_sourcelist_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="create_test_sourcelist_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="create_test_sourcelist_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="create_test_sourcelist_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="define_property_ctx">
- <DetectChar attribute="Normal Text" context="define_property_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="define_property_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="define_property_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="define_property_nargs" />
- <keyword attribute="Property" context="#stay" String="cache-properties" />
- <keyword attribute="Property" context="#stay" String="directory-properties" />
- <IncludeRules context="Detect More directory-properties" />
- <keyword attribute="Property" context="#stay" String="global-properties" />
- <IncludeRules context="Detect More global-properties" />
- <keyword attribute="Property" context="#stay" String="install-properties" />
- <keyword attribute="Property" context="#stay" String="source-properties" />
- <IncludeRules context="Detect More source-properties" />
- <keyword attribute="Property" context="#stay" String="target-properties" />
- <IncludeRules context="Detect More target-properties" />
- <keyword attribute="Property" context="#stay" String="test-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="define_property_nargs"/>
+ <keyword attribute="Property" context="#stay" String="cache-properties"/>
+ <keyword attribute="Property" context="#stay" String="directory-properties"/>
+ <IncludeRules context="Detect More directory-properties"/>
+ <keyword attribute="Property" context="#stay" String="global-properties"/>
+ <IncludeRules context="Detect More global-properties"/>
+ <keyword attribute="Property" context="#stay" String="install-properties"/>
+ <keyword attribute="Property" context="#stay" String="source-properties"/>
+ <IncludeRules context="Detect More source-properties"/>
+ <keyword attribute="Property" context="#stay" String="target-properties"/>
+ <IncludeRules context="Detect More target-properties"/>
+ <keyword attribute="Property" context="#stay" String="test-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="enable_language_ctx">
- <DetectChar attribute="Normal Text" context="enable_language_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="enable_language_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="enable_language_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="enable_language_nargs" />
- <keyword attribute="Special Args" context="#stay" String="enable_language_sargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="enable_testing_ctx">
- <DetectChar attribute="Normal Text" context="enable_testing_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="enable_testing_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="enable_language_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="enable_language_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="export_ctx">
- <DetectChar attribute="Normal Text" context="export_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="export_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="export_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="export_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="fltk_wrap_ui_ctx">
- <DetectChar attribute="Normal Text" context="fltk_wrap_ui_ctx_op" char="(" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGETS" attribute="Named Args" context="export_tgts"/>
+ <keyword attribute="Named Args" context="#stay" String="export_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="fltk_wrap_ui_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="export_tgts">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#pop" String="export_nargs"/>
+ <IncludeRules context="Detect Aliased Targets"/>
+ <IncludeRules context="Detect Targets"/>
+ <IncludeRules context="User Function Args"/>
+ <IncludeRules context="LineError"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_source_file_property_ctx">
- <DetectChar attribute="Normal Text" context="get_source_file_property_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="get_source_file_property_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_source_file_property_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Property" context="#stay" String="source-properties" />
- <IncludeRules context="Detect More source-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="get_source_file_property_nargs"/>
+ <keyword attribute="Property" context="#stay" String="source-properties"/>
+ <IncludeRules context="Detect More source-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_target_property_ctx">
- <DetectChar attribute="Normal Text" context="get_target_property_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="get_target_property_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_target_property_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Property" context="#stay" String="target-properties" />
- <IncludeRules context="Detect More target-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Property" context="#stay" String="target-properties"/>
+ <IncludeRules context="Detect More target-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_test_property_ctx">
- <DetectChar attribute="Normal Text" context="get_test_property_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="get_test_property_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="get_test_property_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Property" context="#stay" String="test-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="get_test_property_nargs"/>
+ <keyword attribute="Property" context="#stay" String="test-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="include_directories_ctx">
- <DetectChar attribute="Normal Text" context="include_directories_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="include_directories_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="include_directories_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="include_directories_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="include_directories_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="include_external_msproject_ctx">
- <DetectChar attribute="Normal Text" context="include_external_msproject_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="include_external_msproject_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="include_external_msproject_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="include_external_msproject_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="include_regular_expression_ctx">
- <DetectChar attribute="Normal Text" context="include_regular_expression_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="include_regular_expression_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="include_external_msproject_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="install_ctx">
- <DetectChar attribute="Normal Text" context="install_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="install_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="install_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="install_nargs" />
- <keyword attribute="Special Args" context="#stay" String="install_sargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGETS" attribute="Named Args" context="install_tgts"/>
+ <keyword attribute="Named Args" context="#stay" String="install_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="install_sargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="install_tgts">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#pop" String="install_nargs"/>
+ <IncludeRules context="Detect Aliased Targets"/>
+ <IncludeRules context="Detect Targets"/>
+ <IncludeRules context="User Function Args"/>
+ <IncludeRules context="LineError"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="link_directories_ctx">
- <DetectChar attribute="Normal Text" context="link_directories_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="link_directories_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="link_directories_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="link_directories_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="link_directories_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="link_libraries_ctx">
- <DetectChar attribute="Normal Text" context="link_libraries_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="link_libraries_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="link_libraries_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="link_libraries_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Special Args" context="#stay" String="link_libraries_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="load_cache_ctx">
- <DetectChar attribute="Normal Text" context="load_cache_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="load_cache_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="load_cache_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="load_cache_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="load_cache_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="project_ctx">
- <DetectChar attribute="Normal Text" context="project_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="project_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="project_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="project_nargs" />
- <keyword attribute="Special Args" context="#stay" String="project_sargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="qt_wrap_cpp_ctx">
- <DetectChar attribute="Normal Text" context="qt_wrap_cpp_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="qt_wrap_cpp_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="qt_wrap_ui_ctx">
- <DetectChar attribute="Normal Text" context="qt_wrap_ui_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="qt_wrap_ui_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="remove_definitions_ctx">
- <DetectChar attribute="Normal Text" context="remove_definitions_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="remove_definitions_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="project_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="project_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_source_files_properties_ctx">
- <DetectChar attribute="Normal Text" context="set_source_files_properties_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="set_source_files_properties_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_source_files_properties_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="set_source_files_properties_nargs" />
- <keyword attribute="Property" context="#stay" String="source-properties" />
- <IncludeRules context="Detect More source-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="set_source_files_properties_nargs"/>
+ <keyword attribute="Property" context="#stay" String="source-properties"/>
+ <IncludeRules context="Detect More source-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_target_properties_ctx">
- <DetectChar attribute="Normal Text" context="set_target_properties_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="set_target_properties_ctx_op_tgts_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="set_target_properties_ctx_op_tgts_first">
+ <DetectSpaces/>
+
+<!-- NOTE Handle the only case in CMake nowadays:
+ 1. `set_target_properties` have a named keyword (`PROPERTIES`) after targets list
+ -->
+ <keyword context="set_target_properties_ctx_op" String="set_directory_properties_nargs" lookAhead="true"/>
+ <IncludeRules context="Detect Aliased Targets"/>
+ <IncludeRules context="Detect Targets"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_target_properties_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="set_target_properties_nargs" />
- <keyword attribute="Property" context="#stay" String="target-properties" />
- <IncludeRules context="Detect More target-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="set_directory_properties_nargs"/>
+ <keyword attribute="Property" context="#stay" String="target-properties"/>
+ <IncludeRules context="Detect More target-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_tests_properties_ctx">
- <DetectChar attribute="Normal Text" context="set_tests_properties_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="set_tests_properties_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="set_tests_properties_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="set_tests_properties_nargs" />
- <keyword attribute="Property" context="#stay" String="test-properties" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="set_tests_properties_nargs"/>
+ <keyword attribute="Property" context="#stay" String="test-properties"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="source_group_ctx">
- <DetectChar attribute="Normal Text" context="source_group_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="source_group_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="source_group_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="source_group_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="source_group_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="target_compile_definitions_ctx">
- <DetectChar attribute="Normal Text" context="target_compile_definitions_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="target_compile_definitions_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="target_compile_definitions_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="target_compile_definitions_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="target_compile_definitions_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="target_compile_definitions_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="target_compile_definitions_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="target_compile_definitions_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="target_compile_features_ctx">
- <DetectChar attribute="Normal Text" context="target_compile_features_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="target_compile_features_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="target_compile_features_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="target_compile_features_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="target_compile_features_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="target_compile_features_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="target_compile_features_nargs" />
- <keyword attribute="Special Args" context="#stay" String="target_compile_features_sargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="target_compile_definitions_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="target_compile_features_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="target_compile_options_ctx">
- <DetectChar attribute="Normal Text" context="target_compile_options_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="target_compile_options_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="target_compile_options_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="target_compile_options_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="target_compile_options_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="target_compile_options_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="target_compile_options_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="target_compile_options_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="target_include_directories_ctx">
- <DetectChar attribute="Normal Text" context="target_include_directories_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="target_include_directories_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="target_include_directories_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="target_include_directories_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="target_link_directories_ctx">
- <DetectChar attribute="Normal Text" context="target_link_directories_ctx_op" char="(" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="target_include_directories_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="target_include_directories_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="target_include_directories_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="target_link_directories_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="target_link_directories_nargs" />
- <IncludeRules context="User Function Args" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="target_include_directories_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="target_include_directories_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="target_link_libraries_ctx">
- <DetectChar attribute="Normal Text" context="target_link_libraries_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="target_link_libraries_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="target_link_libraries_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="target_link_libraries_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="target_link_libraries_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="target_link_libraries_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="target_link_libraries_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="target_compile_definitions_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="link_libraries_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="target_link_options_ctx">
- <DetectChar attribute="Normal Text" context="target_link_options_ctx_op" char="(" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="target_precompile_headers_ctx">
+ <DetectChar attribute="Normal Text" context="target_precompile_headers_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="target_link_options_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="target_link_options_nargs" />
- <IncludeRules context="User Function Args" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="target_precompile_headers_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="target_precompile_headers_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="target_precompile_headers_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="target_precompile_headers_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="target_precompile_headers_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="target_sources_ctx">
- <DetectChar attribute="Normal Text" context="target_sources_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="target_sources_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="target_sources_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="target_sources_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="target_sources_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="target_sources_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="target_sources_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="target_sources_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="target_sources_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="try_compile_ctx">
- <DetectChar attribute="Normal Text" context="try_compile_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="try_compile_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="try_compile_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="try_compile_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="try_compile_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="try_run_ctx">
- <DetectChar attribute="Normal Text" context="try_run_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="try_run_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="try_run_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="try_run_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="try_run_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_build_ctx">
- <DetectChar attribute="Normal Text" context="ctest_build_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="ctest_build_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_build_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="ctest_build_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ctest_build_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_configure_ctx">
- <DetectChar attribute="Normal Text" context="ctest_configure_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="ctest_configure_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_configure_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="ctest_configure_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ctest_configure_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_coverage_ctx">
- <DetectChar attribute="Normal Text" context="ctest_coverage_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="ctest_coverage_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_coverage_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="ctest_coverage_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="ctest_empty_binary_directory_ctx">
- <DetectChar attribute="Normal Text" context="ctest_empty_binary_directory_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="ctest_empty_binary_directory_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ctest_coverage_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_memcheck_ctx">
- <DetectChar attribute="Normal Text" context="ctest_memcheck_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="ctest_memcheck_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_memcheck_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="ctest_memcheck_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="ctest_read_custom_files_ctx">
- <DetectChar attribute="Normal Text" context="ctest_read_custom_files_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="ctest_read_custom_files_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ctest_memcheck_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_run_script_ctx">
- <DetectChar attribute="Normal Text" context="ctest_run_script_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="ctest_run_script_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_run_script_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="ctest_run_script_nargs" />
- <IncludeRules context="User Function Args" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="ctest_sleep_ctx">
- <DetectChar attribute="Normal Text" context="ctest_sleep_ctx_op" char="(" />
- </context>
- <context attribute="Normal Text" lineEndContext="#stay" name="ctest_sleep_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ctest_run_script_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_start_ctx">
- <DetectChar attribute="Normal Text" context="ctest_start_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="ctest_start_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_start_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="ctest_start_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ctest_start_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_submit_ctx">
- <DetectChar attribute="Normal Text" context="ctest_submit_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="ctest_submit_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_submit_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="ctest_submit_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ctest_submit_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_test_ctx">
- <DetectChar attribute="Normal Text" context="ctest_test_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="ctest_test_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_test_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="ctest_test_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ctest_test_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="ctest_test_sargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_update_ctx">
- <DetectChar attribute="Normal Text" context="ctest_update_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="ctest_update_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_update_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="ctest_update_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ctest_update_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_upload_ctx">
- <DetectChar attribute="Normal Text" context="ctest_upload_ctx_op" char="(" />
+ <DetectChar attribute="Normal Text" context="ctest_upload_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="ctest_upload_ctx_op">
- <IncludeRules context="EndCmdPop2" />
- <keyword attribute="Named Args" context="#stay" String="ctest_upload_nargs" />
- <IncludeRules context="User Function Args" />
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ctest_upload_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="android_add_test_data_ctx">
+ <DetectChar attribute="Normal Text" context="android_add_test_data_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="android_add_test_data_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="android_add_test_data_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_c_source_compiles_ctx">
+ <DetectChar attribute="Normal Text" context="check_c_source_compiles_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_c_source_compiles_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="check_c_source_compiles_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_fortran_source_compiles_ctx">
+ <DetectChar attribute="Normal Text" context="check_fortran_source_compiles_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_fortran_source_compiles_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="check_fortran_source_compiles_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_fortran_source_runs_ctx">
+ <DetectChar attribute="Normal Text" context="check_fortran_source_runs_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_fortran_source_runs_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="check_fortran_source_runs_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_include_files_ctx">
+ <DetectChar attribute="Normal Text" context="check_include_files_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_include_files_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="check_include_files_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="check_include_files_sargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_ipo_supported_ctx">
+ <DetectChar attribute="Normal Text" context="check_ipo_supported_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_ipo_supported_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="check_ipo_supported_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="check_ipo_supported_sargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_language_ctx">
+ <DetectChar attribute="Normal Text" context="check_language_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_language_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Special Args" context="#stay" String="enable_language_sargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_pie_supported_ctx">
+ <DetectChar attribute="Normal Text" context="check_pie_supported_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_pie_supported_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="check_pie_supported_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="check_pie_supported_sargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_type_size_ctx">
+ <DetectChar attribute="Normal Text" context="check_type_size_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="check_type_size_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="check_type_size_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="check_include_files_sargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_add_fortran_subdirectory_ctx">
+ <DetectChar attribute="Normal Text" context="cmake_add_fortran_subdirectory_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_add_fortran_subdirectory_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cmake_add_fortran_subdirectory_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="configure_package_config_file_ctx">
+ <DetectChar attribute="Normal Text" context="configure_package_config_file_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="configure_package_config_file_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="configure_package_config_file_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="write_basic_package_version_file_ctx">
+ <DetectChar attribute="Normal Text" context="write_basic_package_version_file_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="write_basic_package_version_file_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="write_basic_package_version_file_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="write_basic_package_version_file_sargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_print_properties_ctx">
+ <DetectChar attribute="Normal Text" context="cmake_print_properties_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_print_properties_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGETS" attribute="Named Args" context="cmake_print_properties_tgts"/>
+ <keyword attribute="Named Args" context="#stay" String="cmake_print_properties_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_print_properties_tgts">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#pop" String="cmake_print_properties_nargs"/>
+ <IncludeRules context="Detect Aliased Targets"/>
+ <IncludeRules context="Detect Targets"/>
+ <IncludeRules context="User Function Args"/>
+ <IncludeRules context="LineError"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_push_check_state_ctx">
+ <DetectChar attribute="Normal Text" context="cmake_push_check_state_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake_push_check_state_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cmake_push_check_state_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_add_component_ctx">
+ <DetectChar attribute="Normal Text" context="cpack_add_component_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_add_component_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cpack_add_component_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_add_component_group_ctx">
+ <DetectChar attribute="Normal Text" context="cpack_add_component_group_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_add_component_group_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cpack_add_component_group_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_add_install_type_ctx">
+ <DetectChar attribute="Normal Text" context="cpack_add_install_type_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_add_install_type_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cpack_add_install_type_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_configure_downloads_ctx">
+ <DetectChar attribute="Normal Text" context="cpack_configure_downloads_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_configure_downloads_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cpack_configure_downloads_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_ifw_configure_component_ctx">
+ <DetectChar attribute="Normal Text" context="cpack_ifw_configure_component_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_ifw_configure_component_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cpack_ifw_configure_component_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_ifw_configure_component_group_ctx">
+ <DetectChar attribute="Normal Text" context="cpack_ifw_configure_component_group_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_ifw_configure_component_group_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cpack_ifw_configure_component_group_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_ifw_add_repository_ctx">
+ <DetectChar attribute="Normal Text" context="cpack_ifw_add_repository_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_ifw_add_repository_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cpack_ifw_add_repository_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_ifw_update_repository_ctx">
+ <DetectChar attribute="Normal Text" context="cpack_ifw_update_repository_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpack_ifw_update_repository_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="cpack_ifw_update_repository_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ctest_coverage_collect_gcov_ctx">
+ <DetectChar attribute="Normal Text" context="ctest_coverage_collect_gcov_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ctest_coverage_collect_gcov_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ctest_coverage_collect_gcov_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExternalData_Add_Target_ctx">
+ <DetectChar attribute="Normal Text" context="ExternalData_Add_Target_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExternalData_Add_Target_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ExternalData_Add_Target_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExternalProject_Add_ctx">
+ <DetectChar attribute="Normal Text" context="ExternalProject_Add_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExternalProject_Add_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ExternalProject_Add_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="ExternalProject_Add_sargs"/>
+ <keyword attribute="Property" context="#stay" String="target-properties"/>
+ <IncludeRules context="Detect More target-properties"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExternalProject_Add_Step_ctx">
+ <DetectChar attribute="Normal Text" context="ExternalProject_Add_Step_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExternalProject_Add_Step_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ExternalProject_Add_Step_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExternalProject_Add_StepTargets_ctx">
+ <DetectChar attribute="Normal Text" context="ExternalProject_Add_StepTargets_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ExternalProject_Add_StepTargets_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="ExternalProject_Add_StepTargets_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="feature_summary_ctx">
+ <DetectChar attribute="Normal Text" context="feature_summary_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="feature_summary_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="feature_summary_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="set_package_properties_ctx">
+ <DetectChar attribute="Normal Text" context="set_package_properties_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="set_package_properties_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="set_package_properties_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FetchContent_Declare_ctx">
+ <DetectChar attribute="Normal Text" context="FetchContent_Declare_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FetchContent_Declare_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="FetchContent_Declare_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FetchContent_Populate_ctx">
+ <DetectChar attribute="Normal Text" context="FetchContent_Populate_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FetchContent_Populate_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="FetchContent_Populate_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FetchContent_GetProperties_ctx">
+ <DetectChar attribute="Normal Text" context="FetchContent_GetProperties_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FetchContent_GetProperties_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="FetchContent_GetProperties_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="find_package_handle_standard_args_ctx">
+ <DetectChar attribute="Normal Text" context="find_package_handle_standard_args_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="find_package_handle_standard_args_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="find_package_handle_standard_args_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="find_package_check_version_ctx">
+ <DetectChar attribute="Normal Text" context="find_package_check_version_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="find_package_check_version_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="find_package_check_version_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FortranCInterface_HEADER_ctx">
+ <DetectChar attribute="Normal Text" context="FortranCInterface_HEADER_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FortranCInterface_HEADER_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="FortranCInterface_HEADER_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FortranCInterface_VERIFY_ctx">
+ <DetectChar attribute="Normal Text" context="FortranCInterface_VERIFY_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FortranCInterface_VERIFY_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="FortranCInterface_VERIFY_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="generate_export_header_ctx">
+ <DetectChar attribute="Normal Text" context="generate_export_header_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="generate_export_header_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="generate_export_header_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="generate_export_header_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="generate_export_header_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="generate_export_header_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="gtest_add_tests_ctx">
+ <DetectChar attribute="Normal Text" context="gtest_add_tests_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="gtest_add_tests_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGET" attribute="Named Args" context="Target Name"/>
+ <keyword attribute="Named Args" context="#stay" String="gtest_add_tests_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="gtest_discover_tests_ctx">
+ <DetectChar attribute="Normal Text" context="gtest_discover_tests_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="gtest_discover_tests_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="gtest_discover_tests_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="gtest_discover_tests_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="gtest_discover_tests_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="gtest_discover_tests_nargs"/>
+ <keyword attribute="Special Args" context="#stay" String="gtest_discover_tests_sargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_jar_ctx">
+ <DetectChar attribute="Normal Text" context="add_jar_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_jar_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="add_jar_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="add_jar_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="add_jar_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="add_jar_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="install_jar_ctx">
+ <DetectChar attribute="Normal Text" context="install_jar_ctx_op_tgt_first" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="install_jar_ctx_op_tgt_first">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="install_jar_ctx_op" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <RegExpr attribute="Targets" context="install_jar_ctx_op" String="&tgt_name_re;"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="install_jar_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="install_jar_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="create_javah_ctx">
+ <DetectChar attribute="Normal Text" context="create_javah_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="create_javah_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGET" attribute="Named Args" context="Target Name"/>
+ <keyword attribute="Named Args" context="#stay" String="create_javah_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="install_jar_exports_ctx">
+ <DetectChar attribute="Normal Text" context="install_jar_exports_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="install_jar_exports_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGETS" attribute="Named Args" context="install_jar_exports_tgts"/>
+ <keyword attribute="Named Args" context="#stay" String="install_jar_exports_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="install_jar_exports_tgts">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#pop" String="install_jar_exports_nargs"/>
+ <IncludeRules context="Detect Aliased Targets"/>
+ <IncludeRules context="Detect Targets"/>
+ <IncludeRules context="User Function Args"/>
+ <IncludeRules context="LineError"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="export_jars_ctx">
+ <DetectChar attribute="Normal Text" context="export_jars_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="export_jars_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <WordDetect String="TARGETS" attribute="Named Args" context="export_jars_tgts"/>
+ <keyword attribute="Named Args" context="#stay" String="export_jars_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="export_jars_tgts">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#pop" String="export_jars_nargs"/>
+ <IncludeRules context="Detect Aliased Targets"/>
+ <IncludeRules context="Detect Targets"/>
+ <IncludeRules context="User Function Args"/>
+ <IncludeRules context="LineError"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="find_jar_ctx">
+ <DetectChar attribute="Normal Text" context="find_jar_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="find_jar_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="find_jar_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="create_javadoc_ctx">
+ <DetectChar attribute="Normal Text" context="create_javadoc_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="create_javadoc_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="create_javadoc_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="swig_add_library_ctx">
+ <DetectChar attribute="Normal Text" context="swig_add_library_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="swig_add_library_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="swig_add_library_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="squish_add_test_ctx">
+ <DetectChar attribute="Normal Text" context="squish_add_test_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="squish_add_test_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="squish_add_test_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="bison_target_ctx">
+ <DetectChar attribute="Normal Text" context="bison_target_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="bison_target_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="bison_target_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="doxygen_add_docs_ctx">
+ <DetectChar attribute="Normal Text" context="doxygen_add_docs_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="doxygen_add_docs_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="doxygen_add_docs_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="env_module_ctx">
+ <DetectChar attribute="Normal Text" context="env_module_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="env_module_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="env_module_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="env_module_swap_ctx">
+ <DetectChar attribute="Normal Text" context="env_module_swap_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="env_module_swap_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="env_module_swap_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="flex_target_ctx">
+ <DetectChar attribute="Normal Text" context="flex_target_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="flex_target_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="flex_target_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="gettext_create_translations_ctx">
+ <DetectChar attribute="Normal Text" context="gettext_create_translations_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="gettext_create_translations_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="gettext_create_translations_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="gettext_process_pot_file_ctx">
+ <DetectChar attribute="Normal Text" context="gettext_process_pot_file_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="gettext_process_pot_file_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="gettext_process_pot_file_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="gettext_process_po_files_ctx">
+ <DetectChar attribute="Normal Text" context="gettext_process_po_files_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="gettext_process_po_files_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="gettext_process_po_files_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="matlab_add_unit_test_ctx">
+ <DetectChar attribute="Normal Text" context="matlab_add_unit_test_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="matlab_add_unit_test_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="matlab_add_unit_test_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="matlab_add_mex_ctx">
+ <DetectChar attribute="Normal Text" context="matlab_add_mex_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="matlab_add_mex_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="matlab_add_mex_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="pkg_check_modules_ctx">
+ <DetectChar attribute="Normal Text" context="pkg_check_modules_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="pkg_check_modules_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="pkg_check_modules_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="pkg_get_variable_ctx">
+ <DetectChar attribute="Normal Text" context="pkg_get_variable_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="pkg_get_variable_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="pkg_get_variable_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="protobuf_generate_cpp_ctx">
+ <DetectChar attribute="Normal Text" context="protobuf_generate_cpp_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="protobuf_generate_cpp_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="protobuf_generate_cpp_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Python_add_library_ctx">
+ <DetectChar attribute="Normal Text" context="Python_add_library_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Python_add_library_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="Python_add_library_nargs"/>
+ <IncludeRules context="User Function Args"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Subversion_WC_INFO_ctx">
+ <DetectChar attribute="Normal Text" context="Subversion_WC_INFO_ctx_op" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Subversion_WC_INFO_ctx_op">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <keyword attribute="Named Args" context="#stay" String="Subversion_WC_INFO_nargs"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect More global-properties">
- <RegExpr attribute="Property" context="#stay" String="\bFeatureSummary_&id_re;_DESCRIPTION\b" />
+ <RegExpr attribute="Property" context="#stay" String="\b(?:FeatureSummary_&var_ref_re;_DESCRIPTION)\b"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect More directory-properties">
- <RegExpr attribute="Property" context="#stay" String="\bINTERPROCEDURAL_OPTIMIZATION_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bVS_GLOBAL_SECTION_POST_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bVS_GLOBAL_SECTION_PRE_&id_re;\b" />
+ <RegExpr attribute="Property" context="#stay" String="\b(?:(INTERPROCEDURAL_OPTIMIZATION|VS_GLOBAL_SECTION_(POST|PRE))_&var_ref_re;)\b"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect More target-properties">
- <RegExpr attribute="Property" context="#stay" String="\bARCHIVE_OUTPUT_DIRECTORY_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bARCHIVE_OUTPUT_NAME_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bCOMPILE_PDB_NAME_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bCOMPILE_PDB_OUTPUT_DIRECTORY_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\b&id_re;_OUTPUT_NAME\b" />
- <RegExpr attribute="Property" context="#stay" String="\b&id_re;_POSTFIX\b" />
- <RegExpr attribute="Property" context="#stay" String="\bEXCLUDE_FROM_DEFAULT_BUILD_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bIMPORTED_IMPLIB_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bIMPORTED_LIBNAME_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bIMPORTED_LINK_DEPENDENT_LIBRARIES_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bIMPORTED_LINK_INTERFACE_LANGUAGES_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bIMPORTED_LINK_INTERFACE_LIBRARIES_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bIMPORTED_LINK_INTERFACE_MULTIPLICITY_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bIMPORTED_LOCATION_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bIMPORTED_NO_SONAME_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bIMPORTED_OBJECTS_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bIMPORTED_SONAME_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bINTERPROCEDURAL_OPTIMIZATION_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\b&id_re;_CLANG_TIDY\b" />
- <RegExpr attribute="Property" context="#stay" String="\b&id_re;_COMPILER_LAUNCHER\b" />
- <RegExpr attribute="Property" context="#stay" String="\b&id_re;_CPPCHECK\b" />
- <RegExpr attribute="Property" context="#stay" String="\b&id_re;_CPPLINT\b" />
- <RegExpr attribute="Property" context="#stay" String="\b&id_re;_INCLUDE_WHAT_YOU_USE\b" />
- <RegExpr attribute="Property" context="#stay" String="\b&id_re;_VISIBILITY_PRESET\b" />
- <RegExpr attribute="Property" context="#stay" String="\bLIBRARY_OUTPUT_DIRECTORY_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bLIBRARY_OUTPUT_NAME_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bLINK_FLAGS_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bLINK_INTERFACE_LIBRARIES_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bLINK_INTERFACE_MULTIPLICITY_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bLOCATION_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bMAP_IMPORTED_CONFIG_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bOSX_ARCHITECTURES_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bOUTPUT_NAME_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bPDB_NAME_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bPDB_OUTPUT_DIRECTORY_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bRUNTIME_OUTPUT_DIRECTORY_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bRUNTIME_OUTPUT_NAME_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bSTATIC_LIBRARY_FLAGS_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bVS_DOTNET_REFERENCE_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bVS_DOTNET_REFERENCEPROP_&id_re;_TAG_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bVS_GLOBAL_&id_re;\b" />
- <RegExpr attribute="Property" context="#stay" String="\bXCODE_ATTRIBUTE_&id_re;\b" />
+ <RegExpr attribute="Property" context="#stay" String="\b(?:&var_ref_re;_((COMPIL|LINK)ER_LAUNCHER|CLANG_TIDY(_EXPORT_FIXES_DIR)?|CPP(CHECK|LINT)|INCLUDE_WHAT_YOU_USE|OUTPUT_NAME|POSTFIX|VISIBILITY_PRESET)|((ARCHIVE|LIBRARY|RUNTIME)_OUTPUT_(DIRECTORY|NAME)|COMPILE_PDB_(NAME|OUTPUT_DIRECTORY)|CXX_MODULE_(DIRS|SET)|EXCLUDE_FROM_DEFAULT_BUILD|FRAMEWORK_MULTI_CONFIG_POSTFIX|HEADER_(DIRS|SET)|IMPORTED_((NO_)?SONAME|IMPLIB|LIBNAME|LINK_(DEPENDENT_LIBRARIES|INTERFACE_(LANGUAGES|LIBRARIES|MULTIPLICITY))|LOCATION|OBJECTS)|INTERPROCEDURAL_OPTIMIZATION|LINK_(FLAGS|INTERFACE_(LIBRARIES|MULTIPLICITY))|LOCATION|MAP_IMPORTED_CONFIG|OSX_ARCHITECTURES|OUTPUT_NAME|PDB_(NAME|OUTPUT_DIRECTORY)|STATIC_LIBRARY_FLAGS|VS_(DOTNET_REFERENCE(PROP_&var_ref_re;_TAG)?|GLOBAL|SOURCE_SETTINGS))_&var_ref_re;|XCODE_(ATTRIBUTE_&var_ref_re;|EMBED_&var_ref_re;(_((CODE_SIGN|REMOVE_HEADERS)_ON_COPY|PATH))?))\b"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect More source-properties">
- <RegExpr attribute="Property" context="#stay" String="\bVS_CSHARP_&id_re;\b" />
- </context>
-
- <context attribute="Normal Text" lineEndContext="#stay" name="EndCmdPop">
- <DetectChar attribute="Normal Text" context="#pop" char=")" />
- </context>
-
- <context attribute="Normal Text" lineEndContext="#stay" name="EndCmdPop2">
- <DetectChar attribute="Normal Text" context="#pop#pop" char=")" />
+ <RegExpr attribute="Property" context="#stay" String="\b(?:VS_CSHARP_&var_ref_re;)\b"/>
</context>
<context attribute="User Function/Macro" lineEndContext="#stay" name="User Function">
- <DetectChar attribute="Normal Text" context="User Function Opened" char="(" />
- <IncludeRules context="EndCmdPop2" />
+ <DetectChar attribute="Normal Text" context="User Function Opened" char="("/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="User Function Opened">
- <IncludeRules context="EndCmdPop2" />
- <IncludeRules context="User Function Args" />
+ <DetectChar attribute="Normal Text" context="#pop" char=")" lookAhead="true"/>
+ <IncludeRules context="User Function Args"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Builtin Variables">
- <keyword attribute="Builtin Variable" context="#stay" String="variables" insensitive="false" />
- <IncludeRules context="Detect More Builtin Variables" />
- <RegExpr attribute="Internal Name" context="#stay" String="\b_&id_re;\b" />
+ <RegExpr attribute="Internal Name" context="#stay" String="\b_&var_ref_re;\b"/>
+ <keyword attribute="CMake Internal Variable" context="#stay" String="deprecated-or-internal-variables" insensitive="false"/>
+ <keyword attribute="Builtin Variable" context="#stay" String="variables" insensitive="false"/>
+ <IncludeRules context="Detect More Builtin Variables"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect More Builtin Variables">
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_BINARY_DIR\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_SOURCE_DIR\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_VERSION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_VERSION_MAJOR\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_VERSION_MINOR\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_VERSION_PATCH\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_VERSION_TWEAK\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_ROOT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;__TRYRUN_OUTPUT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_CONSIDERED_CONFIGS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_CONSIDERED_VERSIONS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FIND_COMPONENTS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FIND_QUIETLY\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FIND_REQUIRED\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FIND_REQUIRED_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FIND_VERSION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FIND_VERSION_COUNT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FIND_VERSION_EXACT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FIND_VERSION_MAJOR\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FIND_VERSION_MINOR\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FIND_VERSION_PATCH\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FIND_VERSION_TWEAK\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_FOUND\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_INCLUDE_DIRS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_LIBRARIES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_LIBRARY_DIRS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_VERSION_COUNT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\b&id_re;_VERSION_STRING\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_POSTFIX\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_ANDROID_TOOLCHAIN_MACHINE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_ANDROID_TOOLCHAIN_PREFIX\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_ANDROID_TOOLCHAIN_SUFFIX\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_ARCHIVE_APPEND\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_ARCHIVE_CREATE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_ARCHIVE_FINISH\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_CLANG_TIDY\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_COMPILER\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_COMPILER_ABI\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_COMPILER_AR\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_COMPILER_EXTERNAL_TOOLCHAIN\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_COMPILER_ID\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_COMPILER_LAUNCHER\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_COMPILER_LOADED\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_COMPILER_RANLIB\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_COMPILER_TARGET\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_COMPILER_VERSION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_COMPILE_OBJECT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_CPPCHECK\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_CPPLINT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_CREATE_SHARED_LIBRARY\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_CREATE_SHARED_MODULE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_CREATE_STATIC_LIBRARY\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_FLAGS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_FLAGS_DEBUG\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_FLAGS_DEBUG_INIT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_FLAGS_INIT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_FLAGS_MINSIZEREL\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_FLAGS_MINSIZEREL_INIT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_FLAGS_RELEASE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_FLAGS_RELEASE_INIT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_FLAGS_RELWITHDEBINFO\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_FLAGS_RELWITHDEBINFO_INIT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_GHS_KERNEL_FLAGS_DEBUG\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_GHS_KERNEL_FLAGS_MINSIZEREL\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_GHS_KERNEL_FLAGS_RELEASE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_GHS_KERNEL_FLAGS_RELWITHDEBINFO\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_IGNORE_EXTENSIONS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_IMPLICIT_INCLUDE_DIRECTORIES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_IMPLICIT_LINK_DIRECTORIES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_IMPLICIT_LINK_LIBRARIES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_INCLUDE_WHAT_YOU_USE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_LIBRARY_ARCHITECTURE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_LINKER_PREFERENCE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_LINKER_PREFERENCE_PROPAGATES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_LINKER_WRAPPER_FLAG\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_LINKER_WRAPPER_FLAG_SEP\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_LINK_EXECUTABLE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_OUTPUT_EXTENSION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_PLATFORM_ID\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_SIMULATE_ID\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_SIMULATE_VERSION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_SIZEOF_DATA_PTR\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_SOURCE_FILE_EXTENSIONS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_STANDARD_INCLUDE_DIRECTORIES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_STANDARD_LIBRARIES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_&id_re;_VISIBILITY_PRESET\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_ARCHIVE_OUTPUT_DIRECTORY_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_COMPILE_PDB_OUTPUT_DIRECTORY_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_DISABLE_FIND_PACKAGE_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_EXE_LINKER_FLAGS_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_EXE_LINKER_FLAGS_&id_re;_INIT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_INTERPROCEDURAL_OPTIMIZATION_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_LIBRARY_OUTPUT_DIRECTORY_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_MAP_IMPORTED_CONFIG_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_MATCH_[0-9]+\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_MODULE_LINKER_FLAGS_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_MODULE_LINKER_FLAGS_&id_re;_INIT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_PDB_OUTPUT_DIRECTORY_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_POLICY_DEFAULT_CMP&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_POLICY_WARNING_CMP&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_PROJECT_&id_re;_INCLUDE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_RUNTIME_OUTPUT_DIRECTORY_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_SHARED_LINKER_FLAGS_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_SHARED_LINKER_FLAGS_&id_re;_INIT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_STATIC_LINKER_FLAGS_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_STATIC_LINKER_FLAGS_&id_re;_INIT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_USER_MAKE_RULES_OVERRIDE_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCMAKE_XCODE_ATTRIBUTE_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_&id_re;_COMPONENT_INSTALL\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_ARCHIVE_&id_re;_FILE_NAME\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_BINARY_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_COMPONENT_&id_re;_DEPENDS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_COMPONENT_&id_re;_DESCRIPTION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_COMPONENT_&id_re;_DISABLED\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_COMPONENT_&id_re;_DISPLAY_NAME\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_COMPONENT_&id_re;_GROUP\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_COMPONENT_&id_re;_HIDDEN\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_COMPONENT_&id_re;_REQUIRED\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_COMPONENT_&id_re;_DESCRIPTION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_FILE_NAME\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_ARCHITECTURE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_BREAKS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_CONFLICTS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_CONTROL_EXTRA\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_CONTROL_STRICT_PERMISSION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_DEPENDS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_ENHANCES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_NAME\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_PREDEPENDS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_PRIORITY\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_PROVIDES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_RECOMMENDS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_REPLACES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_SECTION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_SHLIBDEPS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_SOURCE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_PACKAGE_SUGGESTS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_DEBIAN_&id_re;_DEBUGINFO_PACKAGE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NSIS_&id_re;_INSTALL_DIRECTORY\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_AUTHORS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_COPYRIGHT\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_DEPENDENCIES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_DEPENDENCIES_&id_re;_VERSION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_DESCRIPTION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_DESCRIPTION_SUMMARY\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_HOMEPAGE_URL\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_ICONURL\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_LICENSEURL\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_NAME\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_OWNERS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_RELEASE_NOTES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_TAGS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_TITLE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_&id_re;_PACKAGE_VERSION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_NUGET_PACKAGE_DEPENDENCIES_&id_re;_VERSION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_DEFAULT_DIR_PERMISSIONS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_DEFAULT_FILE_PERMISSIONS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_DEFAULT_GROUP\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_DEFAULT_USER\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_BUILD_SOURCE_DIRS_PREFIX\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_DEBUGINFO_FILE_NAME\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_DEBUGINFO_PACKAGE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_FILE_NAME\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_ARCHITECTURE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_AUTOPROV\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_AUTOREQ\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_AUTOREQPROV\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_CONFLICTS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_DESCRIPTION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_GROUP\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_NAME\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_OBSOLETES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_PREFIX\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_PROVIDES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_REQUIRES\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_REQUIRES_POST\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_REQUIRES_POSTUN\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_REQUIRES_PRE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_REQUIRES_PREUN\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_SUGGESTS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_SUMMARY\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_PACKAGE_URL\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_USER_FILELIST\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_&id_re;_USER_BINARY_SPECFILE\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_RPM_NO_&id_re;_INSTALL_PREFIX_RELOCATION\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_WIX_&id_re;_EXTENSIONS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_WIX_&id_re;_EXTRA_FLAGS\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bCPACK_WIX_PROPERTY_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bDOXYGEN_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bExternalData_CUSTOM_SCRIPT_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bExternalData_URL_ALGO_&id_re;_&id_re;\b" />
- <RegExpr attribute="Builtin Variable" context="#stay" String="\bSWIG_MODULE_&id_re;_EXTRA_DEPS\b" />
+ <RegExpr attribute="CMake Internal Variable" context="#stay" String="\b(?:CMAKE_&var_ref_re;_(COMPILER_(ABI|ARCHITECTURE_ID|VERSION_INTERNAL)|PLATFORM_ID))\b"/>
+ <RegExpr attribute="Builtin Variable" context="#stay" String="\b(?:&var_ref_re;_(((STATIC_)?LINK_)?LIBRARIES|(BINARY|SOURCE)_DIR|(C|LD)FLAGS(_OTHER)?|(INCLUDE|LIBRARY)_DIRS|CONFIG|CONSIDERED_(CONFIGS|VERSIONS)|DESCRIPTION|FIND_(COMPONENTS|REQUIRED(_&var_ref_re;)?|VERSION_(COMPLETE|COUNT|EXACT|M(AX|IN)(_(COUNT|MAJOR|MINOR|PATCH|TWEAK))?|RANGE(_(MAX|MIN))?)|QUIETLY)|FOUND|HOMEPAGE_URL|IS_TOP_LEVEL|KEYWORDS_MISSING_VALUES|MODULE_NAME|ROOT|UNPARSED_ARGUMENTS|VERSION(_(MAJOR|MINOR|PATCH|TWEAK|COUNT|STRING))?)|&var_ref_re;__TRYRUN_OUTPUT|(DOXYGEN|ExternalData_(CUSTOM_SCRIPT|URL_ALGO)|FETCHCONTENT_(SOURCE_DIR|UPDATES_DISCONNECTED))_&var_ref_re;|ARGV[0-9]+|BISON_&var_ref_re;_(COMPILE_FLAGS|DEFINED|INPUT|OUTPUT(S|_(HEADER|SOURCE)))|Boost_&var_ref_re;_LIBRARY(_(DEBUG|RELEASE))?|CMAKE_(&var_ref_re;_(ANDROID_TOOLCHAIN_((PRE|SUF)FIX|MACHINE)|ARCHIVE_(APPEND|CREATE|FINISH)|BYTE_ORDER|CLANG_TIDY(_EXPORT_FIXES_DIR)?|COMPILER(_(AR|EXTERNAL_TOOLCHAIN|FRONTEND_VARIANT|ID|LAUNCHER|LOADED|RANLIB|TARGET|VERSION))?|COMPILE_OBJECT|CPP(CHECK|LINT)|CREATE_(SHARED_(LIBRARY|MODULE)|STATIC_LIBRARY)|EXTENSIONS(_DEFAULT)?|FLAGS(_((DEBUG|MINSIZEREL|REL(EASE|WITHDEBINFO)|&var_ref_re;)(_INIT)?|INIT))?|GHS_KERNEL_FLAGS_(DEBUG|MINSIZEREL|REL(EASE|WITHDEBINFO))|IGNORE_EXTENSIONS|IMPLICIT_(INCLUDE_DIRECTORIES|LINK_((FRAMEWORK_)?DIRECTORIES|LIBRARIES))|INCLUDE_WHAT_YOU_USE|LIBRARY_ARCHITECTURE|LINKER_(LAUNCHER|PREFERENCE(_PROPAGATES)?|WRAPPER_FLAG(_SEP)?)|LINK_(EXECUTABLE|GROUP_USING_&var_ref_re;(_SUPPORTED)?|LIBRARY_(FILE_FLAG|FLAG|USING_&var_ref_re;(_SUPPORTED)?)|WHAT_YOU_USE_FLAG)|OUTPUT_EXTENSION|POSTFIX|SIMULATE_(ID|VERSION)|SIZEOF_DATA_PTR|SOURCE_FILE_EXTENSIONS|STANDARD_(INCLUDE_DIRECTO|LIBRA)RIES|VISIBILITY_PRESET)|((ARCHIVE|(COMPILE_)?PDB|LIBRARY|RUNTIME)_OUTPUT_DIRECTORY|(DISABLE|REQUIRE)_FIND_PACKAGE|FRAMEWORK_MULTI_CONFIG_POSTFIX|GET_OS_RELEASE_FALLBACK_RESULT|INTERPROCEDURAL_OPTIMIZATION|MAP_IMPORTED_CONFIG|USER_MAKE_RULES_OVERRIDE|XCODE_ATTRIBUTE)_&var_ref_re;|(EXE|MODULE|SHARED|STATIC)_LINKER_FLAGS_&var_ref_re;(_INIT)?|LINK_(GROUP|LIBRARY)_USING_&var_ref_re;(_SUPPORTED)?|PROJECT_&var_ref_re;_INCLUDE(_BEFORE)?)|CMAKE_(ARGV|MATCH_)[0-9]+|CMAKE_POLICY_(DEFAULT|WARNING)_CMP[0-9]{4}|CPACK_(&var_ref_re;_COMPONENT_INSTALL|ARCHIVE_&var_ref_re;_FILE_NAME|BINARY_&var_ref_re;|COMPONENT_&var_ref_re;_(DEPENDS|DESCRIPTION|DIS(ABLED|PLAY_NAME)|GROUP|HIDDEN|REQUIRED)|DEBIAN_&var_ref_re;_(DESCRIPTION|FILE_NAME|PACKAGE_((PRE)?DEPENDS|ARCHITECTURE|BREAKS|CONFLICTS|CONTROL_(EXTRA|STRICT_PERMISSION)|ENHANCES|NAME|PRIORITY|PROVIDES|RECOMMENDS|REPLACES|SECTION|SHLIBDEPS|SOURCE|SUGGESTS)|DEBUGINFO_PACKAGE)|DMG_&var_ref_re;_FILE_NAME|INNOSETUP_(&var_ref_re;_INSTALL_DIRECTORY|(DEFINE|SETUP)_&var_ref_re;)|NSIS_&var_ref_re;_INSTALL_DIRECTORY|NUGET_(&var_ref_re;_PACKAGE_(AUTHORS|COPYRIGHT|DEPENDENCIES(_&var_ref_re;_VERSION)?|DESCRIPTION(_SUMMARY)?|HOMEPAGE_URL|ICON(URL)?|LANGUAGE|LICENSE(URL|_(EXPRESSION|FILE_NAME))|NAME|OWNERS|RELEASE_NOTES|TAGS|TITLE|VERSION)|PACKAGE_DEPENDENCIES_&var_ref_re;_VERSION)|P(RE|OST)FLIGHT_&var_ref_re;_SCRIPT|RPM_(&var_ref_re;_(DEFAULT_((DIR|FILE)_PERMISSIONS|GROUP|USER)|BUILD_SOURCE_DIRS_PREFIX|DEBUGINFO_(FILE_NAME|PACKAGE)|FILE_NAME|PACKAGE_(ARCHITECTURE|AUTO(PROV|REQ(PROV)?)|CONFLICTS|DESCRIPTION|GROUP|NAME|OBSOLETES|PREFIX|PROVIDES|REQUIRES(_P(RE|OST)(UN)?)?|SUGGESTS|SUMMARY|URL)|USER_(FILELIST|BINARY_SPECFILE))|NO_&var_ref_re;_INSTALL_PREFIX_RELOCATION)|WIX_(&var_ref_re;_EXT(ENSIONS|RA_FLAGS)|PROPERTY_&var_ref_re;))|ICU_&var_ref_re;_(LIBRARY|EXECUTABLE)|MPI_&var_ref_re;_(ADDITIONAL_INCLUDE_VARS|COMPILE(R|_(DEFINI|OP)TIONS)|LIB(_NAMES|RARY))|OpenACC_&var_ref_re;_(FLAGS|OPTIONS|SPEC_DATE)|OpenMP_&var_ref_re;_(FLAGS|LIB(_NAMES|RARY)|SPEC_DATE)|SWIG_MODULE_&var_ref_re;_EXTRA_DEPS)\b"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Variable Substitutions">
- <RegExpr attribute="Environment Variable Substitution" context="#stay" String="\$ENV\{\s*[\w-]+\s*\}" />
- <Detect2Chars attribute="Variable Substitution" context="VarSubst" char="$" char1="{" />
- <RegExpr attribute="@Variable Substitution" context="@VarSubst" String="@&id_re;@" lookAhead="true" />
+ <RegExpr attribute="Cache Variable Substitution" context="#stay" String="\$CACHE\{\s*[\w-]+\s*\}"/>
+ <RegExpr attribute="Environment Variable Substitution" context="EnvVarSubst" String="\$?ENV\{"/>
+ <Detect2Chars attribute="Variable Substitution" context="VarSubst" char="$" char1="{"/>
+ <RegExpr attribute="@Variable Substitution" context="@VarSubst" String="@&var_ref_re;@" lookAhead="true"/>
+ </context>
+
+ <context attribute="Environment Variable Substitution" lineEndContext="#pop" name="EnvVarSubst">
+ <DetectChar attribute="Environment Variable Substitution" context="#pop" char="}"/>
+ <keyword attribute="Standard Environment Variable" context="#stay" String="environment-variables" insensitive="false"/>
+ <RegExpr attribute="Standard Environment Variable" context="#stay" String="\b(?:&var_ref_re;_(DIR|ROOT)|ASM&var_ref_re;(FLAGS)?|CMAKE_&var_ref_re;_(COMPIL|LINK)ER_LAUNCHER)\b"/>
+ <DetectIdentifier/>
+ <IncludeRules context="Detect Variable Substitutions"/>
</context>
<context attribute="Variable Substitution" lineEndContext="#pop" name="VarSubst">
- <IncludeRules context="Detect Builtin Variables" />
- <DetectIdentifier />
- <DetectChar attribute="Variable Substitution" context="#pop" char="}" />
- <IncludeRules context="Detect Variable Substitutions" />
+ <DetectChar attribute="Variable Substitution" context="#pop" char="}"/>
+ <IncludeRules context="Detect Builtin Variables"/>
+ <DetectIdentifier/>
+ <IncludeRules context="Detect Variable Substitutions"/>
</context>
<context attribute="@Variable Substitution" lineEndContext="#pop" name="@VarSubst">
- <DetectChar attribute="@Variable Substitution" context="VarSubst@" char="@" />
+ <DetectChar attribute="@Variable Substitution" context="VarSubst@" char="@"/>
</context>
<context attribute="@Variable Substitution" lineEndContext="#pop#pop" name="VarSubst@">
- <IncludeRules context="Detect Builtin Variables" />
- <DetectIdentifier />
- <DetectChar attribute="@Variable Substitution" context="#pop#pop" char="@" />
+ <DetectChar attribute="@Variable Substitution" context="#pop#pop" char="@"/>
+ <IncludeRules context="Detect Builtin Variables"/>
+ <DetectIdentifier/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="Target Name">
+ <DetectSpaces/>
+ <RegExpr attribute="Aliased Targets" context="#pop" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ <IncludeRules context="Detect Targets"/>
+ <IncludeRules context="User Function Opened"/>
+ <IncludeRules context="LineError"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="Detect Targets">
+ <RegExpr attribute="Targets" context="#stay" String="&tgt_name_re;"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="LineError">
+ <RegExpr attribute="Error" context="#stay" String=".*"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="User Function Args">
- <Detect2Chars attribute="Normal Text" context="#stay" char="\" char1="(" />
- <Detect2Chars attribute="Normal Text" context="#stay" char="\" char1=")" />
- <RegExpr attribute="Escapes" context="#stay" String="\\[&quot;$n\\]" />
- <DetectChar attribute="Strings" context="String" char="&quot;" />
- <RegExpr attribute="Strings" context="Bracketed String" String="\[(=*)\[" />
- <RegExpr attribute="Comment" context="Bracketed Comment" String="#\[(=*)\[" />
- <DetectChar attribute="Comment" context="Comment" char="#" />
- <IncludeRules context="Detect Builtin Variables" />
- <IncludeRules context="Detect Variable Substitutions" />
- <IncludeRules context="Detect Special Values" />
- <IncludeRules context="Detect Aliased Targets" />
- <IncludeRules context="Detect Generator Expressions" />
+ <Detect2Chars attribute="Normal Text" context="#stay" char="\" char1="("/>
+ <Detect2Chars attribute="Normal Text" context="#stay" char="\" char1=")"/>
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="&quot;"/>
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="$"/>
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="n"/>
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="\"/>
+ <DetectChar attribute="Strings" context="String" char="&quot;"/>
+ <RegExpr attribute="Strings" context="Bracketed String" String="\[(=*)\[" beginRegion="BracketedString"/>
+ <DetectChar attribute="Comment" context="Match Comments" char="#" lookAhead="true"/>
+ <IncludeRules context="Detect Builtin Variables"/>
+ <IncludeRules context="Detect Variable Substitutions"/>
+ <IncludeRules context="Detect Special Values"/>
+ <IncludeRules context="Detect Aliased Targets"/>
+ <IncludeRules context="Detect Generator Expressions"/>
+ <DetectIdentifier/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Special Values">
- <RegExpr attribute="True Special Arg" context="#stay" String="\b(TRUE|ON)\b" />
- <RegExpr attribute="False Special Arg" context="#stay" String="\b(FALSE|OFF|(&id_re;-)?NOTFOUND)\b" />
- <RegExpr attribute="Special Args" context="#stay" String="\bCMP[0-9][0-9][0-9]\b" />
+ <RegExpr attribute="Version Arg" context="#stay" String="\b[0-9]++(.[0-9]++)+\b"/>
+ <keyword attribute="True Special Arg" context="#stay" String="true_special_arg" insensitive="true"/>
+ <keyword attribute="False Special Arg" context="#stay" String="false_special_arg" insensitive="true"/>
+ <RegExpr attribute="False Special Arg" context="#stay" String="\b(?:&var_ref_re;-)?NOTFOUND\b"/>
+ <RegExpr attribute="Special Args" context="#stay" String="\bCMP[0-9][0-9][0-9][0-9]\b"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Aliased Targets">
- <RegExpr attribute="Aliased Targets" context="#stay" String="\b&id_re;::&id_re;(::&id_re;)*\b" />
+ <RegExpr attribute="Aliased Targets" context="#stay" String="&tgt_name_re;::&tgt_name_re;(?:\:\:&tgt_name_re;)*"/>
+ </context>
+
+ <context attribute="Comment" lineEndContext="#pop" name="Match Comments">
+ <DetectSpaces/>
+ <RegExpr attribute="Comment" context="#pop!Bracketed Comment" String="#\[(=*)\[" beginRegion="BracketedComment"/>
+ <DetectChar attribute="Comment" context="#pop!Comment" char="#"/>
+ <DetectIdentifier/>
+ </context>
+
+ <context attribute="Comment" lineEndContext="#pop" name="Match Comments and Docs">
+ <RegExpr attribute="Region Marker" context="#pop!RST Documentation" String="^#\[(=*)\[\.rst:" column="0" beginRegion="RSTDocumentation"/>
+ <IncludeRules context="Match Comments"/>
</context>
<context attribute="Comment" lineEndContext="#pop" name="Comment">
- <LineContinue attribute="Comment" context="#pop" />
- <IncludeRules context="##Alerts" />
- <IncludeRules context="##Modelines" />
+ <DetectSpaces/>
+ <LineContinue attribute="Comment" context="#pop"/>
+ <IncludeRules context="##Comments"/>
+ <DetectIdentifier/>
</context>
<context attribute="Comment" lineEndContext="#stay" name="RST Documentation" dynamic="true">
- <RegExpr attribute="Region Marker" context="#pop" String="^#?\]%1\]" dynamic="true" column="0" />
- <IncludeRules context="##reStructuredText" />
+ <RegExpr attribute="Region Marker" context="#pop" String="^#?\]%1\]" dynamic="true" column="0" endRegion="RSTDocumentation"/>
+ <IncludeRules context="##reStructuredText"/>
</context>
<context attribute="Comment" lineEndContext="#stay" name="Bracketed Comment" dynamic="true">
- <RegExpr attribute="Comment" context="#pop" String=".*\]%1\]" dynamic="true" />
- <IncludeRules context="##Alerts" />
- <IncludeRules context="##Modelines" />
+ <LineContinue attribute="Comment" context="#stay"/>
+ <DetectSpaces/>
+ <StringDetect attribute="Comment" context="#pop" String="]%1]" dynamic="true" endRegion="BracketedComment"/>
+ <IncludeRules context="##Comments"/>
</context>
<context attribute="Strings" lineEndContext="#stay" name="String">
- <RegExpr attribute="Strings" context="#pop" String="&quot;(?=[ );]|$)" />
- <RegExpr attribute="Escapes" context="#stay" String="\\[&quot;$nrt\\]" />
- <IncludeRules context="Detect Variable Substitutions" />
- <IncludeRules context="Detect Generator Expressions" />
+ <DetectSpaces/>
+ <DetectIdentifier/>
+ <RegExpr attribute="Strings" context="#pop" String="&quot;(?=[ );]|$)"/>
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="&quot;"/>
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="$"/>
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="n"/>
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="r"/>
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="t"/>
+ <Detect2Chars attribute="Escapes" context="#stay" char="\" char1="\"/>
+ <IncludeRules context="Detect Variable Substitutions"/>
+ <IncludeRules context="Detect Generator Expressions"/>
</context>
<context attribute="Strings" lineEndContext="#stay" name="Bracketed String" dynamic="true">
- <RegExpr attribute="Strings" context="#pop" String="\]%1\]" dynamic="true" />
+ <StringDetect attribute="Strings" context="#pop" String="]%1]" dynamic="true" endRegion="BracketedString"/>
</context>
<context attribute="Normal Text" lineEndContext="#stay" name="Detect Generator Expressions">
- <Detect2Chars attribute="Generator Expression" context="Generator Expression" char="$" char1="&lt;" />
+ <Detect2Chars attribute="Generator Expression" context="Generator Expression" char="$" char1="&lt;"/>
</context>
<context attribute="Generator Expression" lineEndContext="#stay" name="Generator Expression">
- <IncludeRules context="Detect Generator Expressions" />
- <DetectChar attribute="Comment" context="Comment" char="#" />
- <DetectChar attribute="Generator Expression" context="#pop" char="&gt;" />
- <keyword attribute="Generator Expression Keyword" context="#stay" String="generator-expressions" insensitive="false" />
- <IncludeRules context="Detect Aliased Targets" />
- <IncludeRules context="Detect Variable Substitutions" />
+ <IncludeRules context="Detect Generator Expressions"/>
+ <DetectChar attribute="Comment" context="Comment" char="#"/>
+ <DetectChar attribute="Generator Expression" context="#pop" char="&gt;"/>
+ <keyword attribute="Generator Expression Keyword" context="#stay" String="generator-expressions" insensitive="false"/>
+ <WordDetect String="LIST" attribute="Generator Expression Keyword" context="genex_LIST_ctx"/>
+ <WordDetect String="PATH" attribute="Generator Expression Keyword" context="genex_PATH_ctx"/>
+ <IncludeRules context="Detect Aliased Targets"/>
+ <IncludeRules context="Detect Variable Substitutions"/>
+ <DetectIdentifier/>
+ </context>
+ <context attribute="Generator Expression" lineEndContext="#stay" name="genex_LIST_ctx" fallthroughContext="#pop">
+ <DetectChar char=":" context="#stay"/>
+ <DetectSpaces/>
+ <keyword attribute="Generator Expression Sub-Command" context="#pop" String="genex-LIST-subcommands" insensitive="false"/>
+ </context>
+ <context attribute="Generator Expression" lineEndContext="#stay" name="genex_PATH_ctx" fallthroughContext="#pop">
+ <DetectChar char=":" context="#stay"/>
+ <DetectSpaces/>
+ <keyword attribute="Generator Expression Sub-Command" context="#pop" String="genex-PATH-subcommands" insensitive="false"/>
</context>
</contexts>
<itemDatas>
- <itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false" />
- <itemData name="Command" defStyleNum="dsKeyword" spellChecking="false" />
- <itemData name="User Function/Macro" defStyleNum="dsFunction" spellChecking="false" />
- <itemData name="Property" defStyleNum="dsOthers" spellChecking="false" />
- <itemData name="Aliased Targets" defStyleNum="dsBaseN" spellChecking="false" />
- <itemData name="Named Args" defStyleNum="dsOthers" spellChecking="false" />
- <itemData name="Special Args" defStyleNum="dsOthers" spellChecking="false" />
- <itemData name="True Special Arg" defStyleNum="dsOthers" color="#30a030" selColor="#30a030" spellChecking="false" />
- <itemData name="False Special Arg" defStyleNum="dsOthers" color="#e05050" selColor="#e05050" spellChecking="false" />
- <itemData name="Strings" defStyleNum="dsString" spellChecking="true" />
- <itemData name="Escapes" defStyleNum="dsChar" spellChecking="false" />
- <itemData name="Builtin Variable" defStyleNum="dsDecVal" color="#c09050" selColor="#c09050" spellChecking="false" />
- <itemData name="Variable Substitution" defStyleNum="dsDecVal" spellChecking="false" />
- <itemData name="@Variable Substitution" defStyleNum="dsBaseN" spellChecking="false" />
- <itemData name="Internal Name" defStyleNum="dsDecVal" color="#303030" selColor="#303030" spellChecking="false" />
- <itemData name="Environment Variable Substitution" defStyleNum="dsFloat" spellChecking="false" />
- <itemData name="Generator Expression Keyword" defStyleNum="dsKeyword" color="#b84040" selColor="#b84040" spellChecking="false" />
- <itemData name="Generator Expression" defStyleNum="dsOthers" color="#b86050" selColor="#b86050" spellChecking="false" />
- <itemData name="Comment" defStyleNum="dsComment" spellChecking="true" />
- <itemData name="Region Marker" defStyleNum="dsRegionMarker" spellChecking="false" />
+ <itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false"/>
+ <itemData name="Comment" defStyleNum="dsComment" spellChecking="true"/>
+ <itemData name="Command" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="Control Flow" defStyleNum="dsControlFlow" spellChecking="false"/>
+ <itemData name="CMake Provided Function/Macro" defStyleNum="dsFunction" bold="true" spellChecking="false"/>
+ <itemData name="User Function/Macro" defStyleNum="dsFunction" spellChecking="false"/>
+ <itemData name="Property" defStyleNum="dsOthers" spellChecking="false"/>
+ <itemData name="Targets" defStyleNum="dsBaseN" spellChecking="false"/>
+ <itemData name="Aliased Targets" defStyleNum="dsBaseN" spellChecking="false"/>
+ <itemData name="Named Args" defStyleNum="dsOthers" spellChecking="false"/>
+ <itemData name="Special Args" defStyleNum="dsOthers" spellChecking="false"/>
+ <itemData name="True Special Arg" defStyleNum="dsOthers" color="#30a030" selColor="#30a030" spellChecking="false"/>
+ <itemData name="False Special Arg" defStyleNum="dsOthers" color="#e05050" selColor="#e05050" spellChecking="false"/>
+ <itemData name="Version Arg" defStyleNum="dsDataType" spellChecking="false"/>
+ <itemData name="Strings" defStyleNum="dsString" spellChecking="true"/>
+ <itemData name="Escapes" defStyleNum="dsSpecialChar" spellChecking="false"/>
+ <itemData name="Builtin Variable" defStyleNum="dsDecVal" color="#c09050" selColor="#c09050" spellChecking="false"/>
+ <itemData name="CMake Internal Variable" defStyleNum="dsVariable" spellChecking="false"/>
+ <itemData name="Internal Name" defStyleNum="dsVariable" spellChecking="false"/>
+ <itemData name="Variable Substitution" defStyleNum="dsDecVal" spellChecking="false"/>
+ <itemData name="@Variable Substitution" defStyleNum="dsBaseN" spellChecking="false"/>
+ <itemData name="Cache Variable Substitution" defStyleNum="dsFloat" spellChecking="false"/>
+ <itemData name="Environment Variable Substitution" defStyleNum="dsFloat" spellChecking="false"/>
+ <itemData name="Standard Environment Variable" defStyleNum="dsFloat" spellChecking="false"/>
+ <itemData name="Generator Expression Keyword" defStyleNum="dsKeyword" color="#b84040" selColor="#b84040" spellChecking="false"/>
+ <itemData name="Generator Expression Sub-Command" defStyleNum="dsKeyword" color="#c05050" selColor="#c05050" spellChecking="false"/>
+ <itemData name="Generator Expression" defStyleNum="dsOthers" color="#b86050" selColor="#b86050" spellChecking="false"/>
+ <itemData name="Standard Module" defStyleNum="dsImport" spellChecking="false"/>
+ <itemData name="Deprecated Module" defStyleNum="dsImport" spellChecking="false"/>
+ <itemData name="Region Marker" defStyleNum="dsRegionMarker" spellChecking="false"/>
+ <itemData name="Error" defStyleNum="dsError" spellChecking="false"/>
</itemDatas>
</highlighting>
<general>
<comments>
- <comment name="singleLine" start="#" />
+ <comment name="singleLine" start="#" position="afterwhitespace"/>
+ <comment name="multiLine" start="#[[" end="]]" region="BracketedComment"/>
</comments>
- <keywords casesensitive="1" />
+ <keywords casesensitive="1" weakDeliminator="."/>
</general>
</language>
-
-<!-- kate: indent-width 2; tab-width 2; -->
-
+<!-- kate: replace-tabs on; indent-width 2; tab-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/comments.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/comments.xml
new file mode 100644
index 0000000000..011dc8ab21
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/comments.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language>
+<language
+ version="1"
+ kateversion="5.0"
+ name="Comments"
+ section="Other"
+ extensions=""
+ mimetype=""
+ author="Alex Turbov (i.zaufi@gmail.com)"
+ license="MIT"
+ hidden="true"
+ >
+ <highlighting>
+ <contexts>
+ <context name="Normal" attribute="Comment" lineEndContext="#pop">
+ <IncludeRules context="##Alerts" />
+ <IncludeRules context="##Modelines" />
+ <IncludeRules context="##SPDX-Comments" />
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Comment" defStyleNum="dsComment" />
+ </itemDatas>
+ </highlighting>
+</language>
+<!-- kate: indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/css.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/css.xml
index 25a614e826..dd2f37ba35 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/css.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/css.xml
@@ -1,9 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd"
+<!DOCTYPE language
[
<!-- https://www.w3.org/TR/CSS22/syndata.html#tokenization -->
- <!ENTITY nmstart "[_a-zA-Z]|(\\[0-9a-fA-F]{1,6})|(\\[^\n\r\f0-9a-fA-F])">
- <!ENTITY nmchar "[_a-zA-Z0-9-]|(\\[0-9a-fA-F]{1,6})|(\\[^\n\r\f0-9a-fA-F])">
+ <!ENTITY escaped "\\([0-9a-fA-F]{1,6}|[^\n\r\f0-9a-fA-F])">
+ <!ENTITY ident "-?([_a-zA-Z]|&escaped;)([-_a-zA-Z0-9]+|&escaped;)*">
+ <!-- identifiers starting with several - are invalid, but supported by browsers -->
+ <!ENTITY ident2 "([-\w]+|&escaped;)+">
+ <!ENTITY var "--[-\w]*">
+ <!ENTITY func "-?[a-zA-Z][-a-zA-Z0-9]*">
+ <!ENTITY propcheck "(?=\s*(/[*].*?[*]/\s*)?:)">
]>
<!--
@@ -38,17 +43,23 @@ Changelog:
-->
-<language name="CSS" version="8" kateversion="5.0" section="Markup" extensions="*.css" indenter="cstyle" mimetype="text/css" author="Wilbert Berendsen (wilbert@kde.nl)" license="LGPL" priority="10">
-
+<language name="CSS" version="17" kateversion="5.79" section="Markup" extensions="*.css" indenter="cstyle" mimetype="text/css" author="Wilbert Berendsen (wilbert@kde.nl)" license="LGPL" priority="10">
<highlighting>
+ <!-- W3C > Work Draft, from https://www.w3.org/Style/CSS/all-properties (exclude FPWD)-->
<list name="properties">
- <!-- W3C > Work Draft, from https://www.w3.org/Style/CSS/all-properties -->
+ <item>-webkit-line-clamp</item>
+ <item>-webkit-text-fill-color</item>
+ <item>-webkit-text-stroke</item>
+ <item>-webkit-text-stroke-color</item>
+ <item>-webkit-text-stroke-width</item>
+ <item>accent-color</item>
+ <item>additive-symbols</item>
<item>align-content</item>
<item>align-items</item>
- <item>alignment-baseline</item>
<item>align-self</item>
<item>all</item>
<item>animation</item>
+ <item>animation-composition</item>
<item>animation-delay</item>
<item>animation-direction</item>
<item>animation-duration</item>
@@ -58,7 +69,9 @@ Changelog:
<item>animation-play-state</item>
<item>animation-timing-function</item>
<item>appearance</item>
- <item>azimuth</item>
+ <item>ascent-override</item>
+ <item>aspect-ratio</item>
+ <item>backdrop-filter</item>
<item>backface-visibility</item>
<item>background</item>
<item>background-attachment</item>
@@ -66,21 +79,14 @@ Changelog:
<item>background-clip</item>
<item>background-color</item>
<item>background-image</item>
- <item>background-image-transform</item>
<item>background-origin</item>
<item>background-position</item>
+ <item>background-position-x</item>
+ <item>background-position-y</item>
<item>background-repeat</item>
<item>background-size</item>
- <item>baseline-shift</item>
+ <item>base-palette</item>
<item>block-size</item>
- <item>block-step</item>
- <item>block-step-align</item>
- <item>block-step-insert</item>
- <item>block-step-round</item>
- <item>block-step-size</item>
- <item>bookmark-label</item>
- <item>bookmark-level</item>
- <item>bookmark-state</item>
<item>border</item>
<item>border-block</item>
<item>border-block-color</item>
@@ -100,15 +106,15 @@ Changelog:
<item>border-bottom-right-radius</item>
<item>border-bottom-style</item>
<item>border-bottom-width</item>
- <item>border-boundary</item>
<item>border-collapse</item>
<item>border-color</item>
+ <item>border-end-end-radius</item>
+ <item>border-end-start-radius</item>
<item>border-image</item>
<item>border-image-outset</item>
<item>border-image-repeat</item>
<item>border-image-slice</item>
<item>border-image-source</item>
- <item>border-image-transform</item>
<item>border-image-width</item>
<item>border-inline</item>
<item>border-inline-color</item>
@@ -132,6 +138,8 @@ Changelog:
<item>border-right-style</item>
<item>border-right-width</item>
<item>border-spacing</item>
+ <item>border-start-end-radius</item>
+ <item>border-start-start-radius</item>
<item>border-style</item>
<item>border-top</item>
<item>border-top-color</item>
@@ -144,22 +152,15 @@ Changelog:
<item>box-decoration-break</item>
<item>box-shadow</item>
<item>box-sizing</item>
- <item>box-snap</item>
<item>break-after</item>
<item>break-before</item>
<item>break-inside</item>
<item>caption-side</item>
- <item>caret</item>
<item>caret-color</item>
- <item>caret-shape</item>
<item>clear</item>
- <item>clip</item>
<item>clip-path</item>
- <item>clip-rule</item>
<item>color</item>
- <item>color-adjust</item>
- <item>color-interpolation-filters</item>
- <item>columns</item>
+ <item>color-scheme</item>
<item>column-count</item>
<item>column-fill</item>
<item>column-gap</item>
@@ -169,31 +170,26 @@ Changelog:
<item>column-rule-width</item>
<item>column-span</item>
<item>column-width</item>
+ <item>columns</item>
<item>contain</item>
+ <item>contain-intrinsic-block-size</item>
+ <item>contain-intrinsic-height</item>
+ <item>contain-intrinsic-inline-size</item>
+ <item>contain-intrinsic-size</item>
+ <item>contain-intrinsic-width</item>
+ <item>container</item>
+ <item>container-name</item>
+ <item>container-type</item>
<item>content</item>
- <item>continue</item>
<item>counter-increment</item>
<item>counter-reset</item>
<item>counter-set</item>
- <item>cue</item>
- <item>cue-after</item>
- <item>cue-before</item>
<item>cursor</item>
+ <item>descent-override</item>
<item>direction</item>
<item>display</item>
- <item>dominant-baseline</item>
- <item>elevation</item>
<item>empty-cells</item>
- <item>fill</item>
- <item>fill-break</item>
- <item>fill-color</item>
- <item>fill-image</item>
- <item>fill-opacity</item>
- <item>fill-origin</item>
- <item>fill-position</item>
- <item>fill-repeat</item>
- <item>fill-rule</item>
- <item>fill-size</item>
+ <item>fallback</item>
<item>filter</item>
<item>flex</item>
<item>flex-basis</item>
@@ -203,20 +199,12 @@ Changelog:
<item>flex-shrink</item>
<item>flex-wrap</item>
<item>float</item>
- <item>float-defer</item>
- <item>float-offset</item>
- <item>float-reference</item>
- <item>flood-color</item>
- <item>flood-opacity</item>
- <item>flow-from</item>
- <item>flow-into</item>
<item>font</item>
+ <item>font-display</item>
<item>font-family</item>
<item>font-feature-settings</item>
<item>font-kerning</item>
<item>font-language-override</item>
- <item>font-max-size</item>
- <item>font-min-size</item>
<item>font-optical-sizing</item>
<item>font-palette</item>
<item>font-size</item>
@@ -224,6 +212,10 @@ Changelog:
<item>font-stretch</item>
<item>font-style</item>
<item>font-synthesis</item>
+ <item>font-synthesis-position</item>
+ <item>font-synthesis-small-caps</item>
+ <item>font-synthesis-style</item>
+ <item>font-synthesis-weight</item>
<item>font-variant</item>
<item>font-variant-alternates</item>
<item>font-variant-caps</item>
@@ -234,10 +226,8 @@ Changelog:
<item>font-variant-position</item>
<item>font-variation-settings</item>
<item>font-weight</item>
- <item>footnote-display</item>
- <item>footnote-policy</item>
+ <item>forced-color-adjust</item>
<item>gap</item>
- <item>glyph-orientation-vertical</item>
<item>grid</item>
<item>grid-area</item>
<item>grid-auto-columns</item>
@@ -245,12 +235,9 @@ Changelog:
<item>grid-auto-rows</item>
<item>grid-column</item>
<item>grid-column-end</item>
- <item>grid-column-gap</item>
<item>grid-column-start</item>
- <item>grid-gap</item>
<item>grid-row</item>
<item>grid-row-end</item>
- <item>grid-row-gap</item>
<item>grid-row-start</item>
<item>grid-template</item>
<item>grid-template-areas</item>
@@ -260,16 +247,11 @@ Changelog:
<item>height</item>
<item>hyphenate-character</item>
<item>hyphenate-limit-chars</item>
- <item>hyphenate-limit-last</item>
- <item>hyphenate-limit-lines</item>
- <item>hyphenate-limit-zone</item>
<item>hyphens</item>
<item>image-orientation</item>
<item>image-rendering</item>
- <item>image-resolution</item>
- <item>initial-letter</item>
- <item>initial-letter-align</item>
- <item>initial-letter-wrap</item>
+ <item>inherits</item>
+ <item>initial-value</item>
<item>inline-size</item>
<item>inset</item>
<item>inset-block</item>
@@ -284,12 +266,9 @@ Changelog:
<item>justify-self</item>
<item>left</item>
<item>letter-spacing</item>
- <item>lighting-color</item>
<item>line-break</item>
- <item>line-grid</item>
+ <item>line-gap-override</item>
<item>line-height</item>
- <item>line-height-step</item>
- <item>line-snap</item>
<item>list-style</item>
<item>list-style-image</item>
<item>list-style-position</item>
@@ -305,19 +284,6 @@ Changelog:
<item>margin-left</item>
<item>margin-right</item>
<item>margin-top</item>
- <item>marker</item>
- <item>marker-end</item>
- <item>marker-knockout-left</item>
- <item>marker-knockout-right</item>
- <item>marker-mid</item>
- <item>marker-pattern</item>
- <item>marker-segment</item>
- <item>marker-side</item>
- <item>marker-start</item>
- <item>marquee-direction</item>
- <item>marquee-loop</item>
- <item>marquee-speed</item>
- <item>marquee-style</item>
<item>mask</item>
<item>mask-border</item>
<item>mask-border-mode</item>
@@ -335,32 +301,25 @@ Changelog:
<item>mask-repeat</item>
<item>mask-size</item>
<item>mask-type</item>
+ <item>math-depth</item>
+ <item>math-style</item>
<item>max-block-size</item>
<item>max-height</item>
<item>max-inline-size</item>
- <item>max-lines</item>
<item>max-width</item>
<item>min-block-size</item>
<item>min-height</item>
<item>min-inline-size</item>
<item>min-width</item>
<item>mix-blend-mode</item>
- <item>nav-up</item>
- <item>nav-down</item>
- <item>nav-left</item>
- <item>nav-right</item>
+ <item>negative</item>
<item>object-fit</item>
<item>object-position</item>
<item>offset</item>
- <item>offset-after</item>
<item>offset-anchor</item>
- <item>offset-before</item>
<item>offset-distance</item>
- <item>offset-end</item>
<item>offset-path</item>
- <item>offset-position</item>
<item>offset-rotate</item>
- <item>offset-start</item>
<item>opacity</item>
<item>order</item>
<item>orphans</item>
@@ -370,10 +329,20 @@ Changelog:
<item>outline-style</item>
<item>outline-width</item>
<item>overflow</item>
- <item>overflow-style</item>
+ <item>overflow-anchor</item>
+ <item>overflow-block</item>
+ <item>overflow-clip-margin</item>
+ <item>overflow-inline</item>
<item>overflow-wrap</item>
<item>overflow-x</item>
<item>overflow-y</item>
+ <item>override-colors</item>
+ <item>overscroll-behavior</item>
+ <item>overscroll-behavior-block</item>
+ <item>overscroll-behavior-inline</item>
+ <item>overscroll-behavior-x</item>
+ <item>overscroll-behavior-y</item>
+ <item>pad</item>
<item>padding</item>
<item>padding-block</item>
<item>padding-block-end</item>
@@ -389,75 +358,91 @@ Changelog:
<item>page-break-after</item>
<item>page-break-before</item>
<item>page-break-inside</item>
- <item>pause</item>
- <item>pause-after</item>
- <item>pause-before</item>
+ <item>page-orientation</item>
+ <item>paint-order</item>
<item>perspective</item>
<item>perspective-origin</item>
- <item>pitch</item>
- <item>pitch-range</item>
<item>place-content</item>
<item>place-items</item>
<item>place-self</item>
- <item>play-during</item>
<item>pointer-events</item>
<item>position</item>
+ <item>prefix</item>
+ <item>print-color-adjust</item>
<item>quotes</item>
- <item>region-fragment</item>
+ <item>range</item>
<item>resize</item>
- <item>richness</item>
<item>right</item>
- <item>rotation</item>
- <item>rotation-point</item>
+ <item>rotate</item>
<item>row-gap</item>
- <item>ruby-align</item>
- <item>ruby-merge</item>
<item>ruby-position</item>
- <item>running</item>
+ <item>scale</item>
+ <item>scroll-behavior</item>
+ <item>scroll-margin</item>
+ <item>scroll-margin-block</item>
+ <item>scroll-margin-block-end</item>
+ <item>scroll-margin-block-start</item>
+ <item>scroll-margin-bottom</item>
+ <item>scroll-margin-inline</item>
+ <item>scroll-margin-inline-end</item>
+ <item>scroll-margin-inline-start</item>
+ <item>scroll-margin-left</item>
+ <item>scroll-margin-right</item>
+ <item>scroll-margin-top</item>
+ <item>scroll-padding</item>
+ <item>scroll-padding-block</item>
+ <item>scroll-padding-block-end</item>
+ <item>scroll-padding-block-start</item>
+ <item>scroll-padding-bottom</item>
+ <item>scroll-padding-inline</item>
+ <item>scroll-padding-inline-end</item>
+ <item>scroll-padding-inline-start</item>
+ <item>scroll-padding-left</item>
+ <item>scroll-padding-right</item>
+ <item>scroll-padding-top</item>
+ <item>scroll-snap-align</item>
+ <item>scroll-snap-stop</item>
+ <item>scroll-snap-type</item>
+ <item>scrollbar-color</item>
<item>scrollbar-gutter</item>
+ <item>scrollbar-width</item>
<item>shape-image-threshold</item>
- <item>shape-inside</item>
<item>shape-margin</item>
<item>shape-outside</item>
<item>size</item>
- <item>speak</item>
- <item>speak-header</item>
- <item>speak-numeral</item>
- <item>speak-punctuation</item>
- <item>speech-rate</item>
- <item>stress</item>
- <item>string-set</item>
- <item>table-layout</item>
+ <item>size-adjust</item>
+ <item>speak-as</item>
+ <item>src</item>
+ <item>suffix</item>
+ <item>symbols</item>
+ <item>syntax</item>
+ <item>system</item>
<item>tab-size</item>
- <item>text-align-all</item>
+ <item>table-layout</item>
<item>text-align</item>
<item>text-align-last</item>
<item>text-combine-upright</item>
- <item>text-decoration-color</item>
<item>text-decoration</item>
+ <item>text-decoration-color</item>
<item>text-decoration-line</item>
- <item>text-decoration-skip</item>
<item>text-decoration-skip-ink</item>
<item>text-decoration-style</item>
- <item>text-decoration-width</item>
+ <item>text-decoration-thickness</item>
<item>text-emphasis</item>
<item>text-emphasis-color</item>
<item>text-emphasis-position</item>
- <item>text-emphasis-skip</item>
<item>text-emphasis-style</item>
<item>text-indent</item>
<item>text-justify</item>
<item>text-orientation</item>
<item>text-overflow</item>
+ <item>text-rendering</item>
<item>text-shadow</item>
- <item>text-space-collapse</item>
- <item>text-space-trim</item>
- <item>text-spacing</item>
<item>text-transform</item>
<item>text-underline-offset</item>
<item>text-underline-position</item>
- <item>text-wrap</item>
<item>top</item>
+ <item>touch-action</item>
<item>transform</item>
<item>transform-box</item>
<item>transform-origin</item>
@@ -467,47 +452,52 @@ Changelog:
<item>transition-duration</item>
<item>transition-property</item>
<item>transition-timing-function</item>
+ <item>translate</item>
<item>unicode-bidi</item>
+ <item>unicode-range</item>
<item>user-select</item>
<item>vertical-align</item>
<item>visibility</item>
- <item>voice-family</item>
- <item>volume</item>
<item>white-space</item>
<item>widows</item>
<item>width</item>
<item>will-change</item>
<item>word-break</item>
<item>word-spacing</item>
- <item>word-wrap</item>
- <item>wrap-after</item>
- <item>wrap-before</item>
- <item>wrap-flow</item>
- <item>wrap-inside</item>
- <item>wrap-through</item>
<item>writing-mode</item>
<item>z-index</item>
+ <!-- SVG only -->
+
+ <item>alignment-baseline</item>
+ <item>baseline-shift</item>
+ <item>color-interpolation</item>
+ <item>color-interpolation-filters</item>
+ <item>dominant-baseline</item>
+ <item>fill</item>
+ <item>fill-opacity</item>
+ <item>fill-rule</item>
+ <item>flood-color</item>
+ <item>flood-opacity</item>
+ <item>marker-end</item>
+ <item>marker-mid</item>
+ <item>marker-start</item>
+ <item>shape-rendering</item>
+ <item>stop-color</item>
+ <item>stop-opacity</item>
+ <item>stroke</item>
+ <item>stroke-dasharray</item>
+ <item>stroke-dashoffset</item>
+ <item>stroke-linecap</item>
+ <item>stroke-linejoin</item>
+ <item>stroke-miterlimit</item>
+ <item>stroke-opacity</item>
+ <item>text-anchor</item>
+ <item>vector-effect</item>
+ </list>
+
+ <list name="vendor properties">
<!-- Gecko rendering engine CSS property extensions -->
- <item>-moz-animation</item>
- <item>-moz-animation-delay</item>
- <item>-moz-animation-direction</item>
- <item>-moz-animation-duration</item>
- <item>-moz-animation-fill-mode</item>
- <item>-moz-animation-iteration-count</item>
- <item>-moz-animation-name</item>
- <item>-moz-animation-play-state</item>
- <item>-moz-animation-timing-function</item>
- <item>-moz-appearance</item>
- <item>-moz-background-clip</item>
- <item>-moz-background-origin</item>
- <item>-moz-background-size</item>
- <item>-moz-border-image</item>
- <item>-moz-border-radius</item>
- <item>-moz-border-radius-bottomleft</item>
- <item>-moz-border-radius-bottomright</item>
- <item>-moz-border-radius-topleft</item>
- <item>-moz-border-radius-topright</item>
<item>-moz-box-align</item>
<item>-moz-box-direction</item>
<item>-moz-box-flex</item>
@@ -515,80 +505,16 @@ Changelog:
<item>-moz-box-ordinal-group</item>
<item>-moz-box-orient</item>
<item>-moz-box-pack</item>
- <item>-moz-box-shadow</item>
- <item>-moz-box-sizing</item>
<item>-moz-box</item>
- <item>-moz-column-count</item>
- <item>-moz-column-fill</item>
- <item>-moz-column-gap</item>
- <item>-moz-column-rule</item>
- <item>-moz-column-rule-color</item>
- <item>-moz-column-rule-style</item>
- <item>-moz-column-rule-width</item>
- <item>-moz-columns</item>
- <item>-moz-column-width</item>
- <item>-moz-hyphens</item>
- <item>-moz-opacity</item>
- <item>-moz-outline-style</item>
- <item>-moz-perspective</item>
- <item>-moz-resize</item>
- <item>-moz-text-align-last</item>
- <item>-moz-text-decoration-color</item>
- <item>-moz-text-decoration-line</item>
- <item>-moz-text-decoration-style</item>
- <item>-moz-transform</item>
- <item>-moz-transform-origin</item>
- <item>-moz-transform-style</item>
- <item>-moz-transition</item>
- <item>-moz-transition-delay</item>
- <item>-moz-transition-duration</item>
- <item>-moz-transition-property</item>
- <item>-moz-transition-timing-function</item>
- <item>-moz-user-select</item>
<!-- Opera rendering engine CSS property extensions -->
- <item>-o-background-size</item>
<item>-o-linear-gradient</item>
- <item>-o-text-overflow</item>
- <item>-o-transition</item>
- <item>-o-transform-origin</item>
<!-- konq specific -->
<item>konq_bgpos_x</item>
<item>konq_bgpos_y</item>
- <item>-khtml-background-size</item>
- <item>-khtml-border-top-left-radius</item>
- <item>-khtml-border-top-right-radius</item>
- <item>-khtml-border-bottom-left-radius</item>
- <item>-khtml-border-bottom-right-radius</item>
- <item>-khtml-border-radius</item>
- <item>-khtml-box-shadow</item>
- <item>-khtml-opacity</item>
<!-- Webkit rendering engine CSS property extensions -->
- <item>-webkit-appearance</item>
- <item>-webkit-animation</item>
- <item>-webkit-animation-name</item>
- <item>-webkit-animation-duration</item>
- <item>-webkit-animation-iteration</item>
- <item>-webkit-animation-direction</item>
- <item>-webkit-animation-delay</item>
- <item>-webkit-animation-play-state</item>
- <item>-webkit-animation-fill-mode</item>
- <item>-webkit-background-size</item>
- <item>-webkit-backface-visibility</item>
- <item>-webkit-border-image</item>
- <item>-webkit-border-bottom-colors</item>
- <item>-webkit-border-left-colors</item>
- <item>-webkit-border-radius</item>
- <item>-webkit-border-right-colors</item>
- <item>-webkit-border-top-colors</item>
- <item>-webkit-border-top-left-radius</item>
- <item>-webkit-border-top-right-radius</item>
- <item>-webkit-border-bottom-left-radius</item>
- <item>-webkit-border-bottom-right-radius</item>
- <item>-webkit-border-radius-bottomleft</item>
- <item>-webkit-border-radius-bottomright</item>
<item>-webkit-box-align</item>
<item>-webkit-box-direction</item>
<item>-webkit-box-flex</item>
@@ -596,46 +522,17 @@ Changelog:
<item>-webkit-box-orient</item>
<item>-webkit-box-pack</item>
<item>-webkit-box-reflect</item>
- <item>-webkit-box-shadow</item>
- <item>-webkit-box-sizing</item>
- <item>-webkit-column-count</item>
- <item>-webkit-column-gap</item>
- <item>-webkit-hyphens</item>
<item>-webkit-linear-gradient</item>
<item>-webkit-gradient</item>
<item>-webkit-overflow-scrolling</item>
- <item>-webkit-perspective</item>
- <item>-webkit-text-decoration</item>
<item>-webkit-text-decoration-skip</item>
- <item>-webkit-text-fill-color</item>
- <item>-webkit-text-stroke-color</item>
- <item>-webkit-text-stroke-width</item>
<item>-webkit-text-size-adjust</item>
<item>-webkit-tap-highlight-color</item>
- <item>-webkit-transform</item>
- <item>-webkit-transform-origin</item>
- <item>-webkit-transform-style</item>
- <item>-webkit-transition</item>
- <item>-webkit-transition-property</item>
- <item>-webkit-transition-delay</item>
- <item>-webkit-transition-duration</item>
- <item>-webkit-user-select</item>
<!-- Trident (a.k.a., MSHTML) CSS property extensions -->
- <item>zoom</item>
- <item>-ms-animation-name</item>
- <item>-ms-animation-duration</item>
- <item>-ms-animation-iteration</item>
- <item>-ms-animation-direction</item>
- <item>-ms-animation-delay</item>
- <item>-ms-animation-play-state</item>
- <item>-ms-animation-fill-mode</item>
- <item>-ms-box-sizing</item>
<item>-ms-filter</item>
<item>-ms-flex</item>
<item>-ms-flex-align</item>
- <item>-ms-flex-direction</item>
- <item>-ms-flex-flow</item>
<item>-ms-flex-item-align</item>
<item>-ms-flex-line-pack</item>
<item>-ms-flex-negative</item>
@@ -644,249 +541,537 @@ Changelog:
<item>-ms-flex-positive</item>
<item>-ms-flex-position</item>
<item>-ms-flex-preferred-size</item>
- <item>-ms-flex-wrap</item>
<item>-ms-interpolation-mode</item>
<item>-ms-linear-gradient</item>
- <item>-ms-overflow-style</item>
<item>-ms-text-size-adjust</item>
- <item>-ms-transform</item>
- <item>-ms-transition</item>
- <item>-ms-user-select</item>
</list>
<list name="value keywords">
+ <item>auto</item>
<item>inherit</item>
+ <item>initial</item>
+ <item>revert</item>
+ <item>revert-layer</item>
<item>unset</item>
- <item>auto</item>
- <!-- <item>revert</item> -->
</list>
<list name="values">
- <item>none</item>
- <item>hidden</item>
- <item>dotted</item>
- <item>dashed</item>
- <item>solid</item>
- <item>double</item>
- <item>groove</item>
- <item>ridge</item>
- <item>inset</item>
- <item>outset</item>
- <item>xx-small</item>
- <item>x-small</item>
- <item>small</item>
- <item>medium</item>
- <item>large</item>
- <item>x-large</item>
- <item>xx-large</item>
- <item>smaller</item>
- <item>larger</item>
- <item>italic</item>
- <item>oblique</item>
- <item>small-caps</item>
- <item>normal</item>
- <item>bold</item>
- <item>bolder</item>
- <item>lighter</item>
- <item>light</item>
- <item>transparent</item>
- <item>repeat</item>
- <item>repeat-x</item>
- <item>repeat-y</item>
- <item>no-repeat</item>
- <item>baseline</item>
- <item>sub</item>
- <item>super</item>
- <item>top</item>
- <item>text-top</item>
- <item>middle</item>
- <item>bottom</item>
- <item>text-bottom</item>
- <item>left</item>
- <item>right</item>
- <item>center</item>
- <item>justify</item>
- <item>konq-center</item>
- <item>disc</item>
- <item>circle</item>
- <item>square</item>
- <item>box</item>
- <item>decimal</item>
- <item>decimal-leading-zero</item>
- <item>lower-roman</item>
- <item>upper-roman</item>
- <item>lower-greek</item>
- <item>lower-alpha</item>
- <item>lower-latin</item>
- <item>upper-alpha</item>
- <item>upper-latin</item>
- <item>hebrew</item>
- <item>armenian</item>
- <item>georgian</item>
- <item>cjk-ideographic</item>
- <item>hiragana</item>
- <item>katakana</item>
- <item>hiragana-iroha</item>
- <item>katakana-iroha</item>
- <item>inline</item>
- <item>inline-block</item>
- <item>block</item>
- <item>list-item</item>
- <item>run-in</item>
- <item>compact</item>
- <item>marker</item>
- <item>table</item>
- <item>inline-table</item>
- <item>table-row-group</item>
- <item>table-header-group</item>
- <item>table-footer-group</item>
- <item>table-row</item>
- <item>table-column-group</item>
- <item>table-column</item>
- <item>table-cell</item>
- <item>table-caption</item>
- <item>crosshair</item>
- <item>default</item>
- <item>pointer</item>
- <item>move</item>
- <item>e-resize</item>
- <item>ne-resize</item>
- <item>nw-resize</item>
- <item>n-resize</item>
- <item>se-resize</item>
- <item>sw-resize</item>
- <item>s-resize</item>
- <item>w-resize</item>
- <item>text</item>
- <item>wait</item>
- <item>help</item>
- <item>above</item>
+ <item>-moz-arabic-indic</item>
+ <item>-moz-bengali</item>
+ <item>-moz-cjk-earthly-branch</item>
+ <item>-moz-cjk-heavenly-stem</item>
+ <item>-moz-devanagari</item>
+ <item>-moz-gujarati</item>
+ <item>-moz-gurmukhi</item>
+ <item>-moz-kannada</item>
+ <item>-moz-lao</item>
+ <item>-moz-malayalam</item>
+ <item>-moz-myanmar</item>
+ <item>-moz-oriya</item>
+ <item>-moz-persian</item>
+ <item>-moz-tamil</item>
+ <item>-moz-telugu</item>
+ <item>-moz-thai</item>
<item>absolute</item>
+ <item>accumulate</item>
+ <item>add</item>
+ <item>additive</item>
+ <item>alias</item>
+ <item>all</item>
+ <item>all-petite-caps</item>
+ <item>all-scroll</item>
+ <item>all-small-caps</item>
+ <item>allow-end</item>
+ <item>alpha</item>
+ <item>alphabetic</item>
+ <item>alternate</item>
+ <item>alternate-reverse</item>
<item>always</item>
+ <item>anywhere</item>
+ <item>arabic-indic</item>
+ <item>armenian</item>
+ <item>auto-add</item>
+ <item>auto-fill</item>
+ <item>auto-fit</item>
<item>avoid</item>
- <item>below</item>
+ <item>avoid-column</item>
+ <item>avoid-page</item>
+ <item>avoid-region</item>
+ <item>backwards</item>
+ <item>balance</item>
+ <item>balance-all</item>
+ <item>baseline</item>
+ <item>bengali</item>
<item>bidi-override</item>
- <item>blink</item>
+ <item>bigger</item>
+ <item>block</item>
+ <item>block-end</item>
+ <item>block-start</item>
+ <item>bold</item>
+ <item>bolder</item>
+ <item>border</item>
+ <item>border-box</item>
<item>both</item>
+ <item>both-edges</item>
+ <item>bottom</item>
+ <item>break-all</item>
+ <item>break-spaces</item>
+ <item>break-word</item>
+ <item>bullets</item>
+ <item>cambodian</item>
+ <item>cap-height</item>
<item>capitalize</item>
- <item>caption</item>
+ <item>cell</item>
+ <item>center</item>
+ <item>ch-width</item>
+ <item>circle</item>
+ <item>cjk-decimal</item>
+ <item>cjk-earthly-branch</item>
+ <item>cjk-heavenly-stem</item>
+ <item>cjk-ideographic</item>
<item>clip</item>
+ <item>clone</item>
<item>close-quote</item>
+ <item>col-resize</item>
<item>collapse</item>
+ <item>color</item>
+ <item>color-burn</item>
+ <item>color-dodge</item>
+ <item>column</item>
+ <item>column-reverse</item>
+ <item>common-ligatures</item>
+ <item>compact</item>
<item>condensed</item>
- <item>crop</item>
- <item>cross</item>
+ <item>contain</item>
+ <item>content</item>
+ <item>content-box</item>
+ <item>contents</item>
+ <item>context-menu</item>
+ <item>contextual</item>
+ <item>copy</item>
+ <item>cover</item>
+ <item>crisp-edges</item>
+ <item>crosshair</item>
+ <item>currentcolor</item>
+ <item>cursive</item>
+ <item>cyclic</item>
+ <item>dark</item>
+ <item>darken</item>
+ <item>dashed</item>
+ <item>decimal</item>
+ <item>decimal-leading-zero</item>
+ <item>default</item>
+ <item>dense</item>
+ <item>devanagari</item>
+ <item>diagonal-fractions</item>
+ <item>difference</item>
+ <item>disc</item>
+ <item>disclosure-closed</item>
+ <item>disclosure-open</item>
+ <item>discretionary-ligatures</item>
+ <item>dot</item>
+ <item>dotted</item>
+ <item>double</item>
+ <item>double-circle</item>
+ <item>e-resize</item>
+ <item>each-line</item>
+ <item>ease</item>
+ <item>ease-in</item>
+ <item>ease-in-out</item>
+ <item>ease-out</item>
+ <item>economy</item>
<item>ellipsis</item>
- <item>ellipsis-word</item>
<item>embed</item>
+ <item>emoji</item>
+ <item>end</item>
+ <item>ethiopic-numeric</item>
+ <item>ew-resize</item>
+ <item>ex-height</item>
+ <item>exact</item>
+ <item>exclude</item>
+ <item>exclusion</item>
<item>expanded</item>
+ <item>extends</item>
<item>extra-condensed</item>
<item>extra-expanded</item>
+ <item>fade</item>
+ <item>fallback</item>
+ <item>false</item>
+ <item>fangsong</item>
+ <item>fantasy</item>
+ <item>fill</item>
+ <item>fill-box</item>
+ <item>filled</item>
+ <item>first</item>
+ <item>fit-content</item>
<item>fixed</item>
- <item>hand</item>
+ <item>flat</item>
+ <item>flex</item>
+ <item>flex-end</item>
+ <item>flex-start</item>
+ <item>flow</item>
+ <item>flow-root</item>
+ <item>force-end</item>
+ <item>forwards</item>
+ <item>from-font</item>
+ <item>from-image</item>
+ <item>full-size-kana</item>
+ <item>full-width</item>
+ <item>georgian</item>
+ <item>grab</item>
+ <item>grabbing</item>
+ <item>grid</item>
+ <item>groove</item>
+ <item>gujarati</item>
+ <item>gurmukhi</item>
+ <item>hanging</item>
+ <item>hard-light</item>
+ <item>hebrew</item>
+ <item>help</item>
+ <item>hidden</item>
<item>hide</item>
- <item>higher</item>
- <item>icon</item>
+ <item>high-quality</item>
+ <item>hiragana</item>
+ <item>hiragana-iroha</item>
+ <item>historical-forms</item>
+ <item>historical-ligatures</item>
+ <item>horizontal</item>
+ <item>horizontal-tb</item>
+ <item>hue</item>
+ <item>ic-height</item>
+ <item>ic-width</item>
+ <item>infinite</item>
+ <item>inline</item>
+ <item>inline-block</item>
+ <item>inline-end</item>
+ <item>inline-flex</item>
+ <item>inline-grid</item>
+ <item>inline-size</item>
+ <item>inline-start</item>
+ <item>inline-table</item>
+ <item>inset</item>
<item>inside</item>
+ <item>inter-character</item>
+ <item>inter-word</item>
+ <item>intersect</item>
<item>invert</item>
+ <item>isolate</item>
+ <item>isolate-override</item>
+ <item>italic</item>
+ <item>japanese-formal</item>
+ <item>japanese-informal</item>
+ <item>jump-both</item>
+ <item>jump-end</item>
+ <item>jump-none</item>
+ <item>jump-start</item>
+ <item>justify</item>
+ <item>justify-all</item>
+ <item>kannada</item>
+ <item>katakana</item>
+ <item>katakana-iroha</item>
+ <item>keep-all</item>
+ <item>keyword</item>
+ <item>khmer</item>
+ <item>korean-hangul-formal</item>
+ <item>korean-hanja-formal</item>
+ <item>korean-hanja-informal</item>
<item>landscape</item>
- <item>level</item>
+ <item>lao</item>
+ <item>large</item>
+ <item>larger</item>
+ <item>last</item>
+ <item>layout</item>
+ <item>left</item>
+ <item>legacy</item>
+ <item>light</item>
+ <item>lighten</item>
+ <item>lighter</item>
<item>line-through</item>
- <item>loud</item>
- <item>lower</item>
+ <item>linear</item>
+ <item>lining-nums</item>
+ <item>local</item>
+ <item>loose</item>
+ <item>lower-alpha</item>
+ <item>lower-armenian</item>
+ <item>lower-greek</item>
+ <item>lower-latin</item>
+ <item>lower-roman</item>
<item>lowercase</item>
<item>ltr</item>
- <item>menu</item>
- <item>message-box</item>
- <item>mix</item>
- <item>narrower</item>
+ <item>luminance</item>
+ <item>luminosity</item>
+ <item>malayalam</item>
+ <item>mandatory</item>
+ <item>manipulation</item>
+ <item>manual</item>
+ <item>margin-box</item>
+ <item>match-parent</item>
+ <item>match-source</item>
+ <item>math</item>
+ <item>max-content</item>
+ <item>medium</item>
+ <item>min-content</item>
+ <item>mixed</item>
+ <item>mongolian</item>
+ <item>monospace</item>
+ <item>move</item>
+ <item>multiply</item>
+ <item>myanmar</item>
+ <item>n-resize</item>
+ <item>ne-resize</item>
+ <item>nesw-resize</item>
+ <item>no-clip</item>
<item>no-close-quote</item>
+ <item>no-common-ligatures</item>
+ <item>no-contextual</item>
+ <item>no-discretionary-ligatures</item>
+ <item>no-drop</item>
+ <item>no-historical-ligatures</item>
<item>no-open-quote</item>
+ <item>no-repeat</item>
+ <item>none</item>
+ <item>normal</item>
+ <item>not-allowed</item>
<item>nowrap</item>
+ <item>ns-resize</item>
+ <item>numbers</item>
+ <item>numeric</item>
+ <item>nw-resize</item>
+ <item>nwse-resize</item>
+ <item>oblique</item>
+ <item>oldstyle-nums</item>
+ <item>only</item>
+ <item>open</item>
<item>open-quote</item>
+ <item>optional</item>
+ <item>ordinal</item>
+ <item>oriya</item>
+ <item>outset</item>
<item>outside</item>
+ <item>over</item>
+ <item>overlay</item>
<item>overline</item>
+ <item>padding</item>
+ <item>padding-box</item>
+ <item>paint</item>
+ <item>painted</item>
+ <item>pan-down</item>
+ <item>pan-left</item>
+ <item>pan-right</item>
+ <item>pan-up</item>
+ <item>pan-x</item>
+ <item>pan-y</item>
+ <item>paused</item>
+ <item>persian</item>
+ <item>petite-caps</item>
+ <item>pinch-zoom</item>
+ <item>pixelated</item>
+ <item>plaintext</item>
+ <item>pointer</item>
<item>portrait</item>
+ <item>position</item>
<item>pre</item>
<item>pre-line</item>
<item>pre-wrap</item>
+ <item>preserve-3d</item>
+ <item>progress</item>
+ <item>proportional-nums</item>
+ <item>proportional-width</item>
+ <item>proximity</item>
+ <item>recto</item>
<item>relative</item>
+ <item>repeat</item>
+ <item>repeat-x</item>
+ <item>repeat-y</item>
+ <item>replace</item>
+ <item>reverse</item>
+ <item>ridge</item>
+ <item>right</item>
+ <item>rotate-left</item>
+ <item>rotate-right</item>
+ <item>round</item>
+ <item>row</item>
+ <item>row-resize</item>
+ <item>row-reverse</item>
<item>rtl</item>
+ <item>ruby</item>
+ <item>ruby-base</item>
+ <item>ruby-base-container</item>
+ <item>ruby-text</item>
+ <item>ruby-text-container</item>
+ <item>running</item>
+ <item>s-resize</item>
+ <item>safe</item>
+ <item>sans-serif</item>
+ <item>saturation</item>
+ <item>scale-down</item>
+ <item>screen</item>
<item>scroll</item>
+ <item>scroll-position</item>
+ <item>se-resize</item>
+ <item>self-end</item>
+ <item>self-start</item>
<item>semi-condensed</item>
<item>semi-expanded</item>
<item>separate</item>
+ <item>serif</item>
+ <item>sesame</item>
<item>show</item>
- <item>small-caption</item>
+ <item>sideways</item>
+ <item>sideways-lr</item>
+ <item>sideways-right</item>
+ <item>sideways-rl</item>
+ <item>simp-chinese-formal</item>
+ <item>simp-chinese-informal</item>
+ <item>size</item>
+ <item>slashed-zero</item>
+ <item>slice</item>
+ <item>small</item>
+ <item>small-caps</item>
+ <item>smaller</item>
+ <item>smooth</item>
+ <item>soft-light</item>
+ <item>solid</item>
+ <item>space</item>
+ <item>space-around</item>
+ <item>space-between</item>
+ <item>space-evenly</item>
+ <item>spell-out</item>
+ <item>square</item>
+ <item>stable</item>
+ <item>stacked-fractions</item>
+ <item>start</item>
<item>static</item>
- <item>static-position</item>
- <item>status-bar</item>
+ <item>step-end</item>
+ <item>step-start</item>
+ <item>sticky</item>
+ <item>stretch</item>
+ <item>strict</item>
+ <item>stroke</item>
+ <item>stroke-box</item>
+ <item>style</item>
+ <item>sub</item>
+ <item>subtract</item>
+ <item>super</item>
+ <item>sw-resize</item>
+ <item>swap</item>
+ <item>symbolic</item>
+ <item>system-ui</item>
+ <item>table</item>
+ <item>table-caption</item>
+ <item>table-cell</item>
+ <item>table-column</item>
+ <item>table-column-group</item>
+ <item>table-footer-group</item>
+ <item>table-header-group</item>
+ <item>table-row</item>
+ <item>table-row-group</item>
+ <item>tabular-nums</item>
+ <item>tamil</item>
+ <item>telugu</item>
+ <item>text</item>
+ <item>thai</item>
<item>thick</item>
<item>thin</item>
+ <item>tibetan</item>
+ <item>titling-caps</item>
+ <item>top</item>
+ <item>trad-chinese-formal</item>
+ <item>trad-chinese-informal</item>
+ <item>transparent</item>
+ <item>triangle</item>
+ <item>true</item>
+ <item>ui-monospace</item>
+ <item>ui-rounded</item>
+ <item>ui-sans-serif</item>
+ <item>ui-serif</item>
<item>ultra-condensed</item>
<item>ultra-expanded</item>
+ <item>under</item>
<item>underline</item>
+ <item>unicase</item>
+ <item>unicode</item>
+ <item>unsafe</item>
+ <item>upper-alpha</item>
+ <item>upper-armenian</item>
+ <item>upper-latin</item>
+ <item>upper-roman</item>
<item>uppercase</item>
+ <item>upright</item>
+ <item>use-glyph-orientation</item>
+ <item>verso</item>
+ <item>vertical</item>
+ <item>vertical-lr</item>
+ <item>vertical-rl</item>
+ <item>vertical-text</item>
+ <item>view-box</item>
<item>visible</item>
- <item>wider</item>
- <item>break</item>
- <item>serif</item>
- <item>sans-serif</item>
- <item>cursive</item>
- <item>fantasy</item>
- <item>monospace</item>
- <item>border-box</item>
- <item>content-box</item>
- <item>-epub-hyphens</item>
- <item>contain</item>
- <item>cover</item>
-
- <!-- transition and animation -->
- <item>all</item>
- <item>ease</item>
- <item>ease-in</item>
- <item>ease-out</item>
- <item>ease-in-out</item>
- <item>step-start</item>
- <item>step-end</item>
- <item>linear</item>
+ <item>visibleFill</item>
+ <item>visiblePainted</item>
+ <item>visibleStroke</item>
+ <item>w-resize</item>
+ <item>wait</item>
+ <item>weight</item>
+ <item>words</item>
+ <item>wrap</item>
+ <item>wrap-reverse</item>
+ <item>x-large</item>
+ <item>x-small</item>
+ <item>xx-large</item>
+ <item>xx-small</item>
+ <item>xxx-large</item>
+ <item>zoom-in</item>
+ <item>zoom-out</item>
+
+ <!-- SVG only -->
+
+ <item>after-edge</item>
+ <item>arcs</item>
+ <item>before-edge</item>
+ <item>bevel</item>
+ <item>bounding-box</item>
+ <item>butt</item>
+ <item>central</item>
+ <item>crispEdges</item>
+ <item>evenodd</item>
+ <item>fixed-position</item>
+ <item>freeze</item>
+ <item>geometricPrecision</item>
+ <item>ideographic</item>
+ <item>linearRGB</item>
+ <item>mathematical</item>
+ <item>middle</item>
+ <item>miter</item>
+ <item>miter-clip</item>
+ <item>non-rotation</item>
+ <item>non-scaling-size</item>
+ <item>non-scaling-stroke</item>
+ <item>nonzero</item>
+ <item>optimizeLegibility</item>
+ <item>optimizeQuality</item>
+ <item>optimizeSpeed</item>
+ <item>remove</item>
+ <item>sRGB</item>
+ <item>text-after-edge</item>
+ <item>text-before-edge</item>
+ <item>text-bottom</item>
+ <item>text-top</item>
- <!-- animation -->
- <item>infinite</item>
- <item>reverse</item>
- <item>alternate</item>
- <item>alternate-reverse</item>
- <item>forwards</item>
- <item>backwards</item>
- <item>running</item>
- <item>paused</item>
+ <!-- manual list -->
+ <item>konq-center</item>
</list>
<list name="colors">
- <item>black</item>
- <item>silver</item>
- <item>gray</item>
- <item>white</item>
- <item>maroon</item>
- <item>red</item>
- <item>purple</item>
- <item>fuchsia</item>
- <item>green</item>
- <item>lime</item>
- <item>olive</item>
- <item>yellow</item>
- <item>navy</item>
- <item>blue</item>
- <item>teal</item>
- <item>aqua</item>
- <item>orange</item>
<item>aliceblue</item>
<item>antiquewhite</item>
+ <item>aqua</item>
<item>aquamarine</item>
<item>azure</item>
<item>beige</item>
<item>bisque</item>
+ <item>black</item>
<item>blanchedalmond</item>
+ <item>blue</item>
<item>blueviolet</item>
<item>brown</item>
<item>burlywood</item>
@@ -898,7 +1083,6 @@ Changelog:
<item>cornsilk</item>
<item>crimson</item>
<item>cyan</item>
- <item>aqua</item>
<item>darkblue</item>
<item>darkcyan</item>
<item>darkgoldenrod</item>
@@ -926,10 +1110,13 @@ Changelog:
<item>firebrick</item>
<item>floralwhite</item>
<item>forestgreen</item>
+ <item>fuchsia</item>
<item>gainsboro</item>
<item>ghostwhite</item>
<item>gold</item>
<item>goldenrod</item>
+ <item>gray</item>
+ <item>green</item>
<item>greenyellow</item>
<item>grey</item>
<item>honeydew</item>
@@ -957,10 +1144,11 @@ Changelog:
<item>lightslategrey</item>
<item>lightsteelblue</item>
<item>lightyellow</item>
+ <item>lime</item>
<item>limegreen</item>
<item>linen</item>
<item>magenta</item>
- <item>fuchsia</item>
+ <item>maroon</item>
<item>mediumaquamarine</item>
<item>mediumblue</item>
<item>mediumorchid</item>
@@ -975,8 +1163,11 @@ Changelog:
<item>mistyrose</item>
<item>moccasin</item>
<item>navajowhite</item>
+ <item>navy</item>
<item>oldlace</item>
+ <item>olive</item>
<item>olivedrab</item>
+ <item>orange</item>
<item>orangered</item>
<item>orchid</item>
<item>palegoldenrod</item>
@@ -989,6 +1180,9 @@ Changelog:
<item>pink</item>
<item>plum</item>
<item>powderblue</item>
+ <item>purple</item>
+ <item>rebeccapurple</item>
+ <item>red</item>
<item>rosybrown</item>
<item>royalblue</item>
<item>saddlebrown</item>
@@ -997,6 +1191,7 @@ Changelog:
<item>seagreen</item>
<item>seashell</item>
<item>sienna</item>
+ <item>silver</item>
<item>skyblue</item>
<item>slateblue</item>
<item>slategray</item>
@@ -1005,189 +1200,196 @@ Changelog:
<item>springgreen</item>
<item>steelblue</item>
<item>tan</item>
+ <item>teal</item>
<item>thistle</item>
<item>tomato</item>
<item>turquoise</item>
<item>violet</item>
<item>wheat</item>
+ <item>white</item>
<item>whitesmoke</item>
+ <item>yellow</item>
<item>yellowgreen</item>
- <item>rebeccapurple</item>
-
- <item>ActiveBorder</item>
- <item>ActiveCaption</item>
- <item>AppWorkspace</item>
- <item>Background</item>
+
+ <!-- System colors -->
+ <item>AccentColor</item>
+ <item>AccentColorText</item>
+ <item>ActiveText</item>
+ <item>ButtonBorder</item>
<item>ButtonFace</item>
- <item>ButtonHighlight</item>
- <item>ButtonShadow</item>
<item>ButtonText</item>
- <item>CaptionText</item>
+ <item>Canvas</item>
+ <item>CanvasText</item>
+ <item>Field</item>
+ <item>FieldText</item>
<item>GrayText</item>
<item>Highlight</item>
<item>HighlightText</item>
- <item>InactiveBorder</item>
- <item>InactiveCaption</item>
- <item>InactiveCaptionText</item>
- <item>InfoBackground</item>
- <item>InfoText</item>
- <item>Menu</item>
- <item>MenuText</item>
- <item>Scrollbar</item>
- <item>ThreeDDarkShadow</item>
- <item>ThreeDFace</item>
- <item>ThreeDHighlight</item>
- <item>ThreeDLightShadow</item>
- <item>ThreeDShadow</item>
- <item>Window</item>
- <item>WindowFrame</item>
- <item>WindowText</item>
+ <item>LinkText</item>
+ <item>Mark</item>
+ <item>MarkText</item>
+ <item>VisitedText</item>
</list>
+ <!-- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions -->
<list name="functions">
+ <item>abs</item>
+ <item>acos</item>
+ <item>annotation</item>
+ <item>asin</item>
+ <item>atan</item>
+ <item>atan2</item>
<item>attr</item>
- <item>calc</item>
- <item>hsl</item>
- <item>hsla</item>
- <item>linear-gradient</item>
- <item>radial-gradient</item>
- <item>repeating-linear-gradient</item>
- <item>repeating-radial-gradient</item>
- <item>rgb</item>
- <item>rgba</item>
- <item>var</item>
- <item>url</item>
-
- <!-- clip -->
- <item>rect</item>
-
- <!-- shape-outside and clip-path -->
- <item>inset</item>
- <item>circle</item>
- <item>ellipse</item>
- <item>polygon</item>
-
- <!-- filter and backdrop-filter -->
<item>blur</item>
<item>brightness</item>
+ <item>calc</item>
+ <item>character-variant</item>
+ <item>circle</item>
+ <item>clamp</item>
+ <item>color</item>
+ <item>color-mix</item>
+ <item>conic-gradient</item>
<item>contrast</item>
+ <item>cos</item>
+ <item>counter</item>
+ <item>counters</item>
+ <item>cross-fade</item>
+ <item>device-cmyk</item>
<item>drop-shadow</item>
+ <item>ellipse</item>
+ <item>env</item>
+ <item>exp</item>
+ <item>fit-content</item>
+ <item>format</item>
<item>grayscale</item>
+ <item>hsl</item>
<item>hue-rotate</item>
+ <item>hwb</item>
+ <item>hypot</item>
+ <item>image</item>
+ <item>image-set</item>
+ <item>inset</item>
<item>invert</item>
- <item>opacity</item>
- <item>saturate</item>
- <item>sepia</item>
-
- <!-- grid-template-columns and grid-template-rows -->
- <item>max-content</item>
- <item>min-content</item>
- <item>minmax</item>
- <item>fix-content</item>
- <item>repeat</item>
-
- <!-- transition and animation -->
- <item>cubic-bezier</item>
- <item>frames</item>
- <item>steps</item>
-
- <!-- transform -->
+ <item>lab</item>
+ <item>lch</item>
+ <item>linear-gradient</item>
+ <item>local</item>
+ <item>log</item>
<item>matrix</item>
<item>matrix3d</item>
+ <item>max</item>
+ <item>min</item>
+ <item>minmax</item>
+ <item>mod</item>
+ <item>oklab</item>
+ <item>oklch</item>
+ <item>opacity</item>
+ <item>ornaments</item>
+ <item>paint</item>
+ <item>path</item>
<item>perspective</item>
+ <item>polygon</item>
+ <item>pow</item>
+ <item>radial-gradient</item>
+ <item>rem</item>
+ <item>repeat</item>
+ <item>repeating-conic-gradient</item>
+ <item>repeating-linear-gradient</item>
+ <item>repeating-radial-gradient</item>
+ <item>rgb</item>
<item>rotate</item>
<item>rotate3d</item>
<item>rotateX</item>
<item>rotateY</item>
<item>rotateZ</item>
+ <item>round</item>
+ <item>saturate</item>
<item>scale</item>
<item>scale3d</item>
<item>scaleX</item>
<item>scaleY</item>
<item>scaleZ</item>
+ <item>sepia</item>
+ <item>sign</item>
+ <item>sin</item>
<item>skew</item>
<item>skewX</item>
<item>skewY</item>
+ <item>sqrt</item>
+ <item>styleset</item>
+ <item>stylistic</item>
+ <item>swash</item>
+ <item>symbols</item>
+ <item>tan</item>
+ <item>tech</item>
<item>translate</item>
<item>translate3d</item>
<item>translateX</item>
<item>translateY</item>
<item>translateZ</item>
-
- <!-- inner @font-face -->
- <item>local</item>
- <item>format</item>
+ <item>url</item>
+ <item>var</item>
</list>
- <list name="medias">
- <!-- types -->
+ <list name="media types">
<item>all</item>
<item>print</item>
<item>screen</item>
<item>speech</item>
+ </list>
- <!-- features -->
- <item>any-pointer</item>
+ <list name="media features">
+ <item>-webkit-device-pixel-ratio</item>
+ <item>-webkit-transform-3d</item>
<item>any-hover</item>
+ <item>any-pointer</item>
<item>aspect-ratio</item>
<item>color</item>
<item>color-gamut</item>
<item>color-index</item>
<item>display-mode</item>
+ <item>dynamic-range</item>
+ <item>forced-colors</item>
<item>grid</item>
<item>height</item>
<item>hover</item>
- <item>max-aspect-ratio</item>
- <item>max-color</item>
- <item>max-color-index</item>
- <item>max-device-aspect-ratio</item>
- <item>max-device-height</item>
- <item>max-device-width</item>
+ <item>inverted-colors</item>
<item>max-height</item>
- <item>max-monochrome</item>
- <item>max-resolution</item>
<item>max-width</item>
- <item>min-aspect-ratio</item>
- <item>min-color</item>
- <item>min-color-index</item>
- <item>min-device-aspect-ratio</item>
- <item>min-device-height</item>
- <item>min-device-width</item>
<item>min-height</item>
- <item>min-monochrome</item>
- <item>min-resolution</item>
<item>min-width</item>
<item>monochrome</item>
<item>orientation</item>
+ <item>overflow-block</item>
+ <item>overflow-inline</item>
<item>pointer</item>
+ <item>prefers-color-scheme</item>
+ <item>prefers-contrast</item>
+ <item>prefers-reduced-motion</item>
<item>resolution</item>
- <item>scan</item>
+ <item>scripting</item>
<item>update</item>
+ <item>video-dynamic-range</item>
<item>width</item>
</list>
<!-- prefixed by :: -->
- <list name="pseudoelements">
+ <list name="pseudo-elements">
<item>after</item>
+ <item>backdrop</item>
<item>before</item>
<item>cue</item>
+ <item>cue-region</item>
+ <item>file-selector-button</item>
<item>first-letter</item>
<item>first-line</item>
- <item>selection</item>
-
- <!-- experimental -->
- <item>backdrop</item>
+ <item>marker</item>
+ <item>part</item>
<item>placeholder</item>
- <!-- <item>marker</item> -->
+ <item>selection</item>
<item>slotted</item>
- <!-- <item>spelling-error</item> -->
- <!-- <item>grammar-error</item> -->
- <!-- UI pseudo-classes -->
- <item>value</item>
- <item>choices</item>
- <item>repeat-item</item>
- <item>repeat-index</item>
+ <!-- manual list -->
<item>-moz-progress-bar</item>
<item>-moz-range-progress</item>
@@ -1206,30 +1408,42 @@ Changelog:
</list>
<!-- prefixed by : -->
- <list name="pseudoclasses">
+ <list name="pseudo-classes">
<item>active</item>
<item>any-link</item>
+ <item>autofill</item>
+ <item>buffering</item>
<item>checked</item>
+ <item>current</item>
<item>default</item>
<item>defined</item>
- <!-- <item>dir</item> -->
+ <item>dir</item>
<item>disabled</item>
<item>empty</item>
<item>enabled</item>
+ <item>first</item>
<item>first-child</item>
<item>first-of-type</item>
- <item>fullscreen</item>
<item>focus</item>
+ <item>focus-visible</item>
<item>focus-within</item>
+ <item>fullscreen</item>
+ <item>has</item>
<item>host</item>
+ <item>host-context</item>
<item>hover</item>
<item>in-range</item>
<item>indeterminate</item>
<item>invalid</item>
+ <item>is</item>
<item>lang</item>
<item>last-child</item>
<item>last-of-type</item>
+ <item>left</item>
<item>link</item>
+ <item>local-link</item>
+ <item>modal</item>
+ <item>muted</item>
<item>not</item>
<item>nth-child</item>
<item>nth-last-child</item>
@@ -1239,30 +1453,28 @@ Changelog:
<item>only-of-type</item>
<item>optional</item>
<item>out-of-range</item>
+ <item>paused</item>
+ <item>picture-in-picture</item>
<item>placeholder-shown</item>
+ <item>playing</item>
<item>read-only</item>
<item>read-write</item>
<item>required</item>
+ <item>right</item>
<item>root</item>
<item>scope</item>
+ <item>seeking</item>
+ <item>stalled</item>
<item>target</item>
+ <item>user-invalid</item>
+ <item>user-valid</item>
<item>valid</item>
<item>visited</item>
-
- <!-- pseudo-elements -->
- <item>after</item>
- <item>before</item>
- <item>cue</item>
- <item>first-letter</item>
- <item>first-line</item>
- <item>selection</item>
- </list>
-
- <list name="pseudoclass-not">
- <item>not</item>
+ <item>volume-locked</item>
+ <item>where</item>
</list>
- <list name="pseudoclasses-@page">
+ <list name="@page pseudo-classes">
<item>blank</item>
<item>first</item>
<item>left</item>
@@ -1272,426 +1484,548 @@ Changelog:
</list>
<list name="at-rules">
- <item>@character</item>
<item>@charset</item>
+ <item>@container</item>
+ <item>@font-feature-values</item>
<item>@import</item>
- <item>@namespace</item>
- </list>
-
- <list name="nested at-rules">
- <item>@document</item>
+ <item>@keyframes</item>
+ <item>@layer</item>
<item>@media</item>
- <item>@supports</item>
- </list>
-
- <list name="@viewport">
- <item>@viewport</item>
- </list>
-
- <list name="within-@viewport">
- <item>min-width</item>
- <item>max-width</item>
- <item>width</item>
- <item>min-height</item>
- <item>max-height</item>
- <item>height</item>
- <item>zoom</item>
- <item>min-zoom</item>
- <item>max-zoom</item>
- <item>user-zoom</item>
- <item>orientation</item>
- <item>viewport-fit</item>
- </list>
-
- <list name="@page">
+ <item>@namespace</item>
<item>@page</item>
+ <item>@supports</item>
</list>
- <list name="within-@page">
- <item>size</item>
- <item>marks</item>
- <item>bleed</item>
- </list>
-
- <list name="@font-face">
+ <list name="at-rules definitions">
+ <item>@color-profile</item>
+ <item>@counter-style</item>
<item>@font-face</item>
- </list>
-
- <list name="within-@font-face">
- <item>font-display</item>
- <item>font-family</item>
- <item>font-stretch</item>
- <item>font-style</item>
- <item>font-weight</item>
- <item>font-variant</item>
- <item>font-feature-settings</item>
- <item>font-variation-settings</item>
- <item>src</item>
- <item>unicode-range</item>
- </list>
-
- <list name="@keyframes">
- <item>@keyframes</item>
- </list>
-
- <list name="within-@keyframes">
- <item>from</item>
- <item>to</item>
+ <item>@font-palette-values</item>
+ <item>@property</item>
</list>
<list name="media operators">
+ <item>or</item>
<item>and</item>
<item>only</item>
<item>not</item>
</list>
<contexts>
- <context name="Base" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <IncludeRules context="FindComments" />
- <DetectChar attribute="Normal Text" context="RuleSet" char="{" beginRegion="ruleset" />
- <DetectChar attribute="At Rule" context="SelectAtRule" char="@" lookAhead="true" />
- <!-- find selectors // .class #id :hover :nth-child(2n+1) [type="search"] -->
- <DetectChar attribute="Selector Pseudo" context="SelectorPseudo" char=":" />
- <DetectChar attribute="Selector Attribute" context="SelectorAttr" char="[" />
- <DetectChar attribute="Separator Symbol" context="#stay" char="," />
- <AnyChar attribute="Operator" context="#stay" String="*>+~|" />
- <RegExpr attribute="Selector Id" context="#stay" String="#[-]?(&nmstart;)(&nmchar;)*" />
- <RegExpr attribute="Selector Class" context="#stay" String="\.([a-zA-Z0-9\-_]|[\x80-\xFF]|\\[0-9A-Fa-f]{1,6})*" />
- <RegExpr attribute="Selector Tag" context="#stay" String="[-]?(&nmstart;)(&nmchar;)*" />
- </context>
-
- <context name="FindSelector" attribute="Normal Text" lineEndContext="#stay">
- <IncludeRules context="FindComments" />
- <!-- find selectors // .class #id :hover :nth-child(2n+1) [type="search"] -->
- <DetectChar attribute="Selector Attribute" context="SelectorAttr" char="[" />
- <DetectChar attribute="Separator Symbol" context="#stay" char="," />
- <AnyChar attribute="Operator" context="#stay" String="*>+~|" />
- <RegExpr attribute="Selector Id" context="#stay" String="#[-]?(&nmstart;)(&nmchar;)*" />
- <RegExpr attribute="Selector Class" context="#stay" String="\.([a-zA-Z0-9\-_]|[\x80-\xFF]|\\[0-9A-Fa-f]{1,6})*" />
- <RegExpr attribute="Selector Tag" context="#stay" String="[-]?(&nmstart;)(&nmchar;)*" />
+ <context name="Base" attribute="Normal Text">
+ <DetectSpaces/>
+ <DetectChar attribute="Block Symbol" context="RuleSet" char="{" beginRegion="ruleset"/>
+ <DetectChar context="SelectAtRule" char="@" lookAhead="1"/>
+ <IncludeRules context="FindSelector"/>
+ </context>
+
+ <!-- find selectors // .class #id :hover :nth-child(2n+1) [type="search"] -->
+ <context name="FindSelector" attribute="Normal Text">
+ <DetectChar attribute="Separator Symbol" char=","/>
+ <DetectChar attribute="Selector Pseudo" context="PseudoClassOrElement" char=":"/>
+ <DetectChar attribute="Selector Attribute" context="SelectorAttr" char="["/>
+ <DetectChar attribute="Selector Pseudo" context="PseudoClassSelector" char="("/>
+ <AnyChar attribute="Selector Symbol" String="*>+~|&amp;"/>
+ <IncludeRules context="FindComments"/>
+ <RegExpr attribute="Selector Class" String="\.&ident2;"/>
+ <RegExpr attribute="Selector Id" String="#&ident2;"/>
+ <RegExpr attribute="Selector Tag" String="&ident;"/>
</context>
<!-- find functions // rgba(255,255,255,0.75) -->
- <context name="FindFunctions" attribute="Normal Text" lineEndContext="#stay">
- <RegExpr attribute="Function" context="Function" String="[a-z\-]{2,}\(" lookAhead="true" />
+ <context name="FindFunctions" attribute="Normal Text">
+ <RegExpr context="Function" String="(&func;)?\(" lookAhead="1"/>
+ </context>
+ <context name="FindNestedFunctions" attribute="Normal Text">
+ <RegExpr context="NestedFunction" String="(&func;)?\(" lookAhead="1"/>
</context>
<!-- find values // 10px 12pt 2.5em 1rem 75% #ffcc99 red solid -->
- <context name="FindValues" attribute="Normal Text" lineEndContext="#stay">
- <DetectChar attribute="Annotation" context="Annotation" char="!" />
- <keyword attribute="Value Keyword" context="#stay" String="value keywords" />
- <keyword attribute="Value" context="#stay" String="values" />
- <keyword attribute="Color" context="#stay" String="colors" />
- <RegExpr attribute="Color" context="#stay" String="#([0-9A-Fa-f]{3}){1,2}\b" />
- <RegExpr attribute="Number" context="FindUnits" String="[-+]?[0-9.]+(?![-+])" />
- <RegExpr attribute="Normal Text" context="#stay" String="[-]?(&nmstart;)(&nmchar;)*" />
+ <context name="FindValues" attribute="Normal Text">
+ <DetectChar attribute="Annotation" context="Annotation" char="!"/>
+ <keyword attribute="Value Keyword" String="value keywords"/>
+ <keyword attribute="Value" String="values"/>
+ <keyword attribute="Named Color" String="colors"/>
+ <RegExpr attribute="Color" String="#([0-9A-Fa-f]{8}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{3,4})"/>
+ <IncludeRules context="FindNumber"/>
+ <IncludeRules context="FindIdentifier"/>
</context>
- <context name="Annotation" attribute="Normal Text" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
- <WordDetect attribute="Annotation" context="#pop" String="important" />
+ <context name="FindNumber" attribute="Number" lineEndContext="#pop">
+ <RegExpr attribute="Number" context="FindUnits" String="[-+]?([0-9]+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?(?![-+])"/>
</context>
- <context name="FindUnits" attribute="Normal Text" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
- <RegExpr attribute="Unit" context="#stay" String="(%|(em|ex|ch|rem|vw|vh|vm|px|in|cm|mm|pt|pc|deg|rad|grad|turn|ms|s|Hz|kHz)\b)(?![-+])" />
+ <context name="FindIdentifier" attribute="Number" lineEndContext="#pop">
+ <RegExpr String="&ident2;\s*"/>
+ </context>
+
+ <context name="FindVendorProperty" attribute="Normal Text">
+ <RegExpr attribute="Vendor Property" String="-(webkit|moz|o|ms)-[-_a-zA-Z0-9]*"/>
+ </context>
+
+ <context name="Annotation" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="#pop">
+ <WordDetect attribute="Annotation" context="#pop" String="important"/>
+ </context>
+
+ <context name="FindUnits" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="#pop">
+ <!-- https://drafts.csswg.org/css-values-4/#lengths -->
+ <!-- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Values_and_Units -->
+ <RegExpr attribute="Unit" String="(%|(em|ex|cap|ch|ic|rem|lh|rlh|vw|vh|vi|vb|vmin|vmax|cqw|cqh|cqi|cqb|cqmin|cqmax|cm|mm|Q|in|pc|pt|px|deg|grad|rad|turn|s|ms|Hz|kHz|fr|dpi|dpcm|dppx|x)\b)(?![-+])"/>
</context>
<!-- find strings // "some words" 'some words' -->
- <context name="FindStrings" attribute="Normal Text" lineEndContext="#stay">
- <DetectChar attribute="String" context="StringDQ" char="&quot;" />
- <DetectChar attribute="String" context="StringSQ" char="'" />
+ <context name="FindStrings" attribute="Normal Text">
+ <DetectChar attribute="String" context="StringDQ" char="&quot;"/>
+ <DetectChar attribute="String" context="StringSQ" char="'"/>
</context>
<!-- find comments // /* comment */ -->
- <context name="FindComments" attribute="Normal Text" lineEndContext="#stay">
- <Detect2Chars attribute="Comment" context="IsComments" char="/" char1="*" lookAhead="true"/>
- <Detect2Chars attribute="Error" context="Error" char="/" char1="/" />
+ <context name="FindComments" attribute="Normal Text">
+ <Detect2Chars attribute="Comment" context="IsComments" char="/" char1="*" lookAhead="1"/>
+ <Detect2Chars attribute="Error" context="Error" char="/" char1="/"/>
</context>
<context name="Error" attribute="Error" lineEndContext="#pop">
- <RegExpr attribute="Error" context="#pop" String=".*" />
+ <RegExpr attribute="Error" context="#pop" String=".*"/>
</context>
- <context name="IsComments" attribute="Normal Text" lineEndContext="#stay">
- <RegExpr attribute="Region Marker" context="#pop" String="/\*\s*BEGIN\b.*\*/" beginRegion="UserDefined" />
- <RegExpr attribute="Region Marker" context="#pop" String="/\*\s*END\b.*\*/" endRegion="UserDefined" />
- <Detect2Chars attribute="Comment" context="Comment" char="/" char1="*" beginRegion="comment" />
+ <context name="IsComments" attribute="Normal Text">
+ <RegExpr attribute="Region Marker" context="#pop" String="/\*\s*BEGIN\b.*\*/" beginRegion="UserDefined"/>
+ <RegExpr attribute="Region Marker" context="#pop" String="/\*\s*END\b.*\*/" endRegion="UserDefined"/>
+ <Detect2Chars attribute="Comment" context="Comment" char="/" char1="*" beginRegion="comment"/>
</context>
<context name="SelectAtRule" attribute="At Rule" lineEndContext="#pop">
- <keyword attribute="At Rule" context="#pop!NestedAtRule" String="nested at-rules" />
- <keyword attribute="At Rule" context="#pop!AtRule" String="at-rules" />
- <keyword attribute="At Rule" context="#pop!@keyframes" String="@keyframes" />
- <keyword attribute="At Rule" context="#pop!@viewport" String="@viewport" />
- <keyword attribute="At Rule" context="#pop!@font-face" String="@font-face" />
- <keyword attribute="At Rule" context="#pop!@page" String="@page" />
- <RegExpr attribute="At Rule" context="#pop!UnknownAtRule" String="@[a-zA-Z0-9\-_]+\b" />
+ <WordDetect attribute="At Rule" context="@keyframes" String="@keyframes"/>
+ <WordDetect attribute="At Rule" context="@layer" String="@layer"/>
+ <WordDetect attribute="At Rule" context="@font-feature-values" String="@font-feature-values"/>
+ <WordDetect attribute="At Rule" context="@page" String="@page"/>
+ <WordDetect attribute="At Rule" context="@supports" String="@supports"/>
+ <keyword attribute="At Rule" context="@property" String="at-rules definitions"/>
+ <RegExpr attribute="At Rule" context="@media" String="@[-a-zA-Z0-9_]*"/>
</context>
- <context name="@keyframes" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <IncludeRules context="FindComments" />
- <DetectChar attribute="Normal Text" context="Within-@keyframes" char="{" beginRegion="ruleset" />
- </context>
- <context name="Within-@keyframes" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <IncludeRules context="FindComments" />
- <DetectChar attribute="Normal Text" context="#pop#pop" char="}" endRegion="ruleset" />
- <DetectChar attribute="Normal Text" context="RuleSet" char="{" beginRegion="ruleset" />
- <keyword attribute="Value" context="#stay" String="within-@keyframes" />
- <RegExpr attribute="Value" context="#stay" String="[-+]?[0-9.]+%" />
+ <context name="AtRuleCommon" attribute="At Rule Text">
+ <DetectChar char=";" context="#pop#pop" attribute="Separator Symbol"/>
+ <DetectChar char="}" context="#pop#pop" lookAhead="1"/>
+ <IncludeRules context="FindStrings"/>
+ <IncludeRules context="FindComments"/>
</context>
-
- <context name="@viewport" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <IncludeRules context="FindComments" />
- <DetectChar attribute="Selector Pseudo" context="SelectorPseudo" char=":" />
- <DetectChar attribute="Normal Text" context="#pop!Within-@viewport" char="{" beginRegion="ruleset" />
+ <context name="AtRuleCommon2" attribute="At Rule Text">
+ <DetectChar char="{" context="#pop#pop!NestedRule" attribute="Block Symbol" beginRegion="ruleset"/>
+ <IncludeRules context="AtRuleCommon"/>
</context>
- <context name="Within-@viewport" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <keyword attribute="Property" context="IsProperty" String="within-@viewport" />
- <IncludeRules context="RuleSet" />
- </context>
- <context name="@font-face" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <IncludeRules context="FindComments" />
- <DetectChar attribute="Normal Text" context="#pop!Within-@font-face" char="{" beginRegion="ruleset" />
+ <context name="@media" attribute="At Rule Text">
+ <DetectSpaces/>
+ <DetectChar char="(" context="AtRuleMediaCondition" attribute="Condition Open/Close"/>
+ <IncludeRules context="AtRuleCommon2"/>
+ <IncludeRules context="FindFunctions"/>
+ <keyword String="media operators" attribute="Keyword"/>
+ <keyword String="media types" attribute="Media Type"/>
+ <DetectIdentifier/>
</context>
- <context name="Within-@font-face" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <IncludeRules context="FindComments" />
- <keyword attribute="Property" context="IsProperty" String="within-@font-face" />
- <RegExpr attribute="Unknown Property" context="IsProperty" String="[A-Za-z_-]+\b" />
- <DetectChar attribute="Normal Text" context="#pop" char="}" endRegion="ruleset" />
+ <context name="AtRuleMediaCondition" attribute="At Rule Text">
+ <DetectSpaces/>
+ <DetectChar char=";" context="#pop#pop#pop" attribute="Error"/>
+ <DetectChar char="{" context="#pop#pop#pop!RuleSet" attribute="Error" beginRegion="ruleset"/>
+ <DetectChar char="}" context="#pop#pop#pop" lookAhead="1"/>
+ <IncludeRules context="AtRuleMediaConditionValue"/>
</context>
-
- <context name="@page" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <IncludeRules context="FindComments" />
- <DetectChar attribute="Selector Pseudo" context="SelectorPseudo-@page" char=":" />
- <DetectChar attribute="Normal Text" context="#pop!Within-@page" char="{" beginRegion="ruleset" />
+ <context name="NestedAtRuleMediaCondition" attribute="At Rule Text">
+ <DetectSpaces/>
+ <AnyChar String=";{}" context="#pop" lookAhead="1"/>
+ <IncludeRules context="AtRuleMediaConditionValue"/>
+ </context>
+ <context name="AtRuleMediaConditionValue" attribute="At Rule Text">
+ <AnyChar String="&lt;>=" attribute="Operator"/>
+ <DetectChar char=")" context="#pop" attribute="Condition Open/Close"/>
+ <DetectChar char="(" context="NestedAtRuleMediaCondition" attribute="Condition Open/Close"/>
+ <DetectChar char=":" attribute="Property Separator"/>
+ <IncludeRules context="FindStrings"/>
+ <IncludeRules context="FindComments"/>
+ <IncludeRules context="FindFunctions"/>
+ <IncludeRules context="FindNumber"/>
+ <keyword String="media operators" attribute="Keyword"/>
+ <keyword String="media features" attribute="Condition Property"/>
+ <IncludeRules context="FindIdentifier"/>
</context>
- <context name="SelectorPseudo-@page" attribute="Selector Pseudo" lineEndContext="#pop">
- <keyword attribute="Selector Pseudo" context="#pop" String="pseudoclasses-@page" />
- <RegExpr attribute="Selector Pseudo" context="#pop" String="[-a-zA-Z][-a-zA-Z0-9]*" />
+ <context name="NestedRule" attribute="Normal Text">
+ <DetectSpaces/>
+ <DetectChar char="}" context="#pop" attribute="Block Symbol" endRegion="ruleset"/>
+ <DetectChar char="{" context="RuleSet" attribute="Block Symbol" beginRegion="ruleset"/>
+ <DetectChar char="@" context="SelectAtRule" lookAhead="1"/>
+ <IncludeRules context="FindSelector"/>
</context>
- <context name="Within-@page" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <keyword attribute="Property" context="IsProperty" String="within-@page" />
- <IncludeRules context="RuleSet" />
+
+ <context name="@supports" attribute="At Rule Text">
+ <DetectSpaces/>
+ <DetectChar char="(" context="AtRuleSupportsCondition" lookAhead="1"/>
+ <IncludeRules context="AtRuleCommon2"/>
+ <keyword String="media operators" attribute="Keyword"/>
+ <WordDetect String="selector(" context="FunctionSelector" attribute="Function"/>
+ <WordDetect String="font-tech(" context="#pop!SupportsProp" lookAhead="1"/>
+ <WordDetect String="font-format(" context="#pop!SupportsProp" lookAhead="1"/>
+ <DetectIdentifier/>
+ </context>
+ <context name="SupportsProp" attribute="At Rule Text">
+ <DetectSpaces/>
+ <DetectChar char="{" context="#pop#pop!RuleSet" attribute="Block Symbol" beginRegion="ruleset"/>
+ <WordDetect String="font-tech(" context="FunctionFont" attribute="Function"/>
+ <WordDetect String="font-format(" context="FunctionFont" attribute="Function"/>
+ <IncludeRules context="AtRuleCommon"/>
</context>
- <context name="IsProperty" attribute="Error" lineEndContext="#stay">
- <DetectSpaces attribute="Normal Text" />
- <DetectChar attribute="Normal Text" context="RuleParameters" char=":" />
+ <context name="AtRuleSupportsCondition" attribute="At Rule Text" fallthroughContext="#pop">
+ <DetectChar char="(" context="SupportsCondition" attribute="Condition Open/Close"/>
+ <DetectChar char=";" context="#pop#pop#pop" attribute="Error"/>
+ <DetectChar char="{" context="#pop#pop#pop!NestedRule" attribute="Error" beginRegion="ruleset"/>
+ <DetectChar char="}" context="#pop#pop#pop" lookAhead="1"/>
+ </context>
+ <context name="SupportsCondition" attribute="At Rule Text" fallthroughContext="#pop!SupportsConditionValue">
+ <DetectSpaces/>
+ <DetectChar char="(" context="SupportsCondition" attribute="Condition Open/Close"/>
+ <DetectChar char=":" context="#pop!SupportsConditionValue" attribute="Property Separator"/>
+ <IncludeRules context="FindComments"/>
+ <keyword String="media operators" attribute="Keyword"/>
+ <keyword String="properties" attribute="Condition Property"/>
+ <IncludeRules context="FindVendorProperty"/>
+ </context>
+ <context name="SupportsConditionValue" attribute="At Rule Text">
+ <AnyChar String=";{}" context="#pop" lookAhead="1"/>
+ <DetectChar char=")" context="#pop" attribute="Condition Open/Close"/>
+ <DetectChar char="(" context="SupportsCondition" attribute="Condition Open/Close"/>
+ <IncludeRules context="FindStrings"/>
+ <IncludeRules context="FindComments"/>
+ <IncludeRules context="FindFunctions"/>
+ <IncludeRules context="FindValues"/>
</context>
- <context name="NestedAtRule" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <DetectChar attribute="Normal Text" context="NestedBase" char="{" beginRegion="ruleset" />
- <IncludeRules context="AtRuleValue" />
+ <context name="FunctionSelector" attribute="At Rule Text">
+ <DetectSpaces/>
+ <AnyChar String=";{}" context="#pop" lookAhead="1"/>
+ <DetectChar char=")" context="#pop" attribute="Function"/>
+ <IncludeRules context="FindSelector"/>
+ </context>
+ <context name="FunctionFont" attribute="At Rule Text">
+ <DetectSpaces/>
+ <DetectIdentifier/>
+ <AnyChar String=";{}" context="#pop" lookAhead="1"/>
+ <DetectChar char=")" context="#pop" attribute="Function"/>
+ <IncludeRules context="FindStrings"/>
+ <IncludeRules context="FindComments"/>
</context>
- <context name="NestedBase" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <DetectChar attribute="Normal Text" context="#pop#pop" char="}" endRegion="ruleset" />
- <IncludeRules context="Base" />
+
+ <context name="@layer" attribute="At Rule Text">
+ <DetectSpaces/>
+ <DetectIdentifier/>
+ <DetectChar attribute="Separator Symbol" char=","/>
+ <IncludeRules context="AtRuleCommon2"/>
</context>
- <context name="AtRule" attribute="Normal Text" lineEndContext="#pop">
- <DetectSpaces />
- <DetectChar attribute="Separator Symbol" context="#pop" char=";" />
- <IncludeRules context="AtRuleValue" />
+
+ <context name="@page" attribute="At Rule Text">
+ <DetectSpaces/>
+ <DetectChar attribute="Selector Pseudo" char=":"/>
+ <AnyChar String="{};" context="#pop#pop" lookAhead="1"/>
+ <IncludeRules context="FindComments"/>
+ <keyword attribute="Selector Pseudo" String="@page pseudo-classes"/>
+ <DetectIdentifier/>
</context>
- <context name="UnknownAtRule" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <DetectChar attribute="Separator Symbol" context="#pop" char=";" />
- <DetectChar attribute="Normal Text" context="#pop!RuleSet" char="{" beginRegion="ruleset" />
- <DetectChar attribute="Normal Text" context="#pop" char="}" endRegion="ruleset" />
- <IncludeRules context="AtRuleValue" />
+
+ <context name="@property" attribute="At Rule Text">
+ <DetectSpaces/>
+ <DetectChar char="-"/>
+ <DetectChar char="{" context="#pop#pop!RuleSet" attribute="Block Symbol" beginRegion="ruleset"/>
+ <IncludeRules context="AtRuleCommon"/>
+ <DetectIdentifier/>
</context>
- <context name="AtRuleValue" attribute="Normal Text" lineEndContext="#stay">
- <IncludeRules context="FindComments" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindFunctions" />
- <keyword attribute="Keyword" context="#stay" String="media operators" />
- <RegExpr attribute="Property" context="#stay" String="[A-Za-z_-]+(?=\s*:)" />
- <keyword attribute="Value" context="#stay" String="medias" />
- <IncludeRules context="FindValues" />
+
+ <context name="@font-feature-values" attribute="At Rule Text">
+ <DetectSpaces/>
+ <DetectIdentifier/>
+ <DetectChar char="{" context="#pop#pop!FontFeatureBlock" attribute="Block Symbol" beginRegion="ruleset"/>
+ <IncludeRules context="AtRuleCommon"/>
+ </context>
+ <context name="FontFeatureBlock" attribute="Normal Text" fallthroughContext="#pop!NestedRule">
+ <DetectSpaces/>
+ <DetectChar char="}" context="#pop" attribute="Block Symbol" endRegion="ruleset"/>
+ <DetectChar char="{" context="AtRuleSet" attribute="Block Symbol" beginRegion="ruleset"/>
+ <RegExpr String="@[-a-zA-Z]*" attribute="At Rule"/>
+ <IncludeRules context="FindComments"/>
</context>
- <context name="SelectorAttr" attribute="Selector Attribute" lineEndContext="#stay">
- <DetectChar attribute="Selector Attribute" context="#pop" char="]" />
- <IncludeRules context="FindStrings" />
- <DetectChar attribute="Operator" context="SelectorAttrValue" char="=" />
- <Detect2Chars attribute="Operator" context="SelectorAttrValue" char="~" char1="=" />
- <Detect2Chars attribute="Operator" context="SelectorAttrValue" char="^" char1="=" />
- <Detect2Chars attribute="Operator" context="SelectorAttrValue" char="$" char1="=" />
- <Detect2Chars attribute="Operator" context="SelectorAttrValue" char="*" char1="=" />
- <Detect2Chars attribute="Operator" context="SelectorAttrValue" char="|" char1="=" />
- <DetectIdentifier />
+ <context name="AtRuleSet" attribute="Normal Text" fallthroughContext="#pop!NestedRule">
+ <IncludeRules context="RuleSet"/>
</context>
- <context name="SelectorAttrValue" attribute="String" lineEndContext="#stay">
- <DetectChar attribute="Selector Attribute" context="#pop#pop" char="]" />
- <DetectIdentifier />
+
+ <context name="@keyframes" attribute="At Rule Text">
+ <DetectSpaces/>
+ <DetectChar char="{" context="#pop#pop!KeyFramesBlock" attribute="Block Symbol" beginRegion="ruleset"/>
+ <IncludeRules context="AtRuleCommon"/>
+ <DetectIdentifier/>
</context>
+ <context name="KeyFramesBlock" attribute="Normal Text">
+ <DetectSpaces/>
+ <DetectChar char="{" context="RuleSet" attribute="Block Symbol" beginRegion="ruleset"/>
+ <DetectChar char="}" context="#pop" attribute="Block Symbol" endRegion="ruleset"/>
+ <DetectChar char="," attribute="Separator Symbol"/>
+ <IncludeRules context="FindComments"/>
+ <RegExpr attribute="Value" String="\b(from|to)\b|[-+]?[0-9.]+%"/>
+ </context>
+
- <context name="SelectorPseudo" attribute="Selector Pseudo" lineEndContext="#pop">
- <DetectChar attribute="Selector Pseudo" context="SelectorPseudoElements" char=":" />
- <keyword attribute="Selector Pseudo" context="SelectorPseudoValueSelector" String="pseudoclass-not" />
- <keyword attribute="Selector Pseudo" context="SelectorPseudoValue" String="pseudoclasses" />
- <RegExpr attribute="Selector Pseudo" context="SelectorPseudoValue" String="[-a-zA-Z][-a-zA-Z0-9]*" />
+ <context name="SelectorAttr" attribute="Attribute Name">
+ <DetectChar attribute="Selector Attribute" context="#pop" char="]"/>
+ <IncludeRules context="FindStrings"/>
+ <DetectChar attribute="Attribute Operator" context="SelectorAttrValue" char="="/>
+ <AnyChar attribute="Attribute Operator" String="~^$*|"/>
+ <IncludeRules context="FindComments"/>
+ <DetectIdentifier/>
+ <AnyChar context="#pop" String="{};#.,:>" lookAhead="1"/>
</context>
- <context name="SelectorPseudoElements" attribute="Selector Pseudo" lineEndContext="#pop#pop">
- <keyword attribute="Selector Pseudo" context="#pop!SelectorPseudoValue" String="pseudoelements" />
- <RegExpr attribute="Selector Pseudo" context="#pop!SelectorPseudoValue" String="[-a-zA-Z][-a-zA-Z0-9]*" />
+ <context name="SelectorAttrValue" attribute="Normal Text" fallthroughContext="SelectorAttrEnd">
+ <DetectSpaces/>
+ <AnyChar context="SelectorAttrValueQuoted" String="&quot;'" lookAhead="1"/>
+ <RegExpr context="SelectorAttrEnd" attribute="String" String="-?(\w|&escaped;)([-\w]+|&escaped;)*"/>
+ <IncludeRules context="FindComments"/>
+ </context>
+ <context name="SelectorAttrValueQuoted" attribute="String" fallthroughContext="#pop!SelectorAttrEnd">
+ <IncludeRules context="FindStrings"/>
+ </context>
+ <context name="SelectorAttrEnd" attribute="String" fallthroughContext="#pop#pop#pop">
+ <DetectSpaces/>
+ <DetectChar attribute="Selector Attribute" context="#pop#pop#pop" char="]"/>
+ <Detect2Chars context="SelectorAttrValueSensitiveOp" char="i" char1="]" lookAhead="1"/>
+ <Detect2Chars context="SelectorAttrValueSensitiveOp" char="I" char1="]" lookAhead="1"/>
+ <Detect2Chars context="SelectorAttrValueSensitiveOp" char="s" char1="]" lookAhead="1"/>
+ <Detect2Chars context="SelectorAttrValueSensitiveOp" char="S" char1="]" lookAhead="1"/>
+ <IncludeRules context="FindComments"/>
+ </context>
+ <context name="SelectorAttrValueSensitiveOp" attribute="String">
+ <DetectChar attribute="Selector Attribute" context="#pop#pop#pop#pop" char="]"/>
+ <AnyChar attribute="Attribute Operator" String="isIS"/>
</context>
- <context name="SelectorPseudoValueSelector" attribute="Selector Pseudo" lineEndContext="#pop#pop" fallthrough="true" fallthroughContext="#pop#pop">
- <DetectChar attribute="Selector Pseudo" context="SelectorPseudoValueCloseSelector" char="(" />
+ <context name="PseudoClassOrElement" attribute="Selector Pseudo" lineEndContext="#pop" fallthroughContext="#pop">
+ <DetectChar char=":" attribute="Selector Pseudo"/>
+ <WordDetect String="lang" attribute="Selector Pseudo" context="PseudoClassStr"/>
+ <WordDetect String="part" attribute="Selector Pseudo" context="PseudoClassStr"/>
+ <RegExpr String="nth-[-a-zA-Z0-9]*" attribute="Selector Pseudo" context="PseudoClassNth"/>
+ <RegExpr String="[-a-zA-Z]+" attribute="Selector Pseudo" context="#pop"/>
+ <IncludeRules context="FindComments"/>
</context>
- <context name="SelectorPseudoValueCloseSelector" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <DetectChar attribute="Selector Pseudo" context="#pop#pop#pop" char=")" />
- <DetectChar attribute="Error" context="#pop#pop#pop!RuleSet" char="{" beginRegion="ruleset" />
- <IncludeRules context="FindSelector" />
+ <context name="PseudoClassStr" attribute="Error" lineEndContext="#pop#pop" fallthroughContext="#pop#pop">
+ <DetectChar char="(" context="PseudoClassStrInner" attribute="Selector Pseudo"/>
+ <IncludeRules context="FindComments"/>
+ </context>
+ <context name="PseudoClassStrInner" attribute="Error">
+ <DetectChar char=")" attribute="Selector Pseudo" context="#pop#pop#pop"/>
+ <DetectChar char="{" context="#pop#pop#pop!RuleSet" attribute="Error" beginRegion="ruleset"/>
+ <DetectChar char=":" context="#pop#pop#pop!PseudoClassOrElement" attribute="Error"/>
+ <RegExpr String="\s*&ident;(\s+&ident;|\s+)*" attribute="String"/>
+ <IncludeRules context="FindComments"/>
</context>
- <context name="SelectorPseudoValue" attribute="Selector Pseudo" lineEndContext="#pop#pop" fallthrough="true" fallthroughContext="#pop#pop">
- <DetectChar attribute="Selector Pseudo" context="SelectorPseudoValueClose" char="(" />
+ <context name="PseudoClassNth" attribute="Error" lineEndContext="#pop#pop" fallthroughContext="#pop#pop">
+ <DetectChar char="(" context="PseudoClassNthInner" attribute="Selector Pseudo"/>
+ <IncludeRules context="FindComments"/>
+ </context>
+ <context name="PseudoClassNthInner" attribute="Normal Text" fallthroughContext="#pop#pop!PseudoClassSelector">
+ <Int attribute="Number" additionalDeliminator="-"/>
+ <AnyChar String="+-" attribute="Operator"/>
+ <DetectChar char="n" attribute="Unit"/>
+ <DetectChar char=")" attribute="Selector Pseudo" context="#pop#pop#pop"/>
+ <WordDetect String="odd" attribute="Value"/>
+ <WordDetect String="even" attribute="Value"/>
+ <WordDetect String="of" attribute="Keyword" context="#pop#pop!PseudoClassSelector"/>
+ <IncludeRules context="FindComments"/>
+ <DetectSpaces/>
</context>
- <context name="SelectorPseudoValueClose" attribute="Selector Pseudo" lineEndContext="#pop#pop#pop">
- <DetectChar attribute="Selector Pseudo" context="#pop#pop#pop" char=")" />
- <DetectIdentifier />
+ <context name="PseudoClassSelector" attribute="Normal Text">
+ <DetectSpaces/>
+ <DetectChar char=")" context="#pop" attribute="Selector Pseudo"/>
+ <DetectChar attribute="Selector Pseudo" context="NestedPseudoClassSelector" char="("/>
+ <IncludeRules context="FindSelector"/>
+ <DetectChar char="{" context="#pop!RuleSet" attribute="Error" beginRegion="ruleset"/>
+ </context>
+ <context name="NestedPseudoClassSelector" attribute="Normal Text">
+ <DetectChar char="{" context="#pop" lookAhead="1"/>
+ <IncludeRules context="PseudoClassSelector"/>
</context>
- <context name="Comment" attribute="Comment" lineEndContext="#stay">
- <DetectSpaces />
- <Detect2Chars attribute="Comment" context="#pop#pop" char="*" char1="/" endRegion="comment" />
- <IncludeRules context="##Alerts" />
- <DetectIdentifier />
+ <context name="Comment" attribute="Comment">
+ <DetectSpaces/>
+ <Detect2Chars attribute="Comment" context="#pop#pop" char="*" char1="/" endRegion="comment"/>
+ <IncludeRules context="##Comments"/>
+ <DetectIdentifier/>
</context>
- <context name="RuleSet" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <DetectChar attribute="Normal Text" context="#pop" char="}" endRegion="ruleset" />
- <RegExpr attribute="Property" context="Rule" String="[A-Za-z_-]+(?=\s*:)" lookAhead="true" />
- <IncludeRules context="Base" />
- <RegExpr attribute="Normal Text" context="#stay" String="[A-Za-z_-]+" />
- <!-- auto-completion only, includes by previous rule -->
- <keyword attribute="Normal Text" context="#stay" String="properties" />
+ <context name="RuleSet" attribute="Normal Text" fallthroughContext="Rule">
+ <DetectSpaces/>
+ <DetectChar attribute="Block Symbol" context="#pop" char="}" endRegion="ruleset"/>
+ <DetectChar attribute="Block Symbol" context="RuleSet" char="{" beginRegion="ruleset"/>
+ <DetectChar context="SelectAtRule" char="@" lookAhead="1"/>
+ <IncludeRules context="FindComments"/>
+ <AnyChar context="Selector" String=".#:,[*>+~|&amp;" lookAhead="1"/>
</context>
- <context name="Rule" attribute="Normal Text" lineEndContext="#stay">
- <DetectChar attribute="Normal Text" context="RuleParameters" char=":" />
- <Detect2Chars attribute="Variable" context="IsVariable" char="-" char1="-" />
- <keyword attribute="Property" context="#stay" String="properties" />
- <RegExpr attribute="Unknown Property" context="#stay" String="[^:]+" />
+ <context name="Selector" attribute="Normal Text">
+ <DetectSpaces/>
+ <DetectChar attribute="Block Symbol" context="#pop!RuleSet" char="{" beginRegion="ruleset"/>
+ <DetectChar attribute="Block Symbol" context="#pop#pop" char="}" endRegion="ruleset"/>
+ <DetectChar context="#pop!SelectAtRule" char="@" lookAhead="1"/>
+ <IncludeRules context="FindComments"/>
+ <IncludeRules context="FindSelector"/>
</context>
- <context name="IsVariable" attribute="Normal Text" lineEndContext="#pop">
- <DetectChar attribute="Normal Text" context="#pop!RuleParameters" char=":" />
- <RegExpr attribute="Variable" context="#stay" String="[^:]+" />
+ <context name="Rule" attribute="Normal Text">
+ <DetectChar attribute="Property Separator" context="RuleParameters" char=":"/>
+ <Detect2Chars attribute="Variable" context="PropertyVariable" char="-" char1="-"/>
+ <IncludeRules context="FindComments"/>
+ <AnyChar context="#pop!Selector" String=".#,[*>+~|&amp;@{}" lookAhead="1"/>
+ <keyword attribute="Property" String="properties"/>
+ <DetectSpaces attribute="Normal Text"/>
+ <IncludeRules context="FindVendorProperty"/>
+ <RegExpr attribute="Unknown Property" String="[-_a-zA-Z]*"/>
+ </context>
+ <context name="PropertyVariable" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="#pop">
+ <RegExpr attribute="Variable" context="#pop" String="[-_a-zA-Z0-9]*"/>
</context>
- <context name="RuleParameters" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <IncludeRules context="FindComments" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindFunctions" />
- <IncludeRules context="FindValues" />
+ <context name="RuleParameters" attribute="Normal Text">
+ <DetectSpaces/>
<!-- Jump out conditions -->
- <DetectChar attribute="Separator Symbol" context="#pop#pop" char=";" />
- <DetectChar attribute="Separator Symbol" context="#stay" char="," />
- <DetectChar attribute="Normal Text" context="#pop#pop#pop" char="}" endRegion="ruleset" />
- <!-- auto-completion only -->
- <keyword attribute="Normal Text" context="#stay" String="functions" />
+ <DetectChar attribute="Separator Symbol" context="#pop#pop" char=";"/>
+ <DetectChar attribute="Block Symbol" context="#pop#pop#pop" char="}" endRegion="ruleset"/>
+ <DetectChar attribute="Separator Symbol" char=","/>
+ <IncludeRules context="FindComments"/>
+ <IncludeRules context="FindStrings"/>
+ <IncludeRules context="FindFunctions"/>
+ <IncludeRules context="FindValues"/>
</context>
- <context name="Function" attribute="Normal Text" lineEndContext="#stay">
- <DetectChar attribute="Function" context="FunctionParameters" char="(" />
- <StringDetect attribute="Function" context="FunctionVar" String="var(" />
- <StringDetect attribute="Function" context="FunctionUrl" String="url(" />
- <StringDetect attribute="Function" context="FunctionCalc" String="calc(" />
- <keyword attribute="Function" context="#stay" String="functions" />
- <RegExpr attribute="Normal Text" context="#stay" String="[-a-zA-Z][-a-zA-Z0-9]*" />
+ <context name="Function" attribute="Normal Text">
+ <IncludeRules context="FunctionInc"/>
+ <DetectChar attribute="Error" context="#pop#pop#pop" char=";"/>
+ <DetectChar attribute="Error" context="#pop#pop#pop" char="}" endRegion="ruleset"/>
+ <DetectChar attribute="Error" context="#pop#pop#pop!RuleSet" char="{" beginRegion="ruleset"/>
</context>
-
- <context name="FunctionVar" attribute="Variable" lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop!FunctionParameters">
- <DetectSpaces />
- <RegExpr attribute="Variable" context="#pop!FunctionParameters" String="--[^ \t\),]+" />
+ <context name="NestedFunction" attribute="Normal Text">
+ <IncludeRules context="FunctionInc"/>
+ <AnyChar context="#pop" String="{};" lookAhead="1"/>
</context>
-
- <context name="FunctionCalc" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <DetectChar attribute="Function" context="#pop#pop" char=")" />
- <IncludeRules context="Calc" />
+ <context name="FunctionInc" attribute="Normal Text">
+ <DetectChar attribute="Function" context="FunctionParameters" char="("/>
+ <StringDetect attribute="Function" context="FunctionVar" String="var("/>
+ <StringDetect attribute="Function" context="FunctionCalc" String="calc("/>
+ <StringDetect attribute="Function" context="FunctionUrl" String="url("/>
+ <StringDetect attribute="Function" context="FunctionEnv" String="env("/>
+ <keyword attribute="Function" String="functions"/>
+ <!-- Legacy -->
+ <WordDetect attribute="Function" String="rgba"/>
+ <WordDetect attribute="Function" String="hsla"/>
+ <RegExpr attribute="Unknown Function" String="&func;"/>
</context>
- <context name="NestedCalc" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <DetectChar attribute="Normal Text" context="#pop" char=")" />
- <IncludeRules context="Calc" />
+ <context name="FunctionVar" attribute="Normal Text" fallthroughContext="FunctionVarSep">
+ <RegExpr String="&var;" context="FunctionVarSep" attribute="Variable"/>
+ <DetectSpaces/>
+ <IncludeRules context="FindComments"/>
+ </context>
+ <context name="FunctionVarSep" attribute="Normal Text" fallthroughContext="FunctionVarParam">
+ <DetectChar char=")" context="#pop#pop#pop" attribute="Function"/>
+ <DetectChar char="," context="FunctionVarParam" attribute="Separator Symbol"/>
+ </context>
+ <context name="FunctionVarParam" attribute="Normal Text">
+ <DetectSpaces/>
+ <DetectChar char=")" context="#pop#pop#pop#pop" attribute="Function"/>
+ <AnyChar String=";{}" context="#pop#pop#pop" lookAhead="1"/>
+ <IncludeRules context="FindStrings"/>
+ <IncludeRules context="FindComments"/>
+ <IncludeRules context="FindNestedFunctions"/>
+ <IncludeRules context="FindValues"/>
</context>
- <context name="Calc" attribute="Normal Text" lineEndContext="#stay">
- <DetectChar attribute="Separator Symbol" context="#stay" char="," />
- <DetectChar attribute="Normal Text" context="NestedCalc" char="(" />
- <IncludeRules context="FindComments" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindFunctions" />
- <IncludeRules context="FindValues" />
- <RegExpr attribute="Operator" context="#stay" String="[-](?=$|[ \t(,;])|[+](?=$|[^0-9)])|[/*]" />
- <!-- auto-completion only -->
- <keyword attribute="Normal Text" context="#stay" String="functions" />
+ <context name="FunctionEnv" attribute="Normal Text" fallthroughContext="FunctionVarSep">
+ <RegExpr String="&ident;" context="FunctionVarSep" attribute="Normal Text"/>
+ <DetectSpaces/>
+ <IncludeRules context="FindComments"/>
</context>
- <context name="FunctionUrl" attribute="Normal Text" lineEndContext="#stay" fallthrough="true" fallthroughContext="UrlValue">
- <DetectSpaces />
- <IncludeRules context="FindStrings" />
- <DetectChar attribute="Function" context="#pop#pop" char=")" />
+ <context name="FunctionCalc" attribute="Normal Text">
+ <DetectSpaces/>
+ <DetectChar attribute="Function" context="#pop#pop" char=")"/>
+ <IncludeRules context="Calc"/>
+ </context>
+ <context name="NestedCalc" attribute="Normal Text">
+ <DetectSpaces/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ <IncludeRules context="Calc"/>
+ </context>
+ <context name="Calc" attribute="Normal Text">
+ <DetectChar attribute="Separator Symbol" char=","/>
+ <DetectChar attribute="Normal Text" context="NestedCalc" char="("/>
+ <AnyChar String=";{}" context="#pop" lookAhead="1"/>
+ <IncludeRules context="FindStrings"/>
+ <IncludeRules context="FindComments"/>
+ <AnyChar attribute="Operator" String="*/%"/>
+ <IncludeRules context="FindNestedFunctions"/>
+ <IncludeRules context="FindNumber"/>
+ <RegExpr String="[-+]?((?i:infinity|e|pi)|NaN)($|[^-\w])|[-+]($|[\s(,;]|/\*)" context="CalcConstantOrOperator" lookAhead="1"/>
+ <IncludeRules context="FindIdentifier"/>
+ </context>
+ <context name="CalcConstantOrOperator" attribute="Normal Text" fallthroughContext="#pop">
+ <DetectIdentifier context="#pop" attribute="Calc Constant"/>
+ <AnyChar String="-+" attribute="Operator"/>
</context>
- <context name="UrlValue" attribute="String" lineEndContext="#stay">
- <DetectChar attribute="Function" context="#pop#pop#pop" char=")" />
+ <context name="FunctionUrl" attribute="Normal Text" fallthroughContext="UrlValue">
+ <DetectSpaces/>
+ <IncludeRules context="FindStrings"/>
+ </context>
+ <context name="UrlValue" attribute="String">
+ <DetectChar attribute="Function" context="#pop#pop#pop" char=")"/>
+ <AnyChar String=";{}" context="#pop#pop" lookAhead="1"/>
</context>
- <context name="FunctionParameters" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces />
- <DetectChar attribute="Function" context="#pop#pop" char=")" />
- <IncludeRules context="FindComments" />
- <IncludeRules context="FindStrings" />
- <IncludeRules context="FindValues" />
- <DetectChar attribute="Separator Symbol" context="#stay" char="," />
+ <context name="FunctionParameters" attribute="Normal Text">
+ <DetectSpaces/>
+ <DetectChar attribute="Function" context="#pop#pop" char=")"/>
+ <DetectChar attribute="Separator Symbol" char=","/>
+ <AnyChar String=";{}" context="#pop" lookAhead="1"/>
+ <IncludeRules context="FindComments"/>
+ <IncludeRules context="FindStrings"/>
+ <IncludeRules context="FindNestedFunctions"/>
+ <IncludeRules context="FindValues"/>
</context>
<!-- string contexts -->
- <context name="StringDQ" attribute="String" lineEndContext="#stay">
- <DetectChar attribute="String" context="#pop" char="&quot;" />
- <IncludeRules context="InsideString" />
+ <context name="StringDQ" attribute="String" lineEndContext="#pop">
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ <IncludeRules context="InsideString"/>
</context>
- <context name="StringSQ" attribute="String" lineEndContext="#stay">
- <DetectChar attribute="String" context="#pop" char="'" />
- <IncludeRules context="InsideString" />
+ <context name="StringSQ" attribute="String" lineEndContext="#pop">
+ <DetectChar attribute="String" context="#pop" char="'"/>
+ <IncludeRules context="InsideString"/>
</context>
- <context name="InsideString" attribute="String" lineEndContext="#stay">
- <RegExpr attribute="SpecialChar" context="#stay" String="\\([0-9A-Fa-f]{1,6}|.?)" />
- <RegExpr attribute="String" context="#stay" String="\\?[^&quot;'\\]+" />
+ <context name="InsideString" attribute="String">
+ <RegExpr attribute="SpecialChar" String="\\([0-9A-Fa-f]{1,6}|.?)"/>
+ <RegExpr attribute="String" String="\\?[^&quot;'\\]+"/>
</context>
</contexts>
@@ -1699,37 +2033,51 @@ Changelog:
<itemDatas>
<itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false"/>
<itemData name="At Rule" defStyleNum="dsImport" spellChecking="false"/>
+ <itemData name="At Rule Text" defStyleNum="dsNormal" spellChecking="false"/>
+ <itemData name="Media Type" defStyleNum="dsAnnotation" spellChecking="false"/>
<itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
<itemData name="Property" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="Vendor Property" defStyleNum="dsKeyword" spellChecking="false" italic="1"/>
<itemData name="Unknown Property" defStyleNum="dsNormal" spellChecking="false"/>
+ <itemData name="Property Separator" defStyleNum="dsChar" spellChecking="false"/>
<itemData name="String" defStyleNum="dsString"/>
<itemData name="SpecialChar" defStyleNum="dsSpecialChar" spellChecking="false"/>
<itemData name="Operator" defStyleNum="dsOperator" spellChecking="false"/>
<itemData name="Separator Symbol" defStyleNum="dsOperator" spellChecking="false"/>
+ <itemData name="Block Symbol" defStyleNum="dsNormal" spellChecking="false"/>
<itemData name="Value" defStyleNum="dsDecVal" spellChecking="false"/>
<itemData name="Number" defStyleNum="dsDecVal" spellChecking="false"/>
<itemData name="Value Keyword" defStyleNum="dsBuiltIn" spellChecking="false"/>
+ <itemData name="Named Color" defStyleNum="dsConstant" spellChecking="false"/>
<itemData name="Color" defStyleNum="dsConstant" spellChecking="false"/>
<itemData name="Unit" defStyleNum="dsDataType" spellChecking="false"/>
<itemData name="Variable" defStyleNum="dsVariable" spellChecking="false"/>
<itemData name="Function" defStyleNum="dsFunction" spellChecking="false"/>
+ <itemData name="Unknown Function" defStyleNum="dsNormal" spellChecking="false"/>
+ <itemData name="Calc Constant" defStyleNum="dsDecVal" spellChecking="false"/>
<itemData name="Annotation" defStyleNum="dsAttribute" spellChecking="false"/>
+ <itemData name="Condition Open/Close" defStyleNum="dsFunction" spellChecking="false"/>
+ <itemData name="Condition Property" defStyleNum="dsKeyword" spellChecking="false"/>
<itemData name="Selector Id" defStyleNum="dsPreprocessor" bold="1" spellChecking="false"/>
<itemData name="Selector Class" defStyleNum="dsFunction" spellChecking="false"/>
<itemData name="Selector Attribute" defStyleNum="dsExtension" spellChecking="false"/>
<itemData name="Selector Pseudo" defStyleNum="dsInformation" italic="1" spellChecking="false"/>
<itemData name="Selector Tag" defStyleNum="dsNormal" spellChecking="false"/>
- <itemData name="Comment" defStyleNum="dsComment" />
+ <itemData name="Selector Symbol" defStyleNum="dsOperator" spellChecking="false"/>
+ <itemData name="Attribute Name" defStyleNum="dsSpecialString" spellChecking="false"/>
+ <itemData name="Attribute Operator" defStyleNum="dsOperator" spellChecking="false"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
<itemData name="Region Marker" defStyleNum="dsRegionMarker" spellChecking="false"/>
<itemData name="Error" defStyleNum="dsError" spellChecking="false"/>
</itemDatas>
</highlighting>
<general>
- <keywords casesensitive="0" weakDeliminator="-%@" />
+ <keywords casesensitive="0" weakDeliminator="-%@"/>
<comments>
- <comment name="multiLine" start="/*" end="*/" />
+ <comment name="multiLine" start="/*" end="*/" region="comment"/>
</comments>
</general>
</language>
+<!-- kate: replace-tabs on; tab-width 4; indent-width 4; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/doxygen.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/doxygen.xml
index a6abda9204..da87e4d917 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/doxygen.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/doxygen.xml
@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd"
+<!DOCTYPE language
[
- <!ENTITY wordsep "([][,?;()]|\.$|\.?\s)"> <!-- things that end a TagWord -->
+ <!ENTITY wordsep "(?:[][,?;()]|\.$|\.?\s|$)"> <!-- things that end a TagWord -->
+ <!ENTITY sl_word ".*?(?=&wordsep;)">
+ <!ENTITY ml_word ".*?(?=&wordsep;|\*/)">
]>
<language name="Doxygen"
- version="6"
+ version="15"
kateversion="5.0"
section="Markup"
extensions="*.dox;*.doxygen"
@@ -42,10 +44,6 @@
<item>\endrtfonly</item> <item>@endrtfonly</item>
<item>\endsecreflist</item> <item>@endsecreflist</item>
<item>\endxmlonly</item> <item>@endxmlonly</item>
- <item>\f[</item> <item>@f[</item>
- <item>\f]</item> <item>@f]</item>
- <item>\f$</item> <item>@f$</item>
- <!-- TODO Add @f{environment}{ and @f} -->
<item>\hideinitializer</item> <item>@hideinitializer</item>
<item>\htmlonly</item> <item>@htmlonly</item>
<!-- TODO @internal is a candidate to be handled separately, cuz may introduce folding region -->
@@ -84,21 +82,6 @@
<item>\test</item> <item>@test</item>
<item>\version</item> <item>@version</item>
<item>\xmlonly</item> <item>@xmlonly</item>
- <!-- TODO Introduce separate context for @~ ? -->
- <item>\#</item> <item>@#</item>
- <item>\$</item> <item>@$</item>
- <item>\%</item> <item>@%</item>
- <item>\&amp;</item> <item>@&amp;</item>
- <item>\&gt;</item> <item>@&gt;</item>
- <item>\&lt;</item> <item>@&lt;</item>
- <item>\&quot;</item> <item>@&quot;</item>
- <item>\::</item> <item>@::</item>
- <item>\@</item> <item>@@</item>
- <item>\\</item> <item>@\</item>
- <item>\~</item> <item>@~</item>
- <item>\.</item> <item>@.</item>
- <item>\--</item> <item>@--</item>
- <item>\---</item> <item>@---</item>
</list>
<list name="TagWord">
@@ -202,6 +185,12 @@
<item>\var</item> <item>@var</item>
<item>\vhdlflow</item> <item>@vhdlflow</item>
</list>
+ <list name="TagEnd">
+ <item>\endcode</item> <item>@endcode</item>
+ <item>\endverbatim</item> <item>@endverbatim</item>
+ <item>\endmsc</item> <item>@endmsc</item>
+ <item>\enddot</item> <item>@enddot</item>
+ </list>
<list name="Note">
<item>\note</item> <item>@note</item>
</list>
@@ -231,8 +220,8 @@
TODO Not all commands are handled properly nowadays :( Need few more contexts...
-->
<context attribute="Normal Text" lineEndContext="#stay" name="Normal">
- <RegExpr attribute="Comment" context="LineComment" String="//(!|(/(?=[^/]|$)))&lt;?" />
- <RegExpr attribute="Comment" context="BlockComment" String="/\*(\*[^*/]|!|[*!]&lt;|\*$)" beginRegion="BlockComment" />
+ <RegExpr attribute="Comment" context="LineComment" String="//(?:!|(?:/(?=[^/]|$)))&lt;?" />
+ <RegExpr attribute="Comment" context="BlockComment" String="/\*(?:\*[^*/]|!|[*!]&lt;|\*$)" beginRegion="BlockComment" />
<RegExpr attribute="Region" context="#stay" String="//\s*@\{\s*$" beginRegion="MemberGroup" />
<RegExpr attribute="Region" context="#stay" String="//\s*@\}\s*$" endRegion="MemberGroup" />
<RegExpr attribute="Region" context="#stay" String="/\*\s*@\{\s*\*/" beginRegion="MemberGroup" />
@@ -241,39 +230,20 @@
<context attribute="Comment" lineEndContext="#pop" name="LineComment">
<LineContinue attribute="Comment" context="#stay" />
<DetectSpaces />
- <IncludeRules context="##Alerts" />
- <IncludeRules context="SL_DetectEnv" />
- <keyword attribute="Tags" context="#stay" String="TagOnly" />
- <keyword attribute="Tags" context="SL_TagWord" String="TagWord" />
- <keyword attribute="Tags" context="SL_TagParam" String="TagParam" />
- <keyword attribute="Tags" context="SL_TagWordWord" String="TagWordWord" />
- <keyword attribute="Tags" context="SL_TagString" String="TagString" />
- <keyword attribute="Tags" context="SL_TagWordString" String="TagWordString" />
- <RegExpr attribute="Custom Tags" context="#stay" String="[@\\][^@\\ \t]+" />
+ <IncludeRules context="##Comments" />
<DetectIdentifier />
- <StringDetect attribute="HTML Comment" context="SL_htmlcomment" String="&lt;!--" />
- <Detect2Chars attribute="Comment" context="#stay" char="&lt;" char1="&lt;" />
- <RegExpr attribute="HTML Tag" context="SL_htmltag" String="&lt;\/?[-\w0-9._:@]+" />
+ <AnyChar attribute="Comment" context="SL_StartTag" String="\@" lookAhead="true" />
+ <DetectChar attribute="Comment" context="SL_StartHTMLTag" char="&lt;" lookAhead="true" />
+ <DetectChar attribute="Comment" context="Entities" char="&amp;" lookAhead="true" />
</context>
<context attribute="Comment" lineEndContext="#stay" name="BlockComment">
<DetectSpaces />
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="BlockComment" />
- <IncludeRules context="##Alerts" />
- <Detect2Chars attribute="Region" context="#stay" char="@" char1="{" beginRegion="Group" />
- <Detect2Chars attribute="Region" context="#stay" char="@" char1="}" endRegion="Group" />
- <IncludeRules context="SL_DetectEnv" />
- <keyword attribute="Tags" context="#stay" String="TagOnly" />
- <keyword attribute="Tags" context="ML_TagWord" String="TagWord" />
- <keyword attribute="Tags" context="ML_TagParam" String="TagParam" />
- <keyword attribute="Tags" context="ML_TagWordWord" String="TagWordWord" />
- <keyword attribute="Tags" context="ML_TagString" String="TagString" />
- <keyword attribute="Tags" context="ML_TagWordString" String="TagWordString" />
- <RegExpr attribute="Custom Tags" context="#stay" String="[@\\]([^@\\ \t\*]|\*(?!/))+" />
+ <IncludeRules context="##Comments" />
<DetectIdentifier />
- <RegExpr attribute="Tags" context="#stay" String="\\(&lt;|&gt;)" />
- <Detect2Chars attribute="Comment" context="#stay" char="&lt;" char1="&lt;" />
- <RegExpr attribute="HTML Tag" context="ML_htmltag" String="&lt;\/?[-\w0-9._:@]+" />
- <StringDetect attribute="HTML Comment" context="ML_htmlcomment" String="&lt;!--" />
+ <AnyChar attribute="Comment" context="ML_StartTag" String="\@" lookAhead="true" />
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="BlockComment" />
+ <DetectChar attribute="Comment" context="ML_StartHTMLTag" char="&lt;" lookAhead="true" />
+ <DetectChar attribute="Comment" context="Entities" char="&amp;" lookAhead="true" />
</context>
<!-- NOTE: all contexts beginning with ML_ are for multiline comments
@@ -284,123 +254,254 @@
much more complex and very hard to understand! (But use IncludeRules where the rules don't
refer to another SL_*/ML_* context, to help maintainability.)
-->
+ <context attribute="Comment" lineEndContext="#pop" name="ML_StartTag">
+ <keyword attribute="Tags" context="#pop!ML_TagWord" String="TagWord" />
+ <keyword attribute="Tags" context="#pop!ML_TagParam" String="TagParam" />
+ <keyword attribute="Tags" context="#pop!ML_TagWordWord" String="TagWordWord" />
+ <keyword attribute="Tags" context="#pop!ML_TagString" String="TagString" />
+ <keyword attribute="Tags" context="#pop!ML_TagWordString" String="TagWordString" />
+ <WordDetect attribute="Tags" context="#pop!ML_Code" String="\code" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!ML_Code" String="@code" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!ML_Verbatim" String="\verbatim" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!ML_Verbatim" String="@verbatim" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!ML_Formula" String="\f[" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!ML_Formula" String="@f[" beginRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop!ML_FormulaShort" String="\f$" beginRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop!ML_FormulaShort" String="@f$" beginRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop!ML_FormulaEnv" String="\f{" beginRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop!ML_FormulaEnv" String="@f{" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!ML_Msc" String="\msc" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!ML_Msc" String="@msc" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!ML_Dot" String="\dot" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!ML_Dot" String="@dot" beginRegion="VerbatimBlock" />
+ <IncludeRules context="Inc_OtherTag" />
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="ML_StartHTMLTag">
+ <Detect2Chars attribute="Comment" context="#pop" char="&lt;" char1="&lt;" />
+ <RegExpr attribute="HTML Tag" context="#pop!ML_htmltag" String="&lt;\/?[-\w0-9._:@]+" />
+ <StringDetect attribute="HTML Comment" context="#pop!ML_htmlcomment" String="&lt;!--" />
+ <DetectChar attribute="Comment" context="#pop" char="&lt;" />
+ </context>
+
<!-- tag contexts -->
<context attribute="Comment" lineEndContext="#pop" name="ML_TagWord">
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" lookAhead="true" />
- <IncludeRules context="SL_TagWord" />
+ <DetectSpaces />
+ <Detect2Chars attribute="Comment" context="#pop#pop" char="*" char1="/" endRegion="BlockComment" />
+ <keyword attribute="Tags" context="#pop!ML_TagWord" String="TagWord" />
+ <IncludeRules context="ML_Inc_Word" />
</context>
<context attribute="Comment" lineEndContext="#pop" name="ML_TagParam">
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" lookAhead="true" />
<DetectSpaces />
- <StringDetect attribute="Tags" context="ML_Tag2ndWord" String="[in]" />
- <StringDetect attribute="Tags" context="ML_Tag2ndWord" String="[out]" />
- <StringDetect attribute="Tags" context="ML_Tag2ndWord" String="[in,out]" />
- <RegExpr attribute="Word" context="#pop" String="\S(?=&wordsep;)" />
- <RegExpr attribute="Word" context="#stay" String="\S" />
+ <Detect2Chars attribute="Comment" context="#pop#pop" char="*" char1="/" endRegion="BlockComment" />
+ <StringDetect attribute="Tags" context="#pop!ML_TagWord" String="[in]" />
+ <StringDetect attribute="Tags" context="#pop!ML_TagWord" String="[out]" />
+ <StringDetect attribute="Tags" context="#pop!ML_TagWord" String="[in,out]" />
+ <IncludeRules context="ML_Inc_Word" />
</context>
<context attribute="Comment" lineEndContext="#pop" name="ML_TagWordWord">
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" lookAhead="true" />
<DetectSpaces />
- <RegExpr attribute="Word" context="ML_Tag2ndWord" String="\S(?=&wordsep;)" />
- <RegExpr attribute="Word" context="#stay" String="\S" />
- </context>
- <!-- TODO for kate 2.x, convert references to "#pop!ML_TagWord" -->
- <context attribute="Comment" lineEndContext="#pop#pop" name="ML_Tag2ndWord">
- <Detect2Chars attribute="Comment" context="#pop#pop" char="*" char1="/" lookAhead="true" />
- <IncludeRules context="SL_Tag2ndWord" />
+ <Detect2Chars attribute="Comment" context="#pop#pop" char="*" char1="/" endRegion="BlockComment" />
+ <RegExpr attribute="Word" context="#pop!ML_TagWord" String="&ml_word;" />
</context>
<context attribute="Comment" lineEndContext="#pop" name="ML_TagString">
<DetectSpaces />
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" lookAhead="true" />
+ <Detect2Chars attribute="Comment" context="#pop#pop" char="*" char1="/" endRegion="BlockComment" />
<StringDetect attribute="HTML Comment" context="ML_htmlcomment" String="&lt;!--" />
<Detect2Chars attribute="Comment" context="#stay" char="&lt;" char1="&lt;" />
<RegExpr attribute="HTML Tag" context="ML_htmltag" String="&lt;\/?[-\w0-9._:@]+" />
- <RegExpr attribute="Description" context="#stay" String="." />
+ <RegExpr attribute="Description" context="#stay" String="[&lt;*]?[^&lt;*\s]+" />
</context>
<context attribute="Comment" lineEndContext="#pop" name="ML_TagWordString">
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" lookAhead="true" />
- <IncludeRules context="SL_TagWordString" />
+ <DetectSpaces />
+ <Detect2Chars attribute="Comment" context="#pop#pop" char="*" char1="/" endRegion="BlockComment" />
+ <IncludeRules context="ML_Inc_Word" />
</context>
+ <context name="ML_Inc_Word" attribute="Word" lineEndContext="#pop">
+ <RegExpr attribute="Word" context="#pop" String="&ml_word;" />
+ </context>
+
+
<!-- html contexts -->
<context name="ML_htmltag" attribute="Identifier" lineEndContext="#stay">
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" lookAhead="true" />
+ <Detect2Chars attribute="Comment" context="#pop#pop" char="*" char1="/" endRegion="BlockComment" />
<Detect2Chars attribute="HTML Tag" context="#pop" char="/" char1="&gt;" />
<DetectChar attribute="HTML Tag" context="#pop" char="&gt;" />
- <RegExpr attribute="Identifier" context="ML_identifiers" String="\s*=\s*" />
+ <DetectSpaces />
+ <DetectChar attribute="Identifier" context="ML_identifiers" char="=" />
</context>
<context name="ML_htmlcomment" attribute="HTML Comment" lineEndContext="#stay">
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" lookAhead="true" />
- <IncludeRules context="##Alerts" />
+ <DetectSpaces />
+ <Detect2Chars attribute="Comment" context="#pop#pop" char="*" char1="/" endRegion="BlockComment" />
+ <IncludeRules context="##Comments" />
+ <DetectIdentifier />
<StringDetect attribute="HTML Comment" context="#pop" String="--&gt;" />
</context>
<context name="ML_identifiers" attribute="Identifier" lineEndContext="#stay">
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" lookAhead="true" />
- <RegExpr attribute="Identifier" context="#pop" String="\s*#?[a-zA-Z0-9]*" />
+ <DetectSpaces />
+ <Detect2Chars attribute="Comment" context="#pop#pop#pop" char="*" char1="/" endRegion="BlockComment" />
<DetectChar attribute="Types" context="ML_types1" char="'" />
<DetectChar attribute="Types" context="ML_types2" char="&quot;" />
</context>
<context name="ML_types1" attribute="Types" lineEndContext="#stay">
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" lookAhead="true" />
+ <Detect2Chars attribute="Comment" context="#pop#pop#pop#pop" char="*" char1="/" endRegion="BlockComment" />
<DetectChar attribute="Types" context="#pop#pop" char="'" />
</context>
<context name="ML_types2" attribute="Types" lineEndContext="#stay">
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" lookAhead="true" />
+ <Detect2Chars attribute="Comment" context="#pop#pop#pop#pop" char="*" char1="/" endRegion="BlockComment" />
<DetectChar attribute="Types" context="#pop#pop" char="&quot;" />
</context>
+ <context name="ML_FindNextLine" attribute="Comment" lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop">
+ <DetectSpaces />
+ <Detect2Chars attribute="Comment" context="#pop!ML_End_BlockComment" char="*" char1="/" endRegion="VerbatimBlock" lookAhead="1"/>
+ <Detect2Chars attribute="Comment" context="ML_VerbatimPrefix" char="*" char1="*" lookAhead="true" />
+ <DetectChar attribute="Comment" context="#pop" char="*" />
+ </context>
+ <context name="ML_VerbatimPrefix" attribute="Comment" lineEndContext="#stay">
+ <DetectChar attribute="Comment" context="#pop" char="*" />
+ </context>
+
+ <context name="ML_End_BlockComment" attribute="Comment" lineEndContext="#stay">
+ <Detect2Chars attribute="Error" context="#pop#pop#pop" char="*" char1="/" endRegion="BlockComment"/>
+ </context>
+
+ <context name="ML_Code" attribute="Code" lineEndContext="ML_FindNextLine">
+ <Detect2Chars attribute="Comment" context="ML_End_BlockComment" char="*" char1="/" endRegion="VerbatimBlock" lookAhead="1"/>
+ <WordDetect attribute="Tags" context="#pop" String="\endcode" endRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop" String="@endcode" endRegion="VerbatimBlock" />
+ </context>
+
+ <context name="ML_Verbatim" attribute="Verbatim" lineEndContext="ML_FindNextLine">
+ <Detect2Chars attribute="Comment" context="ML_End_BlockComment" char="*" char1="/" endRegion="VerbatimBlock" lookAhead="1"/>
+ <WordDetect attribute="Tags" context="#pop" String="\endverbatim" endRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop" String="@endverbatim" endRegion="VerbatimBlock" />
+ </context>
+
+ <context name="ML_Formula" attribute="Formulas" lineEndContext="ML_FindNextLine">
+ <Detect2Chars attribute="Comment" context="ML_End_BlockComment" char="*" char1="/" endRegion="VerbatimBlock" lookAhead="1"/>
+ <StringDetect attribute="Tags" context="#pop" String="\f]" endRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop" String="@f]" endRegion="VerbatimBlock" />
+ <!-- TODO: How to force LaTeX math context here?? -->
+ <!-- <IncludeRules context="##LaTeX" /> -->
+ </context>
+
+ <context name="ML_FormulaShort" attribute="Formulas" lineEndContext="ML_FindNextLine">
+ <Detect2Chars attribute="Comment" context="ML_End_BlockComment" char="*" char1="/" endRegion="VerbatimBlock" lookAhead="1"/>
+ <StringDetect attribute="Tags" context="#pop" String="\f$" endRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop" String="@f$" endRegion="VerbatimBlock" />
+ <!-- TODO: How to force LaTeX math context here?? -->
+ <!-- <IncludeRules context="##LaTeX" /> -->
+ </context>
+
+ <context name="ML_FormulaEnv" attribute="Word" lineEndContext="ML_FindNextLine">
+ <DetectChar attribute="Tags" context="#pop!ML_FormulaEnvStart" char="}" />
+ <Detect2Chars attribute="Comment" context="ML_End_BlockComment" char="*" char1="/" endRegion="VerbatimBlock" lookAhead="1"/>
+ </context>
+
+ <context name="ML_FormulaEnvStart" attribute="Formulas" lineEndContext="ML_FindNextLine" fallthrough="true" fallthroughContext="#pop!ML_FormulaEnvFormula">
+ <DetectSpaces attribute="Comment" />
+ <DetectChar attribute="Tags" context="#pop!ML_FormulaEnvFormula" char="{" />
+ <Detect2Chars attribute="Comment" context="ML_End_BlockComment" char="*" char1="/" endRegion="VerbatimBlock" lookAhead="1"/>
+ </context>
+
+ <context name="ML_FormulaEnvFormula" attribute="Formulas" lineEndContext="ML_FindNextLine">
+ <Detect2Chars attribute="Comment" context="ML_End_BlockComment" char="*" char1="/" endRegion="VerbatimBlock" lookAhead="1"/>
+ <StringDetect attribute="Tags" context="#pop" String="\f}" endRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop" String="@f}" endRegion="VerbatimBlock" />
+ <!-- TODO: How to force LaTeX math context here?? -->
+ <!-- <IncludeRules context="##LaTeX" /> -->
+ </context>
+
+ <context name="ML_Msc" attribute="Message Sequence Chart" lineEndContext="ML_FindNextLine">
+ <Detect2Chars attribute="Comment" context="ML_End_BlockComment" char="*" char1="/" endRegion="VerbatimBlock" lookAhead="1"/>
+ <WordDetect attribute="Tags" context="#pop" String="\endmsc" endRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop" String="@endmsc" endRegion="VerbatimBlock" />
+ </context>
+
+ <context name="ML_Dot" attribute="Dot Graph" lineEndContext="ML_FindNextLine">
+ <Detect2Chars attribute="Comment" context="ML_End_BlockComment" char="*" char1="/" endRegion="VerbatimBlock" lookAhead="1"/>
+ <WordDetect attribute="Tags" context="#pop" String="\enddot" endRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop" String="@enddot" endRegion="VerbatimBlock" />
+ </context>
+
<!-- NOTE: all contexts beginning with SL_ are for singleline comments -->
+ <context attribute="Comment" lineEndContext="#pop" name="SL_StartTag">
+ <keyword attribute="Tags" context="#pop!SL_TagWord" String="TagWord" />
+ <keyword attribute="Tags" context="#pop!SL_TagParam" String="TagParam" />
+ <keyword attribute="Tags" context="#pop!SL_TagWordWord" String="TagWordWord" />
+ <keyword attribute="Tags" context="#pop!SL_TagString" String="TagString" />
+ <keyword attribute="Tags" context="#pop!SL_TagWordString" String="TagWordString" />
+ <WordDetect attribute="Tags" context="#pop!SL_Code" String="\code" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!SL_Code" String="@code" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!SL_Verbatim" String="\verbatim" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!SL_Verbatim" String="@verbatim" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!SL_Formula" String="\f[" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!SL_Formula" String="@f[" beginRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop!SL_FormulaShort" String="\f$" beginRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop!SL_FormulaShort" String="@f$" beginRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop!SL_FormulaEnv" String="\f{" beginRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop!SL_FormulaEnv" String="@f{" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!SL_Msc" String="\msc" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!SL_Msc" String="@msc" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!SL_Dot" String="\dot" beginRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop!SL_Dot" String="@dot" beginRegion="VerbatimBlock" />
+ <IncludeRules context="Inc_OtherTag" />
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="SL_StartHTMLTag">
+ <Detect2Chars attribute="Comment" context="#pop" char="&lt;" char1="&lt;" />
+ <RegExpr attribute="HTML Tag" context="#pop!SL_htmltag" String="&lt;\/?[-\w0-9._:@]+" />
+ <StringDetect attribute="HTML Comment" context="#pop!SL_htmlcomment" String="&lt;!--" />
+ <DetectChar attribute="Comment" context="#pop" char="&lt;" />
+ </context>
+
<!-- tag contexts -->
<context attribute="Comment" lineEndContext="#pop" name="SL_TagWord">
<DetectSpaces />
- <keyword attribute="Tags" context="#pop" String="TagWord" lookAhead="true" />
- <RegExpr attribute="Word" context="#pop" String="\S(?=&wordsep;)" />
- <RegExpr attribute="Word" context="#stay" String="\S" />
+ <keyword attribute="Tags" context="#pop!SL_TagWord" String="TagWord" />
+ <IncludeRules context="SL_Inc_Word" />
</context>
<context attribute="Comment" lineEndContext="#pop" name="SL_TagParam">
<DetectSpaces />
- <StringDetect attribute="Tags" context="SL_Tag2ndWord" String="[in]" />
- <StringDetect attribute="Tags" context="SL_Tag2ndWord" String="[out]" />
- <StringDetect attribute="Tags" context="SL_Tag2ndWord" String="[in,out]" />
- <RegExpr attribute="Word" context="#pop" String="\S(?=&wordsep;)" />
- <RegExpr attribute="Word" context="#stay" String="\S" />
+ <StringDetect attribute="Tags" context="#pop!SL_TagWord" String="[in]" />
+ <StringDetect attribute="Tags" context="#pop!SL_TagWord" String="[out]" />
+ <StringDetect attribute="Tags" context="#pop!SL_TagWord" String="[in,out]" />
+ <IncludeRules context="SL_Inc_Word" />
</context>
<context attribute="Comment" lineEndContext="#pop" name="SL_TagWordWord">
<DetectSpaces />
- <RegExpr attribute="Word" context="SL_Tag2ndWord" String="\S(?=&wordsep;)" />
- <RegExpr attribute="Word" context="#stay" String="\S" />
- </context>
- <!-- TODO for kate 2.x, convert references to "#pop!SL_TagWord" -->
- <context attribute="Comment" lineEndContext="#pop#pop" name="SL_Tag2ndWord">
- <DetectSpaces />
- <RegExpr attribute="Word" context="#pop#pop" String="\S(?=&wordsep;)" />
- <RegExpr attribute="Word" context="#stay" String="\S" />
+ <RegExpr attribute="Word" context="#pop!SL_TagWord" String="&sl_word;" />
</context>
<context attribute="Comment" lineEndContext="#pop" name="SL_TagString">
<DetectSpaces />
<StringDetect attribute="HTML Comment" context="SL_htmlcomment" String="&lt;!--" />
<Detect2Chars attribute="Comment" context="#stay" char="&lt;" char1="&lt;" />
<RegExpr attribute="HTML Tag" context="SL_htmltag" String="&lt;\/?[-\w0-9._:@]+" />
- <RegExpr attribute="Description" context="#stay" String="." />
+ <RegExpr attribute="Description" context="#stay" String="&lt;?[^&lt;\s]+" />
</context>
<context attribute="Comment" lineEndContext="#pop" name="SL_TagWordString">
<DetectSpaces />
- <RegExpr attribute="Word" context="#pop" String="\S(?=&wordsep;)" />
- <RegExpr attribute="Word" context="#stay" String="\S" />
+ <IncludeRules context="SL_Inc_Word" />
+ </context>
+ <context name="SL_Inc_Word" attribute="Word" lineEndContext="#pop">
+ <RegExpr attribute="Word" context="#pop" String="&sl_word;" />
</context>
<!-- html contexts -->
<context name="SL_htmltag" attribute="Identifier" lineEndContext="#pop">
<Detect2Chars attribute="HTML Tag" context="#pop" char="/" char1="&gt;" />
<DetectChar attribute="HTML Tag" context="#pop" char="&gt;" />
- <RegExpr attribute="Identifier" context="SL_identifiers" String="\s*=\s*" />
+ <DetectSpaces />
+ <DetectChar attribute="Identifier" context="SL_identifiers" char="=" />
</context>
<context name="SL_htmlcomment" attribute="HTML Comment" lineEndContext="#pop">
- <IncludeRules context="##Alerts" />
+ <DetectSpaces />
+ <IncludeRules context="##Comments" />
+ <DetectIdentifier />
<StringDetect attribute="HTML Comment" context="#pop" String="--&gt;" />
</context>
<context name="SL_identifiers" attribute="Identifier" lineEndContext="#pop">
- <RegExpr attribute="Identifier" context="#pop" String="\s*#?[a-zA-Z0-9]*" />
+ <DetectSpaces />
<DetectChar attribute="Types" context="SL_types1" char="'" />
<DetectChar attribute="Types" context="SL_types2" char="&quot;" />
</context>
@@ -411,55 +512,93 @@
<DetectChar attribute="Types" context="#pop#pop" char="&quot;" />
</context>
- <context attribute="Comment" name="SL_DetectEnv" lineEndContext="#pop">
- <RegExpr attribute="Tags" context="Code" String="[@\\]code\b" beginRegion="Code" />
- <RegExpr attribute="Tags" context="Verbatim" String="[@\\]verbatim\b" beginRegion="Verbatim" />
- <RegExpr attribute="Tags" context="Formula" String="[@\\]f\[" beginRegion="Formula" />
- <RegExpr attribute="Tags" context="Msc" String="[@\\]msc\b" beginRegion="Msc" />
- <RegExpr attribute="Tags" context="Dot" String="[@\\]dot\b" beginRegion="Dot" />
- <keyword attribute="Note" context="#stay" String="Note" />
- <keyword attribute="Warning" context="#stay" String="Warning" />
- <keyword attribute="Attention" context="#stay" String="Attention" />
- <keyword attribute="Todo" context="#stay" String="Todo" />
- <RegExpr attribute="Entities" context="#stay" String="&amp;[A-Za-z]+&#59;" />
+ <context name="SL_FindNextLine" attribute="Normal Text" lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop#pop#pop">
+ <DetectSpaces />
+ <StringDetect attribute="Comment" context="#pop" String="///" />
+ <StringDetect attribute="Comment" context="#pop" String="//!" />
+ <RegExpr attribute="Comment" context="#pop#pop#pop" String="." lookAhead="true" endRegion="VerbatimBlock" />
</context>
- <context attribute="Comment" name="SL_DetectComment" lineEndContext="#pop">
- <Detect2Chars attribute="Comment" context="#pop#pop" char="*" char1="/" endRegion="BlockComment" />
- <DetectChar attribute="Comment" context="#stay" char="*" />
- <StringDetect attribute="Comment" context="#stay" String="///" />
+ <context name="SL_Code" attribute="Code" lineEndContext="SL_FindNextLine">
+ <WordDetect attribute="Tags" context="#pop" String="\endcode" endRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop" String="@endcode" endRegion="VerbatimBlock" />
</context>
- <context attribute="Code" lineEndContext="#stay" name="Code">
- <IncludeRules context="SL_DetectComment" />
- <RegExpr attribute="Tags" context="#pop" String="[@\\]endcode\b" endRegion="Code" />
+ <context name="SL_Verbatim" attribute="Verbatim" lineEndContext="SL_FindNextLine">
+ <WordDetect attribute="Tags" context="#pop" String="\endverbatim" endRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop" String="@endverbatim" endRegion="VerbatimBlock" />
</context>
- <context attribute="Verbatim" lineEndContext="#stay" name="Verbatim">
- <IncludeRules context="SL_DetectComment" />
- <RegExpr attribute="Tags" context="#pop" String="[@\\]endverbatim\b" endRegion="Verbatim" />
+ <context name="SL_Formula" attribute="Formulas" lineEndContext="SL_FindNextLine">
+ <StringDetect attribute="Tags" context="#pop" String="\f]" endRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop" String="@f]" endRegion="VerbatimBlock" />
+ <!-- TODO: How to force LaTeX math context here?? -->
+ <!-- <IncludeRules context="##LaTeX" /> -->
</context>
- <context attribute="Formulas" lineEndContext="#stay" name="Formula">
- <IncludeRules context="SL_DetectComment" />
- <RegExpr attribute="Tags" context="#pop" String="[@\\]f\]" endRegion="Formula" />
+ <context name="SL_FormulaShort" attribute="Formulas" lineEndContext="SL_FindNextLine">
+ <StringDetect attribute="Tags" context="#pop" String="\f$" endRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop" String="@f$" endRegion="VerbatimBlock" />
<!-- TODO: How to force LaTeX math context here?? -->
<!-- <IncludeRules context="##LaTeX" /> -->
</context>
- <context attribute="Message Sequence Chart" lineEndContext="#stay" name="Msc">
- <IncludeRules context="SL_DetectComment" />
- <RegExpr attribute="Tags" context="#pop" String="[@\\]endmsc\b" endRegion="Msc" />
+ <context name="SL_FormulaEnv" attribute="Word" lineEndContext="SL_FindNextLine">
+ <DetectChar attribute="Tags" context="#pop!SL_FormulaEnvStart" char="}" />
</context>
- <context attribute="Dot Graph" lineEndContext="#stay" name="Dot">
- <IncludeRules context="SL_DetectComment" />
- <RegExpr attribute="Tags" context="#pop" String="[@\\]enddot\b" endRegion="Dot" />
+ <context name="SL_FormulaEnvStart" attribute="Formulas" lineEndContext="SL_FindNextLine" fallthrough="true" fallthroughContext="#pop!SL_FormulaEnvFormula">
+ <DetectSpaces attribute="Comment" />
+ <DetectChar attribute="Tags" context="#pop!SL_FormulaEnvFormula" char="{" />
+ </context>
+
+ <context name="SL_FormulaEnvFormula" attribute="Formulas" lineEndContext="SL_FindNextLine">
+ <StringDetect attribute="Tags" context="#pop" String="\f}" endRegion="VerbatimBlock" />
+ <StringDetect attribute="Tags" context="#pop" String="@f}" endRegion="VerbatimBlock" />
+ <!-- TODO: How to force LaTeX math context here?? -->
+ <!-- <IncludeRules context="##LaTeX" /> -->
+ </context>
+
+ <context name="SL_Msc" attribute="Message Sequence Chart" lineEndContext="SL_FindNextLine">
+ <WordDetect attribute="Tags" context="#pop" String="\endmsc" endRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop" String="@endmsc" endRegion="VerbatimBlock" />
+ </context>
+
+ <context name="SL_Dot" attribute="Dot Graph" lineEndContext="SL_FindNextLine">
+ <WordDetect attribute="Tags" context="#pop" String="\enddot" endRegion="VerbatimBlock" />
+ <WordDetect attribute="Tags" context="#pop" String="@enddot" endRegion="VerbatimBlock" />
+ </context>
+
+ <context name="Entities" attribute="Word" lineEndContext="#stay">
+ <RegExpr attribute="Entities" context="#pop" String="&amp;[A-Za-z]+&#59;" />
+ <DetectChar attribute="Comment" context="#pop" char="&amp;" />
+ </context>
+
+ <context name="Inc_OtherTag" attribute="Comment" lineEndContext="#pop">
+ <keyword attribute="Note" context="#pop" String="Note" />
+ <keyword attribute="Warning" context="#pop" String="Warning" />
+ <keyword attribute="Attention" context="#pop" String="Attention" />
+ <keyword attribute="Todo" context="#pop" String="Todo" />
+ <keyword attribute="Tags" context="#pop" String="TagOnly" />
+ <Detect2Chars attribute="Region" context="#pop" char="@" char1="{" beginRegion="Group" />
+ <Detect2Chars attribute="Region" context="#pop" char="@" char1="}" endRegion="Group" />
+ <Detect2Chars attribute="Tags" context="#pop!LanguageId" char="\" char1="~" />
+ <Detect2Chars attribute="Tags" context="#pop!LanguageId" char="@" char1="~" />
+ <keyword attribute="Error" context="#pop" String="TagEnd" />
+ <RegExpr attribute="Error" context="#pop" String="[@\\]f[]}]" />
+ <RegExpr attribute="Escape sequence" context="#pop" String="[@\\](?:[#$%&amp;&lt;&gt;&quot;@\\.~=|]|::|---?)" />
+ <RegExpr attribute="Custom Tags" context="#pop" String="[@\\](?:[^@\\ \t\*]|\*(?!/))+" />
+ <AnyChar attribute="Comment" context="#pop" String="\@" />
+ </context>
+
+ <context name="LanguageId" attribute="Word" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
+ <DetectIdentifier />
</context>
</contexts>
<itemDatas>
<itemData name="Normal Text" defStyleNum="dsNormal" />
+ <itemData name="Escape sequence" defStyleNum="dsSpecialChar" />
<itemData name="Tags" defStyleNum="dsAnnotation" bold="1" />
<itemData name="Custom Tags" defStyleNum="dsAnnotation" />
<itemData name="Word" defStyleNum="dsCommentVar" bold="1" italic="0" />
@@ -480,10 +619,11 @@
<itemData name="Warning" defStyleNum="dsWarning" bold="1" italic="0" />
<itemData name="Attention" defStyleNum="dsAnnotation" bold="1" italic="0" />
<itemData name="Todo" defStyleNum="dsAnnotation" bold="1" italic="0" />
+ <itemData name="Error" defStyleNum="dsError" />
</itemDatas>
</highlighting>
<general>
- <keywords casesensitive="1" weakDeliminator="\$~" />
+ <keywords casesensitive="1" weakDeliminator="\$" />
</general>
</language>
<!-- kate: indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/dtd.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/dtd.xml
index 5c39c1b597..0526efc625 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/dtd.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/dtd.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE language SYSTEM "language.dtd"[
+<!DOCTYPE language[
<!ENTITY nmtoken "[\-\w\d\.:_]+">
<!ENTITY entref "(#[0-9]+|#[xX][0-9A-Fa-f]+|&nmtoken;);">
]>
-<language name="DTD" version="2" kateversion="3.4" section="Markup" extensions="*.dtd" mimetype="application/xml-dtd" author="Andriy Lesyuk (s-andy@in.if.ua)" license="LGPL">
+<language name="DTD" version="4" kateversion="5.0" section="Markup" extensions="*.dtd" mimetype="application/xml-dtd" author="Andriy Lesyuk (s-andy@in.if.ua)" license="LGPL">
<highlighting>
<list name="Category">
@@ -45,7 +45,7 @@
<context name="Comment" attribute="Comment" lineEndContext="#stay">
<DetectSpaces />
<StringDetect attribute="Comment" context="#pop" String="--&gt;" endRegion="comment" />
- <IncludeRules context="##Alerts" />
+ <IncludeRules context="##Comments" />
<DetectIdentifier />
</context>
@@ -77,7 +77,7 @@
<context name="InlineComment" attribute="Comment" lineEndContext="#pop">
<DetectSpaces />
<Detect2Chars attribute="Comment" context="#pop" char="-" char1="-" />
- <IncludeRules context="##Alerts" />
+ <IncludeRules context="##Comments" />
<DetectIdentifier />
</context>
@@ -101,10 +101,9 @@
<general>
<comments>
- <comment name="multiLine" start="&lt;!--" end="--&gt;" />
+ <comment name="multiLine" start="&lt;!--" end="--&gt;" region="comment" />
</comments>
</general>
</language>
-<!-- kate: indent-width 2; indent-mode normal; tab-indents on; -->
-
+<!-- kate: replace-tabs on; tab-width 2; indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/gnuassembler.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/gnuassembler.xml
new file mode 100644
index 0000000000..26a306c28a
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/gnuassembler.xml
@@ -0,0 +1,2352 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language>
+<!--
+
+*************************************************************************
+* *
+* Syntax highlighting for the GNU Assembler *
+* Copyright (C) 2002, John Zaitseff *
+* Copyright (C) 2021, Waqar Ahmed *
+* *
+*************************************************************************
+
+Updated: Waqar Ahmed <waqar.17a@gmail.com>
+Date: 30 April, 2021
+Version: 2.0
+
+Updated: Miquel Sabaté <mikisabate@gmail.com>
+Date: 14th September, 2010
+Version: 1.02
+
+Updated: Roland Pabel <roland@pabel.name>
+Date: 15th August, 2002
+Version: 1.01
+
+Author: John Zaitseff <J.Zaitseff@zap.org.au>
+Date: 15th April, 2002
+Version: 1.0
+
+This file contains the XML syntax highlighting description for the GNU
+Assembler, for KATE, the KDE Advanced Editor. Keywords have been taken
+directly from the GNU Assembler source code (read.c).
+
+Known problems: Floating point highlighting does not work correctly.
+
+This program, including associated files, is free software. You may
+distribute 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 (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.
+
+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
+
+-->
+
+<language name="GNU Assembler" version="9" kateversion="5.0" section="Assembler" extensions="*.s;*.S" mimetype="text/x-asm" author="John Zaitseff (J.Zaitseff@zap.org.au), Roland Pabel (roland@pabel.name), Miquel Sabaté (mikisabate@gmail.com) Waqar Ahmed (waqar.17a@gmail.com)" license="GPLv2+">
+ <highlighting>
+ <list name="registers">
+ <!-- General purpose registers -->
+ <item>rax</item>
+ <item>eax</item>
+ <item>ax</item>
+ <item>ah</item>
+ <item>al</item>
+ <item>rbx</item>
+ <item>ebx</item>
+ <item>bx</item>
+ <item>bh</item>
+ <item>bl</item>
+ <item>rcx</item>
+ <item>ecx</item>
+ <item>cx</item>
+ <item>ch</item>
+ <item>cl</item>
+ <item>rdx</item>
+ <item>edx</item>
+ <item>dx</item>
+ <item>dh</item>
+ <item>dl</item>
+ <item>rbp</item>
+ <item>ebp</item>
+ <item>bp</item>
+ <item>bpl</item>
+ <item>rsi</item>
+ <item>esi</item>
+ <item>si</item>
+ <item>sil</item>
+ <item>rdi</item>
+ <item>edi</item>
+ <item>di</item>
+ <item>dil</item>
+
+ <item>rip</item>
+ <item>eip</item>
+ <item>ip</item>
+
+ <item>rsp</item>
+ <item>esp</item>
+ <item>sp</item>
+ <item>spl</item>
+ <item>r8</item>
+ <item>r8d</item>
+ <item>r8w</item>
+ <item>r8b</item>
+ <item>r9</item>
+ <item>r9d</item>
+ <item>r9w</item>
+ <item>r9b</item>
+ <item>r10</item>
+ <item>r10d</item>
+ <item>r10w</item>
+ <item>r10b</item>
+ <item>r11</item>
+ <item>r11d</item>
+ <item>r11w</item>
+ <item>r11b</item>
+ <item>r12</item>
+ <item>r12d</item>
+ <item>r12w</item>
+ <item>r12b</item>
+ <item>r13</item>
+ <item>r13d</item>
+ <item>r13w</item>
+ <item>r13b</item>
+ <item>r14</item>
+ <item>r14d</item>
+ <item>r14w</item>
+ <item>r14b</item>
+ <item>r15</item>
+ <item>r15d</item>
+ <item>r15w</item>
+ <item>r15b</item>
+ <!-- Segmentation registers -->
+ <item>cs</item>
+ <item>ds</item>
+ <item>es</item>
+ <item>fs</item>
+ <item>gs</item>
+ <item>ss</item>
+ <!-- Control registers -->
+ <item>cr0</item>
+ <!--<item>cr1</item>-->
+ <item>cr2</item>
+ <item>cr3</item>
+ <item>cr4</item>
+ <!-- Debug registers -->
+ <item>dr0</item>
+ <item>dr1</item>
+ <item>dr2</item>
+ <item>dr3</item>
+ <item>dr6</item>
+ <item>dr7</item>
+ <!-- x87 FPU Registers -->
+ <item>st</item>
+ <!-- MMX registers -->
+ <item>mm0</item>
+ <item>mm1</item>
+ <item>mm2</item>
+ <item>mm3</item>
+ <item>mm4</item>
+ <item>mm5</item>
+ <item>mm6</item>
+ <item>mm7</item>
+ <!-- XMM registers -->
+ <item>xmm0</item>
+ <item>xmm1</item>
+ <item>xmm2</item>
+ <item>xmm3</item>
+ <item>xmm4</item>
+ <item>xmm5</item>
+ <item>xmm6</item>
+ <item>xmm7</item>
+ <item>xmm8</item>
+ <item>xmm9</item>
+ <item>xmm10</item>
+ <item>xmm11</item>
+ <item>xmm12</item>
+ <item>xmm13</item>
+ <item>xmm14</item>
+ <item>xmm15</item>
+ <item>xmm16</item>
+ <item>xmm17</item>
+ <item>xmm18</item>
+ <item>xmm19</item>
+ <item>xmm20</item>
+ <item>xmm21</item>
+ <item>xmm22</item>
+ <item>xmm23</item>
+ <item>xmm24</item>
+ <item>xmm25</item>
+ <item>xmm26</item>
+ <item>xmm27</item>
+ <item>xmm28</item>
+ <item>xmm29</item>
+ <item>xmm30</item>
+ <item>xmm31</item>
+ <!-- YMM registers -->
+ <item>ymm0</item>
+ <item>ymm1</item>
+ <item>ymm2</item>
+ <item>ymm3</item>
+ <item>ymm4</item>
+ <item>ymm5</item>
+ <item>ymm6</item>
+ <item>ymm7</item>
+ <item>ymm8</item>
+ <item>ymm9</item>
+ <item>ymm10</item>
+ <item>ymm11</item>
+ <item>ymm12</item>
+ <item>ymm13</item>
+ <item>ymm14</item>
+ <item>ymm15</item>
+ <item>ymm16</item>
+ <item>ymm17</item>
+ <item>ymm18</item>
+ <item>ymm19</item>
+ <item>ymm20</item>
+ <item>ymm21</item>
+ <item>ymm22</item>
+ <item>ymm23</item>
+ <item>ymm24</item>
+ <item>ymm25</item>
+ <item>ymm26</item>
+ <item>ymm27</item>
+ <item>ymm28</item>
+ <item>ymm29</item>
+ <item>ymm30</item>
+ <item>ymm31</item>
+ <!-- ZMM registers -->
+ <item>zmm0</item>
+ <item>zmm1</item>
+ <item>zmm2</item>
+ <item>zmm3</item>
+ <item>zmm4</item>
+ <item>zmm5</item>
+ <item>zmm6</item>
+ <item>zmm7</item>
+ <item>zmm8</item>
+ <item>zmm9</item>
+ <item>zmm10</item>
+ <item>zmm11</item>
+ <item>zmm12</item>
+ <item>zmm13</item>
+ <item>zmm14</item>
+ <item>zmm15</item>
+ <item>zmm16</item>
+ <item>zmm17</item>
+ <item>zmm18</item>
+ <item>zmm19</item>
+ <item>zmm20</item>
+ <item>zmm21</item>
+ <item>zmm22</item>
+ <item>zmm23</item>
+ <item>zmm24</item>
+ <item>zmm25</item>
+ <item>zmm26</item>
+ <item>zmm27</item>
+ <item>zmm28</item>
+ <item>zmm29</item>
+ <item>zmm30</item>
+ <item>zmm31</item>
+ </list>
+
+ <list name="instructions">
+ <item>lock</item>
+ <item>rep</item>
+ <item>repe</item>
+ <item>repz</item>
+ <item>repne</item>
+ <item>repnz</item>
+ <item>xaquire</item>
+ <item>xrelease</item>
+ <item>bnd</item>
+ <item>nobnd</item>
+ <item>aaa</item>
+ <item>aad</item>
+ <item>aam</item>
+ <item>aas</item>
+ <item>adc</item>
+ <item>addb</item>
+ <item>addw</item>
+ <item>addl</item>
+ <item>addq</item>
+ <item>and</item>
+ <item>arpl</item>
+ <item>bb0_reset</item>
+ <item>bb1_reset</item>
+ <item>bound</item>
+ <item>bsf</item>
+ <item>bsr</item>
+ <item>bswap</item>
+ <item>bt</item>
+ <item>btc</item>
+ <item>btr</item>
+ <item>bts</item>
+ <item>cbw</item>
+ <item>cdq</item>
+ <item>cdqe</item>
+ <item>clc</item>
+ <item>cld</item>
+ <item>cli</item>
+ <item>clts</item>
+ <item>cltd</item>
+ <item>cbtw</item>
+ <item>cwtl</item>
+ <item>cwtd</item>
+ <item>cltq</item>
+ <item>cqto</item>
+ <item>cmc</item>
+ <item>cmpb</item>
+ <item>cmpw</item>
+ <item>cmpl</item>
+ <item>cmpq</item>
+ <item>cmpsb</item>
+ <item>cmpsd</item>
+ <item>cmpsq</item>
+ <item>cmpsw</item>
+ <item>cmpxchg</item>
+ <item>cmpxchg486</item>
+ <item>cmpxchg8b</item>
+ <item>cmpxchg16b</item>
+ <item>cpuid</item>
+ <item>cpu_read</item>
+ <item>cpu_write</item>
+ <item>cqo</item>
+ <item>cwd</item>
+ <item>cwde</item>
+ <item>daa</item>
+ <item>das</item>
+ <item>dec</item>
+ <item>decl</item>
+ <item>decq</item>
+ <item>div</item>
+ <item>divb</item>
+ <item>divw</item>
+ <item>divl</item>
+ <item>divq</item>
+ <item>dmint</item>
+ <item>emms</item>
+ <item>enter</item>
+ <item>equ</item>
+ <item>f2xm1</item>
+ <item>fabs</item>
+ <item>fadd</item>
+ <item>faddp</item>
+ <item>fbld</item>
+ <item>fbstp</item>
+ <item>fchs</item>
+ <item>fclex</item>
+ <item>fcmovb</item>
+ <item>fcmovbe</item>
+ <item>fcmove</item>
+ <item>fcmovnb</item>
+ <item>fcmovnbe</item>
+ <item>fcmovne</item>
+ <item>fcmovnu</item>
+ <item>fcmovu</item>
+ <item>fcom</item>
+ <item>fcomi</item>
+ <item>fcomip</item>
+ <item>fcomp</item>
+ <item>fcompp</item>
+ <item>fcos</item>
+ <item>fdecstp</item>
+ <item>fdisi</item>
+ <item>fdiv</item>
+ <item>fdivp</item>
+ <item>fdivr</item>
+ <item>fdivrp</item>
+ <item>femms</item>
+ <item>feni</item>
+ <item>ffree</item>
+ <item>ffreep</item>
+ <item>fiadd</item>
+ <item>ficom</item>
+ <item>ficomp</item>
+ <item>fidiv</item>
+ <item>fidivr</item>
+ <item>fild</item>
+ <item>fimul</item>
+ <item>fincstp</item>
+ <item>finit</item>
+ <item>fist</item>
+ <item>fistp</item>
+ <item>fisttp</item>
+ <item>fisub</item>
+ <item>fisubr</item>
+ <item>fld</item>
+ <item>fld1</item>
+ <item>fldcw</item>
+ <item>fldenv</item>
+ <item>fldl2e</item>
+ <item>fldl2t</item>
+ <item>fldlg2</item>
+ <item>fldln2</item>
+ <item>fldpi</item>
+ <item>fldz</item>
+ <item>fmul</item>
+ <item>fmulp</item>
+ <item>fnclex</item>
+ <item>fndisi</item>
+ <item>fneni</item>
+ <item>fninit</item>
+ <item>fnop</item>
+ <item>fnsave</item>
+ <item>fnstcw</item>
+ <item>fnstenv</item>
+ <item>fnstsw</item>
+ <item>fpatan</item>
+ <item>fprem</item>
+ <item>fprem1</item>
+ <item>fptan</item>
+ <item>frndint</item>
+ <item>frstor</item>
+ <item>fsave</item>
+ <item>fscale</item>
+ <item>fsetpm</item>
+ <item>fsin</item>
+ <item>fsincos</item>
+ <item>fsqrt</item>
+ <item>fst</item>
+ <item>fstcw</item>
+ <item>fstenv</item>
+ <item>fstp</item>
+ <item>fstsw</item>
+ <item>fsub</item>
+ <item>fsubp</item>
+ <item>fsubr</item>
+ <item>fsubrp</item>
+ <item>ftst</item>
+ <item>fucom</item>
+ <item>fucomi</item>
+ <item>fucomip</item>
+ <item>fucomp</item>
+ <item>fucompp</item>
+ <item>fxam</item>
+ <item>fxch</item>
+ <item>fxtract</item>
+ <item>fyl2x</item>
+ <item>fyl2xp1</item>
+ <item>global</item>
+ <item>globl</item>
+ <item>hlt</item>
+ <item>ibts</item>
+ <item>icebp</item>
+ <item>idiv</item>
+ <item>idivb</item>
+ <item>idivw</item>
+ <item>idivl</item>
+ <item>idivq</item>
+ <item>imul</item>
+ <item>imulb</item>
+ <item>imulw</item>
+ <item>imull</item>
+ <item>imulq</item>
+ <item>in</item>
+ <item>inc</item>
+ <item>incl</item>
+ <item>incq</item>
+ <item>incbin</item>
+ <item>insb</item>
+ <item>insd</item>
+ <item>insw</item>
+ <item>int</item>
+ <item>int01</item>
+ <item>int1</item>
+ <item>int03</item>
+ <item>int3</item>
+ <item>into</item>
+ <item>invd</item>
+ <item>invpcid</item>
+ <item>invlpg</item>
+ <item>invlpga</item>
+ <item>iret</item>
+ <item>iretd</item>
+ <item>iretq</item>
+ <item>iretw</item>
+ <item>jcxz</item>
+ <item>jecxz</item>
+ <item>jrcxz</item>
+ <item>lahf</item>
+ <item>lar</item>
+ <item>lds</item>
+ <item>leal</item>
+ <item>leaq</item>
+ <item>leave</item>
+ <item>les</item>
+ <item>lfence</item>
+ <item>lfs</item>
+ <item>lgdt</item>
+ <item>lgs</item>
+ <item>lidt</item>
+ <item>lldt</item>
+ <item>lmsw</item>
+ <item>loadall</item>
+ <item>loadall286</item>
+ <item>lodsb</item>
+ <item>lodsd</item>
+ <item>lodsq</item>
+ <item>lodsw</item>
+ <item>loop</item>
+ <item>loope</item>
+ <item>loopne</item>
+ <item>loopnz</item>
+ <item>loopz</item>
+ <item>lsl</item>
+ <item>lss</item>
+ <item>ltr</item>
+ <item>mfence</item>
+ <item>monitor</item>
+ <item>movb</item>
+ <item>movw</item>
+ <item>movl</item>
+ <item>movq</item>
+ <item>movsb</item>
+ <item>movsd</item>
+ <item>movsq</item>
+ <item>movsw</item>
+ <item>movsx</item>
+ <item>movsxd</item>
+ <item>movzx</item>
+ <item>movsbw</item>
+ <item>movsxb</item>
+ <item>movsbl</item>
+ <item>movsbq</item>
+ <item>movswl</item>
+ <item>movsxw</item>
+ <item>movswq</item>
+ <item>movslq</item>
+ <item>movsxl</item>
+ <item>movzbw</item>
+ <item>movzxb</item>
+ <item>movzbl</item>
+ <item>movzbq</item>
+ <item>movzwl</item>
+ <item>movzxw</item>
+ <item>movzwq</item>
+ <item>mul</item>
+ <item>mulb</item>
+ <item>mulw</item>
+ <item>mull</item>
+ <item>mulq</item>
+ <item>mwait</item>
+ <item>neg</item>
+ <item>nop</item>
+ <item>not</item>
+ <item>or</item>
+ <item>out</item>
+ <item>outsb</item>
+ <item>outsd</item>
+ <item>outsw</item>
+ <item>packssdw</item>
+ <item>packsswb</item>
+ <item>packuswb</item>
+ <item>paddb</item>
+ <item>paddd</item>
+ <item>paddsb</item>
+ <item>paddsiw</item>
+ <item>paddsw</item>
+ <item>paddusb</item>
+ <item>paddusw</item>
+ <item>paddw</item>
+ <item>pand</item>
+ <item>pandn</item>
+ <item>pause</item>
+ <item>paveb</item>
+ <item>pavgusb</item>
+ <item>pcmpeqb</item>
+ <item>pcmpeqd</item>
+ <item>pcmpeqw</item>
+ <item>pcmpgtb</item>
+ <item>pcmpgtd</item>
+ <item>pcmpgtw</item>
+ <item>pdistib</item>
+ <item>pf2id</item>
+ <item>pfacc</item>
+ <item>pfadd</item>
+ <item>pfcmpeq</item>
+ <item>pfcmpge</item>
+ <item>pfcmpgt</item>
+ <item>pfmax</item>
+ <item>pfmin</item>
+ <item>pfmul</item>
+ <item>pfrcp</item>
+ <item>pfrcpit1</item>
+ <item>pfrcpit2</item>
+ <item>pfrsqit1</item>
+ <item>pfrsqrt</item>
+ <item>pfsub</item>
+ <item>pfsubr</item>
+ <item>pi2fd</item>
+ <item>pmachriw</item>
+ <item>pmaddwd</item>
+ <item>pmagw</item>
+ <item>pmulhriw</item>
+ <item>pmulhrwa</item>
+ <item>pmulhrwc</item>
+ <item>pmulhw</item>
+ <item>pmullw</item>
+ <item>pmvgezb</item>
+ <item>pmvlzb</item>
+ <item>pmvnzb</item>
+ <item>pmvzb</item>
+ <item>pop</item>
+ <item>popl</item>
+ <item>popq</item>
+ <item>popa</item>
+ <item>popad</item>
+ <item>popaw</item>
+ <item>popf</item>
+ <item>popfd</item>
+ <item>popfq</item>
+ <item>popfw</item>
+ <item>por</item>
+ <item>prefetch</item>
+ <item>prefetchw</item>
+ <item>pslld</item>
+ <item>psllq</item>
+ <item>psllw</item>
+ <item>psrad</item>
+ <item>psraw</item>
+ <item>psrld</item>
+ <item>psrlq</item>
+ <item>psrlw</item>
+ <item>psubb</item>
+ <item>psubd</item>
+ <item>psubsb</item>
+ <item>psubsiw</item>
+ <item>psubsw</item>
+ <item>psubusb</item>
+ <item>psubusw</item>
+ <item>psubw</item>
+ <item>punpckhbw</item>
+ <item>punpckhdq</item>
+ <item>punpckhwd</item>
+ <item>punpcklbw</item>
+ <item>punpckldq</item>
+ <item>punpcklwd</item>
+ <item>push</item>
+ <item>pusha</item>
+ <item>pushl</item>
+ <item>pushq</item>
+ <item>pushad</item>
+ <item>pushaw</item>
+ <item>pushf</item>
+ <item>pushfd</item>
+ <item>pushfq</item>
+ <item>pushfw</item>
+ <item>pxor</item>
+ <item>rcl</item>
+ <item>rcr</item>
+ <item>rdshr</item>
+ <item>rdmsr</item>
+ <item>rdpmc</item>
+ <item>rdtsc</item>
+ <item>rdtscp</item>
+ <item>ret</item>
+ <item>retf</item>
+ <item>retn</item>
+ <item>retq</item>
+ <item>rol</item>
+ <item>ror</item>
+ <item>rdm</item>
+ <item>rsdc</item>
+ <item>rsldt</item>
+ <item>rsm</item>
+ <item>rsts</item>
+ <item>sahf</item>
+ <item>sal</item>
+ <item>sall</item>
+ <item>salq</item>
+ <item>salc</item>
+ <item>sar</item>
+ <item>sarl</item>
+ <item>sarq</item>
+ <item>sbb</item>
+ <item>scasb</item>
+ <item>scasd</item>
+ <item>scasq</item>
+ <item>scasw</item>
+ <item>sfence</item>
+ <item>sgdt</item>
+ <item>shl</item>
+ <item>shll</item>
+ <item>shllq</item>
+ <item>shld</item>
+ <item>shr</item>
+ <item>shrd</item>
+ <item>sidt</item>
+ <item>sldt</item>
+ <item>skinit</item>
+ <item>smi</item>
+ <item>smint</item>
+ <item>smintold</item>
+ <item>smsw</item>
+ <item>stc</item>
+ <item>std</item>
+ <item>sti</item>
+ <item>stosb</item>
+ <item>stosd</item>
+ <item>stosq</item>
+ <item>stosw</item>
+ <item>str</item>
+ <item>sub</item>
+ <item>subw</item>
+ <item>subl</item>
+ <item>subq</item>
+ <item>svdc</item>
+ <item>svldt</item>
+ <item>svts</item>
+ <item>swapgs</item>
+ <item>syscall</item>
+ <item>sysenter</item>
+ <item>sysexit</item>
+ <item>sysret</item>
+ <item>test</item>
+ <item>testl</item>
+ <item>testq</item>
+ <item>ud0</item>
+ <item>ud1</item>
+ <item>ud2b</item>
+ <item>ud2</item>
+ <item>ud2a</item>
+ <item>umov</item>
+ <item>verr</item>
+ <item>verw</item>
+ <item>fwait</item>
+ <item>wbinvd</item>
+ <item>wrshr</item>
+ <item>wrmsr</item>
+ <item>xadd</item>
+ <item>xbts</item>
+ <item>xchg</item>
+ <item>xlatb</item>
+ <item>xlat</item>
+ <item>xor</item>
+ <item>xorl</item>
+ <item>xorq</item>
+ <item>cmove</item>
+ <item>cmovz</item>
+ <item>cmovne</item>
+ <item>cmovnz</item>
+ <item>cmova</item>
+ <item>cmovnbe</item>
+ <item>cmovae</item>
+ <item>cmovnb</item>
+ <item>cmovb</item>
+ <item>cmovnae</item>
+ <item>cmovbe</item>
+ <item>cmovna</item>
+ <item>cmovg</item>
+ <item>cmovnle</item>
+ <item>cmovge</item>
+ <item>cmovnl</item>
+ <item>cmovl</item>
+ <item>cmovnge</item>
+ <item>cmovle</item>
+ <item>cmovng</item>
+ <item>cmovc</item>
+ <item>cmovnc</item>
+ <item>cmovo</item>
+ <item>cmovno</item>
+ <item>cmovs</item>
+ <item>cmovns</item>
+ <item>cmovp</item>
+ <item>cmovpe</item>
+ <item>cmovnp</item>
+ <item>cmovpo</item>
+ <item>sete</item>
+ <item>setz</item>
+ <item>setne</item>
+ <item>setnz</item>
+ <item>seta</item>
+ <item>setnbe</item>
+ <item>setae</item>
+ <item>setnb</item>
+ <item>setnc</item>
+ <item>setb</item>
+ <item>setnae</item>
+ <item>setcset</item>
+ <item>setbe</item>
+ <item>setna</item>
+ <item>setg</item>
+ <item>setnle</item>
+ <item>setge</item>
+ <item>setnl</item>
+ <item>setl</item>
+ <item>setnge</item>
+ <item>setle</item>
+ <item>setng</item>
+ <item>sets</item>
+ <item>setns</item>
+ <item>seto</item>
+ <item>setno</item>
+ <item>setpe</item>
+ <item>setp</item>
+ <item>setpo</item>
+ <item>setnp</item>
+ <item>addps</item>
+ <item>addss</item>
+ <item>andnps</item>
+ <item>andps</item>
+ <item>cmpeqps</item>
+ <item>cmpeqss</item>
+ <item>cmpleps</item>
+ <item>cmpless</item>
+ <item>cmpltps</item>
+ <item>cmpltss</item>
+ <item>cmpneqps</item>
+ <item>cmpneqss</item>
+ <item>cmpnleps</item>
+ <item>cmpnless</item>
+ <item>cmpnltps</item>
+ <item>cmpnltss</item>
+ <item>cmpordps</item>
+ <item>cmpordss</item>
+ <item>cmpunordps</item>
+ <item>cmpunordss</item>
+ <item>cmpps</item>
+ <item>cmpss</item>
+ <item>comiss</item>
+ <item>cvtpi2ps</item>
+ <item>cvtps2pi</item>
+ <item>cvtsi2ss</item>
+ <item>cvtss2si</item>
+ <item>cvttps2pi</item>
+ <item>cvttss2si</item>
+ <item>divps</item>
+ <item>divss</item>
+ <item>ldmxcsr</item>
+ <item>maxps</item>
+ <item>maxss</item>
+ <item>minps</item>
+ <item>minss</item>
+ <item>movaps</item>
+ <item>movhps</item>
+ <item>movlhps</item>
+ <item>movlps</item>
+ <item>movhlps</item>
+ <item>movmskps</item>
+ <item>movntps</item>
+ <item>movss</item>
+ <item>movups</item>
+ <item>mulps</item>
+ <item>mulss</item>
+ <item>orps</item>
+ <item>rcpps</item>
+ <item>rcpss</item>
+ <item>rsqrtps</item>
+ <item>rsqrtss</item>
+ <item>shufps</item>
+ <item>sqrtps</item>
+ <item>sqrtss</item>
+ <item>stmxcsr</item>
+ <item>subps</item>
+ <item>subss</item>
+ <item>ucomiss</item>
+ <item>unpckhps</item>
+ <item>unpcklps</item>
+ <item>xorps</item>
+ <item>fxrstor</item>
+ <item>fxrstor64</item>
+ <item>fxsave</item>
+ <item>fxsave64</item>
+ <item>xgetbv</item>
+ <item>xsetbv</item>
+ <item>xsave</item>
+ <item>xsave64</item>
+ <item>xsaveopt</item>
+ <item>xsaveopt64</item>
+ <item>xrstor</item>
+ <item>xrstor64</item>
+ <item>prefetchnta</item>
+ <item>prefetcht0</item>
+ <item>prefetcht1</item>
+ <item>prefetcht2</item>
+ <item>maskmovq</item>
+ <item>movntq</item>
+ <item>pavgb</item>
+ <item>pavgw</item>
+ <item>pextrw</item>
+ <item>pinsrw</item>
+ <item>pmaxsw</item>
+ <item>pmaxub</item>
+ <item>pminsw</item>
+ <item>pminub</item>
+ <item>pmovmskb</item>
+ <item>pmulhuw</item>
+ <item>psadbw</item>
+ <item>pshufw</item>
+ <item>pf2iw</item>
+ <item>pfnacc</item>
+ <item>pfpnacc</item>
+ <item>pi2fw</item>
+ <item>pswapd</item>
+ <item>maskmovdqu</item>
+ <item>clflush</item>
+ <item>movntdq</item>
+ <item>movnti</item>
+ <item>movntpd</item>
+ <item>movdqa</item>
+ <item>movdqu</item>
+ <item>movdq2q</item>
+ <item>movq2dq</item>
+ <item>paddq</item>
+ <item>pmuludq</item>
+ <item>pshufd</item>
+ <item>pshufhw</item>
+ <item>pshuflw</item>
+ <item>pslldq</item>
+ <item>psrldq</item>
+ <item>psubq</item>
+ <item>punpckhqdq</item>
+ <item>punpcklqdq</item>
+ <item>addpd</item>
+ <item>addsd</item>
+ <item>andnpd</item>
+ <item>andpd</item>
+ <item>cmpeqpd</item>
+ <item>cmpeqsd</item>
+ <item>cmplepd</item>
+ <item>cmplesd</item>
+ <item>cmpltpd</item>
+ <item>cmpltsd</item>
+ <item>cmpneqpd</item>
+ <item>cmpneqsd</item>
+ <item>cmpnlepd</item>
+ <item>cmpnlesd</item>
+ <item>cmpnltpd</item>
+ <item>cmpnltsd</item>
+ <item>cmpordpd</item>
+ <item>cmpordsd</item>
+ <item>cmpunordpd</item>
+ <item>cmpunordsd</item>
+ <item>cmppd</item>
+ <item>comisd</item>
+ <item>cvtdq2pd</item>
+ <item>cvtdq2ps</item>
+ <item>cvtpd2dq</item>
+ <item>cvtpd2pi</item>
+ <item>cvtpd2ps</item>
+ <item>cvtpi2pd</item>
+ <item>cvtps2dq</item>
+ <item>cvtps2pd</item>
+ <item>cvtsd2si</item>
+ <item>cvtsd2ss</item>
+ <item>cvtsi2sd</item>
+ <item>cvtss2sd</item>
+ <item>cvttpd2pi</item>
+ <item>cvttpd2dq</item>
+ <item>cvttps2dq</item>
+ <item>cvttsd2si</item>
+ <item>divpd</item>
+ <item>divsd</item>
+ <item>maxpd</item>
+ <item>maxsd</item>
+ <item>minpd</item>
+ <item>minsd</item>
+ <item>movapd</item>
+ <item>movhpd</item>
+ <item>movlpd</item>
+ <item>movmskpd</item>
+ <item>movupd</item>
+ <item>mulpd</item>
+ <item>mulsd</item>
+ <item>orpd</item>
+ <item>shufpd</item>
+ <item>sqrtpd</item>
+ <item>sqrtsd</item>
+ <item>subpd</item>
+ <item>subsd</item>
+ <item>ucomisd</item>
+ <item>unpckhpd</item>
+ <item>unpcklpd</item>
+ <item>xorpd</item>
+ <item>addsubpd</item>
+ <item>addsubps</item>
+ <item>haddpd</item>
+ <item>haddps</item>
+ <item>hsubpd</item>
+ <item>hsubps</item>
+ <item>lddqu</item>
+ <item>movddup</item>
+ <item>movshdup</item>
+ <item>movsldup</item>
+ <item>clgi</item>
+ <item>stgi</item>
+ <item>vmcall</item>
+ <item>vmclear</item>
+ <item>vmfunc</item>
+ <item>vmlaunch</item>
+ <item>vmload</item>
+ <item>vmmcall</item>
+ <item>vmptrld</item>
+ <item>vmptrst</item>
+ <item>vmread</item>
+ <item>vmresume</item>
+ <item>vmrun</item>
+ <item>vmsave</item>
+ <item>vmwrite</item>
+ <item>vmxoff</item>
+ <item>vmxon</item>
+ <item>invept</item>
+ <item>invvpid</item>
+ <item>pabsb</item>
+ <item>pabsw</item>
+ <item>pabsd</item>
+ <item>palignr</item>
+ <item>phaddw</item>
+ <item>phaddd</item>
+ <item>phaddsw</item>
+ <item>phsubw</item>
+ <item>phsubd</item>
+ <item>phsubsw</item>
+ <item>pmaddubsw</item>
+ <item>pmulhrsw</item>
+ <item>pshufb</item>
+ <item>psignb</item>
+ <item>psignw</item>
+ <item>psignd</item>
+ <item>extrq</item>
+ <item>insertq</item>
+ <item>movntsd</item>
+ <item>movntss</item>
+ <item>lzcnt</item>
+ <item>blendpd</item>
+ <item>blendps</item>
+ <item>blendvpd</item>
+ <item>blendvps</item>
+ <item>dppd</item>
+ <item>dpps</item>
+ <item>extractps</item>
+ <item>insertps</item>
+ <item>movntdqa</item>
+ <item>mpsadbw</item>
+ <item>packusdw</item>
+ <item>pblendvb</item>
+ <item>pblendw</item>
+ <item>pcmpeqq</item>
+ <item>pextrb</item>
+ <item>pextrd</item>
+ <item>pextrq</item>
+ <item>phminposuw</item>
+ <item>pinsrb</item>
+ <item>pinsrd</item>
+ <item>pinsrq</item>
+ <item>pmaxsb</item>
+ <item>pmaxsd</item>
+ <item>pmaxud</item>
+ <item>pmaxuw</item>
+ <item>pminsb</item>
+ <item>pminsd</item>
+ <item>pminud</item>
+ <item>pminuw</item>
+ <item>pmovsxbw</item>
+ <item>pmovsxbd</item>
+ <item>pmovsxbq</item>
+ <item>pmovsxwd</item>
+ <item>pmovsxwq</item>
+ <item>pmovsxdq</item>
+ <item>pmovzxbw</item>
+ <item>pmovzxbd</item>
+ <item>pmovzxbq</item>
+ <item>pmovzxwd</item>
+ <item>pmovzxwq</item>
+ <item>pmovzxdq</item>
+ <item>pmuldq</item>
+ <item>pmulld</item>
+ <item>ptest</item>
+ <item>roundpd</item>
+ <item>roundps</item>
+ <item>roundsd</item>
+ <item>roundss</item>
+ <item>crc32</item>
+ <item>pcmpestri</item>
+ <item>pcmpestrm</item>
+ <item>pcmpistri</item>
+ <item>pcmpistrm</item>
+ <item>pcmpgtq</item>
+ <item>popcnt</item>
+ <item>getsec</item>
+ <item>pfrcpv</item>
+ <item>pfrsqrtv</item>
+ <item>movbe</item>
+ <item>aesenc</item>
+ <item>aesenclast</item>
+ <item>aesdec</item>
+ <item>aesdeclast</item>
+ <item>aesimc</item>
+ <item>aeskeygenassist</item>
+ <item>vaesenc</item>
+ <item>vaesenclast</item>
+ <item>vaesdec</item>
+ <item>vaesdeclast</item>
+ <item>vaesimc</item>
+ <item>vaeskeygenassist</item>
+ <item>vaddpd</item>
+ <item>vaddps</item>
+ <item>vaddsd</item>
+ <item>vaddss</item>
+ <item>vaddsubpd</item>
+ <item>vaddsubps</item>
+ <item>vandpd</item>
+ <item>vandps</item>
+ <item>vandnpd</item>
+ <item>vandnps</item>
+ <item>vblendpd</item>
+ <item>vblendps</item>
+ <item>vblendvpd</item>
+ <item>vblendvps</item>
+ <item>vbroadcastss</item>
+ <item>vbroadcastsd</item>
+ <item>vbroadcastf128</item>
+ <item>vcmpeq_ospd</item>
+ <item>vcmpeqpd</item>
+ <item>vcmplt_ospd</item>
+ <item>vcmpltpd</item>
+ <item>vcmple_ospd</item>
+ <item>vcmplepd</item>
+ <item>vcmpunord_qpd</item>
+ <item>vcmpunordpd</item>
+ <item>vcmpneq_uqpd</item>
+ <item>vcmpneqpd</item>
+ <item>vcmpnlt_uspd</item>
+ <item>vcmpnltpd</item>
+ <item>vcmpnle_uspd</item>
+ <item>vcmpnlepd</item>
+ <item>vcmpord_qpd</item>
+ <item>vcmpordpd</item>
+ <item>vcmpeq_uqpd</item>
+ <item>vcmpnge_uspd</item>
+ <item>vcmpngepd</item>
+ <item>vcmpngt_uspd</item>
+ <item>vcmpngtpd</item>
+ <item>vcmpfalse_oqpd</item>
+ <item>vcmpfalsepd</item>
+ <item>vcmpneq_oqpd</item>
+ <item>vcmpge_ospd</item>
+ <item>vcmpgepd</item>
+ <item>vcmpgt_ospd</item>
+ <item>vcmpgtpd</item>
+ <item>vcmptrue_uqpd</item>
+ <item>vcmptruepd</item>
+ <item>vcmplt_oqpd</item>
+ <item>vcmple_oqpd</item>
+ <item>vcmpunord_spd</item>
+ <item>vcmpneq_uspd</item>
+ <item>vcmpnlt_uqpd</item>
+ <item>vcmpnle_uqpd</item>
+ <item>vcmpord_spd</item>
+ <item>vcmpeq_uspd</item>
+ <item>vcmpnge_uqpd</item>
+ <item>vcmpngt_uqpd</item>
+ <item>vcmpfalse_ospd</item>
+ <item>vcmpneq_ospd</item>
+ <item>vcmpge_oqpd</item>
+ <item>vcmpgt_oqpd</item>
+ <item>vcmptrue_uspd</item>
+ <item>vcmppd</item>
+ <item>vcmpeq_osps</item>
+ <item>vcmpeqps</item>
+ <item>vcmplt_osps</item>
+ <item>vcmpltps</item>
+ <item>vcmple_osps</item>
+ <item>vcmpleps</item>
+ <item>vcmpunord_qps</item>
+ <item>vcmpunordps</item>
+ <item>vcmpneq_uqps</item>
+ <item>vcmpneqps</item>
+ <item>vcmpnlt_usps</item>
+ <item>vcmpnltps</item>
+ <item>vcmpnle_usps</item>
+ <item>vcmpnleps</item>
+ <item>vcmpord_qps</item>
+ <item>vcmpordps</item>
+ <item>vcmpeq_uqps</item>
+ <item>vcmpnge_usps</item>
+ <item>vcmpngeps</item>
+ <item>vcmpngt_usps</item>
+ <item>vcmpngtps</item>
+ <item>vcmpfalse_oqps</item>
+ <item>vcmpfalseps</item>
+ <item>vcmpneq_oqps</item>
+ <item>vcmpge_osps</item>
+ <item>vcmpgeps</item>
+ <item>vcmpgt_osps</item>
+ <item>vcmpgtps</item>
+ <item>vcmptrue_uqps</item>
+ <item>vcmptrueps</item>
+ <item>vcmplt_oqps</item>
+ <item>vcmple_oqps</item>
+ <item>vcmpunord_sps</item>
+ <item>vcmpneq_usps</item>
+ <item>vcmpnlt_uqps</item>
+ <item>vcmpnle_uqps</item>
+ <item>vcmpord_sps</item>
+ <item>vcmpeq_usps</item>
+ <item>vcmpnge_uqps</item>
+ <item>vcmpngt_uqps</item>
+ <item>vcmpfalse_osps</item>
+ <item>vcmpneq_osps</item>
+ <item>vcmpge_oqps</item>
+ <item>vcmpgt_oqps</item>
+ <item>vcmptrue_usps</item>
+ <item>vcmpps</item>
+ <item>vcmpeq_ossd</item>
+ <item>vcmpeqsd</item>
+ <item>vcmplt_ossd</item>
+ <item>vcmpltsd</item>
+ <item>vcmple_ossd</item>
+ <item>vcmplesd</item>
+ <item>vcmpunord_qsd</item>
+ <item>vcmpunordsd</item>
+ <item>vcmpneq_uqsd</item>
+ <item>vcmpneqsd</item>
+ <item>vcmpnlt_ussd</item>
+ <item>vcmpnltsd</item>
+ <item>vcmpnle_ussd</item>
+ <item>vcmpnlesd</item>
+ <item>vcmpord_qsd</item>
+ <item>vcmpordsd</item>
+ <item>vcmpeq_uqsd</item>
+ <item>vcmpnge_ussd</item>
+ <item>vcmpngesd</item>
+ <item>vcmpngt_ussd</item>
+ <item>vcmpngtsd</item>
+ <item>vcmpfalse_oqsd</item>
+ <item>vcmpfalsesd</item>
+ <item>vcmpneq_oqsd</item>
+ <item>vcmpge_ossd</item>
+ <item>vcmpgesd</item>
+ <item>vcmpgt_ossd</item>
+ <item>vcmpgtsd</item>
+ <item>vcmptrue_uqsd</item>
+ <item>vcmptruesd</item>
+ <item>vcmplt_oqsd</item>
+ <item>vcmple_oqsd</item>
+ <item>vcmpunord_ssd</item>
+ <item>vcmpneq_ussd</item>
+ <item>vcmpnlt_uqsd</item>
+ <item>vcmpnle_uqsd</item>
+ <item>vcmpord_ssd</item>
+ <item>vcmpeq_ussd</item>
+ <item>vcmpnge_uqsd</item>
+ <item>vcmpngt_uqsd</item>
+ <item>vcmpfalse_ossd</item>
+ <item>vcmpneq_ossd</item>
+ <item>vcmpge_oqsd</item>
+ <item>vcmpgt_oqsd</item>
+ <item>vcmptrue_ussd</item>
+ <item>vcmpsd</item>
+ <item>vcmpeq_osss</item>
+ <item>vcmpeqss</item>
+ <item>vcmplt_osss</item>
+ <item>vcmpltss</item>
+ <item>vcmple_osss</item>
+ <item>vcmpless</item>
+ <item>vcmpunord_qss</item>
+ <item>vcmpunordss</item>
+ <item>vcmpneq_uqss</item>
+ <item>vcmpneqss</item>
+ <item>vcmpnlt_usss</item>
+ <item>vcmpnltss</item>
+ <item>vcmpnle_usss</item>
+ <item>vcmpnless</item>
+ <item>vcmpord_qss</item>
+ <item>vcmpordss</item>
+ <item>vcmpeq_uqss</item>
+ <item>vcmpnge_usss</item>
+ <item>vcmpngess</item>
+ <item>vcmpngt_usss</item>
+ <item>vcmpngtss</item>
+ <item>vcmpfalse_oqss</item>
+ <item>vcmpfalsess</item>
+ <item>vcmpneq_oqss</item>
+ <item>vcmpge_osss</item>
+ <item>vcmpgess</item>
+ <item>vcmpgt_osss</item>
+ <item>vcmpgtss</item>
+ <item>vcmptrue_uqss</item>
+ <item>vcmptruess</item>
+ <item>vcmplt_oqss</item>
+ <item>vcmple_oqss</item>
+ <item>vcmpunord_sss</item>
+ <item>vcmpneq_usss</item>
+ <item>vcmpnlt_uqss</item>
+ <item>vcmpnle_uqss</item>
+ <item>vcmpord_sss</item>
+ <item>vcmpeq_usss</item>
+ <item>vcmpnge_uqss</item>
+ <item>vcmpngt_uqss</item>
+ <item>vcmpfalse_osss</item>
+ <item>vcmpneq_osss</item>
+ <item>vcmpge_oqss</item>
+ <item>vcmpgt_oqss</item>
+ <item>vcmptrue_usss</item>
+ <item>vcmpss</item>
+ <item>vcomisd</item>
+ <item>vcomiss</item>
+ <item>vcvtdq2pd</item>
+ <item>vcvtdq2ps</item>
+ <item>vcvtpd2dq</item>
+ <item>vcvtpd2ps</item>
+ <item>vcvtps2dq</item>
+ <item>vcvtps2pd</item>
+ <item>vcvtsd2si</item>
+ <item>vcvtsd2ss</item>
+ <item>vcvtsi2sd</item>
+ <item>vcvtsi2ss</item>
+ <item>vcvtss2sd</item>
+ <item>vcvtss2si</item>
+ <item>vcvttpd2dq</item>
+ <item>vcvttps2dq</item>
+ <item>vcvttsd2si</item>
+ <item>vcvttss2si</item>
+ <item>vdivpd</item>
+ <item>vdivps</item>
+ <item>vdivsd</item>
+ <item>vdivss</item>
+ <item>vdppd</item>
+ <item>vdpps</item>
+ <item>vextractf128</item>
+ <item>vextractps</item>
+ <item>vhaddpd</item>
+ <item>vhaddps</item>
+ <item>vhsubpd</item>
+ <item>vhsubps</item>
+ <item>vinsertf128</item>
+ <item>vinsertps</item>
+ <item>vlddqu</item>
+ <item>vldqqu</item>
+ <item>vldmxcsr</item>
+ <item>vmaskmovdqu</item>
+ <item>vmaskmovps</item>
+ <item>vmaskmovpd</item>
+ <item>vmaxpd</item>
+ <item>vmaxps</item>
+ <item>vmaxsd</item>
+ <item>vmaxss</item>
+ <item>vminpd</item>
+ <item>vminps</item>
+ <item>vminsd</item>
+ <item>vminss</item>
+ <item>vmovapd</item>
+ <item>vmovaps</item>
+ <item>vmovd</item>
+ <item>vmovq</item>
+ <item>vmovddup</item>
+ <item>vmovdqa</item>
+ <item>vmovqqa</item>
+ <item>vmovdqu</item>
+ <item>vmovqqu</item>
+ <item>vmovhlps</item>
+ <item>vmovhpd</item>
+ <item>vmovhps</item>
+ <item>vmovlhps</item>
+ <item>vmovlpd</item>
+ <item>vmovlps</item>
+ <item>vmovmskpd</item>
+ <item>vmovmskps</item>
+ <item>vmovntdq</item>
+ <item>vmovntqq</item>
+ <item>vmovntdqa</item>
+ <item>vmovntpd</item>
+ <item>vmovntps</item>
+ <item>vmovsd</item>
+ <item>vmovshdup</item>
+ <item>vmovsldup</item>
+ <item>vmovss</item>
+ <item>vmovupd</item>
+ <item>vmovups</item>
+ <item>vmpsadbw</item>
+ <item>vmulpd</item>
+ <item>vmulps</item>
+ <item>vmulsd</item>
+ <item>vmulss</item>
+ <item>vorpd</item>
+ <item>vorps</item>
+ <item>vpabsb</item>
+ <item>vpabsw</item>
+ <item>vpabsd</item>
+ <item>vpacksswb</item>
+ <item>vpackssdw</item>
+ <item>vpackuswb</item>
+ <item>vpackusdw</item>
+ <item>vpaddb</item>
+ <item>vpaddw</item>
+ <item>vpaddd</item>
+ <item>vpaddq</item>
+ <item>vpaddsb</item>
+ <item>vpaddsw</item>
+ <item>vpaddusb</item>
+ <item>vpaddusw</item>
+ <item>vpalignr</item>
+ <item>vpand</item>
+ <item>vpandn</item>
+ <item>vpavgb</item>
+ <item>vpavgw</item>
+ <item>vpblendvb</item>
+ <item>vpblendw</item>
+ <item>vpcmpestri</item>
+ <item>vpcmpestrm</item>
+ <item>vpcmpistri</item>
+ <item>vpcmpistrm</item>
+ <item>vpcmpeqb</item>
+ <item>vpcmpeqw</item>
+ <item>vpcmpeqd</item>
+ <item>vpcmpeqq</item>
+ <item>vpcmpgtb</item>
+ <item>vpcmpgtw</item>
+ <item>vpcmpgtd</item>
+ <item>vpcmpgtq</item>
+ <item>vpermilpd</item>
+ <item>vpermilps</item>
+ <item>vperm2f128</item>
+ <item>vpextrb</item>
+ <item>vpextrw</item>
+ <item>vpextrd</item>
+ <item>vpextrq</item>
+ <item>vphaddw</item>
+ <item>vphaddd</item>
+ <item>vphaddsw</item>
+ <item>vphminposuw</item>
+ <item>vphsubw</item>
+ <item>vphsubd</item>
+ <item>vphsubsw</item>
+ <item>vpinsrb</item>
+ <item>vpinsrw</item>
+ <item>vpinsrd</item>
+ <item>vpinsrq</item>
+ <item>vpmaddwd</item>
+ <item>vpmaddubsw</item>
+ <item>vpmaxsb</item>
+ <item>vpmaxsw</item>
+ <item>vpmaxsd</item>
+ <item>vpmaxub</item>
+ <item>vpmaxuw</item>
+ <item>vpmaxud</item>
+ <item>vpminsb</item>
+ <item>vpminsw</item>
+ <item>vpminsd</item>
+ <item>vpminub</item>
+ <item>vpminuw</item>
+ <item>vpminud</item>
+ <item>vpmovmskb</item>
+ <item>vpmovsxbw</item>
+ <item>vpmovsxbd</item>
+ <item>vpmovsxbq</item>
+ <item>vpmovsxwd</item>
+ <item>vpmovsxwq</item>
+ <item>vpmovsxdq</item>
+ <item>vpmovzxbw</item>
+ <item>vpmovzxbd</item>
+ <item>vpmovzxbq</item>
+ <item>vpmovzxwd</item>
+ <item>vpmovzxwq</item>
+ <item>vpmovzxdq</item>
+ <item>vpmulhuw</item>
+ <item>vpmulhrsw</item>
+ <item>vpmulhw</item>
+ <item>vpmullw</item>
+ <item>vpmulld</item>
+ <item>vpmuludq</item>
+ <item>vpmuldq</item>
+ <item>vpor</item>
+ <item>vpsadbw</item>
+ <item>vpshufb</item>
+ <item>vpshufd</item>
+ <item>vpshufhw</item>
+ <item>vpshuflw</item>
+ <item>vpsignb</item>
+ <item>vpsignw</item>
+ <item>vpsignd</item>
+ <item>vpslldq</item>
+ <item>vpsrldq</item>
+ <item>vpsllw</item>
+ <item>vpslld</item>
+ <item>vpsllq</item>
+ <item>vpsraw</item>
+ <item>vpsrad</item>
+ <item>vpsrlw</item>
+ <item>vpsrld</item>
+ <item>vpsrlq</item>
+ <item>vptest</item>
+ <item>vpsubb</item>
+ <item>vpsubw</item>
+ <item>vpsubd</item>
+ <item>vpsubq</item>
+ <item>vpsubsb</item>
+ <item>vpsubsw</item>
+ <item>vpsubusb</item>
+ <item>vpsubusw</item>
+ <item>vpunpckhbw</item>
+ <item>vpunpckhwd</item>
+ <item>vpunpckhdq</item>
+ <item>vpunpckhqdq</item>
+ <item>vpunpcklbw</item>
+ <item>vpunpcklwd</item>
+ <item>vpunpckldq</item>
+ <item>vpunpcklqdq</item>
+ <item>vpxor</item>
+ <item>vrcpps</item>
+ <item>vrcpss</item>
+ <item>vrsqrtps</item>
+ <item>vrsqrtss</item>
+ <item>vroundpd</item>
+ <item>vroundps</item>
+ <item>vroundsd</item>
+ <item>vroundss</item>
+ <item>vshufpd</item>
+ <item>vshufps</item>
+ <item>vsqrtpd</item>
+ <item>vsqrtps</item>
+ <item>vsqrtsd</item>
+ <item>vsqrtss</item>
+ <item>vstmxcsr</item>
+ <item>vsubpd</item>
+ <item>vsubps</item>
+ <item>vsubsd</item>
+ <item>vsubss</item>
+ <item>vtestps</item>
+ <item>vtestpd</item>
+ <item>vucomisd</item>
+ <item>vucomiss</item>
+ <item>vunpckhpd</item>
+ <item>vunpckhps</item>
+ <item>vunpcklpd</item>
+ <item>vunpcklps</item>
+ <item>vxorpd</item>
+ <item>vxorps</item>
+ <item>vzeroall</item>
+ <item>vzeroupper</item>
+ <item>pclmullqlqdq</item>
+ <item>pclmulhqlqdq</item>
+ <item>pclmullqhqdq</item>
+ <item>pclmulhqhqdq</item>
+ <item>pclmulqdq</item>
+ <item>vpclmullqlqdq</item>
+ <item>vpclmulhqlqdq</item>
+ <item>vpclmullqhqdq</item>
+ <item>vpclmulhqhqdq</item>
+ <item>vpclmulqdq</item>
+ <item>vfmadd132ps</item>
+ <item>vfmadd132pd</item>
+ <item>vfmadd312ps</item>
+ <item>vfmadd312pd</item>
+ <item>vfmadd213ps</item>
+ <item>vfmadd213pd</item>
+ <item>vfmadd123ps</item>
+ <item>vfmadd123pd</item>
+ <item>vfmadd231ps</item>
+ <item>vfmadd231pd</item>
+ <item>vfmadd321ps</item>
+ <item>vfmadd321pd</item>
+ <item>vfmaddsub132ps</item>
+ <item>vfmaddsub132pd</item>
+ <item>vfmaddsub312ps</item>
+ <item>vfmaddsub312pd</item>
+ <item>vfmaddsub213ps</item>
+ <item>vfmaddsub213pd</item>
+ <item>vfmaddsub123ps</item>
+ <item>vfmaddsub123pd</item>
+ <item>vfmaddsub231ps</item>
+ <item>vfmaddsub231pd</item>
+ <item>vfmaddsub321ps</item>
+ <item>vfmaddsub321pd</item>
+ <item>vfmsub132ps</item>
+ <item>vfmsub132pd</item>
+ <item>vfmsub312ps</item>
+ <item>vfmsub312pd</item>
+ <item>vfmsub213ps</item>
+ <item>vfmsub213pd</item>
+ <item>vfmsub123ps</item>
+ <item>vfmsub123pd</item>
+ <item>vfmsub231ps</item>
+ <item>vfmsub231pd</item>
+ <item>vfmsub321ps</item>
+ <item>vfmsub321pd</item>
+ <item>vfmsubadd132ps</item>
+ <item>vfmsubadd132pd</item>
+ <item>vfmsubadd312ps</item>
+ <item>vfmsubadd312pd</item>
+ <item>vfmsubadd213ps</item>
+ <item>vfmsubadd213pd</item>
+ <item>vfmsubadd123ps</item>
+ <item>vfmsubadd123pd</item>
+ <item>vfmsubadd231ps</item>
+ <item>vfmsubadd231pd</item>
+ <item>vfmsubadd321ps</item>
+ <item>vfmsubadd321pd</item>
+ <item>vfnmadd132ps</item>
+ <item>vfnmadd132pd</item>
+ <item>vfnmadd312ps</item>
+ <item>vfnmadd312pd</item>
+ <item>vfnmadd213ps</item>
+ <item>vfnmadd213pd</item>
+ <item>vfnmadd123ps</item>
+ <item>vfnmadd123pd</item>
+ <item>vfnmadd231ps</item>
+ <item>vfnmadd231pd</item>
+ <item>vfnmadd321ps</item>
+ <item>vfnmadd321pd</item>
+ <item>vfnmsub132ps</item>
+ <item>vfnmsub132pd</item>
+ <item>vfnmsub312ps</item>
+ <item>vfnmsub312pd</item>
+ <item>vfnmsub213ps</item>
+ <item>vfnmsub213pd</item>
+ <item>vfnmsub123ps</item>
+ <item>vfnmsub123pd</item>
+ <item>vfnmsub231ps</item>
+ <item>vfnmsub231pd</item>
+ <item>vfnmsub321ps</item>
+ <item>vfnmsub321pd</item>
+ <item>vfmadd132ss</item>
+ <item>vfmadd132sd</item>
+ <item>vfmadd312ss</item>
+ <item>vfmadd312sd</item>
+ <item>vfmadd213ss</item>
+ <item>vfmadd213sd</item>
+ <item>vfmadd123ss</item>
+ <item>vfmadd123sd</item>
+ <item>vfmadd231ss</item>
+ <item>vfmadd231sd</item>
+ <item>vfmadd321ss</item>
+ <item>vfmadd321sd</item>
+ <item>vfmsub132ss</item>
+ <item>vfmsub132sd</item>
+ <item>vfmsub312ss</item>
+ <item>vfmsub312sd</item>
+ <item>vfmsub213ss</item>
+ <item>vfmsub213sd</item>
+ <item>vfmsub123ss</item>
+ <item>vfmsub123sd</item>
+ <item>vfmsub231ss</item>
+ <item>vfmsub231sd</item>
+ <item>vfmsub321ss</item>
+ <item>vfmsub321sd</item>
+ <item>vfnmadd132ss</item>
+ <item>vfnmadd132sd</item>
+ <item>vfnmadd312ss</item>
+ <item>vfnmadd312sd</item>
+ <item>vfnmadd213ss</item>
+ <item>vfnmadd213sd</item>
+ <item>vfnmadd123ss</item>
+ <item>vfnmadd123sd</item>
+ <item>vfnmadd231ss</item>
+ <item>vfnmadd231sd</item>
+ <item>vfnmadd321ss</item>
+ <item>vfnmadd321sd</item>
+ <item>vfnmsub132ss</item>
+ <item>vfnmsub132sd</item>
+ <item>vfnmsub312ss</item>
+ <item>vfnmsub312sd</item>
+ <item>vfnmsub213ss</item>
+ <item>vfnmsub213sd</item>
+ <item>vfnmsub123ss</item>
+ <item>vfnmsub123sd</item>
+ <item>vfnmsub231ss</item>
+ <item>vfnmsub231sd</item>
+ <item>vfnmsub321ss</item>
+ <item>vfnmsub321sd</item>
+ <item>rdfsbase</item>
+ <item>rdgsbase</item>
+ <item>rdrand</item>
+ <item>wrfsbase</item>
+ <item>wrgsbase</item>
+ <item>vcvtph2ps</item>
+ <item>vcvtps2ph</item>
+ <item>adcx</item>
+ <item>adox</item>
+ <item>rdseed</item>
+ <item>clac</item>
+ <item>stac</item>
+ <item>xstore</item>
+ <item>xcryptecb</item>
+ <item>xcryptcbc</item>
+ <item>xcryptctr</item>
+ <item>xcryptcfb</item>
+ <item>xcryptofb</item>
+ <item>montmul</item>
+ <item>xsha1</item>
+ <item>xsha256</item>
+ <item>llwpcb</item>
+ <item>slwpcb</item>
+ <item>lwpval</item>
+ <item>lwpins</item>
+ <item>vfmaddpd</item>
+ <item>vfmaddps</item>
+ <item>vfmaddsd</item>
+ <item>vfmaddss</item>
+ <item>vfmaddsubpd</item>
+ <item>vfmaddsubps</item>
+ <item>vfmsubaddpd</item>
+ <item>vfmsubaddps</item>
+ <item>vfmsubpd</item>
+ <item>vfmsubps</item>
+ <item>vfmsubsd</item>
+ <item>vfmsubss</item>
+ <item>vfnmaddpd</item>
+ <item>vfnmaddps</item>
+ <item>vfnmaddsd</item>
+ <item>vfnmaddss</item>
+ <item>vfnmsubpd</item>
+ <item>vfnmsubps</item>
+ <item>vfnmsubsd</item>
+ <item>vfnmsubss</item>
+ <item>vfrczpd</item>
+ <item>vfrczps</item>
+ <item>vfrczsd</item>
+ <item>vfrczss</item>
+ <item>vpcmov</item>
+ <item>vpcomb</item>
+ <item>vpcomd</item>
+ <item>vpcomq</item>
+ <item>vpcomub</item>
+ <item>vpcomud</item>
+ <item>vpcomuq</item>
+ <item>vpcomuw</item>
+ <item>vpcomw</item>
+ <item>vphaddbd</item>
+ <item>vphaddbq</item>
+ <item>vphaddbw</item>
+ <item>vphadddq</item>
+ <item>vphaddubd</item>
+ <item>vphaddubq</item>
+ <item>vphaddubw</item>
+ <item>vphaddudq</item>
+ <item>vphadduwd</item>
+ <item>vphadduwq</item>
+ <item>vphaddwd</item>
+ <item>vphaddwq</item>
+ <item>vphsubbw</item>
+ <item>vphsubdq</item>
+ <item>vphsubwd</item>
+ <item>vpmacsdd</item>
+ <item>vpmacsdqh</item>
+ <item>vpmacsdql</item>
+ <item>vpmacssdd</item>
+ <item>vpmacssdqh</item>
+ <item>vpmacssdql</item>
+ <item>vpmacsswd</item>
+ <item>vpmacssww</item>
+ <item>vpmacswd</item>
+ <item>vpmacsww</item>
+ <item>vpmadcsswd</item>
+ <item>vpmadcswd</item>
+ <item>vpperm</item>
+ <item>vprotb</item>
+ <item>vprotd</item>
+ <item>vprotq</item>
+ <item>vprotw</item>
+ <item>vpshab</item>
+ <item>vpshad</item>
+ <item>vpshaq</item>
+ <item>vpshaw</item>
+ <item>vpshlb</item>
+ <item>vpshld</item>
+ <item>vpshlq</item>
+ <item>vpshlw</item>
+ <item>vbroadcasti128</item>
+ <item>vpblendd</item>
+ <item>vpbroadcastb</item>
+ <item>vpbroadcastw</item>
+ <item>vpbroadcastd</item>
+ <item>vpbroadcastq</item>
+ <item>vpermd</item>
+ <item>vpermpd</item>
+ <item>vpermps</item>
+ <item>vpermq</item>
+ <item>vperm2i128</item>
+ <item>vextracti128</item>
+ <item>vinserti128</item>
+ <item>vpmaskmovd</item>
+ <item>vpmaskmovq</item>
+ <item>vpsllvd</item>
+ <item>vpsllvq</item>
+ <item>vpsravd</item>
+ <item>vpsrlvd</item>
+ <item>vpsrlvq</item>
+ <item>vgatherdpd</item>
+ <item>vgatherqpd</item>
+ <item>vgatherdps</item>
+ <item>vgatherqps</item>
+ <item>vpgatherdd</item>
+ <item>vpgatherqd</item>
+ <item>vpgatherdq</item>
+ <item>vpgatherqq</item>
+ <item>xabort</item>
+ <item>xbegin</item>
+ <item>xend</item>
+ <item>xtest</item>
+ <item>andn</item>
+ <item>bextr</item>
+ <item>blci</item>
+ <item>blcic</item>
+ <item>blsi</item>
+ <item>blsic</item>
+ <item>blcfill</item>
+ <item>blsfill</item>
+ <item>blcmsk</item>
+ <item>blsmsk</item>
+ <item>blsr</item>
+ <item>blcs</item>
+ <item>bzhi</item>
+ <item>mulx</item>
+ <item>pdep</item>
+ <item>pext</item>
+ <item>rorx</item>
+ <item>sarx</item>
+ <item>shlx</item>
+ <item>shrx</item>
+ <item>tzcnt</item>
+ <item>tzmsk</item>
+ <item>t1mskc</item>
+ <item>valignd</item>
+ <item>valignq</item>
+ <item>vblendmpd</item>
+ <item>vblendmps</item>
+ <item>vbroadcastf32x4</item>
+ <item>vbroadcastf64x4</item>
+ <item>vbroadcasti32x4</item>
+ <item>vbroadcasti64x4</item>
+ <item>vcompresspd</item>
+ <item>vcompressps</item>
+ <item>vcvtpd2udq</item>
+ <item>vcvtps2udq</item>
+ <item>vcvtsd2usi</item>
+ <item>vcvtss2usi</item>
+ <item>vcvttpd2udq</item>
+ <item>vcvttps2udq</item>
+ <item>vcvttsd2usi</item>
+ <item>vcvttss2usi</item>
+ <item>vcvtudq2pd</item>
+ <item>vcvtudq2ps</item>
+ <item>vcvtusi2sd</item>
+ <item>vcvtusi2ss</item>
+ <item>vexpandpd</item>
+ <item>vexpandps</item>
+ <item>vextractf32x4</item>
+ <item>vextractf64x4</item>
+ <item>vextracti32x4</item>
+ <item>vextracti64x4</item>
+ <item>vfixupimmpd</item>
+ <item>vfixupimmps</item>
+ <item>vfixupimmsd</item>
+ <item>vfixupimmss</item>
+ <item>vgetexppd</item>
+ <item>vgetexpps</item>
+ <item>vgetexpsd</item>
+ <item>vgetexpss</item>
+ <item>vgetmantpd</item>
+ <item>vgetmantps</item>
+ <item>vgetmantsd</item>
+ <item>vgetmantss</item>
+ <item>vinsertf32x4</item>
+ <item>vinsertf64x4</item>
+ <item>vinserti32x4</item>
+ <item>vinserti64x4</item>
+ <item>vmovdqa32</item>
+ <item>vmovdqa64</item>
+ <item>vmovdqu32</item>
+ <item>vmovdqu64</item>
+ <item>vpabsq</item>
+ <item>vpandd</item>
+ <item>vpandnd</item>
+ <item>vpandnq</item>
+ <item>vpandq</item>
+ <item>vpblendmd</item>
+ <item>vpblendmq</item>
+ <item>vpcmpltd</item>
+ <item>vpcmpled</item>
+ <item>vpcmpneqd</item>
+ <item>vpcmpnltd</item>
+ <item>vpcmpnled</item>
+ <item>vpcmpd</item>
+ <item>vpcmpltq</item>
+ <item>vpcmpleq</item>
+ <item>vpcmpneqq</item>
+ <item>vpcmpnltq</item>
+ <item>vpcmpnleq</item>
+ <item>vpcmpq</item>
+ <item>vpcmpequd</item>
+ <item>vpcmpltud</item>
+ <item>vpcmpleud</item>
+ <item>vpcmpnequd</item>
+ <item>vpcmpnltud</item>
+ <item>vpcmpnleud</item>
+ <item>vpcmpud</item>
+ <item>vpcmpequq</item>
+ <item>vpcmpltuq</item>
+ <item>vpcmpleuq</item>
+ <item>vpcmpnequq</item>
+ <item>vpcmpnltuq</item>
+ <item>vpcmpnleuq</item>
+ <item>vpcmpuq</item>
+ <item>vpcompressd</item>
+ <item>vpcompressq</item>
+ <item>vpermi2d</item>
+ <item>vpermi2pd</item>
+ <item>vpermi2ps</item>
+ <item>vpermi2q</item>
+ <item>vpermt2d</item>
+ <item>vpermt2pd</item>
+ <item>vpermt2ps</item>
+ <item>vpermt2q</item>
+ <item>vpexpandd</item>
+ <item>vpexpandq</item>
+ <item>vpmaxsq</item>
+ <item>vpmaxuq</item>
+ <item>vpminsq</item>
+ <item>vpminuq</item>
+ <item>vpmovdb</item>
+ <item>vpmovdw</item>
+ <item>vpmovqb</item>
+ <item>vpmovqd</item>
+ <item>vpmovqw</item>
+ <item>vpmovsdb</item>
+ <item>vpmovsdw</item>
+ <item>vpmovsqb</item>
+ <item>vpmovsqd</item>
+ <item>vpmovsqw</item>
+ <item>vpmovusdb</item>
+ <item>vpmovusdw</item>
+ <item>vpmovusqb</item>
+ <item>vpmovusqd</item>
+ <item>vpmovusqw</item>
+ <item>vpord</item>
+ <item>vporq</item>
+ <item>vprold</item>
+ <item>vprolq</item>
+ <item>vprolvd</item>
+ <item>vprolvq</item>
+ <item>vprord</item>
+ <item>vprorq</item>
+ <item>vprorvd</item>
+ <item>vprorvq</item>
+ <item>vpscatterdd</item>
+ <item>vpscatterdq</item>
+ <item>vpscatterqd</item>
+ <item>vpscatterqq</item>
+ <item>vpsraq</item>
+ <item>vpsravq</item>
+ <item>vpternlogd</item>
+ <item>vpternlogq</item>
+ <item>vptestmd</item>
+ <item>vptestmq</item>
+ <item>vptestnmd</item>
+ <item>vptestnmq</item>
+ <item>vpxord</item>
+ <item>vpxorq</item>
+ <item>vrcp14pd</item>
+ <item>vrcp14ps</item>
+ <item>vrcp14sd</item>
+ <item>vrcp14ss</item>
+ <item>vrndscalepd</item>
+ <item>vrndscaleps</item>
+ <item>vrndscalesd</item>
+ <item>vrndscaless</item>
+ <item>vrsqrt14pd</item>
+ <item>vrsqrt14ps</item>
+ <item>vrsqrt14sd</item>
+ <item>vrsqrt14ss</item>
+ <item>vscalefpd</item>
+ <item>vscalefps</item>
+ <item>vscalefsd</item>
+ <item>vscalefss</item>
+ <item>vscatterdpd</item>
+ <item>vscatterdps</item>
+ <item>vscatterqpd</item>
+ <item>vscatterqps</item>
+ <item>vshuff32x4</item>
+ <item>vshuff64x2</item>
+ <item>vshufi32x4</item>
+ <item>vshufi64x2</item>
+ <item>kandnw</item>
+ <item>kandw</item>
+ <item>kmovw</item>
+ <item>knotw</item>
+ <item>kortestw</item>
+ <item>korw</item>
+ <item>kshiftlw</item>
+ <item>kshiftrw</item>
+ <item>kunpckbw</item>
+ <item>kxnorw</item>
+ <item>kxorw</item>
+ <item>vpbroadcastmb2q</item>
+ <item>vpbroadcastmw2d</item>
+ <item>vpconflictd</item>
+ <item>vpconflictq</item>
+ <item>vplzcntd</item>
+ <item>vplzcntq</item>
+ <item>vexp2pd</item>
+ <item>vexp2ps</item>
+ <item>vrcp28pd</item>
+ <item>vrcp28ps</item>
+ <item>vrcp28sd</item>
+ <item>vrcp28ss</item>
+ <item>vrsqrt28pd</item>
+ <item>vrsqrt28ps</item>
+ <item>vrsqrt28sd</item>
+ <item>vrsqrt28ss</item>
+ <item>vgatherpf0dpd</item>
+ <item>vgatherpf0dps</item>
+ <item>vgatherpf0qpd</item>
+ <item>vgatherpf0qps</item>
+ <item>vgatherpf1dpd</item>
+ <item>vgatherpf1dps</item>
+ <item>vgatherpf1qpd</item>
+ <item>vgatherpf1qps</item>
+ <item>vscatterpf0dpd</item>
+ <item>vscatterpf0dps</item>
+ <item>vscatterpf0qpd</item>
+ <item>vscatterpf0qps</item>
+ <item>vscatterpf1dpd</item>
+ <item>vscatterpf1dps</item>
+ <item>vscatterpf1qpd</item>
+ <item>vscatterpf1qps</item>
+ <item>prefetchwt1</item>
+ <item>bndmk</item>
+ <item>bndcl</item>
+ <item>bndcu</item>
+ <item>bndcn</item>
+ <item>bndmov</item>
+ <item>bndldx</item>
+ <item>bndstx</item>
+ <item>sha1rnds4</item>
+ <item>sha1nexte</item>
+ <item>sha1msg1</item>
+ <item>sha1msg2</item>
+ <item>sha256rnds2</item>
+ <item>sha256msg1</item>
+ <item>sha256msg2</item>
+ <item>hint_nop</item>
+ </list>
+
+ <list name="branch instructions">
+ <item>call</item>
+ <item>callq</item>
+ <item>iret</item>
+ <item>iretd</item>
+ <item>iretq</item>
+ <item>iretw</item>
+ <item>ja</item>
+ <item>jae</item>
+ <item>jb</item>
+ <item>jbe</item>
+ <item>jc</item>
+ <item>jcxz</item>
+ <item>je</item>
+ <item>jecxz</item>
+ <item>jg</item>
+ <item>jge</item>
+ <item>jl</item>
+ <item>jle</item>
+ <item>jmp</item>
+ <item>jmpe</item>
+ <item>jna</item>
+ <item>jnae</item>
+ <item>jnb</item>
+ <item>jnbe</item>
+ <item>jnc</item>
+ <item>jne</item>
+ <item>jng</item>
+ <item>jnge</item>
+ <item>jnl</item>
+ <item>jnle</item>
+ <item>jno</item>
+ <item>jnp</item>
+ <item>jns</item>
+ <item>jnz</item>
+ <item>jo</item>
+ <item>jp</item>
+ <item>jpe</item>
+ <item>jpo</item>
+ <item>jrcxz</item>
+ <item>js</item>
+ <item>jz</item>
+ <item>ret</item>
+ <item>retd</item>
+ <item>retf</item>
+ <item>retfd</item>
+ <item>retfq</item>
+ <item>retfw</item>
+ <item>retn</item>
+ <item>retnd</item>
+ <item>retnq</item>
+ <item>retnw</item>
+ <item>retq</item>
+ <item>retw</item>
+ </list>
+
+ <list name="keywords">
+ <item>.abort</item>
+ <item>.align</item>
+ <item>.app-file</item>
+ <item>.appline</item>
+ <item>.ascii</item>
+ <item>.asciz</item>
+ <item>.att_syntax</item>
+ <item>.balign</item>
+ <item>.balignl</item>
+ <item>.balignw</item>
+ <item>.byte</item>
+ <item>.code16</item>
+ <item>.code32</item>
+ <item>.comm</item>
+ <item>.common.s</item>
+ <item>.common</item>
+ <item>.data</item>
+ <item>.dc.b</item>
+ <item>.dc.d</item>
+ <item>.dc.l</item>
+ <item>.dc.s</item>
+ <item>.dc.w</item>
+ <item>.dc.x</item>
+ <item>.dc</item>
+ <item>.dcb.b</item>
+ <item>.dcb.d</item>
+ <item>.dcb.l</item>
+ <item>.dcb.s</item>
+ <item>.dcb.w</item>
+ <item>.dcb.x</item>
+ <item>.dcb</item>
+ <item>.debug</item>
+ <item>.def</item>
+ <item>.desc</item>
+ <item>.dim</item>
+ <item>.double</item>
+ <item>.ds.b</item>
+ <item>.ds.d</item>
+ <item>.ds.l</item>
+ <item>.ds.p</item>
+ <item>.ds.s</item>
+ <item>.ds.w</item>
+ <item>.ds.x</item>
+ <item>.ds</item>
+ <item>.dsect</item>
+ <item>.eject</item>
+ <item>.else</item>
+ <item>.elsec</item>
+ <item>.elseif</item>
+ <item>.end</item>
+ <item>.endc</item>
+ <item>.endef</item>
+ <item>.endfunc</item>
+ <item>.endif</item>
+ <item>.endm</item>
+ <item>.endr</item>
+ <item>.equ</item>
+ <item>.equiv</item>
+ <item>.err</item>
+ <item>.exitm</item>
+ <item>.extend</item>
+ <item>.extern</item>
+ <item>.fail</item>
+ <item>.file</item>
+ <item>.fill</item>
+ <item>.float</item>
+ <item>.format</item>
+ <item>.func</item>
+ <item>.global</item>
+ <item>.globl</item>
+ <item>.hidden</item>
+ <item>.hword</item>
+ <item>.ident</item>
+ <item>.if</item>
+ <item>.ifc</item>
+ <item>.ifdef</item>
+ <item>.ifeq</item>
+ <item>.ifeqs</item>
+ <item>.ifge</item>
+ <item>.ifgt</item>
+ <item>.ifle</item>
+ <item>.iflt</item>
+ <item>.ifnc</item>
+ <item>.ifndef</item>
+ <item>.ifne</item>
+ <item>.ifnes</item>
+ <item>.ifnotdef</item>
+ <item>.include</item>
+ <item>.int</item>
+ <item>.intel_syntax</item>
+ <item>.internal</item>
+ <item>.irep</item>
+ <item>.irepc</item>
+ <item>.irp</item>
+ <item>.irpc</item>
+ <item>.lcomm</item>
+ <item>.lflags</item>
+ <item>.line</item>
+ <item>.linkonce</item>
+ <item>.list</item>
+ <item>.llen</item>
+ <item>.ln</item>
+ <item>.long</item>
+ <item>.lsym</item>
+ <item>.macro</item>
+ <item>.mexit</item>
+ <item>.name</item>
+ <item>.noformat</item>
+ <item>.nolist</item>
+ <item>.nopage</item>
+ <item>noprefix</item>
+ <item>.octa</item>
+ <item>.offset</item>
+ <item>.org</item>
+ <item>.p2align</item>
+ <item>.p2alignl</item>
+ <item>.p2alignw</item>
+ <item>.page</item>
+ <item>.plen</item>
+ <item>.popsection</item>
+ <item>.previous</item>
+ <item>.print</item>
+ <item>.protected</item>
+ <item>.psize</item>
+ <item>.purgem</item>
+ <item>.pushsection</item>
+ <item>.quad</item>
+ <item>.rodata</item>
+ <item>.rep</item>
+ <item>.rept</item>
+ <item>.rva</item>
+ <item>.sbttl</item>
+ <item>.scl</item>
+ <item>.sect.s</item>
+ <item>.sect</item>
+ <item>.section.s</item>
+ <item>.section</item>
+ <item>.set</item>
+ <item>.short</item>
+ <item>.single</item>
+ <item>.size</item>
+ <item>.skip</item>
+ <item>.sleb128</item>
+ <item>.space</item>
+ <item>.spc</item>
+ <item>.stabd</item>
+ <item>.stabn</item>
+ <item>.stabs</item>
+ <item>.string</item>
+ <item>.struct</item>
+ <item>.subsection</item>
+ <item>.symver</item>
+ <item>.tag</item>
+ <item>.text</item>
+ <item>.title</item>
+ <item>.ttl</item>
+ <item>.type</item>
+ <item>.uleb128</item>
+ <item>.use</item>
+ <item>.val</item>
+ <item>.version</item>
+ <item>.vtable_entry</item>
+ <item>.vtable_inherit</item>
+ <item>.weak</item>
+ <item>.word</item>
+ <item>.xcom</item>
+ <item>.xdef</item>
+ <item>.xref</item>
+ <item>.xstabs</item>
+ <item>.zero</item>
+ <!-- Directives specific to ARM -->
+ <item>.arm</item>
+ <item>.bss</item>
+ <item>.code</item>
+ <item>.even</item>
+ <item>.force_thumb</item>
+ <item>.ldouble</item>
+ <item>.loc</item>
+ <item>.ltorg</item>
+ <item>.packed</item>
+ <item>.pool</item>
+ <item>.req</item>
+ <item>.thumb</item>
+ <item>.thumb_func</item>
+ <item>.thumb_set</item>
+ </list>
+
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <RegExpr attribute="Label" context="#stay" String="[\.]?[_\w\d-]*\s*:" />
+ <keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <keyword attribute="Registers" context="#stay" String="registers"/>
+ <keyword attribute="Instructions" context="#stay" String="instructions"/>
+ <keyword attribute="Branch Instructions" context="#stay" String="branch instructions"/>
+ <HlCOct attribute="Octal" context="#stay" />
+ <HlCHex attribute="Hex" context="#stay" />
+ <RegExpr attribute="Binary" context="#stay" String="0[bB][01]+" />
+ <Int attribute="Decimal" context="#stay" />
+ <RegExpr attribute="Decimal" context="#stay" String="\$[0-9]+" />
+ <RegExpr attribute="Float" context="#stay" String="0[fFeEdD][-+]?[0-9]*\.?[0-9]*[eE]?[-+]?[0-9]+" />
+ <RegExpr attribute="Normal Text" context="#stay" String="[A-Za-z_.$][A-Za-z0-9_.$]*" />
+ <HlCChar attribute="Char" context="#stay" />
+ <RegExpr attribute="Char" context="#stay" String="'(\\x[0-9a-fA-F][0-9a-fA-F]?|\\[0-7]?[0-7]?[0-7]?|\\.|.)" />
+ <DetectChar attribute="String" context="String" char="&quot;" />
+ <RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*if(?:def|ndef)?(?=\s+\S)" insensitive="true" beginRegion="PP" firstNonSpace="true" />
+ <RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*endif" insensitive="true" endRegion="PP" firstNonSpace="true" />
+ <RegExpr attribute="Preprocessor" context="Define" String="#\s*define.*((?=\\))" insensitive="true" firstNonSpace="true" />
+ <RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*(?:el(?:se|if)|include(?:_next)?|define|undef|line|error|warning|pragma)" firstNonSpace="true" />
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="*" beginRegion="BlockComment" />
+ <AnyChar attribute="Comment" context="Commentar 2" String="@;#" />
+ <AnyChar attribute="Symbol" context="#stay" String="!#%&amp;*()+,-&lt;=&gt;?/:[]^{|}~" />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar 1">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="BlockComment" />
+ <DetectSpaces />
+ <IncludeRules context="##Comments" />
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="Commentar 2" >
+ <DetectSpaces />
+ <IncludeRules context="##Comments" />
+ </context>
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <LineContinue attribute="String" context="Some Context" />
+ <HlCStringChar attribute="String Char" context="#stay" />
+ <DetectChar attribute="String" context="#pop" char="&quot;" />
+ </context>
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor" />
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Define">
+ <LineContinue attribute="Preprocessor" context="#stay"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Some Context" />
+ </contexts>
+
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal" />
+ <itemData name="Label" defStyleNum="dsFunction" />
+ <itemData name="Keyword" defStyleNum="dsKeyword" />
+ <itemData name="Registers" defStyleNum="dsKeyword" />
+ <itemData name="Instructions" defStyleNum="dsKeyword" />
+ <itemData name="Branch Instructions" defStyleNum="dsControlFlow" />
+ <itemData name="Decimal" defStyleNum="dsDecVal" />
+ <itemData name="Octal" defStyleNum="dsBaseN" />
+ <itemData name="Hex" defStyleNum="dsBaseN" />
+ <itemData name="Binary" defStyleNum="dsBaseN" />
+ <itemData name="Float" defStyleNum="dsFloat" />
+ <itemData name="Char" defStyleNum="dsChar" />
+ <itemData name="String" defStyleNum="dsString" />
+ <itemData name="String Char" defStyleNum="dsSpecialChar" />
+ <itemData name="Symbol" defStyleNum="dsOperator" />
+ <itemData name="Comment" defStyleNum="dsComment" />
+ <itemData name="Preprocessor" defStyleNum="dsPreprocessor" />
+ </itemDatas>
+ </highlighting>
+
+ <general>
+ <comments>
+ <comment name="singleLine" start="#" position="afterwhitespace" />
+ <comment name="multiLine" start="/*" end="*/" region="BlockComment" />
+ </comments>
+ <keywords casesensitive="1" weakDeliminator="_.$" />
+ </general>
+</language>
+<!-- kate: replace-tabs on; tab-width 2; indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/html.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/html.xml
index 107e1067da..6e42c66f20 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/html.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/html.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd"
+<!DOCTYPE language
[
<!ENTITY name "[A-Za-z_:][\w.:_-]*">
<!ENTITY attributeName "[A-Za-z_:*#\(\[][\)\]\w.:_-]*">
- <!ENTITY entref "&amp;(#[0-9]+|#[xX][0-9A-Fa-f]+|&name;);">
+ <!ENTITY entref "&amp;(?:#[0-9]+|#[xX][0-9A-Fa-f]+|&name;);">
]>
-<language name="HTML" version="7" kateversion="3.4" section="Markup" extensions="*.htm;*.html;*.shtml;*.shtm" mimetype="text/html" author="Wilbert Berendsen (wilbert@kde.nl)" license="LGPL" priority="10">
+<language name="HTML" version="18" kateversion="5.79" section="Markup" extensions="*.htm;*.html;*.shtml;*.shtm;*.aspx" mimetype="text/html" author="Wilbert Berendsen (wilbert@kde.nl)" license="LGPL" priority="10">
<highlighting>
<contexts>
@@ -18,46 +18,72 @@
<DetectIdentifier/>
<StringDetect attribute="Comment" context="Comment" String="&lt;!--" beginRegion="comment" />
<StringDetect attribute="CDATA" context="CDATA" String="&lt;![CDATA[" beginRegion="cdata" />
- <RegExpr attribute="Doctype" context="Doctype" String="&lt;!DOCTYPE\s+" insensitive="true" beginRegion="doctype" />
+ <WordDetect attribute="Doctype" context="Doctype" String="&lt;!DOCTYPE" insensitive="true" beginRegion="doctype" />
+ <IncludeRules context="FindElements" />
<RegExpr attribute="Processing Instruction" context="PI" String="&lt;\?[\w:-]*" beginRegion="pi" />
- <RegExpr attribute="Element" context="CSS" String="&lt;style\b" insensitive="true" beginRegion="style" />
- <RegExpr attribute="Element" context="JS" String="&lt;script\b" insensitive="true" beginRegion="script" />
- <RegExpr attribute="Element" context="El Open" String="&lt;pre\b" insensitive="true" beginRegion="pre" />
- <RegExpr attribute="Element" context="El Open" String="&lt;div\b" insensitive="true" beginRegion="div" />
- <RegExpr attribute="Element" context="El Open" String="&lt;table\b" insensitive="true" beginRegion="table" />
- <RegExpr attribute="Element" context="El Open" String="&lt;ul\b" insensitive="true" beginRegion="ul" />
- <RegExpr attribute="Element" context="El Open" String="&lt;ol\b" insensitive="true" beginRegion="ol" />
- <RegExpr attribute="Element" context="El Open" String="&lt;dl\b" insensitive="true" beginRegion="dl" />
- <RegExpr attribute="Element" context="El Open" String="&lt;article\b" insensitive="true" beginRegion="article" />
- <RegExpr attribute="Element" context="El Open" String="&lt;aside\b" insensitive="true" beginRegion="aside" />
- <RegExpr attribute="Element" context="El Open" String="&lt;details\b" insensitive="true" beginRegion="details" />
- <RegExpr attribute="Element" context="El Open" String="&lt;figure\b" insensitive="true" beginRegion="figure" />
- <RegExpr attribute="Element" context="El Open" String="&lt;footer\b" insensitive="true" beginRegion="footer" />
- <RegExpr attribute="Element" context="El Open" String="&lt;header\b" insensitive="true" beginRegion="header" />
- <RegExpr attribute="Element" context="El Open" String="&lt;main\b" insensitive="true" beginRegion="main" />
- <RegExpr attribute="Element" context="El Open" String="&lt;nav\b" insensitive="true" beginRegion="nav" />
- <RegExpr attribute="Element" context="El Open" String="&lt;section\b" insensitive="true" beginRegion="section" />
- <RegExpr attribute="Element" context="El Open" String="&lt;&name;" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/pre\b" insensitive="true" endRegion="pre" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/div\b" insensitive="true" endRegion="div" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/table\b" insensitive="true" endRegion="table" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/ul\b" insensitive="true" endRegion="ul" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/ol\b" insensitive="true" endRegion="ol" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/dl\b" insensitive="true" endRegion="dl" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/article\b" insensitive="true" endRegion="article" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/aside\b" insensitive="true" endRegion="aside" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/details\b" insensitive="true" endRegion="details" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/figure\b" insensitive="true" endRegion="figure" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/footer\b" insensitive="true" endRegion="footer" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/header\b" insensitive="true" endRegion="header" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/main\b" insensitive="true" endRegion="main" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/nav\b" insensitive="true" endRegion="nav" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/section\b" insensitive="true" endRegion="section" />
- <RegExpr attribute="Element" context="El Close" String="&lt;/&name;" />
+
<!-- as long as kde gives DTDs the text/html mimetype--><IncludeRules context="FindDTDRules" />
<IncludeRules context="FindEntityRefs" />
</context>
+ <context name="FindElements" attribute="Other Text" lineEndContext="#pop">
+ <RegExpr attribute="Element Symbols" context="ElementTagName" String="&lt;(?=(&name;))" />
+ <RegExpr attribute="Element Symbols" context="ElementTagNameClose" String="&lt;/(?=(&name;))" />
+ </context>
+
+ <context name="ElementTagName" attribute="Other Text" lineEndContext="#pop">
+ <IncludeRules context="FindHTMLTags" />
+ <IncludeRules context="FindSpecialHTMLTags" />
+ <StringDetect attribute="Element" context="#pop!El Open" String="%1" dynamic="true" />
+ </context>
+
+ <context name="ElementTagNameClose" attribute="Other Text" lineEndContext="#pop">
+ <IncludeRules context="FindHTMLTagsClose" />
+ <StringDetect attribute="Element" context="#pop!El Close" String="%1" dynamic="true" />
+ </context>
+
+ <!-- This allows you to insert HTML tags in other syntax definitions -->
+ <context name="FindSpecialHTMLTags" attribute="Normal Text" lineEndContext="#stay">
+ <WordDetect attribute="Element" context="#pop!CSS" String="style" insensitive="true" beginRegion="style" />
+ <WordDetect attribute="Element" context="#pop!JS" String="script" insensitive="true" beginRegion="script" />
+ </context>
+
+ <context name="FindHTMLTags" attribute="Normal Text" lineEndContext="#stay">
+ <WordDetect attribute="Element" context="#pop!El Open" String="pre" insensitive="true" beginRegion="pre" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="div" insensitive="true" beginRegion="div" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="table" insensitive="true" beginRegion="table" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="ul" insensitive="true" beginRegion="ul" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="ol" insensitive="true" beginRegion="ol" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="dl" insensitive="true" beginRegion="dl" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="article" insensitive="true" beginRegion="article" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="aside" insensitive="true" beginRegion="aside" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="details" insensitive="true" beginRegion="details" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="figure" insensitive="true" beginRegion="figure" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="footer" insensitive="true" beginRegion="footer" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="header" insensitive="true" beginRegion="header" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="main" insensitive="true" beginRegion="main" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="nav" insensitive="true" beginRegion="nav" />
+ <WordDetect attribute="Element" context="#pop!El Open" String="section" insensitive="true" beginRegion="section" />
+ </context>
+
+ <context name="FindHTMLTagsClose" attribute="Normal Text" lineEndContext="#stay">
+ <WordDetect attribute="Element" context="#pop!El Close" String="pre" insensitive="true" endRegion="pre" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="div" insensitive="true" endRegion="div" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="table" insensitive="true" endRegion="table" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="ul" insensitive="true" endRegion="ul" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="ol" insensitive="true" endRegion="ol" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="dl" insensitive="true" endRegion="dl" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="article" insensitive="true" endRegion="article" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="aside" insensitive="true" endRegion="aside" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="details" insensitive="true" endRegion="details" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="figure" insensitive="true" endRegion="figure" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="footer" insensitive="true" endRegion="footer" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="header" insensitive="true" endRegion="header" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="main" insensitive="true" endRegion="main" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="nav" insensitive="true" endRegion="nav" />
+ <WordDetect attribute="Element" context="#pop!El Close" String="section" insensitive="true" endRegion="section" />
+ </context>
+
<context name="FindEntityRefs" attribute="Other Text" lineEndContext="#stay">
<RegExpr attribute="EntityRef" context="#stay" String="&entref;" />
<AnyChar attribute="Error" context="#stay" String="&amp;&lt;" />
@@ -70,22 +96,21 @@
</context>
<context name="FindAttributes" attribute="Other Text" lineEndContext="#stay">
- <RegExpr attribute="Attribute" context="#stay" String="&attributeName;" column="0"/>
- <RegExpr attribute="Attribute" context="#stay" String="\s+&attributeName;" />
- <DetectChar attribute="Attribute" context="Value" char="=" />
+ <DetectChar attribute="Attribute Separator" context="Value" char="=" />
+ <RegExpr attribute="Attribute" context="#stay" String="(^|\s+)&attributeName;(\s+&attributeName;)*\s*|\s+" />
</context>
<context name="FindDTDRules" attribute="Other Text" lineEndContext="#stay">
- <RegExpr attribute="Doctype" context="Doctype Markupdecl" String="&lt;!(ELEMENT|ENTITY|ATTLIST|NOTATION)\b" />
+ <RegExpr attribute="Doctype" context="Doctype Markupdecl" String="&lt;!(?:ELEMENT|ENTITY|ATTLIST|NOTATION)\b" />
</context>
<context name="Comment" attribute="Comment" lineEndContext="#stay">
<DetectSpaces/>
- <IncludeRules context="##Alerts" />
- <DetectIdentifier/>
<StringDetect attribute="Comment" context="#pop" String="--&gt;" endRegion="comment" />
- <RegExpr attribute="Error" context="#stay" String="-(-(?!-&gt;))+" />
+ <IncludeRules context="##Comments" />
+ <DetectIdentifier/>
+ <RegExpr attribute="Error" context="#stay" String="-(?:-(?!-&gt;))+" />
</context>
<context name="CDATA" attribute="Other Text" lineEndContext="#stay">
@@ -128,49 +153,40 @@
<IncludeRules context="FindPEntityRefs" />
</context>
- <context name="El Open" attribute="Other Text" lineEndContext="#stay">
- <Detect2Chars attribute="Element" context="#pop" char="/" char1="&gt;" />
- <DetectChar attribute="Element" context="#pop" char="&gt;" />
+ <context name="El Open" attribute="Error" lineEndContext="#stay">
+ <Detect2Chars attribute="Element Symbols" context="#pop" char="/" char1="&gt;" />
+ <DetectChar attribute="Element Symbols" context="#pop" char="&gt;" />
<IncludeRules context="FindAttributes" />
- <RegExpr attribute="Error" context="#stay" String="\S" />
</context>
<context name="El Close" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Element" context="#pop" char="&gt;" />
- <RegExpr attribute="Error" context="#stay" String="\S" />
- </context>
-
- <context name="El Close 2" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Element" context="#pop#pop#pop" char="&gt;" />
- <RegExpr attribute="Error" context="#stay" String="\S" />
- </context>
-
- <context name="El Close 3" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Element" context="#pop#pop#pop#pop" char="&gt;" />
+ <DetectChar attribute="Element Symbols" context="#pop" char="&gt;" />
<RegExpr attribute="Error" context="#stay" String="\S" />
</context>
- <context name="CSS" attribute="Other Text" lineEndContext="#stay">
- <Detect2Chars attribute="Element" context="#pop" char="/" char1="&gt;" endRegion="style" />
- <DetectChar attribute="Element" context="CSS content" char="&gt;" />
+ <context name="CSS" attribute="Error" lineEndContext="#stay">
+ <Detect2Chars attribute="Element Symbols" context="#pop" char="/" char1="&gt;" endRegion="style" />
+ <DetectChar attribute="Element Symbols" context="CSS content" char="&gt;" />
<IncludeRules context="FindAttributes" />
- <RegExpr attribute="Error" context="#stay" String="\S" />
</context>
<context name="CSS content" attribute="Other Text" lineEndContext="#stay">
- <RegExpr attribute="Element" context="El Close 2" String="&lt;/style\b" insensitive="true" endRegion="style" />
+ <RegExpr attribute="Element Symbols" context="CSS content Close" String="&lt;/(?=style\b)" insensitive="true" />
<IncludeRules context="##CSS" includeAttrib="true"/>
</context>
+ <context name="CSS content Close" attribute="Other Text" lineEndContext="#stay">
+ <DetectIdentifier attribute="Element" context="#pop#pop#pop!El Close" endRegion="style" />
+ </context>
- <context name="JS" attribute="Other Text" lineEndContext="#stay">
- <RegExpr attribute="Attribute" context="Script-Type" String="(\s+|^)type(?=\=|\s|$)" insensitive="true"/>
- <DetectChar attribute="Element" context="JS content" char="&gt;" />
+ <context name="JS" attribute="Error" lineEndContext="#stay">
+ <RegExpr attribute="Attribute" context="Script-Type" String="(?:\s+|^)type(?=\=|\s|$)" insensitive="true"/>
+ <DetectChar attribute="Element Symbols" context="JS content" char="&gt;" />
<IncludeRules context="DefaultJS" />
</context>
<context name="DefaultJS" attribute="Other Text" lineEndContext="#stay">
- <Detect2Chars attribute="Element" context="#pop" char="/" char1="&gt;" endRegion="script" />
- <IncludeRules context="FindAttributes" />
- <RegExpr attribute="Error" context="#stay" String="\S" />
+ <Detect2Chars attribute="Element Symbols" context="#pop" char="/" char1="&gt;" endRegion="script" />
+ <DetectChar attribute="Attribute Separator" context="Value" char="=" />
+ <RegExpr attribute="Attribute" context="#stay" String="(^|\s+)&attributeName;|\s+" />
</context>
<context name="JS content" attribute="Other Text" lineEndContext="#stay">
@@ -178,25 +194,35 @@
<IncludeRules context="Normal##JavaScript" includeAttrib="true"/>
</context>
<context name="Default JS content" attribute="Other Text" lineEndContext="#stay">
- <RegExpr attribute="Element" context="El Close 2" String="&lt;/script\b" insensitive="true" endRegion="script" />
+ <IncludeRules context="FindScriptTagClose" />
<RegExpr attribute="Comment" context="JS comment close" String="//(?=.*&lt;/script\b)" insensitive="true" />
</context>
+ <context name="FindScriptTagClose" attribute="Other Text" lineEndContext="#stay">
+ <RegExpr attribute="Element Symbols" context="ScriptTagClose" String="&lt;/(?=script\b)" insensitive="true" />
+ </context>
+ <context name="ScriptTagClose" attribute="Other Text" lineEndContext="#stay">
+ <DetectIdentifier attribute="Element" context="#pop#pop#pop!El Close" endRegion="script" />
+ </context>
+
<context name="JS comment close" attribute="Comment" lineEndContext="#pop">
- <RegExpr attribute="Element" context="El Close 3" String="&lt;/script\b" insensitive="true" endRegion="script" />
- <IncludeRules context="##Alerts" />
+ <RegExpr attribute="Element Symbols" context="#pop!ScriptTagClose" String="&lt;/(?=script\b)" insensitive="true" />
+ <DetectSpaces />
+ <IncludeRules context="##Comments" />
</context>
- <context name="Value" attribute="Other Text" lineEndContext="#stay" fallthrough="true" fallthroughContext="Value NQ">
+ <context name="Value" attribute="Other Text" lineEndContext="#stay" fallthroughContext="Value NQ">
<DetectChar attribute="Value" context="Value DQ" char="&quot;" />
<DetectChar attribute="Value" context="Value SQ" char="&apos;" />
<DetectSpaces />
</context>
- <context name="Value NQ" attribute="Other Text" lineEndContext="#pop#pop" fallthrough="true" fallthroughContext="#pop#pop">
+ <context name="Value NQ" attribute="Other Text" lineEndContext="#pop#pop" fallthroughContext="#pop#pop">
+ <!-- '{' and '}' are valid, but used with twig -->
+ <RegExpr attribute="Value" String="[^&gt;&lt;&quot;&apos;&amp;\s=`{}]+" />
<IncludeRules context="FindEntityRefs" />
- <RegExpr attribute="Value" context="#stay" String="/(?!&gt;)" />
- <RegExpr attribute="Value" context="#stay" String="[^/&gt;&lt;&quot;&apos;\s]" />
+ <AnyChar attribute="Error" String="&quot;&apos;`=" />
+ <AnyChar attribute="Value" String="{}" />
</context>
<context name="Value DQ" attribute="Value" lineEndContext="#stay">
@@ -212,11 +238,11 @@
<!-- Read content from the "type" attribute to change the language to
highlight in the <script> tag. The default language is JavaScript. -->
- <context name="Script-Type" attribute="Other Text" lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop">
+ <context name="Script-Type" attribute="Other Text" lineEndContext="#stay" fallthroughContext="#pop">
<DetectSpaces />
<DetectChar attribute="Attribute" context="#pop!Script-Type Value" char="=" />
</context>
- <context name="Script-Type Value" attribute="Other Text" lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop!Value">
+ <context name="Script-Type Value" attribute="Other Text" lineEndContext="#stay" fallthroughContext="#pop!Value">
<DetectSpaces />
<!-- TypeScript -->
<StringDetect attribute="Value" context="#pop#pop!TypeScript" String="&quot;text/typescript&quot;"/>
@@ -230,7 +256,7 @@
<StringDetect attribute="Value" context="#pop#pop!MustacheJS" String="&quot;x-tmpl-mustache&quot;"/>
<StringDetect attribute="Value" context="#pop#pop!MustacheJS" String="&apos;x-tmpl-mustache&apos;"/>
<StringDetect attribute="Value" context="#pop#pop!MustacheJS" String="&quot;text/mustache&quot;"/>
- <StringDetect attribute="Value" context="#pop#pop!MustacheJS" String="&apos;text/mustache&apos;"/>
+ <StringDetect attribute="Value" context="#pop#pop!MustacheJS" String="&apos;text/mustache&apos;"/>
<StringDetect attribute="Value" context="#pop#pop!MustacheJS" String="&quot;text/x-mustache-template&quot;"/>
<StringDetect attribute="Value" context="#pop#pop!MustacheJS" String="&apos;text/x-mustache-template&apos;"/>
<StringDetect attribute="Value" context="#pop#pop!MustacheJS" String="&quot;text/x-handlebars-template&quot;"/>
@@ -242,17 +268,17 @@
<StringDetect attribute="Value" context="#pop#pop!Script HTML template" String="&apos;text/html&apos;"/>
</context>
- <context name="JSX" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Element" context="JSX content" char="&gt;" />
+ <context name="JSX" attribute="Error" lineEndContext="#stay">
+ <DetectChar attribute="Element Symbols" context="JSX content" char="&gt;" />
<IncludeRules context="DefaultJS" />
</context>
<context name="JSX content" attribute="Other Text" lineEndContext="#stay">
<IncludeRules context="Default JS content"/>
- <IncludeRules context="Normal##JavaScript React" includeAttrib="true"/>
+ <IncludeRules context="Normal##JavaScript React (JSX)" includeAttrib="true"/>
</context>
- <context name="TypeScript" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Element" context="TypeScript content" char="&gt;" />
+ <context name="TypeScript" attribute="Error" lineEndContext="#stay">
+ <DetectChar attribute="Element Symbols" context="TypeScript content" char="&gt;" />
<IncludeRules context="DefaultJS" />
</context>
<context name="TypeScript content" attribute="Other Text" lineEndContext="#stay">
@@ -260,25 +286,25 @@
<IncludeRules context="Normal##TypeScript" includeAttrib="true"/>
</context>
- <context name="MustacheJS" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Element" context="MustacheJS content" char="&gt;" />
+ <context name="MustacheJS" attribute="Error" lineEndContext="#stay">
+ <DetectChar attribute="Element Symbols" context="MustacheJS content" char="&gt;" />
<IncludeRules context="DefaultJS" />
</context>
<context name="MustacheJS content" attribute="Other Text" lineEndContext="#stay">
- <RegExpr attribute="Element" context="El Close 2" String="&lt;/script\b" insensitive="true" endRegion="script" />
+ <IncludeRules context="FindScriptTagClose" />
<StringDetect attribute="Error" context="#stay" String="&lt;script&gt;" insensitive="true" />
- <RegExpr attribute="Error" context="#stay" String="&lt;script\b" insensitive="true" />
+ <WordDetect attribute="Error" context="#stay" String="&lt;script" insensitive="true" />
<IncludeRules context="Base##Mustache/Handlebars (HTML)" includeAttrib="true"/>
</context>
- <context name="Script HTML template" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Element" context="Script HTML template content" char="&gt;" />
+ <context name="Script HTML template" attribute="Error" lineEndContext="#stay">
+ <DetectChar attribute="Element Symbols" context="Script HTML template content" char="&gt;" />
<IncludeRules context="DefaultJS" />
</context>
<context name="Script HTML template content" attribute="Other Text" lineEndContext="#stay">
- <RegExpr attribute="Element" context="El Close 2" String="&lt;/script\b" insensitive="true" endRegion="script" />
+ <IncludeRules context="FindScriptTagClose" />
<StringDetect attribute="Error" context="#stay" String="&lt;script&gt;" insensitive="true" />
- <RegExpr attribute="Error" context="#stay" String="&lt;script\b" insensitive="true" />
+ <WordDetect attribute="Error" context="#stay" String="&lt;script" insensitive="true" />
<IncludeRules context="FindHTML" />
</context>
@@ -291,7 +317,9 @@
<itemData name="Processing Instruction" defStyleNum="dsKeyword" spellChecking="false" />
<itemData name="Doctype" defStyleNum="dsDataType" bold="1" spellChecking="false" />
<itemData name="Element" defStyleNum="dsKeyword" spellChecking="false" />
+ <itemData name="Element Symbols" defStyleNum="dsDataType" spellChecking="false" />
<itemData name="Attribute" defStyleNum="dsOthers" spellChecking="false" />
+ <itemData name="Attribute Separator" defStyleNum="dsOperator" spellChecking="false" />
<itemData name="Value" defStyleNum="dsString" spellChecking="false" />
<itemData name="EntityRef" defStyleNum="dsDecVal" spellChecking="false" />
<itemData name="PEntityRef" defStyleNum="dsDecVal" spellChecking="false" />
@@ -301,7 +329,8 @@
</highlighting>
<general>
<comments>
- <comment name="multiLine" start="&lt;!--" end="--&gt;" />
+ <comment name="multiLine" start="&lt;!--" end="--&gt;" region="comment" />
</comments>
</general>
</language>
+<!-- kate: replace-tabs on; tab-width 2; indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/ini.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/ini.xml
index ce300fff47..56a35e80c7 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/ini.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/ini.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd">
-<language name="INI Files" section="Configuration" extensions="*.ini;*.pls;*.kcfgc" mimetype="" version="5" kateversion="2.4" author="Jan Janssen (medhefgo@web.de)" license="LGPL">
+<!DOCTYPE language>
+<language name="INI Files" section="Configuration" extensions="*.ini;*.cfg;*.pls;*.kcfgc;.gitattributes*;.gitconfig*;.gitmodules*;.editorconfig*" mimetype="" version="13" kateversion="5.0" author="Jan Janssen (medhefgo@web.de)" license="LGPL" priority="1">
<highlighting>
<list name="keywords">
@@ -34,45 +34,55 @@
<contexts>
<context name="ini" attribute="Normal Text" lineEndContext="#stay">
- <RangeDetect attribute="Section" context="#stay" char="[" char1="]" beginRegion="Section" endRegion="Section" />
+ <RangeDetect attribute="Section" context="#stay" char="[" char1="]" beginRegion="Section" endRegion="Section" />
<DetectChar attribute="Assignment" context="Value" char="=" />
- <DetectChar char=";" attribute="Comment" context="Comment" firstNonSpace="true" />
- <DetectChar char="#" attribute="Comment" context="Comment" firstNonSpace="true" />
+ <AnyChar String=";#" attribute="Comment" context="Comment" firstNonSpace="true" />
+ <DetectIdentifier />
+ <DetectSpaces />
+ </context>
+
+ <context name="Value" attribute="Value" lineEndContext="#pop" fallthrough="1" fallthroughContext="#pop!NormalValue">
+ <RegExpr context="#pop!SpecialValue" String="\s*((-?(\d+(\.\d*)?|\.d+)(e\d+)?|On|Off|Defaults?|Localhost|Null|True|False|Yes|No|Normal)\s*$|~?(E_ALL|E_ERROR|E_WARNING|E_PARSE|E_NOTICE|E_STRICT|E_CORE_ERROR|E_CORE_WARNING|E_COMPILE_ERROR|E_COMPILE_WARNING|E_USER_ERROR|E_USER_WARNING|E_USER_NOTICE)\b)" lookAhead="1" insensitive="1"/>
</context>
- <context name="Value" attribute="Value" lineEndContext="#pop" >
+ <context name="SpecialValue" attribute="Value" lineEndContext="#pop">
<Float attribute="Float" />
<Int attribute="Int" />
- <keyword attribute="Keyword" String="keywords" />
+ <keyword attribute="Keyword" String="keywords"/>
+ <DetectChar attribute="Int" char="-" />
+ <DetectSpaces />
+ </context>
+
+ <context name="NormalValue" attribute="Value" lineEndContext="#pop">
</context>
<context name="Comment" attribute="Comment" lineEndContext="#pop">
<DetectSpaces />
- <IncludeRules context="##Alerts" />
+ <IncludeRules context="##Comments" />
<DetectIdentifier />
</context>
</contexts>
<itemDatas>
- <itemData name="Normal Text" defStyleNum="dsDataType" />
- <itemData name="Section" defStyleNum="dsKeyword" />
- <itemData name="Comment" defStyleNum="dsComment" />
- <itemData name="Assignment" defStyleNum="dsOthers" />
- <itemData name="Value" defStyleNum="dsString" />
- <itemData name="Float" defStyleNum="dsFloat" />
- <itemData name="Int" defStyleNum="dsDecVal" />
- <itemData name="Keyword" defStyleNum="dsKeyword" />
+ <itemData name="Normal Text" defStyleNum="dsDataType" spellChecking="false"/>
+ <itemData name="Section" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Assignment" defStyleNum="dsOthers" spellChecking="false"/>
+ <itemData name="Value" defStyleNum="dsString" spellChecking="false"/>
+ <itemData name="Float" defStyleNum="dsFloat" spellChecking="false"/>
+ <itemData name="Int" defStyleNum="dsDecVal" spellChecking="false"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
</itemDatas>
</highlighting>
<general>
<comments>
- <comment name="singleLine" start="#" />
<comment name="singleLine" start=";" />
</comments>
<keywords casesensitive="0" />
</general>
</language>
+<!-- kate: replace-tabs on; indent-width 1; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/java.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/java.xml
index f59415048e..aaeef26c3f 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/java.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/java.xml
@@ -1,10 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd"
+<!DOCTYPE language
[
- <!ENTITY int "[0-9]([0-9_]*[0-9])?">
- <!ENTITY hex "[0-9a-fA-F]([0-9a-fA-F_]*[0-9a-fA-F])?">
+ <!ENTITY int "(?:[0-9]++(_++[0-9]++)*+)">
+ <!ENTITY hex "(?:[0-9a-fA-F]++(_++[0-9a-fA-F]++)*+)">
+ <!ENTITY exp "(?:[eE][-+]?&int;)">
+ <!ENTITY float "(\b&int;(\.((&int;&exp;?+|&exp;)[fFdD]?\b|[fFdD]\b)?|&exp;[fFdD]?\b|[fFdD]\b)|\.&int;&exp;?[fFdD]?\b)">
+ <!ENTITY hexfloat "\b0[xX](&hex;\.?+&hex;?+|\.&hex;?)[pP][-+]?&int;[fFdD]?\b">
]>
-<language name="Java" version="5" kateversion="5.0" section="Sources" extensions="*.java" mimetype="text/x-java" license="LGPL" author="Alfredo Luiz Foltran Fialho (alfoltran@ig.com.br)">
+<language name="Java" version="13" kateversion="5.79" section="Sources" extensions="*.java" mimetype="text/x-java" license="LGPL" author="Alfredo Luiz Foltran Fialho (alfoltran@ig.com.br)">
<highlighting>
<list name="java15">
<item>ACTIVE</item>
@@ -3702,26 +3705,17 @@
<!-- end new classes -->
</list>
+
+ <!-- https://docs.oracle.com/javase/specs/jls/se20/html/jls-3.html#jls-Keyword -->
<list name="keywords">
<item>abstract</item>
- <item>break</item>
- <item>case</item>
- <item>catch</item>
<item>class</item>
- <item>continue</item>
<item>default</item>
- <item>do</item>
- <item>else</item>
<item>enum</item>
<item>extends</item>
<item>false</item>
- <item>finally</item>
- <item>for</item>
- <item>goto</item>
- <item>if</item>
<item>implements</item>
<item>instanceof</item>
- <item>@interface</item>
<item>interface</item>
<item>native</item>
<item>new</item>
@@ -3729,19 +3723,38 @@
<item>private</item>
<item>protected</item>
<item>public</item>
- <item>return</item>
<item>super</item>
<item>strictfp</item>
- <item>switch</item>
<item>synchronized</item>
<item>this</item>
<item>throws</item>
- <item>throw</item>
<item>transient</item>
<item>true</item>
- <item>try</item>
<item>volatile</item>
+ <!-- contextual -->
+ <item>non-sealed</item>
+ <item>permits</item>
+ <item>record</item>
+ <item>sealed</item>
+ </list>
+ <list name="control flow">
+ <item>assert</item>
+ <item>break</item>
+ <item>case</item>
+ <item>catch</item>
+ <item>continue</item>
+ <item>do</item>
+ <item>else</item>
+ <item>finally</item>
+ <item>for</item>
+ <item>goto</item>
+ <item>if</item>
+ <item>return</item>
+ <item>switch</item>
+ <item>throw</item>
+ <item>try</item>
<item>while</item>
+ <item>yield</item>
</list>
<list name="types">
<item>boolean</item>
@@ -3755,87 +3768,158 @@
<item>long</item>
<item>short</item>
<item>static</item>
+ <item>var</item>
<item>void</item>
</list>
+
+ <list name="annotations">
+ <!-- builtin -->
+ <item>@Override</item>
+ <item>@Deprecated</item>
+ <item>@SuppressWarnings</item>
+ <item>@SafeVarargs</item>
+ <item>@FunctionalInterface</item>
+ <item>@interface</item>
+ <!-- java.lang.annotation -->
+ <item>@Retention</item>
+ <item>@Documented</item>
+ <item>@Target</item>
+ <item>@Inherited</item>
+ <item>@Repeatable</item>
+ </list>
+
+ <!-- https://docs.oracle.com/javase/specs/jls/se20/html/jls-3.html -->
+
<contexts>
<context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <DetectSpaces context="#stay"/>
+
+ <DetectChar attribute="Symbol" context="Dot" char="." lookAhead="1"/>
+ <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace1"/>
+ <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace1"/>
+ <DetectChar attribute="Annotation" context="Annotation" char="@" />
+ <DetectChar attribute="Symbol" context="InFunctionCall" char="("/>
+
<!-- Comment next line if you don't use Javadoc tool -->
<IncludeRules context="##Javadoc"/>
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+
+ <AnyChar attribute="Symbol" context="#stay" String="[]&amp;|+,-/*&lt;=&gt;;!%?:~^"/>
+
+ <AnyChar context="Number" String="0123456789" lookAhead="1"/>
+
+ <StringDetect attribute="Text Block" context="TextBlock" String="&quot;&quot;&quot;"/>
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <DetectChar context="Char" char="'" lookAhead="1"/>
+
<keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <keyword attribute="Control Flow" context="#stay" String="control flow"/>
<keyword attribute="Data Type" context="#stay" String="types"/>
<keyword attribute="Java15" context="#stay" String="java15"/>
- <RegExpr attribute="Float" context="#stay" String="(\b&int;\.?|(\b&int;)?\.&int;)([eE][\-\+]?&int;[fFdD]?|[fFdD])\b"/>
- <RegExpr attribute="Float" context="#stay" String="(\b&int;\.|(\b&int;)?\.&int;\b)"/>
- <RegExpr attribute="Float" context="#stay" String="\b0[xX](&hex;\.?|(&hex;)?\.&hex;)[pP][\-\+]?&int;[fFdD]?\b"/>
- <RegExpr attribute="Hex" context="#stay" String="\b0[xX]&hex;[lL]?\b"/>
- <RegExpr attribute="Binary" context="#stay" String="\b0[bB][01]([01_]*[01])?[lL]?\b"/>
- <RegExpr attribute="Octal" context="#stay" String="\b0[0-7]([0-7_]*[0-7])?[lL]?\b"/>
- <RegExpr attribute="Decimal" context="#stay" String="\b(0|[1-9]([0-9_]*[0-9])?)[lL]?\b"/>
- <HlCChar attribute="Char" context="#stay"/>
- <RegExpr attribute="Char" context="#stay" String="'\\u[0-9a-fA-F]{4}'"/>
- <RegExpr attribute="Decimal" context="#stay" String="//\s*BEGIN.*$" beginRegion="Region1"/>
- <RegExpr attribute="Decimal" context="#stay" String="//\s*END.*$" endRegion="Region1"/>
- <DetectChar attribute="String" context="String" char="&quot;"/>
- <RegExpr attribute="Function" context="EnterPrintf" String="\.(format|printf)\b"/>
- <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
- <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
- <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace1"/>
- <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace1"/>
- <RegExpr attribute="Keyword" context="#stay" String="\.{3,3}\s+" />
- <RegExpr attribute="Keyword" context="StaticImports" String="\b(import\s+static)\b" />
- <RegExpr attribute="Keyword" context="Imports" String="\b(package|import)\b" />
- <RegExpr attribute="Function" context="#stay" String="\b[_\w][_\w\d]*(?=[\s]*(/\*\s*\d+\s*\*/\s*)?[(])" />
- <RegExpr attribute="Annotation" context="#stay" String="@[_\w][_\w\d]*" />
- <RegExpr attribute="Symbol" context="Member" String="[.]{1,1}" />
- <DetectChar attribute="Symbol" context="InFunctionCall" char="("/>
- <AnyChar attribute="Symbol" context="#stay" String=":!%&amp;+,-/.*&lt;=&gt;?[]|~^&#59;"/>
+
+ <WordDetect attribute="Keyword" context="Imports" String="package" />
+ <WordDetect attribute="Keyword" context="ImportsOrStaticImports" String="import" />
+
+ <RegExpr attribute="Function" context="#stay" String="\b[_a-zA-Z]\w*(?=[\s]*(/\*\s*\d+\s*\*/\s*)?[(])" />
+ <WordDetect attribute="Keyword" context="ImportsOrStaticImports" String="non-sealed" />
+ <DetectIdentifier attribute="Normal Text"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="Number">
+ <IncludeRules context="FindFloat"/>
+ <RegExpr attribute="Hex" context="#pop" String="\b0[xX]&hex;[lL]?\b"/>
+ <RegExpr attribute="Binary" context="#pop" String="\b0[bB][01]++(_++[01]++)*+[lL]?\b"/>
+ <RegExpr attribute="Octal" context="#pop" String="\b0_*+[0-7]++(_++[0-7_]++)*+[lL]?\b"/>
+ <RegExpr attribute="Decimal" context="#pop" String="\b(0|[1-9][0-9]*+(_++[0-9_]++)*+)[lL]?\b"/>
+ <AnyChar attribute="Error" context="#pop" String="0123456789"/>
</context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="FindFloat">
+ <RegExpr attribute="Float" context="#pop" String="&float;|&hexfloat;"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="Char">
+ <HlCChar attribute="Char" context="#pop"/>
+ <RegExpr attribute="Char" context="#pop" String="'\\u+[0-9a-fA-F]{4}'"/>
+ <RegExpr attribute="Error" context="#pop" String="'(\\(u+[0-9a-fA-F]*|.))?'?"/>
+ </context>
+
<context attribute="Normal Text" lineEndContext="#stay" name="InFunctionCall">
<IncludeRules context="Normal"/>
<DetectChar attribute="Symbol" context="#pop" char=")"/>
</context>
+
<context attribute="String" lineEndContext="#pop" name="String">
- <LineContinue attribute="String" context="#stay"/>
- <HlCStringChar attribute="String Char" context="#stay"/>
- <RegExpr attribute="String Char" context="#stay" String="\\u[0-9a-fA-F]{4}"/>
+ <DetectChar context="StringEscapedChar" char="\" lookAhead="1"/>
<DetectChar attribute="String" context="#pop" char="&quot;"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="EnterPrintf" fallthrough="true" fallthroughContext="#pop">
- <DetectChar attribute="Symbol" context="Printf" char="("/>
+ <context attribute="String" lineEndContext="#pop" name="StringEscapedChar">
+ <HlCStringChar attribute="String Char" context="#pop"/>
+ <RegExpr attribute="String Char" context="#pop" String="\\u+[0-9a-fA-F]{4}"/>
+ <RegExpr attribute="Error" context="#pop" String="\\(u+[0-9a-fA-F]*|.)?"/>
+ </context>
+
+ <context attribute="Text Block" lineEndContext="#stay" name="TextBlock">
+ <DetectChar context="StringEscapedChar" char="\" lookAhead="1"/>
+ <StringDetect attribute="Text Block" context="#pop" String="&quot;&quot;&quot;"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="Printf" fallthroughContext="#pop">
+ <DetectChar attribute="Symbol" context="#pop!InPrintf" char="("/>
<DetectSpaces attribute="Normal Text" context="#stay"/>
</context>
- <context attribute="Normal Text" lineEndContext="#stay" name="Printf">
+ <context attribute="Normal Text" lineEndContext="#stay" name="InPrintf">
<DetectChar attribute="String" context="PrintfString" char="&quot;"/>
<IncludeRules context="InFunctionCall"/>
</context>
<context attribute="PrintfString" lineEndContext="#pop" name="PrintfString">
- <LineContinue attribute="String" context="#stay"/>
- <HlCStringChar attribute="String Char" context="#stay"/>
- <DetectChar attribute="String" context="#pop" char="&quot;"/>
- <RegExpr attribute="String Char" context="#stay" String="%(\d+\$)?(-|#|\+|\ |0|,|\()*\d*(\.\d+)?[a-hosxA-CEGHSX]" />
- <RegExpr attribute="String Char" context="#stay" String="%(\d+\$)?(-|#|\+|\ |0|,|\()*\d*(t|T)(a|A|b|B|c|C|d|D|e|F|h|H|I|j|k|l|L|m|M|N|p|P|Q|r|R|s|S|T|y|Y|z|Z)" />
- <RegExpr attribute="String Char" context="#stay" String="%(%|n)" />
+ <IncludeRules context="String"/>
+ <RegExpr attribute="String Char" context="#stay" String="%((\d+\$)?[-#+ 0,(]*\d*((\.\d+)?[a-hosxA-CEGHSX]|[tT][aAbBcCdDeFhHIjklLmMNpPQrRsSTyYzZ])|[%n])" />
</context>
- <context attribute="Normal Text" lineEndContext="#pop" name="Member" fallthrough="true" fallthroughContext="#pop">
- <RegExpr attribute="Function" context="#pop" String="\b[_a-zA-Z]\w*(?=[\s]*)" />
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="Dot">
+ <StringDetect attribute="Keyword" context="#pop" String="..." />
+ <IncludeRules context="FindFloat"/>
+ <DetectChar attribute="Symbol" context="#pop!Member" char="." />
</context>
- <context attribute="Normal Text" lineEndContext="#pop" name="StaticImports">
- <RegExpr attribute="StaticImports" context="#pop" String="\s*.*;" />
+ <context attribute="Normal Text" lineEndContext="#stay" name="Member" fallthroughContext="#pop">
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <WordDetect attribute="Function" context="#pop!Printf" String="printf"/>
+ <WordDetect attribute="Function" context="#pop!Printf" String="format"/>
+ <DetectIdentifier attribute="Function" context="#pop"/>
</context>
- <context attribute="Normal Text" lineEndContext="#pop" name="Imports">
- <RegExpr attribute="Imports" context="#pop" String="\s*.*;" />
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="Annotation" fallthroughContext="#pop">
+ <DetectIdentifier attribute="Annotation" context="#pop"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="ImportsOrStaticImports" fallthroughContext="#pop!Imports">
+ <DetectSpaces attribute="Normal Text"/>
+ <WordDetect attribute="Keyword" context="#pop!StaticImports" String="static"/>
</context>
+ <context attribute="StaticImports" lineEndContext="#pop" name="StaticImports">
+ <IncludeRules context="Imports"/>
+ </context>
+ <context attribute="Imports" lineEndContext="#pop" name="Imports">
+ <AnyChar attribute="Symbol" context="#stay" String=".*"/>
+ <DetectChar attribute="Symbol" context="#pop" char=";"/>
+ </context>
+
<context attribute="Comment" lineEndContext="#pop" name="Commentar 1">
- <IncludeRules context="##Alerts"/>
+ <DetectSpaces />
+ <IncludeRules context="##Comments"/>
</context>
<context attribute="Comment" lineEndContext="#stay" name="Commentar 2">
<Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/>
- <IncludeRules context="##Alerts"/>
+ <DetectSpaces />
+ <IncludeRules context="##Comments"/>
</context>
+
</contexts>
<itemDatas>
<itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false"/>
<itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="Control Flow" defStyleNum="dsControlFlow" spellChecking="false"/>
<itemData name="Annotation" defStyleNum="dsAttribute" spellChecking="false"/>
<itemData name="Function" defStyleNum="dsFunction" spellChecking="false"/>
<itemData name="StaticImports" defStyleNum="dsImport" spellChecking="false"/>
@@ -3847,19 +3931,22 @@
<itemData name="Binary" defStyleNum="dsBaseN" spellChecking="false"/>
<itemData name="Float" defStyleNum="dsFloat" spellChecking="false"/>
<itemData name="Char" defStyleNum="dsChar" spellChecking="false"/>
+ <itemData name="Text Block" defStyleNum="dsString"/>
<itemData name="String" defStyleNum="dsString"/>
<itemData name="String Char" defStyleNum="dsSpecialChar" spellChecking="false"/>
<itemData name="PrintfString" defStyleNum="dsString"/>
<itemData name="Comment" defStyleNum="dsComment"/>
- <itemData name="Symbol" defStyleNum="dsNormal" spellChecking="false"/>
+ <itemData name="Symbol" defStyleNum="dsOperator" spellChecking="false"/>
<itemData name="Java15" defStyleNum="dsBuiltIn" spellChecking="false"/>
+ <itemData name="Error" defStyleNum="dsError" spellChecking="false"/>
</itemDatas>
</highlighting>
<general>
<comments>
- <comment name="singleLine" start="//"/>
- <comment name="multiLine" start="/*" end="*/"/>
+ <comment name="singleLine" start="//" position="afterwhitespace"/>
+ <comment name="multiLine" start="/*" end="*/" region="Comment"/>
</comments>
<keywords casesensitive="1"/>
</general>
</language>
+<!-- kate: replace-tabs off; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/javadoc.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/javadoc.xml
index e72488b221..84c7841cad 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/javadoc.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/javadoc.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd">
-<language name="Javadoc" version="3" kateversion="5.0" section="Markup" extensions="" license="LGPL" author="Alfredo Luiz Foltran Fialho (alfoltran@ig.com.br)">
+<!DOCTYPE language>
+<language name="Javadoc" version="7" kateversion="5.79" section="Markup" extensions="" license="LGPL" author="Alfredo Luiz Foltran Fialho (alfoltran@ig.com.br)">
<highlighting>
<contexts>
<context name="Start" attribute="Normal Text" lineEndContext="#stay">
@@ -12,74 +12,52 @@
</context>
<context attribute="JavadocFS" lineEndContext="#stay" name="JavadocFSar">
<Detect2Chars attribute="JavadocFS" context="#pop" char="*" char1="/" endRegion="Javadoc"/>
- <RegExpr attribute="JavadocFS" context="Javadocar" String="(!|\?)" />
- <RegExpr attribute="JavadocFS" context="Javadocar" String="(\.\s*$)" />
- <RegExpr attribute="JavadocFS" context="Javadocar" String="(\.\s)(?![\da-z])" />
- <RegExpr attribute="JavadocFS" context="Javadocar" String="\**\s*(?=@(author|deprecated|exception|param|return|see|serial|serialData|serialField|since|throws|version)(\s|$))" firstNonSpace="true"/>
- <StringDetect attribute="InlineTag" context="LiteralTagar" String="{@code " />
- <StringDetect attribute="InlineTag" context="LiteralTagar" String="{@code&#009;" />
- <StringDetect attribute="InlineTag" context="#stay" String="{@docRoot}" />
- <StringDetect attribute="InlineTag" context="#stay" String="{@inheritDoc}" />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@link " />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@link&#009;" />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@linkplain " />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@linkplain&#009;" />
- <StringDetect attribute="InlineTag" context="LiteralTagar" String="{@literal " />
- <StringDetect attribute="InlineTag" context="LiteralTagar" String="{@literal&#009;" />
- <StringDetect attribute="InlineTag" context="#stay" String="{@value}" />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@value " />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@value&#009;" />
- <IncludeRules context="##Alerts"/>
+ <RegExpr attribute="JavadocFS" context="Javadocar" String="[!?.](\s*$|\s(?![\da-z]))|^\s*\**\s*(?=@(author|deprecated|exception|param|return|see|serial|serialData|serialField|since|throws|version)(\s|$))"/>
+ <IncludeRules context="FindInlineTag"/>
+ <IncludeRules context="##Comments"/>
<IncludeRules context="##HTML"/>
</context>
+ <context attribute="Javadoc" lineEndContext="#stay" name="FindInlineTag">
+ <WordDetect attribute="InlineTag" context="LiteralTagar" String="{@code"/>
+ <StringDetect attribute="InlineTag" context="#stay" String="{@docRoot}"/>
+ <StringDetect attribute="InlineTag" context="#stay" String="{@inheritDoc}"/>
+ <WordDetect attribute="InlineTag" context="InlineTagar" String="{@link"/>
+ <WordDetect attribute="InlineTag" context="InlineTagar" String="{@linkplain"/>
+ <WordDetect attribute="InlineTag" context="LiteralTagar" String="{@literal"/>
+ <StringDetect attribute="InlineTag" context="#stay" String="{@value}"/>
+ <WordDetect attribute="InlineTag" context="InlineTagar" String="{@value"/>
+ <WordDetect attribute="InlineTag" context="LiteralTagar" String="{@summary"/>
+ <WordDetect attribute="InlineTag" context="LiteralTagar" String="{@systemProperty"/>
+ </context>
<context attribute="Javadoc" lineEndContext="#stay" name="Javadocar">
<Detect2Chars attribute="JavadocFS" context="#pop#pop" char="*" char1="/" endRegion="Javadoc"/>
- <RegExpr attribute="JavadocFS" context="#stay" String="\*+(?!/)" firstNonSpace="true"/>
- <StringDetect attribute="BlockTag" context="#stay" String="@author " />
- <StringDetect attribute="BlockTag" context="#stay" String="@deprecated " />
- <StringDetect attribute="BlockTag" context="JavadocParam" String="@exception " />
- <StringDetect attribute="BlockTag" context="JavadocParam" String="@param " />
- <StringDetect attribute="BlockTag" context="#stay" String="@return " />
- <StringDetect attribute="BlockTag" context="SeeTag" String="@see "/>
- <StringDetect attribute="BlockTag" context="#stay" String="@serial " />
- <StringDetect attribute="BlockTag" context="#stay" String="@serialData " />
- <StringDetect attribute="BlockTag" context="#stay" String="@serialField " />
- <StringDetect attribute="BlockTag" context="#stay" String="@since " />
- <StringDetect attribute="BlockTag" context="JavadocParam" String="@throws " />
- <StringDetect attribute="BlockTag" context="#stay" String="@version " />
- <StringDetect attribute="BlockTag" context="#stay" String="@author&#009;" />
- <StringDetect attribute="BlockTag" context="#stay" String="@deprecated&#009;" />
- <StringDetect attribute="BlockTag" context="JavadocParam" String="@exception&#009;" />
- <StringDetect attribute="BlockTag" context="JavadocParam" String="@param&#009;" />
- <StringDetect attribute="BlockTag" context="#stay" String="@return&#009;" />
- <StringDetect attribute="BlockTag" context="SeeTag" String="@see&#009;" />
- <StringDetect attribute="BlockTag" context="#stay" String="@serial&#009;" />
- <StringDetect attribute="BlockTag" context="#stay" String="@serialData&#009;" />
- <StringDetect attribute="BlockTag" context="#stay" String="@serialField&#009;" />
- <StringDetect attribute="BlockTag" context="#stay" String="@since&#009;" />
- <StringDetect attribute="BlockTag" context="JavadocParam" String="@throws&#009;" />
- <StringDetect attribute="BlockTag" context="#stay" String="@version&#009;" />
- <StringDetect attribute="InlineTag" context="LiteralTagar" String="{@code " />
- <StringDetect attribute="InlineTag" context="LiteralTagar" String="{@code&#009;" />
- <StringDetect attribute="InlineTag" context="#stay" String="{@docRoot}" />
- <StringDetect attribute="InlineTag" context="#stay" String="{@inheritDoc}" />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@link " />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@link&#009;" />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@linkplain " />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@linkplain&#009;" />
- <StringDetect attribute="InlineTag" context="LiteralTagar" String="{@literal " />
- <StringDetect attribute="InlineTag" context="LiteralTagar" String="{@literal&#009;" />
- <StringDetect attribute="InlineTag" context="#stay" String="{@value}" />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@value " />
- <StringDetect attribute="InlineTag" context="InlineTagar" String="{@value&#009;" />
+ <DetectChar attribute="JavadocFS" context="JavadocFSStarPrefix" char="*" firstNonSpace="true"/>
+ <WordDetect attribute="BlockTag" context="#stay" String="@author"/>
+ <WordDetect attribute="BlockTag" context="#stay" String="@deprecated"/>
+ <WordDetect attribute="BlockTag" context="JavadocParam" String="@exception"/>
+ <WordDetect attribute="BlockTag" context="JavadocParam" String="@param"/>
+ <WordDetect attribute="BlockTag" context="#stay" String="@return"/>
+ <WordDetect attribute="BlockTag" context="SeeTag" String="@see"/>
+ <WordDetect attribute="BlockTag" context="#stay" String="@serial"/>
+ <WordDetect attribute="BlockTag" context="#stay" String="@serialData"/>
+ <WordDetect attribute="BlockTag" context="#stay" String="@serialField"/>
+ <WordDetect attribute="BlockTag" context="#stay" String="@since"/>
+ <WordDetect attribute="BlockTag" context="JavadocParam" String="@throws"/>
+ <WordDetect attribute="BlockTag" context="#stay" String="@version"/>
+ <IncludeRules context="FindInlineTag"/>
<IncludeRules context="##Alerts"/>
<IncludeRules context="##HTML"/>
</context>
- <context attribute="Javadoc" lineEndContext="#pop" name="JavadocParam">
- <DetectSpaces />
- <RegExpr attribute="JavadocParam" context="#pop#pop" String="\S*(?=\*/)" />
- <RegExpr attribute="JavadocParam" context="#pop" String="\S*(\s|$)" />
- <IncludeRules context="##Alerts"/>
+ <context attribute="Javadoc" lineEndContext="#pop" name="JavadocFSStarPrefix" fallthroughContext="#pop">
+ <Detect2Chars attribute="JavadocFS" context="#pop#pop#pop" char="*" char1="/" endRegion="Javadoc"/>
+ <DetectChar attribute="JavadocFS" context="#stay" char="*"/>
+ </context>
+ <context attribute="Javadoc" lineEndContext="#pop" name="JavadocParam" fallthroughContext="JavadocParamIdent">
+ <DetectSpaces context="JavadocParamIdent"/>
+ </context>
+ <context attribute="JavadocParam" lineEndContext="#pop#pop" name="JavadocParamIdent">
+ <DetectSpaces attribute="Javadoc" context="#pop#pop"/>
+ <Detect2Chars attribute="JavadocFS" context="#pop#pop#pop#pop" char="*" char1="/" endRegion="Javadoc"/>
</context>
<context attribute="InlineTag" name="InlineTagar" lineEndContext="#stay">
<DetectChar attribute="InlineTag" context="#pop" char="}" />
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/json.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/json.xml
index 28ad8e8a33..f302ff1369 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/json.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/json.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd">
+<!DOCTYPE language>
<!--
***************************************************************************
** Writing a Kate Highlighting XML File
@@ -10,9 +10,10 @@
**
** The application/json Media Type for JavaScript Object Notation (JSON)
** http://tools.ietf.org/html/rfc4627
+ ** https://www.json.org/json-en.html
***************************************************************************
-->
-<language name="JSON" section="Markup" version="3" kateversion="2.4" extensions="*.json;.kateproject;.arcconfig;*.gltf" mimetype="application/json" author="Sebastian Pipping (sebastian@pipping.org)" license="GPL">
+<language name="JSON" section="Markup" version="8" kateversion="2.4" extensions="*.json;.kateproject;.arcconfig;*.geojson;*.gltf;*.theme" mimetype="application/json" author="Sebastian Pipping (sebastian@pipping.org)" license="GPL">
<highlighting>
<list name="Constants">
<item>null</item>
@@ -39,43 +40,47 @@
<context name="String_Key" lineEndContext="#stay" attribute="Style_String_Key">
<DetectChar char="&quot;" context="#pop" attribute="Style_String_Key" />
- <RegExpr String="\\(?:[&quot;\\/bfnrt]|u[0-9a-fA-f]{4})" context="#stay" insensitive="false" minimal="false" attribute="Style_String_Key_Char" />
+ <DetectChar char="\" context="String_Key_Char" lookAhead="1"/>
+ </context>
+
+ <context name="String_Key_Char" lineEndContext="#stay" attribute="Style_Normal">
+ <RegExpr String="\\(?:[&quot;\\/bfnrt]|u[0-9a-fA-f]{4})" context="#pop" attribute="Style_String_Key_Char" />
+ <RegExpr String="\\(u[0-9a-fA-f]+|.)?" context="#pop" attribute="Style_Error" />
</context>
<context name="Value" lineEndContext="#stay" attribute="Style_Error" >
+ <AnyChar String="}," context="#pop" lookAhead="true" />
+ <IncludeRules context="JsonValue"/>
+ </context>
+
+ <context name="JsonValue" lineEndContext="#stay" attribute="Style_Error" >
<DetectChar char="&quot;" context="String_Value" attribute="Style_String_Value" />
<DetectChar char="{" context="Pair" beginRegion="Region_Object" attribute="Style_Seperator_Pair" />
<DetectChar char="[" context="Array" beginRegion="Region_Array" attribute="Style_Seperator_Array" />
- <DetectChar char="}" context="#pop" lookAhead="true" />
- <DetectChar char="," context="#pop" lookAhead="true" />
<DetectSpaces context="#stay" attribute="Style_Normal" />
<keyword String="Constants" context="#stay" attribute="Style_Keyword" />
- <RegExpr String="-?(?:[0-9]|[1-9][0-9]+)\.[0-9]+(?:[eE][+-]?[0-9]+)?" context="#stay" insensitive="false" minimal="false" attribute="Style_Float" />
- <RegExpr String="-?(?:[0-9]|[1-9][0-9]+)(?:[eE][+-]?[0-9]+)?" context="#stay" insensitive="false" minimal="false" attribute="Style_Decimal" />
+ <RegExpr String="-?\b([1-9][0-9]*\.[0-9]+(?:[eE][+-]?[0-9]+)?)" context="#stay" attribute="Style_Float" />
+ <RegExpr String="-?\b(0\b|[1-9][0-9]*(?:[eE][+-]?[0-9]+)?)" context="#stay" attribute="Style_Decimal" />
</context>
<context name="String_Value" lineEndContext="#stay" attribute="Style_String_Value">
<DetectChar char="&quot;" context="#pop" attribute="Style_String_Value" />
- <RegExpr String="\\(?:[&quot;\\/bfnrt]|u[0-9a-fA-f]{4})" context="#stay" insensitive="false" minimal="false" attribute="Style_String_Value_Char" />
+ <DetectChar char="\" context="String_Key_Value" lookAhead="1" />
+ </context>
+
+ <context name="String_Key_Value" lineEndContext="#stay" attribute="Style_Normal">
+ <RegExpr String="\\(?:[&quot;\\/bfnrt]|u[0-9a-fA-f]{4})" context="#pop" attribute="Style_String_Value_Char" />
+ <RegExpr String="\\(u[0-9a-fA-f]+|.)?" context="#pop" attribute="Style_Error" />
</context>
<context name="Array" lineEndContext="#stay" attribute="Style_Error">
<DetectChar char="," context="#stay" attribute="Style_Seperator_Array" />
<DetectChar char="]" context="#pop" endRegion="Region_Array" attribute="Style_Seperator_Array" />
- <DetectChar char="{" context="Pair" beginRegion="Region_Object" attribute="Style_Seperator_Pair" />
- <DetectChar char="[" context="Array" beginRegion="Region_Array" attribute="Style_Seperator_Array" />
- <DetectChar char="&quot;" context="String_Value" attribute="Style_String_Value" />
-
- <DetectSpaces context="#stay" attribute="Style_Normal" />
-
- <keyword String="Constants" context="#stay" attribute="Style_Keyword" />
-
- <RegExpr String="-?(?:[0-9]|[1-9][0-9]+)\.[0-9]+(?:[eE][+-]?[0-9]+)?" context="#stay" insensitive="false" minimal="false" attribute="Style_Float" />
- <RegExpr String="-?(?:[0-9]|[1-9][0-9]+)(?:[eE][+-]?[0-9]+)?" context="#stay" insensitive="false" minimal="false" attribute="Style_Decimal" />
+ <IncludeRules context="JsonValue"/>
</context>
</contexts>
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.GPLv2 b/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.GPLv2
new file mode 100644
index 0000000000..b9033aeae6
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.GPLv2
@@ -0,0 +1,282 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+Preamble
+
+ The licenses for most software are designed to take away your freedom
+to share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software
+--to make sure the software is free for all its users. This General
+Public License applies to most of the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+(Some other Free Software Foundation software is covered by the GNU
+Lesser General Public License instead.) You can apply it to your
+programs, too.
+
+When we speak of free software, we are referring to freedom, not price.
+Our General Public Licenses are designed to make sure that you have the
+freedom to distribute copies of free software (and charge for this
+service if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone
+to deny you these rights or to ask you to surrender the rights. These
+restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis
+ or for a fee, you must give the recipients all the rights that you
+have. You must make sure that they, too, receive or can get the source
+code. And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary. To prevent this, we have made it clear that any patent
+must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a
+notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of running
+the Program is not restricted, and the output from the Program is
+covered only if its contents constitute a work based on the Program
+(independent of having been made by running the Program). Whether that
+is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously
+and appropriately publish on each copy an appropriate copyright notice
+and disclaimer of warranty; keep intact all the notices that refer to
+this License and to the absence of any warranty; and give any other
+recipients of the Program a copy of this License along with the
+Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of
+it, thus forming a work based on the Program, and copy and distribute
+such modifications or work under the terms of Section 1 above, provided
+that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but does
+ not normally print such an announcement, your work based on the
+ Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of a
+storage or distribution medium does not bring the other work under the
+scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software
+ interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your cost
+ of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to control
+compilation and installation of the executable. However, as a special
+exception, the source code distributed need not include anything that
+is normally distributed (in either source or binary form) with the
+major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies
+the executable.
+
+If distribution of executable or object code is made by offering access
+to copy from a designated place, then offering equivalent access to
+copy the source code from the same place counts as distribution of the
+source code, even though third parties are not compelled to copy the
+source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt otherwise
+to copy, modify, sublicense or distribute the Program is void, and will
+automatically terminate your rights under this License. However,
+parties who have received copies, or rights, from you under this License
+will not have their licenses terminated so long as such parties remain
+in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further restrictions
+on the recipients' exercise of the rights granted herein. You are not
+responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent license
+would not permit royalty-free redistribution of the Program by all
+those who receive copies directly or indirectly through you, then the
+only way you could satisfy both it and this License would be to refrain
+entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License may
+add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among countries
+not thus excluded. In such case, this License incorporates the limitation
+as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail
+to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Program does not specify a version
+number of this License, you may choose any version ever published by
+the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the
+author to ask for permission. For software which is copyrighted by
+the Free Software Foundation, write to the Free Software Foundation;
+we sometimes make exceptions for this. Our decision will be guided by
+the two goals of preserving the free status of all derivatives of our
+free software and of promoting the sharing and reuse of software
+generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
+YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY
+MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE
+TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+END OF TERMS AND CONDITIONS
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.GPLv3 b/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.GPLv3
new file mode 100644
index 0000000000..94a9ed024d
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.GPLv3
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.LGPLv21 b/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.LGPLv21
new file mode 100644
index 0000000000..ec3ce5f2b0
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.LGPLv21
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.LGPLv3 b/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.LGPLv3
new file mode 100644
index 0000000000..eb33241551
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/licenses/LICENSE.LGPLv3
@@ -0,0 +1,163 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>
+Everyone is permitted to copy and distribute verbatim copies of this
+licensedocument, but changing it is not allowed.
+
+This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+0. Additional Definitions.
+
+ As used herein, “this License” refers to version 3 of the GNU Lesser
+General Public License, and the “GNU GPL” refers to version 3 of the
+GNU General Public License.
+
+ “The Library” refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An “Application” is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A “Combined Work” is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the “Linked
+Version”.
+
+ The “Minimal Corresponding Source” for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The “Corresponding Application Code” for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort
+ to ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this
+ license document.
+
+4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that, taken
+together, effectively do not restrict modification of the portions of
+the Library contained in the Combined Work and reverse engineering for
+debugging such modifications, if you also do each of the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this
+ license document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of
+ this License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with
+ the Library. A suitable mechanism is one that (a) uses at run
+ time a copy of the Library already present on the user's
+ computer system, and (b) will operate properly with a modified
+ version of the Library that is interface-compatible with the
+ Linked Version.
+
+ e) Provide Installation Information, but only if you would
+ otherwise be required to provide such information under section 6
+ of the GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the Application
+ with a modified version of the Linked Version. (If you use option
+ 4d0, the Installation Information must accompany the Minimal
+ Corresponding Source and Corresponding Application Code. If you
+ use option 4d1, you must provide the Installation Information in
+ the manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.)
+
+5. Combined Libraries.
+
+ You may place library facilities that are a work based on the Library
+side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities, conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of
+ it is a work based on the Library, and explaining where to find
+ the accompanying uncombined form of the same work.
+
+6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+as you received it specifies that a certain numbered version of the
+GNU Lesser General Public License “or any later version” applies to
+it, you have the option of following the terms and conditions either
+of that published version or of any later version published by the
+Free Software Foundation. If the Library as you received it does not
+specify a version number of the GNU Lesser General Public License,
+you may choose any version of the GNU Lesser General Public License
+ever published by the Free Software Foundation.
+
+If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the Library.
+
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/makefile.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/makefile.xml
deleted file mode 100644
index 6067a2470f..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/makefile.xml
+++ /dev/null
@@ -1,498 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd">
-<!-- Makefile syntaxfile v0.9 by Per Wigren <wigren@home.se> -->
-<!-- Modified by Joseph Wenninger <jowenn@kde.org> -->
-<!-- Modified by Rui Santana <santana.rui@gmail.com> -->
-<!-- v2.0 by Andreas Nordal <andreas.nordal@gmail.com> -->
-<!-- small priority to allow for example Makefile.cpp to be detected as cpp file -->
-<!-- v2.1 by Alex Turbov <i.zaufi@gmail.com>
- improve comments handling -->
-<!-- v4 by Alex Richardson <arichardson.kde@gmail.com>
- added bmake support -->
-<language name="Makefile" section="Other"
- version="7" kateversion="3.4"
- extensions="GNUmakefile;Makefile;makefile;GNUmakefile.*;Makefile.*;makefile.*;*.mk"
- mimetype="text/x-makefile" priority="11"
- author="Per Wigren (wigren@home.se)" license="">
- <highlighting>
- <list name = "keywords" >
- <item>include</item>
- <item>-include</item>
- <item>define</item>
- <item>endef</item>
- </list>
- <!-- https://www.gnu.org/software/make/manual/html_node/Conditional-Syntax.html -->
- <list name="gmake_if_keywords">
- <item>if</item>
- <item>ifeq</item>
- <item>ifneq</item>
- <item>ifdef</item>
- <item>ifndef</item>
- </list>
- <list name="gmake_else_keywords"><item>else</item></list>
- <list name="gmake_endif_keywords"><item>endif</item></list>
- <!-- bmake statements: https://www.freebsd.org/cgi/man.cgi?make(1)#INCLUDE_STATEMENTS,_CONDITIONALS_AND_FOR_LOOPS -->
- <list name="bmake_if_keywords">
- <item>.if</item>
- <item>.ifdef</item>
- <item>.ifndef</item>
- <item>.ifmake</item>
- <item>.ifnmake</item>
- </list>
- <list name="bmake_else_keywords">
- <item>.elif</item>
- <item>.elifdef</item>
- <item>.elifndef</item>
- <item>.elifmake</item>
- <item>.elifnmake</item>
- <item>.else</item> <!-- TODO: anything following .else should be highlighted as an error -->
- </list>
- <list name="bmake_endif_keywords"><item>.endif</item></list>
-
- <list name="bmake_expressions">
- <item>defined</item>
- <item>empty</item>
- <item>exists</item>
- <item>target</item>
- <item>commands</item>
- </list>
-
- <list name="bmake_include_stmt">
- <item>.include</item>
- <item>.-include</item>
- <item>.sinclude</item>
- <!-- For compat bmake also handles include but this is already highlighted. <item>include</item> -->
- </list>
- <list name="bmake_message_stmt">
- <item>.info</item>
- <item>.warning</item>
- <item>.error</item>
- </list>
- <!-- Use single element list here so that .for/.endfor also shows up in code completion -->
- <list name="bmake_for_stmt"><item>.for</item></list>
- <list name="bmake_endfor_stmt"><item>.endfor</item></list>
-
- <!-- https://www.freebsd.org/cgi/man.cgi?make(1)#SPECIAL_TARGETS -->
- <list name="bmake_special_targets">
- <item>.BEGIN</item>
- <item>.DEFAULT</item>
- <item>.END</item>
- <item>.ERROR</item>
- <item>.IGNORE</item>
- <item>.INTERRUPT</item>
- <item>.MAIN</item>
- <item>.MAKEFLAGS</item>
- <item>.NOPATH</item>
- <item>.NOTPARALLEL</item>
- <item>.NO_PARALLEL</item>
- <item>.OBJDIR</item>
- <item>.ORDER</item>
- <item>.PATH</item>
- <item>.PHONY</item>
- <item>.PRECIOUS</item>
- <item>.SHELL</item>
- <item>.SILENT</item>
- <item>.STALE</item>
- <item>.SUFFIXES</item>
- </list>
- <!-- https://www.freebsd.org/cgi/man.cgi?make(1)#SPECIAL_SOURCES_(ATTRIBUTES) -->
- <list name="bmake_special_sources">
- <item>.EXEC</item>
- <item>.IGNORE</item>
- <item>.MADE</item>
- <item>.MAKE</item>
- <item>.META</item>
- <item>.NOMETA</item>
- <item>.NOMETA_CMP</item>
- <item>.NOPATH</item>
- <item>.NOTMAIN</item>
- <item>.OPTIONAL</item>
- <item>.PHONY</item>
- <item>.PRECIOUS</item>
- <item>.RECURSIVE</item>
- <item>.SILENT</item>
- <item>.USE</item>
- <item>.USEBEFORE</item>
- <item>.WAIT</item>
- </list>
-
- <list name="bmake_other_stmts">
- <item>.unexport-env</item>
- <item>.unexport</item>
- <item>.undef</item>
- <item>.export-env</item>
- <item>.export</item>
- </list>
-
- <list name = "functions">
- <item>call</item>
- <item>subst</item>
- <item>patsubst</item>
- <item>strip</item>
- <item>findstring</item>
- <item>filter</item>
- <item>filter-out</item>
- <item>sort</item>
- <item>word</item>
- <item>wordlist</item>
- <item>words</item>
- <item>firstword</item>
- <item>lastword</item>
- <item>dir</item>
- <item>notdir</item>
- <item>suffix</item>
- <item>basename</item>
- <item>addsuffix</item>
- <item>addprefix</item>
- <item>join</item>
- <item>wildcard</item>
- <item>realpath</item>
- <item>abspath</item>
- <item>if</item>
- <item>or</item>
- <item>and</item>
- <item>foreach</item>
- <item>value</item>
- <item>eval</item>
- <item>origin</item>
- <item>flavor</item>
- <item>shell</item>
- <item>error</item>
- <item>warning</item>
- <item>info</item>
- </list>
- <contexts>
- <context name="normal" attribute="Normal" lineEndContext="#stay">
- <DetectSpaces/>
- <DetectChar attribute="Comment" context="Comment" char="#"/>
- <keyword attribute="ControlFlow" String="bmake_if_keywords" context="bmake_conditional" beginRegion="bmake_if" firstNonSpace="true"/>
- <!--TODO: trailing non-space after .else should be highlighted as an error -->
- <keyword attribute="ControlFlow" String="bmake_else_keywords" context="bmake_conditional" endRegion="bmake_if" beginRegion="bmake_if" firstNonSpace="true"/>
- <keyword attribute="ControlFlow" String="bmake_endif_keywords" context="#stay" endRegion="bmake_if"/>
-
- <keyword attribute="ControlFlow" String="gmake_if_keywords" context="#stay" beginRegion="gmake_if" firstNonSpace="true"/>
- <!--TODO: trailing non-space after .else should be highlighted as an error -->
- <keyword attribute="ControlFlow" String="gmake_else_keywords" context="gmake_else" endRegion="gmake_if" beginRegion="gmake_if" firstNonSpace="true"/>
- <keyword attribute="ControlFlow" String="gmake_endif_keywords" context="#stay" endRegion="gmake_if"/>
-
- <keyword attribute="Keyword" context="#stay" String="keywords"/>
- <keyword attribute="Keyword" context="bmake_other_stmts" String="bmake_other_stmts" firstNonSpace="true"/>
- <RegExpr attribute="Variable" context="assign" String="[^\s+:?+]*\s*(?=:=|=|\+=|\?=)"/>
- <keyword attribute="SpecialTarget" context="bmake_special_target" String="bmake_special_targets" firstNonSpace="true"/>
- <RegExpr attribute="SpecialTarget" context="prereq" String="\.PATH\.[^:]*:" firstNonSpace="true"/>
- <keyword attribute="Keyword" context="bmake_include" String="bmake_include_stmt" firstNonSpace="true"/>
- <keyword attribute="Keyword" context="bmake_message" String="bmake_message_stmt" firstNonSpace="true"/>
- <keyword attribute="Keyword" context="bmake_for_loop" String="bmake_for_stmt" firstNonSpace="true" beginRegion="for"/>
- <keyword attribute="Keyword" context="#stay" String="bmake_endfor_stmt" firstNonSpace="true" endRegion="for"/>
-
- <RegExpr attribute="Section" context="prereq" String="^\.[^.][^:]*:"/>
- <RegExpr attribute="Target" context="prereq" String="^[^:]*:"/>
- <DetectIdentifier/>
- <DetectChar attribute="String" context="string&quot;" char="&quot;"/>
- <DetectChar attribute="String" context="string'" char="'"/>
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- <Detect2Chars attribute="Special" context="#stay" char="\" char1="#"/>
- <Detect2Chars attribute="Special" context="#stay" char="\" char1="\"/>
- <AnyChar attribute="Operator" context="silent" String="@-" firstNonSpace="1"/>
- </context>
-
- <context name="strings_and_vars" attribute="Normal" lineEndContext="#pop">
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- <DetectChar attribute="String" context="string&quot;" char="&quot;"/>
- <DetectChar attribute="String" context="string'" char="'"/>
- <DetectChar attribute="Comment" context="Comment" char="#"/>
- </context>
-
- <context name="gmake_else" attribute="Error" lineEndContext="#pop">
- <DetectSpaces attribute="Normal"/>
- <keyword attribute="ControlFlow" String="gmake_if_keywords" context="#stay"/>
- <IncludeRules context="strings_and_vars"/>
- </context>
-
- <context name="bmake_include" attribute="Normal" lineEndContext="#pop">
- <DetectSpaces />
- <RangeDetect char="&lt;" char1="&gt;" context="#stay" attribute="Include" />
- <RangeDetect char="&quot;" char1="&quot;" context="#stay" attribute="Include" />
- </context>
-
-
- <context name="bmake_conditional" attribute="Normal" lineEndContext="#pop">
- <DetectSpaces/>
- <IncludeRules context="strings_and_vars"/>
- <keyword attribute="Builtin" context="bmake_expression" String="bmake_expressions"/>
- <Detect2Chars attribute="RealOperator" context="#stay" char="&amp;" char1="&amp;"/>
- <Detect2Chars attribute="RealOperator" context="#stay" char="|" char1="|"/>
- <Detect2Chars attribute="RealOperator" context="#stay" char="!" char1="="/>
- <Detect2Chars attribute="RealOperator" context="#stay" char="=" char1="="/>
- <DetectChar attribute="RealOperator" context="#stay" char="!"/>
- <HlCHex attribute="Number" context="#stay"/>
- <Int attribute="Number" context="#stay"/>
- <DetectIdentifier attribute="Variable" context="#stay"/>
- <LineContinue attribute="Operator" context="#stay"/>
- </context>
-
- <context name="bmake_for_loop" attribute="Variable" lineEndContext="#pop">
- <DetectSpaces/>
- <IncludeRules context="strings_and_vars"/>
- <WordDetect attribute="RealOperator" context="#stay" String="in" />
- </context>
-
- <context name="bmake_other_stmts" attribute="Variable" lineEndContext="#pop">
- <DetectSpaces/>
- <IncludeRules context="strings_and_vars"/>
- </context>
-
- <context name="bmake_expression" attribute="Variable" lineEndContext="#pop">
- <DetectSpaces/>
- <IncludeRules context="strings_and_vars"/>
- <DetectChar attribute="Normal" context="#stay" char="("/>
- <DetectChar attribute="Normal" context="#pop" char=")"/>
- <DetectIdentifier attribute="Variable"/>
- </context>
-
- <context name="bmake_special_target" attribute="Error" lineEndContext="rule">
- <!-- anything but spaces before the : should be an error -->
- <DetectSpaces attribute="Normal" />
- <DetectChar attribute="SpecialTarget" context="#pop!prereq" char=":"/>
- </context>
-
- <context name="bmake_message" attribute="String" lineEndContext="#pop">
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- </context>
-
- <context name="prereq" attribute="Prereq" lineEndContext="rule">
- <DetectSpaces/>
- <keyword attribute="SpecialPrereq" context="#stay" String="bmake_special_sources"/>
- <DetectIdentifier/>
- <LineContinue attribute="Operator" context="#stay"/>
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- <Detect2Chars attribute="Special" context="#stay" char="\" char1="#"/>
- <Detect2Chars attribute="Special" context="#stay" char="\" char1="\"/>
- <DetectChar attribute="Comment" context="Comment" char="#"/>
- </context>
-
- <context name="rule" attribute="Normal" lineEndContext="#stay">
- <LineContinue attribute="Operator" context="#stay"/>
- <!-- pop if there is a leading non-tab,
- and in particular, mark leading spaces as errors -->
- <RegExpr attribute="Error" context="#pop#pop#pop" String="^ +" firstNonSpace="1"/>
- <RegExpr context="#pop#pop#pop" String="^[^\t]" lookAhead="1" firstNonSpace="1"/>
- <DetectSpaces/>
- <DetectIdentifier/>
- <DetectChar attribute="String" context="string&quot;" char="&quot;"/>
- <DetectChar attribute="String" context="string'" char="'"/>
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- <Detect2Chars attribute="Special" context="#stay" char="\" char1="#"/>
- <Detect2Chars attribute="Special" context="#stay" char="\" char1="\"/>
- <AnyChar attribute="Operator" context="silent" String="@-" firstNonSpace="1"/>
- <DetectChar attribute="Comment" context="Comment" char="#"/>
- </context>
-
- <context name="silent" attribute="Silent" lineEndContext="#pop">
- <LineContinue attribute="Operator" context="#stay"/>
- <DetectSpaces/>
- <DetectIdentifier/>
- <DetectChar attribute="String" context="string&quot;" char="&quot;"/>
- <DetectChar attribute="String" context="string'" char="'"/>
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- <Detect2Chars attribute="Special" context="#stay" char="\" char1="#"/>
- <Detect2Chars attribute="Special" context="#stay" char="\" char1="\"/>
- <DetectChar attribute="Comment" context="Comment" char="#"/>
- </context>
-
- <context name="string&quot;" attribute="String" lineEndContext="#pop">
- <LineContinue attribute="Operator" context="#stay"/>
- <DetectChar attribute="String" context="#pop" char="&quot;"/>
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- </context>
-
- <context name="string'" attribute="String" lineEndContext="#pop">
- <LineContinue attribute="String" context="#stay"/>
- <DetectChar attribute="String" context="#pop" char="'"/>
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- </context>
-
- <context name="assign" attribute="Operator" lineEndContext="#pop">
- <DetectChar attribute="Operator" context="value" char="="/>
- </context>
-
- <context name="value" attribute="String" lineEndContext="#pop#pop">
- <LineContinue attribute="Operator"/>
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- <RegExpr attribute="Special" context="#pop#pop" String="@[-_\d\w]*@"/>
- <DetectChar attribute="Operator" char=";" context="#pop#pop"/>
- </context>
-
- <context name="dollar" attribute="Operator" lineEndContext="#pop">
- <DetectChar attribute="Operator" context="call(" char="("/>
- <DetectChar attribute="Operator" context="call{" char="{"/>
- <RegExpr attribute="Operator" context="#pop" String="."/>
- </context>
-
- <context name="call(" attribute="Variable" lineEndContext="#stay" fallthroughContext="callVar(" fallthrough="1">
- <keyword attribute="Keyword" context="callFunc(" String="functions"/>
- </context>
-
- <context name="call{" attribute="Variable" lineEndContext="#stay" fallthroughContext="callVar{" fallthrough="1">
- <keyword attribute="Keyword" context="callFunc{" String="functions"/>
- </context>
-
- <context name="callVar(" attribute="Variable" lineEndContext="#stay">
- <DetectChar attribute="Operator" char=")" context="#pop#pop#pop"/>
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- <DetectSpaces attribute="Error" context="#stay"/>
- <AnyChar attribute="Error" context="#stay" String="=#:"/>
- </context>
-
- <context name="callVar{" attribute="Variable" lineEndContext="#stay">
- <DetectChar attribute="Operator" char="}" context="#pop#pop#pop"/>
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- <DetectSpaces attribute="Error" context="#stay"/>
- <!-- Handle bmake Variable modifiers: ${variable[:modifier[:...]]} -->
- <Detect2Chars context="#stay" char="\" char1=":"/>
- <!-- other modifiers -->
- <DetectChar attribute="RealOperator" context="bmake_var_modifier" char=":"/>
- <AnyChar attribute="Error" context="#stay" String="=#"/>
- </context>
-
- <context name="bmake_var_modifier_arg" attribute="VarModifier" lineEndContext="#pop">
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- <Detect2Chars context="#stay" char="\" char1="}"/>
- <DetectChar attribute="Operator" char="}" context="#pop#pop#pop#pop"/>
- <Detect2Chars context="#stay" char="\" char1=":"/>
- <DetectChar attribute="RealOperator" context="#pop!bmake_var_modifier" char=":"/>
- </context>
-
- <!-- TODO: once all modifiers are properly handled change fallthroughContext to "expect}" -->
- <context name="bmake_var_modifier" attribute="VarModifier" lineEndContext="#pop" fallthroughContext="#pop!bmake_var_modifier_arg" fallthrough="true">
- <DetectChar attribute="Operator" context="dollar" char="$"/>
-
- <!--
- TODO :?true_string:false_string
- TODO :old_string=new_string This is the AT&T System V UNIX style variable substitution.
- TODO :@temp@string@ This is the loop expansion mechanism from the OSF Development Environment (ODE) make.
- TODO :!cmd! The output of running cmd is the value.
- TODO :[range]
- -->
- <!-- Modifiers without arguments:-->
- <!--
- :E Replaces each word in the variable with its suffix.
- :H Replaces each word in the variable with everything but the last component.
- :O Order every word in variable alphabetically.
- :Q Quotes every shell meta-character in the variable
- :R Replaces each word in the variable with everything but its suffix.
- :T Replaces each word in the variable with its last component.
- :u Remove adjacent duplicate words (like uniq(1)).
- :L The name of the variable is the value.
- :P The path of the node which has the same name as the variable is the value.
- -->
- <AnyChar attribute="Builtin" context="#pop!expect}" String="EHOQRTuLP"/>
- <!-- :sh If the variable is non-empty it is run as a command and the output becomes the new value. -->
- <Detect2Chars attribute="Builtin" context="#pop!expect}" char="s" char1="h"/>
- <!-- :Ox Randomize words in variable. -->
- <Detect2Chars attribute="Builtin" context="#pop!expect}" char="O" char1="x"/>
- <!-- :tA Attempt to convert variable to an absolute path using realpath(3),-->
- <Detect2Chars attribute="Builtin" context="#pop!expect}" char="t" char1="A"/>
- <!-- :tl Converts variable to lower-case letters.-->
- <Detect2Chars attribute="Builtin" context="#pop!expect}" char="t" char1="L"/>
- <!-- :gmtime The value is a format string for strftime(3), using the current gmtime(3). -->
- <StringDetect attribute="Builtin" String="gmtime" context="#pop!expect}" />
- <!-- :hash Compute a 32bit hash of the value and encode it as hex digits. -->
- <StringDetect attribute="Builtin" String="hash" context="#pop!expect}" />
- <!-- :localtime The value is a format string for strftime(3), using the current localtime(3). -->
- <StringDetect attribute="Builtin" String="localtime" context="#pop!expect}" />
- <!--Now the modifiers with arguments:-->
- <!--
- :Mpattern Select only those words that match pattern.
- :Npattern This is identical to `:M', but selects all words which do not match pattern.
- :Dnewval If the variable is defined newval is the value.
- :Unewval If the variable is undefined newval is the value.
- -->
- <AnyChar attribute="Builtin" context="#pop!bmake_var_modifier_arg" String="MNDU"/>
- <!-- TODO: add new context to highligh old_string and new_string differently
- :S/old_string/new_string/[1gW]
- :C/pattern/replacement/[1gW]
- -->
- <AnyChar attribute="Builtin" context="#pop!bmake_var_modifier_arg" String="SC"/>
- <!-- ::=str The variable is assigned the value str after substitution. -->
- <Detect2Chars attribute="Builtin" context="#pop!bmake_var_modifier_arg" char=":" char1="="/>
- <!-- ::?=str As for ::= but only if the variable does not already have a value. -->
- <StringDetect attribute="Builtin" String=":?=" context="#pop!bmake_var_modifier_arg" />
- <!-- ::+=str Append str to the variable. -->
- <StringDetect attribute="Builtin" String=":+=" context="#pop!bmake_var_modifier_arg" />
- <!-- ::!=cmd Assign the output of cmd to the variable. -->
- <StringDetect attribute="Builtin" String=":!=" context="#pop!bmake_var_modifier_arg" />
- <!-- :tu Converts variable to upper-case letters. -->
- <Detect2Chars attribute="Builtin" context="#pop!bmake_var_modifier_arg" char="t" char1="u"/>
- <!-- :tW Causes the value to be treated as a single word. See also `:[*]'. -->
- <Detect2Chars attribute="Builtin" context="#pop!bmake_var_modifier_arg" char="t" char1="W"/>
- <!-- :tw Causes the value to be treated as a sequence of words delimited by white space. See also `:[@]'.-->
- <Detect2Chars attribute="Builtin" context="#pop!bmake_var_modifier_arg" char="t" char1="w"/>
- <!-- :tsc Words in the variable are normally separated by a space on
- expansion. This modifier sets the separator to the character c. If
- c is omitted, then no separator is used. The common escapes
- (including octal numeric codes), work as expected. -->
- <Detect2Chars attribute="Builtin" context="#pop!bmake_var_modifier_arg" char="t" char1="s"/>
-
- <IncludeRules context="bmake_var_modifier_arg"/>
- </context>
-
- <context name="expect}" attribute="Error" lineEndContext="#pop#pop#pop#pop">
- <Detect2Chars context="#stay" char="\" char1="}"/>
- <DetectChar attribute="Operator" char="}" context="#pop#pop#pop#pop"/>
- </context>
-
- <context name="callFunc(" attribute="FuncParam" lineEndContext="#stay">
- <DetectChar attribute="Operator" char=")" context="#pop#pop#pop"/>
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- <DetectChar attribute="Keyword" context="#stay" char=","/>
- <DetectChar attribute="String" context="string'" char="'"/>
- </context>
-
- <context name="callFunc{" attribute="FuncParam" lineEndContext="#stay">
- <DetectChar attribute="Operator" char="}" context="#pop#pop#pop"/>
- <DetectChar attribute="Operator" context="dollar" char="$"/>
- <DetectChar attribute="Keyword" context="#stay" char=","/>
- <DetectChar attribute="String" context="string'" char="'"/>
- </context>
-
- <context attribute="Comment" lineEndContext="#pop" name="Comment">
- <LineContinue attribute="Comment" context="#stay" />
- <IncludeRules context="##Alerts" />
- <IncludeRules context="##Modelines" />
- </context>
-
- </contexts>
- <itemDatas>
- <itemData name="Normal" defStyleNum="dsNormal" spellChecking="0"/>
- <itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="0"/>
- <itemData name="Comment" defStyleNum="dsComment"/>
- <itemData name="String" defStyleNum="dsString" spellChecking="0"/>
- <itemData name="Variable" defStyleNum="dsDataType" spellChecking="0"/>
- <itemData name="Target" defStyleNum="dsDecVal" spellChecking="0"/>
- <itemData name="Section" defStyleNum="dsOthers" spellChecking="0"/>
- <itemData name="Prereq" defStyleNum="dsDataType" spellChecking="0"/>
- <itemData name="FuncParam" defStyleNum="dsString" spellChecking="0"/>
- <itemData name="Operator" defStyleNum="dsChar" spellChecking="0"/>
- <itemData name="Silent" defStyleNum="dsFunction" spellChecking="0"/>
- <itemData name="Special" defStyleNum="dsFloat" spellChecking="0"/>
- <itemData name="Error" defStyleNum="dsError" spellChecking="0"/>
- <itemData name="Builtin" defStyleNum="dsBuiltIn" spellChecking="0"/>
- <itemData name="Number" defStyleNum="dsDecVal" spellChecking="0"/>
- <itemData name="Include" defStyleNum="dsImport" spellChecking="0"/>
- <itemData name="ControlFlow" defStyleNum="dsControlFlow" spellChecking="0"/>
- <itemData name="VarModifier" defStyleNum="dsSpecialString" spellChecking="0"/>
- <itemData name="RealOperator" defStyleNum="dsKeyword" spellChecking="0"/>
- <itemData name="SpecialTarget" defStyleNum="dsOthers" spellChecking="0" bold="1"/>
- <itemData name="SpecialPrereq" defStyleNum="dsDataType" spellChecking="0" bold="1"/>
- </itemDatas>
- </highlighting>
- <general>
- <comments>
- <comment name = "singleLine" start = "#"/>
- </comments>
- <!-- '-' is not a deliminator in function filter-out -->
- <keywords casesensitive="1" weakDeliminator = ".-"/>
- </general>
-</language>
-<!-- kate: space-indent on; indent-width 2; replace-tabs on; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/markdown.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/markdown.xml
index f88848729f..c3811c772f 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/markdown.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/markdown.xml
@@ -6,6 +6,7 @@
Dual-Licensed under both GPL and BSD licenses.
Extended 2009 Claes Holmerson. http://github.com/claes/kate-markdown/
+ Extended 2019 Nibaldo González S. <nibgonz@gmail.com> (changes under MIT license).
-->
<!--
The [^\s]{1} is in the regex to avoid interfering with bullet list which
@@ -15,189 +16,685 @@
Match space or newline, followed by "*", followed by one non-space,
followed by anything non-asterisk, followed by "*", followed by
space, end-of-sentence punctuation, or the end of the line.
+
+ TODO: We can be more specific and highlight text combinations in bold,
+ italic and strikethrough.
+ Example: **bold text and _italic and bold text_**
+ __bold and ~~strikeout and bold~~__
-->
-<!DOCTYPE language SYSTEM "language.dtd"
+<!DOCTYPE language
[
-<!ENTITY strongemphasisregex "(\s|^)[\*_]{3}[^\*_]+[\*_]{3}(\s|\.|,|;|:|\-|\?|$)">
-<!ENTITY strongregex "(\s|^)[\*_]{2}[^\s]{1}[^\*_]+[\*_]{2}(\s|\.|,|;|:|\-|\?|$)">
-<!ENTITY emphasisregex "(\s|^)[\*_]{1}[^\s]{1}[^\*_]+[\*_]{1}(\s|\.|,|;|:|\-|\?|$)">
-<!ENTITY reflinkregex '\[[^\]\^]+\]\s*\[[^\]]*\]\s*(\s+\"[^\"]*\"){0,1}'>
-<!ENTITY reflinktargetregex '\[[^\]\^]+\]\:\s+[^\s]+(\s+\"[^\"]*\"){0,1}'>
-<!ENTITY footnoteregex "\[\^[^\]]+\]">
-<!ENTITY inlinelinkregex "\[[^\]\^]+\]\s*\([^\(]*\)">
-<!ENTITY inlineimageregex "\!\[[^\]\^]+\]\([^\(]*\)">
-<!ENTITY refimageregex "\!\[[^\]\^]+\]\[[^\[]*\]">
-<!ENTITY autolinkregex '&lt;(https?|ftp):[^\"&gt;\s]+&gt;'>
-<!ENTITY mailtolinkregex "&lt;(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)&gt;">
-<!ENTITY rulerregex "\s*([\*\-_]\s?){3,}\s*">
+<!-- NOTE: To correctly detect bold, italic or strike out text, use minimal="true" in RegExpr rules -->
+<!ENTITY contentregex_ast "(?:(?:[^\*\s\\]|\\.)(?:[^\\]|\\.)*)?(?:[^\*\s\\]|\\\S)">
+<!ENTITY contentregex_und "(?:(?:[^_\s\\]|\\.)(?:[^\\]|\\.)*)?(?:[^_\s\\]|\\\S)">
+<!-- strong + emphasis text -->
+<!ENTITY strongemphasisregex_ast "\*{3,5}&contentregex_ast;\*{3}">
+<!ENTITY strongemphasisregex_und "\b_{3,5}&contentregex_und;_{3}">
+<!ENTITY strongemphasisregex_ast_und "\*{2,5}(?:(?:_|_{3}|_{5})&contentregex_und;_+|(?:_{2}|_{4})&contentregex_und;_)\*{2}">
+<!ENTITY strongemphasisregex_ast_und2 "\*_{2,5}&contentregex_und;_{2,5}\*(?!\*)">
+<!ENTITY strongemphasisregex_und_ast "\b_{2,5}(?:(?:\*|\*{3}|\*{5})&contentregex_ast;\*+|(?:\*{2}|\*{4})&contentregex_ast;\*)_{2}">
+<!ENTITY strongemphasisregex_und_ast2 "\b_(?:\*{2,5}&contentregex_ast;\*{2,5}_{0,4}|_{0,4}\*{2,5}&contentregex_ast;\*{2,5})_\b">
+<!-- strong text -->
+<!ENTITY strongregex_ast "\*{2}(?:&contentregex_ast;\*{2}|\*{1,3}&contentregex_ast;\*{2}(?!\*))">
+<!ENTITY strongregex_und "\b_{2}(?:&contentregex_und;_{2}|_{1,3}&contentregex_und;_{2}(?!_))">
+<!-- emphasis text -->
+<!ENTITY emphasisregex_ast "\*(?:&contentregex_ast;\*|\*{1,4}&contentregex_ast;\*(?!\*))">
+<!ENTITY emphasisregex_und "\b_(?:&contentregex_und;_+|_{1,4}&contentregex_und;_)\b">
+<!-- links.
+ Keep in sync with reStructuredText (rest) -->
+<!ENTITY startlink "(?:https?|ftp)\://">
+<!ENTITY link "&startlink;[^&quot;&gt;\s]+">
+<!-- link in normal text.
+ Keep in sync with reStructuredText’s (rest) StandaloneHyperlink attribute -->
+<!ENTITY implicitlink "\b&startlink;[^&quot;&gt;\s`\)]*[^\s!&quot;&apos;`\(\)\*,\.:;&lt;&gt;\?~\]\}\\](?=[[:punct:]]*(?:[\s\)]|$))">
+<!-- references: [name], [name][id], [name][id] "title", [name](https://example.com) -->
+<!ENTITY refchar "(?:\\.|[^\]\\])">
+<!ENTITY refname "(?:\\.|[^\s\[\]\\])&refchar;*">
+<!ENTITY reflinkid "\s*(?:&refname;)?">
+<!ENTITY reflinkidregex "(?:\s?\[&reflinkid;\]|\((?:\\.|[^\(\)\\])*\))">
+<!ENTITY reflinkregex "\[(?:(?=\s*&refname;\])|\s+(?=\]))">
+<!-- target ref.: [id]: https://example.com "Title" -->
+<!ENTITY reflinktargetregex '\[\s*(?:&refname;|\s)\]\:\s+(?=\S)'>
+<!-- footnote: [^name] -->
+<!ENTITY footnoteregex "\[\^&refchar;+\]">
+<!-- image: ![title](https://example.com), ![title][id] -->
+<!ENTITY startinlineimage "\!\[(?:\\.|[^\[\]\\])*\]\(">
+<!ENTITY endinlineimage "(?:\\.|[^\(\)\\])*\)">
+<!ENTITY inlineimageregex "&startinlineimage;(?=&endinlineimage;)">
+<!ENTITY refimageregex "\!\[(?:\\.|[^\[\]\\])*\]\[&reflinkid;\]">
+<!-- autolink & email: <https://example.com>, <example@kde.org>, <mailto:example@kde.org> -->
+<!ENTITY autolinkregex '&lt;&link;&gt;'>
+<!ENTITY email "[\-\.\w]+\@[\-a-z0-9]+(?:\.[\-a-z0-9]+)*\.[a-z]+">
+<!ENTITY mailtolinkregex "&lt;(?:mailto:)?(?=&email;&gt;)">
+<!-- horizontal rule: avoid conflicts with strong-emphasis text, lists and code blocks -->
+<!ENTITY rulerregex "\s*(?:(?:\*\s*){3,}|(?:_\s*){3,}|(?:\-\s*){3,})\s*$">
<!-- two spaces at end of line generates linebreak -->
<!ENTITY linebreakregex " $">
-<!ENTITY strikeoutregex "[~]{2}[^~].*[^~][~]{2}">
-<!-- pandoc style -->
+<!-- strikethrough text, pandoc style -->
+<!ENTITY strikeoutregex "[~]{2}[^~](?:.*[^~])?[~]{2}">
+<!-- highlight text -->
+<!ENTITY highlightregex "[=]{2}[^=](?:.*[^=])?[=]{2}">
+<!-- emoji -->
+<!ENTITY emojiregex ":([-+]1|\w+):">
+<!-- start of fenced code block -->
+<!ENTITY fcode "(`{3,}|~{3,})">
+<!-- end of line & empty line -->
+<!ENTITY end "\s*$">
+<!ENTITY emptyline "^\s*$">
+<!-- indented code block -->
+<!ENTITY indentedcodeblock "(?:\s{4}|\t).*$">
+<!-- bullet of list -->
+<!ENTITY listbullet "[\*\+\-]">
+<!-- metadata key & name of html tag -->
+<!ENTITY metadatakey "[a-zA-Z0-9][\w\-\s]*\:">
+<!ENTITY htmlname "[A-Za-z_\:][\w\.\:\-]*">
+<!-- comment -->
+<!ENTITY startcomment "&lt;!--">
+<!ENTITY endcomment "--&gt;">
+<!ENTITY checkbox "\[[ x]\](?=\s)">
]>
-<language name="Markdown" version="6" kateversion="5.0" section="Markup" extensions="*.md;*.mmd;*.markdown" priority="15" author="Darrin Yeager, Claes Holmerson" license="GPL,BSD">
+
+<language name="Markdown" version="30" kateversion="5.79" section="Markup" extensions="*.md;*.mmd;*.markdown;*.md.html" mimetype="text/markdown" priority="15" author="Darrin Yeager, Claes Holmerson" license="GPL,BSD">
<highlighting>
<contexts>
- <context attribute="Normal Text" lineEndContext="#stay" name="Normal Text">
- <DetectChar context="blockquote" char="&gt;" column="0"/>
- <RegExpr attribute="h1" String="^#\s.*[#]?$"/>
- <RegExpr attribute="h2" String="^##\s.*[#]?$"/>
- <RegExpr attribute="h3" String="^###\s.*[#]?$"/>
- <RegExpr attribute="h4" String="^####\s.*[#]?$"/>
- <RegExpr attribute="h5" String="^#####\s.*[#]?$"/>
- <RegExpr attribute="h6" String="^######\s.*[#]?$"/>
- <RegExpr attribute="ruler" String="&rulerregex;"/>
- <RegExpr attribute="strong" String="&strongregex;"/>
- <RegExpr attribute="emphasis" String="&emphasisregex;"/>
- <RegExpr attribute="strongemphasis" String="&strongemphasisregex;"/>
- <RegExpr attribute="code" String="^([\s]{4,}|\t+).*$"/>
- <RegExpr context="bullet" String="^[\*\+\-]\s"/>
- <RegExpr context="numlist" String="^[\d]+\.\s"/>
- <RegExpr attribute="meta" String="^(Title|Author|Date|Copyright|Revision|CSS|LaTeX\ XSLT|Categories|Tags|BaseName|Excerpt):(.*)+$"/>
- <IncludeRules context="inc"/>
+ <!-- Start of the Markdown document: find metadata or code block -->
+ <context name="Start Document" attribute="Normal Text" lineEndContext="Normal Text" lineEmptyContext="Normal Text" fallthroughContext="Normal Text">
+ <!-- Some titles: (Title|Author|Date|Copyright|Revision|CSS|LaTeX\ XSLT|Categories|Tags|BaseName|Excerpt) -->
+ <!-- Metadata: Markdown format -->
+ <RegExpr attribute="Metadata" context="metadata" String="^\-{3}&end;" column="0" beginRegion="metadata"/>
+ <!-- Metadata: MultiMarkdown format -->
+ <RegExpr attribute="Metadata Title" context="metadata-mm-value" String="^&metadatakey;(?=\s|$)" column="0"/>
+ <!-- Code Block -->
+ <RegExpr attribute="Code" context="find-code-block-start" String="^&indentedcodeblock;" column="0"/>
+ </context>
+ <context name="metadata" attribute="Metadata" lineEndContext="#stay">
+ <StringDetect attribute="Metadata" context="#pop!Normal Text" String="---" column="0" endRegion="metadata"/>
+ <StringDetect attribute="Metadata" context="#pop!Normal Text" String="..." column="0" endRegion="metadata"/>
+ <RegExpr attribute="Metadata Title" String="^&metadatakey;(?=\s|$)" column="0"/>
+ </context>
+ <context name="metadata-mm-value" attribute="Metadata" lineEndContext="#pop!metadata-mm"/>
+ <context name="metadata-mm" attribute="Metadata" lineEndContext="#stay" lineEmptyContext="#pop!Normal Text" fallthroughContext="#pop!Normal Text">
+ <!-- Find Metadata key or value -->
+ <RegExpr attribute="Metadata Title" context="#pop!metadata-mm-value" String="^&metadatakey;(?=\s|$)" column="0"/>
+ <RegExpr attribute="Metadata" context="#pop!metadata-mm-value" String="^(?:\s{4}|\t)" column="0"/>
</context>
- <context attribute="blockquote" lineEndContext="#stay" lineEmptyContext="#pop" name="blockquote">
- <RegExpr attribute="bq-strong" String="&strongregex;"/>
- <RegExpr attribute="bq-emphasis" String="&emphasisregex;"/>
+ <context name="find-code-block-start" attribute="Normal Text" lineEndContext="#stay" lineEmptyContext="#stay" fallthroughContext="#pop!Normal Text">
+ <IncludeRules context="find-code-block"/>
+ </context>
+
+ <!-- Normal Text Document -->
+ <context name="Normal Text" attribute="Normal Text" lineEndContext="#stay" lineEmptyContext="find-code-block">
+ <!-- Header -->
+ <DetectChar context="find-header" char="#" column="0" lookAhead="true"/>
+ <RegExpr attribute="Header H1" String="(?:\=\s*){2,}$" firstNonSpace="true"/>
+ <!-- Highlight code block after line only with spaces or comment -->
+ <RegExpr attribute="Normal Text" context="find-code-block" String="&emptyline;" column="0"/>
+ <StringDetect attribute="Comment" context="find-code-block" String="&startcomment;" column="0" lookAhead="true"/>
+ <!-- Horizontal rules, Blockquotes and Lists.
+ NOTE: The indentation of the lists is captured. -->
+ <RegExpr attribute="Horizontal Rule" context="find-code-block" String="^&rulerregex;" column="0"/>
+ <DetectChar attribute="Blockquote" context="blockquote" char="&gt;" firstNonSpace="true"/>
+ <RegExpr attribute="List" context="list" String="^(\s*)&listbullet;(\s+)" column="0"/>
+ <RegExpr attribute="Number List" context="numlist" String="^(\s*)\d\.(\s+)" column="0"/>
+ <RegExpr attribute="Number List" context="numlist2" String="^(\s*)\d\d+\.(\s+)" column="0"/>
+ <!-- Find Strong, Emphasis and Strikethrough Text -->
+ <DetectChar context="find-strong-normal" char="*" lookAhead="true"/>
+ <DetectChar context="find-emphasis-normal" char="_" lookAhead="true"/>
+ <RegExpr attribute="Strikethrough Text" minimal="true" String="&strikeoutregex;"/>
+ <RegExpr attribute="Highlight Text" minimal="true" String="&highlightregex;"/>
+ <!-- Common -->
<IncludeRules context="inc"/>
+ <RegExpr attribute="Normal Text: Link" String="&implicitlink;"/>
+ <!-- Table -->
+ <DetectChar attribute="Table" char="|" context="table" firstNonSpace="true" lookAhead="1"/>
+ </context>
+ <!-- Find indented code blocks. These are only allowed after an empty line or on the first line -->
+ <context name="find-code-block" attribute="Normal Text" lineEndContext="#stay" lineEmptyContext="#stay" fallthroughContext="#pop">
+ <RegExpr attribute="Code" String="^&indentedcodeblock;" column="0"/>
+ <RegExpr attribute="Normal Text" String="&end;" />
+ <RegExpr attribute="Comment" context="comment" String="^\s*&startcomment;|\s*&startcomment;(?=.*&endcomment;)" beginRegion="comment"/>
+ </context>
+
+ <!-- Table in Normal Text Document -->
+ <context name="table" attribute="Normal Text" lineEndContext="#pop" lineEmptyContext="#pop!find-code-block">
+ <IncludeRules context="find-table-element"/>
+ <IncludeRules context="Normal Text"/>
+ </context>
+
+ <context name="find-table-element" attribute="Normal Text" lineEndContext="#pop">
+ <RegExpr attribute="Table" String="(\|(\s*:?---+:?)?)++"/>
+ </context>
+
+ <context name="find-header" attribute="Normal Text" lineEndContext="#pop">
+ <RegExpr attribute="Header H1" context="#pop!close-H2-region" String="^#\s" column="0" endRegion="H1" beginRegion="H1" lookAhead="true"/>
+ <RegExpr attribute="Header H2" context="#pop!close-H3-region" String="^##\s" column="0" endRegion="H2" beginRegion="H2" lookAhead="true"/>
+ <RegExpr attribute="Header H3" context="#pop!close-H4-region" String="^###\s" column="0" endRegion="H3" beginRegion="H3" lookAhead="true"/>
+ <RegExpr attribute="Header H4" context="#pop!close-H5-region" String="^####\s" column="0" endRegion="H4" beginRegion="H4" lookAhead="true"/>
+ <RegExpr attribute="Header H5" context="#pop!close-H6-region" String="^#####\s" column="0" endRegion="H5" beginRegion="H5" lookAhead="true"/>
+ <RegExpr attribute="Header H6" context="#pop" String="^######\s.*" column="0" endRegion="H6" beginRegion="H6"/>
+ <DetectChar attribute="Normal Text" context="#pop" char="#"/>
+ </context>
+ <!-- BUG: 441278 sub-headers should be closed when their parent header is closed (e.g. in h1 h2 h3 h1, h1-h3 should all be closed at the 2nd h1) -->
+ <!-- TODO: Port to a less hacky version (maybe a new attribute for declaring multiple endRegions) -->
+ <context name="close-H2-region" attribute="Header H2" lineEndContext="#pop!close-H3-region" fallthroughContext="#pop!close-H3-region">
+ <DetectChar attribute="Header H2" context="#pop!close-H3-region" char="#" lookAhead="true" endRegion="H2"/>
+ </context>
+ <context name="close-H3-region" attribute="Header H3" lineEndContext="#pop!close-H4-region" fallthroughContext="#pop!close-H4-region">
+ <DetectChar attribute="Header H3" context="#pop!close-H4-region" char="#" lookAhead="true" endRegion="H3"/>
+ </context>
+ <context name="close-H4-region" attribute="Header H4" lineEndContext="#pop!close-H5-region" fallthroughContext="#pop!close-H5-region">
+ <DetectChar attribute="Header H4" context="#pop!close-H5-region" char="#" lookAhead="true" endRegion="H4"/>
+ </context>
+ <context name="close-H5-region" attribute="Header H5" lineEndContext="#pop!close-H6-region" fallthroughContext="#pop!close-H6-region">
+ <DetectChar attribute="Header H5" context="#pop!close-H6-region" char="#" lookAhead="true" endRegion="H5"/>
+ </context>
+ <context name="close-H6-region" attribute="Header H6" lineEndContext="#pop" fallthroughContext="#pop">
+ <DetectChar attribute="Header H6" context="#pop!close-sentinel-region" char="#" lookAhead="true" endRegion="H6"/>
+ </context>
+ <!-- This sentinel does not close any actual region, it's just here so that the proper attribute/highlighting is applied and -->
+ <!--to ensure that H6 headers won't be set as the "primary" region that was closed just because it was closed last -->
+ <context name="close-sentinel-region" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="#pop">
+ <RegExpr attribute="Header H1" context="#pop" String="^#\s.*" column="0"/>
+ <RegExpr attribute="Header H2" context="#pop" String="^##\s.*" column="0"/>
+ <RegExpr attribute="Header H3" context="#pop" String="^###\s.*" column="0"/>
+ <RegExpr attribute="Header H4" context="#pop" String="^####\s.*" column="0"/>
+ <RegExpr attribute="Header H5" context="#pop" String="^#####\s.*" column="0"/>
</context>
- <context attribute="bullet" lineEndContext="#stay" lineEmptyContext="#pop" name="bullet">
- <RegExpr attribute="bl-strong" String="&strongregex;"/>
- <RegExpr attribute="bl-emphasis" String="&emphasisregex;"/>
+
+
+ <context name="find-strong-normal" attribute="Normal Text" lineEndContext="#pop">
+ <RegExpr attribute="Strong-Emphasis Text" context="#pop" minimal="true" String="&strongemphasisregex_ast_und;|&strongemphasisregex_ast_und2;"/>
+ <RegExpr attribute="Strong Text" context="#pop" minimal="true" String="&strongregex_ast;"/>
+ <RegExpr attribute="Strong-Emphasis Text" context="#pop" minimal="true" String="&strongemphasisregex_ast;"/>
+ <RegExpr attribute="Emphasis Text" context="#pop" minimal="true" String="&emphasisregex_ast;"/>
+ <DetectChar attribute="Normal Text" context="#pop" char="*"/>
+ </context>
+ <context name="find-emphasis-normal" attribute="Normal Text" lineEndContext="#pop">
+ <RegExpr attribute="Strong-Emphasis Text" context="#pop" minimal="true" String="&strongemphasisregex_und_ast;|&strongemphasisregex_und_ast2;"/>
+ <RegExpr attribute="Strong Text" context="#pop" minimal="true" String="&strongregex_und;"/>
+ <RegExpr attribute="Strong-Emphasis Text" context="#pop" minimal="true" String="&strongemphasisregex_und;"/>
+ <RegExpr attribute="Emphasis Text" context="#pop" minimal="true" String="&emphasisregex_und;"/>
+ <DetectChar attribute="Normal Text" context="#pop" char="_"/>
+ </context>
+
+ <!-- Blockquote -->
+ <context name="blockquote" attribute="Blockquote: Normal Text" lineEndContext="#stay" lineEmptyContext="#pop">
+ <IncludeRules context="default-blockquote-1"/>
+ <!-- Find code block -->
+ <RegExpr attribute="Normal Text" context="#pop!find-code-block" String="&emptyline;" column="0"/>
+ <StringDetect attribute="Comment" context="#pop!find-code-block" String="&startcomment;" column="0" lookAhead="true"/>
+ <IncludeRules context="default-blockquote-2"/>
+ <IncludeRules context="find-table-element"/>
+ </context>
+ <!-- Blockquote within a list -->
+ <context name="blockquote-list" attribute="Blockquote: Normal Text" lineEndContext="#stay" lineEmptyContext="#pop">
+ <IncludeRules context="default-blockquote-1"/>
+ <!-- Don't find code block -->
+ <RegExpr attribute="Normal Text" context="#pop" String="&emptyline;" column="0"/>
+ <StringDetect attribute="Comment" context="#pop!comment" String="&startcomment;" firstNonSpace="true"/>
+ <IncludeRules context="default-blockquote-2"/>
+ </context>
+
+ <context name="default-blockquote-1" attribute="Blockquote: Normal Text" lineEndContext="#stay">
+ <DetectChar attribute="Blockquote" char="&gt;" firstNonSpace="true"/>
+ <!-- End with header, horizontal rule or list/numlist -->
+ <RegExpr context="#pop" String="^(?:\s*(?:&listbullet;|[\d]+\.)\s|#{1,6}\s|&rulerregex;)" column="0" lookAhead="true"/>
+ </context>
+ <context name="default-blockquote-2" attribute="Blockquote: Normal Text" lineEndContext="#stay">
+ <!-- Strong, emphasis, strong-emphasis and strikethrough text -->
+ <AnyChar context="find-strong-emphasis-blockquote" String="*_" lookAhead="true"/>
+ <RegExpr attribute="Blockquote: Strikethrough Text" minimal="true" String="&strikeoutregex;"/>
+ <RegExpr attribute="Blockquote: Highlight Text" minimal="true" String="&highlightregex;"/>
+ <!-- Common -->
<IncludeRules context="inc"/>
+ <RegExpr attribute="Blockquote: Link" String="&implicitlink;"/>
+ </context>
+ <context name="find-strong-emphasis-blockquote" attribute="Blockquote: Normal Text" lineEndContext="#pop">
+ <RegExpr attribute="Blockquote: Strong-Emphasis Text" context="#pop" minimal="true" String="&strongemphasisregex_ast_und;|&strongemphasisregex_und_ast;|&strongemphasisregex_ast_und2;|&strongemphasisregex_und_ast2;"/>
+ <RegExpr attribute="Blockquote: Strong Text" context="#pop" minimal="true" String="&strongregex_ast;|&strongregex_und;"/>
+ <RegExpr attribute="Blockquote: Strong-Emphasis Text" context="#pop" minimal="true" String="&strongemphasisregex_ast;|&strongemphasisregex_und;"/>
+ <RegExpr attribute="Blockquote: Emphasis Text" context="#pop" minimal="true" String="&emphasisregex_ast;|&emphasisregex_und;"/>
+ <AnyChar attribute="Blockquote: Normal Text" context="#pop" String="*_"/>
</context>
- <context attribute="numlist" lineEndContext="#stay" lineEmptyContext="#pop" name="numlist">
- <RegExpr attribute="nl-strong" String="&strongregex;"/>
- <RegExpr attribute="nl-emphasis" String="&emphasisregex;"/>
+
+ <!-- List and Numbered List -->
+ <!-- NOTE: The indentation of the lists is captured. New paragraphs, code blocks, blockquotes or
+ horizontal rules must have an indentation equal to or greater than the list, to be part of it. -->
+ <!-- * list -->
+ <context name="list" attribute="List: Normal Text" lineEndContext="#stay" fallthroughContext="content-list">
+ <!-- Find indented code blocks, blockquotes and horizontal rules -->
+ <RegExpr attribute="Code" String="^%1%2\s&indentedcodeblock;" column="0" dynamic="true"/>
+ <RegExpr attribute="Blockquote" context="blockquote-list" String="^%1%2\s+&gt;" column="0" dynamic="true"/>
+ <RegExpr attribute="Horizontal Rule" String="^%1%2\s+&rulerregex;" column="0" dynamic="true"/>
+ <RegExpr String="&emptyline;" column="0"/>
+ <!-- Text with the same indentation captured corresponds to the item list -->
+ <RegExpr context="content-list" String="^%1%2\s" column="0" lookAhead="true" dynamic="true"/>
+ <!-- Finish when the text has a lower indentation than the list -->
+ <RegExpr context="#pop" String="^\s*\S" column="0" lookAhead="true"/>
+ <!-- Highlight checkbox at the start of the item (task list) -->
+ <RegExpr attribute="List: Checkbox" context="content-list" String="\s*&checkbox;"/>
+ <!-- Highlight checkbox at the start of the item (task list) -->
+ <RegExpr attribute="Table" context="content-list-table" String="\s*\|"/>
+ </context>
+ <!-- 1. numlist (one digit) -->
+ <context name="numlist" attribute="List: Normal Text" lineEndContext="#stay" fallthroughContext="content-list">
+ <!-- Find indented code blocks, blockquotes and horizontal rules -->
+ <RegExpr attribute="Code" String="^%1%2\s{2}&indentedcodeblock;" column="0" dynamic="true"/>
+ <RegExpr attribute="Blockquote" context="blockquote-list" String="^%1%2\s{2,}&gt;" column="0" dynamic="true"/>
+ <RegExpr attribute="Horizontal Rule" String="^%1%2\s{2,}&rulerregex;" column="0" dynamic="true"/>
+ <RegExpr String="&emptyline;" column="0"/>
+ <!-- Text with the same indentation captured -->
+ <RegExpr context="content-list" String="^%1%2\s{2}" column="0" lookAhead="true" dynamic="true"/>
+ <!-- Finish when the text has a lower indentation than the list -->
+ <RegExpr context="#pop" String="^\s*\S" column="0" lookAhead="true"/>
+ </context>
+ <!-- 10. numlist (two or more digits) -->
+ <context name="numlist2" attribute="List: Normal Text" lineEndContext="#stay" fallthroughContext="content-list">
+ <!-- Find indented code blocks, blockquotes and horizontal rules -->
+ <RegExpr attribute="Code" String="^%1%2\s{3}&indentedcodeblock;" column="0" dynamic="true"/>
+ <RegExpr attribute="Blockquote" context="blockquote-list" String="^%1%2\s{3,}&gt;" column="0" dynamic="true"/>
+ <RegExpr attribute="Horizontal Rule" String="^%1%2\s{3,}&rulerregex;" column="0" dynamic="true"/>
+ <RegExpr String="&emptyline;" column="0"/>
+ <!-- Text with the same indentation captured -->
+ <RegExpr context="content-list" String="^%1%2\s{3}" column="0" lookAhead="true" dynamic="true"/>
+ <!-- Finish when the text has a lower indentation than the list -->
+ <RegExpr context="#pop" String="^\s*\S" column="0" lookAhead="true"/>
+ </context>
+
+ <!-- NOTE: Empty lines, blockquotes and horizontal rules send to contexts "list", "numlist" or "numlist2" (#pop),
+ to check the indentation of the text and determine if the content of the list ends. -->
+ <context name="content-list" attribute="List: Normal Text" lineEndContext="#stay" lineEmptyContext="#pop">
+ <RegExpr context="#pop" String="&emptyline;" column="0"/>
+ <!-- Blockquote and horizontal rule (check indentation) -->
+ <RegExpr context="#pop" String="^\s*(?:&gt;|&rulerregex;)" column="0" lookAhead="true"/>
+ <!-- End with header or new list/numlist -->
+ <RegExpr context="#pop#pop" String="^(?:\s*(?:&listbullet;|[\d]+\.)\s|#{1,6}\s)" column="0" lookAhead="true"/>
+ <!-- Strong, emphasis, strong-emphasis and strikethrough text -->
+ <AnyChar context="find-strong-emphasis-list" String="*_" lookAhead="true"/>
+ <RegExpr attribute="List: Strikethrough Text" minimal="true" String="&strikeoutregex;"/>
+ <RegExpr attribute="List: Highlight Text" minimal="true" String="&highlightregex;"/>
+ <!-- Common -->
<IncludeRules context="inc"/>
+ <RegExpr attribute="List: Link" String="&implicitlink;"/>
+ </context>
+ <context name="find-strong-emphasis-list" attribute="List: Normal Text" lineEndContext="#pop">
+ <RegExpr attribute="List: Strong-Emphasis Text" context="#pop" minimal="true" String="&strongemphasisregex_ast_und;|&strongemphasisregex_und_ast;|&strongemphasisregex_ast_und2;|&strongemphasisregex_und_ast2;"/>
+ <RegExpr attribute="List: Strong Text" context="#pop" minimal="true" String="&strongregex_ast;|&strongregex_und;"/>
+ <RegExpr attribute="List: Strong-Emphasis Text" context="#pop" minimal="true" String="&strongemphasisregex_ast;|&strongemphasisregex_und;"/>
+ <RegExpr attribute="List: Emphasis Text" context="#pop" minimal="true" String="&emphasisregex_ast;|&emphasisregex_und;"/>
+ <AnyChar attribute="List: Normal Text" context="#pop" String="*_"/>
+ </context>
+
+ <!-- Table in List -->
+ <context name="content-list-table" attribute="List: Normal Text" lineEndContext="#stay" lineEmptyContext="#pop">
+ <IncludeRules context="find-table-element"/>
+ <IncludeRules context="content-list"/>
+ </context>
+
+ <!-- Comments -->
+ <context name="comment" attribute="Comment" lineEndContext="#stay">
+ <StringDetect attribute="Comment" context="#pop" String="&endcomment;" endRegion="comment"/>
+ <IncludeRules context="##Comments"/>
</context>
- <context attribute="comment" lineEndContext="#stay" name="comment">
- <RegExpr String="--&gt;" attribute="comment" context="#pop" endRegion="comment"/>
+
+ <!-- Fenced Code Blocks -->
+ <context name="find-lang-fenced-code" attribute="Normal Text" lineEndContext="#pop">
+ <!-- Apply syntax highlighting to fenced code blocks for some languages -->
+ <RegExpr attribute="Fenced Code" context="#pop!code" String="&fcode;&end;" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!bash-code" String="&fcode;\s*(?:bash(?:rc|_profile|_login|_logout)?|shell|sh|profile|PKGBUILD|APKBUILD|ebuild|eclass|nix)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!zsh-code" String="&fcode;\s*(?:zsh)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!cpp-code" String="&fcode;\s*(?:[ch]pp|[ch]\+\+|[ch]xx|h?cc|hh|cuh?|ino|pde|moc)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!csharp-code" String="&fcode;\s*(?:cs|csharp|c\#)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!cmake-code" String="&fcode;\s*(?:cmake|CMakeLists(?:\.txt)?)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!css-code" String="&fcode;\s*css&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!c-code" String="&fcode;\s*[ch]&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!doxygen-code" String="&fcode;\s*doxygen&end;" insensitive="true" beginRegion="code-block"/> <!-- Block comment of Doxygen -->
+ <RegExpr attribute="Fenced Code" context="#pop!email-code" String="&fcode;\s*(?:email|emlx?|mbo?x)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!go-code" String="&fcode;\s*go(?:lang)?&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!hamlet-code" String="&fcode;\s*[wxs]?hamlet&end;" insensitive="true" beginRegion="code-block"/> <!-- Included in the Haskell definition -->
+ <RegExpr attribute="Fenced Code" context="#pop!haskell-code" String="&fcode;\s*(?:haskell|c?hs|hs\-boot)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!html-code" String="&fcode;\s*(?:[sx]?html?|inc|tmpl|tpl)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!java-code" String="&fcode;\s*(?:java|bsh)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!javascript-code" String="&fcode;\s*(?:javascript|m?js|es6|kwinscript|julius)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!jsx-code" String="&fcode;\s*(?:jsx|tsx|(?:java|type)script\-react)&end;" insensitive="true" beginRegion="code-block"/> <!-- Included in the HTML definition. Also apply for TSX. -->
+ <RegExpr attribute="Fenced Code" context="#pop!json-code" String="&fcode;\s*(?:json5?|gltf)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!yaml-code" String="&fcode;\s*(?:ya?ml)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!matlab-code" String="&fcode;\s*matlab&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!markdown-code" String="&fcode;\s*(?:markdown|m?md)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!mustache-code" String="&fcode;\s*(?:handlebars|hbs|mustache|mst|ractive|hogan|hulk)&end;" insensitive="true" beginRegion="code-block"/> <!-- Included in the HTML definition -->
+ <RegExpr attribute="Fenced Code" context="#pop!perl-code" String="&fcode;\s*(?:perl|p[lm]|pod|psgi|vcl)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!php-code" String="&fcode;\s*(?:php[3457t]?|wml|phtml?|aw|ctp)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!python-code" String="&fcode;\s*(?:python[23]?|py[23w]?|[rc]py|sconstruct|gypi?)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!qml-code" String="&fcode;\s*qml(?:types)?&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!r-code" String="&fcode;\s*(?:r|rprofile|rscript)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!raku-code" String="&fcode;\s*(?:raku(?:mod|doc|test)?|perl6|p[lm]?6|pod6|nqp)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!rest-code" String="&fcode;\s*(?:rst|rest|restructuredtext)&end;" insensitive="true" beginRegion="code-block"/> <!-- Included in the CMake definition -->
+ <RegExpr attribute="Fenced Code" context="#pop!ruby-code" String="&fcode;\s*(?:ruby|rbx?|rjs|rake|f?cgi|gemspec|irbrc|ru|prawn|Appraisals|(?:Rake|Cap|Chef|Gem|Guard|Hobo|Vagrant||Rant|Berks|Thor|Puppet)file|rxml|(?:xml|js)\.erb)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!rust-code" String="&fcode;\s*(?:rust|rs)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!mysql-code" String="&fcode;\s*(?:mysql|sql|ddl)&end;" insensitive="true" beginRegion="code-block"/> <!-- Included in the PHP definition -->
+ <RegExpr attribute="Fenced Code" context="#pop!nim-code" String="&fcode;\s*(?:nims?)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!typescript-code" String="&fcode;\s*(?:typescript|ts)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!xml-code" String="&fcode;\s*(?:xml|xsd|xspf|tld|jsp|c?pt|dtml|rss|opml|svg|daml|rdf|ui|kcfg|qrc|wsdl|scxml|xbel|dae|sch|brd|docbook)&end;" insensitive="true" beginRegion="code-block"/>
+ <RegExpr attribute="Fenced Code" context="#pop!code" String="&fcode;.*$" beginRegion="code-block"/>
+ </context>
+ <context name="code" attribute="Code" lineEndContext="#stay"> <!-- Unknown language -->
+ <RegExpr attribute="Fenced Code" context="#pop" String="%1[~`]*(?=&end;)" firstNonSpace="true" dynamic="true" endRegion="code-block"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="bash-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="bash-code" fallthroughContext="Command##Bash">
+ <IncludeRules context="code"/>
<IncludeRules context="##Bash" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="cmake-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="zsh-code" fallthroughContext="Command##Zsh">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##Zsh" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cmake-code">
+ <IncludeRules context="code"/>
<IncludeRules context="##CMake" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="cpp-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="c-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##C" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="cpp-code">
+ <IncludeRules context="code"/>
<IncludeRules context="##C++" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="css-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="csharp-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##C#" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="css-code">
+ <IncludeRules context="code"/>
<IncludeRules context="##CSS" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="email-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="doxygen-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="BlockComment##Doxygen" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="email-code">
+ <IncludeRules context="code"/>
<IncludeRules context="##Email" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="haskell-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="go-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##Go" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="hamlet-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##Hamlet" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="haskell-code">
+ <IncludeRules context="code"/>
<IncludeRules context="##Haskell" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="html-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="html-code">
+ <IncludeRules context="code"/>
<IncludeRules context="##HTML" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="json-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="java-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##Java" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="javascript-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="Normal##JavaScript" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="jsx-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="Normal##JavaScript React (JSX)" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="json-code">
+ <IncludeRules context="code"/>
<IncludeRules context="##JSON" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="php-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="yaml-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##YAML" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" lineEmptyContext="find-code-block" name="markdown-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="Normal Text"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="matlab-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##Matlab" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="mustache-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##Mustache/Handlebars (HTML)" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="perl-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##Perl" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="php-code">
+ <IncludeRules context="code"/>
<IncludeRules context="phpsource##PHP/PHP" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="python-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="python-code">
+ <IncludeRules context="code"/>
<IncludeRules context="##Python" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="qml-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
- <IncludeRules context="##QML" includeAttrib="true"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="qml-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="Normal##QML" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="r-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##R Script" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="raku-code" fallthroughContext="term##Raku">
+ <IncludeRules context="code"/>
+ <IncludeRules context="base##Raku" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="rest-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##reStructuredText" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="ruby-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##Ruby" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="rust-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="rust-code">
+ <IncludeRules context="code"/>
<IncludeRules context="##Rust" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="xml-code">
- <WordDetect attribute="code" context="#pop" String="```"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="mysql-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##SQL (MySQL)" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="nim-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="##Nim" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="typescript-code">
+ <IncludeRules context="code"/>
+ <IncludeRules context="Normal##TypeScript" includeAttrib="true"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="xml-code">
+ <IncludeRules context="code"/>
<IncludeRules context="##XML" includeAttrib="true"/>
</context>
- <context attribute="code" lineEndContext="#stay" name="code">
- <WordDetect attribute="code" context="#pop" String="```"/>
- </context>
- <context attribute="common" name="inc" lineEndContext="#stay">
- <RegExpr attribute="code" String="`[^`]+`"/>
- <RegExpr context="comment" String="&lt;!--" beginRegion="comment"/>
- <RegExpr attribute="reflink" String="&reflinkregex;"/>
- <RegExpr attribute="footnote" String="&footnoteregex;"/>
- <RegExpr attribute="inlinelink" String="&inlinelinkregex;"/>
- <RegExpr attribute="reflinktarget" String="&reflinktargetregex;"/>
- <RegExpr attribute="inlineimage" String="&inlineimageregex;"/>
- <RegExpr attribute="refimage" String="&refimageregex;"/>
- <RegExpr attribute="autolink" String="&autolinkregex;"/>
- <RegExpr attribute="mailtolink" String="&mailtolinkregex;"/>
- <RegExpr attribute="strikeout" minimal="true" String="&strikeoutregex;"/>
- <RegExpr attribute="linebreak" minimal="true" String="&linebreakregex;"/>
- <WordDetect attribute="code" context="bash-code" String="```bash"/>
- <WordDetect attribute="code" context="cmake-code" String="```cmake"/>
- <WordDetect attribute="code" context="cpp-code" String="```cpp"/>
- <WordDetect attribute="code" context="css-code" String="```css"/>
- <WordDetect attribute="code" context="email-code" String="```email"/>
- <WordDetect attribute="code" context="haskell-code" String="```haskell"/>
- <WordDetect attribute="code" context="html-code" String="```html"/>
- <WordDetect attribute="code" context="json-code" String="```json"/>
- <WordDetect attribute="code" context="php-code" String="```php"/>
- <WordDetect attribute="code" context="python-code" String="```python"/>
- <WordDetect attribute="code" context="qml-code" String="```qml"/>
- <WordDetect attribute="code" context="rust-code" String="```rust"/>
- <WordDetect attribute="code" context="xml-code" String="```xml"/>
- <StringDetect attribute="code" context="code" String="```"/>
+
+ <!-- Common -->
+ <context name="inc" attribute="Normal Text" lineEndContext="#stay">
+ <!-- Code -->
+ <RegExpr attribute="Code" String="`[^`]+`(?!`)|`{2}[^`](?:.*?[^`])?`{2}(?!`)|`{3}[^`](?:.*?[^`])?`{3}(?!`)|`{4}[^`](?:.*?[^`])?`{4}(?!`)|`{5,}[^`](?:.*?[^`])?`{5,}"/>
+ <!-- Find Fenced Code Block -->
+ <RegExpr attribute="Fenced Code" context="find-lang-fenced-code" String="`{3,}(?=[^`]*$)|~{3,}(?=[^~]*$)" firstNonSpace="true" lookAhead="true"/>
+ <!-- Comment -->
+ <StringDetect attribute="Comment" context="comment" String="&startcomment;" beginRegion="comment"/>
+ <!-- Links and References -->
+ <RegExpr attribute="Reference-Link Target" context="reflinktarget" String="&reflinktargetregex;" firstNonSpace="true"/>
+ <RegExpr attribute="Footnote" String="&footnoteregex;"/>
+ <RegExpr attribute="Reference-Link" context="find-reflink" String="&reflinkregex;"/>
+ <RegExpr attribute="Inline Image" context="inlineimage" String="&inlineimageregex;"/>
+ <RegExpr attribute="Reference Image" String="&refimageregex;"/>
+ <RegExpr attribute="Auto-Link" context="autolink" String="&autolinkregex;" lookAhead="true"/>
+ <RegExpr attribute="Mailto-Link" context="mailtolink" String="&mailtolinkregex;"/>
+ <!-- Emoji -->
+ <RegExpr attribute="Emoji" String="&emojiregex;"/>
+ <!-- Line Break -->
+ <RegExpr attribute="Line Break" minimal="true" String="&linebreakregex;"/>
+ <!-- Backslash Escapes -->
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="\"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="`"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="*"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="_"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="{"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="}"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="["/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="]"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="("/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1=")"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="#"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="+"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="-"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="."/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="!"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="|"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="&lt;"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="&gt;"/>
+ <Detect2Chars attribute="Backslash Escape" char="\" char1="&amp;"/>
+ <!-- Inline HTML -->
+ <RegExpr attribute="EntityRef" String="&amp;(?:#[0-9]+|#[xX][0-9A-Fa-f]+|&htmlname;);"/>
+ <RegExpr context="find-html-block" String="&lt;/?&htmlname;(?:[\s&gt;]|/&gt;|$)" lookAhead="true"/>
+ </context>
+ <context name="find-html-block" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="#pop">
+ <IncludeRules context="FindElements##HTML"/>
+ </context>
+
+ <!-- Links and email: <https://example.com>, <example@kde.org> -->
+ <context name="autolink" attribute="Auto-Link" lineEndContext="#pop" fallthroughContext="#pop">
+ <DetectChar attribute="Auto-Link" char="&lt;"/>
+ <DetectChar attribute="Auto-Link" context="#pop" char="&gt;"/>
+ <RegExpr attribute="Link" String="&startlink;[^&quot;&gt;\s]+"/>
+ </context>
+ <context name="mailtolink" attribute="Email" lineEndContext="#pop">
+ <DetectChar attribute="Mailto-Link" context="#pop" char="&gt;"/>
+ </context>
+
+ <!-- References: [example][id], [example](http://example.com) -->
+ <context name="find-reflink" attribute="Reference-Link Name" lineEndContext="#pop">
+ <Detect2Chars attribute="Reference-Link Name" char="\" char1="]"/>
+ <RegExpr attribute="Reference-Link" context="#pop!find-reflink-id" String="\](?=&reflinkidregex;)"/> <!-- Find id -->
+ <DetectChar attribute="Reference-Link" context="#pop" char="]"/>
+ <!-- Image as link or reference -->
+ <RegExpr attribute="Inline Image" context="inlineimage" String="&startinlineimage;(?=&endinlineimage;&refchar;*\])"/>
+ <RegExpr attribute="Reference Image" String="&refimageregex;(?=&refchar;*\])"/>
+ <RegExpr attribute="Reference-Link Name" context="#stay" String="(?:\[(?:\\.|[^\[\]\\])*\](?:\\.|[^\[\]\\])*)+(?=\])" minimal="true"/>
+ </context>
+ <context name="find-reflink-id" attribute="Reference-Link" lineEndContext="#pop">
+ <DetectSpaces />
+ <!-- Ref.: [an example][id] "Optional title" -->
+ <DetectChar attribute="Reference-Link" context="#pop!reflink-id" char="["/>
+ <!-- Inline Ref.: [an example](http://example.com/ "Title") -->
+ <DetectChar attribute="Reference-Link" context="#pop!reflink-inline-id" char="("/>
+ </context>
+ <context name="reflink-inline-id" attribute="Reference-Link" lineEndContext="#pop">
+ <Detect2Chars attribute="Reference-Link" char="\" char1=")"/>
+ <DetectChar attribute="Reference-Link" context="#pop" char=")"/>
+ <RegExpr attribute="Reference-Link: Link" String="\b&startlink;(?:\\.|[^&quot;&gt;\s\)\\])+"/>
+ <RegExpr attribute="Reference-Link: Email" String="\b&email;\b"/>
+ </context>
+ <context name="reflink-id" attribute="Reference-Link ID" lineEndContext="#pop">
+ <Detect2Chars attribute="Reference-Link ID" char="\" char1="]"/>
+ <DetectChar attribute="Reference-Link" context="#pop!reflink-title" char="]"/>
+ <RegExpr attribute="Reference-Link: Link" String="\b&startlink;(?:\\.|[^&quot;&gt;\s\]\\])+"/>
+ </context>
+ <!-- [an example][id] "title" -->
+ <context name="reflink-title" attribute="Normal Text" lineEndContext="#pop" fallthroughContext="#pop">
+ <RegExpr attribute="Reference-Link" context="#pop" String="\s+&quot;(?:\\.|[^&quot;\\])*&quot;"/>
+ </context>
+
+ <!-- Target Ref.: [foo]: http://example.com/ "Optional Title Here" -->
+ <context name="reflinktarget" attribute="Reference-Link Target" lineEndContext="#pop" fallthroughContext="#pop">
+ <RegExpr attribute="Reference-Link Target: Link" context="#pop!reflinktarget-title" String="\s*&link;(?=\s|$)"/>
+ <RegExpr attribute="Reference-Link Target" context="#pop!reflinktarget-link" String="\s*&lt;(?=&link;&gt;(?:\s|$))"/>
+ <RegExpr attribute="Reference-Link Target" context="#pop!reflinktarget-title" String="\s*#[\w\.\:\-]+\b"/>
+ </context>
+ <context name="reflinktarget-link" attribute="Reference-Link Target" lineEndContext="#pop" fallthroughContext="#pop">
+ <DetectChar attribute="Reference-Link Target" context="#pop!reflinktarget-title" char="&gt;"/>
+ <RegExpr attribute="Reference-Link Target: Link" String="&link;"/>
+ </context>
+ <context name="reflinktarget-title" attribute="Reference-Link Target" lineEndContext="#pop" fallthroughContext="#pop">
+ <DetectSpaces attribute="Reference-Link Target"/>
+ <RegExpr attribute="Reference-Link Target" context="#pop" String="&quot;(?:\\.|[^&quot;\\])*&quot;|&apos;(?:\\.|[^&apos;\\])*&apos;|\((?:\\.|[^\)\\])*\)"/>
+ </context>
+
+ <!-- Image -->
+ <context name="inlineimage" attribute="Inline Image" lineEndContext="#pop">
+ <DetectChar attribute="Inline Image" context="#pop" char=")"/>
+ <Detect2Chars attribute="Inline Image" char="\" char1=")"/>
+ <RegExpr attribute="Inline Image: Link" String="\b&startlink;(?:\\.|[^&quot;&gt;\s\)\\])+"/>
</context>
</contexts>
<itemDatas>
<itemData name="Normal Text" defStyleNum="dsNormal"/>
- <itemData name="common" defStyleNum="dsNormal"/>
- <itemData name="strongemphasis" defStyleNum="dsNormal" italic="true" bold="true"/>
- <itemData name="emphasis" defStyleNum="dsNormal" italic="true"/>
- <itemData name="strong" defStyleNum="dsNormal" bold="true"/>
- <itemData name="ruler" defStyleNum="dsNormal" bold="true"/>
- <itemData name="strikeout" defStyleNum="dsNormal" strikeOut="true"/>
- <itemData name="linebreak" defStyleNum="dsNormal" underline="true" color="#999999"/>
- <itemData name="h1" defStyleNum="dsFunction" bold="true"/>
- <itemData name="h2" defStyleNum="dsFunction" bold="true"/>
- <itemData name="h3" defStyleNum="dsFunction" bold="true"/>
- <itemData name="h4" defStyleNum="dsFunction" bold="true"/>
- <itemData name="h5" defStyleNum="dsFunction" bold="true"/>
- <itemData name="h6" defStyleNum="dsFunction" bold="true"/>
- <itemData name="blockquote" defStyleNum="dsDataType"/>
- <itemData name="bq-emphasis" defStyleNum="dsDataType" italic="true"/>
- <itemData name="bq-strong" defStyleNum="dsDataType" bold="true"/>
- <itemData name="bullet" defStyleNum="dsString"/>
- <itemData name="bl-emphasis" defStyleNum="dsString" italic="true"/>
- <itemData name="bl-strong" defStyleNum="dsString" bold="true"/>
- <itemData name="numlist" defStyleNum="dsString"/>
- <itemData name="nl-emphasis" defStyleNum="dsString" italic="true"/>
- <itemData name="nl-strong" defStyleNum="dsString" bold="true"/>
- <itemData name="comment" defStyleNum="dsComment"/>
- <itemData name="code" defStyleNum="dsBaseN"/>
- <itemData name="reflink" defStyleNum="dsOthers" underline="true"/>
- <itemData name="inlinelink" defStyleNum="dsOthers" underline="true"/>
- <itemData name="autolink" defStyleNum="dsOthers" underline="true"/>
- <itemData name="mailtolink" defStyleNum="dsOthers" underline="true"/>
- <itemData name="footnote" defStyleNum="dsOthers" italic="true"/>
- <itemData name="meta" defStyleNum="dsComment"/>
- <itemData name="reflinktarget" defStyleNum="dsOthers" italic="false" bold="false"/>
- <itemData name="inlineimage" defStyleNum="dsAlert" italic="false" bold="false"/>
- <itemData name="refimage" defStyleNum="dsAlert" italic="false" bold="false"/>
+ <itemData name="Emphasis Text" defStyleNum="dsNormal" italic="true"/>
+ <itemData name="Strong Text" defStyleNum="dsNormal" bold="true"/>
+ <itemData name="Strong-Emphasis Text" defStyleNum="dsNormal" italic="true" bold="true"/>
+ <itemData name="Strikethrough Text" defStyleNum="dsNormal" strikeOut="true"/>
+ <itemData name="Highlight Text" defStyleNum="dsAlert"/>
+ <itemData name="Normal Text: Link" defStyleNum="dsNormal" underline="true" spellChecking="false"/>
+ <itemData name="Horizontal Rule" defStyleNum="dsNormal" bold="true" spellChecking="false"/>
+ <itemData name="Line Break" defStyleNum="dsNormal" underline="true" color="#999999" spellChecking="false"/>
+ <itemData name="Header H1" defStyleNum="dsFunction" bold="true"/>
+ <itemData name="Header H2" defStyleNum="dsFunction" bold="true"/>
+ <itemData name="Header H3" defStyleNum="dsFunction" bold="true"/>
+ <itemData name="Header H4" defStyleNum="dsFunction" bold="true"/>
+ <itemData name="Header H5" defStyleNum="dsFunction" bold="true"/>
+ <itemData name="Header H6" defStyleNum="dsFunction" bold="true"/>
+ <itemData name="Blockquote" defStyleNum="dsAttribute" spellChecking="false"/>
+ <itemData name="Blockquote: Normal Text" defStyleNum="dsAttribute"/>
+ <itemData name="Blockquote: Emphasis Text" defStyleNum="dsAttribute" italic="true"/>
+ <itemData name="Blockquote: Strong Text" defStyleNum="dsAttribute" bold="true"/>
+ <itemData name="Blockquote: Strong-Emphasis Text" defStyleNum="dsAttribute" italic="true" bold="true"/>
+ <itemData name="Blockquote: Strikethrough Text" defStyleNum="dsAttribute" strikeOut="true"/>
+ <itemData name="Blockquote: Highlight Text" defStyleNum="dsAlert"/>
+ <itemData name="Blockquote: Link" defStyleNum="dsAttribute" underline="true" spellChecking="false"/>
+ <itemData name="List" defStyleNum="dsSpecialString" bold="1" spellChecking="false"/>
+ <itemData name="Number List" defStyleNum="dsSpecialString" spellChecking="false"/>
+ <itemData name="List: Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="List: Emphasis Text" defStyleNum="dsNormal" italic="true"/>
+ <itemData name="List: Strong Text" defStyleNum="dsNormal" bold="true"/>
+ <itemData name="List: Strong-Emphasis Text" defStyleNum="dsNormal" italic="true" bold="true"/>
+ <itemData name="List: Strikethrough Text" defStyleNum="dsNormal" strikeOut="true"/>
+ <itemData name="List: Highlight Text" defStyleNum="dsAlert"/>
+ <itemData name="List: Link" defStyleNum="dsNormal" underline="true" spellChecking="false"/>
+ <itemData name="List: Checkbox" defStyleNum="dsVariable" spellChecking="false"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Code" defStyleNum="dsInformation"/>
+ <itemData name="Fenced Code" defStyleNum="dsInformation" spellChecking="false"/>
+ <itemData name="Table" defStyleNum="dsPreprocessor"/>
+ <itemData name="Emoji" defStyleNum="dsSpecialChar" spellChecking="false"/>
+ <itemData name="Auto-Link" defStyleNum="dsOthers" spellChecking="false"/>
+ <itemData name="Link" defStyleNum="dsOthers" underline="true" spellChecking="false"/>
+ <itemData name="Mailto-Link" defStyleNum="dsOthers" spellChecking="false"/>
+ <itemData name="Email" defStyleNum="dsOthers" italic="true" underline="true" spellChecking="false"/>
+ <itemData name="Footnote" defStyleNum="dsOthers" italic="true"/>
+ <itemData name="Reference-Link" defStyleNum="dsComment" italic="false"/>
+ <itemData name="Reference-Link Name" defStyleNum="dsOthers" underline="true" italic="false"/>
+ <itemData name="Reference-Link ID" defStyleNum="dsComment" italic="false"/>
+ <itemData name="Reference-Link: Link" defStyleNum="dsComment" underline="true" italic="false" spellChecking="false"/>
+ <itemData name="Reference-Link: Email" defStyleNum="dsComment" italic="true" underline="true" spellChecking="false"/>
+ <itemData name="Reference-Link Target" defStyleNum="dsOthers" italic="false" bold="false"/>
+ <itemData name="Reference-Link Target: Link" defStyleNum="dsOthers" underline="true" spellChecking="false"/>
+ <itemData name="Inline Image" defStyleNum="dsAlert" italic="false" bold="false"/>
+ <itemData name="Reference Image" defStyleNum="dsAlert" italic="false" bold="false"/>
+ <itemData name="Inline Image: Link" defStyleNum="dsAlert" italic="false" bold="false" underline="true"/>
+ <itemData name="Metadata Title" defStyleNum="dsAnnotation"/>
+ <itemData name="Metadata" defStyleNum="dsComment" italic="0"/>
+ <itemData name="Backslash Escape" defStyleNum="dsSpecialChar" spellChecking="false"/>
+ <itemData name="EntityRef" defStyleNum="dsDecVal" spellChecking="false"/>
</itemDatas>
</highlighting>
<general>
+ <keywords additionalDeliminator="`"/>
<comments>
- <comment name="multiLine" start="&lt;!--" end="--&gt;" region="comment"/>
+ <comment name="multiLine" start="&startcomment;" end="&endcomment;" region="comment"/>
</comments>
</general>
</language>
+<!-- kate: replace-tabs on; indent-width 2; tab-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/modelines.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/modelines.xml
index 0f3c882b6c..855bca4802 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/modelines.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/modelines.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd"
+<!DOCTYPE language
[
<!ENTITY space " ">
<!ENTITY end "&#59;">
@@ -8,7 +8,7 @@
Copyright (c) 2012-2014 by Alex Turbov (i.zaufi@gmail.com)
-->
<language name="Modelines"
- version="4"
+ version="6"
kateversion="5.0"
section="Other"
extensions=""
@@ -127,50 +127,44 @@
</list>
<contexts>
- <context name="Normal" attribute="Comment" lineEndContext="#pop">
- <DetectSpaces />
+ <context name="Normal" attribute="Comment" lineEndContext="#stay">
<keyword String="ModelineStartKeyword" context="Modeline" attribute="Keyword" />
- <RegExpr String="kate-(mimetype|wildcard)\(.*\):" context="Modeline" attribute="Keyword" />
+ <RegExpr String="\bkate-(?:mimetype|wildcard)\(.*\):" context="Modeline" attribute="Keyword" />
</context>
- <context name="Modeline" attribute="Comment" lineEndContext="#pop">
+ <context name="Modeline" attribute="Comment" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
<DetectSpaces />
<keyword String="Booleans" context="Booleans" attribute="Variable" />
<keyword String="Integrals" context="Integrals" attribute="Variable" />
<keyword String="Strings" context="Strings" attribute="Variable" />
<keyword String="RemoveSpaces" context="RemoveSpaces" attribute="Variable" />
- <LineContinue context="#pop" />
</context>
- <context name="Booleans" attribute="Comment" lineEndContext="#pop">
+ <context name="Booleans" attribute="Comment" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
<DetectSpaces />
<keyword String="True" attribute="Option ON" context="#stay" />
<keyword String="False" attribute="Option OFF" context="#stay" />
<DetectChar char="&end;" context="#pop" attribute="Variable" />
- <LineContinue context="#pop" />
</context>
- <context name="Integrals" attribute="Comment" lineEndContext="#pop">
+ <context name="Integrals" attribute="Comment" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
<DetectSpaces />
<Int attribute="Number" context="#stay" />
<DetectChar char="&end;" context="#pop" attribute="Variable" />
- <LineContinue context="#pop" />
</context>
<context name="Strings" attribute="String" lineEndContext="#pop">
<DetectSpaces />
<RegExpr String="[^&end;&space;]" context="#stay" />
<DetectChar char="&end;" context="#pop" attribute="Variable" />
- <LineContinue context="#pop" />
</context>
- <context name="RemoveSpaces" attribute="Comment" lineEndContext="#pop">
+ <context name="RemoveSpaces" attribute="Comment" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
<DetectSpaces />
<keyword String="RemoveSpacesOptions" attribute="Value" context="#pop!RemoveSpacesEnd" />
<DetectChar char="&end;" context="#pop" attribute="Variable" />
- <LineContinue context="#pop" />
</context>
- <context name="RemoveSpacesEnd" attribute="Comment" lineEndContext="#pop">
+ <context name="RemoveSpacesEnd" attribute="Comment" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
<DetectChar char="&end;" context="#pop" attribute="Variable" />
</context>
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/perl.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/perl.xml
index 0d1931649c..ed843a7d46 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/perl.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/perl.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd">
+<!DOCTYPE language>
<!--
This file is part of the KDE project
Copyright (C) 2001, 2002, 2003, 2004 Anders Lund <anders@alweb.dk>
@@ -39,7 +39,7 @@
Enhance tr/// and y/// support.
-->
-<language name="Perl" version="8" kateversion="2.4" section="Scripts" extensions="*.pl;*.PL;*.pm;*.pl6;*.PL6;*.p6;*.pm6;" mimetype="application/x-perl;text/x-perl" priority="5" author="Anders Lund (anders@alweb.dk)" license="LGPLv2">
+<language name="Perl" version="19" kateversion="5.0" section="Scripts" extensions="*.pl;*.PL;*.pm" mimetype="application/x-perl;text/x-perl" priority="5" author="Anders Lund (anders@alweb.dk)" license="LGPLv2">
<highlighting>
<list name="keywords">
<item>if</item>
@@ -92,7 +92,7 @@
<item>+</item>
<item>-</item>
<item>*</item>
- <!-- <item>/</item>//-->
+ <!-- <item>/</item>//-->
<item>%</item>
<item>||</item>
<item>//</item>
@@ -252,6 +252,7 @@
<item>rewinddir</item>
<item>rindex</item>
<item>rmdir</item>
+ <item>say</item>
<item>scalar</item>
<item>seek</item>
<item>seekdir</item>
@@ -338,7 +339,7 @@
</list>
<contexts>
<context name="normal" attribute="Normal Text" lineEndContext="#stay">
- <RegExpr attribute="Keyword" context="#stay" String="^#!\/.*" />
+ <RegExpr attribute="Keyword" context="#stay" String="^#!\/.*" column="0" />
<StringDetect attribute="Keyword" context="data_handle" String="__DATA__" firstNonSpace="true" />
<StringDetect attribute="Keyword" context="#stay" String="__END__" firstNonSpace="true"/>
<RegExpr attribute="Keyword" context="sub_name_def" String="\bsub\s+" />
@@ -346,7 +347,7 @@
<keyword attribute="Operator" context="#stay" String="operators" />
<keyword attribute="Function" context="#stay" String="functions" />
<keyword attribute="Pragma" context="#stay" String="pragmas" />
- <RegExpr attribute="Pod" context="pod" String="\=\w+(\s|$)" column="0" beginRegion="POD"/>
+ <RegExpr attribute="Pod" context="pod" String="^\=\w+(\s|$)" column="0" beginRegion="POD"/>
<DetectSpaces />
<DetectChar attribute="Comment" context="comment" char="#" />
@@ -383,8 +384,7 @@
<RegExpr attribute="Operator" context="find_pattern" String="\b(?:m|qr)(?=\s*[^\w\s\]})])" />
- <RegExpr attribute="Normal Text" context="#stay" String="[\w_]+\s*//?\=?" />
- <RegExpr attribute="Normal Text" context="#stay" String="[&lt;&gt;&quot;':]//?\=?" />
+ <RegExpr attribute="Normal Text" context="#stay" String="[\w_]+\s*//?\=?|[&lt;&gt;&quot;':]//?\=?" />
<!-- Avoid conflicts between operators / and // -->
<StringDetect attribute="Normal Text" context="#stay" String="//=" />
<Detect2Chars attribute="Normal Text" context="#stay" char="/" char1="/" />
@@ -420,22 +420,15 @@
<DetectChar attribute="Operator" context="quote_word_paren" char="(" beginRegion="Wordlist" />
<DetectChar attribute="Operator" context="quote_word_brace" char="{" beginRegion="Wordlist" />
<DetectChar attribute="Operator" context="quote_word_bracket" char="[" beginRegion="Wordlist" />
- <RegExpr attribute="Operator" context="quote_word" String="([^a-zA-Z0-9_\s[\]{}()])" beginRegion="Wordlist" />
+ <DetectChar attribute="Operator" context="quote_word_angular" char="&lt;" beginRegion="Wordlist" />
+ <RegExpr attribute="Operator" context="quote_word" String="([^a-zA-Z0-9_\s[\]{}()\&lt;&gt;])" beginRegion="Wordlist" />
<RegExpr attribute="Comment" context="#stay" String="\s+#.*" /><!-- q[qwx] # == comment, look for the delim on the next line -->
</context>
<!-- ====== Contexts for strings ===== -->
<context name="ipstring_internal" attribute="String (interpolated)" lineEndContext="#stay">
<DetectIdentifier />
- <RegExpr attribute="String Special Character" context="#stay" String="\\c[^\s\\]" />
- <RegExpr attribute="String Special Character" context="#stay" String="\\g(\{(\w+|\-\d+)\}|\d+)" />
- <RegExpr attribute="String Special Character" context="#stay" String="\\k(\{\w+\}|&lt;\w+&gt;|'\w+')" />
- <RegExpr attribute="String Special Character" context="#stay" String="\\N\{[^\{\}]*\}" />
- <RegExpr attribute="String Special Character" context="#stay" String="\\o\{[0-7]+\}" />
- <RegExpr attribute="String Special Character" context="#stay" String="\\[pP](\{\w+\}|P)" />
- <RegExpr attribute="String Special Character" context="#stay" String="\\x([0-9a-fA-F]{2}|\{[0-9a-fA-F]+\})" />
- <RegExpr attribute="String Special Character" context="#stay" String="\\[0-7]{3}" />
- <RegExpr attribute="String Special Character" context="#stay" String="\\[1aAbBdDeEfFGhHKlLnNQrRsStuUvVwWXzZ]" />
+ <RegExpr attribute="String Special Character" context="#stay" String="\\c[^\s\\]|\\g(\{(\w+|\-\d+)\}|\d+)|\\k(\{\w+\}|&lt;\w+&gt;|'\w+')|\\N\{[^\{\}]*\}|\\o\{[0-7]+\}|\\[pP](\{\w+\}|P)|\\x([0-9a-fA-F]{2}|\{[0-9a-fA-F]+\})|\\[0-7]{3}|\\[1aAbBdDeEfFGhHKlLnNQrRsStuUvVwWXzZ]" />
<RegExpr attribute="String (interpolated)" context="#stay" String="\\." />
<RegExpr attribute="Normal Text" context="find_variable_unsafe" String="(?:[\$@]\S|%([\w\{\-\+!]|\^H))" lookAhead="true" />
</context>
@@ -464,7 +457,7 @@
<IncludeRules context="ipstring_internal" />
</context>
<context name="ip_string_6" attribute="String (interpolated)" lineEndContext="#stay" dynamic="true">
- <RegExpr attribute="String (interpolated)" context="#stay" String="\%1" dynamic="true" />
+ <StringDetect attribute="String (interpolated)" context="#stay" String="\%1" dynamic="true" />
<DetectChar attribute="Operator" context="#pop#pop#pop" char="1" dynamic="true" endRegion="String"/>
<IncludeRules context="ipstring_internal" />
</context>
@@ -507,7 +500,7 @@
<context name="string_6" attribute="String" lineEndContext="#stay" dynamic="true">
<DetectIdentifier />
<Detect2Chars attribute="String Special Character" context="#stay" char="\" char1="\" />
- <RegExpr attribute="String Special Character" context="#stay" String="\%1" dynamic="true"/>
+ <StringDetect attribute="String Special Character" context="#stay" String="\%1" dynamic="true"/>
<DetectChar attribute="Operator" context="#pop#pop" char="1" dynamic="true" endRegion="String" />
</context>
@@ -533,7 +526,7 @@
<DetectChar attribute="Pattern" context="subst_curlybrace_pattern_recursive" char="{" />
</context>
<context name="subst_curlybrace_middle" attribute="Normal Text" lineEndContext="#stay" >
- <RegExpr attribute="Comment" context="#stay" String="#.*$" />
+ <DetectChar attribute="Comment" context="comment" char="#" />
<DetectChar attribute="Operator" context="subst_curlybrace_replace" char="{" beginRegion="Replacement" />
</context>
<context name="subst_curlybrace_replace" attribute="String (interpolated)" lineEndContext="#stay">
@@ -551,8 +544,12 @@
<IncludeRules context="subst_curlybrace_pattern" />
</context>
+ <context name="subst_maybe_comment" attribute="Pattern" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
+ <DetectChar attribute="Comment" context="#pop!comment" char="#" />
+ </context>
+
<context name="subst_paren_pattern" attribute="Pattern" lineEndContext="#stay">
- <RegExpr attribute="Comment" context="#stay" String="\s+#.*$" />
+ <DetectSpaces attribute="Pattern" context="subst_maybe_comment" />
<DetectChar attribute="Operator" context="subst_paren_replace" char=")" endRegion="Pattern" />
<!-- Round brackets of RegExp pattern inside -->
<DetectChar attribute="Pattern Internal Operator" context="subst_paren_pattern_internal_recursive" char="(" />
@@ -569,7 +566,7 @@
</context>
<context name="subst_bracket_pattern" attribute="Pattern" lineEndContext="#stay">
- <RegExpr attribute="Comment" context="#stay" String="\s+#.*$" />
+ <DetectSpaces attribute="Pattern" context="subst_maybe_comment" />
<IncludeRules context="regex_pattern_internal_ip" />
<DetectChar attribute="Operator" context="subst_bracket_replace" char="]" endRegion="Pattern" />
</context>
@@ -591,7 +588,7 @@
</context>
<context name="subst_sq_pattern" attribute="Pattern" lineEndContext="#stay">
- <RegExpr attribute="Comment" context="#stay" String="\s+#.*$" />
+ <DetectSpaces attribute="Pattern" context="subst_maybe_comment" />
<IncludeRules context="regex_pattern_internal" />
<DetectChar attribute="Operator" context="subst_sq_replace" char="'" endRegion="Pattern" beginRegion="Pattern" />
</context>
@@ -600,10 +597,7 @@
</context>
<context name="tr" attribute="Pattern" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop" >
- <RegExpr attribute="Pattern" context="#pop" String="\([^)]*\)\s*\(?:[^)]*\)" />
- <RegExpr attribute="Pattern" context="#pop" String="\{[^}]*\}\s*\{[^}]*\}" />
- <RegExpr attribute="Pattern" context="#pop" String="\[[^]]*\]\s*\[[^\]]*\]" />
- <RegExpr attribute="Pattern" context="#pop" String="([^a-zA-Z0-9_\s[\]{}()]).*\1.*\1" minimal="true"/>
+ <RegExpr attribute="Pattern" context="#pop" String="\([^)]*\)\s*\(?:[^)]*\)|\{[^}]*\}\s*\{[^}]*\}|\[[^]]*\]\s*\[[^\]]*\]|([^a-zA-Z0-9_\s[\]{}()]).*?\1.*?\1"/>
</context>
<!-- ====== PATTERNs ====== -->
@@ -624,7 +618,6 @@
<RegExpr attribute="Pattern Internal Operator" context="#stay" String="\$(?=%1)" dynamic="true" />
<RegExpr attribute="Operator" context="#pop#pop" String="%1[cgimosx]*" dynamic="true" endRegion="Pattern" />
<IncludeRules context="regex_pattern_internal_ip" />
- <RegExpr attribute="Pattern Internal Operator" context="#stay" String="\$(?=\%1)" dynamic="true" />
</context>
<context name="pattern_brace" attribute="Pattern" lineEndContext="#stay">
<RegExpr attribute="Operator" context="#pop#pop" String="\}[cgimosx]*" endRegion="Pattern" />
@@ -657,14 +650,8 @@
<!-- rules internal stuff wrt regex patterns -->
<context name="regex_pattern_internal_rules_1" attribute="Pattern" lineEndContext="#stay">
- <RegExpr attribute="Comment" context="#stay" String="#.*$" firstNonSpace="true" />
- <RegExpr attribute="Special Variable" context="#stay" String="\\c[^\s\\]" />
- <RegExpr attribute="Special Variable" context="#stay" String="\\g(\{(\w+|\-\d+)\}|\d+)" />
- <RegExpr attribute="Special Variable" context="#stay" String="\\k(\{\w+\}|&lt;\w+&gt;|'\w+')" />
- <RegExpr attribute="Special Variable" context="#stay" String="\\N\{[^\{\}]*\}" />
- <RegExpr attribute="Special Variable" context="#stay" String="\\o\{[0-7]+\}" />
- <RegExpr attribute="Special Variable" context="#stay" String="\\[pP](\{\w+\}|P)" />
- <RegExpr attribute="Special Variable" context="#stay" String="\\x([0-9a-fA-F]{2}|\{[0-9a-fA-F]+\})" />
+ <DetectChar attribute="Comment" context="comment" char="#" firstNonSpace="true" />
+ <RegExpr attribute="Special Variable" context="#stay" String="\\c[^\s\\]|\\g(\{(\w+|\-\d+)\}|\d+)|\\k(\{\w+\}|&lt;\w+&gt;|'\w+')|\\N\{[^\{\}]*\}|\\o\{[0-7]+\}|\\[pP](\{\w+\}|P)|\\x([0-9a-fA-F]{2}|\{[0-9a-fA-F]+\})" />
<RegExpr attribute="Pattern Character Class" context="#stay" String="\\[anDdSsWw]" />
<RegExpr attribute="Pattern Internal Operator" context="#stay" String="\\[ABbEGLlNUuQdQZz]" /> <!-- Also?: efFhHKrRtvVX -->
<RegExpr attribute="Special Variable" context="#stay" String="\\[\d]+" />
@@ -673,7 +660,7 @@
<context name="regex_pattern_internal_rules_2" attribute="Pattern" lineEndContext="#stay">
<Detect2Chars attribute="Pattern Internal Operator" context="pat_ext" char="(" char1="?" />
<DetectChar attribute="Pattern Internal Operator" context="pat_char_class" char="[" />
- <RegExpr attribute="Pattern Internal Operator" context="#stay" String="[()?^*+|]" />
+ <AnyChar attribute="Pattern Internal Operator" context="#stay" String="()?^*+|" />
<RegExpr attribute="Pattern Internal Operator" context="#stay" String="\{[\d, ]+\}" />
<DetectChar attribute="Pattern Internal Operator" context="#stay" char="$" />
<RegExpr attribute="Comment" context="#stay" String="\s{3,}#.*$" />
@@ -693,8 +680,8 @@
<IncludeRules context="regex_pattern_internal_rules_2" />
</context>
<context name="pat_ext" attribute="Pattern Internal Operator" lineEndContext="#stay">
+ <AnyChar attribute="Pattern Internal Operator" context="#pop" String=":=!&gt;&lt;" />
<RegExpr attribute="Comment" context="#pop" String="\#[^)]*" />
- <RegExpr attribute="Pattern Internal Operator" context="#pop" String="[:=!&gt;&lt;]+" />
<DetectChar attribute="Pattern Internal Operator" context="#pop" char=")" />
</context>
<context name="pat_char_class" attribute="Pattern Character Class" lineEndContext="#stay">
@@ -708,17 +695,10 @@
<!-- ====== Variables ====== -->
<context name="find_variable" attribute="Data Type" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop" >
<RegExpr attribute="Data Type" context="var_detect" String="\$[#_][\w_]" />
- <RegExpr attribute="Special Variable" context="var_detect" String="\$[0-9]+" />
- <RegExpr attribute="Special Variable" context="var_detect" String="[@\$](?:[\+\-_]\B|ARGV\b|INC\b)" />
- <RegExpr attribute="Special Variable" context="var_detect" String="[%\$](?:INC\b|ENV\b|SIG\b)" />
- <RegExpr attribute="Special Variable" context="var_detect" String="\$\^[A-Z_\]\[\^\?\\]" />
- <RegExpr attribute="Special Variable" context="var_detect" String="%([\-\+!]|\^H)" />
- <RegExpr attribute="Data Type" context="var_detect" String="\$\$[\$\w_]" />
- <RegExpr attribute="Data Type" context="var_detect" String="\$+::" />
+ <RegExpr attribute="Special Variable" context="var_detect" String="\$[0-9]+|[@\$](?:[\+\-_]\B|ARGV\b|INC\b)|[%\$](?:INC\b|ENV\b|SIG\b)|\$\^[A-Z_\]\[\^\?\\]|%([\-\+!]|\^H)" />
+ <RegExpr attribute="Data Type" context="var_detect" String="\$\$[\$\w_]|\$+::" />
<RegExpr attribute="Special Variable" context="var_detect" String="\$[^a-zA-Z0-9\s{][A-Z]?" />
- <RegExpr attribute="Data Type" context="var_detect" String="[\$@%]\{\^?[\w_]+\}" />
- <AnyChar attribute="Data Type" context="var_detect" String="$@%" />
- <RegExpr attribute="Data Type" context="var_detect" String="\*[a-zA-Z_]+" />
+ <RegExpr attribute="Data Type" context="var_detect" String="[\$@%]\{\^?[\w_]+\}|[$@%]|\*[a-zA-Z_]+" />
<!-- Do not highlight brackets after *, Ex: (... @*) (see bug #391577) -->
<RegExpr attribute="Special Variable" context="#stay" String="\*[^a-zA-Z0-9\s\{\(\)\[\]\}][A-Z]?" />
<!-- this should be a rare case! -->
@@ -727,17 +707,11 @@
<!-- This does not check fo a trailing slash, for usage in strings. -->
<context name="find_variable_unsafe" attribute="Data Type" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop" >
<RegExpr attribute="Data Type" context="var_detect_unsafe" String="\$[#_][\w_]" />
- <RegExpr attribute="Special Variable" context="var_detect_unsafe" String="\$[0-9]+" />
- <RegExpr attribute="Special Variable" context="var_detect_unsafe" String="[@\$](?:[\+\-_]\B|ARGV\b|INC\b)" />
- <RegExpr attribute="Special Variable" context="var_detect_unsafe" String="[%\$](?:INC\b|ENV\b|SIG\b)" />
- <RegExpr attribute="Special Variable" context="var_detect" String="\$\^[A-Z_\]\[\^\?\\]" />
- <RegExpr attribute="Special Variable" context="var_detect" String="%([\-\+!]|\^H)" />
- <RegExpr attribute="Data Type" context="var_detect_unsafe" String="\$\$[\$\w_]" />
- <RegExpr attribute="Data Type" context="var_detect_unsafe" String="\$+::" />
+ <RegExpr attribute="Special Variable" context="var_detect_unsafe" String="\$[0-9]+|[@\$](?:[\+\-_]\B|ARGV\b|INC\b)|[%\$](?:INC\b|ENV\b|SIG\b)" />
+ <RegExpr attribute="Special Variable" context="var_detect" String="\$\^[A-Z_\]\[\^\?\\]|%([\-\+!]|\^H)" />
+ <RegExpr attribute="Data Type" context="var_detect_unsafe" String="\$\$[\$\w_]|\$+::" />
<RegExpr attribute="Special Variable" context="#stay" String="\$[^a-zA-Z0-9\s{][A-Z]?" />
- <RegExpr attribute="Data Type" context="var_detect_unsafe" String="[\$@%]\{\^?[\w_]+\}" />
- <AnyChar attribute="Data Type" context="var_detect_unsafe" String="$@%" />
- <RegExpr attribute="Data Type" context="var_detect_unsafe" String="\*\w+" />
+ <RegExpr attribute="Data Type" context="var_detect_unsafe" String="[\$@%]\{\^?[\w_]+\}|[$@%]|\*\w+" />
<AnyChar attribute="Operator" context="#pop" String="$@%*" />
</context>
<context name="var_detect" attribute="Data Type" lineEndContext="#pop#pop" fallthrough="true" fallthroughContext="#pop#pop">
@@ -762,7 +736,7 @@
<context name="quote_word" attribute="Normal Text" lineEndContext="#stay" dynamic="true">
<DetectSpaces />
<DetectIdentifier />
- <RegExpr attribute="Normal Text" context="#stay" String="\\%1" dynamic="true" />
+ <StringDetect attribute="Normal Text" context="#stay" String="\%1" dynamic="true" />
<DetectChar attribute="Operator" context="#pop#pop#pop" char="1" dynamic="true" endRegion="Wordlist" />
</context>
<context name="quote_word_paren" attribute="Normal Text" lineEndContext="#stay">
@@ -783,29 +757,33 @@
<Detect2Chars attribute="Normal Text" context="#stay" char="\" char1="]" />
<DetectChar attribute="Operator" context="#pop#pop#pop" char="]" endRegion="Wordlist" />
</context>
+ <context name="quote_word_angular" attribute="Normal Text" lineEndContext="#stay">
+ <DetectSpaces />
+ <DetectIdentifier />
+ <Detect2Chars attribute="Normal Text" context="#stay" char="\" char1="&gt;" />
+ <DetectChar attribute="Operator" context="#pop#pop#pop" char="&gt;" endRegion="Wordlist" />
+ </context>
<!-- ====== Here Documents ====== -->
<context name="find_here_document" attribute="Normal Text" lineEndContext="#pop" >
- <RegExpr attribute="Keyword" context="here_document" String="(\w+)\s*;?" />
- <RegExpr attribute="Keyword" context="here_document" String="\s*&quot;([^&quot;]+)&quot;\s*;?" />
- <RegExpr attribute="Keyword" context="here_document" String="\s*`([^`]+)`\s*;?" />
+ <RegExpr attribute="Keyword" context="here_document" String="(?|(\w+)\s*;?|\s*&quot;([^&quot;]+)&quot;\s*;?|\s*`([^`]+)`\s*;?)" />
<RegExpr attribute="Keyword" context="here_document_dumb" String="\s*'([^']+)'\s*;?" />
</context>
<context name="here_document" attribute="String (interpolated)" lineEndContext="#stay" dynamic="true">
- <RegExpr attribute="Keyword" context="#pop#pop" String="%1\b" column="0" dynamic="true" endRegion="HereDocument"/>
+ <RegExpr attribute="Keyword" context="#pop#pop" String="^%1\b" column="0" dynamic="true" endRegion="HereDocument"/>
<RegExpr attribute="Keyword" context="here_document" String="\=\s*&lt;&lt;\s*[&quot;']?([A-Z0-9_\-]+)[&quot;']?" beginRegion="HEREDoc" />
<IncludeRules context="ipstring_internal" />
<DetectSpaces />
</context>
<context name="here_document_dumb" attribute="Normal Text" lineEndContext="#stay" dynamic="true">
- <RegExpr attribute="Keyword" context="#pop#pop" String="%1" column="0" dynamic="true" endRegion="HereDocument"/>
+ <StringDetect attribute="Keyword" context="#pop#pop" String="%1" column="0" dynamic="true" endRegion="HereDocument"/>
<DetectSpaces />
<DetectIdentifier />
</context>
<!-- ====== Misc ====== -->
<context name="data_handle" attribute="Data" lineEndContext="#stay">
- <RegExpr attribute="Pod" context="pod" String="\=(?:head[1-6]|over|back|item|for|begin|end|pod)\s+.*" column="0" beginRegion="POD"/>
+ <RegExpr attribute="Pod" context="pod" String="^\=(?:head[1-6]|over|back|item|for|begin|end|pod)\s+.*" column="0" beginRegion="POD"/>
<StringDetect attribute="Keyword" context="normal" String="__END__" firstNonSpace="true" />
</context>
@@ -836,36 +814,38 @@
<context name="pod" attribute="Pod" lineEndContext="#stay">
<DetectSpaces />
<DetectIdentifier />
- <RegExpr attribute="Pod" context="#stay" String="\=(?:head[1-6]|over|back|item|for|begin|end|pod)\s*.*" column="0" beginRegion="POD" endRegion="POD"/>
- <RegExpr attribute="Pod" context="#pop" String="\=cut.*$" column="0" endRegion="POD"/>
+ <RegExpr attribute="Pod" context="#stay" String="^\=(?:head[1-6]|over|back|item|for|begin|end|pod)\s*.*" column="0" beginRegion="POD" endRegion="POD"/>
+ <StringDetect attribute="Pod" context="Pod" String="=cut" column="0" endRegion="POD"/>
</context>
<context name="comment" attribute="Comment" lineEndContext="#pop">
<DetectSpaces />
- <IncludeRules context="##Alerts" />
+ <IncludeRules context="##Comments" />
<DetectIdentifier />
</context>
+ <context name="Pod" attribute="Pod" lineEndContext="#pop#pop"/>
+
</contexts>
<itemDatas>
<itemData name="Normal Text" defStyleNum="dsNormal" />
- <itemData name="Keyword" defStyleNum="dsKeyword" />
- <itemData name="Pragma" defStyleNum="dsKeyword" />
- <itemData name="Function" defStyleNum="dsFunction" />
- <itemData name="Operator" defStyleNum="dsKeyword" color="#008000"/>
- <itemData name="Data Type" defStyleNum="dsDataType" />
- <itemData name="Special Variable" defStyleNum="dsDataType" color="#C00000" selColor="#C00000" bold="0" italic="0" />
- <itemData name="Decimal" defStyleNum="dsDecVal" />
- <itemData name="Octal" defStyleNum="dsBaseN" />
- <itemData name="Hex" defStyleNum="dsBaseN" />
- <itemData name="Bin" defStyleNum="dsBaseN" />
- <itemData name="Float" defStyleNum="dsFloat" />
- <itemData name="String" defStyleNum="dsString" color="#FF6C6C" selColor="#FF6C6C" bold="0" italic="0" />
+ <itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="Pragma" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="Function" defStyleNum="dsFunction" spellChecking="false"/>
+ <itemData name="Operator" defStyleNum="dsOthers" bold="1" spellChecking="false"/> <!-- #008000 -->
+ <itemData name="Data Type" defStyleNum="dsDataType" spellChecking="false"/>
+ <itemData name="Special Variable" defStyleNum="dsWarning" bold="0" italic="0" /> <!-- #C00000 -->
+ <itemData name="Decimal" defStyleNum="dsDecVal" spellChecking="false"/>
+ <itemData name="Octal" defStyleNum="dsBaseN" spellChecking="false"/>
+ <itemData name="Hex" defStyleNum="dsBaseN" spellChecking="false"/>
+ <itemData name="Bin" defStyleNum="dsBaseN" spellChecking="false"/>
+ <itemData name="Float" defStyleNum="dsFloat" spellChecking="false"/>
+ <itemData name="String" defStyleNum="dsSpecialString" /> <!-- #FF6C6C -->
<itemData name="String (interpolated)" defStyleNum="dsString" />
<itemData name="String Special Character" defStyleNum="dsChar" />
- <itemData name="Pattern" defStyleNum="dsOthers" />
- <itemData name="Pattern Internal Operator" defStyleNum="dsChar" />
- <itemData name="Pattern Character Class" defStyleNum="dsBaseN" />
+ <itemData name="Pattern" defStyleNum="dsOthers" spellChecking="false"/>
+ <itemData name="Pattern Internal Operator" defStyleNum="dsChar" spellChecking="false"/>
+ <itemData name="Pattern Character Class" defStyleNum="dsBaseN" spellChecking="false"/>
<itemData name="Data" defStyleNum="dsNormal" />
<itemData name="Comment" defStyleNum="dsComment" />
<itemData name="Pod" defStyleNum="dsComment" />
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/powershell.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/powershell.xml
index 8208fd7aae..06c6dac079 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/powershell.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/powershell.xml
@@ -1,48 +1,54 @@
-<!DOCTYPE language SYSTEM "language.dtd">
+<!DOCTYPE language [
+ <!ENTITY varscope "(?:global|local|private|script|using|workflow|alias|env|function|variable)">
+ <!ENTITY varname "(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)">
+]>
<language
name="PowerShell"
- version="4"
- kateversion="5.0"
- extensions="*.ps1;*.ps1m;*.ps1d"
+ version="14"
+ kateversion="5.79"
+ extensions="*.ps1;*.psm1;*.psd1"
section="Scripts"
author="Motoki Kashihara (motoki8791@gmail.com); Michael Lombardi (Michael.T.Lombardi@outlook.com)"
casesensitive="0"
license="MIT">
<highlighting>
- <list name="keywords">
- <item>Begin</item>
+ <list name="control-flow">
<item>Exit</item>
<item>Process</item>
<item>Break</item>
- <item>Filter</item>
<item>Return</item>
<item>Catch</item>
<item>Finally</item>
- <item>Sequence</item>
<item>Class</item>
<item>For</item>
<item>Switch</item>
<item>Continue</item>
<item>ForEach</item>
<item>Throw</item>
+ <item>Try</item>
+ <item>Do</item>
+ <item>If</item>
+ <item>Until</item>
+ <item>Else</item>
+ <item>ElseIf</item>
+ <item>While</item>
+ </list>
+
+ <list name="keywords">
+ <item>Begin</item>
+ <item>Filter</item>
+ <item>Sequence</item>
<item>Data</item>
<item>From</item>
<item>Trap</item>
<item>Define</item>
<item>Function</item>
- <item>Try</item>
- <item>Do</item>
- <item>If</item>
- <item>Until</item>
<item>DynamicParam</item>
<item>In</item>
<item>Using</item>
- <item>Else</item>
<item>InlineScript</item>
<item>Var</item>
- <item>ElseIf</item>
<item>Parallel</item>
- <item>While</item>
<item>End</item>
<item>Param</item>
<item>Workflow</item>
@@ -75,69 +81,6 @@
<item>ulong</item>
<item>ushort</item>
</list>
-<!-- TODO: Seems unused?!
- <list name="operators">
- <item>-split</item>
- <item>-isplit</item>
- <item>-csplit</item>
- <item>-join</item>
- <item>-is</item>
- <item>-isnot</item>
- <item>-as</item>
- <item>-eq</item>
- <item>-ieq</item>
- <item>-ceq</item>
- <item>-ne</item>
- <item>-ine</item>
- <item>-cne</item>
- <item>-gt</item>
- <item>-igt</item>
- <item>-cgt</item>
- <item>-ge</item>
- <item>-ige</item>
- <item>-cge</item>
- <item>-lt</item>
- <item>-ilt</item>
- <item>-clt</item>
- <item>-le</item>
- <item>-ile</item>
- <item>-cle</item>
- <item>-like</item>
- <item>-ilike</item>
- <item>-clike</item>
- <item>-notlike</item>
- <item>-inotlike</item>
- <item>-cnotlike</item>
- <item>-match</item>
- <item>-imatch</item>
- <item>-cmatch</item>
- <item>-notmatch</item>
- <item>-inotmatch</item>
- <item>-cnotmatch</item>
- <item>-contains</item>
- <item>-icontains</item>
- <item>-ccontains</item>
- <item>-notcontains</item>
- <item>-inotcontains</item>
- <item>-cnotcontains</item>
- <item>-replace</item>
- <item>-ireplace</item>
- <item>-creplace</item>
- <item>-band</item>
- <item>-bor</item>
- <item>-bxor</item>
- <item>-and</item>
- <item>-or</item>
- <item>-xor</item>
- <item>.</item>
- <item>&amp;</item>
- <item>=</item>
- <item>+=</item>
- <item>-=</item>
- <item>*=</item>
- <item>/=</item>
- <item>%=</item>
- </list>-->
<list name="cmdlets">
<item>Add-Content</item>
<item>Add-ADComputerServiceAccount</item>
@@ -847,11 +790,23 @@
<item>\%</item>
<item>\?</item>
</list>
+
<list name="special-variables">
+ <!-- https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes?view=powershell-7.3#scope-modifiers -->
+ <item>$Global:</item>
+ <item>$Local:</item>
+ <item>$Private:</item>
+ <item>$Script:</item>
+ <item>$Using:</item>
+ <item>$Workflow:</item>
+ <item>$Alias:</item>
+ <item>$Env:</item>
+ <item>$Function:</item>
+ <item>$Variable:</item>
+
<item>$_</item>
<item>$True</item>
<item>$False</item>
- <item>$Env</item>
<item>$Null</item>
<item>$^</item>
<item>$$</item>
@@ -873,55 +828,176 @@
<item>$Host</item>
<item>$OFS</item>
</list>
+
+ <!-- https://learn.microsoft.com/en-us/powershell/scripting/developer/help/comment-based-help-keywords?view=powershell-7.3 -->
+ <list name="comment-based-help">
+ <item>.SYNOPSIS</item>
+ <item>.DESCRIPTION</item>
+ <item>.PARAMETER</item>
+ <item>.EXAMPLE</item>
+ <item>.INPUTS</item>
+ <item>.OUTPUTS</item>
+ <item>.NOTES</item>
+ <item>.LINK</item>
+ <item>.COMPONENT</item>
+ <item>.ROLE</item>
+ <item>.FUNCTIONALITY</item>
+ <item>.FORWARDHELPTARGETNAME</item>
+ <item>.FORWARDHELPCATEGORY</item>
+ <item>.REMOTEHELPRUNSPACE</item>
+ <item>.EXTERNALHELP</item>
+ </list>
+
<contexts>
<context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <DetectSpaces attribute="Normal Text"/>
+ <IncludeRules context="FindSpecialSymbol"/>
+ <DetectChar attribute="Symbol" context="Attribute" char="["/>
+ <IncludeRules context="FindSpecialOtherSymbol"/>
<keyword attribute="Keyword" context="#stay" String="keywords"/>
- <keyword attribute="Data Type" context="#stay" String="types" />
- <IncludeRules context="Cmdlet" />
- <DetectChar attribute="String" context="String" char="&quot;"/>
- <Detect2Chars attribute="HereString" context="HereStringer" char="@" char1="&quot;" beginRegion="StringRegion"/>
- <IncludeRules context="##Doxygen" />
+ <keyword attribute="Control Flow" context="#stay" String="control-flow"/>
+ <keyword attribute="Function" context="#stay" String="cmdlets"/>
+ <IncludeRules context="FindIdentifiant"/>
+ <IncludeRules context="FindEscape"/>
+ </context>
+
+ <context attribute="Attribute" name="Attribute">
+ <DetectSpaces attribute="Normal Text"/>
+ <keyword attribute="Data Type" context="#stay" String="types"/>
+ <IncludeRules context="FindIdentifiant"/>
+ <DetectChar attribute="Symbol" context="#pop" char="]"/>
+ <IncludeRules context="FindSpecialSymbol"/>
+ <IncludeRules context="FindSpecialOtherSymbol"/>
+ <IncludeRules context="FindEscape"/>
+ </context>
+
+ <context attribute="Normal Text" name="FindSpecialSymbol">
+ <DetectChar attribute="String" context="StringDQ" char="&quot;"/>
+ <DetectChar attribute="String" context="StringSQ" char="'"/>
+ <StringDetect attribute="HereString" context="HereStringDQ" String="@&quot;" beginRegion="StringRegion"/>
+ <StringDetect attribute="HereString" context="HereStringSQ" String="@'" beginRegion="StringRegion"/>
+ <StringDetect attribute="Symbol" String="@("/>
<DetectChar attribute="Comment" context="Commentar 1" char="#"/>
- <Detect2Chars attribute="Comment" context="Commentar 2" char="&lt;" char1="#" beginRegion="CommentRegion"/>
+ <StringDetect attribute="Comment" context="Commentar 2" String="&lt;#" beginRegion="CommentRegion"/>
<DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="block1"/>
<DetectChar attribute="Symbol" context="#stay" char="}" endRegion="block1"/>
- <RegExpr attribute="Keyword" context="#stay" String="\b\$global(?=\s+(:))"/>
- <RegExpr attribute="Keyword" context="#stay" String="\b\$script(?=\s+(:))"/>
- <RegExpr attribute="Variable" context="#stay" String="\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*" />
- <keyword attribute="Special Variable" context="#stay" String="special-variables"/>
- <RegExpr attribute="Symbol" context="Member" String="[.]{1,1}" />
- <AnyChar attribute="Symbol" context="#stay" String=":!%&amp;()+,-/.*&lt;=&gt;?[]|~^&#59;"/>
+ <DetectChar attribute="Symbol" context="Member" char="."/>
+ </context>
+
+ <context attribute="Normal Text" name="FindSpecialOtherSymbol">
+ <RegExpr attribute="Operator" String="(?&lt;=^|[\s(){}])-([ic]?(split|eq|ne|gt|ge|lt|le|like|notlike|match|notmatch|contains|notcontains|replace)|b?(and|or|xor)|join|not|isnot|is|as)(?=[\s(){}$]|$)"/>
+ <AnyChar attribute="Symbol" context="#stay" String=":!%&amp;()+,-/*&lt;=&gt;?[]|~^;"/>
+ <IncludeRules context="FindVariable"/>
+ </context>
+
+ <context attribute="Normal Text" name="FindEscape">
+ <RegExpr attribute="String Char" String="`u\{[0-9A-Fa-f]+\}|`."/>
+ <RegExpr attribute="Escape" String="(?&lt;=\s)`"/>
+ </context>
+
+ <context attribute="Normal Text" name="FindVariable">
+ <StringDetect attribute="Variable Substitution" context="VarSubst" String="${" />
+ <StringDetect attribute="Symbol" context="VarCmd" String="$(" />
+ <RegExpr attribute="Variable" context="VariableScopeModifier" String="\$(?=&varscope;:)" insensitive="1"/>
+ <RegExpr attribute="Variable" String="\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*|[?^$])"/>
+ </context>
+
+ <context attribute="Normal Text" name="FindIdentifiant">
+ <RegExpr attribute="Number" context="NumericSuffix" String="\b(0b[01]+|0x[0-9a-fA-F]+|([0-9]+(\.[0-9]*)?|\.[0-9]+)(e([-+][0-9]+|[0-9]*))?)(?=(u?[ysl]|[und])?([kmgtp]b)?\b)" insensitive="1"/>
+ <RegExpr String="[-\w]+"/>
</context>
- <context attribute="String" lineEndContext="#pop" name="String">
- <LineContinue attribute="String" context="#pop"/>
+ <context attribute="Numeric Suffix" name="NumericSuffix" fallthroughContext="#pop">
+ <DetectIdentifier attribute="Numeric Suffix" context="#pop"/>
+ </context>
+
+ <!-- $( -->
+ <context attribute="Normal Text" name="VarCmd">
+ <DetectChar attribute="Symbol" context="#pop" char=")"/>
+ <IncludeRules context="Normal"/>
+ </context>
+
+ <!-- ${ -->
+ <context attribute="Variable" name="VarSubst" fallthroughContext="VarNameSubst">
+ <DetectChar attribute="Variable Substitution" context="#pop" char="}"/>
+ <RegExpr attribute="Scope Modifier" context="VariableName" String="&varscope;:" insensitive="1"/>
+ </context>
+ <context attribute="Variable" name="VarNameSubst">
+ <DetectChar attribute="Variable Substitution" context="#pop#pop" char="}"/>
+ </context>
+
+ <!-- $xxx:varname -->
+ <context attribute="Normal Text" name="VariableScopeModifier" lineEndContext="#pop">
+ <DetectChar attribute="Symbol" context="#pop!VariableName" char=":"/>
+ <DetectIdentifier attribute="Scope Modifier"/>
+ </context>
+ <context attribute="Normal Text" name="VariableName" lineEndContext="#pop">
+ <RegExpr attribute="Variable" context="#pop" String="&varname;(:&varname;)*"/>
+ </context>
+
+ <context attribute="String" name="StringDQ">
+ <IncludeRules context="FindVariable"/>
+ <RegExpr attribute="String Char" String="`u\{[0-9A-Fa-f]+\}|`.|`$|&quot;&quot;"/>
<DetectChar attribute="String" context="#pop" char="&quot;"/>
</context>
- <context attribute="HereString" lineEndContext="#stay" name="HereStringer">
- <Detect2Chars attribute="HereString" context="#pop" char="&quot;" char1="@" endRegion="StringRegion"/>
+ <context attribute="String" name="StringSQ">
+ <StringDetect attribute="String Char" String="''"/>
+ <DetectChar attribute="String" context="#pop" char="'"/>
</context>
- <context attribute="Normal Text" lineEndContext="#pop" name="Member" fallthrough="true" fallthroughContext="#pop">
- <RegExpr attribute="Function" context="#pop" String="\b[_\w][_\w\d]*(?=[\s]*)" />
+
+ <context attribute="HereString" lineEndContext="#stay" name="HereStringDQ">
+ <IncludeRules context="FindVariable"/>
+ <StringDetect attribute="HereString" context="#pop" String="&quot;@" endRegion="StringRegion" column="0"/>
</context>
- <context attribute="Comment" lineEndContext="#pop" name="Commentar 1"/>
+ <context attribute="HereString" lineEndContext="#stay" name="HereStringSQ">
+ <StringDetect attribute="HereString" context="#pop" String="'@" endRegion="StringRegion" column="0"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="Member" fallthroughContext="#pop">
+ <RegExpr attribute="Function" context="#pop" String="[_[:alpha:]]\w*" />
+ </context>
+
+ <context attribute="Comment" lineEndContext="#pop" name="Commentar 1">
+ <DetectSpaces />
+ <IncludeRules context="##Comments"/>
+ </context>
+
<context attribute="Comment" lineEndContext="#stay" name="Commentar 2">
+ <DetectSpaces />
<Detect2Chars attribute="Comment" context="#pop" char="#" char1="&gt;" endRegion="CommentRegion"/>
+ <!-- no delimiter except spaces -->
+ <keyword context="CommentHelp" String="comment-based-help" weakDeliminator="!%&amp;()*+,-./:;$lt;=>?[\]^{|}~" firstNonSpace="1" lookAhead="1" insensitive="0"/>
+ <IncludeRules context="##Comments"/>
</context>
- <context attribute="Cmdlets" lineEndContext="#stay" name="Cmdlet">
- <keyword attribute="Function" context="#stay" String="cmdlets"/>
+ <context attribute="Comment-Based Help Keyword" lineEndContext="#pop" name="CommentHelp">
+ <DetectChar attribute="Symbol" char="."/>
+ <DetectIdentifier attribute="Comment-Based Help Keyword" context="CommentHelpParam"/>
+ </context>
+ <context attribute="Comment-Based Help Paramater" lineEndContext="#pop#pop" name="CommentHelpParam">
</context>
+
</contexts>
+
<itemDatas>
- <itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false"/>
- <itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
- <itemData name="Function" defStyleNum="dsFunction" spellChecking="false"/>
- <itemData name="Data Type" defStyleNum="dsDataType" spellChecking="false"/>
- <itemData name="String" defStyleNum="dsString"/>
- <itemData name="HereString" defStyleNum="dsVerbatimString"/>
- <itemData name="Comment" defStyleNum="dsComment"/>
- <itemData name="Cmdlets" defStyleNum="dsBuiltIn" spellChecking="false"/>
- <itemData name="Symbol" defStyleNum="dsNormal" spellChecking="false"/>
- <itemData name="Variable" defStyleNum="dsVariable" spellChecking="false"/>
- <itemData name="Special Variable" defStyleNum="dsVariable" bold="1" spellChecking="false"/>
+ <itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false"/>
+ <itemData name="Attribute" defStyleNum="dsAttribute" spellChecking="false"/>
+ <itemData name="Escape" defStyleNum="dsChar" spellChecking="false"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="Control Flow" defStyleNum="dsControlFlow" spellChecking="false"/>
+ <itemData name="Function" defStyleNum="dsFunction" spellChecking="false"/>
+ <itemData name="Data Type" defStyleNum="dsDataType" spellChecking="false"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="String Char" defStyleNum="dsChar" spellChecking="false"/>
+ <itemData name="HereString" defStyleNum="dsVerbatimString"/>
+ <itemData name="Number" defStyleNum="dsDecVal" spellChecking="false"/>
+ <itemData name="Numeric Suffix" defStyleNum="dsDataType" spellChecking="false"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Comment-Based Help Keyword" defStyleNum="dsDocumentation" spellChecking="false"/>
+ <itemData name="Comment-Based Help Paramater" defStyleNum="dsSpecialString" spellChecking="false"/>
+ <itemData name="Symbol" defStyleNum="dsOperator" spellChecking="false"/>
+ <itemData name="Operator" defStyleNum="dsOperator" spellChecking="false"/>
+ <itemData name="Variable" defStyleNum="dsVariable" spellChecking="false"/>
+ <itemData name="Scope Modifier" defStyleNum="dsVariable" spellChecking="false" bold="1"/>
+ <itemData name="Variable Substitution" defStyleNum="dsPreprocessor" spellChecking="false"/>
</itemDatas>
</highlighting>
<general>
@@ -932,3 +1008,4 @@
<keywords casesensitive="0" weakDeliminator=":-"/>
</general>
</language>
+<!-- kate: replace-tabs on; tab-width 2; indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/python.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/python.xml
deleted file mode 100644
index 33a313a694..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/python.xml
+++ /dev/null
@@ -1,688 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language>
-<!-- Python syntax highlightning v0.9 by Per Wigren -->
-<!-- Python syntax highlighting v1.9 by Michael Bueker (improved keyword differentiation) -->
-<!-- Python syntax highlighting v1.97 by Paul Giannaros -->
-<!-- Python syntax highlighting v1.99 by Primoz Anzur -->
-<!-- Python syntax highlighting v2.01 by Paul Giannaros:
- * full format character support
- * unicode string modifier supported -->
-<!-- v2.02 remove RegExpr for nums and make indent consistent -->
-<!-- v2.03 highlight decorators, remove operator regex, don't highlight parens as operators -->
-<!-- v2.04 make alerts visible even if they are directly after ''' or # without a space -->
-<!-- v2.06 decorator names can (and often do) contain periods -->
-<!-- v2.07 add support for %prog and co, see bug 142832 -->
-<!-- v2.08 add missing overloaders, new Python 3 statements, builtins, and keywords -->
-<!-- v2.29 recognize escape sequenzes correctly -->
-<language name="Python" version="9" style="python" indenter="python" kateversion="5.0" section="Scripts" extensions="*.py;*.pyw;SConstruct;SConscript" mimetype="application/x-python;text/x-python;text/x-python3" casesensitive="1" author="Michael Bueker" license="">
- <highlighting>
- <list name="import">
- <item>import</item>
- <item>from</item>
- <item>as</item>
- </list>
- <list name="defs">
- <item>class</item>
- <item>def</item>
- <item>del</item>
- <item>global</item>
- <item>lambda</item>
- <item>nonlocal</item>
- </list>
- <list name="operators">
- <item>and</item>
- <item>in</item>
- <item>is</item>
- <item>not</item>
- <item>or</item>
- </list>
- <list name="flow">
- <item>assert</item>
- <item>break</item>
- <item>continue</item>
- <item>elif</item>
- <item>else</item>
- <item>except</item>
- <item>finally</item>
- <item>for</item>
- <item>if</item>
- <item>pass</item>
- <item>raise</item>
- <item>return</item>
- <item>try</item>
- <item>while</item>
- <item>with</item>
- <item>yield</item>
- <item>async</item>
- <item>await</item>
- </list>
- <list name="builtinfuncs">
- <item>__import__</item>
- <item>abs</item>
- <item>all</item>
- <item>any</item>
- <item>apply</item>
- <item>ascii</item>
- <item>basestring</item>
- <item>bin</item>
- <item>bool</item>
- <item>buffer</item>
- <item>bytearray</item>
- <item>bytes</item>
- <item>callable</item>
- <item>chr</item>
- <item>classmethod</item>
- <item>cmp</item>
- <item>coerce</item>
- <item>compile</item>
- <item>complex</item>
- <item>delattr</item>
- <item>dict</item>
- <item>dir</item>
- <item>divmod</item>
- <item>enumerate</item>
- <item>eval</item>
- <item>exec</item>
- <item>execfile</item>
- <item>file</item>
- <item>filter</item>
- <item>float</item>
- <item>format</item>
- <item>frozenset</item>
- <item>getattr</item>
- <item>globals</item>
- <item>hasattr</item>
- <item>hash</item>
- <item>help</item>
- <item>hex</item>
- <item>id</item>
- <item>input</item>
- <item>int</item>
- <item>intern</item>
- <item>isinstance</item>
- <item>issubclass</item>
- <item>iter</item>
- <item>len</item>
- <item>list</item>
- <item>locals</item>
- <item>long</item>
- <item>map</item>
- <item>max</item>
- <item>memoryview</item>
- <item>min</item>
- <item>next</item>
- <item>object</item>
- <item>oct</item>
- <item>open</item>
- <item>ord</item>
- <item>pow</item>
- <item>print</item>
- <item>property</item>
- <item>range</item>
- <item>raw_input</item>
- <item>reduce</item>
- <item>reload</item>
- <item>repr</item>
- <item>reversed</item>
- <item>round</item>
- <item>set</item>
- <item>setattr</item>
- <item>slice</item>
- <item>sorted</item>
- <item>staticmethod</item>
- <item>str</item>
- <item>sum</item>
- <item>super</item>
- <item>tuple</item>
- <item>type</item>
- <item>unichr</item>
- <item>unicode</item>
- <item>vars</item>
- <item>xrange</item>
- <item>zip</item>
- </list>
- <list name="specialvars">
- <item>None</item>
- <item>self</item>
- <item>True</item>
- <item>False</item>
- <item>NotImplemented</item>
- <item>Ellipsis</item>
- <item>__debug__</item>
- <item>__file__</item>
- <item>__name__</item>
- </list>
- <list name="bindings">
- <item>SIGNAL</item>
- <item>SLOT</item>
- <item>connect</item>
- </list>
- <list name="overloaders">
- <item>__new__</item>
- <item>__init__</item>
- <item>__del__</item>
- <item>__repr__</item>
- <item>__str__</item>
- <item>__lt__</item>
- <item>__le__</item>
- <item>__eq__</item>
- <item>__ne__</item>
- <item>__gt__</item>
- <item>__ge__</item>
- <item>__cmp__</item>
- <item>__rcmp__</item>
- <item>__hash__</item>
- <item>__nonzero__</item>
- <item>__unicode__</item>
- <item>__getattr__</item>
- <item>__setattr__</item>
- <item>__delattr__</item>
- <item>__getattribute__</item>
- <item>__get__</item>
- <item>__set__</item>
- <item>__delete__</item>
- <item>__call__</item>
- <item>__len__</item>
- <item>__getitem__</item>
- <item>__setitem__</item>
- <item>__delitem__</item>
- <item>__iter__</item>
- <item>__reversed__</item>
- <item>__contains__</item>
- <item>__getslice__</item>
- <item>__setslice__</item>
- <item>__delslice__</item>
- <item>__add__</item>
- <item>__sub__</item>
- <item>__mul__</item>
- <item>__floordiv__</item>
- <item>__mod__</item>
- <item>__divmod__</item>
- <item>__pow__</item>
- <item>__lshift__</item>
- <item>__rshift__</item>
- <item>__and__</item>
- <item>__xor__</item>
- <item>__or__</item>
- <item>__div__</item>
- <item>__truediv__</item>
- <item>__radd__</item>
- <item>__rsub__</item>
- <item>__rmul__</item>
- <item>__rdiv__</item>
- <item>__rtruediv__</item>
- <item>__rfloordiv__</item>
- <item>__rmod__</item>
- <item>__rdivmod__</item>
- <item>__rpow__</item>
- <item>__rlshift__</item>
- <item>__rrshift__</item>
- <item>__rand__</item>
- <item>__rxor__</item>
- <item>__ror__</item>
- <item>__iadd__</item>
- <item>__isub__</item>
- <item>__imul__</item>
- <item>__idiv__</item>
- <item>__itruediv__</item>
- <item>__ifloordiv__</item>
- <item>__imod__</item>
- <item>__ipow__</item>
- <item>__ilshift__</item>
- <item>__irshift__</item>
- <item>__iand__</item>
- <item>__ixor__</item>
- <item>__ior__</item>
- <item>__neg__</item>
- <item>__pos__</item>
- <item>__abs__</item>
- <item>__invert__</item>
- <item>__complex__</item>
- <item>__int__</item>
- <item>__long__</item>
- <item>__float__</item>
- <item>__oct__</item>
- <item>__hex__</item>
- <item>__index__</item>
- <item>__coerce__</item>
- <item>__enter__</item>
- <item>__exit__</item>
- <item>__bytes__</item>
- <item>__format__</item>
- <item>__next__</item>
- <item>__dir__</item>
- <item>__await__</item>
- <item>__aiter__</item>
- <item>__anext__</item>
- <item>__aenter__</item>
- <item>__aexit__</item>
- </list>
- <list name="exceptions">
- <!--
- Exceptions list resources used:
- - http://docs.python.org/2.7/library/exceptions.html#exception-hierarchy
- - http://docs.python.org/3.4/library/exceptions.html#exception-hierarchy
- -->
- <item>ArithmeticError</item>
- <item>AssertionError</item>
- <item>AttributeError</item>
- <item>BaseException</item>
- <item>BlockingIOError</item>
- <item>BrokenPipeError</item>
- <item>BufferError</item>
- <item>BytesWarning</item>
- <item>ChildProcessError</item>
- <item>ConnectionAbortedError</item>
- <item>ConnectionError</item>
- <item>ConnectionRefusedError</item>
- <item>ConnectionResetError</item>
- <item>DeprecationWarning</item>
- <item>EnvironmentError</item>
- <item>EOFError</item>
- <item>Exception</item>
- <item>FileExistsError</item>
- <item>FileNotFoundError</item>
- <item>FloatingPointError</item>
- <item>FutureWarning</item>
- <item>GeneratorExit</item>
- <item>ImportError</item>
- <item>ImportWarning</item>
- <item>IndentationError</item>
- <item>IndexError</item>
- <item>InterruptedError</item>
- <item>IOError</item>
- <item>IsADirectoryError</item>
- <item>KeyboardInterrupt</item>
- <item>KeyError</item>
- <item>LookupError</item>
- <item>MemoryError</item>
- <item>NameError</item>
- <item>NotADirectoryError</item>
- <item>NotImplementedError</item>
- <item>OSError</item>
- <item>OverflowError</item>
- <item>PendingDeprecationWarning</item>
- <item>PermissionError</item>
- <item>ProcessLookupError</item>
- <item>ReferenceError</item>
- <item>ResourceWarning</item>
- <item>RuntimeError</item>
- <item>RuntimeWarning</item>
- <item>StandardError</item>
- <item>StopIteration</item>
- <item>SyntaxError</item>
- <item>SyntaxWarning</item>
- <item>SystemError</item>
- <item>SystemExit</item>
- <item>TabError</item>
- <item>TimeoutError</item>
- <item>TypeError</item>
- <item>UnboundLocalError</item>
- <item>UnicodeDecodeError</item>
- <item>UnicodeEncodeError</item>
- <item>UnicodeError</item>
- <item>UnicodeTranslateError</item>
- <item>UnicodeWarning</item>
- <item>UserWarning</item>
- <item>ValueError</item>
- <item>Warning</item>
- <item>WindowsError</item>
- <item>ZeroDivisionError</item>
- </list>
- <contexts>
- <context name="Normal" attribute="Normal Text" lineEndContext="#stay">
- <keyword attribute="Import" String="import" context="#stay"/>
- <keyword attribute="Definition Keyword" String="defs" context="#stay"/>
- <keyword attribute="Operator Keyword" String="operators" context="#stay"/>
- <keyword attribute="Flow Control Keyword" String="flow" context="#stay"/>
- <keyword attribute="Builtin Function" String="builtinfuncs" context="#stay"/>
- <keyword attribute="Special Variable" String="specialvars" context="#stay"/>
- <keyword attribute="Extensions" String="bindings" context="#stay"/>
- <keyword attribute="Exceptions" String="exceptions" context="#stay"/>
- <keyword attribute="Overloaders" String="overloaders" context="#stay"/>
- <RegExpr attribute="Normal Text" String="[a-zA-Z_][a-zA-Z_0-9]{2,}" context="#stay"/>
-
- <RegExpr attribute="Complex" String=" ((([0-9]*\.[0-9]+|[0-9]+\.)|([0-9]+|([0-9]*\.[0-9]+|[0-9]+\.))[eE](\+|-)?[0-9]+)|[0-9]+)[jJ]" context="#stay"/>
- <Float attribute="Float" context="#stay" />
- <HlCHex attribute="Hex" context="#stay"/>
- <HlCOct attribute="Octal" context="#stay"/>
- <Int attribute="Int" context="Int Suffixes"/>
-
- <RegExpr attribute="Int" String=" ([0-9]+_)+[0-9]+" context="#stay"/>
- <RegExpr attribute="Float" String=" ([0-9]+_)+[0-9]+\.[0-9]+" context="#stay"/>
- <RegExpr attribute="Hex" String=" [0-9]x([A-F0-9]+_)+[A-F0-9]+" context="#stay"/>
-
- <DetectChar attribute="Normal Text" char="{" context="Dictionary" beginRegion="Dictionary"/>
- <DetectChar attribute="Normal Text" char="[" context="List" beginRegion="List"/>
- <DetectChar attribute="Normal Text" char="(" context="Tuple" beginRegion="Tuple"/>
-
- <IncludeRules context="CommentVariants" />
-
- <DetectChar attribute="Comment" char="#" context="Hash comment"/>
-
- <IncludeRules context="StringVariants" />
-
- <RegExpr attribute="Decorator" String="@[_a-zA-Z][\._a-zA-Z0-9]*" firstNonSpace="true"/>
- <AnyChar attribute="Operator" String="+*/%\|=;\!&lt;&gt;!^&amp;~-@" context="#stay"/>
- </context>
-
- <context name="Int Suffixes" attribute="Int" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
- <StringDetect attribute="Int" context="#pop" String="L" insensitive="true"/>
- </context>
-
- <context name="#CheckForString" attribute="Normal Text" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
- <DetectSpaces/>
- <LineContinue attribute="Normal Text" context="CheckForStringNext"/>
- </context>
-
- <context name="CheckForStringNext" attribute="Normal Text" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
- <DetectSpaces/>
- <LineContinue attribute="Normal Text" context="CheckForStringNext"/>
- <IncludeRules context="StringVariants"/>
- </context>
-
- <context name="StringVariants" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces/>
-
- <RegExpr attribute="String" String="u?'''" insensitive="true" context="Triple A-string" beginRegion="Triple A-region"/>
- <RegExpr attribute="String" String="u?&quot;&quot;&quot;" insensitive="true" context="Triple Q-string" beginRegion="Triple Q-region"/>
- <RegExpr attribute="String" String="u?'" insensitive="true" context="Single A-string"/>
- <RegExpr attribute="String" String="u?&quot;" insensitive="true" context="Single Q-string"/>
-
- <RegExpr attribute="Raw String" String="(u?r|ru)'''" insensitive="true" context="Raw Triple A-string" beginRegion="Triple A-region"/>
- <RegExpr attribute="Raw String" String="(u?r|ru)&quot;&quot;&quot;" insensitive="true" context="Raw Triple Q-string" beginRegion="Triple Q-region"/>
- <RegExpr attribute="Raw String" String="(u?r|ru)'" insensitive="true" context="Raw A-string"/>
- <RegExpr attribute="Raw String" String="(u?r|ru)&quot;" insensitive="true" context="Raw Q-string"/>
-
- <StringDetect attribute="F-String" String="f'''" insensitive="true" context="Triple A-F-String" beginRegion="Triple A-region"/>
- <StringDetect attribute="F-String" String="f&quot;&quot;&quot;" insensitive="true" context="Triple Q-F-String" beginRegion="Triple Q-region"/>
- <StringDetect attribute="F-String" String="f'" insensitive="true" context="Single A-F-String"/>
- <StringDetect attribute="F-String" String="f&quot;" insensitive="true" context="Single Q-F-String"/>
-
- <RegExpr attribute="Raw F-String" String="(fr|rf)'''" insensitive="true" context="Raw Triple A-F-String" beginRegion="Triple A-region"/>
- <RegExpr attribute="Raw F-String" String="(fr|rf)&quot;&quot;&quot;" insensitive="true" context="Raw Triple Q-F-String" beginRegion="Triple Q-region"/>
- <RegExpr attribute="Raw F-String" String="(fr|rf)'" insensitive="true" context="Raw A-F-String"/>
- <RegExpr attribute="Raw F-String" String="(fr|rf)&quot;" insensitive="true" context="Raw Q-F-String"/>
- </context>
-
- <context name="CommentVariants" attribute="Normal Text" lineEndContext="#stay">
- <DetectSpaces/>
-
- <RegExpr attribute="Comment" String="u?'''" insensitive="true" firstNonSpace="true" context="Triple A-comment" beginRegion="Triple A-region"/>
- <RegExpr attribute="Comment" String="u?&quot;&quot;&quot;" insensitive="true" firstNonSpace="true" context="Triple Q-comment" beginRegion="Triple Q-region"/>
- <RegExpr attribute="Comment" String="u?'" insensitive="true" firstNonSpace="true" context="Single A-comment"/>
- <RegExpr attribute="Comment" String="u?&quot;" insensitive="true" firstNonSpace="true" context="Single Q-comment"/>
-
- <RegExpr attribute="Comment" String="(u?r|ru)'''" insensitive="true" firstNonSpace="true" context="Triple A-comment" beginRegion="Triple A-region"/>
- <RegExpr attribute="Comment" String="(u?r|ru)&quot;&quot;&quot;" insensitive="true" firstNonSpace="true" context="Triple Q-comment" beginRegion="Triple Q-region"/>
- <RegExpr attribute="Comment" String="(u?r|ru)'" insensitive="true" firstNonSpace="true" context="Single A-comment"/>
- <RegExpr attribute="Comment" String="(u?r|ru)&quot;" insensitive="true" firstNonSpace="true" context="Single Q-comment"/>
- </context>
-
- <context name="Dictionary" attribute="Normal Text" lineEndContext="#stay" noIndentationBasedFolding="true">
- <DetectSpaces/>
- <DetectChar attribute="Normal Text" char="}" context="#pop" endRegion="Dictionary"/>
- <IncludeRules context="StringVariants" />
- <IncludeRules context="Normal" />
- </context>
-
- <context name="List" attribute="Normal Text" lineEndContext="#stay" noIndentationBasedFolding="true">
- <DetectSpaces/>
- <DetectChar attribute="Normal Text" char="]" context="#pop" endRegion="List"/>
- <IncludeRules context="StringVariants" />
- <IncludeRules context="Normal" />
- </context>
-
- <context name="Tuple" attribute="Normal Text" lineEndContext="#stay" noIndentationBasedFolding="true">
- <DetectSpaces/>
- <DetectChar attribute="Normal Text" char=")" context="#pop" endRegion="Tuple"/>
- <IncludeRules context="StringVariants" />
- <IncludeRules context="Normal" />
- </context>
-
- <!-- Comments -->
-
- <context name="Hash comment" attribute="Comment" lineEndContext="#pop">
- <IncludeRules context="##Alerts" />
- <IncludeRules context="##Modelines" />
- </context>
-
- <context name="Triple A-comment" attribute="Comment" lineEndContext="#stay" noIndentationBasedFolding="true">
- <IncludeRules context="stringescape"/>
- <StringDetect attribute="Comment" String="'''" context="#pop" endRegion="Triple A-region"/>
- <IncludeRules context="##Alerts" />
- </context>
-
- <context name="Triple Q-comment" attribute="Comment" lineEndContext="#stay" noIndentationBasedFolding="true">
- <IncludeRules context="stringescape"/>
- <StringDetect attribute="Comment" String="&quot;&quot;&quot;" context="#pop" endRegion="Triple Q-region"/>
- <IncludeRules context="##Alerts" />
- </context>
-
- <context name="Single A-comment" attribute="Comment" lineEndContext="#stay">
- <IncludeRules context="stringescape"/>
- <DetectChar attribute="Comment" char="'" context="#pop"/>
- <IncludeRules context="##Alerts" />
- </context>
-
- <context name="Single Q-comment" attribute="Comment" lineEndContext="#stay">
- <IncludeRules context="stringescape"/>
- <DetectChar attribute="Comment" char="&quot;" context="#pop"/>
- <IncludeRules context="##Alerts" />
- </context>
-
- <!-- Strings -->
-
- <!-- format characters -->
- <context name="stringformat" attribute="String Substitution" lineEndContext="#stay">
- <!-- Python 2 style. Syntax:
- 1. start character '%'
- 2. [optional] Mapping key, e.g. '(foo)'
- 3. [optional] Conversion flags, one of '#0- +'
- 4. [optional] Minimum width, integer or '*'
- 5. [optional] Precision, '.' followed by integer or '*'
- 6. [optional] Length modifier, one of 'hlL'
- 7. conversion type, one of 'crsdiouxXeEfFgG%'
- [Special cases: %prog and %default - see http://docs.python.org/library/optparse.html]
- -->
- <RegExpr attribute="String Substitution" String="%((\([a-zA-Z0-9_]+\))?[#0\- +]?([1-9][0-9]*|\*)?(\.([1-9][0-9]*|\*))?[hlL]?[crsdiouxXeEfFgG%]|prog|default)" context="#stay"/>
- <!-- http://docs.python.org/2/library/string.html#format-string-syntax:
- replacement_field ::= "{" [field_name] ["!" conversion] [":" format_spec] "}"
- field_name ::= arg_name ("." attribute_name | "[" element_index "]")*
- arg_name ::= [identifier | integer]
- attribute_name ::= identifier
- element_index ::= integer | index_string
- index_string ::= <any source character except "]"> +
- conversion ::= "r" | "s"
- format_spec ::= [[fill]align][sign][#][0][width][,][.precision][type]
- fill ::= <any character>
- align ::= "<" | ">" | "=" | "^"
- sign ::= "+" | "-" | " "
- width ::= integer
- precision ::= integer
- type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
- -->
- <RegExpr attribute="String Substitution" String="\{(([a-zA-Z0-9_]+|[0-9]+)(\.[a-zA-Z0-9_]+|\[[^ \]]+\])*)?(![rs])?(:([^}]?[&lt;&gt;=^])?[ +-]?#?0?[0-9]*(\.[0-9]+)?[bcdeEfFgGnosxX%]?)?\}" context="#stay"/>
- <Detect2Chars attribute="String Substitution" char="{" char1="{" context="#stay" />
- <Detect2Chars attribute="String Substitution" char="}" char1="}" context="#stay" />
- </context>
-
- <!-- escape characters -->
- <context name="stringescape" attribute="String Char" lineEndContext="#stay">
- <!-- As this highlighting style is for both, Python 2 and 3,
- we do not know if a normal string is “unicode” or not. So we
- -->
- <RegExpr attribute="String Char" String="\\[\\'&quot;abfnrtv]" context="#stay"/>
- <RegExpr attribute="String Char" String="\\[0-7]{1,3}" context="#stay"/>
- <RegExpr attribute="String Char" String="\\x[0-9A-Fa-f]{2}" context="#stay"/>
- <RegExpr attribute="String Char" String="\\u[0-9A-Fa-f]{4}" context="#stay"/>
- <RegExpr attribute="String Char" String="\\U[0-9A-Fa-f]{8}" context="#stay"/>
- <RegExpr attribute="String Char" String="\\N\{[a-zA-Z0-9\- ]+\}" context="#stay"/>
- </context>
-
- <!-- f-literals -->
- <context name="stringinterpolation" attribute="F-String" lineEndContext="#stay">
- <Detect2Chars attribute="String Char" char="{" char1="{" context="#stay"/>
- <DetectChar attribute="String Substitution" char="{" context="String Interpolation"/>
- </context>
- <context name="String Interpolation" attribute="String Substitution" lineEndContext="#stay">
- <DetectChar attribute="Error" char="\" context="#pop"/>
- <RegExpr attribute="String Substitution" String="(![rs])?(:([^}]?[&lt;&gt;=^])?[ +-]?#?0?[0-9]*(\.[0-9]+)?[bcdeEfFgGnosxX%]?)?\}" context="#pop"/>
- <IncludeRules context="Normal"/> <!-- TODO: create expression context instead -->
- </context>
-
-
- <!--
- It follows a Binary tree of string kinds (not even touching byte literals).
- The levels are:
- 1. triple- vs. single-quoted
- 2. apostrophe vs. quotation mark
- 3. static vs. interpolated (f-literal)
- 4. escaped vs. raw
- Adding byte literals wouldn’t make the current 2⁴ into 2⁵ contexts, as there are no byte f-literals
- -->
-
-
- <!-- Triple-quoted A-strings -->
- <context name="Triple A-string" attribute="String" lineEndContext="#stay" noIndentationBasedFolding="true">
- <IncludeRules context="stringescape"/>
- <IncludeRules context="stringformat"/>
- <StringDetect attribute="String" String="'''" context="#pop#CheckForString" endRegion="Triple A-region"/>
- </context>
-
- <context name="Raw Triple A-string" attribute="Raw String" lineEndContext="#stay" noIndentationBasedFolding="true">
- <HlCStringChar attribute="Raw String" context="#stay"/>
- <IncludeRules context="stringformat"/>
- <StringDetect attribute="Raw String" String="'''" context="#pop#CheckForString" endRegion="Triple A-region"/>
- </context>
-
- <context name="Triple A-F-String" attribute="F-String" lineEndContext="#stay" noIndentationBasedFolding="true">
- <IncludeRules context="stringescape"/>
- <IncludeRules context="stringinterpolation"/>
- <StringDetect attribute="F-String" String="'''" context="#pop#CheckForString" endRegion="Triple A-region"/>
- </context>
-
- <context name="Raw Triple A-F-String" attribute="Raw F-String" lineEndContext="#stay" noIndentationBasedFolding="true">
- <HlCStringChar attribute="Raw F-String" context="#stay"/>
- <IncludeRules context="stringinterpolation"/>
- <StringDetect attribute="Raw F-String" String="'''" context="#pop#CheckForString" endRegion="Triple A-region"/>
- </context>
-
- <!-- Triple-quoted Q-strings -->
- <context name="Triple Q-string" attribute="String" lineEndContext="#stay" noIndentationBasedFolding="true">
- <IncludeRules context="stringescape"/>
- <IncludeRules context="stringformat"/>
- <StringDetect attribute="String" String="&quot;&quot;&quot;" context="#pop#CheckForString" endRegion="Triple Q-region"/>
- </context>
-
- <context name="Raw Triple Q-string" attribute="Raw String" lineEndContext="#stay" noIndentationBasedFolding="true">
- <HlCStringChar attribute="Raw String" context="#stay"/>
- <IncludeRules context="stringformat"/>
- <StringDetect attribute="Raw String" String="&quot;&quot;&quot;" context="#pop#CheckForString" endRegion="Triple Q-region"/>
- </context>
-
- <context name="Triple Q-F-String" attribute="F-String" lineEndContext="#stay" noIndentationBasedFolding="true">
- <IncludeRules context="stringescape"/>
- <IncludeRules context="stringinterpolation"/>
- <StringDetect attribute="F-String" String="&quot;&quot;&quot;" context="#pop#CheckForString" endRegion="Triple Q-region"/>
- </context>
-
- <context name="Raw Triple Q-F-String" attribute="Raw F-String" lineEndContext="#stay" noIndentationBasedFolding="true">
- <HlCStringChar attribute="Raw F-String" context="#stay"/>
- <IncludeRules context="stringinterpolation"/>
- <StringDetect attribute="Raw F-String" String="&quot;&quot;&quot;" context="#pop#CheckForString" endRegion="Triple Q-region"/>
- </context>
-
-
- <!-- Single-quoted A-strings -->
- <context name="Single A-string" attribute="String" lineEndContext="#stay">
- <IncludeRules context="stringescape"/>
- <IncludeRules context="stringformat"/>
- <DetectChar attribute="String" char="'" context="#pop#CheckForString"/>
- </context>
-
- <context name="Raw A-string" attribute="Raw String" lineEndContext="#stay">
- <HlCStringChar attribute="Raw String" context="#stay"/>
- <IncludeRules context="stringformat"/>
- <DetectChar attribute="Raw String" char="'" context="#pop#CheckForString"/>
- </context>
-
- <context name="Single A-F-String" attribute="F-String" lineEndContext="#stay">
- <IncludeRules context="stringescape"/>
- <IncludeRules context="stringinterpolation"/>
- <DetectChar attribute="F-String" char="'" context="#pop#CheckForString"/>
- </context>
-
- <context name="Raw A-F-String" attribute="Raw F-String" lineEndContext="#stay">
- <HlCStringChar attribute="Raw F-String" context="#stay"/>
- <IncludeRules context="stringinterpolation"/>
- <DetectChar attribute="Raw F-String" char="'" context="#pop#CheckForString"/>
- </context>
-
- <!-- Single-quoted Q-strings -->
- <context name="Single Q-string" attribute="String" lineEndContext="#stay">
- <IncludeRules context="stringescape"/>
- <IncludeRules context="stringformat"/>
- <DetectChar attribute="String" char="&quot;" context="#pop#CheckForString"/>
- </context>
-
- <context name="Raw Q-string" attribute="Raw String" lineEndContext="#stay">
- <HlCStringChar attribute="Raw String" context="#stay"/>
- <IncludeRules context="stringformat"/>
- <DetectChar attribute="Raw String" char="&quot;" context="#pop#CheckForString"/>
- </context>
-
- <context name="Single Q-F-String" attribute="F-String" lineEndContext="#stay">
- <IncludeRules context="stringescape"/>
- <IncludeRules context="stringinterpolation"/>
- <DetectChar attribute="F-String" char="&quot;" context="#pop#CheckForString"/>
- </context>
-
- <context name="Raw Q-F-String" attribute="Raw F-String" lineEndContext="#stay">
- <HlCStringChar attribute="Raw F-String" context="#stay"/>
- <IncludeRules context="stringinterpolation"/>
- <DetectChar attribute="Raw F-String" char="&quot;" context="#pop#CheckForString"/>
- </context>
- </contexts>
-
- <itemDatas>
- <itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false"/>
- <itemData name="Definition Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
- <itemData name="Operator" defStyleNum="dsOperator" spellChecking="false"/>
- <itemData name="Operator Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
- <itemData name="Flow Control Keyword" defStyleNum="dsControlFlow" spellChecking="false"/>
- <itemData name="Builtin Function" defStyleNum="dsBuiltIn" spellChecking="false"/>
- <itemData name="Special Variable" defStyleNum="dsVariable" spellChecking="false"/>
- <itemData name="Extensions" defStyleNum="dsExtension" spellChecking="false"/>
- <itemData name="Exceptions" defStyleNum="dsPreprocessor" spellChecking="false"/>
- <itemData name="Overloaders" defStyleNum="dsFunction" spellChecking="false"/>
- <itemData name="Import" defStyleNum="dsImport" spellChecking="false"/>
- <itemData name="Float" defStyleNum="dsFloat" spellChecking="false"/>
- <itemData name="Int" defStyleNum="dsDecVal" spellChecking="false"/>
- <itemData name="Hex" defStyleNum="dsBaseN" spellChecking="false"/>
- <itemData name="Octal" defStyleNum="dsBaseN" spellChecking="false"/>
- <itemData name="Complex" defStyleNum="dsOthers" spellChecking="false"/>
- <itemData name="Comment" defStyleNum="dsComment"/>
- <itemData name="String" defStyleNum="dsString"/>
- <itemData name="Raw String" defStyleNum="dsVerbatimString"/>
- <itemData name="F-String" defStyleNum="dsSpecialString"/>
- <itemData name="Raw F-String" defStyleNum="dsVerbatimString"/>
- <itemData name="String Char" defStyleNum="dsChar" spellChecking="false"/>
- <itemData name="String Substitution" defStyleNum="dsSpecialChar" spellChecking="false"/>
- <itemData name="Decorator" defStyleNum="dsAttribute" spellChecking="false"/>
- <itemData name="Error" defStyleNum="dsError"/>
- </itemDatas>
- </highlighting>
- <general>
- <folding indentationsensitive="1" />
- <emptyLines>
- <emptyLine regexpr="(?:\s+|\s*#.*)"/>
- </emptyLines>
- <comments>
- <comment name="singleLine" start="#" position="afterwhitespace"/>
- </comments>
- <keywords casesensitive="1" additionalDeliminator="#'"/>
- </general>
-</language>
-
-<!-- kate: space-indent off; indent-width 4; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/qdocconf.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/qdocconf.xml
index 0efd6edc3f..dfd8008d98 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/qdocconf.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/qdocconf.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd">
+<!DOCTYPE language>
<language name="QDoc Configuration"
- version="1"
+ version="3"
kateversion="5.0"
section="Configuration"
extensions="*.qdocconf"
@@ -84,7 +84,7 @@
<contexts>
<context name="key-context" attribute="Normal Text" lineEndContext="#stay">
- <DetectChar char="#" context="comment-context" column="0"/>
+ <DetectChar char="#" attribute="Comment" context="comment-context" column="0"/>
<keyword attribute="Keyword" String="key-names"/>
<keyword attribute="Function" String="function-names"/>
<DetectChar char="=" context="value-context"/>
@@ -112,7 +112,8 @@
</context>
<context name="comment-context" attribute="Comment" lineEndContext="#pop">
- <IncludeRules context="##Alerts"/>
+ <DetectSpaces />
+ <IncludeRules context="##Comments"/>
</context>
</contexts>
@@ -134,3 +135,4 @@
<keywords casesensitive="1" weakDeliminator="-/"/>
</general>
</language>
+<!-- kate: replace-tabs on; tab-width 4; indent-width 4; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/ruby.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/ruby.xml
index a451e1442f..7c1163910f 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/ruby.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/ruby.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd">
+<!DOCTYPE language>
<!--
Ruby syntax highlighting definition for Kate.
@@ -31,7 +31,7 @@
<!-- Hold the "language" opening tag on a single line, as mentioned in "language.dtd". -->
<language name="Ruby" section="Scripts"
- version="10" kateversion="3.3"
+ version="18" kateversion="5.0"
extensions="*.rb;*.rjs;*.rxml;*.xml.erb;*.js.erb;*.rake;Rakefile;Gemfile;*.gemspec;Vagrantfile"
mimetype="application/x-ruby"
style="ruby" indenter="ruby"
@@ -183,6 +183,8 @@
<item>extend</item>
<item>include</item>
<item>prepend</item>
+ <item>refine</item>
+ <item>using</item>
</list>
<contexts>
@@ -191,19 +193,17 @@
<LineContinue attribute="Normal Text" context="Line Continue"/>
<!-- __END__ token on own line. -->
- <RegExpr attribute="Keyword" String="__END__$" context="DATA" column="0"/>
+ <RegExpr attribute="Keyword" String="^__END__$" context="DATA" column="0"/>
<!-- "shebang" line -->
- <RegExpr attribute="Keyword" String="#!\/.*" context="#stay" column="0"/>
+ <RegExpr attribute="Keyword" String="^#!\/.*" context="#stay" column="0"/>
<!-- "def" - "end" blocks -->
<!-- check for statement modifiers with regexes -->
<DetectChar attribute="Operator" char="{" context="Find closing block brace" beginRegion="def block"/>
- <RegExpr attribute="Keyword" String="(\=|\(|\[|\{)\s*(if|unless|while|until)\b" context="#stay" beginRegion="def block"/>
- <RegExpr attribute="Keyword" String="(while|until)\b(?!.*\bdo\b)" context="#stay" beginRegion="def block" />
- <RegExpr attribute="Keyword" String="\;\s*(while|until)\b(?!.*\bdo\b)" context="#stay" beginRegion="def block"/>
+ <DetectChar attribute="Delimiter" char="}" context="check_div_1" endRegion="def block"/>
+ <RegExpr attribute="Keyword" String="[=([]\s*(if|unless|while|until)\b|(while|until)\b(?!.*\bdo\b)|\;\s*(while|until)\b(?!.*\bdo\b)|\;\s*(if|unless)\b" context="#stay" beginRegion="def block"/>
<RegExpr attribute="Keyword" String="(if|unless)\b" context="#stay" beginRegion="def block" firstNonSpace="true"/>
- <RegExpr attribute="Keyword" String="\;\s*(if|unless)\b" context="#stay" beginRegion="def block"/>
<WordDetect attribute="Keyword" String="class" context="no_heredoc" beginRegion="def block"/>
<WordDetect attribute="Keyword" String="module" context="#stay" beginRegion="def block"/>
<WordDetect attribute="Keyword" String="begin" context="#stay" beginRegion="def block"/>
@@ -236,8 +236,7 @@
<!-- (global) vars starting with $
Match them before $_.
-->
- <RegExpr attribute="Global Variable" String="\$[a-zA-Z_0-9]+" context="check_div_1"/>
- <RegExpr attribute="Global Variable" String="\$\-[a-zA-Z_]\b" context="check_div_1"/>
+ <RegExpr attribute="Global Variable" String="\$[a-zA-Z_0-9]+|\$\-[a-zA-Z_]\b" context="check_div_1"/>
<!-- special-character globals -->
<RegExpr attribute="Default globals" String="\$[\d_*`+@;,.~=\!\$:?'/\\\-\&amp;&quot;&gt;&lt;]" context="check_div_1"/>
<RegExpr attribute="Global Constant" String="\b[_A-Z]+[A-Z_0-9]+\b" context="check_div_2"/>
@@ -269,21 +268,16 @@
<DetectChar attribute="Operator" char="." context="#stay"/>
<Detect2Chars attribute="Operator" char="&amp;" char1="&amp;" context="#stay"/>
<Detect2Chars attribute="Operator" char="|" char1="|" context="#stay"/>
- <RegExpr attribute="Operator" String="\s[\?\:\%]\s" context="#stay"/>
- <RegExpr attribute="Operator" String="[|&amp;&lt;&gt;\^\+*~\-=]+" context="#stay"/>
- <!-- regexp hack -->
- <RegExpr attribute="Operator" String="\s!" context="#stay"/>
- <RegExpr attribute="Operator" String="/=\s" context="#stay" insensitive="0"/>
+ <!-- \s! is regexp hack -->
+ <RegExpr attribute="Operator" String="\s[\?\:\%]\s|[|&amp;&lt;&gt;\^\+*~\-=]+|\s!|/=\s" context="#stay"/>
<Detect2Chars attribute="Operator" char="%" char1="=" context="#stay"/>
<Detect2Chars attribute="Operator" char=":" char1=":" context="Member Access"/>
- <RegExpr attribute="Symbol" String=":(@{1,2}|\$)?[a-zA-Z_][a-zA-Z0-9_]*[=?!]?" context="check_div_1"/>
- <RegExpr attribute="Symbol" String=":\[\]=?" context="check_div_1"/>
+ <RegExpr attribute="Symbol" String=":(@{1,2}|\$)?[a-zA-Z_][a-zA-Z0-9_]*[=?!]?|:\[\]=?" context="check_div_1"/>
<!-- Do not send to "check_div_1" context!:
after detecting these rules (": ") there can be a regular expression (see bug: #361875) -->
- <RegExpr attribute="Symbol" String="(@{1,2}|\$)?[a-zA-Z_][a-zA-Z0-9_]*[=?!]?: " context="#stay"/>
- <RegExpr attribute="Symbol" String="\[\]=?: " context="#stay"/>
+ <RegExpr attribute="Symbol" String="(@{1,2}|\$)?[a-zA-Z_][a-zA-Z0-9_]*[=?!]?: |\[\]=?: " context="#stay"/>
<DetectChar attribute="String" char="&quot;" context="Quoted String"/>
<DetectChar attribute="Raw String" char="'" context="Apostrophed String"/>
@@ -291,14 +285,12 @@
<Detect2Chars attribute="Normal Text" char="?" char1="#" context="#stay"/>
- <RegExpr attribute="Comment" String="#\s*BEGIN.*$" context="#stay" beginRegion="marker" column="0"/>
- <RegExpr attribute="Comment" String="#\s*END.*$" context="#stay" endRegion="marker" column="0"/>
+ <RegExpr attribute="Comment" String="^#\s*BEGIN.*$" context="#stay" beginRegion="marker" column="0"/>
+ <RegExpr attribute="Comment" String="^#\s*END.*$" context="#stay" endRegion="marker" column="0"/>
<DetectChar attribute="Comment" char="#" context="General Comment"/>
<DetectChar attribute="Delimiter" char="[" context="#stay"/>
<DetectChar attribute="Delimiter" char="]" context="check_div_1"/>
- <DetectChar attribute="Delimiter" char="{" context="#stay" beginRegion="def block"/>
- <DetectChar attribute="Delimiter" char="}" context="check_div_1" endRegion="def block"/>
<RegExpr attribute="Instance Variable" String="@[a-zA-Z_0-9]+" context="check_div_1"/>
<RegExpr attribute="Class Variable" String="@@[a-zA-Z_0-9]+" context="check_div_1"/>
@@ -320,20 +312,20 @@
<!-- A slash is always a division operator, even if preceeded by whitespace -->
<context name="check_div_1" attribute="Normal Text" fallthrough="true" fallthroughContext="#pop" lineEndContext="#pop">
- <RegExpr attribute="Normal Text" String="\s*" context="#stay"/>
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
<AnyChar attribute="Operator" String="/%" context="#pop"/>
</context>
<!-- Same as check_div_1, but with double pop to exit the surrounding context -->
<context name="check_div_1_pop" attribute="Normal Text" fallthrough="true" fallthroughContext="#pop#pop" lineEndContext="#pop#pop">
- <RegExpr attribute="Normal Text" String="\s*" context="#stay"/>
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
<AnyChar attribute="Operator" String="/%" context="#pop#pop"/>
</context>
<!-- A slash is division operator if it's the first character, or if preceeded and followed by whitespace -->
<context name="check_div_2" attribute="Normal Text" fallthrough="true" fallthroughContext="#pop" lineEndContext="#pop">
<AnyChar attribute="Operator" String="/%" context="#pop"/>
- <RegExpr attribute="Normal Text" String="\s+" context="check_div_2_internal"/>
+ <DetectSpaces attribute="Normal Text" context="check_div_2_internal"/>
</context>
<!-- Internal context used by check_div_2 -->
@@ -345,7 +337,7 @@
<!-- Same as check_div_2, but with double pop to exit the surrounding context -->
<context name="check_div_2_pop" attribute="Normal Text" fallthrough="true" fallthroughContext="#pop#pop" lineEndContext="#pop#pop">
<AnyChar attribute="Operator" String="/%" context="#pop#pop"/>
- <RegExpr attribute="Normal Text" String="\s+" context="check_div_2_pop_internal"/>
+ <DetectSpaces attribute="Normal Text" context="check_div_2_pop_internal"/>
</context>
<!-- Internal context used by check_div_2_pop -->
@@ -355,8 +347,7 @@
</context>
<context name="Line Continue" attribute="Normal Text" lineEndContext="#pop">
- <RegExpr attribute="Keyword" String="(while|until)\b(?!.*\bdo\b)" context="#stay" firstNonSpace="true"/>
- <RegExpr attribute="Keyword" String="(if|unless)\b" context="#stay" firstNonSpace="true"/>
+ <RegExpr attribute="Keyword" String="(while|until)\b(?!.*\bdo\b)|(if|unless)\b" context="#stay" firstNonSpace="true"/>
<IncludeRules context="Normal"/>
</context>
@@ -369,6 +360,7 @@
<Detect2Chars attribute="String" char="\" char1="\" context="#stay"/>
<Detect2Chars attribute="String" char="\" char1="&quot;" context="#stay"/>
<RegExpr attribute="Substitution" String="#@{1,2}" context="Short Subst"/>
+ <Detect2Chars attribute="Substitution" char="#" char1="$" context="Short Subst"/>
<Detect2Chars attribute="Substitution" char="#" char1="{" context="Subst"/>
<DetectChar char="&quot;" attribute="String" context="check_div_1_pop"/>
</context>
@@ -383,18 +375,21 @@
<Detect2Chars attribute="String" char="\" char1="\" context="#stay"/>
<Detect2Chars attribute="String" char="\" char1="`" context="#stay"/>
<RegExpr attribute="Substitution" String="#@{1,2}" context="Short Subst"/>
+ <Detect2Chars attribute="Substitution" char="#" char1="$" context="Short Subst"/>
<Detect2Chars attribute="Substitution" char="#" char1="{" context="Subst"/>
<DetectChar char="`" attribute="Command" context="check_div_1_pop"/>
</context>
<context name="Embedded documentation" attribute="Blockcomment" lineEndContext="#stay">
<RegExpr attribute="Comment" String="^=end(?:\s.*|$)" context="#pop" endRegion="comment block" column="0"/>
- <IncludeRules context="##Alerts" />
+ <DetectSpaces />
+ <IncludeRules context="##Comments" />
</context>
<context name="RegEx 1" attribute="Regular Expression" lineEndContext="#stay">
<Detect2Chars attribute="Regular Expression" char="\" char1="/" context="#stay"/>
<RegExpr attribute="Substitution" String="#@{1,2}" context="Short Subst"/>
+ <Detect2Chars attribute="Substitution" char="#" char1="$" context="Short Subst"/>
<Detect2Chars attribute="Substitution" char="#" char1="{" context="Subst"/>
<RegExpr attribute="Regular Expression" String="/[uiomxn]*" context="check_div_1_pop"/>
</context>
@@ -409,6 +404,7 @@
<context name="Short Subst" attribute="Substitution" lineEndContext="#pop">
<!-- Check for e.g.: "#@var#@@xy" -->
<RegExpr attribute="Substitution" String="#@{1,2}" context="#stay"/>
+ <Detect2Chars attribute="Substitution" char="#" char1="$" context="#stay"/>
<RegExpr attribute="Substitution" String="\w(?!\w)" context="#pop"/>
</context>
@@ -431,7 +427,8 @@
</context>
<context name="General Comment" attribute="Comment" lineEndContext="#pop">
- <IncludeRules context="##Alerts" />
+ <DetectSpaces />
+ <IncludeRules context="##Comments" />
</context>
<!-- HEREDOC support
@@ -440,15 +437,11 @@
<!-- here we markup the heredoc markers -->
<context name="find_heredoc" attribute="Normal Text" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
<RegExpr attribute="Keyword" context="apostrophed_normal_heredoc" String="'(\w+)'" />
- <RegExpr attribute="Keyword" context="normal_heredoc" String="(\w+)" />
- <RegExpr attribute="Keyword" context="normal_heredoc" String="&quot;(\w+)&quot;" />
- <RegExpr attribute="Keyword" context="normal_heredoc" String="`(\w+)`" />
+ <RegExpr attribute="Keyword" context="normal_heredoc" String="(?|(\w+)|&quot;(\w+)&quot;|`(\w+)`)" />
</context>
<context name="find_indented_heredoc" attribute="Normal Text" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
<RegExpr attribute="Keyword" context="apostrophed_indented_heredoc" String="'(\w+)'" />
- <RegExpr attribute="Keyword" context="indented_heredoc" String="(\w+)" />
- <RegExpr attribute="Keyword" context="indented_heredoc" String="&quot;(\w+)&quot;" />
- <RegExpr attribute="Keyword" context="indented_heredoc" String="`(\w+)`" />
+ <RegExpr attribute="Keyword" context="indented_heredoc" String="(?|(\w+)|&quot;(\w+)&quot;|`(\w+)`)" />
</context>
<!-- these are the real heredoc contexts -->
<context name="indented_heredoc" attribute="Here Document" lineEndContext="#stay" dynamic="true">
@@ -460,16 +453,17 @@
</context>
<context name="normal_heredoc" attribute="Here Document" lineEndContext="#stay" dynamic="true">
- <RegExpr attribute="Keyword" context="#pop#pop" String="%1$" dynamic="true" endRegion="HereDocument" column="0"/>
+ <RegExpr attribute="Keyword" context="#pop#pop" String="^%1$" dynamic="true" endRegion="HereDocument" column="0"/>
<IncludeRules context="heredoc_rules" />
</context>
<context name="apostrophed_normal_heredoc" attribute="Here Document" lineEndContext="#stay" dynamic="true">
- <RegExpr attribute="Keyword" context="#pop#pop" String="%1$" dynamic="true" endRegion="HereDocument" column="0"/>
+ <RegExpr attribute="Keyword" context="#pop#pop" String="^%1$" dynamic="true" endRegion="HereDocument" column="0"/>
</context>
<!-- rules for heredoc types -->
<context name="heredoc_rules" attribute="Normal Text" lineEndContext="#stay">
<RegExpr attribute="Substitution" String="#@{1,2}" context="Short Subst"/>
+ <Detect2Chars attribute="Substitution" char="#" char1="$" context="Short Subst"/>
<Detect2Chars attribute="Substitution" char="#" char1="{" context="Subst"/>
</context>
@@ -478,8 +472,8 @@
<DetectSpaces />
<Detect2Chars attribute="Operator" char="&lt;" char1="&lt;" context="#pop"/>
<!-- comments -->
- <RegExpr attribute="Comment" String="#\s*BEGIN.*$" context="#stay" beginRegion="marker" column="0"/>
- <RegExpr attribute="Comment" String="#\s*END.*$" context="#stay" endRegion="marker" column="0"/>
+ <RegExpr attribute="Comment" String="^#\s*BEGIN.*$" context="#stay" beginRegion="marker" column="0"/>
+ <RegExpr attribute="Comment" String="^#\s*END.*$" context="#stay" endRegion="marker" column="0"/>
<DetectChar attribute="Comment" char="#" context="General Comment"/>
</context>
@@ -599,13 +593,14 @@
-->
<context name="gdl_dq_string_5" attribute="String" lineEndContext="#stay" dynamic="true">
<IncludeRules context="dq_string_rules" />
- <RegExpr attribute="String" String="\\%1" context="#stay" dynamic="true" />
+ <StringDetect attribute="String" String="\%1" context="#stay" dynamic="true" />
<RegExpr attribute="GDL input" context="#pop#pop" String="\s*%1" dynamic="true" endRegion="GdlInput" />
</context>
<!-- rules to be included in all dq_string contexts -->
<context name="dq_string_rules" attribute="String" lineEndContext="#stay" >
<Detect2Chars attribute="String" char="\" char1="\" context="#stay"/>
<RegExpr attribute="Substitution" String="#@{1,2}" context="Short Subst"/>
+ <Detect2Chars attribute="Substitution" char="#" char1="$" context="Short Subst"/>
<Detect2Chars attribute="Substitution" char="#" char1="{" context="Subst"/>
</context>
@@ -664,7 +659,7 @@
-->
<context name="gdl_token_array_5" attribute="String" lineEndContext="#stay" dynamic="true">
<IncludeRules context="token_array_rules" />
- <RegExpr attribute="String" String="\\%1" context="#stay" dynamic="true"/>
+ <StringDetect attribute="String" String="\%1" context="#stay" dynamic="true"/>
<RegExpr attribute="GDL input" context="#pop#pop" String="\s*%1" dynamic="true" endRegion="GdlInput" />
</context>
@@ -728,7 +723,7 @@
-->
<context name="gdl_apostrophed_5" attribute="Raw String" lineEndContext="#stay" dynamic="true">
<IncludeRules context="apostrophed_rules" />
- <RegExpr attribute="Raw String" String="\\%1" context="#stay" dynamic="true"/>
+ <StringDetect attribute="Raw String" String="\%1" context="#stay" dynamic="true"/>
<RegExpr attribute="GDL input" context="#pop#pop" String="\s*%1" dynamic="true" endRegion="GdlInput" />
</context>
@@ -792,7 +787,7 @@
-->
<context name="gdl_shell_command_5" attribute="Command" lineEndContext="#stay" dynamic="true">
<IncludeRules context="shell_command_rules" />
- <RegExpr attribute="Command" String="\\%1" context="#stay" dynamic="true" />
+ <StringDetect attribute="Command" String="\%1" context="#stay" dynamic="true" />
<RegExpr attribute="GDL input" context="#pop#pop" String="\s*%1" dynamic="true" endRegion="GdlInput" />
</context>
@@ -800,6 +795,7 @@
<context name="shell_command_rules" attribute="Command" lineEndContext="#stay" >
<Detect2Chars attribute="Command" char="\" char1="\" context="#stay"/>
<RegExpr attribute="Substitution" String="#@{1,2}" context="Short Subst"/>
+ <Detect2Chars attribute="Substitution" char="#" char1="$" context="Short Subst"/>
<Detect2Chars attribute="Substitution" char="#" char1="{" context="Subst"/>
</context>
@@ -858,7 +854,7 @@
-->
<context name="gdl_regexpr_5" attribute="Regular Expression" lineEndContext="#stay" dynamic="true">
<IncludeRules context="regexpr_rules" />
- <RegExpr attribute="Regular Expression" String="\\%1" context="#stay" dynamic="true" />
+ <StringDetect attribute="Regular Expression" String="\%1" context="#stay" dynamic="true" />
<RegExpr attribute="GDL input" context="#pop#pop" String="\s*%1[uiomxn]*" dynamic="true" endRegion="GdlInput" />
</context>
@@ -866,6 +862,7 @@
<context name="regexpr_rules" attribute="Regular Expression" lineEndContext="#stay" >
<Detect2Chars attribute="Regular Expression" char="\" char1="\" context="#stay"/>
<RegExpr attribute="Substitution" String="#@{1,2}" context="Short Subst"/>
+ <Detect2Chars attribute="Substitution" char="#" char1="$" context="Short Subst"/>
<Detect2Chars attribute="Substitution" char="#" char1="{" context="Subst"/>
</context>
@@ -878,9 +875,9 @@
<itemDatas>
<itemData name="Normal Text" defStyleNum="dsNormal"/>
- <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Keyword" defStyleNum="dsControlFlow"/>
<itemData name="Attribute Definition" defStyleNum="dsOthers"/>
- <itemData name="Access Control" defStyleNum="dsKeyword" color="#0000FF"/>
+ <itemData name="Access Control" defStyleNum="dsAttribute" bold="1"/> <!-- #0000FF -->
<itemData name="Definition" defStyleNum="dsKeyword"/>
<itemData name="Pseudo variable" defStyleNum="dsDecVal"/>
@@ -891,24 +888,24 @@
<itemData name="Hex" defStyleNum="dsBaseN"/>
<itemData name="Bin" defStyleNum="dsBaseN"/>
- <itemData name="Symbol" defStyleNum="dsString" color="#D40000"/>
+ <itemData name="Symbol" defStyleNum="dsWarning" bold="0" underline="0"/> <!-- #D40000 -->
<itemData name="String" defStyleNum="dsString"/>
- <itemData name="Raw String" defStyleNum="dsString" color="#DD4A4A" selColor="#DD4A4A"/>
- <itemData name="Command" defStyleNum="dsString" color="#AA3000"/>
- <itemData name="Message" defStyleNum="dsNormal" color="#4000A7"/> <!-- #4A00C0 -->
- <itemData name="Regular Expression" defStyleNum="dsOthers" color="#4A5704"/>
- <itemData name="Substitution" defStyleNum="dsOthers"/>
+ <itemData name="Raw String" defStyleNum="dsVerbatimString" /> <!-- #DD4A4A -->
+ <itemData name="Command" defStyleNum="dsInformation"/> <!-- #AA3000 -->
+ <itemData name="Message" defStyleNum="dsAttribute" bold="0"/> <!-- #4000A7 -->
+ <itemData name="Regular Expression" defStyleNum="dsSpecialString"/> <!-- #4A5704 -->
+ <itemData name="Substitution" defStyleNum="dsSpecialChar"/>
<itemData name="Data" defStyleNum="dsNormal"/>
<!-- short for 'general delimited input' -->
<itemData name="GDL input" defStyleNum="dsOthers" />
- <itemData name="Default globals" defStyleNum="dsDataType" color="#C00000" bold="1"/>
- <itemData name="Global Variable" defStyleNum="dsDataType" color="#C00000"/>
- <itemData name="Global Constant" defStyleNum="dsDataType" color="#bb1188" bold="1"/>
+ <itemData name="Default globals" defStyleNum="dsVariable" bold="1"/> <!-- #C00000 -->
+ <itemData name="Global Variable" defStyleNum="dsVariable"/> <!-- #C00000 -->
+ <itemData name="Global Constant" defStyleNum="dsConstant" bold="1"/> <!-- #bb1188 -->
<itemData name="Constant" defStyleNum="dsDataType"/>
- <itemData name="Constant Value" defStyleNum="dsDataType" color="#bb1188"/>
- <itemData name="Kernel methods" defStyleNum="dsNormal" color="#000080" selColor="#ffffff"/> <!-- #CC0E86 -->
- <itemData name="Module mixin methods" defStyleNum="dsNormal" color="#000080" selColor="#ffffff"/> <!-- #CC0E86 -->
+ <itemData name="Constant Value" defStyleNum="dsConstant" bold="0"/> <!-- #bb1188 -->
+ <itemData name="Kernel methods" defStyleNum="dsFunction" bold="1"/> <!-- #CC0E86 -->
+ <itemData name="Module mixin methods" defStyleNum="dsFunction" bold="1"/> <!-- #CC0E86 -->
<itemData name="Member" defStyleNum="dsNormal"/>
<itemData name="Instance Variable" defStyleNum="dsOthers"/>
<itemData name="Class Variable" defStyleNum="dsOthers"/>
@@ -916,15 +913,15 @@
<itemData name="Comment" defStyleNum="dsComment"/>
<itemData name="Blockcomment" defStyleNum="dsComment"/>
- <itemData name="Here Document" defStyleNum="dsOthers"/>
+ <itemData name="Here Document" defStyleNum="dsDocumentation"/>
- <itemData name="Delimiter" defStyleNum="dsNormal" color="#FF9FEC"/>
- <itemData name="Operator" defStyleNum="dsNormal" color="#FF9FEC"/>
+ <itemData name="Delimiter" defStyleNum="dsKeyword"/> <!-- #FF9FEC -->
+ <itemData name="Operator" defStyleNum="dsKeyword"/> <!-- #FF9FEC -->
</itemDatas>
</highlighting>
<general>
<comments>
- <comment name="singleLine" start="#"/>
+ <comment name="singleLine" start="#" position="afterwhitespace"/>
</comments>
<keywords casesensitive="1" weakDeliminator="!?"/>
</general>
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/spdx-comments.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/spdx-comments.xml
new file mode 100644
index 0000000000..2b89fc5ac7
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/spdx-comments.xml
@@ -0,0 +1,591 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language>
+<!-- ***** THIS FILE WAS GENERATED BY A SCRIPT - DO NOT EDIT *****
+ cd data/generators
+ # increase version of spdx-comments.xml.tpl then
+ ./generate-spdx-syntax.py > ../syntax/spdx-comments.xml
+-->
+<language
+ version="6"
+ kateversion="3.1"
+ name="SPDX-Comments"
+ section="Other"
+ extensions=""
+ mimetype=""
+ author="Alex Turbov (i.zaufi@gmail.com)"
+ license="MIT"
+ hidden="true"
+ >
+ <highlighting>
+ <list name="tags">
+ <item>SPDX-License-Identifier:</item>
+ <item>SPDX-FileContributor:</item>
+ <item>SPDX-FileCopyrightText:</item>
+ <item>SPDX-LicenseInfoInFile:</item>
+ </list>
+
+ <list name="operators">
+ <item>AND</item>
+ <item>OR</item>
+ <item>WITH</item>
+ </list>
+
+ <list name="licenses">
+ <item>bzip2-1.0.6</item>
+ <item>Intel-ACPI</item>
+ <item>XSkat</item>
+ <item>CC-BY-NC-SA-2.0</item>
+ <item>Plexus</item>
+ <item>Giftware</item>
+ <item>BitTorrent-1.0</item>
+ <item>APSL-1.1</item>
+ <item>UPL-1.0</item>
+ <item>Caldera</item>
+ <item>Zend-2.0</item>
+ <item>CUA-OPL-1.0</item>
+ <item>JPNIC</item>
+ <item>SAX-PD</item>
+ <item>CC-BY-ND-2.5</item>
+ <item>eGenix</item>
+ <item>LGPLLR</item>
+ <item>OLDAP-2.2.2</item>
+ <item>CC-BY-ND-3.0-DE</item>
+ <item>IPA</item>
+ <item>NCSA</item>
+ <item>W3C</item>
+ <item>Adobe-2006</item>
+ <item>Net-SNMP</item>
+ <item>CC-BY-SA-4.0</item>
+ <item>YPL-1.0</item>
+ <item>MITNFA</item>
+ <item>PHP-3.01</item>
+ <item>BSD-Source-Code</item>
+ <item>CC-BY-SA-2.5</item>
+ <item>Motosoto</item>
+ <item>OSL-1.1</item>
+ <item>NGPL</item>
+ <item>CC-BY-2.5-AU</item>
+ <item>Unicode-TOU</item>
+ <item>BSD-3-Clause-No-Nuclear-License</item>
+ <item>OPUBL-1.0</item>
+ <item>CC-BY-NC-SA-2.0-UK</item>
+ <item>NLOD-2.0</item>
+ <item>gnuplot</item>
+ <item>EPICS</item>
+ <item>Info-ZIP</item>
+ <item>OLDAP-2.0</item>
+ <item>CERN-OHL-P-2.0</item>
+ <item>BSD-3-Clause-No-Nuclear-Warranty</item>
+ <item>AML</item>
+ <item>MulanPSL-1.0</item>
+ <item>Multics</item>
+ <item>VSL-1.0</item>
+ <item>RSA-MD</item>
+ <item>CC-PDDC</item>
+ <item>CC-BY-SA-2.1-JP</item>
+ <item>LPPL-1.2</item>
+ <item>Spencer-94</item>
+ <item>OLDAP-1.2</item>
+ <item>O-UDA-1.0</item>
+ <item>OLDAP-2.7</item>
+ <item>Glulxe</item>
+ <item>iMatix</item>
+ <item>TAPR-OHL-1.0</item>
+ <item>NBPL-1.0</item>
+ <item>LiLiQ-R-1.1</item>
+ <item>Noweb</item>
+ <item>CC0-1.0</item>
+ <item>BSD-Protection</item>
+ <item>CC-BY-NC-2.5</item>
+ <item>Zlib</item>
+ <item>GFDL-1.3-invariants-or-later</item>
+ <item>CC-BY-3.0-AT</item>
+ <item>LPPL-1.3c</item>
+ <item>EPL-1.0</item>
+ <item>GFDL-1.1-invariants-or-later</item>
+ <item>ANTLR-PD-fallback</item>
+ <item>OLDAP-2.4</item>
+ <item>OLDAP-2.3</item>
+ <item>ZPL-2.1</item>
+ <item>Apache-2.0</item>
+ <item>SGI-B-2.0</item>
+ <item>Hippocratic-2.1</item>
+ <item>CC-BY-SA-3.0-DE</item>
+ <item>CC-BY-NC-SA-1.0</item>
+ <item>LGPL-2.1-or-later</item>
+ <item>CC-BY-3.0-US</item>
+ <item>TCP-wrappers</item>
+ <item>GFDL-1.2-invariants-or-later</item>
+ <item>Eurosym</item>
+ <item>LPPL-1.0</item>
+ <item>SGI-B-1.0</item>
+ <item>APL-1.0</item>
+ <item>libtiff</item>
+ <item>AFL-2.1</item>
+ <item>CC-BY-NC-1.0</item>
+ <item>GD</item>
+ <item>AFL-1.1</item>
+ <item>CC-BY-NC-ND-3.0-IGO</item>
+ <item>Unicode-DFS-2015</item>
+ <item>GFDL-1.2-only</item>
+ <item>MPL-1.1</item>
+ <item>GPL-2.0-only</item>
+ <item>CC-BY-NC-4.0</item>
+ <item>FreeImage</item>
+ <item>SHL-0.51</item>
+ <item>CNRI-Jython</item>
+ <item>ZPL-1.1</item>
+ <item>Afmparse</item>
+ <item>OLDAP-2.1</item>
+ <item>Rdisc</item>
+ <item>Imlib2</item>
+ <item>BSD-4-Clause-Shortened</item>
+ <item>Sendmail</item>
+ <item>CC-BY-2.5</item>
+ <item>AAL</item>
+ <item>MPL-2.0-no-copyleft-exception</item>
+ <item>CC-BY-NC-ND-2.5</item>
+ <item>CC-BY-3.0-NL</item>
+ <item>LPL-1.02</item>
+ <item>ECL-1.0</item>
+ <item>OFL-1.0-no-RFN</item>
+ <item>CC-BY-NC-SA-3.0-DE</item>
+ <item>CC-BY-SA-3.0</item>
+ <item>NTP</item>
+ <item>MPL-2.0</item>
+ <item>APSL-1.2</item>
+ <item>GFDL-1.2-no-invariants-only</item>
+ <item>Artistic-2.0</item>
+ <item>RSCPL</item>
+ <item>Sleepycat</item>
+ <item>xpp</item>
+ <item>CDLA-Sharing-1.0</item>
+ <item>ClArtistic</item>
+ <item>AGPL-1.0-only</item>
+ <item>CC-BY-3.0-DE</item>
+ <item>AFL-2.0</item>
+ <item>Intel</item>
+ <item>GFDL-1.1-no-invariants-or-later</item>
+ <item>APAFML</item>
+ <item>SISSL</item>
+ <item>Naumen</item>
+ <item>HTMLTIDY</item>
+ <item>OLDAP-2.8</item>
+ <item>blessing</item>
+ <item>CC-BY-ND-2.0</item>
+ <item>OGTSL</item>
+ <item>LGPL-2.0-or-later</item>
+ <item>Parity-7.0.0</item>
+ <item>CC-BY-ND-1.0</item>
+ <item>dvipdfm</item>
+ <item>CNRI-Python</item>
+ <item>BSD-4-Clause-UC</item>
+ <item>NLOD-1.0</item>
+ <item>MS-RL</item>
+ <item>CC-BY-NC-SA-4.0</item>
+ <item>HaskellReport</item>
+ <item>CC-BY-1.0</item>
+ <item>UCL-1.0</item>
+ <item>Mup</item>
+ <item>SMPPL</item>
+ <item>PHP-3.0</item>
+ <item>GL2PS</item>
+ <item>CrystalStacker</item>
+ <item>W3C-20150513</item>
+ <item>NIST-PD-fallback</item>
+ <item>OGL-UK-1.0</item>
+ <item>CPL-1.0</item>
+ <item>LGPL-2.1-only</item>
+ <item>ZPL-2.0</item>
+ <item>Frameworx-1.0</item>
+ <item>AGPL-3.0-only</item>
+ <item>DRL-1.0</item>
+ <item>EFL-2.0</item>
+ <item>Spencer-99</item>
+ <item>CAL-1.0-Combined-Work-Exception</item>
+ <item>GFDL-1.1-invariants-only</item>
+ <item>TCL</item>
+ <item>SHL-0.5</item>
+ <item>OFL-1.0-RFN</item>
+ <item>CERN-OHL-W-2.0</item>
+ <item>Glide</item>
+ <item>mpich2</item>
+ <item>psutils</item>
+ <item>SPL-1.0</item>
+ <item>Apache-1.1</item>
+ <item>CC-BY-ND-4.0</item>
+ <item>FreeBSD-DOC</item>
+ <item>SCEA</item>
+ <item>Latex2e</item>
+ <item>Artistic-1.0-cl8</item>
+ <item>SGI-B-1.1</item>
+ <item>NRL</item>
+ <item>SWL</item>
+ <item>Zed</item>
+ <item>CERN-OHL-1.1</item>
+ <item>RHeCos-1.1</item>
+ <item>JasPer-2.0</item>
+ <item>SSPL-1.0</item>
+ <item>OLDAP-1.4</item>
+ <item>libpng-2.0</item>
+ <item>CNRI-Python-GPL-Compatible</item>
+ <item>Aladdin</item>
+ <item>CECILL-1.0</item>
+ <item>Ruby</item>
+ <item>NPL-1.1</item>
+ <item>ImageMagick</item>
+ <item>Cube</item>
+ <item>GFDL-1.1-only</item>
+ <item>CC-BY-2.0</item>
+ <item>AFL-1.2</item>
+ <item>CC-BY-SA-2.0</item>
+ <item>CECILL-2.0</item>
+ <item>MIT-advertising</item>
+ <item>CC-BY-NC-SA-2.5</item>
+ <item>Artistic-1.0</item>
+ <item>OSL-3.0</item>
+ <item>X11</item>
+ <item>Bahyph</item>
+ <item>OLDAP-2.0.1</item>
+ <item>EUDatagrid</item>
+ <item>MTLL</item>
+ <item>GFDL-1.2-invariants-only</item>
+ <item>GFDL-1.3-no-invariants-or-later</item>
+ <item>curl</item>
+ <item>LAL-1.3</item>
+ <item>DSDP</item>
+ <item>CERN-OHL-1.2</item>
+ <item>TOSL</item>
+ <item>CC-BY-3.0</item>
+ <item>Qhull</item>
+ <item>GFDL-1.3-no-invariants-only</item>
+ <item>TORQUE-1.1</item>
+ <item>MS-PL</item>
+ <item>Apache-1.0</item>
+ <item>copyleft-next-0.3.1</item>
+ <item>GFDL-1.2-or-later</item>
+ <item>MulanPSL-2.0</item>
+ <item>FSFAP</item>
+ <item>Xerox</item>
+ <item>CDDL-1.0</item>
+ <item>GFDL-1.3-invariants-only</item>
+ <item>etalab-2.0</item>
+ <item>XFree86-1.1</item>
+ <item>SNIA</item>
+ <item>LPPL-1.1</item>
+ <item>CATOSL-1.1</item>
+ <item>TU-Berlin-2.0</item>
+ <item>GFDL-1.3-or-later</item>
+ <item>LAL-1.2</item>
+ <item>ICU</item>
+ <item>FTL</item>
+ <item>MirOS</item>
+ <item>CC-BY-NC-ND-3.0</item>
+ <item>OSET-PL-2.1</item>
+ <item>CC-BY-NC-ND-2.0</item>
+ <item>SISSL-1.2</item>
+ <item>Wsuipa</item>
+ <item>Zimbra-1.4</item>
+ <item>Linux-OpenIB</item>
+ <item>OLDAP-2.5</item>
+ <item>AMPAS</item>
+ <item>GPL-1.0-or-later</item>
+ <item>BUSL-1.1</item>
+ <item>Adobe-Glyph</item>
+ <item>0BSD</item>
+ <item>W3C-19980720</item>
+ <item>FSFUL</item>
+ <item>CC-BY-NC-SA-3.0</item>
+ <item>DOC</item>
+ <item>TMate</item>
+ <item>MIT-open-group</item>
+ <item>AMDPLPA</item>
+ <item>Condor-1.1</item>
+ <item>PolyForm-Noncommercial-1.0.0</item>
+ <item>BSD-3-Clause-No-Military-License</item>
+ <item>CC-BY-4.0</item>
+ <item>OGL-Canada-2.0</item>
+ <item>CC-BY-NC-SA-3.0-IGO</item>
+ <item>EFL-1.0</item>
+ <item>Newsletr</item>
+ <item>copyleft-next-0.3.0</item>
+ <item>GPL-3.0-or-later</item>
+ <item>CDLA-Permissive-2.0</item>
+ <item>CC-BY-ND-3.0</item>
+ <item>C-UDA-1.0</item>
+ <item>Barr</item>
+ <item>Vim</item>
+ <item>BitTorrent-1.1</item>
+ <item>CDL-1.0</item>
+ <item>CC-BY-SA-1.0</item>
+ <item>ADSL</item>
+ <item>PostgreSQL</item>
+ <item>OFL-1.1</item>
+ <item>NPL-1.0</item>
+ <item>xinetd</item>
+ <item>LGPL-2.0-only</item>
+ <item>zlib-acknowledgement</item>
+ <item>OLDAP-2.2.1</item>
+ <item>APSL-1.0</item>
+ <item>BSD-3-Clause-LBNL</item>
+ <item>GLWTPL</item>
+ <item>LGPL-3.0-only</item>
+ <item>OGC-1.0</item>
+ <item>Dotseqn</item>
+ <item>MakeIndex</item>
+ <item>GPL-3.0-only</item>
+ <item>BSD-3-Clause-No-Nuclear-License-2014</item>
+ <item>GPL-1.0-only</item>
+ <item>IJG</item>
+ <item>AGPL-1.0-or-later</item>
+ <item>OFL-1.1-no-RFN</item>
+ <item>BSL-1.0</item>
+ <item>Libpng</item>
+ <item>CC-BY-NC-3.0</item>
+ <item>CC-BY-NC-2.0</item>
+ <item>Unlicense</item>
+ <item>LPL-1.0</item>
+ <item>bzip2-1.0.5</item>
+ <item>Entessa</item>
+ <item>BSD-2-Clause-Patent</item>
+ <item>ECL-2.0</item>
+ <item>Crossword</item>
+ <item>CC-BY-NC-ND-1.0</item>
+ <item>OCLC-2.0</item>
+ <item>CECILL-1.1</item>
+ <item>CECILL-2.1</item>
+ <item>OGDL-Taiwan-1.0</item>
+ <item>Abstyles</item>
+ <item>libselinux-1.0</item>
+ <item>ANTLR-PD</item>
+ <item>GPL-2.0-or-later</item>
+ <item>IPL-1.0</item>
+ <item>MIT-enna</item>
+ <item>CPOL-1.02</item>
+ <item>CC-BY-SA-3.0-AT</item>
+ <item>BSD-1-Clause</item>
+ <item>NTP-0</item>
+ <item>SugarCRM-1.1.3</item>
+ <item>MIT</item>
+ <item>OFL-1.1-RFN</item>
+ <item>Watcom-1.0</item>
+ <item>CC-BY-NC-SA-2.0-FR</item>
+ <item>ODbL-1.0</item>
+ <item>FSFULLR</item>
+ <item>OLDAP-1.3</item>
+ <item>SSH-OpenSSH</item>
+ <item>BSD-2-Clause</item>
+ <item>HPND</item>
+ <item>Zimbra-1.3</item>
+ <item>Borceux</item>
+ <item>OLDAP-1.1</item>
+ <item>OFL-1.0</item>
+ <item>NASA-1.3</item>
+ <item>VOSTROM</item>
+ <item>MIT-0</item>
+ <item>ISC</item>
+ <item>Unicode-DFS-2016</item>
+ <item>BlueOak-1.0.0</item>
+ <item>LiLiQ-Rplus-1.1</item>
+ <item>NOSL</item>
+ <item>SMLNJ</item>
+ <item>CPAL-1.0</item>
+ <item>PSF-2.0</item>
+ <item>RPL-1.5</item>
+ <item>MIT-Modern-Variant</item>
+ <item>Nokia</item>
+ <item>GFDL-1.1-no-invariants-only</item>
+ <item>PDDL-1.0</item>
+ <item>EUPL-1.0</item>
+ <item>CDDL-1.1</item>
+ <item>GFDL-1.3-only</item>
+ <item>OLDAP-2.6</item>
+ <item>JSON</item>
+ <item>LGPL-3.0-or-later</item>
+ <item>Fair</item>
+ <item>OSL-2.1</item>
+ <item>LPPL-1.3a</item>
+ <item>NAIST-2003</item>
+ <item>CC-BY-NC-ND-4.0</item>
+ <item>CC-BY-NC-3.0-DE</item>
+ <item>OPL-1.0</item>
+ <item>HPND-sell-variant</item>
+ <item>QPL-1.0</item>
+ <item>EUPL-1.2</item>
+ <item>GFDL-1.2-no-invariants-or-later</item>
+ <item>NCGL-UK-2.0</item>
+ <item>Beerware</item>
+ <item>BSD-3-Clause-Open-MPI</item>
+ <item>CECILL-B</item>
+ <item>EPL-2.0</item>
+ <item>MIT-feh</item>
+ <item>RPL-1.1</item>
+ <item>CDLA-Permissive-1.0</item>
+ <item>Python-2.0</item>
+ <item>MPL-1.0</item>
+ <item>GFDL-1.1-or-later</item>
+ <item>diffmark</item>
+ <item>OpenSSL</item>
+ <item>OSL-1.0</item>
+ <item>Parity-6.0.0</item>
+ <item>YPL-1.1</item>
+ <item>SSH-short</item>
+ <item>IBM-pibs</item>
+ <item>Xnet</item>
+ <item>TU-Berlin-1.0</item>
+ <item>CAL-1.0</item>
+ <item>AFL-3.0</item>
+ <item>CECILL-C</item>
+ <item>OGL-UK-3.0</item>
+ <item>BSD-3-Clause-Clear</item>
+ <item>BSD-3-Clause-Modification</item>
+ <item>CC-BY-SA-2.0-UK</item>
+ <item>Saxpath</item>
+ <item>NLPL</item>
+ <item>SimPL-2.0</item>
+ <item>psfrag</item>
+ <item>Spencer-86</item>
+ <item>OCCT-PL</item>
+ <item>CERN-OHL-S-2.0</item>
+ <item>ErlPL-1.1</item>
+ <item>MIT-CMU</item>
+ <item>NIST-PD</item>
+ <item>OSL-2.0</item>
+ <item>APSL-2.0</item>
+ <item>Leptonica</item>
+ <item>PolyForm-Small-Business-1.0.0</item>
+ <item>LiLiQ-P-1.1</item>
+ <item>NetCDF</item>
+ <item>OML</item>
+ <item>AGPL-3.0-or-later</item>
+ <item>OLDAP-2.2</item>
+ <item>BSD-3-Clause</item>
+ <item>WTFPL</item>
+ <item>OGL-UK-2.0</item>
+ <item>BSD-3-Clause-Attribution</item>
+ <item>RPSL-1.0</item>
+ <item>CC-BY-NC-ND-3.0-DE</item>
+ <item>EUPL-1.1</item>
+ <item>Sendmail-8.23</item>
+ <item>ODC-By-1.0</item>
+ <item>D-FSL-1.0</item>
+ <item>BSD-4-Clause</item>
+ <item>BSD-2-Clause-Views</item>
+ <item>Artistic-1.0-Perl</item>
+ <item>NPOSL-3.0</item>
+ <item>gSOAP-1.3b</item>
+ <item>Interbase-1.0</item>
+ </list>
+
+ <list name="deprecated-licenses">
+ <item>GPL-1.0</item>
+ <item>GPL-2.0-with-GCC-exception</item>
+ <item>wxWindows</item>
+ <item>Nunit</item>
+ <item>GFDL-1.1</item>
+ <item>GPL-2.0</item>
+ <item>GFDL-1.2</item>
+ <item>LGPL-2.0</item>
+ <item>GPL-3.0-with-autoconf-exception</item>
+ <item>GFDL-1.3</item>
+ <item>BSD-2-Clause-NetBSD</item>
+ <item>LGPL-3.0</item>
+ <item>GPL-2.0-with-classpath-exception</item>
+ <item>GPL-3.0-with-GCC-exception</item>
+ <item>BSD-2-Clause-FreeBSD</item>
+ <item>GPL-3.0</item>
+ <item>GPL-2.0-with-font-exception</item>
+ <item>eCos-2.0</item>
+ <item>GPL-2.0-with-bison-exception</item>
+ <item>GPL-2.0-with-autoconf-exception</item>
+ <item>AGPL-1.0</item>
+ <item>AGPL-3.0</item>
+ <item>LGPL-2.1</item>
+ <item>StandardML-NJ</item>
+ </list>
+
+ <list name="exceptions">
+ <item>GPL-CC-1.0</item>
+ <item>openvpn-openssl-exception</item>
+ <item>WxWindows-exception-3.1</item>
+ <item>GPL-3.0-linking-exception</item>
+ <item>i2p-gpl-java-exception</item>
+ <item>OpenJDK-assembly-exception-1.0</item>
+ <item>mif-exception</item>
+ <item>CLISP-exception-2.0</item>
+ <item>freertos-exception-2.0</item>
+ <item>Bison-exception-2.2</item>
+ <item>OCCT-exception-1.0</item>
+ <item>Autoconf-exception-2.0</item>
+ <item>LLVM-exception</item>
+ <item>GCC-exception-3.1</item>
+ <item>Font-exception-2.0</item>
+ <item>Libtool-exception</item>
+ <item>u-boot-exception-2.0</item>
+ <item>Swift-exception</item>
+ <item>eCos-exception-2.0</item>
+ <item>OCaml-LGPL-linking-exception</item>
+ <item>Qt-GPL-exception-1.0</item>
+ <item>Linux-syscall-note</item>
+ <item>Bootloader-exception</item>
+ <item>PS-or-PDF-font-exception-20170817</item>
+ <item>Universal-FOSS-exception-1.0</item>
+ <item>Classpath-exception-2.0</item>
+ <item>Qwt-exception-1.0</item>
+ <item>LZMA-exception</item>
+ <item>Autoconf-exception-3.0</item>
+ <item>DigiRule-FOSS-exception</item>
+ <item>389-exception</item>
+ <item>SHL-2.0</item>
+ <item>GCC-exception-2.0</item>
+ <item>GPL-3.0-linking-source-exception</item>
+ <item>Qt-LGPL-exception-1.1</item>
+ <item>Fawkes-Runtime-exception</item>
+ <item>gnu-javamail-exception</item>
+ <item>FLTK-exception</item>
+ <item>LGPL-3.0-linking-exception</item>
+ <item>SHL-2.1</item>
+ </list>
+
+ <list name="deprecated-exceptions">
+ <item>Nokia-Qt-exception-1.1</item>
+ </list>
+
+ <contexts>
+
+ <context name="Normal" attribute="SPDX Tag" lineEndContext="#pop">
+ <WordDetect String="SPDX-License-Identifier:" attribute="SPDX Tag" context="license-expression" />
+ <keyword String="tags" attribute="SPDX Tag" />
+ </context>
+
+ <context name="license-expression" attribute="SPDX Value" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop">
+ <DetectSpaces/>
+ <AnyChar String="()+" context="#stay" attribute="SPDX License Expression Operator" />
+ <keyword String="licenses" context="#stay" attribute="SPDX License" />
+ <keyword String="deprecated-licenses" context="#stay" attribute="SPDX Deprecated License" />
+ <keyword String="exceptions" context="#stay" attribute="SPDX License Exception" />
+ <keyword String="deprecated-exceptions" context="#stay" attribute="SPDX Deprecated License Exception" />
+ <keyword String="operators" context="#stay" attribute="SPDX License Expression Operator" />
+ <RegExpr attribute="SPDX License" context="#stay" String="\bLicenseRef-[^\s]+" />
+ </context>
+
+ </contexts>
+
+ <itemDatas>
+ <itemData name="SPDX Tag" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX Value" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX License" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX License Exception" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX Deprecated License" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX Deprecated License Exception" defStyleNum="dsAnnotation" italic="true" spellChecking="false" />
+ <itemData name="SPDX License Expression Operator" defStyleNum="dsOperator" italic="true" spellChecking="false" />
+ </itemDatas>
+
+ </highlighting>
+
+ <general>
+ <keywords casesensitive="1" weakDeliminator=":-." />
+ </general>
+
+</language>
+<!-- kate: indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/toml.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/toml.xml
new file mode 100644
index 0000000000..d643069bf8
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/toml.xml
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language
+[
+ <!ENTITY more "(_[0-9]++)*+">
+ <!ENTITY int "(0|[1-9][0-9]*+&more;)">
+ <!ENTITY frac "\.[0-9]+&more;">
+ <!ENTITY exp "[eE][+-]?[0-9]+&more;">
+
+ <!ENTITY offset "[+-][0-9][0-9]:[0-9][0-9]">
+ <!ENTITY time "[0-9][0-9]:[0-9][0-9]:[0-9][0-9](\.[0-9]+)?">
+ <!ENTITY datetime "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]([T ]&time;(&offset;|Z)?)?|&time;">
+]>
+<!-- https://github.com/toml-lang/toml -->
+<!-- https://toml.io/en/v1.0.0 -->
+<language
+ name="TOML"
+ section="Configuration"
+ extensions="Cargo.lock;*.toml;*.rc;*.conf;*.cfg;*.cf;*.cnf;*.ini;mirrorlist"
+ mimetype="text/x-toml;application/toml"
+ version="13"
+ kateversion="5.0"
+ author="flying-sheep@web.de"
+ license="LGPLv2+"
+ priority="-1"
+ >
+<highlighting>
+ <list name="bools">
+ <item>true</item>
+ <item>false</item>
+ </list>
+
+ <contexts>
+ <context attribute="Error" lineEndContext="#stay" name="Toml">
+ <DetectSpaces attribute="Whitespace"/>
+ <Detect2Chars attribute="TableHeader" context="NestedTableHeader" char="[" char1="[" endRegion="Table"/>
+ <DetectChar attribute="TableHeader" context="TableHeader" char="[" endRegion="Table"/>
+ <DetectChar attribute="Assignment" context="Value" char="="/>
+ <DetectChar char="#" attribute="Comment" context="Comment"/>
+ <IncludeRules context="FindKey"/>
+ </context>
+
+
+ <context attribute="Key" lineEndContext="#stay" name="FindKey">
+ <DetectChar attribute="Key" char="."/>
+ <RegExpr attribute="Key" context="#stay" String="[A-Za-z0-9_-]+"/>
+ <DetectChar attribute="Key" context="QuotedKey" char="&quot;"/>
+ <DetectChar attribute="Key" context="LitQuotedKey" char="'"/>
+ </context>
+
+
+ <!-- table headers -->
+ <context attribute="TableHeader" fallthrough="true" fallthroughContext="#pop" lineEndContext="#pop" name="TableHeader">
+ <DetectChar attribute="TableHeader" context="#pop" char="]" beginRegion="Table"/>
+ <IncludeRules context="TableHeaderCommon"/>
+ </context>
+
+ <context attribute="TableHeader" fallthrough="true" fallthroughContext="#pop" lineEndContext="#pop" name="NestedTableHeader">
+ <Detect2Chars attribute="TableHeader" context="#pop" char="]" char1="]" beginRegion="Table"/>
+ <IncludeRules context="TableHeaderCommon"/>
+ </context>
+
+ <context attribute="TableHeader" lineEndContext="#pop" name="TableHeaderCommon">
+ <DetectSpaces attribute="Whitespace"/>
+ <DetectChar attribute="TableHeader" char="."/>
+ <RegExpr attribute="TableHeader" context="#stay" String="[A-Za-z0-9_-]+"/>
+ <DetectChar attribute="TableHeader" context="QuotedKey" char="&quot;"/>
+ <DetectChar attribute="TableHeader" context="LitQuotedKey" char="'"/>
+ </context>
+
+
+ <!-- values -->
+ <context attribute="Error" lineEndContext="#pop" fallthrough="true" fallthroughContext="#pop" name="Value">
+ <DetectSpaces attribute="Whitespace"/>
+ <WordDetect attribute="Boolean true" context="#pop" String="true"/>
+ <WordDetect attribute="Boolean false" context="#pop" String="false"/>
+ <StringDetect attribute="String" context="#pop!MultilineString" String="&quot;&quot;&quot;"/>
+ <DetectChar attribute="String" context="#pop!String" char="&quot;"/>
+ <StringDetect attribute="String" context="#pop!LitMultilineString" String="'''"/>
+ <DetectChar attribute="String" context="#pop!LitString" char="'"/>
+ <DetectChar attribute="Array" context="#pop!Array" char="["/>
+ <DetectChar attribute="InlineTable" context="#pop!InlineTable" char="{"/>
+ <RegExpr attribute="Date" context="#pop" String="&datetime;"/>
+ <RegExpr attribute="Int" context="#pop" String="[+-]?(0x[0-9a-fA-F]+(_[0-9a-fA-F]+)*|0o[0-7]+(_[0-7]+)*|0b[01]+(_[01]+)*|&int;(?!(\.|[eE][+-]?)[0-9]))"/>
+ <RegExpr attribute="Float" context="#pop" String="[+-]?(&int;(&frac;(&exp;)?|&exp;)|inf|nan)"/>
+ </context>
+
+ <context attribute="Comment" lineEndContext="#pop" name="Comment">
+ <DetectSpaces/>
+ <IncludeRules context="##Comments"/>
+ <DetectIdentifier/>
+ </context>
+
+
+ <!-- Quoted keys and Strings-->
+ <context attribute="Key" lineEndContext="#pop" name="QuotedKey">
+ <DetectChar attribute="Key" context="#pop" char="&quot;"/>
+ <IncludeRules context="FindEscapedChar"/>
+ </context>
+
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ <IncludeRules context="FindEscapedChar"/>
+ </context>
+
+ <context attribute="String" lineEndContext="#stay" name="MultilineString">
+ <StringDetect attribute="String" context="#pop" String="&quot;&quot;&quot;&quot;&quot;"/>
+ <StringDetect attribute="String" context="#pop" String="&quot;&quot;&quot;&quot;"/>
+ <StringDetect attribute="String" context="#pop" String="&quot;&quot;&quot;"/>
+ <LineContinue attribute="Escape" context="#stay"/>
+ <IncludeRules context="FindEscapedChar"/>
+ </context>
+
+ <context attribute="String" lineEndContext="#pop" name="FindEscapedChar">
+ <RegExpr attribute="Escape" String="\\[btnfr&quot;\\]|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}" context="#stay"/>
+ <RegExpr attribute="Error" String="\\[uU][0-9a-fA-F]*|\\." context="#stay"/>
+ </context>
+
+ <context attribute="Key" lineEndContext="#pop" name="LitQuotedKey">
+ <DetectChar attribute="Key" context="#pop" char="'"/>
+ </context>
+
+ <context attribute="LitString" lineEndContext="#pop" name="LitString">
+ <DetectChar attribute="String" context="#pop" char="'"/>
+ </context>
+
+ <context attribute="LitString" lineEndContext="#stay" name="LitMultilineString">
+ <StringDetect attribute="String" context="#pop" String="'''''"/>
+ <StringDetect attribute="String" context="#pop" String="''''"/>
+ <StringDetect attribute="String" context="#pop" String="'''"/>
+ </context>
+
+
+ <!-- Arrays -->
+ <context attribute="Array" lineEndContext="#stay" name="Array" fallthrough="true" fallthroughContext="InArray">
+ <DetectSpaces attribute="Whitespace"/>
+ <DetectChar context="#pop" attribute="Array" char="]"/>
+ <DetectChar attribute="Comment" context="Comment" char="#"/>
+ <DetectChar context="InArray" attribute="NextEntry" char=","/>
+ </context>
+ <context attribute="Error" lineEndContext="#stay" name="InArray">
+ <DetectChar context="#pop#pop" attribute="Array" char="]"/>
+ <DetectChar context="#stay" attribute="Error" char=","/>
+ <DetectChar attribute="Comment" context="Comment" char="#"/>
+ <IncludeRules context="Value"/>
+ </context>
+
+ <context attribute="InlineTable" lineEndContext="#stay" name="InlineTable">
+ <DetectChar attribute="Assignment" context="Value" char="="/>
+ <DetectChar char="#" attribute="Comment" context="Comment"/>
+ <DetectChar context="#pop" attribute="InlineTable" char="}"/>
+ <DetectChar context="#stay" attribute="NextEntry" char=","/>
+ <IncludeRules context="FindKey"/>
+ </context>
+
+ </contexts>
+ <itemDatas>
+ <itemData name="Whitespace" defStyleNum="dsNormal"/>
+ <itemData name="Key" defStyleNum="dsDataType"/>
+ <itemData name="TableHeader" defStyleNum="dsKeyword"/>
+ <itemData name="Assignment" defStyleNum="dsOperator"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+
+ <itemData name="Date" defStyleNum="dsBaseN"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Int" defStyleNum="dsDecVal"/>
+ <itemData name="Boolean true" defStyleNum="dsConstant"/>
+ <itemData name="Boolean false" defStyleNum="dsConstant"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="LitString" defStyleNum="dsVerbatimString"/>
+ <itemData name="Escape" defStyleNum="dsSpecialChar"/>
+ <itemData name="Array" defStyleNum="dsOperator"/>
+ <itemData name="InlineTable" defStyleNum="dsOperator"/>
+ <itemData name="NextEntry" defStyleNum="dsOperator"/>
+
+ <itemData name="Error" defStyleNum="dsError"/>
+ </itemDatas>
+</highlighting>
+<general>
+ <comments>
+ <comment name="singleLine" start="#" position="afterwhitespace"/>
+ </comments>
+</general>
+</language>
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/valgrind-suppression.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/valgrind-suppression.xml
index 7fe3dd474e..e795aca79f 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/valgrind-suppression.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/valgrind-suppression.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd">
-<language name="Valgrind Suppression" section="Other" extensions="*.supp;" mimetype="" version="2" kateversion="2.4" author="Milian Wolff (mail@milianw.de)" license="LGPL">
+<!DOCTYPE language>
+<language name="Valgrind Suppression" section="Other" extensions="*.supp;" mimetype="" version="4" kateversion="5.0" author="Milian Wolff (mail@milianw.de)" license="LGPL">
<highlighting>
<contexts>
<context name="File" attribute="Normal Text" lineEndContext="#stay">
@@ -9,7 +9,7 @@
</context>
<context name="Rule" attribute="Normal Text" lineEndContext="#stay" >
- <RegExpr attribute="RuleName" String="^[^\}]+$" context="Rule2" />
+ <RegExpr attribute="RuleName" String="^[^\}]+$" context="Rule2" column="0" />
<DetectChar char="}" attribute="Normal Text" context="#pop" firstNonSpace="true" />
</context>
@@ -25,7 +25,7 @@
<context name="Comment" attribute="Comment" lineEndContext="#pop">
<DetectSpaces />
- <IncludeRules context="##Alerts" />
+ <IncludeRules context="##Comments" />
<DetectIdentifier />
</context>
</contexts>
@@ -49,3 +49,4 @@
</general>
</language>
+<!-- kate: replace-tabs on; indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/xml.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/xml.xml
index ad34a450b6..da1910e26c 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/xml.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/xml.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd"
+<!DOCTYPE language
[
<!-- names must start with a letter, ideogram or underscore. \w matches any
word character *or* a number, hence the lookahead -->
<!ENTITY name "(?![0-9])[\w_:][\w.:_-]*">
- <!ENTITY entref "&amp;(#[0-9]+|#[xX][0-9A-Fa-f]+|&name;);">
+ <!ENTITY entref "&amp;(?:#[0-9]+|#[xX][0-9A-Fa-f]+|&name;);">
]>
-<language name="XML" version="9" kateversion="3.4" section="Markup" extensions="*.docbook;*.xml;*.rc;*.daml;*.rdf;*.rss;*.xspf;*.xsd;*.svg;*.ui;*.kcfg;*.qrc;*.wsdl;*.scxml;*.xbel;*.dae;*.sch;*.brd" mimetype="text/xml;text/book;text/daml;text/rdf;application/rss+xml;application/xspf+xml;image/svg+xml;application/x-designer;application/x-xbel;application/xml;application/scxml+xml" casesensitive="1" indenter="xml" author="Wilbert Berendsen (wilbert@kde.nl)" license="LGPL">
+<language name="XML" version="20" kateversion="5.0" section="Markup" extensions="*.page;*.docbook;*.xml;*ui.rc;*.daml;*.rdf;*.rss;*.xspf;*.xsd;*.svg;*.ui;*.kcfg;*.qrc;*.wsdl;*.scxml;*.xbel;*.dae;*.sch;*.brd" mimetype="text/xml;text/book;text/daml;text/rdf;application/rss+xml;application/xspf+xml;image/svg+xml;application/x-designer;application/x-xbel;application/xml;application/scxml+xml;application/vnd.oasis.opendocument.text-flat-xml;application/vnd.oasis.opendocument.graphics-flat-xml;application/vnd.oasis.opendocument.presentation-flat-xml;application/vnd.oasis.opendocument.spreadsheet-flat-xml;application/gpx+xml" casesensitive="1" indenter="xml" author="Wilbert Berendsen (wilbert@kde.nl)" license="LGPL">
<highlighting>
<contexts>
@@ -16,13 +16,13 @@
<context name="FindXML" attribute="Normal Text" lineEndContext="#stay">
<DetectSpaces />
+ <DetectIdentifier />
+ <RegExpr attribute="Element Symbols" context="ElementTagName" String="&lt;(?=(&name;))" beginRegion="element" />
<StringDetect attribute="Comment" context="Comment" String="&lt;!--" beginRegion="comment" />
- <StringDetect attribute="CDATA" context="CDATA" String="&lt;![CDATA[" beginRegion="cdata" />
- <RegExpr attribute="Doctype" context="Doctype" String="&lt;!DOCTYPE\s+" beginRegion="doctype" />
- <RegExpr attribute="Processing Instruction" context="PI" String="&lt;\?[\w:_-]*" beginRegion="pi" />
- <RegExpr attribute="Element" context="Element" String="&lt;&name;" beginRegion="element" />
+ <StringDetect attribute="CDATA" context="CDATAStart" String="&lt;![CDATA[" lookAhead="true" />
+ <RegExpr attribute="Doctype Symbols" context="DoctypeTagName" String="&lt;!(?=DOCTYPE(\s|$))" beginRegion="doctype" />
+ <IncludeRules context="FindProcessingInstruction" />
<IncludeRules context="FindEntityRefs" />
- <DetectIdentifier />
</context>
<context name="FindEntityRefs" attribute="Other Text" lineEndContext="#stay">
@@ -33,44 +33,76 @@
<context name="FindPEntityRefs" attribute="Other Text" lineEndContext="#stay">
<RegExpr attribute="EntityRef" context="#stay" String="&entref;" />
<RegExpr attribute="PEntityRef" context="#stay" String="%&name;;" />
- <AnyChar attribute="Error" context="#stay" String="&amp;%" />
+ <AnyChar attribute="Error" context="#stay" String="&amp;%&lt;" />
</context>
<context name="Comment" attribute="Comment" lineEndContext="#stay">
<DetectSpaces />
<StringDetect attribute="Comment" context="#pop" String="--&gt;" endRegion="comment" />
- <RegExpr attribute="Error" context="#stay" String="-(-(?!-&gt;))+" />
- <IncludeRules context="##Alerts" />
+ <RegExpr attribute="Error" context="#stay" String="-(?:\-(?!-&gt;))+" />
+ <IncludeRules context="##Comments" />
<DetectIdentifier />
</context>
+ <context name="CDATAStart" attribute="Other Text" lineEndContext="#pop">
+ <StringDetect attribute="CDATA Symbols" context="#stay" String="&lt;![" beginRegion="cdata" />
+ <StringDetect attribute="CDATA" context="#stay" String="CDATA" />
+ <DetectChar attribute="CDATA Symbols" context="#pop!CDATA" char="[" />
+ </context>
<context name="CDATA" attribute="Other Text" lineEndContext="#stay">
<DetectSpaces />
<DetectIdentifier />
- <StringDetect attribute="CDATA" context="#pop" String="]]&gt;" endRegion="cdata" />
+ <StringDetect attribute="CDATA Symbols" context="#pop" String="]]&gt;" endRegion="cdata" />
<StringDetect attribute="EntityRef" context="#stay" String="]]&amp;gt;" />
</context>
+ <context name="FindProcessingInstruction" attribute="Other Text" lineEndContext="#stay">
+ <RegExpr attribute="PI Symbols" context="PI TagName" String="&lt;\?(?=([\w:_-]*))" beginRegion="pi" />
+ </context>
+ <context name="PI TagName" attribute="Other Text" lineEndContext="#pop!PI" fallthrough="true" fallthroughContext="#pop!PI">
+ <RegExpr attribute="Processing Instruction" context="#pop!PI-XML" String="xml(?=\s|$)" insensitive="true" />
+ <StringDetect attribute="Processing Instruction" context="#pop!PI" String="%1" dynamic="true" />
+ </context>
<context name="PI" attribute="Other Text" lineEndContext="#stay">
- <Detect2Chars attribute="Processing Instruction" context="#pop" char="?" char1="&gt;" endRegion="pi" />
+ <Detect2Chars attribute="PI Symbols" context="#pop" char="?" char1="&gt;" endRegion="pi" />
+ </context>
+ <context name="PI-XML" attribute="Other Text" lineEndContext="#stay">
+ <IncludeRules context="PI" />
+ <RegExpr attribute="Attribute" context="#stay" String="(?:^|\s+)&name;" />
+ <DetectChar attribute="Attribute Separator" context="Value" char="=" />
</context>
+ <context name="DoctypeTagName" attribute="Other Text" lineEndContext="#pop">
+ <StringDetect attribute="Doctype" context="#pop!DoctypeVariableName" String="DOCTYPE" />
+ </context>
+ <context name="DoctypeVariableName" attribute="Other Text" lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop!Doctype">
+ <DetectSpaces />
+ <RegExpr attribute="Doctype Name" context="#pop!Doctype" String="&name;" />
+ </context>
<context name="Doctype" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Doctype" context="#pop" char="&gt;" endRegion="doctype" />
- <DetectChar attribute="Doctype" context="Doctype Internal Subset" char="[" beginRegion="int_subset" />
+ <DetectChar attribute="Doctype Symbols" context="#pop" char="&gt;" endRegion="doctype" />
+ <DetectChar attribute="Doctype Symbols" context="Doctype Internal Subset" char="[" beginRegion="int_subset" />
</context>
<context name="Doctype Internal Subset" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Doctype" context="#pop" char="]" endRegion="int_subset" />
- <RegExpr attribute="Doctype" context="Doctype Markupdecl" String="&lt;!(ELEMENT|ENTITY|ATTLIST|NOTATION)\b" />
+ <DetectSpaces />
+ <DetectChar attribute="Doctype Symbols" context="#pop" char="]" endRegion="int_subset" />
+ <RegExpr attribute="Doctype Symbols" context="Doctype Markupdecl TagName" String="&lt;!(?=(ELEMENT|ENTITY|ATTLIST|NOTATION)\b)" />
<StringDetect attribute="Comment" context="Comment" String="&lt;!--" beginRegion="comment" />
- <RegExpr attribute="Processing Instruction" context="PI" String="&lt;\?[\w:_-]*" beginRegion="pi" />
+ <IncludeRules context="FindProcessingInstruction" />
<IncludeRules context="FindPEntityRefs" />
</context>
+ <context name="Doctype Markupdecl TagName" attribute="Other Text" lineEndContext="#pop">
+ <DetectIdentifier attribute="Doctype" context="#pop!Doctype Markupdecl VariableName" />
+ </context>
+ <context name="Doctype Markupdecl VariableName" attribute="Other Text" lineEndContext="#pop!Doctype Markupdecl" fallthrough="true" fallthroughContext="#pop!Doctype Markupdecl">
+ <DetectSpaces />
+ <RegExpr attribute="Doctype Name" context="#pop!Doctype Markupdecl" String="&name;" />
+ </context>
<context name="Doctype Markupdecl" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Doctype" context="#pop" char="&gt;" />
+ <DetectChar attribute="Doctype Symbols" context="#pop" char="&gt;" />
<DetectChar attribute="Value" context="Doctype Markupdecl DQ" char="&quot;" />
<DetectChar attribute="Value" context="Doctype Markupdecl SQ" char="&apos;" />
</context>
@@ -85,26 +117,31 @@
<IncludeRules context="FindPEntityRefs" />
</context>
+ <context name="ElementTagName" attribute="Other Text" lineEndContext="#pop!Element">
+ <StringDetect attribute="Element" context="#pop!Element" String="%1" dynamic="true" />
+ </context>
<context name="Element" attribute="Other Text" lineEndContext="#stay">
- <Detect2Chars attribute="Element" context="#pop" char="/" char1="&gt;" endRegion="element" />
- <DetectChar attribute="Element" context="El Content" char="&gt;" />
- <RegExpr attribute="Attribute" context="Attribute" String="^&name;" />
- <RegExpr attribute="Attribute" context="Attribute" String="\s+&name;" />
+ <Detect2Chars attribute="Element Symbols" context="#pop" char="/" char1="&gt;" endRegion="element" />
+ <DetectChar attribute="Element Symbols" context="El Content" char="&gt;" />
+ <RegExpr attribute="Attribute" context="Attribute" String="(?:^|\s+)&name;" />
<RegExpr attribute="Error" context="#stay" String="\S" />
</context>
<context name="El Content" attribute="Other Text" lineEndContext="#stay">
- <RegExpr attribute="Element" context="El End" String="&lt;/&name;" />
+ <RegExpr attribute="Element Symbols" context="El End TagName" String="&lt;/(?=(&name;))" />
<IncludeRules context="FindXML" />
</context>
+ <context name="El End TagName" attribute="Other Text" lineEndContext="#pop!El End">
+ <StringDetect attribute="Element" context="#pop!El End" String="%1" dynamic="true" />
+ </context>
<context name="El End" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Element" context="#pop#pop#pop" char="&gt;" endRegion="element" />
+ <DetectChar attribute="Element Symbols" context="#pop#pop#pop" char="&gt;" endRegion="element" />
<RegExpr attribute="Error" context="#stay" String="\S" />
</context>
<context name="Attribute" attribute="Other Text" lineEndContext="#stay">
- <DetectChar attribute="Attribute" context="Value" char="=" />
+ <DetectChar attribute="Attribute Separator" context="#pop!Value" char="=" />
<RegExpr attribute="Error" context="#stay" String="\S" />
</context>
@@ -115,35 +152,42 @@
</context>
<context name="Value DQ" attribute="Value" lineEndContext="#stay">
- <DetectChar attribute="Value" context="#pop#pop#pop" char="&quot;" />
+ <DetectChar attribute="Value" context="#pop#pop" char="&quot;" />
<IncludeRules context="FindEntityRefs" />
</context>
<context name="Value SQ" attribute="Value" lineEndContext="#stay">
- <DetectChar attribute="Value" context="#pop#pop#pop" char="&apos;" />
+ <DetectChar attribute="Value" context="#pop#pop" char="&apos;" />
<IncludeRules context="FindEntityRefs" />
</context>
</contexts>
<itemDatas>
- <itemData name="Normal Text" defStyleNum="dsNormal" />
- <itemData name="Other Text" defStyleNum="dsNormal" />
- <itemData name="Comment" defStyleNum="dsComment" spellChecking="false" />
- <itemData name="CDATA" defStyleNum="dsBaseN" bold="1" spellChecking="false" />
- <itemData name="Processing Instruction" defStyleNum="dsKeyword" spellChecking="false" />
- <itemData name="Doctype" defStyleNum="dsDataType" bold="1" spellChecking="false" />
- <itemData name="Element" defStyleNum="dsKeyword" spellChecking="false" />
- <itemData name="Attribute" defStyleNum="dsOthers" spellChecking="false" />
- <itemData name="Value" defStyleNum="dsString" spellChecking="false" />
- <itemData name="EntityRef" defStyleNum="dsDecVal" spellChecking="false" />
- <itemData name="PEntityRef" defStyleNum="dsDecVal" spellChecking="false" />
- <itemData name="Error" defStyleNum="dsError" spellChecking="false" />
+ <itemData name="Normal Text" defStyleNum="dsNormal" />
+ <itemData name="Other Text" defStyleNum="dsNormal" />
+ <itemData name="Comment" defStyleNum="dsComment" spellChecking="true" />
+ <itemData name="CDATA" defStyleNum="dsBaseN" bold="1" italic="0" spellChecking="false" />
+ <itemData name="CDATA Symbols" defStyleNum="dsBaseN" bold="0" italic="0" spellChecking="false" />
+ <itemData name="Processing Instruction" defStyleNum="dsFunction" bold="1" italic="0" spellChecking="false" />
+ <itemData name="PI Symbols" defStyleNum="dsFunction" bold="0" italic="0" spellChecking="false" />
+ <itemData name="Doctype" defStyleNum="dsDataType" bold="1" italic="0" spellChecking="false" />
+ <itemData name="Doctype Name" defStyleNum="dsDataType" bold="0" italic="0" spellChecking="false" />
+ <itemData name="Doctype Symbols" defStyleNum="dsDataType" bold="0" italic="0" spellChecking="false" />
+ <itemData name="Element" defStyleNum="dsKeyword" spellChecking="false" />
+ <itemData name="Element Symbols" defStyleNum="dsNormal" spellChecking="false" />
+ <itemData name="Attribute" defStyleNum="dsOthers" spellChecking="false" />
+ <itemData name="Attribute Separator" defStyleNum="dsOthers" spellChecking="false" />
+ <itemData name="Value" defStyleNum="dsString" spellChecking="false" />
+ <itemData name="EntityRef" defStyleNum="dsDecVal" spellChecking="false" />
+ <itemData name="PEntityRef" defStyleNum="dsDecVal" spellChecking="false" />
+ <itemData name="Error" defStyleNum="dsError" spellChecking="false" />
</itemDatas>
</highlighting>
<general>
<comments>
- <comment name="multiLine" start="&lt;!--" end="--&gt;" />
+ <comment name="multiLine" start="&lt;!--" end="--&gt;" region="comment" />
</comments>
</general>
</language>
+<!-- kate: replace-tabs on; tab-width 2; indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/yacc.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/yacc.xml
index 06d6492ff1..4129a09bd7 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/syntax/yacc.xml
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/yacc.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd">
+<!DOCTYPE language>
<!--
========================================================================
YACC.XML supports syntax highlighting for Yacc/Bison source under Kate.
@@ -12,12 +12,12 @@ This code is released under the LGPL as part of kdelibs/kate.
== UPDATE HISTORY ==
2018-02-20 // Nibaldo González <nibgonz@gmail.com>
- Fix '$' symbol, highlighted as 'dsError' by C++ (isocpp.xml).
+ Fix '$' symbol, highlighted as 'dsError' by C++ (isocpp.xml).
Update syntax for Bison (3.0.4):
- Add declarations, directives in rules and the '@' variable.
- - Allow a tag in '%union', declarations in multiple lines and
+ - Allow a tag in '%union', declarations in multiple lines and
grammar declarations in the grammar rules section.
- - The ';' char is not necessary to finish a rule. Allow '; |'
+ - The ';' char is not necessary to finish a rule. Allow '; |'
within rules.
Add mimetypes and extensions '*.ypp' & '*.y++'.
@@ -32,7 +32,7 @@ This code is released under the LGPL as part of kdelibs/kate.
========================================================================
-->
-<language name="Yacc/Bison" version="5" kateversion="5.0" section="Sources" extensions="*.y;*.yy;*.ypp;*.y++" mimetype="text/x-yacc;text/x-bison" priority="5" author="Jan Villat (jan.villat@net2000.ch)" license="LGPL">
+<language name="Yacc/Bison" version="9" kateversion="5.79" section="Sources" extensions="*.y;*.yy;*.ypp;*.y++" mimetype="text/x-yacc;text/x-bison" priority="5" author="Jan Villat (jan.villat@net2000.ch)" license="LGPL">
<highlighting>
<contexts>
@@ -62,7 +62,7 @@ This code is released under the LGPL as part of kdelibs/kate.
<WordDetect attribute="Directive" context="Percent Command In" String="%&lt;flag&gt;" />
<!-- Any word followed by '%' (End with ';' or '%') -->
<DetectChar attribute="Directive" context="Percent Command" char="%" />
- </context>
+ </context>
<context name="Grammar Declarations" attribute="Normal Text" lineEndContext="#stay">
<WordDetect attribute="Directive" context="Union Start" String="%union" />
<WordDetect attribute="Directive" context="Union Start" String="%code" />
@@ -74,7 +74,7 @@ This code is released under the LGPL as part of kdelibs/kate.
<IncludeRules context="Comment" />
<DetectSpaces />
<DetectChar attribute="Normal Text" context="Union In" char="{" beginRegion="union" />
- <RegExpr attribute="Normal Text" context="#pop!Union Tag" String="[^\s\{](?=(\s|$|//))" />
+ <RegExpr attribute="Normal Text" context="#pop!Union Tag" String="[^\s\{](?=\s|$|//)" />
</context>
<context name="Union Tag" attribute="Normal Text" lineEndContext="#stay">
<IncludeRules context="Comment" />
@@ -112,7 +112,7 @@ This code is released under the LGPL as part of kdelibs/kate.
<IncludeRules context="Symbol-Variable" />
<IncludeRules context="##C++" />
</context>
- <context name="Code-Symbols End" attribute="Normal Text" lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop!Percent Command In">
+ <context name="Code-Symbols End" attribute="Normal Text" lineEndContext="#stay" fallthroughContext="#pop!Percent Command In">
<IncludeRules context="Comment" />
<DetectSpaces />
<DetectChar attribute="Normal Text" context="#pop" char=";" lookAhead="true" />
@@ -139,13 +139,13 @@ This code is released under the LGPL as part of kdelibs/kate.
<WordDetect attribute="Directive" context="#stay" String="%merge" />
<!-- Finish rule without the ';' character (see the 'rhses.1' rule in the 'src/parse-gram.y' file, from the Bison source) -->
- <RegExpr attribute="Open Rule" context="#pop" String="[\w\-\.](?=[\w\-\.]*:)" column="0" endRegion="rule" />
+ <RegExpr attribute="Open Rule" context="#pop" String="^[\w\-\.](?=[\w\-\.]*:)" column="0" endRegion="rule" />
<Detect2Chars attribute="Content-Type Delimiter" context="#pop" char="%" char1="%" lookAhead="true" firstNonSpace="true" endRegion="rule" />
- <RegExpr attribute="Directive" context="#pop" String="%(union|code|destructor|printer|start|(no\-)?default\-prec|nterm|token|type|left|right|nonassoc|precedence)\b" lookAhead="true" column="0" endRegion="rule" />
+ <RegExpr attribute="Directive" context="#pop" String="^%(?:union|code|destructor|printer|start|(?:no\-)?default\-prec|nterm|token|type|left|right|nonassoc|precedence)\b" lookAhead="true" column="0" endRegion="rule" />
</context>
- <!-- The Bison parser allows to have ';' followed by '|', without the rule ending.
+ <!-- The Bison parser allows to have ';' followed by '|', without the rule ending.
The problem here is that the ';' char has endRegion="rule" (although it is not very relevant). -->
- <context name="Rule End" attribute="Normal Text" lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop#pop">
+ <context name="Rule End" attribute="Normal Text" lineEndContext="#stay" fallthroughContext="#pop#pop">
<DetectSpaces />
<DetectChar attribute="Normal Text" context="#stay" char=";" />
<DetectChar attribute="Normal Text" context="#pop" char="|" />
@@ -164,7 +164,7 @@ This code is released under the LGPL as part of kdelibs/kate.
<WordDetect attribute="Directive" context="Percent Command In" String="%left" />
<WordDetect attribute="Directive" context="Percent Command In" String="%right" />
<WordDetect attribute="Directive" context="Percent Command In" String="%nonassoc" />
- <WordDetect attribute="Directive" context="Percent Command In" String="%precedence" />
+ <WordDetect attribute="Directive" context="Percent Command In" String="%precedence" />
<DetectChar attribute="Rule" context="#pop" char="%" /> <!-- End when there is an invalid declaration -->
<DetectChar attribute="Normal Text" context="#pop" char=";" />
@@ -195,17 +195,17 @@ This code is released under the LGPL as part of kdelibs/kate.
</context>
<context name="Comment" attribute="Comment" lineEndContext="#stay">
- <Detect2Chars attribute="Comment" context="CommentStar" char="/" char1="*" />
+ <Detect2Chars attribute="Comment" context="CommentStar" char="/" char1="*" beginRegion="comment" />
<Detect2Chars attribute="Comment" context="CommentSlash" char="/" char1="/" />
</context>
<context name="CommentStar" attribute="Comment" lineEndContext="#stay">
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" />
- <IncludeRules context="##Alerts" />
- <IncludeRules context="##Modelines" />
+ <DetectSpaces />
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="comment" />
+ <IncludeRules context="##Comments" />
</context>
<context name="CommentSlash" attribute="Comment" lineEndContext="#pop">
- <IncludeRules context="##Alerts" />
- <IncludeRules context="##Modelines" />
+ <DetectSpaces />
+ <IncludeRules context="##Comments" />
</context>
<context name="StringOrChar" attribute="Normal Text" lineEndContext="#stay">
@@ -229,12 +229,12 @@ This code is released under the LGPL as part of kdelibs/kate.
</context>
<context name="Symbol-Variable" attribute="Normal Text" lineEndContext="#stay">
<DetectChar attribute="Directive" context="Dol" char="$" />
- <RegExpr attribute="Directive" context="#stay" String="@(\$?)(\d+|[A-Za-z_]\w*)?" />
+ <RegExpr attribute="Directive" context="#stay" String="@\$?(?:\d+|[A-Za-z_]\w*)?" />
</context>
- <context name="Dol" attribute="Normal Text" fallthrough="true" fallthroughContext="DolEnd" lineEndContext="#stay">
+ <context name="Dol" attribute="Normal Text" fallthroughContext="DolEnd" lineEndContext="#stay">
<RegExpr attribute="Data Type" context="DolEnd" String="&lt;[^&gt;]+&gt;" />
</context>
- <context name="DolEnd" attribute="Normal Text" lineEndContext="#stay" fallthrough="true" fallthroughContext="#pop#pop">
+ <context name="DolEnd" attribute="Normal Text" lineEndContext="#stay" fallthroughContext="#pop#pop">
<RegExpr attribute="Directive" context="#pop#pop" String="@?\d+" />
<DetectChar attribute="Directive" context="#pop#pop" char="$" />
<DetectIdentifier attribute="Directive" context="#pop#pop" />
@@ -259,7 +259,7 @@ This code is released under the LGPL as part of kdelibs/kate.
</highlighting>
<general>
<comments>
- <comment name="multiLine" start="/*" end="*/" />
+ <comment name="multiLine" start="/*" end="*/" region="comment" />
<comment name="singleLine" start="//" />
</comments>
</general>
diff --git a/src/libs/3rdparty/syntax-highlighting/data/syntax/yaml.xml b/src/libs/3rdparty/syntax-highlighting/data/syntax/yaml.xml
new file mode 100644
index 0000000000..120dbd55f7
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/syntax/yaml.xml
@@ -0,0 +1,642 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language
+[
+ <!ENTITY null "(?:null|Null|NULL|~)">
+ <!ENTITY bool "(?:y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)">
+
+ <!ENTITY int "(?:0|[\-\+]?[1-9][0-9_]*)">
+ <!ENTITY intOther "[\-\+]?0(?:x_*[0-9a-fA-F][0-9a-fA-F_]*|o?_*[0-7][0-7_]*|b_*[01][01_]*)"> <!-- Hex, Octal, Binary -->
+ <!ENTITY intBase60 "[\-\+]?[1-9][0-9_]*(?:\:[0-5]?[0-9])+">
+ <!ENTITY allInt "(?:&intBase60;|&intOther;|&int;)">
+
+ <!ENTITY float "[\-\+]?(?:[0-9][0-9_]*\.[0-9\._]*|\._*[0-9][0-9\._]*)(?:[eE][\-\+]?[0-9]+)?">
+ <!ENTITY floatExp "[\-\+]?[0-9][0-9_]*[eE][\-\+]?[0-9]+">
+ <!ENTITY floatBase60 "[\-\+]?[0-9][0-9_]*(?:\:[0-5]?[0-9])+\.[0-9_]*">
+ <!ENTITY inf "[\-\+]?\.(?:inf|Inf|INF)\b">
+ <!ENTITY nan "\.(?:nan|NaN|NAN)\b">
+ <!ENTITY allFloat "(?:&float;|&floatExp;|&floatBase60;|&inf;|&nan;)">
+
+ <!ENTITY endValue "(?:\s*$|\s+#)">
+ <!ENTITY endValueInline "\s*[:,\[\]\{\}]">
+ <!ENTITY space "[ ]">
+
+ <!-- Key quoted -->
+ <!ENTITY keyDQ "&quot;(?:\\.|[^&quot;])+&quot;\s*">
+ <!ENTITY keySQ "'(?:[^']|'')+'\s*">
+ <!-- Literal/folded operator -->
+ <!ENTITY literalOp "[\|&gt;][\-\+]?">
+ <!-- Key after "?" or "-", used to detect literal/folded operator -->
+ <!ENTITY keyAfterOp "(?:[^&quot;'#\-\?\s][^:#]*|\-(?:[^\s:#][^:#]*)?|&keyDQ;|&keySQ;)">
+
+ <!ENTITY dataTypes "!!\S+">
+ <!ENTITY alias "&amp;\S+">
+ <!ENTITY reference "\*\S+">
+
+ <!ENTITY dpointsHashAttrPreInline1 "[^\s&quot;'#\-,\}\s][^:#,\}]*(?=\:(?:\s|$))">
+ <!ENTITY dpointsHashAttrPreInline2 "\-(?:[^\s:#,\}][^:#,\}]*)?(?=\:(?:\s|$))">
+ <!ENTITY dpointsHashAttrPreInline3 "&keyDQ;(?=\:(?:\s|$))">
+ <!ENTITY dpointsHashAttrPreInline4 "&keySQ;(?=\:(?:\s|$))">
+
+ <!ENTITY dpointsListAttrPreInline1 "[^&quot;'#\-,\]\s][^:#,\]]*(?=\:(?:\s|$))">
+ <!ENTITY dpointsListAttrPreInline2 "\-(?:[^\s:#,\]][^:#,\]]*)?(?=\:(?:\s|$))">
+ <!ENTITY dpointsListAttrPreInline3 "&keyDQ;(?=\:(?:\s|$))">
+ <!ENTITY dpointsListAttrPreInline4 "&keySQ;(?=\:(?:\s|$))">
+
+ <!ENTITY dpointsAttrPre1 "[^&quot;'#\-\s][^:#]*(?=\:(?:\s|$))">
+ <!ENTITY dpointsAttrPre2 "\-(?:[^\s:#][^:#]*)?(?=\:(?:\s|$))">
+ <!ENTITY dpointsAttrPre3 "&keyDQ;(?=\:(?:\s|$))">
+ <!ENTITY dpointsAttrPre4 "&keySQ;(?=\:(?:\s|$))">
+
+]>
+
+<!-- Author: Dr Orlovsky MA <maxim@orlovsky.info> //-->
+<!-- Modifications (YAML 1.2), values & support for literal/folded style:
+ Nibaldo González S. <nibgonz@gmail.com>
+ These modifications are under the MIT license. //-->
+<language name="YAML" version="11" kateversion="5.0" section="Markup"
+ extensions="*.yaml;*.yml" mimetype="text/yaml" priority="9"
+ author="Dr Orlovsky MA (dr.orlovsky@gmail.com), Nibaldo González (nibgonz@gmail.com)" license="LGPL">
+ <highlighting>
+ <contexts>
+ <context attribute="Attribute" lineEndContext="#stay" name="normal" >
+ <StringDetect attribute="Document Header" context="header" String="---" column="0"/>
+ <RegExpr attribute="End of Document" context="EOD" String="^\.\.\.$" column="0"/>
+ <DetectChar attribute="Directive" context="directive" char="%" column="0"/>
+
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+
+ <!-- Literal/Folded Style -->
+ <IncludeRules context="find-literal-block" />
+
+ <RegExpr attribute="Operator" firstNonSpace="true" context="dash" String="\-(?=\s|$)" />
+ <DetectChar attribute="Operator" firstNonSpace="true" context="mapping-key" char="?" />
+
+ <DetectChar attribute="Operator" firstNonSpace="true" context="list" char="[" beginRegion="List" />
+ <DetectChar attribute="Operator" firstNonSpace="true" context="hash" char="{" beginRegion="Hash" />
+
+ <RegExpr attribute="Data Types" firstNonSpace="true" context="after-data" String="&dataTypes;" />
+ <RegExpr attribute="Alias" firstNonSpace="true" context="after-data" String="&alias;" />
+ <RegExpr attribute="Reference" firstNonSpace="true" context="after-data" String="&reference;" />
+
+ <RegExpr attribute="Key" context="dpoints-attribute-pre" String="&dpointsAttrPre1;|&dpointsAttrPre2;|&dpointsAttrPre3;|&dpointsAttrPre4;"/>
+ <RegExpr attribute="Key Points Operator" context="attribute-pre" String=":(?=\s|$)"/>
+
+ <DetectChar attribute="String" firstNonSpace="true" context="string" char="'" beginRegion="String" />
+ <DetectChar attribute="String" firstNonSpace="true" context="stringx" char="&quot;" beginRegion="String" />
+ <IncludeRules context="values-firstnonspace" />
+ <DetectSpaces/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="mapping-key" fallthrough="true" fallthroughContext="#pop">
+ <RegExpr attribute="Comment" context="#pop!comment" String="(?:^|\s+)#" />
+ <DetectSpaces />
+ <RegExpr attribute="Operator" context="#pop!dash" String="\-(?=\s|$)" />
+ <RegExpr attribute="Data Types" context="#pop!after-data" String="&dataTypes;" />
+ <RegExpr attribute="Alias" context="#pop!after-data" String="&alias;" />
+ <RegExpr attribute="Reference" context="#pop!after-data" String="&reference;" />
+
+ <DetectChar attribute="Operator" context="#pop!list" char="[" beginRegion="List" />
+ <DetectChar attribute="Operator" context="#pop!hash" char="{" beginRegion="Hash" />
+ <DetectChar attribute="String" context="#pop!string" char="'" beginRegion="String" />
+ <DetectChar attribute="String" context="#pop!stringx" char="&quot;" beginRegion="String" />
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="dash" fallthrough="true" fallthroughContext="#pop">
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ <DetectSpaces/>
+ <RegExpr attribute="Data Types" context="#stay" String="&dataTypes;" />
+ <RegExpr attribute="Alias" context="#stay" String="&alias;" />
+ <RegExpr attribute="Reference" context="#stay" String="&reference;" />
+ <IncludeRules context="values" />
+ <DetectChar attribute="Operator" context="#pop!mapping-key" char="?" />
+ <RegExpr attribute="Operator" context="#stay" String="\-(?=\s|$)" />
+
+ <DetectChar attribute="Operator" context="#pop!list" char="[" beginRegion="List" />
+ <DetectChar attribute="Operator" context="#pop!hash" char="{" beginRegion="Hash" />
+ <DetectChar attribute="String" context="#pop!string" char="'" beginRegion="String" />
+ <DetectChar attribute="String" context="#pop!stringx" char="&quot;" beginRegion="String" />
+ </context>
+
+ <!-- Highlight lists, hashes and strings after a data type, reference or alias -->
+ <context attribute="Normal Text" lineEndContext="#pop" name="after-data" fallthrough="true" fallthroughContext="#pop">
+ <RegExpr attribute="Comment" context="#pop!comment" String="(?:^|\s+)#" />
+ <DetectSpaces />
+ <RegExpr attribute="Data Types" context="#stay" String="&dataTypes;" />
+ <RegExpr attribute="Alias" context="#stay" String="&alias;" />
+ <RegExpr attribute="Reference" context="#stay" String="&reference;" />
+
+ <DetectChar attribute="Operator" context="list" char="[" beginRegion="List" />
+ <DetectChar attribute="Operator" context="hash" char="{" beginRegion="Hash" />
+ <DetectChar attribute="String" context="string" char="'" beginRegion="String" />
+ <DetectChar attribute="String" context="stringx" char="&quot;" beginRegion="String" />
+ </context>
+
+ <context attribute="Document Header" lineEndContext="#pop" name="header">
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ <RegExpr attribute="Literal/Folded Operator" context="header-literal-operator" String="\s&literalOp;(?=&endValue;)" lookAhead="true" />
+ </context>
+ <context attribute="Document Header" lineEndContext="#pop#pop" name="header-literal-operator" fallthrough="true" fallthroughContext="#pop">
+ <DetectSpaces />
+ <RegExpr attribute="Literal/Folded Operator" context="#pop#pop!literal-block-simple" String="&literalOp;" beginRegion="Literal" />
+ </context>
+
+ <context attribute="End of Document" lineEndContext="#stay" name="EOD">
+ </context>
+
+ <context attribute="Directive" lineEndContext="#pop" name="directive">
+ </context>
+
+ <context attribute="Attribute" lineEndContext="#pop#pop" name="attribute">
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ </context>
+
+ <context attribute="Attribute" lineEndContext="#stay" name="list-attribute-inline">
+ <AnyChar attribute="Operator" context="#pop#pop" lookAhead="true" String=",]" />
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ </context>
+ <context attribute="Attribute" lineEndContext="#stay" name="hash-attribute-inline">
+ <AnyChar attribute="Operator" context="#pop#pop" lookAhead="true" String=",}" />
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ </context>
+
+ <!-- Attribute -->
+ <context attribute="Attribute" lineEndContext="#pop" name="dpoints-attribute-pre" fallthrough="true" fallthroughContext="#pop!attribute-pre">
+ <DetectChar attribute="Key Points Operator" context="#pop!attribute-pre" char=":" /> <!-- Highlight two points after Key -->
+ </context>
+ <context attribute="Attribute" lineEndContext="#pop" name="attribute-pre" fallthrough="true" fallthroughContext="attribute">
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ <DetectSpaces/>
+ <DetectChar attribute="Operator" context="#stay" char="?" />
+ <RegExpr attribute="Data Types" context="#stay" String="&dataTypes;" />
+ <DetectChar attribute="Operator" context="list" char="[" beginRegion="List" />
+ <DetectChar attribute="Operator" context="hash" char="{" beginRegion="Hash" />
+ <DetectChar attribute="String" context="attribute-string" char="'" beginRegion="String" />
+ <DetectChar attribute="String" context="attribute-stringx" char="&quot;" beginRegion="String" />
+ <RegExpr attribute="Alias" context="#stay" String="&alias;(?=\s+[\[\{])" />
+ <RegExpr attribute="Reference" context="#stay" String="&reference;(?=\s+[\[\{])" />
+ <RegExpr attribute="Alias" context="attribute" String="&alias;" />
+ <RegExpr attribute="Reference" context="attribute" String="&reference;" />
+ <IncludeRules context="values" />
+ <RegExpr attribute="Literal/Folded Operator" context="#stay" String="&literalOp;(?=&endValue;)" />
+ </context>
+
+ <context attribute="Attribute" lineEndContext="#pop" name="default-attribute-pre-inline">
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ <DetectSpaces/>
+
+ <DetectChar attribute="Operator" context="#stay" char="?" />
+ <RegExpr attribute="Data Types" context="#stay" String="&dataTypes;" />
+ <DetectChar attribute="Operator" context="list" char="[" beginRegion="List" />
+ <DetectChar attribute="Operator" context="hash" char="{" beginRegion="Hash" />
+ <DetectChar attribute="String" context="attribute-string-inline" char="'" beginRegion="String" />
+ <DetectChar attribute="String" context="attribute-stringx-inline" char="&quot;" beginRegion="String" />
+ <RegExpr attribute="Alias" context="#stay" String="&alias;(?=\s+[\[\{])" />
+ <RegExpr attribute="Reference" context="#stay" String="&reference;(?=\s+[\[\{])" />
+ </context>
+
+ <!-- Attribute Inline, Within List -->
+ <context attribute="Attribute" lineEndContext="#pop" name="dpoints-list-attribute-pre-inline" fallthrough="true" fallthroughContext="#pop!list-attribute-pre-inline">
+ <DetectChar attribute="Key Points Operator" context="#pop!list-attribute-pre-inline" char=":" /> <!-- Highlight two points after Key -->
+ </context>
+ <context attribute="Attribute" lineEndContext="#pop" name="list-attribute-pre-inline" fallthrough="true" fallthroughContext="list-attribute-inline">
+ <IncludeRules context="default-attribute-pre-inline" />
+ <RegExpr attribute="Alias" context="list-attribute-inline" String="&alias;" />
+ <RegExpr attribute="Reference" context="list-attribute-inline" String="&reference;" />
+
+ <AnyChar attribute="Operator" context="#pop" lookAhead="true" String=",]" />
+ <IncludeRules context="values-inline" />
+ </context>
+
+ <!-- Attribute Inline, Within Hash -->
+ <context attribute="Attribute" lineEndContext="#pop" name="dpoints-hash-attribute-pre-inline" fallthrough="true" fallthroughContext="#pop!hash-attribute-pre-inline">
+ <DetectChar attribute="Key Points Operator" context="#pop!hash-attribute-pre-inline" char=":" /> <!-- Highlight two points after Key -->
+ </context>
+ <context attribute="Attribute" lineEndContext="#pop" name="hash-attribute-pre-inline" fallthrough="true" fallthroughContext="hash-attribute-inline">
+ <IncludeRules context="default-attribute-pre-inline" />
+ <RegExpr attribute="Alias" context="hash-attribute-inline" String="&alias;" />
+ <RegExpr attribute="Reference" context="hash-attribute-inline" String="&reference;" />
+
+ <AnyChar attribute="Operator" context="#pop" lookAhead="true" String=",}" />
+ <IncludeRules context="values-inline" />
+ </context>
+
+ <!-- List -->
+ <!-- Context "find-values-list" highlights values and then sends to "list-element" -->
+ <context attribute="List" lineEndContext="#stay" name="list" fallthrough="true" fallthroughContext="#pop!find-values-list" noIndentationBasedFolding="true">
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ <DetectSpaces />
+ <DetectChar attribute="Operator" context="#pop!find-values-list" char="?" />
+ </context>
+ <context attribute="List" lineEndContext="#stay" name="list-element" noIndentationBasedFolding="true">
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+
+ <DetectChar attribute="Operator" context="#pop" char="]" endRegion="List" />
+ <DetectChar attribute="Operator" context="list" char="[" beginRegion="List" />
+ <DetectChar attribute="Operator" context="hash" char="{" beginRegion="Hash" />
+
+ <RegExpr attribute="Key" context="dpoints-list-attribute-pre-inline" String="&dpointsListAttrPreInline1;|&dpointsListAttrPreInline2;|&dpointsListAttrPreInline3;|&dpointsListAttrPreInline4;"/>
+ <RegExpr attribute="Key Points Operator" context="list-attribute-pre-inline" String=":(?=\s|$)" firstNonSpace="true" />
+
+ <RegExpr attribute="Data Types" context="#stay" String="&dataTypes;" />
+ <RegExpr attribute="Alias" context="#stay" String="&alias;" />
+ <RegExpr attribute="Reference" context="#stay" String="&reference;" />
+ <DetectChar attribute="String" context="string" char="'" beginRegion="String" />
+ <DetectChar attribute="String" context="stringx" char="&quot;" beginRegion="String" />
+
+ <DetectChar attribute="Operator" context="#pop!list" char="," />
+ <IncludeRules context="values-list" />
+ </context>
+
+ <!-- Hash -->
+ <context attribute="Hash" lineEndContext="#stay" name="hash" fallthrough="true" fallthroughContext="#pop!hash-element" noIndentationBasedFolding="true">
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ <DetectSpaces />
+ <DetectChar attribute="Operator" context="#pop!hash-element" char="?" />
+ </context>
+ <context attribute="Hash" lineEndContext="#stay" name="hash-element" noIndentationBasedFolding="true">
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ <DetectSpaces/>
+
+ <RegExpr attribute="Key" context="dpoints-hash-attribute-pre-inline" String="&dpointsHashAttrPreInline1;|&dpointsHashAttrPreInline2;|&dpointsHashAttrPreInline3;|&dpointsHashAttrPreInline4;"/>
+ <RegExpr attribute="Key Points Operator" context="hash-attribute-pre-inline" String=":(?=\s|$)"/>
+
+ <DetectChar attribute="Operator" context="#pop" char="}" endRegion="Hash" />
+ <DetectChar attribute="Operator" context="#pop!hash" char="," />
+
+ <!-- This improves highlighting in keys with multiple lines -->
+ <RegExpr attribute="Data Types" context="#stay" String="&dataTypes;" />
+ <RegExpr attribute="Alias" context="#stay" String="&alias;" />
+ <RegExpr attribute="Reference" context="#stay" String="&reference;" />
+ <DetectChar attribute="String" context="string" char="'" beginRegion="String" />
+ <DetectChar attribute="String" context="stringx" char="&quot;" beginRegion="String" />
+ </context>
+
+ <!-- Strings -->
+ <context attribute="String" lineEndContext="#stay" name="attribute-string" noIndentationBasedFolding="true">
+ <DetectIdentifier />
+ <IncludeRules context="escaped-char-singleq" />
+ <DetectChar attribute="String" context="attribute-end" char="'" endRegion="String" />
+ </context>
+
+ <context attribute="String" lineEndContext="#stay" name="attribute-stringx" noIndentationBasedFolding="true">
+ <DetectIdentifier />
+ <IncludeRules context="escaped-char-doubleq" />
+ <DetectChar attribute="String" context="attribute-end" char="&quot;" endRegion="String" />
+ </context>
+
+ <context attribute="String" lineEndContext="#stay" name="attribute-string-inline" noIndentationBasedFolding="true">
+ <DetectIdentifier />
+ <IncludeRules context="escaped-char-singleq" />
+ <DetectChar attribute="String" context="attribute-end-inline" char="'" endRegion="String" />
+ </context>
+
+ <context attribute="String" lineEndContext="#stay" name="attribute-stringx-inline" noIndentationBasedFolding="true">
+ <DetectIdentifier />
+ <IncludeRules context="escaped-char-doubleq" />
+ <DetectChar attribute="String" context="attribute-end-inline" char="&quot;" endRegion="String" />
+ </context>
+
+ <context attribute="Error" lineEndContext="#pop#pop#pop" name="attribute-end">
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ </context>
+
+ <context attribute="Error" lineEndContext="#pop#pop#pop" name="attribute-end-inline">
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s+)#" />
+ <DetectSpaces attribute="Normal Text" context="#stay"/>
+ <AnyChar context="#pop#pop#pop" lookAhead="true" String="}],"/>
+ </context>
+
+ <context attribute="String" lineEndContext="#stay" name="string" noIndentationBasedFolding="true">
+ <DetectIdentifier />
+ <IncludeRules context="escaped-char-singleq" />
+ <DetectChar attribute="String" context="#pop" char="'" endRegion="String" />
+ </context>
+
+ <context attribute="String" lineEndContext="#stay" name="stringx" noIndentationBasedFolding="true">
+ <DetectIdentifier />
+ <IncludeRules context="escaped-char-doubleq" />
+ <DetectChar attribute="String" context="#pop" char="&quot;" endRegion="String" />
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="escaped-char-doubleq">
+ <RegExpr attribute="Escaped Character" context="#stay" String="\\(?:[\s0abtnvfre&quot;/\\N_Lp]|x[a-fA-F0-9]{2}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{8})"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="escaped-char-singleq">
+ <Detect2Chars attribute="Escaped Character" context="#stay" char="'" char1="'" />
+ </context>
+
+ <context attribute="Comment" lineEndContext="#pop" name="comment">
+ <DetectSpaces />
+ <IncludeRules context="##Comments" />
+ </context>
+
+ <!-- Values -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="values">
+ <RegExpr attribute="Null" context="#stay" String="&null;(?=&endValue;)"/>
+ <RegExpr attribute="Boolean" context="#stay" String="&bool;(?=&endValue;)"/>
+ <RegExpr attribute="Float" context="#stay" String="&allFloat;(?=&endValue;)"/>
+ <RegExpr attribute="Integer" context="#stay" String="&allInt;(?=&endValue;)"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="values-firstnonspace">
+ <RegExpr attribute="Null" firstNonSpace="true" context="#stay" String="&null;(?=&endValue;)"/>
+ <RegExpr attribute="Boolean" firstNonSpace="true" context="#stay" String="&bool;(?=&endValue;)"/>
+ <RegExpr attribute="Float" firstNonSpace="true" context="#stay" String="&allFloat;(?=&endValue;)"/>
+ <RegExpr attribute="Integer" firstNonSpace="true" context="#stay" String="&allInt;(?=&endValue;)"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="values-inline">
+ <RegExpr attribute="Null" context="#stay" String="&null;(?=&endValueInline;|&endValue;)"/>
+ <RegExpr attribute="Boolean" context="#stay" String="&bool;(?=&endValueInline;|&endValue;)"/>
+ <RegExpr attribute="Float" context="#stay" String="&allFloat;(?=&endValueInline;|&endValue;)"/>
+ <RegExpr attribute="Integer" context="#stay" String="&allInt;(?=&endValueInline;|&endValue;)"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="values-list">
+ <RegExpr attribute="Null" context="#stay" String="(?:\s|^)&null;(?=&endValueInline;|&endValue;)"/>
+ <RegExpr attribute="Boolean" context="#stay" String="(?:\s|^)&bool;(?=&endValueInline;|&endValue;)"/>
+ <RegExpr attribute="Float" context="#stay" String="(?:\s|^)&allFloat;(?=&endValueInline;|&endValue;)"/>
+ <RegExpr attribute="Integer" context="#stay" String="(?:\s|^)&allInt;(?=&endValueInline;|&endValue;)"/>
+ </context>
+ <!-- If the value is found immediately at the beginning of the list item -->
+ <context attribute="Normal Text" lineEndContext="#pop!list-element" name="find-values-list" fallthrough="true" fallthroughContext="#pop!list-element">
+ <RegExpr attribute="Null" context="#pop!list-element" String="&null;(?=&endValueInline;|&endValue;)"/>
+ <RegExpr attribute="Boolean" context="#pop!list-element" String="&bool;(?=&endValueInline;|&endValue;)"/>
+ <RegExpr attribute="Float" context="#pop!list-element" String="&allFloat;(?=&endValueInline;|&endValue;)"/>
+ <RegExpr attribute="Integer" context="#pop!list-element" String="&allInt;(?=&endValueInline;|&endValue;)"/>
+ </context>
+
+ <!-- Literal/Folded Style: http://yaml.org/spec/1.2/spec.html#id2795688 -->
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="find-literal-block">
+ <!-- Do not allow indentation with tabs: -->
+ <RegExpr attribute="Alert" context="#stay" column="0"
+ String="^&space;*\t+\s*(?=(?:(?:&keyDQ;|&keySQ;|[^#])*[^#\w\|&lt;&gt;&quot;'])?&literalOp;&endValue;)" />
+
+ <!-- CASE 1: The literal/folded operator is the first character of a line.
+ The text after a space is considered literal.
+ Ex:
+ > |
+ > ^Start the literal text
+ -->
+ <RegExpr attribute="Literal/Folded Operator" context="literal-block-simple" column="0"
+ String="^&literalOp;(?=&endValue;)" beginRegion="Literal" />
+
+ <!-- CASE 2: Only the literal/folded operator is present in a line, after a space (the indentation
+ is captured). The text with the same indentation of the operator will be highlighted as literal.
+ Ex:
+ > key:
+ > |
+ > ^Start the literal text
+
+ However, in this case, the correct way is to use the indentation of the block, not the
+ indentation of the the operator. The problem is that it is difficult to capture.
+ > key1:
+ > key2:
+ > key3:
+ > |
+ > ^Block indentation (correct literal text)
+ -->
+ <RegExpr attribute="Literal/Folded Operator" context="literal-block-only-operator" column="0"
+ String="^(&space;+)&literalOp;(?=&endValue;)" beginRegion="Literal" />
+
+ <!-- CASE 3: There is a Key before the literal/folded operator (Key indentation is captured).
+ The text with the Key's indentation plus a space is considered literal.
+ Ex:
+ > key: |
+ > ^Start the literal text
+ > key: !!type >-
+ > ^Start the folded text
+ -->
+ <RegExpr attribute="Key Points Operator" context="literal-block-key" column="0"
+ String="^(&space;*)\:(?=\s+(?:(?:&keyDQ;|&keySQ;|[^#])*[^#\w\|&lt;&gt;&quot;'])?&literalOp;&endValue;)" />
+ <RegExpr attribute="Key" context="literal-block-key" column="0"
+ String="^(&space;*)(?:[^&quot;'#\-\?\s][^:#]*|\-(?:[^\s:#][^:#]*)?|&keyDQ;|&keySQ;)(?=\:\s+(?:(?:&keyDQ;|&keySQ;|[^#])*[^#\w\|&lt;&gt;&quot;'])?&literalOp;&endValue;)" />
+
+ <!-- CASE 4: Is there an operator "?" or "-" at the beginning of the line.
+ NOTE: Nested characters "-" and "?" are considered as part of the indentation.
+ Therefore, the indentation of the Key or the last operator "?" or "-" is captured.
+ Ex:
+ > ? |
+ > ^Start the literal Text
+ > ? - - |
+ > ^Start the literal text
+ > - Key: |
+ > ^Start the literal text
+ > ? - - - - Key: |
+ > ^Start the literal text
+ -->
+ <RegExpr context="start-literal-block-withdash" lookAhead="true" column="0"
+ String="^&space;*(?:\?&space;*|\-&space;+){1,6}(?:(?:&keyDQ;|&keySQ;|[^#\-\?\s]|\-[^\s#])(?:(?:&keyDQ;|&keySQ;|[^#])*[^#\w\|&lt;&gt;&quot;'])?)?&literalOp;&endValue;" />
+
+ <!-- CASE 5: Literal/folded operator after a data type or other content.
+ Ex:
+ > !!type |
+ > ^Start the literal text
+ > key1:
+ > key2:
+ > !!type |
+ > ^Start the literal text
+ -->
+ <RegExpr context="start-literal-block-other" lookAhead="true" column="0"
+ String="^&space;*(?:(?:[&amp;\*]|!!)\S+\s+)+&literalOp;&endValue;" />
+ </context>
+
+ <!-- If the line with the literal operator starts with the "-" or "?" operator.
+ NOTE: The indentation capture is limited to 6 nested operators. -->
+ <context attribute="Normal Text" lineEndContext="#pop" name="start-literal-block-withdash" noIndentationBasedFolding="true">
+ <!-- With Key: Capture the Key indentation -->
+ <RegExpr attribute="Operator" context="#pop!literal-block-key-withdash-s2" String="^(&space;*)[\?\-](&space;*)(?=&keyAfterOp;:\s)" column="0"/>
+ <RegExpr attribute="Operator" context="#pop!literal-block-key-withdash-s3" String="^(&space;*)[\?\-](&space;*)[\?\-](&space;*)(?=&keyAfterOp;:\s)" column="0"/>
+ <RegExpr attribute="Operator" context="#pop!literal-block-key-withdash-s4" String="^(&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)(?=&keyAfterOp;:\s)" column="0"/>
+ <RegExpr attribute="Operator" context="#pop!literal-block-key-withdash-s5" String="^(&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)(?=&keyAfterOp;:\s)" column="0"/>
+ <RegExpr attribute="Operator" context="#pop!literal-block-key-withdash-s6" String="^(&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)(?=&keyAfterOp;:\s)" column="0"/>
+ <RegExpr attribute="Operator" context="#pop!literal-block-key-withdash-s7" String="^(&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)(?=&keyAfterOp;:\s)" column="0"/>
+ <!-- Without Key: Capture the indentation of the last operator "?" or "-" -->
+ <RegExpr attribute="Operator" context="#pop!literal-block-withdash-s1" String="^(&space;*)[\?\-]\s*(?=[^#\-\?\s]|\-[^\s#])" column="0"/>
+ <RegExpr attribute="Operator" context="#pop!literal-block-withdash-s2" String="^(&space;*)[\?\-](&space;*)[\?\-]\s*(?=[^#\-\?\s]|\-[^\s#])" column="0"/>
+ <RegExpr attribute="Operator" context="#pop!literal-block-withdash-s3" String="^(&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-]\s*(?=[^#\-\?\s]|\-[^\s#])" column="0"/>
+ <RegExpr attribute="Operator" context="#pop!literal-block-withdash-s4" String="^(&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-]\s*(?=[^#\-\?\s]|\-[^\s#])" column="0"/>
+ <RegExpr attribute="Operator" context="#pop!literal-block-withdash-s5" String="^(&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-]\s*(?=[^#\-\?\s]|\-[^\s#])" column="0"/>
+ <RegExpr attribute="Operator" context="#pop!literal-block-withdash-s6" String="^(&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-](&space;*)[\?\-]\s*(?=[^#\-\?\s]|\-[^\s#])" column="0"/>
+ </context>
+ <!-- Capture the indentation of data type, reference or alias -->
+ <context attribute="Normal Text" lineEndContext="#pop" name="start-literal-block-other" noIndentationBasedFolding="true">
+ <!-- The text with the same indentation will be considered literal -->
+ <RegExpr attribute="Data Types" context="#pop!literal-block-after-data" String="^(&space;+)&dataTypes;" column="0" />
+ <RegExpr attribute="Alias" context="#pop!literal-block-after-data" String="^(&space;+)&alias;" column="0" />
+ <RegExpr attribute="Reference" context="#pop!literal-block-after-data" String="^(&space;+)&reference;" column="0" />
+ <!-- The text after a space will be considered literal (empty text is captured) -->
+ <RegExpr attribute="Data Types" context="#pop!literal-block-withdash-s1" String="^()&dataTypes;" column="0" />
+ <RegExpr attribute="Alias" context="#pop!literal-block-withdash-s1" String="^()&alias;" column="0" />
+ <RegExpr attribute="Reference" context="#pop!literal-block-withdash-s1" String="^()&reference;" column="0" />
+ </context>
+
+ <!-- Highlight data/attribute before the literal operator (Note that if there is a line
+ break within a string or bracket, the literal line will not be highlighted). -->
+ <context attribute="Attribute" lineEndContext="#pop#pop" name="before-literal-operator" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Operator" context="#pop!end-literal-operator" String="&literalOp;(?=&endValue;)" beginRegion="Literal" />
+
+ <RegExpr attribute="Error" context="#pop#pop" String="(?:[&amp;\*]|!!)\S*&literalOp;(?=&endValue;)" />
+ <RegExpr attribute="Data Types" context="#stay" String="&dataTypes;" />
+ <RegExpr attribute="Alias" context="#stay" String="&alias;" />
+ <RegExpr attribute="Reference" context="#stay" String="&reference;" />
+
+ <DetectChar attribute="Operator" context="list" char="[" beginRegion="List" />
+ <DetectChar attribute="Operator" context="hash" char="{" beginRegion="Hash" />
+ <DetectChar attribute="String" context="string" char="'" beginRegion="String" />
+ <DetectChar attribute="String" context="stringx" char="&quot;" beginRegion="String" />
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop#pop" name="dpoints-key-before-literal-operator" fallthrough="true" fallthroughContext="#pop#pop" noIndentationBasedFolding="true">
+ <DetectChar attribute="Key Points Operator" context="#pop!key-before-literal-operator" char=":" />
+ </context>
+ <context attribute="Attribute" lineEndContext="#pop#pop" name="key-before-literal-operator" noIndentationBasedFolding="true">
+ <IncludeRules context="before-literal-operator" />
+ <DetectChar attribute="Operator" context="#stay" char="?" />
+ </context>
+ <context attribute="Attribute" lineEndContext="#pop" name="end-literal-operator" noIndentationBasedFolding="true">
+ <RegExpr attribute="Comment" context="#pop!comment" String="(?:^|\s+)#" />
+ </context>
+
+ <!-- Common rules for the content of the literal blocks -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-default" noIndentationBasedFolding="true">
+ <!-- End literal/folded block -->
+ <RegExpr attribute="Normal Text" context="#pop" String="^\s*\S" lookAhead="true" column="0" endRegion="Literal" />
+ <!-- Find literal/folded operator -->
+ <RegExpr context="before-literal-operator" String="\S" lookAhead="true" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="literal-block-key-default" noIndentationBasedFolding="true">
+ <!-- End literal/folded block -->
+ <RegExpr attribute="Normal Text" context="#pop" String="^\s*\S" lookAhead="true" column="0" endRegion="Literal" />
+ <!-- Detect Key before the literal/folded operator -->
+ <RegExpr attribute="Key" context="dpoints-key-before-literal-operator" String="&keyAfterOp;(?=:\s)" />
+ <RegExpr attribute="Normal Text" context="#pop" String="\S" lookAhead="true" endRegion="Literal" />
+ </context>
+
+ <!-- Content of the literal block: -->
+
+ <!-- If the literal operator is starting the line (after a space, use block indentation) -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-only-operator" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1.*$" dynamic="true" column="0" />
+
+ <RegExpr attribute="Normal Text" context="#pop" String="^\s*\S" lookAhead="true" column="0" endRegion="Literal" />
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s)#" />
+ <RegExpr context="#pop" String="\S" lookAhead="true" endRegion="Literal" />
+ </context>
+ <!-- If the literal operator is the first character of a line (or after header) -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-simple" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^\s.*$" column="0" />
+
+ <RegExpr attribute="Normal Text" context="#pop" String="^\s*\S" lookAhead="true" column="0" endRegion="Literal" />
+ <RegExpr attribute="Comment" context="comment" String="(?:^|\s)#" />
+ </context>
+ <!-- If there is a data type or other content before the liretal operator (use block indentation) -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-after-data" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1.*$" dynamic="true" column="0" />
+
+ <RegExpr attribute="Normal Text" context="#pop" String="^\s*\S" lookAhead="true" column="0" endRegion="Literal" />
+ <RegExpr context="before-literal-operator" String="\S" lookAhead="true" />
+ </context>
+ <!-- If there is a key before the literal operator -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-key" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1\s.*$" dynamic="true" column="0" />
+
+ <RegExpr attribute="Normal Text" context="#pop" String="^\s*\S" lookAhead="true" column="0" endRegion="Literal" />
+ <!-- Attribute of the Key (the Key was previously highlighted) -->
+ <RegExpr attribute="Key Points Operator" context="key-before-literal-operator" String=":\s" />
+ <RegExpr context="key-before-literal-operator" String="\S" lookAhead="true" />
+ </context>
+
+ <!-- If there are dashes/"?" before the literal operator -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-withdash-s1" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-default" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-withdash-s2" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1%2&space;\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-default" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-withdash-s3" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1%2%3&space;{2}\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-default" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-withdash-s4" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1%2%3%4&space;{3}\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-default" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-withdash-s5" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1%2%3%4%5&space;{4}\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-default" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-withdash-s6" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1%2%3%4%5%6&space;{5}\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-default" />
+ </context>
+ <!-- If there are dashes/"?" and a Key before the literal operator -->
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-key-withdash-s2" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1%2&space;\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-key-default" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-key-withdash-s3" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1%2%3&space;{2}\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-key-default" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-key-withdash-s4" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1%2%3%4&space;{3}\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-key-default" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-key-withdash-s5" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1%2%3%4%5&space;{4}\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-key-default" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-key-withdash-s6" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1%2%3%4%5%6&space;{5}\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-key-default" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="literal-block-key-withdash-s7" dynamic="true" noIndentationBasedFolding="true">
+ <RegExpr attribute="Literal/Folded Block" context="#stay" String="^%1%2%3%4%5%6%7&space;{6}\s.*$" dynamic="true" column="0" />
+ <IncludeRules context="literal-block-key-default" />
+ </context>
+ </contexts>
+
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsAttribute" />
+ <itemData name="Attribute" defStyleNum="dsAttribute" />
+ <itemData name="List" defStyleNum="dsAttribute" />
+ <itemData name="Hash" defStyleNum="dsAttribute" />
+ <itemData name="Comment" defStyleNum="dsComment" />
+ <itemData name="End of Document" defStyleNum="dsComment" />
+ <itemData name="Document Header" defStyleNum="dsPreprocessor" />
+ <itemData name="Data Types" defStyleNum="dsOthers" />
+ <itemData name="Alias" defStyleNum="dsOthers" />
+ <itemData name="Reference" defStyleNum="dsOthers" />
+ <itemData name="Key" defStyleNum="dsFunction" bold="1" />
+ <itemData name="Directive" defStyleNum="dsPreprocessor" />
+ <itemData name="Key Points Operator" defStyleNum="dsKeyword" />
+ <itemData name="Operator" defStyleNum="dsKeyword" />
+ <itemData name="String" defStyleNum="dsString" />
+ <itemData name="Escaped Character" defStyleNum="dsSpecialChar" />
+ <itemData name="Literal/Folded Operator" defStyleNum="dsChar" bold="1" />
+ <itemData name="Literal/Folded Block" defStyleNum="dsNormal" />
+ <itemData name="Null" defStyleNum="dsChar" />
+ <itemData name="Boolean" defStyleNum="dsChar" />
+ <itemData name="Integer" defStyleNum="dsDecVal" />
+ <itemData name="Float" defStyleNum="dsFloat" />
+ <itemData name="Error" defStyleNum="dsError" />
+ <itemData name="Alert" defStyleNum="dsAlert" backgroundColor="#EF9A9A" />
+ </itemDatas>
+ </highlighting>
+
+ <general>
+ <folding indentationsensitive="1" />
+ <emptyLines>
+ <emptyLine regexpr="(?:\s+|\s*#.*)"/>
+ </emptyLines>
+ <comments>
+ <comment name="singleLine" start="#" position="afterwhitespace" />
+ </comments>
+ <keywords casesensitive="1"/>
+ </general>
+</language>
+<!-- kate: replace-tabs on; tab-width 2; indent-width 2; -->
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/atom-one-dark.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/atom-one-dark.theme
new file mode 100644
index 0000000000..358c8a5c70
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/atom-one-dark.theme
@@ -0,0 +1,370 @@
+{
+ "custom-styles": {
+ "Diff": {
+ "Added line": {
+ "selected-text-color": "#98c379",
+ "text-color": "#98c379"
+ },
+ "Changed line (new)": {
+ "selected-text-color": "#98c379",
+ "text-color": "#98c379"
+ },
+ "Changed line (old)": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Removed line": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ }
+ },
+ "Go": {
+ "Predeclared Identifier": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ }
+ },
+ "INI Files": {
+ "Assignment": {
+ "selected-text-color": "#abb2bf",
+ "text-color": "#abb2bf"
+ },
+ "Section": {
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ }
+ },
+ "JavaScript": {
+ "Built-in Objects": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "Function Declaration": {
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ },
+ "Function Name": {
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ },
+ "Module": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "Object Member": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Object Method (Built-in)": {
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ }
+ },
+ "Markdown": {
+ "Code": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "Emphasis Text": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "Fenced Code": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "Header H1": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Header H2": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Header H3": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Header H4": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Header H5": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Header H6": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Link": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "Reference-Link Name": {
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ },
+ "Reference-Link Target": {
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ },
+ "Reference-Link Target: Link": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "Reference-Link: Email": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "Reference-Link: Link": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "Strong Text": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ }
+ },
+ "Python": {
+ "Builtin Function": {
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ },
+ "String Substitution": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ }
+ },
+ "Rust": {
+ "Lifetime": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "Macro": {
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ },
+ "Self": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Trait": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "Type": {
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ }
+ },
+ "TypeScript": {
+ "Built-in Objects": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "Module": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "Object Member": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Object Method (Built-in)": {
+ "italic": false,
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ },
+ "Reserved": {
+ "italic": false
+ },
+ "Types": {
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ }
+ },
+ "XML": {
+ "Attribute": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "Element": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ }
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor": "#282c34",
+ "BracketMatching": "#8e44ad",
+ "CodeFolding": "#363c4a",
+ "CurrentLine": "#0A99BBFF",
+ "CurrentLineNumber": "#abb2bf",
+ "IconBorder": "#282c34",
+ "IndentationLine": "#3a3f44",
+ "LineNumbers": "#636D83",
+ "MarkBookmark": "#0404bf",
+ "MarkBreakpointActive": "#8b0607",
+ "MarkBreakpointDisabled": "#820683",
+ "MarkBreakpointReached": "#6d6e07",
+ "MarkError": "#c24038",
+ "MarkExecution": "#4d4e50",
+ "MarkWarning": "#d19a66",
+ "ModifiedLines": "#e06c75",
+ "ReplaceHighlight": "#a34f56",
+ "SavedLines": "#98c379",
+ "SearchHighlight": "#3D528BFF",
+ "Separator": "#3f4347",
+ "SpellChecking": "#c24038",
+ "TabMarker": "#21252B",
+ "TemplateBackground": "#31363b",
+ "TemplateFocusedPlaceholder": "#123723",
+ "TemplatePlaceholder": "#123723",
+ "TemplateReadOnlyPlaceholder": "#4d1f24",
+ "TextSelection": "#363c4a",
+ "WordWrapMarker": "#3a3f44"
+ },
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2016 GitHub Inc.",
+ "SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "Atom One Dark",
+ "revision": 3
+ },
+ "text-styles": {
+ "Alert": {
+ "background-color": "#4d1f24",
+ "bold": true,
+ "selected-text-color": "#95da4c",
+ "text-color": "#95da4c"
+ },
+ "Annotation": {
+ "selected-text-color": "#98c379",
+ "text-color": "#98c379"
+ },
+ "Attribute": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "BaseN": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "Char": {
+ "selected-text-color": "#98c379",
+ "text-color": "#98c379"
+ },
+ "Comment": {
+ "italic": true,
+ "selected-text-color": "#5c6370",
+ "text-color": "#5c6370"
+ },
+ "CommentVar": {
+ "italic": true,
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Constant": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "ControlFlow": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "DataType": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "DecVal": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "Documentation": {
+ "selected-text-color": "#da4453",
+ "text-color": "#a43340"
+ },
+ "Error": {
+ "selected-text-color": "#f44747",
+ "text-color": "#f44747",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#61afef",
+ "text-color": "#61afef"
+ },
+ "Float": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "Function": {
+ "selected-text-color": "#61afef",
+ "text-color": "#61afef"
+ },
+ "Import": {
+ "selected-text-color": "#98c379",
+ "text-color": "#98c379"
+ },
+ "Information": {
+ "selected-text-color": "#e46700",
+ "text-color": "#c45b00"
+ },
+ "Keyword": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "Normal": {
+ "selected-text-color": "#abb2bf",
+ "text-color": "#abb2bf"
+ },
+ "Operator": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "Others": {
+ "selected-text-color": "#27ae60",
+ "text-color": "#27ae60"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#c678dd",
+ "text-color": "#c678dd"
+ },
+ "RegionMarker": {
+ "background-color": "#153042",
+ "selected-text-color": "#3daee9",
+ "text-color": "#2980b9"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#56b6c2",
+ "text-color": "#56b6c2"
+ },
+ "SpecialString": {
+ "selected-text-color": "#da4453",
+ "text-color": "#da4453"
+ },
+ "String": {
+ "selected-text-color": "#98c379",
+ "text-color": "#98c379"
+ },
+ "Variable": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#da4453",
+ "text-color": "#da4453"
+ },
+ "Warning": {
+ "selected-text-color": "#da4453",
+ "text-color": "#da4453"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/atom-one-light.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/atom-one-light.theme
new file mode 100644
index 0000000000..62cec7394e
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/atom-one-light.theme
@@ -0,0 +1,377 @@
+{
+ "custom-styles": {
+ "Diff": {
+ "Added line": {
+ "selected-text-color": "#98c379",
+ "text-color": "#98c379"
+ },
+ "Changed line (new)": {
+ "selected-text-color": "#98c379",
+ "text-color": "#98c379"
+ },
+ "Changed line (old)": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ },
+ "Removed line": {
+ "selected-text-color": "#e06c75",
+ "text-color": "#e06c75"
+ }
+ },
+ "Go": {
+ "Predeclared Identifier": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ }
+ },
+ "INI Files": {
+ "Assignment": {
+ "selected-text-color": "#383a42",
+ "text-color": "#383a42"
+ },
+ "Section": {
+ "selected-text-color": "#4078f2",
+ "text-color": "#4078f2"
+ }
+ },
+ "JavaScript": {
+ "Built-in Objects": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ },
+ "Function Declaration": {
+ "selected-text-color": "#0184bc",
+ "text-color": "#0184bc"
+ },
+ "Function Name": {
+ "selected-text-color": "#0184bc",
+ "text-color": "#0184bc"
+ },
+ "Module": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "Object Member": {
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ },
+ "Object Method (Built-in)": {
+ "selected-text-color": "#0184bc",
+ "text-color": "#0184bc"
+ }
+ },
+ "Markdown": {
+ "Code": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "Emphasis Text": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "Fenced Code": {
+ "selected-text-color": "#d19a66",
+ "text-color": "#d19a66"
+ },
+ "Header H1": {
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ },
+ "Header H2": {
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ },
+ "Header H3": {
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ },
+ "Header H4": {
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ },
+ "Header H5": {
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ },
+ "Header H6": {
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ },
+ "Line Break": {
+ "text-color": "#383a42"
+ },
+ "Link": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "Reference-Link Name": {
+ "selected-text-color": "#4078f2",
+ "text-color": "#4078f2"
+ },
+ "Reference-Link Target": {
+ "selected-text-color": "#4078f2",
+ "text-color": "#4078f2"
+ },
+ "Reference-Link Target: Link": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "Reference-Link: Email": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "Reference-Link: Link": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "Strong Text": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ }
+ },
+ "Python": {
+ "Builtin Function": {
+ "selected-text-color": "#0184bc",
+ "text-color": "#0184bc"
+ },
+ "Import": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "String Substitution": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ }
+ },
+ "Rust": {
+ "Lifetime": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ },
+ "Macro": {
+ "selected-text-color": "#4078f2",
+ "text-color": "#4078f2"
+ },
+ "Self": {
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ },
+ "Trait": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ },
+ "Type": {
+ "selected-text-color": "#0184bc",
+ "text-color": "#0184bc"
+ }
+ },
+ "TypeScript": {
+ "Built-in Objects": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ },
+ "Module": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "Object Member": {
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ },
+ "Object Method (Built-in)": {
+ "italic": false,
+ "selected-text-color": "#0184bc",
+ "text-color": "#0184bc"
+ },
+ "Reserved": {
+ "italic": false
+ },
+ "Types": {
+ "selected-text-color": "#4078f2",
+ "text-color": "#4078f2"
+ }
+ },
+ "XML": {
+ "Attribute": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ },
+ "Element": {
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ }
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor": "#fafafa",
+ "BracketMatching": "#ff6e6e",
+ "CodeFolding": "#e5e5e6",
+ "CurrentLine": "#0C383A42",
+ "CurrentLineNumber": "#383a42",
+ "IconBorder": "#fafafa",
+ "IndentationLine": "#626772",
+ "LineNumbers": "#9D9D9F",
+ "MarkBookmark": "#0404bf",
+ "MarkBreakpointActive": "#8b0607",
+ "MarkBreakpointDisabled": "#820683",
+ "MarkBreakpointReached": "#6d6e07",
+ "MarkError": "#c24038",
+ "MarkExecution": "#4d4e50",
+ "MarkWarning": "#ce7c2b",
+ "ModifiedLines": "#e06c75",
+ "ReplaceHighlight": "#ef747e",
+ "SavedLines": "#98c379",
+ "SearchHighlight": "#33526FFF",
+ "Separator": "#a6b0ba",
+ "SpellChecking": "#c24038",
+ "TabMarker": "#8894a6",
+ "TemplateBackground": "#31363b",
+ "TemplateFocusedPlaceholder": "#123723",
+ "TemplatePlaceholder": "#123723",
+ "TemplateReadOnlyPlaceholder": "#4d1f24",
+ "TextSelection": "#e5e5e6",
+ "WordWrapMarker": "#cddff1"
+ },
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2016 GitHub Inc.",
+ "SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "Atom One Light",
+ "revision": 4
+ },
+ "text-styles": {
+ "Alert": {
+ "background-color": "#4d1f24",
+ "bold": true,
+ "selected-text-color": "#95da4c",
+ "text-color": "#95da4c"
+ },
+ "Annotation": {
+ "selected-text-color": "#50a14f",
+ "text-color": "#50a14f"
+ },
+ "Attribute": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "BaseN": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "Char": {
+ "selected-text-color": "#50a14f",
+ "text-color": "#50a14f"
+ },
+ "Comment": {
+ "italic": true,
+ "selected-text-color": "#a0a1a7",
+ "text-color": "#a0a1a7"
+ },
+ "CommentVar": {
+ "italic": true,
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ },
+ "Constant": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ },
+ "ControlFlow": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "DataType": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "DecVal": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ },
+ "Documentation": {
+ "selected-text-color": "#da4453",
+ "text-color": "#e45649"
+ },
+ "Error": {
+ "selected-text-color": "#f44747",
+ "text-color": "#f44747",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#4078f2",
+ "text-color": "#4078f2"
+ },
+ "Float": {
+ "selected-text-color": "#986801",
+ "text-color": "#986801"
+ },
+ "Function": {
+ "selected-text-color": "#4078f2",
+ "text-color": "#4078f2"
+ },
+ "Import": {
+ "selected-text-color": "#50a14f",
+ "text-color": "#50a14f"
+ },
+ "Information": {
+ "selected-text-color": "#e46700",
+ "text-color": "#c45b00"
+ },
+ "Keyword": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "Normal": {
+ "selected-text-color": "#383a42",
+ "text-color": "#383a42"
+ },
+ "Operator": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "Others": {
+ "selected-text-color": "#27ae60",
+ "text-color": "#27ae60"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#a626a4",
+ "text-color": "#a626a4"
+ },
+ "RegionMarker": {
+ "background-color": "#153042",
+ "selected-text-color": "#3daee9",
+ "text-color": "#2980b9"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#0184bc",
+ "text-color": "#0184bc"
+ },
+ "SpecialString": {
+ "selected-text-color": "#da4453",
+ "text-color": "#da4453"
+ },
+ "String": {
+ "selected-text-color": "#50a14f",
+ "text-color": "#50a14f"
+ },
+ "Variable": {
+ "selected-text-color": "#e45649",
+ "text-color": "#e45649"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#da4453",
+ "text-color": "#da4453"
+ },
+ "Warning": {
+ "selected-text-color": "#da4453",
+ "text-color": "#da4453"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/ayu-dark.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/ayu-dark.theme
new file mode 100644
index 0000000000..fea8def01f
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/ayu-dark.theme
@@ -0,0 +1,205 @@
+{
+ "_comments": [
+ "Last update: Jul 28, 2021 (revision 4)",
+ "This file has been converted from: https://github.com/dempfi/ayu",
+ "Also see: https://github.com/ayu-theme"
+ ],
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2016 Ike Ku",
+ "SPDX-FileCopyrightText: 2020 Nibaldo González <nibgonz@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "ayu Dark",
+ "revision": 4
+ },
+ "editor-colors": {
+ "BackgroundColor": "#0a0e14",
+ "BracketMatching": "#1e232d",
+ "CodeFolding": "#121a23",
+ "CurrentLine": "#00010a",
+ "CurrentLineNumber": "#414857",
+ "IconBorder": "#0e1218",
+ "IndentationLine": "#252b35",
+ "LineNumbers": "#323844",
+ "MarkBookmark": "#59c2ff",
+ "MarkBreakpointActive": "#f07178",
+ "MarkBreakpointDisabled": "#ffee99",
+ "MarkBreakpointReached": "#e6b673",
+ "MarkError": "#ff3333",
+ "MarkExecution": "#95e6cb",
+ "MarkWarning": "#c2d94c",
+ "ModifiedLines": "#6994bf",
+ "ReplaceHighlight": "#705728",
+ "SavedLines": "#91b362",
+ "SearchHighlight": "#414857",
+ "Separator": "#151a1e",
+ "SpellChecking": "#d96c75",
+ "TabMarker": "#1d222b",
+ "TemplateBackground": "#030810",
+ "TemplateFocusedPlaceholder": "#3c4250",
+ "TemplatePlaceholder": "#2b303a",
+ "TemplateReadOnlyPlaceholder": "#0d1016",
+ "TextSelection": "#273747",
+ "WordWrapMarker": "#1e222b"
+ },
+ "text-styles": {
+ "Alert": {
+ "background-color": "#2a0f15",
+ "bold": true,
+ "selected-text-color": "#ff3333",
+ "text-color": "#ff3333"
+ },
+ "Annotation": {
+ "selected-text-color": "#e6b673",
+ "text-color": "#e6b673"
+ },
+ "Attribute": {
+ "selected-text-color": "#59c2ff",
+ "text-color": "#59c2ff"
+ },
+ "BaseN": {
+ "selected-text-color": "#e6b450",
+ "text-color": "#e6b450"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ },
+ "Char": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ },
+ "Comment": {
+ "italic": true,
+ "selected-text-color": "#626a73",
+ "text-color": "#626a73"
+ },
+ "CommentVar": {
+ "selected-text-color": "#ffee99",
+ "text-color": "#ffee99"
+ },
+ "Constant": {
+ "selected-text-color": "#ffee99",
+ "text-color": "#ffee99"
+ },
+ "ControlFlow": {
+ "bold": true,
+ "selected-text-color": "#ff8f40",
+ "text-color": "#ff8f40"
+ },
+ "DataType": {
+ "selected-text-color": "#ff8f40",
+ "text-color": "#ff8f40"
+ },
+ "DecVal": {
+ "selected-text-color": "#e6b450",
+ "text-color": "#e6b450"
+ },
+ "Documentation": {
+ "selected-text-color": "#626a73",
+ "text-color": "#626a73"
+ },
+ "Error": {
+ "selected-text-color": "#ff3333",
+ "text-color": "#ff3333",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#59c2ff",
+ "text-color": "#59c2ff"
+ },
+ "Float": {
+ "selected-text-color": "#e6b450",
+ "text-color": "#e6b450"
+ },
+ "Function": {
+ "selected-text-color": "#ffb454",
+ "text-color": "#ffb454"
+ },
+ "Import": {
+ "selected-text-color": "#c2d94c",
+ "text-color": "#c2d94c"
+ },
+ "Information": {
+ "selected-text-color": "#e6b450",
+ "text-color": "#e6b450"
+ },
+ "Keyword": {
+ "bold": true,
+ "selected-text-color": "#ff8f40",
+ "text-color": "#ff8f40"
+ },
+ "Normal": {
+ "selected-text-color": "#b3b1ad",
+ "text-color": "#b3b1ad"
+ },
+ "Operator": {
+ "selected-text-color": "#f29668",
+ "text-color": "#f29668"
+ },
+ "Others": {
+ "selected-text-color": "#39bae6",
+ "text-color": "#39bae6"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#f07178",
+ "text-color": "#f07178"
+ },
+ "RegionMarker": {
+ "background-color": "#173649",
+ "selected-text-color": "#59c2ff",
+ "text-color": "#59c2ff"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ },
+ "SpecialString": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ },
+ "String": {
+ "selected-text-color": "#c2d94c",
+ "text-color": "#c2d94c"
+ },
+ "Variable": {
+ "selected-text-color": "#39bae6",
+ "text-color": "#39bae6"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#99ca44",
+ "text-color": "#99ca44"
+ },
+ "Warning": {
+ "selected-text-color": "#f07178",
+ "text-color": "#f07178"
+ }
+ },
+ "custom-styles": {
+ "XML": {
+ "Attribute": {
+ "selected-text-color": "#ffb454",
+ "text-color": "#ffb454"
+ },
+ "Element": {
+ "selected-text-color": "#39bae6",
+ "text-color": "#39bae6",
+ "bold": false
+ },
+ "Element Symbols": {
+ "selected-text-color": "#307896",
+ "text-color": "#22647d"
+ },
+ "EntityRef": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ },
+ "PEntityRef": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ }
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/ayu-light.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/ayu-light.theme
new file mode 100644
index 0000000000..30e119ed1b
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/ayu-light.theme
@@ -0,0 +1,180 @@
+{
+ "_comments": [
+ "Last update: Jul 28, 2021 (revision 3)",
+ "This file has been converted from: https://github.com/dempfi/ayu",
+ "Also see: https://github.com/ayu-theme"
+ ],
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2016 Ike Ku",
+ "SPDX-FileCopyrightText: 2020 Nibaldo González <nibgonz@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "ayu Light",
+ "revision": 3
+ },
+ "editor-colors": {
+ "BackgroundColor": "#fafafa",
+ "BracketMatching": "#d9dbdd",
+ "CodeFolding": "#ffe6dc",
+ "CurrentLine": "#eff0f2",
+ "CurrentLineNumber": "#767676",
+ "IconBorder": "#f3f3f3",
+ "IndentationLine": "#dcdee1",
+ "LineNumbers": "#9f9f9f",
+ "MarkBookmark": "#399ee6",
+ "MarkBreakpointActive": "#f07171",
+ "MarkBreakpointDisabled": "#a37acc",
+ "MarkBreakpointReached": "#e6ba7e",
+ "MarkError": "#f51818",
+ "MarkExecution": "#4cbf99",
+ "MarkWarning": "#86b300",
+ "ModifiedLines": "#709ecc",
+ "ReplaceHighlight": "#b0d4e4",
+ "SavedLines": "#99bf4d",
+ "SearchHighlight": "#fdd1bc",
+ "Separator": "#e0dfdf",
+ "SpellChecking": "#f27983",
+ "TabMarker": "#dcdee1",
+ "TemplateBackground": "#f7f7f7",
+ "TemplateFocusedPlaceholder": "#bec1c6",
+ "TemplatePlaceholder": "#dddfe1",
+ "TemplateReadOnlyPlaceholder": "#ffffff",
+ "TextSelection": "#d1e4f4",
+ "WordWrapMarker": "#e9eaeb"
+ },
+ "text-styles": {
+ "Alert": {
+ "background-color": "#faefef",
+ "bold": true,
+ "selected-text-color": "#f51818",
+ "text-color": "#f51818"
+ },
+ "Annotation": {
+ "selected-text-color": "#e6ba7e",
+ "text-color": "#e6ba7e"
+ },
+ "Attribute": {
+ "selected-text-color": "#399ee6",
+ "text-color": "#399ee6"
+ },
+ "BaseN": {
+ "selected-text-color": "#ff9940",
+ "text-color": "#ff9940"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#4cbf99",
+ "text-color": "#4cbf99"
+ },
+ "Char": {
+ "selected-text-color": "#4cbf99",
+ "text-color": "#4cbf99"
+ },
+ "Comment": {
+ "italic": true,
+ "selected-text-color": "#607880",
+ "text-color": "#607880"
+ },
+ "CommentVar": {
+ "selected-text-color": "#a37acc",
+ "text-color": "#a37acc"
+ },
+ "Constant": {
+ "selected-text-color": "#a37acc",
+ "text-color": "#a37acc"
+ },
+ "ControlFlow": {
+ "bold": true,
+ "selected-text-color": "#fa8d3e",
+ "text-color": "#fa8d3e"
+ },
+ "DataType": {
+ "selected-text-color": "#fa8d3e",
+ "text-color": "#fa8d3e"
+ },
+ "DecVal": {
+ "selected-text-color": "#ff9940",
+ "text-color": "#ff9940"
+ },
+ "Documentation": {
+ "selected-text-color": "#607880",
+ "text-color": "#607880"
+ },
+ "Error": {
+ "selected-text-color": "#f51818",
+ "text-color": "#f51818",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#399ee6",
+ "text-color": "#399ee6"
+ },
+ "Float": {
+ "selected-text-color": "#ff9940",
+ "text-color": "#ff9940"
+ },
+ "Function": {
+ "selected-text-color": "#f2ae49",
+ "text-color": "#f2ae49"
+ },
+ "Import": {
+ "selected-text-color": "#86b300",
+ "text-color": "#86b300"
+ },
+ "Information": {
+ "selected-text-color": "#ff9940",
+ "text-color": "#ff9940"
+ },
+ "Keyword": {
+ "bold": true,
+ "selected-text-color": "#fa8d3e",
+ "text-color": "#fa8d3e"
+ },
+ "Normal": {
+ "selected-text-color": "#575f66",
+ "text-color": "#575f66"
+ },
+ "Operator": {
+ "selected-text-color": "#ed9366",
+ "text-color": "#ed9366"
+ },
+ "Others": {
+ "selected-text-color": "#55b4d4",
+ "text-color": "#55b4d4"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#f07171",
+ "text-color": "#f07171"
+ },
+ "RegionMarker": {
+ "background-color": "#ddecf3",
+ "selected-text-color": "#399ee6",
+ "text-color": "#399ee6"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#4cbf99",
+ "text-color": "#4cbf99"
+ },
+ "SpecialString": {
+ "selected-text-color": "#4cbf99",
+ "text-color": "#4cbf99"
+ },
+ "String": {
+ "selected-text-color": "#86b300",
+ "text-color": "#86b300"
+ },
+ "Variable": {
+ "selected-text-color": "#55b4d4",
+ "text-color": "#55b4d4"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#729800",
+ "text-color": "#729800"
+ },
+ "Warning": {
+ "selected-text-color": "#f07171",
+ "text-color": "#f07171"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/ayu-mirage.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/ayu-mirage.theme
new file mode 100644
index 0000000000..005d6a0ad2
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/ayu-mirage.theme
@@ -0,0 +1,205 @@
+{
+ "_comments": [
+ "Last update: Jul 28, 2021 (revision 4)",
+ "This file has been converted from: https://github.com/dempfi/ayu",
+ "Also see: https://github.com/ayu-theme"
+ ],
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2016 Ike Ku",
+ "SPDX-FileCopyrightText: 2020 Nibaldo González <nibgonz@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "ayu Mirage",
+ "revision": 4
+ },
+ "editor-colors": {
+ "BackgroundColor": "#1f2430",
+ "BracketMatching": "#383e4c",
+ "CodeFolding": "#252c3e",
+ "CurrentLine": "#191e2a",
+ "CurrentLineNumber": "#606979",
+ "IconBorder": "#222733",
+ "IndentationLine": "#383f4c",
+ "LineNumbers": "#444b59",
+ "MarkBookmark": "#73d0ff",
+ "MarkBreakpointActive": "#f28779",
+ "MarkBreakpointDisabled": "#d4bfff",
+ "MarkBreakpointReached": "#ffe6b3",
+ "MarkError": "#ff3333",
+ "MarkExecution": "#95e6cb",
+ "MarkWarning": "#bae67e",
+ "ModifiedLines": "#77a8d9",
+ "ReplaceHighlight": "#7f553b",
+ "SavedLines": "#a6cc70",
+ "SearchHighlight": "#606979",
+ "Separator": "#2c313d",
+ "SpellChecking": "#f27983",
+ "TabMarker": "#303642",
+ "TemplateBackground": "#1d222e",
+ "TemplateFocusedPlaceholder": "#596171",
+ "TemplatePlaceholder": "#434957",
+ "TemplateReadOnlyPlaceholder": "#232834",
+ "TextSelection": "#33415e",
+ "WordWrapMarker": "#303642"
+ },
+ "text-styles": {
+ "Alert": {
+ "background-color": "#332430",
+ "bold": true,
+ "selected-text-color": "#ff3333",
+ "text-color": "#ff3333"
+ },
+ "Annotation": {
+ "selected-text-color": "#ffe6b3",
+ "text-color": "#ffe6b3"
+ },
+ "Attribute": {
+ "selected-text-color": "#73d0ff",
+ "text-color": "#73d0ff"
+ },
+ "BaseN": {
+ "selected-text-color": "#ffcc66",
+ "text-color": "#ffcc66"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ },
+ "Char": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ },
+ "Comment": {
+ "italic": true,
+ "selected-text-color": "#5c6773",
+ "text-color": "#5c6773"
+ },
+ "CommentVar": {
+ "selected-text-color": "#d4bfff",
+ "text-color": "#d4bfff"
+ },
+ "Constant": {
+ "selected-text-color": "#d4bfff",
+ "text-color": "#d4bfff"
+ },
+ "ControlFlow": {
+ "bold": true,
+ "selected-text-color": "#ffa759",
+ "text-color": "#ffa759"
+ },
+ "DataType": {
+ "selected-text-color": "#ffa759",
+ "text-color": "#ffa759"
+ },
+ "DecVal": {
+ "selected-text-color": "#ffcc66",
+ "text-color": "#ffcc66"
+ },
+ "Documentation": {
+ "selected-text-color": "#5c6773",
+ "text-color": "#5c6773"
+ },
+ "Error": {
+ "selected-text-color": "#ff3333",
+ "text-color": "#ff3333",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#73d0ff",
+ "text-color": "#73d0ff"
+ },
+ "Float": {
+ "selected-text-color": "#ffcc66",
+ "text-color": "#ffcc66"
+ },
+ "Function": {
+ "selected-text-color": "#ffd580",
+ "text-color": "#ffd580"
+ },
+ "Import": {
+ "selected-text-color": "#bae67e",
+ "text-color": "#bae67e"
+ },
+ "Information": {
+ "selected-text-color": "#ffcc66",
+ "text-color": "#ffcc66"
+ },
+ "Keyword": {
+ "bold": true,
+ "selected-text-color": "#ffa759",
+ "text-color": "#ffa759"
+ },
+ "Normal": {
+ "selected-text-color": "#cbccc6",
+ "text-color": "#cbccc6"
+ },
+ "Operator": {
+ "selected-text-color": "#f29e74",
+ "text-color": "#f29e74"
+ },
+ "Others": {
+ "selected-text-color": "#5ccfe6",
+ "text-color": "#5ccfe6"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#f28779",
+ "text-color": "#f28779"
+ },
+ "RegionMarker": {
+ "background-color": "#2a4254",
+ "selected-text-color": "#73d0ff",
+ "text-color": "#73d0ff"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ },
+ "SpecialString": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ },
+ "String": {
+ "selected-text-color": "#bae67e",
+ "text-color": "#bae67e"
+ },
+ "Variable": {
+ "selected-text-color": "#5ccfe6",
+ "text-color": "#5ccfe6"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#82e26a",
+ "text-color": "#82e26a"
+ },
+ "Warning": {
+ "selected-text-color": "#f28779",
+ "text-color": "#f28779"
+ }
+ },
+ "custom-styles": {
+ "XML": {
+ "Attribute": {
+ "selected-text-color": "#ffd580",
+ "text-color": "#ffd580"
+ },
+ "Element": {
+ "selected-text-color": "#5ccfe6",
+ "text-color": "#5ccfe6",
+ "bold": false
+ },
+ "Element Symbols": {
+ "selected-text-color": "#4788a2",
+ "text-color": "#3d7a8b"
+ },
+ "EntityRef": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ },
+ "PEntityRef": {
+ "selected-text-color": "#95e6cb",
+ "text-color": "#95e6cb"
+ }
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/breeze-dark.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/breeze-dark.theme
index 8147948eeb..1ad7fffee3 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/themes/breeze-dark.theme
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/breeze-dark.theme
@@ -1,6 +1,11 @@
{
"metadata" : {
- "revision" : 2,
+ "copyright": [
+ "SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>",
+ "SPDX-FileCopyrightText: 2016 Dominik Haumann <dhaumann@kde.org>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "revision" : 7,
"name" : "Breeze Dark"
},
"text-styles": {
@@ -31,8 +36,8 @@
"bold" : true
},
"Operator" : {
- "text-color" : "#cfcfc2",
- "selected-text-color" : "#cfcfc2"
+ "text-color" : "#3f8058",
+ "selected-text-color" : "#54aa75"
},
"BuiltIn" : {
"text-color" : "#7f8c8d",
@@ -142,33 +147,33 @@
}
},
"editor-colors": {
- "background-color" : "#232629",
- "code-folding" : "#224e65",
- "bracket-matching" : "#8e44ad",
- "current-line" : "#2A2E32",
- "icon-border" : "#31363b",
- "indentation-line" : "#3a3f44",
- "line-numbers" : "#7a7c7d",
- "current-line-number" : "#a5a6a8",
- "mark-bookmark" : "#0404bf",
- "mark-breakpoint-active" : "#8b0607",
- "mark-breakpoint-reached" : "#6d6e07",
- "mark-breakpoint-disabled" : "#820683",
- "mark-execution" : "#4d4e50",
- "mark-warning" : "#f67400",
- "mark-error" : "#da4453",
- "modified-lines" : "#c04900",
- "replace-highlight" : "#808021",
- "saved-lines" : "#1c8042",
- "search-highlight" : "#218058",
- "selection" : "#2d5c76",
- "separator" : "#7a7c7d",
- "spell-checking" : "#c0392b",
- "tab-marker" : "#4d4d4d",
- "template-background" : "#31363b",
- "template-placeholder" : "#123723",
- "template-focused-placeholder" : "#123723",
- "template-read-only-placeholder" : "#4d1f24",
- "word-wrap-marker" : "#3a3f44"
+ "BackgroundColor" : "#232629",
+ "CodeFolding" : "#224e65",
+ "BracketMatching" : "#8e44ad",
+ "CurrentLine" : "#2A2E32",
+ "IconBorder" : "#31363b",
+ "IndentationLine" : "#3a3f44",
+ "LineNumbers" : "#7a7c7d",
+ "CurrentLineNumber" : "#a5a6a8",
+ "MarkBookmark" : "#0404bf",
+ "MarkBreakpointActive" : "#8b0607",
+ "MarkBreakpointReached" : "#6d6e07",
+ "MarkBreakpointDisabled" : "#820683",
+ "MarkExecution" : "#4d4e50",
+ "MarkWarning" : "#f67400",
+ "MarkError" : "#da4453",
+ "ModifiedLines" : "#c04900",
+ "ReplaceHighlight" : "#808021",
+ "SavedLines" : "#1c8042",
+ "SearchHighlight" : "#218058",
+ "TextSelection" : "#2d5c76",
+ "Separator" : "#3f4347",
+ "SpellChecking" : "#c0392b",
+ "TabMarker" : "#4d4d4d",
+ "TemplateBackground" : "#31363b",
+ "TemplatePlaceholder" : "#123723",
+ "TemplateFocusedPlaceholder" : "#123723",
+ "TemplateReadOnlyPlaceholder" : "#4d1f24",
+ "WordWrapMarker" : "#3a3f44"
}
}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/default.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/breeze-light.theme
index e9c5c838d3..6dee8dd777 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/themes/default.theme
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/breeze-light.theme
@@ -1,7 +1,12 @@
{
"metadata" : {
- "revision" : 3,
- "name" : "Default"
+ "copyright": [
+ "SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>",
+ "SPDX-FileCopyrightText: 2016 Dominik Haumann <dhaumann@kde.org>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "revision" : 9,
+ "name" : "Breeze Light"
},
"text-styles": {
"Normal" : {
@@ -31,8 +36,8 @@
"bold" : true
},
"Operator" : {
- "text-color" : "#1f1c1b",
- "selected-text-color" : "#ffffff"
+ "text-color" : "#ca60ca",
+ "selected-text-color" : "#a44ea4"
},
"BuiltIn" : {
"text-color" : "#644a9b",
@@ -65,7 +70,7 @@
"selected-text-color" : "#9c0e0e"
},
"VerbatimString" : {
- "text-color" : "#bf0303",
+ "text-color" : "#e31616",
"selected-text-color" : "#9c0e0e"
},
"SpecialString" : {
@@ -142,33 +147,33 @@
}
},
"editor-colors": {
- "background-color" : "#ffffff",
- "code-folding" : "#94caef",
- "bracket-matching" : "#ffff00",
- "current-line" : "#f8f7f6",
- "icon-border" : "#f0f0f0",
- "indentation-line" : "#d2d2d2",
- "line-numbers" : "#a0a0a0",
- "current-line-number" : "#1e1e1e",
- "mark-bookmark" : "#0000ff",
- "mark-breakpoint-active" : "#ff0000",
- "mark-breakpoint-reached" : "#ffff00",
- "mark-breakpoint-disabled" : "#ff00ff",
- "mark-execution" : "#a0a0a4",
- "mark-warning" : "#00ff00",
- "mark-error" : "#ff0000",
- "modified-lines" : "#fdbc4b",
- "replace-highlight" : "#00ff00",
- "saved-lines" : "#2ecc71",
- "search-highlight" : "#ffff00",
- "selection" : "#94caef",
- "separator" : "#898887",
- "spell-checking" : "#bf0303",
- "tab-marker" : "#d2d2d2",
- "template-background" : "#d6d2d0",
- "template-placeholder" : "#baf8ce",
- "template-focused-placeholder" : "#76da98",
- "template-read-only-placeholder" : "#f6e6e6",
- "word-wrap-marker" : "#ededed"
+ "BackgroundColor" : "#ffffff",
+ "CodeFolding" : "#94caef",
+ "BracketMatching" : "#ffff00",
+ "CurrentLine" : "#f8f7f6",
+ "IconBorder" : "#f0f0f0",
+ "IndentationLine" : "#d2d2d2",
+ "LineNumbers" : "#a0a0a0",
+ "CurrentLineNumber" : "#1e1e1e",
+ "MarkBookmark" : "#0000ff",
+ "MarkBreakpointActive" : "#ff0000",
+ "MarkBreakpointReached" : "#ffff00",
+ "MarkBreakpointDisabled" : "#ff00ff",
+ "MarkExecution" : "#a0a0a4",
+ "MarkWarning" : "#00ff00",
+ "MarkError" : "#ff0000",
+ "ModifiedLines" : "#fdbc4b",
+ "ReplaceHighlight" : "#00ff00",
+ "SavedLines" : "#2ecc71",
+ "SearchHighlight" : "#ffff00",
+ "TextSelection" : "#94caef",
+ "Separator" : "#d5d5d5",
+ "SpellChecking" : "#bf0303",
+ "TabMarker" : "#d2d2d2",
+ "TemplateBackground" : "#d6d2d0",
+ "TemplatePlaceholder" : "#baf8ce",
+ "TemplateFocusedPlaceholder" : "#76da98",
+ "TemplateReadOnlyPlaceholder" : "#f6e6e6",
+ "WordWrapMarker" : "#ededed"
}
}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/dracula.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/dracula.theme
new file mode 100644
index 0000000000..b388396050
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/dracula.theme
@@ -0,0 +1,705 @@
+{
+ "custom-styles": {
+ "Alerts": {
+ "Region Marker": {
+ "selected-text-color": "#6db8c7",
+ "text-color": "#6db8c7"
+ }
+ },
+ "Apache Configuration": {
+ "Directives": {
+ "bold": false
+ }
+ },
+ "Bash": {
+ "Path": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ },
+ "Redirection": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "Variable": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ }
+ },
+ "C": {
+ "Prep. Lib": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ }
+ },
+ "C++": {
+ "Qt Macros": {
+ "bold": false,
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ }
+ },
+ "CMake": {
+ "Builtin Variable": {
+ "selected-text-color": "#ffb86c",
+ "text-color": "#ffb86c"
+ }
+ },
+ "CSS": {
+ "Color": {
+ "bold": false
+ },
+ "Property": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Selector Class": {
+ "italic": true
+ },
+ "Selector Id": {
+ "bold": false,
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ },
+ "Selector Pseudo": {
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ },
+ "Selector Tag": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "Unit": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ }
+ },
+ "D": {
+ "Attribute": {
+ "bold": false
+ },
+ "Declarator": {
+ "bold": false
+ },
+ "Deprecated": {
+ "bold": false
+ },
+ "Expression": {
+ "bold": false
+ },
+ "Module": {
+ "bold": false
+ },
+ "Property": {
+ "bold": false
+ },
+ "Template": {
+ "bold": false
+ }
+ },
+ "Diff": {
+ "Added line": {
+ "selected-text-color": "#5fde38",
+ "text-color": "#50fa7b"
+ },
+ "Changed line (new)": {
+ "background-color": "#50fa7b",
+ "selected-text-color": "#5fde38",
+ "text-color": "#50fa7b"
+ },
+ "Changed line (old)": {
+ "selected-text-color": "#e66eb4",
+ "text-color": "#ff79c6"
+ },
+ "Removed line": {
+ "selected-text-color": "#e66eb4",
+ "text-color": "#ff79c6"
+ }
+ },
+ "Doxygen": {
+ "Custom Tags": {
+ "selected-text-color": "#d465a7",
+ "text-color": "#d465a7"
+ },
+ "Description": {
+ "selected-text-color": "#c58e53",
+ "text-color": "#c58e53"
+ },
+ "Entities": {
+ "bold": false
+ },
+ "HTML Tag": {
+ "bold": false,
+ "selected-text-color": "#d465a7",
+ "text-color": "#d465a7"
+ },
+ "Region": {
+ "selected-text-color": "#6db8c7",
+ "text-color": "#6db8c7"
+ },
+ "Tags": {
+ "bold": false,
+ "selected-text-color": "#d465a7",
+ "text-color": "#d465a7"
+ },
+ "Word": {
+ "bold": false,
+ "selected-text-color": "#c58e53",
+ "text-color": "#c58e53"
+ }
+ },
+ "GNU Assembler": {
+ "Label": {
+ "underline": true
+ }
+ },
+ "Go": {
+ "Builtin Function": {
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ }
+ },
+ "HTML": {
+ "Doctype": {
+ "bold": false,
+ "italic": false,
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ }
+ },
+ "ISO C++": {
+ "Prep. Lib": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ },
+ "Standard Suffix": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "UDL Numeric Suffix": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "UDL String Suffix": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ }
+ },
+ "Intel x86 (NASM)": {
+ "Label": {
+ "underline": true
+ },
+ "Registers": {
+ "selected-text-color": "#ffb86c",
+ "text-color": "#ffb86c"
+ }
+ },
+ "JSON": {
+ "Style_Keyword": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "Style_String_Key": {
+ "italic": false
+ }
+ },
+ "JavaScript": {
+ "Built-in Objects": {
+ "italic": true
+ },
+ "Function (Built-in)": {
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ },
+ "Object Member": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ }
+ },
+ "JavaScript React (JSX)": {
+ "Attribute": {
+ "italic": true
+ },
+ "Component Tag": {
+ "bold": false,
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ }
+ },
+ "Makefile": {
+ "Operator": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "Prereq": {
+ "italic": false,
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ },
+ "Target": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Variable": {
+ "selected-text-color": "#ffb86c",
+ "text-color": "#ffb86c"
+ }
+ },
+ "Markdown": {
+ "Blockquote: Link": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Email": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Emphasis Text": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ },
+ "Header H1": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "Header H2": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "Header H3": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "Header H4": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "Header H5": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "Header H6": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "Inline Image": {
+ "selected-text-color": "#ffb86c",
+ "text-color": "#ffb86c"
+ },
+ "Inline Image: Link": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Link": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "List: Emphasis Text": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ },
+ "List: Link": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "List: Strong Text": {
+ "selected-text-color": "#ffb86c",
+ "text-color": "#ffb86c"
+ },
+ "Normal Text: Link": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Reference Image": {
+ "selected-text-color": "#ffb86c",
+ "text-color": "#ffb86c"
+ },
+ "Reference-Link": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Reference-Link Name": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6",
+ "underline": false
+ },
+ "Reference-Link Target": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "Reference-Link Target: Link": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Reference-Link: Link": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Strong Text": {
+ "selected-text-color": "#ffb86c",
+ "text-color": "#ffb86c"
+ }
+ },
+ "Modelines": {
+ "Variable": {
+ "selected-text-color": "#c58e53",
+ "text-color": "#c58e53"
+ }
+ },
+ "PHP/PHP": {
+ "Backslash Code": {
+ "bold": false,
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "Control Structures": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "Library Constant": {
+ "bold": false,
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "Special Variable": {
+ "bold": false,
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "Variable": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ }
+ },
+ "Python": {
+ "Builtin Function": {
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ },
+ "Special Variable": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ }
+ },
+ "QMake": {
+ "Backslash Code": {
+ "bold": false
+ },
+ "Predefined Variable": {
+ "bold": false,
+ "selected-text-color": "#ffb86c",
+ "text-color": "#ffb86c"
+ }
+ },
+ "Ruby": {
+ "Access Control": {
+ "bold": false,
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ },
+ "Default globals": {
+ "bold": false
+ },
+ "Definition": {
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ },
+ "Global Constant": {
+ "bold": false,
+ "italic": true,
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Kernel methods": {
+ "bold": false,
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Message": {
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ },
+ "Module mixin methods": {
+ "bold": false
+ },
+ "Pseudo variable": {
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ }
+ },
+ "Rust": {
+ "Attribute": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "CConstant": {
+ "bold": false
+ },
+ "CType": {
+ "italic": true,
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Constant": {
+ "bold": false
+ },
+ "Definition": {
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ },
+ "Lifetime": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "Macro": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Scope": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "Self": {
+ "italic": true,
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "Trait": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ }
+ },
+ "SPDX-Comments": {
+ "SPDX Deprecated License": {
+ "selected-text-color": "#d465a7",
+ "text-color": "#d465a7"
+ },
+ "SPDX Deprecated License Exception": {
+ "selected-text-color": "#d465a7",
+ "text-color": "#d465a7"
+ },
+ "SPDX License": {
+ "selected-text-color": "#d465a7",
+ "text-color": "#d465a7"
+ },
+ "SPDX License Exception": {
+ "selected-text-color": "#d465a7",
+ "text-color": "#d465a7"
+ },
+ "SPDX Tag": {
+ "selected-text-color": "#d465a7",
+ "text-color": "#d465a7"
+ },
+ "SPDX Value": {
+ "selected-text-color": "#d465a7",
+ "text-color": "#d465a7"
+ }
+ },
+ "TypeScript": {
+ "Built-in Objects": {
+ "italic": true
+ },
+ "Function (Built-in)": {
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ },
+ "Object Member": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ }
+ },
+ "TypeScript React (TSX)": {
+ "Attribute": {
+ "italic": true
+ },
+ "Component Tag": {
+ "bold": false,
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ }
+ },
+ "YAML": {
+ "Attribute": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ },
+ "Key": {
+ "bold": false,
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "List": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ }
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor": "#282a36",
+ "BracketMatching": "#7c62a5",
+ "CodeFolding": "#44475a",
+ "CurrentLine": "#44475a",
+ "CurrentLineNumber": "#f8f8f2",
+ "IconBorder": "#282a36",
+ "IndentationLine": "#6272a4",
+ "LineNumbers": "#6272a4",
+ "MarkBookmark": "#8be9fd",
+ "MarkBreakpointActive": "#ff5555",
+ "MarkBreakpointDisabled": "#bd93f9",
+ "MarkBreakpointReached": "#f1fa8c",
+ "MarkError": "#ff5555",
+ "MarkExecution": "#44475a",
+ "MarkWarning": "#ffb86c",
+ "ModifiedLines": "#ff79c6",
+ "ReplaceHighlight": "#2c8843",
+ "SavedLines": "#50fa7b",
+ "SearchHighlight": "#566591",
+ "Separator": "#45474e",
+ "SpellChecking": "#ff5555",
+ "TabMarker": "#6272a4",
+ "TemplateBackground": "#282a36",
+ "TemplateFocusedPlaceholder": "#282a36",
+ "TemplatePlaceholder": "#282a36",
+ "TemplateReadOnlyPlaceholder": "#44475a",
+ "TextSelection": "#44475a",
+ "WordWrapMarker": "#282a36"
+ },
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2016 Dracula Theme",
+ "SPDX-FileCopyrightText: 2020 Christoph Cullmann <cullmann@kde.org>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "Dracula",
+ "revision": 8
+ },
+ "text-styles": {
+ "Alert": {
+ "bold": true,
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555"
+ },
+ "Annotation": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "Attribute": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "BaseN": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Char": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ },
+ "Comment": {
+ "selected-text-color": "#6272a4",
+ "text-color": "#6272a4"
+ },
+ "CommentVar": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Constant": {
+ "bold": true,
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "ControlFlow": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "DataType": {
+ "italic": true,
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "DecVal": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "Documentation": {
+ "selected-text-color": "#ffb86c",
+ "text-color": "#ffb86c"
+ },
+ "Error": {
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555",
+ "underline": true
+ },
+ "Extension": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "Float": {
+ "selected-text-color": "#bd93f9",
+ "text-color": "#bd93f9"
+ },
+ "Function": {
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ },
+ "Import": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "Information": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ },
+ "Keyword": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "Normal": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "Operator": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "Others": {
+ "selected-text-color": "#50fa7b",
+ "text-color": "#50fa7b"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "RegionMarker": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#ff79c6",
+ "text-color": "#ff79c6"
+ },
+ "SpecialString": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ },
+ "String": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ },
+ "Variable": {
+ "selected-text-color": "#8be9fd",
+ "text-color": "#8be9fd"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#d7e60a",
+ "text-color": "#d7e60a"
+ },
+ "Warning": {
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/falcon.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/falcon.theme
new file mode 100644
index 0000000000..1ebf8c6946
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/falcon.theme
@@ -0,0 +1,184 @@
+{
+ "_comments": [
+ "A theme focused on ergonomics, using the 3 digit Tango color palette"
+ ],
+ "metadata": {
+ "name": "Falcon",
+ "revision": 4,
+ "license": "SPDX-License-Identifier: MIT",
+ "copyright": [ "SPDX-FileCopyrightText: 2021 Alberto Salvia Novella <https://es20490446e.wordpress.com>" ]
+ },
+ "custom-styles": {
+ "Bash": {
+ "Normal Text": {
+ "text-color": "#bbbbbb"
+ }
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor": "#223333",
+ "BracketMatching": "#555555",
+ "CodeFolding": "#224488",
+ "CurrentLine": "#555555",
+ "CurrentLineNumber": "#bbbbbb",
+ "IconBorder": "#223333",
+ "IndentationLine": "#888888",
+ "LineNumbers": "#888888",
+ "MarkBookmark": "#7799cc",
+ "MarkBreakpointActive": "#ffaa33",
+ "MarkBreakpointDisabled": "#aa77aa",
+ "MarkBreakpointReached": "#449900",
+ "MarkError": "#ef2929",
+ "MarkExecution": "#888888",
+ "MarkWarning": "#aa77aa",
+ "ModifiedLines": "#aa0000",
+ "ReplaceHighlight": "#224488",
+ "SavedLines": "#449900",
+ "SearchHighlight": "#224488",
+ "Separator": "#555753",
+ "SpellChecking": "#ef2929",
+ "TabMarker": "#555555",
+ "TemplateBackground": "#223333",
+ "TemplateFocusedPlaceholder": "#23321a",
+ "TemplatePlaceholder": "#23321a",
+ "TemplateReadOnlyPlaceholder": "#451e1a",
+ "TextSelection": "#3366aa",
+ "WordWrapMarker": "#555753"
+ },
+ "text-styles": {
+ "Alert": {
+ "background-color": "#320000",
+ "bold": true,
+ "selected-text-color": "#ffaa33",
+ "text-color": "#ee2222"
+ },
+ "Annotation": {
+ "selected-text-color": "#ddddcc",
+ "text-color": "#aa77aa"
+ },
+ "Attribute": {
+ "selected-text-color": "#ddddcc",
+ "text-color": "#aa77aa"
+ },
+ "BaseN": {
+ "selected-text-color": "#ffee44",
+ "text-color": "#eedd00"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#ddddcc",
+ "text-color": "#7799cc"
+ },
+ "Char": {
+ "selected-text-color": "#ffaa33",
+ "text-color": "#ffaa33"
+ },
+ "Comment": {
+ "selected-text-color": "#eeeeec",
+ "text-color": "#888888"
+ },
+ "CommentVar": {
+ "selected-text-color": "#ddddcc",
+ "text-color": "#aa77aa"
+ },
+ "Constant": {
+ "bold": true,
+ "selected-text-color": "#ffee44",
+ "text-color": "#eedd00"
+ },
+ "ControlFlow": {
+ "bold": true,
+ "selected-text-color": "#ffee44",
+ "text-color": "#eedd00"
+ },
+ "DataType": {
+ "selected-text-color": "#ddddcc",
+ "text-color": "#7799cc"
+ },
+ "DecVal": {
+ "selected-text-color": "#ffee44",
+ "text-color": "#eedd00"
+ },
+ "Documentation": {
+ "selected-text-color": "#eeeeee",
+ "text-color": "#d3d7cf"
+ },
+ "Error": {
+ "selected-text-color": "#ffaa33",
+ "text-color": "#ee2222",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#ddddcc",
+ "text-color": "#7799cc"
+ },
+ "Float": {
+ "selected-text-color": "#ffaa33",
+ "text-color": "#ffaa33"
+ },
+ "Function": {
+ "bold": true,
+ "selected-text-color": "#ddddcc",
+ "text-color": "#7799cc"
+ },
+ "Import": {
+ "selected-text-color": "#88ee33",
+ "text-color": "#88ee33"
+ },
+ "Information": {
+ "selected-text-color": "#eeeeee",
+ "text-color": "#ddddcc"
+ },
+ "Keyword": {
+ "bold": true,
+ "selected-text-color": "#ddddcc",
+ "text-color": "#aa77aa"
+ },
+ "Normal": {
+ "selected-text-color": "#eeeeee",
+ "text-color": "#ddddcc"
+ },
+ "Operator": {
+ "selected-text-color": "#ffee44",
+ "text-color": "#eedd00"
+ },
+ "Others": {
+ "selected-text-color": "#ffee44",
+ "text-color": "#eedd00"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#ddddcc",
+ "text-color": "#aa77aa"
+ },
+ "RegionMarker": {
+ "background-color": "#0d1932",
+ "selected-text-color": "#ddddcc",
+ "text-color": "#7799cc"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#ffaa33",
+ "text-color": "#ffaa33"
+ },
+ "SpecialString": {
+ "selected-text-color": "#eeeeee",
+ "text-color": "#ddddcc"
+ },
+ "String": {
+ "selected-text-color": "#eeeeee",
+ "text-color": "#ddddcc"
+ },
+ "Variable": {
+ "bold": true,
+ "selected-text-color": "#ffaa33",
+ "text-color": "#ffaa33"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#ffaa33",
+ "text-color": "#ffaa33"
+ },
+ "Warning": {
+ "selected-text-color": "#ffee44",
+ "text-color": "#eedd00"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/github-dark.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/github-dark.theme
new file mode 100644
index 0000000000..a699638532
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/github-dark.theme
@@ -0,0 +1,216 @@
+{
+ "custom-styles": {
+ "INI Files": {
+ "Section": {
+ "selected-text-color": "#b392f0",
+ "text-color": "#b392f0"
+ }
+ },
+ "Python": {
+ "Builtin Function": {
+ "selected-text-color": "#b392f0",
+ "text-color": "#b392f0"
+ },
+ "Import": {
+ "selected-text-color": "#f97583",
+ "text-color": "#f97583"
+ },
+ "Special Variable": {
+ "selected-text-color": "#79b8ff",
+ "text-color": "#79b8ff"
+ }
+ },
+ "Rust": {
+ "Attribute": {
+ "selected-text-color": "#e1e4e8",
+ "text-color": "#e1e4e8"
+ },
+ "Macro": {
+ "selected-text-color": "#79b8ff",
+ "text-color": "#79b8ff"
+ },
+ "Self": {
+ "selected-text-color": "#79b8ff",
+ "text-color": "#79b8ff"
+ },
+ "Trait": {
+ "selected-text-color": "#e1e4e8",
+ "text-color": "#e1e4e8"
+ }
+ },
+ "XML": {
+ "Element": {
+ "selected-text-color": "#85e89d",
+ "text-color": "#85e89d"
+ }
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor": "#24292e",
+ "BracketMatching": "#65676a",
+ "CodeFolding": "#253749",
+ "CurrentLine": "#2b3036",
+ "CurrentLineNumber": "#e1e4e8",
+ "IconBorder": "#24292e",
+ "IndentationLine": "#d7dbe0",
+ "LineNumbers": "#444d56",
+ "MarkBookmark": "#8be9fd",
+ "MarkBreakpointActive": "#ff5555",
+ "MarkBreakpointDisabled": "#bd93f9",
+ "MarkBreakpointReached": "#f1fa8c",
+ "MarkError": "#b31d28",
+ "MarkExecution": "#44475a",
+ "MarkWarning": "#ffab70",
+ "ModifiedLines": "#f97583",
+ "ReplaceHighlight": "#40c661",
+ "SavedLines": "#28a745",
+ "SearchHighlight": "#404030",
+ "Separator": "#1b1f23",
+ "SpellChecking": "#ff5555",
+ "TabMarker": "#444d56",
+ "TemplateBackground": "#23241e",
+ "TemplateFocusedPlaceholder": "#22231d",
+ "TemplatePlaceholder": "#22231d",
+ "TemplateReadOnlyPlaceholder": "#262721",
+ "TextSelection": "#253749",
+ "WordWrapMarker": "#2f3031"
+ },
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2020 GitHub Inc.",
+ "SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "GitHub Dark",
+ "revision": 3
+ },
+ "text-styles": {
+ "Alert": {
+ "bold": true,
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555"
+ },
+ "Annotation": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "Attribute": {
+ "selected-text-color": "#f97583",
+ "text-color": "#f97583"
+ },
+ "BaseN": {
+ "selected-text-color": "#79b8ff",
+ "text-color": "#79b8ff"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#f97583",
+ "text-color": "#f97583"
+ },
+ "Char": {
+ "selected-text-color": "#9ecbff",
+ "text-color": "#9ecbff"
+ },
+ "Comment": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "CommentVar": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "Constant": {
+ "selected-text-color": "#79b8ff",
+ "text-color": "#79b8ff"
+ },
+ "ControlFlow": {
+ "selected-text-color": "#f97583",
+ "text-color": "#f97583"
+ },
+ "DataType": {
+ "selected-text-color": "#f97583",
+ "text-color": "#f97583"
+ },
+ "DecVal": {
+ "selected-text-color": "#79b8ff",
+ "text-color": "#79b8ff"
+ },
+ "Documentation": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "Error": {
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#f97583",
+ "text-color": "#f97583"
+ },
+ "Float": {
+ "selected-text-color": "#79b8ff",
+ "text-color": "#79b8ff"
+ },
+ "Function": {
+ "selected-text-color": "#b392f0",
+ "text-color": "#b392f0"
+ },
+ "Import": {
+ "selected-text-color": "#9ecbff",
+ "text-color": "#9ecbff"
+ },
+ "Information": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "Keyword": {
+ "selected-text-color": "#f97583",
+ "text-color": "#f97583"
+ },
+ "Normal": {
+ "selected-text-color": "#e1e4e8",
+ "text-color": "#e1e4e8"
+ },
+ "Operator": {
+ "selected-text-color": "#e1e4e8",
+ "text-color": "#e1e4e8"
+ },
+ "Others": {
+ "selected-text-color": "#b392f0",
+ "text-color": "#b392f0"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#f97583",
+ "text-color": "#f97583"
+ },
+ "RegionMarker": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#79b8ff",
+ "text-color": "#79b8ff"
+ },
+ "SpecialString": {
+ "selected-text-color": "#9ecbff",
+ "text-color": "#9ecbff"
+ },
+ "String": {
+ "selected-text-color": "#9ecbff",
+ "text-color": "#9ecbff"
+ },
+ "Variable": {
+ "selected-text-color": "#ffab70",
+ "text-color": "#ffab70"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#41a0ff",
+ "text-color": "#41a0ff"
+ },
+ "Warning": {
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/github-light.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/github-light.theme
new file mode 100644
index 0000000000..ed56418fde
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/github-light.theme
@@ -0,0 +1,216 @@
+{
+ "custom-styles": {
+ "INI Files": {
+ "Section": {
+ "selected-text-color": "#6f42c1",
+ "text-color": "#6f42c1"
+ }
+ },
+ "Python": {
+ "Builtin Function": {
+ "selected-text-color": "#6f42c1",
+ "text-color": "#6f42c1"
+ },
+ "Import": {
+ "selected-text-color": "#d73a49",
+ "text-color": "#d73a49"
+ },
+ "Special Variable": {
+ "selected-text-color": "#005cc5",
+ "text-color": "#005cc5"
+ }
+ },
+ "Rust": {
+ "Attribute": {
+ "selected-text-color": "#24292e",
+ "text-color": "#24292e"
+ },
+ "Macro": {
+ "selected-text-color": "#005cc5",
+ "text-color": "#005cc5"
+ },
+ "Self": {
+ "selected-text-color": "#005cc5",
+ "text-color": "#005cc5"
+ },
+ "Trait": {
+ "selected-text-color": "#24292e",
+ "text-color": "#24292e"
+ }
+ },
+ "XML": {
+ "Element": {
+ "selected-text-color": "#22863a",
+ "text-color": "#22863a"
+ }
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor": "#ffffff",
+ "BracketMatching": "#bef5cb",
+ "CodeFolding": "#f6f8fa",
+ "CurrentLine": "#f6f8fa",
+ "CurrentLineNumber": "#24292e",
+ "IconBorder": "#ffffff",
+ "IndentationLine": "#d7dbe0",
+ "LineNumbers": "#c7c2bc",
+ "MarkBookmark": "#8be9fd",
+ "MarkBreakpointActive": "#ff5555",
+ "MarkBreakpointDisabled": "#bd93f9",
+ "MarkBreakpointReached": "#f1fa8c",
+ "MarkError": "#b31d28",
+ "MarkExecution": "#44475a",
+ "MarkWarning": "#e36209",
+ "ModifiedLines": "#d73a49",
+ "ReplaceHighlight": "#50fa7b",
+ "SavedLines": "#28a745",
+ "SearchHighlight": "#ffea7f",
+ "Separator": "#e1e4e8",
+ "SpellChecking": "#ff5555",
+ "TabMarker": "#d1d5da",
+ "TemplateBackground": "#23241e",
+ "TemplateFocusedPlaceholder": "#22231d",
+ "TemplatePlaceholder": "#22231d",
+ "TemplateReadOnlyPlaceholder": "#262721",
+ "TextSelection": "#dee6fc",
+ "WordWrapMarker": "#e1e4e8"
+ },
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2020 GitHub Inc.",
+ "SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "GitHub Light",
+ "revision": 3
+ },
+ "text-styles": {
+ "Alert": {
+ "bold": true,
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555"
+ },
+ "Annotation": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "Attribute": {
+ "selected-text-color": "#d73a49",
+ "text-color": "#d73a49"
+ },
+ "BaseN": {
+ "selected-text-color": "#005cc5",
+ "text-color": "#005cc5"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#d73a49",
+ "text-color": "#d73a49"
+ },
+ "Char": {
+ "selected-text-color": "#032f62",
+ "text-color": "#032f62"
+ },
+ "Comment": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "CommentVar": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "Constant": {
+ "selected-text-color": "#005cc5",
+ "text-color": "#005cc5"
+ },
+ "ControlFlow": {
+ "selected-text-color": "#d73a49",
+ "text-color": "#d73a49"
+ },
+ "DataType": {
+ "selected-text-color": "#d73a49",
+ "text-color": "#d73a49"
+ },
+ "DecVal": {
+ "selected-text-color": "#005cc5",
+ "text-color": "#005cc5"
+ },
+ "Documentation": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "Error": {
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#d73a49",
+ "text-color": "#d73a49"
+ },
+ "Float": {
+ "selected-text-color": "#005cc5",
+ "text-color": "#005cc5"
+ },
+ "Function": {
+ "selected-text-color": "#6f42c1",
+ "text-color": "#6f42c1"
+ },
+ "Import": {
+ "selected-text-color": "#032f62",
+ "text-color": "#032f62"
+ },
+ "Information": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "Keyword": {
+ "selected-text-color": "#d73a49",
+ "text-color": "#d73a49"
+ },
+ "Normal": {
+ "selected-text-color": "#24292e",
+ "text-color": "#24292e"
+ },
+ "Operator": {
+ "selected-text-color": "#24292e",
+ "text-color": "#24292e"
+ },
+ "Others": {
+ "selected-text-color": "#6f42c1",
+ "text-color": "#6f42c1"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#d73a49",
+ "text-color": "#d73a49"
+ },
+ "RegionMarker": {
+ "selected-text-color": "#6a737d",
+ "text-color": "#6a737d"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#005cc5",
+ "text-color": "#005cc5"
+ },
+ "SpecialString": {
+ "selected-text-color": "#032f62",
+ "text-color": "#032f62"
+ },
+ "String": {
+ "selected-text-color": "#032f62",
+ "text-color": "#032f62"
+ },
+ "Variable": {
+ "selected-text-color": "#e36209",
+ "text-color": "#e36209"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#034c95",
+ "text-color": "#034c95"
+ },
+ "Warning": {
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/gruvbox-dark.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/gruvbox-dark.theme
new file mode 100644
index 0000000000..0e006ab46e
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/gruvbox-dark.theme
@@ -0,0 +1,185 @@
+{
+ "_comments": [
+ "Last update: Jul 28, 2021 (revision 3)",
+ "This file has been converted from: https://github.com/morhetz/gruvbox"
+ ],
+ "metadata" : {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2017 Pavel Pertsev <morhetz@gmail.com>",
+ "SPDX-FileCopyrightText: 2020 Frederik Banning <laubblaeser@live.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name" : "gruvbox Dark",
+ "revision" : 3
+ },
+ "text-styles": {
+ "Normal" : {
+ "text-color" : "#ebdbb2",
+ "selected-text-color" : "#ebdbb2",
+ "bold" : false,
+ "italic" : false,
+ "underline" : false,
+ "strike-through" : false
+ },
+ "Keyword" : {
+ "text-color" : "#ebdbb2",
+ "selected-text-color" : "#ebdbb2",
+ "bold" : true
+ },
+ "Function" : {
+ "text-color" : "#689d6a",
+ "selected-text-color" : "#8ec07c"
+ },
+ "Variable" : {
+ "text-color" : "#458588",
+ "selected-text-color" : "#83a598"
+ },
+ "ControlFlow" : {
+ "text-color" : "#cc241d",
+ "selected-text-color" : "#fb4934",
+ "bold" : true
+ },
+ "Operator" : {
+ "text-color" : "#ebdbb2",
+ "selected-text-color" : "#ebdbb2"
+ },
+ "BuiltIn" : {
+ "text-color" : "#d65d0e",
+ "selected-text-color" : "#fe8019"
+ },
+ "Extension" : {
+ "text-color" : "#689d6a",
+ "selected-text-color" : "#8ec07c",
+ "bold" : true
+ },
+ "Preprocessor" : {
+ "text-color" : "#d65d0e",
+ "selected-text-color" : "#fe8019"
+ },
+ "Attribute" : {
+ "text-color" : "#d79921",
+ "selected-text-color" : "#fabd2f"
+ },
+ "Char" : {
+ "text-color" : "#b16286",
+ "selected-text-color" : "#d3869b"
+ },
+ "SpecialChar" : {
+ "text-color" : "#b16286",
+ "selected-text-color" : "#d3869b"
+ },
+ "String" : {
+ "text-color" : "#98971a",
+ "selected-text-color" : "#b8bb26"
+ },
+ "VerbatimString" : {
+ "text-color" : "#848216",
+ "selected-text-color" : "#b8bb26"
+ },
+ "SpecialString" : {
+ "text-color" : "#98971a",
+ "selected-text-color" : "#b8bb26"
+ },
+ "Import" : {
+ "text-color" : "#689d6a",
+ "selected-text-color" : "#8ec07c"
+ },
+ "DataType" : {
+ "text-color" : "#d79921",
+ "selected-text-color" : "#fabd2f"
+ },
+ "DecVal" : {
+ "text-color" : "#f67400",
+ "selected-text-color" : "#f67400"
+ },
+ "BaseN" : {
+ "text-color" : "#f67400",
+ "selected-text-color" : "#f67400"
+ },
+ "Float" : {
+ "text-color" : "#f67400",
+ "selected-text-color" : "#f67400"
+ },
+ "Constant" : {
+ "text-color" : "#b16286",
+ "selected-text-color" : "#d3869b",
+ "bold" : true
+ },
+ "Comment" : {
+ "text-color" : "#928374",
+ "selected-text-color" : "#a89984"
+ },
+ "Documentation" : {
+ "text-color" : "#98971a",
+ "selected-text-color" : "#b8bb26"
+ },
+ "Annotation" : {
+ "text-color" : "#98971a",
+ "selected-text-color" : "#b8bb26"
+ },
+ "CommentVar" : {
+ "text-color" : "#928374",
+ "selected-text-color" : "#a89984"
+ },
+ "RegionMarker" : {
+ "text-color" : "#928374",
+ "selected-text-color" : "#a89984",
+ "background-color" : "#1d2021"
+ },
+ "Information" : {
+ "text-color" : "#282828",
+ "selected-text-color" : "#282828",
+ "background-color" : "#83a598"
+ },
+ "Warning" : {
+ "text-color" : "#282828",
+ "selected-text-color" : "#282828",
+ "background-color" : "#fabd2f"
+ },
+ "Alert" : {
+ "text-color" : "#282828",
+ "selected-text-color" : "#282828",
+ "background-color" : "#cc241d",
+ "bold" : true
+ },
+ "Error" : {
+ "text-color" : "#cc241d",
+ "selected-text-color" : "#fb4934",
+ "underline" : true
+ },
+ "Others" : {
+ "text-color" : "#689d6a",
+ "selected-text-color" : "#8ec07c"
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor" : "#282828",
+ "CodeFolding" : "#1d2021",
+ "BracketMatching" : "#a89984",
+ "CurrentLine" : "#32302f",
+ "IconBorder" : "#282828",
+ "IndentationLine" : "#504945",
+ "LineNumbers" : "#ebdbb2",
+ "CurrentLineNumber" : "#ebdbb2",
+ "MarkBookmark" : "#458588",
+ "MarkBreakpointActive" : "#cc241d",
+ "MarkBreakpointReached" : "#98971a",
+ "MarkBreakpointDisabled" : "#b16286",
+ "MarkExecution" : "#ebdbb2",
+ "MarkWarning" : "#d65d0e",
+ "MarkError" : "#cc241d",
+ "ModifiedLines" : "#fe8019",
+ "ReplaceHighlight" : "#b8bb26",
+ "SavedLines" : "#689d6a",
+ "SearchHighlight" : "#8ec07c",
+ "TextSelection" : "#504945",
+ "Separator" : "#504945",
+ "SpellChecking" : "#cc241d",
+ "TabMarker" : "#504945",
+ "TemplateBackground" : "#282828",
+ "TemplatePlaceholder" : "#98971a",
+ "TemplateFocusedPlaceholder" : "#b8bb26",
+ "TemplateReadOnlyPlaceholder" : "#fb4934",
+ "WordWrapMarker" : "#a89984"
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/gruvbox-light.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/gruvbox-light.theme
new file mode 100644
index 0000000000..937a43d084
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/gruvbox-light.theme
@@ -0,0 +1,185 @@
+{
+ "_comments": [
+ "Last update: Jul 28, 2021 (revision 3)",
+ "This file has been converted from: https://github.com/morhetz/gruvbox"
+ ],
+ "metadata" : {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2017 Pavel Pertsev <morhetz@gmail.com>",
+ "SPDX-FileCopyrightText: 2020 Frederik Banning <laubblaeser@live.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name" : "gruvbox Light",
+ "revision" : 3
+ },
+ "text-styles": {
+ "Normal" : {
+ "text-color" : "#3c3836",
+ "selected-text-color" : "#3c3836",
+ "bold" : false,
+ "italic" : false,
+ "underline" : false,
+ "strike-through" : false
+ },
+ "Keyword" : {
+ "text-color" : "#3c3836",
+ "selected-text-color" : "#3c3836",
+ "bold" : true
+ },
+ "Function" : {
+ "text-color" : "#689d6a",
+ "selected-text-color" : "#427b58"
+ },
+ "Variable" : {
+ "text-color" : "#458588",
+ "selected-text-color" : "#076678"
+ },
+ "ControlFlow" : {
+ "text-color" : "#cc241d",
+ "selected-text-color" : "#9d0006",
+ "bold" : true
+ },
+ "Operator" : {
+ "text-color" : "#3c3836",
+ "selected-text-color" : "#3c3836"
+ },
+ "BuiltIn" : {
+ "text-color" : "#d65d0e",
+ "selected-text-color" : "#af3a03"
+ },
+ "Extension" : {
+ "text-color" : "#689d6a",
+ "selected-text-color" : "#427b58",
+ "bold" : true
+ },
+ "Preprocessor" : {
+ "text-color" : "#d65d0e",
+ "selected-text-color" : "#af3a03"
+ },
+ "Attribute" : {
+ "text-color" : "#d79921",
+ "selected-text-color" : "#b57614"
+ },
+ "Char" : {
+ "text-color" : "#b16286",
+ "selected-text-color" : "#8f3f71"
+ },
+ "SpecialChar" : {
+ "text-color" : "#b16286",
+ "selected-text-color" : "#8f3f71"
+ },
+ "String" : {
+ "text-color" : "#98971a",
+ "selected-text-color" : "#79740e"
+ },
+ "VerbatimString" : {
+ "text-color" : "#848216",
+ "selected-text-color" : "#79740e"
+ },
+ "SpecialString" : {
+ "text-color" : "#98971a",
+ "selected-text-color" : "#79740e"
+ },
+ "Import" : {
+ "text-color" : "#689d6a",
+ "selected-text-color" : "#427b58"
+ },
+ "DataType" : {
+ "text-color" : "#d79921",
+ "selected-text-color" : "#b57614"
+ },
+ "DecVal" : {
+ "text-color" : "#f67400",
+ "selected-text-color" : "#f67400"
+ },
+ "BaseN" : {
+ "text-color" : "#f67400",
+ "selected-text-color" : "#f67400"
+ },
+ "Float" : {
+ "text-color" : "#f67400",
+ "selected-text-color" : "#f67400"
+ },
+ "Constant" : {
+ "text-color" : "#b16286",
+ "selected-text-color" : "#8f3f71",
+ "bold" : true
+ },
+ "Comment" : {
+ "text-color" : "#928374",
+ "selected-text-color" : "#a89984"
+ },
+ "Documentation" : {
+ "text-color" : "#98971a",
+ "selected-text-color" : "#79740e"
+ },
+ "Annotation" : {
+ "text-color" : "#98971a",
+ "selected-text-color" : "#79740e"
+ },
+ "CommentVar" : {
+ "text-color" : "#928374",
+ "selected-text-color" : "#a89984"
+ },
+ "RegionMarker" : {
+ "text-color" : "#928374",
+ "selected-text-color" : "#a89984",
+ "background-color" : "#f9f5d7"
+ },
+ "Information" : {
+ "text-color" : "#282828",
+ "selected-text-color" : "#282828",
+ "background-color" : "#83a598"
+ },
+ "Warning" : {
+ "text-color" : "#282828",
+ "selected-text-color" : "#282828",
+ "background-color" : "#fabd2f"
+ },
+ "Alert" : {
+ "text-color" : "#282828",
+ "selected-text-color" : "#282828",
+ "background-color" : "#cc241d",
+ "bold" : true
+ },
+ "Error" : {
+ "text-color" : "#cc241d",
+ "selected-text-color" : "#9d0006",
+ "underline" : true
+ },
+ "Others" : {
+ "text-color" : "#689d6a",
+ "selected-text-color" : "#427b58"
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor" : "#fbf1c7",
+ "CodeFolding" : "#f9f5d7",
+ "BracketMatching" : "#a89984",
+ "CurrentLine" : "#f2e5bc",
+ "IconBorder" : "#fbf1c7",
+ "IndentationLine" : "#d5c4a1",
+ "LineNumbers" : "#3c3836",
+ "CurrentLineNumber" : "#3c3836",
+ "MarkBookmark" : "#458588",
+ "MarkBreakpointActive" : "#cc241d",
+ "MarkBreakpointReached" : "#98971a",
+ "MarkBreakpointDisabled" : "#b16286",
+ "MarkExecution" : "#3c3836",
+ "MarkWarning" : "#d65d0e",
+ "MarkError" : "#cc241d",
+ "ModifiedLines" : "#af3a03",
+ "ReplaceHighlight" : "#79740e",
+ "SavedLines" : "#689d6a",
+ "SearchHighlight" : "#427b58",
+ "TextSelection" : "#d5c4a1",
+ "Separator" : "#d5c4a1",
+ "SpellChecking" : "#cc241d",
+ "TabMarker" : "#d5c4a1",
+ "TemplateBackground" : "#fbf1c7",
+ "TemplatePlaceholder" : "#98971a",
+ "TemplateFocusedPlaceholder" : "#79740e",
+ "TemplateReadOnlyPlaceholder" : "#9d0006",
+ "WordWrapMarker" : "#a89984"
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/homunculus.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/homunculus.theme
new file mode 100644
index 0000000000..ab48ead170
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/homunculus.theme
@@ -0,0 +1,177 @@
+{
+ "custom-styles": {
+ "Common Lisp": {
+ "Brackets": {
+ "selected-text-color": "#904200",
+ "text-color": "#005f88"
+ }
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor": "#f7f3f3",
+ "BracketMatching": "#fceae2",
+ "CodeFolding": "#94caef",
+ "CurrentLine": "#daecf7",
+ "CurrentLineNumber": "#1e1e1e",
+ "IconBorder": "#f0f0f0",
+ "IndentationLine": "#d2d2d2",
+ "LineNumbers": "#a0a0a0",
+ "MarkBookmark": "#005275",
+ "MarkBreakpointActive": "#ff0000",
+ "MarkBreakpointDisabled": "#ff00ff",
+ "MarkBreakpointReached": "#ffff00",
+ "MarkError": "#ff0000",
+ "MarkExecution": "#a0a0a4",
+ "MarkWarning": "#00ff00",
+ "ModifiedLines": "#fdbc4b",
+ "ReplaceHighlight": "#25d970",
+ "SavedLines": "#2ecc71",
+ "SearchHighlight": "#e0af82",
+ "Separator": "#d5d5d5",
+ "SpellChecking": "#bf0303",
+ "TabMarker": "#d2d2d2",
+ "TemplateBackground": "#d6d2d0",
+ "TemplateFocusedPlaceholder": "#76da98",
+ "TemplatePlaceholder": "#baf8ce",
+ "TemplateReadOnlyPlaceholder": "#f6e6e6",
+ "TextSelection": "#bcbcbc",
+ "WordWrapMarker": "#ededed"
+ },
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2021 shenlebantongying <shenlebantongying@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "Homunculus",
+ "revision": 1
+ },
+ "text-styles": {
+ "Alert": {
+ "background-color": "#f7e6e6",
+ "bold": true,
+ "selected-text-color": "#9c0e0e",
+ "text-color": "#bf0303"
+ },
+ "Annotation": {
+ "selected-text-color": "#000000",
+ "text-color": "#ca60ca"
+ },
+ "Attribute": {
+ "selected-text-color": "#000000",
+ "text-color": "#0057ae"
+ },
+ "BaseN": {
+ "selected-text-color": "#000000",
+ "text-color": "#b08000"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#000000",
+ "text-color": "#644a9b"
+ },
+ "Char": {
+ "selected-text-color": "#000000",
+ "text-color": "#924c9d"
+ },
+ "Comment": {
+ "selected-text-color": "#000000",
+ "text-color": "#505050"
+ },
+ "CommentVar": {
+ "selected-text-color": "#ffffff",
+ "text-color": "#0095ff"
+ },
+ "Constant": {
+ "selected-text-color": "#000000",
+ "text-color": "#aa5500"
+ },
+ "ControlFlow": {
+ "selected-text-color": "#000000",
+ "text-color": "#005f88"
+ },
+ "DataType": {
+ "selected-text-color": "#000000",
+ "text-color": "#0057ae"
+ },
+ "DecVal": {
+ "selected-text-color": "#000000",
+ "text-color": "#b08000"
+ },
+ "Documentation": {
+ "selected-text-color": "#000000",
+ "text-color": "#2a486a"
+ },
+ "Error": {
+ "selected-text-color": "#9c0e0e",
+ "text-color": "#bf0303",
+ "underline": true
+ },
+ "Extension": {
+ "selected-text-color": "#000000",
+ "text-color": "#0095ff"
+ },
+ "Float": {
+ "selected-text-color": "#000000",
+ "text-color": "#b08000"
+ },
+ "Function": {
+ "selected-text-color": "#000000",
+ "text-color": "#721045"
+ },
+ "Import": {
+ "selected-text-color": "#000000",
+ "text-color": "#813e00"
+ },
+ "Information": {
+ "selected-text-color": "#000000",
+ "text-color": "#b08000"
+ },
+ "Keyword": {
+ "selected-text-color": "#000000",
+ "text-color": "#5317ac"
+ },
+ "Normal": {
+ "selected-text-color": "#000000",
+ "text-color": "#303030"
+ },
+ "Operator": {
+ "selected-text-color": "#000000",
+ "text-color": "#b455b4"
+ },
+ "Others": {
+ "selected-text-color": "#000000",
+ "text-color": "#006e28"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#000000",
+ "text-color": "#006e28"
+ },
+ "RegionMarker": {
+ "selected-text-color": "#000000",
+ "text-color": "#0057ae"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#000000",
+ "text-color": "#3daee9"
+ },
+ "SpecialString": {
+ "selected-text-color": "#000000",
+ "text-color": "#854001"
+ },
+ "String": {
+ "selected-text-color": "#000000",
+ "text-color": "#2544bb"
+ },
+ "Variable": {
+ "selected-text-color": "#000000",
+ "text-color": "#0057ae"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#000000",
+ "text-color": "#2544bb"
+ },
+ "Warning": {
+ "selected-text-color": "#9c0e0e",
+ "text-color": "#bf0303"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/monokai.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/monokai.theme
new file mode 100644
index 0000000000..e0026ca3b3
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/monokai.theme
@@ -0,0 +1,394 @@
+{
+ "custom-styles": {
+ "C": {
+ "Prep. Lib": {
+ "selected-text-color": "#e6db74",
+ "text-color": "#e6db74"
+ }
+ },
+ "C++": {
+ "Qt Classes": {
+ "bold": false,
+ "selected-text-color": "#66d9ef",
+ "text-color": "#66d9ef"
+ },
+ "Qt Macros": {
+ "bold": false,
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ },
+ "Qt Types": {
+ "bold": false,
+ "selected-text-color": "#66D9EF",
+ "text-color": "#66D9EF"
+ }
+ },
+ "CMake": {
+ "Builtin Variable": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "False Special Arg": {
+ "selected-text-color": "#e03232",
+ "text-color": "#e03232"
+ },
+ "True Special Arg": {
+ "selected-text-color": "#38bd38",
+ "text-color": "#38bd38"
+ }
+ },
+ "Doxygen": {
+ "Tags": {
+ "bold": false,
+ "selected-text-color": "#52afbf",
+ "text-color": "#52afbf"
+ },
+ "Word": {
+ "bold": false,
+ "selected-text-color": "#7ba822",
+ "text-color": "#7ba822",
+ "underline": true
+ }
+ },
+ "Diff": {
+ "Added line": {
+ "selected-text-color": "#a6e22e",
+ "text-color": "#a6e22e"
+ },
+ "Changed line (new)": {
+ "selected-text-color": "#a6e22e",
+ "text-color": "#a6e22e"
+ },
+ "Changed line (old)": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ },
+ "Removed line": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ }
+ },
+ "Go": {
+ "Builtin Function": {
+ "selected-text-color": "#a6e22e",
+ "text-color": "#a6e22e"
+ },
+ "Predeclared Identifier": {
+ "selected-text-color": "#ae81ff",
+ "text-color": "#ae81ff"
+ }
+ },
+ "ISO C++": {
+ "Attribute": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "Boost Stuff": {
+ "bold": false
+ },
+ "Prep. Lib": {
+ "selected-text-color": "#e6db74",
+ "text-color": "#e6db74"
+ },
+ "Standard Attribute": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "Standard Macros": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ },
+ "Standard Suffix": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ },
+ "UDL Numeric Suffix": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ },
+ "UDL String Suffix": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ }
+ },
+ "JSON": {
+ "Style_String_Key": {
+ "italic": false,
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ }
+ },
+ "JavaScript": {
+ "Object Member": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "Substitution": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ }
+ },
+ "JavaScript React (JSX)": {
+ "Component Tag": {
+ "bold": false,
+ "selected-text-color": "#66d9ef",
+ "text-color": "#66d9ef"
+ }
+ },
+ "Makefile": {
+ "FuncParam": {
+ "selected-text-color": "#fd971f",
+ "text-color": "#fd971f"
+ },
+ "Target": {
+ "selected-text-color": "#a6e22e",
+ "text-color": "#a6e22e"
+ },
+ "Variable": {
+ "italic": false,
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ }
+ },
+ "Markdown": {
+ "Emphasis Text": {
+ "selected-text-color": "#66D9EF",
+ "text-color": "#66D9EF"
+ },
+ "Reference-Link ID": {
+ "selected-text-color": "#ae81ff",
+ "text-color": "#ae81ff"
+ },
+ "Reference-Link Name": {
+ "selected-text-color": "#ae81ff",
+ "text-color": "#ae81ff"
+ },
+ "Reference-Link Target": {
+ "selected-text-color": "#ae81ff",
+ "text-color": "#ae81ff"
+ },
+ "Strong Text": {
+ "selected-text-color": "#66d9ef",
+ "text-color": "#66d9ef"
+ }
+ },
+ "Python": {
+ "Import": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ }
+ },
+ "Rust": {
+ "Definition": {
+ "selected-text-color": "#a6e22e",
+ "text-color": "#a6e22e"
+ },
+ "Lifetime": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ },
+ "Macro": {
+ "selected-text-color": "#a6e22e",
+ "text-color": "#a6e22e"
+ },
+ "Self": {
+ "selected-text-color": "#fd971f",
+ "text-color": "#fd971f"
+ }
+ },
+ "TypeScript": {
+ "Function (Built-in)": {
+ "selected-text-color": "#a6e22e",
+ "text-color": "#a6e22e"
+ },
+ "Object Member": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "Substitution": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ }
+ },
+ "TypeScript React (TSX)": {
+ "Component Tag": {
+ "bold": false,
+ "selected-text-color": "#66d9ef",
+ "text-color": "#66d9ef"
+ },
+ "Substitution": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ }
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor": "#272822",
+ "BracketMatching": "#5b5a4a",
+ "CodeFolding": "#3a3b32",
+ "CurrentLine": "#3e3d32",
+ "CurrentLineNumber": "#d1d931",
+ "IconBorder": "#272822",
+ "IndentationLine": "#6272a4",
+ "LineNumbers": "#909194",
+ "MarkBookmark": "#66D9EF",
+ "MarkBreakpointActive": "#ff5555",
+ "MarkBreakpointDisabled": "#bd93f9",
+ "MarkBreakpointReached": "#f1fa8c",
+ "MarkError": "#ff5555",
+ "MarkExecution": "#44475a",
+ "MarkWarning": "#ffb86c",
+ "ModifiedLines": "#ff473d",
+ "ReplaceHighlight": "#735d16",
+ "SavedLines": "#20e852",
+ "SearchHighlight": "#245676",
+ "Separator": "#45474e",
+ "SpellChecking": "#ff5555",
+ "TabMarker": "#6272a4",
+ "TemplateBackground": "#23241e",
+ "TemplateFocusedPlaceholder": "#22231d",
+ "TemplatePlaceholder": "#22231d",
+ "TemplateReadOnlyPlaceholder": "#262721",
+ "TextSelection": "#3f413e",
+ "WordWrapMarker": "#282a36"
+ },
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2006 Wimer Hazenberg",
+ "SPDX-FileCopyrightText: 2020 Waqar Ahmed <waqar.17a@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "Monokai",
+ "revision": 6
+ },
+ "text-styles": {
+ "Alert": {
+ "bold": true,
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555"
+ },
+ "Annotation": {
+ "selected-text-color": "#75715e",
+ "text-color": "#75715e"
+ },
+ "Attribute": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ },
+ "BaseN": {
+ "selected-text-color": "#ae81ff",
+ "text-color": "#ae81ff"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#66D9EF",
+ "text-color": "#66D9EF"
+ },
+ "Char": {
+ "selected-text-color": "#e6db74",
+ "text-color": "#e6db74"
+ },
+ "Comment": {
+ "selected-text-color": "#75715e",
+ "text-color": "#75715e"
+ },
+ "CommentVar": {
+ "selected-text-color": "#75715e",
+ "text-color": "#75715e"
+ },
+ "Constant": {
+ "selected-text-color": "#ae81ff",
+ "text-color": "#ae81ff"
+ },
+ "ControlFlow": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ },
+ "DataType": {
+ "italic": true,
+ "selected-text-color": "#66d9ef",
+ "text-color": "#66d9ef"
+ },
+ "DecVal": {
+ "selected-text-color": "#ae81ff",
+ "text-color": "#ae81ff"
+ },
+ "Documentation": {
+ "selected-text-color": "#75715e",
+ "text-color": "#75715e"
+ },
+ "Error": {
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#a6e22e",
+ "text-color": "#a6e22e"
+ },
+ "Float": {
+ "selected-text-color": "#ae81ff",
+ "text-color": "#ae81ff"
+ },
+ "Function": {
+ "selected-text-color": "#a6e22e",
+ "text-color": "#a6e22e"
+ },
+ "Import": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ },
+ "Information": {
+ "selected-text-color": "#f1fa8c",
+ "text-color": "#f1fa8c"
+ },
+ "Keyword": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ },
+ "Normal": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "Operator": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "Others": {
+ "selected-text-color": "#a6e22e",
+ "text-color": "#a6e22e"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#f92672",
+ "text-color": "#f92672"
+ },
+ "RegionMarker": {
+ "selected-text-color": "#75715e",
+ "text-color": "#75715e"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#ae81ff",
+ "text-color": "#ae81ff"
+ },
+ "SpecialString": {
+ "selected-text-color": "#e6db74",
+ "text-color": "#e6db74"
+ },
+ "String": {
+ "selected-text-color": "#e6db74",
+ "text-color": "#e6db74"
+ },
+ "Variable": {
+ "selected-text-color": "#f8f8f2",
+ "text-color": "#f8f8f2"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#d8c72c",
+ "text-color": "#d8c72c"
+ },
+ "Warning": {
+ "selected-text-color": "#ff5555",
+ "text-color": "#ff5555"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/nord.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/nord.theme
new file mode 100644
index 0000000000..dbe44d9b09
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/nord.theme
@@ -0,0 +1,181 @@
+{
+ "_comments": [
+ "Last update: Jul 28, 2020 (revision 3)",
+ "This theme has been adapted from: https://www.nordtheme.com"
+ ],
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2016 Arctic Ice Studio <development@arcticicestudio.com>",
+ "SPDX-FileCopyrightText: 2016 Sven Greb <development@svengreb.de>",
+ "SPDX-FileCopyrightText: 2020 Nibaldo González <nibgonz@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "Nord",
+ "revision": 3
+ },
+ "editor-colors": {
+ "BackgroundColor": "#2e3440",
+ "BracketMatching": "#4c566a",
+ "CodeFolding": "#434c5e",
+ "CurrentLine": "#3b4252",
+ "CurrentLineNumber": "#616e88",
+ "IconBorder": "#2e3440",
+ "IndentationLine": "#434c5e",
+ "LineNumbers": "#4c566a",
+ "MarkBookmark": "#55749a",
+ "MarkBreakpointActive": "#79aab9",
+ "MarkBreakpointDisabled": "#b48ead",
+ "MarkBreakpointReached": "#91a97e",
+ "MarkError": "#a95862",
+ "MarkExecution": "#4c566a",
+ "MarkWarning": "#d0b47d",
+ "ModifiedLines": "#ebcb8b",
+ "ReplaceHighlight": "#8e6057",
+ "SavedLines": "#a3be8c",
+ "SearchHighlight": "#5f8491",
+ "Separator": "#3b4252",
+ "SpellChecking": "#bf616a",
+ "TabMarker": "#3e4656",
+ "TemplateBackground": "#3b4252",
+ "TemplateFocusedPlaceholder": "#81a1c1",
+ "TemplatePlaceholder": "#5e81ac",
+ "TemplateReadOnlyPlaceholder": "#4c566a",
+ "TextSelection": "#4c566a",
+ "WordWrapMarker": "#3f4859"
+ },
+ "text-styles": {
+ "Alert": {
+ "background-color": "#3b4252",
+ "bold": true,
+ "selected-text-color": "#bf616a",
+ "text-color": "#bf616a"
+ },
+ "Annotation": {
+ "selected-text-color": "#d08770",
+ "text-color": "#d08770"
+ },
+ "Attribute": {
+ "selected-text-color": "#8fbcbb",
+ "text-color": "#8fbcbb"
+ },
+ "BaseN": {
+ "selected-text-color": "#b48ead",
+ "text-color": "#b48ead"
+ },
+ "BuiltIn": {
+ "italic": true,
+ "selected-text-color": "#88c0d0",
+ "text-color": "#88c0d0"
+ },
+ "Char": {
+ "selected-text-color": "#ebcb8b",
+ "text-color": "#ebcb8b"
+ },
+ "Comment": {
+ "selected-text-color": "#616e88",
+ "text-color": "#616e88"
+ },
+ "CommentVar": {
+ "selected-text-color": "#e5e9f0",
+ "text-color": "#e5e9f0"
+ },
+ "Constant": {
+ "bold": true,
+ "selected-text-color": "#eceff4",
+ "text-color": "#eceff4"
+ },
+ "ControlFlow": {
+ "bold": true,
+ "selected-text-color": "#81a1c1",
+ "text-color": "#81a1c1"
+ },
+ "DataType": {
+ "selected-text-color": "#81a1c1",
+ "text-color": "#81a1c1"
+ },
+ "DecVal": {
+ "selected-text-color": "#b48ead",
+ "text-color": "#b48ead"
+ },
+ "Documentation": {
+ "selected-text-color": "#5e81ac",
+ "text-color": "#5e81ac"
+ },
+ "Error": {
+ "selected-text-color": "#bf616a",
+ "text-color": "#bf616a",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#8fbcbb",
+ "text-color": "#8fbcbb"
+ },
+ "Float": {
+ "selected-text-color": "#b48ead",
+ "text-color": "#b48ead"
+ },
+ "Function": {
+ "selected-text-color": "#88c0d0",
+ "text-color": "#88c0d0"
+ },
+ "Import": {
+ "selected-text-color": "#a3be8c",
+ "text-color": "#a3be8c"
+ },
+ "Information": {
+ "selected-text-color": "#ebcb8b",
+ "text-color": "#ebcb8b"
+ },
+ "Keyword": {
+ "bold": true,
+ "selected-text-color": "#81a1c1",
+ "text-color": "#81a1c1"
+ },
+ "Normal": {
+ "selected-text-color": "#d8dee9",
+ "text-color": "#d8dee9"
+ },
+ "Operator": {
+ "selected-text-color": "#81a1c1",
+ "text-color": "#81a1c1"
+ },
+ "Others": {
+ "selected-text-color": "#8fbcbb",
+ "text-color": "#8fbcbb"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#5e81ac",
+ "text-color": "#5e81ac"
+ },
+ "RegionMarker": {
+ "background-color": "#3b4252",
+ "selected-text-color": "#88c0d0",
+ "text-color": "#88c0d0"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#ebcb8b",
+ "text-color": "#ebcb8b"
+ },
+ "SpecialString": {
+ "selected-text-color": "#d08770",
+ "text-color": "#d08770"
+ },
+ "String": {
+ "selected-text-color": "#a3be8c",
+ "text-color": "#a3be8c"
+ },
+ "Variable": {
+ "selected-text-color": "#5e81ac",
+ "text-color": "#5e81ac"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#8dae70",
+ "text-color": "#8dae70"
+ },
+ "Warning": {
+ "selected-text-color": "#bf616a",
+ "text-color": "#bf616a"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/oblivion.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/oblivion.theme
new file mode 100644
index 0000000000..87dac8a5d3
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/oblivion.theme
@@ -0,0 +1,179 @@
+{
+ "_comments": [
+ "This theme has been adapted from the GtkSourceView Oblivion theme"
+ ],
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2007 Paolo Borelli <pborelli@gnome.org>, GtkSourceView team",
+ "SPDX-FileCopyrightText: 2020 Alexander Schlarb <alexander@ninetailed.ninja>"
+ ],
+ "name": "Oblivion",
+ "revision": 2,
+ "license": "SPDX-License-Identifier: MIT"
+ },
+ "text-styles": {
+ "Alert": {
+ "background-color": "#451e1a",
+ "bold": true,
+ "selected-text-color": "#e85848",
+ "text-color": "#e85848"
+ },
+ "Annotation": {
+ "selected-text-color": "#ad7fa8",
+ "text-color": "#ad7fa8"
+ },
+ "Attribute": {
+ "selected-text-color": "#ad7fa8",
+ "text-color": "#ad7fa8"
+ },
+ "BaseN": {
+ "selected-text-color": "#fce94f",
+ "text-color": "#edd400"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#729fcf",
+ "text-color": "#729fcf"
+ },
+ "Char": {
+ "selected-text-color": "#fcaf3e",
+ "text-color": "#ce5c00"
+ },
+ "Comment": {
+ "selected-text-color": "#8ae234",
+ "text-color": "#30a100"
+ },
+ "CommentVar": {
+ "selected-text-color": "#ad7fa8",
+ "text-color": "#ad7fa8"
+ },
+ "Constant": {
+ "bold": true,
+ "selected-text-color": "#ffffff",
+ "text-color": "#edd400"
+ },
+ "ControlFlow": {
+ "bold": true,
+ "selected-text-color": "#ffffff",
+ "text-color": "#ffffff"
+ },
+ "DataType": {
+ "selected-text-color": "#508ed8",
+ "text-color": "#508ed8"
+ },
+ "DecVal": {
+ "selected-text-color": "#fce94f",
+ "text-color": "#edd400"
+ },
+ "Documentation": {
+ "selected-text-color": "#8ae234",
+ "text-color": "#4e9a06"
+ },
+ "Error": {
+ "selected-text-color": "#e85848",
+ "text-color": "#e85848",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#508ed8",
+ "text-color": "#508ed8"
+ },
+ "Float": {
+ "selected-text-color": "#fcaf3e",
+ "text-color": "#ce5c00"
+ },
+ "Function": {
+ "bold": true,
+ "selected-text-color": "#729fcf",
+ "text-color": "#729fcf"
+ },
+ "Import": {
+ "selected-text-color": "#ad7fa8",
+ "text-color": "#ad7fa8"
+ },
+ "Information": {
+ "selected-text-color": "#c0a25f",
+ "text-color": "#c0a25f"
+ },
+ "Keyword": {
+ "bold": true,
+ "selected-text-color": "#ffffff",
+ "text-color": "#ffffff"
+ },
+ "Normal": {
+ "selected-text-color": "#eeeeec",
+ "text-color": "#d3d7c1"
+ },
+ "Operator": {
+ "selected-text-color": "#eeeeec",
+ "text-color": "#eeeeec"
+ },
+ "Others": {
+ "selected-text-color": "#fce94f",
+ "text-color": "#edd400"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#ad7fa8",
+ "text-color": "#ad7fa8"
+ },
+ "RegionMarker": {
+ "background-color": "#1c2c3f",
+ "selected-text-color": "#508ed8",
+ "text-color": "#508ed8"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#fcaf3e",
+ "text-color": "#ce5c00"
+ },
+ "SpecialString": {
+ "selected-text-color": "#fce94f",
+ "text-color": "#fce94f"
+ },
+ "String": {
+ "selected-text-color": "#fce94f",
+ "text-color": "#edd400"
+ },
+ "Variable": {
+ "selected-text-color": "#ce5c00",
+ "text-color": "#ce5c00"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#fce94f",
+ "text-color": "#c4a000"
+ },
+ "Warning": {
+ "selected-text-color": "#e85848",
+ "text-color": "#e85848"
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor": "#201f1f",
+ "BracketMatching": "#8f5902",
+ "CodeFolding": "#19395f",
+ "CurrentLine": "#2e3436",
+ "CurrentLineNumber": "#ffffff",
+ "IconBorder": "#302f2f",
+ "IndentationLine": "#989595",
+ "LineNumbers": "#e0dedb",
+ "MarkBookmark": "#0000cc",
+ "MarkBreakpointActive": "#cc0000",
+ "MarkBreakpointDisabled": "#cc00cc",
+ "MarkBreakpointReached": "#00cc00",
+ "MarkError": "#cc0000",
+ "MarkExecution": "#888a85",
+ "MarkWarning": "#ad7fa8",
+ "ModifiedLines": "#cc0000",
+ "ReplaceHighlight": "#356703",
+ "SavedLines": "#4e9a06",
+ "SearchHighlight": "#4e9a06",
+ "Separator": "#787775",
+ "SpellChecking": "#e85848",
+ "TabMarker": "#555753",
+ "TemplateBackground": "#302f2f",
+ "TemplateFocusedPlaceholder": "#23321a",
+ "TemplatePlaceholder": "#23321a",
+ "TemplateReadOnlyPlaceholder": "#451e1a",
+ "TextSelection": "#184880",
+ "WordWrapMarker": "#3c3a3a"
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/printing.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/printing.theme
index a6048ed01a..49bbe6419f 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/themes/printing.theme
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/printing.theme
@@ -1,6 +1,11 @@
{
"metadata" : {
- "revision" : 3,
+ "copyright": [
+ "SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>",
+ "SPDX-FileCopyrightText: 2016 Dominik Haumann <dhaumann@kde.org>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "revision" : 5,
"name" : "Printing"
},
"text-styles": {
@@ -64,7 +69,7 @@
"selected-text-color" : "#9c0e0e"
},
"VerbatimString" : {
- "text-color" : "#bf0303",
+ "text-color" : "#ea0404",
"selected-text-color" : "#9c0e0e"
},
"SpecialString" : {
@@ -141,33 +146,33 @@
}
},
"editor-colors": {
- "background-color" : "#ffffff",
- "code-folding" : "#94caef",
- "bracket-matching" : "#edf9ff",
- "current-line" : "#f8f7f6",
- "icon-border" : "#d6d2d0",
- "indentation-line" : "#d2d2d2",
- "line-numbers" : "#221f1e",
- "current-line-number" : "#221f1e",
- "mark-bookmark" : "#0000ff",
- "mark-breakpoint-active" : "#ff0000",
- "mark-breakpoint-reached" : "#ffff00",
- "mark-breakpoint-disabled" : "#ff00ff",
- "mark-execution" : "#a0a0a4",
- "mark-warning" : "#00ff00",
- "mark-error" : "#ff0000",
- "modified-lines" : "#f6e6e6",
- "replace-highlight" : "#00ff00",
- "saved-lines" : "#baf8ce",
- "search-highlight" : "#ffff00",
- "selection" : "#94caef",
- "separator" : "#898887",
- "spell-checking" : "#bf0303",
- "tab-marker" : "#d2d2d2",
- "template-background" : "#d6d2d0",
- "template-placeholder" : "#baf8ce",
- "template-focused-placeholder" : "#76da98",
- "template-read-only-placeholder" : "#f6e6e6",
- "word-wrap-marker" : "#ededed"
+ "BackgroundColor" : "#ffffff",
+ "CodeFolding" : "#94caef",
+ "BracketMatching" : "#edf9ff",
+ "CurrentLine" : "#f8f7f6",
+ "IconBorder" : "#d6d2d0",
+ "IndentationLine" : "#d2d2d2",
+ "LineNumbers" : "#221f1e",
+ "CurrentLineNumber" : "#221f1e",
+ "MarkBookmark" : "#0000ff",
+ "MarkBreakpointActive" : "#ff0000",
+ "MarkBreakpointReached" : "#ffff00",
+ "MarkBreakpointDisabled" : "#ff00ff",
+ "MarkExecution" : "#a0a0a4",
+ "MarkWarning" : "#00ff00",
+ "MarkError" : "#ff0000",
+ "ModifiedLines" : "#f6e6e6",
+ "ReplaceHighlight" : "#00ff00",
+ "SavedLines" : "#baf8ce",
+ "SearchHighlight" : "#ffff00",
+ "TextSelection" : "#94caef",
+ "Separator" : "#221f1e",
+ "SpellChecking" : "#bf0303",
+ "TabMarker" : "#d2d2d2",
+ "TemplateBackground" : "#d6d2d0",
+ "TemplatePlaceholder" : "#baf8ce",
+ "TemplateFocusedPlaceholder" : "#76da98",
+ "TemplateReadOnlyPlaceholder" : "#f6e6e6",
+ "WordWrapMarker" : "#ededed"
}
}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/radical.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/radical.theme
new file mode 100644
index 0000000000..ec0eb8ca9f
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/radical.theme
@@ -0,0 +1,182 @@
+{
+ "_comments": [
+ "Last update: Sep 23, 2020 (revision 2)",
+ "This file has been converted from: https://github.com/dhedgecock/radical-vscode"
+ ],
+ "metadata": {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2018 Dan Hedgecock",
+ "SPDX-FileCopyrightText: 2020 Nibaldo González <nibgonz@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "name": "Radical",
+ "revision": 2
+ },
+ "editor-colors": {
+ "BackgroundColor": "#141322",
+ "BracketMatching": "#4f2e93",
+ "CodeFolding": "#1e1836",
+ "CurrentLine": "#231630",
+ "CurrentLineNumber": "#d0fff4",
+ "IconBorder": "#141322",
+ "IndentationLine": "#353541",
+ "LineNumbers": "#415e6c",
+ "MarkBookmark": "#391ab5",
+ "MarkBreakpointActive": "#fa61b8",
+ "MarkBreakpointDisabled": "#83fee8",
+ "MarkBreakpointReached": "#fffc7e",
+ "MarkError": "#fc0065",
+ "MarkExecution": "#c8ff00",
+ "MarkWarning": "#ffd000",
+ "ModifiedLines": "#a3ff57",
+ "ReplaceHighlight": "#ffb000",
+ "SavedLines": "#ffb000",
+ "SearchHighlight": "#642581",
+ "Separator": "#252531",
+ "SpellChecking": "#ff1767",
+ "TabMarker": "#2e2e3a",
+ "TemplateBackground": "#1c1a30",
+ "TemplateFocusedPlaceholder": "#ff428e",
+ "TemplatePlaceholder": "#242560",
+ "TemplateReadOnlyPlaceholder": "#1a1b46",
+ "TextSelection": "#4f2e93",
+ "WordWrapMarker": "#100f1a"
+ },
+ "text-styles": {
+ "Alert": {
+ "background-color": "#2f183b",
+ "bold": true,
+ "selected-text-color": "#ff427b",
+ "text-color": "#ff427b"
+ },
+ "Annotation": {
+ "selected-text-color": "#fda8bc",
+ "text-color": "#fda8bc"
+ },
+ "Attribute": {
+ "selected-text-color": "#5af5f0",
+ "text-color": "#5af5f0"
+ },
+ "BaseN": {
+ "selected-text-color": "#f834bb",
+ "text-color": "#f834bb"
+ },
+ "BuiltIn": {
+ "selected-text-color": "#999ee1",
+ "text-color": "#999ee1"
+ },
+ "Char": {
+ "selected-text-color": "#dff959",
+ "text-color": "#dff959"
+ },
+ "Comment": {
+ "italic": true,
+ "selected-text-color": "#45898c",
+ "text-color": "#45898c"
+ },
+ "CommentVar": {
+ "selected-text-color": "#a8c0c2",
+ "text-color": "#a8c0c2"
+ },
+ "Constant": {
+ "bold": true,
+ "selected-text-color": "#fa61b8",
+ "text-color": "#fa61b8"
+ },
+ "ControlFlow": {
+ "bold": true,
+ "selected-text-color": "#d5358f",
+ "text-color": "#d5358f"
+ },
+ "DataType": {
+ "selected-text-color": "#ff85a1",
+ "text-color": "#ff85a1"
+ },
+ "DecVal": {
+ "selected-text-color": "#fa61b8",
+ "text-color": "#fa61b8"
+ },
+ "Documentation": {
+ "selected-text-color": "#75b7bb",
+ "text-color": "#75b7bb"
+ },
+ "Error": {
+ "bold": true,
+ "italic": true,
+ "selected-text-color": "#ff427b",
+ "text-color": "#ff427b",
+ "underline": true
+ },
+ "Extension": {
+ "bold": true,
+ "selected-text-color": "#a8ffdb",
+ "text-color": "#a8ffdb"
+ },
+ "Float": {
+ "selected-text-color": "#f834bb",
+ "text-color": "#f834bb"
+ },
+ "Function": {
+ "selected-text-color": "#a9fef7",
+ "text-color": "#a9fef7"
+ },
+ "Import": {
+ "selected-text-color": "#a9fef7",
+ "text-color": "#a9fef7"
+ },
+ "Information": {
+ "selected-text-color": "#ffd000",
+ "text-color": "#ffd000"
+ },
+ "Keyword": {
+ "bold": true,
+ "selected-text-color": "#d5358f",
+ "text-color": "#d5358f"
+ },
+ "Normal": {
+ "selected-text-color": "#7c9c9e",
+ "text-color": "#7c9c9e"
+ },
+ "Operator": {
+ "selected-text-color": "#d5358f",
+ "text-color": "#d5358f"
+ },
+ "Others": {
+ "selected-text-color": "#5effbd",
+ "text-color": "#5effbd"
+ },
+ "Preprocessor": {
+ "selected-text-color": "#d5358f",
+ "text-color": "#d5358f"
+ },
+ "RegionMarker": {
+ "background-color": "#242560",
+ "selected-text-color": "#baf7fc",
+ "text-color": "#baf7fc"
+ },
+ "SpecialChar": {
+ "selected-text-color": "#c3f920",
+ "text-color": "#c3f920"
+ },
+ "SpecialString": {
+ "selected-text-color": "#ff96aa",
+ "text-color": "#ff96aa"
+ },
+ "String": {
+ "selected-text-color": "#a9fef7",
+ "text-color": "#a9fef7"
+ },
+ "Variable": {
+ "selected-text-color": "#c7e3ee",
+ "text-color": "#c7e3ee"
+ },
+ "VerbatimString": {
+ "selected-text-color": "#a8ffdb",
+ "text-color": "#a8ffdb"
+ },
+ "Warning": {
+ "selected-text-color": "#ff427b",
+ "text-color": "#ff427b"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/solarized-dark.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/solarized-dark.theme
index 42a6eacbe8..1fe48bacd0 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/themes/solarized-dark.theme
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/solarized-dark.theme
@@ -1,6 +1,15 @@
{
+ "_comments": [
+ "This theme has been adapted from: https://ethanschoonover.com/solarized/"
+ ],
"metadata" : {
- "revision" : 1,
+ "copyright": [
+ "SPDX-FileCopyrightText: 2011 Ethan Schoonover",
+ "SPDX-FileCopyrightText: 2012 Dominik Haumann <dhaumann@kde.org>",
+ "SPDX-FileCopyrightText: 2018 Andrew Crouthamel <andrew.crouthamel@kdemail.net>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "revision" : 6,
"name" : "Solarized Dark"
},
"text-styles": {
@@ -14,8 +23,7 @@
},
"Keyword" : {
"text-color" : "#859900",
- "selected-text-color" : "#859900",
- "bold" : true
+ "selected-text-color" : "#859900"
},
"Function" : {
"text-color" : "#268bd2",
@@ -27,8 +35,7 @@
},
"ControlFlow" : {
"text-color" : "#859900",
- "selected-text-color" : "#859900",
- "bold" : true
+ "selected-text-color" : "#859900"
},
"Operator" : {
"text-color" : "#859900",
@@ -40,8 +47,7 @@
},
"Extension" : {
"text-color" : "#268bd2",
- "selected-text-color" : "#268bd2",
- "bold" : true
+ "selected-text-color" : "#268bd2"
},
"Preprocessor" : {
"text-color" : "#cb4b16",
@@ -64,8 +70,8 @@
"selected-text-color" : "#2aa198"
},
"VerbatimString" : {
- "text-color" : "#2aa198",
- "selected-text-color" : "#2aa198"
+ "text-color" : "#23877e",
+ "selected-text-color" : "#23877e"
},
"SpecialString" : {
"text-color" : "#dc322f",
@@ -77,8 +83,7 @@
},
"DataType" : {
"text-color" : "#b58900",
- "selected-text-color" : "#b58900",
- "bold" : true
+ "selected-text-color" : "#b58900"
},
"DecVal" : {
"text-color" : "#2aa198",
@@ -143,33 +148,45 @@
}
},
"editor-colors": {
- "background-color" : "#002b36",
- "code-folding" : "#6c71c4",
- "bracket-matching" : "#073642",
- "current-line" : "#073642",
- "icon-border" : "#073642",
- "indentation-line" : "#073642",
- "line-numbers" : "#586e75",
- "current-line-number" : "#586e75",
- "mark-bookmark" : "#268bd2",
- "mark-breakpoint-active" : "#dc322f",
- "mark-breakpoint-reached" : "#b58900",
- "mark-breakpoint-disabled" : "#d33682",
- "mark-execution" : "#586e75",
- "mark-warning" : "#cb4b16",
- "mark-error" : "#dc322f",
- "modified-lines" : "#cb4b16",
- "replace-highlight" : "#859900",
- "saved-lines" : "#2aa198",
- "search-highlight" : "#b58900",
- "selection" : "#eee8d5",
- "separator" : "#586e75",
- "spell-checking" : "#dc322f",
- "tab-marker" : "#586e75",
- "template-background" : "#073642",
- "template-placeholder" : "#073642",
- "template-focused-placeholder" : "#073642",
- "template-read-only-placeholder" : "#073642",
- "word-wrap-marker" : "#586e75"
+ "BackgroundColor" : "#002b36",
+ "CodeFolding": "#083d4a",
+ "BracketMatching" : "#083d4a",
+ "CurrentLine" : "#073642",
+ "IconBorder" : "#073642",
+ "IndentationLine" : "#083d4a",
+ "LineNumbers" : "#586e75",
+ "CurrentLineNumber": "#93a1a1",
+ "MarkBookmark" : "#268bd2",
+ "MarkBreakpointActive" : "#dc322f",
+ "MarkBreakpointReached" : "#b58900",
+ "MarkBreakpointDisabled" : "#d33682",
+ "MarkExecution" : "#586e75",
+ "MarkWarning" : "#cb4b16",
+ "MarkError" : "#dc322f",
+ "ModifiedLines" : "#cb4b16",
+ "ReplaceHighlight": "#3c4300",
+ "SavedLines" : "#2aa198",
+ "SearchHighlight": "#0a4d5e",
+ "TextSelection": "#083d4a",
+ "Separator" : "#1c3e49",
+ "SpellChecking" : "#dc322f",
+ "TabMarker" : "#586e75",
+ "TemplateBackground" : "#073642",
+ "TemplatePlaceholder" : "#073642",
+ "TemplateFocusedPlaceholder" : "#073642",
+ "TemplateReadOnlyPlaceholder" : "#073642",
+ "WordWrapMarker" : "#586e75"
+ },
+ "custom-styles": {
+ "XML": {
+ "Element": {
+ "selected-text-color": "#839496",
+ "text-color": "#268bd2"
+ },
+ "Element Symbols": {
+ "selected-text-color": "#586e75",
+ "text-color": "#657b83"
+ }
+ }
}
}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/solarized-light.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/solarized-light.theme
index 473f98add3..51c76faaec 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/themes/solarized-light.theme
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/solarized-light.theme
@@ -1,6 +1,15 @@
{
+ "_comments": [
+ "This theme has been adapted from: https://ethanschoonover.com/solarized/"
+ ],
"metadata" : {
- "revision" : 1,
+ "copyright": [
+ "SPDX-FileCopyrightText: 2011 Ethan Schoonover",
+ "SPDX-FileCopyrightText: 2012 Dominik Haumann <dhaumann@kde.org>",
+ "SPDX-FileCopyrightText: 2018 Andrew Crouthamel <andrew.crouthamel@kdemail.net>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "revision" : 5,
"name" : "Solarized Light"
},
"text-styles": {
@@ -64,8 +73,8 @@
"selected-text-color" : "#2aa198"
},
"VerbatimString" : {
- "text-color" : "#2aa198",
- "selected-text-color" : "#2aa198"
+ "text-color" : "#23837a",
+ "selected-text-color" : "#23837a"
},
"SpecialString" : {
"text-color" : "#dc322f",
@@ -143,33 +152,45 @@
}
},
"editor-colors": {
- "background-color" : "#fdf6e3",
- "code-folding" : "#6c71c4",
- "bracket-matching" : "#eee8d5",
- "current-line" : "#eee8d5",
- "icon-border" : "#eee8d5",
- "indentation-line" : "#eee8d5",
- "line-numbers" : "#93a1a1",
- "current-line-number" : "#93a1a1",
- "mark-bookmark" : "#268bd2",
- "mark-breakpoint-active" : "#dc322f",
- "mark-breakpoint-reached" : "#b58900",
- "mark-breakpoint-disabled" : "#d33682",
- "mark-execution" : "#93a1a1",
- "mark-warning" : "#cb4b16",
- "mark-error" : "#dc322f",
- "modified-lines" : "#cb4b16",
- "replace-highlight" : "#859900",
- "saved-lines" : "#2aa198",
- "search-highlight" : "#b58900",
- "selection" : "#073642",
- "separator" : "#93a1a1",
- "spell-checking" : "#dc322f",
- "tab-marker" : "#93a1a1",
- "template-background" : "#eee8d5",
- "template-placeholder" : "#eee8d5",
- "template-focused-placeholder" : "#eee8d5",
- "template-read-only-placeholder" : "#eee8d5",
- "word-wrap-marker" : "#93a1a1"
+ "BackgroundColor" : "#fdf6e3",
+ "CodeFolding" : "#6c71c4",
+ "BracketMatching" : "#eee8d5",
+ "CurrentLine" : "#eee8d5",
+ "IconBorder" : "#eee8d5",
+ "IndentationLine" : "#eee8d5",
+ "LineNumbers" : "#93a1a1",
+ "CurrentLineNumber" : "#93a1a1",
+ "MarkBookmark" : "#268bd2",
+ "MarkBreakpointActive" : "#dc322f",
+ "MarkBreakpointReached" : "#b58900",
+ "MarkBreakpointDisabled" : "#d33682",
+ "MarkExecution" : "#93a1a1",
+ "MarkWarning" : "#cb4b16",
+ "MarkError" : "#dc322f",
+ "ModifiedLines" : "#cb4b16",
+ "ReplaceHighlight" : "#859900",
+ "SavedLines" : "#2aa198",
+ "SearchHighlight" : "#b58900",
+ "TextSelection" : "#073642",
+ "Separator" : "#e0dccc",
+ "SpellChecking" : "#dc322f",
+ "TabMarker" : "#93a1a1",
+ "TemplateBackground" : "#eee8d5",
+ "TemplatePlaceholder" : "#eee8d5",
+ "TemplateFocusedPlaceholder" : "#eee8d5",
+ "TemplateReadOnlyPlaceholder" : "#eee8d5",
+ "WordWrapMarker" : "#93a1a1"
+ },
+ "custom-styles": {
+ "XML": {
+ "Element": {
+ "selected-text-color": "#268bd2",
+ "text-color": "#268bd2"
+ },
+ "Element Symbols": {
+ "selected-text-color": "#93a1a1",
+ "text-color": "#839496"
+ }
+ }
}
}
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/theme-data.qrc b/src/libs/3rdparty/syntax-highlighting/data/themes/theme-data.qrc
index 46af4213b5..8f4aa6c7e4 100644
--- a/src/libs/3rdparty/syntax-highlighting/data/themes/theme-data.qrc
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/theme-data.qrc
@@ -1,10 +1,26 @@
-<!DOCTYPE RCC>
-<RCC version="1.0">
- <qresource prefix="/org.kde.syntax-highlighting/themes">
- <file alias="default.theme">default.theme</file>
- <file alias="breeze-dark.theme">breeze-dark.theme</file>
- <file alias="printing.theme">printing.theme</file>
- <file alias="solarized-dark.theme">solarized-dark.theme</file>
- <file alias="solarized-light.theme">solarized-light.theme</file>
- </qresource>
+<RCC>
+ <qresource prefix="/org.kde.syntax-highlighting/themes">
+ <file>atom-one-dark.theme</file>
+ <file>atom-one-light.theme</file>
+ <file>ayu-dark.theme</file>
+ <file>ayu-light.theme</file>
+ <file>ayu-mirage.theme</file>
+ <file>breeze-dark.theme</file>
+ <file>breeze-light.theme</file>
+ <file>dracula.theme</file>
+ <file>falcon.theme</file>
+ <file>github-dark.theme</file>
+ <file>github-light.theme</file>
+ <file>gruvbox-dark.theme</file>
+ <file>gruvbox-light.theme</file>
+ <file>homunculus.theme</file>
+ <file>monokai.theme</file>
+ <file>nord.theme</file>
+ <file>oblivion.theme</file>
+ <file>printing.theme</file>
+ <file>radical.theme</file>
+ <file>solarized-dark.theme</file>
+ <file>solarized-light.theme</file>
+ <file>vim-dark.theme</file>
+ </qresource>
</RCC>
diff --git a/src/libs/3rdparty/syntax-highlighting/data/themes/vim-dark.theme b/src/libs/3rdparty/syntax-highlighting/data/themes/vim-dark.theme
new file mode 100644
index 0000000000..d039822cae
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/data/themes/vim-dark.theme
@@ -0,0 +1,180 @@
+{
+ "metadata" : {
+ "copyright": [
+ "SPDX-FileCopyrightText: 2012 Dominik Haumann <dhaumann@kde.org>",
+ "SPDX-FileCopyrightText: 2020 Nibaldo González <nibgonz@gmail.com>"
+ ],
+ "license": "SPDX-License-Identifier: MIT",
+ "revision" : 4,
+ "name" : "Vim Dark"
+ },
+ "text-styles": {
+ "Normal" : {
+ "text-color" : "#b2b2b2",
+ "selected-text-color" : "#b2b2b2",
+ "bold" : false,
+ "italic" : false,
+ "underline" : false,
+ "strike-through" : false
+ },
+ "Keyword" : {
+ "text-color" : "#5fd7ff",
+ "selected-text-color" : "#e0ffff",
+ "bold" : true
+ },
+ "Function" : {
+ "text-color" : "#cd00cd",
+ "selected-text-color" : "#cd00cd"
+ },
+ "Variable" : {
+ "text-color" : "#06989a",
+ "selected-text-color" : "#06989a"
+ },
+ "ControlFlow" : {
+ "text-color" : "#ffff54",
+ "selected-text-color" : "#ffff54",
+ "bold" : true
+ },
+ "Operator" : {
+ "text-color" : "#b2b2b2",
+ "selected-text-color" : "#b2b2b2"
+ },
+ "BuiltIn" : {
+ "text-color" : "#87ffaf",
+ "selected-text-color" : "#87ffaf",
+ "bold" : true
+ },
+ "Extension" : {
+ "text-color" : "#0095ff",
+ "selected-text-color" : "#0095ff",
+ "bold" : true
+ },
+ "Preprocessor" : {
+ "text-color" : "#87ffaf",
+ "selected-text-color" : "#87ffaf"
+ },
+ "Attribute" : {
+ "text-color" : "#ffffaf",
+ "selected-text-color" : "#ffffaf"
+ },
+ "Char" : {
+ "text-color" : "#ff5454",
+ "selected-text-color" : "#ff5454"
+ },
+ "SpecialChar" : {
+ "text-color" : "#0095ff",
+ "selected-text-color" : "#0095ff"
+ },
+ "String" : {
+ "text-color" : "#ff54ff",
+ "selected-text-color" : "#ff54ff"
+ },
+ "VerbatimString" : {
+ "text-color" : "#f000f0",
+ "selected-text-color" : "#f000f0"
+ },
+ "SpecialString" : {
+ "text-color" : "#ff5500",
+ "selected-text-color" : "#ff5500"
+ },
+ "Import" : {
+ "text-color" : "#ff54ff",
+ "selected-text-color" : "#ff54ff"
+ },
+ "DataType" : {
+ "text-color" : "#ffff54",
+ "selected-text-color" : "#ffff54"
+ },
+ "DecVal" : {
+ "text-color" : "#ff8b8b",
+ "selected-text-color" : "#ff8b8b"
+ },
+ "BaseN" : {
+ "text-color" : "#ff8b8b",
+ "selected-text-color" : "#ff8b8b"
+ },
+ "Float" : {
+ "text-color" : "#ff8b8b",
+ "selected-text-color" : "#ff8b8b"
+ },
+ "Constant" : {
+ "text-color" : "#af7f00",
+ "selected-text-color" : "#af7f00",
+ "bold" : true
+ },
+ "Comment" : {
+ "text-color" : "#54ffff",
+ "selected-text-color" : "#54ffff"
+ },
+ "Documentation" : {
+ "text-color" : "#e0ffff",
+ "selected-text-color" : "#e0ffff"
+ },
+ "Annotation" : {
+ "text-color" : "#ff00ff",
+ "selected-text-color" : "#ff00ff"
+ },
+ "CommentVar" : {
+ "text-color" : "#0095ff",
+ "selected-text-color" : "#0095ff"
+ },
+ "RegionMarker" : {
+ "text-color" : "#0095ff",
+ "selected-text-color" : "#0095ff",
+ "background-color" : "#22226d"
+ },
+ "Information" : {
+ "text-color" : "#ffaa00",
+ "selected-text-color" : "#ffaa00"
+ },
+ "Warning" : {
+ "text-color" : "#ff0000",
+ "selected-text-color" : "#ed1515"
+ },
+ "Alert" : {
+ "text-color" : "#ff0000",
+ "selected-text-color" : "#bf0303",
+ "background-color" : "#3f0000",
+ "bold" : true
+ },
+ "Error" : {
+ "text-color" : "#ff5500",
+ "selected-text-color" : "#ff5500",
+ "underline" : true
+ },
+ "Others" : {
+ "text-color" : "#54ff54",
+ "selected-text-color" : "#54ff54"
+ }
+ },
+ "editor-colors": {
+ "BackgroundColor" : "#000000",
+ "CodeFolding" : "#002b26",
+ "BracketMatching" : "#4400aa",
+ "CurrentLine" : "#17003b",
+ "IconBorder" : "#000000",
+ "IndentationLine" : "#2a00d2",
+ "LineNumbers" : "#005d7a",
+ "CurrentLineNumber" : "#005d7a",
+ "MarkBookmark" : "#0000ff",
+ "MarkBreakpointActive" : "#ff0000",
+ "MarkBreakpointReached" : "#ffff00",
+ "MarkBreakpointDisabled" : "#ff00ff",
+ "MarkExecution" : "#a0a0a4",
+ "MarkWarning" : "#00ff00",
+ "MarkError" : "#ff0000",
+ "ModifiedLines" : "#54ff54",
+ "ReplaceHighlight" : "#54ff54",
+ "SavedLines" : "#5454ff",
+ "SearchHighlight" : "#ffff00",
+ "TextSelection" : "#232323",
+ "Separator" : "#003344",
+ "SpellChecking" : "#ff0000",
+ "TabMarker" : "#414141",
+ "TemplateBackground" : "#cccccc",
+ "TemplatePlaceholder" : "#ccffcc",
+ "TemplateFocusedPlaceholder" : "#66ff66",
+ "TemplateReadOnlyPlaceholder" : "#ffcccc",
+ "WordWrapMarker" : "#262626"
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/examples/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/examples/CMakeLists.txt
deleted file mode 100644
index 652b72cb0a..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/examples/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-if(Qt5Widgets_FOUND)
- add_executable(codeeditor codeeditor.cpp main.cpp)
- target_link_libraries(codeeditor Qt5::Widgets KF5SyntaxHighlighting)
-endif()
diff --git a/src/libs/3rdparty/syntax-highlighting/examples/codeeditor.cpp b/src/libs/3rdparty/syntax-highlighting/examples/codeeditor.cpp
deleted file mode 100644
index 88f315462d..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/examples/codeeditor.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#include "codeeditor.h"
-
-#include <definition.h>
-#include <foldingregion.h>
-#include <syntaxhighlighter.h>
-#include <theme.h>
-
-#include <QApplication>
-#include <QDebug>
-#include <QFile>
-#include <QFileDialog>
-#include <QFontDatabase>
-#include <QMenu>
-#include <QPainter>
-#include <QPalette>
-
-class CodeEditorSidebar : public QWidget
-{
- Q_OBJECT
-public:
- explicit CodeEditorSidebar(CodeEditor *editor);
- QSize sizeHint() const Q_DECL_OVERRIDE;
-
-protected:
- void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
- void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
-
-private:
- CodeEditor *m_codeEditor;
-};
-
-CodeEditorSidebar::CodeEditorSidebar(CodeEditor *editor) :
- QWidget(editor),
- m_codeEditor(editor)
-{
-}
-
-QSize CodeEditorSidebar::sizeHint() const
-{
- return QSize(m_codeEditor->sidebarWidth(), 0);
-}
-
-void CodeEditorSidebar::paintEvent(QPaintEvent *event)
-{
- m_codeEditor->sidebarPaintEvent(event);
-}
-
-void CodeEditorSidebar::mouseReleaseEvent(QMouseEvent *event)
-{
- if (event->x() >= width() - m_codeEditor->fontMetrics().lineSpacing()) {
- auto block = m_codeEditor->blockAtPosition(event->y());
- if (!block.isValid() || !m_codeEditor->isFoldable(block))
- return;
- m_codeEditor->toggleFold(block);
- }
- QWidget::mouseReleaseEvent(event);
-}
-
-
-CodeEditor::CodeEditor(QWidget *parent) :
- QPlainTextEdit(parent),
- m_highlighter(new KSyntaxHighlighting::SyntaxHighlighter(document())),
- m_sideBar(new CodeEditorSidebar(this))
-{
- setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
-
- setTheme((palette().color(QPalette::Base).lightness() < 128)
- ? m_repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme)
- : m_repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme));
-
- connect(this, &QPlainTextEdit::blockCountChanged, this, &CodeEditor::updateSidebarGeometry);
- connect(this, &QPlainTextEdit::updateRequest, this, &CodeEditor::updateSidebarArea);
- connect(this, &QPlainTextEdit::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine);
-
- updateSidebarGeometry();
- highlightCurrentLine();
-}
-
-CodeEditor::~CodeEditor()
-{
-}
-
-void CodeEditor::openFile(const QString& fileName)
-{
- QFile f(fileName);
- if (!f.open(QFile::ReadOnly)) {
- qWarning() << "Failed to open" << fileName << ":" << f.errorString();
- return;
- }
-
- clear();
-
- const auto def = m_repository.definitionForFileName(fileName);
- m_highlighter->setDefinition(def);
-
- setWindowTitle(fileName);
- setPlainText(QString::fromUtf8(f.readAll()));
-}
-
-void CodeEditor::contextMenuEvent(QContextMenuEvent *event)
-{
- auto menu = createStandardContextMenu(event->pos());
- menu->addSeparator();
- auto openAction = menu->addAction(QStringLiteral("Open File..."));
- connect(openAction, &QAction::triggered, this, [this]() {
- const auto fileName = QFileDialog::getOpenFileName(this, QStringLiteral("Open File"));
- if (!fileName.isEmpty())
- openFile(fileName);
- });
-
- // syntax selection
- auto hlActionGroup = new QActionGroup(menu);
- hlActionGroup->setExclusive(true);
- auto hlGroupMenu = menu->addMenu(QStringLiteral("Syntax"));
- QMenu *hlSubMenu = hlGroupMenu;
- QString currentGroup;
- foreach (const auto &def, m_repository.definitions()) {
- if (def.isHidden())
- continue;
- if (currentGroup != def.section()) {
- currentGroup = def.section();
- hlSubMenu = hlGroupMenu->addMenu(def.translatedSection());
- }
-
- Q_ASSERT(hlSubMenu);
- auto action = hlSubMenu->addAction(def.translatedName());
- action->setCheckable(true);
- action->setData(def.name());
- hlActionGroup->addAction(action);
- if (def.name() == m_highlighter->definition().name())
- action->setChecked(true);
- }
- connect(hlActionGroup, &QActionGroup::triggered, this, [this](QAction *action) {
- const auto defName = action->data().toString();
- const auto def = m_repository.definitionForName(defName);
- m_highlighter->setDefinition(def);
- });
-
- // theme selection
- auto themeGroup = new QActionGroup(menu);
- themeGroup->setExclusive(true);
- auto themeMenu = menu->addMenu(QStringLiteral("Theme"));
- foreach (const auto &theme, m_repository.themes()) {
- auto action = themeMenu->addAction(theme.translatedName());
- action->setCheckable(true);
- action->setData(theme.name());
- themeGroup->addAction(action);
- if (theme.name() == m_highlighter->theme().name())
- action->setChecked(true);
- }
- connect(themeGroup, &QActionGroup::triggered, this, [this](QAction *action) {
- const auto themeName = action->data().toString();
- const auto theme = m_repository.theme(themeName);
- setTheme(theme);
- });
-
- menu->exec(event->globalPos());
- delete menu;
-}
-
-void CodeEditor::resizeEvent(QResizeEvent *event)
-{
- QPlainTextEdit::resizeEvent(event);
- updateSidebarGeometry();
-}
-
-void CodeEditor::setTheme(const KSyntaxHighlighting::Theme &theme)
-{
- auto pal = qApp->palette();
- if (theme.isValid()) {
- pal.setColor(QPalette::Base, theme.editorColor(KSyntaxHighlighting::Theme::BackgroundColor));
- pal.setColor(QPalette::Text, theme.textColor(KSyntaxHighlighting::Theme::Normal));
- pal.setColor(QPalette::Highlight, theme.editorColor(KSyntaxHighlighting::Theme::TextSelection));
- }
- setPalette(pal);
-
- m_highlighter->setTheme(theme);
- m_highlighter->rehighlight();
- highlightCurrentLine();
-}
-
-int CodeEditor::sidebarWidth() const
-{
- int digits = 1;
- auto count = blockCount();
- while (count >= 10) {
- ++digits;
- count /= 10;
- }
- return 4 + fontMetrics().width(QLatin1Char('9')) * digits + fontMetrics().lineSpacing();
-}
-
-void CodeEditor::sidebarPaintEvent(QPaintEvent *event)
-{
- QPainter painter(m_sideBar);
- painter.fillRect(event->rect(), m_highlighter->theme().editorColor(KSyntaxHighlighting::Theme::IconBorder));
-
- auto block = firstVisibleBlock();
- auto blockNumber = block.blockNumber();
- int top = blockBoundingGeometry(block).translated(contentOffset()).top();
- int bottom = top + blockBoundingRect(block).height();
- const int currentBlockNumber = textCursor().blockNumber();
-
- const auto foldingMarkerSize = fontMetrics().lineSpacing();
-
- while (block.isValid() && top <= event->rect().bottom()) {
- if (block.isVisible() && bottom >= event->rect().top()) {
- const auto number = QString::number(blockNumber + 1);
- painter.setPen(m_highlighter->theme().editorColor(
- (blockNumber == currentBlockNumber) ? KSyntaxHighlighting::Theme::CurrentLineNumber
- : KSyntaxHighlighting::Theme::LineNumbers));
- painter.drawText(0, top, m_sideBar->width() - 2 - foldingMarkerSize, fontMetrics().height(), Qt::AlignRight, number);
- }
-
- // folding marker
- if (block.isVisible() && isFoldable(block)) {
- QPolygonF polygon;
- if (isFolded(block)) {
- polygon << QPointF(foldingMarkerSize * 0.4, foldingMarkerSize * 0.25);
- polygon << QPointF(foldingMarkerSize * 0.4, foldingMarkerSize * 0.75);
- polygon << QPointF(foldingMarkerSize * 0.8, foldingMarkerSize * 0.5);
- } else {
- polygon << QPointF(foldingMarkerSize * 0.25, foldingMarkerSize * 0.4);
- polygon << QPointF(foldingMarkerSize * 0.75, foldingMarkerSize * 0.4);
- polygon << QPointF(foldingMarkerSize * 0.5, foldingMarkerSize * 0.8);
- }
- painter.save();
- painter.setRenderHint(QPainter::Antialiasing);
- painter.setPen(Qt::NoPen);
- painter.setBrush(QColor(m_highlighter->theme().editorColor(KSyntaxHighlighting::Theme::CodeFolding)));
- painter.translate(m_sideBar->width() - foldingMarkerSize, top);
- painter.drawPolygon(polygon);
- painter.restore();
- }
-
- block = block.next();
- top = bottom;
- bottom = top + blockBoundingRect(block).height();
- ++blockNumber;
- }
-}
-
-void CodeEditor::updateSidebarGeometry()
-{
- setViewportMargins(sidebarWidth(), 0, 0, 0);
- const auto r = contentsRect();
- m_sideBar->setGeometry(QRect(r.left(), r.top(), sidebarWidth(), r.height()));
-}
-
-void CodeEditor::updateSidebarArea(const QRect& rect, int dy)
-{
- if (dy)
- m_sideBar->scroll(0, dy);
- else
- m_sideBar->update(0, rect.y(), m_sideBar->width(), rect.height());
-}
-
-void CodeEditor::highlightCurrentLine()
-{
- QTextEdit::ExtraSelection selection;
- selection.format.setBackground(QColor(m_highlighter->theme().editorColor(KSyntaxHighlighting::Theme::CurrentLine)));
- selection.format.setProperty(QTextFormat::FullWidthSelection, true);
- selection.cursor = textCursor();
- selection.cursor.clearSelection();
-
- QList<QTextEdit::ExtraSelection> extraSelections;
- extraSelections.append(selection);
- setExtraSelections(extraSelections);
-}
-
-QTextBlock CodeEditor::blockAtPosition(int y) const
-{
- auto block = firstVisibleBlock();
- if (!block.isValid())
- return QTextBlock();
-
- int top = blockBoundingGeometry(block).translated(contentOffset()).top();
- int bottom = top + blockBoundingRect(block).height();
- do {
- if (top <= y && y <= bottom)
- return block;
- block = block.next();
- top = bottom;
- bottom = top + blockBoundingRect(block).height();
- } while (block.isValid());
- return QTextBlock();
-}
-
-bool CodeEditor::isFoldable(const QTextBlock &block) const
-{
- return m_highlighter->startsFoldingRegion(block);
-}
-
-bool CodeEditor::isFolded(const QTextBlock &block) const
-{
- if (!block.isValid())
- return false;
- const auto nextBlock = block.next();
- if (!nextBlock.isValid())
- return false;
- return !nextBlock.isVisible();
-}
-
-void CodeEditor::toggleFold(const QTextBlock &startBlock)
-{
- // we also want to fold the last line of the region, therefore the ".next()"
- const auto endBlock = m_highlighter->findFoldingRegionEnd(startBlock).next();
-
- if (isFolded(startBlock)) {
- // unfold
- auto block = startBlock.next();
- while (block.isValid() && !block.isVisible()) {
- block.setVisible(true);
- block.setLineCount(block.layout()->lineCount());
- block = block.next();
- }
-
- } else {
- // fold
- auto block = startBlock.next();
- while (block.isValid() && block != endBlock) {
- block.setVisible(false);
- block.setLineCount(0);
- block = block.next();
- }
- }
-
- // redraw document
- document()->markContentsDirty(startBlock.position(), endBlock.position() - startBlock.position() + 1);
-
- // update scrollbars
- emit document()->documentLayout()->documentSizeChanged(document()->documentLayout()->documentSize());
-}
-
-#include "codeeditor.moc"
diff --git a/src/libs/3rdparty/syntax-highlighting/examples/codeeditor.h b/src/libs/3rdparty/syntax-highlighting/examples/codeeditor.h
deleted file mode 100644
index 1823b43e85..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/examples/codeeditor.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#ifndef CODEEDITOR_H
-#define CODEEDITOR_H
-
-#include <repository.h>
-
-#include <QPlainTextEdit>
-
-namespace KSyntaxHighlighting {
-class SyntaxHighlighter;
-}
-
-class CodeEditorSidebar;
-
-class CodeEditor : public QPlainTextEdit
-{
- Q_OBJECT
-public:
- explicit CodeEditor(QWidget *parent = nullptr);
- ~CodeEditor();
-
- void openFile(const QString &fileName);
-
-protected:
- void contextMenuEvent(QContextMenuEvent *event) override;
- void resizeEvent(QResizeEvent *event) override;
-
-private:
- friend class CodeEditorSidebar;
- void setTheme(const KSyntaxHighlighting::Theme &theme);
- int sidebarWidth() const;
- void sidebarPaintEvent(QPaintEvent *event);
- void updateSidebarGeometry();
- void updateSidebarArea(const QRect &rect, int dy);
- void highlightCurrentLine();
-
- QTextBlock blockAtPosition(int y) const;
- bool isFoldable(const QTextBlock &block) const;
- bool isFolded(const QTextBlock &block) const;
- void toggleFold(const QTextBlock &block);
-
- KSyntaxHighlighting::Repository m_repository;
- KSyntaxHighlighting::SyntaxHighlighter *m_highlighter;
- CodeEditorSidebar *m_sideBar;
-};
-
-#endif // CODEEDITOR_H
diff --git a/src/libs/3rdparty/syntax-highlighting/examples/main.cpp b/src/libs/3rdparty/syntax-highlighting/examples/main.cpp
deleted file mode 100644
index 3fb542954d..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/examples/main.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#include "codeeditor.h"
-
-#include <QApplication>
-#include <QCommandLineParser>
-#include <QFile>
-#include <QTextEdit>
-
-int main(int argc, char **argv)
-{
- QApplication app(argc, argv);
-
- QCommandLineParser parser;
- parser.addHelpOption();
- parser.addPositionalArgument(QStringLiteral("source"), QStringLiteral("The source file to highlight."));
- parser.process(app);
-
- CodeEditor edit;
- edit.resize(1024, 1024);
- edit.show();
- if (parser.positionalArguments().size() == 1)
- edit.openFile(parser.positionalArguments().at(0));
- return app.exec();
-}
diff --git a/src/libs/3rdparty/syntax-highlighting/metainfo.yaml b/src/libs/3rdparty/syntax-highlighting/metainfo.yaml
index 7a3220a31e..a82f7a986d 100644
--- a/src/libs/3rdparty/syntax-highlighting/metainfo.yaml
+++ b/src/libs/3rdparty/syntax-highlighting/metainfo.yaml
@@ -6,7 +6,7 @@ platforms:
- name: Linux
- name: FreeBSD
- name: Windows
- - name: MacOSX
+ - name: macOS
- name: Android
portingAid: false
deprecated: false
diff --git a/src/libs/3rdparty/syntax-highlighting/patches/0001-Remove-highlight-definitions-with-incompatible-licen.patch b/src/libs/3rdparty/syntax-highlighting/patches/0001-Remove-highlight-definitions-with-incompatible-licen.patch
deleted file mode 100644
index 7854f3a218..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/patches/0001-Remove-highlight-definitions-with-incompatible-licen.patch
+++ /dev/null
@@ -1,1381 +0,0 @@
-From 40ee252983bd0d74b43d204de195ef4426ccb690 Mon Sep 17 00:00:00 2001
-From: Alessandro Portale <alessandro.portale@qt.io>
-Date: Tue, 12 Feb 2019 19:43:26 +0100
-Subject: [PATCH 1/6] Remove highlight definitions with incompatible license
-
----
- data/syntax/4dos.xml | 924 ----
- data/syntax/abap.xml | 285 --
- data/syntax/abc.xml | 86 -
- data/syntax/actionscript.xml | 336 --
- data/syntax/ada.xml | 201 -
- data/syntax/adblock.xml | 87 -
- data/syntax/agda.xml | 107 -
- data/syntax/ahdl.xml | 145 -
- data/syntax/ahk.xml | 1143 -----
- data/syntax/ample.xml | 3234 ------------
- data/syntax/ansforth94.xml | 595 ---
- data/syntax/ansic89.xml | 134 -
- data/syntax/ansys.xml | 2056 --------
- data/syntax/apache.xml | 633 ---
- data/syntax/apparmor.xml | 1555 ------
- data/syntax/asm-avr.xml | 212 -
- data/syntax/asm-dsp56k.xml | 329 --
- data/syntax/asm-m68k.xml | 651 ---
- data/syntax/asm6502.xml | 117 -
- data/syntax/asn1.xml | 70 -
- data/syntax/asp.xml | 389 --
- data/syntax/awk.xml | 288 --
- data/syntax/bibtex.xml | 108 -
- data/syntax/bitbake.xml | 183 -
- data/syntax/bmethod.xml | 96 -
- data/syntax/boo.xml | 304 --
- data/syntax/c.xml | 260 -
- data/syntax/carto-css.xml | 405 --
- data/syntax/ccss.xml | 655 ---
- data/syntax/cg.xml | 273 -
- data/syntax/cgis.xml | 134 -
- data/syntax/changelog.xml | 29 -
- data/syntax/chicken.xml | 435 --
- data/syntax/cisco.xml | 302 --
- data/syntax/clipper.xml | 498 --
- data/syntax/clojure.xml | 883 ----
- data/syntax/coffee.xml | 340 --
- data/syntax/coldfusion.xml | 731 ---
- data/syntax/commonlisp.xml | 1177 -----
- data/syntax/component-pascal.xml | 172 -
- data/syntax/context.xml | 130 -
- data/syntax/cpp.xml | 2030 --------
- data/syntax/crk.xml | 163 -
- data/syntax/cs.xml | 168 -
- data/syntax/cubescript.xml | 456 --
- data/syntax/cue.xml | 74 -
- data/syntax/curry.xml | 364 --
- data/syntax/d.xml | 819 ---
- data/syntax/debianchangelog.xml | 241 -
- data/syntax/debiancontrol.xml | 65 -
- data/syntax/desktop.xml | 30 -
- data/syntax/diff.xml | 118 -
- data/syntax/djangotemplate.xml | 330 --
- data/syntax/dockerfile.xml | 67 -
- data/syntax/dosbat.xml | 309 --
- data/syntax/dot.xml | 172 -
- data/syntax/doxyfile.xml | 350 --
- data/syntax/doxygenlua.xml | 497 --
- data/syntax/e.xml | 260 -
- data/syntax/eiffel.xml | 125 -
- data/syntax/elixir.xml | 225 -
- data/syntax/email.xml | 101 -
- data/syntax/erlang.xml | 250 -
- data/syntax/euphoria.xml | 378 --
- data/syntax/fasm.xml | 898 ----
- data/syntax/fastq.xml | 35 -
- data/syntax/ferite.xml | 117 -
- data/syntax/fgl-4gl.xml | 363 --
- data/syntax/fgl-per.xml | 172 -
- data/syntax/fortran.xml | 584 ---
- data/syntax/freebasic.xml | 573 ---
- data/syntax/fsharp.xml | 283 --
- data/syntax/fstab.xml | 96 -
- data/syntax/ftl.xml | 84 -
- data/syntax/gap.xml | 7835 -----------------------------
- data/syntax/gcc.xml | 610 ---
- data/syntax/gcode.xml | 202 -
- data/syntax/gdb-bt.xml | 53 -
- data/syntax/gdb.xml | 72 -
- data/syntax/gdbinit.xml | 24 -
- data/syntax/gdl.xml | 313 --
- data/syntax/gettext.xml | 170 -
- data/syntax/git-ignore.xml | 41 -
- data/syntax/git-rebase.xml | 65 -
- data/syntax/gitolite.xml | 44 -
- data/syntax/glosstex.xml | 45 -
- data/syntax/glsl.xml | 1217 -----
- data/syntax/gnuassembler.xml | 303 --
- data/syntax/gnuplot.xml | 890 ----
- data/syntax/go.xml | 175 -
- data/syntax/grammar.xml | 145 -
- data/syntax/groovy.xml | 3889 --------------
- data/syntax/haml.xml | 500 --
- data/syntax/hamlet.xml | 104 -
- data/syntax/haskell.xml | 649 ---
- data/syntax/haxe.xml | 181 -
- data/syntax/hunspell-aff.xml | 239 -
- data/syntax/hunspell-dat.xml | 48 -
- data/syntax/hunspell-dic.xml | 85 -
- data/syntax/hunspell-idx.xml | 40 -
- data/syntax/idconsole.xml | 2145 --------
- data/syntax/idl.xml | 114 -
- data/syntax/ilerpg.xml | 701 ---
- data/syntax/inform.xml | 405 --
- data/syntax/intelhex.xml | 41 -
- data/syntax/isocpp.xml | 825 ---
- data/syntax/j.xml | 119 -
- data/syntax/jam.xml | 340 --
- data/syntax/javascript-react.xml | 259 -
- data/syntax/javascript.xml | 283 --
- data/syntax/jira.xml | 718 ---
- data/syntax/jsp.xml | 3061 -----------
- data/syntax/julia.xml | 289 --
- data/syntax/k.xml | 430 --
- data/syntax/kbasic.xml | 97 -
- data/syntax/kconfig.xml | 1144 -----
- data/syntax/kdesrc-buildrc.xml | 247 -
- data/syntax/kotlin.xml | 298 --
- data/syntax/latex.xml | 697 ---
- data/syntax/ld.xml | 84 -
- data/syntax/ldif.xml | 304 --
- data/syntax/less.xml | 1467 ------
- data/syntax/lex.xml | 144 -
- data/syntax/lilypond.xml | 1199 -----
- data/syntax/literate-curry.xml | 50 -
- data/syntax/literate-haskell.xml | 38 -
- data/syntax/logcat.xml | 202 -
- data/syntax/logtalk.xml | 248 -
- data/syntax/lpc.xml | 167 -
- data/syntax/lsl.xml | 1050 ----
- data/syntax/lua.xml | 538 --
- data/syntax/m3u.xml | 51 -
- data/syntax/m4.xml | 193 -
- data/syntax/mab.xml | 30 -
- data/syntax/magma.xml | 154 -
- data/syntax/mako.xml | 253 -
- data/syntax/mandoc.xml | 88 -
- data/syntax/mason.xml | 233 -
- data/syntax/mathematica.xml | 3229 ------------
- data/syntax/matlab.xml | 230 -
- data/syntax/maxima.xml | 1890 -------
- data/syntax/mediawiki.xml | 622 ---
- data/syntax/mel.xml | 1380 -----
- data/syntax/mergetagtext.xml | 145 -
- data/syntax/meson.xml | 117 -
- data/syntax/metafont.xml | 986 ----
- data/syntax/metamath.xml | 122 -
- data/syntax/mib.xml | 182 -
- data/syntax/mips.xml | 359 --
- data/syntax/modelica.xml | 260 -
- data/syntax/modelines.xml | 197 -
- data/syntax/modula-2-iso-only.xml | 342 --
- data/syntax/modula-2-pim-only.xml | 308 --
- data/syntax/modula-2-r10-only.xml | 347 --
- data/syntax/modula-2.xml | 1191 -----
- data/syntax/monobasic.xml | 216 -
- data/syntax/mup.xml | 865 ----
- data/syntax/nagios.xml | 83 -
- data/syntax/nasm.xml | 893 ----
- data/syntax/nemerle.xml | 208 -
- data/syntax/nesc.xml | 185 -
- data/syntax/noweb.xml | 52 -
- data/syntax/nsis.xml | 494 --
- data/syntax/objectivec.xml | 130 -
- data/syntax/objectivecpp.xml | 300 --
- data/syntax/ocaml.xml | 624 ---
- data/syntax/ocamllex.xml | 81 -
- data/syntax/ocamlyacc.xml | 160 -
- data/syntax/octave.xml | 2218 --------
- data/syntax/oors.xml | 87 -
- data/syntax/opal.xml | 331 --
- data/syntax/opencl.xml | 260 -
- data/syntax/openscad.xml | 164 -
- data/syntax/pango.xml | 160 -
- data/syntax/pascal.xml | 219 -
- data/syntax/pgn.xml | 108 -
- data/syntax/php.xml | 5705 ---------------------
- data/syntax/picsrc.xml | 401 --
- data/syntax/pig.xml | 205 -
- data/syntax/pike.xml | 121 -
- data/syntax/pli.xml | 454 --
- data/syntax/ply.xml | 54 -
- data/syntax/pony.xml | 314 --
- data/syntax/postscript.xml | 434 --
- data/syntax/povray.xml | 967 ----
- data/syntax/ppd.xml | 58 -
- data/syntax/praat.xml | 510 --
- data/syntax/progress.xml | 1707 -------
- data/syntax/prolog.xml | 1031 ----
- data/syntax/protobuf.xml | 120 -
- data/syntax/pug.xml | 118 -
- data/syntax/puppet.xml | 711 ---
- data/syntax/purebasic.xml | 1767 -------
- data/syntax/q.xml | 210 -
- data/syntax/qmake.xml | 659 ---
- data/syntax/qml.xml | 168 -
- data/syntax/r.xml | 152 -
- data/syntax/rapidq.xml | 432 --
- data/syntax/rdoc.xml | 388 --
- data/syntax/relaxng.xml | 119 -
- data/syntax/relaxngcompact.xml | 108 -
- data/syntax/replicode.xml | 196 -
- data/syntax/rest.xml | 108 -
- data/syntax/rexx.xml | 199 -
- data/syntax/rhtml.xml | 1237 -----
- data/syntax/rib.xml | 143 -
- data/syntax/rmarkdown.xml | 81 -
- data/syntax/roff.xml | 197 -
- data/syntax/rpmspec.xml | 505 --
- data/syntax/rsiidl.xml | 440 --
- data/syntax/rtf.xml | 49 -
- data/syntax/rust.xml | 374 --
- data/syntax/sather.xml | 141 -
- data/syntax/scala.xml | 3505 -------------
- data/syntax/scheme.xml | 394 --
- data/syntax/sci.xml | 1191 -----
- data/syntax/scss.xml | 2242 ---------
- data/syntax/sed.xml | 267 -
- data/syntax/selinux-cil.xml | 683 ---
- data/syntax/selinux-fc.xml | 299 --
- data/syntax/selinux.xml | 1627 ------
- data/syntax/sgml.xml | 46 -
- data/syntax/sieve.xml | 152 -
- data/syntax/sisu.xml | 154 -
- data/syntax/sml.xml | 104 -
- data/syntax/spice.xml | 51 -
- data/syntax/sql-mysql.xml | 481 --
- data/syntax/sql-oracle.xml | 1954 -------
- data/syntax/sql-postgresql.xml | 1074 ----
- data/syntax/sql.xml | 955 ----
- data/syntax/stan.xml | 129 -
- data/syntax/stata.xml | 3223 ------------
- data/syntax/stl.xml | 31 -
- data/syntax/systemc.xml | 133 -
- data/syntax/systemverilog.xml | 1005 ----
- data/syntax/tads3.xml | 176 -
- data/syntax/taskjuggler.xml | 390 --
- data/syntax/tcl.xml | 565 ---
- data/syntax/tcsh.xml | 810 ---
- data/syntax/template-toolkit.xml | 321 --
- data/syntax/texinfo.xml | 63 -
- data/syntax/textile.xml | 100 -
- data/syntax/tibasic.xml | 71 -
- data/syntax/tiger.xml | 96 -
- data/syntax/toml.xml | 135 -
- data/syntax/txt2tags.xml | 67 -
- data/syntax/uscript.xml | 188 -
- data/syntax/vala.xml | 288 --
- data/syntax/varnish.xml | 349 --
- data/syntax/varnish4.xml | 372 --
- data/syntax/varnishcc.xml | 101 -
- data/syntax/varnishcc4.xml | 128 -
- data/syntax/varnishtest.xml | 547 --
- data/syntax/varnishtest4.xml | 706 ---
- data/syntax/vcard.xml | 45 -
- data/syntax/velocity.xml | 51 -
- data/syntax/vera.xml | 648 ---
- data/syntax/verilog.xml | 252 -
- data/syntax/vhdl.xml | 654 ---
- data/syntax/vrml.xml | 139 -
- data/syntax/wavefront-obj.xml | 69 -
- data/syntax/winehq.xml | 34 -
- data/syntax/wml.xml | 209 -
- data/syntax/xharbour.xml | 539 --
- data/syntax/xmldebug.xml | 592 ---
- data/syntax/xonotic-console.xml | 5660 ---------------------
- data/syntax/xorg.xml | 65 -
- data/syntax/xslt.xml | 440 --
- data/syntax/xul.xml | 624 ---
- data/syntax/yacas.xml | 236 -
- data/syntax/yaml.xml | 572 ---
- data/syntax/yang.xml | 155 -
- data/syntax/zonnon.xml | 144 -
- data/syntax/zsh.xml | 959 ----
- 274 files changed, 145722 deletions(-)
- delete mode 100644 data/syntax/4dos.xml
- delete mode 100644 data/syntax/abap.xml
- delete mode 100644 data/syntax/abc.xml
- delete mode 100644 data/syntax/actionscript.xml
- delete mode 100644 data/syntax/ada.xml
- delete mode 100644 data/syntax/adblock.xml
- delete mode 100644 data/syntax/agda.xml
- delete mode 100644 data/syntax/ahdl.xml
- delete mode 100644 data/syntax/ahk.xml
- delete mode 100644 data/syntax/ample.xml
- delete mode 100644 data/syntax/ansforth94.xml
- delete mode 100644 data/syntax/ansic89.xml
- delete mode 100644 data/syntax/ansys.xml
- delete mode 100644 data/syntax/apache.xml
- delete mode 100644 data/syntax/apparmor.xml
- delete mode 100644 data/syntax/asm-avr.xml
- delete mode 100644 data/syntax/asm-dsp56k.xml
- delete mode 100644 data/syntax/asm-m68k.xml
- delete mode 100644 data/syntax/asm6502.xml
- delete mode 100644 data/syntax/asn1.xml
- delete mode 100644 data/syntax/asp.xml
- delete mode 100644 data/syntax/awk.xml
- delete mode 100644 data/syntax/bibtex.xml
- delete mode 100644 data/syntax/bitbake.xml
- delete mode 100644 data/syntax/bmethod.xml
- delete mode 100644 data/syntax/boo.xml
- delete mode 100644 data/syntax/c.xml
- delete mode 100644 data/syntax/carto-css.xml
- delete mode 100644 data/syntax/ccss.xml
- delete mode 100644 data/syntax/cg.xml
- delete mode 100644 data/syntax/cgis.xml
- delete mode 100644 data/syntax/changelog.xml
- delete mode 100644 data/syntax/chicken.xml
- delete mode 100644 data/syntax/cisco.xml
- delete mode 100644 data/syntax/clipper.xml
- delete mode 100644 data/syntax/clojure.xml
- delete mode 100644 data/syntax/coffee.xml
- delete mode 100644 data/syntax/coldfusion.xml
- delete mode 100644 data/syntax/commonlisp.xml
- delete mode 100644 data/syntax/component-pascal.xml
- delete mode 100644 data/syntax/context.xml
- delete mode 100644 data/syntax/cpp.xml
- delete mode 100644 data/syntax/crk.xml
- delete mode 100644 data/syntax/cs.xml
- delete mode 100644 data/syntax/cubescript.xml
- delete mode 100644 data/syntax/cue.xml
- delete mode 100644 data/syntax/curry.xml
- delete mode 100644 data/syntax/d.xml
- delete mode 100644 data/syntax/debianchangelog.xml
- delete mode 100644 data/syntax/debiancontrol.xml
- delete mode 100644 data/syntax/desktop.xml
- delete mode 100644 data/syntax/diff.xml
- delete mode 100644 data/syntax/djangotemplate.xml
- delete mode 100644 data/syntax/dockerfile.xml
- delete mode 100644 data/syntax/dosbat.xml
- delete mode 100644 data/syntax/dot.xml
- delete mode 100644 data/syntax/doxyfile.xml
- delete mode 100644 data/syntax/doxygenlua.xml
- delete mode 100644 data/syntax/e.xml
- delete mode 100644 data/syntax/eiffel.xml
- delete mode 100644 data/syntax/elixir.xml
- delete mode 100644 data/syntax/email.xml
- delete mode 100644 data/syntax/erlang.xml
- delete mode 100644 data/syntax/euphoria.xml
- delete mode 100644 data/syntax/fasm.xml
- delete mode 100644 data/syntax/fastq.xml
- delete mode 100644 data/syntax/ferite.xml
- delete mode 100644 data/syntax/fgl-4gl.xml
- delete mode 100644 data/syntax/fgl-per.xml
- delete mode 100644 data/syntax/fortran.xml
- delete mode 100644 data/syntax/freebasic.xml
- delete mode 100644 data/syntax/fsharp.xml
- delete mode 100644 data/syntax/fstab.xml
- delete mode 100644 data/syntax/ftl.xml
- delete mode 100644 data/syntax/gap.xml
- delete mode 100644 data/syntax/gcc.xml
- delete mode 100644 data/syntax/gcode.xml
- delete mode 100644 data/syntax/gdb-bt.xml
- delete mode 100644 data/syntax/gdb.xml
- delete mode 100644 data/syntax/gdbinit.xml
- delete mode 100644 data/syntax/gdl.xml
- delete mode 100644 data/syntax/gettext.xml
- delete mode 100644 data/syntax/git-ignore.xml
- delete mode 100644 data/syntax/git-rebase.xml
- delete mode 100644 data/syntax/gitolite.xml
- delete mode 100644 data/syntax/glosstex.xml
- delete mode 100644 data/syntax/glsl.xml
- delete mode 100644 data/syntax/gnuassembler.xml
- delete mode 100644 data/syntax/gnuplot.xml
- delete mode 100644 data/syntax/go.xml
- delete mode 100644 data/syntax/grammar.xml
- delete mode 100644 data/syntax/groovy.xml
- delete mode 100644 data/syntax/haml.xml
- delete mode 100644 data/syntax/hamlet.xml
- delete mode 100644 data/syntax/haskell.xml
- delete mode 100644 data/syntax/haxe.xml
- delete mode 100644 data/syntax/hunspell-aff.xml
- delete mode 100644 data/syntax/hunspell-dat.xml
- delete mode 100644 data/syntax/hunspell-dic.xml
- delete mode 100644 data/syntax/hunspell-idx.xml
- delete mode 100644 data/syntax/idconsole.xml
- delete mode 100644 data/syntax/idl.xml
- delete mode 100644 data/syntax/ilerpg.xml
- delete mode 100644 data/syntax/inform.xml
- delete mode 100644 data/syntax/intelhex.xml
- delete mode 100644 data/syntax/isocpp.xml
- delete mode 100644 data/syntax/j.xml
- delete mode 100644 data/syntax/jam.xml
- delete mode 100644 data/syntax/javascript-react.xml
- delete mode 100644 data/syntax/javascript.xml
- delete mode 100644 data/syntax/jira.xml
- delete mode 100644 data/syntax/jsp.xml
- delete mode 100644 data/syntax/julia.xml
- delete mode 100644 data/syntax/k.xml
- delete mode 100644 data/syntax/kbasic.xml
- delete mode 100644 data/syntax/kconfig.xml
- delete mode 100644 data/syntax/kdesrc-buildrc.xml
- delete mode 100644 data/syntax/kotlin.xml
- delete mode 100644 data/syntax/latex.xml
- delete mode 100644 data/syntax/ld.xml
- delete mode 100644 data/syntax/ldif.xml
- delete mode 100644 data/syntax/less.xml
- delete mode 100644 data/syntax/lex.xml
- delete mode 100644 data/syntax/lilypond.xml
- delete mode 100644 data/syntax/literate-curry.xml
- delete mode 100644 data/syntax/literate-haskell.xml
- delete mode 100644 data/syntax/logcat.xml
- delete mode 100644 data/syntax/logtalk.xml
- delete mode 100644 data/syntax/lpc.xml
- delete mode 100644 data/syntax/lsl.xml
- delete mode 100644 data/syntax/lua.xml
- delete mode 100644 data/syntax/m3u.xml
- delete mode 100644 data/syntax/m4.xml
- delete mode 100644 data/syntax/mab.xml
- delete mode 100644 data/syntax/magma.xml
- delete mode 100644 data/syntax/mako.xml
- delete mode 100644 data/syntax/mandoc.xml
- delete mode 100644 data/syntax/mason.xml
- delete mode 100644 data/syntax/mathematica.xml
- delete mode 100644 data/syntax/matlab.xml
- delete mode 100644 data/syntax/maxima.xml
- delete mode 100644 data/syntax/mediawiki.xml
- delete mode 100644 data/syntax/mel.xml
- delete mode 100644 data/syntax/mergetagtext.xml
- delete mode 100644 data/syntax/meson.xml
- delete mode 100644 data/syntax/metafont.xml
- delete mode 100644 data/syntax/metamath.xml
- delete mode 100644 data/syntax/mib.xml
- delete mode 100644 data/syntax/mips.xml
- delete mode 100644 data/syntax/modelica.xml
- delete mode 100644 data/syntax/modelines.xml
- delete mode 100644 data/syntax/modula-2-iso-only.xml
- delete mode 100644 data/syntax/modula-2-pim-only.xml
- delete mode 100644 data/syntax/modula-2-r10-only.xml
- delete mode 100644 data/syntax/modula-2.xml
- delete mode 100644 data/syntax/monobasic.xml
- delete mode 100644 data/syntax/mup.xml
- delete mode 100644 data/syntax/nagios.xml
- delete mode 100644 data/syntax/nasm.xml
- delete mode 100644 data/syntax/nemerle.xml
- delete mode 100644 data/syntax/nesc.xml
- delete mode 100644 data/syntax/noweb.xml
- delete mode 100644 data/syntax/nsis.xml
- delete mode 100644 data/syntax/objectivec.xml
- delete mode 100644 data/syntax/objectivecpp.xml
- delete mode 100644 data/syntax/ocaml.xml
- delete mode 100644 data/syntax/ocamllex.xml
- delete mode 100644 data/syntax/ocamlyacc.xml
- delete mode 100644 data/syntax/octave.xml
- delete mode 100644 data/syntax/oors.xml
- delete mode 100644 data/syntax/opal.xml
- delete mode 100644 data/syntax/opencl.xml
- delete mode 100644 data/syntax/openscad.xml
- delete mode 100644 data/syntax/pango.xml
- delete mode 100644 data/syntax/pascal.xml
- delete mode 100644 data/syntax/pgn.xml
- delete mode 100644 data/syntax/php.xml
- delete mode 100644 data/syntax/picsrc.xml
- delete mode 100644 data/syntax/pig.xml
- delete mode 100644 data/syntax/pike.xml
- delete mode 100644 data/syntax/pli.xml
- delete mode 100644 data/syntax/ply.xml
- delete mode 100644 data/syntax/pony.xml
- delete mode 100644 data/syntax/postscript.xml
- delete mode 100644 data/syntax/povray.xml
- delete mode 100644 data/syntax/ppd.xml
- delete mode 100644 data/syntax/praat.xml
- delete mode 100644 data/syntax/progress.xml
- delete mode 100644 data/syntax/prolog.xml
- delete mode 100644 data/syntax/protobuf.xml
- delete mode 100644 data/syntax/pug.xml
- delete mode 100644 data/syntax/puppet.xml
- delete mode 100644 data/syntax/purebasic.xml
- delete mode 100644 data/syntax/q.xml
- delete mode 100644 data/syntax/qmake.xml
- delete mode 100644 data/syntax/qml.xml
- delete mode 100644 data/syntax/r.xml
- delete mode 100644 data/syntax/rapidq.xml
- delete mode 100644 data/syntax/rdoc.xml
- delete mode 100644 data/syntax/relaxng.xml
- delete mode 100644 data/syntax/relaxngcompact.xml
- delete mode 100644 data/syntax/replicode.xml
- delete mode 100644 data/syntax/rest.xml
- delete mode 100644 data/syntax/rexx.xml
- delete mode 100644 data/syntax/rhtml.xml
- delete mode 100644 data/syntax/rib.xml
- delete mode 100644 data/syntax/rmarkdown.xml
- delete mode 100644 data/syntax/roff.xml
- delete mode 100644 data/syntax/rpmspec.xml
- delete mode 100644 data/syntax/rsiidl.xml
- delete mode 100644 data/syntax/rtf.xml
- delete mode 100644 data/syntax/rust.xml
- delete mode 100644 data/syntax/sather.xml
- delete mode 100644 data/syntax/scala.xml
- delete mode 100644 data/syntax/scheme.xml
- delete mode 100644 data/syntax/sci.xml
- delete mode 100644 data/syntax/scss.xml
- delete mode 100644 data/syntax/sed.xml
- delete mode 100644 data/syntax/selinux-cil.xml
- delete mode 100644 data/syntax/selinux-fc.xml
- delete mode 100644 data/syntax/selinux.xml
- delete mode 100644 data/syntax/sgml.xml
- delete mode 100644 data/syntax/sieve.xml
- delete mode 100644 data/syntax/sisu.xml
- delete mode 100644 data/syntax/sml.xml
- delete mode 100644 data/syntax/spice.xml
- delete mode 100644 data/syntax/sql-mysql.xml
- delete mode 100644 data/syntax/sql-oracle.xml
- delete mode 100644 data/syntax/sql-postgresql.xml
- delete mode 100644 data/syntax/sql.xml
- delete mode 100644 data/syntax/stan.xml
- delete mode 100644 data/syntax/stata.xml
- delete mode 100644 data/syntax/stl.xml
- delete mode 100644 data/syntax/systemc.xml
- delete mode 100644 data/syntax/systemverilog.xml
- delete mode 100644 data/syntax/tads3.xml
- delete mode 100644 data/syntax/taskjuggler.xml
- delete mode 100644 data/syntax/tcl.xml
- delete mode 100644 data/syntax/tcsh.xml
- delete mode 100644 data/syntax/template-toolkit.xml
- delete mode 100644 data/syntax/texinfo.xml
- delete mode 100644 data/syntax/textile.xml
- delete mode 100644 data/syntax/tibasic.xml
- delete mode 100644 data/syntax/tiger.xml
- delete mode 100644 data/syntax/toml.xml
- delete mode 100644 data/syntax/txt2tags.xml
- delete mode 100644 data/syntax/uscript.xml
- delete mode 100644 data/syntax/vala.xml
- delete mode 100644 data/syntax/varnish.xml
- delete mode 100644 data/syntax/varnish4.xml
- delete mode 100644 data/syntax/varnishcc.xml
- delete mode 100644 data/syntax/varnishcc4.xml
- delete mode 100644 data/syntax/varnishtest.xml
- delete mode 100644 data/syntax/varnishtest4.xml
- delete mode 100644 data/syntax/vcard.xml
- delete mode 100644 data/syntax/velocity.xml
- delete mode 100644 data/syntax/vera.xml
- delete mode 100644 data/syntax/verilog.xml
- delete mode 100644 data/syntax/vhdl.xml
- delete mode 100644 data/syntax/vrml.xml
- delete mode 100644 data/syntax/wavefront-obj.xml
- delete mode 100644 data/syntax/winehq.xml
- delete mode 100644 data/syntax/wml.xml
- delete mode 100644 data/syntax/xharbour.xml
- delete mode 100644 data/syntax/xmldebug.xml
- delete mode 100644 data/syntax/xonotic-console.xml
- delete mode 100644 data/syntax/xorg.xml
- delete mode 100644 data/syntax/xslt.xml
- delete mode 100644 data/syntax/xul.xml
- delete mode 100644 data/syntax/yacas.xml
- delete mode 100644 data/syntax/yaml.xml
- delete mode 100644 data/syntax/yang.xml
- delete mode 100644 data/syntax/zonnon.xml
- delete mode 100644 data/syntax/zsh.xml
-
-diff --git a/data/syntax/4dos.xml b/data/syntax/4dos.xml
-deleted file mode 100644
-index b5f72ec..0000000
-diff --git a/data/syntax/abap.xml b/data/syntax/abap.xml
-deleted file mode 100644
-index 12c604a..0000000
-diff --git a/data/syntax/abc.xml b/data/syntax/abc.xml
-deleted file mode 100644
-index a4c3d9f..0000000
-diff --git a/data/syntax/actionscript.xml b/data/syntax/actionscript.xml
-deleted file mode 100644
-index 89c91a9..0000000
-diff --git a/data/syntax/ada.xml b/data/syntax/ada.xml
-deleted file mode 100644
-index 64bb9b6..0000000
-diff --git a/data/syntax/adblock.xml b/data/syntax/adblock.xml
-deleted file mode 100644
-index 796b299..0000000
-diff --git a/data/syntax/agda.xml b/data/syntax/agda.xml
-deleted file mode 100644
-index 958bc95..0000000
-diff --git a/data/syntax/ahdl.xml b/data/syntax/ahdl.xml
-deleted file mode 100644
-index 76bd335..0000000
-diff --git a/data/syntax/ahk.xml b/data/syntax/ahk.xml
-deleted file mode 100644
-index 79965c4..0000000
-diff --git a/data/syntax/ample.xml b/data/syntax/ample.xml
-deleted file mode 100644
-index 2c77865..0000000
-diff --git a/data/syntax/ansforth94.xml b/data/syntax/ansforth94.xml
-deleted file mode 100644
-index 44876e8..0000000
-diff --git a/data/syntax/ansic89.xml b/data/syntax/ansic89.xml
-deleted file mode 100644
-index e442560..0000000
-diff --git a/data/syntax/ansys.xml b/data/syntax/ansys.xml
-deleted file mode 100644
-index d405220..0000000
-diff --git a/data/syntax/apache.xml b/data/syntax/apache.xml
-deleted file mode 100644
-index 732d769..0000000
-diff --git a/data/syntax/apparmor.xml b/data/syntax/apparmor.xml
-deleted file mode 100644
-index 0b81467..0000000
-diff --git a/data/syntax/asm-avr.xml b/data/syntax/asm-avr.xml
-deleted file mode 100644
-index 7075d10..0000000
-diff --git a/data/syntax/asm-dsp56k.xml b/data/syntax/asm-dsp56k.xml
-deleted file mode 100644
-index c9d7cd1..0000000
-diff --git a/data/syntax/asm-m68k.xml b/data/syntax/asm-m68k.xml
-deleted file mode 100644
-index bd5c36f..0000000
-diff --git a/data/syntax/asm6502.xml b/data/syntax/asm6502.xml
-deleted file mode 100644
-index 47e1179..0000000
-diff --git a/data/syntax/asn1.xml b/data/syntax/asn1.xml
-deleted file mode 100644
-index ec3432a..0000000
-diff --git a/data/syntax/asp.xml b/data/syntax/asp.xml
-deleted file mode 100644
-index e03e23b..0000000
-diff --git a/data/syntax/awk.xml b/data/syntax/awk.xml
-deleted file mode 100644
-index 9288a13..0000000
-diff --git a/data/syntax/bibtex.xml b/data/syntax/bibtex.xml
-deleted file mode 100644
-index 339f477..0000000
-diff --git a/data/syntax/bitbake.xml b/data/syntax/bitbake.xml
-deleted file mode 100644
-index 056edf6..0000000
-diff --git a/data/syntax/bmethod.xml b/data/syntax/bmethod.xml
-deleted file mode 100644
-index 153b114..0000000
-diff --git a/data/syntax/boo.xml b/data/syntax/boo.xml
-deleted file mode 100644
-index 26ec818..0000000
-diff --git a/data/syntax/c.xml b/data/syntax/c.xml
-deleted file mode 100644
-index 6f224fc..0000000
-diff --git a/data/syntax/carto-css.xml b/data/syntax/carto-css.xml
-deleted file mode 100644
-index 5ce4474..0000000
-diff --git a/data/syntax/ccss.xml b/data/syntax/ccss.xml
-deleted file mode 100644
-index 4648420..0000000
-diff --git a/data/syntax/cg.xml b/data/syntax/cg.xml
-deleted file mode 100644
-index 0ad61f1..0000000
-diff --git a/data/syntax/cgis.xml b/data/syntax/cgis.xml
-deleted file mode 100644
-index 5b718c7..0000000
-diff --git a/data/syntax/changelog.xml b/data/syntax/changelog.xml
-deleted file mode 100644
-index e004816..0000000
-diff --git a/data/syntax/chicken.xml b/data/syntax/chicken.xml
-deleted file mode 100644
-index eaa9e97..0000000
-diff --git a/data/syntax/cisco.xml b/data/syntax/cisco.xml
-deleted file mode 100644
-index c3d78f9..0000000
-diff --git a/data/syntax/clipper.xml b/data/syntax/clipper.xml
-deleted file mode 100644
-index 898ff31..0000000
-diff --git a/data/syntax/clojure.xml b/data/syntax/clojure.xml
-deleted file mode 100644
-index 855e01a..0000000
-diff --git a/data/syntax/coffee.xml b/data/syntax/coffee.xml
-deleted file mode 100644
-index 58dc498..0000000
-diff --git a/data/syntax/coldfusion.xml b/data/syntax/coldfusion.xml
-deleted file mode 100644
-index 1875eee..0000000
-diff --git a/data/syntax/commonlisp.xml b/data/syntax/commonlisp.xml
-deleted file mode 100644
-index 49dd7f5..0000000
-diff --git a/data/syntax/component-pascal.xml b/data/syntax/component-pascal.xml
-deleted file mode 100644
-index 5017e06..0000000
-diff --git a/data/syntax/context.xml b/data/syntax/context.xml
-deleted file mode 100644
-index c1f1296..0000000
-diff --git a/data/syntax/cpp.xml b/data/syntax/cpp.xml
-deleted file mode 100644
-index e370efb..0000000
-diff --git a/data/syntax/crk.xml b/data/syntax/crk.xml
-deleted file mode 100644
-index 211ddcc..0000000
-diff --git a/data/syntax/cs.xml b/data/syntax/cs.xml
-deleted file mode 100644
-index 264d1f4..0000000
-diff --git a/data/syntax/cubescript.xml b/data/syntax/cubescript.xml
-deleted file mode 100644
-index 5677dbf..0000000
-diff --git a/data/syntax/cue.xml b/data/syntax/cue.xml
-deleted file mode 100644
-index fc4eb1b..0000000
-diff --git a/data/syntax/curry.xml b/data/syntax/curry.xml
-deleted file mode 100644
-index b289f1c..0000000
-diff --git a/data/syntax/d.xml b/data/syntax/d.xml
-deleted file mode 100644
-index 91cb3e8..0000000
-diff --git a/data/syntax/debianchangelog.xml b/data/syntax/debianchangelog.xml
-deleted file mode 100644
-index 5de2900..0000000
-diff --git a/data/syntax/debiancontrol.xml b/data/syntax/debiancontrol.xml
-deleted file mode 100644
-index d3149c6..0000000
-diff --git a/data/syntax/desktop.xml b/data/syntax/desktop.xml
-deleted file mode 100644
-index fc508c4..0000000
-diff --git a/data/syntax/diff.xml b/data/syntax/diff.xml
-deleted file mode 100644
-index 0d2ade7..0000000
-diff --git a/data/syntax/djangotemplate.xml b/data/syntax/djangotemplate.xml
-deleted file mode 100644
-index 6b1fa4f..0000000
-diff --git a/data/syntax/dockerfile.xml b/data/syntax/dockerfile.xml
-deleted file mode 100644
-index 980fd04..0000000
-diff --git a/data/syntax/dosbat.xml b/data/syntax/dosbat.xml
-deleted file mode 100644
-index 807763b..0000000
-diff --git a/data/syntax/dot.xml b/data/syntax/dot.xml
-deleted file mode 100644
-index 6f8aacf..0000000
-diff --git a/data/syntax/doxyfile.xml b/data/syntax/doxyfile.xml
-deleted file mode 100644
-index 1002d67..0000000
-diff --git a/data/syntax/doxygenlua.xml b/data/syntax/doxygenlua.xml
-deleted file mode 100644
-index f3298b5..0000000
-diff --git a/data/syntax/e.xml b/data/syntax/e.xml
-deleted file mode 100644
-index 0b96d97..0000000
-diff --git a/data/syntax/eiffel.xml b/data/syntax/eiffel.xml
-deleted file mode 100644
-index 51420ec..0000000
-diff --git a/data/syntax/elixir.xml b/data/syntax/elixir.xml
-deleted file mode 100644
-index f7a4cab..0000000
-diff --git a/data/syntax/email.xml b/data/syntax/email.xml
-deleted file mode 100644
-index de1d44c..0000000
-diff --git a/data/syntax/erlang.xml b/data/syntax/erlang.xml
-deleted file mode 100644
-index 7bbcbc9..0000000
-diff --git a/data/syntax/euphoria.xml b/data/syntax/euphoria.xml
-deleted file mode 100644
-index ef28077..0000000
-diff --git a/data/syntax/fasm.xml b/data/syntax/fasm.xml
-deleted file mode 100644
-index 04158a5..0000000
-diff --git a/data/syntax/fastq.xml b/data/syntax/fastq.xml
-deleted file mode 100644
-index 30387fc..0000000
-diff --git a/data/syntax/ferite.xml b/data/syntax/ferite.xml
-deleted file mode 100644
-index 13951c7..0000000
-diff --git a/data/syntax/fgl-4gl.xml b/data/syntax/fgl-4gl.xml
-deleted file mode 100644
-index b546243..0000000
-diff --git a/data/syntax/fgl-per.xml b/data/syntax/fgl-per.xml
-deleted file mode 100644
-index b3cea4d..0000000
-diff --git a/data/syntax/fortran.xml b/data/syntax/fortran.xml
-deleted file mode 100644
-index 3baf579..0000000
-diff --git a/data/syntax/freebasic.xml b/data/syntax/freebasic.xml
-deleted file mode 100644
-index e7f3831..0000000
-diff --git a/data/syntax/fsharp.xml b/data/syntax/fsharp.xml
-deleted file mode 100644
-index ad42ca0..0000000
-diff --git a/data/syntax/fstab.xml b/data/syntax/fstab.xml
-deleted file mode 100644
-index c047037..0000000
-diff --git a/data/syntax/ftl.xml b/data/syntax/ftl.xml
-deleted file mode 100644
-index e20db76..0000000
-diff --git a/data/syntax/gap.xml b/data/syntax/gap.xml
-deleted file mode 100644
-index 43bcaff..0000000
-diff --git a/data/syntax/gcc.xml b/data/syntax/gcc.xml
-deleted file mode 100644
-index 258686d..0000000
-diff --git a/data/syntax/gcode.xml b/data/syntax/gcode.xml
-deleted file mode 100644
-index 23b6318..0000000
-diff --git a/data/syntax/gdb-bt.xml b/data/syntax/gdb-bt.xml
-deleted file mode 100644
-index 2ebe7e1..0000000
-diff --git a/data/syntax/gdb.xml b/data/syntax/gdb.xml
-deleted file mode 100644
-index ec385de..0000000
-diff --git a/data/syntax/gdbinit.xml b/data/syntax/gdbinit.xml
-deleted file mode 100644
-index 7999bf5..0000000
-diff --git a/data/syntax/gdl.xml b/data/syntax/gdl.xml
-deleted file mode 100644
-index 8966f5b..0000000
-diff --git a/data/syntax/gettext.xml b/data/syntax/gettext.xml
-deleted file mode 100644
-index 858fcf2..0000000
-diff --git a/data/syntax/git-ignore.xml b/data/syntax/git-ignore.xml
-deleted file mode 100644
-index ceb25e4..0000000
-diff --git a/data/syntax/git-rebase.xml b/data/syntax/git-rebase.xml
-deleted file mode 100644
-index 49c2dae..0000000
-diff --git a/data/syntax/gitolite.xml b/data/syntax/gitolite.xml
-deleted file mode 100644
-index d82838e..0000000
-diff --git a/data/syntax/glosstex.xml b/data/syntax/glosstex.xml
-deleted file mode 100644
-index 751cce4..0000000
-diff --git a/data/syntax/glsl.xml b/data/syntax/glsl.xml
-deleted file mode 100644
-index 09d171a..0000000
-diff --git a/data/syntax/gnuassembler.xml b/data/syntax/gnuassembler.xml
-deleted file mode 100644
-index 3820342..0000000
-diff --git a/data/syntax/gnuplot.xml b/data/syntax/gnuplot.xml
-deleted file mode 100644
-index 97ee96a..0000000
-diff --git a/data/syntax/go.xml b/data/syntax/go.xml
-deleted file mode 100644
-index 61d422d..0000000
-diff --git a/data/syntax/grammar.xml b/data/syntax/grammar.xml
-deleted file mode 100644
-index b797f3c..0000000
-diff --git a/data/syntax/groovy.xml b/data/syntax/groovy.xml
-deleted file mode 100644
-index 16118a9..0000000
-diff --git a/data/syntax/haml.xml b/data/syntax/haml.xml
-deleted file mode 100644
-index 1358edd..0000000
-diff --git a/data/syntax/hamlet.xml b/data/syntax/hamlet.xml
-deleted file mode 100644
-index 9d5b61b..0000000
-diff --git a/data/syntax/haskell.xml b/data/syntax/haskell.xml
-deleted file mode 100644
-index c105be9..0000000
-diff --git a/data/syntax/haxe.xml b/data/syntax/haxe.xml
-deleted file mode 100644
-index 3c8cd0d..0000000
-diff --git a/data/syntax/hunspell-aff.xml b/data/syntax/hunspell-aff.xml
-deleted file mode 100644
-index 843d505..0000000
-diff --git a/data/syntax/hunspell-dat.xml b/data/syntax/hunspell-dat.xml
-deleted file mode 100644
-index a57fd58..0000000
-diff --git a/data/syntax/hunspell-dic.xml b/data/syntax/hunspell-dic.xml
-deleted file mode 100644
-index d714d8d..0000000
-diff --git a/data/syntax/hunspell-idx.xml b/data/syntax/hunspell-idx.xml
-deleted file mode 100644
-index 27ba59a..0000000
-diff --git a/data/syntax/idconsole.xml b/data/syntax/idconsole.xml
-deleted file mode 100644
-index 5464bd9..0000000
-diff --git a/data/syntax/idl.xml b/data/syntax/idl.xml
-deleted file mode 100644
-index a8a2787..0000000
-diff --git a/data/syntax/ilerpg.xml b/data/syntax/ilerpg.xml
-deleted file mode 100644
-index 4858efb..0000000
-diff --git a/data/syntax/inform.xml b/data/syntax/inform.xml
-deleted file mode 100644
-index 8bc04a2..0000000
-diff --git a/data/syntax/intelhex.xml b/data/syntax/intelhex.xml
-deleted file mode 100644
-index cfadf67..0000000
-diff --git a/data/syntax/isocpp.xml b/data/syntax/isocpp.xml
-deleted file mode 100644
-index 56f171b..0000000
-diff --git a/data/syntax/j.xml b/data/syntax/j.xml
-deleted file mode 100644
-index fae5b2f..0000000
-diff --git a/data/syntax/jam.xml b/data/syntax/jam.xml
-deleted file mode 100644
-index 0d8e520..0000000
-diff --git a/data/syntax/javascript-react.xml b/data/syntax/javascript-react.xml
-deleted file mode 100644
-index 98d0612..0000000
-diff --git a/data/syntax/javascript.xml b/data/syntax/javascript.xml
-deleted file mode 100644
-index a76e9cc..0000000
-diff --git a/data/syntax/jira.xml b/data/syntax/jira.xml
-deleted file mode 100644
-index 8274223..0000000
-diff --git a/data/syntax/jsp.xml b/data/syntax/jsp.xml
-deleted file mode 100644
-index 90614ac..0000000
-diff --git a/data/syntax/julia.xml b/data/syntax/julia.xml
-deleted file mode 100644
-index ed21005..0000000
-diff --git a/data/syntax/k.xml b/data/syntax/k.xml
-deleted file mode 100644
-index 9cd0fe3..0000000
-diff --git a/data/syntax/kbasic.xml b/data/syntax/kbasic.xml
-deleted file mode 100644
-index 41fc705..0000000
-diff --git a/data/syntax/kconfig.xml b/data/syntax/kconfig.xml
-deleted file mode 100644
-index c430772..0000000
-diff --git a/data/syntax/kdesrc-buildrc.xml b/data/syntax/kdesrc-buildrc.xml
-deleted file mode 100644
-index 6eaddff..0000000
-diff --git a/data/syntax/kotlin.xml b/data/syntax/kotlin.xml
-deleted file mode 100644
-index db58f67..0000000
-diff --git a/data/syntax/latex.xml b/data/syntax/latex.xml
-deleted file mode 100644
-index 9583e55..0000000
-diff --git a/data/syntax/ld.xml b/data/syntax/ld.xml
-deleted file mode 100644
-index 0f7a8af..0000000
-diff --git a/data/syntax/ldif.xml b/data/syntax/ldif.xml
-deleted file mode 100644
-index c49dbf7..0000000
-diff --git a/data/syntax/less.xml b/data/syntax/less.xml
-deleted file mode 100644
-index 38eb490..0000000
-diff --git a/data/syntax/lex.xml b/data/syntax/lex.xml
-deleted file mode 100644
-index 7f60575..0000000
-diff --git a/data/syntax/lilypond.xml b/data/syntax/lilypond.xml
-deleted file mode 100644
-index fc92d54..0000000
-diff --git a/data/syntax/literate-curry.xml b/data/syntax/literate-curry.xml
-deleted file mode 100644
-index ceb53b2..0000000
-diff --git a/data/syntax/literate-haskell.xml b/data/syntax/literate-haskell.xml
-deleted file mode 100644
-index 0089cb1..0000000
-diff --git a/data/syntax/logcat.xml b/data/syntax/logcat.xml
-deleted file mode 100644
-index 3481bdc..0000000
-diff --git a/data/syntax/logtalk.xml b/data/syntax/logtalk.xml
-deleted file mode 100644
-index 43d063c..0000000
-diff --git a/data/syntax/lpc.xml b/data/syntax/lpc.xml
-deleted file mode 100644
-index bf1dac5..0000000
-diff --git a/data/syntax/lsl.xml b/data/syntax/lsl.xml
-deleted file mode 100644
-index 2d0fc51..0000000
-diff --git a/data/syntax/lua.xml b/data/syntax/lua.xml
-deleted file mode 100644
-index cb842c9..0000000
-diff --git a/data/syntax/m3u.xml b/data/syntax/m3u.xml
-deleted file mode 100644
-index 99171b0..0000000
-diff --git a/data/syntax/m4.xml b/data/syntax/m4.xml
-deleted file mode 100644
-index 687c3ea..0000000
-diff --git a/data/syntax/mab.xml b/data/syntax/mab.xml
-deleted file mode 100644
-index 387432d..0000000
-diff --git a/data/syntax/magma.xml b/data/syntax/magma.xml
-deleted file mode 100644
-index a2fe461..0000000
-diff --git a/data/syntax/mako.xml b/data/syntax/mako.xml
-deleted file mode 100644
-index 7e9a071..0000000
-diff --git a/data/syntax/mandoc.xml b/data/syntax/mandoc.xml
-deleted file mode 100644
-index ed23b82..0000000
-diff --git a/data/syntax/mason.xml b/data/syntax/mason.xml
-deleted file mode 100644
-index 32c2885..0000000
-diff --git a/data/syntax/mathematica.xml b/data/syntax/mathematica.xml
-deleted file mode 100644
-index 3deb3de..0000000
-diff --git a/data/syntax/matlab.xml b/data/syntax/matlab.xml
-deleted file mode 100644
-index 1a93ccc..0000000
-diff --git a/data/syntax/maxima.xml b/data/syntax/maxima.xml
-deleted file mode 100644
-index cd06a3a..0000000
-diff --git a/data/syntax/mediawiki.xml b/data/syntax/mediawiki.xml
-deleted file mode 100644
-index accd0ec..0000000
-diff --git a/data/syntax/mel.xml b/data/syntax/mel.xml
-deleted file mode 100644
-index 8806852..0000000
-diff --git a/data/syntax/mergetagtext.xml b/data/syntax/mergetagtext.xml
-deleted file mode 100644
-index efcc1f7..0000000
-diff --git a/data/syntax/meson.xml b/data/syntax/meson.xml
-deleted file mode 100644
-index 4ecf547..0000000
-diff --git a/data/syntax/metafont.xml b/data/syntax/metafont.xml
-deleted file mode 100644
-index 88166ef..0000000
-diff --git a/data/syntax/metamath.xml b/data/syntax/metamath.xml
-deleted file mode 100644
-index 232603a..0000000
-diff --git a/data/syntax/mib.xml b/data/syntax/mib.xml
-deleted file mode 100644
-index 0455206..0000000
-diff --git a/data/syntax/mips.xml b/data/syntax/mips.xml
-deleted file mode 100644
-index 6802706..0000000
-diff --git a/data/syntax/modelica.xml b/data/syntax/modelica.xml
-deleted file mode 100644
-index 76cf281..0000000
-diff --git a/data/syntax/modelines.xml b/data/syntax/modelines.xml
-deleted file mode 100644
-index 0f3c882..0000000
-diff --git a/data/syntax/modula-2-iso-only.xml b/data/syntax/modula-2-iso-only.xml
-deleted file mode 100644
-index 9da7ae3..0000000
-diff --git a/data/syntax/modula-2-pim-only.xml b/data/syntax/modula-2-pim-only.xml
-deleted file mode 100644
-index 9906339..0000000
-diff --git a/data/syntax/modula-2-r10-only.xml b/data/syntax/modula-2-r10-only.xml
-deleted file mode 100644
-index de4fd1b..0000000
-diff --git a/data/syntax/modula-2.xml b/data/syntax/modula-2.xml
-deleted file mode 100644
-index da3676b..0000000
-diff --git a/data/syntax/monobasic.xml b/data/syntax/monobasic.xml
-deleted file mode 100644
-index 558dded..0000000
-diff --git a/data/syntax/mup.xml b/data/syntax/mup.xml
-deleted file mode 100644
-index a33fb49..0000000
-diff --git a/data/syntax/nagios.xml b/data/syntax/nagios.xml
-deleted file mode 100644
-index 20b81ea..0000000
-diff --git a/data/syntax/nasm.xml b/data/syntax/nasm.xml
-deleted file mode 100644
-index ba50d50..0000000
-diff --git a/data/syntax/nemerle.xml b/data/syntax/nemerle.xml
-deleted file mode 100644
-index 208d70d..0000000
-diff --git a/data/syntax/nesc.xml b/data/syntax/nesc.xml
-deleted file mode 100644
-index 5a5d0db..0000000
-diff --git a/data/syntax/noweb.xml b/data/syntax/noweb.xml
-deleted file mode 100644
-index 09641af..0000000
-diff --git a/data/syntax/nsis.xml b/data/syntax/nsis.xml
-deleted file mode 100644
-index 567419d..0000000
-diff --git a/data/syntax/objectivec.xml b/data/syntax/objectivec.xml
-deleted file mode 100644
-index bdad6fe..0000000
-diff --git a/data/syntax/objectivecpp.xml b/data/syntax/objectivecpp.xml
-deleted file mode 100644
-index a48f9c9..0000000
-diff --git a/data/syntax/ocaml.xml b/data/syntax/ocaml.xml
-deleted file mode 100644
-index 4d47d7b..0000000
-diff --git a/data/syntax/ocamllex.xml b/data/syntax/ocamllex.xml
-deleted file mode 100644
-index 14b5837..0000000
-diff --git a/data/syntax/ocamlyacc.xml b/data/syntax/ocamlyacc.xml
-deleted file mode 100644
-index 6d4d7ba..0000000
-diff --git a/data/syntax/octave.xml b/data/syntax/octave.xml
-deleted file mode 100644
-index 5cdd261..0000000
-diff --git a/data/syntax/oors.xml b/data/syntax/oors.xml
-deleted file mode 100644
-index d28011c..0000000
-diff --git a/data/syntax/opal.xml b/data/syntax/opal.xml
-deleted file mode 100644
-index e390d13..0000000
-diff --git a/data/syntax/opencl.xml b/data/syntax/opencl.xml
-deleted file mode 100644
-index f18ac7f..0000000
-diff --git a/data/syntax/openscad.xml b/data/syntax/openscad.xml
-deleted file mode 100644
-index b5005df..0000000
-diff --git a/data/syntax/pango.xml b/data/syntax/pango.xml
-deleted file mode 100644
-index 2a4f420..0000000
-diff --git a/data/syntax/pascal.xml b/data/syntax/pascal.xml
-deleted file mode 100644
-index 54ae266..0000000
-diff --git a/data/syntax/pgn.xml b/data/syntax/pgn.xml
-deleted file mode 100644
-index ee508df..0000000
-diff --git a/data/syntax/php.xml b/data/syntax/php.xml
-deleted file mode 100644
-index acad510..0000000
-diff --git a/data/syntax/picsrc.xml b/data/syntax/picsrc.xml
-deleted file mode 100644
-index a60c890..0000000
-diff --git a/data/syntax/pig.xml b/data/syntax/pig.xml
-deleted file mode 100644
-index fe2a6f1..0000000
-diff --git a/data/syntax/pike.xml b/data/syntax/pike.xml
-deleted file mode 100644
-index 5a111e2..0000000
-diff --git a/data/syntax/pli.xml b/data/syntax/pli.xml
-deleted file mode 100644
-index 15148bc..0000000
-diff --git a/data/syntax/ply.xml b/data/syntax/ply.xml
-deleted file mode 100644
-index 852f4b3..0000000
-diff --git a/data/syntax/pony.xml b/data/syntax/pony.xml
-deleted file mode 100644
-index 43c24ae..0000000
-diff --git a/data/syntax/postscript.xml b/data/syntax/postscript.xml
-deleted file mode 100644
-index 474167d..0000000
-diff --git a/data/syntax/povray.xml b/data/syntax/povray.xml
-deleted file mode 100644
-index 152559a..0000000
-diff --git a/data/syntax/ppd.xml b/data/syntax/ppd.xml
-deleted file mode 100644
-index a049512..0000000
-diff --git a/data/syntax/praat.xml b/data/syntax/praat.xml
-deleted file mode 100644
-index a10219d..0000000
-diff --git a/data/syntax/progress.xml b/data/syntax/progress.xml
-deleted file mode 100644
-index c467ae1..0000000
-diff --git a/data/syntax/prolog.xml b/data/syntax/prolog.xml
-deleted file mode 100644
-index 7c1b15b..0000000
-diff --git a/data/syntax/protobuf.xml b/data/syntax/protobuf.xml
-deleted file mode 100644
-index bc05a15..0000000
-diff --git a/data/syntax/pug.xml b/data/syntax/pug.xml
-deleted file mode 100644
-index b960317..0000000
-diff --git a/data/syntax/puppet.xml b/data/syntax/puppet.xml
-deleted file mode 100644
-index 57b6eea..0000000
-diff --git a/data/syntax/purebasic.xml b/data/syntax/purebasic.xml
-deleted file mode 100644
-index be1a33b..0000000
-diff --git a/data/syntax/q.xml b/data/syntax/q.xml
-deleted file mode 100644
-index c8fbc8b..0000000
-diff --git a/data/syntax/qmake.xml b/data/syntax/qmake.xml
-deleted file mode 100644
-index bae7296..0000000
-diff --git a/data/syntax/qml.xml b/data/syntax/qml.xml
-deleted file mode 100644
-index b41680d..0000000
-diff --git a/data/syntax/r.xml b/data/syntax/r.xml
-deleted file mode 100644
-index 144418a..0000000
-diff --git a/data/syntax/rapidq.xml b/data/syntax/rapidq.xml
-deleted file mode 100644
-index 67a7a8e..0000000
-diff --git a/data/syntax/rdoc.xml b/data/syntax/rdoc.xml
-deleted file mode 100644
-index e6d73cd..0000000
-diff --git a/data/syntax/relaxng.xml b/data/syntax/relaxng.xml
-deleted file mode 100644
-index 28fdb31..0000000
-diff --git a/data/syntax/relaxngcompact.xml b/data/syntax/relaxngcompact.xml
-deleted file mode 100644
-index 6bcc484..0000000
-diff --git a/data/syntax/replicode.xml b/data/syntax/replicode.xml
-deleted file mode 100644
-index 189a0ff..0000000
-diff --git a/data/syntax/rest.xml b/data/syntax/rest.xml
-deleted file mode 100644
-index 6ca3a1a..0000000
-diff --git a/data/syntax/rexx.xml b/data/syntax/rexx.xml
-deleted file mode 100644
-index a51eb3f..0000000
-diff --git a/data/syntax/rhtml.xml b/data/syntax/rhtml.xml
-deleted file mode 100644
-index 291cea5..0000000
-diff --git a/data/syntax/rib.xml b/data/syntax/rib.xml
-deleted file mode 100644
-index e8ec3da..0000000
-diff --git a/data/syntax/rmarkdown.xml b/data/syntax/rmarkdown.xml
-deleted file mode 100644
-index 6d27305..0000000
-diff --git a/data/syntax/roff.xml b/data/syntax/roff.xml
-deleted file mode 100644
-index 6b428be..0000000
-diff --git a/data/syntax/rpmspec.xml b/data/syntax/rpmspec.xml
-deleted file mode 100644
-index 3ae4eb9..0000000
-diff --git a/data/syntax/rsiidl.xml b/data/syntax/rsiidl.xml
-deleted file mode 100644
-index 11c5c69..0000000
-diff --git a/data/syntax/rtf.xml b/data/syntax/rtf.xml
-deleted file mode 100644
-index 42ce267..0000000
-diff --git a/data/syntax/rust.xml b/data/syntax/rust.xml
-deleted file mode 100644
-index 6657cf2..0000000
-diff --git a/data/syntax/sather.xml b/data/syntax/sather.xml
-deleted file mode 100644
-index 89819cf..0000000
-diff --git a/data/syntax/scala.xml b/data/syntax/scala.xml
-deleted file mode 100644
-index 284281e..0000000
-diff --git a/data/syntax/scheme.xml b/data/syntax/scheme.xml
-deleted file mode 100644
-index 749a913..0000000
-diff --git a/data/syntax/sci.xml b/data/syntax/sci.xml
-deleted file mode 100644
-index 20af7ac..0000000
-diff --git a/data/syntax/scss.xml b/data/syntax/scss.xml
-deleted file mode 100644
-index e4efc0a..0000000
-diff --git a/data/syntax/sed.xml b/data/syntax/sed.xml
-deleted file mode 100644
-index 10e4b8c..0000000
-diff --git a/data/syntax/selinux-cil.xml b/data/syntax/selinux-cil.xml
-deleted file mode 100644
-index 8f6f972..0000000
-diff --git a/data/syntax/selinux-fc.xml b/data/syntax/selinux-fc.xml
-deleted file mode 100644
-index 5bab65f..0000000
-diff --git a/data/syntax/selinux.xml b/data/syntax/selinux.xml
-deleted file mode 100644
-index cb396d1..0000000
-diff --git a/data/syntax/sgml.xml b/data/syntax/sgml.xml
-deleted file mode 100644
-index 87f2fe3..0000000
-diff --git a/data/syntax/sieve.xml b/data/syntax/sieve.xml
-deleted file mode 100644
-index 448766a..0000000
-diff --git a/data/syntax/sisu.xml b/data/syntax/sisu.xml
-deleted file mode 100644
-index 8376855..0000000
-diff --git a/data/syntax/sml.xml b/data/syntax/sml.xml
-deleted file mode 100644
-index 7d63002..0000000
-diff --git a/data/syntax/spice.xml b/data/syntax/spice.xml
-deleted file mode 100644
-index b67763c..0000000
-diff --git a/data/syntax/sql-mysql.xml b/data/syntax/sql-mysql.xml
-deleted file mode 100644
-index f37b706..0000000
-diff --git a/data/syntax/sql-oracle.xml b/data/syntax/sql-oracle.xml
-deleted file mode 100644
-index 7bb32fc..0000000
-diff --git a/data/syntax/sql-postgresql.xml b/data/syntax/sql-postgresql.xml
-deleted file mode 100644
-index 6cf5a6a..0000000
-diff --git a/data/syntax/sql.xml b/data/syntax/sql.xml
-deleted file mode 100644
-index 1e33b86..0000000
-diff --git a/data/syntax/stan.xml b/data/syntax/stan.xml
-deleted file mode 100644
-index 10c1696..0000000
-diff --git a/data/syntax/stata.xml b/data/syntax/stata.xml
-deleted file mode 100644
-index ce3b435..0000000
-diff --git a/data/syntax/stl.xml b/data/syntax/stl.xml
-deleted file mode 100644
-index 41f4856..0000000
-diff --git a/data/syntax/systemc.xml b/data/syntax/systemc.xml
-deleted file mode 100644
-index eb43a8e..0000000
-diff --git a/data/syntax/systemverilog.xml b/data/syntax/systemverilog.xml
-deleted file mode 100644
-index b0e78aa..0000000
-diff --git a/data/syntax/tads3.xml b/data/syntax/tads3.xml
-deleted file mode 100644
-index 7df8690..0000000
-diff --git a/data/syntax/taskjuggler.xml b/data/syntax/taskjuggler.xml
-deleted file mode 100644
-index bb81dfe..0000000
-diff --git a/data/syntax/tcl.xml b/data/syntax/tcl.xml
-deleted file mode 100644
-index 3de1876..0000000
-diff --git a/data/syntax/tcsh.xml b/data/syntax/tcsh.xml
-deleted file mode 100644
-index dc63b3c..0000000
-diff --git a/data/syntax/template-toolkit.xml b/data/syntax/template-toolkit.xml
-deleted file mode 100644
-index b004cb5..0000000
-diff --git a/data/syntax/texinfo.xml b/data/syntax/texinfo.xml
-deleted file mode 100644
-index a43394a..0000000
-diff --git a/data/syntax/textile.xml b/data/syntax/textile.xml
-deleted file mode 100644
-index b0e8250..0000000
-diff --git a/data/syntax/tibasic.xml b/data/syntax/tibasic.xml
-deleted file mode 100644
-index d1a0fca..0000000
-diff --git a/data/syntax/tiger.xml b/data/syntax/tiger.xml
-deleted file mode 100644
-index 482fdc4..0000000
-diff --git a/data/syntax/toml.xml b/data/syntax/toml.xml
-deleted file mode 100644
-index 2369854..0000000
-diff --git a/data/syntax/txt2tags.xml b/data/syntax/txt2tags.xml
-deleted file mode 100644
-index 1635f99..0000000
-diff --git a/data/syntax/uscript.xml b/data/syntax/uscript.xml
-deleted file mode 100644
-index 13d3262..0000000
-diff --git a/data/syntax/vala.xml b/data/syntax/vala.xml
-deleted file mode 100644
-index 8f9aab7..0000000
-diff --git a/data/syntax/varnish.xml b/data/syntax/varnish.xml
-deleted file mode 100644
-index 5916872..0000000
-diff --git a/data/syntax/varnish4.xml b/data/syntax/varnish4.xml
-deleted file mode 100644
-index 03ad852..0000000
-diff --git a/data/syntax/varnishcc.xml b/data/syntax/varnishcc.xml
-deleted file mode 100644
-index 0a0052b..0000000
-diff --git a/data/syntax/varnishcc4.xml b/data/syntax/varnishcc4.xml
-deleted file mode 100644
-index 481ad93..0000000
-diff --git a/data/syntax/varnishtest.xml b/data/syntax/varnishtest.xml
-deleted file mode 100644
-index ceb94d2..0000000
-diff --git a/data/syntax/varnishtest4.xml b/data/syntax/varnishtest4.xml
-deleted file mode 100644
-index ec0d96f..0000000
-diff --git a/data/syntax/vcard.xml b/data/syntax/vcard.xml
-deleted file mode 100644
-index 5eafaa0..0000000
-diff --git a/data/syntax/velocity.xml b/data/syntax/velocity.xml
-deleted file mode 100644
-index cf901d8..0000000
-diff --git a/data/syntax/vera.xml b/data/syntax/vera.xml
-deleted file mode 100644
-index c3802c8..0000000
-diff --git a/data/syntax/verilog.xml b/data/syntax/verilog.xml
-deleted file mode 100644
-index 9f113b4..0000000
-diff --git a/data/syntax/vhdl.xml b/data/syntax/vhdl.xml
-deleted file mode 100644
-index 6a59911..0000000
-diff --git a/data/syntax/vrml.xml b/data/syntax/vrml.xml
-deleted file mode 100644
-index 25839fd..0000000
-diff --git a/data/syntax/wavefront-obj.xml b/data/syntax/wavefront-obj.xml
-deleted file mode 100644
-index 46d6ea7..0000000
-diff --git a/data/syntax/winehq.xml b/data/syntax/winehq.xml
-deleted file mode 100644
-index 112f67d..0000000
-diff --git a/data/syntax/wml.xml b/data/syntax/wml.xml
-deleted file mode 100644
-index 433649c..0000000
-diff --git a/data/syntax/xharbour.xml b/data/syntax/xharbour.xml
-deleted file mode 100644
-index 43336fd..0000000
-diff --git a/data/syntax/xmldebug.xml b/data/syntax/xmldebug.xml
-deleted file mode 100644
-index 68a1e8d..0000000
-diff --git a/data/syntax/xonotic-console.xml b/data/syntax/xonotic-console.xml
-deleted file mode 100644
-index 13ef083..0000000
-diff --git a/data/syntax/xorg.xml b/data/syntax/xorg.xml
-deleted file mode 100644
-index 8ea8855..0000000
-diff --git a/data/syntax/xslt.xml b/data/syntax/xslt.xml
-deleted file mode 100644
-index 37b8577..0000000
-diff --git a/data/syntax/xul.xml b/data/syntax/xul.xml
-deleted file mode 100644
-index 8dc6b65..0000000
-diff --git a/data/syntax/yacas.xml b/data/syntax/yacas.xml
-deleted file mode 100644
-index 12dd078..0000000
-diff --git a/data/syntax/yaml.xml b/data/syntax/yaml.xml
-deleted file mode 100644
-index 7c69ab6..0000000
-diff --git a/data/syntax/yang.xml b/data/syntax/yang.xml
-deleted file mode 100644
-index 051be6a..0000000
-diff --git a/data/syntax/zonnon.xml b/data/syntax/zonnon.xml
-deleted file mode 100644
-index ebe51e1..0000000
-diff --git a/data/syntax/zsh.xml b/data/syntax/zsh.xml
-deleted file mode 100644
-index be40144..0000000
---
-2.20.1.windows.1
-
diff --git a/src/libs/3rdparty/syntax-highlighting/patches/0002-Remove-autotests.patch b/src/libs/3rdparty/syntax-highlighting/patches/0002-Remove-autotests.patch
deleted file mode 100644
index a7174cabea..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/patches/0002-Remove-autotests.patch
+++ /dev/null
@@ -1,2901 +0,0 @@
-From 5191890626af72cabcf49a2b6197ef2e29af218b Mon Sep 17 00:00:00 2001
-From: Alessandro Portale <alessandro.portale@qt.io>
-Date: Tue, 12 Feb 2019 19:35:08 +0100
-Subject: [PATCH 2/6] Remove autotests
-
----
- CMakeLists.txt | 3 -
- autotests/CMakeLists.txt | 49 -
- autotests/folding/Dockerfile.fold | 19 -
- autotests/folding/Doxyfile.example.fold | 2406 ----------------
- autotests/folding/Kconfig.fold | 15 -
- autotests/folding/Makefile.fold | 28 -
- autotests/folding/adblock.txt.fold | 53 -
- autotests/folding/apache.conf.fold | 67 -
- autotests/folding/basic.markdown.fold | 6 -
- autotests/folding/basic.xml.fold | 4 -
- autotests/folding/build.gradle.fold | 57 -
- autotests/folding/clojure.clj.fold | 73 -
- autotests/folding/complex.xml.fold | 13 -
- autotests/folding/craftenv.ps1.fold | 109 -
- autotests/folding/csharp.cs.fold | 38 -
- autotests/folding/cube.obj.fold | 33 -
- autotests/folding/cube.ply.fold | 26 -
- autotests/folding/cube.stl.fold | 30 -
- autotests/folding/example.rmd.fold | 39 -
- autotests/folding/firstNonSpace.c.fold | 7 -
- autotests/folding/folding.cpp.fold | 15 -
- autotests/folding/git-rebase.fold | 32 -
- autotests/folding/hello.exs.fold | 11 -
- autotests/folding/highlight.ahdl.fold | 43 -
- autotests/folding/highlight.asm-avr.fold | 75 -
- autotests/folding/highlight.asm-nasm.fold | 27 -
- autotests/folding/highlight.asp.fold | 58 -
- autotests/folding/highlight.awk.fold | 52 -
- autotests/folding/highlight.bib.fold | 31 -
- autotests/folding/highlight.bt.fold | 31 -
- autotests/folding/highlight.cmake.fold | 111 -
- autotests/folding/highlight.cpp.fold | 507 ----
- autotests/folding/highlight.css.fold | 286 --
- autotests/folding/highlight.d.fold | 195 --
- autotests/folding/highlight.do.fold | 89 -
- autotests/folding/highlight.dox.fold | 227 --
- autotests/folding/highlight.erl.fold | 68 -
- autotests/folding/highlight.exu.fold | 97 -
- autotests/folding/highlight.f90.fold | 181 --
- autotests/folding/highlight.gdb.fold | 156 --
- autotests/folding/highlight.gdbinit.fold | 9 -
- autotests/folding/highlight.glsl.fold | 62 -
- autotests/folding/highlight.hex.fold | 8 -
- autotests/folding/highlight.hs.fold | 139 -
- autotests/folding/highlight.java.fold | 48 -
- autotests/folding/highlight.js.fold | 139 -
- autotests/folding/highlight.jsp.fold | 170 --
- autotests/folding/highlight.less.fold | 701 -----
- autotests/folding/highlight.lex.fold | 82 -
- autotests/folding/highlight.lgt.fold | 448 ---
- autotests/folding/highlight.lhs.fold | 124 -
- autotests/folding/highlight.lisp.fold | 36 -
- autotests/folding/highlight.lua.fold | 160 --
- autotests/folding/highlight.ly.fold | 114 -
- autotests/folding/highlight.m.fold | 61 -
- autotests/folding/highlight.mac.fold | 145 -
- autotests/folding/highlight.mup.fold | 103 -
- autotests/folding/highlight.pb.fold | 87 -
- autotests/folding/highlight.php.fold | 26 -
- autotests/folding/highlight.pike.fold | 24 -
- autotests/folding/highlight.pl.fold | 75 -
- autotests/folding/highlight.pony.fold | 219 --
- autotests/folding/highlight.pov.fold | 76 -
- autotests/folding/highlight.prg.fold | 71 -
- autotests/folding/highlight.qml.fold | 59 -
- autotests/folding/highlight.rb.fold | 551 ----
- autotests/folding/highlight.scad.fold | 23 -
- autotests/folding/highlight.scheme.fold | 186 --
- autotests/folding/highlight.scss.fold | 693 -----
- autotests/folding/highlight.sh.fold | 207 --
- autotests/folding/highlight.spec.fold | 212 --
- autotests/folding/highlight.stan.fold | 380 ---
- autotests/folding/highlight.t2t.fold | 90 -
- autotests/folding/highlight.tcl.fold | 50 -
- autotests/folding/highlight.tex.fold | 85 -
- autotests/folding/highlight.tig.fold | 78 -
- autotests/folding/highlight.wrl.fold | 41 -
- autotests/folding/highlight.xml.fold | 67 -
- autotests/folding/highlight.xsl.fold | 109 -
- autotests/folding/highlight.y.fold | 93 -
- autotests/folding/highlight.yang.fold | 38 -
- autotests/folding/highlight_lpc.c.fold | 64 -
- autotests/folding/highlight_ocaml.ml.fold | 105 -
- autotests/folding/highlight_octave.m.fold | 74 -
- autotests/folding/learnelixir.exs.fold | 397 ---
- autotests/folding/light52_muldiv.vhdl.fold | 239 --
- autotests/folding/light52_tb.vhdl.fold | 186 --
- autotests/folding/meson.build.fold | 21 -
- autotests/folding/modelines.py.fold | 10 -
- autotests/folding/or1200_dc_fsm.v.fold | 563 ----
- autotests/folding/or1200_du.v.fold | 1803 ------------
- .../folding/preprocessor-bug363280.c.fold | 8 -
- .../folding/preprocessor-bug363280.cpp.fold | 8 -
- autotests/folding/review128925-1.css.fold | 112 -
- autotests/folding/review128925-1.scss.fold | 211 --
- autotests/folding/review128925-2.css.fold | 62 -
- autotests/folding/review128925-2.scss.fold | 81 -
- autotests/folding/review128935.html.fold | 164 --
- autotests/folding/test-iso.mod.fold | 96 -
- autotests/folding/test-pim.mod.fold | 90 -
- autotests/folding/test-r10.mod.fold | 112 -
- autotests/folding/test.Rd.fold | 51 -
- autotests/folding/test.bash.fold | 23 -
- autotests/folding/test.bb.fold | 34 -
- autotests/folding/test.c.fold | 56 -
- autotests/folding/test.cil.fold | 153 --
- autotests/folding/test.coffee.fold | 71 -
- autotests/folding/test.css.fold | 18 -
- autotests/folding/test.desktop.fold | 19 -
- autotests/folding/test.diff.fold | 48 -
- autotests/folding/test.eml.fold | 94 -
- autotests/folding/test.fc.fold | 116 -
- autotests/folding/test.frag.fold | 26 -
- autotests/folding/test.htm.fold | 17 -
- autotests/folding/test.ijs.fold | 61 -
- autotests/folding/test.ini.fold | 18 -
- autotests/folding/test.js.fold | 23 -
- autotests/folding/test.json.fold | 13 -
- autotests/folding/test.jsx.fold | 50 -
- autotests/folding/test.logcat.fold | 90 -
- autotests/folding/test.markdown.fold | 67 -
- autotests/folding/test.mib.fold | 526 ----
- autotests/folding/test.mm.fold | 42 -
- autotests/folding/test.mod.fold | 96 -
- autotests/folding/test.mss.fold | 215 --
- autotests/folding/test.py.fold | 50 -
- autotests/folding/test.qdocconf.fold | 65 -
- autotests/folding/test.qml.fold | 23 -
- autotests/folding/test.rexx.fold | 52 -
- autotests/folding/test.rs.fold | 87 -
- autotests/folding/test.sieve.fold | 387 ---
- autotests/folding/test.sql.fold | 65 -
- autotests/folding/test.sql_oracle.fold | 54 -
- autotests/folding/test.te.fold | 139 -
- autotests/folding/test.tex.fold | 5 -
- autotests/folding/test.yaml.fold | 151 --
- autotests/folding/test.zsh.fold | 66 -
- .../usr.bin.apparmor-profile-test.fold | 270 --
- autotests/foldingtest.cpp | 191 --
- autotests/highlighter_benchmark.cpp | 145 -
- autotests/html/Dockerfile.html | 26 -
- autotests/html/Doxyfile.example.html | 2413 -----------------
- autotests/html/Kconfig.html | 22 -
- autotests/html/Makefile.html | 35 -
- autotests/html/adblock.txt.html | 60 -
- autotests/html/apache.conf.html | 74 -
- autotests/html/basic.markdown.html | 13 -
- autotests/html/basic.xml.html | 11 -
- autotests/html/build.gradle.html | 64 -
- autotests/html/clojure.clj.html | 80 -
- autotests/html/complex.xml.html | 20 -
- autotests/html/craftenv.ps1.html | 116 -
- autotests/html/csharp.cs.html | 45 -
- autotests/html/cube.obj.html | 40 -
- autotests/html/cube.ply.html | 33 -
- autotests/html/cube.stl.html | 37 -
- autotests/html/example.rmd.html | 46 -
- autotests/html/firstNonSpace.c.html | 14 -
- autotests/html/folding.cpp.html | 22 -
- autotests/html/git-rebase.html | 39 -
- autotests/html/hello.exs.html | 18 -
- autotests/html/highlight.ahdl.html | 50 -
- autotests/html/highlight.asm-avr.html | 82 -
- autotests/html/highlight.asm-nasm.html | 34 -
- autotests/html/highlight.asp.html | 65 -
- autotests/html/highlight.awk.html | 59 -
- autotests/html/highlight.bib.html | 38 -
- autotests/html/highlight.bt.html | 38 -
- autotests/html/highlight.cmake.html | 118 -
- autotests/html/highlight.cpp.html | 514 ----
- autotests/html/highlight.css.html | 293 --
- autotests/html/highlight.d.html | 202 --
- autotests/html/highlight.do.html | 96 -
- autotests/html/highlight.dox.html | 234 --
- autotests/html/highlight.erl.html | 75 -
- autotests/html/highlight.exu.html | 104 -
- autotests/html/highlight.f90.html | 188 --
- autotests/html/highlight.gdb.html | 163 --
- autotests/html/highlight.gdbinit.html | 16 -
- autotests/html/highlight.glsl.html | 69 -
- autotests/html/highlight.hex.html | 15 -
- autotests/html/highlight.hs.html | 146 -
- autotests/html/highlight.java.html | 55 -
- autotests/html/highlight.js.html | 146 -
- autotests/html/highlight.jsp.html | 177 --
- autotests/html/highlight.less.html | 708 -----
- autotests/html/highlight.lex.html | 89 -
- autotests/html/highlight.lgt.html | 455 ----
- autotests/html/highlight.lhs.html | 131 -
- autotests/html/highlight.lisp.html | 43 -
- autotests/html/highlight.lua.html | 167 --
- autotests/html/highlight.ly.html | 121 -
- autotests/html/highlight.m.html | 68 -
- autotests/html/highlight.mac.html | 152 --
- autotests/html/highlight.mup.html | 110 -
- autotests/html/highlight.pb.html | 94 -
- autotests/html/highlight.php.html | 33 -
- autotests/html/highlight.pike.html | 31 -
- autotests/html/highlight.pl.html | 82 -
- autotests/html/highlight.pony.html | 226 --
- autotests/html/highlight.pov.html | 83 -
- autotests/html/highlight.prg.html | 78 -
- autotests/html/highlight.qml.html | 66 -
- autotests/html/highlight.rb.html | 558 ----
- autotests/html/highlight.scad.html | 30 -
- autotests/html/highlight.scheme.html | 193 --
- autotests/html/highlight.scss.html | 700 -----
- autotests/html/highlight.sh.html | 214 --
- autotests/html/highlight.spec.html | 219 --
- autotests/html/highlight.stan.html | 387 ---
- autotests/html/highlight.t2t.html | 97 -
- autotests/html/highlight.tcl.html | 57 -
- autotests/html/highlight.tex.html | 92 -
- autotests/html/highlight.tig.html | 85 -
- autotests/html/highlight.wrl.html | 48 -
- autotests/html/highlight.xml.html | 74 -
- autotests/html/highlight.xsl.html | 116 -
- autotests/html/highlight.y.html | 100 -
- autotests/html/highlight.yang.html | 45 -
- autotests/html/highlight_lpc.c.html | 71 -
- autotests/html/highlight_ocaml.ml.html | 112 -
- autotests/html/highlight_octave.m.html | 81 -
- autotests/html/learnelixir.exs.html | 404 ---
- autotests/html/light52_muldiv.vhdl.html | 246 --
- autotests/html/light52_tb.vhdl.html | 193 --
- autotests/html/meson.build.html | 28 -
- autotests/html/modelines.py.html | 17 -
- autotests/html/or1200_dc_fsm.v.html | 570 ----
- autotests/html/or1200_du.v.html | 1810 -------------
- autotests/html/preprocessor-bug363280.c.html | 15 -
- .../html/preprocessor-bug363280.cpp.html | 15 -
- autotests/html/review128925-1.css.html | 119 -
- autotests/html/review128925-1.scss.html | 218 --
- autotests/html/review128925-2.css.html | 69 -
- autotests/html/review128925-2.scss.html | 88 -
- autotests/html/review128935.html.html | 171 --
- autotests/html/test-iso.mod.html | 103 -
- autotests/html/test-pim.mod.html | 97 -
- autotests/html/test-r10.mod.html | 119 -
- autotests/html/test.Rd.html | 58 -
- autotests/html/test.bash.html | 30 -
- autotests/html/test.bb.html | 41 -
- autotests/html/test.c.html | 63 -
- autotests/html/test.cil.html | 160 --
- autotests/html/test.coffee.html | 78 -
- autotests/html/test.css.html | 25 -
- autotests/html/test.desktop.html | 26 -
- autotests/html/test.diff.html | 55 -
- autotests/html/test.eml.html | 101 -
- autotests/html/test.fc.html | 123 -
- autotests/html/test.frag.html | 33 -
- autotests/html/test.htm.html | 24 -
- autotests/html/test.ijs.html | 68 -
- autotests/html/test.ini.html | 25 -
- autotests/html/test.js.html | 30 -
- autotests/html/test.json.html | 20 -
- autotests/html/test.jsx.html | 57 -
- autotests/html/test.logcat.html | 97 -
- autotests/html/test.markdown.html | 74 -
- autotests/html/test.mib.html | 533 ----
- autotests/html/test.mm.html | 49 -
- autotests/html/test.mod.html | 103 -
- autotests/html/test.mss.html | 222 --
- autotests/html/test.py.html | 57 -
- autotests/html/test.qdocconf.html | 72 -
- autotests/html/test.qml.html | 30 -
- autotests/html/test.rexx.html | 59 -
- autotests/html/test.rs.html | 94 -
- autotests/html/test.sieve.html | 394 ---
- autotests/html/test.sql.html | 72 -
- autotests/html/test.sql_oracle.html | 61 -
- autotests/html/test.te.html | 146 -
- autotests/html/test.tex.html | 12 -
- autotests/html/test.yaml.html | 158 --
- autotests/html/test.zsh.html | 73 -
- .../html/usr.bin.apparmor-profile-test.html | 277 --
- autotests/htmlhighlighter_test.cpp | 121 -
- autotests/input/Dockerfile | 19 -
- autotests/input/Doxyfile.example | 2406 ----------------
- autotests/input/Kconfig | 15 -
- autotests/input/Makefile | 28 -
- autotests/input/adblock.txt | 53 -
- autotests/input/adblock.txt.syntax | 1 -
- autotests/input/apache.conf | 67 -
- autotests/input/apache.conf.syntax | 1 -
- autotests/input/basic.markdown | 6 -
- autotests/input/basic.xml | 4 -
- autotests/input/build.gradle | 57 -
- autotests/input/clojure.clj | 73 -
- autotests/input/complex.xml | 13 -
- autotests/input/craftenv.ps1 | 109 -
- autotests/input/csharp.cs | 38 -
- autotests/input/cube.obj | 33 -
- autotests/input/cube.ply | 26 -
- autotests/input/cube.stl | 30 -
- autotests/input/example.rmd | 39 -
- autotests/input/firstNonSpace.c | 7 -
- autotests/input/folding.cpp | 15 -
- autotests/input/git-rebase | 32 -
- autotests/input/git-rebase.syntax | 1 -
- autotests/input/hello.exs | 11 -
- autotests/input/highlight.ahdl | 43 -
- autotests/input/highlight.asm-avr | 75 -
- autotests/input/highlight.asm-nasm | 27 -
- autotests/input/highlight.asm-nasm.syntax | 1 -
- autotests/input/highlight.asp | 58 -
- autotests/input/highlight.awk | 52 -
- autotests/input/highlight.bib | 31 -
- autotests/input/highlight.bt | 31 -
- autotests/input/highlight.cmake | 111 -
- autotests/input/highlight.cpp | 507 ----
- autotests/input/highlight.css | 286 --
- autotests/input/highlight.d | 195 --
- autotests/input/highlight.do | 89 -
- autotests/input/highlight.dox | 227 --
- autotests/input/highlight.erl | 68 -
- autotests/input/highlight.exu | 97 -
- autotests/input/highlight.f90 | 181 --
- autotests/input/highlight.gdb | 156 --
- autotests/input/highlight.gdbinit | 9 -
- autotests/input/highlight.glsl | 62 -
- autotests/input/highlight.hex | 8 -
- autotests/input/highlight.hs | 139 -
- autotests/input/highlight.java | 48 -
- autotests/input/highlight.js | 139 -
- autotests/input/highlight.jsp | 170 --
- autotests/input/highlight.less | 701 -----
- autotests/input/highlight.lex | 82 -
- autotests/input/highlight.lgt | 448 ---
- autotests/input/highlight.lhs | 124 -
- autotests/input/highlight.lisp | 36 -
- autotests/input/highlight.lua | 160 --
- autotests/input/highlight.ly | 114 -
- autotests/input/highlight.m | 61 -
- autotests/input/highlight.m.syntax | 1 -
- autotests/input/highlight.mac | 145 -
- autotests/input/highlight.mup | 103 -
- autotests/input/highlight.pb | 87 -
- autotests/input/highlight.php | 26 -
- autotests/input/highlight.pike | 24 -
- autotests/input/highlight.pl | 75 -
- autotests/input/highlight.pony | 219 --
- autotests/input/highlight.pov | 76 -
- autotests/input/highlight.prg | 71 -
- autotests/input/highlight.prg.syntax | 1 -
- autotests/input/highlight.qml | 59 -
- autotests/input/highlight.rb | 551 ----
- autotests/input/highlight.scad | 23 -
- autotests/input/highlight.scheme | 186 --
- autotests/input/highlight.scss | 693 -----
- autotests/input/highlight.sh | 207 --
- autotests/input/highlight.sh.syntax | 1 -
- autotests/input/highlight.spec | 212 --
- autotests/input/highlight.stan | 380 ---
- autotests/input/highlight.t2t | 90 -
- autotests/input/highlight.tcl | 50 -
- autotests/input/highlight.tex | 85 -
- autotests/input/highlight.tig | 78 -
- autotests/input/highlight.wrl | 41 -
- autotests/input/highlight.xml | 67 -
- autotests/input/highlight.xsl | 109 -
- autotests/input/highlight.y | 93 -
- autotests/input/highlight.yang | 38 -
- autotests/input/highlight_lpc.c | 64 -
- autotests/input/highlight_lpc.c.syntax | 1 -
- autotests/input/highlight_ocaml.ml | 105 -
- autotests/input/highlight_octave.m | 74 -
- autotests/input/highlight_octave.m.syntax | 1 -
- autotests/input/learnelixir.exs | 397 ---
- autotests/input/light52_muldiv.vhdl | 239 --
- autotests/input/light52_tb.vhdl | 186 --
- autotests/input/meson.build | 21 -
- autotests/input/modelines.py | 10 -
- autotests/input/or1200_dc_fsm.v | 563 ----
- autotests/input/or1200_du.v | 1803 ------------
- autotests/input/preprocessor-bug363280.c | 8 -
- autotests/input/preprocessor-bug363280.cpp | 8 -
- autotests/input/review128925-1.css | 112 -
- autotests/input/review128925-1.scss | 211 --
- autotests/input/review128925-2.css | 62 -
- autotests/input/review128925-2.scss | 81 -
- autotests/input/review128935.html | 164 --
- autotests/input/syntax/testlang.xml | 27 -
- autotests/input/test-iso.mod | 96 -
- autotests/input/test-iso.mod.syntax | 1 -
- autotests/input/test-pim.mod | 90 -
- autotests/input/test-pim.mod.syntax | 1 -
- autotests/input/test-r10.mod | 112 -
- autotests/input/test-r10.mod.syntax | 1 -
- autotests/input/test.Rd | 51 -
- autotests/input/test.bash | 23 -
- autotests/input/test.bb | 34 -
- autotests/input/test.c | 56 -
- autotests/input/test.c.syntax | 1 -
- autotests/input/test.cil | 153 --
- autotests/input/test.coffee | 71 -
- autotests/input/test.css | 18 -
- autotests/input/test.css.syntax | 1 -
- autotests/input/test.desktop | 19 -
- autotests/input/test.diff | 48 -
- autotests/input/test.eml | 94 -
- autotests/input/test.fc | 116 -
- autotests/input/test.frag | 26 -
- autotests/input/test.htm | 17 -
- autotests/input/test.htm.syntax | 1 -
- autotests/input/test.ijs | 61 -
- autotests/input/test.ini | 18 -
- autotests/input/test.js | 23 -
- autotests/input/test.json | 13 -
- autotests/input/test.jsx | 50 -
- autotests/input/test.logcat | 90 -
- autotests/input/test.markdown | 67 -
- autotests/input/test.mib | 526 ----
- autotests/input/test.mm | 42 -
- autotests/input/test.mm.syntax | 1 -
- autotests/input/test.mod | 96 -
- autotests/input/test.mss | 215 --
- autotests/input/test.py | 50 -
- autotests/input/test.qdocconf | 65 -
- autotests/input/test.qml | 23 -
- autotests/input/test.rexx | 52 -
- autotests/input/test.rs | 87 -
- autotests/input/test.sieve | 387 ---
- autotests/input/test.sql | 65 -
- autotests/input/test.sql.syntax | 1 -
- autotests/input/test.sql_oracle | 54 -
- autotests/input/test.sql_oracle.syntax | 1 -
- autotests/input/test.te | 139 -
- autotests/input/test.tex | 5 -
- autotests/input/test.yaml | 151 --
- autotests/input/test.zsh | 66 -
- autotests/input/themes/customtheme.theme | 46 -
- autotests/input/usr.bin.apparmor-profile-test | 270 --
- autotests/reference/Dockerfile.ref | 19 -
- autotests/reference/Doxyfile.example.ref | 2406 ----------------
- autotests/reference/Kconfig.ref | 15 -
- autotests/reference/Makefile.ref | 28 -
- autotests/reference/adblock.txt.ref | 53 -
- autotests/reference/apache.conf.ref | 67 -
- autotests/reference/basic.markdown.ref | 6 -
- autotests/reference/basic.xml.ref | 4 -
- autotests/reference/build.gradle.ref | 57 -
- autotests/reference/clojure.clj.ref | 73 -
- autotests/reference/complex.xml.ref | 13 -
- autotests/reference/craftenv.ps1.ref | 109 -
- autotests/reference/csharp.cs.ref | 38 -
- autotests/reference/cube.obj.ref | 33 -
- autotests/reference/cube.ply.ref | 26 -
- autotests/reference/cube.stl.ref | 30 -
- autotests/reference/example.rmd.ref | 39 -
- autotests/reference/firstNonSpace.c.ref | 7 -
- autotests/reference/folding.cpp.ref | 15 -
- autotests/reference/git-rebase.ref | 32 -
- autotests/reference/hello.exs.ref | 11 -
- autotests/reference/highlight.ahdl.ref | 43 -
- autotests/reference/highlight.asm-avr.ref | 75 -
- autotests/reference/highlight.asm-nasm.ref | 27 -
- autotests/reference/highlight.asp.ref | 58 -
- autotests/reference/highlight.awk.ref | 52 -
- autotests/reference/highlight.bib.ref | 31 -
- autotests/reference/highlight.bt.ref | 31 -
- autotests/reference/highlight.cmake.ref | 111 -
- autotests/reference/highlight.cpp.ref | 507 ----
- autotests/reference/highlight.css.ref | 286 --
- autotests/reference/highlight.d.ref | 195 --
- autotests/reference/highlight.do.ref | 89 -
- autotests/reference/highlight.dox.ref | 227 --
- autotests/reference/highlight.erl.ref | 68 -
- autotests/reference/highlight.exu.ref | 97 -
- autotests/reference/highlight.f90.ref | 181 --
- autotests/reference/highlight.gdb.ref | 156 --
- autotests/reference/highlight.gdbinit.ref | 9 -
- autotests/reference/highlight.glsl.ref | 62 -
- autotests/reference/highlight.hex.ref | 8 -
- autotests/reference/highlight.hs.ref | 139 -
- autotests/reference/highlight.java.ref | 48 -
- autotests/reference/highlight.js.ref | 139 -
- autotests/reference/highlight.jsp.ref | 170 --
- autotests/reference/highlight.less.ref | 701 -----
- autotests/reference/highlight.lex.ref | 82 -
- autotests/reference/highlight.lgt.ref | 448 ---
- autotests/reference/highlight.lhs.ref | 124 -
- autotests/reference/highlight.lisp.ref | 36 -
- autotests/reference/highlight.lua.ref | 160 --
- autotests/reference/highlight.ly.ref | 114 -
- autotests/reference/highlight.m.ref | 61 -
- autotests/reference/highlight.mac.ref | 145 -
- autotests/reference/highlight.mup.ref | 103 -
- autotests/reference/highlight.pb.ref | 87 -
- autotests/reference/highlight.php.ref | 26 -
- autotests/reference/highlight.pike.ref | 24 -
- autotests/reference/highlight.pl.ref | 75 -
- autotests/reference/highlight.pony.ref | 219 --
- autotests/reference/highlight.pov.ref | 76 -
- autotests/reference/highlight.prg.ref | 71 -
- autotests/reference/highlight.qml.ref | 59 -
- autotests/reference/highlight.rb.ref | 551 ----
- autotests/reference/highlight.scad.ref | 23 -
- autotests/reference/highlight.scheme.ref | 186 --
- autotests/reference/highlight.scss.ref | 693 -----
- autotests/reference/highlight.sh.ref | 207 --
- autotests/reference/highlight.spec.ref | 212 --
- autotests/reference/highlight.stan.ref | 380 ---
- autotests/reference/highlight.t2t.ref | 90 -
- autotests/reference/highlight.tcl.ref | 50 -
- autotests/reference/highlight.tex.ref | 85 -
- autotests/reference/highlight.tig.ref | 78 -
- autotests/reference/highlight.wrl.ref | 41 -
- autotests/reference/highlight.xml.ref | 67 -
- autotests/reference/highlight.xsl.ref | 109 -
- autotests/reference/highlight.y.ref | 93 -
- autotests/reference/highlight.yang.ref | 38 -
- autotests/reference/highlight_lpc.c.ref | 64 -
- autotests/reference/highlight_ocaml.ml.ref | 105 -
- autotests/reference/highlight_octave.m.ref | 74 -
- autotests/reference/learnelixir.exs.ref | 397 ---
- autotests/reference/light52_muldiv.vhdl.ref | 239 --
- autotests/reference/light52_tb.vhdl.ref | 186 --
- autotests/reference/meson.build.ref | 21 -
- autotests/reference/modelines.py.ref | 10 -
- autotests/reference/or1200_dc_fsm.v.ref | 563 ----
- autotests/reference/or1200_du.v.ref | 1803 ------------
- .../reference/preprocessor-bug363280.c.ref | 8 -
- .../reference/preprocessor-bug363280.cpp.ref | 8 -
- autotests/reference/review128925-1.css.ref | 112 -
- autotests/reference/review128925-1.scss.ref | 211 --
- autotests/reference/review128925-2.css.ref | 62 -
- autotests/reference/review128925-2.scss.ref | 81 -
- autotests/reference/review128935.html.ref | 164 --
- autotests/reference/test-iso.mod.ref | 96 -
- autotests/reference/test-pim.mod.ref | 90 -
- autotests/reference/test-r10.mod.ref | 112 -
- autotests/reference/test.Rd.ref | 51 -
- autotests/reference/test.bash.ref | 23 -
- autotests/reference/test.bb.ref | 34 -
- autotests/reference/test.c.ref | 56 -
- autotests/reference/test.cil.ref | 153 --
- autotests/reference/test.coffee.ref | 71 -
- autotests/reference/test.css.ref | 18 -
- autotests/reference/test.desktop.ref | 19 -
- autotests/reference/test.diff.ref | 48 -
- autotests/reference/test.eml.ref | 94 -
- autotests/reference/test.fc.ref | 116 -
- autotests/reference/test.frag.ref | 26 -
- autotests/reference/test.htm.ref | 17 -
- autotests/reference/test.ijs.ref | 61 -
- autotests/reference/test.ini.ref | 18 -
- autotests/reference/test.js.ref | 23 -
- autotests/reference/test.json.ref | 13 -
- autotests/reference/test.jsx.ref | 50 -
- autotests/reference/test.logcat.ref | 90 -
- autotests/reference/test.markdown.ref | 67 -
- autotests/reference/test.mib.ref | 526 ----
- autotests/reference/test.mm.ref | 42 -
- autotests/reference/test.mod.ref | 96 -
- autotests/reference/test.mss.ref | 215 --
- autotests/reference/test.py.ref | 50 -
- autotests/reference/test.qdocconf.ref | 65 -
- autotests/reference/test.qml.ref | 23 -
- autotests/reference/test.rexx.ref | 52 -
- autotests/reference/test.rs.ref | 87 -
- autotests/reference/test.sieve.ref | 387 ---
- autotests/reference/test.sql.ref | 65 -
- autotests/reference/test.sql_oracle.ref | 54 -
- autotests/reference/test.te.ref | 139 -
- autotests/reference/test.tex.ref | 5 -
- autotests/reference/test.yaml.ref | 151 --
- autotests/reference/test.zsh.ref | 66 -
- .../usr.bin.apparmor-profile-test.ref | 270 --
- autotests/repository_benchmark.cpp | 49 -
- autotests/syntaxrepository_test.cpp | 406 ---
- autotests/test-config.h.in | 103 -
- autotests/testhighlighter.cpp | 189 --
- autotests/theme_test.cpp | 311 ---
- autotests/update-reference-data.sh.in | 5 -
- autotests/wildcardmatcher_test.cpp | 91 -
- 576 files changed, 79310 deletions(-)
- delete mode 100644 autotests/CMakeLists.txt
- delete mode 100644 autotests/folding/Dockerfile.fold
- delete mode 100644 autotests/folding/Doxyfile.example.fold
- delete mode 100644 autotests/folding/Kconfig.fold
- delete mode 100644 autotests/folding/Makefile.fold
- delete mode 100644 autotests/folding/adblock.txt.fold
- delete mode 100644 autotests/folding/apache.conf.fold
- delete mode 100644 autotests/folding/basic.markdown.fold
- delete mode 100644 autotests/folding/basic.xml.fold
- delete mode 100644 autotests/folding/build.gradle.fold
- delete mode 100644 autotests/folding/clojure.clj.fold
- delete mode 100644 autotests/folding/complex.xml.fold
- delete mode 100644 autotests/folding/craftenv.ps1.fold
- delete mode 100644 autotests/folding/csharp.cs.fold
- delete mode 100644 autotests/folding/cube.obj.fold
- delete mode 100644 autotests/folding/cube.ply.fold
- delete mode 100644 autotests/folding/cube.stl.fold
- delete mode 100644 autotests/folding/example.rmd.fold
- delete mode 100644 autotests/folding/firstNonSpace.c.fold
- delete mode 100644 autotests/folding/folding.cpp.fold
- delete mode 100644 autotests/folding/git-rebase.fold
- delete mode 100644 autotests/folding/hello.exs.fold
- delete mode 100644 autotests/folding/highlight.ahdl.fold
- delete mode 100644 autotests/folding/highlight.asm-avr.fold
- delete mode 100644 autotests/folding/highlight.asm-nasm.fold
- delete mode 100644 autotests/folding/highlight.asp.fold
- delete mode 100644 autotests/folding/highlight.awk.fold
- delete mode 100644 autotests/folding/highlight.bib.fold
- delete mode 100644 autotests/folding/highlight.bt.fold
- delete mode 100644 autotests/folding/highlight.cmake.fold
- delete mode 100644 autotests/folding/highlight.cpp.fold
- delete mode 100644 autotests/folding/highlight.css.fold
- delete mode 100644 autotests/folding/highlight.d.fold
- delete mode 100644 autotests/folding/highlight.do.fold
- delete mode 100644 autotests/folding/highlight.dox.fold
- delete mode 100644 autotests/folding/highlight.erl.fold
- delete mode 100644 autotests/folding/highlight.exu.fold
- delete mode 100644 autotests/folding/highlight.f90.fold
- delete mode 100644 autotests/folding/highlight.gdb.fold
- delete mode 100644 autotests/folding/highlight.gdbinit.fold
- delete mode 100644 autotests/folding/highlight.glsl.fold
- delete mode 100644 autotests/folding/highlight.hex.fold
- delete mode 100644 autotests/folding/highlight.hs.fold
- delete mode 100644 autotests/folding/highlight.java.fold
- delete mode 100644 autotests/folding/highlight.js.fold
- delete mode 100644 autotests/folding/highlight.jsp.fold
- delete mode 100644 autotests/folding/highlight.less.fold
- delete mode 100644 autotests/folding/highlight.lex.fold
- delete mode 100644 autotests/folding/highlight.lgt.fold
- delete mode 100644 autotests/folding/highlight.lhs.fold
- delete mode 100644 autotests/folding/highlight.lisp.fold
- delete mode 100644 autotests/folding/highlight.lua.fold
- delete mode 100644 autotests/folding/highlight.ly.fold
- delete mode 100644 autotests/folding/highlight.m.fold
- delete mode 100644 autotests/folding/highlight.mac.fold
- delete mode 100644 autotests/folding/highlight.mup.fold
- delete mode 100644 autotests/folding/highlight.pb.fold
- delete mode 100644 autotests/folding/highlight.php.fold
- delete mode 100644 autotests/folding/highlight.pike.fold
- delete mode 100644 autotests/folding/highlight.pl.fold
- delete mode 100644 autotests/folding/highlight.pony.fold
- delete mode 100644 autotests/folding/highlight.pov.fold
- delete mode 100644 autotests/folding/highlight.prg.fold
- delete mode 100644 autotests/folding/highlight.qml.fold
- delete mode 100644 autotests/folding/highlight.rb.fold
- delete mode 100644 autotests/folding/highlight.scad.fold
- delete mode 100644 autotests/folding/highlight.scheme.fold
- delete mode 100644 autotests/folding/highlight.scss.fold
- delete mode 100644 autotests/folding/highlight.sh.fold
- delete mode 100644 autotests/folding/highlight.spec.fold
- delete mode 100644 autotests/folding/highlight.stan.fold
- delete mode 100644 autotests/folding/highlight.t2t.fold
- delete mode 100644 autotests/folding/highlight.tcl.fold
- delete mode 100644 autotests/folding/highlight.tex.fold
- delete mode 100644 autotests/folding/highlight.tig.fold
- delete mode 100644 autotests/folding/highlight.wrl.fold
- delete mode 100644 autotests/folding/highlight.xml.fold
- delete mode 100644 autotests/folding/highlight.xsl.fold
- delete mode 100644 autotests/folding/highlight.y.fold
- delete mode 100644 autotests/folding/highlight.yang.fold
- delete mode 100644 autotests/folding/highlight_lpc.c.fold
- delete mode 100644 autotests/folding/highlight_ocaml.ml.fold
- delete mode 100644 autotests/folding/highlight_octave.m.fold
- delete mode 100644 autotests/folding/learnelixir.exs.fold
- delete mode 100644 autotests/folding/light52_muldiv.vhdl.fold
- delete mode 100644 autotests/folding/light52_tb.vhdl.fold
- delete mode 100644 autotests/folding/meson.build.fold
- delete mode 100644 autotests/folding/modelines.py.fold
- delete mode 100644 autotests/folding/or1200_dc_fsm.v.fold
- delete mode 100644 autotests/folding/or1200_du.v.fold
- delete mode 100644 autotests/folding/preprocessor-bug363280.c.fold
- delete mode 100644 autotests/folding/preprocessor-bug363280.cpp.fold
- delete mode 100644 autotests/folding/review128925-1.css.fold
- delete mode 100644 autotests/folding/review128925-1.scss.fold
- delete mode 100644 autotests/folding/review128925-2.css.fold
- delete mode 100644 autotests/folding/review128925-2.scss.fold
- delete mode 100644 autotests/folding/review128935.html.fold
- delete mode 100644 autotests/folding/test-iso.mod.fold
- delete mode 100644 autotests/folding/test-pim.mod.fold
- delete mode 100644 autotests/folding/test-r10.mod.fold
- delete mode 100644 autotests/folding/test.Rd.fold
- delete mode 100644 autotests/folding/test.bash.fold
- delete mode 100644 autotests/folding/test.bb.fold
- delete mode 100644 autotests/folding/test.c.fold
- delete mode 100644 autotests/folding/test.cil.fold
- delete mode 100644 autotests/folding/test.coffee.fold
- delete mode 100644 autotests/folding/test.css.fold
- delete mode 100644 autotests/folding/test.desktop.fold
- delete mode 100644 autotests/folding/test.diff.fold
- delete mode 100644 autotests/folding/test.eml.fold
- delete mode 100644 autotests/folding/test.fc.fold
- delete mode 100644 autotests/folding/test.frag.fold
- delete mode 100644 autotests/folding/test.htm.fold
- delete mode 100644 autotests/folding/test.ijs.fold
- delete mode 100644 autotests/folding/test.ini.fold
- delete mode 100644 autotests/folding/test.js.fold
- delete mode 100644 autotests/folding/test.json.fold
- delete mode 100644 autotests/folding/test.jsx.fold
- delete mode 100644 autotests/folding/test.logcat.fold
- delete mode 100644 autotests/folding/test.markdown.fold
- delete mode 100644 autotests/folding/test.mib.fold
- delete mode 100644 autotests/folding/test.mm.fold
- delete mode 100644 autotests/folding/test.mod.fold
- delete mode 100644 autotests/folding/test.mss.fold
- delete mode 100644 autotests/folding/test.py.fold
- delete mode 100644 autotests/folding/test.qdocconf.fold
- delete mode 100644 autotests/folding/test.qml.fold
- delete mode 100644 autotests/folding/test.rexx.fold
- delete mode 100644 autotests/folding/test.rs.fold
- delete mode 100644 autotests/folding/test.sieve.fold
- delete mode 100644 autotests/folding/test.sql.fold
- delete mode 100644 autotests/folding/test.sql_oracle.fold
- delete mode 100644 autotests/folding/test.te.fold
- delete mode 100644 autotests/folding/test.tex.fold
- delete mode 100644 autotests/folding/test.yaml.fold
- delete mode 100644 autotests/folding/test.zsh.fold
- delete mode 100644 autotests/folding/usr.bin.apparmor-profile-test.fold
- delete mode 100644 autotests/foldingtest.cpp
- delete mode 100644 autotests/highlighter_benchmark.cpp
- delete mode 100644 autotests/html/Dockerfile.html
- delete mode 100644 autotests/html/Doxyfile.example.html
- delete mode 100644 autotests/html/Kconfig.html
- delete mode 100644 autotests/html/Makefile.html
- delete mode 100644 autotests/html/adblock.txt.html
- delete mode 100644 autotests/html/apache.conf.html
- delete mode 100644 autotests/html/basic.markdown.html
- delete mode 100644 autotests/html/basic.xml.html
- delete mode 100644 autotests/html/build.gradle.html
- delete mode 100644 autotests/html/clojure.clj.html
- delete mode 100644 autotests/html/complex.xml.html
- delete mode 100644 autotests/html/craftenv.ps1.html
- delete mode 100644 autotests/html/csharp.cs.html
- delete mode 100644 autotests/html/cube.obj.html
- delete mode 100644 autotests/html/cube.ply.html
- delete mode 100644 autotests/html/cube.stl.html
- delete mode 100644 autotests/html/example.rmd.html
- delete mode 100644 autotests/html/firstNonSpace.c.html
- delete mode 100644 autotests/html/folding.cpp.html
- delete mode 100644 autotests/html/git-rebase.html
- delete mode 100644 autotests/html/hello.exs.html
- delete mode 100644 autotests/html/highlight.ahdl.html
- delete mode 100644 autotests/html/highlight.asm-avr.html
- delete mode 100644 autotests/html/highlight.asm-nasm.html
- delete mode 100644 autotests/html/highlight.asp.html
- delete mode 100644 autotests/html/highlight.awk.html
- delete mode 100644 autotests/html/highlight.bib.html
- delete mode 100644 autotests/html/highlight.bt.html
- delete mode 100644 autotests/html/highlight.cmake.html
- delete mode 100644 autotests/html/highlight.cpp.html
- delete mode 100644 autotests/html/highlight.css.html
- delete mode 100644 autotests/html/highlight.d.html
- delete mode 100644 autotests/html/highlight.do.html
- delete mode 100644 autotests/html/highlight.dox.html
- delete mode 100644 autotests/html/highlight.erl.html
- delete mode 100644 autotests/html/highlight.exu.html
- delete mode 100644 autotests/html/highlight.f90.html
- delete mode 100644 autotests/html/highlight.gdb.html
- delete mode 100644 autotests/html/highlight.gdbinit.html
- delete mode 100644 autotests/html/highlight.glsl.html
- delete mode 100644 autotests/html/highlight.hex.html
- delete mode 100644 autotests/html/highlight.hs.html
- delete mode 100644 autotests/html/highlight.java.html
- delete mode 100644 autotests/html/highlight.js.html
- delete mode 100644 autotests/html/highlight.jsp.html
- delete mode 100644 autotests/html/highlight.less.html
- delete mode 100644 autotests/html/highlight.lex.html
- delete mode 100644 autotests/html/highlight.lgt.html
- delete mode 100644 autotests/html/highlight.lhs.html
- delete mode 100644 autotests/html/highlight.lisp.html
- delete mode 100644 autotests/html/highlight.lua.html
- delete mode 100644 autotests/html/highlight.ly.html
- delete mode 100644 autotests/html/highlight.m.html
- delete mode 100644 autotests/html/highlight.mac.html
- delete mode 100644 autotests/html/highlight.mup.html
- delete mode 100644 autotests/html/highlight.pb.html
- delete mode 100644 autotests/html/highlight.php.html
- delete mode 100644 autotests/html/highlight.pike.html
- delete mode 100644 autotests/html/highlight.pl.html
- delete mode 100644 autotests/html/highlight.pony.html
- delete mode 100644 autotests/html/highlight.pov.html
- delete mode 100644 autotests/html/highlight.prg.html
- delete mode 100644 autotests/html/highlight.qml.html
- delete mode 100644 autotests/html/highlight.rb.html
- delete mode 100644 autotests/html/highlight.scad.html
- delete mode 100644 autotests/html/highlight.scheme.html
- delete mode 100644 autotests/html/highlight.scss.html
- delete mode 100644 autotests/html/highlight.sh.html
- delete mode 100644 autotests/html/highlight.spec.html
- delete mode 100644 autotests/html/highlight.stan.html
- delete mode 100644 autotests/html/highlight.t2t.html
- delete mode 100644 autotests/html/highlight.tcl.html
- delete mode 100644 autotests/html/highlight.tex.html
- delete mode 100644 autotests/html/highlight.tig.html
- delete mode 100644 autotests/html/highlight.wrl.html
- delete mode 100644 autotests/html/highlight.xml.html
- delete mode 100644 autotests/html/highlight.xsl.html
- delete mode 100644 autotests/html/highlight.y.html
- delete mode 100644 autotests/html/highlight.yang.html
- delete mode 100644 autotests/html/highlight_lpc.c.html
- delete mode 100644 autotests/html/highlight_ocaml.ml.html
- delete mode 100644 autotests/html/highlight_octave.m.html
- delete mode 100644 autotests/html/learnelixir.exs.html
- delete mode 100644 autotests/html/light52_muldiv.vhdl.html
- delete mode 100644 autotests/html/light52_tb.vhdl.html
- delete mode 100644 autotests/html/meson.build.html
- delete mode 100644 autotests/html/modelines.py.html
- delete mode 100644 autotests/html/or1200_dc_fsm.v.html
- delete mode 100644 autotests/html/or1200_du.v.html
- delete mode 100644 autotests/html/preprocessor-bug363280.c.html
- delete mode 100644 autotests/html/preprocessor-bug363280.cpp.html
- delete mode 100644 autotests/html/review128925-1.css.html
- delete mode 100644 autotests/html/review128925-1.scss.html
- delete mode 100644 autotests/html/review128925-2.css.html
- delete mode 100644 autotests/html/review128925-2.scss.html
- delete mode 100644 autotests/html/review128935.html.html
- delete mode 100644 autotests/html/test-iso.mod.html
- delete mode 100644 autotests/html/test-pim.mod.html
- delete mode 100644 autotests/html/test-r10.mod.html
- delete mode 100644 autotests/html/test.Rd.html
- delete mode 100644 autotests/html/test.bash.html
- delete mode 100644 autotests/html/test.bb.html
- delete mode 100644 autotests/html/test.c.html
- delete mode 100644 autotests/html/test.cil.html
- delete mode 100644 autotests/html/test.coffee.html
- delete mode 100644 autotests/html/test.css.html
- delete mode 100644 autotests/html/test.desktop.html
- delete mode 100644 autotests/html/test.diff.html
- delete mode 100644 autotests/html/test.eml.html
- delete mode 100644 autotests/html/test.fc.html
- delete mode 100644 autotests/html/test.frag.html
- delete mode 100644 autotests/html/test.htm.html
- delete mode 100644 autotests/html/test.ijs.html
- delete mode 100644 autotests/html/test.ini.html
- delete mode 100644 autotests/html/test.js.html
- delete mode 100644 autotests/html/test.json.html
- delete mode 100644 autotests/html/test.jsx.html
- delete mode 100644 autotests/html/test.logcat.html
- delete mode 100644 autotests/html/test.markdown.html
- delete mode 100644 autotests/html/test.mib.html
- delete mode 100644 autotests/html/test.mm.html
- delete mode 100644 autotests/html/test.mod.html
- delete mode 100644 autotests/html/test.mss.html
- delete mode 100644 autotests/html/test.py.html
- delete mode 100644 autotests/html/test.qdocconf.html
- delete mode 100644 autotests/html/test.qml.html
- delete mode 100644 autotests/html/test.rexx.html
- delete mode 100644 autotests/html/test.rs.html
- delete mode 100644 autotests/html/test.sieve.html
- delete mode 100644 autotests/html/test.sql.html
- delete mode 100644 autotests/html/test.sql_oracle.html
- delete mode 100644 autotests/html/test.te.html
- delete mode 100644 autotests/html/test.tex.html
- delete mode 100644 autotests/html/test.yaml.html
- delete mode 100644 autotests/html/test.zsh.html
- delete mode 100644 autotests/html/usr.bin.apparmor-profile-test.html
- delete mode 100644 autotests/htmlhighlighter_test.cpp
- delete mode 100644 autotests/input/Dockerfile
- delete mode 100644 autotests/input/Doxyfile.example
- delete mode 100644 autotests/input/Kconfig
- delete mode 100644 autotests/input/Makefile
- delete mode 100644 autotests/input/adblock.txt
- delete mode 100644 autotests/input/adblock.txt.syntax
- delete mode 100644 autotests/input/apache.conf
- delete mode 100644 autotests/input/apache.conf.syntax
- delete mode 100644 autotests/input/basic.markdown
- delete mode 100644 autotests/input/basic.xml
- delete mode 100644 autotests/input/build.gradle
- delete mode 100644 autotests/input/clojure.clj
- delete mode 100644 autotests/input/complex.xml
- delete mode 100644 autotests/input/craftenv.ps1
- delete mode 100644 autotests/input/csharp.cs
- delete mode 100644 autotests/input/cube.obj
- delete mode 100644 autotests/input/cube.ply
- delete mode 100644 autotests/input/cube.stl
- delete mode 100644 autotests/input/example.rmd
- delete mode 100644 autotests/input/firstNonSpace.c
- delete mode 100644 autotests/input/folding.cpp
- delete mode 100644 autotests/input/git-rebase
- delete mode 100644 autotests/input/git-rebase.syntax
- delete mode 100644 autotests/input/hello.exs
- delete mode 100644 autotests/input/highlight.ahdl
- delete mode 100644 autotests/input/highlight.asm-avr
- delete mode 100644 autotests/input/highlight.asm-nasm
- delete mode 100644 autotests/input/highlight.asm-nasm.syntax
- delete mode 100644 autotests/input/highlight.asp
- delete mode 100644 autotests/input/highlight.awk
- delete mode 100644 autotests/input/highlight.bib
- delete mode 100644 autotests/input/highlight.bt
- delete mode 100644 autotests/input/highlight.cmake
- delete mode 100644 autotests/input/highlight.cpp
- delete mode 100644 autotests/input/highlight.css
- delete mode 100644 autotests/input/highlight.d
- delete mode 100644 autotests/input/highlight.do
- delete mode 100644 autotests/input/highlight.dox
- delete mode 100644 autotests/input/highlight.erl
- delete mode 100644 autotests/input/highlight.exu
- delete mode 100644 autotests/input/highlight.f90
- delete mode 100644 autotests/input/highlight.gdb
- delete mode 100644 autotests/input/highlight.gdbinit
- delete mode 100644 autotests/input/highlight.glsl
- delete mode 100644 autotests/input/highlight.hex
- delete mode 100644 autotests/input/highlight.hs
- delete mode 100644 autotests/input/highlight.java
- delete mode 100644 autotests/input/highlight.js
- delete mode 100644 autotests/input/highlight.jsp
- delete mode 100644 autotests/input/highlight.less
- delete mode 100644 autotests/input/highlight.lex
- delete mode 100644 autotests/input/highlight.lgt
- delete mode 100644 autotests/input/highlight.lhs
- delete mode 100644 autotests/input/highlight.lisp
- delete mode 100644 autotests/input/highlight.lua
- delete mode 100644 autotests/input/highlight.ly
- delete mode 100644 autotests/input/highlight.m
- delete mode 100644 autotests/input/highlight.m.syntax
- delete mode 100644 autotests/input/highlight.mac
- delete mode 100644 autotests/input/highlight.mup
- delete mode 100644 autotests/input/highlight.pb
- delete mode 100644 autotests/input/highlight.php
- delete mode 100644 autotests/input/highlight.pike
- delete mode 100644 autotests/input/highlight.pl
- delete mode 100644 autotests/input/highlight.pony
- delete mode 100644 autotests/input/highlight.pov
- delete mode 100644 autotests/input/highlight.prg
- delete mode 100644 autotests/input/highlight.prg.syntax
- delete mode 100644 autotests/input/highlight.qml
- delete mode 100644 autotests/input/highlight.rb
- delete mode 100644 autotests/input/highlight.scad
- delete mode 100644 autotests/input/highlight.scheme
- delete mode 100644 autotests/input/highlight.scss
- delete mode 100644 autotests/input/highlight.sh
- delete mode 100644 autotests/input/highlight.sh.syntax
- delete mode 100644 autotests/input/highlight.spec
- delete mode 100644 autotests/input/highlight.stan
- delete mode 100644 autotests/input/highlight.t2t
- delete mode 100644 autotests/input/highlight.tcl
- delete mode 100644 autotests/input/highlight.tex
- delete mode 100644 autotests/input/highlight.tig
- delete mode 100644 autotests/input/highlight.wrl
- delete mode 100644 autotests/input/highlight.xml
- delete mode 100644 autotests/input/highlight.xsl
- delete mode 100644 autotests/input/highlight.y
- delete mode 100644 autotests/input/highlight.yang
- delete mode 100644 autotests/input/highlight_lpc.c
- delete mode 100644 autotests/input/highlight_lpc.c.syntax
- delete mode 100644 autotests/input/highlight_ocaml.ml
- delete mode 100644 autotests/input/highlight_octave.m
- delete mode 100644 autotests/input/highlight_octave.m.syntax
- delete mode 100644 autotests/input/learnelixir.exs
- delete mode 100644 autotests/input/light52_muldiv.vhdl
- delete mode 100644 autotests/input/light52_tb.vhdl
- delete mode 100644 autotests/input/meson.build
- delete mode 100644 autotests/input/modelines.py
- delete mode 100644 autotests/input/or1200_dc_fsm.v
- delete mode 100644 autotests/input/or1200_du.v
- delete mode 100644 autotests/input/preprocessor-bug363280.c
- delete mode 100644 autotests/input/preprocessor-bug363280.cpp
- delete mode 100644 autotests/input/review128925-1.css
- delete mode 100644 autotests/input/review128925-1.scss
- delete mode 100644 autotests/input/review128925-2.css
- delete mode 100644 autotests/input/review128925-2.scss
- delete mode 100644 autotests/input/review128935.html
- delete mode 100644 autotests/input/syntax/testlang.xml
- delete mode 100644 autotests/input/test-iso.mod
- delete mode 100644 autotests/input/test-iso.mod.syntax
- delete mode 100644 autotests/input/test-pim.mod
- delete mode 100644 autotests/input/test-pim.mod.syntax
- delete mode 100644 autotests/input/test-r10.mod
- delete mode 100644 autotests/input/test-r10.mod.syntax
- delete mode 100644 autotests/input/test.Rd
- delete mode 100644 autotests/input/test.bash
- delete mode 100644 autotests/input/test.bb
- delete mode 100644 autotests/input/test.c
- delete mode 100644 autotests/input/test.c.syntax
- delete mode 100644 autotests/input/test.cil
- delete mode 100644 autotests/input/test.coffee
- delete mode 100644 autotests/input/test.css
- delete mode 100644 autotests/input/test.css.syntax
- delete mode 100644 autotests/input/test.desktop
- delete mode 100644 autotests/input/test.diff
- delete mode 100644 autotests/input/test.eml
- delete mode 100644 autotests/input/test.fc
- delete mode 100644 autotests/input/test.frag
- delete mode 100644 autotests/input/test.htm
- delete mode 100644 autotests/input/test.htm.syntax
- delete mode 100644 autotests/input/test.ijs
- delete mode 100644 autotests/input/test.ini
- delete mode 100644 autotests/input/test.js
- delete mode 100644 autotests/input/test.json
- delete mode 100644 autotests/input/test.jsx
- delete mode 100644 autotests/input/test.logcat
- delete mode 100644 autotests/input/test.markdown
- delete mode 100644 autotests/input/test.mib
- delete mode 100644 autotests/input/test.mm
- delete mode 100644 autotests/input/test.mm.syntax
- delete mode 100644 autotests/input/test.mod
- delete mode 100644 autotests/input/test.mss
- delete mode 100644 autotests/input/test.py
- delete mode 100644 autotests/input/test.qdocconf
- delete mode 100644 autotests/input/test.qml
- delete mode 100644 autotests/input/test.rexx
- delete mode 100644 autotests/input/test.rs
- delete mode 100644 autotests/input/test.sieve
- delete mode 100644 autotests/input/test.sql
- delete mode 100644 autotests/input/test.sql.syntax
- delete mode 100644 autotests/input/test.sql_oracle
- delete mode 100644 autotests/input/test.sql_oracle.syntax
- delete mode 100644 autotests/input/test.te
- delete mode 100644 autotests/input/test.tex
- delete mode 100644 autotests/input/test.yaml
- delete mode 100644 autotests/input/test.zsh
- delete mode 100644 autotests/input/themes/customtheme.theme
- delete mode 100644 autotests/input/usr.bin.apparmor-profile-test
- delete mode 100644 autotests/reference/Dockerfile.ref
- delete mode 100644 autotests/reference/Doxyfile.example.ref
- delete mode 100644 autotests/reference/Kconfig.ref
- delete mode 100644 autotests/reference/Makefile.ref
- delete mode 100644 autotests/reference/adblock.txt.ref
- delete mode 100644 autotests/reference/apache.conf.ref
- delete mode 100644 autotests/reference/basic.markdown.ref
- delete mode 100644 autotests/reference/basic.xml.ref
- delete mode 100644 autotests/reference/build.gradle.ref
- delete mode 100644 autotests/reference/clojure.clj.ref
- delete mode 100644 autotests/reference/complex.xml.ref
- delete mode 100644 autotests/reference/craftenv.ps1.ref
- delete mode 100644 autotests/reference/csharp.cs.ref
- delete mode 100644 autotests/reference/cube.obj.ref
- delete mode 100644 autotests/reference/cube.ply.ref
- delete mode 100644 autotests/reference/cube.stl.ref
- delete mode 100644 autotests/reference/example.rmd.ref
- delete mode 100644 autotests/reference/firstNonSpace.c.ref
- delete mode 100644 autotests/reference/folding.cpp.ref
- delete mode 100644 autotests/reference/git-rebase.ref
- delete mode 100644 autotests/reference/hello.exs.ref
- delete mode 100644 autotests/reference/highlight.ahdl.ref
- delete mode 100644 autotests/reference/highlight.asm-avr.ref
- delete mode 100644 autotests/reference/highlight.asm-nasm.ref
- delete mode 100644 autotests/reference/highlight.asp.ref
- delete mode 100644 autotests/reference/highlight.awk.ref
- delete mode 100644 autotests/reference/highlight.bib.ref
- delete mode 100644 autotests/reference/highlight.bt.ref
- delete mode 100644 autotests/reference/highlight.cmake.ref
- delete mode 100644 autotests/reference/highlight.cpp.ref
- delete mode 100644 autotests/reference/highlight.css.ref
- delete mode 100644 autotests/reference/highlight.d.ref
- delete mode 100644 autotests/reference/highlight.do.ref
- delete mode 100644 autotests/reference/highlight.dox.ref
- delete mode 100644 autotests/reference/highlight.erl.ref
- delete mode 100644 autotests/reference/highlight.exu.ref
- delete mode 100644 autotests/reference/highlight.f90.ref
- delete mode 100644 autotests/reference/highlight.gdb.ref
- delete mode 100644 autotests/reference/highlight.gdbinit.ref
- delete mode 100644 autotests/reference/highlight.glsl.ref
- delete mode 100644 autotests/reference/highlight.hex.ref
- delete mode 100644 autotests/reference/highlight.hs.ref
- delete mode 100644 autotests/reference/highlight.java.ref
- delete mode 100644 autotests/reference/highlight.js.ref
- delete mode 100644 autotests/reference/highlight.jsp.ref
- delete mode 100644 autotests/reference/highlight.less.ref
- delete mode 100644 autotests/reference/highlight.lex.ref
- delete mode 100644 autotests/reference/highlight.lgt.ref
- delete mode 100644 autotests/reference/highlight.lhs.ref
- delete mode 100644 autotests/reference/highlight.lisp.ref
- delete mode 100644 autotests/reference/highlight.lua.ref
- delete mode 100644 autotests/reference/highlight.ly.ref
- delete mode 100644 autotests/reference/highlight.m.ref
- delete mode 100644 autotests/reference/highlight.mac.ref
- delete mode 100644 autotests/reference/highlight.mup.ref
- delete mode 100644 autotests/reference/highlight.pb.ref
- delete mode 100644 autotests/reference/highlight.php.ref
- delete mode 100644 autotests/reference/highlight.pike.ref
- delete mode 100644 autotests/reference/highlight.pl.ref
- delete mode 100644 autotests/reference/highlight.pony.ref
- delete mode 100644 autotests/reference/highlight.pov.ref
- delete mode 100644 autotests/reference/highlight.prg.ref
- delete mode 100644 autotests/reference/highlight.qml.ref
- delete mode 100644 autotests/reference/highlight.rb.ref
- delete mode 100644 autotests/reference/highlight.scad.ref
- delete mode 100644 autotests/reference/highlight.scheme.ref
- delete mode 100644 autotests/reference/highlight.scss.ref
- delete mode 100644 autotests/reference/highlight.sh.ref
- delete mode 100644 autotests/reference/highlight.spec.ref
- delete mode 100644 autotests/reference/highlight.stan.ref
- delete mode 100644 autotests/reference/highlight.t2t.ref
- delete mode 100644 autotests/reference/highlight.tcl.ref
- delete mode 100644 autotests/reference/highlight.tex.ref
- delete mode 100644 autotests/reference/highlight.tig.ref
- delete mode 100644 autotests/reference/highlight.wrl.ref
- delete mode 100644 autotests/reference/highlight.xml.ref
- delete mode 100644 autotests/reference/highlight.xsl.ref
- delete mode 100644 autotests/reference/highlight.y.ref
- delete mode 100644 autotests/reference/highlight.yang.ref
- delete mode 100644 autotests/reference/highlight_lpc.c.ref
- delete mode 100644 autotests/reference/highlight_ocaml.ml.ref
- delete mode 100644 autotests/reference/highlight_octave.m.ref
- delete mode 100644 autotests/reference/learnelixir.exs.ref
- delete mode 100644 autotests/reference/light52_muldiv.vhdl.ref
- delete mode 100644 autotests/reference/light52_tb.vhdl.ref
- delete mode 100644 autotests/reference/meson.build.ref
- delete mode 100644 autotests/reference/modelines.py.ref
- delete mode 100644 autotests/reference/or1200_dc_fsm.v.ref
- delete mode 100644 autotests/reference/or1200_du.v.ref
- delete mode 100644 autotests/reference/preprocessor-bug363280.c.ref
- delete mode 100644 autotests/reference/preprocessor-bug363280.cpp.ref
- delete mode 100644 autotests/reference/review128925-1.css.ref
- delete mode 100644 autotests/reference/review128925-1.scss.ref
- delete mode 100644 autotests/reference/review128925-2.css.ref
- delete mode 100644 autotests/reference/review128925-2.scss.ref
- delete mode 100644 autotests/reference/review128935.html.ref
- delete mode 100644 autotests/reference/test-iso.mod.ref
- delete mode 100644 autotests/reference/test-pim.mod.ref
- delete mode 100644 autotests/reference/test-r10.mod.ref
- delete mode 100644 autotests/reference/test.Rd.ref
- delete mode 100644 autotests/reference/test.bash.ref
- delete mode 100644 autotests/reference/test.bb.ref
- delete mode 100644 autotests/reference/test.c.ref
- delete mode 100644 autotests/reference/test.cil.ref
- delete mode 100644 autotests/reference/test.coffee.ref
- delete mode 100644 autotests/reference/test.css.ref
- delete mode 100644 autotests/reference/test.desktop.ref
- delete mode 100644 autotests/reference/test.diff.ref
- delete mode 100644 autotests/reference/test.eml.ref
- delete mode 100644 autotests/reference/test.fc.ref
- delete mode 100644 autotests/reference/test.frag.ref
- delete mode 100644 autotests/reference/test.htm.ref
- delete mode 100644 autotests/reference/test.ijs.ref
- delete mode 100644 autotests/reference/test.ini.ref
- delete mode 100644 autotests/reference/test.js.ref
- delete mode 100644 autotests/reference/test.json.ref
- delete mode 100644 autotests/reference/test.jsx.ref
- delete mode 100644 autotests/reference/test.logcat.ref
- delete mode 100644 autotests/reference/test.markdown.ref
- delete mode 100644 autotests/reference/test.mib.ref
- delete mode 100644 autotests/reference/test.mm.ref
- delete mode 100644 autotests/reference/test.mod.ref
- delete mode 100644 autotests/reference/test.mss.ref
- delete mode 100644 autotests/reference/test.py.ref
- delete mode 100644 autotests/reference/test.qdocconf.ref
- delete mode 100644 autotests/reference/test.qml.ref
- delete mode 100644 autotests/reference/test.rexx.ref
- delete mode 100644 autotests/reference/test.rs.ref
- delete mode 100644 autotests/reference/test.sieve.ref
- delete mode 100644 autotests/reference/test.sql.ref
- delete mode 100644 autotests/reference/test.sql_oracle.ref
- delete mode 100644 autotests/reference/test.te.ref
- delete mode 100644 autotests/reference/test.tex.ref
- delete mode 100644 autotests/reference/test.yaml.ref
- delete mode 100644 autotests/reference/test.zsh.ref
- delete mode 100644 autotests/reference/usr.bin.apparmor-profile-test.ref
- delete mode 100644 autotests/repository_benchmark.cpp
- delete mode 100644 autotests/syntaxrepository_test.cpp
- delete mode 100644 autotests/test-config.h.in
- delete mode 100644 autotests/testhighlighter.cpp
- delete mode 100644 autotests/theme_test.cpp
- delete mode 100755 autotests/update-reference-data.sh.in
- delete mode 100644 autotests/wildcardmatcher_test.cpp
-
-diff --git a/CMakeLists.txt b/CMakeLists.txt
-index b23d482..49923fb 100644
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
-@@ -90,9 +90,6 @@ add_subdirectory(data)
- add_subdirectory(src)
- if(TARGET Qt5::Gui)
- add_subdirectory(examples)
-- if (BUILD_TESTING)
-- add_subdirectory(autotests)
-- endif()
- endif()
-
- #
-diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
-deleted file mode 100644
-index 827a182..0000000
-diff --git a/autotests/folding/Dockerfile.fold b/autotests/folding/Dockerfile.fold
-deleted file mode 100644
-index 95a81d3..0000000
-diff --git a/autotests/folding/Doxyfile.example.fold b/autotests/folding/Doxyfile.example.fold
-deleted file mode 100644
-index ef39bfc..0000000
-diff --git a/autotests/folding/Kconfig.fold b/autotests/folding/Kconfig.fold
-deleted file mode 100644
-index 26df6be..0000000
-diff --git a/autotests/folding/Makefile.fold b/autotests/folding/Makefile.fold
-deleted file mode 100644
-index 1cea5dd..0000000
-diff --git a/autotests/folding/adblock.txt.fold b/autotests/folding/adblock.txt.fold
-deleted file mode 100644
-index 7ad85dd..0000000
-diff --git a/autotests/folding/apache.conf.fold b/autotests/folding/apache.conf.fold
-deleted file mode 100644
-index 8d8baa5..0000000
-diff --git a/autotests/folding/basic.markdown.fold b/autotests/folding/basic.markdown.fold
-deleted file mode 100644
-index f9a5bb3..0000000
-diff --git a/autotests/folding/basic.xml.fold b/autotests/folding/basic.xml.fold
-deleted file mode 100644
-index 1d6ab4b..0000000
-diff --git a/autotests/folding/build.gradle.fold b/autotests/folding/build.gradle.fold
-deleted file mode 100644
-index 2d0d25f..0000000
-diff --git a/autotests/folding/clojure.clj.fold b/autotests/folding/clojure.clj.fold
-deleted file mode 100644
-index b2f3768..0000000
-diff --git a/autotests/folding/complex.xml.fold b/autotests/folding/complex.xml.fold
-deleted file mode 100644
-index 0c79e82..0000000
-diff --git a/autotests/folding/craftenv.ps1.fold b/autotests/folding/craftenv.ps1.fold
-deleted file mode 100644
-index 9a8fbfc..0000000
-diff --git a/autotests/folding/csharp.cs.fold b/autotests/folding/csharp.cs.fold
-deleted file mode 100644
-index 906705c..0000000
-diff --git a/autotests/folding/cube.obj.fold b/autotests/folding/cube.obj.fold
-deleted file mode 100644
-index ef06c98..0000000
-diff --git a/autotests/folding/cube.ply.fold b/autotests/folding/cube.ply.fold
-deleted file mode 100644
-index 08d5f89..0000000
-diff --git a/autotests/folding/cube.stl.fold b/autotests/folding/cube.stl.fold
-deleted file mode 100644
-index ae74fd9..0000000
-diff --git a/autotests/folding/example.rmd.fold b/autotests/folding/example.rmd.fold
-deleted file mode 100644
-index c198d13..0000000
-diff --git a/autotests/folding/firstNonSpace.c.fold b/autotests/folding/firstNonSpace.c.fold
-deleted file mode 100644
-index 570ba90..0000000
-diff --git a/autotests/folding/folding.cpp.fold b/autotests/folding/folding.cpp.fold
-deleted file mode 100644
-index be50e40..0000000
-diff --git a/autotests/folding/git-rebase.fold b/autotests/folding/git-rebase.fold
-deleted file mode 100644
-index 43041bb..0000000
-diff --git a/autotests/folding/hello.exs.fold b/autotests/folding/hello.exs.fold
-deleted file mode 100644
-index cca72a1..0000000
-diff --git a/autotests/folding/highlight.ahdl.fold b/autotests/folding/highlight.ahdl.fold
-deleted file mode 100644
-index 26924b4..0000000
-diff --git a/autotests/folding/highlight.asm-avr.fold b/autotests/folding/highlight.asm-avr.fold
-deleted file mode 100644
-index 62e3ee9..0000000
-diff --git a/autotests/folding/highlight.asm-nasm.fold b/autotests/folding/highlight.asm-nasm.fold
-deleted file mode 100644
-index a5da4d1..0000000
-diff --git a/autotests/folding/highlight.asp.fold b/autotests/folding/highlight.asp.fold
-deleted file mode 100644
-index b938201..0000000
-diff --git a/autotests/folding/highlight.awk.fold b/autotests/folding/highlight.awk.fold
-deleted file mode 100644
-index 2ad62f1..0000000
-diff --git a/autotests/folding/highlight.bib.fold b/autotests/folding/highlight.bib.fold
-deleted file mode 100644
-index 9d521ee..0000000
-diff --git a/autotests/folding/highlight.bt.fold b/autotests/folding/highlight.bt.fold
-deleted file mode 100644
-index 51d2075..0000000
-diff --git a/autotests/folding/highlight.cmake.fold b/autotests/folding/highlight.cmake.fold
-deleted file mode 100644
-index 30688fa..0000000
-diff --git a/autotests/folding/highlight.cpp.fold b/autotests/folding/highlight.cpp.fold
-deleted file mode 100644
-index 5f80fa2..0000000
-diff --git a/autotests/folding/highlight.css.fold b/autotests/folding/highlight.css.fold
-deleted file mode 100644
-index ff27482..0000000
-diff --git a/autotests/folding/highlight.d.fold b/autotests/folding/highlight.d.fold
-deleted file mode 100644
-index 44bcb09..0000000
-diff --git a/autotests/folding/highlight.do.fold b/autotests/folding/highlight.do.fold
-deleted file mode 100644
-index 83ae062..0000000
-diff --git a/autotests/folding/highlight.dox.fold b/autotests/folding/highlight.dox.fold
-deleted file mode 100644
-index ad907ab..0000000
-diff --git a/autotests/folding/highlight.erl.fold b/autotests/folding/highlight.erl.fold
-deleted file mode 100644
-index 5b485cb..0000000
-diff --git a/autotests/folding/highlight.exu.fold b/autotests/folding/highlight.exu.fold
-deleted file mode 100644
-index 84f561f..0000000
-diff --git a/autotests/folding/highlight.f90.fold b/autotests/folding/highlight.f90.fold
-deleted file mode 100644
-index 2ea0f76..0000000
-diff --git a/autotests/folding/highlight.gdb.fold b/autotests/folding/highlight.gdb.fold
-deleted file mode 100644
-index 7246b73..0000000
-diff --git a/autotests/folding/highlight.gdbinit.fold b/autotests/folding/highlight.gdbinit.fold
-deleted file mode 100644
-index 70c9820..0000000
-diff --git a/autotests/folding/highlight.glsl.fold b/autotests/folding/highlight.glsl.fold
-deleted file mode 100644
-index 53df4f1..0000000
-diff --git a/autotests/folding/highlight.hex.fold b/autotests/folding/highlight.hex.fold
-deleted file mode 100644
-index 7c3f3ea..0000000
-diff --git a/autotests/folding/highlight.hs.fold b/autotests/folding/highlight.hs.fold
-deleted file mode 100644
-index 71ee81f..0000000
-diff --git a/autotests/folding/highlight.java.fold b/autotests/folding/highlight.java.fold
-deleted file mode 100644
-index e3b4cbc..0000000
-diff --git a/autotests/folding/highlight.js.fold b/autotests/folding/highlight.js.fold
-deleted file mode 100644
-index 122c0ac..0000000
-diff --git a/autotests/folding/highlight.jsp.fold b/autotests/folding/highlight.jsp.fold
-deleted file mode 100644
-index b938095..0000000
-diff --git a/autotests/folding/highlight.less.fold b/autotests/folding/highlight.less.fold
-deleted file mode 100644
-index b6e9075..0000000
-diff --git a/autotests/folding/highlight.lex.fold b/autotests/folding/highlight.lex.fold
-deleted file mode 100644
-index d2336c7..0000000
-diff --git a/autotests/folding/highlight.lgt.fold b/autotests/folding/highlight.lgt.fold
-deleted file mode 100644
-index c444473..0000000
-diff --git a/autotests/folding/highlight.lhs.fold b/autotests/folding/highlight.lhs.fold
-deleted file mode 100644
-index 9dc52e5..0000000
-diff --git a/autotests/folding/highlight.lisp.fold b/autotests/folding/highlight.lisp.fold
-deleted file mode 100644
-index 5b6bf55..0000000
-diff --git a/autotests/folding/highlight.lua.fold b/autotests/folding/highlight.lua.fold
-deleted file mode 100644
-index 183a258..0000000
-diff --git a/autotests/folding/highlight.ly.fold b/autotests/folding/highlight.ly.fold
-deleted file mode 100644
-index a7e787b..0000000
-diff --git a/autotests/folding/highlight.m.fold b/autotests/folding/highlight.m.fold
-deleted file mode 100644
-index 302659f..0000000
-diff --git a/autotests/folding/highlight.mac.fold b/autotests/folding/highlight.mac.fold
-deleted file mode 100644
-index ad7ee63..0000000
-diff --git a/autotests/folding/highlight.mup.fold b/autotests/folding/highlight.mup.fold
-deleted file mode 100644
-index e9afd19..0000000
-diff --git a/autotests/folding/highlight.pb.fold b/autotests/folding/highlight.pb.fold
-deleted file mode 100644
-index aad9803..0000000
-diff --git a/autotests/folding/highlight.php.fold b/autotests/folding/highlight.php.fold
-deleted file mode 100644
-index d0a5bc8..0000000
-diff --git a/autotests/folding/highlight.pike.fold b/autotests/folding/highlight.pike.fold
-deleted file mode 100644
-index 5c65bae..0000000
-diff --git a/autotests/folding/highlight.pl.fold b/autotests/folding/highlight.pl.fold
-deleted file mode 100644
-index 6845697..0000000
-diff --git a/autotests/folding/highlight.pony.fold b/autotests/folding/highlight.pony.fold
-deleted file mode 100644
-index d89d314..0000000
-diff --git a/autotests/folding/highlight.pov.fold b/autotests/folding/highlight.pov.fold
-deleted file mode 100644
-index 01e848c..0000000
-diff --git a/autotests/folding/highlight.prg.fold b/autotests/folding/highlight.prg.fold
-deleted file mode 100644
-index 9f03189..0000000
-diff --git a/autotests/folding/highlight.qml.fold b/autotests/folding/highlight.qml.fold
-deleted file mode 100644
-index 2494d70..0000000
-diff --git a/autotests/folding/highlight.rb.fold b/autotests/folding/highlight.rb.fold
-deleted file mode 100644
-index 8ae2980..0000000
-diff --git a/autotests/folding/highlight.scad.fold b/autotests/folding/highlight.scad.fold
-deleted file mode 100644
-index 68de2e0..0000000
-diff --git a/autotests/folding/highlight.scheme.fold b/autotests/folding/highlight.scheme.fold
-deleted file mode 100644
-index c42b22f..0000000
-diff --git a/autotests/folding/highlight.scss.fold b/autotests/folding/highlight.scss.fold
-deleted file mode 100644
-index afef111..0000000
-diff --git a/autotests/folding/highlight.sh.fold b/autotests/folding/highlight.sh.fold
-deleted file mode 100644
-index 9f81112..0000000
-diff --git a/autotests/folding/highlight.spec.fold b/autotests/folding/highlight.spec.fold
-deleted file mode 100644
-index 6ed00cf..0000000
-diff --git a/autotests/folding/highlight.stan.fold b/autotests/folding/highlight.stan.fold
-deleted file mode 100644
-index 38bfe01..0000000
-diff --git a/autotests/folding/highlight.t2t.fold b/autotests/folding/highlight.t2t.fold
-deleted file mode 100644
-index 428f204..0000000
-diff --git a/autotests/folding/highlight.tcl.fold b/autotests/folding/highlight.tcl.fold
-deleted file mode 100644
-index 7b6264c..0000000
-diff --git a/autotests/folding/highlight.tex.fold b/autotests/folding/highlight.tex.fold
-deleted file mode 100644
-index 772b210..0000000
-diff --git a/autotests/folding/highlight.tig.fold b/autotests/folding/highlight.tig.fold
-deleted file mode 100644
-index 8f3d99e..0000000
-diff --git a/autotests/folding/highlight.wrl.fold b/autotests/folding/highlight.wrl.fold
-deleted file mode 100644
-index c800b08..0000000
-diff --git a/autotests/folding/highlight.xml.fold b/autotests/folding/highlight.xml.fold
-deleted file mode 100644
-index b120aea..0000000
-diff --git a/autotests/folding/highlight.xsl.fold b/autotests/folding/highlight.xsl.fold
-deleted file mode 100644
-index feea15b..0000000
-diff --git a/autotests/folding/highlight.y.fold b/autotests/folding/highlight.y.fold
-deleted file mode 100644
-index 4f6242b..0000000
-diff --git a/autotests/folding/highlight.yang.fold b/autotests/folding/highlight.yang.fold
-deleted file mode 100644
-index 2de6183..0000000
-diff --git a/autotests/folding/highlight_lpc.c.fold b/autotests/folding/highlight_lpc.c.fold
-deleted file mode 100644
-index 350ce06..0000000
-diff --git a/autotests/folding/highlight_ocaml.ml.fold b/autotests/folding/highlight_ocaml.ml.fold
-deleted file mode 100644
-index 2403ed1..0000000
-diff --git a/autotests/folding/highlight_octave.m.fold b/autotests/folding/highlight_octave.m.fold
-deleted file mode 100644
-index ee3fdbd..0000000
-diff --git a/autotests/folding/learnelixir.exs.fold b/autotests/folding/learnelixir.exs.fold
-deleted file mode 100644
-index 1f51bd1..0000000
-diff --git a/autotests/folding/light52_muldiv.vhdl.fold b/autotests/folding/light52_muldiv.vhdl.fold
-deleted file mode 100644
-index c5d461e..0000000
-diff --git a/autotests/folding/light52_tb.vhdl.fold b/autotests/folding/light52_tb.vhdl.fold
-deleted file mode 100644
-index 17be507..0000000
-diff --git a/autotests/folding/meson.build.fold b/autotests/folding/meson.build.fold
-deleted file mode 100644
-index f20c35b..0000000
-diff --git a/autotests/folding/modelines.py.fold b/autotests/folding/modelines.py.fold
-deleted file mode 100644
-index 9c02779..0000000
-diff --git a/autotests/folding/or1200_dc_fsm.v.fold b/autotests/folding/or1200_dc_fsm.v.fold
-deleted file mode 100644
-index c54fbe7..0000000
-diff --git a/autotests/folding/or1200_du.v.fold b/autotests/folding/or1200_du.v.fold
-deleted file mode 100644
-index 97a6319..0000000
-diff --git a/autotests/folding/preprocessor-bug363280.c.fold b/autotests/folding/preprocessor-bug363280.c.fold
-deleted file mode 100644
-index bf559fb..0000000
-diff --git a/autotests/folding/preprocessor-bug363280.cpp.fold b/autotests/folding/preprocessor-bug363280.cpp.fold
-deleted file mode 100644
-index f9aabcc..0000000
-diff --git a/autotests/folding/review128925-1.css.fold b/autotests/folding/review128925-1.css.fold
-deleted file mode 100644
-index 05621d3..0000000
-diff --git a/autotests/folding/review128925-1.scss.fold b/autotests/folding/review128925-1.scss.fold
-deleted file mode 100644
-index 0b1f50b..0000000
-diff --git a/autotests/folding/review128925-2.css.fold b/autotests/folding/review128925-2.css.fold
-deleted file mode 100644
-index f10e2f0..0000000
-diff --git a/autotests/folding/review128925-2.scss.fold b/autotests/folding/review128925-2.scss.fold
-deleted file mode 100644
-index 056e331..0000000
-diff --git a/autotests/folding/review128935.html.fold b/autotests/folding/review128935.html.fold
-deleted file mode 100644
-index 9a5887a..0000000
-diff --git a/autotests/folding/test-iso.mod.fold b/autotests/folding/test-iso.mod.fold
-deleted file mode 100644
-index 57faabc..0000000
-diff --git a/autotests/folding/test-pim.mod.fold b/autotests/folding/test-pim.mod.fold
-deleted file mode 100644
-index dd152a7..0000000
-diff --git a/autotests/folding/test-r10.mod.fold b/autotests/folding/test-r10.mod.fold
-deleted file mode 100644
-index 7dc2e0d..0000000
-diff --git a/autotests/folding/test.Rd.fold b/autotests/folding/test.Rd.fold
-deleted file mode 100644
-index 77d07c4..0000000
-diff --git a/autotests/folding/test.bash.fold b/autotests/folding/test.bash.fold
-deleted file mode 100644
-index 216f11c..0000000
-diff --git a/autotests/folding/test.bb.fold b/autotests/folding/test.bb.fold
-deleted file mode 100644
-index 98d9089..0000000
-diff --git a/autotests/folding/test.c.fold b/autotests/folding/test.c.fold
-deleted file mode 100644
-index 7b43b2d..0000000
-diff --git a/autotests/folding/test.cil.fold b/autotests/folding/test.cil.fold
-deleted file mode 100644
-index 6ff2ff2..0000000
-diff --git a/autotests/folding/test.coffee.fold b/autotests/folding/test.coffee.fold
-deleted file mode 100644
-index de7ad09..0000000
-diff --git a/autotests/folding/test.css.fold b/autotests/folding/test.css.fold
-deleted file mode 100644
-index 0edd45b..0000000
-diff --git a/autotests/folding/test.desktop.fold b/autotests/folding/test.desktop.fold
-deleted file mode 100644
-index 77db817..0000000
-diff --git a/autotests/folding/test.diff.fold b/autotests/folding/test.diff.fold
-deleted file mode 100644
-index 95ef591..0000000
-diff --git a/autotests/folding/test.eml.fold b/autotests/folding/test.eml.fold
-deleted file mode 100644
-index 1e43110..0000000
-diff --git a/autotests/folding/test.fc.fold b/autotests/folding/test.fc.fold
-deleted file mode 100644
-index 3b90d3c..0000000
-diff --git a/autotests/folding/test.frag.fold b/autotests/folding/test.frag.fold
-deleted file mode 100644
-index a6a7bc1..0000000
-diff --git a/autotests/folding/test.htm.fold b/autotests/folding/test.htm.fold
-deleted file mode 100644
-index 23df023..0000000
-diff --git a/autotests/folding/test.ijs.fold b/autotests/folding/test.ijs.fold
-deleted file mode 100644
-index 7ff269b..0000000
-diff --git a/autotests/folding/test.ini.fold b/autotests/folding/test.ini.fold
-deleted file mode 100644
-index 27d80c8..0000000
-diff --git a/autotests/folding/test.js.fold b/autotests/folding/test.js.fold
-deleted file mode 100644
-index 90eecbf..0000000
-diff --git a/autotests/folding/test.json.fold b/autotests/folding/test.json.fold
-deleted file mode 100644
-index 622ad42..0000000
-diff --git a/autotests/folding/test.jsx.fold b/autotests/folding/test.jsx.fold
-deleted file mode 100644
-index bb0ffe8..0000000
-diff --git a/autotests/folding/test.logcat.fold b/autotests/folding/test.logcat.fold
-deleted file mode 100644
-index 80f0657..0000000
-diff --git a/autotests/folding/test.markdown.fold b/autotests/folding/test.markdown.fold
-deleted file mode 100644
-index 5078865..0000000
-diff --git a/autotests/folding/test.mib.fold b/autotests/folding/test.mib.fold
-deleted file mode 100644
-index aa273c2..0000000
-diff --git a/autotests/folding/test.mm.fold b/autotests/folding/test.mm.fold
-deleted file mode 100644
-index 6c8c39a..0000000
-diff --git a/autotests/folding/test.mod.fold b/autotests/folding/test.mod.fold
-deleted file mode 100644
-index 57faabc..0000000
-diff --git a/autotests/folding/test.mss.fold b/autotests/folding/test.mss.fold
-deleted file mode 100644
-index 49e0b97..0000000
-diff --git a/autotests/folding/test.py.fold b/autotests/folding/test.py.fold
-deleted file mode 100644
-index 8f9cdf4..0000000
-diff --git a/autotests/folding/test.qdocconf.fold b/autotests/folding/test.qdocconf.fold
-deleted file mode 100644
-index 0a382be..0000000
-diff --git a/autotests/folding/test.qml.fold b/autotests/folding/test.qml.fold
-deleted file mode 100644
-index 316ff65..0000000
-diff --git a/autotests/folding/test.rexx.fold b/autotests/folding/test.rexx.fold
-deleted file mode 100644
-index 378f8b7..0000000
-diff --git a/autotests/folding/test.rs.fold b/autotests/folding/test.rs.fold
-deleted file mode 100644
-index 7e43837..0000000
-diff --git a/autotests/folding/test.sieve.fold b/autotests/folding/test.sieve.fold
-deleted file mode 100644
-index c3344eb..0000000
-diff --git a/autotests/folding/test.sql.fold b/autotests/folding/test.sql.fold
-deleted file mode 100644
-index b76fae2..0000000
-diff --git a/autotests/folding/test.sql_oracle.fold b/autotests/folding/test.sql_oracle.fold
-deleted file mode 100644
-index e123cdf..0000000
-diff --git a/autotests/folding/test.te.fold b/autotests/folding/test.te.fold
-deleted file mode 100644
-index e266975..0000000
-diff --git a/autotests/folding/test.tex.fold b/autotests/folding/test.tex.fold
-deleted file mode 100644
-index 8da60c9..0000000
-diff --git a/autotests/folding/test.yaml.fold b/autotests/folding/test.yaml.fold
-deleted file mode 100644
-index e044140..0000000
-diff --git a/autotests/folding/test.zsh.fold b/autotests/folding/test.zsh.fold
-deleted file mode 100644
-index 924d798..0000000
-diff --git a/autotests/folding/usr.bin.apparmor-profile-test.fold b/autotests/folding/usr.bin.apparmor-profile-test.fold
-deleted file mode 100644
-index 64deede..0000000
-diff --git a/autotests/foldingtest.cpp b/autotests/foldingtest.cpp
-deleted file mode 100644
-index 5d10166..0000000
-diff --git a/autotests/highlighter_benchmark.cpp b/autotests/highlighter_benchmark.cpp
-deleted file mode 100644
-index 0b945ff..0000000
-diff --git a/autotests/html/Dockerfile.html b/autotests/html/Dockerfile.html
-deleted file mode 100644
-index ef324f6..0000000
-diff --git a/autotests/html/Doxyfile.example.html b/autotests/html/Doxyfile.example.html
-deleted file mode 100644
-index 0f2a6a5..0000000
-diff --git a/autotests/html/Kconfig.html b/autotests/html/Kconfig.html
-deleted file mode 100644
-index ab4ca14..0000000
-diff --git a/autotests/html/Makefile.html b/autotests/html/Makefile.html
-deleted file mode 100644
-index 801c14a..0000000
-diff --git a/autotests/html/adblock.txt.html b/autotests/html/adblock.txt.html
-deleted file mode 100644
-index 04dc5d1..0000000
-diff --git a/autotests/html/apache.conf.html b/autotests/html/apache.conf.html
-deleted file mode 100644
-index 50f254c..0000000
-diff --git a/autotests/html/basic.markdown.html b/autotests/html/basic.markdown.html
-deleted file mode 100644
-index 8ee1f19..0000000
-diff --git a/autotests/html/basic.xml.html b/autotests/html/basic.xml.html
-deleted file mode 100644
-index 770b081..0000000
-diff --git a/autotests/html/build.gradle.html b/autotests/html/build.gradle.html
-deleted file mode 100644
-index 5f0e056..0000000
-diff --git a/autotests/html/clojure.clj.html b/autotests/html/clojure.clj.html
-deleted file mode 100644
-index 548e3ea..0000000
-diff --git a/autotests/html/complex.xml.html b/autotests/html/complex.xml.html
-deleted file mode 100644
-index 117029d..0000000
-diff --git a/autotests/html/craftenv.ps1.html b/autotests/html/craftenv.ps1.html
-deleted file mode 100644
-index cfdf0e2..0000000
-diff --git a/autotests/html/csharp.cs.html b/autotests/html/csharp.cs.html
-deleted file mode 100644
-index fb4b469..0000000
-diff --git a/autotests/html/cube.obj.html b/autotests/html/cube.obj.html
-deleted file mode 100644
-index 6ecd1df..0000000
-diff --git a/autotests/html/cube.ply.html b/autotests/html/cube.ply.html
-deleted file mode 100644
-index 39d9102..0000000
-diff --git a/autotests/html/cube.stl.html b/autotests/html/cube.stl.html
-deleted file mode 100644
-index 0a23eab..0000000
-diff --git a/autotests/html/example.rmd.html b/autotests/html/example.rmd.html
-deleted file mode 100644
-index 9d7b639..0000000
-diff --git a/autotests/html/firstNonSpace.c.html b/autotests/html/firstNonSpace.c.html
-deleted file mode 100644
-index 55e02dd..0000000
-diff --git a/autotests/html/folding.cpp.html b/autotests/html/folding.cpp.html
-deleted file mode 100644
-index a920612..0000000
-diff --git a/autotests/html/git-rebase.html b/autotests/html/git-rebase.html
-deleted file mode 100644
-index 17d6da9..0000000
-diff --git a/autotests/html/hello.exs.html b/autotests/html/hello.exs.html
-deleted file mode 100644
-index 43d76a5..0000000
-diff --git a/autotests/html/highlight.ahdl.html b/autotests/html/highlight.ahdl.html
-deleted file mode 100644
-index b833298..0000000
-diff --git a/autotests/html/highlight.asm-avr.html b/autotests/html/highlight.asm-avr.html
-deleted file mode 100644
-index 867894e..0000000
-diff --git a/autotests/html/highlight.asm-nasm.html b/autotests/html/highlight.asm-nasm.html
-deleted file mode 100644
-index 6ae4028..0000000
-diff --git a/autotests/html/highlight.asp.html b/autotests/html/highlight.asp.html
-deleted file mode 100644
-index 85a1bba..0000000
-diff --git a/autotests/html/highlight.awk.html b/autotests/html/highlight.awk.html
-deleted file mode 100644
-index d92268a..0000000
-diff --git a/autotests/html/highlight.bib.html b/autotests/html/highlight.bib.html
-deleted file mode 100644
-index 48cc672..0000000
-diff --git a/autotests/html/highlight.bt.html b/autotests/html/highlight.bt.html
-deleted file mode 100644
-index dca943a..0000000
-diff --git a/autotests/html/highlight.cmake.html b/autotests/html/highlight.cmake.html
-deleted file mode 100644
-index a70c5ad..0000000
-diff --git a/autotests/html/highlight.cpp.html b/autotests/html/highlight.cpp.html
-deleted file mode 100644
-index 07be9db..0000000
-diff --git a/autotests/html/highlight.css.html b/autotests/html/highlight.css.html
-deleted file mode 100644
-index a3efe69..0000000
-diff --git a/autotests/html/highlight.d.html b/autotests/html/highlight.d.html
-deleted file mode 100644
-index 5e97d7b..0000000
-diff --git a/autotests/html/highlight.do.html b/autotests/html/highlight.do.html
-deleted file mode 100644
-index 40ac67d..0000000
-diff --git a/autotests/html/highlight.dox.html b/autotests/html/highlight.dox.html
-deleted file mode 100644
-index 990dc60..0000000
-diff --git a/autotests/html/highlight.erl.html b/autotests/html/highlight.erl.html
-deleted file mode 100644
-index 8424c84..0000000
-diff --git a/autotests/html/highlight.exu.html b/autotests/html/highlight.exu.html
-deleted file mode 100644
-index 5631898..0000000
-diff --git a/autotests/html/highlight.f90.html b/autotests/html/highlight.f90.html
-deleted file mode 100644
-index 897b853..0000000
-diff --git a/autotests/html/highlight.gdb.html b/autotests/html/highlight.gdb.html
-deleted file mode 100644
-index 5341b3c..0000000
-diff --git a/autotests/html/highlight.gdbinit.html b/autotests/html/highlight.gdbinit.html
-deleted file mode 100644
-index 84f3083..0000000
-diff --git a/autotests/html/highlight.glsl.html b/autotests/html/highlight.glsl.html
-deleted file mode 100644
-index bb22782..0000000
-diff --git a/autotests/html/highlight.hex.html b/autotests/html/highlight.hex.html
-deleted file mode 100644
-index b43e169..0000000
-diff --git a/autotests/html/highlight.hs.html b/autotests/html/highlight.hs.html
-deleted file mode 100644
-index b0ae90c..0000000
-diff --git a/autotests/html/highlight.java.html b/autotests/html/highlight.java.html
-deleted file mode 100644
-index e2b38e2..0000000
-diff --git a/autotests/html/highlight.js.html b/autotests/html/highlight.js.html
-deleted file mode 100644
-index 9c0f8d3..0000000
-diff --git a/autotests/html/highlight.jsp.html b/autotests/html/highlight.jsp.html
-deleted file mode 100644
-index 919297f..0000000
-diff --git a/autotests/html/highlight.less.html b/autotests/html/highlight.less.html
-deleted file mode 100644
-index 1491037..0000000
-diff --git a/autotests/html/highlight.lex.html b/autotests/html/highlight.lex.html
-deleted file mode 100644
-index dc1a884..0000000
-diff --git a/autotests/html/highlight.lgt.html b/autotests/html/highlight.lgt.html
-deleted file mode 100644
-index 5edceb3..0000000
-diff --git a/autotests/html/highlight.lhs.html b/autotests/html/highlight.lhs.html
-deleted file mode 100644
-index 086360e..0000000
-diff --git a/autotests/html/highlight.lisp.html b/autotests/html/highlight.lisp.html
-deleted file mode 100644
-index 6d59f6b..0000000
-diff --git a/autotests/html/highlight.lua.html b/autotests/html/highlight.lua.html
-deleted file mode 100644
-index 6ea0df4..0000000
-diff --git a/autotests/html/highlight.ly.html b/autotests/html/highlight.ly.html
-deleted file mode 100644
-index 3eafd4b..0000000
-diff --git a/autotests/html/highlight.m.html b/autotests/html/highlight.m.html
-deleted file mode 100644
-index 3296705..0000000
-diff --git a/autotests/html/highlight.mac.html b/autotests/html/highlight.mac.html
-deleted file mode 100644
-index 62a6068..0000000
-diff --git a/autotests/html/highlight.mup.html b/autotests/html/highlight.mup.html
-deleted file mode 100644
-index 48c6a3b..0000000
-diff --git a/autotests/html/highlight.pb.html b/autotests/html/highlight.pb.html
-deleted file mode 100644
-index 09a3ee8..0000000
-diff --git a/autotests/html/highlight.php.html b/autotests/html/highlight.php.html
-deleted file mode 100644
-index 08cf9ad..0000000
-diff --git a/autotests/html/highlight.pike.html b/autotests/html/highlight.pike.html
-deleted file mode 100644
-index 7f63985..0000000
-diff --git a/autotests/html/highlight.pl.html b/autotests/html/highlight.pl.html
-deleted file mode 100644
-index 4e31dda..0000000
-diff --git a/autotests/html/highlight.pony.html b/autotests/html/highlight.pony.html
-deleted file mode 100644
-index 97ae59d..0000000
-diff --git a/autotests/html/highlight.pov.html b/autotests/html/highlight.pov.html
-deleted file mode 100644
-index e566558..0000000
-diff --git a/autotests/html/highlight.prg.html b/autotests/html/highlight.prg.html
-deleted file mode 100644
-index cc6f18a..0000000
-diff --git a/autotests/html/highlight.qml.html b/autotests/html/highlight.qml.html
-deleted file mode 100644
-index e74d91c..0000000
-diff --git a/autotests/html/highlight.rb.html b/autotests/html/highlight.rb.html
-deleted file mode 100644
-index 8e2bb99..0000000
-diff --git a/autotests/html/highlight.scad.html b/autotests/html/highlight.scad.html
-deleted file mode 100644
-index 31a7b10..0000000
-diff --git a/autotests/html/highlight.scheme.html b/autotests/html/highlight.scheme.html
-deleted file mode 100644
-index a561179..0000000
-diff --git a/autotests/html/highlight.scss.html b/autotests/html/highlight.scss.html
-deleted file mode 100644
-index ec36e31..0000000
-diff --git a/autotests/html/highlight.sh.html b/autotests/html/highlight.sh.html
-deleted file mode 100644
-index b661a14..0000000
-diff --git a/autotests/html/highlight.spec.html b/autotests/html/highlight.spec.html
-deleted file mode 100644
-index 459655b..0000000
-diff --git a/autotests/html/highlight.stan.html b/autotests/html/highlight.stan.html
-deleted file mode 100644
-index 44f7274..0000000
-diff --git a/autotests/html/highlight.t2t.html b/autotests/html/highlight.t2t.html
-deleted file mode 100644
-index 044837e..0000000
-diff --git a/autotests/html/highlight.tcl.html b/autotests/html/highlight.tcl.html
-deleted file mode 100644
-index cda9499..0000000
-diff --git a/autotests/html/highlight.tex.html b/autotests/html/highlight.tex.html
-deleted file mode 100644
-index f277153..0000000
-diff --git a/autotests/html/highlight.tig.html b/autotests/html/highlight.tig.html
-deleted file mode 100644
-index ea1b29e..0000000
-diff --git a/autotests/html/highlight.wrl.html b/autotests/html/highlight.wrl.html
-deleted file mode 100644
-index 4ddc08f..0000000
-diff --git a/autotests/html/highlight.xml.html b/autotests/html/highlight.xml.html
-deleted file mode 100644
-index cccbe3b..0000000
-diff --git a/autotests/html/highlight.xsl.html b/autotests/html/highlight.xsl.html
-deleted file mode 100644
-index 44208fb..0000000
-diff --git a/autotests/html/highlight.y.html b/autotests/html/highlight.y.html
-deleted file mode 100644
-index a11c230..0000000
-diff --git a/autotests/html/highlight.yang.html b/autotests/html/highlight.yang.html
-deleted file mode 100644
-index 0be31a1..0000000
-diff --git a/autotests/html/highlight_lpc.c.html b/autotests/html/highlight_lpc.c.html
-deleted file mode 100644
-index 2ac3653..0000000
-diff --git a/autotests/html/highlight_ocaml.ml.html b/autotests/html/highlight_ocaml.ml.html
-deleted file mode 100644
-index 0e85e63..0000000
-diff --git a/autotests/html/highlight_octave.m.html b/autotests/html/highlight_octave.m.html
-deleted file mode 100644
-index 25d8cd8..0000000
-diff --git a/autotests/html/learnelixir.exs.html b/autotests/html/learnelixir.exs.html
-deleted file mode 100644
-index 9c0a390..0000000
-diff --git a/autotests/html/light52_muldiv.vhdl.html b/autotests/html/light52_muldiv.vhdl.html
-deleted file mode 100644
-index e5e27d3..0000000
-diff --git a/autotests/html/light52_tb.vhdl.html b/autotests/html/light52_tb.vhdl.html
-deleted file mode 100644
-index 8e73226..0000000
-diff --git a/autotests/html/meson.build.html b/autotests/html/meson.build.html
-deleted file mode 100644
-index 66e7942..0000000
-diff --git a/autotests/html/modelines.py.html b/autotests/html/modelines.py.html
-deleted file mode 100644
-index 0b3feff..0000000
-diff --git a/autotests/html/or1200_dc_fsm.v.html b/autotests/html/or1200_dc_fsm.v.html
-deleted file mode 100644
-index 7f26b75..0000000
-diff --git a/autotests/html/or1200_du.v.html b/autotests/html/or1200_du.v.html
-deleted file mode 100644
-index daf06ab..0000000
-diff --git a/autotests/html/preprocessor-bug363280.c.html b/autotests/html/preprocessor-bug363280.c.html
-deleted file mode 100644
-index e7b930b..0000000
-diff --git a/autotests/html/preprocessor-bug363280.cpp.html b/autotests/html/preprocessor-bug363280.cpp.html
-deleted file mode 100644
-index 1cdb7cc..0000000
-diff --git a/autotests/html/review128925-1.css.html b/autotests/html/review128925-1.css.html
-deleted file mode 100644
-index 192353d..0000000
-diff --git a/autotests/html/review128925-1.scss.html b/autotests/html/review128925-1.scss.html
-deleted file mode 100644
-index 79db0f9..0000000
-diff --git a/autotests/html/review128925-2.css.html b/autotests/html/review128925-2.css.html
-deleted file mode 100644
-index f519726..0000000
-diff --git a/autotests/html/review128925-2.scss.html b/autotests/html/review128925-2.scss.html
-deleted file mode 100644
-index 3c6d98e..0000000
-diff --git a/autotests/html/review128935.html.html b/autotests/html/review128935.html.html
-deleted file mode 100644
-index 4e59fc3..0000000
-diff --git a/autotests/html/test-iso.mod.html b/autotests/html/test-iso.mod.html
-deleted file mode 100644
-index e931d2c..0000000
-diff --git a/autotests/html/test-pim.mod.html b/autotests/html/test-pim.mod.html
-deleted file mode 100644
-index 38457df..0000000
-diff --git a/autotests/html/test-r10.mod.html b/autotests/html/test-r10.mod.html
-deleted file mode 100644
-index b35222f..0000000
-diff --git a/autotests/html/test.Rd.html b/autotests/html/test.Rd.html
-deleted file mode 100644
-index de292d6..0000000
-diff --git a/autotests/html/test.bash.html b/autotests/html/test.bash.html
-deleted file mode 100644
-index 161a975..0000000
-diff --git a/autotests/html/test.bb.html b/autotests/html/test.bb.html
-deleted file mode 100644
-index 9be6849..0000000
-diff --git a/autotests/html/test.c.html b/autotests/html/test.c.html
-deleted file mode 100644
-index 06d8a22..0000000
-diff --git a/autotests/html/test.cil.html b/autotests/html/test.cil.html
-deleted file mode 100644
-index ef2aa91..0000000
-diff --git a/autotests/html/test.coffee.html b/autotests/html/test.coffee.html
-deleted file mode 100644
-index 6fe3b4d..0000000
-diff --git a/autotests/html/test.css.html b/autotests/html/test.css.html
-deleted file mode 100644
-index b08cd84..0000000
-diff --git a/autotests/html/test.desktop.html b/autotests/html/test.desktop.html
-deleted file mode 100644
-index 66cdb27..0000000
-diff --git a/autotests/html/test.diff.html b/autotests/html/test.diff.html
-deleted file mode 100644
-index 8aa506d..0000000
-diff --git a/autotests/html/test.eml.html b/autotests/html/test.eml.html
-deleted file mode 100644
-index 7231c81..0000000
-diff --git a/autotests/html/test.fc.html b/autotests/html/test.fc.html
-deleted file mode 100644
-index 7302ed7..0000000
-diff --git a/autotests/html/test.frag.html b/autotests/html/test.frag.html
-deleted file mode 100644
-index 9ad3654..0000000
-diff --git a/autotests/html/test.htm.html b/autotests/html/test.htm.html
-deleted file mode 100644
-index 4c5eca5..0000000
-diff --git a/autotests/html/test.ijs.html b/autotests/html/test.ijs.html
-deleted file mode 100644
-index b9b91dd..0000000
-diff --git a/autotests/html/test.ini.html b/autotests/html/test.ini.html
-deleted file mode 100644
-index e834c8a..0000000
-diff --git a/autotests/html/test.js.html b/autotests/html/test.js.html
-deleted file mode 100644
-index fb47bde..0000000
-diff --git a/autotests/html/test.json.html b/autotests/html/test.json.html
-deleted file mode 100644
-index 1c3ec27..0000000
-diff --git a/autotests/html/test.jsx.html b/autotests/html/test.jsx.html
-deleted file mode 100644
-index 4af3ffa..0000000
-diff --git a/autotests/html/test.logcat.html b/autotests/html/test.logcat.html
-deleted file mode 100644
-index 2b2f6ee..0000000
-diff --git a/autotests/html/test.markdown.html b/autotests/html/test.markdown.html
-deleted file mode 100644
-index 2a6f8d7..0000000
-diff --git a/autotests/html/test.mib.html b/autotests/html/test.mib.html
-deleted file mode 100644
-index 30c2806..0000000
-diff --git a/autotests/html/test.mm.html b/autotests/html/test.mm.html
-deleted file mode 100644
-index 8ae2f11..0000000
-diff --git a/autotests/html/test.mod.html b/autotests/html/test.mod.html
-deleted file mode 100644
-index c03fea4..0000000
-diff --git a/autotests/html/test.mss.html b/autotests/html/test.mss.html
-deleted file mode 100644
-index 47b6292..0000000
-diff --git a/autotests/html/test.py.html b/autotests/html/test.py.html
-deleted file mode 100644
-index 0420ef4..0000000
-diff --git a/autotests/html/test.qdocconf.html b/autotests/html/test.qdocconf.html
-deleted file mode 100644
-index 3a000d5..0000000
-diff --git a/autotests/html/test.qml.html b/autotests/html/test.qml.html
-deleted file mode 100644
-index 39cc95d..0000000
-diff --git a/autotests/html/test.rexx.html b/autotests/html/test.rexx.html
-deleted file mode 100644
-index d528f24..0000000
-diff --git a/autotests/html/test.rs.html b/autotests/html/test.rs.html
-deleted file mode 100644
-index dd4da52..0000000
-diff --git a/autotests/html/test.sieve.html b/autotests/html/test.sieve.html
-deleted file mode 100644
-index f619d21..0000000
-diff --git a/autotests/html/test.sql.html b/autotests/html/test.sql.html
-deleted file mode 100644
-index 91e2ff4..0000000
-diff --git a/autotests/html/test.sql_oracle.html b/autotests/html/test.sql_oracle.html
-deleted file mode 100644
-index 72486e3..0000000
-diff --git a/autotests/html/test.te.html b/autotests/html/test.te.html
-deleted file mode 100644
-index 88dac5e..0000000
-diff --git a/autotests/html/test.tex.html b/autotests/html/test.tex.html
-deleted file mode 100644
-index e3ab47c..0000000
-diff --git a/autotests/html/test.yaml.html b/autotests/html/test.yaml.html
-deleted file mode 100644
-index 94c5d71..0000000
-diff --git a/autotests/html/test.zsh.html b/autotests/html/test.zsh.html
-deleted file mode 100644
-index 12683d7..0000000
-diff --git a/autotests/html/usr.bin.apparmor-profile-test.html b/autotests/html/usr.bin.apparmor-profile-test.html
-deleted file mode 100644
-index 4e2a486..0000000
-diff --git a/autotests/htmlhighlighter_test.cpp b/autotests/htmlhighlighter_test.cpp
-deleted file mode 100644
-index f7cc30a..0000000
-diff --git a/autotests/input/Dockerfile b/autotests/input/Dockerfile
-deleted file mode 100644
-index 95a81d3..0000000
-diff --git a/autotests/input/Doxyfile.example b/autotests/input/Doxyfile.example
-deleted file mode 100644
-index ef39bfc..0000000
-diff --git a/autotests/input/Kconfig b/autotests/input/Kconfig
-deleted file mode 100644
-index 5ebfaf4..0000000
-diff --git a/autotests/input/Makefile b/autotests/input/Makefile
-deleted file mode 100644
-index 05e4590..0000000
-diff --git a/autotests/input/adblock.txt b/autotests/input/adblock.txt
-deleted file mode 100644
-index 7ad85dd..0000000
-diff --git a/autotests/input/adblock.txt.syntax b/autotests/input/adblock.txt.syntax
-deleted file mode 100644
-index 0ce0446..0000000
-diff --git a/autotests/input/apache.conf b/autotests/input/apache.conf
-deleted file mode 100644
-index 2afa0be..0000000
-diff --git a/autotests/input/apache.conf.syntax b/autotests/input/apache.conf.syntax
-deleted file mode 100644
-index eab529d..0000000
-diff --git a/autotests/input/basic.markdown b/autotests/input/basic.markdown
-deleted file mode 100644
-index f9a5bb3..0000000
-diff --git a/autotests/input/basic.xml b/autotests/input/basic.xml
-deleted file mode 100644
-index 20feae1..0000000
-diff --git a/autotests/input/build.gradle b/autotests/input/build.gradle
-deleted file mode 100644
-index 3ad9096..0000000
-diff --git a/autotests/input/clojure.clj b/autotests/input/clojure.clj
-deleted file mode 100644
-index b2f3768..0000000
-diff --git a/autotests/input/complex.xml b/autotests/input/complex.xml
-deleted file mode 100644
-index d738998..0000000
-diff --git a/autotests/input/craftenv.ps1 b/autotests/input/craftenv.ps1
-deleted file mode 100644
-index a58dfee..0000000
-diff --git a/autotests/input/csharp.cs b/autotests/input/csharp.cs
-deleted file mode 100644
-index 8a91459..0000000
-diff --git a/autotests/input/cube.obj b/autotests/input/cube.obj
-deleted file mode 100644
-index ef06c98..0000000
-diff --git a/autotests/input/cube.ply b/autotests/input/cube.ply
-deleted file mode 100644
-index 08d5f89..0000000
-diff --git a/autotests/input/cube.stl b/autotests/input/cube.stl
-deleted file mode 100644
-index ae74fd9..0000000
-diff --git a/autotests/input/example.rmd b/autotests/input/example.rmd
-deleted file mode 100644
-index ef9a33e..0000000
-diff --git a/autotests/input/firstNonSpace.c b/autotests/input/firstNonSpace.c
-deleted file mode 100644
-index 570ba90..0000000
-diff --git a/autotests/input/folding.cpp b/autotests/input/folding.cpp
-deleted file mode 100644
-index 9e26b3f..0000000
-diff --git a/autotests/input/git-rebase b/autotests/input/git-rebase
-deleted file mode 100644
-index 43041bb..0000000
-diff --git a/autotests/input/git-rebase.syntax b/autotests/input/git-rebase.syntax
-deleted file mode 100644
-index abf0034..0000000
-diff --git a/autotests/input/hello.exs b/autotests/input/hello.exs
-deleted file mode 100644
-index cca72a1..0000000
-diff --git a/autotests/input/highlight.ahdl b/autotests/input/highlight.ahdl
-deleted file mode 100644
-index e059aa8..0000000
-diff --git a/autotests/input/highlight.asm-avr b/autotests/input/highlight.asm-avr
-deleted file mode 100644
-index f1e0542..0000000
-diff --git a/autotests/input/highlight.asm-nasm b/autotests/input/highlight.asm-nasm
-deleted file mode 100644
-index a5da4d1..0000000
-diff --git a/autotests/input/highlight.asm-nasm.syntax b/autotests/input/highlight.asm-nasm.syntax
-deleted file mode 100644
-index 67140d2..0000000
-diff --git a/autotests/input/highlight.asp b/autotests/input/highlight.asp
-deleted file mode 100644
-index 07a8619..0000000
-diff --git a/autotests/input/highlight.awk b/autotests/input/highlight.awk
-deleted file mode 100644
-index a80c86a..0000000
-diff --git a/autotests/input/highlight.bib b/autotests/input/highlight.bib
-deleted file mode 100644
-index 0ae404c..0000000
-diff --git a/autotests/input/highlight.bt b/autotests/input/highlight.bt
-deleted file mode 100644
-index 51d2075..0000000
-diff --git a/autotests/input/highlight.cmake b/autotests/input/highlight.cmake
-deleted file mode 100644
-index b8a1842..0000000
-diff --git a/autotests/input/highlight.cpp b/autotests/input/highlight.cpp
-deleted file mode 100644
-index f293064..0000000
-diff --git a/autotests/input/highlight.css b/autotests/input/highlight.css
-deleted file mode 100644
-index 7a5ea67..0000000
-diff --git a/autotests/input/highlight.d b/autotests/input/highlight.d
-deleted file mode 100644
-index b3daaf6..0000000
-diff --git a/autotests/input/highlight.do b/autotests/input/highlight.do
-deleted file mode 100644
-index 0b90b16..0000000
-diff --git a/autotests/input/highlight.dox b/autotests/input/highlight.dox
-deleted file mode 100644
-index 39b404f..0000000
-diff --git a/autotests/input/highlight.erl b/autotests/input/highlight.erl
-deleted file mode 100644
-index 5b485cb..0000000
-diff --git a/autotests/input/highlight.exu b/autotests/input/highlight.exu
-deleted file mode 100644
-index 3651adf..0000000
-diff --git a/autotests/input/highlight.f90 b/autotests/input/highlight.f90
-deleted file mode 100644
-index e2008c2..0000000
-diff --git a/autotests/input/highlight.gdb b/autotests/input/highlight.gdb
-deleted file mode 100644
-index a4b6459..0000000
-diff --git a/autotests/input/highlight.gdbinit b/autotests/input/highlight.gdbinit
-deleted file mode 100644
-index d33f9ea..0000000
-diff --git a/autotests/input/highlight.glsl b/autotests/input/highlight.glsl
-deleted file mode 100644
-index 3495757..0000000
-diff --git a/autotests/input/highlight.hex b/autotests/input/highlight.hex
-deleted file mode 100644
-index 7c3f3ea..0000000
-diff --git a/autotests/input/highlight.hs b/autotests/input/highlight.hs
-deleted file mode 100644
-index 8c0b145..0000000
-diff --git a/autotests/input/highlight.java b/autotests/input/highlight.java
-deleted file mode 100644
-index c807d64..0000000
-diff --git a/autotests/input/highlight.js b/autotests/input/highlight.js
-deleted file mode 100644
-index d0e7533..0000000
-diff --git a/autotests/input/highlight.jsp b/autotests/input/highlight.jsp
-deleted file mode 100644
-index d912836..0000000
-diff --git a/autotests/input/highlight.less b/autotests/input/highlight.less
-deleted file mode 100644
-index e178263..0000000
-diff --git a/autotests/input/highlight.lex b/autotests/input/highlight.lex
-deleted file mode 100644
-index 33527bd..0000000
-diff --git a/autotests/input/highlight.lgt b/autotests/input/highlight.lgt
-deleted file mode 100644
-index d39b45c..0000000
-diff --git a/autotests/input/highlight.lhs b/autotests/input/highlight.lhs
-deleted file mode 100644
-index 9dc52e5..0000000
-diff --git a/autotests/input/highlight.lisp b/autotests/input/highlight.lisp
-deleted file mode 100644
-index e86c85f..0000000
-diff --git a/autotests/input/highlight.lua b/autotests/input/highlight.lua
-deleted file mode 100644
-index 85ac95d..0000000
-diff --git a/autotests/input/highlight.ly b/autotests/input/highlight.ly
-deleted file mode 100644
-index 29aa7a6..0000000
-diff --git a/autotests/input/highlight.m b/autotests/input/highlight.m
-deleted file mode 100644
-index f240138..0000000
-diff --git a/autotests/input/highlight.m.syntax b/autotests/input/highlight.m.syntax
-deleted file mode 100644
-index d22d99a..0000000
-diff --git a/autotests/input/highlight.mac b/autotests/input/highlight.mac
-deleted file mode 100644
-index cb9da2d..0000000
-diff --git a/autotests/input/highlight.mup b/autotests/input/highlight.mup
-deleted file mode 100644
-index ed5a03d..0000000
-diff --git a/autotests/input/highlight.pb b/autotests/input/highlight.pb
-deleted file mode 100644
-index 5560233..0000000
-diff --git a/autotests/input/highlight.php b/autotests/input/highlight.php
-deleted file mode 100644
-index bb50951..0000000
-diff --git a/autotests/input/highlight.pike b/autotests/input/highlight.pike
-deleted file mode 100644
-index 2f63144..0000000
-diff --git a/autotests/input/highlight.pl b/autotests/input/highlight.pl
-deleted file mode 100644
-index 7d15d24..0000000
-diff --git a/autotests/input/highlight.pony b/autotests/input/highlight.pony
-deleted file mode 100644
-index aefa3b7..0000000
-diff --git a/autotests/input/highlight.pov b/autotests/input/highlight.pov
-deleted file mode 100644
-index 0bb3077..0000000
-diff --git a/autotests/input/highlight.prg b/autotests/input/highlight.prg
-deleted file mode 100644
-index 713c605..0000000
-diff --git a/autotests/input/highlight.prg.syntax b/autotests/input/highlight.prg.syntax
-deleted file mode 100644
-index 3355ff8..0000000
-diff --git a/autotests/input/highlight.qml b/autotests/input/highlight.qml
-deleted file mode 100644
-index b860512..0000000
-diff --git a/autotests/input/highlight.rb b/autotests/input/highlight.rb
-deleted file mode 100644
-index 2e52e5f..0000000
-diff --git a/autotests/input/highlight.scad b/autotests/input/highlight.scad
-deleted file mode 100644
-index 3ca4ec3..0000000
-diff --git a/autotests/input/highlight.scheme b/autotests/input/highlight.scheme
-deleted file mode 100644
-index 08c7351..0000000
-diff --git a/autotests/input/highlight.scss b/autotests/input/highlight.scss
-deleted file mode 100644
-index 11a4028..0000000
-diff --git a/autotests/input/highlight.sh b/autotests/input/highlight.sh
-deleted file mode 100644
-index a354757..0000000
-diff --git a/autotests/input/highlight.sh.syntax b/autotests/input/highlight.sh.syntax
-deleted file mode 100644
-index 5b85af9..0000000
-diff --git a/autotests/input/highlight.spec b/autotests/input/highlight.spec
-deleted file mode 100644
-index bc3b95b..0000000
-diff --git a/autotests/input/highlight.stan b/autotests/input/highlight.stan
-deleted file mode 100644
-index a71c231..0000000
-diff --git a/autotests/input/highlight.t2t b/autotests/input/highlight.t2t
-deleted file mode 100644
-index 5b5f379..0000000
-diff --git a/autotests/input/highlight.tcl b/autotests/input/highlight.tcl
-deleted file mode 100644
-index d6b7276..0000000
-diff --git a/autotests/input/highlight.tex b/autotests/input/highlight.tex
-deleted file mode 100644
-index 34c0890..0000000
-diff --git a/autotests/input/highlight.tig b/autotests/input/highlight.tig
-deleted file mode 100644
-index 893cbfe..0000000
-diff --git a/autotests/input/highlight.wrl b/autotests/input/highlight.wrl
-deleted file mode 100644
-index 2fb4b41..0000000
-diff --git a/autotests/input/highlight.xml b/autotests/input/highlight.xml
-deleted file mode 100644
-index 007449c..0000000
-diff --git a/autotests/input/highlight.xsl b/autotests/input/highlight.xsl
-deleted file mode 100644
-index f6e0efb..0000000
-diff --git a/autotests/input/highlight.y b/autotests/input/highlight.y
-deleted file mode 100644
-index df42a54..0000000
-diff --git a/autotests/input/highlight.yang b/autotests/input/highlight.yang
-deleted file mode 100644
-index bd95152..0000000
-diff --git a/autotests/input/highlight_lpc.c b/autotests/input/highlight_lpc.c
-deleted file mode 100644
-index fe5c629..0000000
-diff --git a/autotests/input/highlight_lpc.c.syntax b/autotests/input/highlight_lpc.c.syntax
-deleted file mode 100644
-index 2016d4f..0000000
-diff --git a/autotests/input/highlight_ocaml.ml b/autotests/input/highlight_ocaml.ml
-deleted file mode 100644
-index dc1717a..0000000
-diff --git a/autotests/input/highlight_octave.m b/autotests/input/highlight_octave.m
-deleted file mode 100644
-index 1f96036..0000000
-diff --git a/autotests/input/highlight_octave.m.syntax b/autotests/input/highlight_octave.m.syntax
-deleted file mode 100644
-index 65d755f..0000000
-diff --git a/autotests/input/learnelixir.exs b/autotests/input/learnelixir.exs
-deleted file mode 100644
-index 1f51bd1..0000000
-diff --git a/autotests/input/light52_muldiv.vhdl b/autotests/input/light52_muldiv.vhdl
-deleted file mode 100644
-index 723f154..0000000
-diff --git a/autotests/input/light52_tb.vhdl b/autotests/input/light52_tb.vhdl
-deleted file mode 100644
-index c110c5a..0000000
-diff --git a/autotests/input/meson.build b/autotests/input/meson.build
-deleted file mode 100644
-index d6dd6e6..0000000
-diff --git a/autotests/input/modelines.py b/autotests/input/modelines.py
-deleted file mode 100644
-index cd624ea..0000000
-diff --git a/autotests/input/or1200_dc_fsm.v b/autotests/input/or1200_dc_fsm.v
-deleted file mode 100644
-index e3d80ec..0000000
-diff --git a/autotests/input/or1200_du.v b/autotests/input/or1200_du.v
-deleted file mode 100644
-index dbd3614..0000000
-diff --git a/autotests/input/preprocessor-bug363280.c b/autotests/input/preprocessor-bug363280.c
-deleted file mode 100644
-index 9908bf7..0000000
-diff --git a/autotests/input/preprocessor-bug363280.cpp b/autotests/input/preprocessor-bug363280.cpp
-deleted file mode 100644
-index 9908bf7..0000000
-diff --git a/autotests/input/review128925-1.css b/autotests/input/review128925-1.css
-deleted file mode 100644
-index b61b616..0000000
-diff --git a/autotests/input/review128925-1.scss b/autotests/input/review128925-1.scss
-deleted file mode 100644
-index 6aa1da6..0000000
-diff --git a/autotests/input/review128925-2.css b/autotests/input/review128925-2.css
-deleted file mode 100644
-index 05c20a8..0000000
-diff --git a/autotests/input/review128925-2.scss b/autotests/input/review128925-2.scss
-deleted file mode 100644
-index c71fe3b..0000000
-diff --git a/autotests/input/review128935.html b/autotests/input/review128935.html
-deleted file mode 100644
-index f373235..0000000
-diff --git a/autotests/input/syntax/testlang.xml b/autotests/input/syntax/testlang.xml
-deleted file mode 100644
-index 0a1f9ba..0000000
-diff --git a/autotests/input/test-iso.mod b/autotests/input/test-iso.mod
-deleted file mode 100644
-index c30b91f..0000000
-diff --git a/autotests/input/test-iso.mod.syntax b/autotests/input/test-iso.mod.syntax
-deleted file mode 100644
-index 6179cd9..0000000
-diff --git a/autotests/input/test-pim.mod b/autotests/input/test-pim.mod
-deleted file mode 100644
-index be72a09..0000000
-diff --git a/autotests/input/test-pim.mod.syntax b/autotests/input/test-pim.mod.syntax
-deleted file mode 100644
-index 2580222..0000000
-diff --git a/autotests/input/test-r10.mod b/autotests/input/test-r10.mod
-deleted file mode 100644
-index 5c8b97c..0000000
-diff --git a/autotests/input/test-r10.mod.syntax b/autotests/input/test-r10.mod.syntax
-deleted file mode 100644
-index 55f4a6c..0000000
-diff --git a/autotests/input/test.Rd b/autotests/input/test.Rd
-deleted file mode 100644
-index 392343e..0000000
-diff --git a/autotests/input/test.bash b/autotests/input/test.bash
-deleted file mode 100644
-index 6241e5a..0000000
-diff --git a/autotests/input/test.bb b/autotests/input/test.bb
-deleted file mode 100644
-index 98d9089..0000000
-diff --git a/autotests/input/test.c b/autotests/input/test.c
-deleted file mode 100644
-index b2462d7..0000000
-diff --git a/autotests/input/test.c.syntax b/autotests/input/test.c.syntax
-deleted file mode 100644
-index 3cc58df..0000000
-diff --git a/autotests/input/test.cil b/autotests/input/test.cil
-deleted file mode 100644
-index 6ff2ff2..0000000
-diff --git a/autotests/input/test.coffee b/autotests/input/test.coffee
-deleted file mode 100644
-index 56f8a69..0000000
-diff --git a/autotests/input/test.css b/autotests/input/test.css
-deleted file mode 100644
-index 763e2d6..0000000
-diff --git a/autotests/input/test.css.syntax b/autotests/input/test.css.syntax
-deleted file mode 100644
-index 30599f0..0000000
-diff --git a/autotests/input/test.desktop b/autotests/input/test.desktop
-deleted file mode 100644
-index f3edd78..0000000
-diff --git a/autotests/input/test.diff b/autotests/input/test.diff
-deleted file mode 100644
-index 7a6807c..0000000
-diff --git a/autotests/input/test.eml b/autotests/input/test.eml
-deleted file mode 100644
-index 1e43110..0000000
-diff --git a/autotests/input/test.fc b/autotests/input/test.fc
-deleted file mode 100644
-index 76935a1..0000000
-diff --git a/autotests/input/test.frag b/autotests/input/test.frag
-deleted file mode 100644
-index 67ccc99..0000000
-diff --git a/autotests/input/test.htm b/autotests/input/test.htm
-deleted file mode 100644
-index 24ed4aa..0000000
-diff --git a/autotests/input/test.htm.syntax b/autotests/input/test.htm.syntax
-deleted file mode 100644
-index f05185c..0000000
-diff --git a/autotests/input/test.ijs b/autotests/input/test.ijs
-deleted file mode 100644
-index 9acfe4b..0000000
-diff --git a/autotests/input/test.ini b/autotests/input/test.ini
-deleted file mode 100644
-index d67d83d..0000000
-diff --git a/autotests/input/test.js b/autotests/input/test.js
-deleted file mode 100644
-index 478bd6e..0000000
-diff --git a/autotests/input/test.json b/autotests/input/test.json
-deleted file mode 100644
-index 6c16ee6..0000000
-diff --git a/autotests/input/test.jsx b/autotests/input/test.jsx
-deleted file mode 100644
-index 52b35c5..0000000
-diff --git a/autotests/input/test.logcat b/autotests/input/test.logcat
-deleted file mode 100644
-index 80f0657..0000000
-diff --git a/autotests/input/test.markdown b/autotests/input/test.markdown
-deleted file mode 100644
-index 74bcb61..0000000
-diff --git a/autotests/input/test.mib b/autotests/input/test.mib
-deleted file mode 100644
-index aa273c2..0000000
-diff --git a/autotests/input/test.mm b/autotests/input/test.mm
-deleted file mode 100644
-index 7f2677a..0000000
-diff --git a/autotests/input/test.mm.syntax b/autotests/input/test.mm.syntax
-deleted file mode 100644
-index b3ddb33..0000000
-diff --git a/autotests/input/test.mod b/autotests/input/test.mod
-deleted file mode 100644
-index c30b91f..0000000
-diff --git a/autotests/input/test.mss b/autotests/input/test.mss
-deleted file mode 100644
-index 4f742c1..0000000
-diff --git a/autotests/input/test.py b/autotests/input/test.py
-deleted file mode 100644
-index ac2ad0d..0000000
-diff --git a/autotests/input/test.qdocconf b/autotests/input/test.qdocconf
-deleted file mode 100644
-index 0a382be..0000000
-diff --git a/autotests/input/test.qml b/autotests/input/test.qml
-deleted file mode 100644
-index d8f7cb5..0000000
-diff --git a/autotests/input/test.rexx b/autotests/input/test.rexx
-deleted file mode 100644
-index 3e25125..0000000
-diff --git a/autotests/input/test.rs b/autotests/input/test.rs
-deleted file mode 100644
-index ec0d188..0000000
-diff --git a/autotests/input/test.sieve b/autotests/input/test.sieve
-deleted file mode 100644
-index 596ac45..0000000
-diff --git a/autotests/input/test.sql b/autotests/input/test.sql
-deleted file mode 100644
-index b76fae2..0000000
-diff --git a/autotests/input/test.sql.syntax b/autotests/input/test.sql.syntax
-deleted file mode 100644
-index b433cc9..0000000
-diff --git a/autotests/input/test.sql_oracle b/autotests/input/test.sql_oracle
-deleted file mode 100644
-index 80ade14..0000000
-diff --git a/autotests/input/test.sql_oracle.syntax b/autotests/input/test.sql_oracle.syntax
-deleted file mode 100644
-index 679c648..0000000
-diff --git a/autotests/input/test.te b/autotests/input/test.te
-deleted file mode 100644
-index 4a5ec9d..0000000
-diff --git a/autotests/input/test.tex b/autotests/input/test.tex
-deleted file mode 100644
-index 8da60c9..0000000
-diff --git a/autotests/input/test.yaml b/autotests/input/test.yaml
-deleted file mode 100644
-index cebc411..0000000
-diff --git a/autotests/input/test.zsh b/autotests/input/test.zsh
-deleted file mode 100644
-index 9da5db2..0000000
-diff --git a/autotests/input/themes/customtheme.theme b/autotests/input/themes/customtheme.theme
-deleted file mode 100644
-index 9ef0eea..0000000
-diff --git a/autotests/input/usr.bin.apparmor-profile-test b/autotests/input/usr.bin.apparmor-profile-test
-deleted file mode 100644
-index d112068..0000000
-diff --git a/autotests/reference/Dockerfile.ref b/autotests/reference/Dockerfile.ref
-deleted file mode 100644
-index 4d075e5..0000000
-diff --git a/autotests/reference/Doxyfile.example.ref b/autotests/reference/Doxyfile.example.ref
-deleted file mode 100644
-index 05100a4..0000000
-diff --git a/autotests/reference/Kconfig.ref b/autotests/reference/Kconfig.ref
-deleted file mode 100644
-index 0871457..0000000
-diff --git a/autotests/reference/Makefile.ref b/autotests/reference/Makefile.ref
-deleted file mode 100644
-index aa6a7c8..0000000
-diff --git a/autotests/reference/adblock.txt.ref b/autotests/reference/adblock.txt.ref
-deleted file mode 100644
-index 0b70891..0000000
-diff --git a/autotests/reference/apache.conf.ref b/autotests/reference/apache.conf.ref
-deleted file mode 100644
-index 9b48e0b..0000000
-diff --git a/autotests/reference/basic.markdown.ref b/autotests/reference/basic.markdown.ref
-deleted file mode 100644
-index 8f32c5d..0000000
-diff --git a/autotests/reference/basic.xml.ref b/autotests/reference/basic.xml.ref
-deleted file mode 100644
-index 9e61a57..0000000
-diff --git a/autotests/reference/build.gradle.ref b/autotests/reference/build.gradle.ref
-deleted file mode 100644
-index fc3d280..0000000
-diff --git a/autotests/reference/clojure.clj.ref b/autotests/reference/clojure.clj.ref
-deleted file mode 100644
-index 0c9ae6d..0000000
-diff --git a/autotests/reference/complex.xml.ref b/autotests/reference/complex.xml.ref
-deleted file mode 100644
-index 4ffac2b..0000000
-diff --git a/autotests/reference/craftenv.ps1.ref b/autotests/reference/craftenv.ps1.ref
-deleted file mode 100644
-index 7d7f496..0000000
-diff --git a/autotests/reference/csharp.cs.ref b/autotests/reference/csharp.cs.ref
-deleted file mode 100644
-index bd5f806..0000000
-diff --git a/autotests/reference/cube.obj.ref b/autotests/reference/cube.obj.ref
-deleted file mode 100644
-index ff41fa3..0000000
-diff --git a/autotests/reference/cube.ply.ref b/autotests/reference/cube.ply.ref
-deleted file mode 100644
-index 2ba0fc5..0000000
-diff --git a/autotests/reference/cube.stl.ref b/autotests/reference/cube.stl.ref
-deleted file mode 100644
-index d3e273d..0000000
-diff --git a/autotests/reference/example.rmd.ref b/autotests/reference/example.rmd.ref
-deleted file mode 100644
-index 56eedaa..0000000
-diff --git a/autotests/reference/firstNonSpace.c.ref b/autotests/reference/firstNonSpace.c.ref
-deleted file mode 100644
-index 6c14dd6..0000000
-diff --git a/autotests/reference/folding.cpp.ref b/autotests/reference/folding.cpp.ref
-deleted file mode 100644
-index 5fb1ec8..0000000
-diff --git a/autotests/reference/git-rebase.ref b/autotests/reference/git-rebase.ref
-deleted file mode 100644
-index a0a99ab..0000000
-diff --git a/autotests/reference/hello.exs.ref b/autotests/reference/hello.exs.ref
-deleted file mode 100644
-index f81ebbf..0000000
-diff --git a/autotests/reference/highlight.ahdl.ref b/autotests/reference/highlight.ahdl.ref
-deleted file mode 100644
-index ee149c2..0000000
-diff --git a/autotests/reference/highlight.asm-avr.ref b/autotests/reference/highlight.asm-avr.ref
-deleted file mode 100644
-index af6b627..0000000
-diff --git a/autotests/reference/highlight.asm-nasm.ref b/autotests/reference/highlight.asm-nasm.ref
-deleted file mode 100644
-index d674717..0000000
-diff --git a/autotests/reference/highlight.asp.ref b/autotests/reference/highlight.asp.ref
-deleted file mode 100644
-index b64fd4e..0000000
-diff --git a/autotests/reference/highlight.awk.ref b/autotests/reference/highlight.awk.ref
-deleted file mode 100644
-index 0560d4b..0000000
-diff --git a/autotests/reference/highlight.bib.ref b/autotests/reference/highlight.bib.ref
-deleted file mode 100644
-index d3f79c4..0000000
-diff --git a/autotests/reference/highlight.bt.ref b/autotests/reference/highlight.bt.ref
-deleted file mode 100644
-index b4559d7..0000000
-diff --git a/autotests/reference/highlight.cmake.ref b/autotests/reference/highlight.cmake.ref
-deleted file mode 100644
-index 2f7136f..0000000
-diff --git a/autotests/reference/highlight.cpp.ref b/autotests/reference/highlight.cpp.ref
-deleted file mode 100644
-index 295b447..0000000
-diff --git a/autotests/reference/highlight.css.ref b/autotests/reference/highlight.css.ref
-deleted file mode 100644
-index a262282..0000000
-diff --git a/autotests/reference/highlight.d.ref b/autotests/reference/highlight.d.ref
-deleted file mode 100644
-index 68962b9..0000000
-diff --git a/autotests/reference/highlight.do.ref b/autotests/reference/highlight.do.ref
-deleted file mode 100644
-index afc5bac..0000000
-diff --git a/autotests/reference/highlight.dox.ref b/autotests/reference/highlight.dox.ref
-deleted file mode 100644
-index 3e8346a..0000000
-diff --git a/autotests/reference/highlight.erl.ref b/autotests/reference/highlight.erl.ref
-deleted file mode 100644
-index 3a38fb7..0000000
-diff --git a/autotests/reference/highlight.exu.ref b/autotests/reference/highlight.exu.ref
-deleted file mode 100644
-index 5ec9969..0000000
-diff --git a/autotests/reference/highlight.f90.ref b/autotests/reference/highlight.f90.ref
-deleted file mode 100644
-index 810a1d6..0000000
-diff --git a/autotests/reference/highlight.gdb.ref b/autotests/reference/highlight.gdb.ref
-deleted file mode 100644
-index 3b0fdc0..0000000
-diff --git a/autotests/reference/highlight.gdbinit.ref b/autotests/reference/highlight.gdbinit.ref
-deleted file mode 100644
-index ef8dc48..0000000
-diff --git a/autotests/reference/highlight.glsl.ref b/autotests/reference/highlight.glsl.ref
-deleted file mode 100644
-index d7d0ef3..0000000
-diff --git a/autotests/reference/highlight.hex.ref b/autotests/reference/highlight.hex.ref
-deleted file mode 100644
-index ae09fd7..0000000
-diff --git a/autotests/reference/highlight.hs.ref b/autotests/reference/highlight.hs.ref
-deleted file mode 100644
-index e59f9d3..0000000
-diff --git a/autotests/reference/highlight.java.ref b/autotests/reference/highlight.java.ref
-deleted file mode 100644
-index ce941bd..0000000
-diff --git a/autotests/reference/highlight.js.ref b/autotests/reference/highlight.js.ref
-deleted file mode 100644
-index 7ddd8c9..0000000
-diff --git a/autotests/reference/highlight.jsp.ref b/autotests/reference/highlight.jsp.ref
-deleted file mode 100644
-index de73f18..0000000
-diff --git a/autotests/reference/highlight.less.ref b/autotests/reference/highlight.less.ref
-deleted file mode 100644
-index b5ac4ae..0000000
-diff --git a/autotests/reference/highlight.lex.ref b/autotests/reference/highlight.lex.ref
-deleted file mode 100644
-index 85912dc..0000000
-diff --git a/autotests/reference/highlight.lgt.ref b/autotests/reference/highlight.lgt.ref
-deleted file mode 100644
-index 354d3ad..0000000
-diff --git a/autotests/reference/highlight.lhs.ref b/autotests/reference/highlight.lhs.ref
-deleted file mode 100644
-index 838c687..0000000
-diff --git a/autotests/reference/highlight.lisp.ref b/autotests/reference/highlight.lisp.ref
-deleted file mode 100644
-index fe8cbf0..0000000
-diff --git a/autotests/reference/highlight.lua.ref b/autotests/reference/highlight.lua.ref
-deleted file mode 100644
-index 26e640c..0000000
-diff --git a/autotests/reference/highlight.ly.ref b/autotests/reference/highlight.ly.ref
-deleted file mode 100644
-index f11654d..0000000
-diff --git a/autotests/reference/highlight.m.ref b/autotests/reference/highlight.m.ref
-deleted file mode 100644
-index e75ca5e..0000000
-diff --git a/autotests/reference/highlight.mac.ref b/autotests/reference/highlight.mac.ref
-deleted file mode 100644
-index 79f336a..0000000
-diff --git a/autotests/reference/highlight.mup.ref b/autotests/reference/highlight.mup.ref
-deleted file mode 100644
-index 7ec1480..0000000
-diff --git a/autotests/reference/highlight.pb.ref b/autotests/reference/highlight.pb.ref
-deleted file mode 100644
-index d6adb56..0000000
-diff --git a/autotests/reference/highlight.php.ref b/autotests/reference/highlight.php.ref
-deleted file mode 100644
-index b1d8139..0000000
-diff --git a/autotests/reference/highlight.pike.ref b/autotests/reference/highlight.pike.ref
-deleted file mode 100644
-index f40f3d9..0000000
-diff --git a/autotests/reference/highlight.pl.ref b/autotests/reference/highlight.pl.ref
-deleted file mode 100644
-index e9cedd1..0000000
-diff --git a/autotests/reference/highlight.pony.ref b/autotests/reference/highlight.pony.ref
-deleted file mode 100644
-index 525f712..0000000
-diff --git a/autotests/reference/highlight.pov.ref b/autotests/reference/highlight.pov.ref
-deleted file mode 100644
-index 4a21aaf..0000000
-diff --git a/autotests/reference/highlight.prg.ref b/autotests/reference/highlight.prg.ref
-deleted file mode 100644
-index 2888283..0000000
-diff --git a/autotests/reference/highlight.qml.ref b/autotests/reference/highlight.qml.ref
-deleted file mode 100644
-index 3df0de1..0000000
-diff --git a/autotests/reference/highlight.rb.ref b/autotests/reference/highlight.rb.ref
-deleted file mode 100644
-index 139825f..0000000
-diff --git a/autotests/reference/highlight.scad.ref b/autotests/reference/highlight.scad.ref
-deleted file mode 100644
-index 93d31eb..0000000
-diff --git a/autotests/reference/highlight.scheme.ref b/autotests/reference/highlight.scheme.ref
-deleted file mode 100644
-index d6887a3..0000000
-diff --git a/autotests/reference/highlight.scss.ref b/autotests/reference/highlight.scss.ref
-deleted file mode 100644
-index 081743f..0000000
-diff --git a/autotests/reference/highlight.sh.ref b/autotests/reference/highlight.sh.ref
-deleted file mode 100644
-index fbfba05..0000000
-diff --git a/autotests/reference/highlight.spec.ref b/autotests/reference/highlight.spec.ref
-deleted file mode 100644
-index 20b6c11..0000000
-diff --git a/autotests/reference/highlight.stan.ref b/autotests/reference/highlight.stan.ref
-deleted file mode 100644
-index 581f8a2..0000000
-diff --git a/autotests/reference/highlight.t2t.ref b/autotests/reference/highlight.t2t.ref
-deleted file mode 100644
-index 87bff65..0000000
-diff --git a/autotests/reference/highlight.tcl.ref b/autotests/reference/highlight.tcl.ref
-deleted file mode 100644
-index de12b4c..0000000
-diff --git a/autotests/reference/highlight.tex.ref b/autotests/reference/highlight.tex.ref
-deleted file mode 100644
-index bcd6926..0000000
-diff --git a/autotests/reference/highlight.tig.ref b/autotests/reference/highlight.tig.ref
-deleted file mode 100644
-index 056b891..0000000
-diff --git a/autotests/reference/highlight.wrl.ref b/autotests/reference/highlight.wrl.ref
-deleted file mode 100644
-index 0f2b724..0000000
-diff --git a/autotests/reference/highlight.xml.ref b/autotests/reference/highlight.xml.ref
-deleted file mode 100644
-index dde97a6..0000000
-diff --git a/autotests/reference/highlight.xsl.ref b/autotests/reference/highlight.xsl.ref
-deleted file mode 100644
-index 86c1736..0000000
-diff --git a/autotests/reference/highlight.y.ref b/autotests/reference/highlight.y.ref
-deleted file mode 100644
-index b3defa7..0000000
-diff --git a/autotests/reference/highlight.yang.ref b/autotests/reference/highlight.yang.ref
-deleted file mode 100644
-index d4708a6..0000000
-diff --git a/autotests/reference/highlight_lpc.c.ref b/autotests/reference/highlight_lpc.c.ref
-deleted file mode 100644
-index 474d5f0..0000000
-diff --git a/autotests/reference/highlight_ocaml.ml.ref b/autotests/reference/highlight_ocaml.ml.ref
-deleted file mode 100644
-index d036f0e..0000000
-diff --git a/autotests/reference/highlight_octave.m.ref b/autotests/reference/highlight_octave.m.ref
-deleted file mode 100644
-index 4b93a73..0000000
-diff --git a/autotests/reference/learnelixir.exs.ref b/autotests/reference/learnelixir.exs.ref
-deleted file mode 100644
-index d877463..0000000
-diff --git a/autotests/reference/light52_muldiv.vhdl.ref b/autotests/reference/light52_muldiv.vhdl.ref
-deleted file mode 100644
-index 11338ac..0000000
-diff --git a/autotests/reference/light52_tb.vhdl.ref b/autotests/reference/light52_tb.vhdl.ref
-deleted file mode 100644
-index b7a799b..0000000
-diff --git a/autotests/reference/meson.build.ref b/autotests/reference/meson.build.ref
-deleted file mode 100644
-index 75efe1a..0000000
-diff --git a/autotests/reference/modelines.py.ref b/autotests/reference/modelines.py.ref
-deleted file mode 100644
-index 59e5bf2..0000000
-diff --git a/autotests/reference/or1200_dc_fsm.v.ref b/autotests/reference/or1200_dc_fsm.v.ref
-deleted file mode 100644
-index 10a80e5..0000000
-diff --git a/autotests/reference/or1200_du.v.ref b/autotests/reference/or1200_du.v.ref
-deleted file mode 100644
-index a1e838c..0000000
-diff --git a/autotests/reference/preprocessor-bug363280.c.ref b/autotests/reference/preprocessor-bug363280.c.ref
-deleted file mode 100644
-index 4c23041..0000000
-diff --git a/autotests/reference/preprocessor-bug363280.cpp.ref b/autotests/reference/preprocessor-bug363280.cpp.ref
-deleted file mode 100644
-index 8025f16..0000000
-diff --git a/autotests/reference/review128925-1.css.ref b/autotests/reference/review128925-1.css.ref
-deleted file mode 100644
-index 136dd6f..0000000
-diff --git a/autotests/reference/review128925-1.scss.ref b/autotests/reference/review128925-1.scss.ref
-deleted file mode 100644
-index fee32c7..0000000
-diff --git a/autotests/reference/review128925-2.css.ref b/autotests/reference/review128925-2.css.ref
-deleted file mode 100644
-index 0bef602..0000000
-diff --git a/autotests/reference/review128925-2.scss.ref b/autotests/reference/review128925-2.scss.ref
-deleted file mode 100644
-index 082c1e4..0000000
-diff --git a/autotests/reference/review128935.html.ref b/autotests/reference/review128935.html.ref
-deleted file mode 100644
-index 0936f9c..0000000
-diff --git a/autotests/reference/test-iso.mod.ref b/autotests/reference/test-iso.mod.ref
-deleted file mode 100644
-index 027dfac..0000000
-diff --git a/autotests/reference/test-pim.mod.ref b/autotests/reference/test-pim.mod.ref
-deleted file mode 100644
-index 4fac4d1..0000000
-diff --git a/autotests/reference/test-r10.mod.ref b/autotests/reference/test-r10.mod.ref
-deleted file mode 100644
-index bc61067..0000000
-diff --git a/autotests/reference/test.Rd.ref b/autotests/reference/test.Rd.ref
-deleted file mode 100644
-index ae77db2..0000000
-diff --git a/autotests/reference/test.bash.ref b/autotests/reference/test.bash.ref
-deleted file mode 100644
-index adeaa9d..0000000
-diff --git a/autotests/reference/test.bb.ref b/autotests/reference/test.bb.ref
-deleted file mode 100644
-index f0ea3b8..0000000
-diff --git a/autotests/reference/test.c.ref b/autotests/reference/test.c.ref
-deleted file mode 100644
-index 61c3ca8..0000000
-diff --git a/autotests/reference/test.cil.ref b/autotests/reference/test.cil.ref
-deleted file mode 100644
-index cbc0125..0000000
-diff --git a/autotests/reference/test.coffee.ref b/autotests/reference/test.coffee.ref
-deleted file mode 100644
-index e499f72..0000000
-diff --git a/autotests/reference/test.css.ref b/autotests/reference/test.css.ref
-deleted file mode 100644
-index 26de9f8..0000000
-diff --git a/autotests/reference/test.desktop.ref b/autotests/reference/test.desktop.ref
-deleted file mode 100644
-index 12ca73f..0000000
-diff --git a/autotests/reference/test.diff.ref b/autotests/reference/test.diff.ref
-deleted file mode 100644
-index b3d7bd4..0000000
-diff --git a/autotests/reference/test.eml.ref b/autotests/reference/test.eml.ref
-deleted file mode 100644
-index 25de38c..0000000
-diff --git a/autotests/reference/test.fc.ref b/autotests/reference/test.fc.ref
-deleted file mode 100644
-index dee4455..0000000
-diff --git a/autotests/reference/test.frag.ref b/autotests/reference/test.frag.ref
-deleted file mode 100644
-index 1426b6e..0000000
-diff --git a/autotests/reference/test.htm.ref b/autotests/reference/test.htm.ref
-deleted file mode 100644
-index c3d98ff..0000000
-diff --git a/autotests/reference/test.ijs.ref b/autotests/reference/test.ijs.ref
-deleted file mode 100644
-index 30c9733..0000000
-diff --git a/autotests/reference/test.ini.ref b/autotests/reference/test.ini.ref
-deleted file mode 100644
-index f2d5e79..0000000
-diff --git a/autotests/reference/test.js.ref b/autotests/reference/test.js.ref
-deleted file mode 100644
-index ca23322..0000000
-diff --git a/autotests/reference/test.json.ref b/autotests/reference/test.json.ref
-deleted file mode 100644
-index b7f0e52..0000000
-diff --git a/autotests/reference/test.jsx.ref b/autotests/reference/test.jsx.ref
-deleted file mode 100644
-index e9eda2f..0000000
-diff --git a/autotests/reference/test.logcat.ref b/autotests/reference/test.logcat.ref
-deleted file mode 100644
-index e4c1c80..0000000
-diff --git a/autotests/reference/test.markdown.ref b/autotests/reference/test.markdown.ref
-deleted file mode 100644
-index aa5f06c..0000000
-diff --git a/autotests/reference/test.mib.ref b/autotests/reference/test.mib.ref
-deleted file mode 100644
-index a83156b..0000000
-diff --git a/autotests/reference/test.mm.ref b/autotests/reference/test.mm.ref
-deleted file mode 100644
-index 4412d36..0000000
-diff --git a/autotests/reference/test.mod.ref b/autotests/reference/test.mod.ref
-deleted file mode 100644
-index 027dfac..0000000
-diff --git a/autotests/reference/test.mss.ref b/autotests/reference/test.mss.ref
-deleted file mode 100644
-index f3f6ff7..0000000
-diff --git a/autotests/reference/test.py.ref b/autotests/reference/test.py.ref
-deleted file mode 100644
-index a226875..0000000
-diff --git a/autotests/reference/test.qdocconf.ref b/autotests/reference/test.qdocconf.ref
-deleted file mode 100644
-index a5e1727..0000000
-diff --git a/autotests/reference/test.qml.ref b/autotests/reference/test.qml.ref
-deleted file mode 100644
-index 6a84875..0000000
-diff --git a/autotests/reference/test.rexx.ref b/autotests/reference/test.rexx.ref
-deleted file mode 100644
-index 6e69ce4..0000000
-diff --git a/autotests/reference/test.rs.ref b/autotests/reference/test.rs.ref
-deleted file mode 100644
-index 9a27445..0000000
-diff --git a/autotests/reference/test.sieve.ref b/autotests/reference/test.sieve.ref
-deleted file mode 100644
-index a030180..0000000
-diff --git a/autotests/reference/test.sql.ref b/autotests/reference/test.sql.ref
-deleted file mode 100644
-index ba869cd..0000000
-diff --git a/autotests/reference/test.sql_oracle.ref b/autotests/reference/test.sql_oracle.ref
-deleted file mode 100644
-index d121a71..0000000
-diff --git a/autotests/reference/test.te.ref b/autotests/reference/test.te.ref
-deleted file mode 100644
-index 5b3998d..0000000
-diff --git a/autotests/reference/test.tex.ref b/autotests/reference/test.tex.ref
-deleted file mode 100644
-index 3f24c2a..0000000
-diff --git a/autotests/reference/test.yaml.ref b/autotests/reference/test.yaml.ref
-deleted file mode 100644
-index eeeabc0..0000000
-diff --git a/autotests/reference/test.zsh.ref b/autotests/reference/test.zsh.ref
-deleted file mode 100644
-index f8da406..0000000
-diff --git a/autotests/reference/usr.bin.apparmor-profile-test.ref b/autotests/reference/usr.bin.apparmor-profile-test.ref
-deleted file mode 100644
-index c55bd5c..0000000
-diff --git a/autotests/repository_benchmark.cpp b/autotests/repository_benchmark.cpp
-deleted file mode 100644
-index 80bba64..0000000
-diff --git a/autotests/syntaxrepository_test.cpp b/autotests/syntaxrepository_test.cpp
-deleted file mode 100644
-index 3dc654d..0000000
-diff --git a/autotests/test-config.h.in b/autotests/test-config.h.in
-deleted file mode 100644
-index 9e574d7..0000000
-diff --git a/autotests/testhighlighter.cpp b/autotests/testhighlighter.cpp
-deleted file mode 100644
-index fad0300..0000000
-diff --git a/autotests/theme_test.cpp b/autotests/theme_test.cpp
-deleted file mode 100644
-index 961cdb9..0000000
-diff --git a/autotests/update-reference-data.sh.in b/autotests/update-reference-data.sh.in
-deleted file mode 100755
-index 1b5e45e..0000000
-diff --git a/autotests/wildcardmatcher_test.cpp b/autotests/wildcardmatcher_test.cpp
-deleted file mode 100644
-index 8a2208a..0000000
---
-2.20.1.windows.1
-
diff --git a/src/libs/3rdparty/syntax-highlighting/patches/0003-Add-qmake-Qbs-files-and-files-generated-by-CMake.patch b/src/libs/3rdparty/syntax-highlighting/patches/0003-Add-qmake-Qbs-files-and-files-generated-by-CMake.patch
deleted file mode 100644
index c35188fbec..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/patches/0003-Add-qmake-Qbs-files-and-files-generated-by-CMake.patch
+++ /dev/null
@@ -1,389 +0,0 @@
-From c9f4bdaa0902c0fdb5ea750d11935e15777a090d Mon Sep 17 00:00:00 2001
-From: Alessandro Portale <alessandro.portale@qt.io>
-Date: Tue, 12 Feb 2019 19:45:08 +0100
-Subject: [PATCH 3/6] Add qmake/Qbs files and files generated by CMake
-
----
- autogenerated/autogenerated.pri | 9 ++
- autogenerated/ksyntaxhighlighting_version.h | 12 +++
- autogenerated/src/lib/AbstractHighlighter | 1 +
- autogenerated/src/lib/Definition | 1 +
- autogenerated/src/lib/FoldingRegion | 1 +
- autogenerated/src/lib/Format | 1 +
- autogenerated/src/lib/Repository | 1 +
- autogenerated/src/lib/State | 1 +
- autogenerated/src/lib/SyntaxHighlighter | 1 +
- autogenerated/src/lib/Theme | 1 +
- .../src/lib/ksyntaxhighlighting_logging.cpp | 11 +++
- .../src/lib/ksyntaxhighlighting_logging.h | 11 +++
- data/data.pro | 11 +++
- src/lib/ksyntaxhighlighting_export.h | 34 +++++++
- syntax-highlighting.pro | 55 ++++++++++++
- syntax-highlighting.qbs | 88 +++++++++++++++++++
- syntax-highlighting_dependencies.pri | 3 +
- 17 files changed, 242 insertions(+)
- create mode 100644 autogenerated/autogenerated.pri
- create mode 100644 autogenerated/ksyntaxhighlighting_version.h
- create mode 100644 autogenerated/src/lib/AbstractHighlighter
- create mode 100644 autogenerated/src/lib/Definition
- create mode 100644 autogenerated/src/lib/FoldingRegion
- create mode 100644 autogenerated/src/lib/Format
- create mode 100644 autogenerated/src/lib/Repository
- create mode 100644 autogenerated/src/lib/State
- create mode 100644 autogenerated/src/lib/SyntaxHighlighter
- create mode 100644 autogenerated/src/lib/Theme
- create mode 100644 autogenerated/src/lib/ksyntaxhighlighting_logging.cpp
- create mode 100644 autogenerated/src/lib/ksyntaxhighlighting_logging.h
- create mode 100644 data/data.pro
- create mode 100644 src/lib/ksyntaxhighlighting_export.h
- create mode 100644 syntax-highlighting.pro
- create mode 100644 syntax-highlighting.qbs
- create mode 100644 syntax-highlighting_dependencies.pri
-
-diff --git a/autogenerated/autogenerated.pri b/autogenerated/autogenerated.pri
-new file mode 100644
-index 0000000..aee620a
---- /dev/null
-+++ b/autogenerated/autogenerated.pri
-@@ -0,0 +1,9 @@
-+INCLUDEPATH *= $$PWD/src/lib
-+INCLUDEPATH *= $$PWD
-+
-+SOURCES += \
-+ $$PWD/src/lib/ksyntaxhighlighting_logging.cpp
-+
-+HEADERS += \
-+ $$PWD/ksyntaxhighlighting_version.h \
-+ $$PWD/src/lib/ksyntaxhighlighting_logging.h
-diff --git a/autogenerated/ksyntaxhighlighting_version.h b/autogenerated/ksyntaxhighlighting_version.h
-new file mode 100644
-index 0000000..bd31a4d
---- /dev/null
-+++ b/autogenerated/ksyntaxhighlighting_version.h
-@@ -0,0 +1,12 @@
-+// This file was generated by ecm_setup_version(): DO NOT EDIT!
-+
-+#ifndef SyntaxHighlighting_VERSION_H
-+#define SyntaxHighlighting_VERSION_H
-+
-+#define SyntaxHighlighting_VERSION_STRING "5.52.0"
-+#define SyntaxHighlighting_VERSION_MAJOR 5
-+#define SyntaxHighlighting_VERSION_MINOR 52
-+#define SyntaxHighlighting_VERSION_PATCH 0
-+#define SyntaxHighlighting_VERSION ((5<<16)|(52<<8)|(0))
-+
-+#endif
-diff --git a/autogenerated/src/lib/AbstractHighlighter b/autogenerated/src/lib/AbstractHighlighter
-new file mode 100644
-index 0000000..b787873
---- /dev/null
-+++ b/autogenerated/src/lib/AbstractHighlighter
-@@ -0,0 +1 @@
-+#include "abstracthighlighter.h"
-diff --git a/autogenerated/src/lib/Definition b/autogenerated/src/lib/Definition
-new file mode 100644
-index 0000000..2c3241f
---- /dev/null
-+++ b/autogenerated/src/lib/Definition
-@@ -0,0 +1 @@
-+#include "definition.h"
-diff --git a/autogenerated/src/lib/FoldingRegion b/autogenerated/src/lib/FoldingRegion
-new file mode 100644
-index 0000000..005b829
---- /dev/null
-+++ b/autogenerated/src/lib/FoldingRegion
-@@ -0,0 +1 @@
-+#include "foldingregion.h"
-diff --git a/autogenerated/src/lib/Format b/autogenerated/src/lib/Format
-new file mode 100644
-index 0000000..b0d6a10
---- /dev/null
-+++ b/autogenerated/src/lib/Format
-@@ -0,0 +1 @@
-+#include "format.h"
-diff --git a/autogenerated/src/lib/Repository b/autogenerated/src/lib/Repository
-new file mode 100644
-index 0000000..189dbc2
---- /dev/null
-+++ b/autogenerated/src/lib/Repository
-@@ -0,0 +1 @@
-+#include "repository.h"
-diff --git a/autogenerated/src/lib/State b/autogenerated/src/lib/State
-new file mode 100644
-index 0000000..e148d70
---- /dev/null
-+++ b/autogenerated/src/lib/State
-@@ -0,0 +1 @@
-+#include "state.h"
-diff --git a/autogenerated/src/lib/SyntaxHighlighter b/autogenerated/src/lib/SyntaxHighlighter
-new file mode 100644
-index 0000000..b429824
---- /dev/null
-+++ b/autogenerated/src/lib/SyntaxHighlighter
-@@ -0,0 +1 @@
-+#include "syntaxhighlighter.h"
-diff --git a/autogenerated/src/lib/Theme b/autogenerated/src/lib/Theme
-new file mode 100644
-index 0000000..34a3e98
---- /dev/null
-+++ b/autogenerated/src/lib/Theme
-@@ -0,0 +1 @@
-+#include "theme.h"
-diff --git a/autogenerated/src/lib/ksyntaxhighlighting_logging.cpp b/autogenerated/src/lib/ksyntaxhighlighting_logging.cpp
-new file mode 100644
-index 0000000..4082ac4
---- /dev/null
-+++ b/autogenerated/src/lib/ksyntaxhighlighting_logging.cpp
-@@ -0,0 +1,11 @@
-+// This file was generated by ecm_qt_declare_logging_category(): DO NOT EDIT!
-+
-+#include "ksyntaxhighlighting_logging.h"
-+
-+ namespace KSyntaxHighlighting {
-+#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
-+Q_LOGGING_CATEGORY(Log, "org.kde.ksyntaxhighlighting", QtInfoMsg)
-+#else
-+Q_LOGGING_CATEGORY(Log, "org.kde.ksyntaxhighlighting")
-+#endif
-+}
-diff --git a/autogenerated/src/lib/ksyntaxhighlighting_logging.h b/autogenerated/src/lib/ksyntaxhighlighting_logging.h
-new file mode 100644
-index 0000000..c351b2c
---- /dev/null
-+++ b/autogenerated/src/lib/ksyntaxhighlighting_logging.h
-@@ -0,0 +1,11 @@
-+// This file was generated by ecm_qt_declare_logging_category(): DO NOT EDIT!
-+
-+#ifndef ECM_QLOGGINGCATEGORY_KSYNTAXHIGHLIGHTING_LOG_KSYNTAXHIGHLIGHTING_LOGGING_H
-+#define ECM_QLOGGINGCATEGORY_KSYNTAXHIGHLIGHTING_LOG_KSYNTAXHIGHLIGHTING_LOGGING_H
-+
-+#include <QLoggingCategory>
-+ namespace KSyntaxHighlighting {
-+Q_DECLARE_LOGGING_CATEGORY(Log)
-+}
-+
-+#endif
-diff --git a/data/data.pro b/data/data.pro
-new file mode 100644
-index 0000000..1028ea1
---- /dev/null
-+++ b/data/data.pro
-@@ -0,0 +1,11 @@
-+TEMPLATE = aux
-+
-+include(../../../../../qtcreator.pri)
-+
-+STATIC_BASE = $$PWD
-+STATIC_OUTPUT_BASE = $$IDE_DATA_PATH/generic-highlighter
-+STATIC_INSTALL_BASE = $$INSTALL_DATA_PATH/generic-highlighter
-+
-+STATIC_FILES += $$files($$PWD/syntax/*, true)
-+
-+include(../../../../../qtcreatordata.pri)
-diff --git a/src/lib/ksyntaxhighlighting_export.h b/src/lib/ksyntaxhighlighting_export.h
-new file mode 100644
-index 0000000..a39adb5
---- /dev/null
-+++ b/src/lib/ksyntaxhighlighting_export.h
-@@ -0,0 +1,34 @@
-+/****************************************************************************
-+**
-+** Copyright (C) 2018 The Qt Company Ltd.
-+** Contact: https://www.qt.io/licensing/
-+**
-+** This file is part of Qt Creator.
-+**
-+** Commercial License Usage
-+** Licensees holding valid commercial Qt licenses may use this file in
-+** accordance with the commercial license agreement provided with the
-+** Software or, alternatively, in accordance with the terms contained in
-+** a written agreement between you and The Qt Company. For licensing terms
-+** and conditions see https://www.qt.io/terms-conditions. For further
-+** information use the contact form at https://www.qt.io/contact-us.
-+**
-+** GNU General Public License Usage
-+** Alternatively, this file may be used under the terms of the GNU
-+** General Public License version 3 as published by the Free Software
-+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-+** included in the packaging of this file. Please review the following
-+** information to ensure the GNU General Public License requirements will
-+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-+**
-+****************************************************************************/
-+
-+#pragma once
-+
-+#include <QtGlobal>
-+
-+#if defined(KSYNTAXHIGHLIGHTING_LIBRARY)
-+# define KSYNTAXHIGHLIGHTING_EXPORT Q_DECL_EXPORT
-+#else
-+# define KSYNTAXHIGHLIGHTING_EXPORT Q_DECL_IMPORT
-+#endif
-diff --git a/syntax-highlighting.pro b/syntax-highlighting.pro
-new file mode 100644
-index 0000000..38127e1
---- /dev/null
-+++ b/syntax-highlighting.pro
-@@ -0,0 +1,55 @@
-+include(../../../qtcreatorlibrary.pri)
-+include(autogenerated/autogenerated.pri)
-+
-+QT += network
-+
-+DEFINES += KSYNTAXHIGHLIGHTING_LIBRARY
-+
-+RESOURCES += \
-+ data/themes/theme-data.qrc
-+
-+HEADERS += \
-+ src/lib/abstracthighlighter.h \
-+ src/lib/abstracthighlighter_p.h \
-+ src/lib/context_p.h \
-+ src/lib/contextswitch_p.h \
-+ src/lib/definition.h \
-+ src/lib/definition_p.h \
-+ src/lib/definitiondownloader.h \
-+ src/lib/definitionref_p.h \
-+ src/lib/foldingregion.h \
-+ src/lib/format.h \
-+ src/lib/format_p.h \
-+ src/lib/htmlhighlighter.h \
-+ src/lib/keywordlist_p.h \
-+ src/lib/ksyntaxhighlighting_export.h \
-+ src/lib/matchresult_p.h \
-+ src/lib/repository.h \
-+ src/lib/repository_p.h \
-+ src/lib/rule_p.h \
-+ src/lib/state.h \
-+ src/lib/state_p.h \
-+ src/lib/syntaxhighlighter.h \
-+ src/lib/textstyledata_p.h \
-+ src/lib/theme.h \
-+ src/lib/themedata_p.h \
-+ src/lib/wildcardmatcher_p.h \
-+ src/lib/xml_p.h \
-+
-+SOURCES += \
-+ src/lib/abstracthighlighter.cpp \
-+ src/lib/context.cpp \
-+ src/lib/contextswitch.cpp \
-+ src/lib/definition.cpp \
-+ src/lib/definitiondownloader.cpp \
-+ src/lib/foldingregion.cpp \
-+ src/lib/format.cpp \
-+ src/lib/htmlhighlighter.cpp \
-+ src/lib/keywordlist.cpp \
-+ src/lib/repository.cpp \
-+ src/lib/rule.cpp \
-+ src/lib/state.cpp \
-+ src/lib/syntaxhighlighter.cpp \
-+ src/lib/theme.cpp \
-+ src/lib/themedata.cpp \
-+ src/lib/wildcardmatcher.cpp \
-diff --git a/syntax-highlighting.qbs b/syntax-highlighting.qbs
-new file mode 100644
-index 0000000..248ebe0
---- /dev/null
-+++ b/syntax-highlighting.qbs
-@@ -0,0 +1,88 @@
-+import qbs 1.0
-+import qbs.File
-+import qbs.FileInfo
-+import qbs.Environment
-+
-+Project {
-+ QtcDevHeaders {
-+ productName: "syntax-highlighting (3rd party)"
-+ baseDir: sourceDirectory + "/src/lib"
-+ }
-+ QtcDevHeaders {
-+ productName: "syntax-highlighting autogenerated (3rd party)"
-+ baseDir: sourceDirectory + "/autogenerated/src/lib"
-+ Group {
-+ prefix: baseDir + '/'
-+ files: [
-+ "AbstractHighlighter",
-+ "Definition",
-+ "FoldingRegion",
-+ "Format",
-+ "Repository",
-+ "State",
-+ "SyntaxHighlighter",
-+ "Theme"
-+ ]
-+ qbs.install: true
-+ qbs.installDir: qtc.ide_include_path + '/' + FileInfo.fileName(product.sourceDirectory)
-+ qbs.installSourceBase: baseDir
-+ }
-+ }
-+
-+ QtcLibrary {
-+ name: "KSyntaxHighlighting"
-+
-+ cpp.defines: base.concat("KSYNTAXHIGHLIGHTING_LIBRARY")
-+ cpp.includePaths: [
-+ product.sourceDirectory + "/src/lib/",
-+ product.sourceDirectory + "/autogenerated/src/lib/",
-+ product.sourceDirectory + "/autogenerated/"
-+ ]
-+
-+ Depends { name: "Qt.gui" }
-+ Depends { name: "Qt.network" }
-+
-+ Group {
-+ name: "lib"
-+ prefix: "src/lib/"
-+ files: [
-+ "*.h",
-+ "*.cpp"
-+ ]
-+ }
-+
-+ Group {
-+ name: "KSyntaxHighlighting data"
-+ qbs.install: true
-+ qbs.installDir: qtc.ide_data_path + "/generic-highlighter/"
-+ qbs.installSourceBase: project.ide_source_tree + "/src/libs/3rdparty/syntax-highlighting/data/"
-+ prefix: project.ide_source_tree + "/src/libs/3rdparty/syntax-highlighting/data/"
-+ files: [
-+ "syntax/**/*"
-+ ]
-+ }
-+
-+ Group {
-+ name: "autogenerated lib"
-+ prefix: "autogenerated/src/lib/"
-+ files: [
-+ "*.h",
-+ "*.cpp"
-+ ]
-+ }
-+
-+ Group {
-+ name: "theme data"
-+ prefix: "data/themes/"
-+ files: [ "theme-data.qrc" ]
-+ }
-+
-+ Export {
-+ Depends { name: "cpp" }
-+ cpp.includePaths: [
-+ product.sourceDirectory + "/src/lib/",
-+ product.sourceDirectory + "/autogenerated/src/lib/",
-+ ]
-+ }
-+ }
-+}
-diff --git a/syntax-highlighting_dependencies.pri b/syntax-highlighting_dependencies.pri
-new file mode 100644
-index 0000000..f2fd3e6
---- /dev/null
-+++ b/syntax-highlighting_dependencies.pri
-@@ -0,0 +1,3 @@
-+QTC_LIB_NAME = KSyntaxHighlighting
-+INCLUDEPATH *= $$PWD/src/lib
-+INCLUDEPATH *= $$PWD/autogenerated/src/lib
---
-2.20.1.windows.1
-
diff --git a/src/libs/3rdparty/syntax-highlighting/patches/0004-Compile-with-namespaced-Qt.patch b/src/libs/3rdparty/syntax-highlighting/patches/0004-Compile-with-namespaced-Qt.patch
deleted file mode 100644
index 1c37d3158e..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/patches/0004-Compile-with-namespaced-Qt.patch
+++ /dev/null
@@ -1,281 +0,0 @@
-From 916af0204a45c91092e53241e6a867cadb9dedec Mon Sep 17 00:00:00 2001
-From: Alessandro Portale <alessandro.portale@qt.io>
-Date: Tue, 12 Feb 2019 19:21:57 +0100
-Subject: [PATCH 4/6] Compile with namespaced Qt
-
----
- src/lib/abstracthighlighter.h | 4 ++++
- src/lib/abstracthighlighter_p.h | 2 ++
- src/lib/context_p.h | 2 ++
- src/lib/definition.h | 4 ++++
- src/lib/foldingregion.h | 2 ++
- src/lib/format.h | 5 ++++-
- src/lib/htmlhighlighter.h | 2 ++
- src/lib/keywordlist_p.h | 2 ++
- src/lib/repository.h | 2 ++
- src/lib/repository_p.h | 2 ++
- src/lib/rule_p.h | 2 ++
- src/lib/state.h | 2 ++
- src/lib/theme.h | 2 ++
- src/lib/themedata_p.h | 2 ++
- src/lib/wildcardmatcher.cpp | 2 +-
- src/lib/wildcardmatcher_p.h | 4 ++++
- 16 files changed, 39 insertions(+), 2 deletions(-)
-
-diff --git a/src/lib/abstracthighlighter.h b/src/lib/abstracthighlighter.h
-index 056f65b..2b88729 100644
---- a/src/lib/abstracthighlighter.h
-+++ b/src/lib/abstracthighlighter.h
-@@ -30,7 +30,9 @@
-
- #include <memory>
-
-+QT_BEGIN_NAMESPACE
- class QString;
-+QT_END_NAMESPACE
-
- namespace KSyntaxHighlighting {
-
-@@ -186,6 +188,8 @@ private:
- };
- }
-
-+QT_BEGIN_NAMESPACE
- Q_DECLARE_INTERFACE(KSyntaxHighlighting::AbstractHighlighter, "org.kde.SyntaxHighlighting.AbstractHighlighter")
-+QT_END_NAMESPACE
-
- #endif // KSYNTAXHIGHLIGHTING_ABSTRACTHIGHLIGHTERM_H
-diff --git a/src/lib/abstracthighlighter_p.h b/src/lib/abstracthighlighter_p.h
-index 1690eb0..bdfdf23 100644
---- a/src/lib/abstracthighlighter_p.h
-+++ b/src/lib/abstracthighlighter_p.h
-@@ -27,7 +27,9 @@
- #include "definition.h"
- #include "theme.h"
-
-+QT_BEGIN_NAMESPACE
- class QStringList;
-+QT_END_NAMESPACE
-
- namespace KSyntaxHighlighting {
-
-diff --git a/src/lib/context_p.h b/src/lib/context_p.h
-index 3e3ab97..a034d0e 100644
---- a/src/lib/context_p.h
-+++ b/src/lib/context_p.h
-@@ -34,7 +34,9 @@
-
- #include <vector>
-
-+QT_BEGIN_NAMESPACE
- class QXmlStreamReader;
-+QT_END_NAMESPACE
-
- namespace KSyntaxHighlighting {
-
-diff --git a/src/lib/definition.h b/src/lib/definition.h
-index 25996dd..6f0dba9 100644
---- a/src/lib/definition.h
-+++ b/src/lib/definition.h
-@@ -31,10 +31,12 @@
-
- #include <memory>
-
-+QT_BEGIN_NAMESPACE
- class QChar;
- class QString;
- class QStringList;
- template <typename T> class QVector;
-+QT_END_NAMESPACE
-
- namespace KSyntaxHighlighting {
-
-@@ -391,6 +393,8 @@ private:
-
- }
-
-+QT_BEGIN_NAMESPACE
- Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Definition, Q_MOVABLE_TYPE);
-+QT_END_NAMESPACE
-
- #endif
-diff --git a/src/lib/foldingregion.h b/src/lib/foldingregion.h
-index baf65d1..074b947 100644
---- a/src/lib/foldingregion.h
-+++ b/src/lib/foldingregion.h
-@@ -101,6 +101,8 @@ private:
-
- }
-
-+QT_BEGIN_NAMESPACE
- Q_DECLARE_TYPEINFO(KSyntaxHighlighting::FoldingRegion, Q_PRIMITIVE_TYPE);
-+QT_END_NAMESPACE
-
- #endif
-diff --git a/src/lib/format.h b/src/lib/format.h
-index ba85f0d..24c58e7 100644
---- a/src/lib/format.h
-+++ b/src/lib/format.h
-@@ -30,10 +30,11 @@
- #include <QExplicitlySharedDataPointer>
- #include <QTypeInfo>
-
--
-+QT_BEGIN_NAMESPACE
- class QColor;
- class QString;
- class QXmlStreamReader;
-+QT_END_NAMESPACE
-
- namespace KSyntaxHighlighting {
-
-@@ -144,6 +145,8 @@ private:
- };
- }
-
-+QT_BEGIN_NAMESPACE
- Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Format, Q_MOVABLE_TYPE);
-+QT_END_NAMESPACE
-
- #endif // KSYNTAXHIGHLIGHTING_FORMAT_H
-diff --git a/src/lib/htmlhighlighter.h b/src/lib/htmlhighlighter.h
-index 22d33ad..b7eda02 100644
---- a/src/lib/htmlhighlighter.h
-+++ b/src/lib/htmlhighlighter.h
-@@ -32,8 +32,10 @@
-
- #include <memory>
-
-+QT_BEGIN_NAMESPACE
- class QFile;
- class QTextStream;
-+QT_END_NAMESPACE
-
- namespace KSyntaxHighlighting {
-
-diff --git a/src/lib/keywordlist_p.h b/src/lib/keywordlist_p.h
-index fa70f11..8c41aab 100644
---- a/src/lib/keywordlist_p.h
-+++ b/src/lib/keywordlist_p.h
-@@ -30,7 +30,9 @@
-
- #include <vector>
-
-+QT_BEGIN_NAMESPACE
- class QXmlStreamReader;
-+QT_END_NAMESPACE
-
- namespace KSyntaxHighlighting {
-
-diff --git a/src/lib/repository.h b/src/lib/repository.h
-index 2c32d02..c35da5e 100644
---- a/src/lib/repository.h
-+++ b/src/lib/repository.h
-@@ -29,8 +29,10 @@
- #include <qglobal.h>
- #include <memory>
-
-+QT_BEGIN_NAMESPACE
- class QString;
- template <typename T> class QVector;
-+QT_END_NAMESPACE
-
- /**
- * @namespace KSyntaxHighlighting
-diff --git a/src/lib/repository_p.h b/src/lib/repository_p.h
-index da765a2..9db876b 100644
---- a/src/lib/repository_p.h
-+++ b/src/lib/repository_p.h
-@@ -27,7 +27,9 @@
- #include <QHash>
- #include <QVector>
-
-+QT_BEGIN_NAMESPACE
- class QString;
-+QT_END_NAMESPACE
-
- namespace KSyntaxHighlighting {
-
-diff --git a/src/lib/rule_p.h b/src/lib/rule_p.h
-index eccf0df..538fded 100644
---- a/src/lib/rule_p.h
-+++ b/src/lib/rule_p.h
-@@ -38,7 +38,9 @@
-
- #include <memory>
-
-+QT_BEGIN_NAMESPACE
- class QXmlStreamReader;
-+QT_END_NAMESPACE
-
- namespace KSyntaxHighlighting {
-
-diff --git a/src/lib/state.h b/src/lib/state.h
-index c6e5344..fce4bc7 100644
---- a/src/lib/state.h
-+++ b/src/lib/state.h
-@@ -79,6 +79,8 @@ private:
-
- }
-
-+QT_BEGIN_NAMESPACE
- Q_DECLARE_TYPEINFO(KSyntaxHighlighting::State, Q_MOVABLE_TYPE);
-+QT_END_NAMESPACE
-
- #endif // KSYNTAXHIGHLIGHTING_STATE_H
-diff --git a/src/lib/theme.h b/src/lib/theme.h
-index 5277429..adb8431 100644
---- a/src/lib/theme.h
-+++ b/src/lib/theme.h
-@@ -371,6 +371,8 @@ private:
-
- }
-
-+QT_BEGIN_NAMESPACE
- Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Theme, Q_MOVABLE_TYPE);
-+QT_END_NAMESPACE
-
- #endif // KSYNTAXHIGHLIGHTING_THEME_H
-diff --git a/src/lib/themedata_p.h b/src/lib/themedata_p.h
-index 68cb873..3b5f463 100644
---- a/src/lib/themedata_p.h
-+++ b/src/lib/themedata_p.h
-@@ -163,6 +163,8 @@ private:
-
- }
-
-+QT_BEGIN_NAMESPACE
- Q_DECLARE_TYPEINFO(KSyntaxHighlighting::TextStyleData, Q_MOVABLE_TYPE);
-+QT_END_NAMESPACE
-
- #endif // KSYNTAXHIGHLIGHTING_THEMEDATA_P_H
-diff --git a/src/lib/wildcardmatcher.cpp b/src/lib/wildcardmatcher.cpp
-index 963e78d..167295a 100644
---- a/src/lib/wildcardmatcher.cpp
-+++ b/src/lib/wildcardmatcher.cpp
-@@ -79,5 +79,5 @@ static bool exactMatch(const QString &candidate, const QString &wildcard, int ca
- bool WildcardMatcher::exactMatch(const QString &candidate, const QString &wildcard,
- bool caseSensitive)
- {
-- return exactMatch(candidate, wildcard, candidate.length() - 1, wildcard.length() - 1, caseSensitive);
-+ return ::exactMatch(candidate, wildcard, candidate.length() - 1, wildcard.length() - 1, caseSensitive);
- }
-diff --git a/src/lib/wildcardmatcher_p.h b/src/lib/wildcardmatcher_p.h
-index 823dbd5..016b10f 100644
---- a/src/lib/wildcardmatcher_p.h
-+++ b/src/lib/wildcardmatcher_p.h
-@@ -24,7 +24,11 @@
- #ifndef KSYNTAXHIGHLIGHTING_WILDCARDMATCHER_P_H
- #define KSYNTAXHIGHLIGHTING_WILDCARDMATCHER_P_H
-
-+#include <QtGlobal>
-+
-+QT_BEGIN_NAMESPACE
- class QString;
-+QT_END_NAMESPACE
-
- namespace KSyntaxHighlighting {
-
---
-2.20.1.windows.1
-
diff --git a/src/libs/3rdparty/syntax-highlighting/patches/0005-Prevent-assertion-in-RegExpr-doLoad.patch b/src/libs/3rdparty/syntax-highlighting/patches/0005-Prevent-assertion-in-RegExpr-doLoad.patch
deleted file mode 100644
index 7941c718df..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/patches/0005-Prevent-assertion-in-RegExpr-doLoad.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From 3c05e92c71f6675fa2071036dafbc8d337bd2d01 Mon Sep 17 00:00:00 2001
-From: Alessandro Portale <alessandro.portale@qt.io>
-Date: Tue, 12 Feb 2019 19:27:46 +0100
-Subject: [PATCH 5/6] Prevent assertion in RegExpr::doLoad
-
----
- src/lib/rule.cpp | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
-diff --git a/src/lib/rule.cpp b/src/lib/rule.cpp
-index a201375..c48753b 100644
---- a/src/lib/rule.cpp
-+++ b/src/lib/rule.cpp
-@@ -575,7 +575,11 @@ bool RegExpr::doLoad(QXmlStreamReader& reader)
- }
-
- // always using m_regexp.isValid() would be better, but parses the regexp and thus is way too expensive for release builds
-- Q_ASSERT(m_regexp.isValid());
-+
-+ if (Log().isDebugEnabled()) {
-+ if (!m_regexp.isValid())
-+ qCDebug(Log) << "Invalid regexp:" << m_regexp.pattern();
-+ }
- return !m_regexp.pattern().isEmpty();
- }
-
---
-2.20.1.windows.1
-
diff --git a/src/libs/3rdparty/syntax-highlighting/patches/0006-Syntax-Highlighter-return-all-definitions-for-a-file.patch b/src/libs/3rdparty/syntax-highlighting/patches/0006-Syntax-Highlighter-return-all-definitions-for-a-file.patch
deleted file mode 100644
index b9de66121c..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/patches/0006-Syntax-Highlighter-return-all-definitions-for-a-file.patch
+++ /dev/null
@@ -1,101 +0,0 @@
-From 4b64058fe7d788ac79c16a08b280ce05d500e9fb Mon Sep 17 00:00:00 2001
-From: Alessandro Portale <alessandro.portale@qt.io>
-Date: Tue, 12 Feb 2019 19:32:54 +0100
-Subject: [PATCH 6/6] Syntax Highlighter: return all definitions for a file
- name/mimetype
-
-Can be used to create user controls to switch between
-multiple definitions for a file or mime type.
-
-Change-Id: I5fd3744db1e819d0d6f8448a53adaf9d2c7c168d
-Reviewed-by: Christian Stenger <christian.stenger@qt.io>
----
- src/lib/repository.cpp | 17 +++++++++++++----
- src/lib/repository.h | 12 ++++++++++++
- 2 files changed, 25 insertions(+), 4 deletions(-)
-
-diff --git a/src/lib/repository.cpp b/src/lib/repository.cpp
-index 6b2fabd..922225a 100644
---- a/src/lib/repository.cpp
-+++ b/src/lib/repository.cpp
-@@ -78,7 +78,7 @@ Definition Repository::definitionForName(const QString& defName) const
- return d->m_defs.value(defName);
- }
-
--static Definition bestCandidate(QVector<Definition>& candidates)
-+static Definition bestCandidate(QVector<Definition> &&candidates)
- {
- if (candidates.isEmpty())
- return Definition();
-@@ -91,6 +91,11 @@ static Definition bestCandidate(QVector<Definition>& candidates)
- }
-
- Definition Repository::definitionForFileName(const QString& fileName) const
-+{
-+ return bestCandidate(definitionsForFileName(fileName));
-+}
-+
-+QVector<Definition> Repository::definitionsForFileName(const QString &fileName) const
- {
- QFileInfo fi(fileName);
- const auto name = fi.fileName();
-@@ -106,10 +111,15 @@ Definition Repository::definitionForFileName(const QString& fileName) const
- }
- }
-
-- return bestCandidate(candidates);
-+ return candidates;
- }
-
- Definition Repository::definitionForMimeType(const QString& mimeType) const
-+{
-+ return bestCandidate(definitionsForMimeType(mimeType));
-+}
-+
-+QVector<Definition> Repository::definitionsForMimeType(const QString &mimeType) const
- {
- QVector<Definition> candidates;
- for (auto it = d->m_defs.constBegin(); it != d->m_defs.constEnd(); ++it) {
-@@ -121,8 +131,7 @@ Definition Repository::definitionForMimeType(const QString& mimeType) const
- }
- }
- }
--
-- return bestCandidate(candidates);
-+ return candidates;
- }
-
- QVector<Definition> Repository::definitions() const
-diff --git a/src/lib/repository.h b/src/lib/repository.h
-index c35da5e..e4e9bed 100644
---- a/src/lib/repository.h
-+++ b/src/lib/repository.h
-@@ -166,6 +166,13 @@ public:
- */
- Definition definitionForFileName(const QString &fileName) const;
-
-+ /**
-+ * Returns all Definition%s for the file named @p fileName.
-+ * The match is performed based on the \e extensions and @e mimetype of
-+ * the definition files.
-+ */
-+ QVector<Definition> definitionsForFileName(const QString &fileName) const;
-+
- /**
- * Returns the best matching Definition to the type named @p mimeType
- *
-@@ -176,6 +183,11 @@ public:
- */
- Definition definitionForMimeType(const QString &mimeType) const;
-
-+ /**
-+ * Returns all Definition%s to the type named @p mimeType
-+ */
-+ QVector<Definition> definitionsForMimeType(const QString &mimeType) const;
-+
- /**
- * Returns all available Definition%s.
- * Definition%ss are ordered by translated section and translated names,
---
-2.20.1.windows.1
-
diff --git a/src/libs/3rdparty/syntax-highlighting/src/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/src/CMakeLists.txt
index b8820252c5..9d53a5bc3d 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/CMakeLists.txt
+++ b/src/libs/3rdparty/syntax-highlighting/src/CMakeLists.txt
@@ -1,5 +1,14 @@
add_subdirectory(indexer)
-if(TARGET Qt5::Gui)
+if(TARGET Qt6::Gui)
add_subdirectory(lib)
add_subdirectory(cli)
endif()
+if(TARGET Qt6::Quick)
+ add_subdirectory(quick)
+endif()
+
+ecm_qt_install_logging_categories(
+ EXPORT KSYNTAXHIGHLIGHTING
+ FILE ksyntaxhighlighting.categories
+ DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
+)
diff --git a/src/libs/3rdparty/syntax-highlighting/src/Messages.sh b/src/libs/3rdparty/syntax-highlighting/src/Messages.sh
index 6fb605ddf0..4024d9b74d 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/Messages.sh
+++ b/src/libs/3rdparty/syntax-highlighting/src/Messages.sh
@@ -8,4 +8,4 @@ sed -i -e 's/^i18nc/QT_TRANSLATE_NOOP/' rc.cpp
grep --no-filename '"name"' ../data/themes/*.theme | \
sed -r -e 's/"name"/QT_TRANSLATE_NOOP("Theme", /; s/://; s/,?$/);/' >> rc.cpp
-$EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/syntaxhighlighting5_qt.pot
+$EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/syntaxhighlighting6_qt.pot
diff --git a/src/libs/3rdparty/syntax-highlighting/src/cli/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/src/cli/CMakeLists.txt
index 113115359e..3aa3a8afc3 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/cli/CMakeLists.txt
+++ b/src/libs/3rdparty/syntax-highlighting/src/cli/CMakeLists.txt
@@ -1,5 +1,5 @@
-add_executable(kate-syntax-highlighter kate-syntax-highlighter.cpp)
-ecm_mark_nongui_executable(kate-syntax-highlighter)
-target_link_libraries(kate-syntax-highlighter KF5SyntaxHighlighting)
+add_executable(ksyntaxhighlighter6 ksyntaxhighlighter.cpp)
+ecm_mark_nongui_executable(ksyntaxhighlighter6)
+target_link_libraries(ksyntaxhighlighter6 KF6SyntaxHighlighting)
-install(TARGETS kate-syntax-highlighter ${INSTALL_TARGETS_DEFAULT_ARGS})
+install(TARGETS ksyntaxhighlighter6 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/src/libs/3rdparty/syntax-highlighting/src/cli/kate-syntax-highlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/cli/kate-syntax-highlighter.cpp
deleted file mode 100644
index 8334dd32e9..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/src/cli/kate-syntax-highlighter.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#include "ksyntaxhighlighting_version.h"
-
-#include <definition.h>
-#include <definitiondownloader.h>
-#include <htmlhighlighter.h>
-#include <repository.h>
-#include <theme.h>
-
-#include <QCommandLineParser>
-#include <QCoreApplication>
-#include <QFile>
-#include <QVector>
-
-#include <iostream>
-
-using namespace KSyntaxHighlighting;
-
-int main(int argc, char **argv)
-{
- QCoreApplication app(argc, argv);
- QCoreApplication::setApplicationName(QStringLiteral("kate-syntax-highlighter"));
- QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
- QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
- QCoreApplication::setApplicationVersion(QStringLiteral(SyntaxHighlighting_VERSION_STRING));
-
- QCommandLineParser parser;
- parser.setApplicationDescription(app.translate("SyntaxHighlightingCLI", "Command line syntax highlighter using Kate syntax definitions."));
- parser.addHelpOption();
- parser.addVersionOption();
- parser.addPositionalArgument(app.translate("SyntaxHighlightingCLI", "source"),
- app.translate("SyntaxHighlightingCLI", "The source file to highlight."));
-
- QCommandLineOption listDefs(QStringList() << QStringLiteral("l") << QStringLiteral("list"),
- app.translate("SyntaxHighlightingCLI", "List all available syntax definitions."));
- parser.addOption(listDefs);
- QCommandLineOption listThemes(QStringList() << QStringLiteral("list-themes"),
- app.translate("SyntaxHighlightingCLI", "List all available themes."));
- parser.addOption(listThemes);
-
- QCommandLineOption updateDefs(QStringList() << QStringLiteral("u") << QStringLiteral("update"),
- app.translate("SyntaxHighlightingCLI", "Download new/updated syntax definitions."));
- parser.addOption(updateDefs);
-
- QCommandLineOption outputName(QStringList() << QStringLiteral("o") << QStringLiteral("output"),
- app.translate("SyntaxHighlightingCLI", "File to write HTML output to (default: stdout)."),
- app.translate("SyntaxHighlightingCLI", "output"));
- parser.addOption(outputName);
-
- QCommandLineOption syntaxName(QStringList() << QStringLiteral("s") << QStringLiteral("syntax"),
- app.translate("SyntaxHighlightingCLI", "Highlight using this syntax definition (default: auto-detect based on input file)."),
- app.translate("SyntaxHighlightingCLI", "syntax"));
- parser.addOption(syntaxName);
-
- QCommandLineOption themeName(QStringList() << QStringLiteral("t") << QStringLiteral("theme"),
- app.translate("SyntaxHighlightingCLI", "Color theme to use for highlighting."),
- app.translate("SyntaxHighlightingCLI", "theme"), QStringLiteral("Default"));
- parser.addOption(themeName);
-
- QCommandLineOption titleOption(QStringList() << QStringLiteral("T") << QStringLiteral("title"),
- app.translate("SyntaxHighlightingCLI", "Set HTML page's title\n(default: the filename or \"Kate Syntax Highlighter\" if reading from stdin)."),
- app.translate("SyntaxHighlightingCLI", "title"));
- parser.addOption(titleOption);
-
- QCommandLineOption stdinOption(QStringList() << QStringLiteral("stdin"),
- app.translate("SyntaxHighlightingCLI", "Read file from stdin. The -s option must also be used."));
- parser.addOption(stdinOption);
-
- parser.process(app);
-
- Repository repo;
- if (parser.isSet(listDefs)) {
- for (const auto &def : repo.definitions()) {
- std::cout << qPrintable(def.name()) << std::endl;
- }
- return 0;
- }
-
- if (parser.isSet(listThemes)) {
- for (const auto &theme : repo.themes())
- std::cout << qPrintable(theme.name()) << std::endl;
- return 0;
- }
-
- if (parser.isSet(updateDefs)) {
- DefinitionDownloader downloader(&repo);
- QObject::connect(&downloader, &DefinitionDownloader::informationMessage, [](const QString &msg) {
- std::cout << qPrintable(msg) << std::endl;
- });
- QObject::connect(&downloader, &DefinitionDownloader::done, &app, &QCoreApplication::quit);
- downloader.start();
- return app.exec();
- }
-
- bool fromFileName = false;
- QString inFileName;
- if (parser.positionalArguments().size() == 1) {
- fromFileName = true;
- inFileName = parser.positionalArguments().at(0);
- }
-
- Definition def;
- if (parser.isSet(syntaxName)) {
- def = repo.definitionForName(parser.value(syntaxName));
- if (!def.isValid())
- /* see if it's a mimetype instead */
- def = repo.definitionForMimeType(parser.value(syntaxName));
- } else if (fromFileName) {
- def = repo.definitionForFileName(inFileName);
- } else {
- parser.showHelp(1);
- }
-
- QString title;
- if (parser.isSet(titleOption))
- title = parser.value(titleOption);
-
- if (!def.isValid()) {
- std::cerr << "Unknown syntax." << std::endl;
- return 1;
- }
-
- HtmlHighlighter highlighter;
- highlighter.setDefinition(def);
- if (parser.isSet(outputName))
- highlighter.setOutputFile(parser.value(outputName));
- else
- highlighter.setOutputFile(stdout);
- highlighter.setTheme(repo.theme(parser.value(themeName)));
-
- if (fromFileName) {
- highlighter.highlightFile(inFileName, title);
- } else if (parser.isSet(stdinOption)) {
- QFile inFile;
- inFile.open(stdin, QIODevice::ReadOnly);
- highlighter.highlightData(&inFile, title);
- } else {
- parser.showHelp(1);
- }
-
- return 0;
-}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/cli/ksyntaxhighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/cli/ksyntaxhighlighter.cpp
new file mode 100644
index 0000000000..681410cb70
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/cli/ksyntaxhighlighter.cpp
@@ -0,0 +1,236 @@
+/*
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#include "ksyntaxhighlighting_version.h"
+
+#include <KSyntaxHighlighting/Definition>
+#include <KSyntaxHighlighting/DefinitionDownloader>
+#include <KSyntaxHighlighting/Repository>
+#include <KSyntaxHighlighting/Theme>
+#include <ansihighlighter.h>
+#include <htmlhighlighter.h>
+
+#include <QCommandLineParser>
+#include <QCoreApplication>
+#include <QFile>
+
+#include <iostream>
+
+using namespace KSyntaxHighlighting;
+
+template<class Highlighter, class... Ts>
+static void applyHighlighter(Highlighter &highlighter,
+ QCommandLineParser &parser,
+ bool fromFileName,
+ const QString &inFileName,
+ const QCommandLineOption &outputName,
+ const Ts &...highlightParams)
+{
+ if (parser.isSet(outputName)) {
+ highlighter.setOutputFile(parser.value(outputName));
+ } else {
+ highlighter.setOutputFile(stdout);
+ }
+
+ if (fromFileName) {
+ highlighter.highlightFile(inFileName, highlightParams...);
+ } else {
+ QFile inFile;
+ inFile.open(stdin, QIODevice::ReadOnly);
+ highlighter.highlightData(&inFile, highlightParams...);
+ }
+}
+
+static Theme theme(const Repository &repo, const QString &themeName, Repository::DefaultTheme t)
+{
+ if (themeName.isEmpty()) {
+ return repo.defaultTheme(t);
+ }
+ return repo.theme(themeName);
+}
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+ QCoreApplication::setApplicationName(QStringLiteral("ksyntaxhighlighter"));
+ QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
+ QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
+ QCoreApplication::setApplicationVersion(QStringLiteral(KSYNTAXHIGHLIGHTING_VERSION_STRING));
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription(app.translate("SyntaxHighlightingCLI", "Command line syntax highlighter using KSyntaxHighlighting syntax definitions."));
+ parser.addHelpOption();
+ parser.addVersionOption();
+ parser.addPositionalArgument(
+ app.translate("SyntaxHighlightingCLI", "source"),
+ app.translate("SyntaxHighlightingCLI", "The source file to highlight. If absent, read the file from stdin and the --syntax option must be used."));
+
+ QCommandLineOption listDefs(QStringList() << QStringLiteral("l") << QStringLiteral("list"),
+ app.translate("SyntaxHighlightingCLI", "List all available syntax definitions."));
+ parser.addOption(listDefs);
+ QCommandLineOption listThemes(QStringList() << QStringLiteral("list-themes"), app.translate("SyntaxHighlightingCLI", "List all available themes."));
+ parser.addOption(listThemes);
+
+ QCommandLineOption updateDefs(QStringList() << QStringLiteral("u") << QStringLiteral("update"),
+ app.translate("SyntaxHighlightingCLI", "Download new/updated syntax definitions."));
+ parser.addOption(updateDefs);
+
+ QCommandLineOption outputName(QStringList() << QStringLiteral("o") << QStringLiteral("output"),
+ app.translate("SyntaxHighlightingCLI", "File to write HTML output to (default: stdout)."),
+ app.translate("SyntaxHighlightingCLI", "output"));
+ parser.addOption(outputName);
+
+ QCommandLineOption syntaxName(QStringList() << QStringLiteral("s") << QStringLiteral("syntax"),
+ app.translate("SyntaxHighlightingCLI", "Highlight using this syntax definition (default: auto-detect based on input file)."),
+ app.translate("SyntaxHighlightingCLI", "syntax"));
+ parser.addOption(syntaxName);
+
+ QCommandLineOption themeName(QStringList() << QStringLiteral("t") << QStringLiteral("theme"),
+ app.translate("SyntaxHighlightingCLI", "Color theme to use for highlighting."),
+ app.translate("SyntaxHighlightingCLI", "theme"));
+ parser.addOption(themeName);
+
+ QCommandLineOption outputFormatOption(
+ QStringList() << QStringLiteral("f") << QStringLiteral("output-format"),
+ app.translate("SyntaxHighlightingCLI", "Use the specified format instead of html. Must be html, ansi or ansi256Colors."),
+ app.translate("SyntaxHighlightingCLI", "format"),
+ QStringLiteral("html"));
+ parser.addOption(outputFormatOption);
+
+ QCommandLineOption traceOption(QStringList() << QStringLiteral("syntax-trace"),
+ app.translate("SyntaxHighlightingCLI",
+ "Add information to debug a syntax file. Only works with --output-format=ansi or ansi256Colors. Possible "
+ "values are format, region, context, stackSize and all."),
+ app.translate("SyntaxHighlightingCLI", "type"));
+ parser.addOption(traceOption);
+
+ QCommandLineOption noAnsiEditorBg(QStringList() << QStringLiteral("b") << QStringLiteral("no-ansi-background"),
+ app.translate("SyntaxHighlightingCLI", "Disable ANSI background for the default color."));
+ parser.addOption(noAnsiEditorBg);
+
+ QCommandLineOption unbufferedAnsi(QStringList() << QStringLiteral("U") << QStringLiteral("unbuffered"),
+ app.translate("SyntaxHighlightingCLI", "For ansi and ansi256Colors formats, flush the output buffer on each line."));
+ parser.addOption(unbufferedAnsi);
+
+ QCommandLineOption titleOption(
+ QStringList() << QStringLiteral("T") << QStringLiteral("title"),
+ app.translate("SyntaxHighlightingCLI", "Set HTML page's title\n(default: the filename or \"KSyntaxHighlighter\" if reading from stdin)."),
+ app.translate("SyntaxHighlightingCLI", "title"));
+ parser.addOption(titleOption);
+
+ parser.process(app);
+
+ Repository repo;
+
+ if (parser.isSet(listDefs)) {
+ for (const auto &def : repo.definitions()) {
+ std::cout << qPrintable(def.name()) << std::endl;
+ }
+ return 0;
+ }
+
+ if (parser.isSet(listThemes)) {
+ for (const auto &theme : repo.themes()) {
+ std::cout << qPrintable(theme.name()) << std::endl;
+ }
+ return 0;
+ }
+
+ if (parser.isSet(updateDefs)) {
+ DefinitionDownloader downloader(&repo);
+ QObject::connect(&downloader, &DefinitionDownloader::informationMessage, [](const QString &msg) {
+ std::cout << qPrintable(msg) << std::endl;
+ });
+ QObject::connect(&downloader, &DefinitionDownloader::done, &app, &QCoreApplication::quit);
+ downloader.start();
+ return app.exec();
+ }
+
+ bool fromFileName = false;
+ QString inFileName;
+ if (parser.positionalArguments().size() == 1) {
+ fromFileName = true;
+ inFileName = parser.positionalArguments().at(0);
+ }
+
+ Definition def;
+ if (parser.isSet(syntaxName)) {
+ const QString syntax = parser.value(syntaxName);
+ def = repo.definitionForName(syntax);
+ if (!def.isValid()) {
+ /* see if it's a mimetype instead */
+ def = repo.definitionForMimeType(syntax);
+ if (!def.isValid()) {
+ /* see if it's a extension instead */
+ def = repo.definitionForFileName(QLatin1String("f.") + syntax);
+ if (!def.isValid()) {
+ /* see if it's a filename instead */
+ def = repo.definitionForFileName(syntax);
+ }
+ }
+ }
+ } else if (fromFileName) {
+ def = repo.definitionForFileName(inFileName);
+ } else {
+ parser.showHelp(1);
+ }
+
+ if (!def.isValid()) {
+ std::cerr << "Unknown syntax." << std::endl;
+ return 1;
+ }
+
+ const QString outputFormat = parser.value(outputFormatOption);
+ if (0 == outputFormat.compare(QLatin1String("html"), Qt::CaseInsensitive)) {
+ QString title;
+ if (parser.isSet(titleOption)) {
+ title = parser.value(titleOption);
+ }
+
+ HtmlHighlighter highlighter;
+ highlighter.setDefinition(def);
+ highlighter.setTheme(theme(repo, parser.value(themeName), Repository::LightTheme));
+ applyHighlighter(highlighter, parser, fromFileName, inFileName, outputName, title);
+ } else {
+ auto AnsiFormat = AnsiHighlighter::AnsiFormat::TrueColor;
+ if (0 == outputFormat.compare(QLatin1String("ansi256Colors"), Qt::CaseInsensitive)) {
+ AnsiFormat = AnsiHighlighter::AnsiFormat::XTerm256Color;
+ } else if (0 != outputFormat.compare(QLatin1String("ansi"), Qt::CaseInsensitive)) {
+ std::cerr << "Unknown output format." << std::endl;
+ return 2;
+ }
+
+ AnsiHighlighter::Options options{};
+ options |= parser.isSet(noAnsiEditorBg) ? AnsiHighlighter::Option::NoOptions : AnsiHighlighter::Option::UseEditorBackground;
+ options |= parser.isSet(unbufferedAnsi) ? AnsiHighlighter::Option::Unbuffered : AnsiHighlighter::Option::NoOptions;
+ if (parser.isSet(traceOption)) {
+ const auto traceOptions = parser.values(traceOption);
+ for (auto const &option : traceOptions) {
+ if (option == QStringLiteral("format")) {
+ options |= AnsiHighlighter::Option::TraceFormat;
+ } else if (option == QStringLiteral("region")) {
+ options |= AnsiHighlighter::Option::TraceRegion;
+ } else if (option == QStringLiteral("context")) {
+ options |= AnsiHighlighter::Option::TraceContext;
+ } else if (option == QStringLiteral("stackSize")) {
+ options |= AnsiHighlighter::Option::TraceStackSize;
+ } else if (option == QStringLiteral("all")) {
+ options |= AnsiHighlighter::Option::TraceAll;
+ } else {
+ std::cerr << "Unknown trace name." << std::endl;
+ return 2;
+ }
+ }
+ }
+
+ AnsiHighlighter highlighter;
+ highlighter.setDefinition(def);
+ highlighter.setTheme(theme(repo, parser.value(themeName), Repository::DarkTheme));
+ applyHighlighter(highlighter, parser, fromFileName, inFileName, outputName, AnsiFormat, options);
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/indexer/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/src/indexer/CMakeLists.txt
index 9fa26b27ce..cf6940b7bd 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/indexer/CMakeLists.txt
+++ b/src/libs/3rdparty/syntax-highlighting/src/indexer/CMakeLists.txt
@@ -4,13 +4,8 @@ if(CMAKE_CROSSCOMPILING AND KATEHIGHLIGHTINGINDEXER_EXECUTABLE)
add_executable(katehighlightingindexer IMPORTED GLOBAL)
set_target_properties(katehighlightingindexer PROPERTIES IMPORTED_LOCATION ${KATEHIGHLIGHTINGINDEXER_EXECUTABLE})
elseif(CMAKE_CROSSCOMPILING)
- if (NOT KF5_HOST_TOOLING)
- message(FATAL_ERROR "Please provide a prefix with a native Qt build and pass -DKF5_HOST_TOOLING=path")
- endif()
-
- # search native tooling prefix
- string(FIND ${KF5_HOST_TOOLING} /lib idx)
- string(SUBSTRING ${KF5_HOST_TOOLING} 0 ${idx} NATIVE_PREFIX)
+ include(ECMQueryQt)
+ ecm_query_qt(NATIVE_PREFIX QT_HOST_PREFIX)
message(STATUS "Building katehighlightingindexer against ${NATIVE_PREFIX}")
include(ExternalProject)
@@ -19,7 +14,9 @@ elseif(CMAKE_CROSSCOMPILING)
CMAKE_ARGS -DKSYNTAXHIGHLIGHTING_USE_GUI=OFF
-DECM_DIR=${ECM_DIR} -DCMAKE_PREFIX_PATH=${NATIVE_PREFIX}
-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}
+ -DQT_MAJOR_VERSION=6
INSTALL_COMMAND ""
+ BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/native_katehighlightingindexer-prefix/src/native_katehighlightingindexer-build/bin/katehighlightingindexer
)
add_executable(katehighlightingindexer IMPORTED GLOBAL)
add_dependencies(katehighlightingindexer native_katehighlightingindexer)
@@ -27,10 +24,13 @@ elseif(CMAKE_CROSSCOMPILING)
${CMAKE_CURRENT_BINARY_DIR}/native_katehighlightingindexer-prefix/src/native_katehighlightingindexer-build/bin/katehighlightingindexer)
else()
# host build
- add_executable(katehighlightingindexer katehighlightingindexer.cpp)
- if(Qt5XmlPatterns_FOUND)
- target_link_libraries(katehighlightingindexer Qt5::XmlPatterns)
+ add_executable(katehighlightingindexer katehighlightingindexer.cpp ../lib/worddelimiters.cpp)
+ ecm_mark_nongui_executable(katehighlightingindexer)
+ if(XercesC_FOUND)
+ add_definitions(-DHAS_XERCESC)
+ kde_target_enable_exceptions(katehighlightingindexer PRIVATE)
+ target_link_libraries(katehighlightingindexer Qt6::Core XercesC::XercesC)
else()
- target_link_libraries(katehighlightingindexer Qt5::Core)
+ target_link_libraries(katehighlightingindexer Qt6::Core)
endif()
endif()
diff --git a/src/libs/3rdparty/syntax-highlighting/src/indexer/katehighlightingindexer.cpp b/src/libs/3rdparty/syntax-highlighting/src/indexer/katehighlightingindexer.cpp
index 3534cfde90..787747e21c 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/indexer/katehighlightingindexer.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/indexer/katehighlightingindexer.cpp
@@ -1,386 +1,2627 @@
/*
- Copyright (C) 2014 Christoph Cullmann <cullmann@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2014 Christoph Cullmann <cullmann@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
+#include <QCborValue>
#include <QCoreApplication>
+#include <QDebug>
#include <QFile>
#include <QFileInfo>
-#include <QTextStream>
+#include <QMutableMapIterator>
+#include <QRegularExpression>
+#include <QScopeGuard>
#include <QVariant>
#include <QXmlStreamReader>
-#include <QJsonDocument>
-#include <QRegularExpression>
-#include <QDebug>
-#ifdef QT_XMLPATTERNS_LIB
-#include <QXmlSchema>
-#include <QXmlSchemaValidator>
-#endif
+#ifdef HAS_XERCESC
-namespace {
+#include <xercesc/framework/XMLGrammarPoolImpl.hpp>
-QStringList readListing(const QString &fileName)
+#include <xercesc/parsers/SAX2XMLReaderImpl.hpp>
+
+#include <xercesc/sax/ErrorHandler.hpp>
+#include <xercesc/sax/SAXParseException.hpp>
+
+#include <xercesc/util/PlatformUtils.hpp>
+#include <xercesc/util/XMLString.hpp>
+#include <xercesc/util/XMLUni.hpp>
+
+#include <xercesc/framework/XMLGrammarPoolImpl.hpp>
+#include <xercesc/validators/common/Grammar.hpp>
+
+using namespace xercesc;
+
+/*
+ * Ideas taken from:
+ *
+ * author : Boris Kolpackov <boris@codesynthesis.com>
+ * copyright : not copyrighted - public domain
+ *
+ * This program uses Xerces-C++ SAX2 parser to load a set of schema files
+ * and then to validate a set of XML documents against these schemas. To
+ * build this program you will need Xerces-C++ 3.0.0 or later. For more
+ * information, see:
+ *
+ * http://www.codesynthesis.com/~boris/blog/2010/03/15/validating-external-schemas-xerces-cxx/
+ */
+
+/**
+ * Error handler object used during xml schema validation.
+ */
+class CustomErrorHandler : public ErrorHandler
{
- QFile file(fileName);
- if (!file.open(QIODevice::ReadOnly)) {
- return QStringList();
+public:
+ /**
+ * Constructor
+ * @param messages Pointer to the error message string to fill.
+ */
+ CustomErrorHandler(QString *messages)
+ : m_messages(messages)
+ {
}
- QXmlStreamReader xml(&file);
- QStringList listing;
- while (!xml.atEnd()) {
- xml.readNext();
+ /**
+ * Check global success/fail state.
+ * @return True if there was a failure, false otherwise.
+ */
+ bool failed() const
+ {
+ return m_failed;
+ }
- // add only .xml files, no .json or stuff
- if (xml.isCharacters() && xml.text().toString().contains(QLatin1String(".xml"))) {
- listing.append(xml.text().toString());
- }
+private:
+ /**
+ * Severity classes for error messages.
+ */
+ enum severity { s_warning, s_error, s_fatal };
+
+ /**
+ * Wrapper for warning exceptions.
+ * @param e Exception to handle.
+ */
+ void warning(const SAXParseException &e) override
+ {
+ m_failed = true; // be strict, warnings are evil, too!
+ handle(e, s_warning);
}
- if (xml.hasError()) {
- qWarning() << "XML error while reading" << fileName << " - "
- << qPrintable(xml.errorString()) << "@ offset" << xml.characterOffset();
+ /**
+ * Wrapper for error exceptions.
+ * @param e Exception to handle.
+ */
+ void error(const SAXParseException &e) override
+ {
+ m_failed = true;
+ handle(e, s_error);
}
- return listing;
-}
+ /**
+ * Wrapper for fatal error exceptions.
+ * @param e Exception to handle.
+ */
+ void fatalError(const SAXParseException &e) override
+ {
+ m_failed = true;
+ handle(e, s_fatal);
+ }
-/**
- * check if the "extensions" attribute have valid wildcards
- * @param extensions extensions string to check
- * @return valid?
- */
-bool checkExtensions(QString extensions)
+ /**
+ * Reset the error status to "no error".
+ */
+ void resetErrors() override
+ {
+ m_failed = false;
+ }
+
+ /**
+ * Generic handler for error/warning/fatal error message exceptions.
+ * @param e Exception to handle.
+ * @param s Enum value encoding the message severtity.
+ */
+ void handle(const SAXParseException &e, severity s)
+ {
+ // get id to print
+ const XMLCh *xid(e.getPublicId());
+ if (!xid)
+ xid = e.getSystemId();
+
+ m_messages << QString::fromUtf16(xid) << ":" << e.getLineNumber() << ":" << e.getColumnNumber() << " " << (s == s_warning ? "warning: " : "error: ")
+ << QString::fromUtf16(e.getMessage()) << Qt::endl;
+ }
+
+private:
+ /**
+ * Storage for created error messages in this handler.
+ */
+ QTextStream m_messages;
+
+ /**
+ * Global error state. True if there was an error, false otherwise.
+ */
+ bool m_failed = false;
+};
+
+void init_parser(SAX2XMLReaderImpl &parser)
{
- // get list of extensions
- const QStringList extensionParts = extensions.split(QLatin1Char(';'), QString::SkipEmptyParts);
+ // Commonly useful configuration.
+ //
+ parser.setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
+ parser.setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true);
+ parser.setFeature(XMLUni::fgSAX2CoreValidation, true);
+
+ // Enable validation.
+ //
+ parser.setFeature(XMLUni::fgXercesSchema, true);
+ parser.setFeature(XMLUni::fgXercesSchemaFullChecking, true);
+ parser.setFeature(XMLUni::fgXercesValidationErrorAsFatal, true);
+
+ // Use the loaded grammar during parsing.
+ //
+ parser.setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true);
+
+ // Don't load schemas from any other source (e.g., from XML document's
+ // xsi:schemaLocation attributes).
+ //
+ parser.setFeature(XMLUni::fgXercesLoadSchema, false);
+
+ // Xerces-C++ 3.1.0 is the first version with working multi import
+ // support.
+ //
+ parser.setFeature(XMLUni::fgXercesHandleMultipleImports, true);
+}
- // ok if empty
- if (extensionParts.isEmpty()) {
- return true;
+#endif
+
+#include "../lib/worddelimiters_p.h"
+#include "../lib/xml_p.h"
+
+#include <array>
+
+using KSyntaxHighlighting::WordDelimiters;
+using KSyntaxHighlighting::Xml::attrToBool;
+
+class HlFilesChecker
+{
+public:
+ template<typename T>
+ void setDefinition(const T &verStr, const QString &filename, const QString &name)
+ {
+ m_currentDefinition = &*m_definitions.insert(name, Definition{});
+ m_currentDefinition->languageName = name;
+ m_currentDefinition->filename = filename;
+ m_currentDefinition->kateVersionStr = verStr.toString();
+ m_currentKeywords = nullptr;
+ m_currentContext = nullptr;
+
+ const auto idx = verStr.indexOf(QLatin1Char('.'));
+ if (idx <= 0) {
+ qWarning() << filename << "invalid kateversion" << verStr;
+ m_success = false;
+ } else {
+ m_currentDefinition->kateVersion = {verStr.left(idx).toInt(), verStr.mid(idx + 1).toInt()};
+ }
}
- // check that only valid wildcard things are inside the parts
- for (const auto& extension : extensionParts) {
- for (const auto c : extension) {
- // eat normal things
- if (c.isDigit() || c.isLetter()) {
- continue;
+ void processElement(QXmlStreamReader &xml)
+ {
+ if (xml.isStartElement()) {
+ if (m_currentContext) {
+ m_currentContext->rules.push_back(Context::Rule{});
+ auto &rule = m_currentContext->rules.back();
+ m_success = rule.parseElement(m_currentDefinition->filename, xml) && m_success;
+ m_currentContext->hasDynamicRule = m_currentContext->hasDynamicRule || rule.dynamic == XmlBool::True;
+ } else if (m_currentKeywords) {
+ m_success = m_currentKeywords->items.parseElement(m_currentDefinition->filename, xml) && m_success;
+ } else if (xml.name() == QStringLiteral("context")) {
+ processContextElement(xml);
+ } else if (xml.name() == QStringLiteral("list")) {
+ processListElement(xml);
+ } else if (xml.name() == QStringLiteral("keywords")) {
+ m_success = m_currentDefinition->parseKeywords(xml) && m_success;
+ } else if (xml.name() == QStringLiteral("emptyLine")) {
+ m_success = parseEmptyLine(m_currentDefinition->filename, xml) && m_success;
+ } else if (xml.name() == QStringLiteral("itemData")) {
+ m_success = m_currentDefinition->itemDatas.parseElement(m_currentDefinition->filename, xml) && m_success;
+ }
+ } else if (xml.isEndElement()) {
+ if (m_currentContext && xml.name() == QStringLiteral("context")) {
+ m_currentContext = nullptr;
+ } else if (m_currentKeywords && xml.name() == QStringLiteral("list")) {
+ m_currentKeywords = nullptr;
}
+ }
+ }
- // allow some special characters
- if (c == QLatin1Char('.') || c == QLatin1Char('-') || c == QLatin1Char('_') || c == QLatin1Char('+')) {
+ //! Resolve context attribute and include tag
+ void resolveContexts()
+ {
+ QMutableMapIterator<QString, Definition> def(m_definitions);
+ while (def.hasNext()) {
+ def.next();
+ auto &definition = def.value();
+ auto &contexts = definition.contexts;
+
+ if (contexts.isEmpty()) {
+ qWarning() << definition.filename << "has no context";
+ m_success = false;
continue;
}
- // only allowed wildcard things: '?' and '*'
- if (c == QLatin1Char('?') || c == QLatin1Char('*')) {
- continue;
+ auto markAsUsedContext = [](ContextName &contextName) {
+ if (!contextName.stay && contextName.context) {
+ contextName.context->isOnlyIncluded = false;
+ }
+ };
+
+ QMutableMapIterator<QString, Context> contextIt(contexts);
+ while (contextIt.hasNext()) {
+ contextIt.next();
+ auto &context = contextIt.value();
+ resolveContextName(definition, context, context.lineEndContext, context.line);
+ resolveContextName(definition, context, context.lineEmptyContext, context.line);
+ resolveContextName(definition, context, context.fallthroughContext, context.line);
+ markAsUsedContext(context.lineEndContext);
+ markAsUsedContext(context.lineEmptyContext);
+ markAsUsedContext(context.fallthroughContext);
+ for (auto &rule : context.rules) {
+ rule.parentContext = &context;
+ resolveContextName(definition, context, rule.context, rule.line);
+ if (rule.type != Context::Rule::Type::IncludeRules) {
+ markAsUsedContext(rule.context);
+ } else if (rule.includeAttrib == XmlBool::True && rule.context.context) {
+ rule.context.context->referencedWithIncludeAttrib = true;
+ }
+ }
}
- qWarning() << "invalid character" << c << " seen in extensions wildcard";
- return false;
+ auto *firstContext = &*definition.contexts.find(definition.firstContextName);
+ firstContext->isOnlyIncluded = false;
+ definition.firstContext = firstContext;
}
+
+ resolveIncludeRules();
}
- // all checks passed
- return true;
-}
+ bool check() const
+ {
+ bool success = m_success;
-//! Check that a regular expression in a RegExpr rule:
-//! - is not empty
-//! - isValid()
-//! - character ranges such as [A-Z] are valid and not accidentally e.g. [A-z].
-bool checkRegularExpression(const QString &hlFilename, QXmlStreamReader &xml)
-{
- if (xml.name() == QLatin1String("RegExpr") || xml.name() == QLatin1String("emptyLine")) {
- // get right attribute
- const QString string (xml.attributes().value((xml.name() == QLatin1String("RegExpr")) ? QLatin1String("String") : QLatin1String("regexpr")).toString());
+ const auto usedContexts = extractUsedContexts();
- // validate regexp
- const QRegularExpression regexp (string);
- if (!regexp.isValid()) {
- qWarning() << hlFilename << "line" << xml.lineNumber() << "broken regex:" << string << "problem:" << regexp.errorString() << "at offset" << regexp.patternErrorOffset();
- return false;
- } else if (string.isEmpty()) {
- qWarning() << hlFilename << "line" << xml.lineNumber() << "empty regex not allowed.";
- return false;
+ QMap<const Definition *, const Definition *> maxVersionByDefinitions;
+ QMap<const Context::Rule *, IncludedRuleUnreachableBy> unreachableIncludedRules;
+
+ QMapIterator<QString, Definition> def(m_definitions);
+ while (def.hasNext()) {
+ def.next();
+ const auto &definition = def.value();
+ const auto &filename = definition.filename;
+
+ auto *maxDef = maxKateVersionDefinition(definition, maxVersionByDefinitions);
+ if (maxDef != &definition) {
+ qWarning() << definition.filename << "depends on a language" << maxDef->languageName << "in version" << maxDef->kateVersionStr
+ << ". Please, increase kateversion.";
+ success = false;
+ }
+
+ QSet<ItemDatas::Style> usedAttributeNames;
+ QSet<ItemDatas::Style> ignoredAttributeNames;
+ success = checkKeywordsList(definition) && success;
+ success = checkContexts(definition, usedAttributeNames, ignoredAttributeNames, usedContexts, unreachableIncludedRules) && success;
+
+ // search for non-existing itemDatas.
+ const auto invalidNames = usedAttributeNames - definition.itemDatas.styleNames;
+ for (const auto &styleName : invalidNames) {
+ qWarning() << filename << "line" << styleName.line << "reference of non-existing itemData attributes:" << styleName.name;
+ success = false;
+ }
+
+ // search for existing itemDatas, but unusable.
+ const auto ignoredNames = ignoredAttributeNames - usedAttributeNames;
+ for (const auto &styleName : ignoredNames) {
+ qWarning() << filename << "line" << styleName.line << "attribute" << styleName.name
+ << "is never used. All uses are with lookAhead=true or <IncludeRules/>";
+ success = false;
+ }
+
+ // search for unused itemDatas.
+ auto unusedNames = definition.itemDatas.styleNames - usedAttributeNames;
+ unusedNames -= ignoredNames;
+ for (const auto &styleName : std::as_const(unusedNames)) {
+ qWarning() << filename << "line" << styleName.line << "unused itemData:" << styleName.name;
+ success = false;
+ }
}
- // catch possible case typos: [A-z] or [a-Z]
- const int azOffset = std::max(string.indexOf(QStringLiteral("A-z")), string.indexOf(QStringLiteral("a-Z")));
- if (azOffset >= 0) {
- qWarning() << hlFilename << "line" << xml.lineNumber() << "broken regex:" << string << "problem: [a-Z] or [A-z] at offset" << azOffset;
- return false;
+ QMutableMapIterator<const Context::Rule *, IncludedRuleUnreachableBy> unreachableIncludedRuleIt(unreachableIncludedRules);
+ while (unreachableIncludedRuleIt.hasNext()) {
+ unreachableIncludedRuleIt.next();
+ IncludedRuleUnreachableBy &unreachableRulesBy = unreachableIncludedRuleIt.value();
+ if (unreachableRulesBy.alwaysUnreachable) {
+ auto *rule = unreachableIncludedRuleIt.key();
+
+ if (!rule->parentContext->isOnlyIncluded) {
+ continue;
+ }
+
+ // remove duplicates rules
+ QSet<const Context::Rule *> rules;
+ auto &unreachableBy = unreachableRulesBy.unreachableBy;
+ unreachableBy.erase(std::remove_if(unreachableBy.begin(),
+ unreachableBy.end(),
+ [&](const RuleAndInclude &ruleAndInclude) {
+ if (rules.contains(ruleAndInclude.rule)) {
+ return true;
+ }
+ rules.insert(ruleAndInclude.rule);
+ return false;
+ }),
+ unreachableBy.end());
+
+ QString message;
+ message.reserve(128);
+ for (auto &ruleAndInclude : std::as_const(unreachableBy)) {
+ message += QStringLiteral("line ");
+ message += QString::number(ruleAndInclude.rule->line);
+ message += QStringLiteral(" [");
+ message += ruleAndInclude.rule->parentContext->name;
+ if (rule->filename != ruleAndInclude.rule->filename) {
+ message += QStringLiteral(" (");
+ message += ruleAndInclude.rule->filename;
+ message += QLatin1Char(')');
+ }
+ if (ruleAndInclude.includeRules) {
+ message += QStringLiteral(" via line ");
+ message += QString::number(ruleAndInclude.includeRules->line);
+ }
+ message += QStringLiteral("], ");
+ }
+ message.chop(2);
+
+ qWarning() << rule->filename << "line" << rule->line << "no IncludeRule can reach this rule, hidden by" << message;
+ success = false;
+ }
}
+
+ return success;
}
- return true;
-}
+private:
+ enum class XmlBool {
+ Unspecified,
+ False,
+ True,
+ };
-//! Check that keyword list items do not have trailing or leading spaces,
-//! e.g.: <item> keyword </item>
-bool checkItemsTrimmed(const QString &hlFilename, QXmlStreamReader &xml)
-{
- if (xml.name() == QLatin1String("item")) {
- const QString keyword = xml.readElementText();
- if (keyword != keyword.trimmed()) {
- qWarning() << hlFilename << "line" << xml.lineNumber() << "keyword with leading/trailing spaces:" << keyword;
+ struct Context;
+
+ struct ContextName {
+ QString name;
+ int popCount = 0;
+ bool stay = false;
+
+ Context *context = nullptr;
+ };
+
+ struct Parser {
+ const QString &filename;
+ QXmlStreamReader &xml;
+ QXmlStreamAttribute &attr;
+ bool success;
+
+ //! Read a string type attribute, \c success = \c false when \p str is not empty
+ //! \return \c true when attr.name() == attrName, otherwise false
+ bool extractString(QString &str, const QString &attrName)
+ {
+ if (attr.name() != attrName) {
+ return false;
+ }
+
+ str = attr.value().toString();
+ if (str.isEmpty()) {
+ qWarning() << filename << "line" << xml.lineNumber() << attrName << "attribute is empty";
+ success = false;
+ }
+
+ return true;
+ }
+
+ //! Read a bool type attribute, \c success = \c false when \p xmlBool is not \c XmlBool::Unspecified.
+ //! \return \c true when attr.name() == attrName, otherwise false
+ bool extractXmlBool(XmlBool &xmlBool, const QString &attrName)
+ {
+ if (attr.name() != attrName) {
+ return false;
+ }
+
+ xmlBool = attr.value().isNull() ? XmlBool::Unspecified : attrToBool(attr.value()) ? XmlBool::True : XmlBool::False;
+
+ return true;
+ }
+
+ //! Read a positive integer type attribute, \c success = \c false when \p positive is already greater than or equal to 0
+ //! \return \c true when attr.name() == attrName, otherwise false
+ bool extractPositive(int &positive, const QString &attrName)
+ {
+ if (attr.name() != attrName) {
+ return false;
+ }
+
+ bool ok = true;
+ positive = attr.value().toInt(&ok);
+
+ if (!ok || positive < 0) {
+ qWarning() << filename << "line" << xml.lineNumber() << attrName << "should be a positive integer:" << attr.value();
+ success = false;
+ }
+
+ return true;
+ }
+
+ //! Read a color, \c success = \c false when \p color is already greater than or equal to 0
+ //! \return \c true when attr.name() == attrName, otherwise false
+ bool checkColor(const QString &attrName)
+ {
+ if (attr.name() != attrName) {
+ return false;
+ }
+
+ const auto value = attr.value();
+ if (value.isEmpty() /*|| QColor(value).isValid()*/) {
+ qWarning() << filename << "line" << xml.lineNumber() << attrName << "should be a color:" << value;
+ success = false;
+ }
+
+ return true;
+ }
+
+ //! Read a QChar, \c success = \c false when \p c is not \c '\0' or does not have one char
+ //! \return \c true when attr.name() == attrName, otherwise false
+ bool extractChar(QChar &c, const QString &attrName)
+ {
+ if (attr.name() != attrName) {
+ return false;
+ }
+
+ if (attr.value().size() == 1) {
+ c = attr.value()[0];
+ } else {
+ c = QLatin1Char('_');
+ qWarning() << filename << "line" << xml.lineNumber() << attrName << "must contain exactly one char:" << attr.value();
+ success = false;
+ }
+
+ return true;
+ }
+
+ //! \return parsing status when \p isExtracted is \c true, otherwise \c false
+ bool checkIfExtracted(bool isExtracted)
+ {
+ if (isExtracted) {
+ return success;
+ }
+
+ qWarning() << filename << "line" << xml.lineNumber() << "unknown attribute:" << attr.name();
return false;
}
- }
+ };
- return true;
-}
+ struct Keywords {
+ struct Items {
+ struct Item {
+ QString content;
+ int line;
-//! Checks that DetectChar and Detect2Chars really only have one char
-//! in the attributes 'char' and 'char1'.
-bool checkSingleChars(const QString &hlFilename, QXmlStreamReader &xml)
-{
- const bool testChar1 = xml.name() == QLatin1String("Detect2Chars");
- const bool testChar = testChar1 || xml.name() == QLatin1String("DetectChar");
+ friend size_t qHash(const Item &item, size_t seed = 0)
+ {
+ return qHash(item.content, seed);
+ }
+
+ friend bool operator==(const Item &item0, const Item &item1)
+ {
+ return item0.content == item1.content;
+ }
+ };
+
+ QList<Item> keywords;
+ QSet<Item> includes;
+
+ bool parseElement(const QString &filename, QXmlStreamReader &xml)
+ {
+ bool success = true;
+
+ const int line = xml.lineNumber();
+ QString content = xml.readElementText();
- if (testChar) {
- const QString c = xml.attributes().value(QLatin1String("char")).toString();
- if (c.size() != 1) {
- qWarning() << hlFilename << "line" << xml.lineNumber() << "'char' must contain exactly one char:" << c;
+ if (content.isEmpty()) {
+ qWarning() << filename << "line" << line << "is empty:" << xml.name();
+ success = false;
+ }
+
+ if (xml.name() == QStringLiteral("include")) {
+ includes.insert({content, line});
+ } else if (xml.name() == QStringLiteral("item")) {
+ keywords.append({content, line});
+ } else {
+ qWarning() << filename << "line" << line << "invalid element:" << xml.name();
+ success = false;
+ }
+
+ return success;
+ }
+ };
+
+ QString name;
+ Items items;
+ int line;
+
+ bool parseElement(const QString &filename, QXmlStreamReader &xml)
+ {
+ line = xml.lineNumber();
+
+ bool success = true;
+ for (auto &attr : xml.attributes()) {
+ Parser parser{filename, xml, attr, success};
+
+ const bool isExtracted = parser.extractString(name, QStringLiteral("name"));
+
+ success = parser.checkIfExtracted(isExtracted);
+ }
+ return success;
+ }
+ };
+
+ struct Context {
+ struct Rule {
+ enum class Type {
+ Unknown,
+ AnyChar,
+ Detect2Chars,
+ DetectChar,
+ DetectIdentifier,
+ DetectSpaces,
+ Float,
+ HlCChar,
+ HlCHex,
+ HlCOct,
+ HlCStringChar,
+ IncludeRules,
+ Int,
+ LineContinue,
+ RangeDetect,
+ RegExpr,
+ StringDetect,
+ WordDetect,
+ keyword,
+ };
+
+ Type type{};
+
+ bool isDotRegex = false;
+ int line = -1;
+
+ // commonAttributes
+ QString attribute;
+ ContextName context;
+ QString beginRegion;
+ QString endRegion;
+ int column = -1;
+ XmlBool lookAhead{};
+ XmlBool firstNonSpace{};
+
+ // StringDetect, WordDetect, keyword
+ XmlBool insensitive{};
+
+ // DetectChar, StringDetect, RegExpr, keyword
+ XmlBool dynamic{};
+
+ // Regex
+ XmlBool minimal{};
+
+ // IncludeRule
+ XmlBool includeAttrib{};
+
+ // DetectChar, Detect2Chars, LineContinue, RangeDetect
+ QChar char0;
+ // Detect2Chars, RangeDetect
+ QChar char1;
+
+ // AnyChar, DetectChar, StringDetect, RegExpr, WordDetect, keyword
+ QString string;
+ // RegExpr without .* as suffix
+ QString sanitizedString;
+
+ // Float, HlCHex, HlCOct, Int, WordDetect, keyword
+ QString additionalDeliminator;
+ QString weakDeliminator;
+
+ // rules included by IncludeRules (without IncludeRule)
+ QList<const Rule *> includedRules;
+
+ // IncludeRules included by IncludeRules
+ QSet<const Rule *> includedIncludeRules;
+
+ Context const *parentContext = nullptr;
+
+ QString filename;
+
+ bool parseElement(const QString &filename, QXmlStreamReader &xml)
+ {
+ this->filename = filename;
+ line = xml.lineNumber();
+
+ using Pair = QPair<QString, Type>;
+ static const auto pairs = {
+ Pair{QStringLiteral("AnyChar"), Type::AnyChar},
+ Pair{QStringLiteral("Detect2Chars"), Type::Detect2Chars},
+ Pair{QStringLiteral("DetectChar"), Type::DetectChar},
+ Pair{QStringLiteral("DetectIdentifier"), Type::DetectIdentifier},
+ Pair{QStringLiteral("DetectSpaces"), Type::DetectSpaces},
+ Pair{QStringLiteral("Float"), Type::Float},
+ Pair{QStringLiteral("HlCChar"), Type::HlCChar},
+ Pair{QStringLiteral("HlCHex"), Type::HlCHex},
+ Pair{QStringLiteral("HlCOct"), Type::HlCOct},
+ Pair{QStringLiteral("HlCStringChar"), Type::HlCStringChar},
+ Pair{QStringLiteral("IncludeRules"), Type::IncludeRules},
+ Pair{QStringLiteral("Int"), Type::Int},
+ Pair{QStringLiteral("LineContinue"), Type::LineContinue},
+ Pair{QStringLiteral("RangeDetect"), Type::RangeDetect},
+ Pair{QStringLiteral("RegExpr"), Type::RegExpr},
+ Pair{QStringLiteral("StringDetect"), Type::StringDetect},
+ Pair{QStringLiteral("WordDetect"), Type::WordDetect},
+ Pair{QStringLiteral("keyword"), Type::keyword},
+ };
+
+ for (auto pair : pairs) {
+ if (xml.name() == pair.first) {
+ type = pair.second;
+ bool success = parseAttributes(filename, xml);
+ success = checkMandoryAttributes(filename, xml) && success;
+ if (success && type == Type::RegExpr) {
+ // ., (.) followed by *, +, {1} or nothing
+ static const QRegularExpression isDot(QStringLiteral(R"(^\(?\.(?:[*+][*+?]?|[*+]|\{1\})?\$?$)"));
+ // remove "(?:" and ")"
+ static const QRegularExpression removeParentheses(QStringLiteral(R"(\((?:\?:)?|\))"));
+ // remove parentheses on a copy of string
+ auto reg = QString(string).replace(removeParentheses, QString());
+ isDotRegex = reg.contains(isDot);
+
+ // Remove .* and .*$ suffix.
+ static const QRegularExpression allSuffix(QStringLiteral("(?<!\\\\)[.][*][?+]?[$]?$"));
+ sanitizedString = string;
+ sanitizedString.replace(allSuffix, QString());
+ // string is a catch-all, do not sanitize
+ if (sanitizedString.isEmpty() || sanitizedString == QStringLiteral("^")) {
+ sanitizedString = string;
+ }
+ }
+ return success;
+ }
+ }
+
+ qWarning() << filename << "line" << xml.lineNumber() << "unknown element:" << xml.name();
+ return false;
+ }
+
+ private:
+ bool parseAttributes(const QString &filename, QXmlStreamReader &xml)
+ {
+ bool success = true;
+
+ for (auto &attr : xml.attributes()) {
+ Parser parser{filename, xml, attr, success};
+
+ // clang-format off
+ const bool isExtracted
+ = parser.extractString(attribute, QStringLiteral("attribute"))
+ || parser.extractString(context.name, QStringLiteral("context"))
+ || parser.extractXmlBool(lookAhead, QStringLiteral("lookAhead"))
+ || parser.extractXmlBool(firstNonSpace, QStringLiteral("firstNonSpace"))
+ || parser.extractString(beginRegion, QStringLiteral("beginRegion"))
+ || parser.extractString(endRegion, QStringLiteral("endRegion"))
+ || parser.extractPositive(column, QStringLiteral("column"))
+ || ((type == Type::RegExpr
+ || type == Type::StringDetect
+ || type == Type::WordDetect
+ || type == Type::keyword
+ ) && parser.extractXmlBool(insensitive, QStringLiteral("insensitive")))
+ || ((type == Type::DetectChar
+ || type == Type::RegExpr
+ || type == Type::StringDetect
+ || type == Type::keyword
+ ) && parser.extractXmlBool(dynamic, QStringLiteral("dynamic")))
+ || ((type == Type::RegExpr)
+ && parser.extractXmlBool(minimal, QStringLiteral("minimal")))
+ || ((type == Type::DetectChar
+ || type == Type::Detect2Chars
+ || type == Type::LineContinue
+ || type == Type::RangeDetect
+ ) && parser.extractChar(char0, QStringLiteral("char")))
+ || ((type == Type::Detect2Chars
+ || type == Type::RangeDetect
+ ) && parser.extractChar(char1, QStringLiteral("char1")))
+ || ((type == Type::AnyChar
+ || type == Type::RegExpr
+ || type == Type::StringDetect
+ || type == Type::WordDetect
+ || type == Type::keyword
+ ) && parser.extractString(string, QStringLiteral("String")))
+ || ((type == Type::IncludeRules)
+ && parser.extractXmlBool(includeAttrib, QStringLiteral("includeAttrib")))
+ || ((type == Type::Float
+ || type == Type::HlCHex
+ || type == Type::HlCOct
+ || type == Type::Int
+ || type == Type::keyword
+ || type == Type::WordDetect
+ ) && (parser.extractString(additionalDeliminator, QStringLiteral("additionalDeliminator"))
+ || parser.extractString(weakDeliminator, QStringLiteral("weakDeliminator"))))
+ ;
+ // clang-format on
+
+ success = parser.checkIfExtracted(isExtracted);
+
+ if (type == Type::LineContinue && char0 == QLatin1Char('\0')) {
+ char0 = QLatin1Char('\\');
+ }
+ }
+
+ return success;
+ }
+
+ bool checkMandoryAttributes(const QString &filename, QXmlStreamReader &xml)
+ {
+ QString missingAttr;
+
+ switch (type) {
+ case Type::Unknown:
+ return false;
+
+ case Type::AnyChar:
+ case Type::RegExpr:
+ case Type::StringDetect:
+ case Type::WordDetect:
+ case Type::keyword:
+ missingAttr = string.isEmpty() ? QStringLiteral("String") : QString();
+ break;
+
+ case Type::DetectChar:
+ missingAttr = !char0.unicode() ? QStringLiteral("char") : QString();
+ break;
+
+ case Type::Detect2Chars:
+ case Type::RangeDetect:
+ missingAttr = !char0.unicode() && !char1.unicode() ? QStringLiteral("char and char1")
+ : !char0.unicode() ? QStringLiteral("char")
+ : !char1.unicode() ? QStringLiteral("char1")
+ : QString();
+ break;
+
+ case Type::IncludeRules:
+ missingAttr = context.name.isEmpty() ? QStringLiteral("context") : QString();
+ break;
+
+ case Type::DetectIdentifier:
+ case Type::DetectSpaces:
+ case Type::Float:
+ case Type::HlCChar:
+ case Type::HlCHex:
+ case Type::HlCOct:
+ case Type::HlCStringChar:
+ case Type::Int:
+ case Type::LineContinue:
+ break;
+ }
+
+ if (!missingAttr.isEmpty()) {
+ qWarning() << filename << "line" << xml.lineNumber() << "missing attribute:" << missingAttr;
+ return false;
+ }
+
+ return true;
+ }
+ };
+
+ int line;
+ // becomes false when a context (except includeRule) refers to it
+ bool isOnlyIncluded = true;
+ // becomes true when an includedRule refers to it with includeAttrib=true
+ bool referencedWithIncludeAttrib = false;
+ bool hasDynamicRule = false;
+ QString name;
+ QString attribute;
+ ContextName lineEndContext;
+ ContextName lineEmptyContext;
+ ContextName fallthroughContext;
+ QList<Rule> rules;
+ XmlBool dynamic{};
+ XmlBool fallthrough{};
+ XmlBool stopEmptyLineContextSwitchLoop{};
+
+ bool parseElement(const QString &filename, QXmlStreamReader &xml)
+ {
+ line = xml.lineNumber();
+
+ bool success = true;
+
+ for (auto &attr : xml.attributes()) {
+ Parser parser{filename, xml, attr, success};
+ XmlBool noIndentationBasedFolding{};
+
+ // clang-format off
+ const bool isExtracted = parser.extractString(name, QStringLiteral("name"))
+ || parser.extractString(attribute, QStringLiteral("attribute"))
+ || parser.extractString(lineEndContext.name, QStringLiteral("lineEndContext"))
+ || parser.extractString(lineEmptyContext.name, QStringLiteral("lineEmptyContext"))
+ || parser.extractString(fallthroughContext.name, QStringLiteral("fallthroughContext"))
+ || parser.extractXmlBool(dynamic, QStringLiteral("dynamic"))
+ || parser.extractXmlBool(fallthrough, QStringLiteral("fallthrough"))
+ || parser.extractXmlBool(stopEmptyLineContextSwitchLoop, QStringLiteral("stopEmptyLineContextSwitchLoop"))
+ || parser.extractXmlBool(noIndentationBasedFolding, QStringLiteral("noIndentationBasedFolding"));
+ // clang-format on
+
+ success = parser.checkIfExtracted(isExtracted);
+ }
+
+ if (name.isEmpty()) {
+ qWarning() << filename << "line" << xml.lineNumber() << "missing attribute: name";
+ success = false;
+ }
+
+ if (attribute.isEmpty()) {
+ qWarning() << filename << "line" << xml.lineNumber() << "missing attribute: attribute";
+ success = false;
+ }
+
+ return success;
+ }
+ };
+
+ struct Version {
+ int majorRevision;
+ int minorRevision;
+
+ Version(int majorRevision = 0, int minorRevision = 0)
+ : majorRevision(majorRevision)
+ , minorRevision(minorRevision)
+ {
+ }
+
+ bool operator<(const Version &version) const
+ {
+ return majorRevision < version.majorRevision || (majorRevision == version.majorRevision && minorRevision < version.minorRevision);
+ }
+ };
+
+ struct ItemDatas {
+ struct Style {
+ QString name;
+ int line;
+
+ friend size_t qHash(const Style &style, size_t seed = 0)
+ {
+ return qHash(style.name, seed);
+ }
+
+ friend bool operator==(const Style &style0, const Style &style1)
+ {
+ return style0.name == style1.name;
+ }
+ };
+
+ QSet<Style> styleNames;
+
+ bool parseElement(const QString &filename, QXmlStreamReader &xml)
+ {
+ bool success = true;
+
+ QString name;
+ QString defStyleNum;
+ XmlBool boolean;
+
+ for (auto &attr : xml.attributes()) {
+ Parser parser{filename, xml, attr, success};
+
+ const bool isExtracted = parser.extractString(name, QStringLiteral("name")) || parser.extractString(defStyleNum, QStringLiteral("defStyleNum"))
+ || parser.extractXmlBool(boolean, QStringLiteral("bold")) || parser.extractXmlBool(boolean, QStringLiteral("italic"))
+ || parser.extractXmlBool(boolean, QStringLiteral("underline")) || parser.extractXmlBool(boolean, QStringLiteral("strikeOut"))
+ || parser.extractXmlBool(boolean, QStringLiteral("spellChecking")) || parser.checkColor(QStringLiteral("color"))
+ || parser.checkColor(QStringLiteral("selColor")) || parser.checkColor(QStringLiteral("backgroundColor"))
+ || parser.checkColor(QStringLiteral("selBackgroundColor"));
+
+ success = parser.checkIfExtracted(isExtracted);
+ }
+
+ if (!name.isEmpty()) {
+ const auto len = styleNames.size();
+ styleNames.insert({name, int(xml.lineNumber())});
+ if (len == styleNames.size()) {
+ qWarning() << filename << "line" << xml.lineNumber() << "itemData duplicate:" << name;
+ success = false;
+ }
+ }
+
+ return success;
}
+ };
+
+ struct Definition {
+ QMap<QString, Keywords> keywordsList;
+ QMap<QString, Context> contexts;
+ ItemDatas itemDatas;
+ QString firstContextName;
+ const Context *firstContext = nullptr;
+ QString filename;
+ WordDelimiters wordDelimiters;
+ Version kateVersion{};
+ QString kateVersionStr;
+ QString languageName;
+ QSet<const Definition *> referencedDefinitions;
+
+ // Parse <keywords ...>
+ bool parseKeywords(QXmlStreamReader &xml)
+ {
+ wordDelimiters.append(xml.attributes().value(QStringLiteral("additionalDeliminator")));
+ wordDelimiters.remove(xml.attributes().value(QStringLiteral("weakDeliminator")));
+ return true;
+ }
+ };
+
+ // Parse <context>
+ void processContextElement(QXmlStreamReader &xml)
+ {
+ Context context;
+ m_success = context.parseElement(m_currentDefinition->filename, xml) && m_success;
+ if (m_currentDefinition->firstContextName.isEmpty()) {
+ m_currentDefinition->firstContextName = context.name;
+ }
+ if (m_currentDefinition->contexts.contains(context.name)) {
+ qWarning() << m_currentDefinition->filename << "line" << xml.lineNumber() << "duplicate context:" << context.name;
+ m_success = false;
+ }
+ m_currentContext = &*m_currentDefinition->contexts.insert(context.name, context);
}
- if (testChar1) {
- const QString c = xml.attributes().value(QLatin1String("char1")).toString();
- if (c.size() != 1) {
- qWarning() << hlFilename << "line" << xml.lineNumber() << "'char1' must contain exactly one char:" << c;
+ // Parse <list name="...">
+ void processListElement(QXmlStreamReader &xml)
+ {
+ Keywords keywords;
+ m_success = keywords.parseElement(m_currentDefinition->filename, xml) && m_success;
+ if (m_currentDefinition->keywordsList.contains(keywords.name)) {
+ qWarning() << m_currentDefinition->filename << "line" << xml.lineNumber() << "duplicate list:" << keywords.name;
+ m_success = false;
}
+ m_currentKeywords = &*m_currentDefinition->keywordsList.insert(keywords.name, keywords);
}
- return true;
-}
+ const Definition *maxKateVersionDefinition(const Definition &definition, QMap<const Definition *, const Definition *> &maxVersionByDefinitions) const
+ {
+ auto it = maxVersionByDefinitions.find(&definition);
+ if (it != maxVersionByDefinitions.end()) {
+ return it.value();
+ } else {
+ auto it = maxVersionByDefinitions.insert(&definition, &definition);
+ for (const auto &referencedDef : definition.referencedDefinitions) {
+ auto *maxDef = maxKateVersionDefinition(*referencedDef, maxVersionByDefinitions);
+ if (it.value()->kateVersion < maxDef->kateVersion) {
+ it.value() = maxDef;
+ }
+ }
+ return it.value();
+ }
+ }
-//! Search for rules with lookAhead="true" and context="#stay".
-//! This would cause an infinite loop.
-bool checkLookAhead(const QString &hlFilename, QXmlStreamReader &xml)
-{
- if (xml.attributes().hasAttribute(QStringLiteral("lookAhead"))) {
- auto lookAhead = xml.attributes().value(QStringLiteral("lookAhead"));
- if (lookAhead == QStringLiteral("true")) {
- auto context = xml.attributes().value(QStringLiteral("context"));
- if (context == QStringLiteral("#stay")) {
- qWarning() << hlFilename << "line" << xml.lineNumber() << "Infinite loop: lookAhead with context #stay";
- return false;
+ // Initialize the referenced rules (Rule::includedRules)
+ void resolveIncludeRules()
+ {
+ QSet<const Context *> usedContexts;
+ QList<const Context *> contexts;
+
+ QMutableMapIterator<QString, Definition> def(m_definitions);
+ while (def.hasNext()) {
+ def.next();
+ auto &definition = def.value();
+ QMutableMapIterator<QString, Context> contextIt(definition.contexts);
+ while (contextIt.hasNext()) {
+ contextIt.next();
+ auto &currentContext = contextIt.value();
+ for (auto &rule : currentContext.rules) {
+ if (rule.type != Context::Rule::Type::IncludeRules) {
+ continue;
+ }
+
+ if (rule.context.stay) {
+ qWarning() << definition.filename << "line" << rule.line << "IncludeRules refers to himself";
+ m_success = false;
+ continue;
+ }
+
+ if (rule.context.popCount) {
+ qWarning() << definition.filename << "line" << rule.line << "IncludeRules with #pop prefix";
+ m_success = false;
+ }
+
+ if (!rule.context.context) {
+ m_success = false;
+ continue;
+ }
+
+ // resolve includedRules and includedIncludeRules
+
+ usedContexts.clear();
+ usedContexts.insert(rule.context.context);
+ contexts.clear();
+ contexts.append(rule.context.context);
+
+ for (int i = 0; i < contexts.size(); ++i) {
+ currentContext.hasDynamicRule = contexts[i]->hasDynamicRule;
+ for (const auto &includedRule : contexts[i]->rules) {
+ if (includedRule.type != Context::Rule::Type::IncludeRules) {
+ rule.includedRules.append(&includedRule);
+ } else if (&rule == &includedRule) {
+ qWarning() << definition.filename << "line" << rule.line << "IncludeRules refers to himself by recursivity";
+ m_success = false;
+ } else {
+ rule.includedIncludeRules.insert(&includedRule);
+
+ if (includedRule.includedRules.isEmpty()) {
+ const auto *context = includedRule.context.context;
+ if (context && !usedContexts.contains(context)) {
+ contexts.append(context);
+ usedContexts.insert(context);
+ }
+ } else {
+ rule.includedRules.append(includedRule.includedRules);
+ }
+ }
+ }
+ }
+ }
}
}
}
- return true;
-}
-/**
- * Helper class to search for non-existing keyword include.
- */
-class KeywordIncludeChecker
-{
-public:
- void processElement(const QString &hlFilename, const QString &hlName, QXmlStreamReader &xml)
+ //! Recursively extracts the contexts used from the first context of the definitions.
+ //! This method detects groups of contexts which are only used among themselves.
+ QSet<const Context *> extractUsedContexts() const
{
- if (xml.name() == QLatin1String("list")) {
- auto &keywords = m_keywordMap[hlName];
- keywords.filename = hlFilename;
- auto name = xml.attributes().value(QLatin1String("name")).toString();
- m_currentIncludes = &keywords.includes[name];
- }
- else if (xml.name() == QLatin1String("include")) {
- if (!m_currentIncludes) {
- qWarning() << hlFilename << "line" << xml.lineNumber() << "<include> tag ouside <list>";
- m_success = false;
- } else {
- m_currentIncludes->push_back({xml.lineNumber(), xml.readElementText()});
- }
- }
+ QSet<const Context *> usedContexts;
+ QList<const Context *> contexts;
+
+ QMapIterator<QString, Definition> def(m_definitions);
+ while (def.hasNext()) {
+ def.next();
+ const auto &definition = def.value();
+
+ if (definition.firstContext) {
+ usedContexts.insert(definition.firstContext);
+ contexts.clear();
+ contexts.append(definition.firstContext);
+
+ for (int i = 0; i < contexts.size(); ++i) {
+ auto appendContext = [&](const Context *context) {
+ if (context && !usedContexts.contains(context)) {
+ contexts.append(context);
+ usedContexts.insert(context);
+ }
+ };
+
+ const auto *context = contexts[i];
+ appendContext(context->lineEndContext.context);
+ appendContext(context->lineEmptyContext.context);
+ appendContext(context->fallthroughContext.context);
+
+ for (auto &rule : context->rules) {
+ appendContext(rule.context.context);
+ }
+ }
+ }
+ }
+
+ return usedContexts;
}
- bool check() const
+ struct RuleAndInclude {
+ const Context::Rule *rule;
+ const Context::Rule *includeRules;
+
+ explicit operator bool() const
+ {
+ return rule;
+ }
+ };
+
+ struct IncludedRuleUnreachableBy {
+ QList<RuleAndInclude> unreachableBy;
+ bool alwaysUnreachable = true;
+ };
+
+ //! Check contexts and rules
+ bool checkContexts(const Definition &definition,
+ QSet<ItemDatas::Style> &usedAttributeNames,
+ QSet<ItemDatas::Style> &ignoredAttributeNames,
+ const QSet<const Context *> &usedContexts,
+ QMap<const Context::Rule *, IncludedRuleUnreachableBy> &unreachableIncludedRules) const
{
- bool success = m_success;
- for (auto &keywords : m_keywordMap) {
- QMapIterator<QString, QVector<Keywords::Include>> includes(keywords.includes);
- while (includes.hasNext()) {
- includes.next();
- for (auto &include : includes.value()) {
- bool containsKeywordName = true;
- int const idx = include.name.indexOf(QStringLiteral("##"));
- if (idx == -1) {
- auto &keywordName = includes.key();
- containsKeywordName = keywords.includes.contains(keywordName);
- }
- else {
- auto defName = include.name.mid(idx + 2);
- auto listName = include.name.left(idx);
- auto it = m_keywordMap.find(defName);
- if (it == m_keywordMap.end()) {
- qWarning() << keywords.filename << "line" << include.line << "unknown definition in" << include.name;
- success = false;
- } else {
- containsKeywordName = it->includes.contains(listName);
+ bool success = true;
+
+ QMapIterator<QString, Context> contextIt(definition.contexts);
+ while (contextIt.hasNext()) {
+ contextIt.next();
+
+ const auto &context = contextIt.value();
+ const auto &filename = definition.filename;
+
+ if (!usedContexts.contains(&context)) {
+ qWarning() << filename << "line" << context.line << "unused context:" << context.name;
+ success = false;
+ continue;
+ }
+
+ if (context.name.startsWith(QStringLiteral("#pop"))) {
+ qWarning() << filename << "line" << context.line << "the context name must not start with '#pop':" << context.name;
+ success = false;
+ }
+
+ if (!context.attribute.isEmpty() && (!context.isOnlyIncluded || context.referencedWithIncludeAttrib)) {
+ usedAttributeNames.insert({context.attribute, context.line});
+ }
+
+ success = checkContextAttribute(definition, context) && success;
+ success = checkUreachableRules(definition.filename, context, unreachableIncludedRules) && success;
+ success = suggestRuleMerger(definition.filename, context) && success;
+
+ for (const auto &rule : context.rules) {
+ if (!rule.attribute.isEmpty()) {
+ if (rule.lookAhead != XmlBool::True) {
+ usedAttributeNames.insert({rule.attribute, rule.line});
+ } else {
+ ignoredAttributeNames.insert({rule.attribute, rule.line});
+ }
+ }
+ success = checkLookAhead(rule) && success;
+ success = checkStringDetect(rule) && success;
+ success = checkKeyword(definition, rule) && success;
+ success = checkRegExpr(filename, rule, context) && success;
+ success = checkDelimiters(definition, rule) && success;
+ }
+ }
+
+ return success;
+ }
+
+ //! Check that a regular expression in a RegExpr rule:
+ //! - isValid()
+ //! - character ranges such as [A-Z] are valid and not accidentally e.g. [A-z].
+ //! - dynamic=true but no place holder used?
+ //! - is not . with lookAhead="1"
+ //! - is not ^... without column ou firstNonSpace attribute
+ //! - is not equivalent to DetectSpaces, DetectChar, Detect2Chars, StringDetect, DetectIdentifier, RangeDetect, LineContinue or AnyChar
+ //! - has no unused captures
+ //! - has no unnecessary quantifier with lookAhead
+ bool checkRegExpr(const QString &filename, const Context::Rule &rule, const Context &context) const
+ {
+ // ignore empty regex because the error is raised during xml parsing
+ if (rule.type == Context::Rule::Type::RegExpr && !rule.string.isEmpty()) {
+ const QRegularExpression regexp(rule.string);
+ if (!checkRegularExpression(rule.filename, regexp, rule.line)) {
+ return false;
+ }
+
+ // dynamic == true and no place holder?
+ if (rule.dynamic == XmlBool::True) {
+ static const QRegularExpression placeHolder(QStringLiteral("%\\d+"));
+ if (!rule.string.contains(placeHolder)) {
+ qWarning() << rule.filename << "line" << rule.line << "broken regex:" << rule.string << "problem: dynamic=true but no %\\d+ placeholder";
+ return false;
+ }
+ }
+
+ auto reg = (rule.lookAhead == XmlBool::True) ? rule.sanitizedString : rule.string;
+ if (rule.lookAhead == XmlBool::True) {
+ static const QRegularExpression removeAllSuffix(QStringLiteral(
+ R"(((?<!\\)\\(?:[DSWdsw]|x[0-9a-fA-F]{2}|x\{[0-9a-fA-F]+\}|0\d\d|o\{[0-7]+\}|u[0-9a-fA-F]{4})|(?<!\\)[^])}\\]|(?=\\)\\\\)[*][?+]?$)"));
+ reg.replace(removeAllSuffix, QString());
+ }
+
+ reg.replace(QStringLiteral("{1}"), QString());
+
+ // is DetectSpaces
+ // optional ^ then \s, [\s], [\t ], [ \t] possibly in (...) or (?:...) followed by *, +
+ static const QRegularExpression isDetectSpaces(
+ QStringLiteral(R"(^\^?(?:\((?:\?:)?)?\^?(?:\\s|\[(?:\\s| (?:\t|\\t)|(?:\t|\\t) )\])\)?(?:[*+][*+?]?|[*+])?\)?\)?$)"));
+ if (rule.string.contains(isDetectSpaces)) {
+ char const *extraMsg = rule.string.contains(QLatin1Char('^')) ? "+ column=\"0\" or firstNonSpace=\"1\"" : "";
+ qWarning() << rule.filename << "line" << rule.line << "RegExpr should be replaced by DetectSpaces / DetectChar / AnyChar" << extraMsg << ":"
+ << rule.string;
+ return false;
+ }
+
+#define REG_ESCAPE_CHAR R"(\\(?:[^0BDPSWbdpswoux]|x[0-9a-fA-F]{2}|x\{[0-9a-fA-F]+\}|0\d\d|o\{[0-7]+\}|u[0-9a-fA-F]{4}))"
+#define REG_CHAR "(?:" REG_ESCAPE_CHAR "|\\[(?:" REG_ESCAPE_CHAR "|.)\\]|[^[.^])"
+
+ // is RangeDetect
+ static const QRegularExpression isRange(QStringLiteral("^\\^?" REG_CHAR "(?:"
+ "\\.\\*[?+]?" REG_CHAR "|"
+ "\\[\\^(" REG_ESCAPE_CHAR "|.)\\]\\*[?+]?\\1"
+ ")$"));
+ if ((rule.lookAhead == XmlBool::True || rule.minimal == XmlBool::True || rule.string.contains(QStringLiteral(".*?"))
+ || rule.string.contains(QStringLiteral("[^")))
+ && reg.contains(isRange)) {
+ qWarning() << rule.filename << "line" << rule.line << "RegExpr should be replaced by RangeDetect:" << rule.string;
+ return false;
+ }
+
+ // is AnyChar
+ static const QRegularExpression isAnyChar(QStringLiteral(R"(^(\^|\((\?:)?)*\[(?!\^)[-\]]?(\\[^0BDPSWbdpswoux]|[^-\]\\])*\]\)*$)"));
+ if (rule.string.contains(isAnyChar)) {
+ auto extra = (reg[0] == QLatin1Char('^') || reg[1] == QLatin1Char('^')) ? "with column=\"0\"" : "";
+ qWarning() << rule.filename << "line" << rule.line << "RegExpr should be replaced by AnyChar:" << rule.string << extra;
+ return false;
+ }
+
+ // is LineContinue
+ static const QRegularExpression isLineContinue(QStringLiteral("^\\^?" REG_CHAR "\\$$"));
+ if (reg.contains(isLineContinue)) {
+ auto extra = (reg[0] == QLatin1Char('^')) ? "with column=\"0\"" : "";
+ qWarning() << rule.filename << "line" << rule.line << "RegExpr should be replaced by LineContinue:" << rule.string << extra;
+ return false;
+ }
+
+ // replace \c, \xhhh, \x{hhh...}, \0dd, \o{ddd}, \uhhhh, with _
+ static const QRegularExpression sanitize1(QStringLiteral(REG_ESCAPE_CHAR));
+ reg.replace(sanitize1, QStringLiteral("_"));
+
+#undef REG_CHAR
+#undef REG_ESCAPE_CHAR
+
+ // use minimal or lazy operator
+ static const QRegularExpression isMinimal(QStringLiteral("(?![.][*+?][$]?[)]*$)[.][*+?][^?+]"));
+ static const QRegularExpression hasNotGreedy(QStringLiteral("[*+?][?+]"));
+
+ if (rule.lookAhead == XmlBool::True && rule.minimal != XmlBool::True && reg.contains(isMinimal) && !reg.contains(hasNotGreedy)
+ && (!rule.context.context || !rule.context.context->hasDynamicRule || regexp.captureCount() == 0)
+ && (reg.back() != QLatin1Char('$') || reg.contains(QLatin1Char('|')))) {
+ qWarning() << rule.filename << "line" << rule.line
+ << "RegExpr should be have minimal=\"1\" or use lazy operator (i.g, '.*' -> '.*?'):" << rule.string;
+ return false;
+ }
+
+ // replace [:...:] with ___
+ static const QRegularExpression sanitize2(QStringLiteral(R"(\[:\w+:\])"));
+ reg.replace(sanitize2, QStringLiteral("___"));
+
+ // replace [ccc...], [special] with ...
+ static const QRegularExpression sanitize3(QStringLiteral(R"(\[(?:\^\]?[^]]*|\]?[^]\\]*?\\.[^]]*|\][^]]{2,}|[^]]{3,})\]|(\[\]?[^]]*\]))"));
+ reg.replace(sanitize3, QStringLiteral("...\\1"));
+
+ // replace [c] with _
+ static const QRegularExpression sanitize4(QStringLiteral(R"(\[.\])"));
+ reg.replace(sanitize4, QStringLiteral("_"));
+
+ const int len = reg.size();
+ // replace [cC] with _
+ static const QRegularExpression toInsensitive(QStringLiteral(R"(\[(?:([^]])\1)\])"));
+ reg = reg.toUpper();
+ reg.replace(toInsensitive, QString());
+
+ // is StringDetect
+ // ignore (?:, ) and {n}
+ static const QRegularExpression isStringDetect(QStringLiteral(R"(^\^?(?:[^|\\?*+$^[{(.]|{(?!\d+,\d*}|,\d+})|\(\?:)+$)"));
+ if (reg.contains(isStringDetect)) {
+ char const *extraMsg = rule.string.contains(QLatin1Char('^')) ? "+ column=\"0\" or firstNonSpace=\"1\"" : "";
+ qWarning() << rule.filename << "line" << rule.line << "RegExpr should be replaced by StringDetect / Detect2Chars / DetectChar" << extraMsg
+ << ":" << rule.string;
+ if (len != reg.size()) {
+ qWarning() << rule.filename << "line" << rule.line << "insensitive=\"1\" missing:" << rule.string;
+ }
+ return false;
+ }
+
+ // column="0"
+ if (rule.column == -1) {
+ // ^ without |
+ // (^sas*) -> ok
+ // (^sa|s*) -> ko
+ // (^(sa|s*)) -> ok
+ auto first = std::as_const(reg).begin();
+ auto last = std::as_const(reg).end();
+ int depth = 0;
+
+ while (QLatin1Char('(') == *first) {
+ ++depth;
+ ++first;
+ if (QLatin1Char('?') == *first || QLatin1Char(':') == first[1]) {
+ first += 2;
+ }
+ }
+
+ if (QLatin1Char('^') == *first) {
+ const int bolDepth = depth;
+ bool replace = true;
+
+ while (++first != last) {
+ if (QLatin1Char('(') == *first) {
+ ++depth;
+ } else if (QLatin1Char(')') == *first) {
+ --depth;
+ if (depth < bolDepth) {
+ // (^a)? === (^a|) -> ko
+ if (first + 1 != last && QStringLiteral("*?").contains(first[1])) {
+ replace = false;
+ break;
+ }
+ }
+ } else if (QLatin1Char('|') == *first) {
+ // ignore '|' within subgroup
+ if (depth <= bolDepth) {
+ replace = false;
+ break;
+ }
}
}
- if (!containsKeywordName) {
- qWarning() << keywords.filename << "line" << include.line << "unknown keyword name in" << include.name;
- success = false;
+ if (replace) {
+ qWarning() << rule.filename << "line" << rule.line << "column=\"0\" missing with RegExpr:" << rule.string;
+ return false;
+ }
+ }
+ }
+
+ // add ^ with column=0
+ if (rule.column == 0 && !rule.isDotRegex) {
+ bool hasStartOfLine = false;
+ auto first = std::as_const(reg).begin();
+ auto last = std::as_const(reg).end();
+ for (; first != last; ++first) {
+ if (*first == QLatin1Char('^')) {
+ hasStartOfLine = true;
+ break;
+ } else if (*first == QLatin1Char('(')) {
+ if (last - first >= 3 && first[1] == QLatin1Char('?') && first[2] == QLatin1Char(':')) {
+ first += 2;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (!hasStartOfLine) {
+ qWarning() << rule.filename << "line" << rule.line
+ << "start of line missing in the pattern with column=\"0\" (i.e. abc -> ^abc):" << rule.string;
+ return false;
+ }
+ }
+
+ bool useCapture = false;
+
+ // detection of unnecessary capture
+ if (regexp.captureCount()) {
+ auto maximalCapture = [](const QString(&referenceNames)[9], const QString &s) {
+ int maxCapture = 9;
+ while (maxCapture && !s.contains(referenceNames[maxCapture - 1])) {
+ --maxCapture;
+ }
+ return maxCapture;
+ };
+
+ int maxCaptureUsed = 0;
+ // maximal dynamic reference
+ if (rule.context.context && !rule.context.stay) {
+ for (const auto &nextRule : rule.context.context->rules) {
+ if (nextRule.dynamic == XmlBool::True) {
+ static const QString cap[]{
+ QStringLiteral("%1"),
+ QStringLiteral("%2"),
+ QStringLiteral("%3"),
+ QStringLiteral("%4"),
+ QStringLiteral("%5"),
+ QStringLiteral("%6"),
+ QStringLiteral("%7"),
+ QStringLiteral("%8"),
+ QStringLiteral("%9"),
+ };
+ int maxDynamicCapture = maximalCapture(cap, nextRule.string);
+ maxCaptureUsed = std::max(maxCaptureUsed, maxDynamicCapture);
+ }
+ }
+ }
+
+ static const QString num1[]{
+ QStringLiteral("\\1"),
+ QStringLiteral("\\2"),
+ QStringLiteral("\\3"),
+ QStringLiteral("\\4"),
+ QStringLiteral("\\5"),
+ QStringLiteral("\\6"),
+ QStringLiteral("\\7"),
+ QStringLiteral("\\8"),
+ QStringLiteral("\\9"),
+ };
+ static const QString num2[]{
+ QStringLiteral("\\g1"),
+ QStringLiteral("\\g2"),
+ QStringLiteral("\\g3"),
+ QStringLiteral("\\g4"),
+ QStringLiteral("\\g5"),
+ QStringLiteral("\\g6"),
+ QStringLiteral("\\g7"),
+ QStringLiteral("\\g8"),
+ QStringLiteral("\\g9"),
+ };
+ const int maxBackReference = std::max(maximalCapture(num1, rule.string), maximalCapture(num1, rule.string));
+
+ const int maxCapture = std::max(maxCaptureUsed, maxBackReference);
+
+ if (maxCapture && regexp.captureCount() > maxCapture) {
+ qWarning() << rule.filename << "line" << rule.line << "RegExpr with" << regexp.captureCount() << "captures but only" << maxCapture
+ << "are used. Please, replace '(...)' with '(?:...)':" << rule.string;
+ return false;
+ }
+
+ useCapture = maxCapture;
+ }
+
+ if (!useCapture) {
+ // is DetectIdentifier
+ static const QRegularExpression isDetectIdentifier(
+ QStringLiteral(R"(^(\((\?:)?|\^)*\[(\\p\{L\}|_){2}\]([+][?+]?)?\[(\\p\{N\}|\\p\{L\}|_){3}\][*][?+]?\)*$)"));
+ if (rule.string.contains(isDetectIdentifier)) {
+ qWarning() << rule.filename << "line" << rule.line << "RegExpr should be replaced by DetectIdentifier:" << rule.string;
+ return false;
+ }
+ }
+
+ if (rule.isDotRegex) {
+ // search next rule with same column or firstNonSpace
+ int i = &rule - context.rules.data() + 1;
+ const bool hasColumn = (rule.column != -1);
+ const bool hasFirstNonSpace = (rule.firstNonSpace == XmlBool::True);
+ const bool isSpecial = (hasColumn || hasFirstNonSpace);
+ for (; i < context.rules.size(); ++i) {
+ auto &rule2 = context.rules[i];
+ if (rule2.type == Context::Rule::Type::IncludeRules && isSpecial) {
+ i = context.rules.size();
+ break;
+ }
+
+ const bool hasColumn2 = (rule2.column != -1);
+ const bool hasFirstNonSpace2 = (rule2.firstNonSpace == XmlBool::True);
+ if ((!isSpecial && !hasColumn2 && !hasFirstNonSpace2) || (hasColumn && rule.column == rule2.column)
+ || (hasFirstNonSpace && hasFirstNonSpace2)) {
+ break;
+ }
+ }
+
+ auto ruleFilename = (filename == rule.filename) ? QString() : QStringLiteral("in ") + rule.filename;
+ if (i == context.rules.size()) {
+ if (rule.lookAhead == XmlBool::True && rule.firstNonSpace != XmlBool::True && rule.column == -1 && rule.beginRegion.isEmpty()
+ && rule.endRegion.isEmpty() && !useCapture) {
+ qWarning() << filename << "context line" << context.line << ": RegExpr line" << rule.line << ruleFilename
+ << "should be replaced by fallthroughContext:" << rule.string;
}
+ } else {
+ auto &nextRule = context.rules[i];
+ auto nextRuleFilename = (filename == nextRule.filename) ? QString() : QStringLiteral("in ") + nextRule.filename;
+ qWarning() << filename << "context line" << context.line << "contains unreachable element line" << nextRule.line << nextRuleFilename
+ << "because a dot RegExpr is used line" << rule.line << ruleFilename;
+ }
+
+ // unnecessary quantifier
+ static const QRegularExpression unnecessaryQuantifier1(QStringLiteral(R"([*+?]([.][*+?]{0,2})?$)"));
+ static const QRegularExpression unnecessaryQuantifier2(QStringLiteral(R"([*+?]([.][*+?]{0,2})?[)]*$)"));
+ auto &unnecessaryQuantifier = useCapture ? unnecessaryQuantifier1 : unnecessaryQuantifier2;
+ if (rule.lookAhead == XmlBool::True && rule.minimal != XmlBool::True && reg.contains(unnecessaryQuantifier)) {
+ qWarning() << rule.filename << "line" << rule.line
+ << "Last quantifier is not necessary (i.g., 'xyz*' -> 'xy', 'xyz+.' -> 'xyz.'):" << rule.string;
+ return false;
}
}
}
+
+ return true;
+ }
+
+ // Parse and check <emptyLine>
+ bool parseEmptyLine(const QString &filename, QXmlStreamReader &xml)
+ {
+ bool success = true;
+
+ QString pattern;
+ XmlBool casesensitive{};
+
+ for (auto &attr : xml.attributes()) {
+ Parser parser{filename, xml, attr, success};
+
+ const bool isExtracted =
+ parser.extractString(pattern, QStringLiteral("regexpr")) || parser.extractXmlBool(casesensitive, QStringLiteral("casesensitive"));
+
+ success = parser.checkIfExtracted(isExtracted);
+ }
+
+ if (pattern.isEmpty()) {
+ qWarning() << filename << "line" << xml.lineNumber() << "missing attribute: regexpr";
+ success = false;
+ } else {
+ success = checkRegularExpression(filename, QRegularExpression(pattern), xml.lineNumber());
+ }
+
return success;
}
-private:
- struct Keywords
+ //! Check that a regular expression:
+ //! - isValid()
+ //! - character ranges such as [A-Z] are valid and not accidentally e.g. [A-z].
+ bool checkRegularExpression(const QString &filename, const QRegularExpression &regexp, int line) const
{
- QString filename;
- struct Include
- {
- qint64 line;
- QString name;
- };
- QMap<QString, QVector<Include>> includes;
- };
- QHash<QString, Keywords> m_keywordMap;
- QVector<Keywords::Include> *m_currentIncludes = nullptr;
- bool m_success = true;
-};
+ const auto pattern = regexp.pattern();
-/**
- * Helper class to search for non-existing or unreferenced keyword lists.
- */
-class KeywordChecker
-{
-public:
- KeywordChecker(const QString &filename)
- : m_filename(filename)
- {}
+ // validate regexp
+ if (!regexp.isValid()) {
+ qWarning() << filename << "line" << line << "broken regex:" << pattern << "problem:" << regexp.errorString() << "at offset"
+ << regexp.patternErrorOffset();
+ return false;
+ }
- void processElement(QXmlStreamReader &xml)
+ // catch possible case typos: [A-z] or [a-Z]
+ const int azOffset = std::max(pattern.indexOf(QStringLiteral("A-z")), pattern.indexOf(QStringLiteral("a-Z")));
+ if (azOffset >= 0) {
+ qWarning() << filename << "line" << line << "broken regex:" << pattern << "problem: [a-Z] or [A-z] at offset" << azOffset;
+ return false;
+ }
+
+ return true;
+ }
+
+ //! Check fallthrough and fallthroughContext.
+ //! Check kateversion for stopEmptyLineContextSwitchLoop.
+ bool checkContextAttribute(const Definition &definition, const Context &context) const
{
- if (xml.name() == QLatin1String("list")) {
- const QString name = xml.attributes().value(QLatin1String("name")).toString();
- if (m_existingNames.contains(name)) {
- qWarning() << m_filename << "list duplicate:" << name;
+ bool success = true;
+
+ if (!context.fallthroughContext.name.isEmpty()) {
+ const bool mandatoryFallthroughAttribute = definition.kateVersion < Version{5, 62};
+ if (context.fallthrough == XmlBool::True && !mandatoryFallthroughAttribute) {
+ qWarning() << definition.filename << "line" << context.line << "fallthrough attribute is unnecessary with kateversion >= 5.62 in context"
+ << context.name;
+ success = false;
+ } else if (context.fallthrough != XmlBool::True && mandatoryFallthroughAttribute) {
+ qWarning() << definition.filename << "line" << context.line
+ << "fallthroughContext attribute without fallthrough=\"1\" attribute is only valid with kateversion >= 5.62 in context"
+ << context.name;
+ success = false;
}
- m_existingNames.insert(name);
- } else if (xml.name() == QLatin1String("keyword")) {
- const QString context = xml.attributes().value(QLatin1String("String")).toString();
- if (!context.isEmpty())
- m_usedNames.insert(context);
}
+
+ if (context.stopEmptyLineContextSwitchLoop != XmlBool::Unspecified && definition.kateVersion < Version{5, 103}) {
+ qWarning() << definition.filename << "line" << context.line
+ << "stopEmptyLineContextSwitchLoop attribute is only valid with kateversion >= 5.103 in context" << context.name;
+ success = false;
+ }
+
+ return success;
}
- bool check() const
+ //! Search for additionalDeliminator/weakDeliminator which has no effect.
+ bool checkDelimiters(const Definition &definition, const Context::Rule &rule) const
{
+ if (rule.additionalDeliminator.isEmpty() && rule.weakDeliminator.isEmpty()) {
+ return true;
+ }
+
bool success = true;
- const auto invalidNames = m_usedNames - m_existingNames;
- if (!invalidNames.isEmpty()) {
- qWarning() << m_filename << "Reference of non-existing keyword list:" << invalidNames;
+
+ if (definition.kateVersion < Version{5, 79}) {
+ qWarning() << definition.filename << "line" << rule.line
+ << "additionalDeliminator and weakDeliminator are only available since version \"5.79\". Please, increase kateversion.";
success = false;
}
- const auto unusedNames = m_existingNames - m_usedNames;
- if (!unusedNames.isEmpty()) {
- qWarning() << m_filename << "Unused keyword lists:" << unusedNames;
+ for (QChar c : rule.additionalDeliminator) {
+ if (!definition.wordDelimiters.contains(c)) {
+ return success;
+ }
}
- return success;
+ for (QChar c : rule.weakDeliminator) {
+ if (definition.wordDelimiters.contains(c)) {
+ return success;
+ }
+ }
+
+ qWarning() << rule.filename << "line" << rule.line << "unnecessary use of additionalDeliminator and/or weakDeliminator" << rule.string;
+ return false;
}
-private:
- QString m_filename;
- QSet<QString> m_usedNames;
- QSet<QString> m_existingNames;
-};
+ //! Check that keyword rule reference an existing keyword list.
+ bool checkKeyword(const Definition &definition, const Context::Rule &rule) const
+ {
+ if (rule.type == Context::Rule::Type::keyword) {
+ auto it = definition.keywordsList.find(rule.string);
+ if (it == definition.keywordsList.end()) {
+ qWarning() << rule.filename << "line" << rule.line << "reference of non-existing keyword list:" << rule.string;
+ return false;
+ }
+ }
+ return true;
+ }
-/**
- * Helper class to search for non-existing contexts
- */
-class ContextChecker
-{
-public:
- void processElement(const QString &hlFilename, const QString &hlName, QXmlStreamReader &xml)
+ //! Search for rules with lookAhead="true" and context="#stay".
+ //! This would cause an infinite loop.
+ bool checkLookAhead(const Context::Rule &rule) const
+ {
+ if (rule.lookAhead == XmlBool::True && rule.context.stay) {
+ qWarning() << rule.filename << "line" << rule.line << "infinite loop: lookAhead with context #stay";
+ }
+ return true;
+ }
+
+ //! Check that StringDetect contains a placeHolder when dynamic="1"
+ bool checkStringDetect(const Context::Rule &rule) const
{
- if (xml.name() == QLatin1String("context")) {
- auto & language = m_contextMap[hlName];
- language.hlFilename = hlFilename;
- const QString name = xml.attributes().value(QLatin1String("name")).toString();
- if (language.isFirstContext) {
- language.isFirstContext = false;
- language.usedContextNames.insert(name);
+ if (rule.type == Context::Rule::Type::StringDetect) {
+ // dynamic == true and no place holder?
+ if (rule.dynamic == XmlBool::True) {
+ static const QRegularExpression placeHolder(QStringLiteral("%\\d+"));
+ if (!rule.string.contains(placeHolder)) {
+ qWarning() << rule.filename << "line" << rule.line << "broken regex:" << rule.string << "problem: dynamic=true but no %\\d+ placeholder";
+ return false;
+ }
}
+ }
+ return true;
+ }
- if (language.existingContextNames.contains(name)) {
- qWarning() << hlFilename << "Duplicate context:" << name;
- } else {
- language.existingContextNames.insert(name);
+ //! Check \<include> and delimiter in a keyword list
+ bool checkKeywordsList(const Definition &definition) const
+ {
+ bool success = true;
+
+ bool includeNotSupport = (definition.kateVersion < Version{5, 53});
+ QMapIterator<QString, Keywords> keywordsIt(definition.keywordsList);
+ while (keywordsIt.hasNext()) {
+ keywordsIt.next();
+
+ for (const auto &include : keywordsIt.value().items.includes) {
+ if (includeNotSupport) {
+ qWarning() << definition.filename << "line" << include.line
+ << "<include> is only available since version \"5.53\". Please, increase kateversion.";
+ success = false;
+ }
+ success = checkKeywordInclude(definition, include) && success;
}
- if (xml.attributes().value(QLatin1String("fallthroughContext")).toString() == QLatin1String("#stay")) {
- qWarning() << hlFilename << "possible infinite loop due to fallthroughContext=\"#stay\" in context " << name;
+ // Check that keyword list items do not have deliminator character
+#if 0
+ for (const auto& keyword : keywordsIt.value().items.keywords) {
+ for (QChar c : keyword.content) {
+ if (definition.wordDelimiters.contains(c)) {
+ qWarning() << definition.filename << "line" << keyword.line << "keyword with delimiter:" << c << "in" << keyword.content;
+ success = false;
+ }
+ }
}
+#endif
+ }
+
+ return success;
+ }
- processContext(hlName, xml.attributes().value(QLatin1String("lineEndContext")).toString());
- processContext(hlName, xml.attributes().value(QLatin1String("lineEmptyContext")).toString());
- processContext(hlName, xml.attributes().value(QLatin1String("fallthroughContext")).toString());
+ //! Search for non-existing keyword include.
+ bool checkKeywordInclude(const Definition &definition, const Keywords::Items::Item &include) const
+ {
+ bool containsKeywordName = true;
+ int const idx = include.content.indexOf(QStringLiteral("##"));
+ if (idx == -1) {
+ auto it = definition.keywordsList.find(include.content);
+ containsKeywordName = (it != definition.keywordsList.end());
} else {
- if (xml.attributes().hasAttribute(QLatin1String("context"))) {
- const QString context = xml.attributes().value(QLatin1String("context")).toString();
- if (context.isEmpty()) {
- qWarning() << hlFilename << "Missing context name in line" << xml.lineNumber();
- } else {
- processContext(hlName, context);
- }
+ auto defName = include.content.mid(idx + 2);
+ auto listName = include.content.left(idx);
+ auto it = m_definitions.find(defName);
+ if (it == m_definitions.end()) {
+ qWarning() << definition.filename << "line" << include.line << "unknown definition in" << include.content;
+ return false;
}
+ containsKeywordName = it->keywordsList.contains(listName);
}
+
+ if (!containsKeywordName) {
+ qWarning() << definition.filename << "line" << include.line << "unknown keyword name in" << include.content;
+ }
+
+ return containsKeywordName;
}
- bool check() const
+ //! Check if a rule is hidden by another
+ //! - rule hidden by DetectChar or AnyChar
+ //! - DetectSpaces, AnyChar, Int, Float with all their characters hidden by DetectChar or AnyChar
+ //! - StringDetect, WordDetect, RegExpr with as prefix Detect2Chars or other strings
+ //! - duplicate rule (Int, Float, keyword with same String, etc)
+ //! - Rule hidden by a dot regex
+ bool checkUreachableRules(const QString &filename,
+ const Context &context,
+ QMap<const Context::Rule *, IncludedRuleUnreachableBy> &unreachableIncludedRules) const
{
+ if (context.isOnlyIncluded) {
+ return true;
+ }
+
+ struct Rule4 {
+ RuleAndInclude setRule(const Context::Rule &rule, const Context::Rule *includeRules = nullptr)
+ {
+ auto set = [&](RuleAndInclude &ruleAndInclude) {
+ auto old = ruleAndInclude;
+ ruleAndInclude = {&rule, includeRules};
+ return old;
+ };
+
+ if (rule.firstNonSpace == XmlBool::True) {
+ return set(firstNonSpace);
+ } else if (rule.column == 0) {
+ return set(column0);
+ } else if (rule.column > 0) {
+ return set(columnGreaterThan0[rule.column]);
+ } else {
+ return set(normal);
+ }
+ }
+
+ private:
+ RuleAndInclude normal;
+ RuleAndInclude column0;
+ QMap<int, RuleAndInclude> columnGreaterThan0;
+ RuleAndInclude firstNonSpace;
+ };
+
+ // Associate QChar with RuleAndInclude
+ struct CharTable {
+ /// Search RuleAndInclude associated with @p c.
+ RuleAndInclude find(QChar c) const
+ {
+ if (c.unicode() < 128) {
+ return m_asciiMap[c.unicode()];
+ }
+ auto it = m_utf8Map.find(c);
+ return it == m_utf8Map.end() ? RuleAndInclude{nullptr, nullptr} : it.value();
+ }
+
+ /// Search RuleAndInclude associated with the characters of @p s.
+ /// \return an empty QList when at least one character is not found.
+ QList<RuleAndInclude> find(QStringView s) const
+ {
+ QList<RuleAndInclude> result;
+
+ for (QChar c : s) {
+ if (!find(c)) {
+ return result;
+ }
+ }
+
+ for (QChar c : s) {
+ result.append(find(c));
+ }
+
+ return result;
+ }
+
+ /// Associates @p c with a rule.
+ void append(QChar c, const Context::Rule &rule, const Context::Rule *includeRule = nullptr)
+ {
+ if (c.unicode() < 128) {
+ m_asciiMap[c.unicode()] = {&rule, includeRule};
+ } else {
+ m_utf8Map[c] = {&rule, includeRule};
+ }
+ }
+
+ /// Associates each character of @p s with a rule.
+ void append(QStringView s, const Context::Rule &rule, const Context::Rule *includeRule = nullptr)
+ {
+ for (QChar c : s) {
+ append(c, rule, includeRule);
+ }
+ }
+
+ private:
+ RuleAndInclude m_asciiMap[127]{};
+ QMap<QChar, RuleAndInclude> m_utf8Map;
+ };
+
+ struct Char4Tables {
+ CharTable chars;
+ CharTable charsColumn0;
+ QMap<int, CharTable> charsColumnGreaterThan0;
+ CharTable charsFirstNonSpace;
+ };
+
+ // View on Char4Tables members
+ struct CharTableArray {
+ // Append Char4Tables members that satisfies firstNonSpace and column.
+ // Char4Tables::char is always added.
+ CharTableArray(Char4Tables &tables, const Context::Rule &rule)
+ {
+ if (rule.firstNonSpace == XmlBool::True) {
+ appendTable(tables.charsFirstNonSpace);
+ }
+
+ if (rule.column == 0) {
+ appendTable(tables.charsColumn0);
+ } else if (rule.column > 0) {
+ appendTable(tables.charsColumnGreaterThan0[rule.column]);
+ }
+
+ appendTable(tables.chars);
+ }
+
+ // Removes Char4Tables::chars when the rule contains firstNonSpace or column
+ void removeNonSpecialWhenSpecial()
+ {
+ if (m_size > 1) {
+ --m_size;
+ }
+ }
+
+ /// Search RuleAndInclude associated with @p c.
+ RuleAndInclude find(QChar c) const
+ {
+ for (int i = 0; i < m_size; ++i) {
+ if (auto ruleAndInclude = m_charTables[i]->find(c)) {
+ return ruleAndInclude;
+ }
+ }
+ return RuleAndInclude{nullptr, nullptr};
+ }
+
+ /// Search RuleAndInclude associated with the characters of @p s.
+ /// \return an empty QList when at least one character is not found.
+ QList<RuleAndInclude> find(QStringView s) const
+ {
+ for (int i = 0; i < m_size; ++i) {
+ auto result = m_charTables[i]->find(s);
+ if (result.size()) {
+ while (++i < m_size) {
+ result.append(m_charTables[i]->find(s));
+ }
+ return result;
+ }
+ }
+ return QList<RuleAndInclude>();
+ }
+
+ /// Associates @p c with a rule.
+ void append(QChar c, const Context::Rule &rule, const Context::Rule *includeRule = nullptr)
+ {
+ for (int i = 0; i < m_size; ++i) {
+ m_charTables[i]->append(c, rule, includeRule);
+ }
+ }
+
+ /// Associates each character of @p s with a rule.
+ void append(QStringView s, const Context::Rule &rule, const Context::Rule *includeRule = nullptr)
+ {
+ for (int i = 0; i < m_size; ++i) {
+ m_charTables[i]->append(s, rule, includeRule);
+ }
+ }
+
+ private:
+ void appendTable(CharTable &t)
+ {
+ m_charTables[m_size] = &t;
+ ++m_size;
+ }
+
+ CharTable *m_charTables[3];
+ int m_size = 0;
+ };
+
+ struct ObservableRule {
+ const Context::Rule *rule;
+ const Context::Rule *includeRules;
+
+ bool hasResolvedIncludeRules() const
+ {
+ return rule == includeRules;
+ }
+ };
+
+ // Iterates over all the rules, including those in includedRules
+ struct RuleIterator {
+ RuleIterator(const QList<ObservableRule> &rules, const ObservableRule &endRule)
+ : m_end(&endRule - rules.data())
+ , m_rules(rules)
+ {
+ }
+
+ /// \return next rule or nullptr
+ const Context::Rule *next()
+ {
+ // if in includedRules
+ if (m_includedRules) {
+ ++m_i2;
+ if (m_i2 != m_includedRules->size()) {
+ return (*m_includedRules)[m_i2];
+ }
+ ++m_i;
+ m_includedRules = nullptr;
+ }
+
+ // if is a includedRules
+ while (m_i < m_end && m_rules[m_i].rule->type == Context::Rule::Type::IncludeRules) {
+ if (!m_rules[m_i].includeRules && m_rules[m_i].rule->includedRules.size()) {
+ m_i2 = 0;
+ m_includedRules = &m_rules[m_i].rule->includedRules;
+ return (*m_includedRules)[m_i2];
+ }
+ ++m_i;
+ }
+
+ if (m_i < m_end) {
+ ++m_i;
+ return m_rules[m_i - 1].rule;
+ }
+
+ return nullptr;
+ }
+
+ /// \return current IncludeRules or nullptr
+ const Context::Rule *currentIncludeRules() const
+ {
+ return m_includedRules ? m_rules[m_i].rule : m_rules[m_i].includeRules;
+ }
+
+ private:
+ int m_i = 0;
+ int m_i2 = 0;
+ const int m_end;
+ const QList<ObservableRule> &m_rules;
+ const QList<const Context::Rule *> *m_includedRules = nullptr;
+ };
+
+ // Dot regex container that satisfies firstNonSpace and column.
+ struct DotRegex {
+ /// Append a dot regex rule.
+ void append(const Context::Rule &rule, const Context::Rule *includedRule)
+ {
+ auto array = extractDotRegexes(rule);
+ if (array[0]) {
+ *array[0] = {&rule, includedRule};
+ }
+ if (array[1]) {
+ *array[1] = {&rule, includedRule};
+ }
+ }
+
+ /// Search dot regex which hides @p rule
+ RuleAndInclude find(const Context::Rule &rule)
+ {
+ auto array = extractDotRegexes(rule);
+ if (array[0]) {
+ return *array[0];
+ }
+ if (array[1]) {
+ return *array[1];
+ }
+ return RuleAndInclude{};
+ }
+
+ private:
+ using Array = std::array<RuleAndInclude *, 2>;
+
+ Array extractDotRegexes(const Context::Rule &rule)
+ {
+ Array ret{};
+
+ if (rule.firstNonSpace != XmlBool::True && rule.column == -1) {
+ ret[0] = &dotRegex;
+ } else {
+ if (rule.firstNonSpace == XmlBool::True) {
+ ret[0] = &dotRegexFirstNonSpace;
+ }
+
+ if (rule.column == 0) {
+ ret[1] = &dotRegexColumn0;
+ } else if (rule.column > 0) {
+ ret[1] = &dotRegexColumnGreaterThan0[rule.column];
+ }
+ }
+
+ return ret;
+ }
+
+ RuleAndInclude dotRegex{};
+ RuleAndInclude dotRegexColumn0{};
+ QMap<int, RuleAndInclude> dotRegexColumnGreaterThan0{};
+ RuleAndInclude dotRegexFirstNonSpace{};
+ };
+
bool success = true;
- for (auto &language : m_contextMap) {
- const auto invalidContextNames = language.usedContextNames - language.existingContextNames;
- if (!invalidContextNames.isEmpty()) {
- qWarning() << language.hlFilename << "Reference of non-existing contexts:" << invalidContextNames;
- success = false;
+
+ // characters of DetectChar/AnyChar
+ Char4Tables detectChars;
+ // characters of dynamic DetectChar
+ Char4Tables dynamicDetectChars;
+ // characters of LineContinue
+ Char4Tables lineContinueChars;
+
+ Rule4 intRule{};
+ Rule4 floatRule{};
+ Rule4 hlCCharRule{};
+ Rule4 hlCOctRule{};
+ Rule4 hlCHexRule{};
+ Rule4 hlCStringCharRule{};
+ Rule4 detectIdentifierRule{};
+
+ // Contains includedRules and included includedRules
+ QMap<Context const *, RuleAndInclude> includeContexts;
+
+ DotRegex dotRegex;
+
+ QList<ObservableRule> observedRules;
+ observedRules.reserve(context.rules.size());
+ for (const Context::Rule &rule : context.rules) {
+ const Context::Rule *includeRule = nullptr;
+ if (rule.type == Context::Rule::Type::IncludeRules) {
+ auto *context = rule.context.context;
+ if (context && context->isOnlyIncluded) {
+ includeRule = &rule;
+ }
+ }
+
+ observedRules.push_back({&rule, includeRule});
+ if (includeRule) {
+ for (const Context::Rule *rule2 : rule.includedRules) {
+ observedRules.push_back({rule2, includeRule});
+ }
+ }
+ }
+
+ for (auto &observedRule : observedRules) {
+ const Context::Rule &rule = *observedRule.rule;
+ bool isUnreachable = false;
+ QList<RuleAndInclude> unreachableBy;
+
+ // declare rule as unreachable if ruleAndInclude is not empty
+ auto updateUnreachable1 = [&](RuleAndInclude ruleAndInclude) {
+ if (ruleAndInclude) {
+ isUnreachable = true;
+ unreachableBy.append(ruleAndInclude);
+ }
+ };
+
+ // declare rule as unreachable if ruleAndIncludes is not empty
+ auto updateUnreachable2 = [&](const QList<RuleAndInclude> &ruleAndIncludes) {
+ if (!ruleAndIncludes.isEmpty()) {
+ isUnreachable = true;
+ unreachableBy.append(ruleAndIncludes);
+ }
+ };
+
+ // check if rule2.firstNonSpace/column is compatible with those of rule
+ auto isCompatible = [&rule](Context::Rule const &rule2) {
+ return (rule2.firstNonSpace != XmlBool::True && rule2.column == -1) || (rule.column == rule2.column && rule.column != -1)
+ || (rule.firstNonSpace == rule2.firstNonSpace && rule.firstNonSpace == XmlBool::True);
+ };
+
+ updateUnreachable1(dotRegex.find(rule));
+
+ switch (rule.type) {
+ // checks if hidden by DetectChar/AnyChar
+ // then add the characters to detectChars
+ case Context::Rule::Type::AnyChar: {
+ auto tables = CharTableArray(detectChars, rule);
+ updateUnreachable2(tables.find(rule.string));
+ tables.removeNonSpecialWhenSpecial();
+ tables.append(rule.string, rule);
+ break;
+ }
+
+ // check if is hidden by DetectChar/AnyChar
+ // then add the characters to detectChars or dynamicDetectChars
+ case Context::Rule::Type::DetectChar: {
+ auto &chars4 = (rule.dynamic != XmlBool::True) ? detectChars : dynamicDetectChars;
+ auto tables = CharTableArray(chars4, rule);
+ updateUnreachable1(tables.find(rule.char0));
+ tables.removeNonSpecialWhenSpecial();
+ tables.append(rule.char0, rule);
+ break;
+ }
+
+ // check if hidden by DetectChar/AnyChar
+ // then add spaces characters to detectChars
+ case Context::Rule::Type::DetectSpaces: {
+ auto tables = CharTableArray(detectChars, rule);
+ updateUnreachable2(tables.find(QStringLiteral(" \t")));
+ tables.removeNonSpecialWhenSpecial();
+ tables.append(QLatin1Char(' '), rule);
+ tables.append(QLatin1Char('\t'), rule);
+ break;
}
- const auto unusedNames = language.existingContextNames - language.usedContextNames;
- if (!unusedNames.isEmpty()) {
- qWarning() << language.hlFilename << "Unused contexts:" << unusedNames;
+ // check if hidden by DetectChar/AnyChar
+ case Context::Rule::Type::HlCChar:
+ updateUnreachable1(CharTableArray(detectChars, rule).find(QLatin1Char('\'')));
+ updateUnreachable1(hlCCharRule.setRule(rule));
+ break;
+
+ // check if hidden by DetectChar/AnyChar
+ case Context::Rule::Type::HlCHex:
+ updateUnreachable1(CharTableArray(detectChars, rule).find(QLatin1Char('0')));
+ updateUnreachable1(hlCHexRule.setRule(rule));
+ break;
+
+ // check if hidden by DetectChar/AnyChar
+ case Context::Rule::Type::HlCOct:
+ updateUnreachable1(CharTableArray(detectChars, rule).find(QLatin1Char('0')));
+ updateUnreachable1(hlCOctRule.setRule(rule));
+ break;
+
+ // check if hidden by DetectChar/AnyChar
+ case Context::Rule::Type::HlCStringChar:
+ updateUnreachable1(CharTableArray(detectChars, rule).find(QLatin1Char('\\')));
+ updateUnreachable1(hlCStringCharRule.setRule(rule));
+ break;
+
+ // check if hidden by DetectChar/AnyChar
+ case Context::Rule::Type::Int:
+ updateUnreachable2(CharTableArray(detectChars, rule).find(QStringLiteral("0123456789")));
+ updateUnreachable1(intRule.setRule(rule));
+ break;
+
+ // check if hidden by DetectChar/AnyChar
+ case Context::Rule::Type::Float:
+ updateUnreachable2(CharTableArray(detectChars, rule).find(QStringLiteral("0123456789.")));
+ updateUnreachable1(floatRule.setRule(rule));
+ // check that Float is before Int
+ updateUnreachable1(Rule4(intRule).setRule(rule));
+ break;
+
+ // check if hidden by another DetectIdentifier rule
+ case Context::Rule::Type::DetectIdentifier:
+ updateUnreachable1(detectIdentifierRule.setRule(rule));
+ break;
+
+ // check if hidden by DetectChar/AnyChar or another LineContinue
+ case Context::Rule::Type::LineContinue: {
+ updateUnreachable1(CharTableArray(detectChars, rule).find(rule.char0));
+
+ auto tables = CharTableArray(lineContinueChars, rule);
+ updateUnreachable1(tables.find(rule.char0));
+ tables.removeNonSpecialWhenSpecial();
+ tables.append(rule.char0, rule);
+ break;
+ }
+
+ // check if hidden by DetectChar/AnyChar or another Detect2Chars/RangeDetect
+ case Context::Rule::Type::Detect2Chars:
+ case Context::Rule::Type::RangeDetect:
+ updateUnreachable1(CharTableArray(detectChars, rule).find(rule.char0));
+ if (!isUnreachable) {
+ RuleIterator ruleIterator(observedRules, observedRule);
+ while (const auto *rulePtr = ruleIterator.next()) {
+ if (isUnreachable) {
+ break;
+ }
+ const auto &rule2 = *rulePtr;
+ if (rule2.type == rule.type && isCompatible(rule2) && rule.char0 == rule2.char0 && rule.char1 == rule2.char1) {
+ updateUnreachable1({&rule2, ruleIterator.currentIncludeRules()});
+ }
+ }
+ }
+ break;
+
+ case Context::Rule::Type::RegExpr: {
+ if (rule.isDotRegex) {
+ dotRegex.append(rule, nullptr);
+ break;
+ }
+
+ // check that `rule` does not have another RegExpr as a prefix
+ RuleIterator ruleIterator(observedRules, observedRule);
+ while (const auto *rulePtr = ruleIterator.next()) {
+ if (isUnreachable) {
+ break;
+ }
+ const auto &rule2 = *rulePtr;
+ if (rule2.type == Context::Rule::Type::RegExpr && isCompatible(rule2) && rule.insensitive == rule2.insensitive
+ && rule.dynamic == rule2.dynamic && rule.sanitizedString.startsWith(rule2.sanitizedString)) {
+ bool add = (rule.sanitizedString.startsWith(rule2.string) || rule.sanitizedString.size() < rule2.sanitizedString.size() + 2);
+ if (!add) {
+ // \s.* (sanitized = \s) is considered hiding \s*\S
+ // we check the quantifiers to see if this is the case
+ auto c1 = rule.sanitizedString[rule2.sanitizedString.size()].unicode();
+ auto c2 = rule.sanitizedString[rule2.sanitizedString.size() + 1].unicode();
+ auto c3 = rule2.sanitizedString.back().unicode();
+ if (c3 == '*' || c3 == '?' || c3 == '+') {
+ add = true;
+ } else if (c1 == '*' || c1 == '?') {
+ add = !((c2 == '?' || c2 == '+') || (rule.sanitizedString.size() >= rule2.sanitizedString.size() + 3));
+ } else {
+ add = true;
+ }
+ }
+ if (add) {
+ updateUnreachable1({&rule2, ruleIterator.currentIncludeRules()});
+ }
+ }
+ }
+
+ Q_FALLTHROUGH();
+ }
+ // check if a rule does not have another rule as a prefix
+ case Context::Rule::Type::WordDetect:
+ case Context::Rule::Type::StringDetect: {
+ // check that dynamic `rule` does not have another dynamic StringDetect as a prefix
+ if (rule.type == Context::Rule::Type::StringDetect && rule.dynamic == XmlBool::True) {
+ RuleIterator ruleIterator(observedRules, observedRule);
+ while (const auto *rulePtr = ruleIterator.next()) {
+ if (isUnreachable) {
+ break;
+ }
+
+ const auto &rule2 = *rulePtr;
+ if (rule2.type != Context::Rule::Type::StringDetect || rule2.dynamic != XmlBool::True || !isCompatible(rule2)) {
+ continue;
+ }
+
+ const bool isSensitive = (rule2.insensitive == XmlBool::True);
+ const auto caseSensitivity = isSensitive ? Qt::CaseInsensitive : Qt::CaseSensitive;
+ if ((isSensitive || rule.insensitive != XmlBool::True) && rule.string.startsWith(rule2.string, caseSensitivity)) {
+ updateUnreachable1({&rule2, ruleIterator.currentIncludeRules()});
+ }
+ }
+ }
+
+ // string used for comparison and truncated from "dynamic" part
+ QStringView s = rule.string;
+
+ // truncate to '%' with dynamic rules
+ if (rule.dynamic == XmlBool::True) {
+ static const QRegularExpression dynamicPosition(QStringLiteral(R"(^(?:[^%]*|%(?![1-9]))*)"));
+ auto result = dynamicPosition.match(rule.string);
+ s = s.left(result.capturedLength());
+ }
+
+ QString sanitizedRegex;
+ // truncate to special character with RegExpr.
+ // If regexp contains '|', `s` becomes empty.
+ if (rule.type == Context::Rule::Type::RegExpr) {
+ static const QRegularExpression regularChars(QStringLiteral(R"(^(?:[^.?*+^$[{(\\|]+|\\[-.?*+^$[\]{}()\\|]+|\[[^^\\]\])+)"));
+ static const QRegularExpression sanitizeChars(QStringLiteral(R"(\\([-.?*+^$[\]{}()\\|])|\[([^^\\])\])"));
+ const qsizetype result = regularChars.match(rule.string).capturedLength();
+ const qsizetype pos = qMin(result, s.size());
+ if (rule.string.indexOf(QLatin1Char('|'), pos) < pos) {
+ sanitizedRegex = rule.string.left(qMin(result, s.size()));
+ sanitizedRegex.replace(sanitizeChars, QStringLiteral("\\1"));
+ s = sanitizedRegex;
+ } else {
+ s = QStringView();
+ }
+ }
+
+ // check if hidden by DetectChar/AnyChar
+ if (s.size() > 0) {
+ auto t = CharTableArray(detectChars, rule);
+ if (rule.insensitive != XmlBool::True) {
+ updateUnreachable1(t.find(s[0]));
+ } else {
+ QChar c2[]{s[0].toLower(), s[0].toUpper()};
+ updateUnreachable2(t.find(QStringView(c2, 2)));
+ }
+ }
+
+ // check if Detect2Chars, StringDetect, WordDetect is not a prefix of s
+ if (s.size() > 0 && !isUnreachable) {
+ // combination of uppercase and lowercase
+ RuleAndInclude detect2CharsInsensitives[]{{}, {}, {}, {}};
+
+ RuleIterator ruleIterator(observedRules, observedRule);
+ while (const auto *rulePtr = ruleIterator.next()) {
+ if (isUnreachable) {
+ break;
+ }
+ const auto &rule2 = *rulePtr;
+ const bool isSensitive = (rule2.insensitive == XmlBool::True);
+ const auto caseSensitivity = isSensitive ? Qt::CaseInsensitive : Qt::CaseSensitive;
+
+ switch (rule2.type) {
+ // check that it is not a detectChars prefix
+ case Context::Rule::Type::Detect2Chars:
+ if (isCompatible(rule2) && s.size() >= 2) {
+ if (rule.insensitive != XmlBool::True) {
+ if (rule2.char0 == s[0] && rule2.char1 == s[1]) {
+ updateUnreachable1({&rule2, ruleIterator.currentIncludeRules()});
+ }
+ } else {
+ // when the string is case insensitive,
+ // all 4 upper/lower case combinations must be found
+ auto set = [&](RuleAndInclude &x, QChar c1, QChar c2) {
+ if (!x && rule2.char0 == c1 && rule2.char0 == c2) {
+ x = {&rule2, ruleIterator.currentIncludeRules()};
+ }
+ };
+ set(detect2CharsInsensitives[0], s[0].toLower(), s[1].toLower());
+ set(detect2CharsInsensitives[1], s[0].toLower(), s[1].toUpper());
+ set(detect2CharsInsensitives[2], s[0].toUpper(), s[1].toUpper());
+ set(detect2CharsInsensitives[3], s[0].toUpper(), s[1].toLower());
+
+ if (detect2CharsInsensitives[0] && detect2CharsInsensitives[1] && detect2CharsInsensitives[2]
+ && detect2CharsInsensitives[3]) {
+ isUnreachable = true;
+ unreachableBy.append(detect2CharsInsensitives[0]);
+ unreachableBy.append(detect2CharsInsensitives[1]);
+ unreachableBy.append(detect2CharsInsensitives[2]);
+ unreachableBy.append(detect2CharsInsensitives[3]);
+ }
+ }
+ }
+ break;
+
+ // check that it is not a StringDetect prefix
+ case Context::Rule::Type::StringDetect:
+ if (isCompatible(rule2) && rule2.dynamic != XmlBool::True && (isSensitive || rule.insensitive != XmlBool::True)
+ && s.startsWith(rule2.string, caseSensitivity)) {
+ updateUnreachable1({&rule2, ruleIterator.currentIncludeRules()});
+ }
+ break;
+
+ // check if a WordDetect is hidden by another WordDetect
+ case Context::Rule::Type::WordDetect:
+ if (rule.type == Context::Rule::Type::WordDetect && isCompatible(rule2) && (isSensitive || rule.insensitive != XmlBool::True)
+ && 0 == rule.string.compare(rule2.string, caseSensitivity)) {
+ updateUnreachable1({&rule2, ruleIterator.currentIncludeRules()});
+ }
+ break;
+
+ default:;
+ }
+ }
+ }
+
+ break;
+ }
+
+ // check if hidden by another keyword rule
+ case Context::Rule::Type::keyword: {
+ RuleIterator ruleIterator(observedRules, observedRule);
+ while (const auto *rulePtr = ruleIterator.next()) {
+ if (isUnreachable) {
+ break;
+ }
+ const auto &rule2 = *rulePtr;
+ if (rule2.type == Context::Rule::Type::keyword && isCompatible(rule2) && rule.string == rule2.string) {
+ updateUnreachable1({&rule2, ruleIterator.currentIncludeRules()});
+ }
+ }
+ // TODO check that all keywords are hidden by another rules
+ break;
+ }
+
+ // add characters in those used but without checking if they are already.
+ // <DetectChar char="}" />
+ // <includedRules .../> <- reference an another <DetectChar char="}" /> who will not be checked
+ // <includedRules .../> <- reference a <DetectChar char="{" /> who will be added
+ // <DetectChar char="{" /> <- hidden by previous rule
+ case Context::Rule::Type::IncludeRules:
+ if (observedRule.includeRules && !observedRule.hasResolvedIncludeRules()) {
+ break;
+ }
+
+ if (auto &ruleAndInclude = includeContexts[rule.context.context]) {
+ updateUnreachable1(ruleAndInclude);
+ } else {
+ ruleAndInclude.rule = &rule;
+ }
+
+ for (const auto *rulePtr : rule.includedIncludeRules) {
+ includeContexts.insert(rulePtr->context.context, RuleAndInclude{rulePtr, &rule});
+ }
+
+ if (observedRule.includeRules) {
+ break;
+ }
+
+ for (const auto *rulePtr : rule.includedRules) {
+ const auto &rule2 = *rulePtr;
+ switch (rule2.type) {
+ case Context::Rule::Type::AnyChar: {
+ auto tables = CharTableArray(detectChars, rule2);
+ tables.removeNonSpecialWhenSpecial();
+ tables.append(rule2.string, rule2, &rule);
+ break;
+ }
+
+ case Context::Rule::Type::DetectChar: {
+ auto &chars4 = (rule.dynamic != XmlBool::True) ? detectChars : dynamicDetectChars;
+ auto tables = CharTableArray(chars4, rule2);
+ tables.removeNonSpecialWhenSpecial();
+ tables.append(rule2.char0, rule2, &rule);
+ break;
+ }
+
+ case Context::Rule::Type::DetectSpaces: {
+ auto tables = CharTableArray(detectChars, rule2);
+ tables.removeNonSpecialWhenSpecial();
+ tables.append(QLatin1Char(' '), rule2, &rule);
+ tables.append(QLatin1Char('\t'), rule2, &rule);
+ break;
+ }
+
+ case Context::Rule::Type::HlCChar:
+ hlCCharRule.setRule(rule2, &rule);
+ break;
+
+ case Context::Rule::Type::HlCHex:
+ hlCHexRule.setRule(rule2, &rule);
+ break;
+
+ case Context::Rule::Type::HlCOct:
+ hlCOctRule.setRule(rule2, &rule);
+ break;
+
+ case Context::Rule::Type::HlCStringChar:
+ hlCStringCharRule.setRule(rule2, &rule);
+ break;
+
+ case Context::Rule::Type::Int:
+ intRule.setRule(rule2, &rule);
+ break;
+
+ case Context::Rule::Type::Float:
+ floatRule.setRule(rule2, &rule);
+ break;
+
+ case Context::Rule::Type::LineContinue: {
+ auto tables = CharTableArray(lineContinueChars, rule2);
+ tables.removeNonSpecialWhenSpecial();
+ tables.append(rule2.char0, rule2, &rule);
+ break;
+ }
+
+ case Context::Rule::Type::RegExpr:
+ if (rule2.isDotRegex) {
+ dotRegex.append(rule2, &rule);
+ }
+ break;
+
+ case Context::Rule::Type::WordDetect:
+ case Context::Rule::Type::StringDetect:
+ case Context::Rule::Type::Detect2Chars:
+ case Context::Rule::Type::IncludeRules:
+ case Context::Rule::Type::DetectIdentifier:
+ case Context::Rule::Type::keyword:
+ case Context::Rule::Type::Unknown:
+ case Context::Rule::Type::RangeDetect:
+ break;
+ }
+ }
+ break;
+
+ case Context::Rule::Type::Unknown:
+ break;
+ }
+
+ if (observedRule.includeRules && !observedRule.hasResolvedIncludeRules()) {
+ auto &unreachableIncludedRule = unreachableIncludedRules[&rule];
+ if (isUnreachable && unreachableIncludedRule.alwaysUnreachable) {
+ unreachableIncludedRule.unreachableBy.append(unreachableBy);
+ } else {
+ unreachableIncludedRule.alwaysUnreachable = false;
+ }
+ } else if (isUnreachable) {
success = false;
+ QString message;
+ message.reserve(128);
+ for (auto &ruleAndInclude : unreachableBy) {
+ message += QStringLiteral("line ");
+ if (ruleAndInclude.includeRules) {
+ message += QString::number(ruleAndInclude.includeRules->line);
+ message += QStringLiteral(" [by '");
+ message += ruleAndInclude.includeRules->context.name;
+ message += QStringLiteral("' line ");
+ message += QString::number(ruleAndInclude.rule->line);
+ if (ruleAndInclude.includeRules->filename != ruleAndInclude.rule->filename) {
+ message += QStringLiteral(" (");
+ message += ruleAndInclude.rule->filename;
+ message += QLatin1Char(')');
+ }
+ message += QLatin1Char(']');
+ } else {
+ message += QString::number(ruleAndInclude.rule->line);
+ }
+ message += QStringLiteral(", ");
+ }
+ message.chop(2);
+ qWarning() << filename << "line" << rule.line << "unreachable rule by" << message;
}
}
return success;
}
-private:
- //! Extract the referenced context name and language.
+ //! Proposes to merge certain rule sequences
+ //! - several DetectChar/AnyChar into AnyChar
+ //! - several RegExpr into one RegExpr
+ bool suggestRuleMerger(const QString &filename, const Context &context) const
+ {
+ bool success = true;
+
+ if (context.rules.isEmpty()) {
+ return success;
+ }
+
+ auto it = context.rules.begin();
+ const auto end = context.rules.end() - 1;
+
+ for (; it < end; ++it) {
+ auto &rule1 = *it;
+ auto &rule2 = it[1];
+
+ auto isCommonCompatible = [&] {
+ if (rule1.lookAhead != rule2.lookAhead) {
+ return false;
+ }
+ // ignore attribute when lookAhead is true
+ if (rule1.lookAhead != XmlBool::True && rule1.attribute != rule2.attribute) {
+ return false;
+ }
+ // clang-format off
+ return rule1.beginRegion == rule2.beginRegion
+ && rule1.endRegion == rule2.endRegion
+ && rule1.firstNonSpace == rule2.firstNonSpace
+ && rule1.context.context == rule2.context.context
+ && rule1.context.popCount == rule2.context.popCount;
+ // clang-format on
+ };
+
+ switch (rule1.type) {
+ // request to merge AnyChar/DetectChar
+ case Context::Rule::Type::AnyChar:
+ case Context::Rule::Type::DetectChar:
+ if ((rule2.type == Context::Rule::Type::AnyChar || rule2.type == Context::Rule::Type::DetectChar) && isCommonCompatible()
+ && rule1.column == rule2.column) {
+ qWarning() << filename << "line" << rule2.line << "can be merged as AnyChar with the previous rule";
+ success = false;
+ }
+ break;
+
+ // request to merge multiple RegExpr
+ case Context::Rule::Type::RegExpr:
+ if (rule2.type == Context::Rule::Type::RegExpr && isCommonCompatible() && rule1.dynamic == rule2.dynamic
+ && (rule1.column == rule2.column || (rule1.column <= 0 && rule2.column <= 0))) {
+ qWarning() << filename << "line" << rule2.line << "can be merged with the previous rule";
+ success = false;
+ }
+ break;
+
+ case Context::Rule::Type::DetectSpaces:
+ case Context::Rule::Type::HlCChar:
+ case Context::Rule::Type::HlCHex:
+ case Context::Rule::Type::HlCOct:
+ case Context::Rule::Type::HlCStringChar:
+ case Context::Rule::Type::Int:
+ case Context::Rule::Type::Float:
+ case Context::Rule::Type::LineContinue:
+ case Context::Rule::Type::WordDetect:
+ case Context::Rule::Type::StringDetect:
+ case Context::Rule::Type::Detect2Chars:
+ case Context::Rule::Type::IncludeRules:
+ case Context::Rule::Type::DetectIdentifier:
+ case Context::Rule::Type::keyword:
+ case Context::Rule::Type::Unknown:
+ case Context::Rule::Type::RangeDetect:
+ break;
+ }
+ }
+
+ return success;
+ }
+
+ //! Initialize the referenced context (ContextName::context)
//! Some input / output examples are:
//! - "#stay" -> ""
//! - "#pop" -> ""
@@ -388,115 +2629,141 @@ private:
//! - "#pop!Comment" -> "Comment"
//! - "##ISO C++" -> ""
//! - "Comment##ISO C++"-> "Comment" in ISO C++
- void processContext(const QString &language, QString context)
+ void resolveContextName(Definition &definition, Context &context, ContextName &contextName, int line)
{
- if (context.isEmpty())
- return;
+ QStringView name = contextName.name;
+ if (name.isEmpty()) {
+ contextName.stay = true;
+ } else if (name.startsWith(QStringLiteral("#stay"))) {
+ name = name.mid(5);
+ contextName.stay = true;
+ contextName.context = &context;
+ if (!name.isEmpty()) {
+ qWarning() << definition.filename << "line" << line << "invalid context in" << context.name;
+ m_success = false;
+ }
+ } else {
+ while (name.startsWith(QStringLiteral("#pop"))) {
+ name = name.mid(4);
+ ++contextName.popCount;
+ }
- // filter out #stay and #pop
- static QRegularExpression stayPop(QStringLiteral("^(#stay|#pop)+"));
- context.remove(stayPop);
+ if (contextName.popCount && !name.isEmpty()) {
+ if (name.startsWith(QLatin1Char('!')) && name.size() > 1) {
+ name = name.mid(1);
+ } else {
+ qWarning() << definition.filename << "line" << line << "'!' missing between '#pop' and context name" << context.name;
+ m_success = false;
+ }
+ }
+
+ if (!name.isEmpty()) {
+ const int idx = name.indexOf(QStringLiteral("##"));
+ if (idx == -1) {
+ auto it = definition.contexts.find(name.toString());
+ if (it != definition.contexts.end()) {
+ contextName.context = &*it;
+ }
+ } else {
+ auto defName = name.mid(idx + 2);
+ auto it = m_definitions.find(defName.toString());
+ if (it != m_definitions.end()) {
+ auto listName = name.left(idx).toString();
+ definition.referencedDefinitions.insert(&*it);
+ auto ctxIt = it->contexts.find(listName.isEmpty() ? it->firstContextName : listName);
+ if (ctxIt != it->contexts.end()) {
+ contextName.context = &*ctxIt;
+ }
+ } else {
+ qWarning() << definition.filename << "line" << line << "unknown definition in" << context.name;
+ m_success = false;
+ }
+ }
- // handle cross-language context references
- if (context.contains(QStringLiteral("##"))) {
- const QStringList list = context.split(QStringLiteral("##"), QString::SkipEmptyParts);
- if (list.size() == 1) {
- // nothing to do, other language is included: e.g. ##Doxygen
- } else if (list.size() == 2) {
- // specific context of other language, e.g. Comment##ISO C++
- m_contextMap[list[1]].usedContextNames.insert(list[0]);
+ if (!contextName.context) {
+ qWarning() << definition.filename << "line" << line << "unknown context" << name << "in" << context.name;
+ m_success = false;
+ }
}
- return;
}
+ }
- // handle #pop!context" (#pop was already removed above)
- if (context.startsWith(QLatin1Char('!')))
- context.remove(0, 1);
+ QMap<QString, Definition> m_definitions;
+ Definition *m_currentDefinition = nullptr;
+ Keywords *m_currentKeywords = nullptr;
+ Context *m_currentContext = nullptr;
+ bool m_success = true;
+};
- if (!context.isEmpty())
- m_contextMap[language].usedContextNames.insert(context);
+namespace
+{
+QStringList readListing(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly)) {
+ return QStringList();
}
-private:
- class Language
- {
- public:
- // filename on disk or in Qt resource
- QString hlFilename;
-
- // Is true, if the first context in xml file is encountered, and
- // false in all other cases. This is required, since the first context
- // is typically not referenced explicitly. So we will simply add the
- // first context to the usedContextNames list.
- bool isFirstContext = true;
+ QXmlStreamReader xml(&file);
+ QStringList listing;
+ while (!xml.atEnd()) {
+ xml.readNext();
- // holds all contexts that were referenced
- QSet<QString> usedContextNames;
+ // add only .xml files, no .json or stuff
+ if (xml.isCharacters() && xml.text().contains(QLatin1String(".xml"))) {
+ listing.append(xml.text().toString());
+ }
+ }
- // holds all existing context names
- QSet<QString> existingContextNames;
- };
+ if (xml.hasError()) {
+ qWarning() << "XML error while reading" << fileName << " - " << qPrintable(xml.errorString()) << "@ offset" << xml.characterOffset();
+ listing.clear();
+ }
- /**
- * "Language name" to "Language" map.
- * Example key: "Doxygen"
- */
- QHash<QString, Language> m_contextMap;
-};
+ return listing;
+}
/**
- * Helper class to search for non-existing itemDatas.
+ * check if the "extensions" attribute have valid wildcards
+ * @param extensions extensions string to check
+ * @return valid?
*/
-class AttributeChecker
+bool checkExtensions(QStringView extensions)
{
-public:
- AttributeChecker(const QString &filename)
- : m_filename(filename)
- {}
+ // get list of extensions
+ const QList<QStringView> extensionParts = extensions.split(QLatin1Char(';'), Qt::SkipEmptyParts);
- void processElement(QXmlStreamReader &xml)
- {
- if (xml.name() == QLatin1String("itemData")) {
- const QString name = xml.attributes().value(QLatin1String("name")).toString();
- if (!name.isEmpty()) {
- if (m_existingAttributeNames.contains(name)) {
- qWarning() << m_filename << "itemData duplicate:" << name;
- } else {
- m_existingAttributeNames.insert(name);
- }
+ // ok if empty
+ if (extensionParts.isEmpty()) {
+ return true;
+ }
+
+ // check that only valid wildcard things are inside the parts
+ for (const auto &extension : extensionParts) {
+ for (const auto c : extension) {
+ // eat normal things
+ if (c.isDigit() || c.isLetter()) {
+ continue;
}
- } else if (xml.attributes().hasAttribute(QLatin1String("attribute"))) {
- const QString name = xml.attributes().value(QLatin1String("attribute")).toString();
- if (name.isEmpty()) {
- qWarning() << m_filename << "specified attribute is empty:" << xml.name();
- } else {
- m_usedAttributeNames.insert(name);
+
+ // allow some special characters
+ if (c == QLatin1Char('.') || c == QLatin1Char('-') || c == QLatin1Char('_') || c == QLatin1Char('+')) {
+ continue;
}
- }
- }
- bool check() const
- {
- bool success = true;
- const auto invalidNames = m_usedAttributeNames - m_existingAttributeNames;
- if (!invalidNames.isEmpty()) {
- qWarning() << m_filename << "Reference of non-existing itemData attributes:" << invalidNames;
- success = false;
- }
+ // only allowed wildcard things: '?' and '*'
+ if (c == QLatin1Char('?') || c == QLatin1Char('*')) {
+ continue;
+ }
- auto unusedNames = m_existingAttributeNames - m_usedAttributeNames;
- if (!unusedNames.isEmpty()) {
- qWarning() << m_filename << "Unused itemData:" << unusedNames;
+ qWarning() << "invalid character" << c << "seen in extensions wildcard";
+ return false;
}
-
- return success;
}
-private:
- QString m_filename;
- QSet<QString> m_usedAttributeNames;
- QSet<QString> m_existingAttributeNames;
-};
+ // all checks passed
+ return true;
+}
}
@@ -506,14 +2773,36 @@ int main(int argc, char *argv[])
QCoreApplication app(argc, argv);
// ensure enough arguments are passed
- if (app.arguments().size() < 3)
+ if (app.arguments().size() < 3) {
return 1;
+ }
+
+#ifdef HAS_XERCESC
+ // care for proper init and cleanup
+ XMLPlatformUtils::Initialize();
+ auto cleanup = qScopeGuard(XMLPlatformUtils::Terminate);
-#ifdef QT_XMLPATTERNS_LIB
- // open schema
- QXmlSchema schema;
- if (!schema.load(QUrl::fromLocalFile(app.arguments().at(2))))
+ /*
+ * parse XSD first time and cache it
+ */
+ XMLGrammarPoolImpl xsd(XMLPlatformUtils::fgMemoryManager);
+
+ // create parser for the XSD
+ SAX2XMLReaderImpl parser(XMLPlatformUtils::fgMemoryManager, &xsd);
+ init_parser(parser);
+ QString messages;
+ CustomErrorHandler eh(&messages);
+ parser.setErrorHandler(&eh);
+
+ // load grammar into the pool, on error just abort
+ const auto xsdFile = app.arguments().at(2);
+ if (!parser.loadGrammar((const char16_t *)xsdFile.utf16(), Grammar::SchemaGrammarType, true) || eh.failed()) {
+ qWarning("Failed to parse XSD %s: %s", qPrintable(xsdFile), qPrintable(messages));
return 2;
+ }
+
+ // lock the pool, no later modifications wanted!
+ xsd.lockPool();
#endif
const QString hlFilenamesListing = app.arguments().value(3);
@@ -529,26 +2818,35 @@ int main(int argc, char *argv[])
// text attributes
const QStringList textAttributes = QStringList() << QStringLiteral("name") << QStringLiteral("section") << QStringLiteral("mimetype")
- << QStringLiteral("extensions") << QStringLiteral("style")
- << QStringLiteral("author") << QStringLiteral("license") << QStringLiteral("indenter");
+ << QStringLiteral("extensions") << QStringLiteral("style") << QStringLiteral("author")
+ << QStringLiteral("license") << QStringLiteral("indenter");
// index all given highlightings
- ContextChecker contextChecker;
- KeywordIncludeChecker keywordIncludeChecker;
+ HlFilesChecker filesChecker;
QVariantMap hls;
int anyError = 0;
- for (const QString &hlFilename : qAsConst(hlFilenames)) {
+ for (const QString &hlFilename : std::as_const(hlFilenames)) {
QFile hlFile(hlFilename);
if (!hlFile.open(QIODevice::ReadOnly)) {
- qWarning ("Failed to open %s", qPrintable(hlFilename));
+ qWarning("Failed to open %s", qPrintable(hlFilename));
anyError = 3;
continue;
}
-#ifdef QT_XMLPATTERNS_LIB
- // validate against schema
- QXmlSchemaValidator validator(schema);
- if (!validator.validate(&hlFile, QUrl::fromLocalFile(hlFile.fileName()))) {
+#ifdef HAS_XERCESC
+ // create parser
+ SAX2XMLReaderImpl parser(XMLPlatformUtils::fgMemoryManager, &xsd);
+ init_parser(parser);
+ QString messages;
+ CustomErrorHandler eh(&messages);
+ parser.setErrorHandler(&eh);
+
+ // parse the XML file
+ parser.parse((const char16_t *)hlFile.fileName().utf16());
+
+ // report issues
+ if (eh.failed()) {
+ qWarning("Failed to validate XML %s: %s", qPrintable(hlFile.fileName()), qPrintable(messages));
anyError = 4;
continue;
}
@@ -571,7 +2869,7 @@ int main(int argc, char *argv[])
QVariantMap hl;
// transfer text attributes
- for (const QString &attribute : qAsConst(textAttributes)) {
+ for (const QString &attribute : std::as_const(textAttributes)) {
hl[attribute] = xml.attributes().value(attribute).toString();
}
@@ -586,87 +2884,50 @@ int main(int argc, char *argv[])
hl[QStringLiteral("priority")] = xml.attributes().value(QLatin1String("priority")).toInt();
// add boolean one
- const QString hidden = xml.attributes().value(QLatin1String("hidden")).toString();
- hl[QStringLiteral("hidden")] = (hidden == QLatin1String("true") || hidden == QLatin1String("1"));
+ hl[QStringLiteral("hidden")] = attrToBool(xml.attributes().value(QLatin1String("hidden")));
+
+ // keep some strings as UTF-8 for faster translations
+ hl[QStringLiteral("nameUtf8")] = hl[QStringLiteral("name")].toString().toUtf8();
+ hl[QStringLiteral("sectionUtf8")] = hl[QStringLiteral("section")].toString().toUtf8();
// remember hl
hls[QFileInfo(hlFile).fileName()] = hl;
- AttributeChecker attributeChecker(hlFilename);
- KeywordChecker keywordChecker(hlFilename);
const QString hlName = hl[QStringLiteral("name")].toString();
+ filesChecker.setDefinition(xml.attributes().value(QStringLiteral("kateversion")), hlFilename, hlName);
+
// scan for broken regex or keywords with spaces
while (!xml.atEnd()) {
xml.readNext();
- if (!xml.isStartElement()) {
- continue;
- }
-
- // search for used/existing contexts if applicable
- contextChecker.processElement(hlFilename, hlName, xml);
-
- // search for existing keyword includes
- keywordIncludeChecker.processElement(hlFilename, hlName, xml);
-
- // search for used/existing attributes if applicable
- attributeChecker.processElement(xml);
-
- // search for used/existing keyword lists if applicable
- keywordChecker.processElement(xml);
-
- // scan for bad regex
- if (!checkRegularExpression(hlFilename, xml)) {
- anyError = 7;
- continue;
- }
-
- // scan for bogus <item> lala </item> spaces
- if (!checkItemsTrimmed(hlFilename, xml)) {
- anyError = 8;
- continue;
- }
-
- // check single chars in DetectChar and Detect2Chars
- if (!checkSingleChars(hlFilename, xml)) {
- anyError = 8;
- continue;
- }
-
- // scan for lookAhead="true" with context="#stay"
- if (!checkLookAhead(hlFilename, xml)) {
- anyError = 7;
- continue;
- }
+ filesChecker.processElement(xml);
}
- if (!attributeChecker.check()) {
- anyError = 7;
- }
-
- if (!keywordChecker.check()) {
- anyError = 7;
+ if (xml.hasError()) {
+ anyError = 33;
+ qWarning() << hlFilename << "-" << xml.errorString() << "@ offset" << xml.characterOffset();
}
}
- if (!contextChecker.check())
- anyError = 7;
+ filesChecker.resolveContexts();
- if (!keywordIncludeChecker.check())
+ if (!filesChecker.check()) {
anyError = 7;
-
+ }
// bail out if any problem was seen
- if (anyError)
+ if (anyError) {
return anyError;
+ }
// create outfile, after all has worked!
QFile outFile(app.arguments().at(1));
- if (!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
+ if (!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
return 9;
+ }
// write out json
- outFile.write(QJsonDocument::fromVariant(QVariant(hls)).toBinaryData());
+ outFile.write(QCborValue::fromVariant(QVariant(hls)).toCbor());
// be done
return 0;
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt
index 95bf4c349e..2b2845eba4 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt
@@ -1,13 +1,23 @@
-ecm_create_qm_loader(syntax_highlighting_QM_LOADER syntaxhighlighting5_qt)
+add_library(KF6SyntaxHighlighting)
-set(syntax_highlighting_srcs
+set_target_properties(KF6SyntaxHighlighting PROPERTIES
+ VERSION ${KSYNTAXHIGHLIGHTING_VERSION}
+ SOVERSION ${KSYNTAXHIGHLIGHTING_SOVERSION}
+ EXPORT_NAME SyntaxHighlighting
+)
+
+ecm_create_qm_loader(syntax_highlighting_QM_LOADER syntaxhighlighting6_qt)
+
+target_sources(KF6SyntaxHighlighting PRIVATE
abstracthighlighter.cpp
context.cpp
contextswitch.cpp
definitiondownloader.cpp
+ highlightingdata.cpp
foldingregion.cpp
format.cpp
htmlhighlighter.cpp
+ ansihighlighter.cpp
keywordlist.cpp
rule.cpp
definition.cpp
@@ -17,58 +27,84 @@ set(syntax_highlighting_srcs
theme.cpp
wildcardmatcher.cpp
themedata.cpp
+ worddelimiters.cpp
${syntax_highlighting_QM_LOADER}
+ $<TARGET_OBJECTS:SyntaxHighlightingData>
)
-ecm_qt_declare_logging_category(syntax_highlighting_srcs
+ecm_qt_declare_logging_category(KF6SyntaxHighlighting
HEADER ksyntaxhighlighting_logging.h
IDENTIFIER KSyntaxHighlighting::Log
- CATEGORY_NAME org.kde.ksyntaxhighlighting
+ CATEGORY_NAME kf.syntaxhighlighting
+ OLD_CATEGORY_NAMES org.kde.ksyntaxhighlighting
+ DESCRIPTION "Syntax Highlighting"
+ EXPORT KSYNTAXHIGHLIGHTING
)
-add_library(KF5SyntaxHighlighting ${syntax_highlighting_srcs} $<TARGET_OBJECTS:SyntaxHighlightingData>)
-generate_export_header(KF5SyntaxHighlighting BASE_NAME KSyntaxHighlighting)
-set_target_properties(KF5SyntaxHighlighting PROPERTIES
- VERSION ${SyntaxHighlighting_VERSION_STRING}
- SOVERSION ${SyntaxHighlighting_SOVERSION}
- EXPORT_NAME SyntaxHighlighting
+ecm_generate_export_header(KF6SyntaxHighlighting
+ BASE_NAME KSyntaxHighlighting
+ GROUP_BASE_NAME KF
+ VERSION ${KF_VERSION}
+ USE_VERSION_HEADER
+ DEPRECATED_BASE_VERSION 0
+ DEPRECATION_VERSIONS
+ EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
+)
+
+target_link_libraries(KF6SyntaxHighlighting
+ PUBLIC
+ Qt6::Gui
+ PRIVATE
+ Qt6::Network
)
-target_include_directories(KF5SyntaxHighlighting INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF5}/KSyntaxHighlighting;${KDE_INSTALL_INCLUDEDIR_KF5}>")
-target_include_directories(KF5SyntaxHighlighting PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR};>")
-target_link_libraries(KF5SyntaxHighlighting LINK_PUBLIC Qt5::Gui LINK_PRIVATE Qt5::Network)
-ecm_generate_headers(SyntaxHighlighting_HEADERS
- HEADER_NAMES
- AbstractHighlighter
- Definition
- DefinitionDownloader
- FoldingRegion
- Format
- Repository
- State
- SyntaxHighlighter
- Theme
+set(Forwarding_Header_Names
+ AbstractHighlighter
+ Definition
+ DefinitionDownloader
+ FoldingRegion
+ Format
+ Repository
+ State
+ SyntaxHighlighter
+ Theme
+ WildcardMatcher
+)
+
+ecm_generate_headers(CamelCase_HEADERS
+ HEADER_NAMES ${Forwarding_Header_Names}
REQUIRED_HEADERS SyntaxHighlighting_HEADERS
+ OUTPUT_DIR ${CMAKE_BINARY_DIR}/KSyntaxHighlighting/KSyntaxHighlighting
+)
+
+target_include_directories(KF6SyntaxHighlighting
+ INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KSyntaxHighlighting>"
+ PUBLIC "$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/KSyntaxHighlighting;>"
)
-install(TARGETS KF5SyntaxHighlighting EXPORT KF5SyntaxHighlightingTargets ${INSTALL_TARGETS_DEFAULT_ARGS})
+install(TARGETS KF6SyntaxHighlighting EXPORT KF6SyntaxHighlightingTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
+
install(FILES
+ ${CamelCase_HEADERS}
${SyntaxHighlighting_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/ksyntaxhighlighting_export.h
- DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KSyntaxHighlighting)
+ DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KSyntaxHighlighting/KSyntaxHighlighting
+)
if(BUILD_QCH)
ecm_add_qch(
- KF5SyntaxHighlighting_QCH
+ KF6SyntaxHighlighting_QCH
NAME KSyntaxHighlighting
- BASE_NAME KF5SyntaxHighlighting
- VERSION ${KF5_VERSION}
+ BASE_NAME KF6SyntaxHighlighting
+ VERSION ${KF_VERSION}
ORG_DOMAIN org.kde
SOURCES # using only public headers, to cover only public API
${SyntaxHighlighting_HEADERS}
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
LINK_QCHS
- Qt5Core_QCH
- Qt5Gui_QCH
+ Qt6Core_QCH
+ Qt6Gui_QCH
+ INCLUDE_DIRS
+ ${CMAKE_CURRENT_BINARY_DIR}
BLANK_MACROS
KSYNTAXHIGHLIGHTING_EXPORT
KSYNTAXHIGHLIGHTING_DEPRECATED
@@ -77,11 +113,3 @@ if(BUILD_QCH)
COMPONENT Devel
)
endif()
-ecm_generate_pri_file(
- BASE_NAME KSyntaxHighlighting LIB_NAME
- KF5SyntaxHighlighting
- DEPS "gui"
- FILENAME_VAR PRI_FILENAME
- INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KSyntaxHighlighting
-)
-install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.cpp
index c4ef86a771..87dabadc7b 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.cpp
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#include "abstracthighlighter.h"
@@ -27,11 +10,12 @@
#include "definition_p.h"
#include "foldingregion.h"
#include "format.h"
+#include "ksyntaxhighlighting_logging.h"
#include "repository.h"
+#include "repository_p.h"
#include "rule_p.h"
#include "state.h"
#include "state_p.h"
-#include "ksyntaxhighlighting_logging.h"
#include "theme.h"
using namespace KSyntaxHighlighting;
@@ -53,20 +37,22 @@ void AbstractHighlighterPrivate::ensureDefinitionLoaded()
defData = DefinitionData::get(m_definition);
}
- if (Q_UNLIKELY(!defData->repo && !defData->fileName.isEmpty()))
+ if (Q_UNLIKELY(!defData->repo && !defData->fileName.isEmpty())) {
qCCritical(Log) << "Repository got deleted while a highlighter is still active!";
+ }
- if (m_definition.isValid())
+ if (m_definition.isValid()) {
defData->load();
+ }
}
-AbstractHighlighter::AbstractHighlighter() :
- d_ptr(new AbstractHighlighterPrivate)
+AbstractHighlighter::AbstractHighlighter()
+ : d_ptr(new AbstractHighlighterPrivate)
{
}
-AbstractHighlighter::AbstractHighlighter(AbstractHighlighterPrivate *dd) :
- d_ptr(dd)
+AbstractHighlighter::AbstractHighlighter(AbstractHighlighterPrivate *dd)
+ : d_ptr(dd)
{
}
@@ -102,7 +88,7 @@ void AbstractHighlighter::setTheme(const Theme &theme)
* Returns the index of the first non-space character. If the line is empty,
* or only contains white spaces, text.size() is returned.
*/
-static inline int firstNonSpaceChar(const QString & text)
+static inline int firstNonSpaceChar(QStringView text)
{
for (int i = 0; i < text.length(); ++i) {
if (!text[i].isSpace()) {
@@ -112,7 +98,7 @@ static inline int firstNonSpaceChar(const QString & text)
return text.size();
}
-State AbstractHighlighter::highlightLine(const QString& text, const State &state)
+State AbstractHighlighter::highlightLine(QStringView text, const State &state)
{
Q_D(AbstractHighlighter);
@@ -124,45 +110,49 @@ State AbstractHighlighter::highlightLine(const QString& text, const State &state
return State();
}
+ // limit the cache for unification to some reasonable size
+ // we use here at the moment 64k elements to not hog too much memory
+ // and to make the clearing no big stall
+ if (defData->unify.size() > 64 * 1024)
+ defData->unify.clear();
+
// verify/initialize state
auto newState = state;
auto stateData = StateData::get(newState);
- const DefinitionRef currentDefRef(d->m_definition);
- if (!stateData->isEmpty() && (stateData->m_defRef != currentDefRef)) {
+ bool isSharedData = true;
+ if (Q_UNLIKELY(stateData && stateData->m_defId != defData->id)) {
qCDebug(Log) << "Got invalid state, resetting.";
- stateData->clear();
+ stateData = nullptr;
}
- if (stateData->isEmpty()) {
+ if (Q_UNLIKELY(!stateData)) {
+ stateData = StateData::reset(newState);
stateData->push(defData->initialContext(), QStringList());
- stateData->m_defRef = currentDefRef;
+ stateData->m_defId = defData->id;
+ isSharedData = false;
}
// process empty lines
- if (text.isEmpty()) {
+ if (Q_UNLIKELY(text.isEmpty())) {
/**
* handle line empty context switches
* guard against endless loops
* see https://phabricator.kde.org/D18509
*/
int endlessLoopingCounter = 0;
- while (!stateData->topContext()->lineEmptyContext().isStay() || (stateData->topContext()->lineEmptyContext().isStay() && !stateData->topContext()->lineEndContext().isStay())) {
+ while (!stateData->topContext()->lineEmptyContext().isStay()) {
/**
* line empty context switches
*/
- if (!stateData->topContext()->lineEmptyContext().isStay()) {
- if (!d->switchContext(stateData, stateData->topContext()->lineEmptyContext(), QStringList())) {
- /**
- * end when trying to #pop the main context
- */
- break;
- }
- /**
- * line end context switches only when lineEmptyContext is #stay. This avoids
- * skipping empty lines after a line continuation character (see bug 405903)
- */
- } else if (!stateData->topContext()->lineEndContext().isStay() &&
- !d->switchContext(stateData, stateData->topContext()->lineEndContext(), QStringList()))
+ if (!d->switchContext(stateData, stateData->topContext()->lineEmptyContext(), QStringList(), newState, isSharedData)) {
+ /**
+ * end when trying to #pop the main context
+ */
break;
+ }
+
+ if (stateData->topContext()->stopEmptyLineContextSwitchLoop()) {
+ break;
+ }
// guard against endless loops
++endlessLoopingCounter;
@@ -173,12 +163,43 @@ State AbstractHighlighter::highlightLine(const QString& text, const State &state
}
auto context = stateData->topContext();
applyFormat(0, 0, context->attributeFormat());
- return newState;
+ return *defData->unify.insert(newState);
}
- int offset = 0, beginOffset = 0;
+ auto &dynamicRegexpCache = RepositoryPrivate::get(defData->repo)->m_dynamicRegexpCache;
+
+ int offset = 0;
+ int beginOffset = 0;
bool lineContinuation = false;
- QHash<Rule*, int> skipOffsets;
+
+ /**
+ * for expensive rules like regexes we do:
+ * - match them for the complete line, as this is faster than re-trying them at all positions
+ * - store the result of the first position that matches (or -1 for no match in the full line) in the skipOffsets hash for re-use
+ * - have capturesForLastDynamicSkipOffset as guard for dynamic regexes to invalidate the cache if they might have changed
+ */
+ QVarLengthArray<QPair<const Rule *, int>, 8> skipOffsets;
+ QStringList capturesForLastDynamicSkipOffset;
+
+ auto getSkipOffsetValue = [&skipOffsets](const Rule *r) -> int {
+ auto i = std::find_if(skipOffsets.begin(), skipOffsets.end(), [r](const auto &v) {
+ return v.first == r;
+ });
+ if (i == skipOffsets.end())
+ return 0;
+ return i->second;
+ };
+
+ auto insertSkipOffset = [&skipOffsets](const Rule *r, int i) {
+ auto it = std::find_if(skipOffsets.begin(), skipOffsets.end(), [r](const auto &v) {
+ return v.first == r;
+ });
+ if (it == skipOffsets.end()) {
+ skipOffsets.push_back({r, i});
+ } else {
+ it->second = i;
+ }
+ };
/**
* current active format
@@ -216,7 +237,8 @@ State AbstractHighlighter::highlightLine(const QString& text, const State &state
bool isLookAhead = false;
int newOffset = 0;
const Format *newFormat = nullptr;
- for (const auto &rule : stateData->topContext()->rules()) {
+ for (const auto &ruleShared : stateData->topContext()->rules()) {
+ auto rule = ruleShared.get();
/**
* filter out rules that require a specific column
*/
@@ -244,27 +266,43 @@ State AbstractHighlighter::highlightLine(const QString& text, const State &state
}
}
- /**
- * shall we skip application of this rule? two cases:
- * - rule can't match at all => currentSkipOffset < 0
- * - rule will only match for some higher offset => currentSkipOffset > offset
- */
- const auto currentSkipOffset = skipOffsets.value(rule.get());
- if (currentSkipOffset < 0 || currentSkipOffset > offset)
- continue;
-
+ int currentSkipOffset = 0;
+ if (Q_UNLIKELY(rule->hasSkipOffset())) {
+ /**
+ * shall we skip application of this rule? two cases:
+ * - rule can't match at all => currentSkipOffset < 0
+ * - rule will only match for some higher offset => currentSkipOffset > offset
+ *
+ * we need to invalidate this if we are dynamic and have different captures then last time
+ */
+ if (rule->isDynamic() && (capturesForLastDynamicSkipOffset != stateData->topCaptures())) {
+ skipOffsets.clear();
+ } else {
+ currentSkipOffset = getSkipOffsetValue(rule);
+ if (currentSkipOffset < 0 || currentSkipOffset > offset) {
+ continue;
+ }
+ }
+ }
- const auto newResult = rule->doMatch(text, offset, stateData->topCaptures());
+ auto newResult = rule->doMatch(text, offset, stateData->topCaptures(), dynamicRegexpCache);
newOffset = newResult.offset();
/**
* update skip offset if new one rules out any later match or is larger than current one
*/
- if (newResult.skipOffset() < 0 || newResult.skipOffset() > currentSkipOffset)
- skipOffsets.insert(rule.get(), newResult.skipOffset());
+ if (newResult.skipOffset() < 0 || newResult.skipOffset() > currentSkipOffset) {
+ insertSkipOffset(rule, newResult.skipOffset());
- if (newOffset <= offset)
+ // remember new captures, if dynamic to enforce proper reset above on change!
+ if (rule->isDynamic()) {
+ capturesForLastDynamicSkipOffset = stateData->topCaptures();
+ }
+ }
+
+ if (newOffset <= offset) {
continue;
+ }
/**
* apply folding.
@@ -272,32 +310,36 @@ State AbstractHighlighter::highlightLine(const QString& text, const State &state
* - rule with endRegion + beginRegion: in endRegion, the length is 0
* - rule with lookAhead: length is 0
*/
- if (rule->endRegion().isValid() && rule->beginRegion().isValid())
+ if (rule->endRegion().isValid() && rule->beginRegion().isValid()) {
applyFolding(offset, 0, rule->endRegion());
- else if (rule->endRegion().isValid())
+ } else if (rule->endRegion().isValid()) {
applyFolding(offset, rule->isLookAhead() ? 0 : newOffset - offset, rule->endRegion());
- if (rule->beginRegion().isValid())
+ }
+ if (rule->beginRegion().isValid()) {
applyFolding(offset, rule->isLookAhead() ? 0 : newOffset - offset, rule->beginRegion());
+ }
if (rule->isLookAhead()) {
Q_ASSERT(!rule->context().isStay());
- d->switchContext(stateData, rule->context(), newResult.captures());
+ d->switchContext(stateData, rule->context(), std::move(newResult.captures()), newState, isSharedData);
isLookAhead = true;
break;
}
- d->switchContext(stateData, rule->context(), newResult.captures());
+ d->switchContext(stateData, rule->context(), std::move(newResult.captures()), newState, isSharedData);
newFormat = rule->attributeFormat().isValid() ? &rule->attributeFormat() : &stateData->topContext()->attributeFormat();
- if (newOffset == text.size() && std::dynamic_pointer_cast<LineContinue>(rule))
+ if (newOffset == text.size() && rule->isLineContinue()) {
lineContinuation = true;
+ }
break;
}
- if (isLookAhead)
+ if (isLookAhead) {
continue;
+ }
if (newOffset <= offset) { // no matching rule
if (stateData->topContext()->fallthrough()) {
- d->switchContext(stateData, stateData->topContext()->fallthroughContext(), QStringList());
+ d->switchContext(stateData, stateData->topContext()->fallthroughContext(), QStringList(), newState, isSharedData);
continue;
}
@@ -314,8 +356,9 @@ State AbstractHighlighter::highlightLine(const QString& text, const State &state
* on format change, apply the last one and switch to new one
*/
if (newFormat != currentFormat && newFormat->id() != currentFormat->id()) {
- if (offset > 0)
+ if (offset > 0) {
applyFormat(beginOffset, offset - beginOffset, *currentFormat);
+ }
beginOffset = offset;
currentFormat = newFormat;
}
@@ -331,8 +374,9 @@ State AbstractHighlighter::highlightLine(const QString& text, const State &state
/**
* apply format for remaining text, if any
*/
- if (beginOffset < offset)
+ if (beginOffset < offset) {
applyFormat(beginOffset, text.size() - beginOffset, *currentFormat);
+ }
/**
* handle line end context switches
@@ -342,8 +386,9 @@ State AbstractHighlighter::highlightLine(const QString& text, const State &state
{
int endlessLoopingCounter = 0;
while (!stateData->topContext()->lineEndContext().isStay() && !lineContinuation) {
- if (!d->switchContext(stateData, stateData->topContext()->lineEndContext(), QStringList()))
+ if (!d->switchContext(stateData, stateData->topContext()->lineEndContext(), QStringList(), newState, isSharedData)) {
break;
+ }
// guard against endless loops
++endlessLoopingCounter;
@@ -354,18 +399,30 @@ State AbstractHighlighter::highlightLine(const QString& text, const State &state
}
}
- return newState;
+ return *defData->unify.insert(newState);
}
-bool AbstractHighlighterPrivate::switchContext(StateData *data, const ContextSwitch &contextSwitch, const QStringList &captures)
+bool AbstractHighlighterPrivate::switchContext(StateData *&data, const ContextSwitch &contextSwitch, QStringList &&captures, State &state, bool &isSharedData)
{
+ const auto popCount = contextSwitch.popCount();
+ const auto context = contextSwitch.context();
+ if (popCount <= 0 && !context) {
+ return true;
+ }
+
+ // a modified state must be detached before modification
+ if (isSharedData) {
+ data = StateData::detach(state);
+ isSharedData = false;
+ }
+
// kill as many items as requested from the stack, will always keep the initial context alive!
- const bool initialContextSurvived = data->pop(contextSwitch.popCount());
+ const bool initialContextSurvived = data->pop(popCount);
// if we have a new context to add, push it
// then we always "succeed"
- if (contextSwitch.context()) {
- data->push(contextSwitch.context(), captures);
+ if (context) {
+ data->push(context, std::move(captures));
return true;
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h
index 2b88729697..676a0f522a 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h
@@ -1,43 +1,21 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_ABSTRACTHIGHLIGHTERM_H
#define KSYNTAXHIGHLIGHTING_ABSTRACTHIGHLIGHTERM_H
+#include "definition.h"
#include "ksyntaxhighlighting_export.h"
#include <QObject>
+#include <QStringView>
-#include <memory>
-
-QT_BEGIN_NAMESPACE
-class QString;
-QT_END_NAMESPACE
-
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class AbstractHighlighterPrivate;
-class Definition;
class FoldingRegion;
class Format;
class State;
@@ -123,12 +101,8 @@ public:
protected:
AbstractHighlighter();
- AbstractHighlighter(AbstractHighlighterPrivate *dd);
+ KSYNTAXHIGHLIGHTING_NO_EXPORT explicit AbstractHighlighter(AbstractHighlighterPrivate *dd);
- // TODO KF6: add an optional void* context argument that is passed through
- // to the applyX() calls, so highlighters dealing with some form of line object
- // (such as QSyntaxHighlighter or KTextEditor) can avoid some ugly hacks to have
- // this context available in their applyX methods
/**
* Highlight the given line. Call this from your derived class
* where appropriate. This will result in any number of applyFormat()
@@ -137,14 +111,14 @@ protected:
* @param state The highlighting state handle returned by the call
* to highlightLine() for the previous line. For the very first line,
* just pass a default constructed State().
- * @returns The state of the highlighing engine after processing the
+ * @returns The state of the highlighting engine after processing the
* given line. This needs to passed into highlightLine() for the
* next line. You can store the state for efficient partial
* re-highlighting for example during editing.
*
* @see applyFormat(), applyFolding()
*/
- State highlightLine(const QString &text, const State &state);
+ State highlightLine(QStringView text, const State &state);
/**
* Reimplement this to apply formats to your output. The provided @p format
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h
index bdfdf23bc1..04ac9898f8 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_ABSTRACTHIGHLIGHTER_P_H
@@ -27,14 +10,11 @@
#include "definition.h"
#include "theme.h"
-QT_BEGIN_NAMESPACE
-class QStringList;
-QT_END_NAMESPACE
-
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class ContextSwitch;
class StateData;
+class State;
class AbstractHighlighterPrivate
{
@@ -43,7 +23,7 @@ public:
virtual ~AbstractHighlighterPrivate();
void ensureDefinitionLoaded();
- bool switchContext(StateData* data, const ContextSwitch &contextSwitch, const QStringList &captures);
+ bool switchContext(StateData *&data, const ContextSwitch &contextSwitch, QStringList &&captures, State &state, bool &isSharedData);
Definition m_definition;
Theme m_theme;
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.cpp
new file mode 100644
index 0000000000..e9bea02d1c
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.cpp
@@ -0,0 +1,1422 @@
+/*
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#include "ansihighlighter.h"
+#include "abstracthighlighter_p.h"
+#include "context_p.h"
+#include "definition.h"
+#include "definition_p.h"
+#include "format.h"
+#include "ksyntaxhighlighting_logging.h"
+#include "state.h"
+#include "state_p.h"
+#include "theme.h"
+
+#include <QColor>
+#include <QFile>
+#include <QFileInfo>
+#include <QHash>
+#include <QIODevice>
+#include <QTextStream>
+
+#include <cmath>
+#include <vector>
+
+using namespace KSyntaxHighlighting;
+
+namespace
+{
+struct Lab {
+ double L;
+ double a;
+ double b;
+};
+
+// clang-format off
+ // xterm color reference
+ // constexpr Rgb888 xterm256Colors[] {
+ // {0x00, 0x00, 0x00}, {0x80, 0x00, 0x00}, {0x00, 0x80, 0x00}, {0x80, 0x80, 0x00},
+ // {0x00, 0x00, 0x80}, {0x80, 0x00, 0x80}, {0x00, 0x80, 0x80}, {0xc0, 0xc0, 0xc0},
+ // {0x80, 0x80, 0x80}, {0xff, 0x00, 0x00}, {0x00, 0xff, 0x00}, {0xff, 0xff, 0x00},
+ // {0x00, 0x00, 0xff}, {0xff, 0x00, 0xff}, {0x00, 0xff, 0xff}, {0xff, 0xff, 0xff},
+ // {0x00, 0x00, 0x00}, {0x00, 0x00, 0x5f}, {0x00, 0x00, 0x87}, {0x00, 0x00, 0xaf},
+ // {0x00, 0x00, 0xd7}, {0x00, 0x00, 0xff}, {0x00, 0x5f, 0x00}, {0x00, 0x5f, 0x5f},
+ // {0x00, 0x5f, 0x87}, {0x00, 0x5f, 0xaf}, {0x00, 0x5f, 0xd7}, {0x00, 0x5f, 0xff},
+ // {0x00, 0x87, 0x00}, {0x00, 0x87, 0x5f}, {0x00, 0x87, 0x87}, {0x00, 0x87, 0xaf},
+ // {0x00, 0x87, 0xd7}, {0x00, 0x87, 0xff}, {0x00, 0xaf, 0x00}, {0x00, 0xaf, 0x5f},
+ // {0x00, 0xaf, 0x87}, {0x00, 0xaf, 0xaf}, {0x00, 0xaf, 0xd7}, {0x00, 0xaf, 0xff},
+ // {0x00, 0xd7, 0x00}, {0x00, 0xd7, 0x5f}, {0x00, 0xd7, 0x87}, {0x00, 0xd7, 0xaf},
+ // {0x00, 0xd7, 0xd7}, {0x00, 0xd7, 0xff}, {0x00, 0xff, 0x00}, {0x00, 0xff, 0x5f},
+ // {0x00, 0xff, 0x87}, {0x00, 0xff, 0xaf}, {0x00, 0xff, 0xd7}, {0x00, 0xff, 0xff},
+ // {0x5f, 0x00, 0x00}, {0x5f, 0x00, 0x5f}, {0x5f, 0x00, 0x87}, {0x5f, 0x00, 0xaf},
+ // {0x5f, 0x00, 0xd7}, {0x5f, 0x00, 0xff}, {0x5f, 0x5f, 0x00}, {0x5f, 0x5f, 0x5f},
+ // {0x5f, 0x5f, 0x87}, {0x5f, 0x5f, 0xaf}, {0x5f, 0x5f, 0xd7}, {0x5f, 0x5f, 0xff},
+ // {0x5f, 0x87, 0x00}, {0x5f, 0x87, 0x5f}, {0x5f, 0x87, 0x87}, {0x5f, 0x87, 0xaf},
+ // {0x5f, 0x87, 0xd7}, {0x5f, 0x87, 0xff}, {0x5f, 0xaf, 0x00}, {0x5f, 0xaf, 0x5f},
+ // {0x5f, 0xaf, 0x87}, {0x5f, 0xaf, 0xaf}, {0x5f, 0xaf, 0xd7}, {0x5f, 0xaf, 0xff},
+ // {0x5f, 0xd7, 0x00}, {0x5f, 0xd7, 0x5f}, {0x5f, 0xd7, 0x87}, {0x5f, 0xd7, 0xaf},
+ // {0x5f, 0xd7, 0xd7}, {0x5f, 0xd7, 0xff}, {0x5f, 0xff, 0x00}, {0x5f, 0xff, 0x5f},
+ // {0x5f, 0xff, 0x87}, {0x5f, 0xff, 0xaf}, {0x5f, 0xff, 0xd7}, {0x5f, 0xff, 0xff},
+ // {0x87, 0x00, 0x00}, {0x87, 0x00, 0x5f}, {0x87, 0x00, 0x87}, {0x87, 0x00, 0xaf},
+ // {0x87, 0x00, 0xd7}, {0x87, 0x00, 0xff}, {0x87, 0x5f, 0x00}, {0x87, 0x5f, 0x5f},
+ // {0x87, 0x5f, 0x87}, {0x87, 0x5f, 0xaf}, {0x87, 0x5f, 0xd7}, {0x87, 0x5f, 0xff},
+ // {0x87, 0x87, 0x00}, {0x87, 0x87, 0x5f}, {0x87, 0x87, 0x87}, {0x87, 0x87, 0xaf},
+ // {0x87, 0x87, 0xd7}, {0x87, 0x87, 0xff}, {0x87, 0xaf, 0x00}, {0x87, 0xaf, 0x5f},
+ // {0x87, 0xaf, 0x87}, {0x87, 0xaf, 0xaf}, {0x87, 0xaf, 0xd7}, {0x87, 0xaf, 0xff},
+ // {0x87, 0xd7, 0x00}, {0x87, 0xd7, 0x5f}, {0x87, 0xd7, 0x87}, {0x87, 0xd7, 0xaf},
+ // {0x87, 0xd7, 0xd7}, {0x87, 0xd7, 0xff}, {0x87, 0xff, 0x00}, {0x87, 0xff, 0x5f},
+ // {0x87, 0xff, 0x87}, {0x87, 0xff, 0xaf}, {0x87, 0xff, 0xd7}, {0x87, 0xff, 0xff},
+ // {0xaf, 0x00, 0x00}, {0xaf, 0x00, 0x5f}, {0xaf, 0x00, 0x87}, {0xaf, 0x00, 0xaf},
+ // {0xaf, 0x00, 0xd7}, {0xaf, 0x00, 0xff}, {0xaf, 0x5f, 0x00}, {0xaf, 0x5f, 0x5f},
+ // {0xaf, 0x5f, 0x87}, {0xaf, 0x5f, 0xaf}, {0xaf, 0x5f, 0xd7}, {0xaf, 0x5f, 0xff},
+ // {0xaf, 0x87, 0x00}, {0xaf, 0x87, 0x5f}, {0xaf, 0x87, 0x87}, {0xaf, 0x87, 0xaf},
+ // {0xaf, 0x87, 0xd7}, {0xaf, 0x87, 0xff}, {0xaf, 0xaf, 0x00}, {0xaf, 0xaf, 0x5f},
+ // {0xaf, 0xaf, 0x87}, {0xaf, 0xaf, 0xaf}, {0xaf, 0xaf, 0xd7}, {0xaf, 0xaf, 0xff},
+ // {0xaf, 0xd7, 0x00}, {0xaf, 0xd7, 0x5f}, {0xaf, 0xd7, 0x87}, {0xaf, 0xd7, 0xaf},
+ // {0xaf, 0xd7, 0xd7}, {0xaf, 0xd7, 0xff}, {0xaf, 0xff, 0x00}, {0xaf, 0xff, 0x5f},
+ // {0xaf, 0xff, 0x87}, {0xaf, 0xff, 0xaf}, {0xaf, 0xff, 0xd7}, {0xaf, 0xff, 0xff},
+ // {0xd7, 0x00, 0x00}, {0xd7, 0x00, 0x5f}, {0xd7, 0x00, 0x87}, {0xd7, 0x00, 0xaf},
+ // {0xd7, 0x00, 0xd7}, {0xd7, 0x00, 0xff}, {0xd7, 0x5f, 0x00}, {0xd7, 0x5f, 0x5f},
+ // {0xd7, 0x5f, 0x87}, {0xd7, 0x5f, 0xaf}, {0xd7, 0x5f, 0xd7}, {0xd7, 0x5f, 0xff},
+ // {0xd7, 0x87, 0x00}, {0xd7, 0x87, 0x5f}, {0xd7, 0x87, 0x87}, {0xd7, 0x87, 0xaf},
+ // {0xd7, 0x87, 0xd7}, {0xd7, 0x87, 0xff}, {0xd7, 0xaf, 0x00}, {0xd7, 0xaf, 0x5f},
+ // {0xd7, 0xaf, 0x87}, {0xd7, 0xaf, 0xaf}, {0xd7, 0xaf, 0xd7}, {0xd7, 0xaf, 0xff},
+ // {0xd7, 0xd7, 0x00}, {0xd7, 0xd7, 0x5f}, {0xd7, 0xd7, 0x87}, {0xd7, 0xd7, 0xaf},
+ // {0xd7, 0xd7, 0xd7}, {0xd7, 0xd7, 0xff}, {0xd7, 0xff, 0x00}, {0xd7, 0xff, 0x5f},
+ // {0xd7, 0xff, 0x87}, {0xd7, 0xff, 0xaf}, {0xd7, 0xff, 0xd7}, {0xd7, 0xff, 0xff},
+ // {0xff, 0x00, 0x00}, {0xff, 0x00, 0x5f}, {0xff, 0x00, 0x87}, {0xff, 0x00, 0xaf},
+ // {0xff, 0x00, 0xd7}, {0xff, 0x00, 0xff}, {0xff, 0x5f, 0x00}, {0xff, 0x5f, 0x5f},
+ // {0xff, 0x5f, 0x87}, {0xff, 0x5f, 0xaf}, {0xff, 0x5f, 0xd7}, {0xff, 0x5f, 0xff},
+ // {0xff, 0x87, 0x00}, {0xff, 0x87, 0x5f}, {0xff, 0x87, 0x87}, {0xff, 0x87, 0xaf},
+ // {0xff, 0x87, 0xd7}, {0xff, 0x87, 0xff}, {0xff, 0xaf, 0x00}, {0xff, 0xaf, 0x5f},
+ // {0xff, 0xaf, 0x87}, {0xff, 0xaf, 0xaf}, {0xff, 0xaf, 0xd7}, {0xff, 0xaf, 0xff},
+ // {0xff, 0xd7, 0x00}, {0xff, 0xd7, 0x5f}, {0xff, 0xd7, 0x87}, {0xff, 0xd7, 0xaf},
+ // {0xff, 0xd7, 0xd7}, {0xff, 0xd7, 0xff}, {0xff, 0xff, 0x00}, {0xff, 0xff, 0x5f},
+ // {0xff, 0xff, 0x87}, {0xff, 0xff, 0xaf}, {0xff, 0xff, 0xd7}, {0xff, 0xff, 0xff},
+ // {0x08, 0x08, 0x08}, {0x12, 0x12, 0x12}, {0x1c, 0x1c, 0x1c}, {0x26, 0x26, 0x26},
+ // {0x30, 0x30, 0x30}, {0x3a, 0x3a, 0x3a}, {0x44, 0x44, 0x44}, {0x4e, 0x4e, 0x4e},
+ // {0x58, 0x58, 0x58}, {0x62, 0x62, 0x62}, {0x6c, 0x6c, 0x6c}, {0x76, 0x76, 0x76},
+ // {0x80, 0x80, 0x80}, {0x8a, 0x8a, 0x8a}, {0x94, 0x94, 0x94}, {0x9e, 0x9e, 0x9e},
+ // {0xa8, 0xa8, 0xa8}, {0xb2, 0xb2, 0xb2}, {0xbc, 0xbc, 0xbc}, {0xc6, 0xc6, 0xc6},
+ // {0xd0, 0xd0, 0xd0}, {0xda, 0xda, 0xda}, {0xe4, 0xe4, 0xe4}, {0xee, 0xee, 0xee},
+ // };
+
+ // xterm color represented in Oklab
+ // see rgbToOklab()
+ constexpr Lab xterm240_Oklabs[] {
+ // ignore the first 16 colors as they are unpredictable (user configurable)
+ // {0x0p+0, 0x0p+0, 0x0p+0},
+ // {0x1.2d5e6bee2c4f6p+5, 0x1.af99c042ea40cp+3, 0x1.e2f6ba84d2d25p+2},
+ // {0x1.9fcc4f3622914p+5, -0x1.c105bf1d2d218p+3, 0x1.586870aec30a4p+3},
+ // {0x1.d089126c75579p+5, -0x1.12107f2e3119p+2, 0x1.7d020b82d4b9cp+3},
+ // {0x1.b1ce15c4fcb51p+4, -0x1.f203762eb1242p+0, -0x1.2b150ae3c14bep+4},
+ // {0x1.50bc446f31833p+5, 0x1.078a1150b431ap+4, -0x1.44d35b3de7eafp+3},
+ // {0x1.b27d96eb8f471p+5, -0x1.1ee8867b0e065p+3, -0x1.2f2f18261c69ap+1},
+ // {0x1.431e523cc2f4dp+6, -0x1.ad694c777b8p-11, -0x1.c7c3ea0c32ep-8},
+ // {0x1.dfe5855ae1528p+5, -0x1.3ee1ad40618p-11, -0x1.5273b9784a3p-8},
+ // {0x1.f663baac570efp+5, 0x1.67be9f690c994p+4, 0x1.928e76c3750aep+3},
+ // {0x1.5a92b1ff8af32p+6, -0x1.76441609cfb3ap+4, 0x1.1f1186319beaap+4},
+ // {0x1.83323c984ee7ap+6, -0x1.c8df58716d4cbp+2, 0x1.3d9335b5d20f9p+4},
+ // {0x1.69950098864afp+5, -0x1.9f19c42a8c674p+1, -0x1.f293e325bec2ep+4},
+ // {0x1.18ac5c68852cdp+6, 0x1.b753a9bd5dbeep+4, -0x1.0ebf0dff35bfbp+4},
+ // {0x1.6a27499e4d3d6p+6, -0x1.de4892c062f8p+3, -0x1.f96a59bc9de4cp+1},
+ // {0x1.8ffffbb77c76ap+6, -0x1.09cab717214p-10, -0x1.1a1aa7765a7p-7},
+ // 240 colors mode
+ {0x0p+0, 0x0p+0, 0x0p+0},
+ {0x1.5f181b2779cap+4, -0x1.930f78e22f09ap+0, -0x1.e41dbddfca08ap+3},
+ {0x1.c2d3be821f882p+4, -0x1.02c70dd8af008p+1, -0x1.36d1623919ffp+4},
+ {0x1.10a39beeb2926p+5, -0x1.38fe38b7dab01p+1, -0x1.77efa95d520b4p+4},
+ {0x1.3de43fe8d92efp+5, -0x1.6cf188320fff2p+1, -0x1.b655790a27192p+4},
+ {0x1.69950098864afp+5, -0x1.9f19c42a8c674p+1, -0x1.f293e325bec2ep+4},
+ {0x1.50853f46a9f9ep+5, -0x1.6b6901a80404fp+3, 0x1.16bdec11e60d8p+3},
+ {0x1.5fa625f2c3fbcp+5, -0x1.d0691ed5aa7ap+2, -0x1.eac16e8cb9241p+0},
+ {0x1.6f4222fc3f0dbp+5, -0x1.61b0e7ffa5e8ap+2, -0x1.05e8906ee23d7p+3},
+ {0x1.83e99e6187f46p+5, -0x1.1a61c98b895p+2, -0x1.c3da094bbcf2fp+3},
+ {0x1.9ca47689a503dp+5, -0x1.e9259e3439104p+1, -0x1.396812c634de3p+4},
+ {0x1.b872b55db6144p+5, -0x1.ccd50a2fd6662p+1, -0x1.89dec898ec996p+4},
+ {0x1.b01d15d276ef1p+5, -0x1.d2a4448f6ccacp+3, 0x1.65ec167dcb488p+3},
+ {0x1.b9760adf5c444p+5, -0x1.6a3760af4b6c4p+3, 0x1.a26bb322495e2p+1},
+ {0x1.c38a22a31944p+5, -0x1.2a2a93c4b741p+3, -0x1.3b14a376ffbecp+1},
+ {0x1.d185a36cfcd6ep+5, -0x1.e760f29e8fcb4p+2, -0x1.0bf936f2a6743p+3},
+ {0x1.e321d754f2b44p+5, -0x1.95a749a95debp+2, -0x1.c3928e31b9cf8p+3},
+ {0x1.f7eb8d9ad84b9p+5, -0x1.5e28b7cc193ddp+2, -0x1.38bda7259441ep+4},
+ {0x1.0552717cdb82p+6, -0x1.1a33f75f67b0fp+4, 0x1.b0e8bd24c3fdep+3},
+ {0x1.08898a5b7805dp+6, -0x1.e29bd3071d97ap+3, 0x1.e0b5994e0238ep+2},
+ {0x1.0c10ad1b87866p+6, -0x1.a54f2215371a5p+3, 0x1.45758e5457898p+1},
+ {0x1.1111e924447e9p+6, -0x1.68a24e1d5efe1p+3, -0x1.7d178a838ea32p+1},
+ {0x1.178bb94d30a5cp+6, -0x1.335d7d75dd017p+3, -0x1.13f78c003ba13p+3},
+ {0x1.1f6aa49fcff2p+6, -0x1.0830d167759a4p+3, -0x1.c578e33f21d88p+3},
+ {0x1.30b236b57ac86p+6, -0x1.490afbe3d8e11p+4, 0x1.f8c35fbb689dap+3},
+ {0x1.331144aae0ad4p+6, -0x1.289bac3eeb83p+4, 0x1.626ea3a63748p+3},
+ {0x1.35b09f5c62a7ap+6, -0x1.0d33e3db59803p+4, 0x1.b66836d6f7ff6p+2},
+ {0x1.3973d5b39baa4p+6, -0x1.de87e61f0c86ap+3, 0x1.de8cf8346969cp+0},
+ {0x1.3e64dbeab2c38p+6, -0x1.a47dea5d85dbbp+3, -0x1.bc586cdcba45p+1},
+ {0x1.44815d7f73a41p+6, -0x1.706c35b4850ecp+3, -0x1.1d13e731dfc5bp+3},
+ {0x1.5a92b1ff8af32p+6, -0x1.76441609cfb3ap+4, 0x1.1f1186319beaap+4},
+ {0x1.5c6885a0ab4cap+6, -0x1.5c0bf4148d03dp+4, 0x1.c5b4a75d0a01ep+3},
+ {0x1.5e72281eb918cp+6, -0x1.4422b7d2ad44bp+4, 0x1.5305a49da3492p+3},
+ {0x1.61631d5752788p+6, -0x1.282b31110d79dp+4, 0x1.8b1a60b561753p+2},
+ {0x1.65488920f4795p+6, -0x1.0b1a0cfacacfbp+4, 0x1.3d72a3bb176fp+0},
+ {0x1.6a27499e4d3d6p+6, -0x1.de4892c062f8p+3, -0x1.f96a59bc9de4cp+1},
+ {0x1.e7d1475ebe201p+4, 0x1.5d4f5ebb6cf8ep+3, 0x1.86e150bac0e61p+2},
+ {0x1.10883ee613f6bp+5, 0x1.aa957aceb4328p+3, -0x1.06e4a6bcf37ep+3},
+ {0x1.2a507c82ee1e7p+5, 0x1.880d450b132c9p+3, -0x1.c81b5ba73664ap+3},
+ {0x1.4902475f20191p+5, 0x1.4f4dfeda4e013p+3, -0x1.38d6c960a7255p+4},
+ {0x1.6aa42ff68fb65p+5, 0x1.1116140836dp+3, -0x1.84f2dbb4bf7b2p+4},
+ {0x1.8def7d6adc3d5p+5, 0x1.ab6406f65a5b9p+2, -0x1.caee116cbc66ep+4},
+ {0x1.77f724f99d0c9p+5, -0x1.bb9ee0e906bf2p+1, 0x1.345d15707c9e4p+3},
+ {0x1.8465d178eda3bp+5, -0x1.021519c6d64p-11, -0x1.11ebea130b3p-8},
+ {0x1.917d476fba08dp+5, 0x1.84e9b4b9a4816p+0, -0x1.89910d2df7414p+2},
+ {0x1.a32d016052029p+5, 0x1.352fbc1045df3p+1, -0x1.847eed4ab1e03p+3},
+ {0x1.b8cfd7baeb72ap+5, 0x1.5d6575ced028ep+1, -0x1.1bff70d82f589p+4},
+ {0x1.d1a20bfd91dbap+5, 0x1.4de285fe39a4dp+1, -0x1.6f381fca63bcep+4},
+ {0x1.c99e943a2d79fp+5, -0x1.219ec15c5de22p+3, 0x1.78f2db7cd8205p+3},
+ {0x1.d20af5c832c06p+5, -0x1.86c31353f2968p+2, 0x1.15f4d958ffb6ep+2},
+ {0x1.db2d2b76b7db4p+5, -0x1.107aa185fa178p+2, -0x1.3a545f6bb32fep+0},
+ {0x1.e7ef429658179p+5, -0x1.58a57459e2113p+1, -0x1.c50012145074dp+2},
+ {0x1.f821bad822a9ep+5, -0x1.8ea06e2a9e1f6p+0, -0x1.9a5cf82ebb612p+3},
+ {0x1.05b4f2f9696abp+6, -0x1.b3645fe7f9294p-1, -0x1.24f471b4e8b62p+4},
+ {0x1.0e4a1f1b1ddcep+6, -0x1.b32bbdf7204c1p+3, 0x1.be3e8e0b04a1fp+3},
+ {0x1.114fcf04f35b3p+6, -0x1.6696d5115c7e4p+3, 0x1.058704ea2708p+3},
+ {0x1.14a2558b5b6c7p+6, -0x1.2c7e211429d81p+3, 0x1.a9f809f8ee4dfp+1},
+ {0x1.195c00cf22a51p+6, -0x1.e58de99c40fb6p+2, -0x1.0e4f6479339a5p+1},
+ {0x1.1f7e4eea78cb7p+6, -0x1.809e6530a25d5p+2, -0x1.ee2cfac349d5ep+2},
+ {0x1.26f9f5da0f33bp+6, -0x1.2ffe0404fe1e4p+2, -0x1.a87af760f19c2p+3},
+ {0x1.37635555b270cp+6, -0x1.17deced6d377ep+4, 0x1.0159387931185p+4},
+ {0x1.39aa8b444a1c7p+6, -0x1.f1b4258b74e35p+3, 0x1.70a1d9ed49333p+3},
+ {0x1.3c30003dc4bdap+6, -0x1.bcf061cd62971p+3, 0x1.d845b8f464806p+2},
+ {0x1.3fcf0a7e4fdb8p+6, -0x1.8316f8fb04f1cp+3, 0x1.3be7d797ab219p+1},
+ {0x1.4492530767fa4p+6, -0x1.4af657f86bc35p+3, -0x1.69ccecfbead6p+1},
+ {0x1.4a78e7a0304fbp+6, -0x1.18be182ded159p+3, -0x1.07ac09ccdfff4p+3},
+ {0x1.5fc9ac083946p+6, -0x1.4f840d59cd9aep+4, 0x1.22ef620ec7775p+4},
+ {0x1.6192b2ae205fp+6, -0x1.361d4509435d5p+4, 0x1.cfe893676e62ep+3},
+ {0x1.638e487d443e1p+6, -0x1.1edcf418396fcp+4, 0x1.5f105a3f7b33p+3},
+ {0x1.666b5164f3799p+6, -0x1.03988ade0159ep+4, 0x1.a6a2e5baac692p+2},
+ {0x1.6a3706fa48d42p+6, -0x1.ce6b98f424c54p+3, 0x1.b67fa4fafc2e8p+0},
+ {0x1.6ef6b7860b53fp+6, -0x1.97ce09961f218p+3, -0x1.b926ed0fbe897p+1},
+ {0x1.3931bb83cb32dp+5, 0x1.c0894426a198dp+3, 0x1.f5ea328bf4058p+2},
+ {0x1.4b9e77eb58ebfp+5, 0x1.108cfd41d7919p+4, -0x1.0eaf04c8d3b35p+2},
+ {0x1.5df2d7bacd40ap+5, 0x1.11e15f9b225acp+4, -0x1.51924b9c514f3p+3},
+ {0x1.756bc7d79519bp+5, 0x1.03a63c750b36bp+4, -0x1.052fc194cf52p+4},
+ {0x1.90b3e1d276c5dp+5, 0x1.d79fd75fb3811p+3, -0x1.58e6a7a1da1fcp+4},
+ {0x1.aea5654d2d631p+5, 0x1.9f14550c88c57p+3, -0x1.a57e86563dbcap+4},
+ {0x1.9b6948efcb1e8p+5, 0x1.dddaa142e7b3ap+0, 0x1.4f83ba256abcfp+3},
+ {0x1.a5f9bfdb796c8p+5, 0x1.3de3647070a5bp+2, 0x1.b416b1e2a3817p+0},
+ {0x1.b1407c7a80b46p+5, 0x1.9d8674f672f17p+2, -0x1.112d7835e0443p+2},
+ {0x1.c0b6552db89b1p+5, 0x1.d8c2a5262b398p+2, -0x1.480f10f6d0b5fp+3},
+ {0x1.d3edc2b5ca1f7p+5, 0x1.edbf49a0778e4p+2, -0x1.fe5ca96caae15p+3},
+ {0x1.ea519f7b4bf8cp+5, 0x1.e35c8bfe38bf9p+2, -0x1.5483ed4b7e5ebp+4},
+ {0x1.e2c36ca30962cp+5, -0x1.1cd1878e91233p+2, 0x1.8bf558e4fbde3p+3},
+ {0x1.ea67769abd5c1p+5, -0x1.c053e5b6c3b66p+0, 0x1.59302161a2865p+2},
+ {0x1.f2ba2dd022991p+5, -0x1.4b64e809e68p-11, -0x1.5fbb8b338b8p-8},
+ {0x1.fe6b844bbf20fp+5, 0x1.8040f07e39554p+0, -0x1.7196ffd7de002p+2},
+ {0x1.06aeef460087dp+6, 0x1.4afa79e390244p+1, -0x1.705a7c8493135p+3},
+ {0x1.0fa5554a5c955p+6, 0x1.9dea7336ee828p+1, -0x1.1089bb9ca046ep+4},
+ {0x1.178e9051b853ep+6, -0x1.3b3b8e6c52f1cp+3, 0x1.cc268b9a4e157p+3},
+ {0x1.1a65db5c4c0d1p+6, -0x1.e697a167b2b86p+2, 0x1.1b4d0d1ca381dp+3},
+ {0x1.1d86bd983524p+6, -0x1.77ccb1bcef226p+2, 0x1.08e1384d16e75p+2},
+ {0x1.21fc7ad985402p+6, -0x1.08e3f099fb00fp+2, -0x1.363e6fbac8902p+0},
+ {0x1.27cb1d6dd9434p+6, -0x1.4f7ef424b1025p+1, -0x1.b1af4c945ce98p+2},
+ {0x1.2ee710ec206cfp+6, -0x1.6ac7f2787bda1p+0, -0x1.89e8c18db3849p+3},
+ {0x1.3e7c86695f695p+6, -0x1.ceac064fd3a97p+3, 0x1.06a67a74ff4cp+4},
+ {0x1.40ac0504d65f2p+6, -0x1.935bda5136fdbp+3, 0x1.7fa8ea5683dffp+3},
+ {0x1.4317a9d652f14p+6, -0x1.608608dcc2607p+3, 0x1.fc21edd271f8bp+2},
+ {0x1.46928cf49114ap+6, -0x1.2883a0410f74bp+3, 0x1.8d4b911a8102bp+1},
+ {0x1.4b27f53f2d1c8p+6, -0x1.e40f8922e26b2p+2, -0x1.11ca0bf0acee9p+1},
+ {0x1.50d83b6f7788bp+6, -0x1.82a0417f98419p+2, -0x1.e18432a6676edp+2},
+ {0x1.6567db05a012cp+6, -0x1.27d55f8b64f16p+4, 0x1.271e9400a42e4p+4},
+ {0x1.6723b3feab73ap+6, -0x1.0f412c5a09d1ap+4, 0x1.dae43f7f7eb6ep+3},
+ {0x1.6910cf423acf1p+6, -0x1.f152d3d18c3b3p+3, 0x1.6c04c44876eb2p+3},
+ {0x1.6bd948eca017fp+6, -0x1.bc29d2c53f57ap+3, 0x1.c44a28d107a62p+2},
+ {0x1.6f8a69cf10b1p+6, -0x1.84b224c6d040cp+3, 0x1.1c910c629db67p+1},
+ {0x1.7429ebc4e406dp+6, -0x1.4f4a0b735c531p+3, -0x1.73a39097eadefp+1},
+ {0x1.7acf7694f8c6p+5, 0x1.0f40ef4bed7e8p+4, 0x1.2f88d81b23f2ep+3},
+ {0x1.87b52573912c8p+5, 0x1.3e9cadbae2c71p+4, -0x1.21c127942ffc2p-1},
+ {0x1.95308092c646cp+5, 0x1.4bd8ec93cbe28p+4, -0x1.b1ad2fc6cd5ccp+2},
+ {0x1.a743d71712a69p+5, 0x1.4b428f09e4704p+4, -0x1.984b766ca527bp+3},
+ {0x1.bd35da1ada54ap+5, 0x1.3ee7b023f1a36p+4, -0x1.252f399063a3p+4},
+ {0x1.d638af110e2bfp+5, 0x1.2a6369728426p+4, -0x1.777a4238ecc9cp+4},
+ {0x1.c5db1e678f93bp+5, 0x1.be23ef0d11b9fp+2, 0x1.706e52e6555aep+3},
+ {0x1.ceab15b1e5e2cp+5, 0x1.376364145a451p+3, 0x1.d7fd2a954eedcp+1},
+ {0x1.d8302a174d63dp+5, 0x1.67a05469bf7c1p+3, -0x1.0236bbe6ffddap+1},
+ {0x1.e56c6a282c6fep+5, 0x1.8915eed9a5994p+3, -0x1.fb2d49a257381p+2},
+ {0x1.f622a3ef65e7fp+5, 0x1.98638aed6156bp+3, -0x1.b57e66e36241ap+3},
+ {0x1.04f5c4d9c25cfp+6, 0x1.9710ff57a7b7ap+3, -0x1.32004fe7bd5b5p+4},
+ {0x1.018127d59826cp+6, 0x1.1e619caaa3a8fp-1, 0x1.a49bd0215a96dp+3},
+ {0x1.04e57659b4cf6p+6, 0x1.8024718692a3cp+1, 0x1.addd1b874609p+2},
+ {0x1.089bd543a0e63p+6, 0x1.2a7652607e02cp+2, 0x1.8d85013677bap+0},
+ {0x1.0ddb424d04fadp+6, 0x1.889c9b8223651p+2, -0x1.05aa1bc86a9d4p+2},
+ {0x1.149da8fbd8909p+6, 0x1.ce4542c130c31p+2, -0x1.3934aac98d184p+3},
+ {0x1.1cca409e32c6dp+6, 0x1.f855300b25ba5p+2, -0x1.eac45e49f86bap+3},
+ {0x1.23f3e081f0e6fp+6, -0x1.587d5d644e818p+2, 0x1.deea03a7e9e2p+3},
+ {0x1.26935d2d5bb58p+6, -0x1.a6ab45ebb79ddp+1, 0x1.38265c4c6d156p+3},
+ {0x1.29783d4927cb6p+6, -0x1.aa081e388f0b6p+0, 0x1.4de28afb99798p+2},
+ {0x1.2d9b5abdd0df6p+6, -0x1.90d2c34c4c8p-11, -0x1.a96c3913736p-8},
+ {0x1.3303704c60227p+6, 0x1.7842d686da2p+0, -0x1.6001098473213p+2},
+ {0x1.39a911d79bf6cp+6, 0x1.51ae39272f6fp+1, -0x1.604eff9e13224p+3},
+ {0x1.483b66bb75f41p+6, -0x1.53e4a65e5bd0ep+3, 0x1.0dfa4f4feec89p+4},
+ {0x1.4a4cab5bdb777p+6, -0x1.1bf0c7950b46ap+3, 0x1.9433415e2e559p+3},
+ {0x1.4c9756fc325a6p+6, -0x1.d6e499fe26166p+2, 0x1.1693b52228314p+3},
+ {0x1.4fe3e8502420ap+6, -0x1.6b29463056a7ep+2, 0x1.fcea504279e1bp+1},
+ {0x1.543e4f84c51c9p+6, -0x1.01c201c3fcfbep+2, -0x1.311b349896504p+0},
+ {0x1.59a86b698fe17p+6, -0x1.4697ac30fd276p+1, -0x1.a218614573a54p+2},
+ {0x1.6d3f94c53849cp+6, -0x1.e750c3b6abf94p+3, 0x1.2cfd8e3a3298cp+4},
+ {0x1.6ee9ffc7c561bp+6, -0x1.b847cb4caca9ap+3, 0x1.ea32a397a5ea3p+3},
+ {0x1.70c3ef56a2364p+6, -0x1.8cc812a446223p+3, 0x1.7e0dc7e1aa4eep+3},
+ {0x1.737125604c02dp+6, -0x1.5959ff3792892p+3, 0x1.ed9e6884d6db8p+2},
+ {0x1.76fef3ccb11e4p+6, -0x1.2379a952d29eap+3, 0x1.77d044e094287p+1},
+ {0x1.7b739668b58adp+6, -0x1.def62d62ac5d3p+2, -0x1.1242c0559f9b9p+1},
+ {0x1.b9af6705b3e1ap+5, 0x1.3c46b4e4aa724p+4, 0x1.61ea416f62116p+3},
+ {0x1.c34652a386648p+5, 0x1.673d5400dac6ap+4, 0x1.575529f864e4ap+1},
+ {0x1.cd90a5a7155efp+5, 0x1.7aac661c86347p+4, -0x1.97f5d2781d1d2p+1},
+ {0x1.dbc2183aadc6dp+5, 0x1.83f34257f81ecp+4, -0x1.24c7f27451716p+3},
+ {0x1.ed84b050fd5fep+5, 0x1.823e0b9a9286fp+4, -0x1.dc10370a896a1p+3},
+ {0x1.012d3f7585d8bp+6, 0x1.772916c92c8cp+4, -0x1.444d46ad4a3f5p+4},
+ {0x1.f498a57d50c8dp+5, 0x1.72c50647a1b01p+3, 0x1.9501b7bc92a26p+3},
+ {0x1.fbec5add8799fp+5, 0x1.c10937ac1ff0cp+3, 0x1.71506b03a0cfcp+2},
+ {0x1.01f4d75996fc6p+6, 0x1.f168c838b27a5p+3, 0x1.a2871b4170f08p-2},
+ {0x1.07934868d42f1p+6, 0x1.0ba82dcee8d5fp+4, -0x1.55f50c12fdb69p+2},
+ {0x1.0ec4c024626fep+6, 0x1.16b4701bb2cep+4, -0x1.627c9a1b01177p+3},
+ {0x1.1768d8b0aa031p+6, 0x1.199f4e2e9e65fp+4, -0x1.09c1c4e924702p+4},
+ {0x1.14522a4a9a7e2p+6, 0x1.6020b0a7ecac7p+2, 0x1.c1a8f99df62d9p+3},
+ {0x1.174b930ef061ap+6, 0x1.ec83068a9839fp+2, 0x1.06f134f7cc07ep+3},
+ {0x1.1a90921a38fe7p+6, 0x1.28d89eb448a2cp+3, 0x1.ab5fe99a94265p+1},
+ {0x1.1f36b9520ad75p+6, 0x1.57ace8397fb96p+3, -0x1.0feb2da7758d4p+1},
+ {0x1.253f2b741784fp+6, 0x1.7c087d08384b1p+3, -0x1.efdb49212f992p+2},
+ {0x1.2c9a998952cf1p+6, 0x1.9366b5a4edd8ap+3, -0x1.a9848b6099745p+3},
+ {0x1.330541ae3e385p+6, -0x1.39e238ea7801p-1, 0x1.f5ec9f058d778p+3},
+ {0x1.3568ff8940412p+6, 0x1.4b2aea6a2b17bp+0, 0x1.5aadb9534e15p+3},
+ {0x1.380d495b09ffp+6, 0x1.6b2260c162acep+1, 0x1.a0b3e40fdedfp+2},
+ {0x1.3bd6db55a86abp+6, 0x1.1c0894632c07ep+2, 0x1.73acfbc73a52cp+0},
+ {0x1.40ced5740b6ecp+6, 0x1.784d8d5bd59bap+2, -0x1.f87f29ae0432ap+1},
+ {0x1.46f1d56d6a627p+6, 0x1.c2acec311d85p+2, -0x1.2d078d1d0ec93p+3},
+ {0x1.54692a261ff77p+6, -0x1.91aafde5841e2p+2, 0x1.1733b7c5acf09p+4},
+ {0x1.5657fb0185f53p+6, -0x1.292fef1c6094ap+2, 0x1.adacb9827ba1ep+3},
+ {0x1.587cf8c168362p+6, -0x1.9aa045411de21p+1, 0x1.34f47fa15abacp+3},
+ {0x1.5b946ca219efdp+6, -0x1.98465c0dc7b2ap+0, 0x1.43ded9d06d2f2p+2},
+ {0x1.5faadb5249c01p+6, -0x1.d35a0bdd6ap-11, -0x1.f008c176226p-8},
+ {0x1.64c3b31613c93p+6, 0x1.6ffd305ac700dp+0, -0x1.52580b551209ep+2},
+ {0x1.773c645e32d2dp+6, -0x1.6c001f7fba478p+3, 0x1.3482f283026fp+4},
+ {0x1.78d215b19332bp+6, -0x1.3f6c1cce8d703p+3, 0x1.fda06ae8dbf53p+3},
+ {0x1.7a9531dc8addap+6, -0x1.15dd42d16c7e4p+3, 0x1.94e8098e96fccp+3},
+ {0x1.7d21e2193d5bcp+6, -0x1.c8c718caa9f8ep+2, 0x1.1105d830d79dp+3},
+ {0x1.808572bc9578ap+6, -0x1.607bf910c57eep+2, 0x1.ebe2b93545493p+1},
+ {0x1.84c6a7914070fp+6, -0x1.f6c2ba3dfc25ap+1, -0x1.2beab2de5e36ep+0},
+ {0x1.f663baac570efp+5, 0x1.67be9f690c994p+4, 0x1.928e76c3750aep+3},
+ {0x1.fdd78bfa0c16bp+5, 0x1.8da11d29326eap+4, 0x1.63128e5e5d16fp+2},
+ {0x1.02fa8211ca545p+6, 0x1.a3a4c44447321p+4, 0x1.04d0a318f0ebp-3},
+ {0x1.08ace6a6b1bbcp+6, 0x1.b32f97fdc6c72p+4, -0x1.69ea7f28a916ap+2},
+ {0x1.0ff3cb8943e81p+6, 0x1.b9bf03db71bbbp+4, -0x1.6cb54bc7e9c6p+3},
+ {0x1.18ac5c68852cdp+6, 0x1.b753a9bd5dbeep+4, -0x1.0ebf0dff35bfbp+4},
+ {0x1.12e57190e4906p+6, 0x1.f7243f8456bc9p+3, 0x1.bbbf019d6f15fp+3},
+ {0x1.15f567ae01685p+6, 0x1.1e238872ebfe1p+4, 0x1.f5db81101607bp+2},
+ {0x1.1952176f81196p+6, 0x1.35eb43e1b3454p+4, 0x1.6f044118da651p+1},
+ {0x1.1e17b3f5be1e9p+6, 0x1.4aa233635e996p+4, -0x1.54536023af51fp+1},
+ {0x1.244540fde90c8p+6, 0x1.590428a4cd957p+4, -0x1.09fd595c81312p+3},
+ {0x1.2bc8d270727cdp+6, 0x1.6002d76c0fae1p+4, -0x1.bbbb8dd0769e1p+3},
+ {0x1.2910051d7f4b8p+6, 0x1.4505f3d8c93bep+3, 0x1.e1f0441f64aep+3},
+ {0x1.2ba848891bba1p+6, 0x1.83d80a13be75dp+3, 0x1.39f759ee811ebp+3},
+ {0x1.2e852380cafc2p+6, 0x1.b3d439de20201p+3, 0x1.5018eb1694a48p+2},
+ {0x1.329c951f395aap+6, 0x1.e28db45c54996p+3, 0x1.689e197414bp-7},
+ {0x1.37f5013da44b7p+6, 0x1.04846845ce376p+4, -0x1.5f9623aa14c25p+2},
+ {0x1.3e86c9cea44c3p+6, 0x1.11f59319394fdp+4, -0x1.6049926fe33dap+3},
+ {0x1.443efb939f1d1p+6, 0x1.0a131d35fab88p+2, 0x1.0838884c6c35ap+4},
+ {0x1.4667729b66dbap+6, 0x1.793a281884d1p+2, 0x1.8155ad5254ba1p+3},
+ {0x1.48cb51f255527p+6, 0x1.d609d14c020d1p+2, 0x1.fd82e0735fdcep+2},
+ {0x1.4c3af36c4bb13p+6, 0x1.1c709781a9fb2p+3, 0x1.8ca66dc4e33fdp+1},
+ {0x1.50c14ed0ba88bp+6, 0x1.4a1d653379591p+3, -0x1.14ee52d3fd0e1p+1},
+ {0x1.565e87fd89a8p+6, 0x1.6fe5c4c37980fp+3, -0x1.e3e3e3d81f648p+2},
+ {0x1.62b687ada6a2dp+6, -0x1.b1a7c98c53b9bp+0, 0x1.221d2fc2be6cp+4},
+ {0x1.6480f342fedfap+6, -0x1.728e49c5a9ba8p-3, 0x1.cb44060a17de8p+3},
+ {0x1.667e081a8e931p+6, 0x1.2c204353d8acp+0, 0x1.582d07a1ac706p+3},
+ {0x1.695d03a4b4ea6p+6, 0x1.5b9dd1e5192c9p+1, 0x1.949cc8da7db3dp+2},
+ {0x1.6d2ad25b49139p+6, 0x1.10c661536b1e3p+2, 0x1.60b9aee667ea2p+0},
+ {0x1.71ec510363cccp+6, 0x1.6b2af260ca3f1p+2, -0x1.e8e3d6769ed61p+1},
+ {0x1.83323c984ee7ap+6, -0x1.c8df58716d4cbp+2, 0x1.3d9335b5d20f9p+4},
+ {0x1.84b109344c92cp+6, -0x1.750134f94569cp+2, 0x1.0a63166d3ca32p+4},
+ {0x1.865ae5967696dp+6, -0x1.260f129da839ep+2, 0x1.b00f6d0ac1f93p+3},
+ {0x1.88c3891dbffdp+6, -0x1.8e7bf5d542ea4p+1, 0x1.30323669c3d67p+3},
+ {0x1.8bf825267af8ep+6, -0x1.89d8c0f2ba1ffp+0, 0x1.3b2af0c9eed76p+2},
+ {0x1.8ffffbb77c76ap+6, -0x1.09cab717214p-10, -0x1.1a1aa7765a7p-7},
+ {0x1.ae1c063cf8075p+3, -0x1.1dcc8d6b21p-13, -0x1.2f56d49352fp-10},
+ {0x1.23869fde6955fp+4, -0x1.836d13c82dp-13, -0x1.9b340cb2926p-10},
+ {0x1.6a51d9755cb1ep+4, -0x1.e1821bf6d08p-13, -0x1.ff0f3c5806ap-10},
+ {0x1.adca073d0c9a1p+4, -0x1.1d961152df8p-12, -0x1.2f1d00745cap-9},
+ {0x1.eeb26a3638306p+4, -0x1.48b751f0a58p-12, -0x1.5ce3e1a3db8p-9},
+ {0x1.16c4868a9dbc4p+5, -0x1.7278906708p-12, -0x1.89352628678p-9},
+ {0x1.3552cb4726ed2p+5, -0x1.9b140ac6fbp-12, -0x1.b44e9f2325p-9},
+ {0x1.53242132979b2p+5, -0x1.c2b46f9c328p-12, -0x1.de5d99b6f9p-9},
+ {0x1.7051013cf5a69p+5, -0x1.e97a44c10a8p-12, -0x1.03c24d5b897p-8},
+ {0x1.8ceca3a0569fdp+5, -0x1.07bf8a7e0fcp-11, -0x1.17ef5f1c441p-8},
+ {0x1.a9067ed63306p+5, -0x1.1a6bb67a6bp-11, -0x1.2bc0e9df0a8p-8},
+ {0x1.c4ab42f91535bp+5, -0x1.2cca14a233p-11, -0x1.3f3fe0662d2p-8},
+ {0x1.dfe5855ae1528p+5, -0x1.3ee1ad40618p-11, -0x1.5273b9784a3p-8},
+ {0x1.fabe397d15c9dp+5, -0x1.50b872fc158p-11, -0x1.6562c531248p-8},
+ {0x1.0a9e8459f8d9ap+6, -0x1.62537d1958p-11, -0x1.78126ad030ep-8},
+ {0x1.17b44990f13f5p+6, -0x1.73b732a42fp-11, -0x1.8a87568ccdp-8},
+ {0x1.24a350705ddf4p+6, -0x1.84e76b1c308p-11, -0x1.9cc59c424aap-8},
+ {0x1.316e23ed9d96ap+6, -0x1.95e7879947p-11, -0x1.aed0d2216dp-8},
+ {0x1.3e1704b29c069p+6, -0x1.a6ba8679e08p-11, -0x1.c0ac258f992p-8},
+ {0x1.4a9ff4d8984e6p+6, -0x1.b76312f3808p-11, -0x1.d25a6bb27acp-8},
+ {0x1.570ac151c1615p+6, -0x1.c7e3919c138p-11, -0x1.e3de2eb684p-8},
+ {0x1.6359098c40c61p+6, -0x1.d83e2a89bbp-11, -0x1.f539b894d66p-8},
+ {0x1.6f8c45b456692p+6, -0x1.e874d1a7f5p-11, -0x1.03378df3804p-7},
+ {0x1.7ba5cbe12c3fcp+6, -0x1.f8894d9602p-11, -0x1.0bc01d99c93p-7},
+ };
+
+ // Perform the inverse gamma companding for a sRGB color
+ // http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
+ // double inverseGammaCompanding(int c) {
+ // // [0, 255] to [0, 1]
+ // double v = c / 255.0;
+ // if (v <= 0.04045) {
+ // return v / 12.92;
+ // }
+ // return std::pow((v + 0.055) / 1.055, 2.4);
+ // };
+ constexpr double RGB888_to_sRGB_table[] {
+ 0x0p+0, 0x1.3e45677c176f7p-12, 0x1.3e45677c176f7p-11, 0x1.dd681b3a23272p-11,
+ 0x1.3e45677c176f7p-10, 0x1.8dd6c15b1d4b4p-10, 0x1.dd681b3a23272p-10, 0x1.167cba8c94818p-9,
+ 0x1.3e45677c176f7p-9, 0x1.660e146b9a5d6p-9, 0x1.8dd6c15b1d4b4p-9, 0x1.b6a31b5259c99p-9,
+ 0x1.e1e31d70c99ddp-9, 0x1.07c38bf8583a9p-8, 0x1.1fcc2beed6421p-8, 0x1.390ffaf95e279p-8,
+ 0x1.53936cc7bc928p-8, 0x1.6f5addb50c915p-8, 0x1.8c6a94031b561p-8, 0x1.aac6c0fb97351p-8,
+ 0x1.ca7381f9f602bp-8, 0x1.eb74e160978dp-8, 0x1.06e76bbda92b8p-7, 0x1.18c2a5a8a8044p-7,
+ 0x1.2b4e09b3f0ae3p-7, 0x1.3e8b7b3bde965p-7, 0x1.527cd60af8b85p-7, 0x1.6723eea8d3709p-7,
+ 0x1.7c8292a3db6b3p-7, 0x1.929a88d67b521p-7, 0x1.a96d91a8016bdp-7, 0x1.c0fd67499fab6p-7,
+ 0x1.d94bbdefd740ep-7, 0x1.f25a44089883fp-7, 0x1.061551372c694p-6, 0x1.135f3e4c2cce2p-6,
+ 0x1.210bb8642b172p-6, 0x1.2f1b8c1ae46bdp-6, 0x1.3d8f839b79c0bp-6, 0x1.4c6866b3e9fa4p-6,
+ 0x1.5ba6fae794313p-6, 0x1.6b4c0380d2deep-6, 0x1.7b5841a1bf3acp-6, 0x1.8bcc74542addbp-6,
+ 0x1.9ca95898dc8b5p-6, 0x1.adefa9761c02p-6, 0x1.bfa0200597bd9p-6, 0x1.d1bb7381aec1fp-6,
+ 0x1.e442595227bcap-6, 0x1.f73585185e1b5p-6, 0x1.054ad45d76878p-5, 0x1.0f31ba386ff26p-5,
+ 0x1.194fcb663747bp-5, 0x1.23a55e62a662ap-5, 0x1.2e32c8e148d11p-5, 0x1.38f85fd21eacfp-5,
+ 0x1.43f67766310ffp-5, 0x1.4f2d6313fa8dp-5, 0x1.5a9d759ba5edp-5, 0x1.6647010b254eep-5,
+ 0x1.722a56c2239eep-5, 0x1.7e47c775d2427p-5, 0x1.8a9fa33494b07p-5, 0x1.973239698b9ccp-5,
+ 0x1.a3ffd8e001389p-5, 0x1.b108cfc6b7fbcp-5, 0x1.be4d6bb31d522p-5, 0x1.cbcdf9a4616f2p-5,
+ 0x1.d98ac60675833p-5, 0x1.e7841cb4f16dfp-5, 0x1.f5ba48fde2048p-5, 0x1.0216cad240765p-4,
+ 0x1.096f2671eb815p-4, 0x1.10e65c38a5192p-4, 0x1.187c90bf8bce2p-4, 0x1.2031e85f5d6dap-4,
+ 0x1.28068731a1952p-4, 0x1.2ffa9111cb94bp-4, 0x1.380e299e53f92p-4, 0x1.40417439ca10fp-4,
+ 0x1.4894940bddbfbp-4, 0x1.5107ac0261e59p-4, 0x1.599aded247aacp-4, 0x1.624e4ef892ed4p-4,
+ 0x1.6b221ebb4817ep-4, 0x1.7416702a539d1p-4, 0x1.7d2b65206b527p-4, 0x1.86611f43e9e6ap-4,
+ 0x1.8fb7c007a4a7p-4, 0x1.992f68abbbc89p-4, 0x1.a2c83a3e6566dp-4, 0x1.ac82559cb3644p-4,
+ 0x1.b65ddb7354604p-4, 0x1.c05aec3f4fe5ep-4, 0x1.ca79a84ebe03p-4, 0x1.d4ba2fc17a6a5p-4,
+ 0x1.df1ca289d34b8p-4, 0x1.e9a1206d34003p-4, 0x1.f447c904cbb4ep-4, 0x1.ff10bbbe302c2p-4,
+ 0x1.04fe0bedfe5f1p-3, 0x1.0a84fe3b36d8fp-3, 0x1.101d443dfc06fp-3, 0x1.15c6ed58eefdfp-3,
+ 0x1.1b8208da5fefp-3, 0x1.214ea5fc9514ap-3, 0x1.272cd3e610123p-3, 0x1.2d1ca1a9d1cfbp-3,
+ 0x1.331e1e479cdf5p-3, 0x1.393158ac3674ep-3, 0x1.3f565fb1a5fd5p-3, 0x1.458d421f735dfp-3,
+ 0x1.4bd60eaae3e73p-3, 0x1.5230d3f736034p-3, 0x1.589da095dbaa1p-3, 0x1.5f1c8306b3a3cp-3,
+ 0x1.65ad89b841a2bp-3, 0x1.6c50c307e53bfp-3, 0x1.73063d420fc8p-3, 0x1.79ce06a279303p-3,
+ 0x1.80a82d5453b5dp-3, 0x1.8794bf727eb3fp-3, 0x1.8e93cb07b8679p-3, 0x1.95a55e0ecec0bp-3,
+ 0x1.9cc98672cf47ep-3, 0x1.a400520f3619cp-3, 0x1.ab49ceb01c003p-3, 0x1.b2a60a1263b0ap-3,
+ 0x1.ba1511e3e632dp-3, 0x1.c196f3c39e76fp-3, 0x1.c92bbd41d41fep-3, 0x1.d0d37be045851p-3,
+ 0x1.d88e3d1250f68p-3, 0x1.e05c0e3d1d3ep-3, 0x1.e83cfcb7c16fp-3, 0x1.f03115cb6bfd3p-3,
+ 0x1.f83866b38924dp-3, 0x1.00297e4ef4553p-2, 0x1.044072557177ap-2, 0x1.086115f6beb3ap-2,
+ 0x1.0c8b6fb5c735ep-2, 0x1.10bf860ef039ap-2, 0x1.14fd5f782a5a6p-2, 0x1.1945026102997p-2,
+ 0x1.1d967532b31b1p-2, 0x1.21f1be50339e7p-2, 0x1.2656e41649ae3p-2, 0x1.2ac5ecdb988f8p-2,
+ 0x1.2f3edef0b0ed8p-2, 0x1.33c1c0a020438p-2, 0x1.384e982e800b1p-2, 0x1.3ce56bda84a81p-2,
+ 0x1.418641dd0c1bcp-2, 0x1.463120692c7afp-2, 0x1.4ae60dac4229dp-2, 0x1.4fa50fcdfde15p-2,
+ 0x1.546e2cf0727a9p-2, 0x1.59416b3022858p-2, 0x1.5e1ed0a40daabp-2, 0x1.6306635dbdd7bp-2,
+ 0x1.67f82969543a2p-2, 0x1.6cf428cd96079p-2, 0x1.71fa678bf915dp-2, 0x1.770aeba0b042ap-2,
+ 0x1.7c25bb02b7ac5p-2, 0x1.814adba3e0bd9p-2, 0x1.867a5370de0b1p-2, 0x1.8bb428514f067p-2,
+ 0x1.90f86027cb84ep-2, 0x1.964700d1ef1b1p-2, 0x1.9ba0102864521p-2, 0x1.a10393feefafdp-2,
+ 0x1.a67192247a9bep-2, 0x1.abea10631e195p-2, 0x1.b16d14802d5cap-2, 0x1.b6faa43c403bbp-2,
+ 0x1.bc92c5533d785p-2, 0x1.c2357d7c64e5dp-2, 0x1.c7e2d26a596dep-2, 0x1.cd9ac9cb2aef2p-2,
+ 0x1.d35d69485ffc5p-2, 0x1.d92ab686ff782p-2, 0x1.df02b7279a10dp-2, 0x1.e4e570c6539c5p-2,
+ 0x1.ead2e8faec526p-2, 0x1.f0cb2558c9ea4p-2, 0x1.f6ce2b6f00983p-2, 0x1.fcdc00c85bec2p-2,
+ 0x1.017a5575b3cb2p-1, 0x1.048c17ad3c04bp-1, 0x1.07a349c9d9837p-1, 0x1.0abfee888c05p-1,
+ 0x1.0de208a4444c8p-1, 0x1.11099ad5e83ebp-1, 0x1.1436a7d456eefp-1, 0x1.176932546ca12p-1,
+ 0x1.1aa13d0906bdap-1, 0x1.1ddecaa307b85p-1, 0x1.2121ddd15aecep-1, 0x1.246a7940f86d1p-1,
+ 0x1.27b89f9ce8c4bp-1, 0x1.2b0c538e48b07p-1, 0x1.2e6597bc4ccap-1, 0x1.31c46ecc4528dp-1,
+ 0x1.3528db61a0f73p-1, 0x1.3892e01df1fccp-1, 0x1.3c027fa0f01ebp-1, 0x1.3f77bc887cd3bp-1,
+ 0x1.42f29970a68f8p-1, 0x1.467318f3ac22dp-1, 0x1.49f93daa00113p-1, 0x1.4d850a2a4bde1p-1,
+ 0x1.51168109734e5p-1, 0x1.54ada4da97a1bp-1, 0x1.584a782f1ac23p-1, 0x1.5becfd96a2698p-1,
+ 0x1.5f95379f1b3edp-1, 0x1.634328d4bbe97p-1, 0x1.66f6d3c2081cfp-1, 0x1.6ab03aefd39aap-1,
+ 0x1.6e6f60e5452b1p-1, 0x1.72344827d98f6p-1, 0x1.75fef33b6669bp-1, 0x1.79cf64a21d1e2p-1,
+ 0x1.7da59edc8dabp-1, 0x1.8181a469a9787p-1, 0x1.856377c6c6224p-1, 0x1.894b1b6fa0377p-1,
+ 0x1.8d3891de5df49p-1, 0x1.912bdd8b91f45p-1, 0x1.952500ee3dda5p-1, 0x1.9923fe7bd4f67p-1,
+ 0x1.9d28d8a83edfcp-1, 0x1.a13391e5da09fp-1, 0x1.a5442ca57e52ep-1, 0x1.a95aab567f88fp-1,
+ 0x1.ad771066afec2p-1, 0x1.b1995e4262a69p-1, 0x1.b5c197546e3f8p-1, 0x1.b9efbe062f086p-1,
+ 0x1.be23d4bf8981bp-1, 0x1.c25ddde6ecbbbp-1, 0x1.c69ddbe154af1p-1, 0x1.cae3d1124c90bp-1,
+ 0x1.cf2fbfdbf11f1p-1, 0x1.d381aa9ef2e82p-1, 0x1.d7d993ba988d4p-1, 0x1.dc377d8cc0fd5p-1,
+ 0x1.e09b6a71e5aa6p-1, 0x1.e5055cc51cbb4p-1, 0x1.e97556e01b351p-1, 0x1.edeb5b1b37216p-1,
+ 0x1.f2676bcd69adep-1, 0x1.f6e98b4c51466p-1, 0x1.fb71bbec33ab2p-1, 0x1p+0,
+ };
+// clang-format on
+
+// convert a RGB color to Oklab (https://bottosson.github.io/posts/oklab/)
+Lab rgbToOklab(QRgb rgb)
+{
+ const double r = RGB888_to_sRGB_table[qRed(rgb)];
+ const double g = RGB888_to_sRGB_table[qGreen(rgb)];
+ const double b = RGB888_to_sRGB_table[qBlue(rgb)];
+
+ const double l = std::cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
+ const double m = std::cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
+ const double s = std::cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
+
+ // M2 * 100 * {l', m', s'}
+ return Lab{
+ (021.04542553 * l + 079.36177850 * m - 000.40720468 * s),
+ (197.79984951 * l - 242.85922050 * m + 045.05937099 * s),
+ (002.59040371 * l + 078.27717662 * m - 080.86757660 * s),
+ };
+}
+
+constexpr double epsilon = 1e-15;
+
+inline double sinDegree(double x)
+{
+ return std::sin(x * M_PI / 180.0);
+}
+
+inline double cosDegree(double x)
+{
+ return std::cos(x * M_PI / 180.0);
+}
+
+inline double pow2(double x)
+{
+ return x * x;
+}
+
+inline double computeHPrime(double a_prime, double b)
+{
+ if (std::abs(a_prime) < epsilon && std::abs(b) < epsilon) {
+ return 0.0;
+ }
+
+ const double value = std::atan2(b, a_prime) * 180.0 / M_PI;
+ return (value < 0.0) ? value + 360.0 : value;
+}
+
+inline double computeDeltaHPrime(double C1_prime, double C2_prime, double h1_prime, double h2_prime)
+{
+ if (C1_prime * C2_prime < epsilon) {
+ return 0.0;
+ }
+
+ const double diff = h2_prime - h1_prime;
+
+ if (std::abs(diff) <= 180.0) {
+ return diff;
+ } else if (diff > 180.0) {
+ return diff - 360.0;
+ } else {
+ return diff + 360.0;
+ }
+}
+
+inline double computeHPrimeBar(double C1_prime, double C2_prime, double h1_prime, double h2_prime)
+{
+ const double sum = h1_prime + h2_prime;
+
+ if (C1_prime * C2_prime < epsilon) {
+ return sum;
+ }
+
+ const double dist = std::abs(h1_prime - h2_prime);
+
+ if (dist <= 180.0) {
+ return 0.5 * sum;
+ } else if (sum < 360.0) {
+ return 0.5 * (sum + 360.0);
+ } else {
+ return 0.5 * (sum - 360.0);
+ }
+}
+
+/// Calculate the perceptual color difference based on CIEDE2000.
+/// https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
+/// \return The color difference of the two colors.
+double calculate_CIEDE2000(const Lab &color1, const Lab &color2)
+{
+ const double L1 = color1.L;
+ const double a1 = color1.a;
+ const double b1 = color1.b;
+ const double L2 = color2.L;
+ const double a2 = color2.a;
+ const double b2 = color2.b;
+
+ const double _25_pow_7 = /*std::pow(25.0, 7.0) = */ 6103515625.0;
+
+ const double C1_ab = std::sqrt(a1 * a1 + b1 * b1);
+ const double C2_ab = std::sqrt(a2 * a2 + b2 * b2);
+ const double C_ab_bar = 0.5 * (C1_ab + C2_ab);
+ const double c_ab_bar_pow_7 = std::pow(C_ab_bar, 7.0);
+ const double G = 0.5 * (1.0 - std::sqrt(c_ab_bar_pow_7 / (c_ab_bar_pow_7 + _25_pow_7)));
+ const double a1_prime = (1.0 + G) * a1;
+ const double a2_prime = (1.0 + G) * a2;
+ const double C1_prime = std::sqrt(a1_prime * a1_prime + b1 * b1);
+ const double C2_prime = std::sqrt(a2_prime * a2_prime + b2 * b2);
+ const double h1_prime = computeHPrime(a1_prime, b1);
+ const double h2_prime = computeHPrime(a2_prime, b2);
+
+ const double deltaL_prime = L2 - L1;
+ const double deltaC_prime = C2_prime - C1_prime;
+ const double deltah_prime = computeDeltaHPrime(C1_prime, C2_prime, h1_prime, h2_prime);
+ const double deltaH_prime = 2.0 * std::sqrt(C1_prime * C2_prime) * sinDegree(0.5 * deltah_prime);
+
+ const double L_primeBar = 0.5 * (L1 + L2);
+ const double C_primeBar = 0.5 * (C1_prime + C2_prime);
+ const double h_primeBar = computeHPrimeBar(C1_prime, C2_prime, h1_prime, h2_prime);
+
+ const double T = 1.0 - 0.17 * cosDegree(h_primeBar - 30.0) + 0.24 * cosDegree(2.0 * h_primeBar) + 0.32 * cosDegree(3.0 * h_primeBar + 6.0)
+ - 0.20 * cosDegree(4.0 * h_primeBar - 63.0);
+
+ const double C_primeBar_pow7 = std::pow(C_primeBar, 7.0);
+ const double R_C = 2.0 * std::sqrt(C_primeBar_pow7 / (C_primeBar_pow7 + _25_pow_7));
+ const double S_L = 1.0 + (0.015 * pow2(L_primeBar - 50.0)) / std::sqrt(20.0 + pow2(L_primeBar - 50.0));
+ const double S_C = 1.0 + 0.045 * C_primeBar;
+ const double S_H = 1.0 + 0.015 * C_primeBar * T;
+ const double R_T = -R_C * sinDegree(60.0 * std::exp(-pow2((h_primeBar - 275) / 25.0)));
+
+ constexpr double kL = 1.0;
+ constexpr double kC = 1.0;
+ constexpr double kH = 1.0;
+
+ const double deltaL = deltaL_prime / (kL * S_L);
+ const double deltaC = deltaC_prime / (kC * S_C);
+ const double deltaH = deltaH_prime / (kH * S_H);
+
+ return /*std::sqrt*/ (deltaL * deltaL + deltaC * deltaC + deltaH * deltaH + R_T * deltaC * deltaH);
+}
+
+struct AnsiBuffer {
+ using ColorCache = QHash<QRgb, int>;
+
+ void append(char c)
+ {
+ Q_ASSERT(remaining() >= 1);
+ m_data[m_size] = c;
+ ++m_size;
+ }
+
+ void append(QLatin1String str)
+ {
+ Q_ASSERT(remaining() >= str.size());
+ memcpy(m_data + m_size, str.data(), str.size());
+ m_size += str.size();
+ }
+
+ void appendForeground(QRgb rgb, bool is256Colors, ColorCache &colorCache)
+ {
+ append(QLatin1String("38;"));
+ append(rgb, is256Colors, colorCache);
+ }
+
+ void appendBackground(QRgb rgb, bool is256Colors, ColorCache &colorCache)
+ {
+ append(QLatin1String("48;"));
+ append(rgb, is256Colors, colorCache);
+ }
+
+ void append(QRgb rgb, bool is256Colors, ColorCache &colorCache)
+ {
+ auto appendUInt8 = [&](int x) {
+ Q_ASSERT(x <= 255 && x >= 0);
+ if (x > 99) {
+ if (x >= 200) {
+ append('2');
+ x -= 200;
+ } else {
+ append('1');
+ x -= 100;
+ }
+ } else if (x < 10) {
+ append(char('0' + x));
+ return;
+ }
+
+ // clang-format off
+ constexpr char const* tb2digits =
+ "00" "01" "02" "03" "04" "05" "06" "07" "08" "09"
+ "10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
+ "20" "21" "22" "23" "24" "25" "26" "27" "28" "29"
+ "30" "31" "32" "33" "34" "35" "36" "37" "38" "39"
+ "40" "41" "42" "43" "44" "45" "46" "47" "48" "49"
+ "50" "51" "52" "53" "54" "55" "56" "57" "58" "59"
+ "60" "61" "62" "63" "64" "65" "66" "67" "68" "69"
+ "70" "71" "72" "73" "74" "75" "76" "77" "78" "79"
+ "80" "81" "82" "83" "84" "85" "86" "87" "88" "89"
+ "90" "91" "92" "93" "94" "95" "96" "97" "98" "99";
+ // clang-format on
+
+ auto *p = tb2digits + x * 2;
+ append(p[0]);
+ append(p[1]);
+ };
+
+ if (is256Colors) {
+ double dist = 1e24;
+ int idx = 0;
+ auto it = colorCache.find(rgb);
+ if (it == colorCache.end()) {
+ const auto lab = rgbToOklab(rgb);
+ // find the nearest xterm color
+ for (Lab const &xtermLab : xterm240_Oklabs) {
+ auto dist2 = calculate_CIEDE2000(lab, xtermLab);
+ if (dist2 < dist) {
+ dist = dist2;
+ idx = &xtermLab - xterm240_Oklabs;
+ }
+ }
+ // add 16 to convert 240 colors mode to 256 colors mode
+ idx += 16;
+ colorCache.insert(rgb, idx);
+ } else {
+ idx = it.value();
+ }
+
+ append('5');
+ append(';');
+ appendUInt8(idx);
+ } else {
+ append('2');
+ append(';');
+ appendUInt8(qRed(rgb));
+ append(';');
+ appendUInt8(qGreen(rgb));
+ append(';');
+ appendUInt8(qBlue(rgb));
+ }
+ append(';');
+ }
+
+ // Replace last character with 'm'. Last character must be ';'
+ void setFinalStyle()
+ {
+ Q_ASSERT(m_data[m_size - 1] == ';');
+ m_data[m_size - 1] = 'm';
+ }
+
+ void clear()
+ {
+ m_size = 0;
+ }
+
+ QLatin1String latin1() const
+ {
+ return QLatin1String(m_data, m_size);
+ }
+
+private:
+ char m_data[128];
+ int m_size = 0;
+
+ int remaining() const noexcept
+ {
+ return 128 - m_size;
+ }
+};
+
+void fillString(QString &s, int n, QStringView fill)
+{
+ if (n > 0) {
+ for (; n > fill.size(); n -= fill.size()) {
+ s += fill;
+ }
+ s += fill.left(n);
+ }
+}
+
+struct GraphLine {
+ QString graphLine;
+ QString labelLine;
+ int graphLineLength = 0;
+ int labelLineLength = 0;
+ int nextLabelOffset = 0;
+
+ template<class String>
+ void pushLabel(int offset, String const &s, int numberDisplayableChar)
+ {
+ Q_ASSERT(offset >= labelLineLength);
+ const int n = offset - labelLineLength;
+ labelLineLength += numberDisplayableChar + n;
+ fillLine(labelLine, n);
+ labelLine += s;
+ nextLabelOffset = labelLineLength;
+ }
+
+ template<class String>
+ void pushGraph(int offset, String const &s, int numberDisplayableChar)
+ {
+ Q_ASSERT(offset >= graphLineLength);
+ const int n = offset - graphLineLength;
+ graphLineLength += numberDisplayableChar + n;
+ fillLine(graphLine, n);
+ const int ps1 = graphLine.size();
+ graphLine += s;
+ if (offset >= labelLineLength) {
+ const int n2 = offset - labelLineLength;
+ labelLineLength += n2 + 1;
+ fillLine(labelLine, n2);
+ labelLine += QStringView(graphLine).right(graphLine.size() - ps1);
+ }
+ }
+
+private:
+ static void fillLine(QString &s, int n)
+ {
+ Q_ASSERT(n >= 0);
+ fillString(s,
+ n,
+ QStringLiteral(" "
+ " "
+ " "));
+ }
+};
+
+/**
+ * Returns the first free line at a given position or create a new one
+ */
+GraphLine &lineAtOffset(std::vector<GraphLine> &graphLines, int offset)
+{
+ const auto last = graphLines.end();
+ auto p = std::find_if(graphLines.begin(), last, [=](GraphLine const &line) {
+ return line.nextLabelOffset < offset;
+ });
+ if (p == last) {
+ graphLines.emplace_back();
+ return graphLines.back();
+ }
+ return *p;
+}
+
+// disable bold, italic and underline on |
+const QLatin1String graphSymbol("\x1b[22;23;24m|");
+// reverse video
+const QLatin1String nameStyle("\x1b[7m");
+
+/**
+ * ANSI Highlighter dedicated to traces
+ */
+class DebugSyntaxHighlighter : public KSyntaxHighlighting::AbstractHighlighter
+{
+public:
+ using Option = KSyntaxHighlighting::AnsiHighlighter::Option;
+ using Options = KSyntaxHighlighting::AnsiHighlighter::Options;
+
+ void setDefinition(const KSyntaxHighlighting::Definition &def) override
+ {
+ AbstractHighlighter::setDefinition(def);
+ m_contextCapture.setDefinition(def);
+
+ const auto &definitions = def.includedDefinitions();
+ for (const auto &definition : definitions) {
+ const auto *defData = DefinitionData::get(definition);
+ for (const auto &context : defData->contexts) {
+ m_defDataBycontexts.insert(&context, defData);
+ }
+ }
+ }
+
+ void highlightData(QTextStream &in,
+ QTextStream &out,
+ QLatin1String infoStyle,
+ QLatin1String editorBackground,
+ const std::vector<QPair<QString, QString>> &ansiStyles,
+ Options options)
+ {
+ initRegionStyles(ansiStyles);
+
+ m_hasFormatTrace = options.testFlag(Option::TraceFormat);
+ m_hasRegionTrace = options.testFlag(Option::TraceRegion);
+ m_hasStackSizeTrace = options.testFlag(Option::TraceStackSize);
+ m_hasContextTrace = options.testFlag(Option::TraceContext);
+ const bool hasFormatOrContextTrace = m_hasFormatTrace || m_hasContextTrace || m_hasStackSizeTrace;
+
+ const bool hasSeparator = hasFormatOrContextTrace && m_hasRegionTrace;
+ const QString resetBgColor = (editorBackground.isEmpty() ? QStringLiteral("\x1b[0m") : editorBackground);
+
+ bool firstLine = true;
+ State state;
+ QString currentLine;
+ const bool isUnbuffered = options.testFlag(Option::Unbuffered);
+ while (in.readLineInto(&currentLine)) {
+ auto oldState = state;
+ state = highlightLine(currentLine, state);
+
+ if (hasSeparator) {
+ if (!firstLine) {
+ out << QStringLiteral("\x1b[0m────────────────────────────────────────────────────\x1b[K\n");
+ }
+ firstLine = false;
+ }
+
+ if (!m_regions.empty()) {
+ printRegions(out, infoStyle, currentLine.size());
+ out << resetBgColor;
+ }
+
+ for (const auto &fragment : m_highlightedFragments) {
+ auto const &ansiStyle = ansiStyles[fragment.formatId];
+ out << ansiStyle.first << QStringView(currentLine).mid(fragment.offset, fragment.length) << ansiStyle.second;
+ }
+
+ out << QStringLiteral("\x1b[K\n");
+
+ if (hasFormatOrContextTrace && !m_highlightedFragments.empty()) {
+ if (m_hasContextTrace || m_hasStackSizeTrace) {
+ appendContextNames(oldState, currentLine);
+ }
+
+ printFormats(out, infoStyle, ansiStyles);
+ out << resetBgColor;
+ }
+
+ m_highlightedFragments.clear();
+
+ if (isUnbuffered) {
+ out.flush();
+ }
+ }
+ }
+
+ void applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) override
+ {
+ m_highlightedFragments.push_back({m_hasFormatTrace ? format.name() : QString(), offset, length, format.id()});
+ }
+
+ void applyFolding(int offset, int /*length*/, FoldingRegion region) override
+ {
+ if (!m_hasRegionTrace) {
+ return;
+ }
+
+ const auto id = region.id();
+
+ if (region.type() == FoldingRegion::Begin) {
+ m_regions.push_back(Region{m_regionDepth, offset, -1, id, Region::State::Open});
+ // swap with previous region if this is a closing region with same offset in order to superimpose labels
+ if (m_regions.size() >= 2) {
+ auto &previousRegion = m_regions[m_regions.size() - 2];
+ if (previousRegion.state == Region::State::Close && previousRegion.offset == offset) {
+ std::swap(previousRegion, m_regions.back());
+ if (previousRegion.bindIndex != -1) {
+ m_regions[previousRegion.bindIndex].bindIndex = m_regions.size() - 1;
+ }
+ }
+ }
+ ++m_regionDepth;
+ } else {
+ // find open region
+ auto it = m_regions.rbegin();
+ auto eit = m_regions.rend();
+ for (int depth = 0; it != eit; ++it) {
+ if (it->regionId == id && it->bindIndex < 0) {
+ if (it->state == Region::State::Close) {
+ ++depth;
+ } else if (--depth < 0) {
+ break;
+ }
+ }
+ }
+
+ if (it != eit) {
+ it->bindIndex = int(m_regions.size());
+ int bindIndex = int(&*it - m_regions.data());
+ m_regions.push_back(Region{it->depth, offset, bindIndex, id, Region::State::Close});
+ } else {
+ m_regions.push_back(Region{-1, offset, -1, id, Region::State::Close});
+ }
+
+ m_regionDepth = std::max(m_regionDepth - 1, 0);
+ }
+ }
+
+ using KSyntaxHighlighting::AbstractHighlighter::highlightLine;
+
+private:
+ /**
+ * Initializes with colors of \p ansiStyle without duplicate.
+ */
+ void initRegionStyles(const std::vector<QPair<QString, QString>> &ansiStyles)
+ {
+ m_regionStyles.resize(ansiStyles.size());
+ for (std::size_t i = 0; i < m_regionStyles.size(); ++i) {
+ m_regionStyles[i] = ansiStyles[i].first;
+ }
+
+ std::sort(m_regionStyles.begin(), m_regionStyles.end());
+ m_regionStyles.erase(std::unique(m_regionStyles.begin(), m_regionStyles.end()), m_regionStyles.end());
+ }
+
+ /**
+ * Append the context name in front of the format name.
+ */
+ void appendContextNames(State &state, QStringView currentLine)
+ {
+ auto newState = state;
+ for (auto &fragment : m_highlightedFragments) {
+ QString contextName = extractContextName(StateData::get(newState));
+
+ m_contextCapture.offsetNext = 0;
+ m_contextCapture.lengthNext = 0;
+ // truncate the line to deduce the context from the format
+ const auto lineFragment = currentLine.mid(0, fragment.offset + fragment.length + 1);
+ newState = m_contextCapture.highlightLine(lineFragment, state);
+
+ // Deduced context does not start at the position of the format.
+ // This can happen because of lookAhead/fallthrought attribute,
+ // assertion in regex, etc.
+ if (m_contextCapture.offset != fragment.offset && m_contextCapture.length != fragment.length) {
+ contextName.insert(0, QLatin1Char('~'));
+ }
+ fragment.name.insert(0, contextName);
+ }
+ }
+
+ /**
+ * \return Current context name with definition name if different
+ * from the current definition name
+ */
+ QString extractContextName(StateData *stateData) const
+ {
+ QString label;
+
+ if (m_hasStackSizeTrace) {
+ // first state is empty
+ int stateSize = stateData ? stateData->size() : 0;
+ label = QLatin1Char('(') % QString::number(stateSize) % QLatin1Char(')');
+ }
+
+ if (m_hasContextTrace) {
+ // first state is empty
+ if (!stateData) {
+ return label + QStringLiteral("[???]");
+ }
+
+ const auto context = stateData->topContext();
+ const auto defDataIt = m_defDataBycontexts.find(context);
+ const auto contextName = (defDataIt != m_defDataBycontexts.end()) ? QString(QLatin1Char('<') % (*defDataIt)->name % QLatin1Char('>')) : QString();
+ return QString(label % contextName % QLatin1Char('[') % context->name() % QLatin1Char(']'));
+ }
+
+ return label;
+ }
+
+ void printFormats(QTextStream &out, QLatin1String regionStyle, const std::vector<QPair<QString, QString>> &ansiStyles)
+ {
+ // init graph
+ m_formatGraph.clear();
+ for (auto const &fragment : m_highlightedFragments) {
+ GraphLine &line = lineAtOffset(m_formatGraph, fragment.offset);
+ auto const &style = ansiStyles[fragment.formatId].first;
+ line.pushLabel(fragment.offset, style % nameStyle % fragment.name % regionStyle, fragment.name.size());
+
+ for (GraphLine *pline = m_formatGraph.data(); pline <= &line; ++pline) {
+ pline->pushGraph(fragment.offset, style % graphSymbol % regionStyle, 1);
+ }
+ }
+
+ // display graph
+ out << regionStyle;
+ auto first = m_formatGraph.begin();
+ auto last = m_formatGraph.end();
+ --last;
+ for (; first != last; ++first) {
+ out << first->graphLine << "\x1b[K\n" << first->labelLine << "\x1b[K\n";
+ }
+ out << first->graphLine << "\x1b[K\n" << first->labelLine << "\x1b[K\x1b[0m\n";
+ }
+
+ void printRegions(QTextStream &out, QLatin1String regionStyle, int lineLength)
+ {
+ const QString continuationLine = QStringLiteral(
+ "------------------------------"
+ "------------------------------"
+ "------------------------------");
+
+ bool hasContinuation = false;
+
+ m_regionGraph.clear();
+ QString label;
+ QString numStr;
+
+ // init graph
+ for (Region &region : m_regions) {
+ if (region.state == Region::State::Continuation) {
+ hasContinuation = true;
+ continue;
+ }
+
+ auto pushGraphs = [&](int offset, const GraphLine *endline, QStringView style) {
+ for (GraphLine *pline = m_regionGraph.data(); pline <= endline; ++pline) {
+ // a label can hide a graph
+ if (pline->graphLineLength <= offset) {
+ pline->pushGraph(offset, style % graphSymbol % regionStyle, 1);
+ }
+ }
+ };
+
+ QChar openChar;
+ QChar closeChar;
+ int lpad = 0;
+ int rpad = 0;
+
+ int offsetLabel = region.offset;
+
+ numStr.setNum(region.regionId);
+
+ if (region.state == Region::State::Open) {
+ openChar = QLatin1Char('(');
+ if (region.bindIndex == -1) {
+ rpad = lineLength - region.offset - numStr.size();
+ } else {
+ rpad = m_regions[region.bindIndex].offset - region.offset - 2;
+ closeChar = QLatin1Char(')');
+ }
+ // close without open
+ } else if (region.bindIndex == -1) {
+ closeChar = QLatin1Char('>');
+ // label already present, we only display the graph
+ } else if (m_regions[region.bindIndex].state == Region::State::Open) {
+ const auto &openRegion = m_regions[region.bindIndex];
+ // here offset is a graph index
+ const GraphLine &line = m_regionGraph[openRegion.offset];
+ const auto &style = m_regionStyles[openRegion.depth % m_regionStyles.size()];
+ pushGraphs(region.offset, &line, style);
+ continue;
+ } else {
+ closeChar = QLatin1Char(')');
+ lpad = region.offset - numStr.size();
+ offsetLabel = 0;
+ }
+
+ const QStringView openS(&openChar, openChar.unicode() ? 1 : 0);
+ const QStringView closeS(&closeChar, closeChar.unicode() ? 1 : 0);
+
+ label.clear();
+ fillString(label, lpad, continuationLine);
+ label += numStr;
+ fillString(label, rpad, continuationLine);
+
+ GraphLine &line = lineAtOffset(m_regionGraph, offsetLabel);
+ const auto &style = m_regionStyles[region.depth % m_regionStyles.size()];
+ line.pushLabel(offsetLabel, style % nameStyle % openS % label % closeS % regionStyle, label.size() + openS.size() + closeS.size());
+ pushGraphs(region.offset, &line, style);
+
+ // transforms offset into graph index when region is on 1 line
+ if (region.state == Region::State::Open && region.bindIndex != -1) {
+ region.offset = &line - m_regionGraph.data();
+ }
+ }
+
+ out << regionStyle;
+
+ // display regions which are neither closed nor open
+ if (hasContinuation) {
+ label.clear();
+ fillString(label, lineLength ? lineLength : 5, continuationLine);
+ for (const auto &region : m_regions) {
+ if (region.state == Region::State::Continuation && region.bindIndex == -1) {
+ const auto &style = m_regionStyles[region.depth % m_regionStyles.size()];
+ out << style << nameStyle << label << regionStyle << "\x1b[K\n";
+ }
+ }
+ }
+
+ // display graph
+ if (!m_regionGraph.empty()) {
+ auto first = m_regionGraph.rbegin();
+ auto last = m_regionGraph.rend();
+ --last;
+ for (; first != last; ++first) {
+ out << first->labelLine << "\x1b[K\n" << first->graphLine << "\x1b[K\n";
+ }
+ out << first->labelLine << "\x1b[K\n" << first->graphLine << "\x1b[K\x1b[0m\n";
+ }
+
+ // keep regions that are not closed
+ m_regions.erase(std::remove_if(m_regions.begin(),
+ m_regions.end(),
+ [](Region const &region) {
+ return region.bindIndex != -1 || region.state == Region::State::Close;
+ }),
+ m_regions.end());
+ // all remaining regions become Continuation
+ for (auto &region : m_regions) {
+ region.offset = 0;
+ region.state = Region::State::Continuation;
+ }
+ }
+
+ struct HighlightFragment {
+ QString name;
+ int offset;
+ int length;
+ int formatId;
+ };
+
+ struct ContextCaptureHighlighter : KSyntaxHighlighting::AbstractHighlighter {
+ int offset;
+ int length;
+ int offsetNext;
+ int lengthNext;
+
+ void applyFormat(int offset, int length, const KSyntaxHighlighting::Format & /*format*/) override
+ {
+ offset = offsetNext;
+ length = lengthNext;
+ offsetNext = offset;
+ lengthNext = length;
+ }
+
+ using KSyntaxHighlighting::AbstractHighlighter::highlightLine;
+ };
+
+ struct Region {
+ enum class State : int8_t {
+ Open,
+ Close,
+ Continuation,
+ };
+
+ int depth;
+ int offset;
+ int bindIndex;
+ int regionId;
+ State state;
+ };
+
+ bool m_hasFormatTrace;
+ bool m_hasRegionTrace;
+ bool m_hasStackSizeTrace;
+ bool m_hasContextTrace;
+
+ std::vector<HighlightFragment> m_highlightedFragments;
+ std::vector<GraphLine> m_formatGraph;
+ ContextCaptureHighlighter m_contextCapture;
+
+ int m_regionDepth = 0;
+ std::vector<Region> m_regions;
+ std::vector<GraphLine> m_regionGraph;
+ std::vector<QStringView> m_regionStyles;
+
+ QHash<const Context *, const DefinitionData *> m_defDataBycontexts;
+};
+} // anonymous namespace
+
+class KSyntaxHighlighting::AnsiHighlighterPrivate : public AbstractHighlighterPrivate
+{
+public:
+ QTextStream out;
+ QFile file;
+ QStringView currentLine;
+ // pairs of startColor / resetColor
+ std::vector<QPair<QString, QString>> ansiStyles;
+};
+
+AnsiHighlighter::AnsiHighlighter()
+ : AbstractHighlighter(new AnsiHighlighterPrivate())
+{
+}
+
+AnsiHighlighter::~AnsiHighlighter() = default;
+
+void AnsiHighlighter::setOutputFile(const QString &fileName)
+{
+ Q_D(AnsiHighlighter);
+ if (d->file.isOpen()) {
+ d->file.close();
+ }
+ d->file.setFileName(fileName);
+ if (!d->file.open(QFile::WriteOnly | QFile::Truncate)) {
+ qCWarning(Log) << "Failed to open output file" << fileName << ":" << d->file.errorString();
+ return;
+ }
+ d->out.setDevice(&d->file);
+}
+
+void AnsiHighlighter::setOutputFile(FILE *fileHandle)
+{
+ Q_D(AnsiHighlighter);
+ d->file.open(fileHandle, QIODevice::WriteOnly);
+ d->out.setDevice(&d->file);
+}
+
+void AnsiHighlighter::highlightFile(const QString &fileName, AnsiFormat format, Options options)
+{
+ QFileInfo fi(fileName);
+ QFile f(fileName);
+ if (!f.open(QFile::ReadOnly)) {
+ qCWarning(Log) << "Failed to open input file" << fileName << ":" << f.errorString();
+ return;
+ }
+
+ highlightData(&f, format, options);
+}
+
+void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, Options options)
+{
+ Q_D(AnsiHighlighter);
+
+ if (!d->out.device()) {
+ qCWarning(Log) << "No output stream defined!";
+ return;
+ }
+
+ const auto is256Colors = (format == AnsiFormat::XTerm256Color);
+ const auto &theme = d->m_theme;
+ const auto &definition = d->m_definition;
+
+ auto definitions = definition.includedDefinitions();
+ definitions.append(definition);
+
+ AnsiBuffer::ColorCache colorCache;
+
+ AnsiBuffer foregroundColorBuffer;
+ AnsiBuffer backgroundColorBuffer;
+ QLatin1String foregroundDefaultColor;
+ QLatin1String backgroundDefaultColor;
+
+ const bool useEditorBackground = options.testFlag(Option::UseEditorBackground);
+
+ // https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
+
+ if (useEditorBackground) {
+ const QRgb foregroundColor = theme.textColor(Theme::Normal);
+ const QRgb backgroundColor = theme.editorColor(Theme::BackgroundColor);
+ foregroundColorBuffer.appendForeground(foregroundColor, is256Colors, colorCache);
+ backgroundColorBuffer.append(QLatin1String("\x1b["));
+ backgroundColorBuffer.appendBackground(backgroundColor, is256Colors, colorCache);
+ foregroundDefaultColor = foregroundColorBuffer.latin1();
+ backgroundDefaultColor = backgroundColorBuffer.latin1().mid(2);
+ }
+
+ int maxId = 0;
+ for (const auto &definition : std::as_const(definitions)) {
+ for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) {
+ maxId = qMax(maxId, format.id());
+ }
+ }
+ d->ansiStyles.clear();
+ // ansiStyles must not be empty for applyFormat to work even with a definition without any context
+ d->ansiStyles.resize(maxId + 1);
+
+ // initialize ansiStyles
+ for (const auto &definition : std::as_const(definitions)) {
+ for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) {
+ AnsiBuffer buffer;
+
+ buffer.append(QLatin1String("\x1b["));
+
+ const bool hasFg = format.hasTextColor(theme);
+ const bool hasBg = format.hasBackgroundColor(theme);
+ const bool hasBold = format.isBold(theme);
+ const bool hasItalic = format.isItalic(theme);
+ const bool hasUnderline = format.isUnderline(theme);
+ const bool hasStrikeThrough = format.isStrikeThrough(theme);
+
+ if (hasFg) {
+ buffer.appendForeground(format.textColor(theme).rgb(), is256Colors, colorCache);
+ } else {
+ buffer.append(foregroundDefaultColor);
+ }
+ if (hasBg) {
+ buffer.appendBackground(format.backgroundColor(theme).rgb(), is256Colors, colorCache);
+ }
+ if (hasBold) {
+ buffer.append(QLatin1String("1;"));
+ }
+ if (hasItalic) {
+ buffer.append(QLatin1String("3;"));
+ }
+ if (hasUnderline) {
+ buffer.append(QLatin1String("4;"));
+ }
+ if (hasStrikeThrough) {
+ buffer.append(QLatin1String("9;"));
+ }
+
+ // if there is ANSI style
+ if (buffer.latin1().size() > 2) {
+ buffer.setFinalStyle();
+ auto &style = d->ansiStyles[format.id()];
+ style.first = buffer.latin1();
+
+ if (useEditorBackground) {
+ buffer.clear();
+ const bool hasEffect = hasBold || hasItalic || hasUnderline || hasStrikeThrough;
+ if (hasBg) {
+ buffer.append(hasEffect ? QLatin1String("\x1b[0;") : QLatin1String("\x1b["));
+ buffer.append(backgroundDefaultColor);
+ buffer.setFinalStyle();
+ style.second = buffer.latin1();
+ } else if (hasEffect) {
+ buffer.append(QLatin1String("\x1b["));
+ if (hasBold) {
+ buffer.append(QLatin1String("22;"));
+ }
+ if (hasItalic) {
+ buffer.append(QLatin1String("23;"));
+ }
+ if (hasUnderline) {
+ buffer.append(QLatin1String("24;"));
+ }
+ if (hasStrikeThrough) {
+ buffer.append(QLatin1String("29;"));
+ }
+ buffer.setFinalStyle();
+ style.second = buffer.latin1();
+ }
+ } else {
+ style.second = QStringLiteral("\x1b[0m");
+ }
+ }
+ }
+ }
+
+ if (useEditorBackground) {
+ backgroundColorBuffer.setFinalStyle();
+ backgroundDefaultColor = backgroundColorBuffer.latin1();
+ d->out << backgroundDefaultColor;
+ }
+
+ QTextStream in(dev);
+
+ if (!options.testAnyFlag(Option::TraceAll)) {
+ State state;
+ QString currentLine;
+ const bool isUnbuffered = options.testFlag(Option::Unbuffered);
+ while (in.readLineInto(&currentLine)) {
+ d->currentLine = currentLine;
+ state = highlightLine(d->currentLine, state);
+
+ if (useEditorBackground) {
+ d->out << QStringLiteral("\x1b[K\n");
+ } else {
+ d->out << QLatin1Char('\n');
+ }
+
+ if (isUnbuffered) {
+ d->out.flush();
+ }
+ }
+ } else {
+ AnsiBuffer buffer;
+ buffer.append(QLatin1String("\x1b[0;"));
+ buffer.appendBackground(theme.editorColor(useEditorBackground ? Theme::TemplateBackground : Theme::BackgroundColor), is256Colors, colorCache);
+ buffer.setFinalStyle();
+ DebugSyntaxHighlighter debugHighlighter;
+ debugHighlighter.setDefinition(definition);
+ debugHighlighter.highlightData(in, d->out, buffer.latin1(), backgroundDefaultColor, d->ansiStyles, options);
+ }
+
+ if (useEditorBackground) {
+ d->out << QStringLiteral("\x1b[0m");
+ }
+
+ d->out.setDevice(nullptr);
+ d->file.close();
+ d->ansiStyles.clear();
+}
+
+void AnsiHighlighter::applyFormat(int offset, int length, const Format &format)
+{
+ Q_D(AnsiHighlighter);
+ auto const &ansiStyle = d->ansiStyles[format.id()];
+ d->out << ansiStyle.first << d->currentLine.mid(offset, length) << ansiStyle.second;
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.h
new file mode 100644
index 0000000000..0942aa0242
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.h
@@ -0,0 +1,66 @@
+/*
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#ifndef KSYNTAXHIGHLIGHTING_ANSIHIGHLIGHTER_H
+#define KSYNTAXHIGHLIGHTING_ANSIHIGHLIGHTER_H
+
+#include "abstracthighlighter.h"
+#include "ksyntaxhighlighting_export.h"
+
+#include <QFlags>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+QT_END_NAMESPACE
+
+namespace KSyntaxHighlighting
+{
+class AnsiHighlighterPrivate;
+
+// Exported for a bundled helper program
+class KSYNTAXHIGHLIGHTING_EXPORT AnsiHighlighter final : public AbstractHighlighter
+{
+public:
+ enum class AnsiFormat {
+ TrueColor,
+ XTerm256Color,
+ };
+
+ enum class Option {
+ NoOptions,
+ UseEditorBackground = 1 << 0,
+ Unbuffered = 1 << 1,
+
+ // Options that displays a useful visual aid for syntax creation
+ TraceFormat = 1 << 2,
+ TraceRegion = 1 << 3,
+ TraceContext = 1 << 4,
+ TraceStackSize = 1 << 5,
+ TraceAll = TraceFormat | TraceRegion | TraceContext | TraceStackSize,
+ };
+ Q_DECLARE_FLAGS(Options, Option)
+
+ AnsiHighlighter();
+ ~AnsiHighlighter() override;
+
+ void highlightFile(const QString &fileName, AnsiFormat format = AnsiFormat::TrueColor, Options options = Option::UseEditorBackground);
+ void highlightData(QIODevice *device, AnsiFormat format = AnsiFormat::TrueColor, Options options = Option::UseEditorBackground);
+
+ void setOutputFile(const QString &fileName);
+ void setOutputFile(FILE *fileHandle);
+
+protected:
+ void applyFormat(int offset, int length, const Format &format) override;
+
+private:
+ Q_DECLARE_PRIVATE(AnsiHighlighter)
+};
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(KSyntaxHighlighting::AnsiHighlighter::Options)
+
+#endif // KSYNTAXHIGHLIGHTING_ANSIHIGHLIGHTER_H
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp
index 9887b959d0..af269d14d0 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp
@@ -1,32 +1,16 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
#include "context_p.h"
#include "definition_p.h"
#include "format.h"
+#include "ksyntaxhighlighting_logging.h"
#include "repository.h"
#include "rule_p.h"
-#include "ksyntaxhighlighting_logging.h"
#include "xml_p.h"
#include <QString>
@@ -34,173 +18,122 @@
using namespace KSyntaxHighlighting;
-Definition Context::definition() const
-{
- return m_def.definition();
-}
-
-void Context::setDefinition(const DefinitionRef &def)
+Context::Context(const DefinitionData &def, const HighlightingContextData &data)
+ : m_name(data.name)
+ , m_attributeFormat(data.attribute.isEmpty() ? Format() : def.formatByName(data.attribute))
+ , m_indentationBasedFolding(!data.noIndentationBasedFolding && def.indentationBasedFolding)
{
- m_def = def;
+ if (!data.attribute.isEmpty() && !m_attributeFormat.isValid()) {
+ qCWarning(Log) << "Context: Unknown format" << data.attribute << "in context" << m_name << "of definition" << def.name;
+ }
}
bool Context::indentationBasedFoldingEnabled() const
{
- if (m_noIndentationBasedFolding)
- return false;
-
- return m_def.definition().indentationBasedFoldingEnabled();
+ return m_indentationBasedFolding;
}
-void Context::load(QXmlStreamReader& reader)
+void Context::resolveContexts(DefinitionData &def, const HighlightingContextData &data)
{
- Q_ASSERT(reader.name() == QLatin1String("context"));
- Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
-
- m_name = reader.attributes().value(QStringLiteral("name")).toString();
- m_attribute = reader.attributes().value(QStringLiteral("attribute")).toString();
- m_lineEndContext.parse(reader.attributes().value(QStringLiteral("lineEndContext")));
- m_lineEmptyContext.parse(reader.attributes().value(QStringLiteral("lineEmptyContext")));
- m_fallthrough = Xml::attrToBool(reader.attributes().value(QStringLiteral("fallthrough")));
- m_fallthroughContext.parse(reader.attributes().value(QStringLiteral("fallthroughContext")));
- if (m_fallthroughContext.isStay())
- m_fallthrough = false;
- m_noIndentationBasedFolding = Xml::attrToBool(reader.attributes().value(QStringLiteral("noIndentationBasedFolding")));
-
- reader.readNext();
- while (!reader.atEnd()) {
- switch (reader.tokenType()) {
- case QXmlStreamReader::StartElement:
- {
- auto rule = Rule::create(reader.name());
- if (rule) {
- rule->setDefinition(m_def.definition());
- if (rule->load(reader))
- m_rules.push_back(rule);
- } else {
- reader.skipCurrentElement();
- }
- reader.readNext();
- break;
- }
- case QXmlStreamReader::EndElement:
- return;
- default:
- reader.readNext();
- break;
- }
- }
-}
+ m_lineEndContext.resolve(def, data.lineEndContext);
+ m_lineEmptyContext.resolve(def, data.lineEmptyContext);
+ m_fallthroughContext.resolve(def, data.fallthroughContext);
+ m_stopEmptyLineContextSwitchLoop = data.stopEmptyLineContextSwitchLoop;
-void Context::resolveContexts()
-{
- const auto def = m_def.definition();
- m_lineEndContext.resolve(def);
- m_lineEmptyContext.resolve(def);
- m_fallthroughContext.resolve(def);
- for (const auto &rule : m_rules)
- rule->resolveContext();
-}
+ /**
+ * line end context switches only when lineEmptyContext is #stay. This avoids
+ * skipping empty lines after a line continuation character (see bug 405903)
+ */
+ if (m_lineEmptyContext.isStay()) {
+ m_lineEmptyContext = m_lineEndContext;
+ }
-Context::ResolveState Context::resolveState()
-{
- if (m_resolveState == Unknown) {
- for (const auto &rule : m_rules) {
- auto inc = std::dynamic_pointer_cast<IncludeRules>(rule);
- if (inc) {
- m_resolveState = Unresolved;
- return m_resolveState;
- }
+ m_rules.reserve(data.rules.size());
+ for (const auto &ruleData : data.rules) {
+ m_rules.push_back(Rule::create(def, ruleData, m_name));
+ if (!m_rules.back()) {
+ m_rules.pop_back();
}
- m_resolveState = Resolved;
}
- return m_resolveState;
}
-void Context::resolveIncludes()
+void Context::resolveIncludes(DefinitionData &def)
{
- if (resolveState() == Resolved)
+ if (m_resolveState == Resolved) {
return;
- if (resolveState() == Resolving) {
+ }
+ if (m_resolveState == Resolving) {
qCWarning(Log) << "Cyclic dependency!";
return;
}
- Q_ASSERT(resolveState() == Unresolved);
+ Q_ASSERT(m_resolveState == Unresolved);
m_resolveState = Resolving; // cycle guard
for (auto it = m_rules.begin(); it != m_rules.end();) {
- auto inc = std::dynamic_pointer_cast<IncludeRules>(*it);
- if (!inc) {
+ const IncludeRules *includeRules = it->get()->castToIncludeRules();
+ if (!includeRules) {
+ m_hasDynamicRule = m_hasDynamicRule || it->get()->isDynamic();
++it;
continue;
}
- Context* context = nullptr;
- auto myDefData = DefinitionData::get(m_def.definition());
- if (inc->definitionName().isEmpty()) { // local include
- context = myDefData->contextByName(inc->contextName());
+
+ Context *context = nullptr;
+ DefinitionData *defData = &def;
+
+ const auto &contextName = includeRules->contextName();
+ const int idx = contextName.indexOf(QLatin1String("##"));
+
+ if (idx == -1) { // local include
+ context = def.contextByName(contextName);
} else {
- auto def = myDefData->repo->definitionForName(inc->definitionName());
- if (!def.isValid()) {
- qCWarning(Log) << "Unable to resolve external include rule for definition" << inc->definitionName() << "in" << m_def.definition().name();
+ auto definitionName = contextName.mid(idx + 2);
+ auto includedDef = def.repo->definitionForName(definitionName);
+ if (!includedDef.isValid()) {
+ qCWarning(Log) << "Unable to resolve external include rule for definition" << definitionName << "in" << def.name;
++it;
continue;
}
- auto defData = DefinitionData::get(def);
+ defData = DefinitionData::get(includedDef);
+ def.addImmediateIncludedDefinition(includedDef);
defData->load();
- if (inc->contextName().isEmpty())
+ if (idx == 0) {
context = defData->initialContext();
- else
- context = defData->contextByName(inc->contextName());
+ } else {
+ context = defData->contextByName(QStringView(contextName).left(idx));
+ }
}
+
if (!context) {
- qCWarning(Log) << "Unable to resolve include rule for definition" << inc->contextName() << "##" << inc->definitionName() << "in" << m_def.definition().name();
+ qCWarning(Log) << "Unable to resolve include rule for definition" << contextName << "in" << def.name;
+ ++it;
+ continue;
+ }
+
+ if (context == this) {
+ qCWarning(Log) << "Unable to resolve self include rule for definition" << contextName << "in" << def.name;
++it;
continue;
}
- context->resolveIncludes();
+
+ if (context->m_resolveState != Resolved) {
+ context->resolveIncludes(*defData);
+ }
+
+ m_hasDynamicRule = m_hasDynamicRule || context->m_hasDynamicRule;
/**
* handle included attribute
* transitive closure: we might include attributes included from somewhere else
*/
- if (inc->includeAttribute()) {
- m_attribute = context->m_attribute;
- m_attributeContext = context->m_attributeContext ? context->m_attributeContext : context;
+ if (includeRules->includeAttribute()) {
+ m_attributeFormat = context->m_attributeFormat;
}
it = m_rules.erase(it);
- for (const auto &rule : context->rules()) {
- it = m_rules.insert(it, rule);
- ++it;
- }
+ it = m_rules.insert(it, context->rules().begin(), context->rules().end());
+ it += context->rules().size();
}
m_resolveState = Resolved;
}
-
-void Context::resolveAttributeFormat()
-{
- /**
- * try to get our format from the definition we stem from
- * we need to handle included attributes via m_attributeContext
- */
- if (!m_attribute.isEmpty()) {
- const auto def = m_attributeContext ? m_attributeContext->m_def.definition() : m_def.definition();
- m_attributeFormat = DefinitionData::get(def)->formatByName(m_attribute);
- if (!m_attributeFormat.isValid()) {
- if (m_attributeContext) {
- qCWarning(Log) << "Context: Unknown format" << m_attribute << "in context" << m_name << "of definition" << m_def.definition().name() << "from included context" << m_attributeContext->m_name << "of definition" << def.name();
- } else {
- qCWarning(Log) << "Context: Unknown format" << m_attribute << "in context" << m_name << "of definition" << m_def.definition().name();
- }
- }
- }
-
- /**
- * lookup formats for our rules
- */
- for (const auto &rule : m_rules) {
- rule->resolveAttributeFormat(this);
- }
-}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h
index a034d0e27d..8cf0f1bfab 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h
@@ -1,53 +1,35 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_CONTEXT_P_H
#define KSYNTAXHIGHLIGHTING_CONTEXT_P_H
-#include "rule_p.h"
#include "contextswitch_p.h"
-#include "definition.h"
-#include "definitionref_p.h"
#include "format.h"
+#include "highlightingdata_p.hpp"
+#include "rule_p.h"
#include <QString>
#include <vector>
-QT_BEGIN_NAMESPACE
-class QXmlStreamReader;
-QT_END_NAMESPACE
-
-namespace KSyntaxHighlighting {
+namespace KSyntaxHighlighting
+{
+class DefinitionData;
class Context
{
public:
- Context() = default;
- ~Context() = default;
+ Q_DISABLE_COPY(Context)
+
+ Context(Context &&) = default;
+ Context &operator=(Context &&) = default;
- Definition definition() const;
- void setDefinition(const DefinitionRef &def);
+ Context(const DefinitionData &def, const HighlightingContextData &data);
+ ~Context() = default;
const QString &name() const
{
@@ -66,7 +48,17 @@ public:
bool fallthrough() const
{
- return m_fallthrough;
+ return !m_fallthroughContext.isStay();
+ }
+
+ bool hasDynamicRule() const
+ {
+ return m_hasDynamicRule;
+ }
+
+ bool stopEmptyLineContextSwitchLoop() const
+ {
+ return m_stopEmptyLineContextSwitchLoop;
}
const ContextSwitch &fallthroughContext() const
@@ -90,49 +82,29 @@ public:
*/
bool indentationBasedFoldingEnabled() const;
- void load(QXmlStreamReader &reader);
- void resolveContexts();
- void resolveIncludes();
- void resolveAttributeFormat();
+ void resolveContexts(DefinitionData &def, const HighlightingContextData &data);
+ void resolveIncludes(DefinitionData &def);
private:
- Q_DISABLE_COPY(Context)
+ enum ResolveState : quint8 { Unresolved, Resolving, Resolved };
- enum ResolveState {
- Unknown,
- Unresolved,
- Resolving,
- Resolved
- };
- ResolveState resolveState();
+ std::vector<Rule::Ptr> m_rules;
- DefinitionRef m_def;
QString m_name;
- /**
- * attribute name, to lookup our format
- */
- QString m_attribute;
-
- /**
- * context to use for lookup, if != this context
- */
- const Context *m_attributeContext = nullptr;
-
- /**
- * resolved format for our attribute, done in resolveAttributeFormat
- */
- Format m_attributeFormat;
-
ContextSwitch m_lineEndContext;
ContextSwitch m_lineEmptyContext;
ContextSwitch m_fallthroughContext;
- std::vector<Rule::Ptr> m_rules;
+ /**
+ * resolved format for our attribute, done in constructor and resolveIncludes
+ */
+ Format m_attributeFormat;
- ResolveState m_resolveState = Unknown;
- bool m_fallthrough = false;
- bool m_noIndentationBasedFolding = false;
+ ResolveState m_resolveState = Unresolved;
+ bool m_hasDynamicRule = false;
+ bool m_stopEmptyLineContextSwitchLoop = true;
+ bool m_indentationBasedFolding;
};
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch.cpp
index a4d1dbbd85..71a5a01c29 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch.cpp
@@ -1,90 +1,51 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-License-Identifier: MIT
*/
#include "contextswitch_p.h"
#include "definition.h"
#include "definition_p.h"
-#include "repository.h"
+#include "highlightingdata_p.hpp"
#include "ksyntaxhighlighting_logging.h"
-
+#include "repository.h"
using namespace KSyntaxHighlighting;
-bool ContextSwitch::isStay() const
+void ContextSwitch::resolve(DefinitionData &def, QStringView contextInstr)
{
- return m_popCount == 0 && !m_context && m_contextName.isEmpty() && m_defName.isEmpty();
-}
+ HighlightingContextData::ContextSwitch ctx(contextInstr);
-int ContextSwitch::popCount() const
-{
- return m_popCount;
-}
+ m_popCount = ctx.popCount();
+ m_isStay = !m_popCount;
-Context* ContextSwitch::context() const
-{
- return m_context;
-}
+ auto contextName = ctx.contextName();
+ auto defName = ctx.defName();
-void ContextSwitch::parse(const QStringRef& contextInstr)
-{
- if (contextInstr.isEmpty() || contextInstr == QLatin1String("#stay"))
- return;
-
- if (contextInstr.startsWith(QLatin1String("#pop!"))) {
- ++m_popCount;
- m_contextName = contextInstr.mid(5).toString();
- return;
- }
-
- if (contextInstr.startsWith(QLatin1String("#pop"))) {
- ++m_popCount;
- parse(contextInstr.mid(4));
+ if (contextName.isEmpty() && defName.isEmpty()) {
return;
}
- const auto idx = contextInstr.indexOf(QLatin1String("##"));
- if (idx >= 0) {
- m_contextName = contextInstr.left(idx).toString();
- m_defName = contextInstr.mid(idx + 2).toString();
+ if (defName.isEmpty()) {
+ m_context = def.contextByName(contextName);
} else {
- m_contextName = contextInstr.toString();
+ auto d = def.repo->definitionForName(defName.toString());
+ if (d.isValid()) {
+ auto data = DefinitionData::get(d);
+ def.addImmediateIncludedDefinition(d);
+ data->load();
+ if (contextName.isEmpty()) {
+ m_context = data->initialContext();
+ } else {
+ m_context = data->contextByName(contextName);
+ }
+ }
}
-}
-void ContextSwitch::resolve(const Definition &def)
-{
- auto d = def;
- if (!m_defName.isEmpty()) {
- d = DefinitionData::get(def)->repo->definitionForName(m_defName);
- auto data = DefinitionData::get(d);
- data->load();
- if (m_contextName.isEmpty())
- m_context = data->initialContext();
- }
-
- if (!m_contextName.isEmpty()) {
- m_context = DefinitionData::get(d)->contextByName(m_contextName);
- if (!m_context)
- qCWarning(Log) << "cannot find context" << m_contextName << "in" << def.name();
+ if (!m_context) {
+ qCWarning(Log) << "cannot find context" << contextName << "in" << def.name;
+ } else {
+ m_isStay = false;
}
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch_p.h
index 669855af7a..29b0e685e2 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch_p.h
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_CONTEXTSWITCH_P_H
@@ -26,10 +9,10 @@
#include <QString>
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class Context;
-class Definition;
+class DefinitionData;
class ContextSwitch
{
@@ -37,19 +20,27 @@ public:
ContextSwitch() = default;
~ContextSwitch() = default;
- bool isStay() const;
+ bool isStay() const
+ {
+ return m_isStay;
+ }
+
+ int popCount() const
+ {
+ return m_popCount;
+ }
- int popCount() const;
- Context* context() const;
+ Context *context() const
+ {
+ return m_context;
+ }
- void parse(const QStringRef &contextInstr);
- void resolve(const Definition &def);
+ void resolve(DefinitionData &def, QStringView contextInstr);
private:
- QString m_defName;
- QString m_contextName;
Context *m_context = nullptr;
int m_popCount = 0;
+ bool m_isStay = true;
};
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp
index ae95a6b235..e2cca6da71 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp
@@ -1,28 +1,11 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
- Copyright (C) 2018 Dominik Haumann <dhaumann@kde.org>
- Copyright (C) 2018 Christoph Cullmann <cullmann@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2018 Dominik Haumann <dhaumann@kde.org>
+ SPDX-FileCopyrightText: 2018 Christoph Cullmann <cullmann@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+ SPDX-License-Identifier: MIT
+*/
#include "definition.h"
#include "definition_p.h"
@@ -31,66 +14,51 @@
#include "context_p.h"
#include "format.h"
#include "format_p.h"
+#include "highlightingdata_p.hpp"
+#include "ksyntaxhighlighting_logging.h"
+#include "ksyntaxhighlighting_version.h"
#include "repository.h"
#include "repository_p.h"
#include "rule_p.h"
-#include "ksyntaxhighlighting_logging.h"
-#include "ksyntaxhighlighting_version.h"
+#include "worddelimiters_p.h"
#include "xml_p.h"
+#include <QCborMap>
#include <QCoreApplication>
-#include <QDebug>
#include <QFile>
-#include <QHash>
-#include <QJsonObject>
-#include <QStringList>
-#include <QVector>
#include <QXmlStreamReader>
#include <algorithm>
+#include <atomic>
using namespace KSyntaxHighlighting;
DefinitionData::DefinitionData()
- : wordDelimiters(QStringLiteral("\t !%&()*+,-./:;<=>?[\\]^{|}~")) // must be sorted!
+ : wordDelimiters()
, wordWrapDelimiters(wordDelimiters)
{
}
-DefinitionData::~DefinitionData()
-{
- qDeleteAll(contexts);
-}
-
-DefinitionData* DefinitionData::get(const Definition &def)
-{
- return def.d.get();
-}
-
-Definition::Definition() :
- d(new DefinitionData)
-{
-}
+DefinitionData::~DefinitionData() = default;
-Definition::Definition(const Definition &other) :
- d(other.d)
+Definition::Definition()
+ : d(std::make_shared<DefinitionData>())
{
d->q = *this;
}
-Definition::Definition(const std::shared_ptr<DefinitionData> &dd) :
- d(dd)
-{
-}
-
-Definition::~Definition()
-{
-}
+Definition::Definition(Definition &&other) noexcept = default;
+Definition::Definition(const Definition &) = default;
+Definition::~Definition() = default;
+Definition &Definition::operator=(Definition &&other) noexcept = default;
+Definition &Definition::operator=(const Definition &) = default;
-Definition& Definition::operator=(const Definition &rhs)
+Definition::Definition(std::shared_ptr<DefinitionData> &&dd)
+ : d(std::move(dd))
{
- d = rhs.d;
- return *this;
+ if (!d) {
+ Definition().d.swap(d);
+ }
}
bool Definition::operator==(const Definition &other) const
@@ -98,7 +66,7 @@ bool Definition::operator==(const Definition &other) const
return d->fileName == other.d->fileName;
}
-bool Definition::operator!=(const Definition& other) const
+bool Definition::operator!=(const Definition &other) const
{
return d->fileName != other.d->fileName;
}
@@ -120,7 +88,10 @@ QString Definition::name() const
QString Definition::translatedName() const
{
- return QCoreApplication::instance()->translate("Language", d->name.toUtf8().constData());
+ if (d->translatedName.isEmpty()) {
+ d->translatedName = QCoreApplication::instance()->translate("Language", d->nameUtf8.isEmpty() ? d->name.toUtf8().constData() : d->nameUtf8.constData());
+ }
+ return d->translatedName;
}
QString Definition::section() const
@@ -130,15 +101,19 @@ QString Definition::section() const
QString Definition::translatedSection() const
{
- return QCoreApplication::instance()->translate("Language Section", d->section.toUtf8().constData());
+ if (d->translatedSection.isEmpty()) {
+ d->translatedSection = QCoreApplication::instance()->translate("Language Section",
+ d->sectionUtf8.isEmpty() ? d->section.toUtf8().constData() : d->sectionUtf8.constData());
+ }
+ return d->translatedSection;
}
-QVector<QString> Definition::mimeTypes() const
+QList<QString> Definition::mimeTypes() const
{
return d->mimetypes;
}
-QVector<QString> Definition::extensions() const
+QList<QString> Definition::extensions() const
{
return d->extensions;
}
@@ -181,13 +156,13 @@ QString Definition::license() const
bool Definition::isWordDelimiter(QChar c) const
{
d->load();
- return d->isWordDelimiter(c);
+ return d->wordDelimiters.contains(c);
}
bool Definition::isWordWrapDelimiter(QChar c) const
{
d->load();
- return std::binary_search(d->wordWrapDelimiters.constBegin(), d->wordWrapDelimiters.constEnd(), c);
+ return d->wordWrapDelimiters.contains(c);
}
bool Definition::foldingEnabled() const
@@ -227,69 +202,60 @@ QStringList Definition::keywordLists() const
return d->keywordLists.keys();
}
-QStringList Definition::keywordList(const QString& name) const
+QStringList Definition::keywordList(const QString &name) const
{
d->load(DefinitionData::OnlyKeywords(true));
const auto list = d->keywordList(name);
return list ? list->keywords() : QStringList();
}
-QVector<Format> Definition::formats() const
+bool Definition::setKeywordList(const QString &name, const QStringList &content)
+{
+ d->load(DefinitionData::OnlyKeywords(true));
+ KeywordList *list = d->keywordList(name);
+ if (list) {
+ list->setKeywordList(content);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+QList<Format> Definition::formats() const
{
d->load();
// sort formats so that the order matches the order of the itemDatas in the xml files.
- auto formatList = QVector<Format>::fromList(d->formats.values());
- std::sort(formatList.begin(), formatList.end(), [](const KSyntaxHighlighting::Format & lhs, const KSyntaxHighlighting::Format & rhs){
+ auto formatList = d->formats.values();
+ std::sort(formatList.begin(), formatList.end(), [](const KSyntaxHighlighting::Format &lhs, const KSyntaxHighlighting::Format &rhs) {
return lhs.id() < rhs.id();
});
return formatList;
}
-QVector<Definition> Definition::includedDefinitions() const
+QList<Definition> Definition::includedDefinitions() const
{
d->load();
// init worklist and result used as guard with this definition
- QVector<Definition> queue{*this};
- QVector<Definition> definitions{*this};
- while (!queue.isEmpty()) {
- // Iterate all context rules to find associated Definitions. This will
- // automatically catch other Definitions referenced with IncludeRuldes or ContextSwitch.
- const auto definition = queue.takeLast();
- for (const auto & context : qAsConst(definition.d->contexts)) {
- // handle context switch attributes of this context itself
- for (const auto switchContext : {context->lineEndContext().context(), context->lineEmptyContext().context(), context->fallthroughContext().context()}) {
- if (switchContext) {
- if (!definitions.contains(switchContext->definition())) {
- queue.push_back(switchContext->definition());
- definitions.push_back(switchContext->definition());
- }
- }
- }
-
- // handle the embedded rules
- for (const auto &rule : context->rules()) {
- // handle include rules like inclusion
- if (!definitions.contains(rule->definition())) {
- queue.push_back(rule->definition());
- definitions.push_back(rule->definition());
- }
-
- // handle context switch context inclusion
- if (auto switchContext = rule->context().context()) {
- if (!definitions.contains(switchContext->definition())) {
- queue.push_back(switchContext->definition());
- definitions.push_back(switchContext->definition());
- }
- }
+ QList<const DefinitionData *> queue{d.get()};
+ QList<Definition> definitions{*this};
+ while (!queue.empty()) {
+ const auto *def = queue.back();
+ queue.pop_back();
+ for (const auto &defRef : std::as_const(def->immediateIncludedDefinitions)) {
+ const auto definition = defRef.definition();
+ if (!definitions.contains(definition)) {
+ definitions.push_back(definition);
+ queue.push_back(definition.d.get());
}
}
}
// remove the 1st entry, since it is this Definition
- definitions.pop_front();
+ definitions.front() = std::move(definitions.back());
+ definitions.pop_back();
return definitions;
}
@@ -309,75 +275,82 @@ CommentPosition Definition::singleLineCommentPosition() const
QPair<QString, QString> Definition::multiLineCommentMarker() const
{
d->load();
- return { d->multiLineCommentStartMarker, d->multiLineCommentEndMarker };
+ return {d->multiLineCommentStartMarker, d->multiLineCommentEndMarker};
}
-QVector<QPair<QChar, QString>> Definition::characterEncodings() const
+QList<QPair<QChar, QString>> Definition::characterEncodings() const
{
d->load();
return d->characterEncodings;
}
-Context* DefinitionData::initialContext() const
+Context *DefinitionData::initialContext()
{
- Q_ASSERT(!contexts.isEmpty());
- return contexts.first();
+ Q_ASSERT(!contexts.empty());
+ return &contexts.front();
}
-Context* DefinitionData::contextByName(const QString& wantedName) const
+Context *DefinitionData::contextByName(QStringView wantedName)
{
- for (const auto context : contexts) {
- if (context->name() == wantedName)
- return context;
+ for (auto &context : contexts) {
+ if (context.name() == wantedName) {
+ return &context;
+ }
}
return nullptr;
}
-KeywordList *DefinitionData::keywordList(const QString& wantedName)
+KeywordList *DefinitionData::keywordList(const QString &wantedName)
{
auto it = keywordLists.find(wantedName);
return (it == keywordLists.end()) ? nullptr : &it.value();
}
-bool DefinitionData::isWordDelimiter(QChar c) const
-{
- return std::binary_search(wordDelimiters.constBegin(), wordDelimiters.constEnd(), c);
-}
-
-Format DefinitionData::formatByName(const QString& wantedName) const
+Format DefinitionData::formatByName(const QString &wantedName) const
{
const auto it = formats.constFind(wantedName);
- if (it != formats.constEnd())
+ if (it != formats.constEnd()) {
return it.value();
+ }
return Format();
}
bool DefinitionData::isLoaded() const
{
- return !contexts.isEmpty();
+ return !contexts.empty();
+}
+
+namespace
+{
+std::atomic<uint64_t> definitionId{1};
}
bool DefinitionData::load(OnlyKeywords onlyKeywords)
{
- if (fileName.isEmpty())
+ if (fileName.isEmpty()) {
return false;
+ }
- if (isLoaded())
+ if (isLoaded()) {
return true;
+ }
- if (bool(onlyKeywords) && keywordIsLoaded)
+ if (bool(onlyKeywords) && keywordIsLoaded) {
return true;
+ }
QFile file(fileName);
- if (!file.open(QFile::ReadOnly))
+ if (!file.open(QFile::ReadOnly)) {
return false;
+ }
QXmlStreamReader reader(&file);
while (!reader.atEnd()) {
const auto token = reader.readNext();
- if (token != QXmlStreamReader::StartElement)
+ if (token != QXmlStreamReader::StartElement) {
continue;
+ }
if (reader.name() == QLatin1String("highlighting")) {
loadHighlighting(reader, onlyKeywords);
@@ -386,61 +359,79 @@ bool DefinitionData::load(OnlyKeywords onlyKeywords)
}
}
- else if (reader.name() == QLatin1String("general"))
+ else if (reader.name() == QLatin1String("general")) {
loadGeneral(reader);
+ }
}
for (auto it = keywordLists.begin(); it != keywordLists.end(); ++it) {
it->setCaseSensitivity(caseSensitive);
}
- for (const auto context : qAsConst(contexts)) {
- context->resolveContexts();
- context->resolveIncludes();
- context->resolveAttributeFormat();
- }
+ resolveContexts();
+
+ id = definitionId.fetch_add(1, std::memory_order_relaxed);
- Q_ASSERT(std::is_sorted(wordDelimiters.constBegin(), wordDelimiters.constEnd()));
return true;
}
void DefinitionData::clear()
{
// keep only name and repo, so we can re-lookup to make references persist over repo reloads
+ id = 0;
keywordLists.clear();
- qDeleteAll(contexts);
contexts.clear();
formats.clear();
+ contextDatas.clear();
+ immediateIncludedDefinitions.clear();
+ wordDelimiters = WordDelimiters();
+ wordWrapDelimiters = wordDelimiters;
+ keywordIsLoaded = false;
+ hasFoldingRegions = false;
+ indentationBasedFolding = false;
+ foldingIgnoreList.clear();
+ singleLineCommentMarker.clear();
+ singleLineCommentPosition = CommentPosition::StartOfLine;
+ multiLineCommentStartMarker.clear();
+ multiLineCommentEndMarker.clear();
+ characterEncodings.clear();
fileName.clear();
+ nameUtf8.clear();
+ translatedName.clear();
section.clear();
+ sectionUtf8.clear();
+ translatedSection.clear();
style.clear();
indenter.clear();
author.clear();
license.clear();
mimetypes.clear();
extensions.clear();
- wordDelimiters = QStringLiteral("\t !%&()*+,-./:;<=>?[\\]^{|}~"); // must be sorted!
- wordWrapDelimiters = wordDelimiters;
caseSensitive = Qt::CaseSensitive;
version = 0.0f;
priority = 0;
hidden = false;
+
+ // purge our cache that is used to unify states
+ unify.clear();
}
-bool DefinitionData::loadMetaData(const QString& definitionFileName)
+bool DefinitionData::loadMetaData(const QString &definitionFileName)
{
fileName = definitionFileName;
QFile file(definitionFileName);
- if (!file.open(QFile::ReadOnly))
+ if (!file.open(QFile::ReadOnly)) {
return false;
+ }
QXmlStreamReader reader(&file);
while (!reader.atEnd()) {
const auto token = reader.readNext();
- if (token != QXmlStreamReader::StartElement)
+ if (token != QXmlStreamReader::StartElement) {
continue;
+ }
if (reader.name() == QLatin1String("language")) {
return loadLanguage(reader);
}
@@ -449,25 +440,26 @@ bool DefinitionData::loadMetaData(const QString& definitionFileName)
return false;
}
-bool DefinitionData::loadMetaData(const QString &file, const QJsonObject &obj)
+bool DefinitionData::loadMetaData(const QString &file, const QCborMap &obj)
{
- name = obj.value(QLatin1String("name")).toString();
- section = obj.value(QLatin1String("section")).toString();
- version = obj.value(QLatin1String("version")).toInt();
- priority = obj.value(QLatin1String("priority")).toInt();
- style = obj.value(QLatin1String("style")).toString();
- author = obj.value(QLatin1String("author")).toString();
- license = obj.value(QLatin1String("license")).toString();
+ name = obj.value(QLatin1String("name")).toString();
+ nameUtf8 = obj.value(QLatin1String("name")).toByteArray();
+ section = obj.value(QLatin1String("section")).toString();
+ sectionUtf8 = obj.value(QLatin1String("section")).toByteArray();
+ version = obj.value(QLatin1String("version")).toInteger();
+ priority = obj.value(QLatin1String("priority")).toInteger();
+ style = obj.value(QLatin1String("style")).toString();
+ author = obj.value(QLatin1String("author")).toString();
+ license = obj.value(QLatin1String("license")).toString();
indenter = obj.value(QLatin1String("indenter")).toString();
- hidden = obj.value(QLatin1String("hidden")).toBool();
+ hidden = obj.value(QLatin1String("hidden")).toBool();
fileName = file;
const auto exts = obj.value(QLatin1String("extensions")).toString();
- for (const auto &ext : exts.split(QLatin1Char(';'), QString::SkipEmptyParts))
- extensions.push_back(ext);
+ extensions = exts.split(QLatin1Char(';'), Qt::SkipEmptyParts);
+
const auto mts = obj.value(QLatin1String("mimetype")).toString();
- for (const auto &mt : mts.split(QLatin1Char(';'), QString::SkipEmptyParts))
- mimetypes.push_back(mt);
+ mimetypes = mts.split(QLatin1Char(';'), Qt::SkipEmptyParts);
return true;
}
@@ -477,31 +469,35 @@ bool DefinitionData::loadLanguage(QXmlStreamReader &reader)
Q_ASSERT(reader.name() == QLatin1String("language"));
Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
- if (!checkKateVersion(reader.attributes().value(QStringLiteral("kateversion"))))
+ if (!checkKateVersion(reader.attributes().value(QLatin1String("kateversion")))) {
return false;
+ }
- name = reader.attributes().value(QStringLiteral("name")).toString();
- section = reader.attributes().value(QStringLiteral("section")).toString();
+ name = reader.attributes().value(QLatin1String("name")).toString();
+ section = reader.attributes().value(QLatin1String("section")).toString();
// toFloat instead of toInt for backward compatibility with old Kate files
- version = reader.attributes().value(QStringLiteral("version")).toFloat();
- priority = reader.attributes().value(QStringLiteral("priority")).toInt();
- hidden = Xml::attrToBool(reader.attributes().value(QStringLiteral("hidden")));
- style = reader.attributes().value(QStringLiteral("style")).toString();
- indenter = reader.attributes().value(QStringLiteral("indenter")).toString();
- author = reader.attributes().value(QStringLiteral("author")).toString();
- license = reader.attributes().value(QStringLiteral("license")).toString();
- const auto exts = reader.attributes().value(QStringLiteral("extensions")).toString();
- for (const auto &ext : exts.split(QLatin1Char(';'), QString::SkipEmptyParts))
- extensions.push_back(ext);
- const auto mts = reader.attributes().value(QStringLiteral("mimetype")).toString();
- for (const auto &mt : mts.split(QLatin1Char(';'), QString::SkipEmptyParts))
- mimetypes.push_back(mt);
- if (reader.attributes().hasAttribute(QStringLiteral("casesensitive")))
- caseSensitive = Xml::attrToBool(reader.attributes().value(QStringLiteral("casesensitive"))) ? Qt::CaseSensitive : Qt::CaseInsensitive;
+ version = reader.attributes().value(QLatin1String("version")).toFloat();
+ priority = reader.attributes().value(QLatin1String("priority")).toInt();
+ hidden = Xml::attrToBool(reader.attributes().value(QLatin1String("hidden")));
+ style = reader.attributes().value(QLatin1String("style")).toString();
+ indenter = reader.attributes().value(QLatin1String("indenter")).toString();
+ author = reader.attributes().value(QLatin1String("author")).toString();
+ license = reader.attributes().value(QLatin1String("license")).toString();
+ const auto exts = reader.attributes().value(QLatin1String("extensions"));
+ for (const auto &ext : exts.split(QLatin1Char(';'), Qt::SkipEmptyParts)) {
+ extensions.push_back(ext.toString());
+ }
+ const auto mts = reader.attributes().value(QLatin1String("mimetype"));
+ for (const auto &mt : mts.split(QLatin1Char(';'), Qt::SkipEmptyParts)) {
+ mimetypes.push_back(mt.toString());
+ }
+ if (reader.attributes().hasAttribute(QLatin1String("casesensitive"))) {
+ caseSensitive = Xml::attrToBool(reader.attributes().value(QLatin1String("casesensitive"))) ? Qt::CaseSensitive : Qt::CaseInsensitive;
+ }
return true;
}
-void DefinitionData::loadHighlighting(QXmlStreamReader& reader, OnlyKeywords onlyKeywords)
+void DefinitionData::loadHighlighting(QXmlStreamReader &reader, OnlyKeywords onlyKeywords)
{
Q_ASSERT(reader.name() == QLatin1String("highlighting"));
Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
@@ -511,35 +507,34 @@ void DefinitionData::loadHighlighting(QXmlStreamReader& reader, OnlyKeywords onl
while (!reader.atEnd()) {
switch (reader.tokenType()) {
- case QXmlStreamReader::StartElement:
- if (reader.name() == QLatin1String("list")) {
- if (!keywordIsLoaded) {
- KeywordList keywords;
- keywords.load(reader);
- keywordLists.insert(keywords.name(), keywords);
- }
- else {
- reader.skipCurrentElement();
- reader.readNext(); // Skip </list>
- }
- } else if (bool(onlyKeywords)) {
- resolveIncludeKeywords();
- return;
- } else if (reader.name() == QLatin1String("contexts")) {
- resolveIncludeKeywords();
- loadContexts(reader);
- reader.readNext();
- } else if (reader.name() == QLatin1String("itemDatas")) {
- loadItemData(reader);
+ case QXmlStreamReader::StartElement:
+ if (reader.name() == QLatin1String("list")) {
+ if (!keywordIsLoaded) {
+ KeywordList keywords;
+ keywords.load(reader);
+ keywordLists.insert(keywords.name(), keywords);
} else {
- reader.readNext();
+ reader.skipCurrentElement();
+ reader.readNext(); // Skip </list>
}
- break;
- case QXmlStreamReader::EndElement:
+ } else if (bool(onlyKeywords)) {
+ resolveIncludeKeywords();
return;
- default:
+ } else if (reader.name() == QLatin1String("contexts")) {
+ resolveIncludeKeywords();
+ loadContexts(reader);
reader.readNext();
- break;
+ } else if (reader.name() == QLatin1String("itemDatas")) {
+ loadItemData(reader);
+ } else {
+ reader.readNext();
+ }
+ break;
+ case QXmlStreamReader::EndElement:
+ return;
+ default:
+ reader.readNext();
+ break;
}
}
}
@@ -557,60 +552,95 @@ void DefinitionData::resolveIncludeKeywords()
}
}
-void DefinitionData::loadContexts(QXmlStreamReader& reader)
+void DefinitionData::loadContexts(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("contexts"));
Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
+ contextDatas.reserve(32);
+
while (!reader.atEnd()) {
switch (reader.tokenType()) {
- case QXmlStreamReader::StartElement:
- if (reader.name() == QLatin1String("context")) {
- auto context = new Context;
- context->setDefinition(q);
- context->load(reader);
- contexts.push_back(context);
- }
- reader.readNext();
- break;
- case QXmlStreamReader::EndElement:
- return;
- default:
- reader.readNext();
- break;
+ case QXmlStreamReader::StartElement:
+ if (reader.name() == QLatin1String("context")) {
+ contextDatas.push_back(HighlightingContextData());
+ contextDatas.back().load(name, reader);
+ }
+ reader.readNext();
+ break;
+ case QXmlStreamReader::EndElement:
+ return;
+ default:
+ reader.readNext();
+ break;
}
}
}
-void DefinitionData::loadItemData(QXmlStreamReader& reader)
+void DefinitionData::resolveContexts()
+{
+ contexts.reserve(contextDatas.size());
+
+ /**
+ * Transform all HighlightingContextData to Context.
+ * This is necessary so that Context::resolveContexts() can find the referenced contexts.
+ */
+ for (const auto &contextData : std::as_const(contextDatas)) {
+ contexts.emplace_back(*this, contextData);
+ }
+
+ /**
+ * Resolves contexts and rules.
+ */
+ auto ctxIt = contexts.begin();
+ for (const auto &contextData : std::as_const(contextDatas)) {
+ ctxIt->resolveContexts(*this, contextData);
+ ++ctxIt;
+ }
+
+ /**
+ * To free the memory, constDatas is emptied because it is no longer used.
+ */
+ contextDatas.clear();
+ contextDatas.shrink_to_fit();
+
+ /**
+ * Resolved includeRules.
+ */
+ for (auto &context : contexts) {
+ context.resolveIncludes(*this);
+ }
+}
+
+void DefinitionData::loadItemData(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("itemDatas"));
Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
while (!reader.atEnd()) {
switch (reader.tokenType()) {
- case QXmlStreamReader::StartElement:
- if (reader.name() == QLatin1String("itemData")) {
- Format f;
- auto formatData = FormatPrivate::detachAndGet(f);
- formatData->definition = q;
- formatData->load(reader);
- formatData->id = RepositoryPrivate::get(repo)->nextFormatId();
- formats.insert(f.name(), f);
- reader.readNext();
- }
+ case QXmlStreamReader::StartElement:
+ if (reader.name() == QLatin1String("itemData")) {
+ Format f;
+ auto formatData = FormatPrivate::detachAndGet(f);
+ formatData->definitionName = name;
+ formatData->load(reader);
+ formatData->id = RepositoryPrivate::get(repo)->nextFormatId();
+ formats.insert(f.name(), f);
reader.readNext();
- break;
- case QXmlStreamReader::EndElement:
- return;
- default:
- reader.readNext();
- break;
+ }
+ reader.readNext();
+ break;
+ case QXmlStreamReader::EndElement:
+ return;
+ default:
+ reader.readNext();
+ break;
}
}
}
-void DefinitionData::loadGeneral(QXmlStreamReader& reader)
+void DefinitionData::loadGeneral(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("general"));
Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
@@ -621,49 +651,50 @@ void DefinitionData::loadGeneral(QXmlStreamReader& reader)
while (!reader.atEnd()) {
switch (reader.tokenType()) {
- case QXmlStreamReader::StartElement:
- ++elementRefCounter;
-
- if (reader.name() == QLatin1String("keywords")) {
- if (reader.attributes().hasAttribute(QStringLiteral("casesensitive")))
- caseSensitive = Xml::attrToBool(reader.attributes().value(QStringLiteral("casesensitive"))) ? Qt::CaseSensitive : Qt::CaseInsensitive;
-
- // adapt sorted wordDelimiters
- wordDelimiters += reader.attributes().value(QStringLiteral("additionalDeliminator"));
- std::sort(wordDelimiters.begin(), wordDelimiters.end());
- auto it = std::unique(wordDelimiters.begin(), wordDelimiters.end());
- wordDelimiters.truncate(std::distance(wordDelimiters.begin(), it));
- for (const auto c : reader.attributes().value(QLatin1String("weakDeliminator")))
- wordDelimiters.remove(c);
-
- // adaptWordWrapDelimiters, and sort
- wordWrapDelimiters = reader.attributes().value(QStringLiteral("wordWrapDeliminator")).toString();
- std::sort(wordWrapDelimiters.begin(), wordWrapDelimiters.end());
- if (wordWrapDelimiters.isEmpty())
- wordWrapDelimiters = wordDelimiters;
- } else if (reader.name() == QLatin1String("folding")) {
- if (reader.attributes().hasAttribute(QStringLiteral("indentationsensitive")))
- indentationBasedFolding = Xml::attrToBool(reader.attributes().value(QStringLiteral("indentationsensitive")));
- } else if (reader.name() == QLatin1String("emptyLines")) {
- loadFoldingIgnoreList(reader);
- } else if (reader.name() == QLatin1String("comments")) {
- loadComments(reader);
- } else if (reader.name() == QLatin1String("spellchecking")) {
- loadSpellchecking(reader);
+ case QXmlStreamReader::StartElement:
+ ++elementRefCounter;
+
+ if (reader.name() == QLatin1String("keywords")) {
+ if (reader.attributes().hasAttribute(QLatin1String("casesensitive"))) {
+ caseSensitive = Xml::attrToBool(reader.attributes().value(QLatin1String("casesensitive"))) ? Qt::CaseSensitive : Qt::CaseInsensitive;
+ }
+
+ // adapt wordDelimiters
+ wordDelimiters.append(reader.attributes().value(QLatin1String("additionalDeliminator")));
+ wordDelimiters.remove(reader.attributes().value(QLatin1String("weakDeliminator")));
+
+ // adapt WordWrapDelimiters
+ auto wordWrapDeliminatorAttr = reader.attributes().value(QLatin1String("wordWrapDeliminator"));
+ if (wordWrapDeliminatorAttr.isEmpty()) {
+ wordWrapDelimiters = wordDelimiters;
} else {
- reader.skipCurrentElement();
+ wordWrapDelimiters.append(wordWrapDeliminatorAttr);
}
- reader.readNext();
- break;
- case QXmlStreamReader::EndElement:
- --elementRefCounter;
- if (elementRefCounter == 0)
- return;
- reader.readNext();
- break;
- default:
- reader.readNext();
- break;
+ } else if (reader.name() == QLatin1String("folding")) {
+ if (reader.attributes().hasAttribute(QLatin1String("indentationsensitive"))) {
+ indentationBasedFolding = Xml::attrToBool(reader.attributes().value(QLatin1String("indentationsensitive")));
+ }
+ } else if (reader.name() == QLatin1String("emptyLines")) {
+ loadFoldingIgnoreList(reader);
+ } else if (reader.name() == QLatin1String("comments")) {
+ loadComments(reader);
+ } else if (reader.name() == QLatin1String("spellchecking")) {
+ loadSpellchecking(reader);
+ } else {
+ reader.skipCurrentElement();
+ }
+ reader.readNext();
+ break;
+ case QXmlStreamReader::EndElement:
+ --elementRefCounter;
+ if (elementRefCounter == 0) {
+ return;
+ }
+ reader.readNext();
+ break;
+ default:
+ reader.readNext();
+ break;
}
}
}
@@ -679,35 +710,36 @@ void DefinitionData::loadComments(QXmlStreamReader &reader)
while (!reader.atEnd()) {
switch (reader.tokenType()) {
- case QXmlStreamReader::StartElement:
- ++elementRefCounter;
- if (reader.name() == QLatin1String("comment")) {
- const bool isSingleLine = reader.attributes().value(QStringLiteral("name")) == QStringLiteral("singleLine");
- if (isSingleLine) {
- singleLineCommentMarker = reader.attributes().value(QStringLiteral("start")).toString();
- const bool afterWhiteSpace = reader.attributes().value(QStringLiteral("position")).toString() == QStringLiteral("afterwhitespace");
- singleLineCommentPosition = afterWhiteSpace ? CommentPosition::AfterWhitespace : CommentPosition::StartOfLine;
- } else {
- multiLineCommentStartMarker = reader.attributes().value(QStringLiteral("start")).toString();
- multiLineCommentEndMarker = reader.attributes().value(QStringLiteral("end")).toString();
- }
+ case QXmlStreamReader::StartElement:
+ ++elementRefCounter;
+ if (reader.name() == QLatin1String("comment")) {
+ const bool isSingleLine = reader.attributes().value(QLatin1String("name")) == QLatin1String("singleLine");
+ if (isSingleLine) {
+ singleLineCommentMarker = reader.attributes().value(QLatin1String("start")).toString();
+ const bool afterWhiteSpace = reader.attributes().value(QLatin1String("position")) == QLatin1String("afterwhitespace");
+ singleLineCommentPosition = afterWhiteSpace ? CommentPosition::AfterWhitespace : CommentPosition::StartOfLine;
+ } else {
+ multiLineCommentStartMarker = reader.attributes().value(QLatin1String("start")).toString();
+ multiLineCommentEndMarker = reader.attributes().value(QLatin1String("end")).toString();
}
- reader.readNext();
- break;
- case QXmlStreamReader::EndElement:
- --elementRefCounter;
- if (elementRefCounter == 0)
- return;
- reader.readNext();
- break;
- default:
- reader.readNext();
- break;
+ }
+ reader.readNext();
+ break;
+ case QXmlStreamReader::EndElement:
+ --elementRefCounter;
+ if (elementRefCounter == 0) {
+ return;
+ }
+ reader.readNext();
+ break;
+ default:
+ reader.readNext();
+ break;
}
}
}
-void DefinitionData::loadFoldingIgnoreList(QXmlStreamReader& reader)
+void DefinitionData::loadFoldingIgnoreList(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("emptyLines"));
Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
@@ -718,22 +750,23 @@ void DefinitionData::loadFoldingIgnoreList(QXmlStreamReader& reader)
while (!reader.atEnd()) {
switch (reader.tokenType()) {
- case QXmlStreamReader::StartElement:
- ++elementRefCounter;
- if (reader.name() == QLatin1String("emptyLine")) {
- foldingIgnoreList << reader.attributes().value(QStringLiteral("regexpr")).toString();
- }
- reader.readNext();
- break;
- case QXmlStreamReader::EndElement:
- --elementRefCounter;
- if (elementRefCounter == 0)
- return;
- reader.readNext();
- break;
- default:
- reader.readNext();
- break;
+ case QXmlStreamReader::StartElement:
+ ++elementRefCounter;
+ if (reader.name() == QLatin1String("emptyLine")) {
+ foldingIgnoreList << reader.attributes().value(QLatin1String("regexpr")).toString();
+ }
+ reader.readNext();
+ break;
+ case QXmlStreamReader::EndElement:
+ --elementRefCounter;
+ if (elementRefCounter == 0) {
+ return;
+ }
+ reader.readNext();
+ break;
+ default:
+ reader.readNext();
+ break;
}
}
}
@@ -749,31 +782,32 @@ void DefinitionData::loadSpellchecking(QXmlStreamReader &reader)
while (!reader.atEnd()) {
switch (reader.tokenType()) {
- case QXmlStreamReader::StartElement:
- ++elementRefCounter;
- if (reader.name() == QLatin1String("encoding")) {
- const auto charRef = reader.attributes().value(QStringLiteral("char"));
- if (!charRef.isEmpty()) {
- const auto str = reader.attributes().value(QStringLiteral("string")).toString();
- characterEncodings.push_back({ charRef[0], str });
- }
+ case QXmlStreamReader::StartElement:
+ ++elementRefCounter;
+ if (reader.name() == QLatin1String("encoding")) {
+ const auto charRef = reader.attributes().value(QLatin1String("char"));
+ if (!charRef.isEmpty()) {
+ const auto str = reader.attributes().value(QLatin1String("string"));
+ characterEncodings.push_back({charRef[0], str.toString()});
}
- reader.readNext();
- break;
- case QXmlStreamReader::EndElement:
- --elementRefCounter;
- if (elementRefCounter == 0)
- return;
- reader.readNext();
- break;
- default:
- reader.readNext();
- break;
+ }
+ reader.readNext();
+ break;
+ case QXmlStreamReader::EndElement:
+ --elementRefCounter;
+ if (elementRefCounter == 0) {
+ return;
+ }
+ reader.readNext();
+ break;
+ default:
+ reader.readNext();
+ break;
}
}
}
-bool DefinitionData::checkKateVersion(const QStringRef& verStr)
+bool DefinitionData::checkKateVersion(QStringView verStr)
{
const auto idx = verStr.indexOf(QLatin1Char('.'));
if (idx <= 0) {
@@ -783,7 +817,7 @@ bool DefinitionData::checkKateVersion(const QStringRef& verStr)
const auto major = verStr.left(idx).toInt();
const auto minor = verStr.mid(idx + 1).toInt();
- if (major > SyntaxHighlighting_VERSION_MAJOR || (major == SyntaxHighlighting_VERSION_MAJOR && minor > SyntaxHighlighting_VERSION_MINOR)) {
+ if (major > KSYNTAXHIGHLIGHTING_VERSION_MAJOR || (major == KSYNTAXHIGHLIGHTING_VERSION_MAJOR && minor > KSYNTAXHIGHLIGHTING_VERSION_MINOR)) {
qCWarning(Log) << "Skipping" << fileName << "due to being too new, version:" << verStr;
return false;
}
@@ -797,20 +831,24 @@ quint16 DefinitionData::foldingRegionId(const QString &foldName)
return RepositoryPrivate::get(repo)->foldingRegionId(name, foldName);
}
-DefinitionRef::DefinitionRef()
+void DefinitionData::addImmediateIncludedDefinition(const Definition &def)
{
+ if (get(def) != this) {
+ DefinitionRef defRef(def);
+ if (!immediateIncludedDefinitions.contains(defRef)) {
+ immediateIncludedDefinitions.push_back(std::move(defRef));
+ }
+ }
}
-DefinitionRef::DefinitionRef(const Definition &def) :
- d(def.d)
-{
-}
+DefinitionRef::DefinitionRef() = default;
-DefinitionRef::~DefinitionRef()
+DefinitionRef::DefinitionRef(const Definition &def) noexcept
+ : d(def.d)
{
}
-DefinitionRef& DefinitionRef::operator=(const Definition &def)
+DefinitionRef &DefinitionRef::operator=(const Definition &def) noexcept
{
d = def.d;
return *this;
@@ -818,16 +856,17 @@ DefinitionRef& DefinitionRef::operator=(const Definition &def)
Definition DefinitionRef::definition() const
{
- if (!d.expired())
- return Definition(d.lock());
- return Definition();
+ return Definition(d.lock());
}
bool DefinitionRef::operator==(const DefinitionRef &other) const
{
- if (d.expired() != other.d.expired()) {
- return false;
- }
+ return !d.owner_before(other.d) && !other.d.owner_before(d);
+}
- return d.expired() || d.lock().get() == other.d.lock().get();
+bool DefinitionRef::operator==(const Definition &other) const
+{
+ return !d.owner_before(other.d) && !other.d.owner_before(d);
}
+
+#include "moc_definition.cpp"
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definition.h b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.h
index 6f0dba9a45..e69492bee4 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/definition.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.h
@@ -1,24 +1,8 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_DEFINITION_H
@@ -26,20 +10,14 @@
#include "ksyntaxhighlighting_export.h"
-#include <QTypeInfo>
+#include <QList>
#include <QPair>
-
+#include <QString>
#include <memory>
+#include <qobjectdefs.h>
-QT_BEGIN_NAMESPACE
-class QChar;
-class QString;
-class QStringList;
-template <typename T> class QVector;
-QT_END_NAMESPACE
-
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class Context;
class Format;
class KeywordList;
@@ -51,8 +29,7 @@ class DefinitionData;
* @since 5.50
* @see Definition::singleLineCommentPosition()
*/
-enum class CommentPosition
-{
+enum class CommentPosition {
//! The comment marker is inserted at the beginning of a line at column 0
StartOfLine = 0,
//! The comment marker is inserted after leading whitespaces right befire
@@ -94,7 +71,7 @@ enum class CommentPosition
* singleLineCommentMarker() and multiLineCommentMarker() provide comment
* markers that can be used for commenting/uncommenting code. Similarly,
* formats() returns a list of Format items defined by this Definition (which
- * equal the itemDatas of a highlighing definition file). includedDefinitions()
+ * equal the itemDatas of a highlighting definition file). includedDefinitions()
* returns a list of all included Definition%s referenced by this Definition via
* the rule IncludeRules, which is useful for displaying all Format items for
* color configuration in the user interface.
@@ -104,6 +81,13 @@ enum class CommentPosition
*/
class KSYNTAXHIGHLIGHTING_EXPORT Definition
{
+ Q_GADGET
+ Q_PROPERTY(QString name READ name)
+ Q_PROPERTY(QString translatedName READ translatedName)
+ Q_PROPERTY(QString section READ section)
+ Q_PROPERTY(QString translatedSection READ translatedSection)
+ Q_PROPERTY(QString author READ author)
+ Q_PROPERTY(QString license READ license)
public:
/**
* Default constructor, creating an empty (invalid) Definition instance.
@@ -114,6 +98,14 @@ public:
Definition();
/**
+ * Move constructor.
+ * This definition takes the Definition data from @p other.
+ * @note @p other may only be assigned to or destroyed afterwards.
+ * @since 5.86
+ */
+ Definition(Definition &&other) noexcept;
+
+ /**
* Copy constructor.
* Both this definition as well as @p other share the Definition data.
*/
@@ -125,10 +117,18 @@ public:
~Definition();
/**
- * Assignment operator.
+ * Move assignment operator.
+ * This definition takes the Definition data from @p other.
+ * @note @p other may only be assigned to or destroyed afterwards.
+ * @since 5.86
+ */
+ Definition &operator=(Definition &&other) noexcept;
+
+ /**
+ * Copy assignment operator.
* Both this definition as well as @p rhs share the Definition data.
*/
- Definition& operator=(const Definition &rhs);
+ Definition &operator=(const Definition &rhs);
/**
* Checks two definitions for equality.
@@ -181,13 +181,13 @@ public:
/**
* Mime types associated with this syntax definition.
*/
- QVector<QString> mimeTypes() const;
+ QList<QString> mimeTypes() const;
/**
* File extensions associated with this syntax definition.
* The returned list contains wildcards.
*/
- QVector<QString> extensions() const;
+ QList<QString> extensions() const;
/**
* Returns the definition version.
@@ -329,16 +329,34 @@ public:
/**
* Returns the list of keywords for the keyword list @p name.
* @since 5.49
- * @see keywordLists()
+ * @see keywordLists(), setKeywordList()
+ */
+ QStringList keywordList(const QString &name) const;
+
+ /**
+ * Set the contents of the keyword list @p name to @p content.
+ * Only existing keywordLists() can be changed. For non-existent keyword lists,
+ * false is returned.
+ *
+ * Whenever you change a keyword list, make sure to trigger a rehighlight of
+ * your documents. In case you are using QSyntaxHighlighter via SyntaxHighlighter,
+ * this can be done by calling SyntaxHighlighter::rehighlight().
+ *
+ * @note In general, changing keyword lists via setKeywordList() is discouraged,
+ * since if a keyword list name in the syntax highlighting definition
+ * file changes, the call setKeywordList() may suddenly fail.
+ *
+ * @see keywordList(), keywordLists()
+ * @since 5.62
*/
- QStringList keywordList(const QString& name) const;
+ bool setKeywordList(const QString &name, const QStringList &content);
/**
* Returns a list of all Format items used by this definition.
* The order of the Format items equals the order of the itemDatas in the xml file.
* @since 5.49
*/
- QVector<Format> formats() const;
+ QList<Format> formats() const;
/**
* Returns a list of Definitions that are referenced with the IncludeRules rule.
@@ -347,7 +365,7 @@ public:
*
* @since 5.49
*/
- QVector<Definition> includedDefinitions() const;
+ QList<Definition> includedDefinitions() const;
/**
* Returns the marker that starts a single line comment.
@@ -378,7 +396,7 @@ public:
* the string \"{A} represents the character Ä.
* @since 5.50
*/
- QVector<QPair<QChar, QString>> characterEncodings() const;
+ QList<QPair<QChar, QString>> characterEncodings() const;
/**
* @}
@@ -387,14 +405,14 @@ public:
private:
friend class DefinitionData;
friend class DefinitionRef;
- explicit Definition(const std::shared_ptr<DefinitionData> &dd);
+ KSYNTAXHIGHLIGHTING_NO_EXPORT explicit Definition(std::shared_ptr<DefinitionData> &&dd);
std::shared_ptr<DefinitionData> d;
};
}
QT_BEGIN_NAMESPACE
-Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Definition, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Definition, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
#endif
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definition_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/definition_p.h
index 9bbf59691c..ec00b31897 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/definition_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definition_p.h
@@ -1,43 +1,32 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_DEFINITION_P_H
#define KSYNTAXHIGHLIGHTING_DEFINITION_P_H
#include "definitionref_p.h"
-#include "definition.h"
+#include "highlightingdata_p.hpp"
+#include "state.h"
+#include "worddelimiters_p.h"
#include <QHash>
+#include <QList>
+#include <QSet>
#include <QString>
-#include <QVector>
+
+#include <vector>
QT_BEGIN_NAMESPACE
+class QCborMap;
class QXmlStreamReader;
-class QJsonObject;
QT_END_NAMESPACE
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class Repository;
class DefinitionData
@@ -49,11 +38,14 @@ public:
DefinitionData(const DefinitionData &) = delete;
DefinitionData &operator=(const DefinitionData &) = delete;
- static DefinitionData* get(const Definition &def);
+ static DefinitionData *get(const Definition &def)
+ {
+ return def.d.get();
+ }
bool isLoaded() const;
bool loadMetaData(const QString &definitionFileName);
- bool loadMetaData(const QString &fileName, const QJsonObject &obj);
+ bool loadMetaData(const QString &fileName, const QCborMap &obj);
void clear();
@@ -68,28 +60,36 @@ public:
void loadComments(QXmlStreamReader &reader);
void loadFoldingIgnoreList(QXmlStreamReader &reader);
void loadSpellchecking(QXmlStreamReader &reader);
- bool checkKateVersion(const QStringRef &verStr);
+ bool checkKateVersion(QStringView verStr);
+
+ void resolveContexts();
void resolveIncludeKeywords();
KeywordList *keywordList(const QString &name);
- bool isWordDelimiter(QChar c) const;
- Context* initialContext() const;
- Context* contextByName(const QString &name) const;
+ Context *initialContext();
+ Context *contextByName(QStringView name);
Format formatByName(const QString &name) const;
quint16 foldingRegionId(const QString &foldName);
+ void addImmediateIncludedDefinition(const Definition &def);
+
DefinitionRef q;
+ uint64_t id = 0;
Repository *repo = nullptr;
QHash<QString, KeywordList> keywordLists;
- QVector<Context*> contexts;
+ std::vector<Context> contexts;
QHash<QString, Format> formats;
- QString wordDelimiters;
- QString wordWrapDelimiters;
+ // data loaded from xml file and emptied after loading contexts
+ QList<HighlightingContextData> contextDatas;
+ // Definition referenced by IncludeRules and ContextSwitch
+ QList<DefinitionRef> immediateIncludedDefinitions;
+ WordDelimiters wordDelimiters;
+ WordDelimiters wordWrapDelimiters;
bool keywordIsLoaded = false;
bool hasFoldingRegions = false;
bool indentationBasedFolding = false;
@@ -98,21 +98,28 @@ public:
CommentPosition singleLineCommentPosition = CommentPosition::StartOfLine;
QString multiLineCommentStartMarker;
QString multiLineCommentEndMarker;
- QVector<QPair<QChar, QString>> characterEncodings;
+ QList<QPair<QChar, QString>> characterEncodings;
QString fileName;
- QString name = QStringLiteral(QT_TRANSLATE_NOOP("Syntax highlighting", "None"));
+ QString name = QStringLiteral(QT_TRANSLATE_NOOP("Language", "None"));
+ QByteArray nameUtf8;
+ mutable QString translatedName;
QString section;
+ QByteArray sectionUtf8;
+ mutable QString translatedSection;
QString style;
QString indenter;
QString author;
QString license;
- QVector<QString> mimetypes;
- QVector<QString> extensions;
+ QList<QString> mimetypes;
+ QList<QString> extensions;
Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive;
int version = 0;
int priority = 0;
bool hidden = false;
+
+ // cache that is used to unify states in AbstractHighlighter::highlightLine
+ mutable QSet<State> unify;
};
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp
index 4c3e5f5f1e..88ba5d7759 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp
@@ -1,33 +1,15 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#include "definitiondownloader.h"
#include "definition.h"
-#include "repository.h"
#include "ksyntaxhighlighting_logging.h"
#include "ksyntaxhighlighting_version.h"
+#include "repository.h"
-#include <QDebug>
#include <QDir>
#include <QFile>
#include <QNetworkAccessManager>
@@ -58,56 +40,62 @@ public:
void DefinitionDownloaderPrivate::definitionListDownloadFinished(QNetworkReply *reply)
{
- if (reply->error() != QNetworkReply::NoError) {
- qCWarning(Log) << reply->error();
- emit q->done(); // TODO return error
+ const auto networkError = reply->error();
+ if (networkError != QNetworkReply::NoError) {
+ qCWarning(Log) << networkError;
+ Q_EMIT q->done(); // TODO return error
return;
}
QXmlStreamReader parser(reply);
while (!parser.atEnd()) {
switch (parser.readNext()) {
- case QXmlStreamReader::StartElement:
- if (parser.name() == QLatin1String("Definition"))
- updateDefinition(parser);
- break;
- default:
- break;
+ case QXmlStreamReader::StartElement:
+ if (parser.name() == QLatin1String("Definition")) {
+ updateDefinition(parser);
+ }
+ break;
+ default:
+ break;
}
}
- if (pendingDownloads == 0)
- emit q->informationMessage(QObject::tr("All syntax definitions are up-to-date."));
+ if (pendingDownloads == 0) {
+ Q_EMIT q->informationMessage(QObject::tr("All syntax definitions are up-to-date."));
+ }
checkDone();
}
void DefinitionDownloaderPrivate::updateDefinition(QXmlStreamReader &parser)
{
const auto name = parser.attributes().value(QLatin1String("name"));
- if (name.isEmpty())
+ if (name.isEmpty()) {
return;
+ }
auto localDef = repo->definitionForName(name.toString());
if (!localDef.isValid()) {
- emit q->informationMessage(QObject::tr("Downloading new syntax definition for '%1'...").arg(name.toString()));
+ Q_EMIT q->informationMessage(QObject::tr("Downloading new syntax definition for '%1'...").arg(name));
downloadDefinition(QUrl(parser.attributes().value(QLatin1String("url")).toString()));
return;
}
const auto version = parser.attributes().value(QLatin1String("version"));
if (localDef.version() < version.toFloat()) {
- emit q->informationMessage(QObject::tr("Updating syntax definition for '%1' to version %2...").arg(name.toString(), version.toString()));
+ Q_EMIT q->informationMessage(QObject::tr("Updating syntax definition for '%1' to version %2...").arg(name, version));
downloadDefinition(QUrl(parser.attributes().value(QLatin1String("url")).toString()));
}
}
-void DefinitionDownloaderPrivate::downloadDefinition(const QUrl& downloadUrl)
+void DefinitionDownloaderPrivate::downloadDefinition(const QUrl &downloadUrl)
{
- if (!downloadUrl.isValid())
+ if (!downloadUrl.isValid()) {
return;
+ }
auto url = downloadUrl;
- if (url.scheme() == QLatin1String("http"))
+ if (url.scheme() == QLatin1String("http")) {
url.setScheme(QStringLiteral("https"));
+ }
QNetworkRequest req(url);
auto reply = nam->get(req);
@@ -121,8 +109,10 @@ void DefinitionDownloaderPrivate::downloadDefinition(const QUrl& downloadUrl)
void DefinitionDownloaderPrivate::downloadDefinitionFinished(QNetworkReply *reply)
{
--pendingDownloads;
- if (reply->error() != QNetworkReply::NoError) {
- qCWarning(Log) << "Failed to download definition file" << reply->url() << reply->error();
+
+ const auto networkError = reply->error();
+ if (networkError != QNetworkReply::NoError) {
+ qCWarning(Log) << "Failed to download definition file" << reply->url() << networkError;
checkDone();
return;
}
@@ -148,14 +138,14 @@ void DefinitionDownloaderPrivate::downloadDefinitionFinished(QNetworkReply *repl
void DefinitionDownloaderPrivate::checkDone()
{
if (pendingDownloads == 0) {
- if (needsReload)
+ if (needsReload) {
repo->reload();
+ }
- emit QTimer::singleShot(0, q, &DefinitionDownloader::done);
+ Q_EMIT QTimer::singleShot(0, q, &DefinitionDownloader::done);
}
}
-
DefinitionDownloader::DefinitionDownloader(Repository *repo, QObject *parent)
: QObject(parent)
, d(new DefinitionDownloaderPrivate())
@@ -179,15 +169,14 @@ DefinitionDownloader::~DefinitionDownloader()
void DefinitionDownloader::start()
{
- const QString url = QLatin1String("https://www.kate-editor.org/syntax/update-")
- + QString::number(SyntaxHighlighting_VERSION_MAJOR)
- + QLatin1Char('.')
- + QString::number(SyntaxHighlighting_VERSION_MINOR)
- + QLatin1String(".xml");
+ const QString url = QLatin1String("https://www.kate-editor.org/syntax/update-") + QString::number(KSYNTAXHIGHLIGHTING_VERSION_MAJOR) + QLatin1Char('.')
+ + QString::number(KSYNTAXHIGHLIGHTING_VERSION_MINOR) + QLatin1String(".xml");
auto req = QNetworkRequest(QUrl(url));
- req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
+ req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
auto reply = d->nam->get(req);
- QObject::connect(reply, &QNetworkReply::finished, this, [=]() {
+ QObject::connect(reply, &QNetworkReply::finished, this, [this, reply]() {
d->definitionListDownloadFinished(reply);
});
}
+
+#include "moc_definitiondownloader.cpp"
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.h b/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.h
index 06e28f6a65..2eaf0561ef 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.h
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_DEFINITIONDOWNLOADER_H
@@ -29,8 +12,8 @@
#include <QObject>
#include <memory>
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class DefinitionDownloaderPrivate;
class Repository;
@@ -76,7 +59,7 @@ public:
/**
* Destructor.
*/
- ~DefinitionDownloader();
+ ~DefinitionDownloader() override;
/**
* Starts the update procedure.
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h
index 08604a4821..a7ef08f614 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h
@@ -1,36 +1,20 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_DEFINITIONREF_P_H
#define KSYNTAXHIGHLIGHTING_DEFINITIONREF_P_H
-#include <memory>
+#include "definition.h"
-namespace KSyntaxHighlighting {
+#include <memory>
+namespace KSyntaxHighlighting
+{
class Definition;
class DefinitionData;
-class DefinitionPrivate;
/** Weak reference for Definition instances.
*
@@ -38,15 +22,16 @@ class DefinitionPrivate;
* in objects hold directly or indirectly by Definition
* to avoid reference count loops and thus memory leaks.
*
+ * This class follows the rule of zero. It is implicitly movable and copyable.
+ *
* @internal
*/
class DefinitionRef
{
public:
DefinitionRef();
- explicit DefinitionRef(const Definition &def);
- ~DefinitionRef();
- DefinitionRef& operator=(const Definition &def);
+ explicit DefinitionRef(const Definition &def) noexcept;
+ DefinitionRef &operator=(const Definition &def) noexcept;
Definition definition() const;
@@ -63,6 +48,19 @@ public:
return !(*this == other);
}
+ /**
+ * Checks two definition for equality.
+ */
+ bool operator==(const Definition &other) const;
+
+ /**
+ * Checks two definition for inequality.
+ */
+ bool operator!=(const Definition &other) const
+ {
+ return !(*this == other);
+ }
+
private:
friend class DefinitionData;
std::weak_ptr<DefinitionData> d;
@@ -71,4 +69,3 @@ private:
}
#endif
-
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/dynamicregexpcache_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/dynamicregexpcache_p.h
new file mode 100644
index 0000000000..dcef97a841
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/dynamicregexpcache_p.h
@@ -0,0 +1,39 @@
+/*
+ SPDX-FileCopyrightText: 2023 Jonathan Poelen <jonathan.poelen+kde@gmail.com>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#ifndef KSYNTAXHIGHLIGHTING_DYNAMICREGEXPCACHE_P_H
+#define KSYNTAXHIGHLIGHTING_DYNAMICREGEXPCACHE_P_H
+
+#include <QCache>
+#include <QRegularExpression>
+#include <QString>
+
+#include <utility>
+
+namespace KSyntaxHighlighting
+{
+
+class DynamicRegexpCache
+{
+public:
+ const QRegularExpression &compileRegexp(QString &&pattern, QRegularExpression::PatternOptions patternOptions)
+ {
+ const auto key = std::pair{std::move(pattern), patternOptions};
+ if (const auto regexp = m_cache.object(key)) {
+ return *regexp;
+ }
+ auto regexp = new QRegularExpression(key.first, patternOptions);
+ m_cache.insert(key, regexp);
+ return *regexp;
+ }
+
+private:
+ QCache<std::pair<QString, QRegularExpression::PatternOptions>, QRegularExpression> m_cache;
+};
+
+}
+
+#endif
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp
index e8f89bd788..9ed625b12e 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp
@@ -1,60 +1,46 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#include "foldingregion.h"
using namespace KSyntaxHighlighting;
-static_assert(sizeof(FoldingRegion) == 2, "FoldingRegion is size-sensitive to frequent use in KTextEditor!");
+static_assert(sizeof(FoldingRegion) == sizeof(int), "FoldingRegion is size-sensitive to frequent use in KTextEditor!");
-FoldingRegion::FoldingRegion() :
- m_type(None),
- m_id(0)
-{
-}
+FoldingRegion::FoldingRegion() = default;
-FoldingRegion::FoldingRegion(Type type, quint16 id) :
- m_type(type),
- m_id(id)
+FoldingRegion::FoldingRegion(Type type, int id)
+ : m_idWithType((type == End) ? -id : id)
{
}
bool FoldingRegion::operator==(const FoldingRegion &other) const
{
- return m_id == other.m_id && m_type == other.m_type;
+ return m_idWithType == other.m_idWithType;
}
bool FoldingRegion::isValid() const
{
- return type() != None;
+ return m_idWithType != 0;
}
-quint16 FoldingRegion::id() const
+int FoldingRegion::id() const
{
- return m_id;
+ return (m_idWithType < 0) ? -m_idWithType : m_idWithType;
}
FoldingRegion::Type FoldingRegion::type() const
{
- return static_cast<FoldingRegion::Type>(m_type);
+ if (isValid()) {
+ return (m_idWithType < 0) ? End : Begin;
+ }
+ return None;
+}
+
+FoldingRegion FoldingRegion::sibling() const
+{
+ return isValid() ? FoldingRegion(type() ? End : Begin, id()) : FoldingRegion();
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h
index 074b9478be..e2a9e1fc68 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_FOLDINGREGION_H
@@ -28,8 +11,8 @@
#include <QTypeInfo>
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
/** Represents a begin or end of a folding region.
* @since 5.28 */
class KSYNTAXHIGHLIGHTING_EXPORT FoldingRegion
@@ -39,7 +22,7 @@ public:
* Defines whether a FoldingRegion starts or ends a folding region.
*/
enum Type {
- //! Used internally as indicator for invalid FoldingRegion%s.
+ //! Used internally as indicator for an invalid FoldingRegion.
None,
//! Indicates the start of a FoldingRegion.
Begin,
@@ -81,7 +64,7 @@ public:
* brace, you need to do kind of a reference counting to find the correct
* closing brace.
*/
- quint16 id() const;
+ int id() const;
/**
* Returns whether this is the begin or end of a region.
@@ -91,12 +74,21 @@ public:
*/
Type type() const;
+ /**
+ * Returns the matching start or end region.
+ *
+ * @note Will return invalid region for an invalid region.
+ *
+ * @since 6.0
+ */
+ FoldingRegion sibling() const;
+
private:
friend class Rule;
- FoldingRegion(Type type, quint16 id);
+ KSYNTAXHIGHLIGHTING_NO_EXPORT FoldingRegion(Type type, int id);
- quint16 m_type : 2;
- quint16 m_id: 14;
+ // 0 is invalid, positive begin, negative end
+ int m_idWithType = 0;
};
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp
index d1808cafef..2259cd3411 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp
@@ -1,58 +1,41 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
#include "format.h"
-#include "format_p.h"
#include "definition.h"
#include "definitionref_p.h"
+#include "format_p.h"
#include "textstyledata_p.h"
#include "themedata_p.h"
#include "xml_p.h"
#include <QColor>
-#include <QDebug>
#include <QMetaEnum>
#include <QXmlStreamReader>
using namespace KSyntaxHighlighting;
-static Theme::TextStyle stringToDefaultFormat(const QStringRef &str)
+static Theme::TextStyle stringToDefaultFormat(QStringView str)
{
- if (!str.startsWith(QLatin1String("ds")))
+ if (!str.startsWith(QLatin1String("ds"))) {
return Theme::Normal;
+ }
- static const auto idx = Theme::staticMetaObject.indexOfEnumerator("TextStyle");
- Q_ASSERT(idx >= 0);
- const auto metaEnum = Theme::staticMetaObject.enumerator(idx);
+ const auto metaEnum = QMetaEnum::fromType<Theme::TextStyle>();
bool ok = false;
const auto value = metaEnum.keyToValue(str.mid(2).toLatin1().constData(), &ok);
- if (!ok || value < 0)
+ if (!ok || value < 0) {
return Theme::Normal;
+ }
return static_cast<Theme::TextStyle>(value);
}
-FormatPrivate* FormatPrivate::detachAndGet(Format &format)
+FormatPrivate *FormatPrivate::detachAndGet(Format &format)
{
format.d.detach();
return format.d.data();
@@ -60,10 +43,23 @@ FormatPrivate* FormatPrivate::detachAndGet(Format &format)
TextStyleData FormatPrivate::styleOverride(const Theme &theme) const
{
- const auto themeData = ThemeData::get(theme);
- if (themeData)
- return themeData->textStyleOverride(definition.definition().name(), name);
- return TextStyleData();
+ return ThemeData::get(theme)->textStyleOverride(definitionName, name);
+}
+
+QColor FormatPrivate::color(const Theme &theme, StyleColor styleColor, ThemeColor themeColor) const
+{
+ const auto overrideStyle = styleOverride(theme);
+ if (overrideStyle.*styleColor) {
+ return QColor::fromRgb(overrideStyle.*styleColor);
+ }
+ // use QColor::fromRgba for QRgb => QColor conversion to avoid unset colors == black!
+ return QColor::fromRgba(style.*styleColor ? style.*styleColor : (theme.*themeColor)(defaultStyle));
+}
+
+bool FormatPrivate::hasColor(const Theme &theme, StyleColor styleColor, ThemeColor themeColor) const
+{
+ // use QColor::fromRgba for background QRgb => QColor conversion to avoid unset colors == black!
+ return color(theme, styleColor, themeColor) != QColor::fromRgba((theme.*themeColor)(Theme::Normal)) && (style.*styleColor || (theme.*themeColor)(defaultStyle) || styleOverride(theme).*styleColor);
}
static QExplicitlySharedDataPointer<FormatPrivate> &sharedDefaultPrivate()
@@ -72,12 +68,13 @@ static QExplicitlySharedDataPointer<FormatPrivate> &sharedDefaultPrivate()
return def;
}
-Format::Format() : d(sharedDefaultPrivate())
+Format::Format()
+ : d(sharedDefaultPrivate())
{
}
-Format::Format(const Format &other) :
- d(other.d)
+Format::Format(const Format &other)
+ : d(other.d)
{
}
@@ -85,7 +82,7 @@ Format::~Format()
{
}
-Format& Format::operator=(const Format& other)
+Format &Format::operator=(const Format &other)
{
d = other.d;
return *this;
@@ -101,7 +98,7 @@ QString Format::name() const
return d->name;
}
-quint16 Format::id() const
+int Format::id() const
{
return d->id;
}
@@ -113,92 +110,76 @@ Theme::TextStyle Format::textStyle() const
bool Format::isDefaultTextStyle(const Theme &theme) const
{
- return (!hasTextColor(theme))
- && (!hasBackgroundColor(theme))
- && (selectedTextColor(theme) == theme.selectedTextColor(Theme::Normal))
- && (selectedBackgroundColor(theme) == theme.selectedBackgroundColor(Theme::Normal))
- && (isBold(theme) == theme.isBold(Theme::Normal))
- && (isItalic(theme) == theme.isItalic(Theme::Normal))
- && (isUnderline(theme) == theme.isUnderline(Theme::Normal))
+ // use QColor::fromRgba for background QRgb => QColor conversion to avoid unset colors == black!
+ return (!hasTextColor(theme)) && (!hasBackgroundColor(theme)) && (selectedTextColor(theme).rgba() == theme.selectedTextColor(Theme::Normal))
+ && (selectedBackgroundColor(theme).rgba() == (theme.selectedBackgroundColor(Theme::Normal))) && (isBold(theme) == theme.isBold(Theme::Normal))
+ && (isItalic(theme) == theme.isItalic(Theme::Normal)) && (isUnderline(theme) == theme.isUnderline(Theme::Normal))
&& (isStrikeThrough(theme) == theme.isStrikeThrough(Theme::Normal));
}
bool Format::hasTextColor(const Theme &theme) const
{
- const auto overrideStyle = d->styleOverride(theme);
- return textColor(theme) != theme.textColor(Theme::Normal)
- && (d->style.textColor || theme.textColor(d->defaultStyle) || overrideStyle.textColor);
+ return d->hasColor(theme, &TextStyleData::textColor, &Theme::textColor);
}
QColor Format::textColor(const Theme &theme) const
{
- const auto overrideStyle = d->styleOverride(theme);
- if (overrideStyle.textColor)
- return overrideStyle.textColor;
- return d->style.textColor ? d->style.textColor : theme.textColor(d->defaultStyle);
+ return d->color(theme, &TextStyleData::textColor, &Theme::textColor);
}
QColor Format::selectedTextColor(const Theme &theme) const
{
- const auto overrideStyle = d->styleOverride(theme);
- if (overrideStyle.selectedTextColor)
- return overrideStyle.selectedTextColor;
- return d->style.selectedTextColor ? d->style.selectedTextColor : theme.selectedTextColor(d->defaultStyle);
+ return d->color(theme, &TextStyleData::selectedTextColor, &Theme::selectedTextColor);
}
bool Format::hasBackgroundColor(const Theme &theme) const
{
- const auto overrideStyle = d->styleOverride(theme);
- return backgroundColor(theme) != theme.backgroundColor(Theme::Normal)
- && (d->style.backgroundColor || theme.backgroundColor(d->defaultStyle) || overrideStyle.backgroundColor);
+ return d->hasColor(theme, &TextStyleData::backgroundColor, &Theme::backgroundColor);
}
QColor Format::backgroundColor(const Theme &theme) const
{
- const auto overrideStyle = d->styleOverride(theme);
- if (overrideStyle.backgroundColor)
- return overrideStyle.backgroundColor;
- return d->style.backgroundColor ? d->style.backgroundColor : theme.backgroundColor(d->defaultStyle);
+ return d->color(theme, &TextStyleData::backgroundColor, &Theme::backgroundColor);
}
QColor Format::selectedBackgroundColor(const Theme &theme) const
{
- const auto overrideStyle = d->styleOverride(theme);
- if (overrideStyle.selectedBackgroundColor)
- return overrideStyle.selectedBackgroundColor;
- return d->style.selectedBackgroundColor ? d->style.selectedBackgroundColor
- : theme.selectedBackgroundColor(d->defaultStyle);
+ return d->color(theme, &TextStyleData::selectedBackgroundColor, &Theme::selectedBackgroundColor);
}
bool Format::isBold(const Theme &theme) const
{
const auto overrideStyle = d->styleOverride(theme);
- if (overrideStyle.hasBold)
+ if (overrideStyle.hasBold) {
return overrideStyle.bold;
+ }
return d->style.hasBold ? d->style.bold : theme.isBold(d->defaultStyle);
}
bool Format::isItalic(const Theme &theme) const
{
const auto overrideStyle = d->styleOverride(theme);
- if (overrideStyle.hasItalic)
+ if (overrideStyle.hasItalic) {
return overrideStyle.italic;
+ }
return d->style.hasItalic ? d->style.italic : theme.isItalic(d->defaultStyle);
}
bool Format::isUnderline(const Theme &theme) const
{
const auto overrideStyle = d->styleOverride(theme);
- if (overrideStyle.hasUnderline)
+ if (overrideStyle.hasUnderline) {
return overrideStyle.underline;
+ }
return d->style.hasUnderline ? d->style.underline : theme.isUnderline(d->defaultStyle);
}
bool Format::isStrikeThrough(const Theme &theme) const
{
const auto overrideStyle = d->styleOverride(theme);
- if (overrideStyle.hasStrikeThrough)
+ if (overrideStyle.hasStrikeThrough) {
return overrideStyle.strikeThrough;
+ }
return d->style.hasStrikeThrough ? d->style.strikeThrough : theme.isStrikeThrough(d->defaultStyle);
}
@@ -207,57 +188,96 @@ bool Format::spellCheck() const
return d->spellCheck;
}
+bool Format::hasBoldOverride() const
+{
+ return d->style.hasBold;
+}
+
+bool Format::hasItalicOverride() const
+{
+ return d->style.hasItalic;
+}
+
+bool Format::hasUnderlineOverride() const
+{
+ return d->style.hasUnderline;
+}
+
+bool Format::hasStrikeThroughOverride() const
+{
+ return d->style.hasStrikeThrough;
+}
+
+bool Format::hasTextColorOverride() const
+{
+ return d->style.textColor;
+}
+
+bool Format::hasBackgroundColorOverride() const
+{
+ return d->style.backgroundColor;
+}
+
+bool Format::hasSelectedTextColorOverride() const
+{
+ return d->style.selectedTextColor;
+}
+
+bool Format::hasSelectedBackgroundColorOverride() const
+{
+ return d->style.selectedBackgroundColor;
+}
-void FormatPrivate::load(QXmlStreamReader& reader)
+void FormatPrivate::load(QXmlStreamReader &reader)
{
- name = reader.attributes().value(QStringLiteral("name")).toString();
- defaultStyle = stringToDefaultFormat(reader.attributes().value(QStringLiteral("defStyleNum")));
+ name = reader.attributes().value(QLatin1String("name")).toString();
+ defaultStyle = stringToDefaultFormat(reader.attributes().value(QLatin1String("defStyleNum")));
- QStringRef attribute = reader.attributes().value(QStringLiteral("color"));
+ QStringView attribute = reader.attributes().value(QLatin1String("color"));
if (!attribute.isEmpty()) {
- style.textColor = QColor(attribute.toString()).rgba();
+ style.textColor = QColor(attribute).rgba();
}
- attribute = reader.attributes().value(QStringLiteral("selColor"));
+ attribute = reader.attributes().value(QLatin1String("selColor"));
if (!attribute.isEmpty()) {
- style.selectedTextColor = QColor(attribute.toString()).rgba();
+ style.selectedTextColor = QColor(attribute).rgba();
}
- attribute = reader.attributes().value(QStringLiteral("backgroundColor"));
+ attribute = reader.attributes().value(QLatin1String("backgroundColor"));
if (!attribute.isEmpty()) {
- style.backgroundColor = QColor(attribute.toString()).rgba();
+ style.backgroundColor = QColor(attribute).rgba();
}
- attribute = reader.attributes().value(QStringLiteral("selBackgroundColor"));
+ attribute = reader.attributes().value(QLatin1String("selBackgroundColor"));
if (!attribute.isEmpty()) {
- style.selectedBackgroundColor = QColor(attribute.toString()).rgba();
+ style.selectedBackgroundColor = QColor(attribute).rgba();
}
- attribute = reader.attributes().value(QStringLiteral("italic"));
+ attribute = reader.attributes().value(QLatin1String("italic"));
if (!attribute.isEmpty()) {
style.hasItalic = true;
style.italic = Xml::attrToBool(attribute);
}
- attribute = reader.attributes().value(QStringLiteral("bold"));
+ attribute = reader.attributes().value(QLatin1String("bold"));
if (!attribute.isEmpty()) {
style.hasBold = true;
style.bold = Xml::attrToBool(attribute);
}
- attribute = reader.attributes().value(QStringLiteral("underline"));
+ attribute = reader.attributes().value(QLatin1String("underline"));
if (!attribute.isEmpty()) {
style.hasUnderline = true;
style.underline = Xml::attrToBool(attribute);
}
- attribute = reader.attributes().value(QStringLiteral("strikeOut"));
+ attribute = reader.attributes().value(QLatin1String("strikeOut"));
if (!attribute.isEmpty()) {
style.hasStrikeThrough = true;
style.strikeThrough = Xml::attrToBool(attribute);
}
- attribute = reader.attributes().value(QStringLiteral("spellChecking"));
+ attribute = reader.attributes().value(QLatin1String("spellChecking"));
if (!attribute.isEmpty()) {
spellCheck = Xml::attrToBool(attribute);
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/format.h b/src/libs/3rdparty/syntax-highlighting/src/lib/format.h
index 24c58e73f6..397a1bab01 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/format.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/format.h
@@ -1,24 +1,8 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_FORMAT_H
@@ -28,17 +12,9 @@
#include "theme.h"
#include <QExplicitlySharedDataPointer>
-#include <QTypeInfo>
-
-QT_BEGIN_NAMESPACE
-class QColor;
-class QString;
-class QXmlStreamReader;
-QT_END_NAMESPACE
-
-namespace KSyntaxHighlighting {
-class DefinitionRef;
+namespace KSyntaxHighlighting
+{
class FormatPrivate;
/** Describes the format to be used for a specific text fragment.
@@ -56,7 +32,7 @@ public:
Format(const Format &other);
~Format();
- Format& operator=(const Format &other);
+ Format &operator=(const Format &other);
/** Returns @c true if this is a valid format, ie. one that
* was read from a syntax definition file.
@@ -72,7 +48,7 @@ public:
* the repository is reloaded (which also invalidatess the corresponding
* Definition anyway).
*/
- quint16 id() const;
+ int id() const;
/** Returns the underlying TextStyle of this Format.
* Every Theme::TextStyle is visually defined by a Theme. A Format uses one
@@ -139,6 +115,70 @@ public:
*/
bool spellCheck() const;
+ /** Returns @c true if the syntax definition file sets a value for the bold text
+ * attribute and, therefore, overrides the theme and the default formatting
+ * style. If the return is @p true, this value is obtained by isBold().
+ * @see isBold()
+ * @since 5.62
+ */
+ bool hasBoldOverride() const;
+
+ /** Returns @c true if the syntax definition file sets a value for the italic text
+ * attribute and, therefore, overrides the theme and the default formatting style.
+ * If the return is @p true, this value is obtained by isItalic().
+ * @see isItalic()
+ * @since 5.62
+ */
+ bool hasItalicOverride() const;
+
+ /** Returns @c true if the syntax definition file sets a value for the underlined
+ * text attribute and, therefore, overrides the theme and the default formatting
+ * style. If the return is @p true, this value is obtained by isUnderline().
+ * @see isUnderline()
+ * @since 5.62
+ */
+ bool hasUnderlineOverride() const;
+
+ /** Returns @c true if the syntax definition file specifies a value for the
+ * struck through text attribute. If the return is @p true, this value
+ * is obtained by isStrikeThrough().
+ * @see isStrikeThrough()
+ * @since 5.62
+ */
+ bool hasStrikeThroughOverride() const;
+
+ /** Returns @c true if the syntax definition file sets a value for the foreground
+ * text color attribute and, therefore, overrides the theme and the default formatting
+ * style. If the return is @p true, this value is obtained by textColor().
+ * @see textColor(), hasTextColor()
+ * @since 5.62
+ */
+ bool hasTextColorOverride() const;
+
+ /** Returns @c true if the syntax definition file sets a value for the background
+ * color attribute and, therefore, overrides the theme and the default formatting
+ * style. If the return is @p true, this value is obtained by backgroundColor().
+ * @see backgroundColor(), hasBackgroundColor()
+ * @since 5.62
+ */
+ bool hasBackgroundColorOverride() const;
+
+ /** Returns @c true if the syntax definition file specifies a value for the
+ * selected text color attribute. If the return is @p true, this value is
+ * obtained by selectedTextColor().
+ * @see selectedTextColor()
+ * @since 5.62
+ */
+ bool hasSelectedTextColorOverride() const;
+
+ /** Returns @c true if the syntax definition file specifies a value for the
+ * selected background color attribute. If the return is @p true, this
+ * value is obtained by selectedBackgroundColor().
+ * @see selectedBackgroundColor()
+ * @since 5.62
+ */
+ bool hasSelectedBackgroundColorOverride() const;
+
private:
friend class FormatPrivate;
QExplicitlySharedDataPointer<FormatPrivate> d;
@@ -146,7 +186,7 @@ private:
}
QT_BEGIN_NAMESPACE
-Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Format, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Format, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
#endif // KSYNTAXHIGHLIGHTING_FORMAT_H
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/format_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/format_p.h
index e79b26b6a7..ea74531445 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/format_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/format_p.h
@@ -1,52 +1,48 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_FORMAT_P_H
#define KSYNTAXHIGHLIGHTING_FORMAT_P_H
-#include "definitionref_p.h"
#include "textstyledata_p.h"
#include "theme.h"
#include <QSharedData>
#include <QString>
-namespace KSyntaxHighlighting {
+QT_BEGIN_NAMESPACE
+class QXmlStreamReader;
+QT_END_NAMESPACE
+namespace KSyntaxHighlighting
+{
class FormatPrivate : public QSharedData
{
public:
FormatPrivate() = default;
- static FormatPrivate* detachAndGet(Format &format);
+ static FormatPrivate *detachAndGet(Format &format);
+
+ static std::intptr_t ptrId(const Format &format)
+ {
+ return std::intptr_t(format.d.data());
+ }
TextStyleData styleOverride(const Theme &theme) const;
void load(QXmlStreamReader &reader);
- DefinitionRef definition;
+ using StyleColor = QRgb(TextStyleData::*);
+ using ThemeColor = QRgb (Theme::*)(Theme::TextStyle) const;
+ bool hasColor(const Theme &theme, StyleColor styleColor, ThemeColor themeColor) const;
+ QColor color(const Theme &theme, StyleColor styleColor, ThemeColor themeColor) const;
+
+ QString definitionName;
QString name;
TextStyleData style;
Theme::TextStyle defaultStyle = Theme::Normal;
- quint16 id = 0;
+ int id = 0;
bool spellCheck = true;
};
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata.cpp
new file mode 100644
index 0000000000..d95ad43b7f
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata.cpp
@@ -0,0 +1,406 @@
+/*
+ SPDX-FileCopyrightText: 2021 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#include "highlightingdata_p.hpp"
+#include "ksyntaxhighlighting_logging.h"
+#include "xml_p.h"
+
+#include <QXmlStreamReader>
+#include <QStringView>
+
+using namespace KSyntaxHighlighting;
+
+template<class Data, class... Args>
+static void initRuleData(Data &data, Args &&...args)
+{
+ new (&data) Data{std::move(args)...};
+}
+
+static Qt::CaseSensitivity attrToCaseSensitivity(QStringView str)
+{
+ return Xml::attrToBool(str) ? Qt::CaseInsensitive : Qt::CaseSensitive;
+}
+
+static HighlightingContextData::Rule::WordDelimiters loadAdditionalWordDelimiters(QXmlStreamReader &reader)
+{
+ return HighlightingContextData::Rule::WordDelimiters{
+ reader.attributes().value(QLatin1String("additionalDeliminator")).toString(),
+ reader.attributes().value(QLatin1String("weakDeliminator")).toString(),
+ };
+}
+
+static bool checkIsNotEmpty(QStringView str, const char *attrName, const QString &defName, QXmlStreamReader &reader)
+{
+ if (!str.isEmpty()) {
+ return true;
+ }
+
+ qCWarning(Log) << defName << "at line" << reader.lineNumber() << ": " << attrName << "attribute is empty";
+ return false;
+}
+
+static bool checkIsChar(QStringView str, const char *attrName, const QString &defName, QXmlStreamReader &reader)
+{
+ if (str.size() == 1) {
+ return true;
+ }
+
+ qCWarning(Log) << defName << "at line" << reader.lineNumber() << ": " << attrName << "attribute must contain exactly 1 character";
+ return false;
+}
+
+static bool loadRule(const QString &defName, HighlightingContextData::Rule &rule, QXmlStreamReader &reader)
+{
+ using Rule = HighlightingContextData::Rule;
+
+ QStringView name = reader.name();
+ const auto attrs = reader.attributes();
+ bool isIncludeRules = false;
+
+ if (name == QLatin1String("DetectChar")) {
+ const auto s = attrs.value(QLatin1String("char"));
+ if (!checkIsChar(s, "char", defName, reader)) {
+ return false;
+ }
+ const QChar c = s.at(0);
+ const bool dynamic = Xml::attrToBool(attrs.value(QLatin1String("dynamic")));
+
+ initRuleData(rule.data.detectChar, c, dynamic);
+ rule.type = Rule::Type::DetectChar;
+ } else if (name == QLatin1String("RegExpr")) {
+ const auto pattern = attrs.value(QLatin1String("String"));
+ if (!checkIsNotEmpty(pattern, "String", defName, reader)) {
+ return false;
+ }
+
+ const auto isCaseInsensitive = attrToCaseSensitivity(attrs.value(QLatin1String("insensitive")));
+ const auto isMinimal = Xml::attrToBool(attrs.value(QLatin1String("minimal")));
+ const auto dynamic = Xml::attrToBool(attrs.value(QLatin1String("dynamic")));
+
+ initRuleData(rule.data.regExpr, pattern.toString(), isCaseInsensitive, isMinimal, dynamic);
+ rule.type = Rule::Type::RegExpr;
+ } else if (name == QLatin1String("IncludeRules")) {
+ const auto context = attrs.value(QLatin1String("context"));
+ if (!checkIsNotEmpty(context, "context", defName, reader)) {
+ return false;
+ }
+ const bool includeAttribute = Xml::attrToBool(attrs.value(QLatin1String("includeAttrib")));
+
+ initRuleData(rule.data.includeRules, context.toString(), includeAttribute);
+ rule.type = Rule::Type::IncludeRules;
+ isIncludeRules = true;
+ } else if (name == QLatin1String("Detect2Chars")) {
+ const auto s1 = attrs.value(QLatin1String("char"));
+ const auto s2 = attrs.value(QLatin1String("char1"));
+ if (!checkIsChar(s1, "char", defName, reader)) {
+ return false;
+ }
+ if (!checkIsChar(s2, "char1", defName, reader)) {
+ return false;
+ }
+
+ initRuleData(rule.data.detect2Chars, s1.at(0), s2.at(0));
+ rule.type = Rule::Type::Detect2Chars;
+ } else if (name == QLatin1String("keyword")) {
+ const auto s = attrs.value(QLatin1String("String"));
+ if (!checkIsNotEmpty(s, "String", defName, reader)) {
+ return false;
+ }
+ Qt::CaseSensitivity caseSensitivityOverride = Qt::CaseInsensitive;
+ bool hasCaseSensitivityOverride = false;
+
+ /**
+ * we might overwrite the case sensitivity
+ * then we need to init the list for lookup of that sensitivity setting
+ */
+ if (attrs.hasAttribute(QLatin1String("insensitive"))) {
+ hasCaseSensitivityOverride = true;
+ caseSensitivityOverride = attrToCaseSensitivity(attrs.value(QLatin1String("insensitive")));
+ }
+
+ initRuleData(rule.data.keyword, s.toString(), loadAdditionalWordDelimiters(reader), caseSensitivityOverride, hasCaseSensitivityOverride);
+ rule.type = Rule::Type::Keyword;
+ } else if (name == QLatin1String("DetectSpaces")) {
+ rule.type = Rule::Type::DetectSpaces;
+ } else if (name == QLatin1String("StringDetect")) {
+ const auto string = attrs.value(QLatin1String("String"));
+ if (!checkIsNotEmpty(string, "String", defName, reader)) {
+ return false;
+ }
+ const auto caseSensitivity = attrToCaseSensitivity(attrs.value(QLatin1String("insensitive")));
+ const auto dynamic = Xml::attrToBool(attrs.value(QLatin1String("dynamic")));
+ const bool isSensitive = (caseSensitivity == Qt::CaseSensitive);
+
+ // String can be replaced with DetectChar or AnyChar
+ if (!dynamic && string.size() == 1) {
+ QChar c = string.at(0);
+ if (isSensitive || c.toLower() == c.toUpper()) {
+ initRuleData(rule.data.detectChar, c, dynamic);
+ rule.type = Rule::Type::DetectChar;
+ } else {
+ initRuleData(rule.data.anyChar, c.toLower() + c.toUpper());
+ rule.type = Rule::Type::AnyChar;
+ }
+ }
+ // String can be replaced with Detect2Chars
+ else if (isSensitive && !dynamic && string.size() == 2) {
+ initRuleData(rule.data.detect2Chars, string.at(0), string.at(1));
+ rule.type = Rule::Type::Detect2Chars;
+ } else {
+ initRuleData(rule.data.stringDetect, string.toString(), caseSensitivity, dynamic);
+ rule.type = Rule::Type::StringDetect;
+ }
+ } else if (name == QLatin1String("WordDetect")) {
+ const auto word = attrs.value(QLatin1String("String"));
+ if (!checkIsNotEmpty(word, "String", defName, reader)) {
+ return false;
+ }
+ const auto caseSensitivity = attrToCaseSensitivity(attrs.value(QLatin1String("insensitive")));
+
+ initRuleData(rule.data.wordDetect, word.toString(), loadAdditionalWordDelimiters(reader), caseSensitivity);
+ rule.type = Rule::Type::WordDetect;
+ } else if (name == QLatin1String("AnyChar")) {
+ const auto chars = attrs.value(QLatin1String("String"));
+ if (!checkIsNotEmpty(chars, "String", defName, reader)) {
+ return false;
+ }
+
+ // AnyChar can be replaced with DetectChar
+ if (chars.size() == 1) {
+ initRuleData(rule.data.detectChar, chars.at(0), false);
+ rule.type = Rule::Type::DetectChar;
+ } else {
+ initRuleData(rule.data.anyChar, chars.toString());
+ rule.type = Rule::Type::AnyChar;
+ }
+ } else if (name == QLatin1String("DetectIdentifier")) {
+ rule.type = Rule::Type::DetectIdentifier;
+ } else if (name == QLatin1String("LineContinue")) {
+ const auto s = attrs.value(QLatin1String("char"));
+ const QChar c = s.isEmpty() ? QLatin1Char('\\') : s.at(0);
+
+ initRuleData(rule.data.lineContinue, c);
+ rule.type = Rule::Type::LineContinue;
+ } else if (name == QLatin1String("Int")) {
+ initRuleData(rule.data.detectInt, loadAdditionalWordDelimiters(reader));
+ rule.type = Rule::Type::Int;
+ } else if (name == QLatin1String("Float")) {
+ initRuleData(rule.data.detectFloat, loadAdditionalWordDelimiters(reader));
+ rule.type = Rule::Type::Float;
+ } else if (name == QLatin1String("HlCStringChar")) {
+ rule.type = Rule::Type::HlCStringChar;
+ } else if (name == QLatin1String("RangeDetect")) {
+ const auto s1 = attrs.value(QLatin1String("char"));
+ const auto s2 = attrs.value(QLatin1String("char1"));
+ if (!checkIsChar(s1, "char", defName, reader)) {
+ return false;
+ }
+ if (!checkIsChar(s2, "char1", defName, reader)) {
+ return false;
+ }
+
+ initRuleData(rule.data.rangeDetect, s1.at(0), s2.at(0));
+ rule.type = Rule::Type::RangeDetect;
+ } else if (name == QLatin1String("HlCHex")) {
+ initRuleData(rule.data.hlCHex, loadAdditionalWordDelimiters(reader));
+ rule.type = Rule::Type::HlCHex;
+ } else if (name == QLatin1String("HlCChar")) {
+ rule.type = Rule::Type::HlCChar;
+ } else if (name == QLatin1String("HlCOct")) {
+ initRuleData(rule.data.hlCOct, loadAdditionalWordDelimiters(reader));
+ rule.type = Rule::Type::HlCOct;
+ } else {
+ qCWarning(Log) << "Unknown rule type:" << name;
+ return false;
+ }
+
+ if (!isIncludeRules) {
+ rule.common.contextName = attrs.value(QLatin1String("context")).toString();
+ rule.common.beginRegionName = attrs.value(QLatin1String("beginRegion")).toString();
+ rule.common.endRegionName = attrs.value(QLatin1String("endRegion")).toString();
+ rule.common.firstNonSpace = Xml::attrToBool(attrs.value(QLatin1String("firstNonSpace")));
+ rule.common.lookAhead = Xml::attrToBool(attrs.value(QLatin1String("lookAhead")));
+ // attribute is only used when lookAhead is false
+ if (!rule.common.lookAhead) {
+ rule.common.attributeName = attrs.value(QLatin1String("attribute")).toString();
+ }
+ bool colOk = false;
+ rule.common.column = attrs.value(QLatin1String("column")).toInt(&colOk);
+ if (!colOk) {
+ rule.common.column = -1;
+ }
+ }
+
+ return true;
+}
+
+template<class Data1, class Data2, class Visitor>
+static void dataRuleVisit(HighlightingContextData::Rule::Type type, Data1 &&data1, Data2 &&data2, Visitor &&visitor)
+{
+ using Rule = HighlightingContextData::Rule;
+ using Type = Rule::Type;
+ switch (type) {
+ case Type::AnyChar:
+ visitor(data1.anyChar, data2.anyChar);
+ break;
+ case Type::DetectChar:
+ visitor(data1.detectChar, data2.detectChar);
+ break;
+ case Type::Detect2Chars:
+ visitor(data1.detect2Chars, data2.detect2Chars);
+ break;
+ case Type::HlCOct:
+ visitor(data1.hlCOct, data2.hlCOct);
+ break;
+ case Type::IncludeRules:
+ visitor(data1.includeRules, data2.includeRules);
+ break;
+ case Type::Int:
+ visitor(data1.detectInt, data2.detectInt);
+ break;
+ case Type::Keyword:
+ visitor(data1.keyword, data2.keyword);
+ break;
+ case Type::LineContinue:
+ visitor(data1.lineContinue, data2.lineContinue);
+ break;
+ case Type::RangeDetect:
+ visitor(data1.rangeDetect, data2.rangeDetect);
+ break;
+ case Type::RegExpr:
+ visitor(data1.regExpr, data2.regExpr);
+ break;
+ case Type::StringDetect:
+ visitor(data1.stringDetect, data2.stringDetect);
+ break;
+ case Type::WordDetect:
+ visitor(data1.wordDetect, data2.wordDetect);
+ break;
+ case Type::Float:
+ visitor(data1.detectFloat, data2.detectFloat);
+ break;
+ case Type::HlCHex:
+ visitor(data1.hlCHex, data2.hlCHex);
+ break;
+
+ case Type::HlCStringChar:
+ case Type::DetectIdentifier:
+ case Type::DetectSpaces:
+ case Type::HlCChar:
+ case Type::Unknown:;
+ }
+}
+
+HighlightingContextData::Rule::Rule() noexcept = default;
+
+HighlightingContextData::Rule::Rule(Rule &&other) noexcept
+ : common(std::move(other.common))
+{
+ dataRuleVisit(other.type, data, other.data, [](auto &data1, auto &data2) {
+ using Data = std::remove_reference_t<decltype(data1)>;
+ new (&data1) Data(std::move(data2));
+ });
+ type = other.type;
+}
+
+HighlightingContextData::Rule::Rule(const Rule &other)
+ : common(other.common)
+{
+ dataRuleVisit(other.type, data, other.data, [](auto &data1, auto &data2) {
+ using Data = std::remove_reference_t<decltype(data1)>;
+ new (&data1) Data(data2);
+ });
+ type = other.type;
+}
+
+HighlightingContextData::Rule::~Rule()
+{
+ dataRuleVisit(type, data, data, [](auto &data, auto &) {
+ using Data = std::remove_reference_t<decltype(data)>;
+ data.~Data();
+ });
+}
+
+HighlightingContextData::ContextSwitch::ContextSwitch(QStringView str)
+{
+ if (str.isEmpty() || str == QStringLiteral("#stay")) {
+ return;
+ }
+
+ while (str.startsWith(QStringLiteral("#pop"))) {
+ ++m_popCount;
+ if (str.size() > 4 && str.at(4) == QLatin1Char('!')) {
+ str = str.mid(5);
+ break;
+ }
+ str = str.mid(4);
+ }
+
+ if (str.isEmpty()) {
+ return;
+ }
+
+ m_contextAndDefName = str.toString();
+ m_defNameIndex = str.indexOf(QStringLiteral("##"));
+}
+
+bool HighlightingContextData::ContextSwitch::isStay() const
+{
+ return m_popCount == -1 && m_contextAndDefName.isEmpty();
+}
+
+QStringView HighlightingContextData::ContextSwitch::contextName() const
+{
+ if (m_defNameIndex == -1) {
+ return m_contextAndDefName;
+ }
+ return QStringView(m_contextAndDefName).left(m_defNameIndex);
+}
+
+QStringView HighlightingContextData::ContextSwitch::defName() const
+{
+ if (m_defNameIndex == -1) {
+ return QStringView();
+ }
+ return QStringView(m_contextAndDefName).mid(m_defNameIndex + 2);
+}
+
+void HighlightingContextData::load(const QString &defName, QXmlStreamReader &reader)
+{
+ Q_ASSERT(reader.name() == QLatin1String("context"));
+ Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
+
+ name = reader.attributes().value(QLatin1String("name")).toString();
+ attribute = reader.attributes().value(QLatin1String("attribute")).toString();
+ lineEndContext = reader.attributes().value(QLatin1String("lineEndContext")).toString();
+ lineEmptyContext = reader.attributes().value(QLatin1String("lineEmptyContext")).toString();
+ fallthroughContext = reader.attributes().value(QLatin1String("fallthroughContext")).toString();
+ noIndentationBasedFolding = Xml::attrToBool(reader.attributes().value(QLatin1String("noIndentationBasedFolding")));
+ stopEmptyLineContextSwitchLoop = Xml::attrToBool(reader.attributes().value(QLatin1String("stopEmptyLineContextSwitchLoop")));
+
+ rules.reserve(8);
+
+ reader.readNext();
+ while (!reader.atEnd()) {
+ switch (reader.tokenType()) {
+ case QXmlStreamReader::StartElement: {
+ auto &rule = rules.emplace_back();
+ if (!loadRule(defName, rule, reader)) {
+ rules.pop_back();
+ }
+ // be done with this rule, skip all subelements, e.g. no longer supported sub-rules
+ reader.skipCurrentElement();
+ reader.readNext();
+ break;
+ }
+ case QXmlStreamReader::EndElement:
+ return;
+ default:
+ reader.readNext();
+ break;
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata_p.hpp b/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata_p.hpp
new file mode 100644
index 0000000000..f49227dbf9
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata_p.hpp
@@ -0,0 +1,216 @@
+/*
+ SPDX-FileCopyrightText: 2021 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#ifndef KSYNTAXHIGHLIGHTING_HIGHLIGHTING_DATA_P_H
+#define KSYNTAXHIGHLIGHTING_HIGHLIGHTING_DATA_P_H
+
+#include <QString>
+#include <QStringList>
+
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+class QXmlStreamReader;
+QT_END_NAMESPACE
+
+namespace KSyntaxHighlighting
+{
+/**
+ * Represents the raw xml data of a context and its rules.
+ * After resolving contexts, members of this class are no longer
+ * use and the instance can be freed to recover used memory.
+ */
+class HighlightingContextData
+{
+public:
+ void load(const QString &defName, QXmlStreamReader &reader);
+
+ struct ContextSwitch {
+ ContextSwitch() = default;
+ ContextSwitch(QStringView str);
+
+ QStringView contextName() const;
+ QStringView defName() const;
+
+ bool isStay() const;
+
+ int popCount() const
+ {
+ return m_popCount;
+ }
+
+ private:
+ int m_popCount = 0;
+ int m_defNameIndex = -1;
+ QString m_contextAndDefName;
+ };
+
+ struct Rule {
+ enum class Type : quint8 {
+ Unknown,
+ AnyChar,
+ Detect2Chars,
+ DetectChar,
+ HlCOct,
+ IncludeRules,
+ Int,
+ Keyword,
+ LineContinue,
+ RangeDetect,
+ RegExpr,
+ StringDetect,
+ WordDetect,
+ Float,
+ HlCStringChar,
+ DetectIdentifier,
+ DetectSpaces,
+ HlCChar,
+ HlCHex,
+ };
+
+ struct AnyChar {
+ QString chars;
+ };
+
+ struct Detect2Chars {
+ QChar char1;
+ QChar char2;
+ };
+
+ struct DetectChar {
+ QChar char1;
+ bool dynamic;
+ };
+
+ struct WordDelimiters {
+ QString additionalDeliminator;
+ QString weakDeliminator;
+ };
+
+ struct Float {
+ WordDelimiters wordDelimiters;
+ };
+
+ struct HlCHex {
+ WordDelimiters wordDelimiters;
+ };
+
+ struct HlCOct {
+ WordDelimiters wordDelimiters;
+ };
+
+ struct IncludeRules {
+ QString contextName;
+ bool includeAttribute;
+ };
+
+ struct Int {
+ WordDelimiters wordDelimiters;
+ };
+
+ struct Keyword {
+ QString name;
+ WordDelimiters wordDelimiters;
+ Qt::CaseSensitivity caseSensitivityOverride;
+ bool hasCaseSensitivityOverride;
+ };
+
+ struct LineContinue {
+ QChar char1;
+ };
+
+ struct RangeDetect {
+ QChar begin;
+ QChar end;
+ };
+
+ struct RegExpr {
+ QString pattern;
+ Qt::CaseSensitivity caseSensitivity;
+ bool isMinimal;
+ bool dynamic;
+ };
+
+ struct StringDetect {
+ QString string;
+ Qt::CaseSensitivity caseSensitivity;
+ bool dynamic;
+ };
+
+ struct WordDetect {
+ QString word;
+ WordDelimiters wordDelimiters;
+ Qt::CaseSensitivity caseSensitivity;
+ };
+
+ union Data {
+ AnyChar anyChar;
+ Detect2Chars detect2Chars;
+ DetectChar detectChar;
+ HlCOct hlCOct;
+ IncludeRules includeRules;
+ Int detectInt;
+ Keyword keyword;
+ LineContinue lineContinue;
+ RangeDetect rangeDetect;
+ RegExpr regExpr;
+ StringDetect stringDetect;
+ WordDetect wordDetect;
+ Float detectFloat;
+ HlCHex hlCHex;
+
+ Data() noexcept
+ {
+ }
+
+ ~Data()
+ {
+ }
+ };
+
+ struct Common {
+ QString contextName;
+ QString attributeName;
+ QString beginRegionName;
+ QString endRegionName;
+ int column = -1;
+ bool firstNonSpace = false;
+ bool lookAhead = false;
+ };
+
+ Type type = Type::Unknown;
+ Common common;
+ Data data;
+
+ Rule() noexcept;
+ Rule(Rule &&other) noexcept;
+ Rule(const Rule &other);
+ ~Rule();
+
+ // since nothing is deleted in the rules vector, these functions do not need to be implemented
+ Rule &operator=(Rule &&other) = delete;
+ Rule &operator=(const Rule &other) = delete;
+ };
+
+ QString name;
+
+ /**
+ * attribute name, to lookup our format
+ */
+ QString attribute;
+
+ QString lineEndContext;
+ QString lineEmptyContext;
+ QString fallthroughContext;
+
+ std::vector<Rule> rules;
+
+ bool stopEmptyLineContextSwitchLoop = false;
+ bool noIndentationBasedFolding = false;
+};
+}
+
+#endif
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp
index 4ebd465b77..928ae149d1 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp
@@ -1,52 +1,37 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
- Copyright (C) 2018 Christoph Cullmann <cullmann@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2018 Christoph Cullmann <cullmann@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#include "htmlhighlighter.h"
+#include "abstracthighlighter_p.h"
#include "definition.h"
+#include "definition_p.h"
#include "format.h"
+#include "ksyntaxhighlighting_logging.h"
#include "state.h"
#include "theme.h"
-#include "ksyntaxhighlighting_logging.h"
-#include <QDebug>
#include <QFile>
#include <QFileInfo>
+#include <QIODevice>
#include <QTextStream>
-#include <QVarLengthArray>
using namespace KSyntaxHighlighting;
-class KSyntaxHighlighting::HtmlHighlighterPrivate
+class KSyntaxHighlighting::HtmlHighlighterPrivate : public AbstractHighlighterPrivate
{
public:
std::unique_ptr<QTextStream> out;
std::unique_ptr<QFile> file;
QString currentLine;
+ std::vector<QString> htmlStyles;
};
HtmlHighlighter::HtmlHighlighter()
- : d(new HtmlHighlighterPrivate())
+ : AbstractHighlighter(new HtmlHighlighterPrivate())
{
}
@@ -54,24 +39,26 @@ HtmlHighlighter::~HtmlHighlighter()
{
}
-void HtmlHighlighter::setOutputFile(const QString& fileName)
+void HtmlHighlighter::setOutputFile(const QString &fileName)
{
+ Q_D(HtmlHighlighter);
d->file.reset(new QFile(fileName));
if (!d->file->open(QFile::WriteOnly | QFile::Truncate)) {
qCWarning(Log) << "Failed to open output file" << fileName << ":" << d->file->errorString();
return;
}
d->out.reset(new QTextStream(d->file.get()));
- d->out->setCodec("UTF-8");
+ d->out->setEncoding(QStringConverter::Utf8);
}
void HtmlHighlighter::setOutputFile(FILE *fileHandle)
{
+ Q_D(HtmlHighlighter);
d->out.reset(new QTextStream(fileHandle, QIODevice::WriteOnly));
- d->out->setCodec("UTF-8");
+ d->out->setEncoding(QStringConverter::Utf8);
}
-void HtmlHighlighter::highlightFile(const QString& fileName, const QString& title)
+void HtmlHighlighter::highlightFile(const QString &fileName, const QString &title)
{
QFileInfo fi(fileName);
QFile f(fileName);
@@ -80,40 +67,120 @@ void HtmlHighlighter::highlightFile(const QString& fileName, const QString& titl
return;
}
- if (title.isEmpty())
+ if (title.isEmpty()) {
highlightData(&f, fi.fileName());
- else
+ } else {
highlightData(&f, title);
+ }
+}
+
+/**
+ * @brief toHtmlRgba
+ * Converts QColor -> #RRGGBBAA if there is an alpha channel
+ * otherwise it will just return the hexcode. This is because QColor
+ * outputs #AARRGGBB, whereas browser support #RRGGBBAA.
+ *
+ * @param color
+ * @return
+ */
+static QString toHtmlRgbaString(const QColor &color)
+{
+ if (color.alpha() == 0xFF) {
+ return color.name();
+ }
+ static const char16_t digits[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ QChar hexcode[9];
+ hexcode[0] = QLatin1Char('#');
+ hexcode[1] = digits[color.red() >> 4];
+ hexcode[2] = digits[color.red() & 0xf];
+ hexcode[3] = digits[color.green() >> 4];
+ hexcode[4] = digits[color.green() & 0xf];
+ hexcode[5] = digits[color.blue() >> 4];
+ hexcode[6] = digits[color.blue() & 0xf];
+ hexcode[7] = digits[color.alpha() >> 4];
+ hexcode[8] = digits[color.alpha() & 0xf];
+ return QString(hexcode, 9);
}
-void HtmlHighlighter::highlightData(QIODevice *dev, const QString& title)
+void HtmlHighlighter::highlightData(QIODevice *dev, const QString &title)
{
+ Q_D(HtmlHighlighter);
+
if (!d->out) {
qCWarning(Log) << "No output stream defined!";
return;
}
QString htmlTitle;
- if (title.isEmpty())
- htmlTitle = QStringLiteral("Kate Syntax Highlighter");
- else
+ if (title.isEmpty()) {
+ htmlTitle = QStringLiteral("KSyntaxHighlighter");
+ } else {
htmlTitle = title.toHtmlEscaped();
+ }
+
+ const auto &theme = d->m_theme;
+ const auto &definition = d->m_definition;
+
+ auto definitions = definition.includedDefinitions();
+ definitions.append(definition);
+
+ int maxId = 0;
+ for (const auto &definition : std::as_const(definitions)) {
+ for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) {
+ maxId = qMax(maxId, format.id());
+ }
+ }
+ d->htmlStyles.clear();
+ // htmlStyles must not be empty for applyFormat to work even with a definition without any context
+ d->htmlStyles.resize(maxId + 1);
+
+ // initialize htmlStyles
+ for (const auto &definition : std::as_const(definitions)) {
+ for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) {
+ auto &buffer = d->htmlStyles[format.id()];
+ if (format.hasTextColor(theme)) {
+ buffer += QStringLiteral("color:") + toHtmlRgbaString(format.textColor(theme)) + QStringLiteral(";");
+ }
+ if (format.hasBackgroundColor(theme)) {
+ buffer += QStringLiteral("background-color:") + toHtmlRgbaString(format.backgroundColor(theme)) + QStringLiteral(";");
+ }
+ if (format.isBold(theme)) {
+ buffer += QStringLiteral("font-weight:bold;");
+ }
+ if (format.isItalic(theme)) {
+ buffer += QStringLiteral("font-style:italic;");
+ }
+ if (format.isUnderline(theme)) {
+ buffer += QStringLiteral("text-decoration:underline;");
+ }
+ if (format.isStrikeThrough(theme)) {
+ buffer += QStringLiteral("text-decoration:line-through;");
+ }
+
+ if (!buffer.isEmpty()) {
+ buffer.insert(0, QStringLiteral("<span style=\""));
+ // replace last ';'
+ buffer.back() = u'"';
+ buffer += u'>';
+ }
+ }
+ }
State state;
*d->out << "<!DOCTYPE html>\n";
*d->out << "<html><head>\n";
*d->out << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n";
*d->out << "<title>" << htmlTitle << "</title>\n";
- *d->out << "<meta name=\"generator\" content=\"KF5::SyntaxHighlighting (" << definition().name() << ")\"/>\n";
+ *d->out << "<meta name=\"generator\" content=\"KF5::SyntaxHighlighting - Definition (" << definition.name() << ") - Theme (" << theme.name() << ")\"/>\n";
*d->out << "</head><body";
- if (theme().textColor(Theme::Normal))
- *d->out << " style=\"color:" << QColor(theme().textColor(Theme::Normal)).name() << "\"";
- *d->out << "><pre>\n";
+ *d->out << " style=\"background-color:" << toHtmlRgbaString(QColor::fromRgba(theme.editorColor(Theme::BackgroundColor)));
+ if (theme.textColor(Theme::Normal)) {
+ *d->out << ";color:" << toHtmlRgbaString(QColor::fromRgba(theme.textColor(Theme::Normal)));
+ }
+ *d->out << "\"><pre>\n";
QTextStream in(dev);
- in.setCodec("UTF-8");
- while (!in.atEnd()) {
- d->currentLine = in.readLine();
+ while (in.readLineInto(&d->currentLine)) {
state = highlightLine(d->currentLine, state);
*d->out << "\n";
}
@@ -125,37 +192,30 @@ void HtmlHighlighter::highlightData(QIODevice *dev, const QString& title)
d->file.reset();
}
-void HtmlHighlighter::applyFormat(int offset, int length, const Format& format)
+void HtmlHighlighter::applyFormat(int offset, int length, const Format &format)
{
- if (length == 0)
+ if (length == 0) {
return;
+ }
- // collect potential output, cheaper than thinking about "is there any?"
- QVarLengthArray<QString, 16> formatOutput;
- if (format.hasTextColor(theme()))
- formatOutput << QStringLiteral("color:") << format.textColor(theme()).name() << QStringLiteral(";");
- if (format.hasBackgroundColor(theme()))
- formatOutput << QStringLiteral("background-color:") << format.backgroundColor(theme()).name() << QStringLiteral(";");
- if (format.isBold(theme()))
- formatOutput << QStringLiteral("font-weight:bold;");
- if (format.isItalic(theme()))
- formatOutput << QStringLiteral("font-style:italic;");
- if (format.isUnderline(theme()))
- formatOutput << QStringLiteral("text-decoration:underline;");
- if (format.isStrikeThrough(theme()))
- formatOutput << QStringLiteral("text-decoration:line-through;");
-
- if (!formatOutput.isEmpty()) {
- *d->out << "<span style=\"";
- for (const auto &out : qAsConst(formatOutput)) {
- *d->out << out;
- }
- *d->out << "\">";
+ Q_D(HtmlHighlighter);
+
+ auto const &htmlStyle = d->htmlStyles[format.id()];
+
+ if (!htmlStyle.isEmpty()) {
+ *d->out << htmlStyle;
}
- *d->out << d->currentLine.mid(offset, length).toHtmlEscaped();
+ for (QChar ch : QStringView(d->currentLine).mid(offset, length)) {
+ if (ch == u'<')
+ *d->out << QStringLiteral("&lt;");
+ else if (ch == u'&')
+ *d->out << QStringLiteral("&amp;");
+ else
+ *d->out << ch;
+ }
- if (!formatOutput.isEmpty()) {
- *d->out << "</span>";
+ if (!htmlStyle.isEmpty()) {
+ *d->out << QStringLiteral("</span>");
}
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h
index b7eda02d54..741cb85103 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h
@@ -1,44 +1,23 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_HTMLHIGHLIGHTER_H
#define KSYNTAXHIGHLIGHTING_HTMLHIGHLIGHTER_H
-#include "ksyntaxhighlighting_export.h"
#include "abstracthighlighter.h"
+#include "ksyntaxhighlighting_export.h"
#include <QString>
-#include <QIODevice>
-
-#include <memory>
QT_BEGIN_NAMESPACE
-class QFile;
-class QTextStream;
+class QIODevice;
QT_END_NAMESPACE
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class HtmlHighlighterPrivate;
class KSYNTAXHIGHLIGHTING_EXPORT HtmlHighlighter : public AbstractHighlighter
@@ -57,7 +36,7 @@ protected:
void applyFormat(int offset, int length, const Format &format) override;
private:
- std::unique_ptr<HtmlHighlighterPrivate> d;
+ Q_DECLARE_PRIVATE(HtmlHighlighter)
};
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp
index f042baac27..847f6af6d4 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp
@@ -1,39 +1,43 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
-#include "keywordlist_p.h"
-#include "repository.h"
#include "definition_p.h"
+#include "keywordlist_p.h"
#include "ksyntaxhighlighting_logging.h"
+#include "repository.h"
-#include <QDebug>
#include <QXmlStreamReader>
#include <algorithm>
using namespace KSyntaxHighlighting;
-bool KeywordList::contains(const QStringRef &str, Qt::CaseSensitivity caseSensitive) const
+namespace
+{
+struct KeywordComparator {
+ Qt::CaseSensitivity caseSensitive;
+
+ bool operator()(QStringView a, QStringView b) const
+ {
+ if (a.size() < b.size()) {
+ return true;
+ }
+
+ if (a.size() > b.size()) {
+ return false;
+ }
+
+ return a.compare(b, caseSensitive) < 0;
+ }
+};
+
+}
+
+bool KeywordList::contains(QStringView str, Qt::CaseSensitivity caseSensitive) const
{
/**
* get right vector to search in
@@ -43,37 +47,36 @@ bool KeywordList::contains(const QStringRef &str, Qt::CaseSensitivity caseSensit
/**
* search with right predicate
*/
- return std::binary_search(vectorToSearch.begin(), vectorToSearch.end(), str, [caseSensitive] (const QStringRef &a, const QStringRef &b) { return a.compare(b, caseSensitive) < 0; });
+ return std::binary_search(vectorToSearch.begin(), vectorToSearch.end(), str, KeywordComparator{caseSensitive});
}
-void KeywordList::load(QXmlStreamReader& reader)
+void KeywordList::load(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("list"));
Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
- m_name = reader.attributes().value(QStringLiteral("name")).toString();
+ m_name = reader.attributes().value(QLatin1String("name")).toString();
while (!reader.atEnd()) {
switch (reader.tokenType()) {
- case QXmlStreamReader::StartElement:
- if (reader.name() == QLatin1String("item")) {
- m_keywords.append(reader.readElementText().trimmed());
- reader.readNextStartElement();
- break;
- }
- else if (reader.name() == QLatin1String("include")) {
- m_includes.append(reader.readElementText().trimmed());
- reader.readNextStartElement();
- break;
- }
- reader.readNext();
+ case QXmlStreamReader::StartElement:
+ if (reader.name() == QLatin1String("item")) {
+ m_keywords.append(reader.readElementText().trimmed());
+ reader.readNextStartElement();
break;
- case QXmlStreamReader::EndElement:
- reader.readNext();
- return;
- default:
- reader.readNext();
+ } else if (reader.name() == QLatin1String("include")) {
+ m_includes.append(reader.readElementText().trimmed());
+ reader.readNextStartElement();
break;
+ }
+ reader.readNext();
+ break;
+ case QXmlStreamReader::EndElement:
+ reader.readNext();
+ return;
+ default:
+ reader.readNext();
+ break;
}
}
}
@@ -100,15 +103,12 @@ void KeywordList::initLookupForCaseSensitivity(Qt::CaseSensitivity caseSensitive
/**
* fill vector with refs to keywords
*/
- vectorToSort.reserve(m_keywords.size());
- for (const auto &keyword : qAsConst(m_keywords)) {
- vectorToSort.push_back(&keyword);
- }
+ vectorToSort.assign(m_keywords.constBegin(), m_keywords.constEnd());
/**
* sort with right predicate
*/
- std::sort(vectorToSort.begin(), vectorToSort.end(), [caseSensitive] (const QStringRef &a, const QStringRef &b) { return a.compare(b, caseSensitive) < 0; });
+ std::sort(vectorToSort.begin(), vectorToSort.end(), KeywordComparator{caseSensitive});
}
void KeywordList::resolveIncludeKeywords(DefinitionData &def)
@@ -121,15 +121,14 @@ void KeywordList::resolveIncludeKeywords(DefinitionData &def)
KeywordList *keywords = nullptr;
if (idx >= 0) {
- auto listName = kw_include.left(idx);
auto defName = kw_include.mid(idx + 2);
auto includeDef = def.repo->definitionForName(defName);
if (includeDef.isValid()) {
+ auto listName = kw_include.left(idx);
auto defData = DefinitionData::get(includeDef);
defData->load(DefinitionData::OnlyKeywords(true));
keywords = defData->keywordList(listName);
- }
- else {
+ } else {
qCWarning(Log) << "Unable to resolve external include keyword for definition" << defName << "in" << def.name;
}
} else {
@@ -141,8 +140,7 @@ void KeywordList::resolveIncludeKeywords(DefinitionData &def)
keywords->resolveIncludeKeywords(def);
}
m_keywords += keywords->m_keywords;
- }
- else {
+ } else {
qCWarning(Log) << "Unresolved include keyword" << kw_include << "in" << def.name;
}
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist_p.h
index 25d0022dbe..a8578522cb 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist_p.h
@@ -1,32 +1,16 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_KEYWORDLIST_P_H
#define KSYNTAXHIGHLIGHTING_KEYWORDLIST_P_H
-#include <QSet>
#include <QString>
-#include <QVector>
+#include <QStringList>
+#include <QStringView>
#include <vector>
@@ -34,8 +18,8 @@ QT_BEGIN_NAMESPACE
class QXmlStreamReader;
QT_END_NAMESPACE
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class Repository;
class DefinitionData;
@@ -60,14 +44,27 @@ public:
return m_keywords;
}
+ Qt::CaseSensitivity caseSensitivity() const
+ {
+ return m_caseSensitive;
+ }
+
+ void setKeywordList(const QStringList &keywords)
+ {
+ m_keywords = keywords;
+ m_keywordsSortedCaseSensitive.clear();
+ m_keywordsSortedCaseInsensitive.clear();
+ initLookupForCaseSensitivity(m_caseSensitive);
+ }
+
/** Checks if @p str is a keyword in this list. */
- bool contains(const QStringRef &str) const
+ bool contains(QStringView str) const
{
return contains(str, m_caseSensitive);
}
/** Checks if @p str is a keyword in this list, overriding the global case-sensitivity setting. */
- bool contains(const QStringRef &str, Qt::CaseSensitivity caseSensitive) const;
+ bool contains(QStringView str, Qt::CaseSensitivity caseSensitive) const;
void load(QXmlStreamReader &reader);
void setCaseSensitivity(Qt::CaseSensitivity caseSensitive);
@@ -98,12 +95,12 @@ private:
/**
* case-sensitive sorted string references to m_keywords for lookup
*/
- std::vector<QStringRef> m_keywordsSortedCaseSensitive;
+ std::vector<QStringView> m_keywordsSortedCaseSensitive;
/**
* case-insensitive sorted string references to m_keywords for lookup
*/
- std::vector<QStringRef> m_keywordsSortedCaseInsensitive;
+ std::vector<QStringView> m_keywordsSortedCaseInsensitive;
};
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h
index b1a05ee636..7112d4e291 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_MATCHRESULT_P_H
@@ -26,8 +9,8 @@
#include <QStringList>
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
/**
* Storage for match result of a Rule.
* Heavily used internally during highlightLine, therefore completely inline.
@@ -58,9 +41,9 @@ public:
* @param offset offset of match
* @param captures captures of the match
*/
- explicit MatchResult(const int offset, const QStringList &captures)
+ explicit MatchResult(const int offset, QStringList &&captures)
: m_offset(offset)
- , m_captures(captures)
+ , m_captures(std::move(captures))
{
}
@@ -73,7 +56,6 @@ public:
return m_offset;
}
-
/**
* Skip offset of the match
* @return skip offset of the match, no match possible until this offset is reached
@@ -87,7 +69,7 @@ public:
* Captures of the match.
* @return captured text of this match
*/
- const QStringList &captures() const
+ QStringList &captures()
{
return m_captures;
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp
index aaba9616dc..07c28454c5 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp
@@ -1,50 +1,93 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#include "repository.h"
-#include "repository_p.h"
#include "definition.h"
#include "definition_p.h"
+#include "ksyntaxhighlighting_logging.h"
+#include "repository_p.h"
#include "theme.h"
#include "themedata_p.h"
-#include "ksyntaxhighlighting_logging.h"
-#include "wildcardmatcher_p.h"
+#include "wildcardmatcher.h"
-#include <QDebug>
+#include <QCborMap>
+#include <QCborValue>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
-#include <QJsonDocument>
-#include <QJsonObject>
+#include <QPalette>
+#include <QString>
+#include <QStringView>
#ifndef NO_STANDARD_PATHS
#include <QStandardPaths>
#endif
+#include <algorithm>
+#include <iterator>
#include <limits>
using namespace KSyntaxHighlighting;
+namespace
+{
+QString fileNameFromFilePath(const QString &filePath)
+{
+ return QFileInfo{filePath}.fileName();
+}
+
+auto anyWildcardMatches(QStringView str)
+{
+ return [str](const Definition &def) {
+ const auto strings = def.extensions();
+ return std::any_of(strings.cbegin(), strings.cend(), [str](QStringView wildcard) {
+ return WildcardMatcher::exactMatch(str, wildcard);
+ });
+ };
+}
+
+auto anyMimeTypeEquals(QStringView mimeTypeName)
+{
+ return [mimeTypeName](const Definition &def) {
+ const auto strings = def.mimeTypes();
+ return std::any_of(strings.cbegin(), strings.cend(), [mimeTypeName](QStringView name) {
+ return mimeTypeName == name;
+ });
+ };
+}
+
+// The two function templates below take defs - a map sorted by highlighting name - to be deterministic and independent of translations.
+
+template<typename UnaryPredicate>
+Definition findHighestPriorityDefinitionIf(const QMap<QString, Definition> &defs, UnaryPredicate predicate)
+{
+ const Definition *match = nullptr;
+ auto matchPriority = std::numeric_limits<int>::lowest();
+ for (const Definition &def : defs) {
+ const auto defPriority = def.priority();
+ if (defPriority > matchPriority && predicate(def)) {
+ match = &def;
+ matchPriority = defPriority;
+ }
+ }
+ return match == nullptr ? Definition{} : *match;
+}
+
+template<typename UnaryPredicate>
+QList<Definition> findDefinitionsIf(const QMap<QString, Definition> &defs, UnaryPredicate predicate)
+{
+ QList<Definition> matches;
+ std::copy_if(defs.cbegin(), defs.cend(), std::back_inserter(matches), predicate);
+ std::stable_sort(matches.begin(), matches.end(), [](const Definition &lhs, const Definition &rhs) {
+ return lhs.priority() > rhs.priority();
+ });
+ return matches;
+}
+} // unnamed namespace
+
static void initResource()
{
#ifdef HAS_SYNTAX_RESOURCE
@@ -53,13 +96,13 @@ static void initResource()
Q_INIT_RESOURCE(theme_data);
}
-RepositoryPrivate* RepositoryPrivate::get(Repository *repo)
+RepositoryPrivate *RepositoryPrivate::get(Repository *repo)
{
return repo->d.get();
}
-Repository::Repository() :
- d(new RepositoryPrivate)
+Repository::Repository()
+ : d(new RepositoryPrivate)
{
initResource();
d->load(this);
@@ -69,93 +112,97 @@ Repository::~Repository()
{
// reset repo so we can detect in still alive definition instances
// that the repo was deleted
- for (const auto &def : qAsConst(d->m_sortedDefs))
+ for (const auto &def : std::as_const(d->m_sortedDefs)) {
DefinitionData::get(def)->repo = nullptr;
+ }
}
-Definition Repository::definitionForName(const QString& defName) const
+Definition Repository::definitionForName(const QString &defName) const
{
return d->m_defs.value(defName);
}
-static void sortDefinitions(QVector<Definition> &definitions)
+Definition Repository::definitionForFileName(const QString &fileName) const
{
- std::stable_sort(definitions.begin(), definitions.end(), [](const Definition &lhs, const Definition &rhs) {
- return lhs.priority() > rhs.priority();
- });
+ return findHighestPriorityDefinitionIf(d->m_defs, anyWildcardMatches(fileNameFromFilePath(fileName)));
}
-Definition Repository::definitionForFileName(const QString& fileName) const
+QList<Definition> Repository::definitionsForFileName(const QString &fileName) const
{
- return definitionsForFileName(fileName).value(0);
+ return findDefinitionsIf(d->m_defs, anyWildcardMatches(fileNameFromFilePath(fileName)));
}
-QVector<Definition> Repository::definitionsForFileName(const QString &fileName) const
+Definition Repository::definitionForMimeType(const QString &mimeType) const
{
- QFileInfo fi(fileName);
- const auto name = fi.fileName();
-
- QVector<Definition> candidates;
- for (const Definition &def : qAsConst(d->m_sortedDefs)) {
- for (const auto &pattern : def.extensions()) {
- if (WildcardMatcher::exactMatch(name, pattern)) {
- candidates.push_back(def);
- break;
- }
- }
- }
-
- sortDefinitions(candidates);
- return candidates;
+ return findHighestPriorityDefinitionIf(d->m_defs, anyMimeTypeEquals(mimeType));
}
-Definition Repository::definitionForMimeType(const QString& mimeType) const
+QList<Definition> Repository::definitionsForMimeType(const QString &mimeType) const
{
- return definitionsForMimeType(mimeType).value(0);
+ return findDefinitionsIf(d->m_defs, anyMimeTypeEquals(mimeType));
}
-QVector<Definition> Repository::definitionsForMimeType(const QString &mimeType) const
+QList<Definition> Repository::definitions() const
{
- QVector<Definition> candidates;
- for (const Definition &def : qAsConst(d->m_sortedDefs)) {
- for (const auto &matchType : def.mimeTypes()) {
- if (mimeType == matchType) {
- candidates.push_back(def);
- break;
- }
- }
- }
-
- sortDefinitions(candidates);
- return candidates;
+ return d->m_sortedDefs;
}
-QVector<Definition> Repository::definitions() const
+QList<Theme> Repository::themes() const
{
- return d->m_sortedDefs;
+ return d->m_themes;
}
-QVector<Theme> Repository::themes() const
+static auto lowerBoundTheme(const QList<KSyntaxHighlighting::Theme> &themes, QStringView themeName)
{
- return d->m_themes;
+ return std::lower_bound(themes.begin(), themes.end(), themeName, [](const Theme &lhs, QStringView rhs) {
+ return lhs.name() < rhs;
+ });
}
Theme Repository::theme(const QString &themeName) const
{
- for (const auto &theme : qAsConst(d->m_themes)) {
- if (theme.name() == themeName) {
- return theme;
- }
+ const auto &themes = d->m_themes;
+ const auto it = lowerBoundTheme(themes, themeName);
+ if (it != themes.end() && (*it).name() == themeName) {
+ return *it;
}
-
return Theme();
}
-Theme Repository::defaultTheme(Repository::DefaultTheme t)
+Theme Repository::defaultTheme(Repository::DefaultTheme t) const
{
- if (t == DarkTheme)
- return theme(QLatin1String("Breeze Dark"));
- return theme(QLatin1String("Default"));
+ if (t == DarkTheme) {
+ return theme(QStringLiteral("Breeze Dark"));
+ }
+ return theme(QStringLiteral("Breeze Light"));
+}
+
+Theme Repository::themeForPalette(const QPalette &palette) const
+{
+ const auto base = palette.color(QPalette::Base);
+ const auto highlight = palette.color(QPalette::Highlight).rgb();
+
+ // find themes with matching background and highlight colors
+ const Theme *firstMatchingTheme = nullptr;
+ for (const auto &theme : std::as_const(d->m_themes)) {
+ const auto background = theme.editorColor(Theme::EditorColorRole::BackgroundColor);
+ if (background == base.rgb()) {
+ // find theme with a matching highlight color
+ auto selection = theme.editorColor(Theme::EditorColorRole::TextSelection);
+ if (selection == highlight) {
+ return theme;
+ }
+ if (!firstMatchingTheme) {
+ firstMatchingTheme = &theme;
+ }
+ }
+ }
+ if (firstMatchingTheme) {
+ return *firstMatchingTheme;
+ }
+
+ // fallback to just use the default light or dark theme
+ return defaultTheme((base.lightness() < 128) ? Repository::DarkTheme : Repository::LightTheme);
}
void RepositoryPrivate::load(Repository *repo)
@@ -165,28 +212,44 @@ void RepositoryPrivate::load(Repository *repo)
// do lookup in standard paths, if not disabled
#ifndef NO_STANDARD_PATHS
- for (const auto &dir : QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("org.kde.syntax-highlighting/syntax"), QStandardPaths::LocateDirectory))
- loadSyntaxFolder(repo, dir);
+ // do lookup in installed path when has no syntax resource
+#ifndef HAS_SYNTAX_RESOURCE
+ for (const auto &dir : QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
+ QStringLiteral("org.kde.syntax-highlighting/syntax-bundled"),
+ QStandardPaths::LocateDirectory)) {
+ if (!loadSyntaxFolderFromIndex(repo, dir)) {
+ loadSyntaxFolder(repo, dir);
+ }
+ }
+#endif
- // backward compatibility with Kate
- for (const auto &dir : QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("katepart5/syntax"), QStandardPaths::LocateDirectory))
+ for (const auto &dir : QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
+ QStringLiteral("org.kde.syntax-highlighting/syntax"),
+ QStandardPaths::LocateDirectory)) {
loadSyntaxFolder(repo, dir);
+ }
#endif
- // default resources are always used
- loadSyntaxFolder(repo, QStringLiteral(":/org.kde.syntax-highlighting/syntax"));
+ // default resources are always used, this is the one location that has a index cbor file
+ loadSyntaxFolderFromIndex(repo, QStringLiteral(":/org.kde.syntax-highlighting/syntax"));
+
+ // extra resources provided by 3rdparty libraries/applications
+ loadSyntaxFolder(repo, QStringLiteral(":/org.kde.syntax-highlighting/syntax-addons"));
// user given extra paths
- for (const auto &path : qAsConst(m_customSearchPaths))
+ for (const auto &path : std::as_const(m_customSearchPaths)) {
loadSyntaxFolder(repo, path + QStringLiteral("/syntax"));
+ }
m_sortedDefs.reserve(m_defs.size());
- for (auto it = m_defs.constBegin(); it != m_defs.constEnd(); ++it)
+ for (auto it = m_defs.constBegin(); it != m_defs.constEnd(); ++it) {
m_sortedDefs.push_back(it.value());
+ }
std::sort(m_sortedDefs.begin(), m_sortedDefs.end(), [](const Definition &left, const Definition &right) {
auto comparison = left.translatedSection().compare(right.translatedSection(), Qt::CaseInsensitive);
- if (comparison == 0)
+ if (comparison == 0) {
comparison = left.translatedName().compare(right.translatedName(), Qt::CaseInsensitive);
+ }
return comparison < 0;
});
@@ -194,52 +257,61 @@ void RepositoryPrivate::load(Repository *repo)
// do lookup in standard paths, if not disabled
#ifndef NO_STANDARD_PATHS
- for (const auto &dir : QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("org.kde.syntax-highlighting/themes"), QStandardPaths::LocateDirectory))
+ for (const auto &dir : QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
+ QStringLiteral("org.kde.syntax-highlighting/themes"),
+ QStandardPaths::LocateDirectory)) {
loadThemeFolder(dir);
+ }
#endif
// default resources are always used
loadThemeFolder(QStringLiteral(":/org.kde.syntax-highlighting/themes"));
+ // extra resources provided by 3rdparty libraries/applications
+ loadThemeFolder(QStringLiteral(":/org.kde.syntax-highlighting/themes-addons"));
+
// user given extra paths
- for (const auto &path : qAsConst(m_customSearchPaths))
+ for (const auto &path : std::as_const(m_customSearchPaths)) {
loadThemeFolder(path + QStringLiteral("/themes"));
+ }
}
void RepositoryPrivate::loadSyntaxFolder(Repository *repo, const QString &path)
{
- if (loadSyntaxFolderFromIndex(repo, path))
- return;
-
QDirIterator it(path, QStringList() << QLatin1String("*.xml"), QDir::Files);
while (it.hasNext()) {
Definition def;
auto defData = DefinitionData::get(def);
defData->repo = repo;
- if (defData->loadMetaData(it.next()))
+ if (defData->loadMetaData(it.next())) {
addDefinition(def);
+ }
}
}
bool RepositoryPrivate::loadSyntaxFolderFromIndex(Repository *repo, const QString &path)
{
QFile indexFile(path + QLatin1String("/index.katesyntax"));
- if (!indexFile.open(QFile::ReadOnly))
+ if (!indexFile.open(QFile::ReadOnly)) {
return false;
+ }
- const auto indexDoc(QJsonDocument::fromBinaryData(indexFile.readAll()));
- const auto index = indexDoc.object();
+ const auto indexDoc(QCborValue::fromCbor(indexFile.readAll()));
+ const auto index = indexDoc.toMap();
for (auto it = index.begin(); it != index.end(); ++it) {
- if (!it.value().isObject())
+ if (!it.value().isMap()) {
continue;
- const auto fileName = QString(path + QLatin1Char('/') + it.key());
- const auto defMap = it.value().toObject();
+ }
+ const auto fileName = QString(path + QLatin1Char('/') + it.key().toString());
+ const auto defMap = it.value().toMap();
Definition def;
auto defData = DefinitionData::get(def);
defData->repo = repo;
- if (defData->loadMetaData(fileName, defMap))
+ if (defData->loadMetaData(fileName, defMap)) {
addDefinition(def);
+ }
}
+
return true;
}
@@ -251,8 +323,9 @@ void RepositoryPrivate::addDefinition(const Definition &def)
return;
}
- if (it.value().version() >= def.version())
+ if (it.value().version() >= def.version()) {
return;
+ }
m_defs.insert(def.name(), def);
}
@@ -261,8 +334,9 @@ void RepositoryPrivate::loadThemeFolder(const QString &path)
QDirIterator it(path, QStringList() << QLatin1String("*.theme"), QDir::Files);
while (it.hasNext()) {
auto themeData = std::unique_ptr<ThemeData>(new ThemeData);
- if (themeData->load(it.next()))
+ if (themeData->load(it.next())) {
addTheme(Theme(themeData.release()));
+ }
}
}
@@ -274,37 +348,43 @@ static int themeRevision(const Theme &theme)
void RepositoryPrivate::addTheme(const Theme &theme)
{
- const auto it = std::lower_bound(m_themes.begin(), m_themes.end(), theme, [](const Theme &lhs, const Theme &rhs) {
- return lhs.name() < rhs.name();
- });
- if (it == m_themes.end() || (*it).name() != theme.name()) {
- m_themes.insert(it, theme);
+ const auto &constThemes = m_themes;
+ const auto themeName = theme.name();
+ const auto constIt = lowerBoundTheme(constThemes, themeName);
+ const auto index = constIt - constThemes.begin();
+ if (constIt == constThemes.end() || (*constIt).name() != themeName) {
+ m_themes.insert(index, theme);
return;
}
- if (themeRevision(*it) < themeRevision(theme))
- *it = theme;
+ if (themeRevision(*constIt) < themeRevision(theme)) {
+ m_themes[index] = theme;
+ }
}
-quint16 RepositoryPrivate::foldingRegionId(const QString &defName, const QString &foldName)
+int RepositoryPrivate::foldingRegionId(const QString &defName, const QString &foldName)
{
const auto it = m_foldingRegionIds.constFind(qMakePair(defName, foldName));
- if (it != m_foldingRegionIds.constEnd())
+ if (it != m_foldingRegionIds.constEnd()) {
return it.value();
+ }
+ Q_ASSERT(m_foldingRegionId < std::numeric_limits<int>::max());
m_foldingRegionIds.insert(qMakePair(defName, foldName), ++m_foldingRegionId);
return m_foldingRegionId;
}
-quint16 RepositoryPrivate::nextFormatId()
+int RepositoryPrivate::nextFormatId()
{
- Q_ASSERT(m_formatId < std::numeric_limits<quint16>::max());
+ Q_ASSERT(m_formatId < std::numeric_limits<int>::max());
return ++m_formatId;
}
void Repository::reload()
{
- qCDebug(Log) << "Reloading syntax definitions!";
- for (const auto &def : qAsConst(d->m_sortedDefs))
+ Q_EMIT aboutToReload();
+
+ for (const auto &def : std::as_const(d->m_sortedDefs)) {
DefinitionData::get(def)->clear();
+ }
d->m_defs.clear();
d->m_sortedDefs.clear();
@@ -316,6 +396,8 @@ void Repository::reload()
d->m_formatId = 0;
d->load(this);
+
+ Q_EMIT reloaded();
}
void Repository::addCustomSearchPath(const QString &path)
@@ -324,7 +406,9 @@ void Repository::addCustomSearchPath(const QString &path)
reload();
}
-QVector<QString> Repository::customSearchPaths() const
+QList<QString> Repository::customSearchPaths() const
{
return d->m_customSearchPaths;
}
+
+#include "moc_repository.cpp"
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/repository.h b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.h
index 2bc66965cf..612ba54d6a 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/repository.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.h
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_REPOSITORY_H
@@ -26,12 +9,15 @@
#include "ksyntaxhighlighting_export.h"
-#include <qglobal.h>
+#include <QList>
+#include <QObject>
+#include <QtGlobal>
+
#include <memory>
QT_BEGIN_NAMESPACE
class QString;
-template <typename T> class QVector;
+class QPalette;
QT_END_NAMESPACE
/**
@@ -43,8 +29,8 @@ QT_END_NAMESPACE
*
* @see Repository
*/
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class Definition;
class RepositoryPrivate;
class Theme;
@@ -91,17 +77,15 @@ class Theme;
* map to $HOME/.local5/share/org.kde.syntax-highlighting/syntax and
* /usr/share/org.kde.syntax-highlighting/syntax.
*
- * -# Next, for backwards compatibility with Kate, all syntax highlighting
- * files are loaded that are located in
- * QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("katepart5/syntax"), QStandardPaths::LocateDirectory);
- * Again, under Unix, this uses $XDG_DATA_HOME and $XDG_DATA_DIRS, which
- * could map to $HOME/.local5/share/katepart5/syntax and
- * /usr/share/katepart5/syntax.
- *
* -# Then, all files compiled into the library through resources are loaded.
* The internal resource path is ":/org.kde.syntax-highlighting/syntax".
* This path should never be touched by other applications.
*
+ * -# Then, all custom files compiled into resources are loaded.
+ * The resource path is ":/org.kde.syntax-highlighting/syntax-addons".
+ * This path can be used by other libraries/applications to bundle specialized definitions.
+ * Per default this path isn't used by the framework itself.
+ *
* -# Finally, the search path can be extended by calling addCustomSearchPath().
* A custom search path can either be a path on disk or again a path to
* a Qt resource.
@@ -118,6 +102,11 @@ class Theme;
* The internal resource path is ":/org.kde.syntax-highlighting/themes".
* This path should never be touched by other applications.
*
+ * -# Then, all custom files compiled into resources are loaded.
+ * The resource path is ":/org.kde.syntax-highlighting/themes-addons".
+ * This path can be used by other libraries/applications to bundle specialized themes.
+ * Per default this path isn't used by the framework itself.
+ *
* -# Finally, all Theme%s located in the paths added addCustomSearchPath()
* are loaded.
*
@@ -129,8 +118,12 @@ class Theme;
* @see Definition, Theme, AbstractHighlighter
* @since 5.28
*/
-class KSYNTAXHIGHLIGHTING_EXPORT Repository
+class KSYNTAXHIGHLIGHTING_EXPORT Repository : public QObject
{
+ Q_OBJECT
+ Q_PROPERTY(QList<KSyntaxHighlighting::Definition> definitions READ definitions NOTIFY reloaded)
+ Q_PROPERTY(QList<KSyntaxHighlighting::Theme> themes READ themes NOTIFY reloaded)
+
public:
/**
* Create a new syntax definition repository.
@@ -153,7 +146,7 @@ public:
* Therefore, only the string "JavaScript" will return a valid
* Definition file.
*/
- Definition definitionForName(const QString &defName) const;
+ Q_INVOKABLE KSyntaxHighlighting::Definition definitionForName(const QString &defName) const;
/**
* Returns the best matching Definition for the file named @p fileName.
@@ -164,7 +157,7 @@ public:
* If no match is found, Definition::isValid() of the returned instance
* returns false.
*/
- Definition definitionForFileName(const QString &fileName) const;
+ Q_INVOKABLE KSyntaxHighlighting::Definition definitionForFileName(const QString &fileName) const;
/**
* Returns all Definition%s for the file named @p fileName sorted by priority.
@@ -173,7 +166,7 @@ public:
*
* @since 5.56
*/
- QVector<Definition> definitionsForFileName(const QString &fileName) const;
+ Q_INVOKABLE QList<KSyntaxHighlighting::Definition> definitionsForFileName(const QString &fileName) const;
/**
* Returns the best matching Definition to the type named @p mimeType
@@ -183,34 +176,34 @@ public:
*
* @since 5.50
*/
- Definition definitionForMimeType(const QString &mimeType) const;
+ Q_INVOKABLE KSyntaxHighlighting::Definition definitionForMimeType(const QString &mimeType) const;
/**
* Returns all Definition%s to the type named @p mimeType sorted by priority
*
* @since 5.56
*/
- QVector<Definition> definitionsForMimeType(const QString &mimeType) const;
+ Q_INVOKABLE QList<KSyntaxHighlighting::Definition> definitionsForMimeType(const QString &mimeType) const;
/**
* Returns all available Definition%s.
* Definition%ss are ordered by translated section and translated names,
* for consistent displaying.
*/
- QVector<Definition> definitions() const;
+ Q_INVOKABLE QList<KSyntaxHighlighting::Definition> definitions() const;
/**
* Returns all available color themes.
* The returned list should never be empty.
*/
- QVector<Theme> themes() const;
+ Q_INVOKABLE QList<KSyntaxHighlighting::Theme> themes() const;
/**
* Returns the theme called @p themeName.
* If the requested theme cannot be found, the retunred Theme is invalid,
* see Theme::isValid().
*/
- Theme theme(const QString &themeName) const;
+ Q_INVOKABLE KSyntaxHighlighting::Theme theme(const QString &themeName) const;
/**
* Built-in default theme types.
@@ -222,12 +215,20 @@ public:
//! Theme with a dark background color.
DarkTheme
};
+ Q_ENUM(DefaultTheme)
/**
* Returns a default theme instance of the given type.
* The returned Theme is guaranteed to be a valid theme.
+ * @since 5.79
*/
- Theme defaultTheme(DefaultTheme t = LightTheme);
+ Q_INVOKABLE KSyntaxHighlighting::Theme defaultTheme(DefaultTheme t = LightTheme) const;
+
+ /**
+ * Returns the best matching theme for the given palette
+ * @since 5.79
+ **/
+ Theme themeForPalette(const QPalette &palette) const;
/**
* Reloads the repository.
@@ -260,7 +261,20 @@ public:
* @see addCustomSearchPath()
* @since 5.39
*/
- QVector<QString> customSearchPaths() const;
+ QList<QString> customSearchPaths() const;
+
+Q_SIGNALS:
+ /**
+ * This signal is emitted before the reload is started.
+ * @since 6.0
+ */
+ void aboutToReload();
+
+ /**
+ * This signal is emitted when the reload is finished.
+ * @since 6.0
+ */
+ void reloaded();
private:
Q_DISABLE_COPY(Repository)
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h
index 9db876be59..bb9f8ba082 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h
@@ -1,38 +1,21 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_REPOSITORY_P_H
#define KSYNTAXHIGHLIGHTING_REPOSITORY_P_H
#include <QHash>
-#include <QVector>
-
-QT_BEGIN_NAMESPACE
-class QString;
-QT_END_NAMESPACE
+#include <QList>
+#include <QMap>
+#include <QString>
-namespace KSyntaxHighlighting {
+#include "dynamicregexpcache_p.h"
+namespace KSyntaxHighlighting
+{
class Definition;
class Repository;
class Theme;
@@ -42,7 +25,7 @@ class RepositoryPrivate
public:
RepositoryPrivate() = default;
- static RepositoryPrivate* get(Repository *repo);
+ static RepositoryPrivate *get(Repository *repo);
void load(Repository *repo);
void loadSyntaxFolder(Repository *repo, const QString &path);
@@ -53,19 +36,24 @@ public:
void loadThemeFolder(const QString &path);
void addTheme(const Theme &theme);
- quint16 foldingRegionId(const QString &defName, const QString &foldName);
- quint16 nextFormatId();
+ int foldingRegionId(const QString &defName, const QString &foldName);
+ int nextFormatId();
+
+ QList<QString> m_customSearchPaths;
+
+ // sorted map to have deterministic iteration order for e.g. definitionsForFileName
+ QMap<QString, Definition> m_defs;
- QVector<QString> m_customSearchPaths;
+ // this vector is sorted by translated sections/names
+ QList<Definition> m_sortedDefs;
- QHash<QString, Definition> m_defs;
- QVector<Definition> m_sortedDefs;
+ QList<Theme> m_themes;
- QVector<Theme> m_themes;
+ QHash<QPair<QString, QString>, int> m_foldingRegionIds;
+ int m_foldingRegionId = 0;
+ int m_formatId = 0;
- QHash<QPair<QString, QString>, quint16> m_foldingRegionIds;
- quint16 m_foldingRegionId = 0;
- quint16 m_formatId = 0;
+ DynamicRegexpCache m_dynamicRegexpCache;
};
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp
index d9cf5eb211..186ed16120 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp
@@ -1,84 +1,86 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
- Copyright (C) 2018 Christoph Cullmann <cullmann@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2018 Christoph Cullmann <cullmann@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen+kde@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
-#include "rule_p.h"
#include "context_p.h"
#include "definition_p.h"
+#include "dynamicregexpcache_p.h"
#include "ksyntaxhighlighting_logging.h"
+#include "rule_p.h"
+#include "worddelimiters_p.h"
#include "xml_p.h"
-#include <QString>
-#include <QXmlStreamReader>
-
using namespace KSyntaxHighlighting;
+// QChar::isDigit() match any digit in unicode (romain numeral, etc)
+static bool isDigit(QChar c)
+{
+ return (c <= QLatin1Char('9') && QLatin1Char('0') <= c);
+}
+
static bool isOctalChar(QChar c)
{
- return c.isNumber() && c != QLatin1Char('9') && c != QLatin1Char('8');
+ return (c <= QLatin1Char('7') && QLatin1Char('0') <= c);
}
static bool isHexChar(QChar c)
{
- return c.isNumber()
- || c == QLatin1Char('a') || c == QLatin1Char('A')
- || c == QLatin1Char('b') || c == QLatin1Char('B')
- || c == QLatin1Char('c') || c == QLatin1Char('C')
- || c == QLatin1Char('d') || c == QLatin1Char('D')
- || c == QLatin1Char('e') || c == QLatin1Char('E')
- || c == QLatin1Char('f') || c == QLatin1Char('F');
+ return isDigit(c) || (c <= QLatin1Char('f') && QLatin1Char('a') <= c) || (c <= QLatin1Char('F') && QLatin1Char('A') <= c);
}
-static int matchEscapedChar(const QString &text, int offset)
+static int matchEscapedChar(QStringView text, int offset)
{
- if (text.at(offset) != QLatin1Char('\\') || text.size() < offset + 2)
+ if (text.at(offset) != QLatin1Char('\\') || text.size() < offset + 2) {
return offset;
+ }
const auto c = text.at(offset + 1);
- static const auto controlChars = QStringLiteral("abefnrtv\"'?\\");
- if (controlChars.contains(c))
+ switch (c.unicode()) {
+ // control chars
+ case 'a':
+ case 'b':
+ case 'e':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ case 'v':
+ case '"':
+ case '\'':
+ case '?':
+ case '\\':
return offset + 2;
// hex encoded character
- if (c == QLatin1Char('x')) {
- auto newOffset = offset + 2;
- for (int i = 0; i < 2 && newOffset + i < text.size(); ++i, ++newOffset) {
- if (!isHexChar(text.at(newOffset)))
- break;
+ case 'x':
+ if (offset + 2 < text.size() && isHexChar(text.at(offset + 2))) {
+ if (offset + 3 < text.size() && isHexChar(text.at(offset + 3))) {
+ return offset + 4;
+ }
+ return offset + 3;
}
- if (newOffset == offset + 2)
- return offset;
- return newOffset;
- }
+ return offset;
// octal encoding, simple \0 is OK, too, unlike simple \x above
- if (isOctalChar(c)) {
- auto newOffset = offset + 2;
- for (int i = 0; i < 2 && newOffset + i < text.size(); ++i, ++newOffset) {
- if (!isOctalChar(text.at(newOffset)))
- break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ if (offset + 2 < text.size() && isOctalChar(text.at(offset + 2))) {
+ if (offset + 3 < text.size() && isOctalChar(text.at(offset + 3))) {
+ return offset + 4;
+ }
+ return offset + 3;
}
- return newOffset;
+ return offset + 2;
}
return offset;
@@ -87,521 +89,545 @@ static int matchEscapedChar(const QString &text, int offset)
static QString replaceCaptures(const QString &pattern, const QStringList &captures, bool quote)
{
auto result = pattern;
- for (int i = captures.size() - 1; i >= 1; --i) {
- result.replace(QLatin1Char('%') + QString::number(i), quote ? QRegularExpression::escape(captures.at(i)) : captures.at(i));
+ for (int i = captures.size(); i >= 1; --i) {
+ result.replace(QLatin1Char('%') + QString::number(i), quote ? QRegularExpression::escape(captures.at(i - 1)) : captures.at(i - 1));
}
return result;
}
-Definition Rule::definition() const
+static MatchResult matchString(QStringView pattern, QStringView text, int offset, Qt::CaseSensitivity caseSensitivity)
{
- return m_def.definition();
+ if (offset + pattern.size() <= text.size() && text.mid(offset, pattern.size()).compare(pattern, caseSensitivity) == 0) {
+ return offset + pattern.size();
+ }
+ return offset;
}
-void Rule::setDefinition(const Definition &def)
+static void resolveAdditionalWordDelimiters(WordDelimiters &wordDelimiters, const HighlightingContextData::Rule::WordDelimiters &delimiters)
{
- m_def = def;
-
// cache for DefinitionData::wordDelimiters, is accessed VERY often
- m_wordDelimiter = &DefinitionData::get(m_def.definition())->wordDelimiters;
+ if (!delimiters.additionalDeliminator.isEmpty() || !delimiters.weakDeliminator.isEmpty()) {
+ wordDelimiters.append(QStringView(delimiters.additionalDeliminator));
+ wordDelimiters.remove(QStringView(delimiters.weakDeliminator));
+ }
}
-bool Rule::load(QXmlStreamReader &reader)
-{
- Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement);
-
- m_attribute = reader.attributes().value(QStringLiteral("attribute")).toString();
- if (reader.name() != QLatin1String("IncludeRules")) // IncludeRules uses this with a different semantic
- m_context.parse(reader.attributes().value(QStringLiteral("context")));
- m_firstNonSpace = Xml::attrToBool(reader.attributes().value(QStringLiteral("firstNonSpace")));
- m_lookAhead = Xml::attrToBool(reader.attributes().value(QStringLiteral("lookAhead")));
- bool colOk = false;
- m_column = reader.attributes().value(QStringLiteral("column")).toInt(&colOk);
- if (!colOk)
- m_column = -1;
+Rule::~Rule() = default;
- auto regionName = reader.attributes().value(QLatin1String("beginRegion"));
- if (!regionName.isEmpty())
- m_beginRegion = FoldingRegion(FoldingRegion::Begin, DefinitionData::get(m_def.definition())->foldingRegionId(regionName.toString()));
- regionName = reader.attributes().value(QLatin1String("endRegion"));
- if (!regionName.isEmpty())
- m_endRegion = FoldingRegion(FoldingRegion::End, DefinitionData::get(m_def.definition())->foldingRegionId(regionName.toString()));
-
- auto result = doLoad(reader);
-
- if (m_lookAhead && m_context.isStay())
- result = false;
-
- // be done with this rule, skip all subelements, e.g. no longer supported sub-rules
- reader.skipCurrentElement();
- return result;
-}
-
-void Rule::resolveContext()
+const IncludeRules *Rule::castToIncludeRules() const
{
- m_context.resolve(m_def.definition());
-}
+ if (m_type != Type::IncludeRules) {
+ return nullptr;
+ }
+ return static_cast<const IncludeRules *>(this);
+}
+
+bool Rule::resolveCommon(DefinitionData &def, const HighlightingContextData::Rule &ruleData, QStringView lookupContextName)
+{
+ switch (ruleData.type) {
+ // IncludeRules uses this with a different semantic
+ case HighlightingContextData::Rule::Type::IncludeRules:
+ m_type = Type::IncludeRules;
+ return true;
+ case HighlightingContextData::Rule::Type::LineContinue:
+ m_type = Type::LineContinue;
+ break;
+ default:
+ m_type = Type::OtherRule;
+ break;
+ }
-void Rule::resolveAttributeFormat(Context *lookupContext)
-{
/**
* try to get our format from the definition we stem from
*/
- if (!m_attribute.isEmpty()) {
- m_attributeFormat = DefinitionData::get(definition())->formatByName(m_attribute);
+ if (!ruleData.common.attributeName.isEmpty()) {
+ m_attributeFormat = def.formatByName(ruleData.common.attributeName);
if (!m_attributeFormat.isValid()) {
- qCWarning(Log) << "Rule: Unknown format" << m_attribute << "in context" << lookupContext->name() << "of definition" << definition().name();
+ qCWarning(Log) << "Rule: Unknown format" << ruleData.common.attributeName << "in context" << lookupContextName << "of definition" << def.name;
}
}
-}
-bool Rule::doLoad(QXmlStreamReader& reader)
-{
- Q_UNUSED(reader);
- return true;
-}
+ m_firstNonSpace = ruleData.common.firstNonSpace;
+ m_lookAhead = ruleData.common.lookAhead;
+ m_column = ruleData.common.column;
-Rule::Ptr Rule::create(const QStringRef& name)
-{
- Rule *rule = nullptr;
- if (name == QLatin1String("AnyChar"))
- rule = new AnyChar;
- else if (name == QLatin1String("DetectChar"))
- rule = new DetectChar;
- else if (name == QLatin1String("Detect2Chars"))
- rule = new Detect2Char;
- else if (name == QLatin1String("DetectIdentifier"))
- rule = new DetectIdentifier;
- else if (name == QLatin1String("DetectSpaces"))
- rule = new DetectSpaces;
- else if (name == QLatin1String("Float"))
- rule = new Float;
- else if (name == QLatin1String("Int"))
- rule = new Int;
- else if (name == QLatin1String("HlCChar"))
- rule = new HlCChar;
- else if (name == QLatin1String("HlCHex"))
- rule = new HlCHex;
- else if (name == QLatin1String("HlCOct"))
- rule = new HlCOct;
- else if (name == QLatin1String("HlCStringChar"))
- rule = new HlCStringChar;
- else if (name == QLatin1String("IncludeRules"))
- rule = new IncludeRules;
- else if (name == QLatin1String("keyword"))
- rule = new KeywordListRule;
- else if (name == QLatin1String("LineContinue"))
- rule = new LineContinue;
- else if (name == QLatin1String("RangeDetect"))
- rule = new RangeDetect;
- else if (name == QLatin1String("RegExpr"))
- rule = new RegExpr;
- else if (name == QLatin1String("StringDetect"))
- rule = new StringDetect;
- else if (name == QLatin1String("WordDetect"))
- rule = new WordDetect;
- else
- qCWarning(Log) << "Unknown rule type:" << name;
+ if (!ruleData.common.beginRegionName.isEmpty()) {
+ m_beginRegion = FoldingRegion(FoldingRegion::Begin, def.foldingRegionId(ruleData.common.beginRegionName));
+ }
+ if (!ruleData.common.endRegionName.isEmpty()) {
+ m_endRegion = FoldingRegion(FoldingRegion::End, def.foldingRegionId(ruleData.common.endRegionName));
+ }
- return Ptr(rule);
+ m_context.resolve(def, ruleData.common.contextName);
+
+ return !(m_lookAhead && m_context.isStay());
+}
+
+static Rule::Ptr createRule(DefinitionData &def, const HighlightingContextData::Rule &ruleData, QStringView lookupContextName)
+{
+ using Type = HighlightingContextData::Rule::Type;
+
+ switch (ruleData.type) {
+ case Type::AnyChar:
+ return std::make_shared<AnyChar>(ruleData.data.anyChar);
+ case Type::DetectChar:
+ return std::make_shared<DetectChar>(ruleData.data.detectChar);
+ case Type::Detect2Chars:
+ return std::make_shared<Detect2Chars>(ruleData.data.detect2Chars);
+ case Type::IncludeRules:
+ return std::make_shared<IncludeRules>(ruleData.data.includeRules);
+ case Type::Int:
+ return std::make_shared<Int>(def, ruleData.data.detectInt);
+ case Type::Keyword:
+ return KeywordListRule::create(def, ruleData.data.keyword, lookupContextName);
+ case Type::LineContinue:
+ return std::make_shared<LineContinue>(ruleData.data.lineContinue);
+ case Type::RangeDetect:
+ return std::make_shared<RangeDetect>(ruleData.data.rangeDetect);
+ case Type::RegExpr:
+ if (!ruleData.data.regExpr.dynamic) {
+ return std::make_shared<RegExpr>(ruleData.data.regExpr);
+ } else {
+ return std::make_shared<DynamicRegExpr>(ruleData.data.regExpr);
+ }
+ case Type::StringDetect:
+ if (ruleData.data.stringDetect.dynamic) {
+ return std::make_shared<DynamicStringDetect>(ruleData.data.stringDetect);
+ }
+ return std::make_shared<StringDetect>(ruleData.data.stringDetect);
+ case Type::WordDetect:
+ return std::make_shared<WordDetect>(def, ruleData.data.wordDetect);
+ case Type::Float:
+ return std::make_shared<Float>(def, ruleData.data.detectFloat);
+ case Type::HlCOct:
+ return std::make_shared<HlCOct>(def, ruleData.data.hlCOct);
+ case Type::HlCStringChar:
+ return std::make_shared<HlCStringChar>();
+ case Type::DetectIdentifier:
+ return std::make_shared<DetectIdentifier>();
+ case Type::DetectSpaces:
+ return std::make_shared<DetectSpaces>();
+ case Type::HlCChar:
+ return std::make_shared<HlCChar>();
+ case Type::HlCHex:
+ return std::make_shared<HlCHex>(def, ruleData.data.hlCHex);
+
+ case Type::Unknown:;
+ }
+
+ return Rule::Ptr(nullptr);
}
-bool Rule::isWordDelimiter(QChar c) const
+Rule::Ptr Rule::create(DefinitionData &def, const HighlightingContextData::Rule &ruleData, QStringView lookupContextName)
{
- // perf tells contains is MUCH faster than binary search here, very short array
- return m_wordDelimiter.contains(c);
+ auto rule = createRule(def, ruleData, lookupContextName);
+ if (rule && !rule->resolveCommon(def, ruleData, lookupContextName)) {
+ rule.reset();
+ }
+ return rule;
}
-
-bool AnyChar::doLoad(QXmlStreamReader& reader)
+AnyChar::AnyChar(const HighlightingContextData::Rule::AnyChar &data)
+ : m_chars(data.chars)
{
- m_chars = reader.attributes().value(QStringLiteral("String")).toString();
- if (m_chars.size() == 1)
- qCDebug(Log) << "AnyChar rule with just one char: use DetectChar instead.";
- return !m_chars.isEmpty();
}
-MatchResult AnyChar::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult AnyChar::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- if (m_chars.contains(text.at(offset)))
+ if (m_chars.contains(text.at(offset))) {
return offset + 1;
+ }
return offset;
}
-
-bool DetectChar::doLoad(QXmlStreamReader& reader)
+DetectChar::DetectChar(const HighlightingContextData::Rule::DetectChar &data)
+ : m_char(data.char1)
+ , m_captureIndex((data.dynamic ? data.char1.digitValue() : 0) - 1)
{
- const auto s = reader.attributes().value(QStringLiteral("char"));
- if (s.isEmpty())
- return false;
- m_char = s.at(0);
- m_dynamic = Xml::attrToBool(reader.attributes().value(QStringLiteral("dynamic")));
- if (m_dynamic) {
- m_captureIndex = m_char.digitValue();
- }
- return true;
+ m_dynamic = data.dynamic;
}
-MatchResult DetectChar::doMatch(const QString& text, int offset, const QStringList &captures) const
+MatchResult DetectChar::doMatch(QStringView text, int offset, const QStringList &captures, DynamicRegexpCache &) const
{
if (m_dynamic) {
- if (m_captureIndex == 0 || captures.size() <= m_captureIndex || captures.at(m_captureIndex).isEmpty())
+ if (m_captureIndex == -1 || captures.size() <= m_captureIndex || captures.at(m_captureIndex).isEmpty()) {
return offset;
- if (text.at(offset) == captures.at(m_captureIndex).at(0))
+ }
+ if (text.at(offset) == captures.at(m_captureIndex).at(0)) {
return offset + 1;
+ }
return offset;
}
- if (text.at(offset) == m_char)
+ if (text.at(offset) == m_char) {
return offset + 1;
+ }
return offset;
}
-
-bool Detect2Char::doLoad(QXmlStreamReader& reader)
+Detect2Chars::Detect2Chars(const HighlightingContextData::Rule::Detect2Chars &data)
+ : m_char1(data.char1)
+ , m_char2(data.char2)
{
- const auto s1 = reader.attributes().value(QStringLiteral("char"));
- const auto s2 = reader.attributes().value(QStringLiteral("char1"));
- if (s1.isEmpty() || s2.isEmpty())
- return false;
- m_char1 = s1.at(0);
- m_char2 = s2.at(0);
- return true;
}
-MatchResult Detect2Char::doMatch(const QString& text, int offset, const QStringList &) const
+MatchResult Detect2Chars::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- if (text.size() - offset < 2)
+ if (text.size() - offset < 2) {
return offset;
- if (text.at(offset) == m_char1 && text.at(offset + 1) == m_char2)
+ }
+ if (text.at(offset) == m_char1 && text.at(offset + 1) == m_char2) {
return offset + 2;
+ }
return offset;
}
-
-MatchResult DetectIdentifier::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult DetectIdentifier::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- if (!text.at(offset).isLetter() && text.at(offset) != QLatin1Char('_'))
+ if (!text.at(offset).isLetter() && text.at(offset) != QLatin1Char('_')) {
return offset;
+ }
for (int i = offset + 1; i < text.size(); ++i) {
const auto c = text.at(i);
- if (!c.isLetterOrNumber() && c != QLatin1Char('_'))
+ if (!c.isLetterOrNumber() && c != QLatin1Char('_')) {
return i;
+ }
}
return text.size();
}
-
-MatchResult DetectSpaces::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult DetectSpaces::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- while(offset < text.size() && text.at(offset).isSpace())
+ while (offset < text.size() && text.at(offset).isSpace()) {
++offset;
+ }
return offset;
}
+Float::Float(DefinitionData &def, const HighlightingContextData::Rule::Float &data)
+ : m_wordDelimiters(def.wordDelimiters)
+{
+ resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters);
+}
-MatchResult Float::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult Float::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- if (offset > 0 && !isWordDelimiter(text.at(offset - 1)))
+ if (offset > 0 && !m_wordDelimiters.contains(text.at(offset - 1))) {
return offset;
+ }
auto newOffset = offset;
- while (newOffset < text.size() && text.at(newOffset).isDigit())
+ while (newOffset < text.size() && isDigit(text.at(newOffset))) {
++newOffset;
+ }
- if (newOffset >= text.size() || text.at(newOffset) != QLatin1Char('.'))
+ if (newOffset >= text.size() || text.at(newOffset) != QLatin1Char('.')) {
return offset;
+ }
++newOffset;
- while (newOffset < text.size() && text.at(newOffset).isDigit())
+ while (newOffset < text.size() && isDigit(text.at(newOffset))) {
++newOffset;
+ }
- if (newOffset == offset + 1) // we only found a decimal point
+ if (newOffset == offset + 1) { // we only found a decimal point
return offset;
+ }
auto expOffset = newOffset;
- if (expOffset >= text.size() || (text.at(expOffset) != QLatin1Char('e') && text.at(expOffset) != QLatin1Char('E')))
+ if (expOffset >= text.size() || (text.at(expOffset) != QLatin1Char('e') && text.at(expOffset) != QLatin1Char('E'))) {
return newOffset;
+ }
++expOffset;
- if (expOffset < text.size() && (text.at(expOffset) == QLatin1Char('+') || text.at(expOffset) == QLatin1Char('-')))
+ if (expOffset < text.size() && (text.at(expOffset) == QLatin1Char('+') || text.at(expOffset) == QLatin1Char('-'))) {
++expOffset;
+ }
bool foundExpDigit = false;
- while (expOffset < text.size() && text.at(expOffset).isDigit()) {
+ while (expOffset < text.size() && isDigit(text.at(expOffset))) {
++expOffset;
foundExpDigit = true;
}
- if (!foundExpDigit)
+ if (!foundExpDigit) {
return newOffset;
+ }
return expOffset;
}
-
-MatchResult HlCChar::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult HlCChar::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- if (text.size() < offset + 3)
+ if (text.size() < offset + 3) {
return offset;
+ }
- if (text.at(offset) != QLatin1Char('\'') || text.at(offset + 1) == QLatin1Char('\''))
+ if (text.at(offset) != QLatin1Char('\'') || text.at(offset + 1) == QLatin1Char('\'')) {
return offset;
+ }
auto newOffset = matchEscapedChar(text, offset + 1);
if (newOffset == offset + 1) {
- if (text.at(newOffset) == QLatin1Char('\\'))
+ if (text.at(newOffset) == QLatin1Char('\\')) {
return offset;
- else
+ } else {
++newOffset;
+ }
}
- if (newOffset >= text.size())
+ if (newOffset >= text.size()) {
return offset;
+ }
- if (text.at(newOffset) == QLatin1Char('\''))
+ if (text.at(newOffset) == QLatin1Char('\'')) {
return newOffset + 1;
+ }
return offset;
}
+HlCHex::HlCHex(DefinitionData &def, const HighlightingContextData::Rule::HlCHex &data)
+ : m_wordDelimiters(def.wordDelimiters)
+{
+ resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters);
+}
-MatchResult HlCHex::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult HlCHex::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- if (offset > 0 && !isWordDelimiter(text.at(offset - 1)))
+ if (offset > 0 && !m_wordDelimiters.contains(text.at(offset - 1))) {
return offset;
+ }
- if (text.size() < offset + 3)
+ if (text.size() < offset + 3) {
return offset;
+ }
- if (text.at(offset) != QLatin1Char('0') || (text.at(offset + 1) != QLatin1Char('x') && text.at(offset + 1) != QLatin1Char('X')))
+ if (text.at(offset) != QLatin1Char('0') || (text.at(offset + 1) != QLatin1Char('x') && text.at(offset + 1) != QLatin1Char('X'))) {
return offset;
+ }
- if (!isHexChar(text.at(offset + 2)))
+ if (!isHexChar(text.at(offset + 2))) {
return offset;
+ }
offset += 3;
- while (offset < text.size() && isHexChar(text.at(offset)))
+ while (offset < text.size() && isHexChar(text.at(offset))) {
++offset;
+ }
// TODO Kate matches U/L suffix, QtC does not?
return offset;
}
+HlCOct::HlCOct(DefinitionData &def, const HighlightingContextData::Rule::HlCOct &data)
+ : m_wordDelimiters(def.wordDelimiters)
+{
+ resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters);
+}
-MatchResult HlCOct::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult HlCOct::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- if (offset > 0 && !isWordDelimiter(text.at(offset - 1)))
+ if (offset > 0 && !m_wordDelimiters.contains(text.at(offset - 1))) {
return offset;
+ }
- if (text.size() < offset + 2)
+ if (text.size() < offset + 2) {
return offset;
+ }
- if (text.at(offset) != QLatin1Char('0'))
+ if (text.at(offset) != QLatin1Char('0')) {
return offset;
+ }
- if (!isOctalChar(text.at(offset + 1)))
+ if (!isOctalChar(text.at(offset + 1))) {
return offset;
+ }
offset += 2;
- while (offset < text.size() && isOctalChar(text.at(offset)))
+ while (offset < text.size() && isOctalChar(text.at(offset))) {
++offset;
+ }
return offset;
}
-
-MatchResult HlCStringChar::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult HlCStringChar::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
return matchEscapedChar(text, offset);
}
-
-QString IncludeRules::contextName() const
+IncludeRules::IncludeRules(const HighlightingContextData::Rule::IncludeRules &data)
+ : m_contextName(data.contextName)
+ , m_includeAttribute(data.includeAttribute)
{
- return m_contextName;
}
-QString IncludeRules::definitionName() const
-{
- return m_defName;
-}
-
-bool IncludeRules::includeAttribute() const
-{
- return m_includeAttribute;
-}
-
-bool IncludeRules::doLoad(QXmlStreamReader& reader)
-{
- const auto s = reader.attributes().value(QLatin1String("context"));
- const auto split = s.split(QLatin1String("##"), QString::KeepEmptyParts);
- if (split.isEmpty())
- return false;
- m_contextName = split.at(0).toString();
- if (split.size() > 1)
- m_defName = split.at(1).toString();
- m_includeAttribute = Xml::attrToBool(reader.attributes().value(QLatin1String("includeAttrib")));
-
- return !m_contextName.isEmpty() || !m_defName.isEmpty();
-}
-
-MatchResult IncludeRules::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult IncludeRules::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
Q_UNUSED(text);
- qCWarning(Log) << "Unresolved include rule for" << m_contextName << "##" << m_defName;
+ qCWarning(Log) << "Unresolved include rule";
return offset;
}
+Int::Int(DefinitionData &def, const HighlightingContextData::Rule::Int &data)
+ : m_wordDelimiters(def.wordDelimiters)
+{
+ resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters);
+}
-MatchResult Int::doMatch(const QString& text, int offset, const QStringList &) const
+MatchResult Int::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- if (offset > 0 && !isWordDelimiter(text.at(offset - 1)))
+ if (offset > 0 && !m_wordDelimiters.contains(text.at(offset - 1))) {
return offset;
+ }
- while(offset < text.size() && text.at(offset).isDigit())
+ while (offset < text.size() && isDigit(text.at(offset))) {
++offset;
+ }
return offset;
}
-
-bool KeywordListRule::doLoad(QXmlStreamReader& reader)
+Rule::Ptr KeywordListRule::create(DefinitionData &def, const HighlightingContextData::Rule::Keyword &data, QStringView lookupContextName)
{
/**
* get our keyword list, if not found => bail out
*/
- auto defData = DefinitionData::get(definition());
- m_keywordList = defData->keywordList(reader.attributes().value(QLatin1String("String")).toString());
- if (!m_keywordList) {
- return false;
+ auto *keywordList = def.keywordList(data.name);
+ if (!keywordList) {
+ qCWarning(Log) << "Rule: Unknown keyword list" << data.name << "in context" << lookupContextName << "of definition" << def.name;
+ return Rule::Ptr();
+ }
+
+ if (keywordList->isEmpty()) {
+ return Rule::Ptr();
}
/**
* we might overwrite the case sensitivity
* then we need to init the list for lookup of that sensitivity setting
*/
- if (reader.attributes().hasAttribute(QLatin1String("insensitive"))) {
- m_hasCaseSensitivityOverride = true;
- m_caseSensitivityOverride = Xml::attrToBool(reader.attributes().value(QLatin1String("insensitive"))) ?
- Qt::CaseInsensitive : Qt::CaseSensitive;
- m_keywordList->initLookupForCaseSensitivity(m_caseSensitivityOverride);
- } else {
- m_hasCaseSensitivityOverride = false;
+ if (data.hasCaseSensitivityOverride) {
+ keywordList->initLookupForCaseSensitivity(data.caseSensitivityOverride);
}
- return !m_keywordList->isEmpty();
+ return std::make_shared<KeywordListRule>(*keywordList, def, data);
+}
+
+KeywordListRule::KeywordListRule(const KeywordList &keywordList, DefinitionData &def, const HighlightingContextData::Rule::Keyword &data)
+ : m_wordDelimiters(def.wordDelimiters)
+ , m_keywordList(keywordList)
+ , m_caseSensitivity(data.hasCaseSensitivityOverride ? data.caseSensitivityOverride : keywordList.caseSensitivity())
+{
+ resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters);
+ m_hasSkipOffset = true;
}
-MatchResult KeywordListRule::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult KeywordListRule::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
auto newOffset = offset;
- while (text.size() > newOffset && !isWordDelimiter(text.at(newOffset)))
+ while (text.size() > newOffset && !m_wordDelimiters.contains(text.at(newOffset))) {
++newOffset;
- if (newOffset == offset)
+ }
+ if (newOffset == offset) {
return offset;
+ }
- if (m_hasCaseSensitivityOverride) {
- if (m_keywordList->contains(text.midRef(offset, newOffset - offset), m_caseSensitivityOverride))
- return newOffset;
- } else {
- if (m_keywordList->contains(text.midRef(offset, newOffset - offset)))
- return newOffset;
+ if (m_keywordList.contains(text.mid(offset, newOffset - offset), m_caseSensitivity)) {
+ return newOffset;
}
// we don't match, but we can skip until newOffset as we can't start a keyword in-between
return MatchResult(offset, newOffset);
}
-
-bool LineContinue::doLoad(QXmlStreamReader& reader)
+LineContinue::LineContinue(const HighlightingContextData::Rule::LineContinue &data)
+ : m_char(data.char1)
{
- const auto s = reader.attributes().value(QStringLiteral("char"));
- if (s.isEmpty())
- m_char = QLatin1Char('\\');
- else
- m_char = s.at(0);
- return true;
}
-MatchResult LineContinue::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult LineContinue::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- if (offset == text.size() - 1 && text.at(offset) == m_char)
+ if (offset == text.size() - 1 && text.at(offset) == m_char) {
return offset + 1;
+ }
return offset;
}
-
-bool RangeDetect::doLoad(QXmlStreamReader& reader)
+RangeDetect::RangeDetect(const HighlightingContextData::Rule::RangeDetect &data)
+ : m_begin(data.begin)
+ , m_end(data.end)
{
- const auto s1 = reader.attributes().value(QStringLiteral("char"));
- const auto s2 = reader.attributes().value(QStringLiteral("char1"));
- if (s1.isEmpty() || s2.isEmpty())
- return false;
- m_begin = s1.at(0);
- m_end = s2.at(0);
- return true;
}
-MatchResult RangeDetect::doMatch(const QString& text, int offset, const QStringList&) const
+MatchResult RangeDetect::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- if (text.size() - offset < 2)
+ if (text.size() - offset < 2) {
return offset;
- if (text.at(offset) != m_begin)
+ }
+ if (text.at(offset) != m_begin) {
return offset;
+ }
auto newOffset = offset + 1;
while (newOffset < text.size()) {
- if (text.at(newOffset) == m_end)
+ if (text.at(newOffset) == m_end) {
return newOffset + 1;
+ }
++newOffset;
}
return offset;
}
-bool RegExpr::doLoad(QXmlStreamReader& reader)
+static QRegularExpression::PatternOptions makePattenOptions(const HighlightingContextData::Rule::RegExpr &data)
{
- m_regexp.setPattern(reader.attributes().value(QStringLiteral("String")).toString());
+ return (data.isMinimal ? QRegularExpression::InvertedGreedinessOption : QRegularExpression::NoPatternOption)
+ | (data.caseSensitivity == Qt::CaseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption)
+ // DontCaptureOption is removed by resolve() when necessary
+ | QRegularExpression::DontCaptureOption
+ // ensure Unicode support is enabled
+ | QRegularExpression::UseUnicodePropertiesOption;
+}
- const auto isMinimal = Xml::attrToBool(reader.attributes().value(QStringLiteral("minimal")));
- const auto isCaseInsensitive = Xml::attrToBool(reader.attributes().value(QStringLiteral("insensitive")));
- m_regexp.setPatternOptions(
- (isMinimal ? QRegularExpression::InvertedGreedinessOption : QRegularExpression::NoPatternOption) |
- (isCaseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption));
+static void resolveRegex(QRegularExpression &regexp, Context *context)
+{
+ bool enableCapture = context && context->hasDynamicRule();
- // optimize the pattern for the non-dynamic case, we use them OFTEN
- m_dynamic = Xml::attrToBool(reader.attributes().value(QStringLiteral("dynamic")));
- if (!m_dynamic) {
- m_regexp.optimize();
+ // disable DontCaptureOption when reference a context with dynamic rule or
+ // with invalid regex because DontCaptureOption with back reference capture is an error
+ if (enableCapture || !regexp.isValid()) {
+ regexp.setPatternOptions(regexp.patternOptions() & ~QRegularExpression::DontCaptureOption);
}
- // always using m_regexp.isValid() would be better, but parses the regexp and thus is way too expensive for release builds
-
- if (Log().isDebugEnabled()) {
- if (!m_regexp.isValid())
- qCDebug(Log) << "Invalid regexp:" << m_regexp.pattern();
+ if (!regexp.isValid()) {
+ qCDebug(Log) << "Invalid regexp:" << regexp.pattern();
}
- return !m_regexp.pattern().isEmpty();
}
-MatchResult RegExpr::doMatch(const QString& text, int offset, const QStringList &captures) const
+static MatchResult regexMatch(const QRegularExpression &regexp, QStringView text, int offset)
{
/**
- * for dynamic case: create new pattern with right instantiation
- */
- const auto &regexp = m_dynamic ? QRegularExpression(replaceCaptures(m_regexp.pattern(), captures, true), m_regexp.patternOptions()) : m_regexp;
-
- /**
* match the pattern
*/
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+ const auto result = regexp.matchView(text, offset, QRegularExpression::NormalMatch, QRegularExpression::DontCheckSubjectStringMatchOption);
+#else
const auto result = regexp.match(text, offset, QRegularExpression::NormalMatch, QRegularExpression::DontCheckSubjectStringMatchOption);
+#endif
if (result.capturedStart() == offset) {
/**
* we only need to compute the captured texts if we have real capture groups
* highlightings should only address %1..%.., see e.g. replaceCaptures
* DetectChar ignores %0, too
*/
- if (result.lastCapturedIndex() > 0) {
- return MatchResult(offset + result.capturedLength(), result.capturedTexts());
+ int lastCapturedIndex = result.lastCapturedIndex();
+ if (lastCapturedIndex > 0) {
+ QStringList captures;
+ captures.reserve(lastCapturedIndex);
+ // ignore the capturing group number 0
+ for (int i = 1; i <= lastCapturedIndex; ++i)
+ captures.push_back(result.captured(i));
+ return MatchResult(offset + result.capturedLength(), std::move(captures));
}
/**
@@ -612,52 +638,121 @@ MatchResult RegExpr::doMatch(const QString& text, int offset, const QStringList
/**
* no match
+ * we can always compute the skip offset as the highlighter will invalidate the cache for changed captures for dynamic rules!
*/
return MatchResult(offset, result.capturedStart());
}
+RegExpr::RegExpr(const HighlightingContextData::Rule::RegExpr &data)
+ : m_regexp(data.pattern, makePattenOptions(data))
+{
+ m_hasSkipOffset = true;
+}
-bool StringDetect::doLoad(QXmlStreamReader& reader)
+void RegExpr::resolve()
{
- m_string = reader.attributes().value(QStringLiteral("String")).toString();
- m_caseSensitivity = Xml::attrToBool(reader.attributes().value(QStringLiteral("insensitive"))) ? Qt::CaseInsensitive : Qt::CaseSensitive;
- m_dynamic = Xml::attrToBool(reader.attributes().value(QStringLiteral("dynamic")));
- return !m_string.isEmpty();
+ m_isResolved = true;
+
+ resolveRegex(m_regexp, context().context());
}
-MatchResult StringDetect::doMatch(const QString& text, int offset, const QStringList &captures) const
+MatchResult RegExpr::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
+ if (Q_UNLIKELY(!m_isResolved)) {
+ const_cast<RegExpr *>(this)->resolve();
+ }
+
+ return regexMatch(m_regexp, text, offset);
+}
+
+DynamicRegExpr::DynamicRegExpr(const HighlightingContextData::Rule::RegExpr &data)
+ : m_pattern(data.pattern)
+ , m_patternOptions(makePattenOptions(data))
+{
+ m_dynamic = true;
+ m_hasSkipOffset = true;
+}
+
+void DynamicRegExpr::resolve()
+{
+ m_isResolved = true;
+
+ QRegularExpression regexp(m_pattern, m_patternOptions);
+ resolveRegex(regexp, context().context());
+ m_patternOptions = regexp.patternOptions();
+}
+
+MatchResult DynamicRegExpr::doMatch(QStringView text, int offset, const QStringList &captures, DynamicRegexpCache &dynamicRegexpCache) const
+{
+ if (Q_UNLIKELY(!m_isResolved)) {
+ const_cast<DynamicRegExpr *>(this)->resolve();
+ }
+
/**
- * for dynamic case: create new pattern with right instantiation
+ * create new pattern with right instantiation
*/
- const auto &pattern = m_dynamic ? replaceCaptures(m_string, captures, false) : m_string;
+ auto pattern = replaceCaptures(m_pattern, captures, true);
+ auto &regexp = dynamicRegexpCache.compileRegexp(std::move(pattern), m_patternOptions);
+ return regexMatch(regexp, text, offset);
+}
- if (text.midRef(offset, pattern.size()).compare(pattern, m_caseSensitivity) == 0)
- return offset + pattern.size();
- return offset;
+StringDetect::StringDetect(const HighlightingContextData::Rule::StringDetect &data)
+ : m_string(data.string)
+ , m_caseSensitivity(data.caseSensitivity)
+{
+}
+
+MatchResult StringDetect::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
+{
+ return matchString(m_string, text, offset, m_caseSensitivity);
}
+DynamicStringDetect::DynamicStringDetect(const HighlightingContextData::Rule::StringDetect &data)
+ : m_string(data.string)
+ , m_caseSensitivity(data.caseSensitivity)
+{
+ m_dynamic = true;
+}
+
+MatchResult DynamicStringDetect::doMatch(QStringView text, int offset, const QStringList &captures, DynamicRegexpCache &) const
+{
+ /**
+ * for dynamic case: create new pattern with right instantiation
+ */
+ const auto pattern = replaceCaptures(m_string, captures, false);
+ return matchString(pattern, text, offset, m_caseSensitivity);
+}
-bool WordDetect::doLoad(QXmlStreamReader& reader)
+WordDetect::WordDetect(DefinitionData &def, const HighlightingContextData::Rule::WordDetect &data)
+ : m_wordDelimiters(def.wordDelimiters)
+ , m_word(data.word)
+ , m_caseSensitivity(data.caseSensitivity)
{
- m_word = reader.attributes().value(QStringLiteral("String")).toString();
- m_caseSensitivity = Xml::attrToBool(reader.attributes().value(QStringLiteral("insensitive"))) ? Qt::CaseInsensitive : Qt::CaseSensitive;
- return !m_word.isEmpty();
+ resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters);
}
-MatchResult WordDetect::doMatch(const QString& text, int offset, const QStringList &) const
+MatchResult WordDetect::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const
{
- if (text.size() - offset < m_word.size())
+ if (text.size() - offset < m_word.size()) {
return offset;
+ }
- if (offset > 0 && !isWordDelimiter(text.at(offset - 1)))
+ /**
+ * detect delimiter characters on the inner and outer boundaries of the string
+ * NOTE: m_word isn't empty
+ */
+ if (offset > 0 && !m_wordDelimiters.contains(text.at(offset - 1)) && !m_wordDelimiters.contains(text.at(offset))) {
return offset;
+ }
- if (text.midRef(offset, m_word.size()).compare(m_word, m_caseSensitivity) != 0)
+ if (text.mid(offset, m_word.size()).compare(m_word, m_caseSensitivity) != 0) {
return offset;
+ }
- if (text.size() == offset + m_word.size() || isWordDelimiter(text.at(offset + m_word.size())))
+ if (text.size() == offset + m_word.size() || m_wordDelimiters.contains(text.at(offset + m_word.size()))
+ || m_wordDelimiters.contains(text.at(offset + m_word.size() - 1))) {
return offset + m_word.size();
+ }
return offset;
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/rule_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/rule_p.h
index 538fdeda8a..bc5f367ad6 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/rule_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/rule_p.h
@@ -1,60 +1,42 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_RULE_P_H
#define KSYNTAXHIGHLIGHTING_RULE_P_H
#include "contextswitch_p.h"
-#include "definition.h"
#include "definitionref_p.h"
#include "foldingregion.h"
#include "format.h"
+#include "highlightingdata_p.hpp"
#include "keywordlist_p.h"
#include "matchresult_p.h"
+#include "worddelimiters_p.h"
#include <QRegularExpression>
#include <QString>
-#include <QVector>
#include <memory>
-QT_BEGIN_NAMESPACE
-class QXmlStreamReader;
-QT_END_NAMESPACE
-
-namespace KSyntaxHighlighting {
+namespace KSyntaxHighlighting
+{
+class WordDelimiters;
+class DefinitionData;
+class IncludeRules;
+class DynamicRegexpCache;
class Rule
{
public:
Rule() = default;
- virtual ~Rule() = default;
+ virtual ~Rule();
typedef std::shared_ptr<Rule> Ptr;
- Definition definition() const;
- void setDefinition(const Definition &def);
-
const Format &attributeFormat() const
{
return m_attributeFormat;
@@ -70,6 +52,11 @@ public:
return m_lookAhead;
}
+ bool isDynamic() const
+ {
+ return m_dynamic;
+ }
+
bool firstNonSpace() const
{
return m_firstNonSpace;
@@ -90,198 +77,290 @@ public:
return m_endRegion;
}
- bool load(QXmlStreamReader &reader);
- void resolveContext();
- void resolveAttributeFormat(Context *lookupContext);
+ const IncludeRules *castToIncludeRules() const;
- virtual MatchResult doMatch(const QString &text, int offset, const QStringList &captures) const = 0;
+ bool isLineContinue() const
+ {
+ return m_type == Type::LineContinue;
+ }
- static Rule::Ptr create(const QStringRef &name);
+ // If true, then the rule uses the skipOffset parameter of MatchResult.
+ // This is used by AbstractHighlighter::highlightLine() to look for a rule
+ // in the skipOffsets cache only if it can be found there.
+ bool hasSkipOffset() const
+ {
+ return m_hasSkipOffset;
+ }
-protected:
- virtual bool doLoad(QXmlStreamReader &reader);
+ virtual MatchResult doMatch(QStringView text, int offset, const QStringList &captures, DynamicRegexpCache &dynamicRegexpCache) const = 0;
- bool isWordDelimiter(QChar c) const;
+ static Rule::Ptr create(DefinitionData &def, const HighlightingContextData::Rule &ruleData, QStringView lookupContextName);
private:
Q_DISABLE_COPY(Rule)
- DefinitionRef m_def;
- QString m_attribute;
+ bool resolveCommon(DefinitionData &def, const HighlightingContextData::Rule &ruleData, QStringView lookupContextName);
+
+ enum class Type : quint8 {
+ OtherRule,
+ LineContinue,
+ IncludeRules,
+ };
+
+private:
Format m_attributeFormat;
ContextSwitch m_context;
int m_column = -1;
FoldingRegion m_beginRegion;
FoldingRegion m_endRegion;
+ Type m_type;
bool m_firstNonSpace = false;
bool m_lookAhead = false;
- // cache for DefinitionData::wordDelimiters, is accessed VERY often
- QStringRef m_wordDelimiter;
+protected:
+ bool m_hasSkipOffset = false;
+ bool m_dynamic = false;
};
-
-class AnyChar : public Rule
+class AnyChar final : public Rule
{
+public:
+ AnyChar(const HighlightingContextData::Rule::AnyChar &data);
+
protected:
- bool doLoad(QXmlStreamReader & reader) override;
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
private:
- QString m_chars;
+ WordDelimiters m_chars;
};
-class DetectChar : public Rule
+class DetectChar final : public Rule
{
+public:
+ DetectChar(const HighlightingContextData::Rule::DetectChar &data);
+
protected:
- bool doLoad(QXmlStreamReader & reader) override;
- MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
private:
QChar m_char;
- bool m_dynamic = false;
int m_captureIndex = 0;
};
-class Detect2Char : public Rule
+class Detect2Chars final : public Rule
{
+public:
+ Detect2Chars(const HighlightingContextData::Rule::Detect2Chars &data);
+
protected:
- bool doLoad(QXmlStreamReader & reader) override;
- MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
private:
QChar m_char1;
QChar m_char2;
};
-class DetectIdentifier : public Rule
+class DetectIdentifier final : public Rule
{
protected:
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
};
-class DetectSpaces : public Rule
+class DetectSpaces final : public Rule
{
protected:
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
};
-class Float : public Rule
+class Float final : public Rule
{
+public:
+ Float(DefinitionData &def, const HighlightingContextData::Rule::Float &data);
+
protected:
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
+
+private:
+ WordDelimiters m_wordDelimiters;
};
-class IncludeRules : public Rule
+class IncludeRules final : public Rule
{
public:
- QString contextName() const;
- QString definitionName() const;
- bool includeAttribute() const;
+ IncludeRules(const HighlightingContextData::Rule::IncludeRules &data);
+
+ const QString &contextName() const
+ {
+ return m_contextName;
+ }
+
+ bool includeAttribute() const
+ {
+ return m_includeAttribute;
+ }
protected:
- bool doLoad(QXmlStreamReader & reader) override;
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
private:
QString m_contextName;
- QString m_defName;
bool m_includeAttribute;
};
-class Int : public Rule
+class Int final : public Rule
{
+public:
+ Int(DefinitionData &def, const HighlightingContextData::Rule::Int &data);
+
protected:
- MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
+
+private:
+ WordDelimiters m_wordDelimiters;
};
-class HlCChar : public Rule
+class HlCChar final : public Rule
{
protected:
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
};
-class HlCHex : public Rule
+class HlCHex final : public Rule
{
+public:
+ HlCHex(DefinitionData &def, const HighlightingContextData::Rule::HlCHex &data);
+
protected:
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
+
+private:
+ WordDelimiters m_wordDelimiters;
};
-class HlCOct : public Rule
+class HlCOct final : public Rule
{
+public:
+ HlCOct(DefinitionData &def, const HighlightingContextData::Rule::HlCOct &data);
+
protected:
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
+
+private:
+ WordDelimiters m_wordDelimiters;
};
-class HlCStringChar : public Rule
+class HlCStringChar final : public Rule
{
protected:
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
};
-class KeywordListRule : public Rule
+class KeywordListRule final : public Rule
{
+public:
+ KeywordListRule(const KeywordList &keywordList, DefinitionData &def, const HighlightingContextData::Rule::Keyword &data);
+
+ static Rule::Ptr create(DefinitionData &def, const HighlightingContextData::Rule::Keyword &data, QStringView lookupContextName);
+
protected:
- bool doLoad(QXmlStreamReader & reader) override;
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
private:
- KeywordList *m_keywordList;
- bool m_hasCaseSensitivityOverride;
- Qt::CaseSensitivity m_caseSensitivityOverride;
+ WordDelimiters m_wordDelimiters;
+ const KeywordList &m_keywordList;
+ Qt::CaseSensitivity m_caseSensitivity;
};
-class LineContinue : public Rule
+class LineContinue final : public Rule
{
+public:
+ LineContinue(const HighlightingContextData::Rule::LineContinue &data);
+
protected:
- bool doLoad(QXmlStreamReader & reader) override;
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
private:
QChar m_char;
};
-class RangeDetect : public Rule
+class RangeDetect final : public Rule
{
+public:
+ RangeDetect(const HighlightingContextData::Rule::RangeDetect &data);
+
protected:
- bool doLoad(QXmlStreamReader & reader) override;
- MatchResult doMatch(const QString & text, int offset, const QStringList&) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
private:
QChar m_begin;
QChar m_end;
};
-class RegExpr : public Rule
+class RegExpr final : public Rule
{
+public:
+ RegExpr(const HighlightingContextData::Rule::RegExpr &data);
+
protected:
- bool doLoad(QXmlStreamReader & reader) override;
- MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
private:
+ void resolve();
QRegularExpression m_regexp;
- bool m_dynamic = false;
+ bool m_isResolved = false;
+};
+
+class DynamicRegExpr final : public Rule
+{
+public:
+ DynamicRegExpr(const HighlightingContextData::Rule::RegExpr &data);
+
+protected:
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
+
+private:
+ void resolve();
+ QString m_pattern;
+ QRegularExpression::PatternOptions m_patternOptions;
+ bool m_isResolved = false;
};
-class StringDetect : public Rule
+class StringDetect final : public Rule
{
+public:
+ StringDetect(const HighlightingContextData::Rule::StringDetect &data);
+
protected:
- bool doLoad(QXmlStreamReader & reader) override;
- MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
+
+private:
+ QString m_string;
+ Qt::CaseSensitivity m_caseSensitivity;
+};
+
+class DynamicStringDetect final : public Rule
+{
+public:
+ DynamicStringDetect(const HighlightingContextData::Rule::StringDetect &data);
+
+protected:
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
private:
QString m_string;
Qt::CaseSensitivity m_caseSensitivity;
- bool m_dynamic = false;
};
-class WordDetect : public Rule
+class WordDetect final : public Rule
{
+public:
+ WordDetect(DefinitionData &def, const HighlightingContextData::Rule::WordDetect &data);
+
protected:
- bool doLoad(QXmlStreamReader & reader) override;
- MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override;
+ MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override;
private:
+ WordDelimiters m_wordDelimiters;
QString m_word;
Qt::CaseSensitivity m_caseSensitivity;
};
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp
index f970e13f8b..dca58b35b7 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp
@@ -1,25 +1,8 @@
-/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
- Copyright (C) 2018 Christoph Cullmann <cullmann@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+/*
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2018 Christoph Cullmann <cullmann@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#include "state.h"
@@ -31,31 +14,23 @@
using namespace KSyntaxHighlighting;
-StateData* StateData::get(State &state)
-{
- state.d.detach();
- return state.d.data();
-}
-
-bool StateData::isEmpty() const
-{
- return m_contextStack.isEmpty();
-}
-
-void StateData::clear()
+StateData *StateData::reset(State &state)
{
- m_contextStack.clear();
+ auto *p = new StateData();
+ state.d.reset(p);
+ return p;
}
-int StateData::size() const
+StateData *StateData::detach(State &state)
{
- return m_contextStack.size();
+ state.d.detach();
+ return state.d.data();
}
-void StateData::push(Context *context, const QStringList &captures)
+void StateData::push(Context *context, QStringList &&captures)
{
Q_ASSERT(context);
- m_contextStack.push_back(qMakePair(context, captures));
+ m_contextStack.push_back(StackValue{context, std::move(captures)});
}
bool StateData::pop(int popCount)
@@ -66,48 +41,28 @@ bool StateData::pop(int popCount)
}
// keep the initial context alive in any case
- Q_ASSERT(!isEmpty());
- const bool initialContextSurvived = m_contextStack.size() > popCount;
- m_contextStack.resize(std::max(1, m_contextStack.size() - popCount));
+ Q_ASSERT(!m_contextStack.empty());
+ const bool initialContextSurvived = int(m_contextStack.size()) > popCount;
+ m_contextStack.resize(std::max(1, int(m_contextStack.size()) - popCount));
return initialContextSurvived;
}
-Context* StateData::topContext() const
-{
- Q_ASSERT(!isEmpty());
- return m_contextStack.last().first;
-}
+State::State() = default;
-const QStringList &StateData::topCaptures() const
-{
- Q_ASSERT(!isEmpty());
- return m_contextStack.last().second;
-}
+State::State(State &&other) noexcept = default;
-State::State() :
- d(new StateData)
-{
-}
+State::State(const State &other) noexcept = default;
-State::State(const State &other) :
- d(other.d)
-{
-}
+State::~State() = default;
-State::~State()
-{
-}
+State &State::operator=(State &&other) noexcept = default;
-State& State::operator=(const State &other)
-{
- d = other.d;
- return *this;
-}
+State &State::operator=(const State &other) noexcept = default;
bool State::operator==(const State &other) const
{
// use pointer equal as shortcut for shared states
- return (d == other.d) || (d->m_contextStack == other.d->m_contextStack && d->m_defRef == other.d->m_defRef);
+ return (d == other.d) || (d && other.d && d->m_contextStack == other.d->m_contextStack && d->m_defId == other.d->m_defId);
}
bool State::operator!=(const State &other) const
@@ -117,7 +72,13 @@ bool State::operator!=(const State &other) const
bool State::indentationBasedFoldingEnabled() const
{
- if (d->m_contextStack.isEmpty())
+ if (!d || d->m_contextStack.empty()) {
return false;
- return d->m_contextStack.last().first->indentationBasedFoldingEnabled();
+ }
+ return d->m_contextStack.back().context->indentationBasedFoldingEnabled();
+}
+
+std::size_t KSyntaxHighlighting::qHash(const State &state, std::size_t seed)
+{
+ return state.d ? qHashMulti(seed, *state.d) : 0;
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/state.h b/src/libs/3rdparty/syntax-highlighting/src/lib/state.h
index fce4bc71e8..3003a9b7cb 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/state.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/state.h
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_STATE_H
@@ -27,12 +10,15 @@
#include "ksyntaxhighlighting_export.h"
#include <QExplicitlySharedDataPointer>
-#include <QTypeInfo>
-
-namespace KSyntaxHighlighting {
+#include <QHash>
+namespace KSyntaxHighlighting
+{
+class State;
class StateData;
+KSYNTAXHIGHLIGHTING_EXPORT std::size_t qHash(const State &state, std::size_t seed = 0);
+
/** Opaque handle to the state of the highlighting engine.
* This needs to be fed into AbstractHighlighter for every line of text
* and allows concrete highlighter implementations to store state per
@@ -47,9 +33,11 @@ public:
* in a document.
*/
State();
- State(const State &other);
+ State(State &&other) noexcept;
+ State(const State &other) noexcept;
~State();
- State& operator=(const State &rhs);
+ State &operator=(State &&rhs) noexcept;
+ State &operator=(const State &rhs) noexcept;
/** Compares two states for equality.
* For two equal states and identical text input, AbstractHighlighter
@@ -74,13 +62,13 @@ public:
private:
friend class StateData;
+ KSYNTAXHIGHLIGHTING_EXPORT friend std::size_t qHash(const State &, std::size_t);
QExplicitlySharedDataPointer<StateData> d;
};
-
}
QT_BEGIN_NAMESPACE
-Q_DECLARE_TYPEINFO(KSyntaxHighlighting::State, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(KSyntaxHighlighting::State, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
#endif // KSYNTAXHIGHLIGHTING_STATE_H
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h
index a99192b4c6..9971f7f660 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h
@@ -1,57 +1,47 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
- Copyright (C) 2018 Christoph Cullmann <cullmann@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2018 Christoph Cullmann <cullmann@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_STATE_P_H
#define KSYNTAXHIGHLIGHTING_STATE_P_H
+#include <vector>
+
#include <QSharedData>
-#include <QVector>
+#include <QStringList>
#include "definitionref_p.h"
-QT_BEGIN_NAMESPACE
-class QStringList;
-QT_END_NAMESPACE
-
namespace KSyntaxHighlighting
{
-
class Context;
class StateData : public QSharedData
{
friend class State;
friend class AbstractHighlighter;
+ friend std::size_t qHash(const StateData &, std::size_t);
public:
StateData() = default;
- static StateData* get(State &state);
- bool isEmpty() const;
- void clear();
- int size() const;
- void push(Context *context, const QStringList &captures);
+ static StateData *reset(State &state);
+ static StateData *detach(State &state);
+
+ static StateData *get(const State &state)
+ {
+ return state.d.data();
+ }
+
+ std::size_t size() const
+ {
+ return m_contextStack.size();
+ }
+
+ void push(Context *context, QStringList &&captures);
/**
* Pop the number of elements given from the top of the current stack.
@@ -61,21 +51,47 @@ public:
*/
bool pop(int popCount);
- Context* topContext() const;
- const QStringList &topCaptures() const;
+ Context *topContext() const
+ {
+ return m_contextStack.back().context;
+ }
+
+ const QStringList &topCaptures() const
+ {
+ return m_contextStack.back().captures;
+ }
+
+ struct StackValue {
+ Context *context;
+ QStringList captures;
+
+ bool operator==(const StackValue &other) const
+ {
+ return context == other.context && captures == other.captures;
+ }
+ };
private:
/**
- * weak reference to the used definition to filter out invalid states
+ * definition id to filter out invalid states
*/
- DefinitionRef m_defRef;
+ uint64_t m_defId = 0;
/**
* the context stack combines the active context + valid captures
*/
- QVector<QPair<Context *, QStringList>> m_contextStack;
+ std::vector<StackValue> m_contextStack;
};
+inline std::size_t qHash(const StateData::StackValue &stackValue, std::size_t seed = 0)
+{
+ return qHashMulti(seed, stackValue.context, stackValue.captures);
+}
+
+inline std::size_t qHash(const StateData &k, std::size_t seed = 0)
+{
+ return qHashMulti(seed, k.m_defId, qHashRange(k.m_contextStack.begin(), k.m_contextStack.end(), seed));
+}
}
#endif
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp
index 4987dc95f0..70b26a79bf 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp
@@ -1,79 +1,124 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#include "syntaxhighlighter.h"
#include "abstracthighlighter_p.h"
#include "definition.h"
+#include "definition_p.h"
#include "foldingregion.h"
#include "format.h"
+#include "format_p.h"
#include "state.h"
#include "theme.h"
-
-#include <QDebug>
+#include "themedata_p.h"
Q_DECLARE_METATYPE(QTextBlock)
using namespace KSyntaxHighlighting;
-namespace KSyntaxHighlighting {
+namespace KSyntaxHighlighting
+{
class TextBlockUserData : public QTextBlockUserData
{
public:
State state;
- QVector<FoldingRegion> foldingRegions;
+ QList<FoldingRegion> foldingRegions;
};
class SyntaxHighlighterPrivate : public AbstractHighlighterPrivate
{
public:
static FoldingRegion foldingRegion(const QTextBlock &startBlock);
- QVector<FoldingRegion> foldingRegions;
+ void initTextFormat(QTextCharFormat &tf, const Format &format);
+ void computeTextFormats();
+
+ struct TextFormat {
+ QTextCharFormat tf;
+ /**
+ * id to check that the format belongs to the definition
+ */
+ std::intptr_t ptrId;
+ };
+
+ QList<FoldingRegion> foldingRegions;
+ std::vector<TextFormat> tfs;
};
}
-FoldingRegion SyntaxHighlighterPrivate::foldingRegion(const QTextBlock& startBlock)
+FoldingRegion SyntaxHighlighterPrivate::foldingRegion(const QTextBlock &startBlock)
{
- const auto data = dynamic_cast<TextBlockUserData*>(startBlock.userData());
- if (!data)
+ const auto data = dynamic_cast<TextBlockUserData *>(startBlock.userData());
+ if (!data) {
return FoldingRegion();
+ }
for (int i = data->foldingRegions.size() - 1; i >= 0; --i) {
- if (data->foldingRegions.at(i).type() == FoldingRegion::Begin)
+ if (data->foldingRegions.at(i).type() == FoldingRegion::Begin) {
return data->foldingRegions.at(i);
+ }
}
return FoldingRegion();
}
-SyntaxHighlighter::SyntaxHighlighter(QObject* parent) :
- QSyntaxHighlighter(parent),
- AbstractHighlighter(new SyntaxHighlighterPrivate)
+void SyntaxHighlighterPrivate::initTextFormat(QTextCharFormat &tf, const Format &format)
+{
+ // always set the foreground color to avoid palette issues
+ tf.setForeground(format.textColor(m_theme));
+
+ if (format.hasBackgroundColor(m_theme)) {
+ tf.setBackground(format.backgroundColor(m_theme));
+ }
+ if (format.isBold(m_theme)) {
+ tf.setFontWeight(QFont::Bold);
+ }
+ if (format.isItalic(m_theme)) {
+ tf.setFontItalic(true);
+ }
+ if (format.isUnderline(m_theme)) {
+ tf.setFontUnderline(true);
+ }
+ if (format.isStrikeThrough(m_theme)) {
+ tf.setFontStrikeOut(true);
+ }
+}
+
+void SyntaxHighlighterPrivate::computeTextFormats()
+{
+ auto definitions = m_definition.includedDefinitions();
+ definitions.append(m_definition);
+
+ int maxId = 0;
+ for (const auto &definition : std::as_const(definitions)) {
+ for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) {
+ maxId = qMax(maxId, format.id());
+ }
+ }
+ tfs.clear();
+ tfs.resize(maxId + 1);
+
+ // initialize tfs
+ for (const auto &definition : std::as_const(definitions)) {
+ for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) {
+ auto &tf = tfs[format.id()];
+ tf.ptrId = FormatPrivate::ptrId(format);
+ initTextFormat(tf.tf, format);
+ }
+ }
+}
+
+SyntaxHighlighter::SyntaxHighlighter(QObject *parent)
+ : QSyntaxHighlighter(parent)
+ , AbstractHighlighter(new SyntaxHighlighterPrivate)
{
qRegisterMetaType<QTextBlock>();
}
-SyntaxHighlighter::SyntaxHighlighter(QTextDocument *document) :
- QSyntaxHighlighter(document),
- AbstractHighlighter(new SyntaxHighlighterPrivate)
+SyntaxHighlighter::SyntaxHighlighter(QTextDocument *document)
+ : QSyntaxHighlighter(document)
+ , AbstractHighlighter(new SyntaxHighlighterPrivate)
{
qRegisterMetaType<QTextBlock>();
}
@@ -82,12 +127,27 @@ SyntaxHighlighter::~SyntaxHighlighter()
{
}
-void SyntaxHighlighter::setDefinition(const Definition& def)
+void SyntaxHighlighter::setDefinition(const Definition &def)
{
- const auto needsRehighlight = definition() != def;
- AbstractHighlighter::setDefinition(def);
- if (needsRehighlight)
+ Q_D(SyntaxHighlighter);
+
+ const auto needsRehighlight = d->m_definition != def;
+ if (DefinitionData::get(d->m_definition) != DefinitionData::get(def)) {
+ d->m_definition = def;
+ d->tfs.clear();
+ }
+ if (needsRehighlight) {
rehighlight();
+ }
+}
+
+void SyntaxHighlighter::setTheme(const Theme &theme)
+{
+ Q_D(SyntaxHighlighter);
+ if (ThemeData::get(d->m_theme) != ThemeData::get(theme)) {
+ d->m_theme = theme;
+ d->tfs.clear();
+ }
}
bool SyntaxHighlighter::startsFoldingRegion(const QTextBlock &startBlock) const
@@ -103,78 +163,87 @@ QTextBlock SyntaxHighlighter::findFoldingRegionEnd(const QTextBlock &startBlock)
int depth = 1;
while (block.isValid()) {
block = block.next();
- const auto data = dynamic_cast<TextBlockUserData*>(block.userData());
- if (!data)
+ const auto data = dynamic_cast<TextBlockUserData *>(block.userData());
+ if (!data) {
continue;
- for (auto it = data->foldingRegions.constBegin(); it != data->foldingRegions.constEnd(); ++it) {
- if ((*it).id() != region.id())
+ }
+ for (const auto &foldingRegion : std::as_const(data->foldingRegions)) {
+ if (foldingRegion.id() != region.id()) {
continue;
- if ((*it).type() == FoldingRegion::End)
+ }
+ if (foldingRegion.type() == FoldingRegion::End) {
--depth;
- else if ((*it).type() == FoldingRegion::Begin)
+ } else if (foldingRegion.type() == FoldingRegion::Begin) {
++depth;
- if (depth == 0)
+ }
+ if (depth == 0) {
return block;
+ }
}
}
return QTextBlock();
}
-void SyntaxHighlighter::highlightBlock(const QString& text)
+void SyntaxHighlighter::highlightBlock(const QString &text)
{
Q_D(SyntaxHighlighter);
- State state;
+ static const State emptyState;
+ const State *previousState = &emptyState;
if (currentBlock().position() > 0) {
const auto prevBlock = currentBlock().previous();
- const auto prevData = dynamic_cast<TextBlockUserData*>(prevBlock.userData());
- if (prevData)
- state = prevData->state;
+ const auto prevData = dynamic_cast<TextBlockUserData *>(prevBlock.userData());
+ if (prevData) {
+ previousState = &prevData->state;
+ }
}
d->foldingRegions.clear();
- state = highlightLine(text, state);
+ auto newState = highlightLine(text, *previousState);
- auto data = dynamic_cast<TextBlockUserData*>(currentBlockUserData());
+ auto data = dynamic_cast<TextBlockUserData *>(currentBlockUserData());
if (!data) { // first time we highlight this
data = new TextBlockUserData;
- data->state = state;
+ data->state = std::move(newState);
data->foldingRegions = d->foldingRegions;
setCurrentBlockUserData(data);
return;
}
- if (data->state == state && data->foldingRegions == d->foldingRegions) // we ended up in the same state, so we are done here
+ if (data->state == newState && data->foldingRegions == d->foldingRegions) { // we ended up in the same state, so we are done here
return;
- data->state = state;
+ }
+ data->state = std::move(newState);
data->foldingRegions = d->foldingRegions;
const auto nextBlock = currentBlock().next();
- if (nextBlock.isValid())
+ if (nextBlock.isValid()) {
QMetaObject::invokeMethod(this, "rehighlightBlock", Qt::QueuedConnection, Q_ARG(QTextBlock, nextBlock));
+ }
}
-void SyntaxHighlighter::applyFormat(int offset, int length, const KSyntaxHighlighting::Format& format)
+void SyntaxHighlighter::applyFormat(int offset, int length, const Format &format)
{
- if (length == 0)
+ if (length == 0) {
return;
+ }
- QTextCharFormat tf;
- // always set the foreground color to avoid palette issues
- tf.setForeground(format.textColor(theme()));
+ Q_D(SyntaxHighlighter);
- if (format.hasBackgroundColor(theme()))
- tf.setBackground(format.backgroundColor(theme()));
- if (format.isBold(theme()))
- tf.setFontWeight(QFont::Bold);
- if (format.isItalic(theme()))
- tf.setFontItalic(true);
- if (format.isUnderline(theme()))
- tf.setFontUnderline(true);
- if (format.isStrikeThrough(theme()))
- tf.setFontStrikeOut(true);
+ if (Q_UNLIKELY(d->tfs.empty())) {
+ d->computeTextFormats();
+ }
- QSyntaxHighlighter::setFormat(offset, length, tf);
+ const auto id = static_cast<std::size_t>(format.id());
+ // This doesn't happen when format comes from the definition.
+ // But as the user can override the function to pass any format, this is a possible scenario.
+ if (id < d->tfs.size() && d->tfs[id].ptrId == FormatPrivate::ptrId(format)) {
+ QSyntaxHighlighter::setFormat(offset, length, d->tfs[id].tf);
+ } else {
+ QTextCharFormat tf;
+ d->initTextFormat(tf, format);
+ QSyntaxHighlighter::setFormat(offset, length, tf);
+ }
}
void SyntaxHighlighter::applyFolding(int offset, int length, FoldingRegion region)
@@ -183,16 +252,20 @@ void SyntaxHighlighter::applyFolding(int offset, int length, FoldingRegion regio
Q_UNUSED(length);
Q_D(SyntaxHighlighter);
- if (region.type() == FoldingRegion::Begin)
+ if (region.type() == FoldingRegion::Begin) {
d->foldingRegions.push_back(region);
+ }
if (region.type() == FoldingRegion::End) {
for (int i = d->foldingRegions.size() - 1; i >= 0; --i) {
- if (d->foldingRegions.at(i).id() != region.id() || d->foldingRegions.at(i).type() != FoldingRegion::Begin)
+ if (d->foldingRegions.at(i).id() != region.id() || d->foldingRegions.at(i).type() != FoldingRegion::Begin) {
continue;
+ }
d->foldingRegions.remove(i);
return;
}
d->foldingRegions.push_back(region);
}
}
+
+#include "moc_syntaxhighlighter.cpp"
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h
index f5d2a5e219..c19cb798dd 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_QSYNTAXHIGHLIGHTER_H
@@ -30,8 +13,8 @@
#include <QSyntaxHighlighter>
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class SyntaxHighlighterPrivate;
/** A QSyntaxHighlighter implementation for use with QTextDocument.
@@ -49,6 +32,7 @@ public:
~SyntaxHighlighter() override;
void setDefinition(const Definition &def) override;
+ void setTheme(const Theme &theme) override;
/** Returns whether there is a folding region beginning at @p startBlock.
* This only considers syntax-based folding regions,
@@ -73,7 +57,7 @@ public:
QTextBlock findFoldingRegionEnd(const QTextBlock &startBlock) const;
protected:
- void highlightBlock(const QString & text) override;
+ void highlightBlock(const QString &text) override;
void applyFormat(int offset, int length, const Format &format) override;
void applyFolding(int offset, int length, FoldingRegion region) override;
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/textstyledata_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/textstyledata_p.h
index 40c5ef679e..51910bd8be 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/textstyledata_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/textstyledata_p.h
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_TEXTSTYLEDATA_P_H
@@ -26,13 +9,13 @@
#include <QColor>
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class TextStyleData
{
public:
// Constructor initializing all data.
- TextStyleData()
+ TextStyleData() noexcept
: bold(false)
, italic(false)
, underline(false)
@@ -41,21 +24,22 @@ public:
, hasItalic(false)
, hasUnderline(false)
, hasStrikeThrough(false)
- {}
+ {
+ }
QRgb textColor = 0x0;
QRgb backgroundColor = 0x0;
QRgb selectedTextColor = 0x0;
QRgb selectedBackgroundColor = 0x0;
- bool bold :1;
- bool italic :1;
- bool underline :1;
- bool strikeThrough :1;
-
- bool hasBold :1;
- bool hasItalic :1;
- bool hasUnderline :1;
- bool hasStrikeThrough :1;
+ bool bold : 1;
+ bool italic : 1;
+ bool underline : 1;
+ bool strikeThrough : 1;
+
+ bool hasBold : 1;
+ bool hasItalic : 1;
+ bool hasUnderline : 1;
+ bool hasStrikeThrough : 1;
};
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp
index 57f62ef6ab..c54bb38b18 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp
@@ -1,24 +1,8 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2022 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
#include "theme.h"
@@ -28,97 +12,96 @@
using namespace KSyntaxHighlighting;
-Theme::Theme()
+static QExplicitlySharedDataPointer<ThemeData> &sharedDefaultThemeData()
{
+ static QExplicitlySharedDataPointer<ThemeData> data(new ThemeData);
+ return data;
}
-Theme::Theme(const Theme &copy)
+Theme::Theme()
+ : m_data(sharedDefaultThemeData())
{
- m_data = copy.m_data;
}
-Theme::Theme(ThemeData* data)
+Theme::Theme(const Theme &copy) = default;
+
+Theme::Theme(ThemeData *data)
: m_data(data)
{
}
-Theme::~Theme()
-{
-}
+Theme::~Theme() = default;
-Theme &Theme::operator=(const Theme &other)
-{
- m_data = other.m_data;
- return *this;
-}
+Theme &Theme::operator=(const Theme &other) = default;
bool Theme::isValid() const
{
- return m_data.data();
+ return m_data.data() != sharedDefaultThemeData().data();
}
QString Theme::name() const
{
- return m_data ? m_data->name() : QString();
+ return m_data->name();
}
QString Theme::translatedName() const
{
- return m_data ? QCoreApplication::instance()->translate("Theme", m_data->name().toUtf8().constData())
- : QString();
+ return isValid() ? QCoreApplication::instance()->translate("Theme", m_data->name().toUtf8().constData()) : QString();
}
bool Theme::isReadOnly() const
{
- return m_data ? m_data->isReadOnly() : false;
+ return m_data->isReadOnly();
}
QString Theme::filePath() const
{
- return m_data ? m_data->filePath() : QString();
+ return m_data->filePath();
}
QRgb Theme::textColor(TextStyle style) const
{
- return m_data ? m_data->textColor(style) : 0;
+ return m_data->textColor(style);
}
QRgb Theme::selectedTextColor(TextStyle style) const
{
- return m_data ? m_data->selectedTextColor(style) : 0;
+ return m_data->selectedTextColor(style);
}
QRgb Theme::backgroundColor(TextStyle style) const
{
- return m_data ? m_data->backgroundColor(style) : 0;
+ return m_data->backgroundColor(style);
}
QRgb Theme::selectedBackgroundColor(TextStyle style) const
{
- return m_data ? m_data->selectedBackgroundColor(style) : 0;
+ return m_data->selectedBackgroundColor(style);
}
bool Theme::isBold(TextStyle style) const
{
- return m_data ? m_data->isBold(style) : false;
+ return m_data->isBold(style);
}
bool Theme::isItalic(TextStyle style) const
{
- return m_data ? m_data->isItalic(style) : false;
+ return m_data->isItalic(style);
}
bool Theme::isUnderline(TextStyle style) const
{
- return m_data ? m_data->isUnderline(style) : false;
+ return m_data->isUnderline(style);
}
bool Theme::isStrikeThrough(TextStyle style) const
{
- return m_data ? m_data->isStrikeThrough(style) : false;
+ return m_data->isStrikeThrough(style);
}
QRgb Theme::editorColor(EditorColorRole role) const
{
- return m_data ? m_data->editorColor(role) : 0;
+ return m_data->editorColor(role);
}
+
+#include "moc_theme.cpp"
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/theme.h b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.h
index adb8431f6a..c3fb0e6b6e 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/theme.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.h
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_THEME_H
@@ -28,11 +11,11 @@
#include <QColor>
#include <QExplicitlySharedDataPointer>
-#include <qobjectdefs.h>
#include <QTypeInfo>
+#include <qobjectdefs.h>
-namespace KSyntaxHighlighting {
-
+namespace KSyntaxHighlighting
+{
class ThemeData;
class RepositoryPrivate;
@@ -81,13 +64,9 @@ class RepositoryPrivate;
class KSYNTAXHIGHLIGHTING_EXPORT Theme
{
Q_GADGET
+ Q_PROPERTY(QString name READ name)
+ Q_PROPERTY(QString translatedName READ translatedName)
public:
-
- // TODO KF6:
- // - make TextStyle an enum class
- // - move out of Theme into KSyntaxHighlighting
- // - do the same for EditorColorRole
-
/**
* Default styles that can be referenced from syntax definition XML files.
* Make sure to choose readable colors with good contrast especially in
@@ -358,7 +337,7 @@ private:
/**
* Constructor taking a shared ThemeData instance.
*/
- explicit Theme(ThemeData* data);
+ KSYNTAXHIGHLIGHTING_NO_EXPORT explicit Theme(ThemeData *data);
friend class RepositoryPrivate;
friend class ThemeData;
@@ -372,7 +351,7 @@ private:
}
QT_BEGIN_NAMESPACE
-Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Theme, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Theme, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
#endif // KSYNTAXHIGHLIGHTING_THEME_H
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/themedata.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/themedata.cpp
index eac9a92264..9d42d03db0 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/themedata.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/themedata.cpp
@@ -1,29 +1,13 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
- Copyright (C) 2016 Dominik Haumann <dhaumann@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Dominik Haumann <dhaumann@kde.org>
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
*/
-#include "themedata_p.h"
#include "ksyntaxhighlighting_logging.h"
+#include "themedata_p.h"
#include <QFile>
#include <QFileInfo>
@@ -32,23 +16,17 @@
#include <QJsonValue>
#include <QMetaEnum>
-#include <QDebug>
-
using namespace KSyntaxHighlighting;
-ThemeData* ThemeData::get(const Theme &theme)
-{
- return theme.m_data.data();
-}
-
ThemeData::ThemeData()
{
memset(m_editorColors, 0, sizeof(m_editorColors));
+ m_textStyles.resize(QMetaEnum::fromType<Theme::TextStyle>().keyCount());
}
/**
* Convert QJsonValue @p val into a color, if possible. Valid colors are only
- * in hex format: #rrggbb. On error, returns 0x00000000.
+ * in hex format: #aarrggbb. On error, returns 0x00000000.
*/
static inline QRgb readColor(const QJsonValue &val)
{
@@ -61,7 +39,7 @@ static inline QRgb readColor(const QJsonValue &val)
return unsetColor;
}
const QColor color(str);
- return color.isValid() ? color.rgb() : unsetColor;
+ return color.isValid() ? color.rgba() : unsetColor;
}
static inline TextStyleData readThemeData(const QJsonObject &obj)
@@ -104,9 +82,18 @@ bool ThemeData::load(const QString &filePath)
return false;
}
const QByteArray jsonData = loadFile.readAll();
+ // look for metadata object
+ int metaDataStart = jsonData.indexOf("\"metadata\"");
+ int start = jsonData.indexOf('{', metaDataStart);
+ int end = jsonData.indexOf("}", metaDataStart);
+ if (start < 0 || end < 0) {
+ qCWarning(Log) << "Failed to parse theme file" << filePath << ":"
+ << "no metadata object found";
+ return false;
+ }
QJsonParseError parseError;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData.mid(start, (end + 1) - start), &parseError);
if (parseError.error != QJsonParseError::NoError) {
qCWarning(Log) << "Failed to parse theme file" << filePath << ":" << parseError.errorString();
return false;
@@ -114,65 +101,93 @@ bool ThemeData::load(const QString &filePath)
m_filePath = filePath;
- QJsonObject obj = jsonDoc.object();
-
// read metadata
- const QJsonObject metadata = obj.value(QLatin1String("metadata")).toObject();
+ QJsonObject metadata = jsonDoc.object();
m_name = metadata.value(QLatin1String("name")).toString();
m_revision = metadata.value(QLatin1String("revision")).toInt();
+ return true;
+}
+
+void ThemeData::loadComplete()
+{
+ if (m_completelyLoaded) {
+ return;
+ }
+ m_completelyLoaded = true;
+
+ QFile loadFile(m_filePath);
+ if (!loadFile.open(QIODevice::ReadOnly)) {
+ return;
+ }
+ const QByteArray jsonData = loadFile.readAll();
+
+ QJsonParseError parseError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ qCWarning(Log) << "Failed to parse theme file" << m_filePath << ":" << parseError.errorString();
+ return;
+ }
+ QJsonObject obj = jsonDoc.object();
// read text styles
- static const auto idx = Theme::staticMetaObject.indexOfEnumerator("TextStyle");
- Q_ASSERT(idx >= 0);
- const auto metaEnum = Theme::staticMetaObject.enumerator(idx);
+ const auto metaEnumStyle = QMetaEnum::fromType<Theme::TextStyle>();
const QJsonObject textStyles = obj.value(QLatin1String("text-styles")).toObject();
- for (int i = 0; i < metaEnum.keyCount(); ++i) {
- Q_ASSERT(i == metaEnum.value(i));
- m_textStyles[i] = readThemeData(textStyles.value(QLatin1String(metaEnum.key(i))).toObject());
+ for (int i = 0; i < metaEnumStyle.keyCount(); ++i) {
+ Q_ASSERT(i == metaEnumStyle.value(i));
+ m_textStyles[i] = readThemeData(textStyles.value(QLatin1String(metaEnumStyle.key(i))).toObject());
}
- // read editor area colors
+ // read editor colors
+ const auto metaEnumColor = QMetaEnum::fromType<Theme::EditorColorRole>();
const QJsonObject editorColors = obj.value(QLatin1String("editor-colors")).toObject();
- m_editorColors[Theme::BackgroundColor] = readColor(editorColors.value(QLatin1String("background-color")));
- m_editorColors[Theme::TextSelection] = readColor(editorColors.value(QLatin1String("selection")));
- m_editorColors[Theme::CurrentLine] = readColor(editorColors.value(QLatin1String("current-line")));
- m_editorColors[Theme::SearchHighlight] = readColor(editorColors.value(QLatin1String("search-highlight")));
- m_editorColors[Theme::ReplaceHighlight] = readColor(editorColors.value(QLatin1String("replace-highlight")));
- m_editorColors[Theme::BracketMatching] = readColor(editorColors.value(QLatin1String("bracket-matching")));
- m_editorColors[Theme::TabMarker] = readColor(editorColors.value(QLatin1String("tab-marker")));
- m_editorColors[Theme::SpellChecking] = readColor(editorColors.value(QLatin1String("spell-checking")));
- m_editorColors[Theme::IndentationLine] = readColor(editorColors.value(QLatin1String("indentation-line")));
- m_editorColors[Theme::IconBorder] = readColor(editorColors.value(QLatin1String("icon-border")));
- m_editorColors[Theme::CodeFolding] = readColor(editorColors.value(QLatin1String("code-folding")));
- m_editorColors[Theme::LineNumbers] = readColor(editorColors.value(QLatin1String("line-numbers")));
- m_editorColors[Theme::CurrentLineNumber] = readColor(editorColors.value(QLatin1String("current-line-number")));
- m_editorColors[Theme::WordWrapMarker] = readColor(editorColors.value(QLatin1String("word-wrap-marker")));
- m_editorColors[Theme::ModifiedLines] = readColor(editorColors.value(QLatin1String("modified-lines")));
- m_editorColors[Theme::SavedLines] = readColor(editorColors.value(QLatin1String("saved-lines")));
- m_editorColors[Theme::Separator] = readColor(editorColors.value(QLatin1String("separator")));
- m_editorColors[Theme::MarkBookmark] = readColor(editorColors.value(QLatin1String("mark-bookmark")));
- m_editorColors[Theme::MarkBreakpointActive] = readColor(editorColors.value(QLatin1String("mark-breakpoint-active")));
- m_editorColors[Theme::MarkBreakpointReached] = readColor(editorColors.value(QLatin1String("mark-breakpoint-reached")));
- m_editorColors[Theme::MarkBreakpointDisabled] = readColor(editorColors.value(QLatin1String("mark-breakpoint-disabled")));
- m_editorColors[Theme::MarkExecution] = readColor(editorColors.value(QLatin1String("mark-execution")));
- m_editorColors[Theme::MarkWarning] = readColor(editorColors.value(QLatin1String("mark-warning")));
- m_editorColors[Theme::MarkError] = readColor(editorColors.value(QLatin1String("mark-error")));
- m_editorColors[Theme::TemplateBackground] = readColor(editorColors.value(QLatin1String("template-background")));
- m_editorColors[Theme::TemplatePlaceholder] = readColor(editorColors.value(QLatin1String("template-placeholder")));
- m_editorColors[Theme::TemplateFocusedPlaceholder] = readColor(editorColors.value(QLatin1String("template-focused-placeholder")));
- m_editorColors[Theme::TemplateReadOnlyPlaceholder] = readColor(editorColors.value(QLatin1String("template-read-only-placeholder")));
+ for (int i = 0; i < metaEnumColor.keyCount(); ++i) {
+ Q_ASSERT(i == metaEnumColor.value(i));
+ m_editorColors[i] = readColor(editorColors.value(QLatin1String(metaEnumColor.key(i))));
+ }
+
+ // if we have no new key around for Theme::BackgroundColor => use old variants to be compatible
+ if (!editorColors.contains(QLatin1String(metaEnumColor.key(Theme::BackgroundColor)))) {
+ m_editorColors[Theme::BackgroundColor] = readColor(editorColors.value(QLatin1String("background-color")));
+ m_editorColors[Theme::TextSelection] = readColor(editorColors.value(QLatin1String("selection")));
+ m_editorColors[Theme::CurrentLine] = readColor(editorColors.value(QLatin1String("current-line")));
+ m_editorColors[Theme::SearchHighlight] = readColor(editorColors.value(QLatin1String("search-highlight")));
+ m_editorColors[Theme::ReplaceHighlight] = readColor(editorColors.value(QLatin1String("replace-highlight")));
+ m_editorColors[Theme::BracketMatching] = readColor(editorColors.value(QLatin1String("bracket-matching")));
+ m_editorColors[Theme::TabMarker] = readColor(editorColors.value(QLatin1String("tab-marker")));
+ m_editorColors[Theme::SpellChecking] = readColor(editorColors.value(QLatin1String("spell-checking")));
+ m_editorColors[Theme::IndentationLine] = readColor(editorColors.value(QLatin1String("indentation-line")));
+ m_editorColors[Theme::IconBorder] = readColor(editorColors.value(QLatin1String("icon-border")));
+ m_editorColors[Theme::CodeFolding] = readColor(editorColors.value(QLatin1String("code-folding")));
+ m_editorColors[Theme::LineNumbers] = readColor(editorColors.value(QLatin1String("line-numbers")));
+ m_editorColors[Theme::CurrentLineNumber] = readColor(editorColors.value(QLatin1String("current-line-number")));
+ m_editorColors[Theme::WordWrapMarker] = readColor(editorColors.value(QLatin1String("word-wrap-marker")));
+ m_editorColors[Theme::ModifiedLines] = readColor(editorColors.value(QLatin1String("modified-lines")));
+ m_editorColors[Theme::SavedLines] = readColor(editorColors.value(QLatin1String("saved-lines")));
+ m_editorColors[Theme::Separator] = readColor(editorColors.value(QLatin1String("separator")));
+ m_editorColors[Theme::MarkBookmark] = readColor(editorColors.value(QLatin1String("mark-bookmark")));
+ m_editorColors[Theme::MarkBreakpointActive] = readColor(editorColors.value(QLatin1String("mark-breakpoint-active")));
+ m_editorColors[Theme::MarkBreakpointReached] = readColor(editorColors.value(QLatin1String("mark-breakpoint-reached")));
+ m_editorColors[Theme::MarkBreakpointDisabled] = readColor(editorColors.value(QLatin1String("mark-breakpoint-disabled")));
+ m_editorColors[Theme::MarkExecution] = readColor(editorColors.value(QLatin1String("mark-execution")));
+ m_editorColors[Theme::MarkWarning] = readColor(editorColors.value(QLatin1String("mark-warning")));
+ m_editorColors[Theme::MarkError] = readColor(editorColors.value(QLatin1String("mark-error")));
+ m_editorColors[Theme::TemplateBackground] = readColor(editorColors.value(QLatin1String("template-background")));
+ m_editorColors[Theme::TemplatePlaceholder] = readColor(editorColors.value(QLatin1String("template-placeholder")));
+ m_editorColors[Theme::TemplateFocusedPlaceholder] = readColor(editorColors.value(QLatin1String("template-focused-placeholder")));
+ m_editorColors[Theme::TemplateReadOnlyPlaceholder] = readColor(editorColors.value(QLatin1String("template-read-only-placeholder")));
+ }
// read per-definition style overrides
const auto customStyles = obj.value(QLatin1String("custom-styles")).toObject();
for (auto it = customStyles.begin(); it != customStyles.end(); ++it) {
const auto obj = it.value().toObject();
- QHash<QString, TextStyleData> overrideStyle;
- for (auto it2 = obj.begin(); it2 != obj.end(); ++it2)
+ auto &overrideStyle = m_textStyleOverrides[it.key()];
+ for (auto it2 = obj.begin(); it2 != obj.end(); ++it2) {
overrideStyle.insert(it2.key(), readThemeData(it2.value().toObject()));
- m_textStyleOverrides.insert(it.key(), overrideStyle);
+ }
}
- return true;
+ return;
}
QString ThemeData::name() const
@@ -195,61 +210,71 @@ QString ThemeData::filePath() const
return m_filePath;
}
+TextStyleData ThemeData::textStyle(Theme::TextStyle style) const
+{
+ if (!m_completelyLoaded) {
+ const_cast<ThemeData *>(this)->loadComplete();
+ }
+ return m_textStyles[style];
+}
+
QRgb ThemeData::textColor(Theme::TextStyle style) const
{
- Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others));
- return m_textStyles[style].textColor;
+ return textStyle(style).textColor;
}
QRgb ThemeData::selectedTextColor(Theme::TextStyle style) const
{
- Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others));
- return m_textStyles[style].selectedTextColor;
+ return textStyle(style).selectedTextColor;
}
QRgb ThemeData::backgroundColor(Theme::TextStyle style) const
{
- Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others));
- return m_textStyles[style].backgroundColor;
+ return textStyle(style).backgroundColor;
}
QRgb ThemeData::selectedBackgroundColor(Theme::TextStyle style) const
{
- Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others));
- return m_textStyles[style].selectedBackgroundColor;
+ return textStyle(style).selectedBackgroundColor;
}
bool ThemeData::isBold(Theme::TextStyle style) const
{
- Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others));
- return m_textStyles[style].bold;
+ return textStyle(style).bold;
}
bool ThemeData::isItalic(Theme::TextStyle style) const
{
- Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others));
- return m_textStyles[style].italic;
+ return textStyle(style).italic;
}
bool ThemeData::isUnderline(Theme::TextStyle style) const
{
- Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others));
- return m_textStyles[style].underline;
+ return textStyle(style).underline;
}
bool ThemeData::isStrikeThrough(Theme::TextStyle style) const
{
- Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others));
- return m_textStyles[style].strikeThrough;
+ return textStyle(style).strikeThrough;
}
QRgb ThemeData::editorColor(Theme::EditorColorRole role) const
{
+ if (!m_completelyLoaded) {
+ const_cast<ThemeData *>(this)->loadComplete();
+ }
Q_ASSERT(static_cast<int>(role) >= 0 && static_cast<int>(role) <= static_cast<int>(Theme::TemplateReadOnlyPlaceholder));
return m_editorColors[role];
}
-TextStyleData ThemeData::textStyleOverride(const QString& definitionName, const QString& attributeName) const
+TextStyleData ThemeData::textStyleOverride(const QString &definitionName, const QString &attributeName) const
{
- return m_textStyleOverrides.value(definitionName).value(attributeName);
+ if (!m_completelyLoaded) {
+ const_cast<ThemeData *>(this)->loadComplete();
+ }
+ auto it = m_textStyleOverrides.find(definitionName);
+ if (it != m_textStyleOverrides.end()) {
+ return it->value(attributeName);
+ }
+ return TextStyleData();
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/themedata_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/themedata_p.h
index 3b5f4637a9..6ee772f172 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/themedata_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/themedata_p.h
@@ -1,45 +1,33 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
- Copyright (C) 2016 Dominik Haumann <dhaumann@kde.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Dominik Haumann <dhaumann@kde.org>
+
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_THEMEDATA_P_H
#define KSYNTAXHIGHLIGHTING_THEMEDATA_P_H
-#include "theme.h"
#include "textstyledata_p.h"
+#include "theme.h"
#include <QHash>
#include <QSharedData>
-namespace KSyntaxHighlighting {
+#include <vector>
+namespace KSyntaxHighlighting
+{
/**
* Data container for a Theme.
*/
class ThemeData : public QSharedData
{
public:
- static ThemeData* get(const Theme &theme);
+ static ThemeData *get(const Theme &theme)
+ {
+ return theme.m_data.data();
+ }
/**
* Default constructor, creating an uninitialized ThemeData instance.
@@ -52,6 +40,8 @@ public:
*/
bool load(const QString &filePath);
+ void loadComplete();
+
/**
* Returns the unique name of this Theme.
*/
@@ -141,6 +131,11 @@ public:
*/
TextStyleData textStyleOverride(const QString &definitionName, const QString &attributeName) const;
+ /**
+ * Returns the TextStyle data for the given @p style.
+ */
+ TextStyleData textStyle(Theme::TextStyle style) const;
+
private:
int m_revision = 0;
QString m_name;
@@ -150,12 +145,14 @@ private:
//! on disk (in a read-only or a writeable location).
QString m_filePath;
+ bool m_completelyLoaded = false;
+
//! TextStyles
- TextStyleData m_textStyles[Theme::Others + 1];
+ std::vector<TextStyleData> m_textStyles;
//! style overrides for individual itemData entries
//! definition name -> attribute name -> style
- QHash<QString, QHash<QString, TextStyleData> > m_textStyleOverrides;
+ QHash<QString, QHash<QString, TextStyleData>> m_textStyleOverrides;
//! Editor area colors
QRgb m_editorColors[Theme::TemplateReadOnlyPlaceholder + 1];
@@ -164,7 +161,7 @@ private:
}
QT_BEGIN_NAMESPACE
-Q_DECLARE_TYPEINFO(KSyntaxHighlighting::TextStyleData, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(KSyntaxHighlighting::TextStyleData, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
#endif // KSYNTAXHIGHLIGHTING_THEMEDATA_P_H
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.cpp
index 167295a930..ab62f88b7e 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.cpp
@@ -1,35 +1,18 @@
/*
- Copyright (C) 2007 Sebastian Pipping <webmaster@hartwork.org>
+ SPDX-FileCopyrightText: 2007 Sebastian Pipping <webmaster@hartwork.org>
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-License-Identifier: MIT
*/
-#include "wildcardmatcher_p.h"
+#include "wildcardmatcher.h"
using namespace KSyntaxHighlighting;
-#include <QString>
#include <QChar>
-static bool exactMatch(const QString &candidate, const QString &wildcard, int candidatePosFromRight,
- int wildcardPosFromRight, bool caseSensitive = true)
+namespace
+{
+bool wildcardMatch(QStringView candidate, QStringView wildcard, int candidatePosFromRight, int wildcardPosFromRight)
{
for (; wildcardPosFromRight >= 0; wildcardPosFromRight--) {
const auto ch = wildcard.at(wildcardPosFromRight).unicode();
@@ -45,7 +28,7 @@ static bool exactMatch(const QString &candidate, const QString &wildcard, int ca
// Eat all we can and go back as far as we have to
for (int j = -1; j <= candidatePosFromRight; j++) {
- if (exactMatch(candidate, wildcard, j, wildcardPosFromRight - 1)) {
+ if (wildcardMatch(candidate, wildcard, j, wildcardPosFromRight - 1)) {
return true;
}
}
@@ -65,19 +48,19 @@ static bool exactMatch(const QString &candidate, const QString &wildcard, int ca
}
const auto candidateCh = candidate.at(candidatePosFromRight).unicode();
- const auto match = caseSensitive ? (candidateCh == ch) : (QChar::toLower(candidateCh) == QChar::toLower(ch));
- if (match) {
+ if (candidateCh == ch) {
candidatePosFromRight--;
} else {
return false;
}
}
}
- return true;
+ return candidatePosFromRight == -1;
}
-bool WildcardMatcher::exactMatch(const QString &candidate, const QString &wildcard,
- bool caseSensitive)
+} // unnamed namespace
+
+bool WildcardMatcher::exactMatch(QStringView candidate, QStringView wildcard)
{
- return ::exactMatch(candidate, wildcard, candidate.length() - 1, wildcard.length() - 1, caseSensitive);
+ return ::wildcardMatch(candidate, wildcard, candidate.length() - 1, wildcard.length() - 1);
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.h b/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.h
new file mode 100644
index 0000000000..4042de3788
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.h
@@ -0,0 +1,33 @@
+/*
+ SPDX-FileCopyrightText: 2007 Sebastian Pipping <webmaster@hartwork.org>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#ifndef KSYNTAXHIGHLIGHTING_WILDCARDMATCHER_H
+#define KSYNTAXHIGHLIGHTING_WILDCARDMATCHER_H
+
+#include "ksyntaxhighlighting_export.h"
+
+#include <QStringView>
+
+namespace KSyntaxHighlighting
+{
+namespace WildcardMatcher
+{
+/**
+ * Matches a string against a given wildcard case-sensitively.
+ * The wildcard supports '*' (".*" in regex) and '?' ("." in regex), not more.
+ *
+ * @param candidate Text to match
+ * @param wildcard Wildcard to use
+ * @return True for an exact match, false otherwise
+ *
+ * @since 5.86
+ */
+KSYNTAXHIGHLIGHTING_EXPORT bool exactMatch(QStringView candidate, QStringView wildcard);
+}
+
+}
+
+#endif // KSYNTAXHIGHLIGHTING_WILDCARDMATCHER_H
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher_p.h
deleted file mode 100644
index 016b10fe66..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher_p.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- Copyright (C) 2007 Sebastian Pipping <webmaster@hartwork.org>
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#ifndef KSYNTAXHIGHLIGHTING_WILDCARDMATCHER_P_H
-#define KSYNTAXHIGHLIGHTING_WILDCARDMATCHER_P_H
-
-#include <QtGlobal>
-
-QT_BEGIN_NAMESPACE
-class QString;
-QT_END_NAMESPACE
-
-namespace KSyntaxHighlighting {
-
-namespace WildcardMatcher
-{
- /**
- * Matches a string against a given wildcard.
- * The wildcard supports '*' (".*" in regex) and '?' ("." in regex), not more.
- *
- * @param candidate Text to match
- * @param wildcard Wildcard to use
- * @param caseSensitive Case-sensitivity flag
- * @return True for an exact match, false otherwise
- */
- bool exactMatch(const QString &candidate, const QString &wildcard, bool caseSensitive = true);
-}
-
-}
-
-#endif // KSYNTAXHIGHLIGHTING_WILDCARDMATCHER_P_H
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters.cpp
new file mode 100644
index 0000000000..ce55cd4b29
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters.cpp
@@ -0,0 +1,54 @@
+/*
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#include "worddelimiters_p.h"
+
+using namespace KSyntaxHighlighting;
+
+WordDelimiters::WordDelimiters()
+ : asciiDelimiters{}
+{
+ for (const char *p = "\t !%&()*+,-./:;<=>?[\\]^{|}~"; *p; ++p) {
+ asciiDelimiters.set(*p);
+ }
+}
+
+WordDelimiters::WordDelimiters(QStringView str)
+ : asciiDelimiters{}
+{
+ append(str);
+}
+
+bool WordDelimiters::contains(QChar c) const
+{
+ if (c.unicode() < 128) {
+ return asciiDelimiters.test(c.unicode());
+ }
+ // perf tells contains is MUCH faster than binary search here, very short array
+ return notAsciiDelimiters.contains(c);
+}
+
+void WordDelimiters::append(QStringView s)
+{
+ for (QChar c : s) {
+ if (c.unicode() < 128) {
+ asciiDelimiters.set(c.unicode());
+ } else {
+ notAsciiDelimiters.append(c);
+ }
+ }
+}
+
+void WordDelimiters::remove(QStringView s)
+{
+ for (QChar c : s) {
+ if (c.unicode() < 128) {
+ asciiDelimiters.set(c.unicode(), false);
+ } else {
+ notAsciiDelimiters.remove(c);
+ }
+ }
+}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters_p.h
new file mode 100644
index 0000000000..c23670d634
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters_p.h
@@ -0,0 +1,63 @@
+/*
+ SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#ifndef KSYNTAXHIGHLIGHTING_WORDDELIMITERS_P_H
+#define KSYNTAXHIGHLIGHTING_WORDDELIMITERS_P_H
+
+#include <QString>
+
+#include <bitset>
+
+namespace KSyntaxHighlighting
+{
+/**
+ * Represents a list of character that separates 2 words.
+ *
+ * Default delimiters are .():!+*,-<=>%&/;?[]^{|}~\, space (' ') and tabulator ('\t').
+ *
+ * @see Rule
+ * @since 5.74
+ */
+class WordDelimiters
+{
+public:
+ WordDelimiters();
+
+ /**
+ * Initialize with a default delimiters.
+ */
+ explicit WordDelimiters(QStringView str);
+
+ /**
+ * Returns @c true if @p c is a word delimiter; otherwise returns @c false.
+ */
+ bool contains(QChar c) const;
+
+ /**
+ * Appends each character of @p s to word delimiters.
+ */
+ void append(QStringView s);
+
+ /**
+ * Removes each character of @p s from word delimiters.
+ */
+ void remove(QStringView c);
+
+private:
+ /**
+ * An array which represents ascii characters for very fast lookup.
+ * The character is used as an index and the value @c true indicates a word delimiter.
+ */
+ std::bitset<128> asciiDelimiters;
+
+ /**
+ * Contains characters that are not ascii and is empty for most syntax definition.
+ */
+ QString notAsciiDelimiters;
+};
+}
+
+#endif
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/xml_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/xml_p.h
index 5f1f066dfd..2e1dd25cc8 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/xml_p.h
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/xml_p.h
@@ -1,24 +1,7 @@
/*
- Copyright (C) 2016 Volker Krause <vkrause@kde.org>
+ SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ SPDX-License-Identifier: MIT
*/
#ifndef KSYNTAXHIGHLIGHTING_XML_P_H
@@ -26,14 +9,15 @@
#include <QString>
-namespace KSyntaxHighlighting {
+namespace KSyntaxHighlighting
+{
/** Utilities for XML parsing. */
-namespace Xml {
-
+namespace Xml
+{
/** Parse a xs:boolean attribute. */
-inline bool attrToBool(const QStringRef &str)
+inline bool attrToBool(QStringView str)
{
- return str == QLatin1String("1") || str.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0;
+ return str == QStringLiteral("1") || str.compare(QStringLiteral("true"), Qt::CaseInsensitive) == 0;
}
}
diff --git a/src/libs/3rdparty/syntax-highlighting/src/quick/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/src/quick/CMakeLists.txt
new file mode 100644
index 0000000000..1fb92ad220
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/quick/CMakeLists.txt
@@ -0,0 +1,16 @@
+# SPDX-FileCopyrightText: 2018 Eike Hein <hein@kde.org>
+# SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
+# SPDX-License-Identifier: BSD-3-Clause
+
+ecm_add_qml_module(kquicksyntaxhighlightingplugin URI "org.kde.syntaxhighlighting")
+
+target_sources(kquicksyntaxhighlightingplugin PRIVATE
+ kquicksyntaxhighlightingplugin.cpp
+ kquicksyntaxhighlighter.cpp
+)
+target_link_libraries(kquicksyntaxhighlightingplugin PRIVATE
+ KF6SyntaxHighlighting
+ Qt6::Quick
+)
+
+ecm_finalize_qml_module(kquicksyntaxhighlightingplugin DESTINATION ${KDE_INSTALL_QMLDIR})
diff --git a/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.cpp
new file mode 100644
index 0000000000..19cfbacf58
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.cpp
@@ -0,0 +1,114 @@
+/*
+ SPDX-FileCopyrightText: 2018 Eike Hein <hein@kde.org>
+ SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#include "kquicksyntaxhighlighter.h"
+
+#include <KSyntaxHighlighting/Repository>
+#include <KSyntaxHighlighting/SyntaxHighlighter>
+
+#include <QGuiApplication>
+#include <QPalette>
+#include <QQuickTextDocument>
+#include <QTextDocument>
+
+using namespace KSyntaxHighlighting;
+
+extern Repository *defaultRepository();
+
+KQuickSyntaxHighlighter::KQuickSyntaxHighlighter(QObject *parent)
+ : QObject(parent)
+ , m_textEdit(nullptr)
+ , m_highlighter(new KSyntaxHighlighting::SyntaxHighlighter(this))
+{
+}
+
+KQuickSyntaxHighlighter::~KQuickSyntaxHighlighter() = default;
+
+QObject *KQuickSyntaxHighlighter::textEdit() const
+{
+ return m_textEdit;
+}
+
+void KQuickSyntaxHighlighter::setTextEdit(QObject *textEdit)
+{
+ if (m_textEdit != textEdit) {
+ m_textEdit = textEdit;
+ m_highlighter->setDocument(m_textEdit->property("textDocument").value<QQuickTextDocument *>()->textDocument());
+ }
+}
+
+QVariant KQuickSyntaxHighlighter::definition() const
+{
+ return QVariant::fromValue(m_definition);
+}
+
+void KQuickSyntaxHighlighter::setDefinition(const QVariant &definition)
+{
+ Definition def;
+ if (definition.userType() == QMetaType::QString) {
+ def = unwrappedRepository()->definitionForName(definition.toString());
+ } else {
+ def = definition.value<Definition>();
+ }
+
+ if (m_definition != def) {
+ m_definition = def;
+
+ m_highlighter->setTheme(m_theme.isValid() ? m_theme : unwrappedRepository()->themeForPalette(QGuiApplication::palette()));
+ m_highlighter->setDefinition(def);
+
+ Q_EMIT definitionChanged();
+ }
+}
+
+QVariant KQuickSyntaxHighlighter::theme() const
+{
+ return QVariant::fromValue(m_theme);
+}
+
+void KQuickSyntaxHighlighter::setTheme(const QVariant &theme)
+{
+ Theme t;
+ if (theme.userType() == QMetaType::QString) {
+ t = unwrappedRepository()->theme(theme.toString());
+ } else if (theme.userType() == QMetaType::Int) {
+ t = unwrappedRepository()->defaultTheme(static_cast<Repository::DefaultTheme>(theme.toInt()));
+ } else {
+ t = theme.value<Theme>();
+ }
+
+ if (m_theme.name() != t.name()) {
+ m_theme = t;
+ m_highlighter->setTheme(m_theme);
+ m_highlighter->rehighlight();
+ Q_EMIT themeChanged();
+ }
+}
+
+Repository *KQuickSyntaxHighlighter::repository() const
+{
+ return m_repository;
+}
+
+void KQuickSyntaxHighlighter::setRepository(Repository *repository)
+{
+ if (m_repository == repository) {
+ return;
+ }
+ m_repository = repository;
+ Q_EMIT repositoryChanged();
+}
+
+Repository *KQuickSyntaxHighlighter::unwrappedRepository() const
+{
+ if (m_repository) {
+ return m_repository;
+ }
+ return defaultRepository();
+}
+
+#include "moc_kquicksyntaxhighlighter.cpp"
diff --git a/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.h
new file mode 100644
index 0000000000..b45c26339f
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.h
@@ -0,0 +1,64 @@
+/*
+ SPDX-FileCopyrightText: 2018 Eike Hein <hein@kde.org>
+ SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#ifndef KQUICKSYNTAXHIGHLIGHTER_H
+#define KQUICKSYNTAXHIGHLIGHTER_H
+
+#include <KSyntaxHighlighting/Definition>
+#include <KSyntaxHighlighting/Theme>
+
+#include <QObject>
+#include <QVariant>
+
+namespace KSyntaxHighlighting
+{
+class Repository;
+class SyntaxHighlighter;
+}
+
+class KQuickSyntaxHighlighter : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QObject *textEdit READ textEdit WRITE setTextEdit NOTIFY textEditChanged)
+ Q_PROPERTY(QVariant definition READ definition WRITE setDefinition NOTIFY definitionChanged)
+ Q_PROPERTY(QVariant theme READ theme WRITE setTheme NOTIFY themeChanged)
+ Q_PROPERTY(KSyntaxHighlighting::Repository *repository READ repository WRITE setRepository NOTIFY repositoryChanged)
+
+public:
+ explicit KQuickSyntaxHighlighter(QObject *parent = nullptr);
+ ~KQuickSyntaxHighlighter() override;
+
+ QObject *textEdit() const;
+ void setTextEdit(QObject *textEdit);
+
+ QVariant definition() const;
+ void setDefinition(const QVariant &definition);
+
+ QVariant theme() const;
+ void setTheme(const QVariant &theme);
+
+ KSyntaxHighlighting::Repository *repository() const;
+ void setRepository(KSyntaxHighlighting::Repository *repository);
+
+Q_SIGNALS:
+ void textEditChanged() const;
+ void definitionChanged() const;
+ void themeChanged();
+ void repositoryChanged();
+
+private:
+ KSyntaxHighlighting::Repository *unwrappedRepository() const;
+
+ QObject *m_textEdit;
+ KSyntaxHighlighting::Definition m_definition;
+ KSyntaxHighlighting::Theme m_theme;
+ KSyntaxHighlighting::Repository *m_repository = nullptr;
+ KSyntaxHighlighting::SyntaxHighlighter *m_highlighter = nullptr;
+};
+
+#endif // KQUICKSYNTAXHIGHLIGHTER_H
diff --git a/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.cpp b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.cpp
new file mode 100644
index 0000000000..5eb06862df
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.cpp
@@ -0,0 +1,46 @@
+/*
+ SPDX-FileCopyrightText: 2018 Eike Hein <hein@kde.org>
+ SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#include "kquicksyntaxhighlightingplugin.h"
+#include "kquicksyntaxhighlighter.h"
+
+#include <KSyntaxHighlighting/Definition>
+#include <KSyntaxHighlighting/Repository>
+#include <KSyntaxHighlighting/Theme>
+
+#include <memory>
+
+using namespace KSyntaxHighlighting;
+
+Repository *defaultRepository()
+{
+ static std::unique_ptr<Repository> s_instance;
+ if (!s_instance) {
+ s_instance = std::make_unique<Repository>();
+ }
+ return s_instance.get();
+}
+
+void KQuickSyntaxHighlightingPlugin::registerTypes(const char *uri)
+{
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.syntaxhighlighting"));
+ qRegisterMetaType<Definition>();
+ qRegisterMetaType<QList<Definition>>();
+ qRegisterMetaType<Theme>();
+ qRegisterMetaType<QList<Theme>>();
+ qmlRegisterType<KQuickSyntaxHighlighter>(uri, 1, 0, "SyntaxHighlighter");
+ qmlRegisterUncreatableMetaObject(Definition::staticMetaObject, uri, 1, 0, "Definition", {});
+ qmlRegisterUncreatableMetaObject(Theme::staticMetaObject, uri, 1, 0, "Theme", {});
+ qmlRegisterSingletonType<Repository>(uri, 1, 0, "Repository", [](auto engine, auto scriptEngine) {
+ (void)engine;
+ auto repo = defaultRepository();
+ scriptEngine->setObjectOwnership(repo, QJSEngine::CppOwnership);
+ return defaultRepository();
+ });
+}
+
+#include "moc_kquicksyntaxhighlightingplugin.cpp"
diff --git a/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.h b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.h
new file mode 100644
index 0000000000..a92405bb15
--- /dev/null
+++ b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.h
@@ -0,0 +1,22 @@
+/*
+ SPDX-FileCopyrightText: 2018 Eike Hein <hein@kde.org>
+
+ SPDX-License-Identifier: MIT
+*/
+
+#ifndef KQUICKSYNTAXHIGHLIGHTINGPLUGIN_H
+#define KQUICKSYNTAXHIGHLIGHTINGPLUGIN_H
+
+#include <QQmlEngine>
+#include <QQmlExtensionPlugin>
+
+class KQuickSyntaxHighlightingPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+
+public:
+ void registerTypes(const char *uri) override;
+};
+
+#endif // KQUICKSYNTAXHIGHLIGHTINGPLUGIN_H
diff --git a/src/libs/3rdparty/syntax-highlighting/syntax-highlighting.pro b/src/libs/3rdparty/syntax-highlighting/syntax-highlighting.pro
deleted file mode 100644
index 38127e1cfe..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/syntax-highlighting.pro
+++ /dev/null
@@ -1,55 +0,0 @@
-include(../../../qtcreatorlibrary.pri)
-include(autogenerated/autogenerated.pri)
-
-QT += network
-
-DEFINES += KSYNTAXHIGHLIGHTING_LIBRARY
-
-RESOURCES += \
- data/themes/theme-data.qrc
-
-HEADERS += \
- src/lib/abstracthighlighter.h \
- src/lib/abstracthighlighter_p.h \
- src/lib/context_p.h \
- src/lib/contextswitch_p.h \
- src/lib/definition.h \
- src/lib/definition_p.h \
- src/lib/definitiondownloader.h \
- src/lib/definitionref_p.h \
- src/lib/foldingregion.h \
- src/lib/format.h \
- src/lib/format_p.h \
- src/lib/htmlhighlighter.h \
- src/lib/keywordlist_p.h \
- src/lib/ksyntaxhighlighting_export.h \
- src/lib/matchresult_p.h \
- src/lib/repository.h \
- src/lib/repository_p.h \
- src/lib/rule_p.h \
- src/lib/state.h \
- src/lib/state_p.h \
- src/lib/syntaxhighlighter.h \
- src/lib/textstyledata_p.h \
- src/lib/theme.h \
- src/lib/themedata_p.h \
- src/lib/wildcardmatcher_p.h \
- src/lib/xml_p.h \
-
-SOURCES += \
- src/lib/abstracthighlighter.cpp \
- src/lib/context.cpp \
- src/lib/contextswitch.cpp \
- src/lib/definition.cpp \
- src/lib/definitiondownloader.cpp \
- src/lib/foldingregion.cpp \
- src/lib/format.cpp \
- src/lib/htmlhighlighter.cpp \
- src/lib/keywordlist.cpp \
- src/lib/repository.cpp \
- src/lib/rule.cpp \
- src/lib/state.cpp \
- src/lib/syntaxhighlighter.cpp \
- src/lib/theme.cpp \
- src/lib/themedata.cpp \
- src/lib/wildcardmatcher.cpp \
diff --git a/src/libs/3rdparty/syntax-highlighting/syntax-highlighting.qbs b/src/libs/3rdparty/syntax-highlighting/syntax-highlighting.qbs
index 526265220c..b049589767 100644
--- a/src/libs/3rdparty/syntax-highlighting/syntax-highlighting.qbs
+++ b/src/libs/3rdparty/syntax-highlighting/syntax-highlighting.qbs
@@ -4,51 +4,93 @@ import qbs.FileInfo
import qbs.Environment
Project {
- QtcDevHeaders {
- productName: "syntax-highlighting (3rd party)"
- baseDir: sourceDirectory + "/src/lib"
- }
- QtcDevHeaders {
- productName: "syntax-highlighting autogenerated (3rd party)"
- baseDir: sourceDirectory + "/autogenerated/src/lib"
- Group {
- prefix: baseDir + '/'
- files: [
- "AbstractHighlighter",
- "Definition",
- "DefinitionDownloader",
- "FoldingRegion",
- "Format",
- "Repository",
- "State",
- "SyntaxHighlighter",
- "Theme"
- ]
- qbs.install: true
- qbs.installDir: qtc.ide_include_path + '/' + FileInfo.fileName(product.sourceDirectory)
- qbs.installSourceBase: baseDir
+ Product {
+ name: "KSyntaxHighlighting"
+
+ Export {
+ Depends { name: "qtc" }
+ Depends {
+ name: "Qt.KSyntaxHighlighting"
+ condition: qtc.preferSystemSyntaxHighlighting
+ required: false
+ }
+ Depends {
+ name: "KSyntaxHighlighting_bundled"
+ required: !qtc.preferSystemSyntaxHighlighting
+ }
}
}
QtcLibrary {
- name: "KSyntaxHighlighting"
+ name: "KSyntaxHighlighting_bundled"
+ condition: !qtc.preferSystemSyntaxHighlighting || !Qt.KSyntaxHighlighting.present
- cpp.defines: base.concat("KSYNTAXHIGHLIGHTING_LIBRARY")
+ cpp.defines: base.concat("KF6SyntaxHighlighting_EXPORTS")
cpp.includePaths: [
product.sourceDirectory + "/src/lib/",
+ product.sourceDirectory + "/autogenerated/include/",
product.sourceDirectory + "/autogenerated/src/lib/",
product.sourceDirectory + "/autogenerated/"
]
Depends { name: "Qt.gui" }
Depends { name: "Qt.network" }
+ Depends {
+ name: "Qt.KSyntaxHighlighting"
+ condition: qtc.preferSystemSyntaxHighlighting
+ required: false
+ }
Group {
name: "lib"
prefix: "src/lib/"
files: [
- "*.h",
- "*.cpp"
+ "abstracthighlighter.cpp",
+ "abstracthighlighter.h",
+ "abstracthighlighter_p.h",
+ "context.cpp",
+ "context_p.h",
+ "contextswitch.cpp",
+ "contextswitch_p.h",
+ "definition.cpp",
+ "definition.h",
+ "definition_p.h",
+ "definitiondownloader.cpp",
+ "definitiondownloader.h",
+ "definitionref_p.h",
+ "dynamicregexpcache_p.h",
+ "foldingregion.cpp",
+ "foldingregion.h",
+ "format.cpp",
+ "format.h",
+ "format_p.h",
+ "highlightingdata.cpp",
+ "highlightingdata_p.hpp",
+ "htmlhighlighter.cpp",
+ "htmlhighlighter.h",
+ "keywordlist.cpp",
+ "keywordlist_p.h",
+ "matchresult_p.h",
+ "repository.cpp",
+ "repository.h",
+ "repository_p.h",
+ "rule.cpp",
+ "rule_p.h",
+ "state.cpp",
+ "state.h",
+ "state_p.h",
+ "syntaxhighlighter.cpp",
+ "syntaxhighlighter.h",
+ "textstyledata_p.h",
+ "theme.cpp",
+ "theme.h",
+ "themedata.cpp",
+ "themedata_p.h",
+ "wildcardmatcher.cpp",
+ "wildcardmatcher.h",
+ "worddelimiters.cpp",
+ "worddelimiters_p.h",
+ "xml_p.h",
]
}
@@ -81,8 +123,9 @@ Project {
Export {
Depends { name: "cpp" }
cpp.includePaths: [
- product.sourceDirectory + "/src/lib/",
- product.sourceDirectory + "/autogenerated/src/lib/",
+ exportingProduct.sourceDirectory + "/src/lib/",
+ exportingProduct.sourceDirectory + "/autogenerated/include/",
+ exportingProduct.sourceDirectory + "/autogenerated/src/lib/",
]
}
}
diff --git a/src/libs/3rdparty/syntax-highlighting/syntax-highlighting_dependencies.pri b/src/libs/3rdparty/syntax-highlighting/syntax-highlighting_dependencies.pri
deleted file mode 100644
index f2fd3e61b2..0000000000
--- a/src/libs/3rdparty/syntax-highlighting/syntax-highlighting_dependencies.pri
+++ /dev/null
@@ -1,3 +0,0 @@
-QTC_LIB_NAME = KSyntaxHighlighting
-INCLUDEPATH *= $$PWD/src/lib
-INCLUDEPATH *= $$PWD/autogenerated/src/lib
diff --git a/src/libs/3rdparty/tl_expected/COPYING b/src/libs/3rdparty/tl_expected/COPYING
new file mode 100644
index 0000000000..0e259d42c9
--- /dev/null
+++ b/src/libs/3rdparty/tl_expected/COPYING
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/src/libs/3rdparty/tl_expected/README.md b/src/libs/3rdparty/tl_expected/README.md
new file mode 100644
index 0000000000..2a837519b8
--- /dev/null
+++ b/src/libs/3rdparty/tl_expected/README.md
@@ -0,0 +1,74 @@
+# expected
+Single header implementation of `std::expected` with functional-style extensions.
+
+[![Documentation Status](https://readthedocs.org/projects/tl-docs/badge/?version=latest)](https://tl.tartanllama.xyz/en/latest/?badge=latest)
+Clang + GCC: [![Linux Build Status](https://github.com/TartanLlama/expected/actions/workflows/cmake.yml/badge.svg)](https://github.com/TartanLlama/expected/actions/workflows/cmake.yml)
+MSVC: [![Windows Build Status](https://ci.appveyor.com/api/projects/status/k5x00xa11y3s5wsg?svg=true)](https://ci.appveyor.com/project/TartanLlama/expected)
+
+Available on [Vcpkg](https://github.com/microsoft/vcpkg/tree/master/ports/tl-expected) and [Conan](https://github.com/yipdw/conan-tl-expected).
+
+[`std::expected`](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0323r3.pdf) is proposed as the preferred way to represent object which will either have an expected value, or an unexpected value giving information about why something failed. Unfortunately, chaining together many computations which may fail can be verbose, as error-checking code will be mixed in with the actual programming logic. This implementation provides a number of utilities to make coding with `expected` cleaner.
+
+For example, instead of writing this code:
+
+```cpp
+std::expected<image,fail_reason> get_cute_cat (const image& img) {
+ auto cropped = crop_to_cat(img);
+ if (!cropped) {
+ return cropped;
+ }
+
+ auto with_tie = add_bow_tie(*cropped);
+ if (!with_tie) {
+ return with_tie;
+ }
+
+ auto with_sparkles = make_eyes_sparkle(*with_tie);
+ if (!with_sparkles) {
+ return with_sparkles;
+ }
+
+ return add_rainbow(make_smaller(*with_sparkles));
+}
+```
+
+You can do this:
+
+```cpp
+tl::expected<image,fail_reason> get_cute_cat (const image& img) {
+ return crop_to_cat(img)
+ .and_then(add_bow_tie)
+ .and_then(make_eyes_sparkle)
+ .map(make_smaller)
+ .map(add_rainbow);
+}
+```
+
+The interface is the same as `std::expected` as proposed in [p0323r3](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0323r3.pdf), but the following member functions are also defined. Explicit types are for clarity.
+
+- `map`: carries out some operation on the stored object if there is one.
+ * `tl::expected<std::size_t,std::error_code> s = exp_string.map(&std::string::size);`
+- `map_error`: carries out some operation on the unexpected object if there is one.
+ * `my_error_code translate_error (std::error_code);`
+ * `tl::expected<int,my_error_code> s = exp_int.map_error(translate_error);`
+- `and_then`: like `map`, but for operations which return a `tl::expected`.
+ * `tl::expected<ast, fail_reason> parse (const std::string& s);`
+ * `tl::expected<ast, fail_reason> exp_ast = exp_string.and_then(parse);`
+- `or_else`: calls some function if there is no value stored.
+ * `exp.or_else([] { throw std::runtime_error{"oh no"}; });`
+
+### Compiler support
+
+Tested on:
+
+- Linux
+ * clang++ 3.5, 3.6, 3.7, 3.8, 3.9, 4, 5, 6, 7, 8, 9, 10, 11
+ * g++ 4.8, 4.9, 5.5, 6.4, 7.5, 8, 9, 10
+- Windows
+ * MSVC 2015, 2017, 2019, 2022
+
+----------
+
+[![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)]("http://creativecommons.org/publicdomain/zero/1.0/")
+
+To the extent possible under law, [Sy Brand](https://twitter.com/TartanLlama) has waived all copyright and related or neighboring rights to the `expected` library. This work is published from: United Kingdom.
diff --git a/src/libs/3rdparty/tl_expected/include/tl/expected.hpp b/src/libs/3rdparty/tl_expected/include/tl/expected.hpp
new file mode 100644
index 0000000000..afee404d43
--- /dev/null
+++ b/src/libs/3rdparty/tl_expected/include/tl/expected.hpp
@@ -0,0 +1,2444 @@
+///
+// expected - An implementation of std::expected with extensions
+// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
+//
+// Documentation available at http://tl.tartanllama.xyz/
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to the
+// public domain worldwide. This software is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication
+// along with this software. If not, see
+// <http://creativecommons.org/publicdomain/zero/1.0/>.
+///
+
+#ifndef TL_EXPECTED_HPP
+#define TL_EXPECTED_HPP
+
+#define TL_EXPECTED_VERSION_MAJOR 1
+#define TL_EXPECTED_VERSION_MINOR 1
+#define TL_EXPECTED_VERSION_PATCH 0
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+#define TL_EXPECTED_EXCEPTIONS_ENABLED
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER == 1900)
+#define TL_EXPECTED_MSVC2015
+#define TL_EXPECTED_MSVC2015_CONSTEXPR
+#else
+#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
+ !defined(__clang__))
+#define TL_EXPECTED_GCC49
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
+ !defined(__clang__))
+#define TL_EXPECTED_GCC54
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \
+ !defined(__clang__))
+#define TL_EXPECTED_GCC55
+#endif
+
+#if !defined(TL_ASSERT)
+//can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
+#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
+#include <cassert>
+#define TL_ASSERT(x) assert(x)
+#else
+#define TL_ASSERT(x)
+#endif
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
+ !defined(__clang__))
+// GCC < 5 doesn't support overloading on const&& for member functions
+
+#define TL_EXPECTED_NO_CONSTRR
+// GCC < 5 doesn't support some standard C++11 type traits
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ std::has_trivial_copy_constructor<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::has_trivial_copy_assign<T>
+
+// This one will be different for GCC 5.7 if it's ever supported
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
+ std::is_trivially_destructible<T>
+
+// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
+// std::vector for non-copyable types
+#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
+#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+namespace tl {
+namespace detail {
+template <class T>
+struct is_trivially_copy_constructible
+ : std::is_trivially_copy_constructible<T> {};
+#ifdef _GLIBCXX_VECTOR
+template <class T, class A>
+struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
+#endif
+} // namespace detail
+} // namespace tl
+#endif
+
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ tl::detail::is_trivially_copy_constructible<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::is_trivially_copy_assignable<T>
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
+ std::is_trivially_destructible<T>
+#else
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
+ std::is_trivially_copy_constructible<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
+ std::is_trivially_copy_assignable<T>
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \
+ std::is_trivially_destructible<T>
+#endif
+
+#if __cplusplus > 201103L
+#define TL_EXPECTED_CXX14
+#endif
+
+#ifdef TL_EXPECTED_GCC49
+#define TL_EXPECTED_GCC49_CONSTEXPR
+#else
+#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
+#endif
+
+#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \
+ defined(TL_EXPECTED_GCC49))
+#define TL_EXPECTED_11_CONSTEXPR
+#else
+#define TL_EXPECTED_11_CONSTEXPR constexpr
+#endif
+
+namespace tl {
+template <class T, class E> class expected;
+
+#ifndef TL_MONOSTATE_INPLACE_MUTEX
+#define TL_MONOSTATE_INPLACE_MUTEX
+class monostate {};
+
+struct in_place_t {
+ explicit in_place_t() = default;
+};
+static constexpr in_place_t in_place{};
+#endif
+
+template <class E> class unexpected {
+public:
+ static_assert(!std::is_same<E, void>::value, "E must not be void");
+
+ unexpected() = delete;
+ constexpr explicit unexpected(const E &e) : m_val(e) {}
+
+ constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {}
+
+ template <class... Args, typename std::enable_if<std::is_constructible<
+ E, Args &&...>::value>::type * = nullptr>
+ constexpr explicit unexpected(Args &&...args)
+ : m_val(std::forward<Args>(args)...) {}
+ template <
+ class U, class... Args,
+ typename std::enable_if<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value>::type * = nullptr>
+ constexpr explicit unexpected(std::initializer_list<U> l, Args &&...args)
+ : m_val(l, std::forward<Args>(args)...) {}
+
+ constexpr const E &value() const & { return m_val; }
+ TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; }
+ TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); }
+ constexpr const E &&value() const && { return std::move(m_val); }
+
+private:
+ E m_val;
+};
+
+#ifdef __cpp_deduction_guides
+template <class E> unexpected(E) -> unexpected<E>;
+#endif
+
+template <class E>
+constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() == rhs.value();
+}
+template <class E>
+constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() != rhs.value();
+}
+template <class E>
+constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() < rhs.value();
+}
+template <class E>
+constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() <= rhs.value();
+}
+template <class E>
+constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() > rhs.value();
+}
+template <class E>
+constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+ return lhs.value() >= rhs.value();
+}
+
+template <class E>
+unexpected<typename std::decay<E>::type> make_unexpected(E &&e) {
+ return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
+}
+
+struct unexpect_t {
+ unexpect_t() = default;
+};
+static constexpr unexpect_t unexpect{};
+
+namespace detail {
+template <typename E>
+[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) {
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ throw std::forward<E>(e);
+#else
+ (void)e;
+#ifdef _MSC_VER
+ __assume(0);
+#else
+ __builtin_unreachable();
+#endif
+#endif
+}
+
+#ifndef TL_TRAITS_MUTEX
+#define TL_TRAITS_MUTEX
+// C++14-style aliases for brevity
+template <class T> using remove_const_t = typename std::remove_const<T>::type;
+template <class T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+template <class T> using decay_t = typename std::decay<T>::type;
+template <bool E, class T = void>
+using enable_if_t = typename std::enable_if<E, T>::type;
+template <bool B, class T, class F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+
+// std::conjunction from C++17
+template <class...> struct conjunction : std::true_type {};
+template <class B> struct conjunction<B> : B {};
+template <class B, class... Bs>
+struct conjunction<B, Bs...>
+ : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
+
+#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
+#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+#endif
+
+// In C++11 mode, there's an issue in libc++'s std::mem_fn
+// which results in a hard-error when using it in a noexcept expression
+// in some cases. This is a check to workaround the common failing case.
+#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+template <class T>
+struct is_pointer_to_non_const_member_func : std::false_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
+ : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
+ : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
+ : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
+ : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &>
+ : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &&>
+ : std::true_type {};
+
+template <class T> struct is_const_or_const_ref : std::false_type {};
+template <class T> struct is_const_or_const_ref<T const &> : std::true_type {};
+template <class T> struct is_const_or_const_ref<T const> : std::true_type {};
+#endif
+
+// std::invoke from C++17
+// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
+template <
+ typename Fn, typename... Args,
+#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+ typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
+ is_const_or_const_ref<Args...>::value)>,
+#endif
+ typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
+constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
+ noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
+ -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
+ return std::mem_fn(f)(std::forward<Args>(args)...);
+}
+
+template <typename Fn, typename... Args,
+ typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
+constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
+ noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
+ -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
+ return std::forward<Fn>(f)(std::forward<Args>(args)...);
+}
+
+// std::invoke_result from C++17
+template <class F, class, class... Us> struct invoke_result_impl;
+
+template <class F, class... Us>
+struct invoke_result_impl<
+ F,
+ decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
+ Us...> {
+ using type =
+ decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
+};
+
+template <class F, class... Us>
+using invoke_result = invoke_result_impl<F, void, Us...>;
+
+template <class F, class... Us>
+using invoke_result_t = typename invoke_result<F, Us...>::type;
+
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+// TODO make a version which works with MSVC 2015
+template <class T, class U = T> struct is_swappable : std::true_type {};
+
+template <class T, class U = T> struct is_nothrow_swappable : std::true_type {};
+#else
+// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
+namespace swap_adl_tests {
+// if swap ADL finds this then it would call std::swap otherwise (same
+// signature)
+struct tag {};
+
+template <class T> tag swap(T &, T &);
+template <class T, std::size_t N> tag swap(T (&a)[N], T (&b)[N]);
+
+// helper functions to test if an unqualified swap is possible, and if it
+// becomes std::swap
+template <class, class> std::false_type can_swap(...) noexcept(false);
+template <class T, class U,
+ class = decltype(swap(std::declval<T &>(), std::declval<U &>()))>
+std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T &>(),
+ std::declval<U &>())));
+
+template <class, class> std::false_type uses_std(...);
+template <class T, class U>
+std::is_same<decltype(swap(std::declval<T &>(), std::declval<U &>())), tag>
+uses_std(int);
+
+template <class T>
+struct is_std_swap_noexcept
+ : std::integral_constant<bool,
+ std::is_nothrow_move_constructible<T>::value &&
+ std::is_nothrow_move_assignable<T>::value> {};
+
+template <class T, std::size_t N>
+struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
+
+template <class T, class U>
+struct is_adl_swap_noexcept
+ : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
+} // namespace swap_adl_tests
+
+template <class T, class U = T>
+struct is_swappable
+ : std::integral_constant<
+ bool,
+ decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
+ (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
+ (std::is_move_assignable<T>::value &&
+ std::is_move_constructible<T>::value))> {};
+
+template <class T, std::size_t N>
+struct is_swappable<T[N], T[N]>
+ : std::integral_constant<
+ bool,
+ decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
+ (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
+ 0))::value ||
+ is_swappable<T, T>::value)> {};
+
+template <class T, class U = T>
+struct is_nothrow_swappable
+ : std::integral_constant<
+ bool,
+ is_swappable<T, U>::value &&
+ ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
+ detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
+ (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
+ detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
+#endif
+#endif
+
+// Trait for checking if a type is a tl::expected
+template <class T> struct is_expected_impl : std::false_type {};
+template <class T, class E>
+struct is_expected_impl<expected<T, E>> : std::true_type {};
+template <class T> using is_expected = is_expected_impl<decay_t<T>>;
+
+template <class T, class E, class U>
+using expected_enable_forward_value = detail::enable_if_t<
+ std::is_constructible<T, U &&>::value &&
+ !std::is_same<detail::decay_t<U>, in_place_t>::value &&
+ !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+ !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
+
+template <class T, class E, class U, class G, class UR, class GR>
+using expected_enable_from_other = detail::enable_if_t<
+ std::is_constructible<T, UR>::value &&
+ std::is_constructible<E, GR>::value &&
+ !std::is_constructible<T, expected<U, G> &>::value &&
+ !std::is_constructible<T, expected<U, G> &&>::value &&
+ !std::is_constructible<T, const expected<U, G> &>::value &&
+ !std::is_constructible<T, const expected<U, G> &&>::value &&
+ !std::is_convertible<expected<U, G> &, T>::value &&
+ !std::is_convertible<expected<U, G> &&, T>::value &&
+ !std::is_convertible<const expected<U, G> &, T>::value &&
+ !std::is_convertible<const expected<U, G> &&, T>::value>;
+
+template <class T, class U>
+using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
+
+template <class T>
+using is_copy_constructible_or_void =
+ is_void_or<T, std::is_copy_constructible<T>>;
+
+template <class T>
+using is_move_constructible_or_void =
+ is_void_or<T, std::is_move_constructible<T>>;
+
+template <class T>
+using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
+
+template <class T>
+using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
+
+} // namespace detail
+
+namespace detail {
+struct no_init_t {};
+static constexpr no_init_t no_init{};
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for where neither `T` or `E` is trivially
+// destructible, so the destructors must be called on destruction of the
+// `expected`
+template <class T, class E, bool = std::is_trivially_destructible<T>::value,
+ bool = std::is_trivially_destructible<E>::value>
+struct expected_storage_base {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&...args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&...args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (m_has_val) {
+ m_val.~T();
+ } else {
+ m_unexpect.~unexpected<E>();
+ }
+ }
+ union {
+ T m_val;
+ unexpected<E> m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// This specialization is for when both `T` and `E` are trivially-destructible,
+// so the destructor of the `expected` can be trivial.
+template <class T, class E> struct expected_storage_base<T, E, true, true> {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&...args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&...args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() = default;
+ union {
+ T m_val;
+ unexpected<E> m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// T is trivial, E is not.
+template <class T, class E> struct expected_storage_base<T, E, true, false> {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
+ : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&...args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&...args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (!m_has_val) {
+ m_unexpect.~unexpected<E>();
+ }
+ }
+
+ union {
+ T m_val;
+ unexpected<E> m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// E is trivial, T is not.
+template <class T, class E> struct expected_storage_base<T, E, false, true> {
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected_storage_base(in_place_t, Args &&...args)
+ : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+ Args &&...args)
+ : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (m_has_val) {
+ m_val.~T();
+ }
+ }
+ union {
+ T m_val;
+ unexpected<E> m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E> struct expected_storage_base<void, E, false, true> {
+ #if __GNUC__ <= 5
+ //no constexpr for GCC 4/5 bug
+ #else
+ TL_EXPECTED_MSVC2015_CONSTEXPR
+ #endif
+ expected_storage_base() : m_has_val(true) {}
+
+ constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
+
+ constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() = default;
+ struct dummy {};
+ union {
+ unexpected<E> m_unexpect;
+ dummy m_val;
+ };
+ bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E> struct expected_storage_base<void, E, false, false> {
+ constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
+
+ constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+ : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t,
+ std::initializer_list<U> il,
+ Args &&...args)
+ : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+ ~expected_storage_base() {
+ if (!m_has_val) {
+ m_unexpect.~unexpected<E>();
+ }
+ }
+
+ union {
+ unexpected<E> m_unexpect;
+ char m_dummy;
+ };
+ bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct expected_operations_base : expected_storage_base<T, E> {
+ using expected_storage_base<T, E>::expected_storage_base;
+
+ template <class... Args> void construct(Args &&...args) noexcept {
+ new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
+ this->m_has_val = true;
+ }
+
+ template <class Rhs> void construct_with(Rhs &&rhs) noexcept {
+ new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
+ this->m_has_val = true;
+ }
+
+ template <class... Args> void construct_error(Args &&...args) noexcept {
+ new (std::addressof(this->m_unexpect))
+ unexpected<E>(std::forward<Args>(args)...);
+ this->m_has_val = false;
+ }
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+
+ // These assign overloads ensure that the most efficient assignment
+ // implementation is used while maintaining the strong exception guarantee.
+ // The problematic case is where rhs has a value, but *this does not.
+ //
+ // This overload handles the case where we can just copy-construct `T`
+ // directly into place without throwing.
+ template <class U = T,
+ detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>
+ * = nullptr>
+ void assign(const expected_operations_base &rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(rhs.get());
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ // This overload handles the case where we can attempt to create a copy of
+ // `T`, then no-throw move it into place if the copy was successful.
+ template <class U = T,
+ detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
+ std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(const expected_operations_base &rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ T tmp = rhs.get();
+ geterr().~unexpected<E>();
+ construct(std::move(tmp));
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ // This overload is the worst-case, where we have to move-construct the
+ // unexpected value into temporary storage, then try to copy the T into place.
+ // If the construction succeeds, then everything is fine, but if it throws,
+ // then we move the old unexpected value back into place before rethrowing the
+ // exception.
+ template <class U = T,
+ detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
+ !std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(const expected_operations_base &rhs) {
+ if (!this->m_has_val && rhs.m_has_val) {
+ auto tmp = std::move(geterr());
+ geterr().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ construct(rhs.get());
+ } catch (...) {
+ geterr() = std::move(tmp);
+ throw;
+ }
+#else
+ construct(rhs.get());
+#endif
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ // These overloads do the same as above, but for rvalues
+ template <class U = T,
+ detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(expected_operations_base &&rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(std::move(rhs).get());
+ } else {
+ assign_common(std::move(rhs));
+ }
+ }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value>
+ * = nullptr>
+ void assign(expected_operations_base &&rhs) {
+ if (!this->m_has_val && rhs.m_has_val) {
+ auto tmp = std::move(geterr());
+ geterr().~unexpected<E>();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ construct(std::move(rhs).get());
+ } catch (...) {
+ geterr() = std::move(tmp);
+ throw;
+ }
+#else
+ construct(std::move(rhs).get());
+#endif
+ } else {
+ assign_common(std::move(rhs));
+ }
+ }
+
+#else
+
+ // If exceptions are disabled then we can just copy-construct
+ void assign(const expected_operations_base &rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(rhs.get());
+ } else {
+ assign_common(rhs);
+ }
+ }
+
+ void assign(expected_operations_base &&rhs) noexcept {
+ if (!this->m_has_val && rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct(std::move(rhs).get());
+ } else {
+ assign_common(std::move(rhs));
+ }
+ }
+
+#endif
+
+ // The common part of move/copy assigning
+ template <class Rhs> void assign_common(Rhs &&rhs) {
+ if (this->m_has_val) {
+ if (rhs.m_has_val) {
+ get() = std::forward<Rhs>(rhs).get();
+ } else {
+ destroy_val();
+ construct_error(std::forward<Rhs>(rhs).geterr());
+ }
+ } else {
+ if (!rhs.m_has_val) {
+ geterr() = std::forward<Rhs>(rhs).geterr();
+ }
+ }
+ }
+
+ bool has_value() const { return this->m_has_val; }
+
+ TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; }
+ constexpr const T &get() const & { return this->m_val; }
+ TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); }
+#ifndef TL_EXPECTED_NO_CONSTRR
+ constexpr const T &&get() const && { return std::move(this->m_val); }
+#endif
+
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
+ return this->m_unexpect;
+ }
+ constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
+ return std::move(this->m_unexpect);
+ }
+#ifndef TL_EXPECTED_NO_CONSTRR
+ constexpr const unexpected<E> &&geterr() const && {
+ return std::move(this->m_unexpect);
+ }
+#endif
+
+ TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct expected_operations_base<void, E> : expected_storage_base<void, E> {
+ using expected_storage_base<void, E>::expected_storage_base;
+
+ template <class... Args> void construct() noexcept { this->m_has_val = true; }
+
+ // This function doesn't use its argument, but needs it so that code in
+ // levels above this can work independently of whether T is void
+ template <class Rhs> void construct_with(Rhs &&) noexcept {
+ this->m_has_val = true;
+ }
+
+ template <class... Args> void construct_error(Args &&...args) noexcept {
+ new (std::addressof(this->m_unexpect))
+ unexpected<E>(std::forward<Args>(args)...);
+ this->m_has_val = false;
+ }
+
+ template <class Rhs> void assign(Rhs &&rhs) noexcept {
+ if (!this->m_has_val) {
+ if (rhs.m_has_val) {
+ geterr().~unexpected<E>();
+ construct();
+ } else {
+ geterr() = std::forward<Rhs>(rhs).geterr();
+ }
+ } else {
+ if (!rhs.m_has_val) {
+ construct_error(std::forward<Rhs>(rhs).geterr());
+ }
+ }
+ }
+
+ bool has_value() const { return this->m_has_val; }
+
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
+ return this->m_unexpect;
+ }
+ constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
+ return std::move(this->m_unexpect);
+ }
+#ifndef TL_EXPECTED_NO_CONSTRR
+ constexpr const unexpected<E> &&geterr() const && {
+ return std::move(this->m_unexpect);
+ }
+#endif
+
+ TL_EXPECTED_11_CONSTEXPR void destroy_val() {
+ // no-op
+ }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+ bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
+ value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
+struct expected_copy_base : expected_operations_base<T, E> {
+ using expected_operations_base<T, E>::expected_operations_base;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
+ using expected_operations_base<T, E>::expected_operations_base;
+
+ expected_copy_base() = default;
+ expected_copy_base(const expected_copy_base &rhs)
+ : expected_operations_base<T, E>(no_init) {
+ if (rhs.has_value()) {
+ this->construct_with(rhs);
+ } else {
+ this->construct_error(rhs.geterr());
+ }
+ }
+
+ expected_copy_base(expected_copy_base &&rhs) = default;
+ expected_copy_base &operator=(const expected_copy_base &rhs) = default;
+ expected_copy_base &operator=(expected_copy_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_constructible. We
+// have to make do with a non-trivial move constructor even if T is trivially
+// move constructible
+#ifndef TL_EXPECTED_GCC49
+template <class T, class E,
+ bool = is_void_or<T, std::is_trivially_move_constructible<T>>::value
+ &&std::is_trivially_move_constructible<E>::value>
+struct expected_move_base : expected_copy_base<T, E> {
+ using expected_copy_base<T, E>::expected_copy_base;
+};
+#else
+template <class T, class E, bool = false> struct expected_move_base;
+#endif
+template <class T, class E>
+struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
+ using expected_copy_base<T, E>::expected_copy_base;
+
+ expected_move_base() = default;
+ expected_move_base(const expected_move_base &rhs) = default;
+
+ expected_move_base(expected_move_base &&rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value)
+ : expected_copy_base<T, E>(no_init) {
+ if (rhs.has_value()) {
+ this->construct_with(std::move(rhs));
+ } else {
+ this->construct_error(std::move(rhs.geterr()));
+ }
+ }
+ expected_move_base &operator=(const expected_move_base &rhs) = default;
+ expected_move_base &operator=(expected_move_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial copy assignment operator
+template <class T, class E,
+ bool = is_void_or<
+ T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
+ TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
+ TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value
+ &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value
+ &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value
+ &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
+struct expected_copy_assign_base : expected_move_base<T, E> {
+ using expected_move_base<T, E>::expected_move_base;
+};
+
+template <class T, class E>
+struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
+ using expected_move_base<T, E>::expected_move_base;
+
+ expected_copy_assign_base() = default;
+ expected_copy_assign_base(const expected_copy_assign_base &rhs) = default;
+
+ expected_copy_assign_base(expected_copy_assign_base &&rhs) = default;
+ expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) {
+ this->assign(rhs);
+ return *this;
+ }
+ expected_copy_assign_base &
+ operator=(expected_copy_assign_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move assignment operator
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_assignable. We have
+// to make do with a non-trivial move assignment operator even if T is trivially
+// move assignable
+#ifndef TL_EXPECTED_GCC49
+template <class T, class E,
+ bool =
+ is_void_or<T, conjunction<std::is_trivially_destructible<T>,
+ std::is_trivially_move_constructible<T>,
+ std::is_trivially_move_assignable<T>>>::
+ value &&std::is_trivially_destructible<E>::value
+ &&std::is_trivially_move_constructible<E>::value
+ &&std::is_trivially_move_assignable<E>::value>
+struct expected_move_assign_base : expected_copy_assign_base<T, E> {
+ using expected_copy_assign_base<T, E>::expected_copy_assign_base;
+};
+#else
+template <class T, class E, bool = false> struct expected_move_assign_base;
+#endif
+
+template <class T, class E>
+struct expected_move_assign_base<T, E, false>
+ : expected_copy_assign_base<T, E> {
+ using expected_copy_assign_base<T, E>::expected_copy_assign_base;
+
+ expected_move_assign_base() = default;
+ expected_move_assign_base(const expected_move_assign_base &rhs) = default;
+
+ expected_move_assign_base(expected_move_assign_base &&rhs) = default;
+
+ expected_move_assign_base &
+ operator=(const expected_move_assign_base &rhs) = default;
+
+ expected_move_assign_base &
+ operator=(expected_move_assign_base &&rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value
+ &&std::is_nothrow_move_assignable<T>::value) {
+ this->assign(std::move(rhs));
+ return *this;
+ }
+};
+
+// expected_delete_ctor_base will conditionally delete copy and move
+// constructors depending on whether T is copy/move constructible
+template <class T, class E,
+ bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
+ std::is_copy_constructible<E>::value),
+ bool EnableMove = (is_move_constructible_or_void<T>::value &&
+ std::is_move_constructible<E>::value)>
+struct expected_delete_ctor_base {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, true, false> {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, false, true> {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, false, false> {
+ expected_delete_ctor_base() = default;
+ expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
+ expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
+ expected_delete_ctor_base &
+ operator=(const expected_delete_ctor_base &) = default;
+ expected_delete_ctor_base &
+ operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+// expected_delete_assign_base will conditionally delete copy and move
+// constructors depending on whether T and E are copy/move constructible +
+// assignable
+template <class T, class E,
+ bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
+ std::is_copy_constructible<E>::value &&
+ is_copy_assignable_or_void<T>::value &&
+ std::is_copy_assignable<E>::value),
+ bool EnableMove = (is_move_constructible_or_void<T>::value &&
+ std::is_move_constructible<E>::value &&
+ is_move_assignable_or_void<T>::value &&
+ std::is_move_assignable<E>::value)>
+struct expected_delete_assign_base {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, true, false> {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = delete;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, false, true> {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = delete;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, false, false> {
+ expected_delete_assign_base() = default;
+ expected_delete_assign_base(const expected_delete_assign_base &) = default;
+ expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+ default;
+ expected_delete_assign_base &
+ operator=(const expected_delete_assign_base &) = delete;
+ expected_delete_assign_base &
+ operator=(expected_delete_assign_base &&) noexcept = delete;
+};
+
+// This is needed to be able to construct the expected_default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+ explicit constexpr default_constructor_tag() = default;
+};
+
+// expected_default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E,
+ bool Enable =
+ std::is_default_constructible<T>::value || std::is_void<T>::value>
+struct expected_default_ctor_base {
+ constexpr expected_default_ctor_base() noexcept = default;
+ constexpr expected_default_ctor_base(
+ expected_default_ctor_base const &) noexcept = default;
+ constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
+ default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base const &) noexcept = default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base &&) noexcept = default;
+
+ constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E> struct expected_default_ctor_base<T, E, false> {
+ constexpr expected_default_ctor_base() noexcept = delete;
+ constexpr expected_default_ctor_base(
+ expected_default_ctor_base const &) noexcept = default;
+ constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
+ default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base const &) noexcept = default;
+ expected_default_ctor_base &
+ operator=(expected_default_ctor_base &&) noexcept = default;
+
+ constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
+};
+} // namespace detail
+
+template <class E> class bad_expected_access : public std::exception {
+public:
+ explicit bad_expected_access(E e) : m_val(std::move(e)) {}
+
+ virtual const char *what() const noexcept override {
+ return "Bad expected access";
+ }
+
+ const E &error() const & { return m_val; }
+ E &error() & { return m_val; }
+ const E &&error() const && { return std::move(m_val); }
+ E &&error() && { return std::move(m_val); }
+
+private:
+ E m_val;
+};
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+template <class T, class E>
+class expected : private detail::expected_move_assign_base<T, E>,
+ private detail::expected_delete_ctor_base<T, E>,
+ private detail::expected_delete_assign_base<T, E>,
+ private detail::expected_default_ctor_base<T, E> {
+ static_assert(!std::is_reference<T>::value, "T must not be a reference");
+ static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
+ "T must not be in_place_t");
+ static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
+ "T must not be unexpect_t");
+ static_assert(
+ !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
+ "T must not be unexpected<E>");
+ static_assert(!std::is_reference<E>::value, "E must not be a reference");
+
+ T *valptr() { return std::addressof(this->m_val); }
+ const T *valptr() const { return std::addressof(this->m_val); }
+ unexpected<E> *errptr() { return std::addressof(this->m_unexpect); }
+ const unexpected<E> *errptr() const {
+ return std::addressof(this->m_unexpect);
+ }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &val() {
+ return this->m_val;
+ }
+ TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ constexpr const U &val() const {
+ return this->m_val;
+ }
+ constexpr const unexpected<E> &err() const { return this->m_unexpect; }
+
+ using impl_base = detail::expected_move_assign_base<T, E>;
+ using ctor_base = detail::expected_default_ctor_base<T, E>;
+
+public:
+ typedef T value_type;
+ typedef E error_type;
+ typedef unexpected<E> unexpected_type;
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F> constexpr auto and_then(F &&f) const & {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F> constexpr auto and_then(F &&f) const && {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+
+#else
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR auto
+ and_then(F &&f) & -> decltype(and_then_impl(std::declval<expected &>(),
+ std::forward<F>(f))) {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR auto
+ and_then(F &&f) && -> decltype(and_then_impl(std::declval<expected &&>(),
+ std::forward<F>(f))) {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F>
+ constexpr auto and_then(F &&f) const & -> decltype(and_then_impl(
+ std::declval<expected const &>(), std::forward<F>(f))) {
+ return and_then_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F>
+ constexpr auto and_then(F &&f) const && -> decltype(and_then_impl(
+ std::declval<expected const &&>(), std::forward<F>(f))) {
+ return and_then_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F> constexpr auto map(F &&f) const & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F> constexpr auto map(F &&f) const && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
+ std::declval<expected &>(), std::declval<F &&>()))
+ map(F &&f) & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
+ std::declval<F &&>()))
+ map(F &&f) && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F>
+ constexpr decltype(expected_map_impl(std::declval<const expected &>(),
+ std::declval<F &&>()))
+ map(F &&f) const & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F>
+ constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
+ std::declval<F &&>()))
+ map(F &&f) const && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F> constexpr auto transform(F &&f) const & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F> constexpr auto transform(F &&f) const && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
+ std::declval<expected &>(), std::declval<F &&>()))
+ transform(F &&f) & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
+ std::declval<F &&>()))
+ transform(F &&f) && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F>
+ constexpr decltype(expected_map_impl(std::declval<const expected &>(),
+ std::declval<F &&>()))
+ transform(F &&f) const & {
+ return expected_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F>
+ constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
+ std::declval<F &&>()))
+ transform(F &&f) const && {
+ return expected_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F> constexpr auto map_error(F &&f) const & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F> constexpr auto map_error(F &&f) const && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
+ std::declval<F &&>()))
+ map_error(F &&f) & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
+ std::declval<F &&>()))
+ map_error(F &&f) && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F>
+ constexpr decltype(map_error_impl(std::declval<const expected &>(),
+ std::declval<F &&>()))
+ map_error(F &&f) const & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F>
+ constexpr decltype(map_error_impl(std::declval<const expected &&>(),
+ std::declval<F &&>()))
+ map_error(F &&f) const && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F> constexpr auto transform_error(F &&f) const & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F> constexpr auto transform_error(F &&f) const && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
+ std::declval<F &&>()))
+ transform_error(F &&f) & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+ template <class F>
+ TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
+ std::declval<F &&>()))
+ transform_error(F &&f) && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+ template <class F>
+ constexpr decltype(map_error_impl(std::declval<const expected &>(),
+ std::declval<F &&>()))
+ transform_error(F &&f) const & {
+ return map_error_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F>
+ constexpr decltype(map_error_impl(std::declval<const expected &&>(),
+ std::declval<F &&>()))
+ transform_error(F &&f) const && {
+ return map_error_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+ template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
+ return or_else_impl(*this, std::forward<F>(f));
+ }
+
+ template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && {
+ return or_else_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ template <class F> expected constexpr or_else(F &&f) const & {
+ return or_else_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+ template <class F> expected constexpr or_else(F &&f) const && {
+ return or_else_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+ constexpr expected() = default;
+ constexpr expected(const expected &rhs) = default;
+ constexpr expected(expected &&rhs) = default;
+ expected &operator=(const expected &rhs) = default;
+ expected &operator=(expected &&rhs) = default;
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+ nullptr>
+ constexpr expected(in_place_t, Args &&...args)
+ : impl_base(in_place, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr expected(in_place_t, std::initializer_list<U> il, Args &&...args)
+ : impl_base(in_place, il, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class G = E,
+ detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
+ nullptr,
+ detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
+ nullptr>
+ explicit constexpr expected(const unexpected<G> &e)
+ : impl_base(unexpect, e.value()),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <
+ class G = E,
+ detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
+ nullptr,
+ detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr>
+ constexpr expected(unexpected<G> const &e)
+ : impl_base(unexpect, e.value()),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <
+ class G = E,
+ detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
+ detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr>
+ explicit constexpr expected(unexpected<G> &&e) noexcept(
+ std::is_nothrow_constructible<E, G &&>::value)
+ : impl_base(unexpect, std::move(e.value())),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <
+ class G = E,
+ detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
+ detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr>
+ constexpr expected(unexpected<G> &&e) noexcept(
+ std::is_nothrow_constructible<E, G &&>::value)
+ : impl_base(unexpect, std::move(e.value())),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class... Args,
+ detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+ nullptr>
+ constexpr explicit expected(unexpect_t, Args &&...args)
+ : impl_base(unexpect, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_constructible<
+ E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
+ Args &&...args)
+ : impl_base(unexpect, il, std::forward<Args>(args)...),
+ ctor_base(detail::default_constructor_tag{}) {}
+
+ template <class U, class G,
+ detail::enable_if_t<!(std::is_convertible<U const &, T>::value &&
+ std::is_convertible<G const &, E>::value)> * =
+ nullptr,
+ detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
+ * = nullptr>
+ explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ } else {
+ this->construct_error(rhs.error());
+ }
+ }
+
+ template <class U, class G,
+ detail::enable_if_t<(std::is_convertible<U const &, T>::value &&
+ std::is_convertible<G const &, E>::value)> * =
+ nullptr,
+ detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
+ * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ } else {
+ this->construct_error(rhs.error());
+ }
+ }
+
+ template <
+ class U, class G,
+ detail::enable_if_t<!(std::is_convertible<U &&, T>::value &&
+ std::is_convertible<G &&, E>::value)> * = nullptr,
+ detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
+ explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ } else {
+ this->construct_error(std::move(rhs.error()));
+ }
+ }
+
+ template <
+ class U, class G,
+ detail::enable_if_t<(std::is_convertible<U &&, T>::value &&
+ std::is_convertible<G &&, E>::value)> * = nullptr,
+ detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
+ : ctor_base(detail::default_constructor_tag{}) {
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ } else {
+ this->construct_error(std::move(rhs.error()));
+ }
+ }
+
+ template <
+ class U = T,
+ detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::expected_enable_forward_value<T, E, U> * = nullptr>
+ explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
+ : expected(in_place, std::forward<U>(v)) {}
+
+ template <
+ class U = T,
+ detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::expected_enable_forward_value<T, E, U> * = nullptr>
+ TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
+ : expected(in_place, std::forward<U>(v)) {}
+
+ template <
+ class U = T, class G = T,
+ detail::enable_if_t<std::is_nothrow_constructible<T, U &&>::value> * =
+ nullptr,
+ detail::enable_if_t<!std::is_void<G>::value> * = nullptr,
+ detail::enable_if_t<
+ (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+ !detail::conjunction<std::is_scalar<T>,
+ std::is_same<T, detail::decay_t<U>>>::value &&
+ std::is_constructible<T, U>::value &&
+ std::is_assignable<G &, U>::value &&
+ std::is_nothrow_move_constructible<E>::value)> * = nullptr>
+ expected &operator=(U &&v) {
+ if (has_value()) {
+ val() = std::forward<U>(v);
+ } else {
+ err().~unexpected<E>();
+ ::new (valptr()) T(std::forward<U>(v));
+ this->m_has_val = true;
+ }
+
+ return *this;
+ }
+
+ template <
+ class U = T, class G = T,
+ detail::enable_if_t<!std::is_nothrow_constructible<T, U &&>::value> * =
+ nullptr,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr,
+ detail::enable_if_t<
+ (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+ !detail::conjunction<std::is_scalar<T>,
+ std::is_same<T, detail::decay_t<U>>>::value &&
+ std::is_constructible<T, U>::value &&
+ std::is_assignable<G &, U>::value &&
+ std::is_nothrow_move_constructible<E>::value)> * = nullptr>
+ expected &operator=(U &&v) {
+ if (has_value()) {
+ val() = std::forward<U>(v);
+ } else {
+ auto tmp = std::move(err());
+ err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (valptr()) T(std::forward<U>(v));
+ this->m_has_val = true;
+ } catch (...) {
+ err() = std::move(tmp);
+ throw;
+ }
+#else
+ ::new (valptr()) T(std::forward<U>(v));
+ this->m_has_val = true;
+#endif
+ }
+
+ return *this;
+ }
+
+ template <class G = E,
+ detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
+ std::is_assignable<G &, G>::value> * = nullptr>
+ expected &operator=(const unexpected<G> &rhs) {
+ if (!has_value()) {
+ err() = rhs;
+ } else {
+ this->destroy_val();
+ ::new (errptr()) unexpected<E>(rhs);
+ this->m_has_val = false;
+ }
+
+ return *this;
+ }
+
+ template <class G = E,
+ detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
+ std::is_move_assignable<G>::value> * = nullptr>
+ expected &operator=(unexpected<G> &&rhs) noexcept {
+ if (!has_value()) {
+ err() = std::move(rhs);
+ } else {
+ this->destroy_val();
+ ::new (errptr()) unexpected<E>(std::move(rhs));
+ this->m_has_val = false;
+ }
+
+ return *this;
+ }
+
+ template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
+ T, Args &&...>::value> * = nullptr>
+ void emplace(Args &&...args) {
+ if (has_value()) {
+ val().~T();
+ } else {
+ err().~unexpected<E>();
+ this->m_has_val = true;
+ }
+ ::new (valptr()) T(std::forward<Args>(args)...);
+ }
+
+ template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
+ T, Args &&...>::value> * = nullptr>
+ void emplace(Args &&...args) {
+ if (has_value()) {
+ val().~T();
+ ::new (valptr()) T(std::forward<Args>(args)...);
+ } else {
+ auto tmp = std::move(err());
+ err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (valptr()) T(std::forward<Args>(args)...);
+ this->m_has_val = true;
+ } catch (...) {
+ err() = std::move(tmp);
+ throw;
+ }
+#else
+ ::new (valptr()) T(std::forward<Args>(args)...);
+ this->m_has_val = true;
+#endif
+ }
+ }
+
+ template <class U, class... Args,
+ detail::enable_if_t<std::is_nothrow_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ void emplace(std::initializer_list<U> il, Args &&...args) {
+ if (has_value()) {
+ T t(il, std::forward<Args>(args)...);
+ val() = std::move(t);
+ } else {
+ err().~unexpected<E>();
+ ::new (valptr()) T(il, std::forward<Args>(args)...);
+ this->m_has_val = true;
+ }
+ }
+
+ template <class U, class... Args,
+ detail::enable_if_t<!std::is_nothrow_constructible<
+ T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+ void emplace(std::initializer_list<U> il, Args &&...args) {
+ if (has_value()) {
+ T t(il, std::forward<Args>(args)...);
+ val() = std::move(t);
+ } else {
+ auto tmp = std::move(err());
+ err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (valptr()) T(il, std::forward<Args>(args)...);
+ this->m_has_val = true;
+ } catch (...) {
+ err() = std::move(tmp);
+ throw;
+ }
+#else
+ ::new (valptr()) T(il, std::forward<Args>(args)...);
+ this->m_has_val = true;
+#endif
+ }
+ }
+
+private:
+ using t_is_void = std::true_type;
+ using t_is_not_void = std::false_type;
+ using t_is_nothrow_move_constructible = std::true_type;
+ using move_constructing_t_can_throw = std::false_type;
+ using e_is_nothrow_move_constructible = std::true_type;
+ using move_constructing_e_can_throw = std::false_type;
+
+ void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept {
+ // swapping void is a no-op
+ }
+
+ void swap_where_both_have_value(expected &rhs, t_is_not_void) {
+ using std::swap;
+ swap(val(), rhs.val());
+ }
+
+ void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept(
+ std::is_nothrow_move_constructible<E>::value) {
+ ::new (errptr()) unexpected_type(std::move(rhs.err()));
+ rhs.err().~unexpected_type();
+ std::swap(this->m_has_val, rhs.m_has_val);
+ }
+
+ void swap_where_only_one_has_value(expected &rhs, t_is_not_void) {
+ swap_where_only_one_has_value_and_t_is_not_void(
+ rhs, typename std::is_nothrow_move_constructible<T>::type{},
+ typename std::is_nothrow_move_constructible<E>::type{});
+ }
+
+ void swap_where_only_one_has_value_and_t_is_not_void(
+ expected &rhs, t_is_nothrow_move_constructible,
+ e_is_nothrow_move_constructible) noexcept {
+ auto temp = std::move(val());
+ val().~T();
+ ::new (errptr()) unexpected_type(std::move(rhs.err()));
+ rhs.err().~unexpected_type();
+ ::new (rhs.valptr()) T(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+ }
+
+ void swap_where_only_one_has_value_and_t_is_not_void(
+ expected &rhs, t_is_nothrow_move_constructible,
+ move_constructing_e_can_throw) {
+ auto temp = std::move(val());
+ val().~T();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (errptr()) unexpected_type(std::move(rhs.err()));
+ rhs.err().~unexpected_type();
+ ::new (rhs.valptr()) T(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+ } catch (...) {
+ val() = std::move(temp);
+ throw;
+ }
+#else
+ ::new (errptr()) unexpected_type(std::move(rhs.err()));
+ rhs.err().~unexpected_type();
+ ::new (rhs.valptr()) T(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+#endif
+ }
+
+ void swap_where_only_one_has_value_and_t_is_not_void(
+ expected &rhs, move_constructing_t_can_throw,
+ e_is_nothrow_move_constructible) {
+ auto temp = std::move(rhs.err());
+ rhs.err().~unexpected_type();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ try {
+ ::new (rhs.valptr()) T(std::move(val()));
+ val().~T();
+ ::new (errptr()) unexpected_type(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+ } catch (...) {
+ rhs.err() = std::move(temp);
+ throw;
+ }
+#else
+ ::new (rhs.valptr()) T(std::move(val()));
+ val().~T();
+ ::new (errptr()) unexpected_type(std::move(temp));
+ std::swap(this->m_has_val, rhs.m_has_val);
+#endif
+ }
+
+public:
+ template <class OT = T, class OE = E>
+ detail::enable_if_t<detail::is_swappable<OT>::value &&
+ detail::is_swappable<OE>::value &&
+ (std::is_nothrow_move_constructible<OT>::value ||
+ std::is_nothrow_move_constructible<OE>::value)>
+ swap(expected &rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value
+ &&detail::is_nothrow_swappable<T>::value
+ &&std::is_nothrow_move_constructible<E>::value
+ &&detail::is_nothrow_swappable<E>::value) {
+ if (has_value() && rhs.has_value()) {
+ swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
+ } else if (!has_value() && rhs.has_value()) {
+ rhs.swap(*this);
+ } else if (has_value()) {
+ swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
+ } else {
+ using std::swap;
+ swap(err(), rhs.err());
+ }
+ }
+
+ constexpr const T *operator->() const {
+ TL_ASSERT(has_value());
+ return valptr();
+ }
+ TL_EXPECTED_11_CONSTEXPR T *operator->() {
+ TL_ASSERT(has_value());
+ return valptr();
+ }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ constexpr const U &operator*() const & {
+ TL_ASSERT(has_value());
+ return val();
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &operator*() & {
+ TL_ASSERT(has_value());
+ return val();
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ constexpr const U &&operator*() const && {
+ TL_ASSERT(has_value());
+ return std::move(val());
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &&operator*() && {
+ TL_ASSERT(has_value());
+ return std::move(val());
+ }
+
+ constexpr bool has_value() const noexcept { return this->m_has_val; }
+ constexpr explicit operator bool() const noexcept { return this->m_has_val; }
+
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR const U &value() const & {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(err().value()));
+ return val();
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &value() & {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(err().value()));
+ return val();
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR const U &&value() const && {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
+ return std::move(val());
+ }
+ template <class U = T,
+ detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+ TL_EXPECTED_11_CONSTEXPR U &&value() && {
+ if (!has_value())
+ detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
+ return std::move(val());
+ }
+
+ constexpr const E &error() const & {
+ TL_ASSERT(!has_value());
+ return err().value();
+ }
+ TL_EXPECTED_11_CONSTEXPR E &error() & {
+ TL_ASSERT(!has_value());
+ return err().value();
+ }
+ constexpr const E &&error() const && {
+ TL_ASSERT(!has_value());
+ return std::move(err().value());
+ }
+ TL_EXPECTED_11_CONSTEXPR E &&error() && {
+ TL_ASSERT(!has_value());
+ return std::move(err().value());
+ }
+
+ template <class U> constexpr T value_or(U &&v) const & {
+ static_assert(std::is_copy_constructible<T>::value &&
+ std::is_convertible<U &&, T>::value,
+ "T must be copy-constructible and convertible to from U&&");
+ return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
+ }
+ template <class U> TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && {
+ static_assert(std::is_move_constructible<T>::value &&
+ std::is_convertible<U &&, T>::value,
+ "T must be move-constructible and convertible to from U&&");
+ return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
+ }
+};
+
+namespace detail {
+template <class Exp> using exp_t = typename detail::decay_t<Exp>::value_type;
+template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type;
+template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>;
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>()))>
+constexpr auto and_then_impl(Exp &&exp, F &&f) {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value()
+ ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
+ : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>()))>
+constexpr auto and_then_impl(Exp &&exp, F &&f) {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value() ? detail::invoke(std::forward<F>(f))
+ : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+#else
+template <class> struct TC;
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr>
+auto and_then_impl(Exp &&exp, F &&f) -> Ret {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value()
+ ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
+ : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr>
+constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+ return exp.has_value() ? detail::invoke(std::forward<F>(f))
+ : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+#endif
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
+ *std::forward<Exp>(exp)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = expected<void, err_t<Exp>>;
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
+ return result();
+ }
+
+ return result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto expected_map_impl(Exp &&exp, F &&f) {
+ using result = expected<void, err_t<Exp>>;
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f));
+ return result();
+ }
+
+ return result(unexpect, std::forward<Exp>(exp).error());
+}
+#else
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+
+constexpr auto expected_map_impl(Exp &&exp, F &&f)
+ -> ret_t<Exp, detail::decay_t<Ret>> {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
+ *std::forward<Exp>(exp)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ *std::declval<Exp>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+
+auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
+ return {};
+ }
+
+ return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+
+constexpr auto expected_map_impl(Exp &&exp, F &&f)
+ -> ret_t<Exp, detail::decay_t<Ret>> {
+ using result = ret_t<Exp, detail::decay_t<Ret>>;
+
+ return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
+ : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+
+auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
+ if (exp.has_value()) {
+ detail::invoke(std::forward<F>(f));
+ return {};
+ }
+
+ return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
+}
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \
+ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+ return exp.has_value()
+ ? result(*std::forward<Exp>(exp))
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result(*std::forward<Exp>(exp));
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+ return exp.has_value()
+ ? result()
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result();
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+#else
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f)
+ -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+
+ return exp.has_value()
+ ? result(*std::forward<Exp>(exp))
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result(*std::forward<Exp>(exp));
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f)
+ -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
+ using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+
+ return exp.has_value()
+ ? result()
+ : result(unexpect, detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()));
+}
+
+template <class Exp, class F,
+ detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
+ using result = expected<exp_t<Exp>, monostate>;
+ if (exp.has_value()) {
+ return result();
+ }
+
+ detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+ return result(unexpect, monostate{});
+}
+#endif
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto or_else_impl(Exp &&exp, F &&f) {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+ return exp.has_value() ? std::forward<Exp>(exp)
+ : detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
+ return exp.has_value() ? std::forward<Exp>(exp)
+ : (detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()),
+ std::forward<Exp>(exp));
+}
+#else
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+auto or_else_impl(Exp &&exp, F &&f) -> Ret {
+ static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+ return exp.has_value() ? std::forward<Exp>(exp)
+ : detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+ class Ret = decltype(detail::invoke(std::declval<F>(),
+ std::declval<Exp>().error())),
+ detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
+ return exp.has_value() ? std::forward<Exp>(exp)
+ : (detail::invoke(std::forward<F>(f),
+ std::forward<Exp>(exp).error()),
+ std::forward<Exp>(exp));
+}
+#endif
+} // namespace detail
+
+template <class T, class E, class U, class F>
+constexpr bool operator==(const expected<T, E> &lhs,
+ const expected<U, F> &rhs) {
+ return (lhs.has_value() != rhs.has_value())
+ ? false
+ : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
+}
+template <class T, class E, class U, class F>
+constexpr bool operator!=(const expected<T, E> &lhs,
+ const expected<U, F> &rhs) {
+ return (lhs.has_value() != rhs.has_value())
+ ? true
+ : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
+}
+template <class E, class F>
+constexpr bool operator==(const expected<void, E> &lhs,
+ const expected<void, F> &rhs) {
+ return (lhs.has_value() != rhs.has_value())
+ ? false
+ : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
+}
+template <class E, class F>
+constexpr bool operator!=(const expected<void, E> &lhs,
+ const expected<void, F> &rhs) {
+ return (lhs.has_value() != rhs.has_value())
+ ? true
+ : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
+}
+
+template <class T, class E, class U>
+constexpr bool operator==(const expected<T, E> &x, const U &v) {
+ return x.has_value() ? *x == v : false;
+}
+template <class T, class E, class U>
+constexpr bool operator==(const U &v, const expected<T, E> &x) {
+ return x.has_value() ? *x == v : false;
+}
+template <class T, class E, class U>
+constexpr bool operator!=(const expected<T, E> &x, const U &v) {
+ return x.has_value() ? *x != v : true;
+}
+template <class T, class E, class U>
+constexpr bool operator!=(const U &v, const expected<T, E> &x) {
+ return x.has_value() ? *x != v : true;
+}
+
+template <class T, class E>
+constexpr bool operator==(const expected<T, E> &x, const unexpected<E> &e) {
+ return x.has_value() ? false : x.error() == e.value();
+}
+template <class T, class E>
+constexpr bool operator==(const unexpected<E> &e, const expected<T, E> &x) {
+ return x.has_value() ? false : x.error() == e.value();
+}
+template <class T, class E>
+constexpr bool operator!=(const expected<T, E> &x, const unexpected<E> &e) {
+ return x.has_value() ? true : x.error() != e.value();
+}
+template <class T, class E>
+constexpr bool operator!=(const unexpected<E> &e, const expected<T, E> &x) {
+ return x.has_value() ? true : x.error() != e.value();
+}
+
+template <class T, class E,
+ detail::enable_if_t<(std::is_void<T>::value ||
+ std::is_move_constructible<T>::value) &&
+ detail::is_swappable<T>::value &&
+ std::is_move_constructible<E>::value &&
+ detail::is_swappable<E>::value> * = nullptr>
+void swap(expected<T, E> &lhs,
+ expected<T, E> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
+ lhs.swap(rhs);
+}
+} // namespace tl
+
+#endif
diff --git a/src/libs/3rdparty/variant/LICENSE.md b/src/libs/3rdparty/variant/LICENSE.md
deleted file mode 100644
index 36b7cd93cd..0000000000
--- a/src/libs/3rdparty/variant/LICENSE.md
+++ /dev/null
@@ -1,23 +0,0 @@
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/src/libs/3rdparty/variant/README.md b/src/libs/3rdparty/variant/README.md
deleted file mode 100644
index 6644286d27..0000000000
--- a/src/libs/3rdparty/variant/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# MPark.Variant
-
-> __C++17__ `std::variant` for __C++11__/__14__/__17__
-
-[![release][badge.release]][release]
-[![header][badge.header]][header]
-[![travis][badge.travis]][travis]
-[![appveyor][badge.appveyor]][appveyor]
-[![license][badge.license]][license]
-[![godbolt][badge.godbolt]][godbolt]
-[![wandbox][badge.wandbox]][wandbox]
-
-[badge.release]: https://img.shields.io/github/release/mpark/variant.svg
-[badge.header]: https://img.shields.io/badge/single%20header-master-blue.svg
-[badge.travis]: https://travis-ci.org/mpark/variant.svg?branch=master
-[badge.appveyor]: https://ci.appveyor.com/api/projects/status/github/mpark/variant?branch=master&svg=true
-[badge.license]: https://img.shields.io/badge/license-boost-blue.svg
-[badge.godbolt]: https://img.shields.io/badge/try%20it-on%20godbolt-222266.svg
-[badge.wandbox]: https://img.shields.io/badge/try%20it-on%20wandbox-5cb85c.svg
-
-[release]: https://github.com/mpark/variant/releases/latest
-[header]: https://github.com/mpark/variant/blob/single-header/master/variant.hpp
-[travis]: https://travis-ci.org/mpark/variant
-[appveyor]: https://ci.appveyor.com/project/mpark/variant
-[license]: https://github.com/mpark/variant/blob/master/LICENSE.md
-[godbolt]: https://godbolt.org/g/1qYDAK
-[wandbox]: https://wandbox.org/permlink/QV3gZ2KQQNwgoFIB
-
-## Single Header
-
-This branch provides a standalone `variant.hpp` file for each
-[release](https://github.com/mpark/variant/releases).
-Copy it and `#include` away!
-
-## License
-
-Distributed under the [Boost Software License, Version 1.0](LICENSE.md).
diff --git a/src/libs/3rdparty/variant/variant.hpp b/src/libs/3rdparty/variant/variant.hpp
deleted file mode 100644
index dca26986c9..0000000000
--- a/src/libs/3rdparty/variant/variant.hpp
+++ /dev/null
@@ -1,2465 +0,0 @@
-// MPark.Variant
-//
-// Copyright Michael Park, 2015-2017
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
-
-#ifndef MPARK_VARIANT_HPP
-#define MPARK_VARIANT_HPP
-
-#if defined(__GNUC__) && __GNUC__ >= 9
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-copy"
-#endif
-
-/*
- variant synopsis
-
-namespace std {
-
- // 20.7.2, class template variant
- template <class... Types>
- class variant {
- public:
-
- // 20.7.2.1, constructors
- constexpr variant() noexcept(see below);
- variant(const variant&);
- variant(variant&&) noexcept(see below);
-
- template <class T> constexpr variant(T&&) noexcept(see below);
-
- template <class T, class... Args>
- constexpr explicit variant(in_place_type_t<T>, Args&&...);
-
- template <class T, class U, class... Args>
- constexpr explicit variant(
- in_place_type_t<T>, initializer_list<U>, Args&&...);
-
- template <size_t I, class... Args>
- constexpr explicit variant(in_place_index_t<I>, Args&&...);
-
- template <size_t I, class U, class... Args>
- constexpr explicit variant(
- in_place_index_t<I>, initializer_list<U>, Args&&...);
-
- // 20.7.2.2, destructor
- ~variant();
-
- // 20.7.2.3, assignment
- variant& operator=(const variant&);
- variant& operator=(variant&&) noexcept(see below);
-
- template <class T> variant& operator=(T&&) noexcept(see below);
-
- // 20.7.2.4, modifiers
- template <class T, class... Args>
- T& emplace(Args&&...);
-
- template <class T, class U, class... Args>
- T& emplace(initializer_list<U>, Args&&...);
-
- template <size_t I, class... Args>
- variant_alternative<I, variant>& emplace(Args&&...);
-
- template <size_t I, class U, class... Args>
- variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);
-
- // 20.7.2.5, value status
- constexpr bool valueless_by_exception() const noexcept;
- constexpr size_t index() const noexcept;
-
- // 20.7.2.6, swap
- void swap(variant&) noexcept(see below);
- };
-
- // 20.7.3, variant helper classes
- template <class T> struct variant_size; // undefined
-
- template <class T>
- constexpr size_t variant_size_v = variant_size<T>::value;
-
- template <class T> struct variant_size<const T>;
- template <class T> struct variant_size<volatile T>;
- template <class T> struct variant_size<const volatile T>;
-
- template <class... Types>
- struct variant_size<variant<Types...>>;
-
- template <size_t I, class T> struct variant_alternative; // undefined
-
- template <size_t I, class T>
- using variant_alternative_t = typename variant_alternative<I, T>::type;
-
- template <size_t I, class T> struct variant_alternative<I, const T>;
- template <size_t I, class T> struct variant_alternative<I, volatile T>;
- template <size_t I, class T> struct variant_alternative<I, const volatile T>;
-
- template <size_t I, class... Types>
- struct variant_alternative<I, variant<Types...>>;
-
- constexpr size_t variant_npos = -1;
-
- // 20.7.4, value access
- template <class T, class... Types>
- constexpr bool holds_alternative(const variant<Types...>&) noexcept;
-
- template <size_t I, class... Types>
- constexpr variant_alternative_t<I, variant<Types...>>&
- get(variant<Types...>&);
-
- template <size_t I, class... Types>
- constexpr variant_alternative_t<I, variant<Types...>>&&
- get(variant<Types...>&&);
-
- template <size_t I, class... Types>
- constexpr variant_alternative_t<I, variant<Types...>> const&
- get(const variant<Types...>&);
-
- template <size_t I, class... Types>
- constexpr variant_alternative_t<I, variant<Types...>> const&&
- get(const variant<Types...>&&);
-
- template <class T, class... Types>
- constexpr T& get(variant<Types...>&);
-
- template <class T, class... Types>
- constexpr T&& get(variant<Types...>&&);
-
- template <class T, class... Types>
- constexpr const T& get(const variant<Types...>&);
-
- template <class T, class... Types>
- constexpr const T&& get(const variant<Types...>&&);
-
- template <size_t I, class... Types>
- constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
- get_if(variant<Types...>*) noexcept;
-
- template <size_t I, class... Types>
- constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
- get_if(const variant<Types...>*) noexcept;
-
- template <class T, class... Types>
- constexpr add_pointer_t<T>
- get_if(variant<Types...>*) noexcept;
-
- template <class T, class... Types>
- constexpr add_pointer_t<const T>
- get_if(const variant<Types...>*) noexcept;
-
- // 20.7.5, relational operators
- template <class... Types>
- constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
-
- template <class... Types>
- constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
-
- template <class... Types>
- constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
-
- template <class... Types>
- constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
-
- template <class... Types>
- constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
-
- template <class... Types>
- constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
-
- // 20.7.6, visitation
- template <class Visitor, class... Variants>
- constexpr see below visit(Visitor&&, Variants&&...);
-
- // 20.7.7, class monostate
- struct monostate;
-
- // 20.7.8, monostate relational operators
- constexpr bool operator<(monostate, monostate) noexcept;
- constexpr bool operator>(monostate, monostate) noexcept;
- constexpr bool operator<=(monostate, monostate) noexcept;
- constexpr bool operator>=(monostate, monostate) noexcept;
- constexpr bool operator==(monostate, monostate) noexcept;
- constexpr bool operator!=(monostate, monostate) noexcept;
-
- // 20.7.9, specialized algorithms
- template <class... Types>
- void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
-
- // 20.7.10, class bad_variant_access
- class bad_variant_access;
-
- // 20.7.11, hash support
- template <class T> struct hash;
- template <class... Types> struct hash<variant<Types...>>;
- template <> struct hash<monostate>;
-
-} // namespace std
-
-*/
-
-#include <cstddef>
-#include <exception>
-#include <functional>
-#include <initializer_list>
-#include <new>
-#include <type_traits>
-#include <utility>
-
-// MPark.Variant
-//
-// Copyright Michael Park, 2015-2017
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
-
-#ifndef MPARK_CONFIG_HPP
-#define MPARK_CONFIG_HPP
-
-// MSVC 2015 Update 3.
-#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024210)
-#error "MPark.Variant requires C++11 support."
-#endif
-
-#ifndef __has_builtin
-#define __has_builtin(x) 0
-#endif
-
-#ifndef __has_include
-#define __has_include(x) 0
-#endif
-
-#ifndef __has_feature
-#define __has_feature(x) 0
-#endif
-
-#if __has_builtin(__builtin_addressof) || \
- (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER)
-#define MPARK_BUILTIN_ADDRESSOF
-#endif
-
-#if __has_builtin(__builtin_unreachable)
-#define MPARK_BUILTIN_UNREACHABLE
-#endif
-
-#if __has_builtin(__type_pack_element)
-#define MPARK_TYPE_PACK_ELEMENT
-#endif
-
-#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
-#if !defined(_MSC_VER) || _MSC_VER < 1915 // compile issue in msvc 2017 update 8
-#define MPARK_CPP14_CONSTEXPR
-#endif
-#endif
-
-#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \
- (defined(_MSC_VER) && defined(_CPPUNWIND))
-#define MPARK_EXCEPTIONS
-#endif
-
-#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
-#define MPARK_GENERIC_LAMBDAS
-#endif
-
-#if defined(__cpp_lib_integer_sequence)
-#define MPARK_INTEGER_SEQUENCE
-#endif
-
-#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
-#define MPARK_RETURN_TYPE_DEDUCTION
-#endif
-
-#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
-#define MPARK_TRANSPARENT_OPERATORS
-#endif
-
-#if defined(__cpp_variable_templates) || defined(_MSC_VER)
-#define MPARK_VARIABLE_TEMPLATES
-#endif
-
-#if !defined(__GLIBCXX__) || __has_include(<codecvt>) // >= libstdc++-5
-#define MPARK_TRIVIALITY_TYPE_TRAITS
-#endif
-
-#endif // MPARK_CONFIG_HPP
-
-// MPark.Variant
-//
-// Copyright Michael Park, 2015-2017
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
-
-#ifndef MPARK_IN_PLACE_HPP
-#define MPARK_IN_PLACE_HPP
-
-#include <cstddef>
-
-
-namespace mpark {
-
- struct in_place_t { explicit in_place_t() = default; };
-
- template <std::size_t I>
- struct in_place_index_t { explicit in_place_index_t() = default; };
-
- template <typename T>
- struct in_place_type_t { explicit in_place_type_t() = default; };
-
-#ifdef MPARK_VARIABLE_TEMPLATES
- constexpr in_place_t in_place{};
-
- template <std::size_t I> constexpr in_place_index_t<I> in_place_index{};
-
- template <typename T> constexpr in_place_type_t<T> in_place_type{};
-#endif
-
-} // namespace mpark
-
-#endif // MPARK_IN_PLACE_HPP
-
-// MPark.Variant
-//
-// Copyright Michael Park, 2015-2017
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
-
-#ifndef MPARK_LIB_HPP
-#define MPARK_LIB_HPP
-
-#include <memory>
-#include <functional>
-#include <type_traits>
-#include <utility>
-
-
-#define RETURN(...) \
- noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { \
- return __VA_ARGS__; \
- }
-
-namespace mpark {
- namespace lib {
- template <typename T>
- struct identity { using type = T; };
-
- inline namespace cpp14 {
- template <typename T, std::size_t N>
- struct array {
- constexpr const T &operator[](std::size_t index) const {
- return data[index];
- }
-
- T data[N == 0 ? 1 : N];
- };
-
- template <typename T>
- using add_pointer_t = typename std::add_pointer<T>::type;
-
- template <typename... Ts>
- using common_type_t = typename std::common_type<Ts...>::type;
-
- template <typename T>
- using decay_t = typename std::decay<T>::type;
-
- template <bool B, typename T = void>
- using enable_if_t = typename std::enable_if<B, T>::type;
-
- template <typename T>
- using remove_const_t = typename std::remove_const<T>::type;
-
- template <typename T>
- using remove_reference_t = typename std::remove_reference<T>::type;
-
- template <typename T>
- inline constexpr T &&forward(remove_reference_t<T> &t) noexcept {
- return static_cast<T &&>(t);
- }
-
- template <typename T>
- inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
- static_assert(!std::is_lvalue_reference<T>::value,
- "can not forward an rvalue as an lvalue");
- return static_cast<T &&>(t);
- }
-
- template <typename T>
- inline constexpr remove_reference_t<T> &&move(T &&t) noexcept {
- return static_cast<remove_reference_t<T> &&>(t);
- }
-
-#ifdef MPARK_INTEGER_SEQUENCE
- using std::integer_sequence;
- using std::index_sequence;
- using std::make_index_sequence;
- using std::index_sequence_for;
-#else
- template <typename T, T... Is>
- struct integer_sequence {
- using value_type = T;
- static constexpr std::size_t size() noexcept { return sizeof...(Is); }
- };
-
- template <std::size_t... Is>
- using index_sequence = integer_sequence<std::size_t, Is...>;
-
- template <typename Lhs, typename Rhs>
- struct make_index_sequence_concat;
-
- template <std::size_t... Lhs, std::size_t... Rhs>
- struct make_index_sequence_concat<index_sequence<Lhs...>,
- index_sequence<Rhs...>>
- : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {};
-
- template <std::size_t N>
- struct make_index_sequence_impl;
-
- template <std::size_t N>
- using make_index_sequence = typename make_index_sequence_impl<N>::type;
-
- template <std::size_t N>
- struct make_index_sequence_impl
- : make_index_sequence_concat<make_index_sequence<N / 2>,
- make_index_sequence<N - (N / 2)>> {};
-
- template <>
- struct make_index_sequence_impl<0> : identity<index_sequence<>> {};
-
- template <>
- struct make_index_sequence_impl<1> : identity<index_sequence<0>> {};
-
- template <typename... Ts>
- using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
-#endif
-
- // <functional>
-#ifdef MPARK_TRANSPARENT_OPERATORS
- using equal_to = std::equal_to<>;
-#else
- struct equal_to {
- template <typename Lhs, typename Rhs>
- inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
- RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
- };
-#endif
-
-#ifdef MPARK_TRANSPARENT_OPERATORS
- using not_equal_to = std::not_equal_to<>;
-#else
- struct not_equal_to {
- template <typename Lhs, typename Rhs>
- inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
- RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
- };
-#endif
-
-#ifdef MPARK_TRANSPARENT_OPERATORS
- using less = std::less<>;
-#else
- struct less {
- template <typename Lhs, typename Rhs>
- inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
- RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
- };
-#endif
-
-#ifdef MPARK_TRANSPARENT_OPERATORS
- using greater = std::greater<>;
-#else
- struct greater {
- template <typename Lhs, typename Rhs>
- inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
- RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
- };
-#endif
-
-#ifdef MPARK_TRANSPARENT_OPERATORS
- using less_equal = std::less_equal<>;
-#else
- struct less_equal {
- template <typename Lhs, typename Rhs>
- inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
- RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
- };
-#endif
-
-#ifdef MPARK_TRANSPARENT_OPERATORS
- using greater_equal = std::greater_equal<>;
-#else
- struct greater_equal {
- template <typename Lhs, typename Rhs>
- inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
- RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
- };
-#endif
- } // namespace cpp14
-
- inline namespace cpp17 {
-
- // <type_traits>
- template <bool B>
- using bool_constant = std::integral_constant<bool, B>;
-
- template <typename...>
- struct voider : identity<void> {};
-
- template <typename... Ts>
- using void_t = typename voider<Ts...>::type;
-
- namespace detail {
- namespace swappable {
-
- using std::swap;
-
- template <typename T>
- struct is_swappable {
- private:
- template <typename U,
- typename = decltype(swap(std::declval<U &>(),
- std::declval<U &>()))>
- inline static std::true_type test(int);
-
- template <typename U>
- inline static std::false_type test(...);
-
- public:
- static constexpr bool value = decltype(test<T>(0))::value;
- };
-
- template <typename T, bool = is_swappable<T>::value>
- struct is_nothrow_swappable {
- static constexpr bool value =
- noexcept(swap(std::declval<T &>(), std::declval<T &>()));
- };
-
- template <typename T>
- struct is_nothrow_swappable<T, false> : std::false_type {};
-
- } // namespace swappable
- } // namespace detail
-
- using detail::swappable::is_swappable;
- using detail::swappable::is_nothrow_swappable;
-
- // <functional>
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4100)
-#endif
- template <typename F, typename... As>
- inline constexpr auto invoke(F &&f, As &&... as)
- RETURN(lib::forward<F>(f)(lib::forward<As>(as)...))
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
- template <typename B, typename T, typename D>
- inline constexpr auto invoke(T B::*pmv, D &&d)
- RETURN(lib::forward<D>(d).*pmv)
-
- template <typename Pmv, typename Ptr>
- inline constexpr auto invoke(Pmv pmv, Ptr &&ptr)
- RETURN((*lib::forward<Ptr>(ptr)).*pmv)
-
- template <typename B, typename T, typename D, typename... As>
- inline constexpr auto invoke(T B::*pmf, D &&d, As &&... as)
- RETURN((lib::forward<D>(d).*pmf)(lib::forward<As>(as)...))
-
- template <typename Pmf, typename Ptr, typename... As>
- inline constexpr auto invoke(Pmf pmf, Ptr &&ptr, As &&... as)
- RETURN(((*lib::forward<Ptr>(ptr)).*pmf)(lib::forward<As>(as)...))
-
- namespace detail {
-
- template <typename Void, typename, typename...>
- struct invoke_result {};
-
- template <typename F, typename... Args>
- struct invoke_result<void_t<decltype(lib::invoke(
- std::declval<F>(), std::declval<Args>()...))>,
- F,
- Args...>
- : identity<decltype(
- lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
-
- } // namespace detail
-
- template <typename F, typename... Args>
- using invoke_result = detail::invoke_result<void, F, Args...>;
-
- template <typename F, typename... Args>
- using invoke_result_t = typename invoke_result<F, Args...>::type;
-
- namespace detail {
-
- template <typename Void, typename, typename...>
- struct is_invocable : std::false_type {};
-
- template <typename F, typename... Args>
- struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
- : std::true_type {};
-
- template <typename Void, typename, typename, typename...>
- struct is_invocable_r : std::false_type {};
-
- template <typename R, typename F, typename... Args>
- struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
- R,
- F,
- Args...>
- : std::is_convertible<invoke_result_t<F, Args...>, R> {};
-
- } // namespace detail
-
- template <typename F, typename... Args>
- using is_invocable = detail::is_invocable<void, F, Args...>;
-
- template <typename R, typename F, typename... Args>
- using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
-
- // <memory>
-#ifdef MPARK_BUILTIN_ADDRESSOF
- template <typename T>
- inline constexpr T *addressof(T &arg) {
- return __builtin_addressof(arg);
- }
-#else
- namespace detail {
-
- namespace has_addressof_impl {
-
- struct fail;
-
- template <typename T>
- inline fail operator&(T &&);
-
- template <typename T>
- inline static constexpr bool impl() {
- return (std::is_class<T>::value || std::is_union<T>::value) &&
- !std::is_same<decltype(&std::declval<T &>()), fail>::value;
- }
-
- } // namespace has_addressof_impl
-
- template <typename T>
- using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
-
- template <typename T>
- inline constexpr T *addressof(T &arg, std::true_type) {
- return std::addressof(arg);
- }
-
- template <typename T>
- inline constexpr T *addressof(T &arg, std::false_type) {
- return &arg;
- }
-
- } // namespace detail
-
- template <typename T>
- inline constexpr T *addressof(T &arg) {
- return detail::addressof(arg, detail::has_addressof<T>{});
- }
-#endif
-
- template <typename T>
- inline constexpr T *addressof(const T &&) = delete;
-
- } // namespace cpp17
-
- template <typename T>
- struct remove_all_extents : identity<T> {};
-
- template <typename T, std::size_t N>
- struct remove_all_extents<array<T, N>> : remove_all_extents<T> {};
-
- template <typename T>
- using remove_all_extents_t = typename remove_all_extents<T>::type;
-
- template <std::size_t N>
- using size_constant = std::integral_constant<std::size_t, N>;
-
- template <std::size_t I, typename T>
- struct indexed_type : size_constant<I>, identity<T> {};
-
- template <bool... Bs>
- using all = std::is_same<integer_sequence<bool, true, Bs...>,
- integer_sequence<bool, Bs..., true>>;
-
-#ifdef MPARK_TYPE_PACK_ELEMENT
- template <std::size_t I, typename... Ts>
- using type_pack_element_t = __type_pack_element<I, Ts...>;
-#else
- template <std::size_t I, typename... Ts>
- struct type_pack_element_impl {
- private:
- template <typename>
- struct set;
-
- template <std::size_t... Is>
- struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {};
-
- template <typename T>
- inline static std::enable_if<true, T> impl(indexed_type<I, T>);
-
- inline static std::enable_if<false> impl(...);
-
- public:
- using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
- };
-
- template <std::size_t I, typename... Ts>
- using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
-
- template <std::size_t I, typename... Ts>
- using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
-#endif
-
-#ifdef MPARK_TRIVIALITY_TYPE_TRAITS
- using std::is_trivially_copy_constructible;
- using std::is_trivially_move_constructible;
- using std::is_trivially_copy_assignable;
- using std::is_trivially_move_assignable;
-#else
- template <typename T>
- struct is_trivially_copy_constructible
- : bool_constant<
- std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {};
-
- template <typename T>
- struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {};
-
- template <typename T>
- struct is_trivially_copy_assignable
- : bool_constant<
- std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {};
-
- template <typename T>
- struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {};
-#endif
-
- template <typename T, bool>
- struct dependent_type : T {};
-
- template <typename Is, std::size_t J>
- struct push_back;
-
- template <typename Is, std::size_t J>
- using push_back_t = typename push_back<Is, J>::type;
-
- template <std::size_t... Is, std::size_t J>
- struct push_back<index_sequence<Is...>, J> {
- using type = index_sequence<Is..., J>;
- };
-
- } // namespace lib
-} // namespace mpark
-
-#undef RETURN
-
-#endif // MPARK_LIB_HPP
-
-
-namespace mpark {
-
-#ifdef MPARK_RETURN_TYPE_DEDUCTION
-
-#define AUTO auto
-#define AUTO_RETURN(...) { return __VA_ARGS__; }
-
-#define AUTO_REFREF auto &&
-#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; }
-
-#define DECLTYPE_AUTO decltype(auto)
-#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; }
-
-#else
-
-#define AUTO auto
-#define AUTO_RETURN(...) \
- -> lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; }
-
-#define AUTO_REFREF auto
-#define AUTO_REFREF_RETURN(...) \
- -> decltype((__VA_ARGS__)) { \
- static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \
- return __VA_ARGS__; \
- }
-
-#define DECLTYPE_AUTO auto
-#define DECLTYPE_AUTO_RETURN(...) \
- -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
-
-#endif
-
- class bad_variant_access : public std::exception {
- public:
- virtual const char *what() const noexcept { return "bad_variant_access"; }
- };
-
- [[noreturn]] inline void throw_bad_variant_access() {
-#ifdef MPARK_EXCEPTIONS
- throw bad_variant_access{};
-#else
- std::terminate();
-#ifdef MPARK_BUILTIN_UNREACHABLE
- __builtin_unreachable();
-#endif
-#endif
- }
-
- template <typename... Ts>
- class variant;
-
- template <typename T>
- struct variant_size;
-
-#ifdef MPARK_VARIABLE_TEMPLATES
- template <typename T>
- constexpr std::size_t variant_size_v = variant_size<T>::value;
-#endif
-
- template <typename T>
- struct variant_size<const T> : variant_size<T> {};
-
- template <typename T>
- struct variant_size<volatile T> : variant_size<T> {};
-
- template <typename T>
- struct variant_size<const volatile T> : variant_size<T> {};
-
- template <typename... Ts>
- struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)> {};
-
- template <std::size_t I, typename T>
- struct variant_alternative;
-
- template <std::size_t I, typename T>
- using variant_alternative_t = typename variant_alternative<I, T>::type;
-
- template <std::size_t I, typename T>
- struct variant_alternative<I, const T>
- : std::add_const<variant_alternative_t<I, T>> {};
-
- template <std::size_t I, typename T>
- struct variant_alternative<I, volatile T>
- : std::add_volatile<variant_alternative_t<I, T>> {};
-
- template <std::size_t I, typename T>
- struct variant_alternative<I, const volatile T>
- : std::add_cv<variant_alternative_t<I, T>> {};
-
- template <std::size_t I, typename... Ts>
- struct variant_alternative<I, variant<Ts...>> {
- static_assert(I < sizeof...(Ts),
- "Index out of bounds in std::variant_alternative<>");
- using type = lib::type_pack_element_t<I, Ts...>;
- };
-
- constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
-
- namespace detail {
-
- constexpr std::size_t not_found = static_cast<std::size_t>(-1);
- constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
-
-#ifdef MPARK_CPP14_CONSTEXPR
- template <typename T, typename... Ts>
- inline constexpr std::size_t find_index() {
- constexpr lib::array<bool, sizeof...(Ts)> matches = {
- {std::is_same<T, Ts>::value...}
- };
- std::size_t result = not_found;
- for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
- if (matches[i]) {
- if (result != not_found) {
- return ambiguous;
- }
- result = i;
- }
- }
- return result;
- }
-#else
- inline constexpr std::size_t find_index_impl(std::size_t result,
- std::size_t) {
- return result;
- }
-
- template <typename... Bs>
- inline constexpr std::size_t find_index_impl(std::size_t result,
- std::size_t idx,
- bool b,
- Bs... bs) {
- return b ? (result != not_found ? ambiguous
- : find_index_impl(idx, idx + 1, bs...))
- : find_index_impl(result, idx + 1, bs...);
- }
-
- template <typename T, typename... Ts>
- inline constexpr std::size_t find_index() {
- return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...);
- }
-#endif
-
- template <std::size_t I>
- using find_index_sfinae_impl =
- lib::enable_if_t<I != not_found && I != ambiguous,
- lib::size_constant<I>>;
-
- template <typename T, typename... Ts>
- using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
-
- template <std::size_t I>
- struct find_index_checked_impl : lib::size_constant<I> {
- static_assert(I != not_found, "the specified type is not found.");
- static_assert(I != ambiguous, "the specified type is ambiguous.");
- };
-
- template <typename T, typename... Ts>
- using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
-
- struct valueless_t {};
-
- enum class Trait { TriviallyAvailable, Available, Unavailable };
-
- template <typename T,
- template <typename> class IsTriviallyAvailable,
- template <typename> class IsAvailable>
- inline constexpr Trait trait() {
- return IsTriviallyAvailable<T>::value
- ? Trait::TriviallyAvailable
- : IsAvailable<T>::value ? Trait::Available
- : Trait::Unavailable;
- }
-
-#ifdef MPARK_CPP14_CONSTEXPR
- template <typename... Traits>
- inline constexpr Trait common_trait(Traits... traits) {
- Trait result = Trait::TriviallyAvailable;
- for (Trait t : {traits...}) {
- if (static_cast<int>(t) > static_cast<int>(result)) {
- result = t;
- }
- }
- return result;
- }
-#else
- inline constexpr Trait common_trait_impl(Trait result) { return result; }
-
- template <typename... Traits>
- inline constexpr Trait common_trait_impl(Trait result,
- Trait t,
- Traits... ts) {
- return static_cast<int>(t) > static_cast<int>(result)
- ? common_trait_impl(t, ts...)
- : common_trait_impl(result, ts...);
- }
-
- template <typename... Traits>
- inline constexpr Trait common_trait(Traits... ts) {
- return common_trait_impl(Trait::TriviallyAvailable, ts...);
- }
-#endif
-
- template <typename... Ts>
- struct traits {
- static constexpr Trait copy_constructible_trait =
- common_trait(trait<Ts,
- lib::is_trivially_copy_constructible,
- std::is_copy_constructible>()...);
-
- static constexpr Trait move_constructible_trait =
- common_trait(trait<Ts,
- lib::is_trivially_move_constructible,
- std::is_move_constructible>()...);
-
- static constexpr Trait copy_assignable_trait =
- common_trait(copy_constructible_trait,
- trait<Ts,
- lib::is_trivially_copy_assignable,
- std::is_copy_assignable>()...);
-
- static constexpr Trait move_assignable_trait =
- common_trait(move_constructible_trait,
- trait<Ts,
- lib::is_trivially_move_assignable,
- std::is_move_assignable>()...);
-
- static constexpr Trait destructible_trait =
- common_trait(trait<Ts,
- std::is_trivially_destructible,
- std::is_destructible>()...);
- };
-
- namespace access {
-
- struct recursive_union {
-#ifdef MPARK_RETURN_TYPE_DEDUCTION
- template <typename V>
- inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
- return lib::forward<V>(v).head_;
- }
-
- template <typename V, std::size_t I>
- inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
- return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{});
- }
-#else
- template <std::size_t I, bool Dummy = true>
- struct get_alt_impl {
- template <typename V>
- inline constexpr AUTO_REFREF operator()(V &&v) const
- AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_))
- };
-
- template <bool Dummy>
- struct get_alt_impl<0, Dummy> {
- template <typename V>
- inline constexpr AUTO_REFREF operator()(V &&v) const
- AUTO_REFREF_RETURN(lib::forward<V>(v).head_)
- };
-
- template <typename V, std::size_t I>
- inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t<I>)
- AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v)))
-#endif
- };
-
- struct base {
- template <std::size_t I, typename V>
- inline static constexpr AUTO_REFREF get_alt(V &&v)
- AUTO_REFREF_RETURN(recursive_union::get_alt(
- data(lib::forward<V>(v)), in_place_index_t<I>{}))
- };
-
- struct variant {
- template <std::size_t I, typename V>
- inline static constexpr AUTO_REFREF get_alt(V &&v)
- AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_))
- };
-
- } // namespace access
-
- namespace visitation {
-
- struct base {
- template <typename T>
- inline static constexpr const T &at(const T &elem) {
- return elem;
- }
-
- template <typename T, std::size_t N, typename... Is>
- inline static constexpr const lib::remove_all_extents_t<T> &at(
- const lib::array<T, N> &elems, std::size_t i, Is... is) {
- return at(elems[i], is...);
- }
-
- template <typename F, typename... Fs>
- inline static constexpr int visit_visitor_return_type_check() {
- static_assert(lib::all<std::is_same<F, Fs>::value...>::value,
- "`mpark::visit` requires the visitor to have a single "
- "return type.");
- return 0;
- }
-
- template <typename... Fs>
- inline static constexpr lib::array<
- lib::common_type_t<lib::decay_t<Fs>...>,
- sizeof...(Fs)>
- make_farray(Fs &&... fs) {
- using result = lib::array<lib::common_type_t<lib::decay_t<Fs>...>,
- sizeof...(Fs)>;
- return visit_visitor_return_type_check<lib::decay_t<Fs>...>(),
- result{{lib::forward<Fs>(fs)...}};
- }
-
- template <std::size_t... Is>
- struct dispatcher {
- template <typename F, typename... Vs>
- struct impl {
- inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
- DECLTYPE_AUTO_RETURN(lib::invoke(
- static_cast<F>(f),
- access::base::get_alt<Is>(static_cast<Vs>(vs))...))
- };
- };
-
- template <typename F, typename... Vs, std::size_t... Is>
- inline static constexpr AUTO make_dispatch(lib::index_sequence<Is...>)
- AUTO_RETURN(&dispatcher<Is...>::template impl<F, Vs...>::dispatch)
-
- template <std::size_t I, typename F, typename... Vs>
- inline static constexpr AUTO make_fdiagonal_impl()
- AUTO_RETURN(make_dispatch<F, Vs...>(
- lib::index_sequence<lib::indexed_type<I, Vs>::value...>{}))
-
- template <typename F, typename... Vs, std::size_t... Is>
- inline static constexpr AUTO make_fdiagonal_impl(
- lib::index_sequence<Is...>)
- AUTO_RETURN(make_farray(make_fdiagonal_impl<Is, F, Vs...>()...))
-
- template <typename F, typename V, typename... Vs>
- inline static constexpr /* auto * */ auto make_fdiagonal()
- -> decltype(make_fdiagonal_impl<F, V, Vs...>(
- lib::make_index_sequence<lib::decay_t<V>::size()>{})) {
- static_assert(lib::all<(lib::decay_t<V>::size() ==
- lib::decay_t<Vs>::size())...>::value,
- "all of the variants must be the same size.");
- return make_fdiagonal_impl<F, V, Vs...>(
- lib::make_index_sequence<lib::decay_t<V>::size()>{});
- }
-
-#ifdef MPARK_RETURN_TYPE_DEDUCTION
- template <typename F, typename... Vs, typename Is>
- inline static constexpr auto make_fmatrix_impl(Is is) {
- return make_dispatch<F, Vs...>(is);
- }
-
- template <typename F,
- typename... Vs,
- typename Is,
- std::size_t... Js,
- typename... Ls>
- inline static constexpr auto make_fmatrix_impl(
- Is, lib::index_sequence<Js...>, Ls... ls) {
- return make_farray(make_fmatrix_impl<F, Vs...>(
- lib::push_back_t<Is, Js>{}, ls...)...);
- }
-
- template <typename F, typename... Vs>
- inline static constexpr auto make_fmatrix() {
- return make_fmatrix_impl<F, Vs...>(
- lib::index_sequence<>{},
- lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...);
- }
-#else
- template <typename F, typename... Vs>
- struct make_fmatrix_impl {
- template <typename...>
- struct impl;
-
- template <typename Is>
- struct impl<Is> {
- inline constexpr AUTO operator()() const
- AUTO_RETURN(make_dispatch<F, Vs...>(Is{}))
- };
-
- template <typename Is, std::size_t... Js, typename... Ls>
- struct impl<Is, lib::index_sequence<Js...>, Ls...> {
- inline constexpr AUTO operator()() const
- AUTO_RETURN(
- make_farray(impl<lib::push_back_t<Is, Js>, Ls...>{}()...))
- };
- };
-
- template <typename F, typename... Vs>
- inline static constexpr AUTO make_fmatrix()
- AUTO_RETURN(
- typename make_fmatrix_impl<F, Vs...>::template impl<
- lib::index_sequence<>,
- lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}())
-#endif
- }; // namespace base
-
- template <typename F, typename... Vs>
- using FDiagonal = decltype(base::make_fdiagonal<F, Vs...>());
-
- template <typename F, typename... Vs>
- struct fdiagonal {
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4268)
-#endif
- static constexpr FDiagonal<F, Vs...> value =
- base::make_fdiagonal<F, Vs...>();
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
- };
-
- template <typename F, typename... Vs>
- constexpr FDiagonal<F, Vs...> fdiagonal<F, Vs...>::value;
-
- template <typename F, typename... Vs>
- using FMatrix = decltype(base::make_fmatrix<F, Vs...>());
-
- template <typename F, typename... Vs>
- struct fmatrix {
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4268)
-#endif
- static constexpr FMatrix<F, Vs...> value =
- base::make_fmatrix<F, Vs...>();
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
- };
-
- template <typename F, typename... Vs>
- constexpr FMatrix<F, Vs...> fmatrix<F, Vs...>::value;
-
- struct alt {
- template <typename Visitor, typename... Vs>
- inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
- Visitor &&visitor,
- Vs &&... vs)
- DECLTYPE_AUTO_RETURN(base::at(
- fdiagonal<Visitor &&,
- decltype(as_base(lib::forward<Vs>(vs)))...>::value,
- index)(lib::forward<Visitor>(visitor),
- as_base(lib::forward<Vs>(vs))...))
-
- template <typename Visitor, typename... Vs>
- inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
- Vs &&... vs)
- DECLTYPE_AUTO_RETURN(base::at(
- fmatrix<Visitor &&,
- decltype(as_base(lib::forward<Vs>(vs)))...>::value,
- vs.index()...)(lib::forward<Visitor>(visitor),
- as_base(lib::forward<Vs>(vs))...))
- };
-
- struct variant {
- private:
- template <typename Visitor, typename... Values>
- struct visit_exhaustive_visitor_check {
- static_assert(
- lib::is_invocable<Visitor, Values...>::value,
- "`mpark::visit` requires the visitor to be exhaustive.");
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4100)
-#endif
- inline constexpr DECLTYPE_AUTO operator()(Visitor &&visitor,
- Values &&... values) const
- DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
- lib::forward<Values>(values)...))
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
- };
-
- template <typename Visitor>
- struct value_visitor {
- Visitor &&visitor_;
-
- template <typename... Alts>
- inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const
- DECLTYPE_AUTO_RETURN(
- visit_exhaustive_visitor_check<
- Visitor,
- decltype((lib::forward<Alts>(alts).value))...>{}(
- lib::forward<Visitor>(visitor_),
- lib::forward<Alts>(alts).value...))
- };
-
- template <typename Visitor>
- inline static constexpr AUTO make_value_visitor(Visitor &&visitor)
- AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)})
-
- public:
- template <typename Visitor, typename... Vs>
- inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
- Visitor &&visitor,
- Vs &&... vs)
- DECLTYPE_AUTO_RETURN(
- alt::visit_alt_at(index,
- lib::forward<Visitor>(visitor),
- lib::forward<Vs>(vs).impl_...))
-
- template <typename Visitor, typename... Vs>
- inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
- Vs &&... vs)
- DECLTYPE_AUTO_RETURN(alt::visit_alt(lib::forward<Visitor>(visitor),
- lib::forward<Vs>(vs).impl_...))
-
- template <typename Visitor, typename... Vs>
- inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index,
- Visitor &&visitor,
- Vs &&... vs)
- DECLTYPE_AUTO_RETURN(
- visit_alt_at(index,
- make_value_visitor(lib::forward<Visitor>(visitor)),
- lib::forward<Vs>(vs)...))
-
- template <typename Visitor, typename... Vs>
- inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor,
- Vs &&... vs)
- DECLTYPE_AUTO_RETURN(
- visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)),
- lib::forward<Vs>(vs)...))
- };
-
- } // namespace visitation
-
- template <std::size_t Index, typename T>
- struct alt {
- using value_type = T;
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4244)
-#endif
- template <typename... Args>
- inline explicit constexpr alt(in_place_t, Args &&... args)
- : value(lib::forward<Args>(args)...) {}
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
- T value;
- };
-
- template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
- union recursive_union;
-
- template <Trait DestructibleTrait, std::size_t Index>
- union recursive_union<DestructibleTrait, Index> {};
-
-#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor) \
- template <std::size_t Index, typename T, typename... Ts> \
- union recursive_union<destructible_trait, Index, T, Ts...> { \
- public: \
- inline explicit constexpr recursive_union(valueless_t) noexcept \
- : dummy_{} {} \
- \
- template <typename... Args> \
- inline explicit constexpr recursive_union(in_place_index_t<0>, \
- Args &&... args) \
- : head_(in_place_t{}, lib::forward<Args>(args)...) {} \
- \
- template <std::size_t I, typename... Args> \
- inline explicit constexpr recursive_union(in_place_index_t<I>, \
- Args &&... args) \
- : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...) {} \
- \
- recursive_union(const recursive_union &) = default; \
- recursive_union(recursive_union &&) = default; \
- \
- destructor \
- \
- recursive_union &operator=(const recursive_union &) = default; \
- recursive_union &operator=(recursive_union &&) = default; \
- \
- private: \
- char dummy_; \
- alt<Index, T> head_; \
- recursive_union<destructible_trait, Index + 1, Ts...> tail_; \
- \
- friend struct access::recursive_union; \
- }
-
- MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
- ~recursive_union() = default;);
- MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
- ~recursive_union() {});
- MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
- ~recursive_union() = delete;);
-
-#undef MPARK_VARIANT_RECURSIVE_UNION
-
- using index_t = unsigned int;
-
- template <Trait DestructibleTrait, typename... Ts>
- class base {
- public:
- inline explicit constexpr base(valueless_t tag) noexcept
- : data_(tag), index_(static_cast<index_t>(-1)) {}
-
- template <std::size_t I, typename... Args>
- inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
- : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...),
- index_(I) {}
-
- inline constexpr bool valueless_by_exception() const noexcept {
- return index_ == static_cast<index_t>(-1);
- }
-
- inline constexpr std::size_t index() const noexcept {
- return valueless_by_exception() ? variant_npos : index_;
- }
-
- protected:
- using data_t = recursive_union<DestructibleTrait, 0, Ts...>;
-
- friend inline constexpr base &as_base(base &b) { return b; }
- friend inline constexpr const base &as_base(const base &b) { return b; }
- friend inline constexpr base &&as_base(base &&b) { return lib::move(b); }
- friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); }
-
- friend inline constexpr data_t &data(base &b) { return b.data_; }
- friend inline constexpr const data_t &data(const base &b) { return b.data_; }
- friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; }
- friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; }
-
- inline static constexpr std::size_t size() { return sizeof...(Ts); }
-
- data_t data_;
- index_t index_;
-
- friend struct access::base;
- friend struct visitation::base;
- };
-
- struct dtor {
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4100)
-#endif
- template <typename Alt>
- inline void operator()(Alt &alt) const noexcept { alt.~Alt(); }
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
- };
-
-#if defined(_MSC_VER) && _MSC_VER < 1910
-#define INHERITING_CTOR(type, base) \
- template <typename... Args> \
- inline explicit constexpr type(Args &&... args) \
- : base(lib::forward<Args>(args)...) {}
-#else
-#define INHERITING_CTOR(type, base) using base::base;
-#endif
-
- template <typename Traits, Trait = Traits::destructible_trait>
- class destructor;
-
-#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
- template <typename... Ts> \
- class destructor<traits<Ts...>, destructible_trait> \
- : public base<destructible_trait, Ts...> { \
- using super = base<destructible_trait, Ts...>; \
- \
- public: \
- INHERITING_CTOR(destructor, super) \
- using super::operator=; \
- \
- destructor(const destructor &) = default; \
- destructor(destructor &&) = default; \
- definition \
- destructor &operator=(const destructor &) = default; \
- destructor &operator=(destructor &&) = default; \
- \
- protected: \
- destroy \
- }
-
- MPARK_VARIANT_DESTRUCTOR(
- Trait::TriviallyAvailable,
- ~destructor() = default;,
- inline void destroy() noexcept {
- this->index_ = static_cast<index_t>(-1);
- });
-
- MPARK_VARIANT_DESTRUCTOR(
- Trait::Available,
- ~destructor() { destroy(); },
- inline void destroy() noexcept {
- if (!this->valueless_by_exception()) {
- visitation::alt::visit_alt(dtor{}, *this);
- }
- this->index_ = static_cast<index_t>(-1);
- });
-
- MPARK_VARIANT_DESTRUCTOR(
- Trait::Unavailable,
- ~destructor() = delete;,
- inline void destroy() noexcept = delete;);
-
-#undef MPARK_VARIANT_DESTRUCTOR
-
- template <typename Traits>
- class constructor : public destructor<Traits> {
- using super = destructor<Traits>;
-
- public:
- INHERITING_CTOR(constructor, super)
- using super::operator=;
-
- protected:
-#ifndef MPARK_GENERIC_LAMBDAS
- struct ctor {
- template <typename LhsAlt, typename RhsAlt>
- inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const {
- constructor::construct_alt(lhs_alt,
- lib::forward<RhsAlt>(rhs_alt).value);
- }
- };
-#endif
-
- template <std::size_t I, typename T, typename... Args>
- inline static T &construct_alt(alt<I, T> &a, Args &&... args) {
- ::new (static_cast<void *>(lib::addressof(a)))
- alt<I, T>(in_place_t{}, lib::forward<Args>(args)...);
- return a.value;
- }
-
- template <typename Rhs>
- inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
- lhs.destroy();
- if (!rhs.valueless_by_exception()) {
- visitation::alt::visit_alt_at(
- rhs.index(),
-#ifdef MPARK_GENERIC_LAMBDAS
- [](auto &lhs_alt, auto &&rhs_alt) {
- constructor::construct_alt(
- lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value);
- }
-#else
- ctor{}
-#endif
- ,
- lhs,
- lib::forward<Rhs>(rhs));
- lhs.index_ = rhs.index_;
- }
- }
- };
-
- template <typename Traits, Trait = Traits::move_constructible_trait>
- class move_constructor;
-
-#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
- template <typename... Ts> \
- class move_constructor<traits<Ts...>, move_constructible_trait> \
- : public constructor<traits<Ts...>> { \
- using super = constructor<traits<Ts...>>; \
- \
- public: \
- INHERITING_CTOR(move_constructor, super) \
- using super::operator=; \
- \
- move_constructor(const move_constructor &) = default; \
- definition \
- ~move_constructor() = default; \
- move_constructor &operator=(const move_constructor &) = default; \
- move_constructor &operator=(move_constructor &&) = default; \
- }
-
- MPARK_VARIANT_MOVE_CONSTRUCTOR(
- Trait::TriviallyAvailable,
- move_constructor(move_constructor &&that) = default;);
-
- MPARK_VARIANT_MOVE_CONSTRUCTOR(
- Trait::Available,
- move_constructor(move_constructor &&that) noexcept(
- lib::all<std::is_nothrow_move_constructible<Ts>::value...>::value)
- : move_constructor(valueless_t{}) {
- this->generic_construct(*this, lib::move(that));
- });
-
- MPARK_VARIANT_MOVE_CONSTRUCTOR(
- Trait::Unavailable,
- move_constructor(move_constructor &&) = delete;);
-
-#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
-
- template <typename Traits, Trait = Traits::copy_constructible_trait>
- class copy_constructor;
-
-#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
- template <typename... Ts> \
- class copy_constructor<traits<Ts...>, copy_constructible_trait> \
- : public move_constructor<traits<Ts...>> { \
- using super = move_constructor<traits<Ts...>>; \
- \
- public: \
- INHERITING_CTOR(copy_constructor, super) \
- using super::operator=; \
- \
- definition \
- copy_constructor(copy_constructor &&) = default; \
- ~copy_constructor() = default; \
- copy_constructor &operator=(const copy_constructor &) = default; \
- copy_constructor &operator=(copy_constructor &&) = default; \
- }
-
- MPARK_VARIANT_COPY_CONSTRUCTOR(
- Trait::TriviallyAvailable,
- copy_constructor(const copy_constructor &that) = default;);
-
- MPARK_VARIANT_COPY_CONSTRUCTOR(
- Trait::Available,
- copy_constructor(const copy_constructor &that)
- : copy_constructor(valueless_t{}) {
- this->generic_construct(*this, that);
- });
-
- MPARK_VARIANT_COPY_CONSTRUCTOR(
- Trait::Unavailable,
- copy_constructor(const copy_constructor &) = delete;);
-
-#undef MPARK_VARIANT_COPY_CONSTRUCTOR
-
- template <typename Traits>
- class assignment : public copy_constructor<Traits> {
- using super = copy_constructor<Traits>;
-
- public:
- INHERITING_CTOR(assignment, super)
- using super::operator=;
-
- template <std::size_t I, typename... Args>
- inline /* auto & */ auto emplace(Args &&... args)
- -> decltype(this->construct_alt(access::base::get_alt<I>(*this),
- lib::forward<Args>(args)...)) {
- this->destroy();
- auto &result = this->construct_alt(access::base::get_alt<I>(*this),
- lib::forward<Args>(args)...);
- this->index_ = I;
- return result;
- }
-
- protected:
-#ifndef MPARK_GENERIC_LAMBDAS
- template <typename That>
- struct assigner {
- template <typename ThisAlt, typename ThatAlt>
- inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const {
- self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value);
- }
- assignment *self;
- };
-#endif
-
- template <std::size_t I, typename T, typename Arg>
- inline void assign_alt(alt<I, T> &a, Arg &&arg) {
- if (this->index() == I) {
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4244)
-#endif
- a.value = lib::forward<Arg>(arg);
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
- } else {
- struct {
- void operator()(std::true_type) const {
- this_->emplace<I>(lib::forward<Arg>(arg_));
- }
- void operator()(std::false_type) const {
- this_->emplace<I>(T(lib::forward<Arg>(arg_)));
- }
- assignment *this_;
- Arg &&arg_;
- } impl{this, lib::forward<Arg>(arg)};
- impl(lib::bool_constant<
- std::is_nothrow_constructible<T, Arg>::value ||
- !std::is_nothrow_move_constructible<T>::value>{});
- }
- }
-
- template <typename That>
- inline void generic_assign(That &&that) {
- if (this->valueless_by_exception() && that.valueless_by_exception()) {
- // do nothing.
- } else if (that.valueless_by_exception()) {
- this->destroy();
- } else {
- visitation::alt::visit_alt_at(
- that.index(),
-#ifdef MPARK_GENERIC_LAMBDAS
- [this](auto &this_alt, auto &&that_alt) {
- this->assign_alt(
- this_alt, lib::forward<decltype(that_alt)>(that_alt).value);
- }
-#else
- assigner<That>{this}
-#endif
- ,
- *this,
- lib::forward<That>(that));
- }
- }
- };
-
- template <typename Traits, Trait = Traits::move_assignable_trait>
- class move_assignment;
-
-#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
- template <typename... Ts> \
- class move_assignment<traits<Ts...>, move_assignable_trait> \
- : public assignment<traits<Ts...>> { \
- using super = assignment<traits<Ts...>>; \
- \
- public: \
- INHERITING_CTOR(move_assignment, super) \
- using super::operator=; \
- \
- move_assignment(const move_assignment &) = default; \
- move_assignment(move_assignment &&) = default; \
- ~move_assignment() = default; \
- move_assignment &operator=(const move_assignment &) = default; \
- definition \
- }
-
- MPARK_VARIANT_MOVE_ASSIGNMENT(
- Trait::TriviallyAvailable,
- move_assignment &operator=(move_assignment &&that) = default;);
-
- MPARK_VARIANT_MOVE_ASSIGNMENT(
- Trait::Available,
- move_assignment &
- operator=(move_assignment &&that) noexcept(
- lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
- std::is_nothrow_move_assignable<Ts>::value)...>::value) {
- this->generic_assign(lib::move(that));
- return *this;
- });
-
- MPARK_VARIANT_MOVE_ASSIGNMENT(
- Trait::Unavailable,
- move_assignment &operator=(move_assignment &&) = delete;);
-
-#undef MPARK_VARIANT_MOVE_ASSIGNMENT
-
- template <typename Traits, Trait = Traits::copy_assignable_trait>
- class copy_assignment;
-
-#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
- template <typename... Ts> \
- class copy_assignment<traits<Ts...>, copy_assignable_trait> \
- : public move_assignment<traits<Ts...>> { \
- using super = move_assignment<traits<Ts...>>; \
- \
- public: \
- INHERITING_CTOR(copy_assignment, super) \
- using super::operator=; \
- \
- copy_assignment(const copy_assignment &) = default; \
- copy_assignment(copy_assignment &&) = default; \
- ~copy_assignment() = default; \
- definition \
- copy_assignment &operator=(copy_assignment &&) = default; \
- }
-
- MPARK_VARIANT_COPY_ASSIGNMENT(
- Trait::TriviallyAvailable,
- copy_assignment &operator=(const copy_assignment &that) = default;);
-
- MPARK_VARIANT_COPY_ASSIGNMENT(
- Trait::Available,
- copy_assignment &operator=(const copy_assignment &that) {
- this->generic_assign(that);
- return *this;
- });
-
- MPARK_VARIANT_COPY_ASSIGNMENT(
- Trait::Unavailable,
- copy_assignment &operator=(const copy_assignment &) = delete;);
-
-#undef MPARK_VARIANT_COPY_ASSIGNMENT
-
- template <typename... Ts>
- class impl : public copy_assignment<traits<Ts...>> {
- using super = copy_assignment<traits<Ts...>>;
-
- public:
- INHERITING_CTOR(impl, super)
- using super::operator=;
-
- template <std::size_t I, typename Arg>
- inline void assign(Arg &&arg) {
- this->assign_alt(access::base::get_alt<I>(*this),
- lib::forward<Arg>(arg));
- }
-
- inline void swap(impl &that) {
- if (this->valueless_by_exception() && that.valueless_by_exception()) {
- // do nothing.
- } else if (this->index() == that.index()) {
- visitation::alt::visit_alt_at(this->index(),
-#ifdef MPARK_GENERIC_LAMBDAS
- [](auto &this_alt, auto &that_alt) {
- using std::swap;
- swap(this_alt.value,
- that_alt.value);
- }
-#else
- swapper{}
-#endif
- ,
- *this,
- that);
- } else {
- impl *lhs = this;
- impl *rhs = lib::addressof(that);
- if (lhs->move_nothrow() && !rhs->move_nothrow()) {
- std::swap(lhs, rhs);
- }
- impl tmp(lib::move(*rhs));
-#ifdef MPARK_EXCEPTIONS
- // EXTENSION: When the move construction of `lhs` into `rhs` throws
- // and `tmp` is nothrow move constructible then we move `tmp` back
- // into `rhs` and provide the strong exception safety guarantee.
- try {
- this->generic_construct(*rhs, lib::move(*lhs));
- } catch (...) {
- if (tmp.move_nothrow()) {
- this->generic_construct(*rhs, lib::move(tmp));
- }
- throw;
- }
-#else
- this->generic_construct(*rhs, lib::move(*lhs));
-#endif
- this->generic_construct(*lhs, lib::move(tmp));
- }
- }
-
- private:
-#ifndef MPARK_GENERIC_LAMBDAS
- struct swapper {
- template <typename ThisAlt, typename ThatAlt>
- inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const {
- using std::swap;
- swap(this_alt.value, that_alt.value);
- }
- };
-#endif
-
- inline constexpr bool move_nothrow() const {
- return this->valueless_by_exception() ||
- lib::array<bool, sizeof...(Ts)>{
- {std::is_nothrow_move_constructible<Ts>::value...}
- }[this->index()];
- }
- };
-
- template <std::size_t I, typename T>
- struct overload_leaf {
- using F = lib::size_constant<I> (*)(T);
- operator F() const { return nullptr; }
- };
-
- template <typename... Ts>
- struct overload_impl {
- private:
- template <typename>
- struct impl;
-
- template <std::size_t... Is>
- struct impl<lib::index_sequence<Is...>> : overload_leaf<Is, Ts>... {};
-
- public:
- using type = impl<lib::index_sequence_for<Ts...>>;
- };
-
- template <typename... Ts>
- using overload = typename overload_impl<Ts...>::type;
-
- template <typename T, typename... Ts>
- using best_match = lib::invoke_result_t<overload<Ts...>, T &&>;
-
- template <typename T>
- struct is_in_place_index : std::false_type {};
-
- template <std::size_t I>
- struct is_in_place_index<in_place_index_t<I>> : std::true_type {};
-
- template <typename T>
- struct is_in_place_type : std::false_type {};
-
- template <typename T>
- struct is_in_place_type<in_place_type_t<T>> : std::true_type {};
-
- } // detail
-
- template <typename... Ts>
- class variant {
- static_assert(0 < sizeof...(Ts),
- "variant must consist of at least one alternative.");
-
- static_assert(lib::all<!std::is_array<Ts>::value...>::value,
- "variant can not have an array type as an alternative.");
-
- static_assert(lib::all<!std::is_reference<Ts>::value...>::value,
- "variant can not have a reference type as an alternative.");
-
- static_assert(lib::all<!std::is_void<Ts>::value...>::value,
- "variant can not have a void type as an alternative.");
-
- public:
- template <
- typename Front = lib::type_pack_element_t<0, Ts...>,
- lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
- inline constexpr variant()
- : impl_(in_place_index_t<0>{}) {}
-
- variant(const variant &) = default;
- variant(variant &&) = default;
-
- template <
- typename Arg,
- typename Decayed = lib::decay_t<Arg>,
- lib::enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0,
- lib::enable_if_t<!detail::is_in_place_index<Decayed>::value, int> = 0,
- lib::enable_if_t<!detail::is_in_place_type<Decayed>::value, int> = 0,
- std::size_t I = detail::best_match<Arg, Ts...>::value,
- typename T = lib::type_pack_element_t<I, Ts...>,
- lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
- inline constexpr variant(Arg &&arg) noexcept(
- std::is_nothrow_constructible<T, Arg>::value)
- : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg)) {}
-
- template <
- std::size_t I,
- typename... Args,
- typename T = lib::type_pack_element_t<I, Ts...>,
- lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
- inline explicit constexpr variant(
- in_place_index_t<I>,
- Args &&... args) noexcept(std::is_nothrow_constructible<T,
- Args...>::value)
- : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
-
- template <
- std::size_t I,
- typename Up,
- typename... Args,
- typename T = lib::type_pack_element_t<I, Ts...>,
- lib::enable_if_t<std::is_constructible<T,
- std::initializer_list<Up> &,
- Args...>::value,
- int> = 0>
- inline explicit constexpr variant(
- in_place_index_t<I>,
- std::initializer_list<Up> il,
- Args &&... args) noexcept(std::
- is_nothrow_constructible<
- T,
- std::initializer_list<Up> &,
- Args...>::value)
- : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
-
- template <
- typename T,
- typename... Args,
- std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
- lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
- inline explicit constexpr variant(
- in_place_type_t<T>,
- Args &&... args) noexcept(std::is_nothrow_constructible<T,
- Args...>::value)
- : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
-
- template <
- typename T,
- typename Up,
- typename... Args,
- std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
- lib::enable_if_t<std::is_constructible<T,
- std::initializer_list<Up> &,
- Args...>::value,
- int> = 0>
- inline explicit constexpr variant(
- in_place_type_t<T>,
- std::initializer_list<Up> il,
- Args &&... args) noexcept(std::
- is_nothrow_constructible<
- T,
- std::initializer_list<Up> &,
- Args...>::value)
- : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
-
- ~variant() = default;
-
- variant &operator=(const variant &) = default;
- variant &operator=(variant &&) = default;
-
- template <typename Arg,
- lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value,
- int> = 0,
- std::size_t I = detail::best_match<Arg, Ts...>::value,
- typename T = lib::type_pack_element_t<I, Ts...>,
- lib::enable_if_t<(std::is_assignable<T &, Arg>::value &&
- std::is_constructible<T, Arg>::value),
- int> = 0>
- inline variant &operator=(Arg &&arg) {
- impl_.template assign<I>(lib::forward<Arg>(arg));
- return *this;
- }
-
- template <
- std::size_t I,
- typename... Args,
- typename T = lib::type_pack_element_t<I, Ts...>,
- lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
- inline T &emplace(Args &&... args) {
- return impl_.template emplace<I>(lib::forward<Args>(args)...);
- }
-
- template <
- std::size_t I,
- typename Up,
- typename... Args,
- typename T = lib::type_pack_element_t<I, Ts...>,
- lib::enable_if_t<std::is_constructible<T,
- std::initializer_list<Up> &,
- Args...>::value,
- int> = 0>
- inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
- return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
- }
-
- template <
- typename T,
- typename... Args,
- std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
- lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
- inline T &emplace(Args &&... args) {
- return impl_.template emplace<I>(lib::forward<Args>(args)...);
- }
-
- template <
- typename T,
- typename Up,
- typename... Args,
- std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
- lib::enable_if_t<std::is_constructible<T,
- std::initializer_list<Up> &,
- Args...>::value,
- int> = 0>
- inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
- return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
- }
-
- inline constexpr bool valueless_by_exception() const noexcept {
- return impl_.valueless_by_exception();
- }
-
- inline constexpr std::size_t index() const noexcept {
- return impl_.index();
- }
-
- template <bool Dummy = true,
- lib::enable_if_t<
- lib::all<Dummy,
- (lib::dependent_type<std::is_move_constructible<Ts>,
- Dummy>::value &&
- lib::dependent_type<lib::is_swappable<Ts>,
- Dummy>::value)...>::value,
- int> = 0>
- inline void swap(variant &that) noexcept(
- lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
- lib::is_nothrow_swappable<Ts>::value)...>::value) {
- impl_.swap(that.impl_);
- }
-
- private:
- detail::impl<Ts...> impl_;
-
- friend struct detail::access::variant;
- friend struct detail::visitation::variant;
- };
-
- template <std::size_t I, typename... Ts>
- inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
- return v.index() == I;
- }
-
- template <typename T, typename... Ts>
- inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
- return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
- }
-
- namespace detail {
- template <std::size_t I, typename V>
- struct generic_get_impl {
- constexpr generic_get_impl(int) {}
-
- constexpr AUTO_REFREF operator()(V &&v) const
- AUTO_REFREF_RETURN(
- access::variant::get_alt<I>(lib::forward<V>(v)).value)
- };
-
- template <std::size_t I, typename V>
- inline constexpr AUTO_REFREF generic_get(V &&v)
- AUTO_REFREF_RETURN(generic_get_impl<I, V>(
- holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(
- lib::forward<V>(v)))
- } // namespace detail
-
- template <std::size_t I, typename... Ts>
- inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
- variant<Ts...> &v) {
- return detail::generic_get<I>(v);
- }
-
- template <std::size_t I, typename... Ts>
- inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
- variant<Ts...> &&v) {
- return detail::generic_get<I>(lib::move(v));
- }
-
- template <std::size_t I, typename... Ts>
- inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
- const variant<Ts...> &v) {
- return detail::generic_get<I>(v);
- }
-
- template <std::size_t I, typename... Ts>
- inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
- const variant<Ts...> &&v) {
- return detail::generic_get<I>(lib::move(v));
- }
-
- template <typename T, typename... Ts>
- inline constexpr T &get(variant<Ts...> &v) {
- return get<detail::find_index_checked<T, Ts...>::value>(v);
- }
-
- template <typename T, typename... Ts>
- inline constexpr T &&get(variant<Ts...> &&v) {
- return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
- }
-
- template <typename T, typename... Ts>
- inline constexpr const T &get(const variant<Ts...> &v) {
- return get<detail::find_index_checked<T, Ts...>::value>(v);
- }
-
- template <typename T, typename... Ts>
- inline constexpr const T &&get(const variant<Ts...> &&v) {
- return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
- }
-
- namespace detail {
-
- template <std::size_t I, typename V>
- inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept
- AUTO_RETURN(v && holds_alternative<I>(*v)
- ? lib::addressof(access::variant::get_alt<I>(*v).value)
- : nullptr)
-
- } // namespace detail
-
- template <std::size_t I, typename... Ts>
- inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
- get_if(variant<Ts...> *v) noexcept {
- return detail::generic_get_if<I>(v);
- }
-
- template <std::size_t I, typename... Ts>
- inline constexpr lib::add_pointer_t<
- const variant_alternative_t<I, variant<Ts...>>>
- get_if(const variant<Ts...> *v) noexcept {
- return detail::generic_get_if<I>(v);
- }
-
- template <typename T, typename... Ts>
- inline constexpr lib::add_pointer_t<T>
- get_if(variant<Ts...> *v) noexcept {
- return get_if<detail::find_index_checked<T, Ts...>::value>(v);
- }
-
- template <typename T, typename... Ts>
- inline constexpr lib::add_pointer_t<const T>
- get_if(const variant<Ts...> *v) noexcept {
- return get_if<detail::find_index_checked<T, Ts...>::value>(v);
- }
-
- template <typename... Ts>
- inline constexpr bool operator==(const variant<Ts...> &lhs,
- const variant<Ts...> &rhs) {
- using detail::visitation::variant;
- using lib::equal_to;
-#ifdef MPARK_CPP14_CONSTEXPR
- if (lhs.index() != rhs.index()) return false;
- if (lhs.valueless_by_exception()) return true;
- return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs);
-#else
- return lhs.index() == rhs.index() &&
- (lhs.valueless_by_exception() ||
- variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs));
-#endif
- }
-
- template <typename... Ts>
- inline constexpr bool operator!=(const variant<Ts...> &lhs,
- const variant<Ts...> &rhs) {
- using detail::visitation::variant;
- using lib::not_equal_to;
-#ifdef MPARK_CPP14_CONSTEXPR
- if (lhs.index() != rhs.index()) return true;
- if (lhs.valueless_by_exception()) return false;
- return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs);
-#else
- return lhs.index() != rhs.index() ||
- (!lhs.valueless_by_exception() &&
- variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs));
-#endif
- }
-
- template <typename... Ts>
- inline constexpr bool operator<(const variant<Ts...> &lhs,
- const variant<Ts...> &rhs) {
- using detail::visitation::variant;
- using lib::less;
-#ifdef MPARK_CPP14_CONSTEXPR
- if (rhs.valueless_by_exception()) return false;
- if (lhs.valueless_by_exception()) return true;
- if (lhs.index() < rhs.index()) return true;
- if (lhs.index() > rhs.index()) return false;
- return variant::visit_value_at(lhs.index(), less{}, lhs, rhs);
-#else
- return !rhs.valueless_by_exception() &&
- (lhs.valueless_by_exception() || lhs.index() < rhs.index() ||
- (lhs.index() == rhs.index() &&
- variant::visit_value_at(lhs.index(), less{}, lhs, rhs)));
-#endif
- }
-
- template <typename... Ts>
- inline constexpr bool operator>(const variant<Ts...> &lhs,
- const variant<Ts...> &rhs) {
- using detail::visitation::variant;
- using lib::greater;
-#ifdef MPARK_CPP14_CONSTEXPR
- if (lhs.valueless_by_exception()) return false;
- if (rhs.valueless_by_exception()) return true;
- if (lhs.index() > rhs.index()) return true;
- if (lhs.index() < rhs.index()) return false;
- return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs);
-#else
- return !lhs.valueless_by_exception() &&
- (rhs.valueless_by_exception() || lhs.index() > rhs.index() ||
- (lhs.index() == rhs.index() &&
- variant::visit_value_at(lhs.index(), greater{}, lhs, rhs)));
-#endif
- }
-
- template <typename... Ts>
- inline constexpr bool operator<=(const variant<Ts...> &lhs,
- const variant<Ts...> &rhs) {
- using detail::visitation::variant;
- using lib::less_equal;
-#ifdef MPARK_CPP14_CONSTEXPR
- if (lhs.valueless_by_exception()) return true;
- if (rhs.valueless_by_exception()) return false;
- if (lhs.index() < rhs.index()) return true;
- if (lhs.index() > rhs.index()) return false;
- return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs);
-#else
- return lhs.valueless_by_exception() ||
- (!rhs.valueless_by_exception() &&
- (lhs.index() < rhs.index() ||
- (lhs.index() == rhs.index() &&
- variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs))));
-#endif
- }
-
- template <typename... Ts>
- inline constexpr bool operator>=(const variant<Ts...> &lhs,
- const variant<Ts...> &rhs) {
- using detail::visitation::variant;
- using lib::greater_equal;
-#ifdef MPARK_CPP14_CONSTEXPR
- if (rhs.valueless_by_exception()) return true;
- if (lhs.valueless_by_exception()) return false;
- if (lhs.index() > rhs.index()) return true;
- if (lhs.index() < rhs.index()) return false;
- return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs);
-#else
- return rhs.valueless_by_exception() ||
- (!lhs.valueless_by_exception() &&
- (lhs.index() > rhs.index() ||
- (lhs.index() == rhs.index() &&
- variant::visit_value_at(
- lhs.index(), greater_equal{}, lhs, rhs))));
-#endif
- }
-
- struct monostate {};
-
- inline constexpr bool operator<(monostate, monostate) noexcept {
- return false;
- }
-
- inline constexpr bool operator>(monostate, monostate) noexcept {
- return false;
- }
-
- inline constexpr bool operator<=(monostate, monostate) noexcept {
- return true;
- }
-
- inline constexpr bool operator>=(monostate, monostate) noexcept {
- return true;
- }
-
- inline constexpr bool operator==(monostate, monostate) noexcept {
- return true;
- }
-
- inline constexpr bool operator!=(monostate, monostate) noexcept {
- return false;
- }
-
-#ifdef MPARK_CPP14_CONSTEXPR
- namespace detail {
-
- inline constexpr bool all(std::initializer_list<bool> bs) {
- for (bool b : bs) {
- if (!b) {
- return false;
- }
- }
- return true;
- }
-
- } // namespace detail
-
- template <typename Visitor, typename... Vs>
- inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) {
- return (detail::all({!vs.valueless_by_exception()...})
- ? (void)0
- : throw_bad_variant_access()),
- detail::visitation::variant::visit_value(
- lib::forward<Visitor>(visitor), lib::forward<Vs>(vs)...);
- }
-#else
- namespace detail {
-
- template <std::size_t N>
- inline constexpr bool all_impl(const lib::array<bool, N> &bs,
- std::size_t idx) {
- return idx >= N || (bs[idx] && all_impl(bs, idx + 1));
- }
-
- template <std::size_t N>
- inline constexpr bool all(const lib::array<bool, N> &bs) {
- return all_impl(bs, 0);
- }
-
- } // namespace detail
-
- template <typename Visitor, typename... Vs>
- inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs)
- DECLTYPE_AUTO_RETURN(
- (detail::all(
- lib::array<bool, sizeof...(Vs)>{{!vs.valueless_by_exception()...}})
- ? (void)0
- : throw_bad_variant_access()),
- detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor),
- lib::forward<Vs>(vs)...))
-#endif
-
- template <typename... Ts>
- inline auto swap(variant<Ts...> &lhs,
- variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
- -> decltype(lhs.swap(rhs)) {
- lhs.swap(rhs);
- }
-
- namespace detail {
-
- template <typename T, typename...>
- using enabled_type = T;
-
- namespace hash {
-
- template <typename H, typename K>
- constexpr bool meets_requirements() {
- return std::is_copy_constructible<H>::value &&
- std::is_move_constructible<H>::value &&
- lib::is_invocable_r<std::size_t, H, const K &>::value;
- }
-
- template <typename K>
- constexpr bool is_enabled() {
- using H = std::hash<K>;
- return meets_requirements<H, K>() &&
- std::is_default_constructible<H>::value &&
- std::is_copy_assignable<H>::value &&
- std::is_move_assignable<H>::value;
- }
-
- } // namespace hash
-
- } // namespace detail
-
-#undef AUTO
-#undef AUTO_RETURN
-
-#undef AUTO_REFREF
-#undef AUTO_REFREF_RETURN
-
-#undef DECLTYPE_AUTO
-#undef DECLTYPE_AUTO_RETURN
-
-} // namespace mpark
-
-namespace std {
-
- template <typename... Ts>
- struct hash<mpark::detail::enabled_type<
- mpark::variant<Ts...>,
- mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled<
- mpark::lib::remove_const_t<Ts>>()...>::value>>> {
- using argument_type = mpark::variant<Ts...>;
- using result_type = std::size_t;
-
- inline result_type operator()(const argument_type &v) const {
- using mpark::detail::visitation::variant;
- std::size_t result =
- v.valueless_by_exception()
- ? 299792458 // Random value chosen by the universe upon creation
- : variant::visit_alt(
-#ifdef MPARK_GENERIC_LAMBDAS
- [](const auto &alt) {
- using alt_type = mpark::lib::decay_t<decltype(alt)>;
- using value_type = mpark::lib::remove_const_t<
- typename alt_type::value_type>;
- return hash<value_type>{}(alt.value);
- }
-#else
- hasher{}
-#endif
- ,
- v);
- return hash_combine(result, hash<std::size_t>{}(v.index()));
- }
-
- private:
-#ifndef MPARK_GENERIC_LAMBDAS
- struct hasher {
- template <typename Alt>
- inline std::size_t operator()(const Alt &alt) const {
- using alt_type = mpark::lib::decay_t<Alt>;
- using value_type =
- mpark::lib::remove_const_t<typename alt_type::value_type>;
- return hash<value_type>{}(alt.value);
- }
- };
-#endif
-
- static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
- return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
- }
- };
-
- template <>
- struct hash<mpark::monostate> {
- using argument_type = mpark::monostate;
- using result_type = std::size_t;
-
- inline result_type operator()(const argument_type &) const noexcept {
- return 66740831; // return a fundamentally attractive random value.
- }
- };
-
-} // namespace std
-
-#if defined(__GNUC__) && __GNUC__ >= 9
-#pragma GCC diagnostic pop
-#endif
-
-#endif // MPARK_VARIANT_HPP
diff --git a/src/libs/3rdparty/winpty/.gitattributes b/src/libs/3rdparty/winpty/.gitattributes
new file mode 100644
index 0000000000..36d4c60f1a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/.gitattributes
@@ -0,0 +1,19 @@
+* text=auto
+*.bat text eol=crlf
+*.c text
+*.cc text
+*.gyp text
+*.gypi text
+*.h text
+*.ps1 text eol=crlf
+*.rst text
+*.sh text
+*.txt text
+.gitignore text
+.gitattributes text
+Makefile text
+configure text
+
+*.sh eol=lf
+configure eol=lf
+VERSION.txt eol=lf
diff --git a/src/libs/3rdparty/winpty/.gitignore b/src/libs/3rdparty/winpty/.gitignore
new file mode 100644
index 0000000000..68c6b47fb3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/.gitignore
@@ -0,0 +1,16 @@
+*.sln
+*.suo
+*.vcxproj
+*.vcxproj.filters
+*.pyc
+winpty.sdf
+winpty.opensdf
+/config.mk
+/build
+/build-gyp
+/build-libpty
+/ship/packages
+/ship/tmp
+/src/Default
+/src/Release
+/src/gen
diff --git a/src/libs/3rdparty/winpty/CMakeLists.txt b/src/libs/3rdparty/winpty/CMakeLists.txt
new file mode 100644
index 0000000000..febd4f0ab6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(src)
diff --git a/src/libs/3rdparty/winpty/LICENSE b/src/libs/3rdparty/winpty/LICENSE
new file mode 100644
index 0000000000..246fbe0113
--- /dev/null
+++ b/src/libs/3rdparty/winpty/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2011-2016 Ryan Prichard
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/src/libs/3rdparty/winpty/README.md b/src/libs/3rdparty/winpty/README.md
new file mode 100644
index 0000000000..bc8e7d6e5f
--- /dev/null
+++ b/src/libs/3rdparty/winpty/README.md
@@ -0,0 +1,151 @@
+# winpty
+
+[![Build Status](https://ci.appveyor.com/api/projects/status/69tb9gylsph1ee1x/branch/master?svg=true)](https://ci.appveyor.com/project/rprichard/winpty/branch/master)
+
+winpty is a Windows software package providing an interface similar to a Unix
+pty-master for communicating with Windows console programs. The package
+consists of a library (libwinpty) and a tool for Cygwin and MSYS for running
+Windows console programs in a Cygwin/MSYS pty.
+
+The software works by starting the `winpty-agent.exe` process with a new,
+hidden console window, which bridges between the console API and terminal
+input/output escape codes. It polls the hidden console's screen buffer for
+changes and generates a corresponding stream of output.
+
+The Unix adapter allows running Windows console programs (e.g. CMD, PowerShell,
+IronPython, etc.) under `mintty` or Cygwin's `sshd` with
+properly-functioning input (e.g. arrow and function keys) and output (e.g. line
+buffering). The library could be also useful for writing a non-Cygwin SSH
+server.
+
+## Supported Windows versions
+
+winpty runs on Windows XP through Windows 10, including server versions. It
+can be compiled into either 32-bit or 64-bit binaries.
+
+## Cygwin/MSYS adapter (`winpty.exe`)
+
+### Prerequisites
+
+You need the following to build winpty:
+
+* A Cygwin or MSYS installation
+* GNU make
+* A MinGW g++ toolchain capable of compiling C++11 code to build `winpty.dll`
+ and `winpty-agent.exe`
+* A g++ toolchain targeting Cygwin or MSYS to build `winpty.exe`
+
+Winpty requires two g++ toolchains as it is split into two parts. The
+`winpty.dll` and `winpty-agent.exe` binaries interface with the native
+Windows command prompt window so they are compiled with the native MinGW
+toolchain. The `winpty.exe` binary interfaces with the MSYS/Cygwin terminal so
+it is compiled with the MSYS/Cygwin toolchain.
+
+MinGW appears to be split into two distributions -- MinGW (creates 32-bit
+binaries) and MinGW-w64 (creates both 32-bit and 64-bit binaries). Either
+one is generally acceptable.
+
+#### Cygwin packages
+
+The default g++ compiler for Cygwin targets Cygwin itself, but Cygwin also
+packages MinGW-w64 compilers. As of this writing, the necessary packages are:
+
+* Either `mingw64-i686-gcc-g++` or `mingw64-x86_64-gcc-g++`. Select the
+ appropriate compiler for your CPU architecture.
+* `gcc-g++`
+* `make`
+
+As of this writing (2016-01-23), only the MinGW-w64 compiler is acceptable.
+The MinGW compiler (e.g. from the `mingw-gcc-g++` package) is no longer
+maintained and is too buggy.
+
+#### MSYS packages
+
+For the original MSYS, use the `mingw-get` tool (MinGW Installation Manager),
+and select at least these components:
+
+* `mingw-developer-toolkit`
+* `mingw32-base`
+* `mingw32-gcc-g++`
+* `msys-base`
+* `msys-system-builder`
+
+When running `./configure`, make sure that `mingw32-g++` is in your
+`PATH`. It will be in the `C:\MinGW\bin` directory.
+
+#### MSYS2 packages
+
+For MSYS2, use `pacman` and install at least these packages:
+
+* `msys/gcc`
+* `mingw32/mingw-w64-i686-gcc` or `mingw64/mingw-w64-x86_64-gcc`. Select
+ the appropriate compiler for your CPU architecture.
+* `make`
+
+MSYS2 provides three start menu shortcuts for starting MSYS2:
+
+* MinGW-w64 Win32 Shell
+* MinGW-w64 Win64 Shell
+* MSYS2 Shell
+
+To build winpty, use the MinGW-w64 {Win32,Win64} shortcut of the architecture
+matching MSYS2. These shortcuts will put the g++ compiler from the
+`{mingw32,mingw64}/mingw-w64-{i686,x86_64}-gcc` packages into the `PATH`.
+
+Alternatively, instead of installing `mingw32/mingw-w64-i686-gcc` or
+`mingw64/mingw-w64-x86_64-gcc`, install the `mingw-w64-cross-gcc` and
+`mingw-w64-cross-crt-git` packages. These packages install cross-compilers
+into `/opt/bin`, and then any of the three shortcuts will work.
+
+### Building the Unix adapter
+
+In the project directory, run `./configure`, then `make`, then `make install`.
+By default, winpty is installed into `/usr/local`. Pass `PREFIX=<path>` to
+`make install` to override this default.
+
+### Using the Unix adapter
+
+To run a Windows console program in `mintty` or Cygwin `sshd`, prepend
+`winpty` to the command-line:
+
+ $ winpty powershell
+ Windows PowerShell
+ Copyright (C) 2009 Microsoft Corporation. All rights reserved.
+
+ PS C:\rprichard\proj\winpty> 10 + 20
+ 30
+ PS C:\rprichard\proj\winpty> exit
+
+## Embedding winpty / MSVC compilation
+
+See `src/include/winpty.h` for the prototypes of functions exported by
+`winpty.dll`.
+
+Only the `winpty.exe` binary uses Cygwin; all the other binaries work without
+it and can be compiled with either MinGW or MSVC. To compile using MSVC,
+download gyp and run `gyp -I configurations.gypi` in the `src` subdirectory.
+This will generate a `winpty.sln` and associated project files. See the
+`src/winpty.gyp` and `src/configurations.gypi` files for notes on dealing with
+MSVC versions and different architectures.
+
+Compiling winpty with MSVC currently requires MSVC 2013 or newer.
+
+## Debugging winpty
+
+winpty comes with a tool for collecting timestamped debugging output. To use
+it:
+
+1. Run `winpty-debugserver.exe` on the same computer as winpty.
+2. Set the `WINPTY_DEBUG` environment variable to `trace` for the
+ `winpty.exe` process and/or the process using `libwinpty.dll`.
+
+winpty also recognizes a `WINPTY_SHOW_CONSOLE` environment variable. Set it
+to 1 to prevent winpty from hiding the console window.
+
+## Copyright
+
+This project is distributed under the MIT license (see the `LICENSE` file in
+the project root).
+
+By submitting a pull request for this project, you agree to license your
+contribution under the MIT license to this project.
diff --git a/src/libs/3rdparty/winpty/RELEASES.md b/src/libs/3rdparty/winpty/RELEASES.md
new file mode 100644
index 0000000000..768cdf90e3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/RELEASES.md
@@ -0,0 +1,280 @@
+# Next Version
+
+Input handling changes:
+
+ * Improve Ctrl-C handling with programs that use unprocessed input. (e.g.
+ Ctrl-C now cancels input with PowerShell on Windows 10.)
+ [#116](https://github.com/rprichard/winpty/issues/116)
+ * Fix a theoretical issue with input event ordering.
+ [#117](https://github.com/rprichard/winpty/issues/117)
+ * Ctrl/Shift+{Arrow,Home,End} keys now work with IntelliJ.
+ [#118](https://github.com/rprichard/winpty/issues/118)
+
+# Version 0.4.3 (2017-05-17)
+
+Input handling changes:
+
+ * winpty sets `ENHANCED_KEY` for arrow and navigation keys. This fixes an
+ issue with the Ruby REPL.
+ [#99](https://github.com/rprichard/winpty/issues/99)
+ * AltGr keys are handled better now.
+ [#109](https://github.com/rprichard/winpty/issues/109)
+ * In `ENABLE_VIRTUAL_TERMINAL_INPUT` mode, when typing Home/End with a
+ modifier (e.g. Ctrl), winpty now generates an H/F escape sequence like
+ `^[[1;5F` rather than a 1/4 escape like `^[[4;5~`.
+ [#114](https://github.com/rprichard/winpty/issues/114)
+
+Resizing and scraping fixes:
+
+ * winpty now synthesizes a `WINDOW_BUFFER_SIZE_EVENT` event after resizing
+ the console to better propagate window size changes to console programs.
+ In particular, this affects WSL and Cygwin.
+ [#110](https://github.com/rprichard/winpty/issues/110)
+ * Better handling of resizing for certain full-screen programs, like
+ WSL less.
+ [#112](https://github.com/rprichard/winpty/issues/112)
+ * Hide the cursor if it's currently outside the console window. This change
+ fixes an issue with Far Manager.
+ [#113](https://github.com/rprichard/winpty/issues/113)
+ * winpty now avoids using console fonts smaller than 5px high to improve
+ half-vs-full-width character handling. See
+ https://github.com/Microsoft/vscode/issues/19665.
+ [b4db322010](https://github.com/rprichard/winpty/commit/b4db322010d2d897e6c496fefc4f0ecc9b84c2f3)
+
+Cygwin/MSYS adapter fix:
+
+ * The way the `winpty` Cygwin/MSYS2 adapter searches for the program to
+ launch changed. It now resolves symlinks and searches the PATH explicitly.
+ [#81](https://github.com/rprichard/winpty/issues/81)
+ [#98](https://github.com/rprichard/winpty/issues/98)
+
+This release does not include binaries for the old MSYS1 project anymore.
+MSYS2 will continue to be supported. See
+https://github.com/rprichard/winpty/issues/97.
+
+# Version 0.4.2 (2017-01-18)
+
+This release improves WSL support (i.e. Bash-on-Windows):
+
+ * winpty generates more correct input escape sequences for WSL programs that
+ enable an alternate input mode using DECCKM. This bug affected arrow keys
+ and Home/End in WSL programs such as `vim`, `mc`, and `less`.
+ [#90](https://github.com/rprichard/winpty/issues/90)
+ * winpty now recognizes the `COMMON_LVB_REVERSE_VIDEO` and
+ `COMMON_LVB_UNDERSCORE` text attributes. The Windows console uses these
+ attributes to implement the SGR.4(Underline) and SGR.7(Negative) modes in
+ its VT handling. This change affects WSL pager status bars, man pages, etc.
+
+The build system no longer has a "version suffix" mechanism, so passing
+`VERSION_SUFFIX=<suffix>` to make or `-D VERSION_SUFFIX=<suffix>` to gyp now
+has no effect. AFAIK, the mechanism was never used publicly.
+[67a34b6c03](https://github.com/rprichard/winpty/commit/67a34b6c03557a5c2e0a2bdd502c2210921d8f3e)
+
+# Version 0.4.1 (2017-01-03)
+
+Bug fixes:
+
+ * This version fixes a bug where the `winpty-agent.exe` process could read
+ past the end of a buffer.
+ [#94](https://github.com/rprichard/winpty/issues/94)
+
+# Version 0.4.0 (2016-06-28)
+
+The winpty library has a new API that should be easier for embedding.
+[880c00c69e](https://github.com/rprichard/winpty/commit/880c00c69eeca73643ddb576f02c5badbec81f56)
+
+User-visible changes:
+
+ * winpty now automatically puts the terminal into mouse mode when it detects
+ that the console has left QuickEdit mode. The `--mouse` option still forces
+ the terminal into mouse mode. In principle, an option could be added to
+ suppress terminal mode, but hopefully it won't be necessary. There is a
+ script in the `misc` subdirectory, `misc/ConinMode.ps1`, that can change
+ the QuickEdit mode from the command-line.
+ * winpty now passes keyboard escapes to `bash.exe` in the Windows Subsystem
+ for Linux.
+ [#82](https://github.com/rprichard/winpty/issues/82)
+
+Bug fixes:
+
+ * By default, `winpty.dll` avoids calling `SetProcessWindowStation` within
+ the calling process.
+ [#58](https://github.com/rprichard/winpty/issues/58)
+ * Fixed an uninitialized memory bug that could have crashed winpty.
+ [#80](https://github.com/rprichard/winpty/issues/80)
+ * winpty now works better with very large and very small terminal windows.
+ It resizes the console font according to the number of columns.
+ [#61](https://github.com/rprichard/winpty/issues/61)
+ * winpty no longer uses Mark to freeze the console on Windows 10. The Mark
+ command could interfere with the cursor position, corrupting the data in
+ the screen buffer.
+ [#79](https://github.com/rprichard/winpty/issues/79)
+
+# Version 0.3.0 (2016-05-20)
+
+User-visible changes:
+
+ * The UNIX adapter is renamed from `console.exe` to `winpty.exe` to be
+ consistent with MSYS2. The name `winpty.exe` is less likely to conflict
+ with another program and is easier to search for online (e.g. for someone
+ unfamiliar with winpty).
+ * The UNIX adapter now clears the `TERM` variable.
+ [#43](https://github.com/rprichard/winpty/issues/43)
+ * An escape character appearing in a console screen buffer cell is converted
+ to a '?'.
+ [#47](https://github.com/rprichard/winpty/issues/47)
+
+Bug fixes:
+
+ * A major bug affecting XP users was fixed.
+ [#67](https://github.com/rprichard/winpty/issues/67)
+ * Fixed an incompatibility with ConEmu where winpty hung if ConEmu's
+ "Process 'start'" feature was enabled.
+ [#70](https://github.com/rprichard/winpty/issues/70)
+ * Fixed a bug where `cmd.exe` sometimes printed the message,
+ `Not enough storage is available to process this command.`.
+ [#74](https://github.com/rprichard/winpty/issues/74)
+
+Many changes internally:
+
+ * The codebase is switched from C++03 to C++11 and uses exceptions internally.
+ No exceptions are thrown across the C APIs defined in `winpty.h`.
+ * This version drops support for the original MinGW compiler packaged with
+ Cygwin (`i686-pc-mingw32-g++`). The MinGW-w64 compiler is still supported,
+ as is the MinGW distributed at mingw.org. Compiling with MSVC now requires
+ MSVC 2013 or newer. Windows XP is still supported.
+ [ec3eae8df5](https://github.com/rprichard/winpty/commit/ec3eae8df5bbbb36d7628d168b0815638d122f37)
+ * Pipe security is improved. winpty works harder to produce unique pipe names
+ and includes a random component in the name. winpty secures pipes with a
+ DACL that prevents arbitrary users from connecting to its pipes. winpty now
+ passes `PIPE_REJECT_REMOTE_CLIENTS` on Vista and up, and it verifies that
+ the pipe client PID is correct, again on Vista and up. When connecting to a
+ named pipe, winpty uses the `SECURITY_IDENTIFICATION` flag to restrict
+ impersonation. Previous versions *should* still be secure.
+ * `winpty-debugserver.exe` now has an `--everyone` flag that allows capturing
+ debug output from other users.
+ * The code now compiles cleanly with MSVC's "Security Development Lifecycle"
+ (`/SDL`) checks enabled.
+
+# Version 0.2.2 (2016-02-25)
+
+Minor bug fixes and enhancements:
+
+ * Fix a bug that generated spurious mouse input records when an incomplete
+ mouse escape sequence was seen.
+ * Fix a buffer overflow bug in `winpty-debugserver.exe` affecting messages of
+ exactly 4096 bytes.
+ * For MSVC builds, add a `src/configurations.gypi` file that can be included
+ on the gyp command-line to enable 32-bit and 64-bit builds.
+ * `winpty-agent --show-input` mode: Flush stdout after each line.
+ * Makefile builds: generate a `build/winpty.lib` import library to accompany
+ `build/winpty.dll`.
+
+# Version 0.2.1 (2015-12-19)
+
+ * The main project source was moved into a `src` directory for better code
+ organization and to fix
+ [#51](https://github.com/rprichard/winpty/issues/51).
+ * winpty recognizes many more escape sequences, including:
+ * putty/rxvt's F1-F4 keys
+ [#40](https://github.com/rprichard/winpty/issues/40)
+ * the Linux virtual console's F1-F5 keys
+ * the "application numpad" keys (e.g. enabled with DECPAM)
+ * Fixed handling of Shift-Alt-O and Alt-[.
+ * Added support for mouse input. The UNIX adapter has a `--mouse` argument
+ that puts the terminal into mouse mode, but the agent recognizes mouse
+ input even without the argument. The agent recognizes double-clicks using
+ Windows' double-click interval setting (i.e. GetDoubleClickTime).
+ [#57](https://github.com/rprichard/winpty/issues/57)
+
+Changes to debugging interfaces:
+
+ * The `WINPTY_DEBUG` variable is now a comma-separated list. The old
+ behavior (i.e. tracing) is enabled with `WINPTY_DEBUG=trace`.
+ * The UNIX adapter program now has a `--showkey` argument that dumps input
+ bytes.
+ * The `winpty-agent.exe` program has a `--show-input` argument that dumps
+ `INPUT_RECORD` records. (It omits mouse events unless `--with-mouse` is
+ also specified.) The agent also responds to `WINPTY_DEBUG=trace,input`,
+ which logs input bytes and synthesized console events, and it responds to
+ `WINPTY_DEBUG=trace,dump_input_map`, which dumps the internal table of
+ escape sequences.
+
+# Version 0.2.0 (2015-11-13)
+
+No changes to the API, but many small changes to the implementation. The big
+changes include:
+
+ * Support for 64-bit Cygwin and MSYS2
+ * Support for Windows 10
+ * Better Unicode support (especially East Asian languages)
+
+Details:
+
+ * The `configure` script recognizes 64-bit Cygwin and MSYS2 environments and
+ selects the appropriate compiler.
+ * winpty works much better with the upgraded console in Windows 10. The
+ `conhost.exe` hang can still occur, but only with certain programs, and
+ is much less likely to occur. With the new console, use Mark instead of
+ SelectAll, for better performance.
+ [#31](https://github.com/rprichard/winpty/issues/31)
+ [#30](https://github.com/rprichard/winpty/issues/30)
+ [#53](https://github.com/rprichard/winpty/issues/53)
+ * The UNIX adapter now calls `setlocale(LC_ALL, "")` to set the locale.
+ * Improved Unicode support. When a console is started with an East Asian code
+ page, winpty now chooses an East Asian font rather than Consolas / Lucida
+ Console. Selecting the right font helps synchronize character widths
+ between the console and terminal. (It's not perfect, though.)
+ [#41](https://github.com/rprichard/winpty/issues/41)
+ * winpty now more-or-less works with programs that change the screen buffer
+ or resize the original screen buffer. If the screen buffer height changes,
+ winpty switches to a "direct mode", where it makes no effort to track
+ scrolling. In direct mode, it merely syncs snapshots of the console to the
+ terminal. Caveats:
+ * Changing the screen buffer (i.e. `SetConsoleActiveScreenBuffer`)
+ breaks winpty on Windows 7. This problem can eventually be mitigated,
+ but never completely fixed, due to Windows 7 bugginess.
+ * Resizing the original screen buffer can hang `conhost.exe` on Windows 10.
+ Enabling the legacy console is a workaround.
+ * If a program changes the screen buffer and then exits, relying on the OS
+ to restore the original screen buffer, that restoration probably will not
+ happen with winpty. winpty's behavior can probably be improved here.
+ * Improved color handling:
+ * DkGray-on-Black text was previously hiddenly completely. Now it is
+ output as DkGray, with a fallback to LtGray on terminals that don't
+ recognize the intense colors.
+ [#39](https://github.com/rprichard/winpty/issues/39).
+ * The console is always initialized to LtGray-on-Black, regardless of the
+ user setting, which matches the console color heuristic, which translates
+ LtGray-on-Black to "reset SGR parameters."
+ * Shift-Tab is recognized correctly now.
+ [#19](https://github.com/rprichard/winpty/issues/19)
+ * Add a `--version` argument to `winpty-agent.exe` and the UNIX adapter. The
+ argument reports the nominal version (i.e. the `VERSION.txt`) file, with a
+ "VERSION_SUFFIX" appended (defaulted to `-dev`), and a git commit hash, if
+ the `git` command successfully reports a hash during the build. The `git`
+ command is invoked by either `make` or `gyp`.
+ * The agent now combines `ReadConsoleOutputW` calls when it polls the console
+ buffer for changes, which may slightly reduce its CPU overhead.
+ [#44](https://github.com/rprichard/winpty/issues/44).
+ * A `gyp` file is added to help compile with MSVC.
+ * The code can now be compiled as C++11 code, though it isn't by default.
+ [bde8922e08](https://github.com/rprichard/winpty/commit/bde8922e08c3638e01ecc7b581b676c314163e3c)
+ * If winpty can't create a new window station, it charges ahead rather than
+ aborting. This situation might happen if winpty were started from an SSH
+ session.
+ * Debugging improvements:
+ * `WINPTYDBG` is renamed to `WINPTY_DEBUG`, and a new `WINPTY_SHOW_CONSOLE`
+ variable keeps the underlying console visible.
+ * A `winpty-debugserver.exe` program is built and shipped by default. It
+ collects the trace output enabled with `WINPTY_DEBUG`.
+ * The `Makefile` build of winpty now compiles `winpty-agent.exe` and
+ `winpty.dll` with -O2.
+
+# Version 0.1.1 (2012-07-28)
+
+Minor bugfix release.
+
+# Version 0.1 (2012-04-17)
+
+Initial release.
diff --git a/src/libs/3rdparty/winpty/VERSION.txt b/src/libs/3rdparty/winpty/VERSION.txt
new file mode 100644
index 0000000000..5d47ff8c45
--- /dev/null
+++ b/src/libs/3rdparty/winpty/VERSION.txt
@@ -0,0 +1 @@
+0.4.4-dev
diff --git a/src/libs/3rdparty/winpty/appveyor.yml b/src/libs/3rdparty/winpty/appveyor.yml
new file mode 100644
index 0000000000..a9e8726fc1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/appveyor.yml
@@ -0,0 +1,16 @@
+image: Visual Studio 2015
+
+init:
+ - C:\msys64\usr\bin\bash --login -c "pacman -S --needed --noconfirm --noprogressbar msys/make msys/tar msys/gcc mingw-w64-cross-toolchain"
+ - C:\cygwin\setup-x86 -q -P mingw64-i686-gcc-g++,mingw64-x86_64-gcc-g++,make
+ - C:\cygwin64\setup-x86_64 -q -P mingw64-i686-gcc-g++,mingw64-x86_64-gcc-g++,make
+
+build_script:
+ - C:\Python27-x64\python.exe ship\ship.py --kind msys2 --arch x64 --syspath C:\msys64
+ - C:\Python27-x64\python.exe ship\ship.py --kind cygwin --arch ia32 --syspath C:\cygwin
+ - C:\Python27-x64\python.exe ship\ship.py --kind cygwin --arch x64 --syspath C:\cygwin64
+ - C:\Python27-x64\python.exe ship\make_msvc_package.py
+
+artifacts:
+ - path: ship\packages\*.tar.gz
+ - path: ship\packages\*.zip
diff --git a/src/libs/3rdparty/winpty/configure b/src/libs/3rdparty/winpty/configure
new file mode 100644
index 0000000000..6d37d65b09
--- /dev/null
+++ b/src/libs/3rdparty/winpty/configure
@@ -0,0 +1,167 @@
+#!/bin/bash
+#
+# Copyright (c) 2011-2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+#
+# findTool(desc, commandList)
+#
+# Searches commandLine for the first command in the PATH and returns it.
+# Prints an error and aborts the script if no match is found.
+#
+FINDTOOL_OUT=""
+function findTool {
+ DESC=$1
+ OPTIONS=$2
+ for CMD in ${OPTIONS}; do
+ if (which $CMD &>/dev/null) then
+ echo "Found $DESC: $CMD"
+ FINDTOOL_OUT="$CMD"
+ return
+ fi
+ done
+ echo "Error: could not find $DESC. One of these should be in your PATH:"
+ for CMD in ${OPTIONS}; do
+ echo " * $CMD"
+ done
+ exit 1
+}
+
+IS_CYGWIN=0
+IS_MSYS1=0
+IS_MSYS2=0
+
+# Link parts of the Cygwin binary statically to aid in redistribution? The
+# binary still links dynamically against the main DLL. The MinGW binaries are
+# also statically linked and therefore depend only on Windows DLLs. I started
+# linking the Cygwin/MSYS binary statically, because G++ 4.7 changed the
+# Windows C++ ABI.
+UNIX_LDFLAGS_STATIC='-static -static-libgcc -static-libstdc++'
+
+# Detect the environment -- Cygwin or MSYS.
+case $(uname -s) in
+ CYGWIN*)
+ echo 'uname -s identifies a Cygwin environment.'
+ IS_CYGWIN=1
+ case $(uname -m) in
+ i686)
+ echo 'uname -m identifies an i686 environment.'
+ UNIX_CXX=i686-pc-cygwin-g++
+ MINGW_CXX=i686-w64-mingw32-g++
+ ;;
+ x86_64)
+ echo 'uname -m identifies an x86_64 environment.'
+ UNIX_CXX=x86_64-pc-cygwin-g++
+ MINGW_CXX=x86_64-w64-mingw32-g++
+ ;;
+ *)
+ echo 'Error: uname -m did not match either i686 or x86_64.'
+ exit 1
+ ;;
+ esac
+ ;;
+ MSYS*|MINGW*)
+ # MSYS2 notes:
+ # - MSYS2 offers two shortcuts to open an environment:
+ # - MinGW-w64 Win32 Shell. This env reports a `uname -s` of
+ # MINGW32_NT-6.1 on 32-bit Win7. The MinGW-w64 compiler
+ # (i686-w64-mingw32-g++.exe) is in the PATH.
+ # - MSYS2 Shell. `uname -s` instead reports MSYS_NT-6.1.
+ # The i686-w64-mingw32-g++ compiler is not in the PATH.
+ # - MSYS2 appears to use MinGW-w64, not the older mingw.org.
+ # MSYS notes:
+ # - `uname -s` is always MINGW32_NT-6.1 on Win7.
+ echo 'uname -s identifies an MSYS/MSYS2 environment.'
+ case $(uname -m) in
+ i686)
+ echo 'uname -m identifies an i686 environment.'
+ UNIX_CXX=i686-pc-msys-g++
+ if echo "$(uname -r)" | grep '^1[.]' > /dev/null; then
+ # The MSYS-targeting compiler for the original 32-bit-only
+ # MSYS does not recognize the -static-libstdc++ flag, and
+ # it does not work with -static, because it tries to link
+ # statically with the core MSYS library and fails.
+ #
+ # Distinguish between the two using the major version
+ # number of `uname -r`:
+ #
+ # MSYS uname -r: 1.0.18(0.48/3/2)
+ # MSYS2 uname -r: 2.0.0(0.284/5/3)
+ #
+ # This is suboptimal because MSYS2 is not actually the
+ # second version of MSYS--it's a brand-new fork of Cygwin.
+ #
+ IS_MSYS1=1
+ UNIX_LDFLAGS_STATIC=
+ MINGW_CXX=mingw32-g++
+ else
+ IS_MSYS2=1
+ MINGW_CXX=i686-w64-mingw32-g++.exe
+ fi
+ ;;
+ x86_64)
+ echo 'uname -m identifies an x86_64 environment.'
+ IS_MSYS2=1
+ UNIX_CXX=x86_64-pc-msys-g++
+ MINGW_CXX=x86_64-w64-mingw32-g++
+ ;;
+ *)
+ echo 'Error: uname -m did not match either i686 or x86_64.'
+ exit 1
+ ;;
+ esac
+ ;;
+ *)
+ echo 'Error: uname -s did not match either CYGWIN* or MINGW*.'
+ exit 1
+ ;;
+esac
+
+# Search the PATH and pick the first match.
+findTool "Cygwin/MSYS G++ compiler" "$UNIX_CXX"
+UNIX_CXX=$FINDTOOL_OUT
+findTool "MinGW G++ compiler" "$MINGW_CXX"
+MINGW_CXX=$FINDTOOL_OUT
+
+# Write config files.
+echo Writing config.mk
+echo UNIX_CXX=$UNIX_CXX > config.mk
+echo UNIX_LDFLAGS_STATIC=$UNIX_LDFLAGS_STATIC >> config.mk
+echo MINGW_CXX=$MINGW_CXX >> config.mk
+
+if test $IS_MSYS1 = 1; then
+ echo UNIX_CXXFLAGS += -DWINPTY_TARGET_MSYS1 >> config.mk
+ # The MSYS1 MinGW compiler has a bug that prevents inclusion of algorithm
+ # and math.h in normal C++11 mode. The workaround is to enable the gnu++11
+ # mode instead. The bug was fixed on 2015-07-31, but as of 2016-02-26, the
+ # fix apparently hasn't been released. See
+ # http://ehc.ac/p/mingw/bugs/2250/.
+ echo MINGW_ENABLE_CXX11_FLAG := -std=gnu++11 >> config.mk
+fi
+
+if test -d .git -a -f .git/HEAD -a -f .git/index && git rev-parse HEAD >&/dev/null; then
+ echo "Commit info: git"
+ echo 'COMMIT_HASH = $(shell git rev-parse HEAD)' >> config.mk
+ echo 'COMMIT_HASH_DEP := config.mk .git/HEAD .git/index' >> config.mk
+else
+ echo "Commit info: none"
+ echo 'COMMIT_HASH := none' >> config.mk
+ echo 'COMMIT_HASH_DEP := config.mk' >> config.mk
+fi
diff --git a/src/libs/3rdparty/winpty/misc/.gitignore b/src/libs/3rdparty/winpty/misc/.gitignore
new file mode 100644
index 0000000000..23751645fa
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/.gitignore
@@ -0,0 +1,2 @@
+*.exe
+UnixEcho \ No newline at end of file
diff --git a/src/libs/3rdparty/winpty/misc/BufferResizeTests.cc b/src/libs/3rdparty/winpty/misc/BufferResizeTests.cc
new file mode 100644
index 0000000000..a5bb074826
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/BufferResizeTests.cc
@@ -0,0 +1,90 @@
+#include <windows.h>
+#include <cassert>
+
+#include "TestUtil.cc"
+
+void dumpInfoToTrace() {
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ assert(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info));
+ trace("win=(%d,%d,%d,%d)",
+ (int)info.srWindow.Left,
+ (int)info.srWindow.Top,
+ (int)info.srWindow.Right,
+ (int)info.srWindow.Bottom);
+ trace("buf=(%d,%d)",
+ (int)info.dwSize.X,
+ (int)info.dwSize.Y);
+ trace("cur=(%d,%d)",
+ (int)info.dwCursorPosition.X,
+ (int)info.dwCursorPosition.Y);
+}
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ setWindowPos(0, 0, 1, 1);
+
+ if (false) {
+ // Reducing the buffer height can move the window up.
+ setBufferSize(80, 25);
+ setWindowPos(0, 20, 80, 5);
+ Sleep(2000);
+ setBufferSize(80, 10);
+ }
+
+ if (false) {
+ // Reducing the buffer height moves the window up and the buffer
+ // contents up too.
+ setBufferSize(80, 25);
+ setWindowPos(0, 20, 80, 5);
+ setCursorPos(0, 20);
+ printf("TEST1\nTEST2\nTEST3\nTEST4\n");
+ fflush(stdout);
+ Sleep(2000);
+ setBufferSize(80, 10);
+ }
+
+ if (false) {
+ // Reducing the buffer width can move the window left.
+ setBufferSize(80, 25);
+ setWindowPos(40, 0, 40, 25);
+ Sleep(2000);
+ setBufferSize(60, 25);
+ }
+
+ if (false) {
+ // Sometimes the buffer contents are shifted up; sometimes they're
+ // shifted down. It seems to depend on the cursor position?
+
+ // setBufferSize(80, 25);
+ // setWindowPos(0, 20, 80, 5);
+ // setCursorPos(0, 20);
+ // printf("TESTa\nTESTb\nTESTc\nTESTd\nTESTe");
+ // fflush(stdout);
+ // setCursorPos(0, 0);
+ // printf("TEST1\nTEST2\nTEST3\nTEST4\nTEST5");
+ // fflush(stdout);
+ // setCursorPos(0, 24);
+ // Sleep(5000);
+ // setBufferSize(80, 24);
+
+ setBufferSize(80, 20);
+ setWindowPos(0, 10, 80, 10);
+ setCursorPos(0, 18);
+
+ printf("TEST1\nTEST2");
+ fflush(stdout);
+ setCursorPos(0, 18);
+
+ Sleep(2000);
+ setBufferSize(80, 18);
+ }
+
+ dumpInfoToTrace();
+ Sleep(30000);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ChangeScreenBuffer.cc b/src/libs/3rdparty/winpty/misc/ChangeScreenBuffer.cc
new file mode 100644
index 0000000000..701a2cb4a3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ChangeScreenBuffer.cc
@@ -0,0 +1,53 @@
+// A test program for CreateConsoleScreenBuffer / SetConsoleActiveScreenBuffer
+//
+
+#include <windows.h>
+#include <stdio.h>
+#include <conio.h>
+#include <io.h>
+#include <cassert>
+
+#include "TestUtil.cc"
+
+int main()
+{
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+ HANDLE childBuffer = CreateConsoleScreenBuffer(
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
+
+ SetConsoleActiveScreenBuffer(childBuffer);
+
+ while (true) {
+ char buf[1024];
+ CONSOLE_SCREEN_BUFFER_INFO info;
+
+ assert(GetConsoleScreenBufferInfo(origBuffer, &info));
+ trace("child.size=(%d,%d)", (int)info.dwSize.X, (int)info.dwSize.Y);
+ trace("child.cursor=(%d,%d)", (int)info.dwCursorPosition.X, (int)info.dwCursorPosition.Y);
+ trace("child.window=(%d,%d,%d,%d)",
+ (int)info.srWindow.Left, (int)info.srWindow.Top,
+ (int)info.srWindow.Right, (int)info.srWindow.Bottom);
+ trace("child.maxSize=(%d,%d)", (int)info.dwMaximumWindowSize.X, (int)info.dwMaximumWindowSize.Y);
+
+ int ch = getch();
+ sprintf(buf, "%02x\n", ch);
+ DWORD actual = 0;
+ WriteFile(childBuffer, buf, strlen(buf), &actual, NULL);
+ if (ch == 0x1b/*ESC*/ || ch == 0x03/*CTRL-C*/)
+ break;
+
+ if (ch == 'b') {
+ setBufferSize(origBuffer, 40, 25);
+ } else if (ch == 'w') {
+ setWindowPos(origBuffer, 1, 1, 38, 23);
+ } else if (ch == 'c') {
+ setCursorPos(origBuffer, 10, 10);
+ }
+ }
+
+ SetConsoleActiveScreenBuffer(origBuffer);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ClearConsole.cc b/src/libs/3rdparty/winpty/misc/ClearConsole.cc
new file mode 100644
index 0000000000..f95f8c84ca
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ClearConsole.cc
@@ -0,0 +1,72 @@
+/*
+ * Demonstrates that console clearing sets each cell's character to SP, not
+ * NUL, and it sets the attribute of each cell to the current text attribute.
+ *
+ * This confirms the MSDN instruction in the "Clearing the Screen" article.
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682022(v=vs.85).aspx
+ * It advises using GetConsoleScreenBufferInfo to get the current text
+ * attribute, then FillConsoleOutputCharacter and FillConsoleOutputAttribute to
+ * write to the console buffer.
+ */
+
+#include <windows.h>
+
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ SetConsoleTextAttribute(conout, 0x24);
+ system("cls");
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(80, 25);
+ setWindowPos(0, 0, 80, 25);
+
+ CHAR_INFO buf;
+ COORD bufSize = { 1, 1 };
+ COORD bufCoord = { 0, 0 };
+ SMALL_RECT rect = { 5, 5, 5, 5 };
+ BOOL ret;
+ DWORD actual;
+ COORD writeCoord = { 5, 5 };
+
+ // After cls, each cell's character is a space, and its attributes are the
+ // default text attributes.
+ ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect);
+ assert(ret && buf.Char.UnicodeChar == L' ' && buf.Attributes == 0x24);
+
+ // Nevertheless, it is possible to change a cell to NUL.
+ ret = FillConsoleOutputCharacterW(conout, L'\0', 1, writeCoord, &actual);
+ assert(ret && actual == 1);
+ ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect);
+ assert(ret && buf.Char.UnicodeChar == L'\0' && buf.Attributes == 0x24);
+
+ // As well as a 0 attribute. (As one would expect, the cell is
+ // black-on-black.)
+ ret = FillConsoleOutputAttribute(conout, 0, 1, writeCoord, &actual);
+ assert(ret && actual == 1);
+ ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect);
+ assert(ret && buf.Char.UnicodeChar == L'\0' && buf.Attributes == 0);
+ ret = FillConsoleOutputCharacterW(conout, L'X', 1, writeCoord, &actual);
+ assert(ret && actual == 1);
+ ret = ReadConsoleOutputW(conout, &buf, bufSize, bufCoord, &rect);
+ assert(ret && buf.Char.UnicodeChar == L'X' && buf.Attributes == 0);
+
+ // The 'X' is invisible.
+ countDown(3);
+
+ ret = FillConsoleOutputAttribute(conout, 0x42, 1, writeCoord, &actual);
+ assert(ret && actual == 1);
+
+ countDown(5);
+}
diff --git a/src/libs/3rdparty/winpty/misc/ConinMode.cc b/src/libs/3rdparty/winpty/misc/ConinMode.cc
new file mode 100644
index 0000000000..1e1428d8b0
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ConinMode.cc
@@ -0,0 +1,117 @@
+#include <windows.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+static HANDLE getConin() {
+ HANDLE conin = GetStdHandle(STD_INPUT_HANDLE);
+ if (conin == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "error: cannot get stdin\n");
+ exit(1);
+ }
+ return conin;
+}
+
+static DWORD getConsoleMode() {
+ DWORD mode = 0;
+ if (!GetConsoleMode(getConin(), &mode)) {
+ fprintf(stderr, "error: GetConsoleMode failed (is stdin a console?)\n");
+ exit(1);
+ }
+ return mode;
+}
+
+static void setConsoleMode(DWORD mode) {
+ if (!SetConsoleMode(getConin(), mode)) {
+ fprintf(stderr, "error: SetConsoleMode failed (is stdin a console?)\n");
+ exit(1);
+ }
+}
+
+static long parseInt(const std::string &s) {
+ errno = 0;
+ char *endptr = nullptr;
+ long result = strtol(s.c_str(), &endptr, 0);
+ if (errno != 0 || !endptr || *endptr != '\0') {
+ fprintf(stderr, "error: could not parse integral argument '%s'\n", s.c_str());
+ exit(1);
+ }
+ return result;
+}
+
+static void usage() {
+ printf("Usage: ConinMode [verb] [options]\n");
+ printf("Verbs:\n");
+ printf(" [info] Dumps info about mode flags.\n");
+ printf(" get Prints the mode DWORD.\n");
+ printf(" set VALUE Sets the mode to VALUE, which can be decimal, hex, or octal.\n");
+ printf(" set VALUE MASK\n");
+ printf(" Same as `set VALUE`, but only alters the bits in MASK.\n");
+ exit(1);
+}
+
+struct {
+ const char *name;
+ DWORD value;
+} kInputFlags[] = {
+ "ENABLE_PROCESSED_INPUT", ENABLE_PROCESSED_INPUT, // 0x0001
+ "ENABLE_LINE_INPUT", ENABLE_LINE_INPUT, // 0x0002
+ "ENABLE_ECHO_INPUT", ENABLE_ECHO_INPUT, // 0x0004
+ "ENABLE_WINDOW_INPUT", ENABLE_WINDOW_INPUT, // 0x0008
+ "ENABLE_MOUSE_INPUT", ENABLE_MOUSE_INPUT, // 0x0010
+ "ENABLE_INSERT_MODE", ENABLE_INSERT_MODE, // 0x0020
+ "ENABLE_QUICK_EDIT_MODE", ENABLE_QUICK_EDIT_MODE, // 0x0040
+ "ENABLE_EXTENDED_FLAGS", ENABLE_EXTENDED_FLAGS, // 0x0080
+ "ENABLE_VIRTUAL_TERMINAL_INPUT", 0x0200/*ENABLE_VIRTUAL_TERMINAL_INPUT*/, // 0x0200
+};
+
+int main(int argc, char *argv[]) {
+ std::vector<std::string> args;
+ for (size_t i = 1; i < argc; ++i) {
+ args.push_back(argv[i]);
+ }
+
+ if (args.empty() || args.size() == 1 && args[0] == "info") {
+ DWORD mode = getConsoleMode();
+ printf("mode: 0x%lx\n", mode);
+ for (const auto &flag : kInputFlags) {
+ printf("%-29s 0x%04lx %s\n", flag.name, flag.value, flag.value & mode ? "ON" : "off");
+ mode &= ~flag.value;
+ }
+ for (int i = 0; i < 32; ++i) {
+ if (mode & (1u << i)) {
+ printf("Unrecognized flag: %04x\n", (1u << i));
+ }
+ }
+ return 0;
+ }
+
+ const auto verb = args[0];
+
+ if (verb == "set") {
+ if (args.size() == 2) {
+ const DWORD newMode = parseInt(args[1]);
+ setConsoleMode(newMode);
+ } else if (args.size() == 3) {
+ const DWORD mode = parseInt(args[1]);
+ const DWORD mask = parseInt(args[2]);
+ const int newMode = (getConsoleMode() & ~mask) | (mode & mask);
+ setConsoleMode(newMode);
+ } else {
+ usage();
+ }
+ } else if (verb == "get") {
+ if (args.size() != 1) {
+ usage();
+ }
+ printf("0x%lx\n", getConsoleMode());
+ } else {
+ usage();
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ConinMode.ps1 b/src/libs/3rdparty/winpty/misc/ConinMode.ps1
new file mode 100644
index 0000000000..ecfe8f039e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ConinMode.ps1
@@ -0,0 +1,116 @@
+#
+# PowerShell script for controlling the console QuickEdit and InsertMode flags.
+#
+# Turn QuickEdit off to interact with mouse-driven console programs.
+#
+# Usage:
+#
+# powershell .\ConinMode.ps1 [Options]
+#
+# Options:
+# -QuickEdit [on/off]
+# -InsertMode [on/off]
+# -Mode [integer]
+#
+
+param (
+ [ValidateSet("on", "off")][string] $QuickEdit,
+ [ValidateSet("on", "off")][string] $InsertMode,
+ [int] $Mode
+)
+
+$signature = @'
+[DllImport("kernel32.dll", SetLastError = true)]
+public static extern IntPtr GetStdHandle(int nStdHandle);
+
+[DllImport("kernel32.dll", SetLastError = true)]
+public static extern uint GetConsoleMode(
+ IntPtr hConsoleHandle,
+ out uint lpMode);
+
+[DllImport("kernel32.dll", SetLastError = true)]
+public static extern uint SetConsoleMode(
+ IntPtr hConsoleHandle,
+ uint dwMode);
+
+public const int STD_INPUT_HANDLE = -10;
+public const int ENABLE_INSERT_MODE = 0x0020;
+public const int ENABLE_QUICK_EDIT_MODE = 0x0040;
+public const int ENABLE_EXTENDED_FLAGS = 0x0080;
+'@
+
+$WinAPI = Add-Type -MemberDefinition $signature `
+ -Name WinAPI -Namespace ConinModeScript `
+ -PassThru
+
+function GetConIn {
+ $ret = $WinAPI::GetStdHandle($WinAPI::STD_INPUT_HANDLE)
+ if ($ret -eq -1) {
+ throw "error: cannot get stdin"
+ }
+ return $ret
+}
+
+function GetConsoleMode {
+ $conin = GetConIn
+ $mode = 0
+ $ret = $WinAPI::GetConsoleMode($conin, [ref]$mode)
+ if ($ret -eq 0) {
+ throw "GetConsoleMode failed (is stdin a console?)"
+ }
+ return $mode
+}
+
+function SetConsoleMode($mode) {
+ $conin = GetConIn
+ $ret = $WinAPI::SetConsoleMode($conin, $mode)
+ if ($ret -eq 0) {
+ throw "SetConsoleMode failed (is stdin a console?)"
+ }
+}
+
+$oldMode = GetConsoleMode
+$newMode = $oldMode
+$doingSomething = $false
+
+if ($PSBoundParameters.ContainsKey("Mode")) {
+ $newMode = $Mode
+ $doingSomething = $true
+}
+
+if ($QuickEdit + $InsertMode -ne "") {
+ if (!($newMode -band $WinAPI::ENABLE_EXTENDED_FLAGS)) {
+ # We can't enable an extended flag without overwriting the existing
+ # QuickEdit/InsertMode flags. AFAICT, there is no way to query their
+ # existing values, so at least we can choose sensible defaults.
+ $newMode = $newMode -bor $WinAPI::ENABLE_EXTENDED_FLAGS
+ $newMode = $newMode -bor $WinAPI::ENABLE_QUICK_EDIT_MODE
+ $newMode = $newMode -bor $WinAPI::ENABLE_INSERT_MODE
+ $doingSomething = $true
+ }
+}
+
+if ($QuickEdit -eq "on") {
+ $newMode = $newMode -bor $WinAPI::ENABLE_QUICK_EDIT_MODE
+ $doingSomething = $true
+} elseif ($QuickEdit -eq "off") {
+ $newMode = $newMode -band (-bnot $WinAPI::ENABLE_QUICK_EDIT_MODE)
+ $doingSomething = $true
+}
+
+if ($InsertMode -eq "on") {
+ $newMode = $newMode -bor $WinAPI::ENABLE_INSERT_MODE
+ $doingSomething = $true
+} elseif ($InsertMode -eq "off") {
+ $newMode = $newMode -band (-bnot $WinAPI::ENABLE_INSERT_MODE)
+ $doingSomething = $true
+}
+
+if ($doingSomething) {
+ echo "old mode: $oldMode"
+ SetConsoleMode $newMode
+ $newMode = GetConsoleMode
+ echo "new mode: $newMode"
+} else {
+ echo "mode: $oldMode"
+}
diff --git a/src/libs/3rdparty/winpty/misc/ConoutMode.cc b/src/libs/3rdparty/winpty/misc/ConoutMode.cc
new file mode 100644
index 0000000000..100e0c7bea
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ConoutMode.cc
@@ -0,0 +1,113 @@
+#include <windows.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+static HANDLE getConout() {
+ HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (conout == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "error: cannot get stdout\n");
+ exit(1);
+ }
+ return conout;
+}
+
+static DWORD getConsoleMode() {
+ DWORD mode = 0;
+ if (!GetConsoleMode(getConout(), &mode)) {
+ fprintf(stderr, "error: GetConsoleMode failed (is stdout a console?)\n");
+ exit(1);
+ }
+ return mode;
+}
+
+static void setConsoleMode(DWORD mode) {
+ if (!SetConsoleMode(getConout(), mode)) {
+ fprintf(stderr, "error: SetConsoleMode failed (is stdout a console?)\n");
+ exit(1);
+ }
+}
+
+static long parseInt(const std::string &s) {
+ errno = 0;
+ char *endptr = nullptr;
+ long result = strtol(s.c_str(), &endptr, 0);
+ if (errno != 0 || !endptr || *endptr != '\0') {
+ fprintf(stderr, "error: could not parse integral argument '%s'\n", s.c_str());
+ exit(1);
+ }
+ return result;
+}
+
+static void usage() {
+ printf("Usage: ConoutMode [verb] [options]\n");
+ printf("Verbs:\n");
+ printf(" [info] Dumps info about mode flags.\n");
+ printf(" get Prints the mode DWORD.\n");
+ printf(" set VALUE Sets the mode to VALUE, which can be decimal, hex, or octal.\n");
+ printf(" set VALUE MASK\n");
+ printf(" Same as `set VALUE`, but only alters the bits in MASK.\n");
+ exit(1);
+}
+
+struct {
+ const char *name;
+ DWORD value;
+} kOutputFlags[] = {
+ "ENABLE_PROCESSED_OUTPUT", ENABLE_PROCESSED_OUTPUT, // 0x0001
+ "ENABLE_WRAP_AT_EOL_OUTPUT", ENABLE_WRAP_AT_EOL_OUTPUT, // 0x0002
+ "ENABLE_VIRTUAL_TERMINAL_PROCESSING", 0x0004/*ENABLE_VIRTUAL_TERMINAL_PROCESSING*/, // 0x0004
+ "DISABLE_NEWLINE_AUTO_RETURN", 0x0008/*DISABLE_NEWLINE_AUTO_RETURN*/, // 0x0008
+ "ENABLE_LVB_GRID_WORLDWIDE", 0x0010/*ENABLE_LVB_GRID_WORLDWIDE*/, //0x0010
+};
+
+int main(int argc, char *argv[]) {
+ std::vector<std::string> args;
+ for (size_t i = 1; i < argc; ++i) {
+ args.push_back(argv[i]);
+ }
+
+ if (args.empty() || args.size() == 1 && args[0] == "info") {
+ DWORD mode = getConsoleMode();
+ printf("mode: 0x%lx\n", mode);
+ for (const auto &flag : kOutputFlags) {
+ printf("%-34s 0x%04lx %s\n", flag.name, flag.value, flag.value & mode ? "ON" : "off");
+ mode &= ~flag.value;
+ }
+ for (int i = 0; i < 32; ++i) {
+ if (mode & (1u << i)) {
+ printf("Unrecognized flag: %04x\n", (1u << i));
+ }
+ }
+ return 0;
+ }
+
+ const auto verb = args[0];
+
+ if (verb == "set") {
+ if (args.size() == 2) {
+ const DWORD newMode = parseInt(args[1]);
+ setConsoleMode(newMode);
+ } else if (args.size() == 3) {
+ const DWORD mode = parseInt(args[1]);
+ const DWORD mask = parseInt(args[2]);
+ const int newMode = (getConsoleMode() & ~mask) | (mode & mask);
+ setConsoleMode(newMode);
+ } else {
+ usage();
+ }
+ } else if (verb == "get") {
+ if (args.size() != 1) {
+ usage();
+ }
+ printf("0x%lx\n", getConsoleMode());
+ } else {
+ usage();
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/DebugClient.py b/src/libs/3rdparty/winpty/misc/DebugClient.py
new file mode 100644
index 0000000000..cd12df8924
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/DebugClient.py
@@ -0,0 +1,42 @@
+#!python
+# Run with native CPython. Needs pywin32 extensions.
+
+# Copyright (c) 2011-2012 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import winerror
+import win32pipe
+import win32file
+import win32api
+import sys
+import pywintypes
+import time
+
+if len(sys.argv) != 2:
+ print("Usage: %s message" % sys.argv[0])
+ sys.exit(1)
+
+message = "[%05.3f %s]: %s" % (time.time() % 100000, sys.argv[0], sys.argv[1])
+
+win32pipe.CallNamedPipe(
+ "\\\\.\\pipe\\DebugServer",
+ message.encode(),
+ 16,
+ win32pipe.NMPWAIT_WAIT_FOREVER)
diff --git a/src/libs/3rdparty/winpty/misc/DebugServer.py b/src/libs/3rdparty/winpty/misc/DebugServer.py
new file mode 100644
index 0000000000..3fc068bae7
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/DebugServer.py
@@ -0,0 +1,63 @@
+#!python
+#
+# Run with native CPython. Needs pywin32 extensions.
+
+# Copyright (c) 2011-2012 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+import win32pipe
+import win32api
+import win32file
+import time
+import threading
+import sys
+
+# A message may not be larger than this size.
+MSG_SIZE=4096
+
+serverPipe = win32pipe.CreateNamedPipe(
+ "\\\\.\\pipe\\DebugServer",
+ win32pipe.PIPE_ACCESS_DUPLEX,
+ win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE,
+ win32pipe.PIPE_UNLIMITED_INSTANCES,
+ MSG_SIZE,
+ MSG_SIZE,
+ 10 * 1000,
+ None)
+while True:
+ win32pipe.ConnectNamedPipe(serverPipe, None)
+ (ret, data) = win32file.ReadFile(serverPipe, MSG_SIZE)
+ print(data.decode())
+ sys.stdout.flush()
+
+ # The client uses CallNamedPipe to send its message. CallNamedPipe waits
+ # for a reply message. If I send a reply, however, using WriteFile, then
+ # sometimes WriteFile fails with:
+ # pywintypes.error: (232, 'WriteFile', 'The pipe is being closed.')
+ # I can't figure out how to write a strictly correct pipe server, but if
+ # I comment out the WriteFile line, then everything seems to work. I
+ # think the DisconnectNamedPipe call aborts the client's CallNamedPipe
+ # call normally.
+
+ try:
+ win32file.WriteFile(serverPipe, b'OK')
+ except:
+ pass
+ win32pipe.DisconnectNamedPipe(serverPipe)
diff --git a/src/libs/3rdparty/winpty/misc/DumpLines.py b/src/libs/3rdparty/winpty/misc/DumpLines.py
new file mode 100644
index 0000000000..40049961b5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/DumpLines.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+import sys
+
+for i in range(1, int(sys.argv[1]) + 1):
+ print i, "X" * 78
diff --git a/src/libs/3rdparty/winpty/misc/EnableExtendedFlags.txt b/src/libs/3rdparty/winpty/misc/EnableExtendedFlags.txt
new file mode 100644
index 0000000000..37914dac26
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/EnableExtendedFlags.txt
@@ -0,0 +1,46 @@
+Note regarding ENABLE_EXTENDED_FLAGS (2016-05-30)
+
+There is a complicated interaction between the ENABLE_EXTENDED_FLAGS flag
+and the ENABLE_QUICK_EDIT_MODE and ENABLE_INSERT_MODE flags (presumably for
+backwards compatibility?). I studied the behavior on Windows 7 and Windows
+10, with both the old and new consoles, and I didn't see any differences
+between versions. Here's what I seemed to observe:
+
+ - The console has three flags internally:
+ - QuickEdit
+ - InsertMode
+ - ExtendedFlags
+
+ - SetConsoleMode psuedocode:
+ void SetConsoleMode(..., DWORD mode) {
+ ExtendedFlags = (mode & (ENABLE_EXTENDED_FLAGS
+ | ENABLE_QUICK_EDIT_MODE
+ | ENABLE_INSERT_MODE )) != 0;
+ if (ExtendedFlags) {
+ QuickEdit = (mode & ENABLE_QUICK_EDIT_MODE) != 0;
+ InsertMode = (mode & ENABLE_INSERT_MODE) != 0;
+ }
+ }
+
+ - Setting QuickEdit or InsertMode from the properties dialog GUI does not
+ affect the ExtendedFlags setting -- it simply toggles the one flag.
+
+ - GetConsoleMode psuedocode:
+ GetConsoleMode(..., DWORD *result) {
+ if (ExtendedFlags) {
+ *result |= ENABLE_EXTENDED_FLAGS;
+ if (QuickEdit) { *result |= ENABLE_QUICK_EDIT_MODE; }
+ if (InsertMode) { *result |= ENABLE_INSERT_MODE; }
+ }
+ }
+
+Effectively, the ExtendedFlags flags controls whether the other two flags
+are visible/controlled by the user application. If they aren't visible,
+though, there is no way for the user application to make them visible,
+except by overwriting their values! Calling SetConsoleMode with just
+ENABLE_EXTENDED_FLAGS would clear the extended flags we want to read.
+
+Consequently, if a program temporarily alters the QuickEdit flag (e.g. to
+enable mouse input), it cannot restore the original values of the QuickEdit
+and InsertMode flags, UNLESS every other console program cooperates by
+keeping the ExtendedFlags flag set.
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Consolas.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Consolas.txt
new file mode 100644
index 0000000000..067bd3824a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Consolas.txt
@@ -0,0 +1,528 @@
+==================================
+Code Page 437, Consolas font
+==================================
+
+Options: -face "Consolas" -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+FontSurvey "-face \"Consolas\" -family 0x36"
+
+Windows 7
+---------
+
+Size 1: 1,3 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 1,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 2,5 BAD (HHHHHH)
+Size 6: 3,6 BAD (HHHHHH)
+Size 7: 3,6 BAD (HHHHHH)
+Size 8: 4,8 BAD (HHHHHH)
+Size 9: 4,9 BAD (HHHHHH)
+Size 10: 5,10 BAD (HHHHHH)
+Size 11: 5,11 BAD (HHHHHH)
+Size 12: 6,12 BAD (HHHHHH)
+Size 13: 6,13 BAD (HHHHHH)
+Size 14: 7,14 BAD (HHHHHH)
+Size 15: 7,15 BAD (HHHHHH)
+Size 16: 8,16 BAD (HHHHHH)
+Size 17: 8,17 BAD (HHHHHH)
+Size 18: 8,18 BAD (HHHHHH)
+Size 19: 9,19 BAD (HHHHHH)
+Size 20: 9,20 BAD (HHHHHH)
+Size 21: 10,22 BAD (HHHHHH)
+Size 22: 10,22 BAD (HHHHHH)
+Size 23: 11,23 BAD (HHHHHH)
+Size 24: 11,24 BAD (HHHHHH)
+Size 25: 12,25 BAD (HHHHHH)
+Size 26: 12,26 BAD (HHHHHH)
+Size 27: 13,27 BAD (HHHHHH)
+Size 28: 13,28 BAD (HHHHHH)
+Size 29: 14,29 BAD (HHHHHH)
+Size 30: 14,30 BAD (HHHHHH)
+Size 31: 15,31 BAD (HHHHHH)
+Size 32: 15,32 BAD (HHHHHH)
+Size 33: 15,33 BAD (HHHHHH)
+Size 34: 16,34 BAD (HHHHHH)
+Size 35: 16,36 BAD (HHHHHH)
+Size 36: 17,36 BAD (HHHHHH)
+Size 37: 17,37 BAD (HHHHHH)
+Size 38: 18,38 BAD (HHHHHH)
+Size 39: 18,39 BAD (HHHHHH)
+Size 40: 19,40 BAD (HHHHHH)
+Size 41: 19,41 BAD (HHHHHH)
+Size 42: 20,42 BAD (HHHHHH)
+Size 43: 20,43 BAD (HHHHHH)
+Size 44: 21,44 BAD (HHHHHH)
+Size 45: 21,45 BAD (HHHHHH)
+Size 46: 22,46 BAD (HHHHHH)
+Size 47: 22,47 BAD (HHHHHH)
+Size 48: 23,48 BAD (HHHHHH)
+Size 49: 23,49 BAD (HHHHHH)
+Size 50: 23,50 BAD (HHHHHH)
+Size 51: 24,51 BAD (HHHHHH)
+Size 52: 24,52 BAD (HHHHHH)
+Size 53: 25,53 BAD (HHHHHH)
+Size 54: 25,54 BAD (HHHHHH)
+Size 55: 26,55 BAD (HHHHHH)
+Size 56: 26,56 BAD (HHHHHH)
+Size 57: 27,57 BAD (HHHHHH)
+Size 58: 27,58 BAD (HHHHHH)
+Size 59: 28,59 BAD (HHHHHH)
+Size 60: 28,60 BAD (HHHHHH)
+Size 61: 29,61 BAD (HHHHHH)
+Size 62: 29,62 BAD (HHHHHH)
+Size 63: 30,64 BAD (HHHHHH)
+Size 64: 30,64 BAD (HHHHHH)
+Size 65: 31,65 BAD (HHHHHH)
+Size 66: 31,66 BAD (HHHHHH)
+Size 67: 31,67 BAD (HHHHHH)
+Size 68: 32,68 BAD (HHHHHH)
+Size 69: 32,69 BAD (HHHHHH)
+Size 70: 33,70 BAD (HHHHHH)
+Size 71: 33,71 BAD (HHHHHH)
+Size 72: 34,72 BAD (HHHHHH)
+Size 73: 34,73 BAD (HHHHHH)
+Size 74: 35,74 BAD (HHHHHH)
+Size 75: 35,75 BAD (HHHHHH)
+Size 76: 36,76 BAD (HHHHHH)
+Size 77: 36,77 BAD (HHHHHH)
+Size 78: 37,78 BAD (HHHHHH)
+Size 79: 37,79 BAD (HHHHHH)
+Size 80: 38,80 BAD (HHHHHH)
+Size 81: 38,81 BAD (HHHHHH)
+Size 82: 39,82 BAD (HHHHHH)
+Size 83: 39,83 BAD (HHHHHH)
+Size 84: 39,84 BAD (HHHHHH)
+Size 85: 40,85 BAD (HHHHHH)
+Size 86: 40,86 BAD (HHHHHH)
+Size 87: 41,87 BAD (HHHHHH)
+Size 88: 41,88 BAD (HHHHHH)
+Size 89: 42,89 BAD (HHHHHH)
+Size 90: 42,90 BAD (HHHHHH)
+Size 91: 43,91 BAD (HHHHHH)
+Size 92: 43,92 BAD (HHHHHH)
+Size 93: 44,93 BAD (HHHHHH)
+Size 94: 44,94 BAD (HHHHHH)
+Size 95: 45,95 BAD (HHHHHH)
+Size 96: 45,96 BAD (HHHHHH)
+Size 97: 46,97 BAD (HHHHHH)
+Size 98: 46,98 BAD (HHHHHH)
+Size 99: 46,99 BAD (HHHHHH)
+Size 100: 47,100 BAD (HHHHHH)
+
+Windows 8
+---------
+
+Size 1: 1,3 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 1,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 2,5 BAD (HHHHHH)
+Size 6: 3,6 BAD (HHHHHH)
+Size 7: 3,6 BAD (HHHHHH)
+Size 8: 4,8 BAD (HHHHHH)
+Size 9: 4,9 BAD (HHHHHH)
+Size 10: 5,10 BAD (HHHHHH)
+Size 11: 5,11 BAD (HHHHHH)
+Size 12: 6,12 BAD (HHHHHH)
+Size 13: 6,13 BAD (HHHHHH)
+Size 14: 7,14 BAD (HHHHHH)
+Size 15: 7,15 BAD (HHHHHH)
+Size 16: 8,16 BAD (HHHHHH)
+Size 17: 8,17 BAD (HHHHHH)
+Size 18: 8,18 BAD (HHHHHH)
+Size 19: 9,19 BAD (HHHHHH)
+Size 20: 9,20 BAD (HHHHHH)
+Size 21: 10,22 BAD (HHHHHH)
+Size 22: 10,22 BAD (HHHHHH)
+Size 23: 11,23 BAD (HHHHHH)
+Size 24: 11,24 BAD (HHHHHH)
+Size 25: 12,25 BAD (HHHHHH)
+Size 26: 12,26 BAD (HHHHHH)
+Size 27: 13,27 BAD (HHHHHH)
+Size 28: 13,28 BAD (HHHHHH)
+Size 29: 14,29 BAD (HHHHHH)
+Size 30: 14,30 BAD (HHHHHH)
+Size 31: 15,31 BAD (HHHHHH)
+Size 32: 15,32 BAD (HHHHHH)
+Size 33: 15,33 BAD (HHHHHH)
+Size 34: 16,34 BAD (HHHHHH)
+Size 35: 16,36 BAD (HHHHHH)
+Size 36: 17,36 BAD (HHHHHH)
+Size 37: 17,37 BAD (HHHHHH)
+Size 38: 18,38 BAD (HHHHHH)
+Size 39: 18,39 BAD (HHHHHH)
+Size 40: 19,40 BAD (HHHHHH)
+Size 41: 19,41 BAD (HHHHHH)
+Size 42: 20,42 BAD (HHHHHH)
+Size 43: 20,43 BAD (HHHHHH)
+Size 44: 21,44 BAD (HHHHHH)
+Size 45: 21,45 BAD (HHHHHH)
+Size 46: 22,46 BAD (HHHHHH)
+Size 47: 22,47 BAD (HHHHHH)
+Size 48: 23,48 BAD (HHHHHH)
+Size 49: 23,49 BAD (HHHHHH)
+Size 50: 23,50 BAD (HHHHHH)
+Size 51: 24,51 BAD (HHHHHH)
+Size 52: 24,52 BAD (HHHHHH)
+Size 53: 25,53 BAD (HHHHHH)
+Size 54: 25,54 BAD (HHHHHH)
+Size 55: 26,55 BAD (HHHHHH)
+Size 56: 26,56 BAD (HHHHHH)
+Size 57: 27,57 BAD (HHHHHH)
+Size 58: 27,58 BAD (HHHHHH)
+Size 59: 28,59 BAD (HHHHHH)
+Size 60: 28,60 BAD (HHHHHH)
+Size 61: 29,61 BAD (HHHHHH)
+Size 62: 29,62 BAD (HHHHHH)
+Size 63: 30,64 BAD (HHHHHH)
+Size 64: 30,64 BAD (HHHHHH)
+Size 65: 31,65 BAD (HHHHHH)
+Size 66: 31,66 BAD (HHHHHH)
+Size 67: 31,67 BAD (HHHHHH)
+Size 68: 32,68 BAD (HHHHHH)
+Size 69: 32,69 BAD (HHHHHH)
+Size 70: 33,70 BAD (HHHHHH)
+Size 71: 33,71 BAD (HHHHHH)
+Size 72: 34,72 BAD (HHHHHH)
+Size 73: 34,73 BAD (HHHHHH)
+Size 74: 35,74 BAD (HHHHHH)
+Size 75: 35,75 BAD (HHHHHH)
+Size 76: 36,76 BAD (HHHHHH)
+Size 77: 36,77 BAD (HHHHHH)
+Size 78: 37,78 BAD (HHHHHH)
+Size 79: 37,79 BAD (HHHHHH)
+Size 80: 38,80 BAD (HHHHHH)
+Size 81: 38,81 BAD (HHHHHH)
+Size 82: 39,82 BAD (HHHHHH)
+Size 83: 39,83 BAD (HHHHHH)
+Size 84: 39,84 BAD (HHHHHH)
+Size 85: 40,85 BAD (HHHHHH)
+Size 86: 40,86 BAD (HHHHHH)
+Size 87: 41,87 BAD (HHHHHH)
+Size 88: 41,88 BAD (HHHHHH)
+Size 89: 42,89 BAD (HHHHHH)
+Size 90: 42,90 BAD (HHHHHH)
+Size 91: 43,91 BAD (HHHHHH)
+Size 92: 43,92 BAD (HHHHHH)
+Size 93: 44,93 BAD (HHHHHH)
+Size 94: 44,94 BAD (HHHHHH)
+Size 95: 45,95 BAD (HHHHHH)
+Size 96: 45,96 BAD (HHHHHH)
+Size 97: 46,97 BAD (HHHHHH)
+Size 98: 46,98 BAD (HHHHHH)
+Size 99: 46,99 BAD (HHHHHH)
+Size 100: 47,100 BAD (HHHHHH)
+
+Windows 8.1
+-----------
+
+Size 1: 1,3 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 1,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 2,5 BAD (HHHHHH)
+Size 6: 3,6 BAD (HHHHHH)
+Size 7: 3,6 BAD (HHHHHH)
+Size 8: 4,8 BAD (HHHHHH)
+Size 9: 4,9 BAD (HHHHHH)
+Size 10: 5,10 BAD (HHHHHH)
+Size 11: 5,11 BAD (HHHHHH)
+Size 12: 6,12 BAD (HHHHHH)
+Size 13: 6,13 BAD (HHHHHH)
+Size 14: 7,14 BAD (HHHHHH)
+Size 15: 7,15 BAD (HHHHHH)
+Size 16: 8,16 BAD (HHHHHH)
+Size 17: 8,17 BAD (HHHHHH)
+Size 18: 8,18 BAD (HHHHHH)
+Size 19: 9,19 BAD (HHHHHH)
+Size 20: 9,20 BAD (HHHHHH)
+Size 21: 10,22 BAD (HHHHHH)
+Size 22: 10,22 BAD (HHHHHH)
+Size 23: 11,23 BAD (HHHHHH)
+Size 24: 11,24 BAD (HHHHHH)
+Size 25: 12,25 BAD (HHHHHH)
+Size 26: 12,26 BAD (HHHHHH)
+Size 27: 13,27 BAD (HHHHHH)
+Size 28: 13,28 BAD (HHHHHH)
+Size 29: 14,29 BAD (HHHHHH)
+Size 30: 14,30 BAD (HHHHHH)
+Size 31: 15,31 BAD (HHHHHH)
+Size 32: 15,32 BAD (HHHHHH)
+Size 33: 15,33 BAD (HHHHHH)
+Size 34: 16,34 BAD (HHHHHH)
+Size 35: 16,36 BAD (HHHHHH)
+Size 36: 17,36 BAD (HHHHHH)
+Size 37: 17,37 BAD (HHHHHH)
+Size 38: 18,38 BAD (HHHHHH)
+Size 39: 18,39 BAD (HHHHHH)
+Size 40: 19,40 BAD (HHHHHH)
+Size 41: 19,41 BAD (HHHHHH)
+Size 42: 20,42 BAD (HHHHHH)
+Size 43: 20,43 BAD (HHHHHH)
+Size 44: 21,44 BAD (HHHHHH)
+Size 45: 21,45 BAD (HHHHHH)
+Size 46: 22,46 BAD (HHHHHH)
+Size 47: 22,47 BAD (HHHHHH)
+Size 48: 23,48 BAD (HHHHHH)
+Size 49: 23,49 BAD (HHHHHH)
+Size 50: 23,50 BAD (HHHHHH)
+Size 51: 24,51 BAD (HHHHHH)
+Size 52: 24,52 BAD (HHHHHH)
+Size 53: 25,53 BAD (HHHHHH)
+Size 54: 25,54 BAD (HHHHHH)
+Size 55: 26,55 BAD (HHHHHH)
+Size 56: 26,56 BAD (HHHHHH)
+Size 57: 27,57 BAD (HHHHHH)
+Size 58: 27,58 BAD (HHHHHH)
+Size 59: 28,59 BAD (HHHHHH)
+Size 60: 28,60 BAD (HHHHHH)
+Size 61: 29,61 BAD (HHHHHH)
+Size 62: 29,62 BAD (HHHHHH)
+Size 63: 30,64 BAD (HHHHHH)
+Size 64: 30,64 BAD (HHHHHH)
+Size 65: 31,65 BAD (HHHHHH)
+Size 66: 31,66 BAD (HHHHHH)
+Size 67: 31,67 BAD (HHHHHH)
+Size 68: 32,68 BAD (HHHHHH)
+Size 69: 32,69 BAD (HHHHHH)
+Size 70: 33,70 BAD (HHHHHH)
+Size 71: 33,71 BAD (HHHHHH)
+Size 72: 34,72 BAD (HHHHHH)
+Size 73: 34,73 BAD (HHHHHH)
+Size 74: 35,74 BAD (HHHHHH)
+Size 75: 35,75 BAD (HHHHHH)
+Size 76: 36,76 BAD (HHHHHH)
+Size 77: 36,77 BAD (HHHHHH)
+Size 78: 37,78 BAD (HHHHHH)
+Size 79: 37,79 BAD (HHHHHH)
+Size 80: 38,80 BAD (HHHHHH)
+Size 81: 38,81 BAD (HHHHHH)
+Size 82: 39,82 BAD (HHHHHH)
+Size 83: 39,83 BAD (HHHHHH)
+Size 84: 39,84 BAD (HHHHHH)
+Size 85: 40,85 BAD (HHHHHH)
+Size 86: 40,86 BAD (HHHHHH)
+Size 87: 41,87 BAD (HHHHHH)
+Size 88: 41,88 BAD (HHHHHH)
+Size 89: 42,89 BAD (HHHHHH)
+Size 90: 42,90 BAD (HHHHHH)
+Size 91: 43,91 BAD (HHHHHH)
+Size 92: 43,92 BAD (HHHHHH)
+Size 93: 44,93 BAD (HHHHHH)
+Size 94: 44,94 BAD (HHHHHH)
+Size 95: 45,95 BAD (HHHHHH)
+Size 96: 45,96 BAD (HHHHHH)
+Size 97: 46,97 BAD (HHHHHH)
+Size 98: 46,98 BAD (HHHHHH)
+Size 99: 46,99 BAD (HHHHHH)
+Size 100: 47,100 BAD (HHHHHH)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,3 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 1,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 2,5 BAD (HHHHHH)
+Size 6: 3,6 BAD (HHHHHH)
+Size 7: 3,6 BAD (HHHHHH)
+Size 8: 4,8 BAD (HHHHHH)
+Size 9: 4,9 BAD (HHHHHH)
+Size 10: 5,10 BAD (HHHHHH)
+Size 11: 5,11 BAD (HHHHHH)
+Size 12: 6,12 BAD (HHHHHH)
+Size 13: 6,13 BAD (HHHHHH)
+Size 14: 7,14 BAD (HHHHHH)
+Size 15: 7,15 BAD (HHHHHH)
+Size 16: 8,16 BAD (HHHHHH)
+Size 17: 8,17 BAD (HHHHHH)
+Size 18: 8,18 BAD (HHHHHH)
+Size 19: 9,19 BAD (HHHHHH)
+Size 20: 9,20 BAD (HHHHHH)
+Size 21: 10,22 BAD (HHHHHH)
+Size 22: 10,22 BAD (HHHHHH)
+Size 23: 11,23 BAD (HHHHHH)
+Size 24: 11,24 BAD (HHHHHH)
+Size 25: 12,25 BAD (HHHHHH)
+Size 26: 12,26 BAD (HHHHHH)
+Size 27: 13,27 BAD (HHHHHH)
+Size 28: 13,28 BAD (HHHHHH)
+Size 29: 14,29 BAD (HHHHHH)
+Size 30: 14,30 BAD (HHHHHH)
+Size 31: 15,31 BAD (HHHHHH)
+Size 32: 15,32 BAD (HHHHHH)
+Size 33: 15,33 BAD (HHHHHH)
+Size 34: 16,34 BAD (HHHHHH)
+Size 35: 16,36 BAD (HHHHHH)
+Size 36: 17,36 BAD (HHHHHH)
+Size 37: 17,37 BAD (HHHHHH)
+Size 38: 18,38 BAD (HHHHHH)
+Size 39: 18,39 BAD (HHHHHH)
+Size 40: 19,40 BAD (HHHHHH)
+Size 41: 19,41 BAD (HHHHHH)
+Size 42: 20,42 BAD (HHHHHH)
+Size 43: 20,43 BAD (HHHHHH)
+Size 44: 21,44 BAD (HHHHHH)
+Size 45: 21,45 BAD (HHHHHH)
+Size 46: 22,46 BAD (HHHHHH)
+Size 47: 22,47 BAD (HHHHHH)
+Size 48: 23,48 BAD (HHHHHH)
+Size 49: 23,49 BAD (HHHHHH)
+Size 50: 23,50 BAD (HHHHHH)
+Size 51: 24,51 BAD (HHHHHH)
+Size 52: 24,52 BAD (HHHHHH)
+Size 53: 25,53 BAD (HHHHHH)
+Size 54: 25,54 BAD (HHHHHH)
+Size 55: 26,55 BAD (HHHHHH)
+Size 56: 26,56 BAD (HHHHHH)
+Size 57: 27,57 BAD (HHHHHH)
+Size 58: 27,58 BAD (HHHHHH)
+Size 59: 28,59 BAD (HHHHHH)
+Size 60: 28,60 BAD (HHHHHH)
+Size 61: 29,61 BAD (HHHHHH)
+Size 62: 29,62 BAD (HHHHHH)
+Size 63: 30,64 BAD (HHHHHH)
+Size 64: 30,64 BAD (HHHHHH)
+Size 65: 31,65 BAD (HHHHHH)
+Size 66: 31,66 BAD (HHHHHH)
+Size 67: 31,67 BAD (HHHHHH)
+Size 68: 32,68 BAD (HHHHHH)
+Size 69: 32,69 BAD (HHHHHH)
+Size 70: 33,70 BAD (HHHHHH)
+Size 71: 33,71 BAD (HHHHHH)
+Size 72: 34,72 BAD (HHHHHH)
+Size 73: 34,73 BAD (HHHHHH)
+Size 74: 35,74 BAD (HHHHHH)
+Size 75: 35,75 BAD (HHHHHH)
+Size 76: 36,76 BAD (HHHHHH)
+Size 77: 36,77 BAD (HHHHHH)
+Size 78: 37,78 BAD (HHHHHH)
+Size 79: 37,79 BAD (HHHHHH)
+Size 80: 38,80 BAD (HHHHHH)
+Size 81: 38,81 BAD (HHHHHH)
+Size 82: 39,82 BAD (HHHHHH)
+Size 83: 39,83 BAD (HHHHHH)
+Size 84: 39,84 BAD (HHHHHH)
+Size 85: 40,85 BAD (HHHHHH)
+Size 86: 40,86 BAD (HHHHHH)
+Size 87: 41,87 BAD (HHHHHH)
+Size 88: 41,88 BAD (HHHHHH)
+Size 89: 42,89 BAD (HHHHHH)
+Size 90: 42,90 BAD (HHHHHH)
+Size 91: 43,91 BAD (HHHHHH)
+Size 92: 43,92 BAD (HHHHHH)
+Size 93: 44,93 BAD (HHHHHH)
+Size 94: 44,94 BAD (HHHHHH)
+Size 95: 45,95 BAD (HHHHHH)
+Size 96: 45,96 BAD (HHHHHH)
+Size 97: 46,97 BAD (HHHHHH)
+Size 98: 46,98 BAD (HHHHHH)
+Size 99: 46,99 BAD (HHHHHH)
+Size 100: 47,100 BAD (HHHHHH)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 1,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 2,5 BAD (HHHHHH)
+Size 6: 3,6 BAD (HHHHHH)
+Size 7: 3,7 BAD (HHHHHH)
+Size 8: 4,8 BAD (HHHHHH)
+Size 9: 4,9 BAD (HHHHHH)
+Size 10: 5,10 BAD (HHHHHH)
+Size 11: 5,11 BAD (HHHHHH)
+Size 12: 6,12 BAD (HHHHHH)
+Size 13: 6,13 BAD (HHHHHH)
+Size 14: 7,14 BAD (HHHHHH)
+Size 15: 7,15 BAD (HHHHHH)
+Size 16: 8,16 BAD (HHHHHH)
+Size 17: 8,17 BAD (HHHHHH)
+Size 18: 8,18 BAD (HHHHHH)
+Size 19: 9,19 BAD (HHHHHH)
+Size 20: 9,20 BAD (HHHHHH)
+Size 21: 10,21 BAD (HHHHHH)
+Size 22: 10,22 BAD (HHHHHH)
+Size 23: 11,23 BAD (HHHHHH)
+Size 24: 11,24 BAD (HHHHHH)
+Size 25: 12,25 BAD (HHHHHH)
+Size 26: 12,26 BAD (HHHHHH)
+Size 27: 13,27 BAD (HHHHHH)
+Size 28: 13,28 BAD (HHHHHH)
+Size 29: 14,29 BAD (HHHHHH)
+Size 30: 14,30 BAD (HHHHHH)
+Size 31: 15,31 BAD (HHHHHH)
+Size 32: 15,32 BAD (HHHHHH)
+Size 33: 15,33 BAD (HHHHHH)
+Size 34: 16,34 BAD (HHHHHH)
+Size 35: 16,35 BAD (HHHHHH)
+Size 36: 17,36 BAD (HHHHHH)
+Size 37: 17,37 BAD (HHHHHH)
+Size 38: 18,38 BAD (HHHHHH)
+Size 39: 18,39 BAD (HHHHHH)
+Size 40: 19,40 BAD (HHHHHH)
+Size 41: 19,41 BAD (HHHHHH)
+Size 42: 20,42 BAD (HHHHHH)
+Size 43: 20,43 BAD (HHHHHH)
+Size 44: 21,44 BAD (HHHHHH)
+Size 45: 21,45 BAD (HHHHHH)
+Size 46: 22,46 BAD (HHHHHH)
+Size 47: 22,47 BAD (HHHHHH)
+Size 48: 23,48 BAD (HHHHHH)
+Size 49: 23,49 BAD (HHHHHH)
+Size 50: 23,50 BAD (HHHHHH)
+Size 51: 24,51 BAD (HHHHHH)
+Size 52: 24,52 BAD (HHHHHH)
+Size 53: 25,53 BAD (HHHHHH)
+Size 54: 25,54 BAD (HHHHHH)
+Size 55: 26,55 BAD (HHHHHH)
+Size 56: 26,56 BAD (HHHHHH)
+Size 57: 27,57 BAD (HHHHHH)
+Size 58: 27,58 BAD (HHHHHH)
+Size 59: 28,59 BAD (HHHHHH)
+Size 60: 28,60 BAD (HHHHHH)
+Size 61: 29,61 BAD (HHHHHH)
+Size 62: 29,62 BAD (HHHHHH)
+Size 63: 30,63 BAD (HHHHHH)
+Size 64: 30,64 BAD (HHHHHH)
+Size 65: 31,65 BAD (HHHHHH)
+Size 66: 31,66 BAD (HHHHHH)
+Size 67: 31,67 BAD (HHHHHH)
+Size 68: 32,68 BAD (HHHHHH)
+Size 69: 32,69 BAD (HHHHHH)
+Size 70: 33,70 BAD (HHHHHH)
+Size 71: 33,71 BAD (HHHHHH)
+Size 72: 34,72 BAD (HHHHHH)
+Size 73: 34,73 BAD (HHHHHH)
+Size 74: 35,74 BAD (HHHHHH)
+Size 75: 35,75 BAD (HHHHHH)
+Size 76: 36,76 BAD (HHHHHH)
+Size 77: 36,77 BAD (HHHHHH)
+Size 78: 37,78 BAD (HHHHHH)
+Size 79: 37,79 BAD (HHHHHH)
+Size 80: 38,80 BAD (HHHHHH)
+Size 81: 38,81 BAD (HHHHHH)
+Size 82: 39,82 BAD (HHHHHH)
+Size 83: 39,83 BAD (HHHHHH)
+Size 84: 39,84 BAD (HHHHHH)
+Size 85: 40,85 BAD (HHHHHH)
+Size 86: 40,86 BAD (HHHHHH)
+Size 87: 41,87 BAD (HHHHHH)
+Size 88: 41,88 BAD (HHHHHH)
+Size 89: 42,89 BAD (HHHHHH)
+Size 90: 42,90 BAD (HHHHHH)
+Size 91: 43,91 BAD (HHHHHH)
+Size 92: 43,92 BAD (HHHHHH)
+Size 93: 44,93 BAD (HHHHHH)
+Size 94: 44,94 BAD (HHHHHH)
+Size 95: 45,95 BAD (HHHHHH)
+Size 96: 45,96 BAD (HHHHHH)
+Size 97: 46,97 BAD (HHHHHH)
+Size 98: 46,98 BAD (HHHHHH)
+Size 99: 46,99 BAD (HHHHHH)
+Size 100: 47,100 BAD (HHHHHH)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Lucida.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Lucida.txt
new file mode 100644
index 0000000000..0eed93ad98
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP437-Lucida.txt
@@ -0,0 +1,633 @@
+==================================
+Code Page 437, Lucida Console font
+==================================
+
+Options: -face "Lucida Console" -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+FontSurvey "-face \"Lucida Console\" -family 0x36"
+
+Vista
+-----
+
+Size 1: 1,2 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,65 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
+
+
+Windows 7
+---------
+
+Size 1: 1,2 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,65 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
+
+Windows 8
+---------
+
+Size 1: 1,2 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,65 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
+
+Windows 8.1
+-----------
+
+Size 1: 1,2 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,65 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,2 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,65 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 BAD (HHHHHH)
+Size 2: 1,2 BAD (HHHHHH)
+Size 3: 2,3 BAD (HHHHHH)
+Size 4: 2,4 BAD (HHHHHH)
+Size 5: 3,5 BAD (HHHHHH)
+Size 6: 4,6 BAD (HHHHHH)
+Size 7: 4,7 BAD (HHHHHH)
+Size 8: 5,8 BAD (HHHHHH)
+Size 9: 5,9 BAD (HHHHHH)
+Size 10: 6,10 BAD (HHHHHH)
+Size 11: 7,11 BAD (HHHHHH)
+Size 12: 7,12 BAD (HHHHHH)
+Size 13: 8,13 BAD (HHHHHH)
+Size 14: 8,14 BAD (HHHHHH)
+Size 15: 9,15 BAD (HHHHHH)
+Size 16: 10,16 BAD (HHHHHH)
+Size 17: 10,17 BAD (HHHHHH)
+Size 18: 11,18 BAD (HHHHHH)
+Size 19: 11,19 BAD (HHHHHH)
+Size 20: 12,20 BAD (HHHHHH)
+Size 21: 13,21 BAD (HHHHHH)
+Size 22: 13,22 BAD (HHHHHH)
+Size 23: 14,23 BAD (HHHHHH)
+Size 24: 14,24 BAD (HHHHHH)
+Size 25: 15,25 BAD (HHHHHH)
+Size 26: 16,26 BAD (HHHHHH)
+Size 27: 16,27 BAD (HHHHHH)
+Size 28: 17,28 BAD (HHHHHH)
+Size 29: 17,29 BAD (HHHHHH)
+Size 30: 18,30 BAD (HHHHHH)
+Size 31: 19,31 BAD (HHHHHH)
+Size 32: 19,32 BAD (HHHHHH)
+Size 33: 20,33 BAD (HHHHHH)
+Size 34: 20,34 BAD (HHHHHH)
+Size 35: 21,35 BAD (HHHHHH)
+Size 36: 22,36 BAD (HHHHHH)
+Size 37: 22,37 BAD (HHHHHH)
+Size 38: 23,38 BAD (HHHHHH)
+Size 39: 23,39 BAD (HHHHHH)
+Size 40: 24,40 BAD (HHHHHH)
+Size 41: 25,41 BAD (HHHHHH)
+Size 42: 25,42 BAD (HHHHHH)
+Size 43: 26,43 BAD (HHHHHH)
+Size 44: 27,44 BAD (HHHHHH)
+Size 45: 27,45 BAD (HHHHHH)
+Size 46: 28,46 BAD (HHHHHH)
+Size 47: 28,47 BAD (HHHHHH)
+Size 48: 29,48 BAD (HHHHHH)
+Size 49: 30,49 BAD (HHHHHH)
+Size 50: 30,50 BAD (HHHHHH)
+Size 51: 31,51 BAD (HHHHHH)
+Size 52: 31,52 BAD (HHHHHH)
+Size 53: 32,53 BAD (HHHHHH)
+Size 54: 33,54 BAD (HHHHHH)
+Size 55: 33,55 BAD (HHHHHH)
+Size 56: 34,56 BAD (HHHHHH)
+Size 57: 34,57 BAD (HHHHHH)
+Size 58: 35,58 BAD (HHHHHH)
+Size 59: 36,59 BAD (HHHHHH)
+Size 60: 36,60 BAD (HHHHHH)
+Size 61: 37,61 BAD (HHHHHH)
+Size 62: 37,62 BAD (HHHHHH)
+Size 63: 38,63 BAD (HHHHHH)
+Size 64: 39,64 BAD (HHHHHH)
+Size 65: 39,65 BAD (HHHHHH)
+Size 66: 40,66 BAD (HHHHHH)
+Size 67: 40,67 BAD (HHHHHH)
+Size 68: 41,68 BAD (HHHHHH)
+Size 69: 42,69 BAD (HHHHHH)
+Size 70: 42,70 BAD (HHHHHH)
+Size 71: 43,71 BAD (HHHHHH)
+Size 72: 43,72 BAD (HHHHHH)
+Size 73: 44,73 BAD (HHHHHH)
+Size 74: 45,74 BAD (HHHHHH)
+Size 75: 45,75 BAD (HHHHHH)
+Size 76: 46,76 BAD (HHHHHH)
+Size 77: 46,77 BAD (HHHHHH)
+Size 78: 47,78 BAD (HHHHHH)
+Size 79: 48,79 BAD (HHHHHH)
+Size 80: 48,80 BAD (HHHHHH)
+Size 81: 49,81 BAD (HHHHHH)
+Size 82: 49,82 BAD (HHHHHH)
+Size 83: 50,83 BAD (HHHHHH)
+Size 84: 51,84 BAD (HHHHHH)
+Size 85: 51,85 BAD (HHHHHH)
+Size 86: 52,86 BAD (HHHHHH)
+Size 87: 52,87 BAD (HHHHHH)
+Size 88: 53,88 BAD (HHHHHH)
+Size 89: 54,89 BAD (HHHHHH)
+Size 90: 54,90 BAD (HHHHHH)
+Size 91: 55,91 BAD (HHHHHH)
+Size 92: 55,92 BAD (HHHHHH)
+Size 93: 56,93 BAD (HHHHHH)
+Size 94: 57,94 BAD (HHHHHH)
+Size 95: 57,95 BAD (HHHHHH)
+Size 96: 58,96 BAD (HHHHHH)
+Size 97: 58,97 BAD (HHHHHH)
+Size 98: 59,98 BAD (HHHHHH)
+Size 99: 60,99 BAD (HHHHHH)
+Size 100: 60,100 BAD (HHHHHH)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP932.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP932.txt
new file mode 100644
index 0000000000..ed3637eac1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP932.txt
@@ -0,0 +1,630 @@
+=======================================
+Code Page 932, Japanese, MS Gothic font
+=======================================
+
+Options: -face-gothic -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+Vista
+-----
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,4 OK (HHHFFF)
+Size 5: 3,5 OK (HHHFFF)
+Size 6: 3,6 OK (HHHFFF)
+Size 7: 4,7 OK (HHHFFF)
+Size 8: 4,8 OK (HHHFFF)
+Size 9: 5,9 OK (HHHFFF)
+Size 10: 5,10 OK (HHHFFF)
+Size 11: 6,11 OK (HHHFFF)
+Size 12: 6,12 OK (HHHFFF)
+Size 13: 7,13 OK (HHHFFF)
+Size 14: 7,14 BAD (HHHFHH)
+Size 15: 8,15 OK (HHHFFF)
+Size 16: 8,16 BAD (HHHFHH)
+Size 17: 9,17 OK (HHHFFF)
+Size 18: 9,18 BAD (HHHFHH)
+Size 19: 10,19 OK (HHHFFF)
+Size 20: 10,20 BAD (HHHFHH)
+Size 21: 11,21 OK (HHHFFF)
+Size 22: 11,22 BAD (HHHFHH)
+Size 23: 12,23 BAD (HHHFHH)
+Size 24: 12,24 BAD (HHHFHH)
+Size 25: 13,25 BAD (HHHFHH)
+Size 26: 13,26 BAD (HHHFHH)
+Size 27: 14,27 BAD (HHHFHH)
+Size 28: 14,28 BAD (HHHFHH)
+Size 29: 15,29 BAD (HHHFHH)
+Size 30: 15,30 BAD (HHHFHH)
+Size 31: 16,31 BAD (HHHFHH)
+Size 32: 16,33 BAD (HHHFHH)
+Size 33: 17,33 BAD (HHHFHH)
+Size 34: 17,34 BAD (HHHFHH)
+Size 35: 18,35 BAD (HHHFHH)
+Size 36: 18,36 BAD (HHHFHH)
+Size 37: 19,37 BAD (HHHFHH)
+Size 38: 19,38 BAD (HHHFHH)
+Size 39: 20,39 BAD (HHHFHH)
+Size 40: 20,40 BAD (HHHFHH)
+Size 41: 21,41 BAD (HHHFHH)
+Size 42: 21,42 BAD (HHHFHH)
+Size 43: 22,43 BAD (HHHFHH)
+Size 44: 22,44 BAD (HHHFHH)
+Size 45: 23,45 BAD (HHHFHH)
+Size 46: 23,46 BAD (HHHFHH)
+Size 47: 24,47 BAD (HHHFHH)
+Size 48: 24,48 BAD (HHHFHH)
+Size 49: 25,49 BAD (HHHFHH)
+Size 50: 25,50 BAD (HHHFHH)
+Size 51: 26,51 BAD (HHHFHH)
+Size 52: 26,52 BAD (HHHFHH)
+Size 53: 27,53 BAD (HHHFHH)
+Size 54: 27,54 BAD (HHHFHH)
+Size 55: 28,55 BAD (HHHFHH)
+Size 56: 28,56 BAD (HHHFHH)
+Size 57: 29,57 BAD (HHHFHH)
+Size 58: 29,58 BAD (HHHFHH)
+Size 59: 30,59 BAD (HHHFHH)
+Size 60: 30,60 BAD (HHHFHH)
+Size 61: 31,61 BAD (HHHFHH)
+Size 62: 31,62 BAD (HHHFHH)
+Size 63: 32,63 BAD (HHHFHH)
+Size 64: 32,64 BAD (HHHFHH)
+Size 65: 33,65 BAD (HHHFHH)
+Size 66: 33,66 BAD (HHHFHH)
+Size 67: 34,67 BAD (HHHFHH)
+Size 68: 34,68 BAD (HHHFHH)
+Size 69: 35,69 BAD (HHHFHH)
+Size 70: 35,70 BAD (HHHFHH)
+Size 71: 36,71 BAD (HHHFHH)
+Size 72: 36,72 BAD (HHHFHH)
+Size 73: 37,73 BAD (HHHFHH)
+Size 74: 37,74 BAD (HHHFHH)
+Size 75: 38,75 BAD (HHHFHH)
+Size 76: 38,76 BAD (HHHFHH)
+Size 77: 39,77 BAD (HHHFHH)
+Size 78: 39,78 BAD (HHHFHH)
+Size 79: 40,79 BAD (HHHFHH)
+Size 80: 40,80 BAD (HHHFHH)
+Size 81: 41,81 BAD (HHHFHH)
+Size 82: 41,82 BAD (HHHFHH)
+Size 83: 42,83 BAD (HHHFHH)
+Size 84: 42,84 BAD (HHHFHH)
+Size 85: 43,85 BAD (HHHFHH)
+Size 86: 43,86 BAD (HHHFHH)
+Size 87: 44,87 BAD (HHHFHH)
+Size 88: 44,88 BAD (HHHFHH)
+Size 89: 45,89 BAD (HHHFHH)
+Size 90: 45,90 BAD (HHHFHH)
+Size 91: 46,91 BAD (HHHFHH)
+Size 92: 46,92 BAD (HHHFHH)
+Size 93: 47,93 BAD (HHHFHH)
+Size 94: 47,94 BAD (HHHFHH)
+Size 95: 48,95 BAD (HHHFHH)
+Size 96: 48,97 BAD (HHHFHH)
+Size 97: 49,97 BAD (HHHFHH)
+Size 98: 49,98 BAD (HHHFHH)
+Size 99: 50,99 BAD (HHHFHH)
+Size 100: 50,100 BAD (HHHFHH)
+
+Windows 7
+---------
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,4 OK (HHHFFF)
+Size 5: 3,5 OK (HHHFFF)
+Size 6: 3,6 OK (HHHFFF)
+Size 7: 4,7 OK (HHHFFF)
+Size 8: 4,8 OK (HHHFFF)
+Size 9: 5,9 OK (HHHFFF)
+Size 10: 5,10 OK (HHHFFF)
+Size 11: 6,11 OK (HHHFFF)
+Size 12: 6,12 OK (HHHFFF)
+Size 13: 7,13 OK (HHHFFF)
+Size 14: 7,14 BAD (FFFFFF)
+Size 15: 8,15 OK (HHHFFF)
+Size 16: 8,16 BAD (FFFFFF)
+Size 17: 9,17 OK (HHHFFF)
+Size 18: 9,18 BAD (FFFFFF)
+Size 19: 10,19 OK (HHHFFF)
+Size 20: 10,20 BAD (FFFFFF)
+Size 21: 11,21 OK (HHHFFF)
+Size 22: 11,22 BAD (FFFFFF)
+Size 23: 12,23 BAD (FFFFFF)
+Size 24: 12,24 BAD (FFFFFF)
+Size 25: 13,25 BAD (FFFFFF)
+Size 26: 13,26 BAD (FFFFFF)
+Size 27: 14,27 BAD (FFFFFF)
+Size 28: 14,28 BAD (FFFFFF)
+Size 29: 15,29 BAD (FFFFFF)
+Size 30: 15,30 BAD (FFFFFF)
+Size 31: 16,31 BAD (FFFFFF)
+Size 32: 16,33 BAD (FFFFFF)
+Size 33: 17,33 BAD (FFFFFF)
+Size 34: 17,34 BAD (FFFFFF)
+Size 35: 18,35 BAD (FFFFFF)
+Size 36: 18,36 BAD (FFFFFF)
+Size 37: 19,37 BAD (FFFFFF)
+Size 38: 19,38 BAD (FFFFFF)
+Size 39: 20,39 BAD (FFFFFF)
+Size 40: 20,40 BAD (FFFFFF)
+Size 41: 21,41 BAD (FFFFFF)
+Size 42: 21,42 BAD (FFFFFF)
+Size 43: 22,43 BAD (FFFFFF)
+Size 44: 22,44 BAD (FFFFFF)
+Size 45: 23,45 BAD (FFFFFF)
+Size 46: 23,46 BAD (FFFFFF)
+Size 47: 24,47 BAD (FFFFFF)
+Size 48: 24,48 BAD (FFFFFF)
+Size 49: 25,49 BAD (FFFFFF)
+Size 50: 25,50 BAD (FFFFFF)
+Size 51: 26,51 BAD (FFFFFF)
+Size 52: 26,52 BAD (FFFFFF)
+Size 53: 27,53 BAD (FFFFFF)
+Size 54: 27,54 BAD (FFFFFF)
+Size 55: 28,55 BAD (FFFFFF)
+Size 56: 28,56 BAD (FFFFFF)
+Size 57: 29,57 BAD (FFFFFF)
+Size 58: 29,58 BAD (FFFFFF)
+Size 59: 30,59 BAD (FFFFFF)
+Size 60: 30,60 BAD (FFFFFF)
+Size 61: 31,61 BAD (FFFFFF)
+Size 62: 31,62 BAD (FFFFFF)
+Size 63: 32,63 BAD (FFFFFF)
+Size 64: 32,64 BAD (FFFFFF)
+Size 65: 33,65 BAD (FFFFFF)
+Size 66: 33,66 BAD (FFFFFF)
+Size 67: 34,67 BAD (FFFFFF)
+Size 68: 34,68 BAD (FFFFFF)
+Size 69: 35,69 BAD (FFFFFF)
+Size 70: 35,70 BAD (FFFFFF)
+Size 71: 36,71 BAD (FFFFFF)
+Size 72: 36,72 BAD (FFFFFF)
+Size 73: 37,73 BAD (FFFFFF)
+Size 74: 37,74 BAD (FFFFFF)
+Size 75: 38,75 BAD (FFFFFF)
+Size 76: 38,76 BAD (FFFFFF)
+Size 77: 39,77 BAD (FFFFFF)
+Size 78: 39,78 BAD (FFFFFF)
+Size 79: 40,79 BAD (FFFFFF)
+Size 80: 40,80 BAD (FFFFFF)
+Size 81: 41,81 BAD (FFFFFF)
+Size 82: 41,82 BAD (FFFFFF)
+Size 83: 42,83 BAD (FFFFFF)
+Size 84: 42,84 BAD (FFFFFF)
+Size 85: 43,85 BAD (FFFFFF)
+Size 86: 43,86 BAD (FFFFFF)
+Size 87: 44,87 BAD (FFFFFF)
+Size 88: 44,88 BAD (FFFFFF)
+Size 89: 45,89 BAD (FFFFFF)
+Size 90: 45,90 BAD (FFFFFF)
+Size 91: 46,91 BAD (FFFFFF)
+Size 92: 46,92 BAD (FFFFFF)
+Size 93: 47,93 BAD (FFFFFF)
+Size 94: 47,94 BAD (FFFFFF)
+Size 95: 48,95 BAD (FFFFFF)
+Size 96: 48,97 BAD (FFFFFF)
+Size 97: 49,97 BAD (FFFFFF)
+Size 98: 49,98 BAD (FFFFFF)
+Size 99: 50,99 BAD (FFFFFF)
+Size 100: 50,100 BAD (FFFFFF)
+
+Windows 8
+---------
+
+Size 1: 1,2 BAD (FFFFHH)
+Size 2: 1,2 BAD (FFFFHH)
+Size 3: 2,3 BAD (FFFFFF)
+Size 4: 2,4 BAD (FFFFHH)
+Size 5: 3,5 BAD (FFFFFF)
+Size 6: 3,6 BAD (FFFFHH)
+Size 7: 4,7 BAD (FFFFFF)
+Size 8: 4,8 BAD (FFFFHH)
+Size 9: 5,9 BAD (FFFFFF)
+Size 10: 5,10 BAD (FFFFHH)
+Size 11: 6,11 BAD (FFFFFF)
+Size 12: 6,12 BAD (FFFFHH)
+Size 13: 7,13 BAD (FFFFFF)
+Size 14: 7,14 BAD (FFFFHH)
+Size 15: 8,15 BAD (FFFFFF)
+Size 16: 8,16 BAD (FFFFHH)
+Size 17: 9,17 BAD (FFFFFF)
+Size 18: 9,18 BAD (FFFFHH)
+Size 19: 10,19 BAD (FFFFFF)
+Size 20: 10,20 BAD (FFFFFF)
+Size 21: 11,21 BAD (FFFFFF)
+Size 22: 11,22 BAD (FFFFFF)
+Size 23: 12,23 BAD (FFFFFF)
+Size 24: 12,24 BAD (FFFFFF)
+Size 25: 13,25 BAD (FFFFFF)
+Size 26: 13,26 BAD (FFFFFF)
+Size 27: 14,27 BAD (FFFFFF)
+Size 28: 14,28 BAD (FFFFFF)
+Size 29: 15,29 BAD (FFFFFF)
+Size 30: 15,30 BAD (FFFFFF)
+Size 31: 16,31 BAD (FFFFFF)
+Size 32: 16,33 BAD (FFFFFF)
+Size 33: 17,33 BAD (FFFFFF)
+Size 34: 17,34 BAD (FFFFFF)
+Size 35: 18,35 BAD (FFFFFF)
+Size 36: 18,36 BAD (FFFFFF)
+Size 37: 19,37 BAD (FFFFFF)
+Size 38: 19,38 BAD (FFFFFF)
+Size 39: 20,39 BAD (FFFFFF)
+Size 40: 20,40 BAD (FFFFFF)
+Size 41: 21,41 BAD (FFFFFF)
+Size 42: 21,42 BAD (FFFFFF)
+Size 43: 22,43 BAD (FFFFFF)
+Size 44: 22,44 BAD (FFFFFF)
+Size 45: 23,45 BAD (FFFFFF)
+Size 46: 23,46 BAD (FFFFFF)
+Size 47: 24,47 BAD (FFFFFF)
+Size 48: 24,48 BAD (FFFFFF)
+Size 49: 25,49 BAD (FFFFFF)
+Size 50: 25,50 BAD (FFFFFF)
+Size 51: 26,51 BAD (FFFFFF)
+Size 52: 26,52 BAD (FFFFFF)
+Size 53: 27,53 BAD (FFFFFF)
+Size 54: 27,54 BAD (FFFFFF)
+Size 55: 28,55 BAD (FFFFFF)
+Size 56: 28,56 BAD (FFFFFF)
+Size 57: 29,57 BAD (FFFFFF)
+Size 58: 29,58 BAD (FFFFFF)
+Size 59: 30,59 BAD (FFFFFF)
+Size 60: 30,60 BAD (FFFFFF)
+Size 61: 31,61 BAD (FFFFFF)
+Size 62: 31,62 BAD (FFFFFF)
+Size 63: 32,63 BAD (FFFFFF)
+Size 64: 32,64 BAD (FFFFFF)
+Size 65: 33,65 BAD (FFFFFF)
+Size 66: 33,66 BAD (FFFFFF)
+Size 67: 34,67 BAD (FFFFFF)
+Size 68: 34,68 BAD (FFFFFF)
+Size 69: 35,69 BAD (FFFFFF)
+Size 70: 35,70 BAD (FFFFFF)
+Size 71: 36,71 BAD (FFFFFF)
+Size 72: 36,72 BAD (FFFFFF)
+Size 73: 37,73 BAD (FFFFFF)
+Size 74: 37,74 BAD (FFFFFF)
+Size 75: 38,75 BAD (FFFFFF)
+Size 76: 38,76 BAD (FFFFFF)
+Size 77: 39,77 BAD (FFFFFF)
+Size 78: 39,78 BAD (FFFFFF)
+Size 79: 40,79 BAD (FFFFFF)
+Size 80: 40,80 BAD (FFFFFF)
+Size 81: 41,81 BAD (FFFFFF)
+Size 82: 41,82 BAD (FFFFFF)
+Size 83: 42,83 BAD (FFFFFF)
+Size 84: 42,84 BAD (FFFFFF)
+Size 85: 43,85 BAD (FFFFFF)
+Size 86: 43,86 BAD (FFFFFF)
+Size 87: 44,87 BAD (FFFFFF)
+Size 88: 44,88 BAD (FFFFFF)
+Size 89: 45,89 BAD (FFFFFF)
+Size 90: 45,90 BAD (FFFFFF)
+Size 91: 46,91 BAD (FFFFFF)
+Size 92: 46,92 BAD (FFFFFF)
+Size 93: 47,93 BAD (FFFFFF)
+Size 94: 47,94 BAD (FFFFFF)
+Size 95: 48,95 BAD (FFFFFF)
+Size 96: 48,97 BAD (FFFFFF)
+Size 97: 49,97 BAD (FFFFFF)
+Size 98: 49,98 BAD (FFFFFF)
+Size 99: 50,99 BAD (FFFFFF)
+Size 100: 50,100 BAD (FFFFFF)
+
+Windows 8.1
+-----------
+
+Size 1: 1,2 BAD (FFFFHH)
+Size 2: 1,2 BAD (FFFFHH)
+Size 3: 2,3 BAD (FFFFFF)
+Size 4: 2,4 BAD (FFFFHH)
+Size 5: 3,5 BAD (FFFFFF)
+Size 6: 3,6 BAD (FFFFHH)
+Size 7: 4,7 BAD (FFFFFF)
+Size 8: 4,8 BAD (FFFFHH)
+Size 9: 5,9 BAD (FFFFFF)
+Size 10: 5,10 BAD (FFFFHH)
+Size 11: 6,11 BAD (FFFFFF)
+Size 12: 6,12 BAD (FFFFHH)
+Size 13: 7,13 BAD (FFFFFF)
+Size 14: 7,14 BAD (FFFFHH)
+Size 15: 8,15 BAD (FFFFFF)
+Size 16: 8,16 BAD (FFFFHH)
+Size 17: 9,17 BAD (FFFFFF)
+Size 18: 9,18 BAD (FFFFHH)
+Size 19: 10,19 BAD (FFFFFF)
+Size 20: 10,20 BAD (FFFFFF)
+Size 21: 11,21 BAD (FFFFFF)
+Size 22: 11,22 BAD (FFFFFF)
+Size 23: 12,23 BAD (FFFFFF)
+Size 24: 12,24 BAD (FFFFFF)
+Size 25: 13,25 BAD (FFFFFF)
+Size 26: 13,26 BAD (FFFFFF)
+Size 27: 14,27 BAD (FFFFFF)
+Size 28: 14,28 BAD (FFFFFF)
+Size 29: 15,29 BAD (FFFFFF)
+Size 30: 15,30 BAD (FFFFFF)
+Size 31: 16,31 BAD (FFFFFF)
+Size 32: 16,33 BAD (FFFFFF)
+Size 33: 17,33 BAD (FFFFFF)
+Size 34: 17,34 BAD (FFFFFF)
+Size 35: 18,35 BAD (FFFFFF)
+Size 36: 18,36 BAD (FFFFFF)
+Size 37: 19,37 BAD (FFFFFF)
+Size 38: 19,38 BAD (FFFFFF)
+Size 39: 20,39 BAD (FFFFFF)
+Size 40: 20,40 BAD (FFFFFF)
+Size 41: 21,41 BAD (FFFFFF)
+Size 42: 21,42 BAD (FFFFFF)
+Size 43: 22,43 BAD (FFFFFF)
+Size 44: 22,44 BAD (FFFFFF)
+Size 45: 23,45 BAD (FFFFFF)
+Size 46: 23,46 BAD (FFFFFF)
+Size 47: 24,47 BAD (FFFFFF)
+Size 48: 24,48 BAD (FFFFFF)
+Size 49: 25,49 BAD (FFFFFF)
+Size 50: 25,50 BAD (FFFFFF)
+Size 51: 26,51 BAD (FFFFFF)
+Size 52: 26,52 BAD (FFFFFF)
+Size 53: 27,53 BAD (FFFFFF)
+Size 54: 27,54 BAD (FFFFFF)
+Size 55: 28,55 BAD (FFFFFF)
+Size 56: 28,56 BAD (FFFFFF)
+Size 57: 29,57 BAD (FFFFFF)
+Size 58: 29,58 BAD (FFFFFF)
+Size 59: 30,59 BAD (FFFFFF)
+Size 60: 30,60 BAD (FFFFFF)
+Size 61: 31,61 BAD (FFFFFF)
+Size 62: 31,62 BAD (FFFFFF)
+Size 63: 32,63 BAD (FFFFFF)
+Size 64: 32,64 BAD (FFFFFF)
+Size 65: 33,65 BAD (FFFFFF)
+Size 66: 33,66 BAD (FFFFFF)
+Size 67: 34,67 BAD (FFFFFF)
+Size 68: 34,68 BAD (FFFFFF)
+Size 69: 35,69 BAD (FFFFFF)
+Size 70: 35,70 BAD (FFFFFF)
+Size 71: 36,71 BAD (FFFFFF)
+Size 72: 36,72 BAD (FFFFFF)
+Size 73: 37,73 BAD (FFFFFF)
+Size 74: 37,74 BAD (FFFFFF)
+Size 75: 38,75 BAD (FFFFFF)
+Size 76: 38,76 BAD (FFFFFF)
+Size 77: 39,77 BAD (FFFFFF)
+Size 78: 39,78 BAD (FFFFFF)
+Size 79: 40,79 BAD (FFFFFF)
+Size 80: 40,80 BAD (FFFFFF)
+Size 81: 41,81 BAD (FFFFFF)
+Size 82: 41,82 BAD (FFFFFF)
+Size 83: 42,83 BAD (FFFFFF)
+Size 84: 42,84 BAD (FFFFFF)
+Size 85: 43,85 BAD (FFFFFF)
+Size 86: 43,86 BAD (FFFFFF)
+Size 87: 44,87 BAD (FFFFFF)
+Size 88: 44,88 BAD (FFFFFF)
+Size 89: 45,89 BAD (FFFFFF)
+Size 90: 45,90 BAD (FFFFFF)
+Size 91: 46,91 BAD (FFFFFF)
+Size 92: 46,92 BAD (FFFFFF)
+Size 93: 47,93 BAD (FFFFFF)
+Size 94: 47,94 BAD (FFFFFF)
+Size 95: 48,95 BAD (FFFFFF)
+Size 96: 48,97 BAD (FFFFFF)
+Size 97: 49,97 BAD (FFFFFF)
+Size 98: 49,98 BAD (FFFFFF)
+Size 99: 50,99 BAD (FFFFFF)
+Size 100: 50,100 BAD (FFFFFF)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,2 BAD (FFFFHH)
+Size 2: 1,2 BAD (FFFFHH)
+Size 3: 2,3 BAD (FFFFFF)
+Size 4: 2,4 BAD (FFFFHH)
+Size 5: 3,5 BAD (FFFFFF)
+Size 6: 3,6 BAD (FFFFHH)
+Size 7: 4,7 BAD (FFFFFF)
+Size 8: 4,8 BAD (FFFFHH)
+Size 9: 5,9 BAD (FFFFFF)
+Size 10: 5,10 BAD (FFFFHH)
+Size 11: 6,11 BAD (FFFFFF)
+Size 12: 6,12 BAD (FFFFHH)
+Size 13: 7,13 BAD (FFFFFF)
+Size 14: 7,14 BAD (FFFFHH)
+Size 15: 8,15 BAD (FFFFFF)
+Size 16: 8,16 BAD (FFFFHH)
+Size 17: 9,17 BAD (FFFFFF)
+Size 18: 9,18 BAD (FFFFHH)
+Size 19: 10,19 BAD (FFFFFF)
+Size 20: 10,20 BAD (FFFFFF)
+Size 21: 11,21 BAD (FFFFFF)
+Size 22: 11,22 BAD (FFFFFF)
+Size 23: 12,23 BAD (FFFFFF)
+Size 24: 12,24 BAD (FFFFFF)
+Size 25: 13,25 BAD (FFFFFF)
+Size 26: 13,26 BAD (FFFFFF)
+Size 27: 14,27 BAD (FFFFFF)
+Size 28: 14,28 BAD (FFFFFF)
+Size 29: 15,29 BAD (FFFFFF)
+Size 30: 15,30 BAD (FFFFFF)
+Size 31: 16,31 BAD (FFFFFF)
+Size 32: 16,33 BAD (FFFFFF)
+Size 33: 17,33 BAD (FFFFFF)
+Size 34: 17,34 BAD (FFFFFF)
+Size 35: 18,35 BAD (FFFFFF)
+Size 36: 18,36 BAD (FFFFFF)
+Size 37: 19,37 BAD (FFFFFF)
+Size 38: 19,38 BAD (FFFFFF)
+Size 39: 20,39 BAD (FFFFFF)
+Size 40: 20,40 BAD (FFFFFF)
+Size 41: 21,41 BAD (FFFFFF)
+Size 42: 21,42 BAD (FFFFFF)
+Size 43: 22,43 BAD (FFFFFF)
+Size 44: 22,44 BAD (FFFFFF)
+Size 45: 23,45 BAD (FFFFFF)
+Size 46: 23,46 BAD (FFFFFF)
+Size 47: 24,47 BAD (FFFFFF)
+Size 48: 24,48 BAD (FFFFFF)
+Size 49: 25,49 BAD (FFFFFF)
+Size 50: 25,50 BAD (FFFFFF)
+Size 51: 26,51 BAD (FFFFFF)
+Size 52: 26,52 BAD (FFFFFF)
+Size 53: 27,53 BAD (FFFFFF)
+Size 54: 27,54 BAD (FFFFFF)
+Size 55: 28,55 BAD (FFFFFF)
+Size 56: 28,56 BAD (FFFFFF)
+Size 57: 29,57 BAD (FFFFFF)
+Size 58: 29,58 BAD (FFFFFF)
+Size 59: 30,59 BAD (FFFFFF)
+Size 60: 30,60 BAD (FFFFFF)
+Size 61: 31,61 BAD (FFFFFF)
+Size 62: 31,62 BAD (FFFFFF)
+Size 63: 32,63 BAD (FFFFFF)
+Size 64: 32,64 BAD (FFFFFF)
+Size 65: 33,65 BAD (FFFFFF)
+Size 66: 33,66 BAD (FFFFFF)
+Size 67: 34,67 BAD (FFFFFF)
+Size 68: 34,68 BAD (FFFFFF)
+Size 69: 35,69 BAD (FFFFFF)
+Size 70: 35,70 BAD (FFFFFF)
+Size 71: 36,71 BAD (FFFFFF)
+Size 72: 36,72 BAD (FFFFFF)
+Size 73: 37,73 BAD (FFFFFF)
+Size 74: 37,74 BAD (FFFFFF)
+Size 75: 38,75 BAD (FFFFFF)
+Size 76: 38,76 BAD (FFFFFF)
+Size 77: 39,77 BAD (FFFFFF)
+Size 78: 39,78 BAD (FFFFFF)
+Size 79: 40,79 BAD (FFFFFF)
+Size 80: 40,80 BAD (FFFFFF)
+Size 81: 41,81 BAD (FFFFFF)
+Size 82: 41,82 BAD (FFFFFF)
+Size 83: 42,83 BAD (FFFFFF)
+Size 84: 42,84 BAD (FFFFFF)
+Size 85: 43,85 BAD (FFFFFF)
+Size 86: 43,86 BAD (FFFFFF)
+Size 87: 44,87 BAD (FFFFFF)
+Size 88: 44,88 BAD (FFFFFF)
+Size 89: 45,89 BAD (FFFFFF)
+Size 90: 45,90 BAD (FFFFFF)
+Size 91: 46,91 BAD (FFFFFF)
+Size 92: 46,92 BAD (FFFFFF)
+Size 93: 47,93 BAD (FFFFFF)
+Size 94: 47,94 BAD (FFFFFF)
+Size 95: 48,95 BAD (FFFFFF)
+Size 96: 48,97 BAD (FFFFFF)
+Size 97: 49,97 BAD (FFFFFF)
+Size 98: 49,98 BAD (FFFFFF)
+Size 99: 50,99 BAD (FFFFFF)
+Size 100: 50,100 BAD (FFFFFF)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 OK (HHHFFF)
+Size 4: 2,4 OK (HHHFFF)
+Size 5: 3,5 OK (HHHFFF)
+Size 6: 3,6 OK (HHHFFF)
+Size 7: 4,7 OK (HHHFFF)
+Size 8: 4,8 OK (HHHFFF)
+Size 9: 5,9 OK (HHHFFF)
+Size 10: 5,10 OK (HHHFFF)
+Size 11: 6,11 OK (HHHFFF)
+Size 12: 6,12 OK (HHHFFF)
+Size 13: 7,13 OK (HHHFFF)
+Size 14: 7,14 OK (HHHFFF)
+Size 15: 8,15 OK (HHHFFF)
+Size 16: 8,16 OK (HHHFFF)
+Size 17: 9,17 OK (HHHFFF)
+Size 18: 9,18 OK (HHHFFF)
+Size 19: 10,19 OK (HHHFFF)
+Size 20: 10,20 OK (HHHFFF)
+Size 21: 11,21 OK (HHHFFF)
+Size 22: 11,22 OK (HHHFFF)
+Size 23: 12,23 OK (HHHFFF)
+Size 24: 12,24 OK (HHHFFF)
+Size 25: 13,25 OK (HHHFFF)
+Size 26: 13,26 OK (HHHFFF)
+Size 27: 14,27 OK (HHHFFF)
+Size 28: 14,28 OK (HHHFFF)
+Size 29: 15,29 OK (HHHFFF)
+Size 30: 15,30 OK (HHHFFF)
+Size 31: 16,31 OK (HHHFFF)
+Size 32: 16,32 OK (HHHFFF)
+Size 33: 17,33 OK (HHHFFF)
+Size 34: 17,34 OK (HHHFFF)
+Size 35: 18,35 OK (HHHFFF)
+Size 36: 18,36 OK (HHHFFF)
+Size 37: 19,37 OK (HHHFFF)
+Size 38: 19,38 OK (HHHFFF)
+Size 39: 20,39 OK (HHHFFF)
+Size 40: 20,40 OK (HHHFFF)
+Size 41: 21,41 OK (HHHFFF)
+Size 42: 21,42 OK (HHHFFF)
+Size 43: 22,43 OK (HHHFFF)
+Size 44: 22,44 OK (HHHFFF)
+Size 45: 23,45 OK (HHHFFF)
+Size 46: 23,46 OK (HHHFFF)
+Size 47: 24,47 OK (HHHFFF)
+Size 48: 24,48 OK (HHHFFF)
+Size 49: 25,49 OK (HHHFFF)
+Size 50: 25,50 OK (HHHFFF)
+Size 51: 26,51 OK (HHHFFF)
+Size 52: 26,52 OK (HHHFFF)
+Size 53: 27,53 OK (HHHFFF)
+Size 54: 27,54 OK (HHHFFF)
+Size 55: 28,55 OK (HHHFFF)
+Size 56: 28,56 OK (HHHFFF)
+Size 57: 29,57 OK (HHHFFF)
+Size 58: 29,58 OK (HHHFFF)
+Size 59: 30,59 OK (HHHFFF)
+Size 60: 30,60 OK (HHHFFF)
+Size 61: 31,61 OK (HHHFFF)
+Size 62: 31,62 OK (HHHFFF)
+Size 63: 32,63 OK (HHHFFF)
+Size 64: 32,64 OK (HHHFFF)
+Size 65: 33,65 OK (HHHFFF)
+Size 66: 33,66 OK (HHHFFF)
+Size 67: 34,67 OK (HHHFFF)
+Size 68: 34,68 OK (HHHFFF)
+Size 69: 35,69 OK (HHHFFF)
+Size 70: 35,70 OK (HHHFFF)
+Size 71: 36,71 OK (HHHFFF)
+Size 72: 36,72 OK (HHHFFF)
+Size 73: 37,73 OK (HHHFFF)
+Size 74: 37,74 OK (HHHFFF)
+Size 75: 38,75 OK (HHHFFF)
+Size 76: 38,76 OK (HHHFFF)
+Size 77: 39,77 OK (HHHFFF)
+Size 78: 39,78 OK (HHHFFF)
+Size 79: 40,79 OK (HHHFFF)
+Size 80: 40,80 OK (HHHFFF)
+Size 81: 41,81 OK (HHHFFF)
+Size 82: 41,82 OK (HHHFFF)
+Size 83: 42,83 OK (HHHFFF)
+Size 84: 42,84 OK (HHHFFF)
+Size 85: 43,85 OK (HHHFFF)
+Size 86: 43,86 OK (HHHFFF)
+Size 87: 44,87 OK (HHHFFF)
+Size 88: 44,88 OK (HHHFFF)
+Size 89: 45,89 OK (HHHFFF)
+Size 90: 45,90 OK (HHHFFF)
+Size 91: 46,91 OK (HHHFFF)
+Size 92: 46,92 OK (HHHFFF)
+Size 93: 47,93 OK (HHHFFF)
+Size 94: 47,94 OK (HHHFFF)
+Size 95: 48,95 OK (HHHFFF)
+Size 96: 48,96 OK (HHHFFF)
+Size 97: 49,97 OK (HHHFFF)
+Size 98: 49,98 OK (HHHFFF)
+Size 99: 50,99 OK (HHHFFF)
+Size 100: 50,100 OK (HHHFFF)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP936.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP936.txt
new file mode 100644
index 0000000000..43210dac51
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP936.txt
@@ -0,0 +1,630 @@
+==========================================================
+Code Page 936, Chinese Simplified (China/PRC), SimSun font
+==========================================================
+
+Options: -face-simsun -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+Vista
+-----
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (HHHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (HHHFHH)
+Size 8: 4,9 GOOD (HHFFFF)
+Size 9: 5,10 BAD (HHHFHH)
+Size 10: 5,11 GOOD (HHFFFF)
+Size 11: 6,13 BAD (HHHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,15 BAD (HHHFHH)
+Size 14: 7,16 GOOD (HHFFFF)
+Size 15: 8,17 BAD (HHHFHH)
+Size 16: 8,18 GOOD (HHFFFF)
+Size 17: 9,19 BAD (HHHFHH)
+Size 18: 9,21 GOOD (HHFFFF)
+Size 19: 10,22 BAD (HHHFHH)
+Size 20: 10,23 GOOD (HHFFFF)
+Size 21: 11,24 BAD (HHHFHH)
+Size 22: 11,25 GOOD (HHFFFF)
+Size 23: 12,26 BAD (HHHFHH)
+Size 24: 12,27 GOOD (HHFFFF)
+Size 25: 13,29 BAD (HHHFHH)
+Size 26: 13,30 GOOD (HHFFFF)
+Size 27: 14,31 BAD (HHHFHH)
+Size 28: 14,32 GOOD (HHFFFF)
+Size 29: 15,33 BAD (HHHFHH)
+Size 30: 15,34 GOOD (HHFFFF)
+Size 31: 16,35 BAD (HHHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,38 BAD (HHHFHH)
+Size 34: 17,39 GOOD (HHFFFF)
+Size 35: 18,40 BAD (HHHFHH)
+Size 36: 18,41 GOOD (HHFFFF)
+Size 37: 19,42 BAD (HHHFHH)
+Size 38: 19,43 GOOD (HHFFFF)
+Size 39: 20,44 BAD (HHHFHH)
+Size 40: 20,46 GOOD (HHFFFF)
+Size 41: 21,47 BAD (HHHFHH)
+Size 42: 21,48 GOOD (HHFFFF)
+Size 43: 22,49 BAD (HHHFHH)
+Size 44: 22,50 GOOD (HHFFFF)
+Size 45: 23,51 BAD (HHHFHH)
+Size 46: 23,52 GOOD (HHFFFF)
+Size 47: 24,54 BAD (HHHFHH)
+Size 48: 24,55 GOOD (HHFFFF)
+Size 49: 25,56 BAD (HHHFHH)
+Size 50: 25,57 GOOD (HHFFFF)
+Size 51: 26,58 BAD (HHHFHH)
+Size 52: 26,59 GOOD (HHFFFF)
+Size 53: 27,60 BAD (HHHFHH)
+Size 54: 27,62 GOOD (HHFFFF)
+Size 55: 28,63 BAD (HHHFHH)
+Size 56: 28,64 GOOD (HHFFFF)
+Size 57: 29,65 BAD (HHHFHH)
+Size 58: 29,66 GOOD (HHFFFF)
+Size 59: 30,67 BAD (HHHFHH)
+Size 60: 30,68 GOOD (HHFFFF)
+Size 61: 31,70 BAD (HHHFHH)
+Size 62: 31,71 GOOD (HHFFFF)
+Size 63: 32,72 BAD (HHHFHH)
+Size 64: 32,73 GOOD (HHFFFF)
+Size 65: 33,74 GOOD (HHFFFF)
+Size 66: 33,75 GOOD (HHFFFF)
+Size 67: 34,76 GOOD (HHFFFF)
+Size 68: 34,78 GOOD (HHFFFF)
+Size 69: 35,79 GOOD (HHFFFF)
+Size 70: 35,80 GOOD (HHFFFF)
+Size 71: 36,81 GOOD (HHFFFF)
+Size 72: 36,82 GOOD (HHFFFF)
+Size 73: 37,83 GOOD (HHFFFF)
+Size 74: 37,84 GOOD (HHFFFF)
+Size 75: 38,86 GOOD (HHFFFF)
+Size 76: 38,87 GOOD (HHFFFF)
+Size 77: 39,88 GOOD (HHFFFF)
+Size 78: 39,89 GOOD (HHFFFF)
+Size 79: 40,90 GOOD (HHFFFF)
+Size 80: 40,91 GOOD (HHFFFF)
+Size 81: 41,92 GOOD (HHFFFF)
+Size 82: 41,94 GOOD (HHFFFF)
+Size 83: 42,95 GOOD (HHFFFF)
+Size 84: 42,96 GOOD (HHFFFF)
+Size 85: 43,97 GOOD (HHFFFF)
+Size 86: 43,98 GOOD (HHFFFF)
+Size 87: 44,99 GOOD (HHFFFF)
+Size 88: 44,100 GOOD (HHFFFF)
+Size 89: 45,102 GOOD (HHFFFF)
+Size 90: 45,103 GOOD (HHFFFF)
+Size 91: 46,104 GOOD (HHFFFF)
+Size 92: 46,105 GOOD (HHFFFF)
+Size 93: 47,106 GOOD (HHFFFF)
+Size 94: 47,107 GOOD (HHFFFF)
+Size 95: 48,108 GOOD (HHFFFF)
+Size 96: 48,111 GOOD (HHFFFF)
+Size 97: 49,111 GOOD (HHFFFF)
+Size 98: 49,112 GOOD (HHFFFF)
+Size 99: 50,113 GOOD (HHFFFF)
+Size 100: 50,114 GOOD (HHFFFF)
+
+Windows 7
+---------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,9 GOOD (HHFFFF)
+Size 9: 5,10 BAD (FFHFHH)
+Size 10: 5,11 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,15 BAD (FFHFHH)
+Size 14: 7,16 GOOD (HHFFFF)
+Size 15: 8,17 BAD (FFHFHH)
+Size 16: 8,18 GOOD (HHFFFF)
+Size 17: 9,19 BAD (FFHFHH)
+Size 18: 9,21 GOOD (HHFFFF)
+Size 19: 10,22 BAD (FFHFHH)
+Size 20: 10,23 GOOD (HHFFFF)
+Size 21: 11,24 BAD (FFHFHH)
+Size 22: 11,25 GOOD (HHFFFF)
+Size 23: 12,26 BAD (FFHFHH)
+Size 24: 12,27 GOOD (HHFFFF)
+Size 25: 13,29 BAD (FFHFHH)
+Size 26: 13,30 GOOD (HHFFFF)
+Size 27: 14,31 BAD (FFHFHH)
+Size 28: 14,32 GOOD (HHFFFF)
+Size 29: 15,33 BAD (FFHFHH)
+Size 30: 15,34 GOOD (HHFFFF)
+Size 31: 16,35 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,38 BAD (FFHFHH)
+Size 34: 17,39 GOOD (HHFFFF)
+Size 35: 18,40 BAD (FFHFHH)
+Size 36: 18,41 GOOD (HHFFFF)
+Size 37: 19,42 BAD (FFHFHH)
+Size 38: 19,43 GOOD (HHFFFF)
+Size 39: 20,44 BAD (FFHFHH)
+Size 40: 20,46 GOOD (HHFFFF)
+Size 41: 21,47 BAD (FFHFHH)
+Size 42: 21,48 GOOD (HHFFFF)
+Size 43: 22,49 BAD (FFHFHH)
+Size 44: 22,50 GOOD (HHFFFF)
+Size 45: 23,51 BAD (FFHFHH)
+Size 46: 23,52 GOOD (HHFFFF)
+Size 47: 24,54 BAD (FFHFHH)
+Size 48: 24,55 GOOD (HHFFFF)
+Size 49: 25,56 BAD (FFHFHH)
+Size 50: 25,57 GOOD (HHFFFF)
+Size 51: 26,58 BAD (FFHFHH)
+Size 52: 26,59 GOOD (HHFFFF)
+Size 53: 27,60 BAD (FFHFHH)
+Size 54: 27,62 GOOD (HHFFFF)
+Size 55: 28,63 BAD (FFHFHH)
+Size 56: 28,64 GOOD (HHFFFF)
+Size 57: 29,65 BAD (FFHFHH)
+Size 58: 29,66 GOOD (HHFFFF)
+Size 59: 30,67 BAD (FFHFHH)
+Size 60: 30,68 GOOD (HHFFFF)
+Size 61: 31,70 BAD (FFHFHH)
+Size 62: 31,71 GOOD (HHFFFF)
+Size 63: 32,72 BAD (FFHFHH)
+Size 64: 32,73 GOOD (HHFFFF)
+Size 65: 33,74 GOOD (HHFFFF)
+Size 66: 33,75 GOOD (HHFFFF)
+Size 67: 34,76 GOOD (HHFFFF)
+Size 68: 34,78 GOOD (HHFFFF)
+Size 69: 35,79 GOOD (HHFFFF)
+Size 70: 35,80 GOOD (HHFFFF)
+Size 71: 36,81 GOOD (HHFFFF)
+Size 72: 36,82 GOOD (HHFFFF)
+Size 73: 37,83 GOOD (HHFFFF)
+Size 74: 37,84 GOOD (HHFFFF)
+Size 75: 38,86 GOOD (HHFFFF)
+Size 76: 38,87 GOOD (HHFFFF)
+Size 77: 39,88 GOOD (HHFFFF)
+Size 78: 39,89 GOOD (HHFFFF)
+Size 79: 40,90 GOOD (HHFFFF)
+Size 80: 40,91 GOOD (HHFFFF)
+Size 81: 41,92 GOOD (HHFFFF)
+Size 82: 41,94 GOOD (HHFFFF)
+Size 83: 42,95 GOOD (HHFFFF)
+Size 84: 42,96 GOOD (HHFFFF)
+Size 85: 43,97 GOOD (HHFFFF)
+Size 86: 43,98 GOOD (HHFFFF)
+Size 87: 44,99 GOOD (HHFFFF)
+Size 88: 44,100 GOOD (HHFFFF)
+Size 89: 45,102 GOOD (HHFFFF)
+Size 90: 45,103 GOOD (HHFFFF)
+Size 91: 46,104 GOOD (HHFFFF)
+Size 92: 46,105 GOOD (HHFFFF)
+Size 93: 47,106 GOOD (HHFFFF)
+Size 94: 47,107 GOOD (HHFFFF)
+Size 95: 48,108 GOOD (HHFFFF)
+Size 96: 48,111 GOOD (HHFFFF)
+Size 97: 49,111 GOOD (HHFFFF)
+Size 98: 49,112 GOOD (HHFFFF)
+Size 99: 50,113 GOOD (HHFFFF)
+Size 100: 50,114 GOOD (HHFFFF)
+
+Windows 8
+---------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,9 GOOD (HHFFFF)
+Size 9: 5,10 BAD (FFHFHH)
+Size 10: 5,11 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,15 BAD (FFHFHH)
+Size 14: 7,16 GOOD (HHFFFF)
+Size 15: 8,17 BAD (FFHFHH)
+Size 16: 8,18 GOOD (HHFFFF)
+Size 17: 9,19 BAD (FFHFHH)
+Size 18: 9,21 GOOD (HHFFFF)
+Size 19: 10,22 BAD (FFHFHH)
+Size 20: 10,23 GOOD (HHFFFF)
+Size 21: 11,24 BAD (FFHFHH)
+Size 22: 11,25 GOOD (HHFFFF)
+Size 23: 12,26 BAD (FFHFHH)
+Size 24: 12,27 GOOD (HHFFFF)
+Size 25: 13,29 BAD (FFHFHH)
+Size 26: 13,30 GOOD (HHFFFF)
+Size 27: 14,31 BAD (FFHFHH)
+Size 28: 14,32 GOOD (HHFFFF)
+Size 29: 15,33 BAD (FFHFHH)
+Size 30: 15,34 GOOD (HHFFFF)
+Size 31: 16,35 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,38 BAD (FFHFHH)
+Size 34: 17,39 GOOD (HHFFFF)
+Size 35: 18,40 BAD (FFHFHH)
+Size 36: 18,41 GOOD (HHFFFF)
+Size 37: 19,42 BAD (FFHFHH)
+Size 38: 19,43 GOOD (HHFFFF)
+Size 39: 20,44 BAD (FFHFHH)
+Size 40: 20,46 GOOD (HHFFFF)
+Size 41: 21,47 BAD (FFHFHH)
+Size 42: 21,48 GOOD (HHFFFF)
+Size 43: 22,49 BAD (FFHFHH)
+Size 44: 22,50 GOOD (HHFFFF)
+Size 45: 23,51 BAD (FFHFHH)
+Size 46: 23,52 GOOD (HHFFFF)
+Size 47: 24,54 BAD (FFHFHH)
+Size 48: 24,55 GOOD (HHFFFF)
+Size 49: 25,56 BAD (FFHFHH)
+Size 50: 25,57 GOOD (HHFFFF)
+Size 51: 26,58 BAD (FFHFHH)
+Size 52: 26,59 GOOD (HHFFFF)
+Size 53: 27,60 BAD (FFHFHH)
+Size 54: 27,62 GOOD (HHFFFF)
+Size 55: 28,63 BAD (FFHFHH)
+Size 56: 28,64 GOOD (HHFFFF)
+Size 57: 29,65 BAD (FFHFHH)
+Size 58: 29,66 GOOD (HHFFFF)
+Size 59: 30,67 BAD (FFHFHH)
+Size 60: 30,68 GOOD (HHFFFF)
+Size 61: 31,70 BAD (FFHFHH)
+Size 62: 31,71 GOOD (HHFFFF)
+Size 63: 32,72 BAD (FFHFHH)
+Size 64: 32,73 GOOD (HHFFFF)
+Size 65: 33,74 GOOD (HHFFFF)
+Size 66: 33,75 GOOD (HHFFFF)
+Size 67: 34,76 GOOD (HHFFFF)
+Size 68: 34,78 GOOD (HHFFFF)
+Size 69: 35,79 GOOD (HHFFFF)
+Size 70: 35,80 GOOD (HHFFFF)
+Size 71: 36,81 GOOD (HHFFFF)
+Size 72: 36,82 GOOD (HHFFFF)
+Size 73: 37,83 GOOD (HHFFFF)
+Size 74: 37,84 GOOD (HHFFFF)
+Size 75: 38,86 GOOD (HHFFFF)
+Size 76: 38,87 GOOD (HHFFFF)
+Size 77: 39,88 GOOD (HHFFFF)
+Size 78: 39,89 GOOD (HHFFFF)
+Size 79: 40,90 GOOD (HHFFFF)
+Size 80: 40,91 GOOD (HHFFFF)
+Size 81: 41,92 GOOD (HHFFFF)
+Size 82: 41,94 GOOD (HHFFFF)
+Size 83: 42,95 GOOD (HHFFFF)
+Size 84: 42,96 GOOD (HHFFFF)
+Size 85: 43,97 GOOD (HHFFFF)
+Size 86: 43,98 GOOD (HHFFFF)
+Size 87: 44,99 GOOD (HHFFFF)
+Size 88: 44,100 GOOD (HHFFFF)
+Size 89: 45,102 GOOD (HHFFFF)
+Size 90: 45,103 GOOD (HHFFFF)
+Size 91: 46,104 GOOD (HHFFFF)
+Size 92: 46,105 GOOD (HHFFFF)
+Size 93: 47,106 GOOD (HHFFFF)
+Size 94: 47,107 GOOD (HHFFFF)
+Size 95: 48,108 GOOD (HHFFFF)
+Size 96: 48,111 GOOD (HHFFFF)
+Size 97: 49,111 GOOD (HHFFFF)
+Size 98: 49,112 GOOD (HHFFFF)
+Size 99: 50,113 GOOD (HHFFFF)
+Size 100: 50,114 GOOD (HHFFFF)
+
+Windows 8.1
+-----------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,9 GOOD (HHFFFF)
+Size 9: 5,10 BAD (FFHFHH)
+Size 10: 5,11 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,15 BAD (FFHFHH)
+Size 14: 7,16 GOOD (HHFFFF)
+Size 15: 8,17 BAD (FFHFHH)
+Size 16: 8,18 GOOD (HHFFFF)
+Size 17: 9,19 BAD (FFHFHH)
+Size 18: 9,21 GOOD (HHFFFF)
+Size 19: 10,22 BAD (FFHFHH)
+Size 20: 10,23 GOOD (HHFFFF)
+Size 21: 11,24 BAD (FFHFHH)
+Size 22: 11,25 GOOD (HHFFFF)
+Size 23: 12,26 BAD (FFHFHH)
+Size 24: 12,27 GOOD (HHFFFF)
+Size 25: 13,29 BAD (FFHFHH)
+Size 26: 13,30 GOOD (HHFFFF)
+Size 27: 14,31 BAD (FFHFHH)
+Size 28: 14,32 GOOD (HHFFFF)
+Size 29: 15,33 BAD (FFHFHH)
+Size 30: 15,34 GOOD (HHFFFF)
+Size 31: 16,35 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,38 BAD (FFHFHH)
+Size 34: 17,39 GOOD (HHFFFF)
+Size 35: 18,40 BAD (FFHFHH)
+Size 36: 18,41 GOOD (HHFFFF)
+Size 37: 19,42 BAD (FFHFHH)
+Size 38: 19,43 GOOD (HHFFFF)
+Size 39: 20,44 BAD (FFHFHH)
+Size 40: 20,46 GOOD (HHFFFF)
+Size 41: 21,47 BAD (FFHFHH)
+Size 42: 21,48 GOOD (HHFFFF)
+Size 43: 22,49 BAD (FFHFHH)
+Size 44: 22,50 GOOD (HHFFFF)
+Size 45: 23,51 BAD (FFHFHH)
+Size 46: 23,52 GOOD (HHFFFF)
+Size 47: 24,54 BAD (FFHFHH)
+Size 48: 24,55 GOOD (HHFFFF)
+Size 49: 25,56 BAD (FFHFHH)
+Size 50: 25,57 GOOD (HHFFFF)
+Size 51: 26,58 BAD (FFHFHH)
+Size 52: 26,59 GOOD (HHFFFF)
+Size 53: 27,60 BAD (FFHFHH)
+Size 54: 27,62 GOOD (HHFFFF)
+Size 55: 28,63 BAD (FFHFHH)
+Size 56: 28,64 GOOD (HHFFFF)
+Size 57: 29,65 BAD (FFHFHH)
+Size 58: 29,66 GOOD (HHFFFF)
+Size 59: 30,67 BAD (FFHFHH)
+Size 60: 30,68 GOOD (HHFFFF)
+Size 61: 31,70 BAD (FFHFHH)
+Size 62: 31,71 GOOD (HHFFFF)
+Size 63: 32,72 BAD (FFHFHH)
+Size 64: 32,73 GOOD (HHFFFF)
+Size 65: 33,74 GOOD (HHFFFF)
+Size 66: 33,75 GOOD (HHFFFF)
+Size 67: 34,76 GOOD (HHFFFF)
+Size 68: 34,78 GOOD (HHFFFF)
+Size 69: 35,79 GOOD (HHFFFF)
+Size 70: 35,80 GOOD (HHFFFF)
+Size 71: 36,81 GOOD (HHFFFF)
+Size 72: 36,82 GOOD (HHFFFF)
+Size 73: 37,83 GOOD (HHFFFF)
+Size 74: 37,84 GOOD (HHFFFF)
+Size 75: 38,86 GOOD (HHFFFF)
+Size 76: 38,87 GOOD (HHFFFF)
+Size 77: 39,88 GOOD (HHFFFF)
+Size 78: 39,89 GOOD (HHFFFF)
+Size 79: 40,90 GOOD (HHFFFF)
+Size 80: 40,91 GOOD (HHFFFF)
+Size 81: 41,92 GOOD (HHFFFF)
+Size 82: 41,94 GOOD (HHFFFF)
+Size 83: 42,95 GOOD (HHFFFF)
+Size 84: 42,96 GOOD (HHFFFF)
+Size 85: 43,97 GOOD (HHFFFF)
+Size 86: 43,98 GOOD (HHFFFF)
+Size 87: 44,99 GOOD (HHFFFF)
+Size 88: 44,100 GOOD (HHFFFF)
+Size 89: 45,102 GOOD (HHFFFF)
+Size 90: 45,103 GOOD (HHFFFF)
+Size 91: 46,104 GOOD (HHFFFF)
+Size 92: 46,105 GOOD (HHFFFF)
+Size 93: 47,106 GOOD (HHFFFF)
+Size 94: 47,107 GOOD (HHFFFF)
+Size 95: 48,108 GOOD (HHFFFF)
+Size 96: 48,111 GOOD (HHFFFF)
+Size 97: 49,111 GOOD (HHFFFF)
+Size 98: 49,112 GOOD (HHFFFF)
+Size 99: 50,113 GOOD (HHFFFF)
+Size 100: 50,114 GOOD (HHFFFF)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,9 GOOD (HHFFFF)
+Size 9: 5,10 BAD (FFHFHH)
+Size 10: 5,11 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,15 BAD (FFHFHH)
+Size 14: 7,16 GOOD (HHFFFF)
+Size 15: 8,17 BAD (FFHFHH)
+Size 16: 8,18 GOOD (HHFFFF)
+Size 17: 9,19 BAD (FFHFHH)
+Size 18: 9,21 GOOD (HHFFFF)
+Size 19: 10,22 BAD (FFHFHH)
+Size 20: 10,23 GOOD (HHFFFF)
+Size 21: 11,24 BAD (FFHFHH)
+Size 22: 11,25 GOOD (HHFFFF)
+Size 23: 12,26 BAD (FFHFHH)
+Size 24: 12,27 GOOD (HHFFFF)
+Size 25: 13,29 BAD (FFHFHH)
+Size 26: 13,30 GOOD (HHFFFF)
+Size 27: 14,31 BAD (FFHFHH)
+Size 28: 14,32 GOOD (HHFFFF)
+Size 29: 15,33 BAD (FFHFHH)
+Size 30: 15,34 GOOD (HHFFFF)
+Size 31: 16,35 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,38 BAD (FFHFHH)
+Size 34: 17,39 GOOD (HHFFFF)
+Size 35: 18,40 BAD (FFHFHH)
+Size 36: 18,41 GOOD (HHFFFF)
+Size 37: 19,42 BAD (FFHFHH)
+Size 38: 19,43 GOOD (HHFFFF)
+Size 39: 20,44 BAD (FFHFHH)
+Size 40: 20,46 GOOD (HHFFFF)
+Size 41: 21,47 BAD (FFHFHH)
+Size 42: 21,48 GOOD (HHFFFF)
+Size 43: 22,49 BAD (FFHFHH)
+Size 44: 22,50 GOOD (HHFFFF)
+Size 45: 23,51 BAD (FFHFHH)
+Size 46: 23,52 GOOD (HHFFFF)
+Size 47: 24,54 BAD (FFHFHH)
+Size 48: 24,55 GOOD (HHFFFF)
+Size 49: 25,56 BAD (FFHFHH)
+Size 50: 25,57 GOOD (HHFFFF)
+Size 51: 26,58 BAD (FFHFHH)
+Size 52: 26,59 GOOD (HHFFFF)
+Size 53: 27,60 BAD (FFHFHH)
+Size 54: 27,62 GOOD (HHFFFF)
+Size 55: 28,63 BAD (FFHFHH)
+Size 56: 28,64 GOOD (HHFFFF)
+Size 57: 29,65 BAD (FFHFHH)
+Size 58: 29,66 GOOD (HHFFFF)
+Size 59: 30,67 BAD (FFHFHH)
+Size 60: 30,68 GOOD (HHFFFF)
+Size 61: 31,70 BAD (FFHFHH)
+Size 62: 31,71 GOOD (HHFFFF)
+Size 63: 32,72 BAD (FFHFHH)
+Size 64: 32,73 GOOD (HHFFFF)
+Size 65: 33,74 GOOD (HHFFFF)
+Size 66: 33,75 GOOD (HHFFFF)
+Size 67: 34,76 GOOD (HHFFFF)
+Size 68: 34,78 GOOD (HHFFFF)
+Size 69: 35,79 GOOD (HHFFFF)
+Size 70: 35,80 GOOD (HHFFFF)
+Size 71: 36,81 GOOD (HHFFFF)
+Size 72: 36,82 GOOD (HHFFFF)
+Size 73: 37,83 GOOD (HHFFFF)
+Size 74: 37,84 GOOD (HHFFFF)
+Size 75: 38,86 GOOD (HHFFFF)
+Size 76: 38,87 GOOD (HHFFFF)
+Size 77: 39,88 GOOD (HHFFFF)
+Size 78: 39,89 GOOD (HHFFFF)
+Size 79: 40,90 GOOD (HHFFFF)
+Size 80: 40,91 GOOD (HHFFFF)
+Size 81: 41,92 GOOD (HHFFFF)
+Size 82: 41,94 GOOD (HHFFFF)
+Size 83: 42,95 GOOD (HHFFFF)
+Size 84: 42,96 GOOD (HHFFFF)
+Size 85: 43,97 GOOD (HHFFFF)
+Size 86: 43,98 GOOD (HHFFFF)
+Size 87: 44,99 GOOD (HHFFFF)
+Size 88: 44,100 GOOD (HHFFFF)
+Size 89: 45,102 GOOD (HHFFFF)
+Size 90: 45,103 GOOD (HHFFFF)
+Size 91: 46,104 GOOD (HHFFFF)
+Size 92: 46,105 GOOD (HHFFFF)
+Size 93: 47,106 GOOD (HHFFFF)
+Size 94: 47,107 GOOD (HHFFFF)
+Size 95: 48,108 GOOD (HHFFFF)
+Size 96: 48,111 GOOD (HHFFFF)
+Size 97: 49,111 GOOD (HHFFFF)
+Size 98: 49,112 GOOD (HHFFFF)
+Size 99: 50,113 GOOD (HHFFFF)
+Size 100: 50,114 GOOD (HHFFFF)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 GOOD (HHFFFF)
+Size 4: 2,4 GOOD (HHFFFF)
+Size 5: 3,5 GOOD (HHFFFF)
+Size 6: 3,6 GOOD (HHFFFF)
+Size 7: 4,7 GOOD (HHFFFF)
+Size 8: 4,8 GOOD (HHFFFF)
+Size 9: 5,9 GOOD (HHFFFF)
+Size 10: 5,10 GOOD (HHFFFF)
+Size 11: 6,11 GOOD (HHFFFF)
+Size 12: 6,12 GOOD (HHFFFF)
+Size 13: 7,13 GOOD (HHFFFF)
+Size 14: 7,14 GOOD (HHFFFF)
+Size 15: 8,15 GOOD (HHFFFF)
+Size 16: 8,16 GOOD (HHFFFF)
+Size 17: 9,17 GOOD (HHFFFF)
+Size 18: 9,18 GOOD (HHFFFF)
+Size 19: 10,19 GOOD (HHFFFF)
+Size 20: 10,20 GOOD (HHFFFF)
+Size 21: 11,21 GOOD (HHFFFF)
+Size 22: 11,22 GOOD (HHFFFF)
+Size 23: 12,23 GOOD (HHFFFF)
+Size 24: 12,24 GOOD (HHFFFF)
+Size 25: 13,25 GOOD (HHFFFF)
+Size 26: 13,26 GOOD (HHFFFF)
+Size 27: 14,27 GOOD (HHFFFF)
+Size 28: 14,28 GOOD (HHFFFF)
+Size 29: 15,29 GOOD (HHFFFF)
+Size 30: 15,30 GOOD (HHFFFF)
+Size 31: 16,31 GOOD (HHFFFF)
+Size 32: 16,32 GOOD (HHFFFF)
+Size 33: 17,33 GOOD (HHFFFF)
+Size 34: 17,34 GOOD (HHFFFF)
+Size 35: 18,35 GOOD (HHFFFF)
+Size 36: 18,36 GOOD (HHFFFF)
+Size 37: 19,37 GOOD (HHFFFF)
+Size 38: 19,38 GOOD (HHFFFF)
+Size 39: 20,39 GOOD (HHFFFF)
+Size 40: 20,40 GOOD (HHFFFF)
+Size 41: 21,41 GOOD (HHFFFF)
+Size 42: 21,42 GOOD (HHFFFF)
+Size 43: 22,43 GOOD (HHFFFF)
+Size 44: 22,44 GOOD (HHFFFF)
+Size 45: 23,45 GOOD (HHFFFF)
+Size 46: 23,46 GOOD (HHFFFF)
+Size 47: 24,47 GOOD (HHFFFF)
+Size 48: 24,48 GOOD (HHFFFF)
+Size 49: 25,49 GOOD (HHFFFF)
+Size 50: 25,50 GOOD (HHFFFF)
+Size 51: 26,51 GOOD (HHFFFF)
+Size 52: 26,52 GOOD (HHFFFF)
+Size 53: 27,53 GOOD (HHFFFF)
+Size 54: 27,54 GOOD (HHFFFF)
+Size 55: 28,55 GOOD (HHFFFF)
+Size 56: 28,56 GOOD (HHFFFF)
+Size 57: 29,57 GOOD (HHFFFF)
+Size 58: 29,58 GOOD (HHFFFF)
+Size 59: 30,59 GOOD (HHFFFF)
+Size 60: 30,60 GOOD (HHFFFF)
+Size 61: 31,61 GOOD (HHFFFF)
+Size 62: 31,62 GOOD (HHFFFF)
+Size 63: 32,63 GOOD (HHFFFF)
+Size 64: 32,64 GOOD (HHFFFF)
+Size 65: 33,65 GOOD (HHFFFF)
+Size 66: 33,66 GOOD (HHFFFF)
+Size 67: 34,67 GOOD (HHFFFF)
+Size 68: 34,68 GOOD (HHFFFF)
+Size 69: 35,69 GOOD (HHFFFF)
+Size 70: 35,70 GOOD (HHFFFF)
+Size 71: 36,71 GOOD (HHFFFF)
+Size 72: 36,72 GOOD (HHFFFF)
+Size 73: 37,73 GOOD (HHFFFF)
+Size 74: 37,74 GOOD (HHFFFF)
+Size 75: 38,75 GOOD (HHFFFF)
+Size 76: 38,76 GOOD (HHFFFF)
+Size 77: 39,77 GOOD (HHFFFF)
+Size 78: 39,78 GOOD (HHFFFF)
+Size 79: 40,79 GOOD (HHFFFF)
+Size 80: 40,80 GOOD (HHFFFF)
+Size 81: 41,81 GOOD (HHFFFF)
+Size 82: 41,82 GOOD (HHFFFF)
+Size 83: 42,83 GOOD (HHFFFF)
+Size 84: 42,84 GOOD (HHFFFF)
+Size 85: 43,85 GOOD (HHFFFF)
+Size 86: 43,86 GOOD (HHFFFF)
+Size 87: 44,87 GOOD (HHFFFF)
+Size 88: 44,88 GOOD (HHFFFF)
+Size 89: 45,89 GOOD (HHFFFF)
+Size 90: 45,90 GOOD (HHFFFF)
+Size 91: 46,91 GOOD (HHFFFF)
+Size 92: 46,92 GOOD (HHFFFF)
+Size 93: 47,93 GOOD (HHFFFF)
+Size 94: 47,94 GOOD (HHFFFF)
+Size 95: 48,95 GOOD (HHFFFF)
+Size 96: 48,96 GOOD (HHFFFF)
+Size 97: 49,97 GOOD (HHFFFF)
+Size 98: 49,98 GOOD (HHFFFF)
+Size 99: 50,99 GOOD (HHFFFF)
+Size 100: 50,100 GOOD (HHFFFF)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP949.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP949.txt
new file mode 100644
index 0000000000..2f0ea1e7c2
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP949.txt
@@ -0,0 +1,630 @@
+=====================================
+Code Page 949, Korean, GulimChe font
+=====================================
+
+Options: -face-gulimche -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+Vista
+-----
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,5 OK (HHHFFF)
+Size 5: 3,6 BAD (HHHFHH)
+Size 6: 3,7 OK (HHHFFF)
+Size 7: 4,8 BAD (HHHFHH)
+Size 8: 4,9 OK (HHHFFF)
+Size 9: 5,10 BAD (HHHFHH)
+Size 10: 5,11 OK (HHHFFF)
+Size 11: 6,13 BAD (HHHFHH)
+Size 12: 6,14 OK (HHHFFF)
+Size 13: 7,15 BAD (HHHFHH)
+Size 14: 7,16 OK (HHHFFF)
+Size 15: 8,17 BAD (HHHFHH)
+Size 16: 8,18 OK (HHHFFF)
+Size 17: 9,20 BAD (HHHFHH)
+Size 18: 9,21 OK (HHHFFF)
+Size 19: 10,22 BAD (HHHFHH)
+Size 20: 10,23 OK (HHHFFF)
+Size 21: 11,24 BAD (HHHFHH)
+Size 22: 11,25 OK (HHHFFF)
+Size 23: 12,26 BAD (HHHFHH)
+Size 24: 12,28 OK (HHHFFF)
+Size 25: 13,29 BAD (HHHFHH)
+Size 26: 13,30 OK (HHHFFF)
+Size 27: 14,31 BAD (HHHFHH)
+Size 28: 14,32 OK (HHHFFF)
+Size 29: 15,33 BAD (HHHFHH)
+Size 30: 15,34 OK (HHHFFF)
+Size 31: 16,36 BAD (HHHFHH)
+Size 32: 16,37 OK (HHHFFF)
+Size 33: 17,38 BAD (HHHFHH)
+Size 34: 17,39 OK (HHHFFF)
+Size 35: 18,40 BAD (HHHFHH)
+Size 36: 18,41 OK (HHHFFF)
+Size 37: 19,42 BAD (HHHFHH)
+Size 38: 19,44 OK (HHHFFF)
+Size 39: 20,45 BAD (HHHFHH)
+Size 40: 20,46 OK (HHHFFF)
+Size 41: 21,47 BAD (HHHFHH)
+Size 42: 21,48 OK (HHHFFF)
+Size 43: 22,49 BAD (HHHFHH)
+Size 44: 22,51 OK (HHHFFF)
+Size 45: 23,52 BAD (HHHFHH)
+Size 46: 23,53 OK (HHHFFF)
+Size 47: 24,54 BAD (HHHFHH)
+Size 48: 24,55 OK (HHHFFF)
+Size 49: 25,56 BAD (HHHFHH)
+Size 50: 25,57 OK (HHHFFF)
+Size 51: 26,59 BAD (HHHFHH)
+Size 52: 26,60 OK (HHHFFF)
+Size 53: 27,61 BAD (HHHFHH)
+Size 54: 27,62 OK (HHHFFF)
+Size 55: 28,63 BAD (HHHFHH)
+Size 56: 28,64 OK (HHHFFF)
+Size 57: 29,65 BAD (HHHFHH)
+Size 58: 29,67 OK (HHHFFF)
+Size 59: 30,68 BAD (HHHFHH)
+Size 60: 30,69 OK (HHHFFF)
+Size 61: 31,70 BAD (HHHFHH)
+Size 62: 31,71 OK (HHHFFF)
+Size 63: 32,72 BAD (HHHFHH)
+Size 64: 32,74 OK (HHHFFF)
+Size 65: 33,75 BAD (HHHFHH)
+Size 66: 33,76 OK (HHHFFF)
+Size 67: 34,77 BAD (HHHFHH)
+Size 68: 34,78 OK (HHHFFF)
+Size 69: 35,79 BAD (HHHFHH)
+Size 70: 35,80 OK (HHHFFF)
+Size 71: 36,82 BAD (HHHFHH)
+Size 72: 36,83 OK (HHHFFF)
+Size 73: 37,84 BAD (HHHFHH)
+Size 74: 37,85 OK (HHHFFF)
+Size 75: 38,86 BAD (HHHFHH)
+Size 76: 38,87 OK (HHHFFF)
+Size 77: 39,88 BAD (HHHFHH)
+Size 78: 39,90 OK (HHHFFF)
+Size 79: 40,91 BAD (HHHFHH)
+Size 80: 40,92 OK (HHHFFF)
+Size 81: 41,93 BAD (HHHFHH)
+Size 82: 41,94 OK (HHHFFF)
+Size 83: 42,95 BAD (HHHFHH)
+Size 84: 42,96 OK (HHHFFF)
+Size 85: 43,98 BAD (HHHFHH)
+Size 86: 43,99 OK (HHHFFF)
+Size 87: 44,100 BAD (HHHFHH)
+Size 88: 44,101 OK (HHHFFF)
+Size 89: 45,102 BAD (HHHFHH)
+Size 90: 45,103 OK (HHHFFF)
+Size 91: 46,105 BAD (HHHFHH)
+Size 92: 46,106 OK (HHHFFF)
+Size 93: 47,107 BAD (HHHFHH)
+Size 94: 47,108 OK (HHHFFF)
+Size 95: 48,109 BAD (HHHFHH)
+Size 96: 48,110 OK (HHHFFF)
+Size 97: 49,111 BAD (HHHFHH)
+Size 98: 49,113 OK (HHHFFF)
+Size 99: 50,114 BAD (HHHFHH)
+Size 100: 50,115 OK (HHHFFF)
+
+Windows 7
+---------
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,5 OK (HHHFFF)
+Size 5: 3,6 BAD (FFFFHH)
+Size 6: 3,7 OK (HHHFFF)
+Size 7: 4,8 BAD (FFFFHH)
+Size 8: 4,9 OK (HHHFFF)
+Size 9: 5,10 BAD (FFFFHH)
+Size 10: 5,11 OK (HHHFFF)
+Size 11: 6,13 BAD (FFFFHH)
+Size 12: 6,14 OK (HHHFFF)
+Size 13: 7,15 BAD (FFFFHH)
+Size 14: 7,16 OK (HHHFFF)
+Size 15: 8,17 BAD (FFFFHH)
+Size 16: 8,18 OK (HHHFFF)
+Size 17: 9,20 BAD (FFFFHH)
+Size 18: 9,21 OK (HHHFFF)
+Size 19: 10,22 BAD (FFFFHH)
+Size 20: 10,23 OK (HHHFFF)
+Size 21: 11,24 BAD (FFFFHH)
+Size 22: 11,25 OK (HHHFFF)
+Size 23: 12,26 BAD (FFFFHH)
+Size 24: 12,28 OK (HHHFFF)
+Size 25: 13,29 BAD (FFFFHH)
+Size 26: 13,30 OK (HHHFFF)
+Size 27: 14,31 BAD (FFFFHH)
+Size 28: 14,32 OK (HHHFFF)
+Size 29: 15,33 BAD (FFFFHH)
+Size 30: 15,34 OK (HHHFFF)
+Size 31: 16,36 BAD (FFFFHH)
+Size 32: 16,37 OK (HHHFFF)
+Size 33: 17,38 BAD (FFFFHH)
+Size 34: 17,39 OK (HHHFFF)
+Size 35: 18,40 BAD (FFFFHH)
+Size 36: 18,41 OK (HHHFFF)
+Size 37: 19,42 BAD (FFFFHH)
+Size 38: 19,44 OK (HHHFFF)
+Size 39: 20,45 BAD (FFFFHH)
+Size 40: 20,46 OK (HHHFFF)
+Size 41: 21,47 BAD (FFFFHH)
+Size 42: 21,48 OK (HHHFFF)
+Size 43: 22,49 BAD (FFFFHH)
+Size 44: 22,51 OK (HHHFFF)
+Size 45: 23,52 BAD (FFFFHH)
+Size 46: 23,53 OK (HHHFFF)
+Size 47: 24,54 BAD (FFFFHH)
+Size 48: 24,55 OK (HHHFFF)
+Size 49: 25,56 BAD (FFFFHH)
+Size 50: 25,57 OK (HHHFFF)
+Size 51: 26,59 BAD (FFFFHH)
+Size 52: 26,60 OK (HHHFFF)
+Size 53: 27,61 BAD (FFFFHH)
+Size 54: 27,62 OK (HHHFFF)
+Size 55: 28,63 BAD (FFFFHH)
+Size 56: 28,64 OK (HHHFFF)
+Size 57: 29,65 BAD (FFFFHH)
+Size 58: 29,67 OK (HHHFFF)
+Size 59: 30,68 BAD (FFFFHH)
+Size 60: 30,69 OK (HHHFFF)
+Size 61: 31,70 BAD (FFFFHH)
+Size 62: 31,71 OK (HHHFFF)
+Size 63: 32,72 BAD (FFFFHH)
+Size 64: 32,74 OK (HHHFFF)
+Size 65: 33,75 BAD (FFFFHH)
+Size 66: 33,76 OK (HHHFFF)
+Size 67: 34,77 BAD (FFFFHH)
+Size 68: 34,78 OK (HHHFFF)
+Size 69: 35,79 BAD (FFFFHH)
+Size 70: 35,80 OK (HHHFFF)
+Size 71: 36,82 BAD (FFFFHH)
+Size 72: 36,83 OK (HHHFFF)
+Size 73: 37,84 BAD (FFFFHH)
+Size 74: 37,85 OK (HHHFFF)
+Size 75: 38,86 BAD (FFFFHH)
+Size 76: 38,87 OK (HHHFFF)
+Size 77: 39,88 BAD (FFFFHH)
+Size 78: 39,90 OK (HHHFFF)
+Size 79: 40,91 BAD (FFFFHH)
+Size 80: 40,92 OK (HHHFFF)
+Size 81: 41,93 BAD (FFFFHH)
+Size 82: 41,94 OK (HHHFFF)
+Size 83: 42,95 BAD (FFFFHH)
+Size 84: 42,96 OK (HHHFFF)
+Size 85: 43,98 BAD (FFFFHH)
+Size 86: 43,99 OK (HHHFFF)
+Size 87: 44,100 BAD (FFFFHH)
+Size 88: 44,101 OK (HHHFFF)
+Size 89: 45,102 BAD (FFFFHH)
+Size 90: 45,103 OK (HHHFFF)
+Size 91: 46,105 BAD (FFFFHH)
+Size 92: 46,106 OK (HHHFFF)
+Size 93: 47,107 BAD (FFFFHH)
+Size 94: 47,108 OK (HHHFFF)
+Size 95: 48,109 BAD (FFFFHH)
+Size 96: 48,110 OK (HHHFFF)
+Size 97: 49,111 BAD (FFFFHH)
+Size 98: 49,113 OK (HHHFFF)
+Size 99: 50,114 BAD (FFFFHH)
+Size 100: 50,115 OK (HHHFFF)
+
+Windows 8
+---------
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,5 OK (HHHFFF)
+Size 5: 3,6 BAD (FFFFHH)
+Size 6: 3,7 OK (HHHFFF)
+Size 7: 4,8 BAD (FFFFHH)
+Size 8: 4,9 OK (HHHFFF)
+Size 9: 5,10 BAD (FFFFHH)
+Size 10: 5,11 OK (HHHFFF)
+Size 11: 6,13 BAD (FFFFHH)
+Size 12: 6,14 OK (HHHFFF)
+Size 13: 7,15 BAD (FFFFHH)
+Size 14: 7,16 OK (HHHFFF)
+Size 15: 8,17 BAD (FFFFHH)
+Size 16: 8,18 OK (HHHFFF)
+Size 17: 9,20 BAD (FFFFHH)
+Size 18: 9,21 OK (HHHFFF)
+Size 19: 10,22 BAD (FFFFHH)
+Size 20: 10,23 OK (HHHFFF)
+Size 21: 11,24 BAD (FFFFHH)
+Size 22: 11,25 OK (HHHFFF)
+Size 23: 12,26 BAD (FFFFHH)
+Size 24: 12,28 OK (HHHFFF)
+Size 25: 13,29 BAD (FFFFHH)
+Size 26: 13,30 OK (HHHFFF)
+Size 27: 14,31 BAD (FFFFHH)
+Size 28: 14,32 OK (HHHFFF)
+Size 29: 15,33 BAD (FFFFHH)
+Size 30: 15,34 OK (HHHFFF)
+Size 31: 16,36 BAD (FFFFHH)
+Size 32: 16,37 OK (HHHFFF)
+Size 33: 17,38 BAD (FFFFHH)
+Size 34: 17,39 OK (HHHFFF)
+Size 35: 18,40 BAD (FFFFHH)
+Size 36: 18,41 OK (HHHFFF)
+Size 37: 19,42 BAD (FFFFHH)
+Size 38: 19,44 OK (HHHFFF)
+Size 39: 20,45 BAD (FFFFHH)
+Size 40: 20,46 OK (HHHFFF)
+Size 41: 21,47 BAD (FFFFHH)
+Size 42: 21,48 OK (HHHFFF)
+Size 43: 22,49 BAD (FFFFHH)
+Size 44: 22,51 OK (HHHFFF)
+Size 45: 23,52 BAD (FFFFHH)
+Size 46: 23,53 OK (HHHFFF)
+Size 47: 24,54 BAD (FFFFHH)
+Size 48: 24,55 OK (HHHFFF)
+Size 49: 25,56 BAD (FFFFHH)
+Size 50: 25,57 OK (HHHFFF)
+Size 51: 26,59 BAD (FFFFHH)
+Size 52: 26,60 OK (HHHFFF)
+Size 53: 27,61 BAD (FFFFHH)
+Size 54: 27,62 OK (HHHFFF)
+Size 55: 28,63 BAD (FFFFHH)
+Size 56: 28,64 OK (HHHFFF)
+Size 57: 29,65 BAD (FFFFHH)
+Size 58: 29,67 OK (HHHFFF)
+Size 59: 30,68 BAD (FFFFHH)
+Size 60: 30,69 OK (HHHFFF)
+Size 61: 31,70 BAD (FFFFHH)
+Size 62: 31,71 OK (HHHFFF)
+Size 63: 32,72 BAD (FFFFHH)
+Size 64: 32,74 OK (HHHFFF)
+Size 65: 33,75 BAD (FFFFHH)
+Size 66: 33,76 OK (HHHFFF)
+Size 67: 34,77 BAD (FFFFHH)
+Size 68: 34,78 OK (HHHFFF)
+Size 69: 35,79 BAD (FFFFHH)
+Size 70: 35,80 OK (HHHFFF)
+Size 71: 36,82 BAD (FFFFHH)
+Size 72: 36,83 OK (HHHFFF)
+Size 73: 37,84 BAD (FFFFHH)
+Size 74: 37,85 OK (HHHFFF)
+Size 75: 38,86 BAD (FFFFHH)
+Size 76: 38,87 OK (HHHFFF)
+Size 77: 39,88 BAD (FFFFHH)
+Size 78: 39,90 OK (HHHFFF)
+Size 79: 40,91 BAD (FFFFHH)
+Size 80: 40,92 OK (HHHFFF)
+Size 81: 41,93 BAD (FFFFHH)
+Size 82: 41,94 OK (HHHFFF)
+Size 83: 42,95 BAD (FFFFHH)
+Size 84: 42,96 OK (HHHFFF)
+Size 85: 43,98 BAD (FFFFHH)
+Size 86: 43,99 OK (HHHFFF)
+Size 87: 44,100 BAD (FFFFHH)
+Size 88: 44,101 OK (HHHFFF)
+Size 89: 45,102 BAD (FFFFHH)
+Size 90: 45,103 OK (HHHFFF)
+Size 91: 46,105 BAD (FFFFHH)
+Size 92: 46,106 OK (HHHFFF)
+Size 93: 47,107 BAD (FFFFHH)
+Size 94: 47,108 OK (HHHFFF)
+Size 95: 48,109 BAD (FFFFHH)
+Size 96: 48,110 OK (HHHFFF)
+Size 97: 49,111 BAD (FFFFHH)
+Size 98: 49,113 OK (HHHFFF)
+Size 99: 50,114 BAD (FFFFHH)
+Size 100: 50,115 OK (HHHFFF)
+
+Windows 8.1
+-----------
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,5 OK (HHHFFF)
+Size 5: 3,6 BAD (FFFFHH)
+Size 6: 3,7 OK (HHHFFF)
+Size 7: 4,8 BAD (FFFFHH)
+Size 8: 4,9 OK (HHHFFF)
+Size 9: 5,10 BAD (FFFFHH)
+Size 10: 5,11 OK (HHHFFF)
+Size 11: 6,13 BAD (FFFFHH)
+Size 12: 6,14 OK (HHHFFF)
+Size 13: 7,15 BAD (FFFFHH)
+Size 14: 7,16 OK (HHHFFF)
+Size 15: 8,17 BAD (FFFFHH)
+Size 16: 8,18 OK (HHHFFF)
+Size 17: 9,20 BAD (FFFFHH)
+Size 18: 9,21 OK (HHHFFF)
+Size 19: 10,22 BAD (FFFFHH)
+Size 20: 10,23 OK (HHHFFF)
+Size 21: 11,24 BAD (FFFFHH)
+Size 22: 11,25 OK (HHHFFF)
+Size 23: 12,26 BAD (FFFFHH)
+Size 24: 12,28 OK (HHHFFF)
+Size 25: 13,29 BAD (FFFFHH)
+Size 26: 13,30 OK (HHHFFF)
+Size 27: 14,31 BAD (FFFFHH)
+Size 28: 14,32 OK (HHHFFF)
+Size 29: 15,33 BAD (FFFFHH)
+Size 30: 15,34 OK (HHHFFF)
+Size 31: 16,36 BAD (FFFFHH)
+Size 32: 16,37 OK (HHHFFF)
+Size 33: 17,38 BAD (FFFFHH)
+Size 34: 17,39 OK (HHHFFF)
+Size 35: 18,40 BAD (FFFFHH)
+Size 36: 18,41 OK (HHHFFF)
+Size 37: 19,42 BAD (FFFFHH)
+Size 38: 19,44 OK (HHHFFF)
+Size 39: 20,45 BAD (FFFFHH)
+Size 40: 20,46 OK (HHHFFF)
+Size 41: 21,47 BAD (FFFFHH)
+Size 42: 21,48 OK (HHHFFF)
+Size 43: 22,49 BAD (FFFFHH)
+Size 44: 22,51 OK (HHHFFF)
+Size 45: 23,52 BAD (FFFFHH)
+Size 46: 23,53 OK (HHHFFF)
+Size 47: 24,54 BAD (FFFFHH)
+Size 48: 24,55 OK (HHHFFF)
+Size 49: 25,56 BAD (FFFFHH)
+Size 50: 25,57 OK (HHHFFF)
+Size 51: 26,59 BAD (FFFFHH)
+Size 52: 26,60 OK (HHHFFF)
+Size 53: 27,61 BAD (FFFFHH)
+Size 54: 27,62 OK (HHHFFF)
+Size 55: 28,63 BAD (FFFFHH)
+Size 56: 28,64 OK (HHHFFF)
+Size 57: 29,65 BAD (FFFFHH)
+Size 58: 29,67 OK (HHHFFF)
+Size 59: 30,68 BAD (FFFFHH)
+Size 60: 30,69 OK (HHHFFF)
+Size 61: 31,70 BAD (FFFFHH)
+Size 62: 31,71 OK (HHHFFF)
+Size 63: 32,72 BAD (FFFFHH)
+Size 64: 32,74 OK (HHHFFF)
+Size 65: 33,75 BAD (FFFFHH)
+Size 66: 33,76 OK (HHHFFF)
+Size 67: 34,77 BAD (FFFFHH)
+Size 68: 34,78 OK (HHHFFF)
+Size 69: 35,79 BAD (FFFFHH)
+Size 70: 35,80 OK (HHHFFF)
+Size 71: 36,82 BAD (FFFFHH)
+Size 72: 36,83 OK (HHHFFF)
+Size 73: 37,84 BAD (FFFFHH)
+Size 74: 37,85 OK (HHHFFF)
+Size 75: 38,86 BAD (FFFFHH)
+Size 76: 38,87 OK (HHHFFF)
+Size 77: 39,88 BAD (FFFFHH)
+Size 78: 39,90 OK (HHHFFF)
+Size 79: 40,91 BAD (FFFFHH)
+Size 80: 40,92 OK (HHHFFF)
+Size 81: 41,93 BAD (FFFFHH)
+Size 82: 41,94 OK (HHHFFF)
+Size 83: 42,95 BAD (FFFFHH)
+Size 84: 42,96 OK (HHHFFF)
+Size 85: 43,98 BAD (FFFFHH)
+Size 86: 43,99 OK (HHHFFF)
+Size 87: 44,100 BAD (FFFFHH)
+Size 88: 44,101 OK (HHHFFF)
+Size 89: 45,102 BAD (FFFFHH)
+Size 90: 45,103 OK (HHHFFF)
+Size 91: 46,105 BAD (FFFFHH)
+Size 92: 46,106 OK (HHHFFF)
+Size 93: 47,107 BAD (FFFFHH)
+Size 94: 47,108 OK (HHHFFF)
+Size 95: 48,109 BAD (FFFFHH)
+Size 96: 48,110 OK (HHHFFF)
+Size 97: 49,111 BAD (FFFFHH)
+Size 98: 49,113 OK (HHHFFF)
+Size 99: 50,114 BAD (FFFFHH)
+Size 100: 50,115 OK (HHHFFF)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,2 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 BAD (FFFFHH)
+Size 4: 2,5 OK (HHHFFF)
+Size 5: 3,6 BAD (FFFFHH)
+Size 6: 3,7 OK (HHHFFF)
+Size 7: 4,8 BAD (FFFFHH)
+Size 8: 4,9 OK (HHHFFF)
+Size 9: 5,10 BAD (FFFFHH)
+Size 10: 5,11 OK (HHHFFF)
+Size 11: 6,13 BAD (FFFFHH)
+Size 12: 6,14 OK (HHHFFF)
+Size 13: 7,15 BAD (FFFFHH)
+Size 14: 7,16 OK (HHHFFF)
+Size 15: 8,17 BAD (FFFFHH)
+Size 16: 8,18 OK (HHHFFF)
+Size 17: 9,20 BAD (FFFFHH)
+Size 18: 9,21 OK (HHHFFF)
+Size 19: 10,22 BAD (FFFFHH)
+Size 20: 10,23 OK (HHHFFF)
+Size 21: 11,24 BAD (FFFFHH)
+Size 22: 11,25 OK (HHHFFF)
+Size 23: 12,26 BAD (FFFFHH)
+Size 24: 12,28 OK (HHHFFF)
+Size 25: 13,29 BAD (FFFFHH)
+Size 26: 13,30 OK (HHHFFF)
+Size 27: 14,31 BAD (FFFFHH)
+Size 28: 14,32 OK (HHHFFF)
+Size 29: 15,33 BAD (FFFFHH)
+Size 30: 15,34 OK (HHHFFF)
+Size 31: 16,36 BAD (FFFFHH)
+Size 32: 16,37 OK (HHHFFF)
+Size 33: 17,38 BAD (FFFFHH)
+Size 34: 17,39 OK (HHHFFF)
+Size 35: 18,40 BAD (FFFFHH)
+Size 36: 18,41 OK (HHHFFF)
+Size 37: 19,42 BAD (FFFFHH)
+Size 38: 19,44 OK (HHHFFF)
+Size 39: 20,45 BAD (FFFFHH)
+Size 40: 20,46 OK (HHHFFF)
+Size 41: 21,47 BAD (FFFFHH)
+Size 42: 21,48 OK (HHHFFF)
+Size 43: 22,49 BAD (FFFFHH)
+Size 44: 22,51 OK (HHHFFF)
+Size 45: 23,52 BAD (FFFFHH)
+Size 46: 23,53 OK (HHHFFF)
+Size 47: 24,54 BAD (FFFFHH)
+Size 48: 24,55 OK (HHHFFF)
+Size 49: 25,56 BAD (FFFFHH)
+Size 50: 25,57 OK (HHHFFF)
+Size 51: 26,59 BAD (FFFFHH)
+Size 52: 26,60 OK (HHHFFF)
+Size 53: 27,61 BAD (FFFFHH)
+Size 54: 27,62 OK (HHHFFF)
+Size 55: 28,63 BAD (FFFFHH)
+Size 56: 28,64 OK (HHHFFF)
+Size 57: 29,65 BAD (FFFFHH)
+Size 58: 29,67 OK (HHHFFF)
+Size 59: 30,68 BAD (FFFFHH)
+Size 60: 30,69 OK (HHHFFF)
+Size 61: 31,70 BAD (FFFFHH)
+Size 62: 31,71 OK (HHHFFF)
+Size 63: 32,72 BAD (FFFFHH)
+Size 64: 32,74 OK (HHHFFF)
+Size 65: 33,75 BAD (FFFFHH)
+Size 66: 33,76 OK (HHHFFF)
+Size 67: 34,77 BAD (FFFFHH)
+Size 68: 34,78 OK (HHHFFF)
+Size 69: 35,79 BAD (FFFFHH)
+Size 70: 35,80 OK (HHHFFF)
+Size 71: 36,82 BAD (FFFFHH)
+Size 72: 36,83 OK (HHHFFF)
+Size 73: 37,84 BAD (FFFFHH)
+Size 74: 37,85 OK (HHHFFF)
+Size 75: 38,86 BAD (FFFFHH)
+Size 76: 38,87 OK (HHHFFF)
+Size 77: 39,88 BAD (FFFFHH)
+Size 78: 39,90 OK (HHHFFF)
+Size 79: 40,91 BAD (FFFFHH)
+Size 80: 40,92 OK (HHHFFF)
+Size 81: 41,93 BAD (FFFFHH)
+Size 82: 41,94 OK (HHHFFF)
+Size 83: 42,95 BAD (FFFFHH)
+Size 84: 42,96 OK (HHHFFF)
+Size 85: 43,98 BAD (FFFFHH)
+Size 86: 43,99 OK (HHHFFF)
+Size 87: 44,100 BAD (FFFFHH)
+Size 88: 44,101 OK (HHHFFF)
+Size 89: 45,102 BAD (FFFFHH)
+Size 90: 45,103 OK (HHHFFF)
+Size 91: 46,105 BAD (FFFFHH)
+Size 92: 46,106 OK (HHHFFF)
+Size 93: 47,107 BAD (FFFFHH)
+Size 94: 47,108 OK (HHHFFF)
+Size 95: 48,109 BAD (FFFFHH)
+Size 96: 48,110 OK (HHHFFF)
+Size 97: 49,111 BAD (FFFFHH)
+Size 98: 49,113 OK (HHHFFF)
+Size 99: 50,114 BAD (FFFFHH)
+Size 100: 50,115 OK (HHHFFF)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 OK (HHHFFF)
+Size 2: 1,2 OK (HHHFFF)
+Size 3: 2,3 OK (HHHFFF)
+Size 4: 2,4 OK (HHHFFF)
+Size 5: 3,5 OK (HHHFFF)
+Size 6: 3,6 OK (HHHFFF)
+Size 7: 4,7 OK (HHHFFF)
+Size 8: 4,8 OK (HHHFFF)
+Size 9: 5,9 OK (HHHFFF)
+Size 10: 5,10 OK (HHHFFF)
+Size 11: 6,11 OK (HHHFFF)
+Size 12: 6,12 OK (HHHFFF)
+Size 13: 7,13 OK (HHHFFF)
+Size 14: 7,14 OK (HHHFFF)
+Size 15: 8,15 OK (HHHFFF)
+Size 16: 8,16 OK (HHHFFF)
+Size 17: 9,17 OK (HHHFFF)
+Size 18: 9,18 OK (HHHFFF)
+Size 19: 10,19 OK (HHHFFF)
+Size 20: 10,20 OK (HHHFFF)
+Size 21: 11,21 OK (HHHFFF)
+Size 22: 11,22 OK (HHHFFF)
+Size 23: 12,23 OK (HHHFFF)
+Size 24: 12,24 OK (HHHFFF)
+Size 25: 13,25 OK (HHHFFF)
+Size 26: 13,26 OK (HHHFFF)
+Size 27: 14,27 OK (HHHFFF)
+Size 28: 14,28 OK (HHHFFF)
+Size 29: 15,29 OK (HHHFFF)
+Size 30: 15,30 OK (HHHFFF)
+Size 31: 16,31 OK (HHHFFF)
+Size 32: 16,32 OK (HHHFFF)
+Size 33: 17,33 OK (HHHFFF)
+Size 34: 17,34 OK (HHHFFF)
+Size 35: 18,35 OK (HHHFFF)
+Size 36: 18,36 OK (HHHFFF)
+Size 37: 19,37 OK (HHHFFF)
+Size 38: 19,38 OK (HHHFFF)
+Size 39: 20,39 OK (HHHFFF)
+Size 40: 20,40 OK (HHHFFF)
+Size 41: 21,41 OK (HHHFFF)
+Size 42: 21,42 OK (HHHFFF)
+Size 43: 22,43 OK (HHHFFF)
+Size 44: 22,44 OK (HHHFFF)
+Size 45: 23,45 OK (HHHFFF)
+Size 46: 23,46 OK (HHHFFF)
+Size 47: 24,47 OK (HHHFFF)
+Size 48: 24,48 OK (HHHFFF)
+Size 49: 25,49 OK (HHHFFF)
+Size 50: 25,50 OK (HHHFFF)
+Size 51: 26,51 OK (HHHFFF)
+Size 52: 26,52 OK (HHHFFF)
+Size 53: 27,53 OK (HHHFFF)
+Size 54: 27,54 OK (HHHFFF)
+Size 55: 28,55 OK (HHHFFF)
+Size 56: 28,56 OK (HHHFFF)
+Size 57: 29,57 OK (HHHFFF)
+Size 58: 29,58 OK (HHHFFF)
+Size 59: 30,59 OK (HHHFFF)
+Size 60: 30,60 OK (HHHFFF)
+Size 61: 31,61 OK (HHHFFF)
+Size 62: 31,62 OK (HHHFFF)
+Size 63: 32,63 OK (HHHFFF)
+Size 64: 32,64 OK (HHHFFF)
+Size 65: 33,65 OK (HHHFFF)
+Size 66: 33,66 OK (HHHFFF)
+Size 67: 34,67 OK (HHHFFF)
+Size 68: 34,68 OK (HHHFFF)
+Size 69: 35,69 OK (HHHFFF)
+Size 70: 35,70 OK (HHHFFF)
+Size 71: 36,71 OK (HHHFFF)
+Size 72: 36,72 OK (HHHFFF)
+Size 73: 37,73 OK (HHHFFF)
+Size 74: 37,74 OK (HHHFFF)
+Size 75: 38,75 OK (HHHFFF)
+Size 76: 38,76 OK (HHHFFF)
+Size 77: 39,77 OK (HHHFFF)
+Size 78: 39,78 OK (HHHFFF)
+Size 79: 40,79 OK (HHHFFF)
+Size 80: 40,80 OK (HHHFFF)
+Size 81: 41,81 OK (HHHFFF)
+Size 82: 41,82 OK (HHHFFF)
+Size 83: 42,83 OK (HHHFFF)
+Size 84: 42,84 OK (HHHFFF)
+Size 85: 43,85 OK (HHHFFF)
+Size 86: 43,86 OK (HHHFFF)
+Size 87: 44,87 OK (HHHFFF)
+Size 88: 44,88 OK (HHHFFF)
+Size 89: 45,89 OK (HHHFFF)
+Size 90: 45,90 OK (HHHFFF)
+Size 91: 46,91 OK (HHHFFF)
+Size 92: 46,92 OK (HHHFFF)
+Size 93: 47,93 OK (HHHFFF)
+Size 94: 47,94 OK (HHHFFF)
+Size 95: 48,95 OK (HHHFFF)
+Size 96: 48,96 OK (HHHFFF)
+Size 97: 49,97 OK (HHHFFF)
+Size 98: 49,98 OK (HHHFFF)
+Size 99: 50,99 OK (HHHFFF)
+Size 100: 50,100 OK (HHHFFF)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP950.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP950.txt
new file mode 100644
index 0000000000..0dbade504d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/CP950.txt
@@ -0,0 +1,630 @@
+===========================================================
+Code Page 950, Chinese Traditional (Taiwan), MingLight font
+===========================================================
+
+Options: -face-minglight -family 0x36
+Chars: A2 A3 2014 3044 30FC 4000
+
+Vista
+-----
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,4 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (HHHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (HHHFHH)
+Size 8: 4,10 GOOD (HHFFFF)
+Size 9: 5,11 BAD (HHHFHH)
+Size 10: 5,12 GOOD (HHFFFF)
+Size 11: 6,13 BAD (HHHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,16 BAD (HHHFHH)
+Size 14: 7,17 GOOD (HHFFFF)
+Size 15: 8,18 BAD (HHHFHH)
+Size 16: 8,19 GOOD (HHFFFF)
+Size 17: 9,20 BAD (HHHFHH)
+Size 18: 9,22 GOOD (HHFFFF)
+Size 19: 10,23 BAD (HHHFHH)
+Size 20: 10,24 GOOD (HHFFFF)
+Size 21: 11,25 BAD (HHHFHH)
+Size 22: 11,26 GOOD (HHFFFF)
+Size 23: 12,28 BAD (HHHFHH)
+Size 24: 12,29 GOOD (HHFFFF)
+Size 25: 13,30 BAD (HHHFHH)
+Size 26: 13,31 GOOD (HHFFFF)
+Size 27: 14,32 BAD (HHHFHH)
+Size 28: 14,34 GOOD (HHFFFF)
+Size 29: 15,35 BAD (HHHFHH)
+Size 30: 15,36 GOOD (HHFFFF)
+Size 31: 16,37 BAD (HHHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,40 BAD (HHHFHH)
+Size 34: 17,41 GOOD (HHFFFF)
+Size 35: 18,42 BAD (HHHFHH)
+Size 36: 18,43 GOOD (HHFFFF)
+Size 37: 19,44 BAD (HHHFHH)
+Size 38: 19,46 GOOD (HHFFFF)
+Size 39: 20,47 BAD (HHHFHH)
+Size 40: 20,48 GOOD (HHFFFF)
+Size 41: 21,49 BAD (HHHFHH)
+Size 42: 21,50 GOOD (HHFFFF)
+Size 43: 22,52 BAD (HHHFHH)
+Size 44: 22,53 GOOD (HHFFFF)
+Size 45: 23,54 BAD (HHHFHH)
+Size 46: 23,55 GOOD (HHFFFF)
+Size 47: 24,56 BAD (HHHFHH)
+Size 48: 24,58 GOOD (HHFFFF)
+Size 49: 25,59 BAD (HHHFHH)
+Size 50: 25,60 GOOD (HHFFFF)
+Size 51: 26,61 BAD (HHHFHH)
+Size 52: 26,62 GOOD (HHFFFF)
+Size 53: 27,64 BAD (HHHFHH)
+Size 54: 27,65 GOOD (HHFFFF)
+Size 55: 28,66 BAD (HHHFHH)
+Size 56: 28,67 GOOD (HHFFFF)
+Size 57: 29,68 BAD (HHHFHH)
+Size 58: 29,70 GOOD (HHFFFF)
+Size 59: 30,71 BAD (HHHFHH)
+Size 60: 30,72 GOOD (HHFFFF)
+Size 61: 31,73 BAD (HHHFHH)
+Size 62: 31,74 GOOD (HHFFFF)
+Size 63: 32,76 BAD (HHHFHH)
+Size 64: 32,77 GOOD (HHFFFF)
+Size 65: 33,78 BAD (HHHFHH)
+Size 66: 33,79 GOOD (HHFFFF)
+Size 67: 34,80 BAD (HHHFHH)
+Size 68: 34,82 GOOD (HHFFFF)
+Size 69: 35,83 BAD (HHHFHH)
+Size 70: 35,84 GOOD (HHFFFF)
+Size 71: 36,85 BAD (HHHFHH)
+Size 72: 36,86 GOOD (HHFFFF)
+Size 73: 37,88 BAD (HHHFHH)
+Size 74: 37,89 GOOD (HHFFFF)
+Size 75: 38,90 BAD (HHHFHH)
+Size 76: 38,91 GOOD (HHFFFF)
+Size 77: 39,92 BAD (HHHFHH)
+Size 78: 39,94 GOOD (HHFFFF)
+Size 79: 40,95 BAD (HHHFHH)
+Size 80: 40,96 GOOD (HHFFFF)
+Size 81: 41,97 BAD (HHHFHH)
+Size 82: 41,98 GOOD (HHFFFF)
+Size 83: 42,100 BAD (HHHFHH)
+Size 84: 42,101 GOOD (HHFFFF)
+Size 85: 43,102 BAD (HHHFHH)
+Size 86: 43,103 GOOD (HHFFFF)
+Size 87: 44,104 BAD (HHHFHH)
+Size 88: 44,106 GOOD (HHFFFF)
+Size 89: 45,107 BAD (HHHFHH)
+Size 90: 45,108 GOOD (HHFFFF)
+Size 91: 46,109 BAD (HHHFHH)
+Size 92: 46,110 GOOD (HHFFFF)
+Size 93: 47,112 BAD (HHHFHH)
+Size 94: 47,113 GOOD (HHFFFF)
+Size 95: 48,114 BAD (HHHFHH)
+Size 96: 48,115 GOOD (HHFFFF)
+Size 97: 49,116 BAD (HHHFHH)
+Size 98: 49,118 GOOD (HHFFFF)
+Size 99: 50,119 BAD (HHHFHH)
+Size 100: 50,120 GOOD (HHFFFF)
+
+Windows 7
+---------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,4 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,10 GOOD (HHFFFF)
+Size 9: 5,11 BAD (FFHFHH)
+Size 10: 5,12 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,16 BAD (FFHFHH)
+Size 14: 7,17 GOOD (HHFFFF)
+Size 15: 8,18 BAD (FFHFHH)
+Size 16: 8,19 GOOD (HHFFFF)
+Size 17: 9,20 BAD (FFHFHH)
+Size 18: 9,22 GOOD (HHFFFF)
+Size 19: 10,23 BAD (FFHFHH)
+Size 20: 10,24 GOOD (HHFFFF)
+Size 21: 11,25 BAD (FFHFHH)
+Size 22: 11,26 GOOD (HHFFFF)
+Size 23: 12,28 BAD (FFHFHH)
+Size 24: 12,29 GOOD (HHFFFF)
+Size 25: 13,30 BAD (FFHFHH)
+Size 26: 13,31 GOOD (HHFFFF)
+Size 27: 14,32 BAD (FFHFHH)
+Size 28: 14,34 GOOD (HHFFFF)
+Size 29: 15,35 BAD (FFHFHH)
+Size 30: 15,36 GOOD (HHFFFF)
+Size 31: 16,37 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,40 BAD (FFHFHH)
+Size 34: 17,41 GOOD (HHFFFF)
+Size 35: 18,42 BAD (FFHFHH)
+Size 36: 18,43 GOOD (HHFFFF)
+Size 37: 19,44 BAD (FFHFHH)
+Size 38: 19,46 GOOD (HHFFFF)
+Size 39: 20,47 BAD (FFHFHH)
+Size 40: 20,48 GOOD (HHFFFF)
+Size 41: 21,49 BAD (FFHFHH)
+Size 42: 21,50 GOOD (HHFFFF)
+Size 43: 22,52 BAD (FFHFHH)
+Size 44: 22,53 GOOD (HHFFFF)
+Size 45: 23,54 BAD (FFHFHH)
+Size 46: 23,55 GOOD (HHFFFF)
+Size 47: 24,56 BAD (FFHFHH)
+Size 48: 24,58 GOOD (HHFFFF)
+Size 49: 25,59 BAD (FFHFHH)
+Size 50: 25,60 GOOD (HHFFFF)
+Size 51: 26,61 BAD (FFHFHH)
+Size 52: 26,62 GOOD (HHFFFF)
+Size 53: 27,64 BAD (FFHFHH)
+Size 54: 27,65 GOOD (HHFFFF)
+Size 55: 28,66 BAD (FFHFHH)
+Size 56: 28,67 GOOD (HHFFFF)
+Size 57: 29,68 BAD (FFHFHH)
+Size 58: 29,70 GOOD (HHFFFF)
+Size 59: 30,71 BAD (FFHFHH)
+Size 60: 30,72 GOOD (HHFFFF)
+Size 61: 31,73 BAD (FFHFHH)
+Size 62: 31,74 GOOD (HHFFFF)
+Size 63: 32,76 BAD (FFHFHH)
+Size 64: 32,77 GOOD (HHFFFF)
+Size 65: 33,78 BAD (FFHFHH)
+Size 66: 33,79 GOOD (HHFFFF)
+Size 67: 34,80 BAD (FFHFHH)
+Size 68: 34,82 GOOD (HHFFFF)
+Size 69: 35,83 BAD (FFHFHH)
+Size 70: 35,84 GOOD (HHFFFF)
+Size 71: 36,85 BAD (FFHFHH)
+Size 72: 36,86 GOOD (HHFFFF)
+Size 73: 37,88 BAD (FFHFHH)
+Size 74: 37,89 GOOD (HHFFFF)
+Size 75: 38,90 BAD (FFHFHH)
+Size 76: 38,91 GOOD (HHFFFF)
+Size 77: 39,92 BAD (FFHFHH)
+Size 78: 39,94 GOOD (HHFFFF)
+Size 79: 40,95 BAD (FFHFHH)
+Size 80: 40,96 GOOD (HHFFFF)
+Size 81: 41,97 BAD (FFHFHH)
+Size 82: 41,98 GOOD (HHFFFF)
+Size 83: 42,100 BAD (FFHFHH)
+Size 84: 42,101 GOOD (HHFFFF)
+Size 85: 43,102 BAD (FFHFHH)
+Size 86: 43,103 GOOD (HHFFFF)
+Size 87: 44,104 BAD (FFHFHH)
+Size 88: 44,106 GOOD (HHFFFF)
+Size 89: 45,107 BAD (FFHFHH)
+Size 90: 45,108 GOOD (HHFFFF)
+Size 91: 46,109 BAD (FFHFHH)
+Size 92: 46,110 GOOD (HHFFFF)
+Size 93: 47,112 BAD (FFHFHH)
+Size 94: 47,113 GOOD (HHFFFF)
+Size 95: 48,114 BAD (FFHFHH)
+Size 96: 48,115 GOOD (HHFFFF)
+Size 97: 49,116 BAD (FFHFHH)
+Size 98: 49,118 GOOD (HHFFFF)
+Size 99: 50,119 BAD (FFHFHH)
+Size 100: 50,120 GOOD (HHFFFF)
+
+Windows 8
+---------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,4 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,10 GOOD (HHFFFF)
+Size 9: 5,11 BAD (FFHFHH)
+Size 10: 5,12 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,16 BAD (FFHFHH)
+Size 14: 7,17 GOOD (HHFFFF)
+Size 15: 8,18 BAD (FFHFHH)
+Size 16: 8,19 GOOD (HHFFFF)
+Size 17: 9,20 BAD (FFHFHH)
+Size 18: 9,22 GOOD (HHFFFF)
+Size 19: 10,23 BAD (FFHFHH)
+Size 20: 10,24 GOOD (HHFFFF)
+Size 21: 11,25 BAD (FFHFHH)
+Size 22: 11,26 GOOD (HHFFFF)
+Size 23: 12,28 BAD (FFHFHH)
+Size 24: 12,29 GOOD (HHFFFF)
+Size 25: 13,30 BAD (FFHFHH)
+Size 26: 13,31 GOOD (HHFFFF)
+Size 27: 14,32 BAD (FFHFHH)
+Size 28: 14,34 GOOD (HHFFFF)
+Size 29: 15,35 BAD (FFHFHH)
+Size 30: 15,36 GOOD (HHFFFF)
+Size 31: 16,37 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,40 BAD (FFHFHH)
+Size 34: 17,41 GOOD (HHFFFF)
+Size 35: 18,42 BAD (FFHFHH)
+Size 36: 18,43 GOOD (HHFFFF)
+Size 37: 19,44 BAD (FFHFHH)
+Size 38: 19,46 GOOD (HHFFFF)
+Size 39: 20,47 BAD (FFHFHH)
+Size 40: 20,48 GOOD (HHFFFF)
+Size 41: 21,49 BAD (FFHFHH)
+Size 42: 21,50 GOOD (HHFFFF)
+Size 43: 22,52 BAD (FFHFHH)
+Size 44: 22,53 GOOD (HHFFFF)
+Size 45: 23,54 BAD (FFHFHH)
+Size 46: 23,55 GOOD (HHFFFF)
+Size 47: 24,56 BAD (FFHFHH)
+Size 48: 24,58 GOOD (HHFFFF)
+Size 49: 25,59 BAD (FFHFHH)
+Size 50: 25,60 GOOD (HHFFFF)
+Size 51: 26,61 BAD (FFHFHH)
+Size 52: 26,62 GOOD (HHFFFF)
+Size 53: 27,64 BAD (FFHFHH)
+Size 54: 27,65 GOOD (HHFFFF)
+Size 55: 28,66 BAD (FFHFHH)
+Size 56: 28,67 GOOD (HHFFFF)
+Size 57: 29,68 BAD (FFHFHH)
+Size 58: 29,70 GOOD (HHFFFF)
+Size 59: 30,71 BAD (FFHFHH)
+Size 60: 30,72 GOOD (HHFFFF)
+Size 61: 31,73 BAD (FFHFHH)
+Size 62: 31,74 GOOD (HHFFFF)
+Size 63: 32,76 BAD (FFHFHH)
+Size 64: 32,77 GOOD (HHFFFF)
+Size 65: 33,78 BAD (FFHFHH)
+Size 66: 33,79 GOOD (HHFFFF)
+Size 67: 34,80 BAD (FFHFHH)
+Size 68: 34,82 GOOD (HHFFFF)
+Size 69: 35,83 BAD (FFHFHH)
+Size 70: 35,84 GOOD (HHFFFF)
+Size 71: 36,85 BAD (FFHFHH)
+Size 72: 36,86 GOOD (HHFFFF)
+Size 73: 37,88 BAD (FFHFHH)
+Size 74: 37,89 GOOD (HHFFFF)
+Size 75: 38,90 BAD (FFHFHH)
+Size 76: 38,91 GOOD (HHFFFF)
+Size 77: 39,92 BAD (FFHFHH)
+Size 78: 39,94 GOOD (HHFFFF)
+Size 79: 40,95 BAD (FFHFHH)
+Size 80: 40,96 GOOD (HHFFFF)
+Size 81: 41,97 BAD (FFHFHH)
+Size 82: 41,98 GOOD (HHFFFF)
+Size 83: 42,100 BAD (FFHFHH)
+Size 84: 42,101 GOOD (HHFFFF)
+Size 85: 43,102 BAD (FFHFHH)
+Size 86: 43,103 GOOD (HHFFFF)
+Size 87: 44,104 BAD (FFHFHH)
+Size 88: 44,106 GOOD (HHFFFF)
+Size 89: 45,107 BAD (FFHFHH)
+Size 90: 45,108 GOOD (HHFFFF)
+Size 91: 46,109 BAD (FFHFHH)
+Size 92: 46,110 GOOD (HHFFFF)
+Size 93: 47,112 BAD (FFHFHH)
+Size 94: 47,113 GOOD (HHFFFF)
+Size 95: 48,114 BAD (FFHFHH)
+Size 96: 48,115 GOOD (HHFFFF)
+Size 97: 49,116 BAD (FFHFHH)
+Size 98: 49,118 GOOD (HHFFFF)
+Size 99: 50,119 BAD (FFHFHH)
+Size 100: 50,120 GOOD (HHFFFF)
+
+Windows 8.1
+-----------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,4 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,10 GOOD (HHFFFF)
+Size 9: 5,11 BAD (FFHFHH)
+Size 10: 5,12 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,16 BAD (FFHFHH)
+Size 14: 7,17 GOOD (HHFFFF)
+Size 15: 8,18 BAD (FFHFHH)
+Size 16: 8,19 GOOD (HHFFFF)
+Size 17: 9,20 BAD (FFHFHH)
+Size 18: 9,22 GOOD (HHFFFF)
+Size 19: 10,23 BAD (FFHFHH)
+Size 20: 10,24 GOOD (HHFFFF)
+Size 21: 11,25 BAD (FFHFHH)
+Size 22: 11,26 GOOD (HHFFFF)
+Size 23: 12,28 BAD (FFHFHH)
+Size 24: 12,29 GOOD (HHFFFF)
+Size 25: 13,30 BAD (FFHFHH)
+Size 26: 13,31 GOOD (HHFFFF)
+Size 27: 14,32 BAD (FFHFHH)
+Size 28: 14,34 GOOD (HHFFFF)
+Size 29: 15,35 BAD (FFHFHH)
+Size 30: 15,36 GOOD (HHFFFF)
+Size 31: 16,37 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,40 BAD (FFHFHH)
+Size 34: 17,41 GOOD (HHFFFF)
+Size 35: 18,42 BAD (FFHFHH)
+Size 36: 18,43 GOOD (HHFFFF)
+Size 37: 19,44 BAD (FFHFHH)
+Size 38: 19,46 GOOD (HHFFFF)
+Size 39: 20,47 BAD (FFHFHH)
+Size 40: 20,48 GOOD (HHFFFF)
+Size 41: 21,49 BAD (FFHFHH)
+Size 42: 21,50 GOOD (HHFFFF)
+Size 43: 22,52 BAD (FFHFHH)
+Size 44: 22,53 GOOD (HHFFFF)
+Size 45: 23,54 BAD (FFHFHH)
+Size 46: 23,55 GOOD (HHFFFF)
+Size 47: 24,56 BAD (FFHFHH)
+Size 48: 24,58 GOOD (HHFFFF)
+Size 49: 25,59 BAD (FFHFHH)
+Size 50: 25,60 GOOD (HHFFFF)
+Size 51: 26,61 BAD (FFHFHH)
+Size 52: 26,62 GOOD (HHFFFF)
+Size 53: 27,64 BAD (FFHFHH)
+Size 54: 27,65 GOOD (HHFFFF)
+Size 55: 28,66 BAD (FFHFHH)
+Size 56: 28,67 GOOD (HHFFFF)
+Size 57: 29,68 BAD (FFHFHH)
+Size 58: 29,70 GOOD (HHFFFF)
+Size 59: 30,71 BAD (FFHFHH)
+Size 60: 30,72 GOOD (HHFFFF)
+Size 61: 31,73 BAD (FFHFHH)
+Size 62: 31,74 GOOD (HHFFFF)
+Size 63: 32,76 BAD (FFHFHH)
+Size 64: 32,77 GOOD (HHFFFF)
+Size 65: 33,78 BAD (FFHFHH)
+Size 66: 33,79 GOOD (HHFFFF)
+Size 67: 34,80 BAD (FFHFHH)
+Size 68: 34,82 GOOD (HHFFFF)
+Size 69: 35,83 BAD (FFHFHH)
+Size 70: 35,84 GOOD (HHFFFF)
+Size 71: 36,85 BAD (FFHFHH)
+Size 72: 36,86 GOOD (HHFFFF)
+Size 73: 37,88 BAD (FFHFHH)
+Size 74: 37,89 GOOD (HHFFFF)
+Size 75: 38,90 BAD (FFHFHH)
+Size 76: 38,91 GOOD (HHFFFF)
+Size 77: 39,92 BAD (FFHFHH)
+Size 78: 39,94 GOOD (HHFFFF)
+Size 79: 40,95 BAD (FFHFHH)
+Size 80: 40,96 GOOD (HHFFFF)
+Size 81: 41,97 BAD (FFHFHH)
+Size 82: 41,98 GOOD (HHFFFF)
+Size 83: 42,100 BAD (FFHFHH)
+Size 84: 42,101 GOOD (HHFFFF)
+Size 85: 43,102 BAD (FFHFHH)
+Size 86: 43,103 GOOD (HHFFFF)
+Size 87: 44,104 BAD (FFHFHH)
+Size 88: 44,106 GOOD (HHFFFF)
+Size 89: 45,107 BAD (FFHFHH)
+Size 90: 45,108 GOOD (HHFFFF)
+Size 91: 46,109 BAD (FFHFHH)
+Size 92: 46,110 GOOD (HHFFFF)
+Size 93: 47,112 BAD (FFHFHH)
+Size 94: 47,113 GOOD (HHFFFF)
+Size 95: 48,114 BAD (FFHFHH)
+Size 96: 48,115 GOOD (HHFFFF)
+Size 97: 49,116 BAD (FFHFHH)
+Size 98: 49,118 GOOD (HHFFFF)
+Size 99: 50,119 BAD (FFHFHH)
+Size 100: 50,120 GOOD (HHFFFF)
+
+Windows 10 14342 Old Console
+----------------------------
+
+Size 1: 1,2 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,4 BAD (FFHFHH)
+Size 4: 2,5 GOOD (HHFFFF)
+Size 5: 3,6 BAD (FFHFHH)
+Size 6: 3,7 GOOD (HHFFFF)
+Size 7: 4,8 BAD (FFHFHH)
+Size 8: 4,10 GOOD (HHFFFF)
+Size 9: 5,11 BAD (FFHFHH)
+Size 10: 5,12 GOOD (HHFFFF)
+Size 11: 6,13 BAD (FFHFHH)
+Size 12: 6,14 GOOD (HHFFFF)
+Size 13: 7,16 BAD (FFHFHH)
+Size 14: 7,17 GOOD (HHFFFF)
+Size 15: 8,18 BAD (FFHFHH)
+Size 16: 8,19 GOOD (HHFFFF)
+Size 17: 9,20 BAD (FFHFHH)
+Size 18: 9,22 GOOD (HHFFFF)
+Size 19: 10,23 BAD (FFHFHH)
+Size 20: 10,24 GOOD (HHFFFF)
+Size 21: 11,25 BAD (FFHFHH)
+Size 22: 11,26 GOOD (HHFFFF)
+Size 23: 12,28 BAD (FFHFHH)
+Size 24: 12,29 GOOD (HHFFFF)
+Size 25: 13,30 BAD (FFHFHH)
+Size 26: 13,31 GOOD (HHFFFF)
+Size 27: 14,32 BAD (FFHFHH)
+Size 28: 14,34 GOOD (HHFFFF)
+Size 29: 15,35 BAD (FFHFHH)
+Size 30: 15,36 GOOD (HHFFFF)
+Size 31: 16,37 BAD (FFHFHH)
+Size 32: 16,38 GOOD (HHFFFF)
+Size 33: 17,40 BAD (FFHFHH)
+Size 34: 17,41 GOOD (HHFFFF)
+Size 35: 18,42 BAD (FFHFHH)
+Size 36: 18,43 GOOD (HHFFFF)
+Size 37: 19,44 BAD (FFHFHH)
+Size 38: 19,46 GOOD (HHFFFF)
+Size 39: 20,47 BAD (FFHFHH)
+Size 40: 20,48 GOOD (HHFFFF)
+Size 41: 21,49 BAD (FFHFHH)
+Size 42: 21,50 GOOD (HHFFFF)
+Size 43: 22,52 BAD (FFHFHH)
+Size 44: 22,53 GOOD (HHFFFF)
+Size 45: 23,54 BAD (FFHFHH)
+Size 46: 23,55 GOOD (HHFFFF)
+Size 47: 24,56 BAD (FFHFHH)
+Size 48: 24,58 GOOD (HHFFFF)
+Size 49: 25,59 BAD (FFHFHH)
+Size 50: 25,60 GOOD (HHFFFF)
+Size 51: 26,61 BAD (FFHFHH)
+Size 52: 26,62 GOOD (HHFFFF)
+Size 53: 27,64 BAD (FFHFHH)
+Size 54: 27,65 GOOD (HHFFFF)
+Size 55: 28,66 BAD (FFHFHH)
+Size 56: 28,67 GOOD (HHFFFF)
+Size 57: 29,68 BAD (FFHFHH)
+Size 58: 29,70 GOOD (HHFFFF)
+Size 59: 30,71 BAD (FFHFHH)
+Size 60: 30,72 GOOD (HHFFFF)
+Size 61: 31,73 BAD (FFHFHH)
+Size 62: 31,74 GOOD (HHFFFF)
+Size 63: 32,76 BAD (FFHFHH)
+Size 64: 32,77 GOOD (HHFFFF)
+Size 65: 33,78 BAD (FFHFHH)
+Size 66: 33,79 GOOD (HHFFFF)
+Size 67: 34,80 BAD (FFHFHH)
+Size 68: 34,82 GOOD (HHFFFF)
+Size 69: 35,83 BAD (FFHFHH)
+Size 70: 35,84 GOOD (HHFFFF)
+Size 71: 36,85 BAD (FFHFHH)
+Size 72: 36,86 GOOD (HHFFFF)
+Size 73: 37,88 BAD (FFHFHH)
+Size 74: 37,89 GOOD (HHFFFF)
+Size 75: 38,90 BAD (FFHFHH)
+Size 76: 38,91 GOOD (HHFFFF)
+Size 77: 39,92 BAD (FFHFHH)
+Size 78: 39,94 GOOD (HHFFFF)
+Size 79: 40,95 BAD (FFHFHH)
+Size 80: 40,96 GOOD (HHFFFF)
+Size 81: 41,97 BAD (FFHFHH)
+Size 82: 41,98 GOOD (HHFFFF)
+Size 83: 42,100 BAD (FFHFHH)
+Size 84: 42,101 GOOD (HHFFFF)
+Size 85: 43,102 BAD (FFHFHH)
+Size 86: 43,103 GOOD (HHFFFF)
+Size 87: 44,104 BAD (FFHFHH)
+Size 88: 44,106 GOOD (HHFFFF)
+Size 89: 45,107 BAD (FFHFHH)
+Size 90: 45,108 GOOD (HHFFFF)
+Size 91: 46,109 BAD (FFHFHH)
+Size 92: 46,110 GOOD (HHFFFF)
+Size 93: 47,112 BAD (FFHFHH)
+Size 94: 47,113 GOOD (HHFFFF)
+Size 95: 48,114 BAD (FFHFHH)
+Size 96: 48,115 GOOD (HHFFFF)
+Size 97: 49,116 BAD (FFHFHH)
+Size 98: 49,118 GOOD (HHFFFF)
+Size 99: 50,119 BAD (FFHFHH)
+Size 100: 50,120 GOOD (HHFFFF)
+
+Windows 10 14342 New Console
+----------------------------
+
+Size 1: 1,1 GOOD (HHFFFF)
+Size 2: 1,2 GOOD (HHFFFF)
+Size 3: 2,3 GOOD (HHFFFF)
+Size 4: 2,4 GOOD (HHFFFF)
+Size 5: 3,5 GOOD (HHFFFF)
+Size 6: 3,6 GOOD (HHFFFF)
+Size 7: 4,7 GOOD (HHFFFF)
+Size 8: 4,8 GOOD (HHFFFF)
+Size 9: 5,9 GOOD (HHFFFF)
+Size 10: 5,10 GOOD (HHFFFF)
+Size 11: 6,11 GOOD (HHFFFF)
+Size 12: 6,12 GOOD (HHFFFF)
+Size 13: 7,13 GOOD (HHFFFF)
+Size 14: 7,14 GOOD (HHFFFF)
+Size 15: 8,15 GOOD (HHFFFF)
+Size 16: 8,16 GOOD (HHFFFF)
+Size 17: 9,17 GOOD (HHFFFF)
+Size 18: 9,18 GOOD (HHFFFF)
+Size 19: 10,19 GOOD (HHFFFF)
+Size 20: 10,20 GOOD (HHFFFF)
+Size 21: 11,21 GOOD (HHFFFF)
+Size 22: 11,22 GOOD (HHFFFF)
+Size 23: 12,23 GOOD (HHFFFF)
+Size 24: 12,24 GOOD (HHFFFF)
+Size 25: 13,25 GOOD (HHFFFF)
+Size 26: 13,26 GOOD (HHFFFF)
+Size 27: 14,27 GOOD (HHFFFF)
+Size 28: 14,28 GOOD (HHFFFF)
+Size 29: 15,29 GOOD (HHFFFF)
+Size 30: 15,30 GOOD (HHFFFF)
+Size 31: 16,31 GOOD (HHFFFF)
+Size 32: 16,32 GOOD (HHFFFF)
+Size 33: 17,33 GOOD (HHFFFF)
+Size 34: 17,34 GOOD (HHFFFF)
+Size 35: 18,35 GOOD (HHFFFF)
+Size 36: 18,36 GOOD (HHFFFF)
+Size 37: 19,37 GOOD (HHFFFF)
+Size 38: 19,38 GOOD (HHFFFF)
+Size 39: 20,39 GOOD (HHFFFF)
+Size 40: 20,40 GOOD (HHFFFF)
+Size 41: 21,41 GOOD (HHFFFF)
+Size 42: 21,42 GOOD (HHFFFF)
+Size 43: 22,43 GOOD (HHFFFF)
+Size 44: 22,44 GOOD (HHFFFF)
+Size 45: 23,45 GOOD (HHFFFF)
+Size 46: 23,46 GOOD (HHFFFF)
+Size 47: 24,47 GOOD (HHFFFF)
+Size 48: 24,48 GOOD (HHFFFF)
+Size 49: 25,49 GOOD (HHFFFF)
+Size 50: 25,50 GOOD (HHFFFF)
+Size 51: 26,51 GOOD (HHFFFF)
+Size 52: 26,52 GOOD (HHFFFF)
+Size 53: 27,53 GOOD (HHFFFF)
+Size 54: 27,54 GOOD (HHFFFF)
+Size 55: 28,55 GOOD (HHFFFF)
+Size 56: 28,56 GOOD (HHFFFF)
+Size 57: 29,57 GOOD (HHFFFF)
+Size 58: 29,58 GOOD (HHFFFF)
+Size 59: 30,59 GOOD (HHFFFF)
+Size 60: 30,60 GOOD (HHFFFF)
+Size 61: 31,61 GOOD (HHFFFF)
+Size 62: 31,62 GOOD (HHFFFF)
+Size 63: 32,63 GOOD (HHFFFF)
+Size 64: 32,64 GOOD (HHFFFF)
+Size 65: 33,65 GOOD (HHFFFF)
+Size 66: 33,66 GOOD (HHFFFF)
+Size 67: 34,67 GOOD (HHFFFF)
+Size 68: 34,68 GOOD (HHFFFF)
+Size 69: 35,69 GOOD (HHFFFF)
+Size 70: 35,70 GOOD (HHFFFF)
+Size 71: 36,71 GOOD (HHFFFF)
+Size 72: 36,72 GOOD (HHFFFF)
+Size 73: 37,73 GOOD (HHFFFF)
+Size 74: 37,74 GOOD (HHFFFF)
+Size 75: 38,75 GOOD (HHFFFF)
+Size 76: 38,76 GOOD (HHFFFF)
+Size 77: 39,77 GOOD (HHFFFF)
+Size 78: 39,78 GOOD (HHFFFF)
+Size 79: 40,79 GOOD (HHFFFF)
+Size 80: 40,80 GOOD (HHFFFF)
+Size 81: 41,81 GOOD (HHFFFF)
+Size 82: 41,82 GOOD (HHFFFF)
+Size 83: 42,83 GOOD (HHFFFF)
+Size 84: 42,84 GOOD (HHFFFF)
+Size 85: 43,85 GOOD (HHFFFF)
+Size 86: 43,86 GOOD (HHFFFF)
+Size 87: 44,87 GOOD (HHFFFF)
+Size 88: 44,88 GOOD (HHFFFF)
+Size 89: 45,89 GOOD (HHFFFF)
+Size 90: 45,90 GOOD (HHFFFF)
+Size 91: 46,91 GOOD (HHFFFF)
+Size 92: 46,92 GOOD (HHFFFF)
+Size 93: 47,93 GOOD (HHFFFF)
+Size 94: 47,94 GOOD (HHFFFF)
+Size 95: 48,95 GOOD (HHFFFF)
+Size 96: 48,96 GOOD (HHFFFF)
+Size 97: 49,97 GOOD (HHFFFF)
+Size 98: 49,98 GOOD (HHFFFF)
+Size 99: 50,99 GOOD (HHFFFF)
+Size 100: 50,100 GOOD (HHFFFF)
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt
new file mode 100644
index 0000000000..d5261d8db3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt
@@ -0,0 +1,16 @@
+The narrowest allowed console window, in pixels, on a conventional (~96dpi)
+monitor:
+
+(mode con: cols=40 lines=40) && SetFont.exe -face "Lucida Console" -h 1 && (ping -n 4 127.0.0.1 > NUL) && cls && GetConsolePos.exe && SetFont.exe -face "Lucida Console" -h 12
+
+(mode con: cols=40 lines=40) && SetFont.exe -face "Lucida Console" -h 16 && (ping -n 4 127.0.0.1 > NUL) && cls && GetConsolePos.exe && SetFont.exe -face "Lucida Console" -h 12
+
+ sz1:px sz1:col sz16:px sz16:col
+Vista: 124 104 137 10
+Windows 7: 132 112 147 11
+Windows 8: 140 120 147 11
+Windows 8.1: 140 120 147 11
+Windows 10 OLD: 136 116 147 11
+Windows 10 NEW: 136 103 136 10
+
+I used build 14342 to test Windows 10.
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Results.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Results.txt
new file mode 100644
index 0000000000..15a825cb51
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Results.txt
@@ -0,0 +1,4 @@
+As before, avoid odd sizes in favor of even sizes.
+
+It's curious that the Japanese font is handled so poorly, especially with
+Windows 8 and later.
diff --git a/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt
new file mode 100644
index 0000000000..fef397a1e3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt
@@ -0,0 +1,144 @@
+Issues:
+
+ - Starting with the 14342 build, changing the font using
+ SetCurrentConsoleFontEx does not affect the window size. e.g. The content
+ itself will resize/redraw, but the window neither shrinks nor expands.
+ Presumably this is an oversight? It's almost a convenience; if a program
+ is going to resize the window anyway, then it's nice that the window size
+ contraints don't get in the way. Ordinarily, changing the font doesn't just
+ change the window size in pixels--it can also change the size as measured in
+ rows and columns.
+
+ - (Aside: in the 14342 build, there is also a bug with wmic.exe. Open a console
+ with more than 300 lines of screen buffer, then fill those lines with, e.g.,
+ dir /s. Then run wmic.exe. You won't be able to see the wmic.exe prompt.
+ If you query the screen buffer info somehow, you'll notice that the srWindow
+ is not contained within the dwSize. This breaks winpty's scraping, because
+ it's invalid.)
+
+ - In build 14316, with the Japanese locale, with the 437 code page, attempting
+ to set the Consolas font instead sets the Terminal (raster) font. It seems
+ to pick an appropriate vertical size.
+
+ - It seems necessary to specify "-family 0x36" for maximum reliability.
+ Setting the family to 0 almost always works, and specifying just -tt rarely
+ works.
+
+Win7
+ English locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 932 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt unreliable
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+
+Win10 Build 10586
+ New console
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+
+Win10 Build 14316
+ Old console
+ English locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 932 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selected very small Consolas font
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ New console
+ English locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt works
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 932 code page:
+ SetFont.exe -face Consolas -h 16 selects gothic instead
+ SetFont.exe -face Consolas -h 16 -tt selects gothic instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 selects gothic instead
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36(*) selects Terminal font instead
+
+Win10 Build 14342
+ Old Console
+ English locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 932 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ New console
+ English locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 works
+ SetFont.exe -face Consolas -h 16 -tt works
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+ Japanese locale / 932 code page:
+ SetFont.exe -face Consolas -h 16 selects gothic instead
+ SetFont.exe -face Consolas -h 16 -tt selects gothic instead
+ SetFont.exe -face Consolas -h 16 -family 0x36 selects gothic instead
+ Japanese locale / 437 code page:
+ SetFont.exe -face Consolas -h 16 selects Terminal font instead
+ SetFont.exe -face Consolas -h 16 -tt works
+ SetFont.exe -face Consolas -h 16 -family 0x36 works
+
+(*) I was trying to figure out whether the inconsistency was at when I stumbled
+onto this completely unexpected bug. Here's more detail:
+
+ F:\>SetFont.exe -face Consolas -h 16 -family 0x36 -weight normal -w 8
+ Setting to: nFont=0 dwFontSize=(8,16) FontFamily=0x36 FontWeight=400 FaceName="Consolas"
+ SetCurrentConsoleFontEx returned 1
+
+ F:\>GetFont.exe
+ largestConsoleWindowSize=(96,50)
+ maxWnd=0: nFont=0 dwFontSize=(12,16) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ maxWnd=1: nFont=0 dwFontSize=(96,25) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ 00-00: 12x16
+ GetNumberOfConsoleFonts returned 0
+ CP=437 OutputCP=437
+
+ F:\>SetFont.exe -face "Lucida Console" -h 16 -family 0x36 -weight normal
+ Setting to: nFont=0 dwFontSize=(0,16) FontFamily=0x36 FontWeight=400 FaceName="Lucida Console"
+ SetCurrentConsoleFontEx returned 1
+
+ F:\>GetFont.exe
+ largestConsoleWindowSize=(96,50)
+ maxWnd=0: nFont=0 dwFontSize=(12,16) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ maxWnd=1: nFont=0 dwFontSize=(96,25) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ 00-00: 12x16
+ GetNumberOfConsoleFonts returned 0
+ CP=437 OutputCP=437
+
+ F:\>SetFont.exe -face "Lucida Console" -h 12 -family 0x36 -weight normal
+ Setting to: nFont=0 dwFontSize=(0,12) FontFamily=0x36 FontWeight=400 FaceName="Lucida Console"
+ SetCurrentConsoleFontEx returned 1
+
+ F:\>GetFont.exe
+ largestConsoleWindowSize=(230,66)
+ maxWnd=0: nFont=0 dwFontSize=(5,12) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ maxWnd=1: nFont=0 dwFontSize=(116,36) FontFamily=0x30 FontWeight=400 FaceName=Terminal (54 65 72 6D 69 6E 61 6C)
+ 00-00: 5x12
+ GetNumberOfConsoleFonts returned 0
+ CP=437 OutputCP=437
+
+Even attempting to set to a Lucida Console / Consolas font from the Console
+properties dialog fails.
diff --git a/src/libs/3rdparty/winpty/misc/FontSurvey.cc b/src/libs/3rdparty/winpty/misc/FontSurvey.cc
new file mode 100644
index 0000000000..254bcc81a6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/FontSurvey.cc
@@ -0,0 +1,100 @@
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <vector>
+
+#include "TestUtil.cc"
+
+#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
+
+// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
+const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese
+const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese
+const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese
+const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean
+
+std::vector<bool> condense(const std::vector<CHAR_INFO> &buf) {
+ std::vector<bool> ret;
+ size_t i = 0;
+ while (i < buf.size()) {
+ if (buf[i].Char.UnicodeChar == L' ' &&
+ ((buf[i].Attributes & 0x300) == 0)) {
+ // end of line
+ break;
+ } else if (i + 1 < buf.size() &&
+ ((buf[i].Attributes & 0x300) == 0x100) &&
+ ((buf[i + 1].Attributes & 0x300) == 0x200) &&
+ buf[i].Char.UnicodeChar != L' ' &&
+ buf[i].Char.UnicodeChar == buf[i + 1].Char.UnicodeChar) {
+ // double-width
+ ret.push_back(true);
+ i += 2;
+ } else if ((buf[i].Attributes & 0x300) == 0) {
+ // single-width
+ ret.push_back(false);
+ i++;
+ } else {
+ ASSERT(false && "unexpected output");
+ }
+ }
+ return ret;
+}
+
+int main(int argc, char *argv[]) {
+ if (argc != 2) {
+ printf("Usage: %s \"arguments for SetFont.exe\"\n", argv[0]);
+ return 1;
+ }
+
+ const char *setFontArgs = argv[1];
+
+ const wchar_t testLine[] = { 0xA2, 0xA3, 0x2014, 0x3044, 0x30FC, 0x4000, 0 };
+ const HANDLE conout = openConout();
+
+ char setFontCmd[1024];
+ for (int h = 1; h <= 100; ++h) {
+ sprintf(setFontCmd, ".\\SetFont.exe %s -h %d && cls", setFontArgs, h);
+ system(setFontCmd);
+
+ CONSOLE_FONT_INFOEX infoex = {};
+ infoex.cbSize = sizeof(infoex);
+ BOOL success = GetCurrentConsoleFontEx(conout, FALSE, &infoex);
+ ASSERT(success && "GetCurrentConsoleFontEx failed");
+
+ DWORD actual = 0;
+ success = WriteConsoleW(conout, testLine, wcslen(testLine), &actual, nullptr);
+ ASSERT(success && actual == wcslen(testLine));
+
+ std::vector<CHAR_INFO> readBuf(14);
+ const SMALL_RECT readRegion = {0, 0, static_cast<short>(readBuf.size() - 1), 0};
+ SMALL_RECT readRegion2 = readRegion;
+ success = ReadConsoleOutputW(
+ conout, readBuf.data(),
+ {static_cast<short>(readBuf.size()), 1},
+ {0, 0},
+ &readRegion2);
+ ASSERT(success && !memcmp(&readRegion, &readRegion2, sizeof(readRegion)));
+
+ const auto widths = condense(readBuf);
+ std::string widthsStr;
+ for (bool width : widths) {
+ widthsStr.append(width ? "F" : "H");
+ }
+ char size[16];
+ sprintf(size, "%d,%d", infoex.dwFontSize.X, infoex.dwFontSize.Y);
+ const char *status = "";
+ if (widthsStr == "HHFFFF") {
+ status = "GOOD";
+ } else if (widthsStr == "HHHFFF") {
+ status = "OK";
+ } else {
+ status = "BAD";
+ }
+ trace("Size %3d: %-7s %-4s (%s)", h, size, status, widthsStr.c_str());
+ }
+ sprintf(setFontCmd, ".\\SetFont.exe %s -h 14", setFontArgs);
+ system(setFontCmd);
+}
diff --git a/src/libs/3rdparty/winpty/misc/FormatChar.h b/src/libs/3rdparty/winpty/misc/FormatChar.h
new file mode 100644
index 0000000000..aade488f9e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/FormatChar.h
@@ -0,0 +1,21 @@
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+static inline void formatChar(char *str, char ch)
+{
+ // Print some common control codes.
+ switch (ch) {
+ case '\r': strcpy(str, "CR "); break;
+ case '\n': strcpy(str, "LF "); break;
+ case ' ': strcpy(str, "SP "); break;
+ case 27: strcpy(str, "^[ "); break;
+ case 3: strcpy(str, "^C "); break;
+ default:
+ if (isgraph(ch))
+ sprintf(str, "%c ", ch);
+ else
+ sprintf(str, "%02x ", ch);
+ break;
+ }
+}
diff --git a/src/libs/3rdparty/winpty/misc/FreezePerfTest.cc b/src/libs/3rdparty/winpty/misc/FreezePerfTest.cc
new file mode 100644
index 0000000000..2c0b0086a1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/FreezePerfTest.cc
@@ -0,0 +1,62 @@
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+
+int main(int argc, char *argv[0]) {
+
+ if (argc != 2) {
+ printf("Usage: %s (mark|selectall|read)\n", argv[0]);
+ return 1;
+ }
+
+ enum class Test { Mark, SelectAll, Read } test;
+ if (!strcmp(argv[1], "mark")) {
+ test = Test::Mark;
+ } else if (!strcmp(argv[1], "selectall")) {
+ test = Test::SelectAll;
+ } else if (!strcmp(argv[1], "read")) {
+ test = Test::Read;
+ } else {
+ printf("Invalid test: %s\n", argv[1]);
+ return 1;
+ }
+
+ HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+ TimeMeasurement tm;
+ HWND hwnd = GetConsoleWindow();
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(100, 3000);
+ system("cls");
+ setWindowPos(0, 2975, 100, 25);
+ setCursorPos(0, 2999);
+
+ ShowWindow(hwnd, SW_HIDE);
+
+ for (int i = 0; i < 1000; ++i) {
+ // CONSOLE_SCREEN_BUFFER_INFO info = {};
+ // GetConsoleScreenBufferInfo(conout, &info);
+
+ if (test == Test::Mark) {
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0);
+ SendMessage(hwnd, WM_CHAR, 27, 0x00010001);
+ } else if (test == Test::SelectAll) {
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_SELECT_ALL, 0);
+ SendMessage(hwnd, WM_CHAR, 27, 0x00010001);
+ } else if (test == Test::Read) {
+ static CHAR_INFO buffer[100 * 3000];
+ const SMALL_RECT readRegion = {0, 0, 99, 2999};
+ SMALL_RECT tmp = readRegion;
+ BOOL ret = ReadConsoleOutput(conout, buffer, {100, 3000}, {0, 0}, &tmp);
+ ASSERT(ret && !memcmp(&tmp, &readRegion, sizeof(tmp)));
+ }
+ }
+
+ ShowWindow(hwnd, SW_SHOW);
+
+ printf("elapsed: %f\n", tm.elapsed());
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/GetCh.cc b/src/libs/3rdparty/winpty/misc/GetCh.cc
new file mode 100644
index 0000000000..cd6ed1943a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/GetCh.cc
@@ -0,0 +1,20 @@
+#include <conio.h>
+#include <ctype.h>
+#include <stdio.h>
+
+int main() {
+ printf("\nPress any keys -- Ctrl-D exits\n\n");
+
+ while (true) {
+ const int ch = getch();
+ printf("0x%x", ch);
+ if (isgraph(ch)) {
+ printf(" '%c'", ch);
+ }
+ printf("\n");
+ if (ch == 0x4) { // Ctrl-D
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/GetConsolePos.cc b/src/libs/3rdparty/winpty/misc/GetConsolePos.cc
new file mode 100644
index 0000000000..1f3cc5316f
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/GetConsolePos.cc
@@ -0,0 +1,41 @@
+#include <windows.h>
+
+#include <stdio.h>
+
+#include "TestUtil.cc"
+
+int main() {
+ const HANDLE conout = openConout();
+
+ CONSOLE_SCREEN_BUFFER_INFO info = {};
+ BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
+ ASSERT(ret && "GetConsoleScreenBufferInfo failed");
+
+ trace("cursor=%d,%d", info.dwCursorPosition.X, info.dwCursorPosition.Y);
+ printf("cursor=%d,%d\n", info.dwCursorPosition.X, info.dwCursorPosition.Y);
+
+ trace("srWindow={L=%d,T=%d,R=%d,B=%d}", info.srWindow.Left, info.srWindow.Top, info.srWindow.Right, info.srWindow.Bottom);
+ printf("srWindow={L=%d,T=%d,R=%d,B=%d}\n", info.srWindow.Left, info.srWindow.Top, info.srWindow.Right, info.srWindow.Bottom);
+
+ trace("dwSize=%d,%d", info.dwSize.X, info.dwSize.Y);
+ printf("dwSize=%d,%d\n", info.dwSize.X, info.dwSize.Y);
+
+ const HWND hwnd = GetConsoleWindow();
+ if (hwnd != NULL) {
+ RECT r = {};
+ if (GetWindowRect(hwnd, &r)) {
+ const int w = r.right - r.left;
+ const int h = r.bottom - r.top;
+ trace("hwnd: pos=(%d,%d) size=(%d,%d)", r.left, r.top, w, h);
+ printf("hwnd: pos=(%d,%d) size=(%d,%d)\n", r.left, r.top, w, h);
+ } else {
+ trace("GetWindowRect failed");
+ printf("GetWindowRect failed\n");
+ }
+ } else {
+ trace("GetConsoleWindow returned NULL");
+ printf("GetConsoleWindow returned NULL\n");
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/GetFont.cc b/src/libs/3rdparty/winpty/misc/GetFont.cc
new file mode 100644
index 0000000000..38625317ab
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/GetFont.cc
@@ -0,0 +1,261 @@
+#include <windows.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <wchar.h>
+
+#include "../src/shared/OsModule.h"
+#include "../src/shared/StringUtil.h"
+
+#include "TestUtil.cc"
+#include "../src/shared/StringUtil.cc"
+
+#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
+
+// Some of these types and functions are missing from the MinGW headers.
+// Others are undocumented.
+
+struct AGENT_CONSOLE_FONT_INFO {
+ DWORD nFont;
+ COORD dwFontSize;
+};
+
+struct AGENT_CONSOLE_FONT_INFOEX {
+ ULONG cbSize;
+ DWORD nFont;
+ COORD dwFontSize;
+ UINT FontFamily;
+ UINT FontWeight;
+ WCHAR FaceName[LF_FACESIZE];
+};
+
+// undocumented XP API
+typedef BOOL WINAPI SetConsoleFont_t(
+ HANDLE hOutput,
+ DWORD dwFontIndex);
+
+// undocumented XP API
+typedef DWORD WINAPI GetNumberOfConsoleFonts_t();
+
+// XP and up
+typedef BOOL WINAPI GetCurrentConsoleFont_t(
+ HANDLE hOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFO *lpConsoleCurrentFont);
+
+// XP and up
+typedef COORD WINAPI GetConsoleFontSize_t(
+ HANDLE hConsoleOutput,
+ DWORD nFont);
+
+// Vista and up
+typedef BOOL WINAPI GetCurrentConsoleFontEx_t(
+ HANDLE hConsoleOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
+
+// Vista and up
+typedef BOOL WINAPI SetCurrentConsoleFontEx_t(
+ HANDLE hConsoleOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
+
+#define GET_MODULE_PROC(mod, funcName) \
+ m_##funcName = reinterpret_cast<funcName##_t*>((mod).proc(#funcName)); \
+
+#define DEFINE_ACCESSOR(funcName) \
+ funcName##_t &funcName() const { \
+ ASSERT(valid()); \
+ return *m_##funcName; \
+ }
+
+class XPFontAPI {
+public:
+ XPFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFont);
+ GET_MODULE_PROC(m_kernel32, GetConsoleFontSize);
+ }
+
+ bool valid() const {
+ return m_GetCurrentConsoleFont != NULL &&
+ m_GetConsoleFontSize != NULL;
+ }
+
+ DEFINE_ACCESSOR(GetCurrentConsoleFont)
+ DEFINE_ACCESSOR(GetConsoleFontSize)
+
+private:
+ OsModule m_kernel32;
+ GetCurrentConsoleFont_t *m_GetCurrentConsoleFont;
+ GetConsoleFontSize_t *m_GetConsoleFontSize;
+};
+
+class UndocumentedXPFontAPI : public XPFontAPI {
+public:
+ UndocumentedXPFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, SetConsoleFont);
+ GET_MODULE_PROC(m_kernel32, GetNumberOfConsoleFonts);
+ }
+
+ bool valid() const {
+ return this->XPFontAPI::valid() &&
+ m_SetConsoleFont != NULL &&
+ m_GetNumberOfConsoleFonts != NULL;
+ }
+
+ DEFINE_ACCESSOR(SetConsoleFont)
+ DEFINE_ACCESSOR(GetNumberOfConsoleFonts)
+
+private:
+ OsModule m_kernel32;
+ SetConsoleFont_t *m_SetConsoleFont;
+ GetNumberOfConsoleFonts_t *m_GetNumberOfConsoleFonts;
+};
+
+class VistaFontAPI : public XPFontAPI {
+public:
+ VistaFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFontEx);
+ GET_MODULE_PROC(m_kernel32, SetCurrentConsoleFontEx);
+ }
+
+ bool valid() const {
+ return this->XPFontAPI::valid() &&
+ m_GetCurrentConsoleFontEx != NULL &&
+ m_SetCurrentConsoleFontEx != NULL;
+ }
+
+ DEFINE_ACCESSOR(GetCurrentConsoleFontEx)
+ DEFINE_ACCESSOR(SetCurrentConsoleFontEx)
+
+private:
+ OsModule m_kernel32;
+ GetCurrentConsoleFontEx_t *m_GetCurrentConsoleFontEx;
+ SetCurrentConsoleFontEx_t *m_SetCurrentConsoleFontEx;
+};
+
+static std::vector<std::pair<DWORD, COORD> > readFontTable(
+ XPFontAPI &api, HANDLE conout, DWORD maxCount) {
+ std::vector<std::pair<DWORD, COORD> > ret;
+ for (DWORD i = 0; i < maxCount; ++i) {
+ COORD size = api.GetConsoleFontSize()(conout, i);
+ if (size.X == 0 && size.Y == 0) {
+ break;
+ }
+ ret.push_back(std::make_pair(i, size));
+ }
+ return ret;
+}
+
+static void dumpFontTable(HANDLE conout) {
+ const int kMaxCount = 1000;
+ XPFontAPI api;
+ if (!api.valid()) {
+ printf("dumpFontTable: cannot dump font table -- missing APIs\n");
+ return;
+ }
+ std::vector<std::pair<DWORD, COORD> > table =
+ readFontTable(api, conout, kMaxCount);
+ std::string line;
+ char tmp[128];
+ size_t first = 0;
+ while (first < table.size()) {
+ size_t last = std::min(table.size() - 1, first + 10 - 1);
+ winpty_snprintf(tmp, "%02u-%02u:",
+ static_cast<unsigned>(first), static_cast<unsigned>(last));
+ line = tmp;
+ for (size_t i = first; i <= last; ++i) {
+ if (i % 10 == 5) {
+ line += " - ";
+ }
+ winpty_snprintf(tmp, " %2dx%-2d",
+ table[i].second.X, table[i].second.Y);
+ line += tmp;
+ }
+ printf("%s\n", line.c_str());
+ first = last + 1;
+ }
+ if (table.size() == kMaxCount) {
+ printf("... stopped reading at %d fonts ...\n", kMaxCount);
+ }
+}
+
+static std::string stringToCodePoints(const std::wstring &str) {
+ std::string ret = "(";
+ for (size_t i = 0; i < str.size(); ++i) {
+ char tmp[32];
+ winpty_snprintf(tmp, "%X", str[i]);
+ if (ret.size() > 1) {
+ ret.push_back(' ');
+ }
+ ret += tmp;
+ }
+ ret.push_back(')');
+ return ret;
+}
+
+static void dumpFontInfoEx(
+ const AGENT_CONSOLE_FONT_INFOEX &infoex) {
+ std::wstring faceName(infoex.FaceName,
+ winpty_wcsnlen(infoex.FaceName, COUNT_OF(infoex.FaceName)));
+ cprintf(L"nFont=%u dwFontSize=(%d,%d) "
+ "FontFamily=0x%x FontWeight=%u FaceName=%ls %hs\n",
+ static_cast<unsigned>(infoex.nFont),
+ infoex.dwFontSize.X, infoex.dwFontSize.Y,
+ infoex.FontFamily, infoex.FontWeight, faceName.c_str(),
+ stringToCodePoints(faceName).c_str());
+}
+
+static void dumpVistaFont(VistaFontAPI &api, HANDLE conout, BOOL maxWindow) {
+ AGENT_CONSOLE_FONT_INFOEX infoex = {0};
+ infoex.cbSize = sizeof(infoex);
+ if (!api.GetCurrentConsoleFontEx()(conout, maxWindow, &infoex)) {
+ printf("GetCurrentConsoleFontEx call failed\n");
+ return;
+ }
+ dumpFontInfoEx(infoex);
+}
+
+static void dumpXPFont(XPFontAPI &api, HANDLE conout, BOOL maxWindow) {
+ AGENT_CONSOLE_FONT_INFO info = {0};
+ if (!api.GetCurrentConsoleFont()(conout, maxWindow, &info)) {
+ printf("GetCurrentConsoleFont call failed\n");
+ return;
+ }
+ printf("nFont=%u dwFontSize=(%d,%d)\n",
+ static_cast<unsigned>(info.nFont),
+ info.dwFontSize.X, info.dwFontSize.Y);
+}
+
+static void dumpFontAndTable(HANDLE conout) {
+ VistaFontAPI vista;
+ if (vista.valid()) {
+ printf("maxWnd=0: "); dumpVistaFont(vista, conout, FALSE);
+ printf("maxWnd=1: "); dumpVistaFont(vista, conout, TRUE);
+ dumpFontTable(conout);
+ return;
+ }
+ UndocumentedXPFontAPI xp;
+ if (xp.valid()) {
+ printf("maxWnd=0: "); dumpXPFont(xp, conout, FALSE);
+ printf("maxWnd=1: "); dumpXPFont(xp, conout, TRUE);
+ dumpFontTable(conout);
+ return;
+ }
+ printf("setSmallFont: neither Vista nor XP APIs detected -- giving up\n");
+ dumpFontTable(conout);
+}
+
+int main() {
+ const HANDLE conout = openConout();
+ const COORD largest = GetLargestConsoleWindowSize(conout);
+ printf("largestConsoleWindowSize=(%d,%d)\n", largest.X, largest.Y);
+ dumpFontAndTable(conout);
+ UndocumentedXPFontAPI xp;
+ if (xp.valid()) {
+ printf("GetNumberOfConsoleFonts returned %u\n", xp.GetNumberOfConsoleFonts()());
+ } else {
+ printf("The GetNumberOfConsoleFonts API was missing\n");
+ }
+ printf("CP=%u OutputCP=%u\n", GetConsoleCP(), GetConsoleOutputCP());
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/IdentifyConsoleWindow.ps1 b/src/libs/3rdparty/winpty/misc/IdentifyConsoleWindow.ps1
new file mode 100644
index 0000000000..0c488597bd
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/IdentifyConsoleWindow.ps1
@@ -0,0 +1,51 @@
+#
+# Usage: powershell <path>\IdentifyConsoleWindow.ps1
+#
+# This script determines whether the process has a console attached, whether
+# that console has a non-NULL window (e.g. HWND), and whether the window is on
+# the current window station.
+#
+
+$signature = @'
+[DllImport("kernel32.dll", SetLastError=true)]
+public static extern IntPtr GetConsoleWindow();
+
+[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
+public static extern bool SetConsoleTitle(String title);
+
+[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
+public static extern int GetWindowText(IntPtr hWnd,
+ System.Text.StringBuilder lpString,
+ int nMaxCount);
+'@
+
+$WinAPI = Add-Type -MemberDefinition $signature `
+ -Name WinAPI -Namespace IdentifyConsoleWindow -PassThru
+
+if (!$WinAPI::SetConsoleTitle("ConsoleWindowScript")) {
+ echo "error: could not change console title -- is a console attached?"
+ exit 1
+} else {
+ echo "note: successfully set console title to ""ConsoleWindowScript""."
+}
+
+$hwnd = $WinAPI::GetConsoleWindow()
+if ($hwnd -eq 0) {
+ echo "note: GetConsoleWindow returned NULL."
+} else {
+ echo "note: GetConsoleWindow returned 0x$($hwnd.ToString("X"))."
+ $sb = New-Object System.Text.StringBuilder -ArgumentList 4096
+ if ($WinAPI::GetWindowText($hwnd, $sb, $sb.Capacity)) {
+ $title = $sb.ToString()
+ echo "note: GetWindowText returned ""${title}""."
+ if ($title -eq "ConsoleWindowScript") {
+ echo "success!"
+ } else {
+ echo "error: expected to see ""ConsoleWindowScript""."
+ echo " (Perhaps the console window is on a different window station?)"
+ }
+ } else {
+ echo "error: GetWindowText could not read the window title."
+ echo " (Perhaps the console window is on a different window station?)"
+ }
+}
diff --git a/src/libs/3rdparty/winpty/misc/IsNewConsole.cc b/src/libs/3rdparty/winpty/misc/IsNewConsole.cc
new file mode 100644
index 0000000000..2b554c72c9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/IsNewConsole.cc
@@ -0,0 +1,87 @@
+// Determines whether this is a new console by testing whether MARK moves the
+// cursor.
+//
+// WARNING: This test program may behave erratically if run under winpty.
+//
+
+#include <windows.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "TestUtil.cc"
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+
+static COORD getWindowPos(HANDLE conout) {
+ CONSOLE_SCREEN_BUFFER_INFO info = {};
+ BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
+ ASSERT(ret && "GetConsoleScreenBufferInfo failed");
+ return { info.srWindow.Left, info.srWindow.Top };
+}
+
+static COORD getWindowSize(HANDLE conout) {
+ CONSOLE_SCREEN_BUFFER_INFO info = {};
+ BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
+ ASSERT(ret && "GetConsoleScreenBufferInfo failed");
+ return {
+ static_cast<short>(info.srWindow.Right - info.srWindow.Left + 1),
+ static_cast<short>(info.srWindow.Bottom - info.srWindow.Top + 1)
+ };
+}
+
+static COORD getCursorPos(HANDLE conout) {
+ CONSOLE_SCREEN_BUFFER_INFO info = {};
+ BOOL ret = GetConsoleScreenBufferInfo(conout, &info);
+ ASSERT(ret && "GetConsoleScreenBufferInfo failed");
+ return info.dwCursorPosition;
+}
+
+static void setCursorPos(HANDLE conout, COORD pos) {
+ BOOL ret = SetConsoleCursorPosition(conout, pos);
+ ASSERT(ret && "SetConsoleCursorPosition failed");
+}
+
+int main() {
+ const HANDLE conout = openConout();
+ const HWND hwnd = GetConsoleWindow();
+ ASSERT(hwnd != NULL && "GetConsoleWindow() returned NULL");
+
+ // With the legacy console, the Mark command moves the the cursor to the
+ // top-left cell of the visible console window. Determine whether this
+ // is the new console by seeing if the cursor moves.
+
+ const auto windowSize = getWindowSize(conout);
+ if (windowSize.X <= 1) {
+ printf("Error: console window must be at least 2 columns wide\n");
+ trace("Error: console window must be at least 2 columns wide");
+ return 1;
+ }
+
+ bool cursorMoved = false;
+ const auto initialPos = getCursorPos(conout);
+
+ const auto windowPos = getWindowPos(conout);
+ setCursorPos(conout, { static_cast<short>(windowPos.X + 1), windowPos.Y });
+
+ {
+ const auto posA = getCursorPos(conout);
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0);
+ const auto posB = getCursorPos(conout);
+ cursorMoved = memcmp(&posA, &posB, sizeof(posA)) != 0;
+ SendMessage(hwnd, WM_CHAR, 27, 0x00010001); // Send ESCAPE
+ }
+
+ setCursorPos(conout, initialPos);
+
+ if (cursorMoved) {
+ printf("Legacy console (i.e. MARK moved cursor)\n");
+ trace("Legacy console (i.e. MARK moved cursor)");
+ } else {
+ printf("Windows 10 new console (i.e MARK did not move cursor)\n");
+ trace("Windows 10 new console (i.e MARK did not move cursor)");
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/MouseInputNotes.txt b/src/libs/3rdparty/winpty/misc/MouseInputNotes.txt
new file mode 100644
index 0000000000..18460c6861
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/MouseInputNotes.txt
@@ -0,0 +1,90 @@
+Introduction
+============
+
+The only specification I could find describing mouse input escape sequences
+was the /usr/share/doc/xterm/ctlseqs.txt.gz file installed on my Ubuntu
+machine.
+
+Here are the relevant escape sequences:
+
+ * [ON] CSI '?' M 'h' Enable mouse input mode M
+ * [OFF] CSI '?' M 'l' Disable mouse input mode M
+ * [EVT] CSI 'M' F X Y Mouse event (default or mode 1005)
+ * [EVT6] CSI '<' F ';' X ';' Y 'M' Mouse event with mode 1006
+ * [EVT6] CSI '<' F ';' X ';' Y 'm' Mouse event with mode 1006 (up)
+ * [EVT15] CSI F ';' X ';' Y 'M' Mouse event with mode 1015
+
+The first batch of modes affect what events are reported:
+
+ * 9: Presses only (not as well-supported as the other modes)
+ * 1000: Presses and releases
+ * 1002: Presses, releases, and moves-while-pressed
+ * 1003: Presses, releases, and all moves
+
+The next batch of modes affect the encoding of the mouse events:
+
+ * 1005: The X and Y coordinates are UTF-8 codepoints rather than bytes.
+ * 1006: Use the EVT6 sequences instead of EVT
+ * 1015: Use the EVT15 sequence instead of EVT (aka URVXT-mode)
+
+Support for modes in existing terminals
+=======================================
+
+ | 9 1000 1002 1003 | 1004 | overflow | defhi | 1005 1006 1015
+---------------------------------+---------------------+------+--------------+-------+----------------
+Eclipse TM Terminal (Neon) | _ _ _ _ | _ | n/a | n/a | _ _ _
+gnome-terminal 3.6.2 | X X X X | _ | suppressed*b | 0x07 | _ X X
+iTerm2 2.1.4 | _ X X X | OI | wrap*z | n/a | X X X
+jediterm/IntelliJ | _ X X X | _ | ch='?' | 0xff | X X X
+Konsole 2.13.2 | _ X X *a | _ | suppressed | 0xff | X X X
+mintty 2.2.2 | X X X X | OI | ch='\0' | 0xff | X X X
+putty 0.66 | _ X X _ | _ | suppressed | 0xff | _ X X
+rxvt 2.7.10 | X X _ _ | _ | wrap*z | n/a | _ _ _
+screen(under xterm) | X X X X | _ | suppressed | 0xff | _ _ _
+urxvt 9.21 | X X X X | _ | wrap*z | n/a | X _ X
+xfce4-terminal 0.6.3 (GTK2 VTE) | X X X X | _ | wrap | n/a | _ _ _
+xterm | X X X X | OI | ch='\0' | 0xff | X X X
+
+*a: Mode 1003 is handled the same way as 1002.
+*b: The coordinate wraps from 0xff to 0x00, then maxs out at 0x07. I'm
+ guessing this behavior is a bug? I'm using the Xubuntu 14.04
+ gnome-terminal.
+*z: These terminals have a bug where column 224 (and row 224, presumably)
+ yields a truncated escape sequence. 224 + 32 is 0, so it would normally
+ yield `CSI 'M' F '\0' Y`, but the '\0' is interpreted as a NUL-terminator.
+
+Problem 1: How do these flags work?
+===================================
+
+Terminals accept the OFF sequence with any of the input modes. This makes
+little sense--there are two multi-value settings, not seven independent flags!
+
+All the terminals handle Granularity the same way. ON-Granularity sets
+Granularity to the specified value, and OFF-Granularity sets Granularity to
+OFF.
+
+Terminals vary in how they handle the Encoding modes. For example:
+
+ * xterm. ON-Encoding sets Encoding. OFF-Encoding with a non-active Encoding
+ has no effect. OFF-Encoding otherwise resets Encoding to Default.
+
+ * mintty (tested 2.2.2), iTerm2 2.1.4, and jediterm. ON-Encoding sets
+ Encoding. OFF-Encoding resets Encoding to Default.
+
+ * Konsole (tested 2.13.2) seems to configure each encoding method
+ independently. The effective Encoding is the first enabled encoding in this
+ list:
+ - Mode 1006
+ - Mode 1015
+ - Mode 1005
+ - Default
+
+ * gnome-terminal (tested 3.6.2) also configures each encoding method
+ independently. The effective Encoding is the first enabled encoding in
+ this list:
+ - Mode 1006
+ - Mode 1015
+ - Default
+ Mode 1005 is not supported.
+
+ * xfce4 terminal 0.6.3 (GTK2 VTE) always outputs the default encoding method.
diff --git a/src/libs/3rdparty/winpty/misc/MoveConsoleWindow.cc b/src/libs/3rdparty/winpty/misc/MoveConsoleWindow.cc
new file mode 100644
index 0000000000..7d9684fe94
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/MoveConsoleWindow.cc
@@ -0,0 +1,34 @@
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc != 3 && argc != 5) {
+ printf("Usage: %s x y\n", argv[0]);
+ printf("Usage: %s x y width height\n", argv[0]);
+ return 1;
+ }
+
+ HWND hwnd = GetConsoleWindow();
+
+ const int x = atoi(argv[1]);
+ const int y = atoi(argv[2]);
+
+ int w = 0, h = 0;
+ if (argc == 3) {
+ RECT r = {};
+ BOOL ret = GetWindowRect(hwnd, &r);
+ ASSERT(ret && "GetWindowRect failed on console window");
+ w = r.right - r.left;
+ h = r.bottom - r.top;
+ } else {
+ w = atoi(argv[3]);
+ h = atoi(argv[4]);
+ }
+
+ BOOL ret = MoveWindow(hwnd, x, y, w, h, TRUE);
+ trace("MoveWindow: ret=%d", ret);
+ printf("MoveWindow: ret=%d\n", ret);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Notes.txt b/src/libs/3rdparty/winpty/misc/Notes.txt
new file mode 100644
index 0000000000..410e184198
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Notes.txt
@@ -0,0 +1,219 @@
+Test programs
+-------------
+
+Cygwin
+ emacs
+ vim
+ mc (Midnight Commander)
+ lynx
+ links
+ less
+ more
+ wget
+
+Capturing the console output
+----------------------------
+
+Initial idea:
+
+In the agent, keep track of the remote terminal state for N lines of
+(window+history). Also keep track of the terminal size. Regularly poll for
+changes to the console screen buffer, then use some number of edits to bring
+the remote terminal into sync with the console.
+
+This idea seems to have trouble when a Unix terminal is resized. When the
+server receives a resize notification, it can have a hard time figuring out
+what the terminal did. Race conditions might also be a problem.
+
+The behavior of the terminal can be tricky:
+
+ - When the window is expanded by one line, does the terminal add a blank line
+ to the bottom or move a line from the history into the top?
+
+ - When the window is shrunk by one line, does the terminal delete the topmost
+ or the bottommost line? Can it delete the line with the cursor?
+
+Some popular behaviors for expanding:
+ - [all] If there are no history lines, then add a line at the bottom.
+ - [konsole] Always add a line at the bottom.
+ - [putty,xterm,rxvt] Pull in a history line from the top.
+ - [g-t] I can't tell. It seems to add a blank line, until the program writes
+ to stdout or until I click the scroll bar, then the output "snaps" back down,
+ pulling lines out of the history. I thought I saw different behavior
+ between Ubuntu 10.10 and 11.10, so maybe GNOME 3 changed something. Avoid
+ using "bash" to test this behavior because "bash" apparently always writes
+ the prompt after terminal resize.
+
+Some popular behaviors for shrinking:
+ - [konsole,putty,xterm,rxvt] If the line at the bottom is blank, then delete
+ it. Otherwise, move the topmost line into history.
+ - [g-t] If the line at the bottom has not been touched, then delete it.
+ Otherwise, move the topmost line into history.
+
+(TODO: I need to test my theories about the terminal behavior better still.
+It's interesting to see how g-t handles clear differently than every other
+terminal.)
+
+There is an ANSI escape sequence (DSR) that sends the current cursor location
+to the terminal's input. One idea I had was to use this code to figure out how
+the terminal had handled a resize. I currently think this idea won't work due
+to race conditions.
+
+Newer idea:
+
+Keep track of the last N lines that have been sent to the remote terminal.
+Poll for changes to console output. When the output changes, send just the
+changed content to the terminal. In particular:
+ - Don't send a cursor position (CUP) code. Instead, if the line that's 3
+ steps up from the latest line changes, send a relative cursor up (CUU)
+ code. It's OK to send an absolute column number code (CHA).
+ - At least in general, don't try to send complete screenshots of the current
+ console window.
+
+The idea is that sending just the changes should have good behavior for streams
+of output, even when those streams modify the output (e.g. an archiver, or
+maybe a downloader/packager/wget). I need to think about whether this works
+for full-screen programs (e.g. emacs, less, lynx, the above list of programs).
+
+I noticed that console programs don't typically modify the window or buffer
+coordinates. edit.com is an exception.
+
+I tested the pager in native Python (more?), and I verified that ENTER and SPACE
+both paid no attention to the location of the console window within the screen
+buffer. This makes sense -- why would they care? The Cygwin less, on the other
+hand, does care. If I scroll the window up, then Cygwin less will write to a
+position within the window. I didn't really expect this behavior, but it
+doesn't seem to be a problem.
+
+Setting up a TestNetServer service
+----------------------------------
+
+First run the deploy.sh script to copy files into deploy. Make sure
+TestNetServer.exe will run in a bare environment (no MinGW or Qt in the path).
+
+Install the Windows Server 2003 Resource Kit. It will have two programs in it,
+instsrv and srvany.
+
+Run:
+
+ InstSrv TestNetServer <path-to-srvany>\srvany.exe
+
+This creates a service named "TestNetServer" that uses the Microsoft service
+wrapper. To configure the new service to run TestNetServer, set a registry
+value:
+
+ [HKLM\SYSTEM\CurrentControlSet\Services\TestNetServer\Parameters]
+ Application=<full-path>\TestNetServer.exe
+
+Also see http://www.iopus.com/guides/srvany.htm.
+
+To remove the service, run:
+
+ InstSrv TestNetServer REMOVE
+
+TODO
+----
+
+Agent: When resizing the console, consider whether to add lines to the top
+or bottom. I remember thinking the current behavior was wrong for some
+application, but I forgot which one.
+
+Make the font as small as possible. The console window dimensions are limited by
+the screen size, so making the font small reduces an unnecessary limitation on the
+PseudoConsole size. There's a documented Vista/Win7 API for this
+(SetCurrentConsoleFontEx), and apparently WinXP has an undocumented API
+(SetConsoleFont):
+ http://blogs.microsoft.co.il/blogs/pavely/archive/2009/07/23/changing-console-fonts.aspx
+
+Make the agent work with DOS programs like edit and qbasic.
+ - Detect that the terminal program has resized the window/buffer and enter a
+ simple just-scrape-and-dont-resize mode. Track the client window size and
+ send the intersection of the console and the agent's client.
+ - I also need to generate keyboard scan codes.
+ - Solve the NTVDM.EXE console shutdown problem, probably by ignoring NTVDM.EXE
+ when it appears on the GetConsoleProcessList list.
+
+Rename the agent? Is the term "proxy" more accurate?
+
+Optimize the polling. e.g. Use a longer poll interval when the console is idle.
+Do a minimal poll that checks whether the sync marker or window has moved.
+
+Increase the console buffer size to ~9000 lines. Beware making it so big that
+reading the sync column exhausts the 32KB conhost<->agent heap.
+
+Reduce the memory overhead of the agent. The agent's m_bufferData array can
+be small (a few hundred lines?) relative to the console buffer size.
+
+Try to handle console background color better.
+ Unix terminal emulators have a user-configurable foreground and background
+color, and for best results, the agent really needs to avoid changing the colors,
+especially the background color. It's undesirable/ugly to SSH into a machine
+and see the command prompt change the colors. It's especially ugly that the
+terminal retains its original colors and only drawn cells get the new colors.
+(e.g. Resizing the window to the right uses the local terminal colors rather
+than the remote colors.) It's especially ugly in gnome-terminal, which draws
+user-configurable black as black, but VT100 black as dark-gray.
+ If there were a way to query the terminal emulator's colors, then I could
+match the console's colors to the terminal and everything would just work. As
+far as I know, that's not possible.
+ I thought of a kludge that might work. Instead of translating console white
+and black to VT/100 white and black, I would translate them to "reset" and
+"invert". I'd translate other colors normally. This approach should produce
+ideal results for command-line work and tolerable results for full-screen
+programs without configuration. Configuring the agent for black-on-white or
+white-on-black would produce ideal results in all situations.
+ This kludge only really applies to the SSH application. For a Win32 Konsole
+application, it should be easy to get the colors right all the time.
+
+Try using the screen reader API:
+ - To eliminate polling.
+ - To detect when a line wraps. When a line wraps, it'd be nice not to send a
+ CRLF to the terminal emulator so copy-and-paste works better.
+ - To detect hard tabs with Cygwin.
+
+Implement VT100/ANSI escape sequence recognition for input. Decide where this
+functionality belongs. PseudoConsole.dll? Disambiguating ESC from an escape
+sequence might be tricky. For the SSH server, I was thinking that when a small
+SSH payload ended with an ESC character, I could assume the character was really
+an ESC keypress, on the assumption that if it were an escape sequence, the
+payload would probably contain the whole sequence. I'm not sure this works,
+especially if there's a lot of other traffic multiplexed on the SSH socket.
+
+Support Unicode.
+ - Some DOS programs draw using line/box characters. Can these characters be
+ translated to the Unicode equivalents?
+
+Create automated tests.
+
+Experiment with the Terminator emulator, an emulator that doesn't wrap lines.
+How many columns does it report having? What column does it report the cursor
+in as it's writing past the right end of the window? Will Terminator be a
+problem if I implement line wrapping detection in the agent?
+
+BUG: After the unix-adapter/pconsole.exe program exits, the blinking cursor is
+replaced with a hidden cursor.
+
+Fix assert() in the agent. If it fails, the failure message needs to be
+reported somewhere. Pop up a dialog box? Maybe switch the active desktop,
+then show a dialog box?
+
+TODO: There's already a pconsole project on GitHub. Maybe rename this project
+to something else? winpty?
+
+TODO: Can the DebugServer system be replaced with OutputDebugString? How
+do we decide whose processes' output to collect?
+
+TODO: Three executables:
+ build/winpty-agent.exe
+ build/winpty.dll
+ build/console.exe
+
+BUG: Run the pconsole.exe inside another console. As I type dir, I see this:
+ D:\rprichard\pconsole>
+ D:\rprichard\pconsole>d
+ D:\rprichard\pconsole>di
+ D:\rprichard\pconsole>dir
+ In the output of "dir", every other line is blank.
+ There was a bug in Terminal::sendLine that was causing this to happen
+ frequently. Now that I fixed it, this bug should only manifest on lines
+ whose last column is not a space (i.e. a full line).
diff --git a/src/libs/3rdparty/winpty/misc/OSVersion.cc b/src/libs/3rdparty/winpty/misc/OSVersion.cc
new file mode 100644
index 0000000000..456708f05b
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/OSVersion.cc
@@ -0,0 +1,27 @@
+#include <windows.h>
+
+#include <assert.h>
+#include <locale.h>
+#include <stdio.h>
+
+#include <iostream>
+
+int main() {
+ setlocale(LC_ALL, "");
+
+ OSVERSIONINFOEXW info = {0};
+ info.dwOSVersionInfoSize = sizeof(info);
+ assert(GetVersionExW((OSVERSIONINFOW*)&info));
+
+ printf("dwMajorVersion = %d\n", (int)info.dwMajorVersion);
+ printf("dwMinorVersion = %d\n", (int)info.dwMinorVersion);
+ printf("dwBuildNumber = %d\n", (int)info.dwBuildNumber);
+ printf("dwPlatformId = %d\n", (int)info.dwPlatformId);
+ printf("szCSDVersion = %ls\n", info.szCSDVersion);
+ printf("wServicePackMajor = %d\n", info.wServicePackMajor);
+ printf("wServicePackMinor = %d\n", info.wServicePackMinor);
+ printf("wSuiteMask = 0x%x\n", (unsigned int)info.wSuiteMask);
+ printf("wProductType = 0x%x\n", (unsigned int)info.wProductType);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ScreenBufferFreezeInactive.cc b/src/libs/3rdparty/winpty/misc/ScreenBufferFreezeInactive.cc
new file mode 100644
index 0000000000..656d4f126d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ScreenBufferFreezeInactive.cc
@@ -0,0 +1,101 @@
+//
+// Verify that console selection blocks writes to an inactive console screen
+// buffer. Writes TEST PASSED or TEST FAILED to the popup console window.
+//
+
+#include <windows.h>
+#include <stdio.h>
+
+#include <string>
+
+#include "TestUtil.cc"
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+
+bool g_useMark = false;
+
+CALLBACK DWORD pausingThread(LPVOID dummy)
+{
+ HWND hwnd = GetConsoleWindow();
+ trace("Sending selection to freeze");
+ SendMessage(hwnd, WM_SYSCOMMAND,
+ g_useMark ? SC_CONSOLE_MARK :
+ SC_CONSOLE_SELECT_ALL,
+ 0);
+ Sleep(1000);
+ trace("Sending escape WM_CHAR to unfreeze");
+ SendMessage(hwnd, WM_CHAR, 27, 0x00010001);
+ Sleep(1000);
+}
+
+static HANDLE createBuffer() {
+ HANDLE buf = CreateConsoleScreenBuffer(
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ CONSOLE_TEXTMODE_BUFFER,
+ NULL);
+ ASSERT(buf != INVALID_HANDLE_VALUE);
+ return buf;
+}
+
+static void runTest(bool useMark, bool createEarly) {
+ trace("=======================================");
+ trace("useMark=%d createEarly=%d", useMark, createEarly);
+ g_useMark = useMark;
+ HANDLE buf = INVALID_HANDLE_VALUE;
+
+ if (createEarly) {
+ buf = createBuffer();
+ }
+
+ CreateThread(NULL, 0,
+ pausingThread, NULL,
+ 0, NULL);
+ Sleep(500);
+
+ if (!createEarly) {
+ trace("Creating buffer");
+ TimeMeasurement tm1;
+ buf = createBuffer();
+ const double elapsed1 = tm1.elapsed();
+ if (elapsed1 >= 0.250) {
+ printf("!!! TEST FAILED !!!\n");
+ Sleep(2000);
+ return;
+ }
+ }
+
+ trace("Writing to aux buffer");
+ TimeMeasurement tm2;
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleW(buf, L"HI", 2, &actual, NULL);
+ const double elapsed2 = tm2.elapsed();
+ trace("Writing to aux buffer: finished: ret=%d actual=%d (elapsed=%1.3f)", ret, actual, elapsed2);
+ if (elapsed2 < 0.250) {
+ printf("!!! TEST FAILED !!!\n");
+ } else {
+ printf("TEST PASSED\n");
+ }
+ Sleep(2000);
+}
+
+int main(int argc, char **argv) {
+ if (argc == 1) {
+ startChildProcess(L"child");
+ return 0;
+ }
+
+ std::string arg = argv[1];
+ if (arg == "child") {
+ for (int useMark = 0; useMark <= 1; useMark++) {
+ for (int createEarly = 0; createEarly <= 1; createEarly++) {
+ runTest(useMark, createEarly);
+ }
+ }
+ printf("done...\n");
+ Sleep(1000);
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ScreenBufferTest.cc b/src/libs/3rdparty/winpty/misc/ScreenBufferTest.cc
new file mode 100644
index 0000000000..fa584b9fae
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ScreenBufferTest.cc
@@ -0,0 +1,671 @@
+//
+// Windows versions tested
+//
+// Vista Enterprise SP2 32-bit
+// - ver reports [Version 6.0.6002]
+// - kernel32.dll product/file versions are 6.0.6002.19381
+//
+// Windows 7 Ultimate SP1 32-bit
+// - ver reports [Version 6.1.7601]
+// - conhost.exe product/file versions are 6.1.7601.18847
+// - kernel32.dll product/file versions are 6.1.7601.18847
+//
+// Windows Server 2008 R2 Datacenter SP1 64-bit
+// - ver reports [Version 6.1.7601]
+// - conhost.exe product/file versions are 6.1.7601.23153
+// - kernel32.dll product/file versions are 6.1.7601.23153
+//
+// Windows 8 Enterprise 32-bit
+// - ver reports [Version 6.2.9200]
+// - conhost.exe product/file versions are 6.2.9200.16578
+// - kernel32.dll product/file versions are 6.2.9200.16859
+//
+
+//
+// Specific version details on working Server 2008 R2:
+//
+// dwMajorVersion = 6
+// dwMinorVersion = 1
+// dwBuildNumber = 7601
+// dwPlatformId = 2
+// szCSDVersion = Service Pack 1
+// wServicePackMajor = 1
+// wServicePackMinor = 0
+// wSuiteMask = 0x190
+// wProductType = 0x3
+//
+// Specific version details on broken Win7:
+//
+// dwMajorVersion = 6
+// dwMinorVersion = 1
+// dwBuildNumber = 7601
+// dwPlatformId = 2
+// szCSDVersion = Service Pack 1
+// wServicePackMajor = 1
+// wServicePackMinor = 0
+// wSuiteMask = 0x100
+// wProductType = 0x1
+//
+
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "TestUtil.cc"
+
+const char *g_prefix = "";
+
+static void dumpHandles() {
+ trace("%sSTDIN=0x%I64x STDOUT=0x%I64x STDERR=0x%I64x",
+ g_prefix,
+ (long long)GetStdHandle(STD_INPUT_HANDLE),
+ (long long)GetStdHandle(STD_OUTPUT_HANDLE),
+ (long long)GetStdHandle(STD_ERROR_HANDLE));
+}
+
+static const char *successOrFail(BOOL ret) {
+ return ret ? "ok" : "FAILED";
+}
+
+static void startChildInSameConsole(const wchar_t *args, BOOL
+ bInheritHandles=FALSE) {
+ wchar_t program[1024];
+ wchar_t cmdline[1024];
+ GetModuleFileNameW(NULL, program, 1024);
+ swprintf(cmdline, L"\"%ls\" %ls", program, args);
+
+ STARTUPINFOW sui;
+ PROCESS_INFORMATION pi;
+ memset(&sui, 0, sizeof(sui));
+ memset(&pi, 0, sizeof(pi));
+ sui.cb = sizeof(sui);
+
+ CreateProcessW(program, cmdline,
+ NULL, NULL,
+ /*bInheritHandles=*/bInheritHandles,
+ /*dwCreationFlags=*/0,
+ NULL, NULL,
+ &sui, &pi);
+}
+
+static void closeHandle(HANDLE h) {
+ trace("%sClosing handle 0x%I64x...", g_prefix, (long long)h);
+ trace("%sClosing handle 0x%I64x... %s", g_prefix, (long long)h, successOrFail(CloseHandle(h)));
+}
+
+static HANDLE createBuffer() {
+
+ // If sa isn't provided, the handle defaults to not-inheritable.
+ SECURITY_ATTRIBUTES sa = {0};
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+
+ trace("%sCreating a new buffer...", g_prefix);
+ HANDLE conout = CreateConsoleScreenBuffer(
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sa,
+ CONSOLE_TEXTMODE_BUFFER, NULL);
+
+ trace("%sCreating a new buffer... 0x%I64x", g_prefix, (long long)conout);
+ return conout;
+}
+
+static HANDLE openConout() {
+
+ // If sa isn't provided, the handle defaults to not-inheritable.
+ SECURITY_ATTRIBUTES sa = {0};
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+
+ trace("%sOpening CONOUT...", g_prefix);
+ HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sa,
+ OPEN_EXISTING, 0, NULL);
+ trace("%sOpening CONOUT... 0x%I64x", g_prefix, (long long)conout);
+ return conout;
+}
+
+static void setConsoleActiveScreenBuffer(HANDLE conout) {
+ trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called...",
+ g_prefix, (long long)conout);
+ trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called... %s",
+ g_prefix, (long long)conout,
+ successOrFail(SetConsoleActiveScreenBuffer(conout)));
+}
+
+static void writeTest(HANDLE conout, const char *msg) {
+ char writeData[256];
+ sprintf(writeData, "%s%s\n", g_prefix, msg);
+
+ trace("%sWriting to 0x%I64x: '%s'...",
+ g_prefix, (long long)conout, msg);
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleA(conout, writeData, strlen(writeData), &actual, NULL);
+ trace("%sWriting to 0x%I64x: '%s'... %s",
+ g_prefix, (long long)conout, msg,
+ successOrFail(ret && actual == strlen(writeData)));
+}
+
+static void writeTest(const char *msg) {
+ writeTest(GetStdHandle(STD_OUTPUT_HANDLE), msg);
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST 1 -- create new buffer, activate it, and close the handle. The console
+// automatically switches the screen buffer back to the original.
+//
+// This test passes everywhere.
+//
+
+static void test1(int argc, char *argv[]) {
+ if (!strcmp(argv[1], "1")) {
+ startChildProcess(L"1:child");
+ return;
+ }
+
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+ writeTest(origBuffer, "<-- origBuffer -->");
+
+ HANDLE newBuffer = createBuffer();
+ writeTest(newBuffer, "<-- newBuffer -->");
+ setConsoleActiveScreenBuffer(newBuffer);
+ Sleep(2000);
+
+ writeTest(origBuffer, "TEST PASSED!");
+
+ // Closing the handle w/o switching the active screen buffer automatically
+ // switches the console back to the original buffer.
+ closeHandle(newBuffer);
+
+ while (true) {
+ Sleep(1000);
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST 2 -- Test program that creates and activates newBuffer, starts a child
+// process, then closes its newBuffer handle. newBuffer remains activated,
+// because the child keeps it active. (Also see TEST D.)
+//
+
+static void test2(int argc, char *argv[]) {
+ if (!strcmp(argv[1], "2")) {
+ startChildProcess(L"2:parent");
+ return;
+ }
+
+ if (!strcmp(argv[1], "2:parent")) {
+ g_prefix = "parent: ";
+ dumpHandles();
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+ writeTest(origBuffer, "<-- origBuffer -->");
+
+ HANDLE newBuffer = createBuffer();
+ writeTest(newBuffer, "<-- newBuffer -->");
+ setConsoleActiveScreenBuffer(newBuffer);
+
+ Sleep(1000);
+ writeTest(newBuffer, "bInheritHandles=FALSE:");
+ startChildInSameConsole(L"2:child", FALSE);
+ Sleep(1000);
+ writeTest(newBuffer, "bInheritHandles=TRUE:");
+ startChildInSameConsole(L"2:child", TRUE);
+
+ Sleep(1000);
+ trace("parent:----");
+
+ // Close the new buffer. The active screen buffer doesn't automatically
+ // switch back to origBuffer, because the child process has a handle open
+ // to the original buffer.
+ closeHandle(newBuffer);
+
+ Sleep(600 * 1000);
+ return;
+ }
+
+ if (!strcmp(argv[1], "2:child")) {
+ g_prefix = "child: ";
+ dumpHandles();
+ // The child's output isn't visible, because it's still writing to
+ // origBuffer.
+ trace("child:----");
+ writeTest("writing to STDOUT");
+
+ // Handle inheritability is curious. The console handles this program
+ // creates are inheritable, but CreateProcess is called with both
+ // bInheritHandles=TRUE and bInheritHandles=FALSE.
+ //
+ // Vista and Windows 7: bInheritHandles has no effect. The child and
+ // parent processes have the same STDIN/STDOUT/STDERR handles:
+ // 0x3, 0x7, and 0xB. The parent has a 0xF handle for newBuffer.
+ // The child can only write to 0x7, 0xB, and 0xF. Only the writes to
+ // 0xF are visible (i.e. they touch newBuffer).
+ //
+ // Windows 8 or Windows 10 (legacy or non-legacy): the lowest 2 bits of
+ // the HANDLE to WriteConsole seem to be ignored. The new process'
+ // console handles always refer to the buffer that was active when they
+ // started, but the values of the handles depend upon bInheritHandles.
+ // With bInheritHandles=TRUE, the child has the same
+ // STDIN/STDOUT/STDERR/newBuffer handles as the parent, and the three
+ // output handles all work, though their output is all visible. With
+ // bInheritHandles=FALSE, the child has different STDIN/STDOUT/STDERR
+ // handles, and only the new STDOUT/STDERR handles work.
+ //
+ for (unsigned int i = 0x1; i <= 0xB0; ++i) {
+ char msg[256];
+ sprintf(msg, "Write to handle 0x%x", i);
+ HANDLE h = reinterpret_cast<HANDLE>(i);
+ writeTest(h, msg);
+ }
+
+ Sleep(600 * 1000);
+ return;
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST A -- demonstrate an apparent Windows bug with screen buffers
+//
+// Steps:
+// - The parent starts a child process.
+// - The child process creates and activates newBuffer
+// - The parent opens CONOUT$ and writes to it.
+// - The parent closes CONOUT$.
+// - At this point, broken Windows reactivates origBuffer.
+// - The child writes to newBuffer again.
+// - The child activates origBuffer again, then closes newBuffer.
+//
+// Test passes if the message "TEST PASSED!" is visible.
+// Test commonly fails if conhost.exe crashes.
+//
+// Results:
+// - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes
+// - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS
+// - Windows 8 Enterprise 32-bit: PASS
+// - Windows 10 64-bit (legacy and non-legacy): PASS
+//
+
+static void testA_parentWork() {
+ // Open an extra CONOUT$ handle so that the HANDLE values in parent and
+ // child don't collide. I think it's OK if they collide, but since we're
+ // trying to track down a Windows bug, it's best to avoid unnecessary
+ // complication.
+ HANDLE dummy = openConout();
+
+ Sleep(3000);
+
+ // Step 2: Open CONOUT$ in the parent. This opens the active buffer, which
+ // was just created in the child. It's handle 0x13. Write to it.
+
+ HANDLE newBuffer = openConout();
+ writeTest(newBuffer, "step2: writing to newBuffer");
+
+ Sleep(3000);
+
+ // Step 3: Close handle 0x13. With Windows 7, the console switches back to
+ // origBuffer, and (unless I'm missing something) it shouldn't.
+
+ closeHandle(newBuffer);
+}
+
+static void testA_childWork() {
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ //
+ // Step 1: Create the new screen buffer in the child process and make it
+ // active. (Typically, it's handle 0x0F.)
+ //
+
+ HANDLE newBuffer = createBuffer();
+
+ setConsoleActiveScreenBuffer(newBuffer);
+ writeTest(newBuffer, "<-- newBuffer -->");
+
+ Sleep(9000);
+ trace("child:----");
+
+ // Step 4: write to the newBuffer again.
+ writeTest(newBuffer, "TEST PASSED!");
+
+ //
+ // Step 5: Switch back to the original screen buffer and close the new
+ // buffer. The switch call succeeds, but the CloseHandle call freezes for
+ // several seconds, because conhost.exe crashes.
+ //
+ Sleep(3000);
+
+ setConsoleActiveScreenBuffer(origBuffer);
+ writeTest(origBuffer, "writing to origBuffer");
+
+ closeHandle(newBuffer);
+
+ // The console HWND is NULL.
+ trace("child: console HWND=0x%I64x", (long long)GetConsoleWindow());
+
+ // At this point, the console window has closed, but the parent/child
+ // processes are still running. Calling AllocConsole would fail, but
+ // calling FreeConsole followed by AllocConsole would both succeed, and a
+ // new console would appear.
+}
+
+static void testA(int argc, char *argv[]) {
+
+ if (!strcmp(argv[1], "A")) {
+ startChildProcess(L"A:parent");
+ return;
+ }
+
+ if (!strcmp(argv[1], "A:parent")) {
+ g_prefix = "parent: ";
+ trace("parent:----");
+ dumpHandles();
+ writeTest("<-- origBuffer -->");
+ startChildInSameConsole(L"A:child");
+ testA_parentWork();
+ Sleep(120000);
+ return;
+ }
+
+ if (!strcmp(argv[1], "A:child")) {
+ g_prefix = "child: ";
+ dumpHandles();
+ testA_childWork();
+ Sleep(120000);
+ return;
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST B -- invert TEST A -- also crashes conhost on Windows 7
+//
+// Test passes if the message "TEST PASSED!" is visible.
+// Test commonly fails if conhost.exe crashes.
+//
+// Results:
+// - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes
+// - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS
+// - Windows 8 Enterprise 32-bit: PASS
+// - Windows 10 64-bit (legacy and non-legacy): PASS
+//
+
+static void testB(int argc, char *argv[]) {
+ if (!strcmp(argv[1], "B")) {
+ startChildProcess(L"B:parent");
+ return;
+ }
+
+ if (!strcmp(argv[1], "B:parent")) {
+ g_prefix = "parent: ";
+ startChildInSameConsole(L"B:child");
+ writeTest("<-- origBuffer -->");
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ //
+ // Step 1: Create the new buffer and make it active.
+ //
+ trace("%s----", g_prefix);
+ HANDLE newBuffer = createBuffer();
+ setConsoleActiveScreenBuffer(newBuffer);
+ writeTest(newBuffer, "<-- newBuffer -->");
+
+ //
+ // Step 4: Attempt to write again to the new buffer.
+ //
+ Sleep(9000);
+ trace("%s----", g_prefix);
+ writeTest(newBuffer, "TEST PASSED!");
+
+ //
+ // Step 5: Switch back to the original buffer.
+ //
+ Sleep(3000);
+ trace("%s----", g_prefix);
+ setConsoleActiveScreenBuffer(origBuffer);
+ closeHandle(newBuffer);
+ writeTest(origBuffer, "writing to the initial buffer");
+
+ Sleep(60000);
+ return;
+ }
+
+ if (!strcmp(argv[1], "B:child")) {
+ g_prefix = "child: ";
+ Sleep(3000);
+ trace("%s----", g_prefix);
+
+ //
+ // Step 2: Open the newly active buffer and write to it.
+ //
+ HANDLE newBuffer = openConout();
+ writeTest(newBuffer, "writing to newBuffer");
+
+ //
+ // Step 3: Close the newly active buffer.
+ //
+ Sleep(3000);
+ closeHandle(newBuffer);
+
+ Sleep(60000);
+ return;
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST C -- Interleaving open/close of console handles also seems to break on
+// Windows 7.
+//
+// Test:
+// - child creates and activates newBuf1
+// - parent opens newBuf1
+// - child creates and activates newBuf2
+// - parent opens newBuf2, then closes newBuf1
+// - child switches back to newBuf1
+// * At this point, the console starts malfunctioning.
+// - parent and child close newBuf2
+// - child closes newBuf1
+//
+// Test passes if the message "TEST PASSED!" is visible.
+// Test commonly fails if conhost.exe crashes.
+//
+// Results:
+// - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes
+// - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS
+// - Windows 8 Enterprise 32-bit: PASS
+// - Windows 10 64-bit (legacy and non-legacy): PASS
+//
+
+static void testC(int argc, char *argv[]) {
+ if (!strcmp(argv[1], "C")) {
+ startChildProcess(L"C:parent");
+ return;
+ }
+
+ if (!strcmp(argv[1], "C:parent")) {
+ startChildInSameConsole(L"C:child");
+ writeTest("<-- origBuffer -->");
+ g_prefix = "parent: ";
+
+ // At time=4, open newBuffer1.
+ Sleep(4000);
+ trace("%s---- t=4", g_prefix);
+ const HANDLE newBuffer1 = openConout();
+
+ // At time=8, open newBuffer2, and close newBuffer1.
+ Sleep(4000);
+ trace("%s---- t=8", g_prefix);
+ const HANDLE newBuffer2 = openConout();
+ closeHandle(newBuffer1);
+
+ // At time=25, cleanup of newBuffer2.
+ Sleep(17000);
+ trace("%s---- t=25", g_prefix);
+ closeHandle(newBuffer2);
+
+ Sleep(240000);
+ return;
+ }
+
+ if (!strcmp(argv[1], "C:child")) {
+ g_prefix = "child: ";
+
+ // At time=2, create newBuffer1 and activate it.
+ Sleep(2000);
+ trace("%s---- t=2", g_prefix);
+ const HANDLE newBuffer1 = createBuffer();
+ setConsoleActiveScreenBuffer(newBuffer1);
+ writeTest(newBuffer1, "<-- newBuffer1 -->");
+
+ // At time=6, create newBuffer2 and activate it.
+ Sleep(4000);
+ trace("%s---- t=6", g_prefix);
+ const HANDLE newBuffer2 = createBuffer();
+ setConsoleActiveScreenBuffer(newBuffer2);
+ writeTest(newBuffer2, "<-- newBuffer2 -->");
+
+ // At time=10, attempt to switch back to newBuffer1. The parent process
+ // has opened and closed its handle to newBuffer1, so does it still exist?
+ Sleep(4000);
+ trace("%s---- t=10", g_prefix);
+ setConsoleActiveScreenBuffer(newBuffer1);
+ writeTest(newBuffer1, "write to newBuffer1: TEST PASSED!");
+
+ // At time=25, cleanup of newBuffer2.
+ Sleep(15000);
+ trace("%s---- t=25", g_prefix);
+ closeHandle(newBuffer2);
+
+ // At time=35, cleanup of newBuffer1. The console should switch to the
+ // initial buffer again.
+ Sleep(10000);
+ trace("%s---- t=35", g_prefix);
+ closeHandle(newBuffer1);
+
+ Sleep(240000);
+ return;
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TEST D -- parent creates a new buffer, child launches, writes,
+// closes it output handle, then parent writes again. (Also see TEST 2.)
+//
+// On success, this will appear:
+//
+// parent: <-- newBuffer -->
+// child: writing to newBuffer
+// parent: TEST PASSED!
+//
+// If this appears, it indicates that the child's closing its output handle did
+// not destroy newBuffer.
+//
+// Results:
+// - Windows 7 Ultimate SP1 32-bit: PASS
+// - Windows 8 Enterprise 32-bit: PASS
+// - Windows 10 64-bit (legacy and non-legacy): PASS
+//
+
+static void testD(int argc, char *argv[]) {
+ if (!strcmp(argv[1], "D")) {
+ startChildProcess(L"D:parent");
+ return;
+ }
+
+ if (!strcmp(argv[1], "D:parent")) {
+ g_prefix = "parent: ";
+ HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
+ writeTest(origBuffer, "<-- origBuffer -->");
+
+ HANDLE newBuffer = createBuffer();
+ writeTest(newBuffer, "<-- newBuffer -->");
+ setConsoleActiveScreenBuffer(newBuffer);
+
+ // At t=2, start a child process, explicitly forcing it to use
+ // newBuffer for its standard handles. These calls are apparently
+ // redundant on Windows 8 and up.
+ Sleep(2000);
+ trace("parent:----");
+ trace("parent: starting child process");
+ SetStdHandle(STD_OUTPUT_HANDLE, newBuffer);
+ SetStdHandle(STD_ERROR_HANDLE, newBuffer);
+ startChildInSameConsole(L"D:child");
+ SetStdHandle(STD_OUTPUT_HANDLE, origBuffer);
+ SetStdHandle(STD_ERROR_HANDLE, origBuffer);
+
+ // At t=6, write again to newBuffer.
+ Sleep(4000);
+ trace("parent:----");
+ writeTest(newBuffer, "TEST PASSED!");
+
+ // At t=8, close the newBuffer. In earlier versions of windows
+ // (including Server 2008 R2), the console then switches back to
+ // origBuffer. As of Windows 8, it doesn't, because somehow the child
+ // process is keeping the console on newBuffer, even though the child
+ // process closed its STDIN/STDOUT/STDERR handles. Killing the child
+ // process by hand after the test finishes *does* force the console
+ // back to origBuffer.
+ Sleep(2000);
+ closeHandle(newBuffer);
+
+ Sleep(120000);
+ return;
+ }
+
+ if (!strcmp(argv[1], "D:child")) {
+ g_prefix = "child: ";
+ // At t=2, the child starts.
+ trace("child:----");
+ dumpHandles();
+ writeTest("writing to newBuffer");
+
+ // At t=4, the child explicitly closes its handle.
+ Sleep(2000);
+ trace("child:----");
+ if (GetStdHandle(STD_ERROR_HANDLE) != GetStdHandle(STD_OUTPUT_HANDLE)) {
+ closeHandle(GetStdHandle(STD_ERROR_HANDLE));
+ }
+ closeHandle(GetStdHandle(STD_OUTPUT_HANDLE));
+ closeHandle(GetStdHandle(STD_INPUT_HANDLE));
+
+ Sleep(120000);
+ return;
+ }
+}
+
+
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ printf("USAGE: %s testnum\n", argv[0]);
+ return 0;
+ }
+
+ if (argv[1][0] == '1') {
+ test1(argc, argv);
+ } else if (argv[1][0] == '2') {
+ test2(argc, argv);
+ } else if (argv[1][0] == 'A') {
+ testA(argc, argv);
+ } else if (argv[1][0] == 'B') {
+ testB(argc, argv);
+ } else if (argv[1][0] == 'C') {
+ testC(argc, argv);
+ } else if (argv[1][0] == 'D') {
+ testD(argc, argv);
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ScreenBufferTest2.cc b/src/libs/3rdparty/winpty/misc/ScreenBufferTest2.cc
new file mode 100644
index 0000000000..2b648c9409
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ScreenBufferTest2.cc
@@ -0,0 +1,151 @@
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+const char *g_prefix = "";
+
+static void dumpHandles() {
+ trace("%sSTDIN=0x%I64x STDOUT=0x%I64x STDERR=0x%I64x",
+ g_prefix,
+ (long long)GetStdHandle(STD_INPUT_HANDLE),
+ (long long)GetStdHandle(STD_OUTPUT_HANDLE),
+ (long long)GetStdHandle(STD_ERROR_HANDLE));
+}
+
+static HANDLE createBuffer() {
+
+ // If sa isn't provided, the handle defaults to not-inheritable.
+ SECURITY_ATTRIBUTES sa = {0};
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+
+ trace("%sCreating a new buffer...", g_prefix);
+ HANDLE conout = CreateConsoleScreenBuffer(
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sa,
+ CONSOLE_TEXTMODE_BUFFER, NULL);
+
+ trace("%sCreating a new buffer... 0x%I64x", g_prefix, (long long)conout);
+ return conout;
+}
+
+static const char *successOrFail(BOOL ret) {
+ return ret ? "ok" : "FAILED";
+}
+
+static void setConsoleActiveScreenBuffer(HANDLE conout) {
+ trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called...",
+ g_prefix, (long long)conout);
+ trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called... %s",
+ g_prefix, (long long)conout,
+ successOrFail(SetConsoleActiveScreenBuffer(conout)));
+}
+
+static void writeTest(HANDLE conout, const char *msg) {
+ char writeData[256];
+ sprintf(writeData, "%s%s\n", g_prefix, msg);
+
+ trace("%sWriting to 0x%I64x: '%s'...",
+ g_prefix, (long long)conout, msg);
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleA(conout, writeData, strlen(writeData), &actual, NULL);
+ trace("%sWriting to 0x%I64x: '%s'... %s",
+ g_prefix, (long long)conout, msg,
+ successOrFail(ret && actual == strlen(writeData)));
+}
+
+static HANDLE startChildInSameConsole(const wchar_t *args, BOOL
+ bInheritHandles=FALSE) {
+ wchar_t program[1024];
+ wchar_t cmdline[1024];
+ GetModuleFileNameW(NULL, program, 1024);
+ swprintf(cmdline, L"\"%ls\" %ls", program, args);
+
+ STARTUPINFOW sui;
+ PROCESS_INFORMATION pi;
+ memset(&sui, 0, sizeof(sui));
+ memset(&pi, 0, sizeof(pi));
+ sui.cb = sizeof(sui);
+
+ CreateProcessW(program, cmdline,
+ NULL, NULL,
+ /*bInheritHandles=*/bInheritHandles,
+ /*dwCreationFlags=*/0,
+ NULL, NULL,
+ &sui, &pi);
+
+ return pi.hProcess;
+}
+
+static HANDLE dup(HANDLE h, HANDLE targetProcess) {
+ HANDLE h2 = INVALID_HANDLE_VALUE;
+ BOOL ret = DuplicateHandle(
+ GetCurrentProcess(), h,
+ targetProcess, &h2,
+ 0, TRUE, DUPLICATE_SAME_ACCESS);
+ trace("dup(0x%I64x) to process 0x%I64x... %s, 0x%I64x",
+ (long long)h,
+ (long long)targetProcess,
+ successOrFail(ret),
+ (long long)h2);
+ return h2;
+}
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"parent");
+ return 0;
+ }
+
+ if (!strcmp(argv[1], "parent")) {
+ g_prefix = "parent: ";
+ dumpHandles();
+ HANDLE hChild = startChildInSameConsole(L"child");
+
+ // Windows 10.
+ HANDLE orig1 = GetStdHandle(STD_OUTPUT_HANDLE);
+ HANDLE new1 = createBuffer();
+
+ Sleep(2000);
+ setConsoleActiveScreenBuffer(new1);
+
+ // Handle duplication results to child process in same console:
+ // - Windows XP: fails
+ // - Windows 7 Ultimate SP1 32-bit: fails
+ // - Windows Server 2008 R2 Datacenter SP1 64-bit: fails
+ // - Windows 8 Enterprise 32-bit: succeeds
+ // - Windows 10: succeeds
+ HANDLE orig2 = dup(orig1, GetCurrentProcess());
+ HANDLE new2 = dup(new1, GetCurrentProcess());
+
+ dup(orig1, hChild);
+ dup(new1, hChild);
+
+ // The writes to orig1/orig2 are invisible. The writes to new1/new2
+ // are visible.
+ writeTest(orig1, "write to orig1");
+ writeTest(orig2, "write to orig2");
+ writeTest(new1, "write to new1");
+ writeTest(new2, "write to new2");
+
+ Sleep(120000);
+ return 0;
+ }
+
+ if (!strcmp(argv[1], "child")) {
+ g_prefix = "child: ";
+ dumpHandles();
+ Sleep(4000);
+ for (unsigned int i = 0x1; i <= 0xB0; ++i) {
+ char msg[256];
+ sprintf(msg, "Write to handle 0x%x", i);
+ HANDLE h = reinterpret_cast<HANDLE>(i);
+ writeTest(h, msg);
+ }
+ Sleep(120000);
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SelectAllTest.cc b/src/libs/3rdparty/winpty/misc/SelectAllTest.cc
new file mode 100644
index 0000000000..a6c27739d8
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SelectAllTest.cc
@@ -0,0 +1,45 @@
+#define _WIN32_WINNT 0x0501
+#include <stdio.h>
+#include <windows.h>
+
+#include "../src/shared/DebugClient.cc"
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+
+CALLBACK DWORD pausingThread(LPVOID dummy)
+{
+ HWND hwnd = GetConsoleWindow();
+ while (true) {
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_SELECT_ALL, 0);
+ Sleep(1000);
+ SendMessage(hwnd, WM_CHAR, 27, 0x00010001);
+ Sleep(1000);
+ }
+}
+
+int main()
+{
+ HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO info;
+
+ GetConsoleScreenBufferInfo(out, &info);
+ COORD initial = info.dwCursorPosition;
+
+ CreateThread(NULL, 0,
+ pausingThread, NULL,
+ 0, NULL);
+
+ for (int i = 0; i < 30; ++i) {
+ Sleep(100);
+ GetConsoleScreenBufferInfo(out, &info);
+ if (memcmp(&info.dwCursorPosition, &initial, sizeof(COORD)) != 0) {
+ trace("cursor moved to [%d,%d]",
+ info.dwCursorPosition.X,
+ info.dwCursorPosition.Y);
+ } else {
+ trace("cursor in expected position");
+ }
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SetBufInfo.cc b/src/libs/3rdparty/winpty/misc/SetBufInfo.cc
new file mode 100644
index 0000000000..f37c31bdf7
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SetBufInfo.cc
@@ -0,0 +1,90 @@
+#include <windows.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "TestUtil.cc"
+
+static void usage() {
+ printf("usage: SetBufInfo [-set] [-buf W H] [-win W H] [-pos X Y]\n");
+}
+
+int main(int argc, char *argv[]) {
+ const HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+
+ bool change = false;
+ BOOL success;
+ CONSOLE_SCREEN_BUFFER_INFOEX info = {};
+ info.cbSize = sizeof(info);
+
+ success = GetConsoleScreenBufferInfoEx(conout, &info);
+ ASSERT(success && "GetConsoleScreenBufferInfoEx failed");
+
+ for (int i = 1; i < argc; ) {
+ std::string arg = argv[i];
+ if (arg == "-buf" && (i + 2) < argc) {
+ info.dwSize.X = atoi(argv[i + 1]);
+ info.dwSize.Y = atoi(argv[i + 2]);
+ i += 3;
+ change = true;
+ } else if (arg == "-pos" && (i + 2) < argc) {
+ int dx = info.srWindow.Right - info.srWindow.Left;
+ int dy = info.srWindow.Bottom - info.srWindow.Top;
+ info.srWindow.Left = atoi(argv[i + 1]);
+ info.srWindow.Top = atoi(argv[i + 2]);
+ i += 3;
+ info.srWindow.Right = info.srWindow.Left + dx;
+ info.srWindow.Bottom = info.srWindow.Top + dy;
+ change = true;
+ } else if (arg == "-win" && (i + 2) < argc) {
+ info.srWindow.Right = info.srWindow.Left + atoi(argv[i + 1]) - 1;
+ info.srWindow.Bottom = info.srWindow.Top + atoi(argv[i + 2]) - 1;
+ i += 3;
+ change = true;
+ } else if (arg == "-set") {
+ change = true;
+ ++i;
+ } else if (arg == "--help" || arg == "-help") {
+ usage();
+ exit(0);
+ } else {
+ fprintf(stderr, "error: unrecognized argument: %s\n", arg.c_str());
+ usage();
+ exit(1);
+ }
+ }
+
+ if (change) {
+ success = SetConsoleScreenBufferInfoEx(conout, &info);
+ if (success) {
+ printf("success\n");
+ } else {
+ printf("SetConsoleScreenBufferInfoEx call failed\n");
+ }
+ success = GetConsoleScreenBufferInfoEx(conout, &info);
+ ASSERT(success && "GetConsoleScreenBufferInfoEx failed");
+ }
+
+ auto dump = [](const char *fmt, ...) {
+ char msg[256];
+ va_list ap;
+ va_start(ap, fmt);
+ vsprintf(msg, fmt, ap);
+ va_end(ap);
+ trace("%s", msg);
+ printf("%s\n", msg);
+ };
+
+ dump("buffer-size: %d x %d", info.dwSize.X, info.dwSize.Y);
+ dump("window-size: %d x %d",
+ info.srWindow.Right - info.srWindow.Left + 1,
+ info.srWindow.Bottom - info.srWindow.Top + 1);
+ dump("window-pos: %d, %d", info.srWindow.Left, info.srWindow.Top);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SetBufferSize.cc b/src/libs/3rdparty/winpty/misc/SetBufferSize.cc
new file mode 100644
index 0000000000..b50a1f8dc3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SetBufferSize.cc
@@ -0,0 +1,32 @@
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc != 3) {
+ printf("Usage: %s x y width height\n", argv[0]);
+ return 1;
+ }
+
+ const HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+
+ COORD size = {
+ (short)atoi(argv[1]),
+ (short)atoi(argv[2]),
+ };
+
+ BOOL ret = SetConsoleScreenBufferSize(conout, size);
+ const unsigned lastError = GetLastError();
+ const char *const retStr = ret ? "OK" : "failed";
+ trace("SetConsoleScreenBufferSize ret: %s (LastError=0x%x)", retStr, lastError);
+ printf("SetConsoleScreenBufferSize ret: %s (LastError=0x%x)\n", retStr, lastError);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SetCursorPos.cc b/src/libs/3rdparty/winpty/misc/SetCursorPos.cc
new file mode 100644
index 0000000000..d20fdbdfc0
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SetCursorPos.cc
@@ -0,0 +1,10 @@
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ int col = atoi(argv[1]);
+ int row = atoi(argv[2]);
+ setCursorPos(col, row);
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SetFont.cc b/src/libs/3rdparty/winpty/misc/SetFont.cc
new file mode 100644
index 0000000000..9bcd4b4cc9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SetFont.cc
@@ -0,0 +1,145 @@
+#include <windows.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+#include "TestUtil.cc"
+
+#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
+
+// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
+const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese
+const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese
+const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese
+const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean
+
+int main() {
+ setlocale(LC_ALL, "");
+ wchar_t *cmdline = GetCommandLineW();
+ int argc = 0;
+ wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
+ const HANDLE conout = openConout();
+
+ if (argc == 1) {
+ cprintf(L"Usage:\n");
+ cprintf(L" SetFont <index>\n");
+ cprintf(L" SetFont options\n");
+ cprintf(L"\n");
+ cprintf(L"Options for SetCurrentConsoleFontEx:\n");
+ cprintf(L" -idx INDEX\n");
+ cprintf(L" -w WIDTH\n");
+ cprintf(L" -h HEIGHT\n");
+ cprintf(L" -family (0xNN|NN)\n");
+ cprintf(L" -weight (normal|bold|NNN)\n");
+ cprintf(L" -face FACENAME\n");
+ cprintf(L" -face-{gothic|simsun|minglight|gulimche) [JP,CN-sim,CN-tra,KR]\n");
+ cprintf(L" -tt\n");
+ cprintf(L" -vec\n");
+ cprintf(L" -vp\n");
+ cprintf(L" -dev\n");
+ cprintf(L" -roman\n");
+ cprintf(L" -swiss\n");
+ cprintf(L" -modern\n");
+ cprintf(L" -script\n");
+ cprintf(L" -decorative\n");
+ return 0;
+ }
+
+ if (isdigit(argv[1][0])) {
+ int index = _wtoi(argv[1]);
+ HMODULE kernel32 = LoadLibraryW(L"kernel32.dll");
+ FARPROC proc = GetProcAddress(kernel32, "SetConsoleFont");
+ if (proc == NULL) {
+ cprintf(L"Couldn't get address of SetConsoleFont\n");
+ } else {
+ BOOL ret = reinterpret_cast<BOOL WINAPI(*)(HANDLE, DWORD)>(proc)(
+ conout, index);
+ cprintf(L"SetFont returned %d\n", ret);
+ }
+ return 0;
+ }
+
+ CONSOLE_FONT_INFOEX fontex = {0};
+ fontex.cbSize = sizeof(fontex);
+
+ for (int i = 1; i < argc; ++i) {
+ std::wstring arg = argv[i];
+ if (i + 1 < argc) {
+ std::wstring next = argv[i + 1];
+ if (arg == L"-idx") {
+ fontex.nFont = _wtoi(next.c_str());
+ ++i; continue;
+ } else if (arg == L"-w") {
+ fontex.dwFontSize.X = _wtoi(next.c_str());
+ ++i; continue;
+ } else if (arg == L"-h") {
+ fontex.dwFontSize.Y = _wtoi(next.c_str());
+ ++i; continue;
+ } else if (arg == L"-weight") {
+ if (next == L"normal") {
+ fontex.FontWeight = 400;
+ } else if (next == L"bold") {
+ fontex.FontWeight = 700;
+ } else {
+ fontex.FontWeight = _wtoi(next.c_str());
+ }
+ ++i; continue;
+ } else if (arg == L"-face") {
+ wcsncpy(fontex.FaceName, next.c_str(), COUNT_OF(fontex.FaceName));
+ ++i; continue;
+ } else if (arg == L"-family") {
+ fontex.FontFamily = strtol(narrowString(next).c_str(), nullptr, 0);
+ ++i; continue;
+ }
+ }
+ if (arg == L"-tt") {
+ fontex.FontFamily |= TMPF_TRUETYPE;
+ } else if (arg == L"-vec") {
+ fontex.FontFamily |= TMPF_VECTOR;
+ } else if (arg == L"-vp") {
+ // Setting the TMPF_FIXED_PITCH bit actually indicates variable
+ // pitch.
+ fontex.FontFamily |= TMPF_FIXED_PITCH;
+ } else if (arg == L"-dev") {
+ fontex.FontFamily |= TMPF_DEVICE;
+ } else if (arg == L"-roman") {
+ fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_ROMAN;
+ } else if (arg == L"-swiss") {
+ fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_SWISS;
+ } else if (arg == L"-modern") {
+ fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_MODERN;
+ } else if (arg == L"-script") {
+ fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_SCRIPT;
+ } else if (arg == L"-decorative") {
+ fontex.FontFamily = (fontex.FontFamily & ~0xF0) | FF_DECORATIVE;
+ } else if (arg == L"-face-gothic") {
+ wcsncpy(fontex.FaceName, kMSGothic, COUNT_OF(fontex.FaceName));
+ } else if (arg == L"-face-simsun") {
+ wcsncpy(fontex.FaceName, kNSimSun, COUNT_OF(fontex.FaceName));
+ } else if (arg == L"-face-minglight") {
+ wcsncpy(fontex.FaceName, kMingLight, COUNT_OF(fontex.FaceName));
+ } else if (arg == L"-face-gulimche") {
+ wcsncpy(fontex.FaceName, kGulimChe, COUNT_OF(fontex.FaceName));
+ } else {
+ cprintf(L"Unrecognized argument: %ls\n", arg.c_str());
+ exit(1);
+ }
+ }
+
+ cprintf(L"Setting to: nFont=%u dwFontSize=(%d,%d) "
+ L"FontFamily=0x%x FontWeight=%u "
+ L"FaceName=\"%ls\"\n",
+ static_cast<unsigned>(fontex.nFont),
+ fontex.dwFontSize.X, fontex.dwFontSize.Y,
+ fontex.FontFamily, fontex.FontWeight,
+ fontex.FaceName);
+
+ BOOL ret = SetCurrentConsoleFontEx(
+ conout,
+ FALSE,
+ &fontex);
+ cprintf(L"SetCurrentConsoleFontEx returned %d\n", ret);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/SetWindowRect.cc b/src/libs/3rdparty/winpty/misc/SetWindowRect.cc
new file mode 100644
index 0000000000..6291dd6745
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/SetWindowRect.cc
@@ -0,0 +1,36 @@
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc != 5) {
+ printf("Usage: %s x y width height\n", argv[0]);
+ return 1;
+ }
+
+ const HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+
+ SMALL_RECT sr = {
+ (short)atoi(argv[1]),
+ (short)atoi(argv[2]),
+ (short)(atoi(argv[1]) + atoi(argv[3]) - 1),
+ (short)(atoi(argv[2]) + atoi(argv[4]) - 1),
+ };
+
+ trace("Calling SetConsoleWindowInfo with {L=%d,T=%d,R=%d,B=%d}",
+ sr.Left, sr.Top, sr.Right, sr.Bottom);
+ BOOL ret = SetConsoleWindowInfo(conout, TRUE, &sr);
+ const unsigned lastError = GetLastError();
+ const char *const retStr = ret ? "OK" : "failed";
+ trace("SetConsoleWindowInfo ret: %s (LastError=0x%x)", retStr, lastError);
+ printf("SetConsoleWindowInfo ret: %s (LastError=0x%x)\n", retStr, lastError);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ShowArgv.cc b/src/libs/3rdparty/winpty/misc/ShowArgv.cc
new file mode 100644
index 0000000000..29a0f09131
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ShowArgv.cc
@@ -0,0 +1,12 @@
+// This test program is useful for studying commandline<->argv conversion.
+
+#include <stdio.h>
+#include <windows.h>
+
+int main(int argc, char **argv)
+{
+ printf("cmdline = [%s]\n", GetCommandLine());
+ for (int i = 0; i < argc; ++i)
+ printf("[%s]\n", argv[i]);
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/ShowConsoleInput.cc b/src/libs/3rdparty/winpty/misc/ShowConsoleInput.cc
new file mode 100644
index 0000000000..75fbfb81f1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/ShowConsoleInput.cc
@@ -0,0 +1,40 @@
+#include <windows.h>
+#include <stdio.h>
+#include <ctype.h>
+
+int main(int argc, char *argv[])
+{
+ static int escCount = 0;
+
+ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
+ while (true) {
+ DWORD count;
+ INPUT_RECORD ir;
+ if (!ReadConsoleInput(hStdin, &ir, 1, &count)) {
+ printf("ReadConsoleInput failed\n");
+ return 1;
+ }
+
+ if (true) {
+ DWORD mode;
+ GetConsoleMode(hStdin, &mode);
+ SetConsoleMode(hStdin, mode & ~ENABLE_PROCESSED_INPUT);
+ }
+
+ if (ir.EventType == KEY_EVENT) {
+ const KEY_EVENT_RECORD &ker = ir.Event.KeyEvent;
+ printf("%s", ker.bKeyDown ? "dn" : "up");
+ printf(" ch=");
+ if (isprint(ker.uChar.AsciiChar))
+ printf("'%c'", ker.uChar.AsciiChar);
+ printf("%d", ker.uChar.AsciiChar);
+ printf(" vk=%#x", ker.wVirtualKeyCode);
+ printf(" scan=%#x", ker.wVirtualScanCode);
+ printf(" state=%#x", (int)ker.dwControlKeyState);
+ printf(" repeat=%d", ker.wRepeatCount);
+ printf("\n");
+ if (ker.uChar.AsciiChar == 27 && ++escCount == 6)
+ break;
+ }
+ }
+}
diff --git a/src/libs/3rdparty/winpty/misc/Spew.py b/src/libs/3rdparty/winpty/misc/Spew.py
new file mode 100644
index 0000000000..9d1796af37
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Spew.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+i = 0;
+while True:
+ i += 1
+ print(i)
diff --git a/src/libs/3rdparty/winpty/misc/TestUtil.cc b/src/libs/3rdparty/winpty/misc/TestUtil.cc
new file mode 100644
index 0000000000..c832a12b85
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/TestUtil.cc
@@ -0,0 +1,172 @@
+// This file is included into test programs using #include
+
+#include <windows.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <vector>
+#include <string>
+
+#include "../src/shared/DebugClient.h"
+#include "../src/shared/TimeMeasurement.h"
+
+#include "../src/shared/DebugClient.cc"
+#include "../src/shared/WinptyAssert.cc"
+#include "../src/shared/WinptyException.cc"
+
+// Launch this test program again, in a new console that we will destroy.
+static void startChildProcess(const wchar_t *args) {
+ wchar_t program[1024];
+ wchar_t cmdline[1024];
+ GetModuleFileNameW(NULL, program, 1024);
+ swprintf(cmdline, L"\"%ls\" %ls", program, args);
+
+ STARTUPINFOW sui;
+ PROCESS_INFORMATION pi;
+ memset(&sui, 0, sizeof(sui));
+ memset(&pi, 0, sizeof(pi));
+ sui.cb = sizeof(sui);
+
+ CreateProcessW(program, cmdline,
+ NULL, NULL,
+ /*bInheritHandles=*/FALSE,
+ /*dwCreationFlags=*/CREATE_NEW_CONSOLE,
+ NULL, NULL,
+ &sui, &pi);
+}
+
+static void setBufferSize(HANDLE conout, int x, int y) {
+ COORD size = { static_cast<SHORT>(x), static_cast<SHORT>(y) };
+ BOOL success = SetConsoleScreenBufferSize(conout, size);
+ trace("setBufferSize: (%d,%d), result=%d", x, y, success);
+}
+
+static void setWindowPos(HANDLE conout, int x, int y, int w, int h) {
+ SMALL_RECT r = {
+ static_cast<SHORT>(x), static_cast<SHORT>(y),
+ static_cast<SHORT>(x + w - 1),
+ static_cast<SHORT>(y + h - 1)
+ };
+ BOOL success = SetConsoleWindowInfo(conout, /*bAbsolute=*/TRUE, &r);
+ trace("setWindowPos: (%d,%d,%d,%d), result=%d", x, y, w, h, success);
+}
+
+static void setCursorPos(HANDLE conout, int x, int y) {
+ COORD coord = { static_cast<SHORT>(x), static_cast<SHORT>(y) };
+ SetConsoleCursorPosition(conout, coord);
+}
+
+static void setBufferSize(int x, int y) {
+ setBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), x, y);
+}
+
+static void setWindowPos(int x, int y, int w, int h) {
+ setWindowPos(GetStdHandle(STD_OUTPUT_HANDLE), x, y, w, h);
+}
+
+static void setCursorPos(int x, int y) {
+ setCursorPos(GetStdHandle(STD_OUTPUT_HANDLE), x, y);
+}
+
+static void countDown(int sec) {
+ for (int i = sec; i > 0; --i) {
+ printf("%d.. ", i);
+ fflush(stdout);
+ Sleep(1000);
+ }
+ printf("\n");
+}
+
+static void writeBox(int x, int y, int w, int h, char ch, int attributes=7) {
+ CHAR_INFO info = { 0 };
+ info.Char.AsciiChar = ch;
+ info.Attributes = attributes;
+ std::vector<CHAR_INFO> buf(w * h, info);
+ HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+ COORD bufSize = { static_cast<SHORT>(w), static_cast<SHORT>(h) };
+ COORD bufCoord = { 0, 0 };
+ SMALL_RECT writeRegion = {
+ static_cast<SHORT>(x),
+ static_cast<SHORT>(y),
+ static_cast<SHORT>(x + w - 1),
+ static_cast<SHORT>(y + h - 1)
+ };
+ WriteConsoleOutputA(conout, buf.data(), bufSize, bufCoord, &writeRegion);
+}
+
+static void setChar(int x, int y, char ch, int attributes=7) {
+ writeBox(x, y, 1, 1, ch, attributes);
+}
+
+static void fillChar(int x, int y, int repeat, char ch) {
+ COORD coord = { static_cast<SHORT>(x), static_cast<SHORT>(y) };
+ DWORD actual = 0;
+ FillConsoleOutputCharacterA(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ ch, repeat, coord, &actual);
+}
+
+static void repeatChar(int count, char ch) {
+ for (int i = 0; i < count; ++i) {
+ putchar(ch);
+ }
+ fflush(stdout);
+}
+
+// I don't know why, but wprintf fails to print this face name,
+// "MS ゴシック" (aka MS Gothic). It helps to use wprintf instead of printf, and
+// it helps to call `setlocale(LC_ALL, "")`, but the Japanese symbols are
+// ultimately converted to `?` symbols, even though MS Gothic is able to
+// display its own name, and the current code page is 932 (Shift-JIS).
+static void cvfprintf(HANDLE conout, const wchar_t *fmt, va_list ap) {
+ wchar_t buffer[256];
+ vswprintf(buffer, 256 - 1, fmt, ap);
+ buffer[255] = L'\0';
+ DWORD actual = 0;
+ if (!WriteConsoleW(conout, buffer, wcslen(buffer), &actual, NULL)) {
+ wprintf(L"WriteConsoleW call failed!\n");
+ }
+}
+
+static void cfprintf(HANDLE conout, const wchar_t *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ cvfprintf(conout, fmt, ap);
+ va_end(ap);
+}
+
+static void cprintf(const wchar_t *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ cvfprintf(GetStdHandle(STD_OUTPUT_HANDLE), fmt, ap);
+ va_end(ap);
+}
+
+static std::string narrowString(const std::wstring &input)
+{
+ int mblen = WideCharToMultiByte(
+ CP_UTF8, 0,
+ input.data(), input.size(),
+ NULL, 0, NULL, NULL);
+ if (mblen <= 0) {
+ return std::string();
+ }
+ std::vector<char> tmp(mblen);
+ int mblen2 = WideCharToMultiByte(
+ CP_UTF8, 0,
+ input.data(), input.size(),
+ tmp.data(), tmp.size(),
+ NULL, NULL);
+ assert(mblen2 == mblen);
+ return std::string(tmp.data(), tmp.size());
+}
+
+HANDLE openConout() {
+ const HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+ return conout;
+}
diff --git a/src/libs/3rdparty/winpty/misc/UnicodeDoubleWidthTest.cc b/src/libs/3rdparty/winpty/misc/UnicodeDoubleWidthTest.cc
new file mode 100644
index 0000000000..7210d41032
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/UnicodeDoubleWidthTest.cc
@@ -0,0 +1,102 @@
+// Demonstrates how U+30FC is sometimes handled as a single-width character
+// when it should be handled as a double-width character.
+//
+// It only runs on computers where 932 is a valid code page. Set the system
+// local to "Japanese (Japan)" to ensure this.
+//
+// The problem seems to happen when U+30FC is printed in a console using the
+// Lucida Console font, and only when that font is at certain sizes.
+//
+
+#include <windows.h>
+#include <assert.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "TestUtil.cc"
+
+#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
+
+static void setFont(const wchar_t *faceName, int pxSize) {
+ CONSOLE_FONT_INFOEX infoex = {0};
+ infoex.cbSize = sizeof(infoex);
+ infoex.dwFontSize.Y = pxSize;
+ wcsncpy(infoex.FaceName, faceName, COUNT_OF(infoex.FaceName));
+ BOOL ret = SetCurrentConsoleFontEx(
+ GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &infoex);
+ assert(ret);
+}
+
+static bool performTest(const wchar_t testChar) {
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ SetConsoleTextAttribute(conout, 7);
+
+ system("cls");
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleW(conout, &testChar, 1, &actual, NULL);
+ assert(ret && actual == 1);
+
+ CHAR_INFO verify[2];
+ COORD bufSize = {2, 1};
+ COORD bufCoord = {0, 0};
+ const SMALL_RECT readRegion = {0, 0, 1, 0};
+ SMALL_RECT actualRegion = readRegion;
+ ret = ReadConsoleOutputW(conout, verify, bufSize, bufCoord, &actualRegion);
+ assert(ret && !memcmp(&readRegion, &actualRegion, sizeof(readRegion)));
+ assert(verify[0].Char.UnicodeChar == testChar);
+
+ if (verify[1].Char.UnicodeChar == testChar) {
+ // Typical double-width behavior with a TrueType font. Pass.
+ assert(verify[0].Attributes == 0x107);
+ assert(verify[1].Attributes == 0x207);
+ return true;
+ } else if (verify[1].Char.UnicodeChar == 0) {
+ // Typical double-width behavior with a Raster Font. Pass.
+ assert(verify[0].Attributes == 7);
+ assert(verify[1].Attributes == 0);
+ return true;
+ } else if (verify[1].Char.UnicodeChar == L' ') {
+ // Single-width behavior. Fail.
+ assert(verify[0].Attributes == 7);
+ assert(verify[1].Attributes == 7);
+ return false;
+ } else {
+ // Unexpected output.
+ assert(false);
+ }
+}
+
+int main(int argc, char *argv[]) {
+ setlocale(LC_ALL, "");
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ assert(SetConsoleCP(932));
+ assert(SetConsoleOutputCP(932));
+
+ const wchar_t testChar = 0x30FC;
+ const wchar_t *const faceNames[] = {
+ L"Lucida Console",
+ L"Consolas",
+ L"MS ゴシック",
+ };
+
+ trace("Test started");
+
+ for (auto faceName : faceNames) {
+ for (int px = 1; px <= 50; ++px) {
+ setFont(faceName, px);
+ if (!performTest(testChar)) {
+ trace("FAILURE: %s %dpx", narrowString(faceName).c_str(), px);
+ }
+ }
+ }
+
+ trace("Test complete");
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/UnicodeWideTest1.cc b/src/libs/3rdparty/winpty/misc/UnicodeWideTest1.cc
new file mode 100644
index 0000000000..a8d798e70d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/UnicodeWideTest1.cc
@@ -0,0 +1,246 @@
+#include <windows.h>
+
+#include <assert.h>
+#include <vector>
+
+#include "TestUtil.cc"
+
+#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
+
+
+CHAR_INFO ci(wchar_t ch, WORD attributes) {
+ CHAR_INFO ret;
+ ret.Char.UnicodeChar = ch;
+ ret.Attributes = attributes;
+ return ret;
+}
+
+CHAR_INFO ci(wchar_t ch) {
+ return ci(ch, 7);
+}
+
+CHAR_INFO ci() {
+ return ci(L' ');
+}
+
+bool operator==(SMALL_RECT x, SMALL_RECT y) {
+ return !memcmp(&x, &y, sizeof(x));
+}
+
+SMALL_RECT sr(COORD pt, COORD size) {
+ return {
+ pt.X, pt.Y,
+ static_cast<SHORT>(pt.X + size.X - 1),
+ static_cast<SHORT>(pt.Y + size.Y - 1)
+ };
+}
+
+static void set(
+ const COORD pt,
+ const COORD size,
+ const std::vector<CHAR_INFO> &data) {
+ assert(data.size() == size.X * size.Y);
+ SMALL_RECT writeRegion = sr(pt, size);
+ BOOL ret = WriteConsoleOutputW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), size, {0, 0}, &writeRegion);
+ assert(ret && writeRegion == sr(pt, size));
+}
+
+static void set(
+ const COORD pt,
+ const std::vector<CHAR_INFO> &data) {
+ set(pt, {static_cast<SHORT>(data.size()), 1}, data);
+}
+
+static void writeAttrsAt(
+ const COORD pt,
+ const std::vector<WORD> &data) {
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleOutputAttribute(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), data.size(), pt, &actual);
+ assert(ret && actual == data.size());
+}
+
+static void writeCharsAt(
+ const COORD pt,
+ const std::vector<wchar_t> &data) {
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleOutputCharacterW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), data.size(), pt, &actual);
+ assert(ret && actual == data.size());
+}
+
+static void writeChars(
+ const std::vector<wchar_t> &data) {
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), data.size(), &actual, NULL);
+ assert(ret && actual == data.size());
+}
+
+std::vector<CHAR_INFO> get(
+ const COORD pt,
+ const COORD size) {
+ std::vector<CHAR_INFO> data(size.X * size.Y);
+ SMALL_RECT readRegion = sr(pt, size);
+ BOOL ret = ReadConsoleOutputW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), size, {0, 0}, &readRegion);
+ assert(ret && readRegion == sr(pt, size));
+ return data;
+}
+
+std::vector<wchar_t> readCharsAt(
+ const COORD pt,
+ int size) {
+ std::vector<wchar_t> data(size);
+ DWORD actual = 0;
+ BOOL ret = ReadConsoleOutputCharacterW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ data.data(), data.size(), pt, &actual);
+ assert(ret);
+ data.resize(actual); // With double-width chars, we can read fewer than `size`.
+ return data;
+}
+
+static void dump(const COORD pt, const COORD size) {
+ for (CHAR_INFO ci : get(pt, size)) {
+ printf("%04X %04X\n", ci.Char.UnicodeChar, ci.Attributes);
+ }
+}
+
+static void dumpCharsAt(const COORD pt, int size) {
+ for (wchar_t ch : readCharsAt(pt, size)) {
+ printf("%04X\n", ch);
+ }
+}
+
+static COORD getCursorPos() {
+ CONSOLE_SCREEN_BUFFER_INFO info = { sizeof(info) };
+ assert(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info));
+ return info.dwCursorPosition;
+}
+
+static void test1() {
+ // We write "䀀䀀", then write "䀁" in the middle of the two. The second
+ // write turns the first and last cells into spaces. The LEADING/TRAILING
+ // flags retain consistency.
+ printf("test1 - overlap full-width char with full-width char\n");
+ writeCharsAt({1,0}, {0x4000, 0x4000});
+ dump({0,0}, {6,1});
+ printf("\n");
+ writeCharsAt({2,0}, {0x4001});
+ dump({0,0}, {6,1});
+ printf("\n");
+}
+
+static void test2() {
+ // Like `test1`, but use a lower-level API to do the write. Consistency is
+ // preserved here too -- the first and last cells are replaced with spaces.
+ printf("test2 - overlap full-width char with full-width char (lowlevel)\n");
+ writeCharsAt({1,0}, {0x4000, 0x4000});
+ dump({0,0}, {6,1});
+ printf("\n");
+ set({2,0}, {ci(0x4001,0x107), ci(0x4001,0x207)});
+ dump({0,0}, {6,1});
+ printf("\n");
+}
+
+static void test3() {
+ // However, the lower-level API can break the LEADING/TRAILING invariant
+ // explicitly:
+ printf("test3 - explicitly violate LEADING/TRAILING using lowlevel API\n");
+ set({1,0}, {
+ ci(0x4000, 0x207),
+ ci(0x4001, 0x107),
+ ci(0x3044, 7),
+ ci(L'X', 0x107),
+ ci(L'X', 0x207),
+ });
+ dump({0,0}, {7,1});
+}
+
+static void test4() {
+ // It is possible for the two cells of a double-width character to have two
+ // colors.
+ printf("test4 - use lowlevel to assign two colors to one full-width char\n");
+ set({0,0}, {
+ ci(0x4000, 0x142),
+ ci(0x4000, 0x224),
+ });
+ dump({0,0}, {2,1});
+}
+
+static void test5() {
+ // WriteConsoleOutputAttribute doesn't seem to affect the LEADING/TRAILING
+ // flags.
+ printf("test5 - WriteConsoleOutputAttribute cannot affect LEADING/TRAILING\n");
+
+ // Trying to clear the flags doesn't work...
+ writeCharsAt({0,0}, {0x4000});
+ dump({0,0}, {2,1});
+ writeAttrsAt({0,0}, {0x42, 0x24});
+ printf("\n");
+ dump({0,0}, {2,1});
+
+ // ... and trying to add them also doesn't work.
+ writeCharsAt({0,1}, {'A', ' '});
+ writeAttrsAt({0,1}, {0x107, 0x207});
+ printf("\n");
+ dump({0,1}, {2,1});
+}
+
+static void test6() {
+ // The cursor position may be on either cell of a double-width character.
+ // Visually, the cursor appears under both cells, regardless of which
+ // specific one has the cursor.
+ printf("test6 - cursor can be either left or right cell of full-width char\n");
+
+ writeCharsAt({2,1}, {0x4000});
+
+ setCursorPos(2, 1);
+ auto pos1 = getCursorPos();
+ Sleep(1000);
+
+ setCursorPos(3, 1);
+ auto pos2 = getCursorPos();
+ Sleep(1000);
+
+ setCursorPos(0, 15);
+ printf("%d,%d\n", pos1.X, pos1.Y);
+ printf("%d,%d\n", pos2.X, pos2.Y);
+}
+
+static void runTest(void (&test)()) {
+ system("cls");
+ setCursorPos(0, 14);
+ test();
+ system("pause");
+}
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(80, 40);
+ setWindowPos(0, 0, 80, 40);
+
+ auto cp = GetConsoleOutputCP();
+ assert(cp == 932 || cp == 936 || cp == 949 || cp == 950);
+
+ runTest(test1);
+ runTest(test2);
+ runTest(test3);
+ runTest(test4);
+ runTest(test5);
+ runTest(test6);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/UnicodeWideTest2.cc b/src/libs/3rdparty/winpty/misc/UnicodeWideTest2.cc
new file mode 100644
index 0000000000..05f80f70bd
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/UnicodeWideTest2.cc
@@ -0,0 +1,130 @@
+//
+// Test half-width vs full-width characters.
+//
+
+#include <windows.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "TestUtil.cc"
+
+static void writeChars(const wchar_t *text) {
+ wcslen(text);
+ const int len = wcslen(text);
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ text, len, &actual, NULL);
+ trace("writeChars: ret=%d, actual=%lld", ret, (long long)actual);
+}
+
+static void dumpChars(int x, int y, int w, int h) {
+ BOOL ret;
+ const COORD bufSize = {w, h};
+ const COORD bufCoord = {0, 0};
+ const SMALL_RECT topLeft = {x, y, x + w - 1, y + h - 1};
+ CHAR_INFO mbcsData[w * h];
+ CHAR_INFO unicodeData[w * h];
+ SMALL_RECT readRegion;
+ readRegion = topLeft;
+ ret = ReadConsoleOutputW(GetStdHandle(STD_OUTPUT_HANDLE), unicodeData,
+ bufSize, bufCoord, &readRegion);
+ assert(ret);
+ readRegion = topLeft;
+ ret = ReadConsoleOutputA(GetStdHandle(STD_OUTPUT_HANDLE), mbcsData,
+ bufSize, bufCoord, &readRegion);
+ assert(ret);
+
+ printf("\n");
+ for (int i = 0; i < w * h; ++i) {
+ printf("(%02d,%02d) CHAR: %04x %4x -- %02x %4x\n",
+ x + i % w, y + i / w,
+ (unsigned short)unicodeData[i].Char.UnicodeChar,
+ (unsigned short)unicodeData[i].Attributes,
+ (unsigned char)mbcsData[i].Char.AsciiChar,
+ (unsigned short)mbcsData[i].Attributes);
+ }
+}
+
+int main(int argc, char *argv[]) {
+ system("cls");
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(80, 38);
+ setWindowPos(0, 0, 80, 38);
+
+ // Write text.
+ const wchar_t text1[] = {
+ 0x3044, // U+3044 (HIRAGANA LETTER I)
+ 0x2014, // U+2014 (EM DASH)
+ 0x3044, // U+3044 (HIRAGANA LETTER I)
+ 0xFF2D, // U+FF2D (FULLWIDTH LATIN CAPITAL LETTER M)
+ 0x30FC, // U+30FC (KATAKANA-HIRAGANA PROLONGED SOUND MARK)
+ 0x0031, // U+3031 (DIGIT ONE)
+ 0x2014, // U+2014 (EM DASH)
+ 0x0032, // U+0032 (DIGIT TWO)
+ 0x005C, // U+005C (REVERSE SOLIDUS)
+ 0x3044, // U+3044 (HIRAGANA LETTER I)
+ 0
+ };
+ setCursorPos(0, 0);
+ writeChars(text1);
+
+ setCursorPos(78, 1);
+ writeChars(L"<>");
+
+ const wchar_t text2[] = {
+ 0x0032, // U+3032 (DIGIT TWO)
+ 0x3044, // U+3044 (HIRAGANA LETTER I)
+ 0,
+ };
+ setCursorPos(78, 1);
+ writeChars(text2);
+
+ system("pause");
+
+ dumpChars(0, 0, 17, 1);
+ dumpChars(2, 0, 2, 1);
+ dumpChars(2, 0, 1, 1);
+ dumpChars(3, 0, 1, 1);
+ dumpChars(78, 1, 2, 1);
+ dumpChars(0, 2, 2, 1);
+
+ system("pause");
+ system("cls");
+
+ const wchar_t text3[] = {
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 1
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 2
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 3
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 4
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 5
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 6
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 7
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 8
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 9
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 10
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 11
+ 0x30FC, 0x30FC, 0x30FC, 0xFF2D, // 12
+ L'\r', '\n',
+ L'\r', '\n',
+ 0
+ };
+ writeChars(text3);
+ system("pause");
+ {
+ const COORD bufSize = {80, 2};
+ const COORD bufCoord = {0, 0};
+ SMALL_RECT readRegion = {0, 0, 79, 1};
+ CHAR_INFO unicodeData[160];
+ BOOL ret = ReadConsoleOutputW(GetStdHandle(STD_OUTPUT_HANDLE), unicodeData,
+ bufSize, bufCoord, &readRegion);
+ assert(ret);
+ for (int i = 0; i < 96; ++i) {
+ printf("%04x ", unicodeData[i].Char.UnicodeChar);
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/UnixEcho.cc b/src/libs/3rdparty/winpty/misc/UnixEcho.cc
new file mode 100644
index 0000000000..372e045157
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/UnixEcho.cc
@@ -0,0 +1,89 @@
+/*
+ * Unix test code that puts the terminal into raw mode, then echos typed
+ * characters to stdout. Derived from sample code in the Stevens book, posted
+ * online at http://www.lafn.org/~dave/linux/terminalIO.html.
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "FormatChar.h"
+
+static struct termios save_termios;
+static int term_saved;
+
+/* RAW! mode */
+int tty_raw(int fd)
+{
+ struct termios buf;
+
+ if (tcgetattr(fd, &save_termios) < 0) /* get the original state */
+ return -1;
+
+ buf = save_termios;
+
+ /* echo off, canonical mode off, extended input
+ processing off, signal chars off */
+ buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+
+ /* no SIGINT on BREAK, CR-to-NL off, input parity
+ check off, don't strip the 8th bit on input,
+ ouput flow control off */
+ buf.c_iflag &= ~(BRKINT | ICRNL | ISTRIP | IXON);
+
+ /* clear size bits, parity checking off */
+ buf.c_cflag &= ~(CSIZE | PARENB);
+
+ /* set 8 bits/char */
+ buf.c_cflag |= CS8;
+
+ /* output processing off */
+ buf.c_oflag &= ~(OPOST);
+
+ buf.c_cc[VMIN] = 1; /* 1 byte at a time */
+ buf.c_cc[VTIME] = 0; /* no timer on input */
+
+ if (tcsetattr(fd, TCSAFLUSH, &buf) < 0)
+ return -1;
+
+ term_saved = 1;
+
+ return 0;
+}
+
+
+/* set it to normal! */
+int tty_reset(int fd)
+{
+ if (term_saved)
+ if (tcsetattr(fd, TCSAFLUSH, &save_termios) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+int main()
+{
+ tty_raw(0);
+
+ int count = 0;
+ while (true) {
+ char ch;
+ char buf[16];
+ int actual = read(0, &ch, 1);
+ if (actual != 1) {
+ perror("read error");
+ break;
+ }
+ formatChar(buf, ch);
+ fputs(buf, stdout);
+ fflush(stdout);
+ if (ch == 3) // Ctrl-C
+ break;
+ }
+
+ tty_reset(0);
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Utf16Echo.cc b/src/libs/3rdparty/winpty/misc/Utf16Echo.cc
new file mode 100644
index 0000000000..ef5f302de4
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Utf16Echo.cc
@@ -0,0 +1,46 @@
+#include <windows.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <vector>
+#include <string>
+
+int main(int argc, char *argv[]) {
+ system("cls");
+
+ if (argc == 1) {
+ printf("Usage: %s hhhh\n", argv[0]);
+ return 0;
+ }
+
+ std::wstring dataToWrite;
+ for (int i = 1; i < argc; ++i) {
+ wchar_t ch = strtol(argv[i], NULL, 16);
+ dataToWrite.push_back(ch);
+ }
+
+ DWORD actual = 0;
+ BOOL ret = WriteConsoleW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ dataToWrite.data(), dataToWrite.size(), &actual, NULL);
+ assert(ret && actual == dataToWrite.size());
+
+ // Read it back.
+ std::vector<CHAR_INFO> readBuffer(dataToWrite.size() * 2);
+ COORD bufSize = {static_cast<short>(readBuffer.size()), 1};
+ COORD bufCoord = {0, 0};
+ SMALL_RECT topLeft = {0, 0, static_cast<short>(readBuffer.size() - 1), 0};
+ ret = ReadConsoleOutputW(
+ GetStdHandle(STD_OUTPUT_HANDLE), readBuffer.data(),
+ bufSize, bufCoord, &topLeft);
+ assert(ret);
+
+ printf("\n");
+ for (int i = 0; i < readBuffer.size(); ++i) {
+ printf("CHAR: %04x %04x\n",
+ readBuffer[i].Char.UnicodeChar,
+ readBuffer[i].Attributes);
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/VeryLargeRead.cc b/src/libs/3rdparty/winpty/misc/VeryLargeRead.cc
new file mode 100644
index 0000000000..58f0897022
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/VeryLargeRead.cc
@@ -0,0 +1,122 @@
+//
+// 2015-09-25
+// I measured these limits on the size of a single ReadConsoleOutputW call.
+// The limit seems to more-or-less disppear with Windows 8, which is the first
+// OS to stop using ALPCs for console I/O. My guess is that the new I/O
+// method does not use the 64KiB shared memory buffer that the ALPC method
+// uses.
+//
+// I'm guessing the remaining difference between Windows 8/8.1 and Windows 10
+// might be related to the 32-vs-64-bitness.
+//
+// Client OSs
+//
+// Windows XP 32-bit VM ==> up to 13304 characters
+// - 13304x1 works, but 13305x1 fails instantly
+// Windows 7 32-bit VM ==> between 16-17 thousand characters
+// - 16000x1 works, 17000x1 fails instantly
+// - 163x100 *crashes* conhost.exe but leaves VeryLargeRead.exe running
+// Windows 8 32-bit VM ==> between 240-250 million characters
+// - 10000x24000 works, but 10000x25000 does not
+// Windows 8.1 32-bit VM ==> between 240-250 million characters
+// - 10000x24000 works, but 10000x25000 does not
+// Windows 10 64-bit VM ==> no limit (tested to 576 million characters)
+// - 24000x24000 works
+// - `ver` reports [Version 10.0.10240], conhost.exe and ConhostV1.dll are
+// 10.0.10240.16384 for file and product version. ConhostV2.dll is
+// 10.0.10240.16391 for file and product version.
+//
+// Server OSs
+//
+// Windows Server 2008 64-bit VM ==> 14300-14400 characters
+// - 14300x1 works, 14400x1 fails instantly
+// - This OS does not have conhost.exe.
+// - `ver` reports [Version 6.0.6002]
+// Windows Server 2008 R2 64-bit VM ==> 15600-15700 characters
+// - 15600x1 works, 15700x1 fails instantly
+// - This OS has conhost.exe, and procexp.exe reveals console ALPC ports in
+// use in conhost.exe.
+// - `ver` reports [Version 6.1.7601], conhost.exe is 6.1.7601.23153 for file
+// and product version.
+// Windows Server 2012 64-bit VM ==> at least 100 million characters
+// - 10000x10000 works (VM had only 1GiB of RAM, so I skipped larger tests)
+// - This OS has Windows 8's task manager and procexp.exe reveals the same
+// lack of ALPC ports and the same \Device\ConDrv\* files as Windows 8.
+// - `ver` reports [Version 6.2.9200], conhost.exe is 6.2.9200.16579 for file
+// and product version.
+//
+// To summarize:
+//
+// client-OS server-OS notes
+// ---------------------------------------------------------------------------
+// XP Server 2008 CSRSS, small reads
+// 7 Server 2008 R2 ALPC-to-conhost, small reads
+// 8, 8.1 Server 2012 new I/O interface, large reads allowed
+// 10 <no server OS yet> enhanced console w/rewrapping
+//
+// (Presumably, Win2K, Vista, and Win2K3 behave the same as XP. conhost.exe
+// was announced as a Win7 feature.)
+//
+
+#include <windows.h>
+#include <assert.h>
+#include <vector>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ long long width = 9000;
+ long long height = 9000;
+
+ assert(argc >= 1);
+ if (argc == 4) {
+ width = atoi(argv[2]);
+ height = atoi(argv[3]);
+ } else {
+ if (argc == 3) {
+ width = atoi(argv[1]);
+ height = atoi(argv[2]);
+ }
+ wchar_t args[1024];
+ swprintf(args, 1024, L"CHILD %lld %lld", width, height);
+ startChildProcess(args);
+ return 0;
+ }
+
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(width, height);
+ setWindowPos(0, 0, std::min(80LL, width), std::min(50LL, height));
+
+ setCursorPos(0, 0);
+ printf("A");
+ fflush(stdout);
+ setCursorPos(width - 2, height - 1);
+ printf("B");
+ fflush(stdout);
+
+ trace("sizeof(CHAR_INFO) = %d", (int)sizeof(CHAR_INFO));
+
+ trace("Allocating buffer...");
+ CHAR_INFO *buffer = new CHAR_INFO[width * height];
+ assert(buffer != NULL);
+ memset(&buffer[0], 0, sizeof(CHAR_INFO));
+ memset(&buffer[width * height - 2], 0, sizeof(CHAR_INFO));
+
+ COORD bufSize = { width, height };
+ COORD bufCoord = { 0, 0 };
+ SMALL_RECT readRegion = { 0, 0, width - 1, height - 1 };
+ trace("ReadConsoleOutputW: calling...");
+ BOOL success = ReadConsoleOutputW(conout, buffer, bufSize, bufCoord, &readRegion);
+ trace("ReadConsoleOutputW: success=%d", success);
+
+ assert(buffer[0].Char.UnicodeChar == L'A');
+ assert(buffer[width * height - 2].Char.UnicodeChar == L'B');
+ trace("Top-left and bottom-right characters read successfully!");
+
+ Sleep(30000);
+
+ delete [] buffer;
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/VkEscapeTest.cc b/src/libs/3rdparty/winpty/misc/VkEscapeTest.cc
new file mode 100644
index 0000000000..97bf59f998
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/VkEscapeTest.cc
@@ -0,0 +1,56 @@
+/*
+ * Sending VK_PAUSE to the console window almost works as a mechanism for
+ * pausing it, but it doesn't because the console could turn off the
+ * ENABLE_LINE_INPUT console mode flag.
+ */
+
+#define _WIN32_WINNT 0x0501
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+
+CALLBACK DWORD pausingThread(LPVOID dummy)
+{
+ if (1) {
+ Sleep(1000);
+ HWND hwnd = GetConsoleWindow();
+ SendMessage(hwnd, WM_KEYDOWN, VK_PAUSE, 1);
+ Sleep(1000);
+ SendMessage(hwnd, WM_KEYDOWN, VK_ESCAPE, 1);
+ }
+
+ if (0) {
+ INPUT_RECORD ir;
+ memset(&ir, 0, sizeof(ir));
+ ir.EventType = KEY_EVENT;
+ ir.Event.KeyEvent.bKeyDown = TRUE;
+ ir.Event.KeyEvent.wVirtualKeyCode = VK_PAUSE;
+ ir.Event.KeyEvent.wRepeatCount = 1;
+ }
+
+ return 0;
+}
+
+int main()
+{
+ HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);
+ HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
+ COORD c = { 0, 0 };
+
+ DWORD mode;
+ GetConsoleMode(hin, &mode);
+ SetConsoleMode(hin, mode &
+ ~(ENABLE_LINE_INPUT));
+
+ CreateThread(NULL, 0,
+ pausingThread, NULL,
+ 0, NULL);
+
+ int i = 0;
+ while (true) {
+ Sleep(100);
+ printf("%d\n", ++i);
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win10ResizeWhileFrozen.cc b/src/libs/3rdparty/winpty/misc/Win10ResizeWhileFrozen.cc
new file mode 100644
index 0000000000..82feaf3c50
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win10ResizeWhileFrozen.cc
@@ -0,0 +1,52 @@
+/*
+ * Demonstrates a conhost hang that occurs when widening the console buffer
+ * while selection is in progress. The problem affects the new Windows 10
+ * console, not the "legacy" console mode that Windows 10 also includes.
+ *
+ * First tested with:
+ * - Windows 10.0.10240
+ * - conhost.exe version 10.0.10240.16384
+ * - ConhostV1.dll version 10.0.10240.16384
+ * - ConhostV2.dll version 10.0.10240.16391
+ */
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+#include "TestUtil.cc"
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(80, 25);
+ setWindowPos(0, 0, 80, 25);
+
+ countDown(5);
+
+ SendMessage(GetConsoleWindow(), WM_SYSCOMMAND, SC_CONSOLE_SELECT_ALL, 0);
+ Sleep(2000);
+
+ // This API call does not return. In the console window, the "Select All"
+ // operation appears to end. The console window becomes non-responsive,
+ // and the conhost.exe process must be killed from the Task Manager.
+ // (Killing this test program or closing the console window is not
+ // sufficient.)
+ //
+ // The same hang occurs whether line resizing is off or on. It happens
+ // with both "Mark" and "Select All". Calling setBufferSize with the
+ // existing buffer size does not hang, but calling it with only a changed
+ // buffer height *does* hang. Calling setWindowPos does not hang.
+ setBufferSize(120, 25);
+
+ printf("Done...\n");
+ Sleep(2000);
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win10WrapTest1.cc b/src/libs/3rdparty/winpty/misc/Win10WrapTest1.cc
new file mode 100644
index 0000000000..645fa95d54
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win10WrapTest1.cc
@@ -0,0 +1,57 @@
+/*
+ * Demonstrates some wrapping behaviors of the new Windows 10 console.
+ */
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(40, 20);
+ setWindowPos(0, 0, 40, 20);
+
+ system("cls");
+
+ repeatChar(39, 'A'); repeatChar(1, ' ');
+ repeatChar(39, 'B'); repeatChar(1, ' ');
+ printf("\n");
+
+ repeatChar(39, 'C'); repeatChar(1, ' ');
+ repeatChar(39, 'D'); repeatChar(1, ' ');
+ printf("\n");
+
+ repeatChar(40, 'E');
+ repeatChar(40, 'F');
+ printf("\n");
+
+ repeatChar(39, 'G'); repeatChar(1, ' ');
+ repeatChar(39, 'H'); repeatChar(1, ' ');
+ printf("\n");
+
+ Sleep(2000);
+
+ setChar(39, 0, '*', 0x24);
+ setChar(39, 1, '*', 0x24);
+
+ setChar(39, 3, ' ', 0x24);
+ setChar(39, 4, ' ', 0x24);
+
+ setChar(38, 6, ' ', 0x24);
+ setChar(38, 7, ' ', 0x24);
+
+ Sleep(2000);
+ setWindowPos(0, 0, 35, 20);
+ setBufferSize(35, 20);
+ trace("DONE");
+
+ printf("Sleeping forever...\n");
+ while(true) { Sleep(1000); }
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win10WrapTest2.cc b/src/libs/3rdparty/winpty/misc/Win10WrapTest2.cc
new file mode 100644
index 0000000000..50615fc8c7
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win10WrapTest2.cc
@@ -0,0 +1,30 @@
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ startChildProcess(L"CHILD");
+ return 0;
+ }
+
+ const int WIDTH = 25;
+
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(WIDTH, 40);
+ setWindowPos(0, 0, WIDTH, 20);
+
+ system("cls");
+
+ for (int i = 0; i < 100; ++i) {
+ printf("FOO(%d)\n", i);
+ }
+
+ repeatChar(5, '\n');
+ repeatChar(WIDTH * 5, '.');
+ repeatChar(10, '\n');
+ setWindowPos(0, 20, WIDTH, 20);
+ writeBox(0, 5, 1, 10, '|');
+
+ Sleep(120000);
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Echo1.cc b/src/libs/3rdparty/winpty/misc/Win32Echo1.cc
new file mode 100644
index 0000000000..06fc79f794
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Echo1.cc
@@ -0,0 +1,26 @@
+/*
+ * A Win32 program that reads raw console input with ReadFile and echos
+ * it to stdout.
+ */
+
+#include <stdio.h>
+#include <conio.h>
+#include <windows.h>
+
+int main()
+{
+ int count = 0;
+ HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
+ HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ SetConsoleMode(hStdIn, 0);
+
+ while (true) {
+ DWORD actual;
+ char ch;
+ ReadFile(hStdIn, &ch, 1, &actual, NULL);
+ printf("%02x ", ch);
+ if (++count == 50)
+ break;
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Echo2.cc b/src/libs/3rdparty/winpty/misc/Win32Echo2.cc
new file mode 100644
index 0000000000..b2ea2ad1c5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Echo2.cc
@@ -0,0 +1,19 @@
+/*
+ * A Win32 program that reads raw console input with getch and echos
+ * it to stdout.
+ */
+
+#include <stdio.h>
+#include <conio.h>
+
+int main()
+{
+ int count = 0;
+ while (true) {
+ int ch = getch();
+ printf("%02x ", ch);
+ if (++count == 50)
+ break;
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Test1.cc b/src/libs/3rdparty/winpty/misc/Win32Test1.cc
new file mode 100644
index 0000000000..a40d318a98
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Test1.cc
@@ -0,0 +1,46 @@
+#define _WIN32_WINNT 0x0501
+#include "../src/shared/DebugClient.cc"
+#include <windows.h>
+#include <stdio.h>
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+
+CALLBACK DWORD writerThread(void*)
+{
+ while (true) {
+ Sleep(1000);
+ trace("writing");
+ printf("X\n");
+ trace("written");
+ }
+}
+
+int main()
+{
+ CreateThread(NULL, 0, writerThread, NULL, 0, NULL);
+ trace("marking console");
+ HWND hwnd = GetConsoleWindow();
+ PostMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0);
+
+ Sleep(2000);
+
+ trace("reading output");
+ CHAR_INFO buf[1];
+ COORD bufSize = { 1, 1 };
+ COORD zeroCoord = { 0, 0 };
+ SMALL_RECT readRect = { 0, 0, 0, 0 };
+ ReadConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE),
+ buf,
+ bufSize,
+ zeroCoord,
+ &readRect);
+ trace("done reading output");
+
+ Sleep(2000);
+
+ PostMessage(hwnd, WM_CHAR, 27, 0x00010001);
+
+ Sleep(1100);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Test2.cc b/src/libs/3rdparty/winpty/misc/Win32Test2.cc
new file mode 100644
index 0000000000..2777bad456
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Test2.cc
@@ -0,0 +1,70 @@
+/*
+ * This test demonstrates that putting a console into selection mode does not
+ * block the low-level console APIs, even though it blocks WriteFile.
+ */
+
+#define _WIN32_WINNT 0x0501
+#include "../src/shared/DebugClient.cc"
+#include <windows.h>
+#include <stdio.h>
+
+const int SC_CONSOLE_MARK = 0xFFF2;
+
+CALLBACK DWORD writerThread(void*)
+{
+ CHAR_INFO xChar, fillChar;
+ memset(&xChar, 0, sizeof(xChar));
+ xChar.Char.AsciiChar = 'X';
+ xChar.Attributes = 7;
+ memset(&fillChar, 0, sizeof(fillChar));
+ fillChar.Char.AsciiChar = ' ';
+ fillChar.Attributes = 7;
+ COORD oneCoord = { 1, 1 };
+ COORD zeroCoord = { 0, 0 };
+
+ while (true) {
+ SMALL_RECT writeRegion = { 5, 5, 5, 5 };
+ WriteConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE),
+ &xChar, oneCoord,
+ zeroCoord,
+ &writeRegion);
+ Sleep(500);
+ SMALL_RECT scrollRect = { 1, 1, 20, 20 };
+ COORD destCoord = { 0, 0 };
+ ScrollConsoleScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE),
+ &scrollRect,
+ NULL,
+ destCoord,
+ &fillChar);
+ }
+}
+
+int main()
+{
+ CreateThread(NULL, 0, writerThread, NULL, 0, NULL);
+ trace("marking console");
+ HWND hwnd = GetConsoleWindow();
+ PostMessage(hwnd, WM_SYSCOMMAND, SC_CONSOLE_MARK, 0);
+
+ Sleep(2000);
+
+ trace("reading output");
+ CHAR_INFO buf[1];
+ COORD bufSize = { 1, 1 };
+ COORD zeroCoord = { 0, 0 };
+ SMALL_RECT readRect = { 0, 0, 0, 0 };
+ ReadConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE),
+ buf,
+ bufSize,
+ zeroCoord,
+ &readRect);
+ trace("done reading output");
+
+ Sleep(2000);
+
+ PostMessage(hwnd, WM_CHAR, 27, 0x00010001);
+
+ Sleep(1100);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Test3.cc b/src/libs/3rdparty/winpty/misc/Win32Test3.cc
new file mode 100644
index 0000000000..1fb92aff3d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Test3.cc
@@ -0,0 +1,78 @@
+/*
+ * Creates a window station and starts a process under it. The new process
+ * also gets a new console.
+ */
+
+#include <windows.h>
+#include <string.h>
+#include <stdio.h>
+
+int main()
+{
+ BOOL success;
+
+ SECURITY_ATTRIBUTES sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.bInheritHandle = TRUE;
+
+ HWINSTA originalStation = GetProcessWindowStation();
+ printf("originalStation == 0x%x\n", originalStation);
+ HWINSTA station = CreateWindowStation(NULL,
+ 0,
+ WINSTA_ALL_ACCESS,
+ &sa);
+ printf("station == 0x%x\n", station);
+ if (!SetProcessWindowStation(station))
+ printf("SetWindowStation failed!\n");
+ HDESK desktop = CreateDesktop("Default", NULL, NULL,
+ /*dwFlags=*/0, GENERIC_ALL,
+ &sa);
+ printf("desktop = 0x%x\n", desktop);
+
+ char stationName[256];
+ stationName[0] = '\0';
+ success = GetUserObjectInformation(station, UOI_NAME,
+ stationName, sizeof(stationName),
+ NULL);
+ printf("stationName = [%s]\n", stationName);
+
+ char startupDesktop[256];
+ sprintf(startupDesktop, "%s\\Default", stationName);
+
+ STARTUPINFO sui;
+ PROCESS_INFORMATION pi;
+ memset(&sui, 0, sizeof(sui));
+ memset(&pi, 0, sizeof(pi));
+ sui.cb = sizeof(STARTUPINFO);
+ sui.lpDesktop = startupDesktop;
+
+ // Start a cmd subprocess, and have it start its own cmd subprocess.
+ // Both subprocesses will connect to the same non-interactive window
+ // station.
+
+ const char program[] = "c:\\windows\\system32\\cmd.exe";
+ char cmdline[256];
+ sprintf(cmdline, "%s /c cmd", program);
+ success = CreateProcess(program,
+ cmdline,
+ NULL,
+ NULL,
+ /*bInheritHandles=*/FALSE,
+ /*dwCreationFlags=*/CREATE_NEW_CONSOLE,
+ NULL, NULL,
+ &sui,
+ &pi);
+
+ printf("pid == %d\n", pi.dwProcessId);
+
+ // This sleep is necessary. We must give the child enough time to
+ // connect to the specified window station.
+ Sleep(5000);
+
+ SetProcessWindowStation(originalStation);
+ CloseWindowStation(station);
+ CloseDesktop(desktop);
+ Sleep(5000);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/Win32Write1.cc b/src/libs/3rdparty/winpty/misc/Win32Write1.cc
new file mode 100644
index 0000000000..6e5bf96682
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/Win32Write1.cc
@@ -0,0 +1,44 @@
+/*
+ * A Win32 program that scrolls and writes to the console using the ioctl-like
+ * interface.
+ */
+
+#include <stdio.h>
+#include <windows.h>
+
+int main()
+{
+ HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ for (int i = 0; i < 80; ++i) {
+
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ GetConsoleScreenBufferInfo(conout, &info);
+
+ SMALL_RECT src = { 0, 1, info.dwSize.X - 1, info.dwSize.Y - 1 };
+ COORD destOrigin = { 0, 0 };
+ CHAR_INFO fillCharInfo = { 0 };
+ fillCharInfo.Char.AsciiChar = ' ';
+ fillCharInfo.Attributes = 7;
+ ScrollConsoleScreenBuffer(conout,
+ &src,
+ NULL,
+ destOrigin,
+ &fillCharInfo);
+
+ CHAR_INFO buffer = { 0 };
+ buffer.Char.AsciiChar = 'X';
+ buffer.Attributes = 7;
+ COORD bufferSize = { 1, 1 };
+ COORD bufferCoord = { 0, 0 };
+ SMALL_RECT writeRegion = { 0, 0, 0, 0 };
+ writeRegion.Left = writeRegion.Right = i;
+ writeRegion.Top = writeRegion.Bottom = 5;
+ WriteConsoleOutput(conout,
+ &buffer, bufferSize, bufferCoord,
+ &writeRegion);
+
+ Sleep(250);
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/WindowsBugCrashReader.cc b/src/libs/3rdparty/winpty/misc/WindowsBugCrashReader.cc
new file mode 100644
index 0000000000..e6d9558df6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/WindowsBugCrashReader.cc
@@ -0,0 +1,27 @@
+// I noticed this on the ConEmu web site:
+//
+// https://social.msdn.microsoft.com/Forums/en-US/40c8e395-cca9-45c8-b9b8-2fbe6782ac2b/readconsoleoutput-cause-access-violation-writing-location-exception
+// https://conemu.github.io/en/MicrosoftBugs.html
+//
+// In Windows 7, 8, and 8.1, a ReadConsoleOutputW with an out-of-bounds read
+// region crashes the application. I have reproduced the problem on Windows 8
+// and 8.1, but not on Windows 7.
+//
+
+#include <windows.h>
+
+#include "TestUtil.cc"
+
+int main() {
+ setWindowPos(0, 0, 1, 1);
+ setBufferSize(80, 25);
+ setWindowPos(0, 0, 80, 25);
+
+ const HANDLE conout = openConout();
+ static CHAR_INFO lineBuf[80];
+ SMALL_RECT readRegion = { 0, 999, 79, 999 };
+ const BOOL ret = ReadConsoleOutputW(conout, lineBuf, {80, 1}, {0, 0}, &readRegion);
+ ASSERT(!ret && "ReadConsoleOutputW should have failed");
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/WriteConsole.cc b/src/libs/3rdparty/winpty/misc/WriteConsole.cc
new file mode 100644
index 0000000000..a03670ca92
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/WriteConsole.cc
@@ -0,0 +1,106 @@
+#include <windows.h>
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+static std::wstring mbsToWcs(const std::string &s) {
+ const size_t len = mbstowcs(nullptr, s.c_str(), 0);
+ if (len == static_cast<size_t>(-1)) {
+ assert(false && "mbsToWcs: invalid string");
+ }
+ std::wstring ret;
+ ret.resize(len);
+ const size_t len2 = mbstowcs(&ret[0], s.c_str(), len);
+ assert(len == len2);
+ return ret;
+}
+
+uint32_t parseHex(wchar_t ch, bool &invalid) {
+ if (ch >= L'0' && ch <= L'9') {
+ return ch - L'0';
+ } else if (ch >= L'a' && ch <= L'f') {
+ return ch - L'a' + 10;
+ } else if (ch >= L'A' && ch <= L'F') {
+ return ch - L'A' + 10;
+ } else {
+ invalid = true;
+ return 0;
+ }
+}
+
+int main(int argc, char *argv[]) {
+ std::vector<std::wstring> args;
+ for (int i = 1; i < argc; ++i) {
+ args.push_back(mbsToWcs(argv[i]));
+ }
+
+ std::wstring out;
+ for (const auto &arg : args) {
+ if (!out.empty()) {
+ out.push_back(L' ');
+ }
+ for (size_t i = 0; i < arg.size(); ++i) {
+ wchar_t ch = arg[i];
+ wchar_t nch = i + 1 < arg.size() ? arg[i + 1] : L'\0';
+ if (ch == L'\\') {
+ switch (nch) {
+ case L'a': ch = L'\a'; ++i; break;
+ case L'b': ch = L'\b'; ++i; break;
+ case L'e': ch = L'\x1b'; ++i; break;
+ case L'f': ch = L'\f'; ++i; break;
+ case L'n': ch = L'\n'; ++i; break;
+ case L'r': ch = L'\r'; ++i; break;
+ case L't': ch = L'\t'; ++i; break;
+ case L'v': ch = L'\v'; ++i; break;
+ case L'\\': ch = L'\\'; ++i; break;
+ case L'\'': ch = L'\''; ++i; break;
+ case L'\"': ch = L'\"'; ++i; break;
+ case L'\?': ch = L'\?'; ++i; break;
+ case L'x':
+ if (i + 3 < arg.size()) {
+ bool invalid = false;
+ uint32_t d1 = parseHex(arg[i + 2], invalid);
+ uint32_t d2 = parseHex(arg[i + 3], invalid);
+ if (!invalid) {
+ i += 3;
+ ch = (d1 << 4) | d2;
+ }
+ }
+ break;
+ case L'u':
+ if (i + 5 < arg.size()) {
+ bool invalid = false;
+ uint32_t d1 = parseHex(arg[i + 2], invalid);
+ uint32_t d2 = parseHex(arg[i + 3], invalid);
+ uint32_t d3 = parseHex(arg[i + 4], invalid);
+ uint32_t d4 = parseHex(arg[i + 5], invalid);
+ if (!invalid) {
+ i += 5;
+ ch = (d1 << 24) | (d2 << 16) | (d3 << 8) | d4;
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+ out.push_back(ch);
+ }
+ }
+
+ DWORD actual = 0;
+ if (!WriteConsoleW(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ out.c_str(),
+ out.size(),
+ &actual,
+ nullptr)) {
+ fprintf(stderr, "WriteConsole failed (is stdout a console?)\n");
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/misc/build32.sh b/src/libs/3rdparty/winpty/misc/build32.sh
new file mode 100644
index 0000000000..162993ce33
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/build32.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+set -e
+name=$1
+name=${name%.}
+name=${name%.cc}
+name=${name%.exe}
+echo Compiling $name.cc to $name.exe
+i686-w64-mingw32-g++.exe -static -std=c++11 $name.cc -o $name.exe
+i686-w64-mingw32-strip $name.exe
diff --git a/src/libs/3rdparty/winpty/misc/build64.sh b/src/libs/3rdparty/winpty/misc/build64.sh
new file mode 100644
index 0000000000..6757967684
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/build64.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+set -e
+name=$1
+name=${name%.}
+name=${name%.cc}
+name=${name%.exe}
+echo Compiling $name.cc to $name.exe
+x86_64-w64-mingw32-g++.exe -static -std=c++11 $name.cc -o $name.exe
+x86_64-w64-mingw32-strip $name.exe
diff --git a/src/libs/3rdparty/winpty/misc/color-test.sh b/src/libs/3rdparty/winpty/misc/color-test.sh
new file mode 100644
index 0000000000..065c8094e2
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/color-test.sh
@@ -0,0 +1,212 @@
+#!/bin/bash
+
+FORE=$1
+BACK=$2
+FILL=$3
+
+if [ "$FORE" = "" ]; then
+ FORE=DefaultFore
+fi
+if [ "$BACK" = "" ]; then
+ BACK=DefaultBack
+fi
+
+# To detect color changes, we want a character that fills the whole cell
+# if possible. U+2588 is perfect, except that it becomes invisible in the
+# original xterm, when bolded. For that terminal, use something else, like
+# "#" or "@".
+if [ "$FILL" = "" ]; then
+ FILL="█"
+fi
+
+# SGR (Select Graphic Rendition)
+s() {
+ printf '\033[0m'
+ while [ "$1" != "" ]; do
+ printf '\033['"$1"'m'
+ shift
+ done
+}
+
+# Print
+p() {
+ echo -n "$@"
+}
+
+# Print with newline
+pn() {
+ echo "$@"
+}
+
+# For practical reasons, sandwich black and white in-between the other colors.
+FORE_COLORS="31 30 37 32 33 34 35 36"
+BACK_COLORS="41 40 47 42 43 44 45 46"
+
+
+
+### Test order of Invert(7) -- it does not matter what order it appears in.
+
+# The Red color setting here (31) is shadowed by the green setting (32). The
+# Reverse flag does not cause (32) to alter the background color immediately;
+# instead, the Reverse flag is applied once to determine the final effective
+# Fore/Back colors.
+s 7 31 32; p " -- Should be: $BACK-on-green -- "; s; pn
+s 31 7 32; p " -- Should be: $BACK-on-green -- "; s; pn
+s 31 32 7; p " -- Should be: $BACK-on-green -- "; s; pn
+
+# As above, but for the background color.
+s 7 41 42; p " -- Should be: green-on-$FORE -- "; s; pn
+s 41 7 42; p " -- Should be: green-on-$FORE -- "; s; pn
+s 41 42 7; p " -- Should be: green-on-$FORE -- "; s; pn
+
+# One last, related test
+s 7; p "Invert text"; s 7 1; p " with some words bold"; s; pn;
+s 0; p "Normal text"; s 0 1; p " with some words bold"; s; pn;
+
+pn
+
+
+
+### Test effect of Bold(1) on color, with and without Invert(7).
+
+# The Bold flag does not affect the background color when Reverse is missing.
+# There should always be 8 colored boxes.
+p " "
+for x in $BACK_COLORS; do
+ s $x; p "-"; s $x 1; p "-"
+done
+s; pn " Bold should not affect background"
+
+# On some terminals, Bold affects color, and on some it doesn't. If there
+# are only 8 colored boxes, then the next two tests will also show 8 colored
+# boxes. If there are 16 boxes, then exactly one of the next two tests will
+# also have 16 boxes.
+p " "
+for x in $FORE_COLORS; do
+ s $x; p "$FILL"; s $x 1; p "$FILL"
+done
+s; pn " Does bold affect foreground color?"
+
+# On some terminals, Bold+Invert highlights the final Background color.
+p " "
+for x in $FORE_COLORS; do
+ s $x 7; p "-"; s $x 7 1; p "-"
+done
+s; pn " Test if Bold+Invert affects background color"
+
+# On some terminals, Bold+Invert highlights the final Foreground color.
+p " "
+for x in $BACK_COLORS; do
+ s $x 7; p "$FILL"; s $x 7 1; p "$FILL"
+done
+s; pn " Test if Bold+Invert affects foreground color"
+
+pn
+
+
+
+### Test for support of ForeHi and BackHi properties.
+
+# ForeHi
+p " "
+for x in $FORE_COLORS; do
+ hi=$(( $x + 60 ))
+ s $x; p "$FILL"; s $hi; p "$FILL"
+done
+s; pn " Test for support of ForeHi colors"
+p " "
+for x in $FORE_COLORS; do
+ hi=$(( $x + 60 ))
+ s $x; p "$FILL"; s $x $hi; p "$FILL"
+done
+s; pn " Test for support of ForeHi colors (w/compat)"
+
+# BackHi
+p " "
+for x in $BACK_COLORS; do
+ hi=$(( $x + 60 ))
+ s $x; p "-"; s $hi; p "-"
+done
+s; pn " Test for support of BackHi colors"
+p " "
+for x in $BACK_COLORS; do
+ hi=$(( $x + 60 ))
+ s $x; p "-"; s $x $hi; p "-"
+done
+s; pn " Test for support of BackHi colors (w/compat)"
+
+pn
+
+
+
+### Identify the default fore and back colors.
+
+pn "Match default fore and back colors against 16-color palette"
+pn " ==fore== ==back=="
+for fore in $FORE_COLORS; do
+ forehi=$(( $fore + 60 ))
+ back=$(( $fore + 10 ))
+ backhi=$(( $back + 60 ))
+ p " "
+ s $fore; p "$FILL"; s; p "$FILL"; s $fore; p "$FILL"; s; p " "
+ s $forehi; p "$FILL"; s; p "$FILL"; s $forehi; p "$FILL"; s; p " "
+ s $back; p "-"; s; p "-"; s $back; p "-"; s; p " "
+ s $backhi; p "-"; s; p "-"; s $backhi; p "-"; s; p " "
+ pn " $fore $forehi $back $backhi"
+done
+
+pn
+
+
+
+### Test coloring of rest-of-line.
+
+#
+# When a new line is scrolled in, every cell in the line receives the
+# current background color, which can be the default/transparent color.
+#
+
+p "Newline with red background: usually no red -->"; s 41; pn
+s; pn "This text is plain, but rest is red if scrolled -->"
+s; p " "; s 41; printf '\033[1K'; s; printf '\033[1C'; pn "<-- red Erase-in-Line to beginning"
+s; p "red Erase-in-Line to end -->"; s 41; printf '\033[0K'; s; pn
+pn
+
+
+
+### Moving the cursor around does not change colors of anything.
+
+pn "Test modifying uncolored lines with a colored SGR:"
+pn "aaaa"
+pn
+pn "____e"
+s 31 42; printf '\033[4C\033[3A'; pn "bb"
+pn "cccc"
+pn "dddd"
+s; pn
+
+pn "Test modifying colored+inverted+bold line with plain text:"
+s 42 31 7 1; printf 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r';
+s; pn "This text is plain and followed by green-on-red -->"
+pn
+
+
+
+### Full-width character overwriting
+
+pn 'Overwrite part of a full-width char with a half-width char'
+p 'initial U+4000 ideographs -->'; s 31 42; p '䀀䀀'; s; pn
+p 'write X to index #1 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[24G'; p X; s; pn
+p 'write X to index #2 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[25G'; p X; s; pn
+p 'write X to index #3 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[26G'; p X; s; pn
+p 'write X to index #4 -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[27G'; p X; s; pn
+pn
+
+pn 'Verify that Erase-in-Line can "fix" last char in line'
+p 'original -->'; s 31 42; p '䀀䀀'; s; pn
+p 'overwrite -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[30G'; p 'XXX'; s; pn
+p 'overwrite + Erase-in-Line -->'; s 31 42; p '䀀䀀'; s 35 44; printf '\033[30G'; p 'XXX'; s; printf '\033[0K'; pn
+p 'original -->'; s 31 42; p 'X䀀䀀'; s; pn
+p 'overwrite -->'; s 31 42; p 'X䀀䀀'; s 35 44; printf '\033[30G'; p 'ーー'; s; pn
+p 'overwrite + Erase-in-Line -->'; s 31 42; p 'X䀀䀀'; s 35 44; printf '\033[30G'; p 'ーー'; s; printf '\033[0K'; pn
+pn
diff --git a/src/libs/3rdparty/winpty/misc/font-notes.txt b/src/libs/3rdparty/winpty/misc/font-notes.txt
new file mode 100644
index 0000000000..d4e36d8e25
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/font-notes.txt
@@ -0,0 +1,300 @@
+==================================================================
+Notes regarding fonts, code pages, and East Asian character widths
+==================================================================
+
+
+Registry settings
+=================
+
+ * There are console registry settings in `HKCU\Console`. That key has many
+ default settings (e.g. the default font settings) and also per-app subkeys
+ for app-specific overrides.
+
+ * It is possible to override the code page with an app-specific setting.
+
+ * There are registry settings in
+ `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console`. In particular,
+ the `TrueTypeFont` subkey has a list of suitable font names associated with
+ various CJK code pages, as well as default font names.
+
+ * There are two values in `HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage`
+ that specify the current code pages -- `OEMCP` and `ACP`. Setting the
+ system locale via the Control Panel's "Region" or "Language" dialogs seems
+ to change these code page values.
+
+
+Console fonts
+=============
+
+ * The `FontFamily` field of `CONSOLE_FONT_INFOEX` has two parts:
+ - The high four bits can be exactly one of the `FF_xxxx` font families:
+ FF_DONTCARE(0x00)
+ FF_ROMAN(0x10)
+ FF_SWISS(0x20)
+ FF_MODERN(0x30)
+ FF_SCRIPT(0x40)
+ FF_DECORATIVE(0x50)
+ - The low four bits are a bitmask:
+ TMPF_FIXED_PITCH(1) -- actually means variable pitch
+ TMPF_VECTOR(2)
+ TMPF_TRUETYPE(4)
+ TMPF_DEVICE(8)
+
+ * Each console has its own independent console font table. The current font
+ is identified with an index into this table. The size of the table is
+ returned by the undocumented `GetNumberOfConsoleFonts` API. It is apparently
+ possible to get the table size without this API, by instead calling
+ `GetConsoleFontSize` on each nonnegative index starting with 0 until the API
+ fails by returning (0, 0).
+
+ * The font table grows dynamically. Each time the console is configured with
+ a previously-unused (FaceName, Size) combination, two entries are added to
+ the font table -- one with normal weight and one with bold weight. Fonts
+ added this way are always TrueType fonts.
+
+ * Initially, the font table appears to contain only raster fonts. For
+ example, on an English Windows 8 installation, here is the initial font
+ table:
+ font 0: 4x6
+ font 1: 6x8
+ font 2: 8x8
+ font 3: 16x8
+ font 4: 5x12
+ font 5: 7x12
+ font 6: 8x12 -- the current font
+ font 7: 16x12
+ font 8: 12x16
+ font 9: 10x18
+ `GetNumberOfConsoleFonts` returns 10, and this table matches the raster font
+ sizes according to the console properties dialog.
+
+ * With a Japanese or Chinese locale, the initial font table appears to contain
+ the sizes applicable to both the East Asian raster font, as well as the
+ sizes for the CP437/CP1252 raster font.
+
+ * The index passed to `SetCurrentConsoleFontEx` apparently has no effect.
+ The undocumented `SetConsoleFont` API, however, accepts *only* a font index,
+ and on Windows 8 English, it switches between all 10 fonts, even font index
+ #0.
+
+ * If the index passed to `SetConsoleFont` identifies a Raster Font
+ incompatible with the current code page, then another Raster Font is
+ activated.
+
+ * Passing "Terminal" to `SetCurrentConsoleFontEx` seems to have no effect.
+ Perhaps relatedly, `SetCurrentConsoleFontEx` does not fail if it is given a
+ bogus `FaceName`. Some font is still chosen and activated. Passing a face
+ name and height seems to work reliably, modulo the CP936 issue described
+ below.
+
+
+Console fonts and code pages
+============================
+
+ * On an English Windows installation, the default code page is 437, and it
+ cannot be set to 932 (Shift-JIS). (The API call fails.) Changing the
+ system locale to "Japanese (Japan)" using the Region/Language dialog
+ changes the default CP to 932 and permits changing the console CP between
+ 437 and 932.
+
+ * A console has both an input code page and an output code page
+ (`{Get,Set}ConsoleCP` and `{Get,Set}ConsoleOutputCP`). I'm not going to
+ distinguish between the two for this document; presumably only the output
+ CP matters. The code page can change while the console is open, e.g.
+ by running `mode con: cp select={932,437,1252}` or by calling
+ `SetConsoleOutputCP`.
+
+ * The current code page restricts which TrueType fonts and which Raster Font
+ sizes are available in the console properties dialog. This can change
+ while the console is open.
+
+ * Changing the code page almost(?) always changes the current console font.
+ So far, I don't know how the new font is chosen.
+
+ * With a CP of 932, the only TrueType font available in the console properties
+ dialog is "MS Gothic", displayed as "MS ゴシック". It is still possible to
+ use the English-default TrueType console fonts, Lucida Console and Consolas,
+ via `SetCurrentConsoleFontEx`.
+
+ * When using a Raster Font and CP437 or CP1252, writing a UTF-16 codepoint not
+ representable in the code page instead writes a question mark ('?') to the
+ console. This conversion does not apply with a TrueType font, nor with the
+ Raster Font for CP932 or CP936.
+
+
+ReadConsoleOutput and double-width characters
+==============================================
+
+ * With a Raster Font active, when `ReadConsoleOutputW` reads two cells of a
+ double-width character, it fills only a single `CHAR_INFO` structure. The
+ unused trailing `CHAR_INFO` structures are zero-filled. With a TrueType
+ font active, `ReadConsoleOutputW` instead fills two `CHAR_INFO` structures,
+ the first marked with `COMMON_LVB_LEADING_BYTE` and the second marked with
+ `COMMON_LVB_TRAILING_BYTE`. The flag is a misnomer--there aren't two
+ *bytes*, but two cells, and they have equal `CHAR_INFO.Char.UnicodeChar`
+ values.
+
+ * `ReadConsoleOutputA`, on the other hand, reads two `CHAR_INFO` cells, and
+ if the UTF-16 value can be represented as two bytes in the ANSI/OEM CP, then
+ the two bytes are placed in the two `CHAR_INFO.Char.AsciiChar` values, and
+ the `COMMON_LVB_{LEADING,TRAILING}_BYTE` values are also used. If the
+ codepoint isn't representable, I don't remember what happens -- I think the
+ `AsciiChar` values take on an invalid marker.
+
+ * Reading only one cell of a double-width character reads a space (U+0020)
+ instead. Raster-vs-TrueType and wide-vs-ANSI do not matter.
+ - XXX: what about attributes? Can a double-width character have mismatched
+ color attributes?
+ - XXX: what happens when writing to just one cell of a double-width
+ character?
+
+
+Default Windows fonts for East Asian languages
+==============================================
+CP932 / Japanese: "MS ゴシック" (MS Gothic)
+CP936 / Chinese Simplified: "新宋体" (SimSun)
+
+
+Unreliable character width (half-width vs full-width)
+=====================================================
+
+The half-width vs full-width status of a codepoint depends on at least these variables:
+ * OS version (Win10 legacy and new modes are different versions)
+ * system locale (English vs Japanese vs Chinese Simplified vs Chinese Traditional, etc)
+ * code page (437 vs 932 vs 936, etc)
+ * raster vs TrueType (Terminal vs MS Gothic vs SimSun, etc)
+ * font size
+ * rendered-vs-model (rendered width can be larger or smaller than model width)
+
+Example 1: U+2014 (EM DASH): East_Asian_Width: Ambiguous
+--------------------------------------------------------
+ rendered modeled
+CP932: Win7/8 Raster Fonts half half
+CP932: Win7/8 Gothic 14/15px half full
+CP932: Win7/8 Consolas 14/15px half full
+CP932: Win7/8 Lucida Console 14px half full
+CP932: Win7/8 Lucida Console 15px half half
+CP932: Win10New Raster Fonts half half
+CP932: Win10New Gothic 14/15px half half
+CP932: Win10New Consolas 14/15px half half
+CP932: Win10New Lucida Console 14/15px half half
+
+CP936: Win7/8 Raster Fonts full full
+CP936: Win7/8 SimSun 14px full full
+CP936: Win7/8 SimSun 15px full half
+CP936: Win7/8 Consolas 14/15px half full
+CP936: Win10New Raster Fonts full full
+CP936: Win10New SimSum 14/15px full full
+CP936: Win10New Consolas 14/15px half half
+
+Example 2: U+3044 (HIRAGANA LETTER I): East_Asian_Width: Wide
+-------------------------------------------------------------
+ rendered modeled
+CP932: Win7/8/10N Raster Fonts full full
+CP932: Win7/8/10N Gothic 14/15px full full
+CP932: Win7/8/10N Consolas 14/15px half(*2) full
+CP932: Win7/8/10N Lucida Console 14/15px half(*3) full
+
+CP936: Win7/8/10N Raster Fonts full full
+CP936: Win7/8/10N SimSun 14/15px full full
+CP936: Win7/8/10N Consolas 14/15px full full
+
+Example 3: U+30FC (KATAKANA-HIRAGANA PROLONGED SOUND MARK): East_Asian_Width: Wide
+----------------------------------------------------------------------------------
+ rendered modeled
+CP932: Win7 Raster Fonts full full
+CP932: Win7 Gothic 14/15px full full
+CP932: Win7 Consolas 14/15px half(*2) full
+CP932: Win7 Lucida Console 14px half(*3) full
+CP932: Win7 Lucida Console 15px half(*3) half
+CP932: Win8 Raster Fonts full full
+CP932: Win8 Gothic 14px full half
+CP932: Win8 Gothic 15px full full
+CP932: Win8 Consolas 14/15px half(*2) full
+CP932: Win8 Lucida Console 14px half(*3) full
+CP932: Win8 Lucida Console 15px half(*3) half
+CP932: Win10New Raster Fonts full full
+CP932: Win10New Gothic 14/15px full full
+CP932: Win10New Consolas 14/15px half(*2) half
+CP932: Win10New Lucida Console 14/15px half(*2) half
+
+CP936: Win7/8 Raster Fonts full full
+CP936: Win7/8 SimSun 14px full full
+CP936: Win7/8 SimSun 15px full half
+CP936: Win7/8 Consolas 14px full full
+CP936: Win7/8 Consolas 15px full half
+CP936: Win10New Raster Fonts full full
+CP936: Win10New SimSum 14/15px full full
+CP936: Win10New Consolas 14/15px full full
+
+Example 4: U+4000 (CJK UNIFIED IDEOGRAPH-4000): East_Asian_Width: Wide
+----------------------------------------------------------------------
+ rendered modeled
+CP932: Win7 Raster Fonts half(*1) half
+CP932: Win7 Gothic 14/15px full full
+CP932: Win7 Consolas 14/15px half(*2) full
+CP932: Win7 Lucida Console 14px half(*3) full
+CP932: Win7 Lucida Console 15px half(*3) half
+CP932: Win8 Raster Fonts half(*1) half
+CP932: Win8 Gothic 14px full half
+CP932: Win8 Gothic 15px full full
+CP932: Win8 Consolas 14/15px half(*2) full
+CP932: Win8 Lucida Console 14px half(*3) full
+CP932: Win8 Lucida Console 15px half(*3) half
+CP932: Win10New Raster Fonts half(*1) half
+CP932: Win10New Gothic 14/15px full full
+CP932: Win10New Consolas 14/15px half(*2) half
+CP932: Win10New Lucida Console 14/15px half(*2) half
+
+CP936: Win7/8 Raster Fonts full full
+CP936: Win7/8 SimSun 14px full full
+CP936: Win7/8 SimSun 15px full half
+CP936: Win7/8 Consolas 14px full full
+CP936: Win7/8 Consolas 15px full half
+CP936: Win10New Raster Fonts full full
+CP936: Win10New SimSum 14/15px full full
+CP936: Win10New Consolas 14/15px full full
+
+(*1) Rendered as a half-width filled white box
+(*2) Rendered as a half-width box with a question mark inside
+(*3) Rendered as a half-width empty box
+(!!) One of the only places in Win10New where rendered and modeled width disagree
+
+
+Windows quirk: unreliable font heights with CP936 / Chinese Simplified
+======================================================================
+
+When I set the font to 新宋体 17px, using either the properties dialog or
+`SetCurrentConsoleFontEx`, the height reported by `GetCurrentConsoleFontEx` is
+not 17, but is instead 19. The same problem does not affect Raster Fonts,
+nor have I seen the problem in the English or Japanese locales. I observed
+this with Windows 7 and Windows 10 new mode.
+
+If I set the font using the facename, width, *and* height, then the
+`SetCurrentConsoleFontEx` and `GetCurrentConsoleFontEx` values agree. If I
+set the font using *only* the facename and height, then the two values
+disagree.
+
+
+Windows bug: GetCurrentConsoleFontEx is initially invalid
+=========================================================
+
+ - Assume there is no configured console font name in the registry. In this
+ case, the console defaults to a raster font.
+ - Open a new console and call the `GetCurrentConsoleFontEx` API.
+ - The `FaceName` field of the returned `CONSOLE_FONT_INFOEX` data
+ structure is incorrect. On Windows 7, 8, and 10, I observed that the
+ field was blank. On Windows 8, occasionally, it instead contained:
+ U+AE72 U+75BE U+0001
+ The other fields of the structure all appeared correct:
+ nFont=6 dwFontSize=(8,12) FontFamily=0x30 FontWeight=400
+ - The `FaceName` field becomes initialized easily:
+ - Open the console properties dialog and click OK. (Cancel is not
+ sufficient.)
+ - Call the undocumented `SetConsoleFont` with the current font table
+ index, which is 6 in the example above.
+ - It seems that the console uncritically accepts whatever string is
+ stored in the registry, including a blank string, and passes it on the
+ the `GetCurrentConsoleFontEx` caller. It is possible to get the console
+ to *write* a blank setting into the registry -- simply open the console
+ (default or app-specific) properties and click OK.
diff --git a/src/libs/3rdparty/winpty/misc/winbug-15048.cc b/src/libs/3rdparty/winpty/misc/winbug-15048.cc
new file mode 100644
index 0000000000..0e98d648c5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/misc/winbug-15048.cc
@@ -0,0 +1,201 @@
+/*
+
+Test program demonstrating a problem in Windows 15048's ReadConsoleOutput API.
+
+To compile:
+
+ cl /nologo /EHsc winbug-15048.cc shell32.lib
+
+Example of regressed input:
+
+Case 1:
+
+ > chcp 932
+ > winbug-15048 -face-gothic 3044
+
+ Correct output:
+
+ 1**34 (nb: U+3044 replaced with '**' to avoid MSVC encoding warning)
+ 5678
+
+ ReadConsoleOutputW (both rows, 3 cols)
+ row 0: U+0031(0007) U+3044(0107) U+3044(0207) U+0033(0007)
+ row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007)
+
+ ReadConsoleOutputW (both rows, 4 cols)
+ row 0: U+0031(0007) U+3044(0107) U+3044(0207) U+0033(0007) U+0034(0007)
+ row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
+
+ ReadConsoleOutputW (second row)
+ row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
+
+ ...
+
+ Win10 15048 bad output:
+
+ 1**34
+ 5678
+
+ ReadConsoleOutputW (both rows, 3 cols)
+ row 0: U+0031(0007) U+3044(0007) U+0033(0007) U+0035(0007)
+ row 1: U+0036(0007) U+0037(0007) U+0038(0007) U+0000(0000)
+
+ ReadConsoleOutputW (both rows, 4 cols)
+ row 0: U+0031(0007) U+3044(0007) U+0033(0007) U+0034(0007) U+0035(0007)
+ row 1: U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007) U+0000(0000)
+
+ ReadConsoleOutputW (second row)
+ row 1: U+0035(0007) U+0036(0007) U+0037(0007) U+0038(0007) U+0020(0007)
+
+ ...
+
+ The U+3044 character (HIRAGANA LETTER I) occupies two columns, but it only
+ fills one record in the ReadConsoleOutput output buffer, which has the
+ effect of shifting the first cell of the second row into the last cell of
+ the first row. Ordinarily, the first and second cells would also have the
+ COMMON_LVB_LEADING_BYTE and COMMON_LVB_TRAILING_BYTE attributes set, which
+ allows winpty to detect the double-column character.
+
+Case 2:
+
+ > chcp 437
+ > winbug-15048 -face "Lucida Console" -h 4 221A
+
+ The same issue happens with U+221A (SQUARE ROOT), but only in certain
+ fonts. The console seems to think this character occupies two columns
+ if the font is sufficiently small. The Windows console properties dialog
+ doesn't allow fonts below 5 pt, but winpty tries to use 2pt and 4pt Lucida
+ Console to allow very large console windows.
+
+Case 3:
+
+ > chcp 437
+ > winbug-15048 -face "Lucida Console" -h 12 FF12
+
+ The console selection system thinks U+FF12 (FULLWIDTH DIGIT TWO) occupies
+ two columns, which happens to be correct, but it's displayed as a single
+ column unrecognized character. It otherwise behaves the same as the other
+ cases.
+
+*/
+
+#include <windows.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+
+#include <string>
+
+#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
+
+// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
+const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese
+const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese
+const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese
+const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean
+
+static void set_font(const wchar_t *name, int size) {
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_FONT_INFOEX fontex {};
+ fontex.cbSize = sizeof(fontex);
+ fontex.dwFontSize.Y = size;
+ fontex.FontWeight = 400;
+ fontex.FontFamily = 0x36;
+ wcsncpy(fontex.FaceName, name, COUNT_OF(fontex.FaceName));
+ assert(SetCurrentConsoleFontEx(conout, FALSE, &fontex));
+}
+
+static void usage(const wchar_t *prog) {
+ printf("Usage: %ls [options]\n", prog);
+ printf(" -h HEIGHT\n");
+ printf(" -face FACENAME\n");
+ printf(" -face-{gothic|simsun|minglight|gulimche) [JP,CN-sim,CN-tra,KR]\n");
+ printf(" hhhh -- print U+hhhh\n");
+ exit(1);
+}
+
+static void dump_region(SMALL_RECT region, const char *name) {
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ CHAR_INFO buf[1000];
+ memset(buf, 0xcc, sizeof(buf));
+
+ const int w = region.Right - region.Left + 1;
+ const int h = region.Bottom - region.Top + 1;
+
+ assert(ReadConsoleOutputW(
+ conout, buf, { (short)w, (short)h }, { 0, 0 },
+ &region));
+
+ printf("\n");
+ printf("ReadConsoleOutputW (%s)\n", name);
+ for (int y = 0; y < h; ++y) {
+ printf("row %d: ", region.Top + y);
+ for (int i = 0; i < region.Left * 13; ++i) {
+ printf(" ");
+ }
+ for (int x = 0; x < w; ++x) {
+ const int i = y * w + x;
+ printf("U+%04x(%04x) ", buf[i].Char.UnicodeChar, buf[i].Attributes);
+ }
+ printf("\n");
+ }
+}
+
+int main() {
+ wchar_t *cmdline = GetCommandLineW();
+ int argc = 0;
+ wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
+ const wchar_t *font_name = L"Lucida Console";
+ int font_height = 8;
+ int test_ch = 0xff12; // U+FF12 FULLWIDTH DIGIT TWO
+
+ for (int i = 1; i < argc; ++i) {
+ const std::wstring arg = argv[i];
+ const std::wstring next = i + 1 < argc ? argv[i + 1] : L"";
+ if (arg == L"-face" && i + 1 < argc) {
+ font_name = argv[i + 1];
+ i++;
+ } else if (arg == L"-face-gothic") {
+ font_name = kMSGothic;
+ } else if (arg == L"-face-simsun") {
+ font_name = kNSimSun;
+ } else if (arg == L"-face-minglight") {
+ font_name = kMingLight;
+ } else if (arg == L"-face-gulimche") {
+ font_name = kGulimChe;
+ } else if (arg == L"-h" && i + 1 < argc) {
+ font_height = _wtoi(next.c_str());
+ i++;
+ } else if (arg.c_str()[0] != '-') {
+ test_ch = wcstol(arg.c_str(), NULL, 16);
+ } else {
+ printf("Unrecognized argument: %ls\n", arg.c_str());
+ usage(argv[0]);
+ }
+ }
+
+ const HANDLE conout = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ set_font(font_name, font_height);
+
+ system("cls");
+ DWORD actual = 0;
+ wchar_t output[] = L"1234\n5678\n";
+ output[1] = test_ch;
+ WriteConsoleW(conout, output, 10, &actual, nullptr);
+
+ dump_region({ 0, 0, 3, 1 }, "both rows, 3 cols");
+ dump_region({ 0, 0, 4, 1 }, "both rows, 4 cols");
+ dump_region({ 0, 1, 4, 1 }, "second row");
+ dump_region({ 0, 0, 4, 0 }, "first row");
+ dump_region({ 1, 0, 4, 0 }, "first row, skip 1");
+ dump_region({ 2, 0, 4, 0 }, "first row, skip 2");
+ dump_region({ 3, 0, 4, 0 }, "first row, skip 3");
+
+ set_font(font_name, 14);
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/ship/build-pty4j-libpty.bat b/src/libs/3rdparty/winpty/ship/build-pty4j-libpty.bat
new file mode 100644
index 0000000000..b6bca7b079
--- /dev/null
+++ b/src/libs/3rdparty/winpty/ship/build-pty4j-libpty.bat
@@ -0,0 +1,36 @@
+@echo off
+
+setlocal
+cd %~dp0..
+set Path=C:\Python27;C:\Program Files\Git\cmd;%Path%
+
+call "%VS140COMNTOOLS%\VsDevCmd.bat" || goto :fail
+
+rmdir /s/q build-libpty 2>NUL
+mkdir build-libpty\win
+mkdir build-libpty\win\x86
+mkdir build-libpty\win\x86_64
+mkdir build-libpty\win\xp
+
+rmdir /s/q src\Release 2>NUL
+rmdir /s/q src\.vs 2>NUL
+del src\*.vcxproj src\*.vcxproj.filters src\*.sln src\*.sdf 2>NUL
+
+call vcbuild.bat --msvc-platform Win32 --gyp-msvs-version 2015 --toolset v140_xp || goto :fail
+copy src\Release\Win32\winpty.dll build-libpty\win\xp || goto :fail
+copy src\Release\Win32\winpty-agent.exe build-libpty\win\xp || goto :fail
+
+call vcbuild.bat --msvc-platform Win32 --gyp-msvs-version 2015 || goto :fail
+copy src\Release\Win32\winpty.dll build-libpty\win\x86 || goto :fail
+copy src\Release\Win32\winpty-agent.exe build-libpty\win\x86 || goto :fail
+
+call vcbuild.bat --msvc-platform x64 --gyp-msvs-version 2015 || goto :fail
+copy src\Release\x64\winpty.dll build-libpty\win\x86_64 || goto :fail
+copy src\Release\x64\winpty-agent.exe build-libpty\win\x86_64 || goto :fail
+
+echo success
+goto :EOF
+
+:fail
+echo error: build failed
+exit /b 1
diff --git a/src/libs/3rdparty/winpty/ship/common_ship.py b/src/libs/3rdparty/winpty/ship/common_ship.py
new file mode 100644
index 0000000000..b587ac7ce1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/ship/common_ship.py
@@ -0,0 +1,89 @@
+import os
+import sys
+
+# These scripts need to continue using Python 2 rather than 3, because
+# make_msvc_package.py puts the current Python interpreter on the PATH for the
+# sake of gyp, and gyp doesn't work with Python 3 yet.
+# https://bugs.chromium.org/p/gyp/issues/detail?id=36
+if os.name != "nt":
+ sys.exit("Error: ship scripts require native Python 2.7. (wrong os.name)")
+if sys.version_info[0:2] != (2,7):
+ sys.exit("Error: ship scripts require native Python 2.7. (wrong version)")
+
+import glob
+import hashlib
+import shutil
+import subprocess
+from distutils.spawn import find_executable
+
+topDir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+
+with open(topDir + "/VERSION.txt", "rt") as f:
+ winptyVersion = f.read().strip()
+
+def rmrf(patterns):
+ for pattern in patterns:
+ for path in glob.glob(pattern):
+ if os.path.isdir(path) and not os.path.islink(path):
+ print("+ rm -r " + path)
+ sys.stdout.flush()
+ shutil.rmtree(path)
+ elif os.path.isfile(path):
+ print("+ rm " + path)
+ sys.stdout.flush()
+ os.remove(path)
+
+def mkdir(path):
+ if not os.path.isdir(path):
+ os.makedirs(path)
+
+def requireExe(name, guesses):
+ if find_executable(name) is None:
+ for guess in guesses:
+ if os.path.exists(guess):
+ newDir = os.path.dirname(guess)
+ print("Adding " + newDir + " to Path to provide " + name)
+ os.environ["Path"] = newDir + ";" + os.environ["Path"]
+ ret = find_executable(name)
+ if ret is None:
+ sys.exit("Error: required EXE is missing from Path: " + name)
+ return ret
+
+class ModifyEnv:
+ def __init__(self, **kwargs):
+ self._changes = dict(kwargs)
+ self._original = dict()
+
+ def __enter__(self):
+ for var, val in self._changes.items():
+ self._original[var] = os.environ[var]
+ os.environ[var] = val
+
+ def __exit__(self, type, value, traceback):
+ for var, val in self._original.items():
+ os.environ[var] = val
+
+def sha256(path):
+ with open(path, "rb") as fp:
+ return hashlib.sha256(fp.read()).hexdigest()
+
+def checkSha256(path, expected):
+ actual = sha256(path)
+ if actual != expected:
+ sys.exit("error: sha256 hash mismatch on {}: expected {}, found {}".format(
+ path, expected, actual))
+
+requireExe("git.exe", [
+ "C:\\Program Files\\Git\\cmd\\git.exe",
+ "C:\\Program Files (x86)\\Git\\cmd\\git.exe"
+])
+
+commitHash = subprocess.check_output(["git.exe", "rev-parse", "HEAD"]).strip()
+defaultPathEnviron = "C:\\Windows\\System32;C:\\Windows"
+
+ZIP_TOOL = requireExe("7z.exe", [
+ "C:\\Program Files\\7-Zip\\7z.exe",
+ "C:\\Program Files (x86)\\7-Zip\\7z.exe",
+])
+
+requireExe("curl.exe", [])
diff --git a/src/libs/3rdparty/winpty/ship/make_msvc_package.py b/src/libs/3rdparty/winpty/ship/make_msvc_package.py
new file mode 100644
index 0000000000..2d10aac787
--- /dev/null
+++ b/src/libs/3rdparty/winpty/ship/make_msvc_package.py
@@ -0,0 +1,163 @@
+#!python
+
+# Copyright (c) 2016 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+#
+# Run with native CPython 2.7.
+#
+# This script looks for MSVC using a version-specific environment variable,
+# such as VS140COMNTOOLS for MSVC 2015.
+#
+
+import common_ship
+
+import argparse
+import os
+import shutil
+import subprocess
+import sys
+
+os.chdir(common_ship.topDir)
+
+MSVC_VERSION_TABLE = {
+ "2015" : {
+ "package_name" : "msvc2015",
+ "gyp_version" : "2015",
+ "common_tools_env" : "VS140COMNTOOLS",
+ "xp_toolset" : "v140_xp",
+ },
+ "2013" : {
+ "package_name" : "msvc2013",
+ "gyp_version" : "2013",
+ "common_tools_env" : "VS120COMNTOOLS",
+ "xp_toolset" : "v120_xp",
+ },
+}
+
+ARCH_TABLE = {
+ "x64" : {
+ "msvc_platform" : "x64",
+ },
+ "ia32" : {
+ "msvc_platform" : "Win32",
+ },
+}
+
+def readArguments():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--msvc-version", default="2015")
+ ret = parser.parse_args()
+ if ret.msvc_version not in MSVC_VERSION_TABLE:
+ sys.exit("Error: unrecognized version: " + ret.msvc_version + ". " +
+ "Versions: " + " ".join(sorted(MSVC_VERSION_TABLE.keys())))
+ return ret
+
+ARGS = readArguments()
+
+def checkoutGyp():
+ if os.path.isdir("build-gyp"):
+ return
+ subprocess.check_call([
+ "git.exe",
+ "clone",
+ "https://chromium.googlesource.com/external/gyp",
+ "build-gyp"
+ ])
+
+def cleanMsvc():
+ common_ship.rmrf("""
+ src/Release src/.vs src/gen
+ src/*.vcxproj src/*.vcxproj.filters src/*.sln src/*.sdf
+ """.split())
+
+def build(arch, packageDir, xp=False):
+ archInfo = ARCH_TABLE[arch]
+ versionInfo = MSVC_VERSION_TABLE[ARGS.msvc_version]
+
+ devCmdPath = os.path.join(os.environ[versionInfo["common_tools_env"]], "VsDevCmd.bat")
+ if not os.path.isfile(devCmdPath):
+ sys.exit("Error: MSVC environment script missing: " + devCmdPath)
+
+ toolsetArgument = " --toolset {}".format(versionInfo["xp_toolset"]) if xp else ""
+ newEnv = os.environ.copy()
+ newEnv["PATH"] = os.path.dirname(sys.executable) + ";" + common_ship.defaultPathEnviron
+ commandLine = (
+ '"' + devCmdPath + '" && ' +
+ " vcbuild.bat" +
+ " --gyp-msvs-version " + versionInfo["gyp_version"] +
+ " --msvc-platform " + archInfo["msvc_platform"] +
+ " --commit-hash " + common_ship.commitHash +
+ toolsetArgument
+ )
+
+ subprocess.check_call(commandLine, shell=True, env=newEnv)
+
+ archPackageDir = os.path.join(packageDir, arch)
+ if xp:
+ archPackageDir += "_xp"
+
+ common_ship.mkdir(archPackageDir + "/bin")
+ common_ship.mkdir(archPackageDir + "/lib")
+
+ binSrc = os.path.join(common_ship.topDir, "src/Release", archInfo["msvc_platform"])
+
+ shutil.copy(binSrc + "/winpty.dll", archPackageDir + "/bin")
+ shutil.copy(binSrc + "/winpty-agent.exe", archPackageDir + "/bin")
+ shutil.copy(binSrc + "/winpty-debugserver.exe", archPackageDir + "/bin")
+ shutil.copy(binSrc + "/winpty.lib", archPackageDir + "/lib")
+
+def buildPackage():
+ versionInfo = MSVC_VERSION_TABLE[ARGS.msvc_version]
+
+ packageName = "winpty-%s-%s" % (
+ common_ship.winptyVersion,
+ versionInfo["package_name"],
+ )
+
+ packageRoot = os.path.join(common_ship.topDir, "ship/packages")
+ packageDir = os.path.join(packageRoot, packageName)
+ packageFile = packageDir + ".zip"
+
+ common_ship.rmrf([packageDir])
+ common_ship.rmrf([packageFile])
+ common_ship.mkdir(packageDir)
+
+ checkoutGyp()
+ cleanMsvc()
+ build("ia32", packageDir, True)
+ build("x64", packageDir, True)
+ cleanMsvc()
+ build("ia32", packageDir)
+ build("x64", packageDir)
+
+ topDir = common_ship.topDir
+
+ common_ship.mkdir(packageDir + "/include")
+ shutil.copy(topDir + "/src/include/winpty.h", packageDir + "/include")
+ shutil.copy(topDir + "/src/include/winpty_constants.h", packageDir + "/include")
+ shutil.copy(topDir + "/LICENSE", packageDir)
+ shutil.copy(topDir + "/README.md", packageDir)
+ shutil.copy(topDir + "/RELEASES.md", packageDir)
+
+ subprocess.check_call([common_ship.ZIP_TOOL, "a", packageFile, "."], cwd=packageDir)
+
+if __name__ == "__main__":
+ buildPackage()
diff --git a/src/libs/3rdparty/winpty/ship/ship.py b/src/libs/3rdparty/winpty/ship/ship.py
new file mode 100644
index 0000000000..44f5862e3e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/ship/ship.py
@@ -0,0 +1,104 @@
+#!python
+
+# Copyright (c) 2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+#
+# Run with native CPython 2.7 on a 64-bit computer.
+#
+
+import common_ship
+
+from optparse import OptionParser
+import multiprocessing
+import os
+import shutil
+import subprocess
+import sys
+
+
+def dllVersion(path):
+ version = subprocess.check_output(
+ ["powershell.exe",
+ "[System.Diagnostics.FileVersionInfo]::GetVersionInfo(\"" + path + "\").FileVersion"])
+ return version.strip()
+
+
+os.chdir(common_ship.topDir)
+
+
+# Determine other build parameters.
+BUILD_KINDS = {
+ "cygwin": {
+ "path": ["bin"],
+ "dll": "bin\\cygwin1.dll",
+ },
+ "msys2": {
+ "path": ["opt\\bin", "usr\\bin"],
+ "dll": "usr\\bin\\msys-2.0.dll",
+ },
+}
+
+
+def buildTarget(kind, syspath, arch):
+
+ binPaths = [os.path.join(syspath, p) for p in BUILD_KINDS[kind]["path"]]
+ binPaths += common_ship.defaultPathEnviron.split(";")
+ newPath = ";".join(binPaths)
+
+ dllver = dllVersion(os.path.join(syspath, BUILD_KINDS[kind]["dll"]))
+ packageName = "winpty-{}-{}-{}-{}".format(common_ship.winptyVersion, kind, dllver, arch)
+ if os.path.exists("ship\\packages\\" + packageName):
+ shutil.rmtree("ship\\packages\\" + packageName)
+
+ print("+ Setting PATH to: {}".format(newPath))
+ with common_ship.ModifyEnv(PATH=newPath):
+ subprocess.check_call(["sh.exe", "configure"])
+ subprocess.check_call(["make.exe", "clean"])
+ makeBaseCmd = [
+ "make.exe",
+ "COMMIT_HASH=" + common_ship.commitHash,
+ "PREFIX=ship/packages/" + packageName
+ ]
+ subprocess.check_call(makeBaseCmd + ["all", "tests", "-j%d" % multiprocessing.cpu_count()])
+ subprocess.check_call(["build\\trivial_test.exe"])
+ subprocess.check_call(makeBaseCmd + ["install"])
+ subprocess.check_call(["tar.exe", "cvfz",
+ packageName + ".tar.gz",
+ packageName], cwd=os.path.join(os.getcwd(), "ship", "packages"))
+
+
+def main():
+ parser = OptionParser()
+ parser.add_option("--kind", type="choice", choices=["cygwin", "msys2"])
+ parser.add_option("--syspath")
+ parser.add_option("--arch", type="choice", choices=["ia32", "x64"])
+ (args, extra) = parser.parse_args()
+
+ args.kind or parser.error("--kind must be specified")
+ args.arch or parser.error("--arch must be specified")
+ args.syspath or parser.error("--syspath must be specified")
+ extra and parser.error("unexpected positional argument(s)")
+
+ buildTarget(args.kind, args.syspath, args.arch)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/libs/3rdparty/winpty/src/CMakeLists.txt b/src/libs/3rdparty/winpty/src/CMakeLists.txt
new file mode 100644
index 0000000000..22b15111d4
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/CMakeLists.txt
@@ -0,0 +1,111 @@
+if (MSVC)
+ add_compile_definitions(NOMINMAX UNICODE _UNICODE)
+endif()
+
+file(WRITE ${CMAKE_BINARY_DIR}/GenVersion.h.in [=[
+const char GenVersion_Version[] = "@VERSION@";
+const char GenVersion_Commit[] = "@COMMIT_HASH@";
+]=])
+
+file(READ ../VERSION.txt VERSION)
+string(REPLACE "\n" "" VERSION "${VERSION}")
+configure_file(${CMAKE_BINARY_DIR}/GenVersion.h.in ${CMAKE_BINARY_DIR}/GenVersion.h @ONLY)
+
+set(shared_sources
+ shared/AgentMsg.h
+ shared/BackgroundDesktop.h
+ shared/BackgroundDesktop.cc
+ shared/Buffer.h
+ shared/Buffer.cc
+ shared/DebugClient.h
+ shared/DebugClient.cc
+ shared/GenRandom.h
+ shared/GenRandom.cc
+ shared/OsModule.h
+ shared/OwnedHandle.h
+ shared/OwnedHandle.cc
+ shared/StringBuilder.h
+ shared/StringUtil.cc
+ shared/StringUtil.h
+ shared/UnixCtrlChars.h
+ shared/WindowsSecurity.cc
+ shared/WindowsSecurity.h
+ shared/WindowsVersion.h
+ shared/WindowsVersion.cc
+ shared/WinptyAssert.h
+ shared/WinptyAssert.cc
+ shared/WinptyException.h
+ shared/WinptyException.cc
+ shared/WinptyVersion.h
+ shared/WinptyVersion.cc
+ shared/winpty_snprintf.h
+)
+
+#
+# winpty-agent
+#
+
+add_qtc_executable(winpty-agent
+ INCLUDES
+ include ${CMAKE_BINARY_DIR}
+ DEFINES WINPTY_AGENT_ASSERT
+ PROPERTIES QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
+ SOURCES
+ agent/Agent.h
+ agent/Agent.cc
+ agent/AgentCreateDesktop.h
+ agent/AgentCreateDesktop.cc
+ agent/ConsoleFont.cc
+ agent/ConsoleFont.h
+ agent/ConsoleInput.cc
+ agent/ConsoleInput.h
+ agent/ConsoleInputReencoding.cc
+ agent/ConsoleInputReencoding.h
+ agent/ConsoleLine.cc
+ agent/ConsoleLine.h
+ agent/Coord.h
+ agent/DebugShowInput.h
+ agent/DebugShowInput.cc
+ agent/DefaultInputMap.h
+ agent/DefaultInputMap.cc
+ agent/DsrSender.h
+ agent/EventLoop.h
+ agent/EventLoop.cc
+ agent/InputMap.h
+ agent/InputMap.cc
+ agent/LargeConsoleRead.h
+ agent/LargeConsoleRead.cc
+ agent/NamedPipe.h
+ agent/NamedPipe.cc
+ agent/Scraper.h
+ agent/Scraper.cc
+ agent/SimplePool.h
+ agent/SmallRect.h
+ agent/Terminal.h
+ agent/Terminal.cc
+ agent/UnicodeEncoding.h
+ agent/Win32Console.cc
+ agent/Win32Console.h
+ agent/Win32ConsoleBuffer.cc
+ agent/Win32ConsoleBuffer.h
+ agent/main.cc
+ ${shared_sources}
+)
+
+#
+# libwinpty
+#
+
+add_qtc_library(winpty STATIC
+ INCLUDES ${CMAKE_BINARY_DIR}
+ PUBLIC_DEFINES COMPILING_WINPTY_DLL
+ PROPERTIES QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
+ SOURCES
+ libwinpty/AgentLocation.cc
+ libwinpty/AgentLocation.h
+ libwinpty/winpty.cc
+ ${shared_sources}
+)
+
+target_include_directories(winpty
+ PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
diff --git a/src/libs/3rdparty/winpty/src/agent/Agent.cc b/src/libs/3rdparty/winpty/src/agent/Agent.cc
new file mode 100644
index 0000000000..986edead13
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Agent.cc
@@ -0,0 +1,612 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Agent.h"
+
+#include <windows.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "../include/winpty_constants.h"
+
+#include "../shared/AgentMsg.h"
+#include "../shared/Buffer.h"
+#include "../shared/DebugClient.h"
+#include "../shared/GenRandom.h"
+#include "../shared/StringBuilder.h"
+#include "../shared/StringUtil.h"
+#include "../shared/WindowsVersion.h"
+#include "../shared/WinptyAssert.h"
+
+#include "ConsoleFont.h"
+#include "ConsoleInput.h"
+#include "NamedPipe.h"
+#include "Scraper.h"
+#include "Terminal.h"
+#include "Win32ConsoleBuffer.h"
+
+namespace {
+
+static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType)
+{
+ if (dwCtrlType == CTRL_C_EVENT) {
+ // Do nothing and claim to have handled the event.
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// We can detect the new Windows 10 console by observing the effect of the
+// Mark command. In older consoles, Mark temporarily moves the cursor to the
+// top-left of the console window. In the new console, the cursor isn't
+// initially moved.
+//
+// We might like to use Mark to freeze the console, but we can't, because when
+// the Mark command ends, the console moves the cursor back to its starting
+// point, even if the console application has moved it in the meantime.
+static void detectNewWindows10Console(
+ Win32Console &console, Win32ConsoleBuffer &buffer)
+{
+ if (!isAtLeastWindows8()) {
+ return;
+ }
+
+ ConsoleScreenBufferInfo info = buffer.bufferInfo();
+
+ // Make sure the window isn't 1x1. AFAIK, this should never happen
+ // accidentally. It is difficult to make it happen deliberately.
+ if (info.srWindow.Left == info.srWindow.Right &&
+ info.srWindow.Top == info.srWindow.Bottom) {
+ trace("detectNewWindows10Console: Initial console window was 1x1 -- "
+ "expanding for test");
+ setSmallFont(buffer.conout(), 400, false);
+ buffer.moveWindow(SmallRect(0, 0, 1, 1));
+ buffer.resizeBuffer(Coord(400, 1));
+ buffer.moveWindow(SmallRect(0, 0, 2, 1));
+ // This use of GetLargestConsoleWindowSize ought to be unnecessary
+ // given the behavior I've seen from moveWindow(0, 0, 1, 1), but
+ // I'd like to be especially sure, considering that this code will
+ // rarely be tested.
+ const auto largest = GetLargestConsoleWindowSize(buffer.conout());
+ buffer.moveWindow(
+ SmallRect(0, 0, std::min(largest.X, buffer.bufferSize().X), 1));
+ info = buffer.bufferInfo();
+ ASSERT(info.srWindow.Right > info.srWindow.Left &&
+ "Could not expand console window from 1x1");
+ }
+
+ // Test whether MARK moves the cursor.
+ const Coord initialPosition(info.srWindow.Right, info.srWindow.Bottom);
+ buffer.setCursorPosition(initialPosition);
+ ASSERT(!console.frozen());
+ console.setFreezeUsesMark(true);
+ console.setFrozen(true);
+ const bool isNewW10 = (buffer.cursorPosition() == initialPosition);
+ console.setFrozen(false);
+ buffer.setCursorPosition(Coord(0, 0));
+
+ trace("Attempting to detect new Windows 10 console using MARK: %s",
+ isNewW10 ? "detected" : "not detected");
+ console.setFreezeUsesMark(false);
+ console.setNewW10(isNewW10);
+}
+
+static inline WriteBuffer newPacket() {
+ WriteBuffer packet;
+ packet.putRawValue<uint64_t>(0); // Reserve space for size.
+ return packet;
+}
+
+static HANDLE duplicateHandle(HANDLE h) {
+ HANDLE ret = nullptr;
+ if (!DuplicateHandle(
+ GetCurrentProcess(), h,
+ GetCurrentProcess(), &ret,
+ 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+ ASSERT(false && "DuplicateHandle failed!");
+ }
+ return ret;
+}
+
+// It's safe to truncate a handle from 64-bits to 32-bits, or to sign-extend it
+// back to 64-bits. See the MSDN article, "Interprocess Communication Between
+// 32-bit and 64-bit Applications".
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203.aspx
+static int64_t int64FromHandle(HANDLE h) {
+ return static_cast<int64_t>(reinterpret_cast<intptr_t>(h));
+}
+
+} // anonymous namespace
+
+Agent::Agent(LPCWSTR controlPipeName,
+ uint64_t agentFlags,
+ int mouseMode,
+ int initialCols,
+ int initialRows) :
+ m_useConerr((agentFlags & WINPTY_FLAG_CONERR) != 0),
+ m_plainMode((agentFlags & WINPTY_FLAG_PLAIN_OUTPUT) != 0),
+ m_mouseMode(mouseMode)
+{
+ trace("Agent::Agent entered");
+
+ ASSERT(initialCols >= 1 && initialRows >= 1);
+ initialCols = std::min(initialCols, MAX_CONSOLE_WIDTH);
+ initialRows = std::min(initialRows, MAX_CONSOLE_HEIGHT);
+
+ const bool outputColor =
+ !m_plainMode || (agentFlags & WINPTY_FLAG_COLOR_ESCAPES);
+ const Coord initialSize(initialCols, initialRows);
+
+ auto primaryBuffer = openPrimaryBuffer();
+ if (m_useConerr) {
+ m_errorBuffer = Win32ConsoleBuffer::createErrorBuffer();
+ }
+
+ detectNewWindows10Console(m_console, *primaryBuffer);
+
+ m_controlPipe = &connectToControlPipe(controlPipeName);
+ m_coninPipe = &createDataServerPipe(false, L"conin");
+ m_conoutPipe = &createDataServerPipe(true, L"conout");
+ if (m_useConerr) {
+ m_conerrPipe = &createDataServerPipe(true, L"conerr");
+ }
+
+ // Send an initial response packet to winpty.dll containing pipe names.
+ {
+ auto setupPacket = newPacket();
+ setupPacket.putWString(m_coninPipe->name());
+ setupPacket.putWString(m_conoutPipe->name());
+ if (m_useConerr) {
+ setupPacket.putWString(m_conerrPipe->name());
+ }
+ writePacket(setupPacket);
+ }
+
+ std::unique_ptr<Terminal> primaryTerminal;
+ primaryTerminal.reset(new Terminal(*m_conoutPipe,
+ m_plainMode,
+ outputColor));
+ m_primaryScraper.reset(new Scraper(m_console,
+ *primaryBuffer,
+ std::move(primaryTerminal),
+ initialSize));
+ if (m_useConerr) {
+ std::unique_ptr<Terminal> errorTerminal;
+ errorTerminal.reset(new Terminal(*m_conerrPipe,
+ m_plainMode,
+ outputColor));
+ m_errorScraper.reset(new Scraper(m_console,
+ *m_errorBuffer,
+ std::move(errorTerminal),
+ initialSize));
+ }
+
+ m_console.setTitle(m_currentTitle);
+
+ const HANDLE conin = GetStdHandle(STD_INPUT_HANDLE);
+ m_consoleInput.reset(
+ new ConsoleInput(conin, m_mouseMode, *this, m_console));
+
+ // Setup Ctrl-C handling. First restore default handling of Ctrl-C. This
+ // attribute is inherited by child processes. Then register a custom
+ // Ctrl-C handler that does nothing. The handler will be called when the
+ // agent calls GenerateConsoleCtrlEvent.
+ SetConsoleCtrlHandler(NULL, FALSE);
+ SetConsoleCtrlHandler(consoleCtrlHandler, TRUE);
+
+ setPollInterval(25);
+}
+
+Agent::~Agent()
+{
+ trace("Agent::~Agent entered");
+ agentShutdown();
+ if (m_childProcess != NULL) {
+ CloseHandle(m_childProcess);
+ }
+}
+
+// Write a "Device Status Report" command to the terminal. The terminal will
+// reply with a row+col escape sequence. Presumably, the DSR reply will not
+// split a keypress escape sequence, so it should be safe to assume that the
+// bytes before it are complete keypresses.
+void Agent::sendDsr()
+{
+ if (!m_plainMode && !m_conoutPipe->isClosed()) {
+ m_conoutPipe->write("\x1B[6n");
+ }
+}
+
+NamedPipe &Agent::connectToControlPipe(LPCWSTR pipeName)
+{
+ NamedPipe &pipe = createNamedPipe();
+ pipe.connectToServer(pipeName, NamedPipe::OpenMode::Duplex);
+ pipe.setReadBufferSize(64 * 1024);
+ return pipe;
+}
+
+// Returns a new server named pipe. It has not yet been connected.
+NamedPipe &Agent::createDataServerPipe(bool write, const wchar_t *kind)
+{
+ const auto name =
+ (WStringBuilder(128)
+ << L"\\\\.\\pipe\\winpty-"
+ << kind << L'-'
+ << GenRandom().uniqueName()).str_moved();
+ NamedPipe &pipe = createNamedPipe();
+ pipe.openServerPipe(
+ name.c_str(),
+ write ? NamedPipe::OpenMode::Writing
+ : NamedPipe::OpenMode::Reading,
+ write ? 8192 : 0,
+ write ? 0 : 256);
+ if (!write) {
+ pipe.setReadBufferSize(64 * 1024);
+ }
+ return pipe;
+}
+
+void Agent::onPipeIo(NamedPipe &namedPipe)
+{
+ if (&namedPipe == m_conoutPipe || &namedPipe == m_conerrPipe) {
+ autoClosePipesForShutdown();
+ } else if (&namedPipe == m_coninPipe) {
+ pollConinPipe();
+ } else if (&namedPipe == m_controlPipe) {
+ pollControlPipe();
+ }
+}
+
+void Agent::pollControlPipe()
+{
+ if (m_controlPipe->isClosed()) {
+ trace("Agent exiting (control pipe is closed)");
+ shutdown();
+ return;
+ }
+
+ while (true) {
+ uint64_t packetSize = 0;
+ const auto amt1 =
+ m_controlPipe->peek(&packetSize, sizeof(packetSize));
+ if (amt1 < sizeof(packetSize)) {
+ break;
+ }
+ ASSERT(packetSize >= sizeof(packetSize) && packetSize <= SIZE_MAX);
+ if (m_controlPipe->bytesAvailable() < packetSize) {
+ if (m_controlPipe->readBufferSize() < packetSize) {
+ m_controlPipe->setReadBufferSize(packetSize);
+ }
+ break;
+ }
+ std::vector<char> packetData;
+ packetData.resize(packetSize);
+ const auto amt2 = m_controlPipe->read(packetData.data(), packetSize);
+ ASSERT(amt2 == packetSize);
+ try {
+ ReadBuffer buffer(std::move(packetData));
+ buffer.getRawValue<uint64_t>(); // Discard the size.
+ handlePacket(buffer);
+ } catch (const ReadBuffer::DecodeError&) {
+ ASSERT(false && "Decode error");
+ }
+ }
+}
+
+void Agent::handlePacket(ReadBuffer &packet)
+{
+ const int type = packet.getInt32();
+ switch (type) {
+ case AgentMsg::StartProcess:
+ handleStartProcessPacket(packet);
+ break;
+ case AgentMsg::SetSize:
+ // TODO: I think it might make sense to collapse consecutive SetSize
+ // messages. i.e. The terminal process can probably generate SetSize
+ // messages faster than they can be processed, and some GUIs might
+ // generate a flood of them, so if we can read multiple SetSize packets
+ // at once, we can ignore the early ones.
+ handleSetSizePacket(packet);
+ break;
+ case AgentMsg::GetConsoleProcessList:
+ handleGetConsoleProcessListPacket(packet);
+ break;
+ default:
+ trace("Unrecognized message, id:%d", type);
+ }
+}
+
+void Agent::writePacket(WriteBuffer &packet)
+{
+ const auto &bytes = packet.buf();
+ packet.replaceRawValue<uint64_t>(0, bytes.size());
+ m_controlPipe->write(bytes.data(), bytes.size());
+}
+
+void Agent::handleStartProcessPacket(ReadBuffer &packet)
+{
+ ASSERT(m_childProcess == nullptr);
+ ASSERT(!m_closingOutputPipes);
+
+ const uint64_t spawnFlags = packet.getInt64();
+ const bool wantProcessHandle = packet.getInt32() != 0;
+ const bool wantThreadHandle = packet.getInt32() != 0;
+ const auto program = packet.getWString();
+ const auto cmdline = packet.getWString();
+ const auto cwd = packet.getWString();
+ const auto env = packet.getWString();
+ const auto desktop = packet.getWString();
+ packet.assertEof();
+
+ auto cmdlineV = vectorWithNulFromString(cmdline);
+ auto desktopV = vectorWithNulFromString(desktop);
+ auto envV = vectorFromString(env);
+
+ LPCWSTR programArg = program.empty() ? nullptr : program.c_str();
+ LPWSTR cmdlineArg = cmdline.empty() ? nullptr : cmdlineV.data();
+ LPCWSTR cwdArg = cwd.empty() ? nullptr : cwd.c_str();
+ LPWSTR envArg = env.empty() ? nullptr : envV.data();
+
+ STARTUPINFOW sui = {};
+ PROCESS_INFORMATION pi = {};
+ sui.cb = sizeof(sui);
+ sui.lpDesktop = desktop.empty() ? nullptr : desktopV.data();
+ BOOL inheritHandles = FALSE;
+ if (m_useConerr) {
+ inheritHandles = TRUE;
+ sui.dwFlags |= STARTF_USESTDHANDLES;
+ sui.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ sui.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ sui.hStdError = m_errorBuffer->conout();
+ }
+
+ const BOOL success =
+ CreateProcessW(programArg, cmdlineArg, nullptr, nullptr,
+ /*bInheritHandles=*/inheritHandles,
+ /*dwCreationFlags=*/CREATE_UNICODE_ENVIRONMENT,
+ envArg, cwdArg, &sui, &pi);
+ const int lastError = success ? 0 : GetLastError();
+
+ trace("CreateProcess: %s %u",
+ (success ? "success" : "fail"),
+ static_cast<unsigned int>(pi.dwProcessId));
+
+ auto reply = newPacket();
+ if (success) {
+ int64_t replyProcess = 0;
+ int64_t replyThread = 0;
+ if (wantProcessHandle) {
+ replyProcess = int64FromHandle(duplicateHandle(pi.hProcess));
+ }
+ if (wantThreadHandle) {
+ replyThread = int64FromHandle(duplicateHandle(pi.hThread));
+ }
+ CloseHandle(pi.hThread);
+ m_childProcess = pi.hProcess;
+ m_autoShutdown = (spawnFlags & WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN) != 0;
+ m_exitAfterShutdown = (spawnFlags & WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN) != 0;
+ reply.putInt32(static_cast<int32_t>(StartProcessResult::ProcessCreated));
+ reply.putInt64(replyProcess);
+ reply.putInt64(replyThread);
+ } else {
+ reply.putInt32(static_cast<int32_t>(StartProcessResult::CreateProcessFailed));
+ reply.putInt32(lastError);
+ }
+ writePacket(reply);
+}
+
+void Agent::handleSetSizePacket(ReadBuffer &packet)
+{
+ const int cols = packet.getInt32();
+ const int rows = packet.getInt32();
+ packet.assertEof();
+ resizeWindow(cols, rows);
+ auto reply = newPacket();
+ writePacket(reply);
+}
+
+void Agent::handleGetConsoleProcessListPacket(ReadBuffer &packet)
+{
+ packet.assertEof();
+
+ auto processList = std::vector<DWORD>(64);
+ auto processCount = GetConsoleProcessList(&processList[0], processList.size());
+
+ // The process list can change while we're trying to read it
+ while (processList.size() < processCount) {
+ // Multiplying by two caps the number of iterations
+ const auto newSize = std::max<DWORD>(processList.size() * 2, processCount);
+ processList.resize(newSize);
+ processCount = GetConsoleProcessList(&processList[0], processList.size());
+ }
+
+ if (processCount == 0) {
+ trace("GetConsoleProcessList failed");
+ }
+
+ auto reply = newPacket();
+ reply.putInt32(processCount);
+ for (DWORD i = 0; i < processCount; i++) {
+ reply.putInt32(processList[i]);
+ }
+ writePacket(reply);
+}
+
+void Agent::pollConinPipe()
+{
+ const std::string newData = m_coninPipe->readAllToString();
+ if (hasDebugFlag("input_separated_bytes")) {
+ // This debug flag is intended to help with testing incomplete escape
+ // sequences and multibyte UTF-8 encodings. (I wonder if the normal
+ // code path ought to advance a state machine one byte at a time.)
+ for (size_t i = 0; i < newData.size(); ++i) {
+ m_consoleInput->writeInput(newData.substr(i, 1));
+ }
+ } else {
+ m_consoleInput->writeInput(newData);
+ }
+}
+
+void Agent::onPollTimeout()
+{
+ m_consoleInput->updateInputFlags();
+ const bool enableMouseMode = m_consoleInput->shouldActivateTerminalMouse();
+
+ // Give the ConsoleInput object a chance to flush input from an incomplete
+ // escape sequence (e.g. pressing ESC).
+ m_consoleInput->flushIncompleteEscapeCode();
+
+ const bool shouldScrapeContent = !m_closingOutputPipes;
+
+ // Check if the child process has exited.
+ if (m_autoShutdown &&
+ m_childProcess != nullptr &&
+ WaitForSingleObject(m_childProcess, 0) == WAIT_OBJECT_0) {
+ CloseHandle(m_childProcess);
+ m_childProcess = nullptr;
+
+ // Close the data socket to signal to the client that the child
+ // process has exited. If there's any data left to send, send it
+ // before closing the socket.
+ m_closingOutputPipes = true;
+ }
+
+ // Scrape for output *after* the above exit-check to ensure that we collect
+ // the child process's final output.
+ if (shouldScrapeContent) {
+ syncConsoleTitle();
+ scrapeBuffers();
+ }
+
+ // We must ensure that we disable mouse mode before closing the CONOUT
+ // pipe, so update the mouse mode here.
+ m_primaryScraper->terminal().enableMouseMode(
+ enableMouseMode && !m_closingOutputPipes);
+
+ autoClosePipesForShutdown();
+}
+
+void Agent::autoClosePipesForShutdown()
+{
+ if (m_closingOutputPipes) {
+ // We don't want to close a pipe before it's connected! If we do, the
+ // libwinpty client may try to connect to a non-existent pipe. This
+ // case is important for short-lived programs.
+ if (m_conoutPipe->isConnected() &&
+ m_conoutPipe->bytesToSend() == 0) {
+ trace("Closing CONOUT pipe (auto-shutdown)");
+ m_conoutPipe->closePipe();
+ }
+ if (m_conerrPipe != nullptr &&
+ m_conerrPipe->isConnected() &&
+ m_conerrPipe->bytesToSend() == 0) {
+ trace("Closing CONERR pipe (auto-shutdown)");
+ m_conerrPipe->closePipe();
+ }
+ if (m_exitAfterShutdown &&
+ m_conoutPipe->isClosed() &&
+ (m_conerrPipe == nullptr || m_conerrPipe->isClosed())) {
+ trace("Agent exiting (exit-after-shutdown)");
+ shutdown();
+ }
+ }
+}
+
+std::unique_ptr<Win32ConsoleBuffer> Agent::openPrimaryBuffer()
+{
+ // If we're using a separate buffer for stderr, and a program were to
+ // activate the stderr buffer, then we could accidentally scrape the same
+ // buffer twice. That probably shouldn't happen in ordinary use, but it
+ // can be avoided anyway by using the original console screen buffer in
+ // that mode.
+ if (!m_useConerr) {
+ return Win32ConsoleBuffer::openConout();
+ } else {
+ return Win32ConsoleBuffer::openStdout();
+ }
+}
+
+void Agent::resizeWindow(int cols, int rows)
+{
+ ASSERT(cols >= 1 && rows >= 1);
+ cols = std::min(cols, MAX_CONSOLE_WIDTH);
+ rows = std::min(rows, MAX_CONSOLE_HEIGHT);
+
+ Win32Console::FreezeGuard guard(m_console, m_console.frozen());
+ const Coord newSize(cols, rows);
+ ConsoleScreenBufferInfo info;
+ auto primaryBuffer = openPrimaryBuffer();
+ m_primaryScraper->resizeWindow(*primaryBuffer, newSize, info);
+ m_consoleInput->setMouseWindowRect(info.windowRect());
+ if (m_errorScraper) {
+ m_errorScraper->resizeWindow(*m_errorBuffer, newSize, info);
+ }
+
+ // Synthesize a WINDOW_BUFFER_SIZE_EVENT event. Normally, Windows
+ // generates this event only when the buffer size changes, not when the
+ // window size changes. This behavior is undesirable in two ways:
+ // - When winpty expands the window horizontally, it must expand the
+ // buffer first, then the window. At least some programs (e.g. the WSL
+ // bash.exe wrapper) use the window width rather than the buffer width,
+ // so there is a short timespan during which they can read the wrong
+ // value.
+ // - If the window's vertical size is changed, no event is generated,
+ // even though a typical well-behaved console program cares about the
+ // *window* height, not the *buffer* height.
+ // This synthesization works around a design flaw in the console. It's probably
+ // harmless. See https://github.com/rprichard/winpty/issues/110.
+ INPUT_RECORD sizeEvent {};
+ sizeEvent.EventType = WINDOW_BUFFER_SIZE_EVENT;
+ sizeEvent.Event.WindowBufferSizeEvent.dwSize = primaryBuffer->bufferSize();
+ DWORD actual {};
+ WriteConsoleInputW(GetStdHandle(STD_INPUT_HANDLE), &sizeEvent, 1, &actual);
+}
+
+void Agent::scrapeBuffers()
+{
+ Win32Console::FreezeGuard guard(m_console, m_console.frozen());
+ ConsoleScreenBufferInfo info;
+ m_primaryScraper->scrapeBuffer(*openPrimaryBuffer(), info);
+ m_consoleInput->setMouseWindowRect(info.windowRect());
+ if (m_errorScraper) {
+ m_errorScraper->scrapeBuffer(*m_errorBuffer, info);
+ }
+}
+
+void Agent::syncConsoleTitle()
+{
+ std::wstring newTitle = m_console.title();
+ if (newTitle != m_currentTitle) {
+ if (!m_plainMode && !m_conoutPipe->isClosed()) {
+ std::string command = std::string("\x1b]0;") +
+ utf8FromWide(newTitle) + "\x07";
+ m_conoutPipe->write(command.c_str());
+ }
+ m_currentTitle = newTitle;
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Agent.h b/src/libs/3rdparty/winpty/src/agent/Agent.h
new file mode 100644
index 0000000000..1dde48fe4a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Agent.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_H
+#define AGENT_H
+
+#include <windows.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "DsrSender.h"
+#include "EventLoop.h"
+#include "Win32Console.h"
+
+class ConsoleInput;
+class NamedPipe;
+class ReadBuffer;
+class Scraper;
+class WriteBuffer;
+class Win32ConsoleBuffer;
+
+class Agent : public EventLoop, public DsrSender
+{
+public:
+ Agent(LPCWSTR controlPipeName,
+ uint64_t agentFlags,
+ int mouseMode,
+ int initialCols,
+ int initialRows);
+ virtual ~Agent();
+ void sendDsr() override;
+
+private:
+ NamedPipe &connectToControlPipe(LPCWSTR pipeName);
+ NamedPipe &createDataServerPipe(bool write, const wchar_t *kind);
+
+private:
+ void pollControlPipe();
+ void handlePacket(ReadBuffer &packet);
+ void writePacket(WriteBuffer &packet);
+ void handleStartProcessPacket(ReadBuffer &packet);
+ void handleSetSizePacket(ReadBuffer &packet);
+ void handleGetConsoleProcessListPacket(ReadBuffer &packet);
+ void pollConinPipe();
+
+protected:
+ virtual void onPollTimeout() override;
+ virtual void onPipeIo(NamedPipe &namedPipe) override;
+
+private:
+ void autoClosePipesForShutdown();
+ std::unique_ptr<Win32ConsoleBuffer> openPrimaryBuffer();
+ void resizeWindow(int cols, int rows);
+ void scrapeBuffers();
+ void syncConsoleTitle();
+
+private:
+ const bool m_useConerr;
+ const bool m_plainMode;
+ const int m_mouseMode;
+ Win32Console m_console;
+ std::unique_ptr<Scraper> m_primaryScraper;
+ std::unique_ptr<Scraper> m_errorScraper;
+ std::unique_ptr<Win32ConsoleBuffer> m_errorBuffer;
+ NamedPipe *m_controlPipe = nullptr;
+ NamedPipe *m_coninPipe = nullptr;
+ NamedPipe *m_conoutPipe = nullptr;
+ NamedPipe *m_conerrPipe = nullptr;
+ bool m_autoShutdown = false;
+ bool m_exitAfterShutdown = false;
+ bool m_closingOutputPipes = false;
+ std::unique_ptr<ConsoleInput> m_consoleInput;
+ HANDLE m_childProcess = nullptr;
+
+ // If the title is initialized to the empty string, then cmd.exe will
+ // sometimes print this error:
+ // Not enough storage is available to process this command.
+ // It happens on Windows 7 when logged into a Cygwin SSH session, for
+ // example. Using a title of a single space character avoids the problem.
+ // See https://github.com/rprichard/winpty/issues/74.
+ std::wstring m_currentTitle = L" ";
+};
+
+#endif // AGENT_H
diff --git a/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.cc b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.cc
new file mode 100644
index 0000000000..9ad6503b1c
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "AgentCreateDesktop.h"
+
+#include "../shared/BackgroundDesktop.h"
+#include "../shared/Buffer.h"
+#include "../shared/DebugClient.h"
+#include "../shared/StringUtil.h"
+
+#include "EventLoop.h"
+#include "NamedPipe.h"
+
+namespace {
+
+static inline WriteBuffer newPacket() {
+ WriteBuffer packet;
+ packet.putRawValue<uint64_t>(0); // Reserve space for size.
+ return packet;
+}
+
+class CreateDesktopLoop : public EventLoop {
+public:
+ CreateDesktopLoop(LPCWSTR controlPipeName);
+
+protected:
+ virtual void onPipeIo(NamedPipe &namedPipe) override;
+
+private:
+ void writePacket(WriteBuffer &packet);
+
+ BackgroundDesktop m_desktop;
+ NamedPipe &m_pipe;
+};
+
+CreateDesktopLoop::CreateDesktopLoop(LPCWSTR controlPipeName) :
+ m_pipe(createNamedPipe()) {
+ m_pipe.connectToServer(controlPipeName, NamedPipe::OpenMode::Duplex);
+ auto packet = newPacket();
+ packet.putWString(m_desktop.desktopName());
+ writePacket(packet);
+}
+
+void CreateDesktopLoop::writePacket(WriteBuffer &packet) {
+ const auto &bytes = packet.buf();
+ packet.replaceRawValue<uint64_t>(0, bytes.size());
+ m_pipe.write(bytes.data(), bytes.size());
+}
+
+void CreateDesktopLoop::onPipeIo(NamedPipe &namedPipe) {
+ if (m_pipe.isClosed()) {
+ shutdown();
+ }
+}
+
+} // anonymous namespace
+
+void handleCreateDesktop(LPCWSTR controlPipeName) {
+ try {
+ CreateDesktopLoop loop(controlPipeName);
+ loop.run();
+ trace("Agent exiting...");
+ } catch (const WinptyException &e) {
+ trace("handleCreateDesktop: internal error: %s",
+ utf8FromWide(e.what()).c_str());
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.h b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.h
new file mode 100644
index 0000000000..2ae539c7fa
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/AgentCreateDesktop.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_CREATE_DESKTOP_H
+#define AGENT_CREATE_DESKTOP_H
+
+#include <windows.h>
+
+void handleCreateDesktop(LPCWSTR controlPipeName);
+
+#endif // AGENT_CREATE_DESKTOP_H
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleFont.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.cc
new file mode 100644
index 0000000000..aa1f7876d3
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.cc
@@ -0,0 +1,698 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "ConsoleFont.h"
+
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <algorithm>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "../shared/DebugClient.h"
+#include "../shared/OsModule.h"
+#include "../shared/StringUtil.h"
+#include "../shared/WindowsVersion.h"
+#include "../shared/WinptyAssert.h"
+#include "../shared/winpty_snprintf.h"
+
+namespace {
+
+#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
+
+// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
+const wchar_t kLucidaConsole[] = L"Lucida Console";
+const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // 932, Japanese
+const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // 936, Chinese Simplified
+const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // 949, Korean
+const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // 950, Chinese Traditional
+
+struct FontSize {
+ short size;
+ int width;
+};
+
+struct Font {
+ const wchar_t *faceName;
+ unsigned int family;
+ short size;
+};
+
+// Ideographs in East Asian languages take two columns rather than one.
+// In the console screen buffer, a "full-width" character will occupy two
+// cells of the buffer, the first with attribute 0x100 and the second with
+// attribute 0x200.
+//
+// Windows does not correctly identify code points as double-width in all
+// configurations. It depends heavily on the code page, the font facename,
+// and (somehow) even the font size. In the 437 code page (MS-DOS), for
+// example, no codepoints are interpreted as double-width. When the console
+// is in an East Asian code page (932, 936, 949, or 950), then sometimes
+// selecting a "Western" facename like "Lucida Console" or "Consolas" doesn't
+// register, or if the font *can* be chosen, then the console doesn't handle
+// double-width correctly. I tested the double-width handling by writing
+// several code points with WriteConsole and checking whether one or two cells
+// were filled.
+//
+// In the Japanese code page (932), Microsoft's default font is MS Gothic.
+// MS Gothic double-width handling seems to be broken with console versions
+// prior to Windows 10 (including Windows 10's legacy mode), and it's
+// especially broken in Windows 8 and 8.1.
+//
+// Test by running: misc/Utf16Echo A2 A3 2014 3044 30FC 4000
+//
+// The first three codepoints are always rendered as half-width with the
+// Windows Japanese fonts. (Of these, the first two must be half-width,
+// but U+2014 could be either.) The last three are rendered as full-width,
+// and they are East_Asian_Width=Wide.
+//
+// Windows 7 fails by modeling all codepoints as full-width with font
+// sizes 22 and above.
+//
+// Windows 8 gets U+00A2, U+00A3, U+2014, U+30FC, and U+4000 wrong, but
+// using a point size not listed in the console properties dialog
+// (e.g. "9") is less wrong:
+//
+// | code point |
+// font | 00A2 00A3 2014 3044 30FC 4000 | cell size
+// ------------+---------------------------------+----------
+// 8 | F F F F H H | 4x8
+// 9 | F F F F F F | 5x9
+// 16 | F F F F H H | 8x16
+// raster 6x13 | H H H F F H(*) | 6x13
+//
+// (*) The Raster Font renders U+4000 as a white box (i.e. an unsupported
+// character).
+//
+
+// See:
+// - misc/Font-Report-June2016 directory for per-size details
+// - misc/font-notes.txt
+// - misc/Utf16Echo.cc, misc/FontSurvey.cc, misc/SetFont.cc, misc/GetFont.cc
+
+const FontSize kLucidaFontSizes[] = {
+ { 5, 3 },
+ { 6, 4 },
+ { 8, 5 },
+ { 10, 6 },
+ { 12, 7 },
+ { 14, 8 },
+ { 16, 10 },
+ { 18, 11 },
+ { 20, 12 },
+ { 36, 22 },
+ { 48, 29 },
+ { 60, 36 },
+ { 72, 43 },
+};
+
+// Japanese. Used on Vista and Windows 7.
+const FontSize k932GothicVista[] = {
+ { 6, 3 },
+ { 8, 4 },
+ { 10, 5 },
+ { 12, 6 },
+ { 13, 7 },
+ { 15, 8 },
+ { 17, 9 },
+ { 19, 10 },
+ { 21, 11 },
+ // All larger fonts are more broken w.r.t. full-size East Asian characters.
+};
+
+// Japanese. Used on Windows 8, 8.1, and the legacy 10 console.
+const FontSize k932GothicWin8[] = {
+ // All of these characters are broken w.r.t. full-size East Asian
+ // characters, but they're equally broken.
+ { 5, 3 },
+ { 7, 4 },
+ { 9, 5 },
+ { 11, 6 },
+ { 13, 7 },
+ { 15, 8 },
+ { 17, 9 },
+ { 20, 10 },
+ { 22, 11 },
+ { 24, 12 },
+ // include extra-large fonts for small terminals
+ { 36, 18 },
+ { 48, 24 },
+ { 60, 30 },
+ { 72, 36 },
+};
+
+// Japanese. Used on the new Windows 10 console.
+const FontSize k932GothicWin10[] = {
+ { 6, 3 },
+ { 8, 4 },
+ { 10, 5 },
+ { 12, 6 },
+ { 14, 7 },
+ { 16, 8 },
+ { 18, 9 },
+ { 20, 10 },
+ { 22, 11 },
+ { 24, 12 },
+ // include extra-large fonts for small terminals
+ { 36, 18 },
+ { 48, 24 },
+ { 60, 30 },
+ { 72, 36 },
+};
+
+// Chinese Simplified.
+const FontSize k936SimSun[] = {
+ { 6, 3 },
+ { 8, 4 },
+ { 10, 5 },
+ { 12, 6 },
+ { 14, 7 },
+ { 16, 8 },
+ { 18, 9 },
+ { 20, 10 },
+ { 22, 11 },
+ { 24, 12 },
+ // include extra-large fonts for small terminals
+ { 36, 18 },
+ { 48, 24 },
+ { 60, 30 },
+ { 72, 36 },
+};
+
+// Korean.
+const FontSize k949GulimChe[] = {
+ { 6, 3 },
+ { 8, 4 },
+ { 10, 5 },
+ { 12, 6 },
+ { 14, 7 },
+ { 16, 8 },
+ { 18, 9 },
+ { 20, 10 },
+ { 22, 11 },
+ { 24, 12 },
+ // include extra-large fonts for small terminals
+ { 36, 18 },
+ { 48, 24 },
+ { 60, 30 },
+ { 72, 36 },
+};
+
+// Chinese Traditional.
+const FontSize k950MingLight[] = {
+ { 6, 3 },
+ { 8, 4 },
+ { 10, 5 },
+ { 12, 6 },
+ { 14, 7 },
+ { 16, 8 },
+ { 18, 9 },
+ { 20, 10 },
+ { 22, 11 },
+ { 24, 12 },
+ // include extra-large fonts for small terminals
+ { 36, 18 },
+ { 48, 24 },
+ { 60, 30 },
+ { 72, 36 },
+};
+
+// Some of these types and functions are missing from the MinGW headers.
+// Others are undocumented.
+
+struct AGENT_CONSOLE_FONT_INFO {
+ DWORD nFont;
+ COORD dwFontSize;
+};
+
+struct AGENT_CONSOLE_FONT_INFOEX {
+ ULONG cbSize;
+ DWORD nFont;
+ COORD dwFontSize;
+ UINT FontFamily;
+ UINT FontWeight;
+ WCHAR FaceName[LF_FACESIZE];
+};
+
+// undocumented XP API
+typedef BOOL WINAPI SetConsoleFont_t(
+ HANDLE hOutput,
+ DWORD dwFontIndex);
+
+// undocumented XP API
+typedef DWORD WINAPI GetNumberOfConsoleFonts_t();
+
+// XP and up
+typedef BOOL WINAPI GetCurrentConsoleFont_t(
+ HANDLE hOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFO *lpConsoleCurrentFont);
+
+// XP and up
+typedef COORD WINAPI GetConsoleFontSize_t(
+ HANDLE hConsoleOutput,
+ DWORD nFont);
+
+// Vista and up
+typedef BOOL WINAPI GetCurrentConsoleFontEx_t(
+ HANDLE hConsoleOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
+
+// Vista and up
+typedef BOOL WINAPI SetCurrentConsoleFontEx_t(
+ HANDLE hConsoleOutput,
+ BOOL bMaximumWindow,
+ AGENT_CONSOLE_FONT_INFOEX *lpConsoleCurrentFontEx);
+
+#define GET_MODULE_PROC(mod, funcName) \
+ m_##funcName = reinterpret_cast<funcName##_t*>((mod).proc(#funcName)); \
+
+#define DEFINE_ACCESSOR(funcName) \
+ funcName##_t &funcName() const { \
+ ASSERT(valid()); \
+ return *m_##funcName; \
+ }
+
+class XPFontAPI {
+public:
+ XPFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFont);
+ GET_MODULE_PROC(m_kernel32, GetConsoleFontSize);
+ }
+
+ bool valid() const {
+ return m_GetCurrentConsoleFont != NULL &&
+ m_GetConsoleFontSize != NULL;
+ }
+
+ DEFINE_ACCESSOR(GetCurrentConsoleFont)
+ DEFINE_ACCESSOR(GetConsoleFontSize)
+
+private:
+ OsModule m_kernel32;
+ GetCurrentConsoleFont_t *m_GetCurrentConsoleFont;
+ GetConsoleFontSize_t *m_GetConsoleFontSize;
+};
+
+class UndocumentedXPFontAPI : public XPFontAPI {
+public:
+ UndocumentedXPFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, SetConsoleFont);
+ GET_MODULE_PROC(m_kernel32, GetNumberOfConsoleFonts);
+ }
+
+ bool valid() const {
+ return this->XPFontAPI::valid() &&
+ m_SetConsoleFont != NULL &&
+ m_GetNumberOfConsoleFonts != NULL;
+ }
+
+ DEFINE_ACCESSOR(SetConsoleFont)
+ DEFINE_ACCESSOR(GetNumberOfConsoleFonts)
+
+private:
+ OsModule m_kernel32;
+ SetConsoleFont_t *m_SetConsoleFont;
+ GetNumberOfConsoleFonts_t *m_GetNumberOfConsoleFonts;
+};
+
+class VistaFontAPI : public XPFontAPI {
+public:
+ VistaFontAPI() : m_kernel32(L"kernel32.dll") {
+ GET_MODULE_PROC(m_kernel32, GetCurrentConsoleFontEx);
+ GET_MODULE_PROC(m_kernel32, SetCurrentConsoleFontEx);
+ }
+
+ bool valid() const {
+ return this->XPFontAPI::valid() &&
+ m_GetCurrentConsoleFontEx != NULL &&
+ m_SetCurrentConsoleFontEx != NULL;
+ }
+
+ DEFINE_ACCESSOR(GetCurrentConsoleFontEx)
+ DEFINE_ACCESSOR(SetCurrentConsoleFontEx)
+
+private:
+ OsModule m_kernel32;
+ GetCurrentConsoleFontEx_t *m_GetCurrentConsoleFontEx;
+ SetCurrentConsoleFontEx_t *m_SetCurrentConsoleFontEx;
+};
+
+static std::vector<std::pair<DWORD, COORD> > readFontTable(
+ XPFontAPI &api, HANDLE conout, DWORD maxCount) {
+ std::vector<std::pair<DWORD, COORD> > ret;
+ for (DWORD i = 0; i < maxCount; ++i) {
+ COORD size = api.GetConsoleFontSize()(conout, i);
+ if (size.X == 0 && size.Y == 0) {
+ break;
+ }
+ ret.push_back(std::make_pair(i, size));
+ }
+ return ret;
+}
+
+static void dumpFontTable(HANDLE conout, const char *prefix) {
+ const int kMaxCount = 1000;
+ if (!isTracingEnabled()) {
+ return;
+ }
+ XPFontAPI api;
+ if (!api.valid()) {
+ trace("dumpFontTable: cannot dump font table -- missing APIs");
+ return;
+ }
+ std::vector<std::pair<DWORD, COORD> > table =
+ readFontTable(api, conout, kMaxCount);
+ std::string line;
+ char tmp[128];
+ size_t first = 0;
+ while (first < table.size()) {
+ size_t last = std::min(table.size() - 1, first + 10 - 1);
+ winpty_snprintf(tmp, "%sfonts %02u-%02u:",
+ prefix, static_cast<unsigned>(first), static_cast<unsigned>(last));
+ line = tmp;
+ for (size_t i = first; i <= last; ++i) {
+ if (i % 10 == 5) {
+ line += " - ";
+ }
+ winpty_snprintf(tmp, " %2dx%-2d",
+ table[i].second.X, table[i].second.Y);
+ line += tmp;
+ }
+ trace("%s", line.c_str());
+ first = last + 1;
+ }
+ if (table.size() == kMaxCount) {
+ trace("%sfonts: ... stopped reading at %d fonts ...",
+ prefix, kMaxCount);
+ }
+}
+
+static std::string stringToCodePoints(const std::wstring &str) {
+ std::string ret = "(";
+ for (size_t i = 0; i < str.size(); ++i) {
+ char tmp[32];
+ winpty_snprintf(tmp, "%X", str[i]);
+ if (ret.size() > 1) {
+ ret.push_back(' ');
+ }
+ ret += tmp;
+ }
+ ret.push_back(')');
+ return ret;
+}
+
+static void dumpFontInfoEx(
+ const AGENT_CONSOLE_FONT_INFOEX &infoex,
+ const char *prefix) {
+ if (!isTracingEnabled()) {
+ return;
+ }
+ std::wstring faceName(infoex.FaceName,
+ winpty_wcsnlen(infoex.FaceName, COUNT_OF(infoex.FaceName)));
+ trace("%snFont=%u dwFontSize=(%d,%d) "
+ "FontFamily=0x%x FontWeight=%u FaceName=%s %s",
+ prefix,
+ static_cast<unsigned>(infoex.nFont),
+ infoex.dwFontSize.X, infoex.dwFontSize.Y,
+ infoex.FontFamily, infoex.FontWeight, utf8FromWide(faceName).c_str(),
+ stringToCodePoints(faceName).c_str());
+}
+
+static void dumpVistaFont(VistaFontAPI &api, HANDLE conout, const char *prefix) {
+ if (!isTracingEnabled()) {
+ return;
+ }
+ AGENT_CONSOLE_FONT_INFOEX infoex = {0};
+ infoex.cbSize = sizeof(infoex);
+ if (!api.GetCurrentConsoleFontEx()(conout, FALSE, &infoex)) {
+ trace("GetCurrentConsoleFontEx call failed");
+ return;
+ }
+ dumpFontInfoEx(infoex, prefix);
+}
+
+static void dumpXPFont(XPFontAPI &api, HANDLE conout, const char *prefix) {
+ if (!isTracingEnabled()) {
+ return;
+ }
+ AGENT_CONSOLE_FONT_INFO info = {0};
+ if (!api.GetCurrentConsoleFont()(conout, FALSE, &info)) {
+ trace("GetCurrentConsoleFont call failed");
+ return;
+ }
+ trace("%snFont=%u dwFontSize=(%d,%d)",
+ prefix,
+ static_cast<unsigned>(info.nFont),
+ info.dwFontSize.X, info.dwFontSize.Y);
+}
+
+static bool setFontVista(
+ VistaFontAPI &api,
+ HANDLE conout,
+ const Font &font) {
+ AGENT_CONSOLE_FONT_INFOEX infoex = {};
+ infoex.cbSize = sizeof(AGENT_CONSOLE_FONT_INFOEX);
+ infoex.dwFontSize.Y = font.size;
+ infoex.FontFamily = font.family;
+ infoex.FontWeight = 400;
+ winpty_wcsncpy_nul(infoex.FaceName, font.faceName);
+ dumpFontInfoEx(infoex, "setFontVista: setting font to: ");
+ if (!api.SetCurrentConsoleFontEx()(conout, FALSE, &infoex)) {
+ trace("setFontVista: SetCurrentConsoleFontEx call failed");
+ return false;
+ }
+ memset(&infoex, 0, sizeof(infoex));
+ infoex.cbSize = sizeof(infoex);
+ if (!api.GetCurrentConsoleFontEx()(conout, FALSE, &infoex)) {
+ trace("setFontVista: GetCurrentConsoleFontEx call failed");
+ return false;
+ }
+ if (wcsncmp(infoex.FaceName, font.faceName,
+ COUNT_OF(infoex.FaceName)) != 0) {
+ trace("setFontVista: face name was not set");
+ dumpFontInfoEx(infoex, "setFontVista: post-call font: ");
+ return false;
+ }
+ // We'd like to verify that the new font size is correct, but we can't
+ // predict what it will be, even though we just set it to `pxSize` through
+ // an apprently symmetric interface. For the Chinese and Korean fonts, the
+ // new `infoex.dwFontSize.Y` value can be slightly larger than the height
+ // we specified.
+ return true;
+}
+
+static Font selectSmallFont(int codePage, int columns, bool isNewW10) {
+ // Iterate over a set of font sizes according to the code page, and select
+ // one.
+
+ const wchar_t *faceName = nullptr;
+ unsigned int fontFamily = 0;
+ const FontSize *table = nullptr;
+ size_t tableSize = 0;
+
+ switch (codePage) {
+ case 932: // Japanese
+ faceName = kMSGothic;
+ fontFamily = 0x36;
+ if (isNewW10) {
+ table = k932GothicWin10;
+ tableSize = COUNT_OF(k932GothicWin10);
+ } else if (isAtLeastWindows8()) {
+ table = k932GothicWin8;
+ tableSize = COUNT_OF(k932GothicWin8);
+ } else {
+ table = k932GothicVista;
+ tableSize = COUNT_OF(k932GothicVista);
+ }
+ break;
+ case 936: // Chinese Simplified
+ faceName = kNSimSun;
+ fontFamily = 0x36;
+ table = k936SimSun;
+ tableSize = COUNT_OF(k936SimSun);
+ break;
+ case 949: // Korean
+ faceName = kGulimChe;
+ fontFamily = 0x36;
+ table = k949GulimChe;
+ tableSize = COUNT_OF(k949GulimChe);
+ break;
+ case 950: // Chinese Traditional
+ faceName = kMingLight;
+ fontFamily = 0x36;
+ table = k950MingLight;
+ tableSize = COUNT_OF(k950MingLight);
+ break;
+ default:
+ faceName = kLucidaConsole;
+ fontFamily = 0x36;
+ table = kLucidaFontSizes;
+ tableSize = COUNT_OF(kLucidaFontSizes);
+ break;
+ }
+
+ size_t bestIndex = static_cast<size_t>(-1);
+ std::tuple<int, int> bestScore = std::make_tuple(-1, -1);
+
+ // We might want to pick the smallest possible font, because we don't know
+ // how large the monitor is (and the monitor size can change). We might
+ // want to pick a larger font to accommodate console programs that resize
+ // the console on their own, like DOS edit.com, which tends to resize the
+ // console to 80 columns.
+
+ for (size_t i = 0; i < tableSize; ++i) {
+ const int width = table[i].width * columns;
+
+ // In general, we'd like to pick a font size where cutting the number
+ // of columns in half doesn't immediately violate the minimum width
+ // constraint. (e.g. To run DOS edit.com, a user might resize their
+ // terminal to ~100 columns so it's big enough to show the 80 columns
+ // post-resize.) To achieve this, give priority to fonts that allow
+ // this halving. We don't want to encourage *very* large fonts,
+ // though, so disable the effect as the number of columns scales from
+ // 80 to 40.
+ const int halfColumns = std::min(columns, std::max(40, columns / 2));
+ const int halfWidth = table[i].width * halfColumns;
+
+ std::tuple<int, int> thisScore = std::make_tuple(-1, -1);
+ if (width >= 160 && halfWidth >= 160) {
+ // Both sizes are good. Prefer the smaller fonts.
+ thisScore = std::make_tuple(2, -width);
+ } else if (width >= 160) {
+ // Prefer the smaller fonts.
+ thisScore = std::make_tuple(1, -width);
+ } else {
+ // Otherwise, prefer the largest font in our table.
+ thisScore = std::make_tuple(0, width);
+ }
+ if (thisScore > bestScore) {
+ bestIndex = i;
+ bestScore = thisScore;
+ }
+ }
+
+ ASSERT(bestIndex != static_cast<size_t>(-1));
+ return Font { faceName, fontFamily, table[bestIndex].size };
+}
+
+static void setSmallFontVista(VistaFontAPI &api, HANDLE conout,
+ int columns, bool isNewW10) {
+ int codePage = GetConsoleOutputCP();
+ const auto font = selectSmallFont(codePage, columns, isNewW10);
+ if (setFontVista(api, conout, font)) {
+ trace("setSmallFontVista: success");
+ return;
+ }
+ if (codePage == 932 || codePage == 936 ||
+ codePage == 949 || codePage == 950) {
+ trace("setSmallFontVista: falling back to default codepage font instead");
+ const auto fontFB = selectSmallFont(0, columns, isNewW10);
+ if (setFontVista(api, conout, fontFB)) {
+ trace("setSmallFontVista: fallback was successful");
+ return;
+ }
+ }
+ trace("setSmallFontVista: failure");
+}
+
+struct FontSizeComparator {
+ bool operator()(const std::pair<DWORD, COORD> &obj1,
+ const std::pair<DWORD, COORD> &obj2) const {
+ int score1 = obj1.second.X + obj1.second.Y;
+ int score2 = obj2.second.X + obj2.second.Y;
+ return score1 < score2;
+ }
+};
+
+static void setSmallFontXP(UndocumentedXPFontAPI &api, HANDLE conout) {
+ // Read the console font table and sort it from smallest to largest.
+ const DWORD fontCount = api.GetNumberOfConsoleFonts()();
+ trace("setSmallFontXP: number of console fonts: %u",
+ static_cast<unsigned>(fontCount));
+ std::vector<std::pair<DWORD, COORD> > table =
+ readFontTable(api, conout, fontCount);
+ std::sort(table.begin(), table.end(), FontSizeComparator());
+ for (size_t i = 0; i < table.size(); ++i) {
+ // Skip especially narrow fonts to permit narrower terminals.
+ if (table[i].second.X < 4) {
+ continue;
+ }
+ trace("setSmallFontXP: setting font to %u",
+ static_cast<unsigned>(table[i].first));
+ if (!api.SetConsoleFont()(conout, table[i].first)) {
+ trace("setSmallFontXP: SetConsoleFont call failed");
+ continue;
+ }
+ AGENT_CONSOLE_FONT_INFO info;
+ if (!api.GetCurrentConsoleFont()(conout, FALSE, &info)) {
+ trace("setSmallFontXP: GetCurrentConsoleFont call failed");
+ return;
+ }
+ if (info.nFont != table[i].first) {
+ trace("setSmallFontXP: font was not set");
+ dumpXPFont(api, conout, "setSmallFontXP: post-call font: ");
+ continue;
+ }
+ trace("setSmallFontXP: success");
+ return;
+ }
+ trace("setSmallFontXP: failure");
+}
+
+} // anonymous namespace
+
+// A Windows console window can never be larger than the desktop window. To
+// maximize the possible size of the console in rows*cols, try to configure
+// the console with a small font. Unfortunately, we cannot make the font *too*
+// small, because there is also a minimum window size in pixels.
+void setSmallFont(HANDLE conout, int columns, bool isNewW10) {
+ trace("setSmallFont: attempting to set a small font for %d columns "
+ "(CP=%u OutputCP=%u)",
+ columns,
+ static_cast<unsigned>(GetConsoleCP()),
+ static_cast<unsigned>(GetConsoleOutputCP()));
+ VistaFontAPI vista;
+ if (vista.valid()) {
+ dumpVistaFont(vista, conout, "previous font: ");
+ dumpFontTable(conout, "previous font table: ");
+ setSmallFontVista(vista, conout, columns, isNewW10);
+ dumpVistaFont(vista, conout, "new font: ");
+ dumpFontTable(conout, "new font table: ");
+ return;
+ }
+ UndocumentedXPFontAPI xp;
+ if (xp.valid()) {
+ dumpXPFont(xp, conout, "previous font: ");
+ dumpFontTable(conout, "previous font table: ");
+ setSmallFontXP(xp, conout);
+ dumpXPFont(xp, conout, "new font: ");
+ dumpFontTable(conout, "new font table: ");
+ return;
+ }
+ trace("setSmallFont: neither Vista nor XP APIs detected -- giving up");
+ dumpFontTable(conout, "font table: ");
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleFont.h b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.h
new file mode 100644
index 0000000000..99cb10698d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleFont.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef CONSOLEFONT_H
+#define CONSOLEFONT_H
+
+#include <windows.h>
+
+void setSmallFont(HANDLE conout, int columns, bool isNewW10);
+
+#endif // CONSOLEFONT_H
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInput.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.cc
new file mode 100644
index 0000000000..192cac2a29
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.cc
@@ -0,0 +1,852 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "ConsoleInput.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <algorithm>
+#include <string>
+
+#include "../include/winpty_constants.h"
+
+#include "../shared/DebugClient.h"
+#include "../shared/StringBuilder.h"
+#include "../shared/UnixCtrlChars.h"
+
+#include "ConsoleInputReencoding.h"
+#include "DebugShowInput.h"
+#include "DefaultInputMap.h"
+#include "DsrSender.h"
+#include "UnicodeEncoding.h"
+#include "Win32Console.h"
+
+// MAPVK_VK_TO_VSC isn't defined by the old MinGW.
+#ifndef MAPVK_VK_TO_VSC
+#define MAPVK_VK_TO_VSC 0
+#endif
+
+namespace {
+
+struct MouseRecord {
+ bool release;
+ int flags;
+ COORD coord;
+
+ std::string toString() const;
+};
+
+std::string MouseRecord::toString() const {
+ StringBuilder sb(40);
+ sb << "pos=" << coord.X << ',' << coord.Y
+ << " flags=0x" << hexOfInt(flags);
+ if (release) {
+ sb << " release";
+ }
+ return sb.str_moved();
+}
+
+const unsigned int kIncompleteEscapeTimeoutMs = 1000u;
+
+#define CHECK(cond) \
+ do { \
+ if (!(cond)) { return 0; } \
+ } while(0)
+
+#define ADVANCE() \
+ do { \
+ pch++; \
+ if (pch == stop) { return -1; } \
+ } while(0)
+
+#define SCAN_INT(out, maxLen) \
+ do { \
+ (out) = 0; \
+ CHECK(isdigit(*pch)); \
+ const char *begin = pch; \
+ do { \
+ CHECK(pch - begin + 1 < maxLen); \
+ (out) = (out) * 10 + *pch - '0'; \
+ ADVANCE(); \
+ } while (isdigit(*pch)); \
+ } while(0)
+
+#define SCAN_SIGNED_INT(out, maxLen) \
+ do { \
+ bool negative = false; \
+ if (*pch == '-') { \
+ negative = true; \
+ ADVANCE(); \
+ } \
+ SCAN_INT(out, maxLen); \
+ if (negative) { \
+ (out) = -(out); \
+ } \
+ } while(0)
+
+// Match the Device Status Report console input: ESC [ nn ; mm R
+// Returns:
+// 0 no match
+// >0 match, returns length of match
+// -1 incomplete match
+static int matchDsr(const char *input, int inputSize)
+{
+ int32_t dummy = 0;
+ const char *pch = input;
+ const char *stop = input + inputSize;
+ CHECK(*pch == '\x1B'); ADVANCE();
+ CHECK(*pch == '['); ADVANCE();
+ SCAN_INT(dummy, 8);
+ CHECK(*pch == ';'); ADVANCE();
+ SCAN_INT(dummy, 8);
+ CHECK(*pch == 'R');
+ return pch - input + 1;
+}
+
+static int matchMouseDefault(const char *input, int inputSize,
+ MouseRecord &out)
+{
+ const char *pch = input;
+ const char *stop = input + inputSize;
+ CHECK(*pch == '\x1B'); ADVANCE();
+ CHECK(*pch == '['); ADVANCE();
+ CHECK(*pch == 'M'); ADVANCE();
+ out.flags = (*pch - 32) & 0xFF; ADVANCE();
+ out.coord.X = (*pch - '!') & 0xFF;
+ ADVANCE();
+ out.coord.Y = (*pch - '!') & 0xFF;
+ out.release = false;
+ return pch - input + 1;
+}
+
+static int matchMouse1006(const char *input, int inputSize, MouseRecord &out)
+{
+ const char *pch = input;
+ const char *stop = input + inputSize;
+ int32_t temp;
+ CHECK(*pch == '\x1B'); ADVANCE();
+ CHECK(*pch == '['); ADVANCE();
+ CHECK(*pch == '<'); ADVANCE();
+ SCAN_INT(out.flags, 8);
+ CHECK(*pch == ';'); ADVANCE();
+ SCAN_SIGNED_INT(temp, 8); out.coord.X = temp - 1;
+ CHECK(*pch == ';'); ADVANCE();
+ SCAN_SIGNED_INT(temp, 8); out.coord.Y = temp - 1;
+ CHECK(*pch == 'M' || *pch == 'm');
+ out.release = (*pch == 'm');
+ return pch - input + 1;
+}
+
+static int matchMouse1015(const char *input, int inputSize, MouseRecord &out)
+{
+ const char *pch = input;
+ const char *stop = input + inputSize;
+ int32_t temp;
+ CHECK(*pch == '\x1B'); ADVANCE();
+ CHECK(*pch == '['); ADVANCE();
+ SCAN_INT(out.flags, 8); out.flags -= 32;
+ CHECK(*pch == ';'); ADVANCE();
+ SCAN_SIGNED_INT(temp, 8); out.coord.X = temp - 1;
+ CHECK(*pch == ';'); ADVANCE();
+ SCAN_SIGNED_INT(temp, 8); out.coord.Y = temp - 1;
+ CHECK(*pch == 'M');
+ out.release = false;
+ return pch - input + 1;
+}
+
+// Match a mouse input escape sequence of any kind.
+// 0 no match
+// >0 match, returns length of match
+// -1 incomplete match
+static int matchMouseRecord(const char *input, int inputSize, MouseRecord &out)
+{
+ memset(&out, 0, sizeof(out));
+ int ret;
+ if ((ret = matchMouse1006(input, inputSize, out)) != 0) { return ret; }
+ if ((ret = matchMouse1015(input, inputSize, out)) != 0) { return ret; }
+ if ((ret = matchMouseDefault(input, inputSize, out)) != 0) { return ret; }
+ return 0;
+}
+
+#undef CHECK
+#undef ADVANCE
+#undef SCAN_INT
+
+} // anonymous namespace
+
+ConsoleInput::ConsoleInput(HANDLE conin, int mouseMode, DsrSender &dsrSender,
+ Win32Console &console) :
+ m_console(console),
+ m_conin(conin),
+ m_mouseMode(mouseMode),
+ m_dsrSender(dsrSender)
+{
+ addDefaultEntriesToInputMap(m_inputMap);
+ if (hasDebugFlag("dump_input_map")) {
+ m_inputMap.dumpInputMap();
+ }
+
+ // Configure Quick Edit mode according to the mouse mode. Enable
+ // InsertMode for two reasons:
+ // - If it's OFF, it's difficult for the user to turn it ON. The
+ // properties dialog is inaccesible. winpty still faithfully handles
+ // the Insert key, which toggles between the insertion and overwrite
+ // modes.
+ // - When we modify the QuickEdit setting, if ExtendedFlags is OFF,
+ // then we must choose the InsertMode setting. I don't *think* this
+ // case happens, though, because a new console always has ExtendedFlags
+ // ON.
+ // See misc/EnableExtendedFlags.txt.
+ DWORD mode = 0;
+ if (!GetConsoleMode(conin, &mode)) {
+ trace("Agent startup: GetConsoleMode failed");
+ } else {
+ mode |= ENABLE_EXTENDED_FLAGS;
+ mode |= ENABLE_INSERT_MODE;
+ if (m_mouseMode == WINPTY_MOUSE_MODE_AUTO) {
+ mode |= ENABLE_QUICK_EDIT_MODE;
+ } else {
+ mode &= ~ENABLE_QUICK_EDIT_MODE;
+ }
+ if (!SetConsoleMode(conin, mode)) {
+ trace("Agent startup: SetConsoleMode failed");
+ }
+ }
+
+ updateInputFlags(true);
+}
+
+void ConsoleInput::writeInput(const std::string &input)
+{
+ if (input.size() == 0) {
+ return;
+ }
+
+ if (isTracingEnabled()) {
+ static bool debugInput = hasDebugFlag("input");
+ if (debugInput) {
+ std::string dumpString;
+ for (size_t i = 0; i < input.size(); ++i) {
+ const char ch = input[i];
+ const char ctrl = decodeUnixCtrlChar(ch);
+ if (ctrl != '\0') {
+ dumpString += '^';
+ dumpString += ctrl;
+ } else {
+ dumpString += ch;
+ }
+ }
+ dumpString += " (";
+ for (size_t i = 0; i < input.size(); ++i) {
+ if (i > 0) {
+ dumpString += ' ';
+ }
+ const unsigned char uch = input[i];
+ char buf[32];
+ winpty_snprintf(buf, "%02X", uch);
+ dumpString += buf;
+ }
+ dumpString += ')';
+ trace("input chars: %s", dumpString.c_str());
+ }
+ }
+
+ m_byteQueue.append(input);
+ doWrite(false);
+ if (!m_byteQueue.empty() && !m_dsrSent) {
+ trace("send DSR");
+ m_dsrSender.sendDsr();
+ m_dsrSent = true;
+ }
+ m_lastWriteTick = GetTickCount();
+}
+
+void ConsoleInput::flushIncompleteEscapeCode()
+{
+ if (!m_byteQueue.empty() &&
+ (GetTickCount() - m_lastWriteTick) > kIncompleteEscapeTimeoutMs) {
+ doWrite(true);
+ m_byteQueue.clear();
+ }
+}
+
+void ConsoleInput::updateInputFlags(bool forceTrace)
+{
+ const DWORD mode = inputConsoleMode();
+ const bool newFlagEE = (mode & ENABLE_EXTENDED_FLAGS) != 0;
+ const bool newFlagMI = (mode & ENABLE_MOUSE_INPUT) != 0;
+ const bool newFlagQE = (mode & ENABLE_QUICK_EDIT_MODE) != 0;
+ const bool newFlagEI = (mode & 0x200) != 0;
+ if (forceTrace ||
+ newFlagEE != m_enableExtendedEnabled ||
+ newFlagMI != m_mouseInputEnabled ||
+ newFlagQE != m_quickEditEnabled ||
+ newFlagEI != m_escapeInputEnabled) {
+ trace("CONIN modes: Extended=%s, MouseInput=%s QuickEdit=%s EscapeInput=%s",
+ newFlagEE ? "on" : "off",
+ newFlagMI ? "on" : "off",
+ newFlagQE ? "on" : "off",
+ newFlagEI ? "on" : "off");
+ }
+ m_enableExtendedEnabled = newFlagEE;
+ m_mouseInputEnabled = newFlagMI;
+ m_quickEditEnabled = newFlagQE;
+ m_escapeInputEnabled = newFlagEI;
+}
+
+bool ConsoleInput::shouldActivateTerminalMouse()
+{
+ // Return whether the agent should activate the terminal's mouse mode.
+ if (m_mouseMode == WINPTY_MOUSE_MODE_AUTO) {
+ // Some programs (e.g. Cygwin command-line programs like bash.exe and
+ // python2.7.exe) turn off ENABLE_EXTENDED_FLAGS and turn on
+ // ENABLE_MOUSE_INPUT, but do not turn off QuickEdit mode and do not
+ // actually care about mouse input. Only enable the terminal mouse
+ // mode if ENABLE_EXTENDED_FLAGS is on. See
+ // misc/EnableExtendedFlags.txt.
+ return m_mouseInputEnabled && !m_quickEditEnabled &&
+ m_enableExtendedEnabled;
+ } else if (m_mouseMode == WINPTY_MOUSE_MODE_FORCE) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void ConsoleInput::doWrite(bool isEof)
+{
+ const char *data = m_byteQueue.c_str();
+ std::vector<INPUT_RECORD> records;
+ size_t idx = 0;
+ while (idx < m_byteQueue.size()) {
+ int charSize = scanInput(records, &data[idx], m_byteQueue.size() - idx, isEof);
+ if (charSize == -1)
+ break;
+ idx += charSize;
+ }
+ m_byteQueue.erase(0, idx);
+ flushInputRecords(records);
+}
+
+void ConsoleInput::flushInputRecords(std::vector<INPUT_RECORD> &records)
+{
+ if (records.size() == 0) {
+ return;
+ }
+ DWORD actual = 0;
+ if (!WriteConsoleInputW(m_conin, records.data(), records.size(), &actual)) {
+ trace("WriteConsoleInputW failed");
+ }
+ records.clear();
+}
+
+// This behavior isn't strictly correct, because the keypresses (probably?)
+// adopt the keyboard state (e.g. Ctrl/Alt/Shift modifiers) of the current
+// window station's keyboard, which has no necessary relationship to the winpty
+// instance. It's unlikely to be an issue in practice, but it's conceivable.
+// (Imagine a foreground SSH server, where the local user holds down Ctrl,
+// while the remote user tries to use WSL navigation keys.) I suspect using
+// the BackgroundDesktop mechanism in winpty would fix the problem.
+//
+// https://github.com/rprichard/winpty/issues/116
+static void sendKeyMessage(HWND hwnd, bool isKeyDown, uint16_t virtualKey)
+{
+ uint32_t scanCode = MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC);
+ if (scanCode > 255) {
+ scanCode = 0;
+ }
+ SendMessage(hwnd, isKeyDown ? WM_KEYDOWN : WM_KEYUP, virtualKey,
+ (scanCode << 16) | 1u | (isKeyDown ? 0u : 0xc0000000u));
+}
+
+int ConsoleInput::scanInput(std::vector<INPUT_RECORD> &records,
+ const char *input,
+ int inputSize,
+ bool isEof)
+{
+ ASSERT(inputSize >= 1);
+
+ // Ctrl-C.
+ //
+ // In processed mode, use GenerateConsoleCtrlEvent so that Ctrl-C handlers
+ // are called. GenerateConsoleCtrlEvent unfortunately doesn't interrupt
+ // ReadConsole calls[1]. Using WM_KEYDOWN/UP fixes the ReadConsole
+ // problem, but breaks in background window stations/desktops.
+ //
+ // In unprocessed mode, there's an entry for Ctrl-C in the SimpleEncoding
+ // table in DefaultInputMap.
+ //
+ // [1] https://github.com/rprichard/winpty/issues/116
+ if (input[0] == '\x03' && (inputConsoleMode() & ENABLE_PROCESSED_INPUT)) {
+ flushInputRecords(records);
+ trace("Ctrl-C");
+ const BOOL ret = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
+ trace("GenerateConsoleCtrlEvent: %d", ret);
+ return 1;
+ }
+
+ if (input[0] == '\x1B') {
+ // Attempt to match the Device Status Report (DSR) reply.
+ int dsrLen = matchDsr(input, inputSize);
+ if (dsrLen > 0) {
+ trace("Received a DSR reply");
+ m_dsrSent = false;
+ return dsrLen;
+ } else if (!isEof && dsrLen == -1) {
+ // Incomplete DSR match.
+ trace("Incomplete DSR match");
+ return -1;
+ }
+
+ int mouseLen = scanMouseInput(records, input, inputSize);
+ if (mouseLen > 0 || (!isEof && mouseLen == -1)) {
+ return mouseLen;
+ }
+ }
+
+ // Search the input map.
+ InputMap::Key match;
+ bool incomplete;
+ int matchLen = m_inputMap.lookupKey(input, inputSize, match, incomplete);
+ if (!isEof && incomplete) {
+ // Incomplete match -- need more characters (or wait for a
+ // timeout to signify flushed input).
+ trace("Incomplete escape sequence");
+ return -1;
+ } else if (matchLen > 0) {
+ uint32_t winCodePointDn = match.unicodeChar;
+ if ((match.keyState & LEFT_CTRL_PRESSED) && (match.keyState & LEFT_ALT_PRESSED)) {
+ winCodePointDn = '\0';
+ }
+ uint32_t winCodePointUp = winCodePointDn;
+ if (match.keyState & LEFT_ALT_PRESSED) {
+ winCodePointUp = '\0';
+ }
+ appendKeyPress(records, match.virtualKey,
+ winCodePointDn, winCodePointUp, match.keyState,
+ match.unicodeChar, match.keyState);
+ return matchLen;
+ }
+
+ // Recognize Alt-<character>.
+ //
+ // This code doesn't match Alt-ESC, which is encoded as `ESC ESC`, but
+ // maybe it should. I was concerned that pressing ESC rapidly enough could
+ // accidentally trigger Alt-ESC. (e.g. The user would have to be faster
+ // than the DSR flushing mechanism or use a decrepit terminal. The user
+ // might be on a slow network connection.)
+ if (input[0] == '\x1B' && inputSize >= 2 && input[1] != '\x1B') {
+ const int len = utf8CharLength(input[1]);
+ if (len > 0) {
+ if (1 + len > inputSize) {
+ // Incomplete character.
+ trace("Incomplete UTF-8 character in Alt-<Char>");
+ return -1;
+ }
+ appendUtf8Char(records, &input[1], len, true);
+ return 1 + len;
+ }
+ }
+
+ // A UTF-8 character.
+ const int len = utf8CharLength(input[0]);
+ if (len == 0) {
+ static bool debugInput = isTracingEnabled() && hasDebugFlag("input");
+ if (debugInput) {
+ trace("Discarding invalid input byte: %02X",
+ static_cast<unsigned char>(input[0]));
+ }
+ return 1;
+ }
+ if (len > inputSize) {
+ // Incomplete character.
+ trace("Incomplete UTF-8 character");
+ return -1;
+ }
+ appendUtf8Char(records, &input[0], len, false);
+ return len;
+}
+
+int ConsoleInput::scanMouseInput(std::vector<INPUT_RECORD> &records,
+ const char *input,
+ int inputSize)
+{
+ MouseRecord record;
+ const int len = matchMouseRecord(input, inputSize, record);
+ if (len <= 0) {
+ return len;
+ }
+
+ if (isTracingEnabled()) {
+ static bool debugInput = hasDebugFlag("input");
+ if (debugInput) {
+ trace("mouse input: %s", record.toString().c_str());
+ }
+ }
+
+ const int button = record.flags & 0x03;
+ INPUT_RECORD newRecord = {0};
+ newRecord.EventType = MOUSE_EVENT;
+ MOUSE_EVENT_RECORD &mer = newRecord.Event.MouseEvent;
+
+ mer.dwMousePosition.X =
+ m_mouseWindowRect.Left +
+ std::max(0, std::min<int>(record.coord.X,
+ m_mouseWindowRect.width() - 1));
+
+ mer.dwMousePosition.Y =
+ m_mouseWindowRect.Top +
+ std::max(0, std::min<int>(record.coord.Y,
+ m_mouseWindowRect.height() - 1));
+
+ // The modifier state is neatly independent of everything else.
+ if (record.flags & 0x04) { mer.dwControlKeyState |= SHIFT_PRESSED; }
+ if (record.flags & 0x08) { mer.dwControlKeyState |= LEFT_ALT_PRESSED; }
+ if (record.flags & 0x10) { mer.dwControlKeyState |= LEFT_CTRL_PRESSED; }
+
+ if (record.flags & 0x40) {
+ // Mouse wheel
+ mer.dwEventFlags |= MOUSE_WHEELED;
+ if (button == 0) {
+ // up
+ mer.dwButtonState |= 0x00780000;
+ } else if (button == 1) {
+ // down
+ mer.dwButtonState |= 0xff880000;
+ } else {
+ // Invalid -- do nothing
+ return len;
+ }
+ } else {
+ // Ordinary mouse event
+ if (record.flags & 0x20) { mer.dwEventFlags |= MOUSE_MOVED; }
+ if (button == 3) {
+ m_mouseButtonState = 0;
+ // Potentially advance double-click detection.
+ m_doubleClick.released = true;
+ } else {
+ const DWORD relevantFlag =
+ (button == 0) ? FROM_LEFT_1ST_BUTTON_PRESSED :
+ (button == 1) ? FROM_LEFT_2ND_BUTTON_PRESSED :
+ (button == 2) ? RIGHTMOST_BUTTON_PRESSED :
+ 0;
+ ASSERT(relevantFlag != 0);
+ if (record.release) {
+ m_mouseButtonState &= ~relevantFlag;
+ if (relevantFlag == m_doubleClick.button) {
+ // Potentially advance double-click detection.
+ m_doubleClick.released = true;
+ } else {
+ // End double-click detection.
+ m_doubleClick = DoubleClickDetection();
+ }
+ } else if ((m_mouseButtonState & relevantFlag) == 0) {
+ // The button has been newly pressed.
+ m_mouseButtonState |= relevantFlag;
+ // Detect a double-click. This code looks for an exact
+ // coordinate match, which is stricter than what Windows does,
+ // but Windows has pixel coordinates, and we only have terminal
+ // coordinates.
+ if (m_doubleClick.button == relevantFlag &&
+ m_doubleClick.pos == record.coord &&
+ (GetTickCount() - m_doubleClick.tick <
+ GetDoubleClickTime())) {
+ // Record a double-click and end double-click detection.
+ mer.dwEventFlags |= DOUBLE_CLICK;
+ m_doubleClick = DoubleClickDetection();
+ } else {
+ // Begin double-click detection.
+ m_doubleClick.button = relevantFlag;
+ m_doubleClick.pos = record.coord;
+ m_doubleClick.tick = GetTickCount();
+ }
+ }
+ }
+ }
+
+ mer.dwButtonState |= m_mouseButtonState;
+
+ if (m_mouseInputEnabled && !m_quickEditEnabled) {
+ if (isTracingEnabled()) {
+ static bool debugInput = hasDebugFlag("input");
+ if (debugInput) {
+ trace("mouse event: %s", mouseEventToString(mer).c_str());
+ }
+ }
+
+ records.push_back(newRecord);
+ }
+
+ return len;
+}
+
+void ConsoleInput::appendUtf8Char(std::vector<INPUT_RECORD> &records,
+ const char *charBuffer,
+ const int charLen,
+ const bool terminalAltEscape)
+{
+ const uint32_t codePoint = decodeUtf8(charBuffer);
+ if (codePoint == static_cast<uint32_t>(-1)) {
+ static bool debugInput = isTracingEnabled() && hasDebugFlag("input");
+ if (debugInput) {
+ StringBuilder error(64);
+ error << "Discarding invalid UTF-8 sequence:";
+ for (int i = 0; i < charLen; ++i) {
+ error << ' ';
+ error << hexOfInt<true, uint8_t>(charBuffer[i]);
+ }
+ trace("%s", error.c_str());
+ }
+ return;
+ }
+
+ const short charScan = codePoint > 0xFFFF ? -1 : VkKeyScan(codePoint);
+ uint16_t virtualKey = 0;
+ uint16_t winKeyState = 0;
+ uint32_t winCodePointDn = codePoint;
+ uint32_t winCodePointUp = codePoint;
+ uint16_t vtKeyState = 0;
+
+ if (charScan != -1) {
+ virtualKey = charScan & 0xFF;
+ if (charScan & 0x100) {
+ winKeyState |= SHIFT_PRESSED;
+ }
+ if (charScan & 0x200) {
+ winKeyState |= LEFT_CTRL_PRESSED;
+ }
+ if (charScan & 0x400) {
+ winKeyState |= RIGHT_ALT_PRESSED;
+ }
+ if (terminalAltEscape && (winKeyState & LEFT_CTRL_PRESSED)) {
+ // If the terminal escapes a Ctrl-<Key> with Alt, then set the
+ // codepoint to 0. On the other hand, if a character requires
+ // AltGr (like U+00B2 on a German layout), then VkKeyScan will
+ // report both Ctrl and Alt pressed, and we should keep the
+ // codepoint. See https://github.com/rprichard/winpty/issues/109.
+ winCodePointDn = 0;
+ winCodePointUp = 0;
+ }
+ }
+ if (terminalAltEscape) {
+ winCodePointUp = 0;
+ winKeyState |= LEFT_ALT_PRESSED;
+ vtKeyState |= LEFT_ALT_PRESSED;
+ }
+
+ appendKeyPress(records, virtualKey,
+ winCodePointDn, winCodePointUp, winKeyState,
+ codePoint, vtKeyState);
+}
+
+void ConsoleInput::appendKeyPress(std::vector<INPUT_RECORD> &records,
+ const uint16_t virtualKey,
+ const uint32_t winCodePointDn,
+ const uint32_t winCodePointUp,
+ const uint16_t winKeyState,
+ const uint32_t vtCodePoint,
+ const uint16_t vtKeyState)
+{
+ const bool ctrl = (winKeyState & LEFT_CTRL_PRESSED) != 0;
+ const bool leftAlt = (winKeyState & LEFT_ALT_PRESSED) != 0;
+ const bool rightAlt = (winKeyState & RIGHT_ALT_PRESSED) != 0;
+ const bool shift = (winKeyState & SHIFT_PRESSED) != 0;
+ const bool enhanced = (winKeyState & ENHANCED_KEY) != 0;
+ bool hasDebugInput = false;
+
+ if (isTracingEnabled()) {
+ static bool debugInput = hasDebugFlag("input");
+ if (debugInput) {
+ hasDebugInput = true;
+ InputMap::Key key = { virtualKey, winCodePointDn, winKeyState };
+ trace("keypress: %s", key.toString().c_str());
+ }
+ }
+
+ if (m_escapeInputEnabled &&
+ (virtualKey == VK_UP ||
+ virtualKey == VK_DOWN ||
+ virtualKey == VK_LEFT ||
+ virtualKey == VK_RIGHT ||
+ virtualKey == VK_HOME ||
+ virtualKey == VK_END) &&
+ !ctrl && !leftAlt && !rightAlt && !shift) {
+ flushInputRecords(records);
+ if (hasDebugInput) {
+ trace("sending keypress to console HWND");
+ }
+ sendKeyMessage(m_console.hwnd(), true, virtualKey);
+ sendKeyMessage(m_console.hwnd(), false, virtualKey);
+ return;
+ }
+
+ uint16_t stepKeyState = 0;
+ if (ctrl) {
+ stepKeyState |= LEFT_CTRL_PRESSED;
+ appendInputRecord(records, TRUE, VK_CONTROL, 0, stepKeyState);
+ }
+ if (leftAlt) {
+ stepKeyState |= LEFT_ALT_PRESSED;
+ appendInputRecord(records, TRUE, VK_MENU, 0, stepKeyState);
+ }
+ if (rightAlt) {
+ stepKeyState |= RIGHT_ALT_PRESSED;
+ appendInputRecord(records, TRUE, VK_MENU, 0, stepKeyState | ENHANCED_KEY);
+ }
+ if (shift) {
+ stepKeyState |= SHIFT_PRESSED;
+ appendInputRecord(records, TRUE, VK_SHIFT, 0, stepKeyState);
+ }
+ if (enhanced) {
+ stepKeyState |= ENHANCED_KEY;
+ }
+ if (m_escapeInputEnabled) {
+ reencodeEscapedKeyPress(records, virtualKey, vtCodePoint, vtKeyState);
+ } else {
+ appendCPInputRecords(records, TRUE, virtualKey, winCodePointDn, stepKeyState);
+ }
+ appendCPInputRecords(records, FALSE, virtualKey, winCodePointUp, stepKeyState);
+ if (enhanced) {
+ stepKeyState &= ~ENHANCED_KEY;
+ }
+ if (shift) {
+ stepKeyState &= ~SHIFT_PRESSED;
+ appendInputRecord(records, FALSE, VK_SHIFT, 0, stepKeyState);
+ }
+ if (rightAlt) {
+ stepKeyState &= ~RIGHT_ALT_PRESSED;
+ appendInputRecord(records, FALSE, VK_MENU, 0, stepKeyState | ENHANCED_KEY);
+ }
+ if (leftAlt) {
+ stepKeyState &= ~LEFT_ALT_PRESSED;
+ appendInputRecord(records, FALSE, VK_MENU, 0, stepKeyState);
+ }
+ if (ctrl) {
+ stepKeyState &= ~LEFT_CTRL_PRESSED;
+ appendInputRecord(records, FALSE, VK_CONTROL, 0, stepKeyState);
+ }
+}
+
+void ConsoleInput::appendCPInputRecords(std::vector<INPUT_RECORD> &records,
+ BOOL keyDown,
+ uint16_t virtualKey,
+ uint32_t codePoint,
+ uint16_t keyState)
+{
+ // This behavior really doesn't match that of the Windows console (in
+ // normal, non-escape-mode). Judging by the copy-and-paste behavior,
+ // Windows apparently handles everything outside of the keyboard layout by
+ // first sending a sequence of Alt+KeyPad events, then finally a key-up
+ // event whose UnicodeChar has the appropriate value. For U+00A2 (CENT
+ // SIGN):
+ //
+ // key: dn rpt=1 scn=56 LAlt-MENU ch=0
+ // key: dn rpt=1 scn=79 LAlt-NUMPAD1 ch=0
+ // key: up rpt=1 scn=79 LAlt-NUMPAD1 ch=0
+ // key: dn rpt=1 scn=76 LAlt-NUMPAD5 ch=0
+ // key: up rpt=1 scn=76 LAlt-NUMPAD5 ch=0
+ // key: dn rpt=1 scn=76 LAlt-NUMPAD5 ch=0
+ // key: up rpt=1 scn=76 LAlt-NUMPAD5 ch=0
+ // key: up rpt=1 scn=56 MENU ch=0xa2
+ //
+ // The Alt+155 value matches the encoding of U+00A2 in CP-437. Curiously,
+ // if I use "chcp 1252" to change the encoding, then copy-and-pasting
+ // produces Alt+162 instead. (U+00A2 is 162 in CP-1252.) However, typing
+ // Alt+155 or Alt+162 produce the same characters regardless of console
+ // code page. (That is, they use CP-437 and yield U+00A2 and U+00F3.)
+ //
+ // For characters outside the BMP, Windows repeats the process for both
+ // UTF-16 code units, e.g, for U+1F300 (CYCLONE):
+ //
+ // key: dn rpt=1 scn=56 LAlt-MENU ch=0
+ // key: dn rpt=1 scn=77 LAlt-NUMPAD6 ch=0
+ // key: up rpt=1 scn=77 LAlt-NUMPAD6 ch=0
+ // key: dn rpt=1 scn=81 LAlt-NUMPAD3 ch=0
+ // key: up rpt=1 scn=81 LAlt-NUMPAD3 ch=0
+ // key: up rpt=1 scn=56 MENU ch=0xd83c
+ // key: dn rpt=1 scn=56 LAlt-MENU ch=0
+ // key: dn rpt=1 scn=77 LAlt-NUMPAD6 ch=0
+ // key: up rpt=1 scn=77 LAlt-NUMPAD6 ch=0
+ // key: dn rpt=1 scn=81 LAlt-NUMPAD3 ch=0
+ // key: up rpt=1 scn=81 LAlt-NUMPAD3 ch=0
+ // key: up rpt=1 scn=56 MENU ch=0xdf00
+ //
+ // In this case, it sends Alt+63 twice, which signifies '?'. Apparently
+ // CMD and Cygwin bash are both able to decode this.
+ //
+ // Also note that typing Alt+NNN still works if NumLock is off, e.g.:
+ //
+ // key: dn rpt=1 scn=56 LAlt-MENU ch=0
+ // key: dn rpt=1 scn=79 LAlt-END ch=0
+ // key: up rpt=1 scn=79 LAlt-END ch=0
+ // key: dn rpt=1 scn=76 LAlt-CLEAR ch=0
+ // key: up rpt=1 scn=76 LAlt-CLEAR ch=0
+ // key: dn rpt=1 scn=76 LAlt-CLEAR ch=0
+ // key: up rpt=1 scn=76 LAlt-CLEAR ch=0
+ // key: up rpt=1 scn=56 MENU ch=0xa2
+ //
+ // Evidently, the Alt+NNN key events are not intended to be decoded to a
+ // character. Maybe programs are looking for a key-up ALT/MENU event with
+ // a non-zero character?
+
+ wchar_t ws[2];
+ const int wslen = encodeUtf16(ws, codePoint);
+
+ if (wslen == 1) {
+ appendInputRecord(records, keyDown, virtualKey, ws[0], keyState);
+ } else if (wslen == 2) {
+ appendInputRecord(records, keyDown, virtualKey, ws[0], keyState);
+ appendInputRecord(records, keyDown, virtualKey, ws[1], keyState);
+ } else {
+ // This situation isn't that bad, but it should never happen,
+ // because invalid codepoints shouldn't reach this point.
+ trace("INTERNAL ERROR: appendInputRecordCP: invalid codePoint: "
+ "U+%04X", codePoint);
+ }
+}
+
+void ConsoleInput::appendInputRecord(std::vector<INPUT_RECORD> &records,
+ BOOL keyDown,
+ uint16_t virtualKey,
+ wchar_t utf16Char,
+ uint16_t keyState)
+{
+ INPUT_RECORD ir = {};
+ ir.EventType = KEY_EVENT;
+ ir.Event.KeyEvent.bKeyDown = keyDown;
+ ir.Event.KeyEvent.wRepeatCount = 1;
+ ir.Event.KeyEvent.wVirtualKeyCode = virtualKey;
+ ir.Event.KeyEvent.wVirtualScanCode =
+ MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC);
+ ir.Event.KeyEvent.uChar.UnicodeChar = utf16Char;
+ ir.Event.KeyEvent.dwControlKeyState = keyState;
+ records.push_back(ir);
+}
+
+DWORD ConsoleInput::inputConsoleMode()
+{
+ DWORD mode = 0;
+ if (!GetConsoleMode(m_conin, &mode)) {
+ trace("GetConsoleMode failed");
+ return 0;
+ }
+ return mode;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInput.h b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.h
new file mode 100644
index 0000000000..e807d973ba
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInput.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef CONSOLEINPUT_H
+#define CONSOLEINPUT_H
+
+#include <windows.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "Coord.h"
+#include "InputMap.h"
+#include "SmallRect.h"
+
+class Win32Console;
+class DsrSender;
+
+class ConsoleInput
+{
+public:
+ ConsoleInput(HANDLE conin, int mouseMode, DsrSender &dsrSender,
+ Win32Console &console);
+ void writeInput(const std::string &input);
+ void flushIncompleteEscapeCode();
+ void setMouseWindowRect(SmallRect val) { m_mouseWindowRect = val; }
+ void updateInputFlags(bool forceTrace=false);
+ bool shouldActivateTerminalMouse();
+
+private:
+ void doWrite(bool isEof);
+ void flushInputRecords(std::vector<INPUT_RECORD> &records);
+ int scanInput(std::vector<INPUT_RECORD> &records,
+ const char *input,
+ int inputSize,
+ bool isEof);
+ int scanMouseInput(std::vector<INPUT_RECORD> &records,
+ const char *input,
+ int inputSize);
+ void appendUtf8Char(std::vector<INPUT_RECORD> &records,
+ const char *charBuffer,
+ int charLen,
+ bool terminalAltEscape);
+ void appendKeyPress(std::vector<INPUT_RECORD> &records,
+ uint16_t virtualKey,
+ uint32_t winCodePointDn,
+ uint32_t winCodePointUp,
+ uint16_t winKeyState,
+ uint32_t vtCodePoint,
+ uint16_t vtKeyState);
+
+public:
+ static void appendCPInputRecords(std::vector<INPUT_RECORD> &records,
+ BOOL keyDown,
+ uint16_t virtualKey,
+ uint32_t codePoint,
+ uint16_t keyState);
+ static void appendInputRecord(std::vector<INPUT_RECORD> &records,
+ BOOL keyDown,
+ uint16_t virtualKey,
+ wchar_t utf16Char,
+ uint16_t keyState);
+
+private:
+ DWORD inputConsoleMode();
+
+private:
+ Win32Console &m_console;
+ HANDLE m_conin = nullptr;
+ int m_mouseMode = 0;
+ DsrSender &m_dsrSender;
+ bool m_dsrSent = false;
+ std::string m_byteQueue;
+ InputMap m_inputMap;
+ DWORD m_lastWriteTick = 0;
+ DWORD m_mouseButtonState = 0;
+ struct DoubleClickDetection {
+ DWORD button = 0;
+ Coord pos;
+ DWORD tick = 0;
+ bool released = false;
+ } m_doubleClick;
+ bool m_enableExtendedEnabled = false;
+ bool m_mouseInputEnabled = false;
+ bool m_quickEditEnabled = false;
+ bool m_escapeInputEnabled = false;
+ SmallRect m_mouseWindowRect;
+};
+
+#endif // CONSOLEINPUT_H
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.cc
new file mode 100644
index 0000000000..b79545eea9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "ConsoleInputReencoding.h"
+
+#include "ConsoleInput.h"
+
+namespace {
+
+static void outch(std::vector<INPUT_RECORD> &out, wchar_t ch) {
+ ConsoleInput::appendInputRecord(out, TRUE, 0, ch, 0);
+}
+
+} // anonymous namespace
+
+void reencodeEscapedKeyPress(
+ std::vector<INPUT_RECORD> &out,
+ uint16_t virtualKey,
+ uint32_t codePoint,
+ uint16_t keyState) {
+
+ struct EscapedKey {
+ enum { None, Numeric, Letter } kind;
+ wchar_t content[2];
+ };
+
+ EscapedKey escapeCode = {};
+ switch (virtualKey) {
+ case VK_UP: escapeCode = { EscapedKey::Letter, {'A'} }; break;
+ case VK_DOWN: escapeCode = { EscapedKey::Letter, {'B'} }; break;
+ case VK_RIGHT: escapeCode = { EscapedKey::Letter, {'C'} }; break;
+ case VK_LEFT: escapeCode = { EscapedKey::Letter, {'D'} }; break;
+ case VK_CLEAR: escapeCode = { EscapedKey::Letter, {'E'} }; break;
+ case VK_F1: escapeCode = { EscapedKey::Numeric, {'1', '1'} }; break;
+ case VK_F2: escapeCode = { EscapedKey::Numeric, {'1', '2'} }; break;
+ case VK_F3: escapeCode = { EscapedKey::Numeric, {'1', '3'} }; break;
+ case VK_F4: escapeCode = { EscapedKey::Numeric, {'1', '4'} }; break;
+ case VK_F5: escapeCode = { EscapedKey::Numeric, {'1', '5'} }; break;
+ case VK_F6: escapeCode = { EscapedKey::Numeric, {'1', '7'} }; break;
+ case VK_F7: escapeCode = { EscapedKey::Numeric, {'1', '8'} }; break;
+ case VK_F8: escapeCode = { EscapedKey::Numeric, {'1', '9'} }; break;
+ case VK_F9: escapeCode = { EscapedKey::Numeric, {'2', '0'} }; break;
+ case VK_F10: escapeCode = { EscapedKey::Numeric, {'2', '1'} }; break;
+ case VK_F11: escapeCode = { EscapedKey::Numeric, {'2', '3'} }; break;
+ case VK_F12: escapeCode = { EscapedKey::Numeric, {'2', '4'} }; break;
+ case VK_HOME: escapeCode = { EscapedKey::Letter, {'H'} }; break;
+ case VK_INSERT: escapeCode = { EscapedKey::Numeric, {'2'} }; break;
+ case VK_DELETE: escapeCode = { EscapedKey::Numeric, {'3'} }; break;
+ case VK_END: escapeCode = { EscapedKey::Letter, {'F'} }; break;
+ case VK_PRIOR: escapeCode = { EscapedKey::Numeric, {'5'} }; break;
+ case VK_NEXT: escapeCode = { EscapedKey::Numeric, {'6'} }; break;
+ }
+ if (escapeCode.kind != EscapedKey::None) {
+ int flags = 0;
+ if (keyState & SHIFT_PRESSED) { flags |= 0x1; }
+ if (keyState & LEFT_ALT_PRESSED) { flags |= 0x2; }
+ if (keyState & LEFT_CTRL_PRESSED) { flags |= 0x4; }
+ outch(out, L'\x1b');
+ outch(out, L'[');
+ if (escapeCode.kind == EscapedKey::Numeric) {
+ for (wchar_t ch : escapeCode.content) {
+ if (ch != L'\0') {
+ outch(out, ch);
+ }
+ }
+ } else if (flags != 0) {
+ outch(out, L'1');
+ }
+ if (flags != 0) {
+ outch(out, L';');
+ outch(out, L'1' + flags);
+ }
+ if (escapeCode.kind == EscapedKey::Numeric) {
+ outch(out, L'~');
+ } else {
+ outch(out, escapeCode.content[0]);
+ }
+ return;
+ }
+
+ switch (virtualKey) {
+ case VK_BACK:
+ if (keyState & LEFT_ALT_PRESSED) {
+ outch(out, L'\x1b');
+ }
+ outch(out, L'\x7f');
+ return;
+ case VK_TAB:
+ if (keyState & SHIFT_PRESSED) {
+ outch(out, L'\x1b');
+ outch(out, L'[');
+ outch(out, L'Z');
+ return;
+ }
+ break;
+ }
+
+ if (codePoint != 0) {
+ if (keyState & LEFT_ALT_PRESSED) {
+ outch(out, L'\x1b');
+ }
+ ConsoleInput::appendCPInputRecords(out, TRUE, 0, codePoint, 0);
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.h b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.h
new file mode 100644
index 0000000000..63bc006b5a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleInputReencoding.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_CONSOLE_INPUT_REENCODING_H
+#define AGENT_CONSOLE_INPUT_REENCODING_H
+
+#include <windows.h>
+
+#include <stdint.h>
+
+#include <vector>
+
+void reencodeEscapedKeyPress(
+ std::vector<INPUT_RECORD> &records,
+ uint16_t virtualKey,
+ uint32_t codePoint,
+ uint16_t keyState);
+
+#endif // AGENT_CONSOLE_INPUT_REENCODING_H
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleLine.cc b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.cc
new file mode 100644
index 0000000000..1d2bcb7685
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+//
+// ConsoleLine
+//
+// This data structure keep tracks of the previous CHAR_INFO content of an
+// output line and determines when a line has changed. Detecting line changes
+// is made complicated by terminal resizing.
+//
+
+#include "ConsoleLine.h"
+
+#include <algorithm>
+
+#include "../shared/WinptyAssert.h"
+
+static CHAR_INFO blankChar(WORD attributes)
+{
+ // N.B.: As long as we write to UnicodeChar rather than AsciiChar, there
+ // are no padding bytes that could contain uninitialized bytes. This fact
+ // is important for efficient comparison.
+ CHAR_INFO ret;
+ ret.Attributes = attributes;
+ ret.Char.UnicodeChar = L' ';
+ return ret;
+}
+
+static bool isLineBlank(const CHAR_INFO *line, int length, WORD attributes)
+{
+ for (int col = 0; col < length; ++col) {
+ if (line[col].Attributes != attributes ||
+ line[col].Char.UnicodeChar != L' ') {
+ return false;
+ }
+ }
+ return true;
+}
+
+static inline bool areLinesEqual(
+ const CHAR_INFO *line1,
+ const CHAR_INFO *line2,
+ int length)
+{
+ return memcmp(line1, line2, sizeof(CHAR_INFO) * length) == 0;
+}
+
+ConsoleLine::ConsoleLine() : m_prevLength(0)
+{
+}
+
+void ConsoleLine::reset()
+{
+ m_prevLength = 0;
+ m_prevData.clear();
+}
+
+// Determines whether the given line is sufficiently different from the
+// previously seen line as to justify reoutputting the line. The function
+// also sets the `ConsoleLine` to the given line, exactly as if `setLine` had
+// been called.
+bool ConsoleLine::detectChangeAndSetLine(const CHAR_INFO *const line, const int newLength)
+{
+ ASSERT(newLength >= 1);
+ ASSERT(m_prevLength <= static_cast<int>(m_prevData.size()));
+
+ if (newLength == m_prevLength) {
+ bool equalLines = areLinesEqual(m_prevData.data(), line, newLength);
+ if (!equalLines) {
+ setLine(line, newLength);
+ }
+ return !equalLines;
+ } else {
+ if (m_prevLength == 0) {
+ setLine(line, newLength);
+ return true;
+ }
+
+ ASSERT(m_prevLength >= 1);
+ const WORD prevBlank = m_prevData[m_prevLength - 1].Attributes;
+ const WORD newBlank = line[newLength - 1].Attributes;
+
+ bool equalLines = false;
+ if (newLength < m_prevLength) {
+ // The line has become shorter. The lines are equal if the common
+ // part is equal, and if the newly truncated characters were blank.
+ equalLines =
+ areLinesEqual(m_prevData.data(), line, newLength) &&
+ isLineBlank(m_prevData.data() + newLength,
+ m_prevLength - newLength,
+ newBlank);
+ } else {
+ //
+ // The line has become longer. The lines are equal if the common
+ // part is equal, and if both the extra characters and any
+ // potentially reexposed characters are blank.
+ //
+ // Two of the most relevant terminals for winpty--mintty and
+ // jediterm--don't (currently) erase the obscured content when a
+ // line is cleared, so we should anticipate its existence when
+ // making a terminal wider and reoutput the line. See:
+ //
+ // * https://github.com/mintty/mintty/issues/480
+ // * https://github.com/JetBrains/jediterm/issues/118
+ //
+ ASSERT(newLength > m_prevLength);
+ equalLines =
+ areLinesEqual(m_prevData.data(), line, m_prevLength) &&
+ isLineBlank(m_prevData.data() + m_prevLength,
+ std::min<int>(m_prevData.size(), newLength) - m_prevLength,
+ prevBlank) &&
+ isLineBlank(line + m_prevLength,
+ newLength - m_prevLength,
+ prevBlank);
+ }
+ setLine(line, newLength);
+ return !equalLines;
+ }
+}
+
+void ConsoleLine::setLine(const CHAR_INFO *const line, const int newLength)
+{
+ if (static_cast<int>(m_prevData.size()) < newLength) {
+ m_prevData.resize(newLength);
+ }
+ memcpy(m_prevData.data(), line, sizeof(CHAR_INFO) * newLength);
+ m_prevLength = newLength;
+}
+
+void ConsoleLine::blank(WORD attributes)
+{
+ m_prevData.resize(1);
+ m_prevData[0] = blankChar(attributes);
+ m_prevLength = 1;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/ConsoleLine.h b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.h
new file mode 100644
index 0000000000..802c189c75
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/ConsoleLine.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef CONSOLE_LINE_H
+#define CONSOLE_LINE_H
+
+#include <windows.h>
+
+#include <vector>
+
+class ConsoleLine
+{
+public:
+ ConsoleLine();
+ void reset();
+ bool detectChangeAndSetLine(const CHAR_INFO *line, int newLength);
+ void setLine(const CHAR_INFO *line, int newLength);
+ void blank(WORD attributes);
+private:
+ int m_prevLength;
+ std::vector<CHAR_INFO> m_prevData;
+};
+
+#endif // CONSOLE_LINE_H
diff --git a/src/libs/3rdparty/winpty/src/agent/Coord.h b/src/libs/3rdparty/winpty/src/agent/Coord.h
new file mode 100644
index 0000000000..74c98addac
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Coord.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef COORD_H
+#define COORD_H
+
+#include <windows.h>
+
+#include <string>
+
+#include "../shared/winpty_snprintf.h"
+
+struct Coord : COORD {
+ Coord()
+ {
+ X = 0;
+ Y = 0;
+ }
+
+ Coord(SHORT x, SHORT y)
+ {
+ X = x;
+ Y = y;
+ }
+
+ Coord(COORD other)
+ {
+ *(COORD*)this = other;
+ }
+
+ Coord(const Coord &other)
+ {
+ *(COORD*)this = *(const COORD*)&other;
+ }
+
+ Coord &operator=(const Coord &other)
+ {
+ *(COORD*)this = *(const COORD*)&other;
+ return *this;
+ }
+
+ bool operator==(const Coord &other) const
+ {
+ return X == other.X && Y == other.Y;
+ }
+
+ bool operator!=(const Coord &other) const
+ {
+ return !(*this == other);
+ }
+
+ Coord operator+(const Coord &other) const
+ {
+ return Coord(X + other.X, Y + other.Y);
+ }
+
+ bool isEmpty() const
+ {
+ return X <= 0 || Y <= 0;
+ }
+
+ std::string toString() const
+ {
+ char ret[32];
+ winpty_snprintf(ret, "(%d,%d)", X, Y);
+ return std::string(ret);
+ }
+};
+
+#endif // COORD_H
diff --git a/src/libs/3rdparty/winpty/src/agent/DebugShowInput.cc b/src/libs/3rdparty/winpty/src/agent/DebugShowInput.cc
new file mode 100644
index 0000000000..191b2e1466
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/DebugShowInput.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "DebugShowInput.h"
+
+#include <windows.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "../shared/StringBuilder.h"
+#include "InputMap.h"
+
+namespace {
+
+struct Flag {
+ DWORD value;
+ const char *text;
+};
+
+static const Flag kButtonStates[] = {
+ { FROM_LEFT_1ST_BUTTON_PRESSED, "1" },
+ { FROM_LEFT_2ND_BUTTON_PRESSED, "2" },
+ { FROM_LEFT_3RD_BUTTON_PRESSED, "3" },
+ { FROM_LEFT_4TH_BUTTON_PRESSED, "4" },
+ { RIGHTMOST_BUTTON_PRESSED, "R" },
+};
+
+static const Flag kControlKeyStates[] = {
+ { CAPSLOCK_ON, "CapsLock" },
+ { ENHANCED_KEY, "Enhanced" },
+ { LEFT_ALT_PRESSED, "LAlt" },
+ { LEFT_CTRL_PRESSED, "LCtrl" },
+ { NUMLOCK_ON, "NumLock" },
+ { RIGHT_ALT_PRESSED, "RAlt" },
+ { RIGHT_CTRL_PRESSED, "RCtrl" },
+ { SCROLLLOCK_ON, "ScrollLock" },
+ { SHIFT_PRESSED, "Shift" },
+};
+
+static const Flag kMouseEventFlags[] = {
+ { DOUBLE_CLICK, "Double" },
+ { 8/*MOUSE_HWHEELED*/, "HWheel" },
+ { MOUSE_MOVED, "Move" },
+ { MOUSE_WHEELED, "Wheel" },
+};
+
+static void writeFlags(StringBuilder &out, DWORD flags,
+ const char *remainderName,
+ const Flag *table, size_t tableSize,
+ char pre, char sep, char post) {
+ DWORD remaining = flags;
+ bool wroteSomething = false;
+ for (size_t i = 0; i < tableSize; ++i) {
+ const Flag &f = table[i];
+ if ((f.value & flags) == f.value) {
+ if (!wroteSomething && pre != '\0') {
+ out << pre;
+ } else if (wroteSomething && sep != '\0') {
+ out << sep;
+ }
+ out << f.text;
+ wroteSomething = true;
+ remaining &= ~f.value;
+ }
+ }
+ if (remaining != 0) {
+ if (!wroteSomething && pre != '\0') {
+ out << pre;
+ } else if (wroteSomething && sep != '\0') {
+ out << sep;
+ }
+ out << remainderName << "(0x" << hexOfInt(remaining) << ')';
+ wroteSomething = true;
+ }
+ if (wroteSomething && post != '\0') {
+ out << post;
+ }
+}
+
+template <size_t n>
+static void writeFlags(StringBuilder &out, DWORD flags,
+ const char *remainderName,
+ const Flag (&table)[n],
+ char pre, char sep, char post) {
+ writeFlags(out, flags, remainderName, table, n, pre, sep, post);
+}
+
+} // anonymous namespace
+
+std::string controlKeyStatePrefix(DWORD controlKeyState) {
+ StringBuilder sb;
+ writeFlags(sb, controlKeyState,
+ "keyState", kControlKeyStates, '\0', '-', '-');
+ return sb.str_moved();
+}
+
+std::string mouseEventToString(const MOUSE_EVENT_RECORD &mer) {
+ const uint16_t buttons = mer.dwButtonState & 0xFFFF;
+ const int16_t wheel = mer.dwButtonState >> 16;
+ StringBuilder sb;
+ sb << "pos=" << mer.dwMousePosition.X << ','
+ << mer.dwMousePosition.Y;
+ writeFlags(sb, mer.dwControlKeyState, "keyState", kControlKeyStates, ' ', ' ', '\0');
+ writeFlags(sb, mer.dwEventFlags, "flags", kMouseEventFlags, ' ', ' ', '\0');
+ writeFlags(sb, buttons, "buttons", kButtonStates, ' ', ' ', '\0');
+ if (wheel != 0) {
+ sb << " wheel=" << wheel;
+ }
+ return sb.str_moved();
+}
+
+void debugShowInput(bool enableMouse, bool escapeInput) {
+ HANDLE conin = GetStdHandle(STD_INPUT_HANDLE);
+ DWORD origConsoleMode = 0;
+ if (!GetConsoleMode(conin, &origConsoleMode)) {
+ fprintf(stderr, "Error: could not read console mode -- "
+ "is STDIN a console handle?\n");
+ exit(1);
+ }
+ DWORD restoreConsoleMode = origConsoleMode;
+ if (enableMouse && !(restoreConsoleMode & ENABLE_EXTENDED_FLAGS)) {
+ // We need to disable QuickEdit mode, because it blocks mouse events.
+ // If ENABLE_EXTENDED_FLAGS wasn't originally in the console mode, then
+ // we have no way of knowning whether QuickEdit or InsertMode are
+ // currently enabled. Enable them both (eventually), because they're
+ // sensible defaults. This case shouldn't happen typically. See
+ // misc/EnableExtendedFlags.txt.
+ restoreConsoleMode |= ENABLE_EXTENDED_FLAGS;
+ restoreConsoleMode |= ENABLE_QUICK_EDIT_MODE;
+ restoreConsoleMode |= ENABLE_INSERT_MODE;
+ }
+ DWORD newConsoleMode = restoreConsoleMode;
+ newConsoleMode &= ~ENABLE_PROCESSED_INPUT;
+ newConsoleMode &= ~ENABLE_LINE_INPUT;
+ newConsoleMode &= ~ENABLE_ECHO_INPUT;
+ newConsoleMode |= ENABLE_WINDOW_INPUT;
+ if (enableMouse) {
+ newConsoleMode |= ENABLE_MOUSE_INPUT;
+ newConsoleMode &= ~ENABLE_QUICK_EDIT_MODE;
+ } else {
+ newConsoleMode &= ~ENABLE_MOUSE_INPUT;
+ }
+ if (escapeInput) {
+ // As of this writing (2016-06-05), Microsoft has shipped two preview
+ // builds of Windows 10 (14316 and 14342) that include a new "Windows
+ // Subsystem for Linux" that runs Ubuntu in a new subsystem. Running
+ // bash in this subsystem requires the non-legacy console mode, and the
+ // console input buffer is put into a special mode where escape
+ // sequences are written into the console input buffer. This mode is
+ // enabled with the 0x200 flag, which is as-yet undocumented.
+ // See https://github.com/rprichard/winpty/issues/82.
+ newConsoleMode |= 0x200;
+ }
+ if (!SetConsoleMode(conin, newConsoleMode)) {
+ fprintf(stderr, "Error: could not set console mode "
+ "(0x%x -> 0x%x -> 0x%x)\n",
+ static_cast<unsigned int>(origConsoleMode),
+ static_cast<unsigned int>(newConsoleMode),
+ static_cast<unsigned int>(restoreConsoleMode));
+ exit(1);
+ }
+ printf("\nPress any keys -- Ctrl-D exits\n\n");
+ INPUT_RECORD records[32];
+ DWORD actual = 0;
+ bool finished = false;
+ while (!finished &&
+ ReadConsoleInputW(conin, records, 32, &actual) && actual >= 1) {
+ StringBuilder sb;
+ for (DWORD i = 0; i < actual; ++i) {
+ const INPUT_RECORD &record = records[i];
+ if (record.EventType == KEY_EVENT) {
+ const KEY_EVENT_RECORD &ker = record.Event.KeyEvent;
+ InputMap::Key key = {
+ ker.wVirtualKeyCode,
+ ker.uChar.UnicodeChar,
+ static_cast<uint16_t>(ker.dwControlKeyState),
+ };
+ sb << "key: " << (ker.bKeyDown ? "dn" : "up")
+ << " rpt=" << ker.wRepeatCount
+ << " scn=" << (ker.wVirtualScanCode ? "0x" : "") << hexOfInt(ker.wVirtualScanCode)
+ << ' ' << key.toString() << '\n';
+ if ((ker.dwControlKeyState &
+ (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) &&
+ ker.wVirtualKeyCode == 'D') {
+ finished = true;
+ break;
+ } else if (ker.wVirtualKeyCode == 0 &&
+ ker.wVirtualScanCode == 0 &&
+ ker.uChar.UnicodeChar == 4) {
+ // Also look for a zeroed-out Ctrl-D record generated for
+ // ENABLE_VIRTUAL_TERMINAL_INPUT.
+ finished = true;
+ break;
+ }
+ } else if (record.EventType == MOUSE_EVENT) {
+ const MOUSE_EVENT_RECORD &mer = record.Event.MouseEvent;
+ sb << "mouse: " << mouseEventToString(mer) << '\n';
+ } else if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
+ const WINDOW_BUFFER_SIZE_RECORD &wbsr =
+ record.Event.WindowBufferSizeEvent;
+ sb << "buffer-resized: dwSize=("
+ << wbsr.dwSize.X << ','
+ << wbsr.dwSize.Y << ")\n";
+ } else if (record.EventType == MENU_EVENT) {
+ const MENU_EVENT_RECORD &mer = record.Event.MenuEvent;
+ sb << "menu-event: commandId=0x"
+ << hexOfInt(mer.dwCommandId) << '\n';
+ } else if (record.EventType == FOCUS_EVENT) {
+ const FOCUS_EVENT_RECORD &fer = record.Event.FocusEvent;
+ sb << "focus: " << (fer.bSetFocus ? "gained" : "lost") << '\n';
+ }
+ }
+
+ const auto str = sb.str_moved();
+ fwrite(str.data(), 1, str.size(), stdout);
+ fflush(stdout);
+ }
+ SetConsoleMode(conin, restoreConsoleMode);
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/DebugShowInput.h b/src/libs/3rdparty/winpty/src/agent/DebugShowInput.h
new file mode 100644
index 0000000000..4fa13604bd
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/DebugShowInput.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_DEBUG_SHOW_INPUT_H
+#define AGENT_DEBUG_SHOW_INPUT_H
+
+#include <windows.h>
+
+#include <string>
+
+std::string controlKeyStatePrefix(DWORD controlKeyState);
+std::string mouseEventToString(const MOUSE_EVENT_RECORD &mer);
+void debugShowInput(bool enableMouse, bool escapeInput);
+
+#endif // AGENT_DEBUG_SHOW_INPUT_H
diff --git a/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.cc b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.cc
new file mode 100644
index 0000000000..5e29d98e4e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.cc
@@ -0,0 +1,422 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "DefaultInputMap.h"
+
+#include <windows.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "../shared/StringBuilder.h"
+#include "../shared/WinptyAssert.h"
+#include "InputMap.h"
+
+#define ESC "\x1B"
+#define DIM(x) (sizeof(x) / sizeof((x)[0]))
+
+namespace {
+
+struct EscapeEncoding {
+ bool alt_prefix_allowed;
+ char prefix;
+ char id;
+ int modifiers;
+ InputMap::Key key;
+};
+
+// Modifiers. A "modifier" is an integer from 2 to 8 that conveys the status
+// of Shift(1), Alt(2), and Ctrl(4). The value is constructed by OR'ing the
+// appropriate value for each active modifier, then adding 1.
+//
+// Details:
+// - kBare: expands to: ESC <prefix> <suffix>
+// - kSemiMod: expands to: ESC <prefix> <numid> ; <mod> <suffix>
+// - kBareMod: expands to: ESC <prefix> <mod> <suffix>
+const int kBare = 0x01;
+const int kSemiMod = 0x02;
+const int kBareMod = 0x04;
+
+// Numeric escape sequences suffixes:
+// - with no flag: accept: ~
+// - kSuffixCtrl: accept: ~ ^
+// - kSuffixShift: accept: ~ $
+// - kSuffixBoth: accept: ~ ^ $ @
+const int kSuffixCtrl = 0x08;
+const int kSuffixShift = 0x10;
+const int kSuffixBoth = kSuffixCtrl | kSuffixShift;
+
+static const EscapeEncoding escapeLetterEncodings[] = {
+ // Conventional arrow keys
+ // kBareMod: Ubuntu /etc/inputrc and IntelliJ/JediTerm use escapes like: ESC [ n ABCD
+ { true, '[', 'A', kBare | kBareMod | kSemiMod, { VK_UP, '\0', 0 } },
+ { true, '[', 'B', kBare | kBareMod | kSemiMod, { VK_DOWN, '\0', 0 } },
+ { true, '[', 'C', kBare | kBareMod | kSemiMod, { VK_RIGHT, '\0', 0 } },
+ { true, '[', 'D', kBare | kBareMod | kSemiMod, { VK_LEFT, '\0', 0 } },
+
+ // putty. putty uses this sequence for Ctrl-Arrow, Shift-Arrow, and
+ // Ctrl-Shift-Arrow, but I can only decode to one choice, so I'm just
+ // leaving the modifier off altogether.
+ { true, 'O', 'A', kBare, { VK_UP, '\0', 0 } },
+ { true, 'O', 'B', kBare, { VK_DOWN, '\0', 0 } },
+ { true, 'O', 'C', kBare, { VK_RIGHT, '\0', 0 } },
+ { true, 'O', 'D', kBare, { VK_LEFT, '\0', 0 } },
+
+ // rxvt, rxvt-unicode
+ // Shift-Ctrl-Arrow can't be identified. It's the same as Shift-Arrow.
+ { true, '[', 'a', kBare, { VK_UP, '\0', SHIFT_PRESSED } },
+ { true, '[', 'b', kBare, { VK_DOWN, '\0', SHIFT_PRESSED } },
+ { true, '[', 'c', kBare, { VK_RIGHT, '\0', SHIFT_PRESSED } },
+ { true, '[', 'd', kBare, { VK_LEFT, '\0', SHIFT_PRESSED } },
+ { true, 'O', 'a', kBare, { VK_UP, '\0', LEFT_CTRL_PRESSED } },
+ { true, 'O', 'b', kBare, { VK_DOWN, '\0', LEFT_CTRL_PRESSED } },
+ { true, 'O', 'c', kBare, { VK_RIGHT, '\0', LEFT_CTRL_PRESSED } },
+ { true, 'O', 'd', kBare, { VK_LEFT, '\0', LEFT_CTRL_PRESSED } },
+
+ // Numpad 5 with NumLock off
+ // * xterm, mintty, and gnome-terminal use `ESC [ E`.
+ // * putty, TERM=cygwin, TERM=linux all use `ESC [ G` for 5
+ // * putty uses `ESC O G` for Ctrl-5 and Shift-5. Omit the modifier
+ // as with putty's arrow keys.
+ // * I never saw modifiers inserted into these escapes, but I think
+ // it should be completely OK with the CSI escapes.
+ { true, '[', 'E', kBare | kSemiMod, { VK_CLEAR, '\0', 0 } },
+ { true, '[', 'G', kBare | kSemiMod, { VK_CLEAR, '\0', 0 } },
+ { true, 'O', 'G', kBare, { VK_CLEAR, '\0', 0 } },
+
+ // Home/End, letter version
+ // * gnome-terminal uses `ESC O [HF]`. I never saw it modified.
+ // kBareMod: IntelliJ/JediTerm uses escapes like: ESC [ n HF
+ { true, '[', 'H', kBare | kBareMod | kSemiMod, { VK_HOME, '\0', 0 } },
+ { true, '[', 'F', kBare | kBareMod | kSemiMod, { VK_END, '\0', 0 } },
+ { true, 'O', 'H', kBare, { VK_HOME, '\0', 0 } },
+ { true, 'O', 'F', kBare, { VK_END, '\0', 0 } },
+
+ // F1-F4, letter version (xterm, VTE, konsole)
+ { true, '[', 'P', kSemiMod, { VK_F1, '\0', 0 } },
+ { true, '[', 'Q', kSemiMod, { VK_F2, '\0', 0 } },
+ { true, '[', 'R', kSemiMod, { VK_F3, '\0', 0 } },
+ { true, '[', 'S', kSemiMod, { VK_F4, '\0', 0 } },
+
+ // GNOME VTE and Konsole have special encodings for modified F1-F4:
+ // * [VTE] ESC O 1 ; n [PQRS]
+ // * [Konsole] ESC O n [PQRS]
+ { false, 'O', 'P', kBare | kBareMod | kSemiMod, { VK_F1, '\0', 0 } },
+ { false, 'O', 'Q', kBare | kBareMod | kSemiMod, { VK_F2, '\0', 0 } },
+ { false, 'O', 'R', kBare | kBareMod | kSemiMod, { VK_F3, '\0', 0 } },
+ { false, 'O', 'S', kBare | kBareMod | kSemiMod, { VK_F4, '\0', 0 } },
+
+ // Handle the "application numpad" escape sequences.
+ //
+ // Terminals output these codes under various circumstances:
+ // * rxvt-unicode: numpad, hold down SHIFT
+ // * rxvt: numpad, by default
+ // * xterm: numpad, after enabling app-mode using DECPAM (`ESC =`). xterm
+ // generates `ESC O <mod> <letter>` for modified numpad presses,
+ // necessitating kBareMod.
+ // * mintty: by combining Ctrl with various keys such as '1' or ','.
+ // Handling those keys is difficult, because mintty is generating the
+ // same sequence for Ctrl-1 and Ctrl-NumPadEnd -- should the virtualKey
+ // be '1' or VK_HOME?
+
+ { true, 'O', 'M', kBare | kBareMod, { VK_RETURN, '\r', 0 } },
+ { true, 'O', 'j', kBare | kBareMod, { VK_MULTIPLY, '*', 0 } },
+ { true, 'O', 'k', kBare | kBareMod, { VK_ADD, '+', 0 } },
+ { true, 'O', 'm', kBare | kBareMod, { VK_SUBTRACT, '-', 0 } },
+ { true, 'O', 'n', kBare | kBareMod, { VK_DELETE, '\0', 0 } },
+ { true, 'O', 'o', kBare | kBareMod, { VK_DIVIDE, '/', 0 } },
+ { true, 'O', 'p', kBare | kBareMod, { VK_INSERT, '\0', 0 } },
+ { true, 'O', 'q', kBare | kBareMod, { VK_END, '\0', 0 } },
+ { true, 'O', 'r', kBare | kBareMod, { VK_DOWN, '\0', 0 } },
+ { true, 'O', 's', kBare | kBareMod, { VK_NEXT, '\0', 0 } },
+ { true, 'O', 't', kBare | kBareMod, { VK_LEFT, '\0', 0 } },
+ { true, 'O', 'u', kBare | kBareMod, { VK_CLEAR, '\0', 0 } },
+ { true, 'O', 'v', kBare | kBareMod, { VK_RIGHT, '\0', 0 } },
+ { true, 'O', 'w', kBare | kBareMod, { VK_HOME, '\0', 0 } },
+ { true, 'O', 'x', kBare | kBareMod, { VK_UP, '\0', 0 } },
+ { true, 'O', 'y', kBare | kBareMod, { VK_PRIOR, '\0', 0 } },
+
+ { true, '[', 'M', kBare | kSemiMod, { VK_RETURN, '\r', 0 } },
+ { true, '[', 'j', kBare | kSemiMod, { VK_MULTIPLY, '*', 0 } },
+ { true, '[', 'k', kBare | kSemiMod, { VK_ADD, '+', 0 } },
+ { true, '[', 'm', kBare | kSemiMod, { VK_SUBTRACT, '-', 0 } },
+ { true, '[', 'n', kBare | kSemiMod, { VK_DELETE, '\0', 0 } },
+ { true, '[', 'o', kBare | kSemiMod, { VK_DIVIDE, '/', 0 } },
+ { true, '[', 'p', kBare | kSemiMod, { VK_INSERT, '\0', 0 } },
+ { true, '[', 'q', kBare | kSemiMod, { VK_END, '\0', 0 } },
+ { true, '[', 'r', kBare | kSemiMod, { VK_DOWN, '\0', 0 } },
+ { true, '[', 's', kBare | kSemiMod, { VK_NEXT, '\0', 0 } },
+ { true, '[', 't', kBare | kSemiMod, { VK_LEFT, '\0', 0 } },
+ { true, '[', 'u', kBare | kSemiMod, { VK_CLEAR, '\0', 0 } },
+ { true, '[', 'v', kBare | kSemiMod, { VK_RIGHT, '\0', 0 } },
+ { true, '[', 'w', kBare | kSemiMod, { VK_HOME, '\0', 0 } },
+ { true, '[', 'x', kBare | kSemiMod, { VK_UP, '\0', 0 } },
+ { true, '[', 'y', kBare | kSemiMod, { VK_PRIOR, '\0', 0 } },
+
+ { false, '[', 'Z', kBare, { VK_TAB, '\t', SHIFT_PRESSED } },
+};
+
+static const EscapeEncoding escapeNumericEncodings[] = {
+ { true, '[', 1, kBare | kSemiMod | kSuffixBoth, { VK_HOME, '\0', 0 } },
+ { true, '[', 2, kBare | kSemiMod | kSuffixBoth, { VK_INSERT, '\0', 0 } },
+ { true, '[', 3, kBare | kSemiMod | kSuffixBoth, { VK_DELETE, '\0', 0 } },
+ { true, '[', 4, kBare | kSemiMod | kSuffixBoth, { VK_END, '\0', 0 } },
+ { true, '[', 5, kBare | kSemiMod | kSuffixBoth, { VK_PRIOR, '\0', 0 } },
+ { true, '[', 6, kBare | kSemiMod | kSuffixBoth, { VK_NEXT, '\0', 0 } },
+ { true, '[', 7, kBare | kSemiMod | kSuffixBoth, { VK_HOME, '\0', 0 } },
+ { true, '[', 8, kBare | kSemiMod | kSuffixBoth, { VK_END, '\0', 0 } },
+ { true, '[', 11, kBare | kSemiMod | kSuffixBoth, { VK_F1, '\0', 0 } },
+ { true, '[', 12, kBare | kSemiMod | kSuffixBoth, { VK_F2, '\0', 0 } },
+ { true, '[', 13, kBare | kSemiMod | kSuffixBoth, { VK_F3, '\0', 0 } },
+ { true, '[', 14, kBare | kSemiMod | kSuffixBoth, { VK_F4, '\0', 0 } },
+ { true, '[', 15, kBare | kSemiMod | kSuffixBoth, { VK_F5, '\0', 0 } },
+ { true, '[', 17, kBare | kSemiMod | kSuffixBoth, { VK_F6, '\0', 0 } },
+ { true, '[', 18, kBare | kSemiMod | kSuffixBoth, { VK_F7, '\0', 0 } },
+ { true, '[', 19, kBare | kSemiMod | kSuffixBoth, { VK_F8, '\0', 0 } },
+ { true, '[', 20, kBare | kSemiMod | kSuffixBoth, { VK_F9, '\0', 0 } },
+ { true, '[', 21, kBare | kSemiMod | kSuffixBoth, { VK_F10, '\0', 0 } },
+ { true, '[', 23, kBare | kSemiMod | kSuffixBoth, { VK_F11, '\0', 0 } },
+ { true, '[', 24, kBare | kSemiMod | kSuffixBoth, { VK_F12, '\0', 0 } },
+ { true, '[', 25, kBare | kSemiMod | kSuffixBoth, { VK_F3, '\0', SHIFT_PRESSED } },
+ { true, '[', 26, kBare | kSemiMod | kSuffixBoth, { VK_F4, '\0', SHIFT_PRESSED } },
+ { true, '[', 28, kBare | kSemiMod | kSuffixBoth, { VK_F5, '\0', SHIFT_PRESSED } },
+ { true, '[', 29, kBare | kSemiMod | kSuffixBoth, { VK_F6, '\0', SHIFT_PRESSED } },
+ { true, '[', 31, kBare | kSemiMod | kSuffixBoth, { VK_F7, '\0', SHIFT_PRESSED } },
+ { true, '[', 32, kBare | kSemiMod | kSuffixBoth, { VK_F8, '\0', SHIFT_PRESSED } },
+ { true, '[', 33, kBare | kSemiMod | kSuffixBoth, { VK_F9, '\0', SHIFT_PRESSED } },
+ { true, '[', 34, kBare | kSemiMod | kSuffixBoth, { VK_F10, '\0', SHIFT_PRESSED } },
+};
+
+const int kCsiShiftModifier = 1;
+const int kCsiAltModifier = 2;
+const int kCsiCtrlModifier = 4;
+
+static inline bool useEnhancedForVirtualKey(uint16_t vk) {
+ switch (vk) {
+ case VK_UP:
+ case VK_DOWN:
+ case VK_LEFT:
+ case VK_RIGHT:
+ case VK_INSERT:
+ case VK_DELETE:
+ case VK_HOME:
+ case VK_END:
+ case VK_PRIOR:
+ case VK_NEXT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void addSimpleEntries(InputMap &inputMap) {
+ struct SimpleEncoding {
+ const char *encoding;
+ InputMap::Key key;
+ };
+
+ static const SimpleEncoding simpleEncodings[] = {
+ // Ctrl-<letter/digit> seems to be handled OK by the default code path.
+
+ { "\x7F", { VK_BACK, '\x08', 0, } },
+ { ESC "\x7F", { VK_BACK, '\x08', LEFT_ALT_PRESSED, } },
+ { "\x03", { 'C', '\x03', LEFT_CTRL_PRESSED, } },
+
+ // Handle special F1-F5 for TERM=linux and TERM=cygwin.
+ { ESC "[[A", { VK_F1, '\0', 0 } },
+ { ESC "[[B", { VK_F2, '\0', 0 } },
+ { ESC "[[C", { VK_F3, '\0', 0 } },
+ { ESC "[[D", { VK_F4, '\0', 0 } },
+ { ESC "[[E", { VK_F5, '\0', 0 } },
+
+ { ESC ESC "[[A", { VK_F1, '\0', LEFT_ALT_PRESSED } },
+ { ESC ESC "[[B", { VK_F2, '\0', LEFT_ALT_PRESSED } },
+ { ESC ESC "[[C", { VK_F3, '\0', LEFT_ALT_PRESSED } },
+ { ESC ESC "[[D", { VK_F4, '\0', LEFT_ALT_PRESSED } },
+ { ESC ESC "[[E", { VK_F5, '\0', LEFT_ALT_PRESSED } },
+ };
+
+ for (size_t i = 0; i < DIM(simpleEncodings); ++i) {
+ auto k = simpleEncodings[i].key;
+ if (useEnhancedForVirtualKey(k.virtualKey)) {
+ k.keyState |= ENHANCED_KEY;
+ }
+ inputMap.set(simpleEncodings[i].encoding,
+ strlen(simpleEncodings[i].encoding),
+ k);
+ }
+}
+
+struct ExpandContext {
+ InputMap &inputMap;
+ const EscapeEncoding &e;
+ char *buffer;
+ char *bufferEnd;
+};
+
+static inline void setEncoding(const ExpandContext &ctx, char *end,
+ uint16_t extraKeyState) {
+ InputMap::Key k = ctx.e.key;
+ k.keyState |= extraKeyState;
+ if (k.keyState & LEFT_CTRL_PRESSED) {
+ switch (k.virtualKey) {
+ case VK_ADD:
+ case VK_DIVIDE:
+ case VK_MULTIPLY:
+ case VK_SUBTRACT:
+ k.unicodeChar = '\0';
+ break;
+ case VK_RETURN:
+ k.unicodeChar = '\n';
+ break;
+ }
+ }
+ if (useEnhancedForVirtualKey(k.virtualKey)) {
+ k.keyState |= ENHANCED_KEY;
+ }
+ ctx.inputMap.set(ctx.buffer, end - ctx.buffer, k);
+}
+
+static inline uint16_t keyStateForMod(int mod) {
+ int ret = 0;
+ if ((mod - 1) & kCsiShiftModifier) ret |= SHIFT_PRESSED;
+ if ((mod - 1) & kCsiAltModifier) ret |= LEFT_ALT_PRESSED;
+ if ((mod - 1) & kCsiCtrlModifier) ret |= LEFT_CTRL_PRESSED;
+ return ret;
+}
+
+static void expandNumericEncodingSuffix(const ExpandContext &ctx, char *p,
+ uint16_t extraKeyState) {
+ ASSERT(p <= ctx.bufferEnd - 1);
+ {
+ char *q = p;
+ *q++ = '~';
+ setEncoding(ctx, q, extraKeyState);
+ }
+ if (ctx.e.modifiers & kSuffixShift) {
+ char *q = p;
+ *q++ = '$';
+ setEncoding(ctx, q, extraKeyState | SHIFT_PRESSED);
+ }
+ if (ctx.e.modifiers & kSuffixCtrl) {
+ char *q = p;
+ *q++ = '^';
+ setEncoding(ctx, q, extraKeyState | LEFT_CTRL_PRESSED);
+ }
+ if (ctx.e.modifiers & (kSuffixCtrl | kSuffixShift)) {
+ char *q = p;
+ *q++ = '@';
+ setEncoding(ctx, q, extraKeyState | SHIFT_PRESSED | LEFT_CTRL_PRESSED);
+ }
+}
+
+template <bool is_numeric>
+static inline void expandEncodingAfterAltPrefix(
+ const ExpandContext &ctx, char *p, uint16_t extraKeyState) {
+ auto appendId = [&](char *&ptr) {
+ const auto idstr = decOfInt(ctx.e.id);
+ ASSERT(ptr <= ctx.bufferEnd - idstr.size());
+ std::copy(idstr.data(), idstr.data() + idstr.size(), ptr);
+ ptr += idstr.size();
+ };
+ ASSERT(p <= ctx.bufferEnd - 2);
+ *p++ = '\x1b';
+ *p++ = ctx.e.prefix;
+ if (ctx.e.modifiers & kBare) {
+ char *q = p;
+ if (is_numeric) {
+ appendId(q);
+ expandNumericEncodingSuffix(ctx, q, extraKeyState);
+ } else {
+ ASSERT(q <= ctx.bufferEnd - 1);
+ *q++ = ctx.e.id;
+ setEncoding(ctx, q, extraKeyState);
+ }
+ }
+ if (ctx.e.modifiers & kBareMod) {
+ ASSERT(!is_numeric && "kBareMod is invalid with numeric sequences");
+ for (int mod = 2; mod <= 8; ++mod) {
+ char *q = p;
+ ASSERT(q <= ctx.bufferEnd - 2);
+ *q++ = '0' + mod;
+ *q++ = ctx.e.id;
+ setEncoding(ctx, q, extraKeyState | keyStateForMod(mod));
+ }
+ }
+ if (ctx.e.modifiers & kSemiMod) {
+ for (int mod = 2; mod <= 8; ++mod) {
+ char *q = p;
+ if (is_numeric) {
+ appendId(q);
+ ASSERT(q <= ctx.bufferEnd - 2);
+ *q++ = ';';
+ *q++ = '0' + mod;
+ expandNumericEncodingSuffix(
+ ctx, q, extraKeyState | keyStateForMod(mod));
+ } else {
+ ASSERT(q <= ctx.bufferEnd - 4);
+ *q++ = '1';
+ *q++ = ';';
+ *q++ = '0' + mod;
+ *q++ = ctx.e.id;
+ setEncoding(ctx, q, extraKeyState | keyStateForMod(mod));
+ }
+ }
+ }
+}
+
+template <bool is_numeric>
+static inline void expandEncoding(const ExpandContext &ctx) {
+ if (ctx.e.alt_prefix_allowed) {
+ // For better or for worse, this code expands all of:
+ // * ESC [ <key> -- <key>
+ // * ESC ESC [ <key> -- Alt-<key>
+ // * ESC [ 1 ; 3 <key> -- Alt-<key>
+ // * ESC ESC [ 1 ; 3 <key> -- Alt-<key> specified twice
+ // I suspect no terminal actually emits the last one (i.e. specifying
+ // the Alt modifier using both methods), but I have seen a terminal
+ // that emitted a prefix ESC for Alt and a non-Alt modifier.
+ char *p = ctx.buffer;
+ ASSERT(p <= ctx.bufferEnd - 1);
+ *p++ = '\x1b';
+ expandEncodingAfterAltPrefix<is_numeric>(ctx, p, LEFT_ALT_PRESSED);
+ }
+ expandEncodingAfterAltPrefix<is_numeric>(ctx, ctx.buffer, 0);
+}
+
+template <bool is_numeric, size_t N>
+static void addEscapes(InputMap &inputMap, const EscapeEncoding (&encodings)[N]) {
+ char buffer[32];
+ for (size_t i = 0; i < DIM(encodings); ++i) {
+ ExpandContext ctx = {
+ inputMap, encodings[i],
+ buffer, buffer + sizeof(buffer)
+ };
+ expandEncoding<is_numeric>(ctx);
+ }
+}
+
+} // anonymous namespace
+
+void addDefaultEntriesToInputMap(InputMap &inputMap) {
+ addEscapes<false>(inputMap, escapeLetterEncodings);
+ addEscapes<true>(inputMap, escapeNumericEncodings);
+ addSimpleEntries(inputMap);
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.h b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.h
new file mode 100644
index 0000000000..c4b9083678
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/DefaultInputMap.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef DEFAULT_INPUT_MAP_H
+#define DEFAULT_INPUT_MAP_H
+
+class InputMap;
+
+void addDefaultEntriesToInputMap(InputMap &inputMap);
+
+#endif // DEFAULT_INPUT_MAP_H
diff --git a/src/libs/3rdparty/winpty/src/agent/DsrSender.h b/src/libs/3rdparty/winpty/src/agent/DsrSender.h
new file mode 100644
index 0000000000..1ec0a97d2e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/DsrSender.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef DSRSENDER_H
+#define DSRSENDER_H
+
+class DsrSender
+{
+public:
+ virtual void sendDsr() = 0;
+};
+
+#endif // DSRSENDER_H
diff --git a/src/libs/3rdparty/winpty/src/agent/EventLoop.cc b/src/libs/3rdparty/winpty/src/agent/EventLoop.cc
new file mode 100644
index 0000000000..ba5cf18cc8
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/EventLoop.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "EventLoop.h"
+
+#include <algorithm>
+
+#include "NamedPipe.h"
+#include "../shared/DebugClient.h"
+#include "../shared/WinptyAssert.h"
+
+EventLoop::~EventLoop() {
+ for (NamedPipe *pipe : m_pipes) {
+ delete pipe;
+ }
+ m_pipes.clear();
+}
+
+// Enter the event loop. Runs until the I/O or timeout handler calls exit().
+void EventLoop::run()
+{
+ std::vector<HANDLE> waitHandles;
+ DWORD lastTime = GetTickCount();
+ while (!m_exiting) {
+ bool didSomething = false;
+
+ // Attempt to make progress with the pipes.
+ waitHandles.clear();
+ for (size_t i = 0; i < m_pipes.size(); ++i) {
+ if (m_pipes[i]->serviceIo(&waitHandles)) {
+ onPipeIo(*m_pipes[i]);
+ didSomething = true;
+ }
+ }
+
+ // Call the timeout if enough time has elapsed.
+ if (m_pollInterval > 0) {
+ int elapsed = GetTickCount() - lastTime;
+ if (elapsed >= m_pollInterval) {
+ onPollTimeout();
+ lastTime = GetTickCount();
+ didSomething = true;
+ }
+ }
+
+ if (didSomething)
+ continue;
+
+ // If there's nothing to do, wait.
+ DWORD timeout = INFINITE;
+ if (m_pollInterval > 0)
+ timeout = std::max(0, (int)(lastTime + m_pollInterval - GetTickCount()));
+ if (waitHandles.size() == 0) {
+ ASSERT(timeout != INFINITE);
+ if (timeout > 0)
+ Sleep(timeout);
+ } else {
+ DWORD result = WaitForMultipleObjects(waitHandles.size(),
+ waitHandles.data(),
+ FALSE,
+ timeout);
+ ASSERT(result != WAIT_FAILED);
+ }
+ }
+}
+
+NamedPipe &EventLoop::createNamedPipe()
+{
+ NamedPipe *ret = new NamedPipe();
+ m_pipes.push_back(ret);
+ return *ret;
+}
+
+void EventLoop::setPollInterval(int ms)
+{
+ m_pollInterval = ms;
+}
+
+void EventLoop::shutdown()
+{
+ m_exiting = true;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/EventLoop.h b/src/libs/3rdparty/winpty/src/agent/EventLoop.h
new file mode 100644
index 0000000000..eddb0f6267
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/EventLoop.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef EVENTLOOP_H
+#define EVENTLOOP_H
+
+#include <vector>
+
+class NamedPipe;
+
+class EventLoop
+{
+public:
+ virtual ~EventLoop();
+ void run();
+
+protected:
+ NamedPipe &createNamedPipe();
+ void setPollInterval(int ms);
+ void shutdown();
+ virtual void onPollTimeout() {}
+ virtual void onPipeIo(NamedPipe &namedPipe) {}
+
+private:
+ bool m_exiting = false;
+ std::vector<NamedPipe*> m_pipes;
+ int m_pollInterval = 0;
+};
+
+#endif // EVENTLOOP_H
diff --git a/src/libs/3rdparty/winpty/src/agent/InputMap.cc b/src/libs/3rdparty/winpty/src/agent/InputMap.cc
new file mode 100644
index 0000000000..b1fbfc2e30
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/InputMap.cc
@@ -0,0 +1,246 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "InputMap.h"
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "DebugShowInput.h"
+#include "SimplePool.h"
+#include "../shared/DebugClient.h"
+#include "../shared/UnixCtrlChars.h"
+#include "../shared/WinptyAssert.h"
+#include "../shared/winpty_snprintf.h"
+
+namespace {
+
+static const char *getVirtualKeyString(int virtualKey)
+{
+ switch (virtualKey) {
+#define WINPTY_GVKS_KEY(x) case VK_##x: return #x;
+ WINPTY_GVKS_KEY(RBUTTON) WINPTY_GVKS_KEY(F9)
+ WINPTY_GVKS_KEY(CANCEL) WINPTY_GVKS_KEY(F10)
+ WINPTY_GVKS_KEY(MBUTTON) WINPTY_GVKS_KEY(F11)
+ WINPTY_GVKS_KEY(XBUTTON1) WINPTY_GVKS_KEY(F12)
+ WINPTY_GVKS_KEY(XBUTTON2) WINPTY_GVKS_KEY(F13)
+ WINPTY_GVKS_KEY(BACK) WINPTY_GVKS_KEY(F14)
+ WINPTY_GVKS_KEY(TAB) WINPTY_GVKS_KEY(F15)
+ WINPTY_GVKS_KEY(CLEAR) WINPTY_GVKS_KEY(F16)
+ WINPTY_GVKS_KEY(RETURN) WINPTY_GVKS_KEY(F17)
+ WINPTY_GVKS_KEY(SHIFT) WINPTY_GVKS_KEY(F18)
+ WINPTY_GVKS_KEY(CONTROL) WINPTY_GVKS_KEY(F19)
+ WINPTY_GVKS_KEY(MENU) WINPTY_GVKS_KEY(F20)
+ WINPTY_GVKS_KEY(PAUSE) WINPTY_GVKS_KEY(F21)
+ WINPTY_GVKS_KEY(CAPITAL) WINPTY_GVKS_KEY(F22)
+ WINPTY_GVKS_KEY(HANGUL) WINPTY_GVKS_KEY(F23)
+ WINPTY_GVKS_KEY(JUNJA) WINPTY_GVKS_KEY(F24)
+ WINPTY_GVKS_KEY(FINAL) WINPTY_GVKS_KEY(NUMLOCK)
+ WINPTY_GVKS_KEY(KANJI) WINPTY_GVKS_KEY(SCROLL)
+ WINPTY_GVKS_KEY(ESCAPE) WINPTY_GVKS_KEY(LSHIFT)
+ WINPTY_GVKS_KEY(CONVERT) WINPTY_GVKS_KEY(RSHIFT)
+ WINPTY_GVKS_KEY(NONCONVERT) WINPTY_GVKS_KEY(LCONTROL)
+ WINPTY_GVKS_KEY(ACCEPT) WINPTY_GVKS_KEY(RCONTROL)
+ WINPTY_GVKS_KEY(MODECHANGE) WINPTY_GVKS_KEY(LMENU)
+ WINPTY_GVKS_KEY(SPACE) WINPTY_GVKS_KEY(RMENU)
+ WINPTY_GVKS_KEY(PRIOR) WINPTY_GVKS_KEY(BROWSER_BACK)
+ WINPTY_GVKS_KEY(NEXT) WINPTY_GVKS_KEY(BROWSER_FORWARD)
+ WINPTY_GVKS_KEY(END) WINPTY_GVKS_KEY(BROWSER_REFRESH)
+ WINPTY_GVKS_KEY(HOME) WINPTY_GVKS_KEY(BROWSER_STOP)
+ WINPTY_GVKS_KEY(LEFT) WINPTY_GVKS_KEY(BROWSER_SEARCH)
+ WINPTY_GVKS_KEY(UP) WINPTY_GVKS_KEY(BROWSER_FAVORITES)
+ WINPTY_GVKS_KEY(RIGHT) WINPTY_GVKS_KEY(BROWSER_HOME)
+ WINPTY_GVKS_KEY(DOWN) WINPTY_GVKS_KEY(VOLUME_MUTE)
+ WINPTY_GVKS_KEY(SELECT) WINPTY_GVKS_KEY(VOLUME_DOWN)
+ WINPTY_GVKS_KEY(PRINT) WINPTY_GVKS_KEY(VOLUME_UP)
+ WINPTY_GVKS_KEY(EXECUTE) WINPTY_GVKS_KEY(MEDIA_NEXT_TRACK)
+ WINPTY_GVKS_KEY(SNAPSHOT) WINPTY_GVKS_KEY(MEDIA_PREV_TRACK)
+ WINPTY_GVKS_KEY(INSERT) WINPTY_GVKS_KEY(MEDIA_STOP)
+ WINPTY_GVKS_KEY(DELETE) WINPTY_GVKS_KEY(MEDIA_PLAY_PAUSE)
+ WINPTY_GVKS_KEY(HELP) WINPTY_GVKS_KEY(LAUNCH_MAIL)
+ WINPTY_GVKS_KEY(LWIN) WINPTY_GVKS_KEY(LAUNCH_MEDIA_SELECT)
+ WINPTY_GVKS_KEY(RWIN) WINPTY_GVKS_KEY(LAUNCH_APP1)
+ WINPTY_GVKS_KEY(APPS) WINPTY_GVKS_KEY(LAUNCH_APP2)
+ WINPTY_GVKS_KEY(SLEEP) WINPTY_GVKS_KEY(OEM_1)
+ WINPTY_GVKS_KEY(NUMPAD0) WINPTY_GVKS_KEY(OEM_PLUS)
+ WINPTY_GVKS_KEY(NUMPAD1) WINPTY_GVKS_KEY(OEM_COMMA)
+ WINPTY_GVKS_KEY(NUMPAD2) WINPTY_GVKS_KEY(OEM_MINUS)
+ WINPTY_GVKS_KEY(NUMPAD3) WINPTY_GVKS_KEY(OEM_PERIOD)
+ WINPTY_GVKS_KEY(NUMPAD4) WINPTY_GVKS_KEY(OEM_2)
+ WINPTY_GVKS_KEY(NUMPAD5) WINPTY_GVKS_KEY(OEM_3)
+ WINPTY_GVKS_KEY(NUMPAD6) WINPTY_GVKS_KEY(OEM_4)
+ WINPTY_GVKS_KEY(NUMPAD7) WINPTY_GVKS_KEY(OEM_5)
+ WINPTY_GVKS_KEY(NUMPAD8) WINPTY_GVKS_KEY(OEM_6)
+ WINPTY_GVKS_KEY(NUMPAD9) WINPTY_GVKS_KEY(OEM_7)
+ WINPTY_GVKS_KEY(MULTIPLY) WINPTY_GVKS_KEY(OEM_8)
+ WINPTY_GVKS_KEY(ADD) WINPTY_GVKS_KEY(OEM_102)
+ WINPTY_GVKS_KEY(SEPARATOR) WINPTY_GVKS_KEY(PROCESSKEY)
+ WINPTY_GVKS_KEY(SUBTRACT) WINPTY_GVKS_KEY(PACKET)
+ WINPTY_GVKS_KEY(DECIMAL) WINPTY_GVKS_KEY(ATTN)
+ WINPTY_GVKS_KEY(DIVIDE) WINPTY_GVKS_KEY(CRSEL)
+ WINPTY_GVKS_KEY(F1) WINPTY_GVKS_KEY(EXSEL)
+ WINPTY_GVKS_KEY(F2) WINPTY_GVKS_KEY(EREOF)
+ WINPTY_GVKS_KEY(F3) WINPTY_GVKS_KEY(PLAY)
+ WINPTY_GVKS_KEY(F4) WINPTY_GVKS_KEY(ZOOM)
+ WINPTY_GVKS_KEY(F5) WINPTY_GVKS_KEY(NONAME)
+ WINPTY_GVKS_KEY(F6) WINPTY_GVKS_KEY(PA1)
+ WINPTY_GVKS_KEY(F7) WINPTY_GVKS_KEY(OEM_CLEAR)
+ WINPTY_GVKS_KEY(F8)
+#undef WINPTY_GVKS_KEY
+ default: return NULL;
+ }
+}
+
+} // anonymous namespace
+
+std::string InputMap::Key::toString() const {
+ std::string ret;
+ ret += controlKeyStatePrefix(keyState);
+ char buf[256];
+ const char *vkString = getVirtualKeyString(virtualKey);
+ if (vkString != NULL) {
+ ret += vkString;
+ } else if ((virtualKey >= 'A' && virtualKey <= 'Z') ||
+ (virtualKey >= '0' && virtualKey <= '9')) {
+ ret += static_cast<char>(virtualKey);
+ } else {
+ winpty_snprintf(buf, "%#x", virtualKey);
+ ret += buf;
+ }
+ if (unicodeChar >= 32 && unicodeChar <= 126) {
+ winpty_snprintf(buf, " ch='%c'",
+ static_cast<char>(unicodeChar));
+ } else {
+ winpty_snprintf(buf, " ch=%#x",
+ static_cast<unsigned int>(unicodeChar));
+ }
+ ret += buf;
+ return ret;
+}
+
+void InputMap::set(const char *encoding, int encodingLen, const Key &key) {
+ ASSERT(encodingLen > 0);
+ setHelper(m_root, encoding, encodingLen, key);
+}
+
+void InputMap::setHelper(Node &node, const char *encoding, int encodingLen, const Key &key) {
+ if (encodingLen == 0) {
+ node.key = key;
+ } else {
+ setHelper(getOrCreateChild(node, encoding[0]), encoding + 1, encodingLen - 1, key);
+ }
+}
+
+InputMap::Node &InputMap::getOrCreateChild(Node &node, unsigned char ch) {
+ Node *ret = getChild(node, ch);
+ if (ret != NULL) {
+ return *ret;
+ }
+ if (node.childCount < Node::kTinyCount) {
+ // Maintain sorted order for the sake of the InputMap dumping.
+ int insertIndex = node.childCount;
+ for (int i = 0; i < node.childCount; ++i) {
+ if (ch < node.u.tiny.values[i]) {
+ insertIndex = i;
+ break;
+ }
+ }
+ for (int j = node.childCount; j > insertIndex; --j) {
+ node.u.tiny.values[j] = node.u.tiny.values[j - 1];
+ node.u.tiny.children[j] = node.u.tiny.children[j - 1];
+ }
+ node.u.tiny.values[insertIndex] = ch;
+ node.u.tiny.children[insertIndex] = ret = m_nodePool.alloc();
+ ++node.childCount;
+ return *ret;
+ }
+ if (node.childCount == Node::kTinyCount) {
+ Branch *branch = m_branchPool.alloc();
+ for (int i = 0; i < node.childCount; ++i) {
+ branch->children[node.u.tiny.values[i]] = node.u.tiny.children[i];
+ }
+ node.u.branch = branch;
+ }
+ node.u.branch->children[ch] = ret = m_nodePool.alloc();
+ ++node.childCount;
+ return *ret;
+}
+
+// Find the longest matching key and node.
+int InputMap::lookupKey(const char *input, int inputSize,
+ Key &keyOut, bool &incompleteOut) const {
+ keyOut = kKeyZero;
+ incompleteOut = false;
+
+ const Node *node = &m_root;
+ InputMap::Key longestMatch = kKeyZero;
+ int longestMatchLen = 0;
+
+ for (int i = 0; i < inputSize; ++i) {
+ unsigned char ch = input[i];
+ node = getChild(*node, ch);
+ if (node == NULL) {
+ keyOut = longestMatch;
+ return longestMatchLen;
+ } else if (node->hasKey()) {
+ longestMatchLen = i + 1;
+ longestMatch = node->key;
+ }
+ }
+ keyOut = longestMatch;
+ incompleteOut = node->childCount > 0;
+ return longestMatchLen;
+}
+
+void InputMap::dumpInputMap() const {
+ std::string encoding;
+ dumpInputMapHelper(m_root, encoding);
+}
+
+void InputMap::dumpInputMapHelper(
+ const Node &node, std::string &encoding) const {
+ if (node.hasKey()) {
+ trace("%s -> %s",
+ encoding.c_str(),
+ node.key.toString().c_str());
+ }
+ for (int i = 0; i < 256; ++i) {
+ const Node *child = getChild(node, i);
+ if (child != NULL) {
+ size_t oldSize = encoding.size();
+ if (!encoding.empty()) {
+ encoding.push_back(' ');
+ }
+ char ctrlChar = decodeUnixCtrlChar(i);
+ if (ctrlChar != '\0') {
+ encoding.push_back('^');
+ encoding.push_back(static_cast<char>(ctrlChar));
+ } else if (i == ' ') {
+ encoding.append("' '");
+ } else {
+ encoding.push_back(static_cast<char>(i));
+ }
+ dumpInputMapHelper(*child, encoding);
+ encoding.resize(oldSize);
+ }
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/InputMap.h b/src/libs/3rdparty/winpty/src/agent/InputMap.h
new file mode 100644
index 0000000000..9a666c7976
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/InputMap.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef INPUT_MAP_H
+#define INPUT_MAP_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include "SimplePool.h"
+#include "../shared/WinptyAssert.h"
+
+class InputMap {
+public:
+ struct Key {
+ uint16_t virtualKey;
+ uint32_t unicodeChar;
+ uint16_t keyState;
+
+ std::string toString() const;
+ };
+
+private:
+ struct Node;
+
+ struct Branch {
+ Branch() {
+ memset(&children, 0, sizeof(children));
+ }
+
+ Node *children[256];
+ };
+
+ struct Node {
+ Node() : childCount(0) {
+ Key zeroKey = { 0, 0, 0 };
+ key = zeroKey;
+ }
+
+ Key key;
+ int childCount;
+ enum { kTinyCount = 8 };
+ union {
+ Branch *branch;
+ struct {
+ unsigned char values[kTinyCount];
+ Node *children[kTinyCount];
+ } tiny;
+ } u;
+
+ bool hasKey() const {
+ return key.virtualKey != 0 || key.unicodeChar != 0;
+ }
+ };
+
+private:
+ SimplePool<Node, 256> m_nodePool;
+ SimplePool<Branch, 8> m_branchPool;
+ Node m_root;
+
+public:
+ void set(const char *encoding, int encodingLen, const Key &key);
+ int lookupKey(const char *input, int inputSize,
+ Key &keyOut, bool &incompleteOut) const;
+ void dumpInputMap() const;
+
+private:
+ Node *getChild(Node &node, unsigned char ch) {
+ return const_cast<Node*>(getChild(static_cast<const Node&>(node), ch));
+ }
+
+ const Node *getChild(const Node &node, unsigned char ch) const {
+ if (node.childCount <= Node::kTinyCount) {
+ for (int i = 0; i < node.childCount; ++i) {
+ if (node.u.tiny.values[i] == ch) {
+ return node.u.tiny.children[i];
+ }
+ }
+ return NULL;
+ } else {
+ return node.u.branch->children[ch];
+ }
+ }
+
+ void setHelper(Node &node, const char *encoding, int encodingLen, const Key &key);
+ Node &getOrCreateChild(Node &node, unsigned char ch);
+ void dumpInputMapHelper(const Node &node, std::string &encoding) const;
+};
+
+const InputMap::Key kKeyZero = { 0, 0, 0 };
+
+void dumpInputMap(InputMap &inputMap);
+
+#endif // INPUT_MAP_H
diff --git a/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.cc b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.cc
new file mode 100644
index 0000000000..80ac640e48
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "LargeConsoleRead.h"
+
+#include <stdlib.h>
+
+#include "../shared/WindowsVersion.h"
+#include "Scraper.h"
+#include "Win32ConsoleBuffer.h"
+
+LargeConsoleReadBuffer::LargeConsoleReadBuffer() :
+ m_rect(0, 0, 0, 0), m_rectWidth(0)
+{
+}
+
+void largeConsoleRead(LargeConsoleReadBuffer &out,
+ Win32ConsoleBuffer &buffer,
+ const SmallRect &readArea,
+ WORD attributesMask) {
+ ASSERT(readArea.Left >= 0 &&
+ readArea.Top >= 0 &&
+ readArea.Right >= readArea.Left &&
+ readArea.Bottom >= readArea.Top &&
+ readArea.width() <= MAX_CONSOLE_WIDTH);
+ const size_t count = readArea.width() * readArea.height();
+ if (out.m_data.size() < count) {
+ out.m_data.resize(count);
+ }
+ out.m_rect = readArea;
+ out.m_rectWidth = readArea.width();
+
+ static const bool useLargeReads = isAtLeastWindows8();
+ if (useLargeReads) {
+ buffer.read(readArea, out.m_data.data());
+ } else {
+ const int maxReadLines = std::max(1, MAX_CONSOLE_WIDTH / readArea.width());
+ int curLine = readArea.Top;
+ while (curLine <= readArea.Bottom) {
+ const SmallRect subReadArea(
+ readArea.Left,
+ curLine,
+ readArea.width(),
+ std::min(maxReadLines, readArea.Bottom + 1 - curLine));
+ buffer.read(subReadArea, out.lineDataMut(curLine));
+ curLine = subReadArea.Bottom + 1;
+ }
+ }
+ if (attributesMask != static_cast<WORD>(~0)) {
+ for (size_t i = 0; i < count; ++i) {
+ out.m_data[i].Attributes &= attributesMask;
+ }
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.h b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.h
new file mode 100644
index 0000000000..1bcf2c0232
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/LargeConsoleRead.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LARGE_CONSOLE_READ_H
+#define LARGE_CONSOLE_READ_H
+
+#include <windows.h>
+#include <stdlib.h>
+
+#include <vector>
+
+#include "SmallRect.h"
+#include "../shared/DebugClient.h"
+#include "../shared/WinptyAssert.h"
+
+class Win32ConsoleBuffer;
+
+class LargeConsoleReadBuffer {
+public:
+ LargeConsoleReadBuffer();
+ const SmallRect &rect() const { return m_rect; }
+ const CHAR_INFO *lineData(int line) const {
+ validateLineNumber(line);
+ return &m_data[(line - m_rect.Top) * m_rectWidth];
+ }
+
+private:
+ CHAR_INFO *lineDataMut(int line) {
+ validateLineNumber(line);
+ return &m_data[(line - m_rect.Top) * m_rectWidth];
+ }
+
+ void validateLineNumber(int line) const {
+ if (line < m_rect.Top || line > m_rect.Bottom) {
+ trace("Fatal error: LargeConsoleReadBuffer: invalid line %d for "
+ "read rect %s", line, m_rect.toString().c_str());
+ abort();
+ }
+ }
+
+ SmallRect m_rect;
+ int m_rectWidth;
+ std::vector<CHAR_INFO> m_data;
+
+ friend void largeConsoleRead(LargeConsoleReadBuffer &out,
+ Win32ConsoleBuffer &buffer,
+ const SmallRect &readArea,
+ WORD attributesMask);
+};
+
+#endif // LARGE_CONSOLE_READ_H
diff --git a/src/libs/3rdparty/winpty/src/agent/NamedPipe.cc b/src/libs/3rdparty/winpty/src/agent/NamedPipe.cc
new file mode 100644
index 0000000000..64044e6e5d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/NamedPipe.cc
@@ -0,0 +1,378 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "EventLoop.h"
+#include "NamedPipe.h"
+#include "../shared/DebugClient.h"
+#include "../shared/StringUtil.h"
+#include "../shared/WindowsSecurity.h"
+#include "../shared/WinptyAssert.h"
+
+// Returns true if anything happens (data received, data sent, pipe error).
+bool NamedPipe::serviceIo(std::vector<HANDLE> *waitHandles)
+{
+ bool justConnected = false;
+ const auto kError = ServiceResult::Error;
+ const auto kProgress = ServiceResult::Progress;
+ const auto kNoProgress = ServiceResult::NoProgress;
+ if (m_handle == NULL) {
+ return false;
+ }
+ if (m_connectEvent.get() != nullptr) {
+ // We're still connecting this server pipe. Check whether the pipe is
+ // now connected. If it isn't, add the pipe to the list of handles to
+ // wait on.
+ DWORD actual = 0;
+ BOOL success =
+ GetOverlappedResult(m_handle, &m_connectOver, &actual, FALSE);
+ if (!success && GetLastError() == ERROR_PIPE_CONNECTED) {
+ // I'm not sure this can happen, but it's easy to handle if it
+ // does.
+ success = TRUE;
+ }
+ if (!success) {
+ ASSERT(GetLastError() == ERROR_IO_INCOMPLETE &&
+ "Pended ConnectNamedPipe call failed");
+ waitHandles->push_back(m_connectEvent.get());
+ } else {
+ TRACE("Server pipe [%s] connected",
+ utf8FromWide(m_name).c_str());
+ m_connectEvent.dispose();
+ startPipeWorkers();
+ justConnected = true;
+ }
+ }
+ const auto readProgress = m_inputWorker ? m_inputWorker->service() : kNoProgress;
+ const auto writeProgress = m_outputWorker ? m_outputWorker->service() : kNoProgress;
+ if (readProgress == kError || writeProgress == kError) {
+ closePipe();
+ return true;
+ }
+ if (m_inputWorker && m_inputWorker->getWaitEvent() != nullptr) {
+ waitHandles->push_back(m_inputWorker->getWaitEvent());
+ }
+ if (m_outputWorker && m_outputWorker->getWaitEvent() != nullptr) {
+ waitHandles->push_back(m_outputWorker->getWaitEvent());
+ }
+ return justConnected
+ || readProgress == kProgress
+ || writeProgress == kProgress;
+}
+
+// manual reset, initially unset
+static OwnedHandle createEvent() {
+ HANDLE ret = CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ ASSERT(ret != nullptr && "CreateEventW failed");
+ return OwnedHandle(ret);
+}
+
+NamedPipe::IoWorker::IoWorker(NamedPipe &namedPipe) :
+ m_namedPipe(namedPipe),
+ m_event(createEvent())
+{
+}
+
+NamedPipe::ServiceResult NamedPipe::IoWorker::service()
+{
+ ServiceResult progress = ServiceResult::NoProgress;
+ if (m_pending) {
+ DWORD actual = 0;
+ BOOL ret = GetOverlappedResult(m_namedPipe.m_handle, &m_over, &actual, FALSE);
+ if (!ret) {
+ if (GetLastError() == ERROR_IO_INCOMPLETE) {
+ // There is a pending I/O.
+ return progress;
+ } else {
+ // Pipe error.
+ return ServiceResult::Error;
+ }
+ }
+ ResetEvent(m_event.get());
+ m_pending = false;
+ completeIo(actual);
+ m_currentIoSize = 0;
+ progress = ServiceResult::Progress;
+ }
+ DWORD nextSize = 0;
+ bool isRead = false;
+ while (shouldIssueIo(&nextSize, &isRead)) {
+ m_currentIoSize = nextSize;
+ DWORD actual = 0;
+ memset(&m_over, 0, sizeof(m_over));
+ m_over.hEvent = m_event.get();
+ BOOL ret = isRead
+ ? ReadFile(m_namedPipe.m_handle, m_buffer, nextSize, &actual, &m_over)
+ : WriteFile(m_namedPipe.m_handle, m_buffer, nextSize, &actual, &m_over);
+ if (!ret) {
+ if (GetLastError() == ERROR_IO_PENDING) {
+ // There is a pending I/O.
+ m_pending = true;
+ return progress;
+ } else {
+ // Pipe error.
+ return ServiceResult::Error;
+ }
+ }
+ ResetEvent(m_event.get());
+ completeIo(actual);
+ m_currentIoSize = 0;
+ progress = ServiceResult::Progress;
+ }
+ return progress;
+}
+
+// This function is called after CancelIo has returned. We need to block until
+// the I/O operations have completed, which should happen very quickly.
+// https://blogs.msdn.microsoft.com/oldnewthing/20110202-00/?p=11613
+void NamedPipe::IoWorker::waitForCanceledIo()
+{
+ if (m_pending) {
+ DWORD actual = 0;
+ GetOverlappedResult(m_namedPipe.m_handle, &m_over, &actual, TRUE);
+ m_pending = false;
+ }
+}
+
+HANDLE NamedPipe::IoWorker::getWaitEvent()
+{
+ return m_pending ? m_event.get() : NULL;
+}
+
+void NamedPipe::InputWorker::completeIo(DWORD size)
+{
+ m_namedPipe.m_inQueue.append(m_buffer, size);
+}
+
+bool NamedPipe::InputWorker::shouldIssueIo(DWORD *size, bool *isRead)
+{
+ *isRead = true;
+ ASSERT(!m_namedPipe.isConnecting());
+ if (m_namedPipe.isClosed()) {
+ return false;
+ } else if (m_namedPipe.m_inQueue.size() < m_namedPipe.readBufferSize()) {
+ *size = kIoSize;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void NamedPipe::OutputWorker::completeIo(DWORD size)
+{
+ ASSERT(size == m_currentIoSize);
+}
+
+bool NamedPipe::OutputWorker::shouldIssueIo(DWORD *size, bool *isRead)
+{
+ *isRead = false;
+ if (!m_namedPipe.m_outQueue.empty()) {
+ auto &out = m_namedPipe.m_outQueue;
+ const DWORD writeSize = std::min<size_t>(out.size(), kIoSize);
+ std::copy(&out[0], &out[writeSize], m_buffer);
+ out.erase(0, writeSize);
+ *size = writeSize;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+DWORD NamedPipe::OutputWorker::getPendingIoSize()
+{
+ return m_pending ? m_currentIoSize : 0;
+}
+
+void NamedPipe::openServerPipe(LPCWSTR pipeName, OpenMode::t openMode,
+ int outBufferSize, int inBufferSize) {
+ ASSERT(isClosed());
+ ASSERT((openMode & OpenMode::Duplex) != 0);
+ const DWORD winOpenMode =
+ ((openMode & OpenMode::Reading) ? PIPE_ACCESS_INBOUND : 0)
+ | ((openMode & OpenMode::Writing) ? PIPE_ACCESS_OUTBOUND : 0)
+ | FILE_FLAG_FIRST_PIPE_INSTANCE
+ | FILE_FLAG_OVERLAPPED;
+ const auto sd = createPipeSecurityDescriptorOwnerFullControl();
+ ASSERT(sd && "error creating data pipe SECURITY_DESCRIPTOR");
+ SECURITY_ATTRIBUTES sa = {};
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = sd.get();
+ HANDLE handle = CreateNamedPipeW(
+ pipeName,
+ /*dwOpenMode=*/winOpenMode,
+ /*dwPipeMode=*/rejectRemoteClientsPipeFlag(),
+ /*nMaxInstances=*/1,
+ /*nOutBufferSize=*/outBufferSize,
+ /*nInBufferSize=*/inBufferSize,
+ /*nDefaultTimeOut=*/30000,
+ &sa);
+ TRACE("opened server pipe [%s], handle == %p",
+ utf8FromWide(pipeName).c_str(), handle);
+ ASSERT(handle != INVALID_HANDLE_VALUE && "Could not open server pipe");
+ m_name = pipeName;
+ m_handle = handle;
+ m_openMode = openMode;
+
+ // Start an asynchronous connection attempt.
+ m_connectEvent = createEvent();
+ memset(&m_connectOver, 0, sizeof(m_connectOver));
+ m_connectOver.hEvent = m_connectEvent.get();
+ BOOL success = ConnectNamedPipe(m_handle, &m_connectOver);
+ const auto err = GetLastError();
+ if (!success && err == ERROR_PIPE_CONNECTED) {
+ success = TRUE;
+ }
+ if (success) {
+ TRACE("Server pipe [%s] connected", utf8FromWide(pipeName).c_str());
+ m_connectEvent.dispose();
+ startPipeWorkers();
+ } else if (err != ERROR_IO_PENDING) {
+ ASSERT(false && "ConnectNamedPipe call failed");
+ }
+}
+
+void NamedPipe::connectToServer(LPCWSTR pipeName, OpenMode::t openMode)
+{
+ ASSERT(isClosed());
+ ASSERT((openMode & OpenMode::Duplex) != 0);
+ HANDLE handle = CreateFileW(
+ pipeName,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | FILE_FLAG_OVERLAPPED,
+ NULL);
+ TRACE("connected to [%s], handle == %p",
+ utf8FromWide(pipeName).c_str(), handle);
+ ASSERT(handle != INVALID_HANDLE_VALUE && "Could not connect to pipe");
+ m_name = pipeName;
+ m_handle = handle;
+ m_openMode = openMode;
+ startPipeWorkers();
+}
+
+void NamedPipe::startPipeWorkers()
+{
+ if (m_openMode & OpenMode::Reading) {
+ m_inputWorker.reset(new InputWorker(*this));
+ }
+ if (m_openMode & OpenMode::Writing) {
+ m_outputWorker.reset(new OutputWorker(*this));
+ }
+}
+
+size_t NamedPipe::bytesToSend()
+{
+ ASSERT(m_openMode & OpenMode::Writing);
+ auto ret = m_outQueue.size();
+ if (m_outputWorker != NULL) {
+ ret += m_outputWorker->getPendingIoSize();
+ }
+ return ret;
+}
+
+void NamedPipe::write(const void *data, size_t size)
+{
+ ASSERT(m_openMode & OpenMode::Writing);
+ m_outQueue.append(reinterpret_cast<const char*>(data), size);
+}
+
+void NamedPipe::write(const char *text)
+{
+ write(text, strlen(text));
+}
+
+size_t NamedPipe::readBufferSize()
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ return m_readBufferSize;
+}
+
+void NamedPipe::setReadBufferSize(size_t size)
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ m_readBufferSize = size;
+}
+
+size_t NamedPipe::bytesAvailable()
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ return m_inQueue.size();
+}
+
+size_t NamedPipe::peek(void *data, size_t size)
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ const auto out = reinterpret_cast<char*>(data);
+ const size_t ret = std::min(size, m_inQueue.size());
+ std::copy(&m_inQueue[0], &m_inQueue[ret], out);
+ return ret;
+}
+
+size_t NamedPipe::read(void *data, size_t size)
+{
+ size_t ret = peek(data, size);
+ m_inQueue.erase(0, ret);
+ return ret;
+}
+
+std::string NamedPipe::readToString(size_t size)
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ size_t retSize = std::min(size, m_inQueue.size());
+ std::string ret = m_inQueue.substr(0, retSize);
+ m_inQueue.erase(0, retSize);
+ return ret;
+}
+
+std::string NamedPipe::readAllToString()
+{
+ ASSERT(m_openMode & OpenMode::Reading);
+ std::string ret = m_inQueue;
+ m_inQueue.clear();
+ return ret;
+}
+
+void NamedPipe::closePipe()
+{
+ if (m_handle == NULL) {
+ return;
+ }
+ CancelIo(m_handle);
+ if (m_connectEvent.get() != nullptr) {
+ DWORD actual = 0;
+ GetOverlappedResult(m_handle, &m_connectOver, &actual, TRUE);
+ m_connectEvent.dispose();
+ }
+ if (m_inputWorker) {
+ m_inputWorker->waitForCanceledIo();
+ m_inputWorker.reset();
+ }
+ if (m_outputWorker) {
+ m_outputWorker->waitForCanceledIo();
+ m_outputWorker.reset();
+ }
+ CloseHandle(m_handle);
+ m_handle = NULL;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/NamedPipe.h b/src/libs/3rdparty/winpty/src/agent/NamedPipe.h
new file mode 100644
index 0000000000..0a4d8b0c75
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/NamedPipe.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef NAMEDPIPE_H
+#define NAMEDPIPE_H
+
+#include <windows.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "../shared/OwnedHandle.h"
+
+class EventLoop;
+
+class NamedPipe
+{
+private:
+ // The EventLoop uses these private members.
+ friend class EventLoop;
+ NamedPipe() {}
+ ~NamedPipe() { closePipe(); }
+ bool serviceIo(std::vector<HANDLE> *waitHandles);
+ void startPipeWorkers();
+
+ enum class ServiceResult { NoProgress, Error, Progress };
+
+private:
+ class IoWorker
+ {
+ public:
+ IoWorker(NamedPipe &namedPipe);
+ virtual ~IoWorker() {}
+ ServiceResult service();
+ void waitForCanceledIo();
+ HANDLE getWaitEvent();
+ protected:
+ NamedPipe &m_namedPipe;
+ bool m_pending = false;
+ DWORD m_currentIoSize = 0;
+ OwnedHandle m_event;
+ OVERLAPPED m_over = {};
+ enum { kIoSize = 64 * 1024 };
+ char m_buffer[kIoSize];
+ virtual void completeIo(DWORD size) = 0;
+ virtual bool shouldIssueIo(DWORD *size, bool *isRead) = 0;
+ };
+
+ class InputWorker : public IoWorker
+ {
+ public:
+ InputWorker(NamedPipe &namedPipe) : IoWorker(namedPipe) {}
+ protected:
+ virtual void completeIo(DWORD size) override;
+ virtual bool shouldIssueIo(DWORD *size, bool *isRead) override;
+ };
+
+ class OutputWorker : public IoWorker
+ {
+ public:
+ OutputWorker(NamedPipe &namedPipe) : IoWorker(namedPipe) {}
+ DWORD getPendingIoSize();
+ protected:
+ virtual void completeIo(DWORD size) override;
+ virtual bool shouldIssueIo(DWORD *size, bool *isRead) override;
+ };
+
+public:
+ struct OpenMode {
+ typedef int t;
+ enum { None = 0, Reading = 1, Writing = 2, Duplex = 3 };
+ };
+
+ std::wstring name() const { return m_name; }
+ void openServerPipe(LPCWSTR pipeName, OpenMode::t openMode,
+ int outBufferSize, int inBufferSize);
+ void connectToServer(LPCWSTR pipeName, OpenMode::t openMode);
+ size_t bytesToSend();
+ void write(const void *data, size_t size);
+ void write(const char *text);
+ size_t readBufferSize();
+ void setReadBufferSize(size_t size);
+ size_t bytesAvailable();
+ size_t peek(void *data, size_t size);
+ size_t read(void *data, size_t size);
+ std::string readToString(size_t size);
+ std::string readAllToString();
+ void closePipe();
+ bool isClosed() { return m_handle == nullptr; }
+ bool isConnected() { return !isClosed() && !isConnecting(); }
+ bool isConnecting() { return m_connectEvent.get() != nullptr; }
+
+private:
+ // Input/output buffers
+ std::wstring m_name;
+ OVERLAPPED m_connectOver = {};
+ OwnedHandle m_connectEvent;
+ OpenMode::t m_openMode = OpenMode::None;
+ size_t m_readBufferSize = 64 * 1024;
+ std::string m_inQueue;
+ std::string m_outQueue;
+ HANDLE m_handle = nullptr;
+ std::unique_ptr<InputWorker> m_inputWorker;
+ std::unique_ptr<OutputWorker> m_outputWorker;
+};
+
+#endif // NAMEDPIPE_H
diff --git a/src/libs/3rdparty/winpty/src/agent/Scraper.cc b/src/libs/3rdparty/winpty/src/agent/Scraper.cc
new file mode 100644
index 0000000000..21f9c67104
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Scraper.cc
@@ -0,0 +1,699 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Scraper.h"
+
+#include <windows.h>
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "../shared/WinptyAssert.h"
+#include "../shared/winpty_snprintf.h"
+
+#include "ConsoleFont.h"
+#include "Win32Console.h"
+#include "Win32ConsoleBuffer.h"
+
+namespace {
+
+template <typename T>
+T constrained(T min, T val, T max) {
+ ASSERT(min <= max);
+ return std::min(std::max(min, val), max);
+}
+
+} // anonymous namespace
+
+Scraper::Scraper(
+ Win32Console &console,
+ Win32ConsoleBuffer &buffer,
+ std::unique_ptr<Terminal> terminal,
+ Coord initialSize) :
+ m_console(console),
+ m_terminal(std::move(terminal)),
+ m_ptySize(initialSize)
+{
+ m_consoleBuffer = &buffer;
+
+ resetConsoleTracking(Terminal::OmitClear, buffer.windowRect().top());
+
+ m_bufferData.resize(BUFFER_LINE_COUNT);
+
+ // Setup the initial screen buffer and window size.
+ //
+ // Use SetConsoleWindowInfo to shrink the console window as much as
+ // possible -- to a 1x1 cell at the top-left. This call always succeeds.
+ // Prior to the new Windows 10 console, it also actually resizes the GUI
+ // window to 1x1 cell. Nevertheless, even though the GUI window can
+ // therefore be narrower than its minimum, calling
+ // SetConsoleScreenBufferSize with a 1x1 size still fails.
+ //
+ // While the small font intends to support large buffers, a user could
+ // still hit a limit imposed by their monitor width, so cap the new window
+ // size to GetLargestConsoleWindowSize().
+ setSmallFont(buffer.conout(), initialSize.X, m_console.isNewW10());
+ buffer.moveWindow(SmallRect(0, 0, 1, 1));
+ buffer.resizeBufferRange(Coord(initialSize.X, BUFFER_LINE_COUNT));
+ const auto largest = GetLargestConsoleWindowSize(buffer.conout());
+ buffer.moveWindow(SmallRect(
+ 0, 0,
+ std::min(initialSize.X, largest.X),
+ std::min(initialSize.Y, largest.Y)));
+ buffer.setCursorPosition(Coord(0, 0));
+
+ // For the sake of the color translation heuristic, set the console color
+ // to LtGray-on-Black.
+ buffer.setTextAttribute(Win32ConsoleBuffer::kDefaultAttributes);
+ buffer.clearAllLines(m_consoleBuffer->bufferInfo());
+
+ m_consoleBuffer = nullptr;
+}
+
+Scraper::~Scraper()
+{
+}
+
+// Whether or not the agent is frozen on entry, it will be frozen on exit.
+void Scraper::resizeWindow(Win32ConsoleBuffer &buffer,
+ Coord newSize,
+ ConsoleScreenBufferInfo &finalInfoOut)
+{
+ m_consoleBuffer = &buffer;
+ m_ptySize = newSize;
+ syncConsoleContentAndSize(true, finalInfoOut);
+ m_consoleBuffer = nullptr;
+}
+
+// This function may freeze the agent, but it will not unfreeze it.
+void Scraper::scrapeBuffer(Win32ConsoleBuffer &buffer,
+ ConsoleScreenBufferInfo &finalInfoOut)
+{
+ m_consoleBuffer = &buffer;
+ syncConsoleContentAndSize(false, finalInfoOut);
+ m_consoleBuffer = nullptr;
+}
+
+void Scraper::resetConsoleTracking(
+ Terminal::SendClearFlag sendClear, int64_t scrapedLineCount)
+{
+ for (ConsoleLine &line : m_bufferData) {
+ line.reset();
+ }
+ m_syncRow = -1;
+ m_scrapedLineCount = scrapedLineCount;
+ m_scrolledCount = 0;
+ m_maxBufferedLine = -1;
+ m_dirtyWindowTop = -1;
+ m_dirtyLineCount = 0;
+ m_terminal->reset(sendClear, m_scrapedLineCount);
+}
+
+// Detect window movement. If the window moves down (presumably as a
+// result of scrolling), then assume that all screen buffer lines down to
+// the bottom of the window are dirty.
+void Scraper::markEntireWindowDirty(const SmallRect &windowRect)
+{
+ m_dirtyLineCount = std::max(m_dirtyLineCount,
+ windowRect.top() + windowRect.height());
+}
+
+// Scan the screen buffer and advance the dirty line count when we find
+// non-empty lines.
+void Scraper::scanForDirtyLines(const SmallRect &windowRect)
+{
+ const int w = m_readBuffer.rect().width();
+ ASSERT(m_dirtyLineCount >= 1);
+ const CHAR_INFO *const prevLine =
+ m_readBuffer.lineData(m_dirtyLineCount - 1);
+ WORD prevLineAttr = prevLine[w - 1].Attributes;
+ const int stopLine = windowRect.top() + windowRect.height();
+
+ for (int line = m_dirtyLineCount; line < stopLine; ++line) {
+ const CHAR_INFO *lineData = m_readBuffer.lineData(line);
+ for (int col = 0; col < w; ++col) {
+ const WORD colAttr = lineData[col].Attributes;
+ if (lineData[col].Char.UnicodeChar != L' ' ||
+ colAttr != prevLineAttr) {
+ m_dirtyLineCount = line + 1;
+ break;
+ }
+ }
+ prevLineAttr = lineData[w - 1].Attributes;
+ }
+}
+
+// Clear lines in the line buffer. The `firstRow` parameter is in
+// screen-buffer coordinates.
+void Scraper::clearBufferLines(
+ const int firstRow,
+ const int count)
+{
+ ASSERT(!m_directMode);
+ for (int row = firstRow; row < firstRow + count; ++row) {
+ const int64_t bufLine = row + m_scrolledCount;
+ m_maxBufferedLine = std::max(m_maxBufferedLine, bufLine);
+ m_bufferData[bufLine % BUFFER_LINE_COUNT].blank(
+ Win32ConsoleBuffer::kDefaultAttributes);
+ }
+}
+
+static bool cursorInWindow(const ConsoleScreenBufferInfo &info)
+{
+ return info.dwCursorPosition.Y >= info.srWindow.Top &&
+ info.dwCursorPosition.Y <= info.srWindow.Bottom;
+}
+
+void Scraper::resizeImpl(const ConsoleScreenBufferInfo &origInfo)
+{
+ ASSERT(m_console.frozen());
+ const int cols = m_ptySize.X;
+ const int rows = m_ptySize.Y;
+ Coord finalBufferSize;
+
+ {
+ //
+ // To accommodate Windows 10, erase all lines up to the top of the
+ // visible window. It's hard to tell whether this is strictly
+ // necessary. It ensures that the sync marker won't move downward,
+ // and it ensures that we won't repeat lines that have already scrolled
+ // up into the scrollback.
+ //
+ // It *is* possible for these blank lines to reappear in the visible
+ // window (e.g. if the window is made taller), but because we blanked
+ // the lines in the line buffer, we still don't output them again.
+ //
+ const Coord origBufferSize = origInfo.bufferSize();
+ const SmallRect origWindowRect = origInfo.windowRect();
+
+ if (m_directMode) {
+ for (ConsoleLine &line : m_bufferData) {
+ line.reset();
+ }
+ } else {
+ m_consoleBuffer->clearLines(0, origWindowRect.Top, origInfo);
+ clearBufferLines(0, origWindowRect.Top);
+ if (m_syncRow != -1) {
+ createSyncMarker(std::min(
+ m_syncRow,
+ BUFFER_LINE_COUNT - rows
+ - SYNC_MARKER_LEN
+ - SYNC_MARKER_MARGIN));
+ }
+ }
+
+ finalBufferSize = Coord(
+ cols,
+ // If there was previously no scrollback (e.g. a full-screen app
+ // in direct mode) and we're reducing the window height, then
+ // reduce the console buffer's height too.
+ (origWindowRect.height() == origBufferSize.Y)
+ ? rows
+ : std::max<int>(rows, origBufferSize.Y));
+
+ // Reset the console font size. We need to do this before shrinking
+ // the window, because we might need to make the font bigger to permit
+ // a smaller window width. Making the font smaller could expand the
+ // screen buffer, which would hang the conhost process in the
+ // Windows 10 (10240 build) if the console selection is in progress, so
+ // unfreeze it first.
+ m_console.setFrozen(false);
+ setSmallFont(m_consoleBuffer->conout(), cols, m_console.isNewW10());
+ }
+
+ // We try to make the font small enough so that the entire screen buffer
+ // fits on the monitor, but it can't be guaranteed.
+ const auto largest =
+ GetLargestConsoleWindowSize(m_consoleBuffer->conout());
+ const short visibleCols = std::min<short>(cols, largest.X);
+ const short visibleRows = std::min<short>(rows, largest.Y);
+
+ {
+ // Make the window small enough. We want the console frozen during
+ // this step so we don't accidentally move the window above the cursor.
+ m_console.setFrozen(true);
+ const auto info = m_consoleBuffer->bufferInfo();
+ const auto &bufferSize = info.dwSize;
+ const int tmpWindowWidth = std::min(bufferSize.X, visibleCols);
+ const int tmpWindowHeight = std::min(bufferSize.Y, visibleRows);
+ SmallRect tmpWindowRect(
+ 0,
+ std::min<int>(bufferSize.Y - tmpWindowHeight,
+ info.windowRect().Top),
+ tmpWindowWidth,
+ tmpWindowHeight);
+ if (cursorInWindow(info)) {
+ tmpWindowRect = tmpWindowRect.ensureLineIncluded(
+ info.cursorPosition().Y);
+ }
+ m_consoleBuffer->moveWindow(tmpWindowRect);
+ }
+
+ {
+ // Resize the buffer to the final desired size.
+ m_console.setFrozen(false);
+ m_consoleBuffer->resizeBufferRange(finalBufferSize);
+ }
+
+ {
+ // Expand the window to its full size.
+ m_console.setFrozen(true);
+ const ConsoleScreenBufferInfo info = m_consoleBuffer->bufferInfo();
+
+ SmallRect finalWindowRect(
+ 0,
+ std::min<int>(info.bufferSize().Y - visibleRows,
+ info.windowRect().Top),
+ visibleCols,
+ visibleRows);
+
+ //
+ // Once a line in the screen buffer is "dirty", it should stay visible
+ // in the console window, so that we continue to update its content in
+ // the terminal. This code is particularly (only?) necessary on
+ // Windows 10, where making the buffer wider can rewrap lines and move
+ // the console window upward.
+ //
+ if (!m_directMode && m_dirtyLineCount > finalWindowRect.Bottom + 1) {
+ // In theory, we avoid ensureLineIncluded, because, a massive
+ // amount of output could have occurred while the console was
+ // unfrozen, so that the *top* of the window is now below the
+ // dirtiest tracked line.
+ finalWindowRect = SmallRect(
+ 0, m_dirtyLineCount - visibleRows,
+ visibleCols, visibleRows);
+ }
+
+ // Highest priority constraint: ensure that the cursor remains visible.
+ if (cursorInWindow(info)) {
+ finalWindowRect = finalWindowRect.ensureLineIncluded(
+ info.cursorPosition().Y);
+ }
+
+ m_consoleBuffer->moveWindow(finalWindowRect);
+ m_dirtyWindowTop = finalWindowRect.Top;
+ }
+
+ ASSERT(m_console.frozen());
+}
+
+void Scraper::syncConsoleContentAndSize(
+ bool forceResize,
+ ConsoleScreenBufferInfo &finalInfoOut)
+{
+ // We'll try to avoid freezing the console by reading large chunks (or
+ // all!) of the screen buffer without otherwise attempting to synchronize
+ // with the console application. We can only do this on Windows 10 and up
+ // because:
+ // - Prior to Windows 8, the size of a ReadConsoleOutputW call was limited
+ // by the ~32KB RPC buffer.
+ // - Prior to Windows 10, an out-of-range read region crashes the caller.
+ // (See misc/WindowsBugCrashReader.cc.)
+ //
+ if (!m_console.isNewW10() || forceResize) {
+ m_console.setFrozen(true);
+ }
+
+ const ConsoleScreenBufferInfo info = m_consoleBuffer->bufferInfo();
+ bool cursorVisible = true;
+ CONSOLE_CURSOR_INFO cursorInfo = {};
+ if (!GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursorInfo)) {
+ trace("GetConsoleCursorInfo failed");
+ } else {
+ cursorVisible = cursorInfo.bVisible != 0;
+ }
+
+ // If an app resizes the buffer height, then we enter "direct mode", where
+ // we stop trying to track incremental console changes.
+ const bool newDirectMode = (info.bufferSize().Y != BUFFER_LINE_COUNT);
+ if (newDirectMode != m_directMode) {
+ trace("Entering %s mode", newDirectMode ? "direct" : "scrolling");
+ resetConsoleTracking(Terminal::SendClear,
+ newDirectMode ? 0 : info.windowRect().top());
+ m_directMode = newDirectMode;
+
+ // When we switch from direct->scrolling mode, make sure the console is
+ // the right size.
+ if (!m_directMode) {
+ m_console.setFrozen(true);
+ forceResize = true;
+ }
+ }
+
+ if (m_directMode) {
+ // In direct-mode, resizing the console redraws the terminal, so do it
+ // before scraping.
+ if (forceResize) {
+ resizeImpl(info);
+ }
+ directScrapeOutput(info, cursorVisible);
+ } else {
+ if (!m_console.frozen()) {
+ if (!scrollingScrapeOutput(info, cursorVisible, true)) {
+ m_console.setFrozen(true);
+ }
+ }
+ if (m_console.frozen()) {
+ scrollingScrapeOutput(info, cursorVisible, false);
+ }
+ // In scrolling mode, we want to scrape before resizing, because we'll
+ // erase everything in the console buffer up to the top of the console
+ // window.
+ if (forceResize) {
+ resizeImpl(info);
+ }
+ }
+
+ finalInfoOut = forceResize ? m_consoleBuffer->bufferInfo() : info;
+}
+
+// Try to match Windows' behavior w.r.t. to the LVB attribute flags. In some
+// situations, Windows ignores the LVB flags on a character cell because of
+// backwards compatibility -- apparently some programs set the flags without
+// intending to enable reverse-video or underscores.
+//
+// [rprichard 2017-01-15] I haven't actually noticed any old programs that need
+// this treatment -- the motivation for this function comes from the MSDN
+// documentation for SetConsoleMode and ENABLE_LVB_GRID_WORLDWIDE.
+WORD Scraper::attributesMask()
+{
+ const auto WINPTY_ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4u;
+ const auto WINPTY_ENABLE_LVB_GRID_WORLDWIDE = 0x10u;
+ const auto WINPTY_COMMON_LVB_REVERSE_VIDEO = 0x4000u;
+ const auto WINPTY_COMMON_LVB_UNDERSCORE = 0x8000u;
+
+ const auto cp = GetConsoleOutputCP();
+ const auto isCjk = (cp == 932 || cp == 936 || cp == 949 || cp == 950);
+
+ const DWORD outputMode = [this]{
+ ASSERT(this->m_consoleBuffer != nullptr);
+ DWORD mode = 0;
+ if (!GetConsoleMode(this->m_consoleBuffer->conout(), &mode)) {
+ mode = 0;
+ }
+ return mode;
+ }();
+ const bool hasEnableLvbGridWorldwide =
+ (outputMode & WINPTY_ENABLE_LVB_GRID_WORLDWIDE) != 0;
+ const bool hasEnableVtProcessing =
+ (outputMode & WINPTY_ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0;
+
+ // The new Windows 10 console (as of 14393) seems to respect
+ // COMMON_LVB_REVERSE_VIDEO even in CP437 w/o the other enabling modes, so
+ // try to match that behavior.
+ const auto isReverseSupported =
+ isCjk || hasEnableLvbGridWorldwide || hasEnableVtProcessing || m_console.isNewW10();
+ const auto isUnderscoreSupported =
+ isCjk || hasEnableLvbGridWorldwide || hasEnableVtProcessing;
+
+ WORD mask = ~0;
+ if (!isReverseSupported) { mask &= ~WINPTY_COMMON_LVB_REVERSE_VIDEO; }
+ if (!isUnderscoreSupported) { mask &= ~WINPTY_COMMON_LVB_UNDERSCORE; }
+ return mask;
+}
+
+void Scraper::directScrapeOutput(const ConsoleScreenBufferInfo &info,
+ bool consoleCursorVisible)
+{
+ const SmallRect windowRect = info.windowRect();
+
+ const SmallRect scrapeRect(
+ windowRect.left(), windowRect.top(),
+ std::min<SHORT>(std::min(windowRect.width(), m_ptySize.X),
+ MAX_CONSOLE_WIDTH),
+ std::min<SHORT>(std::min(windowRect.height(), m_ptySize.Y),
+ BUFFER_LINE_COUNT));
+ const int w = scrapeRect.width();
+ const int h = scrapeRect.height();
+
+ const Coord cursor = info.cursorPosition();
+ const bool showTerminalCursor =
+ consoleCursorVisible && scrapeRect.contains(cursor);
+ const int cursorColumn = !showTerminalCursor ? -1 : cursor.X - scrapeRect.Left;
+ const int cursorLine = !showTerminalCursor ? -1 : cursor.Y - scrapeRect.Top;
+
+ if (!showTerminalCursor) {
+ m_terminal->hideTerminalCursor();
+ }
+
+ largeConsoleRead(m_readBuffer, *m_consoleBuffer, scrapeRect, attributesMask());
+
+ for (int line = 0; line < h; ++line) {
+ const CHAR_INFO *const curLine =
+ m_readBuffer.lineData(scrapeRect.top() + line);
+ ConsoleLine &bufLine = m_bufferData[line];
+ if (bufLine.detectChangeAndSetLine(curLine, w)) {
+ const int lineCursorColumn =
+ line == cursorLine ? cursorColumn : -1;
+ m_terminal->sendLine(line, curLine, w, lineCursorColumn);
+ }
+ }
+
+ if (showTerminalCursor) {
+ m_terminal->showTerminalCursor(cursorColumn, cursorLine);
+ }
+}
+
+bool Scraper::scrollingScrapeOutput(const ConsoleScreenBufferInfo &info,
+ bool consoleCursorVisible,
+ bool tentative)
+{
+ const Coord cursor = info.cursorPosition();
+ const SmallRect windowRect = info.windowRect();
+
+ if (m_syncRow != -1) {
+ // If a synchronizing marker was placed into the history, look for it
+ // and adjust the scroll count.
+ const int markerRow = findSyncMarker();
+ if (markerRow == -1) {
+ if (tentative) {
+ // I *think* it's possible to keep going, but it's simple to
+ // bail out.
+ return false;
+ }
+ // Something has happened. Reset the terminal.
+ trace("Sync marker has disappeared -- resetting the terminal"
+ " (m_syncCounter=%u)",
+ m_syncCounter);
+ resetConsoleTracking(Terminal::SendClear, windowRect.top());
+ } else if (markerRow != m_syncRow) {
+ ASSERT(markerRow < m_syncRow);
+ m_scrolledCount += (m_syncRow - markerRow);
+ m_syncRow = markerRow;
+ // If the buffer has scrolled, then the entire window is dirty.
+ markEntireWindowDirty(windowRect);
+ }
+ }
+
+ // Creating a new sync row requires clearing part of the console buffer, so
+ // avoid doing it if there's already a sync row that's good enough.
+ const int newSyncRow =
+ static_cast<int>(windowRect.top()) - SYNC_MARKER_LEN - SYNC_MARKER_MARGIN;
+ const bool shouldCreateSyncRow =
+ newSyncRow >= m_syncRow + SYNC_MARKER_LEN + SYNC_MARKER_MARGIN;
+ if (tentative && shouldCreateSyncRow) {
+ // It's difficult even in principle to put down a new marker if the
+ // console can scroll an arbitrarily amount while we're writing.
+ return false;
+ }
+
+ // Update the dirty line count:
+ // - If the window has moved, the entire window is dirty.
+ // - Everything up to the cursor is dirty.
+ // - All lines above the window are dirty.
+ // - Any non-blank lines are dirty.
+ if (m_dirtyWindowTop != -1) {
+ if (windowRect.top() > m_dirtyWindowTop) {
+ // The window has moved down, presumably as a result of scrolling.
+ markEntireWindowDirty(windowRect);
+ } else if (windowRect.top() < m_dirtyWindowTop) {
+ if (tentative) {
+ // I *think* it's possible to keep going, but it's simple to
+ // bail out.
+ return false;
+ }
+ // The window has moved upward. This is generally not expected to
+ // happen, but the CMD/PowerShell CLS command will move the window
+ // to the top as part of clearing everything else in the console.
+ trace("Window moved upward -- resetting the terminal"
+ " (m_syncCounter=%u)",
+ m_syncCounter);
+ resetConsoleTracking(Terminal::SendClear, windowRect.top());
+ }
+ }
+ m_dirtyWindowTop = windowRect.top();
+ m_dirtyLineCount = std::max(m_dirtyLineCount, cursor.Y + 1);
+ m_dirtyLineCount = std::max(m_dirtyLineCount, (int)windowRect.top());
+
+ // There will be at least one dirty line, because there is a cursor.
+ ASSERT(m_dirtyLineCount >= 1);
+
+ // The first line to scrape, in virtual line coordinates.
+ const int64_t firstVirtLine = std::min(m_scrapedLineCount,
+ windowRect.top() + m_scrolledCount);
+
+ // Read all the data we will need from the console. Start reading with the
+ // first line to scrape, but adjust the the read area upward to account for
+ // scanForDirtyLines' need to read the previous attribute. Read to the
+ // bottom of the window. (It's not clear to me whether the
+ // m_dirtyLineCount adjustment here is strictly necessary. It isn't
+ // necessary so long as the cursor is inside the current window.)
+ const int firstReadLine = std::min<int>(firstVirtLine - m_scrolledCount,
+ m_dirtyLineCount - 1);
+ const int stopReadLine = std::max(windowRect.top() + windowRect.height(),
+ m_dirtyLineCount);
+ ASSERT(firstReadLine >= 0 && stopReadLine > firstReadLine);
+ largeConsoleRead(m_readBuffer,
+ *m_consoleBuffer,
+ SmallRect(0, firstReadLine,
+ std::min<SHORT>(info.bufferSize().X,
+ MAX_CONSOLE_WIDTH),
+ stopReadLine - firstReadLine),
+ attributesMask());
+
+ // If we're scraping the buffer without freezing it, we have to query the
+ // buffer position data separately from the buffer content, so the two
+ // could easily be out-of-sync. If they *are* out-of-sync, abort the
+ // scrape operation and restart it frozen. (We may have updated the
+ // dirty-line high-water-mark, but that should be OK.)
+ if (tentative) {
+ const auto infoCheck = m_consoleBuffer->bufferInfo();
+ if (info.bufferSize() != infoCheck.bufferSize() ||
+ info.windowRect() != infoCheck.windowRect() ||
+ info.cursorPosition() != infoCheck.cursorPosition()) {
+ return false;
+ }
+ if (m_syncRow != -1 && m_syncRow != findSyncMarker()) {
+ return false;
+ }
+ }
+
+ if (shouldCreateSyncRow) {
+ ASSERT(!tentative);
+ createSyncMarker(newSyncRow);
+ }
+
+ // At this point, we're finished interacting (reading or writing) the
+ // console, and we just need to convert our collected data into terminal
+ // output.
+
+ scanForDirtyLines(windowRect);
+
+ // Note that it's possible for all the lines on the current window to
+ // be non-dirty.
+
+ // The line to stop scraping at, in virtual line coordinates.
+ const int64_t stopVirtLine =
+ std::min(m_dirtyLineCount, windowRect.top() + windowRect.height()) +
+ m_scrolledCount;
+
+ const bool showTerminalCursor =
+ consoleCursorVisible && windowRect.contains(cursor);
+ const int64_t cursorLine = !showTerminalCursor ? -1 : cursor.Y + m_scrolledCount;
+ const int cursorColumn = !showTerminalCursor ? -1 : cursor.X;
+
+ if (!showTerminalCursor) {
+ m_terminal->hideTerminalCursor();
+ }
+
+ bool sawModifiedLine = false;
+
+ const int w = m_readBuffer.rect().width();
+ for (int64_t line = firstVirtLine; line < stopVirtLine; ++line) {
+ const CHAR_INFO *curLine =
+ m_readBuffer.lineData(line - m_scrolledCount);
+ ConsoleLine &bufLine = m_bufferData[line % BUFFER_LINE_COUNT];
+ if (line > m_maxBufferedLine) {
+ m_maxBufferedLine = line;
+ sawModifiedLine = true;
+ }
+ if (sawModifiedLine) {
+ bufLine.setLine(curLine, w);
+ } else {
+ sawModifiedLine = bufLine.detectChangeAndSetLine(curLine, w);
+ }
+ if (sawModifiedLine) {
+ const int lineCursorColumn =
+ line == cursorLine ? cursorColumn : -1;
+ m_terminal->sendLine(line, curLine, w, lineCursorColumn);
+ }
+ }
+
+ m_scrapedLineCount = windowRect.top() + m_scrolledCount;
+
+ if (showTerminalCursor) {
+ m_terminal->showTerminalCursor(cursorColumn, cursorLine);
+ }
+
+ return true;
+}
+
+void Scraper::syncMarkerText(CHAR_INFO (&output)[SYNC_MARKER_LEN])
+{
+ // XXX: The marker text generated here could easily collide with ordinary
+ // console output. Does it make sense to try to avoid the collision?
+ char str[SYNC_MARKER_LEN + 1];
+ winpty_snprintf(str, "S*Y*N*C*%08x", m_syncCounter);
+ for (int i = 0; i < SYNC_MARKER_LEN; ++i) {
+ output[i].Char.UnicodeChar = str[i];
+ output[i].Attributes = 7;
+ }
+}
+
+int Scraper::findSyncMarker()
+{
+ ASSERT(m_syncRow >= 0);
+ CHAR_INFO marker[SYNC_MARKER_LEN];
+ CHAR_INFO column[BUFFER_LINE_COUNT];
+ syncMarkerText(marker);
+ SmallRect rect(0, 0, 1, m_syncRow + SYNC_MARKER_LEN);
+ m_consoleBuffer->read(rect, column);
+ int i;
+ for (i = m_syncRow; i >= 0; --i) {
+ int j;
+ for (j = 0; j < SYNC_MARKER_LEN; ++j) {
+ if (column[i + j].Char.UnicodeChar != marker[j].Char.UnicodeChar)
+ break;
+ }
+ if (j == SYNC_MARKER_LEN)
+ return i;
+ }
+ return -1;
+}
+
+void Scraper::createSyncMarker(int row)
+{
+ ASSERT(row >= 1);
+
+ // Clear the lines around the marker to ensure that Windows 10's rewrapping
+ // does not affect the marker.
+ m_consoleBuffer->clearLines(row - 1, SYNC_MARKER_LEN + 1,
+ m_consoleBuffer->bufferInfo());
+
+ // Write a new marker.
+ m_syncCounter++;
+ CHAR_INFO marker[SYNC_MARKER_LEN];
+ syncMarkerText(marker);
+ m_syncRow = row;
+ SmallRect markerRect(0, m_syncRow, 1, SYNC_MARKER_LEN);
+ m_consoleBuffer->write(markerRect, marker);
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Scraper.h b/src/libs/3rdparty/winpty/src/agent/Scraper.h
new file mode 100644
index 0000000000..9c10d80aed
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Scraper.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_SCRAPER_H
+#define AGENT_SCRAPER_H
+
+#include <windows.h>
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "ConsoleLine.h"
+#include "Coord.h"
+#include "LargeConsoleRead.h"
+#include "SmallRect.h"
+#include "Terminal.h"
+
+class ConsoleScreenBufferInfo;
+class Win32Console;
+class Win32ConsoleBuffer;
+
+// We must be able to issue a single ReadConsoleOutputW call of
+// MAX_CONSOLE_WIDTH characters, and a single read of approximately several
+// hundred fewer characters than BUFFER_LINE_COUNT.
+const int BUFFER_LINE_COUNT = 3000;
+const int MAX_CONSOLE_WIDTH = 2500;
+const int MAX_CONSOLE_HEIGHT = 2000;
+const int SYNC_MARKER_LEN = 16;
+const int SYNC_MARKER_MARGIN = 200;
+
+class Scraper {
+public:
+ Scraper(
+ Win32Console &console,
+ Win32ConsoleBuffer &buffer,
+ std::unique_ptr<Terminal> terminal,
+ Coord initialSize);
+ ~Scraper();
+ void resizeWindow(Win32ConsoleBuffer &buffer,
+ Coord newSize,
+ ConsoleScreenBufferInfo &finalInfoOut);
+ void scrapeBuffer(Win32ConsoleBuffer &buffer,
+ ConsoleScreenBufferInfo &finalInfoOut);
+ Terminal &terminal() { return *m_terminal; }
+
+private:
+ void resetConsoleTracking(
+ Terminal::SendClearFlag sendClear, int64_t scrapedLineCount);
+ void markEntireWindowDirty(const SmallRect &windowRect);
+ void scanForDirtyLines(const SmallRect &windowRect);
+ void clearBufferLines(int firstRow, int count);
+ void resizeImpl(const ConsoleScreenBufferInfo &origInfo);
+ void syncConsoleContentAndSize(bool forceResize,
+ ConsoleScreenBufferInfo &finalInfoOut);
+ WORD attributesMask();
+ void directScrapeOutput(const ConsoleScreenBufferInfo &info,
+ bool consoleCursorVisible);
+ bool scrollingScrapeOutput(const ConsoleScreenBufferInfo &info,
+ bool consoleCursorVisible,
+ bool tentative);
+ void syncMarkerText(CHAR_INFO (&output)[SYNC_MARKER_LEN]);
+ int findSyncMarker();
+ void createSyncMarker(int row);
+
+private:
+ Win32Console &m_console;
+ Win32ConsoleBuffer *m_consoleBuffer = nullptr;
+ std::unique_ptr<Terminal> m_terminal;
+
+ int m_syncRow = -1;
+ unsigned int m_syncCounter = 0;
+
+ bool m_directMode = false;
+ Coord m_ptySize;
+ int64_t m_scrapedLineCount = 0;
+ int64_t m_scrolledCount = 0;
+ int64_t m_maxBufferedLine = -1;
+ LargeConsoleReadBuffer m_readBuffer;
+ std::vector<ConsoleLine> m_bufferData;
+ int m_dirtyWindowTop = -1;
+ int m_dirtyLineCount = 0;
+};
+
+#endif // AGENT_SCRAPER_H
diff --git a/src/libs/3rdparty/winpty/src/agent/SimplePool.h b/src/libs/3rdparty/winpty/src/agent/SimplePool.h
new file mode 100644
index 0000000000..41ff94a90d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/SimplePool.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef SIMPLE_POOL_H
+#define SIMPLE_POOL_H
+
+#include <stdlib.h>
+
+#include <vector>
+
+#include "../shared/WinptyAssert.h"
+
+template <typename T, size_t chunkSize>
+class SimplePool {
+public:
+ ~SimplePool();
+ T *alloc();
+ void clear();
+private:
+ struct Chunk {
+ size_t count;
+ T *data;
+ };
+ std::vector<Chunk> m_chunks;
+};
+
+template <typename T, size_t chunkSize>
+SimplePool<T, chunkSize>::~SimplePool() {
+ clear();
+}
+
+template <typename T, size_t chunkSize>
+void SimplePool<T, chunkSize>::clear() {
+ for (size_t ci = 0; ci < m_chunks.size(); ++ci) {
+ Chunk &chunk = m_chunks[ci];
+ for (size_t ti = 0; ti < chunk.count; ++ti) {
+ chunk.data[ti].~T();
+ }
+ free(chunk.data);
+ }
+ m_chunks.clear();
+}
+
+template <typename T, size_t chunkSize>
+T *SimplePool<T, chunkSize>::alloc() {
+ if (m_chunks.empty() || m_chunks.back().count == chunkSize) {
+ T *newData = reinterpret_cast<T*>(malloc(sizeof(T) * chunkSize));
+ ASSERT(newData != NULL);
+ Chunk newChunk = { 0, newData };
+ m_chunks.push_back(newChunk);
+ }
+ Chunk &chunk = m_chunks.back();
+ T *ret = &chunk.data[chunk.count++];
+ new (ret) T();
+ return ret;
+}
+
+#endif // SIMPLE_POOL_H
diff --git a/src/libs/3rdparty/winpty/src/agent/SmallRect.h b/src/libs/3rdparty/winpty/src/agent/SmallRect.h
new file mode 100644
index 0000000000..bad0b88683
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/SmallRect.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef SMALLRECT_H
+#define SMALLRECT_H
+
+#include <windows.h>
+
+#include <algorithm>
+#include <string>
+
+#include "../shared/winpty_snprintf.h"
+#include "Coord.h"
+
+struct SmallRect : SMALL_RECT
+{
+ SmallRect()
+ {
+ Left = Right = Top = Bottom = 0;
+ }
+
+ SmallRect(SHORT x, SHORT y, SHORT width, SHORT height)
+ {
+ Left = x;
+ Top = y;
+ Right = x + width - 1;
+ Bottom = y + height - 1;
+ }
+
+ SmallRect(const COORD &topLeft, const COORD &size)
+ {
+ Left = topLeft.X;
+ Top = topLeft.Y;
+ Right = Left + size.X - 1;
+ Bottom = Top + size.Y - 1;
+ }
+
+ SmallRect(const SMALL_RECT &other)
+ {
+ *(SMALL_RECT*)this = other;
+ }
+
+ SmallRect(const SmallRect &other)
+ {
+ *(SMALL_RECT*)this = *(const SMALL_RECT*)&other;
+ }
+
+ SmallRect &operator=(const SmallRect &other)
+ {
+ *(SMALL_RECT*)this = *(const SMALL_RECT*)&other;
+ return *this;
+ }
+
+ bool contains(const SmallRect &other) const
+ {
+ return other.Left >= Left &&
+ other.Right <= Right &&
+ other.Top >= Top &&
+ other.Bottom <= Bottom;
+ }
+
+ bool contains(const Coord &other) const
+ {
+ return other.X >= Left &&
+ other.X <= Right &&
+ other.Y >= Top &&
+ other.Y <= Bottom;
+ }
+
+ SmallRect intersected(const SmallRect &other) const
+ {
+ int x1 = std::max(Left, other.Left);
+ int x2 = std::min(Right, other.Right);
+ int y1 = std::max(Top, other.Top);
+ int y2 = std::min(Bottom, other.Bottom);
+ return SmallRect(x1,
+ y1,
+ std::max(0, x2 - x1 + 1),
+ std::max(0, y2 - y1 + 1));
+ }
+
+ SmallRect ensureLineIncluded(SHORT line) const
+ {
+ const SHORT h = height();
+ if (line < Top) {
+ return SmallRect(Left, line, width(), h);
+ } else if (line > Bottom) {
+ return SmallRect(Left, line - h + 1, width(), h);
+ } else {
+ return *this;
+ }
+ }
+
+ SHORT top() const { return Top; }
+ SHORT left() const { return Left; }
+ SHORT width() const { return Right - Left + 1; }
+ SHORT height() const { return Bottom - Top + 1; }
+ void setTop(SHORT top) { Top = top; }
+ void setLeft(SHORT left) { Left = left; }
+ void setWidth(SHORT width) { Right = Left + width - 1; }
+ void setHeight(SHORT height) { Bottom = Top + height - 1; }
+ Coord size() const { return Coord(width(), height()); }
+
+ bool operator==(const SmallRect &other) const
+ {
+ return Left == other.Left &&
+ Right == other.Right &&
+ Top == other.Top &&
+ Bottom == other.Bottom;
+ }
+
+ bool operator!=(const SmallRect &other) const
+ {
+ return !(*this == other);
+ }
+
+ std::string toString() const
+ {
+ char ret[64];
+ winpty_snprintf(ret, "(x=%d,y=%d,w=%d,h=%d)",
+ Left, Top, width(), height());
+ return std::string(ret);
+ }
+};
+
+#endif // SMALLRECT_H
diff --git a/src/libs/3rdparty/winpty/src/agent/Terminal.cc b/src/libs/3rdparty/winpty/src/agent/Terminal.cc
new file mode 100644
index 0000000000..afa0a36260
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Terminal.cc
@@ -0,0 +1,535 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Terminal.h"
+
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+
+#include "NamedPipe.h"
+#include "UnicodeEncoding.h"
+#include "../shared/DebugClient.h"
+#include "../shared/WinptyAssert.h"
+#include "../shared/winpty_snprintf.h"
+
+#define CSI "\x1b["
+
+// Work around the old MinGW, which lacks COMMON_LVB_LEADING_BYTE and
+// COMMON_LVB_TRAILING_BYTE.
+const int WINPTY_COMMON_LVB_LEADING_BYTE = 0x100;
+const int WINPTY_COMMON_LVB_TRAILING_BYTE = 0x200;
+const int WINPTY_COMMON_LVB_REVERSE_VIDEO = 0x4000;
+const int WINPTY_COMMON_LVB_UNDERSCORE = 0x8000;
+
+const int COLOR_ATTRIBUTE_MASK =
+ FOREGROUND_BLUE |
+ FOREGROUND_GREEN |
+ FOREGROUND_RED |
+ FOREGROUND_INTENSITY |
+ BACKGROUND_BLUE |
+ BACKGROUND_GREEN |
+ BACKGROUND_RED |
+ BACKGROUND_INTENSITY |
+ WINPTY_COMMON_LVB_REVERSE_VIDEO |
+ WINPTY_COMMON_LVB_UNDERSCORE;
+
+const int FLAG_RED = 1;
+const int FLAG_GREEN = 2;
+const int FLAG_BLUE = 4;
+const int FLAG_BRIGHT = 8;
+
+const int BLACK = 0;
+const int DKGRAY = BLACK | FLAG_BRIGHT;
+const int LTGRAY = FLAG_RED | FLAG_GREEN | FLAG_BLUE;
+const int WHITE = LTGRAY | FLAG_BRIGHT;
+
+// SGR parameters (Select Graphic Rendition)
+const int SGR_FORE = 30;
+const int SGR_FORE_HI = 90;
+const int SGR_BACK = 40;
+const int SGR_BACK_HI = 100;
+
+namespace {
+
+static void outUInt(std::string &out, unsigned int n)
+{
+ char buf[32];
+ char *pbuf = &buf[32];
+ *(--pbuf) = '\0';
+ do {
+ *(--pbuf) = '0' + n % 10;
+ n /= 10;
+ } while (n != 0);
+ out.append(pbuf);
+}
+
+static void outputSetColorSgrParams(std::string &out, bool isFore, int color)
+{
+ out.push_back(';');
+ const int sgrBase = isFore ? SGR_FORE : SGR_BACK;
+ if (color & FLAG_BRIGHT) {
+ // Some terminals don't support the 9X/10X "intensive" color parameters
+ // (e.g. the Eclipse TM terminal as of this writing). Those terminals
+ // will quietly ignore a 9X/10X code, and the other terminals will
+ // ignore a 3X/4X code if it's followed by a 9X/10X code. Therefore,
+ // output a 3X/4X code as a fallback, then override it.
+ const int colorBase = color & ~FLAG_BRIGHT;
+ outUInt(out, sgrBase + colorBase);
+ out.push_back(';');
+ outUInt(out, sgrBase + (SGR_FORE_HI - SGR_FORE) + colorBase);
+ } else {
+ outUInt(out, sgrBase + color);
+ }
+}
+
+static void outputSetColor(std::string &out, int color)
+{
+ int fore = 0;
+ int back = 0;
+ if (color & FOREGROUND_RED) fore |= FLAG_RED;
+ if (color & FOREGROUND_GREEN) fore |= FLAG_GREEN;
+ if (color & FOREGROUND_BLUE) fore |= FLAG_BLUE;
+ if (color & FOREGROUND_INTENSITY) fore |= FLAG_BRIGHT;
+ if (color & BACKGROUND_RED) back |= FLAG_RED;
+ if (color & BACKGROUND_GREEN) back |= FLAG_GREEN;
+ if (color & BACKGROUND_BLUE) back |= FLAG_BLUE;
+ if (color & BACKGROUND_INTENSITY) back |= FLAG_BRIGHT;
+
+ if (color & WINPTY_COMMON_LVB_REVERSE_VIDEO) {
+ // n.b.: The COMMON_LVB_REVERSE_VIDEO flag also swaps
+ // FOREGROUND_INTENSITY and BACKGROUND_INTENSITY. Tested on
+ // Windows 10 v14393.
+ std::swap(fore, back);
+ }
+
+ // Translate the fore/back colors into terminal escape codes using
+ // a heuristic that works OK with common white-on-black or
+ // black-on-white color schemes. We don't know which color scheme
+ // the terminal is using. It is ugly to force white-on-black text
+ // on a black-on-white terminal, and it's even ugly to force the
+ // matching scheme. It's probably relevant that the default
+ // fore/back terminal colors frequently do not match any of the 16
+ // palette colors.
+
+ // Typical default terminal color schemes (according to palette,
+ // when possible):
+ // - mintty: LtGray-on-Black(A)
+ // - putty: LtGray-on-Black(A)
+ // - xterm: LtGray-on-Black(A)
+ // - Konsole: LtGray-on-Black(A)
+ // - JediTerm/JetBrains: Black-on-White(B)
+ // - rxvt: Black-on-White(B)
+
+ // If the background is the default color (black), then it will
+ // map to Black(A) or White(B). If we translate White to White,
+ // then a Black background and a White background in the console
+ // are both White with (B). Therefore, we should translate White
+ // using SGR 7 (Invert). The typical finished mapping table for
+ // background grayscale colors is:
+ //
+ // (A) White => LtGray(fore)
+ // (A) Black => Black(back)
+ // (A) LtGray => LtGray
+ // (A) DkGray => DkGray
+ //
+ // (B) White => Black(fore)
+ // (B) Black => White(back)
+ // (B) LtGray => LtGray
+ // (B) DkGray => DkGray
+ //
+
+ out.append(CSI "0");
+ if (back == BLACK) {
+ if (fore == LTGRAY) {
+ // The "default" foreground color. Use the terminal's
+ // default colors.
+ } else if (fore == WHITE) {
+ // Sending the literal color white would behave poorly if
+ // the terminal were black-on-white. Sending Bold is not
+ // guaranteed to alter the color, but it will make the text
+ // visually distinct, so do that instead.
+ out.append(";1");
+ } else if (fore == DKGRAY) {
+ // Set the foreground color to DkGray(90) with a fallback
+ // of LtGray(37) for terminals that don't handle the 9X SGR
+ // parameters (e.g. Eclipse's TM Terminal as of this
+ // writing).
+ out.append(";37;90");
+ } else {
+ outputSetColorSgrParams(out, true, fore);
+ }
+ } else if (back == WHITE) {
+ // Set the background color using Invert on the default
+ // foreground color, and set the foreground color by setting a
+ // background color.
+
+ // Use the terminal's inverted colors.
+ out.append(";7");
+ if (fore == LTGRAY || fore == BLACK) {
+ // We're likely mapping Console White to terminal LtGray or
+ // Black. If they are the Console foreground color, then
+ // don't set a terminal foreground color to avoid creating
+ // invisible text.
+ } else {
+ outputSetColorSgrParams(out, false, fore);
+ }
+ } else {
+ // Set the foreground and background to match exactly that in
+ // the Windows console.
+ outputSetColorSgrParams(out, true, fore);
+ outputSetColorSgrParams(out, false, back);
+ }
+ if (fore == back) {
+ // The foreground and background colors are exactly equal, so
+ // attempt to hide the text using the Conceal SGR parameter,
+ // which some terminals support.
+ out.append(";8");
+ }
+ if (color & WINPTY_COMMON_LVB_UNDERSCORE) {
+ out.append(";4");
+ }
+ out.push_back('m');
+}
+
+static inline unsigned int fixSpecialCharacters(unsigned int ch)
+{
+ if (ch <= 0x1b) {
+ switch (ch) {
+ // The Windows Console has a popup window (e.g. that appears with
+ // F7) that is sometimes bordered with box-drawing characters.
+ // With the Japanese and Korean system locales (CP932 and CP949),
+ // the UnicodeChar values for the box-drawing characters are 1
+ // through 6. Detect this and map the values to the correct
+ // Unicode values.
+ //
+ // N.B. In the English locale, the UnicodeChar values are correct,
+ // and they identify single-line characters rather than
+ // double-line. In the Chinese Simplified and Traditional locales,
+ // the popups use ASCII characters instead.
+ case 1: return 0x2554; // BOX DRAWINGS DOUBLE DOWN AND RIGHT
+ case 2: return 0x2557; // BOX DRAWINGS DOUBLE DOWN AND LEFT
+ case 3: return 0x255A; // BOX DRAWINGS DOUBLE UP AND RIGHT
+ case 4: return 0x255D; // BOX DRAWINGS DOUBLE UP AND LEFT
+ case 5: return 0x2551; // BOX DRAWINGS DOUBLE VERTICAL
+ case 6: return 0x2550; // BOX DRAWINGS DOUBLE HORIZONTAL
+
+ // Convert an escape character to some other character. This
+ // conversion only applies to console cells containing an escape
+ // character. In newer versions of Windows 10 (e.g. 10.0.10586),
+ // the non-legacy console recognizes escape sequences in
+ // WriteConsole and interprets them without writing them to the
+ // cells of the screen buffer. In that case, the conversion here
+ // does not apply.
+ case 0x1b: return '?';
+ }
+ }
+ return ch;
+}
+
+static inline bool isFullWidthCharacter(const CHAR_INFO *data, int width)
+{
+ if (width < 2) {
+ return false;
+ }
+ return
+ (data[0].Attributes & WINPTY_COMMON_LVB_LEADING_BYTE) &&
+ (data[1].Attributes & WINPTY_COMMON_LVB_TRAILING_BYTE) &&
+ data[0].Char.UnicodeChar == data[1].Char.UnicodeChar;
+}
+
+// Scan to find a single Unicode Scalar Value. Full-width characters occupy
+// two console cells, and this code also tries to handle UTF-16 surrogate
+// pairs.
+//
+// Windows expands at least some wide characters outside the Basic
+// Multilingual Plane into four cells, such as U+20000:
+// 1. 0xD840, attr=0x107
+// 2. 0xD840, attr=0x207
+// 3. 0xDC00, attr=0x107
+// 4. 0xDC00, attr=0x207
+// Even in the Traditional Chinese locale on Windows 10, this text is rendered
+// as two boxes, but if those boxes are copied-and-pasted, the character is
+// copied correctly.
+static inline void scanUnicodeScalarValue(
+ const CHAR_INFO *data, int width,
+ int &outCellCount, unsigned int &outCharValue)
+{
+ ASSERT(width >= 1);
+
+ const int w1 = isFullWidthCharacter(data, width) ? 2 : 1;
+ const wchar_t c1 = data[0].Char.UnicodeChar;
+
+ if ((c1 & 0xF800) == 0xD800) {
+ // The first cell is either a leading or trailing surrogate pair.
+ if ((c1 & 0xFC00) != 0xD800 ||
+ width <= w1 ||
+ ((data[w1].Char.UnicodeChar & 0xFC00) != 0xDC00)) {
+ // Invalid surrogate pair
+ outCellCount = w1;
+ outCharValue = '?';
+ } else {
+ // Valid surrogate pair
+ outCellCount = w1 + (isFullWidthCharacter(&data[w1], width - w1) ? 2 : 1);
+ outCharValue = decodeSurrogatePair(c1, data[w1].Char.UnicodeChar);
+ }
+ } else {
+ outCellCount = w1;
+ outCharValue = c1;
+ }
+}
+
+} // anonymous namespace
+
+void Terminal::reset(SendClearFlag sendClearFirst, int64_t newLine)
+{
+ if (sendClearFirst == SendClear && !m_plainMode) {
+ // 0m ==> reset SGR parameters
+ // 1;1H ==> move cursor to top-left position
+ // 2J ==> clear the entire screen
+ m_output.write(CSI "0m" CSI "1;1H" CSI "2J");
+ }
+ m_remoteLine = newLine;
+ m_remoteColumn = 0;
+ m_lineData.clear();
+ m_cursorHidden = false;
+ m_remoteColor = -1;
+}
+
+void Terminal::sendLine(int64_t line, const CHAR_INFO *lineData, int width,
+ int cursorColumn)
+{
+ ASSERT(width >= 1);
+
+ moveTerminalToLine(line);
+
+ // If possible, see if we can append to what we've already output for this
+ // line.
+ if (m_lineDataValid) {
+ ASSERT(m_lineData.size() == static_cast<size_t>(m_remoteColumn));
+ if (m_remoteColumn > 0) {
+ // In normal mode, if m_lineData.size() equals `width`, then we
+ // will have trouble outputing the "erase rest of line" command,
+ // which must be output before reaching the end of the line. In
+ // plain mode, we don't output that command, so we're OK with a
+ // full line.
+ bool okWidth = false;
+ if (m_plainMode) {
+ okWidth = static_cast<size_t>(width) >= m_lineData.size();
+ } else {
+ okWidth = static_cast<size_t>(width) > m_lineData.size();
+ }
+ if (!okWidth ||
+ memcmp(m_lineData.data(), lineData,
+ sizeof(CHAR_INFO) * m_lineData.size()) != 0) {
+ m_lineDataValid = false;
+ }
+ }
+ }
+ if (!m_lineDataValid) {
+ // We can't reuse, so we must reset this line.
+ hideTerminalCursor();
+ if (m_plainMode) {
+ // We can't backtrack, so repeat this line.
+ m_output.write("\r\n");
+ } else {
+ m_output.write("\r");
+ }
+ m_lineDataValid = true;
+ m_lineData.clear();
+ m_remoteColumn = 0;
+ }
+
+ std::string &termLine = m_termLineWorkingBuffer;
+ termLine.clear();
+ size_t trimmedLineLength = 0;
+ int trimmedCellCount = m_lineData.size();
+ bool alreadyErasedLine = false;
+
+ int cellCount = 1;
+ for (int i = m_lineData.size(); i < width; i += cellCount) {
+ if (m_outputColor) {
+ int color = lineData[i].Attributes & COLOR_ATTRIBUTE_MASK;
+ if (color != m_remoteColor) {
+ outputSetColor(termLine, color);
+ trimmedLineLength = termLine.size();
+ m_remoteColor = color;
+
+ // All the cells just up to this color change will be output.
+ trimmedCellCount = i;
+ }
+ }
+ unsigned int ch;
+ scanUnicodeScalarValue(&lineData[i], width - i, cellCount, ch);
+ if (ch == ' ') {
+ // Tentatively add this space character. We'll only output it if
+ // we see something interesting after it.
+ termLine.push_back(' ');
+ } else {
+ if (i + cellCount == width) {
+ // We'd like to erase the line after outputting all non-blank
+ // characters, but this doesn't work if the last cell in the
+ // line is non-blank. At the point, the cursor is positioned
+ // just past the end of the line, but in many terminals,
+ // issuing a CSI 0K at that point also erases the last cell in
+ // the line. Work around this behavior by issuing the erase
+ // one character early in that case.
+ if (!m_plainMode) {
+ termLine.append(CSI "0K"); // Erase from cursor to EOL
+ }
+ alreadyErasedLine = true;
+ }
+ ch = fixSpecialCharacters(ch);
+ char enc[4];
+ int enclen = encodeUtf8(enc, ch);
+ if (enclen == 0) {
+ enc[0] = '?';
+ enclen = 1;
+ }
+ termLine.append(enc, enclen);
+ trimmedLineLength = termLine.size();
+
+ // All the cells up to and including this cell will be output.
+ trimmedCellCount = i + cellCount;
+ }
+ }
+
+ if (cursorColumn != -1 && trimmedCellCount > cursorColumn) {
+ // The line content would run past the cursor, so hide it before we
+ // output.
+ hideTerminalCursor();
+ }
+
+ m_output.write(termLine.data(), trimmedLineLength);
+ if (!alreadyErasedLine && !m_plainMode) {
+ m_output.write(CSI "0K"); // Erase from cursor to EOL
+ }
+
+ ASSERT(trimmedCellCount <= width);
+ m_lineData.insert(m_lineData.end(),
+ &lineData[m_lineData.size()],
+ &lineData[trimmedCellCount]);
+ m_remoteColumn = trimmedCellCount;
+}
+
+void Terminal::showTerminalCursor(int column, int64_t line)
+{
+ moveTerminalToLine(line);
+ if (!m_plainMode) {
+ if (m_remoteColumn != column) {
+ char buffer[32];
+ winpty_snprintf(buffer, CSI "%dG", column + 1);
+ m_output.write(buffer);
+ m_lineDataValid = (column == 0);
+ m_lineData.clear();
+ m_remoteColumn = column;
+ }
+ if (m_cursorHidden) {
+ m_output.write(CSI "?25h");
+ m_cursorHidden = false;
+ }
+ }
+}
+
+void Terminal::hideTerminalCursor()
+{
+ if (!m_plainMode) {
+ if (m_cursorHidden) {
+ return;
+ }
+ m_output.write(CSI "?25l");
+ m_cursorHidden = true;
+ }
+}
+
+void Terminal::moveTerminalToLine(int64_t line)
+{
+ if (line == m_remoteLine) {
+ return;
+ }
+
+ // Do not use CPL or CNL. Konsole 2.5.4 does not support Cursor Previous
+ // Line (CPL) -- there are "Undecodable sequence" errors. gnome-terminal
+ // 2.32.0 does handle it. Cursor Next Line (CNL) does nothing if the
+ // cursor is on the last line already.
+
+ hideTerminalCursor();
+
+ if (line < m_remoteLine) {
+ if (m_plainMode) {
+ // We can't backtrack, so instead repeat the lines again.
+ m_output.write("\r\n");
+ m_remoteLine = line;
+ } else {
+ // Backtrack and overwrite previous lines.
+ // CUrsor Up (CUU)
+ char buffer[32];
+ winpty_snprintf(buffer, "\r" CSI "%uA",
+ static_cast<unsigned int>(m_remoteLine - line));
+ m_output.write(buffer);
+ m_remoteLine = line;
+ }
+ } else if (line > m_remoteLine) {
+ while (line > m_remoteLine) {
+ m_output.write("\r\n");
+ m_remoteLine++;
+ }
+ }
+
+ m_lineDataValid = true;
+ m_lineData.clear();
+ m_remoteColumn = 0;
+}
+
+void Terminal::enableMouseMode(bool enabled)
+{
+ if (m_mouseModeEnabled == enabled || m_plainMode) {
+ return;
+ }
+ m_mouseModeEnabled = enabled;
+ if (enabled) {
+ // Start by disabling UTF-8 coordinate mode (1005), just in case we
+ // have a terminal that does not support 1006/1015 modes, and 1005
+ // happens to be enabled. The UTF-8 coordinates can't be unambiguously
+ // decoded.
+ //
+ // Enable basic mouse support first (1000), then try to switch to
+ // button-move mode (1002), then try full mouse-move mode (1003).
+ // Terminals that don't support a mode will be stuck at the highest
+ // mode they do support.
+ //
+ // Enable encoding mode 1015 first, then try to switch to 1006. On
+ // some terminals, both modes will be enabled, but 1006 will have
+ // priority. On other terminals, 1006 wins because it's listed last.
+ //
+ // See misc/MouseInputNotes.txt for details.
+ m_output.write(
+ CSI "?1005l"
+ CSI "?1000h" CSI "?1002h" CSI "?1003h" CSI "?1015h" CSI "?1006h");
+ } else {
+ // Resetting both encoding modes (1006 and 1015) is necessary, but
+ // apparently we only need to use reset on one of the 100[023] modes.
+ // Doing both doesn't hurt.
+ m_output.write(
+ CSI "?1006l" CSI "?1015l" CSI "?1003l" CSI "?1002l" CSI "?1000l");
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Terminal.h b/src/libs/3rdparty/winpty/src/agent/Terminal.h
new file mode 100644
index 0000000000..058eb2650e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Terminal.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef TERMINAL_H
+#define TERMINAL_H
+
+#include <windows.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "Coord.h"
+
+class NamedPipe;
+
+class Terminal
+{
+public:
+ explicit Terminal(NamedPipe &output, bool plainMode, bool outputColor)
+ : m_output(output), m_plainMode(plainMode), m_outputColor(outputColor)
+ {
+ }
+
+ enum SendClearFlag { OmitClear, SendClear };
+ void reset(SendClearFlag sendClearFirst, int64_t newLine);
+ void sendLine(int64_t line, const CHAR_INFO *lineData, int width,
+ int cursorColumn);
+ void showTerminalCursor(int column, int64_t line);
+ void hideTerminalCursor();
+
+private:
+ void moveTerminalToLine(int64_t line);
+
+public:
+ void enableMouseMode(bool enabled);
+
+private:
+ NamedPipe &m_output;
+ int64_t m_remoteLine = 0;
+ int m_remoteColumn = 0;
+ bool m_lineDataValid = true;
+ std::vector<CHAR_INFO> m_lineData;
+ bool m_cursorHidden = false;
+ int m_remoteColor = -1;
+ std::string m_termLineWorkingBuffer;
+ bool m_plainMode = false;
+ bool m_outputColor = true;
+ bool m_mouseModeEnabled = false;
+};
+
+#endif // TERMINAL_H
diff --git a/src/libs/3rdparty/winpty/src/agent/UnicodeEncoding.h b/src/libs/3rdparty/winpty/src/agent/UnicodeEncoding.h
new file mode 100644
index 0000000000..6b0de3eff9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/UnicodeEncoding.h
@@ -0,0 +1,157 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNICODE_ENCODING_H
+#define UNICODE_ENCODING_H
+
+#include <stdint.h>
+
+// Encode the Unicode codepoint with UTF-8. The buffer must be at least 4
+// bytes in size.
+static inline int encodeUtf8(char *out, uint32_t code) {
+ if (code < 0x80) {
+ out[0] = code;
+ return 1;
+ } else if (code < 0x800) {
+ out[0] = ((code >> 6) & 0x1F) | 0xC0;
+ out[1] = ((code >> 0) & 0x3F) | 0x80;
+ return 2;
+ } else if (code < 0x10000) {
+ if (code >= 0xD800 && code <= 0xDFFF) {
+ // The code points 0xD800 to 0xDFFF are reserved for UTF-16
+ // surrogate pairs and do not have an encoding in UTF-8.
+ return 0;
+ }
+ out[0] = ((code >> 12) & 0x0F) | 0xE0;
+ out[1] = ((code >> 6) & 0x3F) | 0x80;
+ out[2] = ((code >> 0) & 0x3F) | 0x80;
+ return 3;
+ } else if (code < 0x110000) {
+ out[0] = ((code >> 18) & 0x07) | 0xF0;
+ out[1] = ((code >> 12) & 0x3F) | 0x80;
+ out[2] = ((code >> 6) & 0x3F) | 0x80;
+ out[3] = ((code >> 0) & 0x3F) | 0x80;
+ return 4;
+ } else {
+ // Encoding error
+ return 0;
+ }
+}
+
+// Encode the Unicode codepoint with UTF-16. The buffer must be large enough
+// to hold the output -- either 1 or 2 elements.
+static inline int encodeUtf16(wchar_t *out, uint32_t code) {
+ if (code < 0x10000) {
+ if (code >= 0xD800 && code <= 0xDFFF) {
+ // The code points 0xD800 to 0xDFFF are reserved for UTF-16
+ // surrogate pairs and do not have an encoding in UTF-16.
+ return 0;
+ }
+ out[0] = code;
+ return 1;
+ } else if (code < 0x110000) {
+ code -= 0x10000;
+ out[0] = 0xD800 | (code >> 10);
+ out[1] = 0xDC00 | (code & 0x3FF);
+ return 2;
+ } else {
+ // Encoding error
+ return 0;
+ }
+}
+
+// Return the byte size of a UTF-8 character using the value of the first
+// byte.
+static inline int utf8CharLength(char firstByte) {
+ // This code would probably be faster if it used __builtin_clz.
+ if ((firstByte & 0x80) == 0) {
+ return 1;
+ } else if ((firstByte & 0xE0) == 0xC0) {
+ return 2;
+ } else if ((firstByte & 0xF0) == 0xE0) {
+ return 3;
+ } else if ((firstByte & 0xF8) == 0xF0) {
+ return 4;
+ } else {
+ // Malformed UTF-8.
+ return 0;
+ }
+}
+
+// The pointer must point to 1-4 bytes, as indicated by the first byte.
+// Returns -1 on decoding error.
+static inline uint32_t decodeUtf8(const char *in) {
+ const uint32_t kInvalid = static_cast<uint32_t>(-1);
+ switch (utf8CharLength(in[0])) {
+ case 1: {
+ return in[0];
+ }
+ case 2: {
+ if ((in[1] & 0xC0) != 0x80) {
+ return kInvalid;
+ }
+ uint32_t tmp = 0;
+ tmp = (in[0] & 0x1F) << 6;
+ tmp |= (in[1] & 0x3F);
+ return tmp <= 0x7F ? kInvalid : tmp;
+ }
+ case 3: {
+ if ((in[1] & 0xC0) != 0x80 ||
+ (in[2] & 0xC0) != 0x80) {
+ return kInvalid;
+ }
+ uint32_t tmp = 0;
+ tmp = (in[0] & 0x0F) << 12;
+ tmp |= (in[1] & 0x3F) << 6;
+ tmp |= (in[2] & 0x3F);
+ if (tmp <= 0x07FF || (tmp >= 0xD800 && tmp <= 0xDFFF)) {
+ return kInvalid;
+ } else {
+ return tmp;
+ }
+ }
+ case 4: {
+ if ((in[1] & 0xC0) != 0x80 ||
+ (in[2] & 0xC0) != 0x80 ||
+ (in[3] & 0xC0) != 0x80) {
+ return kInvalid;
+ }
+ uint32_t tmp = 0;
+ tmp = (in[0] & 0x07) << 18;
+ tmp |= (in[1] & 0x3F) << 12;
+ tmp |= (in[2] & 0x3F) << 6;
+ tmp |= (in[3] & 0x3F);
+ if (tmp <= 0xFFFF || tmp > 0x10FFFF) {
+ return kInvalid;
+ } else {
+ return tmp;
+ }
+ }
+ default: {
+ return kInvalid;
+ }
+ }
+}
+
+static inline uint32_t decodeSurrogatePair(wchar_t ch1, wchar_t ch2) {
+ return ((ch1 - 0xD800) << 10) + (ch2 - 0xDC00) + 0x10000;
+}
+
+#endif // UNICODE_ENCODING_H
diff --git a/src/libs/3rdparty/winpty/src/agent/UnicodeEncodingTest.cc b/src/libs/3rdparty/winpty/src/agent/UnicodeEncodingTest.cc
new file mode 100644
index 0000000000..cd4abeb191
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/UnicodeEncodingTest.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+// Encode every code-point using this module and verify that it matches the
+// encoding generated using Windows WideCharToMultiByte.
+
+#include "UnicodeEncoding.h"
+
+#include <windows.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+static void correctnessByCode()
+{
+ char mbstr1[4];
+ char mbstr2[4];
+ wchar_t wch[2];
+ for (unsigned int code = 0; code < 0x110000; ++code) {
+
+ // Surrogate pair reserved region.
+ const bool isReserved = (code >= 0xD800 && code <= 0xDFFF);
+
+ int mblen1 = encodeUtf8(mbstr1, code);
+ if (isReserved ? mblen1 != 0 : mblen1 <= 0) {
+ printf("Error: 0x%04X: mblen1=%d\n", code, mblen1);
+ continue;
+ }
+
+ int wlen = encodeUtf16(wch, code);
+ if (isReserved ? wlen != 0 : wlen <= 0) {
+ printf("Error: 0x%04X: wlen=%d\n", code, wlen);
+ continue;
+ }
+
+ if (isReserved) {
+ continue;
+ }
+
+ if (mblen1 != utf8CharLength(mbstr1[0])) {
+ printf("Error: 0x%04X: mblen1=%d, utf8CharLength(mbstr1[0])=%d\n",
+ code, mblen1, utf8CharLength(mbstr1[0]));
+ continue;
+ }
+
+ if (code != decodeUtf8(mbstr1)) {
+ printf("Error: 0x%04X: decodeUtf8(mbstr1)=%u\n",
+ code, decodeUtf8(mbstr1));
+ continue;
+ }
+
+ int mblen2 = WideCharToMultiByte(CP_UTF8, 0, wch, wlen, mbstr2, 4, NULL, NULL);
+ if (mblen1 != mblen2) {
+ printf("Error: 0x%04X: mblen1=%d, mblen2=%d\n", code, mblen1, mblen2);
+ continue;
+ }
+
+ if (memcmp(mbstr1, mbstr2, mblen1) != 0) {
+ printf("Error: 0x%04x: encodings are different\n", code);
+ continue;
+ }
+ }
+}
+
+static const char *encodingStr(char (&output)[128], char (&buf)[4])
+{
+ sprintf(output, "Encoding %02X %02X %02X %02X",
+ static_cast<uint8_t>(buf[0]),
+ static_cast<uint8_t>(buf[1]),
+ static_cast<uint8_t>(buf[2]),
+ static_cast<uint8_t>(buf[3]));
+ return output;
+}
+
+// This test can take a couple of minutes to run.
+static void correctnessByUtf8Encoding()
+{
+ for (uint64_t encoding = 0; encoding <= 0xFFFFFFFF; ++encoding) {
+
+ char mb[4];
+ mb[0] = encoding;
+ mb[1] = encoding >> 8;
+ mb[2] = encoding >> 16;
+ mb[3] = encoding >> 24;
+
+ const int mblen = utf8CharLength(mb[0]);
+ if (mblen == 0) {
+ continue;
+ }
+
+ // Test this module.
+ const uint32_t code1 = decodeUtf8(mb);
+ wchar_t ws1[2] = {};
+ const int wslen1 = encodeUtf16(ws1, code1);
+
+ // Test using Windows. We can't decode a codepoint directly; we have
+ // to do UTF8->UTF16, then decode the surrogate pair.
+ wchar_t ws2[2] = {};
+ const int wslen2 = MultiByteToWideChar(
+ CP_UTF8, MB_ERR_INVALID_CHARS, mb, mblen, ws2, 2);
+ const uint32_t code2 =
+ (wslen2 == 1 ? ws2[0] :
+ wslen2 == 2 ? decodeSurrogatePair(ws2[0], ws2[1]) :
+ static_cast<uint32_t>(-1));
+
+ // Verify that the two implementations match.
+ char prefix[128];
+ if (code1 != code2) {
+ printf("%s: code1=0x%04x code2=0x%04x\n",
+ encodingStr(prefix, mb),
+ code1, code2);
+ continue;
+ }
+ if (wslen1 != wslen2) {
+ printf("%s: wslen1=%d wslen2=%d\n",
+ encodingStr(prefix, mb),
+ wslen1, wslen2);
+ continue;
+ }
+ if (memcmp(ws1, ws2, wslen1 * sizeof(wchar_t)) != 0) {
+ printf("%s: ws1 != ws2\n", encodingStr(prefix, mb));
+ continue;
+ }
+ }
+}
+
+wchar_t g_wch_TEST[] = { 0xD840, 0xDC00 };
+char g_ch_TEST[4];
+wchar_t *volatile g_pwch = g_wch_TEST;
+char *volatile g_pch = g_ch_TEST;
+unsigned int volatile g_code = 0xA2000;
+
+static void performance()
+{
+ {
+ clock_t start = clock();
+ for (long long i = 0; i < 250000000LL; ++i) {
+ int mblen = WideCharToMultiByte(CP_UTF8, 0, g_pwch, 2, g_pch, 4, NULL, NULL);
+ assert(mblen == 4);
+ }
+ clock_t stop = clock();
+ printf("%.3fns per char\n", (double)(stop - start) / CLOCKS_PER_SEC * 4.0);
+ }
+
+ {
+ clock_t start = clock();
+ for (long long i = 0; i < 3000000000LL; ++i) {
+ int mblen = encodeUtf8(g_pch, g_code);
+ assert(mblen == 4);
+ }
+ clock_t stop = clock();
+ printf("%.3fns per char\n", (double)(stop - start) / CLOCKS_PER_SEC / 3.0);
+ }
+}
+
+int main()
+{
+ printf("Testing correctnessByCode...\n");
+ fflush(stdout);
+ correctnessByCode();
+
+ printf("Testing correctnessByUtf8Encoding... (may take a couple minutes)\n");
+ fflush(stdout);
+ correctnessByUtf8Encoding();
+
+ printf("Testing performance...\n");
+ fflush(stdout);
+ performance();
+
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Win32Console.cc b/src/libs/3rdparty/winpty/src/agent/Win32Console.cc
new file mode 100644
index 0000000000..d53de021f5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Win32Console.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Win32Console.h"
+
+#include <windows.h>
+#include <wchar.h>
+
+#include <string>
+
+#include "../shared/DebugClient.h"
+#include "../shared/WinptyAssert.h"
+
+Win32Console::Win32Console() : m_titleWorkBuf(16)
+{
+ // The console window must be non-NULL. It is used for two purposes:
+ // (1) "Freezing" the console to detect the exact number of lines that
+ // have scrolled.
+ // (2) Killing processes attached to the console, by posting a WM_CLOSE
+ // message to the console window.
+ m_hwnd = GetConsoleWindow();
+ ASSERT(m_hwnd != nullptr);
+}
+
+std::wstring Win32Console::title()
+{
+ while (true) {
+ // Calling GetConsoleTitleW is tricky, because its behavior changed
+ // from XP->Vista, then again from Win7->Win8. The Vista+Win7 behavior
+ // is especially broken.
+ //
+ // The MSDN documentation documents nSize as the "size of the buffer
+ // pointed to by the lpConsoleTitle parameter, in characters" and the
+ // successful return value as "the length of the console window's
+ // title, in characters."
+ //
+ // On XP, the function returns the title length, AFTER truncation
+ // (excluding the NUL terminator). If the title is blank, the API
+ // returns 0 and does not NUL-terminate the buffer. To accommodate
+ // XP, the function must:
+ // * Terminate the buffer itself.
+ // * Double the size of the title buffer in a loop.
+ //
+ // On Vista and up, the function returns the non-truncated title
+ // length (excluding the NUL terminator).
+ //
+ // On Vista and Windows 7, there is a bug where the buffer size is
+ // interpreted as a byte count rather than a wchar_t count. To
+ // work around this, we must pass GetConsoleTitleW a buffer that is
+ // twice as large as what is actually needed.
+ //
+ // See misc/*/Test_GetConsoleTitleW.cc for tests demonstrating Windows'
+ // behavior.
+
+ DWORD count = GetConsoleTitleW(m_titleWorkBuf.data(),
+ m_titleWorkBuf.size());
+ const size_t needed = (count + 1) * sizeof(wchar_t);
+ if (m_titleWorkBuf.size() < needed) {
+ m_titleWorkBuf.resize(needed);
+ continue;
+ }
+ m_titleWorkBuf[count] = L'\0';
+ return m_titleWorkBuf.data();
+ }
+}
+
+void Win32Console::setTitle(const std::wstring &title)
+{
+ if (!SetConsoleTitleW(title.c_str())) {
+ trace("SetConsoleTitleW failed");
+ }
+}
+
+void Win32Console::setFrozen(bool frozen) {
+ const int SC_CONSOLE_MARK = 0xFFF2;
+ const int SC_CONSOLE_SELECT_ALL = 0xFFF5;
+ if (frozen == m_frozen) {
+ // Do nothing.
+ } else if (frozen) {
+ // Enter selection mode by activating either Mark or SelectAll.
+ const int command = m_freezeUsesMark ? SC_CONSOLE_MARK
+ : SC_CONSOLE_SELECT_ALL;
+ SendMessage(m_hwnd, WM_SYSCOMMAND, command, 0);
+ m_frozen = true;
+ } else {
+ // Send Escape to cancel the selection.
+ SendMessage(m_hwnd, WM_CHAR, 27, 0x00010001);
+ m_frozen = false;
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Win32Console.h b/src/libs/3rdparty/winpty/src/agent/Win32Console.h
new file mode 100644
index 0000000000..ed83877e99
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Win32Console.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_WIN32_CONSOLE_H
+#define AGENT_WIN32_CONSOLE_H
+
+#include <windows.h>
+
+#include <string>
+#include <vector>
+
+class Win32Console
+{
+public:
+ class FreezeGuard {
+ public:
+ FreezeGuard(Win32Console &console, bool frozen) :
+ m_console(console), m_previous(console.frozen()) {
+ m_console.setFrozen(frozen);
+ }
+ ~FreezeGuard() {
+ m_console.setFrozen(m_previous);
+ }
+ FreezeGuard(const FreezeGuard &other) = delete;
+ FreezeGuard &operator=(const FreezeGuard &other) = delete;
+ private:
+ Win32Console &m_console;
+ bool m_previous;
+ };
+
+ Win32Console();
+
+ HWND hwnd() { return m_hwnd; }
+ std::wstring title();
+ void setTitle(const std::wstring &title);
+ void setFreezeUsesMark(bool useMark) { m_freezeUsesMark = useMark; }
+ void setNewW10(bool isNewW10) { m_isNewW10 = isNewW10; }
+ bool isNewW10() { return m_isNewW10; }
+ void setFrozen(bool frozen=true);
+ bool frozen() { return m_frozen; }
+
+private:
+ HWND m_hwnd = nullptr;
+ bool m_frozen = false;
+ bool m_freezeUsesMark = false;
+ bool m_isNewW10 = false;
+ std::vector<wchar_t> m_titleWorkBuf;
+};
+
+#endif // AGENT_WIN32_CONSOLE_H
diff --git a/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.cc b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.cc
new file mode 100644
index 0000000000..ed93f4081f
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Win32ConsoleBuffer.h"
+
+#include <windows.h>
+
+#include "../shared/DebugClient.h"
+#include "../shared/StringBuilder.h"
+#include "../shared/WinptyAssert.h"
+
+std::unique_ptr<Win32ConsoleBuffer> Win32ConsoleBuffer::openStdout() {
+ return std::unique_ptr<Win32ConsoleBuffer>(
+ new Win32ConsoleBuffer(GetStdHandle(STD_OUTPUT_HANDLE), false));
+}
+
+std::unique_ptr<Win32ConsoleBuffer> Win32ConsoleBuffer::openConout() {
+ const HANDLE conout = CreateFileW(L"CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+ return std::unique_ptr<Win32ConsoleBuffer>(
+ new Win32ConsoleBuffer(conout, true));
+}
+
+std::unique_ptr<Win32ConsoleBuffer> Win32ConsoleBuffer::createErrorBuffer() {
+ SECURITY_ATTRIBUTES sa = {};
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ const HANDLE conout =
+ CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sa,
+ CONSOLE_TEXTMODE_BUFFER,
+ nullptr);
+ ASSERT(conout != INVALID_HANDLE_VALUE);
+ return std::unique_ptr<Win32ConsoleBuffer>(
+ new Win32ConsoleBuffer(conout, true));
+}
+
+HANDLE Win32ConsoleBuffer::conout() {
+ return m_conout;
+}
+
+void Win32ConsoleBuffer::clearLines(
+ int row,
+ int count,
+ const ConsoleScreenBufferInfo &info) {
+ // TODO: error handling
+ const int width = info.bufferSize().X;
+ DWORD actual = 0;
+ if (!FillConsoleOutputCharacterW(
+ m_conout, L' ', width * count, Coord(0, row),
+ &actual) || static_cast<int>(actual) != width * count) {
+ trace("FillConsoleOutputCharacterW failed");
+ }
+ if (!FillConsoleOutputAttribute(
+ m_conout, kDefaultAttributes, width * count, Coord(0, row),
+ &actual) || static_cast<int>(actual) != width * count) {
+ trace("FillConsoleOutputAttribute failed");
+ }
+}
+
+void Win32ConsoleBuffer::clearAllLines(const ConsoleScreenBufferInfo &info) {
+ clearLines(0, info.bufferSize().Y, info);
+}
+
+ConsoleScreenBufferInfo Win32ConsoleBuffer::bufferInfo() {
+ // TODO: error handling
+ ConsoleScreenBufferInfo info;
+ if (!GetConsoleScreenBufferInfo(m_conout, &info)) {
+ trace("GetConsoleScreenBufferInfo failed");
+ }
+ return info;
+}
+
+Coord Win32ConsoleBuffer::bufferSize() {
+ return bufferInfo().bufferSize();
+}
+
+SmallRect Win32ConsoleBuffer::windowRect() {
+ return bufferInfo().windowRect();
+}
+
+bool Win32ConsoleBuffer::resizeBufferRange(const Coord &initialSize,
+ Coord &finalSize) {
+ if (SetConsoleScreenBufferSize(m_conout, initialSize)) {
+ finalSize = initialSize;
+ return true;
+ }
+ // The font might be too small to accommodate a very narrow console window.
+ // In that case, rather than simply give up, it's better to try wider
+ // buffer sizes until the call succeeds.
+ Coord size = initialSize;
+ while (size.X < 20) {
+ size.X++;
+ if (SetConsoleScreenBufferSize(m_conout, size)) {
+ finalSize = size;
+ trace("SetConsoleScreenBufferSize: initial size (%d,%d) failed, "
+ "but wider size (%d,%d) succeeded",
+ initialSize.X, initialSize.Y,
+ finalSize.X, finalSize.Y);
+ return true;
+ }
+ }
+ trace("SetConsoleScreenBufferSize failed: "
+ "tried (%d,%d) through (%d,%d)",
+ initialSize.X, initialSize.Y,
+ size.X, size.Y);
+ return false;
+}
+
+void Win32ConsoleBuffer::resizeBuffer(const Coord &size) {
+ // TODO: error handling
+ if (!SetConsoleScreenBufferSize(m_conout, size)) {
+ trace("SetConsoleScreenBufferSize failed: size=(%d,%d)",
+ size.X, size.Y);
+ }
+}
+
+void Win32ConsoleBuffer::moveWindow(const SmallRect &rect) {
+ // TODO: error handling
+ if (!SetConsoleWindowInfo(m_conout, TRUE, &rect)) {
+ trace("SetConsoleWindowInfo failed");
+ }
+}
+
+Coord Win32ConsoleBuffer::cursorPosition() {
+ return bufferInfo().dwCursorPosition;
+}
+
+void Win32ConsoleBuffer::setCursorPosition(const Coord &coord) {
+ // TODO: error handling
+ if (!SetConsoleCursorPosition(m_conout, coord)) {
+ trace("SetConsoleCursorPosition failed");
+ }
+}
+
+void Win32ConsoleBuffer::read(const SmallRect &rect, CHAR_INFO *data) {
+ // TODO: error handling
+ SmallRect tmp(rect);
+ if (!ReadConsoleOutputW(m_conout, data, rect.size(), Coord(), &tmp) &&
+ isTracingEnabled()) {
+ StringBuilder sb(256);
+ auto outStruct = [&](const SMALL_RECT &sr) {
+ sb << "{L=" << sr.Left << ",T=" << sr.Top
+ << ",R=" << sr.Right << ",B=" << sr.Bottom << '}';
+ };
+ sb << "Win32ConsoleBuffer::read: ReadConsoleOutput failed: readRegion=";
+ outStruct(rect);
+ CONSOLE_SCREEN_BUFFER_INFO info = {};
+ if (GetConsoleScreenBufferInfo(m_conout, &info)) {
+ sb << ", dwSize=(" << info.dwSize.X << ',' << info.dwSize.Y
+ << "), srWindow=";
+ outStruct(info.srWindow);
+ } else {
+ sb << ", GetConsoleScreenBufferInfo also failed";
+ }
+ trace("%s", sb.c_str());
+ }
+}
+
+void Win32ConsoleBuffer::write(const SmallRect &rect, const CHAR_INFO *data) {
+ // TODO: error handling
+ SmallRect tmp(rect);
+ if (!WriteConsoleOutputW(m_conout, data, rect.size(), Coord(), &tmp)) {
+ trace("WriteConsoleOutput failed");
+ }
+}
+
+void Win32ConsoleBuffer::setTextAttribute(WORD attributes) {
+ if (!SetConsoleTextAttribute(m_conout, attributes)) {
+ trace("SetConsoleTextAttribute failed");
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.h b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.h
new file mode 100644
index 0000000000..a68d8d304f
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/Win32ConsoleBuffer.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef AGENT_WIN32_CONSOLE_BUFFER_H
+#define AGENT_WIN32_CONSOLE_BUFFER_H
+
+#include <windows.h>
+
+#include <string.h>
+
+#include <memory>
+
+#include "Coord.h"
+#include "SmallRect.h"
+
+class ConsoleScreenBufferInfo : public CONSOLE_SCREEN_BUFFER_INFO {
+public:
+ ConsoleScreenBufferInfo()
+ {
+ memset(this, 0, sizeof(*this));
+ }
+
+ Coord bufferSize() const { return dwSize; }
+ SmallRect windowRect() const { return srWindow; }
+ Coord cursorPosition() const { return dwCursorPosition; }
+};
+
+class Win32ConsoleBuffer {
+private:
+ Win32ConsoleBuffer(HANDLE conout, bool owned) :
+ m_conout(conout), m_owned(owned)
+ {
+ }
+
+public:
+ static const int kDefaultAttributes = 7;
+
+ ~Win32ConsoleBuffer() {
+ if (m_owned) {
+ CloseHandle(m_conout);
+ }
+ }
+
+ static std::unique_ptr<Win32ConsoleBuffer> openStdout();
+ static std::unique_ptr<Win32ConsoleBuffer> openConout();
+ static std::unique_ptr<Win32ConsoleBuffer> createErrorBuffer();
+
+ Win32ConsoleBuffer(const Win32ConsoleBuffer &other) = delete;
+ Win32ConsoleBuffer &operator=(const Win32ConsoleBuffer &other) = delete;
+
+ HANDLE conout();
+ void clearLines(int row, int count, const ConsoleScreenBufferInfo &info);
+ void clearAllLines(const ConsoleScreenBufferInfo &info);
+
+ // Buffer and window sizes.
+ ConsoleScreenBufferInfo bufferInfo();
+ Coord bufferSize();
+ SmallRect windowRect();
+ void resizeBuffer(const Coord &size);
+ bool resizeBufferRange(const Coord &initialSize, Coord &finalSize);
+ bool resizeBufferRange(const Coord &initialSize) {
+ Coord dummy;
+ return resizeBufferRange(initialSize, dummy);
+ }
+ void moveWindow(const SmallRect &rect);
+
+ // Cursor.
+ Coord cursorPosition();
+ void setCursorPosition(const Coord &point);
+
+ // Screen content.
+ void read(const SmallRect &rect, CHAR_INFO *data);
+ void write(const SmallRect &rect, const CHAR_INFO *data);
+
+ void setTextAttribute(WORD attributes);
+
+private:
+ HANDLE m_conout = nullptr;
+ bool m_owned = false;
+};
+
+#endif // AGENT_WIN32_CONSOLE_BUFFER_H
diff --git a/src/libs/3rdparty/winpty/src/agent/main.cc b/src/libs/3rdparty/winpty/src/agent/main.cc
new file mode 100644
index 0000000000..427cb3a3aa
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/main.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <wchar.h>
+#include <shellapi.h>
+
+#include "../shared/StringUtil.h"
+#include "../shared/WindowsVersion.h"
+#include "../shared/WinptyAssert.h"
+#include "../shared/WinptyVersion.h"
+
+#include "Agent.h"
+#include "AgentCreateDesktop.h"
+#include "DebugShowInput.h"
+
+const char USAGE[] =
+"Usage: %ls controlPipeName flags mouseMode cols rows\n"
+"Usage: %ls controlPipeName --create-desktop\n"
+"\n"
+"Ordinarily, this program is launched by winpty.dll and is not directly\n"
+"useful to winpty users. However, it also has options intended for\n"
+"debugging winpty.\n"
+"\n"
+"Usage: %ls [options]\n"
+"\n"
+"Options:\n"
+" --show-input [--with-mouse] [--escape-input]\n"
+" Dump INPUT_RECORDs from the console input buffer\n"
+" --with-mouse: Include MOUSE_INPUT_RECORDs in the dump\n"
+" output\n"
+" --escape-input: Direct the new Windows 10 console to use\n"
+" escape sequences for input\n"
+" --version Print the winpty version\n";
+
+static uint64_t winpty_atoi64(const char *str) {
+ return strtoll(str, NULL, 10);
+}
+
+int main() {
+ dumpWindowsVersion();
+ dumpVersionToTrace();
+
+ // Technically, we should free the CommandLineToArgvW return value using
+ // a single call to LocalFree, but the call will never actually happen in
+ // the normal case.
+ int argc = 0;
+ wchar_t *cmdline = GetCommandLineW();
+ ASSERT(cmdline != nullptr && "GetCommandLineW returned NULL");
+ wchar_t **argv = CommandLineToArgvW(cmdline, &argc);
+ ASSERT(argv != nullptr && "CommandLineToArgvW returned NULL");
+
+ if (argc == 2 && !wcscmp(argv[1], L"--version")) {
+ dumpVersionToStdout();
+ return 0;
+ }
+
+ if (argc >= 2 && !wcscmp(argv[1], L"--show-input")) {
+ bool withMouse = false;
+ bool escapeInput = false;
+ for (int i = 2; i < argc; ++i) {
+ if (!wcscmp(argv[i], L"--with-mouse")) {
+ withMouse = true;
+ } else if (!wcscmp(argv[i], L"--escape-input")) {
+ escapeInput = true;
+ } else {
+ fprintf(stderr, "Unrecognized --show-input option: %ls\n",
+ argv[i]);
+ return 1;
+ }
+ }
+ debugShowInput(withMouse, escapeInput);
+ return 0;
+ }
+
+ if (argc == 3 && !wcscmp(argv[2], L"--create-desktop")) {
+ handleCreateDesktop(argv[1]);
+ return 0;
+ }
+
+ if (argc != 6) {
+ fprintf(stderr, USAGE, argv[0], argv[0], argv[0]);
+ return 1;
+ }
+
+ Agent agent(argv[1],
+ winpty_atoi64(utf8FromWide(argv[2]).c_str()),
+ atoi(utf8FromWide(argv[3]).c_str()),
+ atoi(utf8FromWide(argv[4]).c_str()),
+ atoi(utf8FromWide(argv[5]).c_str()));
+ agent.run();
+
+ // The Agent destructor shouldn't return, but if it does, exit
+ // unsuccessfully.
+ return 1;
+}
diff --git a/src/libs/3rdparty/winpty/src/agent/subdir.mk b/src/libs/3rdparty/winpty/src/agent/subdir.mk
new file mode 100644
index 0000000000..1c7d37e3e5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/agent/subdir.mk
@@ -0,0 +1,61 @@
+# Copyright (c) 2011-2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+ALL_TARGETS += build/winpty-agent.exe
+
+$(eval $(call def_mingw_target,agent,-DWINPTY_AGENT_ASSERT))
+
+AGENT_OBJECTS = \
+ build/agent/agent/Agent.o \
+ build/agent/agent/AgentCreateDesktop.o \
+ build/agent/agent/ConsoleFont.o \
+ build/agent/agent/ConsoleInput.o \
+ build/agent/agent/ConsoleInputReencoding.o \
+ build/agent/agent/ConsoleLine.o \
+ build/agent/agent/DebugShowInput.o \
+ build/agent/agent/DefaultInputMap.o \
+ build/agent/agent/EventLoop.o \
+ build/agent/agent/InputMap.o \
+ build/agent/agent/LargeConsoleRead.o \
+ build/agent/agent/NamedPipe.o \
+ build/agent/agent/Scraper.o \
+ build/agent/agent/Terminal.o \
+ build/agent/agent/Win32Console.o \
+ build/agent/agent/Win32ConsoleBuffer.o \
+ build/agent/agent/main.o \
+ build/agent/shared/BackgroundDesktop.o \
+ build/agent/shared/Buffer.o \
+ build/agent/shared/DebugClient.o \
+ build/agent/shared/GenRandom.o \
+ build/agent/shared/OwnedHandle.o \
+ build/agent/shared/StringUtil.o \
+ build/agent/shared/WindowsSecurity.o \
+ build/agent/shared/WindowsVersion.o \
+ build/agent/shared/WinptyAssert.o \
+ build/agent/shared/WinptyException.o \
+ build/agent/shared/WinptyVersion.o
+
+build/agent/shared/WinptyVersion.o : build/gen/GenVersion.h
+
+build/winpty-agent.exe : $(AGENT_OBJECTS)
+ $(info Linking $@)
+ @$(MINGW_CXX) $(MINGW_LDFLAGS) -o $@ $^
+
+-include $(AGENT_OBJECTS:.o=.d)
diff --git a/src/libs/3rdparty/winpty/src/configurations.gypi b/src/libs/3rdparty/winpty/src/configurations.gypi
new file mode 100644
index 0000000000..e990a60338
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/configurations.gypi
@@ -0,0 +1,60 @@
+# By default gyp/msbuild build for 32-bit Windows. This gyp include file
+# defines configurations for both 32-bit and 64-bit Windows. To use it, run:
+#
+# C:\...\winpty\src>gyp -I configurations.gypi
+#
+# This command generates Visual Studio project files with a Release
+# configuration and two Platforms--Win32 and x64. Both can be built:
+#
+# C:\...\winpty\src>msbuild winpty.sln /p:Platform=Win32
+# C:\...\winpty\src>msbuild winpty.sln /p:Platform=x64
+#
+# The output is placed in:
+#
+# C:\...\winpty\src\Release\Win32
+# C:\...\winpty\src\Release\x64
+#
+# Windows XP note: By default, the project files will use the default "toolset"
+# for the given MSVC version. For MSVC 2013 and MSVC 2015, the default toolset
+# generates binaries that do not run on Windows XP. To target Windows XP,
+# select the XP-specific toolset by passing
+# -D WINPTY_MSBUILD_TOOLSET={v120_xp,v140_xp} to gyp (v120_xp == MSVC 2013,
+# v140_xp == MSVC 2015). Unfortunately, it isn't possible to have a single
+# project file with configurations for both XP and post-XP. This seems to be a
+# limitation of the MSVC project file format.
+#
+# This file is not included by default, because I suspect it would interfere
+# with node-gyp, which has a different system for building 32-vs-64-bit
+# binaries. It uses a common.gypi, and the project files it generates can only
+# build a single architecture, the output paths are not differentiated by
+# architecture.
+
+{
+ 'variables': {
+ 'WINPTY_MSBUILD_TOOLSET%': '',
+ },
+ 'target_defaults': {
+ 'default_configuration': 'Release_Win32',
+ 'configurations': {
+ 'Release_Win32': {
+ 'msvs_configuration_platform': 'Win32',
+ },
+ 'Release_x64': {
+ 'msvs_configuration_platform': 'x64',
+ },
+ },
+ 'msvs_configuration_attributes': {
+ 'OutputDirectory': '$(SolutionDir)$(ConfigurationName)\\$(Platform)',
+ 'IntermediateDirectory': '$(ConfigurationName)\\$(Platform)\\obj\\$(ProjectName)',
+ },
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '1', # /SUBSYSTEM:CONSOLE
+ },
+ 'VCCLCompilerTool': {
+ 'RuntimeLibrary': '0', # MultiThreaded (/MT)
+ },
+ },
+ 'msbuild_toolset' : '<(WINPTY_MSBUILD_TOOLSET)',
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/debugserver/DebugServer.cc b/src/libs/3rdparty/winpty/src/debugserver/DebugServer.cc
new file mode 100644
index 0000000000..353d31c1c6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/debugserver/DebugServer.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <cstdio>
+#include <cstdlib>
+
+#include <windows.h>
+
+#include "../shared/WindowsSecurity.h"
+#include "../shared/WinptyException.h"
+
+const wchar_t *kPipeName = L"\\\\.\\pipe\\DebugServer";
+
+// A message may not be larger than this size.
+const int MSG_SIZE = 4096;
+
+static void usage(const char *program, int code) {
+ printf("Usage: %s [--everyone]\n"
+ "\n"
+ "Creates the named pipe %ls and reads messages. Prints each\n"
+ "message to stdout. By default, only the current user can send messages.\n"
+ "Pass --everyone to let anyone send a message.\n"
+ "\n"
+ "Use the WINPTY_DEBUG environment variable to enable winpty trace output.\n"
+ "(e.g. WINPTY_DEBUG=trace for the default trace output.) Set WINPTYDBG=1\n"
+ "to enable trace with older winpty versions.\n",
+ program, kPipeName);
+ exit(code);
+}
+
+int main(int argc, char *argv[]) {
+ bool everyone = false;
+ for (int i = 1; i < argc; ++i) {
+ std::string arg = argv[i];
+ if (arg == "--everyone") {
+ everyone = true;
+ } else if (arg == "-h" || arg == "--help") {
+ usage(argv[0], 0);
+ } else {
+ usage(argv[0], 1);
+ }
+ }
+
+ SecurityDescriptor sd;
+ PSECURITY_ATTRIBUTES psa = nullptr;
+ SECURITY_ATTRIBUTES sa = {};
+ if (everyone) {
+ try {
+ sd = createPipeSecurityDescriptorOwnerFullControlEveryoneWrite();
+ } catch (const WinptyException &e) {
+ fprintf(stderr,
+ "error creating security descriptor: %ls\n", e.what());
+ exit(1);
+ }
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = sd.get();
+ psa = &sa;
+ }
+
+ HANDLE serverPipe = CreateNamedPipeW(
+ kPipeName,
+ /*dwOpenMode=*/PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
+ /*dwPipeMode=*/PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE |
+ rejectRemoteClientsPipeFlag(),
+ /*nMaxInstances=*/1,
+ /*nOutBufferSize=*/MSG_SIZE,
+ /*nInBufferSize=*/MSG_SIZE,
+ /*nDefaultTimeOut=*/10 * 1000,
+ psa);
+
+ if (serverPipe == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "error: could not create %ls pipe: error %u\n",
+ kPipeName, static_cast<unsigned>(GetLastError()));
+ exit(1);
+ }
+
+ char msgBuffer[MSG_SIZE + 1];
+
+ while (true) {
+ if (!ConnectNamedPipe(serverPipe, nullptr)) {
+ fprintf(stderr, "error: ConnectNamedPipe failed\n");
+ fflush(stderr);
+ exit(1);
+ }
+ DWORD bytesRead = 0;
+ if (!ReadFile(serverPipe, msgBuffer, MSG_SIZE, &bytesRead, nullptr)) {
+ fprintf(stderr, "error: ReadFile on pipe failed\n");
+ fflush(stderr);
+ DisconnectNamedPipe(serverPipe);
+ continue;
+ }
+ msgBuffer[bytesRead] = '\n';
+ fwrite(msgBuffer, 1, bytesRead + 1, stdout);
+ fflush(stdout);
+
+ DWORD bytesWritten = 0;
+ WriteFile(serverPipe, "OK", 2, &bytesWritten, nullptr);
+ DisconnectNamedPipe(serverPipe);
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/debugserver/subdir.mk b/src/libs/3rdparty/winpty/src/debugserver/subdir.mk
new file mode 100644
index 0000000000..beed1bd597
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/debugserver/subdir.mk
@@ -0,0 +1,41 @@
+# Copyright (c) 2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+ALL_TARGETS += build/winpty-debugserver.exe
+
+$(eval $(call def_mingw_target,debugserver,))
+
+DEBUGSERVER_OBJECTS = \
+ build/debugserver/debugserver/DebugServer.o \
+ build/debugserver/shared/DebugClient.o \
+ build/debugserver/shared/OwnedHandle.o \
+ build/debugserver/shared/StringUtil.o \
+ build/debugserver/shared/WindowsSecurity.o \
+ build/debugserver/shared/WindowsVersion.o \
+ build/debugserver/shared/WinptyAssert.o \
+ build/debugserver/shared/WinptyException.o
+
+build/debugserver/shared/WindowsVersion.o : build/gen/GenVersion.h
+
+build/winpty-debugserver.exe : $(DEBUGSERVER_OBJECTS)
+ $(info Linking $@)
+ @$(MINGW_CXX) $(MINGW_LDFLAGS) -o $@ $^
+
+-include $(DEBUGSERVER_OBJECTS:.o=.d)
diff --git a/src/libs/3rdparty/winpty/src/include/winpty.h b/src/libs/3rdparty/winpty/src/include/winpty.h
new file mode 100644
index 0000000000..fdfe4bca21
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/include/winpty.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2011-2016 Ryan Prichard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef WINPTY_H
+#define WINPTY_H
+
+#include <windows.h>
+
+#include "winpty_constants.h"
+
+/* On 32-bit Windows, winpty functions have the default __cdecl (not __stdcall)
+ * calling convention. (64-bit Windows has only a single calling convention.)
+ * When compiled with __declspec(dllexport), with either MinGW or MSVC, the
+ * winpty functions are unadorned--no underscore prefix or '@nn' suffix--so
+ * GetProcAddress can be used easily. */
+#ifdef COMPILING_WINPTY_DLL
+#define WINPTY_API __declspec(dllexport)
+#else
+#define WINPTY_API __declspec(dllimport)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The winpty API uses wide characters, instead of UTF-8, to avoid conversion
+ * complications related to surrogates. Windows generally tolerates unpaired
+ * surrogates in text, which makes conversion to and from UTF-8 ambiguous and
+ * complicated. (There are different UTF-8 variants that deal with UTF-16
+ * surrogates differently.) */
+
+
+
+/*****************************************************************************
+ * Error handling. */
+
+/* All the APIs have an optional winpty_error_t output parameter. If a
+ * non-NULL argument is specified, then either the API writes NULL to the
+ * value (on success) or writes a newly allocated winpty_error_t object. The
+ * object must be freed using winpty_error_free. */
+
+/* An error object. */
+typedef struct winpty_error_s winpty_error_t;
+typedef winpty_error_t *winpty_error_ptr_t;
+
+/* An error code -- one of WINPTY_ERROR_xxx. */
+typedef DWORD winpty_result_t;
+
+/* Gets the error code from the error object. */
+WINPTY_API winpty_result_t winpty_error_code(winpty_error_ptr_t err);
+
+/* Returns a textual representation of the error. The string is freed when
+ * the error is freed. */
+WINPTY_API LPCWSTR winpty_error_msg(winpty_error_ptr_t err);
+
+/* Free the error object. Every error returned from the winpty API must be
+ * freed. */
+WINPTY_API void winpty_error_free(winpty_error_ptr_t err);
+
+
+
+/*****************************************************************************
+ * Configuration of a new agent. */
+
+/* The winpty_config_t object is not thread-safe. */
+typedef struct winpty_config_s winpty_config_t;
+
+/* Allocate a winpty_config_t value. Returns NULL on error. There are no
+ * required settings -- the object may immediately be used. agentFlags is a
+ * set of zero or more WINPTY_FLAG_xxx values. An unrecognized flag results
+ * in an assertion failure. */
+WINPTY_API winpty_config_t *
+winpty_config_new(UINT64 agentFlags, winpty_error_ptr_t *err /*OPTIONAL*/);
+
+/* Free the cfg object after passing it to winpty_open. */
+WINPTY_API void winpty_config_free(winpty_config_t *cfg);
+
+WINPTY_API void
+winpty_config_set_initial_size(winpty_config_t *cfg, int cols, int rows);
+
+/* Set the mouse mode to one of the WINPTY_MOUSE_MODE_xxx constants. */
+WINPTY_API void
+winpty_config_set_mouse_mode(winpty_config_t *cfg, int mouseMode);
+
+/* Amount of time to wait for the agent to startup and to wait for any given
+ * agent RPC request. Must be greater than 0. Can be INFINITE. */
+WINPTY_API void
+winpty_config_set_agent_timeout(winpty_config_t *cfg, DWORD timeoutMs);
+
+
+
+/*****************************************************************************
+ * Start the agent. */
+
+/* The winpty_t object is thread-safe. */
+typedef struct winpty_s winpty_t;
+
+/* Starts the agent. Returns NULL on error. This process will connect to the
+ * agent over a control pipe, and the agent will open data pipes (e.g. CONIN
+ * and CONOUT). */
+WINPTY_API winpty_t *
+winpty_open(const winpty_config_t *cfg,
+ winpty_error_ptr_t *err /*OPTIONAL*/);
+
+/* A handle to the agent process. This value is valid for the lifetime of the
+ * winpty_t object. Do not close it. */
+WINPTY_API HANDLE winpty_agent_process(winpty_t *wp);
+
+
+
+/*****************************************************************************
+ * I/O pipes. */
+
+/* Returns the names of named pipes used for terminal I/O. Each input or
+ * output direction uses a different half-duplex pipe. The agent creates
+ * these pipes, and the client can connect to them using ordinary I/O methods.
+ * The strings are freed when the winpty_t object is freed.
+ *
+ * winpty_conerr_name returns NULL unless WINPTY_FLAG_CONERR is specified.
+ *
+ * N.B.: CreateFile does not block when connecting to a local server pipe. If
+ * the server pipe does not exist or is already connected, then it fails
+ * instantly. */
+WINPTY_API LPCWSTR winpty_conin_name(winpty_t *wp);
+WINPTY_API LPCWSTR winpty_conout_name(winpty_t *wp);
+WINPTY_API LPCWSTR winpty_conerr_name(winpty_t *wp);
+
+
+
+/*****************************************************************************
+ * winpty agent RPC call: process creation. */
+
+/* The winpty_spawn_config_t object is not thread-safe. */
+typedef struct winpty_spawn_config_s winpty_spawn_config_t;
+
+/* winpty_spawn_config strings do not need to live as long as the config
+ * object. They are copied. Returns NULL on error. spawnFlags is a set of
+ * zero or more WINPTY_SPAWN_FLAG_xxx values. An unrecognized flag results in
+ * an assertion failure.
+ *
+ * env is a a pointer to an environment block like that passed to
+ * CreateProcess--a contiguous array of NUL-terminated "VAR=VAL" strings
+ * followed by a final NUL terminator.
+ *
+ * N.B.: If you want to gather all of the child's output, you may want the
+ * WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN flag.
+ */
+WINPTY_API winpty_spawn_config_t *
+winpty_spawn_config_new(UINT64 spawnFlags,
+ LPCWSTR appname /*OPTIONAL*/,
+ LPCWSTR cmdline /*OPTIONAL*/,
+ LPCWSTR cwd /*OPTIONAL*/,
+ LPCWSTR env /*OPTIONAL*/,
+ winpty_error_ptr_t *err /*OPTIONAL*/);
+
+/* Free the cfg object after passing it to winpty_spawn. */
+WINPTY_API void winpty_spawn_config_free(winpty_spawn_config_t *cfg);
+
+/*
+ * Spawns the new process.
+ *
+ * The function initializes all output parameters to zero or NULL.
+ *
+ * On success, the function returns TRUE. For each of process_handle and
+ * thread_handle that is non-NULL, the HANDLE returned from CreateProcess is
+ * duplicated from the agent and returned to the winpty client. The client is
+ * responsible for closing these HANDLES.
+ *
+ * On failure, the function returns FALSE, and if err is non-NULL, then *err
+ * is set to an error object.
+ *
+ * If the agent's CreateProcess call failed, then *create_process_error is set
+ * to GetLastError(), and the WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED error
+ * is returned.
+ *
+ * winpty_spawn can only be called once per winpty_t object. If it is called
+ * before the output data pipe(s) is/are connected, then collected output is
+ * buffered until the pipes are connected, rather than being discarded.
+ *
+ * N.B.: GetProcessId works even if the process has exited. The PID is not
+ * recycled until the NT process object is freed.
+ * (https://blogs.msdn.microsoft.com/oldnewthing/20110107-00/?p=11803)
+ */
+WINPTY_API BOOL
+winpty_spawn(winpty_t *wp,
+ const winpty_spawn_config_t *cfg,
+ HANDLE *process_handle /*OPTIONAL*/,
+ HANDLE *thread_handle /*OPTIONAL*/,
+ DWORD *create_process_error /*OPTIONAL*/,
+ winpty_error_ptr_t *err /*OPTIONAL*/);
+
+
+
+/*****************************************************************************
+ * winpty agent RPC calls: everything else */
+
+/* Change the size of the Windows console window. */
+WINPTY_API BOOL
+winpty_set_size(winpty_t *wp, int cols, int rows,
+ winpty_error_ptr_t *err /*OPTIONAL*/);
+
+/* Gets a list of processes attached to the console. */
+WINPTY_API int
+winpty_get_console_process_list(winpty_t *wp, int *processList, const int processCount,
+ winpty_error_ptr_t *err /*OPTIONAL*/);
+
+/* Frees the winpty_t object and the OS resources contained in it. This
+ * call breaks the connection with the agent, which should then close its
+ * console, terminating the processes attached to it.
+ *
+ * This function must not be called if any other threads are using the
+ * winpty_t object. Undefined behavior results. */
+WINPTY_API void winpty_free(winpty_t *wp);
+
+
+
+/****************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WINPTY_H */
diff --git a/src/libs/3rdparty/winpty/src/include/winpty_constants.h b/src/libs/3rdparty/winpty/src/include/winpty_constants.h
new file mode 100644
index 0000000000..11e34cf171
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/include/winpty_constants.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2016 Ryan Prichard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef WINPTY_CONSTANTS_H
+#define WINPTY_CONSTANTS_H
+
+/*
+ * You may want to include winpty.h instead, which includes this header.
+ *
+ * This file is split out from winpty.h so that the agent can access the
+ * winpty flags without also declaring the libwinpty APIs.
+ */
+
+/*****************************************************************************
+ * Error codes. */
+
+#define WINPTY_ERROR_SUCCESS 0
+#define WINPTY_ERROR_OUT_OF_MEMORY 1
+#define WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED 2
+#define WINPTY_ERROR_LOST_CONNECTION 3
+#define WINPTY_ERROR_AGENT_EXE_MISSING 4
+#define WINPTY_ERROR_UNSPECIFIED 5
+#define WINPTY_ERROR_AGENT_DIED 6
+#define WINPTY_ERROR_AGENT_TIMEOUT 7
+#define WINPTY_ERROR_AGENT_CREATION_FAILED 8
+
+
+
+/*****************************************************************************
+ * Configuration of a new agent. */
+
+/* Create a new screen buffer (connected to the "conerr" terminal pipe) and
+ * pass it to child processes as the STDERR handle. This flag also prevents
+ * the agent from reopening CONOUT$ when it polls -- regardless of whether the
+ * active screen buffer changes, winpty continues to monitor the original
+ * primary screen buffer. */
+#define WINPTY_FLAG_CONERR 0x1ull
+
+/* Don't output escape sequences. */
+#define WINPTY_FLAG_PLAIN_OUTPUT 0x2ull
+
+/* Do output color escape sequences. These escapes are output by default, but
+ * are suppressed with WINPTY_FLAG_PLAIN_OUTPUT. Use this flag to reenable
+ * them. */
+#define WINPTY_FLAG_COLOR_ESCAPES 0x4ull
+
+/* On XP and Vista, winpty needs to put the hidden console on a desktop in a
+ * service window station so that its polling does not interfere with other
+ * (visible) console windows. To create this desktop, it must change the
+ * process' window station (i.e. SetProcessWindowStation) for the duration of
+ * the winpty_open call. In theory, this change could interfere with the
+ * winpty client (e.g. other threads, spawning children), so winpty by default
+ * spawns a special agent process to create the hidden desktop. Spawning
+ * processes on Windows is slow, though, so if
+ * WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION is set, winpty changes this
+ * process' window station instead.
+ * See https://github.com/rprichard/winpty/issues/58. */
+#define WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION 0x8ull
+
+#define WINPTY_FLAG_MASK (0ull \
+ | WINPTY_FLAG_CONERR \
+ | WINPTY_FLAG_PLAIN_OUTPUT \
+ | WINPTY_FLAG_COLOR_ESCAPES \
+ | WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION \
+)
+
+/* QuickEdit mode is initially disabled, and the agent does not send mouse
+ * mode sequences to the terminal. If it receives mouse input, though, it
+ * still writes MOUSE_EVENT_RECORD values into CONIN. */
+#define WINPTY_MOUSE_MODE_NONE 0
+
+/* QuickEdit mode is initially enabled. As CONIN enters or leaves mouse
+ * input mode (i.e. where ENABLE_MOUSE_INPUT is on and ENABLE_QUICK_EDIT_MODE
+ * is off), the agent enables or disables mouse input on the terminal.
+ *
+ * This is the default mode. */
+#define WINPTY_MOUSE_MODE_AUTO 1
+
+/* QuickEdit mode is initially disabled, and the agent enables the terminal's
+ * mouse input mode. It does not disable terminal mouse mode (until exit). */
+#define WINPTY_MOUSE_MODE_FORCE 2
+
+
+
+/*****************************************************************************
+ * winpty agent RPC call: process creation. */
+
+/* If the spawn is marked "auto-shutdown", then the agent shuts down console
+ * output once the process exits. The agent stops polling for new console
+ * output, and once all pending data has been written to the output pipe, the
+ * agent closes the pipe. (At that point, the pipe may still have data in it,
+ * which the client may read. Once all the data has been read, further reads
+ * return EOF.) */
+#define WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN 1ull
+
+/* After the agent shuts down output, and after all output has been written
+ * into the pipe(s), exit the agent by closing the console. If there any
+ * surviving processes still attached to the console, they are killed.
+ *
+ * Note: With this flag, an RPC call (e.g. winpty_set_size) issued after the
+ * agent exits will fail with an I/O or dead-agent error. */
+#define WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN 2ull
+
+/* All the spawn flags. */
+#define WINPTY_SPAWN_FLAG_MASK (0ull \
+ | WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN \
+ | WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN \
+)
+
+
+
+#endif /* WINPTY_CONSTANTS_H */
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.cc b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.cc
new file mode 100644
index 0000000000..82d00b2da2
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "AgentLocation.h"
+
+#include <windows.h>
+
+#include <string>
+
+#include "../shared/WinptyAssert.h"
+
+#include "LibWinptyException.h"
+
+#define AGENT_EXE L"winpty-agent.exe"
+
+static HMODULE getCurrentModule() {
+ HMODULE module;
+ if (!GetModuleHandleExW(
+ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<LPCWSTR>(getCurrentModule),
+ &module)) {
+ ASSERT(false && "GetModuleHandleEx failed");
+ }
+ return module;
+}
+
+static std::wstring getModuleFileName(HMODULE module) {
+ const int bufsize = 4096;
+ wchar_t path[bufsize];
+ int size = GetModuleFileNameW(module, path, bufsize);
+ ASSERT(size != 0 && size != bufsize);
+ return std::wstring(path);
+}
+
+static std::wstring dirname(const std::wstring &path) {
+ std::wstring::size_type pos = path.find_last_of(L"\\/");
+ if (pos == std::wstring::npos) {
+ return L"";
+ } else {
+ return path.substr(0, pos);
+ }
+}
+
+static bool pathExists(const std::wstring &path) {
+ return GetFileAttributesW(path.c_str()) != 0xFFFFFFFF;
+}
+
+std::wstring findAgentProgram() {
+ std::wstring progDir = dirname(getModuleFileName(getCurrentModule()));
+ std::wstring ret = progDir + (L"\\" AGENT_EXE);
+ if (!pathExists(ret)) {
+ throw LibWinptyException(
+ WINPTY_ERROR_AGENT_EXE_MISSING,
+ (L"agent executable does not exist: '" + ret + L"'").c_str());
+ }
+ return ret;
+}
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.h b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.h
new file mode 100644
index 0000000000..a96b854cd2
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/AgentLocation.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LIBWINPTY_AGENT_LOCATION_H
+#define LIBWINPTY_AGENT_LOCATION_H
+
+#include <string>
+
+std::wstring findAgentProgram();
+
+#endif // LIBWINPTY_AGENT_LOCATION_H
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/LibWinptyException.h b/src/libs/3rdparty/winpty/src/libwinpty/LibWinptyException.h
new file mode 100644
index 0000000000..2274798d23
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/LibWinptyException.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LIB_WINPTY_EXCEPTION_H
+#define LIB_WINPTY_EXCEPTION_H
+
+#include "../include/winpty.h"
+
+#include "../shared/WinptyException.h"
+
+#include <memory>
+#include <string>
+
+class LibWinptyException : public WinptyException {
+public:
+ LibWinptyException(winpty_result_t code, const wchar_t *what) :
+ m_code(code), m_what(std::make_shared<std::wstring>(what)) {}
+
+ winpty_result_t code() const WINPTY_NOEXCEPT {
+ return m_code;
+ }
+
+ const wchar_t *what() const WINPTY_NOEXCEPT override {
+ return m_what->c_str();
+ }
+
+ std::shared_ptr<std::wstring> whatSharedStr() const WINPTY_NOEXCEPT {
+ return m_what;
+ }
+
+private:
+ winpty_result_t m_code;
+ // Using a shared_ptr ensures that copying the object raises no exception.
+ std::shared_ptr<std::wstring> m_what;
+};
+
+#endif // LIB_WINPTY_EXCEPTION_H
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/WinptyInternal.h b/src/libs/3rdparty/winpty/src/libwinpty/WinptyInternal.h
new file mode 100644
index 0000000000..93e992d5c5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/WinptyInternal.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef LIBWINPTY_WINPTY_INTERNAL_H
+#define LIBWINPTY_WINPTY_INTERNAL_H
+
+#include <memory>
+#include <string>
+
+#include "../include/winpty.h"
+
+#include "../shared/Mutex.h"
+#include "../shared/OwnedHandle.h"
+
+// The structures in this header are not intended to be accessed directly by
+// client programs.
+
+struct winpty_error_s {
+ winpty_result_t code;
+ const wchar_t *msgStatic;
+ // Use a pointer to a std::shared_ptr so that the struct remains simple
+ // enough to statically initialize, for the benefit of static error
+ // objects like kOutOfMemory.
+ std::shared_ptr<std::wstring> *msgDynamic;
+};
+
+struct winpty_config_s {
+ uint64_t flags = 0;
+ int cols = 80;
+ int rows = 25;
+ int mouseMode = WINPTY_MOUSE_MODE_AUTO;
+ DWORD timeoutMs = 30000;
+};
+
+struct winpty_s {
+ Mutex mutex;
+ OwnedHandle agentProcess;
+ OwnedHandle controlPipe;
+ DWORD agentTimeoutMs = 0;
+ OwnedHandle ioEvent;
+ std::wstring spawnDesktopName;
+ std::wstring coninPipeName;
+ std::wstring conoutPipeName;
+ std::wstring conerrPipeName;
+};
+
+struct winpty_spawn_config_s {
+ uint64_t winptyFlags = 0;
+ std::wstring appname;
+ std::wstring cmdline;
+ std::wstring cwd;
+ std::wstring env;
+};
+
+#endif // LIBWINPTY_WINPTY_INTERNAL_H
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/subdir.mk b/src/libs/3rdparty/winpty/src/libwinpty/subdir.mk
new file mode 100644
index 0000000000..ba32bad6e6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/subdir.mk
@@ -0,0 +1,46 @@
+# Copyright (c) 2011-2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+ALL_TARGETS += build/winpty.dll
+
+$(eval $(call def_mingw_target,libwinpty,-DCOMPILING_WINPTY_DLL))
+
+LIBWINPTY_OBJECTS = \
+ build/libwinpty/libwinpty/AgentLocation.o \
+ build/libwinpty/libwinpty/winpty.o \
+ build/libwinpty/shared/BackgroundDesktop.o \
+ build/libwinpty/shared/Buffer.o \
+ build/libwinpty/shared/DebugClient.o \
+ build/libwinpty/shared/GenRandom.o \
+ build/libwinpty/shared/OwnedHandle.o \
+ build/libwinpty/shared/StringUtil.o \
+ build/libwinpty/shared/WindowsSecurity.o \
+ build/libwinpty/shared/WindowsVersion.o \
+ build/libwinpty/shared/WinptyAssert.o \
+ build/libwinpty/shared/WinptyException.o \
+ build/libwinpty/shared/WinptyVersion.o
+
+build/libwinpty/shared/WinptyVersion.o : build/gen/GenVersion.h
+
+build/winpty.dll : $(LIBWINPTY_OBJECTS)
+ $(info Linking $@)
+ @$(MINGW_CXX) $(MINGW_LDFLAGS) -shared -o $@ $^ -Wl,--out-implib,build/winpty.lib
+
+-include $(LIBWINPTY_OBJECTS:.o=.d)
diff --git a/src/libs/3rdparty/winpty/src/libwinpty/winpty.cc b/src/libs/3rdparty/winpty/src/libwinpty/winpty.cc
new file mode 100644
index 0000000000..3d977498ef
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/libwinpty/winpty.cc
@@ -0,0 +1,970 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <windows.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "../include/winpty.h"
+
+#include "../shared/AgentMsg.h"
+#include "../shared/BackgroundDesktop.h"
+#include "../shared/Buffer.h"
+#include "../shared/DebugClient.h"
+#include "../shared/GenRandom.h"
+#include "../shared/OwnedHandle.h"
+#include "../shared/StringBuilder.h"
+#include "../shared/StringUtil.h"
+#include "../shared/WindowsSecurity.h"
+#include "../shared/WindowsVersion.h"
+#include "../shared/WinptyAssert.h"
+#include "../shared/WinptyException.h"
+#include "../shared/WinptyVersion.h"
+
+#include "AgentLocation.h"
+#include "LibWinptyException.h"
+#include "WinptyInternal.h"
+
+
+
+/*****************************************************************************
+ * Error handling -- translate C++ exceptions to an optional error object
+ * output and log the result. */
+
+static const winpty_error_s kOutOfMemory = {
+ WINPTY_ERROR_OUT_OF_MEMORY,
+ L"Out of memory",
+ nullptr
+};
+
+static const winpty_error_s kBadRpcPacket = {
+ WINPTY_ERROR_UNSPECIFIED,
+ L"Bad RPC packet",
+ nullptr
+};
+
+static const winpty_error_s kUncaughtException = {
+ WINPTY_ERROR_UNSPECIFIED,
+ L"Uncaught C++ exception",
+ nullptr
+};
+
+/* Gets the error code from the error object. */
+WINPTY_API winpty_result_t winpty_error_code(winpty_error_ptr_t err) {
+ return err != nullptr ? err->code : WINPTY_ERROR_SUCCESS;
+}
+
+/* Returns a textual representation of the error. The string is freed when
+ * the error is freed. */
+WINPTY_API LPCWSTR winpty_error_msg(winpty_error_ptr_t err) {
+ if (err != nullptr) {
+ if (err->msgStatic != nullptr) {
+ return err->msgStatic;
+ } else {
+ ASSERT(err->msgDynamic != nullptr);
+ std::wstring *msgPtr = err->msgDynamic->get();
+ ASSERT(msgPtr != nullptr);
+ return msgPtr->c_str();
+ }
+ } else {
+ return L"Success";
+ }
+}
+
+/* Free the error object. Every error returned from the winpty API must be
+ * freed. */
+WINPTY_API void winpty_error_free(winpty_error_ptr_t err) {
+ if (err != nullptr && err->msgDynamic != nullptr) {
+ delete err->msgDynamic;
+ delete err;
+ }
+}
+
+static void translateException(winpty_error_ptr_t *&err) {
+ winpty_error_ptr_t ret = nullptr;
+ try {
+ try {
+ throw;
+ } catch (const ReadBuffer::DecodeError&) {
+ ret = const_cast<winpty_error_ptr_t>(&kBadRpcPacket);
+ } catch (const LibWinptyException &e) {
+ std::unique_ptr<winpty_error_t> obj(new winpty_error_t);
+ obj->code = e.code();
+ obj->msgStatic = nullptr;
+ obj->msgDynamic =
+ new std::shared_ptr<std::wstring>(e.whatSharedStr());
+ ret = obj.release();
+ } catch (const WinptyException &e) {
+ std::unique_ptr<winpty_error_t> obj(new winpty_error_t);
+ std::shared_ptr<std::wstring> msg(new std::wstring(e.what()));
+ obj->code = WINPTY_ERROR_UNSPECIFIED;
+ obj->msgStatic = nullptr;
+ obj->msgDynamic = new std::shared_ptr<std::wstring>(msg);
+ ret = obj.release();
+ }
+ } catch (const std::bad_alloc&) {
+ ret = const_cast<winpty_error_ptr_t>(&kOutOfMemory);
+ } catch (...) {
+ ret = const_cast<winpty_error_ptr_t>(&kUncaughtException);
+ }
+ trace("libwinpty error: code=%u msg='%s'",
+ static_cast<unsigned>(ret->code),
+ utf8FromWide(winpty_error_msg(ret)).c_str());
+ if (err != nullptr) {
+ *err = ret;
+ } else {
+ winpty_error_free(ret);
+ }
+}
+
+#define API_TRY \
+ if (err != nullptr) { *err = nullptr; } \
+ try
+
+#define API_CATCH(ret) \
+ catch (...) { translateException(err); return (ret); }
+
+
+
+/*****************************************************************************
+ * Configuration of a new agent. */
+
+WINPTY_API winpty_config_t *
+winpty_config_new(UINT64 flags, winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT((flags & WINPTY_FLAG_MASK) == flags);
+ std::unique_ptr<winpty_config_t> ret(new winpty_config_t);
+ ret->flags = flags;
+ return ret.release();
+ } API_CATCH(nullptr)
+}
+
+WINPTY_API void winpty_config_free(winpty_config_t *cfg) {
+ delete cfg;
+}
+
+WINPTY_API void
+winpty_config_set_initial_size(winpty_config_t *cfg, int cols, int rows) {
+ ASSERT(cfg != nullptr && cols > 0 && rows > 0);
+ cfg->cols = cols;
+ cfg->rows = rows;
+}
+
+WINPTY_API void
+winpty_config_set_mouse_mode(winpty_config_t *cfg, int mouseMode) {
+ ASSERT(cfg != nullptr &&
+ mouseMode >= WINPTY_MOUSE_MODE_NONE &&
+ mouseMode <= WINPTY_MOUSE_MODE_FORCE);
+ cfg->mouseMode = mouseMode;
+}
+
+WINPTY_API void
+winpty_config_set_agent_timeout(winpty_config_t *cfg, DWORD timeoutMs) {
+ ASSERT(cfg != nullptr && timeoutMs > 0);
+ cfg->timeoutMs = timeoutMs;
+}
+
+
+
+/*****************************************************************************
+ * Agent I/O. */
+
+namespace {
+
+// Once an I/O operation fails with ERROR_IO_PENDING, the caller *must* wait
+// for it to complete, even after calling CancelIo on it! See
+// https://blogs.msdn.microsoft.com/oldnewthing/20110202-00/?p=11613. This
+// class enforces that requirement.
+class PendingIo {
+ HANDLE m_file;
+ OVERLAPPED &m_over;
+ bool m_finished;
+public:
+ // The file handle and OVERLAPPED object must live as long as the PendingIo
+ // object.
+ PendingIo(HANDLE file, OVERLAPPED &over) :
+ m_file(file), m_over(over), m_finished(false) {}
+ ~PendingIo() {
+ if (!m_finished) {
+ // We're not usually that interested in CancelIo's return value.
+ // In any case, we must not throw an exception in this dtor.
+ CancelIo(m_file);
+ waitForCompletion();
+ }
+ }
+ std::tuple<BOOL, DWORD> waitForCompletion(DWORD &actual) WINPTY_NOEXCEPT {
+ m_finished = true;
+ const BOOL success =
+ GetOverlappedResult(m_file, &m_over, &actual, TRUE);
+ return std::make_tuple(success, GetLastError());
+ }
+ std::tuple<BOOL, DWORD> waitForCompletion() WINPTY_NOEXCEPT {
+ DWORD actual = 0;
+ return waitForCompletion(actual);
+ }
+};
+
+} // anonymous namespace
+
+static void handlePendingIo(winpty_t &wp, OVERLAPPED &over, BOOL &success,
+ DWORD &lastError, DWORD &actual) {
+ if (!success && lastError == ERROR_IO_PENDING) {
+ PendingIo io(wp.controlPipe.get(), over);
+ const HANDLE waitHandles[2] = { wp.ioEvent.get(),
+ wp.agentProcess.get() };
+ DWORD waitRet = WaitForMultipleObjects(
+ 2, waitHandles, FALSE, wp.agentTimeoutMs);
+ if (waitRet != WAIT_OBJECT_0) {
+ // The I/O is still pending. Cancel it, close the I/O event, and
+ // throw an exception.
+ if (waitRet == WAIT_OBJECT_0 + 1) {
+ throw LibWinptyException(WINPTY_ERROR_AGENT_DIED, L"agent died");
+ } else if (waitRet == WAIT_TIMEOUT) {
+ throw LibWinptyException(WINPTY_ERROR_AGENT_TIMEOUT,
+ L"agent timed out");
+ } else if (waitRet == WAIT_FAILED) {
+ throwWindowsError(L"WaitForMultipleObjects failed");
+ } else {
+ ASSERT(false &&
+ "unexpected WaitForMultipleObjects return value");
+ }
+ }
+ std::tie(success, lastError) = io.waitForCompletion(actual);
+ }
+}
+
+static void handlePendingIo(winpty_t &wp, OVERLAPPED &over, BOOL &success,
+ DWORD &lastError) {
+ DWORD actual = 0;
+ handlePendingIo(wp, over, success, lastError, actual);
+}
+
+static void handleReadWriteErrors(winpty_t &wp, BOOL success, DWORD lastError,
+ const wchar_t *genericErrMsg) {
+ if (!success) {
+ // If the pipe connection is broken after it's been connected, then
+ // later I/O operations fail with ERROR_BROKEN_PIPE (reads) or
+ // ERROR_NO_DATA (writes). With Wine, they may also fail with
+ // ERROR_PIPE_NOT_CONNECTED. See this gist[1].
+ //
+ // [1] https://gist.github.com/rprichard/8dd8ca134b39534b7da2733994aa07ba
+ if (lastError == ERROR_BROKEN_PIPE || lastError == ERROR_NO_DATA ||
+ lastError == ERROR_PIPE_NOT_CONNECTED) {
+ throw LibWinptyException(WINPTY_ERROR_LOST_CONNECTION,
+ L"lost connection to agent");
+ } else {
+ throwWindowsError(genericErrMsg, lastError);
+ }
+ }
+}
+
+// Calls ConnectNamedPipe to wait until the agent connects to the control pipe.
+static void
+connectControlPipe(winpty_t &wp) {
+ OVERLAPPED over = {};
+ over.hEvent = wp.ioEvent.get();
+ BOOL success = ConnectNamedPipe(wp.controlPipe.get(), &over);
+ DWORD lastError = GetLastError();
+ handlePendingIo(wp, over, success, lastError);
+ if (!success && lastError == ERROR_PIPE_CONNECTED) {
+ success = TRUE;
+ }
+ if (!success) {
+ throwWindowsError(L"ConnectNamedPipe failed", lastError);
+ }
+}
+
+static void writeData(winpty_t &wp, const void *data, size_t amount) {
+ // Perform a single pipe write.
+ DWORD actual = 0;
+ OVERLAPPED over = {};
+ over.hEvent = wp.ioEvent.get();
+ BOOL success = WriteFile(wp.controlPipe.get(), data, amount,
+ &actual, &over);
+ DWORD lastError = GetLastError();
+ if (!success) {
+ handlePendingIo(wp, over, success, lastError, actual);
+ handleReadWriteErrors(wp, success, lastError, L"WriteFile failed");
+ ASSERT(success);
+ }
+ // TODO: Can a partial write actually happen somehow?
+ ASSERT(actual == amount && "WriteFile wrote fewer bytes than requested");
+}
+
+static inline WriteBuffer newPacket() {
+ WriteBuffer packet;
+ packet.putRawValue<uint64_t>(0); // Reserve space for size.
+ return packet;
+}
+
+static void writePacket(winpty_t &wp, WriteBuffer &packet) {
+ const auto &buf = packet.buf();
+ packet.replaceRawValue<uint64_t>(0, buf.size());
+ writeData(wp, buf.data(), buf.size());
+}
+
+static size_t readData(winpty_t &wp, void *data, size_t amount) {
+ DWORD actual = 0;
+ OVERLAPPED over = {};
+ over.hEvent = wp.ioEvent.get();
+ BOOL success = ReadFile(wp.controlPipe.get(), data, amount,
+ &actual, &over);
+ DWORD lastError = GetLastError();
+ if (!success) {
+ handlePendingIo(wp, over, success, lastError, actual);
+ handleReadWriteErrors(wp, success, lastError, L"ReadFile failed");
+ }
+ return actual;
+}
+
+static void readAll(winpty_t &wp, void *data, size_t amount) {
+ while (amount > 0) {
+ const size_t chunk = readData(wp, data, amount);
+ ASSERT(chunk <= amount && "readData result is larger than amount");
+ data = reinterpret_cast<char*>(data) + chunk;
+ amount -= chunk;
+ }
+}
+
+static uint64_t readUInt64(winpty_t &wp) {
+ uint64_t ret = 0;
+ readAll(wp, &ret, sizeof(ret));
+ return ret;
+}
+
+// Returns a reply packet's payload.
+static ReadBuffer readPacket(winpty_t &wp) {
+ const uint64_t packetSize = readUInt64(wp);
+ if (packetSize < sizeof(packetSize) || packetSize > SIZE_MAX) {
+ throwWinptyException(L"Agent RPC error: invalid packet size");
+ }
+ const size_t payloadSize = packetSize - sizeof(packetSize);
+ std::vector<char> bytes(payloadSize);
+ readAll(wp, bytes.data(), bytes.size());
+ return ReadBuffer(std::move(bytes));
+}
+
+static OwnedHandle createControlPipe(const std::wstring &name) {
+ const auto sd = createPipeSecurityDescriptorOwnerFullControl();
+ if (!sd) {
+ throwWinptyException(
+ L"could not create the control pipe's SECURITY_DESCRIPTOR");
+ }
+ SECURITY_ATTRIBUTES sa = {};
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = sd.get();
+ HANDLE ret = CreateNamedPipeW(name.c_str(),
+ /*dwOpenMode=*/
+ PIPE_ACCESS_DUPLEX |
+ FILE_FLAG_FIRST_PIPE_INSTANCE |
+ FILE_FLAG_OVERLAPPED,
+ /*dwPipeMode=*/rejectRemoteClientsPipeFlag(),
+ /*nMaxInstances=*/1,
+ /*nOutBufferSize=*/8192,
+ /*nInBufferSize=*/256,
+ /*nDefaultTimeOut=*/30000,
+ &sa);
+ if (ret == INVALID_HANDLE_VALUE) {
+ throwWindowsError(L"CreateNamedPipeW failed");
+ }
+ return OwnedHandle(ret);
+}
+
+
+
+/*****************************************************************************
+ * Start the agent. */
+
+static OwnedHandle createEvent() {
+ // manual reset, initially unset
+ HANDLE h = CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ if (h == nullptr) {
+ throwWindowsError(L"CreateEventW failed");
+ }
+ return OwnedHandle(h);
+}
+
+// For debugging purposes, provide a way to keep the console on the main window
+// station, visible.
+static bool shouldShowConsoleWindow() {
+ char buf[32];
+ return GetEnvironmentVariableA("WINPTY_SHOW_CONSOLE", buf, sizeof(buf)) > 0;
+}
+
+static bool shouldCreateBackgroundDesktop(bool &createUsingAgent) {
+ // Prior to Windows 7, winpty's repeated selection-deselection loop
+ // prevented the user from interacting with their *visible* console
+ // windows, unless we placed the console onto a background desktop.
+ // The SetProcessWindowStation call interferes with the clipboard and
+ // isn't thread-safe, though[1]. The call should perhaps occur in a
+ // special agent subprocess. Spawning a process in a background desktop
+ // also breaks ConEmu, but marking the process SW_HIDE seems to correct
+ // that[2].
+ //
+ // Windows 7 moved a lot of console handling out of csrss.exe and into
+ // a per-console conhost.exe process, which may explain why it isn't
+ // affected.
+ //
+ // This is a somewhat risky change, so there are low-level flags to
+ // assist in debugging if there are issues.
+ //
+ // [1] https://github.com/rprichard/winpty/issues/58
+ // [2] https://github.com/rprichard/winpty/issues/70
+ bool ret = !shouldShowConsoleWindow() && !isAtLeastWindows7();
+ const bool force = hasDebugFlag("force_desktop");
+ const bool force_spawn = hasDebugFlag("force_desktop_spawn");
+ const bool force_curproc = hasDebugFlag("force_desktop_curproc");
+ const bool suppress = hasDebugFlag("no_desktop");
+ if (force + force_spawn + force_curproc + suppress > 1) {
+ trace("error: Only one of force_desktop, force_desktop_spawn, "
+ "force_desktop_curproc, and no_desktop may be set");
+ } else if (force) {
+ ret = true;
+ } else if (force_spawn) {
+ ret = true;
+ createUsingAgent = true;
+ } else if (force_curproc) {
+ ret = true;
+ createUsingAgent = false;
+ } else if (suppress) {
+ ret = false;
+ }
+ return ret;
+}
+
+static bool shouldSpecifyHideFlag() {
+ const bool force = hasDebugFlag("force_sw_hide");
+ const bool suppress = hasDebugFlag("no_sw_hide");
+ bool ret = !shouldShowConsoleWindow();
+ if (force && suppress) {
+ trace("error: Both the force_sw_hide and no_sw_hide flags are set");
+ } else if (force) {
+ ret = true;
+ } else if (suppress) {
+ ret = false;
+ }
+ return ret;
+}
+
+static OwnedHandle startAgentProcess(
+ const std::wstring &desktop,
+ const std::wstring &controlPipeName,
+ const std::wstring &params,
+ DWORD creationFlags,
+ DWORD &agentPid) {
+ const std::wstring exePath = findAgentProgram();
+ const std::wstring cmdline =
+ (WStringBuilder(256)
+ << L"\"" << exePath << L"\" "
+ << controlPipeName << L' '
+ << params).str_moved();
+
+ auto cmdlineV = vectorWithNulFromString(cmdline);
+ auto desktopV = vectorWithNulFromString(desktop);
+
+ // Start the agent.
+ STARTUPINFOW sui = {};
+ sui.cb = sizeof(sui);
+ sui.lpDesktop = desktop.empty() ? nullptr : desktopV.data();
+
+ if (shouldSpecifyHideFlag()) {
+ sui.dwFlags |= STARTF_USESHOWWINDOW;
+ sui.wShowWindow = SW_HIDE;
+ }
+ PROCESS_INFORMATION pi = {};
+ const BOOL success =
+ CreateProcessW(exePath.c_str(),
+ cmdlineV.data(),
+ nullptr, nullptr,
+ /*bInheritHandles=*/FALSE,
+ /*dwCreationFlags=*/creationFlags,
+ nullptr, nullptr,
+ &sui, &pi);
+ if (!success) {
+ const DWORD lastError = GetLastError();
+ const auto errStr =
+ (WStringBuilder(256)
+ << L"winpty-agent CreateProcess failed: cmdline='" << cmdline
+ << L"' err=0x" << whexOfInt(lastError)).str_moved();
+ throw LibWinptyException(
+ WINPTY_ERROR_AGENT_CREATION_FAILED, errStr.c_str());
+ }
+ CloseHandle(pi.hThread);
+ TRACE("Created agent successfully, pid=%u, cmdline=%s",
+ static_cast<unsigned int>(pi.dwProcessId),
+ utf8FromWide(cmdline).c_str());
+ agentPid = pi.dwProcessId;
+ return OwnedHandle(pi.hProcess);
+}
+
+static void verifyPipeClientPid(HANDLE serverPipe, DWORD agentPid) {
+ const auto client = getNamedPipeClientProcessId(serverPipe);
+ const auto success = std::get<0>(client);
+ const auto lastError = std::get<2>(client);
+ if (success == GetNamedPipeClientProcessId_Result::Success) {
+ const auto clientPid = std::get<1>(client);
+ if (clientPid != agentPid) {
+ WStringBuilder errMsg;
+ errMsg << L"Security check failed: pipe client pid (" << clientPid
+ << L") does not match agent pid (" << agentPid << L")";
+ throwWinptyException(errMsg.c_str());
+ }
+ } else if (success == GetNamedPipeClientProcessId_Result::UnsupportedOs) {
+ trace("Pipe client PID security check skipped: "
+ "GetNamedPipeClientProcessId unsupported on this OS version");
+ } else {
+ throwWindowsError(L"GetNamedPipeClientProcessId failed", lastError);
+ }
+}
+
+static std::unique_ptr<winpty_t>
+createAgentSession(const winpty_config_t *cfg,
+ const std::wstring &desktop,
+ const std::wstring &params,
+ DWORD creationFlags) {
+ std::unique_ptr<winpty_t> wp(new winpty_t);
+ wp->agentTimeoutMs = cfg->timeoutMs;
+ wp->ioEvent = createEvent();
+
+ // Create control server pipe.
+ const auto pipeName =
+ L"\\\\.\\pipe\\winpty-control-" + GenRandom().uniqueName();
+ wp->controlPipe = createControlPipe(pipeName);
+
+ DWORD agentPid = 0;
+ wp->agentProcess = startAgentProcess(
+ desktop, pipeName, params, creationFlags, agentPid);
+ connectControlPipe(*wp.get());
+ verifyPipeClientPid(wp->controlPipe.get(), agentPid);
+
+ return std::move(wp);
+}
+
+namespace {
+
+class AgentDesktop {
+public:
+ virtual std::wstring name() = 0;
+ virtual ~AgentDesktop() {}
+};
+
+class AgentDesktopDirect : public AgentDesktop {
+public:
+ AgentDesktopDirect(BackgroundDesktop &&desktop) :
+ m_desktop(std::move(desktop))
+ {
+ }
+ std::wstring name() override { return m_desktop.desktopName(); }
+private:
+ BackgroundDesktop m_desktop;
+};
+
+class AgentDesktopIndirect : public AgentDesktop {
+public:
+ AgentDesktopIndirect(std::unique_ptr<winpty_t> &&wp,
+ std::wstring &&desktopName) :
+ m_wp(std::move(wp)),
+ m_desktopName(std::move(desktopName))
+ {
+ }
+ std::wstring name() override { return m_desktopName; }
+private:
+ std::unique_ptr<winpty_t> m_wp;
+ std::wstring m_desktopName;
+};
+
+} // anonymous namespace
+
+std::unique_ptr<AgentDesktop>
+setupBackgroundDesktop(const winpty_config_t *cfg) {
+ bool useDesktopAgent =
+ !(cfg->flags & WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION);
+ const bool useDesktop = shouldCreateBackgroundDesktop(useDesktopAgent);
+
+ if (!useDesktop) {
+ return std::unique_ptr<AgentDesktop>();
+ }
+
+ if (useDesktopAgent) {
+ auto wp = createAgentSession(
+ cfg, std::wstring(), L"--create-desktop", DETACHED_PROCESS);
+
+ // Read the desktop name.
+ auto packet = readPacket(*wp.get());
+ auto desktopName = packet.getWString();
+ packet.assertEof();
+
+ if (desktopName.empty()) {
+ return std::unique_ptr<AgentDesktop>();
+ } else {
+ return std::unique_ptr<AgentDesktop>(
+ new AgentDesktopIndirect(std::move(wp),
+ std::move(desktopName)));
+ }
+ } else {
+ try {
+ BackgroundDesktop desktop;
+ return std::unique_ptr<AgentDesktop>(new AgentDesktopDirect(
+ std::move(desktop)));
+ } catch (const WinptyException &e) {
+ trace("Error: failed to create background desktop, "
+ "using original desktop instead: %s",
+ utf8FromWide(e.what()).c_str());
+ return std::unique_ptr<AgentDesktop>();
+ }
+ }
+}
+
+WINPTY_API winpty_t *
+winpty_open(const winpty_config_t *cfg,
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT(cfg != nullptr);
+ dumpWindowsVersion();
+ dumpVersionToTrace();
+
+ // Setup a background desktop for the agent.
+ auto desktop = setupBackgroundDesktop(cfg);
+ const auto desktopName = desktop ? desktop->name() : std::wstring();
+
+ // Start the primary agent session.
+ const auto params =
+ (WStringBuilder(128)
+ << cfg->flags << L' '
+ << cfg->mouseMode << L' '
+ << cfg->cols << L' '
+ << cfg->rows).str_moved();
+ auto wp = createAgentSession(cfg, desktopName, params,
+ CREATE_NEW_CONSOLE);
+
+ // Close handles to the background desktop and restore the original
+ // window station. This must wait until we know the agent is running
+ // -- if we close these handles too soon, then the desktop and
+ // windowstation will be destroyed before the agent can connect with
+ // them.
+ //
+ // If we used a separate agent process to create the desktop, we
+ // disconnect from that process here, allowing it to exit.
+ desktop.reset();
+
+ // If we ran the agent process on a background desktop, then when we
+ // spawn a child process from the agent, it will need to be explicitly
+ // placed back onto the original desktop.
+ if (!desktopName.empty()) {
+ wp->spawnDesktopName = getCurrentDesktopName();
+ }
+
+ // Get the CONIN/CONOUT pipe names.
+ auto packet = readPacket(*wp.get());
+ wp->coninPipeName = packet.getWString();
+ wp->conoutPipeName = packet.getWString();
+ if (cfg->flags & WINPTY_FLAG_CONERR) {
+ wp->conerrPipeName = packet.getWString();
+ }
+ packet.assertEof();
+
+ return wp.release();
+ } API_CATCH(nullptr)
+}
+
+WINPTY_API HANDLE winpty_agent_process(winpty_t *wp) {
+ ASSERT(wp != nullptr);
+ return wp->agentProcess.get();
+}
+
+
+
+/*****************************************************************************
+ * I/O pipes. */
+
+static const wchar_t *cstrFromWStringOrNull(const std::wstring &str) {
+ try {
+ return str.c_str();
+ } catch (const std::bad_alloc&) {
+ return nullptr;
+ }
+}
+
+WINPTY_API LPCWSTR winpty_conin_name(winpty_t *wp) {
+ ASSERT(wp != nullptr);
+ return cstrFromWStringOrNull(wp->coninPipeName);
+}
+
+WINPTY_API LPCWSTR winpty_conout_name(winpty_t *wp) {
+ ASSERT(wp != nullptr);
+ return cstrFromWStringOrNull(wp->conoutPipeName);
+}
+
+WINPTY_API LPCWSTR winpty_conerr_name(winpty_t *wp) {
+ ASSERT(wp != nullptr);
+ if (wp->conerrPipeName.empty()) {
+ return nullptr;
+ } else {
+ return cstrFromWStringOrNull(wp->conerrPipeName);
+ }
+}
+
+
+
+/*****************************************************************************
+ * winpty agent RPC calls. */
+
+namespace {
+
+// Close the control pipe if something goes wrong with the pipe communication,
+// which could leave the control pipe in an inconsistent state.
+class RpcOperation {
+public:
+ RpcOperation(winpty_t &wp) : m_wp(wp) {
+ if (m_wp.controlPipe.get() == nullptr) {
+ throwWinptyException(L"Agent shutdown due to RPC failure");
+ }
+ }
+ ~RpcOperation() {
+ if (!m_success) {
+ trace("~RpcOperation: Closing control pipe");
+ m_wp.controlPipe.dispose(true);
+ }
+ }
+ void success() { m_success = true; }
+private:
+ winpty_t &m_wp;
+ bool m_success = false;
+};
+
+} // anonymous namespace
+
+
+
+/*****************************************************************************
+ * winpty agent RPC call: process creation. */
+
+// Return a std::wstring containing every character of the environment block.
+// Typically, the block is non-empty, so the std::wstring returned ends with
+// two NUL terminators. (These two terminators are counted in size(), so
+// calling c_str() produces a triply-terminated string.)
+static std::wstring wstringFromEnvBlock(const wchar_t *env) {
+ std::wstring envStr;
+ if (env != NULL) {
+ const wchar_t *p = env;
+ while (*p != L'\0') {
+ p += wcslen(p) + 1;
+ }
+ p++;
+ envStr.assign(env, p);
+
+ // Assuming the environment was non-empty, envStr now ends with two NUL
+ // terminators.
+ //
+ // If the environment were empty, though, then envStr would only be
+ // singly terminated, but the MSDN documentation thinks an env block is
+ // always doubly-terminated, so add an extra NUL just in case it
+ // matters.
+ const auto envStrSz = envStr.size();
+ if (envStrSz == 1) {
+ ASSERT(envStr[0] == L'\0');
+ envStr.push_back(L'\0');
+ } else {
+ ASSERT(envStrSz >= 3);
+ ASSERT(envStr[envStrSz - 3] != L'\0');
+ ASSERT(envStr[envStrSz - 2] == L'\0');
+ ASSERT(envStr[envStrSz - 1] == L'\0');
+ }
+ }
+ return envStr;
+}
+
+WINPTY_API winpty_spawn_config_t *
+winpty_spawn_config_new(UINT64 winptyFlags,
+ LPCWSTR appname /*OPTIONAL*/,
+ LPCWSTR cmdline /*OPTIONAL*/,
+ LPCWSTR cwd /*OPTIONAL*/,
+ LPCWSTR env /*OPTIONAL*/,
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT((winptyFlags & WINPTY_SPAWN_FLAG_MASK) == winptyFlags);
+ std::unique_ptr<winpty_spawn_config_t> cfg(new winpty_spawn_config_t);
+ cfg->winptyFlags = winptyFlags;
+ if (appname != nullptr) { cfg->appname = appname; }
+ if (cmdline != nullptr) { cfg->cmdline = cmdline; }
+ if (cwd != nullptr) { cfg->cwd = cwd; }
+ if (env != nullptr) { cfg->env = wstringFromEnvBlock(env); }
+ return cfg.release();
+ } API_CATCH(nullptr)
+}
+
+WINPTY_API void winpty_spawn_config_free(winpty_spawn_config_t *cfg) {
+ delete cfg;
+}
+
+// It's safe to truncate a handle from 64-bits to 32-bits, or to sign-extend it
+// back to 64-bits. See the MSDN article, "Interprocess Communication Between
+// 32-bit and 64-bit Applications".
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203.aspx
+static inline HANDLE handleFromInt64(int64_t i) {
+ return reinterpret_cast<HANDLE>(static_cast<intptr_t>(i));
+}
+
+// Given a process and a handle in that process, duplicate the handle into the
+// current process and close it in the originating process.
+static inline OwnedHandle stealHandle(HANDLE process, HANDLE handle) {
+ HANDLE result = nullptr;
+ if (!DuplicateHandle(process, handle,
+ GetCurrentProcess(),
+ &result, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ throwWindowsError(L"DuplicateHandle of process handle");
+ }
+ return OwnedHandle(result);
+}
+
+WINPTY_API BOOL
+winpty_spawn(winpty_t *wp,
+ const winpty_spawn_config_t *cfg,
+ HANDLE *process_handle /*OPTIONAL*/,
+ HANDLE *thread_handle /*OPTIONAL*/,
+ DWORD *create_process_error /*OPTIONAL*/,
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT(wp != nullptr && cfg != nullptr);
+
+ if (process_handle != nullptr) { *process_handle = nullptr; }
+ if (thread_handle != nullptr) { *thread_handle = nullptr; }
+ if (create_process_error != nullptr) { *create_process_error = 0; }
+
+ LockGuard<Mutex> lock(wp->mutex);
+ RpcOperation rpc(*wp);
+
+ // Send spawn request.
+ auto packet = newPacket();
+ packet.putInt32(AgentMsg::StartProcess);
+ packet.putInt64(cfg->winptyFlags);
+ packet.putInt32(process_handle != nullptr);
+ packet.putInt32(thread_handle != nullptr);
+ packet.putWString(cfg->appname);
+ packet.putWString(cfg->cmdline);
+ packet.putWString(cfg->cwd);
+ packet.putWString(cfg->env);
+ packet.putWString(wp->spawnDesktopName);
+ writePacket(*wp, packet);
+
+ // Receive reply.
+ auto reply = readPacket(*wp);
+ const auto result = static_cast<StartProcessResult>(reply.getInt32());
+ if (result == StartProcessResult::CreateProcessFailed) {
+ const DWORD lastError = reply.getInt32();
+ reply.assertEof();
+ if (create_process_error != nullptr) {
+ *create_process_error = lastError;
+ }
+ rpc.success();
+ throw LibWinptyException(WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED,
+ L"CreateProcess failed");
+ } else if (result == StartProcessResult::ProcessCreated) {
+ const HANDLE remoteProcess = handleFromInt64(reply.getInt64());
+ const HANDLE remoteThread = handleFromInt64(reply.getInt64());
+ reply.assertEof();
+ OwnedHandle localProcess;
+ OwnedHandle localThread;
+ if (remoteProcess != nullptr) {
+ localProcess =
+ stealHandle(wp->agentProcess.get(), remoteProcess);
+ }
+ if (remoteThread != nullptr) {
+ localThread =
+ stealHandle(wp->agentProcess.get(), remoteThread);
+ }
+ if (process_handle != nullptr) {
+ *process_handle = localProcess.release();
+ }
+ if (thread_handle != nullptr) {
+ *thread_handle = localThread.release();
+ }
+ rpc.success();
+ } else {
+ throwWinptyException(
+ L"Agent RPC error: invalid StartProcessResult");
+ }
+ return TRUE;
+ } API_CATCH(FALSE)
+}
+
+
+
+/*****************************************************************************
+ * winpty agent RPC calls: everything else */
+
+WINPTY_API BOOL
+winpty_set_size(winpty_t *wp, int cols, int rows,
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT(wp != nullptr && cols > 0 && rows > 0);
+ LockGuard<Mutex> lock(wp->mutex);
+ RpcOperation rpc(*wp);
+ auto packet = newPacket();
+ packet.putInt32(AgentMsg::SetSize);
+ packet.putInt32(cols);
+ packet.putInt32(rows);
+ writePacket(*wp, packet);
+ readPacket(*wp).assertEof();
+ rpc.success();
+ return TRUE;
+ } API_CATCH(FALSE)
+}
+
+WINPTY_API int
+winpty_get_console_process_list(winpty_t *wp, int *processList, const int processCount,
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
+ API_TRY {
+ ASSERT(wp != nullptr);
+ ASSERT(processList != nullptr);
+ LockGuard<Mutex> lock(wp->mutex);
+ RpcOperation rpc(*wp);
+ auto packet = newPacket();
+ packet.putInt32(AgentMsg::GetConsoleProcessList);
+ writePacket(*wp, packet);
+ auto reply = readPacket(*wp);
+
+ auto actualProcessCount = reply.getInt32();
+
+ if (actualProcessCount <= processCount) {
+ for (auto i = 0; i < actualProcessCount; i++) {
+ processList[i] = reply.getInt32();
+ }
+ }
+
+ reply.assertEof();
+ rpc.success();
+ return actualProcessCount;
+ } API_CATCH(0)
+}
+
+WINPTY_API void winpty_free(winpty_t *wp) {
+ // At least in principle, CloseHandle can fail, so this deletion can
+ // fail. It won't throw an exception, but maybe there's an error that
+ // should be propagated?
+ delete wp;
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/AgentMsg.h b/src/libs/3rdparty/winpty/src/shared/AgentMsg.h
new file mode 100644
index 0000000000..ab60c6b961
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/AgentMsg.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_AGENT_MSG_H
+#define WINPTY_SHARED_AGENT_MSG_H
+
+struct AgentMsg
+{
+ enum Type {
+ StartProcess,
+ SetSize,
+ GetConsoleProcessList,
+ };
+};
+
+enum class StartProcessResult {
+ CreateProcessFailed,
+ ProcessCreated,
+};
+
+#endif // WINPTY_SHARED_AGENT_MSG_H
diff --git a/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.cc b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.cc
new file mode 100644
index 0000000000..1bea7e53dd
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "BackgroundDesktop.h"
+
+#include <memory>
+
+#include "DebugClient.h"
+#include "StringUtil.h"
+#include "WinptyException.h"
+
+namespace {
+
+static std::wstring getObjectName(HANDLE object) {
+ BOOL success;
+ DWORD lengthNeeded = 0;
+ GetUserObjectInformationW(object, UOI_NAME,
+ nullptr, 0,
+ &lengthNeeded);
+ ASSERT(lengthNeeded % sizeof(wchar_t) == 0);
+ std::unique_ptr<wchar_t[]> tmp(
+ new wchar_t[lengthNeeded / sizeof(wchar_t)]);
+ success = GetUserObjectInformationW(object, UOI_NAME,
+ tmp.get(), lengthNeeded,
+ nullptr);
+ if (!success) {
+ throwWindowsError(L"GetUserObjectInformationW failed");
+ }
+ return std::wstring(tmp.get());
+}
+
+static std::wstring getDesktopName(HWINSTA winsta, HDESK desk) {
+ return getObjectName(winsta) + L"\\" + getObjectName(desk);
+}
+
+} // anonymous namespace
+
+// Get a non-interactive window station for the agent.
+// TODO: review security w.r.t. windowstation and desktop.
+BackgroundDesktop::BackgroundDesktop() {
+ try {
+ m_originalStation = GetProcessWindowStation();
+ if (m_originalStation == nullptr) {
+ throwWindowsError(
+ L"BackgroundDesktop ctor: "
+ L"GetProcessWindowStation returned NULL");
+ }
+ m_newStation =
+ CreateWindowStationW(nullptr, 0, WINSTA_ALL_ACCESS, nullptr);
+ if (m_newStation == nullptr) {
+ throwWindowsError(
+ L"BackgroundDesktop ctor: CreateWindowStationW returned NULL");
+ }
+ if (!SetProcessWindowStation(m_newStation)) {
+ throwWindowsError(
+ L"BackgroundDesktop ctor: SetProcessWindowStation failed");
+ }
+ m_newDesktop = CreateDesktopW(
+ L"Default", nullptr, nullptr, 0, GENERIC_ALL, nullptr);
+ if (m_newDesktop == nullptr) {
+ throwWindowsError(
+ L"BackgroundDesktop ctor: CreateDesktopW failed");
+ }
+ m_newDesktopName = getDesktopName(m_newStation, m_newDesktop);
+ TRACE("Created background desktop: %s",
+ utf8FromWide(m_newDesktopName).c_str());
+ } catch (...) {
+ dispose();
+ throw;
+ }
+}
+
+void BackgroundDesktop::dispose() WINPTY_NOEXCEPT {
+ if (m_originalStation != nullptr) {
+ SetProcessWindowStation(m_originalStation);
+ m_originalStation = nullptr;
+ }
+ if (m_newDesktop != nullptr) {
+ CloseDesktop(m_newDesktop);
+ m_newDesktop = nullptr;
+ }
+ if (m_newStation != nullptr) {
+ CloseWindowStation(m_newStation);
+ m_newStation = nullptr;
+ }
+}
+
+std::wstring getCurrentDesktopName() {
+ // MSDN says that the handles returned by GetProcessWindowStation and
+ // GetThreadDesktop do not need to be passed to CloseWindowStation and
+ // CloseDesktop, respectively.
+ const HWINSTA winsta = GetProcessWindowStation();
+ if (winsta == nullptr) {
+ throwWindowsError(
+ L"getCurrentDesktopName: "
+ L"GetProcessWindowStation returned NULL");
+ }
+ const HDESK desk = GetThreadDesktop(GetCurrentThreadId());
+ if (desk == nullptr) {
+ throwWindowsError(
+ L"getCurrentDesktopName: "
+ L"GetThreadDesktop returned NULL");
+ }
+ return getDesktopName(winsta, desk);
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.h b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.h
new file mode 100644
index 0000000000..c692e57dc4
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/BackgroundDesktop.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_BACKGROUND_DESKTOP_H
+#define WINPTY_SHARED_BACKGROUND_DESKTOP_H
+
+#include <windows.h>
+
+#include <string>
+
+#include "WinptyException.h"
+
+class BackgroundDesktop {
+public:
+ BackgroundDesktop();
+ ~BackgroundDesktop() { dispose(); }
+ void dispose() WINPTY_NOEXCEPT;
+ const std::wstring &desktopName() const { return m_newDesktopName; }
+
+ BackgroundDesktop(const BackgroundDesktop &other) = delete;
+ BackgroundDesktop &operator=(const BackgroundDesktop &other) = delete;
+
+ // We can't default the move constructor and assignment operator with
+ // MSVC 2013. We *could* if we required at least MSVC 2015 to build.
+
+ BackgroundDesktop(BackgroundDesktop &&other) :
+ m_originalStation(other.m_originalStation),
+ m_newStation(other.m_newStation),
+ m_newDesktop(other.m_newDesktop),
+ m_newDesktopName(std::move(other.m_newDesktopName)) {
+ other.m_originalStation = nullptr;
+ other.m_newStation = nullptr;
+ other.m_newDesktop = nullptr;
+ }
+ BackgroundDesktop &operator=(BackgroundDesktop &&other) {
+ dispose();
+ m_originalStation = other.m_originalStation;
+ m_newStation = other.m_newStation;
+ m_newDesktop = other.m_newDesktop;
+ m_newDesktopName = std::move(other.m_newDesktopName);
+ other.m_originalStation = nullptr;
+ other.m_newStation = nullptr;
+ other.m_newDesktop = nullptr;
+ return *this;
+ }
+
+private:
+ HWINSTA m_originalStation = nullptr;
+ HWINSTA m_newStation = nullptr;
+ HDESK m_newDesktop = nullptr;
+ std::wstring m_newDesktopName;
+};
+
+std::wstring getCurrentDesktopName();
+
+#endif // WINPTY_SHARED_BACKGROUND_DESKTOP_H
diff --git a/src/libs/3rdparty/winpty/src/shared/Buffer.cc b/src/libs/3rdparty/winpty/src/shared/Buffer.cc
new file mode 100644
index 0000000000..158a629d56
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/Buffer.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Buffer.h"
+
+#include <stdint.h>
+
+#include "DebugClient.h"
+#include "WinptyAssert.h"
+
+// Define the READ_BUFFER_CHECK() macro. It *must* evaluate its condition,
+// exactly once.
+#define READ_BUFFER_CHECK(cond) \
+ do { \
+ if (!(cond)) { \
+ trace("decode error: %s", #cond); \
+ throw DecodeError(); \
+ } \
+ } while (false)
+
+enum class Piece : uint8_t { Int32, Int64, WString };
+
+void WriteBuffer::putRawData(const void *data, size_t len) {
+ const auto p = reinterpret_cast<const char*>(data);
+ m_buf.insert(m_buf.end(), p, p + len);
+}
+
+void WriteBuffer::replaceRawData(size_t pos, const void *data, size_t len) {
+ ASSERT(pos <= m_buf.size() && len <= m_buf.size() - pos);
+ const auto p = reinterpret_cast<const char*>(data);
+ std::copy(p, p + len, &m_buf[pos]);
+}
+
+void WriteBuffer::putInt32(int32_t i) {
+ putRawValue(Piece::Int32);
+ putRawValue(i);
+}
+
+void WriteBuffer::putInt64(int64_t i) {
+ putRawValue(Piece::Int64);
+ putRawValue(i);
+}
+
+// len is in characters, excluding NUL, i.e. the number of wchar_t elements
+void WriteBuffer::putWString(const wchar_t *str, size_t len) {
+ putRawValue(Piece::WString);
+ putRawValue(static_cast<uint64_t>(len));
+ putRawData(str, sizeof(wchar_t) * len);
+}
+
+void ReadBuffer::getRawData(void *data, size_t len) {
+ ASSERT(m_off <= m_buf.size());
+ READ_BUFFER_CHECK(len <= m_buf.size() - m_off);
+ const char *const inp = &m_buf[m_off];
+ std::copy(inp, inp + len, reinterpret_cast<char*>(data));
+ m_off += len;
+}
+
+int32_t ReadBuffer::getInt32() {
+ READ_BUFFER_CHECK(getRawValue<Piece>() == Piece::Int32);
+ return getRawValue<int32_t>();
+}
+
+int64_t ReadBuffer::getInt64() {
+ READ_BUFFER_CHECK(getRawValue<Piece>() == Piece::Int64);
+ return getRawValue<int64_t>();
+}
+
+std::wstring ReadBuffer::getWString() {
+ READ_BUFFER_CHECK(getRawValue<Piece>() == Piece::WString);
+ const uint64_t charLen = getRawValue<uint64_t>();
+ READ_BUFFER_CHECK(charLen <= SIZE_MAX / sizeof(wchar_t));
+ // To be strictly conforming, we can't use the convenient wstring
+ // constructor, because the string in m_buf mightn't be aligned.
+ std::wstring ret;
+ if (charLen > 0) {
+ const size_t byteLen = charLen * sizeof(wchar_t);
+ ret.resize(charLen);
+ getRawData(&ret[0], byteLen);
+ }
+ return ret;
+}
+
+void ReadBuffer::assertEof() {
+ READ_BUFFER_CHECK(m_off == m_buf.size());
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/Buffer.h b/src/libs/3rdparty/winpty/src/shared/Buffer.h
new file mode 100644
index 0000000000..c2dd382e5b
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/Buffer.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_BUFFER_H
+#define WINPTY_SHARED_BUFFER_H
+
+#include <stdint.h>
+#include <string.h>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include <string>
+
+#include "WinptyException.h"
+
+class WriteBuffer {
+private:
+ std::vector<char> m_buf;
+
+public:
+ WriteBuffer() {}
+
+ template <typename T> void putRawValue(const T &t) {
+ putRawData(&t, sizeof(t));
+ }
+ template <typename T> void replaceRawValue(size_t pos, const T &t) {
+ replaceRawData(pos, &t, sizeof(t));
+ }
+
+ void putRawData(const void *data, size_t len);
+ void replaceRawData(size_t pos, const void *data, size_t len);
+ void putInt32(int32_t i);
+ void putInt64(int64_t i);
+ void putWString(const wchar_t *str, size_t len);
+ void putWString(const wchar_t *str) { putWString(str, wcslen(str)); }
+ void putWString(const std::wstring &str) { putWString(str.data(), str.size()); }
+ std::vector<char> &buf() { return m_buf; }
+
+ // MSVC 2013 does not generate these automatically, so help it out.
+ WriteBuffer(WriteBuffer &&other) : m_buf(std::move(other.m_buf)) {}
+ WriteBuffer &operator=(WriteBuffer &&other) {
+ m_buf = std::move(other.m_buf);
+ return *this;
+ }
+};
+
+class ReadBuffer {
+public:
+ class DecodeError : public WinptyException {
+ virtual const wchar_t *what() const WINPTY_NOEXCEPT override {
+ return L"DecodeError: RPC message decoding error";
+ }
+ };
+
+private:
+ std::vector<char> m_buf;
+ size_t m_off = 0;
+
+public:
+ explicit ReadBuffer(std::vector<char> &&buf) : m_buf(std::move(buf)) {}
+
+ template <typename T> T getRawValue() {
+ T ret = {};
+ getRawData(&ret, sizeof(ret));
+ return ret;
+ }
+
+ void getRawData(void *data, size_t len);
+ int32_t getInt32();
+ int64_t getInt64();
+ std::wstring getWString();
+ void assertEof();
+
+ // MSVC 2013 does not generate these automatically, so help it out.
+ ReadBuffer(ReadBuffer &&other) :
+ m_buf(std::move(other.m_buf)), m_off(other.m_off) {}
+ ReadBuffer &operator=(ReadBuffer &&other) {
+ m_buf = std::move(other.m_buf);
+ m_off = other.m_off;
+ return *this;
+ }
+};
+
+#endif // WINPTY_SHARED_BUFFER_H
diff --git a/src/libs/3rdparty/winpty/src/shared/DebugClient.cc b/src/libs/3rdparty/winpty/src/shared/DebugClient.cc
new file mode 100644
index 0000000000..bafe0c8954
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/DebugClient.cc
@@ -0,0 +1,187 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "DebugClient.h"
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <string>
+
+#include "winpty_snprintf.h"
+
+const wchar_t *const kPipeName = L"\\\\.\\pipe\\DebugServer";
+
+void *volatile g_debugConfig;
+
+namespace {
+
+// It would be easy to accidentally trample on the Windows LastError value
+// by adding logging/debugging code. Ensure that can't happen by saving and
+// restoring the value. This saving and restoring doesn't happen along the
+// fast path.
+class PreserveLastError {
+public:
+ PreserveLastError() : m_lastError(GetLastError()) {}
+ ~PreserveLastError() { SetLastError(m_lastError); }
+private:
+ DWORD m_lastError;
+};
+
+} // anonymous namespace
+
+static void sendToDebugServer(const char *message)
+{
+ HANDLE tracePipe = INVALID_HANDLE_VALUE;
+
+ do {
+ // The default impersonation level is SECURITY_IMPERSONATION, which allows
+ // a sufficiently authorized named pipe server to impersonate the client.
+ // There's no need for impersonation in this debugging system, so reduce
+ // the impersonation level to SECURITY_IDENTIFICATION, which allows a
+ // server to merely identify us.
+ tracePipe = CreateFileW(
+ kPipeName,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, OPEN_EXISTING,
+ SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION,
+ NULL);
+ } while (tracePipe == INVALID_HANDLE_VALUE &&
+ GetLastError() == ERROR_PIPE_BUSY &&
+ WaitNamedPipeW(kPipeName, NMPWAIT_WAIT_FOREVER));
+
+ if (tracePipe != INVALID_HANDLE_VALUE) {
+ DWORD newMode = PIPE_READMODE_MESSAGE;
+ SetNamedPipeHandleState(tracePipe, &newMode, NULL, NULL);
+ char response[16];
+ DWORD actual = 0;
+ TransactNamedPipe(tracePipe,
+ const_cast<char*>(message), strlen(message),
+ response, sizeof(response), &actual, NULL);
+ CloseHandle(tracePipe);
+ }
+}
+
+// Get the current UTC time as milliseconds from the epoch (ignoring leap
+// seconds). Use the Unix epoch for consistency with DebugClient.py. There
+// are 134774 days between 1601-01-01 (the Win32 epoch) and 1970-01-01 (the
+// Unix epoch).
+static long long unixTimeMillis()
+{
+ FILETIME fileTime;
+ GetSystemTimeAsFileTime(&fileTime);
+ long long msTime = (((long long)fileTime.dwHighDateTime << 32) +
+ fileTime.dwLowDateTime) / 10000;
+ return msTime - 134774LL * 24 * 3600 * 1000;
+}
+
+static const char *getDebugConfig()
+{
+ if (g_debugConfig == NULL) {
+ PreserveLastError preserve;
+ const int bufSize = 256;
+ char buf[bufSize];
+ DWORD actualSize =
+ GetEnvironmentVariableA("WINPTY_DEBUG", buf, bufSize);
+ if (actualSize == 0 || actualSize >= static_cast<DWORD>(bufSize)) {
+ buf[0] = '\0';
+ }
+ const size_t len = strlen(buf) + 1;
+ char *newConfig = new char[len];
+ std::copy(buf, buf + len, newConfig);
+ void *oldValue = InterlockedCompareExchangePointer(
+ &g_debugConfig, newConfig, NULL);
+ if (oldValue != NULL) {
+ delete [] newConfig;
+ }
+ }
+ return static_cast<const char*>(g_debugConfig);
+}
+
+bool isTracingEnabled()
+{
+ static bool disabled, enabled;
+ if (disabled) {
+ return false;
+ } else if (enabled) {
+ return true;
+ } else {
+ // Recognize WINPTY_DEBUG=1 for backwards compatibility.
+ PreserveLastError preserve;
+ bool value = hasDebugFlag("trace") || hasDebugFlag("1");
+ disabled = !value;
+ enabled = value;
+ return value;
+ }
+}
+
+bool hasDebugFlag(const char *flag)
+{
+ if (strchr(flag, ',') != NULL) {
+ trace("INTERNAL ERROR: hasDebugFlag flag has comma: '%s'", flag);
+ abort();
+ }
+ const char *const configCStr = getDebugConfig();
+ if (configCStr[0] == '\0') {
+ return false;
+ }
+ PreserveLastError preserve;
+ std::string config(configCStr);
+ std::string flagStr(flag);
+ config = "," + config + ",";
+ flagStr = "," + flagStr + ",";
+ return config.find(flagStr) != std::string::npos;
+}
+
+void trace(const char *format, ...)
+{
+ if (!isTracingEnabled())
+ return;
+
+ PreserveLastError preserve;
+ char message[1024];
+
+ va_list ap;
+ va_start(ap, format);
+ winpty_vsnprintf(message, format, ap);
+ message[sizeof(message) - 1] = '\0';
+ va_end(ap);
+
+ const int currentTime = (int)(unixTimeMillis() % (100000 * 1000));
+
+ char moduleName[1024];
+ moduleName[0] = '\0';
+ GetModuleFileNameA(NULL, moduleName, sizeof(moduleName));
+ const char *baseName = strrchr(moduleName, '\\');
+ baseName = (baseName != NULL) ? baseName + 1 : moduleName;
+
+ char fullMessage[1024];
+ winpty_snprintf(fullMessage,
+ "[%05d.%03d %s,p%04d,t%04d]: %s",
+ currentTime / 1000, currentTime % 1000,
+ baseName, (int)GetCurrentProcessId(), (int)GetCurrentThreadId(),
+ message);
+ fullMessage[sizeof(fullMessage) - 1] = '\0';
+
+ sendToDebugServer(fullMessage);
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/DebugClient.h b/src/libs/3rdparty/winpty/src/shared/DebugClient.h
new file mode 100644
index 0000000000..b126071130
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/DebugClient.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2011-2012 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef DEBUGCLIENT_H
+#define DEBUGCLIENT_H
+
+#include "winpty_snprintf.h"
+
+bool isTracingEnabled();
+bool hasDebugFlag(const char *flag);
+void trace(const char *format, ...) WINPTY_SNPRINTF_FORMAT(1, 2);
+
+// This macro calls trace without evaluating the arguments.
+#define TRACE(format, ...) \
+ do { \
+ if (isTracingEnabled()) { \
+ trace((format), ## __VA_ARGS__); \
+ } \
+ } while (false)
+
+#endif // DEBUGCLIENT_H
diff --git a/src/libs/3rdparty/winpty/src/shared/GenRandom.cc b/src/libs/3rdparty/winpty/src/shared/GenRandom.cc
new file mode 100644
index 0000000000..6d7920643a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/GenRandom.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "GenRandom.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "DebugClient.h"
+#include "StringBuilder.h"
+
+static volatile LONG g_pipeCounter;
+
+GenRandom::GenRandom() : m_advapi32(L"advapi32.dll") {
+ // First try to use the pseudo-documented RtlGenRandom function from
+ // advapi32.dll. Creating a CryptoAPI context is slow, and RtlGenRandom
+ // avoids the overhead. It's documented in this blog post[1] and on
+ // MSDN[2] with a disclaimer about future breakage. This technique is
+ // apparently built-in into the MSVC CRT, though, for the rand_s function,
+ // so perhaps it is stable enough.
+ //
+ // [1] http://blogs.msdn.com/b/michael_howard/archive/2005/01/14/353379.aspx
+ // [2] https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694(v=vs.85).aspx
+ //
+ // Both RtlGenRandom and the Crypto API functions exist in XP and up.
+ m_rtlGenRandom = reinterpret_cast<RtlGenRandom_t*>(
+ m_advapi32.proc("SystemFunction036"));
+ // The OsModule class logs an error message if the proc is nullptr.
+ if (m_rtlGenRandom != nullptr) {
+ return;
+ }
+
+ // Fall back to the crypto API.
+ m_cryptProvIsValid =
+ CryptAcquireContext(&m_cryptProv, nullptr, nullptr,
+ PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) != 0;
+ if (!m_cryptProvIsValid) {
+ trace("GenRandom: CryptAcquireContext failed: %u",
+ static_cast<unsigned>(GetLastError()));
+ }
+}
+
+GenRandom::~GenRandom() {
+ if (m_cryptProvIsValid) {
+ CryptReleaseContext(m_cryptProv, 0);
+ }
+}
+
+// Returns false if the context is invalid or the generation fails.
+bool GenRandom::fillBuffer(void *buffer, size_t size) {
+ memset(buffer, 0, size);
+ bool success = false;
+ if (m_rtlGenRandom != nullptr) {
+ success = m_rtlGenRandom(buffer, size) != 0;
+ if (!success) {
+ trace("GenRandom: RtlGenRandom/SystemFunction036 failed: %u",
+ static_cast<unsigned>(GetLastError()));
+ }
+ } else if (m_cryptProvIsValid) {
+ success =
+ CryptGenRandom(m_cryptProv, size,
+ reinterpret_cast<BYTE*>(buffer)) != 0;
+ if (!success) {
+ trace("GenRandom: CryptGenRandom failed, size=%d, lasterror=%u",
+ static_cast<int>(size),
+ static_cast<unsigned>(GetLastError()));
+ }
+ }
+ return success;
+}
+
+// Returns an empty string if either of CryptAcquireContext or CryptGenRandom
+// fail.
+std::string GenRandom::randomBytes(size_t numBytes) {
+ std::string ret(numBytes, '\0');
+ if (!fillBuffer(&ret[0], numBytes)) {
+ return std::string();
+ }
+ return ret;
+}
+
+std::wstring GenRandom::randomHexString(size_t numBytes) {
+ const std::string bytes = randomBytes(numBytes);
+ std::wstring ret(bytes.size() * 2, L'\0');
+ for (size_t i = 0; i < bytes.size(); ++i) {
+ static const wchar_t hex[] = L"0123456789abcdef";
+ ret[i * 2] = hex[static_cast<uint8_t>(bytes[i]) >> 4];
+ ret[i * 2 + 1] = hex[static_cast<uint8_t>(bytes[i]) & 0xF];
+ }
+ return ret;
+}
+
+// Returns a 64-bit value representing the number of 100-nanosecond intervals
+// since January 1, 1601.
+static uint64_t systemTimeAsUInt64() {
+ FILETIME monotonicTime = {};
+ GetSystemTimeAsFileTime(&monotonicTime);
+ return (static_cast<uint64_t>(monotonicTime.dwHighDateTime) << 32) |
+ static_cast<uint64_t>(monotonicTime.dwLowDateTime);
+}
+
+// Generates a unique and hard-to-guess case-insensitive string suitable for
+// use in a pipe filename or a Windows object name.
+std::wstring GenRandom::uniqueName() {
+ // First include enough information to avoid collisions assuming
+ // cooperative software. This code assumes that a process won't die and
+ // be replaced with a recycled PID within a single GetSystemTimeAsFileTime
+ // interval.
+ WStringBuilder sb(64);
+ sb << GetCurrentProcessId()
+ << L'-' << InterlockedIncrement(&g_pipeCounter)
+ << L'-' << whexOfInt(systemTimeAsUInt64());
+ // It isn't clear to me how the crypto APIs would fail. It *probably*
+ // doesn't matter that much anyway? In principle, a predictable pipe name
+ // is subject to a local denial-of-service attack.
+ auto random = randomHexString(16);
+ if (!random.empty()) {
+ sb << L'-' << random;
+ }
+ return sb.str_moved();
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/GenRandom.h b/src/libs/3rdparty/winpty/src/shared/GenRandom.h
new file mode 100644
index 0000000000..746cb1ecf7
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/GenRandom.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_GEN_RANDOM_H
+#define WINPTY_GEN_RANDOM_H
+
+// The original MinGW requires that we include wincrypt.h. With MinGW-w64 and
+// MSVC, including windows.h is sufficient.
+#include <windows.h>
+#include <wincrypt.h>
+
+#include <string>
+
+#include "OsModule.h"
+
+class GenRandom {
+ typedef BOOLEAN WINAPI RtlGenRandom_t(PVOID, ULONG);
+
+ OsModule m_advapi32;
+ RtlGenRandom_t *m_rtlGenRandom = nullptr;
+ bool m_cryptProvIsValid = false;
+ HCRYPTPROV m_cryptProv = 0;
+
+public:
+ GenRandom();
+ ~GenRandom();
+ bool fillBuffer(void *buffer, size_t size);
+ std::string randomBytes(size_t numBytes);
+ std::wstring randomHexString(size_t numBytes);
+ std::wstring uniqueName();
+
+ // Return true if the crypto context was successfully initialized.
+ bool valid() const {
+ return m_rtlGenRandom != nullptr || m_cryptProvIsValid;
+ }
+};
+
+#endif // WINPTY_GEN_RANDOM_H
diff --git a/src/libs/3rdparty/winpty/src/shared/GetCommitHash.bat b/src/libs/3rdparty/winpty/src/shared/GetCommitHash.bat
new file mode 100644
index 0000000000..a9f8e9cef0
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/GetCommitHash.bat
@@ -0,0 +1,13 @@
+@echo off
+
+REM -- Echo the git commit hash. If git isn't available for some reason,
+REM -- output nothing instead.
+
+git rev-parse HEAD >NUL 2>NUL && (
+ git rev-parse HEAD
+) || (
+ echo none
+)
+
+REM -- Set ERRORLEVEL to 0 using this cryptic syntax.
+(call )
diff --git a/src/libs/3rdparty/winpty/src/shared/Mutex.h b/src/libs/3rdparty/winpty/src/shared/Mutex.h
new file mode 100644
index 0000000000..98215365ad
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/Mutex.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+// Recent 4.x MinGW and MinGW-w64 gcc compilers lack std::mutex and
+// std::lock_guard. I have a 5.2.0 MinGW-w64 compiler packaged through MSYS2
+// that *is* new enough, but that's one compiler against several deficient
+// ones. Wrap CRITICAL_SECTION instead.
+
+#ifndef WINPTY_SHARED_MUTEX_H
+#define WINPTY_SHARED_MUTEX_H
+
+#include <windows.h>
+
+class Mutex {
+ CRITICAL_SECTION m_mutex;
+public:
+ Mutex() { InitializeCriticalSection(&m_mutex); }
+ ~Mutex() { DeleteCriticalSection(&m_mutex); }
+ void lock() { EnterCriticalSection(&m_mutex); }
+ void unlock() { LeaveCriticalSection(&m_mutex); }
+
+ Mutex(const Mutex &other) = delete;
+ Mutex &operator=(const Mutex &other) = delete;
+};
+
+template <typename T>
+class LockGuard {
+ T &m_lock;
+public:
+ LockGuard(T &lock) : m_lock(lock) { m_lock.lock(); }
+ ~LockGuard() { m_lock.unlock(); }
+
+ LockGuard(const LockGuard &other) = delete;
+ LockGuard &operator=(const LockGuard &other) = delete;
+};
+
+#endif // WINPTY_SHARED_MUTEX_H
diff --git a/src/libs/3rdparty/winpty/src/shared/OsModule.h b/src/libs/3rdparty/winpty/src/shared/OsModule.h
new file mode 100644
index 0000000000..9713fa2b2d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/OsModule.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_OS_MODULE_H
+#define WINPTY_SHARED_OS_MODULE_H
+
+#include <windows.h>
+
+#include <string>
+
+#include "DebugClient.h"
+#include "WinptyAssert.h"
+#include "WinptyException.h"
+
+class OsModule {
+ HMODULE m_module;
+public:
+ enum class LoadErrorBehavior { Abort, Throw };
+ OsModule(const wchar_t *fileName,
+ LoadErrorBehavior behavior=LoadErrorBehavior::Abort) {
+ m_module = LoadLibraryW(fileName);
+ if (behavior == LoadErrorBehavior::Abort) {
+ ASSERT(m_module != NULL);
+ } else {
+ if (m_module == nullptr) {
+ const auto err = GetLastError();
+ throwWindowsError(
+ (L"LoadLibraryW error: " + std::wstring(fileName)).c_str(),
+ err);
+ }
+ }
+ }
+ ~OsModule() {
+ FreeLibrary(m_module);
+ }
+ HMODULE handle() const { return m_module; }
+ FARPROC proc(const char *funcName) {
+ FARPROC ret = GetProcAddress(m_module, funcName);
+ if (ret == NULL) {
+ trace("GetProcAddress: %s is missing", funcName);
+ }
+ return ret;
+ }
+};
+
+#endif // WINPTY_SHARED_OS_MODULE_H
diff --git a/src/libs/3rdparty/winpty/src/shared/OwnedHandle.cc b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.cc
new file mode 100644
index 0000000000..7b173536e6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "OwnedHandle.h"
+
+#include "DebugClient.h"
+#include "WinptyException.h"
+
+void OwnedHandle::dispose(bool nothrow) {
+ if (m_h != nullptr && m_h != INVALID_HANDLE_VALUE) {
+ if (!CloseHandle(m_h)) {
+ trace("CloseHandle(%p) failed", m_h);
+ if (!nothrow) {
+ throwWindowsError(L"CloseHandle failed");
+ }
+ }
+ }
+ m_h = nullptr;
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/OwnedHandle.h b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.h
new file mode 100644
index 0000000000..70a8d6163a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/OwnedHandle.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_OWNED_HANDLE_H
+#define WINPTY_SHARED_OWNED_HANDLE_H
+
+#include <windows.h>
+
+class OwnedHandle {
+ HANDLE m_h;
+public:
+ OwnedHandle() : m_h(nullptr) {}
+ explicit OwnedHandle(HANDLE h) : m_h(h) {}
+ ~OwnedHandle() { dispose(true); }
+ void dispose(bool nothrow=false);
+ HANDLE get() const { return m_h; }
+ HANDLE release() { HANDLE ret = m_h; m_h = nullptr; return ret; }
+ OwnedHandle(const OwnedHandle &other) = delete;
+ OwnedHandle(OwnedHandle &&other) : m_h(other.release()) {}
+ OwnedHandle &operator=(const OwnedHandle &other) = delete;
+ OwnedHandle &operator=(OwnedHandle &&other) {
+ dispose();
+ m_h = other.release();
+ return *this;
+ }
+};
+
+#endif // WINPTY_SHARED_OWNED_HANDLE_H
diff --git a/src/libs/3rdparty/winpty/src/shared/PrecompiledHeader.h b/src/libs/3rdparty/winpty/src/shared/PrecompiledHeader.h
new file mode 100644
index 0000000000..7d9b8f8b4a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/PrecompiledHeader.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_PRECOMPILED_HEADER_H
+#define WINPTY_PRECOMPILED_HEADER_H
+
+#include <windows.h>
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <array>
+#include <limits>
+#include <memory>
+#include <new>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#endif // WINPTY_PRECOMPILED_HEADER_H
diff --git a/src/libs/3rdparty/winpty/src/shared/StringBuilder.h b/src/libs/3rdparty/winpty/src/shared/StringBuilder.h
new file mode 100644
index 0000000000..f3155bdd29
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/StringBuilder.h
@@ -0,0 +1,227 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+// Efficient integer->string conversion and string concatenation. The
+// hexadecimal conversion may optionally have leading zeros. Other ways to
+// convert integers to strings in C++ suffer these drawbacks:
+//
+// * std::stringstream: Inefficient, even more so than stdio.
+//
+// * std::to_string: No hexadecimal output, tends to use heap allocation, not
+// supported on Cygwin.
+//
+// * stdio routines: Requires parsing a format string (inefficient). The
+// caller *must* know how large the content is for correctness. The
+// string-printf functions are extremely inconsistent on Windows. In
+// particular, 64-bit integers, wide strings, and return values are
+// problem areas.
+//
+// StringBuilderTest.cc is a standalone program that tests this header.
+
+#ifndef WINPTY_STRING_BUILDER_H
+#define WINPTY_STRING_BUILDER_H
+
+#include <array>
+#include <string>
+#include <type_traits>
+
+#ifdef STRING_BUILDER_TESTING
+#include <assert.h>
+#define STRING_BUILDER_CHECK(cond) assert(cond)
+#else
+#define STRING_BUILDER_CHECK(cond)
+#endif // STRING_BUILDER_TESTING
+
+#include "WinptyAssert.h"
+
+template <typename C, size_t sz>
+struct ValueString {
+ std::array<C, sz> m_array;
+ size_t m_offset;
+ size_t m_size;
+
+ const C *c_str() const { return m_array.data() + m_offset; }
+ const C *data() const { return m_array.data() + m_offset; }
+ size_t size() const { return m_size; }
+ std::basic_string<C> str() const {
+ return std::basic_string<C>(data(), m_size);
+ }
+};
+
+#ifdef _MSC_VER
+// Disable an MSVC /SDL error that forbids unsigned negation. Signed negation
+// invokes undefined behavior for INTxx_MIN, so unsigned negation is simpler to
+// reason about. (We assume twos-complement in any case.)
+#define STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(x) \
+ ( \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4146)) \
+ (x) \
+ __pragma(warning(pop)) \
+ )
+#else
+#define STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(x) (x)
+#endif
+
+// Formats an integer as decimal without leading zeros.
+template <typename C, typename I>
+ValueString<C, sizeof(I) * 3 + 1 + 1> gdecOfInt(const I value) {
+ typedef typename std::make_unsigned<I>::type U;
+ auto unsValue = static_cast<U>(value);
+ const bool isNegative = (value < 0);
+ if (isNegative) {
+ unsValue = STRING_BUILDER_ALLOW_UNSIGNED_NEGATE(-unsValue);
+ }
+ decltype(gdecOfInt<C, I>(value)) out;
+ auto &arr = out.m_array;
+ C *const endp = arr.data() + arr.size();
+ C *outp = endp;
+ *(--outp) = '\0';
+ STRING_BUILDER_CHECK(outp >= arr.data());
+ do {
+ const int digit = unsValue % 10;
+ unsValue /= 10;
+ *(--outp) = '0' + digit;
+ STRING_BUILDER_CHECK(outp >= arr.data());
+ } while (unsValue != 0);
+ if (isNegative) {
+ *(--outp) = '-';
+ STRING_BUILDER_CHECK(outp >= arr.data());
+ }
+ out.m_offset = outp - arr.data();
+ out.m_size = endp - outp - 1;
+ return out;
+}
+
+template <typename I> decltype(gdecOfInt<char, I>(0)) decOfInt(I i) {
+ return gdecOfInt<char>(i);
+}
+
+template <typename I> decltype(gdecOfInt<wchar_t, I>(0)) wdecOfInt(I i) {
+ return gdecOfInt<wchar_t>(i);
+}
+
+// Formats an integer as hexadecimal, with or without leading zeros.
+template <typename C, bool leadingZeros=false, typename I>
+ValueString<C, sizeof(I) * 2 + 1> ghexOfInt(const I value) {
+ typedef typename std::make_unsigned<I>::type U;
+ const auto unsValue = static_cast<U>(value);
+ static const C hex[16] = {'0','1','2','3','4','5','6','7',
+ '8','9','a','b','c','d','e','f'};
+ decltype(ghexOfInt<C, leadingZeros, I>(value)) out;
+ auto &arr = out.m_array;
+ C *outp = arr.data();
+ int inIndex = 0;
+ int shift = sizeof(I) * 8 - 4;
+ const int len = sizeof(I) * 2;
+ if (!leadingZeros) {
+ for (; inIndex < len - 1; ++inIndex, shift -= 4) {
+ STRING_BUILDER_CHECK(shift >= 0 && shift < sizeof(unsValue) * 8);
+ const int digit = (unsValue >> shift) & 0xF;
+ if (digit != 0) {
+ break;
+ }
+ }
+ }
+ for (; inIndex < len; ++inIndex, shift -= 4) {
+ const int digit = (unsValue >> shift) & 0xF;
+ *(outp++) = hex[digit];
+ STRING_BUILDER_CHECK(outp <= arr.data() + arr.size());
+ }
+ *(outp++) = '\0';
+ STRING_BUILDER_CHECK(outp <= arr.data() + arr.size());
+ out.m_offset = 0;
+ out.m_size = outp - arr.data() - 1;
+ return out;
+}
+
+template <bool leadingZeros=false, typename I>
+decltype(ghexOfInt<char, leadingZeros, I>(0)) hexOfInt(I i) {
+ return ghexOfInt<char, leadingZeros, I>(i);
+}
+
+template <bool leadingZeros=false, typename I>
+decltype(ghexOfInt<wchar_t, leadingZeros, I>(0)) whexOfInt(I i) {
+ return ghexOfInt<wchar_t, leadingZeros, I>(i);
+}
+
+template <typename C>
+class GStringBuilder {
+public:
+ typedef std::basic_string<C> StringType;
+
+ GStringBuilder() {}
+ GStringBuilder(size_t capacity) {
+ m_out.reserve(capacity);
+ }
+
+ GStringBuilder &operator<<(C ch) { m_out.push_back(ch); return *this; }
+ GStringBuilder &operator<<(const C *str) { m_out.append(str); return *this; }
+ GStringBuilder &operator<<(const StringType &str) { m_out.append(str); return *this; }
+
+ template <size_t sz>
+ GStringBuilder &operator<<(const ValueString<C, sz> &str) {
+ m_out.append(str.data(), str.size());
+ return *this;
+ }
+
+private:
+ // Forbid output of char/wchar_t for GStringBuilder if the type doesn't
+ // exactly match the builder element type. The code still allows
+ // signed char and unsigned char, but I'm a little worried about what
+ // happens if a user tries to output int8_t or uint8_t.
+ template <typename P>
+ typename std::enable_if<
+ (std::is_same<P, char>::value || std::is_same<P, wchar_t>::value) &&
+ !std::is_same<C, P>::value, GStringBuilder&>::type
+ operator<<(P ch) {
+ ASSERT(false && "Method was not supposed to be reachable.");
+ return *this;
+ }
+
+public:
+ GStringBuilder &operator<<(short i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(unsigned short i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(int i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(unsigned int i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(long i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(unsigned long i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(long long i) { return *this << gdecOfInt<C>(i); }
+ GStringBuilder &operator<<(unsigned long long i) { return *this << gdecOfInt<C>(i); }
+
+ GStringBuilder &operator<<(const void *p) {
+ m_out.push_back(static_cast<C>('0'));
+ m_out.push_back(static_cast<C>('x'));
+ *this << ghexOfInt<C>(reinterpret_cast<uintptr_t>(p));
+ return *this;
+ }
+
+ StringType str() { return m_out; }
+ StringType str_moved() { return std::move(m_out); }
+ const C *c_str() const { return m_out.c_str(); }
+
+private:
+ StringType m_out;
+};
+
+typedef GStringBuilder<char> StringBuilder;
+typedef GStringBuilder<wchar_t> WStringBuilder;
+
+#endif // WINPTY_STRING_BUILDER_H
diff --git a/src/libs/3rdparty/winpty/src/shared/StringBuilderTest.cc b/src/libs/3rdparty/winpty/src/shared/StringBuilderTest.cc
new file mode 100644
index 0000000000..e6c2d3138c
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/StringBuilderTest.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#define STRING_BUILDER_TESTING
+
+#include "StringBuilder.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <iomanip>
+#include <sstream>
+
+void display(const std::string &str) { fprintf(stderr, "%s", str.c_str()); }
+void display(const std::wstring &str) { fprintf(stderr, "%ls", str.c_str()); }
+
+#define CHECK_EQ(x, y) \
+ do { \
+ const auto xval = (x); \
+ const auto yval = (y); \
+ if (xval != yval) { \
+ fprintf(stderr, "error: %s:%d: %s != %s: ", \
+ __FILE__, __LINE__, #x, #y); \
+ display(xval); \
+ fprintf(stderr, " != "); \
+ display(yval); \
+ fprintf(stderr, "\n"); \
+ } \
+ } while(0)
+
+template <typename C, typename I>
+std::basic_string<C> decOfIntSS(const I value) {
+ // std::to_string and std::to_wstring are missing in Cygwin as of this
+ // writing (early 2016).
+ std::basic_stringstream<C> ss;
+ ss << +value; // We must promote char to print it as an integer.
+ return ss.str();
+}
+
+
+template <typename C, bool leadingZeros=false, typename I>
+std::basic_string<C> hexOfIntSS(const I value) {
+ typedef typename std::make_unsigned<I>::type U;
+ const unsigned long long u64Value = value & static_cast<U>(~0);
+ std::basic_stringstream<C> ss;
+ if (leadingZeros) {
+ ss << std::setfill(static_cast<C>('0')) << std::setw(sizeof(I) * 2);
+ }
+ ss << std::hex << u64Value;
+ return ss.str();
+}
+
+template <typename I>
+void testValue(I value) {
+ CHECK_EQ(decOfInt(value).str(), (decOfIntSS<char>(value)));
+ CHECK_EQ(wdecOfInt(value).str(), (decOfIntSS<wchar_t>(value)));
+ CHECK_EQ((hexOfInt<false>(value).str()), (hexOfIntSS<char, false>(value)));
+ CHECK_EQ((hexOfInt<true>(value).str()), (hexOfIntSS<char, true>(value)));
+ CHECK_EQ((whexOfInt<false>(value).str()), (hexOfIntSS<wchar_t, false>(value)));
+ CHECK_EQ((whexOfInt<true>(value).str()), (hexOfIntSS<wchar_t, true>(value)));
+}
+
+template <typename I>
+void testType() {
+ typedef typename std::make_unsigned<I>::type U;
+ const U quarter = static_cast<U>(1) << (sizeof(U) * 8 - 2);
+ for (unsigned quarterIndex = 0; quarterIndex < 4; ++quarterIndex) {
+ for (int offset = -18; offset <= 18; ++offset) {
+ const I value = quarter * quarterIndex + static_cast<U>(offset);
+ testValue(value);
+ }
+ }
+ testValue(static_cast<I>(42));
+ testValue(static_cast<I>(123456));
+ testValue(static_cast<I>(0xdeadfacecafebeefull));
+}
+
+int main() {
+ testType<char>();
+
+ testType<signed char>();
+ testType<signed short>();
+ testType<signed int>();
+ testType<signed long>();
+ testType<signed long long>();
+
+ testType<unsigned char>();
+ testType<unsigned short>();
+ testType<unsigned int>();
+ testType<unsigned long>();
+ testType<unsigned long long>();
+
+ StringBuilder() << static_cast<const void*>("TEST");
+ WStringBuilder() << static_cast<const void*>("TEST");
+
+ fprintf(stderr, "All tests completed!\n");
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/StringUtil.cc b/src/libs/3rdparty/winpty/src/shared/StringUtil.cc
new file mode 100644
index 0000000000..3a85a3ec94
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/StringUtil.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "StringUtil.h"
+
+#include <windows.h>
+
+#include "WinptyAssert.h"
+
+// Workaround. MinGW (from mingw.org) does not have wcsnlen. MinGW-w64 *does*
+// have wcsnlen, but use this function for consistency.
+size_t winpty_wcsnlen(const wchar_t *s, size_t maxlen) {
+ ASSERT(s != NULL);
+ for (size_t i = 0; i < maxlen; ++i) {
+ if (s[i] == L'\0') {
+ return i;
+ }
+ }
+ return maxlen;
+}
+
+std::string utf8FromWide(const std::wstring &input) {
+ int mblen = WideCharToMultiByte(
+ CP_UTF8, 0,
+ input.data(), input.size(),
+ NULL, 0, NULL, NULL);
+ if (mblen <= 0) {
+ return std::string();
+ }
+ std::vector<char> tmp(mblen);
+ int mblen2 = WideCharToMultiByte(
+ CP_UTF8, 0,
+ input.data(), input.size(),
+ tmp.data(), tmp.size(),
+ NULL, NULL);
+ ASSERT(mblen2 == mblen);
+ return std::string(tmp.data(), tmp.size());
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/StringUtil.h b/src/libs/3rdparty/winpty/src/shared/StringUtil.h
new file mode 100644
index 0000000000..e4bf3c9121
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/StringUtil.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_STRING_UTIL_H
+#define WINPTY_SHARED_STRING_UTIL_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "WinptyAssert.h"
+
+size_t winpty_wcsnlen(const wchar_t *s, size_t maxlen);
+std::string utf8FromWide(const std::wstring &input);
+
+// Return a vector containing each character in the string.
+template <typename T>
+std::vector<T> vectorFromString(const std::basic_string<T> &str) {
+ return std::vector<T>(str.begin(), str.end());
+}
+
+// Return a vector containing each character in the string, followed by a
+// NUL terminator.
+template <typename T>
+std::vector<T> vectorWithNulFromString(const std::basic_string<T> &str) {
+ std::vector<T> ret;
+ ret.reserve(str.size() + 1);
+ ret.insert(ret.begin(), str.begin(), str.end());
+ ret.push_back('\0');
+ return ret;
+}
+
+// A safer(?) version of wcsncpy that is accepted by MSVC's /SDL mode.
+template <size_t N>
+wchar_t *winpty_wcsncpy(wchar_t (&d)[N], const wchar_t *s) {
+ ASSERT(s != nullptr);
+ size_t i = 0;
+ for (; i < N; ++i) {
+ if (s[i] == L'\0') {
+ break;
+ }
+ d[i] = s[i];
+ }
+ for (; i < N; ++i) {
+ d[i] = L'\0';
+ }
+ return d;
+}
+
+// Like wcsncpy, but ensure that the destination buffer is NUL-terminated.
+template <size_t N>
+wchar_t *winpty_wcsncpy_nul(wchar_t (&d)[N], const wchar_t *s) {
+ static_assert(N > 0, "array cannot be 0-size");
+ winpty_wcsncpy(d, s);
+ d[N - 1] = L'\0';
+ return d;
+}
+
+#endif // WINPTY_SHARED_STRING_UTIL_H
diff --git a/src/libs/3rdparty/winpty/src/shared/TimeMeasurement.h b/src/libs/3rdparty/winpty/src/shared/TimeMeasurement.h
new file mode 100644
index 0000000000..716a027fcb
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/TimeMeasurement.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+// Convenience header library for using the high-resolution performance counter
+// to measure how long some process takes.
+
+#ifndef TIME_MEASUREMENT_H
+#define TIME_MEASUREMENT_H
+
+#include <windows.h>
+#include <assert.h>
+#include <stdint.h>
+
+class TimeMeasurement {
+public:
+ TimeMeasurement() {
+ static double freq = static_cast<double>(getFrequency());
+ m_freq = freq;
+ m_start = value();
+ }
+
+ double elapsed() {
+ uint64_t elapsedTicks = value() - m_start;
+ return static_cast<double>(elapsedTicks) / m_freq;
+ }
+
+private:
+ uint64_t getFrequency() {
+ LARGE_INTEGER freq;
+ BOOL success = QueryPerformanceFrequency(&freq);
+ assert(success && "QueryPerformanceFrequency failed");
+ return freq.QuadPart;
+ }
+
+ uint64_t value() {
+ LARGE_INTEGER ret;
+ BOOL success = QueryPerformanceCounter(&ret);
+ assert(success && "QueryPerformanceCounter failed");
+ return ret.QuadPart;
+ }
+
+ uint64_t m_start;
+ double m_freq;
+};
+
+#endif // TIME_MEASUREMENT_H
diff --git a/src/libs/3rdparty/winpty/src/shared/UnixCtrlChars.h b/src/libs/3rdparty/winpty/src/shared/UnixCtrlChars.h
new file mode 100644
index 0000000000..39dfa62ec9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/UnixCtrlChars.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNIX_CTRL_CHARS_H
+#define UNIX_CTRL_CHARS_H
+
+inline char decodeUnixCtrlChar(char ch) {
+ const char ctrlKeys[] = {
+ /* 0x00 */ '@', /* 0x01 */ 'A', /* 0x02 */ 'B', /* 0x03 */ 'C',
+ /* 0x04 */ 'D', /* 0x05 */ 'E', /* 0x06 */ 'F', /* 0x07 */ 'G',
+ /* 0x08 */ 'H', /* 0x09 */ 'I', /* 0x0A */ 'J', /* 0x0B */ 'K',
+ /* 0x0C */ 'L', /* 0x0D */ 'M', /* 0x0E */ 'N', /* 0x0F */ 'O',
+ /* 0x10 */ 'P', /* 0x11 */ 'Q', /* 0x12 */ 'R', /* 0x13 */ 'S',
+ /* 0x14 */ 'T', /* 0x15 */ 'U', /* 0x16 */ 'V', /* 0x17 */ 'W',
+ /* 0x18 */ 'X', /* 0x19 */ 'Y', /* 0x1A */ 'Z', /* 0x1B */ '[',
+ /* 0x1C */ '\\', /* 0x1D */ ']', /* 0x1E */ '^', /* 0x1F */ '_',
+ };
+ unsigned char uch = ch;
+ if (uch < 32) {
+ return ctrlKeys[uch];
+ } else if (uch == 127) {
+ return '?';
+ } else {
+ return '\0';
+ }
+}
+
+#endif // UNIX_CTRL_CHARS_H
diff --git a/src/libs/3rdparty/winpty/src/shared/UpdateGenVersion.bat b/src/libs/3rdparty/winpty/src/shared/UpdateGenVersion.bat
new file mode 100644
index 0000000000..ea2a7d64ed
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/UpdateGenVersion.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -- Echo the git commit hash. If git isn't available for some reason,
+rem -- output nothing instead.
+
+mkdir ..\gen 2>nul
+
+set /p VERSION=<..\..\VERSION.txt
+set COMMIT=%1
+
+echo // AUTO-GENERATED BY %0 %*>..\gen\GenVersion.h
+echo const char GenVersion_Version[] = "%VERSION%";>>..\gen\GenVersion.h
+echo const char GenVersion_Commit[] = "%COMMIT%";>>..\gen\GenVersion.h
+
+rem -- The winpty.gyp file expects the script to output the include directory,
+rem -- relative to src.
+echo gen
+
+rem -- Set ERRORLEVEL to 0 using this cryptic syntax.
+(call )
diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.cc b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.cc
new file mode 100644
index 0000000000..711a8637c8
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.cc
@@ -0,0 +1,460 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WindowsSecurity.h"
+
+#include <array>
+
+#include "DebugClient.h"
+#include "OsModule.h"
+#include "OwnedHandle.h"
+#include "StringBuilder.h"
+#include "WindowsVersion.h"
+#include "WinptyAssert.h"
+#include "WinptyException.h"
+
+namespace {
+
+struct LocalFreer {
+ void operator()(void *ptr) {
+ if (ptr != nullptr) {
+ LocalFree(reinterpret_cast<HLOCAL>(ptr));
+ }
+ }
+};
+
+typedef std::unique_ptr<void, LocalFreer> PointerLocal;
+
+template <typename T>
+SecurityItem<T> localItem(typename T::type v) {
+ typedef typename T::type P;
+ struct Impl : SecurityItem<T>::Impl {
+ P m_v;
+ Impl(P v) : m_v(v) {}
+ virtual ~Impl() {
+ LocalFree(reinterpret_cast<HLOCAL>(m_v));
+ }
+ };
+ return SecurityItem<T>(v, std::unique_ptr<Impl>(new Impl { v }));
+}
+
+Sid allocatedSid(PSID v) {
+ struct Impl : Sid::Impl {
+ PSID m_v;
+ Impl(PSID v) : m_v(v) {}
+ virtual ~Impl() {
+ if (m_v != nullptr) {
+ FreeSid(m_v);
+ }
+ }
+ };
+ return Sid(v, std::unique_ptr<Impl>(new Impl { v }));
+}
+
+} // anonymous namespace
+
+// Returns a handle to the thread's effective security token. If the thread
+// is impersonating another user, its token is returned, and otherwise, the
+// process' security token is opened. The handle is opened with TOKEN_QUERY.
+static OwnedHandle openSecurityTokenForQuery() {
+ HANDLE token = nullptr;
+ // It is unclear to me whether OpenAsSelf matters for winpty, or what the
+ // most appropriate value is.
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY,
+ /*OpenAsSelf=*/FALSE, &token)) {
+ if (GetLastError() != ERROR_NO_TOKEN) {
+ throwWindowsError(L"OpenThreadToken failed");
+ }
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
+ throwWindowsError(L"OpenProcessToken failed");
+ }
+ }
+ ASSERT(token != nullptr &&
+ "OpenThreadToken/OpenProcessToken token is NULL");
+ return OwnedHandle(token);
+}
+
+// Returns the TokenOwner of the thread's effective security token.
+Sid getOwnerSid() {
+ struct Impl : Sid::Impl {
+ std::unique_ptr<char[]> buffer;
+ };
+
+ OwnedHandle token = openSecurityTokenForQuery();
+ DWORD actual = 0;
+ BOOL success;
+ success = GetTokenInformation(token.get(), TokenOwner,
+ nullptr, 0, &actual);
+ if (success) {
+ throwWinptyException(L"getOwnerSid: GetTokenInformation: "
+ L"expected ERROR_INSUFFICIENT_BUFFER");
+ } else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ throwWindowsError(L"getOwnerSid: GetTokenInformation: "
+ L"expected ERROR_INSUFFICIENT_BUFFER");
+ }
+ std::unique_ptr<Impl> impl(new Impl);
+ impl->buffer = std::unique_ptr<char[]>(new char[actual]);
+ success = GetTokenInformation(token.get(), TokenOwner,
+ impl->buffer.get(), actual, &actual);
+ if (!success) {
+ throwWindowsError(L"getOwnerSid: GetTokenInformation");
+ }
+ TOKEN_OWNER tmp;
+ ASSERT(actual >= sizeof(tmp));
+ std::copy(
+ impl->buffer.get(),
+ impl->buffer.get() + sizeof(tmp),
+ reinterpret_cast<char*>(&tmp));
+ return Sid(tmp.Owner, std::move(impl));
+}
+
+Sid wellKnownSid(
+ const wchar_t *debuggingName,
+ SID_IDENTIFIER_AUTHORITY authority,
+ BYTE authorityCount,
+ DWORD subAuthority0/*=0*/,
+ DWORD subAuthority1/*=0*/) {
+ PSID psid = nullptr;
+ if (!AllocateAndInitializeSid(&authority, authorityCount,
+ subAuthority0,
+ subAuthority1,
+ 0, 0, 0, 0, 0, 0,
+ &psid)) {
+ const auto err = GetLastError();
+ const auto msg =
+ std::wstring(L"wellKnownSid: error getting ") +
+ debuggingName + L" SID";
+ throwWindowsError(msg.c_str(), err);
+ }
+ return allocatedSid(psid);
+}
+
+Sid builtinAdminsSid() {
+ // S-1-5-32-544
+ SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY };
+ return wellKnownSid(L"BUILTIN\\Administrators group",
+ authority, 2,
+ SECURITY_BUILTIN_DOMAIN_RID, // 32
+ DOMAIN_ALIAS_RID_ADMINS); // 544
+}
+
+Sid localSystemSid() {
+ // S-1-5-18
+ SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY };
+ return wellKnownSid(L"LocalSystem account",
+ authority, 1,
+ SECURITY_LOCAL_SYSTEM_RID); // 18
+}
+
+Sid everyoneSid() {
+ // S-1-1-0
+ SID_IDENTIFIER_AUTHORITY authority = { SECURITY_WORLD_SID_AUTHORITY };
+ return wellKnownSid(L"Everyone account",
+ authority, 1,
+ SECURITY_WORLD_RID); // 0
+}
+
+static SecurityDescriptor finishSecurityDescriptor(
+ size_t daclEntryCount,
+ EXPLICIT_ACCESSW *daclEntries,
+ Acl &outAcl) {
+ {
+ PACL aclRaw = nullptr;
+ DWORD aclError =
+ SetEntriesInAclW(daclEntryCount,
+ daclEntries,
+ nullptr, &aclRaw);
+ if (aclError != ERROR_SUCCESS) {
+ WStringBuilder sb(64);
+ sb << L"finishSecurityDescriptor: "
+ << L"SetEntriesInAcl failed: " << aclError;
+ throwWinptyException(sb.c_str());
+ }
+ outAcl = localItem<AclTag>(aclRaw);
+ }
+
+ const PSECURITY_DESCRIPTOR sdRaw =
+ reinterpret_cast<PSECURITY_DESCRIPTOR>(
+ LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));
+ if (sdRaw == nullptr) {
+ throwWinptyException(L"finishSecurityDescriptor: LocalAlloc failed");
+ }
+ SecurityDescriptor sd = localItem<SecurityDescriptorTag>(sdRaw);
+ if (!InitializeSecurityDescriptor(sdRaw, SECURITY_DESCRIPTOR_REVISION)) {
+ throwWindowsError(
+ L"finishSecurityDescriptor: InitializeSecurityDescriptor");
+ }
+ if (!SetSecurityDescriptorDacl(sdRaw, TRUE, outAcl.get(), FALSE)) {
+ throwWindowsError(
+ L"finishSecurityDescriptor: SetSecurityDescriptorDacl");
+ }
+
+ return std::move(sd);
+}
+
+// Create a security descriptor that grants full control to the local system
+// account, built-in administrators, and the owner.
+SecurityDescriptor
+createPipeSecurityDescriptorOwnerFullControl() {
+
+ struct Impl : SecurityDescriptor::Impl {
+ Sid localSystem;
+ Sid builtinAdmins;
+ Sid owner;
+ std::array<EXPLICIT_ACCESSW, 3> daclEntries = {};
+ Acl dacl;
+ SecurityDescriptor value;
+ };
+
+ std::unique_ptr<Impl> impl(new Impl);
+ impl->localSystem = localSystemSid();
+ impl->builtinAdmins = builtinAdminsSid();
+ impl->owner = getOwnerSid();
+
+ for (auto &ea : impl->daclEntries) {
+ ea.grfAccessPermissions = GENERIC_ALL;
+ ea.grfAccessMode = SET_ACCESS;
+ ea.grfInheritance = NO_INHERITANCE;
+ ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ }
+ impl->daclEntries[0].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->localSystem.get());
+ impl->daclEntries[1].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->builtinAdmins.get());
+ impl->daclEntries[2].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->owner.get());
+
+ impl->value = finishSecurityDescriptor(
+ impl->daclEntries.size(),
+ impl->daclEntries.data(),
+ impl->dacl);
+
+ const auto retValue = impl->value.get();
+ return SecurityDescriptor(retValue, std::move(impl));
+}
+
+SecurityDescriptor
+createPipeSecurityDescriptorOwnerFullControlEveryoneWrite() {
+
+ struct Impl : SecurityDescriptor::Impl {
+ Sid localSystem;
+ Sid builtinAdmins;
+ Sid owner;
+ Sid everyone;
+ std::array<EXPLICIT_ACCESSW, 4> daclEntries = {};
+ Acl dacl;
+ SecurityDescriptor value;
+ };
+
+ std::unique_ptr<Impl> impl(new Impl);
+ impl->localSystem = localSystemSid();
+ impl->builtinAdmins = builtinAdminsSid();
+ impl->owner = getOwnerSid();
+ impl->everyone = everyoneSid();
+
+ for (auto &ea : impl->daclEntries) {
+ ea.grfAccessPermissions = GENERIC_ALL;
+ ea.grfAccessMode = SET_ACCESS;
+ ea.grfInheritance = NO_INHERITANCE;
+ ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ }
+ impl->daclEntries[0].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->localSystem.get());
+ impl->daclEntries[1].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->builtinAdmins.get());
+ impl->daclEntries[2].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->owner.get());
+ impl->daclEntries[3].Trustee.ptstrName =
+ reinterpret_cast<LPWSTR>(impl->everyone.get());
+ // Avoid using FILE_GENERIC_WRITE because it includes FILE_APPEND_DATA,
+ // which is equal to FILE_CREATE_PIPE_INSTANCE. Instead, include all the
+ // flags that comprise FILE_GENERIC_WRITE, except for the one.
+ impl->daclEntries[3].grfAccessPermissions =
+ FILE_GENERIC_READ |
+ FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA | FILE_WRITE_EA |
+ STANDARD_RIGHTS_WRITE | SYNCHRONIZE;
+
+ impl->value = finishSecurityDescriptor(
+ impl->daclEntries.size(),
+ impl->daclEntries.data(),
+ impl->dacl);
+
+ const auto retValue = impl->value.get();
+ return SecurityDescriptor(retValue, std::move(impl));
+}
+
+SecurityDescriptor getObjectSecurityDescriptor(HANDLE handle) {
+ PACL dacl = nullptr;
+ PSECURITY_DESCRIPTOR sd = nullptr;
+ const DWORD errCode = GetSecurityInfo(handle, SE_KERNEL_OBJECT,
+ OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION,
+ nullptr, nullptr, &dacl, nullptr, &sd);
+ if (errCode != ERROR_SUCCESS) {
+ throwWindowsError(L"GetSecurityInfo failed");
+ }
+ return localItem<SecurityDescriptorTag>(sd);
+}
+
+// The (SID/SD)<->string conversion APIs are useful for testing/debugging, so
+// create convenient accessor functions for them. They're too slow for
+// ordinary use. The APIs exist in XP and up, but the MinGW headers only
+// declare the SID<->string APIs, not the SD APIs. MinGW also gets the
+// prototype wrong for ConvertStringSidToSidW (LPWSTR instead of LPCWSTR) and
+// requires WINVER to be defined. MSVC and MinGW-w64 get everything right, but
+// for consistency, use LoadLibrary/GetProcAddress for all four APIs.
+
+typedef BOOL WINAPI ConvertStringSidToSidW_t(
+ LPCWSTR StringSid,
+ PSID *Sid);
+
+typedef BOOL WINAPI ConvertSidToStringSidW_t(
+ PSID Sid,
+ LPWSTR *StringSid);
+
+typedef BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW_t(
+ LPCWSTR StringSecurityDescriptor,
+ DWORD StringSDRevision,
+ PSECURITY_DESCRIPTOR *SecurityDescriptor,
+ PULONG SecurityDescriptorSize);
+
+typedef BOOL WINAPI ConvertSecurityDescriptorToStringSecurityDescriptorW_t(
+ PSECURITY_DESCRIPTOR SecurityDescriptor,
+ DWORD RequestedStringSDRevision,
+ SECURITY_INFORMATION SecurityInformation,
+ LPWSTR *StringSecurityDescriptor,
+ PULONG StringSecurityDescriptorLen);
+
+#define GET_MODULE_PROC(mod, funcName) \
+ const auto p##funcName = \
+ reinterpret_cast<funcName##_t*>( \
+ mod.proc(#funcName)); \
+ if (p##funcName == nullptr) { \
+ throwWinptyException( \
+ L"" L ## #funcName L" API is missing from ADVAPI32.DLL"); \
+ }
+
+const DWORD kSDDL_REVISION_1 = 1;
+
+std::wstring sidToString(PSID sid) {
+ OsModule advapi32(L"advapi32.dll");
+ GET_MODULE_PROC(advapi32, ConvertSidToStringSidW);
+ wchar_t *sidString = NULL;
+ BOOL success = pConvertSidToStringSidW(sid, &sidString);
+ if (!success) {
+ throwWindowsError(L"ConvertSidToStringSidW failed");
+ }
+ PointerLocal freer(sidString);
+ return std::wstring(sidString);
+}
+
+Sid stringToSid(const std::wstring &str) {
+ // Cast the string from const wchar_t* to LPWSTR because the function is
+ // incorrectly prototyped in the MinGW sddl.h header. The API does not
+ // modify the string -- it is correctly prototyped as taking LPCWSTR in
+ // MinGW-w64, MSVC, and MSDN.
+ OsModule advapi32(L"advapi32.dll");
+ GET_MODULE_PROC(advapi32, ConvertStringSidToSidW);
+ PSID psid = nullptr;
+ BOOL success = pConvertStringSidToSidW(const_cast<LPWSTR>(str.c_str()),
+ &psid);
+ if (!success) {
+ const auto err = GetLastError();
+ throwWindowsError(
+ (std::wstring(L"ConvertStringSidToSidW failed on \"") +
+ str + L'"').c_str(),
+ err);
+ }
+ return localItem<SidTag>(psid);
+}
+
+SecurityDescriptor stringToSd(const std::wstring &str) {
+ OsModule advapi32(L"advapi32.dll");
+ GET_MODULE_PROC(advapi32, ConvertStringSecurityDescriptorToSecurityDescriptorW);
+ PSECURITY_DESCRIPTOR desc = nullptr;
+ if (!pConvertStringSecurityDescriptorToSecurityDescriptorW(
+ str.c_str(), kSDDL_REVISION_1, &desc, nullptr)) {
+ const auto err = GetLastError();
+ throwWindowsError(
+ (std::wstring(L"ConvertStringSecurityDescriptorToSecurityDescriptorW failed on \"") +
+ str + L'"').c_str(),
+ err);
+ }
+ return localItem<SecurityDescriptorTag>(desc);
+}
+
+std::wstring sdToString(PSECURITY_DESCRIPTOR sd) {
+ OsModule advapi32(L"advapi32.dll");
+ GET_MODULE_PROC(advapi32, ConvertSecurityDescriptorToStringSecurityDescriptorW);
+ wchar_t *sdString = nullptr;
+ if (!pConvertSecurityDescriptorToStringSecurityDescriptorW(
+ sd,
+ kSDDL_REVISION_1,
+ OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION,
+ &sdString,
+ nullptr)) {
+ throwWindowsError(
+ L"ConvertSecurityDescriptorToStringSecurityDescriptor failed");
+ }
+ PointerLocal freer(sdString);
+ return std::wstring(sdString);
+}
+
+// Vista added a useful flag to CreateNamedPipe, PIPE_REJECT_REMOTE_CLIENTS,
+// that rejects remote connections. Return this flag on Vista, or return 0
+// otherwise.
+DWORD rejectRemoteClientsPipeFlag() {
+ if (isAtLeastWindowsVista()) {
+ // MinGW lacks this flag; MinGW-w64 has it.
+ const DWORD kPIPE_REJECT_REMOTE_CLIENTS = 8;
+ return kPIPE_REJECT_REMOTE_CLIENTS;
+ } else {
+ trace("Omitting PIPE_REJECT_REMOTE_CLIENTS on pre-Vista OS");
+ return 0;
+ }
+}
+
+typedef BOOL WINAPI GetNamedPipeClientProcessId_t(
+ HANDLE Pipe,
+ PULONG ClientProcessId);
+
+std::tuple<GetNamedPipeClientProcessId_Result, DWORD, DWORD>
+getNamedPipeClientProcessId(HANDLE serverPipe) {
+ OsModule kernel32(L"kernel32.dll");
+ const auto pGetNamedPipeClientProcessId =
+ reinterpret_cast<GetNamedPipeClientProcessId_t*>(
+ kernel32.proc("GetNamedPipeClientProcessId"));
+ if (pGetNamedPipeClientProcessId == nullptr) {
+ return std::make_tuple(
+ GetNamedPipeClientProcessId_Result::UnsupportedOs, 0, 0);
+ }
+ ULONG pid = 0;
+ if (!pGetNamedPipeClientProcessId(serverPipe, &pid)) {
+ return std::make_tuple(
+ GetNamedPipeClientProcessId_Result::Failure, 0, GetLastError());
+ }
+ return std::make_tuple(
+ GetNamedPipeClientProcessId_Result::Success,
+ static_cast<DWORD>(pid),
+ 0);
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.h b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.h
new file mode 100644
index 0000000000..5f9d53aff6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WindowsSecurity.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_WINDOWS_SECURITY_H
+#define WINPTY_WINDOWS_SECURITY_H
+
+#include <windows.h>
+#include <aclapi.h>
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+
+// PSID and PSECURITY_DESCRIPTOR are both pointers to void, but we want
+// Sid and SecurityDescriptor to be different types.
+struct SidTag { typedef PSID type; };
+struct AclTag { typedef PACL type; };
+struct SecurityDescriptorTag { typedef PSECURITY_DESCRIPTOR type; };
+
+template <typename T>
+class SecurityItem {
+public:
+ struct Impl {
+ virtual ~Impl() {}
+ };
+
+private:
+ typedef typename T::type P;
+ P m_v;
+ std::unique_ptr<Impl> m_pimpl;
+
+public:
+ P get() const { return m_v; }
+ operator bool() const { return m_v != nullptr; }
+
+ SecurityItem() : m_v(nullptr) {}
+ SecurityItem(P v, std::unique_ptr<Impl> &&pimpl) :
+ m_v(v), m_pimpl(std::move(pimpl)) {}
+ SecurityItem(SecurityItem &&other) :
+ m_v(other.m_v), m_pimpl(std::move(other.m_pimpl)) {
+ other.m_v = nullptr;
+ }
+ SecurityItem &operator=(SecurityItem &&other) {
+ m_v = other.m_v;
+ other.m_v = nullptr;
+ m_pimpl = std::move(other.m_pimpl);
+ return *this;
+ }
+};
+
+typedef SecurityItem<SidTag> Sid;
+typedef SecurityItem<AclTag> Acl;
+typedef SecurityItem<SecurityDescriptorTag> SecurityDescriptor;
+
+Sid getOwnerSid();
+Sid wellKnownSid(
+ const wchar_t *debuggingName,
+ SID_IDENTIFIER_AUTHORITY authority,
+ BYTE authorityCount,
+ DWORD subAuthority0=0,
+ DWORD subAuthority1=0);
+Sid builtinAdminsSid();
+Sid localSystemSid();
+Sid everyoneSid();
+
+SecurityDescriptor createPipeSecurityDescriptorOwnerFullControl();
+SecurityDescriptor createPipeSecurityDescriptorOwnerFullControlEveryoneWrite();
+SecurityDescriptor getObjectSecurityDescriptor(HANDLE handle);
+
+std::wstring sidToString(PSID sid);
+Sid stringToSid(const std::wstring &str);
+SecurityDescriptor stringToSd(const std::wstring &str);
+std::wstring sdToString(PSECURITY_DESCRIPTOR sd);
+
+DWORD rejectRemoteClientsPipeFlag();
+
+enum class GetNamedPipeClientProcessId_Result {
+ Success,
+ Failure,
+ UnsupportedOs,
+};
+
+std::tuple<GetNamedPipeClientProcessId_Result, DWORD, DWORD>
+getNamedPipeClientProcessId(HANDLE serverPipe);
+
+#endif // WINPTY_WINDOWS_SECURITY_H
diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsVersion.cc b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.cc
new file mode 100644
index 0000000000..d89b00d838
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.cc
@@ -0,0 +1,252 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WindowsVersion.h"
+
+#include <windows.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <tuple>
+
+#include "DebugClient.h"
+#include "OsModule.h"
+#include "StringBuilder.h"
+#include "StringUtil.h"
+#include "WinptyAssert.h"
+#include "WinptyException.h"
+
+namespace {
+
+typedef std::tuple<DWORD, DWORD> Version;
+
+// This function can only return a version up to 6.2 unless the executable is
+// manifested for a newer version of Windows. See the MSDN documentation for
+// GetVersionEx.
+OSVERSIONINFOEX getWindowsVersionInfo() {
+ // Allow use of deprecated functions (i.e. GetVersionEx). We need to use
+ // GetVersionEx for the old MinGW toolchain and with MSVC when it targets XP.
+ // Having two code paths makes code harder to test, and it's not obvious how
+ // to detect the presence of a new enough SDK. (Including ntverp.h and
+ // examining VER_PRODUCTBUILD apparently works, but even then, MinGW-w64 and
+ // MSVC seem to use different version numbers.)
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4996)
+#endif
+ OSVERSIONINFOEX info = {};
+ info.dwOSVersionInfoSize = sizeof(info);
+ const auto success = GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&info));
+ ASSERT(success && "GetVersionEx failed");
+ return info;
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+}
+
+Version getWindowsVersion() {
+ const auto info = getWindowsVersionInfo();
+ return Version(info.dwMajorVersion, info.dwMinorVersion);
+}
+
+struct ModuleNotFound : WinptyException {
+ virtual const wchar_t *what() const WINPTY_NOEXCEPT override {
+ return L"ModuleNotFound";
+ }
+};
+
+// Throws WinptyException on error.
+std::wstring getSystemDirectory() {
+ wchar_t systemDirectory[MAX_PATH];
+ const UINT size = GetSystemDirectoryW(systemDirectory, MAX_PATH);
+ if (size == 0) {
+ throwWindowsError(L"GetSystemDirectory failed");
+ } else if (size >= MAX_PATH) {
+ throwWinptyException(
+ L"GetSystemDirectory: path is longer than MAX_PATH");
+ }
+ return systemDirectory;
+}
+
+#define GET_VERSION_DLL_API(name) \
+ const auto p ## name = \
+ reinterpret_cast<decltype(name)*>( \
+ versionDll.proc(#name)); \
+ if (p ## name == nullptr) { \
+ throwWinptyException(L ## #name L" is missing"); \
+ }
+
+// Throws WinptyException on error.
+VS_FIXEDFILEINFO getFixedFileInfo(const std::wstring &path) {
+ // version.dll is not a conventional KnownDll, so if we link to it, there's
+ // a danger of accidentally loading a malicious DLL. In a more typical
+ // application, perhaps we'd guard against this security issue by
+ // controlling which directories this code runs in (e.g. *not* the
+ // "Downloads" directory), but that's harder for the winpty library.
+ OsModule versionDll(
+ (getSystemDirectory() + L"\\version.dll").c_str(),
+ OsModule::LoadErrorBehavior::Throw);
+ GET_VERSION_DLL_API(GetFileVersionInfoSizeW);
+ GET_VERSION_DLL_API(GetFileVersionInfoW);
+ GET_VERSION_DLL_API(VerQueryValueW);
+ DWORD size = pGetFileVersionInfoSizeW(path.c_str(), nullptr);
+ if (!size) {
+ // I see ERROR_FILE_NOT_FOUND on Win7 and
+ // ERROR_RESOURCE_DATA_NOT_FOUND on WinXP.
+ if (GetLastError() == ERROR_FILE_NOT_FOUND ||
+ GetLastError() == ERROR_RESOURCE_DATA_NOT_FOUND) {
+ throw ModuleNotFound();
+ } else {
+ throwWindowsError(
+ (L"GetFileVersionInfoSizeW failed on " + path).c_str());
+ }
+ }
+ std::unique_ptr<char[]> versionBuffer(new char[size]);
+ if (!pGetFileVersionInfoW(path.c_str(), 0, size, versionBuffer.get())) {
+ throwWindowsError((L"GetFileVersionInfoW failed on " + path).c_str());
+ }
+ VS_FIXEDFILEINFO *versionInfo = nullptr;
+ UINT versionInfoSize = 0;
+ if (!pVerQueryValueW(
+ versionBuffer.get(), L"\\",
+ reinterpret_cast<void**>(&versionInfo), &versionInfoSize) ||
+ versionInfo == nullptr ||
+ versionInfoSize != sizeof(VS_FIXEDFILEINFO) ||
+ versionInfo->dwSignature != 0xFEEF04BD) {
+ throwWinptyException((L"VerQueryValueW failed on " + path).c_str());
+ }
+ return *versionInfo;
+}
+
+uint64_t productVersionFromInfo(const VS_FIXEDFILEINFO &info) {
+ return (static_cast<uint64_t>(info.dwProductVersionMS) << 32) |
+ (static_cast<uint64_t>(info.dwProductVersionLS));
+}
+
+uint64_t fileVersionFromInfo(const VS_FIXEDFILEINFO &info) {
+ return (static_cast<uint64_t>(info.dwFileVersionMS) << 32) |
+ (static_cast<uint64_t>(info.dwFileVersionLS));
+}
+
+std::string versionToString(uint64_t version) {
+ StringBuilder b(32);
+ b << ((uint16_t)(version >> 48));
+ b << '.';
+ b << ((uint16_t)(version >> 32));
+ b << '.';
+ b << ((uint16_t)(version >> 16));
+ b << '.';
+ b << ((uint16_t)(version >> 0));
+ return b.str_moved();
+}
+
+} // anonymous namespace
+
+// Returns true for Windows Vista (or Windows Server 2008) or newer.
+bool isAtLeastWindowsVista() {
+ return getWindowsVersion() >= Version(6, 0);
+}
+
+// Returns true for Windows 7 (or Windows Server 2008 R2) or newer.
+bool isAtLeastWindows7() {
+ return getWindowsVersion() >= Version(6, 1);
+}
+
+// Returns true for Windows 8 (or Windows Server 2012) or newer.
+bool isAtLeastWindows8() {
+ return getWindowsVersion() >= Version(6, 2);
+}
+
+#define WINPTY_IA32 1
+#define WINPTY_X64 2
+
+#if defined(_M_IX86) || defined(__i386__)
+#define WINPTY_ARCH WINPTY_IA32
+#elif defined(_M_X64) || defined(__x86_64__)
+#define WINPTY_ARCH WINPTY_X64
+#endif
+
+typedef BOOL WINAPI IsWow64Process_t(HANDLE hProcess, PBOOL Wow64Process);
+
+void dumpWindowsVersion() {
+ if (!isTracingEnabled()) {
+ return;
+ }
+ const auto info = getWindowsVersionInfo();
+ StringBuilder b;
+ b << info.dwMajorVersion << '.' << info.dwMinorVersion
+ << '.' << info.dwBuildNumber << ' '
+ << "SP" << info.wServicePackMajor << '.' << info.wServicePackMinor
+ << ' ';
+ switch (info.wProductType) {
+ case VER_NT_WORKSTATION: b << "Client"; break;
+ case VER_NT_DOMAIN_CONTROLLER: b << "DomainController"; break;
+ case VER_NT_SERVER: b << "Server"; break;
+ default:
+ b << "product=" << info.wProductType; break;
+ }
+ b << ' ';
+#if WINPTY_ARCH == WINPTY_IA32
+ b << "IA32";
+ OsModule kernel32(L"kernel32.dll");
+ IsWow64Process_t *pIsWow64Process =
+ reinterpret_cast<IsWow64Process_t*>(
+ kernel32.proc("IsWow64Process"));
+ if (pIsWow64Process != nullptr) {
+ BOOL result = false;
+ const BOOL success = pIsWow64Process(GetCurrentProcess(), &result);
+ if (!success) {
+ b << " WOW64:error";
+ } else if (success && result) {
+ b << " WOW64";
+ }
+ } else {
+ b << " WOW64:missingapi";
+ }
+#elif WINPTY_ARCH == WINPTY_X64
+ b << "X64";
+#endif
+ const auto dllVersion = [](const wchar_t *dllPath) -> std::string {
+ try {
+ const auto info = getFixedFileInfo(dllPath);
+ StringBuilder fb(64);
+ fb << utf8FromWide(dllPath) << ':';
+ fb << "F:" << versionToString(fileVersionFromInfo(info)) << '/'
+ << "P:" << versionToString(productVersionFromInfo(info));
+ return fb.str_moved();
+ } catch (const ModuleNotFound&) {
+ return utf8FromWide(dllPath) + ":none";
+ } catch (const WinptyException &e) {
+ trace("Error getting %s version: %s",
+ utf8FromWide(dllPath).c_str(), utf8FromWide(e.what()).c_str());
+ return utf8FromWide(dllPath) + ":error";
+ }
+ };
+ b << ' ' << dllVersion(L"kernel32.dll");
+ // ConEmu provides a DLL that hooks many Windows APIs, especially console
+ // APIs. Its existence and version number could be useful in debugging.
+#if WINPTY_ARCH == WINPTY_IA32
+ b << ' ' << dllVersion(L"ConEmuHk.dll");
+#elif WINPTY_ARCH == WINPTY_X64
+ b << ' ' << dllVersion(L"ConEmuHk64.dll");
+#endif
+ trace("Windows version: %s", b.c_str());
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/WindowsVersion.h b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.h
new file mode 100644
index 0000000000..a80798417e
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WindowsVersion.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SHARED_WINDOWS_VERSION_H
+#define WINPTY_SHARED_WINDOWS_VERSION_H
+
+bool isAtLeastWindowsVista();
+bool isAtLeastWindows7();
+bool isAtLeastWindows8();
+void dumpWindowsVersion();
+
+#endif // WINPTY_SHARED_WINDOWS_VERSION_H
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyAssert.cc b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.cc
new file mode 100644
index 0000000000..1ff0de475a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WinptyAssert.h"
+
+#include <windows.h>
+#include <stdlib.h>
+
+#include "DebugClient.h"
+
+void assertTrace(const char *file, int line, const char *cond) {
+ trace("Assertion failed: %s, file %s, line %d",
+ cond, file, line);
+}
+
+#ifdef WINPTY_AGENT_ASSERT
+
+void agentShutdown() {
+ HWND hwnd = GetConsoleWindow();
+ if (hwnd != NULL) {
+ PostMessage(hwnd, WM_CLOSE, 0, 0);
+ Sleep(30000);
+ trace("Agent shutdown: WM_CLOSE did not end agent process");
+ } else {
+ trace("Agent shutdown: GetConsoleWindow() is NULL");
+ }
+ // abort() prints a message to the console, and if it is frozen, then the
+ // process would hang, so instead use exit(). (We shouldn't ever get here,
+ // though, because the WM_CLOSE message should have ended this process.)
+ exit(1);
+}
+
+void agentAssertFail(const char *file, int line, const char *cond) {
+ assertTrace(file, line, cond);
+ agentShutdown();
+}
+
+#endif
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyAssert.h b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.h
new file mode 100644
index 0000000000..b2b8b5e64c
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyAssert.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2011-2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_ASSERT_H
+#define WINPTY_ASSERT_H
+
+#ifdef WINPTY_AGENT_ASSERT
+
+void agentShutdown();
+void agentAssertFail(const char *file, int line, const char *cond);
+
+// Calling the standard assert() function does not work in the agent because
+// the error message would be printed to the console, and the only way the
+// user can see the console is via a working agent! Moreover, the console may
+// be frozen, so attempting to write to it would block forever. This custom
+// assert function instead sends the message to the DebugServer, then attempts
+// to close the console, then quietly exits.
+#define ASSERT(cond) \
+ do { \
+ if (!(cond)) { \
+ agentAssertFail(__FILE__, __LINE__, #cond); \
+ } \
+ } while(0)
+
+#else
+
+void assertTrace(const char *file, int line, const char *cond);
+
+// In the other targets, log the assert failure to the debugserver, then fail
+// using the ordinary assert mechanism. In case assert is compiled out, fail
+// using abort. The amount of code inlined is unfortunate, but asserts aren't
+// used much outside the agent.
+#include <assert.h>
+#include <stdlib.h>
+#define ASSERT_CONDITION(cond) (false && (cond))
+#define ASSERT(cond) \
+ do { \
+ if (!(cond)) { \
+ assertTrace(__FILE__, __LINE__, #cond); \
+ assert(ASSERT_CONDITION(#cond)); \
+ abort(); \
+ } \
+ } while(0)
+
+#endif
+
+#endif // WINPTY_ASSERT_H
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyException.cc b/src/libs/3rdparty/winpty/src/shared/WinptyException.cc
new file mode 100644
index 0000000000..d0d48823d2
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyException.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WinptyException.h"
+
+#include <memory>
+#include <string>
+
+#include "StringBuilder.h"
+
+namespace {
+
+class ExceptionImpl : public WinptyException {
+public:
+ ExceptionImpl(const wchar_t *what) :
+ m_what(std::make_shared<std::wstring>(what)) {}
+ virtual const wchar_t *what() const WINPTY_NOEXCEPT override {
+ return m_what->c_str();
+ }
+private:
+ // Using a shared_ptr ensures that copying the object raises no exception.
+ std::shared_ptr<std::wstring> m_what;
+};
+
+} // anonymous namespace
+
+void throwWinptyException(const wchar_t *what) {
+ throw ExceptionImpl(what);
+}
+
+void throwWindowsError(const wchar_t *prefix, DWORD errorCode) {
+ WStringBuilder sb(64);
+ if (prefix != nullptr) {
+ sb << prefix << L": ";
+ }
+ // It might make sense to use FormatMessage here, but IIRC, its API is hard
+ // to figure out.
+ sb << L"Windows error " << errorCode;
+ throwWinptyException(sb.c_str());
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyException.h b/src/libs/3rdparty/winpty/src/shared/WinptyException.h
new file mode 100644
index 0000000000..ec353369e5
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyException.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_EXCEPTION_H
+#define WINPTY_EXCEPTION_H
+
+#include <windows.h>
+
+#if defined(__GNUC__)
+#define WINPTY_NOEXCEPT noexcept
+#elif defined(_MSC_VER) && _MSC_VER >= 1900
+#define WINPTY_NOEXCEPT noexcept
+#else
+#define WINPTY_NOEXCEPT
+#endif
+
+class WinptyException {
+public:
+ virtual const wchar_t *what() const WINPTY_NOEXCEPT = 0;
+ virtual ~WinptyException() {}
+};
+
+void throwWinptyException(const wchar_t *what);
+void throwWindowsError(const wchar_t *prefix, DWORD error=GetLastError());
+
+#endif // WINPTY_EXCEPTION_H
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyVersion.cc b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.cc
new file mode 100644
index 0000000000..76bb8a584d
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WinptyVersion.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "DebugClient.h"
+
+// This header is auto-generated by either the Makefile (Unix) or
+// UpdateGenVersion.bat (gyp). It is placed in a 'gen' directory, which is
+// added to the search path.
+#include "GenVersion.h"
+
+void dumpVersionToStdout() {
+ printf("winpty version %s\n", GenVersion_Version);
+ printf("commit %s\n", GenVersion_Commit);
+}
+
+void dumpVersionToTrace() {
+ trace("winpty version %s (commit %s)",
+ GenVersion_Version,
+ GenVersion_Commit);
+}
diff --git a/src/libs/3rdparty/winpty/src/shared/WinptyVersion.h b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.h
new file mode 100644
index 0000000000..e6224d7b84
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/WinptyVersion.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_VERSION_H
+#define WINPTY_VERSION_H
+
+void dumpVersionToStdout();
+void dumpVersionToTrace();
+
+#endif // WINPTY_VERSION_H
diff --git a/src/libs/3rdparty/winpty/src/shared/winpty_snprintf.h b/src/libs/3rdparty/winpty/src/shared/winpty_snprintf.h
new file mode 100644
index 0000000000..e716f245e8
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/shared/winpty_snprintf.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2016 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef WINPTY_SNPRINTF_H
+#define WINPTY_SNPRINTF_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "WinptyAssert.h"
+
+#if defined(__CYGWIN__) || defined(__MSYS__)
+#define WINPTY_SNPRINTF_FORMAT(fmtarg, vararg) \
+ __attribute__((format(printf, (fmtarg), ((vararg)))))
+#elif defined(__GNUC__)
+#define WINPTY_SNPRINTF_FORMAT(fmtarg, vararg) \
+ __attribute__((format(ms_printf, (fmtarg), ((vararg)))))
+#else
+#define WINPTY_SNPRINTF_FORMAT(fmtarg, vararg)
+#endif
+
+// Returns a value between 0 and size - 1 (inclusive) on success. Returns -1
+// on failure (including truncation). The output buffer is always
+// NUL-terminated.
+inline int
+winpty_vsnprintf(char *out, size_t size, const char *fmt, va_list ap) {
+ ASSERT(size > 0);
+ out[0] = '\0';
+#if defined(_MSC_VER) && _MSC_VER < 1900
+ // MSVC 2015 added a C99-conforming vsnprintf.
+ int count = _vsnprintf_s(out, size, _TRUNCATE, fmt, ap);
+#else
+ // MinGW configurations frequently provide a vsnprintf function that simply
+ // calls one of the MS _vsnprintf* functions, which are not C99 conformant.
+ int count = vsnprintf(out, size, fmt, ap);
+#endif
+ if (count < 0 || static_cast<size_t>(count) >= size) {
+ // On truncation, some *printf* implementations return the
+ // non-truncated size, but other implementations returns -1. Return
+ // -1 for consistency.
+ count = -1;
+ // Guarantee NUL termination.
+ out[size - 1] = '\0';
+ } else {
+ // Guarantee NUL termination.
+ out[count] = '\0';
+ }
+ return count;
+}
+
+// Wraps winpty_vsnprintf.
+inline int winpty_snprintf(char *out, size_t size, const char *fmt, ...)
+ WINPTY_SNPRINTF_FORMAT(3, 4);
+inline int winpty_snprintf(char *out, size_t size, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ const int count = winpty_vsnprintf(out, size, fmt, ap);
+ va_end(ap);
+ return count;
+}
+
+// Wraps winpty_vsnprintf with automatic size determination.
+template <size_t size>
+int winpty_vsnprintf(char (&out)[size], const char *fmt, va_list ap) {
+ return winpty_vsnprintf(out, size, fmt, ap);
+}
+
+// Wraps winpty_vsnprintf with automatic size determination.
+template <size_t size>
+int winpty_snprintf(char (&out)[size], const char *fmt, ...)
+ WINPTY_SNPRINTF_FORMAT(2, 3);
+template <size_t size>
+int winpty_snprintf(char (&out)[size], const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ const int count = winpty_vsnprintf(out, size, fmt, ap);
+ va_end(ap);
+ return count;
+}
+
+#endif // WINPTY_SNPRINTF_H
diff --git a/src/libs/3rdparty/winpty/src/subdir.mk b/src/libs/3rdparty/winpty/src/subdir.mk
new file mode 100644
index 0000000000..9ae8031b08
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/subdir.mk
@@ -0,0 +1,5 @@
+include src/agent/subdir.mk
+include src/debugserver/subdir.mk
+include src/libwinpty/subdir.mk
+include src/tests/subdir.mk
+include src/unix-adapter/subdir.mk
diff --git a/src/libs/3rdparty/winpty/src/tests/subdir.mk b/src/libs/3rdparty/winpty/src/tests/subdir.mk
new file mode 100644
index 0000000000..18799c4a5a
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/tests/subdir.mk
@@ -0,0 +1,28 @@
+# Copyright (c) 2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+build/%.exe : src/tests/%.cc build/winpty.dll
+ $(info Building $@)
+ @$(MINGW_CXX) $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS) -o $@ $^
+
+TEST_PROGRAMS = \
+ build/trivial_test.exe
+
+-include $(TEST_PROGRAMS:.exe=.d)
diff --git a/src/libs/3rdparty/winpty/src/tests/trivial_test.cc b/src/libs/3rdparty/winpty/src/tests/trivial_test.cc
new file mode 100644
index 0000000000..2188a4befb
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/tests/trivial_test.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include <windows.h>
+
+#include <cassert>
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
+#include <cwchar>
+#include <vector>
+
+#include "../include/winpty.h"
+#include "../shared/DebugClient.h"
+
+static std::vector<unsigned char> filterContent(
+ const std::vector<unsigned char> &content) {
+ std::vector<unsigned char> result;
+ auto it = content.begin();
+ const auto itEnd = content.end();
+ while (it < itEnd) {
+ if (*it == '\r') {
+ // Filter out carriage returns. Sometimes the output starts with
+ // a single CR; other times, it has multiple CRs.
+ it++;
+ } else if (*it == '\x1b' && (it + 1) < itEnd && *(it + 1) == '[') {
+ // Filter out escape sequences. They have no interior letters and
+ // end with a single letter.
+ it += 2;
+ while (it < itEnd && !isalpha(*it)) {
+ it++;
+ }
+ it++;
+ } else {
+ // Let everything else through.
+ result.push_back(*it);
+ it++;
+ }
+ }
+ return result;
+}
+
+// Read bytes from the non-overlapped file handle until the file is closed or
+// until an I/O error occurs.
+static std::vector<unsigned char> readAll(HANDLE handle) {
+ unsigned char buf[1024];
+ std::vector<unsigned char> result;
+ while (true) {
+ DWORD amount = 0;
+ BOOL ret = ReadFile(handle, buf, sizeof(buf), &amount, nullptr);
+ if (!ret || amount == 0) {
+ break;
+ }
+ result.insert(result.end(), buf, buf + amount);
+ }
+ return result;
+}
+
+static void parentTest() {
+ wchar_t program[1024];
+ wchar_t cmdline[1024];
+ GetModuleFileNameW(nullptr, program, 1024);
+
+ {
+ // XXX: We'd like to use swprintf, which is part of C99 and takes a
+ // size_t maxlen argument. MinGW-w64 has this function, as does MSVC.
+ // The old MinGW doesn't, though -- instead, it apparently provides an
+ // swprintf taking no maxlen argument. This *might* be a regression?
+ // (There is also no swnprintf, but that function is obsolescent with a
+ // correct swprintf, and it isn't in POSIX or ISO C.)
+ //
+ // Visual C++ 6 also provided this non-conformant swprintf, and I'm
+ // guessing MSVCRT.DLL does too. (My impression is that the old MinGW
+ // prefers to rely on MSVCRT.DLL for convenience?)
+ //
+ // I could compile differently for old MinGW, but what if it fixes its
+ // function later? Instead, use a workaround. It's starting to make
+ // sense to drop MinGW support in favor of MinGW-w64. This is too
+ // annoying.
+ //
+ // grepbait: OLD-MINGW / WINPTY_TARGET_MSYS1
+ cmdline[0] = L'\0';
+ wcscat(cmdline, L"\"");
+ wcscat(cmdline, program);
+ wcscat(cmdline, L"\" CHILD");
+ }
+ // swnprintf(cmdline, sizeof(cmdline) / sizeof(cmdline[0]),
+ // L"\"%ls\" CHILD", program);
+
+ auto agentCfg = winpty_config_new(0, nullptr);
+ assert(agentCfg != nullptr);
+ auto pty = winpty_open(agentCfg, nullptr);
+ assert(pty != nullptr);
+ winpty_config_free(agentCfg);
+
+ HANDLE conin = CreateFileW(
+ winpty_conin_name(pty),
+ GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
+ HANDLE conout = CreateFileW(
+ winpty_conout_name(pty),
+ GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);
+ assert(conin != INVALID_HANDLE_VALUE);
+ assert(conout != INVALID_HANDLE_VALUE);
+
+ auto spawnCfg = winpty_spawn_config_new(
+ WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, program, cmdline,
+ nullptr, nullptr, nullptr);
+ assert(spawnCfg != nullptr);
+ HANDLE process = nullptr;
+ BOOL spawnSuccess = winpty_spawn(
+ pty, spawnCfg, &process, nullptr, nullptr, nullptr);
+ assert(spawnSuccess && process != nullptr);
+
+ auto content = readAll(conout);
+ content = filterContent(content);
+
+ std::vector<unsigned char> expectedContent = {
+ 'H', 'I', '\n', 'X', 'Y', '\n'
+ };
+ DWORD exitCode = 0;
+ assert(GetExitCodeProcess(process, &exitCode) && exitCode == 42);
+ CloseHandle(process);
+ CloseHandle(conin);
+ CloseHandle(conout);
+ assert(content == expectedContent);
+ winpty_free(pty);
+}
+
+static void childTest() {
+ printf("HI\nXY\n");
+ exit(42);
+}
+
+int main(int argc, char *argv[]) {
+ if (argc == 1) {
+ parentTest();
+ } else {
+ childTest();
+ }
+ return 0;
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.cc b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.cc
new file mode 100644
index 0000000000..39f1e09685
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "InputHandler.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "../shared/DebugClient.h"
+#include "Util.h"
+#include "WakeupFd.h"
+
+InputHandler::InputHandler(
+ HANDLE conin, int inputfd, WakeupFd &completionWakeup) :
+ m_conin(conin),
+ m_inputfd(inputfd),
+ m_completionWakeup(completionWakeup),
+ m_threadHasBeenJoined(false),
+ m_shouldShutdown(0),
+ m_threadCompleted(0)
+{
+ pthread_create(&m_thread, NULL, InputHandler::threadProcS, this);
+}
+
+void InputHandler::shutdown() {
+ startShutdown();
+ if (!m_threadHasBeenJoined) {
+ int ret = pthread_join(m_thread, NULL);
+ assert(ret == 0 && "pthread_join failed");
+ m_threadHasBeenJoined = true;
+ }
+}
+
+void InputHandler::threadProc() {
+ std::vector<char> buffer(4096);
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ while (true) {
+ // Handle shutdown.
+ m_wakeup.reset();
+ if (m_shouldShutdown) {
+ trace("InputHandler: shutting down");
+ break;
+ }
+
+ // Block until data arrives.
+ {
+ const int max_fd = std::max(m_inputfd, m_wakeup.fd());
+ FD_SET(m_inputfd, &readfds);
+ FD_SET(m_wakeup.fd(), &readfds);
+ selectWrapper("InputHandler", max_fd + 1, &readfds);
+ if (!FD_ISSET(m_inputfd, &readfds)) {
+ continue;
+ }
+ }
+
+ const int numRead = read(m_inputfd, &buffer[0], buffer.size());
+ if (numRead == -1 && errno == EINTR) {
+ // Apparently, this read is interrupted on Cygwin 1.7 by a SIGWINCH
+ // signal even though I set the SA_RESTART flag on the handler.
+ continue;
+ }
+
+ // tty is closed, or the read failed for some unexpected reason.
+ if (numRead <= 0) {
+ trace("InputHandler: tty read failed: numRead=%d", numRead);
+ break;
+ }
+
+ DWORD written = 0;
+ BOOL ret = WriteFile(m_conin,
+ &buffer[0], numRead,
+ &written, NULL);
+ if (!ret || written != static_cast<DWORD>(numRead)) {
+ if (!ret && GetLastError() == ERROR_BROKEN_PIPE) {
+ trace("InputHandler: pipe closed: written=%u",
+ static_cast<unsigned int>(written));
+ } else {
+ trace("InputHandler: write failed: "
+ "ret=%d lastError=0x%x numRead=%d written=%u",
+ ret,
+ static_cast<unsigned int>(GetLastError()),
+ numRead,
+ static_cast<unsigned int>(written));
+ }
+ break;
+ }
+ }
+ m_threadCompleted = 1;
+ m_completionWakeup.set();
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.h b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.h
new file mode 100644
index 0000000000..9c3f540d63
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/InputHandler.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNIX_ADAPTER_INPUT_HANDLER_H
+#define UNIX_ADAPTER_INPUT_HANDLER_H
+
+#include <windows.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include "WakeupFd.h"
+
+// Connect a Cygwin blocking fd to winpty CONIN.
+class InputHandler {
+public:
+ InputHandler(HANDLE conin, int inputfd, WakeupFd &completionWakeup);
+ ~InputHandler() { shutdown(); }
+ bool isComplete() { return m_threadCompleted; }
+ void startShutdown() { m_shouldShutdown = 1; m_wakeup.set(); }
+ void shutdown();
+
+private:
+ static void *threadProcS(void *pvthis) {
+ reinterpret_cast<InputHandler*>(pvthis)->threadProc();
+ return NULL;
+ }
+ void threadProc();
+
+ HANDLE m_conin;
+ int m_inputfd;
+ pthread_t m_thread;
+ WakeupFd &m_completionWakeup;
+ WakeupFd m_wakeup;
+ bool m_threadHasBeenJoined;
+ volatile sig_atomic_t m_shouldShutdown;
+ volatile sig_atomic_t m_threadCompleted;
+};
+
+#endif // UNIX_ADAPTER_INPUT_HANDLER_H
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.cc b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.cc
new file mode 100644
index 0000000000..573b8adced
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "OutputHandler.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "../shared/DebugClient.h"
+#include "Util.h"
+#include "WakeupFd.h"
+
+OutputHandler::OutputHandler(
+ HANDLE conout, int outputfd, WakeupFd &completionWakeup) :
+ m_conout(conout),
+ m_outputfd(outputfd),
+ m_completionWakeup(completionWakeup),
+ m_threadHasBeenJoined(false),
+ m_threadCompleted(0)
+{
+ pthread_create(&m_thread, NULL, OutputHandler::threadProcS, this);
+}
+
+void OutputHandler::shutdown() {
+ if (!m_threadHasBeenJoined) {
+ int ret = pthread_join(m_thread, NULL);
+ assert(ret == 0 && "pthread_join failed");
+ m_threadHasBeenJoined = true;
+ }
+}
+
+void OutputHandler::threadProc() {
+ std::vector<char> buffer(4096);
+ while (true) {
+ DWORD numRead = 0;
+ BOOL ret = ReadFile(m_conout,
+ &buffer[0], buffer.size(),
+ &numRead, NULL);
+ if (!ret || numRead == 0) {
+ if (!ret && GetLastError() == ERROR_BROKEN_PIPE) {
+ trace("OutputHandler: pipe closed: numRead=%u",
+ static_cast<unsigned int>(numRead));
+ } else {
+ trace("OutputHandler: read failed: "
+ "ret=%d lastError=0x%x numRead=%u",
+ ret,
+ static_cast<unsigned int>(GetLastError()),
+ static_cast<unsigned int>(numRead));
+ }
+ break;
+ }
+ if (!writeAll(m_outputfd, &buffer[0], numRead)) {
+ break;
+ }
+ }
+ m_threadCompleted = 1;
+ m_completionWakeup.set();
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.h b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.h
new file mode 100644
index 0000000000..48241c5538
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/OutputHandler.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNIX_ADAPTER_OUTPUT_HANDLER_H
+#define UNIX_ADAPTER_OUTPUT_HANDLER_H
+
+#include <windows.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include "WakeupFd.h"
+
+// Connect winpty CONOUT/CONERR to a Cygwin blocking fd.
+class OutputHandler {
+public:
+ OutputHandler(HANDLE conout, int outputfd, WakeupFd &completionWakeup);
+ ~OutputHandler() { shutdown(); }
+ bool isComplete() { return m_threadCompleted; }
+ void shutdown();
+
+private:
+ static void *threadProcS(void *pvthis) {
+ reinterpret_cast<OutputHandler*>(pvthis)->threadProc();
+ return NULL;
+ }
+ void threadProc();
+
+ HANDLE m_conout;
+ int m_outputfd;
+ pthread_t m_thread;
+ WakeupFd &m_completionWakeup;
+ bool m_threadHasBeenJoined;
+ volatile sig_atomic_t m_threadCompleted;
+};
+
+#endif // UNIX_ADAPTER_OUTPUT_HANDLER_H
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/Util.cc b/src/libs/3rdparty/winpty/src/unix-adapter/Util.cc
new file mode 100644
index 0000000000..e13f84a529
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/Util.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "Util.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../shared/DebugClient.h"
+
+// Write the entire buffer, restarting it as necessary.
+bool writeAll(int fd, const void *buffer, size_t size) {
+ size_t written = 0;
+ while (written < size) {
+ int ret = write(fd,
+ reinterpret_cast<const char*>(buffer) + written,
+ size - written);
+ if (ret == -1 && errno == EINTR) {
+ continue;
+ }
+ if (ret <= 0) {
+ trace("write failed: "
+ "fd=%d errno=%d size=%u written=%d ret=%d",
+ fd,
+ errno,
+ static_cast<unsigned int>(size),
+ static_cast<unsigned int>(written),
+ ret);
+ return false;
+ }
+ assert(static_cast<size_t>(ret) <= size - written);
+ written += ret;
+ }
+ assert(written == size);
+ return true;
+}
+
+bool writeStr(int fd, const char *str) {
+ return writeAll(fd, str, strlen(str));
+}
+
+void selectWrapper(const char *diagName, int nfds, fd_set *readfds) {
+ int ret = select(nfds, readfds, NULL, NULL, NULL);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ FD_ZERO(readfds);
+ return;
+ }
+#ifdef WINPTY_TARGET_MSYS1
+ // The select system call sometimes fails with EAGAIN instead of EINTR.
+ // This apparantly only happens with the old Cygwin fork "MSYS" used in
+ // the mingw.org project. select is not supposed to fail with EAGAIN,
+ // and EAGAIN does not make much sense as an error code. (The whole
+ // point of select is to block.)
+ if (errno == EAGAIN) {
+ trace("%s select returned EAGAIN: interpreting like EINTR",
+ diagName);
+ FD_ZERO(readfds);
+ return;
+ }
+#endif
+ fprintf(stderr, "Internal error: %s select failed: "
+ "error %d", diagName, errno);
+ abort();
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/Util.h b/src/libs/3rdparty/winpty/src/unix-adapter/Util.h
new file mode 100644
index 0000000000..cadb4c82a9
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/Util.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNIX_ADAPTER_UTIL_H
+#define UNIX_ADAPTER_UTIL_H
+
+#include <stdlib.h>
+#include <sys/select.h>
+
+bool writeAll(int fd, const void *buffer, size_t size);
+bool writeStr(int fd, const char *str);
+void selectWrapper(const char *diagName, int nfds, fd_set *readfds);
+
+#endif // UNIX_ADAPTER_UTIL_H
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.cc b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.cc
new file mode 100644
index 0000000000..6b47379015
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#include "WakeupFd.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void setFdNonBlock(int fd) {
+ int status = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, status | O_NONBLOCK);
+}
+
+WakeupFd::WakeupFd() {
+ int pipeFd[2];
+ if (pipe(pipeFd) != 0) {
+ perror("Could not create internal wakeup pipe");
+ abort();
+ }
+ m_pipeReadFd = pipeFd[0];
+ m_pipeWriteFd = pipeFd[1];
+ setFdNonBlock(m_pipeReadFd);
+ setFdNonBlock(m_pipeWriteFd);
+}
+
+WakeupFd::~WakeupFd() {
+ close(m_pipeReadFd);
+ close(m_pipeWriteFd);
+}
+
+void WakeupFd::set() {
+ char dummy = 0;
+ int ret;
+ do {
+ ret = write(m_pipeWriteFd, &dummy, 1);
+ } while (ret < 0 && errno == EINTR);
+}
+
+void WakeupFd::reset() {
+ char tmpBuf[256];
+ while (true) {
+ int amount = read(m_pipeReadFd, tmpBuf, sizeof(tmpBuf));
+ if (amount < 0 && errno == EAGAIN) {
+ break;
+ } else if (amount <= 0) {
+ perror("error reading from internal wakeup pipe");
+ abort();
+ }
+ }
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.h b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.h
new file mode 100644
index 0000000000..dd8d362aa1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/WakeupFd.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+#ifndef UNIX_ADAPTER_WAKEUP_FD_H
+#define UNIX_ADAPTER_WAKEUP_FD_H
+
+class WakeupFd {
+public:
+ WakeupFd();
+ ~WakeupFd();
+ int fd() { return m_pipeReadFd; }
+ void set();
+ void reset();
+
+private:
+ // Do not allow copying the WakeupFd object.
+ WakeupFd(const WakeupFd &other);
+ WakeupFd &operator=(const WakeupFd &other);
+
+private:
+ int m_pipeReadFd;
+ int m_pipeWriteFd;
+};
+
+#endif // UNIX_ADAPTER_WAKEUP_FD_H
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/main.cc b/src/libs/3rdparty/winpty/src/unix-adapter/main.cc
new file mode 100644
index 0000000000..992cb70e44
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/main.cc
@@ -0,0 +1,729 @@
+// Copyright (c) 2011-2015 Ryan Prichard
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+
+// MSYS's sys/cygwin.h header only declares cygwin_internal if WINVER is
+// defined, which is defined in windows.h. Therefore, include windows.h early.
+#include <windows.h>
+
+#include <assert.h>
+#include <cygwin/version.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/cygwin.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <winpty.h>
+#include "../shared/DebugClient.h"
+#include "../shared/UnixCtrlChars.h"
+#include "../shared/WinptyVersion.h"
+#include "InputHandler.h"
+#include "OutputHandler.h"
+#include "Util.h"
+#include "WakeupFd.h"
+
+#define CSI "\x1b["
+
+static WakeupFd *g_mainWakeup = NULL;
+
+static WakeupFd &mainWakeup()
+{
+ if (g_mainWakeup == NULL) {
+ static const char msg[] = "Internal error: g_mainWakeup is NULL\r\n";
+ write(STDERR_FILENO, msg, sizeof(msg) - 1);
+ abort();
+ }
+ return *g_mainWakeup;
+}
+
+struct SavedTermiosMode {
+ int count;
+ bool valid[3];
+ termios mode[3];
+};
+
+// Put the input terminal into non-canonical mode.
+static SavedTermiosMode setRawTerminalMode(
+ bool allowNonTtys, bool setStdout, bool setStderr)
+{
+ SavedTermiosMode ret;
+ const char *const kNames[3] = { "stdin", "stdout", "stderr" };
+
+ ret.valid[0] = true;
+ ret.valid[1] = setStdout;
+ ret.valid[2] = setStderr;
+
+ for (int i = 0; i < 3; ++i) {
+ if (!ret.valid[i]) {
+ continue;
+ }
+ if (!isatty(i)) {
+ ret.valid[i] = false;
+ if (!allowNonTtys) {
+ fprintf(stderr, "%s is not a tty\n", kNames[i]);
+ exit(1);
+ }
+ } else {
+ ret.valid[i] = true;
+ if (tcgetattr(i, &ret.mode[i]) < 0) {
+ perror("tcgetattr failed");
+ exit(1);
+ }
+ }
+ }
+
+ if (ret.valid[STDIN_FILENO]) {
+ termios buf;
+ if (tcgetattr(STDIN_FILENO, &buf) < 0) {
+ perror("tcgetattr failed");
+ exit(1);
+ }
+ buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ buf.c_cflag &= ~(CSIZE | PARENB);
+ buf.c_cflag |= CS8;
+ buf.c_cc[VMIN] = 1; // blocking read
+ buf.c_cc[VTIME] = 0;
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &buf) < 0) {
+ fprintf(stderr, "tcsetattr failed\n");
+ exit(1);
+ }
+ }
+
+ for (int i = STDOUT_FILENO; i <= STDERR_FILENO; ++i) {
+ if (!ret.valid[i]) {
+ continue;
+ }
+ termios buf;
+ if (tcgetattr(i, &buf) < 0) {
+ perror("tcgetattr failed");
+ exit(1);
+ }
+ buf.c_cflag &= ~(CSIZE | PARENB);
+ buf.c_cflag |= CS8;
+ buf.c_oflag &= ~OPOST;
+ if (tcsetattr(i, TCSAFLUSH, &buf) < 0) {
+ fprintf(stderr, "tcsetattr failed\n");
+ exit(1);
+ }
+ }
+
+ return ret;
+}
+
+static void restoreTerminalMode(const SavedTermiosMode &original)
+{
+ for (int i = 0; i < 3; ++i) {
+ if (!original.valid[i]) {
+ continue;
+ }
+ if (tcsetattr(i, TCSAFLUSH, &original.mode[i]) < 0) {
+ perror("error restoring terminal mode");
+ exit(1);
+ }
+ }
+}
+
+static void debugShowKey(bool allowNonTtys)
+{
+ printf("\nPress any keys -- Ctrl-D exits\n\n");
+ const SavedTermiosMode saved =
+ setRawTerminalMode(allowNonTtys, false, false);
+ char buf[128];
+ while (true) {
+ const ssize_t len = read(STDIN_FILENO, buf, sizeof(buf));
+ if (len <= 0) {
+ break;
+ }
+ for (int i = 0; i < len; ++i) {
+ char ctrl = decodeUnixCtrlChar(buf[i]);
+ if (ctrl == '\0') {
+ putchar(buf[i]);
+ } else {
+ putchar('^');
+ putchar(ctrl);
+ }
+ }
+ for (int i = 0; i < len; ++i) {
+ unsigned char uch = buf[i];
+ printf("\t%3d %04o 0x%02x\n", uch, uch, uch);
+ fflush(stdout);
+ }
+ if (buf[0] == 4) {
+ // Ctrl-D
+ break;
+ }
+ }
+ restoreTerminalMode(saved);
+}
+
+static void terminalResized(int signo)
+{
+ mainWakeup().set();
+}
+
+static void registerResizeSignalHandler()
+{
+ struct sigaction resizeSigAct;
+ memset(&resizeSigAct, 0, sizeof(resizeSigAct));
+ resizeSigAct.sa_handler = terminalResized;
+ resizeSigAct.sa_flags = SA_RESTART;
+ sigaction(SIGWINCH, &resizeSigAct, NULL);
+}
+
+// Convert the path to a Win32 path if it is a POSIX path, and convert slashes
+// to backslashes.
+static std::string convertPosixPathToWin(const std::string &path)
+{
+ char *tmp;
+#if defined(CYGWIN_VERSION_CYGWIN_CONV) && \
+ CYGWIN_VERSION_API_MINOR >= CYGWIN_VERSION_CYGWIN_CONV
+ // MSYS2 and versions of Cygwin released after 2009 or so use this API.
+ // The original MSYS still lacks this API.
+ ssize_t newSize = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE,
+ path.c_str(), NULL, 0);
+ assert(newSize >= 0);
+ tmp = new char[newSize + 1];
+ ssize_t success = cygwin_conv_path(CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE,
+ path.c_str(), tmp, newSize + 1);
+ assert(success == 0);
+#else
+ // In the current Cygwin header file, this API is documented as deprecated
+ // because it's restricted to paths of MAX_PATH length. In the CVS version
+ // of MSYS, the newer API doesn't exist, and this older API is implemented
+ // using msys_p2w, which seems like it would handle paths larger than
+ // MAX_PATH, but there's no way to query how large the new path is.
+ // Hopefully, this is large enough.
+ tmp = new char[MAX_PATH + path.size()];
+ cygwin_conv_to_win32_path(path.c_str(), tmp);
+#endif
+ for (int i = 0; tmp[i] != '\0'; ++i) {
+ if (tmp[i] == '/')
+ tmp[i] = '\\';
+ }
+ std::string ret(tmp);
+ delete [] tmp;
+ return ret;
+}
+
+static std::string resolvePath(const std::string &path)
+{
+ char ret[PATH_MAX];
+ ret[0] = '\0';
+ if (realpath(path.c_str(), ret) != ret) {
+ return std::string();
+ }
+ return ret;
+}
+
+template <size_t N>
+static bool endsWith(const std::string &path, const char (&suf)[N])
+{
+ const size_t suffixLen = N - 1;
+ char actualSuf[N];
+ if (path.size() < suffixLen) {
+ return false;
+ }
+ strcpy(actualSuf, &path.c_str()[path.size() - suffixLen]);
+ for (size_t i = 0; i < suffixLen; ++i) {
+ actualSuf[i] = tolower(actualSuf[i]);
+ }
+ return !strcmp(actualSuf, suf);
+}
+
+static std::string findProgram(
+ const char *winptyProgName,
+ const std::string &prog)
+{
+ std::string candidate;
+ if (prog.find('/') == std::string::npos &&
+ prog.find('\\') == std::string::npos) {
+ // XXX: It would be nice to use a lambda here (once/if old MSYS support
+ // is dropped).
+ // Search the PATH.
+ const char *const pathVar = getenv("PATH");
+ const std::string pathList(pathVar ? pathVar : "");
+ size_t elpos = 0;
+ while (true) {
+ const size_t elend = pathList.find(':', elpos);
+ candidate = pathList.substr(elpos, elend - elpos);
+ if (!candidate.empty() && *(candidate.end() - 1) != '/') {
+ candidate += '/';
+ }
+ candidate += prog;
+ candidate = resolvePath(candidate);
+ if (!candidate.empty()) {
+ int perm = X_OK;
+ if (endsWith(candidate, ".bat") || endsWith(candidate, ".cmd")) {
+#ifdef __MSYS__
+ // In MSYS/MSYS2, batch files don't have the execute bit
+ // set, so just check that they're readable.
+ perm = R_OK;
+#endif
+ } else if (endsWith(candidate, ".com") || endsWith(candidate, ".exe")) {
+ // Do nothing.
+ } else {
+ // Make the exe extension explicit so that we don't try to
+ // run shell scripts with CreateProcess/winpty_spawn.
+ candidate += ".exe";
+ }
+ if (!access(candidate.c_str(), perm)) {
+ break;
+ }
+ }
+ if (elend == std::string::npos) {
+ fprintf(stderr, "%s: error: cannot start '%s': Not found in PATH\n",
+ winptyProgName, prog.c_str());
+ exit(1);
+ } else {
+ elpos = elend + 1;
+ }
+ }
+ } else {
+ candidate = resolvePath(prog);
+ if (candidate.empty()) {
+ std::string errstr(strerror(errno));
+ fprintf(stderr, "%s: error: cannot start '%s': %s\n",
+ winptyProgName, prog.c_str(), errstr.c_str());
+ exit(1);
+ }
+ }
+ return convertPosixPathToWin(candidate);
+}
+
+// Convert argc/argv into a Win32 command-line following the escaping convention
+// documented on MSDN. (e.g. see CommandLineToArgvW documentation)
+static std::string argvToCommandLine(const std::vector<std::string> &argv)
+{
+ std::string result;
+ for (size_t argIndex = 0; argIndex < argv.size(); ++argIndex) {
+ if (argIndex > 0)
+ result.push_back(' ');
+ const char *arg = argv[argIndex].c_str();
+ const bool quote =
+ strchr(arg, ' ') != NULL ||
+ strchr(arg, '\t') != NULL ||
+ *arg == '\0';
+ if (quote)
+ result.push_back('\"');
+ int bsCount = 0;
+ for (const char *p = arg; *p != '\0'; ++p) {
+ if (*p == '\\') {
+ bsCount++;
+ } else if (*p == '\"') {
+ result.append(bsCount * 2 + 1, '\\');
+ result.push_back('\"');
+ bsCount = 0;
+ } else {
+ result.append(bsCount, '\\');
+ bsCount = 0;
+ result.push_back(*p);
+ }
+ }
+ if (quote) {
+ result.append(bsCount * 2, '\\');
+ result.push_back('\"');
+ } else {
+ result.append(bsCount, '\\');
+ }
+ }
+ return result;
+}
+
+static wchar_t *heapMbsToWcs(const char *text)
+{
+ // Calling mbstowcs with a NULL first argument seems to be broken on MSYS.
+ // Instead of returning the size of the converted string, it returns 0.
+ // Using strlen(text) * 2 is probably big enough.
+ size_t maxLen = strlen(text) * 2 + 1;
+ wchar_t *ret = new wchar_t[maxLen];
+ size_t len = mbstowcs(ret, text, maxLen);
+ assert(len != (size_t)-1 && len < maxLen);
+ return ret;
+}
+
+static char *heapWcsToMbs(const wchar_t *text)
+{
+ // Calling wcstombs with a NULL first argument seems to be broken on MSYS.
+ // Instead of returning the size of the converted string, it returns 0.
+ // Using wcslen(text) * 3 is big enough for UTF-8 and probably other
+ // encodings. For UTF-8, codepoints that fit in a single wchar
+ // (U+0000 to U+FFFF) are encoded using 1-3 bytes. The remaining code
+ // points needs two wchar's and are encoded using 4 bytes.
+ size_t maxLen = wcslen(text) * 3 + 1;
+ char *ret = new char[maxLen];
+ size_t len = wcstombs(ret, text, maxLen);
+ if (len == (size_t)-1 || len >= maxLen) {
+ delete [] ret;
+ return NULL;
+ } else {
+ return ret;
+ }
+}
+
+static std::string wcsToMbs(const wchar_t *text)
+{
+ std::string ret;
+ const char *ptr = heapWcsToMbs(text);
+ if (ptr != NULL) {
+ ret = ptr;
+ delete [] ptr;
+ }
+ return ret;
+}
+
+void setupWin32Environment()
+{
+ std::map<std::string, std::string> varsToCopy;
+ const char *vars[] = {
+ "WINPTY_DEBUG",
+ "WINPTY_SHOW_CONSOLE",
+ NULL
+ };
+ for (int i = 0; vars[i] != NULL; ++i) {
+ const char *cstr = getenv(vars[i]);
+ if (cstr != NULL && cstr[0] != '\0') {
+ varsToCopy[vars[i]] = cstr;
+ }
+ }
+
+#if defined(__MSYS__) && CYGWIN_VERSION_API_MINOR >= 48 || \
+ !defined(__MSYS__) && CYGWIN_VERSION_API_MINOR >= 153
+ // Use CW_SYNC_WINENV to copy the Unix environment to the Win32
+ // environment. The command performs special translation on some variables
+ // (such as PATH and TMP). It also copies the debugging environment
+ // variables.
+ //
+ // Note that the API minor versions have diverged in Cygwin and MSYS.
+ // CW_SYNC_WINENV was added to Cygwin in version 153. (Cygwin's
+ // include/cygwin/version.h says that CW_SETUP_WINENV was added in 153.
+ // The flag was renamed 8 days after it was added, but the API docs weren't
+ // updated.) The flag was added to MSYS in version 48.
+ //
+ // Also, in my limited testing, this call seems to be necessary with Cygwin
+ // but unnecessary with MSYS. Perhaps MSYS is automatically syncing the
+ // Unix environment with the Win32 environment before starting console.exe?
+ // It shouldn't hurt to call it for MSYS.
+ cygwin_internal(CW_SYNC_WINENV);
+#endif
+
+ // Copy debugging environment variables from the Cygwin environment
+ // to the Win32 environment so the agent will inherit it.
+ for (std::map<std::string, std::string>::iterator it = varsToCopy.begin();
+ it != varsToCopy.end();
+ ++it) {
+ wchar_t *nameW = heapMbsToWcs(it->first.c_str());
+ wchar_t *valueW = heapMbsToWcs(it->second.c_str());
+ SetEnvironmentVariableW(nameW, valueW);
+ delete [] nameW;
+ delete [] valueW;
+ }
+
+ // Clear the TERM variable. The child process's immediate console/terminal
+ // environment is a Windows console, not the terminal that winpty is
+ // communicating with. Leaving the TERM variable set can break programs in
+ // various ways. (e.g. arrows keys broken in Cygwin less, IronPython's
+ // help(...) function doesn't start, misc programs decide they should
+ // output color escape codes on pre-Win10). See
+ // https://github.com/rprichard/winpty/issues/43.
+ SetEnvironmentVariableW(L"TERM", NULL);
+}
+
+static void usage(const char *program, int exitCode)
+{
+ printf("Usage: %s [options] [--] program [args]\n", program);
+ printf("\n");
+ printf("Options:\n");
+ printf(" -h, --help Show this help message\n");
+ printf(" --mouse Enable terminal mouse input\n");
+ printf(" --showkey Dump STDIN escape sequences\n");
+ printf(" --version Show the winpty version number\n");
+ exit(exitCode);
+}
+
+struct Arguments {
+ std::vector<std::string> childArgv;
+ bool mouseInput;
+ bool testAllowNonTtys;
+ bool testConerr;
+ bool testPlainOutput;
+ bool testColorEscapes;
+};
+
+static void parseArguments(int argc, char *argv[], Arguments &out)
+{
+ out.mouseInput = false;
+ out.testAllowNonTtys = false;
+ out.testConerr = false;
+ out.testPlainOutput = false;
+ out.testColorEscapes = false;
+ bool doShowKeys = false;
+ const char *const program = argc >= 1 ? argv[0] : "<program>";
+ int argi = 1;
+ while (argi < argc) {
+ std::string arg(argv[argi++]);
+ if (arg.size() >= 1 && arg[0] == '-') {
+ if (arg == "-h" || arg == "--help") {
+ usage(program, 0);
+ } else if (arg == "--mouse") {
+ out.mouseInput = true;
+ } else if (arg == "--showkey") {
+ doShowKeys = true;
+ } else if (arg == "--version") {
+ dumpVersionToStdout();
+ exit(0);
+ } else if (arg == "-Xallow-non-tty") {
+ out.testAllowNonTtys = true;
+ } else if (arg == "-Xconerr") {
+ out.testConerr = true;
+ } else if (arg == "-Xplain") {
+ out.testPlainOutput = true;
+ } else if (arg == "-Xcolor") {
+ out.testColorEscapes = true;
+ } else if (arg == "--") {
+ break;
+ } else {
+ fprintf(stderr, "Error: unrecognized option: '%s'\n",
+ arg.c_str());
+ exit(1);
+ }
+ } else {
+ out.childArgv.push_back(arg);
+ break;
+ }
+ }
+ for (; argi < argc; ++argi) {
+ out.childArgv.push_back(argv[argi]);
+ }
+ if (doShowKeys) {
+ debugShowKey(out.testAllowNonTtys);
+ exit(0);
+ }
+ if (out.childArgv.size() == 0) {
+ usage(program, 1);
+ }
+}
+
+static std::string errorMessageToString(DWORD err)
+{
+ // Use FormatMessageW rather than FormatMessageA, because we want to use
+ // wcstombs to convert to the Cygwin locale, which might not match the
+ // codepage FormatMessageA would use. We need to convert using wcstombs,
+ // rather than print using %ls, because %ls doesn't work in the original
+ // MSYS.
+ wchar_t *wideMsgPtr = NULL;
+ const DWORD formatRet = FormatMessageW(
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ reinterpret_cast<wchar_t*>(&wideMsgPtr),
+ 0,
+ NULL);
+ if (formatRet == 0 || wideMsgPtr == NULL) {
+ return std::string();
+ }
+ std::string msg = wcsToMbs(wideMsgPtr);
+ LocalFree(wideMsgPtr);
+ const size_t pos = msg.find_last_not_of(" \r\n\t");
+ if (pos == std::string::npos) {
+ msg.clear();
+ } else {
+ msg.erase(pos + 1);
+ }
+ return msg;
+}
+
+static std::string formatErrorMessage(DWORD err)
+{
+ char buf[64];
+ sprintf(buf, "error %#x", static_cast<unsigned int>(err));
+ std::string ret = errorMessageToString(err);
+ if (ret.empty()) {
+ ret += buf;
+ } else {
+ ret += " (";
+ ret += buf;
+ ret += ")";
+ }
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ setlocale(LC_ALL, "");
+
+ g_mainWakeup = new WakeupFd();
+
+ Arguments args;
+ parseArguments(argc, argv, args);
+
+ setupWin32Environment();
+
+ winsize sz = { 0 };
+ sz.ws_col = 80;
+ sz.ws_row = 25;
+ ioctl(STDIN_FILENO, TIOCGWINSZ, &sz);
+
+ DWORD agentFlags = WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION;
+ if (args.testConerr) { agentFlags |= WINPTY_FLAG_CONERR; }
+ if (args.testPlainOutput) { agentFlags |= WINPTY_FLAG_PLAIN_OUTPUT; }
+ if (args.testColorEscapes) { agentFlags |= WINPTY_FLAG_COLOR_ESCAPES; }
+ winpty_config_t *agentCfg = winpty_config_new(agentFlags, NULL);
+ assert(agentCfg != NULL);
+ winpty_config_set_initial_size(agentCfg, sz.ws_col, sz.ws_row);
+ if (args.mouseInput) {
+ winpty_config_set_mouse_mode(agentCfg, WINPTY_MOUSE_MODE_FORCE);
+ }
+
+ winpty_error_ptr_t openErr = NULL;
+ winpty_t *wp = winpty_open(agentCfg, &openErr);
+ if (wp == NULL) {
+ fprintf(stderr, "Error creating winpty: %s\n",
+ wcsToMbs(winpty_error_msg(openErr)).c_str());
+ exit(1);
+ }
+ winpty_config_free(agentCfg);
+ winpty_error_free(openErr);
+
+ HANDLE conin = CreateFileW(winpty_conin_name(wp), GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, 0, NULL);
+ HANDLE conout = CreateFileW(winpty_conout_name(wp), GENERIC_READ, 0, NULL,
+ OPEN_EXISTING, 0, NULL);
+ assert(conin != INVALID_HANDLE_VALUE);
+ assert(conout != INVALID_HANDLE_VALUE);
+ HANDLE conerr = NULL;
+ if (args.testConerr) {
+ conerr = CreateFileW(winpty_conerr_name(wp), GENERIC_READ, 0, NULL,
+ OPEN_EXISTING, 0, NULL);
+ assert(conerr != INVALID_HANDLE_VALUE);
+ }
+
+ HANDLE childHandle = NULL;
+
+ {
+ // Start the child process under the console.
+ args.childArgv[0] = findProgram(argv[0], args.childArgv[0]);
+ std::string cmdLine = argvToCommandLine(args.childArgv);
+ wchar_t *cmdLineW = heapMbsToWcs(cmdLine.c_str());
+
+ winpty_spawn_config_t *spawnCfg = winpty_spawn_config_new(
+ WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN,
+ NULL, cmdLineW, NULL, NULL, NULL);
+ assert(spawnCfg != NULL);
+
+ winpty_error_ptr_t spawnErr = NULL;
+ DWORD lastError = 0;
+ BOOL spawnRet = winpty_spawn(wp, spawnCfg, &childHandle, NULL,
+ &lastError, &spawnErr);
+ winpty_spawn_config_free(spawnCfg);
+
+ if (!spawnRet) {
+ winpty_result_t spawnCode = winpty_error_code(spawnErr);
+ if (spawnCode == WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED) {
+ fprintf(stderr, "%s: error: cannot start '%s': %s\n",
+ argv[0],
+ cmdLine.c_str(),
+ formatErrorMessage(lastError).c_str());
+ } else {
+ fprintf(stderr, "%s: error: cannot start '%s': internal error: %s\n",
+ argv[0],
+ cmdLine.c_str(),
+ wcsToMbs(winpty_error_msg(spawnErr)).c_str());
+ }
+ exit(1);
+ }
+ winpty_error_free(spawnErr);
+ delete [] cmdLineW;
+ }
+
+ registerResizeSignalHandler();
+ SavedTermiosMode mode =
+ setRawTerminalMode(args.testAllowNonTtys, true, args.testConerr);
+
+ InputHandler inputHandler(conin, STDIN_FILENO, mainWakeup());
+ OutputHandler outputHandler(conout, STDOUT_FILENO, mainWakeup());
+ OutputHandler *errorHandler = NULL;
+ if (args.testConerr) {
+ errorHandler = new OutputHandler(conerr, STDERR_FILENO, mainWakeup());
+ }
+
+ while (true) {
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(mainWakeup().fd(), &readfds);
+ selectWrapper("main thread", mainWakeup().fd() + 1, &readfds);
+ mainWakeup().reset();
+
+ // Check for terminal resize.
+ {
+ winsize sz2;
+ ioctl(STDIN_FILENO, TIOCGWINSZ, &sz2);
+ if (memcmp(&sz, &sz2, sizeof(sz)) != 0) {
+ sz = sz2;
+ winpty_set_size(wp, sz.ws_col, sz.ws_row, NULL);
+ }
+ }
+
+ // Check for an I/O handler shutting down (possibly indicating that the
+ // child process has exited).
+ if (inputHandler.isComplete() || outputHandler.isComplete() ||
+ (errorHandler != NULL && errorHandler->isComplete())) {
+ break;
+ }
+ }
+
+ // Kill the agent connection. This will kill the agent, closing the CONIN
+ // and CONOUT pipes on the agent pipe, prompting our I/O handler to shut
+ // down.
+ winpty_free(wp);
+
+ inputHandler.shutdown();
+ outputHandler.shutdown();
+ CloseHandle(conin);
+ CloseHandle(conout);
+
+ if (errorHandler != NULL) {
+ errorHandler->shutdown();
+ delete errorHandler;
+ CloseHandle(conerr);
+ }
+
+ restoreTerminalMode(mode);
+
+ DWORD exitCode = 0;
+ if (!GetExitCodeProcess(childHandle, &exitCode)) {
+ exitCode = 1;
+ }
+ CloseHandle(childHandle);
+ return exitCode;
+}
diff --git a/src/libs/3rdparty/winpty/src/unix-adapter/subdir.mk b/src/libs/3rdparty/winpty/src/unix-adapter/subdir.mk
new file mode 100644
index 0000000000..200193a1b1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/unix-adapter/subdir.mk
@@ -0,0 +1,41 @@
+# Copyright (c) 2011-2015 Ryan Prichard
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+ALL_TARGETS += build/$(UNIX_ADAPTER_EXE)
+
+$(eval $(call def_unix_target,unix-adapter,))
+
+UNIX_ADAPTER_OBJECTS = \
+ build/unix-adapter/unix-adapter/InputHandler.o \
+ build/unix-adapter/unix-adapter/OutputHandler.o \
+ build/unix-adapter/unix-adapter/Util.o \
+ build/unix-adapter/unix-adapter/WakeupFd.o \
+ build/unix-adapter/unix-adapter/main.o \
+ build/unix-adapter/shared/DebugClient.o \
+ build/unix-adapter/shared/WinptyAssert.o \
+ build/unix-adapter/shared/WinptyVersion.o
+
+build/unix-adapter/shared/WinptyVersion.o : build/gen/GenVersion.h
+
+build/$(UNIX_ADAPTER_EXE) : $(UNIX_ADAPTER_OBJECTS) build/winpty.dll
+ $(info Linking $@)
+ @$(UNIX_CXX) $(UNIX_LDFLAGS) -o $@ $^
+
+-include $(UNIX_ADAPTER_OBJECTS:.o=.d)
diff --git a/src/libs/3rdparty/winpty/src/winpty.gyp b/src/libs/3rdparty/winpty/src/winpty.gyp
new file mode 100644
index 0000000000..7ee68d55e6
--- /dev/null
+++ b/src/libs/3rdparty/winpty/src/winpty.gyp
@@ -0,0 +1,206 @@
+{
+ # The MSVC generator is the default. Select the compiler version by
+ # passing -G msvs_version=<ver> to gyp. <ver> is a string like 2013e.
+ # See gyp\pylib\gyp\MSVSVersion.py for sample version strings. You
+ # can also pass configurations.gypi to gyp for 32-bit and 64-bit builds.
+ # See that file for details.
+ #
+ # Pass --format=make to gyp to generate a Makefile instead. The Makefile
+ # can be configured by passing variables to make, e.g.:
+ # make -j4 CXX=i686-w64-mingw32-g++ LDFLAGS="-static -static-libgcc -static-libstdc++"
+
+ 'variables': {
+ 'WINPTY_COMMIT_HASH%': '<!(cmd /c "cd shared && GetCommitHash.bat")',
+ },
+ 'target_defaults' : {
+ 'defines' : [
+ 'UNICODE',
+ '_UNICODE',
+ '_WIN32_WINNT=0x0501',
+ 'NOMINMAX',
+ ],
+ 'include_dirs': [
+ # Add the 'src/gen' directory to the include path and force gyp to
+ # run the script (re)generating the version header.
+ '<!(cmd /c "cd shared && UpdateGenVersion.bat <(WINPTY_COMMIT_HASH)")',
+ ],
+ },
+ 'targets' : [
+ {
+ 'target_name' : 'winpty-agent',
+ 'type' : 'executable',
+ 'include_dirs' : [
+ 'include',
+ ],
+ 'defines' : [
+ 'WINPTY_AGENT_ASSERT',
+ ],
+ 'libraries' : [
+ '-ladvapi32',
+ '-lshell32',
+ '-luser32',
+ ],
+ 'msvs_settings': {
+ # Specify this setting here to override a setting from somewhere
+ # else, such as node's common.gypi.
+ 'VCCLCompilerTool': {
+ 'ExceptionHandling': '1', # /EHsc
+ },
+ },
+ 'sources' : [
+ 'agent/Agent.h',
+ 'agent/Agent.cc',
+ 'agent/AgentCreateDesktop.h',
+ 'agent/AgentCreateDesktop.cc',
+ 'agent/ConsoleFont.cc',
+ 'agent/ConsoleFont.h',
+ 'agent/ConsoleInput.cc',
+ 'agent/ConsoleInput.h',
+ 'agent/ConsoleInputReencoding.cc',
+ 'agent/ConsoleInputReencoding.h',
+ 'agent/ConsoleLine.cc',
+ 'agent/ConsoleLine.h',
+ 'agent/Coord.h',
+ 'agent/DebugShowInput.h',
+ 'agent/DebugShowInput.cc',
+ 'agent/DefaultInputMap.h',
+ 'agent/DefaultInputMap.cc',
+ 'agent/DsrSender.h',
+ 'agent/EventLoop.h',
+ 'agent/EventLoop.cc',
+ 'agent/InputMap.h',
+ 'agent/InputMap.cc',
+ 'agent/LargeConsoleRead.h',
+ 'agent/LargeConsoleRead.cc',
+ 'agent/NamedPipe.h',
+ 'agent/NamedPipe.cc',
+ 'agent/Scraper.h',
+ 'agent/Scraper.cc',
+ 'agent/SimplePool.h',
+ 'agent/SmallRect.h',
+ 'agent/Terminal.h',
+ 'agent/Terminal.cc',
+ 'agent/UnicodeEncoding.h',
+ 'agent/Win32Console.cc',
+ 'agent/Win32Console.h',
+ 'agent/Win32ConsoleBuffer.cc',
+ 'agent/Win32ConsoleBuffer.h',
+ 'agent/main.cc',
+ 'shared/AgentMsg.h',
+ 'shared/BackgroundDesktop.h',
+ 'shared/BackgroundDesktop.cc',
+ 'shared/Buffer.h',
+ 'shared/Buffer.cc',
+ 'shared/DebugClient.h',
+ 'shared/DebugClient.cc',
+ 'shared/GenRandom.h',
+ 'shared/GenRandom.cc',
+ 'shared/OsModule.h',
+ 'shared/OwnedHandle.h',
+ 'shared/OwnedHandle.cc',
+ 'shared/StringBuilder.h',
+ 'shared/StringUtil.cc',
+ 'shared/StringUtil.h',
+ 'shared/UnixCtrlChars.h',
+ 'shared/WindowsSecurity.cc',
+ 'shared/WindowsSecurity.h',
+ 'shared/WindowsVersion.h',
+ 'shared/WindowsVersion.cc',
+ 'shared/WinptyAssert.h',
+ 'shared/WinptyAssert.cc',
+ 'shared/WinptyException.h',
+ 'shared/WinptyException.cc',
+ 'shared/WinptyVersion.h',
+ 'shared/WinptyVersion.cc',
+ 'shared/winpty_snprintf.h',
+ ],
+ },
+ {
+ 'target_name' : 'winpty',
+ 'type' : 'shared_library',
+ 'include_dirs' : [
+ 'include',
+ ],
+ 'defines' : [
+ 'COMPILING_WINPTY_DLL',
+ ],
+ 'libraries' : [
+ '-ladvapi32',
+ '-luser32',
+ ],
+ 'msvs_settings': {
+ # Specify this setting here to override a setting from somewhere
+ # else, such as node's common.gypi.
+ 'VCCLCompilerTool': {
+ 'ExceptionHandling': '1', # /EHsc
+ },
+ },
+ 'sources' : [
+ 'include/winpty.h',
+ 'libwinpty/AgentLocation.cc',
+ 'libwinpty/AgentLocation.h',
+ 'libwinpty/winpty.cc',
+ 'shared/AgentMsg.h',
+ 'shared/BackgroundDesktop.h',
+ 'shared/BackgroundDesktop.cc',
+ 'shared/Buffer.h',
+ 'shared/Buffer.cc',
+ 'shared/DebugClient.h',
+ 'shared/DebugClient.cc',
+ 'shared/GenRandom.h',
+ 'shared/GenRandom.cc',
+ 'shared/OsModule.h',
+ 'shared/OwnedHandle.h',
+ 'shared/OwnedHandle.cc',
+ 'shared/StringBuilder.h',
+ 'shared/StringUtil.cc',
+ 'shared/StringUtil.h',
+ 'shared/WindowsSecurity.cc',
+ 'shared/WindowsSecurity.h',
+ 'shared/WindowsVersion.h',
+ 'shared/WindowsVersion.cc',
+ 'shared/WinptyAssert.h',
+ 'shared/WinptyAssert.cc',
+ 'shared/WinptyException.h',
+ 'shared/WinptyException.cc',
+ 'shared/WinptyVersion.h',
+ 'shared/WinptyVersion.cc',
+ 'shared/winpty_snprintf.h',
+ ],
+ },
+ {
+ 'target_name' : 'winpty-debugserver',
+ 'type' : 'executable',
+ 'msvs_settings': {
+ # Specify this setting here to override a setting from somewhere
+ # else, such as node's common.gypi.
+ 'VCCLCompilerTool': {
+ 'ExceptionHandling': '1', # /EHsc
+ },
+ },
+ 'sources' : [
+ 'debugserver/DebugServer.cc',
+ 'shared/DebugClient.h',
+ 'shared/DebugClient.cc',
+ 'shared/OwnedHandle.h',
+ 'shared/OwnedHandle.cc',
+ 'shared/OsModule.h',
+ 'shared/StringBuilder.h',
+ 'shared/StringUtil.cc',
+ 'shared/StringUtil.h',
+ 'shared/WindowsSecurity.h',
+ 'shared/WindowsSecurity.cc',
+ 'shared/WindowsVersion.h',
+ 'shared/WindowsVersion.cc',
+ 'shared/WinptyAssert.h',
+ 'shared/WinptyAssert.cc',
+ 'shared/WinptyException.h',
+ 'shared/WinptyException.cc',
+ 'shared/winpty_snprintf.h',
+ ],
+ 'libraries' : [
+ '-ladvapi32',
+ ],
+ }
+ ],
+}
diff --git a/src/libs/3rdparty/winpty/vcbuild.bat b/src/libs/3rdparty/winpty/vcbuild.bat
new file mode 100644
index 0000000000..f3787a20f1
--- /dev/null
+++ b/src/libs/3rdparty/winpty/vcbuild.bat
@@ -0,0 +1,83 @@
+@echo off
+
+REM -- Script requirements:
+REM --
+REM -- * git This program must be in the Path to check out
+REM -- build-gyp. If that directory already exists, then
+REM -- git isn't necessary, but if it is missing, no
+REM -- commit hash will be embedded into binaries.
+REM --
+REM -- * python A non-Cygwin Python 2 python.exe must be in the
+REM -- Path to run gyp.
+REM --
+REM -- * msbuild msbuild must be in the Path. It is probably
+REM -- important to have msbuild from the correct MSVC
+REM -- release.
+REM --
+REM -- The script's output binaries are in the src/Release/{Win32,x64}
+REM -- directory.
+
+REM -------------------------------------------------------------------------
+REM -- Parse arguments
+
+setlocal
+cd %~dp0
+set GYP_ARGS=
+set MSVC_PLATFORM=x64
+
+:ParamLoop
+if "%1" == "" goto :ParamDone
+if "%1" == "--msvc-platform" (
+ REM -- One of Win32 or x64.
+ set MSVC_PLATFORM=%2
+ shift && shift
+ goto :ParamLoop
+)
+if "%1" == "--gyp-msvs-version" (
+ set GYP_ARGS=%GYP_ARGS% -G msvs_version=%2
+ shift && shift
+ goto :ParamLoop
+)
+if "%1" == "--toolset" (
+ set GYP_ARGS=%GYP_ARGS% -D WINPTY_MSBUILD_TOOLSET=%2
+ shift && shift
+ goto :ParamLoop
+)
+if "%1" == "--commit-hash" (
+ set GYP_ARGS=%GYP_ARGS% -D WINPTY_COMMIT_HASH=%2
+ shift && shift
+ goto :ParamLoop
+)
+echo error: Unrecognized argument: %1
+exit /b 1
+:ParamDone
+
+REM -------------------------------------------------------------------------
+REM -- Check out GYP. GYP doesn't seem to have releases, so just use the
+REM -- current master commit.
+
+if not exist build-gyp (
+ git clone https://chromium.googlesource.com/external/gyp build-gyp || (
+ echo error: GYP clone failed
+ exit /b 1
+ )
+)
+
+REM -------------------------------------------------------------------------
+REM -- Run gyp to generate MSVC project files.
+
+cd src
+
+call ..\build-gyp\gyp.bat winpty.gyp -I configurations.gypi %GYP_ARGS%
+if errorlevel 1 (
+ echo error: GYP failed
+ exit /b 1
+)
+
+REM -------------------------------------------------------------------------
+REM -- Compile the project.
+
+msbuild winpty.sln /m /p:Platform=%MSVC_PLATFORM% || (
+ echo error: msbuild failed
+ exit /b 1
+)
diff --git a/src/libs/3rdparty/winpty/winpty.qbs b/src/libs/3rdparty/winpty/winpty.qbs
new file mode 100644
index 0000000000..35d56f9265
--- /dev/null
+++ b/src/libs/3rdparty/winpty/winpty.qbs
@@ -0,0 +1,205 @@
+import qbs
+import qbs.TextFile
+
+Project {
+ name: "Winpty"
+ condition: qbs.targetOS.contains("windows")
+
+ Product {
+ name: "winpty_genversion_header"
+ type: "hpp"
+
+ Group {
+ files: "VERSION.txt"
+ fileTags: "txt.in"
+ }
+
+ Rule {
+ inputs: "txt.in"
+ Artifact {
+ filePath: "GenVersion.h"
+ fileTags: "hpp"
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating GenVersion.h";
+ cmd.highlight = "codegen";
+ cmd.sourceCode = function() {
+ var inFile = new TextFile(input.filePath);
+ var versionTxt = inFile.readAll();
+ inFile.close();
+ // remove any line endings
+ versionTxt = versionTxt.replace(/[\r\n]/g, "");
+
+ var content = 'const char GenVersion_Version[] = "@VERSION@";\n'
+ + 'const char GenVersion_Commit[] = "@COMMIT_HASH@";\n';
+ content = content.replace(/@VERSION@/g, versionTxt);
+
+ var outFile = new TextFile(output.filePath, TextFile.WriteOnly);
+ outFile.truncate();
+ outFile.write(content);
+ outFile.close();
+ }
+ return cmd;
+ }
+ }
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: exportingProduct.buildDirectory
+ }
+ }
+
+ QtcTool {
+ name: "winpty-agent"
+ Depends { name: "winpty_genversion_header" }
+ Depends { name: "cpp" }
+
+ useQt: false
+
+ cpp.includePaths: base.concat([sourceDirectory + "/include", buildDirectory])
+ cpp.defines: base.concat(["WINPTY_AGENT_ASSERT",
+ "NOMINMAX", "UNICODE", "_UNICODE"
+ ])
+ cpp.dynamicLibraries: ["user32", "shell32", "advapi32"]
+
+ files: [
+ "src/agent/Agent.h",
+ "src/agent/Agent.cc",
+ "src/agent/AgentCreateDesktop.h",
+ "src/agent/AgentCreateDesktop.cc",
+ "src/agent/ConsoleFont.cc",
+ "src/agent/ConsoleFont.h",
+ "src/agent/ConsoleInput.cc",
+ "src/agent/ConsoleInput.h",
+ "src/agent/ConsoleInputReencoding.cc",
+ "src/agent/ConsoleInputReencoding.h",
+ "src/agent/ConsoleLine.cc",
+ "src/agent/ConsoleLine.h",
+ "src/agent/Coord.h",
+ "src/agent/DebugShowInput.h",
+ "src/agent/DebugShowInput.cc",
+ "src/agent/DefaultInputMap.h",
+ "src/agent/DefaultInputMap.cc",
+ "src/agent/DsrSender.h",
+ "src/agent/EventLoop.h",
+ "src/agent/EventLoop.cc",
+ "src/agent/InputMap.h",
+ "src/agent/InputMap.cc",
+ "src/agent/LargeConsoleRead.h",
+ "src/agent/LargeConsoleRead.cc",
+ "src/agent/NamedPipe.h",
+ "src/agent/NamedPipe.cc",
+ "src/agent/Scraper.h",
+ "src/agent/Scraper.cc",
+ "src/agent/SimplePool.h",
+ "src/agent/SmallRect.h",
+ "src/agent/Terminal.h",
+ "src/agent/Terminal.cc",
+ "src/agent/UnicodeEncoding.h",
+ "src/agent/Win32Console.cc",
+ "src/agent/Win32Console.h",
+ "src/agent/Win32ConsoleBuffer.cc",
+ "src/agent/Win32ConsoleBuffer.h",
+ "src/agent/main.cc",
+ ]
+
+ Group {
+ name: "Shared sources"
+ prefix: "src/shared/"
+ files: [
+ "AgentMsg.h",
+ "BackgroundDesktop.h",
+ "BackgroundDesktop.cc",
+ "Buffer.h",
+ "Buffer.cc",
+ "DebugClient.h",
+ "DebugClient.cc",
+ "GenRandom.h",
+ "GenRandom.cc",
+ "OsModule.h",
+ "OwnedHandle.h",
+ "OwnedHandle.cc",
+ "StringBuilder.h",
+ "StringUtil.cc",
+ "StringUtil.h",
+ "UnixCtrlChars.h",
+ "WindowsSecurity.cc",
+ "WindowsSecurity.h",
+ "WindowsVersion.h",
+ "WindowsVersion.cc",
+ "WinptyAssert.h",
+ "WinptyAssert.cc",
+ "WinptyException.h",
+ "WinptyException.cc",
+ "WinptyVersion.h",
+ "WinptyVersion.cc",
+ "winpty_snprintf.h",
+ ]
+ }
+ }
+
+ QtcLibrary {
+ name: "winpty"
+ type: "staticlibrary"
+
+ Depends { name: "winpty_genversion_header" }
+ Depends { name: "cpp" }
+
+ useNonGuiPchFile: false
+ useGuiPchFile: false
+
+ cpp.defines: base.concat(["COMPILING_WINPTY_DLL",
+ "NOMINMAX", "UNICODE", "_UNICODE"
+ ])
+ cpp.dynamicLibraries: ["user32", "shell32", "advapi32"]
+ cpp.includePaths: base.concat(sourceDirectory + "/src/include")
+
+
+ files: [
+ "src/libwinpty/AgentLocation.cc",
+ "src/libwinpty/AgentLocation.h",
+ "src/libwinpty/winpty.cc",
+ ]
+
+ Group {
+ name: "Shared sources" // FIXME duplication
+ prefix: "src/shared/"
+ files: [
+ "AgentMsg.h",
+ "BackgroundDesktop.h",
+ "BackgroundDesktop.cc",
+ "Buffer.h",
+ "Buffer.cc",
+ "DebugClient.h",
+ "DebugClient.cc",
+ "GenRandom.h",
+ "GenRandom.cc",
+ "OsModule.h",
+ "OwnedHandle.h",
+ "OwnedHandle.cc",
+ "StringBuilder.h",
+ "StringUtil.cc",
+ "StringUtil.h",
+ "UnixCtrlChars.h",
+ "WindowsSecurity.cc",
+ "WindowsSecurity.h",
+ "WindowsVersion.h",
+ "WindowsVersion.cc",
+ "WinptyAssert.h",
+ "WinptyAssert.cc",
+ "WinptyException.h",
+ "WinptyException.cc",
+ "WinptyVersion.h",
+ "WinptyVersion.cc",
+ "winpty_snprintf.h",
+ ]
+ }
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.defines: "COMPILING_WINPTY_DLL"
+ cpp.includePaths: exportingProduct.sourceDirectory + "/src/include"
+ }
+ }
+}
diff --git a/src/libs/3rdparty/xdg/README b/src/libs/3rdparty/xdg/README
new file mode 100644
index 0000000000..00643422ec
--- /dev/null
+++ b/src/libs/3rdparty/xdg/README
@@ -0,0 +1,7 @@
+This contains a copy of freedesktop.org's shared-mime-info base package.
+
+Update instructions:
+
+ curl https://people.freedesktop.org/~hadess/shared-mime-info-{VERSION}.tar.xz | \
+ xzcat | \
+ tar -xO shared-mime-info-{VERSION}/freedestop.org.xml > freedesktop.org.xml
diff --git a/src/libs/3rdparty/xdg/freedesktop.org.xml b/src/libs/3rdparty/xdg/freedesktop.org.xml
new file mode 100644
index 0000000000..b2be8dc88e
--- /dev/null
+++ b/src/libs/3rdparty/xdg/freedesktop.org.xml
@@ -0,0 +1,41233 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mime-info [
+ <!ELEMENT mime-info (mime-type)+>
+ <!ATTLIST mime-info xmlns CDATA #FIXED "http://www.freedesktop.org/standards/shared-mime-info">
+
+ <!ELEMENT mime-type (comment+, (acronym,expanded-acronym)? , (icon? | generic-icon? | glob | magic | treemagic | root-XML | alias | sub-class-of)*)>
+ <!ATTLIST mime-type type CDATA #REQUIRED>
+
+ <!-- a comment describing a document with the respective MIME type. Example: "WMV video" -->
+ <!ELEMENT comment (#PCDATA)>
+ <!ATTLIST comment xml:lang CDATA #IMPLIED>
+
+ <!-- a comment describing the respective unexpanded MIME type acronym. Example: "WMV" -->
+ <!ELEMENT acronym (#PCDATA)>
+ <!ATTLIST acronym xml:lang CDATA #IMPLIED>
+
+ <!-- a comment describing the respective expanded MIME type acronym. Example: "Windows Media Video" -->
+ <!ELEMENT expanded-acronym (#PCDATA)>
+ <!ATTLIST expanded-acronym xml:lang CDATA #IMPLIED>
+
+ <!ELEMENT icon EMPTY>
+ <!ATTLIST icon name CDATA #REQUIRED>
+
+ <!-- a generic icon name as per the Icon Naming Specification, only required if computing
+ it from the mime-type would not work, See "generic-icon" in the Shared Mime Specification -->
+ <!ELEMENT generic-icon EMPTY>
+ <!ATTLIST generic-icon name (application-x-executable|audio-x-generic|folder|font-x-generic|image-x-generic|package-x-generic|text-html|text-x-generic|text-x-generic-template|text-x-script|video-x-generic|x-office-address-book|x-office-calendar|x-office-document|x-office-presentation|x-office-spreadsheet) #REQUIRED>
+
+ <!ELEMENT glob EMPTY>
+ <!ATTLIST glob pattern CDATA #REQUIRED>
+ <!ATTLIST glob weight CDATA "50">
+ <!ATTLIST glob case-sensitive CDATA #IMPLIED>
+
+ <!ELEMENT magic (match)+>
+ <!ATTLIST magic priority CDATA "50">
+
+ <!ELEMENT match (match)*>
+ <!ATTLIST match offset CDATA #REQUIRED>
+ <!ATTLIST match type (string|big16|big32|little16|little32|host16|host32|byte) #REQUIRED>
+ <!ATTLIST match value CDATA #REQUIRED>
+ <!ATTLIST match mask CDATA #IMPLIED>
+
+ <!ELEMENT treemagic (treematch)+>
+ <!ATTLIST treemagic priority CDATA "50">
+
+ <!ELEMENT treematch (treematch)*>
+ <!ATTLIST treematch path CDATA #REQUIRED>
+ <!ATTLIST treematch type (file|directory|link) #IMPLIED>
+ <!ATTLIST treematch match-case (true|false) #IMPLIED>
+ <!ATTLIST treematch executable (true|false) #IMPLIED>
+ <!ATTLIST treematch non-empty (true|false) #IMPLIED>
+ <!ATTLIST treematch mimetype CDATA #IMPLIED>
+
+ <!ELEMENT root-XML EMPTY>
+ <!ATTLIST root-XML namespaceURI CDATA #REQUIRED>
+ <!ATTLIST root-XML localName CDATA #REQUIRED>
+
+ <!ELEMENT alias EMPTY>
+ <!ATTLIST alias type CDATA #REQUIRED>
+
+ <!ELEMENT sub-class-of EMPTY>
+ <!ATTLIST sub-class-of type CDATA #REQUIRED>
+]>
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+ <mime-type type="application/x-atari-2600-rom">
+ <comment>Atari 2600</comment>
+ <comment xml:lang="ast">Atari 2600</comment>
+ <comment xml:lang="ca">Atari 2600</comment>
+ <comment xml:lang="cs">Atari 2600</comment>
+ <comment xml:lang="da">Atari 2600</comment>
+ <comment xml:lang="de">Atari 2600</comment>
+ <comment xml:lang="en_GB">Atari 2600</comment>
+ <comment xml:lang="es">Atari 2600</comment>
+ <comment xml:lang="eu">Atari 2600</comment>
+ <comment xml:lang="fi">Atari 2600</comment>
+ <comment xml:lang="fr">Atari 2600</comment>
+ <comment xml:lang="ga">Atari 2600</comment>
+ <comment xml:lang="he">אטארי 2600</comment>
+ <comment xml:lang="hr">Atari 2600</comment>
+ <comment xml:lang="hu">Atari 2600</comment>
+ <comment xml:lang="id">Atari 2600</comment>
+ <comment xml:lang="it">Atari 2600</comment>
+ <comment xml:lang="kk">Atari 2600</comment>
+ <comment xml:lang="ko">Atari 2600</comment>
+ <comment xml:lang="oc">Atari 2600</comment>
+ <comment xml:lang="pl">Atari 2600</comment>
+ <comment xml:lang="pt_BR">Atari 2600</comment>
+ <comment xml:lang="ru">Atari 2600</comment>
+ <comment xml:lang="sk">Atari 2600</comment>
+ <comment xml:lang="sr">Атари 2600</comment>
+ <comment xml:lang="sv">Atari 2600</comment>
+ <comment xml:lang="tr">Atari 2600</comment>
+ <comment xml:lang="uk">Atari 2600</comment>
+ <comment xml:lang="zh_CN">雅达利 2600</comment>
+ <comment xml:lang="zh_TW">Atari 2600</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.a26"/>
+ </mime-type>
+ <mime-type type="application/x-atari-7800-rom">
+ <comment>Atari 7800</comment>
+ <comment xml:lang="ast">Atari 7800</comment>
+ <comment xml:lang="ca">Atari 7800</comment>
+ <comment xml:lang="cs">Atari 7800</comment>
+ <comment xml:lang="da">Atari 7800</comment>
+ <comment xml:lang="de">Atari 7800</comment>
+ <comment xml:lang="en_GB">Atari 7800</comment>
+ <comment xml:lang="es">Atari 7800</comment>
+ <comment xml:lang="eu">Atari 7800</comment>
+ <comment xml:lang="fi">Atari 7800</comment>
+ <comment xml:lang="fr">Atari 7800</comment>
+ <comment xml:lang="ga">Atari 7800</comment>
+ <comment xml:lang="he">אטארי 7800</comment>
+ <comment xml:lang="hr">Atari 7800</comment>
+ <comment xml:lang="hu">Atari 7800</comment>
+ <comment xml:lang="id">Atari 7800</comment>
+ <comment xml:lang="it">Atari 7800</comment>
+ <comment xml:lang="kk">Atari 7800</comment>
+ <comment xml:lang="ko">Atari 7800</comment>
+ <comment xml:lang="oc">Atari 7800</comment>
+ <comment xml:lang="pl">Atari 7800</comment>
+ <comment xml:lang="pt_BR">Atari 7800</comment>
+ <comment xml:lang="ru">Atari 7800</comment>
+ <comment xml:lang="sk">Atari 7800</comment>
+ <comment xml:lang="sr">Атари 7800</comment>
+ <comment xml:lang="sv">Atari 7800</comment>
+ <comment xml:lang="tr">Atari 7800</comment>
+ <comment xml:lang="uk">Atari 7800</comment>
+ <comment xml:lang="zh_CN">雅达利 7800</comment>
+ <comment xml:lang="zh_TW">Atari 7800</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.a78"/>
+ <magic>
+ <match value="ATARI7800" type="string" offset="1"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-atari-lynx-rom">
+ <comment>Atari Lynx</comment>
+ <comment xml:lang="ast">Atari Lynx</comment>
+ <comment xml:lang="ca">Atari Lynx</comment>
+ <comment xml:lang="cs">Atari Lynx</comment>
+ <comment xml:lang="de">Atari Lynx</comment>
+ <comment xml:lang="en_GB">Atari Lynx</comment>
+ <comment xml:lang="es">Atari Lynx</comment>
+ <comment xml:lang="fi">Atari Lynx</comment>
+ <comment xml:lang="hr">Atari Lynx</comment>
+ <comment xml:lang="hu">Atari Lynx</comment>
+ <comment xml:lang="id">Atari Lynx</comment>
+ <comment xml:lang="it">Atari Lynx</comment>
+ <comment xml:lang="kk">Atari Lynx</comment>
+ <comment xml:lang="ko">Atari Lynx</comment>
+ <comment xml:lang="pl">Atari Lynx</comment>
+ <comment xml:lang="pt_BR">Atari Lynx</comment>
+ <comment xml:lang="ru">Atari Lynx</comment>
+ <comment xml:lang="sk">Atari Lynx</comment>
+ <comment xml:lang="sv">Atari Lynx</comment>
+ <comment xml:lang="uk">Atari Lynx</comment>
+ <comment xml:lang="zh_CN">雅达利 Lynx</comment>
+ <comment xml:lang="zh_TW">Atari Lynx</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.lnx"/>
+ <magic>
+ <match value="LYNX" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/andrew-inset">
+ <comment>ATK inset</comment>
+ <comment xml:lang="ar">شكل ATK</comment>
+ <comment xml:lang="be@latin">Ustaŭka ATK</comment>
+ <comment xml:lang="bg">Сбор — ATK</comment>
+ <comment xml:lang="ca">ATK inset</comment>
+ <comment xml:lang="cs">vložka ATK</comment>
+ <comment xml:lang="da">ATK-indsættelse</comment>
+ <comment xml:lang="de">ATK-Inset</comment>
+ <comment xml:lang="el">Ένθετο ATK</comment>
+ <comment xml:lang="en_GB">ATK inset</comment>
+ <comment xml:lang="es">inserción ATK</comment>
+ <comment xml:lang="eu">ATK sartzapena</comment>
+ <comment xml:lang="fi">ATK-osio</comment>
+ <comment xml:lang="fo">ATK innskot</comment>
+ <comment xml:lang="fr">encart ATK</comment>
+ <comment xml:lang="ga">intlis ATK</comment>
+ <comment xml:lang="gl">conxunto ATK</comment>
+ <comment xml:lang="he">תוספת ATK</comment>
+ <comment xml:lang="hr">ATK umetak</comment>
+ <comment xml:lang="hu">ATK betét</comment>
+ <comment xml:lang="ia">Folio intercalari ATK</comment>
+ <comment xml:lang="id">Inset ATK</comment>
+ <comment xml:lang="it">Inset ATK</comment>
+ <comment xml:lang="ja">ATK インセット</comment>
+ <comment xml:lang="kk">ATK беті</comment>
+ <comment xml:lang="ko">ATK inset</comment>
+ <comment xml:lang="lt">ATK inset</comment>
+ <comment xml:lang="lv">ATK ielaidums</comment>
+ <comment xml:lang="nb">ATK-innsats</comment>
+ <comment xml:lang="nl">ATK-invoegsel</comment>
+ <comment xml:lang="nn">ATK-innskot</comment>
+ <comment xml:lang="oc">encart ATK</comment>
+ <comment xml:lang="pl">Wstawka ATK</comment>
+ <comment xml:lang="pt">Suplemento ATK</comment>
+ <comment xml:lang="pt_BR">Conjunto de entrada do ATK</comment>
+ <comment xml:lang="ro">Inset ATK</comment>
+ <comment xml:lang="ru">Вкладка ATK</comment>
+ <comment xml:lang="sk">Vložka ATK</comment>
+ <comment xml:lang="sl">Vložka ATK</comment>
+ <comment xml:lang="sq">Inset ATK</comment>
+ <comment xml:lang="sr">АТК уметак</comment>
+ <comment xml:lang="sv">ATK-inlägg</comment>
+ <comment xml:lang="tr">ATK iç metni</comment>
+ <comment xml:lang="uk">вкладка ATK</comment>
+ <comment xml:lang="vi">Bộ dát ATK</comment>
+ <comment xml:lang="zh_CN">ATK 嵌入对象</comment>
+ <comment xml:lang="zh_TW">ATK 內嵌</comment>
+ <acronym>ATK</acronym>
+ <expanded-acronym>Andrew Toolkit</expanded-acronym>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.ez"/>
+ </mime-type>
+ <mime-type type="application/epub+zip">
+ <comment>electronic book document</comment>
+ <comment xml:lang="ar">مستند كتاب إلكتروني</comment>
+ <comment xml:lang="ast">documentu de llibru electrónicu</comment>
+ <comment xml:lang="be@latin">elektronnaja kniha</comment>
+ <comment xml:lang="bg">Документ — електронна книга</comment>
+ <comment xml:lang="ca">document de llibre electrònic</comment>
+ <comment xml:lang="cs">dokument elektronické knihy</comment>
+ <comment xml:lang="da">elektronisk bogdokument</comment>
+ <comment xml:lang="de">Elektronisches Buch</comment>
+ <comment xml:lang="el">Έγγραφο ηλεκτρονικού βιβλίου</comment>
+ <comment xml:lang="en_GB">electronic book document</comment>
+ <comment xml:lang="es">documento de libro electrónico</comment>
+ <comment xml:lang="eu">liburu elektronikoaren dokumentua</comment>
+ <comment xml:lang="fi">elektroninen kirja</comment>
+ <comment xml:lang="fo">elektroniskbóka skjal</comment>
+ <comment xml:lang="fr">document livre électronique</comment>
+ <comment xml:lang="ga">leabhar leictreonach</comment>
+ <comment xml:lang="gl">documento de libro electrónico</comment>
+ <comment xml:lang="he">מסמך מסוג ספר אלקטרוני</comment>
+ <comment xml:lang="hr">Dokument elektroničke knjige</comment>
+ <comment xml:lang="hu">elektronikus könyvdokumentum</comment>
+ <comment xml:lang="ia">Documento de libro electronic</comment>
+ <comment xml:lang="id">dokumen buku elektronik</comment>
+ <comment xml:lang="it">Documento libro elettronico</comment>
+ <comment xml:lang="ja">電子ブックドキュメント</comment>
+ <comment xml:lang="kk">электронды кітабы</comment>
+ <comment xml:lang="ko">전자책 문서</comment>
+ <comment xml:lang="lt">elektroninės knygos dokumentas</comment>
+ <comment xml:lang="lv">elektroniskās grāmatas dokuments</comment>
+ <comment xml:lang="nl">elektronisch boek</comment>
+ <comment xml:lang="nn">elektronisk bok-dokument</comment>
+ <comment xml:lang="oc">document libre electronic</comment>
+ <comment xml:lang="pl">Dokument książki elektronicznej</comment>
+ <comment xml:lang="pt">documento de livro eletrónico</comment>
+ <comment xml:lang="pt_BR">Documento de livro eletrônico</comment>
+ <comment xml:lang="ro">document carte electronică</comment>
+ <comment xml:lang="ru">Электронная книга</comment>
+ <comment xml:lang="sk">Dokument elektronickej knihy</comment>
+ <comment xml:lang="sl">dokument elektronske knjige</comment>
+ <comment xml:lang="sq">Dokument libri elektronik</comment>
+ <comment xml:lang="sr">документ електронске књиге</comment>
+ <comment xml:lang="sv">elektroniskt bokdokument</comment>
+ <comment xml:lang="tr">elektronik kitap belgesi</comment>
+ <comment xml:lang="uk">документ електронної книги</comment>
+ <comment xml:lang="vi">tài liệu cuốn sách điện tử</comment>
+ <comment xml:lang="zh_CN">电子书文档</comment>
+ <comment xml:lang="zh_TW">電子書文件</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/epub+zip" type="string" offset="38"/>
+ <match value="application/epub+zip" type="string" offset="43"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.epub"/>
+ </mime-type>
+ <mime-type type="application/illustrator">
+ <comment>Adobe Illustrator document</comment>
+ <comment xml:lang="ar">مستند أدوبي المصور</comment>
+ <comment xml:lang="ast">Documentu d'Adobe Illustrator</comment>
+ <comment xml:lang="be@latin">Dakument Adobe Illustrator</comment>
+ <comment xml:lang="bg">Документ — Adobe Illustrator</comment>
+ <comment xml:lang="ca">document d'Adobe Illustrator</comment>
+ <comment xml:lang="cs">dokument Adobe Illustrator</comment>
+ <comment xml:lang="da">Adobe Illustrator-dokument</comment>
+ <comment xml:lang="de">Adobe-Illustrator-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Adobe Illustrator</comment>
+ <comment xml:lang="en_GB">Adobe Illustrator document</comment>
+ <comment xml:lang="eo">dokumento de Adobe Illustrator</comment>
+ <comment xml:lang="es">documento de Adobe Illustrator</comment>
+ <comment xml:lang="eu">Adobe Illustrator dokumentua</comment>
+ <comment xml:lang="fi">Adobe Illustrator -asiakirja</comment>
+ <comment xml:lang="fo">Adobe Illustrator skjal</comment>
+ <comment xml:lang="fr">document Adobe Illustrator</comment>
+ <comment xml:lang="ga">cáipéis Adobe Illustrator</comment>
+ <comment xml:lang="gl">documento de Adobe Ilustrator</comment>
+ <comment xml:lang="he">מסמך Adobe Ill</comment>
+ <comment xml:lang="hr">Adobe Illustrator dokument</comment>
+ <comment xml:lang="hu">Adobe Illustrator-dokumentum</comment>
+ <comment xml:lang="ia">Documento Adobe Illustrator</comment>
+ <comment xml:lang="id">dokumen Adobe Illustrator</comment>
+ <comment xml:lang="it">Documento Adobe Illustrator</comment>
+ <comment xml:lang="ja">Adobe Illustrator ドキュメント</comment>
+ <comment xml:lang="ka">Adobe Illustrator-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">Adobe Illustrator құжаты</comment>
+ <comment xml:lang="ko">Adobe Illustrator 문서</comment>
+ <comment xml:lang="lt">Adobe Illustrator dokumentas</comment>
+ <comment xml:lang="lv">Adobe Illustrator dokuments</comment>
+ <comment xml:lang="ms">Dokumen Adobe Illustrator</comment>
+ <comment xml:lang="nb">Adobe Illustrator-dokument</comment>
+ <comment xml:lang="nl">Adobe Illustrator-document</comment>
+ <comment xml:lang="nn">Adobe Illustrator-dokument</comment>
+ <comment xml:lang="oc">document Adobe Illustrator</comment>
+ <comment xml:lang="pl">Dokument Adobe Illustrator</comment>
+ <comment xml:lang="pt">documento Adobe Illustrator</comment>
+ <comment xml:lang="pt_BR">Documento do Adobe Illustrator</comment>
+ <comment xml:lang="ro">Document Adobe Illustrator</comment>
+ <comment xml:lang="ru">Документ Adobe Illustrator</comment>
+ <comment xml:lang="sk">Dokument Adobe Illustrator</comment>
+ <comment xml:lang="sl">Dokument Adobe Illustrator</comment>
+ <comment xml:lang="sq">Dokument Adobe Illustrator</comment>
+ <comment xml:lang="sr">документ Адобе илустратора</comment>
+ <comment xml:lang="sv">Adobe Illustrator-dokument</comment>
+ <comment xml:lang="tr">Adobe Illustrator belgesi</comment>
+ <comment xml:lang="uk">документ Adobe Illustrator</comment>
+ <comment xml:lang="vi">Tài liệu Adobe Illustrator</comment>
+ <comment xml:lang="zh_CN">Adobe Illustrator 文档</comment>
+ <comment xml:lang="zh_TW">Adobe Illustrator 文件</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.ai"/>
+ <alias type="application/vnd.adobe.illustrator"/>
+ </mime-type>
+ <mime-type type="application/mac-binhex40">
+ <comment>Macintosh BinHex-encoded file</comment>
+ <comment xml:lang="ar">ملف Macintosh BinHex مشفر</comment>
+ <comment xml:lang="ast">Ficheru codificáu en BinHex de Machintosh</comment>
+ <comment xml:lang="az">Macintosh BinHex-kodlanmış fayl</comment>
+ <comment xml:lang="be@latin">Fajł Macintosh, BinHex-zakadavany</comment>
+ <comment xml:lang="bg">Файл — кодиран във формат BinHex за Macintosh</comment>
+ <comment xml:lang="ca">fitxer amb codificació BinHex de Macintosh</comment>
+ <comment xml:lang="cs">soubor kódovaný pomocí Macintosh BinHex</comment>
+ <comment xml:lang="cy">Ffeil BinHex-amgodwyd Macintosh</comment>
+ <comment xml:lang="da">Macintosh BinHex-kodet fil</comment>
+ <comment xml:lang="de">Macintosh-Datei (BinHex-kodiert)</comment>
+ <comment xml:lang="el">Αρχείο Macintosh κωδικοποίησης BinHex</comment>
+ <comment xml:lang="en_GB">Macintosh BinHex-encoded file</comment>
+ <comment xml:lang="eo">dosiero kodigita laŭ Macintosh BinHex</comment>
+ <comment xml:lang="es">archivo Macintosh codificado con BinHex</comment>
+ <comment xml:lang="eu">Macintosh BinHex-ekin kodetutako fitxategia</comment>
+ <comment xml:lang="fi">Macintosh BinHex -koodattu tiedosto</comment>
+ <comment xml:lang="fo">Macintosh BinHex-bronglað fíla</comment>
+ <comment xml:lang="fr">fichier codé Macintosh BinHex</comment>
+ <comment xml:lang="ga">comhad ionchódaithe le Macintosh BinHex</comment>
+ <comment xml:lang="gl">ficheiro de Macintosh codificado con BinHex</comment>
+ <comment xml:lang="he">קובץ בקידוד Macintosh BinHex</comment>
+ <comment xml:lang="hr">Macintosh BinHex-kôdirana datoteka</comment>
+ <comment xml:lang="hu">Macintosh BinHex kódolású fájl</comment>
+ <comment xml:lang="ia">File codificate in BinHex de Macintosh</comment>
+ <comment xml:lang="id">berkas tersandi Macintosh BinHex</comment>
+ <comment xml:lang="it">File Macintosh codificato BinHex</comment>
+ <comment xml:lang="ja">Macintosh BinHex エンコードファイル</comment>
+ <comment xml:lang="kk">Macintosh BinHex кодталған файлы</comment>
+ <comment xml:lang="ko">매킨토시 BinHex 인코딩된 압축 파일</comment>
+ <comment xml:lang="lt">Macintosh BinHex-encoded failas</comment>
+ <comment xml:lang="lv">Macintosh BinHex-kodēts datne</comment>
+ <comment xml:lang="ms">Fail terenkod-BinHex Macintosh</comment>
+ <comment xml:lang="nb">Macintosh BinHe-kodet arkiv</comment>
+ <comment xml:lang="nl">Macintosh BinHex-gecodeerd bestand</comment>
+ <comment xml:lang="nn">Macintosh BinHex-koda fil</comment>
+ <comment xml:lang="oc">fichièr encodat Macintosh BinHex</comment>
+ <comment xml:lang="pl">Zakodowany w BinHex plik Macintosh</comment>
+ <comment xml:lang="pt">ficheiro codificado em BinHex de Macintosh</comment>
+ <comment xml:lang="pt_BR">Arquivo do Macintosh codificado com BinHex</comment>
+ <comment xml:lang="ro">Fișier codat Macintosh BinHex</comment>
+ <comment xml:lang="ru">Файл (закодированный Macintosh BinHex)</comment>
+ <comment xml:lang="sk">Súbor kódovaný pomocou Macintosh BinHex</comment>
+ <comment xml:lang="sl">Kodirana datoteka Macintosh (BinHex)</comment>
+ <comment xml:lang="sq">File Macintosh i kodifikuar BinHex</comment>
+ <comment xml:lang="sr">Мекинтошова БинХекс-кодирана датотека</comment>
+ <comment xml:lang="sv">Macintosh BinHex-kodad fil</comment>
+ <comment xml:lang="tr">Macintosh BinHex-şifreli dosya</comment>
+ <comment xml:lang="uk">файл закодований Macintosh BinHex</comment>
+ <comment xml:lang="vi">Tập tin đã mã hoá BinHex của Macintosh</comment>
+ <comment xml:lang="zh_CN">Macintosh BinHex 编码的文件</comment>
+ <comment xml:lang="zh_TW">Macintosh BinHex 編碼檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="must be converted with BinHex" type="string" offset="11"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/mathematica">
+ <comment>Mathematica Notebook</comment>
+ <comment xml:lang="ar">مذكرة رياضيات</comment>
+ <comment xml:lang="be@latin">Natatnik Mathematica</comment>
+ <comment xml:lang="bg">Тетрадка — Mathematica</comment>
+ <comment xml:lang="ca">llibreta de notes de Mathematica</comment>
+ <comment xml:lang="cs">sešit Mathematica</comment>
+ <comment xml:lang="da">Mathematica Notebook</comment>
+ <comment xml:lang="de">Mathematica-Dokument</comment>
+ <comment xml:lang="el">Σημειωματάριο Mathematica</comment>
+ <comment xml:lang="en_GB">Mathematica Notebook</comment>
+ <comment xml:lang="es">libreta de Mathematica</comment>
+ <comment xml:lang="eu">Mathematica Notebook</comment>
+ <comment xml:lang="fi">Mathematica-muistilehtiö</comment>
+ <comment xml:lang="fo">Mathematica skriviblokkur</comment>
+ <comment xml:lang="fr">carnet de notes Mathematica</comment>
+ <comment xml:lang="ga">leabhar nótaí Mathematica</comment>
+ <comment xml:lang="gl">notebook de Mathematica</comment>
+ <comment xml:lang="he">מחברת מתמטיקה</comment>
+ <comment xml:lang="hr">Matematička bilježnica</comment>
+ <comment xml:lang="hu">Mathematica notesz</comment>
+ <comment xml:lang="ia">Carnet de notas Mathematica</comment>
+ <comment xml:lang="id">Mathematica Notebook</comment>
+ <comment xml:lang="it">Notebook Mathematica</comment>
+ <comment xml:lang="ja">Mathematica ノートブック</comment>
+ <comment xml:lang="kk">Mathematica Notebook</comment>
+ <comment xml:lang="ko">Mathematica 노트북</comment>
+ <comment xml:lang="lt">Mathematica užrašinė</comment>
+ <comment xml:lang="lv">Mathematica bloknots</comment>
+ <comment xml:lang="nb">Mathematica notisblokk</comment>
+ <comment xml:lang="nl">Mathematica-notitieboek</comment>
+ <comment xml:lang="nn">Mathematica-notatbok</comment>
+ <comment xml:lang="oc">quasernet de nòtas Mathematica</comment>
+ <comment xml:lang="pl">Notatnik Mathematica</comment>
+ <comment xml:lang="pt">Bloco notas Mathematica</comment>
+ <comment xml:lang="pt_BR">Caderno do Mathematica</comment>
+ <comment xml:lang="ro">Carnețel Mathematica</comment>
+ <comment xml:lang="ru">Mathematica Notebook</comment>
+ <comment xml:lang="sk">Zošit programu Mathematica</comment>
+ <comment xml:lang="sl">Datoteka dokumenta Mathematica</comment>
+ <comment xml:lang="sq">Notebook matematike</comment>
+ <comment xml:lang="sr">бележница Математике</comment>
+ <comment xml:lang="sv">Mathematica Notebook-dokument</comment>
+ <comment xml:lang="tr">Mathematica Defteri</comment>
+ <comment xml:lang="uk">математичний записник</comment>
+ <comment xml:lang="vi">Cuốn vở Mathematica</comment>
+ <comment xml:lang="zh_CN">Mathematica 笔记本</comment>
+ <comment xml:lang="zh_TW">Mathematica Notebook</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.nb"/>
+ <magic priority="50">
+ <match value="(************** Content-type: application/mathematica" type="string" offset="0"/>
+ <match value="This notebook can be used on any computer system with Mathematica" type="string" offset="100:256"/>
+ <match value="This is a Mathematica Notebook file. It contains ASCII text" type="string" offset="10:256"/>
+ </magic>
+ <alias type="application/x-mathematica"/>
+ </mime-type>
+ <mime-type type="application/mathml+xml">
+ <comment>MathML document</comment>
+ <comment xml:lang="ar">مستند MathML</comment>
+ <comment xml:lang="ast">Documentu MathML</comment>
+ <comment xml:lang="az">MathML sənədi</comment>
+ <comment xml:lang="be@latin">Dakument MathML</comment>
+ <comment xml:lang="bg">Документ — MathML</comment>
+ <comment xml:lang="ca">document MathML</comment>
+ <comment xml:lang="cs">dokument MathML</comment>
+ <comment xml:lang="cy">Dogfen MathML</comment>
+ <comment xml:lang="da">MathML-dokument</comment>
+ <comment xml:lang="de">MathML-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο MathML</comment>
+ <comment xml:lang="en_GB">MathML document</comment>
+ <comment xml:lang="eo">MathML-dokumento</comment>
+ <comment xml:lang="es">documento MathML</comment>
+ <comment xml:lang="eu">MathML dokumentua</comment>
+ <comment xml:lang="fi">MathML-asiakirja</comment>
+ <comment xml:lang="fo">MathML skjal</comment>
+ <comment xml:lang="fr">document MathML</comment>
+ <comment xml:lang="ga">cáipéis MathML</comment>
+ <comment xml:lang="gl">documento de MathML</comment>
+ <comment xml:lang="he">מסמך MathML</comment>
+ <comment xml:lang="hr">MathML dokument</comment>
+ <comment xml:lang="hu">MathML-dokumentum</comment>
+ <comment xml:lang="ia">Documento MathML</comment>
+ <comment xml:lang="id">dokumen MathML</comment>
+ <comment xml:lang="it">Documento MathML</comment>
+ <comment xml:lang="ja">MathML ドキュメント</comment>
+ <comment xml:lang="ka">MathML-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">MathML құжаты</comment>
+ <comment xml:lang="ko">MathML 문서</comment>
+ <comment xml:lang="lt">MathML dokumentas</comment>
+ <comment xml:lang="lv">MathML dokuments</comment>
+ <comment xml:lang="ms">Dokumen MathML</comment>
+ <comment xml:lang="nb">MathML-dokument</comment>
+ <comment xml:lang="nl">MathML-document</comment>
+ <comment xml:lang="nn">MathML-dokument</comment>
+ <comment xml:lang="oc">document MathML</comment>
+ <comment xml:lang="pl">Dokument MathML</comment>
+ <comment xml:lang="pt">documento MathML</comment>
+ <comment xml:lang="pt_BR">Documento do MathML</comment>
+ <comment xml:lang="ro">Document MathML</comment>
+ <comment xml:lang="ru">Документ MathML</comment>
+ <comment xml:lang="sk">Dokument MathML</comment>
+ <comment xml:lang="sl">Dokument MathML</comment>
+ <comment xml:lang="sq">Dokument MathML</comment>
+ <comment xml:lang="sr">МатМЛ документ</comment>
+ <comment xml:lang="sv">MathML-dokument</comment>
+ <comment xml:lang="tr">MathML belgesi</comment>
+ <comment xml:lang="uk">документ MathML</comment>
+ <comment xml:lang="vi">Tài liệu MathML</comment>
+ <comment xml:lang="zh_CN">MathML 文档</comment>
+ <comment xml:lang="zh_TW">MathML 文件</comment>
+ <acronym>MathML</acronym>
+ <expanded-acronym>Mathematical Markup Language</expanded-acronym>
+ <alias type="text/mathml"/>
+ <sub-class-of type="application/xml"/>
+ <glob pattern="*.mml"/>
+ <root-XML namespaceURI="http://www.w3.org/1998/Math/MathML" localName="math"/>
+ </mime-type>
+ <mime-type type="application/mbox">
+ <comment>mailbox file</comment>
+ <comment xml:lang="ar">ملف صندوق البريد</comment>
+ <comment xml:lang="be@latin">fajł paštovaj skryni</comment>
+ <comment xml:lang="bg">Файл — Mailbox</comment>
+ <comment xml:lang="ca">fitxer mailbox</comment>
+ <comment xml:lang="cs">soubor mailbox</comment>
+ <comment xml:lang="da">postkassefil</comment>
+ <comment xml:lang="de">Mailbox-Datei</comment>
+ <comment xml:lang="el">Αρχείο mailbox</comment>
+ <comment xml:lang="en_GB">mailbox file</comment>
+ <comment xml:lang="es">archivo de buzón de correo</comment>
+ <comment xml:lang="eu">mailbox fitxategia</comment>
+ <comment xml:lang="fi">mailbox-tiedosto</comment>
+ <comment xml:lang="fo">postkassafíla</comment>
+ <comment xml:lang="fr">fichier boîte aux lettres</comment>
+ <comment xml:lang="ga">comhad bhosca poist</comment>
+ <comment xml:lang="gl">ficheiro de caixa de correo</comment>
+ <comment xml:lang="he">קובץ תיבת-דואר</comment>
+ <comment xml:lang="hr">Datoteka poštanskog sandučića</comment>
+ <comment xml:lang="hu">mailbox fájl</comment>
+ <comment xml:lang="ia">File de cassa postal</comment>
+ <comment xml:lang="id">berkas kotak surat</comment>
+ <comment xml:lang="it">File mailbox</comment>
+ <comment xml:lang="ja">メールボックスファイル</comment>
+ <comment xml:lang="kk">пошта жәшігінің файлы</comment>
+ <comment xml:lang="ko">메일함 파일</comment>
+ <comment xml:lang="lt">pašto dėžutės failas</comment>
+ <comment xml:lang="lv">pastkastītes datne</comment>
+ <comment xml:lang="nb">postboksfil</comment>
+ <comment xml:lang="nl">mailbox-bestand</comment>
+ <comment xml:lang="nn">mailbox-fil</comment>
+ <comment xml:lang="oc">fichièr bóstia de letras</comment>
+ <comment xml:lang="pl">Plik poczty (Mailbox)</comment>
+ <comment xml:lang="pt">ficheiro de caixa de correio</comment>
+ <comment xml:lang="pt_BR">Arquivo de caixa de correio</comment>
+ <comment xml:lang="ro">fișier căsuță poștală</comment>
+ <comment xml:lang="ru">Файл почтового ящика</comment>
+ <comment xml:lang="sk">Súbor mailbox</comment>
+ <comment xml:lang="sl">datoteka poštnega predala</comment>
+ <comment xml:lang="sq">File mailbox</comment>
+ <comment xml:lang="sr">датотека поштанског сандучета</comment>
+ <comment xml:lang="sv">brevlådefil</comment>
+ <comment xml:lang="tr">posta kutusu dosyası</comment>
+ <comment xml:lang="uk">файл поштової скриньки</comment>
+ <comment xml:lang="vi">tập tin hộp thư</comment>
+ <comment xml:lang="zh_CN">邮箱文件</comment>
+ <comment xml:lang="zh_TW">郵箱檔</comment>
+ <generic-icon name="text-x-generic"/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="20">
+ <match value="From " type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mbox"/>
+ </mime-type>
+ <mime-type type="application/metalink+xml">
+ <comment>Metalink file</comment>
+ <comment xml:lang="ar">ملف ميتالنك</comment>
+ <comment xml:lang="ast">Ficheru d'enllaz meta</comment>
+ <comment xml:lang="bg">Изтегляне — Metalink</comment>
+ <comment xml:lang="ca">fitxer Metalink</comment>
+ <comment xml:lang="cs">soubor metalink</comment>
+ <comment xml:lang="da">Metahenvisningsfil</comment>
+ <comment xml:lang="de">Metalink-Datei</comment>
+ <comment xml:lang="el">Αρχείο Metalink</comment>
+ <comment xml:lang="en_GB">Metalink file</comment>
+ <comment xml:lang="eo">Metalink-dosiero</comment>
+ <comment xml:lang="es">archivo de Metalink</comment>
+ <comment xml:lang="eu">Metaestekaren fitxategia</comment>
+ <comment xml:lang="fi">Metalink-tiedosto</comment>
+ <comment xml:lang="fo">Metalink fíla</comment>
+ <comment xml:lang="fr">fichier metalink</comment>
+ <comment xml:lang="ga">comhad Metalink</comment>
+ <comment xml:lang="gl">ficheiro Metalink</comment>
+ <comment xml:lang="he">קובץ Metalink</comment>
+ <comment xml:lang="hr">Datoteka meta poveznice</comment>
+ <comment xml:lang="hu">Metalink fájl</comment>
+ <comment xml:lang="ia">File Metalink</comment>
+ <comment xml:lang="id">berkas Metalink</comment>
+ <comment xml:lang="it">File Metalink</comment>
+ <comment xml:lang="ja">Metalink ファイル</comment>
+ <comment xml:lang="kk">Metalink файлы</comment>
+ <comment xml:lang="ko">Metalink 파일</comment>
+ <comment xml:lang="lt">Metalink failas</comment>
+ <comment xml:lang="lv">Metalink datne</comment>
+ <comment xml:lang="nl">Metalink bestand</comment>
+ <comment xml:lang="oc">fichièr metalink</comment>
+ <comment xml:lang="pl">Plik Metalink</comment>
+ <comment xml:lang="pt">ficheiro Metalink</comment>
+ <comment xml:lang="pt_BR">Arquivo Metalink</comment>
+ <comment xml:lang="ro">Fișier Metalink</comment>
+ <comment xml:lang="ru">Файл Metalink</comment>
+ <comment xml:lang="sk">Súbor Metalink</comment>
+ <comment xml:lang="sl">Datoteka povezave Metalink</comment>
+ <comment xml:lang="sr">датотека метавезе</comment>
+ <comment xml:lang="sv">Metalink-fil</comment>
+ <comment xml:lang="tr">Metalink dosyası</comment>
+ <comment xml:lang="uk">файл метапосилання</comment>
+ <comment xml:lang="zh_CN">Metalink 文件</comment>
+ <comment xml:lang="zh_TW">Metalink 檔案</comment>
+ <sub-class-of type="application/xml"/>
+ <magic priority="50">
+ <match value="&lt;metalink version=&quot;3.0&quot;" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.metalink"/>
+ <root-XML namespaceURI="http://www.metalinker.org/" localName="metalink"/>
+ </mime-type>
+ <mime-type type="application/metalink4+xml">
+ <comment>Metalink file</comment>
+ <comment xml:lang="ar">ملف ميتالنك</comment>
+ <comment xml:lang="ast">Ficheru d'enllaz meta</comment>
+ <comment xml:lang="bg">Изтегляне — Metalink</comment>
+ <comment xml:lang="ca">fitxer Metalink</comment>
+ <comment xml:lang="cs">soubor metalink</comment>
+ <comment xml:lang="da">Metahenvisningsfil</comment>
+ <comment xml:lang="de">Metalink-Datei</comment>
+ <comment xml:lang="el">Αρχείο Metalink</comment>
+ <comment xml:lang="en_GB">Metalink file</comment>
+ <comment xml:lang="eo">Metalink-dosiero</comment>
+ <comment xml:lang="es">archivo de Metalink</comment>
+ <comment xml:lang="eu">Metaestekaren fitxategia</comment>
+ <comment xml:lang="fi">Metalink-tiedosto</comment>
+ <comment xml:lang="fo">Metalink fíla</comment>
+ <comment xml:lang="fr">fichier metalink</comment>
+ <comment xml:lang="ga">comhad Metalink</comment>
+ <comment xml:lang="gl">ficheiro Metalink</comment>
+ <comment xml:lang="he">קובץ Metalink</comment>
+ <comment xml:lang="hr">Datoteka meta poveznice</comment>
+ <comment xml:lang="hu">Metalink fájl</comment>
+ <comment xml:lang="ia">File Metalink</comment>
+ <comment xml:lang="id">berkas Metalink</comment>
+ <comment xml:lang="it">File Metalink</comment>
+ <comment xml:lang="ja">Metalink ファイル</comment>
+ <comment xml:lang="kk">Metalink файлы</comment>
+ <comment xml:lang="ko">Metalink 파일</comment>
+ <comment xml:lang="lt">Metalink failas</comment>
+ <comment xml:lang="lv">Metalink datne</comment>
+ <comment xml:lang="nl">Metalink bestand</comment>
+ <comment xml:lang="oc">fichièr metalink</comment>
+ <comment xml:lang="pl">Plik Metalink</comment>
+ <comment xml:lang="pt">ficheiro Metalink</comment>
+ <comment xml:lang="pt_BR">Arquivo Metalink</comment>
+ <comment xml:lang="ro">Fișier Metalink</comment>
+ <comment xml:lang="ru">Файл Metalink</comment>
+ <comment xml:lang="sk">Súbor Metalink</comment>
+ <comment xml:lang="sl">Datoteka povezave Metalink</comment>
+ <comment xml:lang="sr">датотека метавезе</comment>
+ <comment xml:lang="sv">Metalink-fil</comment>
+ <comment xml:lang="tr">Metalink dosyası</comment>
+ <comment xml:lang="uk">файл метапосилання</comment>
+ <comment xml:lang="zh_CN">Metalink 文件</comment>
+ <comment xml:lang="zh_TW">Metalink 檔案</comment>
+ <sub-class-of type="application/xml"/>
+ <magic priority="50">
+ <match value="&lt;metalink xmlns=&quot;urn" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.meta4"/>
+ <root-XML namespaceURI="urn:ietf:params:xml:ns:metalink" localName="metalink"/>
+ </mime-type>
+ <mime-type type="application/octet-stream">
+ <comment>unknown</comment>
+ <comment xml:lang="ar">مجهول</comment>
+ <comment xml:lang="ast">desconozse</comment>
+ <comment xml:lang="be@latin">nieviadomy</comment>
+ <comment xml:lang="bg">Неизвестен тип</comment>
+ <comment xml:lang="ca">desconegut</comment>
+ <comment xml:lang="cs">neznámý</comment>
+ <comment xml:lang="da">ukendt</comment>
+ <comment xml:lang="de">unbekannt</comment>
+ <comment xml:lang="el">Άγνωστο</comment>
+ <comment xml:lang="en_GB">unknown</comment>
+ <comment xml:lang="eo">nekonate</comment>
+ <comment xml:lang="es">desconocido</comment>
+ <comment xml:lang="eu">ezezaguna</comment>
+ <comment xml:lang="fi">tuntematon</comment>
+ <comment xml:lang="fo">ókent</comment>
+ <comment xml:lang="fr">inconnu</comment>
+ <comment xml:lang="ga">anaithnid</comment>
+ <comment xml:lang="gl">descoñecido</comment>
+ <comment xml:lang="he">לא ידוע</comment>
+ <comment xml:lang="hr">Nepoznato</comment>
+ <comment xml:lang="hu">ismeretlen</comment>
+ <comment xml:lang="ia">incognite</comment>
+ <comment xml:lang="id">tak diketahui</comment>
+ <comment xml:lang="it">Sconosciuto</comment>
+ <comment xml:lang="ja">不明</comment>
+ <comment xml:lang="ka">უცნობი</comment>
+ <comment xml:lang="kk">белгісіз</comment>
+ <comment xml:lang="ko">알 수 없음</comment>
+ <comment xml:lang="lt">nežinoma</comment>
+ <comment xml:lang="lv">nezināms</comment>
+ <comment xml:lang="ms">Entah</comment>
+ <comment xml:lang="nb">ukjent</comment>
+ <comment xml:lang="nl">onbekend</comment>
+ <comment xml:lang="nn">ukjend</comment>
+ <comment xml:lang="oc">desconegut</comment>
+ <comment xml:lang="pl">Nieznany typ</comment>
+ <comment xml:lang="pt">desconhecido</comment>
+ <comment xml:lang="pt_BR">Desconhecido</comment>
+ <comment xml:lang="ro">necunoscut</comment>
+ <comment xml:lang="ru">Неизвестно</comment>
+ <comment xml:lang="sk">Neznámy</comment>
+ <comment xml:lang="sl">neznano</comment>
+ <comment xml:lang="sq">Nuk njihet</comment>
+ <comment xml:lang="sr">непознато</comment>
+ <comment xml:lang="sv">okänd</comment>
+ <comment xml:lang="tr">bilinmeyen</comment>
+ <comment xml:lang="uk">невідомо</comment>
+ <comment xml:lang="vi">không rõ</comment>
+ <comment xml:lang="zh_CN">未知</comment>
+ <comment xml:lang="zh_TW">不明</comment>
+ </mime-type>
+ <mime-type type="application/x-partial-download">
+ <comment>Partially downloaded file</comment>
+ <comment xml:lang="ast">Ficheru baxáu parcialmente</comment>
+ <comment xml:lang="ca">fitxer baixat parcialment</comment>
+ <comment xml:lang="cs">částečně stažený soubor</comment>
+ <comment xml:lang="da">Delvist hentet fil</comment>
+ <comment xml:lang="de">Teilweise heruntergeladene Datei</comment>
+ <comment xml:lang="el">Μερικώς ληφθέντο αρχείο</comment>
+ <comment xml:lang="en_GB">Partially downloaded file</comment>
+ <comment xml:lang="es">archivo descargado parcialmente</comment>
+ <comment xml:lang="eu">Partzialki deskargatutako fitxategia</comment>
+ <comment xml:lang="fi">Osittain ladattu tiedosto</comment>
+ <comment xml:lang="fr">fichier partiellement téléchargé</comment>
+ <comment xml:lang="ga">Comhad leath-íoslódáilte</comment>
+ <comment xml:lang="gl">Ficheiro descargado parcialmente</comment>
+ <comment xml:lang="he">קובץ שהתקבל חלקית</comment>
+ <comment xml:lang="hr">Djelomično preuzeta datoteka</comment>
+ <comment xml:lang="hu">Részben letöltött fájl</comment>
+ <comment xml:lang="ia">File partialmente discargate</comment>
+ <comment xml:lang="id">Berkas yang terunduh sebagian</comment>
+ <comment xml:lang="it">File parzialmente scaricato</comment>
+ <comment xml:lang="kk">Жартылай жүктелген файл</comment>
+ <comment xml:lang="ko">일부 다운로드한 파일</comment>
+ <comment xml:lang="oc">fichièr parcialament telecargat</comment>
+ <comment xml:lang="pl">Częściowo pobrany plik</comment>
+ <comment xml:lang="pt">Ficheiro parcialmente transferido</comment>
+ <comment xml:lang="pt_BR">Arquivo baixado parcialmente</comment>
+ <comment xml:lang="ru">Частично загруженный файл</comment>
+ <comment xml:lang="sk">Čiastočne stiahnutý súbor</comment>
+ <comment xml:lang="sl">Delno prenesena datoteka</comment>
+ <comment xml:lang="sr">делимично преузета датотека</comment>
+ <comment xml:lang="sv">Delvis hämtad fil</comment>
+ <comment xml:lang="tr">Kısmen indirilmiş dosya</comment>
+ <comment xml:lang="uk">частково отриманий файл</comment>
+ <comment xml:lang="zh_CN">部分下载的文件</comment>
+ <comment xml:lang="zh_TW">已部份下載的檔案</comment>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.wkdownload"/>
+ <glob pattern="*.crdownload"/>
+ <glob pattern="*.part"/>
+ </mime-type>
+ <mime-type type="application/oda">
+ <comment>ODA document</comment>
+ <comment xml:lang="ar">مستند ODA</comment>
+ <comment xml:lang="ast">Documentu ODA</comment>
+ <comment xml:lang="az">ODA sənədi</comment>
+ <comment xml:lang="be@latin">Dakument ODA</comment>
+ <comment xml:lang="bg">Документ — ODA</comment>
+ <comment xml:lang="ca">document ODA</comment>
+ <comment xml:lang="cs">dokument ODA</comment>
+ <comment xml:lang="cy">Dogfen ODA</comment>
+ <comment xml:lang="da">ODA-dokument</comment>
+ <comment xml:lang="de">ODA-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο ODA</comment>
+ <comment xml:lang="en_GB">ODA document</comment>
+ <comment xml:lang="eo">ODA-dokumento</comment>
+ <comment xml:lang="es">documento ODA</comment>
+ <comment xml:lang="eu">ODA dokumentua</comment>
+ <comment xml:lang="fi">ODA-asiakirja</comment>
+ <comment xml:lang="fo">ODA skjal</comment>
+ <comment xml:lang="fr">document ODA</comment>
+ <comment xml:lang="ga">cáipéis ODA</comment>
+ <comment xml:lang="gl">documento ODA</comment>
+ <comment xml:lang="he">מסמך ODA</comment>
+ <comment xml:lang="hr">ODA dokument</comment>
+ <comment xml:lang="hu">ODA-dokumentum</comment>
+ <comment xml:lang="ia">Documento ODA</comment>
+ <comment xml:lang="id">Dokumen ODA</comment>
+ <comment xml:lang="it">Documento ODA</comment>
+ <comment xml:lang="ja">ODA ドキュメント</comment>
+ <comment xml:lang="ka">ODA დოკუმენტი</comment>
+ <comment xml:lang="kk">ODA құжаты</comment>
+ <comment xml:lang="ko">ODA 문서</comment>
+ <comment xml:lang="lt">ODA dokumentas</comment>
+ <comment xml:lang="lv">ODA dokuments</comment>
+ <comment xml:lang="ms">Dokumen ODA</comment>
+ <comment xml:lang="nb">ODA-dokument</comment>
+ <comment xml:lang="nl">ODA-document</comment>
+ <comment xml:lang="nn">ODA-dokument</comment>
+ <comment xml:lang="oc">document ODA</comment>
+ <comment xml:lang="pl">Dokument ODA</comment>
+ <comment xml:lang="pt">documento ODA</comment>
+ <comment xml:lang="pt_BR">Documento ODA</comment>
+ <comment xml:lang="ro">Document ODA</comment>
+ <comment xml:lang="ru">Документ ODA</comment>
+ <comment xml:lang="sk">Dokument ODA</comment>
+ <comment xml:lang="sl">Dokument ODA</comment>
+ <comment xml:lang="sq">Dokument ODA</comment>
+ <comment xml:lang="sr">ОДА документ</comment>
+ <comment xml:lang="sv">ODA-dokument</comment>
+ <comment xml:lang="tr">ODA belgesi</comment>
+ <comment xml:lang="uk">документ ODA</comment>
+ <comment xml:lang="vi">Tài liệu ODA</comment>
+ <comment xml:lang="zh_CN">ODA 文档</comment>
+ <comment xml:lang="zh_TW">ODA 文件</comment>
+ <acronym>ODA</acronym>
+ <expanded-acronym>Office Document Architecture</expanded-acronym>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.oda"/>
+ </mime-type>
+ <mime-type type="application/x-wwf">
+ <comment>WWF document</comment>
+ <comment xml:lang="ast">Documentu WWF</comment>
+ <comment xml:lang="bg">Документ — WWF</comment>
+ <comment xml:lang="ca">document WWF</comment>
+ <comment xml:lang="cs">dokument WWF</comment>
+ <comment xml:lang="da">WWF-dokument</comment>
+ <comment xml:lang="de">WWF-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο WWF</comment>
+ <comment xml:lang="en_GB">WWF document</comment>
+ <comment xml:lang="eo">WWF-dokumento</comment>
+ <comment xml:lang="es">documento WWF</comment>
+ <comment xml:lang="eu">WWF dokumentua</comment>
+ <comment xml:lang="fi">WWF-asiakirja</comment>
+ <comment xml:lang="fr">document WWF</comment>
+ <comment xml:lang="ga">cáipéis WWF</comment>
+ <comment xml:lang="gl">documento de WWF</comment>
+ <comment xml:lang="he">מסמך WWF</comment>
+ <comment xml:lang="hr">WWF dokument</comment>
+ <comment xml:lang="hu">WWF-dokumentum</comment>
+ <comment xml:lang="ia">Documento WWF</comment>
+ <comment xml:lang="id">Dokumen WWF</comment>
+ <comment xml:lang="it">Documento WWF</comment>
+ <comment xml:lang="ja">WWF 文書</comment>
+ <comment xml:lang="ka">WWF დოკუმენტი</comment>
+ <comment xml:lang="kk">WWF құжаты</comment>
+ <comment xml:lang="ko">WWF 문서</comment>
+ <comment xml:lang="lv">WWF dokuments</comment>
+ <comment xml:lang="nl">WWF document</comment>
+ <comment xml:lang="oc">document WWF</comment>
+ <comment xml:lang="pl">Dokument WWF</comment>
+ <comment xml:lang="pt">documento WWF</comment>
+ <comment xml:lang="pt_BR">Documento WWF</comment>
+ <comment xml:lang="ru">Документ WWF</comment>
+ <comment xml:lang="sk">Dokument WWF</comment>
+ <comment xml:lang="sl">Dokument WWF</comment>
+ <comment xml:lang="sr">ВВФ документ</comment>
+ <comment xml:lang="sv">WWF-dokument</comment>
+ <comment xml:lang="tr">WWF belgesi</comment>
+ <comment xml:lang="uk">документ WWF</comment>
+ <comment xml:lang="zh_CN">WWF</comment>
+ <comment xml:lang="zh_TW">WWF 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.wwf"/>
+ <sub-class-of type="application/pdf"/>
+ <alias type="application/wwf"/>
+ </mime-type>
+ <mime-type type="application/pdf">
+ <comment>PDF document</comment>
+ <comment xml:lang="ar">مستند PDF</comment>
+ <comment xml:lang="ast">Documentu PDF</comment>
+ <comment xml:lang="be@latin">Dakument PDF</comment>
+ <comment xml:lang="bg">Документ — PDF</comment>
+ <comment xml:lang="ca">document PDF</comment>
+ <comment xml:lang="cs">dokument PDF</comment>
+ <comment xml:lang="cy">Dogfen PDF</comment>
+ <comment xml:lang="da">PDF-dokument</comment>
+ <comment xml:lang="de">PDF-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο PDF</comment>
+ <comment xml:lang="en_GB">PDF document</comment>
+ <comment xml:lang="eo">PDF-dokumento</comment>
+ <comment xml:lang="es">documento PDF</comment>
+ <comment xml:lang="eu">PDF dokumentua</comment>
+ <comment xml:lang="fi">PDF-asiakirja</comment>
+ <comment xml:lang="fo">PDF skjal</comment>
+ <comment xml:lang="fr">document PDF</comment>
+ <comment xml:lang="ga">cáipéis PDF</comment>
+ <comment xml:lang="gl">documento PDF</comment>
+ <comment xml:lang="he">מסמך PDF</comment>
+ <comment xml:lang="hr">PDF dokument</comment>
+ <comment xml:lang="hu">PDF-dokumentum</comment>
+ <comment xml:lang="ia">Documento PDF</comment>
+ <comment xml:lang="id">Dokumen PDF</comment>
+ <comment xml:lang="it">Documento PDF</comment>
+ <comment xml:lang="ja">PDF ドキュメント</comment>
+ <comment xml:lang="kk">PDF құжаты</comment>
+ <comment xml:lang="ko">PDF 문서</comment>
+ <comment xml:lang="lt">PDF dokumentas</comment>
+ <comment xml:lang="lv">PDF dokuments</comment>
+ <comment xml:lang="ms">Dokumen PDF</comment>
+ <comment xml:lang="nb">PDF-dokument</comment>
+ <comment xml:lang="nl">PDF-document</comment>
+ <comment xml:lang="nn">PDF-dokument</comment>
+ <comment xml:lang="oc">document PDF</comment>
+ <comment xml:lang="pl">Dokument PDF</comment>
+ <comment xml:lang="pt">documento PDF</comment>
+ <comment xml:lang="pt_BR">Documento PDF</comment>
+ <comment xml:lang="ro">Document PDF</comment>
+ <comment xml:lang="ru">Документ PDF</comment>
+ <comment xml:lang="sk">Dokument PDF</comment>
+ <comment xml:lang="sl">Dokument PDF</comment>
+ <comment xml:lang="sq">Dokument PDF</comment>
+ <comment xml:lang="sr">ПДФ документ</comment>
+ <comment xml:lang="sv">PDF-dokument</comment>
+ <comment xml:lang="tr">PDF belgesi</comment>
+ <comment xml:lang="uk">документ PDF</comment>
+ <comment xml:lang="vi">Tài liệu PDF</comment>
+ <comment xml:lang="zh_CN">PDF 文档</comment>
+ <comment xml:lang="zh_TW">PDF 文件</comment>
+ <acronym>PDF</acronym>
+ <expanded-acronym>Portable Document Format</expanded-acronym>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="%PDF-" type="string" offset="0:1024"/>
+ </magic>
+ <glob pattern="*.pdf"/>
+ <alias type="application/x-pdf"/>
+ <alias type="image/pdf"/>
+ <alias type="application/acrobat"/>
+ <alias type="application/nappdf"/>
+ </mime-type>
+ <mime-type type="application/xspf+xml">
+ <comment>XSPF playlist</comment>
+ <comment xml:lang="ar">قائمة تشغيل XSPF</comment>
+ <comment xml:lang="ast">Llista de reproducción XSPF</comment>
+ <comment xml:lang="be@latin">Śpis piesień XSPF</comment>
+ <comment xml:lang="bg">Списък за изпълнение — XSPF</comment>
+ <comment xml:lang="ca">llista de reproducció XSPF</comment>
+ <comment xml:lang="cs">seznam k přehrání XSPF</comment>
+ <comment xml:lang="da">XSPF-afspilningsliste</comment>
+ <comment xml:lang="de">XSPF-Wiedergabeliste</comment>
+ <comment xml:lang="el">Λίστα αναπαραγωγής XSPF</comment>
+ <comment xml:lang="en_GB">XSPF playlist</comment>
+ <comment xml:lang="eo">XSPF-ludlisto</comment>
+ <comment xml:lang="es">lista de reproducción XSPF</comment>
+ <comment xml:lang="eu">XSPF erreprodukzio-zerrenda</comment>
+ <comment xml:lang="fi">XSPF-soittolista</comment>
+ <comment xml:lang="fo">XSPF avspælingarlisti</comment>
+ <comment xml:lang="fr">liste de lecture XSPF</comment>
+ <comment xml:lang="ga">seinmliosta XSPF</comment>
+ <comment xml:lang="gl">lista de reprodución XSPF</comment>
+ <comment xml:lang="he">רשימת נגינה XSPF</comment>
+ <comment xml:lang="hr">XSPF popis izvođenja</comment>
+ <comment xml:lang="hu">XSPF-lejátszólista</comment>
+ <comment xml:lang="ia">Lista de selection XSPF</comment>
+ <comment xml:lang="id">Senarai pular XSPF</comment>
+ <comment xml:lang="it">Playlist XSPF</comment>
+ <comment xml:lang="ja">XSPF 再生リスト</comment>
+ <comment xml:lang="kk">XSPF ойнау тізімі</comment>
+ <comment xml:lang="ko">XSPF 재생 목록</comment>
+ <comment xml:lang="lt">XSPF grojaraštis</comment>
+ <comment xml:lang="lv">XSPF repertuārs</comment>
+ <comment xml:lang="nb">XSPF-spilleliste</comment>
+ <comment xml:lang="nl">XSPF-afspeellijst</comment>
+ <comment xml:lang="nn">XSPF-speleliste</comment>
+ <comment xml:lang="oc">lista de lectura XSPF</comment>
+ <comment xml:lang="pl">Lista odtwarzania XSPF</comment>
+ <comment xml:lang="pt">lista de reprodução XSPF</comment>
+ <comment xml:lang="pt_BR">Lista de reprodução XSPF</comment>
+ <comment xml:lang="ro">Listă XSPF</comment>
+ <comment xml:lang="ru">Список воспроизведения XSPF</comment>
+ <comment xml:lang="sk">Zoznam skladieb XSPF</comment>
+ <comment xml:lang="sl">Seznam predvajanja XSPF</comment>
+ <comment xml:lang="sq">Listë titujsh XSPF</comment>
+ <comment xml:lang="sr">ИксСПФ списак нумера</comment>
+ <comment xml:lang="sv">XSPF-spellista</comment>
+ <comment xml:lang="tr">XSPF çalma listesi</comment>
+ <comment xml:lang="uk">список програвання XSPF</comment>
+ <comment xml:lang="vi">Danh mục nhạc XSPF</comment>
+ <comment xml:lang="zh_CN">XSPF 播放列表</comment>
+ <comment xml:lang="zh_TW">XSPF 播放清單</comment>
+ <acronym>XSPF</acronym>
+ <expanded-acronym>XML Shareable Playlist Format</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="audio-x-generic"/>
+ <magic priority="50">
+ <match value="&lt;playlist version=&quot;1" type="string" offset="0:64"/>
+ <match value="&lt;playlist version='1" type="string" offset="0:64"/>
+ </magic>
+ <glob pattern="*.xspf"/>
+ <root-XML namespaceURI="http://xspf.org/ns/0/" localName="playlist"/>
+ <alias type="application/x-xspf+xml"/>
+ </mime-type>
+ <mime-type type="application/x-windows-themepack">
+ <comment>Microsoft Windows theme pack</comment>
+ <comment xml:lang="ar">حزمة سمات Microsoft Works</comment>
+ <comment xml:lang="ast">Paquete de temes de Microsoft Windows</comment>
+ <comment xml:lang="bg">Пакет с тема — Microsoft Windows</comment>
+ <comment xml:lang="ca">paquet de temes de Microsoft Windows</comment>
+ <comment xml:lang="cs">balík motivů Microsoft Windows</comment>
+ <comment xml:lang="da">Microsoft Windows-temapakke</comment>
+ <comment xml:lang="de">Microsoft-Windows-Themenpaket</comment>
+ <comment xml:lang="el">Πακέτο θέματος Microsoft Windows</comment>
+ <comment xml:lang="en_GB">Microsoft Windows theme pack</comment>
+ <comment xml:lang="es">paquete de tema para Microsoft Windows</comment>
+ <comment xml:lang="eu">Microsoft Windows-en gaiaren paketea</comment>
+ <comment xml:lang="fi">Microsoft Windows -teemapaketti</comment>
+ <comment xml:lang="fo">Microsoft Windows tema pakki</comment>
+ <comment xml:lang="fr">paquet de thèmes Microsoft Windows</comment>
+ <comment xml:lang="ga">paca téamaí Microsoft Windows</comment>
+ <comment xml:lang="gl">paquete de tema de Microsoft Windows</comment>
+ <comment xml:lang="he">חבילת ערכות נושא של Microsoft Windows</comment>
+ <comment xml:lang="hr">Microsoft Windows paket tema</comment>
+ <comment xml:lang="hu">Microsoft Windows témacsomag</comment>
+ <comment xml:lang="ia">Pacchetto de themas Microsoft Windows</comment>
+ <comment xml:lang="id">Pak tema Microsoft Windows</comment>
+ <comment xml:lang="it">Pacchetto temi Microsoft Windows</comment>
+ <comment xml:lang="ja">Microsoft Windows テーマパック</comment>
+ <comment xml:lang="ka">Microsoft Windows-ის თემის შეკვრა</comment>
+ <comment xml:lang="kk">Microsoft Windows тема дестесі</comment>
+ <comment xml:lang="ko">Microsoft Windows 테마 패키지</comment>
+ <comment xml:lang="lt">Microsoft Windows temų paketas</comment>
+ <comment xml:lang="lv">Microsoft Windows motīvu paka</comment>
+ <comment xml:lang="nl">Microsoft Windows thema pack</comment>
+ <comment xml:lang="oc">paquet de tèmas Microsoft Windows</comment>
+ <comment xml:lang="pl">Pakiet motywu Microsoft Windows</comment>
+ <comment xml:lang="pt">pacote de tema Microsoft Windows</comment>
+ <comment xml:lang="pt_BR">Pacote de temas do Microsoft Windows</comment>
+ <comment xml:lang="ro">Pachet de teme Microsoft Windows</comment>
+ <comment xml:lang="ru">Пакет темы Microsoft Windows</comment>
+ <comment xml:lang="sk">Balík tém Microsoft Windows</comment>
+ <comment xml:lang="sl">Datoteka teme Microsoft Windows</comment>
+ <comment xml:lang="sr">пакет теме Мајкрософт Виндоуза</comment>
+ <comment xml:lang="sv">Microsoft Windows-temapaket</comment>
+ <comment xml:lang="tr">Microsoft Windows tema paketi</comment>
+ <comment xml:lang="uk">пакунок з темою Microsoft Windows</comment>
+ <comment xml:lang="zh_CN">Microsoft Windows 主题包</comment>
+ <comment xml:lang="zh_TW">微軟視窗佈景主題包</comment>
+ <sub-class-of type="application/vnd.ms-cab-compressed"/>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.themepack"/>
+ </mime-type>
+ <mime-type type="audio/x-amzxml">
+ <comment>AmazonMP3 download file</comment>
+ <comment xml:lang="ast">Ficheru de descarga AmazonMP3</comment>
+ <comment xml:lang="ca">fitxer baixat d'AmazonMP3</comment>
+ <comment xml:lang="cs">soubor stahování AmazonMP3</comment>
+ <comment xml:lang="da">AmazonMP3-downloadfil</comment>
+ <comment xml:lang="de">AmazonMP3-Herunterladedatei</comment>
+ <comment xml:lang="el">Αρχείο λήψης AmazonMP3</comment>
+ <comment xml:lang="en_GB">AmazonMP3 download file</comment>
+ <comment xml:lang="es">archivo de descarga de AmazonMP3</comment>
+ <comment xml:lang="eu">AmazonMP3 deskarga fitxategia</comment>
+ <comment xml:lang="fr">fichier téléchargé AmazonMP3</comment>
+ <comment xml:lang="ga">comhad íoslódáilte AmazonMP3</comment>
+ <comment xml:lang="gl">Ficheiro de descarga de AmazonMP3</comment>
+ <comment xml:lang="he">קובץ הורדת AmazonMP3</comment>
+ <comment xml:lang="hr">AmazonMP3 preuzeta datoteka</comment>
+ <comment xml:lang="hu">AmazonMP3 letöltésfájl</comment>
+ <comment xml:lang="ia">File de discargamento AmazonMP3</comment>
+ <comment xml:lang="id">Berkas unduh AmazonMP3</comment>
+ <comment xml:lang="it">File scaricamento AmazonMP3</comment>
+ <comment xml:lang="ja">AmazonMP3 ダウンロードファイル</comment>
+ <comment xml:lang="kk">AmazonMP3 жүктеме файлы</comment>
+ <comment xml:lang="ko">AmazonMP3 다운로드 파일</comment>
+ <comment xml:lang="lv">AmazonMP3 lejupielādes datne</comment>
+ <comment xml:lang="oc">fichièr telecargat AmazonMP3</comment>
+ <comment xml:lang="pl">Pobrany plik AmazonMP3</comment>
+ <comment xml:lang="pt">ficheiro transferido AmazonMP3</comment>
+ <comment xml:lang="pt_BR">Arquivo de download AmazonMP3</comment>
+ <comment xml:lang="ru">Файл загрузки AmazonMP3</comment>
+ <comment xml:lang="sk">Stiahnutý súbor AmazonMP3 </comment>
+ <comment xml:lang="sl">Datoteka prenosa AmazonMP3</comment>
+ <comment xml:lang="sr">датотека преузимања АмазонаМП3</comment>
+ <comment xml:lang="sv">AmazonMP3-hämtningsfil</comment>
+ <comment xml:lang="tr">AmazonMP3 indirme dosyası</comment>
+ <comment xml:lang="uk">файл завантаження AmazonMP3</comment>
+ <comment xml:lang="zh_CN">AmazonMP3 下载文件</comment>
+ <comment xml:lang="zh_TW">AmazonMP3 下載檔</comment>
+ <glob pattern="*.amz"/>
+ </mime-type>
+ <mime-type type="audio/x-gsm">
+ <comment>GSM 06.10 audio</comment>
+ <comment xml:lang="ar">GSM 06.10 سمعي</comment>
+ <comment xml:lang="ast">Audiu GSM 6.10</comment>
+ <comment xml:lang="bg">Аудио — GSM 06.10</comment>
+ <comment xml:lang="ca">àudio de GSM 06.10</comment>
+ <comment xml:lang="cs">zvuk GSM 06.10</comment>
+ <comment xml:lang="da">GSM 06.10-lyd</comment>
+ <comment xml:lang="de">GSM-06.10-Audio</comment>
+ <comment xml:lang="el">Ήχος GSM 06.10</comment>
+ <comment xml:lang="en_GB">GSM 06.10 audio</comment>
+ <comment xml:lang="es">sonido GSM 06.10</comment>
+ <comment xml:lang="eu">GSM 06.10 audioa</comment>
+ <comment xml:lang="fi">GSM 06.10 -ääni</comment>
+ <comment xml:lang="fo">GSM 06.10 ljóður</comment>
+ <comment xml:lang="fr">audio GSM 06.10</comment>
+ <comment xml:lang="ga">fuaim GSM 06.10</comment>
+ <comment xml:lang="gl">son de GSM 06.10</comment>
+ <comment xml:lang="he">שמע GSM 06.10</comment>
+ <comment xml:lang="hr">GSM 06.10 zvučni zapis</comment>
+ <comment xml:lang="hu">GSM 06.10 hang</comment>
+ <comment xml:lang="ia">Audio GSM 06.10</comment>
+ <comment xml:lang="id">Audio GSM 06.10</comment>
+ <comment xml:lang="it">Audio GSM 06.10</comment>
+ <comment xml:lang="ja">GSM 06.10 オーディオ</comment>
+ <comment xml:lang="ka">GSM 06.10 აუდიო</comment>
+ <comment xml:lang="kk">GSM 06.10 аудиосы</comment>
+ <comment xml:lang="ko">GSM 06.10 오디오</comment>
+ <comment xml:lang="lt">GSM 06.10 garso įrašas</comment>
+ <comment xml:lang="lv">GSM 06.10 audio</comment>
+ <comment xml:lang="nl">GSM 06.10 audio</comment>
+ <comment xml:lang="oc">àudio GSM 06.10</comment>
+ <comment xml:lang="pl">Plik dźwiękowy GSM 06.10</comment>
+ <comment xml:lang="pt">áudio GSM 06.10</comment>
+ <comment xml:lang="pt_BR">Áudio GSM 06.10</comment>
+ <comment xml:lang="ro">GSM 06.10 audio</comment>
+ <comment xml:lang="ru">Аудио GSM 06.10</comment>
+ <comment xml:lang="sk">Zvuk GSM 06.10</comment>
+ <comment xml:lang="sl">Zvočna datoteka GSM 06.10</comment>
+ <comment xml:lang="sr">ГСМ 06.10 звук</comment>
+ <comment xml:lang="sv">GSM 06.10-ljud</comment>
+ <comment xml:lang="tr">GSM 06.10 ses dosyası</comment>
+ <comment xml:lang="uk">звук GSM 06.10</comment>
+ <comment xml:lang="vi">Âm thanh GSM 06.10</comment>
+ <comment xml:lang="zh_CN">GSM 06.10 音频</comment>
+ <comment xml:lang="zh_TW">GSM 06.10 音訊</comment>
+ <acronym>GSM</acronym>
+ <expanded-acronym>Global System for Mobile communications</expanded-acronym>
+ <glob pattern="*.gsm"/>
+ </mime-type>
+ <mime-type type="audio/x-iriver-pla">
+ <comment>iRiver Playlist</comment>
+ <comment xml:lang="ar">قائمة تشغيل iRiver</comment>
+ <comment xml:lang="ast">Llista de reproducción iRiver</comment>
+ <comment xml:lang="be@latin">Śpis piesień iRiver</comment>
+ <comment xml:lang="bg">Списък за изпълнение — iRiver</comment>
+ <comment xml:lang="ca">llista de reproducció iRiver</comment>
+ <comment xml:lang="cs">seznam k přehrání iRiver</comment>
+ <comment xml:lang="da">iRiver-afspilningsliste</comment>
+ <comment xml:lang="de">iRiver-Wiedergabeliste</comment>
+ <comment xml:lang="el">Λίστα αναπαραγωγής iRiver</comment>
+ <comment xml:lang="en_GB">iRiver Playlist</comment>
+ <comment xml:lang="eo">iRiver-ludlisto</comment>
+ <comment xml:lang="es">lista de reproducción de iRiver</comment>
+ <comment xml:lang="eu">iRiver erreprodukzio-zerrenda</comment>
+ <comment xml:lang="fi">iRiver-soittolista</comment>
+ <comment xml:lang="fo">iRiver avspælingarlisti</comment>
+ <comment xml:lang="fr">liste de lecture iRiver</comment>
+ <comment xml:lang="ga">seinmliosta iRiver</comment>
+ <comment xml:lang="gl">lista de reprodución de iRiver</comment>
+ <comment xml:lang="he">רשימת נגינה של iRiver</comment>
+ <comment xml:lang="hr">iRiver popis izvođenja</comment>
+ <comment xml:lang="hu">iRiver lejátszólista</comment>
+ <comment xml:lang="ia">Lista de selection iRiver</comment>
+ <comment xml:lang="id">iRiver Playlist</comment>
+ <comment xml:lang="it">Playlist iRiver</comment>
+ <comment xml:lang="ja">iRiver 再生リスト</comment>
+ <comment xml:lang="kk">iRiver ойнау тізімі</comment>
+ <comment xml:lang="ko">iRiver 재생 목록</comment>
+ <comment xml:lang="lt">iRiver grojaraštis</comment>
+ <comment xml:lang="lv">iRiver repertuārs</comment>
+ <comment xml:lang="nb">iRiver-spilleliste</comment>
+ <comment xml:lang="nl">iRiver-afspeellijst</comment>
+ <comment xml:lang="nn">iRiver speleliste</comment>
+ <comment xml:lang="oc">lista de lectura iRiver</comment>
+ <comment xml:lang="pl">Lista odtwarzania iRiver</comment>
+ <comment xml:lang="pt">lista de reprodução iRiver</comment>
+ <comment xml:lang="pt_BR">Lista de reprodução do iRiver</comment>
+ <comment xml:lang="ro">Listă iRiver</comment>
+ <comment xml:lang="ru">Список воспроизведения iRiver</comment>
+ <comment xml:lang="sk">Zoznam skladieb iRiver</comment>
+ <comment xml:lang="sl">Seznam predvajanja iRiver</comment>
+ <comment xml:lang="sq">Listë titujsh iRiver</comment>
+ <comment xml:lang="sr">иРивер списак нумера</comment>
+ <comment xml:lang="sv">iRiver-spellista</comment>
+ <comment xml:lang="tr">iRiver Çalma Listesini</comment>
+ <comment xml:lang="uk">список програвання iRiver</comment>
+ <comment xml:lang="vi">danh mục nhạc iRiver</comment>
+ <comment xml:lang="zh_CN">iRiver 播放列表</comment>
+ <comment xml:lang="zh_TW">iRiver 播放清單</comment>
+ <magic priority="50">
+ <match value="iriver UMS PLA" type="string" offset="4"/>
+ </magic>
+ <glob pattern="*.pla"/>
+ </mime-type>
+ <mime-type type="application/pgp-encrypted">
+ <comment>PGP/MIME-encrypted message header</comment>
+ <comment xml:lang="ar">ترويسة رسالة PGP/MIME-مشفرة</comment>
+ <comment xml:lang="ast">Testera de mensaxe cifrada en PGP/MIME</comment>
+ <comment xml:lang="be@latin">Zahałovak paviedamleńnia, zašyfravany ŭ PGP/MIME</comment>
+ <comment xml:lang="bg">Заглавна част на шифрирано съобщение — PGP/MIME</comment>
+ <comment xml:lang="ca">capçalera de missatge amb xifrat PGP/MIME</comment>
+ <comment xml:lang="cs">záhlaví zprávy zašifrované pomocí PGP/MIME</comment>
+ <comment xml:lang="da">PGP-/MIME-krypteret meddelelseshoved</comment>
+ <comment xml:lang="de">PGP/MIME-verschlüsselter Nachrichtenkopf</comment>
+ <comment xml:lang="el">Κεφαλίδα μηνύματος κρυπτογραφημένου κατά PGP/MIME</comment>
+ <comment xml:lang="en_GB">PGP/MIME-encrypted message header</comment>
+ <comment xml:lang="eo">PGP/MIME-ĉifrita ĉapo de mesaĝo</comment>
+ <comment xml:lang="es">cabecera de mensaje cifrado PGP/MIME</comment>
+ <comment xml:lang="eu">PGP/MIME enkriptatutako mezu-goiburua</comment>
+ <comment xml:lang="fi">PGP/MIME-salattu viestiotsikko</comment>
+ <comment xml:lang="fo">PGP/MIME-encrypted boð tekshøvd</comment>
+ <comment xml:lang="fr">en-tête de message codé PGP/MIME</comment>
+ <comment xml:lang="ga">ceanntásc teachtaireachta ionchódaithe le PGP/MIME</comment>
+ <comment xml:lang="gl">cabeceira de mensaxe cifrado PGP/MIME</comment>
+ <comment xml:lang="he">כותר של קובץ מוצפן מסוג PGP/MIME</comment>
+ <comment xml:lang="hr">PGP/MIME-šrifrirano zaglavlje poruke</comment>
+ <comment xml:lang="hu">PGP/MIME titkosított üzenetfejléc</comment>
+ <comment xml:lang="ia">Capite de message cryptate con PGP/MIME</comment>
+ <comment xml:lang="id">Tajuk pesan terenkripsi PGP/MIME</comment>
+ <comment xml:lang="it">Intestazione messaggio PGP/MIME-encrypted</comment>
+ <comment xml:lang="ja">PGP/MIME 暗号化メッセージヘッダー</comment>
+ <comment xml:lang="kk">PGP/MIME-шифрленген мәлімдеме тақырыптамасы</comment>
+ <comment xml:lang="ko">PGP/MIME으로 암호화된 메시지 헤더</comment>
+ <comment xml:lang="lt">PGP/MIME užšifruota žinutės antraštė</comment>
+ <comment xml:lang="lv">PGP/MIME-šifrēta ziņas galvene</comment>
+ <comment xml:lang="ms">Pengepala mesej terenkripsi PGP/MIME</comment>
+ <comment xml:lang="nb">PGP/MIME-kryptert meldingshode</comment>
+ <comment xml:lang="nl">PGP/MIME-versleutelde berichtkopregels</comment>
+ <comment xml:lang="nn">PGP/MIME-kryptert meldingshovud</comment>
+ <comment xml:lang="oc">entèsta de messatge encodat PGP/MIME</comment>
+ <comment xml:lang="pl">Nagłówek listu zaszyfrowanego PGP/MIME</comment>
+ <comment xml:lang="pt">cabeçalho de mensagem encriptada com PGP/MIME</comment>
+ <comment xml:lang="pt_BR">Cabeçalho de mensagem criptografada PGP/MIME</comment>
+ <comment xml:lang="ro">Antet de mesaj encriptat PGP/MIME</comment>
+ <comment xml:lang="ru">Заголовок сообщения, зашифрованный PGP/MIME</comment>
+ <comment xml:lang="sk">Hlavičke správy zašifrovaná pomocou PGP/MIME</comment>
+ <comment xml:lang="sl">Datoteka glave šifriranega sporočila PGP/MIME</comment>
+ <comment xml:lang="sq">Header mesazhi të kriptuar PGP/MIME</comment>
+ <comment xml:lang="sr">ПГП/МИМЕ шифровано заглавље поруке</comment>
+ <comment xml:lang="sv">PGP/MIME-krypterat meddelandehuvud</comment>
+ <comment xml:lang="tr">PGP/MIME-şifreli ileti başlığı</comment>
+ <comment xml:lang="uk">заголовок шифрованого PGP/MIME повідомлення</comment>
+ <comment xml:lang="vi">Phần đầu thông điệp đã mật mã bằng PGP/MIME</comment>
+ <comment xml:lang="zh_CN">PGP/MIME 加密的信件头</comment>
+ <comment xml:lang="zh_TW">PGP/MIME 加密訊息標頭</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <magic priority="50">
+ <match value="-----BEGIN PGP MESSAGE-----" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.pgp"/>
+ <glob pattern="*.gpg"/>
+ <glob weight="10" pattern="*.asc"/>
+ <alias type="application/pgp"/>
+ </mime-type>
+ <mime-type type="application/pgp-keys">
+ <comment>PGP keys</comment>
+ <comment xml:lang="ar">مفاتيح PGP</comment>
+ <comment xml:lang="ast">Claves PGP</comment>
+ <comment xml:lang="az">PGP açarları</comment>
+ <comment xml:lang="be@latin">Klučy PGP</comment>
+ <comment xml:lang="bg">Ключове — PGP</comment>
+ <comment xml:lang="ca">claus PGP</comment>
+ <comment xml:lang="cs">klíče PGP</comment>
+ <comment xml:lang="cy">Allweddi PGP</comment>
+ <comment xml:lang="da">PGP-nøgler</comment>
+ <comment xml:lang="de">PGP-Schlüssel</comment>
+ <comment xml:lang="el">Κλειδιά PGP</comment>
+ <comment xml:lang="en_GB">PGP keys</comment>
+ <comment xml:lang="eo">PGP-ŝlosiloj</comment>
+ <comment xml:lang="es">claves PGP</comment>
+ <comment xml:lang="eu">PGP giltzak</comment>
+ <comment xml:lang="fi">PGP-avainrengas</comment>
+ <comment xml:lang="fo">PGP lyklar</comment>
+ <comment xml:lang="fr">clés PGP</comment>
+ <comment xml:lang="ga">eochracha PGP</comment>
+ <comment xml:lang="gl">Chaves PGP</comment>
+ <comment xml:lang="he">מפתחות PGP</comment>
+ <comment xml:lang="hr">PGP ključevi</comment>
+ <comment xml:lang="hu">PGP-kulcs</comment>
+ <comment xml:lang="ia">Claves PGP</comment>
+ <comment xml:lang="id">Kunci PGP</comment>
+ <comment xml:lang="it">Chiavi PGP</comment>
+ <comment xml:lang="ja">PGP 鍵</comment>
+ <comment xml:lang="kk">PGP кілттері</comment>
+ <comment xml:lang="ko">PGP 키</comment>
+ <comment xml:lang="lt">PGP raktai</comment>
+ <comment xml:lang="lv">PGP atslēgas</comment>
+ <comment xml:lang="ms">Kekunci PGP</comment>
+ <comment xml:lang="nb">PGP-nøkler</comment>
+ <comment xml:lang="nl">PGP-sleutels</comment>
+ <comment xml:lang="nn">PGP-nøkler</comment>
+ <comment xml:lang="oc">claus PGP</comment>
+ <comment xml:lang="pl">Klucze PGP</comment>
+ <comment xml:lang="pt">chaves PGP</comment>
+ <comment xml:lang="pt_BR">Chaves PGP</comment>
+ <comment xml:lang="ro">Chei PGP</comment>
+ <comment xml:lang="ru">Ключи PGP</comment>
+ <comment xml:lang="sk">Kľúče PGP</comment>
+ <comment xml:lang="sl">Datoteka ključa PGP</comment>
+ <comment xml:lang="sq">Kyçe PGP</comment>
+ <comment xml:lang="sr">ПГП кључеви</comment>
+ <comment xml:lang="sv">PGP-nycklar</comment>
+ <comment xml:lang="tr">PGP anahtarları</comment>
+ <comment xml:lang="uk">ключі PGP</comment>
+ <comment xml:lang="vi">Khoá PGP</comment>
+ <comment xml:lang="zh_CN">PGP 密钥</comment>
+ <comment xml:lang="zh_TW">PGP 鑰匙</comment>
+ <acronym>PGP</acronym>
+ <expanded-acronym>Pretty Good Privacy</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <magic priority="50">
+ <match value="-----BEGIN PGP PUBLIC KEY BLOCK-----" type="string" offset="0"/>
+ <match value="-----BEGIN PGP PRIVATE KEY BLOCK-----" type="string" offset="0"/>
+ <match value="0x9501" type="big16" offset="0"/>
+ <match value="0x9500" type="big16" offset="0"/>
+ <match value="0x9900" type="big16" offset="0"/>
+ <match value="0x9901" type="big16" offset="0"/>
+ </magic>
+ <glob pattern="*.skr"/>
+ <glob pattern="*.pkr"/>
+ <glob weight="10" pattern="*.asc"/>
+ <glob pattern="*.pgp"/>
+ <glob pattern="*.gpg"/>
+ </mime-type>
+ <mime-type type="application/pgp-signature">
+ <comment>detached OpenPGP signature</comment>
+ <comment xml:lang="ar">إمضاء OpenPGP مفصول</comment>
+ <comment xml:lang="be@latin">adłučany podpis OpenPGP</comment>
+ <comment xml:lang="bg">Отделен подпис — OpenPGP</comment>
+ <comment xml:lang="ca">signatura OpenPGP abstreta</comment>
+ <comment xml:lang="cs">oddělený podpis OpenPGP</comment>
+ <comment xml:lang="da">frigjort OpenPGP-signatur</comment>
+ <comment xml:lang="de">isolierte OpenPGP-Signatur</comment>
+ <comment xml:lang="el">Αποκομμένη υπογραφή OpenPGP</comment>
+ <comment xml:lang="en_GB">detached OpenPGP signature</comment>
+ <comment xml:lang="eo">dekroĉa OpenPGP-subskribo</comment>
+ <comment xml:lang="es">firma OpenPGP separada</comment>
+ <comment xml:lang="eu">desuzturtako OpenPGP sinadura</comment>
+ <comment xml:lang="fi">erillinen OpenPGP-allekirjoitus</comment>
+ <comment xml:lang="fo">skild OpenPGP undirskrift</comment>
+ <comment xml:lang="fr">signature OpenPGP détachée</comment>
+ <comment xml:lang="ga">síniú OpenPGP scartha</comment>
+ <comment xml:lang="gl">sinatura de OpenPGP independente</comment>
+ <comment xml:lang="he">חתימת OpenPGP מנותקת</comment>
+ <comment xml:lang="hr">Odvojen OpenPGP potpis</comment>
+ <comment xml:lang="hu">leválasztott OpenPGP-aláírás</comment>
+ <comment xml:lang="ia">Signatura OpenPGP distachate</comment>
+ <comment xml:lang="id">tanda tangan OpenPGP yang terlepas</comment>
+ <comment xml:lang="it">Firma staccata OpenPGP</comment>
+ <comment xml:lang="ja">分離 OpenPGP 署名</comment>
+ <comment xml:lang="kk">бөлінген OpenPGP қолтаңбасы</comment>
+ <comment xml:lang="ko">분리된 OpenPGP 서명</comment>
+ <comment xml:lang="lt">neprisegtas OpenPGP parašas</comment>
+ <comment xml:lang="lv">atvienots OpenPGP paraksts</comment>
+ <comment xml:lang="ms">Tandatangan OpenPGP terlerai</comment>
+ <comment xml:lang="nb">frakoblet OpenPGP-signatur</comment>
+ <comment xml:lang="nl">losse OpenPGP-ondertekening</comment>
+ <comment xml:lang="nn">fråkopla OpenPGP-signatur</comment>
+ <comment xml:lang="oc">signatura OpenPGP destacada</comment>
+ <comment xml:lang="pl">Oddzielony podpis OpenPGP</comment>
+ <comment xml:lang="pt">assinatura OpenPGP solta</comment>
+ <comment xml:lang="pt_BR">Assinatura OpenPGP destacada</comment>
+ <comment xml:lang="ro">semnătură OpenPGP detașată</comment>
+ <comment xml:lang="ru">Отсоединённая подпись OpenPGP</comment>
+ <comment xml:lang="sk">Oddelený podpis OpenPGP</comment>
+ <comment xml:lang="sl">odpet podpis OpenPGP</comment>
+ <comment xml:lang="sq">Firmë e shkëputur OpenPGP</comment>
+ <comment xml:lang="sr">одвојени ОпенПГП потпис</comment>
+ <comment xml:lang="sv">frikopplad OpenPGP-signatur</comment>
+ <comment xml:lang="tr">müstakil OpenPGP imzası</comment>
+ <comment xml:lang="uk">відокремлений OpenPGP підпис</comment>
+ <comment xml:lang="vi">chữ ký OpenPGP tách rời</comment>
+ <comment xml:lang="zh_CN">分离的 OpenPGP 签名</comment>
+ <comment xml:lang="zh_TW">分離的 OpenPGP 簽章</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <magic priority="50">
+ <match value="-----BEGIN PGP SIGNATURE-----" type="string" offset="0"/>
+ </magic>
+ <glob weight="10" pattern="*.asc"/>
+ <glob pattern="*.sig"/>
+ <glob pattern="*.pgp"/>
+ <glob pattern="*.gpg"/>
+ </mime-type>
+
+ <mime-type type="application/pkcs7-mime">
+ <comment>PKCS#7 Message or Certificate</comment>
+ <comment xml:lang="ast">Mensaxe o certificáu PKCS#7</comment>
+ <comment xml:lang="ca">missatge o certificat PKCS#7</comment>
+ <comment xml:lang="cs">zpráva nebo certifikát PKCS#7</comment>
+ <comment xml:lang="da">PKCS#7-besked eller certifikat</comment>
+ <comment xml:lang="de">PKCS#7 Nachricht oder Zertifikat</comment>
+ <comment xml:lang="el">Μήνυμα ή πιστοποιητικό PKCS#7</comment>
+ <comment xml:lang="en_GB">PKCS#7 Message or Certificate</comment>
+ <comment xml:lang="es">mensaje o certificado PKCS#7</comment>
+ <comment xml:lang="eu">PKCS#7 mezu edo zertifikazioa</comment>
+ <comment xml:lang="fi">PKCS#7-viesti tai -varmenne</comment>
+ <comment xml:lang="fr">Message ou certificat PKCS#7</comment>
+ <comment xml:lang="ga">Teachtaireacht nó Teastas PKCS#7</comment>
+ <comment xml:lang="gl">Mensaxe ou certificado PKCS#7</comment>
+ <comment xml:lang="he">הודעה או אישור מסוג PKCS#7</comment>
+ <comment xml:lang="hr">PKCS#7 poruka ili vjerodajnica</comment>
+ <comment xml:lang="hu">PKCS#7 üzenet vagy tanúsítvány</comment>
+ <comment xml:lang="ia">Message o certificato PKCS#7</comment>
+ <comment xml:lang="id">Sertifikat atau Pesan PKCS#7</comment>
+ <comment xml:lang="it">Messaggio o certificato PKCS#7</comment>
+ <comment xml:lang="ja">PKCS#7 メッセージまたは証明書</comment>
+ <comment xml:lang="kk">PKCS#7 хабарламасы не сертификаты</comment>
+ <comment xml:lang="ko">PKCS#7 메시지 또는 인증서</comment>
+ <comment xml:lang="lv">PKCS#7 ziņojums vai sertifikāts</comment>
+ <comment xml:lang="oc">Messatge o certificat PKCS#7</comment>
+ <comment xml:lang="pl">Wiadomość lub certyfikat PKCS#7</comment>
+ <comment xml:lang="pt">Mensagem ou certificado PKCS#7</comment>
+ <comment xml:lang="pt_BR">Certificado ou Mensagem PKCS#7</comment>
+ <comment xml:lang="ru">Сообщение или сертификат PKCS#7</comment>
+ <comment xml:lang="sk">Správa alebo certifikát PKCS#7</comment>
+ <comment xml:lang="sl">Sporočilo ali dovoljenje PKCS#7</comment>
+ <comment xml:lang="sr">ПКЦС#7 порука или уверење</comment>
+ <comment xml:lang="sv">PKCS#7-meddelande eller -certifikat</comment>
+ <comment xml:lang="tr">PKCS#7 İletisi veya Sertifikası</comment>
+ <comment xml:lang="uk">повідомлення або сертифікат PKCS#7</comment>
+ <comment xml:lang="zh_CN">PKCS#7 消息或证书</comment>
+ <comment xml:lang="zh_TW">PKCS#7 訊息或憑證</comment>
+ <acronym>PKCS</acronym>
+ <expanded-acronym>Public-Key Cryptography Standards</expanded-acronym>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.p7c"/>
+ <glob pattern="*.p7m"/>
+ </mime-type>
+ <mime-type type="application/pkcs7-signature">
+ <comment>detached S/MIME signature</comment>
+ <comment xml:lang="ar">إمضاء S/MIME مفصول</comment>
+ <comment xml:lang="be@latin">adłučany podpis S/MIME</comment>
+ <comment xml:lang="bg">Отделен подпис — S/MIME</comment>
+ <comment xml:lang="ca">signatura S/MIME abstreta</comment>
+ <comment xml:lang="cs">oddělený podpis S/MIME</comment>
+ <comment xml:lang="da">frigjort S/MIME-signatur</comment>
+ <comment xml:lang="de">isolierte S/MIME-Signatur</comment>
+ <comment xml:lang="el">Αποκομμένη υπογραφή S/MIME</comment>
+ <comment xml:lang="en_GB">detached S/MIME signature</comment>
+ <comment xml:lang="eo">dekroĉa S/MIME-subskribo</comment>
+ <comment xml:lang="es">firma S/MIME separada</comment>
+ <comment xml:lang="eu">desuzturtako S/MIME sinadura</comment>
+ <comment xml:lang="fi">erillinen S/MIME-allekirjoitus</comment>
+ <comment xml:lang="fo">skild S/MIME undirskrift</comment>
+ <comment xml:lang="fr">signature S/MIME détachée</comment>
+ <comment xml:lang="ga">síniú S/MIME scartha</comment>
+ <comment xml:lang="gl">sinatura S/MIME independente</comment>
+ <comment xml:lang="he">חתימת S/MIME מנותקת</comment>
+ <comment xml:lang="hr">Odvojen S/MIME potpis</comment>
+ <comment xml:lang="hu">leválasztott S/MIME-aláírás</comment>
+ <comment xml:lang="ia">Signatura S/MIME distachate</comment>
+ <comment xml:lang="id">tanda tangan S/MIME yang terlepas</comment>
+ <comment xml:lang="it">Firma staccata S/MIME</comment>
+ <comment xml:lang="ja">分離 S/MIME 署名</comment>
+ <comment xml:lang="kk">бөлінген S/MIME қолтаңбасы</comment>
+ <comment xml:lang="ko">분리된 S/MIME 서명</comment>
+ <comment xml:lang="lt">neprisegtas S/MIME parašas</comment>
+ <comment xml:lang="lv">atvienots S/MIME paraksts</comment>
+ <comment xml:lang="ms">Tandatangan S/MIME terlerai</comment>
+ <comment xml:lang="nb">frakoblet S/MIME-signatur</comment>
+ <comment xml:lang="nl">losse S/MIME-ondertekening</comment>
+ <comment xml:lang="nn">fråkopla S/MIME-signatur</comment>
+ <comment xml:lang="oc">signatura S/MIME destacada</comment>
+ <comment xml:lang="pl">Oddzielony podpis S/MIME</comment>
+ <comment xml:lang="pt">assinatura S/MIME solta</comment>
+ <comment xml:lang="pt_BR">Assinatura S/MIME destacada</comment>
+ <comment xml:lang="ro">semnătură S/MIME detașată</comment>
+ <comment xml:lang="ru">Отсоединённая подпись S/MIME</comment>
+ <comment xml:lang="sk">Oddelený podpis S/MIME</comment>
+ <comment xml:lang="sl">odpet podpis S/MIME</comment>
+ <comment xml:lang="sq">Firmë e shkëputur S/MIME</comment>
+ <comment xml:lang="sr">одвојени С/МИМЕ потпис</comment>
+ <comment xml:lang="sv">frikopplad S/MIME-signatur</comment>
+ <comment xml:lang="tr">müstakil S/MIME imzası</comment>
+ <comment xml:lang="uk">відокремлений S/MIME підпис</comment>
+ <comment xml:lang="vi">chữ ký S/MIME tách rời</comment>
+ <comment xml:lang="zh_CN">分离的 S/MIME 签名</comment>
+ <comment xml:lang="zh_TW">分離的 S/MIME 簽章</comment>
+ <acronym>S/MIME</acronym>
+ <expanded-acronym>Secure/Multipurpose Internet Mail Extensions</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.p7s"/>
+ </mime-type>
+ <mime-type type="application/pkcs8">
+ <comment>PKCS#8 private key</comment>
+ <comment xml:lang="ar">رزمة الشهادة PKCS#8</comment>
+ <comment xml:lang="bg">Ключ, частен — PKCS#8</comment>
+ <comment xml:lang="ca">clau privada PKCS#8</comment>
+ <comment xml:lang="cs">soukromý klíč PKCS#8</comment>
+ <comment xml:lang="da">PKCS#8-privat nøgle</comment>
+ <comment xml:lang="de">PKCS#8 Geheimer Schlüssel</comment>
+ <comment xml:lang="el">Ιδιωτικό κλειδί PKCS#8</comment>
+ <comment xml:lang="en_GB">PKCS#8 private key</comment>
+ <comment xml:lang="es">clave privada PCKS#8</comment>
+ <comment xml:lang="eu">PKCS#8 gako pribatua</comment>
+ <comment xml:lang="fi">PKCS#8 yksityinen avain</comment>
+ <comment xml:lang="fo">PKCS#8 privatur lykil</comment>
+ <comment xml:lang="fr">clé privée PKCS#8</comment>
+ <comment xml:lang="ga">eochair phríobháideach PKCS#8</comment>
+ <comment xml:lang="gl">Chave privada PKCS#8</comment>
+ <comment xml:lang="he">מפתח פרטי של PKCS#8</comment>
+ <comment xml:lang="hr">PKCS#8 privatni ključ</comment>
+ <comment xml:lang="hu">PKCS#8 személyes kulcs</comment>
+ <comment xml:lang="ia">Clave private PKCS#8</comment>
+ <comment xml:lang="id">Kunci privat PKCS#8</comment>
+ <comment xml:lang="it">Chiave privata PKCS#8</comment>
+ <comment xml:lang="ja">PKCS#8 秘密鍵</comment>
+ <comment xml:lang="kk">PKCS#8 меншік кілті</comment>
+ <comment xml:lang="ko">PKCS#8 개인 키</comment>
+ <comment xml:lang="lt">PKCS#8 asmeninis raktas</comment>
+ <comment xml:lang="lv">PKCS#8 privātā atslēga</comment>
+ <comment xml:lang="nl">PKCS#8 private sleutel</comment>
+ <comment xml:lang="oc">clau privada PKCS#8</comment>
+ <comment xml:lang="pl">Klucz prywatny PKCS#8</comment>
+ <comment xml:lang="pt">chave privada PKCS#8</comment>
+ <comment xml:lang="pt_BR">Chave privada PKCS#8</comment>
+ <comment xml:lang="ro">Cheie privată PKCS#8</comment>
+ <comment xml:lang="ru">Личный ключ PKCS#8</comment>
+ <comment xml:lang="sk">Súkromný kľúč PKCS#8</comment>
+ <comment xml:lang="sl">Datoteka osebnega ključa PKCS#8</comment>
+ <comment xml:lang="sr">ПКЦС#8 лични кључ</comment>
+ <comment xml:lang="sv">Privat PKCS#8-nyckel</comment>
+ <comment xml:lang="tr">PKCS#8 özel anahtarı</comment>
+ <comment xml:lang="uk">закритий ключ PKCS#8</comment>
+ <comment xml:lang="zh_CN">PKCS#8 私钥</comment>
+ <comment xml:lang="zh_TW">PKCS#8 私人金鑰</comment>
+ <acronym>PKCS</acronym>
+ <expanded-acronym>Public-Key Cryptography Standards</expanded-acronym>
+ <glob pattern="*.p8"/>
+ </mime-type>
+ <mime-type type="application/pkcs8-encrypted">
+ <comment>PKCS#8 private key (encrypted)</comment>
+ <acronym>PKCS</acronym>
+ <expanded-acronym>Public-Key Cryptography Standards</expanded-acronym>
+ <glob pattern="*.p8e"/>
+ </mime-type>
+ <mime-type type="application/pkcs10">
+ <comment>PKCS#10 certification request</comment>
+ <comment xml:lang="ar">طلب شهادة PKCS#10</comment>
+ <comment xml:lang="be@latin">Zapyt sertyfikacyi PKCS#10</comment>
+ <comment xml:lang="bg">Заявка за сертификат — PKCS#10</comment>
+ <comment xml:lang="ca">sol·licitud de certificació PKCS#10</comment>
+ <comment xml:lang="cs">žádost o certifikát PKCS#10</comment>
+ <comment xml:lang="da">PKCS#10-certifikatanmodning</comment>
+ <comment xml:lang="de">PKCS#10-Zertifikatanfrage</comment>
+ <comment xml:lang="el">Αίτηση πιστοποίησης PKCS#10</comment>
+ <comment xml:lang="en_GB">PKCS#10 certification request</comment>
+ <comment xml:lang="es">petición de certificados PKCS#10</comment>
+ <comment xml:lang="eu">PKCS#10 ziurtagirien eskaera</comment>
+ <comment xml:lang="fi">PKCS#10-varmennepyyntö</comment>
+ <comment xml:lang="fo">PKCS#10 váttanarumbøn</comment>
+ <comment xml:lang="fr">requête de certification PKCS#10</comment>
+ <comment xml:lang="ga">iarratas dheimhniúchán PKCS#10</comment>
+ <comment xml:lang="gl">Solicitude de certificado PKCS#10</comment>
+ <comment xml:lang="he">בקשה מוסמכת PLCS#10</comment>
+ <comment xml:lang="hr">PKCS#10 zahtjev vjerodajnice</comment>
+ <comment xml:lang="hu">PKCS#10-tanúsítványkérés</comment>
+ <comment xml:lang="ia">Requesta de certification PKCS#10</comment>
+ <comment xml:lang="id">Permintaan sertifikasi PKCS#10</comment>
+ <comment xml:lang="it">Richiesta certificazione PKCS#10</comment>
+ <comment xml:lang="ja">PKCS#10 証明書署名要求</comment>
+ <comment xml:lang="kk">PKCS#10 сертификацияға сұранымы</comment>
+ <comment xml:lang="ko">PKCS#10 인증서 요청</comment>
+ <comment xml:lang="lt">PKCS#10 liudijimų užklausa</comment>
+ <comment xml:lang="lv">PKCS#10 sertifikācijas pieprasījums</comment>
+ <comment xml:lang="nb">PKCS#10-sertifikatforespørsel</comment>
+ <comment xml:lang="nl">PKCS#10-certificatieverzoek</comment>
+ <comment xml:lang="nn">PKCS#10-sertifiseringsførespurnad</comment>
+ <comment xml:lang="oc">requèsta de certificacion PKCS#10</comment>
+ <comment xml:lang="pl">Żądanie certyfikatu PKCS#10</comment>
+ <comment xml:lang="pt">pedido de certificação PKCS#10</comment>
+ <comment xml:lang="pt_BR">Pedido de certificação PKCS#12</comment>
+ <comment xml:lang="ro">Cerere de certificat PKCS#10</comment>
+ <comment xml:lang="ru">Запрос сертификации PKCS#10</comment>
+ <comment xml:lang="sk">Požiadavka na certifikát PKCS#10</comment>
+ <comment xml:lang="sl">Datoteka potrdila PKCS#10</comment>
+ <comment xml:lang="sq">Kërkesë çertifikimi PKCS#10</comment>
+ <comment xml:lang="sr">ПКЦС#10 зхатев уверења</comment>
+ <comment xml:lang="sv">PKCS#10-certifikatbegäran</comment>
+ <comment xml:lang="tr">PKCS#10 sertifika isteği</comment>
+ <comment xml:lang="uk">комплект сертифікатів PKCS#10</comment>
+ <comment xml:lang="vi">Yêu cầu chứng nhận PKCS#10</comment>
+ <comment xml:lang="zh_CN">PKCS#10 认证请求</comment>
+ <comment xml:lang="zh_TW">PKCS#10 憑證請求</comment>
+ <acronym>PKCS</acronym>
+ <expanded-acronym>Public-Key Cryptography Standards</expanded-acronym>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.p10"/>
+ </mime-type>
+ <mime-type type="application/pkix-cert">
+ <comment>X.509 certificate</comment>
+ <comment xml:lang="ar">شهادة X.509</comment>
+ <comment xml:lang="ast">Certificáu X.509</comment>
+ <comment xml:lang="bg">Сертификат — X.509</comment>
+ <comment xml:lang="ca">certificat X.509</comment>
+ <comment xml:lang="cs">certifikát X.509</comment>
+ <comment xml:lang="da">X.509-certifikat</comment>
+ <comment xml:lang="de">X.509-Zertifikat</comment>
+ <comment xml:lang="el">Πιστοποιητικό X.509</comment>
+ <comment xml:lang="en_GB">X.509 certificate</comment>
+ <comment xml:lang="es">certificado X.509</comment>
+ <comment xml:lang="eu">X.509 ziurtagiria</comment>
+ <comment xml:lang="fi">X.509-varmenne</comment>
+ <comment xml:lang="fo">X.509 prógv</comment>
+ <comment xml:lang="fr">certificat X.509</comment>
+ <comment xml:lang="ga">teastas X.509</comment>
+ <comment xml:lang="gl">Certificado X.509</comment>
+ <comment xml:lang="he">אישור X.509</comment>
+ <comment xml:lang="hr">X.509 vjerodajnica</comment>
+ <comment xml:lang="hu">X.509 tanúsítvány</comment>
+ <comment xml:lang="ia">Certificato X.509</comment>
+ <comment xml:lang="id">Sertifikat X.509</comment>
+ <comment xml:lang="it">Certificato X.509</comment>
+ <comment xml:lang="ja">X.509 証明書</comment>
+ <comment xml:lang="kk">X.509 сертификаты</comment>
+ <comment xml:lang="ko">X.509 인증서</comment>
+ <comment xml:lang="lt">X.509 liudijimas</comment>
+ <comment xml:lang="lv">X.509 sertifikāts</comment>
+ <comment xml:lang="nl">X.509 certificaat</comment>
+ <comment xml:lang="oc">certificat X.509</comment>
+ <comment xml:lang="pl">Certyfikat X.509</comment>
+ <comment xml:lang="pt">certificado X.509</comment>
+ <comment xml:lang="pt_BR">Certificado X.509</comment>
+ <comment xml:lang="ro">Certificat X.509</comment>
+ <comment xml:lang="ru">Сертификат X.509</comment>
+ <comment xml:lang="sk">Certifikát X.509</comment>
+ <comment xml:lang="sl">Datoteka potrdila X.509</comment>
+ <comment xml:lang="sr">Икс.509 уверење</comment>
+ <comment xml:lang="sv">X.509-certifikat</comment>
+ <comment xml:lang="tr">X.509 sertifikası</comment>
+ <comment xml:lang="uk">сертифікат X.509</comment>
+ <comment xml:lang="zh_CN">X.509 证书</comment>
+ <comment xml:lang="zh_TW">X.509 憑證</comment>
+ <glob pattern="*.cer"/>
+ </mime-type>
+ <mime-type type="application/pkix-crl">
+ <comment>Certificate revocation list</comment>
+ <comment xml:lang="ar">قائمة إبطال الشهادات</comment>
+ <comment xml:lang="ast">Llistáu de revocación de certificaos</comment>
+ <comment xml:lang="bg">Списък с отхвърлени сертификати</comment>
+ <comment xml:lang="ca">llista de revocació de certificats</comment>
+ <comment xml:lang="cs">seznam odvolaných certifikátů</comment>
+ <comment xml:lang="da">Certifikattilbagekaldelsesliste</comment>
+ <comment xml:lang="de">Liste widerrufener Zertifikate</comment>
+ <comment xml:lang="el">Λίστα ανάκλησης πιστοποιητικού</comment>
+ <comment xml:lang="en_GB">Certificate revocation list</comment>
+ <comment xml:lang="es">lista de revocación de certificados</comment>
+ <comment xml:lang="eu">Ziurtagiri-errebokatzeen zerrenda</comment>
+ <comment xml:lang="fi">Varmenteiden sulkulista</comment>
+ <comment xml:lang="fo">Prógv afturtøkulisti</comment>
+ <comment xml:lang="fr">liste de révocation de certificat</comment>
+ <comment xml:lang="ga">Liosta teastas cúlghairmthe</comment>
+ <comment xml:lang="gl">lista de certificados de revogación</comment>
+ <comment xml:lang="he">רשימת אישורים מבוטלים</comment>
+ <comment xml:lang="hr">Popis opozvanih vjerodajnica</comment>
+ <comment xml:lang="hu">Tanúsítvány-visszavonási lista</comment>
+ <comment xml:lang="ia">Lista de revocation de certificatos</comment>
+ <comment xml:lang="id">Daftar pencabutan sertificat (CRL)</comment>
+ <comment xml:lang="it">Elenco certificati di revoca</comment>
+ <comment xml:lang="ja">証明書失効リスト</comment>
+ <comment xml:lang="kk">Сертификатты қайта шақыру тізімі</comment>
+ <comment xml:lang="ko">인증서 철회 목록</comment>
+ <comment xml:lang="lt">Panaikintų liudijimų sąrašas</comment>
+ <comment xml:lang="lv">Sertifikātu atsaukšanu saraksts</comment>
+ <comment xml:lang="nl">Certificaat revocation lijst</comment>
+ <comment xml:lang="oc">lista de revocacion de certificat</comment>
+ <comment xml:lang="pl">Lista unieważnień certyfikatów</comment>
+ <comment xml:lang="pt">lista de revogação de certificados</comment>
+ <comment xml:lang="pt_BR">Lista de revogação de certificado</comment>
+ <comment xml:lang="ro">Listă de revocare a certificatelor</comment>
+ <comment xml:lang="ru">Список аннулирования сертификатов</comment>
+ <comment xml:lang="sk">Zoznam zrušených certifikátov</comment>
+ <comment xml:lang="sl">Datoteka seznama preklica potrdil</comment>
+ <comment xml:lang="sr">списак повлачења уверења</comment>
+ <comment xml:lang="sv">Spärrlista för certifikat</comment>
+ <comment xml:lang="tr">Sertifika iptal listesi</comment>
+ <comment xml:lang="uk">список відкликання сертифікатів</comment>
+ <comment xml:lang="zh_CN">证书吊销列表</comment>
+ <comment xml:lang="zh_TW">憑證撤銷清單</comment>
+ <glob pattern="*.crl"/>
+ </mime-type>
+ <mime-type type="application/pkix-pkipath">
+ <comment>PkiPath certification path</comment>
+ <comment xml:lang="ar">مسار شهادة PkiPath</comment>
+ <comment xml:lang="ast">Camín de certificación PkiPath</comment>
+ <comment xml:lang="bg">Сертификационна верига — PkiPath</comment>
+ <comment xml:lang="ca">camí cap a la certificació PkiPath</comment>
+ <comment xml:lang="cs">cesta k certifikátu PkiPath</comment>
+ <comment xml:lang="da">PkiPath-certifikationssti</comment>
+ <comment xml:lang="de">PkiPath-Zertifikatspfad</comment>
+ <comment xml:lang="el">Διαδρομή πιστοποιητικού PkiPath</comment>
+ <comment xml:lang="en_GB">PkiPath certification path</comment>
+ <comment xml:lang="es">ruta de certificación PkiPath</comment>
+ <comment xml:lang="eu">PkiPath ziurtagirien bide-izena</comment>
+ <comment xml:lang="fi">PkiPath-varmennepolku</comment>
+ <comment xml:lang="fo">PkiPath váttanleið</comment>
+ <comment xml:lang="fr">chemin de certification PkiPath</comment>
+ <comment xml:lang="ga">conair dheimhniúcháin PkiPath</comment>
+ <comment xml:lang="gl">Ruta de certificación PkiPath</comment>
+ <comment xml:lang="he">נתיב מאושר של PkiPath</comment>
+ <comment xml:lang="hr">PkiPath putanja vjerodajnice</comment>
+ <comment xml:lang="hu">PkiPath tanúsítványútvonal</comment>
+ <comment xml:lang="ia">Cammino de certification PkiPath</comment>
+ <comment xml:lang="id">Alamat sertifikasi PkiPath</comment>
+ <comment xml:lang="it">Percorso certificazione PkiPath</comment>
+ <comment xml:lang="ja">PkiPath 証明書パス</comment>
+ <comment xml:lang="kk">PkiPath сертификаттау жолы</comment>
+ <comment xml:lang="ko">PkiPath 인증서 요청</comment>
+ <comment xml:lang="lt">PkiPath liudijimų maršrutas</comment>
+ <comment xml:lang="lv">PkiPath sertifikāta ceļš</comment>
+ <comment xml:lang="nl">PkiPath-certificatiepad</comment>
+ <comment xml:lang="oc">camin de certificacion PkiPath</comment>
+ <comment xml:lang="pl">Ścieżka certyfikacji PkiPath</comment>
+ <comment xml:lang="pt">caminho de certificação PkiPath</comment>
+ <comment xml:lang="pt_BR">Pedido de certificação PkiPath</comment>
+ <comment xml:lang="ro">Cale certificare PkiPath</comment>
+ <comment xml:lang="ru">Путь сертификации PkiPath</comment>
+ <comment xml:lang="sk">Cesta k certifikátu PkiPath</comment>
+ <comment xml:lang="sl">Datoteka poti potrdila PkiPath</comment>
+ <comment xml:lang="sr">путања уверења ПкиПат-а</comment>
+ <comment xml:lang="sv">PkiPath-certifikatsekvens</comment>
+ <comment xml:lang="tr">PkiPath sertifika yolu</comment>
+ <comment xml:lang="uk">шлях сертифікації PkiPath</comment>
+ <comment xml:lang="vi">Đường dẫn cấp chứng nhận PkiPath</comment>
+ <comment xml:lang="zh_CN">PkiPath 证书目录</comment>
+ <comment xml:lang="zh_TW">PkiPath 憑證路徑</comment>
+ <glob pattern="*.pkipath"/>
+ </mime-type>
+ <mime-type type="application/postscript">
+ <comment>PS document</comment>
+ <comment xml:lang="ar">مستند PS</comment>
+ <comment xml:lang="ast">Documentu PS</comment>
+ <comment xml:lang="be@latin">Dakument PS</comment>
+ <comment xml:lang="bg">Документ — PS</comment>
+ <comment xml:lang="ca">document PS</comment>
+ <comment xml:lang="cs">dokument PS</comment>
+ <comment xml:lang="da">PS-dokument</comment>
+ <comment xml:lang="de">PS-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο PS</comment>
+ <comment xml:lang="en_GB">PS document</comment>
+ <comment xml:lang="eo">PS-dokumento</comment>
+ <comment xml:lang="es">documento PS</comment>
+ <comment xml:lang="eu">PS dokumentua</comment>
+ <comment xml:lang="fi">PS-asiakirja</comment>
+ <comment xml:lang="fo">PS skjal</comment>
+ <comment xml:lang="fr">document PS</comment>
+ <comment xml:lang="ga">cáipéis PS</comment>
+ <comment xml:lang="gl">documento PS</comment>
+ <comment xml:lang="he">מסמך PS</comment>
+ <comment xml:lang="hr">PS dokument</comment>
+ <comment xml:lang="hu">PS dokumentum</comment>
+ <comment xml:lang="ia">Documento PS</comment>
+ <comment xml:lang="id">Dokumen PS</comment>
+ <comment xml:lang="it">Documento PS</comment>
+ <comment xml:lang="ja">PS ドキュメント</comment>
+ <comment xml:lang="kk">PS құжаты</comment>
+ <comment xml:lang="ko">PS 문서</comment>
+ <comment xml:lang="lt">PS dokumentas</comment>
+ <comment xml:lang="lv">PS dokuments</comment>
+ <comment xml:lang="nb">PS-dokument</comment>
+ <comment xml:lang="nl">PS-document</comment>
+ <comment xml:lang="nn">PS-dokument</comment>
+ <comment xml:lang="oc">document PS</comment>
+ <comment xml:lang="pl">Dokument PS</comment>
+ <comment xml:lang="pt">documento PS</comment>
+ <comment xml:lang="pt_BR">Documento PS</comment>
+ <comment xml:lang="ro">Document PS</comment>
+ <comment xml:lang="ru">Документ PS</comment>
+ <comment xml:lang="sk">Dokument PS</comment>
+ <comment xml:lang="sl">Dokument PS</comment>
+ <comment xml:lang="sq">Dokument PS</comment>
+ <comment xml:lang="sr">ПС документ</comment>
+ <comment xml:lang="sv">PS-dokument</comment>
+ <comment xml:lang="tr">PS belgesi</comment>
+ <comment xml:lang="uk">документ PS</comment>
+ <comment xml:lang="vi">Tài liệu PS</comment>
+ <comment xml:lang="zh_CN">PS 文档</comment>
+ <comment xml:lang="zh_TW">Ps 文件</comment>
+ <acronym>PS</acronym>
+ <expanded-acronym>PostScript</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="\004%!" type="string" offset="0"/>
+ <match value="%!" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.ps"/>
+ </mime-type>
+ <mime-type type="application/prs.plucker">
+ <comment>Plucker document</comment>
+ <comment xml:lang="ar">مستند Plucker</comment>
+ <comment xml:lang="ast">Documentu Plucker</comment>
+ <comment xml:lang="be@latin">Dakument Plucker</comment>
+ <comment xml:lang="bg">Документ — Plucker</comment>
+ <comment xml:lang="ca">document Plucker</comment>
+ <comment xml:lang="cs">dokument Plucker</comment>
+ <comment xml:lang="da">Pluckerdokument</comment>
+ <comment xml:lang="de">Plucker-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Plucker</comment>
+ <comment xml:lang="en_GB">Plucker document</comment>
+ <comment xml:lang="eo">Plucker-dokumento</comment>
+ <comment xml:lang="es">documento de Plucker</comment>
+ <comment xml:lang="eu">Plucker dokumentua</comment>
+ <comment xml:lang="fi">Plucker-asiakirja</comment>
+ <comment xml:lang="fo">Plucker skjal</comment>
+ <comment xml:lang="fr">document Plucker</comment>
+ <comment xml:lang="ga">cáipéis Plucker</comment>
+ <comment xml:lang="gl">documento de Plucker</comment>
+ <comment xml:lang="he">מסמך של Plucker</comment>
+ <comment xml:lang="hr">Plucker dokument</comment>
+ <comment xml:lang="hu">Plucker dokumentum</comment>
+ <comment xml:lang="ia">Documento Plucker</comment>
+ <comment xml:lang="id">Dokumen Plucker</comment>
+ <comment xml:lang="it">Documento Plucker</comment>
+ <comment xml:lang="ja">Plucker ドキュメント</comment>
+ <comment xml:lang="kk">Plucker құжаты</comment>
+ <comment xml:lang="ko">Plucker 문서</comment>
+ <comment xml:lang="lt">Plucker dokumentas</comment>
+ <comment xml:lang="lv">Plucker dokuments</comment>
+ <comment xml:lang="nb">Plucker-dokument</comment>
+ <comment xml:lang="nl">Plucker-document</comment>
+ <comment xml:lang="nn">Plucker-dokument</comment>
+ <comment xml:lang="oc">document Plucker</comment>
+ <comment xml:lang="pl">Dokument Plucker</comment>
+ <comment xml:lang="pt">documento Plucker</comment>
+ <comment xml:lang="pt_BR">Documento do Plucker</comment>
+ <comment xml:lang="ro">Document Plucker</comment>
+ <comment xml:lang="ru">Документ Plucker</comment>
+ <comment xml:lang="sk">Dokument Plucker</comment>
+ <comment xml:lang="sl">Dokument Plucker</comment>
+ <comment xml:lang="sq">Dokument Plucker</comment>
+ <comment xml:lang="sr">Плакер документ</comment>
+ <comment xml:lang="sv">Plucker-dokument</comment>
+ <comment xml:lang="tr">Plucker belgesi</comment>
+ <comment xml:lang="uk">документ Plucker</comment>
+ <comment xml:lang="vi">Tài liệu Plucker</comment>
+ <comment xml:lang="zh_CN">Plucker 文档</comment>
+ <comment xml:lang="zh_TW">Plucker 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="80">
+ <match value="DataPlkr" type="string" offset="60"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/raml+yaml">
+ <comment>RAML document</comment>
+ <comment xml:lang="ast">Documentu RAML</comment>
+ <comment xml:lang="ca">document RAML</comment>
+ <comment xml:lang="cs">dokument RAML</comment>
+ <comment xml:lang="da">RAML-dokument</comment>
+ <comment xml:lang="de">RAML-Dokument</comment>
+ <comment xml:lang="en_GB">RAML document</comment>
+ <comment xml:lang="es">documento RAML</comment>
+ <comment xml:lang="eu">RAML dokumentua</comment>
+ <comment xml:lang="fi">RAML-asiakirja</comment>
+ <comment xml:lang="fr">document RAML</comment>
+ <comment xml:lang="ga">cáipéis RAML</comment>
+ <comment xml:lang="he">מסמך RAML</comment>
+ <comment xml:lang="hr">RAML dokument</comment>
+ <comment xml:lang="hu">RAML dokumentum</comment>
+ <comment xml:lang="id">dokumen RAML</comment>
+ <comment xml:lang="it">Documento RAML</comment>
+ <comment xml:lang="kk">RAML құжаты</comment>
+ <comment xml:lang="ko">RAML 문서</comment>
+ <comment xml:lang="oc">Document RAML</comment>
+ <comment xml:lang="pl">Dokument RAML</comment>
+ <comment xml:lang="pt_BR">Documento RAML</comment>
+ <comment xml:lang="ru">Документ RAML</comment>
+ <comment xml:lang="sk">Dokument RAML</comment>
+ <comment xml:lang="sr">РАМЛ документ</comment>
+ <comment xml:lang="sv">RAML-dokument</comment>
+ <comment xml:lang="tr">RAML belgesi</comment>
+ <comment xml:lang="uk">документ RAML</comment>
+ <comment xml:lang="zh_CN">RAML 文档</comment>
+ <comment xml:lang="zh_TW">RAML 文件</comment>
+ <acronym>RAML</acronym>
+ <expanded-acronym>RESTful API Modeling Language</expanded-acronym>
+ <sub-class-of type="application/x-yaml"/>
+ <magic>
+ <match value="#%RAML " type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.raml"/>
+ </mime-type>
+ <mime-type type="application/relax-ng-compact-syntax">
+ <comment>RELAX NG XML schema</comment>
+ <comment xml:lang="ar">مخطط RELAX NG XML</comment>
+ <comment xml:lang="bg">Схема за XML — RELAX NG</comment>
+ <comment xml:lang="ca">esquema XML en RELAX NG</comment>
+ <comment xml:lang="cs">schéma RELAX NG XML</comment>
+ <comment xml:lang="da">RELAX NG XML-skema</comment>
+ <comment xml:lang="de">RELAX NG XML-Schema</comment>
+ <comment xml:lang="el">Σχήμα RELAX NG XML</comment>
+ <comment xml:lang="en_GB">RELAX NG XML schema</comment>
+ <comment xml:lang="es">esquema XML RELAX NG</comment>
+ <comment xml:lang="eu">RELAX NG XML eskema</comment>
+ <comment xml:lang="fi">RELAX NG XML-skeema</comment>
+ <comment xml:lang="fr">schéma XML RELAX NG</comment>
+ <comment xml:lang="ga">scéimre XML RELAX NG</comment>
+ <comment xml:lang="gl">Esquema XML RELAX NG</comment>
+ <comment xml:lang="he">סכנת RELAX NG XML</comment>
+ <comment xml:lang="hr">RELAX NG XML shema</comment>
+ <comment xml:lang="hu">RELAX NG XML-séma</comment>
+ <comment xml:lang="ia">Schema XML RELAX NG</comment>
+ <comment xml:lang="id">Skema XML RELAX NG</comment>
+ <comment xml:lang="it">Schema XML RELAX NG</comment>
+ <comment xml:lang="ja">RELAX NG XML スキーマ</comment>
+ <comment xml:lang="kk">RELAX NG XML сұлбасы</comment>
+ <comment xml:lang="ko">RELAX NG XML 스키마</comment>
+ <comment xml:lang="lt">RELAX NG XML schema</comment>
+ <comment xml:lang="lv">RELAX NG XML shēma</comment>
+ <comment xml:lang="nl">RELAX NG XML schema</comment>
+ <comment xml:lang="oc">esquèma XML RELAX NG</comment>
+ <comment xml:lang="pl">Schemat XML RELAX NG</comment>
+ <comment xml:lang="pt">Esquema RELAX NG XML</comment>
+ <comment xml:lang="pt_BR">Esquema XML de RELAX NG</comment>
+ <comment xml:lang="ro">Schemă RELAX NG XML</comment>
+ <comment xml:lang="ru">XML-схема RELAX NG</comment>
+ <comment xml:lang="sk">XML schéma RELAX NG</comment>
+ <comment xml:lang="sl">Datoteka shema RELAX NG XML</comment>
+ <comment xml:lang="sr">РЕЛАКС НГ ИксМЛ шема</comment>
+ <comment xml:lang="sv">RELAX NG XML-schema</comment>
+ <comment xml:lang="tr">RELAX NG XML şeması</comment>
+ <comment xml:lang="uk">XML-схема RELAX NG</comment>
+ <comment xml:lang="zh_CN">RELAX NG XML 模式</comment>
+ <comment xml:lang="zh_TW">RELAX NG XML schema</comment>
+ <acronym>RELAX NG</acronym>
+ <expanded-acronym>REgular LAnguage for XML Next Generation</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.rnc"/>
+ <alias type="application/x-rnc"/>
+ </mime-type>
+ <mime-type type="application/rtf">
+ <comment>RTF document</comment>
+ <comment xml:lang="ar">مستند RTF</comment>
+ <comment xml:lang="ast">Documentu RTF</comment>
+ <comment xml:lang="be@latin">Dakument RTF</comment>
+ <comment xml:lang="bg">Документ — RTF</comment>
+ <comment xml:lang="ca">document RTF</comment>
+ <comment xml:lang="cs">dokument RTF</comment>
+ <comment xml:lang="da">RTF-dokument</comment>
+ <comment xml:lang="de">RTF-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο RTF</comment>
+ <comment xml:lang="en_GB">RTF document</comment>
+ <comment xml:lang="eo">RTF-dokumento</comment>
+ <comment xml:lang="es">documento RTF</comment>
+ <comment xml:lang="eu">RTF dokumentua</comment>
+ <comment xml:lang="fi">RTF-asiakirja</comment>
+ <comment xml:lang="fo">RTF skjal</comment>
+ <comment xml:lang="fr">document RTF</comment>
+ <comment xml:lang="ga">cáipéis RTF</comment>
+ <comment xml:lang="gl">documento RTF</comment>
+ <comment xml:lang="he">מסמך RTF</comment>
+ <comment xml:lang="hr">RTF dokument</comment>
+ <comment xml:lang="hu">RTF dokumentum</comment>
+ <comment xml:lang="ia">Documento RTF</comment>
+ <comment xml:lang="id">Dokumen RTF</comment>
+ <comment xml:lang="it">Documento RTF</comment>
+ <comment xml:lang="ja">RTF ドキュメント</comment>
+ <comment xml:lang="kk">RTF құжаты</comment>
+ <comment xml:lang="ko">RTF 문서</comment>
+ <comment xml:lang="lt">RTF dokumentas</comment>
+ <comment xml:lang="lv">RTF dokuments</comment>
+ <comment xml:lang="nb">RTF-dokument</comment>
+ <comment xml:lang="nl">RTF-document</comment>
+ <comment xml:lang="nn">TRF-dokument</comment>
+ <comment xml:lang="oc">document RTF</comment>
+ <comment xml:lang="pl">Dokument RTF</comment>
+ <comment xml:lang="pt">documento RTF</comment>
+ <comment xml:lang="pt_BR">Documento RTF</comment>
+ <comment xml:lang="ro">Document RTF</comment>
+ <comment xml:lang="ru">Документ RTF</comment>
+ <comment xml:lang="sk">Dokument RTF</comment>
+ <comment xml:lang="sl">Dokument RTF</comment>
+ <comment xml:lang="sq">Dokument RTF</comment>
+ <comment xml:lang="sr">РТФ документ</comment>
+ <comment xml:lang="sv">RTF-dokument</comment>
+ <comment xml:lang="tr">RTF belgesi</comment>
+ <comment xml:lang="uk">документ RTF</comment>
+ <comment xml:lang="vi">Tài liệu RTF</comment>
+ <comment xml:lang="zh_CN">RTF 文档</comment>
+ <comment xml:lang="zh_TW">RTF 文件</comment>
+ <acronym>RTF</acronym>
+ <expanded-acronym>Rich Text Format</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="{\\rtf" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.rtf"/>
+ <alias type="text/rtf"/>
+ </mime-type>
+ <mime-type type="application/sieve">
+ <comment>Sieve mail filter script</comment>
+ <comment xml:lang="ar">سكربت مرشح بريد Sieve</comment>
+ <comment xml:lang="be@latin">Skrypt filtravańnia pošty Sieve</comment>
+ <comment xml:lang="bg">Скрипт-филтър за пресяване на поща</comment>
+ <comment xml:lang="ca">script de filtre de correu Sieve</comment>
+ <comment xml:lang="cs">skript poštovního filtru Sieve</comment>
+ <comment xml:lang="da">Sieve e-post-filterprogram</comment>
+ <comment xml:lang="de">Sieve-E-Mail-Filterskript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών φιλτραρίσματος αλληλογραφίας Sieve</comment>
+ <comment xml:lang="en_GB">Sieve mail filter script</comment>
+ <comment xml:lang="es">secuencia de órdenes de filtro en Sieve</comment>
+ <comment xml:lang="eu">Sieve posta-iragazki script-a</comment>
+ <comment xml:lang="fi">Sieve-postinsuodatuskomentotiedosto</comment>
+ <comment xml:lang="fr">script de filtrage de courriel Sieve</comment>
+ <comment xml:lang="ga">script scagaire r-phost Sieve</comment>
+ <comment xml:lang="gl">Script de filtro de correo Sieve</comment>
+ <comment xml:lang="he">תסריט סינון דואר של Sieve</comment>
+ <comment xml:lang="hr">Sieve skripta filtriranja pošte</comment>
+ <comment xml:lang="hu">Sieve levélszűrő parancsfájl</comment>
+ <comment xml:lang="ia">Script de filtration de e-mail Sieve</comment>
+ <comment xml:lang="id">Skrip filter surat Sieve</comment>
+ <comment xml:lang="it">Script filtro posta Sieve</comment>
+ <comment xml:lang="ja">Sieve メールフィルタスクリプト</comment>
+ <comment xml:lang="kk">Sieve пошталық фильтр сценарийі</comment>
+ <comment xml:lang="ko">Sieve 메일 필터 스크립트</comment>
+ <comment xml:lang="lt">Sieve pašto filtro scenarijus</comment>
+ <comment xml:lang="lv">Sieve pasta filtra skripts</comment>
+ <comment xml:lang="nb">Sieve e-postfilter skript</comment>
+ <comment xml:lang="nl">Sieve mailfilter-script</comment>
+ <comment xml:lang="nn">Sieve e-postfilterskript</comment>
+ <comment xml:lang="oc">escript de filtratge de corrièr electronic Sieve</comment>
+ <comment xml:lang="pl">Skrypt filtra poczty Sieve</comment>
+ <comment xml:lang="pt">Script de filtragem de correio Sieve</comment>
+ <comment xml:lang="pt_BR">Script de filtro de mensagens do Sieve</comment>
+ <comment xml:lang="ro">Script filtrare email Sieve</comment>
+ <comment xml:lang="ru">Сценарий почтового фильтра Sieve</comment>
+ <comment xml:lang="sk">Skript poštového filtra Sieve</comment>
+ <comment xml:lang="sl">Skriptna datoteka Sieve poštnega filtra</comment>
+ <comment xml:lang="sq">Script filtrim poste Sieve</comment>
+ <comment xml:lang="sr">Сјев скрипта пропусника поште</comment>
+ <comment xml:lang="sv">Sieve-epostfilterskript</comment>
+ <comment xml:lang="tr">Sieve posta filtre betiği</comment>
+ <comment xml:lang="uk">скрипт поштового фільтру Sieve</comment>
+ <comment xml:lang="vi">Văn lệnh lọc thư Sieve</comment>
+ <comment xml:lang="zh_CN">Sieve 邮件过滤脚本</comment>
+ <comment xml:lang="zh_TW">Sieve 郵件過濾指令稿</comment>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.siv"/>
+ </mime-type>
+ <mime-type type="application/smil+xml">
+ <comment>SMIL document</comment>
+ <comment xml:lang="ar">مستند SMIL</comment>
+ <comment xml:lang="ast">Documentu SMIL</comment>
+ <comment xml:lang="be@latin">Dakument SMIL</comment>
+ <comment xml:lang="bg">Документ — SMIL</comment>
+ <comment xml:lang="ca">document SMIL</comment>
+ <comment xml:lang="cs">dokument SMIL</comment>
+ <comment xml:lang="da">SMIL-dokument</comment>
+ <comment xml:lang="de">SMIL-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο SMIL</comment>
+ <comment xml:lang="en_GB">SMIL document</comment>
+ <comment xml:lang="eo">SMIL-dokumento</comment>
+ <comment xml:lang="es">documento SMIL</comment>
+ <comment xml:lang="eu">SMIL dokumentua</comment>
+ <comment xml:lang="fi">SMIL-asiakirja</comment>
+ <comment xml:lang="fo">SMIL skjal</comment>
+ <comment xml:lang="fr">document SMIL</comment>
+ <comment xml:lang="ga">cáipéis SMIL</comment>
+ <comment xml:lang="gl">documento SMIL</comment>
+ <comment xml:lang="he">מסמך SMIL</comment>
+ <comment xml:lang="hr">SMIL dokument</comment>
+ <comment xml:lang="hu">SMIL dokumentum</comment>
+ <comment xml:lang="ia">Documento SMIL</comment>
+ <comment xml:lang="id">Dokumen SMIL</comment>
+ <comment xml:lang="it">Documento SMIL</comment>
+ <comment xml:lang="ja">SMIL ドキュメント</comment>
+ <comment xml:lang="kk">SMIL құжаты</comment>
+ <comment xml:lang="ko">SMIL 문서</comment>
+ <comment xml:lang="lt">SMIL dokumentas</comment>
+ <comment xml:lang="lv">SMIL dokuments</comment>
+ <comment xml:lang="nb">SMIL-dokument</comment>
+ <comment xml:lang="nl">SMIL-document</comment>
+ <comment xml:lang="nn">SMIL-dokument</comment>
+ <comment xml:lang="oc">document SMIL</comment>
+ <comment xml:lang="pl">Dokument SMIL</comment>
+ <comment xml:lang="pt">documento SMIL</comment>
+ <comment xml:lang="pt_BR">Documento SMIL</comment>
+ <comment xml:lang="ro">Document SMIL</comment>
+ <comment xml:lang="ru">Документ SMIL</comment>
+ <comment xml:lang="sk">Dokument SMIL</comment>
+ <comment xml:lang="sl">Dokument SMIL</comment>
+ <comment xml:lang="sq">Dokument SMIL</comment>
+ <comment xml:lang="sr">СМИЛ документ</comment>
+ <comment xml:lang="sv">SMIL-dokument</comment>
+ <comment xml:lang="tr">SMIL belgesi</comment>
+ <comment xml:lang="uk">документ SMIL</comment>
+ <comment xml:lang="vi">Tài liệu SMIL</comment>
+ <comment xml:lang="zh_CN">SMIL 文档</comment>
+ <comment xml:lang="zh_TW">SMIL 文件</comment>
+ <acronym>SMIL</acronym>
+ <expanded-acronym>Synchronized Multimedia Integration Language</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <alias type="application/smil"/>
+ <generic-icon name="video-x-generic"/>
+ <glob pattern="*.smil"/>
+ <glob pattern="*.smi"/>
+ <glob pattern="*.sml"/>
+ <glob pattern="*.kino"/>
+ <magic priority="55">
+ <match value="&lt;smil" type="string" offset="0:256"/>
+ </magic>
+ <root-XML namespaceURI="http://www.w3.org/2001/SMIL20/Language" localName="smil"/>
+ <root-XML namespaceURI="http://www.w3.org/2005/SMIL21/Language" localName="smil"/>
+ <root-XML namespaceURI="http://www.w3.org/ns/SMIL" localName="smil"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-wpl">
+ <comment>WPL playlist</comment>
+ <comment xml:lang="ar">قائمة تشغيل WPL</comment>
+ <comment xml:lang="bg">Списък за изпълнение — WPL</comment>
+ <comment xml:lang="ca">llista de reproducció WPL</comment>
+ <comment xml:lang="cs">seznam k přehrání WPL</comment>
+ <comment xml:lang="da">WPL-afspilningsliste</comment>
+ <comment xml:lang="de">WPL-Wiedergabeliste</comment>
+ <comment xml:lang="el">Λίστα αναπαραγωγής WPL</comment>
+ <comment xml:lang="en_GB">WPL playlist</comment>
+ <comment xml:lang="eo">WPL-ludlisto</comment>
+ <comment xml:lang="es">lista de reproducción WPL</comment>
+ <comment xml:lang="eu">WPL erreprodukzio-zerrenda</comment>
+ <comment xml:lang="fi">WPL-soittolista</comment>
+ <comment xml:lang="fo">WPL avspælingarlisti</comment>
+ <comment xml:lang="fr">liste de lecture WPL</comment>
+ <comment xml:lang="ga">seinmliosta WPL</comment>
+ <comment xml:lang="gl">lista de reprodución WPL</comment>
+ <comment xml:lang="he">רשימת נגינה WPL</comment>
+ <comment xml:lang="hr">WPL popis izvođenja</comment>
+ <comment xml:lang="hu">WPL-lejátszólista</comment>
+ <comment xml:lang="ia">Lista de selection WPL</comment>
+ <comment xml:lang="id">Senarai putar WPL</comment>
+ <comment xml:lang="it">Playlist WPL</comment>
+ <comment xml:lang="ja">WPL 再生リスト</comment>
+ <comment xml:lang="kk">WPL ойнау тізімі</comment>
+ <comment xml:lang="ko">WPL 재생 목록</comment>
+ <comment xml:lang="lt">WPL grojaraštis</comment>
+ <comment xml:lang="lv">WPL repertuārs</comment>
+ <comment xml:lang="nl">WPL-afspeellijst</comment>
+ <comment xml:lang="oc">lista de lectura WPL</comment>
+ <comment xml:lang="pl">Lista odtwarzania WPL</comment>
+ <comment xml:lang="pt">lista de reprodução WPL</comment>
+ <comment xml:lang="pt_BR">Lista de reprodução do WPL</comment>
+ <comment xml:lang="ro">Listă redare WPL</comment>
+ <comment xml:lang="ru">Список воспроизведения WPL</comment>
+ <comment xml:lang="sk">Zoznam skladieb WPL</comment>
+ <comment xml:lang="sl">Seznam predvajanja WPL</comment>
+ <comment xml:lang="sr">ВПЛ списак нумера</comment>
+ <comment xml:lang="sv">WPL-spellista</comment>
+ <comment xml:lang="tr">WPL çalma listesi</comment>
+ <comment xml:lang="uk">список відтворення WPL</comment>
+ <comment xml:lang="vi">Danh mục nhạc WPL</comment>
+ <comment xml:lang="zh_CN">WPL 播放列表</comment>
+ <comment xml:lang="zh_TW">WPL 播放清單</comment>
+ <acronym>WPL</acronym>
+ <expanded-acronym>Windows Media Player Playlist</expanded-acronym>
+ <generic-icon name="video-x-generic"/>
+ <glob pattern="*.wpl"/>
+ <magic priority="60">
+ <match value="&lt;?wpl" type="string" offset="0:256"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-sqlite2">
+ <comment>SQLite2 database</comment>
+ <comment xml:lang="ar">قاعدة بيانات SQLite2</comment>
+ <comment xml:lang="be@latin">Baza źviestak SQLite2</comment>
+ <comment xml:lang="bg">База от данни — SQLite2</comment>
+ <comment xml:lang="ca">base de dades SQLite2</comment>
+ <comment xml:lang="cs">databáze SQLite2</comment>
+ <comment xml:lang="da">SQLite2-database</comment>
+ <comment xml:lang="de">SQLite2-Datenbank</comment>
+ <comment xml:lang="el">Βάση δεδομένων SQLite2</comment>
+ <comment xml:lang="en_GB">SQLite2 database</comment>
+ <comment xml:lang="eo">SQLite2-datumbazo</comment>
+ <comment xml:lang="es">base de datos SQLite2</comment>
+ <comment xml:lang="eu">SQLite2 datu-basea</comment>
+ <comment xml:lang="fi">SQLite2-tietokanta</comment>
+ <comment xml:lang="fo">SQLite2 dátustovnur</comment>
+ <comment xml:lang="fr">base de données SQLite2</comment>
+ <comment xml:lang="ga">bunachar sonraí SQLite2</comment>
+ <comment xml:lang="gl">base de datos SQLite2</comment>
+ <comment xml:lang="he">מסד נתונים מסוג SQLite2</comment>
+ <comment xml:lang="hr">SQLite2 baza podataka</comment>
+ <comment xml:lang="hu">SQLite2 adatbázis</comment>
+ <comment xml:lang="ia">Base de datos SQLite2</comment>
+ <comment xml:lang="id">Basis data SQLite2</comment>
+ <comment xml:lang="it">Database SQLite2</comment>
+ <comment xml:lang="ja">SQLite2 データベース</comment>
+ <comment xml:lang="kk">SQLite2 дерекқоры</comment>
+ <comment xml:lang="ko">SQLite2 데이터베이스</comment>
+ <comment xml:lang="lt">SQLite2 duomenų bazė</comment>
+ <comment xml:lang="lv">SQLite2 datubāze</comment>
+ <comment xml:lang="nb">SQLite2-database</comment>
+ <comment xml:lang="nl">SQLite2-gegevensbank</comment>
+ <comment xml:lang="nn">SQLite2-database</comment>
+ <comment xml:lang="oc">banca de donadas SQLite2</comment>
+ <comment xml:lang="pl">Baza danych SQLite2</comment>
+ <comment xml:lang="pt">base de dados SQLite2</comment>
+ <comment xml:lang="pt_BR">Banco de dados SQLite2</comment>
+ <comment xml:lang="ro">Bază de date SQLite2</comment>
+ <comment xml:lang="ru">База данных SQLite2</comment>
+ <comment xml:lang="sk">Databáza SQLite2</comment>
+ <comment xml:lang="sl">Podatkovna zbirka SQLite2</comment>
+ <comment xml:lang="sq">Bazë me të dhëna SQLite2</comment>
+ <comment xml:lang="sr">СКуЛајт2 база података</comment>
+ <comment xml:lang="sv">SQLite2-databas</comment>
+ <comment xml:lang="tr">SQLite2 veritabanı</comment>
+ <comment xml:lang="uk">База даних SQLite2</comment>
+ <comment xml:lang="vi">Cơ sở dữ liệu SQLite2</comment>
+ <comment xml:lang="zh_CN">SQLite2 数据库</comment>
+ <comment xml:lang="zh_TW">SQLite2 資料庫</comment>
+ <glob pattern="*.sqlite2"/>
+ <magic>
+ <match value="** This file contains an SQLite" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/vnd.sqlite3">
+ <comment>SQLite3 database</comment>
+ <comment xml:lang="ar">قاعدة بيانات SQLite3</comment>
+ <comment xml:lang="be@latin">Baza źviestak SQLite3</comment>
+ <comment xml:lang="bg">База от данни — SQLite3</comment>
+ <comment xml:lang="ca">base de dades SQLite3</comment>
+ <comment xml:lang="cs">databáze SQLite3</comment>
+ <comment xml:lang="da">SQLite3-database</comment>
+ <comment xml:lang="de">SQLite3-Datenbank</comment>
+ <comment xml:lang="el">Βάση δεδομένων SQLite3</comment>
+ <comment xml:lang="en_GB">SQLite3 database</comment>
+ <comment xml:lang="eo">SQLite3-datumbazo</comment>
+ <comment xml:lang="es">base de datos SQLite3</comment>
+ <comment xml:lang="eu">SQLite3 datu-basea</comment>
+ <comment xml:lang="fi">SQLite3-tietokanta</comment>
+ <comment xml:lang="fo">SQLite3 dátustovnur</comment>
+ <comment xml:lang="fr">base de données SQLite3</comment>
+ <comment xml:lang="ga">bunachar sonraí SQLite3</comment>
+ <comment xml:lang="gl">base de datos SQLite3</comment>
+ <comment xml:lang="he">מסד נתונים מסוג SQLite3</comment>
+ <comment xml:lang="hr">SQLite3 baza podataka</comment>
+ <comment xml:lang="hu">SQLite3 adatbázis</comment>
+ <comment xml:lang="ia">Base de datos SQLite3</comment>
+ <comment xml:lang="id">Basis data SQLite3</comment>
+ <comment xml:lang="it">Database SQLite3</comment>
+ <comment xml:lang="ja">SQLite3 データベース</comment>
+ <comment xml:lang="kk">SQLite3 дерекқоры</comment>
+ <comment xml:lang="ko">SQLite3 데이터베이스</comment>
+ <comment xml:lang="lt">SQLite3 duomenų bazė</comment>
+ <comment xml:lang="lv">SQLite3 datubāze</comment>
+ <comment xml:lang="nb">SQLite3-database</comment>
+ <comment xml:lang="nl">SQLite3-gegevensbank</comment>
+ <comment xml:lang="nn">SQLite3-database</comment>
+ <comment xml:lang="oc">banca de donadas SQLite3</comment>
+ <comment xml:lang="pl">Baza danych SQLite3</comment>
+ <comment xml:lang="pt">base de dados SQLite3</comment>
+ <comment xml:lang="pt_BR">Banco de dados SQLite3</comment>
+ <comment xml:lang="ro">Bază de date SQLite3</comment>
+ <comment xml:lang="ru">База данных SQLite3</comment>
+ <comment xml:lang="sk">Databáza SQLite3</comment>
+ <comment xml:lang="sl">Podatkovna zbirka SQLite3</comment>
+ <comment xml:lang="sq">Bazë me të dhëna SQLite3</comment>
+ <comment xml:lang="sr">СКуЛајт3 база података</comment>
+ <comment xml:lang="sv">SQLite3-databas</comment>
+ <comment xml:lang="tr">SQLite3 veritabanı</comment>
+ <comment xml:lang="uk">база даних SQLite3</comment>
+ <comment xml:lang="vi">Cơ sở dữ liệu SQLite3</comment>
+ <comment xml:lang="zh_CN">SQLite3 数据库</comment>
+ <comment xml:lang="zh_TW">SQLite3 資料庫</comment>
+ <glob pattern="*.sqlite3"/>
+ <magic>
+ <match value="SQLite format 3" type="string" offset="0"/>
+ </magic>
+ <alias type="application/x-sqlite3"/>
+ </mime-type>
+ <mime-type type="application/x-gedcom">
+ <comment>GEDCOM family history</comment>
+ <comment xml:lang="ar">تاريخ عائلة GEDCOM</comment>
+ <comment xml:lang="be@latin">Siamiejnaja historyja GEDCOM</comment>
+ <comment xml:lang="bg">Родословно дърво — GEDCOM</comment>
+ <comment xml:lang="ca">antecedents familiars GEDCOM</comment>
+ <comment xml:lang="cs">rodokmen GEDCOM</comment>
+ <comment xml:lang="da">GEDCOM-familiehistorie</comment>
+ <comment xml:lang="de">GEDCOM-Stammbaum</comment>
+ <comment xml:lang="el">Οικογενειακό ιστορικό GEDCOM</comment>
+ <comment xml:lang="en_GB">GEDCOM family history</comment>
+ <comment xml:lang="es">historial familiar de GEDCOM</comment>
+ <comment xml:lang="eu">GEDCOM famili historia</comment>
+ <comment xml:lang="fi">GEDCOM-sukuhistoria</comment>
+ <comment xml:lang="fo">GEDCOM familjusøga</comment>
+ <comment xml:lang="fr">généalogie GEDCOM</comment>
+ <comment xml:lang="ga">stair theaghlach GEDCOM</comment>
+ <comment xml:lang="gl">historial de familia GEDCOM</comment>
+ <comment xml:lang="he">היסטוריה משפחתית של GEDCOM</comment>
+ <comment xml:lang="hr">GEDCOM obiteljska povijest</comment>
+ <comment xml:lang="hu">GEDCOM családtörténet</comment>
+ <comment xml:lang="ia">Genealogia GEDCOM</comment>
+ <comment xml:lang="id">Sejarah keluarga GEDCOM</comment>
+ <comment xml:lang="it">Cronologia famiglia GEDCOM</comment>
+ <comment xml:lang="ja">GEDCOM 家系図データ</comment>
+ <comment xml:lang="ka">GEDCOM ოჯახის ისტორია</comment>
+ <comment xml:lang="kk">GEDCOM отбасы тарихы</comment>
+ <comment xml:lang="ko">GEDCOM 패밀리 기록</comment>
+ <comment xml:lang="lt">GEDCOM šeimos istorija</comment>
+ <comment xml:lang="lv">GEDCOM ģimenes vēsture</comment>
+ <comment xml:lang="nb">GEDCOM-familiehistorikk</comment>
+ <comment xml:lang="nl">GEDCOM-stamboom</comment>
+ <comment xml:lang="nn">GEDCOM-familehistorie</comment>
+ <comment xml:lang="oc">genealogia GEDCOM</comment>
+ <comment xml:lang="pl">Plik historii rodziny GEDCOM</comment>
+ <comment xml:lang="pt">história familiar GEDCOM</comment>
+ <comment xml:lang="pt_BR">Histórico familiar do GEDCOM</comment>
+ <comment xml:lang="ro">Tablou genealogic GEDCOM</comment>
+ <comment xml:lang="ru">История семьи GEDCOM</comment>
+ <comment xml:lang="sk">Rodokmeň GEDCOM</comment>
+ <comment xml:lang="sl">Datoteka družinske zgodovine GEDCOM</comment>
+ <comment xml:lang="sq">Kronollogji familje GEDCOM</comment>
+ <comment xml:lang="sr">ГЕДКОМ историјат породице</comment>
+ <comment xml:lang="sv">GEDCOM-släktträd</comment>
+ <comment xml:lang="tr">GEDCOM aile geçmişi</comment>
+ <comment xml:lang="uk">історія родини GEDCOM</comment>
+ <comment xml:lang="vi">Lịch sử gia đình GEDCOM</comment>
+ <comment xml:lang="zh_CN">GEDCOM 家谱</comment>
+ <comment xml:lang="zh_TW">GEDCOM 家族史</comment>
+ <acronym>GEDCOM</acronym>
+ <expanded-acronym>GEnealogical Data COMmunication</expanded-acronym>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="0 HEAD" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.ged"/>
+ <glob pattern="*.gedcom"/>
+ <alias type="text/gedcom"/>
+ </mime-type>
+ <mime-type type="video/x-flv">
+ <comment>Flash video</comment>
+ <comment xml:lang="ar">Flash مرئي</comment>
+ <comment xml:lang="ast">Videu en Flash</comment>
+ <comment xml:lang="be@latin">Videa Flash</comment>
+ <comment xml:lang="bg">Видео — Flash</comment>
+ <comment xml:lang="ca">vídeo de Flash</comment>
+ <comment xml:lang="cs">video Flash</comment>
+ <comment xml:lang="da">Flashvideo</comment>
+ <comment xml:lang="de">Flash-Video</comment>
+ <comment xml:lang="el">Βίντεο Flash</comment>
+ <comment xml:lang="en_GB">Flash video</comment>
+ <comment xml:lang="eo">Flash-video</comment>
+ <comment xml:lang="es">vídeo Flash</comment>
+ <comment xml:lang="eu">Flash bideoa</comment>
+ <comment xml:lang="fi">Flash-video</comment>
+ <comment xml:lang="fo">Flash video</comment>
+ <comment xml:lang="fr">vidéo Flash</comment>
+ <comment xml:lang="ga">físeán Flash</comment>
+ <comment xml:lang="gl">vídeo Flash</comment>
+ <comment xml:lang="he">וידאו של פלאש</comment>
+ <comment xml:lang="hr">Flash video snimka</comment>
+ <comment xml:lang="hu">Flash videó</comment>
+ <comment xml:lang="ia">Video Flash</comment>
+ <comment xml:lang="id">Video Flash</comment>
+ <comment xml:lang="it">Video Flash</comment>
+ <comment xml:lang="ja">Flash 動画</comment>
+ <comment xml:lang="ka">Flash-ის ვიდეო</comment>
+ <comment xml:lang="kk">Flash видеосы</comment>
+ <comment xml:lang="ko">Flash 동영상</comment>
+ <comment xml:lang="lt">Flash vaizdo įrašas</comment>
+ <comment xml:lang="lv">Flash video</comment>
+ <comment xml:lang="nb">Flash-film</comment>
+ <comment xml:lang="nl">Flash-video</comment>
+ <comment xml:lang="nn">Flash-video</comment>
+ <comment xml:lang="oc">vidèo Flash</comment>
+ <comment xml:lang="pl">Plik wideo Flash</comment>
+ <comment xml:lang="pt">vídeo Flash</comment>
+ <comment xml:lang="pt_BR">Vídeo Flash</comment>
+ <comment xml:lang="ro">Video Flash</comment>
+ <comment xml:lang="ru">Видео Flash</comment>
+ <comment xml:lang="sk">Video Flash</comment>
+ <comment xml:lang="sl">Video datoteka Flash</comment>
+ <comment xml:lang="sq">Video Flash</comment>
+ <comment xml:lang="sr">Флеш видео</comment>
+ <comment xml:lang="sv">Flash-video</comment>
+ <comment xml:lang="tr">Flash video</comment>
+ <comment xml:lang="uk">відеокліп Flash</comment>
+ <comment xml:lang="vi">Ảnh động Flash</comment>
+ <comment xml:lang="zh_CN">Flash 视频</comment>
+ <comment xml:lang="zh_TW">Flash 視訊</comment>
+ <generic-icon name="video-x-generic"/>
+ <magic priority="50">
+ <match value="FLV" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.flv"/>
+ <alias type="application/x-flash-video"/>
+ <alias type="flv-application/octet-stream"/>
+ <alias type="video/flv"/>
+ </mime-type>
+ <mime-type type="video/x-javafx">
+ <comment>JavaFX video</comment>
+ <comment xml:lang="ast">Videu en JavaFX</comment>
+ <comment xml:lang="bg">Видео — JavaFX</comment>
+ <comment xml:lang="ca">vídeo de JavaFX</comment>
+ <comment xml:lang="cs">video JavaFX</comment>
+ <comment xml:lang="da">JavaFX-video</comment>
+ <comment xml:lang="de">JavaFX-Video</comment>
+ <comment xml:lang="el">Βίντεο JavaFX</comment>
+ <comment xml:lang="en_GB">JavaFX video</comment>
+ <comment xml:lang="eo">JavaFX-video</comment>
+ <comment xml:lang="es">vídeo JavaFX</comment>
+ <comment xml:lang="eu">JavaFX bideoa</comment>
+ <comment xml:lang="fi">JavaFX-video</comment>
+ <comment xml:lang="fo">JavaFX video</comment>
+ <comment xml:lang="fr">vidéo JavaFX</comment>
+ <comment xml:lang="ga">físeán JavaFX</comment>
+ <comment xml:lang="gl">vídeo JavaFX</comment>
+ <comment xml:lang="he">וידאו JavaFX</comment>
+ <comment xml:lang="hr">JavaFX video snimka</comment>
+ <comment xml:lang="hu">JavaFX videó</comment>
+ <comment xml:lang="ia">Video JavaFX</comment>
+ <comment xml:lang="id">Video JavaFX</comment>
+ <comment xml:lang="it">Video JavaFX</comment>
+ <comment xml:lang="ja">JavaFX 動画</comment>
+ <comment xml:lang="kk">JavaFX аудиосы</comment>
+ <comment xml:lang="ko">JavaFX 동영상</comment>
+ <comment xml:lang="lv">JavaFX video</comment>
+ <comment xml:lang="nl">JavaFX video</comment>
+ <comment xml:lang="oc">vidèo JavaFX</comment>
+ <comment xml:lang="pl">Plik wideo JavaFX</comment>
+ <comment xml:lang="pt">vídeo JavaFX</comment>
+ <comment xml:lang="pt_BR">Vídeo JavaFX</comment>
+ <comment xml:lang="ro">Video JavaFX</comment>
+ <comment xml:lang="ru">Видео JavaFX</comment>
+ <comment xml:lang="sk">Video JavaFX</comment>
+ <comment xml:lang="sl">Video JavaFX</comment>
+ <comment xml:lang="sr">ЈаваФИкс видео</comment>
+ <comment xml:lang="sv">JavaFX-video</comment>
+ <comment xml:lang="tr">JavaFX video</comment>
+ <comment xml:lang="uk">відеокліп JavaFX</comment>
+ <comment xml:lang="zh_CN">JavaFX 视频</comment>
+ <comment xml:lang="zh_TW">JavaFX 視訊</comment>
+ <generic-icon name="video-x-generic"/>
+ <magic priority="40">
+ <match value="FLV" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.fxm"/>
+ <sub-class-of type="video/x-flv"/>
+ </mime-type>
+ <mime-type type="application/x-go-sgf">
+
+ <comment>SGF record</comment>
+ <comment xml:lang="ar">تسجيلة SGF</comment>
+ <comment xml:lang="be@latin">Zapisanaja hulnia SGF</comment>
+ <comment xml:lang="bg">Запис — SGF</comment>
+ <comment xml:lang="ca">registre SGF</comment>
+ <comment xml:lang="cs">nahrávka SGF</comment>
+ <comment xml:lang="da">SGF-optagelse</comment>
+ <comment xml:lang="de">SGF-Aufzeichnung</comment>
+ <comment xml:lang="el">Εγγραφή SGF</comment>
+ <comment xml:lang="en_GB">SGF record</comment>
+ <comment xml:lang="es">grabación SGF</comment>
+ <comment xml:lang="eu">SGF erregistroa</comment>
+ <comment xml:lang="fi">SGF-nauhoitus</comment>
+ <comment xml:lang="fo">SGF met</comment>
+ <comment xml:lang="fr">partie SGF</comment>
+ <comment xml:lang="ga">taifead SGF</comment>
+ <comment xml:lang="gl">Grabación SGF</comment>
+ <comment xml:lang="he">הקלטת SGF</comment>
+ <comment xml:lang="hr">SGF zapis</comment>
+ <comment xml:lang="hu">SGF pontszám</comment>
+ <comment xml:lang="ia">Partita SGF</comment>
+ <comment xml:lang="id">Catatan SGF</comment>
+ <comment xml:lang="it">Registrazione SGF</comment>
+ <comment xml:lang="ja">SGF レコード</comment>
+ <comment xml:lang="kk">SGF жазбасы</comment>
+ <comment xml:lang="ko">SGF 기록 파일</comment>
+ <comment xml:lang="lt">SGF įrašas</comment>
+ <comment xml:lang="lv">SGF ieraksts</comment>
+ <comment xml:lang="nb">SGF-oppføring</comment>
+ <comment xml:lang="nl">SGF-record</comment>
+ <comment xml:lang="nn">SGF-logg</comment>
+ <comment xml:lang="oc">partida SGF</comment>
+ <comment xml:lang="pl">Zapis gry SGF</comment>
+ <comment xml:lang="pt">gravação SGF</comment>
+ <comment xml:lang="pt_BR">Gravação SGF</comment>
+ <comment xml:lang="ro">Înregistrare SGF</comment>
+ <comment xml:lang="ru">Запись SGF</comment>
+ <comment xml:lang="sk">Záznam SGF</comment>
+ <comment xml:lang="sl">Datoteka shranjene igre SGF</comment>
+ <comment xml:lang="sq">Regjistrim SGF</comment>
+ <comment xml:lang="sr">СГФ запис</comment>
+ <comment xml:lang="sv">SGF-protokoll</comment>
+ <comment xml:lang="tr">SGF kaydı</comment>
+ <comment xml:lang="uk">запис SGF</comment>
+ <comment xml:lang="vi">Mục ghi SGF</comment>
+ <comment xml:lang="zh_CN">SGF 记录</comment>
+ <comment xml:lang="zh_TW">SGF 紀錄</comment>
+ <acronym>SGF</acronym>
+ <expanded-acronym>Smart Game Format</expanded-acronym>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.sgf"/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="(;FF[3]" type="string" offset="0"/>
+ <match value="(;FF[4]" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/xliff+xml">
+ <comment>XLIFF translation file</comment>
+ <comment xml:lang="ar">ملف ترجمة XLIFF</comment>
+ <comment xml:lang="ast">Ficheru de traducciones XLIFF</comment>
+ <comment xml:lang="be@latin">Fajł pierakładu XLIFF</comment>
+ <comment xml:lang="bg">Превод — XLIFF</comment>
+ <comment xml:lang="ca">fitxer de traducció XLIFF</comment>
+ <comment xml:lang="cs">soubor překladu XLIFF</comment>
+ <comment xml:lang="da">XLIFF-oversættelsesfil</comment>
+ <comment xml:lang="de">XLIFF-Übersetzung</comment>
+ <comment xml:lang="el">Αρχείο μετάφρασης XLIFF</comment>
+ <comment xml:lang="en_GB">XLIFF translation file</comment>
+ <comment xml:lang="es">archivo de traducción XLIFF</comment>
+ <comment xml:lang="eu">XLIFF itzulpen-fitxategia</comment>
+ <comment xml:lang="fi">XLIFF-käännöstiedosto</comment>
+ <comment xml:lang="fo">XLIFF týðingarfíla</comment>
+ <comment xml:lang="fr">fichier de traduction XLIFF</comment>
+ <comment xml:lang="ga">comhad aistriúcháin XLIFF</comment>
+ <comment xml:lang="gl">ficheiro de tradución XLIFF</comment>
+ <comment xml:lang="he">קובץ תרגום CLIFF</comment>
+ <comment xml:lang="hr">XLIFF datoteka prijevoda</comment>
+ <comment xml:lang="hu">XLIFF fordítási fájl</comment>
+ <comment xml:lang="ia">File de traduction XLIFF</comment>
+ <comment xml:lang="id">Berkas terjemahan XLIFF</comment>
+ <comment xml:lang="it">File traduzione XLIFF</comment>
+ <comment xml:lang="ja">XLIFF 翻訳ファイル</comment>
+ <comment xml:lang="kk">XLIFF аударма файлы</comment>
+ <comment xml:lang="ko">XLIFF 번역 파일</comment>
+ <comment xml:lang="lt">XLIFF vertimo failas</comment>
+ <comment xml:lang="lv">XLIFF tulkošanas datne</comment>
+ <comment xml:lang="nb">XLIFF-oversettelsesfil</comment>
+ <comment xml:lang="nl">XLIFF-vertalingsbestand</comment>
+ <comment xml:lang="nn">XLIFF-omsetjingsfil</comment>
+ <comment xml:lang="oc">fichièr de traduccion XLIFF</comment>
+ <comment xml:lang="pl">Plik tłumaczenia XLIFF</comment>
+ <comment xml:lang="pt">ficheiro de tradução XLIFF</comment>
+ <comment xml:lang="pt_BR">Arquivo de tradução XLIFF</comment>
+ <comment xml:lang="ro">Fișier de traducere XLIFF</comment>
+ <comment xml:lang="ru">Файл перевода XLIFF</comment>
+ <comment xml:lang="sk">Súbor prekladu XLIFF</comment>
+ <comment xml:lang="sl">Datoteka prevoda XLIFF</comment>
+ <comment xml:lang="sq">File përkthimesh XLIFF</comment>
+ <comment xml:lang="sr">ИксЛИФФ датотека превода</comment>
+ <comment xml:lang="sv">XLIFF-översättningsfil</comment>
+ <comment xml:lang="tr">XLIFF çeviri dosyası</comment>
+ <comment xml:lang="uk">файл перекладу XLIFF</comment>
+ <comment xml:lang="vi">Tập tin dịch XLIFF</comment>
+ <comment xml:lang="zh_CN">XLIFF 翻译文件</comment>
+ <comment xml:lang="zh_TW">XLIFF 翻譯檔</comment>
+ <acronym>XLIFF</acronym>
+ <expanded-acronym>XML Localization Interchange File Format</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.xlf"/>
+ <glob pattern="*.xliff"/>
+ <magic priority="80">
+ <match value="&lt;xliff" type="string" offset="0:256"/>
+ </magic>
+ <root-XML namespaceURI='urn:oasis:names:tc:xliff:document:1.1' localName='xliff'/>
+ <alias type="application/x-xliff"/>
+ </mime-type>
+ <mime-type type="application/x-yaml">
+ <comment>YAML document</comment>
+ <comment xml:lang="ar">مستند YAML</comment>
+ <comment xml:lang="ast">Documentu YAML</comment>
+ <comment xml:lang="bg">Документ — YAML</comment>
+ <comment xml:lang="ca">document YAML</comment>
+ <comment xml:lang="cs">dokument YAML</comment>
+ <comment xml:lang="da">YAML-dokument</comment>
+ <comment xml:lang="de">YAML-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο YAML</comment>
+ <comment xml:lang="en_GB">YAML document</comment>
+ <comment xml:lang="eo">YAML-dokumento</comment>
+ <comment xml:lang="es">documento YAML</comment>
+ <comment xml:lang="eu">YAML dokumentua</comment>
+ <comment xml:lang="fi">YAML-asiakirja</comment>
+ <comment xml:lang="fo">YAML skjal</comment>
+ <comment xml:lang="fr">document YAML</comment>
+ <comment xml:lang="ga">cáipéis YAML</comment>
+ <comment xml:lang="gl">documento YAML</comment>
+ <comment xml:lang="he">מסמך YAML</comment>
+ <comment xml:lang="hr">YAML dokument</comment>
+ <comment xml:lang="hu">YAML-dokumentum</comment>
+ <comment xml:lang="ia">Documento YAML</comment>
+ <comment xml:lang="id">Dokumen YAML</comment>
+ <comment xml:lang="it">Documento YAML</comment>
+ <comment xml:lang="ja">YAML ドキュメント</comment>
+ <comment xml:lang="kk">YAML құжаты</comment>
+ <comment xml:lang="ko">YAML 문서</comment>
+ <comment xml:lang="lt">YAML dokumentas</comment>
+ <comment xml:lang="lv">YAML dokuments</comment>
+ <comment xml:lang="nl">YAML document</comment>
+ <comment xml:lang="oc">document YAML</comment>
+ <comment xml:lang="pl">Dokument YAML</comment>
+ <comment xml:lang="pt">documento YAML</comment>
+ <comment xml:lang="pt_BR">Documento YAML</comment>
+ <comment xml:lang="ro">Document YAML</comment>
+ <comment xml:lang="ru">Документ YAML</comment>
+ <comment xml:lang="sk">Dokument YAML</comment>
+ <comment xml:lang="sl">Dokument YAML</comment>
+ <comment xml:lang="sr">ЈАМЛ документ</comment>
+ <comment xml:lang="sv">YAML-dokument</comment>
+ <comment xml:lang="tr">YAML belgesi</comment>
+ <comment xml:lang="uk">документ YAML</comment>
+ <comment xml:lang="zh_CN">YAML 文档</comment>
+ <comment xml:lang="zh_TW">YAML 文件</comment>
+ <acronym>YAML</acronym>
+ <expanded-acronym>YAML Ain't Markup Language</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <magic>
+ <match value="%YAML" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.yaml"/>
+ <glob pattern="*.yml"/>
+ <alias type="text/yaml"/>
+ <alias type="text/x-yaml"/>
+ </mime-type>
+ <mime-type type="application/vnd.corel-draw">
+ <comment>Corel Draw drawing</comment>
+ <comment xml:lang="ar">تصميم Corel Draw</comment>
+ <comment xml:lang="ast">Dibuxu de Corel Draw</comment>
+ <comment xml:lang="az">Corel Draw çəkimi</comment>
+ <comment xml:lang="be@latin">Rysunak Corel Draw</comment>
+ <comment xml:lang="bg">Чертеж — Corel Draw</comment>
+ <comment xml:lang="ca">dibuix de Corel Draw</comment>
+ <comment xml:lang="cs">kresba Corel Draw</comment>
+ <comment xml:lang="cy">Darlun Corel Draw</comment>
+ <comment xml:lang="da">Corel Draw-tegning</comment>
+ <comment xml:lang="de">Corel-Draw-Zeichnung</comment>
+ <comment xml:lang="el">Σχέδιο Corel Draw </comment>
+ <comment xml:lang="en_GB">Corel Draw drawing</comment>
+ <comment xml:lang="eo">grafikaĵo de Corel Draw</comment>
+ <comment xml:lang="es">dibujo de Corel Draw</comment>
+ <comment xml:lang="eu">Corel Draw-eko marrazkia</comment>
+ <comment xml:lang="fi">Corel Draw -piirros</comment>
+ <comment xml:lang="fo">Corel Draw tekning</comment>
+ <comment xml:lang="fr">dessin Corel Draw</comment>
+ <comment xml:lang="ga">líníocht Corel Draw</comment>
+ <comment xml:lang="gl">debuxo de Corel Draw</comment>
+ <comment xml:lang="he">ציור של Corel Draw</comment>
+ <comment xml:lang="hr">Corel Draw crtež</comment>
+ <comment xml:lang="hu">Corel Draw-rajz</comment>
+ <comment xml:lang="ia">Designo Corel Draw</comment>
+ <comment xml:lang="id">Gambar Corel Draw</comment>
+ <comment xml:lang="it">Disegno Corel Draw</comment>
+ <comment xml:lang="ja">Corel Draw ドロー</comment>
+ <comment xml:lang="ka">Corel Draw-ის ნახაზი</comment>
+ <comment xml:lang="kk">Corel Draw суреті</comment>
+ <comment xml:lang="ko">코렐 드로우 드로잉</comment>
+ <comment xml:lang="lt">Corel Draw piešinys</comment>
+ <comment xml:lang="lv">Corel Draw zīmējums</comment>
+ <comment xml:lang="ms">Lukisan Corel Draw</comment>
+ <comment xml:lang="nb">Corel Draw-tegning</comment>
+ <comment xml:lang="nl">Corel Draw-tekening</comment>
+ <comment xml:lang="nn">Corel Draw-teikning</comment>
+ <comment xml:lang="oc">dessenh Corel Draw</comment>
+ <comment xml:lang="pl">Rysunek Corel Draw</comment>
+ <comment xml:lang="pt">desenho Corel Drawdesenho Corel Draw</comment>
+ <comment xml:lang="pt_BR">Desenho do Corel Draw</comment>
+ <comment xml:lang="ro">Desen Corel Draw</comment>
+ <comment xml:lang="ru">Рисунок Corel Draw</comment>
+ <comment xml:lang="sk">Kresba Corel Draw</comment>
+ <comment xml:lang="sl">Datoteka risbe Corel Draw</comment>
+ <comment xml:lang="sq">Vizatim Corel Draw</comment>
+ <comment xml:lang="sr">Корелов цртеж</comment>
+ <comment xml:lang="sv">Corel Draw-teckning</comment>
+ <comment xml:lang="tr">Corel Draw çizimi</comment>
+ <comment xml:lang="uk">малюнок Corel Draw</comment>
+ <comment xml:lang="vi">Bản vẽ Corel Draw</comment>
+ <comment xml:lang="zh_CN">Corel Draw 绘图</comment>
+ <comment xml:lang="zh_TW">Corel Draw 繪圖</comment>
+ <generic-icon name="image-x-generic"/>
+ <magic priority="80">
+ <match value="CDRXvrsn" type="string" offset="8" mask="0xffffff00ffffffff"/>
+ </magic>
+ <glob pattern="*.cdr"/>
+ <alias type="application/cdr"/>
+ <alias type="application/coreldraw"/>
+ <alias type="application/x-cdr"/>
+ <alias type="application/x-coreldraw"/>
+ <alias type="image/cdr"/>
+ <alias type="image/x-cdr"/>
+ <alias type="zz-application/zz-winassoc-cdr"/>
+ </mime-type>
+ <mime-type type="application/vnd.hp-hpgl">
+ <comment>HPGL file</comment>
+ <comment xml:lang="ar">ملف HPGL</comment>
+ <comment xml:lang="ast">Ficheru HPGL</comment>
+ <comment xml:lang="be@latin">Fajł HPGL</comment>
+ <comment xml:lang="bg">Файл — HPGL</comment>
+ <comment xml:lang="ca">fitxer HPGL</comment>
+ <comment xml:lang="cs">soubor HPGL</comment>
+ <comment xml:lang="da">HPGL-fil</comment>
+ <comment xml:lang="de">HPGL-Datei</comment>
+ <comment xml:lang="el">Αρχείο HPGL</comment>
+ <comment xml:lang="en_GB">HPGL file</comment>
+ <comment xml:lang="eo">HPGL-dosiero</comment>
+ <comment xml:lang="es">archivo HPGL</comment>
+ <comment xml:lang="eu">HPGL fitxategia</comment>
+ <comment xml:lang="fi">HPGL-tiedosto</comment>
+ <comment xml:lang="fo">HPGL fíla</comment>
+ <comment xml:lang="fr">fichier HPGL</comment>
+ <comment xml:lang="ga">comhad HPGL</comment>
+ <comment xml:lang="gl">ficheiro HPGL</comment>
+ <comment xml:lang="he">קובץ HGPL</comment>
+ <comment xml:lang="hr">HPGL datoteka</comment>
+ <comment xml:lang="hu">HPGL fájl</comment>
+ <comment xml:lang="ia">File HPGL</comment>
+ <comment xml:lang="id">Berkas HPGL</comment>
+ <comment xml:lang="it">File HPGL</comment>
+ <comment xml:lang="ja">HPGL ファイル</comment>
+ <comment xml:lang="kk">HPGL файлы</comment>
+ <comment xml:lang="ko">HPGL 파일</comment>
+ <comment xml:lang="lt">HPGL failas</comment>
+ <comment xml:lang="lv">HPGL datne</comment>
+ <comment xml:lang="nb">HPGL-fil</comment>
+ <comment xml:lang="nl">HPGL-bestand</comment>
+ <comment xml:lang="nn">HPGL-fil</comment>
+ <comment xml:lang="oc">fichièr HPGL</comment>
+ <comment xml:lang="pl">Plik HPGL</comment>
+ <comment xml:lang="pt">ficheiro HPGL</comment>
+ <comment xml:lang="pt_BR">Arquivo HPGL</comment>
+ <comment xml:lang="ro">Fișier HPGL</comment>
+ <comment xml:lang="ru">Файл HPGL</comment>
+ <comment xml:lang="sk">Súbor HPGL</comment>
+ <comment xml:lang="sl">Datoteka HPGL</comment>
+ <comment xml:lang="sq">File HPGL</comment>
+ <comment xml:lang="sr">ХПГЛ датотека</comment>
+ <comment xml:lang="sv">HPGL-fil</comment>
+ <comment xml:lang="tr">HPGL dosyası</comment>
+ <comment xml:lang="uk">файл HPGL</comment>
+ <comment xml:lang="vi">Tập tin HPGL</comment>
+ <comment xml:lang="zh_CN">HPGL 文件</comment>
+ <comment xml:lang="zh_TW">HPGL 檔案</comment>
+ <acronym>HPGL</acronym>
+ <expanded-acronym>HP Graphics Language</expanded-acronym>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.hpgl"/>
+ </mime-type>
+ <mime-type type="application/vnd.hp-pcl">
+ <comment>PCL file</comment>
+ <comment xml:lang="ar">ملف PCL</comment>
+ <comment xml:lang="ast">FIcheru PCL</comment>
+ <comment xml:lang="be@latin">Fajł PCL</comment>
+ <comment xml:lang="bg">Файл — PCL</comment>
+ <comment xml:lang="ca">fitxer PCL</comment>
+ <comment xml:lang="cs">soubor PCL</comment>
+ <comment xml:lang="da">PCL-fil</comment>
+ <comment xml:lang="de">PCL-Datei</comment>
+ <comment xml:lang="el">Αρχείο PCL</comment>
+ <comment xml:lang="en_GB">PCL file</comment>
+ <comment xml:lang="eo">PCL-dosiero</comment>
+ <comment xml:lang="es">archivo PCL</comment>
+ <comment xml:lang="eu">PCL fitxategia</comment>
+ <comment xml:lang="fi">PCL-tiedosto</comment>
+ <comment xml:lang="fo">PCL fíla</comment>
+ <comment xml:lang="fr">fichier PCL</comment>
+ <comment xml:lang="ga">comhad PCL</comment>
+ <comment xml:lang="gl">ficheiro PCL</comment>
+ <comment xml:lang="he">קובץ PCL</comment>
+ <comment xml:lang="hr">PCL datoteka</comment>
+ <comment xml:lang="hu">PCL fájl</comment>
+ <comment xml:lang="ia">File PCL</comment>
+ <comment xml:lang="id">Berkas PCL</comment>
+ <comment xml:lang="it">File PCL</comment>
+ <comment xml:lang="ja">PCL ファイル</comment>
+ <comment xml:lang="kk">PCL файлы</comment>
+ <comment xml:lang="ko">PCL 파일</comment>
+ <comment xml:lang="lt">PCL failas</comment>
+ <comment xml:lang="lv">PCL datne</comment>
+ <comment xml:lang="nb">PCL-fil</comment>
+ <comment xml:lang="nl">PCL-bestand</comment>
+ <comment xml:lang="nn">PCL-fil</comment>
+ <comment xml:lang="oc">fichièr PCL</comment>
+ <comment xml:lang="pl">Plik PCL</comment>
+ <comment xml:lang="pt">ficheiro PCL</comment>
+ <comment xml:lang="pt_BR">Arquivo PCL</comment>
+ <comment xml:lang="ro">Fișier PCL</comment>
+ <comment xml:lang="ru">Файл PCL</comment>
+ <comment xml:lang="sk">Súbor PCL</comment>
+ <comment xml:lang="sl">Datoteka PCL</comment>
+ <comment xml:lang="sq">File PCL</comment>
+ <comment xml:lang="sr">ПЦЛ датотека</comment>
+ <comment xml:lang="sv">PCL-fil</comment>
+ <comment xml:lang="tr">PCL dosyası</comment>
+ <comment xml:lang="uk">файл PCL</comment>
+ <comment xml:lang="vi">Tập tin PCL</comment>
+ <comment xml:lang="zh_CN">PCL 文件</comment>
+ <comment xml:lang="zh_TW">PCL 檔</comment>
+ <acronym>PCL</acronym>
+ <expanded-acronym>HP Printer Control Language</expanded-acronym>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.pcl"/>
+ </mime-type>
+ <mime-type type="application/vnd.lotus-1-2-3">
+ <comment>Lotus 1-2-3 spreadsheet</comment>
+ <comment xml:lang="ar">جدول Lotus 1-2-3</comment>
+ <comment xml:lang="ast">Fueya de cálculu de Lotus 1-2-3</comment>
+ <comment xml:lang="az">Lotus 1-2-3 hesab cədvəli</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš Lotus 1-2-3</comment>
+ <comment xml:lang="bg">Таблица — Lotus 1-2-3</comment>
+ <comment xml:lang="ca">full de càlcul de Lotus 1-2-3</comment>
+ <comment xml:lang="cs">sešit Lotus 1-2-3</comment>
+ <comment xml:lang="cy">Taenlen Lotus 1-2-3</comment>
+ <comment xml:lang="da">Lotus 1-2-3-regneark</comment>
+ <comment xml:lang="de">Lotus-1-2-3-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο Lotus 1-2-3</comment>
+ <comment xml:lang="en_GB">Lotus 1-2-3 spreadsheet</comment>
+ <comment xml:lang="eo">Kalkultabelo de Lotus 1-2-3</comment>
+ <comment xml:lang="es">hoja de cálculo de Lotus 1-2-3</comment>
+ <comment xml:lang="eu">Lotus 1-2-3 kalkulu-orria</comment>
+ <comment xml:lang="fi">Lotus 1-2-3 -taulukko</comment>
+ <comment xml:lang="fo">Lotus 1-2-3 rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul Lotus 1-2-3</comment>
+ <comment xml:lang="ga">scarbhileog Lotus 1-2-3</comment>
+ <comment xml:lang="gl">folla de cálculo de Lotus 1-2-3</comment>
+ <comment xml:lang="he">גיליון נתונים של Lotus 1-2-3</comment>
+ <comment xml:lang="hr">Lotus 1-2-3 proračunska tablica</comment>
+ <comment xml:lang="hu">Lotus 1-2-3-munkafüzet</comment>
+ <comment xml:lang="ia">Folio de calculo Lotus 1-2-3</comment>
+ <comment xml:lang="id">Lembar sebar Lotus 1-2-3</comment>
+ <comment xml:lang="it">Foglio di calcolo Lotus 1-2-3</comment>
+ <comment xml:lang="ja">Lotus 1-2-3 スプレッドシート</comment>
+ <comment xml:lang="kk">Lotus 1-2-3 электрондық кестесі</comment>
+ <comment xml:lang="ko">Lotus 1-2-3 스프레드시트</comment>
+ <comment xml:lang="lt">Lotus 1-2-3 skaičialentė</comment>
+ <comment xml:lang="lv">Lotus 1-2-3 izklājlapa</comment>
+ <comment xml:lang="ms">Hamparan Lotus 1-2-3</comment>
+ <comment xml:lang="nb">Lotus 1-2-3 regneark</comment>
+ <comment xml:lang="nl">Lotus 1-2-3-rekenblad</comment>
+ <comment xml:lang="nn">Lotus 1-2-3 rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul Lotus 1-2-3</comment>
+ <comment xml:lang="pl">Arkusz Lotus 1-2-3</comment>
+ <comment xml:lang="pt">folha de cálculo Lotus 1-2-3</comment>
+ <comment xml:lang="pt_BR">Planilha do Lotus 1-2-3</comment>
+ <comment xml:lang="ro">Foaie de calcul Lotus 1-2-3</comment>
+ <comment xml:lang="ru">Электронная таблица Lotus 1-2-3</comment>
+ <comment xml:lang="sk">Zošit Lotus 1-2-3</comment>
+ <comment xml:lang="sl">Preglednica Lotus 1-2-3</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh Lotus 1-2-3</comment>
+ <comment xml:lang="sr">Лотусова 1-2-3 табела</comment>
+ <comment xml:lang="sv">Lotus 1-2-3-kalkylblad</comment>
+ <comment xml:lang="tr">Lotus 1-2-3 hesap tablosu</comment>
+ <comment xml:lang="uk">ел. таблиця Lotus 1-2-3</comment>
+ <comment xml:lang="vi">Bảng tính Lotus 1-2-3</comment>
+ <comment xml:lang="zh_CN">Lotus 1-2-3 电子表格</comment>
+ <comment xml:lang="zh_TW">Lotus 1-2-3 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="50">
+ <match value="\x00\x00\x02\x00\x06\x04\x06\x00\x08\x00\x00\x00\x00\x00" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.123"/>
+ <glob pattern="*.wk1"/>
+ <glob pattern="*.wk3"/>
+ <glob pattern="*.wk4"/>
+ <glob pattern="*.wks"/>
+ <alias type="application/x-lotus123"/>
+ <alias type="application/x-123"/>
+ <alias type="application/lotus123"/>
+ <alias type="application/wk1"/>
+ <alias type="zz-application/zz-winassoc-123"/>
+ </mime-type>
+ <mime-type type="application/vnd.lotus-wordpro">
+ <comment>Lotus Word Pro</comment>
+ <comment xml:lang="ast">Lotus Word Pro</comment>
+ <comment xml:lang="ca">Lotus Word Pro</comment>
+ <comment xml:lang="cs">Lotus Word Pro</comment>
+ <comment xml:lang="da">Lotus Word Pro</comment>
+ <comment xml:lang="de">Lotus Word Pro</comment>
+ <comment xml:lang="el">Lotus Word Pro</comment>
+ <comment xml:lang="en_GB">Lotus Word Pro</comment>
+ <comment xml:lang="es">Lotus Word Pro</comment>
+ <comment xml:lang="eu">Lotus Word Pro</comment>
+ <comment xml:lang="fi">Lotus Word Pro</comment>
+ <comment xml:lang="fr">Lotus Word Pro</comment>
+ <comment xml:lang="ga">Lotus Word Pro</comment>
+ <comment xml:lang="gl">Lotus Word Pro</comment>
+ <comment xml:lang="he">Lotus Word Pro</comment>
+ <comment xml:lang="hr">Lotus Word Pro</comment>
+ <comment xml:lang="hu">Lotus Word Pro</comment>
+ <comment xml:lang="ia">Lotus Word Pro</comment>
+ <comment xml:lang="id">Lotus Word Pro</comment>
+ <comment xml:lang="it">Lotus Word Pro</comment>
+ <comment xml:lang="ja">Lotus Word Pro</comment>
+ <comment xml:lang="kk">Lotus Word Pro</comment>
+ <comment xml:lang="ko">Lotus Word Pro</comment>
+ <comment xml:lang="lv">Lotus Word Pro</comment>
+ <comment xml:lang="oc">Lotus Word Pro</comment>
+ <comment xml:lang="pl">Lotus Word Pro</comment>
+ <comment xml:lang="pt">Lotus Word Pro</comment>
+ <comment xml:lang="pt_BR">Lotus Word Pro</comment>
+ <comment xml:lang="ru">Lotus Word Pro</comment>
+ <comment xml:lang="sk">Lotus Word Pro</comment>
+ <comment xml:lang="sl">Lotus Word Pro</comment>
+ <comment xml:lang="sr">Лотусов Писац Про</comment>
+ <comment xml:lang="sv">Lotus Word Pro</comment>
+ <comment xml:lang="tr">Lotus Word Pro</comment>
+ <comment xml:lang="uk">Lotus Word Pro</comment>
+ <comment xml:lang="zh_CN">Lotus Word Pro</comment>
+ <comment xml:lang="zh_TW">Lotus Word Pro</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="WordPro" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.lwp"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-access">
+ <comment>JET database</comment>
+ <comment xml:lang="ar">قاعدة بيانات JET</comment>
+ <comment xml:lang="ast">Base de datos JETº</comment>
+ <comment xml:lang="be@latin">Baza źviestak JET</comment>
+ <comment xml:lang="bg">База от данни — JET</comment>
+ <comment xml:lang="ca">base de dades JET</comment>
+ <comment xml:lang="cs">databáze JET</comment>
+ <comment xml:lang="da">JET-database</comment>
+ <comment xml:lang="de">JET-Datenbank</comment>
+ <comment xml:lang="el">Βάση δεδομένων JET</comment>
+ <comment xml:lang="en_GB">JET database</comment>
+ <comment xml:lang="eo">JET-datumbazo</comment>
+ <comment xml:lang="es">base de datos JET</comment>
+ <comment xml:lang="eu">JET datu-basea</comment>
+ <comment xml:lang="fi">JET-tietokanta</comment>
+ <comment xml:lang="fo">JET dátustovnur</comment>
+ <comment xml:lang="fr">base de données JET</comment>
+ <comment xml:lang="ga">bunachar sonraí JET</comment>
+ <comment xml:lang="gl">base de datos JET</comment>
+ <comment xml:lang="he">מסד נתונים מסוג JET</comment>
+ <comment xml:lang="hr">JET baza podataka</comment>
+ <comment xml:lang="hu">JET adatbázis</comment>
+ <comment xml:lang="ia">Base de datos JET</comment>
+ <comment xml:lang="id">Basis data JET</comment>
+ <comment xml:lang="it">Database JET</comment>
+ <comment xml:lang="ja">JET データベース</comment>
+ <comment xml:lang="kk">JET дерекқоры</comment>
+ <comment xml:lang="ko">JET 데이터베이스</comment>
+ <comment xml:lang="lt">JET duomenų bazė</comment>
+ <comment xml:lang="lv">JET datubāze</comment>
+ <comment xml:lang="nb">JET-database</comment>
+ <comment xml:lang="nl">JET-gegevensbank</comment>
+ <comment xml:lang="nn">JET-database</comment>
+ <comment xml:lang="oc">banca de donadas JET</comment>
+ <comment xml:lang="pl">Baza Danych JET</comment>
+ <comment xml:lang="pt">base de dados JET</comment>
+ <comment xml:lang="pt_BR">Banco de dados JET</comment>
+ <comment xml:lang="ro">Bază de date JET</comment>
+ <comment xml:lang="ru">База данных JET</comment>
+ <comment xml:lang="sk">Databáza JET</comment>
+ <comment xml:lang="sl">Podatkovna zbirka JET</comment>
+ <comment xml:lang="sq">Bazë me të dhëna JET</comment>
+ <comment xml:lang="sr">ЈЕТ база података</comment>
+ <comment xml:lang="sv">JET-databas</comment>
+ <comment xml:lang="tr">JET veritabanı</comment>
+ <comment xml:lang="uk">База даних JET</comment>
+ <comment xml:lang="vi">Cơ sở dữ liệu JET</comment>
+ <comment xml:lang="zh_CN">JET 数据库</comment>
+ <comment xml:lang="zh_TW">JET 資料庫</comment>
+ <acronym>JET</acronym>
+ <expanded-acronym>Joint Engine Technology</expanded-acronym>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="\x00\x01\x00\x00Standard Jet DB" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mdb"/>
+ <alias type="application/x-msaccess"/>
+ <alias type="application/msaccess"/>
+ <alias type="application/vnd.msaccess"/>
+ <alias type="application/x-msaccess"/>
+ <alias type="application/mdb"/>
+ <alias type="application/x-mdb"/>
+ <alias type="zz-application/zz-winassoc-mdb"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-cab-compressed">
+ <comment>Microsoft Cabinet archive</comment>
+ <comment xml:lang="ar">أرشيف Microsoft Cabinet</comment>
+ <comment xml:lang="bg">Архив — Microsoft Cabinet</comment>
+ <comment xml:lang="ca">arxiu de Microsoft Cabinet</comment>
+ <comment xml:lang="cs">archiv Microsoft Cabinet</comment>
+ <comment xml:lang="da">Microsoft Cabinet-arkiv</comment>
+ <comment xml:lang="de">Microsoft-Cabinet-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Microsoft Cabinet</comment>
+ <comment xml:lang="en_GB">Microsoft Cabinet archive</comment>
+ <comment xml:lang="es">archivador Cabinet de Microsoft</comment>
+ <comment xml:lang="eu">Microsoft Cabinet artxiboa</comment>
+ <comment xml:lang="fi">Microsoft Cabinet -arkisto</comment>
+ <comment xml:lang="fo">Microsoft Cabinet skjalasavn</comment>
+ <comment xml:lang="fr">archive Cab Microsoft</comment>
+ <comment xml:lang="ga">cartlann Microsoft Cabinet</comment>
+ <comment xml:lang="gl">arquivo de Microsoft Cabinet</comment>
+ <comment xml:lang="he">ארכיון CAB (מיקרוסופט)</comment>
+ <comment xml:lang="hr">Microsoft Cabinet arhiva</comment>
+ <comment xml:lang="hu">Microsoft Cabinet archívum</comment>
+ <comment xml:lang="ia">Archivo Microsoft Cabinet</comment>
+ <comment xml:lang="id">Arsip Microsoft Cabinet</comment>
+ <comment xml:lang="it">Archivio Microsoft Cabinet</comment>
+ <comment xml:lang="ja">Microsoft Cabinet アーカイブ</comment>
+ <comment xml:lang="ka">Microsoft-ის Cabinet არქივი</comment>
+ <comment xml:lang="kk">Microsoft Cabinet архиві</comment>
+ <comment xml:lang="ko">Microsoft Cabinte 압축 파일</comment>
+ <comment xml:lang="lt">Microsoft Cabinet archyvas</comment>
+ <comment xml:lang="lv">Microsoft kabineta arhīvs</comment>
+ <comment xml:lang="nl">Microsoft Cabinet-archief</comment>
+ <comment xml:lang="oc">archiu Cab Microsoft</comment>
+ <comment xml:lang="pl">Archiwum Microsoft Cabinet</comment>
+ <comment xml:lang="pt">arquivo Microsoft Cabinet</comment>
+ <comment xml:lang="pt_BR">Pacote Cabinet da Microsoft</comment>
+ <comment xml:lang="ro">Arhivă Microsoft Cabinet</comment>
+ <comment xml:lang="ru">Архив Microsoft Cabinet</comment>
+ <comment xml:lang="sk">Archív Microsoft Cabinet</comment>
+ <comment xml:lang="sl">Datoteka arhiva Microsoft Cabinet</comment>
+ <comment xml:lang="sr">Мајкрософтова кабинет архива</comment>
+ <comment xml:lang="sv">Microsoft Cabinet-arkiv</comment>
+ <comment xml:lang="tr">Microsoft Cabinet arşivi</comment>
+ <comment xml:lang="uk">архів Cabinet Microsoft</comment>
+ <comment xml:lang="vi">Kho lưu Cabinet Microsoft</comment>
+ <comment xml:lang="zh_CN">Microsoft Cabinet 归档文件</comment>
+ <comment xml:lang="zh_TW">微軟 Cabinet 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="MSCF\0\0\0\0" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.cab"/>
+ <alias type="zz-application/zz-winassoc-cab"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-excel">
+ <comment>Excel spreadsheet</comment>
+ <comment xml:lang="ar">جدول Excel</comment>
+ <comment xml:lang="be@latin">Raźlikovy akruš Excel</comment>
+ <comment xml:lang="bg">Таблица — Excel</comment>
+ <comment xml:lang="ca">full de càlcul d'Excel</comment>
+ <comment xml:lang="cs">sešit Excel</comment>
+ <comment xml:lang="da">Excelregneark</comment>
+ <comment xml:lang="de">Excel-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο Excel</comment>
+ <comment xml:lang="en_GB">Excel spreadsheet</comment>
+ <comment xml:lang="eo">Excel-kalkultabelo</comment>
+ <comment xml:lang="es">hoja de cálculo de Excel</comment>
+ <comment xml:lang="eu">Excel kalkulu-orria</comment>
+ <comment xml:lang="fi">Excel-taulukko</comment>
+ <comment xml:lang="fo">Excel rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul Excel</comment>
+ <comment xml:lang="ga">scarbhileog Excel</comment>
+ <comment xml:lang="gl">folla de cálculo de Excel</comment>
+ <comment xml:lang="he">גליון נתונים של Excel</comment>
+ <comment xml:lang="hr">Excel proračunska tablica</comment>
+ <comment xml:lang="hu">Excel táblázat</comment>
+ <comment xml:lang="ia">Folio de calculo Excel</comment>
+ <comment xml:lang="id">Lembar sebar Excel</comment>
+ <comment xml:lang="it">Foglio di calcolo Excel</comment>
+ <comment xml:lang="ja">Excel スプレッドシート</comment>
+ <comment xml:lang="ka">Excel-ის ცხრილი</comment>
+ <comment xml:lang="kk">Excel электрондық кестесі</comment>
+ <comment xml:lang="ko">Excel 스프레드시트</comment>
+ <comment xml:lang="lt">Excel skaičialentė</comment>
+ <comment xml:lang="lv">Excel izklājlapa</comment>
+ <comment xml:lang="nb">Excel regneark</comment>
+ <comment xml:lang="nl">Excel-rekenblad</comment>
+ <comment xml:lang="nn">Excel-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul Excel</comment>
+ <comment xml:lang="pl">Arkusz Excel</comment>
+ <comment xml:lang="pt">folha de cálculo Excel</comment>
+ <comment xml:lang="pt_BR">Planilha do Excel</comment>
+ <comment xml:lang="ro">Foaie de calcul Excel</comment>
+ <comment xml:lang="ru">Электронная таблица Excel</comment>
+ <comment xml:lang="sk">Zošit Excel</comment>
+ <comment xml:lang="sl">Razpredelnica Microsoft Excel</comment>
+ <comment xml:lang="sq">Fletë llogaritje Excel</comment>
+ <comment xml:lang="sr">Екселова табела</comment>
+ <comment xml:lang="sv">Excel-kalkylblad</comment>
+ <comment xml:lang="tr">Excel çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця Excel</comment>
+ <comment xml:lang="vi">Bảng tính Excel</comment>
+ <comment xml:lang="zh_CN">Excel 电子表格</comment>
+ <comment xml:lang="zh_TW">Excel 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="50">
+ <match value="Microsoft Excel 5.0 Worksheet" type="string" offset="2080"/>
+ </magic>
+ <glob pattern="*.xls"/>
+ <glob pattern="*.xlc"/>
+ <glob pattern="*.xll"/>
+ <glob pattern="*.xlm"/>
+ <glob pattern="*.xlw"/>
+ <glob pattern="*.xla"/>
+ <glob pattern="*.xlt"/>
+ <glob pattern="*.xld"/>
+ <alias type="application/msexcel"/>
+ <alias type="application/x-msexcel"/>
+ <alias type="zz-application/zz-winassoc-xls"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-excel.addin.macroEnabled.12">
+ <comment>Excel add-in</comment>
+ <comment xml:lang="bg">Приставка — Excel</comment>
+ <comment xml:lang="ca">complement d'Excel</comment>
+ <comment xml:lang="cs">doplněk aplikace Excel</comment>
+ <comment xml:lang="da">Excel-tilføjelse</comment>
+ <comment xml:lang="de">Excel Add-in</comment>
+ <comment xml:lang="el">Πρόσθετο Excel</comment>
+ <comment xml:lang="en_GB">Excel add-in</comment>
+ <comment xml:lang="es">complemento de Excel</comment>
+ <comment xml:lang="eu">Excel gehigarria</comment>
+ <comment xml:lang="fi">Excel-lisäosa</comment>
+ <comment xml:lang="fr">complément Excel</comment>
+ <comment xml:lang="ga">breiseán Excel</comment>
+ <comment xml:lang="gl">complemento de Excel</comment>
+ <comment xml:lang="he">תוסף של Excel</comment>
+ <comment xml:lang="hr">Excel dodatak</comment>
+ <comment xml:lang="hu">Excel bővítmény</comment>
+ <comment xml:lang="ia">Add-in Excel</comment>
+ <comment xml:lang="id">Add-in Excel</comment>
+ <comment xml:lang="it">Add-in Excel</comment>
+ <comment xml:lang="ja">Excel アドイン</comment>
+ <comment xml:lang="ka">Excel-ის დამატება</comment>
+ <comment xml:lang="kk">Excel қосымшасы</comment>
+ <comment xml:lang="ko">Excel 추가 기능</comment>
+ <comment xml:lang="lv">Excel pievienojumprogramma</comment>
+ <comment xml:lang="nl">Excel add-in</comment>
+ <comment xml:lang="oc">complement Excel</comment>
+ <comment xml:lang="pl">Dodatek Excel</comment>
+ <comment xml:lang="pt">Extensão Excel</comment>
+ <comment xml:lang="pt_BR">Suplemento do Excel</comment>
+ <comment xml:lang="ru">Дополнение Excel</comment>
+ <comment xml:lang="sk">Doplnok aplikácie Excel</comment>
+ <comment xml:lang="sl">Vstavek Excel</comment>
+ <comment xml:lang="sr">Екселов додатак</comment>
+ <comment xml:lang="sv">Excel-tillägg</comment>
+ <comment xml:lang="tr">Excel eklentisi</comment>
+ <comment xml:lang="uk">додаток Excel</comment>
+ <comment xml:lang="zh_CN">Excel 外接程序</comment>
+ <comment xml:lang="zh_TW">Excel 增益集</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.xlam"/>
+ <sub-class-of type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-excel.sheet.binary.macroEnabled.12">
+ <comment>Excel 2007 binary spreadsheet</comment>
+ <comment xml:lang="bg">Таблица — Excel 2007, двоична</comment>
+ <comment xml:lang="ca">full de càlcul binari d'Excel 2007</comment>
+ <comment xml:lang="cs">binární formát sešitu Excel 2007</comment>
+ <comment xml:lang="da">Binært Excel 2007-regneark</comment>
+ <comment xml:lang="de">Excel-2007-Tabelle (binär)</comment>
+ <comment xml:lang="el">Δυαδικό λογιστικό φύλλο Excel 2007</comment>
+ <comment xml:lang="en_GB">Excel 2007 binary spreadsheet</comment>
+ <comment xml:lang="es">hoja de cálculo binaria de Excel 2007</comment>
+ <comment xml:lang="eu">Excel 2007 kalkulu-orri binarioa</comment>
+ <comment xml:lang="fi">Excel 2007:n binaarinen taulukko</comment>
+ <comment xml:lang="fr">feuille de calcul binaire Excel 2007</comment>
+ <comment xml:lang="ga">scarbhileog dhénártha Excel 2007</comment>
+ <comment xml:lang="gl">ficheiro binario de folla de cálculo Excel 2007</comment>
+ <comment xml:lang="he">גיליון נתונים בינרי של Excel 2007</comment>
+ <comment xml:lang="hr">Excel 2007 binarna proračunska tablica</comment>
+ <comment xml:lang="hu">Excel 2007 bináris táblázat</comment>
+ <comment xml:lang="ia">Folio de calculo binari Excel 2007</comment>
+ <comment xml:lang="id">Lembar kerja biner Excel 2007</comment>
+ <comment xml:lang="it">Foglio di calcolo binario Excel 2007</comment>
+ <comment xml:lang="ja">Excel 2007 バイナリスプレッドシート</comment>
+ <comment xml:lang="ka">Excel 2007-ის ბინარული ცხრილი</comment>
+ <comment xml:lang="kk">Excel 2007 бинарды кестесі</comment>
+ <comment xml:lang="ko">Excel 2007 바이너리 스프레드시트</comment>
+ <comment xml:lang="lv">Excel 2007 binārā izklājlapa</comment>
+ <comment xml:lang="nl">Excel 2007 binary spreadsheet</comment>
+ <comment xml:lang="oc">fuèlh de calcul binaire Excel 2007</comment>
+ <comment xml:lang="pl">Binarny arkusz Excel 2007</comment>
+ <comment xml:lang="pt">folha de cálculo binária Excel 2007</comment>
+ <comment xml:lang="pt_BR">Planilha binária do Excel 2007</comment>
+ <comment xml:lang="ru">Двоичная электронная таблица Excel 2007</comment>
+ <comment xml:lang="sk">Binárny zošit Excel 2007</comment>
+ <comment xml:lang="sl">Binarna preglednica Excel 2007</comment>
+ <comment xml:lang="sr">Ексел 2007 бинарна табела</comment>
+ <comment xml:lang="sv">Binärt Excel 2007-kalkylblad</comment>
+ <comment xml:lang="tr">Excel 2007 ikilik çalışma sayfası</comment>
+ <comment xml:lang="uk">бінарна електронна таблиця Excel 2007</comment>
+ <comment xml:lang="zh_CN">Excel 2007 二进制电子表格</comment>
+ <comment xml:lang="zh_TW">Excel 2007 二進位試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.xlsb"/>
+ <sub-class-of type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-excel.sheet.macroEnabled.12">
+ <comment>Excel spreadsheet</comment>
+ <comment xml:lang="ar">جدول Excel</comment>
+ <comment xml:lang="be@latin">Raźlikovy akruš Excel</comment>
+ <comment xml:lang="bg">Таблица — Excel</comment>
+ <comment xml:lang="ca">full de càlcul d'Excel</comment>
+ <comment xml:lang="cs">sešit Excel</comment>
+ <comment xml:lang="da">Excelregneark</comment>
+ <comment xml:lang="de">Excel-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο Excel</comment>
+ <comment xml:lang="en_GB">Excel spreadsheet</comment>
+ <comment xml:lang="eo">Excel-kalkultabelo</comment>
+ <comment xml:lang="es">hoja de cálculo de Excel</comment>
+ <comment xml:lang="eu">Excel kalkulu-orria</comment>
+ <comment xml:lang="fi">Excel-taulukko</comment>
+ <comment xml:lang="fo">Excel rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul Excel</comment>
+ <comment xml:lang="ga">scarbhileog Excel</comment>
+ <comment xml:lang="gl">folla de cálculo de Excel</comment>
+ <comment xml:lang="he">גליון נתונים של Excel</comment>
+ <comment xml:lang="hr">Excel proračunska tablica</comment>
+ <comment xml:lang="hu">Excel táblázat</comment>
+ <comment xml:lang="ia">Folio de calculo Excel</comment>
+ <comment xml:lang="id">Lembar sebar Excel</comment>
+ <comment xml:lang="it">Foglio di calcolo Excel</comment>
+ <comment xml:lang="ja">Excel スプレッドシート</comment>
+ <comment xml:lang="ka">Excel-ის ცხრილი</comment>
+ <comment xml:lang="kk">Excel электрондық кестесі</comment>
+ <comment xml:lang="ko">Excel 스프레드시트</comment>
+ <comment xml:lang="lt">Excel skaičialentė</comment>
+ <comment xml:lang="lv">Excel izklājlapa</comment>
+ <comment xml:lang="nb">Excel regneark</comment>
+ <comment xml:lang="nl">Excel-rekenblad</comment>
+ <comment xml:lang="nn">Excel-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul Excel</comment>
+ <comment xml:lang="pl">Arkusz Excel</comment>
+ <comment xml:lang="pt">folha de cálculo Excel</comment>
+ <comment xml:lang="pt_BR">Planilha do Excel</comment>
+ <comment xml:lang="ro">Foaie de calcul Excel</comment>
+ <comment xml:lang="ru">Электронная таблица Excel</comment>
+ <comment xml:lang="sk">Zošit Excel</comment>
+ <comment xml:lang="sl">Razpredelnica Microsoft Excel</comment>
+ <comment xml:lang="sq">Fletë llogaritje Excel</comment>
+ <comment xml:lang="sr">Екселова табела</comment>
+ <comment xml:lang="sv">Excel-kalkylblad</comment>
+ <comment xml:lang="tr">Excel çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця Excel</comment>
+ <comment xml:lang="vi">Bảng tính Excel</comment>
+ <comment xml:lang="zh_CN">Excel 电子表格</comment>
+ <comment xml:lang="zh_TW">Excel 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.xlsm"/>
+ <sub-class-of type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-excel.template.macroEnabled.12">
+ <comment>Excel spreadsheet template</comment>
+ <comment xml:lang="ca">plantilla de full de càlcul d'Excel</comment>
+ <comment xml:lang="cs">šablona tabulky Excel</comment>
+ <comment xml:lang="da">Excel-regnearksskabelon</comment>
+ <comment xml:lang="de">Excel-Tabellenvorlage</comment>
+ <comment xml:lang="el">Πρότυπο λογιστικού φύλλου Excel</comment>
+ <comment xml:lang="en_GB">Excel spreadsheet template</comment>
+ <comment xml:lang="es">plantilla de libro de Excel</comment>
+ <comment xml:lang="eu">Excel kalkulu-orri txantiloia</comment>
+ <comment xml:lang="fi">Excel-taulukkomalli</comment>
+ <comment xml:lang="fr">modèle de feuille de calcul Excel</comment>
+ <comment xml:lang="ga">teimpléad scarbhileoige Excel</comment>
+ <comment xml:lang="he">תבנית גיליון נתונים של Excel</comment>
+ <comment xml:lang="hr">Predložak Excel proračunske tablice</comment>
+ <comment xml:lang="hu">Excel munkafüzetsablon</comment>
+ <comment xml:lang="ia">Patrono de folio de calculo Excel</comment>
+ <comment xml:lang="id">Templat lembar kerja Excel</comment>
+ <comment xml:lang="it">Modello foglio di calcolo Excel</comment>
+ <comment xml:lang="kk">Excel кестесінің үлгісі</comment>
+ <comment xml:lang="ko">Excel 스프레드시트 서식</comment>
+ <comment xml:lang="oc">Modèl de fuèlh de calcul Excel</comment>
+ <comment xml:lang="pl">Szablon arkusza Excel</comment>
+ <comment xml:lang="pt">modelo de folha de cálculo Excel</comment>
+ <comment xml:lang="pt_BR">Modelo de planilha do Excel</comment>
+ <comment xml:lang="ru">Шаблон таблицы Excel</comment>
+ <comment xml:lang="sk">Šablóna tabuľky aplikácie Excel</comment>
+ <comment xml:lang="sr">Екселов шаблон табеле</comment>
+ <comment xml:lang="sv">Excel-kalkylarksmall</comment>
+ <comment xml:lang="tr">Excel hesap tablosu şablonu</comment>
+ <comment xml:lang="uk">шаблон електронної таблиці Excel</comment>
+ <comment xml:lang="zh_CN">Excel 电子表格模板</comment>
+ <comment xml:lang="zh_TW">Excel 試算表範本</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.xltm"/>
+ <sub-class-of type="application/vnd.openxmlformats-officedocument.spreadsheetml.template"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-powerpoint">
+ <comment>PowerPoint presentation</comment>
+ <comment xml:lang="ar">عرض تقديمي PowerPoint</comment>
+ <comment xml:lang="be@latin">Prezentacyja PowerPoint</comment>
+ <comment xml:lang="bg">Презентация — PowerPoint</comment>
+ <comment xml:lang="ca">presentació de PowerPoint</comment>
+ <comment xml:lang="cs">prezentace PowerPoint</comment>
+ <comment xml:lang="da">PowerPoint-præsentation</comment>
+ <comment xml:lang="de">PowerPoint-Präsentation</comment>
+ <comment xml:lang="el">Παρουσίαση PowerPoint</comment>
+ <comment xml:lang="en_GB">PowerPoint presentation</comment>
+ <comment xml:lang="eo">PowerPoint-prezentaĵo</comment>
+ <comment xml:lang="es">presentación de PowerPoint</comment>
+ <comment xml:lang="eu">PowerPoint aurkezpena</comment>
+ <comment xml:lang="fi">PowerPoint-esitys</comment>
+ <comment xml:lang="fo">PowerPoint framløga</comment>
+ <comment xml:lang="fr">présentation PowerPoint</comment>
+ <comment xml:lang="ga">láithreoireacht PowerPoint</comment>
+ <comment xml:lang="gl">presentación de PowerPoint</comment>
+ <comment xml:lang="he">מצגת PowerPoint</comment>
+ <comment xml:lang="hr">PowerPoint prezentacija</comment>
+ <comment xml:lang="hu">PowerPoint prezentáció</comment>
+ <comment xml:lang="ia">Presentation PowerPoint</comment>
+ <comment xml:lang="id">Presentasi PowerPoint</comment>
+ <comment xml:lang="it">Presentazione PowerPoint</comment>
+ <comment xml:lang="ja">PowerPoint プレゼンテーション</comment>
+ <comment xml:lang="kk">PowerPoint презентациясы</comment>
+ <comment xml:lang="ko">PowerPoint 프레젠테이션</comment>
+ <comment xml:lang="lt">PowerPoint pateiktis</comment>
+ <comment xml:lang="lv">PowerPoint prezentācija</comment>
+ <comment xml:lang="nb">PowerPoint-presentasjon</comment>
+ <comment xml:lang="nl">PowerPoint-presentatie</comment>
+ <comment xml:lang="nn">PowerPoint-presentasjon</comment>
+ <comment xml:lang="oc">presentacion PowerPoint</comment>
+ <comment xml:lang="pl">Prezentacja PowerPoint</comment>
+ <comment xml:lang="pt">apresentação PowerPoint</comment>
+ <comment xml:lang="pt_BR">Apresentação do PowerPoint</comment>
+ <comment xml:lang="ro">Prezentare PowerPoint</comment>
+ <comment xml:lang="ru">Презентация PowerPoint</comment>
+ <comment xml:lang="sk">Prezentácia PowerPoint</comment>
+ <comment xml:lang="sl">Predstavitev Microsoft PowerPoint</comment>
+ <comment xml:lang="sq">Prezantim PowerPoint</comment>
+ <comment xml:lang="sr">Пауер поинт презентација</comment>
+ <comment xml:lang="sv">PowerPoint-presentation</comment>
+ <comment xml:lang="tr">PowerPoint sunumu</comment>
+ <comment xml:lang="uk">презентація PowerPoint</comment>
+ <comment xml:lang="vi">Trình diễn PowerPoint</comment>
+ <comment xml:lang="zh_CN">PowerPoint 演示文稿</comment>
+ <comment xml:lang="zh_TW">PowerPoint 簡報</comment>
+ <generic-icon name="x-office-presentation"/>
+ <glob pattern="*.ppz"/>
+ <glob pattern="*.ppt"/>
+ <glob pattern="*.pps"/>
+ <glob pattern="*.pot"/>
+ <alias type="application/powerpoint"/>
+ <alias type="application/mspowerpoint"/>
+ <alias type="application/x-mspowerpoint"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-powerpoint.addin.macroEnabled.12">
+ <comment>PowerPoint add-in</comment>
+ <comment xml:lang="bg">Приставка — PowerPoint</comment>
+ <comment xml:lang="ca">complement de PowerPoint</comment>
+ <comment xml:lang="cs">doplněk PowerPoint</comment>
+ <comment xml:lang="da">PowerPoint-tilføjelse</comment>
+ <comment xml:lang="de">PowerPoint Add-in</comment>
+ <comment xml:lang="el">Πρόσθετο PowerPoint</comment>
+ <comment xml:lang="en_GB">PowerPoint add-in</comment>
+ <comment xml:lang="es">complemento de PowerPoint</comment>
+ <comment xml:lang="eu">PowerPoint gehigarria</comment>
+ <comment xml:lang="fi">PowerPoint-lisäosa</comment>
+ <comment xml:lang="fr">complément PowerPoint</comment>
+ <comment xml:lang="ga">breiseán PowerPoint</comment>
+ <comment xml:lang="gl">complemento de PowerPoint</comment>
+ <comment xml:lang="he">תוסף של PowerPoint</comment>
+ <comment xml:lang="hr">PowerPoint dodatak</comment>
+ <comment xml:lang="hu">PowerPoint bővítmény</comment>
+ <comment xml:lang="ia">Add-in PowerPoint</comment>
+ <comment xml:lang="id">Add-in PowerPoint</comment>
+ <comment xml:lang="it">Add-in PowerPoint</comment>
+ <comment xml:lang="ja">PowerPoint アドイン</comment>
+ <comment xml:lang="ka">PowerPoint-ის დამატება</comment>
+ <comment xml:lang="kk">PowerPoint қосымшасы</comment>
+ <comment xml:lang="ko">PowerPoint 추가 기능</comment>
+ <comment xml:lang="lv">PowerPoint pievienojumprogramma</comment>
+ <comment xml:lang="nl">PowerPoint add-in</comment>
+ <comment xml:lang="oc">complement PowerPoint</comment>
+ <comment xml:lang="pl">Dodatek PowerPoint</comment>
+ <comment xml:lang="pt">extensão PowerPoint</comment>
+ <comment xml:lang="pt_BR">Suplemento do PowerPoint</comment>
+ <comment xml:lang="ru">Дополнение PowerPoint</comment>
+ <comment xml:lang="sk">Doplnok aplikácie PowerPoint </comment>
+ <comment xml:lang="sl">Vstavek PowerPoint</comment>
+ <comment xml:lang="sr">Пауер поинт додатак</comment>
+ <comment xml:lang="sv">PowerPoint-tillägg</comment>
+ <comment xml:lang="tr">PowerPoint eklentisi</comment>
+ <comment xml:lang="uk">додаток PowerPoint</comment>
+ <comment xml:lang="zh_CN">PowerPoint 外接程序</comment>
+ <comment xml:lang="zh_TW">PowerPoint 增益集</comment>
+ <generic-icon name="x-office-presentation"/>
+ <glob pattern="*.ppam"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-powerpoint.presentation.macroEnabled.12">
+ <comment>PowerPoint presentation</comment>
+ <comment xml:lang="ar">عرض تقديمي PowerPoint</comment>
+ <comment xml:lang="be@latin">Prezentacyja PowerPoint</comment>
+ <comment xml:lang="bg">Презентация — PowerPoint</comment>
+ <comment xml:lang="ca">presentació de PowerPoint</comment>
+ <comment xml:lang="cs">prezentace PowerPoint</comment>
+ <comment xml:lang="da">PowerPoint-præsentation</comment>
+ <comment xml:lang="de">PowerPoint-Präsentation</comment>
+ <comment xml:lang="el">Παρουσίαση PowerPoint</comment>
+ <comment xml:lang="en_GB">PowerPoint presentation</comment>
+ <comment xml:lang="eo">PowerPoint-prezentaĵo</comment>
+ <comment xml:lang="es">presentación de PowerPoint</comment>
+ <comment xml:lang="eu">PowerPoint aurkezpena</comment>
+ <comment xml:lang="fi">PowerPoint-esitys</comment>
+ <comment xml:lang="fo">PowerPoint framløga</comment>
+ <comment xml:lang="fr">présentation PowerPoint</comment>
+ <comment xml:lang="ga">láithreoireacht PowerPoint</comment>
+ <comment xml:lang="gl">presentación de PowerPoint</comment>
+ <comment xml:lang="he">מצגת PowerPoint</comment>
+ <comment xml:lang="hr">PowerPoint prezentacija</comment>
+ <comment xml:lang="hu">PowerPoint prezentáció</comment>
+ <comment xml:lang="ia">Presentation PowerPoint</comment>
+ <comment xml:lang="id">Presentasi PowerPoint</comment>
+ <comment xml:lang="it">Presentazione PowerPoint</comment>
+ <comment xml:lang="ja">PowerPoint プレゼンテーション</comment>
+ <comment xml:lang="kk">PowerPoint презентациясы</comment>
+ <comment xml:lang="ko">PowerPoint 프레젠테이션</comment>
+ <comment xml:lang="lt">PowerPoint pateiktis</comment>
+ <comment xml:lang="lv">PowerPoint prezentācija</comment>
+ <comment xml:lang="nb">PowerPoint-presentasjon</comment>
+ <comment xml:lang="nl">PowerPoint-presentatie</comment>
+ <comment xml:lang="nn">PowerPoint-presentasjon</comment>
+ <comment xml:lang="oc">presentacion PowerPoint</comment>
+ <comment xml:lang="pl">Prezentacja PowerPoint</comment>
+ <comment xml:lang="pt">apresentação PowerPoint</comment>
+ <comment xml:lang="pt_BR">Apresentação do PowerPoint</comment>
+ <comment xml:lang="ro">Prezentare PowerPoint</comment>
+ <comment xml:lang="ru">Презентация PowerPoint</comment>
+ <comment xml:lang="sk">Prezentácia PowerPoint</comment>
+ <comment xml:lang="sl">Predstavitev Microsoft PowerPoint</comment>
+ <comment xml:lang="sq">Prezantim PowerPoint</comment>
+ <comment xml:lang="sr">Пауер поинт презентација</comment>
+ <comment xml:lang="sv">PowerPoint-presentation</comment>
+ <comment xml:lang="tr">PowerPoint sunumu</comment>
+ <comment xml:lang="uk">презентація PowerPoint</comment>
+ <comment xml:lang="vi">Trình diễn PowerPoint</comment>
+ <comment xml:lang="zh_CN">PowerPoint 演示文稿</comment>
+ <comment xml:lang="zh_TW">PowerPoint 簡報</comment>
+ <generic-icon name="x-office-presentation"/>
+ <glob pattern="*.pptm"/>
+ <sub-class-of type="application/vnd.openxmlformats-officedocument.presentationml.presentation"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-powerpoint.slide.macroEnabled.12">
+ <comment>PowerPoint slide</comment>
+ <comment xml:lang="ast">Diapositiva de PowerPoint</comment>
+ <comment xml:lang="ca">dispositiva de PowerPoint</comment>
+ <comment xml:lang="cs">promítání PowerPoint</comment>
+ <comment xml:lang="da">PowerPoint-dias</comment>
+ <comment xml:lang="de">PowerPoint-Folie</comment>
+ <comment xml:lang="el">Διαφάνεια PowerPoint</comment>
+ <comment xml:lang="en_GB">PowerPoint slide</comment>
+ <comment xml:lang="es">diapositiva de PowerPoint</comment>
+ <comment xml:lang="eu">PowerPoint diapositiba</comment>
+ <comment xml:lang="fi">PowerPoint-dia</comment>
+ <comment xml:lang="fr">diapositive PowerPoint</comment>
+ <comment xml:lang="ga">sleamhnán PowerPoint</comment>
+ <comment xml:lang="he">שקופית של PowerPoint</comment>
+ <comment xml:lang="hr">PowerPoint prezentacija</comment>
+ <comment xml:lang="hu">PowerPoint dia</comment>
+ <comment xml:lang="ia">Diapositiva PowerPoint</comment>
+ <comment xml:lang="id">Salindia PowerPoint</comment>
+ <comment xml:lang="it">Diapositiva PowerPoint</comment>
+ <comment xml:lang="kk">PowerPoint слайды</comment>
+ <comment xml:lang="ko">파워포인트 슬라이드</comment>
+ <comment xml:lang="oc">Diapositiva PowerPoint</comment>
+ <comment xml:lang="pl">Slajd PowerPoint</comment>
+ <comment xml:lang="pt">diapositivo PowerPoint</comment>
+ <comment xml:lang="pt_BR">Slide do PowerPoint</comment>
+ <comment xml:lang="ru">Слайд PowerPoint</comment>
+ <comment xml:lang="sk">Snímka aplikácie PowerPoint</comment>
+ <comment xml:lang="sr">Пауер поинт слајд</comment>
+ <comment xml:lang="sv">PowerPoint-bildspel</comment>
+ <comment xml:lang="tr">PowerPoint sunusu</comment>
+ <comment xml:lang="uk">слайд PowerPoint</comment>
+ <comment xml:lang="zh_CN">PowerPoint 幻灯片</comment>
+ <comment xml:lang="zh_TW">PowerPoint 投影片</comment>
+ <generic-icon name="x-office-presentation"/>
+ <glob pattern="*.sldm"/>
+ <sub-class-of type="application/vnd.openxmlformats-officedocument.presentationml.slide"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-powerpoint.slideshow.macroEnabled.12">
+ <comment>PowerPoint presentation</comment>
+ <comment xml:lang="ar">عرض تقديمي PowerPoint</comment>
+ <comment xml:lang="be@latin">Prezentacyja PowerPoint</comment>
+ <comment xml:lang="bg">Презентация — PowerPoint</comment>
+ <comment xml:lang="ca">presentació de PowerPoint</comment>
+ <comment xml:lang="cs">prezentace PowerPoint</comment>
+ <comment xml:lang="da">PowerPoint-præsentation</comment>
+ <comment xml:lang="de">PowerPoint-Präsentation</comment>
+ <comment xml:lang="el">Παρουσίαση PowerPoint</comment>
+ <comment xml:lang="en_GB">PowerPoint presentation</comment>
+ <comment xml:lang="eo">PowerPoint-prezentaĵo</comment>
+ <comment xml:lang="es">presentación de PowerPoint</comment>
+ <comment xml:lang="eu">PowerPoint aurkezpena</comment>
+ <comment xml:lang="fi">PowerPoint-esitys</comment>
+ <comment xml:lang="fo">PowerPoint framløga</comment>
+ <comment xml:lang="fr">présentation PowerPoint</comment>
+ <comment xml:lang="ga">láithreoireacht PowerPoint</comment>
+ <comment xml:lang="gl">presentación de PowerPoint</comment>
+ <comment xml:lang="he">מצגת PowerPoint</comment>
+ <comment xml:lang="hr">PowerPoint prezentacija</comment>
+ <comment xml:lang="hu">PowerPoint prezentáció</comment>
+ <comment xml:lang="ia">Presentation PowerPoint</comment>
+ <comment xml:lang="id">Presentasi PowerPoint</comment>
+ <comment xml:lang="it">Presentazione PowerPoint</comment>
+ <comment xml:lang="ja">PowerPoint プレゼンテーション</comment>
+ <comment xml:lang="kk">PowerPoint презентациясы</comment>
+ <comment xml:lang="ko">PowerPoint 프레젠테이션</comment>
+ <comment xml:lang="lt">PowerPoint pateiktis</comment>
+ <comment xml:lang="lv">PowerPoint prezentācija</comment>
+ <comment xml:lang="nb">PowerPoint-presentasjon</comment>
+ <comment xml:lang="nl">PowerPoint-presentatie</comment>
+ <comment xml:lang="nn">PowerPoint-presentasjon</comment>
+ <comment xml:lang="oc">presentacion PowerPoint</comment>
+ <comment xml:lang="pl">Prezentacja PowerPoint</comment>
+ <comment xml:lang="pt">apresentação PowerPoint</comment>
+ <comment xml:lang="pt_BR">Apresentação do PowerPoint</comment>
+ <comment xml:lang="ro">Prezentare PowerPoint</comment>
+ <comment xml:lang="ru">Презентация PowerPoint</comment>
+ <comment xml:lang="sk">Prezentácia PowerPoint</comment>
+ <comment xml:lang="sl">Predstavitev Microsoft PowerPoint</comment>
+ <comment xml:lang="sq">Prezantim PowerPoint</comment>
+ <comment xml:lang="sr">Пауер поинт презентација</comment>
+ <comment xml:lang="sv">PowerPoint-presentation</comment>
+ <comment xml:lang="tr">PowerPoint sunumu</comment>
+ <comment xml:lang="uk">презентація PowerPoint</comment>
+ <comment xml:lang="vi">Trình diễn PowerPoint</comment>
+ <comment xml:lang="zh_CN">PowerPoint 演示文稿</comment>
+ <comment xml:lang="zh_TW">PowerPoint 簡報</comment>
+ <generic-icon name="x-office-presentation"/>
+ <glob pattern="*.ppsm"/>
+ <sub-class-of type="application/vnd.openxmlformats-officedocument.presentationml.slideshow"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-powerpoint.template.macroEnabled.12">
+ <comment>PowerPoint presentation template</comment>
+ <comment xml:lang="ast">Plantía de presentaciones de PowerPoint</comment>
+ <comment xml:lang="ca">plantilla de presentació de PowerPoint</comment>
+ <comment xml:lang="cs">šablona prezentace PowerPoint</comment>
+ <comment xml:lang="da">PowerPoint-præsentationsskabelon</comment>
+ <comment xml:lang="de">PowerPoint-Präsentationsvorlage</comment>
+ <comment xml:lang="el">Πρότυπο παρουσίασης PowerPoint</comment>
+ <comment xml:lang="en_GB">PowerPoint presentation template</comment>
+ <comment xml:lang="es">plantilla de presentación de PowerPoint</comment>
+ <comment xml:lang="eu">PowerPoint aurkezpen txantiloia</comment>
+ <comment xml:lang="fi">PowerPoint-esitysmalli</comment>
+ <comment xml:lang="fr">modèle de présentation PowerPoint</comment>
+ <comment xml:lang="ga">teimpléad láithreoireachta PowerPoint</comment>
+ <comment xml:lang="he">תבנית מצגת PowerPoint</comment>
+ <comment xml:lang="hr">Predložak PowerPoint prezentacije</comment>
+ <comment xml:lang="hu">PowerPoint bemutatósablon</comment>
+ <comment xml:lang="ia">Patrono de presentation PowerPoint</comment>
+ <comment xml:lang="id">Templat presentasi PowerPoint</comment>
+ <comment xml:lang="it">Modello presentazione PowerPoint</comment>
+ <comment xml:lang="kk">PowerPoint презентация үлгісі</comment>
+ <comment xml:lang="ko">PowerPoint 프리젠테이션 서식</comment>
+ <comment xml:lang="oc">Modèl de presentacion PowerPoint</comment>
+ <comment xml:lang="pl">Szablon prezentacji PowerPoint</comment>
+ <comment xml:lang="pt">modelo de apresentação PowerPoint</comment>
+ <comment xml:lang="pt_BR">Modelo de apresentação do PowerPoint</comment>
+ <comment xml:lang="ru">Шаблон презентации PowerPoint</comment>
+ <comment xml:lang="sk">Šablóna prezentácie aplikácie PowerPoint</comment>
+ <comment xml:lang="sr">Шаблон презентације Пауер поинта</comment>
+ <comment xml:lang="sv">PowerPoint-presentationsmall</comment>
+ <comment xml:lang="tr">PowerPoint sunum şablonu</comment>
+ <comment xml:lang="uk">шаблон презентації PowerPoint</comment>
+ <comment xml:lang="zh_CN">PowerPoint 演示文稿模板</comment>
+ <comment xml:lang="zh_TW">PowerPoint 簡報範本</comment>
+ <generic-icon name="x-office-presentation"/>
+ <glob pattern="*.potm"/>
+ <sub-class-of type="application/vnd.openxmlformats-officedocument.presentationml.template"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-visio.drawing.main+xml">
+ <comment>Office Open XML Visio Drawing</comment>
+ <comment xml:lang="ca">dibuix en Office Open XML de Visio</comment>
+ <comment xml:lang="cs">kresba Office Open XML Visio</comment>
+ <comment xml:lang="da">Office Open XML Visio-tegning</comment>
+ <comment xml:lang="de">Office-Open-XML-Visio-Zeichnung</comment>
+ <comment xml:lang="el">Σχέδιο Office Open XML Visio</comment>
+ <comment xml:lang="en_GB">Office Open XML Visio Drawing</comment>
+ <comment xml:lang="es">dibujo en OOXML de Visio</comment>
+ <comment xml:lang="eu">Office Open XML Visio marrazkia</comment>
+ <comment xml:lang="fi">Office Open XML Visio -piirros</comment>
+ <comment xml:lang="fr">dessin Visio Office Open XML</comment>
+ <comment xml:lang="ga">Líníocht Office Open XML Visio</comment>
+ <comment xml:lang="he">ציור Visio ב־Open XML מבית Office</comment>
+ <comment xml:lang="hr">Office Open XML Visio crtež</comment>
+ <comment xml:lang="hu">Office Open XML Visio rajz</comment>
+ <comment xml:lang="ia">Designo Office Open XML Visio</comment>
+ <comment xml:lang="id">Gambar Visio Office Open XML</comment>
+ <comment xml:lang="it">Disegno Visio Office Open XML</comment>
+ <comment xml:lang="kk">Office Open XML Visio суреті</comment>
+ <comment xml:lang="ko">오피스 오픈 XML 비지오 드로잉</comment>
+ <comment xml:lang="pl">Rysunek Office Open XML Visio</comment>
+ <comment xml:lang="pt">desenho Office Open XML Visio</comment>
+ <comment xml:lang="pt_BR">Desenho do Visio em Office Open XML</comment>
+ <comment xml:lang="ru">Рисунок Visio формата Office Open XML</comment>
+ <comment xml:lang="sk">Kresba aplikácie Visio Office Open XML</comment>
+ <comment xml:lang="sr">Офисов отворени ИксМЛ Визио цртеж</comment>
+ <comment xml:lang="sv">Office Open XML Visio-teckning</comment>
+ <comment xml:lang="tr">Office Open XML Visio Çizimi</comment>
+ <comment xml:lang="uk">схема VIisio у форматі Office Open XML</comment>
+ <comment xml:lang="zh_CN">Office Open XML Visio 绘图</comment>
+ <comment xml:lang="zh_TW">Office Open XML Visio 繪圖</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.vsdx"/>
+ <sub-class-of type="application/zip"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-visio.template.main+xml">
+ <comment>Office Open XML Visio Template</comment>
+ <comment xml:lang="ca">plantilla en Office Open XML de Visio</comment>
+ <comment xml:lang="cs">šablona Office Open XML Visio</comment>
+ <comment xml:lang="da">Office Open XML Visio-skabelon</comment>
+ <comment xml:lang="de">Office-Open-XML-Visio-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο Office Open XML Visio</comment>
+ <comment xml:lang="en_GB">Office Open XML Visio Template</comment>
+ <comment xml:lang="es">plantilla en OOXML de Visio</comment>
+ <comment xml:lang="eu">Office Open XML Visio txantiloia</comment>
+ <comment xml:lang="fi">Office Open XML Visio -malli</comment>
+ <comment xml:lang="fr">modèle Visio Office Open XML</comment>
+ <comment xml:lang="ga">Teimpléad Office Open XML Visio</comment>
+ <comment xml:lang="he">תבנית Visio ב־Open XML מבית Office</comment>
+ <comment xml:lang="hr">Predložak Office Open XML Visio</comment>
+ <comment xml:lang="hu">Office Open XML Visio sablon</comment>
+ <comment xml:lang="ia">Patrono Office Open XML Visio</comment>
+ <comment xml:lang="id">Templat Visio Office Open XML</comment>
+ <comment xml:lang="it">Modello Visio Office Open XML</comment>
+ <comment xml:lang="kk">Office Open XML Visio үлгісі</comment>
+ <comment xml:lang="ko">오피스 오픈 XML 비지오 서식</comment>
+ <comment xml:lang="pl">Szablon Office Open XML Visio</comment>
+ <comment xml:lang="pt">modelo Office Open XML Visio</comment>
+ <comment xml:lang="pt_BR">Modelo do Visio em Office Open XML</comment>
+ <comment xml:lang="ru">Шаблон Visio формата Office Open XML</comment>
+ <comment xml:lang="sk">Šablóna aplikácie Visio Office Open XML</comment>
+ <comment xml:lang="sr">Офисов отворени ИксМЛ Визио шаблон</comment>
+ <comment xml:lang="sv">Office Open XML Visio-mall</comment>
+ <comment xml:lang="tr">Office Open XML Visio Şablonu</comment>
+ <comment xml:lang="uk">шаблон Visio у форматі Office Open XML</comment>
+ <comment xml:lang="zh_CN">Office Open XML Visio 模板</comment>
+ <comment xml:lang="zh_TW">Office Open XML Visio 範本</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.vstx"/>
+ <sub-class-of type="application/zip"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-visio.stencil.main+xml">
+ <comment>Office Open XML Visio Stencil</comment>
+ <comment xml:lang="ca">patró en Office Open XML de Visio</comment>
+ <comment xml:lang="cs">objekty Office Open XML Visio</comment>
+ <comment xml:lang="da">Office Open XML Visio-stencil</comment>
+ <comment xml:lang="de">Office-Open-XML-Visio-Schablone</comment>
+ <comment xml:lang="en_GB">Office Open XML Visio Stencil</comment>
+ <comment xml:lang="es">esténcil en OOXML de Visio</comment>
+ <comment xml:lang="eu">Office Open XML Visio txantiloia</comment>
+ <comment xml:lang="fi">Office Open XML Visio -kaavio</comment>
+ <comment xml:lang="fr">stencil Visio Office Open XML</comment>
+ <comment xml:lang="ga">Stionsal Office Open XML Visio</comment>
+ <comment xml:lang="he">דגם ל־Visio ב־Open XML מבית Office</comment>
+ <comment xml:lang="hr">Office Open XML Visio šablona</comment>
+ <comment xml:lang="hu">Office Open XML Visio stencil</comment>
+ <comment xml:lang="ia">Stencil Office Open XML Visio</comment>
+ <comment xml:lang="id">Stensil Visio Office Open XML</comment>
+ <comment xml:lang="it">Stencil Visio Office Open XML</comment>
+ <comment xml:lang="kk">Office Open XML пішімінің Visio трафареті</comment>
+ <comment xml:lang="ko">오피스 오픈 XML 비지오 스텐실</comment>
+ <comment xml:lang="pl">Wzór Office Open XML Visio</comment>
+ <comment xml:lang="pt">Stencil Office Open XML Visio</comment>
+ <comment xml:lang="pt_BR">Estêncil do Visio em Office Open XML</comment>
+ <comment xml:lang="ru">Трафарет Visio формата Office Open XML</comment>
+ <comment xml:lang="sk">Objekt aplikácie Visio Office Open XML</comment>
+ <comment xml:lang="sr">Офисов отворени ИксМЛ Визио шаблон</comment>
+ <comment xml:lang="sv">Office Open XML Visio-stencil</comment>
+ <comment xml:lang="tr">Office Open XML Visio Kalıbı</comment>
+ <comment xml:lang="uk">трафарет Visio у форматі Office Open XML</comment>
+ <comment xml:lang="zh_CN">Office Open XML Visio 模具</comment>
+ <comment xml:lang="zh_TW">Office Open XML Visio 圖形樣本</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.vssx"/>
+ <sub-class-of type="application/zip"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-visio.drawing.macroEnabled.main+xml">
+ <comment>Office Open XML Visio Drawing</comment>
+ <comment xml:lang="ca">dibuix en Office Open XML de Visio</comment>
+ <comment xml:lang="cs">kresba Office Open XML Visio</comment>
+ <comment xml:lang="da">Office Open XML Visio-tegning</comment>
+ <comment xml:lang="de">Office-Open-XML-Visio-Zeichnung</comment>
+ <comment xml:lang="el">Σχέδιο Office Open XML Visio</comment>
+ <comment xml:lang="en_GB">Office Open XML Visio Drawing</comment>
+ <comment xml:lang="es">dibujo en OOXML de Visio</comment>
+ <comment xml:lang="eu">Office Open XML Visio marrazkia</comment>
+ <comment xml:lang="fi">Office Open XML Visio -piirros</comment>
+ <comment xml:lang="fr">dessin Visio Office Open XML</comment>
+ <comment xml:lang="ga">Líníocht Office Open XML Visio</comment>
+ <comment xml:lang="he">ציור Visio ב־Open XML מבית Office</comment>
+ <comment xml:lang="hr">Office Open XML Visio crtež</comment>
+ <comment xml:lang="hu">Office Open XML Visio rajz</comment>
+ <comment xml:lang="ia">Designo Office Open XML Visio</comment>
+ <comment xml:lang="id">Gambar Visio Office Open XML</comment>
+ <comment xml:lang="it">Disegno Visio Office Open XML</comment>
+ <comment xml:lang="kk">Office Open XML Visio суреті</comment>
+ <comment xml:lang="ko">오피스 오픈 XML 비지오 드로잉</comment>
+ <comment xml:lang="pl">Rysunek Office Open XML Visio</comment>
+ <comment xml:lang="pt">desenho Office Open XML Visio</comment>
+ <comment xml:lang="pt_BR">Desenho do Visio em Office Open XML</comment>
+ <comment xml:lang="ru">Рисунок Visio формата Office Open XML</comment>
+ <comment xml:lang="sk">Kresba aplikácie Visio Office Open XML</comment>
+ <comment xml:lang="sr">Офисов отворени ИксМЛ Визио цртеж</comment>
+ <comment xml:lang="sv">Office Open XML Visio-teckning</comment>
+ <comment xml:lang="tr">Office Open XML Visio Çizimi</comment>
+ <comment xml:lang="uk">схема VIisio у форматі Office Open XML</comment>
+ <comment xml:lang="zh_CN">Office Open XML Visio 绘图</comment>
+ <comment xml:lang="zh_TW">Office Open XML Visio 繪圖</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.vsdm"/>
+ <sub-class-of type="application/zip"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-visio.template.macroEnabled.main+xml">
+ <comment>Office Open XML Visio Template</comment>
+ <comment xml:lang="ca">plantilla en Office Open XML de Visio</comment>
+ <comment xml:lang="cs">šablona Office Open XML Visio</comment>
+ <comment xml:lang="da">Office Open XML Visio-skabelon</comment>
+ <comment xml:lang="de">Office-Open-XML-Visio-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο Office Open XML Visio</comment>
+ <comment xml:lang="en_GB">Office Open XML Visio Template</comment>
+ <comment xml:lang="es">plantilla en OOXML de Visio</comment>
+ <comment xml:lang="eu">Office Open XML Visio txantiloia</comment>
+ <comment xml:lang="fi">Office Open XML Visio -malli</comment>
+ <comment xml:lang="fr">modèle Visio Office Open XML</comment>
+ <comment xml:lang="ga">Teimpléad Office Open XML Visio</comment>
+ <comment xml:lang="he">תבנית Visio ב־Open XML מבית Office</comment>
+ <comment xml:lang="hr">Predložak Office Open XML Visio</comment>
+ <comment xml:lang="hu">Office Open XML Visio sablon</comment>
+ <comment xml:lang="ia">Patrono Office Open XML Visio</comment>
+ <comment xml:lang="id">Templat Visio Office Open XML</comment>
+ <comment xml:lang="it">Modello Visio Office Open XML</comment>
+ <comment xml:lang="kk">Office Open XML Visio үлгісі</comment>
+ <comment xml:lang="ko">오피스 오픈 XML 비지오 서식</comment>
+ <comment xml:lang="pl">Szablon Office Open XML Visio</comment>
+ <comment xml:lang="pt">modelo Office Open XML Visio</comment>
+ <comment xml:lang="pt_BR">Modelo do Visio em Office Open XML</comment>
+ <comment xml:lang="ru">Шаблон Visio формата Office Open XML</comment>
+ <comment xml:lang="sk">Šablóna aplikácie Visio Office Open XML</comment>
+ <comment xml:lang="sr">Офисов отворени ИксМЛ Визио шаблон</comment>
+ <comment xml:lang="sv">Office Open XML Visio-mall</comment>
+ <comment xml:lang="tr">Office Open XML Visio Şablonu</comment>
+ <comment xml:lang="uk">шаблон Visio у форматі Office Open XML</comment>
+ <comment xml:lang="zh_CN">Office Open XML Visio 模板</comment>
+ <comment xml:lang="zh_TW">Office Open XML Visio 範本</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.vstm"/>
+ <sub-class-of type="application/zip"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-visio.stencil.macroEnabled.main+xml">
+ <comment>Office Open XML Visio Stencil</comment>
+ <comment xml:lang="ca">patró en Office Open XML de Visio</comment>
+ <comment xml:lang="cs">objekty Office Open XML Visio</comment>
+ <comment xml:lang="da">Office Open XML Visio-stencil</comment>
+ <comment xml:lang="de">Office-Open-XML-Visio-Schablone</comment>
+ <comment xml:lang="en_GB">Office Open XML Visio Stencil</comment>
+ <comment xml:lang="es">esténcil en OOXML de Visio</comment>
+ <comment xml:lang="eu">Office Open XML Visio txantiloia</comment>
+ <comment xml:lang="fi">Office Open XML Visio -kaavio</comment>
+ <comment xml:lang="fr">stencil Visio Office Open XML</comment>
+ <comment xml:lang="ga">Stionsal Office Open XML Visio</comment>
+ <comment xml:lang="he">דגם ל־Visio ב־Open XML מבית Office</comment>
+ <comment xml:lang="hr">Office Open XML Visio šablona</comment>
+ <comment xml:lang="hu">Office Open XML Visio stencil</comment>
+ <comment xml:lang="ia">Stencil Office Open XML Visio</comment>
+ <comment xml:lang="id">Stensil Visio Office Open XML</comment>
+ <comment xml:lang="it">Stencil Visio Office Open XML</comment>
+ <comment xml:lang="kk">Office Open XML пішімінің Visio трафареті</comment>
+ <comment xml:lang="ko">오피스 오픈 XML 비지오 스텐실</comment>
+ <comment xml:lang="pl">Wzór Office Open XML Visio</comment>
+ <comment xml:lang="pt">Stencil Office Open XML Visio</comment>
+ <comment xml:lang="pt_BR">Estêncil do Visio em Office Open XML</comment>
+ <comment xml:lang="ru">Трафарет Visio формата Office Open XML</comment>
+ <comment xml:lang="sk">Objekt aplikácie Visio Office Open XML</comment>
+ <comment xml:lang="sr">Офисов отворени ИксМЛ Визио шаблон</comment>
+ <comment xml:lang="sv">Office Open XML Visio-stencil</comment>
+ <comment xml:lang="tr">Office Open XML Visio Kalıbı</comment>
+ <comment xml:lang="uk">трафарет Visio у форматі Office Open XML</comment>
+ <comment xml:lang="zh_CN">Office Open XML Visio 模具</comment>
+ <comment xml:lang="zh_TW">Office Open XML Visio 圖形樣本</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.vssm"/>
+ <sub-class-of type="application/zip"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-word.document.macroEnabled.12">
+ <comment>Word document</comment>
+ <comment xml:lang="ar">مستند Word</comment>
+ <comment xml:lang="ast">Documentu de Word</comment>
+ <comment xml:lang="be@latin">Dakument Word</comment>
+ <comment xml:lang="bg">Документ — Word</comment>
+ <comment xml:lang="ca">document Word</comment>
+ <comment xml:lang="cs">dokument Word</comment>
+ <comment xml:lang="da">Worddokument</comment>
+ <comment xml:lang="de">Word-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Word</comment>
+ <comment xml:lang="en_GB">Word document</comment>
+ <comment xml:lang="eo">Word-dokumento</comment>
+ <comment xml:lang="es">documento de Word</comment>
+ <comment xml:lang="eu">Word dokumentua</comment>
+ <comment xml:lang="fi">Word-asiakirja</comment>
+ <comment xml:lang="fo">Word skjal</comment>
+ <comment xml:lang="fr">document Word</comment>
+ <comment xml:lang="ga">cáipéis Word</comment>
+ <comment xml:lang="gl">documento de Word</comment>
+ <comment xml:lang="he">מסמך Word</comment>
+ <comment xml:lang="hr">Word dokument</comment>
+ <comment xml:lang="hu">Word dokumentum</comment>
+ <comment xml:lang="ia">Documento Word</comment>
+ <comment xml:lang="id">Dokumen Word</comment>
+ <comment xml:lang="it">Documento Word</comment>
+ <comment xml:lang="ja">Word ドキュメント</comment>
+ <comment xml:lang="kk">Word құжаты</comment>
+ <comment xml:lang="ko">Word 문서</comment>
+ <comment xml:lang="lt">Word dokumentas</comment>
+ <comment xml:lang="lv">Word dokuments</comment>
+ <comment xml:lang="nb">Word-dokument</comment>
+ <comment xml:lang="nl">Word-document</comment>
+ <comment xml:lang="nn">Word-dokument</comment>
+ <comment xml:lang="oc">document Word</comment>
+ <comment xml:lang="pl">Dokument Word</comment>
+ <comment xml:lang="pt">documento Word</comment>
+ <comment xml:lang="pt_BR">Documento do Word</comment>
+ <comment xml:lang="ro">Document Word</comment>
+ <comment xml:lang="ru">Документ Word</comment>
+ <comment xml:lang="sk">Dokument Word</comment>
+ <comment xml:lang="sl">Dokument Word</comment>
+ <comment xml:lang="sq">Dokument Word</comment>
+ <comment xml:lang="sr">Ворд документ</comment>
+ <comment xml:lang="sv">Word-dokument</comment>
+ <comment xml:lang="tr">Word belgesi</comment>
+ <comment xml:lang="uk">документ Word</comment>
+ <comment xml:lang="vi">Tài liệu Word</comment>
+ <comment xml:lang="zh_CN">Word 文档</comment>
+ <comment xml:lang="zh_TW">Word 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.docm"/>
+ <sub-class-of type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-word.template.macroEnabled.12">
+ <comment>Word document template</comment>
+ <comment xml:lang="ast">Plantía de documentu de Word</comment>
+ <comment xml:lang="ca">plantilla de document Word</comment>
+ <comment xml:lang="cs">šablona dokumentu Word</comment>
+ <comment xml:lang="da">Word-dokumentskabelon</comment>
+ <comment xml:lang="de">Word-Dokumentvorlage</comment>
+ <comment xml:lang="el">Πρότυπο έγγραφο Word</comment>
+ <comment xml:lang="en_GB">Word document template</comment>
+ <comment xml:lang="es">plantilla de documento de Word</comment>
+ <comment xml:lang="eu">Word dokumentuaren txantiloia</comment>
+ <comment xml:lang="fi">Word-asiakirjamalli</comment>
+ <comment xml:lang="fr">modèle de document Word</comment>
+ <comment xml:lang="ga">teimpléad Word</comment>
+ <comment xml:lang="he">תבנית מסמך Word</comment>
+ <comment xml:lang="hr">Predložak Word dokumenta</comment>
+ <comment xml:lang="hu">Word dokumentumsablon</comment>
+ <comment xml:lang="ia">Patrono de documento Word</comment>
+ <comment xml:lang="id">Templat dokumen Word</comment>
+ <comment xml:lang="it">Modello documento Word</comment>
+ <comment xml:lang="kk">Word құжатының үлгісі</comment>
+ <comment xml:lang="ko">Word 문서 서식</comment>
+ <comment xml:lang="oc">modèl de document Word</comment>
+ <comment xml:lang="pl">Szablon dokumentu Word</comment>
+ <comment xml:lang="pt">modelo de documento Word</comment>
+ <comment xml:lang="pt_BR">Modelo de documento do Word</comment>
+ <comment xml:lang="ru">Шаблон документа Word</comment>
+ <comment xml:lang="sk">Šablóna dokumentu aplikácie Word</comment>
+ <comment xml:lang="sr">Шаблон Ворд документа</comment>
+ <comment xml:lang="sv">Word-dokumentmall</comment>
+ <comment xml:lang="tr">Word belgesi şablonu</comment>
+ <comment xml:lang="uk">шаблон документа Word</comment>
+ <comment xml:lang="zh_CN">Word 文档模板</comment>
+ <comment xml:lang="zh_TW">Word 文件範本</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.dotm"/>
+ <sub-class-of type="application/vnd.openxmlformats-officedocument.wordprocessingml.template"/>
+ </mime-type>
+ <mime-type type="application/oxps">
+ <comment>XPS document</comment>
+ <comment xml:lang="ar">مستند XPS</comment>
+ <comment xml:lang="ast">Documentu XPS</comment>
+ <comment xml:lang="be@latin">Dakument XPS</comment>
+ <comment xml:lang="bg">Документ — XPS</comment>
+ <comment xml:lang="ca">document XPS</comment>
+ <comment xml:lang="cs">dokument XPS</comment>
+ <comment xml:lang="da">XPS-dokument</comment>
+ <comment xml:lang="de">XPS-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο XPS</comment>
+ <comment xml:lang="en_GB">XPS document</comment>
+ <comment xml:lang="eo">XPS-dokumento</comment>
+ <comment xml:lang="es">documento XPS</comment>
+ <comment xml:lang="eu">XPS dokumentua</comment>
+ <comment xml:lang="fi">XPS-asiakirja</comment>
+ <comment xml:lang="fo">XPS skjal</comment>
+ <comment xml:lang="fr">document XPS</comment>
+ <comment xml:lang="ga">cáipéis XPS</comment>
+ <comment xml:lang="gl">documento XPS</comment>
+ <comment xml:lang="he">מסמך XPS</comment>
+ <comment xml:lang="hr">XPS dokument</comment>
+ <comment xml:lang="hu">XPS dokumentum</comment>
+ <comment xml:lang="ia">Documento XPS</comment>
+ <comment xml:lang="id">Dokumen XPS</comment>
+ <comment xml:lang="it">Documento XPS</comment>
+ <comment xml:lang="ja">XPS ドキュメント</comment>
+ <comment xml:lang="kk">XPS құжаты</comment>
+ <comment xml:lang="ko">XPS 문서</comment>
+ <comment xml:lang="lt">XPS dokumentas</comment>
+ <comment xml:lang="lv">XPS dokuments</comment>
+ <comment xml:lang="nb">XPS-dokument</comment>
+ <comment xml:lang="nl">XPS-document</comment>
+ <comment xml:lang="nn">XPS-dokument</comment>
+ <comment xml:lang="oc">document XPS</comment>
+ <comment xml:lang="pl">Dokument XPS</comment>
+ <comment xml:lang="pt">documento XPS</comment>
+ <comment xml:lang="pt_BR">Documento XPS</comment>
+ <comment xml:lang="ro">Document XPS</comment>
+ <comment xml:lang="ru">Документ XPS</comment>
+ <comment xml:lang="sk">Dokument XPS</comment>
+ <comment xml:lang="sl">Dokument XPS</comment>
+ <comment xml:lang="sq">Dokument XPS</comment>
+ <comment xml:lang="sr">ИксПС документ</comment>
+ <comment xml:lang="sv">XPS-dokument</comment>
+ <comment xml:lang="tr">XPS belgesi</comment>
+ <comment xml:lang="uk">документ XPS</comment>
+ <comment xml:lang="vi">Tài liệu XPS</comment>
+ <comment xml:lang="zh_CN">XPS 文档</comment>
+ <comment xml:lang="zh_TW">XPS 文件</comment>
+ <acronym>XPS</acronym>
+ <expanded-acronym>Open XML Paper Specification</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.oxps"/>
+ <glob pattern="*.xps"/>
+ <alias type="application/vnd.ms-xpsdocument"/>
+ <alias type="application/xps"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-works">
+ <comment>Microsoft Works document</comment>
+ <comment xml:lang="ar">مستند Microsoft Works</comment>
+ <comment xml:lang="ast">Documentu de Microsoft Works</comment>
+ <comment xml:lang="be@latin">Dakument Microsoft Works</comment>
+ <comment xml:lang="bg">Документ — Microsoft Works</comment>
+ <comment xml:lang="ca">document de Microsoft Works</comment>
+ <comment xml:lang="cs">dokument Microsoft Works</comment>
+ <comment xml:lang="da">Microsoft Works-dokument</comment>
+ <comment xml:lang="de">Microsoft-Works-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Microsoft Works</comment>
+ <comment xml:lang="en_GB">Microsoft Works document</comment>
+ <comment xml:lang="es">documento de Microsoft Works</comment>
+ <comment xml:lang="eu">Microsoft Works dokumentua</comment>
+ <comment xml:lang="fi">Microsoft Works -asiakirja</comment>
+ <comment xml:lang="fo">Microsoft Works skjal</comment>
+ <comment xml:lang="fr">document Microsoft Works</comment>
+ <comment xml:lang="ga">cáipéis Microsoft Works</comment>
+ <comment xml:lang="gl">documento de Microsoft Works</comment>
+ <comment xml:lang="he">מסמך Microsoft Works</comment>
+ <comment xml:lang="hr">Microsoft Works dokument</comment>
+ <comment xml:lang="hu">Microsoft Works dokumentum</comment>
+ <comment xml:lang="ia">Documento Microsoft Works</comment>
+ <comment xml:lang="id">Dokumen Microsoft Works</comment>
+ <comment xml:lang="it">Documento Microsoft Works</comment>
+ <comment xml:lang="ja">Microsoft Works ドキュメント</comment>
+ <comment xml:lang="ka">Microsoft Works-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">Microsoft Works құжаты</comment>
+ <comment xml:lang="ko">Microsoft Works 문서</comment>
+ <comment xml:lang="lt">Microsoft Works dokumentas</comment>
+ <comment xml:lang="lv">Microsoft Works dokuments</comment>
+ <comment xml:lang="nb">Microsoft Works-dokument</comment>
+ <comment xml:lang="nl">Microsoft Works-document</comment>
+ <comment xml:lang="nn">Microsoft Works-dokument</comment>
+ <comment xml:lang="oc">document Microsoft Works</comment>
+ <comment xml:lang="pl">Dokument Microsoft Works</comment>
+ <comment xml:lang="pt">documento Microsoft Works</comment>
+ <comment xml:lang="pt_BR">Documento do Microsoft Works</comment>
+ <comment xml:lang="ro">Document Microsoft Works</comment>
+ <comment xml:lang="ru">Документ Microsoft Works</comment>
+ <comment xml:lang="sk">Dokument Microsoft Works</comment>
+ <comment xml:lang="sl">Dokument Microsoft Works</comment>
+ <comment xml:lang="sq">Dokument Microsoft Works</comment>
+ <comment xml:lang="sr">документ Мајкрософт Воркса</comment>
+ <comment xml:lang="sv">Microsoft Works-dokument</comment>
+ <comment xml:lang="tr">Microsoft Works belgesi</comment>
+ <comment xml:lang="uk">документ Microsoft Works</comment>
+ <comment xml:lang="vi">Tài liệu Microsoft Works</comment>
+ <comment xml:lang="zh_CN">Microsoft Works 文档</comment>
+ <comment xml:lang="zh_TW">微軟 Works 文件</comment>
+ <sub-class-of type="application/x-ole-storage"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.wcm"/>
+ <glob pattern="*.wdb"/>
+ <glob pattern="*.wks"/>
+ <glob pattern="*.wps"/>
+ <glob pattern="*.xlr"/>
+ </mime-type>
+ <mime-type type="application/vnd.visio">
+ <comment>Microsoft Visio document</comment>
+ <comment xml:lang="ast">Documentu de Microsoft Visio</comment>
+ <comment xml:lang="bg">Документ — Microsoft Visio</comment>
+ <comment xml:lang="ca">document de Microsoft Visio</comment>
+ <comment xml:lang="cs">dokument Microsoft Visio</comment>
+ <comment xml:lang="da">Microsoft Visio-dokument</comment>
+ <comment xml:lang="de">Microsoft-Visio-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Microsoft Visio</comment>
+ <comment xml:lang="en_GB">Microsoft Visio document</comment>
+ <comment xml:lang="es">documento de Microsoft Visio</comment>
+ <comment xml:lang="eu">Microsoft Visio dokumentua</comment>
+ <comment xml:lang="fi">Microsoft Visio -asiakirja</comment>
+ <comment xml:lang="fr">document Microsoft Visio</comment>
+ <comment xml:lang="ga">cáipéis Microsoft Visio</comment>
+ <comment xml:lang="gl">Documento de Microsoft Visio</comment>
+ <comment xml:lang="he">מסמך </comment>
+ <comment xml:lang="hr">Microsoft Visio dokument</comment>
+ <comment xml:lang="hu">Microsoft Visio dokumentum</comment>
+ <comment xml:lang="ia">Documento Microsoft Visio</comment>
+ <comment xml:lang="id">Dokumen Microsoft Visio</comment>
+ <comment xml:lang="it">Documento Microsoft Visio</comment>
+ <comment xml:lang="ja">Microsoft Visio ドキュメント</comment>
+ <comment xml:lang="ka">Microsoft Visio-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">Microsoft Visio құжаты</comment>
+ <comment xml:lang="ko">Microsoft Visio 문서</comment>
+ <comment xml:lang="lv">Microsoft Visio dokuments</comment>
+ <comment xml:lang="nl">Microsoft Visio document</comment>
+ <comment xml:lang="oc">document Microsoft Visio</comment>
+ <comment xml:lang="pl">Dokument Microsoft Visio</comment>
+ <comment xml:lang="pt">documento Microsoft Visio</comment>
+ <comment xml:lang="pt_BR">Documento do Microsoft Visio</comment>
+ <comment xml:lang="ru">Документ Microsoft Visio</comment>
+ <comment xml:lang="sk">Dokument Microsoft Visio</comment>
+ <comment xml:lang="sl">Dokument Microsoft Visio</comment>
+ <comment xml:lang="sr">документ Мајкрософт Визиа</comment>
+ <comment xml:lang="sv">Microsoft Visio-dokument</comment>
+ <comment xml:lang="tr">Microsoft Visio belgesi</comment>
+ <comment xml:lang="uk">документ Microsoft Visio</comment>
+ <comment xml:lang="zh_CN">Microsoft Visio 文档</comment>
+ <comment xml:lang="zh_TW">Microsoft Visio文件</comment>
+ <sub-class-of type="application/x-ole-storage"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.vsd"/>
+ <glob pattern="*.vst"/>
+ <glob pattern="*.vsw"/>
+ <glob pattern="*.vss"/>
+ </mime-type>
+ <mime-type type="application/msword">
+ <comment>Word document</comment>
+ <comment xml:lang="ar">مستند Word</comment>
+ <comment xml:lang="ast">Documentu de Word</comment>
+ <comment xml:lang="be@latin">Dakument Word</comment>
+ <comment xml:lang="bg">Документ — Word</comment>
+ <comment xml:lang="ca">document Word</comment>
+ <comment xml:lang="cs">dokument Word</comment>
+ <comment xml:lang="da">Worddokument</comment>
+ <comment xml:lang="de">Word-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Word</comment>
+ <comment xml:lang="en_GB">Word document</comment>
+ <comment xml:lang="eo">Word-dokumento</comment>
+ <comment xml:lang="es">documento de Word</comment>
+ <comment xml:lang="eu">Word dokumentua</comment>
+ <comment xml:lang="fi">Word-asiakirja</comment>
+ <comment xml:lang="fo">Word skjal</comment>
+ <comment xml:lang="fr">document Word</comment>
+ <comment xml:lang="ga">cáipéis Word</comment>
+ <comment xml:lang="gl">documento de Word</comment>
+ <comment xml:lang="he">מסמך Word</comment>
+ <comment xml:lang="hr">Word dokument</comment>
+ <comment xml:lang="hu">Word dokumentum</comment>
+ <comment xml:lang="ia">Documento Word</comment>
+ <comment xml:lang="id">Dokumen Word</comment>
+ <comment xml:lang="it">Documento Word</comment>
+ <comment xml:lang="ja">Word ドキュメント</comment>
+ <comment xml:lang="kk">Word құжаты</comment>
+ <comment xml:lang="ko">Word 문서</comment>
+ <comment xml:lang="lt">Word dokumentas</comment>
+ <comment xml:lang="lv">Word dokuments</comment>
+ <comment xml:lang="nb">Word-dokument</comment>
+ <comment xml:lang="nl">Word-document</comment>
+ <comment xml:lang="nn">Word-dokument</comment>
+ <comment xml:lang="oc">document Word</comment>
+ <comment xml:lang="pl">Dokument Word</comment>
+ <comment xml:lang="pt">documento Word</comment>
+ <comment xml:lang="pt_BR">Documento do Word</comment>
+ <comment xml:lang="ro">Document Word</comment>
+ <comment xml:lang="ru">Документ Word</comment>
+ <comment xml:lang="sk">Dokument Word</comment>
+ <comment xml:lang="sl">Dokument Word</comment>
+ <comment xml:lang="sq">Dokument Word</comment>
+ <comment xml:lang="sr">Ворд документ</comment>
+ <comment xml:lang="sv">Word-dokument</comment>
+ <comment xml:lang="tr">Word belgesi</comment>
+ <comment xml:lang="uk">документ Word</comment>
+ <comment xml:lang="vi">Tài liệu Word</comment>
+ <comment xml:lang="zh_CN">Word 文档</comment>
+ <comment xml:lang="zh_TW">Word 文件</comment>
+ <sub-class-of type="application/x-ole-storage"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="60">
+ <match value="\x31\xbe\x00\x00" type="string" offset="0"/>
+ <match value="PO^Q`" type="string" offset="0"/>
+ <match value="\376\067\0\043" type="string" offset="0"/>
+ <match value="\333\245-\0\0\0" type="string" offset="0"/>
+ <match value="MSWordDoc" type="string" offset="2112"/>
+ <match value="MSWordDoc" type="string" offset="2108"/>
+ <match value="Microsoft Word document data" type="string" offset="2112"/>
+ <match value="bjbj" type="string" offset="546"/>
+ <match value="jbjb" type="string" offset="546"/>
+ </magic>
+ <glob pattern="*.doc"/>
+ <alias type="application/vnd.ms-word"/>
+ <alias type="application/x-msword"/>
+ <alias type="zz-application/zz-winassoc-doc"/>
+ </mime-type>
+ <mime-type type="application/msword-template">
+ <comment>Word template</comment>
+ <comment xml:lang="ar">قالب Word</comment>
+ <comment xml:lang="ast">Plantía de Word</comment>
+ <comment xml:lang="be@latin">Šablon Word</comment>
+ <comment xml:lang="bg">Шаблон за документи — Word</comment>
+ <comment xml:lang="ca">plantilla de Word</comment>
+ <comment xml:lang="cs">šablona Word</comment>
+ <comment xml:lang="da">Wordskabelon</comment>
+ <comment xml:lang="de">Word-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο έγγραφο Word</comment>
+ <comment xml:lang="en_GB">Word template</comment>
+ <comment xml:lang="eo">Word-ŝablono</comment>
+ <comment xml:lang="es">plantilla de Word</comment>
+ <comment xml:lang="eu">Word txantiloia</comment>
+ <comment xml:lang="fi">Word-malli</comment>
+ <comment xml:lang="fo">Word formur</comment>
+ <comment xml:lang="fr">modèle Word</comment>
+ <comment xml:lang="ga">teimpléad Word</comment>
+ <comment xml:lang="gl">Plantilla de Word</comment>
+ <comment xml:lang="he">תבנית Word</comment>
+ <comment xml:lang="hr">Word predložak</comment>
+ <comment xml:lang="hu">Word sablon</comment>
+ <comment xml:lang="ia">Patrono Word</comment>
+ <comment xml:lang="id">Templat Word</comment>
+ <comment xml:lang="it">Modello Word</comment>
+ <comment xml:lang="ja">Word テンプレート</comment>
+ <comment xml:lang="kk">Word үлгісі</comment>
+ <comment xml:lang="ko">Word 서식</comment>
+ <comment xml:lang="lt">Word šablonas</comment>
+ <comment xml:lang="lv">Word veidne</comment>
+ <comment xml:lang="nb">Word-mal</comment>
+ <comment xml:lang="nl">Word-sjabloon</comment>
+ <comment xml:lang="nn">Word-mal</comment>
+ <comment xml:lang="oc">modèl Word</comment>
+ <comment xml:lang="pl">Szablon Word</comment>
+ <comment xml:lang="pt">modelo Word</comment>
+ <comment xml:lang="pt_BR">Modelo do Word</comment>
+ <comment xml:lang="ro">Șablon Word</comment>
+ <comment xml:lang="ru">Шаблон Word</comment>
+ <comment xml:lang="sk">Šablóna Word</comment>
+ <comment xml:lang="sl">Predloga dokumenta Microsoft Word</comment>
+ <comment xml:lang="sq">Model Word</comment>
+ <comment xml:lang="sr">Ворд шаблон</comment>
+ <comment xml:lang="sv">Word-mall</comment>
+ <comment xml:lang="tr">Word şablonu</comment>
+ <comment xml:lang="uk">шаблон Word</comment>
+ <comment xml:lang="vi">Mẫu Word</comment>
+ <comment xml:lang="zh_CN">Word 模板</comment>
+ <comment xml:lang="zh_TW">Word 範本</comment>
+ <sub-class-of type="application/msword"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.dot"/>
+ </mime-type>
+ <mime-type type="application/gml+xml">
+ <comment>GML document</comment>
+ <comment xml:lang="ast">Documentu GML</comment>
+ <comment xml:lang="ca">document GML</comment>
+ <comment xml:lang="cs">dokument GML</comment>
+ <comment xml:lang="da">GML-dokument</comment>
+ <comment xml:lang="de">GML-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο GML</comment>
+ <comment xml:lang="en_GB">GML document</comment>
+ <comment xml:lang="es">documento GML</comment>
+ <comment xml:lang="eu">GML dokumentua</comment>
+ <comment xml:lang="fi">GML-asiakirja</comment>
+ <comment xml:lang="fr">document GML</comment>
+ <comment xml:lang="ga">cáipéis GML</comment>
+ <comment xml:lang="gl">Documento GML</comment>
+ <comment xml:lang="he">מסמך GML</comment>
+ <comment xml:lang="hr">GML dokument</comment>
+ <comment xml:lang="hu">GML dokumentum</comment>
+ <comment xml:lang="ia">Documento GML</comment>
+ <comment xml:lang="id">Dokumen GML</comment>
+ <comment xml:lang="it">Documento GML</comment>
+ <comment xml:lang="ja">GML ドキュメント</comment>
+ <comment xml:lang="kk">GML құжаты</comment>
+ <comment xml:lang="ko">GML 문서</comment>
+ <comment xml:lang="lv">GML dokuments</comment>
+ <comment xml:lang="oc">document GML</comment>
+ <comment xml:lang="pl">Dokument GML</comment>
+ <comment xml:lang="pt">documento GML</comment>
+ <comment xml:lang="pt_BR">Documento GML</comment>
+ <comment xml:lang="ru">Документ GML</comment>
+ <comment xml:lang="sk">Dokument GML</comment>
+ <comment xml:lang="sl">Dokument GML</comment>
+ <comment xml:lang="sr">ГМЛ документ</comment>
+ <comment xml:lang="sv">GML-dokument</comment>
+ <comment xml:lang="tr">GML belgesi</comment>
+ <comment xml:lang="uk">документ GML</comment>
+ <comment xml:lang="zh_CN">GML 文档</comment>
+ <comment xml:lang="zh_TW">GML 文件</comment>
+ <acronym>GML</acronym>
+ <expanded-acronym>Geography Markup Language</expanded-acronym>
+ <glob pattern="*.gml"/>
+ <root-XML namespaceURI="http://www.opengis.net/gml/3.2" localName="gml"/>
+ <sub-class-of type="application/xml"/>
+ </mime-type>
+ <mime-type type="application/gnunet-directory">
+ <comment>GNUnet search file</comment>
+ <comment xml:lang="ar">ملف بحث GNUnet</comment>
+ <comment xml:lang="be@latin">fajł pošuku GNUnet</comment>
+ <comment xml:lang="bg">Указател за търсене — GNUnet</comment>
+ <comment xml:lang="ca">fitxer de cerca GNUnet</comment>
+ <comment xml:lang="cs">vyhledávací soubor GNUnet</comment>
+ <comment xml:lang="da">GNunet-søgefil</comment>
+ <comment xml:lang="de">GNUnet-Suchdatei</comment>
+ <comment xml:lang="el">Αρχείο αναζήτησης GNUnet</comment>
+ <comment xml:lang="en_GB">GNUnet search file</comment>
+ <comment xml:lang="es">archivo de búsqueda GNUnet</comment>
+ <comment xml:lang="eu">GNUnet bilaketako fitxategia</comment>
+ <comment xml:lang="fi">GNUnet-hakutiedosto</comment>
+ <comment xml:lang="fo">GNUnet leitifíla</comment>
+ <comment xml:lang="fr">fichier de recherche GNUnet</comment>
+ <comment xml:lang="ga">comhad cuardaigh GNUnet</comment>
+ <comment xml:lang="gl">ficheiro de busca de GNUnet</comment>
+ <comment xml:lang="he">קובץ חיפוש של GNUnet</comment>
+ <comment xml:lang="hr">GNUnet datoteka pretrage</comment>
+ <comment xml:lang="hu">GNUnet keresési fájl</comment>
+ <comment xml:lang="ia">File de recerca GNUnet</comment>
+ <comment xml:lang="id">Berkas telusur GNUnet</comment>
+ <comment xml:lang="it">File ricerca GNUnet</comment>
+ <comment xml:lang="ja">GNUnet 検索ファイル</comment>
+ <comment xml:lang="ka">GNUnet ძებნის ფაილი</comment>
+ <comment xml:lang="kk">GNUnet іздеу файлы</comment>
+ <comment xml:lang="ko">GNUnet 검색 파일</comment>
+ <comment xml:lang="lt">GNUnet paieškos failas</comment>
+ <comment xml:lang="lv">GNUnet meklēšanas datne</comment>
+ <comment xml:lang="nb">GNUnet søkefil</comment>
+ <comment xml:lang="nl">GNUnet-zoekbestand</comment>
+ <comment xml:lang="nn">GNUnet-søkjefil</comment>
+ <comment xml:lang="oc">fichièr de recèrca GNUnet</comment>
+ <comment xml:lang="pl">Plik wyszukiwania GNUnet</comment>
+ <comment xml:lang="pt">ficheiro de procura GNUnet</comment>
+ <comment xml:lang="pt_BR">Arquivo de pesquisa do GNUnet</comment>
+ <comment xml:lang="ro">Fișier căutare GNUnet</comment>
+ <comment xml:lang="ru">Файл поиска GNUnet</comment>
+ <comment xml:lang="sk">Vyhľadávací súbor GNUnet</comment>
+ <comment xml:lang="sl">Iskalna datoteka GNUnet</comment>
+ <comment xml:lang="sq">File kërkimi GNUnet</comment>
+ <comment xml:lang="sr">ГНУнет датотека претраге</comment>
+ <comment xml:lang="sv">GNUnet-sökfil</comment>
+ <comment xml:lang="tr">GNUnet arama dosyası</comment>
+ <comment xml:lang="uk">файл пошуку GNUnet</comment>
+ <comment xml:lang="vi">Tập tin tìm kiếm GNUnet</comment>
+ <comment xml:lang="zh_CN">GNUnet 搜索文件</comment>
+ <comment xml:lang="zh_TW">GNUnet 搜尋檔案</comment>
+ <magic priority="50">
+ <match value="\211GND\r\n\032\n" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.gnd"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-tnef">
+ <comment>TNEF message</comment>
+ <comment xml:lang="ar">رسالة TNEF</comment>
+ <comment xml:lang="ast">Mensaxe TNEF</comment>
+ <comment xml:lang="be@latin">List TNEF</comment>
+ <comment xml:lang="bg">Съобщение — TNEF</comment>
+ <comment xml:lang="ca">missatge TNEF</comment>
+ <comment xml:lang="cs">zpráva TNEF</comment>
+ <comment xml:lang="da">TNEF-meddelelse</comment>
+ <comment xml:lang="de">TNEF-Nachricht</comment>
+ <comment xml:lang="el">Μήνυμα TNEF</comment>
+ <comment xml:lang="en_GB">TNEF message</comment>
+ <comment xml:lang="es">mensaje TNEF</comment>
+ <comment xml:lang="eu">TNEF mezua</comment>
+ <comment xml:lang="fi">TNEF-viesti</comment>
+ <comment xml:lang="fo">TNEF boð</comment>
+ <comment xml:lang="fr">message TNEF</comment>
+ <comment xml:lang="ga">teachtaireacht TNEF</comment>
+ <comment xml:lang="gl">mensaxe TNEF</comment>
+ <comment xml:lang="he">הודעת TNEF</comment>
+ <comment xml:lang="hr">TNEF poruka</comment>
+ <comment xml:lang="hu">TNEF üzenet</comment>
+ <comment xml:lang="ia">Message TNEF</comment>
+ <comment xml:lang="id">Pesan TNEF</comment>
+ <comment xml:lang="it">Messaggio TNEF</comment>
+ <comment xml:lang="ja">TNEF メッセージ</comment>
+ <comment xml:lang="kk">TNEF мәлімдемесі</comment>
+ <comment xml:lang="ko">TNEF 메시지</comment>
+ <comment xml:lang="lt">TNEF žinutė</comment>
+ <comment xml:lang="lv">TNEF ziņojums</comment>
+ <comment xml:lang="nb">TNEF-melding</comment>
+ <comment xml:lang="nl">TNEF-bericht</comment>
+ <comment xml:lang="nn">TNEF-melding</comment>
+ <comment xml:lang="oc">messatge TNEF</comment>
+ <comment xml:lang="pl">Wiadomość TNEF</comment>
+ <comment xml:lang="pt">mensagem TNEF</comment>
+ <comment xml:lang="pt_BR">Mensagem TNEF</comment>
+ <comment xml:lang="ro">Mesaj TNEF</comment>
+ <comment xml:lang="ru">Сообщение TNEF</comment>
+ <comment xml:lang="sk">Správa TNEF</comment>
+ <comment xml:lang="sl">Datoteka sporočila TNEF</comment>
+ <comment xml:lang="sq">Mesazh TNEF</comment>
+ <comment xml:lang="sr">ТНЕФ порука</comment>
+ <comment xml:lang="sv">TNEF-meddelande</comment>
+ <comment xml:lang="tr">TNEF iletisi</comment>
+ <comment xml:lang="uk">повідомлення TNEF</comment>
+ <comment xml:lang="vi">Thông điệp TNEF</comment>
+ <comment xml:lang="zh_CN">TNEF 信件</comment>
+ <comment xml:lang="zh_TW">TNEF 訊息</comment>
+ <acronym>TNEF</acronym>
+ <expanded-acronym>Transport Neutral Encapsulation Format</expanded-acronym>
+ <magic priority="50">
+ <match value="0x223e9f78" type="little32" offset="0"/>
+ </magic>
+ <glob pattern="*.tnef"/>
+ <glob pattern="*.tnf"/>
+ <glob pattern="winmail.dat"/>
+ <alias type="application/ms-tnef"/>
+ </mime-type>
+ <mime-type type="application/vnd.stardivision.calc">
+ <comment>StarCalc spreadsheet</comment>
+ <comment xml:lang="ar">جدول StarCalc</comment>
+ <comment xml:lang="az">StarCalc hesab cədvəli</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš StarCalc</comment>
+ <comment xml:lang="bg">Таблица — StarCalc</comment>
+ <comment xml:lang="ca">full de càlcul de StarCalc</comment>
+ <comment xml:lang="cs">sešit StarCalc</comment>
+ <comment xml:lang="cy">Taenlen StarCalc</comment>
+ <comment xml:lang="da">StarCalc-regneark</comment>
+ <comment xml:lang="de">StarCalc-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο StarCalc</comment>
+ <comment xml:lang="en_GB">StarCalc spreadsheet</comment>
+ <comment xml:lang="eo">StarCalc-kalkultabelo</comment>
+ <comment xml:lang="es">hoja de cálculo de StarCalc</comment>
+ <comment xml:lang="eu">StarCalc kalkulu-orria</comment>
+ <comment xml:lang="fi">StarCalc-taulukko</comment>
+ <comment xml:lang="fo">StarCalc rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul StarCalc</comment>
+ <comment xml:lang="ga">scarbhileog StarCalc</comment>
+ <comment xml:lang="gl">folla de cálculo de StarCalc</comment>
+ <comment xml:lang="he">גליון נתונים של StarCalc</comment>
+ <comment xml:lang="hr">StarCalc proračunska tablica</comment>
+ <comment xml:lang="hu">StarCalc-munkafüzet</comment>
+ <comment xml:lang="ia">Folio de calculo StarCalc</comment>
+ <comment xml:lang="id">Lembar sebar StarCalc</comment>
+ <comment xml:lang="it">Foglio di calcolo StarCalc</comment>
+ <comment xml:lang="ja">StarCalc スプレッドシート</comment>
+ <comment xml:lang="kk">StarCalc электрондық кестесі</comment>
+ <comment xml:lang="ko">StarCalc 스프레드시트</comment>
+ <comment xml:lang="lt">StarCalc skaičialentė</comment>
+ <comment xml:lang="lv">StarCalc izklājlapa</comment>
+ <comment xml:lang="ms">Hamparan StarCalc</comment>
+ <comment xml:lang="nb">StarCalc-regneark</comment>
+ <comment xml:lang="nl">StarCalc-rekenblad</comment>
+ <comment xml:lang="nn">StarCalc-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul StarCalc</comment>
+ <comment xml:lang="pl">Arkusz StarCalc</comment>
+ <comment xml:lang="pt">folha de cálculo do StarCalc</comment>
+ <comment xml:lang="pt_BR">Planilha do StarCalc</comment>
+ <comment xml:lang="ro">Foaie de calcul StarCalc</comment>
+ <comment xml:lang="ru">Электронная таблица StarCalc</comment>
+ <comment xml:lang="sk">Zošit StarCalc</comment>
+ <comment xml:lang="sl">Preglednica StarCalc</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh StarCalc</comment>
+ <comment xml:lang="sr">Стар калк табела</comment>
+ <comment xml:lang="sv">StarCalc-kalkylblad</comment>
+ <comment xml:lang="tr">StarCalc çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця StarCalc</comment>
+ <comment xml:lang="vi">Bảng tính StarCalc</comment>
+ <comment xml:lang="zh_CN">StarCalc 电子表格</comment>
+ <comment xml:lang="zh_TW">StarCalc 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.sdc"/>
+ </mime-type>
+ <mime-type type="application/vnd.stardivision.chart">
+ <comment>StarChart chart</comment>
+ <comment xml:lang="ar">مخطط StarChart</comment>
+ <comment xml:lang="az">StarChart cədvəli</comment>
+ <comment xml:lang="be@latin">Dyjahrama StarChart</comment>
+ <comment xml:lang="bg">Диаграма — StarChart</comment>
+ <comment xml:lang="ca">diagrama de StarChart</comment>
+ <comment xml:lang="cs">graf StarChart</comment>
+ <comment xml:lang="cy">Siart StarChart</comment>
+ <comment xml:lang="da">StarChart-diagram</comment>
+ <comment xml:lang="de">StarChart-Diagramm</comment>
+ <comment xml:lang="el">Γράφημα StarChart</comment>
+ <comment xml:lang="en_GB">StarChart chart</comment>
+ <comment xml:lang="eo">StarChart-diagramo</comment>
+ <comment xml:lang="es">gráfico de StarChart</comment>
+ <comment xml:lang="eu">StarChart diagrama</comment>
+ <comment xml:lang="fi">StarChart-kaavio</comment>
+ <comment xml:lang="fo">StarChart strikumynd</comment>
+ <comment xml:lang="fr">graphique StarChart</comment>
+ <comment xml:lang="ga">cairt StarChart</comment>
+ <comment xml:lang="gl">gráfica de StarChart</comment>
+ <comment xml:lang="he">טבלה של StarChart</comment>
+ <comment xml:lang="hr">StarChart grafikon</comment>
+ <comment xml:lang="hu">StarChart-grafikon</comment>
+ <comment xml:lang="ia">Graphico StarChart</comment>
+ <comment xml:lang="id">Bagan StarChart</comment>
+ <comment xml:lang="it">Grafico StarChart</comment>
+ <comment xml:lang="ja">StarChart チャート</comment>
+ <comment xml:lang="kk">StarChart диаграммасы</comment>
+ <comment xml:lang="ko">StarCalc 표</comment>
+ <comment xml:lang="lt">StarChart diagrama</comment>
+ <comment xml:lang="lv">StarChart diagramma</comment>
+ <comment xml:lang="ms">Carta StarChart</comment>
+ <comment xml:lang="nb">StarChart graf</comment>
+ <comment xml:lang="nl">StarChart-kaart</comment>
+ <comment xml:lang="nn">StarChart-graf</comment>
+ <comment xml:lang="oc">grafic StarChart</comment>
+ <comment xml:lang="pl">Wykres StarChart</comment>
+ <comment xml:lang="pt">gráfico do StarChart</comment>
+ <comment xml:lang="pt_BR">Gráfico do StarChart</comment>
+ <comment xml:lang="ro">Diagramă StarChart</comment>
+ <comment xml:lang="ru">Диаграмма StarChart</comment>
+ <comment xml:lang="sk">Graf StarChart</comment>
+ <comment xml:lang="sl">Datoteka grafikona StarChart</comment>
+ <comment xml:lang="sq">Grafik StarChart</comment>
+ <comment xml:lang="sr">График Стар Графика</comment>
+ <comment xml:lang="sv">StarChart-diagram</comment>
+ <comment xml:lang="tr">StarChart çizgelgesi</comment>
+ <comment xml:lang="uk">діаграма StarChart</comment>
+ <comment xml:lang="vi">Đồ thị StarChart</comment>
+ <comment xml:lang="zh_CN">StarCalc 图表</comment>
+ <comment xml:lang="zh_TW">StarChart 圖表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.sds"/>
+ </mime-type>
+ <mime-type type="application/vnd.stardivision.draw">
+ <comment>StarDraw drawing</comment>
+ <comment xml:lang="ar">تصميم StarDraw</comment>
+ <comment xml:lang="az">StarDraw çəkimi</comment>
+ <comment xml:lang="be@latin">Rysunak StarDraw</comment>
+ <comment xml:lang="bg">Чертеж — StarDraw</comment>
+ <comment xml:lang="ca">dibuix de StarDraw</comment>
+ <comment xml:lang="cs">kresba StarDraw</comment>
+ <comment xml:lang="cy">Darlun StarDraw</comment>
+ <comment xml:lang="da">StarDraw-tegning</comment>
+ <comment xml:lang="de">StarDraw-Zeichnung</comment>
+ <comment xml:lang="el">Σχέδιο StarDraw</comment>
+ <comment xml:lang="en_GB">StarDraw drawing</comment>
+ <comment xml:lang="eo">StarDraw-grafikaĵo</comment>
+ <comment xml:lang="es">dibujo de StarDraw</comment>
+ <comment xml:lang="eu">StarDraw marrazkia</comment>
+ <comment xml:lang="fi">StarDraw-piirros</comment>
+ <comment xml:lang="fo">StarDraw tekning</comment>
+ <comment xml:lang="fr">dessin StarDraw</comment>
+ <comment xml:lang="ga">líníocht StarDraw</comment>
+ <comment xml:lang="gl">debuxo de StarDraw</comment>
+ <comment xml:lang="he">ציור של StarDrawing</comment>
+ <comment xml:lang="hr">StarDraw crtež</comment>
+ <comment xml:lang="hu">StarDraw-rajz</comment>
+ <comment xml:lang="ia">Designo StarDraw</comment>
+ <comment xml:lang="id">Gambar StarDraw</comment>
+ <comment xml:lang="it">Disegno StarDraw</comment>
+ <comment xml:lang="ja">StarDraw ドロー</comment>
+ <comment xml:lang="kk">StarDraw суреті</comment>
+ <comment xml:lang="ko">StarCalc 드로잉</comment>
+ <comment xml:lang="lt">StarDraw piešinys</comment>
+ <comment xml:lang="lv">StarDraw zīmējums</comment>
+ <comment xml:lang="ms">Lukisan StarDraw</comment>
+ <comment xml:lang="nb">StarDraw tegning</comment>
+ <comment xml:lang="nl">StarDraw-tekening</comment>
+ <comment xml:lang="nn">StarDraw-teikning</comment>
+ <comment xml:lang="oc">dessenh StarDraw</comment>
+ <comment xml:lang="pl">Rysunek StarDraw</comment>
+ <comment xml:lang="pt">desenho do StarDraw</comment>
+ <comment xml:lang="pt_BR">Desenho do StarDraw</comment>
+ <comment xml:lang="ro">Desen StarDraw</comment>
+ <comment xml:lang="ru">Рисунок StarDraw</comment>
+ <comment xml:lang="sk">Kresba StarDraw</comment>
+ <comment xml:lang="sl">Datoteka risbe StarDraw</comment>
+ <comment xml:lang="sq">Vizatim StarDraw</comment>
+ <comment xml:lang="sr">Цртеж Стар Цртежа</comment>
+ <comment xml:lang="sv">StarDraw-teckning</comment>
+ <comment xml:lang="tr">StarDraw çizimi</comment>
+ <comment xml:lang="uk">малюнок StarDraw</comment>
+ <comment xml:lang="vi">Bản vẽ StarDraw</comment>
+ <comment xml:lang="zh_CN">StarDraw 绘图</comment>
+ <comment xml:lang="zh_TW">StarDraw 繪圖</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.sda"/>
+ </mime-type>
+ <mime-type type="application/vnd.stardivision.impress">
+ <comment>StarImpress presentation</comment>
+ <comment xml:lang="ar">عرض تقديمي StarImpress</comment>
+ <comment xml:lang="az">StarImpress təqdimatı</comment>
+ <comment xml:lang="be@latin">Prezentacyja StarImpress</comment>
+ <comment xml:lang="bg">Презентация — StarImpress</comment>
+ <comment xml:lang="ca">presentació de StarImpress</comment>
+ <comment xml:lang="cs">prezentace StarImpress</comment>
+ <comment xml:lang="cy">Cyflwyniad StarImpress</comment>
+ <comment xml:lang="da">StarImpress-præsentation</comment>
+ <comment xml:lang="de">StarImpress-Präsentation</comment>
+ <comment xml:lang="el">Παρουσίαση StarImpress</comment>
+ <comment xml:lang="en_GB">StarImpress presentation</comment>
+ <comment xml:lang="eo">StarImpress-prezentaĵo</comment>
+ <comment xml:lang="es">presentación de StarImpress</comment>
+ <comment xml:lang="eu">StarImpress aurkezpena</comment>
+ <comment xml:lang="fi">StarImpress-esitys</comment>
+ <comment xml:lang="fo">StarImpress framløga</comment>
+ <comment xml:lang="fr">présentation StarImpress</comment>
+ <comment xml:lang="ga">láithreoireacht StarImpress</comment>
+ <comment xml:lang="gl">presentación de StarImpress</comment>
+ <comment xml:lang="he">מצגת של StarImpress</comment>
+ <comment xml:lang="hr">StarImpress prezentacija</comment>
+ <comment xml:lang="hu">StarImpress-bemutató</comment>
+ <comment xml:lang="ia">Presentation StarImpress</comment>
+ <comment xml:lang="id">Presentasi StarImpress</comment>
+ <comment xml:lang="it">Presentazione StarImpress</comment>
+ <comment xml:lang="ja">StarImpress プレゼンテーション</comment>
+ <comment xml:lang="kk">StarImpress презентациясы</comment>
+ <comment xml:lang="ko">StarImpress 프레젠테이션</comment>
+ <comment xml:lang="lt">StarImpress pateiktis</comment>
+ <comment xml:lang="lv">StarImpress prezentācija</comment>
+ <comment xml:lang="ms">Persembahan StarImpress</comment>
+ <comment xml:lang="nb">StarImpress-presentasjon</comment>
+ <comment xml:lang="nl">StarImpress-presentatie</comment>
+ <comment xml:lang="nn">StarImpress-presentasjon</comment>
+ <comment xml:lang="oc">presentacion StarImpress</comment>
+ <comment xml:lang="pl">Prezentacja StarImpress</comment>
+ <comment xml:lang="pt">apresentação do StarImpress</comment>
+ <comment xml:lang="pt_BR">Apresentação do StarImpress</comment>
+ <comment xml:lang="ro">Prezentare StarImpress</comment>
+ <comment xml:lang="ru">Презентация StarImpress</comment>
+ <comment xml:lang="sk">Prezentácia StarImpress</comment>
+ <comment xml:lang="sl">Predstavitev StarImpress</comment>
+ <comment xml:lang="sq">Prezantim StarImpress</comment>
+ <comment xml:lang="sr">Презентација Стар Импреса</comment>
+ <comment xml:lang="sv">StarImpress-presentation</comment>
+ <comment xml:lang="tr">StarImpress sunumu</comment>
+ <comment xml:lang="uk">презентація StarImpress</comment>
+ <comment xml:lang="vi">Trình diễn StarImpress</comment>
+ <comment xml:lang="zh_CN">StarImpress 演示文稿</comment>
+ <comment xml:lang="zh_TW">StarImpress 簡報檔</comment>
+ <generic-icon name="x-office-presentation"/>
+ <glob pattern="*.sdd"/>
+ <glob pattern="*.sdp"/>
+ </mime-type>
+ <mime-type type="application/vnd.stardivision.mail">
+ <comment>StarMail email</comment>
+ <comment xml:lang="ar">بريد StarMail الإلكتروني</comment>
+ <comment xml:lang="be@latin">Email StarMail</comment>
+ <comment xml:lang="bg">Електронно писмо — StarMail</comment>
+ <comment xml:lang="ca">correu electrònic de StarMail</comment>
+ <comment xml:lang="cs">e-mail StarMail</comment>
+ <comment xml:lang="da">StarMail-e-brev</comment>
+ <comment xml:lang="de">StarMail-E-Mail</comment>
+ <comment xml:lang="el">Ηλ. μήνυμα StarMail</comment>
+ <comment xml:lang="en_GB">StarMail email</comment>
+ <comment xml:lang="eo">StarMail-retpoŝto</comment>
+ <comment xml:lang="es">correo electrónico de StarMail</comment>
+ <comment xml:lang="eu">StarMail helb.el.</comment>
+ <comment xml:lang="fi">StarMail-sähköposti</comment>
+ <comment xml:lang="fo">StarMail t-postur</comment>
+ <comment xml:lang="fr">courriel StarMail</comment>
+ <comment xml:lang="ga">ríomhphost StarMail</comment>
+ <comment xml:lang="gl">Correo electrónico de StarMail</comment>
+ <comment xml:lang="he">דוא״ל של StarMail</comment>
+ <comment xml:lang="hr">StarMail e-pošta</comment>
+ <comment xml:lang="hu">StarMail e-mail</comment>
+ <comment xml:lang="ia">Message electronic StarMail</comment>
+ <comment xml:lang="id">Email StarMail</comment>
+ <comment xml:lang="it">Email StarMail</comment>
+ <comment xml:lang="ja">StarMail メール</comment>
+ <comment xml:lang="kk">StarMail электрондық хаты</comment>
+ <comment xml:lang="ko">StarMail 전자우편</comment>
+ <comment xml:lang="lt">StarMail el. laiškas</comment>
+ <comment xml:lang="lv">StarMail epasts</comment>
+ <comment xml:lang="ms">Emel StarMail</comment>
+ <comment xml:lang="nb">StarMail-melding</comment>
+ <comment xml:lang="nl">StarMail-e-mail</comment>
+ <comment xml:lang="nn">StarMail-fil</comment>
+ <comment xml:lang="oc">corrièr electronic StarMail</comment>
+ <comment xml:lang="pl">E-Mail StarMail</comment>
+ <comment xml:lang="pt">email do StarMail</comment>
+ <comment xml:lang="pt_BR">E-mail do StarMail</comment>
+ <comment xml:lang="ro">Email StarEmail</comment>
+ <comment xml:lang="ru">Электронное письмо StarMail</comment>
+ <comment xml:lang="sk">E-mail StarMail</comment>
+ <comment xml:lang="sl">Datoteka pošte StarMail</comment>
+ <comment xml:lang="sq">Mesazh StarMail</comment>
+ <comment xml:lang="sr">Ел. пошта Стар Поште</comment>
+ <comment xml:lang="sv">StarMail-e-post</comment>
+ <comment xml:lang="tr">StarMail epostası</comment>
+ <comment xml:lang="uk">поштове повідомлення StarMail</comment>
+ <comment xml:lang="vi">Thư điện tử StarMail</comment>
+ <comment xml:lang="zh_CN">StarMail 电子邮件</comment>
+ <comment xml:lang="zh_TW">StarMail 郵件</comment>
+ <glob pattern="*.smd"/>
+ </mime-type>
+ <mime-type type="application/vnd.stardivision.math">
+ <comment>StarMath formula</comment>
+ <comment xml:lang="ar">صيغة StarMath</comment>
+ <comment xml:lang="be@latin">Formuła StarMath</comment>
+ <comment xml:lang="bg">Формула — StarMath</comment>
+ <comment xml:lang="ca">fórmula de StarMath</comment>
+ <comment xml:lang="cs">vzorec StarMath</comment>
+ <comment xml:lang="da">StarMath-formel</comment>
+ <comment xml:lang="de">StarMath-Formel</comment>
+ <comment xml:lang="el">Μαθηματικός τύπος StarMath</comment>
+ <comment xml:lang="en_GB">StarMath formula</comment>
+ <comment xml:lang="eo">StarMath-formulo</comment>
+ <comment xml:lang="es">fórmula de StarMath</comment>
+ <comment xml:lang="eu">StarMath formula</comment>
+ <comment xml:lang="fi">StarMath-kaava</comment>
+ <comment xml:lang="fo">StarMath frymil</comment>
+ <comment xml:lang="fr">formule StarMath</comment>
+ <comment xml:lang="ga">foirmle StarMath</comment>
+ <comment xml:lang="gl">fórmula de StarMath</comment>
+ <comment xml:lang="he">נוסחה של StarMath</comment>
+ <comment xml:lang="hr">StarMath formula</comment>
+ <comment xml:lang="hu">StarMath-képlet</comment>
+ <comment xml:lang="ia">Formula StarMath</comment>
+ <comment xml:lang="id">Formula StarMath</comment>
+ <comment xml:lang="it">Formula StarMath</comment>
+ <comment xml:lang="ja">StarMath 計算式</comment>
+ <comment xml:lang="kk">StarMath формуласы</comment>
+ <comment xml:lang="ko">StarMath 수식</comment>
+ <comment xml:lang="lt">StarMath formulė</comment>
+ <comment xml:lang="lv">StarMath formula</comment>
+ <comment xml:lang="ms">Formula StarMath</comment>
+ <comment xml:lang="nb">StarMath-formel</comment>
+ <comment xml:lang="nl">StarMath-formule</comment>
+ <comment xml:lang="nn">StarMath-formel</comment>
+ <comment xml:lang="oc">formula StarMath</comment>
+ <comment xml:lang="pl">Formuła StarMath</comment>
+ <comment xml:lang="pt">fórmula do StarMath</comment>
+ <comment xml:lang="pt_BR">Fórmula do StarMath</comment>
+ <comment xml:lang="ro">Formulă StarMath</comment>
+ <comment xml:lang="ru">Формула StarMath</comment>
+ <comment xml:lang="sk">Vzorec StarMath</comment>
+ <comment xml:lang="sl">Datoteka formule StarMath</comment>
+ <comment xml:lang="sq">Formulë StarMath</comment>
+ <comment xml:lang="sr">Формула Стар Математике</comment>
+ <comment xml:lang="sv">StarMath-formel</comment>
+ <comment xml:lang="tr">StarMath formülü</comment>
+ <comment xml:lang="uk">формула StarMath</comment>
+ <comment xml:lang="vi">Công thức StarMath</comment>
+ <comment xml:lang="zh_CN">StarMath 公式</comment>
+ <comment xml:lang="zh_TW">StarMath 公式</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.smf"/>
+ </mime-type>
+ <mime-type type="application/vnd.stardivision.writer">
+ <comment>StarWriter document</comment>
+ <comment xml:lang="ar">مستند StarWriter</comment>
+ <comment xml:lang="ast">Documentu de StarWriter</comment>
+ <comment xml:lang="az">StarWriter sənədi</comment>
+ <comment xml:lang="be@latin">Dakument StarWriter</comment>
+ <comment xml:lang="bg">Документ — StarWriter</comment>
+ <comment xml:lang="ca">document StarWriter</comment>
+ <comment xml:lang="cs">dokument StarWriter</comment>
+ <comment xml:lang="cy">Dogfen StarWriter</comment>
+ <comment xml:lang="da">StarWriter-dokument</comment>
+ <comment xml:lang="de">StarWriter-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο StarWriter</comment>
+ <comment xml:lang="en_GB">StarWriter document</comment>
+ <comment xml:lang="eo">StarWriter-dokumento</comment>
+ <comment xml:lang="es">documento de StarWriter</comment>
+ <comment xml:lang="eu">StarWriter dokumentua</comment>
+ <comment xml:lang="fi">StarWriter-asiakirja</comment>
+ <comment xml:lang="fo">StarWriter skjal</comment>
+ <comment xml:lang="fr">document StarWriter</comment>
+ <comment xml:lang="ga">cáipéis StarWriter</comment>
+ <comment xml:lang="gl">documento de StarWriter</comment>
+ <comment xml:lang="he">מסמך של StarWriter</comment>
+ <comment xml:lang="hr">StarWriter dokument</comment>
+ <comment xml:lang="hu">StarWriter-dokumentum</comment>
+ <comment xml:lang="ia">Documento StarWriter</comment>
+ <comment xml:lang="id">Dokumen StarWriter</comment>
+ <comment xml:lang="it">Documento StrarWriter</comment>
+ <comment xml:lang="ja">StarWriter ドキュメント</comment>
+ <comment xml:lang="kk">StarWriter құжаты</comment>
+ <comment xml:lang="ko">StarWriter 문서</comment>
+ <comment xml:lang="lt">StarWriter dokumentas</comment>
+ <comment xml:lang="lv">StarWriter dokuments</comment>
+ <comment xml:lang="ms">Dokumen StarWriter</comment>
+ <comment xml:lang="nb">StarWriter-dokument</comment>
+ <comment xml:lang="nl">StarWriter-document</comment>
+ <comment xml:lang="nn">StarWriter document</comment>
+ <comment xml:lang="oc">document StarWriter</comment>
+ <comment xml:lang="pl">Dokument StarWriter</comment>
+ <comment xml:lang="pt">documento do StarWriter</comment>
+ <comment xml:lang="pt_BR">Documento do StarWriter</comment>
+ <comment xml:lang="ro">Document StarWriter</comment>
+ <comment xml:lang="ru">Документ StarWriter</comment>
+ <comment xml:lang="sk">Dokument StarWriter</comment>
+ <comment xml:lang="sl">Dokument StarWriter</comment>
+ <comment xml:lang="sq">Dokument StarWriter</comment>
+ <comment xml:lang="sr">Документ Стар Писца</comment>
+ <comment xml:lang="sv">StarWriter-dokument</comment>
+ <comment xml:lang="tr">StarWriter belgesi</comment>
+ <comment xml:lang="uk">документ StarWriter</comment>
+ <comment xml:lang="vi">Tài liệu StarWriter</comment>
+ <comment xml:lang="zh_CN">StarWriter 文档</comment>
+ <comment xml:lang="zh_TW">StarWriter 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.sdw"/>
+ <glob pattern="*.vor"/>
+ <glob pattern="*.sgl"/>
+ <magic priority="90">
+ <match value="StarWriter" type="string" offset="2089"/>
+ </magic>
+ <alias type="application/vnd.stardivision.writer-global"/>
+ </mime-type>
+ <mime-type type="application/vnd.sun.xml.calc">
+ <comment>OpenOffice Calc spreadsheet</comment>
+ <comment xml:lang="ar">جدول Calc المكتب المفتوح</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš OpenOffice Calc</comment>
+ <comment xml:lang="bg">Таблица — OpenOffice Calc</comment>
+ <comment xml:lang="ca">full de càlcul d'OpenOffice Calc</comment>
+ <comment xml:lang="cs">sešit OpenOffice Calc</comment>
+ <comment xml:lang="da">OpenOffice Calc-regneark</comment>
+ <comment xml:lang="de">OpenOffice-Calc-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο OpenOffice Calc</comment>
+ <comment xml:lang="en_GB">OpenOffice Calc spreadsheet</comment>
+ <comment xml:lang="es">hoja de cálculo de OpenOffice Calc</comment>
+ <comment xml:lang="eu">OpenOffice.org Calc kalkulu-orria</comment>
+ <comment xml:lang="fi">OpenOffice Calc -taulukko</comment>
+ <comment xml:lang="fo">OpenOffice Calc rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul OpenOffice Calc</comment>
+ <comment xml:lang="ga">scarbhileog OpenOffice Calc</comment>
+ <comment xml:lang="gl">folla de cálculo de OpenOffice Calc</comment>
+ <comment xml:lang="he">גליון נתונים של OpenOffice Calc</comment>
+ <comment xml:lang="hr">OpenOffice Calc proračunska tablica</comment>
+ <comment xml:lang="hu">OpenOffice Calc táblázat</comment>
+ <comment xml:lang="ia">Folio de calculo OpenOffice Calc</comment>
+ <comment xml:lang="id">Lembar sebar OpenOffice Calc</comment>
+ <comment xml:lang="it">Foglio di calcolo OpenOffice Calc</comment>
+ <comment xml:lang="ja">OpenOffice Calc スプレッドシート</comment>
+ <comment xml:lang="ka">OpenOffice Calc-ის ცხრილი</comment>
+ <comment xml:lang="kk">OpenOffice Calc электрондық кестесі</comment>
+ <comment xml:lang="ko">OpenOffice Calc 스프레드시트</comment>
+ <comment xml:lang="lt">OpenOffice Calc skaičialentė</comment>
+ <comment xml:lang="lv">OpenOffice Calc izklājlapa</comment>
+ <comment xml:lang="nb">OpenOffice Calc-regneark</comment>
+ <comment xml:lang="nl">OpenOffice.org Calc-rekenblad</comment>
+ <comment xml:lang="nn">OpenOffice Calc-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul OpenOffice Calc</comment>
+ <comment xml:lang="pl">Arkusz kalkulacyjny OpenOffice.org Calc</comment>
+ <comment xml:lang="pt">folha de cálculo OpenOffice Calc</comment>
+ <comment xml:lang="pt_BR">Planilha do OpenOffice Calc</comment>
+ <comment xml:lang="ro">Foaie de calcul OpenOffice Calc</comment>
+ <comment xml:lang="ru">Электронная таблица OpenOffice Calc</comment>
+ <comment xml:lang="sk">Zošit OpenOffice Calc</comment>
+ <comment xml:lang="sl">Razpredelnica OpenOffice.org Calc</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh OpenOffice Calc</comment>
+ <comment xml:lang="sr">Табела Опен Офис Рачуна</comment>
+ <comment xml:lang="sv">OpenOffice Calc-kalkylblad</comment>
+ <comment xml:lang="tr">OpenOffice Calc çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця OpenOffice Calc</comment>
+ <comment xml:lang="vi">Bảng tính Calc của OpenOffice.org</comment>
+ <comment xml:lang="zh_CN">OpenOffice Calc 电子表格</comment>
+ <comment xml:lang="zh_TW">OpenOffice Calc 試算表</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.sun.xml.calc" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.sxc"/>
+ </mime-type>
+ <mime-type type="application/vnd.sun.xml.calc.template">
+ <comment>OpenOffice Calc template</comment>
+ <comment xml:lang="ar">قالب Calc المكتب المفتوح</comment>
+ <comment xml:lang="be@latin">Šablon OpenOffice Calc</comment>
+ <comment xml:lang="bg">Шаблон за таблици — OpenOffice Calc</comment>
+ <comment xml:lang="ca">plantilla d'OpenOffice Calc</comment>
+ <comment xml:lang="cs">šablona OpenOffice Calc</comment>
+ <comment xml:lang="da">OpenOffice Calc-skabelon</comment>
+ <comment xml:lang="de">OpenOffice-Calc-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο OpenOffice Calc</comment>
+ <comment xml:lang="en_GB">OpenOffice Calc template</comment>
+ <comment xml:lang="es">plantilla de OpenOffice Calc</comment>
+ <comment xml:lang="eu">OpenOffice Calc txantiloia</comment>
+ <comment xml:lang="fi">OpenOffice Calc -malli</comment>
+ <comment xml:lang="fo">OpenOffice Calc formur</comment>
+ <comment xml:lang="fr">modèle OpenOffice Calc</comment>
+ <comment xml:lang="ga">teimpléad OpenOffice Calc</comment>
+ <comment xml:lang="gl">modelo de OpenOffice Calc</comment>
+ <comment xml:lang="he">תבנית של OpenOffice Calc</comment>
+ <comment xml:lang="hr">OpenOffice Calc predložak</comment>
+ <comment xml:lang="hu">OpenOffice Calc sablon</comment>
+ <comment xml:lang="ia">Patrono OpenOffice Calc</comment>
+ <comment xml:lang="id">Templat OpenOffice Calc</comment>
+ <comment xml:lang="it">Modello OpenOffice Calc</comment>
+ <comment xml:lang="ja">OpenOffice Calc テンプレート</comment>
+ <comment xml:lang="ka">OpenOffice Calc-ის შაბლონი</comment>
+ <comment xml:lang="kk">OpenOffice Calc үлгісі</comment>
+ <comment xml:lang="ko">OpenOffice Calc 스프레드시트 문서 서식</comment>
+ <comment xml:lang="lt">OpenOffice Calc šablonas</comment>
+ <comment xml:lang="lv">OpenOffice Calc veidne</comment>
+ <comment xml:lang="nb">OpenOffice Calc-mal</comment>
+ <comment xml:lang="nl">OpenOffice.org Calc-sjabloon</comment>
+ <comment xml:lang="nn">OpenOffice Calc-mal</comment>
+ <comment xml:lang="oc">modèl OpenOffice Calc</comment>
+ <comment xml:lang="pl">Szablon arkusza OpenOffice.org Calc</comment>
+ <comment xml:lang="pt">modelo OpenOffice Calc</comment>
+ <comment xml:lang="pt_BR">Modelo do OpenOffice Calc</comment>
+ <comment xml:lang="ro">Șablon OpenOffice Calc</comment>
+ <comment xml:lang="ru">Шаблон OpenOffice Calc</comment>
+ <comment xml:lang="sk">Šablóna OpenOffice Calc</comment>
+ <comment xml:lang="sl">Predloga OpenOffice.org Calc</comment>
+ <comment xml:lang="sq">Model OpenOffice Calc</comment>
+ <comment xml:lang="sr">Шаблон Опен Офис Рачуна</comment>
+ <comment xml:lang="sv">OpenOffice Calc-mall</comment>
+ <comment xml:lang="tr">OpenOffice Calc şablonu</comment>
+ <comment xml:lang="uk">шаблон ел.таблиці OpenOffice Calc</comment>
+ <comment xml:lang="vi">Mẫu bảng tính Calc của OpenOffice.org</comment>
+ <comment xml:lang="zh_CN">OpenOffice Calc 模板</comment>
+ <comment xml:lang="zh_TW">OpenOffice Calc 範本</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.sun.xml.calc" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.stc"/>
+ </mime-type>
+ <mime-type type="application/vnd.sun.xml.draw">
+ <comment>OpenOffice Draw drawing</comment>
+ <comment xml:lang="ar">تصميم Draw المكتب المفتوح</comment>
+ <comment xml:lang="be@latin">Rysunak OpenOffice Draw</comment>
+ <comment xml:lang="bg">Чертеж — OpenOffice Draw</comment>
+ <comment xml:lang="ca">dibuix d'OpenOffice Draw</comment>
+ <comment xml:lang="cs">kresba OpenOffice Draw</comment>
+ <comment xml:lang="da">OpenOffice Draw-tegning</comment>
+ <comment xml:lang="de">OpenOffice-Draw-Zeichnung</comment>
+ <comment xml:lang="el">Σχέδιο OpenOffice Draw</comment>
+ <comment xml:lang="en_GB">OpenOffice Draw drawing</comment>
+ <comment xml:lang="es">dibujo de OpenOffice Draw</comment>
+ <comment xml:lang="eu">OpenOffice.org Draw marrazkia</comment>
+ <comment xml:lang="fi">OpenOffice Draw -piirros</comment>
+ <comment xml:lang="fo">OpenOffice Draw tekning</comment>
+ <comment xml:lang="fr">dessin OpenOffice Draw</comment>
+ <comment xml:lang="ga">líníocht OpenOffice Draw</comment>
+ <comment xml:lang="gl">debuxo de OpenOffice Draw</comment>
+ <comment xml:lang="he">ציור של OpenOffice Draw</comment>
+ <comment xml:lang="hr">OpenOffice Draw crtež</comment>
+ <comment xml:lang="hu">OpenOffice Draw rajz</comment>
+ <comment xml:lang="ia">Designo OpenOffice Draw</comment>
+ <comment xml:lang="id">Gambar OpenOffice Draw</comment>
+ <comment xml:lang="it">Disegno OpenOffice Draw</comment>
+ <comment xml:lang="ja">OpenOffice Draw ドロー</comment>
+ <comment xml:lang="ka">OpenOffice Draw-ის ნახაზი</comment>
+ <comment xml:lang="kk">OpenOffice Draw суреті</comment>
+ <comment xml:lang="ko">OpenOffice Draw 그림</comment>
+ <comment xml:lang="lt">OpenOffice Draw piešinys</comment>
+ <comment xml:lang="lv">OpenOffice Draw zīmējums</comment>
+ <comment xml:lang="nb">OpenOffice Draw-tegning</comment>
+ <comment xml:lang="nl">OpenOffice.org Draw-tekening</comment>
+ <comment xml:lang="nn">OpenOffice Draw-teikning</comment>
+ <comment xml:lang="oc">dessenh OpenOffice Draw</comment>
+ <comment xml:lang="pl">Rysunek OpenOffice.org Draw</comment>
+ <comment xml:lang="pt">desenho OpenOffice Draw</comment>
+ <comment xml:lang="pt_BR">Desenho do OpenOffice Draw</comment>
+ <comment xml:lang="ro">Desen OpenOffice Draw</comment>
+ <comment xml:lang="ru">Рисунок OpenOffice Draw</comment>
+ <comment xml:lang="sk">Kresba OpenOffice Draw</comment>
+ <comment xml:lang="sl">Datoteka risbe OpenOffice.org Draw</comment>
+ <comment xml:lang="sq">Vizatim OpenOffice Draw</comment>
+ <comment xml:lang="sr">Цртеж Опен Офис Цртежа</comment>
+ <comment xml:lang="sv">OpenOffice Draw-teckning</comment>
+ <comment xml:lang="tr">OpenOffice Draw çizimi</comment>
+ <comment xml:lang="uk">малюнок OpenOffice Draw</comment>
+ <comment xml:lang="vi">Bản vẽ Draw của OpenOffice.org</comment>
+ <comment xml:lang="zh_CN">OpenOffice Draw 绘图</comment>
+ <comment xml:lang="zh_TW">OpenOffice Draw 繪圖</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="image-x-generic"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.sun.xml.draw" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.sxd"/>
+ </mime-type>
+ <mime-type type="application/vnd.sun.xml.draw.template">
+ <comment>OpenOffice Draw template</comment>
+ <comment xml:lang="ar">قالب Draw المكتب المفتوح</comment>
+ <comment xml:lang="be@latin">Šablon OpenOffice Draw</comment>
+ <comment xml:lang="bg">Шаблон за чертежи — OpenOffice Draw</comment>
+ <comment xml:lang="ca">plantilla d'OpenOffice Draw</comment>
+ <comment xml:lang="cs">šablona OpenOffice Draw</comment>
+ <comment xml:lang="da">OpenOffice Draw-skabelon</comment>
+ <comment xml:lang="de">OpenOffice-Draw-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο OpenOffice Draw</comment>
+ <comment xml:lang="en_GB">OpenOffice Draw template</comment>
+ <comment xml:lang="es">plantilla de OpenOffice Draw</comment>
+ <comment xml:lang="eu">OpenOffice Draw txantiloia</comment>
+ <comment xml:lang="fi">OpenOffice Draw -malli</comment>
+ <comment xml:lang="fo">OpenOffice Draw formur</comment>
+ <comment xml:lang="fr">modèle OpenOffice Draw</comment>
+ <comment xml:lang="ga">teimpléad OpenOffice Draw</comment>
+ <comment xml:lang="gl">modelo de OpenOffice Draw</comment>
+ <comment xml:lang="he">תבנית של OpenOffice Draw</comment>
+ <comment xml:lang="hr">Predložak OpenOffice Drawa</comment>
+ <comment xml:lang="hu">OpenOffice Draw sablon</comment>
+ <comment xml:lang="ia">Patrono OpenOffice Draw</comment>
+ <comment xml:lang="id">Templat OpenOffice Draw</comment>
+ <comment xml:lang="it">Modello OpenOffice Draw</comment>
+ <comment xml:lang="ja">OpenOffice Draw テンプレート</comment>
+ <comment xml:lang="ka">OpenOffice Draw-ის შაბლონი</comment>
+ <comment xml:lang="kk">OpenOffice Draw үлгісі</comment>
+ <comment xml:lang="ko">OpenOffice Draw 그림 문서 서식</comment>
+ <comment xml:lang="lt">OpenOffice Draw šablonas</comment>
+ <comment xml:lang="lv">OpenOffice Draw veidne</comment>
+ <comment xml:lang="nb">OpenOffice Draw-mal</comment>
+ <comment xml:lang="nl">OpenOffice.org Draw-sjabloon</comment>
+ <comment xml:lang="nn">OpenOffice Draw-mal</comment>
+ <comment xml:lang="oc">modèl OpenOffice Draw</comment>
+ <comment xml:lang="pl">Szablon rysunku OpenOffice.org Draw</comment>
+ <comment xml:lang="pt">modelo OpenOffice Draw</comment>
+ <comment xml:lang="pt_BR">Modelo do OpenOffice Draw</comment>
+ <comment xml:lang="ro">Șablon OpenOffice Draw</comment>
+ <comment xml:lang="ru">Шаблон OpenOffice Draw</comment>
+ <comment xml:lang="sk">Šablóna OpenOffice Draw</comment>
+ <comment xml:lang="sl">Predloga OpenOffice.org Draw</comment>
+ <comment xml:lang="sq">Model OpenOffice Draw</comment>
+ <comment xml:lang="sr">Шаблон Опен Офис Цртежа</comment>
+ <comment xml:lang="sv">OpenOffice Draw-mall</comment>
+ <comment xml:lang="tr">OpenOffice Draw şablonu</comment>
+ <comment xml:lang="uk">шаблон малюнку OpenOffice Draw</comment>
+ <comment xml:lang="vi">Mẫu bản vẽ Draw của OpenOffice.org</comment>
+ <comment xml:lang="zh_CN">OpenOffice Draw 模板</comment>
+ <comment xml:lang="zh_TW">OpenOffice Draw 範本</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="image-x-generic"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.sun.xml.draw" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.std"/>
+ </mime-type>
+ <mime-type type="application/vnd.sun.xml.impress">
+ <comment>OpenOffice Impress presentation</comment>
+ <comment xml:lang="ar">عرض تقديمي Impress المكتب المفتوح</comment>
+ <comment xml:lang="az">OpenOffice Impress sənədi</comment>
+ <comment xml:lang="be@latin">Prezentacyja OpenOffice Impress</comment>
+ <comment xml:lang="bg">Презентация — OpenOffice Impress</comment>
+ <comment xml:lang="ca">presentació d'OpenOffice Impress</comment>
+ <comment xml:lang="cs">prezentace OpenOffice Impress</comment>
+ <comment xml:lang="cy">Cyflwyniad OpenOffice (Impress)</comment>
+ <comment xml:lang="da">OpenOffice Impress-præsentation</comment>
+ <comment xml:lang="de">OpenOffice-Impress-Vorlage</comment>
+ <comment xml:lang="el">Παρουσίαση OpenOffice Impress</comment>
+ <comment xml:lang="en_GB">OpenOffice Impress presentation</comment>
+ <comment xml:lang="es">presentación de OpenOffice Impress</comment>
+ <comment xml:lang="eu">OpenOffice.org Impress aurkezpena</comment>
+ <comment xml:lang="fi">OpenOffice Impress -esitys</comment>
+ <comment xml:lang="fo">OpenOffice Impress framløga</comment>
+ <comment xml:lang="fr">présentation OpenOffice Impress</comment>
+ <comment xml:lang="ga">láithreoireacht OpenOffice Impress</comment>
+ <comment xml:lang="gl">presentación de de OpenOffice Impress</comment>
+ <comment xml:lang="he">מצגת של OpenOffice Impress</comment>
+ <comment xml:lang="hr">OpenOffice Impress prezentacija</comment>
+ <comment xml:lang="hu">OpenOffice Impress bemutató</comment>
+ <comment xml:lang="ia">Presentation OpenOffice Impress</comment>
+ <comment xml:lang="id">Presentasi OpenOffice Impress</comment>
+ <comment xml:lang="it">Presentazione OpenOffice Impress</comment>
+ <comment xml:lang="ja">OpenOffice Impress プレゼンテーション</comment>
+ <comment xml:lang="ka">OpenOffice Impress-ის პრეზენტაცია</comment>
+ <comment xml:lang="kk">OpenOffice Impress презентациясы</comment>
+ <comment xml:lang="ko">OpenOffice Impress 프레젠테이션</comment>
+ <comment xml:lang="lt">OpenOffice Impress pateiktis</comment>
+ <comment xml:lang="lv">OpenOffice Impress prezentācija</comment>
+ <comment xml:lang="nb">OpenOffice Impress-presentasjon</comment>
+ <comment xml:lang="nl">OpenOffice.org Impress-presentatie</comment>
+ <comment xml:lang="nn">OpenOffice Impress-presentasjon</comment>
+ <comment xml:lang="oc">presentacion OpenOffice Impress</comment>
+ <comment xml:lang="pl">Prezentacja OpenOffice.org Impress</comment>
+ <comment xml:lang="pt">apresentação OpenOffice Impress</comment>
+ <comment xml:lang="pt_BR">Apresentação do OpenOffice Impress</comment>
+ <comment xml:lang="ro">Prezentare OpenOffice Impress</comment>
+ <comment xml:lang="ru">Презентация OpenOffice Impress</comment>
+ <comment xml:lang="sk">Prezentácia OpenOffice Impress</comment>
+ <comment xml:lang="sl">Predstavitev OpenOffice.org Impress</comment>
+ <comment xml:lang="sq">Prezantim OpenOffice Impress</comment>
+ <comment xml:lang="sr">Презентација Опен Офис Импреса</comment>
+ <comment xml:lang="sv">OpenOffice Impress-presentation</comment>
+ <comment xml:lang="tr">OpenOffice Impress sunumu</comment>
+ <comment xml:lang="uk">презентація OpenOffice Impress</comment>
+ <comment xml:lang="vi">Trình diễn Impress của OpenOffice.org</comment>
+ <comment xml:lang="zh_CN">OpenOffice Impress 演示文稿</comment>
+ <comment xml:lang="zh_TW">OpenOffice Impress 簡報</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-presentation"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.sun.xml.impress" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.sxi"/>
+ </mime-type>
+ <mime-type type="application/vnd.sun.xml.impress.template">
+ <comment>OpenOffice Impress template</comment>
+ <comment xml:lang="ar">قالب Impress المكتب المفتوح</comment>
+ <comment xml:lang="be@latin">Šablon OpenOffice Impress</comment>
+ <comment xml:lang="bg">Шаблон за презентации — OpenOffice Impress</comment>
+ <comment xml:lang="ca">plantilla d'OpenOffice Impress</comment>
+ <comment xml:lang="cs">šablona OpenOffice Impress</comment>
+ <comment xml:lang="da">OpenOffice Impress-skabelon</comment>
+ <comment xml:lang="de">OpenOffice-Impress-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο OpenOffice Impress</comment>
+ <comment xml:lang="en_GB">OpenOffice Impress template</comment>
+ <comment xml:lang="es">plantilla de OpenOffice Impress</comment>
+ <comment xml:lang="eu">OpenOffice Impress txantiloia</comment>
+ <comment xml:lang="fi">OpenOffice Impress -malli</comment>
+ <comment xml:lang="fo">OpenOffice Impress formur</comment>
+ <comment xml:lang="fr">modèle OpenOffice Impress</comment>
+ <comment xml:lang="ga">teimpléad OpenOffice Impress</comment>
+ <comment xml:lang="gl">modelo de OpenOffice Impress</comment>
+ <comment xml:lang="he">תבנית של OpenOffice Impress</comment>
+ <comment xml:lang="hr">Predložak OpenOffice Impressa</comment>
+ <comment xml:lang="hu">OpenOffice Impress sablon</comment>
+ <comment xml:lang="ia">Patrono OpenOffice Impress</comment>
+ <comment xml:lang="id">Templat OpenOffice Impress</comment>
+ <comment xml:lang="it">Modello OpenOffice Impress</comment>
+ <comment xml:lang="ja">OpenOffice Impress テンプレート</comment>
+ <comment xml:lang="ka">OpenOffice Impress-ის შაბლონი</comment>
+ <comment xml:lang="kk">OpenOffice Impress үлгісі</comment>
+ <comment xml:lang="ko">OpenOffice Impress 프레젠테이션 문서 서식</comment>
+ <comment xml:lang="lt">OpenOffice Impress šablonas</comment>
+ <comment xml:lang="lv">OpenOffice Impress veidne</comment>
+ <comment xml:lang="nb">OpenOffice Impress-mal</comment>
+ <comment xml:lang="nl">OpenOffice.org Impress-sjabloon</comment>
+ <comment xml:lang="nn">OpenOffice Impress-mal</comment>
+ <comment xml:lang="oc">modèl OpenOffice Impress</comment>
+ <comment xml:lang="pl">Szablon prezentacji OpenOffice.org Impress</comment>
+ <comment xml:lang="pt">modelo OpenOffice Impress</comment>
+ <comment xml:lang="pt_BR">Modelo do OpenOffice Impress</comment>
+ <comment xml:lang="ro">Șablon OpenOffice Impress</comment>
+ <comment xml:lang="ru">Шаблон OpenOffice Impress</comment>
+ <comment xml:lang="sk">Šablóna OpenOffice Impress</comment>
+ <comment xml:lang="sl">Predloga OpenOffice.org Impress</comment>
+ <comment xml:lang="sq">Model OpenOffice Impress</comment>
+ <comment xml:lang="sr">Шаблон Опен Офис Импреса</comment>
+ <comment xml:lang="sv">OpenOffice Impress-mall</comment>
+ <comment xml:lang="tr">OpenOffice Impress şablonu</comment>
+ <comment xml:lang="uk">шаблон презентації OpenOffice Impress</comment>
+ <comment xml:lang="vi">Mẫu trình diễn Impress của OpenOffice.org</comment>
+ <comment xml:lang="zh_CN">OpenOffice Impress 模板</comment>
+ <comment xml:lang="zh_TW">OpenOffice Impress 範本</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-presentation"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.sun.xml.impress" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.sti"/>
+ </mime-type>
+ <mime-type type="application/vnd.sun.xml.math">
+ <comment>OpenOffice Math formula</comment>
+ <comment xml:lang="ar">صيغة Math المكتب المفتوح</comment>
+ <comment xml:lang="be@latin">Formuła OpenOffice Math</comment>
+ <comment xml:lang="bg">Формула — OpenOffice Math</comment>
+ <comment xml:lang="ca">fórmula d'OpenOffice Math</comment>
+ <comment xml:lang="cs">vzorec OpenOffice Math</comment>
+ <comment xml:lang="da">OpenOffice Math-formel</comment>
+ <comment xml:lang="de">OpenOffice-Math-Formel</comment>
+ <comment xml:lang="el">Μαθηματικός τύπος OpenOffice Math</comment>
+ <comment xml:lang="en_GB">OpenOffice Math formula</comment>
+ <comment xml:lang="es">fórmula de OpenOffice Math</comment>
+ <comment xml:lang="eu">OpenOffice.org Math formula</comment>
+ <comment xml:lang="fi">OpenOffice Math -kaava</comment>
+ <comment xml:lang="fo">OpenOffice Math frymil</comment>
+ <comment xml:lang="fr">formule OpenOffice Math</comment>
+ <comment xml:lang="ga">foirmle OpenOffice Math</comment>
+ <comment xml:lang="gl">fórmula de OpenOffice Math</comment>
+ <comment xml:lang="he">נוסחה של OpenOffice Math</comment>
+ <comment xml:lang="hr">OpenOffice Math formula</comment>
+ <comment xml:lang="hu">OpenOffice Math képlet</comment>
+ <comment xml:lang="ia">Formula OpenOffice Math</comment>
+ <comment xml:lang="id">Formula OpenOffice Math</comment>
+ <comment xml:lang="it">Formula OpenOffice Math</comment>
+ <comment xml:lang="ja">OpenOffice Math 計算式</comment>
+ <comment xml:lang="ka">OpenOffice Math-ის ფორმულა</comment>
+ <comment xml:lang="kk">OpenOffice Math формуласы</comment>
+ <comment xml:lang="ko">OpenOffice Math 수식</comment>
+ <comment xml:lang="lt">OpenOffice Math formulė</comment>
+ <comment xml:lang="lv">OpenOffice Math formula</comment>
+ <comment xml:lang="nb">OpenOffice Math-formel</comment>
+ <comment xml:lang="nl">OpenOffice.org Math-formule</comment>
+ <comment xml:lang="nn">OpenOffice Math-formel</comment>
+ <comment xml:lang="oc">formula OpenOffice Math</comment>
+ <comment xml:lang="pl">Formuła OpenOffice.org Math</comment>
+ <comment xml:lang="pt">fórmula OpenOffice Math</comment>
+ <comment xml:lang="pt_BR">Fórmula do OpenOffice Math</comment>
+ <comment xml:lang="ro">Formulă OpenOffice Math</comment>
+ <comment xml:lang="ru">Формула OpenOffice Math</comment>
+ <comment xml:lang="sk">Vzorec OpenOffice Math</comment>
+ <comment xml:lang="sl">Dokument formule OpenOffice.org Math</comment>
+ <comment xml:lang="sq">Formulë OpenOffice Math</comment>
+ <comment xml:lang="sr">Формула Опен Офис Математике</comment>
+ <comment xml:lang="sv">OpenOffice Math-formel</comment>
+ <comment xml:lang="tr">OpenOffice Math formülü</comment>
+ <comment xml:lang="uk">формула OpenOffice Math</comment>
+ <comment xml:lang="vi">Công thức Math của OpenOffice.org</comment>
+ <comment xml:lang="zh_CN">OpenOffice Math 公式</comment>
+ <comment xml:lang="zh_TW">OpenOffice Math 公式</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.sun.xml.math" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.sxm"/>
+ </mime-type>
+ <mime-type type="application/vnd.sun.xml.writer">
+ <comment>OpenOffice Writer document</comment>
+ <comment xml:lang="ar">مستند Writer المكتب المفتوح</comment>
+ <comment xml:lang="ast">Documentu d'OpenOffice Writer</comment>
+ <comment xml:lang="az">OpenOffice Writer sənədi</comment>
+ <comment xml:lang="be@latin">Dakument OpenOffice Writer</comment>
+ <comment xml:lang="bg">Документ — OpenOffice Writer</comment>
+ <comment xml:lang="ca">document d'OpenOffice Writer</comment>
+ <comment xml:lang="cs">dokument OpenOffice Writer</comment>
+ <comment xml:lang="cy">Dogfen OpenOffice (Writer)</comment>
+ <comment xml:lang="da">OpenOffice Writer-dokument</comment>
+ <comment xml:lang="de">OpenOffice-Writer-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο OpenOffice Writer</comment>
+ <comment xml:lang="en_GB">OpenOffice Writer document</comment>
+ <comment xml:lang="es">documento de OpenOffice Writer</comment>
+ <comment xml:lang="eu">OpenOffice.org Writer dokumentua</comment>
+ <comment xml:lang="fi">OpenOffice Writer -asiakirja</comment>
+ <comment xml:lang="fo">OpenOffice Writer skjal</comment>
+ <comment xml:lang="fr">document OpenOffice Writer</comment>
+ <comment xml:lang="ga">cáipéis OpenOffice Writer</comment>
+ <comment xml:lang="gl">documento de OpenOffice Writer</comment>
+ <comment xml:lang="he">מסמך של OpenOffice Writer</comment>
+ <comment xml:lang="hr">OpenOffice Writer dokument</comment>
+ <comment xml:lang="hu">OpenOffice Writer dokumentum</comment>
+ <comment xml:lang="ia">Documento OpenOffice Writer</comment>
+ <comment xml:lang="id">Dokumen OpenOffice Writer</comment>
+ <comment xml:lang="it">Documento OpenOffice Writer</comment>
+ <comment xml:lang="ja">OpenOffice Writer ドキュメント</comment>
+ <comment xml:lang="ka">OpenOffice Writer-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">OpenOffice Writer құжаты</comment>
+ <comment xml:lang="ko">OpenOffice Writer 문서</comment>
+ <comment xml:lang="lt">OpenOffice Writer dokumentas</comment>
+ <comment xml:lang="lv">OpenOffice Writer dokuments</comment>
+ <comment xml:lang="nb">OpenOffice Writer-dokument</comment>
+ <comment xml:lang="nl">OpenOffice.org Writer-document</comment>
+ <comment xml:lang="nn">OpenOffice Writer-dokument</comment>
+ <comment xml:lang="oc">document OpenOffice Writer</comment>
+ <comment xml:lang="pl">Dokument OpenOffice.org Writer</comment>
+ <comment xml:lang="pt">documento OpenOffice Writer</comment>
+ <comment xml:lang="pt_BR">Documento do OpenOffice Writer</comment>
+ <comment xml:lang="ro">Document OpenOffice Writer</comment>
+ <comment xml:lang="ru">Документ OpenOffice Writer</comment>
+ <comment xml:lang="sk">Dokument OpenOffice Writer</comment>
+ <comment xml:lang="sl">Dokument OpenOffice.org Writer</comment>
+ <comment xml:lang="sq">Dokument OpenOffice Writer</comment>
+ <comment xml:lang="sr">Документ Опен Офис Писца</comment>
+ <comment xml:lang="sv">OpenOffice Writer-dokument</comment>
+ <comment xml:lang="tr">OpenOffice Writer belgesi</comment>
+ <comment xml:lang="uk">документ OpenOffice Writer</comment>
+ <comment xml:lang="vi">Tài liệu Writer của OpenOffice.org</comment>
+ <comment xml:lang="zh_CN">OpenOffice Writer 文档</comment>
+ <comment xml:lang="zh_TW">OpenOffice Writer 文件</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.sun.xml.writer" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.sxw"/>
+ </mime-type>
+ <mime-type type="application/vnd.sun.xml.writer.global">
+ <comment>OpenOffice Writer global document</comment>
+ <comment xml:lang="ar">مستند المكتب المفتوح Writer العالمي</comment>
+ <comment xml:lang="ast">Documentu global d'OpenOffice Writer</comment>
+ <comment xml:lang="az">OpenOffice Writer qlobal sənədi</comment>
+ <comment xml:lang="be@latin">Hlabalny dakument OpenOffice Writer</comment>
+ <comment xml:lang="bg">Документ - глобален — OpenOffice Writer</comment>
+ <comment xml:lang="ca">document global d'OpenOffice Writer</comment>
+ <comment xml:lang="cs">globální dokument OpenOffice Writer</comment>
+ <comment xml:lang="cy">Dogfen eang OpenOffice (Writer)</comment>
+ <comment xml:lang="da">OpenOffice Writer-globalt dokument</comment>
+ <comment xml:lang="de">OpenOffice-Writer-Globaldokument</comment>
+ <comment xml:lang="el">Καθολικό έγγραφο OpenOffice Writer</comment>
+ <comment xml:lang="en_GB">OpenOffice Writer global document</comment>
+ <comment xml:lang="es">documento global de OpenOffice Writer</comment>
+ <comment xml:lang="eu">OpenOffice.org Writer dokumentu globala</comment>
+ <comment xml:lang="fi">OpenOffice Writer - yleinen asiakirja</comment>
+ <comment xml:lang="fo">OpenOffice Writer heiltøkt skjal</comment>
+ <comment xml:lang="fr">document global OpenOffice Writer</comment>
+ <comment xml:lang="ga">cáipéis chomhchoiteann OpenOffice Writer</comment>
+ <comment xml:lang="gl">documento global de OpenOffice Writer</comment>
+ <comment xml:lang="he">מסמך גלובלי של OpenOffice Writer</comment>
+ <comment xml:lang="hr">OpenOffice Writer globalni dokument</comment>
+ <comment xml:lang="hu">OpenOffice Writer globális dokumentum</comment>
+ <comment xml:lang="ia">Documento global OpenOffice Writer</comment>
+ <comment xml:lang="id">Dokumen global OpenOffice Writer</comment>
+ <comment xml:lang="it">Documento globale OpenOffice Writer</comment>
+ <comment xml:lang="ja">OpenOffice Writer グローバルドキュメント</comment>
+ <comment xml:lang="ka">OpenOffice Writer-ის გლობალური დოკუმენტი</comment>
+ <comment xml:lang="kk">OpenOffice Writer негізгі құжаты</comment>
+ <comment xml:lang="ko">OpenOffice Writer 글로벌 문서</comment>
+ <comment xml:lang="lt">OpenOffice Writer bendrinis dokumentas</comment>
+ <comment xml:lang="lv">OpenOffice Writer globālais dokuments</comment>
+ <comment xml:lang="nb">Global OpenOffice Writer globalt dokument</comment>
+ <comment xml:lang="nl">OpenOffice.org Writer-globaal-document</comment>
+ <comment xml:lang="nn">OpenOffice Writer globalt dokument</comment>
+ <comment xml:lang="oc">document global OpenOffice Writer</comment>
+ <comment xml:lang="pl">Globalny dokument OpenOffice.org Writer</comment>
+ <comment xml:lang="pt">documento global OpenOffice Writer</comment>
+ <comment xml:lang="pt_BR">Documento global do OpenOffice Writer</comment>
+ <comment xml:lang="ro">Document global OpenOffice Writer</comment>
+ <comment xml:lang="ru">Основной документ OpenOffice Writer</comment>
+ <comment xml:lang="sk">Globálny dokument OpenOffice Writer</comment>
+ <comment xml:lang="sl">Splošni dokument OpenOffice.org Writer</comment>
+ <comment xml:lang="sq">Dokument i përgjithshëm OpenOffice Writer</comment>
+ <comment xml:lang="sr">Општи документ Опен Офис Писца</comment>
+ <comment xml:lang="sv">OpenOffice Writer-globaldokument</comment>
+ <comment xml:lang="tr">OpenOffice Writer global belgesi</comment>
+ <comment xml:lang="uk">загальний документ OpenOffice Writer</comment>
+ <comment xml:lang="vi">Tài liệu toàn cục Writer của OpenOffice.org</comment>
+ <comment xml:lang="zh_CN">OpenOffice Writer 全局文档</comment>
+ <comment xml:lang="zh_TW">OpenOffice Writer 主控文件</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.sun.xml.writer" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.sxg"/>
+ </mime-type>
+ <mime-type type="application/vnd.sun.xml.writer.template">
+ <comment>OpenOffice Writer template</comment>
+ <comment xml:lang="ar">قالب Writer المكتب المفتوح</comment>
+ <comment xml:lang="az">OpenOffice Writer şablonu</comment>
+ <comment xml:lang="be@latin">Šablon OpenOffice Writer</comment>
+ <comment xml:lang="bg">Шаблон за документи — OpenOffice Writer</comment>
+ <comment xml:lang="ca">plantilla d'OpenOffice Writer</comment>
+ <comment xml:lang="cs">šablona OpenOffice Writer</comment>
+ <comment xml:lang="cy">Templed OpenOffice (Writer)</comment>
+ <comment xml:lang="da">OpenOffice Writer-skabelon</comment>
+ <comment xml:lang="de">OpenOffice-Writer-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο OpenOffice Writer</comment>
+ <comment xml:lang="en_GB">OpenOffice Writer template</comment>
+ <comment xml:lang="es">plantilla de OpenOffice Writer</comment>
+ <comment xml:lang="eu">OpenOffice Writer txantiloia</comment>
+ <comment xml:lang="fi">OpenOffice Writer -malli</comment>
+ <comment xml:lang="fo">OpenOffice Writer formur</comment>
+ <comment xml:lang="fr">modèle OpenOffice Writer</comment>
+ <comment xml:lang="ga">teimpléad OpenOffice Writer</comment>
+ <comment xml:lang="gl">modelo de OpenOffice Writer</comment>
+ <comment xml:lang="he">תסנית של OpenOffice Writer</comment>
+ <comment xml:lang="hr">OpenOffice Writer predložak</comment>
+ <comment xml:lang="hu">OpenOffice Writer sablon</comment>
+ <comment xml:lang="ia">Patrono OpenOffice Writer</comment>
+ <comment xml:lang="id">Templat OpenOffice Writer</comment>
+ <comment xml:lang="it">Modello OpenOffice Writer</comment>
+ <comment xml:lang="ja">OpenOffice Writer ドキュメントテンプレート</comment>
+ <comment xml:lang="ka">OpenOffice Writer-ის შაბლონი</comment>
+ <comment xml:lang="kk">OpenOffice Writer үлгісі</comment>
+ <comment xml:lang="ko">OpenOffice Writer 문서 서식</comment>
+ <comment xml:lang="lt">OpenOffice Writer šablonas</comment>
+ <comment xml:lang="lv">OpenOffice Writer veidne</comment>
+ <comment xml:lang="ms">Templat OpenOffice Writer</comment>
+ <comment xml:lang="nb">OpenOffice Writer-mal</comment>
+ <comment xml:lang="nl">OpenOffice.org Writer-sjabloon</comment>
+ <comment xml:lang="nn">OpenOffice Writer-mal</comment>
+ <comment xml:lang="oc">modèl OpenOffice Writer</comment>
+ <comment xml:lang="pl">Szablon dokumentu OpenOffice.org Writer</comment>
+ <comment xml:lang="pt">modelo OpenOffice Writer</comment>
+ <comment xml:lang="pt_BR">Modelo do OpenOffice Writer</comment>
+ <comment xml:lang="ro">Șablon OpenOffice Writer</comment>
+ <comment xml:lang="ru">Шаблон OpenOffice Writer</comment>
+ <comment xml:lang="sk">Šablóna OpenOffice Writer</comment>
+ <comment xml:lang="sl">Predloga OpenOffice.org Writer</comment>
+ <comment xml:lang="sq">Model OpenOffice Writer</comment>
+ <comment xml:lang="sr">Шаблон Опен Офис Писца</comment>
+ <comment xml:lang="sv">OpenOffice Writer-mall</comment>
+ <comment xml:lang="tr">OpenOffice Writer şablonu</comment>
+ <comment xml:lang="uk">шаблон документа OpenOffice Writer</comment>
+ <comment xml:lang="vi">Mẫu tài liệu Writer của OpenOffice.org</comment>
+ <comment xml:lang="zh_CN">OpenOffice Writer 模板</comment>
+ <comment xml:lang="zh_TW">OpenOffice Writer 範本</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.sun.xml.writer" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.stw"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.text">
+ <comment>ODT document</comment>
+ <comment xml:lang="ar">مستند ODT</comment>
+ <comment xml:lang="ast">Documentu ODT</comment>
+ <comment xml:lang="be@latin">Dakument ODT</comment>
+ <comment xml:lang="bg">Документ — ODT</comment>
+ <comment xml:lang="ca">document ODT</comment>
+ <comment xml:lang="cs">dokument ODT</comment>
+ <comment xml:lang="da">ODT-dokument</comment>
+ <comment xml:lang="de">ODT-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο ODT</comment>
+ <comment xml:lang="en_GB">ODT document</comment>
+ <comment xml:lang="eo">ODT-dokumento</comment>
+ <comment xml:lang="es">documento ODT</comment>
+ <comment xml:lang="eu">ODT dokumentua</comment>
+ <comment xml:lang="fi">ODT-asiakirja</comment>
+ <comment xml:lang="fo">ODT skjal</comment>
+ <comment xml:lang="fr">document ODT</comment>
+ <comment xml:lang="ga">cáipéis ODT</comment>
+ <comment xml:lang="gl">documento ODT</comment>
+ <comment xml:lang="he">מסמך ODT</comment>
+ <comment xml:lang="hr">ODT dokument</comment>
+ <comment xml:lang="hu">ODT-dokumentum</comment>
+ <comment xml:lang="ia">Documento ODT</comment>
+ <comment xml:lang="id">Dokumen ODT</comment>
+ <comment xml:lang="it">Documento ODT</comment>
+ <comment xml:lang="ja">ODT ドキュメント</comment>
+ <comment xml:lang="ka">ODT დოკუმენტი</comment>
+ <comment xml:lang="kk">ODT құжаты</comment>
+ <comment xml:lang="ko">ODT 문서</comment>
+ <comment xml:lang="lt">ODT dokumentas</comment>
+ <comment xml:lang="lv">ODT dokuments</comment>
+ <comment xml:lang="nb">ODT-dokument</comment>
+ <comment xml:lang="nl">ODT-document</comment>
+ <comment xml:lang="nn">ODT-dokument</comment>
+ <comment xml:lang="oc">document ODT</comment>
+ <comment xml:lang="pl">Dokument ODT</comment>
+ <comment xml:lang="pt">documento ODT</comment>
+ <comment xml:lang="pt_BR">Documento ODT</comment>
+ <comment xml:lang="ro">Document ODT</comment>
+ <comment xml:lang="ru">Документ ODT</comment>
+ <comment xml:lang="sk">Dokument ODT</comment>
+ <comment xml:lang="sl">Dokument ODT</comment>
+ <comment xml:lang="sq">Dokument ODT</comment>
+ <comment xml:lang="sr">ОДТ документ</comment>
+ <comment xml:lang="sv">ODT-dokument</comment>
+ <comment xml:lang="tr">ODT belgesi</comment>
+ <comment xml:lang="uk">документ ODT</comment>
+ <comment xml:lang="vi">Tài liệu ODT</comment>
+ <comment xml:lang="zh_CN">ODT 文档</comment>
+ <comment xml:lang="zh_TW">ODT 文件</comment>
+ <acronym>ODT</acronym>
+ <expanded-acronym>OpenDocument Text</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.text" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.odt"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.text-flat-xml">
+ <comment>ODT document (Flat XML)</comment>
+ <comment xml:lang="ar">مستند ODT (Flat XML)</comment>
+ <comment xml:lang="ast">Documentu ODT (XML planu)</comment>
+ <comment xml:lang="bg">Документ — ODT (само XML)</comment>
+ <comment xml:lang="ca">document ODT (XML pla)</comment>
+ <comment xml:lang="cs">dokument ODT (Flat XML)</comment>
+ <comment xml:lang="da">ODT-dokument (flad XML)</comment>
+ <comment xml:lang="de">ODT-Dokument (Unkomprimiertes XML)</comment>
+ <comment xml:lang="el">Έγγραφο ODT (Flat XML)</comment>
+ <comment xml:lang="en_GB">ODT document (Flat XML)</comment>
+ <comment xml:lang="es">documento ODT (XML plano)</comment>
+ <comment xml:lang="eu">ODT dokumentua (XML soila)</comment>
+ <comment xml:lang="fi">ODT-asiakirja (Flat XML)</comment>
+ <comment xml:lang="fo">ODT skjal (Flat XML)</comment>
+ <comment xml:lang="fr">document ODT (XML plat)</comment>
+ <comment xml:lang="ga">cáipéis ODT (XML cothrom)</comment>
+ <comment xml:lang="gl">documento ODT (XML plano)</comment>
+ <comment xml:lang="he">מסמך ODT‏ (Flat XML)</comment>
+ <comment xml:lang="hr">ODT dokument (Flat XML)</comment>
+ <comment xml:lang="hu">ODT-dokumentum (egyszerű XML)</comment>
+ <comment xml:lang="ia">Documento ODT (XML platte)</comment>
+ <comment xml:lang="id">Dokumen ODT (Flat XML)</comment>
+ <comment xml:lang="it">Documento ODT (XML semplice)</comment>
+ <comment xml:lang="ja">ODT ドキュメント (Flat XML)</comment>
+ <comment xml:lang="ka">ODT დოკუმენტი (Flat XML)</comment>
+ <comment xml:lang="kk">ODT құжаты (Тек XML)</comment>
+ <comment xml:lang="ko">ODT 문서(단일 XML)</comment>
+ <comment xml:lang="lt">ODT dokumentas (Flat XML)</comment>
+ <comment xml:lang="lv">ODT dokuments (plakans XML)</comment>
+ <comment xml:lang="nl">ODT document (Flat XML)</comment>
+ <comment xml:lang="oc">document ODT (XML plat)</comment>
+ <comment xml:lang="pl">Dokument ODT (prosty XML)</comment>
+ <comment xml:lang="pt">documento ODT (XML plano)</comment>
+ <comment xml:lang="pt_BR">Documento ODT (Flat XML)</comment>
+ <comment xml:lang="ro">Document ODT (XML simplu)</comment>
+ <comment xml:lang="ru">Документ ODT (простой XML)</comment>
+ <comment xml:lang="sk">Dokument ODT (čisté XML)</comment>
+ <comment xml:lang="sl">Datoteka dokumenta ODT (nepovezan XML)</comment>
+ <comment xml:lang="sr">ОДТ документ (Обични ИксМЛ)</comment>
+ <comment xml:lang="sv">ODT-dokument (platt XML)</comment>
+ <comment xml:lang="tr">ODT belgesi (Düz XML)</comment>
+ <comment xml:lang="uk">документ ODT (Flat XML)</comment>
+ <comment xml:lang="zh_CN">ODT 文档(Flat XML)</comment>
+ <comment xml:lang="zh_TW">ODT 文件 (Flat XML)</comment>
+ <acronym>FODT</acronym>
+ <expanded-acronym>OpenDocument Text (Flat XML)</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.fodt"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.text-template">
+ <comment>ODT template</comment>
+ <comment xml:lang="ar">قالب ODT</comment>
+ <comment xml:lang="be@latin">Šablon ODT</comment>
+ <comment xml:lang="bg">Шаблон за документи — ODT</comment>
+ <comment xml:lang="ca">plantilla ODT</comment>
+ <comment xml:lang="cs">šablona ODT</comment>
+ <comment xml:lang="da">ODT-skabelon</comment>
+ <comment xml:lang="de">ODT-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο ODT</comment>
+ <comment xml:lang="en_GB">ODT template</comment>
+ <comment xml:lang="eo">ODT-ŝablono</comment>
+ <comment xml:lang="es">plantilla ODT</comment>
+ <comment xml:lang="eu">ODT txantiloia</comment>
+ <comment xml:lang="fi">ODT-malli</comment>
+ <comment xml:lang="fo">ODT formur</comment>
+ <comment xml:lang="fr">modèle ODT</comment>
+ <comment xml:lang="ga">teimpléad ODT</comment>
+ <comment xml:lang="gl">modelo ODT</comment>
+ <comment xml:lang="he">תבנית ODT</comment>
+ <comment xml:lang="hr">ODT predložak</comment>
+ <comment xml:lang="hu">ODT-sablon</comment>
+ <comment xml:lang="ia">Patrono ODT</comment>
+ <comment xml:lang="id">Templat ODT</comment>
+ <comment xml:lang="it">Modello ODT</comment>
+ <comment xml:lang="ja">ODT テンプレート</comment>
+ <comment xml:lang="ka">ODT დოკუმენტი</comment>
+ <comment xml:lang="kk">ODT үлгісі</comment>
+ <comment xml:lang="ko">ODT 문서 서식</comment>
+ <comment xml:lang="lt">ODT šablonas</comment>
+ <comment xml:lang="lv">ODT veidne</comment>
+ <comment xml:lang="nb">ODT-mal</comment>
+ <comment xml:lang="nl">ODT-sjabloon</comment>
+ <comment xml:lang="nn">ODT-mal</comment>
+ <comment xml:lang="oc">modèl ODT</comment>
+ <comment xml:lang="pl">Szablon ODT</comment>
+ <comment xml:lang="pt">modelo ODT</comment>
+ <comment xml:lang="pt_BR">Modelo ODT</comment>
+ <comment xml:lang="ro">Șablon ODT</comment>
+ <comment xml:lang="ru">Шаблон ODT</comment>
+ <comment xml:lang="sk">Šablóna ODT</comment>
+ <comment xml:lang="sl">Predloga dokumenta ODT</comment>
+ <comment xml:lang="sq">Model ODT</comment>
+ <comment xml:lang="sr">ОДТ шаблон</comment>
+ <comment xml:lang="sv">ODT-mall</comment>
+ <comment xml:lang="tr">ODT şablonu</comment>
+ <comment xml:lang="uk">шаблон ODT</comment>
+ <comment xml:lang="vi">Mẫu ODT</comment>
+ <comment xml:lang="zh_CN">ODT 模板</comment>
+ <comment xml:lang="zh_TW">ODT 範本</comment>
+ <acronym>ODT</acronym>
+ <expanded-acronym>OpenDocument Text</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.text-template" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.ott"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.text-web">
+ <comment>OTH template</comment>
+ <comment xml:lang="ar">قالب OTH</comment>
+ <comment xml:lang="be@latin">Šablon OTH</comment>
+ <comment xml:lang="bg">Шаблон за страници — OTH</comment>
+ <comment xml:lang="ca">plantilla OTH</comment>
+ <comment xml:lang="cs">šablona OTH</comment>
+ <comment xml:lang="da">OTH-skabelon</comment>
+ <comment xml:lang="de">OTH-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο OTH</comment>
+ <comment xml:lang="en_GB">OTH template</comment>
+ <comment xml:lang="eo">OTH-ŝablono</comment>
+ <comment xml:lang="es">plantilla OTH</comment>
+ <comment xml:lang="eu">OTH txantiloia</comment>
+ <comment xml:lang="fi">OTH-malli</comment>
+ <comment xml:lang="fo">OTH formur</comment>
+ <comment xml:lang="fr">modèle OTH</comment>
+ <comment xml:lang="ga">teimpléad OTH</comment>
+ <comment xml:lang="gl">modelo OTH</comment>
+ <comment xml:lang="he">תבנית OTH</comment>
+ <comment xml:lang="hr">OTH predložak</comment>
+ <comment xml:lang="hu">OTH-sablon</comment>
+ <comment xml:lang="ia">Patrono OTH</comment>
+ <comment xml:lang="id">Templat OTH</comment>
+ <comment xml:lang="it">Modello OTH</comment>
+ <comment xml:lang="ja">OTH テンプレート</comment>
+ <comment xml:lang="ka">OTH შაბლონი</comment>
+ <comment xml:lang="kk">OTH үлгісі</comment>
+ <comment xml:lang="ko">OTH 문서 서식</comment>
+ <comment xml:lang="lt">OTH šablonas</comment>
+ <comment xml:lang="lv">OTH veidne</comment>
+ <comment xml:lang="nb">OTH-mal</comment>
+ <comment xml:lang="nl">OTH-sjabloon</comment>
+ <comment xml:lang="nn">OTH-mal</comment>
+ <comment xml:lang="oc">modèl OTH</comment>
+ <comment xml:lang="pl">Szablon OTH</comment>
+ <comment xml:lang="pt">modelo OTH</comment>
+ <comment xml:lang="pt_BR">Modelo OTH</comment>
+ <comment xml:lang="ro">Șablon OTH</comment>
+ <comment xml:lang="ru">Шаблон OTH</comment>
+ <comment xml:lang="sk">Šablóna OTH</comment>
+ <comment xml:lang="sl">Predloga OTH</comment>
+ <comment xml:lang="sq">Model OTH</comment>
+ <comment xml:lang="sr">ОТХ шаблон</comment>
+ <comment xml:lang="sv">OTH-mall</comment>
+ <comment xml:lang="tr">OTH şablonu</comment>
+ <comment xml:lang="uk">шаблон OTH</comment>
+ <comment xml:lang="vi">Mẫu ODH</comment>
+ <comment xml:lang="zh_CN">OTH 模板</comment>
+ <comment xml:lang="zh_TW">OTH 範本</comment>
+ <acronym>OTH</acronym>
+ <expanded-acronym>OpenDocument HTML</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="text-html"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.text-web" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.oth"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.text-master">
+ <comment>ODM document</comment>
+ <comment xml:lang="ar">مستند ODM</comment>
+ <comment xml:lang="ast">Documentu ODM</comment>
+ <comment xml:lang="be@latin">Dakument ODM</comment>
+ <comment xml:lang="bg">Документ — ODM</comment>
+ <comment xml:lang="ca">document ODM</comment>
+ <comment xml:lang="cs">dokument ODM</comment>
+ <comment xml:lang="da">ODM-dokument</comment>
+ <comment xml:lang="de">ODM-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο ODM</comment>
+ <comment xml:lang="en_GB">ODM document</comment>
+ <comment xml:lang="eo">ODM-dokumento</comment>
+ <comment xml:lang="es">documento ODM</comment>
+ <comment xml:lang="eu">ODM dokumentua</comment>
+ <comment xml:lang="fi">ODM-asiakirja</comment>
+ <comment xml:lang="fo">ODM skjal</comment>
+ <comment xml:lang="fr">document ODM</comment>
+ <comment xml:lang="ga">cáipéis ODM</comment>
+ <comment xml:lang="gl">documento ODM</comment>
+ <comment xml:lang="he">מסמך ODM</comment>
+ <comment xml:lang="hr">ODM dokument</comment>
+ <comment xml:lang="hu">ODM-dokumentum</comment>
+ <comment xml:lang="ia">Documento ODM</comment>
+ <comment xml:lang="id">Dokumen ODM</comment>
+ <comment xml:lang="it">Documento ODM</comment>
+ <comment xml:lang="ja">ODM ドキュメント</comment>
+ <comment xml:lang="ka">ODM დოკუმენტი</comment>
+ <comment xml:lang="kk">ODM құжаты</comment>
+ <comment xml:lang="ko">ODM 문서</comment>
+ <comment xml:lang="lt">ODM dokumentas</comment>
+ <comment xml:lang="lv">ODM dokuments</comment>
+ <comment xml:lang="nb">ODM-dokument</comment>
+ <comment xml:lang="nl">ODM-document</comment>
+ <comment xml:lang="nn">ODM-dokument</comment>
+ <comment xml:lang="oc">document ODM</comment>
+ <comment xml:lang="pl">Dokument ODM</comment>
+ <comment xml:lang="pt">documento ODM</comment>
+ <comment xml:lang="pt_BR">Documento ODM</comment>
+ <comment xml:lang="ro">Document ODM</comment>
+ <comment xml:lang="ru">Документ ODM</comment>
+ <comment xml:lang="sk">Dokument ODM</comment>
+ <comment xml:lang="sl">Dokument ODM</comment>
+ <comment xml:lang="sq">Dokument ODM</comment>
+ <comment xml:lang="sr">ОДМ документ</comment>
+ <comment xml:lang="sv">ODM-dokument</comment>
+ <comment xml:lang="tr">ODM belgesi</comment>
+ <comment xml:lang="uk">документ ODM</comment>
+ <comment xml:lang="vi">Tài liệu ODM</comment>
+ <comment xml:lang="zh_CN">ODM 文档</comment>
+ <comment xml:lang="zh_TW">ODM 文件</comment>
+ <acronym>ODM</acronym>
+ <expanded-acronym>OpenDocument Master</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.text-master" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.odm"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.graphics">
+ <comment>ODG drawing</comment>
+ <comment xml:lang="ar">تصميم ODG</comment>
+ <comment xml:lang="be@latin">Rysunak ODG</comment>
+ <comment xml:lang="bg">Чертеж — ODG</comment>
+ <comment xml:lang="ca">dibuix ODG</comment>
+ <comment xml:lang="cs">kresba ODG</comment>
+ <comment xml:lang="da">ODG-tegning</comment>
+ <comment xml:lang="de">ODG-Zeichnung</comment>
+ <comment xml:lang="el">Σχέδιο ODG</comment>
+ <comment xml:lang="en_GB">ODG drawing</comment>
+ <comment xml:lang="eo">ODG-desegnaĵo</comment>
+ <comment xml:lang="es">dibujo ODG</comment>
+ <comment xml:lang="eu">ODG marrazkia</comment>
+ <comment xml:lang="fi">ODG-piirros</comment>
+ <comment xml:lang="fo">ODG tekning</comment>
+ <comment xml:lang="fr">dessin ODG</comment>
+ <comment xml:lang="ga">líníocht ODG</comment>
+ <comment xml:lang="gl">debuxo ODG</comment>
+ <comment xml:lang="he">ציור ODG</comment>
+ <comment xml:lang="hr">ODG crtež</comment>
+ <comment xml:lang="hu">ODG-rajz</comment>
+ <comment xml:lang="ia">Designo ODG</comment>
+ <comment xml:lang="id">Gambar ODG</comment>
+ <comment xml:lang="it">Disegno ODG</comment>
+ <comment xml:lang="ja">ODG ドロー</comment>
+ <comment xml:lang="ka">ODG-ის ნახაზი</comment>
+ <comment xml:lang="kk">ODG суреті</comment>
+ <comment xml:lang="ko">ODG 드로잉</comment>
+ <comment xml:lang="lt">ODG piešinys</comment>
+ <comment xml:lang="lv">ODG zīmējums</comment>
+ <comment xml:lang="nb">ODG-tegning</comment>
+ <comment xml:lang="nl">ODG-tekening</comment>
+ <comment xml:lang="nn">ODG-teikning</comment>
+ <comment xml:lang="oc">dessenh ODG</comment>
+ <comment xml:lang="pl">Rysunek ODG</comment>
+ <comment xml:lang="pt">desenho ODG</comment>
+ <comment xml:lang="pt_BR">Desenho ODG</comment>
+ <comment xml:lang="ro">Desen ODG</comment>
+ <comment xml:lang="ru">Рисунок ODG</comment>
+ <comment xml:lang="sk">Kresba ODG</comment>
+ <comment xml:lang="sl">Datoteka risbe ODG</comment>
+ <comment xml:lang="sq">Vizatim ODG</comment>
+ <comment xml:lang="sr">ОДГ цртеж</comment>
+ <comment xml:lang="sv">ODG-teckning</comment>
+ <comment xml:lang="tr">ODG çizimi</comment>
+ <comment xml:lang="uk">малюнок ODG</comment>
+ <comment xml:lang="vi">Bản vẽ ODG</comment>
+ <comment xml:lang="zh_CN">ODG 绘图</comment>
+ <comment xml:lang="zh_TW">ODG 繪圖</comment>
+ <acronym>ODG</acronym>
+ <expanded-acronym>OpenDocument Drawing</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="image-x-generic"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.graphics" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.odg"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.graphics-flat-xml">
+ <comment>ODG drawing (Flat XML)</comment>
+ <comment xml:lang="ar">رسمة ODG (Flat XML)</comment>
+ <comment xml:lang="bg">Чертеж — ODG (само XML)</comment>
+ <comment xml:lang="ca">dibuix ODG (XML pla) </comment>
+ <comment xml:lang="cs">kresba ODG (Flat XML)</comment>
+ <comment xml:lang="da">ODG-tegning (flad XML)</comment>
+ <comment xml:lang="de">ODG-Zeichnung (Unkomprimiertes XML)</comment>
+ <comment xml:lang="el">Σχέδιο ODG (Flat XML)</comment>
+ <comment xml:lang="en_GB">ODG drawing (Flat XML)</comment>
+ <comment xml:lang="es">dibujo ODG (XML plano)</comment>
+ <comment xml:lang="eu">ODG marrazkia (XML soila)</comment>
+ <comment xml:lang="fi">ODG-piirros (Flat XML)</comment>
+ <comment xml:lang="fo">ODG tekning (Flat XML)</comment>
+ <comment xml:lang="fr">dessin ODG (XML plat)</comment>
+ <comment xml:lang="ga">líníocht ODG (XML cothrom)</comment>
+ <comment xml:lang="gl">debuxo ODB (XML plano)</comment>
+ <comment xml:lang="he">ציור ODG (Flat XML(</comment>
+ <comment xml:lang="hr">ODG crtež (Flat XML)</comment>
+ <comment xml:lang="hu">ODG-rajz (egyszerű XML)</comment>
+ <comment xml:lang="ia">Designo ODG (XML platte)</comment>
+ <comment xml:lang="id">Gambar ODG (FLAT XML)</comment>
+ <comment xml:lang="it">Disegno ODG (XML semplice)</comment>
+ <comment xml:lang="ja">ODG ドロー (Flat XML)</comment>
+ <comment xml:lang="ka">ODG-ის ნახაზი (Flat XML)</comment>
+ <comment xml:lang="kk">ODG сызбасы (Тек XML)</comment>
+ <comment xml:lang="ko">ODG 드로잉(단일 XML)</comment>
+ <comment xml:lang="lt">ODG piešinys (Flat XML)</comment>
+ <comment xml:lang="lv">ODG zīmējums (plakans XML)</comment>
+ <comment xml:lang="nl">ODG-tekening (Flat XML)</comment>
+ <comment xml:lang="oc">dessenh ODG (XML plat)</comment>
+ <comment xml:lang="pl">Rysunek ODG (prosty XML)</comment>
+ <comment xml:lang="pt">desenho ODG (XML plano)</comment>
+ <comment xml:lang="pt_BR">Desenho ODG (Flat XML)</comment>
+ <comment xml:lang="ro">Desen ODG (XML simplu)</comment>
+ <comment xml:lang="ru">Рисунок ODG (простой XML)</comment>
+ <comment xml:lang="sk">Kresba ODG (čisté XML)</comment>
+ <comment xml:lang="sl">Datoteka risbe ODG (nepovezan XML)</comment>
+ <comment xml:lang="sr">ОДГ цртеж (Обичан ИксМЛ)</comment>
+ <comment xml:lang="sv">ODG-teckning (platt XML)</comment>
+ <comment xml:lang="tr">ODG çizimi (Düz XML)</comment>
+ <comment xml:lang="uk">малюнок ODG (Flat XML)</comment>
+ <comment xml:lang="zh_CN">ODG 绘图(Flat XML)</comment>
+ <comment xml:lang="zh_TW">ODG 繪圖 (Flat XML)</comment>
+ <acronym>FODG</acronym>
+ <expanded-acronym>OpenDocument Drawing (Flat XML)</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.fodg"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.graphics-template">
+ <comment>ODG template</comment>
+ <comment xml:lang="ar">قالب ODG</comment>
+ <comment xml:lang="be@latin">Šablon ODG</comment>
+ <comment xml:lang="bg">Шаблон за чертежи — ODG</comment>
+ <comment xml:lang="ca">plantilla ODG</comment>
+ <comment xml:lang="cs">šablona ODG</comment>
+ <comment xml:lang="da">ODG-skabelon</comment>
+ <comment xml:lang="de">ODG-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο ODG</comment>
+ <comment xml:lang="en_GB">ODG template</comment>
+ <comment xml:lang="eo">ODG-ŝablono</comment>
+ <comment xml:lang="es">plantilla ODG</comment>
+ <comment xml:lang="eu">ODG txantiloia</comment>
+ <comment xml:lang="fi">ODG-malli</comment>
+ <comment xml:lang="fo">ODG formur</comment>
+ <comment xml:lang="fr">modèle ODG</comment>
+ <comment xml:lang="ga">teimpléad ODG</comment>
+ <comment xml:lang="gl">modelo ODG</comment>
+ <comment xml:lang="he">תבנית ODG</comment>
+ <comment xml:lang="hr">ODG predložak</comment>
+ <comment xml:lang="hu">ODG-sablon</comment>
+ <comment xml:lang="ia">Patrono ODG</comment>
+ <comment xml:lang="id">Templat ODG</comment>
+ <comment xml:lang="it">Modello ODG</comment>
+ <comment xml:lang="ja">ODG テンプレート</comment>
+ <comment xml:lang="ka">ODG-ის შაბლონი</comment>
+ <comment xml:lang="kk">ODG үлгісі</comment>
+ <comment xml:lang="ko">ODG 문서 서식</comment>
+ <comment xml:lang="lt">ODG šablonas</comment>
+ <comment xml:lang="lv">ODG veidne</comment>
+ <comment xml:lang="nb">ODG-mal</comment>
+ <comment xml:lang="nl">ODG-sjabloon</comment>
+ <comment xml:lang="nn">ODG-mal</comment>
+ <comment xml:lang="oc">modèl ODG</comment>
+ <comment xml:lang="pl">Szablon ODG</comment>
+ <comment xml:lang="pt">modelo ODG</comment>
+ <comment xml:lang="pt_BR">Modelo ODG</comment>
+ <comment xml:lang="ro">Șablon ODG</comment>
+ <comment xml:lang="ru">Шаблон ODG</comment>
+ <comment xml:lang="sk">Šablóna ODG</comment>
+ <comment xml:lang="sl">Predloga dokumenta ODG</comment>
+ <comment xml:lang="sq">Model ODG</comment>
+ <comment xml:lang="sr">ОДГ шаблон</comment>
+ <comment xml:lang="sv">ODG-mall</comment>
+ <comment xml:lang="tr">ODG şablonu</comment>
+ <comment xml:lang="uk">шаблон ODG</comment>
+ <comment xml:lang="vi">Mẫu ODG</comment>
+ <comment xml:lang="zh_CN">ODG 模板</comment>
+ <comment xml:lang="zh_TW">ODG 範本</comment>
+ <acronym>ODG</acronym>
+ <expanded-acronym>OpenDocument Drawing</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="image-x-generic"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.graphics-template" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.otg"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.presentation">
+ <comment>ODP presentation</comment>
+ <comment xml:lang="ar">عرض تقديمي ODP</comment>
+ <comment xml:lang="be@latin">Prezentacyja ODP</comment>
+ <comment xml:lang="bg">Презентация — ODP</comment>
+ <comment xml:lang="ca">presentació ODP</comment>
+ <comment xml:lang="cs">prezentace ODP</comment>
+ <comment xml:lang="da">ODP-præsentation</comment>
+ <comment xml:lang="de">ODP-Präsentation</comment>
+ <comment xml:lang="el">Παρουσίαση ODP</comment>
+ <comment xml:lang="en_GB">ODP presentation</comment>
+ <comment xml:lang="eo">ODP-prezentaĵo</comment>
+ <comment xml:lang="es">presentación ODP</comment>
+ <comment xml:lang="eu">ODP aurkezpena</comment>
+ <comment xml:lang="fi">ODP-esitys</comment>
+ <comment xml:lang="fo">ODP framløga</comment>
+ <comment xml:lang="fr">présentation ODP</comment>
+ <comment xml:lang="ga">láithreoireacht ODP</comment>
+ <comment xml:lang="gl">presentación ODP</comment>
+ <comment xml:lang="he">מצגת ODP</comment>
+ <comment xml:lang="hr">ODP prezentacija</comment>
+ <comment xml:lang="hu">ODP-prezentáció</comment>
+ <comment xml:lang="ia">Presentation ODP</comment>
+ <comment xml:lang="id">Presentasi ODP</comment>
+ <comment xml:lang="it">Presentazione ODP</comment>
+ <comment xml:lang="ja">ODP プレゼンテーション</comment>
+ <comment xml:lang="ka">ODP პრეზენტაცია</comment>
+ <comment xml:lang="kk">ODP презентациясы</comment>
+ <comment xml:lang="ko">ODP 프레젠테이션</comment>
+ <comment xml:lang="lt">ODP pateiktis</comment>
+ <comment xml:lang="lv">ODP prezentācija</comment>
+ <comment xml:lang="nb">ODP-presentasjon</comment>
+ <comment xml:lang="nl">ODP-presentatie</comment>
+ <comment xml:lang="nn">ODP-presentasjon</comment>
+ <comment xml:lang="oc">presentacion ODP</comment>
+ <comment xml:lang="pl">Prezentacja ODP</comment>
+ <comment xml:lang="pt">apresentação ODP</comment>
+ <comment xml:lang="pt_BR">Apresentação ODP</comment>
+ <comment xml:lang="ro">Prezentare ODP</comment>
+ <comment xml:lang="ru">Презентация ODP</comment>
+ <comment xml:lang="sk">Prezentácia ODP</comment>
+ <comment xml:lang="sl">Predstavitev ODP</comment>
+ <comment xml:lang="sq">Prezantim ODP</comment>
+ <comment xml:lang="sr">ОДП презентација</comment>
+ <comment xml:lang="sv">ODP-presentation</comment>
+ <comment xml:lang="tr">ODP sunumu</comment>
+ <comment xml:lang="uk">презентація ODP</comment>
+ <comment xml:lang="vi">Trình diễn ODM</comment>
+ <comment xml:lang="zh_CN">ODP 演示文稿</comment>
+ <comment xml:lang="zh_TW">ODP 簡報</comment>
+ <acronym>ODP</acronym>
+ <expanded-acronym>OpenDocument Presentation</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-presentation"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.presentation" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.odp"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.presentation-flat-xml">
+ <comment>ODP presentation (Flat XML)</comment>
+ <comment xml:lang="ar">عرض ODP (Flat XML)</comment>
+ <comment xml:lang="bg">Презентация — ODP (само XML)</comment>
+ <comment xml:lang="ca">presentació ODP (XML pla)</comment>
+ <comment xml:lang="cs">prezentace ODP (Flat XML)</comment>
+ <comment xml:lang="da">ODP-præsentation (flad XML)</comment>
+ <comment xml:lang="de">ODP-Präsentation (Unkomprimiertes XML)</comment>
+ <comment xml:lang="el">Παρουσίαση ODP (Flat XML)</comment>
+ <comment xml:lang="en_GB">ODP presentation (Flat XML)</comment>
+ <comment xml:lang="es">presentación ODP (XML plano)</comment>
+ <comment xml:lang="eu">ODP aurkezpena (XML soila)</comment>
+ <comment xml:lang="fi">ODP-esitys (Flat XML)</comment>
+ <comment xml:lang="fo">ODP framløga (Flat XML)</comment>
+ <comment xml:lang="fr">présentation ODP (XML plat)</comment>
+ <comment xml:lang="ga">láithreoireacht ODP (XML cothrom)</comment>
+ <comment xml:lang="gl">presentación ODP (XML plano)</comment>
+ <comment xml:lang="he">מצגת ODP‏ (Flat XML)</comment>
+ <comment xml:lang="hr">ODP prezentacija (Flat XML)</comment>
+ <comment xml:lang="hu">ODP-prezentáció (egyszerű XML)</comment>
+ <comment xml:lang="ia">Presentation ODP (XML platte)</comment>
+ <comment xml:lang="id">Presentasi ODP (Flat XML)</comment>
+ <comment xml:lang="it">Presentazione ODP (XML semplice)</comment>
+ <comment xml:lang="ja">ODP プレゼンテーション (Flat XML)</comment>
+ <comment xml:lang="ka">ODP პრეზენტაცია (Flat XML)</comment>
+ <comment xml:lang="kk">ODP презентациясы (Тек XML)</comment>
+ <comment xml:lang="ko">ODP 프레젠테이션(단일 XML)</comment>
+ <comment xml:lang="lt">ODP pateiktis (Flat XML)</comment>
+ <comment xml:lang="lv">ODP prezentācija (plakans XML)</comment>
+ <comment xml:lang="nl">ODP presentatie (Flat XML)</comment>
+ <comment xml:lang="oc">presentacion ODP (XML plat)</comment>
+ <comment xml:lang="pl">Prezentacja ODP (prosty XML)</comment>
+ <comment xml:lang="pt">apresentação ODP (XML plano)</comment>
+ <comment xml:lang="pt_BR">Apresentação ODP (Flat XML)</comment>
+ <comment xml:lang="ro">Prezentare ODP (XML simplu)</comment>
+ <comment xml:lang="ru">Презентация ODP (простой XML)</comment>
+ <comment xml:lang="sk">Prezentácia ODP (čisté XML)</comment>
+ <comment xml:lang="sl">Predstavitev ODP (nepovezan XML)</comment>
+ <comment xml:lang="sr">ОДП презентација (Обични ИксМЛ)</comment>
+ <comment xml:lang="sv">ODP-presentation (platt XML)</comment>
+ <comment xml:lang="tr">ODP sunumu (Düz XML)</comment>
+ <comment xml:lang="uk">презентація ODP (Flat XML)</comment>
+ <comment xml:lang="zh_CN">ODP 演示文稿(Flat XML)</comment>
+ <comment xml:lang="zh_TW">ODP 範本 (Flat XML)</comment>
+ <acronym>FODP</acronym>
+ <expanded-acronym>OpenDocument Presentation (Flat XML)</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="x-office-presentation"/>
+ <glob pattern="*.fodp"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.presentation-template">
+ <comment>ODP template</comment>
+ <comment xml:lang="ar">قالب ODP</comment>
+ <comment xml:lang="be@latin">Šablon ODP</comment>
+ <comment xml:lang="bg">Шаблон за презентации — ODP</comment>
+ <comment xml:lang="ca">plantilla ODP</comment>
+ <comment xml:lang="cs">šablona ODP</comment>
+ <comment xml:lang="da">ODP-skabelon</comment>
+ <comment xml:lang="de">ODP-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο ODP</comment>
+ <comment xml:lang="en_GB">ODP template</comment>
+ <comment xml:lang="eo">ODP-ŝablono</comment>
+ <comment xml:lang="es">plantilla ODP</comment>
+ <comment xml:lang="eu">ODP txantiloia</comment>
+ <comment xml:lang="fi">ODP-malli</comment>
+ <comment xml:lang="fo">ODP formur</comment>
+ <comment xml:lang="fr">modèle ODP</comment>
+ <comment xml:lang="ga">teimpléad ODP</comment>
+ <comment xml:lang="gl">modelo ODP</comment>
+ <comment xml:lang="he">תבנית ODP</comment>
+ <comment xml:lang="hr">ODP predložak</comment>
+ <comment xml:lang="hu">ODP-sablon</comment>
+ <comment xml:lang="ia">Patrono ODP</comment>
+ <comment xml:lang="id">Templat ODP</comment>
+ <comment xml:lang="it">Modello ODP</comment>
+ <comment xml:lang="ja">ODP テンプレート</comment>
+ <comment xml:lang="ka">ODP შაბლონი</comment>
+ <comment xml:lang="kk">ODP үлгісі</comment>
+ <comment xml:lang="ko">ODP 문서 서식</comment>
+ <comment xml:lang="lt">ODP šablonas</comment>
+ <comment xml:lang="lv">ODP veidne</comment>
+ <comment xml:lang="nb">ODP-mal</comment>
+ <comment xml:lang="nl">ODP-sjabloon</comment>
+ <comment xml:lang="nn">ODP-mal</comment>
+ <comment xml:lang="oc">modèl ODP</comment>
+ <comment xml:lang="pl">Szablon ODP</comment>
+ <comment xml:lang="pt">modelo ODP</comment>
+ <comment xml:lang="pt_BR">Modelo ODP</comment>
+ <comment xml:lang="ro">Șablon ODP</comment>
+ <comment xml:lang="ru">Шаблон ODP</comment>
+ <comment xml:lang="sk">Šablóna ODP</comment>
+ <comment xml:lang="sl">Predloga dokumenta ODP</comment>
+ <comment xml:lang="sq">Model ODP</comment>
+ <comment xml:lang="sr">ОДП шаблон</comment>
+ <comment xml:lang="sv">ODP-mall</comment>
+ <comment xml:lang="tr">ODP şablonu</comment>
+ <comment xml:lang="uk">шаблон ODP</comment>
+ <comment xml:lang="vi">Mẫu ODP</comment>
+ <comment xml:lang="zh_CN">ODP 模板</comment>
+ <comment xml:lang="zh_TW">ODP 範本</comment>
+ <acronym>ODP</acronym>
+ <expanded-acronym>OpenDocument Presentation</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-presentation"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.presentation-template" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.otp"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.spreadsheet">
+ <comment>ODS spreadsheet</comment>
+ <comment xml:lang="ar">جدول ODS</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš ODS</comment>
+ <comment xml:lang="bg">Таблица — ODS</comment>
+ <comment xml:lang="ca">full de càlcul ODS</comment>
+ <comment xml:lang="cs">sešit ODS</comment>
+ <comment xml:lang="da">ODS-regneark</comment>
+ <comment xml:lang="de">ODS-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο ODS</comment>
+ <comment xml:lang="en_GB">ODS spreadsheet</comment>
+ <comment xml:lang="eo">ODS-kalkultabelo</comment>
+ <comment xml:lang="es">hoja de cálculo ODS</comment>
+ <comment xml:lang="eu">ODS kalkulu-orria</comment>
+ <comment xml:lang="fi">ODS-taulukko</comment>
+ <comment xml:lang="fo">ODS rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul ODS</comment>
+ <comment xml:lang="ga">scarbhileog ODS</comment>
+ <comment xml:lang="gl">folla de cálculo ODS</comment>
+ <comment xml:lang="he">גליון נתונים ODS</comment>
+ <comment xml:lang="hr">ODS proračunska tablica</comment>
+ <comment xml:lang="hu">ODS-táblázat</comment>
+ <comment xml:lang="ia">Folio de calculo ODS</comment>
+ <comment xml:lang="id">Lembar sebar ODS</comment>
+ <comment xml:lang="it">Foglio di calcolo ODS</comment>
+ <comment xml:lang="ja">ODS スプレッドシート</comment>
+ <comment xml:lang="ka">ODS ცხრილი</comment>
+ <comment xml:lang="kk">ODS электрондық кестесі</comment>
+ <comment xml:lang="ko">ODS 스프레드시트</comment>
+ <comment xml:lang="lt">ODS skaičialentė</comment>
+ <comment xml:lang="lv">ODS izklājlapa</comment>
+ <comment xml:lang="nb">ODS-regneark</comment>
+ <comment xml:lang="nl">ODS-rekenblad</comment>
+ <comment xml:lang="nn">ODS-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul ODS</comment>
+ <comment xml:lang="pl">Arkusz ODS</comment>
+ <comment xml:lang="pt">folha de cálculo ODS</comment>
+ <comment xml:lang="pt_BR">Planilha ODS</comment>
+ <comment xml:lang="ro">Foaie de calcul ODS</comment>
+ <comment xml:lang="ru">Электронная таблица ODS</comment>
+ <comment xml:lang="sk">Zošit ODS</comment>
+ <comment xml:lang="sl">Preglednica ODS</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh ODS</comment>
+ <comment xml:lang="sr">ОДС табела</comment>
+ <comment xml:lang="sv">ODS-kalkylblad</comment>
+ <comment xml:lang="tr">ODS çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця ODS</comment>
+ <comment xml:lang="vi">Bảng tính ODS</comment>
+ <comment xml:lang="zh_CN">ODS 电子表格</comment>
+ <comment xml:lang="zh_TW">ODS 試算表</comment>
+ <acronym>ODS</acronym>
+ <expanded-acronym>OpenDocument Spreadsheet</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.spreadsheet" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.ods"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.spreadsheet-flat-xml">
+ <comment>ODS spreadsheet (Flat XML)</comment>
+ <comment xml:lang="ar">جدول ODS (Flat XML)</comment>
+ <comment xml:lang="bg">Таблица — ODS (само XML)</comment>
+ <comment xml:lang="ca">full de càlcul ODS (XML pla)</comment>
+ <comment xml:lang="cs">sešit ODS (Flat XML)</comment>
+ <comment xml:lang="da">ODS-regneark (flad XML)</comment>
+ <comment xml:lang="de">ODS-Tabelle (Unkomprimiertes XML)</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο ODS (Flat XML)</comment>
+ <comment xml:lang="en_GB">ODS spreadsheet (Flat XML)</comment>
+ <comment xml:lang="es">hoja de cálculo ODS (XML plano)</comment>
+ <comment xml:lang="eu">ODS kalkulu-orria (XML soila)</comment>
+ <comment xml:lang="fi">ODS-laskentataulukko (Flat XML)</comment>
+ <comment xml:lang="fo">ODS rokniark (Flat XML)</comment>
+ <comment xml:lang="fr">feuille de calcul ODS (XML plat)</comment>
+ <comment xml:lang="ga">scarbhileog ODS (XML cothrom)</comment>
+ <comment xml:lang="gl">folla de cálculo ODS (XML plano)</comment>
+ <comment xml:lang="he">גליון נתונים ODS‏ (XML פשוט)</comment>
+ <comment xml:lang="hr">ODS proračunska tablica (Flat XML)</comment>
+ <comment xml:lang="hu">ODS-táblázat (egyszerű XML)</comment>
+ <comment xml:lang="ia">Folio de calculo ODS (XML platte)</comment>
+ <comment xml:lang="id">Lembar sebar ODS (Flat XML)</comment>
+ <comment xml:lang="it">Foglio di calcolo ODS (XML semplice)</comment>
+ <comment xml:lang="ja">ODS スプレッドシート (Flat XML)</comment>
+ <comment xml:lang="ka">ODS ცხრილი (Flat XML)</comment>
+ <comment xml:lang="kk">ODS электрондық кестесі (Тек XML)</comment>
+ <comment xml:lang="ko">ODS 스프레드시트(단일 XML)</comment>
+ <comment xml:lang="lt">ODS skaičialentė (Flat XML)</comment>
+ <comment xml:lang="lv">ODS izklājlapa (plakans XML)</comment>
+ <comment xml:lang="nl">ODS spreadsheet (Flat XML)</comment>
+ <comment xml:lang="oc">fuèlh de calcul ODS (XML plat)</comment>
+ <comment xml:lang="pl">Arkusz ODS (prosty XML)</comment>
+ <comment xml:lang="pt">folha de cálculo ODS (XML plano)</comment>
+ <comment xml:lang="pt_BR">Planilha ODS (Flat XML)</comment>
+ <comment xml:lang="ro">Foaie de calcul ODS (XML simplu)</comment>
+ <comment xml:lang="ru">Электронная таблица ODS (простой XML)</comment>
+ <comment xml:lang="sk">Zošit ODS (čisté XML)</comment>
+ <comment xml:lang="sl">Preglednica ODS (nepovezan XML)</comment>
+ <comment xml:lang="sr">ОДС табела (обични ИксМЛ)</comment>
+ <comment xml:lang="sv">ODS-kalkylblad (platt XML)</comment>
+ <comment xml:lang="tr">ODS sunumu (Düz XML)</comment>
+ <comment xml:lang="uk">ел. таблиця ODS (Flat XML)</comment>
+ <comment xml:lang="zh_CN">ODS 电子表格 (Flat XML)</comment>
+ <comment xml:lang="zh_TW">ODS 試算表 (Flat XML)</comment>
+ <acronym>FODS</acronym>
+ <expanded-acronym>OpenDocument Spreadsheet (Flat XML)</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.fods"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.spreadsheet-template">
+ <comment>ODS template</comment>
+ <comment xml:lang="ar">قالب ODS</comment>
+ <comment xml:lang="be@latin">Šablon ODS</comment>
+ <comment xml:lang="bg">Шаблон за таблици — ODS</comment>
+ <comment xml:lang="ca">plantilla ODS</comment>
+ <comment xml:lang="cs">šablona ODS</comment>
+ <comment xml:lang="da">ODS-skabelon</comment>
+ <comment xml:lang="de">ODS-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο ODS</comment>
+ <comment xml:lang="en_GB">ODS template</comment>
+ <comment xml:lang="eo">ODS-ŝablono</comment>
+ <comment xml:lang="es">plantilla ODS</comment>
+ <comment xml:lang="eu">ODS txantiloia</comment>
+ <comment xml:lang="fi">ODS-malli</comment>
+ <comment xml:lang="fo">ODS formur</comment>
+ <comment xml:lang="fr">modèle ODS</comment>
+ <comment xml:lang="ga">teimpléad ODS</comment>
+ <comment xml:lang="gl">modelo ODS</comment>
+ <comment xml:lang="he">תבנית ODS</comment>
+ <comment xml:lang="hr">ODS predložak</comment>
+ <comment xml:lang="hu">ODS-sablon</comment>
+ <comment xml:lang="ia">Patrono ODS</comment>
+ <comment xml:lang="id">Templat ODS</comment>
+ <comment xml:lang="it">Modello ODS</comment>
+ <comment xml:lang="ja">ODS テンプレート</comment>
+ <comment xml:lang="ka">ODS-ის შაბლონი</comment>
+ <comment xml:lang="kk">ODS үлгісі</comment>
+ <comment xml:lang="ko">ODS 문서 서식</comment>
+ <comment xml:lang="lt">ODS šablonas</comment>
+ <comment xml:lang="lv">ODS veidne</comment>
+ <comment xml:lang="nb">ODS-mal</comment>
+ <comment xml:lang="nl">ODS-sjabloon</comment>
+ <comment xml:lang="nn">ODS-mal</comment>
+ <comment xml:lang="oc">modèl ODS</comment>
+ <comment xml:lang="pl">Szablon ODS</comment>
+ <comment xml:lang="pt">modelo ODS</comment>
+ <comment xml:lang="pt_BR">Modelo ODS</comment>
+ <comment xml:lang="ro">Șablon ODS</comment>
+ <comment xml:lang="ru">Шаблон ODS</comment>
+ <comment xml:lang="sk">Šablóna ODS</comment>
+ <comment xml:lang="sl">Predloga dokumenta ODS</comment>
+ <comment xml:lang="sq">Model ODS</comment>
+ <comment xml:lang="sr">ОДС шаблон</comment>
+ <comment xml:lang="sv">ODS-mall</comment>
+ <comment xml:lang="tr">ODS şablonu</comment>
+ <comment xml:lang="uk">шаблон ODS</comment>
+ <comment xml:lang="vi">Mẫu ODS</comment>
+ <comment xml:lang="zh_CN">ODS 模板</comment>
+ <comment xml:lang="zh_TW">ODS 範本</comment>
+ <acronym>ODS</acronym>
+ <expanded-acronym>OpenDocument Spreadsheet</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.spreadsheet-template" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.ots"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.chart">
+ <comment>ODC chart</comment>
+ <comment xml:lang="ar">مخطط ODC</comment>
+ <comment xml:lang="be@latin">Dyjahrama ODC</comment>
+ <comment xml:lang="bg">Диаграма — ODC</comment>
+ <comment xml:lang="ca">diagrama ODC</comment>
+ <comment xml:lang="cs">graf ODC</comment>
+ <comment xml:lang="da">ODC-diagram</comment>
+ <comment xml:lang="de">ODC-Diagramm</comment>
+ <comment xml:lang="el">Διάγραμμα ODC</comment>
+ <comment xml:lang="en_GB">ODC chart</comment>
+ <comment xml:lang="eo">ODC-diagramo</comment>
+ <comment xml:lang="es">gráfico ODC</comment>
+ <comment xml:lang="eu">ODC diagrama</comment>
+ <comment xml:lang="fi">ODC-kaavio</comment>
+ <comment xml:lang="fo">ODC strikumynd</comment>
+ <comment xml:lang="fr">graphique ODC</comment>
+ <comment xml:lang="ga">cairt ODC</comment>
+ <comment xml:lang="gl">gráfica ODC</comment>
+ <comment xml:lang="he">תו ODC</comment>
+ <comment xml:lang="hr">ODC grafikon</comment>
+ <comment xml:lang="hu">ODC-táblázat</comment>
+ <comment xml:lang="ia">Graphico ODC</comment>
+ <comment xml:lang="id">Bagan ODC</comment>
+ <comment xml:lang="it">Grafico ODC</comment>
+ <comment xml:lang="ja">ODC チャート</comment>
+ <comment xml:lang="kk">ODC диаграммасы</comment>
+ <comment xml:lang="ko">ODC 차트</comment>
+ <comment xml:lang="lt">ODC diagrama</comment>
+ <comment xml:lang="lv">ODC diagramma</comment>
+ <comment xml:lang="nb">ODC-graf</comment>
+ <comment xml:lang="nl">ODC-grafiek</comment>
+ <comment xml:lang="nn">ODC-diagram</comment>
+ <comment xml:lang="oc">grafic ODC</comment>
+ <comment xml:lang="pl">Wykres ODC</comment>
+ <comment xml:lang="pt">gráfico ODC</comment>
+ <comment xml:lang="pt_BR">Gráfico ODC</comment>
+ <comment xml:lang="ro">Diagramă ODC</comment>
+ <comment xml:lang="ru">Диаграмма ODC</comment>
+ <comment xml:lang="sk">Graf ODC</comment>
+ <comment xml:lang="sl">Datoteka grafikona ODC</comment>
+ <comment xml:lang="sq">Grafik ODC</comment>
+ <comment xml:lang="sr">ОДЦ график</comment>
+ <comment xml:lang="sv">ODC-diagram</comment>
+ <comment xml:lang="tr">ODC çizelgesi</comment>
+ <comment xml:lang="uk">діаграма ODC</comment>
+ <comment xml:lang="vi">Sơ đồ ODC</comment>
+ <comment xml:lang="zh_CN">ODC 图表</comment>
+ <comment xml:lang="zh_TW">ODC 圖表</comment>
+ <acronym>ODC</acronym>
+ <expanded-acronym>OpenDocument Chart</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.chart" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.odc"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.chart-template">
+ <comment>ODC template</comment>
+ <comment xml:lang="ar">قالب ODC</comment>
+ <comment xml:lang="bg">Шаблон за диаграми — ODC</comment>
+ <comment xml:lang="ca">plantilla ODC</comment>
+ <comment xml:lang="cs">šablona ODC</comment>
+ <comment xml:lang="da">ODC-skabelon</comment>
+ <comment xml:lang="de">ODC-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο ODC</comment>
+ <comment xml:lang="en_GB">ODC template</comment>
+ <comment xml:lang="eo">ODC-ŝablono</comment>
+ <comment xml:lang="es">plantilla ODC</comment>
+ <comment xml:lang="eu">ODC txantiloia</comment>
+ <comment xml:lang="fi">ODC-malli</comment>
+ <comment xml:lang="fo">ODC formur</comment>
+ <comment xml:lang="fr">modèle ODC</comment>
+ <comment xml:lang="ga">teimpléad ODC</comment>
+ <comment xml:lang="gl">modelo ODC</comment>
+ <comment xml:lang="he">תבנית ODC</comment>
+ <comment xml:lang="hr">ODC predložak</comment>
+ <comment xml:lang="hu">ODC-sablon</comment>
+ <comment xml:lang="ia">Patrono ODC</comment>
+ <comment xml:lang="id">Templat ODC</comment>
+ <comment xml:lang="it">Modello ODC</comment>
+ <comment xml:lang="ja">ODC テンプレート</comment>
+ <comment xml:lang="ka">ODC შაბლონი</comment>
+ <comment xml:lang="kk">ODC үлгісі</comment>
+ <comment xml:lang="ko">ODC 문서 서식</comment>
+ <comment xml:lang="lt">ODC šablonas</comment>
+ <comment xml:lang="lv">ODC veidne</comment>
+ <comment xml:lang="nl">ODC-sjabloon</comment>
+ <comment xml:lang="oc">modèl ODC</comment>
+ <comment xml:lang="pl">Szablon ODC</comment>
+ <comment xml:lang="pt">modelo ODC</comment>
+ <comment xml:lang="pt_BR">Modelo ODC</comment>
+ <comment xml:lang="ro">Șablon ODC</comment>
+ <comment xml:lang="ru">Шаблон ODC</comment>
+ <comment xml:lang="sk">Šablóna ODC</comment>
+ <comment xml:lang="sl">Predloga ODC</comment>
+ <comment xml:lang="sr">ОДЦ шаблон</comment>
+ <comment xml:lang="sv">ODC-mall</comment>
+ <comment xml:lang="tr">ODC şablonu</comment>
+ <comment xml:lang="uk">шаблон ODC</comment>
+ <comment xml:lang="vi">Mẫu ODC</comment>
+ <comment xml:lang="zh_CN">ODC 模板</comment>
+ <comment xml:lang="zh_TW">ODC 範本</comment>
+ <acronym>ODC</acronym>
+ <expanded-acronym>OpenDocument Chart</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.chart-template" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.otc"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.formula">
+ <comment>ODF formula</comment>
+ <comment xml:lang="ar">صيغة ODF</comment>
+ <comment xml:lang="be@latin">Formuła ODF</comment>
+ <comment xml:lang="bg">Формула — ODF</comment>
+ <comment xml:lang="ca">fórmula ODF</comment>
+ <comment xml:lang="cs">vzorec ODF</comment>
+ <comment xml:lang="da">ODF-formel</comment>
+ <comment xml:lang="de">ODF-Formel</comment>
+ <comment xml:lang="el">Μαθηματικός τύπος ODF</comment>
+ <comment xml:lang="en_GB">ODF formula</comment>
+ <comment xml:lang="eo">ODF-formulo</comment>
+ <comment xml:lang="es">fórmula ODF</comment>
+ <comment xml:lang="eu">ODF formula</comment>
+ <comment xml:lang="fi">ODF-kaava</comment>
+ <comment xml:lang="fo">ODF frymil</comment>
+ <comment xml:lang="fr">formule ODF</comment>
+ <comment xml:lang="ga">foirmle ODF</comment>
+ <comment xml:lang="gl">Fórula ODF</comment>
+ <comment xml:lang="he">נוסחת ODF</comment>
+ <comment xml:lang="hr">ODF formula</comment>
+ <comment xml:lang="hu">ODF-képlet</comment>
+ <comment xml:lang="ia">Formula ODF</comment>
+ <comment xml:lang="id">Formula ODF</comment>
+ <comment xml:lang="it">Formula ODF</comment>
+ <comment xml:lang="ja">ODF 計算式</comment>
+ <comment xml:lang="ka">ODF-ის ფორმულა</comment>
+ <comment xml:lang="kk">ODF формуласы</comment>
+ <comment xml:lang="ko">ODF 수식</comment>
+ <comment xml:lang="lt">ODF formulė</comment>
+ <comment xml:lang="lv">ODF formula</comment>
+ <comment xml:lang="nb">ODF-formel</comment>
+ <comment xml:lang="nl">ODF-formule</comment>
+ <comment xml:lang="nn">ODF-formel</comment>
+ <comment xml:lang="oc">formula ODF</comment>
+ <comment xml:lang="pl">Formuła ODF</comment>
+ <comment xml:lang="pt">fórmula ODF</comment>
+ <comment xml:lang="pt_BR">Fórmula ODF</comment>
+ <comment xml:lang="ro">Formulă ODF</comment>
+ <comment xml:lang="ru">Формула ODF</comment>
+ <comment xml:lang="sk">Vzorec ODF</comment>
+ <comment xml:lang="sl">Dokument formule ODF</comment>
+ <comment xml:lang="sq">Formulë ODF</comment>
+ <comment xml:lang="sr">ОДФ формула</comment>
+ <comment xml:lang="sv">ODF-formel</comment>
+ <comment xml:lang="tr">ODF formülü</comment>
+ <comment xml:lang="uk">формула ODF</comment>
+ <comment xml:lang="vi">Công thức ODF</comment>
+ <comment xml:lang="zh_CN">ODF 公式</comment>
+ <comment xml:lang="zh_TW">ODF 公式</comment>
+ <acronym>ODF</acronym>
+ <expanded-acronym>OpenDocument Formula</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.formula" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.odf"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.formula-template">
+ <comment>ODF template</comment>
+ <comment xml:lang="ar">قالب ODF</comment>
+ <comment xml:lang="bg">Шаблон за формули — ODF</comment>
+ <comment xml:lang="ca">plantilla ODF</comment>
+ <comment xml:lang="cs">šablona ODF</comment>
+ <comment xml:lang="da">ODF-skabelon</comment>
+ <comment xml:lang="de">ODF-Vorlage</comment>
+ <comment xml:lang="el">Πρότυπο ODF</comment>
+ <comment xml:lang="en_GB">ODF template</comment>
+ <comment xml:lang="eo">ODF-ŝablono</comment>
+ <comment xml:lang="es">plantilla ODF</comment>
+ <comment xml:lang="eu">ODF txantiloia</comment>
+ <comment xml:lang="fi">ODF-malli</comment>
+ <comment xml:lang="fo">ODF formur</comment>
+ <comment xml:lang="fr">modèle ODF</comment>
+ <comment xml:lang="ga">teimpléad ODF</comment>
+ <comment xml:lang="gl">modelo ODF</comment>
+ <comment xml:lang="he">תבנית ODF</comment>
+ <comment xml:lang="hr">ODF predložak</comment>
+ <comment xml:lang="hu">ODG-sablon</comment>
+ <comment xml:lang="ia">Patrono ODF</comment>
+ <comment xml:lang="id">Templat ODF</comment>
+ <comment xml:lang="it">Modello ODF</comment>
+ <comment xml:lang="ja">ODF テンプレート</comment>
+ <comment xml:lang="ka">ODF-ის შაბლონი</comment>
+ <comment xml:lang="kk">ODF үлгісі</comment>
+ <comment xml:lang="ko">ODF 문서 서식</comment>
+ <comment xml:lang="lt">ODF šablonas</comment>
+ <comment xml:lang="lv">ODF veidne</comment>
+ <comment xml:lang="nl">ODF-sjabloon</comment>
+ <comment xml:lang="oc">modèl ODF</comment>
+ <comment xml:lang="pl">Szablon ODF</comment>
+ <comment xml:lang="pt">modelo ODF</comment>
+ <comment xml:lang="pt_BR">Modelo ODF</comment>
+ <comment xml:lang="ro">Șablon ODF</comment>
+ <comment xml:lang="ru">Шаблон ODF</comment>
+ <comment xml:lang="sk">Šablóna ODF</comment>
+ <comment xml:lang="sl">Predloga dokumenta ODF</comment>
+ <comment xml:lang="sr">ОДФ шаблон</comment>
+ <comment xml:lang="sv">ODF-mall</comment>
+ <comment xml:lang="tr">ODF şablonu</comment>
+ <comment xml:lang="uk">шаблон ODF</comment>
+ <comment xml:lang="vi">Mẫu ODF</comment>
+ <comment xml:lang="zh_CN">ODF 模板</comment>
+ <comment xml:lang="zh_TW">ODF 範本</comment>
+ <acronym>ODF</acronym>
+ <expanded-acronym>OpenDocument Formula</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.formula-template" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.otf"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.database">
+ <comment>ODB database</comment>
+ <comment xml:lang="ar">قاعدة بيانات ODB</comment>
+ <comment xml:lang="be@latin">Baza źviestak ODB</comment>
+ <comment xml:lang="bg">База от данни — ODB</comment>
+ <comment xml:lang="ca">base de dades ODB</comment>
+ <comment xml:lang="cs">databáze ODB</comment>
+ <comment xml:lang="da">ODB-database</comment>
+ <comment xml:lang="de">ODB-Datenbank</comment>
+ <comment xml:lang="el">Βάση δεδομένων ODB</comment>
+ <comment xml:lang="en_GB">ODB database</comment>
+ <comment xml:lang="eo">ODB-datumbazo</comment>
+ <comment xml:lang="es">base de datos ODB</comment>
+ <comment xml:lang="eu">ODB datu-basea</comment>
+ <comment xml:lang="fi">ODB-tietokanta</comment>
+ <comment xml:lang="fo">ODB dátustovnur</comment>
+ <comment xml:lang="fr">base de données ODB</comment>
+ <comment xml:lang="ga">bunachar sonraí ODB</comment>
+ <comment xml:lang="gl">base de datos ODB</comment>
+ <comment xml:lang="he">מסד נתונים ODB</comment>
+ <comment xml:lang="hr">ODB baza podataka</comment>
+ <comment xml:lang="hu">ODB-adatbázis</comment>
+ <comment xml:lang="ia">Base de datos ODB</comment>
+ <comment xml:lang="id">Basis data ODB</comment>
+ <comment xml:lang="it">Database ODB</comment>
+ <comment xml:lang="ja">ODB データベース</comment>
+ <comment xml:lang="ka">ODB-ის მონაცემთა ბაზა</comment>
+ <comment xml:lang="kk">ODB дерекқоры</comment>
+ <comment xml:lang="ko">ODB 데이터베이스</comment>
+ <comment xml:lang="lt">ODB duomenų bazė</comment>
+ <comment xml:lang="lv">ODB datubāze</comment>
+ <comment xml:lang="nb">ODB-database</comment>
+ <comment xml:lang="nl">ODB-gegevensbank</comment>
+ <comment xml:lang="nn">ODB-database</comment>
+ <comment xml:lang="oc">banca de donadas ODB</comment>
+ <comment xml:lang="pl">Baza danych ODB</comment>
+ <comment xml:lang="pt">base de dados ODB</comment>
+ <comment xml:lang="pt_BR">Banco de dados ODB</comment>
+ <comment xml:lang="ro">Bază de date ODB</comment>
+ <comment xml:lang="ru">База данных ODB</comment>
+ <comment xml:lang="sk">Databáza ODB</comment>
+ <comment xml:lang="sl">Podatkovna zbirka ODB</comment>
+ <comment xml:lang="sq">Bazë me të dhëna ODB</comment>
+ <comment xml:lang="sr">ОДБ база података</comment>
+ <comment xml:lang="sv">ODB-databas</comment>
+ <comment xml:lang="tr">ODB veritabanı</comment>
+ <comment xml:lang="uk">база даних ODB</comment>
+ <comment xml:lang="vi">Cơ sở dữ liệu ODB</comment>
+ <comment xml:lang="zh_CN">ODB 数据库</comment>
+ <comment xml:lang="zh_TW">ODB 資料庫</comment>
+ <acronym>ODB</acronym>
+ <expanded-acronym>OpenDocument Database</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.base" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.odb"/>
+ <alias type="application/vnd.sun.xml.base"/>
+ </mime-type>
+ <mime-type type="application/vnd.oasis.opendocument.image">
+ <comment>ODI image</comment>
+ <comment xml:lang="ar">صورة ODI</comment>
+ <comment xml:lang="ast">Imaxe ODI</comment>
+ <comment xml:lang="be@latin">Vyjava ODI</comment>
+ <comment xml:lang="bg">Изображение — ODI</comment>
+ <comment xml:lang="ca">imatge ODI</comment>
+ <comment xml:lang="cs">obrázek ODI</comment>
+ <comment xml:lang="da">ODI-billede</comment>
+ <comment xml:lang="de">ODI-Bild</comment>
+ <comment xml:lang="el">Εικόνα ODI</comment>
+ <comment xml:lang="en_GB">ODI image</comment>
+ <comment xml:lang="eo">ODI-bildo</comment>
+ <comment xml:lang="es">imagen ODI</comment>
+ <comment xml:lang="eu">ODI irudia</comment>
+ <comment xml:lang="fi">ODI-kuva</comment>
+ <comment xml:lang="fo">ODI mynd</comment>
+ <comment xml:lang="fr">image ODI</comment>
+ <comment xml:lang="ga">íomhá ODI</comment>
+ <comment xml:lang="gl">imaxe ODI</comment>
+ <comment xml:lang="he">תמונת ODI</comment>
+ <comment xml:lang="hr">ODI slika</comment>
+ <comment xml:lang="hu">ODI-kép</comment>
+ <comment xml:lang="ia">Imagine ODI</comment>
+ <comment xml:lang="id">Citra ODI</comment>
+ <comment xml:lang="it">Immagine ODI</comment>
+ <comment xml:lang="ja">ODI 画像</comment>
+ <comment xml:lang="ka">ODI გამოსახულება</comment>
+ <comment xml:lang="kk">ODI суреті</comment>
+ <comment xml:lang="ko">ODI 그림</comment>
+ <comment xml:lang="lt">ODI paveikslėlis</comment>
+ <comment xml:lang="lv">ODI attēls</comment>
+ <comment xml:lang="nb">ODI-bilde</comment>
+ <comment xml:lang="nl">ODI-afbeelding</comment>
+ <comment xml:lang="nn">ODI-bilete</comment>
+ <comment xml:lang="oc">imatge ODI</comment>
+ <comment xml:lang="pl">Obraz ODI</comment>
+ <comment xml:lang="pt">imagem ODI</comment>
+ <comment xml:lang="pt_BR">Imagem ODI</comment>
+ <comment xml:lang="ro">Imagine ODI</comment>
+ <comment xml:lang="ru">Изображение ODI</comment>
+ <comment xml:lang="sk">Obrázok ODI</comment>
+ <comment xml:lang="sl">Slikovna datoteka ODI</comment>
+ <comment xml:lang="sq">Figurë ODI</comment>
+ <comment xml:lang="sr">ОДИ слика</comment>
+ <comment xml:lang="sv">ODI-bild</comment>
+ <comment xml:lang="tr">ODI görüntüsü</comment>
+ <comment xml:lang="uk">зображення ODI</comment>
+ <comment xml:lang="vi">Ảnh ODI</comment>
+ <comment xml:lang="zh_CN">ODI 图像</comment>
+ <comment xml:lang="zh_TW">ODI 影像</comment>
+ <acronym>ODI</acronym>
+ <expanded-acronym>OpenDocument Image</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="image-x-generic"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/vnd.oasis.opendocument.image" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.odi"/>
+ </mime-type>
+ <mime-type type="application/vnd.openofficeorg.extension">
+ <comment>OpenOffice.org extension</comment>
+ <comment xml:lang="ar">امتداد OpenOffice.org</comment>
+ <comment xml:lang="ast">Estensión d'OpenOffice.org</comment>
+ <comment xml:lang="be@latin">Pašyreńnie OpenOffice.org</comment>
+ <comment xml:lang="bg">Разширение — OpenOffice</comment>
+ <comment xml:lang="ca">extensió d'OpenOffice.org</comment>
+ <comment xml:lang="cs">rozšíření OpenOffice.org</comment>
+ <comment xml:lang="da">OpenOffice.org-udvidelse</comment>
+ <comment xml:lang="de">OpenOffice.org-Erweiterung</comment>
+ <comment xml:lang="el">Επέκταση OpenOffice.org</comment>
+ <comment xml:lang="en_GB">OpenOffice.org extension</comment>
+ <comment xml:lang="es">extensión de LibreOffice</comment>
+ <comment xml:lang="eu">OpenOffice.org luzapena</comment>
+ <comment xml:lang="fi">OpenOffice.org-laajennus</comment>
+ <comment xml:lang="fo">OpenOffice.org víðkan</comment>
+ <comment xml:lang="fr">extension OpenOffice.org</comment>
+ <comment xml:lang="ga">eisínteacht OpenOffice.org</comment>
+ <comment xml:lang="gl">Extensión de OpenOffice.org</comment>
+ <comment xml:lang="he">הרחבה של OpenOffice.org</comment>
+ <comment xml:lang="hr">OpenOffice.org proširenje</comment>
+ <comment xml:lang="hu">OpenOffice.org kiterjesztés</comment>
+ <comment xml:lang="ia">Extension OpenOffice.org</comment>
+ <comment xml:lang="id">Ekstensi OpenOffice.org</comment>
+ <comment xml:lang="it">Estensione OpenOffice.org</comment>
+ <comment xml:lang="ja">OpenOffice.org 拡張機能</comment>
+ <comment xml:lang="ka">OpenOffice.org-ის გაფართოება</comment>
+ <comment xml:lang="kk">OpenOffice.org кеңейтуі</comment>
+ <comment xml:lang="ko">OpenOffice.org 확장</comment>
+ <comment xml:lang="lt">OpenOffice.org plėtinys</comment>
+ <comment xml:lang="lv">OpenOffice.org paplašinājums</comment>
+ <comment xml:lang="nl">OpenOffice.org-uitbreiding</comment>
+ <comment xml:lang="nn">OpenOffice Writer-utviding</comment>
+ <comment xml:lang="oc">extension OpenOffice.org</comment>
+ <comment xml:lang="pl">Rozszerzenie OpenOffice.org</comment>
+ <comment xml:lang="pt">extensão OpenOffice.org</comment>
+ <comment xml:lang="pt_BR">Extensão do OpenOffice</comment>
+ <comment xml:lang="ro">Extensie OpenOffice.org</comment>
+ <comment xml:lang="ru">Расширение OpenOffice.org</comment>
+ <comment xml:lang="sk">Rozšírenie OpenOffice.org</comment>
+ <comment xml:lang="sl">Razširitev OpenOffice.org</comment>
+ <comment xml:lang="sq">Shtojcë për OpenOffice.org</comment>
+ <comment xml:lang="sr">проширење ОпенОфис.орг-а</comment>
+ <comment xml:lang="sv">OpenOffice.org-tillägg</comment>
+ <comment xml:lang="tr">OpenOffice.org eklentisi</comment>
+ <comment xml:lang="uk">розширення OpenOffice.org</comment>
+ <comment xml:lang="vi">Phần mở rộng của OpenOffice.org</comment>
+ <comment xml:lang="zh_CN">OpenOffice.org 扩展</comment>
+ <comment xml:lang="zh_TW">OpenOffice.org 擴充套件</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.oxt"/>
+ </mime-type>
+ <mime-type type="application/vnd.android.package-archive">
+ <comment>Android package</comment>
+ <comment xml:lang="ast">Paquete d'Android</comment>
+ <comment xml:lang="bg">Пакет — Android</comment>
+ <comment xml:lang="ca">paquet d'Android</comment>
+ <comment xml:lang="cs">balíčky systému Android</comment>
+ <comment xml:lang="da">Android-pakke</comment>
+ <comment xml:lang="de">Android-Paket</comment>
+ <comment xml:lang="el">Πακέτο Android</comment>
+ <comment xml:lang="en_GB">Android package</comment>
+ <comment xml:lang="eo">Android-pakaĵo</comment>
+ <comment xml:lang="es">paquete de Android</comment>
+ <comment xml:lang="eu">Android paketea</comment>
+ <comment xml:lang="fi">Android-paketti</comment>
+ <comment xml:lang="fr">paquet Android</comment>
+ <comment xml:lang="ga">pacáiste Android</comment>
+ <comment xml:lang="gl">paquete de Android</comment>
+ <comment xml:lang="he">חבילת אנדרויד</comment>
+ <comment xml:lang="hr">Android paket</comment>
+ <comment xml:lang="hu">Android csomag</comment>
+ <comment xml:lang="ia">Pacchetto Android</comment>
+ <comment xml:lang="id">Paket Android</comment>
+ <comment xml:lang="it">Pacchetto Android</comment>
+ <comment xml:lang="ja">Android パッケージ</comment>
+ <comment xml:lang="ka">Android-ის პაკეტი</comment>
+ <comment xml:lang="kk">Android дестесі</comment>
+ <comment xml:lang="ko">Android 패키지</comment>
+ <comment xml:lang="lv">Android pakotne</comment>
+ <comment xml:lang="nl">Android pakket</comment>
+ <comment xml:lang="oc">paquet Android</comment>
+ <comment xml:lang="pl">Pakiet Androida</comment>
+ <comment xml:lang="pt">pacote Android</comment>
+ <comment xml:lang="pt_BR">Pacote do Android</comment>
+ <comment xml:lang="ru">Пакет Android</comment>
+ <comment xml:lang="sk">Balík Android</comment>
+ <comment xml:lang="sl">Paket Android</comment>
+ <comment xml:lang="sr">Андроидов пакет</comment>
+ <comment xml:lang="sv">Android-paket</comment>
+ <comment xml:lang="tr">Android paketi</comment>
+ <comment xml:lang="uk">пакунок Android</comment>
+ <comment xml:lang="zh_CN">Android 应用包</comment>
+ <comment xml:lang="zh_TW">Android 軟體包</comment>
+ <sub-class-of type="application/x-java-archive"/>
+ <glob pattern="*.apk"/>
+ </mime-type>
+ <mime-type type="application/vnd.symbian.install">
+ <comment>SIS package</comment>
+ <comment xml:lang="ar">حزمة SIS</comment>
+ <comment xml:lang="ast">Paquete SIS</comment>
+ <comment xml:lang="be@latin">Pakunak SIS</comment>
+ <comment xml:lang="bg">Пакет — SIS</comment>
+ <comment xml:lang="ca">paquet SIS</comment>
+ <comment xml:lang="cs">balíček SIS</comment>
+ <comment xml:lang="da">SIS-pakke</comment>
+ <comment xml:lang="de">SIS-Paket</comment>
+ <comment xml:lang="el">Πακέτο SIS</comment>
+ <comment xml:lang="en_GB">SIS package</comment>
+ <comment xml:lang="eo">SIS-pakaĵo</comment>
+ <comment xml:lang="es">paquete SIS</comment>
+ <comment xml:lang="eu">SIS paketea</comment>
+ <comment xml:lang="fi">SIS-paketti</comment>
+ <comment xml:lang="fo">SIS pakki</comment>
+ <comment xml:lang="fr">paquet SIS</comment>
+ <comment xml:lang="ga">pacáiste SIS</comment>
+ <comment xml:lang="gl">paquete SIS</comment>
+ <comment xml:lang="he">חבילת SIS</comment>
+ <comment xml:lang="hr">SIS paket</comment>
+ <comment xml:lang="hu">SIS csomag</comment>
+ <comment xml:lang="ia">Pacchetto SIS</comment>
+ <comment xml:lang="id">Paket SIS</comment>
+ <comment xml:lang="it">Pacchetto SIS</comment>
+ <comment xml:lang="ja">SIS パッケージ</comment>
+ <comment xml:lang="kk">SIS дестесі</comment>
+ <comment xml:lang="ko">SIS 패키지</comment>
+ <comment xml:lang="lt">SIS paketas</comment>
+ <comment xml:lang="lv">SIS pakotne</comment>
+ <comment xml:lang="nb">SIS-pakke</comment>
+ <comment xml:lang="nl">SIS-pakket</comment>
+ <comment xml:lang="nn">SIS-pakke</comment>
+ <comment xml:lang="oc">paquet SIS</comment>
+ <comment xml:lang="pl">Pakiet SIS</comment>
+ <comment xml:lang="pt">pacote SIS</comment>
+ <comment xml:lang="pt_BR">Pacote SIS</comment>
+ <comment xml:lang="ro">Pachet SIS</comment>
+ <comment xml:lang="ru">Пакет SIS</comment>
+ <comment xml:lang="sk">Balíček SIS</comment>
+ <comment xml:lang="sl">Datoteka paketa SIS</comment>
+ <comment xml:lang="sq">Paketë SIS</comment>
+ <comment xml:lang="sr">СИС пакет</comment>
+ <comment xml:lang="sv">SIS-paket</comment>
+ <comment xml:lang="tr">SIS paketi</comment>
+ <comment xml:lang="uk">пакунок SIS</comment>
+ <comment xml:lang="vi">Gói SIS</comment>
+ <comment xml:lang="zh_CN">SIS 软件包</comment>
+ <comment xml:lang="zh_TW">SIS 軟體包</comment>
+ <acronym>SIS</acronym>
+ <expanded-acronym>Symbian Installation File</expanded-acronym>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="0x10000419" type="little32" offset="8"/>
+ </magic>
+ <glob pattern="*.sis"/>
+ </mime-type>
+ <mime-type type="x-epoc/x-sisx-app">
+ <comment>SISX package</comment>
+ <comment xml:lang="ar">حزمة SISX</comment>
+ <comment xml:lang="ast">Paquete SISX</comment>
+ <comment xml:lang="be@latin">Pakunak SISX</comment>
+ <comment xml:lang="bg">Пакет — SISX</comment>
+ <comment xml:lang="ca">paquet SISX</comment>
+ <comment xml:lang="cs">balíček SISX</comment>
+ <comment xml:lang="da">SISX-pakke</comment>
+ <comment xml:lang="de">SISX-Paket</comment>
+ <comment xml:lang="el">Πακέτο SISX</comment>
+ <comment xml:lang="en_GB">SISX package</comment>
+ <comment xml:lang="eo">SISX-pakaĵo</comment>
+ <comment xml:lang="es">paquete SISX</comment>
+ <comment xml:lang="eu">SISX paketea</comment>
+ <comment xml:lang="fi">SISX-paketti</comment>
+ <comment xml:lang="fo">SISX pakki</comment>
+ <comment xml:lang="fr">paquet SISX</comment>
+ <comment xml:lang="ga">pacáiste SISX</comment>
+ <comment xml:lang="gl">paquete SISX</comment>
+ <comment xml:lang="he">חבילת SISX</comment>
+ <comment xml:lang="hr">SISX paket</comment>
+ <comment xml:lang="hu">SISX csomag</comment>
+ <comment xml:lang="ia">Pacchetto SISX</comment>
+ <comment xml:lang="id">Paket SISX</comment>
+ <comment xml:lang="it">Pacchetto SISX</comment>
+ <comment xml:lang="ja">SISX パッケージ</comment>
+ <comment xml:lang="kk">SISX дестесі</comment>
+ <comment xml:lang="ko">SISX 패키지</comment>
+ <comment xml:lang="lt">SISX paketas</comment>
+ <comment xml:lang="lv">SISX pakotne</comment>
+ <comment xml:lang="nb">SISX-pakke</comment>
+ <comment xml:lang="nl">SISX-pakket</comment>
+ <comment xml:lang="nn">SISX-pakke</comment>
+ <comment xml:lang="oc">paquet SISX</comment>
+ <comment xml:lang="pl">Pakiet SISX</comment>
+ <comment xml:lang="pt">pacote SISX</comment>
+ <comment xml:lang="pt_BR">Pacote SISX</comment>
+ <comment xml:lang="ro">Pachet SISX</comment>
+ <comment xml:lang="ru">Пакет SISX</comment>
+ <comment xml:lang="sk">Balíček SISX</comment>
+ <comment xml:lang="sl">Datoteka paketa SISX</comment>
+ <comment xml:lang="sq">Paketë SISX</comment>
+ <comment xml:lang="sr">СИСИкс пакет</comment>
+ <comment xml:lang="sv">SISX-paket</comment>
+ <comment xml:lang="tr">SISX paketi</comment>
+ <comment xml:lang="uk">пакунок SISX</comment>
+ <comment xml:lang="vi">Gói SISX</comment>
+ <comment xml:lang="zh_CN">SISX 软件包</comment>
+ <comment xml:lang="zh_TW">SISX 軟體包</comment>
+ <acronym>SIS</acronym>
+ <expanded-acronym>Symbian Installation File</expanded-acronym>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="0x10201a7a" type="little32" offset="0"/>
+ </magic>
+ <glob pattern="*.sisx"/>
+ </mime-type>
+ <mime-type type="application/vnd.tcpdump.pcap">
+ <comment>Network Packet Capture</comment>
+ <comment xml:lang="bg">Прихванати пакети по мрежата</comment>
+ <comment xml:lang="ca">captura de paquets de xarxa</comment>
+ <comment xml:lang="cs">Network Packet Capture</comment>
+ <comment xml:lang="da">Netværkspakkeoptegnelse</comment>
+ <comment xml:lang="de">Netzwerk-Paketmitschnitt</comment>
+ <comment xml:lang="el">Σύλληψη πακέτων δικτύου</comment>
+ <comment xml:lang="en_GB">Network Packet Capture</comment>
+ <comment xml:lang="es">captura de paquete de red</comment>
+ <comment xml:lang="eu">Sareko pakete kaptura</comment>
+ <comment xml:lang="fi">Verkkopakettien kaappaus</comment>
+ <comment xml:lang="fr">capture de paquet réseau</comment>
+ <comment xml:lang="ga">Gabháltas Paicéid Líonra</comment>
+ <comment xml:lang="gl">Captura de Network Packet</comment>
+ <comment xml:lang="he">לכידה של מנות נתונים ברשת</comment>
+ <comment xml:lang="hr">Mrežno hvatanje paketa</comment>
+ <comment xml:lang="hu">Hálózati csomagelfogás</comment>
+ <comment xml:lang="ia">Captura de pacchettos de rete</comment>
+ <comment xml:lang="id">Tangkapan Paket Jaringan</comment>
+ <comment xml:lang="it">Cattura pacchetti rete</comment>
+ <comment xml:lang="ja">ネットワークパケットキャプチャー</comment>
+ <comment xml:lang="ka">ქსელური პაკეტის ანაბეჭდი</comment>
+ <comment xml:lang="kk">ұсталған желілік пакеттер</comment>
+ <comment xml:lang="ko">네트워크 패킷 캡처</comment>
+ <comment xml:lang="lv">Network Packet Capture</comment>
+ <comment xml:lang="nl">Network Packet Capture</comment>
+ <comment xml:lang="oc">captura de paquet ret</comment>
+ <comment xml:lang="pl">Przechwycenie pakietu sieciowego</comment>
+ <comment xml:lang="pt">captura Network Packet</comment>
+ <comment xml:lang="pt_BR">Pacote de captura de rede</comment>
+ <comment xml:lang="ru">Захваченные сетевые пакеты</comment>
+ <comment xml:lang="sk">Zachytené sieťové pakety</comment>
+ <comment xml:lang="sl">Zajem omrežnih paketov</comment>
+ <comment xml:lang="sr">Снимање мрежног пакета</comment>
+ <comment xml:lang="sv">Fångst av nätverkspaket</comment>
+ <comment xml:lang="tr">Ağ Paket Yakalaması</comment>
+ <comment xml:lang="uk">перехоплені дані мережевих пакетів</comment>
+ <comment xml:lang="zh_CN">网络包抓取</comment>
+ <comment xml:lang="zh_TW">網路封包捕捉</comment>
+ <magic priority="50">
+ <match value="0xa1b2c3d4" type="host32" offset="0"/>
+ <match value="0xd4c3b2a1" type="host32" offset="0"/>
+ </magic>
+ <glob pattern="*.pcap"/>
+ <glob pattern="*.cap"/>
+ <glob pattern="*.dmp"/>
+ <alias type="application/x-pcap"/>
+ <alias type="application/pcap"/>
+ </mime-type>
+ <mime-type type="application/vnd.wordperfect">
+ <comment>WordPerfect document</comment>
+ <comment xml:lang="ar">مستند WordPerfect</comment>
+ <comment xml:lang="ast">Documentu de WordPerfect</comment>
+ <comment xml:lang="az">WordPerfect sənədi</comment>
+ <comment xml:lang="be@latin">Dakument WordPerfect</comment>
+ <comment xml:lang="bg">Документ — WordPerfect</comment>
+ <comment xml:lang="ca">document WordPerfect</comment>
+ <comment xml:lang="cs">dokument WordPerfect</comment>
+ <comment xml:lang="cy">Dogfen WordPerfect</comment>
+ <comment xml:lang="da">WordPerfect-dokument</comment>
+ <comment xml:lang="de">WordPerfect-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο WordPerfect</comment>
+ <comment xml:lang="en_GB">WordPerfect document</comment>
+ <comment xml:lang="eo">WordPerfect-dokumento</comment>
+ <comment xml:lang="es">documento de WordPerfect</comment>
+ <comment xml:lang="eu">WordPerfect dokumentua</comment>
+ <comment xml:lang="fi">WordPerfect-asiakirja</comment>
+ <comment xml:lang="fo">WordPerfect skjal</comment>
+ <comment xml:lang="fr">document WordPerfect</comment>
+ <comment xml:lang="ga">cáipéis WordPerfect</comment>
+ <comment xml:lang="gl">documento de WordPerfect</comment>
+ <comment xml:lang="he">מסמך WordPerfect</comment>
+ <comment xml:lang="hr">WordPerfect dokument</comment>
+ <comment xml:lang="hu">WordPerfect-dokumentum</comment>
+ <comment xml:lang="ia">Documento WordPerfect</comment>
+ <comment xml:lang="id">Dokumen WordPerfect</comment>
+ <comment xml:lang="it">Documento WordPerfect</comment>
+ <comment xml:lang="ja">WordPerfect ドキュメント</comment>
+ <comment xml:lang="kk">WordPerfect құжаты</comment>
+ <comment xml:lang="ko">WordPerfect 문서</comment>
+ <comment xml:lang="lt">WordPerfect dokumentas</comment>
+ <comment xml:lang="lv">WordPerfect dokuments</comment>
+ <comment xml:lang="ms">Dokumen WordPerfect</comment>
+ <comment xml:lang="nb">WordPerfect-dokument</comment>
+ <comment xml:lang="nl">WordPerfect-document</comment>
+ <comment xml:lang="nn">WordPerfect-dokument</comment>
+ <comment xml:lang="oc">document WordPerfect</comment>
+ <comment xml:lang="pl">Dokument WordPerfect</comment>
+ <comment xml:lang="pt">documento WordPerfect</comment>
+ <comment xml:lang="pt_BR">Documento do WordPerfect</comment>
+ <comment xml:lang="ro">Document WordPerfect</comment>
+ <comment xml:lang="ru">Документ WordPerfect</comment>
+ <comment xml:lang="sk">Dokument WordPerfect</comment>
+ <comment xml:lang="sl">Dokument WordPerfect</comment>
+ <comment xml:lang="sq">Dokument WordPerfect</comment>
+ <comment xml:lang="sr">документ Ворд перфекта</comment>
+ <comment xml:lang="sv">WordPerfect-dokument</comment>
+ <comment xml:lang="tr">WordPerfect belgesi</comment>
+ <comment xml:lang="uk">документ WordPerfect</comment>
+ <comment xml:lang="vi">Tài liệu WordPerfect</comment>
+ <comment xml:lang="zh_CN">WordPerfect 文档</comment>
+ <comment xml:lang="zh_TW">WordPerfect 文件</comment>
+ <alias type="application/x-wordperfect"/>
+ <alias type="application/wordperfect"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="WPC" type="string" offset="1"/>
+
+ </magic>
+ <glob pattern="*.wp"/>
+ <glob pattern="*.wp4"/>
+ <glob pattern="*.wp5"/>
+ <glob pattern="*.wp6"/>
+ <glob pattern="*.wpd"/>
+ <glob pattern="*.wpp"/>
+ </mime-type>
+ <mime-type type="application/vnd.youtube.yt">
+ <comment>YouTube Media Archive</comment>
+ <generic-icon name="video-x-generic"/>
+ <glob pattern="*.yt"/>
+ <sub-class-of type="application/zip"/>
+ </mime-type>
+ <mime-type type="application/x-spss-por">
+ <comment>SPSS Portable Data File</comment>
+ <comment xml:lang="ar">ملف بيانات SPSS متنقلة</comment>
+ <comment xml:lang="bg">Данни — SPSS, преносими</comment>
+ <comment xml:lang="ca">fitxer de dades portables SPSS</comment>
+ <comment xml:lang="cs">soubor přenositelných dat SPSS</comment>
+ <comment xml:lang="da">Portabel SPSS-datafil</comment>
+ <comment xml:lang="de">SPSS portable Datendatei</comment>
+ <comment xml:lang="el">Φορητό αρχείο δεδομένων SPSS</comment>
+ <comment xml:lang="en_GB">SPSS Portable Data File</comment>
+ <comment xml:lang="es">archivo de datos portátil de SPSS</comment>
+ <comment xml:lang="eu">SPSS datuen fitxategi eramangarria</comment>
+ <comment xml:lang="fo">SPSS flytifør dátufíla</comment>
+ <comment xml:lang="fr">fichier portable de données SPSS</comment>
+ <comment xml:lang="ga">comhad iniompartha sonraí SPSS</comment>
+ <comment xml:lang="gl">ficheiro de datos portábel SPSS</comment>
+ <comment xml:lang="he">קובץ מידע נייד SPSS</comment>
+ <comment xml:lang="hr">SPSS prenosiva podatkovna datoteka</comment>
+ <comment xml:lang="hu">SPSS hordozható adatfájl</comment>
+ <comment xml:lang="ia">File portabile de datos SPSS</comment>
+ <comment xml:lang="id">Berkas Data Portabel SPSS</comment>
+ <comment xml:lang="it">File dati SPSS Portable</comment>
+ <comment xml:lang="ja">SPSS ポータブルデータファイル</comment>
+ <comment xml:lang="kk">SPSS тасымалы ақпарат файлы</comment>
+ <comment xml:lang="ko">SPSS 이동식 데이터 파일</comment>
+ <comment xml:lang="lt">SPSS perkeliamų duomenų failas</comment>
+ <comment xml:lang="lv">SPSS pārvietojamu datu datne</comment>
+ <comment xml:lang="nl">SPSS Portable Databestand</comment>
+ <comment xml:lang="oc">fichièr portable de donadas SPSS</comment>
+ <comment xml:lang="pl">Plik przenośnych danych SPSS</comment>
+ <comment xml:lang="pt">ficheiro de dados portátil SPSS</comment>
+ <comment xml:lang="pt_BR">Arquivo de Dados Portáteis SPSS</comment>
+ <comment xml:lang="ro">Fișier portabil de date SPSS</comment>
+ <comment xml:lang="ru">Файл переносимых данных SPSS</comment>
+ <comment xml:lang="sk">Súbor prenosných dát SPSS</comment>
+ <comment xml:lang="sl">Prenosna podatkovna datoteka SPSS</comment>
+ <comment xml:lang="sr">СПСС датотека преносних података</comment>
+ <comment xml:lang="sv">Portabel SPSS-datafil</comment>
+ <comment xml:lang="tr">SPSS Taşınabilir Veri Dosyası</comment>
+ <comment xml:lang="uk">файл даних SPSS Portable</comment>
+ <comment xml:lang="zh_CN">SPSS 便携式数据文件</comment>
+ <comment xml:lang="zh_TW">SPSS 可攜式資料檔</comment>
+ <magic priority="50">
+ <match value="ASCII SPSS PORT FILE" type="string" offset="40"/>
+ </magic>
+ <glob pattern="*.por"/>
+ </mime-type>
+ <mime-type type="application/x-spss-sav">
+ <comment>SPSS Data File</comment>
+ <comment xml:lang="ar">ملف بيانات SPSS</comment>
+ <comment xml:lang="bg">Данни — SPSS</comment>
+ <comment xml:lang="ca">fitxer de dades SPSS</comment>
+ <comment xml:lang="cs">datový soubor SPSS</comment>
+ <comment xml:lang="da">SPSS-datafil</comment>
+ <comment xml:lang="de">SPSS-Datendatei</comment>
+ <comment xml:lang="el">Αρχείο δεδομένων SPSS</comment>
+ <comment xml:lang="en_GB">SPSS Data File</comment>
+ <comment xml:lang="es">archivo de datos SPSS</comment>
+ <comment xml:lang="eu">SPSS datuen fitxategia</comment>
+ <comment xml:lang="fi">SPSS-datatiedosto</comment>
+ <comment xml:lang="fo">SPSS dátufíla</comment>
+ <comment xml:lang="fr">fichier de données SPSS</comment>
+ <comment xml:lang="ga">comhad sonraí SPSS</comment>
+ <comment xml:lang="gl">ficheiro de datos SPSS</comment>
+ <comment xml:lang="he">קובץ מידע SPSS</comment>
+ <comment xml:lang="hr">SPSS podatkovna datoteka</comment>
+ <comment xml:lang="hu">SPSS adatfájl</comment>
+ <comment xml:lang="ia">File de datos SPSS</comment>
+ <comment xml:lang="id">Berkas Data SPSS</comment>
+ <comment xml:lang="it">File dati SPSS</comment>
+ <comment xml:lang="ja">SPSS データファイル</comment>
+ <comment xml:lang="kk">SPSS ақпарат файлы</comment>
+ <comment xml:lang="ko">SPSS 데이터 파일</comment>
+ <comment xml:lang="lt">SPSS duomenų failas</comment>
+ <comment xml:lang="lv">SPSS datu datne</comment>
+ <comment xml:lang="nl">SPSS Databstand</comment>
+ <comment xml:lang="oc">fichièr de donadas SPSS</comment>
+ <comment xml:lang="pl">Plik danych SPSS</comment>
+ <comment xml:lang="pt">ficheiro de dados SPSS</comment>
+ <comment xml:lang="pt_BR">Arquivo de dados SPSS</comment>
+ <comment xml:lang="ro">Fișier date SPSS</comment>
+ <comment xml:lang="ru">Файл данных SPSS</comment>
+ <comment xml:lang="sk">Dátový súbor SPSS</comment>
+ <comment xml:lang="sl">Podatkovna datoteka SPSS</comment>
+ <comment xml:lang="sr">СПСС датотека података</comment>
+ <comment xml:lang="sv">SPSS-datafil</comment>
+ <comment xml:lang="tr">SPSS Veri Dosyası</comment>
+ <comment xml:lang="uk">файл даних SPSS</comment>
+ <comment xml:lang="zh_CN">SPSS 数据文件</comment>
+ <comment xml:lang="zh_TW">SPSS 資料檔</comment>
+ <alias type="application/x-spss-savefile"/>
+ <magic priority="50">
+ <match value="$FL2" type="string" offset="0"/>
+ <match value="$FL3" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.sav"/>
+ <glob pattern="*.zsav"/>
+ </mime-type>
+ <mime-type type="application/x-xbel">
+ <comment>XBEL bookmarks</comment>
+ <comment xml:lang="ar">علامات XBEL</comment>
+ <comment xml:lang="be@latin">Zakładki XBEL</comment>
+ <comment xml:lang="bg">Отметки — XBEL</comment>
+ <comment xml:lang="ca">llista d'adreces d'interès XBEL</comment>
+ <comment xml:lang="cs">záložky XBEL</comment>
+ <comment xml:lang="da">XBEL-bogmærker</comment>
+ <comment xml:lang="de">XBEL-Lesezeichen</comment>
+ <comment xml:lang="el">Σελιδοδείκτες XBEL</comment>
+ <comment xml:lang="en_GB">XBEL bookmarks</comment>
+ <comment xml:lang="eo">XBEL-legosignoj</comment>
+ <comment xml:lang="es">marcadores XBEL</comment>
+ <comment xml:lang="eu">XBEL laster-markak</comment>
+ <comment xml:lang="fi">XBEL-kirjanmerkit</comment>
+ <comment xml:lang="fo">XBEL bókamerki</comment>
+ <comment xml:lang="fr">marque-pages XBEL</comment>
+ <comment xml:lang="ga">leabharmharcanna XBEL</comment>
+ <comment xml:lang="gl">Marcadores XBEL</comment>
+ <comment xml:lang="he">סימניית XBEL</comment>
+ <comment xml:lang="hr">XBEL zabilješka</comment>
+ <comment xml:lang="hu">XBEL-könyvjelzők</comment>
+ <comment xml:lang="ia">Marcapaginas XBEL</comment>
+ <comment xml:lang="id">Bookmark XBEL</comment>
+ <comment xml:lang="it">Segnalibri XBEL</comment>
+ <comment xml:lang="ja">XBEL ブックマーク</comment>
+ <comment xml:lang="kk">XBEL бетбелгілері</comment>
+ <comment xml:lang="ko">XBEL 책갈피</comment>
+ <comment xml:lang="lt">XBEL žymelės</comment>
+ <comment xml:lang="lv">XBEL grāmatzīmes</comment>
+ <comment xml:lang="ms">Tandabuku XBEL</comment>
+ <comment xml:lang="nb">XBEL-bokmerker</comment>
+ <comment xml:lang="nl">XBEL-bladwijzers</comment>
+ <comment xml:lang="nn">XBEL-bokmerker</comment>
+ <comment xml:lang="oc">marcapaginas XBEL</comment>
+ <comment xml:lang="pl">Zakładki XBEL</comment>
+ <comment xml:lang="pt">marcadores XBEL</comment>
+ <comment xml:lang="pt_BR">Marcadores do XBEL</comment>
+ <comment xml:lang="ro">Semne de carte XBEL</comment>
+ <comment xml:lang="ru">Закладки XBEL</comment>
+ <comment xml:lang="sk">Záložky XBEL</comment>
+ <comment xml:lang="sl">Datoteka zaznamkov XBEL</comment>
+ <comment xml:lang="sq">Libërshënues XBEL</comment>
+ <comment xml:lang="sr">ИксБЕЛ обележивачи</comment>
+ <comment xml:lang="sv">XBEL-bokmärken</comment>
+ <comment xml:lang="tr">XBEL yer imleri</comment>
+ <comment xml:lang="uk">закладки XBEL</comment>
+ <comment xml:lang="vi">Liên kết đã lưu XBEL</comment>
+ <comment xml:lang="zh_CN">XBEL 书签</comment>
+ <comment xml:lang="zh_TW">XBEL 格式書籤</comment>
+ <acronym>XBEL</acronym>
+ <expanded-acronym>XML Bookmark Exchange Language</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="text-html"/>
+ <magic priority="50">
+ <match value="&lt;!DOCTYPE\ xbel" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.xbel"/>
+ </mime-type>
+ <mime-type type="application/x-7z-compressed">
+ <comment>7-zip archive</comment>
+ <comment xml:lang="ar">أرشيف 7-zip</comment>
+ <comment xml:lang="ast">Archivu 7-zip</comment>
+ <comment xml:lang="be@latin">Archiŭ 7-zip</comment>
+ <comment xml:lang="bg">Архив — 7-zip</comment>
+ <comment xml:lang="ca">arxiu 7-zip</comment>
+ <comment xml:lang="cs">archiv 7-zip</comment>
+ <comment xml:lang="da">7-zip-arkiv</comment>
+ <comment xml:lang="de">7zip-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο 7-zip</comment>
+ <comment xml:lang="en_GB">7-zip archive</comment>
+ <comment xml:lang="eo">7z-arkivo</comment>
+ <comment xml:lang="es">archivador 7-zip</comment>
+ <comment xml:lang="eu">7-zip artxiboa</comment>
+ <comment xml:lang="fi">7-zip-arkisto</comment>
+ <comment xml:lang="fo">7-zip skjalasavn</comment>
+ <comment xml:lang="fr">archive 7-zip</comment>
+ <comment xml:lang="ga">cartlann 7-zip</comment>
+ <comment xml:lang="gl">arquivo 7-zip</comment>
+ <comment xml:lang="he">ארכיון 7-zip</comment>
+ <comment xml:lang="hr">7-zip arhiva</comment>
+ <comment xml:lang="hu">7-zip archívum</comment>
+ <comment xml:lang="ia">Archivo 7-zip</comment>
+ <comment xml:lang="id">Arsip 7-zip</comment>
+ <comment xml:lang="it">Archivio 7-zip</comment>
+ <comment xml:lang="ja">7-zip アーカイブ</comment>
+ <comment xml:lang="ka">7-zip არქივი</comment>
+ <comment xml:lang="kk">7-zip архиві</comment>
+ <comment xml:lang="ko">7-ZIP 압축 파일</comment>
+ <comment xml:lang="lt">7-zip archyvas</comment>
+ <comment xml:lang="lv">7-zip arhīvs</comment>
+ <comment xml:lang="nb">7-zip-arkiv</comment>
+ <comment xml:lang="nl">7-zip-archief</comment>
+ <comment xml:lang="nn">7-zip-arkiv</comment>
+ <comment xml:lang="oc">archiu 7-zip</comment>
+ <comment xml:lang="pl">Archiwum 7-zip</comment>
+ <comment xml:lang="pt">arquivo 7-zip</comment>
+ <comment xml:lang="pt_BR">Pacote 7-Zip</comment>
+ <comment xml:lang="ro">Arhivă 7-zip</comment>
+ <comment xml:lang="ru">Архив 7-zip</comment>
+ <comment xml:lang="sk">Archív 7-zip</comment>
+ <comment xml:lang="sl">Datoteka arhiva 7-zip</comment>
+ <comment xml:lang="sq">Arkiv 7-zip</comment>
+ <comment xml:lang="sr">7-зип архива</comment>
+ <comment xml:lang="sv">7-zip-arkiv</comment>
+ <comment xml:lang="tr">7-Zip arşivi</comment>
+ <comment xml:lang="uk">архів 7-zip</comment>
+ <comment xml:lang="vi">Kho nén 7-zip</comment>
+ <comment xml:lang="zh_CN">7-zip 归档文件</comment>
+ <comment xml:lang="zh_TW">7-zip 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="7z\274\257\047\034" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.7z"/>
+ </mime-type>
+ <mime-type type="application/x-abiword">
+ <comment>AbiWord document</comment>
+ <comment xml:lang="ar">مستند آبي وورد</comment>
+ <comment xml:lang="ast">Documentu d'AbiWord</comment>
+ <comment xml:lang="be@latin">Dakument AbiWord</comment>
+ <comment xml:lang="bg">Документ — AbiWord</comment>
+ <comment xml:lang="ca">document AbiWord</comment>
+ <comment xml:lang="cs">dokument AbiWord</comment>
+ <comment xml:lang="da">AbiWord-dokument</comment>
+ <comment xml:lang="de">AbiWord-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο AbiWord</comment>
+ <comment xml:lang="en_GB">AbiWord document</comment>
+ <comment xml:lang="eo">AbiWord-dokumento</comment>
+ <comment xml:lang="es">documento de Abiword</comment>
+ <comment xml:lang="eu">AbiWord dokumentua</comment>
+ <comment xml:lang="fi">AbiWord-asiakirja</comment>
+ <comment xml:lang="fo">AbiWord skjal</comment>
+ <comment xml:lang="fr">document AbiWord</comment>
+ <comment xml:lang="ga">cáipéis AbiWord</comment>
+ <comment xml:lang="gl">documento de AbiWord</comment>
+ <comment xml:lang="he">מסמך AbiWord</comment>
+ <comment xml:lang="hr">AbiWord dokument</comment>
+ <comment xml:lang="hu">AbiWord-dokumentum</comment>
+ <comment xml:lang="ia">Documento AbiWord</comment>
+ <comment xml:lang="id">Dokumen AbiWord</comment>
+ <comment xml:lang="it">Documento AbiWord</comment>
+ <comment xml:lang="ja">AbiWord ドキュメント</comment>
+ <comment xml:lang="ka">AbiWord-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">AbiWord құжаты</comment>
+ <comment xml:lang="ko">AbiWord 문서</comment>
+ <comment xml:lang="lt">AbiWord dokumentas</comment>
+ <comment xml:lang="lv">AbiWord dokuments</comment>
+ <comment xml:lang="ms">Dokumen AbiWord</comment>
+ <comment xml:lang="nb">AbiWord-dokument</comment>
+ <comment xml:lang="nl">AbiWord-document</comment>
+ <comment xml:lang="nn">AbiWord-dokument</comment>
+ <comment xml:lang="oc">document AbiWord</comment>
+ <comment xml:lang="pl">Dokument AbiWord</comment>
+ <comment xml:lang="pt">documento AbiWord</comment>
+ <comment xml:lang="pt_BR">Documento do AbiWord</comment>
+ <comment xml:lang="ro">Document AbiWord</comment>
+ <comment xml:lang="ru">Документ AbiWord</comment>
+ <comment xml:lang="sk">Dokument AbiWord</comment>
+ <comment xml:lang="sl">Dokument AbiWord</comment>
+ <comment xml:lang="sq">Dokument AbiWord</comment>
+ <comment xml:lang="sr">Абиворд документ</comment>
+ <comment xml:lang="sv">AbiWord-dokument</comment>
+ <comment xml:lang="tr">AbiWord belgesi</comment>
+ <comment xml:lang="uk">документ AbiWord</comment>
+ <comment xml:lang="vi">Tài liệu AbiWord</comment>
+ <comment xml:lang="zh_CN">AbiWord 文档</comment>
+ <comment xml:lang="zh_TW">AbiWord 文件</comment>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="&lt;abiword" type="string" offset="0:256"/>
+ <match value="&lt;!DOCTYPE abiword" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.abw"/>
+ <glob pattern="*.abw.CRASHED"/>
+ <glob pattern="*.abw.gz"/>
+ <glob pattern="*.zabw"/>
+ <root-XML namespaceURI="http://www.abisource.com/awml.dtd" localName="abiword"/>
+ </mime-type>
+ <mime-type type="application/x-cue">
+ <comment>CD image cuesheet</comment>
+ <comment xml:lang="ar">صفيحة صورة الـCD جديلة</comment>
+ <comment xml:lang="be@latin">Infarmacyjny arkuš vyjavy CD</comment>
+ <comment xml:lang="bg">Описание на изображение на CD</comment>
+ <comment xml:lang="ca">«cuesheet» d'imatge de CD</comment>
+ <comment xml:lang="cs">rozvržení stop obrazu CD</comment>
+ <comment xml:lang="da">Cd-aftrykscuesheet</comment>
+ <comment xml:lang="de">CD-Abbild-Cuesheet</comment>
+ <comment xml:lang="el">Φύλλο cue εικόνας CD</comment>
+ <comment xml:lang="en_GB">CD image cuesheet</comment>
+ <comment xml:lang="es">hoja CUE de imagen de CD</comment>
+ <comment xml:lang="eu">CD irudiaren CUE orria</comment>
+ <comment xml:lang="fi">CD-vedos cuesheet</comment>
+ <comment xml:lang="fr">index de pistes de CD</comment>
+ <comment xml:lang="ga">bileog chiúála íomhá CD</comment>
+ <comment xml:lang="gl">cue sheet dunha imaxe de CD</comment>
+ <comment xml:lang="he">גליון נתונים לתמונת דיסק</comment>
+ <comment xml:lang="hr">CD slika s meta podacima</comment>
+ <comment xml:lang="hu">CD kép cuesheet</comment>
+ <comment xml:lang="ia">Indice de pistas de CD</comment>
+ <comment xml:lang="id">Citra cuesheet CD</comment>
+ <comment xml:lang="it">Cuesheet immagine CD</comment>
+ <comment xml:lang="ja">CD イメージキューシート</comment>
+ <comment xml:lang="kk">CD бейнесінің құрама кестесі</comment>
+ <comment xml:lang="ko">CD 이미지 큐시트</comment>
+ <comment xml:lang="lt">CD atvaizdžio aprašas</comment>
+ <comment xml:lang="lv">CD attēla rindulapa</comment>
+ <comment xml:lang="nb">Filliste for CD-avtrykk</comment>
+ <comment xml:lang="nl">CD-inhoudsopgave</comment>
+ <comment xml:lang="nn">CD-bilete-indeksfil</comment>
+ <comment xml:lang="oc">indèx de pistas de CD</comment>
+ <comment xml:lang="pl">Obraz cuesheet płyty CD</comment>
+ <comment xml:lang="pt">índice de CD de imagem</comment>
+ <comment xml:lang="pt_BR">Índice de Imagem de CD</comment>
+ <comment xml:lang="ro">Imagine CD cuesheet</comment>
+ <comment xml:lang="ru">Таблица содержания образа CD</comment>
+ <comment xml:lang="sk">Rozvrhnutie stôp obrazu CD</comment>
+ <comment xml:lang="sl">Datoteka razpredelnice odtisa CD cue</comment>
+ <comment xml:lang="sq">Cuesheet imazhi CD</comment>
+ <comment xml:lang="sr">Кју лист ЦД одраза</comment>
+ <comment xml:lang="sv">Indexblad för cd-avbild</comment>
+ <comment xml:lang="tr">CD görüntüsü belgesi</comment>
+ <comment xml:lang="uk">таблиця CUE образу CD</comment>
+ <comment xml:lang="vi">Tờ tín hiệu báo ảnh CD</comment>
+ <comment xml:lang="zh_CN">CD 映像标记文件</comment>
+ <comment xml:lang="zh_TW">CD 映像指示表</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.cue"/>
+ </mime-type>
+ <mime-type type="application/x-amipro">
+ <comment>Lotus AmiPro document</comment>
+ <comment xml:lang="ar">مستند Lotus AmiPro</comment>
+ <comment xml:lang="ast">Documentu de Lotus AmiPro</comment>
+ <comment xml:lang="az">Lotus AmiPro sənədi</comment>
+ <comment xml:lang="be@latin">Dakument Lotus AmiPro</comment>
+ <comment xml:lang="bg">Документ — Lotus AmiPro</comment>
+ <comment xml:lang="ca">document de Lotus AmiPro</comment>
+ <comment xml:lang="cs">dokument Lotus AmiPro</comment>
+ <comment xml:lang="cy">Dogfen Lotus AmiPro</comment>
+ <comment xml:lang="da">Lotus AmiPro-dokument</comment>
+ <comment xml:lang="de">Lotus-AmiPro-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Lotus AmiPro</comment>
+ <comment xml:lang="en_GB">Lotus AmiPro document</comment>
+ <comment xml:lang="eo">dokumento de Lotus AmiPro</comment>
+ <comment xml:lang="es">documento de Lotus AmiPro</comment>
+ <comment xml:lang="eu">Lotus AmiPro dokumentua</comment>
+ <comment xml:lang="fi">Lotus AmiPro -asiakirja</comment>
+ <comment xml:lang="fo">Lotus AmiPro skjal</comment>
+ <comment xml:lang="fr">document Lotus AmiPro</comment>
+ <comment xml:lang="ga">cáipéis Lotus AmiPro</comment>
+ <comment xml:lang="gl">documento de Lotus AmiPro</comment>
+ <comment xml:lang="he">מסמך של Lotus AmiPro</comment>
+ <comment xml:lang="hr">Lotus AmiPro dokument</comment>
+ <comment xml:lang="hu">Lotus AmiPro-dokumentum</comment>
+ <comment xml:lang="ia">Documento Lotus AmiPro</comment>
+ <comment xml:lang="id">Dokumen Lotus AmiPro</comment>
+ <comment xml:lang="it">Documento Lotus AmiPro</comment>
+ <comment xml:lang="ja">Lotus AmiPro ドキュメント</comment>
+ <comment xml:lang="kk">Lotus AmiPro құжаты</comment>
+ <comment xml:lang="ko">Lotus AmiPro 문서</comment>
+ <comment xml:lang="lt">Lotus AmiPro dokumentas</comment>
+ <comment xml:lang="lv">Lotus AmiPro dokuments</comment>
+ <comment xml:lang="ms">Dokumen Lotus AmiPro</comment>
+ <comment xml:lang="nb">Lotus AmiPro-dokument</comment>
+ <comment xml:lang="nl">Lotus AmiPro-document</comment>
+ <comment xml:lang="nn">Lotus AmiPro-dokument</comment>
+ <comment xml:lang="oc">document Lotus AmiPro</comment>
+ <comment xml:lang="pl">Dokument Lotus AmiPro</comment>
+ <comment xml:lang="pt">documento Lotus AmiPro</comment>
+ <comment xml:lang="pt_BR">Documento do Lotus AmiPro</comment>
+ <comment xml:lang="ro">Document Lotus AmiPro</comment>
+ <comment xml:lang="ru">Документ Lotus AmiPro</comment>
+ <comment xml:lang="sk">Dokument Lotus AmiPro</comment>
+ <comment xml:lang="sl">Dokument Lotus AmiPro</comment>
+ <comment xml:lang="sq">Dokument Lotus AmiPro</comment>
+ <comment xml:lang="sr">Лотусов Ами Про документ</comment>
+ <comment xml:lang="sv">Lotus AmiPro-dokument</comment>
+ <comment xml:lang="tr">Lotus AmiPro belgesi</comment>
+ <comment xml:lang="uk">документ Lotus AmiPro</comment>
+ <comment xml:lang="vi">Tài liệu Lotus AmiPro</comment>
+ <comment xml:lang="zh_CN">Lotus AmiPro 文档</comment>
+ <comment xml:lang="zh_TW">Lotus AmiPro 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.sam"/>
+ </mime-type>
+ <mime-type type="application/x-aportisdoc">
+ <comment>AportisDoc document</comment>
+ <comment xml:lang="ar">مستند AportisDoc</comment>
+ <comment xml:lang="ast">Documentu d'AportisDoc</comment>
+ <comment xml:lang="bg">Документ — AportisDoc</comment>
+ <comment xml:lang="ca">document AportisDoc</comment>
+ <comment xml:lang="cs">dokument AportisDoc</comment>
+ <comment xml:lang="da">AportisDoc-dokument</comment>
+ <comment xml:lang="de">AportisDoc-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο AportisDoc</comment>
+ <comment xml:lang="en_GB">AportisDoc document</comment>
+ <comment xml:lang="eo">AportisDoc-dokumento</comment>
+ <comment xml:lang="es">documento de AportisDoc</comment>
+ <comment xml:lang="eu">AportisDoc dokumentua</comment>
+ <comment xml:lang="fi">AportisDoc-asiakirja</comment>
+ <comment xml:lang="fo">AportisDoc skjal</comment>
+ <comment xml:lang="fr">document AportisDoc</comment>
+ <comment xml:lang="ga">cáipéis AportisDoc</comment>
+ <comment xml:lang="gl">documento de AportiDoc</comment>
+ <comment xml:lang="he">מסמך AportisDoc</comment>
+ <comment xml:lang="hr">AportisDoc dokument</comment>
+ <comment xml:lang="hu">AportisDoc-dokumentum</comment>
+ <comment xml:lang="ia">Documento AportisDoc</comment>
+ <comment xml:lang="id">Dokumen AportisDoc</comment>
+ <comment xml:lang="it">Documento AportisDoc</comment>
+ <comment xml:lang="ja">AportisDoc ドキュメント</comment>
+ <comment xml:lang="ka">AportisDoc-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">AportisDoc құжаты</comment>
+ <comment xml:lang="ko">AportisDoc 문서</comment>
+ <comment xml:lang="lt">AportisDoc dokumentas</comment>
+ <comment xml:lang="lv">AportisDoc dokuments</comment>
+ <comment xml:lang="nl">AportisDoc-document</comment>
+ <comment xml:lang="oc">document AportisDoc</comment>
+ <comment xml:lang="pl">Dokument AportisDoc</comment>
+ <comment xml:lang="pt">documento AportisDoc</comment>
+ <comment xml:lang="pt_BR">Documento do AportisDoc</comment>
+ <comment xml:lang="ro">Document AportisDoc</comment>
+ <comment xml:lang="ru">Документ AportisDoc</comment>
+ <comment xml:lang="sk">Dokument AportisDoc</comment>
+ <comment xml:lang="sl">Dokument AportisDoc</comment>
+ <comment xml:lang="sr">Апортис Док документ</comment>
+ <comment xml:lang="sv">AportisDoc-dokument</comment>
+ <comment xml:lang="tr">AportisDoc belgesi</comment>
+ <comment xml:lang="uk">документ AportisDoc</comment>
+ <comment xml:lang="vi">Tài liệu AportisDoc</comment>
+ <comment xml:lang="zh_CN">AportisDoc 文档</comment>
+ <comment xml:lang="zh_TW">AportisDoc 文件</comment>
+ <sub-class-of type="application/vnd.palm"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="TEXtREAd" type="string" offset="60"/>
+ <match value="TEXtTlDc" type="string" offset="60"/>
+ </magic>
+ <glob pattern="*.pdb"/>
+ <glob pattern="*.pdc"/>
+ </mime-type>
+ <mime-type type="application/x-applix-spreadsheet">
+ <comment>Applix Spreadsheets spreadsheet</comment>
+ <comment xml:lang="ar">جداول بيانات Applix</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš Applix Spreadsheets</comment>
+ <comment xml:lang="bg">Таблица — Applix Spreadsheets</comment>
+ <comment xml:lang="ca">full de càlcul d'Applix Spreadsheets</comment>
+ <comment xml:lang="cs">sešit Applix Spreadsheets</comment>
+ <comment xml:lang="da">Applix Spreadsheets-regneark</comment>
+ <comment xml:lang="de">Applix-Spreadsheets-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο Applix Spreadsheets</comment>
+ <comment xml:lang="en_GB">Applix Spreadsheets spreadsheet</comment>
+ <comment xml:lang="eo">sterntabelo de Applix Spreadsheets</comment>
+ <comment xml:lang="es">hoja de cálculo de Applix Spreadsheets</comment>
+ <comment xml:lang="eu">Applix Spreadsheets kalkulu-orria</comment>
+ <comment xml:lang="fi">Applix Spreadsheets -taulukko</comment>
+ <comment xml:lang="fo">Applix Spreadsheets rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul Applix</comment>
+ <comment xml:lang="ga">scarbhileog Applix Spreadsheets</comment>
+ <comment xml:lang="gl">folla de cálculo de Applix</comment>
+ <comment xml:lang="he">גליון נתונים של Applix Spreadsheets</comment>
+ <comment xml:lang="hr">Applix Spreadsheets proračunska tablica</comment>
+ <comment xml:lang="hu">Applix Spreadsheets-munkafüzet</comment>
+ <comment xml:lang="ia">Folio de calculo Applix Spreadsheets</comment>
+ <comment xml:lang="id">Lembar sebar Applix Spreadsheets</comment>
+ <comment xml:lang="it">Foglio di calcolo Applix Spreadsheets</comment>
+ <comment xml:lang="ja">Applix Spreadsheets スプレッドシート</comment>
+ <comment xml:lang="ka">Applix Spreadsheets-ის ცხრილი</comment>
+ <comment xml:lang="kk">Applix Spreadsheets электрондық кестесі</comment>
+ <comment xml:lang="ko">Applix 스프레드시트</comment>
+ <comment xml:lang="lt">Applix Spreadsheets skaičialentė</comment>
+ <comment xml:lang="lv">Applix Spreadsheets izklājlapa</comment>
+ <comment xml:lang="ms">Hamparan Applix Spreadsheets</comment>
+ <comment xml:lang="nb">Applix Spreadsheets-regneark</comment>
+ <comment xml:lang="nl">Applix Spreadsheets-rekenblad</comment>
+ <comment xml:lang="nn">Applix Spreadsheets-dokument</comment>
+ <comment xml:lang="oc">fuèlh de calcul Applix</comment>
+ <comment xml:lang="pl">Arkusz Applix Spreadsheets</comment>
+ <comment xml:lang="pt">folha de cálculo Applix Spreadsheets</comment>
+ <comment xml:lang="pt_BR">Planilha do Applix Spreadsheets</comment>
+ <comment xml:lang="ro">Foaie de calcul Applix</comment>
+ <comment xml:lang="ru">Электронная таблица Applix Spreadsheets</comment>
+ <comment xml:lang="sk">Zošit Applix Spreadsheets</comment>
+ <comment xml:lang="sl">Razpredelnica Applix Spreadsheets</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh Applix Spreadsheets</comment>
+ <comment xml:lang="sr">документ Апликсове Табеле</comment>
+ <comment xml:lang="sv">Applix Spreadsheets-kalkylblad</comment>
+ <comment xml:lang="tr">Applix Spreadsheets çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця Applix Spreadsheets</comment>
+ <comment xml:lang="vi">Bảng tính Applix Spreadsheets</comment>
+ <comment xml:lang="zh_CN">Applix Spreadsheets 电子表格</comment>
+ <comment xml:lang="zh_TW">Applix Spreadsheets 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="50">
+ <match value="*BEGIN SPREADSHEETS" type="string" offset="0"/>
+ <match value="*BEGIN" type="string" offset="0">
+ <match value="SPREADSHEETS" type="string" offset="7"/>
+ </match>
+ </magic>
+ <glob pattern="*.as"/>
+ </mime-type>
+ <mime-type type="application/x-applix-word">
+ <comment>Applix Words document</comment>
+ <comment xml:lang="ar">مستند كلمات Applix</comment>
+ <comment xml:lang="ast">Documentu d'Applix Words</comment>
+ <comment xml:lang="az">Applix Words sənədi</comment>
+ <comment xml:lang="be@latin">Dakument Applix Words</comment>
+ <comment xml:lang="bg">Документ — Applix Words</comment>
+ <comment xml:lang="ca">document d'Applix Words</comment>
+ <comment xml:lang="cs">dokument Applix Words</comment>
+ <comment xml:lang="cy">Dogfen Applix Words</comment>
+ <comment xml:lang="da">Applix Words-dokument</comment>
+ <comment xml:lang="de">Applix-Words-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Applix Words</comment>
+ <comment xml:lang="en_GB">Applix Words document</comment>
+ <comment xml:lang="eo">dokumento de Applix Words</comment>
+ <comment xml:lang="es">documento de Applix Words</comment>
+ <comment xml:lang="eu">Applix Words dokumentua</comment>
+ <comment xml:lang="fi">Applix Words -asiakirja</comment>
+ <comment xml:lang="fo">Applix Words skjal</comment>
+ <comment xml:lang="fr">document Applix Words</comment>
+ <comment xml:lang="ga">cáipéis Applix Words</comment>
+ <comment xml:lang="gl">documento de Applix Words</comment>
+ <comment xml:lang="he">מסמך של Applix Words</comment>
+ <comment xml:lang="hr">Applix Words dokument</comment>
+ <comment xml:lang="hu">Applix Words-dokumentum</comment>
+ <comment xml:lang="ia">Documento Applix Words</comment>
+ <comment xml:lang="id">Dokumen Applix Words</comment>
+ <comment xml:lang="it">Documento Applix Words</comment>
+ <comment xml:lang="ja">Applix Words ドキュメント</comment>
+ <comment xml:lang="ka">Applix Words-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">Applix Words құжаты</comment>
+ <comment xml:lang="ko">Applix Words 문서</comment>
+ <comment xml:lang="lt">Applix Words dokumentas</comment>
+ <comment xml:lang="lv">Applix Words dokuments</comment>
+ <comment xml:lang="ms">Dokumen Perkataan Applix</comment>
+ <comment xml:lang="nb">Applix Words-dokument</comment>
+ <comment xml:lang="nl">Applix Words-document</comment>
+ <comment xml:lang="nn">Applix Words dokument</comment>
+ <comment xml:lang="oc">document Applix Words</comment>
+ <comment xml:lang="pl">Dokument Applix Words</comment>
+ <comment xml:lang="pt">documento Applix Words</comment>
+ <comment xml:lang="pt_BR">Documento do Applix Words</comment>
+ <comment xml:lang="ro">Document Applix Words</comment>
+ <comment xml:lang="ru">Документ Applix Words</comment>
+ <comment xml:lang="sk">Dokument Applix Words</comment>
+ <comment xml:lang="sl">Dokument Applix Words</comment>
+ <comment xml:lang="sq">Dokument Applix Words</comment>
+ <comment xml:lang="sr">документ Апликсових Речи</comment>
+ <comment xml:lang="sv">Applix Words-dokument</comment>
+ <comment xml:lang="tr">Applix Words belgesi</comment>
+ <comment xml:lang="uk">документ Applix Words</comment>
+ <comment xml:lang="vi">Tài liệu Applix Words</comment>
+ <comment xml:lang="zh_CN">Applix Words 文档</comment>
+ <comment xml:lang="zh_TW">Applix Words 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="*BEGIN" type="string" offset="0">
+ <match value="WORDS" type="string" offset="7"/>
+ </match>
+ </magic>
+ <glob pattern="*.aw"/>
+ </mime-type>
+ <mime-type type="application/x-arc">
+ <comment>ARC archive</comment>
+ <comment xml:lang="ar">أرشيف ARC</comment>
+ <comment xml:lang="be@latin">Archiŭ ARC</comment>
+ <comment xml:lang="bg">Архив — ARC</comment>
+ <comment xml:lang="ca">arxiu ARC</comment>
+ <comment xml:lang="cs">archiv ARC</comment>
+ <comment xml:lang="da">ARC-arkiv</comment>
+ <comment xml:lang="de">ARC-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο ARC</comment>
+ <comment xml:lang="en_GB">ARC archive</comment>
+ <comment xml:lang="eo">ARC-arkivo</comment>
+ <comment xml:lang="es">archivador ARC</comment>
+ <comment xml:lang="eu">ARC artxiboa</comment>
+ <comment xml:lang="fi">ARC-arkisto</comment>
+ <comment xml:lang="fo">ARC skjalasavn</comment>
+ <comment xml:lang="fr">archive ARC</comment>
+ <comment xml:lang="ga">cartlann ARC</comment>
+ <comment xml:lang="gl">arquivo ARC</comment>
+ <comment xml:lang="he">ארכיון ARC</comment>
+ <comment xml:lang="hr">ARC arhiva</comment>
+ <comment xml:lang="hu">ARC-archívum</comment>
+ <comment xml:lang="ia">Archivo ARC</comment>
+ <comment xml:lang="id">Arsip ARC</comment>
+ <comment xml:lang="it">Archivio ARC</comment>
+ <comment xml:lang="ja">ARC アーカイブ</comment>
+ <comment xml:lang="ka">ARC არქივი</comment>
+ <comment xml:lang="kk">ARC архиві</comment>
+ <comment xml:lang="ko">ARC 압축 파일</comment>
+ <comment xml:lang="lt">ARC archyvas</comment>
+ <comment xml:lang="lv">ARC arhīvs</comment>
+ <comment xml:lang="nb">ARC-arkiv</comment>
+ <comment xml:lang="nl">ARC-archief</comment>
+ <comment xml:lang="nn">ARC-arkiv</comment>
+ <comment xml:lang="oc">archiu ARC</comment>
+ <comment xml:lang="pl">Archiwum ARC</comment>
+ <comment xml:lang="pt">arquivo ARC</comment>
+ <comment xml:lang="pt_BR">Pacote ARC</comment>
+ <comment xml:lang="ro">Arhivă ARC</comment>
+ <comment xml:lang="ru">Архив ARC</comment>
+ <comment xml:lang="sk">Archív ARC</comment>
+ <comment xml:lang="sl">Datoteka arhiva ARC</comment>
+ <comment xml:lang="sq">Arkiv ARC</comment>
+ <comment xml:lang="sr">АРЦ архива</comment>
+ <comment xml:lang="sv">ARC-arkiv</comment>
+ <comment xml:lang="tr">ARC arşivi</comment>
+ <comment xml:lang="uk">архів ARC</comment>
+ <comment xml:lang="vi">Kho nén ARC</comment>
+ <comment xml:lang="zh_CN">ARC 归档文件</comment>
+ <comment xml:lang="zh_TW">ARC 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="0x0000081a" type="little32" offset="0" mask="0x8080ffff"/>
+ <match value="0x0000091a" type="little32" offset="0" mask="0x8080ffff"/>
+ <match value="0x0000021a" type="little32" offset="0" mask="0x8080ffff"/>
+ <match value="0x0000031a" type="little32" offset="0" mask="0x8080ffff"/>
+ <match value="0x0000041a" type="little32" offset="0" mask="0x8080ffff"/>
+ <match value="0x0000061a" type="little32" offset="0" mask="0x8080ffff"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-archive">
+ <comment>AR archive</comment>
+ <comment xml:lang="ar">أرشيف AR</comment>
+ <comment xml:lang="be@latin">Archiŭ AR</comment>
+ <comment xml:lang="bg">Архив — AR</comment>
+ <comment xml:lang="ca">arxiu AR</comment>
+ <comment xml:lang="cs">archiv AR</comment>
+ <comment xml:lang="da">AR-arkiv</comment>
+ <comment xml:lang="de">AR-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο AR</comment>
+ <comment xml:lang="en_GB">AR archive</comment>
+ <comment xml:lang="eo">AR-arkivo</comment>
+ <comment xml:lang="es">archivador AR</comment>
+ <comment xml:lang="eu">AR artxiboa</comment>
+ <comment xml:lang="fi">AR-arkisto</comment>
+ <comment xml:lang="fo">AR skjalasavn</comment>
+ <comment xml:lang="fr">archive AR</comment>
+ <comment xml:lang="ga">cartlann AR</comment>
+ <comment xml:lang="gl">arquivo AR</comment>
+ <comment xml:lang="he">ארכיון AR</comment>
+ <comment xml:lang="hr">AR arhiva</comment>
+ <comment xml:lang="hu">AR-archívum</comment>
+ <comment xml:lang="ia">Archivo AR</comment>
+ <comment xml:lang="id">Arsip AR</comment>
+ <comment xml:lang="it">Archivio AR</comment>
+ <comment xml:lang="ja">AR アーカイブ</comment>
+ <comment xml:lang="ka">AR არქივი</comment>
+ <comment xml:lang="kk">AR архиві</comment>
+ <comment xml:lang="ko">AR 묶음 파일</comment>
+ <comment xml:lang="lt">AR archyvas</comment>
+ <comment xml:lang="lv">AR arhīvs</comment>
+ <comment xml:lang="ms">Arkib AR</comment>
+ <comment xml:lang="nb">AR-arkiv</comment>
+ <comment xml:lang="nl">AR-archief</comment>
+ <comment xml:lang="nn">AR-arkiv</comment>
+ <comment xml:lang="oc">archiu AR</comment>
+ <comment xml:lang="pl">Archiwum AR</comment>
+ <comment xml:lang="pt">arquivo AR</comment>
+ <comment xml:lang="pt_BR">Pacote AR</comment>
+ <comment xml:lang="ro">Arhivă AR</comment>
+ <comment xml:lang="ru">Архив AR</comment>
+ <comment xml:lang="sk">Archív AR</comment>
+ <comment xml:lang="sl">Datoteka arhiva AR</comment>
+ <comment xml:lang="sq">Arkiv AR</comment>
+ <comment xml:lang="sr">АР архива</comment>
+ <comment xml:lang="sv">AR-arkiv</comment>
+ <comment xml:lang="tr">AR arşivi</comment>
+ <comment xml:lang="uk">архів AR</comment>
+ <comment xml:lang="vi">Kho nén AR</comment>
+ <comment xml:lang="zh_CN">AR 归档文件</comment>
+ <comment xml:lang="zh_TW">AR 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="45">
+ <match value="&lt;ar&gt;" type="string" offset="0"/>
+ <match value="!&lt;arch&gt;" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.a"/>
+ <glob pattern="*.ar"/>
+ </mime-type>
+ <mime-type type="application/x-arj">
+ <comment>ARJ archive</comment>
+ <comment xml:lang="ar">أرشيف ARJ</comment>
+ <comment xml:lang="az">ARJ arxivi</comment>
+ <comment xml:lang="be@latin">Archiŭ ARJ</comment>
+ <comment xml:lang="bg">Архив — ARJ</comment>
+ <comment xml:lang="ca">arxiu ARJ</comment>
+ <comment xml:lang="cs">archiv ARJ</comment>
+ <comment xml:lang="cy">Archif ARJ</comment>
+ <comment xml:lang="da">ARJ-arkiv</comment>
+ <comment xml:lang="de">ARJ-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο ARJ</comment>
+ <comment xml:lang="en_GB">ARJ archive</comment>
+ <comment xml:lang="eo">ARJ-arkivo</comment>
+ <comment xml:lang="es">archivador ARJ</comment>
+ <comment xml:lang="eu">ARJ artxiboa</comment>
+ <comment xml:lang="fi">ARJ-arkisto</comment>
+ <comment xml:lang="fo">ARJ skjalasavn</comment>
+ <comment xml:lang="fr">archive ARJ</comment>
+ <comment xml:lang="ga">cartlann ARJ</comment>
+ <comment xml:lang="gl">arquivo ARJ</comment>
+ <comment xml:lang="he">ארכיון ARJ</comment>
+ <comment xml:lang="hr">ARJ arhiva</comment>
+ <comment xml:lang="hu">ARJ-archívum</comment>
+ <comment xml:lang="ia">Archivo ARJ</comment>
+ <comment xml:lang="id">Arsip ARJ</comment>
+ <comment xml:lang="it">Archivio ARJ</comment>
+ <comment xml:lang="ja">ARJ アーカイブ</comment>
+ <comment xml:lang="ka">ARJ არქივი</comment>
+ <comment xml:lang="kk">ARJ архиві</comment>
+ <comment xml:lang="ko">ARJ 압축 파일</comment>
+ <comment xml:lang="lt">ARJ archyvas</comment>
+ <comment xml:lang="lv">ARJ arhīvs</comment>
+ <comment xml:lang="ms">Arkib ARJ</comment>
+ <comment xml:lang="nb">ARJ-arkiv</comment>
+ <comment xml:lang="nl">ARJ-archief</comment>
+ <comment xml:lang="nn">ARJ-arkiv</comment>
+ <comment xml:lang="oc">archiu ARJ</comment>
+ <comment xml:lang="pl">Archiwum ARJ</comment>
+ <comment xml:lang="pt">arquivo ARJ</comment>
+ <comment xml:lang="pt_BR">Pacote ARJ</comment>
+ <comment xml:lang="ro">Arhivă ARJ</comment>
+ <comment xml:lang="ru">Архив ARJ</comment>
+ <comment xml:lang="sk">Archív ARJ</comment>
+ <comment xml:lang="sl">Datoteka arhiva ARJ</comment>
+ <comment xml:lang="sq">Arkiv ARJ</comment>
+ <comment xml:lang="sr">АРЈ архива</comment>
+ <comment xml:lang="sv">ARJ-arkiv</comment>
+ <comment xml:lang="tr">ARJ arşivi</comment>
+ <comment xml:lang="uk">архів ARJ</comment>
+ <comment xml:lang="vi">Kho nén ARJ</comment>
+ <comment xml:lang="zh_CN">ARJ 归档文件</comment>
+ <comment xml:lang="zh_TW">ARJ 封存檔</comment>
+ <acronym>ARJ</acronym>
+ <expanded-acronym>Archived by Robert Jung</expanded-acronym>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="0xea60" type="little16" offset="0"/>
+ </magic>
+ <glob pattern="*.arj"/>
+ </mime-type>
+ <mime-type type="application/x-asp">
+ <comment>ASP page</comment>
+ <comment xml:lang="ar">صفحة ASP</comment>
+ <comment xml:lang="ast">Páxina ASP</comment>
+ <comment xml:lang="be@latin">Staronka ASP</comment>
+ <comment xml:lang="bg">Страница — ASP</comment>
+ <comment xml:lang="ca">pàgina ASP</comment>
+ <comment xml:lang="cs">stránka ASP</comment>
+ <comment xml:lang="da">ASP-side</comment>
+ <comment xml:lang="de">ASP-Seite</comment>
+ <comment xml:lang="el">Σελίδα ASP</comment>
+ <comment xml:lang="en_GB">ASP page</comment>
+ <comment xml:lang="eo">ASP-paĝo</comment>
+ <comment xml:lang="es">página ASP</comment>
+ <comment xml:lang="eu">ASP orria</comment>
+ <comment xml:lang="fi">ASP-sivu</comment>
+ <comment xml:lang="fo">ASP síða</comment>
+ <comment xml:lang="fr">page ASP</comment>
+ <comment xml:lang="ga">leathanach ASP</comment>
+ <comment xml:lang="gl">páxina ASP</comment>
+ <comment xml:lang="he">עמוד ASP</comment>
+ <comment xml:lang="hr">ASP stranica</comment>
+ <comment xml:lang="hu">ASP oldal</comment>
+ <comment xml:lang="ia">Pagina ASP</comment>
+ <comment xml:lang="id">Halaman ASP</comment>
+ <comment xml:lang="it">Pagina ASP</comment>
+ <comment xml:lang="ja">ASP ページ</comment>
+ <comment xml:lang="ka">ASP გვერდი</comment>
+ <comment xml:lang="kk">ASP парағы</comment>
+ <comment xml:lang="ko">ASP 페이지</comment>
+ <comment xml:lang="lt">ASP puslapis</comment>
+ <comment xml:lang="lv">ASP lapa</comment>
+ <comment xml:lang="nb">ASP-side</comment>
+ <comment xml:lang="nl">ASP-pagina</comment>
+ <comment xml:lang="nn">ASP-side</comment>
+ <comment xml:lang="oc">pagina ASP</comment>
+ <comment xml:lang="pl">Strona ASP</comment>
+ <comment xml:lang="pt">página ASP</comment>
+ <comment xml:lang="pt_BR">Página ASP</comment>
+ <comment xml:lang="ro">Pagină ASP</comment>
+ <comment xml:lang="ru">Страница ASP</comment>
+ <comment xml:lang="sk">Stránka ASP</comment>
+ <comment xml:lang="sl">Datoteka spletne strani ASP</comment>
+ <comment xml:lang="sq">Faqe ASP</comment>
+ <comment xml:lang="sr">АСП страница</comment>
+ <comment xml:lang="sv">ASP-sida</comment>
+ <comment xml:lang="tr">ASP sayfası</comment>
+ <comment xml:lang="uk">сторінка ASP</comment>
+ <comment xml:lang="vi">Trang ASP</comment>
+ <comment xml:lang="zh_CN">ASP 页面</comment>
+ <comment xml:lang="zh_TW">ASP 頁面</comment>
+ <acronym>ASP</acronym>
+ <expanded-acronym>Active Server Page</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.asp"/>
+ </mime-type>
+ <mime-type type="application/x-awk">
+ <comment>AWK script</comment>
+ <comment xml:lang="ar">سكربت AWK</comment>
+ <comment xml:lang="ast">Script AWK</comment>
+ <comment xml:lang="az">AWK skripti</comment>
+ <comment xml:lang="be@latin">Skrypt AWK</comment>
+ <comment xml:lang="bg">Скрипт — AWK</comment>
+ <comment xml:lang="ca">script AWK</comment>
+ <comment xml:lang="cs">skript AWK</comment>
+ <comment xml:lang="cy">Sgript AWK</comment>
+ <comment xml:lang="da">AWK-program</comment>
+ <comment xml:lang="de">AWK-Skript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών AWK</comment>
+ <comment xml:lang="en_GB">AWK script</comment>
+ <comment xml:lang="eo">AWK-skripto</comment>
+ <comment xml:lang="es">secuencia de órdenes en AWK</comment>
+ <comment xml:lang="eu">AWK script-a</comment>
+ <comment xml:lang="fi">AWK-komentotiedosto</comment>
+ <comment xml:lang="fo">AWK boðrøð</comment>
+ <comment xml:lang="fr">script AWK</comment>
+ <comment xml:lang="ga">script AWK</comment>
+ <comment xml:lang="gl">script de AWK</comment>
+ <comment xml:lang="he">תסריט AWK</comment>
+ <comment xml:lang="hr">AWK skripta</comment>
+ <comment xml:lang="hu">AWK-parancsfájl</comment>
+ <comment xml:lang="ia">Script AWK</comment>
+ <comment xml:lang="id">Skrip AWK</comment>
+ <comment xml:lang="it">Script AWK</comment>
+ <comment xml:lang="ja">AWK スクリプト</comment>
+ <comment xml:lang="ka">AWK სცენარი</comment>
+ <comment xml:lang="kk">AWK сценарийі</comment>
+ <comment xml:lang="ko">AWK 스크립트</comment>
+ <comment xml:lang="lt">AWK scenarijus</comment>
+ <comment xml:lang="lv">AWK skripts</comment>
+ <comment xml:lang="ms">Skrip AWK</comment>
+ <comment xml:lang="nb">AWK-skript</comment>
+ <comment xml:lang="nl">AWK-script</comment>
+ <comment xml:lang="nn">WAK-skript</comment>
+ <comment xml:lang="oc">escript AWK</comment>
+ <comment xml:lang="pl">Skrypt AWK</comment>
+ <comment xml:lang="pt">script AWK</comment>
+ <comment xml:lang="pt_BR">Script AWK</comment>
+ <comment xml:lang="ro">Script AWK</comment>
+ <comment xml:lang="ru">Сценарий AWK</comment>
+ <comment xml:lang="sk">Skript AWK</comment>
+ <comment xml:lang="sl">Skriptna datoteka AWK</comment>
+ <comment xml:lang="sq">Script AWK</comment>
+ <comment xml:lang="sr">АВК скрипта</comment>
+ <comment xml:lang="sv">AWK-skript</comment>
+ <comment xml:lang="tr">AWK betiği</comment>
+ <comment xml:lang="uk">скрипт AWK</comment>
+ <comment xml:lang="vi">Văn lệnh AWK</comment>
+ <comment xml:lang="zh_CN">AWK 脚本</comment>
+ <comment xml:lang="zh_TW">AWK 指令稿</comment>
+ <sub-class-of type="application/x-executable"/>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-script"/>
+ <magic priority="50">
+ <match value="#!/bin/gawk" type="string" offset="0"/>
+ <match value="#! /bin/gawk" type="string" offset="0"/>
+ <match value="#!/usr/bin/gawk" type="string" offset="0"/>
+ <match value="#! /usr/bin/gawk" type="string" offset="0"/>
+ <match value="#!/usr/local/bin/gawk" type="string" offset="0"/>
+ <match value="#! /usr/local/bin/gawk" type="string" offset="0"/>
+ <match value="#!/bin/awk" type="string" offset="0"/>
+ <match value="#! /bin/awk" type="string" offset="0"/>
+ <match value="#!/usr/bin/awk" type="string" offset="0"/>
+ <match value="#! /usr/bin/awk" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.awk"/>
+ </mime-type>
+ <mime-type type="application/x-bcpio">
+ <comment>BCPIO document</comment>
+ <comment xml:lang="ar">مستند BCPIO</comment>
+ <comment xml:lang="ast">Documentu BCPIO</comment>
+ <comment xml:lang="az">BCPIO sənədi</comment>
+ <comment xml:lang="be@latin">Dakument BCPIO</comment>
+ <comment xml:lang="bg">Документ — BCPIO</comment>
+ <comment xml:lang="ca">document BCPIO</comment>
+ <comment xml:lang="cs">dokument BCPIO</comment>
+ <comment xml:lang="cy">Dogfen BCPIO</comment>
+ <comment xml:lang="da">BCPIO-dokument</comment>
+ <comment xml:lang="de">BCPIO-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο BCPIO</comment>
+ <comment xml:lang="en_GB">BCPIO document</comment>
+ <comment xml:lang="eo">BCPIO-dokumento</comment>
+ <comment xml:lang="es">documento BCPIO</comment>
+ <comment xml:lang="eu">BCPIO dokumentua</comment>
+ <comment xml:lang="fi">BCPIO-asiakirja</comment>
+ <comment xml:lang="fo">BCPIO skjal</comment>
+ <comment xml:lang="fr">document BCPIO</comment>
+ <comment xml:lang="ga">cáipéis BCPIO</comment>
+ <comment xml:lang="gl">documento BCPIO</comment>
+ <comment xml:lang="he">מסמך של BCPO</comment>
+ <comment xml:lang="hr">BCPIO dokument</comment>
+ <comment xml:lang="hu">BCPIO-dokumentum</comment>
+ <comment xml:lang="ia">Documento BCPIO</comment>
+ <comment xml:lang="id">Dokumen BCPIO</comment>
+ <comment xml:lang="it">Documento BCPIO</comment>
+ <comment xml:lang="ja">BCPIO ドキュメント</comment>
+ <comment xml:lang="ka">BCPIO-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">BCPIO құжаты</comment>
+ <comment xml:lang="ko">BCPIO 문서</comment>
+ <comment xml:lang="lt">BCPIO dokumentas</comment>
+ <comment xml:lang="lv">BCPIO dokuments</comment>
+ <comment xml:lang="ms">Dokumen BCPIO</comment>
+ <comment xml:lang="nb">BCPIO-dokument</comment>
+ <comment xml:lang="nl">BCPIO-document</comment>
+ <comment xml:lang="nn">BCPIO-dokument</comment>
+ <comment xml:lang="oc">document BCPIO</comment>
+ <comment xml:lang="pl">Dokument BCPIO</comment>
+ <comment xml:lang="pt">documento BCPIO</comment>
+ <comment xml:lang="pt_BR">Documento BCPIO</comment>
+ <comment xml:lang="ro">Document BCPIO</comment>
+ <comment xml:lang="ru">Документ BCPIO</comment>
+ <comment xml:lang="sk">Dokument BCPIO</comment>
+ <comment xml:lang="sl">Dokument BCPIO</comment>
+ <comment xml:lang="sq">Dokument BCPIO</comment>
+ <comment xml:lang="sr">БЦПИО документ</comment>
+ <comment xml:lang="sv">BCPIO-dokument</comment>
+ <comment xml:lang="tr">BCPIO belgesi</comment>
+ <comment xml:lang="uk">документ BCPIO</comment>
+ <comment xml:lang="vi">Tài liệu BCPIO</comment>
+ <comment xml:lang="zh_CN">BCPIO 文档</comment>
+ <comment xml:lang="zh_TW">BCPIO 文件</comment>
+ <acronym>BCPIO</acronym>
+ <expanded-acronym>Binary CPIO</expanded-acronym>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.bcpio"/>
+ </mime-type>
+ <mime-type type="application/x-bittorrent">
+ <comment>BitTorrent seed file</comment>
+ <comment xml:lang="ar">ملف باذر البت تورنت</comment>
+ <comment xml:lang="az">BitTorrent seed faylı</comment>
+ <comment xml:lang="be@latin">Fajł krynicy BitTorrent</comment>
+ <comment xml:lang="bg">Файл-източник — BitTorrent</comment>
+ <comment xml:lang="ca">fitxer de llavor BitTorrent</comment>
+ <comment xml:lang="cs">soubor BitTorrent</comment>
+ <comment xml:lang="cy">Ffeil hadu BitTorrent</comment>
+ <comment xml:lang="da">BitTorrent-frøfil</comment>
+ <comment xml:lang="de">BitTorrent-Seed-Datei</comment>
+ <comment xml:lang="el">Αρχείο BitTorrent seed</comment>
+ <comment xml:lang="en_GB">BitTorrent seed file</comment>
+ <comment xml:lang="eo">BitTorrent-semdosiero</comment>
+ <comment xml:lang="es">archivo semilla de BitTorrent</comment>
+ <comment xml:lang="eu">BitTorrent hazi-fitxategia</comment>
+ <comment xml:lang="fi">BitTorrent-siementiedosto</comment>
+ <comment xml:lang="fo">BitTorrent seed fíla</comment>
+ <comment xml:lang="fr">fichier graine BitTorrent</comment>
+ <comment xml:lang="ga">comhad síl BitTorrent</comment>
+ <comment xml:lang="gl">ficheiro de orixe BitTorrent</comment>
+ <comment xml:lang="he">קובץ זריעה של BitTorrent</comment>
+ <comment xml:lang="hr">BitTorrent datoteka</comment>
+ <comment xml:lang="hu">BitTorrent-magfájl</comment>
+ <comment xml:lang="ia">File seminal de BitTorrent</comment>
+ <comment xml:lang="id">Berkas benih BitTorrent</comment>
+ <comment xml:lang="it">File seed BitTorrent</comment>
+ <comment xml:lang="ja">BitTorrent シードファイル</comment>
+ <comment xml:lang="kk">BitTorrent көз файлы</comment>
+ <comment xml:lang="ko">비트토렌트 시드 파일</comment>
+ <comment xml:lang="lt">BitTorrent šaltinio failas</comment>
+ <comment xml:lang="lv">BitTorrent avota datne</comment>
+ <comment xml:lang="ms">Fail seed BitTorrent</comment>
+ <comment xml:lang="nb">Fil med utgangsverdi for BitTorrent</comment>
+ <comment xml:lang="nl">BitTorrent-bestand</comment>
+ <comment xml:lang="nn">Nedlastingsfil for BitTorrent</comment>
+ <comment xml:lang="oc">fichièr grana BitTorrent</comment>
+ <comment xml:lang="pl">Plik ziarna BitTorrent</comment>
+ <comment xml:lang="pt">ficheiro de semente BitTorrent</comment>
+ <comment xml:lang="pt_BR">Arquivo semente BitTorrent</comment>
+ <comment xml:lang="ro">Fișier sursă-completă BitTorrent</comment>
+ <comment xml:lang="ru">Файл источника BitTorrent</comment>
+ <comment xml:lang="sk">Súbor BitTorrent</comment>
+ <comment xml:lang="sl">Datoteka sejanja BitTorrent</comment>
+ <comment xml:lang="sq">File bazë BitTorrent</comment>
+ <comment xml:lang="sr">датотека сејача Бит Торента</comment>
+ <comment xml:lang="sv">BitTorrent-distributionsfil</comment>
+ <comment xml:lang="tr">BitTorrent tohum dosyası</comment>
+ <comment xml:lang="uk">файл поширення BitTorrent</comment>
+ <comment xml:lang="vi">Tải tập hạt BitTorrent</comment>
+ <comment xml:lang="zh_CN">BitTorrent 种子文件</comment>
+ <comment xml:lang="zh_TW">BitTorrent 種子檔</comment>
+ <magic priority="50">
+ <match value="d8:announce" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.torrent"/>
+ </mime-type>
+ <mime-type type="application/x-blender">
+ <comment>Blender scene</comment>
+ <comment xml:lang="ar">مشهد بلندر</comment>
+ <comment xml:lang="ast">Escena de Blender</comment>
+ <comment xml:lang="be@latin">Scena Blender</comment>
+ <comment xml:lang="bg">Сцена — Blender</comment>
+ <comment xml:lang="ca">escena de Blender</comment>
+ <comment xml:lang="cs">scéna Blender</comment>
+ <comment xml:lang="da">Blenderscene</comment>
+ <comment xml:lang="de">Blender-Szene</comment>
+ <comment xml:lang="el">Σκηνή Blender</comment>
+ <comment xml:lang="en_GB">Blender scene</comment>
+ <comment xml:lang="eo">Blender-sceno</comment>
+ <comment xml:lang="es">escena de Blender</comment>
+ <comment xml:lang="eu">Blender-eko fitxategia</comment>
+ <comment xml:lang="fi">Blender-näkymä</comment>
+ <comment xml:lang="fo">Blender leikmynd</comment>
+ <comment xml:lang="fr">scène Blender</comment>
+ <comment xml:lang="ga">radharc Blender</comment>
+ <comment xml:lang="gl">escena de Blender</comment>
+ <comment xml:lang="he">סצנת Blender</comment>
+ <comment xml:lang="hr">Blender scena</comment>
+ <comment xml:lang="hu">Blender-jelenet</comment>
+ <comment xml:lang="ia">Scena Blender</comment>
+ <comment xml:lang="id">Scene Blender</comment>
+ <comment xml:lang="it">Scena Blender</comment>
+ <comment xml:lang="ja">Blender シーン</comment>
+ <comment xml:lang="ka">Blender-ის სცენა</comment>
+ <comment xml:lang="kk">Blender сахнасы</comment>
+ <comment xml:lang="ko">Blender 장면</comment>
+ <comment xml:lang="lt">Blender scena</comment>
+ <comment xml:lang="lv">Blender aina</comment>
+ <comment xml:lang="ms">Babak Blender</comment>
+ <comment xml:lang="nb">Blender-scene</comment>
+ <comment xml:lang="nl">Blender-scène</comment>
+ <comment xml:lang="nn">Blender-scene</comment>
+ <comment xml:lang="oc">scèna Blender</comment>
+ <comment xml:lang="pl">Scena programu Blender</comment>
+ <comment xml:lang="pt">cenário Blender</comment>
+ <comment xml:lang="pt_BR">Cena do Blender</comment>
+ <comment xml:lang="ro">Scenă Blender</comment>
+ <comment xml:lang="ru">Сцена Blender</comment>
+ <comment xml:lang="sk">Scéna Blender</comment>
+ <comment xml:lang="sl">Datoteka scene Blender</comment>
+ <comment xml:lang="sq">Skenë Blender</comment>
+ <comment xml:lang="sr">Блендерова сцена</comment>
+ <comment xml:lang="sv">Blender-scen</comment>
+ <comment xml:lang="tr">Blender sahnesi</comment>
+ <comment xml:lang="uk">сцена Blender</comment>
+ <comment xml:lang="vi">Cảnh Blender</comment>
+ <comment xml:lang="zh_CN">Blender 场景</comment>
+ <comment xml:lang="zh_TW">Blender 場景</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.blender"/>
+ <glob pattern="*.blend"/>
+ <glob pattern="*.BLEND"/>
+ <magic priority="50">
+ <match value="BLENDER" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-bzdvi">
+ <comment>TeX DVI document (bzip-compressed)</comment>
+ <comment xml:lang="ar">مستند TeX DVI (مضغوط-bzip)</comment>
+ <comment xml:lang="ast">Documentu Tex DVI (comprimíu en bzip)</comment>
+ <comment xml:lang="be@latin">Dakument TeX DVI (bzip-skampresavany)</comment>
+ <comment xml:lang="bg">Документ — TeX DVI, компресиран с bzip</comment>
+ <comment xml:lang="ca">document de TeX DVI (amb compressió bzip)</comment>
+ <comment xml:lang="cs">dokument TeX DVI (komprimovaný pomocí bzip)</comment>
+ <comment xml:lang="da">TeX DVI-dokument (bzip-komprimeret)</comment>
+ <comment xml:lang="de">TeX-DVI-Dokument (bzip-komprimiert)</comment>
+ <comment xml:lang="el">Αρχείο TeX DVI (συμπιεσμένο με bzip)</comment>
+ <comment xml:lang="en_GB">TeX DVI document (bzip-compressed)</comment>
+ <comment xml:lang="es">documento DVI de TeX (comprimido con bzip)</comment>
+ <comment xml:lang="eu">TeX DVI dokumentua (bzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">TeX DVI -asiakirja (bzip-pakattu)</comment>
+ <comment xml:lang="fo">TeX DVI skjal (bzip-stappað)</comment>
+ <comment xml:lang="fr">document DVI TeX (compressé bzip)</comment>
+ <comment xml:lang="ga">cáipéis DVI TeX (comhbhrúite le bzip)</comment>
+ <comment xml:lang="gl">documento DVI de TeX (comprimido con bzip)</comment>
+ <comment xml:lang="he">מסמך מסוג TeX DVI (מכווץ ע״י bzip)</comment>
+ <comment xml:lang="hr">TeX DVI dokument (bzip sažeto)</comment>
+ <comment xml:lang="hu">TeX DVI dokumentum (bzip-pel tömörítve)</comment>
+ <comment xml:lang="ia">Documento TeX DVI (comprimite con bzip)</comment>
+ <comment xml:lang="id">Dokumen TeX DVI (terkompresi bzip)</comment>
+ <comment xml:lang="it">Documento TeX DVI (compresso con bzip)</comment>
+ <comment xml:lang="ja">Tex DVI ドキュメント (bzip 圧縮)</comment>
+ <comment xml:lang="kk">TeX DVI құжаты (bzip-пен сығылған)</comment>
+ <comment xml:lang="ko">TeX DVI 문서(BZIP 압축)</comment>
+ <comment xml:lang="lt">TeX DVI dokumentas (suglaudintas su bzip)</comment>
+ <comment xml:lang="lv">TeX DVI dokuments (saspiests ar bzip)</comment>
+ <comment xml:lang="nb">TeX DVI-dokument (bzip-komprimert)</comment>
+ <comment xml:lang="nl">TeX DVI-document (ingepakt met bzip)</comment>
+ <comment xml:lang="nn">TeX DVI-dokument (pakka med bzip)</comment>
+ <comment xml:lang="oc">document DVI TeX (compressat bzip)</comment>
+ <comment xml:lang="pl">Dokument TeX DVI (kompresja bzip)</comment>
+ <comment xml:lang="pt">documento TeX DVI (compressão bzip)</comment>
+ <comment xml:lang="pt_BR">Documento DVI TeX (compactado com bzip)</comment>
+ <comment xml:lang="ro">Document TeX DVI (comprimat bzip)</comment>
+ <comment xml:lang="ru">Документ TeX DVI (сжатый bzip)</comment>
+ <comment xml:lang="sk">Dokument TeX DVI (komprimovaný pomocou bzip)</comment>
+ <comment xml:lang="sl">Dokument TeX DVI (stisnjen z bzip)</comment>
+ <comment xml:lang="sq">Dokument Tex DVI (i kompresuar me bzip)</comment>
+ <comment xml:lang="sr">ТеКс ДВИ документ (запакована бзипом)</comment>
+ <comment xml:lang="sv">TeX DVI-dokument (bzip-komprimerat)</comment>
+ <comment xml:lang="tr">TeX DVI belgesi (bzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">документ TeX DVI (стиснений bzip)</comment>
+ <comment xml:lang="vi">Tài liệu DVI TeX (đã nén bzip)</comment>
+ <comment xml:lang="zh_CN">TeX DVI 文档(gzip 压缩)</comment>
+ <comment xml:lang="zh_TW">TeX DVI 文件 (bzip 格式壓縮)</comment>
+ <sub-class-of type="application/x-bzip"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.dvi.bz2"/>
+ </mime-type>
+ <mime-type type="application/x-bzip">
+ <comment>Bzip archive</comment>
+ <comment xml:lang="ar">أرشيف Bzip</comment>
+ <comment xml:lang="ast">Archivu Bzip</comment>
+ <comment xml:lang="be@latin">Archiŭ bzip</comment>
+ <comment xml:lang="bg">Архив — bzip</comment>
+ <comment xml:lang="ca">arxiu bzip</comment>
+ <comment xml:lang="cs">archiv bzip</comment>
+ <comment xml:lang="da">Bzip-arkiv</comment>
+ <comment xml:lang="de">Bzip-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Bzip</comment>
+ <comment xml:lang="en_GB">Bzip archive</comment>
+ <comment xml:lang="eo">Bzip-arkivo</comment>
+ <comment xml:lang="es">archivador Bzip</comment>
+ <comment xml:lang="eu">Bzip artxiboa</comment>
+ <comment xml:lang="fi">Bzip-arkisto</comment>
+ <comment xml:lang="fo">Bzip skjalasavn</comment>
+ <comment xml:lang="fr">archive bzip</comment>
+ <comment xml:lang="ga">cartlann Bzip</comment>
+ <comment xml:lang="gl">arquivo Bzip</comment>
+ <comment xml:lang="he">ארכיון Bzip</comment>
+ <comment xml:lang="hr">Bzip arhiva</comment>
+ <comment xml:lang="hu">Bzip archívum</comment>
+ <comment xml:lang="ia">Archivo Bzip</comment>
+ <comment xml:lang="id">Arsip Bzip</comment>
+ <comment xml:lang="it">Archivio bzip</comment>
+ <comment xml:lang="ja">Bzip アーカイブ</comment>
+ <comment xml:lang="ka">Bzip არქივი</comment>
+ <comment xml:lang="kk">Bzip архиві</comment>
+ <comment xml:lang="ko">BZIP 압축 파일</comment>
+ <comment xml:lang="lt">Bzip archyvas</comment>
+ <comment xml:lang="lv">Bzip arhīvs</comment>
+ <comment xml:lang="nb">Bzip-arkiv</comment>
+ <comment xml:lang="nl">Bzip-archief</comment>
+ <comment xml:lang="nn">Bzip-arkiv</comment>
+ <comment xml:lang="oc">archiu bzip</comment>
+ <comment xml:lang="pl">Archiwum bzip</comment>
+ <comment xml:lang="pt">arquivo Bzip</comment>
+ <comment xml:lang="pt_BR">Pacote Bzip</comment>
+ <comment xml:lang="ro">Arhivă Bzip</comment>
+ <comment xml:lang="ru">Архив BZIP</comment>
+ <comment xml:lang="sk">Archív bzip</comment>
+ <comment xml:lang="sl">Datoteka arhiva Bzip</comment>
+ <comment xml:lang="sq">Arkiv bzip</comment>
+ <comment xml:lang="sr">Бзип архива</comment>
+ <comment xml:lang="sv">Bzip-arkiv</comment>
+ <comment xml:lang="tr">Bzip arşivi</comment>
+ <comment xml:lang="uk">архів bzip</comment>
+ <comment xml:lang="vi">Kho nén bzip</comment>
+ <comment xml:lang="zh_CN">Bzip 归档文件</comment>
+ <comment xml:lang="zh_TW">Bzip 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="BZh" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.bz2"/>
+ <glob pattern="*.bz"/>
+ <alias type="application/x-bzip2"/>
+ </mime-type>
+ <mime-type type="application/x-bzip-compressed-tar">
+ <comment>Tar archive (bzip-compressed)</comment>
+ <comment xml:lang="ar">أرشيف Tar (مضغوط-bzip)</comment>
+ <comment xml:lang="ast">Archivu Tar (comprimíu en bzip)</comment>
+ <comment xml:lang="be@latin">Archiŭ tar (bzip-skampresavany)</comment>
+ <comment xml:lang="bg">Архив — tar, компресиран с bzip</comment>
+ <comment xml:lang="ca">arxiu tar (amb compressió bzip)</comment>
+ <comment xml:lang="cs">archiv Tar (komprimovaný pomocí bzip)</comment>
+ <comment xml:lang="da">Tar-arkiv (bzip-komprimeret)</comment>
+ <comment xml:lang="de">Tar-Archiv (bzip-komprimiert)</comment>
+ <comment xml:lang="el">Αρχείο Tar (συμπιεσμένο με bzip)</comment>
+ <comment xml:lang="en_GB">Tar archive (bzip-compressed)</comment>
+ <comment xml:lang="es">archivador Tar (comprimido con bzip)</comment>
+ <comment xml:lang="eu">Tar artxiboa (bzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">Tar-arkisto (bzip-pakattu)</comment>
+ <comment xml:lang="fo">Tar skjalasavn (bzip-stappað)</comment>
+ <comment xml:lang="fr">archive tar (compressée bzip)</comment>
+ <comment xml:lang="ga">cartlann Tar (comhbhrúite le bzip)</comment>
+ <comment xml:lang="gl">arquivo Tar (comprimido con bzip)</comment>
+ <comment xml:lang="he">ארכיון Tar (מכווץ ע״י bzip)</comment>
+ <comment xml:lang="hr">Tar arhiva (bzip sažeto)</comment>
+ <comment xml:lang="hu">Tar archívum (bzip-pel tömörítve)</comment>
+ <comment xml:lang="ia">Archivo Tar (comprimite con bzip)</comment>
+ <comment xml:lang="id">Arsip Tar (terkompresi bzip)</comment>
+ <comment xml:lang="it">Archivio tar (compresso con bzip)</comment>
+ <comment xml:lang="ja">Tar アーカイブ (bzip 圧縮)</comment>
+ <comment xml:lang="kk">Tar архиві (bzip-пен сығылған)</comment>
+ <comment xml:lang="ko">TAR 묶음 파일(BZIP 압축)</comment>
+ <comment xml:lang="lt">Tar archyvas (suglaudintas su bzip)</comment>
+ <comment xml:lang="lv">Tar arhīvs (saspiests ar bzip)</comment>
+ <comment xml:lang="nb">Tar-arkiv (bzip-komprimert)</comment>
+ <comment xml:lang="nl">Tar-archief (ingepakt met bzip)</comment>
+ <comment xml:lang="nn">Tar-arkiv (pakka med bzip)</comment>
+ <comment xml:lang="oc">archiu tar (compressat bzip)</comment>
+ <comment xml:lang="pl">Archiwum tar (kompresja bzip)</comment>
+ <comment xml:lang="pt">arquivo Tar (compressão bzip)</comment>
+ <comment xml:lang="pt_BR">Pacote Tar (compactado com bzip)</comment>
+ <comment xml:lang="ro">Arhivă Tar (comprimată bzip)</comment>
+ <comment xml:lang="ru">Архив TAR (сжатый bzip)</comment>
+ <comment xml:lang="sk">Archív tar (komprimovaný pomocou bzip)</comment>
+ <comment xml:lang="sl">Datoteka arhiva Tar (stisnjen z bzip)</comment>
+ <comment xml:lang="sq">Arkiv tar (i kompresuar me bzip)</comment>
+ <comment xml:lang="sr">Тар архива (запакована бзипом)</comment>
+ <comment xml:lang="sv">Tar-arkiv (bzip-komprimerat)</comment>
+ <comment xml:lang="tr">Tar arşivi (bzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">архів tar (стиснений bzip)</comment>
+ <comment xml:lang="vi">Kho nén tar (đã nén bzip)</comment>
+ <comment xml:lang="zh_CN">Tar 归档文件(bzip 压缩)</comment>
+ <comment xml:lang="zh_TW">Tar 封存檔 (bzip 格式壓縮)</comment>
+ <generic-icon name="package-x-generic"/>
+ <sub-class-of type="application/x-bzip"/>
+ <glob pattern="*.tar.bz2"/>
+ <glob pattern="*.tar.bz"/>
+ <glob pattern="*.tbz2"/>
+ <glob pattern="*.tbz"/>
+ <glob pattern="*.tb2"/>
+ </mime-type>
+ <mime-type type="application/x-bzpdf">
+ <comment>PDF document (bzip-compressed)</comment>
+ <comment xml:lang="ar">مستند PDF (مضغوط-bzip)</comment>
+ <comment xml:lang="ast">Documentu PDF (comprimíu en bzip)</comment>
+ <comment xml:lang="be@latin">Dakument PDF (bzip-skampresavany)</comment>
+ <comment xml:lang="bg">Документ — PDF, компресиран с bzip</comment>
+ <comment xml:lang="ca">document PDF (amb compressió bzip)</comment>
+ <comment xml:lang="cs">dokument PDF (komprimovaný pomocí bzip)</comment>
+ <comment xml:lang="da">PDF-dokument (bzip-komprimeret)</comment>
+ <comment xml:lang="de">PDF-Dokument (bzip-komprimiert)</comment>
+ <comment xml:lang="el">Έγγραφο PDF (συμπιεσμένο με bzip)</comment>
+ <comment xml:lang="en_GB">PDF document (bzip-compressed)</comment>
+ <comment xml:lang="es">documento PDF (comprimido con bzip)</comment>
+ <comment xml:lang="eu">PostScript dokumentua (bzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">PDF-asiakirja (bzip-pakattu)</comment>
+ <comment xml:lang="fo">PDF skjal (bzip-stappað)</comment>
+ <comment xml:lang="fr">document PDF (compressé bzip)</comment>
+ <comment xml:lang="ga">cáipéis PDF (comhbhrúite le bzip)</comment>
+ <comment xml:lang="gl">documento PDF (comprimido en bzip)</comment>
+ <comment xml:lang="he">מסמך PDF (מכווץ ע״י bzip)</comment>
+ <comment xml:lang="hr">PDF dokument (bzip sažet)</comment>
+ <comment xml:lang="hu">PDF dokumentum (bzip-tömörítésű)</comment>
+ <comment xml:lang="ia">Documento PDF (comprimite con bzip)</comment>
+ <comment xml:lang="id">Dokumen PDF (terkompresi bzip)</comment>
+ <comment xml:lang="it">Documento PDF (compresso con bzip)</comment>
+ <comment xml:lang="ja">PDF ドキュメント (bzip 圧縮)</comment>
+ <comment xml:lang="kk">PDF құжаты (bzip-пен сығылған)</comment>
+ <comment xml:lang="ko">PDF 문서(BZIP 압축)</comment>
+ <comment xml:lang="lt">PDF dokumentas (suglaudintas su bzip)</comment>
+ <comment xml:lang="lv">PDF dokuments (saspiests ar bzip)</comment>
+ <comment xml:lang="nb">PDF-dokument (bzip-komprimert)</comment>
+ <comment xml:lang="nl">PDF-document (ingepakt met bzip)</comment>
+ <comment xml:lang="nn">PDF-dokument (pakka med bzip)</comment>
+ <comment xml:lang="oc">document PDF (compressat bzip)</comment>
+ <comment xml:lang="pl">Dokument PDF (kompresja bzip)</comment>
+ <comment xml:lang="pt">documento PDF (compressão bzip)</comment>
+ <comment xml:lang="pt_BR">Documento PDF (compactado com bzip)</comment>
+ <comment xml:lang="ro">Document PDF (comprimat bzip)</comment>
+ <comment xml:lang="ru">Документ PDF (сжатый bzip)</comment>
+ <comment xml:lang="sk">Dokument PDF (komprimovaný pomocou bzip)</comment>
+ <comment xml:lang="sl">Dokument PDF (stisnjen z bzip)</comment>
+ <comment xml:lang="sq">Dokument PDF (i kompresuar me bzip)</comment>
+ <comment xml:lang="sr">ПДФ документ (запакован бзипом)</comment>
+ <comment xml:lang="sv">PDF-dokument (bzip-komprimerat)</comment>
+ <comment xml:lang="tr">PDF belgesi (bzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">документ PDF (стиснений bzip)</comment>
+ <comment xml:lang="vi">Tài liệu PDF (đã nén bzip)</comment>
+ <comment xml:lang="zh_CN">PDF 文档(bzip 压缩)</comment>
+ <comment xml:lang="zh_TW">PDF 文件 (bzip 格式壓縮)</comment>
+ <sub-class-of type="application/x-bzip"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.pdf.bz2"/>
+ </mime-type>
+ <mime-type type="application/x-bzpostscript">
+ <comment>PostScript document (bzip-compressed)</comment>
+ <comment xml:lang="ar">مستند PostScript (مضغوط-bzip)</comment>
+ <comment xml:lang="ast">Documentu PostScript (comprimíu en bzip)</comment>
+ <comment xml:lang="be@latin">Dakument PostScript (bzip-skampresavany)</comment>
+ <comment xml:lang="bg">Документ — PostScript, компресиран с bzip</comment>
+ <comment xml:lang="ca">document PostScript (amb compressió bzip)</comment>
+ <comment xml:lang="cs">dokument PostScript (komprimovaný pomocí bzip)</comment>
+ <comment xml:lang="da">PostScript-dokument (bzip-komprimeret)</comment>
+ <comment xml:lang="de">PostScript-Dokument (bzip-komprimiert)</comment>
+ <comment xml:lang="el">Έγγραφο PostScript (συμπιεσμένο με bzip)</comment>
+ <comment xml:lang="en_GB">PostScript document (bzip-compressed)</comment>
+ <comment xml:lang="es">documento PostScript (comprimido con bzip)</comment>
+ <comment xml:lang="eu">PostScript dokumentua (bzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">PostScript-asiakirja (bzip-pakattu)</comment>
+ <comment xml:lang="fo">PostScript skjal (bzip-stappað)</comment>
+ <comment xml:lang="fr">document PostScript (compressé bzip)</comment>
+ <comment xml:lang="ga">cáipéis PostScript (comhbhrúite le bzip)</comment>
+ <comment xml:lang="gl">documento PostScript (comprimido con bzip)</comment>
+ <comment xml:lang="he">מסמך PostDcript (מכווץ ע״י bzip)</comment>
+ <comment xml:lang="hr">PostScript dokument (bzip sažet)</comment>
+ <comment xml:lang="hu">PostScript dokumentum (bzip-tömörítésű)</comment>
+ <comment xml:lang="ia">Documento PostScript (comprimite con bzip)</comment>
+ <comment xml:lang="id">Dokumen PostScript (terkompresi bzip)</comment>
+ <comment xml:lang="it">Documento PostScript (compresso con bzip)</comment>
+ <comment xml:lang="ja">PostScript ドキュメント (bzip 圧縮)</comment>
+ <comment xml:lang="kk">PostScript құжаты (bzip-пен сығылған)</comment>
+ <comment xml:lang="ko">PostScript 문서(BZIP 압축)</comment>
+ <comment xml:lang="lt">PostScript dokumentas (suglaudintas su bzip)</comment>
+ <comment xml:lang="lv">PostScript dokuments (saspiests ar bzip)</comment>
+ <comment xml:lang="nb">PostScript-dokument (bzip-komprimert)</comment>
+ <comment xml:lang="nl">PostScript-document (ingepakt met bzip)</comment>
+ <comment xml:lang="nn">PostScript-dokument (pakka med bzip)</comment>
+ <comment xml:lang="oc">document PostEscript (compressat bzip)</comment>
+ <comment xml:lang="pl">Dokument Postscript (kompresja bzip)</comment>
+ <comment xml:lang="pt">documento PostScript (compressão bzip)</comment>
+ <comment xml:lang="pt_BR">Documento PostScript (compactado com bzip)</comment>
+ <comment xml:lang="ro">Document PostScript (comprimat bzip)</comment>
+ <comment xml:lang="ru">Документ PostScript (сжатый bzip)</comment>
+ <comment xml:lang="sk">Dokument PostScript (komprimovaný pomocou bzip)</comment>
+ <comment xml:lang="sl">Dokument PostScript (stisnjen z bzip)</comment>
+ <comment xml:lang="sq">Dokument PostScript (i kompresuar me bzip)</comment>
+ <comment xml:lang="sr">Постскрипт документ (запакован бзипом)</comment>
+ <comment xml:lang="sv">Postscript-dokument (bzip-komprimerat)</comment>
+ <comment xml:lang="tr">PostScript belgesi (bzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">документ PostScript (стиснене bzip)</comment>
+ <comment xml:lang="vi">Tài liệu PostScript (đã nén bzip)</comment>
+ <comment xml:lang="zh_CN">PostScript 文档(bzip 压缩)</comment>
+ <comment xml:lang="zh_TW">PostScript 文件 (bzip 格式壓縮)</comment>
+ <sub-class-of type="application/x-bzip"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.ps.bz2"/>
+ </mime-type>
+ <mime-type type="application/vnd.comicbook-rar">
+ <comment>comic book archive</comment>
+ <comment xml:lang="ar">أرشيف comic book</comment>
+ <comment xml:lang="be@latin">archiŭ komiksaŭ</comment>
+ <comment xml:lang="bg">Архив — комикси</comment>
+ <comment xml:lang="ca">arxiu comic book</comment>
+ <comment xml:lang="cs">archiv knihy komiksů</comment>
+ <comment xml:lang="da">comic book-arkiv</comment>
+ <comment xml:lang="de">Comic-Book-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο κόμικ</comment>
+ <comment xml:lang="en_GB">comic book archive</comment>
+ <comment xml:lang="es">archivador de libro de cómic</comment>
+ <comment xml:lang="eu">komiki artxiboa</comment>
+ <comment xml:lang="fi">sarjakuva-arkisto</comment>
+ <comment xml:lang="fo">teknisøgubóka skjalasavn</comment>
+ <comment xml:lang="fr">archive Comic Book</comment>
+ <comment xml:lang="ga">cartlann chartúin</comment>
+ <comment xml:lang="gl">ficheiro de libro de banda deseñada</comment>
+ <comment xml:lang="he">ארכיון ספר קומי</comment>
+ <comment xml:lang="hr">Strip arhiva</comment>
+ <comment xml:lang="hu">képregényarchívum</comment>
+ <comment xml:lang="ia">Archivo Comic Book</comment>
+ <comment xml:lang="id">arsip buku komik</comment>
+ <comment xml:lang="it">Archivio comic book</comment>
+ <comment xml:lang="ja">コミックブックアーカイブ</comment>
+ <comment xml:lang="kk">комикстар архиві</comment>
+ <comment xml:lang="ko">만화책 압축 파일</comment>
+ <comment xml:lang="lt">komiksų knygos archyvas</comment>
+ <comment xml:lang="lv">komiksu grāmatas arhīvs</comment>
+ <comment xml:lang="nb">Tegneseriearkiv</comment>
+ <comment xml:lang="nl">stripboek-archief</comment>
+ <comment xml:lang="nn">teikneseriearkiv</comment>
+ <comment xml:lang="oc">archiu Comic Book</comment>
+ <comment xml:lang="pl">Archiwum komiksu</comment>
+ <comment xml:lang="pt">arquivo de banda desenhada</comment>
+ <comment xml:lang="pt_BR">Pacote de histórias em quadrinhos</comment>
+ <comment xml:lang="ro">arhivă benzi desenate</comment>
+ <comment xml:lang="ru">Архив комиксов</comment>
+ <comment xml:lang="sk">Archív knihy komiksov</comment>
+ <comment xml:lang="sl">Datoteka arhiva stripov</comment>
+ <comment xml:lang="sq">Arkiv comic book</comment>
+ <comment xml:lang="sr">архива стрипа</comment>
+ <comment xml:lang="sv">serietidningsarkiv</comment>
+ <comment xml:lang="tr">çizgi roman arşivi</comment>
+ <comment xml:lang="uk">архів коміксів</comment>
+ <comment xml:lang="vi">Kho nén sách tranh chuyện vui</comment>
+ <comment xml:lang="zh_CN">漫画书归档文件</comment>
+ <comment xml:lang="zh_TW">漫畫書封存檔</comment>
+ <sub-class-of type="application/vnd.rar"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.cbr"/>
+ <alias type="application/x-cbr"/>
+ </mime-type>
+ <mime-type type="application/x-cb7">
+ <comment>comic book archive</comment>
+ <comment xml:lang="ar">أرشيف comic book</comment>
+ <comment xml:lang="be@latin">archiŭ komiksaŭ</comment>
+ <comment xml:lang="bg">Архив — комикси</comment>
+ <comment xml:lang="ca">arxiu comic book</comment>
+ <comment xml:lang="cs">archiv knihy komiksů</comment>
+ <comment xml:lang="da">comic book-arkiv</comment>
+ <comment xml:lang="de">Comic-Book-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο κόμικ</comment>
+ <comment xml:lang="en_GB">comic book archive</comment>
+ <comment xml:lang="es">archivador de libro de cómic</comment>
+ <comment xml:lang="eu">komiki artxiboa</comment>
+ <comment xml:lang="fi">sarjakuva-arkisto</comment>
+ <comment xml:lang="fo">teknisøgubóka skjalasavn</comment>
+ <comment xml:lang="fr">archive Comic Book</comment>
+ <comment xml:lang="ga">cartlann chartúin</comment>
+ <comment xml:lang="gl">ficheiro de libro de banda deseñada</comment>
+ <comment xml:lang="he">ארכיון ספר קומי</comment>
+ <comment xml:lang="hr">Strip arhiva</comment>
+ <comment xml:lang="hu">képregényarchívum</comment>
+ <comment xml:lang="ia">Archivo Comic Book</comment>
+ <comment xml:lang="id">arsip buku komik</comment>
+ <comment xml:lang="it">Archivio comic book</comment>
+ <comment xml:lang="ja">コミックブックアーカイブ</comment>
+ <comment xml:lang="kk">комикстар архиві</comment>
+ <comment xml:lang="ko">만화책 압축 파일</comment>
+ <comment xml:lang="lt">komiksų knygos archyvas</comment>
+ <comment xml:lang="lv">komiksu grāmatas arhīvs</comment>
+ <comment xml:lang="nb">Tegneseriearkiv</comment>
+ <comment xml:lang="nl">stripboek-archief</comment>
+ <comment xml:lang="nn">teikneseriearkiv</comment>
+ <comment xml:lang="oc">archiu Comic Book</comment>
+ <comment xml:lang="pl">Archiwum komiksu</comment>
+ <comment xml:lang="pt">arquivo de banda desenhada</comment>
+ <comment xml:lang="pt_BR">Pacote de histórias em quadrinhos</comment>
+ <comment xml:lang="ro">arhivă benzi desenate</comment>
+ <comment xml:lang="ru">Архив комиксов</comment>
+ <comment xml:lang="sk">Archív knihy komiksov</comment>
+ <comment xml:lang="sl">Datoteka arhiva stripov</comment>
+ <comment xml:lang="sq">Arkiv comic book</comment>
+ <comment xml:lang="sr">архива стрипа</comment>
+ <comment xml:lang="sv">serietidningsarkiv</comment>
+ <comment xml:lang="tr">çizgi roman arşivi</comment>
+ <comment xml:lang="uk">архів коміксів</comment>
+ <comment xml:lang="vi">Kho nén sách tranh chuyện vui</comment>
+ <comment xml:lang="zh_CN">漫画书归档文件</comment>
+ <comment xml:lang="zh_TW">漫畫書封存檔</comment>
+ <sub-class-of type="application/x-7z-compressed"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.cb7"/>
+ </mime-type>
+ <mime-type type="application/x-cbt">
+ <comment>comic book archive</comment>
+ <comment xml:lang="ar">أرشيف comic book</comment>
+ <comment xml:lang="be@latin">archiŭ komiksaŭ</comment>
+ <comment xml:lang="bg">Архив — комикси</comment>
+ <comment xml:lang="ca">arxiu comic book</comment>
+ <comment xml:lang="cs">archiv knihy komiksů</comment>
+ <comment xml:lang="da">comic book-arkiv</comment>
+ <comment xml:lang="de">Comic-Book-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο κόμικ</comment>
+ <comment xml:lang="en_GB">comic book archive</comment>
+ <comment xml:lang="es">archivador de libro de cómic</comment>
+ <comment xml:lang="eu">komiki artxiboa</comment>
+ <comment xml:lang="fi">sarjakuva-arkisto</comment>
+ <comment xml:lang="fo">teknisøgubóka skjalasavn</comment>
+ <comment xml:lang="fr">archive Comic Book</comment>
+ <comment xml:lang="ga">cartlann chartúin</comment>
+ <comment xml:lang="gl">ficheiro de libro de banda deseñada</comment>
+ <comment xml:lang="he">ארכיון ספר קומי</comment>
+ <comment xml:lang="hr">Strip arhiva</comment>
+ <comment xml:lang="hu">képregényarchívum</comment>
+ <comment xml:lang="ia">Archivo Comic Book</comment>
+ <comment xml:lang="id">arsip buku komik</comment>
+ <comment xml:lang="it">Archivio comic book</comment>
+ <comment xml:lang="ja">コミックブックアーカイブ</comment>
+ <comment xml:lang="kk">комикстар архиві</comment>
+ <comment xml:lang="ko">만화책 압축 파일</comment>
+ <comment xml:lang="lt">komiksų knygos archyvas</comment>
+ <comment xml:lang="lv">komiksu grāmatas arhīvs</comment>
+ <comment xml:lang="nb">Tegneseriearkiv</comment>
+ <comment xml:lang="nl">stripboek-archief</comment>
+ <comment xml:lang="nn">teikneseriearkiv</comment>
+ <comment xml:lang="oc">archiu Comic Book</comment>
+ <comment xml:lang="pl">Archiwum komiksu</comment>
+ <comment xml:lang="pt">arquivo de banda desenhada</comment>
+ <comment xml:lang="pt_BR">Pacote de histórias em quadrinhos</comment>
+ <comment xml:lang="ro">arhivă benzi desenate</comment>
+ <comment xml:lang="ru">Архив комиксов</comment>
+ <comment xml:lang="sk">Archív knihy komiksov</comment>
+ <comment xml:lang="sl">Datoteka arhiva stripov</comment>
+ <comment xml:lang="sq">Arkiv comic book</comment>
+ <comment xml:lang="sr">архива стрипа</comment>
+ <comment xml:lang="sv">serietidningsarkiv</comment>
+ <comment xml:lang="tr">çizgi roman arşivi</comment>
+ <comment xml:lang="uk">архів коміксів</comment>
+ <comment xml:lang="vi">Kho nén sách tranh chuyện vui</comment>
+ <comment xml:lang="zh_CN">漫画书归档文件</comment>
+ <comment xml:lang="zh_TW">漫畫書封存檔</comment>
+ <sub-class-of type="application/x-tar"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.cbt"/>
+ </mime-type>
+ <mime-type type="application/vnd.comicbook+zip">
+ <comment>comic book archive</comment>
+ <comment xml:lang="ar">أرشيف comic book</comment>
+ <comment xml:lang="be@latin">archiŭ komiksaŭ</comment>
+ <comment xml:lang="bg">Архив — комикси</comment>
+ <comment xml:lang="ca">arxiu comic book</comment>
+ <comment xml:lang="cs">archiv knihy komiksů</comment>
+ <comment xml:lang="da">comic book-arkiv</comment>
+ <comment xml:lang="de">Comic-Book-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο κόμικ</comment>
+ <comment xml:lang="en_GB">comic book archive</comment>
+ <comment xml:lang="es">archivador de libro de cómic</comment>
+ <comment xml:lang="eu">komiki artxiboa</comment>
+ <comment xml:lang="fi">sarjakuva-arkisto</comment>
+ <comment xml:lang="fo">teknisøgubóka skjalasavn</comment>
+ <comment xml:lang="fr">archive Comic Book</comment>
+ <comment xml:lang="ga">cartlann chartúin</comment>
+ <comment xml:lang="gl">ficheiro de libro de banda deseñada</comment>
+ <comment xml:lang="he">ארכיון ספר קומי</comment>
+ <comment xml:lang="hr">Strip arhiva</comment>
+ <comment xml:lang="hu">képregényarchívum</comment>
+ <comment xml:lang="ia">Archivo Comic Book</comment>
+ <comment xml:lang="id">arsip buku komik</comment>
+ <comment xml:lang="it">Archivio comic book</comment>
+ <comment xml:lang="ja">コミックブックアーカイブ</comment>
+ <comment xml:lang="kk">комикстар архиві</comment>
+ <comment xml:lang="ko">만화책 압축 파일</comment>
+ <comment xml:lang="lt">komiksų knygos archyvas</comment>
+ <comment xml:lang="lv">komiksu grāmatas arhīvs</comment>
+ <comment xml:lang="nb">Tegneseriearkiv</comment>
+ <comment xml:lang="nl">stripboek-archief</comment>
+ <comment xml:lang="nn">teikneseriearkiv</comment>
+ <comment xml:lang="oc">archiu Comic Book</comment>
+ <comment xml:lang="pl">Archiwum komiksu</comment>
+ <comment xml:lang="pt">arquivo de banda desenhada</comment>
+ <comment xml:lang="pt_BR">Pacote de histórias em quadrinhos</comment>
+ <comment xml:lang="ro">arhivă benzi desenate</comment>
+ <comment xml:lang="ru">Архив комиксов</comment>
+ <comment xml:lang="sk">Archív knihy komiksov</comment>
+ <comment xml:lang="sl">Datoteka arhiva stripov</comment>
+ <comment xml:lang="sq">Arkiv comic book</comment>
+ <comment xml:lang="sr">архива стрипа</comment>
+ <comment xml:lang="sv">serietidningsarkiv</comment>
+ <comment xml:lang="tr">çizgi roman arşivi</comment>
+ <comment xml:lang="uk">архів коміксів</comment>
+ <comment xml:lang="vi">Kho nén sách tranh chuyện vui</comment>
+ <comment xml:lang="zh_CN">漫画书归档文件</comment>
+ <comment xml:lang="zh_TW">漫畫書封存檔</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.cbz"/>
+ <alias type="application/x-cbz"/>
+ </mime-type>
+ <mime-type type="application/x-lrzip">
+ <comment>Lrzip archive</comment>
+ <comment xml:lang="ar">أرشيف Lrzip</comment>
+ <comment xml:lang="bg">Архив — lrzip</comment>
+ <comment xml:lang="ca">arxiu lrzip</comment>
+ <comment xml:lang="cs">archiv Lrzip</comment>
+ <comment xml:lang="da">Lrzip-arkiv</comment>
+ <comment xml:lang="de">Lrzip-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Lrzip</comment>
+ <comment xml:lang="en_GB">Lrzip archive</comment>
+ <comment xml:lang="eo">Lrzip-arkivo</comment>
+ <comment xml:lang="es">archivador Lrzip</comment>
+ <comment xml:lang="eu">Lrzip artxiboa</comment>
+ <comment xml:lang="fi">Lrzip-arkisto</comment>
+ <comment xml:lang="fo">Lrzip skjalasavn</comment>
+ <comment xml:lang="fr">archive lrzip</comment>
+ <comment xml:lang="ga">cartlann Lrzip</comment>
+ <comment xml:lang="gl">arquivo Lrzip</comment>
+ <comment xml:lang="he">ארכיון Lrzip</comment>
+ <comment xml:lang="hr">Lrzip arhiva</comment>
+ <comment xml:lang="hu">Lrzip archívum</comment>
+ <comment xml:lang="ia">Archivo Lrzip</comment>
+ <comment xml:lang="id">Arsip Lrzip</comment>
+ <comment xml:lang="it">Archivio Lrzip</comment>
+ <comment xml:lang="ja">Lrzip アーカイブ</comment>
+ <comment xml:lang="kk">Lrzip архиві</comment>
+ <comment xml:lang="ko">LRZIP 압축 파일</comment>
+ <comment xml:lang="lt">Lrzip archyvas</comment>
+ <comment xml:lang="lv">Lrzip arhīvs</comment>
+ <comment xml:lang="nl">Lrzip archief</comment>
+ <comment xml:lang="oc">archiu lrzip</comment>
+ <comment xml:lang="pl">Archiwum lrzip</comment>
+ <comment xml:lang="pt">arquivo Lrzip</comment>
+ <comment xml:lang="pt_BR">Pacote Lrzip</comment>
+ <comment xml:lang="ro">Arhivă Lrzip</comment>
+ <comment xml:lang="ru">Архив LRZIP</comment>
+ <comment xml:lang="sk">Archív Lrzip</comment>
+ <comment xml:lang="sl">Datoteka arhiva Lrzip</comment>
+ <comment xml:lang="sr">Лрзип архива</comment>
+ <comment xml:lang="sv">Lrzip-arkiv</comment>
+ <comment xml:lang="tr">Lrzip arşivi</comment>
+ <comment xml:lang="uk">архів lrzip</comment>
+ <comment xml:lang="zh_CN">Lrzip 归档文件</comment>
+ <comment xml:lang="zh_TW">Lrzip 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="LRZI" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.lrz"/>
+ </mime-type>
+ <mime-type type="application/x-lrzip-compressed-tar">
+ <comment>Tar archive (lrzip-compressed)</comment>
+ <comment xml:lang="ar">أرشيف Tar (مضغوط-lrzip)</comment>
+ <comment xml:lang="bg">Архив — tar, компресиран с lrzip</comment>
+ <comment xml:lang="ca">arxiu tar (amb compressió lrzip)</comment>
+ <comment xml:lang="cs">archiv Tar (komprimovaný pomocí lrzip)</comment>
+ <comment xml:lang="da">Tar-arkiv (lrzip-komprimeret)</comment>
+ <comment xml:lang="de">Tar-Archiv (lrzip-komprimiert)</comment>
+ <comment xml:lang="el">Αρχείο Tar (συμπιεσμένο με lrzip)</comment>
+ <comment xml:lang="en_GB">Tar archive (lrzip-compressed)</comment>
+ <comment xml:lang="es">archivador Tar (comprimido con lrzip)</comment>
+ <comment xml:lang="eu">Tar artxiboa (lrzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">Tar-arkisto (lrzip-pakattu)</comment>
+ <comment xml:lang="fo">Tar skjalasavn (lrzip-stappað)</comment>
+ <comment xml:lang="fr">archive tar (compressée lrzip)</comment>
+ <comment xml:lang="ga">cartlann Tar (comhbhrúite le lrzip)</comment>
+ <comment xml:lang="gl">arquivo Tar (comprimido con lrzip)</comment>
+ <comment xml:lang="he">ארכיון Tar (מכווץ ע״י lrzip)</comment>
+ <comment xml:lang="hr">Tar arhiva (lrzip sažeta)</comment>
+ <comment xml:lang="hu">Tar archívum (lrzip-pel tömörítve)</comment>
+ <comment xml:lang="ia">Archivo Tar (comprimite con lrzip)</comment>
+ <comment xml:lang="id">Arsip Tar (terkompresi lrzip)</comment>
+ <comment xml:lang="it">Archivio tar (compresso con lrzip)</comment>
+ <comment xml:lang="ja">Tar アーカイブ (lrzip 圧縮)</comment>
+ <comment xml:lang="kk">Tar архиві (lrzip-пен сығылған)</comment>
+ <comment xml:lang="ko">TAR 묶음 파일(LRZIP 압축)</comment>
+ <comment xml:lang="lt">Tar archyvas (suglaudintas su lrzip)</comment>
+ <comment xml:lang="lv">Tar arhīvs (saspiests ar lrzip)</comment>
+ <comment xml:lang="nl">Tar archief (lrzip-compressed)</comment>
+ <comment xml:lang="oc">archiu tar (compressat lrzip)</comment>
+ <comment xml:lang="pl">Archiwum tar (kompresja lrzip)</comment>
+ <comment xml:lang="pt">arquivo Tar (compressão Lrzip)</comment>
+ <comment xml:lang="pt_BR">Pacote Tar (compactado com lrzip)</comment>
+ <comment xml:lang="ro">Arhivă Tar (comprimată lrzip)</comment>
+ <comment xml:lang="ru">Архив TAR (сжатый lrzip)</comment>
+ <comment xml:lang="sk">Archív tar (komprimovaný pomocou lrzip)</comment>
+ <comment xml:lang="sl">Datoteka arhiva Tar (stisnjen z lrzip)</comment>
+ <comment xml:lang="sr">Тар архива (запакована лрзипом)</comment>
+ <comment xml:lang="sv">Tar-arkiv (lrzip-komprimerat)</comment>
+ <comment xml:lang="tr">Tar arşivi (lrzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">архів tar (стиснений lrzip)</comment>
+ <comment xml:lang="zh_CN">Tar 归档文件(lrzip 压缩)</comment>
+ <comment xml:lang="zh_TW">Tar 封存檔 (lrzip 格式壓縮)</comment>
+ <generic-icon name="package-x-generic"/>
+ <sub-class-of type="application/x-lrzip"/>
+ <glob pattern="*.tar.lrz"/>
+ <glob pattern="*.tlrz"/>
+ </mime-type>
+ <mime-type type="application/x-apple-diskimage">
+ <comment>Apple disk image</comment>
+ <comment xml:lang="ast">Imaxe de discu d'Apple</comment>
+ <comment xml:lang="bg">Диск — Apple</comment>
+ <comment xml:lang="ca">imatge de disc d'Apple</comment>
+ <comment xml:lang="cs">obraz disku Apple</comment>
+ <comment xml:lang="da">Apple-diskaftryk</comment>
+ <comment xml:lang="de">Apple-Datenträgerabbild</comment>
+ <comment xml:lang="el">Εικόνα δίσκου Apple</comment>
+ <comment xml:lang="en_GB">Apple disk image</comment>
+ <comment xml:lang="es">imagen de disco de Apple</comment>
+ <comment xml:lang="eu">Apple disko irudia</comment>
+ <comment xml:lang="fi">Apple-levytiedosto</comment>
+ <comment xml:lang="fr">image disque Apple</comment>
+ <comment xml:lang="ga">íomhá diosca Apple</comment>
+ <comment xml:lang="gl">imaxe de disco de Appl</comment>
+ <comment xml:lang="he">תמונת כונן Apple</comment>
+ <comment xml:lang="hr">Apple slika diska</comment>
+ <comment xml:lang="hu">Apple lemezkép</comment>
+ <comment xml:lang="ia">Imagine de disco Apple</comment>
+ <comment xml:lang="id">Image disk Apple</comment>
+ <comment xml:lang="it">Immagine disco Apple</comment>
+ <comment xml:lang="ja">Apple ディスクイメージ</comment>
+ <comment xml:lang="ka">Apple-ის სადისკო გამოსახულება</comment>
+ <comment xml:lang="kk">Apple диск бейнесі</comment>
+ <comment xml:lang="ko">Apple 디스크 이미지</comment>
+ <comment xml:lang="lv">Apple diska attēls</comment>
+ <comment xml:lang="nl">Apple disk image</comment>
+ <comment xml:lang="oc">imatge disc Apple</comment>
+ <comment xml:lang="pl">Obraz dysku Apple</comment>
+ <comment xml:lang="pt">imagem de disco Apple</comment>
+ <comment xml:lang="pt_BR">Imagem de disco Apple</comment>
+ <comment xml:lang="ru">Образ диска Apple Mac OS X</comment>
+ <comment xml:lang="sk">Obraz disku Apple</comment>
+ <comment xml:lang="sl">Odtis diska Apple</comment>
+ <comment xml:lang="sr">Еплов одраз диска</comment>
+ <comment xml:lang="sv">Apple-diskavbild</comment>
+ <comment xml:lang="tr">Apple disk görüntüsü</comment>
+ <comment xml:lang="uk">образ диска Apple</comment>
+ <comment xml:lang="zh_CN">Apple 磁盘映像</comment>
+ <comment xml:lang="zh_TW">Apple 磁碟映像檔</comment>
+ <glob pattern="*.dmg"/>
+ </mime-type>
+ <mime-type type="application/x-raw-disk-image">
+ <comment>Raw disk image</comment>
+ <comment xml:lang="ast">Imaxe de discu en bruto</comment>
+ <comment xml:lang="ca">imatge de disc RAW</comment>
+ <comment xml:lang="cs">surový obraz disku</comment>
+ <comment xml:lang="da">Rå diskaftryk</comment>
+ <comment xml:lang="de">Rohes Datenträgerabbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα δίσκου</comment>
+ <comment xml:lang="en_GB">Raw disk image</comment>
+ <comment xml:lang="es">imagen de disco en bruto</comment>
+ <comment xml:lang="eu">Disko gordinaren irudia</comment>
+ <comment xml:lang="fi">Raaka levytiedosto</comment>
+ <comment xml:lang="fr">image disque Raw</comment>
+ <comment xml:lang="ga">Amhíomha diosca</comment>
+ <comment xml:lang="gl">Imaxe de disco en bruto</comment>
+ <comment xml:lang="he">דמות גולמית של כונן</comment>
+ <comment xml:lang="hr">Osnovna slika diska</comment>
+ <comment xml:lang="hu">Nyers lemezkép</comment>
+ <comment xml:lang="ia">Imagine de disco crude</comment>
+ <comment xml:lang="id">Image disk mentah</comment>
+ <comment xml:lang="it">Immagine disco raw</comment>
+ <comment xml:lang="kk">Шикі диск бейнесі</comment>
+ <comment xml:lang="ko">RAW 디스크 이미지</comment>
+ <comment xml:lang="oc">imatge disc Raw</comment>
+ <comment xml:lang="pl">Surowy obraz dysku</comment>
+ <comment xml:lang="pt">imagem de disco Raw</comment>
+ <comment xml:lang="pt_BR">Imagem bruta de disco</comment>
+ <comment xml:lang="ru">Необработанный образ диска</comment>
+ <comment xml:lang="sk">Obraz disku</comment>
+ <comment xml:lang="sl">Surovi odtis diska</comment>
+ <comment xml:lang="sr">сиров одраз диска</comment>
+ <comment xml:lang="sv">Rå diskavbild</comment>
+ <comment xml:lang="tr">İşlem görmemiş disk imajı</comment>
+ <comment xml:lang="uk">простий образ диска</comment>
+ <comment xml:lang="zh_CN">原始磁盘映像</comment>
+ <comment xml:lang="zh_TW">原生磁碟映像檔</comment>
+ <glob pattern="*.raw-disk-image"/>
+ <glob pattern="*.img"/>
+ </mime-type>
+ <mime-type type="application/x-raw-floppy-disk-image">
+ <comment>Floppy disk image</comment>
+ <sub-class-of type="application/x-raw-disk-image"/>
+ <alias type="application/x-fd-file"/>
+ <glob pattern="*.fd"/>
+ <glob pattern="*.qd"/>
+ </mime-type>
+ <mime-type type="application/x-raw-disk-image-xz-compressed">
+ <comment>Raw disk image (XZ-compressed)</comment>
+ <comment xml:lang="ast">Imaxe de discu en bruto (comprimida en XZ)</comment>
+ <comment xml:lang="ca">imatge de disc RAW (amb compressió XZ)</comment>
+ <comment xml:lang="cs">surový obraz disku (komprimovaný pomocí XZ)</comment>
+ <comment xml:lang="da">Rå diskaftryk (XZ-komprimeret)</comment>
+ <comment xml:lang="de">Rohes Datenträgerabbild (XZ-komprimiert)</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα δίσκου (συμπιεσμένη XZ)</comment>
+ <comment xml:lang="en_GB">Raw disk image (XZ-compressed)</comment>
+ <comment xml:lang="es">imagen de disco en bruto (comprimida con XZ)</comment>
+ <comment xml:lang="eu">Disko gordinaren irudia (XZ-rekin konprimitua)</comment>
+ <comment xml:lang="fi">Raaka levytiedosto (XZ-pakattu)</comment>
+ <comment xml:lang="fr">image disque Raw (compression XZ)</comment>
+ <comment xml:lang="ga">Amhíomhá (comhbhrúite le XZ)</comment>
+ <comment xml:lang="gl">Imaxe de disco en bruto (comprimida en XZ)</comment>
+ <comment xml:lang="he">דמות גולמית של כונן (בדחיסת XZ)</comment>
+ <comment xml:lang="hr">Osnovna slika diska (XZ sažeta)</comment>
+ <comment xml:lang="hu">Nyers lemezkép (XZ-vel tömörítve)</comment>
+ <comment xml:lang="ia">Imagine de disco crude (comprimite con XZ)</comment>
+ <comment xml:lang="id">Image disk mentah (terkompresi XZ)</comment>
+ <comment xml:lang="it">Immagine disco raw (compressa XZ)</comment>
+ <comment xml:lang="kk">Шикі диск бейнесі (XZ-мен сығылған)</comment>
+ <comment xml:lang="ko">RAW 디스크 이미지(XZ 압축)</comment>
+ <comment xml:lang="oc">imatge disc Raw (compression XZ)</comment>
+ <comment xml:lang="pl">Surowy obraz dysku (kompresja XZ)</comment>
+ <comment xml:lang="pt">imagem de disco Raw (compressão XZ)</comment>
+ <comment xml:lang="pt_BR">Imagem bruta de disco (compactada com XZ)</comment>
+ <comment xml:lang="ru">Необработанный образ диска (сжатый xz)</comment>
+ <comment xml:lang="sk">Obraz disku (komprimovaný pomocou XZ)</comment>
+ <comment xml:lang="sl">Surovi odtis diska (stisnjeno z XZ)</comment>
+ <comment xml:lang="sr">сиров одраз диска (запакована ИксЗ-ом)</comment>
+ <comment xml:lang="sv">Rå diskavbild (XZ-komprimerad)</comment>
+ <comment xml:lang="tr">İşlem görmemiş disk imajı (XZ ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">простий образ диска (стиснений XZ)</comment>
+ <comment xml:lang="zh_CN">原始磁盘映像(XZ 压缩)</comment>
+ <comment xml:lang="zh_TW">原生磁碟映像 (XZ 格式壓縮)</comment>
+ <sub-class-of type="application/x-xz"/>
+ <glob pattern="*.raw-disk-image.xz"/>
+ <glob pattern="*.img.xz"/>
+ </mime-type>
+ <mime-type type="application/x-cd-image">
+ <comment>raw CD image</comment>
+ <comment xml:lang="ar">صورة CD خامة</comment>
+ <comment xml:lang="ast">imaxe de CD en bruto</comment>
+ <comment xml:lang="be@latin">suvoraja vyjava CD</comment>
+ <comment xml:lang="bg">Изображение — raw CD</comment>
+ <comment xml:lang="ca">imatge de CD en cru</comment>
+ <comment xml:lang="cs">surový obraz CD</comment>
+ <comment xml:lang="da">rå cd-aftryk</comment>
+ <comment xml:lang="de">CD-Roh-Abbild</comment>
+ <comment xml:lang="el">Εικόνα περιεχομένου ψηφιακού δίσκου</comment>
+ <comment xml:lang="en_GB">raw CD image</comment>
+ <comment xml:lang="eo">kruda lumdiskbildo</comment>
+ <comment xml:lang="es">imagen de CD en bruto</comment>
+ <comment xml:lang="eu">CD gordinaren irudia </comment>
+ <comment xml:lang="fi">raaka CD-vedos</comment>
+ <comment xml:lang="fo">rá CD mynd</comment>
+ <comment xml:lang="fr">image CD brute</comment>
+ <comment xml:lang="ga">amhíomhá dhlúthdhiosca</comment>
+ <comment xml:lang="gl">imaxe de CD en bruto</comment>
+ <comment xml:lang="he">תמונת דיסק גולמית</comment>
+ <comment xml:lang="hr">Osnovna CD slika</comment>
+ <comment xml:lang="hu">nyers CD-lemezkép</comment>
+ <comment xml:lang="ia">Imagine CD brute</comment>
+ <comment xml:lang="id">citra CD mentah</comment>
+ <comment xml:lang="it">Immagine raw CD</comment>
+ <comment xml:lang="ja">生 CD イメージ</comment>
+ <comment xml:lang="kk">өңделмеген CD бейнесі</comment>
+ <comment xml:lang="ko">CD 이미지</comment>
+ <comment xml:lang="lt">raw CD atvaizdis</comment>
+ <comment xml:lang="lv">CD jēlattēls</comment>
+ <comment xml:lang="ms">Imej CD mentah</comment>
+ <comment xml:lang="nb">rått CD-bilde</comment>
+ <comment xml:lang="nl">ruw CD-beeldbestand</comment>
+ <comment xml:lang="nn">rått CD-bilete</comment>
+ <comment xml:lang="oc">imatge CD brut</comment>
+ <comment xml:lang="pl">Surowy obraz CD</comment>
+ <comment xml:lang="pt">imagem em bruto de CD</comment>
+ <comment xml:lang="pt_BR">Imagem bruta de CD</comment>
+ <comment xml:lang="ro">imagine de CD brută</comment>
+ <comment xml:lang="ru">Необработанный образ компакт-диска</comment>
+ <comment xml:lang="sk">Surový obraz CD</comment>
+ <comment xml:lang="sl">surovi CD odtis</comment>
+ <comment xml:lang="sq">Imazh raw CD</comment>
+ <comment xml:lang="sr">сиров одраз ЦД-а</comment>
+ <comment xml:lang="sv">rå cd-avbild</comment>
+ <comment xml:lang="tr">Ham CD görüntüsü</comment>
+ <comment xml:lang="uk">образ raw CD</comment>
+ <comment xml:lang="vi">ảnh đĩa CD thô</comment>
+ <comment xml:lang="zh_CN">原始 CD 映像</comment>
+ <comment xml:lang="zh_TW">原生 CD 映像檔</comment>
+ <sub-class-of type="application/x-raw-disk-image"/>
+ <alias type="application/x-iso9660-image"/>
+
+ <glob weight="80" pattern="*.iso"/>
+ <glob pattern="*.iso9660"/>
+ </mime-type>
+ <mime-type type="application/x-iso9660-appimage">
+ <comment>AppImage application bundle</comment>
+ <comment xml:lang="ca">paquet d'aplicació AppImage</comment>
+ <comment xml:lang="cs">balíček AppImage s aplikací</comment>
+ <comment xml:lang="da">Applmage-programsamling</comment>
+ <comment xml:lang="de">AppImage-Anwendungspaket</comment>
+ <comment xml:lang="en_GB">AppImage application bundle</comment>
+ <comment xml:lang="es">paquete de aplicación AppImage</comment>
+ <comment xml:lang="eu">AppImage aplikazio bilduma</comment>
+ <comment xml:lang="fi">AppImage-sovelluspaketti</comment>
+ <comment xml:lang="fr">lot applicatif AppImage</comment>
+ <comment xml:lang="ga">burla feidhmchláir AppImage</comment>
+ <comment xml:lang="he">חבילת יישומי AppImage</comment>
+ <comment xml:lang="hr">AppImage paket aplikacije</comment>
+ <comment xml:lang="hu">AppImage alkalmazáscsomag</comment>
+ <comment xml:lang="id">bundel aplikasi AppImage</comment>
+ <comment xml:lang="it">Bundle applicazione AppImage</comment>
+ <comment xml:lang="kk">AppImage қолданбалар дестесі</comment>
+ <comment xml:lang="ko">AppImage 프로그램 번들</comment>
+ <comment xml:lang="pl">Pakiet programu AppImage</comment>
+ <comment xml:lang="pt_BR">Pacote de aplicativo AppImage</comment>
+ <comment xml:lang="ru">Пакет приложения AppImage</comment>
+ <comment xml:lang="sk">Balík aplikácií AppImage</comment>
+ <comment xml:lang="sr">скуп програма Ап-слике</comment>
+ <comment xml:lang="sv">AppImage-programbunt</comment>
+ <comment xml:lang="tr">AppImage uygulama paketi</comment>
+ <comment xml:lang="uk">пакунок із програмами AppImage</comment>
+ <comment xml:lang="zh_CN">AppImage 应用组合包</comment>
+ <comment xml:lang="zh_TW">AppImage 應用程式套組</comment>
+ <sub-class-of type="application/x-executable"/>
+ <sub-class-of type="application/x-iso9660-image"/>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="ELF" type="string" offset="1">
+ <match value="0x41" type="byte" offset="8">
+ <match value="0x49" type="byte" offset="9">
+ <match value="0x01" type="byte" offset="10"/>
+ </match>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.appimage"/>
+ </mime-type>
+ <mime-type type="application/x-cdrdao-toc">
+ <comment>CD Table Of Contents</comment>
+ <comment xml:lang="ar">جدول محتويات الـ CD</comment>
+ <comment xml:lang="be@latin">Źmieściva CD</comment>
+ <comment xml:lang="bg">Съдържание на CD</comment>
+ <comment xml:lang="ca">taula de continguts de CD</comment>
+ <comment xml:lang="cs">obsah CD</comment>
+ <comment xml:lang="da">Cd-indholdsfotegnelse</comment>
+ <comment xml:lang="de">CD-Inhaltsverzeichnis</comment>
+ <comment xml:lang="el">Πίνακας περιεχομένων CD</comment>
+ <comment xml:lang="en_GB">CD Table Of Contents</comment>
+ <comment xml:lang="es">índice de contenido de CD</comment>
+ <comment xml:lang="eu">CDaren edukien aurkibidea</comment>
+ <comment xml:lang="fi">CD-sisällysluettelo</comment>
+ <comment xml:lang="fo">CD innihaldsyvurlit</comment>
+ <comment xml:lang="fr">table des matières de CD</comment>
+ <comment xml:lang="ga">clár ábhar dlúthdhiosca</comment>
+ <comment xml:lang="gl">táboa de contidos de CD</comment>
+ <comment xml:lang="he">תוכן עניינים של דיסק</comment>
+ <comment xml:lang="hr">CD sadržaj</comment>
+ <comment xml:lang="hu">CD tartalomjegyzék</comment>
+ <comment xml:lang="ia">Tabula de contento de CD</comment>
+ <comment xml:lang="id">Tabel Isi CD</comment>
+ <comment xml:lang="it">Indice CD</comment>
+ <comment xml:lang="ja">CD Table Of Contents</comment>
+ <comment xml:lang="kk">CD құрама кестесі</comment>
+ <comment xml:lang="ko">CD 내용 목록</comment>
+ <comment xml:lang="lt">CD turinys</comment>
+ <comment xml:lang="lv">CD satura rādītājs</comment>
+ <comment xml:lang="nb">Innholdsfortegnelse for CD</comment>
+ <comment xml:lang="nl">CD-inhoudsopgave</comment>
+ <comment xml:lang="nn">CD innhaldsliste</comment>
+ <comment xml:lang="oc">ensenhador de CD</comment>
+ <comment xml:lang="pl">Plik zawartości płyty CD</comment>
+ <comment xml:lang="pt">Tabela de conteúdos de CD</comment>
+ <comment xml:lang="pt_BR">Sumário de CD</comment>
+ <comment xml:lang="ro">Tabel conținut CD</comment>
+ <comment xml:lang="ru">Таблица содержания CD</comment>
+ <comment xml:lang="sk">Obsah CD</comment>
+ <comment xml:lang="sl">Kazalo vsebine CD nosilca</comment>
+ <comment xml:lang="sq">Tregues CD</comment>
+ <comment xml:lang="sr">табела садржаја ЦД-а</comment>
+ <comment xml:lang="sv">Cd-innehållsförteckning</comment>
+ <comment xml:lang="tr">CD İçindekiler Tablosu</comment>
+ <comment xml:lang="uk">зміст CD</comment>
+ <comment xml:lang="vi">Mục Lục của đĩa CD</comment>
+ <comment xml:lang="zh_CN">CD 索引</comment>
+ <comment xml:lang="zh_TW">CD 內容目錄</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <magic priority="50">
+ <match value="CD_ROM\n" type="string" offset="0"/>
+ <match value="CD_DA\n" type="string" offset="0"/>
+ <match value="CD_ROM_XA\n" type="string" offset="0"/>
+ <match value="CD_TEXT " type="string" offset="0"/>
+ <match value="CATALOG &quot;" type="string" offset="0">
+ <match value="&quot;" type="string" offset="22"/>
+ </match>
+ </magic>
+ <glob pattern="*.toc"/>
+ </mime-type>
+ <mime-type type="application/vnd.chess-pgn">
+ <comment>PGN chess game notation</comment>
+ <comment xml:lang="ar">تدوينة لعبة الشطرنج PGN</comment>
+ <comment xml:lang="be@latin">Zaciem ab šachmatnaj partyi PGN</comment>
+ <comment xml:lang="bg">Игра шах — PGN</comment>
+ <comment xml:lang="ca">notació de joc d'escacs PGN</comment>
+ <comment xml:lang="cs">šachová notace PGN</comment>
+ <comment xml:lang="da">PGN-skakspilsnotation</comment>
+ <comment xml:lang="de">PGN-Schachspielnotation</comment>
+ <comment xml:lang="el">Σημειογραφία παιχνιδιού σκακιού PGN</comment>
+ <comment xml:lang="en_GB">PGN chess game notation</comment>
+ <comment xml:lang="es">notación para juegos de ajedrez PGN</comment>
+ <comment xml:lang="eu">PGN xake jokoaren notazioa</comment>
+ <comment xml:lang="fi">PGN-šakkipelinotaatio</comment>
+ <comment xml:lang="fo">PGN talv teknskipan</comment>
+ <comment xml:lang="fr">notation de jeu d'échecs PGN</comment>
+ <comment xml:lang="ga">nodaireacht chluiche ficheall PGN</comment>
+ <comment xml:lang="gl">Notación de xogo de xadrez PGN</comment>
+ <comment xml:lang="he">סימון משחק שח PGN</comment>
+ <comment xml:lang="hr">PGN zapis šahovske igre</comment>
+ <comment xml:lang="hu">PGN sakkfeljegyzés</comment>
+ <comment xml:lang="ia">Notation de joco de chacos PGN</comment>
+ <comment xml:lang="id">Notasi permainan catur PGN</comment>
+ <comment xml:lang="it">Notazione partita a scacchi PGN</comment>
+ <comment xml:lang="ja">PGN チェスゲーム記録</comment>
+ <comment xml:lang="kk">PGN шахмат ойыны</comment>
+ <comment xml:lang="ko">PGN 체스 게임 기보</comment>
+ <comment xml:lang="lt">PGN šachmatų žaidimo žymėjimas</comment>
+ <comment xml:lang="lv">PGN šaha spēles notācija</comment>
+ <comment xml:lang="nb">PGN sjakkspillnotasjon</comment>
+ <comment xml:lang="nl">PGN-schaakspelnotatie</comment>
+ <comment xml:lang="nn">PGN-sjakkspelnotasjon</comment>
+ <comment xml:lang="oc">notacion de jòc d'escacs PGN</comment>
+ <comment xml:lang="pl">Plik PGN notacji gry w szachy</comment>
+ <comment xml:lang="pt">notação de jogo de xadrez PGN</comment>
+ <comment xml:lang="pt_BR">Notação de jogo de xadrez PGN</comment>
+ <comment xml:lang="ro">Notație joc șah PGN</comment>
+ <comment xml:lang="ru">Шахматная партия PGN</comment>
+ <comment xml:lang="sk">Šachová notácia PGN</comment>
+ <comment xml:lang="sl">Datoteka opomb šahovske igre PGN</comment>
+ <comment xml:lang="sq">Njoftim loje shahu PGN</comment>
+ <comment xml:lang="sr">ПГН забелешка шаховске игре</comment>
+ <comment xml:lang="sv">PGN-schackpartinotation</comment>
+ <comment xml:lang="tr">PGN satranç oyun gösterimi</comment>
+ <comment xml:lang="uk">запис гри у шахи PGN</comment>
+ <comment xml:lang="vi">Cách ghi lượt chơi cờ PGN</comment>
+ <comment xml:lang="zh_CN">PGN 象棋游戏注记</comment>
+ <comment xml:lang="zh_TW">PGN 國際象棋棋譜</comment>
+ <acronym>PGN</acronym>
+ <expanded-acronym>Portable Game Notation</expanded-acronym>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.pgn"/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="[Event " type="string" offset="0"/>
+ </magic>
+ <alias type="application/x-chess-pgn"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-htmlhelp">
+ <comment>CHM document</comment>
+ <comment xml:lang="ar">مستند CHM</comment>
+ <comment xml:lang="ast">Documentu CHM</comment>
+ <comment xml:lang="be@latin">Dakument CHM</comment>
+ <comment xml:lang="bg">Документ — CHM</comment>
+ <comment xml:lang="ca">document CHM</comment>
+ <comment xml:lang="cs">dokument CHM</comment>
+ <comment xml:lang="da">CHM-dokument</comment>
+ <comment xml:lang="de">CHM-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο CHM</comment>
+ <comment xml:lang="en_GB">CHM document</comment>
+ <comment xml:lang="eo">CHM-dokumento</comment>
+ <comment xml:lang="es">documento CHM</comment>
+ <comment xml:lang="eu">CHM dokumentua</comment>
+ <comment xml:lang="fi">CHM-asiakirja</comment>
+ <comment xml:lang="fo">CHM skjal</comment>
+ <comment xml:lang="fr">document CHM</comment>
+ <comment xml:lang="ga">cáipéis CHM</comment>
+ <comment xml:lang="gl">documento CHM</comment>
+ <comment xml:lang="he">מסמך CHM</comment>
+ <comment xml:lang="hr">CHM dokument</comment>
+ <comment xml:lang="hu">CHM dokumentum</comment>
+ <comment xml:lang="ia">Documento CHM</comment>
+ <comment xml:lang="id">Dokumen CHM</comment>
+ <comment xml:lang="it">Documento CHM</comment>
+ <comment xml:lang="ja">CHM ドキュメント</comment>
+ <comment xml:lang="ka">CHM დოკუმენტი</comment>
+ <comment xml:lang="kk">CHM құжаты</comment>
+ <comment xml:lang="ko">CHM 문서</comment>
+ <comment xml:lang="lt">CHM dokumentas</comment>
+ <comment xml:lang="lv">CHM dokuments</comment>
+ <comment xml:lang="nb">CHM-dokument</comment>
+ <comment xml:lang="nl">CHM-document</comment>
+ <comment xml:lang="nn">CHM-dokument</comment>
+ <comment xml:lang="oc">document CHM</comment>
+ <comment xml:lang="pl">Dokument CHM</comment>
+ <comment xml:lang="pt">documento CHM</comment>
+ <comment xml:lang="pt_BR">Documento CHM</comment>
+ <comment xml:lang="ro">Document CHM</comment>
+ <comment xml:lang="ru">Документ CHM</comment>
+ <comment xml:lang="sk">Dokument CHM</comment>
+ <comment xml:lang="sl">Dokument CHM</comment>
+ <comment xml:lang="sq">Dokument CHM</comment>
+ <comment xml:lang="sr">ЦХМ документ</comment>
+ <comment xml:lang="sv">CHM-dokument</comment>
+ <comment xml:lang="tr">CHM belgesi</comment>
+ <comment xml:lang="uk">документ CHM</comment>
+ <comment xml:lang="vi">Tài liệu CHM</comment>
+ <comment xml:lang="zh_CN">CHM 文档</comment>
+ <comment xml:lang="zh_TW">CHM 文件</comment>
+ <acronym>CHM</acronym>
+ <expanded-acronym>Compiled Help Modules</expanded-acronym>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.chm"/>
+ <alias type="application/x-chm"/>
+ </mime-type>
+ <mime-type type="application/x-class-file">
+ <comment>Java byte code</comment>
+ <comment xml:lang="ar">رمز بايت الـJava</comment>
+ <comment xml:lang="az">Java bayt kodu</comment>
+ <comment xml:lang="be@latin">Bajtavy kod Java</comment>
+ <comment xml:lang="bg">Байт код за Java</comment>
+ <comment xml:lang="ca">Bytecode de Java</comment>
+ <comment xml:lang="cs">bajtový kód Java</comment>
+ <comment xml:lang="cy">Côd beit Java</comment>
+ <comment xml:lang="da">Javabytekode</comment>
+ <comment xml:lang="de">Java-Bytecode</comment>
+ <comment xml:lang="el">Συμβολοκώδικας Java</comment>
+ <comment xml:lang="en_GB">Java byte code</comment>
+ <comment xml:lang="eo">Java-bajtkodo</comment>
+ <comment xml:lang="es">bytecode de Java</comment>
+ <comment xml:lang="eu">Java byte-kodea</comment>
+ <comment xml:lang="fi">Java-tavukoodi</comment>
+ <comment xml:lang="fo">Java býtkota</comment>
+ <comment xml:lang="fr">code Java binaire</comment>
+ <comment xml:lang="ga">beartchód Java</comment>
+ <comment xml:lang="gl">byte code de Java</comment>
+ <comment xml:lang="he">קוד Java byte</comment>
+ <comment xml:lang="hr">Java bajt kôd</comment>
+ <comment xml:lang="hu">Java-bájtkód</comment>
+ <comment xml:lang="ia">Codice intermediari de Java</comment>
+ <comment xml:lang="id">Kode bita Java</comment>
+ <comment xml:lang="it">Bytecode Java</comment>
+ <comment xml:lang="ja">Java バイトコード</comment>
+ <comment xml:lang="kk">Java байт коды</comment>
+ <comment xml:lang="ko">Java 바이트 코드</comment>
+ <comment xml:lang="lt">Java baitinis kodas</comment>
+ <comment xml:lang="lv">Java bitu kods</comment>
+ <comment xml:lang="ms">Kod bait Java</comment>
+ <comment xml:lang="nb">Java-bytekode</comment>
+ <comment xml:lang="nl">Java-bytecode</comment>
+ <comment xml:lang="nn">Jave byte-kode</comment>
+ <comment xml:lang="oc">còde Java binari</comment>
+ <comment xml:lang="pl">Kod bajtowy Java</comment>
+ <comment xml:lang="pt">byte-code Java</comment>
+ <comment xml:lang="pt_BR">Código compilado Java</comment>
+ <comment xml:lang="ro">Bytecode Java</comment>
+ <comment xml:lang="ru">Байт-код Java</comment>
+ <comment xml:lang="sk">Bajtový kód Java</comment>
+ <comment xml:lang="sl">Datoteka bitne kode Java</comment>
+ <comment xml:lang="sq">Byte code Java</comment>
+ <comment xml:lang="sr">бајтни ко̂д Јаве</comment>
+ <comment xml:lang="sv">Java-bytekod</comment>
+ <comment xml:lang="tr">Java derlenmiş kodu</comment>
+ <comment xml:lang="uk">Байт-код Java</comment>
+ <comment xml:lang="vi">Mã byte Java</comment>
+ <comment xml:lang="zh_CN">Java 字节码</comment>
+ <comment xml:lang="zh_TW">Java 位元組碼</comment>
+ </mime-type>
+ <mime-type type="application/x-compress">
+ <comment>UNIX-compressed file</comment>
+ <comment xml:lang="ar">ملف يونكس-مضغوط</comment>
+ <comment xml:lang="be@latin">Skampresavany UNIX-fajł</comment>
+ <comment xml:lang="bg">Файл — компресиран за UNIX</comment>
+ <comment xml:lang="ca">fitxer amb compressió UNIX</comment>
+ <comment xml:lang="cs">soubor komprimovaný v Unixu</comment>
+ <comment xml:lang="da">UNIX-komprimeret fil</comment>
+ <comment xml:lang="de">UNIX-komprimierte Datei</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο UNIX</comment>
+ <comment xml:lang="en_GB">UNIX-compressed file</comment>
+ <comment xml:lang="eo">UNIX-kunpremita dosiero</comment>
+ <comment xml:lang="es">archivo comprimido de Unix</comment>
+ <comment xml:lang="eu">UNIX-en konprimitutako fitxategia</comment>
+ <comment xml:lang="fi">UNIX-pakattu tiedosto</comment>
+ <comment xml:lang="fo">UNIX-stappað fíla</comment>
+ <comment xml:lang="fr">fichier compressé UNIX</comment>
+ <comment xml:lang="ga">comhad UNIX-comhbhrúite</comment>
+ <comment xml:lang="gl">ficheiro comprimido de UNIX</comment>
+ <comment xml:lang="he">קובץ בכיווץ UNIX</comment>
+ <comment xml:lang="hr">UNIX sažeta datoteka</comment>
+ <comment xml:lang="hu">Tömörített UNIX-fájl</comment>
+ <comment xml:lang="ia">File comprimite de UNIX</comment>
+ <comment xml:lang="id">Berkas terkompresi UNIX</comment>
+ <comment xml:lang="it">File compresso-UNIX</comment>
+ <comment xml:lang="ja">UNIX-compress ファイル</comment>
+ <comment xml:lang="kk">файл (UNIX-сығылған)</comment>
+ <comment xml:lang="ko">UNIX 압축 파일</comment>
+ <comment xml:lang="lt">UNIX suglaudintas failas</comment>
+ <comment xml:lang="lv">UNIX saspiesta datne</comment>
+ <comment xml:lang="ms">Fail termampat-UNIX</comment>
+ <comment xml:lang="nb">UNIX-komprimert fil</comment>
+ <comment xml:lang="nl">UNIX-ingepakt bestand</comment>
+ <comment xml:lang="nn">UNIX-komprimert fil</comment>
+ <comment xml:lang="oc">fichièr compressat UNIX</comment>
+ <comment xml:lang="pl">Skompresowany plik systemu UNIX</comment>
+ <comment xml:lang="pt">ficheiro comprimido UNIX</comment>
+ <comment xml:lang="pt_BR">Arquivo compactado do UNIX</comment>
+ <comment xml:lang="ro">Fișier comprimat UNIX</comment>
+ <comment xml:lang="ru">Файл (UNIX-сжатый)</comment>
+ <comment xml:lang="sk">Súbor komprimovaný v Unixe</comment>
+ <comment xml:lang="sl">Skrčena Unix datoteka</comment>
+ <comment xml:lang="sq">File i kompresuar UNIX</comment>
+ <comment xml:lang="sr">датотека запакована ЈУНИКС-ом</comment>
+ <comment xml:lang="sv">UNIX-komprimerad fil</comment>
+ <comment xml:lang="tr">UNIX-sıkıştırılmış dosyası</comment>
+ <comment xml:lang="uk">стиснений файл UNIX</comment>
+ <comment xml:lang="vi">Tập tin đã nén UNIX</comment>
+ <comment xml:lang="zh_CN">UNIX 压缩文件</comment>
+ <comment xml:lang="zh_TW">UNIX 格式壓縮檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="\037\235" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.Z"/>
+ </mime-type>
+ <mime-type type="application/x-compressed-tar">
+ <comment>Tar archive (gzip-compressed)</comment>
+ <comment xml:lang="ar">أرشيف Tar (مضغوط-gzip)</comment>
+ <comment xml:lang="be@latin">Archiŭ tar (gzip-skampresavany)</comment>
+ <comment xml:lang="bg">Архив — tar, компресиран с gzip</comment>
+ <comment xml:lang="ca">arxiu tar (amb compressió gzip)</comment>
+ <comment xml:lang="cs">archiv tar (komprimovaný pomocí gzip)</comment>
+ <comment xml:lang="da">Tar-arkiv (gzip-komprimeret)</comment>
+ <comment xml:lang="de">Tar-Archiv (gzip-komprimiert)</comment>
+ <comment xml:lang="el">Αρχείο Tar (συμπιεσμένο με gzip)</comment>
+ <comment xml:lang="en_GB">Tar archive (gzip-compressed)</comment>
+ <comment xml:lang="es">archivador Tar (comprimido con gzip)</comment>
+ <comment xml:lang="eu">Tar artxiboa (gzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">Tar-arkisto (gzip-pakattu)</comment>
+ <comment xml:lang="fo">Tar skjalasavn (gzip-stappað)</comment>
+ <comment xml:lang="fr">archive tar (compressée gzip)</comment>
+ <comment xml:lang="ga">cartlann Tar (comhbhrúite le gzip)</comment>
+ <comment xml:lang="gl">arquivo Tar (comprimido con gzip)</comment>
+ <comment xml:lang="he">ארכיון Tar (מכווץ ע״י gzip)</comment>
+ <comment xml:lang="hr">Tar arhiva (gzip sažeta)</comment>
+ <comment xml:lang="hu">Tar archívum (gzip-pel tömörítve)</comment>
+ <comment xml:lang="ia">Archivo Tar (comprimite con gzip)</comment>
+ <comment xml:lang="id">Arsip Tar (terkompresi gzip)</comment>
+ <comment xml:lang="it">Archivio tar (compresso con gzip)</comment>
+ <comment xml:lang="ja">Tar アーカイブ (gzip 圧縮)</comment>
+ <comment xml:lang="kk">Tar архиві (gzip-пен сығылған)</comment>
+ <comment xml:lang="ko">TAR 묶음 파일(GZIP 압축)</comment>
+ <comment xml:lang="lt">Tar archyvas (suglaudintas su gzip)</comment>
+ <comment xml:lang="lv">Tar arhīvs (saspiests ar gzip)</comment>
+ <comment xml:lang="nb">Tar-arkiv (gzip-komprimert)</comment>
+ <comment xml:lang="nl">Tar-archief (ingepakt met gzip)</comment>
+ <comment xml:lang="nn">Tar-arkiv (pakka med gzip)</comment>
+ <comment xml:lang="oc">archiu tar (compressat gzip)</comment>
+ <comment xml:lang="pl">Archiwum tar (kompresja gzip)</comment>
+ <comment xml:lang="pt">arquivo Tar (compressão gzip)</comment>
+ <comment xml:lang="pt_BR">Pacote Tar (compactado com gzip)</comment>
+ <comment xml:lang="ro">Arhivă Tar (comprimată gzip)</comment>
+ <comment xml:lang="ru">Архив TAR (сжатый gzip)</comment>
+ <comment xml:lang="sk">Archív tar (komprimovaný pomocou gzip)</comment>
+ <comment xml:lang="sl">Datoteka arhiva Tar (stisnjen z gzip)</comment>
+ <comment xml:lang="sq">Arkiv tar (i kompresuar me gzip)</comment>
+ <comment xml:lang="sr">Тар архива (запакована гзипом)</comment>
+ <comment xml:lang="sv">Tar-arkiv (gzip-komprimerat)</comment>
+ <comment xml:lang="tr">Tar arşivi (gzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">архів tar (стиснений gzip)</comment>
+ <comment xml:lang="vi">Kho nén tar (đã nén gzip)</comment>
+ <comment xml:lang="zh_CN">Tar 归档文件(gzip 压缩)</comment>
+ <comment xml:lang="zh_TW">Tar 封存檔 (gzip 格式壓縮)</comment>
+ <sub-class-of type="application/gzip"/>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.tar.gz"/>
+ <glob pattern="*.tgz"/>
+ </mime-type>
+ <mime-type type="application/x-core">
+ <comment>program crash data</comment>
+ <comment xml:lang="ar">معلومات انهيار البرنامج</comment>
+ <comment xml:lang="be@latin">źviestki złamanaj prahramy</comment>
+ <comment xml:lang="bg">Данни от забиване на програма</comment>
+ <comment xml:lang="ca">dades de fallada de programa</comment>
+ <comment xml:lang="cs">data o pádu programu</comment>
+ <comment xml:lang="da">programnedbrudsdata</comment>
+ <comment xml:lang="de">Daten zu Programmabsturz</comment>
+ <comment xml:lang="el">δεδομένα από την κατάρρευση προγράμματος</comment>
+ <comment xml:lang="en_GB">program crash data</comment>
+ <comment xml:lang="eo">datumo pri kraŝo de programo</comment>
+ <comment xml:lang="es">datos de cuelgue de programa</comment>
+ <comment xml:lang="eu">programaren kraskaduraren datuak</comment>
+ <comment xml:lang="fi">ohjelman kaatumistiedot</comment>
+ <comment xml:lang="fo">forrits sordáta</comment>
+ <comment xml:lang="fr">données de plantage de programme</comment>
+ <comment xml:lang="ga">sonraí tuairte ríomhchláir</comment>
+ <comment xml:lang="gl">datos de colgue do programa</comment>
+ <comment xml:lang="he">מידע מקריסת תכנית</comment>
+ <comment xml:lang="hr">podaci o rušenju programa</comment>
+ <comment xml:lang="hu">összeomlott program adatai</comment>
+ <comment xml:lang="ia">Datos de fallimento de programma</comment>
+ <comment xml:lang="id">data program macet</comment>
+ <comment xml:lang="it">Dati crash di applicazione</comment>
+ <comment xml:lang="ja">プログラムクラッシュデータ</comment>
+ <comment xml:lang="kk">апатты аяқтаудың мәліметтері</comment>
+ <comment xml:lang="ko">프로그램 비정상 종료 데이터</comment>
+ <comment xml:lang="lt">programos nulūžimo duomenys</comment>
+ <comment xml:lang="lv">programmas avārijas dati</comment>
+ <comment xml:lang="ms">Data program musnah</comment>
+ <comment xml:lang="nb">krasjdata fra program</comment>
+ <comment xml:lang="nl">programma-crashgegevens</comment>
+ <comment xml:lang="nn">data om programkrasj</comment>
+ <comment xml:lang="oc">donadas de plantage de programa</comment>
+ <comment xml:lang="pl">Dane awarii programu</comment>
+ <comment xml:lang="pt">dados de rebentamento de aplicação</comment>
+ <comment xml:lang="pt_BR">Dados de travamento de programa</comment>
+ <comment xml:lang="ro">date eroare program</comment>
+ <comment xml:lang="ru">Данные аварийного завершения программы</comment>
+ <comment xml:lang="sk">Údaje o páde programu</comment>
+ <comment xml:lang="sl">podatki sesutja programa</comment>
+ <comment xml:lang="sq">Të dhëna nga programi i bllokuar</comment>
+ <comment xml:lang="sr">подаци о падовима програма</comment>
+ <comment xml:lang="sv">programkraschdata</comment>
+ <comment xml:lang="tr">program çökme verisi</comment>
+ <comment xml:lang="uk">аварійні дані про програму</comment>
+ <comment xml:lang="vi">dữ liệu sụp đổ chương trình</comment>
+ <comment xml:lang="zh_CN">程序崩溃数据</comment>
+ <comment xml:lang="zh_TW">程式當掉資料</comment>
+ <magic priority="50">
+ <match value="\177ELF \004" type="string" offset="0" mask="0xffffffff000000000000000000000000ff"/>
+ <match value="\177ELF" type="string" offset="0">
+ <match value="1" type="byte" offset="5">
+ <match value="4" type="little16" offset="16"/>
+ </match>
+ </match>
+ <match value="\177ELF" type="string" offset="0">
+ <match value="2" type="byte" offset="5">
+ <match value="4" type="big16" offset="16"/>
+ </match>
+ </match>
+ <match value="Core\001" type="string" offset="0"/>
+ <match value="Core\002" type="string" offset="0"/>
+ </magic>
+ <glob pattern="core" case-sensitive="true"/>
+ </mime-type>
+ <mime-type type="application/x-cpio">
+ <comment>CPIO archive</comment>
+ <comment xml:lang="ar">أرشيف CPIO</comment>
+ <comment xml:lang="ast">Archivu CPIO</comment>
+ <comment xml:lang="az">CPIO arxivi</comment>
+ <comment xml:lang="be@latin">Archiŭ CPIO</comment>
+ <comment xml:lang="bg">Архив — CPIO</comment>
+ <comment xml:lang="ca">arxiu CPIO</comment>
+ <comment xml:lang="cs">archiv CPIO</comment>
+ <comment xml:lang="cy">Archif CPIO</comment>
+ <comment xml:lang="da">CPIO-arkiv</comment>
+ <comment xml:lang="de">CPIO-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο CPIO</comment>
+ <comment xml:lang="en_GB">CPIO archive</comment>
+ <comment xml:lang="eo">CPIO-arkivo</comment>
+ <comment xml:lang="es">archivador CPIO</comment>
+ <comment xml:lang="eu">CPIO artxiboa</comment>
+ <comment xml:lang="fi">CPIO-arkisto</comment>
+ <comment xml:lang="fo">CPIO skjalasavn</comment>
+ <comment xml:lang="fr">archive CPIO</comment>
+ <comment xml:lang="ga">cartlann CPIO</comment>
+ <comment xml:lang="gl">arquivo CPIO</comment>
+ <comment xml:lang="he">ארכיון CPIO</comment>
+ <comment xml:lang="hr">CPIO arhiva</comment>
+ <comment xml:lang="hu">CPIO-archívum</comment>
+ <comment xml:lang="ia">Archivo CPIO</comment>
+ <comment xml:lang="id">Arsip CPIO</comment>
+ <comment xml:lang="it">Archivio CPIO</comment>
+ <comment xml:lang="ja">CPIO アーカイブ</comment>
+ <comment xml:lang="ka">CPIO არქივი</comment>
+ <comment xml:lang="kk">CPIO архиві</comment>
+ <comment xml:lang="ko">CPIO 묶음 파일</comment>
+ <comment xml:lang="lt">CPIO archyvas</comment>
+ <comment xml:lang="lv">CPIO arhīvs</comment>
+ <comment xml:lang="ms">Arkib CPIO</comment>
+ <comment xml:lang="nb">CPIO-arkiv</comment>
+ <comment xml:lang="nl">CPIO-archief</comment>
+ <comment xml:lang="nn">CPIO-arkiv</comment>
+ <comment xml:lang="oc">archiu CPIO</comment>
+ <comment xml:lang="pl">Archiwum CPIO</comment>
+ <comment xml:lang="pt">arquivo CPIO</comment>
+ <comment xml:lang="pt_BR">Pacote CPIO</comment>
+ <comment xml:lang="ro">Arhivă CPIO</comment>
+ <comment xml:lang="ru">Архив CPIO</comment>
+ <comment xml:lang="sk">Archív CPIO</comment>
+ <comment xml:lang="sl">Datoteka arhiva CPIO</comment>
+ <comment xml:lang="sq">Arkiv CPIO</comment>
+ <comment xml:lang="sr">ЦПИО архива</comment>
+ <comment xml:lang="sv">CPIO-arkiv</comment>
+ <comment xml:lang="tr">CPIO arşivi</comment>
+ <comment xml:lang="uk">архів CPIO</comment>
+ <comment xml:lang="vi">Kho nén CPIO</comment>
+ <comment xml:lang="zh_CN">CPIO 归档文件</comment>
+ <comment xml:lang="zh_TW">CPIO 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="070707" type="host16" offset="0"/>
+ <match value="070701" type="string" offset="0"/>
+ <match value="070702" type="string" offset="0"/>
+ <match value="0143561" type="host16" offset="0"/>
+ </magic>
+ <glob pattern="*.cpio"/>
+ </mime-type>
+ <mime-type type="application/x-cpio-compressed">
+ <comment>CPIO archive (gzip-compressed)</comment>
+ <comment xml:lang="ar">أرشيف CPIO (مضغوط-gzip)</comment>
+ <comment xml:lang="az">CPIO arxivi (gzip ilə sıxışdırılmış)</comment>
+ <comment xml:lang="be@latin">Archiŭ CPIO (gzip-skampresavany)</comment>
+ <comment xml:lang="bg">Архив — CPIO, компресиран с gzip</comment>
+ <comment xml:lang="ca">arxiu CPIO (amb compressió gzip)</comment>
+ <comment xml:lang="cs">archiv CPIO (komprimovaný pomocí gzip)</comment>
+ <comment xml:lang="cy">Archif CPIO (gywasgwyd drwy gzip)</comment>
+ <comment xml:lang="da">CPIO-arkiv (gzip-komprimeret)</comment>
+ <comment xml:lang="de">CPIO-Archiv (gzip-komprimiert)</comment>
+ <comment xml:lang="el">Αρχείο CPIO (συμπιεσμένο με gzip)</comment>
+ <comment xml:lang="en_GB">CPIO archive (gzip-compressed)</comment>
+ <comment xml:lang="eo">CPIO-arkivo (kunpremita per gzip)</comment>
+ <comment xml:lang="es">archivador CPIO (comprimido con gzip)</comment>
+ <comment xml:lang="eu">CPIO artxiboa (gzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">CPIO-arkisto (gzip-pakattu)</comment>
+ <comment xml:lang="fo">CPIO skjalasavn (gzip-stappað)</comment>
+ <comment xml:lang="fr">archive CPIO (compressé gzip)</comment>
+ <comment xml:lang="ga">cartlann CPIO (comhbhrúite le gzip)</comment>
+ <comment xml:lang="gl">arquivo CPIO (comprimido con gzip)</comment>
+ <comment xml:lang="he">ארכיון CPIO (מכווץ ע״י gzip)</comment>
+ <comment xml:lang="hr">CPIO arhiva (gzip sažeta)</comment>
+ <comment xml:lang="hu">CPIO-archívum (gzip-pel tömörítve)</comment>
+ <comment xml:lang="ia">Archivo CPIO (comprimite con gzip)</comment>
+ <comment xml:lang="id">Arsip CPIO (terkompresi gzip)</comment>
+ <comment xml:lang="it">Archivio CPIO (compresso con gzip)</comment>
+ <comment xml:lang="ja">CPIO (gzip 圧縮) アーカイブ</comment>
+ <comment xml:lang="ka">CPIO არქივი (gzip-ით შეკუმშული)</comment>
+ <comment xml:lang="kk">CPIO архиві (gzip-пен сығылған)</comment>
+ <comment xml:lang="ko">CPIO 묶음 파일(GZIP 압축)</comment>
+ <comment xml:lang="lt">CPIO archyvas (suglaudintas su gzip)</comment>
+ <comment xml:lang="lv">CPIO arhīvs (saspiests ar gzip)</comment>
+ <comment xml:lang="ms">Arkib CPIO (dimampatkan-gzip)</comment>
+ <comment xml:lang="nb">CPIO-arkiv (gzip-komprimert)</comment>
+ <comment xml:lang="nl">CPIO-archief (ingepakt met gzip)</comment>
+ <comment xml:lang="nn">CPIO-arkiv (gzip-pakka)</comment>
+ <comment xml:lang="oc">archiu CPIO (compressat gzip)</comment>
+ <comment xml:lang="pl">Archiwum CPIO (kompresja gzip)</comment>
+ <comment xml:lang="pt">arquivo CPIO (compressão gzip)</comment>
+ <comment xml:lang="pt_BR">Pacote CPIO (compactado com gzip)</comment>
+ <comment xml:lang="ro">Arhivă CPIO (compresie gzip)</comment>
+ <comment xml:lang="ru">Архив CPIO (сжатый gzip)</comment>
+ <comment xml:lang="sk">Archív CPIO (komprimovaný pomocou gzip)</comment>
+ <comment xml:lang="sl">Datoteka arhiva CPIO (skrčena z gzip)</comment>
+ <comment xml:lang="sq">Arkiv CPIO (kompresuar me gzip)</comment>
+ <comment xml:lang="sr">ЦПИО архива (компресована гзип-ом)</comment>
+ <comment xml:lang="sv">CPIO-arkiv (gzip-komprimerat)</comment>
+ <comment xml:lang="tr">CPIO arşivi (gzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">архів CPIO (стиснений gzip)</comment>
+ <comment xml:lang="vi">Kho nén CPIO (đã nén gzip)</comment>
+ <comment xml:lang="zh_CN">CPIO 归档文件(gzip 压缩)</comment>
+ <comment xml:lang="zh_TW">CPIO 封存檔 (gzip 格式壓縮)</comment>
+ <sub-class-of type="application/gzip"/>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.cpio.gz"/>
+ </mime-type>
+ <mime-type type="application/x-csh">
+ <comment>C shell script</comment>
+ <comment xml:lang="ar">سكربت شِل سي</comment>
+ <comment xml:lang="az">C qabıq skripti</comment>
+ <comment xml:lang="be@latin">Skrypt abałonki C</comment>
+ <comment xml:lang="bg">Скрипт — обвивка C</comment>
+ <comment xml:lang="ca">script C shell</comment>
+ <comment xml:lang="cs">skript shellu C</comment>
+ <comment xml:lang="cy">Sgript plisgyn C</comment>
+ <comment xml:lang="da">C-skalprogram</comment>
+ <comment xml:lang="de">C-Shell-Skript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών κελύφους C</comment>
+ <comment xml:lang="en_GB">C shell script</comment>
+ <comment xml:lang="eo">skripto de C-ŝelo</comment>
+ <comment xml:lang="es">secuencia de órdenes de consola en C</comment>
+ <comment xml:lang="eu">C shell script-a</comment>
+ <comment xml:lang="fi">Csh-komentotiedosto</comment>
+ <comment xml:lang="fo">C skel boðrøð</comment>
+ <comment xml:lang="fr">script C shell</comment>
+ <comment xml:lang="ga">script bhlaoisce C</comment>
+ <comment xml:lang="gl">script de C shell</comment>
+ <comment xml:lang="he">תסריט מעטפת C</comment>
+ <comment xml:lang="hr">C skripta ljuske</comment>
+ <comment xml:lang="hu">C héj-parancsfájl</comment>
+ <comment xml:lang="ia">Script C-shell</comment>
+ <comment xml:lang="id">Skrip shell C</comment>
+ <comment xml:lang="it">Script C shell</comment>
+ <comment xml:lang="ja">C シェルスクリプト</comment>
+ <comment xml:lang="kk">C shell сценарийі</comment>
+ <comment xml:lang="ko">C 셸 스크립트</comment>
+ <comment xml:lang="lt">C shell scenarijus</comment>
+ <comment xml:lang="lv">C čaulas skripts</comment>
+ <comment xml:lang="ms">Skrip shell C</comment>
+ <comment xml:lang="nb">C-skallskript</comment>
+ <comment xml:lang="nl">C-shellscript</comment>
+ <comment xml:lang="nn">C-skalskript</comment>
+ <comment xml:lang="oc">escript C shell</comment>
+ <comment xml:lang="pl">Skrypt powłoki C</comment>
+ <comment xml:lang="pt">script de terminal C</comment>
+ <comment xml:lang="pt_BR">Script de shell C</comment>
+ <comment xml:lang="ro">Script C shell</comment>
+ <comment xml:lang="ru">Сценарий C shell</comment>
+ <comment xml:lang="sk">Skript shellu C</comment>
+ <comment xml:lang="sl">Skriptna datoteka lupine C</comment>
+ <comment xml:lang="sq">Script shell C</comment>
+ <comment xml:lang="sr">скрипта Ц шкољке</comment>
+ <comment xml:lang="sv">Skalskript (csh)</comment>
+ <comment xml:lang="tr">C kabuk betiği</comment>
+ <comment xml:lang="uk">скрипт оболонки C</comment>
+ <comment xml:lang="vi">Văn lệnh trình bao C</comment>
+ <comment xml:lang="zh_CN">C shell 脚本</comment>
+ <comment xml:lang="zh_TW">C shell 指令稿</comment>
+ <sub-class-of type="application/x-shellscript"/>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-script"/>
+ <magic>
+ <match value="/bin/tcsh" type="string" offset="2:16"/>
+ <match value="/bin/csh" type="string" offset="2:16"/>
+ <match value="/bin/env csh" type="string" offset="2:16"/>
+ <match value="/bin/env tcsh" type="string" offset="2:16"/>
+ </magic>
+ <glob pattern="*.csh"/>
+ </mime-type>
+ <mime-type type="application/x-dbf">
+ <comment>Xbase document</comment>
+ <comment xml:lang="ar">مستند Xbase</comment>
+ <comment xml:lang="ast">Documentu Xbase</comment>
+ <comment xml:lang="be@latin">Dakument Xbase</comment>
+ <comment xml:lang="bg">Документ — Xbase</comment>
+ <comment xml:lang="ca">document Xbase</comment>
+ <comment xml:lang="cs">dokument Xbase</comment>
+ <comment xml:lang="da">Xbasedokument</comment>
+ <comment xml:lang="de">Xbase-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Xbase</comment>
+ <comment xml:lang="en_GB">Xbase document</comment>
+ <comment xml:lang="eo">Xbase-dokumento</comment>
+ <comment xml:lang="es">documento Xbase</comment>
+ <comment xml:lang="eu">Xbase dokumentua</comment>
+ <comment xml:lang="fi">Xbase-asiakirja</comment>
+ <comment xml:lang="fo">Xbase skjal</comment>
+ <comment xml:lang="fr">document Xbase</comment>
+ <comment xml:lang="ga">cáipéis Xbase</comment>
+ <comment xml:lang="gl">documento Xbase</comment>
+ <comment xml:lang="he">מסמך Xbase</comment>
+ <comment xml:lang="hr">Xbase dokumenet</comment>
+ <comment xml:lang="hu">Xbase dokumentum</comment>
+ <comment xml:lang="ia">Documento Xbase</comment>
+ <comment xml:lang="id">Dokumen Xbase</comment>
+ <comment xml:lang="it">Documento Xbase</comment>
+ <comment xml:lang="ja">Xbase ドキュメント</comment>
+ <comment xml:lang="kk">Xbase құжаты</comment>
+ <comment xml:lang="ko">Xbase 문서</comment>
+ <comment xml:lang="lt">Xbase dokumentas</comment>
+ <comment xml:lang="lv">Xbase dokuments</comment>
+ <comment xml:lang="nb">Xbase-dokument</comment>
+ <comment xml:lang="nl">Xbase-document</comment>
+ <comment xml:lang="nn">Xbase-dokument</comment>
+ <comment xml:lang="oc">document Xbase</comment>
+ <comment xml:lang="pl">Dokument Xbase</comment>
+ <comment xml:lang="pt">documento Xbase</comment>
+ <comment xml:lang="pt_BR">Documento do Xbase</comment>
+ <comment xml:lang="ro">Document Xbase</comment>
+ <comment xml:lang="ru">Документ Xbase</comment>
+ <comment xml:lang="sk">Dokument Xbase</comment>
+ <comment xml:lang="sl">Dokument Xbase</comment>
+ <comment xml:lang="sq">Dokument Xbase</comment>
+ <comment xml:lang="sr">документ Иксбазе</comment>
+ <comment xml:lang="sv">Xbase-dokument</comment>
+ <comment xml:lang="tr">Xbase belgesi</comment>
+ <comment xml:lang="uk">документ Xbase</comment>
+ <comment xml:lang="vi">Tài liệu Xbase</comment>
+ <comment xml:lang="zh_CN">Xbase 文档</comment>
+ <comment xml:lang="zh_TW">Xbase 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.dbf"/>
+ <alias type="application/x-dbase"/>
+ <alias type="application/dbf"/>
+ <alias type="application/dbase"/>
+ </mime-type>
+ <mime-type type="application/ecmascript">
+ <comment>ECMAScript program</comment>
+ <comment xml:lang="ar">برنامج ECMAScript</comment>
+ <comment xml:lang="be@latin">Prahrama ECMAScript</comment>
+ <comment xml:lang="bg">Програма — ECMAScript</comment>
+ <comment xml:lang="ca">programa ECMAScript</comment>
+ <comment xml:lang="cs">program v jazyce ECMAScript</comment>
+ <comment xml:lang="da">ECMA-program</comment>
+ <comment xml:lang="de">ECMAScript-Programm</comment>
+ <comment xml:lang="el">Πρόγραμμα ECMAScript</comment>
+ <comment xml:lang="en_GB">ECMAScript program</comment>
+ <comment xml:lang="es">programa en ECMAScript</comment>
+ <comment xml:lang="eu">ECMAScript programa</comment>
+ <comment xml:lang="fi">ECMAScript-ohjelma</comment>
+ <comment xml:lang="fo">ECMAScript forrit</comment>
+ <comment xml:lang="fr">programme ECMAScript</comment>
+ <comment xml:lang="ga">ríomhchlár ECMAScript</comment>
+ <comment xml:lang="gl">programa en ECMAScript</comment>
+ <comment xml:lang="he">תכנית EMCAScript</comment>
+ <comment xml:lang="hr">ECMAScript program</comment>
+ <comment xml:lang="hu">ECMAScript program</comment>
+ <comment xml:lang="ia">Programma ECMAScript</comment>
+ <comment xml:lang="id">Program ECMAScript</comment>
+ <comment xml:lang="it">Programma ECMAScript</comment>
+ <comment xml:lang="ja">ECMAScript プログラム</comment>
+ <comment xml:lang="ka">ECMAScript პროგრამა</comment>
+ <comment xml:lang="kk">ECMAScript программасы</comment>
+ <comment xml:lang="ko">ECMAScript 프로그램</comment>
+ <comment xml:lang="lt">ECMAScript programa</comment>
+ <comment xml:lang="lv">ECMAScript programma</comment>
+ <comment xml:lang="nb">ECMAScript-program</comment>
+ <comment xml:lang="nl">ECMAScript-programma</comment>
+ <comment xml:lang="nn">ECMAScript-program</comment>
+ <comment xml:lang="oc">programa ECMAEscript</comment>
+ <comment xml:lang="pl">Pogram ECMAScript</comment>
+ <comment xml:lang="pt">programa ECMAScript</comment>
+ <comment xml:lang="pt_BR">Programa ECMAScript</comment>
+ <comment xml:lang="ro">Program ECMAScript</comment>
+ <comment xml:lang="ru">Программа ECMAScript</comment>
+ <comment xml:lang="sk">Program ECMAScript</comment>
+ <comment xml:lang="sl">Programska datoteka ECMAScript</comment>
+ <comment xml:lang="sq">Program ECMAScript</comment>
+ <comment xml:lang="sr">програм ЕЦМАСкрипте</comment>
+ <comment xml:lang="sv">ECMAScript-program</comment>
+ <comment xml:lang="tr">ECMAScript programı</comment>
+ <comment xml:lang="uk">програма мовою ECMAScript</comment>
+ <comment xml:lang="vi">Chương trình ECMAScript</comment>
+ <comment xml:lang="zh_CN">ECMAScript 程序</comment>
+ <comment xml:lang="zh_TW">ECMAScript 程式</comment>
+ <alias type="text/ecmascript"/>
+ <sub-class-of type='application/x-executable'/>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.es"/>
+ </mime-type>
+ <mime-type type="application/x-sega-cd-rom">
+
+ <comment>Sega CD disc image</comment>
+ <comment xml:lang="ast">Imaxe de discu de Sega CD</comment>
+ <comment xml:lang="ca">imatge de disc de Sega CD</comment>
+ <comment xml:lang="cs">obraz disku CD pro Sega</comment>
+ <comment xml:lang="da">Sega CD-diskaftryk</comment>
+ <comment xml:lang="de">Sega-CD-Datenträgerabbild</comment>
+ <comment xml:lang="en_GB">Sega CD disc image</comment>
+ <comment xml:lang="es">imagen de disco CD de Sega</comment>
+ <comment xml:lang="eu">Sega CD disko irudia</comment>
+ <comment xml:lang="fi">Sega CD -levykuva</comment>
+ <comment xml:lang="fr">image disque Sega CD</comment>
+ <comment xml:lang="ga">íomhá dlúthdhiosca Sega</comment>
+ <comment xml:lang="he">דמות כונן Sega CD</comment>
+ <comment xml:lang="hr">Sega CD slika diska</comment>
+ <comment xml:lang="hu">Sega CD-lemezkép</comment>
+ <comment xml:lang="id">image cakram CD Sega</comment>
+ <comment xml:lang="it">Immagine disco Sega Mega CD</comment>
+ <comment xml:lang="kk">Sega CD диск бейнесі</comment>
+ <comment xml:lang="ko">세가 CD 디스크 이미지</comment>
+ <comment xml:lang="pl">Obraz płyty konsoli Mega-CD</comment>
+ <comment xml:lang="pt_BR">Imagem de disco Sega CD</comment>
+ <comment xml:lang="ru">Образ диска CD Sega</comment>
+ <comment xml:lang="sk">Obraz disku CD Sega</comment>
+ <comment xml:lang="sr">одраз диска Сега ЦД-а</comment>
+ <comment xml:lang="sv">Mega-CD-skivavbild</comment>
+ <comment xml:lang="tr">Sega CD disk kalıbı</comment>
+ <comment xml:lang="uk">образ диска Sega CD</comment>
+ <comment xml:lang="zh_CN">Sega CD 光盘映像</comment>
+ <comment xml:lang="zh_TW">Sega CD 光碟映像檔</comment>
+ <generic-icon name="application-x-executable"/>
+
+ <magic priority="60">
+ <match value="SEGADISCSYSTEM" type="string" offset="0">
+ <match value="SEGA" type="string" offset="256"/>
+ </match>
+ <match value="SEGADISCSYSTEM" type="string" offset="16">
+ <match value="SEGA" type="string" offset="272"/>
+ </match>
+ </magic>
+ <glob pattern="*.bin"/>
+ <glob pattern="*.iso"/>
+ </mime-type>
+ <mime-type type="application/x-sega-pico-rom">
+
+ <comment>Sega Pico ROM</comment>
+ <comment xml:lang="ast">ROM de Sega Pico</comment>
+ <comment xml:lang="ca">ROM de Sega Pico</comment>
+ <comment xml:lang="cs">ROM pro Sega Pico</comment>
+ <comment xml:lang="de">Sega Pico ROM</comment>
+ <comment xml:lang="en_GB">Sega Pico ROM</comment>
+ <comment xml:lang="es">ROM de Sega Pico</comment>
+ <comment xml:lang="eu">Sega Pico ROM</comment>
+ <comment xml:lang="fi">Sega Pico ROM</comment>
+ <comment xml:lang="fr">ROM Sega Pico</comment>
+ <comment xml:lang="ga">ROM Sega Pico</comment>
+ <comment xml:lang="hr">Sega Pico ROM</comment>
+ <comment xml:lang="hu">Sega Pico ROM</comment>
+ <comment xml:lang="id">ROM Sega Pico</comment>
+ <comment xml:lang="it">ROM Sega Pico</comment>
+ <comment xml:lang="kk">Sega Pico ROM</comment>
+ <comment xml:lang="ko">세카 피코 롬</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Sega Pico</comment>
+ <comment xml:lang="pt_BR">ROM de Sega Pico</comment>
+ <comment xml:lang="ru">Sega Pico ROM</comment>
+ <comment xml:lang="sk">ROM pre Sega Pico</comment>
+ <comment xml:lang="sr">Сега Пико РОМ</comment>
+ <comment xml:lang="sv">Sega Pico-rom</comment>
+ <comment xml:lang="tr">Sega Pico ROM</comment>
+ <comment xml:lang="uk">ППП Sega Pico</comment>
+ <comment xml:lang="zh_CN">Sega Pico ROM</comment>
+ <comment xml:lang="zh_TW">Sega Pico ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="SEGA PICO" type="string" offset="256"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-saturn-rom">
+ <comment>Sega Saturn disc image</comment>
+ <comment xml:lang="ast">Imaxe de discu de Sega Saturn</comment>
+ <comment xml:lang="ca">imatge de disc de Sega Saturn</comment>
+ <comment xml:lang="cs">obraz disku pro Sega Saturn</comment>
+ <comment xml:lang="da">Sega Saturn-diskaftryk</comment>
+ <comment xml:lang="de">Sega-Saturn-Datenträgerabbild</comment>
+ <comment xml:lang="el">Εικόνα δίσκου Sega Saturn</comment>
+ <comment xml:lang="en_GB">Sega Saturn disc image</comment>
+ <comment xml:lang="es">imagen de disco de Sega Saturn</comment>
+ <comment xml:lang="eu">Sega Saturn disko irudia</comment>
+ <comment xml:lang="fi">Sega Saturn -levykuva</comment>
+ <comment xml:lang="fr">image disque Sega Saturn</comment>
+ <comment xml:lang="ga">íomhá diosca Sega Saturn</comment>
+ <comment xml:lang="he">דמות כונן Sega Saturn</comment>
+ <comment xml:lang="hr">Sega Saturn slika diska</comment>
+ <comment xml:lang="hu">Sega Saturn lemezkép</comment>
+ <comment xml:lang="ia">Imagine de disco Sega Saturn</comment>
+ <comment xml:lang="id">Image cakram Sega Saturn</comment>
+ <comment xml:lang="it">Immagine disco Sega Saturn</comment>
+ <comment xml:lang="kk">Sega Saturn диск бейнесі</comment>
+ <comment xml:lang="ko">세가 새턴 디스크 이미지</comment>
+ <comment xml:lang="oc">imatge disc Sega Saturn</comment>
+ <comment xml:lang="pl">Obraz płyty konsoli Sega Saturn</comment>
+ <comment xml:lang="pt">imagem de disco Sega Saturn</comment>
+ <comment xml:lang="pt_BR">Imagem de disco do Sega Saturn</comment>
+ <comment xml:lang="ru">Образ диска Sega Saturn</comment>
+ <comment xml:lang="sk">Obraz disku Sega Saturn</comment>
+ <comment xml:lang="sr">одраз диска Сега Сатурна</comment>
+ <comment xml:lang="sv">Sega Saturn-skivavbild</comment>
+ <comment xml:lang="tr">Sega Saturn disk kalıbı</comment>
+ <comment xml:lang="uk">образ диска Sega Saturn</comment>
+ <comment xml:lang="zh_CN">Sega Saturn 光盘映像</comment>
+ <comment xml:lang="zh_TW">Sega Saturn 光碟映像檔</comment>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="SEGA SEGASATURN" type="string" offset="0"/>
+ <match value="SEGA SEGASATURN" type="string" offset="16"/>
+ </magic>
+ <glob pattern="*.bin"/>
+ <glob pattern="*.iso"/>
+ </mime-type>
+ <mime-type type="application/x-dc-rom">
+ <comment>Dreamcast GD-ROM</comment>
+ <comment xml:lang="ast">GD-ROM de Dreamcast</comment>
+ <comment xml:lang="ca">GD-ROM de Dreamcast</comment>
+ <comment xml:lang="cs">GD-ROM pro Dreamcast</comment>
+ <comment xml:lang="da">Dreamcast GD-ROM</comment>
+ <comment xml:lang="de">Dreamcast GD-ROM</comment>
+ <comment xml:lang="el">Dreamcast GD-ROM</comment>
+ <comment xml:lang="en_GB">Dreamcast GD-ROM</comment>
+ <comment xml:lang="es">GD-ROM de Dreamcast</comment>
+ <comment xml:lang="eu">Dreamcast GD-ROM</comment>
+ <comment xml:lang="fr">GD-ROM Dreamcast</comment>
+ <comment xml:lang="ga">GD-ROM Dreamcast</comment>
+ <comment xml:lang="he">Dreamcast GD-ROM</comment>
+ <comment xml:lang="hr">Dreamcast GD-ROM</comment>
+ <comment xml:lang="hu">Dreamcast GD-ROM</comment>
+ <comment xml:lang="ia">GD-ROM Dreamcast</comment>
+ <comment xml:lang="id">GD-ROM Dreamcast</comment>
+ <comment xml:lang="it">GD-ROM Dreamcast</comment>
+ <comment xml:lang="kk">Dreamcast GD-ROM</comment>
+ <comment xml:lang="ko">드림캐스트 GD-ROM</comment>
+ <comment xml:lang="oc">GD-ROM Dreamcast</comment>
+ <comment xml:lang="pl">Plik GD-ROM konsoli Dreamcast</comment>
+ <comment xml:lang="pt">GD-ROM Dreamcast</comment>
+ <comment xml:lang="pt_BR">GD-ROM de Dreamcast</comment>
+ <comment xml:lang="ru">Dreamcast GD-ROM</comment>
+ <comment xml:lang="sk">Dreamcast GD-ROM</comment>
+ <comment xml:lang="sr">Дримкаст ГД-РОМ</comment>
+ <comment xml:lang="sv">Dreamcast-gd-rom</comment>
+ <comment xml:lang="tr">Dreamcast GD-ROM</comment>
+ <comment xml:lang="uk">GD-ROM Dreamcast</comment>
+ <comment xml:lang="zh_CN">Dreamcast CD-ROM</comment>
+ <comment xml:lang="zh_TW">Dreamcast GD-ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.dc"/>
+ </mime-type>
+ <mime-type type="application/x-nintendo-ds-rom">
+ <comment>Nintendo DS ROM</comment>
+ <comment xml:lang="ar">Nintendo DS ROM</comment>
+ <comment xml:lang="ast">ROM de Nintendo DS</comment>
+ <comment xml:lang="be@latin">Nintendo DS ROM</comment>
+ <comment xml:lang="bg">ROM — Nintendo DS</comment>
+ <comment xml:lang="ca">ROM de Nintendo DS</comment>
+ <comment xml:lang="cs">ROM pro Nintendo DS</comment>
+ <comment xml:lang="da">Nintendo DS-rom</comment>
+ <comment xml:lang="de">Nintendo DS ROM</comment>
+ <comment xml:lang="el">Nintendo DS ROM</comment>
+ <comment xml:lang="en_GB">Nintendo DS ROM</comment>
+ <comment xml:lang="es">ROM de Nintendo DS</comment>
+ <comment xml:lang="eu">Nintendo DS-ko ROMa</comment>
+ <comment xml:lang="fi">Nintendo DS-ROM</comment>
+ <comment xml:lang="fo">Nintendo DS ROM</comment>
+ <comment xml:lang="fr">ROM Nintendo DS</comment>
+ <comment xml:lang="ga">ROM Nintendo DS</comment>
+ <comment xml:lang="gl">ROM de Nintendo DS</comment>
+ <comment xml:lang="he">ROM של Nintendo</comment>
+ <comment xml:lang="hr">Nintendo DS ROM</comment>
+ <comment xml:lang="hu">Nintendo DS ROM</comment>
+ <comment xml:lang="ia">ROM pro Nintendo DS</comment>
+ <comment xml:lang="id">Memori baca-saja Nintendo DS</comment>
+ <comment xml:lang="it">ROM Nintendo DS</comment>
+ <comment xml:lang="ja">Nintendo DS ROM</comment>
+ <comment xml:lang="kk">Nintendo DS ROM</comment>
+ <comment xml:lang="ko">닌텐도 DS 롬</comment>
+ <comment xml:lang="lt">Nintendo DS ROM</comment>
+ <comment xml:lang="lv">Nintendo DS ROM</comment>
+ <comment xml:lang="nb">Nintendo DS-ROM</comment>
+ <comment xml:lang="nl">Nintendo-DS-ROM</comment>
+ <comment xml:lang="nn">Nintendo DS-ROM</comment>
+ <comment xml:lang="oc">ROM Nintendo DS</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Nintendo DS</comment>
+ <comment xml:lang="pt">ROM Nintendo DS</comment>
+ <comment xml:lang="pt_BR">ROM de Nintendo DS</comment>
+ <comment xml:lang="ro">ROM Nintendo DS</comment>
+ <comment xml:lang="ru">Nintendo DS ROM</comment>
+ <comment xml:lang="sk">ROM pre Nintendo DS</comment>
+ <comment xml:lang="sl">Bralni pomnilnik Nintendo DS</comment>
+ <comment xml:lang="sq">ROM Nintendo DS</comment>
+ <comment xml:lang="sr">Нинтендо ДС РОМ</comment>
+ <comment xml:lang="sv">Nintendo DS-rom</comment>
+ <comment xml:lang="tr">Nintendo DS ROM</comment>
+ <comment xml:lang="uk">ППП Nintendo</comment>
+ <comment xml:lang="vi">ROM DS Nintendo</comment>
+ <comment xml:lang="zh_CN">任天堂 DS ROM</comment>
+ <comment xml:lang="zh_TW">任天堂 DS ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.nds"/>
+ </mime-type>
+ <mime-type type="application/x-pc-engine-rom">
+ <comment>PC Engine ROM</comment>
+ <comment xml:lang="ast">ROM de PC Engine</comment>
+ <comment xml:lang="ca">ROM de PC Engine</comment>
+ <comment xml:lang="cs">ROM pro PC Engine</comment>
+ <comment xml:lang="da">PC Engine ROM</comment>
+ <comment xml:lang="de">PC Engine ROM</comment>
+ <comment xml:lang="el">PC Engine ROM</comment>
+ <comment xml:lang="en_GB">PC Engine ROM</comment>
+ <comment xml:lang="es">ROM de PC Engine</comment>
+ <comment xml:lang="eu">PC Engine ROM</comment>
+ <comment xml:lang="fi">PC Engine ROM</comment>
+ <comment xml:lang="fr">ROM PC Engine</comment>
+ <comment xml:lang="ga">ROM PC Engine</comment>
+ <comment xml:lang="gl">ROM de máquina de PC</comment>
+ <comment xml:lang="he">ROM של PC Engine</comment>
+ <comment xml:lang="hr">PC Engine ROM</comment>
+ <comment xml:lang="hu">PC Engine ROM</comment>
+ <comment xml:lang="ia">ROM PC Engine</comment>
+ <comment xml:lang="id">ROM PC Engine</comment>
+ <comment xml:lang="it">ROM PC Engine</comment>
+ <comment xml:lang="kk">PC Engine ROM</comment>
+ <comment xml:lang="ko">PC 엔진 롬</comment>
+ <comment xml:lang="oc">ROM PC Engine</comment>
+ <comment xml:lang="pl">Plik ROM konsoli PC Engine</comment>
+ <comment xml:lang="pt">ROM PC Engine</comment>
+ <comment xml:lang="pt_BR">ROM de PC Engine</comment>
+ <comment xml:lang="ru">PC Engine ROM</comment>
+ <comment xml:lang="sk">PC Engine ROM</comment>
+ <comment xml:lang="sl">Pomnilnik PC Engine ROM</comment>
+ <comment xml:lang="sr">ПЦ Енџин РОМ</comment>
+ <comment xml:lang="sv">PC Engine-rom</comment>
+ <comment xml:lang="tr">PC Engine ROM</comment>
+ <comment xml:lang="uk">ROM для рушія на ПК</comment>
+ <comment xml:lang="zh_CN">PC Engine ROM</comment>
+ <comment xml:lang="zh_TW">PC Engine ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.pce"/>
+ </mime-type>
+ <mime-type type="application/x-wii-rom">
+ <comment>Wii disc image</comment>
+ <comment xml:lang="ast">Imaxe de discu de Wii</comment>
+ <comment xml:lang="ca">imatge de disc de Wii</comment>
+ <comment xml:lang="cs">obraz disku pro Wii</comment>
+ <comment xml:lang="da">Wii-diskaftryk</comment>
+ <comment xml:lang="de">Wii-Datenträgerabbild</comment>
+ <comment xml:lang="el">Εικόνα δίσκου Wii</comment>
+ <comment xml:lang="en_GB">Wii disc image</comment>
+ <comment xml:lang="es">imagen de disco de Wii</comment>
+ <comment xml:lang="eu">Wii disko irudia</comment>
+ <comment xml:lang="fi">Wii-levykuva</comment>
+ <comment xml:lang="fr">image disque Wii</comment>
+ <comment xml:lang="ga">íomhá diosca Wii</comment>
+ <comment xml:lang="gl">Imaxe de disco de Wii</comment>
+ <comment xml:lang="he">דמות כונן Wii</comment>
+ <comment xml:lang="hr">Wii slika diska</comment>
+ <comment xml:lang="hu">Wii lemezkép</comment>
+ <comment xml:lang="ia">Imagine de disco Wii</comment>
+ <comment xml:lang="id">Image disk Wii</comment>
+ <comment xml:lang="it">Immagine disco Wii</comment>
+ <comment xml:lang="kk">Wii диск бейнесі</comment>
+ <comment xml:lang="ko">Wii 디스크 이미지</comment>
+ <comment xml:lang="oc">imatge disc Wii</comment>
+ <comment xml:lang="pl">Obraz płyty konsoli Wii</comment>
+ <comment xml:lang="pt">imagem de disco Wii</comment>
+ <comment xml:lang="pt_BR">Imagem de disco Wii</comment>
+ <comment xml:lang="ru">Образ диска Wii</comment>
+ <comment xml:lang="sk">Obraz disku Wii</comment>
+ <comment xml:lang="sl">Odtis diska Wii</comment>
+ <comment xml:lang="sr">одраз диска Вии-ја</comment>
+ <comment xml:lang="sv">Wii-skivavbild</comment>
+ <comment xml:lang="tr">Wii disk görüntüsü</comment>
+ <comment xml:lang="uk">образ диска Wii</comment>
+ <comment xml:lang="zh_CN">Wii 光盘映像</comment>
+ <comment xml:lang="zh_TW">Wii 光碟映像檔</comment>
+ <alias type="application/x-wii-iso-image"/>
+ <alias type="application/x-wbfs"/>
+ <alias type="application/x-wia"/>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.iso"/>
+ <magic priority="50">
+ <match value="0x5d1c9ea3" type="big32" offset="24"/>
+ <match value="WBFS" type="string" offset="0"/>
+ <match value="WII\001DISC" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-wii-wad">
+ <comment>WiiWare bundle</comment>
+ <comment xml:lang="ca">paquet de WiiWare</comment>
+ <comment xml:lang="cs">balíček pro WiiWare</comment>
+ <comment xml:lang="da">WiiWare-samling</comment>
+ <comment xml:lang="de">WiiWare-Paket</comment>
+ <comment xml:lang="en_GB">WiiWare bundle</comment>
+ <comment xml:lang="es">conjunto de WiiWare</comment>
+ <comment xml:lang="eu">WiiWare bilduma</comment>
+ <comment xml:lang="fr">lot WiiWare</comment>
+ <comment xml:lang="ga">burla WiiWare</comment>
+ <comment xml:lang="he">מאגד WiiWare</comment>
+ <comment xml:lang="hr">WiiWare paket</comment>
+ <comment xml:lang="hu">WiiWare csomag</comment>
+ <comment xml:lang="ia">Pacchetto WiiWare</comment>
+ <comment xml:lang="id">Bundel WiiWare</comment>
+ <comment xml:lang="it">Bundle WiiWare</comment>
+ <comment xml:lang="kk">WiiWare дестесі</comment>
+ <comment xml:lang="ko">위-웨어 번들</comment>
+ <comment xml:lang="oc">lòt WiiWare</comment>
+ <comment xml:lang="pl">Pakiet WiiWare</comment>
+ <comment xml:lang="pt">pacote WiiWare</comment>
+ <comment xml:lang="pt_BR">Pacote WiiWare</comment>
+ <comment xml:lang="ru">Пакет WiiWare</comment>
+ <comment xml:lang="sk">Balík WiiWare</comment>
+ <comment xml:lang="sr">ВииВер комплет</comment>
+ <comment xml:lang="sv">WiiWare-paket</comment>
+ <comment xml:lang="tr">WiiWare paketi</comment>
+ <comment xml:lang="uk">пакет WiiWare</comment>
+ <comment xml:lang="zh_CN">WiiWare 捆绑包</comment>
+ <comment xml:lang="zh_TW">WiiWare 綁包</comment>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="Is\0\0" type="string" offset="4"/>
+ <match value="ib\0\0" type="string" offset="4"/>
+ <match value="Bk\0\0" type="string" offset="4"/>
+ </magic>
+ <glob pattern="*.wad"/>
+ </mime-type>
+ <mime-type type="application/x-gamecube-rom">
+ <comment>GameCube disc image</comment>
+ <comment xml:lang="ca">imatge de disc de GameCube</comment>
+ <comment xml:lang="cs">obraz disku pro GameCube</comment>
+ <comment xml:lang="da">GameCube-diskaftryk</comment>
+ <comment xml:lang="de">GameCube-Datenträgerabbild</comment>
+ <comment xml:lang="el">Εικόνα δίσκου GameCube</comment>
+ <comment xml:lang="en_GB">GameCube disc image</comment>
+ <comment xml:lang="es">imagen de disco de GameCube</comment>
+ <comment xml:lang="eu">GameCube disko irudia</comment>
+ <comment xml:lang="fi">GameCube-levykuva</comment>
+ <comment xml:lang="fr">image disque GameCube</comment>
+ <comment xml:lang="ga">íomhá diosca GameCube</comment>
+ <comment xml:lang="gl">Imae de disco de GameCube</comment>
+ <comment xml:lang="he">דמות כונן GameCube</comment>
+ <comment xml:lang="hr">GameCube slika diska</comment>
+ <comment xml:lang="hu">GameCube lemezkép</comment>
+ <comment xml:lang="ia">Imagine de disco GameCube</comment>
+ <comment xml:lang="id">Image disk GameCube</comment>
+ <comment xml:lang="it">Immagine disco GameCube</comment>
+ <comment xml:lang="kk">GameCube диск бейнесі</comment>
+ <comment xml:lang="ko">게임큐브 디스크 이미지</comment>
+ <comment xml:lang="oc">imatge disc GameCube</comment>
+ <comment xml:lang="pl">Obraz płyty konsoli GameCube</comment>
+ <comment xml:lang="pt">imagem de disco GameCube</comment>
+ <comment xml:lang="pt_BR">Imagem de disco GameCube</comment>
+ <comment xml:lang="ru">Образ диска GameCube</comment>
+ <comment xml:lang="sk">Obraz disku GameCube</comment>
+ <comment xml:lang="sl">Odtis diska GameCube</comment>
+ <comment xml:lang="sr">одраз диска Гејм Коцке</comment>
+ <comment xml:lang="sv">GameCube-skivavbild</comment>
+ <comment xml:lang="tr">GameCube disk görüntüsü</comment>
+ <comment xml:lang="uk">образ диска GameCube</comment>
+ <comment xml:lang="zh_CN">GameCube 光盘映像</comment>
+ <comment xml:lang="zh_TW">GameCube 光碟映像檔</comment>
+ <generic-icon name="application-x-executable"/>
+ <alias type="application/x-gamecube-iso-image"/>
+ <glob pattern="*.iso"/>
+ <magic priority="50">
+ <match value="0xc2339f3d" type="big32" offset="28"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-thomson-cartridge-memo7">
+ <comment>Thomson Mémo7 cartridge</comment>
+ <comment xml:lang="ca">cartutx Thomson Mémo7</comment>
+ <comment xml:lang="cs">Kazeta Thomson Mémo7</comment>
+ <comment xml:lang="de">Thomson-Mémo7-Steckmodul</comment>
+ <comment xml:lang="en_GB">Thomson Mémo7 cartridge</comment>
+ <comment xml:lang="es">cartucho Mémo7 de Thomson</comment>
+ <comment xml:lang="fr">cartouche Thomson Mémo7</comment>
+ <comment xml:lang="ga">cartús Thomson Mémo7</comment>
+ <comment xml:lang="hr">Thomson Mémo7 uložak</comment>
+ <comment xml:lang="hu">Thomson Mémo7 kazetta</comment>
+ <comment xml:lang="id">cartridge Thomson Mémo7</comment>
+ <comment xml:lang="it">Cartuccia Thomson Mémo7</comment>
+ <comment xml:lang="kk">Thomson Mémo7 картриджі</comment>
+ <comment xml:lang="ko">톰슨 메모7 카트릿지</comment>
+ <comment xml:lang="pl">Kartridż Thomson Mémo7</comment>
+ <comment xml:lang="pt_BR">Cartucho Thomson Mémo7</comment>
+ <comment xml:lang="ru">Картридж Thomson Mémo7</comment>
+ <comment xml:lang="sk">Kazeta Thomson Mémo7</comment>
+ <comment xml:lang="sr">Томсон Мемо7 кертриџ</comment>
+ <comment xml:lang="sv">Thomson Mémo7-spelkassett</comment>
+ <comment xml:lang="tr">Thomson Mémo7 kartuşu</comment>
+ <comment xml:lang="uk">картридж Thomson Mémo7</comment>
+ <comment xml:lang="zh_CN">Thomson Mémo7 卡带</comment>
+ <comment xml:lang="zh_TW">Thomson Mémo7 卡匣</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.m7"/>
+ </mime-type>
+ <mime-type type="application/x-thomson-cassette">
+ <comment>Thomson cassette</comment>
+ <comment xml:lang="ca">cinta de casset Thomson</comment>
+ <comment xml:lang="cs">Kazeta Thomson</comment>
+ <comment xml:lang="de">Thomson-Kassette</comment>
+ <comment xml:lang="en_GB">Thomson cassette</comment>
+ <comment xml:lang="es">casete de Thomson</comment>
+ <comment xml:lang="fr">cassette Thomson</comment>
+ <comment xml:lang="ga">caiséad Thomson</comment>
+ <comment xml:lang="hr">Thomson kaseta</comment>
+ <comment xml:lang="hu">Thomson kazetta</comment>
+ <comment xml:lang="id">kaset Thomson</comment>
+ <comment xml:lang="it">Cassetta Thomson</comment>
+ <comment xml:lang="kk">Thomson кассетасы</comment>
+ <comment xml:lang="ko">톰슨 카셋트</comment>
+ <comment xml:lang="pl">Kaseta Thomson</comment>
+ <comment xml:lang="pt_BR">Cassete Thomson</comment>
+ <comment xml:lang="ru">Кассета Thomson</comment>
+ <comment xml:lang="sk">Kazeta Thomson</comment>
+ <comment xml:lang="sr">Томсон касете</comment>
+ <comment xml:lang="sv">Thomson-kassett</comment>
+ <comment xml:lang="tr">Thomson kaset</comment>
+ <comment xml:lang="uk">касета Thomson</comment>
+ <comment xml:lang="zh_CN">Thomson 磁带</comment>
+ <comment xml:lang="zh_TW">Thomson 卡匣</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.k7"/>
+ </mime-type>
+ <mime-type type="application/x-hfe-floppy-image">
+ <comment>HFE floppy disk image</comment>
+ <comment xml:lang="ca">imatge de disquet HFE</comment>
+ <comment xml:lang="cs">Obraz diskety HFE</comment>
+ <comment xml:lang="de">HFE-Diskettenabbild</comment>
+ <comment xml:lang="en_GB">HFE floppy disk image</comment>
+ <comment xml:lang="es">imagen de disquete HFE</comment>
+ <comment xml:lang="fr">image disquette HFE</comment>
+ <comment xml:lang="ga">íomhá diosca fhlapaigh HFE</comment>
+ <comment xml:lang="hr">HFE slika diskete</comment>
+ <comment xml:lang="hu">HFE flopi lemezkép</comment>
+ <comment xml:lang="id">image disk floppy HFE</comment>
+ <comment xml:lang="it">Immagine disco floppy HFE</comment>
+ <comment xml:lang="kk">HFE иілгіш диск бейнесі</comment>
+ <comment xml:lang="ko">HFE 플로피 디스크 이미지</comment>
+ <comment xml:lang="pl">Obraz dyskietki HFE</comment>
+ <comment xml:lang="pt_BR">Imagem de disco flexível HFE</comment>
+ <comment xml:lang="ru">Образ гибкого диска HFE</comment>
+ <comment xml:lang="sk">Obraz pružného disku HFE</comment>
+ <comment xml:lang="sr">ХФЕ слика флопи диска</comment>
+ <comment xml:lang="sv">HFE-diskavbild</comment>
+ <comment xml:lang="tr">HFE disket kalıbı</comment>
+ <comment xml:lang="uk">образ дискети HFE</comment>
+ <comment xml:lang="zh_CN">HFE 软盘映像</comment>
+ <comment xml:lang="zh_TW">HFE 軟碟映像檔</comment>
+ <acronym>HFE</acronym>
+ <expanded-acronym>HxC Floppy Emulator</expanded-acronym>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.hfe"/>
+ <magic>
+ <match value="HXCPICFE" type="string" offset="0"/>
+ </magic>
+ <alias type="application/x-hfe-file"/>
+ </mime-type>
+ <mime-type type="application/x-thomson-sap-image">
+ <comment>SAP Thomson floppy disk image</comment>
+ <comment xml:lang="ca">imatge de disquet SAP Thomson</comment>
+ <comment xml:lang="cs">Obraz diskety SAP Thomson</comment>
+ <comment xml:lang="de">SAP-Thomson-Diskettenabbild</comment>
+ <comment xml:lang="en_GB">SAP Thomson floppy disk image</comment>
+ <comment xml:lang="es">imagen de disquete SAP de Thomson</comment>
+ <comment xml:lang="fr">image disquette SAP Thomson</comment>
+ <comment xml:lang="ga">íomhá diosca fhlapaigh SAP Thomson</comment>
+ <comment xml:lang="hr">SAP Thomson slika diskete</comment>
+ <comment xml:lang="hu">SAP Thomson flopi lemezkép</comment>
+ <comment xml:lang="id">image disk floppy SAP Thomson</comment>
+ <comment xml:lang="it">Immagine disco floppy Thomson SAP</comment>
+ <comment xml:lang="kk">SAP Thomson иілгіш диск бейнесі</comment>
+ <comment xml:lang="ko">SAP 톰슨 플로피 디스크 이미지</comment>
+ <comment xml:lang="pl">Obraz dyskietki SAP Thomson</comment>
+ <comment xml:lang="pt_BR">Imagem de disco flexível SAP Thomson</comment>
+ <comment xml:lang="ru">Образ гибкого диска SAP Thomson</comment>
+ <comment xml:lang="sk">Obraz pružného disku SAP Thomson</comment>
+ <comment xml:lang="sr">САП Томсон слика флопи диска</comment>
+ <comment xml:lang="sv">SAP Thomson-diskavbild</comment>
+ <comment xml:lang="tr">SAP Thomson disket kalıbı</comment>
+ <comment xml:lang="uk">образ дискети Thomson SAP</comment>
+ <comment xml:lang="zh_CN">SAP Thomson 软盘映像</comment>
+ <comment xml:lang="zh_TW">SAP Thomson 軟碟映像檔</comment>
+ <acronym>SAP</acronym>
+ <expanded-acronym>Système d'Archivage Pukall</expanded-acronym>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.sap"/>
+ <magic>
+ <match value="SYSTEME D'ARCHIVAGE PUKALL S.A.P. (c) Alexandre PUKALL Avril 1998" type="string" offset="1"/>
+ </magic>
+ <alias type="application/x-sap-file"/>
+ </mime-type>
+ <mime-type type="application/vnd.debian.binary-package">
+ <comment>Debian package</comment>
+ <comment xml:lang="ar">حزمة ديبيان</comment>
+ <comment xml:lang="az">Debian paketi</comment>
+ <comment xml:lang="be@latin">Pakunak Debian</comment>
+ <comment xml:lang="bg">Пакет — Debian</comment>
+ <comment xml:lang="ca">paquet Debian</comment>
+ <comment xml:lang="cs">balíček Debianu</comment>
+ <comment xml:lang="cy">Pecyn Debian</comment>
+ <comment xml:lang="da">Debianpakke</comment>
+ <comment xml:lang="de">Debian-Paket</comment>
+ <comment xml:lang="el">Πακέτο Debian</comment>
+ <comment xml:lang="en_GB">Debian package</comment>
+ <comment xml:lang="eo">Debian-pakaĵo</comment>
+ <comment xml:lang="es">paquete de Debian</comment>
+ <comment xml:lang="eu">Debian paketea</comment>
+ <comment xml:lang="fi">Debian-paketti</comment>
+ <comment xml:lang="fo">Debian pakki</comment>
+ <comment xml:lang="fr">paquet Debian</comment>
+ <comment xml:lang="ga">pacáiste Debian</comment>
+ <comment xml:lang="gl">paquete de Debian</comment>
+ <comment xml:lang="he">חבילת דביאן</comment>
+ <comment xml:lang="hr">Debian paket</comment>
+ <comment xml:lang="hu">Debian-csomag</comment>
+ <comment xml:lang="ia">Pacchetto Debian</comment>
+ <comment xml:lang="id">Paket Debian</comment>
+ <comment xml:lang="it">Pacchetto Debian</comment>
+ <comment xml:lang="ja">Debian パッケージ</comment>
+ <comment xml:lang="ka">Debian-ის პაკეტი</comment>
+ <comment xml:lang="kk">Debian дестесі</comment>
+ <comment xml:lang="ko">데비안 패키지</comment>
+ <comment xml:lang="lt">Debian paketas</comment>
+ <comment xml:lang="lv">Debian pakotne</comment>
+ <comment xml:lang="ms">Pakej Debian</comment>
+ <comment xml:lang="nb">Debian pakke</comment>
+ <comment xml:lang="nl">Debian-pakket</comment>
+ <comment xml:lang="nn">Debian pakke</comment>
+ <comment xml:lang="oc">paquet Debian</comment>
+ <comment xml:lang="pl">Pakiet Debiana</comment>
+ <comment xml:lang="pt">pacote Debian</comment>
+ <comment xml:lang="pt_BR">Pacote Debian</comment>
+ <comment xml:lang="ro">Pachet Debian</comment>
+ <comment xml:lang="ru">Пакет Debian</comment>
+ <comment xml:lang="sk">Balíček Debianu</comment>
+ <comment xml:lang="sl">Datoteka paketa Debian</comment>
+ <comment xml:lang="sq">Paketë Debian</comment>
+ <comment xml:lang="sr">Дебијанов пакет</comment>
+ <comment xml:lang="sv">Debianpaket</comment>
+ <comment xml:lang="tr">Debian paketi</comment>
+ <comment xml:lang="uk">пакунок Debian</comment>
+ <comment xml:lang="vi">Gói Debian</comment>
+ <comment xml:lang="zh_CN">Debian 软件包</comment>
+ <comment xml:lang="zh_TW">Debian 軟體包</comment>
+ <alias type="application/x-deb"/>
+ <alias type="application/x-debian-package"/>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="!&lt;arch&gt;" type="string" offset="0">
+ <match value="debian" type="string" offset="8"/>
+ </match>
+ </magic>
+ <glob pattern="*.deb"/>
+ <glob pattern="*.udeb"/>
+ </mime-type>
+ <mime-type type="application/x-designer">
+ <comment>Qt Designer file</comment>
+ <comment xml:lang="ar">ملف Qt Designer</comment>
+ <comment xml:lang="be@latin">Fajł Qt Designer</comment>
+ <comment xml:lang="bg">Файл — Qt Designer</comment>
+ <comment xml:lang="ca">fitxer de Qt Designer</comment>
+ <comment xml:lang="cs">soubor Qt Designer</comment>
+ <comment xml:lang="da">Qt Designer-fil</comment>
+ <comment xml:lang="de">Qt-Designer-Datei</comment>
+ <comment xml:lang="el">Αρχείο Qt Designer</comment>
+ <comment xml:lang="en_GB">Qt Designer file</comment>
+ <comment xml:lang="eo">dosiero de Qt Designer</comment>
+ <comment xml:lang="es">archivo de Qt Designer</comment>
+ <comment xml:lang="eu">Qt Designer Fitxategia</comment>
+ <comment xml:lang="fi">Qt Designer -tiedosto</comment>
+ <comment xml:lang="fo">Qt Designer fíla</comment>
+ <comment xml:lang="fr">fichier Qt Designer</comment>
+ <comment xml:lang="ga">comhad Qt Designer</comment>
+ <comment xml:lang="gl">ficheiro de Qt Designer</comment>
+ <comment xml:lang="he">קובץ של Qt Designer</comment>
+ <comment xml:lang="hr">Qt Designer datoteka</comment>
+ <comment xml:lang="hu">Qt Designer-fájl</comment>
+ <comment xml:lang="ia">File Qt Designer</comment>
+ <comment xml:lang="id">Berkas Qt Designer</comment>
+ <comment xml:lang="it">File Qt Designer</comment>
+ <comment xml:lang="ja">Qt Designer ファイル</comment>
+ <comment xml:lang="kk">Qt Designer файлы</comment>
+ <comment xml:lang="ko">Qt 디자이너 파일</comment>
+ <comment xml:lang="lt">Qt Designer failas</comment>
+ <comment xml:lang="lv">Qt Designer datne</comment>
+ <comment xml:lang="ms">Fail Qt Designer</comment>
+ <comment xml:lang="nb">Qt Designer-fil</comment>
+ <comment xml:lang="nl">Qt Designer-bestand</comment>
+ <comment xml:lang="nn">Qt Designer-fil</comment>
+ <comment xml:lang="oc">fichièr Qt Designer</comment>
+ <comment xml:lang="pl">Plik Qt Designer</comment>
+ <comment xml:lang="pt">ficheiro do Qt Designer</comment>
+ <comment xml:lang="pt_BR">Arquivo do Qt Designer</comment>
+ <comment xml:lang="ro">Fișier Qt Designer</comment>
+ <comment xml:lang="ru">Файл Qt Designer</comment>
+ <comment xml:lang="sk">Súbor Qt Designer</comment>
+ <comment xml:lang="sl">Datoteka Qt Designer</comment>
+ <comment xml:lang="sq">File Qt Designer</comment>
+ <comment xml:lang="sr">датотека Кут дизајнера</comment>
+ <comment xml:lang="sv">Qt Designer-fil</comment>
+ <comment xml:lang="tr">Qt Tasarımcı dosyası</comment>
+ <comment xml:lang="uk">файл програми Qt-дизайнер</comment>
+ <comment xml:lang="vi">Tập tin thiết kế Qt Designer</comment>
+ <comment xml:lang="zh_CN">Qt Designer 文件</comment>
+ <comment xml:lang="zh_TW">Qt Designer 檔案</comment>
+ <generic-icon name="x-office-document"/>
+ <sub-class-of type="application/xml"/>
+ <magic>
+ <match value="&lt;ui " type="string" offset="0:256"/>
+ <match value="&lt;UI " type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.ui"/>
+ </mime-type>
+ <mime-type type="text/x-qml">
+ <comment>Qt Markup Language file</comment>
+ <comment xml:lang="bg">Файл — Qt Markup</comment>
+ <comment xml:lang="ca">fitxer de llenguatge de marcadors Qt</comment>
+ <comment xml:lang="cs">soubor Qt Markup Language</comment>
+ <comment xml:lang="da">Qt Markup Language-fil</comment>
+ <comment xml:lang="de">Qt-Auszeichnungssprachendatei</comment>
+ <comment xml:lang="el">Αρχείο Qt Markup Language</comment>
+ <comment xml:lang="en_GB">Qt Markup Language file</comment>
+ <comment xml:lang="es">archivo de lenguaje de marcado Qt</comment>
+ <comment xml:lang="eu">Qt Markup lengoai fitxategia</comment>
+ <comment xml:lang="fi">QML-tiedosto</comment>
+ <comment xml:lang="fr">fichier Qt Markup Language</comment>
+ <comment xml:lang="ga">comhad teanga mharcála Qt</comment>
+ <comment xml:lang="gl">ficheiro de linguaxe de marcado Qt</comment>
+ <comment xml:lang="he">קובץ שפת סימון של Qt</comment>
+ <comment xml:lang="hr">Qt Markup Language datoteka</comment>
+ <comment xml:lang="hu">Qt jelölőnyelvű fájl</comment>
+ <comment xml:lang="ia">File de linguage de marcation Qt</comment>
+ <comment xml:lang="id">Berkas Bahasa Markup Qt</comment>
+ <comment xml:lang="it">File Qt Markup Language</comment>
+ <comment xml:lang="ja">Qt マークアップ言語ファイル</comment>
+ <comment xml:lang="kk">Qt Markup Language файлы</comment>
+ <comment xml:lang="ko">Qt 마크업 언어 파일</comment>
+ <comment xml:lang="lv">Qt marķēšanas valodas datne</comment>
+ <comment xml:lang="nl">Qt Markup Tallbestand</comment>
+ <comment xml:lang="oc">fichièr Qt Markup Language</comment>
+ <comment xml:lang="pl">Plik języka znaczników Qt</comment>
+ <comment xml:lang="pt">ficheiro de linguagem Qt Markup</comment>
+ <comment xml:lang="pt_BR">Arquivo de Qt Markup Language</comment>
+ <comment xml:lang="ru">Файл Qt Markup Language</comment>
+ <comment xml:lang="sk">Súbor značkovacieho jazyka Qt</comment>
+ <comment xml:lang="sl">Datoteka označevalnega jezika Qt</comment>
+ <comment xml:lang="sr">датотека КуТ-овог језика означавања</comment>
+ <comment xml:lang="sv">Qt-märkspråksfil</comment>
+ <comment xml:lang="tr">Qt İşaretleme Dili dosyası</comment>
+ <comment xml:lang="uk">файл мови розмітки Qt</comment>
+ <comment xml:lang="zh_CN">Qt Markup Language 文件</comment>
+ <comment xml:lang="zh_TW">Qt 標記語言檔</comment>
+ <magic priority="80">
+ <match value="/bin/env qml" type="string" offset="2:16"/>
+ <match value="import Qt" type="string" offset="0:3000">
+ <match value="{" type="string" offset="9:3009"/>
+ </match>
+ <match value="import Qml" type="string" offset="0:3000">
+ <match value="{" type="string" offset="9:3009"/>
+ </match>
+ </magic>
+ <glob pattern="*.qml"/>
+ <glob pattern="*.qmltypes"/>
+ <glob pattern="*.qmlproject"/>
+ </mime-type>
+ <mime-type type="application/x-desktop">
+ <comment>desktop configuration file</comment>
+ <comment xml:lang="ar">ملف تضبيط سطح المكتب</comment>
+ <comment xml:lang="be@latin">kanfihuracyjny fajł asiarodździa</comment>
+ <comment xml:lang="bg">Файл с информация за работния плот</comment>
+ <comment xml:lang="ca">fitxer de configuració d'escriptori</comment>
+ <comment xml:lang="cs">soubor nastavení pracovní plochy</comment>
+ <comment xml:lang="da">skrivebordskonfigurationsfil</comment>
+ <comment xml:lang="de">Desktop-Konfigurationsdatei</comment>
+ <comment xml:lang="el">Αρχείο ρυθμίσεων επιφάνειας εργασίας</comment>
+ <comment xml:lang="en_GB">desktop configuration file</comment>
+ <comment xml:lang="eo">dosiero de agordoj de labortablo</comment>
+ <comment xml:lang="es">archivo de configuración del escritorio</comment>
+ <comment xml:lang="eu">Mahaigainaren konfigurazio-fitxategia</comment>
+ <comment xml:lang="fi">työpöydän asetustiedosto</comment>
+ <comment xml:lang="fo">skriviborðssamansetingarfíla</comment>
+ <comment xml:lang="fr">fichier de configuration desktop</comment>
+ <comment xml:lang="ga">comhad cumraíochta deisce</comment>
+ <comment xml:lang="gl">ficheiro de configuración de escritorio</comment>
+ <comment xml:lang="he">קובץ הגדרות שולחן עבודה</comment>
+ <comment xml:lang="hr">Datoteka prečaca radne površine</comment>
+ <comment xml:lang="hu">asztalbeállító fájl</comment>
+ <comment xml:lang="ia">File de configuration de scriptorio</comment>
+ <comment xml:lang="id">berkas konfigurasi destop</comment>
+ <comment xml:lang="it">File configurazione desktop</comment>
+ <comment xml:lang="ja">デスクトップ設定ファイル</comment>
+ <comment xml:lang="kk">жұмыс үстел баптаулар файлы</comment>
+ <comment xml:lang="ko">데스크톱 설정 파일</comment>
+ <comment xml:lang="lt">darbastalio konfigūracijos failas</comment>
+ <comment xml:lang="lv">darbvirsmas konfigurācijas datne</comment>
+ <comment xml:lang="ms">Fail konfigurasi desktop</comment>
+ <comment xml:lang="nb">konfigurasjonsfil for skrivebordet</comment>
+ <comment xml:lang="nl">bureaublad-configuratiebestand</comment>
+ <comment xml:lang="nn">skrivebordsoppsettfil</comment>
+ <comment xml:lang="oc">fichièr de configuracion desktop</comment>
+ <comment xml:lang="pl">Plik konfiguracji środowiska</comment>
+ <comment xml:lang="pt">ficheiro de configuração de área de trabalho</comment>
+ <comment xml:lang="pt_BR">Arquivo de configuração desktop</comment>
+ <comment xml:lang="ro">fișier de configurare al desktopului</comment>
+ <comment xml:lang="ru">Файл настроек рабочего стола</comment>
+ <comment xml:lang="sk">Súbor nastavení pracovnej plochy</comment>
+ <comment xml:lang="sl">nastavitvena datoteka namizja</comment>
+ <comment xml:lang="sq">File konfigurimi desktop</comment>
+ <comment xml:lang="sr">датотека подешавања радне површи</comment>
+ <comment xml:lang="sv">skrivbordskonfigurationsfil</comment>
+ <comment xml:lang="tr">masa üstü yapılandırma dosyası</comment>
+ <comment xml:lang="uk">файл конфігурації стільниці</comment>
+ <comment xml:lang="vi">tập tin cấu hình môi trường</comment>
+ <comment xml:lang="zh_CN">桌面配置文件</comment>
+ <comment xml:lang="zh_TW">桌面組態檔</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <magic priority="50">
+ <match value="[Desktop Entry]" type="string" offset="0:32"/>
+ <match value="[Desktop Action" type="string" offset="0"/>
+ <match value="[KDE Desktop Entry]" type="string" offset="0"/>
+ <match value="# Config File" type="string" offset="0"/>
+ <match value="# KDE Config File" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.desktop"/>
+ <glob pattern="*.kdelnk"/>
+ <alias type="application/x-gnome-app-info"/>
+ </mime-type>
+ <mime-type type="application/x-fictionbook+xml">
+ <comment>FictionBook document</comment>
+ <comment xml:lang="ar">مستند FictionBook</comment>
+ <comment xml:lang="ast">Documentu de FictionBook</comment>
+ <comment xml:lang="bg">Документ — FictionBook</comment>
+ <comment xml:lang="ca">document FictionBook</comment>
+ <comment xml:lang="cs">dokument FictionBook</comment>
+ <comment xml:lang="da">FictionBook-dokument</comment>
+ <comment xml:lang="de">FictionBook-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο FictionBook</comment>
+ <comment xml:lang="en_GB">FictionBook document</comment>
+ <comment xml:lang="eo">FictionBook-dokumento</comment>
+ <comment xml:lang="es">documento FictionBook</comment>
+ <comment xml:lang="eu">FictionBook dokumentua</comment>
+ <comment xml:lang="fi">FictionBook-asiakirja</comment>
+ <comment xml:lang="fo">FictionBook skjal</comment>
+ <comment xml:lang="fr">document FictionBook</comment>
+ <comment xml:lang="ga">cáipéis FictionBook</comment>
+ <comment xml:lang="gl">documento de FictionBook</comment>
+ <comment xml:lang="he">מסמך FictionBook</comment>
+ <comment xml:lang="hr">FictionBook dokument</comment>
+ <comment xml:lang="hu">FictionBook-dokumentum</comment>
+ <comment xml:lang="ia">Documento FictionBook</comment>
+ <comment xml:lang="id">Dokumen FictionBook</comment>
+ <comment xml:lang="it">Documento FictionBook</comment>
+ <comment xml:lang="ja">FictionBook ドキュメント</comment>
+ <comment xml:lang="ka">FictionBook-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">FictionBook құжаты</comment>
+ <comment xml:lang="ko">FictionBook 문서</comment>
+ <comment xml:lang="lt">FictionBook dokumentas</comment>
+ <comment xml:lang="lv">FictionBook dokuments</comment>
+ <comment xml:lang="nl">FictionBook-document</comment>
+ <comment xml:lang="oc">document FictionBook</comment>
+ <comment xml:lang="pl">Dokument FictionBook</comment>
+ <comment xml:lang="pt">documento FictionBook</comment>
+ <comment xml:lang="pt_BR">Documento FictionBook</comment>
+ <comment xml:lang="ro">Document FictionBook</comment>
+ <comment xml:lang="ru">Документ FictionBook</comment>
+ <comment xml:lang="sk">Dokument FictionBook</comment>
+ <comment xml:lang="sl">Dokument FictionBook</comment>
+ <comment xml:lang="sr">документ Фикшон Књиге</comment>
+ <comment xml:lang="sv">FictionBook-dokument</comment>
+ <comment xml:lang="tr">FictionBook belgesi</comment>
+ <comment xml:lang="uk">документ FictionBook</comment>
+ <comment xml:lang="vi">Tài liệu FictionBook</comment>
+ <comment xml:lang="zh_CN">FictionBook 文档</comment>
+ <comment xml:lang="zh_TW">FictionBook 文件</comment>
+ <sub-class-of type="application/xml"/>
+ <glob pattern="*.fb2"/>
+ <magic priority="80">
+ <match value="&lt;FictionBook" type="string" offset="0:256"/>
+ </magic>
+ <alias type="application/x-fictionbook"/>
+ <root-XML namespaceURI="http://www.gribuser.ru/xml/fictionbook/2.0" localName="FictionBook"/>
+ </mime-type>
+ <mime-type type="application/x-zip-compressed-fb2">
+ <comment>Compressed FictionBook document</comment>
+ <comment xml:lang="ast">Documentu comprimíu de FictionBook</comment>
+ <comment xml:lang="ca">document FictionBook amb compressió</comment>
+ <comment xml:lang="cs">komprimovaný dokument FictionBook</comment>
+ <comment xml:lang="da">Komprimeret FictionBook-dokument</comment>
+ <comment xml:lang="de">Komprimiertes FictionBook-Dokument</comment>
+ <comment xml:lang="el">Συμπιεσμένο έγγραφο FictionBook</comment>
+ <comment xml:lang="en_GB">Compressed FictionBook document</comment>
+ <comment xml:lang="es">documento comprimido de FictionBook</comment>
+ <comment xml:lang="eu">Konprimitutako FictionBook dokumentua</comment>
+ <comment xml:lang="fi">Pakattu FictionBook-asiakirja</comment>
+ <comment xml:lang="fr">document FictionBook compressé</comment>
+ <comment xml:lang="ga">cáipéis chomhbhrúite FictionBook</comment>
+ <comment xml:lang="gl">Documento de FictionBook comprimida</comment>
+ <comment xml:lang="he">מסמך FictionBook מכווץ</comment>
+ <comment xml:lang="hr">Sažet FictionBook dokument</comment>
+ <comment xml:lang="hu">Tömörített FictionBook dokumentum</comment>
+ <comment xml:lang="ia">Documento FictionBook comprimite</comment>
+ <comment xml:lang="id">Dokumen FictionBook terkompresi</comment>
+ <comment xml:lang="it">Documento FictionBook compresso</comment>
+ <comment xml:lang="kk">Сығылған FictionBook құжаты</comment>
+ <comment xml:lang="ko">압축한 FictionBook 문서</comment>
+ <comment xml:lang="oc">document FictionBook compressat</comment>
+ <comment xml:lang="pl">Skompresowany dokument FictionBook</comment>
+ <comment xml:lang="pt">documento comprimido FictionBook</comment>
+ <comment xml:lang="pt_BR">Documento FictionBook comprimido</comment>
+ <comment xml:lang="ru">Сжатый документ FictionBook</comment>
+ <comment xml:lang="sk">Komprimovaný dokument FictionBook</comment>
+ <comment xml:lang="sl">Stisnjeni dokument FictionBook</comment>
+ <comment xml:lang="sr">запаковани документ Фикшон Књиге</comment>
+ <comment xml:lang="sv">Komprimerat FictionBook-dokument</comment>
+ <comment xml:lang="tr">Sıkıştırılmış KurguKitap belgesi</comment>
+ <comment xml:lang="uk">стиснений документ FictionBook</comment>
+ <comment xml:lang="zh_CN">压缩的 FictionBook 文档</comment>
+ <comment xml:lang="zh_TW">壓縮版 FictionBook 文件</comment>
+ <sub-class-of type="application/zip"/>
+ <glob pattern="*.fb2.zip"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+
+ <match value=".fb2" type="string" offset="30:256"/>
+ </match>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-dia-diagram">
+ <comment>Dia diagram</comment>
+ <comment xml:lang="ar">خطاطة Dia</comment>
+ <comment xml:lang="az">Dia diaqramı</comment>
+ <comment xml:lang="be@latin">Dyjahrama Dia</comment>
+ <comment xml:lang="bg">Диаграма — Dia</comment>
+ <comment xml:lang="ca">diagrama de Dia</comment>
+ <comment xml:lang="cs">diagram Dia</comment>
+ <comment xml:lang="cy">Diagram Dia</comment>
+ <comment xml:lang="da">Dia-diagram</comment>
+ <comment xml:lang="de">Dia-Diagramm</comment>
+ <comment xml:lang="el">Διάγραμμα Dia</comment>
+ <comment xml:lang="en_GB">Dia diagram</comment>
+ <comment xml:lang="eo">Dia-diagramo</comment>
+ <comment xml:lang="es">diagrama de Dia</comment>
+ <comment xml:lang="eu">Dia diagrama</comment>
+ <comment xml:lang="fi">Dia-kaavio</comment>
+ <comment xml:lang="fo">Dia ritmynd</comment>
+ <comment xml:lang="fr">diagramme Dia</comment>
+ <comment xml:lang="ga">léaráid Dia</comment>
+ <comment xml:lang="gl">diagrama de Dia</comment>
+ <comment xml:lang="he">גרף של Dia</comment>
+ <comment xml:lang="hr">Dia dijagram</comment>
+ <comment xml:lang="hu">Dia-diagram</comment>
+ <comment xml:lang="ia">Diagramma Dia</comment>
+ <comment xml:lang="id">Diagram Dia</comment>
+ <comment xml:lang="it">Diagramma Dia</comment>
+ <comment xml:lang="ja">Dia ダイアグラム</comment>
+ <comment xml:lang="ka">Dia-ის დიაგრამა</comment>
+ <comment xml:lang="kk">Dia диаграммасы</comment>
+ <comment xml:lang="ko">Dia 다이어그램</comment>
+ <comment xml:lang="lt">Dia diagrama</comment>
+ <comment xml:lang="lv">Dia diagramma</comment>
+ <comment xml:lang="ms">Diagram Dia</comment>
+ <comment xml:lang="nb">Dia-diagram</comment>
+ <comment xml:lang="nl">Dia-diagram</comment>
+ <comment xml:lang="nn">Dia diagram</comment>
+ <comment xml:lang="oc">diagrama Dia</comment>
+ <comment xml:lang="pl">Diagram Dia</comment>
+ <comment xml:lang="pt">diagrama Dia</comment>
+ <comment xml:lang="pt_BR">Diagrama do Dia</comment>
+ <comment xml:lang="ro">Diagramă Dia</comment>
+ <comment xml:lang="ru">Диаграмма Dia</comment>
+ <comment xml:lang="sk">Diagram Dia</comment>
+ <comment xml:lang="sl">Datoteka diagrama Dia</comment>
+ <comment xml:lang="sq">Diagramë Dia</comment>
+ <comment xml:lang="sr">дијаграм Дие</comment>
+ <comment xml:lang="sv">Dia-diagram</comment>
+ <comment xml:lang="tr">Dia çizimi</comment>
+ <comment xml:lang="uk">діаграма Dia</comment>
+ <comment xml:lang="vi">Biểu đồ Dia</comment>
+ <comment xml:lang="zh_CN">Dia 图表</comment>
+ <comment xml:lang="zh_TW">Dia 圖表</comment>
+ <generic-icon name="image-x-generic"/>
+ <sub-class-of type="application/xml"/>
+ <glob pattern="*.dia"/>
+ <magic priority="50">
+ <match value="&lt;dia:" type="string" offset="5:100"/>
+ </magic>
+ <root-XML namespaceURI="http://www.lysator.liu.se/~alla/dia/" localName="diagram"/>
+ </mime-type>
+ <mime-type type="application/x-dia-shape">
+ <comment>Dia shape</comment>
+ <comment xml:lang="ar">شكل Dia</comment>
+ <comment xml:lang="bg">Фигура — Dia</comment>
+ <comment xml:lang="ca">forma de Dia</comment>
+ <comment xml:lang="cs">symboly Dia</comment>
+ <comment xml:lang="da">Dia-figur</comment>
+ <comment xml:lang="de">Dia-Form</comment>
+ <comment xml:lang="el">Σχήμα Dia</comment>
+ <comment xml:lang="en_GB">Dia shape</comment>
+ <comment xml:lang="es">forma de Dia</comment>
+ <comment xml:lang="eu">Dia-ren forma</comment>
+ <comment xml:lang="fi">Dia-muoto</comment>
+ <comment xml:lang="fo">Dia skapur</comment>
+ <comment xml:lang="fr">forme Dia</comment>
+ <comment xml:lang="ga">cruth Dia</comment>
+ <comment xml:lang="gl">forma de Dia</comment>
+ <comment xml:lang="he">צורה של Dia</comment>
+ <comment xml:lang="hr">Dia oblik</comment>
+ <comment xml:lang="hu">Dia alakzat</comment>
+ <comment xml:lang="ia">Forma Dia</comment>
+ <comment xml:lang="id">Shape Dia</comment>
+ <comment xml:lang="it">Sagoma Dia</comment>
+ <comment xml:lang="ja">Dia 図形</comment>
+ <comment xml:lang="kk">Dia сызбасы</comment>
+ <comment xml:lang="ko">Dia 그림</comment>
+ <comment xml:lang="lt">Dia forma</comment>
+ <comment xml:lang="lv">Dia forma</comment>
+ <comment xml:lang="nl">Diavorm</comment>
+ <comment xml:lang="oc">forma Dia</comment>
+ <comment xml:lang="pl">Kształt Dia</comment>
+ <comment xml:lang="pt">forma Dia</comment>
+ <comment xml:lang="pt_BR">Formato Dia</comment>
+ <comment xml:lang="ro">Figură Dia</comment>
+ <comment xml:lang="ru">Фигура Dia</comment>
+ <comment xml:lang="sk">Tvar Dia</comment>
+ <comment xml:lang="sl">Datoteka oblik Dia</comment>
+ <comment xml:lang="sr">облик Дие</comment>
+ <comment xml:lang="sv">Dia-figur</comment>
+ <comment xml:lang="tr">Dia şekli</comment>
+ <comment xml:lang="uk">форма Dia</comment>
+ <comment xml:lang="zh_CN">Dia 形状</comment>
+ <comment xml:lang="zh_TW">Dia 形狀</comment>
+ <generic-icon name="image-x-generic"/>
+ <sub-class-of type="application/xml"/>
+ <glob pattern="*.shape"/>
+ <magic priority="50">
+ <match value="&lt;shape" type="string" offset="5:100"/>
+ </magic>
+ <root-XML namespaceURI="http://www.daa.com.au/~james/dia-shape-ns" localName="shape"/>
+ </mime-type>
+ <mime-type type="application/x-dvi">
+ <comment>TeX DVI document</comment>
+ <comment xml:lang="ar">مستند TeX DVI</comment>
+ <comment xml:lang="ast">Documentu Tex DVI</comment>
+ <comment xml:lang="be@latin">Dakument TeX DVI</comment>
+ <comment xml:lang="bg">Документ — TeX DVI</comment>
+ <comment xml:lang="ca">document DVI de TeX</comment>
+ <comment xml:lang="cs">dokument TeX DVI</comment>
+ <comment xml:lang="da">TeX DVI-dokument</comment>
+ <comment xml:lang="de">TeX-DVI-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο TeX DVI</comment>
+ <comment xml:lang="en_GB">TeX DVI document</comment>
+ <comment xml:lang="eo">DVI-dokumento de TeX</comment>
+ <comment xml:lang="es">documento TeX DVI</comment>
+ <comment xml:lang="eu">TeX DVI dokumentua</comment>
+ <comment xml:lang="fi">TeX DVI -asiakirja</comment>
+ <comment xml:lang="fo">TeX DVI skjal</comment>
+ <comment xml:lang="fr">document TeX DVI</comment>
+ <comment xml:lang="ga">cáipéis DVI TeX</comment>
+ <comment xml:lang="gl">documento TeX DVI</comment>
+ <comment xml:lang="he">מסמך מסוג TeX DVI</comment>
+ <comment xml:lang="hr">TeX DVI dokument</comment>
+ <comment xml:lang="hu">TeX DVI-dokumentum</comment>
+ <comment xml:lang="ia">Documento TeX DVI</comment>
+ <comment xml:lang="id">Dokumen TeX DVI</comment>
+ <comment xml:lang="it">Documento TeX DVI</comment>
+ <comment xml:lang="ja">TeX DVI ドキュメント</comment>
+ <comment xml:lang="kk">TeX DVI құжаты</comment>
+ <comment xml:lang="ko">TeX DVI 문서</comment>
+ <comment xml:lang="lt">TeX DVI dokumentas</comment>
+ <comment xml:lang="lv">TeX DVI dokuments</comment>
+ <comment xml:lang="ms">Dokumen TeX DVI</comment>
+ <comment xml:lang="nb">TeX DVI-dokument</comment>
+ <comment xml:lang="nl">TeX DVI-document</comment>
+ <comment xml:lang="nn">TeX DVI-dokument</comment>
+ <comment xml:lang="oc">document TeX DVI</comment>
+ <comment xml:lang="pl">Dokument TeX DVI</comment>
+ <comment xml:lang="pt">documento TeX DVI</comment>
+ <comment xml:lang="pt_BR">Documento DVI TeX</comment>
+ <comment xml:lang="ro">Document Tex DVI</comment>
+ <comment xml:lang="ru">Документ TeX DVI</comment>
+ <comment xml:lang="sk">Dokument TeX DVI</comment>
+ <comment xml:lang="sl">Dokument TeX DVI</comment>
+ <comment xml:lang="sq">Dokument TeX DVI</comment>
+ <comment xml:lang="sr">ТеКс ДВИ документ</comment>
+ <comment xml:lang="sv">TeX DVI-dokument</comment>
+ <comment xml:lang="tr">TeX DVI belgesi</comment>
+ <comment xml:lang="uk">документ TeX DVI</comment>
+ <comment xml:lang="vi">Tài liệu DVI Tex</comment>
+ <comment xml:lang="zh_CN">TeX DVI 文档</comment>
+ <comment xml:lang="zh_TW">TeX DVI 文件</comment>
+ <acronym>DVI</acronym>
+ <expanded-acronym>Device independent file format</expanded-acronym>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="0x02f7" type="little16" offset="0"/>
+ </magic>
+ <glob pattern="*.dvi"/>
+ </mime-type>
+ <mime-type type="application/x-e-theme">
+ <comment>Enlightenment theme</comment>
+ <comment xml:lang="ar">سمة Enlightenment</comment>
+ <comment xml:lang="az">Enlightenment örtüyü</comment>
+ <comment xml:lang="be@latin">Matyŭ Enlightenment</comment>
+ <comment xml:lang="bg">Тема — Enlightenment</comment>
+ <comment xml:lang="ca">tema d'Enlightenment</comment>
+ <comment xml:lang="cs">motiv Enlightenment</comment>
+ <comment xml:lang="cy">Thema Enlightenment</comment>
+ <comment xml:lang="da">Enlightenmenttema</comment>
+ <comment xml:lang="de">Enlightenment-Thema</comment>
+ <comment xml:lang="el">Θέμα Enlightenment</comment>
+ <comment xml:lang="en_GB">Enlightenment theme</comment>
+ <comment xml:lang="eo">etoso de Enlightenment</comment>
+ <comment xml:lang="es">tema de Enlightenment</comment>
+ <comment xml:lang="eu">Enlightenment gaia</comment>
+ <comment xml:lang="fi">Enlightenment-teema</comment>
+ <comment xml:lang="fo">Enlightenment tema</comment>
+ <comment xml:lang="fr">thème Enlightenment</comment>
+ <comment xml:lang="ga">téama Enlightenment</comment>
+ <comment xml:lang="gl">tema de Enlightenment</comment>
+ <comment xml:lang="he">ערכת נושא של Enlightenment</comment>
+ <comment xml:lang="hr">Enlightenment tema</comment>
+ <comment xml:lang="hu">Enlightenment-téma</comment>
+ <comment xml:lang="ia">Thema de Enlightenment</comment>
+ <comment xml:lang="id">Tema Enlightenment</comment>
+ <comment xml:lang="it">Tema Enlightenment</comment>
+ <comment xml:lang="ja">Enlightenment テーマ</comment>
+ <comment xml:lang="ka">Enlightenment-ის თემა</comment>
+ <comment xml:lang="kk">Enlightenment темасы</comment>
+ <comment xml:lang="ko">인라이튼먼트 테마</comment>
+ <comment xml:lang="lt">Enlightenment tema</comment>
+ <comment xml:lang="lv">Enlightenment motīvs</comment>
+ <comment xml:lang="ms">Tema Enlightenment</comment>
+ <comment xml:lang="nb">Enlightenment tema</comment>
+ <comment xml:lang="nl">Enlightenment-thema</comment>
+ <comment xml:lang="nn">Enlightenment-tema</comment>
+ <comment xml:lang="oc">tèma Enlightenment</comment>
+ <comment xml:lang="pl">Motyw Enlightenment</comment>
+ <comment xml:lang="pt">tema Enlightenment</comment>
+ <comment xml:lang="pt_BR">Tema do Enlightenment</comment>
+ <comment xml:lang="ro">Temă Enlightenment</comment>
+ <comment xml:lang="ru">Тема Enlightenment</comment>
+ <comment xml:lang="sk">Motív Enlightenment</comment>
+ <comment xml:lang="sl">Datoteka teme Enlightenment</comment>
+ <comment xml:lang="sq">Tema Enlightenment</comment>
+ <comment xml:lang="sr">тема за Енлајтмент</comment>
+ <comment xml:lang="sv">Enlightenment-tema</comment>
+ <comment xml:lang="tr">Enlightenment teması</comment>
+ <comment xml:lang="uk">тема Enlightenment</comment>
+ <comment xml:lang="vi">Sắc thái Enlightenment</comment>
+ <comment xml:lang="zh_CN">Enlightenment 主题</comment>
+ <comment xml:lang="zh_TW">Enlightenment 佈景主題</comment>
+ <glob pattern="*.etheme"/>
+ </mime-type>
+ <mime-type type="application/x-egon">
+ <comment>Egon Animator animation</comment>
+ <comment xml:lang="ar">تحريكة محرك Egon</comment>
+ <comment xml:lang="be@latin">Animacyja Egon Animator</comment>
+ <comment xml:lang="bg">Анимация — Egon Animator</comment>
+ <comment xml:lang="ca">animació d'Egon Animator</comment>
+ <comment xml:lang="cs">animace Egon Animator</comment>
+ <comment xml:lang="da">Egon Animator-animation</comment>
+ <comment xml:lang="de">Egon-Animator-Animation</comment>
+ <comment xml:lang="el">Κινούμενο σχέδιο Egon Animator</comment>
+ <comment xml:lang="en_GB">Egon Animator animation</comment>
+ <comment xml:lang="eo">animacio de Egon Animator</comment>
+ <comment xml:lang="es">animación de Egon Animator</comment>
+ <comment xml:lang="eu">Egon Animator-eko animazioa</comment>
+ <comment xml:lang="fi">Egon Animator -animaatio</comment>
+ <comment xml:lang="fo">Egon Animator teknimyndagerð</comment>
+ <comment xml:lang="fr">animation Egon Animator</comment>
+ <comment xml:lang="ga">beochan Egon Animator</comment>
+ <comment xml:lang="gl">animación de Egon Animator</comment>
+ <comment xml:lang="he">אנימצייה של Egon Animator</comment>
+ <comment xml:lang="hr">Egon Animator animacija</comment>
+ <comment xml:lang="hu">Egon Animator-animáció</comment>
+ <comment xml:lang="ia">Imagine Egon Animator</comment>
+ <comment xml:lang="id">Animasi Egon Animator</comment>
+ <comment xml:lang="it">Animazione Egon Animator</comment>
+ <comment xml:lang="ja">Egon Animator アニメーション</comment>
+ <comment xml:lang="ka">Egon Animator-ის ანიმაცია</comment>
+ <comment xml:lang="kk">Egon Animator анимациясы</comment>
+ <comment xml:lang="ko">Egon 애니메이터 애니메이션</comment>
+ <comment xml:lang="lt">Egon Animator animacija</comment>
+ <comment xml:lang="lv">Egon Animator animācija</comment>
+ <comment xml:lang="ms">Animasi Egon Animator</comment>
+ <comment xml:lang="nb">Egon animator-animasjon</comment>
+ <comment xml:lang="nl">Egon Animator-animatie</comment>
+ <comment xml:lang="nn">Egon Animator-animasjon</comment>
+ <comment xml:lang="oc">animacion Egon Animator</comment>
+ <comment xml:lang="pl">Animacja Egon Animator</comment>
+ <comment xml:lang="pt">animação Egon Animator</comment>
+ <comment xml:lang="pt_BR">Animação do Egon Animator</comment>
+ <comment xml:lang="ro">Animație Egon Animator</comment>
+ <comment xml:lang="ru">Анимация Egon Animator</comment>
+ <comment xml:lang="sk">Animácia Egon Animator</comment>
+ <comment xml:lang="sl">Datoteka animacije Egon Animator</comment>
+ <comment xml:lang="sq">Animim Egon Animator</comment>
+ <comment xml:lang="sr">анимација Егон аниматора</comment>
+ <comment xml:lang="sv">Egon Animator-animering</comment>
+ <comment xml:lang="tr">Egon Animator canlandırması</comment>
+ <comment xml:lang="uk">анімація Egon Animator</comment>
+ <comment xml:lang="vi">Hoạt ảnh Egon Animator</comment>
+ <comment xml:lang="zh_CN">Egon Animator 动画</comment>
+ <comment xml:lang="zh_TW">Egon Animator 動畫</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.egon"/>
+ </mime-type>
+ <mime-type type="application/x-executable">
+ <comment>executable</comment>
+ <comment xml:lang="ar">تنفيذي</comment>
+ <comment xml:lang="be@latin">vykonvalny fajł</comment>
+ <comment xml:lang="bg">Изпълним файл</comment>
+ <comment xml:lang="ca">executable</comment>
+ <comment xml:lang="cs">spustitelný soubor</comment>
+ <comment xml:lang="da">kørbar</comment>
+ <comment xml:lang="de">Programm</comment>
+ <comment xml:lang="el">Εκτελέσιμο</comment>
+ <comment xml:lang="en_GB">executable</comment>
+ <comment xml:lang="eo">plenumebla</comment>
+ <comment xml:lang="es">ejecutable</comment>
+ <comment xml:lang="eu">exekutagarria</comment>
+ <comment xml:lang="fi">suoritettava ohjelma</comment>
+ <comment xml:lang="fo">inningarfør</comment>
+ <comment xml:lang="fr">exécutable</comment>
+ <comment xml:lang="ga">comhad inrite</comment>
+ <comment xml:lang="gl">executábel</comment>
+ <comment xml:lang="he">קובץ הרצה</comment>
+ <comment xml:lang="hr">Izvršna datoteka</comment>
+ <comment xml:lang="hu">futtatható</comment>
+ <comment xml:lang="ia">Executabile</comment>
+ <comment xml:lang="id">dapat dieksekusi</comment>
+ <comment xml:lang="it">Eseguibile</comment>
+ <comment xml:lang="ja">実行ファイル</comment>
+ <comment xml:lang="kk">орындалатын</comment>
+ <comment xml:lang="ko">실행 파일</comment>
+ <comment xml:lang="lt">vykdomasis failas</comment>
+ <comment xml:lang="lv">izpildāmais</comment>
+ <comment xml:lang="ms">Bolehlaksana</comment>
+ <comment xml:lang="nb">kjørbar</comment>
+ <comment xml:lang="nl">uitvoerbaar bestand</comment>
+ <comment xml:lang="nn">køyrbar</comment>
+ <comment xml:lang="oc">executable</comment>
+ <comment xml:lang="pl">Program</comment>
+ <comment xml:lang="pt">executável</comment>
+ <comment xml:lang="pt_BR">Executável</comment>
+ <comment xml:lang="ro">executabil</comment>
+ <comment xml:lang="ru">Исполняемый</comment>
+ <comment xml:lang="sk">Spustiteľný súbor</comment>
+ <comment xml:lang="sl">izvedljiva datoteka</comment>
+ <comment xml:lang="sq">I ekzekutueshëm</comment>
+ <comment xml:lang="sr">извршна</comment>
+ <comment xml:lang="sv">körbar fil</comment>
+ <comment xml:lang="tr">çalıştırılabilir</comment>
+ <comment xml:lang="uk">виконуваний файл</comment>
+ <comment xml:lang="vi">thực hiện được</comment>
+ <comment xml:lang="zh_CN">可执行文件</comment>
+ <comment xml:lang="zh_TW">可執行檔</comment>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="40">
+ <match value="\177ELF" type="string" offset="0">
+ <match value="1" type="byte" offset="5">
+ <match value="2" type="little16" offset="16"/>
+ </match>
+ </match>
+ <match value="\177ELF" type="string" offset="0">
+ <match value="2" type="byte" offset="5">
+ <match value="2" type="big16" offset="16"/>
+ </match>
+ </match>
+ <match value="MZ" type="string" offset="0"/>
+ <match value="0x521c" type="little16" offset="0"/>
+ <match value="0420" type="host16" offset="0"/>
+ <match value="0421" type="host16" offset="0"/>
+ <match value="0603" type="little16" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-fluid">
+ <comment>FLTK Fluid file</comment>
+ <comment xml:lang="ar">ملف FLTK Fluid</comment>
+ <comment xml:lang="be@latin">Fajł FLTK Fluid</comment>
+ <comment xml:lang="bg">Интерфейс — FLTK Fluid</comment>
+ <comment xml:lang="ca">fitxer FLTK Fluid</comment>
+ <comment xml:lang="cs">soubor FLTK Fluid</comment>
+ <comment xml:lang="da">FLTK Fluid-fil</comment>
+ <comment xml:lang="de">FLTK-Fluid-Datei</comment>
+ <comment xml:lang="el">Αρχείο FLTK Fluid</comment>
+ <comment xml:lang="en_GB">FLTK Fluid file</comment>
+ <comment xml:lang="es">archivo FLTK Fluid</comment>
+ <comment xml:lang="eu">FLTK Fluid fitxategia</comment>
+ <comment xml:lang="fi">FLTK Fluid -tiedosto</comment>
+ <comment xml:lang="fo">FLTK Fluid fíla</comment>
+ <comment xml:lang="fr">fichier Fluid FLTK</comment>
+ <comment xml:lang="ga">comhad FLTK Fluid</comment>
+ <comment xml:lang="gl">ficheiro FLTK Fluid</comment>
+ <comment xml:lang="he">קובץ FLTK Fluid</comment>
+ <comment xml:lang="hr">FLTK Fluid datoteka</comment>
+ <comment xml:lang="hu">FLTK Fluid fájl</comment>
+ <comment xml:lang="ia">File Fluid de FLTK</comment>
+ <comment xml:lang="id">Berkas FLTK Fluid</comment>
+ <comment xml:lang="it">File FLTK Fluid</comment>
+ <comment xml:lang="ja">FLTK Fluid ファイル</comment>
+ <comment xml:lang="ka">FLTK Fluid-ის ფაილი</comment>
+ <comment xml:lang="kk">FLTK Fluid файлы</comment>
+ <comment xml:lang="ko">FLTK Fluid 파일</comment>
+ <comment xml:lang="lt">FLTK Fluid failas</comment>
+ <comment xml:lang="lv">FLTK Fluid datne</comment>
+ <comment xml:lang="nb">FLTK Fluid-fil</comment>
+ <comment xml:lang="nl">FLTK FLUID-bestand</comment>
+ <comment xml:lang="nn">FLTK Fluid-fil</comment>
+ <comment xml:lang="oc">fichièr Fluid FLTK</comment>
+ <comment xml:lang="pl">Plik Fluid FLTK</comment>
+ <comment xml:lang="pt">ficheiro FLTK Fluid</comment>
+ <comment xml:lang="pt_BR">Arquivo Fluid do FLTK</comment>
+ <comment xml:lang="ro">Fișier FLTK Fluid</comment>
+ <comment xml:lang="ru">Файл FLTK Fluid</comment>
+ <comment xml:lang="sk">Súbor FLTK Fluid</comment>
+ <comment xml:lang="sl">Datoteka FLTK Fluid</comment>
+ <comment xml:lang="sq">File FLTK Fluid</comment>
+ <comment xml:lang="sr">датотека ФЛТК Флуида</comment>
+ <comment xml:lang="sv">FLTK Fluid-fil</comment>
+ <comment xml:lang="tr">FLTK Fluid dosyası</comment>
+ <comment xml:lang="uk">файл FLTK Fluid</comment>
+ <comment xml:lang="vi">Tập tin Fluid FLTK</comment>
+ <comment xml:lang="zh_CN">FLTK 流体文档</comment>
+ <comment xml:lang="zh_TW">FLTK Fluid 檔</comment>
+ <acronym>FLTK</acronym>
+ <expanded-acronym>Fast Light Toolkit</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="# data file for the Fltk" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.fl"/>
+ </mime-type>
+ <mime-type type="font/woff">
+ <comment>WOFF font</comment>
+ <comment xml:lang="ast">Fonte WOFF</comment>
+ <comment xml:lang="ca">lletra WOFF</comment>
+ <comment xml:lang="cs">font WOFF</comment>
+ <comment xml:lang="da">WOFF-skrifttype</comment>
+ <comment xml:lang="de">WOFF-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά WOFF</comment>
+ <comment xml:lang="en_GB">WOFF font</comment>
+ <comment xml:lang="es">tipo de letra WOFF</comment>
+ <comment xml:lang="eu">WOFF letra-tipoa</comment>
+ <comment xml:lang="fi">WOFF-fontti</comment>
+ <comment xml:lang="fr">police WOFF</comment>
+ <comment xml:lang="ga">cló WOFF</comment>
+ <comment xml:lang="gl">Tipo de letra WOFF</comment>
+ <comment xml:lang="he">גופן WOFF</comment>
+ <comment xml:lang="hr">WOFF slovo</comment>
+ <comment xml:lang="hu">WOFF-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras WOFF</comment>
+ <comment xml:lang="id">Fonta WOFF</comment>
+ <comment xml:lang="it">Carattere WOFF</comment>
+ <comment xml:lang="ja">WOFF フォント</comment>
+ <comment xml:lang="kk">WOFF қарібі</comment>
+ <comment xml:lang="ko">WOFF 글꼴</comment>
+ <comment xml:lang="lv">WOFF fonts</comment>
+ <comment xml:lang="oc">poliça WOFF</comment>
+ <comment xml:lang="pl">Czcionka WOFF</comment>
+ <comment xml:lang="pt">letra WOFF</comment>
+ <comment xml:lang="pt_BR">Fonte WOFF</comment>
+ <comment xml:lang="ru">Шрифт WOFF</comment>
+ <comment xml:lang="sk">Písmo WOFF</comment>
+ <comment xml:lang="sl">Pisava WOFF</comment>
+ <comment xml:lang="sr">ВОФФ слова</comment>
+ <comment xml:lang="sv">WOFF-typsnitt</comment>
+ <comment xml:lang="tr">WOFF yazı tipi</comment>
+ <comment xml:lang="uk">шрифт WOFF</comment>
+ <comment xml:lang="zh_CN">WOFF 字体</comment>
+ <comment xml:lang="zh_TW">WOFF 字型</comment>
+ <acronym>WOFF</acronym>
+ <expanded-acronym>Web Open Font Format</expanded-acronym>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="0x774f4646" type="big32" offset="0"/>
+ </magic>
+ <glob pattern="*.woff"/>
+ <alias type="application/font-woff"/>
+ </mime-type>
+ <mime-type type="font/woff">
+ <comment>WOFF2 font</comment>
+ <comment xml:lang="ast">Fonte WOFF2</comment>
+ <comment xml:lang="ca">lletra WOFF2</comment>
+ <comment xml:lang="cs">font WOFF2</comment>
+ <comment xml:lang="de">WOFF2-Schrift</comment>
+ <comment xml:lang="en_GB">WOFF2 font</comment>
+ <comment xml:lang="es">tipo de letra WOFF2</comment>
+ <comment xml:lang="fi">WOFF2-fontti</comment>
+ <comment xml:lang="hr">WOFF2 slovo</comment>
+ <comment xml:lang="hu">WOFF2 betűkészlet</comment>
+ <comment xml:lang="id">Fonta WOFF2</comment>
+ <comment xml:lang="it">Carattere WOFF2</comment>
+ <comment xml:lang="kk">WOFF2 қарібі</comment>
+ <comment xml:lang="ko">WOFF2 글꼴</comment>
+ <comment xml:lang="pl">Czcionka WOFF2</comment>
+ <comment xml:lang="pt_BR">Fonte WOFF2</comment>
+ <comment xml:lang="ru">Шрифт WOFF2</comment>
+ <comment xml:lang="sk">Písmo WOFF2</comment>
+ <comment xml:lang="sv">WOFF2-typsnitt</comment>
+ <comment xml:lang="uk">шрифт WOFF2</comment>
+ <comment xml:lang="zh_CN">WOFF2 字体</comment>
+ <comment xml:lang="zh_TW">WOFF2 字型</comment>
+ <acronym>WOFF2</acronym>
+ <expanded-acronym>Web Open Font Format 2.0</expanded-acronym>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="0x774f4632" type="big32" offset="0"/>
+ </magic>
+ <glob pattern="*.woff2"/>
+ </mime-type>
+ <mime-type type="application/x-font-type1">
+ <comment>PostScript type-1 font</comment>
+ <comment xml:lang="ca">lletra type-1 de PostScript</comment>
+ <comment xml:lang="cs">font PostScript type-1</comment>
+ <comment xml:lang="de">PostScript-Typ-1-Schrift</comment>
+ <comment xml:lang="en_GB">PostScript type-1 font</comment>
+ <comment xml:lang="es">tipo de letra PostScript Type-1</comment>
+ <comment xml:lang="eu">PostScript type-1 letra-tipoa</comment>
+ <comment xml:lang="fr">police PostScript Type 1</comment>
+ <comment xml:lang="ga">cló PostScript type-1</comment>
+ <comment xml:lang="hr">PostScript type-1 slovo</comment>
+ <comment xml:lang="hu">PostScript type-1 betűkészlet</comment>
+ <comment xml:lang="id">fonta PostScript type-1</comment>
+ <comment xml:lang="it">Carattere PostScript type-1</comment>
+ <comment xml:lang="kk">PostScript type-1 қарібі</comment>
+ <comment xml:lang="ko">PostScript Type-1 글꼴</comment>
+ <comment xml:lang="pl">Czcionka PostScript Type-1</comment>
+ <comment xml:lang="pt_BR">Fonte PostScript tipo-1</comment>
+ <comment xml:lang="ru">Шрифт PostScript Type-1</comment>
+ <comment xml:lang="sk">Písmo PostScript typu 1</comment>
+ <comment xml:lang="sr">слова Постскрипта врсте-1</comment>
+ <comment xml:lang="sv">PostScript type-1-typsnitt</comment>
+ <comment xml:lang="tr">PostScript tip-1 yazı tipi</comment>
+ <comment xml:lang="uk">шрифт type-1 PostScript</comment>
+ <comment xml:lang="zh_CN">PostScript type-1 字体</comment>
+ <comment xml:lang="zh_TW">PostScript type-1 字型</comment>
+ <sub-class-of type="application/postscript"/>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="60">
+ <match value="LWFN" type="string" offset="0"/>
+ <match value="LWFN" type="string" offset="65"/>
+ <match value="%!PS-AdobeFont-1." type="string" offset="0"/>
+ <match value="%!PS-AdobeFont-1." type="string" offset="6"/>
+ <match value="%!FontType1-1." type="string" offset="0"/>
+ <match value="%!FontType1-1." type="string" offset="6"/>
+ </magic>
+ <glob pattern="*.pfa"/>
+ <glob pattern="*.pfb"/>
+ <glob pattern="*.gsf"/>
+ </mime-type>
+ <mime-type type="application/x-font-afm">
+ <comment>Adobe font metrics</comment>
+ <comment xml:lang="ar">مقاييس خط أدوبي</comment>
+ <comment xml:lang="az">Adobe yazı növü metrikləri</comment>
+ <comment xml:lang="be@latin">Metryka šryftu Adobe</comment>
+ <comment xml:lang="bg">Шрифтова метрика — Adobe</comment>
+ <comment xml:lang="ca">mètrica de lletra d'Adobe</comment>
+ <comment xml:lang="cs">metrika fontu Adobe</comment>
+ <comment xml:lang="cy">Metrigau Ffont Adobe</comment>
+ <comment xml:lang="da">Adobe skrifttypefil</comment>
+ <comment xml:lang="de">Adobe-Schriftmetriken</comment>
+ <comment xml:lang="el">Μετρικά γραμματοσειράς Adobe</comment>
+ <comment xml:lang="en_GB">Adobe font metrics</comment>
+ <comment xml:lang="eo">metrikoj de Adobe-tiparo</comment>
+ <comment xml:lang="es">métricas tipográficas de Adobe</comment>
+ <comment xml:lang="eu">Adobe letra-tipoen neurriak</comment>
+ <comment xml:lang="fi">Adobe-fonttimitat</comment>
+ <comment xml:lang="fr">métriques de police Adobe</comment>
+ <comment xml:lang="ga">meadarachtaí cló Adobe</comment>
+ <comment xml:lang="gl">métricas de fonte de Adobe</comment>
+ <comment xml:lang="he">מדדי גופן של Adobe</comment>
+ <comment xml:lang="hr">Adobe mjere slova</comment>
+ <comment xml:lang="hu">Adobe-betűmetrika</comment>
+ <comment xml:lang="ia">Metricas de typo de litteras Adobe</comment>
+ <comment xml:lang="id">Metrik fonta Adobe</comment>
+ <comment xml:lang="it">Metriche tipo carattere Adobe</comment>
+ <comment xml:lang="ja">Adobe フォントメトリック</comment>
+ <comment xml:lang="kk">Adobe қаріп метрикалары</comment>
+ <comment xml:lang="ko">Adobe 글꼴 메트릭</comment>
+ <comment xml:lang="lt">Adobe šriftų metrika</comment>
+ <comment xml:lang="lv">Adobe fonta metrika</comment>
+ <comment xml:lang="ms">Metrik font Adobe</comment>
+ <comment xml:lang="nb">Adobe skrifttypefil</comment>
+ <comment xml:lang="nl">Adobe-lettertype-metrieken</comment>
+ <comment xml:lang="nn">Adobe skrifttypemetrikk</comment>
+ <comment xml:lang="oc">metricas de poliça Adobe</comment>
+ <comment xml:lang="pl">Metryka czcionki Adobe</comment>
+ <comment xml:lang="pt">métrica de letras Adobe</comment>
+ <comment xml:lang="pt_BR">Métricas de fonte Adobe</comment>
+ <comment xml:lang="ro">Dimensiuni font Adobe</comment>
+ <comment xml:lang="ru">Метрика шрифта Adobe</comment>
+ <comment xml:lang="sk">Metrika písma Adobe</comment>
+ <comment xml:lang="sl">Matrika pisave Adobe</comment>
+ <comment xml:lang="sq">Metrik lloj gërmash Adobe</comment>
+ <comment xml:lang="sr">метрика Адобе слова</comment>
+ <comment xml:lang="sv">Adobe-typsnittsmetrik</comment>
+ <comment xml:lang="tr">Adobe yazıtipi ölçüleri</comment>
+ <comment xml:lang="uk">метрики шрифту Adobe</comment>
+ <comment xml:lang="vi">Cách đo phông chữ Adobe</comment>
+ <comment xml:lang="zh_CN">Adobe 字体规格</comment>
+ <comment xml:lang="zh_TW">Adobe 字型描述檔</comment>
+ <generic-icon name="font-x-generic"/>
+ <glob pattern="*.afm"/>
+ </mime-type>
+ <mime-type type="application/x-font-bdf">
+ <comment>BDF font</comment>
+ <comment xml:lang="ar">خط BDF</comment>
+ <comment xml:lang="az">BDF yazı növü</comment>
+ <comment xml:lang="be@latin">Šryft BDF</comment>
+ <comment xml:lang="bg">Шрифт — BDF</comment>
+ <comment xml:lang="ca">lletra BDF</comment>
+ <comment xml:lang="cs">font BDF</comment>
+ <comment xml:lang="cy">Ffont BDF</comment>
+ <comment xml:lang="da">BDF-skrifttype</comment>
+ <comment xml:lang="de">BDF-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά BDF</comment>
+ <comment xml:lang="en_GB">BDF font</comment>
+ <comment xml:lang="eo">BDF-tiparo</comment>
+ <comment xml:lang="es">tipo de letra BDF</comment>
+ <comment xml:lang="eu">BDF letra-tipoa</comment>
+ <comment xml:lang="fi">BDF-fontti</comment>
+ <comment xml:lang="fo">BDF stavasnið</comment>
+ <comment xml:lang="fr">police BDF</comment>
+ <comment xml:lang="ga">cló BDF</comment>
+ <comment xml:lang="gl">tipo de fonte BDF</comment>
+ <comment xml:lang="he">גופן BDF</comment>
+ <comment xml:lang="hr">BDF slovo</comment>
+ <comment xml:lang="hu">BDF-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras BDF</comment>
+ <comment xml:lang="id">Fonta BDF</comment>
+ <comment xml:lang="it">Carattere BDF</comment>
+ <comment xml:lang="ja">BDF フォント</comment>
+ <comment xml:lang="kk">BDF қарібі</comment>
+ <comment xml:lang="ko">BDF 글꼴</comment>
+ <comment xml:lang="lt">BDF šriftas</comment>
+ <comment xml:lang="lv">BDF fonts</comment>
+ <comment xml:lang="ms">Font BDF</comment>
+ <comment xml:lang="nb">BDF-skrifttype</comment>
+ <comment xml:lang="nl">BDF-lettertype</comment>
+ <comment xml:lang="nn">BDF-skrifttype</comment>
+ <comment xml:lang="oc">poliça BDF</comment>
+ <comment xml:lang="pl">Czcionka BDF</comment>
+ <comment xml:lang="pt">letra BDF</comment>
+ <comment xml:lang="pt_BR">Fonte BDF</comment>
+ <comment xml:lang="ro">Font BDF</comment>
+ <comment xml:lang="ru">Шрифт BDF</comment>
+ <comment xml:lang="sk">Písmo BDF</comment>
+ <comment xml:lang="sl">Datoteka pisave BDF</comment>
+ <comment xml:lang="sq">Lloj gërme BDF</comment>
+ <comment xml:lang="sr">БДФ слова</comment>
+ <comment xml:lang="sv">BDF-typsnitt</comment>
+ <comment xml:lang="tr">BDF fontu</comment>
+ <comment xml:lang="uk">шрифт BDF</comment>
+ <comment xml:lang="vi">Phông chữ BDF</comment>
+ <comment xml:lang="zh_CN">BDF 字体</comment>
+ <comment xml:lang="zh_TW">BDF 字型</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="STARTFONT\040" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.bdf"/>
+ </mime-type>
+ <mime-type type="application/x-font-dos">
+ <comment>DOS font</comment>
+ <comment xml:lang="ar">خط DOS</comment>
+ <comment xml:lang="az">DOS yazı növü</comment>
+ <comment xml:lang="be@latin">Šryft DOS</comment>
+ <comment xml:lang="bg">Шрифт — DOS</comment>
+ <comment xml:lang="ca">lletra DOS</comment>
+ <comment xml:lang="cs">font pro DOS</comment>
+ <comment xml:lang="cy">Ffont DOS</comment>
+ <comment xml:lang="da">DOS-skrifttype</comment>
+ <comment xml:lang="de">DOS-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά DOS</comment>
+ <comment xml:lang="en_GB">DOS font</comment>
+ <comment xml:lang="eo">DOS-tiparo</comment>
+ <comment xml:lang="es">tipo de letra de DOS</comment>
+ <comment xml:lang="eu">DOS letra-tipoa</comment>
+ <comment xml:lang="fi">DOS-fontti</comment>
+ <comment xml:lang="fo">DOS stavasnið</comment>
+ <comment xml:lang="fr">police DOS</comment>
+ <comment xml:lang="ga">cló DOS</comment>
+ <comment xml:lang="gl">tipo de fonte de DOS</comment>
+ <comment xml:lang="he">גופן DOS</comment>
+ <comment xml:lang="hr">DOS slovo</comment>
+ <comment xml:lang="hu">DOS-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras DOS</comment>
+ <comment xml:lang="id">Fonta DOS</comment>
+ <comment xml:lang="it">Carattere DOS</comment>
+ <comment xml:lang="ja">DOS フォント</comment>
+ <comment xml:lang="kk">DOS қарібі</comment>
+ <comment xml:lang="ko">DOS 글꼴</comment>
+ <comment xml:lang="lt">DOS šriftas</comment>
+ <comment xml:lang="lv">DOS fonts</comment>
+ <comment xml:lang="ms">Font DOS</comment>
+ <comment xml:lang="nb">DOS-skrifttype</comment>
+ <comment xml:lang="nl">DOS-lettertype</comment>
+ <comment xml:lang="nn">DOS-skrifttype</comment>
+ <comment xml:lang="oc">poliça DOS</comment>
+ <comment xml:lang="pl">Czcionka DOS</comment>
+ <comment xml:lang="pt">letra DOS</comment>
+ <comment xml:lang="pt_BR">Fonte do DOS</comment>
+ <comment xml:lang="ro">Font DOS</comment>
+ <comment xml:lang="ru">Шрифт DOS</comment>
+ <comment xml:lang="sk">Písmo pre DOS</comment>
+ <comment xml:lang="sl">Datoteka pisave DOS</comment>
+ <comment xml:lang="sq">Gërmë DOS</comment>
+ <comment xml:lang="sr">ДОС слова</comment>
+ <comment xml:lang="sv">DOS-typsnitt</comment>
+ <comment xml:lang="tr">DOS fontu</comment>
+ <comment xml:lang="uk">шрифт DOS</comment>
+ <comment xml:lang="vi">Phông chữ DOS</comment>
+ <comment xml:lang="zh_CN">DOS 字体</comment>
+ <comment xml:lang="zh_TW">DOS 字型</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="\xff\x46\x4f\x4e" type="string" offset="0"/>
+ <match value="\x00\x45\x47\x41" type="string" offset="7"/>
+ <match value="\x00\x56\x49\x44" type="string" offset="7"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-font-framemaker">
+ <comment>Adobe FrameMaker font</comment>
+ <comment xml:lang="ar">خط أدوبي الصانع للإطارات</comment>
+ <comment xml:lang="az">Adobe FrameMaker yazı növü</comment>
+ <comment xml:lang="be@latin">Šryft Adobe FrameMaker</comment>
+ <comment xml:lang="bg">Шрифт — Adobe FrameMaker</comment>
+ <comment xml:lang="ca">lletra d'Adobe FrameMaker</comment>
+ <comment xml:lang="cs">font Adobe FrameMaker</comment>
+ <comment xml:lang="cy">Ffont Adobe FrameMaker</comment>
+ <comment xml:lang="da">Adobe FrameMaker-skrifttype</comment>
+ <comment xml:lang="de">Adobe-FrameMaker-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά Adobe FrameMaker</comment>
+ <comment xml:lang="en_GB">Adobe FrameMaker font</comment>
+ <comment xml:lang="eo">Tiparo de Adobe FrameMaker</comment>
+ <comment xml:lang="es">tipo de letra de Adobe FrameMaker</comment>
+ <comment xml:lang="eu">Adobe FrameMaker-en letra-tipoa</comment>
+ <comment xml:lang="fi">Adobe FrameMaker -fontti</comment>
+ <comment xml:lang="fo">Adobe FrameMaker stavasnið</comment>
+ <comment xml:lang="fr">police Adobe FrameMaker</comment>
+ <comment xml:lang="ga">cló Adobe FrameMaker</comment>
+ <comment xml:lang="gl">tipo de fonte de Adobe FrameMaker</comment>
+ <comment xml:lang="he">גופן של Adobe FrameMaker</comment>
+ <comment xml:lang="hr">Adobe FrameMaker slovo</comment>
+ <comment xml:lang="hu">Adobe FrameMaker-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras pro Adobe FrameMaker</comment>
+ <comment xml:lang="id">Fonta Adobe FrameMaker</comment>
+ <comment xml:lang="it">Carattere Adobe FrameMaker</comment>
+ <comment xml:lang="ja">Adobe FrameMaker フォント</comment>
+ <comment xml:lang="kk">Adobe FrameMaker қарібі</comment>
+ <comment xml:lang="ko">Adobe 프레임메이커 글꼴</comment>
+ <comment xml:lang="lt">Adobe FrameMaker šriftas</comment>
+ <comment xml:lang="lv">Adobe FrameMaker fonts</comment>
+ <comment xml:lang="ms">Font Adobe FrameMaker</comment>
+ <comment xml:lang="nb">Adobe FrameMaker skrifttype</comment>
+ <comment xml:lang="nl">Adobe FrameMaker-lettertype</comment>
+ <comment xml:lang="nn">Adobe FrameMaker-skrifttype</comment>
+ <comment xml:lang="oc">poliça Adobe FrameMaker</comment>
+ <comment xml:lang="pl">Czcionka Adobe FrameMaker</comment>
+ <comment xml:lang="pt">letra Adobe FrameMaker</comment>
+ <comment xml:lang="pt_BR">Fonte do Adobe FrameMaker</comment>
+ <comment xml:lang="ro">Font Adobe FrameMaker</comment>
+ <comment xml:lang="ru">Шрифт Adobe FrameMaker</comment>
+ <comment xml:lang="sk">Písmo Adobe FrameMaker</comment>
+ <comment xml:lang="sl">Datoteka pisave Adobe FrameMaker</comment>
+ <comment xml:lang="sq">Gërma Adobe FrameMaker</comment>
+ <comment xml:lang="sr">слова Адобе Фрејм Мејкера</comment>
+ <comment xml:lang="sv">Adobe FrameMaker-typsnitt</comment>
+ <comment xml:lang="tr">Adobe FrameMaker yazı tipi</comment>
+ <comment xml:lang="uk">шрифт Adobe FrameMaker</comment>
+ <comment xml:lang="vi">Phông chữ Adobe FrameMaker</comment>
+ <comment xml:lang="zh_CN">Adobe FrameMaker 字体</comment>
+ <comment xml:lang="zh_TW">Adobe FrameMaker 字型</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="&lt;MakerScreenFont" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-font-libgrx">
+ <comment>LIBGRX font</comment>
+ <comment xml:lang="ar">خط LIBGRX</comment>
+ <comment xml:lang="az">LIBGRX yazı növü</comment>
+ <comment xml:lang="be@latin">Šryft LIBGRX</comment>
+ <comment xml:lang="bg">Шрифт — LIBGRX</comment>
+ <comment xml:lang="ca">lletra LIBGRX</comment>
+ <comment xml:lang="cs">font LIBGRX</comment>
+ <comment xml:lang="cy">Ffont LIBGRX</comment>
+ <comment xml:lang="da">LIBGRX-skrifttype</comment>
+ <comment xml:lang="de">LIBGRX-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά LIBGRX</comment>
+ <comment xml:lang="en_GB">LIBGRX font</comment>
+ <comment xml:lang="eo">LIBGRX-tiparo</comment>
+ <comment xml:lang="es">tipo de letra LIBGRX</comment>
+ <comment xml:lang="eu">LIBGRX letra-tipoa</comment>
+ <comment xml:lang="fi">LIBGRX-fontti</comment>
+ <comment xml:lang="fo">LIBGRX stavasnið</comment>
+ <comment xml:lang="fr">police LIBGRX</comment>
+ <comment xml:lang="ga">cló LIBGRX</comment>
+ <comment xml:lang="gl">tipo de fonte en LIBGRX</comment>
+ <comment xml:lang="he">גופן LIBGRX</comment>
+ <comment xml:lang="hr">LIBGRX slovo</comment>
+ <comment xml:lang="hu">LIBGRX-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras LIBGRX</comment>
+ <comment xml:lang="id">Fonta LIBGRX</comment>
+ <comment xml:lang="it">Carattere LIBGRX</comment>
+ <comment xml:lang="ja">LIBGRX フォーマット</comment>
+ <comment xml:lang="kk">LIBGRX қарібі</comment>
+ <comment xml:lang="ko">LIBGRX 글꼴</comment>
+ <comment xml:lang="lt">LIBGRX šriftas</comment>
+ <comment xml:lang="lv">LIBGRX fonts</comment>
+ <comment xml:lang="ms">Font LIBGRX</comment>
+ <comment xml:lang="nb">LIBGRX-skrifttype</comment>
+ <comment xml:lang="nl">LIBGRX-lettertype</comment>
+ <comment xml:lang="nn">LIBGRX skrifttype</comment>
+ <comment xml:lang="oc">poliça LIBGRX</comment>
+ <comment xml:lang="pl">Czcionka LIBGRX</comment>
+ <comment xml:lang="pt">letra LIBGRX</comment>
+ <comment xml:lang="pt_BR">Fonte LIBGRX</comment>
+ <comment xml:lang="ro">Font LIBGRX</comment>
+ <comment xml:lang="ru">Шрифт LIBGRX</comment>
+ <comment xml:lang="sk">Písmo LIBGRX</comment>
+ <comment xml:lang="sl">Datoteka pisave LIBGRX</comment>
+ <comment xml:lang="sq">Lloj gërme LIBGRX</comment>
+ <comment xml:lang="sr">ЛИБГРИкс слова</comment>
+ <comment xml:lang="sv">LIBGRX-typsnitt</comment>
+ <comment xml:lang="tr">LIBGRX fontu</comment>
+ <comment xml:lang="uk">шрифт LIBGRX</comment>
+ <comment xml:lang="vi">Phông chữ LIBGRX</comment>
+ <comment xml:lang="zh_CN">LIBGRX 字体</comment>
+ <comment xml:lang="zh_TW">LIBGRX 字型</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="\x14\x02\x59\x19" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-font-linux-psf">
+ <comment>Linux PSF console font</comment>
+ <comment xml:lang="ar">خط كونسول PSF لينكس</comment>
+ <comment xml:lang="az">Linux PSF konsol yazı növü</comment>
+ <comment xml:lang="be@latin">Kansolny šryft PSF dla Linuksa</comment>
+ <comment xml:lang="bg">Шрифт — PSF, за конзолата на Линукс</comment>
+ <comment xml:lang="ca">lletra de consola PSF de Linux</comment>
+ <comment xml:lang="cs">font PSF pro konzolu Linuxu</comment>
+ <comment xml:lang="cy">Ffont Linux PSF</comment>
+ <comment xml:lang="da">Linux PSF-konsolskrifttype</comment>
+ <comment xml:lang="de">Linux-PSF-Konsolenschrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά κονσόλας PSF Linux</comment>
+ <comment xml:lang="en_GB">Linux PSF console font</comment>
+ <comment xml:lang="eo">PSF-tiparo de Linux-konzolo</comment>
+ <comment xml:lang="es">tipo de letra de consola Linux PSF</comment>
+ <comment xml:lang="eu">Linux PSF kontsolako letra-tipoa</comment>
+ <comment xml:lang="fi">Linux PSF -konsolifontti</comment>
+ <comment xml:lang="fo">Linux PSF stýristøðs stavasnið</comment>
+ <comment xml:lang="fr">police console Linux PSF</comment>
+ <comment xml:lang="ga">cló consóil Linux PSF</comment>
+ <comment xml:lang="gl">tipo de fonte de consola Linux PSF</comment>
+ <comment xml:lang="he">גופן לקונסול מסוג Linux PSF</comment>
+ <comment xml:lang="hr">Linux PSF konzolno slovo</comment>
+ <comment xml:lang="hu">Linux PSF konzolos betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras console Linux PSF</comment>
+ <comment xml:lang="id">Fonta konsol Linux PSF</comment>
+ <comment xml:lang="it">Carattere console Linux PSF</comment>
+ <comment xml:lang="ja">Linux PSF コンソールフォント</comment>
+ <comment xml:lang="kk">Linux PSF консольдік қарібі</comment>
+ <comment xml:lang="ko">리눅스 PSF 콘솔 글꼴</comment>
+ <comment xml:lang="lt">Linux PSF konsolės šriftas</comment>
+ <comment xml:lang="lv">Linux PSF konsoles fonts</comment>
+ <comment xml:lang="ms">Font konsol PSF Linux</comment>
+ <comment xml:lang="nb">Linux PSF konsollskrifttype</comment>
+ <comment xml:lang="nl">Linux PSF-console-lettertype</comment>
+ <comment xml:lang="nn">Linux PSF konsoll-skrifttype</comment>
+ <comment xml:lang="oc">poliça consòla Linux PSF</comment>
+ <comment xml:lang="pl">Czcionka konsoli PSF Linux</comment>
+ <comment xml:lang="pt">letra de consola Linux PSF</comment>
+ <comment xml:lang="pt_BR">Fonte de console Linux PSF</comment>
+ <comment xml:lang="ro">Font consolă Linux PSF</comment>
+ <comment xml:lang="ru">Консольный шрифт Linux PSF</comment>
+ <comment xml:lang="sk">Písmo PSF pre konzolu Linuxu</comment>
+ <comment xml:lang="sl">Datoteka pisave konzole Linux PSF</comment>
+ <comment xml:lang="sq">Lloj gërme për konsolë Linux PSF</comment>
+ <comment xml:lang="sr">слова Линуксове ПСФ конзоле</comment>
+ <comment xml:lang="sv">Linux PSF-konsoltypsnitt</comment>
+ <comment xml:lang="tr">Linux PSF konsol fontu</comment>
+ <comment xml:lang="uk">консольний шрифт Linux PSF</comment>
+ <comment xml:lang="vi">Phông chữ bàn giao tiếp PSF Linux</comment>
+ <comment xml:lang="zh_CN">Linux PSF 控制台字体</comment>
+ <comment xml:lang="zh_TW">Linux PSF console 字型</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="\x36\x04" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.psf"/>
+ </mime-type>
+ <mime-type type="application/x-gz-font-linux-psf">
+ <comment>Linux PSF console font (gzip-compressed)</comment>
+ <comment xml:lang="ar">خط كونسول PSF لينكس (مضغوط-gzip)</comment>
+ <comment xml:lang="be@latin">Kansolny šryft PSF dla Linuksa (gzip-skampresavany)</comment>
+ <comment xml:lang="bg">Шрифт — Linux PSF, компресиран с gzip</comment>
+ <comment xml:lang="ca">lletra de consola PSF de Linux (amb compressió gzip)</comment>
+ <comment xml:lang="cs">font PSF pro konzolu Linuxu (komprimace gzip)</comment>
+ <comment xml:lang="da">Linux PSF-konsolskrifttype (gzip-komprimeret)</comment>
+ <comment xml:lang="de">Linux-PSF-Konsolenschrift (gzip-komprimiert)</comment>
+ <comment xml:lang="el">Γραμματοσειρά κονσόλας PSF Linux (συμπιεσμένη με gzip)</comment>
+ <comment xml:lang="en_GB">Linux PSF console font (gzip-compressed)</comment>
+ <comment xml:lang="es">tipo de letra de consola Linux PSF (comprimido con gzip)</comment>
+ <comment xml:lang="eu">Linux PSF kontsolako letra-tipoa (gzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">Linux PSF -konsolifontti (gzip-pakattu)</comment>
+ <comment xml:lang="fo">Linux PSF stýristøðs stavasnið (gzip-stappað)</comment>
+ <comment xml:lang="fr">police console Linux PSF (compressée gzip)</comment>
+ <comment xml:lang="ga">cló consóil Linux PSF (comhbhrúite le gzip)</comment>
+ <comment xml:lang="gl">tipo de fonte de consola Linux PSF (comprimida con gzip)</comment>
+ <comment xml:lang="he">גופן למסוף מסוג Linux PSF (מכווץ ע״י gzip)</comment>
+ <comment xml:lang="hr">Linux PSF konzolno slovo (gzip sažeto)</comment>
+ <comment xml:lang="hu">Linux PSF konzolos betűkészlet (gzip-tömörítésű)</comment>
+ <comment xml:lang="ia">Typo de litteras console Linux PSF (comprimite con gzip)</comment>
+ <comment xml:lang="id">Fonta konsol Linux PSF (terkompresi gzip)</comment>
+ <comment xml:lang="it">Carattere console Linux PSF (compresso con gzip)</comment>
+ <comment xml:lang="ja">Linux PSF コンソールフォント (gzip 圧縮)</comment>
+ <comment xml:lang="kk">Linux PSF консольдік қарібі (gzip-пен сығылған)</comment>
+ <comment xml:lang="ko">리눅스 PSF 콘솔 글꼴(GZIP 압축)</comment>
+ <comment xml:lang="lt">Linux PSF konsolės šriftas (suglaudintas su gzip)</comment>
+ <comment xml:lang="lv">Linux PSF konsoles fonts (saspiests ar gzip)</comment>
+ <comment xml:lang="nb">Linux PSF konsollskrifttype (gzip-komprimert)</comment>
+ <comment xml:lang="nl">Linux PSF-console-lettertype (ingepakt met gzip)</comment>
+ <comment xml:lang="nn">Linux PSF konsoll-skrifttype (gzip-pakka)</comment>
+ <comment xml:lang="oc">poliça consòla Linux PSF (compressat gzip)</comment>
+ <comment xml:lang="pl">Czcionka konsoli PSF Linux (kompresja gzip)</comment>
+ <comment xml:lang="pt">letra de consola Linux PSF (compressão gzip)</comment>
+ <comment xml:lang="pt_BR">Fonte de console Linux PSF (compactada com gzip)</comment>
+ <comment xml:lang="ro">Font consolă Linux PSF (compresie gzip)</comment>
+ <comment xml:lang="ru">Консольный шрифт Linux PSF (сжатый gzip)</comment>
+ <comment xml:lang="sk">Písmo PSF pre konzolu Linuxu (komprimované pomocou gzip)</comment>
+ <comment xml:lang="sl">Datoteka pisave konzole Linux PSF (skrčena z gzip)</comment>
+ <comment xml:lang="sq">Lloj gërme për konsolë Linux PSF (komresuar me gzip)</comment>
+ <comment xml:lang="sr">слова Линуксове ПСФ конзоле (запакована гзип-ом)</comment>
+ <comment xml:lang="sv">Linux PSF-konsoltypsnitt (gzip-komprimerat)</comment>
+ <comment xml:lang="tr">Linux PSF konsol fontu (gzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">консольний шрифт Linux PSF (стиснений gzip)</comment>
+ <comment xml:lang="vi">Phông chữ bàn giao tiếp PSF Linux (đã nén gzip)</comment>
+ <comment xml:lang="zh_CN">Linux PSF 控制台字体(gzip 压缩)</comment>
+ <comment xml:lang="zh_TW">Linux PSF console 字型 (gzip 格式壓縮)</comment>
+ <sub-class-of type="application/gzip"/>
+ <generic-icon name="font-x-generic"/>
+ <glob pattern="*.psf.gz"/>
+ </mime-type>
+ <mime-type type="application/x-font-pcf">
+ <comment>PCF font</comment>
+ <comment xml:lang="ar">خط PCF</comment>
+ <comment xml:lang="az">PCF yazı növü</comment>
+ <comment xml:lang="be@latin">Šryft PCF</comment>
+ <comment xml:lang="bg">Шрифт — PCF</comment>
+ <comment xml:lang="ca">lletra PCF</comment>
+ <comment xml:lang="cs">font PCF</comment>
+ <comment xml:lang="cy">Ffont PCF</comment>
+ <comment xml:lang="da">PCF-skrifttype</comment>
+ <comment xml:lang="de">PCF-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά PCF</comment>
+ <comment xml:lang="en_GB">PCF font</comment>
+ <comment xml:lang="eo">PCF-tiparo</comment>
+ <comment xml:lang="es">tipo de letra PCF</comment>
+ <comment xml:lang="eu">PCF letra-tipoa</comment>
+ <comment xml:lang="fi">PCF-fontti</comment>
+ <comment xml:lang="fo">PCF stavasnið</comment>
+ <comment xml:lang="fr">police PCF</comment>
+ <comment xml:lang="ga">cló PCF</comment>
+ <comment xml:lang="gl">tipo de letra PCF</comment>
+ <comment xml:lang="he">גופן PCF</comment>
+ <comment xml:lang="hr">PCF slovo</comment>
+ <comment xml:lang="hu">PCF-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras PCF</comment>
+ <comment xml:lang="id">Fonta PCF</comment>
+ <comment xml:lang="it">Carattere PCF</comment>
+ <comment xml:lang="ja">PCF フォント</comment>
+ <comment xml:lang="kk">PCF қарібі</comment>
+ <comment xml:lang="ko">PCF 글꼴</comment>
+ <comment xml:lang="lt">PCF šriftas</comment>
+ <comment xml:lang="lv">PCF fonts</comment>
+ <comment xml:lang="ms">Font PCF</comment>
+ <comment xml:lang="nb">PCF-skrifttype</comment>
+ <comment xml:lang="nl">PCF-lettertype</comment>
+ <comment xml:lang="nn">PCF-skrifttype</comment>
+ <comment xml:lang="oc">poliça PCF</comment>
+ <comment xml:lang="pl">Czcionka PCF</comment>
+ <comment xml:lang="pt">letra PCF</comment>
+ <comment xml:lang="pt_BR">Fonte PCF</comment>
+ <comment xml:lang="ro">Font PCF</comment>
+ <comment xml:lang="ru">Шрифт PCF</comment>
+ <comment xml:lang="sk">Písmo PCF</comment>
+ <comment xml:lang="sl">Datoteka pisave PCF</comment>
+ <comment xml:lang="sq">Gërma PCF</comment>
+ <comment xml:lang="sr">ПЦФ слова</comment>
+ <comment xml:lang="sv">PCF-typsnitt</comment>
+ <comment xml:lang="tr">PCF fontu</comment>
+ <comment xml:lang="uk">шрифт PCF</comment>
+ <comment xml:lang="vi">Phông chữ PCF</comment>
+ <comment xml:lang="zh_CN">PCF 字体</comment>
+ <comment xml:lang="zh_TW">PCF 字型</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="\001fcp" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.pcf"/>
+ <glob pattern="*.pcf.Z"/>
+ <glob pattern="*.pcf.gz"/>
+ </mime-type>
+ <mime-type type="font/otf">
+ <comment>OpenType font</comment>
+ <comment xml:lang="ar">خط OpenType</comment>
+ <comment xml:lang="az">OpenType yazı növü</comment>
+ <comment xml:lang="be@latin">Šryft OpenType</comment>
+ <comment xml:lang="bg">Шрифт — OpenType</comment>
+ <comment xml:lang="ca">lletra OpenType</comment>
+ <comment xml:lang="cs">font OpenType</comment>
+ <comment xml:lang="cy">Ffont OpenType</comment>
+ <comment xml:lang="da">OpenType-skrifttype</comment>
+ <comment xml:lang="de">OpenType-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά OpenType </comment>
+ <comment xml:lang="en_GB">OpenType font</comment>
+ <comment xml:lang="eo">OpenType-tiparo</comment>
+ <comment xml:lang="es">tipo de letra OpenType</comment>
+ <comment xml:lang="eu">OpenType letra-tipoa</comment>
+ <comment xml:lang="fi">OpenType-fontti</comment>
+ <comment xml:lang="fo">OpenType stavasnið</comment>
+ <comment xml:lang="fr">police OpenType</comment>
+ <comment xml:lang="ga">cló OpenType</comment>
+ <comment xml:lang="gl">tipo de fonte OpenType</comment>
+ <comment xml:lang="he">גופן של OpenType</comment>
+ <comment xml:lang="hr">OpenType slovo</comment>
+ <comment xml:lang="hu">OpenType-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras OpenType</comment>
+ <comment xml:lang="id">Fonta OpenType</comment>
+ <comment xml:lang="it">Carattere OpenType</comment>
+ <comment xml:lang="ja">OpenType フォント</comment>
+ <comment xml:lang="kk">OpenType қарібі</comment>
+ <comment xml:lang="ko">오픈타입 글꼴</comment>
+ <comment xml:lang="lt">OpenType šriftas</comment>
+ <comment xml:lang="lv">OpenType fonts</comment>
+ <comment xml:lang="ms">Font OpenType</comment>
+ <comment xml:lang="nb">OpenType-skrifttype</comment>
+ <comment xml:lang="nl">OpenType-lettertype</comment>
+ <comment xml:lang="nn">OpenType-skrifttype</comment>
+ <comment xml:lang="oc">poliça OpenType</comment>
+ <comment xml:lang="pl">Czcionka OpenType</comment>
+ <comment xml:lang="pt">letra OpenType</comment>
+ <comment xml:lang="pt_BR">Fonte OpenType</comment>
+ <comment xml:lang="ro">Font OpenType</comment>
+ <comment xml:lang="ru">Шрифт OpenType</comment>
+ <comment xml:lang="sk">Písmo OpenType</comment>
+ <comment xml:lang="sl">Datoteka pisave OpenType</comment>
+ <comment xml:lang="sq">Gërma OpenType</comment>
+ <comment xml:lang="sr">слова Отворене Врсте</comment>
+ <comment xml:lang="sv">OpenType-typsnitt</comment>
+ <comment xml:lang="tr">OpenType fontu</comment>
+ <comment xml:lang="uk">шрифт OpenType</comment>
+ <comment xml:lang="vi">Phông chữ OpenType</comment>
+ <comment xml:lang="zh_CN">OpenType 字体</comment>
+ <comment xml:lang="zh_TW">OpenType 字型</comment>
+ <sub-class-of type="font/ttf"/>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="OTTO" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.otf"/>
+ <alias type="application/x-font-otf"/>
+ </mime-type>
+ <mime-type type="application/x-font-speedo">
+ <comment>Speedo font</comment>
+ <comment xml:lang="ar">خط Speedo</comment>
+ <comment xml:lang="az">Speedo yazı növü</comment>
+ <comment xml:lang="be@latin">Šryft Speedo</comment>
+ <comment xml:lang="bg">Шрифт — Speedo</comment>
+ <comment xml:lang="ca">lletra Speedo</comment>
+ <comment xml:lang="cs">font Speedo</comment>
+ <comment xml:lang="cy">Ffont Speedo</comment>
+ <comment xml:lang="da">Speedoskrifttype</comment>
+ <comment xml:lang="de">Speedo-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά Speedo</comment>
+ <comment xml:lang="en_GB">Speedo font</comment>
+ <comment xml:lang="eo">Speedo-tiparo</comment>
+ <comment xml:lang="es">tipo de letra de Speedo</comment>
+ <comment xml:lang="eu">Speedo letra-tipoa</comment>
+ <comment xml:lang="fi">Speedo-fontti</comment>
+ <comment xml:lang="fo">Speedo stavasnið</comment>
+ <comment xml:lang="fr">police Speedo</comment>
+ <comment xml:lang="ga">cló Speedo</comment>
+ <comment xml:lang="gl">tipo de letra Speedo</comment>
+ <comment xml:lang="he">גופן של Speedo</comment>
+ <comment xml:lang="hr">Speedo slovo</comment>
+ <comment xml:lang="hu">Speedo-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras Speedo</comment>
+ <comment xml:lang="id">Fonta Speedo</comment>
+ <comment xml:lang="it">Carattere Speedo</comment>
+ <comment xml:lang="ja">Speedo フォント</comment>
+ <comment xml:lang="kk">Speedo қарібі</comment>
+ <comment xml:lang="ko">Speedo 글꼴</comment>
+ <comment xml:lang="lt">Speedo šriftas</comment>
+ <comment xml:lang="lv">Speedo fonts</comment>
+ <comment xml:lang="ms">Font Speedo</comment>
+ <comment xml:lang="nb">Speedo-skrifttype</comment>
+ <comment xml:lang="nl">Speedo-lettertype</comment>
+ <comment xml:lang="nn">Speedo-skrifttype</comment>
+ <comment xml:lang="oc">poliça Speedo</comment>
+ <comment xml:lang="pl">Czcionka Speedo</comment>
+ <comment xml:lang="pt">letra Speedo</comment>
+ <comment xml:lang="pt_BR">Fonte Speedo</comment>
+ <comment xml:lang="ro">Font Speedo</comment>
+ <comment xml:lang="ru">Шрифт Speedo</comment>
+ <comment xml:lang="sk">Písmo Speedo</comment>
+ <comment xml:lang="sl">Datoteka pisave Speedo</comment>
+ <comment xml:lang="sq">Gërma Speedo</comment>
+ <comment xml:lang="sr">Спидо слова</comment>
+ <comment xml:lang="sv">Speedo-typsnitt</comment>
+ <comment xml:lang="tr">Speedo fontu</comment>
+ <comment xml:lang="uk">шрифт Speedo</comment>
+ <comment xml:lang="vi">Phông chữ Speedo</comment>
+ <comment xml:lang="zh_CN">Speedo 字体</comment>
+ <comment xml:lang="zh_TW">Speedo 字型</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="D1.0\015" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.spd"/>
+ </mime-type>
+ <mime-type type="application/x-font-sunos-news">
+ <comment>SunOS News font</comment>
+ <comment xml:lang="ar">خط SunOS News</comment>
+ <comment xml:lang="az">SunOS News yazı növü</comment>
+ <comment xml:lang="be@latin">Šryft SunOS News</comment>
+ <comment xml:lang="bg">Шрифт — SunOS News</comment>
+ <comment xml:lang="ca">lletra News de SunOS</comment>
+ <comment xml:lang="cs">font SunOS News</comment>
+ <comment xml:lang="cy">Ffont SunOS News</comment>
+ <comment xml:lang="da">SunOS News-skrifttype</comment>
+ <comment xml:lang="de">SunOS-News-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά SunOS News</comment>
+ <comment xml:lang="en_GB">SunOS News font</comment>
+ <comment xml:lang="eo">tiparo de SunOS News</comment>
+ <comment xml:lang="es">tipo de letra para NeWS de SunOS</comment>
+ <comment xml:lang="eu">SunOs News letra-tipoa</comment>
+ <comment xml:lang="fi">SunOS News -fontti</comment>
+ <comment xml:lang="fo">SunOS News stavasnið</comment>
+ <comment xml:lang="fr">police SunOS News</comment>
+ <comment xml:lang="ga">cló SunOS News</comment>
+ <comment xml:lang="gl">tipo de letra SunOS News</comment>
+ <comment xml:lang="he">גופן של SunOS News</comment>
+ <comment xml:lang="hr">SunOS News slovo</comment>
+ <comment xml:lang="hu">SunOS News-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras SunOS News</comment>
+ <comment xml:lang="id">Fonta SunOS News</comment>
+ <comment xml:lang="it">Carattere SunOS News</comment>
+ <comment xml:lang="ja">SunOS News フォント</comment>
+ <comment xml:lang="kk">SunOS News қарібі</comment>
+ <comment xml:lang="ko">SunOS News 글꼴</comment>
+ <comment xml:lang="lt">SunOS News šriftas</comment>
+ <comment xml:lang="lv">SunOS News fonts</comment>
+ <comment xml:lang="ms">Font News SunOS</comment>
+ <comment xml:lang="nb">SunOS News-skrifttype</comment>
+ <comment xml:lang="nl">SunOS News-lettertype</comment>
+ <comment xml:lang="nn">SunOS NEWS-skrifttype</comment>
+ <comment xml:lang="oc">poliça SunOS News</comment>
+ <comment xml:lang="pl">Czcionka SunOS News</comment>
+ <comment xml:lang="pt">letra SunOS News</comment>
+ <comment xml:lang="pt_BR">Fonte SunOS News</comment>
+ <comment xml:lang="ro">Font SunOS News</comment>
+ <comment xml:lang="ru">Шрифт SunOS News</comment>
+ <comment xml:lang="sk">Písmo SunOS News</comment>
+ <comment xml:lang="sl">Datoteka pisave SunOS News</comment>
+ <comment xml:lang="sq">Gërma SunOS News</comment>
+ <comment xml:lang="sr">слова СанОС Њуза</comment>
+ <comment xml:lang="sv">SunOS News-typsnitt</comment>
+ <comment xml:lang="tr">SunOS News yazı tipi</comment>
+ <comment xml:lang="uk">шрифт SunOS News</comment>
+ <comment xml:lang="vi">Phông chữ SunOS News</comment>
+ <comment xml:lang="zh_CN">SunOS News 字体</comment>
+ <comment xml:lang="zh_TW">SunOS News 字型</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="StartFont" type="string" offset="0"/>
+ <match value="\x13\x7A\x29" type="string" offset="0"/>
+ <match value="\x13\x7A\x2B" type="string" offset="8"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-font-tex">
+ <comment>TeX font</comment>
+ <comment xml:lang="ar">خط TeX</comment>
+ <comment xml:lang="az">TeX yazı növü</comment>
+ <comment xml:lang="be@latin">Šryft TeX</comment>
+ <comment xml:lang="bg">Шрифт — TeX</comment>
+ <comment xml:lang="ca">lletra TeX</comment>
+ <comment xml:lang="cs">font TeX</comment>
+ <comment xml:lang="cy">Ffont TeX</comment>
+ <comment xml:lang="da">TeX-skrifttype</comment>
+ <comment xml:lang="de">TeX-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά TeX</comment>
+ <comment xml:lang="en_GB">TeX font</comment>
+ <comment xml:lang="eo">TeX-tiparo</comment>
+ <comment xml:lang="es">tipo de letra de TeX</comment>
+ <comment xml:lang="eu">TeX letra-tipoa</comment>
+ <comment xml:lang="fi">TeX-fontti</comment>
+ <comment xml:lang="fo">TeX stavasnið</comment>
+ <comment xml:lang="fr">police TeX</comment>
+ <comment xml:lang="ga">cló TeX</comment>
+ <comment xml:lang="gl">tipo de letra de TeX</comment>
+ <comment xml:lang="he">גופן TeX</comment>
+ <comment xml:lang="hr">TeX slovo</comment>
+ <comment xml:lang="hu">TeX-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras TeX</comment>
+ <comment xml:lang="id">Fonta TeX</comment>
+ <comment xml:lang="it">Carattere TeX</comment>
+ <comment xml:lang="ja">TeX フォント</comment>
+ <comment xml:lang="kk">TeX қарібі</comment>
+ <comment xml:lang="ko">TeX 글꼴</comment>
+ <comment xml:lang="lt">TeX šriftas</comment>
+ <comment xml:lang="lv">TeX fonts</comment>
+ <comment xml:lang="ms">Font TeX</comment>
+ <comment xml:lang="nb">TeX-skrift</comment>
+ <comment xml:lang="nl">TeX-lettertype</comment>
+ <comment xml:lang="nn">TeX-skrifttype</comment>
+ <comment xml:lang="oc">poliça TeX</comment>
+ <comment xml:lang="pl">Czcionka TeX</comment>
+ <comment xml:lang="pt">letra TeX</comment>
+ <comment xml:lang="pt_BR">Fonte TeX</comment>
+ <comment xml:lang="ro">Font TeX</comment>
+ <comment xml:lang="ru">Шрифт TeX</comment>
+ <comment xml:lang="sk">Písmo TeX</comment>
+ <comment xml:lang="sl">Datoteka pisave TeX</comment>
+ <comment xml:lang="sq">Gërma TeX</comment>
+ <comment xml:lang="sr">ТеКс слова</comment>
+ <comment xml:lang="sv">TeX-typsnitt</comment>
+ <comment xml:lang="tr">TeX fontu</comment>
+ <comment xml:lang="uk">шрифт TeX</comment>
+ <comment xml:lang="vi">Phông chữ TeX</comment>
+ <comment xml:lang="zh_CN">TeX 字体</comment>
+ <comment xml:lang="zh_TW">TeX 字型</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="\367\203" type="string" offset="0"/>
+ <match value="\367\131" type="string" offset="0"/>
+ <match value="\367\312" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-font-tex-tfm">
+ <comment>TeX font metrics</comment>
+ <comment xml:lang="ar">مقاييس خط TeX</comment>
+ <comment xml:lang="az">TeX yazı növü metrikləri</comment>
+ <comment xml:lang="be@latin">Metryka šryftu TeX</comment>
+ <comment xml:lang="bg">Шрифтова метрика — TeX</comment>
+ <comment xml:lang="ca">mètrica de lletra de TeX</comment>
+ <comment xml:lang="cs">metrika fontu TeX</comment>
+ <comment xml:lang="cy">Metrigau Ffont TeX</comment>
+ <comment xml:lang="da">TeX-skrifttypeinformation</comment>
+ <comment xml:lang="de">TeX-Schriftmetriken</comment>
+ <comment xml:lang="el">Μετρικά γραμματοσειράς TeX</comment>
+ <comment xml:lang="en_GB">TeX font metrics</comment>
+ <comment xml:lang="eo">metrikoj de TeX-tiparo</comment>
+ <comment xml:lang="es">métricas tipográficas de TeX</comment>
+ <comment xml:lang="eu">TeX letra-tipoen neurriak</comment>
+ <comment xml:lang="fi">TeX-fonttimitat</comment>
+ <comment xml:lang="fr">métriques de police TeX</comment>
+ <comment xml:lang="ga">meadarachtaí cló TeX</comment>
+ <comment xml:lang="gl">Métricas de tipo de letra de TeX</comment>
+ <comment xml:lang="he">ממדי גופן של TeX</comment>
+ <comment xml:lang="hr">TeX mjere slova</comment>
+ <comment xml:lang="hu">TeX-betűmetrika</comment>
+ <comment xml:lang="ia">Metricas de typo de litteras TeX</comment>
+ <comment xml:lang="id">Fonta metrik TeX</comment>
+ <comment xml:lang="it">Metriche tipo carattere TeX</comment>
+ <comment xml:lang="ja">TeX フォントメトリック</comment>
+ <comment xml:lang="kk">TeX қаріп метрикалары</comment>
+ <comment xml:lang="ko">TeX 글꼴 메트릭</comment>
+ <comment xml:lang="lt">TeX šriftų metrika</comment>
+ <comment xml:lang="lv">TeX fonta metrikas</comment>
+ <comment xml:lang="ms">Metrik font TeX</comment>
+ <comment xml:lang="nb">TeX skrifttypemetrikk</comment>
+ <comment xml:lang="nl">TeX-lettertype-metrieken</comment>
+ <comment xml:lang="nn">TeX skrifttypemetrikk</comment>
+ <comment xml:lang="oc">metricas de poliça TeX</comment>
+ <comment xml:lang="pl">Metryki czcionki TeX</comment>
+ <comment xml:lang="pt">métricas de letra TeX</comment>
+ <comment xml:lang="pt_BR">Métrica de fonte TeX</comment>
+ <comment xml:lang="ro">Dimensiuni font TeX</comment>
+ <comment xml:lang="ru">Метрика шрифта TeX</comment>
+ <comment xml:lang="sk">Metrika písma TeX</comment>
+ <comment xml:lang="sl">Matrika pisave Tex</comment>
+ <comment xml:lang="sq">Gërma TeX metrics</comment>
+ <comment xml:lang="sr">метрика слова ТеКс-а</comment>
+ <comment xml:lang="sv">TeX-typsnittsmetrik</comment>
+ <comment xml:lang="tr">TeX yazı tipi ölçüleri</comment>
+ <comment xml:lang="uk">метрики шрифту TeX</comment>
+ <comment xml:lang="vi">Cách đo phông chữ TeX</comment>
+ <comment xml:lang="zh_CN">TeX 字体规格</comment>
+ <comment xml:lang="zh_TW">TeX 字型描述檔</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="\000\021" type="string" offset="2"/>
+ <match value="\000\022" type="string" offset="2"/>
+ </magic>
+ </mime-type>
+ <mime-type type="font/ttf">
+ <comment>TrueType font</comment>
+ <comment xml:lang="ar">خط TrueType</comment>
+ <comment xml:lang="be@latin">Šryft TrueType</comment>
+ <comment xml:lang="bg">Шрифт — TrueType</comment>
+ <comment xml:lang="ca">lletra TrueType</comment>
+ <comment xml:lang="cs">font TrueType</comment>
+ <comment xml:lang="da">TrueType-skrifttype</comment>
+ <comment xml:lang="de">TrueType-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά TrueType</comment>
+ <comment xml:lang="en_GB">TrueType font</comment>
+ <comment xml:lang="eo">TrueType-tiparo</comment>
+ <comment xml:lang="es">tipo de letra TrueType</comment>
+ <comment xml:lang="eu">TrueType letra-tipoa</comment>
+ <comment xml:lang="fi">TrueType-fontti</comment>
+ <comment xml:lang="fo">TrueType stavasnið</comment>
+ <comment xml:lang="fr">police Truetype</comment>
+ <comment xml:lang="ga">cló TrueType</comment>
+ <comment xml:lang="gl">tipo de letra TrueType</comment>
+ <comment xml:lang="he">גופן מסוג TrueType</comment>
+ <comment xml:lang="hr">TrueType slovo</comment>
+ <comment xml:lang="hu">TrueType-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras TrueType</comment>
+ <comment xml:lang="id">Fonta TrueType</comment>
+ <comment xml:lang="it">Carattere TrueType</comment>
+ <comment xml:lang="ja">TrueType フォント</comment>
+ <comment xml:lang="kk">TrueType қарібі</comment>
+ <comment xml:lang="ko">트루타입 글꼴</comment>
+ <comment xml:lang="lt">TrueType šriftas</comment>
+ <comment xml:lang="lv">TrueType fonts</comment>
+ <comment xml:lang="ms">Font TrueType</comment>
+ <comment xml:lang="nb">TrueType-skrift</comment>
+ <comment xml:lang="nl">TrueType-lettertype</comment>
+ <comment xml:lang="nn">TrueType-skrifttype</comment>
+ <comment xml:lang="oc">poliça Truetype</comment>
+ <comment xml:lang="pl">Czcionka TrueType</comment>
+ <comment xml:lang="pt">letra TrueType</comment>
+ <comment xml:lang="pt_BR">Fonte TrueType</comment>
+ <comment xml:lang="ro">Font TrueType</comment>
+ <comment xml:lang="ru">Шрифт TrueType</comment>
+ <comment xml:lang="sk">Písmo TrueType</comment>
+ <comment xml:lang="sl">Datoteka pisave TrueType</comment>
+ <comment xml:lang="sq">Lloj gërme TrueType</comment>
+ <comment xml:lang="sr">Трутајп слова</comment>
+ <comment xml:lang="sv">Truetype-typsnitt</comment>
+ <comment xml:lang="tr">TrueType fontu</comment>
+ <comment xml:lang="uk">шрифт TrueType</comment>
+ <comment xml:lang="vi">Phông chữ TrueType</comment>
+ <comment xml:lang="zh_CN">TrueType 字体</comment>
+ <comment xml:lang="zh_TW">TrueType 字型</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="FFIL" type="string" offset="0"/>
+ <match value="FFIL" type="string" offset="65"/>
+ <match value="\000\001\000\000\000" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.ttf"/>
+ <alias type="application/x-font-ttf"/>
+ </mime-type>
+ <mime-type type="font/collection">
+ <comment>Font collection</comment>
+ <comment xml:lang="ca">ccol·lecció de lletres</comment>
+ <comment xml:lang="cs">kolekce fontů</comment>
+ <comment xml:lang="de">Schriftsammlung</comment>
+ <comment xml:lang="en_GB">Font collection</comment>
+ <comment xml:lang="es">colección tipográfica</comment>
+ <comment xml:lang="fi">Fonttikokoelma</comment>
+ <comment xml:lang="hr">Kolekcija slova</comment>
+ <comment xml:lang="hu">Betűkészlet-gyűjtemény</comment>
+ <comment xml:lang="id">Koleksi fonta</comment>
+ <comment xml:lang="it">Raccolta di caratteri</comment>
+ <comment xml:lang="kk">Қаріптер жинағы</comment>
+ <comment xml:lang="ko">글꼴 모음</comment>
+ <comment xml:lang="pl">Kolekcja czcionek</comment>
+ <comment xml:lang="pt_BR">coleção de fontes</comment>
+ <comment xml:lang="ru">Коллекция шрифтов</comment>
+ <comment xml:lang="sk">Zbierka písiem</comment>
+ <comment xml:lang="sv">Typsnittssamling</comment>
+ <comment xml:lang="uk">збірка шрифтів</comment>
+ <comment xml:lang="zh_CN">字体集</comment>
+ <comment xml:lang="zh_TW">字型集</comment>
+ <generic-icon name="font-x-generic"/>
+ <glob pattern="*.ttc"/>
+ </mime-type>
+ <mime-type type="application/x-font-ttx">
+ <comment>TrueType XML font</comment>
+ <comment xml:lang="ar">خط TrueType XML</comment>
+ <comment xml:lang="be@latin">Šryft TrueType XML</comment>
+ <comment xml:lang="bg">Шрифт — TrueType XML</comment>
+ <comment xml:lang="ca">lletra XML de TrueType</comment>
+ <comment xml:lang="cs">font TrueType XML</comment>
+ <comment xml:lang="da">TrueType XML-skrifttype</comment>
+ <comment xml:lang="de">TrueType-XML-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά XML TrueType</comment>
+ <comment xml:lang="en_GB">TrueType XML font</comment>
+ <comment xml:lang="es">tipo de letra TrueType XML</comment>
+ <comment xml:lang="eu">TrueType XML letra-tipoa</comment>
+ <comment xml:lang="fi">TrueType-XML-fontti</comment>
+ <comment xml:lang="fo">TrueType XML stavasnið</comment>
+ <comment xml:lang="fr">police Truetype XML</comment>
+ <comment xml:lang="ga">cló XML TrueType</comment>
+ <comment xml:lang="gl">tipo de letra TrueType XML</comment>
+ <comment xml:lang="he">גופן XML מסוג TrueType</comment>
+ <comment xml:lang="hr">TrueType XML slovo</comment>
+ <comment xml:lang="hu">TrueType XML betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras TrueType XML</comment>
+ <comment xml:lang="id">Fonta TrueType XML</comment>
+ <comment xml:lang="it">Carattere TrueType XML</comment>
+ <comment xml:lang="ja">TrueType XML フォント</comment>
+ <comment xml:lang="kk">TrueType XML қарібі</comment>
+ <comment xml:lang="ko">트루타입 XML 글꼴</comment>
+ <comment xml:lang="lt">TrueType XML šriftas</comment>
+ <comment xml:lang="lv">TrueType XML fonts</comment>
+ <comment xml:lang="nb">TrueType XML-skrift</comment>
+ <comment xml:lang="nl">TrueType XML-lettertype</comment>
+ <comment xml:lang="nn">TrueType XML-skrifttype</comment>
+ <comment xml:lang="oc">poliça Truetype XML</comment>
+ <comment xml:lang="pl">Czcionka TrueType XML</comment>
+ <comment xml:lang="pt">letra TrueType XML</comment>
+ <comment xml:lang="pt_BR">Fonte TrueType XML</comment>
+ <comment xml:lang="ro">Font XML TrueType</comment>
+ <comment xml:lang="ru">Шрифт TrueType XML</comment>
+ <comment xml:lang="sk">Písmo TrueType XML</comment>
+ <comment xml:lang="sl">Datoteka pisave TrueType XML</comment>
+ <comment xml:lang="sq">Lloj gërme TrueType XML</comment>
+ <comment xml:lang="sr">Трутајп ИксМЛ слова</comment>
+ <comment xml:lang="sv">Truetype XML-typsnitt</comment>
+ <comment xml:lang="tr">TrueType XML fontu</comment>
+ <comment xml:lang="uk">шрифт TrueType XML</comment>
+ <comment xml:lang="vi">Phông chữ XML TrueType</comment>
+ <comment xml:lang="zh_CN">TrueType XML 字体</comment>
+ <comment xml:lang="zh_TW">TrueType XML 字型</comment>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="&lt;ttFont sfntVersion=&quot;\\x00\\x01\\x00\\x00&quot; ttLibVersion=&quot;" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.ttx"/>
+ </mime-type>
+ <mime-type type="application/x-font-vfont">
+ <comment>V font</comment>
+ <comment xml:lang="ar">خط V</comment>
+ <comment xml:lang="az">V yazı növü</comment>
+ <comment xml:lang="be@latin">Šryft V</comment>
+ <comment xml:lang="bg">Шрифт — V</comment>
+ <comment xml:lang="ca">lletra V</comment>
+ <comment xml:lang="cs">font V</comment>
+ <comment xml:lang="cy">Ffont V</comment>
+ <comment xml:lang="da">V-skrifttype</comment>
+ <comment xml:lang="de">V-Schrift</comment>
+ <comment xml:lang="el">Γραμματοσειρά V</comment>
+ <comment xml:lang="en_GB">V font</comment>
+ <comment xml:lang="eo">V-tiparo</comment>
+ <comment xml:lang="es">tipo de letra V</comment>
+ <comment xml:lang="eu">V letra-tipoa</comment>
+ <comment xml:lang="fi">V-fontti</comment>
+ <comment xml:lang="fo">V stavasnið</comment>
+ <comment xml:lang="fr">police V</comment>
+ <comment xml:lang="ga">cló V</comment>
+ <comment xml:lang="gl">tipo de letra V</comment>
+ <comment xml:lang="he">גופן של V</comment>
+ <comment xml:lang="hr">V slovo</comment>
+ <comment xml:lang="hu">V-betűkészlet</comment>
+ <comment xml:lang="ia">Typo de litteras V</comment>
+ <comment xml:lang="id">Fonta V</comment>
+ <comment xml:lang="it">Carattere V</comment>
+ <comment xml:lang="ja">V フォント</comment>
+ <comment xml:lang="kk">V font қарібі</comment>
+ <comment xml:lang="ko">V 글꼴</comment>
+ <comment xml:lang="lt">V šriftas</comment>
+ <comment xml:lang="lv">V fonts</comment>
+ <comment xml:lang="ms">Font V</comment>
+ <comment xml:lang="nb">V-skrift</comment>
+ <comment xml:lang="nl">V-lettertype</comment>
+ <comment xml:lang="nn">V-skrifttype</comment>
+ <comment xml:lang="oc">poliça V</comment>
+ <comment xml:lang="pl">Czcionka V</comment>
+ <comment xml:lang="pt">letra V</comment>
+ <comment xml:lang="pt_BR">Fonte V</comment>
+ <comment xml:lang="ro">Font V</comment>
+ <comment xml:lang="ru">Шрифт V font</comment>
+ <comment xml:lang="sk">Písmo V</comment>
+ <comment xml:lang="sl">Datoteka pisave V</comment>
+ <comment xml:lang="sq">Gërmë V</comment>
+ <comment xml:lang="sr">В слова</comment>
+ <comment xml:lang="sv">V-typsnitt</comment>
+ <comment xml:lang="tr">V fontu</comment>
+ <comment xml:lang="uk">V-шрифт</comment>
+ <comment xml:lang="vi">Phông chữ V</comment>
+ <comment xml:lang="zh_CN">V 字体</comment>
+ <comment xml:lang="zh_TW">V 字型</comment>
+ <generic-icon name="font-x-generic"/>
+ <magic priority="50">
+ <match value="FONT" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/vnd.framemaker">
+ <comment>Adobe FrameMaker document</comment>
+ <comment xml:lang="ar">مستند أدوبي الصانع للإطارات</comment>
+ <comment xml:lang="ast">Documentu d'Adobe FrameMaker</comment>
+ <comment xml:lang="be@latin">Dakument Adobe FrameMaker</comment>
+ <comment xml:lang="bg">Документ — Adobe FrameMaker</comment>
+ <comment xml:lang="ca">document d'Adobe FrameMaker</comment>
+ <comment xml:lang="cs">dokument Adobe FrameMaker</comment>
+ <comment xml:lang="da">Adobe FrameMaker-dokument</comment>
+ <comment xml:lang="de">Adobe-FrameMaker-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Adobe FrameMaker</comment>
+ <comment xml:lang="en_GB">Adobe FrameMaker document</comment>
+ <comment xml:lang="eo">Dokumento de Adobe FrameMaker</comment>
+ <comment xml:lang="es">documento de Adobe FrameMaker</comment>
+ <comment xml:lang="eu">Adobe FrameMaker-en dokumentua</comment>
+ <comment xml:lang="fi">Adobe FrameMaker -asiakirja</comment>
+ <comment xml:lang="fo">Adobe FrameMaker skjal</comment>
+ <comment xml:lang="fr">document Adobe FrameMaker</comment>
+ <comment xml:lang="ga">cáipéis Adobe FrameMaker</comment>
+ <comment xml:lang="gl">documento de Adobe FrameMaker</comment>
+ <comment xml:lang="he">מסמך Adobe FrameMaker</comment>
+ <comment xml:lang="hr">Adobe FrameMaker dokument</comment>
+ <comment xml:lang="hu">Adobe FrameMaker-dokumentum</comment>
+ <comment xml:lang="ia">Documento Adobe FrameMaker</comment>
+ <comment xml:lang="id">Dokumen Adobe FrameMaker</comment>
+ <comment xml:lang="it">Documento Adobe FrameMaker</comment>
+ <comment xml:lang="ja">Adobe FrameMaker ドキュメント</comment>
+ <comment xml:lang="ka">Adobe FrameMaker-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">Adobe FrameMaker құжаты</comment>
+ <comment xml:lang="ko">Adobe 프레임메이커 문서</comment>
+ <comment xml:lang="lt">Adobe FrameMaker dokumentas</comment>
+ <comment xml:lang="lv">Adobe FrameMaker dokuments</comment>
+ <comment xml:lang="nb">Adobe FrameMaker-dokument</comment>
+ <comment xml:lang="nl">Adobe FrameMaker-document</comment>
+ <comment xml:lang="nn">Adobe FrameMaker-dokument</comment>
+ <comment xml:lang="oc">document Adobe FrameMaker</comment>
+ <comment xml:lang="pl">Dokument Adobe FrameMaker</comment>
+ <comment xml:lang="pt">documento Adobe FrameMaker</comment>
+ <comment xml:lang="pt_BR">Documento do Adobe FrameMaker</comment>
+ <comment xml:lang="ro">Document Adobe FrameMaker</comment>
+ <comment xml:lang="ru">Документ Adobe FrameMaker</comment>
+ <comment xml:lang="sk">Dokument Adobe FrameMaker</comment>
+ <comment xml:lang="sl">Dokument Adobe FrameMaker</comment>
+ <comment xml:lang="sq">Dokument Adobe FrameMaker</comment>
+ <comment xml:lang="sr">документ Адобе Фреј Мејкера</comment>
+ <comment xml:lang="sv">Adobe FrameMaker-dokument</comment>
+ <comment xml:lang="tr">Adobe FrameMaker belgesi</comment>
+ <comment xml:lang="uk">документ Adobe FrameMaker</comment>
+ <comment xml:lang="vi">Tài liệu Adobe FrameMaker</comment>
+ <comment xml:lang="zh_CN">Adobe FrameMaker 文档</comment>
+ <comment xml:lang="zh_TW">Adobe FrameMaker 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="&lt;MakerFile" type="string" offset="0"/>
+ <match value="&lt;MIFFile" type="string" offset="0"/>
+ <match value="&lt;MakerDictionary" type="string" offset="0"/>
+ <match value="&lt;MakerScreenFon" type="string" offset="0"/>
+ <match value="&lt;MML" type="string" offset="0"/>
+ <match value="&lt;Book" type="string" offset="0"/>
+ <match value="&lt;Maker" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.fm"/>
+ <alias type="application/x-frame"/>
+ </mime-type>
+ <mime-type type="application/x-gameboy-rom">
+ <comment>Game Boy ROM</comment>
+ <comment xml:lang="ar">Game Boy ROM</comment>
+ <comment xml:lang="be@latin">Game Boy ROM</comment>
+ <comment xml:lang="bg">ROM — Game Boy</comment>
+ <comment xml:lang="ca">ROM de Game Boy</comment>
+ <comment xml:lang="cs">ROM pro Game Boy</comment>
+ <comment xml:lang="da">Game Boy-rom</comment>
+ <comment xml:lang="de">Game Boy ROM</comment>
+ <comment xml:lang="el">Game Boy ROM</comment>
+ <comment xml:lang="en_GB">Game Boy ROM</comment>
+ <comment xml:lang="eo">NLM de Game Boy</comment>
+ <comment xml:lang="es">ROM de Game Boy</comment>
+ <comment xml:lang="eu">Game Boy-eko ROMa</comment>
+ <comment xml:lang="fi">Game Boy -ROM</comment>
+ <comment xml:lang="fo">Game Boy ROM</comment>
+ <comment xml:lang="fr">ROM Game Boy</comment>
+ <comment xml:lang="ga">ROM Game Boy</comment>
+ <comment xml:lang="gl">ROM de Game Boy</comment>
+ <comment xml:lang="he">ROM של Game Boy</comment>
+ <comment xml:lang="hr">Game Boy ROM</comment>
+ <comment xml:lang="hu">Game Boy ROM</comment>
+ <comment xml:lang="ia">ROM de Game Boy</comment>
+ <comment xml:lang="id">Memori baca-saja Game Boy</comment>
+ <comment xml:lang="it">ROM Game Boy</comment>
+ <comment xml:lang="ja">ゲームボーイ ROM</comment>
+ <comment xml:lang="ka">Game Boy-ის ROM</comment>
+ <comment xml:lang="kk">Game Boy ROM</comment>
+ <comment xml:lang="ko">게임보이 롬</comment>
+ <comment xml:lang="lt">Game Boy ROM</comment>
+ <comment xml:lang="lv">Game Boy ROM</comment>
+ <comment xml:lang="ms">ROM Game Boy</comment>
+ <comment xml:lang="nb">Game Boy-ROM</comment>
+ <comment xml:lang="nl">Game Boy-ROM</comment>
+ <comment xml:lang="nn">Game Boy-ROM</comment>
+ <comment xml:lang="oc">ROM Game Boy</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Game Boy</comment>
+ <comment xml:lang="pt">ROM Game Boy</comment>
+ <comment xml:lang="pt_BR">ROM de Game Boy</comment>
+ <comment xml:lang="ro">ROM Game Boy</comment>
+ <comment xml:lang="ru">Game Boy ROM</comment>
+ <comment xml:lang="sk">ROM pre Game Boy</comment>
+ <comment xml:lang="sl">Bralni pomnilnik Game Boy</comment>
+ <comment xml:lang="sq">ROM Game Boy</comment>
+ <comment xml:lang="sr">Гејм Бој РОМ</comment>
+ <comment xml:lang="sv">Game Boy-rom</comment>
+ <comment xml:lang="tr">Game Boy ROM</comment>
+ <comment xml:lang="uk">ППП Game Boy</comment>
+ <comment xml:lang="vi">ROM Game Boy</comment>
+ <comment xml:lang="zh_CN">Game Boy ROM</comment>
+ <comment xml:lang="zh_TW">Game Boy ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="\xce\xed\x66\x66\xcc\x0d\x00\x0b\x03\x73\x00\x83\x00\x0c\x00\x0d\x00\x08\x11\x1f\x88\x89\x00\x0e" type="string" offset="260">
+ <match value="0x0" type="byte" offset="323" mask="0x80"/>
+ </match>
+ </magic>
+ <glob pattern="*.gb"/>
+ <glob pattern="*.sgb"/>
+ </mime-type>
+ <mime-type type="application/x-gameboy-color-rom">
+ <comment>Game Boy Color ROM</comment>
+ <comment xml:lang="ca">ROM de Game Boy Color</comment>
+ <comment xml:lang="cs">ROM pro Game Boy Color</comment>
+ <comment xml:lang="da">Game Boy Color ROM</comment>
+ <comment xml:lang="de">Game Boy Color ROM</comment>
+ <comment xml:lang="en_GB">Game Boy Colour ROM</comment>
+ <comment xml:lang="es">ROM de Game Boy Color</comment>
+ <comment xml:lang="eu">Game Boy Color ROM</comment>
+ <comment xml:lang="fi">Game Boy Color -ROM</comment>
+ <comment xml:lang="fr">ROM Game Boy Color</comment>
+ <comment xml:lang="ga">ROM Game Boy Color</comment>
+ <comment xml:lang="he">ROM של Game Boy Color</comment>
+ <comment xml:lang="hr">Game Boy Color ROM</comment>
+ <comment xml:lang="hu">Game Boy Color ROM</comment>
+ <comment xml:lang="id">ROM Game Boy Color</comment>
+ <comment xml:lang="it">ROM Game Boy Color</comment>
+ <comment xml:lang="kk">Game Boy Color ROM</comment>
+ <comment xml:lang="ko">게임보이 컬러 롬</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Game Boy Color</comment>
+ <comment xml:lang="pt_BR">ROM de Game Boy Color</comment>
+ <comment xml:lang="ru">Game Boy Color ROM</comment>
+ <comment xml:lang="sk">ROM pre Game Boy Color</comment>
+ <comment xml:lang="sr">Гејм Бој РОМ боје</comment>
+ <comment xml:lang="sv">Game Boy Color-rom</comment>
+ <comment xml:lang="tr">Game Boy Color ROM</comment>
+ <comment xml:lang="uk">ППП Game Boy Color</comment>
+ <comment xml:lang="zh_CN">Game Boy Color ROM</comment>
+ <comment xml:lang="zh_TW">Game Boy Color ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="\xce\xed\x66\x66\xcc\x0d\x00\x0b\x03\x73\x00\x83\x00\x0c\x00\x0d\x00\x08" type="string" offset="260">
+ <match value="0x80" type="byte" offset="323" mask="0x80"/>
+ </match>
+ </magic>
+ <glob pattern="*.gbc"/>
+ <glob pattern="*.cgb"/>
+ </mime-type>
+ <mime-type type="application/x-gba-rom">
+ <comment>Game Boy Advance ROM</comment>
+ <comment xml:lang="ar">Game Boy Advance ROM</comment>
+ <comment xml:lang="be@latin">Game Boy Advance ROM</comment>
+ <comment xml:lang="bg">ROM — Game Boy Advance</comment>
+ <comment xml:lang="ca">ROM de Game Boy Advance</comment>
+ <comment xml:lang="cs">ROM pro Game Boy Advance</comment>
+ <comment xml:lang="da">Game Boy Advance-rom</comment>
+ <comment xml:lang="de">Game Boy Advance ROM</comment>
+ <comment xml:lang="el">Game Boy Advance ROM</comment>
+ <comment xml:lang="en_GB">Game Boy Advance ROM</comment>
+ <comment xml:lang="es">ROM de Game Boy Advance</comment>
+ <comment xml:lang="eu">Game Boy Advance-ko ROMa</comment>
+ <comment xml:lang="fi">Game Boy Advance -ROM</comment>
+ <comment xml:lang="fo">Game Boy Advance ROM</comment>
+ <comment xml:lang="fr">ROM Game Boy Advance</comment>
+ <comment xml:lang="ga">ROM Game Boy Advance</comment>
+ <comment xml:lang="gl">ROM de Game Boy Advance</comment>
+ <comment xml:lang="he">ROM של Game Boy Advance</comment>
+ <comment xml:lang="hr">Game Boy Advance ROM</comment>
+ <comment xml:lang="hu">Game Boy Advance ROM</comment>
+ <comment xml:lang="ia">ROM de Game Boy Advance</comment>
+ <comment xml:lang="id">Memori baca-saja Game Boy Advance</comment>
+ <comment xml:lang="it">ROM Game Boy Advance</comment>
+ <comment xml:lang="ja">ゲームボーイアドバンス ROM</comment>
+ <comment xml:lang="ka">Game Boy Advance-ის ROM</comment>
+ <comment xml:lang="kk">Game Boy Advance ROM</comment>
+ <comment xml:lang="ko">게임보이 어드밴스 롬</comment>
+ <comment xml:lang="lt">Game Boy Advance ROM</comment>
+ <comment xml:lang="lv">Game Boy Advance ROM</comment>
+ <comment xml:lang="nb">Game Boy Advance-ROM</comment>
+ <comment xml:lang="nl">Game Boy Advance-ROM</comment>
+ <comment xml:lang="nn">Game Boy Advance-ROM</comment>
+ <comment xml:lang="oc">ROM Game Boy Advance</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Game Boy Advance</comment>
+ <comment xml:lang="pt">ROM Game Boy Advance</comment>
+ <comment xml:lang="pt_BR">ROM de Game Boy Advance</comment>
+ <comment xml:lang="ro">ROM Game Boy Advance</comment>
+ <comment xml:lang="ru">Game Boy Advance ROM</comment>
+ <comment xml:lang="sk">ROM pre Game Boy Advance</comment>
+ <comment xml:lang="sl">Bralni pomnilnik Game Boy Advance</comment>
+ <comment xml:lang="sq">ROM Game Boy Advance</comment>
+ <comment xml:lang="sr">Гејм Бој Адванс РОМ</comment>
+ <comment xml:lang="sv">Game Boy Advance-rom</comment>
+ <comment xml:lang="tr">Game Boy Gelişmiş ROM</comment>
+ <comment xml:lang="uk">розширений ППП Game Boy</comment>
+ <comment xml:lang="vi">ROM Game Boy Advance</comment>
+ <comment xml:lang="zh_CN">Game Boy Advance ROM</comment>
+ <comment xml:lang="zh_TW">Game Boy Advance ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.gba"/>
+ <glob pattern="*.agb"/>
+ </mime-type>
+ <mime-type type="application/x-virtual-boy-rom">
+ <comment>Virtual Boy ROM</comment>
+ <comment xml:lang="ca">ROM de Virtual Boy</comment>
+ <comment xml:lang="cs">ROM pro Virtual Boy</comment>
+ <comment xml:lang="de">Virtual Boy ROM</comment>
+ <comment xml:lang="en_GB">Virtual Boy ROM</comment>
+ <comment xml:lang="es">ROM de Virtual Boy</comment>
+ <comment xml:lang="hr">Virtual Boy ROM</comment>
+ <comment xml:lang="hu">Virtual Boy ROM</comment>
+ <comment xml:lang="id">ROM Virtual Boy</comment>
+ <comment xml:lang="it">ROM Virtual Boy</comment>
+ <comment xml:lang="kk">Virtual Boy ROM</comment>
+ <comment xml:lang="ko">버추얼보이 롬</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Virtual Boy</comment>
+ <comment xml:lang="pt_BR">ROM de Virtual Boy</comment>
+ <comment xml:lang="ru">Virtual Boy ROM</comment>
+ <comment xml:lang="sk">ROM pre Virtual Boy</comment>
+ <comment xml:lang="sv">Virtual Boy-rom</comment>
+ <comment xml:lang="uk">ROM Virtual Boy</comment>
+ <comment xml:lang="zh_CN">Virtual Boy ROM</comment>
+ <comment xml:lang="zh_TW">Virtual Boy ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.vb"/>
+ </mime-type>
+ <mime-type type="application/x-gdbm">
+ <comment>GDBM database</comment>
+ <comment xml:lang="ar">قاعدة بيانات GDBM</comment>
+ <comment xml:lang="be@latin">Baza źviestak GDBM</comment>
+ <comment xml:lang="bg">База от данни — GDBM</comment>
+ <comment xml:lang="ca">base de dades GDBM</comment>
+ <comment xml:lang="cs">databáze GDBM</comment>
+ <comment xml:lang="da">GDBM-database</comment>
+ <comment xml:lang="de">GDBM-Datenbank</comment>
+ <comment xml:lang="el">Βάση δεδομένων GDBM</comment>
+ <comment xml:lang="en_GB">GDBM database</comment>
+ <comment xml:lang="eo">GDBM-datumbazo</comment>
+ <comment xml:lang="es">base de datos GDBM</comment>
+ <comment xml:lang="eu">GDBM datu-basea</comment>
+ <comment xml:lang="fi">GDBM-tietokanta</comment>
+ <comment xml:lang="fo">GDBM dátustovnur</comment>
+ <comment xml:lang="fr">base de données GDBM</comment>
+ <comment xml:lang="ga">bunachar sonraí GDBM</comment>
+ <comment xml:lang="gl">base de datos GDBM</comment>
+ <comment xml:lang="he">מסד נתונים GDBM</comment>
+ <comment xml:lang="hr">GDBM baza podataka</comment>
+ <comment xml:lang="hu">GDBM adatbázis</comment>
+ <comment xml:lang="ia">Base de datos GDBM</comment>
+ <comment xml:lang="id">Basis data GDBM</comment>
+ <comment xml:lang="it">Database GDBM</comment>
+ <comment xml:lang="ja">GDBM データベース</comment>
+ <comment xml:lang="ka">GDBM მონაცემთა ბაზა</comment>
+ <comment xml:lang="kk">GDBM дерекқоры</comment>
+ <comment xml:lang="ko">GDBM 데이터베이스</comment>
+ <comment xml:lang="lt">GDBM duomenų bazė</comment>
+ <comment xml:lang="lv">GDBM datubāze</comment>
+ <comment xml:lang="nb">GDBM-database</comment>
+ <comment xml:lang="nl">GDBM-gegevensbank</comment>
+ <comment xml:lang="nn">GDBM-database</comment>
+ <comment xml:lang="oc">banca de donadas GDBM</comment>
+ <comment xml:lang="pl">Baza danych GDBM</comment>
+ <comment xml:lang="pt">base de dados GDMB</comment>
+ <comment xml:lang="pt_BR">Banco de dados GDBM</comment>
+ <comment xml:lang="ro">Bază de date GDBM</comment>
+ <comment xml:lang="ru">База данных GDBM</comment>
+ <comment xml:lang="sk">Databáza GDBM</comment>
+ <comment xml:lang="sl">Podatkovna zbirka GDBM</comment>
+ <comment xml:lang="sq">Bazë me të dhëna GDBM</comment>
+ <comment xml:lang="sr">ГДБМ база података</comment>
+ <comment xml:lang="sv">GDBM-databas</comment>
+ <comment xml:lang="tr">GDBM veritabanı</comment>
+ <comment xml:lang="uk">база даних GDBM</comment>
+ <comment xml:lang="vi">Cơ sở dữ liệu GDBM</comment>
+ <comment xml:lang="zh_CN">GDBM 数据库</comment>
+ <comment xml:lang="zh_TW">GDBM 資料庫</comment>
+ <acronym>GDBM</acronym>
+ <expanded-acronym>GNU Database Manager</expanded-acronym>
+ <magic priority="50">
+ <match value="0x13579ace" type="big32" offset="0"/>
+ <match value="0x13579ace" type="little32" offset="0"/>
+ <match value="GDBM" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-genesis-rom">
+
+ <comment>Genesis ROM</comment>
+ <comment xml:lang="ar">Genesis ROM</comment>
+ <comment xml:lang="be@latin">Genesis ROM</comment>
+ <comment xml:lang="bg">ROM — Genesis</comment>
+ <comment xml:lang="ca">ROM de Genesis</comment>
+ <comment xml:lang="cs">ROM pro Genesis</comment>
+ <comment xml:lang="da">Genesis-rom</comment>
+ <comment xml:lang="de">Genesis ROM</comment>
+ <comment xml:lang="el">Genesis ROM</comment>
+ <comment xml:lang="en_GB">Genesis ROM</comment>
+ <comment xml:lang="eo">Genesis-NLM</comment>
+ <comment xml:lang="es">ROM de Genesis (Mega Drive)</comment>
+ <comment xml:lang="eu">Genesis-eko ROMa</comment>
+ <comment xml:lang="fi">Genesis-ROM</comment>
+ <comment xml:lang="fo">Genesis ROM</comment>
+ <comment xml:lang="fr">ROM Mega Drive/Genesis</comment>
+ <comment xml:lang="ga">ROM Genesis</comment>
+ <comment xml:lang="gl">ROM xenérica</comment>
+ <comment xml:lang="he">ROM מסוג Genesis</comment>
+ <comment xml:lang="hr">Genesis ROM</comment>
+ <comment xml:lang="hu">Genesis ROM</comment>
+ <comment xml:lang="ia">ROM de Mega Drive/Genesis</comment>
+ <comment xml:lang="id">Memori baca-saja Genesis</comment>
+ <comment xml:lang="it">ROM Megadrive</comment>
+ <comment xml:lang="ja">メガドライブ ROM</comment>
+ <comment xml:lang="kk">Genesis ROM</comment>
+ <comment xml:lang="ko">제네시스 롬</comment>
+ <comment xml:lang="lt">Genesis ROM</comment>
+ <comment xml:lang="lv">Genesis ROM</comment>
+ <comment xml:lang="ms">ROM Genesis</comment>
+ <comment xml:lang="nb">Genesis-ROM</comment>
+ <comment xml:lang="nl">Mega Drive</comment>
+ <comment xml:lang="nn">Genesis-ROM</comment>
+ <comment xml:lang="oc">ROM Mega Drive/Genesis</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Mega Drive</comment>
+ <comment xml:lang="pt">ROM Mega Drive</comment>
+ <comment xml:lang="pt_BR">ROM de Genesis (Mega Drive)</comment>
+ <comment xml:lang="ro">ROM Genesis</comment>
+ <comment xml:lang="ru">Genesis ROM</comment>
+ <comment xml:lang="sk">ROM pre Megadrive</comment>
+ <comment xml:lang="sl">Bralni pomnilnik Genesis</comment>
+ <comment xml:lang="sq">ROM Genesis</comment>
+ <comment xml:lang="sr">Мегадрајв РОМ</comment>
+ <comment xml:lang="sv">Mega Drive-rom</comment>
+ <comment xml:lang="tr">Genesis ROM</comment>
+ <comment xml:lang="uk">ППП Genesis</comment>
+ <comment xml:lang="vi">ROM Genesis</comment>
+ <comment xml:lang="zh_CN">Genesis ROM</comment>
+ <comment xml:lang="zh_TW">Genesis ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="SEGA GENESIS" type="string" offset="256"/>
+ <match value="SEGA MEGA DRIVE" type="string" offset="256"/>
+ <match value="EAGN" type="string" offset="640"/>
+ <match value="EAMG" type="string" offset="640"/>
+ </magic>
+ <glob pattern="*.gen"/>
+ <glob pattern="*.smd"/>
+ </mime-type>
+ <mime-type type="application/x-genesis-32x-rom">
+
+ <comment>Genesis 32X ROM</comment>
+ <comment xml:lang="ca">ROM de Genesis 32X</comment>
+ <comment xml:lang="cs">ROM pro Genesis 32X</comment>
+ <comment xml:lang="da">Genesis 32X ROM</comment>
+ <comment xml:lang="de">Genesis 32X ROM</comment>
+ <comment xml:lang="en_GB">Genesis 32X ROM</comment>
+ <comment xml:lang="es">ROM de Genesis 32X</comment>
+ <comment xml:lang="eu">Genesis 32X ROM</comment>
+ <comment xml:lang="fi">Genesis 32X -ROM</comment>
+ <comment xml:lang="fr">ROM Genesis 32X</comment>
+ <comment xml:lang="ga">ROM Genesis 32X</comment>
+ <comment xml:lang="he">ROM מסוג Genesis 32X</comment>
+ <comment xml:lang="hr">Genesis 32X ROM</comment>
+ <comment xml:lang="hu">Genesis 32X ROM</comment>
+ <comment xml:lang="id">ROM Genesis 32X</comment>
+ <comment xml:lang="it">ROM Sega Mega Drive 32X</comment>
+ <comment xml:lang="kk">Genesis 32X ROM</comment>
+ <comment xml:lang="ko">제네시스 32X 롬</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Mega Drive 32X</comment>
+ <comment xml:lang="pt_BR">ROM de Genesis 32X</comment>
+ <comment xml:lang="ru">Genesis 32X ROM</comment>
+ <comment xml:lang="sk">ROM pre Genesis 32X</comment>
+ <comment xml:lang="sr">Џенезис 32X РОМ</comment>
+ <comment xml:lang="sv">Mega Drive 32X-rom</comment>
+ <comment xml:lang="tr">Genesis 32X ROM</comment>
+ <comment xml:lang="uk">ППП Genesis 32X</comment>
+ <comment xml:lang="zh_CN">Genesis 32X ROM</comment>
+ <comment xml:lang="zh_TW">Genesis 32X ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="SEGA 32X" type="string" offset="256"/>
+ </magic>
+ <glob pattern="*.32x"/>
+ <glob pattern="*.mdx"/>
+ </mime-type>
+ <mime-type type="application/x-gettext-translation">
+ <comment>translated messages (machine-readable)</comment>
+ <comment xml:lang="ar">رسائل مترجمة (مقروءة آليا)</comment>
+ <comment xml:lang="be@latin">pierakładzienyja paviedamleńni (dla čytańnia kamputaram)</comment>
+ <comment xml:lang="bg">Преведени съобщения — машинен формат</comment>
+ <comment xml:lang="ca">missatges traduïts (llegible per màquina)</comment>
+ <comment xml:lang="cs">přeložené zprávy (strojově čitelné)</comment>
+ <comment xml:lang="da">oversatte meddelelser (maskinlæsbare)</comment>
+ <comment xml:lang="de">Übersetzte Meldungen (maschinenlesbar)</comment>
+ <comment xml:lang="el">Μεταφρασμένα μηνύματα (για μηχανική ανάγνωση)</comment>
+ <comment xml:lang="en_GB">translated messages (machine-readable)</comment>
+ <comment xml:lang="eo">tradukitaj mesaĝoj (maŝinlegebla)</comment>
+ <comment xml:lang="es">mensajes traducidos (legibles por máquinas)</comment>
+ <comment xml:lang="eu">itzulitako mezuak (ordenagailuek irakurtzeko)</comment>
+ <comment xml:lang="fi">käännetyt viestit (koneluettava)</comment>
+ <comment xml:lang="fo">týdd boð (maskin-lesifør)</comment>
+ <comment xml:lang="fr">messages traduits (lisibles par machine)</comment>
+ <comment xml:lang="ga">teachtaireachtaí aistrithe (inléite ag meaisín)</comment>
+ <comment xml:lang="gl">mensaxes traducidos (lexíbeis por máquinas)</comment>
+ <comment xml:lang="he">הודעות מתורגמות (מובן ע״י מכונה)</comment>
+ <comment xml:lang="hr">Prevedene poruke (strojno čitljive)</comment>
+ <comment xml:lang="hu">lefordított üzenetek (gépi kód)</comment>
+ <comment xml:lang="ia">messages traducite (legibile pro machinas)</comment>
+ <comment xml:lang="id">pesan diterjemahkan (dapat dibaca mesin)</comment>
+ <comment xml:lang="it">Messaggi tradotti (leggibili da macchina)</comment>
+ <comment xml:lang="ja">翻訳メッセージ (マシン用)</comment>
+ <comment xml:lang="ka">ნათარგმნი შეტყობინებები (მანქანისთვის განკუთვნილი)</comment>
+ <comment xml:lang="kk">аударылған хабарламалар (машиналық түрде)</comment>
+ <comment xml:lang="ko">번역 메시지(컴퓨터 사용 형식)</comment>
+ <comment xml:lang="lt">išversti užrašai (kompiuteriniu formatu)</comment>
+ <comment xml:lang="lv">pārtulkotie ziņojumi (mašīnlasāms)</comment>
+ <comment xml:lang="ms">Mesej diterjemah (bolehdibaca-mesin)</comment>
+ <comment xml:lang="nb">oversatte meldinger (maskinlesbar)</comment>
+ <comment xml:lang="nl">vertaalde berichten (machine-leesbaar)</comment>
+ <comment xml:lang="nn">oversette meldingar (maskinlesbare)</comment>
+ <comment xml:lang="oc">messatges tradusits (legibles per maquina)</comment>
+ <comment xml:lang="pl">Przetłumaczone komunikaty (czytelne dla komputera)</comment>
+ <comment xml:lang="pt">mensagens traduzidas (leitura pelo computador)</comment>
+ <comment xml:lang="pt_BR">Mensagens traduzidas (legível pelo computador)</comment>
+ <comment xml:lang="ro">mesaje traduse (citite de calculator)</comment>
+ <comment xml:lang="ru">Переводы сообщений (откомпилированые)</comment>
+ <comment xml:lang="sk">Preložené správy (strojovo čitateľné)</comment>
+ <comment xml:lang="sl">prevedena sporočila (strojni zapis)</comment>
+ <comment xml:lang="sq">Mesazhe të përkthyer (të lexueshëm nga makina)</comment>
+ <comment xml:lang="sr">преведене поруке (машинама читљиве)</comment>
+ <comment xml:lang="sv">översatta meddelanden (maskinläsbara)</comment>
+ <comment xml:lang="tr">çevrilmiş iletiler (makine tarafından okunabilir)</comment>
+ <comment xml:lang="uk">перекладені повідомлення (у машинній формі)</comment>
+ <comment xml:lang="vi">thông điệp đã dịch (máy đọc được)</comment>
+ <comment xml:lang="zh_CN">已翻译消息(机读)</comment>
+ <comment xml:lang="zh_TW">翻譯訊息 (程式讀取格式)</comment>
+ <magic priority="50">
+ <match value="\336\22\4\225" type="string" offset="0"/>
+ <match value="\225\4\22\336" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.gmo"/>
+ <glob pattern="*.mo"/>
+ </mime-type>
+ <mime-type type="application/x-gtk-builder">
+ <comment>GTK+ Builder</comment>
+ <comment xml:lang="ca">constructor de GTK+</comment>
+ <comment xml:lang="cs">GTK+ Builder</comment>
+ <comment xml:lang="da">GTK+ Builder</comment>
+ <comment xml:lang="de">GTK+ Builder</comment>
+ <comment xml:lang="el">Δομητής GTK+</comment>
+ <comment xml:lang="en_GB">GTK+ Builder</comment>
+ <comment xml:lang="es">GTK+ Builder</comment>
+ <comment xml:lang="eu">GTK+ Builder</comment>
+ <comment xml:lang="fi">GTK+ Builder</comment>
+ <comment xml:lang="fr">GTK+ Builder</comment>
+ <comment xml:lang="ga">Tógálaí GTK+</comment>
+ <comment xml:lang="gl">Construtor de GTK+</comment>
+ <comment xml:lang="he">בנייה של GTK+‎</comment>
+ <comment xml:lang="hr">GTK+ Builder</comment>
+ <comment xml:lang="hu">GTK+ Builder</comment>
+ <comment xml:lang="ia">GTK+ Builder</comment>
+ <comment xml:lang="id">GTK+ Builder</comment>
+ <comment xml:lang="it">GTK+ Builder</comment>
+ <comment xml:lang="ja">GTK+ Builder</comment>
+ <comment xml:lang="kk">GTK+ Builder</comment>
+ <comment xml:lang="ko">GTK+ 빌더</comment>
+ <comment xml:lang="lv">GTK+ būvētājs</comment>
+ <comment xml:lang="oc">GTK+ Builder</comment>
+ <comment xml:lang="pl">GTK+ Builder</comment>
+ <comment xml:lang="pt">Construtor GTK+</comment>
+ <comment xml:lang="pt_BR">GTK+ Builder</comment>
+ <comment xml:lang="ru">GTK+ Builder</comment>
+ <comment xml:lang="sk">GTK+ Builder</comment>
+ <comment xml:lang="sl">GTK+ Builder</comment>
+ <comment xml:lang="sr">ГТК+ Градитељ</comment>
+ <comment xml:lang="sv">GTK+ Builder</comment>
+ <comment xml:lang="tr">GTK+ İnşa Edici</comment>
+ <comment xml:lang="uk">GTK+ Builder</comment>
+ <comment xml:lang="zh_CN">GTK+ Builder</comment>
+ <comment xml:lang="zh_TW">GTK+ Builder</comment>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.ui"/>
+ <magic priority="50">
+ <match value="&lt;interface" type="string" offset="0:256"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-glade">
+ <comment>Glade project</comment>
+ <comment xml:lang="ar">مشروع Glade</comment>
+ <comment xml:lang="az">Glade layihəsi</comment>
+ <comment xml:lang="be@latin">Prajekt Glade</comment>
+ <comment xml:lang="bg">Проект — Glade</comment>
+ <comment xml:lang="ca">projecte de Glade</comment>
+ <comment xml:lang="cs">projekt Glade</comment>
+ <comment xml:lang="cy">Prosiect Glade</comment>
+ <comment xml:lang="da">Gladeprojekt</comment>
+ <comment xml:lang="de">Glade-Projekt</comment>
+ <comment xml:lang="el">Έργο Glade</comment>
+ <comment xml:lang="en_GB">Glade project</comment>
+ <comment xml:lang="eo">Glade-projekto</comment>
+ <comment xml:lang="es">proyecto de Glade</comment>
+ <comment xml:lang="eu">Glade proiektua</comment>
+ <comment xml:lang="fi">Glade-projekti</comment>
+ <comment xml:lang="fo">Glade verkætlan</comment>
+ <comment xml:lang="fr">projet Glade</comment>
+ <comment xml:lang="ga">tionscadal Glade</comment>
+ <comment xml:lang="gl">proxecto de Glade</comment>
+ <comment xml:lang="he">מיזם Glade</comment>
+ <comment xml:lang="hr">Glade projekt</comment>
+ <comment xml:lang="hu">Glade-projekt</comment>
+ <comment xml:lang="ia">Projecto Glade</comment>
+ <comment xml:lang="id">Proyek Glade</comment>
+ <comment xml:lang="it">Progetto Glade</comment>
+ <comment xml:lang="ja">Glade プロジェクト</comment>
+ <comment xml:lang="kk">Glade жобасы</comment>
+ <comment xml:lang="ko">Glade 프로젝트</comment>
+ <comment xml:lang="lt">Glade projektas</comment>
+ <comment xml:lang="lv">Glade projekts</comment>
+ <comment xml:lang="ms">Projek Glade</comment>
+ <comment xml:lang="nb">Glade prosjekt</comment>
+ <comment xml:lang="nl">Glade-project</comment>
+ <comment xml:lang="nn">Glade prosjekt</comment>
+ <comment xml:lang="oc">projècte Glade</comment>
+ <comment xml:lang="pl">Projekt Glade</comment>
+ <comment xml:lang="pt">projecto Glade</comment>
+ <comment xml:lang="pt_BR">Projeto do Glade</comment>
+ <comment xml:lang="ro">Proiect Glade</comment>
+ <comment xml:lang="ru">Проект Glade</comment>
+ <comment xml:lang="sk">Projekt Glade</comment>
+ <comment xml:lang="sl">Datoteka projekta Glade</comment>
+ <comment xml:lang="sq">Projekt Glade</comment>
+ <comment xml:lang="sr">Глејдов пројекат</comment>
+ <comment xml:lang="sv">Glade-projekt</comment>
+ <comment xml:lang="tr">Glade projesi</comment>
+ <comment xml:lang="uk">проект Glade</comment>
+ <comment xml:lang="vi">Dự án Glade</comment>
+ <comment xml:lang="zh_CN">Glade 工程</comment>
+ <comment xml:lang="zh_TW">Glade 專案</comment>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.glade"/>
+ <magic priority="50">
+ <match value="&lt;glade-interface" type="string" offset="0:256"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-gnucash">
+ <comment>GnuCash financial data</comment>
+ <comment xml:lang="ar">معلومات GnuCash المالية</comment>
+ <comment xml:lang="bg">Финансови данни — GnuCash</comment>
+ <comment xml:lang="ca">dades financeres de GnuCash</comment>
+ <comment xml:lang="cs">finanční data GnuCash</comment>
+ <comment xml:lang="da">Finansielle data til GnuCash</comment>
+ <comment xml:lang="de">GnuCash-Finanzdaten</comment>
+ <comment xml:lang="el">Οικονομικά στοιχεία GnuCash</comment>
+ <comment xml:lang="en_GB">GnuCash financial data</comment>
+ <comment xml:lang="es">datos financieros de GnuCash</comment>
+ <comment xml:lang="eu">GnuCash finantzako datuak</comment>
+ <comment xml:lang="fi">GnuCash-taloustiedot</comment>
+ <comment xml:lang="fo">GnuCash fíggjarligar dátur</comment>
+ <comment xml:lang="fr">données financières GnuCash</comment>
+ <comment xml:lang="ga">sonraí airgeadúla GnuCash</comment>
+ <comment xml:lang="gl">datos financeiros de GNUCash</comment>
+ <comment xml:lang="he">מידע כלכלי של GnuCash</comment>
+ <comment xml:lang="hr">GnuCash financijski podaci</comment>
+ <comment xml:lang="hu">GnuCash pénzügyi adatok</comment>
+ <comment xml:lang="ia">Datos financiari GnuCash</comment>
+ <comment xml:lang="id">Data keuangan GnuCash</comment>
+ <comment xml:lang="it">Dati finanziari GnuCash</comment>
+ <comment xml:lang="ja">GnuCash 会計データ</comment>
+ <comment xml:lang="kk">GnuCash қаржы ақпараты</comment>
+ <comment xml:lang="ko">GnuCash 재정 자료</comment>
+ <comment xml:lang="lt">GnuCash finansiniai duomenys</comment>
+ <comment xml:lang="lv">GnuCash finanšu dati</comment>
+ <comment xml:lang="nl">GnuCash financiële gegevens</comment>
+ <comment xml:lang="oc">donadas financières GnuCash</comment>
+ <comment xml:lang="pl">Dane finansowe GnuCash</comment>
+ <comment xml:lang="pt">dados financeiros GnuCash</comment>
+ <comment xml:lang="pt_BR">Dados financeiros do GnuCash</comment>
+ <comment xml:lang="ro">Date financiare GnuCash</comment>
+ <comment xml:lang="ru">Финансовые данные GnuCash</comment>
+ <comment xml:lang="sk">Finančné údaje GnuCash</comment>
+ <comment xml:lang="sl">Datoteka finančnih podatkov GnuCash</comment>
+ <comment xml:lang="sr">финансијски подаци Гнуовог новца</comment>
+ <comment xml:lang="sv">GnuCash-finansdata</comment>
+ <comment xml:lang="tr">GnuCash mali verisi</comment>
+ <comment xml:lang="uk">фінансові дані GnuCash</comment>
+ <comment xml:lang="vi">Dữ liệu tài chính GnuCash</comment>
+ <comment xml:lang="zh_CN">GnuCash 财务数据</comment>
+ <comment xml:lang="zh_TW">GnuCash 財務資料</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.gnucash"/>
+ <glob pattern="*.gnc"/>
+ <glob pattern="*.xac"/>
+ </mime-type>
+ <mime-type type="application/x-gnumeric">
+ <comment>Gnumeric spreadsheet</comment>
+ <comment xml:lang="ar">جدول Gnumeric</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš Gnumeric</comment>
+ <comment xml:lang="bg">Таблица — Gnumeric</comment>
+ <comment xml:lang="ca">full de càlcul de Gnumeric</comment>
+ <comment xml:lang="cs">sešit Gnumeric</comment>
+ <comment xml:lang="da">Gnumeric-regneark</comment>
+ <comment xml:lang="de">Gnumeric-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο Gnumeric</comment>
+ <comment xml:lang="en_GB">Gnumeric spreadsheet</comment>
+ <comment xml:lang="eo">Gnumeric-kalkultabelo</comment>
+ <comment xml:lang="es">hoja de cálculo de Gnumeric</comment>
+ <comment xml:lang="eu">Gnumeric kalkulu-orria</comment>
+ <comment xml:lang="fi">Gnumeric-taulukko</comment>
+ <comment xml:lang="fo">Gnumeric rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul Gnumeric</comment>
+ <comment xml:lang="ga">scarbhileog Gnumeric</comment>
+ <comment xml:lang="gl">folla de cálculo de Gnumeric</comment>
+ <comment xml:lang="he">גליון עבודה Gnumeric</comment>
+ <comment xml:lang="hr">Gnumeric proračunska tablica</comment>
+ <comment xml:lang="hu">Gnumeric-munkafüzet</comment>
+ <comment xml:lang="ia">Folio de calculo Gnumeric</comment>
+ <comment xml:lang="id">Lembar sebar Gnumeric</comment>
+ <comment xml:lang="it">Foglio di calcolo Gnumeric</comment>
+ <comment xml:lang="ja">Gnumeric スプレッドシート</comment>
+ <comment xml:lang="kk">Gnumeric электрондық кестесі</comment>
+ <comment xml:lang="ko">Gnumeric 스프레드시트</comment>
+ <comment xml:lang="lt">Gnumeric skaičialentė</comment>
+ <comment xml:lang="lv">Gnumeric izklājlapa</comment>
+ <comment xml:lang="ms">Hamparan Gnumeric</comment>
+ <comment xml:lang="nb">Gnumeric-regneark</comment>
+ <comment xml:lang="nl">Gnumeric-rekenblad</comment>
+ <comment xml:lang="nn">Gnumeric-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul Gnumeric</comment>
+ <comment xml:lang="pl">Arkusz Gnumeric</comment>
+ <comment xml:lang="pt">folha de cálculo Gnumeric</comment>
+ <comment xml:lang="pt_BR">Planilha do Gnumeric</comment>
+ <comment xml:lang="ro">Foaie de calcul Gnumeric</comment>
+ <comment xml:lang="ru">Электронная таблица Gnumeric</comment>
+ <comment xml:lang="sk">Zošit Gnumeric</comment>
+ <comment xml:lang="sl">Razpredelnica Gnumeric</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh Gnumeric</comment>
+ <comment xml:lang="sr">табела Гномовог бројевника</comment>
+ <comment xml:lang="sv">Gnumeric-kalkylblad</comment>
+ <comment xml:lang="tr">Gnumeric çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця Gnumeric</comment>
+ <comment xml:lang="vi">Bảng tính Gnumeric.</comment>
+ <comment xml:lang="zh_CN">Gnumeric 电子表格</comment>
+ <comment xml:lang="zh_TW">Gnumeric 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="50">
+ <match value="gmr:Workbook" type="string" offset="0:64"/>
+ <match value="gnm:Workbook" type="string" offset="0:64"/>
+ </magic>
+ <glob pattern="*.gnumeric"/>
+ </mime-type>
+ <mime-type type="application/x-gnuplot">
+ <comment>Gnuplot document</comment>
+ <comment xml:lang="ar">مستند Gnuplot</comment>
+ <comment xml:lang="ast">Documentu de Gnuplot</comment>
+ <comment xml:lang="be@latin">Dakument Gnuplot</comment>
+ <comment xml:lang="bg">Документ — Gnuplot</comment>
+ <comment xml:lang="ca">document gnuplot</comment>
+ <comment xml:lang="cs">dokument Gnuplot</comment>
+ <comment xml:lang="da">Gnuplotdokument</comment>
+ <comment xml:lang="de">Gnuplot-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Gnuplot</comment>
+ <comment xml:lang="en_GB">Gnuplot document</comment>
+ <comment xml:lang="eo">Gnuplot-dokumento</comment>
+ <comment xml:lang="es">documento de Gnuplot</comment>
+ <comment xml:lang="eu">Gnuplot dokumentua</comment>
+ <comment xml:lang="fi">Gnuplot-asiakirja</comment>
+ <comment xml:lang="fo">Gnuplot skjal</comment>
+ <comment xml:lang="fr">document Gnuplot</comment>
+ <comment xml:lang="ga">cáipéis Gnuplot</comment>
+ <comment xml:lang="gl">documento de Gnuplot</comment>
+ <comment xml:lang="he">מסמך Gnuplot</comment>
+ <comment xml:lang="hr">Gnuplot dokument</comment>
+ <comment xml:lang="hu">Gnuplot dokumentum</comment>
+ <comment xml:lang="ia">Documento Gnuplot</comment>
+ <comment xml:lang="id">Dokumen Gnuplot</comment>
+ <comment xml:lang="it">Documento Gnuplot</comment>
+ <comment xml:lang="ja">Gnuplot ドキュメント</comment>
+ <comment xml:lang="kk">Gnuplot құжаты</comment>
+ <comment xml:lang="ko">Gnuplot 문서</comment>
+ <comment xml:lang="lt">Gnuplot dokumentas</comment>
+ <comment xml:lang="lv">Gnuplot dokuments</comment>
+ <comment xml:lang="nb">Gnuplot-dokument</comment>
+ <comment xml:lang="nl">Gnuplot-document</comment>
+ <comment xml:lang="nn">Gnuplot-dokument</comment>
+ <comment xml:lang="oc">document Gnuplot</comment>
+ <comment xml:lang="pl">Dokument Gnuplot</comment>
+ <comment xml:lang="pt">documento Gnuplot</comment>
+ <comment xml:lang="pt_BR">Documento do Gnuplot</comment>
+ <comment xml:lang="ro">Document Gnuplot</comment>
+ <comment xml:lang="ru">Документ Gnuplot</comment>
+ <comment xml:lang="sk">Dokument Gnuplot</comment>
+ <comment xml:lang="sl">Dokument Gnuplot</comment>
+ <comment xml:lang="sq">Dokument Gnuplot</comment>
+ <comment xml:lang="sr">документ Гнуплота</comment>
+ <comment xml:lang="sv">Gnuplot-dokument</comment>
+ <comment xml:lang="tr">Gnuplot belgesi</comment>
+ <comment xml:lang="uk">документ Gnuplot</comment>
+ <comment xml:lang="vi">Tài liệu Gnuplot</comment>
+ <comment xml:lang="zh_CN">Gnuplot 文档</comment>
+ <comment xml:lang="zh_TW">Gnuplot 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.gp"/>
+ <glob pattern="*.gplt"/>
+ <glob pattern="*.gnuplot"/>
+ </mime-type>
+ <mime-type type="application/x-graphite">
+ <comment>Graphite scientific graph</comment>
+ <comment xml:lang="ar">مبيان الجرافيت العلمي</comment>
+ <comment xml:lang="be@latin">Navukovy hrafik Graphite</comment>
+ <comment xml:lang="bg">Графика — Graphite</comment>
+ <comment xml:lang="ca">gràfic científic Graphite</comment>
+ <comment xml:lang="cs">vědecký graf Graphite</comment>
+ <comment xml:lang="da">Graphite videnskabelig graf</comment>
+ <comment xml:lang="de">Wissenschaftlicher Graphite-Graph</comment>
+ <comment xml:lang="el">Επιστημονικό γράφημα Graphite</comment>
+ <comment xml:lang="en_GB">Graphite scientific graph</comment>
+ <comment xml:lang="eo">scienca grafikaĵo de Graphite</comment>
+ <comment xml:lang="es">gráfico científico de Graphite</comment>
+ <comment xml:lang="eu">Graphite - grafiko zientifikoak</comment>
+ <comment xml:lang="fi">Graphite- tieteellinen graafi</comment>
+ <comment xml:lang="fo">Grapite vísindarlig ritmynd</comment>
+ <comment xml:lang="fr">graphe Graphite scientific</comment>
+ <comment xml:lang="ga">graf eolaíochta Graphite</comment>
+ <comment xml:lang="gl">gráfica científica de Graphite</comment>
+ <comment xml:lang="he">תרשים מדעי של Graphite</comment>
+ <comment xml:lang="hr">Graphite znanstveni grafikon</comment>
+ <comment xml:lang="hu">Graphite tudományos grafikon</comment>
+ <comment xml:lang="ia">Graphico scientific Graphite</comment>
+ <comment xml:lang="id">Grafik sains Graphite</comment>
+ <comment xml:lang="it">Grafico scientifico Graphite</comment>
+ <comment xml:lang="ja">Graphite scientific グラフ</comment>
+ <comment xml:lang="kk">Graphite ғылыми кескіні</comment>
+ <comment xml:lang="ko">Graphite 과학 그래프</comment>
+ <comment xml:lang="lt">Graphite mokslinė diagrama</comment>
+ <comment xml:lang="lv">Graphite zinātniskais grafiks</comment>
+ <comment xml:lang="ms">Graf saintifik Graphite</comment>
+ <comment xml:lang="nb">Vitenskapelig graf fra Graphite</comment>
+ <comment xml:lang="nl">Graphite wetenschappelijke grafiek</comment>
+ <comment xml:lang="nn">Graphite vitskaplege graf</comment>
+ <comment xml:lang="oc">graphe Graphite scientific</comment>
+ <comment xml:lang="pl">Wykres naukowy Graphite</comment>
+ <comment xml:lang="pt">gráfico científico Graphite</comment>
+ <comment xml:lang="pt_BR">Gráfico científico do Graphite</comment>
+ <comment xml:lang="ro">Grafic științific Graphite</comment>
+ <comment xml:lang="ru">Научная диаграмма Graphite</comment>
+ <comment xml:lang="sk">Vedecký graf Graphite</comment>
+ <comment xml:lang="sl">Datoteka znanstvenega grafa Graphite</comment>
+ <comment xml:lang="sq">Grafik shkencor Graphite </comment>
+ <comment xml:lang="sr">Графитов научни графикони</comment>
+ <comment xml:lang="sv">Vetenskaplig Graphite-grafer</comment>
+ <comment xml:lang="tr">Graphite bilimsel grafiği</comment>
+ <comment xml:lang="uk">наукова графіка Graphite</comment>
+ <comment xml:lang="vi">Biểu đồ khoa học Graphite</comment>
+ <comment xml:lang="zh_CN">Graphite 科学图形</comment>
+ <comment xml:lang="zh_TW">Graphite 科學圖表</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.gra"/>
+ </mime-type>
+ <mime-type type="application/x-gtktalog">
+ <comment>GTKtalog catalog</comment>
+ <comment xml:lang="ar">كتالوج GTKtalog</comment>
+ <comment xml:lang="be@latin">Kataloh GTKtalog</comment>
+ <comment xml:lang="bg">Каталог — Gtktalog</comment>
+ <comment xml:lang="ca">catàleg de GTKtalog</comment>
+ <comment xml:lang="cs">katalog GTKtalog</comment>
+ <comment xml:lang="da">GTKtalog-katalog</comment>
+ <comment xml:lang="de">GTKtalog-Katalog</comment>
+ <comment xml:lang="el">Κατάλογος GTKtalog</comment>
+ <comment xml:lang="en_GB">GTKtalog catalogue</comment>
+ <comment xml:lang="eo">katalogo de GTKtalog</comment>
+ <comment xml:lang="es">catálogo de GTKtalog</comment>
+ <comment xml:lang="eu">Gtktalog katalogoa</comment>
+ <comment xml:lang="fi">GTKtalog-luettelo</comment>
+ <comment xml:lang="fo">GTKtalog skrá</comment>
+ <comment xml:lang="fr">catalogue Gtktalog</comment>
+ <comment xml:lang="ga">catalóg GTKtalog</comment>
+ <comment xml:lang="gl">catálogo de GTKtalog</comment>
+ <comment xml:lang="he">קטלוג GTKtalog</comment>
+ <comment xml:lang="hr">GTKtalog katalog</comment>
+ <comment xml:lang="hu">GTKtalog-katalógus</comment>
+ <comment xml:lang="ia">Catalogo GTKtalog</comment>
+ <comment xml:lang="id">Katalog GTKtalog</comment>
+ <comment xml:lang="it">Catalogo GTKtalog</comment>
+ <comment xml:lang="ja">GTKtalog カタログ</comment>
+ <comment xml:lang="ka">GTKtalog-ის კატალოგი</comment>
+ <comment xml:lang="kk">GTKtalog каталогы</comment>
+ <comment xml:lang="ko">GTKtalog 카탈로그</comment>
+ <comment xml:lang="lt">GTKtalog katalogas</comment>
+ <comment xml:lang="lv">GTKtalog katalogs</comment>
+ <comment xml:lang="ms">Katalog GTKtalog</comment>
+ <comment xml:lang="nb">GTKtalog-katalog</comment>
+ <comment xml:lang="nl">GTKtalog-catalogus</comment>
+ <comment xml:lang="nn">GTKtalog-katalog</comment>
+ <comment xml:lang="oc">catalòg Gtktalog</comment>
+ <comment xml:lang="pl">Katalog programu GTKtalog</comment>
+ <comment xml:lang="pt">catálogo GTKtalog</comment>
+ <comment xml:lang="pt_BR">Catálogo GTKtalog</comment>
+ <comment xml:lang="ro">Catalog GTKalog</comment>
+ <comment xml:lang="ru">Каталог GTKtalog</comment>
+ <comment xml:lang="sk">Katalóg GTKtalog</comment>
+ <comment xml:lang="sl">Datoteka kataloga GTKtalog</comment>
+ <comment xml:lang="sq">Katallog GTKtalog</comment>
+ <comment xml:lang="sr">каталог ГТКталога</comment>
+ <comment xml:lang="sv">GTKtalog-katalog</comment>
+ <comment xml:lang="tr">Gtktalog kataloğu</comment>
+ <comment xml:lang="uk">каталог GTKtalog</comment>
+ <comment xml:lang="vi">Phân loại GTKtalog</comment>
+ <comment xml:lang="zh_CN">GTKtalog 目录</comment>
+ <comment xml:lang="zh_TW">GTKtalog 光碟目錄</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="gtktalog " type="string" offset="4"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-gzdvi">
+ <comment>TeX DVI document (gzip-compressed)</comment>
+ <comment xml:lang="ar">مستند TeX DVI (مضغوط-gzip)</comment>
+ <comment xml:lang="ast">Documentu Tex DVI (comprimíu en gzip)</comment>
+ <comment xml:lang="be@latin">Dakument TeX DVI (gzip-skampresavany)</comment>
+ <comment xml:lang="bg">Документ — TeX DVI, компресиран с gzip</comment>
+ <comment xml:lang="ca">document DVI de TeX (amb compressió gzip)</comment>
+ <comment xml:lang="cs">dokument TeX DVI (komprimovaný pomocí gzip)</comment>
+ <comment xml:lang="da">TeX DVI-dokument (gzip-komprimeret)</comment>
+ <comment xml:lang="de">TeX-DVI-Dokument (gzip-komprimiert)</comment>
+ <comment xml:lang="el">Έγγραφο TeX DVI (συμπιεσμένο με gzip)</comment>
+ <comment xml:lang="en_GB">TeX DVI document (gzip-compressed)</comment>
+ <comment xml:lang="es">documento DVI de TeX (comprimido con gzip)</comment>
+ <comment xml:lang="eu">TeX DVI dokumentua (gzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">TeX DVI -asiakirja (gzip-pakattu)</comment>
+ <comment xml:lang="fo">TeX DVI skjal (gzip-stappað)</comment>
+ <comment xml:lang="fr">document DVI TeX (compressé gzip)</comment>
+ <comment xml:lang="ga">cáipéis DVI TeX (comhbhrúite le gzip)</comment>
+ <comment xml:lang="gl">documento DVI de TeX (comprimido con gzip)</comment>
+ <comment xml:lang="he">מסמך מסוג TeX DVI (מכווץ ע״י gzip)</comment>
+ <comment xml:lang="hr">TeX DVI dokument (gzip sažet)</comment>
+ <comment xml:lang="hu">TeX DVI dokumentum (gzip-pel tömörítve)</comment>
+ <comment xml:lang="ia">Documento TeX DVI (comprimite con gzip)</comment>
+ <comment xml:lang="id">Dokumen TeX DVI (terkompresi gzip)</comment>
+ <comment xml:lang="it">Documento Tex DVI (compresso con gzip)</comment>
+ <comment xml:lang="ja">Tex DVI ドキュメント (gzip 圧縮)</comment>
+ <comment xml:lang="kk">TeX DVI құжаты (gzip-пен сығылған)</comment>
+ <comment xml:lang="ko">TeX DVI 문서(GZIP 압축)</comment>
+ <comment xml:lang="lt">TeX DVI dokumentas (suglaudintas su gzip)</comment>
+ <comment xml:lang="lv">TeX DVI dokuments (saspiests ar gzip)</comment>
+ <comment xml:lang="nb">TeX DVI-dokument (gzip-komprimert)</comment>
+ <comment xml:lang="nl">TeX DVI-document (ingepakt met gzip)</comment>
+ <comment xml:lang="nn">TeX DVI-dokument (pakka med gzip)</comment>
+ <comment xml:lang="oc">document DVI TeX (compressat gzip)</comment>
+ <comment xml:lang="pl">Dokument TeX DVI (kompresja gzip)</comment>
+ <comment xml:lang="pt">documento TeX DVI (compressão gzip)</comment>
+ <comment xml:lang="pt_BR">Documento DVI TeX (compactado com gzip)</comment>
+ <comment xml:lang="ro">Document TeX DVI (comprimat gzip)</comment>
+ <comment xml:lang="ru">Документ TeX DVI (сжатый gzip)</comment>
+ <comment xml:lang="sk">Dokument TeX DVI (komprimovaný pomocou gzip)</comment>
+ <comment xml:lang="sl">Dokument TeX DVI (stisnjen z gzip)</comment>
+ <comment xml:lang="sq">Dokument TeX DVI (i kompresuar me gzip)</comment>
+ <comment xml:lang="sr">ТеКс ДВИ документ (запакован гзип-ом)</comment>
+ <comment xml:lang="sv">TeX DVI-dokument (gzip-komprimerat)</comment>
+ <comment xml:lang="tr">TeX DVI belgesi (gzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">документ TeX DVI (стиснений gzip)</comment>
+ <comment xml:lang="vi">Tài liệu DVI TeX (đã nén gzip)</comment>
+ <comment xml:lang="zh_CN">TeX DVI 文档(gzip 压缩)</comment>
+ <comment xml:lang="zh_TW">TeX DVI 文件 (gzip 格式壓縮)</comment>
+ <sub-class-of type="application/gzip"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.dvi.gz"/>
+ </mime-type>
+ <mime-type type="application/gzip">
+ <comment>Gzip archive</comment>
+ <comment xml:lang="ar">أرشيف Gzip</comment>
+ <comment xml:lang="be@latin">Archiŭ gzip</comment>
+ <comment xml:lang="bg">Архив — gzip</comment>
+ <comment xml:lang="ca">arxiu gzip</comment>
+ <comment xml:lang="cs">archiv gzip</comment>
+ <comment xml:lang="da">Gzip-arkiv</comment>
+ <comment xml:lang="de">Gzip-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Gzip</comment>
+ <comment xml:lang="en_GB">Gzip archive</comment>
+ <comment xml:lang="eo">Gzip-arkivo</comment>
+ <comment xml:lang="es">archivador Gzip</comment>
+ <comment xml:lang="eu">Gzip artxiboa</comment>
+ <comment xml:lang="fi">Gzip-arkisto</comment>
+ <comment xml:lang="fo">Gzip skjalasavn</comment>
+ <comment xml:lang="fr">archive gzip</comment>
+ <comment xml:lang="ga">cartlann Gzip</comment>
+ <comment xml:lang="gl">arquivo Gzip</comment>
+ <comment xml:lang="he">ארכיון Gzip</comment>
+ <comment xml:lang="hr">Gzip arhiva</comment>
+ <comment xml:lang="hu">Gzip archívum</comment>
+ <comment xml:lang="ia">Archivo Gzip</comment>
+ <comment xml:lang="id">Arsip Gzip</comment>
+ <comment xml:lang="it">Archivio gzip</comment>
+ <comment xml:lang="ja">Gzip アーカイブ</comment>
+ <comment xml:lang="kk">Gzip архиві</comment>
+ <comment xml:lang="ko">GZIP 압축 파일</comment>
+ <comment xml:lang="lt">Gzip archyvas</comment>
+ <comment xml:lang="lv">Gzip arhīvs</comment>
+ <comment xml:lang="nb">Gzip-arkiv</comment>
+ <comment xml:lang="nl">Gzip-archief</comment>
+ <comment xml:lang="nn">Gzip-arkiv</comment>
+ <comment xml:lang="oc">archiu gzip</comment>
+ <comment xml:lang="pl">Archiwum gzip</comment>
+ <comment xml:lang="pt">arquivo Gzip</comment>
+ <comment xml:lang="pt_BR">Pacote Gzip</comment>
+ <comment xml:lang="ro">Arhivă Gzip</comment>
+ <comment xml:lang="ru">Архив GZIP</comment>
+ <comment xml:lang="sk">Archív gzip</comment>
+ <comment xml:lang="sl">Datoteka arhiva Gzip</comment>
+ <comment xml:lang="sq">Arkiv gzip</comment>
+ <comment xml:lang="sr">Гзип архива</comment>
+ <comment xml:lang="sv">Gzip-arkiv</comment>
+ <comment xml:lang="tr">Gzip arşivi</comment>
+ <comment xml:lang="uk">архів gzip</comment>
+ <comment xml:lang="vi">Kho nén gzip</comment>
+ <comment xml:lang="zh_CN">Gzip 归档文件</comment>
+ <comment xml:lang="zh_TW">Gzip 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="\037\213" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.gz"/>
+ <alias type="application/x-gzip"/>
+ </mime-type>
+ <mime-type type="application/x-gzpdf">
+ <comment>PDF document (gzip-compressed)</comment>
+ <comment xml:lang="ar">مستند PDF (مضغوط-gzip)</comment>
+ <comment xml:lang="ast">Documentu PDF (comprimíu en gzip)</comment>
+ <comment xml:lang="be@latin">Dakument PDF (gzip-skampresavany)</comment>
+ <comment xml:lang="bg">Документ — PDF, компресиран с gzip</comment>
+ <comment xml:lang="ca">document PDF (amb compressió gzip)</comment>
+ <comment xml:lang="cs">dokument PDF (komprimovaný pomocí gzip)</comment>
+ <comment xml:lang="da">PDF-dokument (gzip-komprimeret)</comment>
+ <comment xml:lang="de">PDF-Dokument (gzip-komprimiert)</comment>
+ <comment xml:lang="el">Έγγραφο PDF (συμπιεσμένο με gzip)</comment>
+ <comment xml:lang="en_GB">PDF document (gzip-compressed)</comment>
+ <comment xml:lang="es">documento PDF (comprimido con gzip)</comment>
+ <comment xml:lang="eu">PDF dokumentua (gzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">PDF-asiakirja (gzip-pakattu)</comment>
+ <comment xml:lang="fo">PDF skjal (gzip-stappað)</comment>
+ <comment xml:lang="fr">document PDF (compressé gzip)</comment>
+ <comment xml:lang="ga">cáipéis PDF (comhbhrúite le gzip)</comment>
+ <comment xml:lang="gl">documento PDF (comprimido en gzip)</comment>
+ <comment xml:lang="he">מסמך PDF (מכווץ ע״י gzip)</comment>
+ <comment xml:lang="hr">PDF dokument (gzip sažet)</comment>
+ <comment xml:lang="hu">PDF dokumentum (gzip-tömörítésű)</comment>
+ <comment xml:lang="ia">Documento PDF (comprimite con gzip)</comment>
+ <comment xml:lang="id">Dokumen PDF (terkompresi gzip)</comment>
+ <comment xml:lang="it">Documento PDF (compresso con gzip)</comment>
+ <comment xml:lang="ja">PDF ドキュメント (gzip 圧縮)</comment>
+ <comment xml:lang="kk">PDF құжаты (gzip-пен сығылған)</comment>
+ <comment xml:lang="ko">PDF 문서(GZIP 압축)</comment>
+ <comment xml:lang="lt">PDF dokumentas (suglaudintas su gzip)</comment>
+ <comment xml:lang="lv">PDF dokuments (saspiests ar gzip)</comment>
+ <comment xml:lang="nb">PDF-dokument (gzip-komprimert)</comment>
+ <comment xml:lang="nl">PDF-document (ingepakt met gzip)</comment>
+ <comment xml:lang="nn">PDF-dokument (pakka med gzip)</comment>
+ <comment xml:lang="oc">document PDF (compressat gzip)</comment>
+ <comment xml:lang="pl">Dokument PDF (kompresja gzip)</comment>
+ <comment xml:lang="pt">documento PDF (compressão gzip)</comment>
+ <comment xml:lang="pt_BR">Documento PDF (compactado com gzip)</comment>
+ <comment xml:lang="ro">Document PDF (comprimat gzip)</comment>
+ <comment xml:lang="ru">Документ PDF (сжатый gzip)</comment>
+ <comment xml:lang="sk">Dokument PDF (komprimovaný pomocou gzip)</comment>
+ <comment xml:lang="sl">Dokument PDF (stisnjen z gzip)</comment>
+ <comment xml:lang="sq">Dokument PDF (i kompresuar me gzip)</comment>
+ <comment xml:lang="sr">ПДФ документ (запакован гзип-ом)</comment>
+ <comment xml:lang="sv">PDF-dokument (gzip-komprimerat)</comment>
+ <comment xml:lang="tr">PDF belgesi (gzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">документ PDF (стиснений gzip)</comment>
+ <comment xml:lang="vi">Tài liệu PDF (đã nén gzip)</comment>
+ <comment xml:lang="zh_CN">PDF 文档(gzip 压缩)</comment>
+ <comment xml:lang="zh_TW">PDF 文件 (gzip 格式壓縮)</comment>
+ <sub-class-of type="application/gzip"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.pdf.gz"/>
+ </mime-type>
+ <mime-type type="application/x-gzpostscript">
+ <comment>PostScript document (gzip-compressed)</comment>
+ <comment xml:lang="ar">مستند PostScript (مضغوط-gzip)</comment>
+ <comment xml:lang="ast">Documentu PostScript (comprimíu en gzip)</comment>
+ <comment xml:lang="be@latin">Dakument PostScript (gzip-skampresavany)</comment>
+ <comment xml:lang="bg">Документ — PostScript, компресиран с gzip</comment>
+ <comment xml:lang="ca">document PostScript (amb compressió gzip)</comment>
+ <comment xml:lang="cs">dokument PostScript (komprimovaný pomocí gzip)</comment>
+ <comment xml:lang="da">PostScript-dokument (gzip-komprimeret)</comment>
+ <comment xml:lang="de">PostScript-Dokument (gzip-komprimiert)</comment>
+ <comment xml:lang="el">Έγγραφο PostScript (συμπιεσμένο με gzip)</comment>
+ <comment xml:lang="en_GB">PostScript document (gzip-compressed)</comment>
+ <comment xml:lang="eo">PostScript-dokumento (kunpremita per gzip)</comment>
+ <comment xml:lang="es">documento PostScript (comprimido con gzip)</comment>
+ <comment xml:lang="eu">PostScript dokumentua (gzip-konprimitua)</comment>
+ <comment xml:lang="fi">PostScript-asiakirja (gzip-pakattu)</comment>
+ <comment xml:lang="fo">PostScript skjal (gzip-stappað)</comment>
+ <comment xml:lang="fr">document PostScript (compressé gzip)</comment>
+ <comment xml:lang="ga">cáipéis PostScript (comhbhrúite le gzip)</comment>
+ <comment xml:lang="gl">documento PostScript (comprimido con gzip)</comment>
+ <comment xml:lang="he">מסמך PostScript (מכוות ע״י gzip)</comment>
+ <comment xml:lang="hr">PostScript dokument (gzip sažet)</comment>
+ <comment xml:lang="hu">PostScript-dokumentum (gzip-pel tömörítve)</comment>
+ <comment xml:lang="ia">Documento PostScript (comprimite con gzip)</comment>
+ <comment xml:lang="id">Dokumen PostScript (terkompresi gzip)</comment>
+ <comment xml:lang="it">Documento PostScript (compresso con gzip)</comment>
+ <comment xml:lang="ja">PostScript ドキュメント (gzip 圧縮)</comment>
+ <comment xml:lang="kk">PostScript құжаты (gzip-пен сығылған)</comment>
+ <comment xml:lang="ko">PostScript 문서(GZIP 압축)</comment>
+ <comment xml:lang="lt">PostScript dokumentas (suglaudintas su gzip)</comment>
+ <comment xml:lang="lv">PostScript dokuments (saspiests ar gzip)</comment>
+ <comment xml:lang="ms">Dokumen PostScript (dimampatkan-gzip)</comment>
+ <comment xml:lang="nb">PostScript-dokument (gzip-komprimert)</comment>
+ <comment xml:lang="nl">PostScript-document (ingepakt met gzip)</comment>
+ <comment xml:lang="nn">PostScript-dokument (pakka med gzip)</comment>
+ <comment xml:lang="oc">document PostEscript (compressat gzip)</comment>
+ <comment xml:lang="pl">Dokument Postscript (kompresja gzip)</comment>
+ <comment xml:lang="pt">documento PostScript (compressão gzip)</comment>
+ <comment xml:lang="pt_BR">Documento PostScript (compactado com gzip)</comment>
+ <comment xml:lang="ro">Document PostScript (comprimat gzip)</comment>
+ <comment xml:lang="ru">Документ PostScript (сжатый gzip)</comment>
+ <comment xml:lang="sk">Dokument PostScript (komprimovaný pomocou gzip)</comment>
+ <comment xml:lang="sl">Dokument PostScript (stisnjen z gzip)</comment>
+ <comment xml:lang="sq">Dokument PostScript (i kompresuar me gzip)</comment>
+ <comment xml:lang="sr">Постскрипт документ (запакован гзип-ом)</comment>
+ <comment xml:lang="sv">Postscript-dokument (gzip-komprimerat)</comment>
+ <comment xml:lang="tr">PostScript belgesi (gzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">документ PostScript (стиснене gzip)</comment>
+ <comment xml:lang="vi">Tài liệu PostScript (đã nén gzip)</comment>
+ <comment xml:lang="zh_CN">PostScript 文档(gzip 压缩)</comment>
+ <comment xml:lang="zh_TW">PostScript 文件 (gzip 格式壓縮)</comment>
+ <sub-class-of type="application/gzip"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.ps.gz"/>
+ </mime-type>
+ <mime-type type="application/x-hdf">
+ <comment>HDF document</comment>
+ <comment xml:lang="ar">مستند HDF</comment>
+ <comment xml:lang="ast">Documentu HDF</comment>
+ <comment xml:lang="az">HDF sənədi</comment>
+ <comment xml:lang="be@latin">Dakument HDF</comment>
+ <comment xml:lang="bg">Документ — HDF</comment>
+ <comment xml:lang="ca">document HDF</comment>
+ <comment xml:lang="cs">dokument HDF</comment>
+ <comment xml:lang="cy">Dogfen HDF</comment>
+ <comment xml:lang="da">HDF-dokument</comment>
+ <comment xml:lang="de">HDF-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο HDF</comment>
+ <comment xml:lang="en_GB">HDF document</comment>
+ <comment xml:lang="eo">HDF-dokumento</comment>
+ <comment xml:lang="es">documento HDF</comment>
+ <comment xml:lang="eu">HDF dokumentua</comment>
+ <comment xml:lang="fi">HDF-asiakirja</comment>
+ <comment xml:lang="fo">HDF skjal</comment>
+ <comment xml:lang="fr">document HDF</comment>
+ <comment xml:lang="ga">cáipéis HDF</comment>
+ <comment xml:lang="gl">documento HDF</comment>
+ <comment xml:lang="he">מסמך HDF</comment>
+ <comment xml:lang="hr">HDF dokument</comment>
+ <comment xml:lang="hu">HDF-dokumentum</comment>
+ <comment xml:lang="ia">Documento HDF</comment>
+ <comment xml:lang="id">Dokumen HDF</comment>
+ <comment xml:lang="it">Documento HDF</comment>
+ <comment xml:lang="ja">HDF ドキュメント</comment>
+ <comment xml:lang="kk">HDF құжаты</comment>
+ <comment xml:lang="ko">HDF 문서</comment>
+ <comment xml:lang="lt">HDF dokumentas</comment>
+ <comment xml:lang="lv">HDF dokuments</comment>
+ <comment xml:lang="ms">Dokumen HDF</comment>
+ <comment xml:lang="nb">HDF-dokument</comment>
+ <comment xml:lang="nl">HDF-document</comment>
+ <comment xml:lang="nn">HDF-dokument</comment>
+ <comment xml:lang="oc">document HDF</comment>
+ <comment xml:lang="pl">Dokument HDF</comment>
+ <comment xml:lang="pt">documento HDF</comment>
+ <comment xml:lang="pt_BR">Documento HDF</comment>
+ <comment xml:lang="ro">Document HDF</comment>
+ <comment xml:lang="ru">Документ HDF</comment>
+ <comment xml:lang="sk">Dokument HDF</comment>
+ <comment xml:lang="sl">Dokument HDF</comment>
+ <comment xml:lang="sq">Dokument HDF</comment>
+ <comment xml:lang="sr">ХДФ документ</comment>
+ <comment xml:lang="sv">HDF-dokument</comment>
+ <comment xml:lang="tr">HDF belgesi</comment>
+ <comment xml:lang="uk">документ HDF</comment>
+ <comment xml:lang="vi">Tài liệu HDF</comment>
+ <comment xml:lang="zh_CN">HDF 文档</comment>
+ <comment xml:lang="zh_TW">HDF 文件</comment>
+ <acronym>HDF</acronym>
+ <expanded-acronym>Hierarchical Data Format</expanded-acronym>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="\211HDF\r\n\032\n" type="string" offset="0"/>
+ <match value="\016\003\023\001" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.hdf"/>
+ <glob pattern="*.hdf4"/>
+ <glob pattern="*.h4"/>
+ <glob pattern="*.hdf5"/>
+ <glob pattern="*.h5"/>
+ </mime-type>
+ <mime-type type="application/x-iff">
+ <comment>IFF file</comment>
+ <comment xml:lang="ca">fitxer IFF</comment>
+ <comment xml:lang="cs">soubor IFF</comment>
+ <comment xml:lang="da">IFF-fil</comment>
+ <comment xml:lang="de">IFF-Datei</comment>
+ <comment xml:lang="el">Αρχείο IFF</comment>
+ <comment xml:lang="en_GB">IFF file</comment>
+ <comment xml:lang="es">archivo IFF</comment>
+ <comment xml:lang="eu">IFF fitxtegia</comment>
+ <comment xml:lang="fi">IFF-tiedosto</comment>
+ <comment xml:lang="fr">fichier IFF</comment>
+ <comment xml:lang="ga">comhad IFF</comment>
+ <comment xml:lang="gl">Ficheiro IFF</comment>
+ <comment xml:lang="he">קובץ IFF</comment>
+ <comment xml:lang="hr">IFF datoteka</comment>
+ <comment xml:lang="hu">IFF fájl</comment>
+ <comment xml:lang="ia">File IFF</comment>
+ <comment xml:lang="id">Berkas IFF</comment>
+ <comment xml:lang="it">File IFF</comment>
+ <comment xml:lang="ja">IFF ファイル</comment>
+ <comment xml:lang="kk">IFF файлы</comment>
+ <comment xml:lang="ko">IFF 파일</comment>
+ <comment xml:lang="lv">IFF datne</comment>
+ <comment xml:lang="oc">fichièr IFF</comment>
+ <comment xml:lang="pl">Plik IFF</comment>
+ <comment xml:lang="pt">ficheiro IFF</comment>
+ <comment xml:lang="pt_BR">Arquivo IFF</comment>
+ <comment xml:lang="ru">Файл IFF</comment>
+ <comment xml:lang="sk">Súbor IFF</comment>
+ <comment xml:lang="sl">Datoteka IFF</comment>
+ <comment xml:lang="sr">ИФФ датотека</comment>
+ <comment xml:lang="sv">IFF-fil</comment>
+ <comment xml:lang="tr">IFF dosyası</comment>
+ <comment xml:lang="uk">файл IFF</comment>
+ <comment xml:lang="zh_CN">IFF 文件</comment>
+ <comment xml:lang="zh_TW">IFF 檔案</comment>
+ <acronym>IFF</acronym>
+ <expanded-acronym>Interchange File Format</expanded-acronym>
+ <magic priority="40">
+ <match value="FORM" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-ipod-firmware">
+ <comment>iPod firmware</comment>
+ <comment xml:lang="ar">برنامج عتاد الـiPod</comment>
+ <comment xml:lang="be@latin">Firmware iPod</comment>
+ <comment xml:lang="bg">Фърмуер за iPod</comment>
+ <comment xml:lang="ca">microprogramari d'iPod</comment>
+ <comment xml:lang="cs">firmware iPod</comment>
+ <comment xml:lang="da">iPod-styreprogram</comment>
+ <comment xml:lang="de">iPod-Firmware</comment>
+ <comment xml:lang="el">Υλικολογισμικό iPod</comment>
+ <comment xml:lang="en_GB">iPod firmware</comment>
+ <comment xml:lang="eo">iPod-mikroprogramaro</comment>
+ <comment xml:lang="es">firmware de iPod</comment>
+ <comment xml:lang="eu">iPod firmwarea</comment>
+ <comment xml:lang="fi">iPod-laiteohjelmisto</comment>
+ <comment xml:lang="fo">iPod fastbúnaður</comment>
+ <comment xml:lang="fr">firmware iPod</comment>
+ <comment xml:lang="ga">dochtearraí iPod</comment>
+ <comment xml:lang="gl">firmware de iPod</comment>
+ <comment xml:lang="he">קושחת ipod</comment>
+ <comment xml:lang="hr">iPod firmver</comment>
+ <comment xml:lang="hu">iPod-firmware</comment>
+ <comment xml:lang="ia">Firmware iPod</comment>
+ <comment xml:lang="id">peranti tegar iPod</comment>
+ <comment xml:lang="it">Firmware iPod</comment>
+ <comment xml:lang="ja">iPod ファームウェア</comment>
+ <comment xml:lang="kk">iPod микробағдарламасы</comment>
+ <comment xml:lang="ko">iPod 펌웨어</comment>
+ <comment xml:lang="lt">iPod programinė įranga</comment>
+ <comment xml:lang="lv">iPod aparātprogrammatūra</comment>
+ <comment xml:lang="ms">Firmware iPod</comment>
+ <comment xml:lang="nb">iPod-firmware</comment>
+ <comment xml:lang="nl">iPod-firmware</comment>
+ <comment xml:lang="nn">iPod-firmvare</comment>
+ <comment xml:lang="oc">firmware iPod</comment>
+ <comment xml:lang="pl">Oprogramowanie wewnętrzne iPod</comment>
+ <comment xml:lang="pt">firmware iPod</comment>
+ <comment xml:lang="pt_BR">Firmware do iPod</comment>
+ <comment xml:lang="ro">Firmware iPod</comment>
+ <comment xml:lang="ru">Микропрограмма iPod</comment>
+ <comment xml:lang="sk">Firmware iPod</comment>
+ <comment xml:lang="sl">Programska strojna oprema iPod</comment>
+ <comment xml:lang="sq">Firmware iPod</comment>
+ <comment xml:lang="sr">ајПод-ов уграђени</comment>
+ <comment xml:lang="sv">fast iPod-program</comment>
+ <comment xml:lang="tr">iPod üretici yazılımı</comment>
+ <comment xml:lang="uk">мікропрограма iPod</comment>
+ <comment xml:lang="vi">phần vững iPod</comment>
+ <comment xml:lang="zh_CN">iPod 固件</comment>
+ <comment xml:lang="zh_TW">iPod 韌體</comment>
+ <magic priority="50">
+ <match value="S T O P" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-java-archive">
+ <comment>Java archive</comment>
+ <comment xml:lang="ar">أرشيف Java</comment>
+ <comment xml:lang="be@latin">Archiŭ Java</comment>
+ <comment xml:lang="bg">Архив — Java</comment>
+ <comment xml:lang="ca">arxiu de Java</comment>
+ <comment xml:lang="cs">archiv Java</comment>
+ <comment xml:lang="da">Javaarkiv</comment>
+ <comment xml:lang="de">Java-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Java</comment>
+ <comment xml:lang="en_GB">Java archive</comment>
+ <comment xml:lang="eo">Java-arkivo</comment>
+ <comment xml:lang="es">archivador Java</comment>
+ <comment xml:lang="eu">Java artxiboa</comment>
+ <comment xml:lang="fi">Java-arkisto</comment>
+ <comment xml:lang="fo">Java skjalasavn</comment>
+ <comment xml:lang="fr">archive Java</comment>
+ <comment xml:lang="ga">cartlann Java</comment>
+ <comment xml:lang="gl">arquivo Java</comment>
+ <comment xml:lang="he">ארכיון Java</comment>
+ <comment xml:lang="hr">Java arhiva</comment>
+ <comment xml:lang="hu">Java-archívum</comment>
+ <comment xml:lang="ia">Archivo Java</comment>
+ <comment xml:lang="id">Arsip Java</comment>
+ <comment xml:lang="it">Archivio Java</comment>
+ <comment xml:lang="ja">Java アーカイブ</comment>
+ <comment xml:lang="kk">Java архиві</comment>
+ <comment xml:lang="ko">Java 묶음 파일</comment>
+ <comment xml:lang="lt">Java archyvas</comment>
+ <comment xml:lang="lv">Java arhīvs</comment>
+ <comment xml:lang="ms">Arkib Java</comment>
+ <comment xml:lang="nb">Java-arkiv</comment>
+ <comment xml:lang="nl">Java-archief</comment>
+ <comment xml:lang="nn">Java-arkiv</comment>
+ <comment xml:lang="oc">archiu Java</comment>
+ <comment xml:lang="pl">Archiwum Java</comment>
+ <comment xml:lang="pt">arquivo Java</comment>
+ <comment xml:lang="pt_BR">Pacote Java</comment>
+ <comment xml:lang="ro">Arhivă Java</comment>
+ <comment xml:lang="ru">Архив Java</comment>
+ <comment xml:lang="sk">Archív Java</comment>
+ <comment xml:lang="sl">Datoteka arhiva Java</comment>
+ <comment xml:lang="sq">Arkiv Java</comment>
+ <comment xml:lang="sr">архива Јаве</comment>
+ <comment xml:lang="sv">Java-arkiv</comment>
+ <comment xml:lang="tr">Java arşivi</comment>
+ <comment xml:lang="uk">архів Java</comment>
+ <comment xml:lang="vi">Kho nén Java</comment>
+ <comment xml:lang="zh_CN">Java 归档文件</comment>
+ <comment xml:lang="zh_TW">Java 封存檔</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="package-x-generic"/>
+ <alias type="application/x-jar"/>
+ <alias type="application/java-archive"/>
+ <glob pattern="*.jar"/>
+ </mime-type>
+ <mime-type type="application/x-java">
+ <comment>Java class</comment>
+ <comment xml:lang="ar">صنف java</comment>
+ <comment xml:lang="be@latin">Klasa Java</comment>
+ <comment xml:lang="bg">Клас на Java</comment>
+ <comment xml:lang="ca">classe de Java</comment>
+ <comment xml:lang="cs">třída Java</comment>
+ <comment xml:lang="da">Javaklasse</comment>
+ <comment xml:lang="de">Java-Klasse</comment>
+ <comment xml:lang="el">Κλάση Java</comment>
+ <comment xml:lang="en_GB">Java class</comment>
+ <comment xml:lang="eo">Java-klaso</comment>
+ <comment xml:lang="es">clase de Java</comment>
+ <comment xml:lang="eu">Java-ko klasea</comment>
+ <comment xml:lang="fi">Java-luokka</comment>
+ <comment xml:lang="fo">Java flokkur</comment>
+ <comment xml:lang="fr">classe Java</comment>
+ <comment xml:lang="ga">aicme Java</comment>
+ <comment xml:lang="gl">clase de Java</comment>
+ <comment xml:lang="he">מחלקת Java</comment>
+ <comment xml:lang="hr">Java klasa</comment>
+ <comment xml:lang="hu">Java-osztály</comment>
+ <comment xml:lang="ia">Classe Java</comment>
+ <comment xml:lang="id">Kelas Java</comment>
+ <comment xml:lang="it">Classe Java</comment>
+ <comment xml:lang="ja">Java クラス</comment>
+ <comment xml:lang="kk">Java класы</comment>
+ <comment xml:lang="ko">Java 클래스</comment>
+ <comment xml:lang="lt">Java klasė</comment>
+ <comment xml:lang="lv">Java klase</comment>
+ <comment xml:lang="ms">Kelas Java</comment>
+ <comment xml:lang="nb">Java-klasse</comment>
+ <comment xml:lang="nl">Java-klasse</comment>
+ <comment xml:lang="nn">Java-klasse</comment>
+ <comment xml:lang="oc">classa Java</comment>
+ <comment xml:lang="pl">Klasa Java</comment>
+ <comment xml:lang="pt">classe Java</comment>
+ <comment xml:lang="pt_BR">Classe Java</comment>
+ <comment xml:lang="ro">Clasă Java</comment>
+ <comment xml:lang="ru">Класс Java</comment>
+ <comment xml:lang="sk">Trieda Java</comment>
+ <comment xml:lang="sl">Datoteka razreda Java</comment>
+ <comment xml:lang="sq">Klasë Java</comment>
+ <comment xml:lang="sr">разред Јаве</comment>
+ <comment xml:lang="sv">Java-klass</comment>
+ <comment xml:lang="tr">Java sınıfı</comment>
+ <comment xml:lang="uk">клас Java</comment>
+ <comment xml:lang="vi">Hạng Java</comment>
+ <comment xml:lang="zh_CN">Java 类</comment>
+ <comment xml:lang="zh_TW">Java class</comment>
+ <magic priority="50">
+ <match value="0xcafebabe" type="big32" offset="0"/>
+ </magic>
+ <alias type="application/java"/>
+ <alias type="application/java-byte-code"/>
+ <alias type="application/java-vm"/>
+ <alias type="application/x-java-class"/>
+ <alias type="application/x-java-vm"/>
+ <glob pattern="*.class"/>
+ </mime-type>
+ <mime-type type="application/x-java-jnlp-file">
+ <comment>JNLP file</comment>
+ <comment xml:lang="ar">ملف JNLP</comment>
+ <comment xml:lang="be@latin">Fajł JNLP</comment>
+ <comment xml:lang="bg">Файл — JNLP</comment>
+ <comment xml:lang="ca">fitxer JNLP</comment>
+ <comment xml:lang="cs">soubor JNLP</comment>
+ <comment xml:lang="da">JNPL-fil</comment>
+ <comment xml:lang="de">JNLP-Datei</comment>
+ <comment xml:lang="el">Αρχείο JNLP</comment>
+ <comment xml:lang="en_GB">JNLP file</comment>
+ <comment xml:lang="eo">JNLP-dosiero</comment>
+ <comment xml:lang="es">archivo JNPL</comment>
+ <comment xml:lang="eu">JNLP fitxategia</comment>
+ <comment xml:lang="fi">JNLP-tiedosto</comment>
+ <comment xml:lang="fo">JNLP fíla</comment>
+ <comment xml:lang="fr">fichier JNLP</comment>
+ <comment xml:lang="ga">comhad JNLP</comment>
+ <comment xml:lang="gl">ficheiro JNLP</comment>
+ <comment xml:lang="he">קובץ JNLP</comment>
+ <comment xml:lang="hr">JNLP datoteka</comment>
+ <comment xml:lang="hu">JNLP fájl</comment>
+ <comment xml:lang="ia">File JNLP</comment>
+ <comment xml:lang="id">Berkas JNLP</comment>
+ <comment xml:lang="it">File JNPL</comment>
+ <comment xml:lang="ja">JNLP ファイル</comment>
+ <comment xml:lang="kk">JNLP файлы</comment>
+ <comment xml:lang="ko">JNLP 파일</comment>
+ <comment xml:lang="lt">JNLP failas</comment>
+ <comment xml:lang="lv">JNLP datne</comment>
+ <comment xml:lang="nb">JNLP-fil</comment>
+ <comment xml:lang="nl">JNLP-bestand</comment>
+ <comment xml:lang="nn">JNLP-fil</comment>
+ <comment xml:lang="oc">fichièr JNLP</comment>
+ <comment xml:lang="pl">Plik JNLP</comment>
+ <comment xml:lang="pt">ficheiro JNLP</comment>
+ <comment xml:lang="pt_BR">Arquivo JNLP</comment>
+ <comment xml:lang="ro">Fișier JNLP</comment>
+ <comment xml:lang="ru">Файл JNLP</comment>
+ <comment xml:lang="sk">Súbor JNLP</comment>
+ <comment xml:lang="sl">Datoteka JNLP</comment>
+ <comment xml:lang="sq">File JNLP</comment>
+ <comment xml:lang="sr">ЈНЛП датотека</comment>
+ <comment xml:lang="sv">JNLP-fil</comment>
+ <comment xml:lang="tr">JNLP dosyası</comment>
+ <comment xml:lang="uk">файл JNLP</comment>
+ <comment xml:lang="vi">Tập tin JNLP</comment>
+ <comment xml:lang="zh_CN">JNLP 文件</comment>
+ <comment xml:lang="zh_TW">JNLP 檔案</comment>
+ <acronym>JNLP</acronym>
+ <expanded-acronym>Java Network Launching Protocol</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.jnlp"/>
+ <magic priority="50">
+ <match value="&lt;jnlp" type="string" offset="0:256"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-java-keystore">
+ <comment>Java keystore</comment>
+ <comment xml:lang="ar">مخزن مفاتيح جافا</comment>
+ <comment xml:lang="bg">Ключодържател — Java</comment>
+ <comment xml:lang="ca">magatzem de claus de Java</comment>
+ <comment xml:lang="cs">úložiště klíčů Java</comment>
+ <comment xml:lang="da">Javanøglelager</comment>
+ <comment xml:lang="de">Java-Schlüsselbund</comment>
+ <comment xml:lang="el">Χώρος αποθήκευσης κλειδιών Java</comment>
+ <comment xml:lang="en_GB">Java keystore</comment>
+ <comment xml:lang="es">almacén de claves de Java</comment>
+ <comment xml:lang="eu">Java-ren gako-biltegia</comment>
+ <comment xml:lang="fi">Java-avainvarasto</comment>
+ <comment xml:lang="fo">Java lyklagoymsla</comment>
+ <comment xml:lang="fr">stockage de clés Java</comment>
+ <comment xml:lang="ga">eochairstór Java</comment>
+ <comment xml:lang="gl">almacén de chaves de Java</comment>
+ <comment xml:lang="he">אחסון מפתחות של Java</comment>
+ <comment xml:lang="hr">Java baza ključeva</comment>
+ <comment xml:lang="hu">Java kulcstároló</comment>
+ <comment xml:lang="ia">Magazin de claves Java</comment>
+ <comment xml:lang="id">Penyimpanan kunci Java</comment>
+ <comment xml:lang="it">Keystore Java</comment>
+ <comment xml:lang="ja">Java キーストア</comment>
+ <comment xml:lang="kk">Java сақталымы</comment>
+ <comment xml:lang="ko">Java 키 저장소</comment>
+ <comment xml:lang="lt">Java raktų saugykla</comment>
+ <comment xml:lang="lv">Java keystore</comment>
+ <comment xml:lang="nl">Java keystore</comment>
+ <comment xml:lang="oc">emmagazinatge de claus Java</comment>
+ <comment xml:lang="pl">Baza kluczy Java</comment>
+ <comment xml:lang="pt">armazém de chaves Java</comment>
+ <comment xml:lang="pt_BR">Keystore de Java</comment>
+ <comment xml:lang="ro">Stocare chei Java</comment>
+ <comment xml:lang="ru">Хранилище ключей Java</comment>
+ <comment xml:lang="sk">Úložisko kľúčov Java</comment>
+ <comment xml:lang="sl">Datoteka tipkovne razporeditve Java</comment>
+ <comment xml:lang="sr">смештај кључа Јаве</comment>
+ <comment xml:lang="sv">Java-nyckellager</comment>
+ <comment xml:lang="tr">Java deposu</comment>
+ <comment xml:lang="uk">сховище ключів Java</comment>
+ <comment xml:lang="zh_CN">Java 密钥库</comment>
+ <comment xml:lang="zh_TW">Java 金鑰儲存</comment>
+ <magic priority="50">
+ <match value="0xfeedfeed" type="big32" offset="0"/>
+ </magic>
+ <glob pattern="*.jks"/>
+ <glob pattern="*.ks"/>
+ <glob pattern="cacerts"/>
+ </mime-type>
+ <mime-type type="application/x-java-jce-keystore">
+ <comment>Java JCE keystore</comment>
+ <comment xml:lang="ar">مخزن مفاتيح Java JCE</comment>
+ <comment xml:lang="bg">Ключодържател — Java JCE</comment>
+ <comment xml:lang="ca">magatzem de claus JCE de Java</comment>
+ <comment xml:lang="cs">úložiště klíčů Java JCE</comment>
+ <comment xml:lang="da">Java JCE-nøglelager</comment>
+ <comment xml:lang="de">Java JCE-Schlüsselbund</comment>
+ <comment xml:lang="el">Αποθήκη κλειδιών Java JCE</comment>
+ <comment xml:lang="en_GB">Java JCE keystore</comment>
+ <comment xml:lang="es">almacén de claves JCE de Java</comment>
+ <comment xml:lang="eu">Java JCE-ren gako-biltegia</comment>
+ <comment xml:lang="fi">Java JCE -avainvarasto</comment>
+ <comment xml:lang="fo">Java JCE lyklagoymsla</comment>
+ <comment xml:lang="fr">stockage de clés Java JCE</comment>
+ <comment xml:lang="ga">eochairstór Java JCE</comment>
+ <comment xml:lang="gl">almacén de chves JCE de Java</comment>
+ <comment xml:lang="he">אחסון מפתחות של Java JCE</comment>
+ <comment xml:lang="hr">Java JCE baza ključeva</comment>
+ <comment xml:lang="hu">Java JCE kulcstároló</comment>
+ <comment xml:lang="ia">Magazin de claves Java JCE</comment>
+ <comment xml:lang="id">Penyimpanan kunci Java JCE</comment>
+ <comment xml:lang="it">Keystore Java JCE</comment>
+ <comment xml:lang="ja">Java JCE キーストア</comment>
+ <comment xml:lang="kk">Java JCE сақталымы</comment>
+ <comment xml:lang="ko">Java JCE 키 저장소</comment>
+ <comment xml:lang="lt">Java JCE raktų saugykla</comment>
+ <comment xml:lang="lv">Java JCE keystore</comment>
+ <comment xml:lang="nl">Java JCE keystore</comment>
+ <comment xml:lang="oc">emmagazinatge de claus Java JCE</comment>
+ <comment xml:lang="pl">Baza kluczy Java JCE</comment>
+ <comment xml:lang="pt">armazém de chaves JavaJCE</comment>
+ <comment xml:lang="pt_BR">Keystore JCE do Java</comment>
+ <comment xml:lang="ro">Stocare chei Java JCE</comment>
+ <comment xml:lang="ru">Хранилище ключей Java JCE</comment>
+ <comment xml:lang="sk">Úložisko kľúčov Java JCE</comment>
+ <comment xml:lang="sl">Datoteka tipkovne razporeditve Java JCE</comment>
+ <comment xml:lang="sr">смештај ЈЦЕ кључа Јаве</comment>
+ <comment xml:lang="sv">Java JCE-nyckellager</comment>
+ <comment xml:lang="tr">Java JCE deposu</comment>
+ <comment xml:lang="uk">сховище ключів JCE Java</comment>
+ <comment xml:lang="zh_CN">Java JCE 密钥库</comment>
+ <comment xml:lang="zh_TW">Java JCE 金鑰儲存</comment>
+ <acronym>JCE</acronym>
+ <expanded-acronym>Java Cryptography Extension</expanded-acronym>
+ <magic priority="50">
+ <match value="0xcececece" type="host32" offset="0"/>
+ </magic>
+ <glob pattern="*.jceks"/>
+ </mime-type>
+ <mime-type type="application/x-java-pack200">
+ <comment>Pack200 Java archive</comment>
+ <comment xml:lang="ar">أرشيف Pack200 Java</comment>
+ <comment xml:lang="be@latin">Archiŭ Pack200 Java</comment>
+ <comment xml:lang="bg">Архив — Java Pack200</comment>
+ <comment xml:lang="ca">arxiu de Java en Pack200</comment>
+ <comment xml:lang="cs">archiv Java Pack200</comment>
+ <comment xml:lang="da">Pack200 Java-arkiv</comment>
+ <comment xml:lang="de">Pack200-Java-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Java Pack200</comment>
+ <comment xml:lang="en_GB">Pack200 Java archive</comment>
+ <comment xml:lang="es">archivador Pack200 Java</comment>
+ <comment xml:lang="eu">Pack2000 Java artxiboa</comment>
+ <comment xml:lang="fi">Pack200-Java-arkisto</comment>
+ <comment xml:lang="fo">Pack200 Java skjalasavn</comment>
+ <comment xml:lang="fr">archive Java Pack200</comment>
+ <comment xml:lang="ga">cartlann Java Pack200</comment>
+ <comment xml:lang="gl">arquivo Pack200 Java</comment>
+ <comment xml:lang="he">ארכיון מסוג Pack200 Java</comment>
+ <comment xml:lang="hr">Pack200 Java arhiva</comment>
+ <comment xml:lang="hu">Pack200 Java-archívum</comment>
+ <comment xml:lang="ia">Archivo Java Pack200</comment>
+ <comment xml:lang="id">Arsip Pack200 Java</comment>
+ <comment xml:lang="it">Archivio Pack200 Java</comment>
+ <comment xml:lang="ja">Pack200 Java アーカイブ</comment>
+ <comment xml:lang="kk">Pack200 Java архиві</comment>
+ <comment xml:lang="ko">Pack200 Java 압축 파일</comment>
+ <comment xml:lang="lt">Pack200 Java archyvas</comment>
+ <comment xml:lang="lv">Pack200 Java arhīvs</comment>
+ <comment xml:lang="nb">Pack200 Java-arkiv</comment>
+ <comment xml:lang="nl">Pack200 Java-archief</comment>
+ <comment xml:lang="nn">Pack200 Java-arkiv</comment>
+ <comment xml:lang="oc">archiu Java Pack200</comment>
+ <comment xml:lang="pl">Archiwum Java Pack200</comment>
+ <comment xml:lang="pt">arquivo Java Pack200</comment>
+ <comment xml:lang="pt_BR">Pacote Java Pack200</comment>
+ <comment xml:lang="ro">Arhivă Java Pack2000</comment>
+ <comment xml:lang="ru">Архив Java Pack200</comment>
+ <comment xml:lang="sk">Archív Java Pack200</comment>
+ <comment xml:lang="sl">Datoteka arhiva Pack200 Java</comment>
+ <comment xml:lang="sq">Arkiv Java Pack200</comment>
+ <comment xml:lang="sr">архива Јаве Пак200</comment>
+ <comment xml:lang="sv">Pack200 Java-arkiv</comment>
+ <comment xml:lang="tr">Pack200 Java arşivi</comment>
+ <comment xml:lang="uk">архів Java Pack200</comment>
+ <comment xml:lang="vi">Kho nén Java Pack200</comment>
+ <comment xml:lang="zh_CN">Pack200 Java 归档文件</comment>
+ <comment xml:lang="zh_TW">Pack200 Java 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="0xcafed00d" type="big32" offset="0"/>
+ </magic>
+ <glob pattern="*.pack"/>
+ </mime-type>
+ <mime-type type="application/javascript">
+ <comment>JavaScript program</comment>
+ <comment xml:lang="ar">برنامج جافاسكربت</comment>
+ <comment xml:lang="be@latin">Prahrama JavaScript</comment>
+ <comment xml:lang="bg">Програма на JavaScript</comment>
+ <comment xml:lang="ca">programa JavaScript</comment>
+ <comment xml:lang="cs">program v JavaScriptu</comment>
+ <comment xml:lang="da">JavaScript-program</comment>
+ <comment xml:lang="de">JavaScript-Programm</comment>
+ <comment xml:lang="el">Πρόγραμμα JavaScript</comment>
+ <comment xml:lang="en_GB">JavaScript program</comment>
+ <comment xml:lang="eo">JavaScript-programo</comment>
+ <comment xml:lang="es">programa en JavaScript</comment>
+ <comment xml:lang="eu">JavaScript programa</comment>
+ <comment xml:lang="fi">JavaScript-ohjelma</comment>
+ <comment xml:lang="fo">JavaScript forrit</comment>
+ <comment xml:lang="fr">programme JavaScript</comment>
+ <comment xml:lang="ga">ríomhchlár JavaScript</comment>
+ <comment xml:lang="gl">programa JavaScript</comment>
+ <comment xml:lang="he">תכנית JavaScript</comment>
+ <comment xml:lang="hr">JavaScript program</comment>
+ <comment xml:lang="hu">JavaScript-program</comment>
+ <comment xml:lang="ia">Programma JavaScript</comment>
+ <comment xml:lang="id">Program JavaScript</comment>
+ <comment xml:lang="it">Programma JavaScript</comment>
+ <comment xml:lang="ja">JavaScript プログラム</comment>
+ <comment xml:lang="kk">JavaScript бағдарламасы</comment>
+ <comment xml:lang="ko">JavaScript 프로그램</comment>
+ <comment xml:lang="lt">JavaScript programa</comment>
+ <comment xml:lang="lv">JavaScript programma</comment>
+ <comment xml:lang="ms">Program JavaScript</comment>
+ <comment xml:lang="nb">JavaScript-program</comment>
+ <comment xml:lang="nl">JavaScript-programma</comment>
+ <comment xml:lang="nn">JavaScript-program</comment>
+ <comment xml:lang="oc">programa JavaEscript</comment>
+ <comment xml:lang="pl">Pogram JavaScript</comment>
+ <comment xml:lang="pt">programa JavaScript</comment>
+ <comment xml:lang="pt_BR">Programa JavaScript</comment>
+ <comment xml:lang="ro">Program JavaScript</comment>
+ <comment xml:lang="ru">Программа JavaScript</comment>
+ <comment xml:lang="sk">Program jazyka JavaScript</comment>
+ <comment xml:lang="sl">Programska datoteka JavaScript</comment>
+ <comment xml:lang="sq">Program JavaScript</comment>
+ <comment xml:lang="sr">програм Јава скрипте</comment>
+ <comment xml:lang="sv">JavaScript-program</comment>
+ <comment xml:lang="tr">JavaScript programı</comment>
+ <comment xml:lang="uk">програма мовою JavaScript</comment>
+ <comment xml:lang="vi">Chương trình JavaScript</comment>
+ <comment xml:lang="zh_CN">JavaScript 程序</comment>
+ <comment xml:lang="zh_TW">JavaScript 程式</comment>
+ <alias type="application/x-javascript"/>
+ <alias type="text/javascript"/>
+ <sub-class-of type="application/ecmascript"/>
+ <generic-icon name="text-x-script"/>
+ <magic priority="50">
+ <match value="#!/bin/gjs" type="string" offset="0"/>
+ <match value="#! /bin/gjs" type="string" offset="0"/>
+ <match value='eval \"exec /bin/gjs' type="string" offset="0"/>
+ <match value="#!/usr/bin/gjs" type="string" offset="0"/>
+ <match value="#! /usr/bin/gjs" type="string" offset="0"/>
+ <match value='eval \"exec /usr/bin/gjs' type="string" offset="0"/>
+ <match value="#!/usr/local/bin/gjs" type="string" offset="0"/>
+ <match value="#! /usr/local/bin/gjs" type="string" offset="0"/>
+ <match value='eval \"exec /usr/local/bin/gjs' type="string" offset="0"/>
+ <match value='/bin/env gjs' type="string" offset="2:16"/>
+ </magic>
+ <glob pattern="*.js"/>
+ <glob pattern="*.jsm"/>
+ <glob pattern="*.mjs"/>
+ </mime-type>
+ <mime-type type="application/json">
+ <comment>JSON document</comment>
+ <comment xml:lang="ast">Documentu JSON</comment>
+ <comment xml:lang="ca">document JSON</comment>
+ <comment xml:lang="cs">dokument JSON</comment>
+ <comment xml:lang="da">JSON-dokument</comment>
+ <comment xml:lang="de">JSON-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο JSON</comment>
+ <comment xml:lang="en_GB">JSON document</comment>
+ <comment xml:lang="es">documento JSON</comment>
+ <comment xml:lang="eu">JSON dokumentua</comment>
+ <comment xml:lang="fi">JSON-asiakirja</comment>
+ <comment xml:lang="fr">document JSON</comment>
+ <comment xml:lang="ga">cáipéis JSON</comment>
+ <comment xml:lang="gl">Documento JSON</comment>
+ <comment xml:lang="he">מסמך JSON</comment>
+ <comment xml:lang="hr">JSON dokument</comment>
+ <comment xml:lang="hu">JSON dokumentum</comment>
+ <comment xml:lang="ia">Documento JSON</comment>
+ <comment xml:lang="id">Dokumen JSON</comment>
+ <comment xml:lang="it">Documento JSON</comment>
+ <comment xml:lang="kk">JSON құжаты</comment>
+ <comment xml:lang="ko">JSON 문서</comment>
+ <comment xml:lang="oc">document JSON</comment>
+ <comment xml:lang="pl">Dokument JSON</comment>
+ <comment xml:lang="pt">documento JSON</comment>
+ <comment xml:lang="pt_BR">Documento JSON</comment>
+ <comment xml:lang="ru">Документ JSON</comment>
+ <comment xml:lang="sk">Dokument JSON</comment>
+ <comment xml:lang="sl">Dokument JSON</comment>
+ <comment xml:lang="sr">ЈСОН документ</comment>
+ <comment xml:lang="sv">JSON-dokument</comment>
+ <comment xml:lang="tr">JSON belgesi</comment>
+ <comment xml:lang="uk">документ JSON</comment>
+ <comment xml:lang="zh_CN">JSON 文档</comment>
+ <comment xml:lang="zh_TW">JSON 文件</comment>
+ <acronym>JSON</acronym>
+ <expanded-acronym>JavaScript Object Notation</expanded-acronym>
+ <sub-class-of type="application/javascript"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.json"/>
+ </mime-type>
+ <mime-type type="application/jrd+json">
+ <comment>JRD document</comment>
+ <comment xml:lang="ast">Documentu JRD</comment>
+ <comment xml:lang="ca">document JRD</comment>
+ <comment xml:lang="cs">dokument JRD</comment>
+ <comment xml:lang="da">JRD-dokument</comment>
+ <comment xml:lang="de">JRD-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο JRD</comment>
+ <comment xml:lang="en_GB">JRD document</comment>
+ <comment xml:lang="es">documento JRD</comment>
+ <comment xml:lang="eu">JRD dokumentua</comment>
+ <comment xml:lang="fi">JRD-asiakirja</comment>
+ <comment xml:lang="fr">document JRD</comment>
+ <comment xml:lang="ga">cáipéis JRD</comment>
+ <comment xml:lang="he">מסמך JRD</comment>
+ <comment xml:lang="hr">JRD dokument</comment>
+ <comment xml:lang="hu">JRD dokumentum</comment>
+ <comment xml:lang="ia">Documento JRD</comment>
+ <comment xml:lang="id">Dokumen JRD</comment>
+ <comment xml:lang="it">Documento JRD</comment>
+ <comment xml:lang="kk">JRD құжаты</comment>
+ <comment xml:lang="ko">JRD 문서</comment>
+ <comment xml:lang="oc">document JRD</comment>
+ <comment xml:lang="pl">Dokument JRD</comment>
+ <comment xml:lang="pt">doxumento JRD</comment>
+ <comment xml:lang="pt_BR">Documento JRD</comment>
+ <comment xml:lang="ru">Документ JRD</comment>
+ <comment xml:lang="sk">Dokument JRD</comment>
+ <comment xml:lang="sr">ЈРД документ</comment>
+ <comment xml:lang="sv">JRD-dokument</comment>
+ <comment xml:lang="tr">JRD belgesi</comment>
+ <comment xml:lang="uk">документ JRD</comment>
+ <comment xml:lang="zh_CN">JRD 文档</comment>
+ <comment xml:lang="zh_TW">JRD 文件</comment>
+ <acronym>JRD</acronym>
+ <expanded-acronym>JSON Resource Descriptor</expanded-acronym>
+ <sub-class-of type="application/json"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.jrd"/>
+ </mime-type>
+ <mime-type type="application/json-patch+json">
+ <comment>JSON patch</comment>
+ <comment xml:lang="ca">pedaç de JSON</comment>
+ <comment xml:lang="cs">cesta JSON</comment>
+ <comment xml:lang="da">JSON-rettelse</comment>
+ <comment xml:lang="de">JSON-Patch</comment>
+ <comment xml:lang="en_GB">JSON patch</comment>
+ <comment xml:lang="es">parche en JSON</comment>
+ <comment xml:lang="eu">JSON adabakia</comment>
+ <comment xml:lang="fr">correctif JSON</comment>
+ <comment xml:lang="ga">paiste JSON</comment>
+ <comment xml:lang="he">טלאי JSON</comment>
+ <comment xml:lang="hr">JSON zakrpa</comment>
+ <comment xml:lang="hu">JSON javítócsomag</comment>
+ <comment xml:lang="ia">Patch JSON</comment>
+ <comment xml:lang="id">Patch JSON</comment>
+ <comment xml:lang="it">Patch JSON</comment>
+ <comment xml:lang="kk">JSON өзгерісі</comment>
+ <comment xml:lang="ko">JSON 패치</comment>
+ <comment xml:lang="oc">correctiu JSON</comment>
+ <comment xml:lang="pl">Łata JSON</comment>
+ <comment xml:lang="pt">patch JSON</comment>
+ <comment xml:lang="pt_BR">Patch JSON</comment>
+ <comment xml:lang="ru">Патч JSON</comment>
+ <comment xml:lang="sk">Záplata JSON</comment>
+ <comment xml:lang="sr">ЈСОН закрпа</comment>
+ <comment xml:lang="sv">JSON patch</comment>
+ <comment xml:lang="tr">JSON yaması</comment>
+ <comment xml:lang="uk">латка JSON</comment>
+ <comment xml:lang="zh_CN">JSON 补丁</comment>
+ <comment xml:lang="zh_TW">JSON 修補檔</comment>
+ <acronym>JSON</acronym>
+ <expanded-acronym>JavaScript Object Notation</expanded-acronym>
+ <sub-class-of type="application/json"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.json-patch"/>
+ </mime-type>
+ <mime-type type="application/ld+json">
+ <comment>JSON-LD document</comment>
+ <comment xml:lang="ast">Documentu JSON-LD</comment>
+ <comment xml:lang="ca">document JSON-LD</comment>
+ <comment xml:lang="cs">dokument JSON-LD</comment>
+ <comment xml:lang="da">JSON-LD-dokument</comment>
+ <comment xml:lang="de">JSON-LD-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο JSON-LD</comment>
+ <comment xml:lang="en_GB">JSON-LD document</comment>
+ <comment xml:lang="es">documento JSON-LD</comment>
+ <comment xml:lang="eu">JSON-LD dokumentua</comment>
+ <comment xml:lang="fi">JSON-LD-asiakirja</comment>
+ <comment xml:lang="fr">document JSON-LD</comment>
+ <comment xml:lang="ga">cáipéis JSON-LD</comment>
+ <comment xml:lang="he">מסמך JSON-LD</comment>
+ <comment xml:lang="hr">JSON-LD dokument</comment>
+ <comment xml:lang="hu">JSON-LD dokumentum</comment>
+ <comment xml:lang="ia">Documento JSON-LD</comment>
+ <comment xml:lang="id">Dokumen JSON-LD</comment>
+ <comment xml:lang="it">Documento JSON-LD</comment>
+ <comment xml:lang="kk">JSON-LD құжаты</comment>
+ <comment xml:lang="ko">JSON-LD 문서</comment>
+ <comment xml:lang="oc">Document JSON-LD</comment>
+ <comment xml:lang="pl">Dokument JSON-LD</comment>
+ <comment xml:lang="pt">documento JSON-LD</comment>
+ <comment xml:lang="pt_BR">Documento JSON-LD</comment>
+ <comment xml:lang="ru">Документ JSON-LD</comment>
+ <comment xml:lang="sk">Dokument JSON-LD</comment>
+ <comment xml:lang="sr">ЈСОН-ЛД документ</comment>
+ <comment xml:lang="sv">JSON-LD-dokument</comment>
+ <comment xml:lang="tr">JSON-LD belgesi</comment>
+ <comment xml:lang="uk">документ JSON-LD</comment>
+ <comment xml:lang="zh_CN">JSON-LD 文档</comment>
+ <comment xml:lang="zh_TW">JSON-LD 文件</comment>
+ <acronym>JSON-LD</acronym>
+ <expanded-acronym>JavaScript Object Notation for Linked Data</expanded-acronym>
+ <sub-class-of type="application/json"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.jsonld"/>
+ </mime-type>
+ <mime-type type="application/x-ipynb+json">
+ <comment>Jupyter Notebook</comment>
+ <comment xml:lang="ca">llibreta de notes de Jupyter</comment>
+ <comment xml:lang="cs">sešit Jupyter</comment>
+ <comment xml:lang="da">Jupyter Notebook</comment>
+ <comment xml:lang="de">Jupyter-Dokument</comment>
+ <comment xml:lang="en_GB">Jupyter Notebook</comment>
+ <comment xml:lang="es">libreta de Jupyter</comment>
+ <comment xml:lang="eu">Jupyter Notebook</comment>
+ <comment xml:lang="fr">carnet de notes Jupyter</comment>
+ <comment xml:lang="ga">Leabhar nótaí Jupyter</comment>
+ <comment xml:lang="he">מחברת Jupyter</comment>
+ <comment xml:lang="hr">Jupyter bilježnica</comment>
+ <comment xml:lang="hu">Jupyter notesz</comment>
+ <comment xml:lang="id">Notebook Jupyter</comment>
+ <comment xml:lang="it">Notebook Jupyter</comment>
+ <comment xml:lang="kk">Jupyter Notebook</comment>
+ <comment xml:lang="ko">Jupyter 노트북</comment>
+ <comment xml:lang="pl">Notatnik Jupyter</comment>
+ <comment xml:lang="pt_BR">Bloco de Notas Jupyter</comment>
+ <comment xml:lang="ru">Jupyter Notebook</comment>
+ <comment xml:lang="sk">Zošit programu Jupyter</comment>
+ <comment xml:lang="sr">Џупитер бележница</comment>
+ <comment xml:lang="sv">Jupyter Notebook-dokument</comment>
+ <comment xml:lang="tr">Jupyter Notebook</comment>
+ <comment xml:lang="uk">записник Jupyter</comment>
+ <comment xml:lang="zh_CN">Jupyter 笔记本</comment>
+ <comment xml:lang="zh_TW">Jupyter 記事本</comment>
+ <sub-class-of type="application/json"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="{" type="string" offset="0">
+ <match value='"cells":' type="string" offset="1:256"/>
+ </match>
+ </magic>
+ <glob pattern="*.ipynb"/>
+ </mime-type>
+ <mime-type type="application/vnd.coffeescript">
+ <comment>CoffeeScript document</comment>
+ <comment xml:lang="ast">Documentu de CoffeScript</comment>
+ <comment xml:lang="ca">document CoffeeScript</comment>
+ <comment xml:lang="cs">dokument CoffeeScript</comment>
+ <comment xml:lang="da">CoffeeScript-dokument</comment>
+ <comment xml:lang="de">CoffeeScript-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο CoffeeScript</comment>
+ <comment xml:lang="en_GB">CoffeeScript document</comment>
+ <comment xml:lang="es">documento en CoffeeScript</comment>
+ <comment xml:lang="eu">CoffeeScript dokumentua</comment>
+ <comment xml:lang="fi">CoffeeScript-asiakirja</comment>
+ <comment xml:lang="fr">document CoffeeScript</comment>
+ <comment xml:lang="ga">cáipéis CoffeeScript</comment>
+ <comment xml:lang="he">מסמך CoffeeScript</comment>
+ <comment xml:lang="hr">CoffeeScript dokument</comment>
+ <comment xml:lang="hu">CoffeeScript dokumentum</comment>
+ <comment xml:lang="ia">Documento CoffeeScript</comment>
+ <comment xml:lang="id">Dokumen CoffeeScript</comment>
+ <comment xml:lang="it">Documento CoffeeScript</comment>
+ <comment xml:lang="kk">CoffeeScript құжаты</comment>
+ <comment xml:lang="ko">CoffeeScript 문서</comment>
+ <comment xml:lang="oc">Document CoffeScript</comment>
+ <comment xml:lang="pl">Dokument CoffeeScript</comment>
+ <comment xml:lang="pt">documento CoffeeScript</comment>
+ <comment xml:lang="pt_BR">Documento CoffeeScript</comment>
+ <comment xml:lang="ru">Документ CoffeeScript</comment>
+ <comment xml:lang="sk">Dokument CoffeeScript</comment>
+ <comment xml:lang="sr">Кофи скрипт документ</comment>
+ <comment xml:lang="sv">CoffeeScript-dokument</comment>
+ <comment xml:lang="tr">CoffeeScript belgesi</comment>
+ <comment xml:lang="uk">документ CoffeeScript</comment>
+ <comment xml:lang="zh_CN">CoffeeScript 文档</comment>
+ <comment xml:lang="zh_TW">CoffeeScript 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.coffee"/>
+ </mime-type>
+ <mime-type type="application/x-jbuilder-project">
+ <comment>JBuilder project</comment>
+ <comment xml:lang="ar">مشروع JBuilder</comment>
+ <comment xml:lang="be@latin">Prajekt JBuilder</comment>
+ <comment xml:lang="bg">Проект — JBuilder</comment>
+ <comment xml:lang="ca">projecte de JBuilder</comment>
+ <comment xml:lang="cs">projekt JBuilder</comment>
+ <comment xml:lang="da">JBuilder-projekt</comment>
+ <comment xml:lang="de">JBuilder-Projekt</comment>
+ <comment xml:lang="el">Εργο JBuilder</comment>
+ <comment xml:lang="en_GB">JBuilder project</comment>
+ <comment xml:lang="eo">JBuilder-projekto</comment>
+ <comment xml:lang="es">proyecto de JBuilder</comment>
+ <comment xml:lang="eu">JBuilder proiektua</comment>
+ <comment xml:lang="fi">JBuilder-projekti</comment>
+ <comment xml:lang="fo">JBuilder verkætlan</comment>
+ <comment xml:lang="fr">projet JBuilder</comment>
+ <comment xml:lang="ga">tionscadal JBuilder</comment>
+ <comment xml:lang="gl">proxecto de JBuilder</comment>
+ <comment xml:lang="he">מיזם JBuilder</comment>
+ <comment xml:lang="hr">JBuilder projekt</comment>
+ <comment xml:lang="hu">JBuilder-projekt</comment>
+ <comment xml:lang="ia">Projecto JBuilder</comment>
+ <comment xml:lang="id">Proyek JBuilder</comment>
+ <comment xml:lang="it">Progetto JBuilder</comment>
+ <comment xml:lang="ja">JBuilder プロジェクト</comment>
+ <comment xml:lang="kk">JBuilder жобасы</comment>
+ <comment xml:lang="ko">JBuilder 프로젝트</comment>
+ <comment xml:lang="lt">JBuilder projektas</comment>
+ <comment xml:lang="lv">JBuilder projekts</comment>
+ <comment xml:lang="ms">Projek JBuilder</comment>
+ <comment xml:lang="nb">JBuilder-prosjekt</comment>
+ <comment xml:lang="nl">JBuilder-project</comment>
+ <comment xml:lang="nn">JBuilder-prosjekt</comment>
+ <comment xml:lang="oc">projècte JBuilder</comment>
+ <comment xml:lang="pl">Projekt JBuilder</comment>
+ <comment xml:lang="pt">projecto JBuilder</comment>
+ <comment xml:lang="pt_BR">Projeto do JBuilder</comment>
+ <comment xml:lang="ro">Proiect JBuilder</comment>
+ <comment xml:lang="ru">Проект JBuilder</comment>
+ <comment xml:lang="sk">Projekt JBuilder</comment>
+ <comment xml:lang="sl">Datoteka projekta JBuilder</comment>
+ <comment xml:lang="sq">Projekt JBuilder</comment>
+ <comment xml:lang="sr">пројекат ЈГрадитеља</comment>
+ <comment xml:lang="sv">JBuilder-projekt</comment>
+ <comment xml:lang="tr">JBuilder projesi</comment>
+ <comment xml:lang="uk">проект JBuilder</comment>
+ <comment xml:lang="vi">Dự án JBuilder</comment>
+ <comment xml:lang="zh_CN">JBuilder 工程</comment>
+ <comment xml:lang="zh_TW">JBuilder 專案</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.jpr"/>
+ <glob pattern="*.jpx"/>
+ </mime-type>
+ <mime-type type="application/x-karbon">
+ <comment>Karbon14 drawing</comment>
+ <comment xml:lang="ar">تصميم Karbon14</comment>
+ <comment xml:lang="be@latin">Rysunak Karbon14</comment>
+ <comment xml:lang="bg">Чертеж — Karbon14</comment>
+ <comment xml:lang="ca">dibuix de Karbon14</comment>
+ <comment xml:lang="cs">kresba Karbon14</comment>
+ <comment xml:lang="da">Karbon14-tegning</comment>
+ <comment xml:lang="de">Karbon14-Zeichnung</comment>
+ <comment xml:lang="el">Σχέδιο Karbon14</comment>
+ <comment xml:lang="en_GB">Karbon14 drawing</comment>
+ <comment xml:lang="eo">Karbon14-grafikaĵo</comment>
+ <comment xml:lang="es">dibujo de Karbon14</comment>
+ <comment xml:lang="eu">Karbon14 marrazkia</comment>
+ <comment xml:lang="fi">Karbon14-piirros</comment>
+ <comment xml:lang="fo">Karbon14 tekning</comment>
+ <comment xml:lang="fr">dessin Karbon14</comment>
+ <comment xml:lang="ga">líníocht Karbon14</comment>
+ <comment xml:lang="gl">debuxo de Karbon14</comment>
+ <comment xml:lang="he">ציור Karbon14</comment>
+ <comment xml:lang="hr">Karbon14 crtež</comment>
+ <comment xml:lang="hu">Karbon14-rajz</comment>
+ <comment xml:lang="ia">Designo Karbon14</comment>
+ <comment xml:lang="id">Gambar Karbon14</comment>
+ <comment xml:lang="it">Disegno Karbon14</comment>
+ <comment xml:lang="ja">Karbon14 ドロー</comment>
+ <comment xml:lang="kk">Karbon14 суреті</comment>
+ <comment xml:lang="ko">Karbon14 그림</comment>
+ <comment xml:lang="lt">Karbon14 piešinys</comment>
+ <comment xml:lang="lv">Karbon14 zīmējums</comment>
+ <comment xml:lang="ms">Lukisan Karbon14</comment>
+ <comment xml:lang="nb">Karbon14-tegning</comment>
+ <comment xml:lang="nl">Karbon14-tekening</comment>
+ <comment xml:lang="nn">Karbon14-teikning</comment>
+ <comment xml:lang="oc">dessenh Karbon14</comment>
+ <comment xml:lang="pl">Rysunek Karbon14</comment>
+ <comment xml:lang="pt">desenho Karbon14</comment>
+ <comment xml:lang="pt_BR">Desenho do Karbon14</comment>
+ <comment xml:lang="ro">Desen Karbon14</comment>
+ <comment xml:lang="ru">Рисунок Karbon14</comment>
+ <comment xml:lang="sk">Kresba Karbon14</comment>
+ <comment xml:lang="sl">Datoteka risbe Karbon14</comment>
+ <comment xml:lang="sq">Vizatim Karbon14</comment>
+ <comment xml:lang="sr">цртеж Карбона14</comment>
+ <comment xml:lang="sv">Karbon14-teckning</comment>
+ <comment xml:lang="tr">Karbon14 çizimi</comment>
+ <comment xml:lang="uk">малюнок Karbon14</comment>
+ <comment xml:lang="vi">Bản vẽ Karbon14</comment>
+ <comment xml:lang="zh_CN">Karbon14 绘图</comment>
+ <comment xml:lang="zh_TW">Karbon14 繪圖</comment>
+ <generic-icon name="image-x-generic"/>
+ <magic priority="60">
+ <match value="\037\213" type="string" offset="0">
+ <match value="KOffice" type="string" offset="10">
+ <match value="application/x-karbon\004\006" type="string" offset="18"/>
+ </match>
+ </match>
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/x-karbon" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.karbon"/>
+ </mime-type>
+ <mime-type type="application/x-kchart">
+ <comment>KChart chart</comment>
+ <comment xml:lang="ar">رسم بياني KChart</comment>
+ <comment xml:lang="be@latin">Hrafik KChart</comment>
+ <comment xml:lang="bg">Диаграма — KChart</comment>
+ <comment xml:lang="ca">diagrama de KChart</comment>
+ <comment xml:lang="cs">graf Chart</comment>
+ <comment xml:lang="da">KChart-diagram</comment>
+ <comment xml:lang="de">KChart-Diagramm</comment>
+ <comment xml:lang="el">Γράφημα KChart</comment>
+ <comment xml:lang="en_GB">KChart chart</comment>
+ <comment xml:lang="eo">KChart-diagramo</comment>
+ <comment xml:lang="es">gráfico de KChart</comment>
+ <comment xml:lang="eu">KChart diagrama</comment>
+ <comment xml:lang="fi">KChart-kaavio</comment>
+ <comment xml:lang="fo">KChart strikumynd</comment>
+ <comment xml:lang="fr">graphique KChart</comment>
+ <comment xml:lang="ga">cairt KChart</comment>
+ <comment xml:lang="gl">gráfica de KChart</comment>
+ <comment xml:lang="he">תרשים KChart</comment>
+ <comment xml:lang="hr">KChart grafikon</comment>
+ <comment xml:lang="hu">KChart-grafikon</comment>
+ <comment xml:lang="ia">Graphico KChart</comment>
+ <comment xml:lang="id">Bagan KChart</comment>
+ <comment xml:lang="it">Grafico KChart</comment>
+ <comment xml:lang="ja">KChart チャート</comment>
+ <comment xml:lang="kk">KChart диаграммасы</comment>
+ <comment xml:lang="ko">KChart 차트</comment>
+ <comment xml:lang="lt">KChart diagrama</comment>
+ <comment xml:lang="lv">KChart diagramma</comment>
+ <comment xml:lang="ms">Carta KChart</comment>
+ <comment xml:lang="nb">KChart-graf</comment>
+ <comment xml:lang="nl">KChart-grafiek</comment>
+ <comment xml:lang="nn">KChart-diagram</comment>
+ <comment xml:lang="oc">grafic KChart</comment>
+ <comment xml:lang="pl">Wykres KChart</comment>
+ <comment xml:lang="pt">gráfico KChart</comment>
+ <comment xml:lang="pt_BR">Gráfico do KChart</comment>
+ <comment xml:lang="ro">Diagramă KChart</comment>
+ <comment xml:lang="ru">Диаграмма KChart</comment>
+ <comment xml:lang="sk">Graf KChart</comment>
+ <comment xml:lang="sl">Datoteka grafikona KChart</comment>
+ <comment xml:lang="sq">Grafik KChart</comment>
+ <comment xml:lang="sr">графикон К-графика</comment>
+ <comment xml:lang="sv">KChart-diagram</comment>
+ <comment xml:lang="tr">KChart çizgesi</comment>
+ <comment xml:lang="uk">діаграма KChart</comment>
+ <comment xml:lang="vi">Sơ đồ KChart</comment>
+ <comment xml:lang="zh_CN">KChart 图表</comment>
+ <comment xml:lang="zh_TW">KChart 圖表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="60">
+ <match value="\037\213" type="string" offset="0">
+ <match value="KOffice" type="string" offset="10">
+ <match value="application/x-kchart\004\006" type="string" offset="18"/>
+ </match>
+ </match>
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/x-kchart" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.chrt"/>
+ </mime-type>
+ <mime-type type="application/x-kexi-connectiondata">
+ <comment>Kexi settings for database server connection</comment>
+ <comment xml:lang="ar">إعدادات Kexi للإتصال بخادم قاعدة البيانات</comment>
+ <comment xml:lang="bg">Връзка към база от данни — Kexi</comment>
+ <comment xml:lang="ca">ajusts de Kexi per a la connexió al servidor de bases de dades</comment>
+ <comment xml:lang="cs">nastavení Kexi ke spojení s databázovým serverem</comment>
+ <comment xml:lang="da">Kexiopsætning til forbindelsen for databaseserveren</comment>
+ <comment xml:lang="de">Kexi-Einstellungen für Verbindung zum Datenbankserver</comment>
+ <comment xml:lang="el">Ρυθμίσεις Kexi για σύνδεση με εξυπηρετητή βάσεων δεδομένων</comment>
+ <comment xml:lang="en_GB">Kexi settings for database server connection</comment>
+ <comment xml:lang="es">configuración de Kexi para conectar con un servidor de bases de datos</comment>
+ <comment xml:lang="eu">Kexi-ren ezarpenak datu-basearen zerbitzariarekin konektatzeko</comment>
+ <comment xml:lang="fi">Kexi-tietokantayhteysasetukset</comment>
+ <comment xml:lang="fo">Kexi stillingar fyri dátustovnsambætara sambinding</comment>
+ <comment xml:lang="fr">paramètres Kexi pour connexion au serveur de base de données</comment>
+ <comment xml:lang="ga">socruithe Kexi do cheangal le freastalaí bunachair sonraí</comment>
+ <comment xml:lang="gl">configuración de Kexi para conexión con servidor de base de datos </comment>
+ <comment xml:lang="he">הגדרות של Kexi עבור חיבור שרת למסד נתונים</comment>
+ <comment xml:lang="hr">Kexi postavke za povezeivanje baza podataka poslužitelja</comment>
+ <comment xml:lang="hu">Kexi beállítások adatbáziskiszolgáló-kapcsolathoz</comment>
+ <comment xml:lang="ia">Configuration Kexi pro connexion al servitor de base de datos</comment>
+ <comment xml:lang="id">Tatanan Kexi bagi koneksi server basis data</comment>
+ <comment xml:lang="it">Impostazioni Kexi per connessione a server di database</comment>
+ <comment xml:lang="ja">データベースサーバ接続用の Kexi 設定</comment>
+ <comment xml:lang="kk">Дерекқор серверге байланыс Kexi баптаулары</comment>
+ <comment xml:lang="ko">Kexi 데이터베이스 서버 연결 설정</comment>
+ <comment xml:lang="lt">Kexi duomenų bazės ryšio su serveriu parametrai</comment>
+ <comment xml:lang="lv">Kexi iestatījumi datubāzes servera savienojumam</comment>
+ <comment xml:lang="nl">Kexi instellingen voor database server connectie</comment>
+ <comment xml:lang="oc">paramètres Kexi per connexion al servidor de banca de donadas</comment>
+ <comment xml:lang="pl">Ustawienia Kexi dla połączenia serwera bazy danych</comment>
+ <comment xml:lang="pt">definições Kexi para ligação de servidor de base de dados</comment>
+ <comment xml:lang="pt_BR">Configurações do Kexi para conexão a servidor de banco de dados</comment>
+ <comment xml:lang="ro">Configurări Kexi pentru conexiunea la serverul de baze de date</comment>
+ <comment xml:lang="ru">Параметры Kexi для подключения к серверу БД</comment>
+ <comment xml:lang="sk">Nastavenia Kexi pre pripojenie k databázovému serveru</comment>
+ <comment xml:lang="sl">Strežniška povezava do nastavitvene datoteke Kexi.</comment>
+ <comment xml:lang="sr">подешавања Кексија за везу са сервером базе података</comment>
+ <comment xml:lang="sv">Kexi-inställningar för anslutning till databasserver</comment>
+ <comment xml:lang="tr">Veritabanı sunucu bağlantısı için Kexi ayarları</comment>
+ <comment xml:lang="uk">параметри Kexi для встановлення з’єднання з сервером бази даних</comment>
+ <comment xml:lang="zh_CN">Kexi 数据库服务器连接设置</comment>
+ <comment xml:lang="zh_TW">Kexi 設定值 (資料庫伺服器連線用)</comment>
+ <glob pattern="*.kexic"/>
+ </mime-type>
+ <mime-type type="application/x-kexiproject-shortcut">
+ <comment>shortcut to Kexi project on database server</comment>
+ <comment xml:lang="ar">اختصار لمشروع Kexi على خادم قاعدة بيانات</comment>
+ <comment xml:lang="bg">Връзка към проект — Kexi</comment>
+ <comment xml:lang="ca">drecera al projecte de Kexi en un servidor de base de dades</comment>
+ <comment xml:lang="cs">zástupce projektu Kexi na databázovém serveru</comment>
+ <comment xml:lang="da">genvej til Kexiprojekt på databaseserver</comment>
+ <comment xml:lang="de">Schnellzugriff zum Kexi-Projekt auf dem Datenbankserver</comment>
+ <comment xml:lang="el">Συντόμευση σε έργο Kexi στον εξυπηρετητή βάσης δεδομένων</comment>
+ <comment xml:lang="en_GB">shortcut to Kexi project on database server</comment>
+ <comment xml:lang="es">acceso directo a proyecto Kexi en el servidor de bases de datos</comment>
+ <comment xml:lang="eu">lasterbidea datu-basearen zerbitzariko Kexi proiekturako</comment>
+ <comment xml:lang="fi">pikakuvake tietokantapalvelimella olevaan Kexi-projektiin</comment>
+ <comment xml:lang="fo">snarvegur til Kexi verkætlan á dátustovnsambætara</comment>
+ <comment xml:lang="fr">raccourci vers projet Kexi sur serveur de base de données</comment>
+ <comment xml:lang="ga">aicearra go tionscadal Kexi ar fhreastalaí bunachair sonraí</comment>
+ <comment xml:lang="gl">acceso directo a proxecto Kexi no servidor de bases de datos</comment>
+ <comment xml:lang="he">קיצור דרך לפרוירט Kexi בשרת נתונים</comment>
+ <comment xml:lang="hr">Prečac do Kexi projekta na poslužitelju baze podataka</comment>
+ <comment xml:lang="hu">indítóikon adatbázis-kiszolgálón lévő Kexi projektre</comment>
+ <comment xml:lang="ia">Ligamine a projecto Kexi in servitor de base de datos</comment>
+ <comment xml:lang="id">pintasan ke projek Kexi pada server basis data</comment>
+ <comment xml:lang="it">Scorciatoia a progetto Kexi su server di database</comment>
+ <comment xml:lang="ja">データベースサーバの Kexi プロジェクトへのショートカット</comment>
+ <comment xml:lang="kk">дерекқор серверіндегі Kexi жобасына сілтеме</comment>
+ <comment xml:lang="ko">데이터베이스 서버의 Kexi 프로젝트 바로 가기</comment>
+ <comment xml:lang="lt">nuoroda į Kexi projektą duomenų bazės serveryje</comment>
+ <comment xml:lang="lv">īsceļš uz Kexi projektu datubāzes serverī</comment>
+ <comment xml:lang="nl">shortcut naar Kexi project op database server</comment>
+ <comment xml:lang="oc">acorchi cap a projècte Kexi sus servidor de banca de donadas</comment>
+ <comment xml:lang="pl">Skrót do projektu Kexi na serwerze bazy danych</comment>
+ <comment xml:lang="pt">atalho para projeto Kexi em servidor de base de dados</comment>
+ <comment xml:lang="pt_BR">Atalho para projeto Kexi no servidor de banco de dados</comment>
+ <comment xml:lang="ro">scurtătură către un proiect Kexi pe un server de baze de date</comment>
+ <comment xml:lang="ru">Ссылка на проект Kexi на сервере БД</comment>
+ <comment xml:lang="sk">Zástupca projektu Kexi na databázovom serveri</comment>
+ <comment xml:lang="sl">bližnjica do Kexi projekta na podatkovnem strežniku</comment>
+ <comment xml:lang="sr">пречица до пројекта Кексија на серверу базе података</comment>
+ <comment xml:lang="sv">genväg till Kexi-projekt på databasserver</comment>
+ <comment xml:lang="tr">veritabanı üzerindeki Kexi projesine kısayol</comment>
+ <comment xml:lang="uk">скорочення для проекту Kexi на сервері бази даних</comment>
+ <comment xml:lang="zh_CN">数据库服务器上 Kexi 项目的快捷方式</comment>
+ <comment xml:lang="zh_TW">資料庫伺服器上 Kexi 專案的捷徑</comment>
+ <glob pattern="*.kexis"/>
+ </mime-type>
+ <mime-type type="application/x-kexiproject-sqlite2">
+ <comment>Kexi database file-based project</comment>
+ <comment xml:lang="ar">مشروع قاعدة بيانات Kexi يعتمد على ملفات</comment>
+ <comment xml:lang="bg">Проект с база от данни — Kexi</comment>
+ <comment xml:lang="ca">projecte basat en fitxer de base de dades de Kexi</comment>
+ <comment xml:lang="cs">projekt založený na souboru databáze Kexi</comment>
+ <comment xml:lang="da">Filbaseret projekt for Kexidatabase</comment>
+ <comment xml:lang="de">Dateibasiertes Kexi-Datenbankprojekt</comment>
+ <comment xml:lang="el">Έργο βάσης δεδομένων Kexi βασισμένο σε αρχεία </comment>
+ <comment xml:lang="en_GB">Kexi database file-based project</comment>
+ <comment xml:lang="es">proyecto de base de datos basada en archivos de Kexi</comment>
+ <comment xml:lang="eu">Kexi datu-baseko fitxategian oinarritutako proiektua</comment>
+ <comment xml:lang="fi">Kexin tiedostoperustainen tietokantaprojekti</comment>
+ <comment xml:lang="fo">Kexi dátustovns fílugrundað verkætlan</comment>
+ <comment xml:lang="fr">projet de base de données Kexi en mode fichier</comment>
+ <comment xml:lang="ga">tionscadal bunachair sonraí Kexi bunaithe ar chomhaid</comment>
+ <comment xml:lang="gl">proxecto baseado no ficheiro-base de datos Kexi</comment>
+ <comment xml:lang="he">מיזם מסד נתונים מבוסס-קובץ של Kexi</comment>
+ <comment xml:lang="hr">Kexi baza podataka datotekom temeljen projekt</comment>
+ <comment xml:lang="hu">Kexi adatbázisfájl-alapú projekt</comment>
+ <comment xml:lang="ia">Projecto de base de datos Kexi in modo file</comment>
+ <comment xml:lang="id">Projek berbasis berkas basis data Kexi</comment>
+ <comment xml:lang="it">Progetto su file di database Kexi</comment>
+ <comment xml:lang="ja">Kexi データベース ファイルベースプロジェクト</comment>
+ <comment xml:lang="kk">Файл негізінде жоба үшін Kexi дерекқоры</comment>
+ <comment xml:lang="ko">Kexi 데이터베이스 파일 기반 프로젝트</comment>
+ <comment xml:lang="lt">Kexi duomenų bazės failo tipo projektas</comment>
+ <comment xml:lang="lv">Kexi datubāzes datnes balstīts projekts</comment>
+ <comment xml:lang="nl">Kexi database bestandgebaseerd project</comment>
+ <comment xml:lang="oc">projècte de banca de donadas Kexi en mòde fichièr</comment>
+ <comment xml:lang="pl">Projekt bazy danych Kexi na podstawie plików</comment>
+ <comment xml:lang="pt">projeto Kexi em base de dados baseada em ficheiros</comment>
+ <comment xml:lang="pt_BR">Projeto de banco de dados baseado em arquivo do Kexi</comment>
+ <comment xml:lang="ro">Proiect bazat pe fișiere al bazei de date Kexi</comment>
+ <comment xml:lang="ru">Файловый проект базы данных Kexi</comment>
+ <comment xml:lang="sk">Projekt databázy Kexi s úložiskom typu súbor</comment>
+ <comment xml:lang="sl">Datoteka projekta podatkovne zbirke Kexi</comment>
+ <comment xml:lang="sr">пројекат Кексијеве базе података на основу датотеке</comment>
+ <comment xml:lang="sv">Kexi-databas för filbaserat projekt</comment>
+ <comment xml:lang="tr">Dosya temelli Kexi veritabanı projesi</comment>
+ <comment xml:lang="uk">проект файлової бази даних Kexi</comment>
+ <comment xml:lang="zh_CN">Kexi 基于文件的数据库项目</comment>
+ <comment xml:lang="zh_TW">Kexi 資料庫檔案基礎專案</comment>
+ <sub-class-of type="application/x-sqlite2"/>
+ <glob pattern="*.kexi"/>
+ </mime-type>
+ <mime-type type="application/x-kexiproject-sqlite3">
+ <comment>Kexi database file-based project</comment>
+ <comment xml:lang="ar">مشروع قاعدة بيانات Kexi يعتمد على ملفات</comment>
+ <comment xml:lang="bg">Проект с база от данни — Kexi</comment>
+ <comment xml:lang="ca">projecte basat en fitxer de base de dades de Kexi</comment>
+ <comment xml:lang="cs">projekt založený na souboru databáze Kexi</comment>
+ <comment xml:lang="da">Filbaseret projekt for Kexidatabase</comment>
+ <comment xml:lang="de">Dateibasiertes Kexi-Datenbankprojekt</comment>
+ <comment xml:lang="el">Έργο βάσης δεδομένων Kexi βασισμένο σε αρχεία </comment>
+ <comment xml:lang="en_GB">Kexi database file-based project</comment>
+ <comment xml:lang="es">proyecto de base de datos basada en archivos de Kexi</comment>
+ <comment xml:lang="eu">Kexi datu-baseko fitxategian oinarritutako proiektua</comment>
+ <comment xml:lang="fi">Kexin tiedostoperustainen tietokantaprojekti</comment>
+ <comment xml:lang="fo">Kexi dátustovns fílugrundað verkætlan</comment>
+ <comment xml:lang="fr">projet de base de données Kexi en mode fichier</comment>
+ <comment xml:lang="ga">tionscadal bunachair sonraí Kexi bunaithe ar chomhaid</comment>
+ <comment xml:lang="gl">proxecto baseado no ficheiro-base de datos Kexi</comment>
+ <comment xml:lang="he">מיזם מסד נתונים מבוסס-קובץ של Kexi</comment>
+ <comment xml:lang="hr">Kexi baza podataka datotekom temeljen projekt</comment>
+ <comment xml:lang="hu">Kexi adatbázisfájl-alapú projekt</comment>
+ <comment xml:lang="ia">Projecto de base de datos Kexi in modo file</comment>
+ <comment xml:lang="id">Projek berbasis berkas basis data Kexi</comment>
+ <comment xml:lang="it">Progetto su file di database Kexi</comment>
+ <comment xml:lang="ja">Kexi データベース ファイルベースプロジェクト</comment>
+ <comment xml:lang="kk">Файл негізінде жоба үшін Kexi дерекқоры</comment>
+ <comment xml:lang="ko">Kexi 데이터베이스 파일 기반 프로젝트</comment>
+ <comment xml:lang="lt">Kexi duomenų bazės failo tipo projektas</comment>
+ <comment xml:lang="lv">Kexi datubāzes datnes balstīts projekts</comment>
+ <comment xml:lang="nl">Kexi database bestandgebaseerd project</comment>
+ <comment xml:lang="oc">projècte de banca de donadas Kexi en mòde fichièr</comment>
+ <comment xml:lang="pl">Projekt bazy danych Kexi na podstawie plików</comment>
+ <comment xml:lang="pt">projeto Kexi em base de dados baseada em ficheiros</comment>
+ <comment xml:lang="pt_BR">Projeto de banco de dados baseado em arquivo do Kexi</comment>
+ <comment xml:lang="ro">Proiect bazat pe fișiere al bazei de date Kexi</comment>
+ <comment xml:lang="ru">Файловый проект базы данных Kexi</comment>
+ <comment xml:lang="sk">Projekt databázy Kexi s úložiskom typu súbor</comment>
+ <comment xml:lang="sl">Datoteka projekta podatkovne zbirke Kexi</comment>
+ <comment xml:lang="sr">пројекат Кексијеве базе података на основу датотеке</comment>
+ <comment xml:lang="sv">Kexi-databas för filbaserat projekt</comment>
+ <comment xml:lang="tr">Dosya temelli Kexi veritabanı projesi</comment>
+ <comment xml:lang="uk">проект файлової бази даних Kexi</comment>
+ <comment xml:lang="zh_CN">Kexi 基于文件的数据库项目</comment>
+ <comment xml:lang="zh_TW">Kexi 資料庫檔案基礎專案</comment>
+ <sub-class-of type="application/x-sqlite3"/>
+ <glob pattern="*.kexi"/>
+ <alias type="application/x-vnd.kde.kexi"/>
+ <alias type="application/x-kexiproject-sqlite"/>
+ </mime-type>
+ <mime-type type="application/x-kformula">
+ <comment>KFormula formula</comment>
+ <comment xml:lang="ar">صيغة KFormula</comment>
+ <comment xml:lang="be@latin">Formuła KFormula</comment>
+ <comment xml:lang="bg">Формула — KFormula</comment>
+ <comment xml:lang="ca">fórmula de KFormula</comment>
+ <comment xml:lang="cs">vzorec KFormula</comment>
+ <comment xml:lang="da">KFormula-formel</comment>
+ <comment xml:lang="de">KFormula-Formel</comment>
+ <comment xml:lang="el">Μαθηματικός τύπος KFormula</comment>
+ <comment xml:lang="en_GB">KFormula formula</comment>
+ <comment xml:lang="eo">KFormula-formulo</comment>
+ <comment xml:lang="es">fórmula de KFormula</comment>
+ <comment xml:lang="eu">KFormula formula</comment>
+ <comment xml:lang="fi">KFormula-kaava</comment>
+ <comment xml:lang="fo">KFormula frymil</comment>
+ <comment xml:lang="fr">formule KFormula</comment>
+ <comment xml:lang="ga">foirmle KFormula</comment>
+ <comment xml:lang="gl">fórmula de KFormula</comment>
+ <comment xml:lang="he">נוסחת KFormula</comment>
+ <comment xml:lang="hr">KFormula formula</comment>
+ <comment xml:lang="hu">KFormula-képlet</comment>
+ <comment xml:lang="ia">Formula KFormula</comment>
+ <comment xml:lang="id">Formula KFormula</comment>
+ <comment xml:lang="it">Formula KFormula</comment>
+ <comment xml:lang="ja">KFormula 計算式</comment>
+ <comment xml:lang="kk">KFormula формуласы</comment>
+ <comment xml:lang="ko">KFormula 수식</comment>
+ <comment xml:lang="lt">KFormula formulė</comment>
+ <comment xml:lang="lv">KFormula formula</comment>
+ <comment xml:lang="ms">Formula KFormula</comment>
+ <comment xml:lang="nb">KFormula-formel</comment>
+ <comment xml:lang="nl">KFormula-formule</comment>
+ <comment xml:lang="nn">KFormula-formel</comment>
+ <comment xml:lang="oc">formula KFormula</comment>
+ <comment xml:lang="pl">Formuła KFormula</comment>
+ <comment xml:lang="pt">fórmula KFormula</comment>
+ <comment xml:lang="pt_BR">Fórmula do KFormula</comment>
+ <comment xml:lang="ro">Formulă KFormula</comment>
+ <comment xml:lang="ru">Формула KFormula</comment>
+ <comment xml:lang="sk">Vzorec KFormula</comment>
+ <comment xml:lang="sl">Datoteka formule KFormula</comment>
+ <comment xml:lang="sq">Formulë KFormula</comment>
+ <comment xml:lang="sr">формула К-формуле</comment>
+ <comment xml:lang="sv">KFormula-formel</comment>
+ <comment xml:lang="tr">KFormula formülü</comment>
+ <comment xml:lang="uk">формула KFormula</comment>
+ <comment xml:lang="vi">Công thức KFormula</comment>
+ <comment xml:lang="zh_CN">KFormula 公式</comment>
+ <comment xml:lang="zh_TW">KFormula 公式</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="60">
+ <match value="\037\213" type="string" offset="0">
+ <match value="KOffice" type="string" offset="10">
+ <match value="application/x-kformula\004\006" type="string" offset="18"/>
+ </match>
+ </match>
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/x-kformula" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.kfo"/>
+ </mime-type>
+ <mime-type type="application/x-killustrator">
+ <comment>KIllustrator drawing</comment>
+ <comment xml:lang="ar">تصميم KIllustrator</comment>
+ <comment xml:lang="be@latin">Rysunak KIllustrator</comment>
+ <comment xml:lang="bg">Чертеж — KIllustrator</comment>
+ <comment xml:lang="ca">dibuix de KIllustrator</comment>
+ <comment xml:lang="cs">kresba KIllustrator</comment>
+ <comment xml:lang="da">KIllustrator-tegning</comment>
+ <comment xml:lang="de">KIllustrator-Zeichnung</comment>
+ <comment xml:lang="el">Σχέδιο KIllustrator</comment>
+ <comment xml:lang="en_GB">KIllustrator drawing</comment>
+ <comment xml:lang="eo">KIllustrator-grafikaĵo</comment>
+ <comment xml:lang="es">dibujo de KIllustrator</comment>
+ <comment xml:lang="eu">KIllustrator marrazkia</comment>
+ <comment xml:lang="fi">KIllustrator-piirros</comment>
+ <comment xml:lang="fo">KIllustrator tekning</comment>
+ <comment xml:lang="fr">dessin KIllustrator</comment>
+ <comment xml:lang="ga">líníocht KIllustrator</comment>
+ <comment xml:lang="gl">debuxo de KIllustrator</comment>
+ <comment xml:lang="he">ציור KIllustrator</comment>
+ <comment xml:lang="hr">KIllustrator crtež</comment>
+ <comment xml:lang="hu">KIllustrator-rajz</comment>
+ <comment xml:lang="ia">Designo KIllustrator</comment>
+ <comment xml:lang="id">Gambar KIllustrator</comment>
+ <comment xml:lang="it">Disegno KIllustrator</comment>
+ <comment xml:lang="ja">KIllustrator ドロー</comment>
+ <comment xml:lang="kk">KIllustrator суреті</comment>
+ <comment xml:lang="ko">KIllustrator 그림</comment>
+ <comment xml:lang="lt">KIllustrator piešinys</comment>
+ <comment xml:lang="lv">KIllustrator zīmējums</comment>
+ <comment xml:lang="ms">Lukisan KIllustrator</comment>
+ <comment xml:lang="nb">KIllustrator-tegning</comment>
+ <comment xml:lang="nl">KIllustrator-tekening</comment>
+ <comment xml:lang="nn">KIllustrator-teikning</comment>
+ <comment xml:lang="oc">dessenh KIllustrator</comment>
+ <comment xml:lang="pl">Rysunek KIllustrator</comment>
+ <comment xml:lang="pt">desenho KIllustrator</comment>
+ <comment xml:lang="pt_BR">Desenho do KIllustrator</comment>
+ <comment xml:lang="ro">Desen KIllustrator</comment>
+ <comment xml:lang="ru">Рисунок KIllustrator</comment>
+ <comment xml:lang="sk">Kresba KIllustrator</comment>
+ <comment xml:lang="sl">Datoteka risbe KIllustrator</comment>
+ <comment xml:lang="sq">Vizatim KIllustrator</comment>
+ <comment xml:lang="sr">цртеж К-илустратора</comment>
+ <comment xml:lang="sv">KIllustrator-teckning</comment>
+ <comment xml:lang="tr">KIllustrator çizimi</comment>
+ <comment xml:lang="uk">малюнок KIllustrator</comment>
+ <comment xml:lang="vi">Bản vẽ KIllustrator</comment>
+ <comment xml:lang="zh_CN">KIllustrator 绘图</comment>
+ <comment xml:lang="zh_TW">KIllustrator 繪圖</comment>
+ <generic-icon name="image-x-generic"/>
+ <magic priority="60">
+ <match value="\037\213" type="string" offset="0">
+ <match value="KOffice" type="string" offset="10">
+ <match value="application/x-killustrator\004\006" type="string" offset="18"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.kil"/>
+ </mime-type>
+ <mime-type type="application/x-kivio">
+ <comment>Kivio flowchart</comment>
+ <comment xml:lang="ar">قائمة تدفق Kivio</comment>
+ <comment xml:lang="be@latin">Blok-schiema Kivio</comment>
+ <comment xml:lang="bg">Диаграма — Kivio</comment>
+ <comment xml:lang="ca">diagrama de flux de Kivio</comment>
+ <comment xml:lang="cs">vývojový diagram Kivio</comment>
+ <comment xml:lang="da">Kiviorutediagram</comment>
+ <comment xml:lang="de">Kivio-Flussdiagramm</comment>
+ <comment xml:lang="el">Διάγραμμα ροής Kivio</comment>
+ <comment xml:lang="en_GB">Kivio flowchart</comment>
+ <comment xml:lang="eo">Kivo-fluskemo</comment>
+ <comment xml:lang="es">diagrama de flujo de Kivio</comment>
+ <comment xml:lang="eu">Kivio diagrama</comment>
+ <comment xml:lang="fi">Kivio-vuokaavio</comment>
+ <comment xml:lang="fo">Kivio leiðarit</comment>
+ <comment xml:lang="fr">diagramme de flux Kivio</comment>
+ <comment xml:lang="ga">sreabhchairt Kivio</comment>
+ <comment xml:lang="gl">gráfica de fluxo de Kivio</comment>
+ <comment xml:lang="he">תרשים זרימה של Kivio</comment>
+ <comment xml:lang="hr">Kivio dijagram toka</comment>
+ <comment xml:lang="hu">Kivio-folyamatábra</comment>
+ <comment xml:lang="ia">Diagramma de fluxo Kivio</comment>
+ <comment xml:lang="id">Bagan Kivio</comment>
+ <comment xml:lang="it">Diagramma di flusso Kivio</comment>
+ <comment xml:lang="ja">Kivio フローチャート</comment>
+ <comment xml:lang="kk">Kivio диаграммасы</comment>
+ <comment xml:lang="ko">Kivio 순서도</comment>
+ <comment xml:lang="lt">Kivio eigos diagrama</comment>
+ <comment xml:lang="lv">Kivio blokshēma</comment>
+ <comment xml:lang="ms">Cartalir Kivio</comment>
+ <comment xml:lang="nb">Kivio-flytdiagram</comment>
+ <comment xml:lang="nl">Kivio-stroomschema</comment>
+ <comment xml:lang="nn">Kivio-flytdiagram</comment>
+ <comment xml:lang="oc">diagrama de flux Kivio</comment>
+ <comment xml:lang="pl">Diagram przepływów Kivio</comment>
+ <comment xml:lang="pt">gráfico de fluxo Kivio</comment>
+ <comment xml:lang="pt_BR">Fluxograma do Kivio</comment>
+ <comment xml:lang="ro">Diagramă Kivio</comment>
+ <comment xml:lang="ru">Диаграмма Kivio</comment>
+ <comment xml:lang="sk">Vývojový diagram Kivio</comment>
+ <comment xml:lang="sl">Datoteka grafikona Kivio</comment>
+ <comment xml:lang="sq">Diagramë fluksi Kivio</comment>
+ <comment xml:lang="sr">Кивиов дијаграм тока</comment>
+ <comment xml:lang="sv">Kivio-flödesschema</comment>
+ <comment xml:lang="tr">Kivio akış şeması</comment>
+ <comment xml:lang="uk">блок-схема Kivio</comment>
+ <comment xml:lang="vi">Lược đồ Kivio</comment>
+ <comment xml:lang="zh_CN">Kivio 流程图</comment>
+ <comment xml:lang="zh_TW">Kivio 圖表</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="60">
+ <match value="\037\213" type="string" offset="0">
+ <match value="KOffice" type="string" offset="10">
+ <match value="application/x-kivio\004\006" type="string" offset="18"/>
+ </match>
+ </match>
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/x-kivio" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.flw"/>
+ </mime-type>
+ <mime-type type="application/x-kontour">
+ <comment>Kontour drawing</comment>
+ <comment xml:lang="ar">تصميم Kontour</comment>
+ <comment xml:lang="be@latin">Rysunak Kontour</comment>
+ <comment xml:lang="bg">Чертеж — Kontour</comment>
+ <comment xml:lang="ca">dibuix de Kontour</comment>
+ <comment xml:lang="cs">kresba Kontour</comment>
+ <comment xml:lang="da">Kontourtegning</comment>
+ <comment xml:lang="de">Kontour-Zeichnung</comment>
+ <comment xml:lang="el">Σχέδιο Kontour</comment>
+ <comment xml:lang="en_GB">Kontour drawing</comment>
+ <comment xml:lang="eo">Kontour-grafikaĵo</comment>
+ <comment xml:lang="es">dibujo de Kontour</comment>
+ <comment xml:lang="eu">Kontour marrazkia</comment>
+ <comment xml:lang="fi">Kontour-piirros</comment>
+ <comment xml:lang="fo">Kontour tekning</comment>
+ <comment xml:lang="fr">dessin Kontour</comment>
+ <comment xml:lang="ga">líníocht Kontour</comment>
+ <comment xml:lang="gl">debuxo de Kontour</comment>
+ <comment xml:lang="he">ציור Kontour</comment>
+ <comment xml:lang="hr">Kontour crtež</comment>
+ <comment xml:lang="hu">Kontour-rajz</comment>
+ <comment xml:lang="ia">Designo Kontour</comment>
+ <comment xml:lang="id">Gambar Kontour</comment>
+ <comment xml:lang="it">Disegno Kontour</comment>
+ <comment xml:lang="ja">Kontour ドロー</comment>
+ <comment xml:lang="kk">Kontour суреті</comment>
+ <comment xml:lang="ko">Kontour 그림</comment>
+ <comment xml:lang="lt">Kontour piešinys</comment>
+ <comment xml:lang="lv">Kontour zīmējums</comment>
+ <comment xml:lang="ms">Lukisan Kontour</comment>
+ <comment xml:lang="nb">Kontour-tegning</comment>
+ <comment xml:lang="nl">Kontour-tekening</comment>
+ <comment xml:lang="nn">Kontour-teikning</comment>
+ <comment xml:lang="oc">dessenh Kontour</comment>
+ <comment xml:lang="pl">Rysunek Kontour</comment>
+ <comment xml:lang="pt">desenho Kontour</comment>
+ <comment xml:lang="pt_BR">Desenho do Kontour</comment>
+ <comment xml:lang="ro">Desen Kontour</comment>
+ <comment xml:lang="ru">Рисунок Kontour</comment>
+ <comment xml:lang="sk">Kresba Kontour</comment>
+ <comment xml:lang="sl">Datoteka risbe Kontour</comment>
+ <comment xml:lang="sq">Vizatim Kontour</comment>
+ <comment xml:lang="sr">Контуров цртеж</comment>
+ <comment xml:lang="sv">Kontour-teckning</comment>
+ <comment xml:lang="tr">Kontour çizimi</comment>
+ <comment xml:lang="uk">малюнок Kontour</comment>
+ <comment xml:lang="vi">Bản vẽ Kontour</comment>
+ <comment xml:lang="zh_CN">Kontour 绘图</comment>
+ <comment xml:lang="zh_TW">Kontour 繪圖</comment>
+ <generic-icon name="image-x-generic"/>
+ <magic priority="60">
+ <match value="\037\213" type="string" offset="0">
+ <match value="KOffice" type="string" offset="10">
+ <match value="application/x-kontour\004\006" type="string" offset="18"/>
+ </match>
+ </match>
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/x-kontour" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.kon"/>
+ </mime-type>
+ <mime-type type="application/x-kpovmodeler">
+ <comment>KPovModeler scene</comment>
+ <comment xml:lang="ar">مشهد KPovModeler</comment>
+ <comment xml:lang="be@latin">Scena KPovModeler</comment>
+ <comment xml:lang="bg">Сцена — KPovModeler</comment>
+ <comment xml:lang="ca">escena de KPovModeler</comment>
+ <comment xml:lang="cs">scéna KPovModeler</comment>
+ <comment xml:lang="da">KPovModeler-scene</comment>
+ <comment xml:lang="de">KPovModeler-Szene</comment>
+ <comment xml:lang="el">Σκηνή KPovModeler</comment>
+ <comment xml:lang="en_GB">KPovModeler scene</comment>
+ <comment xml:lang="eo">KPovModeler-sceno</comment>
+ <comment xml:lang="es">escena de KPovModeler</comment>
+ <comment xml:lang="eu">KPovModeler eszena</comment>
+ <comment xml:lang="fi">KPovModeler-näkymä</comment>
+ <comment xml:lang="fo">KPovModeler leikmynd</comment>
+ <comment xml:lang="fr">scène KPovModeler</comment>
+ <comment xml:lang="ga">radharc KPovModeler</comment>
+ <comment xml:lang="gl">escena de KPovModeler</comment>
+ <comment xml:lang="he">סצנת KPovModeler</comment>
+ <comment xml:lang="hr">KPovModeler scena</comment>
+ <comment xml:lang="hu">KPovModeler-jelenet</comment>
+ <comment xml:lang="ia">Scena KPovModeler</comment>
+ <comment xml:lang="id">Scene KPovModeler</comment>
+ <comment xml:lang="it">Scena KPovModeler</comment>
+ <comment xml:lang="ja">KPovModeler シーン</comment>
+ <comment xml:lang="kk">KPovModeler сахнасы</comment>
+ <comment xml:lang="ko">KPovModeler 장면</comment>
+ <comment xml:lang="lt">KPovModeler scena</comment>
+ <comment xml:lang="lv">KPovModeler aina</comment>
+ <comment xml:lang="ms">Babak KPovModeler</comment>
+ <comment xml:lang="nb">KPovModeler-scene</comment>
+ <comment xml:lang="nl">KPovModeler-scène</comment>
+ <comment xml:lang="nn">KPovModeler-scene</comment>
+ <comment xml:lang="oc">scène KPovModeler</comment>
+ <comment xml:lang="pl">Scena KPovModeler</comment>
+ <comment xml:lang="pt">cenário KPovModeler</comment>
+ <comment xml:lang="pt_BR">Cena do KPovModeler</comment>
+ <comment xml:lang="ro">Scenă KPovModeler</comment>
+ <comment xml:lang="ru">Сцена KPovModeler</comment>
+ <comment xml:lang="sk">Scéna KPovModeler</comment>
+ <comment xml:lang="sl">Datoteka scene KPovModeler</comment>
+ <comment xml:lang="sq">Skenë KPovModeler</comment>
+ <comment xml:lang="sr">сцена КПов Моделера</comment>
+ <comment xml:lang="sv">KPovModeler-scen</comment>
+ <comment xml:lang="tr">KPovModeler sahnesi</comment>
+ <comment xml:lang="uk">сцена KPovModeler</comment>
+ <comment xml:lang="vi">Cảnh KPovModeler</comment>
+ <comment xml:lang="zh_CN">KPovModeler 场景</comment>
+ <comment xml:lang="zh_TW">KPovModeler 場景</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.kpm"/>
+ </mime-type>
+ <mime-type type="application/x-kpresenter">
+ <comment>KPresenter presentation</comment>
+ <comment xml:lang="ar">عرض تقديمي KPresenter</comment>
+ <comment xml:lang="be@latin">Prezentacyja KPresenter</comment>
+ <comment xml:lang="bg">Презентация — KPresenter</comment>
+ <comment xml:lang="ca">presentació de KPresenter</comment>
+ <comment xml:lang="cs">prezentace KPresenter</comment>
+ <comment xml:lang="da">KPresenter-præsentation</comment>
+ <comment xml:lang="de">KPresenter-Präsentation</comment>
+ <comment xml:lang="el">Παρουσίαση KPresenter</comment>
+ <comment xml:lang="en_GB">KPresenter presentation</comment>
+ <comment xml:lang="eo">KPresenter-prezentaĵo</comment>
+ <comment xml:lang="es">presentación de KPresenter</comment>
+ <comment xml:lang="eu">Kpresenter aurkezpena</comment>
+ <comment xml:lang="fi">KPresenter-esitys</comment>
+ <comment xml:lang="fo">KPresenter framløga</comment>
+ <comment xml:lang="fr">présentation KPresenter</comment>
+ <comment xml:lang="ga">láithreoireacht KPresenter</comment>
+ <comment xml:lang="gl">presentación de KPresenter</comment>
+ <comment xml:lang="he">מצגת KPresenter</comment>
+ <comment xml:lang="hr">KPresenter prezentacija</comment>
+ <comment xml:lang="hu">KPresenter-bemutató</comment>
+ <comment xml:lang="ia">Presentation KPresenter</comment>
+ <comment xml:lang="id">Presentasi KPresenter</comment>
+ <comment xml:lang="it">Presentazione KPresenter</comment>
+ <comment xml:lang="ja">KPresenter プレゼンテーション</comment>
+ <comment xml:lang="kk">KPresenter презентациясы</comment>
+ <comment xml:lang="ko">KPresenter 프레젠테이션</comment>
+ <comment xml:lang="lt">KPresenter pateiktis</comment>
+ <comment xml:lang="lv">KPresenter prezentācija</comment>
+ <comment xml:lang="ms">Persembahan Kpresenter</comment>
+ <comment xml:lang="nb">KPresenter-presentasjon</comment>
+ <comment xml:lang="nl">KPresenter-presentatie</comment>
+ <comment xml:lang="nn">KPresenter-presentasjon</comment>
+ <comment xml:lang="oc">presentacion KPresenter</comment>
+ <comment xml:lang="pl">Prezentacja KPresenter</comment>
+ <comment xml:lang="pt">apresentação KPresenter</comment>
+ <comment xml:lang="pt_BR">Apresentação do KPresenter</comment>
+ <comment xml:lang="ro">Prezentare KPresenter</comment>
+ <comment xml:lang="ru">Презентация KPresenter</comment>
+ <comment xml:lang="sk">Prezentácia KPresenter</comment>
+ <comment xml:lang="sl">Predstavitev KPresenter</comment>
+ <comment xml:lang="sq">Prezantim i KPresenter</comment>
+ <comment xml:lang="sr">презентација К-представљача</comment>
+ <comment xml:lang="sv">KPresenter-presentation</comment>
+ <comment xml:lang="tr">KPresenter sunum dosyası</comment>
+ <comment xml:lang="uk">презентація KPresenter</comment>
+ <comment xml:lang="vi">Trình diễn KPresenter</comment>
+ <comment xml:lang="zh_CN">KPresenter 演示文稿</comment>
+ <comment xml:lang="zh_TW">KPresenter 簡報檔</comment>
+ <generic-icon name="x-office-presentation"/>
+ <magic priority="60">
+ <match value="\037\213" type="string" offset="0">
+ <match value="KOffice" type="string" offset="10">
+ <match value="application/x-kpresenter\004\006" type="string" offset="18"/>
+ </match>
+ </match>
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/x-kpresenter" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.kpr"/>
+ <glob pattern="*.kpt"/>
+ </mime-type>
+ <mime-type type="application/x-krita">
+ <comment>Krita document</comment>
+ <comment xml:lang="ar">مستند Krita</comment>
+ <comment xml:lang="ast">Documentu de Krita</comment>
+ <comment xml:lang="be@latin">Dakument Krita</comment>
+ <comment xml:lang="bg">Документ — Krita</comment>
+ <comment xml:lang="ca">document Krita</comment>
+ <comment xml:lang="cs">dokument Krita</comment>
+ <comment xml:lang="da">Kritadokument</comment>
+ <comment xml:lang="de">Krita-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Krita</comment>
+ <comment xml:lang="en_GB">Krita document</comment>
+ <comment xml:lang="eo">Krita-dokumento</comment>
+ <comment xml:lang="es">documento de Krita</comment>
+ <comment xml:lang="eu">Krita dokumentua</comment>
+ <comment xml:lang="fi">Krita-asiakirja</comment>
+ <comment xml:lang="fo">Krita skjal</comment>
+ <comment xml:lang="fr">document Krita</comment>
+ <comment xml:lang="ga">cáipéis Krita</comment>
+ <comment xml:lang="gl">documento de Krita</comment>
+ <comment xml:lang="he">מסמך Krita</comment>
+ <comment xml:lang="hr">Krita dokument</comment>
+ <comment xml:lang="hu">Krita-dokumentum</comment>
+ <comment xml:lang="ia">Documento Krita</comment>
+ <comment xml:lang="id">Dokumen Krita</comment>
+ <comment xml:lang="it">Documento Krita</comment>
+ <comment xml:lang="ja">Krita ドキュメント</comment>
+ <comment xml:lang="kk">Krita құжаты</comment>
+ <comment xml:lang="ko">Krita 문서</comment>
+ <comment xml:lang="lt">Krita dokumentas</comment>
+ <comment xml:lang="lv">Krita dokuments</comment>
+ <comment xml:lang="ms">Dokumen Krita</comment>
+ <comment xml:lang="nb">Krita-dokument</comment>
+ <comment xml:lang="nl">Krita-document</comment>
+ <comment xml:lang="nn">Krita-dokument</comment>
+ <comment xml:lang="oc">document Krita</comment>
+ <comment xml:lang="pl">Dokument Krita</comment>
+ <comment xml:lang="pt">documento Krita</comment>
+ <comment xml:lang="pt_BR">Documento do Krita</comment>
+ <comment xml:lang="ro">Document Krita</comment>
+ <comment xml:lang="ru">Документ Krita</comment>
+ <comment xml:lang="sk">Dokument Krita</comment>
+ <comment xml:lang="sl">Dokument Krita</comment>
+ <comment xml:lang="sq">Dokument Krita</comment>
+ <comment xml:lang="sr">документ Крите</comment>
+ <comment xml:lang="sv">Krita-dokument</comment>
+ <comment xml:lang="tr">Krita belgesi</comment>
+ <comment xml:lang="uk">документ Krita</comment>
+ <comment xml:lang="vi">Tài liệu Krita</comment>
+ <comment xml:lang="zh_CN">Krita 文档</comment>
+ <comment xml:lang="zh_TW">Krita 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="60">
+ <match value="\037\213" type="string" offset="0">
+ <match value="KOffice" type="string" offset="10">
+ <match value="application/x-krita\004\006" type="string" offset="18"/>
+ </match>
+ </match>
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/x-krita" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.kra"/>
+ </mime-type>
+ <mime-type type="application/x-kspread">
+ <comment>KSpread spreadsheet</comment>
+ <comment xml:lang="ar">جدول KSpread</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš KSpread</comment>
+ <comment xml:lang="bg">Таблица — KSpread</comment>
+ <comment xml:lang="ca">full de càlcul de KSpread</comment>
+ <comment xml:lang="cs">sešit KSpread</comment>
+ <comment xml:lang="da">KSpread-regneark</comment>
+ <comment xml:lang="de">KSpread-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο KSpread</comment>
+ <comment xml:lang="en_GB">KSpread spreadsheet</comment>
+ <comment xml:lang="eo">KSpread-kalkultabelo</comment>
+ <comment xml:lang="es">hoja de cálculo de KSpread</comment>
+ <comment xml:lang="eu">KSpread kalkulu-orria</comment>
+ <comment xml:lang="fi">KSpread-taulukko</comment>
+ <comment xml:lang="fo">KSpread rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul KSpread</comment>
+ <comment xml:lang="ga">scarbhileog KSpread</comment>
+ <comment xml:lang="gl">folla de cálculo de KSpread</comment>
+ <comment xml:lang="he">גליון נתונים של Kspread</comment>
+ <comment xml:lang="hr">KSpread proračunska tablica</comment>
+ <comment xml:lang="hu">KSpread-munkafüzet</comment>
+ <comment xml:lang="ia">Folio de calculo KSpread</comment>
+ <comment xml:lang="id">Lembar sebar KSpread</comment>
+ <comment xml:lang="it">Foglio di calcolo KSpread</comment>
+ <comment xml:lang="ja">KSpread スプレッドシート</comment>
+ <comment xml:lang="kk">KSpread электрондық кестесі</comment>
+ <comment xml:lang="ko">KSpread 스프레드시트</comment>
+ <comment xml:lang="lt">KSpread skaičialentė</comment>
+ <comment xml:lang="lv">KSpread izklājlapa</comment>
+ <comment xml:lang="ms">Hamparan KSpread</comment>
+ <comment xml:lang="nb">KSpread-regneark</comment>
+ <comment xml:lang="nl">KSpread-rekenblad</comment>
+ <comment xml:lang="nn">KSpread-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul KSpread</comment>
+ <comment xml:lang="pl">Arkusz KSpread</comment>
+ <comment xml:lang="pt">folha de cálculo KSpread</comment>
+ <comment xml:lang="pt_BR">Planilha do KSpread</comment>
+ <comment xml:lang="ro">Foaie de calcul KSpread</comment>
+ <comment xml:lang="ru">Электронная таблица KSpread</comment>
+ <comment xml:lang="sk">Zošit KSpread</comment>
+ <comment xml:lang="sl">Preglednica KSpread</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh KSpread</comment>
+ <comment xml:lang="sr">табела К-табеле</comment>
+ <comment xml:lang="sv">KSpread-kalkylblad</comment>
+ <comment xml:lang="tr">KSpread çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця KSpread</comment>
+ <comment xml:lang="vi">Bảng tính KSpread</comment>
+ <comment xml:lang="zh_CN">KSpread 电子表格</comment>
+ <comment xml:lang="zh_TW">KSpread 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="60">
+ <match value="\037\213" type="string" offset="0">
+ <match value="KOffice" type="string" offset="10">
+ <match value="application/x-kspread\004\006" type="string" offset="18"/>
+ </match>
+ </match>
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/x-kspread" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.ksp"/>
+ </mime-type>
+ <mime-type type="application/x-kspread-crypt">
+ <comment>KSpread spreadsheet (encrypted)</comment>
+ <comment xml:lang="ar">جدول KSpread (مشفر)</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš KSpread (zašyfravany)</comment>
+ <comment xml:lang="bg">Таблица — KSpread, шифрирана</comment>
+ <comment xml:lang="ca">full de càlcul de KSpread (xifrat)</comment>
+ <comment xml:lang="cs">sešit KSpread (šifrovaný)</comment>
+ <comment xml:lang="da">KSpread-regneark (krypteret)</comment>
+ <comment xml:lang="de">KSpread-Tabelle (verschlüsselt)</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο KSpread (κρυπτογραφημένο)</comment>
+ <comment xml:lang="en_GB">KSpread spreadsheet (encrypted)</comment>
+ <comment xml:lang="eo">KSpread-kalkultabelo (ĉifrita)</comment>
+ <comment xml:lang="es">hoja de cálculo de KSpread (cifrada)</comment>
+ <comment xml:lang="eu">KSpread kalkulu-orria (enkriptatua)</comment>
+ <comment xml:lang="fi">KSpread-taulukko (salattu)</comment>
+ <comment xml:lang="fo">KSpread rokniark (bronglað)</comment>
+ <comment xml:lang="fr">feuille de calcul KSpread (chiffrée)</comment>
+ <comment xml:lang="ga">scarbhileog KSpread (criptithe)</comment>
+ <comment xml:lang="gl">folla de cálculo de KSpread (cifrada)</comment>
+ <comment xml:lang="he">גליון נתונים של KSpread (מוצפן)</comment>
+ <comment xml:lang="hr">KSpread proračunska tablica (šifrirana)</comment>
+ <comment xml:lang="hu">KSpread-munkafüzet (titkosított)</comment>
+ <comment xml:lang="ia">Folio de calculo KSpread (cryptate)</comment>
+ <comment xml:lang="id">Lembar sebar KSpread (terenkripsi)</comment>
+ <comment xml:lang="it">Foglio di calcolo KSpread (cifrato)</comment>
+ <comment xml:lang="ja">KSpread (暗号化) スプレッドシート</comment>
+ <comment xml:lang="kk">KSpread электрондық кестесі (шифрленген)</comment>
+ <comment xml:lang="ko">암호화된 KSpread 스프레드시트</comment>
+ <comment xml:lang="lt">KSpread skaičialentė (užšifruota)</comment>
+ <comment xml:lang="lv">KSpread izklājlapa (šifrēta)</comment>
+ <comment xml:lang="ms">Hampatan KSpread (terenkripsi)</comment>
+ <comment xml:lang="nb">KSpread-regneark (kryptert)</comment>
+ <comment xml:lang="nl">KSpread-rekenblad (versleuteld)</comment>
+ <comment xml:lang="nn">Kryptert KSpread-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul KSpread (chifrada)</comment>
+ <comment xml:lang="pl">Arkusz KSpread (zaszyfrowany)</comment>
+ <comment xml:lang="pt">folha de cálculo KSpread (encriptada)</comment>
+ <comment xml:lang="pt_BR">Planilha do KSpread (criptografada)</comment>
+ <comment xml:lang="ro">Foaie de calcul KSpread (criptat)</comment>
+ <comment xml:lang="ru">Электронная таблица KSpread (зашифрованная)</comment>
+ <comment xml:lang="sk">Zošit KSpread (šifrovaný)</comment>
+ <comment xml:lang="sl">Preglednica KSpread (šifrirana)</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh KSpread (e kriptuar)</comment>
+ <comment xml:lang="sr">табела К-табеле (шифрована)</comment>
+ <comment xml:lang="sv">KSpread-kalkylblad (krypterat)</comment>
+ <comment xml:lang="tr">KSpread çalışma sayfası (şifreli)</comment>
+ <comment xml:lang="uk">ел. таблиця KSpread (зашифрована)</comment>
+ <comment xml:lang="vi">Bảng tính KSpread (đã mật mã)</comment>
+ <comment xml:lang="zh_CN">KSpread 电子表格(加密)</comment>
+ <comment xml:lang="zh_TW">KSpread 試算表 (已加密)</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="50">
+ <match value="0x0d1a2702" type="big32" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-ksysv-package">
+ <comment>KSysV init package</comment>
+ <comment xml:lang="ar">حزمة KSysV init</comment>
+ <comment xml:lang="be@latin">Inicyjalny pakunak KSysV</comment>
+ <comment xml:lang="bg">Пакет — KSysV init</comment>
+ <comment xml:lang="ca">paquet d'inici KSysV</comment>
+ <comment xml:lang="cs">balíček init KSysV</comment>
+ <comment xml:lang="da">KSsV init-pakke</comment>
+ <comment xml:lang="de">KSysV-Init-Paket</comment>
+ <comment xml:lang="el">Αρχικό πακέτο KSysV</comment>
+ <comment xml:lang="en_GB">KSysV init package</comment>
+ <comment xml:lang="es">paquete de configuración de init para KSysV</comment>
+ <comment xml:lang="eu">KSysV hasieratzeko paketea</comment>
+ <comment xml:lang="fi">KSysV init -paketti</comment>
+ <comment xml:lang="fo">KSysV init pakki</comment>
+ <comment xml:lang="fr">paquet d'initialisation KSysV</comment>
+ <comment xml:lang="ga">pacáiste túsaithe KSysV</comment>
+ <comment xml:lang="gl">paquete de KsysV init</comment>
+ <comment xml:lang="he">חבילת KSysV init</comment>
+ <comment xml:lang="hr">KSysV init paket</comment>
+ <comment xml:lang="hu">KSysV init csomag</comment>
+ <comment xml:lang="ia">Pacchetto de initialisation KSysV</comment>
+ <comment xml:lang="id">Paket init KSysV</comment>
+ <comment xml:lang="it">Pacchetto init KSysV</comment>
+ <comment xml:lang="ja">KSysV init パッケージ</comment>
+ <comment xml:lang="kk">KSysV инициализация дестесі</comment>
+ <comment xml:lang="ko">KSysV init 패키지</comment>
+ <comment xml:lang="lt">KSysV init paketas</comment>
+ <comment xml:lang="lv">KSysV inicializācijas pakotne</comment>
+ <comment xml:lang="nb">KSysV init-pakke</comment>
+ <comment xml:lang="nl">KSysV-init-pakket</comment>
+ <comment xml:lang="nn">KSysV init-pakke</comment>
+ <comment xml:lang="oc">paquet d'initializacion KSysV</comment>
+ <comment xml:lang="pl">Pakiet KSysV init</comment>
+ <comment xml:lang="pt">pacote inicial KSysV</comment>
+ <comment xml:lang="pt_BR">Pacote init do KSysV</comment>
+ <comment xml:lang="ro">Pachet KSysV init</comment>
+ <comment xml:lang="ru">Пакет инициализации KSysV</comment>
+ <comment xml:lang="sk">Balíček KSysV init</comment>
+ <comment xml:lang="sl">Datoteka paketa KSysV init</comment>
+ <comment xml:lang="sq">Paketë init KSysV</comment>
+ <comment xml:lang="sr">КСисВ побудни пакет</comment>
+ <comment xml:lang="sv">KSysV init-paket</comment>
+ <comment xml:lang="tr">KSysV init paketi</comment>
+ <comment xml:lang="uk">пакунок KSysV init</comment>
+ <comment xml:lang="vi">Gói sở khởi KSysV</comment>
+ <comment xml:lang="zh_CN">KSysV init 软件包</comment>
+ <comment xml:lang="zh_TW">KSysV init 軟體包</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="KSysV" type="string" offset="4">
+ <match value="1" type="byte" offset="15"/>
+ </match>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-kugar">
+ <comment>Kugar document</comment>
+ <comment xml:lang="ar">مستند Kugar</comment>
+ <comment xml:lang="ast">Documentu de Kugar</comment>
+ <comment xml:lang="be@latin">Dakument Kugar</comment>
+ <comment xml:lang="bg">Документ — Kugar</comment>
+ <comment xml:lang="ca">document Kugar</comment>
+ <comment xml:lang="cs">dokument Kugar</comment>
+ <comment xml:lang="da">Kugardokument</comment>
+ <comment xml:lang="de">Kugar-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Kugar</comment>
+ <comment xml:lang="en_GB">Kugar document</comment>
+ <comment xml:lang="eo">Kugar-dokumento</comment>
+ <comment xml:lang="es">documento de Kugar</comment>
+ <comment xml:lang="eu">Kugar dokumentua</comment>
+ <comment xml:lang="fi">Kugar-asiakirja</comment>
+ <comment xml:lang="fo">Kugar skjal</comment>
+ <comment xml:lang="fr">document Kugar</comment>
+ <comment xml:lang="ga">cáipéis Kugar</comment>
+ <comment xml:lang="gl">documento de Kugar</comment>
+ <comment xml:lang="he">מסמך Kugar</comment>
+ <comment xml:lang="hr">Kugar dokument</comment>
+ <comment xml:lang="hu">Kugar-dokumentum</comment>
+ <comment xml:lang="ia">Documento Kugar</comment>
+ <comment xml:lang="id">Dokumen Kugar</comment>
+ <comment xml:lang="it">Documento Kugar</comment>
+ <comment xml:lang="ja">Kugar ドキュメント</comment>
+ <comment xml:lang="kk">Kugar құжаты</comment>
+ <comment xml:lang="ko">Kugar 문서</comment>
+ <comment xml:lang="lt">Kugar dokumentas</comment>
+ <comment xml:lang="lv">Kugar dokuments</comment>
+ <comment xml:lang="ms">Dokumen Kugar</comment>
+ <comment xml:lang="nb">Kugar-dokument</comment>
+ <comment xml:lang="nl">Kugar-document</comment>
+ <comment xml:lang="nn">Kugar-dokument</comment>
+ <comment xml:lang="oc">document Kugar</comment>
+ <comment xml:lang="pl">Dokument Kuguar</comment>
+ <comment xml:lang="pt">documento Kugar</comment>
+ <comment xml:lang="pt_BR">Documento do Kugar</comment>
+ <comment xml:lang="ro">Document Kugar</comment>
+ <comment xml:lang="ru">Документ Kugar</comment>
+ <comment xml:lang="sk">Dokument Kugar</comment>
+ <comment xml:lang="sl">Dokument Kugar</comment>
+ <comment xml:lang="sq">Dokument Kugar</comment>
+ <comment xml:lang="sr">документ Кугара</comment>
+ <comment xml:lang="sv">Kugar-dokument</comment>
+ <comment xml:lang="tr">Kugar belgesi</comment>
+ <comment xml:lang="uk">документ Kugar</comment>
+ <comment xml:lang="vi">Tài liệu Kugar</comment>
+ <comment xml:lang="zh_CN">Kugar 文档</comment>
+ <comment xml:lang="zh_TW">Kugar 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.kud"/>
+ </mime-type>
+ <mime-type type="application/x-kword">
+ <comment>KWord document</comment>
+ <comment xml:lang="ar">مستند KWord</comment>
+ <comment xml:lang="ast">Documentu de Kword</comment>
+ <comment xml:lang="be@latin">Dakument KWord</comment>
+ <comment xml:lang="bg">Документ — KWord</comment>
+ <comment xml:lang="ca">document KWord</comment>
+ <comment xml:lang="cs">dokument KWord</comment>
+ <comment xml:lang="cy">Dogfen KWord</comment>
+ <comment xml:lang="da">KWord-dokument</comment>
+ <comment xml:lang="de">KWord-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο KWord</comment>
+ <comment xml:lang="en_GB">KWord document</comment>
+ <comment xml:lang="eo">KWord-dokumento</comment>
+ <comment xml:lang="es">documento de KWord</comment>
+ <comment xml:lang="eu">KWord dokumentua</comment>
+ <comment xml:lang="fi">KWord-asiakirja</comment>
+ <comment xml:lang="fo">KWord skjal</comment>
+ <comment xml:lang="fr">document KWord</comment>
+ <comment xml:lang="ga">cáipéis KWord</comment>
+ <comment xml:lang="gl">documento de KWord</comment>
+ <comment xml:lang="he">מסמך KWord</comment>
+ <comment xml:lang="hr">KWord dokument</comment>
+ <comment xml:lang="hu">KWord-dokumentum</comment>
+ <comment xml:lang="ia">Documento KWord</comment>
+ <comment xml:lang="id">Dokumen KWord</comment>
+ <comment xml:lang="it">Documento KWord</comment>
+ <comment xml:lang="ja">KWord ドキュメント</comment>
+ <comment xml:lang="kk">KWord құжаты</comment>
+ <comment xml:lang="ko">KWord 문서</comment>
+ <comment xml:lang="lt">KWord dokumentas</comment>
+ <comment xml:lang="lv">KWord dokuments</comment>
+ <comment xml:lang="ms">Dokumen KWord</comment>
+ <comment xml:lang="nb">KWord-dokument</comment>
+ <comment xml:lang="nl">KWord-document</comment>
+ <comment xml:lang="nn">KWord-dokument</comment>
+ <comment xml:lang="oc">document KWord</comment>
+ <comment xml:lang="pl">Dokument KWord</comment>
+ <comment xml:lang="pt">documento KWord</comment>
+ <comment xml:lang="pt_BR">Documento do KWord</comment>
+ <comment xml:lang="ro">Document KWord</comment>
+ <comment xml:lang="ru">Документ KWord</comment>
+ <comment xml:lang="sk">Dokument KWord</comment>
+ <comment xml:lang="sl">Dokument KWord</comment>
+ <comment xml:lang="sq">Dokument KWord</comment>
+ <comment xml:lang="sr">документ К-речи</comment>
+ <comment xml:lang="sv">KWord-dokument</comment>
+ <comment xml:lang="tr">KWord belgesi</comment>
+ <comment xml:lang="uk">документ KWord</comment>
+ <comment xml:lang="vi">Tài liệu KWord</comment>
+ <comment xml:lang="zh_CN">KWord 文档</comment>
+ <comment xml:lang="zh_TW">KWord 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="60">
+ <match value="\037\213" type="string" offset="0">
+ <match value="KOffice" type="string" offset="10">
+ <match value="application/x-kword\004\006" type="string" offset="18"/>
+ </match>
+ </match>
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="application/x-kword" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.kwd"/>
+ <glob pattern="*.kwt"/>
+ </mime-type>
+ <mime-type type="application/x-kword-crypt">
+ <comment>KWord document (encrypted)</comment>
+ <comment xml:lang="ar">مستند KWord (مشفر)</comment>
+ <comment xml:lang="ast">Documentu de Kword (cifráu)</comment>
+ <comment xml:lang="be@latin">Dakument KWord (zašyfravany)</comment>
+ <comment xml:lang="bg">Документ — KWord, шифриран</comment>
+ <comment xml:lang="ca">document KWord (xifrat)</comment>
+ <comment xml:lang="cs">dokument KWord (šifrovaný)</comment>
+ <comment xml:lang="da">KWord-dokument (krypteret)</comment>
+ <comment xml:lang="de">KWord-Dokument (verschlüsselt)</comment>
+ <comment xml:lang="el">Έγγραφο KWord (κρυπτογραφημένο)</comment>
+ <comment xml:lang="en_GB">KWord document (encrypted)</comment>
+ <comment xml:lang="eo">KWord-dokumento (ĉifrita)</comment>
+ <comment xml:lang="es">documento de KWord (cifrado)</comment>
+ <comment xml:lang="eu">KWord dokumentua (enkriptatua)</comment>
+ <comment xml:lang="fi">KWord-asiakirja (salattu)</comment>
+ <comment xml:lang="fo">KWord skjal (bronglað)</comment>
+ <comment xml:lang="fr">document KWord (chiffré)</comment>
+ <comment xml:lang="ga">cáipéis KWord (criptithe)</comment>
+ <comment xml:lang="gl">documento de KWord (cifrado)</comment>
+ <comment xml:lang="he">מסמך KWord (מוצפן)</comment>
+ <comment xml:lang="hr">KWord dokument (šifriran)</comment>
+ <comment xml:lang="hu">KWord-dokumentum (titkosított)</comment>
+ <comment xml:lang="ia">Documento KWord (cryptate)</comment>
+ <comment xml:lang="id">Dokumen KWord (terenkripsi)</comment>
+ <comment xml:lang="it">Documento KWord (cifrato)</comment>
+ <comment xml:lang="ja">KWord (暗号化) ドキュメント</comment>
+ <comment xml:lang="kk">KWord құжаты (шифрленген)</comment>
+ <comment xml:lang="ko">암호화된 KWord 문서</comment>
+ <comment xml:lang="lt">KWord dokumentas (užšifruotas)</comment>
+ <comment xml:lang="lv">KWord dokuments (šifrēts)</comment>
+ <comment xml:lang="ms">Dokumen Kword (terenkripsi)</comment>
+ <comment xml:lang="nb">KWord-dokument (kryptert)</comment>
+ <comment xml:lang="nl">KWord-document (versleuteld)</comment>
+ <comment xml:lang="nn">Kryptert KWord-dokument</comment>
+ <comment xml:lang="oc">document KWord (chifrat)</comment>
+ <comment xml:lang="pl">Dokument KWord (zaszyfrowany)</comment>
+ <comment xml:lang="pt">documento KWord (encriptado)</comment>
+ <comment xml:lang="pt_BR">Documento do KWord (criptografado)</comment>
+ <comment xml:lang="ro">Document KWord (criptat)</comment>
+ <comment xml:lang="ru">Документ KWord (зашифрованный)</comment>
+ <comment xml:lang="sk">Dokument KWord (šifrovaný)</comment>
+ <comment xml:lang="sl">Dokument KWord (šifriran)</comment>
+ <comment xml:lang="sq">Dokument KWord (i kriptuar)</comment>
+ <comment xml:lang="sr">документ К-речи (шифровани)</comment>
+ <comment xml:lang="sv">KWord-dokument (krypterat)</comment>
+ <comment xml:lang="tr">KWord belgesi (şifreli)</comment>
+ <comment xml:lang="uk">документ KWord (зашифрований)</comment>
+ <comment xml:lang="vi">Tài liệu KWord (đã mật mã)</comment>
+ <comment xml:lang="zh_CN">KWord 文档(加密)</comment>
+ <comment xml:lang="zh_TW">KWord 文件 (已加密)</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="0x0d1a2701" type="big32" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-lha">
+ <comment>LHA archive</comment>
+ <comment xml:lang="ar">أرشيف LHA</comment>
+ <comment xml:lang="az">LHA arxivi</comment>
+ <comment xml:lang="be@latin">Archiŭ LHA</comment>
+ <comment xml:lang="bg">Архив — LHA</comment>
+ <comment xml:lang="ca">arxiu LHA</comment>
+ <comment xml:lang="cs">archiv LHA</comment>
+ <comment xml:lang="cy">Archif LHA</comment>
+ <comment xml:lang="da">LHA-arkiv</comment>
+ <comment xml:lang="de">LHA-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο LHA</comment>
+ <comment xml:lang="en_GB">LHA archive</comment>
+ <comment xml:lang="eo">LHA-arkivo</comment>
+ <comment xml:lang="es">archivador LHA</comment>
+ <comment xml:lang="eu">LHA artxiboa</comment>
+ <comment xml:lang="fi">LHA-arkisto</comment>
+ <comment xml:lang="fo">LHA skjalasavn</comment>
+ <comment xml:lang="fr">archive LHA</comment>
+ <comment xml:lang="ga">cartlann LHA</comment>
+ <comment xml:lang="gl">arquivo LHA</comment>
+ <comment xml:lang="he">ארכיון LHA</comment>
+ <comment xml:lang="hr">LHA arhiva</comment>
+ <comment xml:lang="hu">LHA-archívum</comment>
+ <comment xml:lang="ia">Archivo LHA</comment>
+ <comment xml:lang="id">Arsip LHA</comment>
+ <comment xml:lang="it">Archivio LHA</comment>
+ <comment xml:lang="ja">LHA アーカイブ</comment>
+ <comment xml:lang="kk">LHA архиві</comment>
+ <comment xml:lang="ko">LHA 압축 파일</comment>
+ <comment xml:lang="lt">LHA archyvas</comment>
+ <comment xml:lang="lv">LHA arhīvs</comment>
+ <comment xml:lang="ms">Arkib LHA</comment>
+ <comment xml:lang="nb">LHA-arkiv</comment>
+ <comment xml:lang="nl">LHA-archief</comment>
+ <comment xml:lang="nn">LHA-arkiv</comment>
+ <comment xml:lang="oc">archiu LHA</comment>
+ <comment xml:lang="pl">Archiwum LHA</comment>
+ <comment xml:lang="pt">arquivo LHA</comment>
+ <comment xml:lang="pt_BR">Pacote LHA</comment>
+ <comment xml:lang="ro">Arhivă LHA</comment>
+ <comment xml:lang="ru">Архив LHA</comment>
+ <comment xml:lang="sk">Archív LHA</comment>
+ <comment xml:lang="sl">Datoteka arhiva LHA</comment>
+ <comment xml:lang="sq">Arkiv LHA</comment>
+ <comment xml:lang="sr">ЛХА архива</comment>
+ <comment xml:lang="sv">LHA-arkiv</comment>
+ <comment xml:lang="tr">LHA arşivi</comment>
+ <comment xml:lang="uk">архів LHA</comment>
+ <comment xml:lang="vi">Kho nén LHA</comment>
+ <comment xml:lang="zh_CN">LHA 归档文件</comment>
+ <comment xml:lang="zh_TW">LHA 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="-lh -" type="string" offset="2"/>
+ <match value="-lh0-" type="string" offset="2"/>
+ <match value="-lh1-" type="string" offset="2"/>
+ <match value="-lh2-" type="string" offset="2"/>
+ <match value="-lh3-" type="string" offset="2"/>
+ <match value="-lh4-" type="string" offset="2"/>
+ <match value="-lh5-" type="string" offset="2"/>
+ <match value="-lh40-" type="string" offset="2"/>
+ <match value="-lhd-" type="string" offset="2"/>
+ <match value="-lz4-" type="string" offset="2"/>
+ <match value="-lz5-" type="string" offset="2"/>
+ <match value="-lzs-" type="string" offset="2"/>
+ </magic>
+ <glob pattern="*.lha"/>
+ <glob pattern="*.lzh"/>
+ <alias type="application/x-lzh-compressed"/>
+ </mime-type>
+ <mime-type type="application/x-lhz">
+ <comment>LHZ archive</comment>
+ <comment xml:lang="ar">أرشيف LHZ</comment>
+ <comment xml:lang="be@latin">Archiŭ LHZ</comment>
+ <comment xml:lang="bg">Архив — LHZ</comment>
+ <comment xml:lang="ca">arxiu LHZ</comment>
+ <comment xml:lang="cs">archiv LHZ</comment>
+ <comment xml:lang="da">LHZ-arkiv</comment>
+ <comment xml:lang="de">LHZ-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο LHZ</comment>
+ <comment xml:lang="en_GB">LHZ archive</comment>
+ <comment xml:lang="eo">LHZ-arkivo</comment>
+ <comment xml:lang="es">archivador LHZ</comment>
+ <comment xml:lang="eu">LHZ artxiboa</comment>
+ <comment xml:lang="fi">LHZ-arkisto</comment>
+ <comment xml:lang="fo">LHZ skjalasavn</comment>
+ <comment xml:lang="fr">archive LHZ</comment>
+ <comment xml:lang="ga">cartlann LHZ</comment>
+ <comment xml:lang="gl">arquivo LHZ</comment>
+ <comment xml:lang="he">ארכיון LHZ</comment>
+ <comment xml:lang="hr">LHZ arhiva</comment>
+ <comment xml:lang="hu">LHZ-archívum</comment>
+ <comment xml:lang="ia">Archivo LHZ</comment>
+ <comment xml:lang="id">Arsip LHZ</comment>
+ <comment xml:lang="it">Archivio LHZ</comment>
+ <comment xml:lang="ja">LHZ アーカイブ</comment>
+ <comment xml:lang="kk">LHZ архиві</comment>
+ <comment xml:lang="ko">LHZ 압축 파일</comment>
+ <comment xml:lang="lt">LHZ archyvas</comment>
+ <comment xml:lang="lv">LHZ arhīvs</comment>
+ <comment xml:lang="ms">Arkib LHZ</comment>
+ <comment xml:lang="nb">LHZ-arkiv</comment>
+ <comment xml:lang="nl">LHZ-archief</comment>
+ <comment xml:lang="nn">LHZ-arkiv</comment>
+ <comment xml:lang="oc">archiu LHZ</comment>
+ <comment xml:lang="pl">Archiwum LHZ</comment>
+ <comment xml:lang="pt">arquivo LHZ</comment>
+ <comment xml:lang="pt_BR">Pacote LHZ</comment>
+ <comment xml:lang="ro">Arhivă LHZ</comment>
+ <comment xml:lang="ru">Архив LHZ</comment>
+ <comment xml:lang="sk">Archív LHZ</comment>
+ <comment xml:lang="sl">Datoteka arhiva LHZ</comment>
+ <comment xml:lang="sq">Arkiv LHZ</comment>
+ <comment xml:lang="sr">ЛХЗ архива</comment>
+ <comment xml:lang="sv">LHZ-arkiv</comment>
+ <comment xml:lang="tr">LHZ arşivi</comment>
+ <comment xml:lang="uk">архів LHZ</comment>
+ <comment xml:lang="vi">Kho nén LHZ (LHA đã nén)</comment>
+ <comment xml:lang="zh_CN">LHZ 归档文件</comment>
+ <comment xml:lang="zh_TW">LHZ 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.lhz"/>
+ </mime-type>
+ <mime-type type="text/vnd.qt.linguist">
+ <comment>message catalog</comment>
+ <comment xml:lang="ar">كتالوج الرسالة</comment>
+ <comment xml:lang="be@latin">kataloh paviedamleńniaŭ</comment>
+ <comment xml:lang="bg">Каталог със съобщения</comment>
+ <comment xml:lang="ca">catàleg de missatges</comment>
+ <comment xml:lang="cs">katalog zpráv</comment>
+ <comment xml:lang="da">meddelelseskatalog</comment>
+ <comment xml:lang="de">Nachrichtenkatalog</comment>
+ <comment xml:lang="el">Κατάλογος μηνυμάτων</comment>
+ <comment xml:lang="en_GB">message catalogue</comment>
+ <comment xml:lang="eo">katalogo de mesaĝoj</comment>
+ <comment xml:lang="es">catálogo de mensajes</comment>
+ <comment xml:lang="eu">mezuen katalogoa</comment>
+ <comment xml:lang="fi">viestiluettelo</comment>
+ <comment xml:lang="fo">boðskrá</comment>
+ <comment xml:lang="fr">catalogue de messages</comment>
+ <comment xml:lang="ga">catalóg theachtaireachtaí</comment>
+ <comment xml:lang="gl">catálogo de mensaxes</comment>
+ <comment xml:lang="he">קטלוג הודעות</comment>
+ <comment xml:lang="hr">Katalog poruka</comment>
+ <comment xml:lang="hu">üzenetkatalógus</comment>
+ <comment xml:lang="ia">Catalogo de messages</comment>
+ <comment xml:lang="id">katalog pesan</comment>
+ <comment xml:lang="it">Catalogo di messaggi</comment>
+ <comment xml:lang="ja">メッセージカタログ</comment>
+ <comment xml:lang="kk">мәлімдемелер каталогы</comment>
+ <comment xml:lang="ko">메시지 카탈로그</comment>
+ <comment xml:lang="lt">laiškų katalogas</comment>
+ <comment xml:lang="lv">ziņojumu katalogs</comment>
+ <comment xml:lang="ms">Katalog mesej</comment>
+ <comment xml:lang="nb">meldingskatalog</comment>
+ <comment xml:lang="nl">berichtencatalogus</comment>
+ <comment xml:lang="nn">meldingskatalog</comment>
+ <comment xml:lang="oc">catalòg de messatges</comment>
+ <comment xml:lang="pl">Katalog wiadomości</comment>
+ <comment xml:lang="pt">catálogo de mensagens</comment>
+ <comment xml:lang="pt_BR">Catálogo de mensagens</comment>
+ <comment xml:lang="ro">catalog de mesaje</comment>
+ <comment xml:lang="ru">Каталог сообщений</comment>
+ <comment xml:lang="sk">Katalóg správ</comment>
+ <comment xml:lang="sl">katalogov sporočil</comment>
+ <comment xml:lang="sq">Katallog mesazhesh</comment>
+ <comment xml:lang="sr">каталог порука</comment>
+ <comment xml:lang="sv">meddelandekatalog</comment>
+ <comment xml:lang="tr">ileti kataloğu</comment>
+ <comment xml:lang="uk">каталог повідомлень</comment>
+ <comment xml:lang="vi">phân loại thông điệp</comment>
+ <comment xml:lang="zh_CN">消息库</comment>
+ <comment xml:lang="zh_TW">訊息目錄</comment>
+ <sub-class-of type="application/xml"/>
+ <magic>
+ <match value="&lt;TS" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.ts"/>
+ <alias type="application/x-linguist"/>
+ <alias type="text/vnd.trolltech.linguist"/>
+ </mime-type>
+ <mime-type type="application/x-lyx">
+ <comment>LyX document</comment>
+ <comment xml:lang="ar">مستند LyX</comment>
+ <comment xml:lang="ast">Documentu de Lyx</comment>
+ <comment xml:lang="be@latin">Dakument LyX</comment>
+ <comment xml:lang="bg">Документ — LyX</comment>
+ <comment xml:lang="ca">document LyX</comment>
+ <comment xml:lang="cs">dokument LyX</comment>
+ <comment xml:lang="da">LyX-dokument</comment>
+ <comment xml:lang="de">LyX-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο LyX</comment>
+ <comment xml:lang="en_GB">LyX document</comment>
+ <comment xml:lang="eo">LyX-dokumento</comment>
+ <comment xml:lang="es">documento de LyX</comment>
+ <comment xml:lang="eu">LyX dokumentua</comment>
+ <comment xml:lang="fi">LyX-asiakirja</comment>
+ <comment xml:lang="fo">LyX skjal</comment>
+ <comment xml:lang="fr">document LyX</comment>
+ <comment xml:lang="ga">cáipéis LyX</comment>
+ <comment xml:lang="gl">documento LyX</comment>
+ <comment xml:lang="he">מסמך Lyx</comment>
+ <comment xml:lang="hr">LyX dokument</comment>
+ <comment xml:lang="hu">LyX-dokumentum</comment>
+ <comment xml:lang="ia">Documento LyX</comment>
+ <comment xml:lang="id">Dokumen LyX</comment>
+ <comment xml:lang="it">Documento LyX</comment>
+ <comment xml:lang="ja">LyX ドキュメント</comment>
+ <comment xml:lang="kk">LyX құжаты</comment>
+ <comment xml:lang="ko">LyX 문서</comment>
+ <comment xml:lang="lt">LyX dokumentas</comment>
+ <comment xml:lang="lv">LyX dokuments</comment>
+ <comment xml:lang="ms">Dokumen LyX</comment>
+ <comment xml:lang="nb">LyX-dokument</comment>
+ <comment xml:lang="nl">LyX-document</comment>
+ <comment xml:lang="nn">LyX-dokument</comment>
+ <comment xml:lang="oc">document LyX</comment>
+ <comment xml:lang="pl">Dokument LyX</comment>
+ <comment xml:lang="pt">documento LyX</comment>
+ <comment xml:lang="pt_BR">Documento LyX</comment>
+ <comment xml:lang="ro">Document LyX</comment>
+ <comment xml:lang="ru">Документ LyX</comment>
+ <comment xml:lang="sk">Dokument LyX</comment>
+ <comment xml:lang="sl">Dokument LyX</comment>
+ <comment xml:lang="sq">Dokument LyX</comment>
+ <comment xml:lang="sr">ЛиКс документ</comment>
+ <comment xml:lang="sv">LyX-dokument</comment>
+ <comment xml:lang="tr">LyX belgesi</comment>
+ <comment xml:lang="uk">документ LyX</comment>
+ <comment xml:lang="vi">Tài liệu LyX</comment>
+ <comment xml:lang="zh_CN">LyX 文档</comment>
+ <comment xml:lang="zh_TW">LyX 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="#LyX" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.lyx"/>
+ <alias type="text/x-lyx"/>
+ </mime-type>
+ <mime-type type="application/x-lz4">
+ <comment>LZ4 archive</comment>
+ <comment xml:lang="ca">arxiu LZ4</comment>
+ <comment xml:lang="cs">archiv LZ4</comment>
+ <comment xml:lang="da">LZ4-arkiv</comment>
+ <comment xml:lang="de">LZ4-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο LZ4</comment>
+ <comment xml:lang="en_GB">LZ4 archive</comment>
+ <comment xml:lang="es">archivador LZ4</comment>
+ <comment xml:lang="eu">LZ4 artxiboa</comment>
+ <comment xml:lang="fi">LZ4-arkisto</comment>
+ <comment xml:lang="fr">archive LZ4</comment>
+ <comment xml:lang="ga">Cartlann LZ4</comment>
+ <comment xml:lang="gl">Arquivo LZ4</comment>
+ <comment xml:lang="he">ארכיון LZ4</comment>
+ <comment xml:lang="hr">LZ4 arhiva</comment>
+ <comment xml:lang="hu">LZ4 archívum</comment>
+ <comment xml:lang="ia">Archivo LZ4</comment>
+ <comment xml:lang="id">Arsip LZ4</comment>
+ <comment xml:lang="it">Archivio LZ4</comment>
+ <comment xml:lang="kk">LZ4 архиві</comment>
+ <comment xml:lang="ko">LZ4 압축 파일</comment>
+ <comment xml:lang="oc">archiu LZ4</comment>
+ <comment xml:lang="pl">Archiwum LZ4</comment>
+ <comment xml:lang="pt">arquivo LZ4</comment>
+ <comment xml:lang="pt_BR">Pacote LZ4</comment>
+ <comment xml:lang="ru">Архив LZ4</comment>
+ <comment xml:lang="sk">Archív LZ4</comment>
+ <comment xml:lang="sl">Datoteka arhiva LZ4</comment>
+ <comment xml:lang="sr">ЛЗ4 архива</comment>
+ <comment xml:lang="sv">LZ4-arkiv</comment>
+ <comment xml:lang="tr">LZ4 arşivi</comment>
+ <comment xml:lang="uk">архів LZ4</comment>
+ <comment xml:lang="zh_CN">LZ4 归档文件</comment>
+ <comment xml:lang="zh_TW">LZ4 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="0x184d2204" type="little32" offset="0"/>
+ <match value="0x184c2102" type="little32" offset="0"/>
+ </magic>
+ <glob pattern="*.lz4"/>
+ </mime-type>
+ <mime-type type="application/x-lz4-compressed-tar">
+ <comment>Tar archive (LZ4-compressed)</comment>
+ <comment xml:lang="ca">arxiu tar (amb compressió LZ4)</comment>
+ <comment xml:lang="cs">archiv Tar (komprimace LZ4)</comment>
+ <comment xml:lang="da">Tar-arkiv (LZ4-komprimeret)</comment>
+ <comment xml:lang="de">Tar-Archiv (LZ4-komprimiert)</comment>
+ <comment xml:lang="en_GB">Tar archive (LZ4-compressed)</comment>
+ <comment xml:lang="es">archivador Tar (comprimido con LZ4)</comment>
+ <comment xml:lang="eu">Tar artxiboa (LZ4-rekin konprimatua)</comment>
+ <comment xml:lang="fi">Tar-arkisto (LZ4-pakattu)</comment>
+ <comment xml:lang="fr">archive tar (compression LZ4)</comment>
+ <comment xml:lang="ga">cartlann Tar (comhbhrúite le LZ4)</comment>
+ <comment xml:lang="he">ארכיון Tar (מכווץ ע״י LZ4)</comment>
+ <comment xml:lang="hr">Tar arhiva (LZ4 sažeto)</comment>
+ <comment xml:lang="hu">Tar archívum (LZ4-el tömörítve)</comment>
+ <comment xml:lang="id">arsip tar (terkompresi LZ4)</comment>
+ <comment xml:lang="it">Archivio tar (compresso con LZ4)</comment>
+ <comment xml:lang="kk">Tar архиві (LZ4-пен сығылған)</comment>
+ <comment xml:lang="ko">Tar 묶음 파일(LZ4 압축)</comment>
+ <comment xml:lang="pl">Archiwum tar (kompresja LZ4)</comment>
+ <comment xml:lang="pt_BR">Arquvio Tar (compactado com LZ4)</comment>
+ <comment xml:lang="ru">Архив TAR (сжатый lz4)</comment>
+ <comment xml:lang="sk">Archív tar (komprimovaný pomocou LZ4)</comment>
+ <comment xml:lang="sr">Тар архива (запакована ЛЗ4-ом)</comment>
+ <comment xml:lang="sv">Tar-arkiv (LZ4-komprimerat)</comment>
+ <comment xml:lang="tr">Tar arşivi (LZ4 ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">архів tar (стиснений LZ4)</comment>
+ <comment xml:lang="zh_CN">Tar 归档文件(LZ4 压缩)</comment>
+ <comment xml:lang="zh_TW">Tar 封存檔 (LZ4 格式壓縮)</comment>
+ <generic-icon name="package-x-generic"/>
+ <sub-class-of type="application/x-lz4"/>
+ <glob pattern="*.tar.lz4"/>
+ </mime-type>
+ <mime-type type="application/x-lzip">
+ <comment>Lzip archive</comment>
+ <comment xml:lang="ar">أرشيف Lzip</comment>
+ <comment xml:lang="bg">Архив — lzip</comment>
+ <comment xml:lang="ca">arxiu lzip</comment>
+ <comment xml:lang="cs">archiv Lzip</comment>
+ <comment xml:lang="da">Lzip-arkiv</comment>
+ <comment xml:lang="de">Lzip-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Lzip</comment>
+ <comment xml:lang="en_GB">Lzip archive</comment>
+ <comment xml:lang="eo">Lzip-arkivo</comment>
+ <comment xml:lang="es">archivador Lzip</comment>
+ <comment xml:lang="eu">Lzip artxiboa</comment>
+ <comment xml:lang="fi">Lzip-arkisto</comment>
+ <comment xml:lang="fo">Lzip skjalasavn</comment>
+ <comment xml:lang="fr">archive lzip</comment>
+ <comment xml:lang="ga">cartlann Lzip</comment>
+ <comment xml:lang="gl">arquivo Lzip</comment>
+ <comment xml:lang="he">ארכיון Lzip</comment>
+ <comment xml:lang="hr">Lzip arhiva</comment>
+ <comment xml:lang="hu">Lzip archívum</comment>
+ <comment xml:lang="ia">Archivo Lzip</comment>
+ <comment xml:lang="id">Arsip Lzip</comment>
+ <comment xml:lang="it">Archivio Lzip</comment>
+ <comment xml:lang="ja">Lzip アーカイブ</comment>
+ <comment xml:lang="kk">Lzip архиві</comment>
+ <comment xml:lang="ko">LZIP 압축 파일</comment>
+ <comment xml:lang="lt">Lzip archyvas</comment>
+ <comment xml:lang="lv">Lzip arhīvs</comment>
+ <comment xml:lang="nl">Lzip archief</comment>
+ <comment xml:lang="oc">archiu lzip</comment>
+ <comment xml:lang="pl">Archiwum lzip</comment>
+ <comment xml:lang="pt">arquivo LZip</comment>
+ <comment xml:lang="pt_BR">Pacote Lzip</comment>
+ <comment xml:lang="ro">Arhivă Lzip</comment>
+ <comment xml:lang="ru">Архив LZIP</comment>
+ <comment xml:lang="sk">Archív Lzip</comment>
+ <comment xml:lang="sl">Datoteka arhiva Lzip</comment>
+ <comment xml:lang="sr">Лзип архива</comment>
+ <comment xml:lang="sv">Lzip-arkiv</comment>
+ <comment xml:lang="tr">Lzip arşivi</comment>
+ <comment xml:lang="uk">архів lzip</comment>
+ <comment xml:lang="zh_CN">Lzip 归档文件</comment>
+ <comment xml:lang="zh_TW">Lzip 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="LZIP" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.lz"/>
+ </mime-type>
+ <mime-type type="application/x-lzip-compressed-tar">
+ <comment>Tar archive (lzip-compressed)</comment>
+ <comment xml:lang="ca">arxiu tar (amb compressió lzip)</comment>
+ <comment xml:lang="cs">archiv Tar (komprimace lzip)</comment>
+ <comment xml:lang="da">Tar-arkiv (lzip-komprimeret)</comment>
+ <comment xml:lang="de">Tar-Archiv (lzip-komprimiert)</comment>
+ <comment xml:lang="en_GB">Tar archive (lzip-compressed)</comment>
+ <comment xml:lang="es">archivador Tar (comprimido con lzip)</comment>
+ <comment xml:lang="eu">Tar artxiboa (lzip-rekin konprimatua)</comment>
+ <comment xml:lang="fi">Tar-arkisto (lzip-pakattu)</comment>
+ <comment xml:lang="fr">archive tar (compressée lzip)</comment>
+ <comment xml:lang="ga">cartlann Tar (comhbhrúite le lzip)</comment>
+ <comment xml:lang="he">ארכיון Tar (מכווץ ע״י lzip)</comment>
+ <comment xml:lang="hr">Tar arhiva (lzip sažeto)</comment>
+ <comment xml:lang="hu">Tar archívum (lzippel tömörítve)</comment>
+ <comment xml:lang="id">arsip tar (terkompresi lzip)</comment>
+ <comment xml:lang="it">Archivio tar (compresso con lzip)</comment>
+ <comment xml:lang="kk">Tar архиві (lzip-пен сығылған)</comment>
+ <comment xml:lang="ko">TAR 묶음 파일(LZIP 압축)</comment>
+ <comment xml:lang="pl">Archiwum tar (kompresja lzip)</comment>
+ <comment xml:lang="pt_BR">Arquivo Tar (compactado com lzip)</comment>
+ <comment xml:lang="ru">Архив TAR (сжатый lzip)</comment>
+ <comment xml:lang="sk">Archív tar (komprimovaný pomocou lzip)</comment>
+ <comment xml:lang="sr">Тар архива (запакована лзипом)</comment>
+ <comment xml:lang="sv">Tar-arkiv (lzip-komprimerat)</comment>
+ <comment xml:lang="tr">Tar arşivi (lzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">архів tar (стиснений lzip)</comment>
+ <comment xml:lang="zh_CN">Tar 归档文件(lzip 压缩)</comment>
+ <comment xml:lang="zh_TW">Tar 封存檔 (lzip 格式壓縮)</comment>
+ <generic-icon name="package-x-generic"/>
+ <sub-class-of type="application/x-lzip"/>
+ <glob pattern="*.tar.lz"/>
+ </mime-type>
+ <mime-type type="application/x-lzpdf">
+ <comment>PDF document (lzip-compressed)</comment>
+ <comment xml:lang="ast">Documentu PDF (comprimíu en lzip)</comment>
+ <comment xml:lang="ca">document PDF (amb compressió lzip)</comment>
+ <comment xml:lang="cs">dokument PDF (komprimace lzip)</comment>
+ <comment xml:lang="de">PDF-Dokument (lzip-komprimiert)</comment>
+ <comment xml:lang="en_GB">PDF document (lzip-compressed)</comment>
+ <comment xml:lang="es">documento PDF (comprimido con lzip)</comment>
+ <comment xml:lang="eu">PDF dokumentua (lzip-rekin konprimitua)</comment>
+ <comment xml:lang="fi">PDF-asiakirja (lzip-pakattu)</comment>
+ <comment xml:lang="fr">document PDF (compressé lzip)</comment>
+ <comment xml:lang="ga">cáipéis PDF (comhbhrúite le lzip)</comment>
+ <comment xml:lang="hr">PDF dokument (lzip sažeto)</comment>
+ <comment xml:lang="hu">PDF dokumentum (lzip-tömörítésű)</comment>
+ <comment xml:lang="id">dokumen PDF (termkompresi lzip)</comment>
+ <comment xml:lang="it">Documento PDF (compresso con lzip)</comment>
+ <comment xml:lang="kk">PDF құжаты (lzip-пен сығылған)</comment>
+ <comment xml:lang="ko">PDF 문서(LZIP 압축)</comment>
+ <comment xml:lang="pl">Dokument PDF (kompresja lzip)</comment>
+ <comment xml:lang="pt_BR">Documento PDF (compactado com lzip)</comment>
+ <comment xml:lang="ru">Документ PDF (сжатый lzip)</comment>
+ <comment xml:lang="sk">Dokument PDF (komprimovaný pomocou lzip)</comment>
+ <comment xml:lang="sr">ПДФ документ (запакован лзип-ом)</comment>
+ <comment xml:lang="sv">PDF-dokument (lzip-komprimerat)</comment>
+ <comment xml:lang="tr">PDF belgesi (lzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">документ PDF (стиснений lzip)</comment>
+ <comment xml:lang="zh_CN">PDF 文档(lzip 压缩)</comment>
+ <comment xml:lang="zh_TW">PDF 文件 (lzip 格式壓縮)</comment>
+ <sub-class-of type="application/x-lzip"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.pdf.lz"/>
+ </mime-type>
+ <mime-type type="application/x-lzma">
+ <comment>LZMA archive</comment>
+ <comment xml:lang="ar">أرشيف LZMA</comment>
+ <comment xml:lang="be@latin">Archiŭ LZMA</comment>
+ <comment xml:lang="bg">Архив — LZMA</comment>
+ <comment xml:lang="ca">arxiu LZMA</comment>
+ <comment xml:lang="cs">archiv LZMA</comment>
+ <comment xml:lang="da">LZHA-arkiv</comment>
+ <comment xml:lang="de">LZMA-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο LZMA</comment>
+ <comment xml:lang="en_GB">LZMA archive</comment>
+ <comment xml:lang="eo">LZMA-arkivo</comment>
+ <comment xml:lang="es">archivador LZMA</comment>
+ <comment xml:lang="eu">LZMA artxiboa</comment>
+ <comment xml:lang="fi">LZMA-arkisto</comment>
+ <comment xml:lang="fo">LZMA skjalasavn</comment>
+ <comment xml:lang="fr">archive LZMA</comment>
+ <comment xml:lang="ga">cartlann LZMA</comment>
+ <comment xml:lang="gl">arquivo LZMA</comment>
+ <comment xml:lang="he">ארכיון LZMA</comment>
+ <comment xml:lang="hr">LZMA arhiva</comment>
+ <comment xml:lang="hu">LZMA-archívum</comment>
+ <comment xml:lang="ia">Archivo LZMA</comment>
+ <comment xml:lang="id">Arsip LZMA</comment>
+ <comment xml:lang="it">Archivio LZMA</comment>
+ <comment xml:lang="ja">LZMA アーカイブ</comment>
+ <comment xml:lang="kk">LZMA архиві</comment>
+ <comment xml:lang="ko">LZMA 압축 파일</comment>
+ <comment xml:lang="lt">LZMA archyvas</comment>
+ <comment xml:lang="lv">LZMA arhīvs</comment>
+ <comment xml:lang="nb">LZMA-arkiv</comment>
+ <comment xml:lang="nl">LZMA-archief</comment>
+ <comment xml:lang="nn">LZMA-arkiv</comment>
+ <comment xml:lang="oc">archiu LZMA</comment>
+ <comment xml:lang="pl">Archiwum LZMA</comment>
+ <comment xml:lang="pt">arquivo LZMA</comment>
+ <comment xml:lang="pt_BR">Pacote LZMA</comment>
+ <comment xml:lang="ro">Arhivă LZMA</comment>
+ <comment xml:lang="ru">Архив LZMA</comment>
+ <comment xml:lang="sk">Archív LZMA</comment>
+ <comment xml:lang="sl">Datoteka arhiva LZMA</comment>
+ <comment xml:lang="sq">Arkiv LZMA</comment>
+ <comment xml:lang="sr">ЛЗМА архива</comment>
+ <comment xml:lang="sv">LZMA-arkiv</comment>
+ <comment xml:lang="tr">LZMA arşivi</comment>
+ <comment xml:lang="uk">архів LZMA</comment>
+ <comment xml:lang="vi">Kho nén LZMA</comment>
+ <comment xml:lang="zh_CN">LZMA 归档文件</comment>
+ <comment xml:lang="zh_TW">LZMA 封存檔</comment>
+ <acronym>LZMA</acronym>
+ <expanded-acronym>Lempel-Ziv-Markov chain-Algorithm</expanded-acronym>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.lzma"/>
+ </mime-type>
+ <mime-type type="application/x-lzma-compressed-tar">
+ <comment>Tar archive (LZMA-compressed)</comment>
+ <comment xml:lang="ar">أرشيف Tar (مضغوط-LZMA)</comment>
+ <comment xml:lang="be@latin">Archiŭ tar (LZMA-skampresavany)</comment>
+ <comment xml:lang="bg">Архив — tar, компресиран с LZMA</comment>
+ <comment xml:lang="ca">arxiu tar (amb compressió LZMA)</comment>
+ <comment xml:lang="cs">archiv Tar (komprimace LZMA)</comment>
+ <comment xml:lang="da">Tar-arkiv (LZMA-komprimeret)</comment>
+ <comment xml:lang="de">Tar-Archiv (LZMA-komprimiert)</comment>
+ <comment xml:lang="el">Αρχείο Tar (συμπιεσμένο με LZMA)</comment>
+ <comment xml:lang="en_GB">Tar archive (LZMA-compressed)</comment>
+ <comment xml:lang="es">archivador Tar (comprimido con LZMA)</comment>
+ <comment xml:lang="eu">Tar artxiboa (LZMA-rekin konprimitua)</comment>
+ <comment xml:lang="fi">Tar-arkisto (LZMA-pakattu)</comment>
+ <comment xml:lang="fo">Tar skjalasavn (LZMA-stappað)</comment>
+ <comment xml:lang="fr">archive tar (compression LZMA)</comment>
+ <comment xml:lang="ga">cartlann Tar (comhbhrúite le LZMA)</comment>
+ <comment xml:lang="gl">arquivo Tar (comprimido con LZMA)</comment>
+ <comment xml:lang="he">ארכיון Tar (מכווץ ע״י LZMA)</comment>
+ <comment xml:lang="hr">Tar arhiva (LZMA sažeta)</comment>
+ <comment xml:lang="hu">Tar archívum (LZMA-val tömörítve)</comment>
+ <comment xml:lang="ia">Archivo Tar (comprimite con LZMA)</comment>
+ <comment xml:lang="id">Arsip Tar (terkompresi LZMA)</comment>
+ <comment xml:lang="it">Archivio tar (compresso con LZMA)</comment>
+ <comment xml:lang="ja">Tar アーカイブ (LZMA 圧縮)</comment>
+ <comment xml:lang="kk">Tar архиві (LZMA-мен сығылған)</comment>
+ <comment xml:lang="ko">TAR 묶음 파일(LZMA 압축)</comment>
+ <comment xml:lang="lt">Tar archyvas (suglaudintas su LZMA)</comment>
+ <comment xml:lang="lv">Tar arhīvs (saspiests ar LZMA)</comment>
+ <comment xml:lang="nb">Tar-arkiv (LZMA-komprimert)</comment>
+ <comment xml:lang="nl">Tar-archief (ingepakt met LZMA)</comment>
+ <comment xml:lang="nn">Tar-arkiv (pakka med LZMA)</comment>
+ <comment xml:lang="oc">archiu tar (compression LZMA)</comment>
+ <comment xml:lang="pl">Archiwum tar (kompresja LZMA)</comment>
+ <comment xml:lang="pt">arquivo Tar (compressão LZMA)</comment>
+ <comment xml:lang="pt_BR">Pacote Tar (compactado com LZMA)</comment>
+ <comment xml:lang="ro">Arhivă Tar (comprimată LZMA)</comment>
+ <comment xml:lang="ru">Архив TAR (сжатый lzma)</comment>
+ <comment xml:lang="sk">Archív tar (komprimovaný pomocou LZMA)</comment>
+ <comment xml:lang="sl">Datoteka arhiva Tar (stisnjen z LZMA)</comment>
+ <comment xml:lang="sq">Arkiv tar (i kompresuar me LZMA)</comment>
+ <comment xml:lang="sr">Тар архива (запакована ЛЗМА-ом)</comment>
+ <comment xml:lang="sv">Tar-arkiv (LZMA-komprimerat)</comment>
+ <comment xml:lang="tr">Tar arşivi (LZMA ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">архів tar (стиснений LZMA)</comment>
+ <comment xml:lang="vi">Kho nén tar (đã nén LZMA)</comment>
+ <comment xml:lang="zh_CN">Tar 归档文件(LZMA 压缩)</comment>
+ <comment xml:lang="zh_TW">Tar 封存檔 (LZMA 格式壓縮)</comment>
+ <sub-class-of type="application/x-lzma"/>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.tar.lzma"/>
+ <glob pattern="*.tlz"/>
+ </mime-type>
+ <mime-type type="application/x-lzop">
+ <comment>LZO archive</comment>
+ <comment xml:lang="ar">أرشيف LZO</comment>
+ <comment xml:lang="be@latin">Archiŭ LZO</comment>
+ <comment xml:lang="bg">Архив — LZO</comment>
+ <comment xml:lang="ca">arxiu LZO</comment>
+ <comment xml:lang="cs">archiv LZO</comment>
+ <comment xml:lang="da">LZO-arkiv</comment>
+ <comment xml:lang="de">LZO-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο LZO</comment>
+ <comment xml:lang="en_GB">LZO archive</comment>
+ <comment xml:lang="eo">LZO-arkivo</comment>
+ <comment xml:lang="es">archivador LZO</comment>
+ <comment xml:lang="eu">LZO artxiboa</comment>
+ <comment xml:lang="fi">LZO-arkisto</comment>
+ <comment xml:lang="fo">LZO skjalasavn</comment>
+ <comment xml:lang="fr">archive LZO</comment>
+ <comment xml:lang="ga">cartlann LZO</comment>
+ <comment xml:lang="gl">arquivo LZO</comment>
+ <comment xml:lang="he">ארכיון LZO</comment>
+ <comment xml:lang="hr">LZO arhiva</comment>
+ <comment xml:lang="hu">LZO-archívum</comment>
+ <comment xml:lang="ia">Archivo LZO</comment>
+ <comment xml:lang="id">Arsip LZO</comment>
+ <comment xml:lang="it">Archivio LZO</comment>
+ <comment xml:lang="ja">LZO アーカイブ</comment>
+ <comment xml:lang="kk">LZO архиві</comment>
+ <comment xml:lang="ko">LZO 압축 파일</comment>
+ <comment xml:lang="lt">LZO archyvas</comment>
+ <comment xml:lang="lv">LZO arhīvs</comment>
+ <comment xml:lang="ms">Arkib LZO</comment>
+ <comment xml:lang="nb">LZO-arkiv</comment>
+ <comment xml:lang="nl">LZO-archief</comment>
+ <comment xml:lang="nn">LZO-arkiv</comment>
+ <comment xml:lang="oc">archiu LZO</comment>
+ <comment xml:lang="pl">Archiwum LZO</comment>
+ <comment xml:lang="pt">arquivo LZO</comment>
+ <comment xml:lang="pt_BR">Pacote LZO</comment>
+ <comment xml:lang="ro">Arhivă LZO</comment>
+ <comment xml:lang="ru">Архив LZO</comment>
+ <comment xml:lang="sk">Archív LZO</comment>
+ <comment xml:lang="sl">Datoteka arhiva LZO</comment>
+ <comment xml:lang="sq">Arkiv LZO</comment>
+ <comment xml:lang="sr">ЛЗО архива</comment>
+ <comment xml:lang="sv">LZO-arkiv</comment>
+ <comment xml:lang="tr">LZO arşivi</comment>
+ <comment xml:lang="uk">архів LZO</comment>
+ <comment xml:lang="vi">Kho nén LZO</comment>
+ <comment xml:lang="zh_CN">LZO 归档文件</comment>
+ <comment xml:lang="zh_TW">LZO 封存檔</comment>
+ <acronym>LZO</acronym>
+ <expanded-acronym>Lempel-Ziv-Oberhumer</expanded-acronym>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.lzo"/>
+ </mime-type>
+ <mime-type type="application/x-qpress">
+ <comment>Qpress archive</comment>
+ <comment xml:lang="ca">arxiu Qpress</comment>
+ <comment xml:lang="cs">archiv Qpress</comment>
+ <comment xml:lang="da">Qpress-arkiv</comment>
+ <comment xml:lang="de">Qpress-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Qpress</comment>
+ <comment xml:lang="en_GB">Qpress archive</comment>
+ <comment xml:lang="es">archivador de Qpress</comment>
+ <comment xml:lang="fi">Qpress-arkisto</comment>
+ <comment xml:lang="fr">Archive Qpress</comment>
+ <comment xml:lang="ga">cartlann Qpress</comment>
+ <comment xml:lang="gl">Arquivo Qpress</comment>
+ <comment xml:lang="he">ארכיון Qpress</comment>
+ <comment xml:lang="hr">Qpress arhiva</comment>
+ <comment xml:lang="hu">Qpress archívum</comment>
+ <comment xml:lang="ia">Archivo Qpress</comment>
+ <comment xml:lang="id">Arsip Qpress</comment>
+ <comment xml:lang="it">Archivio Qpress</comment>
+ <comment xml:lang="kk">Qpress архиві</comment>
+ <comment xml:lang="ko">Qpress 압축 파일</comment>
+ <comment xml:lang="oc">Archiu Qpress</comment>
+ <comment xml:lang="pl">Archiwum Qpress</comment>
+ <comment xml:lang="pt">arquivo Qpress</comment>
+ <comment xml:lang="pt_BR">Pacote Qpress</comment>
+ <comment xml:lang="ru">Архив Qpress</comment>
+ <comment xml:lang="sk">Archív Qpress</comment>
+ <comment xml:lang="sl">Datoteka arhiva Qpress</comment>
+ <comment xml:lang="sr">Купрес архива</comment>
+ <comment xml:lang="sv">Qpress-arkiv</comment>
+ <comment xml:lang="tr">Qpress arşivi</comment>
+ <comment xml:lang="uk">архів Qpress</comment>
+ <comment xml:lang="zh_CN">Qpress 归档文件</comment>
+ <comment xml:lang="zh_TW">Qpress 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="qpress10" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.qp"/>
+ </mime-type>
+ <mime-type type="application/x-xar">
+ <comment>XAR archive</comment>
+ <comment xml:lang="ca">arxiu XAR</comment>
+ <comment xml:lang="cs">archiv XAR</comment>
+ <comment xml:lang="da">XAR-arkiv</comment>
+ <comment xml:lang="de">XAR-Archiv</comment>
+ <comment xml:lang="en_GB">XAR archive</comment>
+ <comment xml:lang="es">archivador XAR</comment>
+ <comment xml:lang="eu">XAR artxiboa</comment>
+ <comment xml:lang="fi">XAR-arkisto</comment>
+ <comment xml:lang="fr">archive XAR</comment>
+ <comment xml:lang="ga">cartlann XAR</comment>
+ <comment xml:lang="he">ארכיון XAR</comment>
+ <comment xml:lang="hr">XAR arhiva</comment>
+ <comment xml:lang="hu">XAR archívum</comment>
+ <comment xml:lang="id">Arsip XAR</comment>
+ <comment xml:lang="it">Archivio XAR</comment>
+ <comment xml:lang="kk">XAR архиві</comment>
+ <comment xml:lang="ko">XAR 아카이브</comment>
+ <comment xml:lang="oc">Archiu XAR</comment>
+ <comment xml:lang="pl">Archiwum XAR</comment>
+ <comment xml:lang="pt_BR">Arquivo XAR</comment>
+ <comment xml:lang="ru">Архив XAR</comment>
+ <comment xml:lang="sk">Archív XAR</comment>
+ <comment xml:lang="sr">ИксАР архива</comment>
+ <comment xml:lang="sv">XAR-arkiv</comment>
+ <comment xml:lang="tr">XAR arşivi</comment>
+ <comment xml:lang="uk">архів XAR</comment>
+ <comment xml:lang="zh_CN">XAR 归档文件</comment>
+ <comment xml:lang="zh_TW">XAR 封存檔</comment>
+ <acronym>XAR</acronym>
+ <expanded-acronym>eXtensible ARchive</expanded-acronym>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="0x78617221" type="big32" offset="0"/>
+ </magic>
+ <glob pattern="*.xar"/>
+
+ <glob pattern="*.pkg"/>
+ </mime-type>
+ <mime-type type="application/zlib">
+ <comment>Zlib archive</comment>
+ <comment xml:lang="ca">arxiu Zlib</comment>
+ <comment xml:lang="cs">archiv Zlib</comment>
+ <comment xml:lang="da">Zlib-arkiv</comment>
+ <comment xml:lang="de">Zlib-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Zlib</comment>
+ <comment xml:lang="en_GB">Zlib archive</comment>
+ <comment xml:lang="es">archivador Zlib</comment>
+ <comment xml:lang="fi">Zlib-arkisto</comment>
+ <comment xml:lang="fr">Archive Zlib</comment>
+ <comment xml:lang="ga">cartlann Zlib</comment>
+ <comment xml:lang="gl">Arquivo Zlib</comment>
+ <comment xml:lang="he">ארכיון Zlib</comment>
+ <comment xml:lang="hr">Zlib arhiva</comment>
+ <comment xml:lang="hu">Zlib archívum</comment>
+ <comment xml:lang="ia">Archivo Zlib</comment>
+ <comment xml:lang="id">Arsip Zlib</comment>
+ <comment xml:lang="it">Archivio zlib</comment>
+ <comment xml:lang="kk">Zlib архиві</comment>
+ <comment xml:lang="ko">Zlib 압축 파일</comment>
+ <comment xml:lang="oc">Archiu Zlib</comment>
+ <comment xml:lang="pl">Archiwum Zlib</comment>
+ <comment xml:lang="pt">arquivo Zlib</comment>
+ <comment xml:lang="pt_BR">Pacote Zlib</comment>
+ <comment xml:lang="ru">Архив Zlib</comment>
+ <comment xml:lang="sk">Archív Zlib</comment>
+ <comment xml:lang="sl">Datoteka arhiva Zlib</comment>
+ <comment xml:lang="sr">Злиб архива</comment>
+ <comment xml:lang="sv">Zlib-arkiv</comment>
+ <comment xml:lang="tr">Zlib arşivi</comment>
+ <comment xml:lang="uk">архів zlib</comment>
+ <comment xml:lang="zh_CN">Alzip 归档文件</comment>
+ <comment xml:lang="zh_TW">Zlib 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.zz"/>
+ </mime-type>
+ <mime-type type="application/x-magicpoint">
+ <comment>MagicPoint presentation</comment>
+ <comment xml:lang="ar">عرض تقديمي MagicPoint</comment>
+ <comment xml:lang="be@latin">Prezentacyja MagicPoint</comment>
+ <comment xml:lang="bg">Презентация — MagicPoint</comment>
+ <comment xml:lang="ca">presentació de MagicPoint</comment>
+ <comment xml:lang="cs">prezentace MagicPoint</comment>
+ <comment xml:lang="cy">Cyflwyniad MagicPoint</comment>
+ <comment xml:lang="da">MagicPoint-præsentation</comment>
+ <comment xml:lang="de">MagicPoint-Präsentation</comment>
+ <comment xml:lang="el">Παρουσίαση MagicPoint</comment>
+ <comment xml:lang="en_GB">MagicPoint presentation</comment>
+ <comment xml:lang="eo">MagicPoint-prezentaĵo</comment>
+ <comment xml:lang="es">presentación de MagicPoint</comment>
+ <comment xml:lang="eu">MagicPoint aurkezpena</comment>
+ <comment xml:lang="fi">MagicPoint-esitys</comment>
+ <comment xml:lang="fo">MagicPoint framløga</comment>
+ <comment xml:lang="fr">présentation MagicPoint</comment>
+ <comment xml:lang="ga">láithreoireacht MagicPoint</comment>
+ <comment xml:lang="gl">presentación de MagicPoint</comment>
+ <comment xml:lang="he">מצגת MagicPoint</comment>
+ <comment xml:lang="hr">MagicPoint prezentacija</comment>
+ <comment xml:lang="hu">MagicPoint-bemutató</comment>
+ <comment xml:lang="ia">Presentation MagicPoint</comment>
+ <comment xml:lang="id">Presentasi MagicPoint</comment>
+ <comment xml:lang="it">Presentazione MagicPoint</comment>
+ <comment xml:lang="ja">MagicPoint プレゼンテーション</comment>
+ <comment xml:lang="ka">MagicPoint-ის პრეზენტაცია</comment>
+ <comment xml:lang="kk">MagicPoint презентациясы</comment>
+ <comment xml:lang="ko">MagicPoint 프레젠테이션</comment>
+ <comment xml:lang="lt">MagicPoint pateiktis</comment>
+ <comment xml:lang="lv">MagicPoint prezentācija</comment>
+ <comment xml:lang="ms">Persembahan MagicPoint</comment>
+ <comment xml:lang="nb">MagicPoint-presentasjon</comment>
+ <comment xml:lang="nl">MagicPoint-presentatie</comment>
+ <comment xml:lang="nn">MagicPoint-presentasjon</comment>
+ <comment xml:lang="oc">presentacion MagicPoint</comment>
+ <comment xml:lang="pl">Prezentacja programu MagicPoint</comment>
+ <comment xml:lang="pt">apresentação MagicPoint</comment>
+ <comment xml:lang="pt_BR">Apresentação do MagicPoint</comment>
+ <comment xml:lang="ro">Prezentare MagicPoint</comment>
+ <comment xml:lang="ru">Презентация MagicPoint</comment>
+ <comment xml:lang="sk">Prezentácia MagicPoint</comment>
+ <comment xml:lang="sl">Predstavitev MagicPoint</comment>
+ <comment xml:lang="sq">Prezantim MagicPoint</comment>
+ <comment xml:lang="sr">презентација Меџик Поинта</comment>
+ <comment xml:lang="sv">MagicPoint-presentation</comment>
+ <comment xml:lang="tr">MagicPoint sunumu</comment>
+ <comment xml:lang="uk">презентація MagicPoint</comment>
+ <comment xml:lang="vi">Trình diễn MagicPoint</comment>
+ <comment xml:lang="zh_CN">MagicPoint 演示文稿</comment>
+ <comment xml:lang="zh_TW">MagicPoint 簡報檔</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="x-office-presentation"/>
+ <glob pattern="*.mgp"/>
+ </mime-type>
+ <mime-type type="application/x-macbinary">
+ <comment>Macintosh MacBinary file</comment>
+ <comment xml:lang="ar">ملف Macintosh MacBinary</comment>
+ <comment xml:lang="be@latin">Fajł Macintosh MacBinary</comment>
+ <comment xml:lang="bg">Файл — MacBinary</comment>
+ <comment xml:lang="ca">fitxer MacBinary de Macintosh</comment>
+ <comment xml:lang="cs">soubor MacBinary pro Macintosh </comment>
+ <comment xml:lang="da">Macintosh MacBinary-fil</comment>
+ <comment xml:lang="de">Macintosh-MacBinary-Datei</comment>
+ <comment xml:lang="el">Εκτελέσιμο Macintosh MacBinary</comment>
+ <comment xml:lang="en_GB">Macintosh MacBinary file</comment>
+ <comment xml:lang="eo">MacBinary-dosiero de Macintosh</comment>
+ <comment xml:lang="es">archivo de Macintosh MacBinary</comment>
+ <comment xml:lang="eu">Macintosh MacBinary fitxategia</comment>
+ <comment xml:lang="fi">Macintosh MacBinary -tiedosto</comment>
+ <comment xml:lang="fo">Macintosh MacBinary fíla</comment>
+ <comment xml:lang="fr">fichier Macintosh MacBinary</comment>
+ <comment xml:lang="ga">comhad Macintosh MacBinary</comment>
+ <comment xml:lang="gl">ficheiro MacBinary de Macintosh</comment>
+ <comment xml:lang="he">קובץ בינרי של מקינטוש</comment>
+ <comment xml:lang="hr">Macintosh MacBinary datoteka</comment>
+ <comment xml:lang="hu">Macintosh MacBinary-fájl</comment>
+ <comment xml:lang="ia">File MacBinary de Macintosh</comment>
+ <comment xml:lang="id">Berkas Macintosh MacBinary</comment>
+ <comment xml:lang="it">File Macintosh MacBinary</comment>
+ <comment xml:lang="ja">Macintosh MacBinary ファイル</comment>
+ <comment xml:lang="kk">Macintosh MacBinary файлы</comment>
+ <comment xml:lang="ko">MacBinary 파일</comment>
+ <comment xml:lang="lt">Macintosh MacBinary failas</comment>
+ <comment xml:lang="lv">Macintosh MacBinary datne</comment>
+ <comment xml:lang="ms">Fail MacBinary Macintosh</comment>
+ <comment xml:lang="nb">Macintosh MacBinary-fil</comment>
+ <comment xml:lang="nl">Macintosh MacBinary-bestand</comment>
+ <comment xml:lang="nn">Macintosh MacBinary-fil</comment>
+ <comment xml:lang="oc">fichièr Macintosh MacBinary</comment>
+ <comment xml:lang="pl">Plik MacBinary Macintosh</comment>
+ <comment xml:lang="pt">ficheiro MacBinary de Macintosh</comment>
+ <comment xml:lang="pt_BR">Arquivo do Macintosh MacBinary</comment>
+ <comment xml:lang="ro">Fișier Macintosh MacBinary</comment>
+ <comment xml:lang="ru">Файл Macintosh MacBinary</comment>
+ <comment xml:lang="sk">Súbor pre Macintosh MacBinary</comment>
+ <comment xml:lang="sl">Izvedljiva dvojiška datoteka Macintosh MacBinary</comment>
+ <comment xml:lang="sq">File MacBinary Macintosh</comment>
+ <comment xml:lang="sr">Мекинтош Мек Бинари датотека</comment>
+ <comment xml:lang="sv">Macintosh MacBinary-fil</comment>
+ <comment xml:lang="tr">Macintosh MacBinary dosyası</comment>
+ <comment xml:lang="uk">файл Macintosh MacBinary</comment>
+ <comment xml:lang="vi">Tập tin nhị phân MacBinary của Macintosh</comment>
+ <comment xml:lang="zh_CN">Macintosh MacBinary 文件</comment>
+ <comment xml:lang="zh_TW">Macintosh MacBinary 檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="mBIN" type="string" offset="102"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-matroska">
+ <comment>Matroska stream</comment>
+ <comment xml:lang="ar">دفق Matroska</comment>
+ <comment xml:lang="be@latin">Płyń Matroska</comment>
+ <comment xml:lang="bg">Поток — Matroska</comment>
+ <comment xml:lang="ca">flux Matroska</comment>
+ <comment xml:lang="cs">proud Matroska</comment>
+ <comment xml:lang="da">Matroskastrøm</comment>
+ <comment xml:lang="de">Matroska-Datenstrom</comment>
+ <comment xml:lang="el">Ροή Matroska</comment>
+ <comment xml:lang="en_GB">Matroska stream</comment>
+ <comment xml:lang="es">flujo Matroska</comment>
+ <comment xml:lang="eu">Matroska korrontea</comment>
+ <comment xml:lang="fi">Matroska-virta</comment>
+ <comment xml:lang="fo">Matroska streymur</comment>
+ <comment xml:lang="fr">flux Matroska</comment>
+ <comment xml:lang="ga">sruth Matroska</comment>
+ <comment xml:lang="gl">fluxo de Matroska</comment>
+ <comment xml:lang="he">זרימת Matroska</comment>
+ <comment xml:lang="hr">Matroška zapis</comment>
+ <comment xml:lang="hu">Matroska adatfolyam</comment>
+ <comment xml:lang="ia">Fluxo Matroska</comment>
+ <comment xml:lang="id">Stream Matroska</comment>
+ <comment xml:lang="it">Stream Matroska</comment>
+ <comment xml:lang="ja">Matroska ストリーム</comment>
+ <comment xml:lang="ka">Matroska-ის ნაკადი</comment>
+ <comment xml:lang="kk">Matroska ағымы</comment>
+ <comment xml:lang="ko">Matroska 스트림</comment>
+ <comment xml:lang="lt">Matroska srautas</comment>
+ <comment xml:lang="lv">Matroska straume</comment>
+ <comment xml:lang="nl">Matroska-stream</comment>
+ <comment xml:lang="nn">Matroska-straum</comment>
+ <comment xml:lang="oc">flux Matroska</comment>
+ <comment xml:lang="pl">Strumień Matroska</comment>
+ <comment xml:lang="pt">fluxo Matroska</comment>
+ <comment xml:lang="pt_BR">Transmissão do Matroska</comment>
+ <comment xml:lang="ro">Flux Matroska</comment>
+ <comment xml:lang="ru">Поток Matroska</comment>
+ <comment xml:lang="sk">Stream Matroska</comment>
+ <comment xml:lang="sl">Pretočni vir Matroska</comment>
+ <comment xml:lang="sq">Stream Matroska</comment>
+ <comment xml:lang="sr">Матрошкин ток</comment>
+ <comment xml:lang="sv">Matroska-ström</comment>
+ <comment xml:lang="tr">Matroska akışı</comment>
+ <comment xml:lang="uk">потік даних Matroska</comment>
+ <comment xml:lang="vi">Luồng Matroska</comment>
+ <comment xml:lang="zh_CN">Matroska 流</comment>
+ <comment xml:lang="zh_TW">Matroska 串流</comment>
+ <generic-icon name="video-x-generic"/>
+ <magic priority="50">
+
+ <match value="0x1a45dfa3" type="big32" offset="0">
+
+ <match value="0x4282" type="big16" offset="5:65">
+
+ <match value="matroska" type="string" offset="8:75"/>
+ </match>
+ </match>
+ </magic>
+ </mime-type>
+ <mime-type type="video/x-matroska">
+ <comment>Matroska video</comment>
+ <comment xml:lang="ar">Matroska مرئي</comment>
+ <comment xml:lang="ast">Videu en Matroska</comment>
+ <comment xml:lang="be@latin">Videa Matroska</comment>
+ <comment xml:lang="bg">Видео — Matroska</comment>
+ <comment xml:lang="ca">vídeo Matroska</comment>
+ <comment xml:lang="cs">video Matroska</comment>
+ <comment xml:lang="da">Matroskavideo</comment>
+ <comment xml:lang="de">Matroska-Video</comment>
+ <comment xml:lang="el">Βίντεο Matroska</comment>
+ <comment xml:lang="en_GB">Matroska video</comment>
+ <comment xml:lang="eo">Matroska-video</comment>
+ <comment xml:lang="es">vídeo Matroska</comment>
+ <comment xml:lang="eu">Matroska bideoa</comment>
+ <comment xml:lang="fi">Matroska-video</comment>
+ <comment xml:lang="fo">Matroska video</comment>
+ <comment xml:lang="fr">vidéo Matroska</comment>
+ <comment xml:lang="ga">físeán Matroska</comment>
+ <comment xml:lang="gl">vídeo de Matroska</comment>
+ <comment xml:lang="he">וידאו Matroska</comment>
+ <comment xml:lang="hr">Matroška video snimka</comment>
+ <comment xml:lang="hu">Matroska-videó</comment>
+ <comment xml:lang="ia">Video Matroska</comment>
+ <comment xml:lang="id">Video Matroska</comment>
+ <comment xml:lang="it">Video Matroska</comment>
+ <comment xml:lang="ja">Matroska 動画</comment>
+ <comment xml:lang="ka">Matroska-ის ვიდეო</comment>
+ <comment xml:lang="kk">Matroska видеосы</comment>
+ <comment xml:lang="ko">Matroska 동영상</comment>
+ <comment xml:lang="lt">Matroska vaizdo įrašas</comment>
+ <comment xml:lang="lv">Matroska video</comment>
+ <comment xml:lang="ms">Video Matroska</comment>
+ <comment xml:lang="nb">Matroska-film</comment>
+ <comment xml:lang="nl">Matroska-video</comment>
+ <comment xml:lang="nn">Matroska-video</comment>
+ <comment xml:lang="oc">vidèo Matroska</comment>
+ <comment xml:lang="pl">Plik wideo Matroska</comment>
+ <comment xml:lang="pt">vídeo Matroska</comment>
+ <comment xml:lang="pt_BR">Vídeo Matroska</comment>
+ <comment xml:lang="ro">Video Matroska</comment>
+ <comment xml:lang="ru">Видео Matroska</comment>
+ <comment xml:lang="sk">Video Matroska</comment>
+ <comment xml:lang="sl">Video datoteka Matroska</comment>
+ <comment xml:lang="sq">Video Matroska</comment>
+ <comment xml:lang="sr">Матрошкин видео</comment>
+ <comment xml:lang="sv">Matroska-video</comment>
+ <comment xml:lang="tr">Matroska video</comment>
+ <comment xml:lang="uk">відеокліп Matroska</comment>
+ <comment xml:lang="vi">Ảnh động Matroska</comment>
+ <comment xml:lang="zh_CN">Matroska 视频</comment>
+ <comment xml:lang="zh_TW">Matroska 視訊</comment>
+ <glob pattern="*.mkv"/>
+ <sub-class-of type="application/x-matroska"/>
+ </mime-type>
+ <mime-type type="video/x-matroska-3d">
+ <comment>Matroska 3D video</comment>
+ <comment xml:lang="ast">Videu en Matroska 3D</comment>
+ <comment xml:lang="ca">vídeo Matroska 3D</comment>
+ <comment xml:lang="cs">3D video Matroska</comment>
+ <comment xml:lang="da">Matroska 3D-video</comment>
+ <comment xml:lang="de">Matroska 3D-Video</comment>
+ <comment xml:lang="el">Βίντεο 3Δ Matroska</comment>
+ <comment xml:lang="en_GB">Matroska 3D video</comment>
+ <comment xml:lang="es">vídeo Matroska en 3D</comment>
+ <comment xml:lang="eu">Matroska 3D bideoa</comment>
+ <comment xml:lang="fi">Matroska 3D-video</comment>
+ <comment xml:lang="fr">vidéo Matroska 3D</comment>
+ <comment xml:lang="ga">físeán Matroska 3D</comment>
+ <comment xml:lang="gl">Video Matroska 3D</comment>
+ <comment xml:lang="he">סרטון תלת ממדי מסוג Matroska</comment>
+ <comment xml:lang="hr">Matroška 3D video snimka</comment>
+ <comment xml:lang="hu">Matroska 3D videó</comment>
+ <comment xml:lang="ia">Video 3D Matroska</comment>
+ <comment xml:lang="id">Video 3D Matroska</comment>
+ <comment xml:lang="it">Video Matroska 3D</comment>
+ <comment xml:lang="kk">Matroska 3D видеосы</comment>
+ <comment xml:lang="ko">Matroska 3D 동영상</comment>
+ <comment xml:lang="oc">vidèo Matroska 3D</comment>
+ <comment xml:lang="pl">Plik wideo Matroska 3D</comment>
+ <comment xml:lang="pt">vídeo 3D Matroska</comment>
+ <comment xml:lang="pt_BR">Vídeo 3D Matroska</comment>
+ <comment xml:lang="ru">Видео Matroska 3D</comment>
+ <comment xml:lang="sk">3D video Matroska</comment>
+ <comment xml:lang="sl">Video datoteka Matroska 3D</comment>
+ <comment xml:lang="sr">Матрошкин 3Д видео</comment>
+ <comment xml:lang="sv">Matroska 3D-video</comment>
+ <comment xml:lang="tr">Matroska 3B video</comment>
+ <comment xml:lang="uk">відеокліп Matroska 3D</comment>
+ <comment xml:lang="zh_CN">Matroska 3D 视频</comment>
+ <comment xml:lang="zh_TW">Matroska 3D 視訊</comment>
+ <glob pattern="*.mk3d"/>
+ <sub-class-of type="application/x-matroska"/>
+ </mime-type>
+ <mime-type type="audio/x-matroska">
+ <comment>Matroska audio</comment>
+ <comment xml:lang="ar">سمعي Matroska</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Matroska</comment>
+ <comment xml:lang="bg">Аудио — Matroska</comment>
+ <comment xml:lang="ca">àudio de Matroska</comment>
+ <comment xml:lang="cs">zvuk Matroska</comment>
+ <comment xml:lang="da">Matroskalyd</comment>
+ <comment xml:lang="de">Matroska-Audio</comment>
+ <comment xml:lang="el">Ήχος Matroska</comment>
+ <comment xml:lang="en_GB">Matroska audio</comment>
+ <comment xml:lang="eo">Matroska-sondosiero</comment>
+ <comment xml:lang="es">sonido Matroska</comment>
+ <comment xml:lang="eu">Matroska audioa</comment>
+ <comment xml:lang="fi">Matroska-ääni</comment>
+ <comment xml:lang="fo">Matroska ljóður</comment>
+ <comment xml:lang="fr">audio Matroska</comment>
+ <comment xml:lang="ga">fuaim Matroska</comment>
+ <comment xml:lang="gl">son de Matroska</comment>
+ <comment xml:lang="he">שמע Matroska</comment>
+ <comment xml:lang="hr">Matroška zvučni zapis</comment>
+ <comment xml:lang="hu">Matroska hang</comment>
+ <comment xml:lang="ia">Audio Matroska</comment>
+ <comment xml:lang="id">Audio Matroska</comment>
+ <comment xml:lang="it">Audio Matroska</comment>
+ <comment xml:lang="ja">Matroska オーディオ</comment>
+ <comment xml:lang="ka">Matroska-ის აუდიო</comment>
+ <comment xml:lang="kk">Matroska аудиосы</comment>
+ <comment xml:lang="ko">Matroska 오디오</comment>
+ <comment xml:lang="lt">Matroska garso įrašas</comment>
+ <comment xml:lang="lv">Matroska audio</comment>
+ <comment xml:lang="nb">Matroska-lyd</comment>
+ <comment xml:lang="nl">Matroska-audio</comment>
+ <comment xml:lang="nn">Matroska-lyd</comment>
+ <comment xml:lang="oc">àudio Matroska</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Matroska</comment>
+ <comment xml:lang="pt">áudio Matroska</comment>
+ <comment xml:lang="pt_BR">Áudio Matroska</comment>
+ <comment xml:lang="ro">Audio Matroska</comment>
+ <comment xml:lang="ru">Аудио Matroska</comment>
+ <comment xml:lang="sk">Zvuk Matroska</comment>
+ <comment xml:lang="sl">Zvočna datoteka Matroska</comment>
+ <comment xml:lang="sq">Audio Matroska</comment>
+ <comment xml:lang="sr">Матрошкин звук</comment>
+ <comment xml:lang="sv">Matroska-ljud</comment>
+ <comment xml:lang="tr">Matroska ses</comment>
+ <comment xml:lang="uk">звук Matroska</comment>
+ <comment xml:lang="vi">Âm thanh Matroska</comment>
+ <comment xml:lang="zh_CN">Matroska 音频</comment>
+ <comment xml:lang="zh_TW">Matroska 音訊</comment>
+ <glob pattern="*.mka"/>
+ <sub-class-of type="application/x-matroska"/>
+ </mime-type>
+ <mime-type type="video/webm">
+ <comment>WebM video</comment>
+ <comment xml:lang="ar">WebM مرئي</comment>
+ <comment xml:lang="ast">Videu en WebM</comment>
+ <comment xml:lang="bg">Видео — WebM</comment>
+ <comment xml:lang="ca">vídeo WebM</comment>
+ <comment xml:lang="cs">video WebM</comment>
+ <comment xml:lang="da">WebM-video</comment>
+ <comment xml:lang="de">WebM-Video</comment>
+ <comment xml:lang="el">Βίντεο WebM</comment>
+ <comment xml:lang="en_GB">WebM video</comment>
+ <comment xml:lang="eo">WebM-video</comment>
+ <comment xml:lang="es">vídeo WebM</comment>
+ <comment xml:lang="eu">WebM bideoa</comment>
+ <comment xml:lang="fi">WebM-video</comment>
+ <comment xml:lang="fo">WebM video</comment>
+ <comment xml:lang="fr">vidéo WebM</comment>
+ <comment xml:lang="ga">físeán WebM</comment>
+ <comment xml:lang="gl">vídeo WebM</comment>
+ <comment xml:lang="he">וידאו WebM</comment>
+ <comment xml:lang="hr">WebM video snimka</comment>
+ <comment xml:lang="hu">WebM videó</comment>
+ <comment xml:lang="ia">Video WebM</comment>
+ <comment xml:lang="id">Video WebM</comment>
+ <comment xml:lang="it">Video WebM</comment>
+ <comment xml:lang="ja">WebM 動画</comment>
+ <comment xml:lang="kk">WebM видеосы</comment>
+ <comment xml:lang="ko">WebM 동영상</comment>
+ <comment xml:lang="lt">WebM vaizdo įrašas</comment>
+ <comment xml:lang="lv">WebM video</comment>
+ <comment xml:lang="nl">WebM video</comment>
+ <comment xml:lang="oc">vidèo WebM</comment>
+ <comment xml:lang="pl">Plik wideo WebM</comment>
+ <comment xml:lang="pt">vídeo WebM</comment>
+ <comment xml:lang="pt_BR">Vídeo WebM</comment>
+ <comment xml:lang="ro">Video WebM</comment>
+ <comment xml:lang="ru">Видео WebM</comment>
+ <comment xml:lang="sk">Video WebM</comment>
+ <comment xml:lang="sl">Video datoteka WebM</comment>
+ <comment xml:lang="sr">ВебМ видео</comment>
+ <comment xml:lang="sv">WebM-video</comment>
+ <comment xml:lang="tr">WebM video</comment>
+ <comment xml:lang="uk">відео WebM</comment>
+ <comment xml:lang="zh_CN">WebM 视频</comment>
+ <comment xml:lang="zh_TW">WebM 視訊</comment>
+ <glob pattern="*.webm"/>
+ <magic priority="50">
+
+ <match value="0x1a45dfa3" type="big32" offset="0">
+
+ <match value="0x4282" type="big16" offset="5:65">
+
+ <match value="webm" type="string" offset="8:75"/>
+ </match>
+ </match>
+ </magic>
+ </mime-type>
+ <mime-type type="audio/webm">
+ <comment>WebM audio</comment>
+ <comment xml:lang="ar">WebM سمعي</comment>
+ <comment xml:lang="bg">Аудио — WebM</comment>
+ <comment xml:lang="ca">àudio de WebM</comment>
+ <comment xml:lang="cs">zvuk WebM</comment>
+ <comment xml:lang="da">WebM-lyd</comment>
+ <comment xml:lang="de">WebM-Audio</comment>
+ <comment xml:lang="el">Ήχος WebM</comment>
+ <comment xml:lang="en_GB">WebM audio</comment>
+ <comment xml:lang="eo">WebM-sondosiero</comment>
+ <comment xml:lang="es">sonido WebM</comment>
+ <comment xml:lang="eu">WebM audioa</comment>
+ <comment xml:lang="fi">WebM-ääni</comment>
+ <comment xml:lang="fo">WebM ljóður</comment>
+ <comment xml:lang="fr">audio WebM</comment>
+ <comment xml:lang="ga">fuaim WebM</comment>
+ <comment xml:lang="gl">son WebM</comment>
+ <comment xml:lang="he">שמע WebM</comment>
+ <comment xml:lang="hr">WebM zvučni zapis</comment>
+ <comment xml:lang="hu">WebM hang</comment>
+ <comment xml:lang="ia">Audio WebM</comment>
+ <comment xml:lang="id">Audio WebM</comment>
+ <comment xml:lang="it">Audio WebM</comment>
+ <comment xml:lang="ja">WebM オーディオ</comment>
+ <comment xml:lang="kk">WebM аудиосы</comment>
+ <comment xml:lang="ko">WebM 오디오</comment>
+ <comment xml:lang="lt">WebM garso įrašas</comment>
+ <comment xml:lang="lv">WebM audio</comment>
+ <comment xml:lang="nl">WebM audio</comment>
+ <comment xml:lang="oc">àudio WebM</comment>
+ <comment xml:lang="pl">Plik dźwiękowy WebM</comment>
+ <comment xml:lang="pt">áudio WebM</comment>
+ <comment xml:lang="pt_BR">Áudio WebM</comment>
+ <comment xml:lang="ro">Audio WebM</comment>
+ <comment xml:lang="ru">Аудио WebM</comment>
+ <comment xml:lang="sk">Zvuk WebM</comment>
+ <comment xml:lang="sl">Zvočna datoteka WebM</comment>
+ <comment xml:lang="sr">ВебМ звук</comment>
+ <comment xml:lang="sv">WebM-ljud</comment>
+ <comment xml:lang="tr">WebM sesi</comment>
+ <comment xml:lang="uk">звук WebM</comment>
+ <comment xml:lang="zh_CN">WebM 音频</comment>
+ <comment xml:lang="zh_TW">WebM 音訊</comment>
+ <sub-class-of type="video/webm"/>
+ </mime-type>
+ <mime-type type="application/x-mimearchive">
+ <comment>MHTML web archive</comment>
+ <comment xml:lang="ca">arxiu web MHTML</comment>
+ <comment xml:lang="cs">webový archiv MHTML</comment>
+ <comment xml:lang="da">MHTML-netarkiv</comment>
+ <comment xml:lang="de">MHTML-Webarchiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο ιστού MHTML</comment>
+ <comment xml:lang="en_GB">MHTML web archive</comment>
+ <comment xml:lang="es">archivador web MHTML</comment>
+ <comment xml:lang="eu">MHTML web artxiboa</comment>
+ <comment xml:lang="fi">MHTML-kooste</comment>
+ <comment xml:lang="fr">archive web MHTML</comment>
+ <comment xml:lang="ga">cartlann ghréasáin MHTML</comment>
+ <comment xml:lang="gl">Arquivo web MHTML</comment>
+ <comment xml:lang="he">ארכיון רשת MHTML</comment>
+ <comment xml:lang="hr">MHTML web arhiva</comment>
+ <comment xml:lang="hu">MHTML webarchívum</comment>
+ <comment xml:lang="ia">Archivo web MHTML</comment>
+ <comment xml:lang="id">Arsip web MHTML</comment>
+ <comment xml:lang="it">Archivio web MHTML</comment>
+ <comment xml:lang="ja">MHTML Web アーカイブ</comment>
+ <comment xml:lang="kk">MHTML веб архиві</comment>
+ <comment xml:lang="ko">MHTML 웹 보관 파일</comment>
+ <comment xml:lang="lv">MHTML tīmekļa arhīvs</comment>
+ <comment xml:lang="oc">archiu web MHTML</comment>
+ <comment xml:lang="pl">Archiwum witryny MHTML</comment>
+ <comment xml:lang="pt">arquivo web MHTML</comment>
+ <comment xml:lang="pt_BR">Pacote web MHTML</comment>
+ <comment xml:lang="ru">Веб-архив MHTML</comment>
+ <comment xml:lang="sk">Webový archív MHTML</comment>
+ <comment xml:lang="sl">Spletni arhiv MHTML</comment>
+ <comment xml:lang="sr">МХТМЛ веб архива</comment>
+ <comment xml:lang="sv">MHTML-webbarkiv</comment>
+ <comment xml:lang="tr">MHTML web arşivi</comment>
+ <comment xml:lang="uk">вебархів MHTML</comment>
+ <comment xml:lang="zh_CN">MHTML 网络归档</comment>
+ <comment xml:lang="zh_TW">MHTML 網頁封存檔</comment>
+ <acronym>MHTML</acronym>
+ <expanded-acronym>MIME HTML</expanded-acronym>
+ <glob pattern="*.mhtml"/>
+ <glob pattern="*.mht"/>
+ <sub-class-of type="multipart/related"/>
+ </mime-type>
+ <mime-type type="application/mxf">
+ <comment>MXF video</comment>
+ <comment xml:lang="ar">MXF مرئي</comment>
+ <comment xml:lang="ast">Videu en MXF</comment>
+ <comment xml:lang="bg">Видео — MXF</comment>
+ <comment xml:lang="ca">vídeo MXF</comment>
+ <comment xml:lang="cs">video MXF</comment>
+ <comment xml:lang="da">MXF-video</comment>
+ <comment xml:lang="de">MXF-Video</comment>
+ <comment xml:lang="el">Βίντεο MXF</comment>
+ <comment xml:lang="en_GB">MXF video</comment>
+ <comment xml:lang="eo">MXF-video</comment>
+ <comment xml:lang="es">vídeo MXF</comment>
+ <comment xml:lang="eu">MXF bideoa</comment>
+ <comment xml:lang="fi">MXF-video</comment>
+ <comment xml:lang="fo">MXF video</comment>
+ <comment xml:lang="fr">vidéo MXF</comment>
+ <comment xml:lang="ga">físeán MXF</comment>
+ <comment xml:lang="gl">vídeo MXF</comment>
+ <comment xml:lang="he">וידאו MXF</comment>
+ <comment xml:lang="hr">MXF video snimka</comment>
+ <comment xml:lang="hu">MXF videó</comment>
+ <comment xml:lang="ia">Video MXF</comment>
+ <comment xml:lang="id">Video MXF</comment>
+ <comment xml:lang="it">Video MXF</comment>
+ <comment xml:lang="ja">MXF 動画</comment>
+ <comment xml:lang="ka">MXF ვიდეო</comment>
+ <comment xml:lang="kk">MXF видеосы</comment>
+ <comment xml:lang="ko">MXF 동영상</comment>
+ <comment xml:lang="lt">MXF vaizdo įrašas</comment>
+ <comment xml:lang="lv">MXF video</comment>
+ <comment xml:lang="nl">MXF video</comment>
+ <comment xml:lang="oc">vidèo MXF</comment>
+ <comment xml:lang="pl">Plik wideo MXF</comment>
+ <comment xml:lang="pt">vídeo MXF</comment>
+ <comment xml:lang="pt_BR">Vídeo MXF</comment>
+ <comment xml:lang="ro">Video MXF</comment>
+ <comment xml:lang="ru">Видео MXF</comment>
+ <comment xml:lang="sk">Video MXF</comment>
+ <comment xml:lang="sl">Video datoteka MXF</comment>
+ <comment xml:lang="sr">МИксФ видео</comment>
+ <comment xml:lang="sv">MXF-video</comment>
+ <comment xml:lang="tr">MXF video</comment>
+ <comment xml:lang="uk">відеокліп MXF</comment>
+ <comment xml:lang="zh_CN">MXF 视频</comment>
+ <comment xml:lang="zh_TW">MXF 視訊</comment>
+ <acronym>MXF</acronym>
+ <expanded-acronym>Material Exchange Format</expanded-acronym>
+ <generic-icon name="video-x-generic"/>
+ <magic priority="50">
+ <match value="\x06\x0e\x2b\x34\x02\x05\x01\x01\x0d\x01\x02\x01\x01\x02" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.mxf"/>
+ </mime-type>
+ <mime-type type="text/x-ocl">
+ <comment>OCL file</comment>
+ <comment xml:lang="ar">ملف OCL</comment>
+ <comment xml:lang="be@latin">Fajł OCL</comment>
+ <comment xml:lang="bg">Файл — OCL</comment>
+ <comment xml:lang="ca">fitxer OCL</comment>
+ <comment xml:lang="cs">soubor OCL</comment>
+ <comment xml:lang="da">OCL-fil</comment>
+ <comment xml:lang="de">OCL-Datei</comment>
+ <comment xml:lang="el">Αρχείο OCL</comment>
+ <comment xml:lang="en_GB">OCL file</comment>
+ <comment xml:lang="eo">OCL-dosiero</comment>
+ <comment xml:lang="es">archivo OCL</comment>
+ <comment xml:lang="eu">OCL fitxategia</comment>
+ <comment xml:lang="fi">OCL-tiedosto</comment>
+ <comment xml:lang="fo">OCL fíla</comment>
+ <comment xml:lang="fr">fichier OCL</comment>
+ <comment xml:lang="ga">comhad OCL</comment>
+ <comment xml:lang="gl">ficheiro OCL</comment>
+ <comment xml:lang="he">קובץ OCL</comment>
+ <comment xml:lang="hr">OCL datoteka</comment>
+ <comment xml:lang="hu">OCL fájl</comment>
+ <comment xml:lang="ia">File OCL</comment>
+ <comment xml:lang="id">Berkas OCL</comment>
+ <comment xml:lang="it">File OCL</comment>
+ <comment xml:lang="ja">OCL ファイル</comment>
+ <comment xml:lang="kk">OCL файлы</comment>
+ <comment xml:lang="ko">OCL 파일</comment>
+ <comment xml:lang="lt">OCL failas</comment>
+ <comment xml:lang="lv">OCL datne</comment>
+ <comment xml:lang="nb">OCL-fil</comment>
+ <comment xml:lang="nl">OCL-bestand</comment>
+ <comment xml:lang="nn">OCL-fil</comment>
+ <comment xml:lang="oc">fichièr OCL</comment>
+ <comment xml:lang="pl">Plik OCL</comment>
+ <comment xml:lang="pt">ficheiro OCL</comment>
+ <comment xml:lang="pt_BR">Arquivo OCL</comment>
+ <comment xml:lang="ro">Fișier OCL</comment>
+ <comment xml:lang="ru">Файл OCL</comment>
+ <comment xml:lang="sk">Súbor OCL</comment>
+ <comment xml:lang="sl">Datoteka OCL</comment>
+ <comment xml:lang="sq">File OCL</comment>
+ <comment xml:lang="sr">ОЦЛ датотека</comment>
+ <comment xml:lang="sv">OCL-fil</comment>
+ <comment xml:lang="tr">OCL dosyası</comment>
+ <comment xml:lang="uk">файл OCL</comment>
+ <comment xml:lang="vi">Tập tin OCL</comment>
+ <comment xml:lang="zh_CN">OCL 文件</comment>
+ <comment xml:lang="zh_TW">OCL 檔</comment>
+ <acronym>OCL</acronym>
+ <expanded-acronym>Object Constraint Language</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.ocl"/>
+ </mime-type>
+ <mime-type type="text/x-cobol">
+ <comment>COBOL source file</comment>
+ <comment xml:lang="bg">Изходен код — COBOL</comment>
+ <comment xml:lang="ca">codi font en COBOL</comment>
+ <comment xml:lang="cs">zdrojový soubor COBOL</comment>
+ <comment xml:lang="da">COBOL-kildefil</comment>
+ <comment xml:lang="de">COBOL-Quelldatei</comment>
+ <comment xml:lang="el">Πηγαίο αρχείο COBOL</comment>
+ <comment xml:lang="en_GB">COBOL source file</comment>
+ <comment xml:lang="eo">COBOL-fontdosiero</comment>
+ <comment xml:lang="es">archivo fuente de COBOL</comment>
+ <comment xml:lang="eu">COBOL iturburu-kodea</comment>
+ <comment xml:lang="fi">COBOL-lähdekoodi</comment>
+ <comment xml:lang="fr">fichier source COBOL</comment>
+ <comment xml:lang="ga">cód foinseach COBOL</comment>
+ <comment xml:lang="gl">ficheiro fonte de COBOL</comment>
+ <comment xml:lang="he">קובץ מקור של COBOL</comment>
+ <comment xml:lang="hr">COBOL izvorna datoteka</comment>
+ <comment xml:lang="hu">COBOL forrásfájl</comment>
+ <comment xml:lang="ia">File de codice fonte COBOL</comment>
+ <comment xml:lang="id">Berkas sumber COBOL</comment>
+ <comment xml:lang="it">File sorgente COBOL</comment>
+ <comment xml:lang="ja">COBOL ソースファイル</comment>
+ <comment xml:lang="ka">COBOL-ის საწყისი ფაილი</comment>
+ <comment xml:lang="kk">COBOL бастапқы коды</comment>
+ <comment xml:lang="ko">COBOL 소스 파일</comment>
+ <comment xml:lang="lv">COBOL pirmkods</comment>
+ <comment xml:lang="nl">COBOL bronbestand</comment>
+ <comment xml:lang="oc">fichièr font COBOL</comment>
+ <comment xml:lang="pl">Plik źródłowy COBOL</comment>
+ <comment xml:lang="pt">ficheiro origem COBOL</comment>
+ <comment xml:lang="pt_BR">Arquivo de código-fonte em COBOL</comment>
+ <comment xml:lang="ru">Файл исходного кода на COBOL</comment>
+ <comment xml:lang="sk">Zdrojový súbor COBOLu</comment>
+ <comment xml:lang="sl">Izvorna koda COBOL</comment>
+ <comment xml:lang="sr">изворна датотека КОБОЛ-а</comment>
+ <comment xml:lang="sv">COBOL-källkodsfil</comment>
+ <comment xml:lang="tr">COBOL kaynak dosyası</comment>
+ <comment xml:lang="uk">вихідний код мовою COBOL</comment>
+ <comment xml:lang="zh_CN">COBOL 源文件</comment>
+ <comment xml:lang="zh_TW">COBOL 源檔</comment>
+ <acronym>COBOL</acronym>
+ <expanded-acronym>COmmon Business Oriented Language</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.cbl"/>
+ <glob pattern="*.cob"/>
+ </mime-type>
+ <mime-type type="application/x-mobipocket-ebook">
+ <comment>Mobipocket e-book</comment>
+ <comment xml:lang="bg">Е-книга — Mobipocket</comment>
+ <comment xml:lang="ca">llibre electrònic Mobipocket </comment>
+ <comment xml:lang="cs">elektronická kniha Mobipocket</comment>
+ <comment xml:lang="da">Mobipocket e-bog</comment>
+ <comment xml:lang="de">Mobipocket E-Book</comment>
+ <comment xml:lang="el">Ηλεκτρονικό βιβλίο Mobipocket</comment>
+ <comment xml:lang="en_GB">Mobipocket e-book</comment>
+ <comment xml:lang="es">libro electrónico de Mobipocket</comment>
+ <comment xml:lang="eu">Mobipocket liburua</comment>
+ <comment xml:lang="fi">Mobipocket e-kirja</comment>
+ <comment xml:lang="fr">livre numérique Mobipocket</comment>
+ <comment xml:lang="ga">r-leabhar Mobipocket</comment>
+ <comment xml:lang="gl">E-book Mobipocket</comment>
+ <comment xml:lang="he">ספר אלקטרוני של Mobipocket</comment>
+ <comment xml:lang="hr">Mobipocket e-knjiga</comment>
+ <comment xml:lang="hu">Mobipocket e-könyv</comment>
+ <comment xml:lang="ia">E-libro Mobipocket</comment>
+ <comment xml:lang="id">e-book Mobipocket</comment>
+ <comment xml:lang="it">E-book Mobipocket</comment>
+ <comment xml:lang="ja">Mobipocket 電子書籍</comment>
+ <comment xml:lang="ka">Mobipocket-ის ელწიგნი</comment>
+ <comment xml:lang="kk">Mobipocket эл. кітабы</comment>
+ <comment xml:lang="ko">Mobipocket 전자책</comment>
+ <comment xml:lang="lv">Mobipocket e-grāmata</comment>
+ <comment xml:lang="nl">Mobipocket e-book</comment>
+ <comment xml:lang="oc">libre numeric Mobipocket</comment>
+ <comment xml:lang="pl">E-book Mobipocket</comment>
+ <comment xml:lang="pt">ebook Mobipocket</comment>
+ <comment xml:lang="pt_BR">E-book Mobipocket</comment>
+ <comment xml:lang="ru">Электронная книга Mobipocket</comment>
+ <comment xml:lang="sk">E-kniha Mobipocket</comment>
+ <comment xml:lang="sl">e-knjiga Mobipocket</comment>
+ <comment xml:lang="sr">Мобипокет ел. књига</comment>
+ <comment xml:lang="sv">Mobipocket-e-bok</comment>
+ <comment xml:lang="tr">Mobipocket e-kitap</comment>
+ <comment xml:lang="uk">електронна книга Mobipocket</comment>
+ <comment xml:lang="zh_CN">Mobipocket 电子书</comment>
+ <comment xml:lang="zh_TW">Mobipocket e-book</comment>
+ <sub-class-of type="application/vnd.palm"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.mobi"/>
+ <glob pattern="*.prc"/>
+ <magic priority="30">
+
+ <match value="TEXtREAd" type="string" offset="60"/>
+ </magic>
+ <magic priority="80">
+ <match value="BOOKMOBI" type="string" offset="60"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-mif">
+ <comment>Adobe FrameMaker MIF document</comment>
+ <comment xml:lang="ar">مستند أدوبي الصانع للإطارات MIF</comment>
+ <comment xml:lang="ast">Documentu MIF d'Adobe FrameMaker</comment>
+ <comment xml:lang="be@latin">Dakument Adobe FrameMaker MIF</comment>
+ <comment xml:lang="bg">Документ — Adobe FrameMaker MIF</comment>
+ <comment xml:lang="ca">document MIF d'Adobe FrameMaker</comment>
+ <comment xml:lang="cs">dokument Adobe FrameMaker MIF</comment>
+ <comment xml:lang="da">Adobe FrameMaker MIF-dokument</comment>
+ <comment xml:lang="de">Adobe-FrameMaker-MIF-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο MIF του Adobe FrameMaker </comment>
+ <comment xml:lang="en_GB">Adobe FrameMaker MIF document</comment>
+ <comment xml:lang="eo">MIF-dokumento de Adobe FrameMaker</comment>
+ <comment xml:lang="es">documento MIF de Adobe FrameMaker</comment>
+ <comment xml:lang="eu">Adobe FrameMaker-en MIF dokumentua</comment>
+ <comment xml:lang="fi">Adobe FrameMaker MIF -asiakirja</comment>
+ <comment xml:lang="fo">Adobe FrameMaker MIF skjal</comment>
+ <comment xml:lang="fr">document MIF Adobe FrameMaker</comment>
+ <comment xml:lang="ga">cáipéis MIF Adobe FrameMaker</comment>
+ <comment xml:lang="gl">documento MIF de Adobe FrameMaker</comment>
+ <comment xml:lang="he">מסמך MIF של Adobe FrameMaker</comment>
+ <comment xml:lang="hr">Adobe FrameMaker MIF dokument</comment>
+ <comment xml:lang="hu">Adobe FrameMaker MIF-dokumentum</comment>
+ <comment xml:lang="ia">Documento MIF de Adobe FrameMaker</comment>
+ <comment xml:lang="id">Dokumen Adobe FrameMaker MIF</comment>
+ <comment xml:lang="it">Documento MIF Adobe FrameMaker</comment>
+ <comment xml:lang="ja">Adobe FrameMaker MIF ドキュメント</comment>
+ <comment xml:lang="ka">Adobe FrameMaker-ის MIF დოკუმენტი</comment>
+ <comment xml:lang="kk">Adobe FrameMaker MIF құжаты</comment>
+ <comment xml:lang="ko">Adobe 프레임메이커 MIF 문서</comment>
+ <comment xml:lang="lt">Adobe FrameMaker MIF dokumentas</comment>
+ <comment xml:lang="lv">Adobe FrameMaker MIF dokuments</comment>
+ <comment xml:lang="nb">Adobe FrameMaker MIF-dokument</comment>
+ <comment xml:lang="nl">Adobe FrameMaker MIF-document</comment>
+ <comment xml:lang="nn">Adobe FrameMaker MIF-dokument</comment>
+ <comment xml:lang="oc">document MIF Adobe FrameMaker</comment>
+ <comment xml:lang="pl">Dokument MIF Adobe FrameMaker</comment>
+ <comment xml:lang="pt">documento Adobe FrameMaker MIF</comment>
+ <comment xml:lang="pt_BR">Documento MIF do Adobe FrameMaker</comment>
+ <comment xml:lang="ro">Document Adobe FrameMaker MIF</comment>
+ <comment xml:lang="ru">Документ Adobe FrameMaker MIF</comment>
+ <comment xml:lang="sk">Dokument Adobe FrameMaker MIF</comment>
+ <comment xml:lang="sl">Dokument Adobe FrameMaker MIF</comment>
+ <comment xml:lang="sq">Dokument MIF Adobe FrameMaker</comment>
+ <comment xml:lang="sr">Адобе Фрејм Мејкер МИФ документ</comment>
+ <comment xml:lang="sv">Adobe FrameMaker MIF-dokument</comment>
+ <comment xml:lang="tr">Adobe FrameMaker MIF belgesi</comment>
+ <comment xml:lang="uk">документ Adobe FrameMaker MIF</comment>
+ <comment xml:lang="vi">Tài liệu Adobe FrameMaker MIF</comment>
+ <comment xml:lang="zh_CN">Adobe FrameMaker MIF 文档</comment>
+ <comment xml:lang="zh_TW">Adobe FrameMaker MIF 文件</comment>
+ <glob pattern="*.mif"/>
+ </mime-type>
+ <mime-type type="application/x-mozilla-bookmarks">
+ <comment>Mozilla bookmarks</comment>
+ <comment xml:lang="ar">علامات موزيلا</comment>
+ <comment xml:lang="be@latin">Zakładki Mozilla</comment>
+ <comment xml:lang="bg">Отметки — Mozilla</comment>
+ <comment xml:lang="ca">llista d'adreces d'interès de Mozilla</comment>
+ <comment xml:lang="cs">záložky Mozilla</comment>
+ <comment xml:lang="da">Mozillabogmærker</comment>
+ <comment xml:lang="de">Mozilla-Lesezeichen</comment>
+ <comment xml:lang="el">Σελιδοδείκτες Mozilla</comment>
+ <comment xml:lang="en_GB">Mozilla bookmarks</comment>
+ <comment xml:lang="eo">Mozilla-legosignoj</comment>
+ <comment xml:lang="es">marcadores de Mozilla</comment>
+ <comment xml:lang="eu">Mozillako laster-markak</comment>
+ <comment xml:lang="fi">Mozilla-kirjanmerkit</comment>
+ <comment xml:lang="fo">Mozilla bókamerki</comment>
+ <comment xml:lang="fr">marque-pages Mozilla</comment>
+ <comment xml:lang="ga">leabharmharcanna Mozilla</comment>
+ <comment xml:lang="gl">Marcadores de Mozilla</comment>
+ <comment xml:lang="he">סימניה של Mozilla</comment>
+ <comment xml:lang="hr">Mozilla zabilješke</comment>
+ <comment xml:lang="hu">Mozilla-könyvjelzők</comment>
+ <comment xml:lang="ia">Marcapaginas Mozilla</comment>
+ <comment xml:lang="id">Bookmark Mozilla</comment>
+ <comment xml:lang="it">Segnalibri Mozilla</comment>
+ <comment xml:lang="ja">Mozilla ブックマーク</comment>
+ <comment xml:lang="kk">Mozilla бетбелгілері</comment>
+ <comment xml:lang="ko">모질라 책갈피</comment>
+ <comment xml:lang="lt">Mozilla žymelės</comment>
+ <comment xml:lang="lv">Mozilla grāmatzīmes</comment>
+ <comment xml:lang="ms">Tandabuku Mozilla</comment>
+ <comment xml:lang="nb">Mozilla-bokmerker</comment>
+ <comment xml:lang="nl">Mozilla-bladwijzers</comment>
+ <comment xml:lang="nn">Mozilla-bokmerker</comment>
+ <comment xml:lang="oc">marcapaginas Mozilla</comment>
+ <comment xml:lang="pl">Zakładki Mozilla</comment>
+ <comment xml:lang="pt">marcadores do Mozilla</comment>
+ <comment xml:lang="pt_BR">Favoritos do Mozilla</comment>
+ <comment xml:lang="ro">Semne de carte Mozilla</comment>
+ <comment xml:lang="ru">Закладки Mozilla</comment>
+ <comment xml:lang="sk">Záložky Mozilla</comment>
+ <comment xml:lang="sl">Datoteka zaznamkov Mozilla</comment>
+ <comment xml:lang="sq">Libërshënues Mozilla</comment>
+ <comment xml:lang="sr">Мозилини обележивачи</comment>
+ <comment xml:lang="sv">Mozilla-bokmärken</comment>
+ <comment xml:lang="tr">Mozilla yer imleri</comment>
+ <comment xml:lang="uk">закладки Mozilla</comment>
+ <comment xml:lang="vi">Liên kết đã lưu Mozilla</comment>
+ <comment xml:lang="zh_CN">Mozilla 书签</comment>
+ <comment xml:lang="zh_TW">Mozilla 書籤</comment>
+ <sub-class-of type="text/html"/>
+ <generic-icon name="text-html"/>
+ <magic priority="80">
+ <match value="&lt;!DOCTYPE NETSCAPE-Bookmark-file-1&gt;" type="string" offset="0:64"/>
+ </magic>
+ <alias type="application/x-netscape-bookmarks"/>
+ </mime-type>
+ <mime-type type="application/x-ms-dos-executable">
+ <comment>DOS/Windows executable</comment>
+ <comment xml:lang="ar">تنفيذي DOS/Windows</comment>
+ <comment xml:lang="be@latin">Vykonvalny fajł DOS/Windows</comment>
+ <comment xml:lang="bg">Изпълним файл — DOS/Windows</comment>
+ <comment xml:lang="ca">executable de DOS o de Windows</comment>
+ <comment xml:lang="cs">spustitelný soubor pro DOS/Windows</comment>
+ <comment xml:lang="da">DOS-/Windowskørbar</comment>
+ <comment xml:lang="de">DOS/Windows-Programmdatei</comment>
+ <comment xml:lang="el">Εκτελέσιμο DOS/Windows</comment>
+ <comment xml:lang="en_GB">DOS/Windows executable</comment>
+ <comment xml:lang="eo">DOS/Windows-plenumebla</comment>
+ <comment xml:lang="es">ejecutable de DOS/Windows</comment>
+ <comment xml:lang="eu">DOS/Windows-eko exekutagarria</comment>
+ <comment xml:lang="fi">DOS/Windows-ohjelma</comment>
+ <comment xml:lang="fo">DOS/Windows inningarfør</comment>
+ <comment xml:lang="fr">exécutable DOS/Windows</comment>
+ <comment xml:lang="ga">comhad inrite DOS/Windows</comment>
+ <comment xml:lang="gl">executábel de DOS/Windows</comment>
+ <comment xml:lang="he">קובץ בר־הרצה של DOS/חלונות</comment>
+ <comment xml:lang="hr">DOS/Windows izvršna datoteka</comment>
+ <comment xml:lang="hu">DOS/Windows futtatható</comment>
+ <comment xml:lang="ia">Executabile DOS/Windows</comment>
+ <comment xml:lang="id">DOS/Windows dapat dieksekusi</comment>
+ <comment xml:lang="it">Eseguibile DOS/Windows</comment>
+ <comment xml:lang="ja">DOS/Windows 実行ファイル</comment>
+ <comment xml:lang="ka">DOS/Windows გაშვებადი ფაილი</comment>
+ <comment xml:lang="kk">DOS/Windows орындалатын файлы</comment>
+ <comment xml:lang="ko">DOS/Windows 실행 파일</comment>
+ <comment xml:lang="lt">DOS/Windows vykdomasis failas</comment>
+ <comment xml:lang="lv">DOS/Windows izpildāmais</comment>
+ <comment xml:lang="ms">Bolehlaksana DOS/Windows</comment>
+ <comment xml:lang="nb">Kjørbar fil for DOS/Windows</comment>
+ <comment xml:lang="nl">DOS/Windows-uitvoerbaar bestand</comment>
+ <comment xml:lang="nn">DOS/Windows køyrbar fil</comment>
+ <comment xml:lang="oc">executable DOS/Windows</comment>
+ <comment xml:lang="pl">Program DOS/Windows</comment>
+ <comment xml:lang="pt">executável DOS/Windows</comment>
+ <comment xml:lang="pt_BR">Executável do DOS/Windows</comment>
+ <comment xml:lang="ro">Executabil DOS/Windows</comment>
+ <comment xml:lang="ru">Исполняемый файл DOS/Windows</comment>
+ <comment xml:lang="sk">Spustiteľný súbor pre DOS/Windows</comment>
+ <comment xml:lang="sl">Izvedljiva datoteka DOS/Windows</comment>
+ <comment xml:lang="sq">I ekzekutueshëm DOS/Windows</comment>
+ <comment xml:lang="sr">ДОС/Виндоуз извршна</comment>
+ <comment xml:lang="sv">Körbar DOS/Windows-fil</comment>
+ <comment xml:lang="tr">DOS/Windows çalıştırılabilir</comment>
+ <comment xml:lang="uk">виконуваний файл DOS/Windows</comment>
+ <comment xml:lang="vi">Tập tin có thực hiện được DOS/Windows</comment>
+ <comment xml:lang="zh_CN">DOS/Windows 可执行文件</comment>
+ <comment xml:lang="zh_TW">DOS/Windows 可執行檔</comment>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="MZ" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.exe"/>
+ </mime-type>
+ <mime-type type="application/x-mswinurl">
+ <comment>Internet shortcut</comment>
+ <comment xml:lang="ar">اختصار الإنترنت</comment>
+ <comment xml:lang="be@latin">Sieciŭnaja spasyłka</comment>
+ <comment xml:lang="bg">Адрес в Интернет</comment>
+ <comment xml:lang="ca">drecera d'Internet</comment>
+ <comment xml:lang="cs">odkaz do Internetu</comment>
+ <comment xml:lang="da">Internetgenvej</comment>
+ <comment xml:lang="de">Internet-Verweis</comment>
+ <comment xml:lang="el">Συντόμευση διαδικτύου</comment>
+ <comment xml:lang="en_GB">Internet shortcut</comment>
+ <comment xml:lang="es">acceso directo a Internet</comment>
+ <comment xml:lang="eu">Interneteko lasterbidea</comment>
+ <comment xml:lang="fi">Internet-pikakuvake</comment>
+ <comment xml:lang="fo">Alnetssnarvegur</comment>
+ <comment xml:lang="fr">raccourci Internet</comment>
+ <comment xml:lang="ga">aicearra Idirlín</comment>
+ <comment xml:lang="gl">atallo de Internet</comment>
+ <comment xml:lang="he">קיצור דרך של האינטרנט</comment>
+ <comment xml:lang="hr">Internetski prečac</comment>
+ <comment xml:lang="hu">Internetes indítóikon</comment>
+ <comment xml:lang="ia">Ligamine Internet</comment>
+ <comment xml:lang="id">Jalan pintas Internet</comment>
+ <comment xml:lang="it">Scorciatoia Internet</comment>
+ <comment xml:lang="ja">インターネットショートカット</comment>
+ <comment xml:lang="kk">Интернет сілтемесі</comment>
+ <comment xml:lang="ko">인터넷 바로 가기</comment>
+ <comment xml:lang="lt">Interneto nuoroda</comment>
+ <comment xml:lang="lv">Interneta īsceļš</comment>
+ <comment xml:lang="nb">Internettsnarvei</comment>
+ <comment xml:lang="nl">internetkoppeling</comment>
+ <comment xml:lang="nn">Internett-snarveg</comment>
+ <comment xml:lang="oc">acorchi Internet</comment>
+ <comment xml:lang="pl">Skrót internetowy</comment>
+ <comment xml:lang="pt">atalho da Internet</comment>
+ <comment xml:lang="pt_BR">Atalho da Internet</comment>
+ <comment xml:lang="ro">Scurtătură Internet</comment>
+ <comment xml:lang="ru">Интернет-ссылка</comment>
+ <comment xml:lang="sk">Internetový odkaz</comment>
+ <comment xml:lang="sl">Internetna bližnjica</comment>
+ <comment xml:lang="sq">Shkurtim internet</comment>
+ <comment xml:lang="sr">интернет пречица</comment>
+ <comment xml:lang="sv">Internetgenväg</comment>
+ <comment xml:lang="tr">İnternet kısayolu</comment>
+ <comment xml:lang="uk">інтернет-посилання</comment>
+ <comment xml:lang="vi">Lối tắt Internet</comment>
+ <comment xml:lang="zh_CN">Internet 快捷方式</comment>
+ <comment xml:lang="zh_TW">網際網路捷徑</comment>
+ <magic priority="50">
+ <match value="InternetShortcut" type="string" offset="1"/>
+ <match value="DEFAULT" type="string" offset="1">
+ <match value="BASEURL=" type="string" offset="11"/>
+ </match>
+ </magic>
+ <glob pattern="*.url"/>
+ </mime-type>
+ <mime-type type="application/x-mswrite">
+ <comment>WRI document</comment>
+ <comment xml:lang="ar">مستند WRI</comment>
+ <comment xml:lang="ast">Documentu WRI</comment>
+ <comment xml:lang="be@latin">Dakument WRI</comment>
+ <comment xml:lang="bg">Документ — WRI</comment>
+ <comment xml:lang="ca">document WRI</comment>
+ <comment xml:lang="cs">dokument WRI</comment>
+ <comment xml:lang="da">WRI-dokument</comment>
+ <comment xml:lang="de">WRI-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο WRI</comment>
+ <comment xml:lang="en_GB">WRI document</comment>
+ <comment xml:lang="eo">WRI-dokumento</comment>
+ <comment xml:lang="es">documento WRI</comment>
+ <comment xml:lang="eu">WRI dokumentua</comment>
+ <comment xml:lang="fi">WRI-asiakirja</comment>
+ <comment xml:lang="fo">WRI skjal</comment>
+ <comment xml:lang="fr">document WRI</comment>
+ <comment xml:lang="ga">cáipéis WRI</comment>
+ <comment xml:lang="gl">documento WRI</comment>
+ <comment xml:lang="he">מסמך WRI</comment>
+ <comment xml:lang="hr">WRI dokument</comment>
+ <comment xml:lang="hu">WRI dokumentum</comment>
+ <comment xml:lang="ia">Documento WRI</comment>
+ <comment xml:lang="id">Dokumen WRI</comment>
+ <comment xml:lang="it">Documento WRI</comment>
+ <comment xml:lang="ja">WRI ドキュメント</comment>
+ <comment xml:lang="kk">WRI құжаты</comment>
+ <comment xml:lang="ko">WRI 문서</comment>
+ <comment xml:lang="lt">WRI dokumentas</comment>
+ <comment xml:lang="lv">WRI dokuments</comment>
+ <comment xml:lang="nb">WRI-dokument</comment>
+ <comment xml:lang="nl">WRI-document</comment>
+ <comment xml:lang="nn">WRI-dokument</comment>
+ <comment xml:lang="oc">document WRI</comment>
+ <comment xml:lang="pl">Dokument WRI</comment>
+ <comment xml:lang="pt">documento WRI</comment>
+ <comment xml:lang="pt_BR">Documento WRI</comment>
+ <comment xml:lang="ro">Document WRI</comment>
+ <comment xml:lang="ru">Документ WRI</comment>
+ <comment xml:lang="sk">Dokument WRI</comment>
+ <comment xml:lang="sl">Dokument WRI</comment>
+ <comment xml:lang="sq">Dokument WRI</comment>
+ <comment xml:lang="sr">ВРИ документ</comment>
+ <comment xml:lang="sv">WRI-dokument</comment>
+ <comment xml:lang="tr">WRI belgesi</comment>
+ <comment xml:lang="uk">документ WRI</comment>
+ <comment xml:lang="vi">Tài liệu WRI</comment>
+ <comment xml:lang="zh_CN">WRI 文档</comment>
+ <comment xml:lang="zh_TW">WRI 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.wri"/>
+ </mime-type>
+ <mime-type type="application/x-msx-rom">
+ <comment>MSX ROM</comment>
+ <comment xml:lang="ar">MSX ROM</comment>
+ <comment xml:lang="be@latin">MSX ROM</comment>
+ <comment xml:lang="bg">ROM — MSX</comment>
+ <comment xml:lang="ca">ROM de MSX</comment>
+ <comment xml:lang="cs">ROM pro MSX</comment>
+ <comment xml:lang="cy">ROM MSX</comment>
+ <comment xml:lang="da">MSX-rom</comment>
+ <comment xml:lang="de">MSX ROM</comment>
+ <comment xml:lang="el">MSX ROM</comment>
+ <comment xml:lang="en_GB">MSX ROM</comment>
+ <comment xml:lang="eo">MSX-NLM</comment>
+ <comment xml:lang="es">ROM de MSX</comment>
+ <comment xml:lang="eu">MSX-ko ROMa</comment>
+ <comment xml:lang="fi">MSX-ROM</comment>
+ <comment xml:lang="fo">MSX ROM</comment>
+ <comment xml:lang="fr">ROM MSX</comment>
+ <comment xml:lang="ga">ROM MSX</comment>
+ <comment xml:lang="gl">ROM de MSX</comment>
+ <comment xml:lang="he">MSX ROM</comment>
+ <comment xml:lang="hr">MSX ROM</comment>
+ <comment xml:lang="hu">MSX ROM</comment>
+ <comment xml:lang="ia">ROM pro MSX</comment>
+ <comment xml:lang="id">Memori baca-saja MSX</comment>
+ <comment xml:lang="it">ROM MSX</comment>
+ <comment xml:lang="ja">MSX ROM</comment>
+ <comment xml:lang="ka">MSX-ის ROM</comment>
+ <comment xml:lang="kk">MSX ROM</comment>
+ <comment xml:lang="ko">MSX 롬</comment>
+ <comment xml:lang="lt">MSX ROM</comment>
+ <comment xml:lang="lv">MSX ROM</comment>
+ <comment xml:lang="ms">ROM MSX</comment>
+ <comment xml:lang="nb">MSX-ROM</comment>
+ <comment xml:lang="nl">MSX-ROM</comment>
+ <comment xml:lang="nn">MSX-ROM</comment>
+ <comment xml:lang="oc">ROM MSX</comment>
+ <comment xml:lang="pl">Plik ROM konsoli MSX</comment>
+ <comment xml:lang="pt">ROM MSX</comment>
+ <comment xml:lang="pt_BR">ROM de MSX</comment>
+ <comment xml:lang="ro">ROM MSX</comment>
+ <comment xml:lang="ru">MSX ROM</comment>
+ <comment xml:lang="sk">ROM pre MSX</comment>
+ <comment xml:lang="sl">Bralni pomnilnik MSX</comment>
+ <comment xml:lang="sq">ROM MSX</comment>
+ <comment xml:lang="sr">МСИкс РОМ</comment>
+ <comment xml:lang="sv">MSX-rom</comment>
+ <comment xml:lang="tr">MSX ROM</comment>
+ <comment xml:lang="uk">ППП MSX</comment>
+ <comment xml:lang="vi">ROM MSX</comment>
+ <comment xml:lang="zh_CN">MSX ROM</comment>
+ <comment xml:lang="zh_TW">MSX ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.msx"/>
+ </mime-type>
+ <mime-type type="application/x-m4">
+ <comment>M4 macro</comment>
+ <comment xml:lang="ar">M4 macro</comment>
+ <comment xml:lang="be@latin">Makras M4</comment>
+ <comment xml:lang="bg">Макроси — M4</comment>
+ <comment xml:lang="ca">macro M4</comment>
+ <comment xml:lang="cs">makro M4</comment>
+ <comment xml:lang="da">M4-makro</comment>
+ <comment xml:lang="de">M4-Makro</comment>
+ <comment xml:lang="el">Μακροεντολή m4</comment>
+ <comment xml:lang="en_GB">M4 macro</comment>
+ <comment xml:lang="es">macro M4</comment>
+ <comment xml:lang="eu">M4 makroa</comment>
+ <comment xml:lang="fi">M4-makro</comment>
+ <comment xml:lang="fo">M4 fjølvi</comment>
+ <comment xml:lang="fr">macro M4</comment>
+ <comment xml:lang="ga">macra M4</comment>
+ <comment xml:lang="gl">macro M4</comment>
+ <comment xml:lang="he">מאקרו M4</comment>
+ <comment xml:lang="hr">M4 makro</comment>
+ <comment xml:lang="hu">M4 makró</comment>
+ <comment xml:lang="ia">Macro M4</comment>
+ <comment xml:lang="id">Makro M4</comment>
+ <comment xml:lang="it">Macro M4</comment>
+ <comment xml:lang="ja">M4 マクロ</comment>
+ <comment xml:lang="kk">M4 макросы</comment>
+ <comment xml:lang="ko">M4 매크로</comment>
+ <comment xml:lang="lt">M4 macro</comment>
+ <comment xml:lang="lv">M4 makross</comment>
+ <comment xml:lang="nb">M4-makro</comment>
+ <comment xml:lang="nl">M4-macro</comment>
+ <comment xml:lang="nn">M4-makro</comment>
+ <comment xml:lang="oc">macro M4</comment>
+ <comment xml:lang="pl">Makro M4</comment>
+ <comment xml:lang="pt">macro M4</comment>
+ <comment xml:lang="pt_BR">Macro M4</comment>
+ <comment xml:lang="ro">Macro M4</comment>
+ <comment xml:lang="ru">Макрос M4</comment>
+ <comment xml:lang="sk">Makro M4</comment>
+ <comment xml:lang="sl">Makro datoteka M4</comment>
+ <comment xml:lang="sq">Macro M4</comment>
+ <comment xml:lang="sr">М4 макро</comment>
+ <comment xml:lang="sv">M4-makro</comment>
+ <comment xml:lang="tr">M4 makrosu</comment>
+ <comment xml:lang="uk">макрос M4</comment>
+ <comment xml:lang="vi">Vĩ lệnh M4</comment>
+ <comment xml:lang="zh_CN">M4 宏</comment>
+ <comment xml:lang="zh_TW">M4 巨集</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.m4"/>
+ </mime-type>
+ <mime-type type="application/x-n64-rom">
+ <comment>Nintendo64 ROM</comment>
+ <comment xml:lang="ar">Nintendo64 ROM</comment>
+ <comment xml:lang="be@latin">Nintendo64 ROM</comment>
+ <comment xml:lang="bg">ROM — Nintendo64</comment>
+ <comment xml:lang="ca">ROM de Nintendo64</comment>
+ <comment xml:lang="cs">ROM pro Nintendo64</comment>
+ <comment xml:lang="da">Nintendo64-rom</comment>
+ <comment xml:lang="de">Nintendo64 ROM</comment>
+ <comment xml:lang="el">Nintendo64 ROM</comment>
+ <comment xml:lang="en_GB">Nintendo64 ROM</comment>
+ <comment xml:lang="eo">Nintendo64-NLM</comment>
+ <comment xml:lang="es">ROM de Nintendo64</comment>
+ <comment xml:lang="eu">Nintendo64-ko ROMa</comment>
+ <comment xml:lang="fi">Nintendo64-ROM</comment>
+ <comment xml:lang="fo">Nintendo64 ROM</comment>
+ <comment xml:lang="fr">ROM Nintendo64</comment>
+ <comment xml:lang="ga">ROM Nintendo64</comment>
+ <comment xml:lang="gl">ROM de Nintendo64</comment>
+ <comment xml:lang="he">ROM של Nintendo64</comment>
+ <comment xml:lang="hr">Nintendo64 ROM</comment>
+ <comment xml:lang="hu">Nintendo64 ROM</comment>
+ <comment xml:lang="ia">ROM pro Nintendo64</comment>
+ <comment xml:lang="id">Memori baca-saja Nintendo64</comment>
+ <comment xml:lang="it">ROM Nintendo64</comment>
+ <comment xml:lang="ja">Nintendo64 ROM</comment>
+ <comment xml:lang="kk">Nintendo64 ROM</comment>
+ <comment xml:lang="ko">닌텐도 64 롬</comment>
+ <comment xml:lang="lt">Nintendo64 ROM</comment>
+ <comment xml:lang="lv">Nintendo64 ROM</comment>
+ <comment xml:lang="ms">ROM Nintendo64</comment>
+ <comment xml:lang="nb">Nintendo64-ROM</comment>
+ <comment xml:lang="nl">Nintendo64-ROM</comment>
+ <comment xml:lang="nn">Nintendo64-ROM</comment>
+ <comment xml:lang="oc">ROM Nintendo64</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Nintendo64</comment>
+ <comment xml:lang="pt">ROM Nintendo64</comment>
+ <comment xml:lang="pt_BR">ROM de Nintendo64</comment>
+ <comment xml:lang="ro">ROM Nintendo64</comment>
+ <comment xml:lang="ru">Nintendo64 ROM</comment>
+ <comment xml:lang="sk">ROM pre Nintendo64</comment>
+ <comment xml:lang="sl">Bralni pomnilnik Nintendo64</comment>
+ <comment xml:lang="sq">ROM Nintendo64</comment>
+ <comment xml:lang="sr">Нинтендо64 РОМ</comment>
+ <comment xml:lang="sv">Nintendo64-rom</comment>
+ <comment xml:lang="tr">Nintendo64 ROM</comment>
+ <comment xml:lang="uk">ППП Nintendo64</comment>
+ <comment xml:lang="vi">ROM Nintendo64</comment>
+ <comment xml:lang="zh_CN">任天堂 64 ROM</comment>
+ <comment xml:lang="zh_TW">Nintendo64 ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.n64"/>
+ <glob pattern="*.z64"/>
+ <glob pattern="*.v64"/>
+ <magic>
+
+ <match value="0x80371240" type="big32" offset="0"/>
+
+ <match value="0x37804012" type="big32" offset="0"/>
+
+ <match value="0x40123780" type="big32" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-nautilus-link">
+ <comment>Nautilus link</comment>
+ <comment xml:lang="ar">وصلة Nautilus</comment>
+ <comment xml:lang="az">Nautilus körpüsü</comment>
+ <comment xml:lang="be@latin">Spasyłka Nautilus</comment>
+ <comment xml:lang="bg">Връзка — Nautilus</comment>
+ <comment xml:lang="ca">enllaç de Nautilus</comment>
+ <comment xml:lang="cs">odkaz Nautilus</comment>
+ <comment xml:lang="cy">Cyswllt Nautilus</comment>
+ <comment xml:lang="da">Nautilus-henvisning</comment>
+ <comment xml:lang="de">Nautilus-Verknüpfung</comment>
+ <comment xml:lang="el">Σύνδεσμος Nautilus</comment>
+ <comment xml:lang="en_GB">Nautilus link</comment>
+ <comment xml:lang="eo">Nautilus-ligilo</comment>
+ <comment xml:lang="es">enlace de Nautilus</comment>
+ <comment xml:lang="eu">Nautilus esteka</comment>
+ <comment xml:lang="fi">Nautilus-linkki</comment>
+ <comment xml:lang="fo">Nautilus leinkja</comment>
+ <comment xml:lang="fr">lien Nautilus</comment>
+ <comment xml:lang="ga">nasc Nautilus</comment>
+ <comment xml:lang="gl">ligazón de nautilus</comment>
+ <comment xml:lang="he">קישור של Nautilus</comment>
+ <comment xml:lang="hr">Nautilusova poveznica</comment>
+ <comment xml:lang="hu">Nautilus-link</comment>
+ <comment xml:lang="ia">Ligamine Nautilus</comment>
+ <comment xml:lang="id">Taut Nautilus</comment>
+ <comment xml:lang="it">Collegamento Nautilus</comment>
+ <comment xml:lang="ja">Nautilus リンク</comment>
+ <comment xml:lang="kk">Nautilus сілтемесі</comment>
+ <comment xml:lang="ko">노틸러스 바로 가기</comment>
+ <comment xml:lang="lt">Nautilus nuoroda</comment>
+ <comment xml:lang="lv">Nautilus saite</comment>
+ <comment xml:lang="ms">Pautan Nautilus</comment>
+ <comment xml:lang="nb">Nautilus-lenke</comment>
+ <comment xml:lang="nl">Nautilus-verwijzing</comment>
+ <comment xml:lang="nn">Nautilus-lenke</comment>
+ <comment xml:lang="oc">ligam Nautilus</comment>
+ <comment xml:lang="pl">Odnośnik Nautilus</comment>
+ <comment xml:lang="pt">atalho Nautilus</comment>
+ <comment xml:lang="pt_BR">Link do Nautilus</comment>
+ <comment xml:lang="ro">Legătură Nautilus</comment>
+ <comment xml:lang="ru">Ссылка Nautilus</comment>
+ <comment xml:lang="sk">Odkaz Nautilus</comment>
+ <comment xml:lang="sl">Datoteka povezave Nautilus</comment>
+ <comment xml:lang="sq">Lidhje Nautilus</comment>
+ <comment xml:lang="sr">Наутилусова веза</comment>
+ <comment xml:lang="sv">Nautiluslänk</comment>
+ <comment xml:lang="tr">Nautilus bağlantısı</comment>
+ <comment xml:lang="uk">посилання Nautilus</comment>
+ <comment xml:lang="vi">Liên kết Nautilus</comment>
+ <comment xml:lang="zh_CN">Nautilus 链接</comment>
+ <comment xml:lang="zh_TW">Nautilus 鏈結</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <magic priority="50">
+ <match value="&lt;nautilus_object nautilus_link" type="string" offset="0:32"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-neo-geo-pocket-rom">
+ <comment>Neo-Geo Pocket ROM</comment>
+ <comment xml:lang="ca">ROM de Neo-Geo Pocket</comment>
+ <comment xml:lang="cs">ROM pro Neo-Geo Pocket</comment>
+ <comment xml:lang="da">Neo-Geo Pocket ROM</comment>
+ <comment xml:lang="de">Neo-Geo Pocket ROM</comment>
+ <comment xml:lang="en_GB">Neo-Geo Pocket ROM</comment>
+ <comment xml:lang="es">ROM de Neo-Geo Pocket</comment>
+ <comment xml:lang="eu">Neo-Geo Pocket ROM</comment>
+ <comment xml:lang="fi">Neo-Geo Pocket -ROM</comment>
+ <comment xml:lang="fr">ROM Neo-Geo Pocket</comment>
+ <comment xml:lang="ga">ROM Neo-Geo Pocket</comment>
+ <comment xml:lang="hr">Neo-Geo Pocket ROM</comment>
+ <comment xml:lang="hu">Neo-Geo Pocket ROM</comment>
+ <comment xml:lang="id">ROM Neo-Geo Pocket</comment>
+ <comment xml:lang="it">ROM Neo-Geo Pocket</comment>
+ <comment xml:lang="kk">Neo-Geo Pocket ROM</comment>
+ <comment xml:lang="ko">네오지오 포켓 롬</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Neo-Geo Pocket</comment>
+ <comment xml:lang="pt_BR">ROM de Neo-Geo Pocket</comment>
+ <comment xml:lang="ru">Neo-Geo Pocket ROM</comment>
+ <comment xml:lang="sk">ROM pre Neo-Geo Pocket</comment>
+ <comment xml:lang="sr">Нео-Гео Покет РОМ</comment>
+ <comment xml:lang="sv">Neo-Geo Pocket-rom</comment>
+ <comment xml:lang="tr">Neo-Geo Pocket ROM</comment>
+ <comment xml:lang="uk">ППП Neo-Geo Pocket</comment>
+ <comment xml:lang="zh_CN">Neo-Geo Pocket ROM</comment>
+ <comment xml:lang="zh_TW">Neo-Geo Pocket ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.ngp"/>
+ <magic>
+ <match value="0x0" type="byte" offset="35">
+ <match value="COPYRIGHT BY SNK CORPORATION" type="string" offset="0"/>
+ <match value=" LICENSED BY SNK CORPORATION" type="string" offset="0"/>
+ </match>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-neo-geo-pocket-color-rom">
+ <comment>Neo-Geo Pocket Color ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.ngc"/>
+ <magic>
+ <match value="0x10" type="byte" offset="35">
+ <match value="COPYRIGHT BY SNK CORPORATION" type="string" offset="0"/>
+ <match value=" LICENSED BY SNK CORPORATION" type="string" offset="0"/>
+ </match>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-nes-rom">
+
+ <comment>NES ROM</comment>
+ <comment xml:lang="ar">NES ROM</comment>
+ <comment xml:lang="be@latin">NES ROM</comment>
+ <comment xml:lang="bg">ROM — NES</comment>
+ <comment xml:lang="ca">ROM de NES</comment>
+ <comment xml:lang="cs">ROM pro NES</comment>
+ <comment xml:lang="cy">ROM NES</comment>
+ <comment xml:lang="da">NES-rom</comment>
+ <comment xml:lang="de">NES ROM</comment>
+ <comment xml:lang="el">NES ROM</comment>
+ <comment xml:lang="en_GB">NES ROM</comment>
+ <comment xml:lang="eo">NES-NLM</comment>
+ <comment xml:lang="es">ROM de NES</comment>
+ <comment xml:lang="eu">NES-eko ROMa</comment>
+ <comment xml:lang="fi">NES-ROM</comment>
+ <comment xml:lang="fo">NES ROM</comment>
+ <comment xml:lang="fr">ROM NES</comment>
+ <comment xml:lang="ga">ROM NES</comment>
+ <comment xml:lang="gl">ROM de NES</comment>
+ <comment xml:lang="he">ROM של NES</comment>
+ <comment xml:lang="hr">NES ROM</comment>
+ <comment xml:lang="hu">NES ROM</comment>
+ <comment xml:lang="ia">ROM pro NES</comment>
+ <comment xml:lang="id">Memori baca-saja NES</comment>
+ <comment xml:lang="it">ROM NES</comment>
+ <comment xml:lang="ja">ファミコン ROM</comment>
+ <comment xml:lang="kk">NES ROM</comment>
+ <comment xml:lang="ko">NES 롬</comment>
+ <comment xml:lang="lt">NES ROM</comment>
+ <comment xml:lang="lv">NES ROM</comment>
+ <comment xml:lang="ms">ROM NES</comment>
+ <comment xml:lang="nb">NES ROM</comment>
+ <comment xml:lang="nl">Nintendo</comment>
+ <comment xml:lang="nn">NES-ROM</comment>
+ <comment xml:lang="oc">ROM NES</comment>
+ <comment xml:lang="pl">Plik ROM konsoli NES</comment>
+ <comment xml:lang="pt">ROM NES</comment>
+ <comment xml:lang="pt_BR">ROM de NES</comment>
+ <comment xml:lang="ro">ROM NES</comment>
+ <comment xml:lang="ru">NES ROM</comment>
+ <comment xml:lang="sk">ROM pre NES</comment>
+ <comment xml:lang="sl">Bralni pomnilnik NES</comment>
+ <comment xml:lang="sq">ROM NES</comment>
+ <comment xml:lang="sr">НЕС РОМ</comment>
+ <comment xml:lang="sv">NES-rom</comment>
+ <comment xml:lang="tr">NES ROM</comment>
+ <comment xml:lang="uk">ППП NES</comment>
+ <comment xml:lang="vi">ROM NES</comment>
+ <comment xml:lang="zh_CN">NES ROM</comment>
+ <comment xml:lang="zh_TW">任天堂 ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.nes"/>
+ <glob pattern="*.nez"/>
+ <glob pattern="*.unf"/>
+ <glob pattern="*.unif"/>
+ </mime-type>
+ <mime-type type="application/x-netcdf">
+ <comment>Unidata NetCDF document</comment>
+ <comment xml:lang="ar">مستند Unidata NetCDF</comment>
+ <comment xml:lang="ast">Documentu NetCDF d'Unidata</comment>
+ <comment xml:lang="be@latin">Dakument Unidata NetCDF</comment>
+ <comment xml:lang="bg">Документ — Unidata NetCDF</comment>
+ <comment xml:lang="ca">document d'Unidata NetCDF</comment>
+ <comment xml:lang="cs">dokument Unidata NetCDF</comment>
+ <comment xml:lang="da">Unidata NetCDF-dokument</comment>
+ <comment xml:lang="de">Unidata-NetCDF-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Unidata NetCDF</comment>
+ <comment xml:lang="en_GB">Unidata NetCDF document</comment>
+ <comment xml:lang="eo">dokumento en NetCDF-formato de Unidata</comment>
+ <comment xml:lang="es">documento de Unidata NetCDF</comment>
+ <comment xml:lang="eu">Unidata NetCDF dokumentua</comment>
+ <comment xml:lang="fi">Unidata NetCDF -asiakirja</comment>
+ <comment xml:lang="fo">Unidata NetCDF skjal</comment>
+ <comment xml:lang="fr">document Unidata NetCDF</comment>
+ <comment xml:lang="ga">cáipéis Unidata NetCDF</comment>
+ <comment xml:lang="gl">Documentno de Unixdata NetCDF</comment>
+ <comment xml:lang="he">מסמך של Unidata NetCDF</comment>
+ <comment xml:lang="hr">Unidata NetCDF dokument</comment>
+ <comment xml:lang="hu">Unidata NetCDF-dokumentum</comment>
+ <comment xml:lang="ia">Documento Unidata NetCDF</comment>
+ <comment xml:lang="id">Dokumen Unidata NetCDF</comment>
+ <comment xml:lang="it">Documento Unidata NetCDF</comment>
+ <comment xml:lang="ja">Unidata NetCDF ドキュメント</comment>
+ <comment xml:lang="kk">Unidata NetCDF құжаты</comment>
+ <comment xml:lang="ko">Unidata NetCDF 문서</comment>
+ <comment xml:lang="lt">Unidata NetCDF dokumentas</comment>
+ <comment xml:lang="lv">Unidata NetCDF dokuments</comment>
+ <comment xml:lang="ms">Dokumen Unidata NetCDF</comment>
+ <comment xml:lang="nb">Unidata NetCDF-dokument</comment>
+ <comment xml:lang="nl">Unidata NetCDF-document</comment>
+ <comment xml:lang="nn">Unidata netCDF-dokument</comment>
+ <comment xml:lang="oc">document Unidata NetCDF</comment>
+ <comment xml:lang="pl">Dokument Unidata NetCDF</comment>
+ <comment xml:lang="pt">documento Unidata NetCDF</comment>
+ <comment xml:lang="pt_BR">Documento do Unidata NetCDF</comment>
+ <comment xml:lang="ro">Document Unidata NetCDF</comment>
+ <comment xml:lang="ru">Документ Unidata NetCDF</comment>
+ <comment xml:lang="sk">Dokument Unidata NetCDF</comment>
+ <comment xml:lang="sl">Dokument Unidata NetCDF</comment>
+ <comment xml:lang="sq">Dokument Unidata NetCDF</comment>
+ <comment xml:lang="sr">документ Унидата НетЦДФ-а</comment>
+ <comment xml:lang="sv">Unidata NetCDF-dokument</comment>
+ <comment xml:lang="tr">Unidata NetCDF belgesi</comment>
+ <comment xml:lang="uk">документ Unidata NetCDF</comment>
+ <comment xml:lang="vi">Tài liệu NetCDF Unidata</comment>
+ <comment xml:lang="zh_CN">Unidata NetCDF 文档</comment>
+ <comment xml:lang="zh_TW">Unidata NetCDF 文件</comment>
+ <acronym>NetCDF</acronym>
+ <expanded-acronym>Network Common Data Form</expanded-acronym>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.cdf"/>
+ <glob pattern="*.nc"/>
+ </mime-type>
+ <mime-type type="application/x-nzb">
+ <comment>NewzBin usenet index</comment>
+ <comment xml:lang="bg">Индекс — Usenet, NewzBin</comment>
+ <comment xml:lang="ca">índex d'Usenet NewzBin</comment>
+ <comment xml:lang="cs">index NewzBin diskuzních skupin Usenet</comment>
+ <comment xml:lang="da">NewzBin-brugernetindex</comment>
+ <comment xml:lang="de">NewzBin-Usenet-Index</comment>
+ <comment xml:lang="el">Ευρετήριο usenet NewzBin</comment>
+ <comment xml:lang="en_GB">NewzBin usenet index</comment>
+ <comment xml:lang="es">índice NewzBin de usenet</comment>
+ <comment xml:lang="eu">NewzBin usenet indizea</comment>
+ <comment xml:lang="fr">index usenet </comment>
+ <comment xml:lang="ga">innéacs usenet NewzBin</comment>
+ <comment xml:lang="gl">Índice de usenet NEwzBin</comment>
+ <comment xml:lang="he">אינדקס שרתי חדשות NewzBin</comment>
+ <comment xml:lang="hr">NewzBin usenet indeks</comment>
+ <comment xml:lang="hu">NewzBin usenet index</comment>
+ <comment xml:lang="ia">Indice de usenet NewzBin</comment>
+ <comment xml:lang="id">Indeks usenet NewzBin</comment>
+ <comment xml:lang="it">Indice Usenet NewzBiz</comment>
+ <comment xml:lang="ja">NewzBin Usenet インデックス</comment>
+ <comment xml:lang="kk">NewzBin usenet индексі</comment>
+ <comment xml:lang="ko">NewzBin 유즈넷 인덱스</comment>
+ <comment xml:lang="lv">NewzBin usenet rādītājs</comment>
+ <comment xml:lang="nl">NewzBin usenet index</comment>
+ <comment xml:lang="oc">indèx usenet NewzBin</comment>
+ <comment xml:lang="pl">Indeks grup dyskusyjnych NewzBin</comment>
+ <comment xml:lang="pt">índice usenet NewzBin</comment>
+ <comment xml:lang="pt_BR">Índice de usenet NewzBin</comment>
+ <comment xml:lang="ru">Индекс usenet NewzBin</comment>
+ <comment xml:lang="sk">Index Usenetu NewzBin</comment>
+ <comment xml:lang="sl">Kazalo usenet NewzBin</comment>
+ <comment xml:lang="sr">Њузбин попис јузнета</comment>
+ <comment xml:lang="sv">NewzBin-usenetindex</comment>
+ <comment xml:lang="tr">NewzBin usenet dizini</comment>
+ <comment xml:lang="uk">покажчик usenet NewzBin</comment>
+ <comment xml:lang="zh_CN">NewzBin Usenet 索引</comment>
+ <comment xml:lang="zh_TW">NewzBin usenet 索引</comment>
+ <sub-class-of type="application/xml"/>
+ <magic priority="80">
+ <match value="&lt;nzb" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.nzb"/>
+ </mime-type>
+ <mime-type type="application/x-object">
+ <comment>object code</comment>
+ <comment xml:lang="ar">رمز الكائن</comment>
+ <comment xml:lang="be@latin">abjektny kod</comment>
+ <comment xml:lang="bg">Обектен код</comment>
+ <comment xml:lang="ca">codi objecte</comment>
+ <comment xml:lang="cs">objektový kód</comment>
+ <comment xml:lang="da">objektkode</comment>
+ <comment xml:lang="de">Objektcode</comment>
+ <comment xml:lang="el">Μεταφρασμένος κώδικας</comment>
+ <comment xml:lang="en_GB">object code</comment>
+ <comment xml:lang="eo">celkodo</comment>
+ <comment xml:lang="es">código objeto</comment>
+ <comment xml:lang="eu">objektu kodea</comment>
+ <comment xml:lang="fi">objektikoodi</comment>
+ <comment xml:lang="fr">code objet</comment>
+ <comment xml:lang="ga">cód réada</comment>
+ <comment xml:lang="gl">código obxecto</comment>
+ <comment xml:lang="he">קוד אובייקט</comment>
+ <comment xml:lang="hr">Object kôd</comment>
+ <comment xml:lang="hu">tárgykód</comment>
+ <comment xml:lang="ia">Codice objecto</comment>
+ <comment xml:lang="id">kode objek</comment>
+ <comment xml:lang="it">Codice oggetto</comment>
+ <comment xml:lang="ja">オブジェクトコード</comment>
+ <comment xml:lang="kk">объектті коды</comment>
+ <comment xml:lang="ko">개체 코드</comment>
+ <comment xml:lang="lt">objektinis kodas</comment>
+ <comment xml:lang="lv">objekta kods</comment>
+ <comment xml:lang="ms">Kod objek</comment>
+ <comment xml:lang="nb">objektkode</comment>
+ <comment xml:lang="nl">objectcode</comment>
+ <comment xml:lang="nn">objektkode</comment>
+ <comment xml:lang="oc">còde objet</comment>
+ <comment xml:lang="pl">Kod obiektowy</comment>
+ <comment xml:lang="pt">código de objeto</comment>
+ <comment xml:lang="pt_BR">Código-objeto</comment>
+ <comment xml:lang="ro">cod sursă obiect</comment>
+ <comment xml:lang="ru">Объектный код</comment>
+ <comment xml:lang="sk">Objektový kód</comment>
+ <comment xml:lang="sl">predmetna koda</comment>
+ <comment xml:lang="sq">Kod objekti</comment>
+ <comment xml:lang="sr">објектни ко̂д</comment>
+ <comment xml:lang="sv">objektkod</comment>
+ <comment xml:lang="tr">nesne kodu</comment>
+ <comment xml:lang="uk">об'єктний код</comment>
+ <comment xml:lang="vi">mã đối tượng</comment>
+ <comment xml:lang="zh_CN">目标代码</comment>
+ <comment xml:lang="zh_TW">目的碼</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="\177ELF" type="string" offset="0">
+ <match value="1" type="byte" offset="5">
+ <match value="1" type="little16" offset="16"/>
+ </match>
+ </match>
+ <match value="\177ELF" type="string" offset="0">
+ <match value="2" type="byte" offset="5">
+ <match value="1" type="big16" offset="16"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.o"/>
+ </mime-type>
+ <mime-type type="application/annodex">
+ <comment>Annodex exchange format</comment>
+ <comment xml:lang="ar">صيغة Annodex البديلة</comment>
+ <comment xml:lang="bg">Формат за обмяна — Annodex</comment>
+ <comment xml:lang="ca">format d'intercanvi Annodex</comment>
+ <comment xml:lang="cs">výměnný formát Annodex</comment>
+ <comment xml:lang="da">Udvekslingsformat for Annodex</comment>
+ <comment xml:lang="de">Annodex-Wechselformat</comment>
+ <comment xml:lang="el">Μορφή ανταλλαγής Annodex</comment>
+ <comment xml:lang="en_GB">Annodex exchange format</comment>
+ <comment xml:lang="es">formato de intercambio de Annodex</comment>
+ <comment xml:lang="eu">Annodex trukatze-formatua</comment>
+ <comment xml:lang="fi">Annodex-siirtomuoto</comment>
+ <comment xml:lang="fo">Annodex umbýtingarsnið</comment>
+ <comment xml:lang="fr">format d'échange Annodex</comment>
+ <comment xml:lang="ga">formáid mhalairte Annodex</comment>
+ <comment xml:lang="gl">formato intercambiábel de Annodex</comment>
+ <comment xml:lang="he">תבנית החלפת Annodex</comment>
+ <comment xml:lang="hr">Annodex format razmjene</comment>
+ <comment xml:lang="hu">Annodex csereformátum</comment>
+ <comment xml:lang="ia">Formato de excambio Annodex</comment>
+ <comment xml:lang="id">Format pertukaran Annodex</comment>
+ <comment xml:lang="it">Formato di scambio Annodex</comment>
+ <comment xml:lang="ja">Annodex 交換フォーマット</comment>
+ <comment xml:lang="ka">Annodex-ის გაცვლითი ფორმატი</comment>
+ <comment xml:lang="kk">Annodex алмасу пішімі</comment>
+ <comment xml:lang="ko">Annodex 교환 형식</comment>
+ <comment xml:lang="lt">Annodex mainų formatas</comment>
+ <comment xml:lang="lv">Annodex apmaiņas formāts</comment>
+ <comment xml:lang="nl">Annodex-exchange</comment>
+ <comment xml:lang="oc">format d'escambi Annodex</comment>
+ <comment xml:lang="pl">Format wymiany Annodex</comment>
+ <comment xml:lang="pt">formato de troca Annodex</comment>
+ <comment xml:lang="pt_BR">Formato de troca Annodex</comment>
+ <comment xml:lang="ro">Format schimb Annodex</comment>
+ <comment xml:lang="ru">Формат обмена Annodex</comment>
+ <comment xml:lang="sk">Formát pre výmenu Annodex</comment>
+ <comment xml:lang="sl">Izmenjalna datoteka Annodex</comment>
+ <comment xml:lang="sr">Анодексов запис размене</comment>
+ <comment xml:lang="sv">Annodex-utväxlingsformat</comment>
+ <comment xml:lang="tr">Annodex değişim biçimi</comment>
+ <comment xml:lang="uk">формат обміну даними Annodex</comment>
+ <comment xml:lang="vi">Định dạng trao đổi Annodex</comment>
+ <comment xml:lang="zh_CN">Annodex 交换格式</comment>
+ <comment xml:lang="zh_TW">Annodex 交換格式</comment>
+ <generic-icon name="video-x-generic"/>
+ <magic priority="50">
+ <match value="OggS" type="string" offset="0">
+ <match value="fishead\0" type="string" offset="28">
+ <match value="CMML\0\0\0\0" type="string" offset="56:512"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.anx"/>
+ <alias type="application/x-annodex"/>
+ </mime-type>
+ <mime-type type="video/annodex">
+ <comment>Annodex Video</comment>
+ <comment xml:lang="ar">Annodex مرئي</comment>
+ <comment xml:lang="ast">Videu n'Annodex</comment>
+ <comment xml:lang="bg">Видео — Annodex</comment>
+ <comment xml:lang="ca">Annodex Video</comment>
+ <comment xml:lang="cs">video Annodex</comment>
+ <comment xml:lang="da">Annodexvideo</comment>
+ <comment xml:lang="de">Annodex-Video</comment>
+ <comment xml:lang="el">Βίντεο Annodex</comment>
+ <comment xml:lang="en_GB">Annodex Video</comment>
+ <comment xml:lang="eo">Annodex-video</comment>
+ <comment xml:lang="es">vídeo Annodex</comment>
+ <comment xml:lang="eu">Annodex bideoa</comment>
+ <comment xml:lang="fi">Annodex-video</comment>
+ <comment xml:lang="fo">Annodex video</comment>
+ <comment xml:lang="fr">vidéo Annodex</comment>
+ <comment xml:lang="ga">Físeán Annodex</comment>
+ <comment xml:lang="gl">vídeo de Annodex</comment>
+ <comment xml:lang="he">וידאו Annodex</comment>
+ <comment xml:lang="hr">Annodex video snimka</comment>
+ <comment xml:lang="hu">Annodex videó</comment>
+ <comment xml:lang="ia">Video Annodex</comment>
+ <comment xml:lang="id">Video Annodex</comment>
+ <comment xml:lang="it">Video Annodex</comment>
+ <comment xml:lang="ja">Annodex 動画</comment>
+ <comment xml:lang="ka">Annodex-ის ვიდეო</comment>
+ <comment xml:lang="kk">Annodex видеосы</comment>
+ <comment xml:lang="ko">Annodex 동영상</comment>
+ <comment xml:lang="lt">Annodex vaizdo įrašas</comment>
+ <comment xml:lang="lv">Annodex video</comment>
+ <comment xml:lang="nl">Annodex Video</comment>
+ <comment xml:lang="oc">vidèo Annodex</comment>
+ <comment xml:lang="pl">Plik wideo Annodex</comment>
+ <comment xml:lang="pt">vídeo Annodex</comment>
+ <comment xml:lang="pt_BR">Vídeo Annodex</comment>
+ <comment xml:lang="ro">Video Annodex</comment>
+ <comment xml:lang="ru">Видео Annodex</comment>
+ <comment xml:lang="sk">Video Annodex</comment>
+ <comment xml:lang="sl">Video datoteka Annodex</comment>
+ <comment xml:lang="sr">Анодекс видео</comment>
+ <comment xml:lang="sv">Annodex-video</comment>
+ <comment xml:lang="tr">Annodex Video</comment>
+ <comment xml:lang="uk">відеокліп Annodex</comment>
+ <comment xml:lang="vi">Ảnh động Annodex</comment>
+ <comment xml:lang="zh_CN">Annodex 视频</comment>
+ <comment xml:lang="zh_TW">Annodex 視訊</comment>
+ <sub-class-of type="application/annodex"/>
+ <magic priority="50">
+ <match value="OggS" type="string" offset="0">
+ <match value="fishead\0" type="string" offset="28">
+ <match value="CMML\0\0\0\0" type="string" offset="56:512"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.axv"/>
+ <alias type="video/x-annodex"/>
+ </mime-type>
+ <mime-type type="audio/annodex">
+ <comment>Annodex Audio</comment>
+ <comment xml:lang="ar">Annodex سمعي</comment>
+ <comment xml:lang="bg">Аудио — Annodex</comment>
+ <comment xml:lang="ca">Annodex Audio</comment>
+ <comment xml:lang="cs">zvuk Annodex</comment>
+ <comment xml:lang="da">Annodexlyd</comment>
+ <comment xml:lang="de">Annodex-Audio</comment>
+ <comment xml:lang="el">Ήχος Annodex</comment>
+ <comment xml:lang="en_GB">Annodex Audio</comment>
+ <comment xml:lang="eo">Annodex-sondosiero</comment>
+ <comment xml:lang="es">sonido Annodex</comment>
+ <comment xml:lang="eu">Annodex audioa</comment>
+ <comment xml:lang="fi">Annodex-ääni</comment>
+ <comment xml:lang="fo">Annodex ljóður</comment>
+ <comment xml:lang="fr">audio Annodex</comment>
+ <comment xml:lang="ga">Fuaim Annodex</comment>
+ <comment xml:lang="gl">son de Annodex</comment>
+ <comment xml:lang="he">שמע Annodex</comment>
+ <comment xml:lang="hr">Annodex zvučni zapis</comment>
+ <comment xml:lang="hu">Annodex hang</comment>
+ <comment xml:lang="ia">Audio Annodex</comment>
+ <comment xml:lang="id">Audio Annodex</comment>
+ <comment xml:lang="it">Audio Annodex</comment>
+ <comment xml:lang="ja">Annodex オーディオ</comment>
+ <comment xml:lang="ka">Annodex-ის აუდიო</comment>
+ <comment xml:lang="kk">Annodex аудиосы</comment>
+ <comment xml:lang="ko">Annodex 오디오</comment>
+ <comment xml:lang="lt">Annodex garso įrašas</comment>
+ <comment xml:lang="lv">Annodex audio</comment>
+ <comment xml:lang="nl">Annodex Audio</comment>
+ <comment xml:lang="oc">àudio Annodex</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Annodex</comment>
+ <comment xml:lang="pt">áudio Annodex</comment>
+ <comment xml:lang="pt_BR">Áudio Annodex</comment>
+ <comment xml:lang="ro">Audio Annodex</comment>
+ <comment xml:lang="ru">Аудио Annodex</comment>
+ <comment xml:lang="sk">Zvuk Annodex</comment>
+ <comment xml:lang="sl">Zvočna datoteka Annodex</comment>
+ <comment xml:lang="sr">Анодекс аудио</comment>
+ <comment xml:lang="sv">Annodex-ljud</comment>
+ <comment xml:lang="tr">Annodex Sesi</comment>
+ <comment xml:lang="uk">звук Annodex</comment>
+ <comment xml:lang="vi">Âm thanh Annodex</comment>
+ <comment xml:lang="zh_CN">Annodex 音频</comment>
+ <comment xml:lang="zh_TW">Annodex 音訊</comment>
+ <sub-class-of type="application/annodex"/>
+ <magic priority="50">
+ <match value="OggS" type="string" offset="0">
+ <match value="fishead\0" type="string" offset="28">
+ <match value="CMML\0\0\0\0" type="string" offset="56:512"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.axa"/>
+ <alias type="audio/x-annodex"/>
+ </mime-type>
+ <mime-type type="application/ogg">
+ <comment>Ogg multimedia file</comment>
+ <comment xml:lang="ar">ملف وسائط متعددة Ogg</comment>
+ <comment xml:lang="be@latin">Multymedyjny fajł Ogg</comment>
+ <comment xml:lang="bg">Мултимедия — Ogg</comment>
+ <comment xml:lang="ca">fitxer multimèdia Ogg</comment>
+ <comment xml:lang="cs">multimediální soubor Ogg</comment>
+ <comment xml:lang="da">Ogg multimedie-fil</comment>
+ <comment xml:lang="de">Ogg-Multimediadatei</comment>
+ <comment xml:lang="el">Αρχείο πολυμέσων Ogg</comment>
+ <comment xml:lang="en_GB">Ogg multimedia file</comment>
+ <comment xml:lang="es">archivo multimedia Ogg</comment>
+ <comment xml:lang="eu">Ogg multimediako fitxategia</comment>
+ <comment xml:lang="fi">Ogg-multimediatiedosto</comment>
+ <comment xml:lang="fo">Ogg margmiðlafíla</comment>
+ <comment xml:lang="fr">fichier multimédia Ogg</comment>
+ <comment xml:lang="ga">comhad ilmheán Ogg</comment>
+ <comment xml:lang="gl">ficheiro multimedia Ogg</comment>
+ <comment xml:lang="he">קובץ מולטימדיה Ogg</comment>
+ <comment xml:lang="hr">Ogg multimedijalna datoteka</comment>
+ <comment xml:lang="hu">Ogg multimédiafájl</comment>
+ <comment xml:lang="ia">File multimedial Ogg</comment>
+ <comment xml:lang="id">Berkas multimedia Ogg</comment>
+ <comment xml:lang="it">File multimediale Ogg</comment>
+ <comment xml:lang="ja">Ogg マルチメディアファイル</comment>
+ <comment xml:lang="ka">Ogg-ის მულტიმედია ფაილი</comment>
+ <comment xml:lang="kk">Ogg мультимедиа файлы</comment>
+ <comment xml:lang="ko">Ogg 멀티미디어 파일</comment>
+ <comment xml:lang="lt">Ogg multimedijos failas</comment>
+ <comment xml:lang="lv">Ogg multimediju datne</comment>
+ <comment xml:lang="nb">Ogg-multimediafil</comment>
+ <comment xml:lang="nl">Ogg-multimediabestand</comment>
+ <comment xml:lang="nn">Ogg multimediafil</comment>
+ <comment xml:lang="oc">fichièr multimèdia Ogg</comment>
+ <comment xml:lang="pl">Plik multimedialny Ogg</comment>
+ <comment xml:lang="pt">ficheiro multimédia Ogg</comment>
+ <comment xml:lang="pt_BR">Arquivo multimídia Ogg</comment>
+ <comment xml:lang="ro">Fișier multimedia Ogg</comment>
+ <comment xml:lang="ru">Мультимедийный файл Ogg</comment>
+ <comment xml:lang="sk">Súbor multimédií Ogg</comment>
+ <comment xml:lang="sl">Večpredstavnostna datoteka Ogg</comment>
+ <comment xml:lang="sq">File multimedial Ogg</comment>
+ <comment xml:lang="sr">Огг мултимедијална датотека</comment>
+ <comment xml:lang="sv">Ogg-multimediafil</comment>
+ <comment xml:lang="tr">Ogg çokluortam dosyası</comment>
+ <comment xml:lang="uk">мультимедійний файл Ogg</comment>
+ <comment xml:lang="vi">Tập tin đa phương tiện Ogg</comment>
+ <comment xml:lang="zh_CN">Ogg 多媒体文件</comment>
+ <comment xml:lang="zh_TW">Ogg 多媒體檔案</comment>
+ <generic-icon name="video-x-generic"/>
+ <alias type="application/x-ogg"/>
+ <magic priority="50">
+ <match value="OggS" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.ogx"/>
+ </mime-type>
+ <mime-type type="audio/ogg">
+ <comment>Ogg Audio</comment>
+ <comment xml:lang="ar">Ogg سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Ogg</comment>
+ <comment xml:lang="bg">Аудио — Ogg</comment>
+ <comment xml:lang="ca">àudio d'Ogg</comment>
+ <comment xml:lang="cs">zvuk Ogg</comment>
+ <comment xml:lang="da">Ogg-lyd</comment>
+ <comment xml:lang="de">Ogg-Audio</comment>
+ <comment xml:lang="el">Ήχος Ogg</comment>
+ <comment xml:lang="en_GB">Ogg Audio</comment>
+ <comment xml:lang="es">sonido Ogg</comment>
+ <comment xml:lang="eu">Ogg audioa</comment>
+ <comment xml:lang="fi">Ogg-ääni</comment>
+ <comment xml:lang="fo">Ogg ljóður</comment>
+ <comment xml:lang="fr">audio Ogg</comment>
+ <comment xml:lang="ga">Fuaim Ogg</comment>
+ <comment xml:lang="gl">son Ogg</comment>
+ <comment xml:lang="he">שמע Ogg</comment>
+ <comment xml:lang="hr">Ogg zvučni zapis</comment>
+ <comment xml:lang="hu">Ogg hang</comment>
+ <comment xml:lang="ia">Audio Ogg</comment>
+ <comment xml:lang="id">Audio Ogg</comment>
+ <comment xml:lang="it">Audio Ogg</comment>
+ <comment xml:lang="ja">Ogg オーディオ</comment>
+ <comment xml:lang="ka">Ogg-ის აუდიო</comment>
+ <comment xml:lang="kk">Ogg аудиосы</comment>
+ <comment xml:lang="ko">Ogg 오디오</comment>
+ <comment xml:lang="lt">Ogg garso įrašas</comment>
+ <comment xml:lang="lv">Ogg audio</comment>
+ <comment xml:lang="nb">Ogg lyd</comment>
+ <comment xml:lang="nl">Ogg-audio</comment>
+ <comment xml:lang="nn">Ogg-lyd</comment>
+ <comment xml:lang="oc">àudio Ogg</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Ogg</comment>
+ <comment xml:lang="pt">áudio Ogg</comment>
+ <comment xml:lang="pt_BR">Áudio Ogg</comment>
+ <comment xml:lang="ro">Audio Ogg</comment>
+ <comment xml:lang="ru">Аудио Ogg</comment>
+ <comment xml:lang="sk">Zvuk Ogg</comment>
+ <comment xml:lang="sl">Zvočna datoteka Ogg</comment>
+ <comment xml:lang="sq">Audio Ogg</comment>
+ <comment xml:lang="sr">Огг звук</comment>
+ <comment xml:lang="sv">Ogg-ljud</comment>
+ <comment xml:lang="tr">Ogg Sesi</comment>
+ <comment xml:lang="uk">звук ogg</comment>
+ <comment xml:lang="vi">Âm thanh Ogg</comment>
+ <comment xml:lang="zh_CN">Ogg 音频</comment>
+ <comment xml:lang="zh_TW">Ogg 音訊</comment>
+ <sub-class-of type="application/ogg"/>
+ <magic priority="50">
+ <match value="OggS" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.oga"/>
+ <glob pattern="*.ogg"/>
+ <glob pattern="*.opus"/>
+ <alias type="audio/x-ogg"/>
+ </mime-type>
+ <mime-type type="video/ogg">
+ <comment>Ogg Video</comment>
+ <comment xml:lang="ar">Ogg مرئي</comment>
+ <comment xml:lang="ast">Videu n'Ogg</comment>
+ <comment xml:lang="be@latin">Videa Ogg</comment>
+ <comment xml:lang="bg">Видео — Ogg</comment>
+ <comment xml:lang="ca">vídeo Ogg</comment>
+ <comment xml:lang="cs">video Ogg</comment>
+ <comment xml:lang="da">Ogg-video</comment>
+ <comment xml:lang="de">Ogg-Video</comment>
+ <comment xml:lang="el">Βίντεο Ogg</comment>
+ <comment xml:lang="en_GB">Ogg Video</comment>
+ <comment xml:lang="es">vídeo Ogg</comment>
+ <comment xml:lang="eu">Ogg bideoa</comment>
+ <comment xml:lang="fi">Ogg-video</comment>
+ <comment xml:lang="fo">Ogg Video</comment>
+ <comment xml:lang="fr">vidéo Ogg</comment>
+ <comment xml:lang="ga">Físeán Ogg</comment>
+ <comment xml:lang="gl">vídeo Ogg</comment>
+ <comment xml:lang="he">וידאו Ogg</comment>
+ <comment xml:lang="hr">Ogg video snimka</comment>
+ <comment xml:lang="hu">Ogg videó</comment>
+ <comment xml:lang="ia">Video Ogg</comment>
+ <comment xml:lang="id">Video Ogg</comment>
+ <comment xml:lang="it">Video Ogg</comment>
+ <comment xml:lang="ja">Ogg 動画</comment>
+ <comment xml:lang="ka">Ogg ვიდეო</comment>
+ <comment xml:lang="kk">Ogg видеосы</comment>
+ <comment xml:lang="ko">Ogg 동영상</comment>
+ <comment xml:lang="lt">Ogg vaizdo įrašas</comment>
+ <comment xml:lang="lv">Ogg video</comment>
+ <comment xml:lang="nb">Ogg video</comment>
+ <comment xml:lang="nl">Ogg-video</comment>
+ <comment xml:lang="nn">Ogg-video</comment>
+ <comment xml:lang="oc">vidèo Ogg</comment>
+ <comment xml:lang="pl">Plik wideo Ogg</comment>
+ <comment xml:lang="pt">vídeo Ogg</comment>
+ <comment xml:lang="pt_BR">Vídeo Ogg</comment>
+ <comment xml:lang="ro">Video Ogg</comment>
+ <comment xml:lang="ru">Видео Ogg</comment>
+ <comment xml:lang="sk">Video Ogg</comment>
+ <comment xml:lang="sl">Video datoteka Ogg</comment>
+ <comment xml:lang="sq">Video Ogg</comment>
+ <comment xml:lang="sr">Огг снимак</comment>
+ <comment xml:lang="sv">Ogg-video</comment>
+ <comment xml:lang="tr">Ogg Video</comment>
+ <comment xml:lang="uk">відеокліп ogg</comment>
+ <comment xml:lang="vi">Ảnh động Ogg</comment>
+ <comment xml:lang="zh_CN">Ogg 视频</comment>
+ <comment xml:lang="zh_TW">Ogg 視訊</comment>
+ <sub-class-of type="application/ogg"/>
+ <magic priority="50">
+ <match value="OggS" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.ogv"/>
+ <glob pattern="*.ogg"/>
+ <alias type="video/x-ogg"/>
+ </mime-type>
+ <mime-type type="audio/x-vorbis+ogg">
+ <comment>Ogg Vorbis audio</comment>
+ <comment xml:lang="ar">Ogg Vorbis سمعي</comment>
+ <comment xml:lang="az">Ogg Vorbis audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Ogg Vorbis</comment>
+ <comment xml:lang="bg">Аудио — Ogg Vorbis</comment>
+ <comment xml:lang="ca">àudio d'Ogg Vorbis</comment>
+ <comment xml:lang="cs">zvuk Ogg Vorbis</comment>
+ <comment xml:lang="cy">Sain Ogg Vorbis</comment>
+ <comment xml:lang="da">Ogg Vorbis-lyd</comment>
+ <comment xml:lang="de">Ogg-Vorbis-Audio</comment>
+ <comment xml:lang="el">Ήχος Ogg Vobris</comment>
+ <comment xml:lang="en_GB">Ogg Vorbis audio</comment>
+ <comment xml:lang="eo">Ogg-Vorbis-sondosiero</comment>
+ <comment xml:lang="es">sonido Ogg Vorbis</comment>
+ <comment xml:lang="eu">Ogg Vorbis audioa</comment>
+ <comment xml:lang="fi">Ogg Vorbis -ääni</comment>
+ <comment xml:lang="fo">Ogg Vorbis ljóður</comment>
+ <comment xml:lang="fr">audio Ogg Vorbis</comment>
+ <comment xml:lang="ga">fuaim Ogg Vorbis</comment>
+ <comment xml:lang="gl">son Ogg Vorbis</comment>
+ <comment xml:lang="he">שמע Ogg Vorbis</comment>
+ <comment xml:lang="hr">Ogg Vorbis zvučni zapis</comment>
+ <comment xml:lang="hu">Ogg Vorbis hang</comment>
+ <comment xml:lang="ia">Audio Ogg Vorbis</comment>
+ <comment xml:lang="id">Audio Ogg Vorbis</comment>
+ <comment xml:lang="it">Audio Ogg Vorbis</comment>
+ <comment xml:lang="ja">Ogg Vorbis オーディオ</comment>
+ <comment xml:lang="ka">Ogg Vorbis აუდიო</comment>
+ <comment xml:lang="kk">Ogg Vorbis аудиосы</comment>
+ <comment xml:lang="ko">Ogg Vorbis 오디오</comment>
+ <comment xml:lang="lt">Ogg Vorbis garso įrašas</comment>
+ <comment xml:lang="lv">Ogg Vorbis audio</comment>
+ <comment xml:lang="ms">Audio Ogg Vorbis</comment>
+ <comment xml:lang="nb">Ogg Vorbis lyd</comment>
+ <comment xml:lang="nl">Ogg Vorbis-audio</comment>
+ <comment xml:lang="nn">Ogg Vorbis-lyd</comment>
+ <comment xml:lang="oc">àudio Ogg Vorbis</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Ogg Vorbis</comment>
+ <comment xml:lang="pt">áudio Ogg Vorbis</comment>
+ <comment xml:lang="pt_BR">Áudio Ogg Vorbis</comment>
+ <comment xml:lang="ro">Audio Ogg Vorbis</comment>
+ <comment xml:lang="ru">Аудио Ogg Vorbis</comment>
+ <comment xml:lang="sk">Zvuk Ogg Vorbis</comment>
+ <comment xml:lang="sl">Zvočna datoteka Ogg Vorbis</comment>
+ <comment xml:lang="sq">Audio Ogg Vorbis</comment>
+ <comment xml:lang="sr">Огг Ворбис звук</comment>
+ <comment xml:lang="sv">Ogg Vorbis-ljud</comment>
+ <comment xml:lang="tr">Ogg Vorbis sesi</comment>
+ <comment xml:lang="uk">звук ogg Vorbis</comment>
+ <comment xml:lang="vi">Âm thanh Vorbis Ogg</comment>
+ <comment xml:lang="zh_CN">Ogg Vorbis 音频</comment>
+ <comment xml:lang="zh_TW">Ogg Vorbis 音訊</comment>
+ <sub-class-of type="audio/ogg"/>
+ <alias type="audio/vorbis"/>
+ <alias type="audio/x-vorbis"/>
+ <magic priority="80">
+ <match value="OggS" type="string" offset="0">
+ <match value="\x01vorbis" type="string" offset="28"/>
+ </match>
+ </magic>
+ <glob pattern="*.oga"/>
+ <glob pattern="*.ogg"/>
+ </mime-type>
+ <mime-type type="audio/x-flac+ogg">
+ <comment>Ogg FLAC audio</comment>
+ <comment xml:lang="ar">Ogg FLAC سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Ogg FLAC</comment>
+ <comment xml:lang="bg">Аудио — Ogg FLAC</comment>
+ <comment xml:lang="ca">àudio FLAC d'Ogg</comment>
+ <comment xml:lang="cs">zvuk Ogg FLAC</comment>
+ <comment xml:lang="da">Ogg FLAC-lyd</comment>
+ <comment xml:lang="de">Ogg-FLAC-Audio</comment>
+ <comment xml:lang="el">Ήχος Ogg FLAC</comment>
+ <comment xml:lang="en_GB">Ogg FLAC audio</comment>
+ <comment xml:lang="es">sonido Ogg FLAC</comment>
+ <comment xml:lang="eu">Ogg FLAC audioa</comment>
+ <comment xml:lang="fi">Ogg FLAC -ääni</comment>
+ <comment xml:lang="fo">Ogg FLAC ljóður</comment>
+ <comment xml:lang="fr">audio Ogg FLAC</comment>
+ <comment xml:lang="ga">fuaim Ogg FLAC</comment>
+ <comment xml:lang="gl">son Ogg FLAC</comment>
+ <comment xml:lang="he">שמע Ogg FLAC</comment>
+ <comment xml:lang="hr">Ogg FLAC zvučni zapis</comment>
+ <comment xml:lang="hu">Ogg FLAC hang</comment>
+ <comment xml:lang="ia">Audio Ogg FLAC</comment>
+ <comment xml:lang="id">Audio Ogg FLAC</comment>
+ <comment xml:lang="it">Audio Ogg FLAC</comment>
+ <comment xml:lang="ja">Ogg FLAC オーディオ</comment>
+ <comment xml:lang="ka">Ogg FLAC აუდიო</comment>
+ <comment xml:lang="kk">Ogg FLAC аудиосы</comment>
+ <comment xml:lang="ko">Ogg FLAC 오디오</comment>
+ <comment xml:lang="lt">Ogg FLAC garso įrašas</comment>
+ <comment xml:lang="lv">Ogg FLAC audio</comment>
+ <comment xml:lang="nb">Ogg FLAC-lyd</comment>
+ <comment xml:lang="nl">Ogg FLAC-audio</comment>
+ <comment xml:lang="nn">Ogg FLAC-lyd</comment>
+ <comment xml:lang="oc">àudio Ogg FLAC</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Ogg FLAC</comment>
+ <comment xml:lang="pt">áudio Ogg FLAC</comment>
+ <comment xml:lang="pt_BR">Áudio Ogg FLAC</comment>
+ <comment xml:lang="ro">Audio Ogg FLAC</comment>
+ <comment xml:lang="ru">Аудио Ogg FLAC</comment>
+ <comment xml:lang="sk">Zvuk Ogg FLAC</comment>
+ <comment xml:lang="sl">Zvočna datoteka Ogg FLAC</comment>
+ <comment xml:lang="sq">Audio Ogg FLAC</comment>
+ <comment xml:lang="sr">Огг ФЛАЦ звук</comment>
+ <comment xml:lang="sv">Ogg FLAC-ljud</comment>
+ <comment xml:lang="tr">Ogg FLAC sesi</comment>
+ <comment xml:lang="uk">звук ogg FLAC</comment>
+ <comment xml:lang="vi">Âm thanh FLAC Ogg</comment>
+ <comment xml:lang="zh_CN">Ogg FLAC 音频</comment>
+ <comment xml:lang="zh_TW">Ogg FLAC 音訊</comment>
+ <sub-class-of type="audio/ogg"/>
+ <alias type="audio/x-oggflac"/>
+ <magic priority="80">
+ <match value="OggS" type="string" offset="0">
+ <match value="fLaC" type="string" offset="28"/>
+ </match>
+ <match value="OggS" type="string" offset="0">
+ <match value="\177FLAC" type="string" offset="28"/>
+ </match>
+ </magic>
+ <glob pattern="*.oga"/>
+ <glob pattern="*.ogg"/>
+ </mime-type>
+ <mime-type type="audio/x-opus+ogg">
+ <comment>Opus audio</comment>
+ <comment xml:lang="ca">àudio d'Opus</comment>
+ <comment xml:lang="cs">zvuk Opus</comment>
+ <comment xml:lang="da">Opus-lyd</comment>
+ <comment xml:lang="de">Opus-Audio</comment>
+ <comment xml:lang="el">Ήχος Opus </comment>
+ <comment xml:lang="en_GB">Opus audio</comment>
+ <comment xml:lang="es">sonido Opus</comment>
+ <comment xml:lang="eu">Opus audioa</comment>
+ <comment xml:lang="fi">Opus-ääni</comment>
+ <comment xml:lang="fr">audio Opus</comment>
+ <comment xml:lang="ga">fuaim Opus</comment>
+ <comment xml:lang="gl">Son Opus</comment>
+ <comment xml:lang="he">שמע Opus</comment>
+ <comment xml:lang="hr">Opus zvučni zapis</comment>
+ <comment xml:lang="hu">Opus hang</comment>
+ <comment xml:lang="ia">Audio Opus</comment>
+ <comment xml:lang="id">Audio Opus</comment>
+ <comment xml:lang="it">Audio Opus</comment>
+ <comment xml:lang="kk">Opus аудиосы</comment>
+ <comment xml:lang="ko">Opus 오디오</comment>
+ <comment xml:lang="oc">àudio Opus</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Opus</comment>
+ <comment xml:lang="pt">áudio Opus</comment>
+ <comment xml:lang="pt_BR">Áudio Opus</comment>
+ <comment xml:lang="ru">Аудио Opus</comment>
+ <comment xml:lang="sk">Zvuk Opu</comment>
+ <comment xml:lang="sl">Zvočna datoteka Opus</comment>
+ <comment xml:lang="sr">Опус звук</comment>
+ <comment xml:lang="sv">Opus-ljud</comment>
+ <comment xml:lang="tr">Opus sesi</comment>
+ <comment xml:lang="uk">звук Opus</comment>
+ <comment xml:lang="zh_CN">Opus 音频</comment>
+ <comment xml:lang="zh_TW">Opus 音訊</comment>
+ <sub-class-of type="audio/ogg"/>
+ <magic priority="80">
+ <match value="OggS" type="string" offset="0">
+ <match value="OpusHead" type="string" offset="28"/>
+ </match>
+ </magic>
+ <glob pattern="*.opus"/>
+ </mime-type>
+ <mime-type type="audio/x-speex+ogg">
+ <comment>Ogg Speex audio</comment>
+ <comment xml:lang="ar">Ogg Speex سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Ogg Speex</comment>
+ <comment xml:lang="bg">Аудио — Ogg Speex</comment>
+ <comment xml:lang="ca">àudio Speex d'Ogg</comment>
+ <comment xml:lang="cs">zvuk Ogg Speex</comment>
+ <comment xml:lang="da">Ogg Speex-lyd</comment>
+ <comment xml:lang="de">Ogg-Speex-Audio</comment>
+ <comment xml:lang="el">Ήχος Ogg Speex</comment>
+ <comment xml:lang="en_GB">Ogg Speex audio</comment>
+ <comment xml:lang="es">sonido Ogg Speex</comment>
+ <comment xml:lang="eu">Ogg Speex audioa</comment>
+ <comment xml:lang="fi">Ogg Speex -ääni</comment>
+ <comment xml:lang="fo">Ogg Speex ljóður</comment>
+ <comment xml:lang="fr">audio Ogg Speex</comment>
+ <comment xml:lang="ga">fuaim Ogg Speex</comment>
+ <comment xml:lang="gl">son Ogg Speex</comment>
+ <comment xml:lang="he">שמע Ogg Speex</comment>
+ <comment xml:lang="hr">Ogg Speex zvučni zapis</comment>
+ <comment xml:lang="hu">Ogg Speex hang</comment>
+ <comment xml:lang="ia">Audio Ogg Speex</comment>
+ <comment xml:lang="id">Audio Ogg Speex</comment>
+ <comment xml:lang="it">Audio Ogg Speex</comment>
+ <comment xml:lang="ja">Ogg Speex オーディオ</comment>
+ <comment xml:lang="ka">Ogg Speex აუდიო</comment>
+ <comment xml:lang="kk">Ogg Speex аудиосы</comment>
+ <comment xml:lang="ko">Ogg Speex 오디오</comment>
+ <comment xml:lang="lt">Ogg Speex garso įrašas</comment>
+ <comment xml:lang="lv">Ogg Speex audio</comment>
+ <comment xml:lang="nb">Ogg Speex lyd</comment>
+ <comment xml:lang="nl">Ogg Speex-audio</comment>
+ <comment xml:lang="nn">Ogg Speex-lyd</comment>
+ <comment xml:lang="oc">àudio Ogg Speex</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Ogg Speex</comment>
+ <comment xml:lang="pt">áudio Ogg Speex</comment>
+ <comment xml:lang="pt_BR">Áudio Ogg Speex</comment>
+ <comment xml:lang="ro">Audio Ogg Speex</comment>
+ <comment xml:lang="ru">Аудио Ogg Speex</comment>
+ <comment xml:lang="sk">Zvuk Ogg Speex</comment>
+ <comment xml:lang="sl">Zvočna datoteka Ogg Speex</comment>
+ <comment xml:lang="sq">Audio Ogg Speex</comment>
+ <comment xml:lang="sr">Огг Спикс звук</comment>
+ <comment xml:lang="sv">Ogg Speex-ljud</comment>
+ <comment xml:lang="tr">Ogg Speex sesi</comment>
+ <comment xml:lang="uk">звук ogg Speex</comment>
+ <comment xml:lang="vi">Âm thanh Speex Ogg</comment>
+ <comment xml:lang="zh_CN">Ogg Speex 音频</comment>
+ <comment xml:lang="zh_TW">Ogg Speex 音訊</comment>
+ <sub-class-of type="audio/ogg"/>
+ <magic priority="80">
+ <match value="OggS" type="string" offset="0">
+ <match value="Speex " type="string" offset="28"/>
+ </match>
+ </magic>
+ <glob pattern="*.oga"/>
+ <glob pattern="*.ogg"/>
+ </mime-type>
+ <mime-type type="audio/x-speex">
+ <comment>Speex audio</comment>
+ <comment xml:lang="ar">Speex سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Speex</comment>
+ <comment xml:lang="bg">Аудио — Speex</comment>
+ <comment xml:lang="ca">àudio de Speex</comment>
+ <comment xml:lang="cs">zvuk Speex</comment>
+ <comment xml:lang="da">Speexlyd</comment>
+ <comment xml:lang="de">Speex-Audio</comment>
+ <comment xml:lang="el">Ήχος Speex</comment>
+ <comment xml:lang="en_GB">Speex audio</comment>
+ <comment xml:lang="es">sonido Speex</comment>
+ <comment xml:lang="eu">Speex audioa</comment>
+ <comment xml:lang="fi">Speex-ääni</comment>
+ <comment xml:lang="fo">Speex ljóður</comment>
+ <comment xml:lang="fr">audio Speex</comment>
+ <comment xml:lang="ga">fuaim Speex</comment>
+ <comment xml:lang="gl">son Speex</comment>
+ <comment xml:lang="he">שמע של Speex</comment>
+ <comment xml:lang="hr">Speex zvučni zapis</comment>
+ <comment xml:lang="hu">Speex hang</comment>
+ <comment xml:lang="ia">Audio Speex</comment>
+ <comment xml:lang="id">Audio Speex</comment>
+ <comment xml:lang="it">Audio Speex</comment>
+ <comment xml:lang="ja">Speex オーディオ</comment>
+ <comment xml:lang="kk">Speex аудиосы</comment>
+ <comment xml:lang="ko">Speex 오디오</comment>
+ <comment xml:lang="lt">Speex garso įrašas</comment>
+ <comment xml:lang="lv">Speex audio</comment>
+ <comment xml:lang="nb">Speex lyd</comment>
+ <comment xml:lang="nl">Speex-audio</comment>
+ <comment xml:lang="nn">Speex-lyd</comment>
+ <comment xml:lang="oc">àudio Speex</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Speex</comment>
+ <comment xml:lang="pt">áudio Speex</comment>
+ <comment xml:lang="pt_BR">Áudio Speex</comment>
+ <comment xml:lang="ro">Audio Speex</comment>
+ <comment xml:lang="ru">Аудио Speex</comment>
+ <comment xml:lang="sk">Zvuk Speex</comment>
+ <comment xml:lang="sl">Zvočna datoteka Speex</comment>
+ <comment xml:lang="sq">Audio Speex</comment>
+ <comment xml:lang="sr">Спикс звук</comment>
+ <comment xml:lang="sv">Speex-ljud</comment>
+ <comment xml:lang="tr">Speex sesi</comment>
+ <comment xml:lang="uk">звук Speex</comment>
+ <comment xml:lang="vi">Âm thanh Speex</comment>
+ <comment xml:lang="zh_CN">Speex 音频</comment>
+ <comment xml:lang="zh_TW">Speex 音訊</comment>
+ <magic priority="50">
+ <match value="Speex" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.spx"/>
+ </mime-type>
+ <mime-type type="video/x-theora+ogg">
+ <comment>Ogg Theora video</comment>
+ <comment xml:lang="ar">Ogg Theora مرئي</comment>
+ <comment xml:lang="ast">Videu n'Ogg Theora</comment>
+ <comment xml:lang="be@latin">Videa Ogg Theora</comment>
+ <comment xml:lang="bg">Видео — Ogg Theora</comment>
+ <comment xml:lang="ca">vídeo Ogg Theora</comment>
+ <comment xml:lang="cs">video Ogg Theora</comment>
+ <comment xml:lang="da">Ogg Theora-video</comment>
+ <comment xml:lang="de">Ogg-Theora-Video</comment>
+ <comment xml:lang="el">Βίντεο Ogg Theora</comment>
+ <comment xml:lang="en_GB">Ogg Theora video</comment>
+ <comment xml:lang="es">vídeo Ogg Theora</comment>
+ <comment xml:lang="eu">Ogg Theora bideoa</comment>
+ <comment xml:lang="fi">Ogg Theora -video</comment>
+ <comment xml:lang="fo">Ogg Theora video</comment>
+ <comment xml:lang="fr">vidéo Ogg Theora</comment>
+ <comment xml:lang="ga">físeán Ogg Theora</comment>
+ <comment xml:lang="gl">vídeo Ogg Theora</comment>
+ <comment xml:lang="he">שמע Ogg Theora</comment>
+ <comment xml:lang="hr">Ogg Theora video snimka</comment>
+ <comment xml:lang="hu">Ogg Theora videó</comment>
+ <comment xml:lang="ia">Video Ogg Theora</comment>
+ <comment xml:lang="id">Video Ogg Theora</comment>
+ <comment xml:lang="it">Video Ogg Theora</comment>
+ <comment xml:lang="ja">Ogg Theora 動画</comment>
+ <comment xml:lang="ka">Ogg Theora ვიდეო</comment>
+ <comment xml:lang="kk">Ogg Theora видеосы</comment>
+ <comment xml:lang="ko">Ogg Theora 동영상</comment>
+ <comment xml:lang="lt">Ogg Theora vaizdo įrašas</comment>
+ <comment xml:lang="lv">Ogg Theora video</comment>
+ <comment xml:lang="nb">Ogg Theora video</comment>
+ <comment xml:lang="nl">Ogg Theora-video</comment>
+ <comment xml:lang="nn">Ogg Theora-video</comment>
+ <comment xml:lang="oc">vidèo Ogg Theora</comment>
+ <comment xml:lang="pl">Plik wideo Ogg Theora</comment>
+ <comment xml:lang="pt">vídeo Ogg Theora</comment>
+ <comment xml:lang="pt_BR">Vídeo Ogg Theora</comment>
+ <comment xml:lang="ro">Video Ogg Theora</comment>
+ <comment xml:lang="ru">Видео Ogg Theora</comment>
+ <comment xml:lang="sk">Video Ogg Theora</comment>
+ <comment xml:lang="sl">Video datoteka Ogg Theora</comment>
+ <comment xml:lang="sq">Video Ogg Theora</comment>
+ <comment xml:lang="sr">Огг Теора видео</comment>
+ <comment xml:lang="sv">Ogg Theora-video</comment>
+ <comment xml:lang="tr">Ogg Theora video</comment>
+ <comment xml:lang="uk">відеокліп ogg Theora</comment>
+ <comment xml:lang="vi">Ảnh động Theora Ogg</comment>
+ <comment xml:lang="zh_CN">Ogg Theora 视频</comment>
+ <comment xml:lang="zh_TW">Ogg Theora 視訊</comment>
+ <sub-class-of type="video/ogg"/>
+ <alias type="video/x-theora"/>
+ <magic priority="80">
+ <match value="OggS" type="string" offset="0">
+ <match value="\x80theora" type="string" offset="28"/>
+ </match>
+ </magic>
+ <glob pattern="*.ogg"/>
+ </mime-type>
+ <mime-type type="video/x-ogm+ogg">
+ <comment>OGM video</comment>
+ <comment xml:lang="ar">OGM مرئي</comment>
+ <comment xml:lang="ast">Videu n'OGM</comment>
+ <comment xml:lang="be@latin">Videa OGM</comment>
+ <comment xml:lang="bg">Видео — OGM</comment>
+ <comment xml:lang="ca">vídeo OGM</comment>
+ <comment xml:lang="cs">video OGM</comment>
+ <comment xml:lang="da">OGM-video</comment>
+ <comment xml:lang="de">OGM-Video</comment>
+ <comment xml:lang="el">Βίντεο OGM</comment>
+ <comment xml:lang="en_GB">OGM video</comment>
+ <comment xml:lang="eo">OGM-video</comment>
+ <comment xml:lang="es">vídeo OGM</comment>
+ <comment xml:lang="eu">OGM bideoa</comment>
+ <comment xml:lang="fi">OGM-video</comment>
+ <comment xml:lang="fo">OGM video</comment>
+ <comment xml:lang="fr">vidéo OGM</comment>
+ <comment xml:lang="ga">físeán OGM</comment>
+ <comment xml:lang="gl">vídeo OGM</comment>
+ <comment xml:lang="he">וידאו OGM</comment>
+ <comment xml:lang="hr">OGM video snimka</comment>
+ <comment xml:lang="hu">OGM-videó</comment>
+ <comment xml:lang="ia">Video OGM</comment>
+ <comment xml:lang="id">Video OGM</comment>
+ <comment xml:lang="it">Video OGM</comment>
+ <comment xml:lang="ja">OGM 動画</comment>
+ <comment xml:lang="ka">OGM ვიდეო</comment>
+ <comment xml:lang="kk">OGM видеосы</comment>
+ <comment xml:lang="ko">OGM 동영상</comment>
+ <comment xml:lang="lt">OGM vaizdo įrašas</comment>
+ <comment xml:lang="lv">OGM video</comment>
+ <comment xml:lang="nb">OGM-film</comment>
+ <comment xml:lang="nl">OGM-video</comment>
+ <comment xml:lang="nn">OGM-video</comment>
+ <comment xml:lang="oc">vidèo OGM</comment>
+ <comment xml:lang="pl">Plik wideo OGM</comment>
+ <comment xml:lang="pt">vídeo OGM</comment>
+ <comment xml:lang="pt_BR">Vídeo OGM</comment>
+ <comment xml:lang="ro">Video OGM</comment>
+ <comment xml:lang="ru">Видео OGM</comment>
+ <comment xml:lang="sk">Video OGM</comment>
+ <comment xml:lang="sl">Video datoteka OGM</comment>
+ <comment xml:lang="sq">Video OGM</comment>
+ <comment xml:lang="sr">ОГМ видео</comment>
+ <comment xml:lang="sv">OGM-video</comment>
+ <comment xml:lang="tr">OGM video</comment>
+ <comment xml:lang="uk">відеокліп OGM</comment>
+ <comment xml:lang="vi">Ảnh động OGM</comment>
+ <comment xml:lang="zh_CN">OGM 视频</comment>
+ <comment xml:lang="zh_TW">OGM 視訊</comment>
+ <sub-class-of type="video/ogg"/>
+ <alias type="video/x-ogm"/>
+ <magic priority="80">
+ <match value="OggS" type="string" offset="0">
+ <match value="video" type="string" offset="29"/>
+ </match>
+ </magic>
+ <glob pattern="*.ogm"/>
+ </mime-type>
+
+ <mime-type type="application/x-ole-storage">
+ <comment>OLE2 compound document storage</comment>
+ <comment xml:lang="ar">تخزين مجمع مستند OLE2</comment>
+ <comment xml:lang="ast">Almacenamientu de documentos compuestu por OLE2</comment>
+ <comment xml:lang="be@latin">Schovišča dla kampanentaŭ dakumentu OLE2</comment>
+ <comment xml:lang="bg">Съставен документ-хранилище — OLE2</comment>
+ <comment xml:lang="ca">emmagatzematge de documents compostos OLE2</comment>
+ <comment xml:lang="cs">úložiště složeného dokumentu OLE2</comment>
+ <comment xml:lang="da">OLE2-sammensat dokumentlager</comment>
+ <comment xml:lang="de">OLE2-Verbunddokumentenspeicher</comment>
+ <comment xml:lang="el">Αρχείο συμπαγούς αποθήκευσης εγγράφων OLE2</comment>
+ <comment xml:lang="en_GB">OLE2 compound document storage</comment>
+ <comment xml:lang="eo">OLE2-deponejo de parentezaj dokumentoj</comment>
+ <comment xml:lang="es">almacenamiento de documentos compuestos OLE2</comment>
+ <comment xml:lang="eu">OLE2 konposatutako dokumentu-bilduma</comment>
+ <comment xml:lang="fi">OLE2-yhdisteasiakirjatallenne</comment>
+ <comment xml:lang="fo">OLE2 samansett skjalagoymsla</comment>
+ <comment xml:lang="fr">document de stockage composé OLE2</comment>
+ <comment xml:lang="ga">stóras cáipéisí comhshuite OLE2</comment>
+ <comment xml:lang="gl">almacenamento de documento composto OLE2</comment>
+ <comment xml:lang="he">אחסון מסמך משותף OLE2</comment>
+ <comment xml:lang="hr">OLE2 pohrana složenog dokumenta</comment>
+ <comment xml:lang="hu">OLE2 összetett dokumentumtároló</comment>
+ <comment xml:lang="ia">Magazin de documentos composite OLE2</comment>
+ <comment xml:lang="id">penyimpan dokumen kompon OLE2</comment>
+ <comment xml:lang="it">Memorizzazione documento composto OLE2</comment>
+ <comment xml:lang="ja">OLE2 複合ドキュメントストレージ</comment>
+ <comment xml:lang="kk">OLE2 құрама құжаттар қоймасы</comment>
+ <comment xml:lang="ko">OLE2 복합 문서</comment>
+ <comment xml:lang="lt">OLE2 sudėtinių dokumentų laikmena</comment>
+ <comment xml:lang="lv">OLE2 savienoto dokumentu glabātuve</comment>
+ <comment xml:lang="ms">Storan dokumen halaman OLE2</comment>
+ <comment xml:lang="nb">OLE-lager for sammensatte dokumenter</comment>
+ <comment xml:lang="nl">OLE2-samengestelde documentopslag</comment>
+ <comment xml:lang="nn">OLE2 lager for samansett dokument</comment>
+ <comment xml:lang="oc">document d'emmagazinatge compausat OLE2</comment>
+ <comment xml:lang="pl">Magazyn dokumentu złożonego OLE2</comment>
+ <comment xml:lang="pt">armazenamento de documento composto OLE2</comment>
+ <comment xml:lang="pt_BR">Armazenamento de documento composto OLE2</comment>
+ <comment xml:lang="ro">Document de stocare compus OLE2</comment>
+ <comment xml:lang="ru">Хранилище составных документов OLE2</comment>
+ <comment xml:lang="sk">Úložisko zloženého dokumentu OLE2</comment>
+ <comment xml:lang="sl">Združeni dokument OLE2</comment>
+ <comment xml:lang="sq">Arkiv dokumenti i përbërë OLE2</comment>
+ <comment xml:lang="sr">смештај ОЛЕ2 сједињеног документа</comment>
+ <comment xml:lang="sv">Sammansatt OLE2-dokumentlager</comment>
+ <comment xml:lang="tr">OLE2 bileşik belge depolama</comment>
+ <comment xml:lang="uk">сховище складних документів OLE2</comment>
+ <comment xml:lang="vi">Kho lưu tài liệu ghép OLE2</comment>
+ <comment xml:lang="zh_CN">OLE2 组合文档存储</comment>
+ <comment xml:lang="zh_TW">OLE2 複合文件儲存</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="\320\317\021\340\241\261\032\341" type="string" offset="0"/>
+ <match value="0xe011cfd0" type="little32" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/vnd.ms-publisher">
+ <comment>Microsoft Publisher document</comment>
+ <comment xml:lang="ast">Documentu de Microsoft Publisher</comment>
+ <comment xml:lang="ca">document de Microsoft Publisher</comment>
+ <comment xml:lang="cs">dokument Microsoft Publisher</comment>
+ <comment xml:lang="da">Microsoft Publisher-dokument</comment>
+ <comment xml:lang="de">Microsoft-Publisher-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Microsoft Publisher</comment>
+ <comment xml:lang="en_GB">Microsoft Publisher document</comment>
+ <comment xml:lang="es">documento de Microsoft Publisher</comment>
+ <comment xml:lang="eu">Microsoft Publisher dokumentua</comment>
+ <comment xml:lang="fi">Microsoft Publisher -asiakirja</comment>
+ <comment xml:lang="fr">document Microsoft Publisher</comment>
+ <comment xml:lang="ga">cáipéis Microsoft Publisher</comment>
+ <comment xml:lang="gl">Documento de Microsoft Publisher</comment>
+ <comment xml:lang="he">מסמך Microsoft Publisher</comment>
+ <comment xml:lang="hr">Microsoft Publisher dokument</comment>
+ <comment xml:lang="hu">Microsoft Publisher dokumentum</comment>
+ <comment xml:lang="ia">Documento Microsoft Publisher</comment>
+ <comment xml:lang="id">Dokumen Microsoft Publisher</comment>
+ <comment xml:lang="it">Documento Microsoft Publisher</comment>
+ <comment xml:lang="kk">Microsoft Publisher құжаты</comment>
+ <comment xml:lang="ko">Microsoft Publisher 문서</comment>
+ <comment xml:lang="oc">document Microsoft Publisher</comment>
+ <comment xml:lang="pl">Dokument Microsoft Publisher</comment>
+ <comment xml:lang="pt">documento Microsoft Publisher</comment>
+ <comment xml:lang="pt_BR">Documento do Microsoft Publisher</comment>
+ <comment xml:lang="ru">Документ Microsoft Publisher</comment>
+ <comment xml:lang="sk">Dokument Microsoft Publisher</comment>
+ <comment xml:lang="sl">Dokument Microsoft Publisher</comment>
+ <comment xml:lang="sr">документ Мајкрософтовог Издавача</comment>
+ <comment xml:lang="sv">Microsoft Publisher-dokument</comment>
+ <comment xml:lang="tr">Microsoft Publisher belgesi</comment>
+ <comment xml:lang="uk">документ Microsoft Publisher</comment>
+ <comment xml:lang="zh_CN">Microsoft Publisher 文档</comment>
+ <comment xml:lang="zh_TW">Microsoft Publisher 文件</comment>
+ <sub-class-of type="application/x-ole-storage"/>
+ <glob pattern="*.pub"/>
+ </mime-type>
+ <mime-type type="application/x-msi">
+ <comment>Windows Installer package</comment>
+ <comment xml:lang="ar">حزمة مثبّت ويندوز</comment>
+ <comment xml:lang="be@latin">Pakunak Windows Installer</comment>
+ <comment xml:lang="bg">Пакет — инсталация за Windows</comment>
+ <comment xml:lang="ca">paquet de Windows Installer</comment>
+ <comment xml:lang="cs">balíček Windows Installer</comment>
+ <comment xml:lang="da">Windows Installer-pakke</comment>
+ <comment xml:lang="de">Windows-Installationspaket</comment>
+ <comment xml:lang="el">Πακέτο Windows Installer</comment>
+ <comment xml:lang="en_GB">Windows Installer package</comment>
+ <comment xml:lang="es">paquete de instalación de Windows</comment>
+ <comment xml:lang="eu">Windows-eko pakete instalatzailea</comment>
+ <comment xml:lang="fi">Windows-asennuspaketti</comment>
+ <comment xml:lang="fo">Windows innleggingarpakki</comment>
+ <comment xml:lang="fr">paquet d'installation Windows</comment>
+ <comment xml:lang="ga">pacáiste Windows Installer</comment>
+ <comment xml:lang="gl">paquete de instalación de Windows</comment>
+ <comment xml:lang="he">חבילה של Windows Installer</comment>
+ <comment xml:lang="hr">Windows Instalacijski paket</comment>
+ <comment xml:lang="hu">Windows Installer csomag</comment>
+ <comment xml:lang="ia">Pacchetto Windows Installer</comment>
+ <comment xml:lang="id">Paket Windows Installer</comment>
+ <comment xml:lang="it">Pacchetto Windows Installer</comment>
+ <comment xml:lang="ja">Windows インストーラパッケージ</comment>
+ <comment xml:lang="kk">Windows Installer дестесі</comment>
+ <comment xml:lang="ko">Windows 설치 패키지</comment>
+ <comment xml:lang="lt">Windows Installer paketas</comment>
+ <comment xml:lang="lv">Windows Installer pakotne</comment>
+ <comment xml:lang="nl">Windows-installatiepakket</comment>
+ <comment xml:lang="nn">Windows Installer-pakke</comment>
+ <comment xml:lang="oc">paquet d'installacion Windows</comment>
+ <comment xml:lang="pl">Pakiet instalatora Windows</comment>
+ <comment xml:lang="pt">pacote de instalação Windows</comment>
+ <comment xml:lang="pt_BR">Pacote do Windows Installer</comment>
+ <comment xml:lang="ro">Pachet instalator Windows</comment>
+ <comment xml:lang="ru">Пакет Windows Installer</comment>
+ <comment xml:lang="sk">Balík Windows Installer</comment>
+ <comment xml:lang="sl">Datoteka paketa Windows namestilnika</comment>
+ <comment xml:lang="sq">Paketë Windows Installer</comment>
+ <comment xml:lang="sr">пакет Виндоузовог инсталатера</comment>
+ <comment xml:lang="sv">Windows Installer-paket</comment>
+ <comment xml:lang="tr">Windows Installer paketi</comment>
+ <comment xml:lang="uk">пакунок Windows Installer</comment>
+ <comment xml:lang="vi">Gói cài đặt Windows</comment>
+ <comment xml:lang="zh_CN">Windows 程序安装包</comment>
+ <comment xml:lang="zh_TW">Windows Installer 軟體包</comment>
+ <sub-class-of type="application/x-ole-storage"/>
+ <glob pattern="*.msi"/>
+ </mime-type>
+ <mime-type type="application/x-oleo">
+ <comment>GNU Oleo spreadsheet</comment>
+ <comment xml:lang="ar">جدول جنو Oleo</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš GNU Oleo</comment>
+ <comment xml:lang="bg">Таблица — GNU Oleo</comment>
+ <comment xml:lang="ca">full de càlcul de GNU Oleo</comment>
+ <comment xml:lang="cs">sešit GNU Oleo</comment>
+ <comment xml:lang="da">GNU Oleo-regneark</comment>
+ <comment xml:lang="de">GNU-Oleo-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο GNU Oleo</comment>
+ <comment xml:lang="en_GB">GNU Oleo spreadsheet</comment>
+ <comment xml:lang="eo">Kalkultabelo de GNU Oleo</comment>
+ <comment xml:lang="es">hoja de cálculo de GNU Oleo</comment>
+ <comment xml:lang="eu">GNU Oleo kalkulu-orria</comment>
+ <comment xml:lang="fi">GNU Oleo -taulukko</comment>
+ <comment xml:lang="fo">GNU Oleo rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul GNU Oleo</comment>
+ <comment xml:lang="ga">scarbhileog GNU Oleo</comment>
+ <comment xml:lang="gl">folla de cálculo de Oleo GNU</comment>
+ <comment xml:lang="he">גליון נתונים של GNU Oleo</comment>
+ <comment xml:lang="hr">GNU Oleo proračunska tablica</comment>
+ <comment xml:lang="hu">GNU Oleo-munkafüzet</comment>
+ <comment xml:lang="ia">Folio de calculo GNU Oleo</comment>
+ <comment xml:lang="id">Lembar sebar GNU Oleo</comment>
+ <comment xml:lang="it">Foglio di calcolo GNU Oleo</comment>
+ <comment xml:lang="ja">GNU Oleo スプレッドシート</comment>
+ <comment xml:lang="ka">GNU Oleo ცხრილი</comment>
+ <comment xml:lang="kk">GNU Oleo электрондық кестесі</comment>
+ <comment xml:lang="ko">GNU Oleo 스프레드시트</comment>
+ <comment xml:lang="lt">GNU Oleo skaičialentė</comment>
+ <comment xml:lang="lv">GNU Oleo izklājlapa</comment>
+ <comment xml:lang="ms">Hamparan GNU Oleo</comment>
+ <comment xml:lang="nb">GNU Oleo regneark</comment>
+ <comment xml:lang="nl">GNU Oleo-rekenblad</comment>
+ <comment xml:lang="nn">GNU Oleo-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul GNU Oleo</comment>
+ <comment xml:lang="pl">Arkusz GNU Oleo</comment>
+ <comment xml:lang="pt">folha de cálculo GNU Oleo</comment>
+ <comment xml:lang="pt_BR">Planilha do GNU Oleo</comment>
+ <comment xml:lang="ro">Foaie de calcul GNU Oleo</comment>
+ <comment xml:lang="ru">Электронная таблица GNU Oleo</comment>
+ <comment xml:lang="sk">Zošit GNU Oleo</comment>
+ <comment xml:lang="sl">Preglednica GNU Oleo</comment>
+ <comment xml:lang="sq">Fletë llogaritje GNU Oleo</comment>
+ <comment xml:lang="sr">ГНУ Олео табела</comment>
+ <comment xml:lang="sv">GNU Oleo-kalkylblad</comment>
+ <comment xml:lang="tr">GNU Oleo çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця GNU Oleo</comment>
+ <comment xml:lang="vi">Bảng tính Oleo của GNU</comment>
+ <comment xml:lang="zh_CN">GNU Oleo 电子表格</comment>
+ <comment xml:lang="zh_TW">GNU Oleo 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="50">
+ <match value="Oleo" type="string" offset="31"/>
+ </magic>
+ <glob pattern="*.oleo"/>
+ </mime-type>
+ <mime-type type="application/x-pak">
+ <comment>PAK archive</comment>
+ <comment xml:lang="ar">أرشيف PAK</comment>
+ <comment xml:lang="be@latin">Archiŭ PAK</comment>
+ <comment xml:lang="bg">Архив — PAK</comment>
+ <comment xml:lang="ca">arxiu PAK</comment>
+ <comment xml:lang="cs">archiv PAK</comment>
+ <comment xml:lang="da">PAK-arkiv</comment>
+ <comment xml:lang="de">PAK-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο PAK</comment>
+ <comment xml:lang="en_GB">PAK archive</comment>
+ <comment xml:lang="eo">PAK-arkivo</comment>
+ <comment xml:lang="es">archivador PAK</comment>
+ <comment xml:lang="eu">PAK artxiboa</comment>
+ <comment xml:lang="fi">PAK-arkisto</comment>
+ <comment xml:lang="fo">PAK skjalasavn</comment>
+ <comment xml:lang="fr">archive PAK</comment>
+ <comment xml:lang="ga">cartlann PAK</comment>
+ <comment xml:lang="gl">arquivo PAK</comment>
+ <comment xml:lang="he">ארכיון PAK</comment>
+ <comment xml:lang="hr">PAK arhiva</comment>
+ <comment xml:lang="hu">PAK-archívum</comment>
+ <comment xml:lang="ia">Archivo PAK</comment>
+ <comment xml:lang="id">Arsip PAK</comment>
+ <comment xml:lang="it">Archivio PAK</comment>
+ <comment xml:lang="ja">PAK アーカイブ</comment>
+ <comment xml:lang="ka">PAK არქივი</comment>
+ <comment xml:lang="kk">PAK архиві</comment>
+ <comment xml:lang="ko">PAK 압축 파일</comment>
+ <comment xml:lang="lt">PAK archyvas</comment>
+ <comment xml:lang="lv">PAK arhīvs</comment>
+ <comment xml:lang="nb">PAK-arkiv</comment>
+ <comment xml:lang="nl">PAK-archief</comment>
+ <comment xml:lang="nn">PAK-arkiv</comment>
+ <comment xml:lang="oc">archiu PAK</comment>
+ <comment xml:lang="pl">Archiwum PAK</comment>
+ <comment xml:lang="pt">arquivo PAK</comment>
+ <comment xml:lang="pt_BR">Pacote PAK</comment>
+ <comment xml:lang="ro">Arhivă PAK</comment>
+ <comment xml:lang="ru">Архив PAK</comment>
+ <comment xml:lang="sk">Archív PAK</comment>
+ <comment xml:lang="sl">Datoteka arhiva PAK</comment>
+ <comment xml:lang="sq">Arkiv PAK</comment>
+ <comment xml:lang="sr">ПАК архива</comment>
+ <comment xml:lang="sv">PAK-arkiv</comment>
+ <comment xml:lang="tr">PAK arşivi</comment>
+ <comment xml:lang="uk">архів PAK</comment>
+ <comment xml:lang="vi">Kho nén PAK</comment>
+ <comment xml:lang="zh_CN">PAK 归档文件</comment>
+ <comment xml:lang="zh_TW">PAK 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="80">
+ <match value="PACK" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.pak"/>
+ </mime-type>
+ <mime-type type="application/vnd.palm">
+ <comment>Palm OS database</comment>
+ <comment xml:lang="ar">قاعدة بيانات Palm OS</comment>
+ <comment xml:lang="az">Palm OS mə'lumat bazası</comment>
+ <comment xml:lang="be@latin">Baza źviestak Palm OS</comment>
+ <comment xml:lang="bg">База от данни — Palm OS</comment>
+ <comment xml:lang="ca">base de dades Palm OS</comment>
+ <comment xml:lang="cs">databáze Palm OS</comment>
+ <comment xml:lang="cy">Cronfa Ddata Palm OS</comment>
+ <comment xml:lang="da">Palm OS-database</comment>
+ <comment xml:lang="de">Palm-OS-Datenbank</comment>
+ <comment xml:lang="el">Βάση δεδομένων Palm OS</comment>
+ <comment xml:lang="en_GB">Palm OS database</comment>
+ <comment xml:lang="eo">datumbazo de Palm OS</comment>
+ <comment xml:lang="es">base de datos de Palm OS</comment>
+ <comment xml:lang="eu">Palm OS datu-basea</comment>
+ <comment xml:lang="fi">Palm OS -tietokanta</comment>
+ <comment xml:lang="fo">Palm OS dátustovnur</comment>
+ <comment xml:lang="fr">base de données Palm OS</comment>
+ <comment xml:lang="ga">bunachar sonraí Palm OS</comment>
+ <comment xml:lang="gl">base de datos de Palm OS</comment>
+ <comment xml:lang="he">מסד נתונים של Palm OS</comment>
+ <comment xml:lang="hr">Palm OS baza podataka</comment>
+ <comment xml:lang="hu">Palm OS-adatbázis</comment>
+ <comment xml:lang="ia">Base de datos Palm OS</comment>
+ <comment xml:lang="id">Basis data Palm OS</comment>
+ <comment xml:lang="it">Database Palm OS</comment>
+ <comment xml:lang="ja">Palm OS データベース</comment>
+ <comment xml:lang="kk">Palm OS дерекқоры</comment>
+ <comment xml:lang="ko">Palm OS 데이터베이스</comment>
+ <comment xml:lang="lt">Palm OS duomenų bazė</comment>
+ <comment xml:lang="lv">Palm OS datubāze</comment>
+ <comment xml:lang="ms">Pangkalandata PalmOS</comment>
+ <comment xml:lang="nb">Palm OS-database</comment>
+ <comment xml:lang="nl">Palm OS-gegevensbank</comment>
+ <comment xml:lang="nn">Palm OS-database</comment>
+ <comment xml:lang="oc">banca de donadas Palm OS</comment>
+ <comment xml:lang="pl">Baza danych Palm OS</comment>
+ <comment xml:lang="pt">base de dados Palm OS</comment>
+ <comment xml:lang="pt_BR">Banco de dados do Palm OS</comment>
+ <comment xml:lang="ro">Bază de date Palm OS</comment>
+ <comment xml:lang="ru">База данных Palm OS</comment>
+ <comment xml:lang="sk">Databáza Palm OS</comment>
+ <comment xml:lang="sl">Podatkovna zbirka Palm OS</comment>
+ <comment xml:lang="sq">Bankë me të dhëna Palm OS</comment>
+ <comment xml:lang="sr">база података Палм ОС-а</comment>
+ <comment xml:lang="sv">Palm OS-databas</comment>
+ <comment xml:lang="tr">Palm OS veritabanı</comment>
+ <comment xml:lang="uk">база даних Palm OS</comment>
+ <comment xml:lang="vi">Cơ sở dữ liệu PalmOS</comment>
+ <comment xml:lang="zh_CN">Palm OS 数据库</comment>
+ <comment xml:lang="zh_TW">Palm OS 資料庫</comment>
+ <glob pattern="*.prc"/>
+ <glob pattern="*.pdb"/>
+ <glob pattern="*.pqa"/>
+ <glob pattern="*.oprc"/>
+ <alias type="application/x-palm-database"/>
+ </mime-type>
+ <mime-type type="application/x-par2">
+ <comment>Parchive archive</comment>
+ <comment xml:lang="ar">أرشيف Parchive</comment>
+ <comment xml:lang="be@latin">Archiŭ Parchive</comment>
+ <comment xml:lang="bg">Архив — parchive</comment>
+ <comment xml:lang="ca">arxiu Parchive</comment>
+ <comment xml:lang="cs">archiv Parchive</comment>
+ <comment xml:lang="da">Parchive-arkiv</comment>
+ <comment xml:lang="de">Parchive-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Parchive</comment>
+ <comment xml:lang="en_GB">Parchive archive</comment>
+ <comment xml:lang="es">archivador Parchive</comment>
+ <comment xml:lang="eu">Parchive artxiboa</comment>
+ <comment xml:lang="fi">Parchive-arkisto</comment>
+ <comment xml:lang="fo">Parchive skjalasavn</comment>
+ <comment xml:lang="fr">archive Parchive</comment>
+ <comment xml:lang="ga">cartlann Parchive</comment>
+ <comment xml:lang="gl">arquivo Parchive</comment>
+ <comment xml:lang="he">ארכיון של Parchive</comment>
+ <comment xml:lang="hr">Parchive arhiva</comment>
+ <comment xml:lang="hu">Parchive archívum</comment>
+ <comment xml:lang="ia">Archivo Parchive</comment>
+ <comment xml:lang="id">Arsip Parchive</comment>
+ <comment xml:lang="it">Archivio Parchive</comment>
+ <comment xml:lang="ja">Parchive アーカイブ</comment>
+ <comment xml:lang="kk">Parchive архиві</comment>
+ <comment xml:lang="ko">Parchive 압축 파일</comment>
+ <comment xml:lang="lt">Parchive archyvas</comment>
+ <comment xml:lang="lv">Parchive arhīvs</comment>
+ <comment xml:lang="nb">Parchive-arkiv</comment>
+ <comment xml:lang="nl">Parchive-archief</comment>
+ <comment xml:lang="nn">Parchive-arkiv</comment>
+ <comment xml:lang="oc">archiu Parchive</comment>
+ <comment xml:lang="pl">Archiwum parchive</comment>
+ <comment xml:lang="pt">arquivo Parchive</comment>
+ <comment xml:lang="pt_BR">Pacote Parchive</comment>
+ <comment xml:lang="ro">Arhivă Parchive</comment>
+ <comment xml:lang="ru">Архив Parchive</comment>
+ <comment xml:lang="sk">Archív Parchive</comment>
+ <comment xml:lang="sl">Datoteka arhiva Parchive</comment>
+ <comment xml:lang="sq">Arkiv Parchive</comment>
+ <comment xml:lang="sr">архива Пархива</comment>
+ <comment xml:lang="sv">Parchive-arkiv</comment>
+ <comment xml:lang="tr">Parchive arşivi</comment>
+ <comment xml:lang="uk">архів Parchive</comment>
+ <comment xml:lang="vi">Kho nén Parchive</comment>
+ <comment xml:lang="zh_CN">Parchive 归档文件</comment>
+ <comment xml:lang="zh_TW">Parchive 封存檔</comment>
+ <acronym>Parchive</acronym>
+ <expanded-acronym>Parity Volume Set Archive</expanded-acronym>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="PAR2" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.PAR2"/>
+ <glob pattern="*.par2"/>
+ </mime-type>
+ <mime-type type="application/x-pef-executable">
+ <comment>PEF executable</comment>
+ <comment xml:lang="ar">PEF تنفيذي</comment>
+ <comment xml:lang="be@latin">Vykonvalny fajł PEF</comment>
+ <comment xml:lang="bg">Изпълним файл — PEF</comment>
+ <comment xml:lang="ca">executable PEF</comment>
+ <comment xml:lang="cs">spustitelný soubor PEF</comment>
+ <comment xml:lang="da">PEF-kørbar</comment>
+ <comment xml:lang="de">PEF-Programm</comment>
+ <comment xml:lang="el">Εκτελέσιμο PEF</comment>
+ <comment xml:lang="en_GB">PEF executable</comment>
+ <comment xml:lang="eo">PEF-plenumebla</comment>
+ <comment xml:lang="es">ejecutable PEF</comment>
+ <comment xml:lang="eu">PEF exekutagarria</comment>
+ <comment xml:lang="fi">PEF-ohjelma</comment>
+ <comment xml:lang="fo">PEF inningarfør</comment>
+ <comment xml:lang="fr">exécutable PEF</comment>
+ <comment xml:lang="ga">comhad inrite PEF</comment>
+ <comment xml:lang="gl">Executábel PEF</comment>
+ <comment xml:lang="he">קובץ הרצה PEF</comment>
+ <comment xml:lang="hr">PEF izvršna datoteka</comment>
+ <comment xml:lang="hu">PEF futtatható</comment>
+ <comment xml:lang="ia">Executabile PEF</comment>
+ <comment xml:lang="id">PEF dapat dieksekusi</comment>
+ <comment xml:lang="it">Eseguibile PEF</comment>
+ <comment xml:lang="ja">PEF 実行ファイル</comment>
+ <comment xml:lang="kk">PEF орындалатын файлы</comment>
+ <comment xml:lang="ko">PEF 실행 파일</comment>
+ <comment xml:lang="lt">PEF vykdomasis failas</comment>
+ <comment xml:lang="lv">PEF izpildāmais</comment>
+ <comment xml:lang="ms">Bolehlaksana PEF</comment>
+ <comment xml:lang="nb">PEF-kjørbar</comment>
+ <comment xml:lang="nl">PEF-uitvoerbaar bestand</comment>
+ <comment xml:lang="nn">Køyrbar PEF-fil</comment>
+ <comment xml:lang="oc">executable PEF</comment>
+ <comment xml:lang="pl">Program PEF</comment>
+ <comment xml:lang="pt">executável PEF</comment>
+ <comment xml:lang="pt_BR">Executável PEF</comment>
+ <comment xml:lang="ro">Executabil PEF</comment>
+ <comment xml:lang="ru">Исполняемый файл PEF</comment>
+ <comment xml:lang="sk">Spustiteľný súbor PEF</comment>
+ <comment xml:lang="sl">Izvedljiva datoteka PEF</comment>
+ <comment xml:lang="sq">E ekzekutueshme PEF</comment>
+ <comment xml:lang="sr">ПЕФ извршна</comment>
+ <comment xml:lang="sv">Körbar PEF-fil</comment>
+ <comment xml:lang="tr">PEF çalıştırılabilir</comment>
+ <comment xml:lang="uk">виконуваний файл PEF</comment>
+ <comment xml:lang="vi">Tập tin thực hiện được PEF</comment>
+ <comment xml:lang="zh_CN">PEF 可执行文件</comment>
+ <comment xml:lang="zh_TW">PEF 可執行檔</comment>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="Joy!" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-perl">
+ <comment>Perl script</comment>
+ <comment xml:lang="ar">سكربت بيرل</comment>
+ <comment xml:lang="be@latin">Skrypt Perl</comment>
+ <comment xml:lang="bg">Скрипт — Perl</comment>
+ <comment xml:lang="ca">script Perl</comment>
+ <comment xml:lang="cs">skript v Perlu</comment>
+ <comment xml:lang="cy">Sgript Perl</comment>
+ <comment xml:lang="da">Perlprogram</comment>
+ <comment xml:lang="de">Perl-Skript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών Perl</comment>
+ <comment xml:lang="en_GB">Perl script</comment>
+ <comment xml:lang="eo">Perl-skripto</comment>
+ <comment xml:lang="es">secuencia de órdenes en Perl</comment>
+ <comment xml:lang="eu">Perl script-a</comment>
+ <comment xml:lang="fi">Perl-komentotiedosto</comment>
+ <comment xml:lang="fo">Perl boðrøð</comment>
+ <comment xml:lang="fr">script Perl</comment>
+ <comment xml:lang="ga">script Perl</comment>
+ <comment xml:lang="gl">Script de Perl</comment>
+ <comment xml:lang="he">תסריט מעטפת של Perl</comment>
+ <comment xml:lang="hr">Perl skripta</comment>
+ <comment xml:lang="hu">Perl-parancsfájl</comment>
+ <comment xml:lang="ia">Script Perl</comment>
+ <comment xml:lang="id">Skrip Perl</comment>
+ <comment xml:lang="it">Script Perl</comment>
+ <comment xml:lang="ja">Perl スクリプト</comment>
+ <comment xml:lang="kk">Perl сценарийі</comment>
+ <comment xml:lang="ko">펄 스크립트</comment>
+ <comment xml:lang="lt">Perl scenarijus</comment>
+ <comment xml:lang="lv">Perl skripts</comment>
+ <comment xml:lang="ms">Skrip Perl</comment>
+ <comment xml:lang="nb">Perl skript</comment>
+ <comment xml:lang="nl">Perl-script</comment>
+ <comment xml:lang="nn">Perl-skript</comment>
+ <comment xml:lang="oc">escript Perl</comment>
+ <comment xml:lang="pl">Skrypt Perl</comment>
+ <comment xml:lang="pt">script Perl</comment>
+ <comment xml:lang="pt_BR">Script Perl</comment>
+ <comment xml:lang="ro">Script Perl</comment>
+ <comment xml:lang="ru">Сценарий Perl</comment>
+ <comment xml:lang="sk">Skript jazyka Perl</comment>
+ <comment xml:lang="sl">Skriptna datoteka Perl</comment>
+ <comment xml:lang="sq">Script Perl</comment>
+ <comment xml:lang="sr">Перл скрипта</comment>
+ <comment xml:lang="sv">Perlskript</comment>
+ <comment xml:lang="tr">Perl betiği</comment>
+ <comment xml:lang="uk">скрипт на Perl</comment>
+ <comment xml:lang="vi">Văn lệnh Perl</comment>
+ <comment xml:lang="zh_CN">Perl 脚本</comment>
+ <comment xml:lang="zh_TW">Perl 指令稿</comment>
+ <sub-class-of type="application/x-executable"/>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-script"/>
+ <alias type="text/x-perl"/>
+ <magic priority="50">
+ <match value='eval \"exec /usr/local/bin/perl' type="string" offset="0"/>
+ <match value="/bin/perl" type="string" offset="2:16"/>
+ <match value="/bin/env perl" type="string" offset="2:16"/>
+ <match value="use strict" type="string" offset="0:256"/>
+ <match value="use warnings" type="string" offset="0:256"/>
+ <match value="use diagnostics" type="string" offset="0:256"/>
+ <match value="use Test::" type="string" offset="0:256"/>
+ <match value="BEGIN {" type="string" offset="0:256"/>
+ </magic>
+ <magic priority="40">
+ <match value="\n=pod" type="string" offset="0:256"/>
+ <match value="\n=head1 NAME" type="string" offset="0:256"/>
+ <match value="\n=head1 DESCRIPTION" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.pl"/>
+ <glob pattern="*.PL"/>
+ <glob pattern="*.pm"/>
+ <glob pattern="*.al"/>
+ <glob pattern="*.perl"/>
+ <glob pattern="*.pod"/>
+ <glob weight="10" pattern="*.t"/>
+ </mime-type>
+ <mime-type type="application/x-php">
+ <comment>PHP script</comment>
+ <comment xml:lang="ar">سكربت PHP</comment>
+ <comment xml:lang="az">PHP skripti</comment>
+ <comment xml:lang="be@latin">Skrypt PHP</comment>
+ <comment xml:lang="bg">Скрипт — PHP</comment>
+ <comment xml:lang="ca">script PHP</comment>
+ <comment xml:lang="cs">skript PHP</comment>
+ <comment xml:lang="cy">Sgript PHP</comment>
+ <comment xml:lang="da">PHP-program</comment>
+ <comment xml:lang="de">PHP-Skript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών PHP</comment>
+ <comment xml:lang="en_GB">PHP script</comment>
+ <comment xml:lang="eo">PHP-skripto</comment>
+ <comment xml:lang="es">secuencia de órdenes en PHP</comment>
+ <comment xml:lang="eu">PHP script-a</comment>
+ <comment xml:lang="fi">PHP-komentotiedosto</comment>
+ <comment xml:lang="fo">PHP boðrøð</comment>
+ <comment xml:lang="fr">script PHP</comment>
+ <comment xml:lang="ga">script PHP</comment>
+ <comment xml:lang="gl">Script de PHP</comment>
+ <comment xml:lang="he">תסריט מעטפת של PHP</comment>
+ <comment xml:lang="hr">PHP skripta</comment>
+ <comment xml:lang="hu">PHP-parancsfájl</comment>
+ <comment xml:lang="ia">Script PHP</comment>
+ <comment xml:lang="id">Skrip PHP</comment>
+ <comment xml:lang="it">Script PHP</comment>
+ <comment xml:lang="ja">PHP スクリプト</comment>
+ <comment xml:lang="kk">PHP сценарийі</comment>
+ <comment xml:lang="ko">PHP 스크립트</comment>
+ <comment xml:lang="lt">PHP scenarijus</comment>
+ <comment xml:lang="lv">PHP skripts</comment>
+ <comment xml:lang="ms">Skrip PHP</comment>
+ <comment xml:lang="nb">PHP-skript</comment>
+ <comment xml:lang="nl">PHP-script</comment>
+ <comment xml:lang="nn">PHP-skript</comment>
+ <comment xml:lang="oc">escript PHP</comment>
+ <comment xml:lang="pl">Skrypt PHP</comment>
+ <comment xml:lang="pt">script PHP</comment>
+ <comment xml:lang="pt_BR">Script PHP</comment>
+ <comment xml:lang="ro">Script PHP</comment>
+ <comment xml:lang="ru">Сценарий PHP</comment>
+ <comment xml:lang="sk">Skript PHP</comment>
+ <comment xml:lang="sl">Skriptna datoteka PHP</comment>
+ <comment xml:lang="sq">Script PHP</comment>
+ <comment xml:lang="sr">ПХП скрипта</comment>
+ <comment xml:lang="sv">PHP-skript</comment>
+ <comment xml:lang="tr">PHP betiği</comment>
+ <comment xml:lang="uk">скрипт PHP</comment>
+ <comment xml:lang="vi">Văn lệnh PHP</comment>
+ <comment xml:lang="zh_CN">PHP 脚本</comment>
+ <comment xml:lang="zh_TW">PHP 指令稿</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-script"/>
+ <magic priority="80">
+ <match value="&lt;?php" type="string" offset="0:64"/>
+ </magic>
+ <glob pattern="*.php"/>
+ <glob pattern="*.php3"/>
+ <glob pattern="*.php4"/>
+ <glob pattern="*.php5"/>
+ <glob pattern="*.phps"/>
+ </mime-type>
+ <mime-type type="application/x-pkcs7-certificates">
+ <comment>PKCS#7 certificate bundle</comment>
+ <comment xml:lang="ar">رزمة الشهادة PKCS#7</comment>
+ <comment xml:lang="bg">Сбор със сертификати — PKCS#7</comment>
+ <comment xml:lang="ca">conjunt de certificats PKCS#7</comment>
+ <comment xml:lang="cs">svazek certifikátů PKCS#7</comment>
+ <comment xml:lang="da">PKCS#7-certifikatbundt</comment>
+ <comment xml:lang="de">PKCS#7-Zertifikatspaket</comment>
+ <comment xml:lang="el">Πακέτο ψηφιακών πιστοποιητικών PKCS#7</comment>
+ <comment xml:lang="en_GB">PKCS#7 certificate bundle</comment>
+ <comment xml:lang="es">lote de certificados PCKS#7</comment>
+ <comment xml:lang="eu">PKCS#7 zertifikazio sorta</comment>
+ <comment xml:lang="fi">PKCS#7-varmennenippu</comment>
+ <comment xml:lang="fo">PKCS#7 váttanar bundi</comment>
+ <comment xml:lang="fr">lot de certificats PKCS#7</comment>
+ <comment xml:lang="ga">burla teastas PKCS#7</comment>
+ <comment xml:lang="gl">paquete de certificado PKCS#7</comment>
+ <comment xml:lang="he">בקשה מוסמכת PKCS#7</comment>
+ <comment xml:lang="hr">PKCS#7 paket vjerodajnica</comment>
+ <comment xml:lang="hu">PKCS#7-tanúsítványcsomag</comment>
+ <comment xml:lang="ia">Pacchetto de certificatos PKCS#7</comment>
+ <comment xml:lang="id">Bundel sertifikat PKCS#7</comment>
+ <comment xml:lang="it">Bundle certificato PKCS#7</comment>
+ <comment xml:lang="ja">PKCS#7 証明書</comment>
+ <comment xml:lang="kk">PKCS#7 сертификаттар дестесі</comment>
+ <comment xml:lang="ko">PKCS#7 인증서 묶음</comment>
+ <comment xml:lang="lt">PKCS#7 liudijimų ryšulys</comment>
+ <comment xml:lang="lv">PKCS#7 sertifikātu saišķis</comment>
+ <comment xml:lang="nl">PKCS#7-certificaatbundel</comment>
+ <comment xml:lang="oc">lòt de certificats PKCS#7</comment>
+ <comment xml:lang="pl">Pakiet certyfikatu PKCS#7</comment>
+ <comment xml:lang="pt">pacote de certificação PKCS#7</comment>
+ <comment xml:lang="pt_BR">Pacote de certificados PKCS#7</comment>
+ <comment xml:lang="ro">Pachet certificat PKCS#7</comment>
+ <comment xml:lang="ru">Пакет сертификата PKCS#7</comment>
+ <comment xml:lang="sk">Zväzok certifikátov PKCS#7</comment>
+ <comment xml:lang="sl">Datoteka potrdila PKCS#7</comment>
+ <comment xml:lang="sr">ПКЦС#7 пакет уверења</comment>
+ <comment xml:lang="sv">PKCS#7-certifikatsamling</comment>
+ <comment xml:lang="tr">PKCS#7 sertifika paketi</comment>
+ <comment xml:lang="uk">комплект сертифікатів PKCS#7</comment>
+ <comment xml:lang="vi">Bó chứng nhận PKCS#7</comment>
+ <comment xml:lang="zh_CN">PKCS#7 证书束</comment>
+ <comment xml:lang="zh_TW">PKCS#7 憑證綁包</comment>
+ <acronym>PKCS</acronym>
+ <expanded-acronym>Public-Key Cryptography Standards</expanded-acronym>
+ <glob pattern="*.p7b"/>
+ <glob pattern="*.spc"/>
+ </mime-type>
+ <mime-type type="application/pkcs12">
+ <comment>PKCS#12 certificate bundle</comment>
+ <comment xml:lang="ar">رزمة الشهادة PKCS#12</comment>
+ <comment xml:lang="be@latin">Viazka sertyfikataŭ PKCS#12</comment>
+ <comment xml:lang="bg">Сбор със сертификати — PKCS#12</comment>
+ <comment xml:lang="ca">conjunt de certificats PKCS#12</comment>
+ <comment xml:lang="cs">svazek certifikátů PKCS#12</comment>
+ <comment xml:lang="da">PKCS#12-certifikatbundt</comment>
+ <comment xml:lang="de">PKCS#12-Zertifikatspaket</comment>
+ <comment xml:lang="el">Πακέτο ψηφιακών πιστοποιητικών PKCS#12</comment>
+ <comment xml:lang="en_GB">PKCS#12 certificate bundle</comment>
+ <comment xml:lang="eo">ligaĵo de PKCS#12-atestiloj</comment>
+ <comment xml:lang="es">lote de certificados PCKS#12</comment>
+ <comment xml:lang="eu">PKCS#12 zertifikazio sorta</comment>
+ <comment xml:lang="fi">PKCS#12-varmennenippu</comment>
+ <comment xml:lang="fo">PKCS#12 váttanar bundi</comment>
+ <comment xml:lang="fr">lot de certificats PKCS#12</comment>
+ <comment xml:lang="ga">burla teastas PKCS#12</comment>
+ <comment xml:lang="gl">paquete de certificado PKCS#12</comment>
+ <comment xml:lang="he">בקשה מוסמכת PKCS#12</comment>
+ <comment xml:lang="hr">PKCS#12 paket vjerodajnica</comment>
+ <comment xml:lang="hu">PKCS#12-tanúsítványcsomag</comment>
+ <comment xml:lang="ia">Pacchetto de certificatos PKCS#12</comment>
+ <comment xml:lang="id">Bundel sertifikat PKCS#12</comment>
+ <comment xml:lang="it">Bundle certificato PKCS#12</comment>
+ <comment xml:lang="ja">PKCS#12 証明書</comment>
+ <comment xml:lang="kk">PKCS#12 сертификаттар дестесі</comment>
+ <comment xml:lang="ko">PKCS#12 인증서 묶음</comment>
+ <comment xml:lang="lt">PKCS#12 liudijimų ryšulys</comment>
+ <comment xml:lang="lv">PKCS#12 sertifikātu saišķis</comment>
+ <comment xml:lang="ms">Sijil PKCS#12</comment>
+ <comment xml:lang="nb">PKCS#12 sertifikathaug</comment>
+ <comment xml:lang="nl">PKCS#12-certificaatbundel</comment>
+ <comment xml:lang="nn">PKCS#12-sertifikatbunt</comment>
+ <comment xml:lang="oc">lòt de certificats PKCS#12</comment>
+ <comment xml:lang="pl">Pakiet certyfikatu PKCS#12</comment>
+ <comment xml:lang="pt">pacote de certificação PKCS#12</comment>
+ <comment xml:lang="pt_BR">Pacote de certificados PKCS#12</comment>
+ <comment xml:lang="ro">Certificat împachetat PKCS#12</comment>
+ <comment xml:lang="ru">Пакет сертификата PKCS#12</comment>
+ <comment xml:lang="sk">Zväzok certifikátov PKCS#12</comment>
+ <comment xml:lang="sl">Datoteka potrdila PKCS#12</comment>
+ <comment xml:lang="sq">Bundle çertifikate PKCS#12</comment>
+ <comment xml:lang="sr">ПКЦС#12 пакет уверења</comment>
+ <comment xml:lang="sv">PKCS#12-certifikatsamling</comment>
+ <comment xml:lang="tr">PKCS#12 sertifika paketi</comment>
+ <comment xml:lang="uk">комплект сертифікатів PKCS#12</comment>
+ <comment xml:lang="vi">Bó chứng nhận PKCS#12</comment>
+ <comment xml:lang="zh_CN">PKCS#12 证书束</comment>
+ <comment xml:lang="zh_TW">PKCS#12 憑證檔綁包</comment>
+ <acronym>PKCS</acronym>
+ <expanded-acronym>Public-Key Cryptography Standards</expanded-acronym>
+ <glob pattern="*.p12"/>
+ <glob pattern="*.pfx"/>
+ <alias type="application/x-pkcs12"/>
+ </mime-type>
+ <mime-type type="application/x-planperfect">
+ <comment>PlanPerfect spreadsheet</comment>
+ <comment xml:lang="ar">جدول PlanPerfect</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš PlanPerfect</comment>
+ <comment xml:lang="bg">Таблица — PlanPerfect</comment>
+ <comment xml:lang="ca">full de càlcul de PlanPerfect</comment>
+ <comment xml:lang="cs">sešit PlanPerfect</comment>
+ <comment xml:lang="da">PlanPerfect-regneark</comment>
+ <comment xml:lang="de">PlanPerfect-Tabelle</comment>
+ <comment xml:lang="el">Φύλλο εργασίας PlanPerfect</comment>
+ <comment xml:lang="en_GB">PlanPerfect spreadsheet</comment>
+ <comment xml:lang="es">hoja de cálculo de PlanPerfect</comment>
+ <comment xml:lang="eu">PlanPerfect kalkulu-orria</comment>
+ <comment xml:lang="fi">PlanPerfect-taulukko</comment>
+ <comment xml:lang="fo">PlanPerfect rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul PlanPerfect</comment>
+ <comment xml:lang="ga">scarbhileog PlanPerfect</comment>
+ <comment xml:lang="gl">folla de cálculo de PlanPerfect</comment>
+ <comment xml:lang="he">גליון נתונים של PlanPerfect</comment>
+ <comment xml:lang="hr">PlanPerfect proračunska tablica</comment>
+ <comment xml:lang="hu">PlanPerfect táblázat</comment>
+ <comment xml:lang="ia">Folio de calculo PlanPerfect</comment>
+ <comment xml:lang="id">Lembar sebar PlanPerfect</comment>
+ <comment xml:lang="it">Foglio di calcolo PlanPerfect</comment>
+ <comment xml:lang="ja">PlanPerfect スプレッドシート</comment>
+ <comment xml:lang="kk">PlanPerfect электрондық кестесі</comment>
+ <comment xml:lang="ko">PlanPerfect 스프레드시트</comment>
+ <comment xml:lang="lt">PlanPerfect skaičialentė</comment>
+ <comment xml:lang="lv">PlanPerfect izklājlapa</comment>
+ <comment xml:lang="nb">PlanPerfect-regneark</comment>
+ <comment xml:lang="nl">PlanPerfect-rekenblad</comment>
+ <comment xml:lang="nn">PlanPerfect-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul PlanPerfect</comment>
+ <comment xml:lang="pl">Arkusz PlanPerfect</comment>
+ <comment xml:lang="pt">folha de cálculo PlanPerfect</comment>
+ <comment xml:lang="pt_BR">Planilha do PlanPerfect</comment>
+ <comment xml:lang="ro">Foaie de calcul PlanPerfect</comment>
+ <comment xml:lang="ru">Электронная таблица PlanPerfect</comment>
+ <comment xml:lang="sk">Zošit PlanPerfect</comment>
+ <comment xml:lang="sl">Preglednica PlanPerfect</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh PlanPerfect</comment>
+ <comment xml:lang="sr">табела План Перфекта</comment>
+ <comment xml:lang="sv">PlanPerfect-kalkylblad</comment>
+ <comment xml:lang="tr">PlanPerfect çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця PlanPerfect</comment>
+ <comment xml:lang="vi">Bảng tính PlanPerfect</comment>
+ <comment xml:lang="zh_CN">PlanPerfect 电子表格</comment>
+ <comment xml:lang="zh_TW">PlanPerfect 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.pln"/>
+ </mime-type>
+ <mime-type type="application/x-pocket-word">
+ <comment>Pocket Word document</comment>
+ <comment xml:lang="ar">مستند Pocket Word</comment>
+ <comment xml:lang="ast">Documentu de PocketWord</comment>
+ <comment xml:lang="bg">Документ — Pocket Word</comment>
+ <comment xml:lang="ca">document de Pocket Word</comment>
+ <comment xml:lang="cs">dokument Pocket Word</comment>
+ <comment xml:lang="da">Pocket Word-dokument</comment>
+ <comment xml:lang="de">Pocket-Word-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Pocket Word</comment>
+ <comment xml:lang="en_GB">Pocket Word document</comment>
+ <comment xml:lang="es">documento de Pocket Word</comment>
+ <comment xml:lang="eu">Pocket Word dokumentua</comment>
+ <comment xml:lang="fi">Pocket Word -asiakirja</comment>
+ <comment xml:lang="fo">Pocket Word skjal</comment>
+ <comment xml:lang="fr">document Pocket Word</comment>
+ <comment xml:lang="ga">cáipéis Pocket Word</comment>
+ <comment xml:lang="gl">documento de Pocket Word</comment>
+ <comment xml:lang="he">מסמך של Pocket Word</comment>
+ <comment xml:lang="hr">Pocket Word dokument</comment>
+ <comment xml:lang="hu">Pocket Word dokumentum</comment>
+ <comment xml:lang="ia">Documento Pocket Word</comment>
+ <comment xml:lang="id">Dokumen Pocket Word</comment>
+ <comment xml:lang="it">Documento Pocket Word</comment>
+ <comment xml:lang="ja">Pocket Word ドキュメント</comment>
+ <comment xml:lang="kk">Pocket Word құжаты</comment>
+ <comment xml:lang="ko">Pocket Word 문서</comment>
+ <comment xml:lang="lt">Pocket Word dokumentas</comment>
+ <comment xml:lang="lv">Pocket Word dokuments</comment>
+ <comment xml:lang="nl">Pocket Word-document</comment>
+ <comment xml:lang="oc">document Pocket Word</comment>
+ <comment xml:lang="pl">Dokument Pocket Word</comment>
+ <comment xml:lang="pt">documento Pocket Word</comment>
+ <comment xml:lang="pt_BR">Documento do Pocket Word</comment>
+ <comment xml:lang="ro">Document Pocket Word</comment>
+ <comment xml:lang="ru">Документ Pocket Word</comment>
+ <comment xml:lang="sk">Dokument Pocket Word</comment>
+ <comment xml:lang="sl">Dokument Pocket Word</comment>
+ <comment xml:lang="sr">документ Покет Ворда</comment>
+ <comment xml:lang="sv">Pocket Word-dokument</comment>
+ <comment xml:lang="tr">Pocket Word belgesi</comment>
+ <comment xml:lang="uk">документ Pocket Word</comment>
+ <comment xml:lang="vi">Tài liệu Pocket Word</comment>
+ <comment xml:lang="zh_CN">Pocket Word 文档</comment>
+ <comment xml:lang="zh_TW">Pocket Word 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="{\\pwi" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.psw"/>
+ </mime-type>
+ <mime-type type="application/x-profile">
+ <comment>profiler results</comment>
+ <comment xml:lang="ar">نتائج المحلل</comment>
+ <comment xml:lang="az">profiler nəticələri</comment>
+ <comment xml:lang="be@latin">vyniki profilera</comment>
+ <comment xml:lang="bg">Резултати от анализатора</comment>
+ <comment xml:lang="ca">resultats de profiler</comment>
+ <comment xml:lang="cs">výsledky profileru</comment>
+ <comment xml:lang="cy">canlyniadau proffeilio</comment>
+ <comment xml:lang="da">profileringsresultater</comment>
+ <comment xml:lang="de">Profiler-Ergebnisse</comment>
+ <comment xml:lang="el">Αποτελέσματα μετρήσεων για την εκτέλεση προγράμματος</comment>
+ <comment xml:lang="en_GB">profiler results</comment>
+ <comment xml:lang="eo">resultoj de profililo</comment>
+ <comment xml:lang="es">resultados del perfilador</comment>
+ <comment xml:lang="eu">profiler-aren emaitzak</comment>
+ <comment xml:lang="fi">profilointitulokset</comment>
+ <comment xml:lang="fr">résultats de profileur</comment>
+ <comment xml:lang="ga">torthaí próifíleora</comment>
+ <comment xml:lang="gl">resultados do perfilador</comment>
+ <comment xml:lang="he">תוצאות מאבחן</comment>
+ <comment xml:lang="hr">Rezultati profila</comment>
+ <comment xml:lang="hu">profilírozó-eredmények</comment>
+ <comment xml:lang="ia">Resultatos de profilator</comment>
+ <comment xml:lang="id">hasil profiler</comment>
+ <comment xml:lang="it">Risultati profiler</comment>
+ <comment xml:lang="ja">プロファイラー結果</comment>
+ <comment xml:lang="kk">прифильдеу нәтижелері</comment>
+ <comment xml:lang="ko">프로파일러 결과</comment>
+ <comment xml:lang="lt">profiliklio rezultatai</comment>
+ <comment xml:lang="lv">profilētāja rezultāti</comment>
+ <comment xml:lang="ms">Hasil pemprofil</comment>
+ <comment xml:lang="nb">profileingsresultat</comment>
+ <comment xml:lang="nl">profiler-resultaten</comment>
+ <comment xml:lang="nn">profileringsresultat</comment>
+ <comment xml:lang="oc">resultats de perfilador</comment>
+ <comment xml:lang="pl">Wyniki profilowania</comment>
+ <comment xml:lang="pt">resultados de análise de perfil</comment>
+ <comment xml:lang="pt_BR">Resultados do profiler</comment>
+ <comment xml:lang="ro">rezultate profiler</comment>
+ <comment xml:lang="ru">Результаты профилирования</comment>
+ <comment xml:lang="sk">Výsledky profilera</comment>
+ <comment xml:lang="sl">rezultati profilirnika</comment>
+ <comment xml:lang="sq">Rezultate të profiluesit</comment>
+ <comment xml:lang="sr">резултати профилатора</comment>
+ <comment xml:lang="sv">profilerarresultat</comment>
+ <comment xml:lang="tr">profil sonuçları</comment>
+ <comment xml:lang="uk">результати профілювання</comment>
+ <comment xml:lang="vi">kết quả nét hiện trạng</comment>
+ <comment xml:lang="zh_CN">探查器结果</comment>
+ <comment xml:lang="zh_TW">硬體資訊產生器成果</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="gmon.out"/>
+ </mime-type>
+ <mime-type type="application/x-pw">
+ <comment>Pathetic Writer document</comment>
+ <comment xml:lang="ar">مستند Pathetic Writer</comment>
+ <comment xml:lang="ast">Documentu de Pathetic Writer</comment>
+ <comment xml:lang="be@latin">Dakument Pathetic Writer</comment>
+ <comment xml:lang="bg">Документ — Pathetic Writer</comment>
+ <comment xml:lang="ca">document de Pathetic Writer</comment>
+ <comment xml:lang="cs">dokument Pathetic Writer</comment>
+ <comment xml:lang="da">Pathetic Writer-dokument</comment>
+ <comment xml:lang="de">Pathetic-Writer-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Pathetic Writer</comment>
+ <comment xml:lang="en_GB">Pathetic Writer document</comment>
+ <comment xml:lang="eo">dokumento de Pathetic Writer</comment>
+ <comment xml:lang="es">documento de Pathetic Writer</comment>
+ <comment xml:lang="eu">Pathetic Writer dokumentua</comment>
+ <comment xml:lang="fi">Pathetic Writer -asiakirja</comment>
+ <comment xml:lang="fo">Pathetic Writer skjal</comment>
+ <comment xml:lang="fr">document Pathetic Writer</comment>
+ <comment xml:lang="ga">cáipéis Pathetic Writer</comment>
+ <comment xml:lang="gl">documento de Pathetic Writer</comment>
+ <comment xml:lang="he">מסמך של Pathetic Writer</comment>
+ <comment xml:lang="hr">Pathetic Writer dokument</comment>
+ <comment xml:lang="hu">Pathetic Writer-dokumentum</comment>
+ <comment xml:lang="ia">Documento Pathetic Writer</comment>
+ <comment xml:lang="id">Dokumen Pathetic Writer</comment>
+ <comment xml:lang="it">Documento Pathetic Writer</comment>
+ <comment xml:lang="ja">Pathetic Writer ドキュメント</comment>
+ <comment xml:lang="kk">Pathetic Writer құжаты</comment>
+ <comment xml:lang="ko">Pathetic Writer 문서</comment>
+ <comment xml:lang="lt">Pathetic Writer dokumentas</comment>
+ <comment xml:lang="lv">Pathetic Writer dokuments</comment>
+ <comment xml:lang="ms">Dokumen Pathetic Writer</comment>
+ <comment xml:lang="nb">Pathetic Writer-dokument</comment>
+ <comment xml:lang="nl">Pathetic Writer-document</comment>
+ <comment xml:lang="nn">Pathetic Writer-dokument</comment>
+ <comment xml:lang="oc">document Pathetic Writer</comment>
+ <comment xml:lang="pl">Dokument Pathetic Writer</comment>
+ <comment xml:lang="pt">documento do Pathetic Writer</comment>
+ <comment xml:lang="pt_BR">Documento do Pathetic Writer</comment>
+ <comment xml:lang="ro">Document Pathetic Writer</comment>
+ <comment xml:lang="ru">Документ Pathetic Writer</comment>
+ <comment xml:lang="sk">Dokument Pathetic Writer</comment>
+ <comment xml:lang="sl">Dokument Pathetic Writer</comment>
+ <comment xml:lang="sq">Dokument Pathetic Writer</comment>
+ <comment xml:lang="sr">документ Патетичног Писца</comment>
+ <comment xml:lang="sv">Pathetic Writer-dokument</comment>
+ <comment xml:lang="tr">Pathetic Writer belgesi</comment>
+ <comment xml:lang="uk">документ Pathetic Writer</comment>
+ <comment xml:lang="vi">Tài liệu Pathetic Writer</comment>
+ <comment xml:lang="zh_CN">Pathetic Writer 文档</comment>
+ <comment xml:lang="zh_TW">Pathetic Writer 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.pw"/>
+ </mime-type>
+ <mime-type type="application/x-python-bytecode">
+ <comment>Python bytecode</comment>
+ <comment xml:lang="ar">Python bytecode</comment>
+ <comment xml:lang="az">Python bayt kodu</comment>
+ <comment xml:lang="be@latin">Bajtavy kod Python</comment>
+ <comment xml:lang="bg">Байт код — Python</comment>
+ <comment xml:lang="ca">bytecode de Python</comment>
+ <comment xml:lang="cs">bajtový kód Python</comment>
+ <comment xml:lang="cy">Côd beit Python</comment>
+ <comment xml:lang="da">Pythonbytekode</comment>
+ <comment xml:lang="de">Python-Bytecode</comment>
+ <comment xml:lang="el">Συμβολοκώδικας Python</comment>
+ <comment xml:lang="en_GB">Python bytecode</comment>
+ <comment xml:lang="eo">Python-bajtkodo</comment>
+ <comment xml:lang="es">bytecode de Python</comment>
+ <comment xml:lang="eu">Python byte-kodea</comment>
+ <comment xml:lang="fi">Python-tavukoodi</comment>
+ <comment xml:lang="fo">Python býtkota</comment>
+ <comment xml:lang="fr">bytecode Python</comment>
+ <comment xml:lang="ga">beartchód Python</comment>
+ <comment xml:lang="gl">bytecode de Python</comment>
+ <comment xml:lang="he">Bytecode של Python</comment>
+ <comment xml:lang="hr">Python bajt kôd</comment>
+ <comment xml:lang="hu">Python-bájtkód</comment>
+ <comment xml:lang="ia">Codice intermediari Python</comment>
+ <comment xml:lang="id">Kode bita Python</comment>
+ <comment xml:lang="it">Bytecode Python</comment>
+ <comment xml:lang="ja">Python バイトコード</comment>
+ <comment xml:lang="kk">Python байткоды</comment>
+ <comment xml:lang="ko">파이썬 바이트코드</comment>
+ <comment xml:lang="lt">Python baitinis kodas</comment>
+ <comment xml:lang="lv">Python bitkods</comment>
+ <comment xml:lang="ms">Kodbait Python</comment>
+ <comment xml:lang="nb">Python-bytekode</comment>
+ <comment xml:lang="nl">Python-bytecode</comment>
+ <comment xml:lang="nn">Python-bytekode</comment>
+ <comment xml:lang="oc">bytecode Python</comment>
+ <comment xml:lang="pl">Kod bajtowy Python</comment>
+ <comment xml:lang="pt">código binário Python</comment>
+ <comment xml:lang="pt_BR">Código compilado Python</comment>
+ <comment xml:lang="ro">Bytecode Python</comment>
+ <comment xml:lang="ru">Байт-код Python</comment>
+ <comment xml:lang="sk">Bajtový kód Python</comment>
+ <comment xml:lang="sl">Datoteka bitne kode Python</comment>
+ <comment xml:lang="sq">Bytecode Python</comment>
+ <comment xml:lang="sr">Питонов бајтни ко̂д</comment>
+ <comment xml:lang="sv">Python-bytekod</comment>
+ <comment xml:lang="tr">Python bayt kodu</comment>
+ <comment xml:lang="uk">байт-код Python</comment>
+ <comment xml:lang="vi">Mã byte Python</comment>
+ <comment xml:lang="zh_CN">Python 字节码</comment>
+ <comment xml:lang="zh_TW">Python 位元組碼</comment>
+ <magic priority="50">
+ <match value="0x994e0d0a" type="big32" offset="0"/>
+ </magic>
+ <glob pattern="*.pyc"/>
+ <glob pattern="*.pyo"/>
+ </mime-type>
+ <mime-type type="application/x-qtiplot">
+ <comment>QtiPlot document</comment>
+ <comment xml:lang="ast">Documentu de QtiPlot</comment>
+ <comment xml:lang="ca">document QtiPlot</comment>
+ <comment xml:lang="cs">dokument GtiPlot</comment>
+ <comment xml:lang="da">QtiPlot-dokument</comment>
+ <comment xml:lang="de">QtiPlot-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο QtiPlot</comment>
+ <comment xml:lang="en_GB">QtiPlot document</comment>
+ <comment xml:lang="es">documento de QtiPlot</comment>
+ <comment xml:lang="eu">QtiPlot dokumentua</comment>
+ <comment xml:lang="fi">QtiPlot-asiakirja</comment>
+ <comment xml:lang="fr">document QtiPlot</comment>
+ <comment xml:lang="ga">cáipéis QtiPlot</comment>
+ <comment xml:lang="gl">Documento de QtiPilot</comment>
+ <comment xml:lang="he">מסמך QtiPlot</comment>
+ <comment xml:lang="hr">QtiPlot dokument</comment>
+ <comment xml:lang="hu">QtiPlot dokumentum</comment>
+ <comment xml:lang="ia">Documento QtiPlot</comment>
+ <comment xml:lang="id">Dokumen QtiPlot</comment>
+ <comment xml:lang="it">Documento QtiPlot</comment>
+ <comment xml:lang="ja">QtiPlot ドキュメント</comment>
+ <comment xml:lang="kk">QtiPlot құжаты</comment>
+ <comment xml:lang="ko">QtiPlot 문서</comment>
+ <comment xml:lang="lv">QtiPlot dokuments</comment>
+ <comment xml:lang="oc">document QtiPlot</comment>
+ <comment xml:lang="pl">Dokument QtiPlot</comment>
+ <comment xml:lang="pt">documento QtiPlot</comment>
+ <comment xml:lang="pt_BR">Documento do QtiPlot</comment>
+ <comment xml:lang="ru">Документ QtiPlot</comment>
+ <comment xml:lang="sk">Dokument QtiPlot</comment>
+ <comment xml:lang="sl">Dokument QtiPlot</comment>
+ <comment xml:lang="sr">КутиПлот документ</comment>
+ <comment xml:lang="sv">QtiPlot-dokument</comment>
+ <comment xml:lang="tr">QtiPlot belgesi</comment>
+ <comment xml:lang="uk">документ QtiPlot</comment>
+ <comment xml:lang="zh_CN">QtiPlot 文档</comment>
+ <comment xml:lang="zh_TW">QtiPlot 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="QtiPlot" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.qti"/>
+ <glob pattern="*.qti.gz"/>
+ </mime-type>
+ <mime-type type="application/x-quattropro">
+ <comment>Quattro Pro spreadsheet</comment>
+ <comment xml:lang="ar">جدول Quattro Pro</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš Quattro Pro</comment>
+ <comment xml:lang="bg">Таблица — Quattro Pro</comment>
+ <comment xml:lang="ca">full de càlcul de Quattro Pro</comment>
+ <comment xml:lang="cs">sešit Quattro Pro</comment>
+ <comment xml:lang="da">Quattro Pro-regneark</comment>
+ <comment xml:lang="de">Quattro-Pro-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο Quattro Pro</comment>
+ <comment xml:lang="en_GB">Quattro Pro spreadsheet</comment>
+ <comment xml:lang="eo">sterntabelo de Quattro Pro</comment>
+ <comment xml:lang="es">hoja de cálculo de Quattro Pro</comment>
+ <comment xml:lang="eu">Quattro Pro kalkulu-orria</comment>
+ <comment xml:lang="fi">Quattro Pro -taulukko</comment>
+ <comment xml:lang="fo">Quattro Pro rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul Quattro Pro</comment>
+ <comment xml:lang="ga">scarbhileog Quattro Pro</comment>
+ <comment xml:lang="gl">folla de cálculo Quattro Pro</comment>
+ <comment xml:lang="he">גליון נתונים של Quattro Pro</comment>
+ <comment xml:lang="hr">Quattro Pro proračunska tablica</comment>
+ <comment xml:lang="hu">Quattro Pro-munkafüzet</comment>
+ <comment xml:lang="ia">Folio de calculo Quattro Pro</comment>
+ <comment xml:lang="id">Lembar sebar Quattro Pro</comment>
+ <comment xml:lang="it">Foglio di calcolo Quattro Pro</comment>
+ <comment xml:lang="ja">Quattro Pro スプレッドシート</comment>
+ <comment xml:lang="kk">Quattro Pro электрондық кестесі</comment>
+ <comment xml:lang="ko">Quattro Pro 스프레드시트</comment>
+ <comment xml:lang="lt">Quattro Pro skaičialentė</comment>
+ <comment xml:lang="lv">Quattro Pro izklājlapa</comment>
+ <comment xml:lang="ms">Hamparan Quatro Pro</comment>
+ <comment xml:lang="nb">Quattro Pro-regneark</comment>
+ <comment xml:lang="nl">Quattro Pro-rekenblad</comment>
+ <comment xml:lang="nn">Quattro Pro-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul Quattro Pro</comment>
+ <comment xml:lang="pl">Arkusz Quattro Pro</comment>
+ <comment xml:lang="pt">folha de cálculo Quattro Pro</comment>
+ <comment xml:lang="pt_BR">Planilha do Quattro Pro</comment>
+ <comment xml:lang="ro">Foaie de calcul Quattro Pro</comment>
+ <comment xml:lang="ru">Электронная таблица Quattro Pro</comment>
+ <comment xml:lang="sk">Zošit Quattro Pro</comment>
+ <comment xml:lang="sl">Preglednica Quattro Pro</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh Quattro Pro</comment>
+ <comment xml:lang="sr">Кватро Про табела</comment>
+ <comment xml:lang="sv">Quattro Pro-kalkylblad</comment>
+ <comment xml:lang="tr">Quattro Pro çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця Quattro Pro</comment>
+ <comment xml:lang="vi">Bảng tính Quattro Pro</comment>
+ <comment xml:lang="zh_CN">Quattro Pro 电子表格</comment>
+ <comment xml:lang="zh_TW">Quattro Pro 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.wb1"/>
+ <glob pattern="*.wb2"/>
+ <glob pattern="*.wb3"/>
+ </mime-type>
+ <mime-type type="application/x-quicktime-media-link">
+ <comment>QuickTime metalink playlist</comment>
+ <comment xml:lang="ar">قائمة تشغيل QuickTime metalink</comment>
+ <comment xml:lang="be@latin">śpis metaspasyłak na pieśni QuickTime</comment>
+ <comment xml:lang="bg">Списък за изпълнение — QuickTime</comment>
+ <comment xml:lang="ca">llista de reproducció de metaenllaços QuickTime</comment>
+ <comment xml:lang="cs">seznam k přehrání metalink QuickTime</comment>
+ <comment xml:lang="da">QuickTime metalink-afspilningsliste</comment>
+ <comment xml:lang="de">QuickTime-Metalink-Wiedergabeliste</comment>
+ <comment xml:lang="el">Λίστα αναπαραγωγής metalinks QuickTime</comment>
+ <comment xml:lang="en_GB">QuickTime metalink playlist</comment>
+ <comment xml:lang="es">lista de reproducción de metaenlaces QuickTime</comment>
+ <comment xml:lang="eu">QuickTime meta-esteken erreprodukzio-zerrenda</comment>
+ <comment xml:lang="fi">QuickTime metalink -soittolista</comment>
+ <comment xml:lang="fo">QuickTime metaleinkju avspælingarlisti</comment>
+ <comment xml:lang="fr">liste de lecture metalink QuickTime</comment>
+ <comment xml:lang="ga">seinmliosta meiteanasc QuickTime</comment>
+ <comment xml:lang="gl">lista de reprodución de metaligazóns QuickTime</comment>
+ <comment xml:lang="he">רשימת השמעה מקושרת של QuickTime</comment>
+ <comment xml:lang="hr">QuickTime meta poveznica popisa izvođenja</comment>
+ <comment xml:lang="hu">QuickTime metalink lejátszólista</comment>
+ <comment xml:lang="ia">Lista de selection Metalink QuickTime</comment>
+ <comment xml:lang="id">Senarai berkas taut meta QuickTime</comment>
+ <comment xml:lang="it">Playlist metalink QuickTime</comment>
+ <comment xml:lang="ja">QuickTime メタリンク再生リスト</comment>
+ <comment xml:lang="kk">QuickTime метасілтемелер ойнау тізімі</comment>
+ <comment xml:lang="ko">퀵타임 메타링크 재생 목록</comment>
+ <comment xml:lang="lt">QuickTime metanuorodos grojaraštis</comment>
+ <comment xml:lang="lv">QuickTime metasaites repertuārs</comment>
+ <comment xml:lang="nb">QuickTime metalink-spilleliste</comment>
+ <comment xml:lang="nl">QuickTime metalink-afspeellijst</comment>
+ <comment xml:lang="nn">QuickTime metalink-speleliste</comment>
+ <comment xml:lang="oc">lista de lectura metalink QuickTime</comment>
+ <comment xml:lang="pl">Lista odtwarzania metaodnośników QuickTime</comment>
+ <comment xml:lang="pt">lista de reprodução QuickTime metalink</comment>
+ <comment xml:lang="pt_BR">Lista de reprodução metalink do QuickTime</comment>
+ <comment xml:lang="ro">Listă cu metalegături QuickTime</comment>
+ <comment xml:lang="ru">Список воспроизведения мета-ссылок QuickTime</comment>
+ <comment xml:lang="sk">Zoznam skladieb metalink QuickTime</comment>
+ <comment xml:lang="sl">Seznam predvajanja QuickTime</comment>
+ <comment xml:lang="sq">Listë titujsh metalink QuickTime</comment>
+ <comment xml:lang="sr">списак нумера мета везе Квик Тајма</comment>
+ <comment xml:lang="sv">QuickTime-metalänkspellista</comment>
+ <comment xml:lang="tr">QuickTime metalink çalma listesi</comment>
+ <comment xml:lang="uk">список відтворення QuickTime metalink</comment>
+ <comment xml:lang="vi">Danh mục nhạc siêu liên kết Quicktime</comment>
+ <comment xml:lang="zh_CN">QuickTime Metalink 播放列表</comment>
+ <comment xml:lang="zh_TW">QuickTime metalink 播放清單</comment>
+ <generic-icon name="video-x-generic"/>
+ <sub-class-of type="video/quicktime"/>
+ <alias type="application/x-quicktimeplayer"/>
+ <magic priority="60">
+ <match value="&lt;?xml" type="string" offset="0">
+ <match value="&lt;?quicktime" type="string" offset="0:64"/>
+ </match>
+ <match value="RTSPtext" type="string" offset="0"/>
+ <match value="rtsptext" type="string" offset="0"/>
+ <match value="SMILtext" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.qtl"/>
+ </mime-type>
+ <mime-type type="application/x-qw">
+ <comment>Quicken document</comment>
+ <comment xml:lang="ar">مستند Quicken</comment>
+ <comment xml:lang="ast">Documentu de Quicken</comment>
+ <comment xml:lang="az">Quicken sənədi</comment>
+ <comment xml:lang="be@latin">Dakument Quicken</comment>
+ <comment xml:lang="bg">Документ — Quicken</comment>
+ <comment xml:lang="ca">document Quicken</comment>
+ <comment xml:lang="cs">dokument Quicken</comment>
+ <comment xml:lang="cy">Dogfen Quicken</comment>
+ <comment xml:lang="da">Quickendokument</comment>
+ <comment xml:lang="de">Quicken-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Quicken</comment>
+ <comment xml:lang="en_GB">Quicken document</comment>
+ <comment xml:lang="eo">Quicken-dokumento</comment>
+ <comment xml:lang="es">documento de Quicken</comment>
+ <comment xml:lang="eu">Quicken dokumentua</comment>
+ <comment xml:lang="fi">Quicken-asiakirja</comment>
+ <comment xml:lang="fo">Quicken skjal</comment>
+ <comment xml:lang="fr">document Quicken</comment>
+ <comment xml:lang="ga">cáipéis Quicken</comment>
+ <comment xml:lang="gl">documento de Quicken</comment>
+ <comment xml:lang="he">מסמך של Quicken</comment>
+ <comment xml:lang="hr">Quicken dokument</comment>
+ <comment xml:lang="hu">Quicken-dokumentum</comment>
+ <comment xml:lang="ia">Documento Quicken</comment>
+ <comment xml:lang="id">Dokumen Quicken</comment>
+ <comment xml:lang="it">Documento Quicken</comment>
+ <comment xml:lang="ja">Quicken ドキュメント</comment>
+ <comment xml:lang="kk">Quicken құжаты</comment>
+ <comment xml:lang="ko">Quicken 문서</comment>
+ <comment xml:lang="lt">Quicken dokumentas</comment>
+ <comment xml:lang="lv">Quicken dokuments</comment>
+ <comment xml:lang="ms">Dokumen Quicken</comment>
+ <comment xml:lang="nb">Quicken-dokument</comment>
+ <comment xml:lang="nl">Quicken-document</comment>
+ <comment xml:lang="nn">Quicken-dokument</comment>
+ <comment xml:lang="oc">document Quicken</comment>
+ <comment xml:lang="pl">Dokument Quicken</comment>
+ <comment xml:lang="pt">documento Quicken</comment>
+ <comment xml:lang="pt_BR">Documento do Quicken</comment>
+ <comment xml:lang="ro">Document Quicken</comment>
+ <comment xml:lang="ru">Документ Quicken</comment>
+ <comment xml:lang="sk">Dokument Quicken</comment>
+ <comment xml:lang="sl">Dokument Quicken</comment>
+ <comment xml:lang="sq">Dokument Quicken</comment>
+ <comment xml:lang="sr">Квикен документ</comment>
+ <comment xml:lang="sv">Quicken-dokument</comment>
+ <comment xml:lang="tr">Quicken belgesi</comment>
+ <comment xml:lang="uk">документ Quicken</comment>
+ <comment xml:lang="vi">Tài liệu Quicken</comment>
+ <comment xml:lang="zh_CN">Quicken 文档</comment>
+ <comment xml:lang="zh_TW">Quicken 文件</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.qif"/>
+ </mime-type>
+ <mime-type type="application/vnd.rar">
+ <comment>RAR archive</comment>
+ <comment xml:lang="ar">أرشيف RAR</comment>
+ <comment xml:lang="be@latin">Archiŭ RAR</comment>
+ <comment xml:lang="bg">Архив — RAR</comment>
+ <comment xml:lang="ca">arxiu RAR</comment>
+ <comment xml:lang="cs">archiv RAR</comment>
+ <comment xml:lang="cy">Archif RAR</comment>
+ <comment xml:lang="da">RAR-arkiv</comment>
+ <comment xml:lang="de">RAR-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο RAR</comment>
+ <comment xml:lang="en_GB">RAR archive</comment>
+ <comment xml:lang="eo">RAR-arkivo</comment>
+ <comment xml:lang="es">archivador RAR</comment>
+ <comment xml:lang="eu">RAR artxiboa</comment>
+ <comment xml:lang="fi">RAR-arkisto</comment>
+ <comment xml:lang="fo">RAR skjalasavn</comment>
+ <comment xml:lang="fr">archive RAR</comment>
+ <comment xml:lang="ga">cartlann RAR</comment>
+ <comment xml:lang="gl">ficheiro RAR</comment>
+ <comment xml:lang="he">ארכיון RAR</comment>
+ <comment xml:lang="hr">RAR arhiva</comment>
+ <comment xml:lang="hu">RAR-archívum</comment>
+ <comment xml:lang="ia">Archivo RAR</comment>
+ <comment xml:lang="id">Arsip RAR</comment>
+ <comment xml:lang="it">Archivio RAR</comment>
+ <comment xml:lang="ja">RAR アーカイブ</comment>
+ <comment xml:lang="kk">RAR архиві</comment>
+ <comment xml:lang="ko">RAR 압축 파일</comment>
+ <comment xml:lang="lt">RAR archyvas</comment>
+ <comment xml:lang="lv">RAR arhīvs</comment>
+ <comment xml:lang="ms">Arkib RAR</comment>
+ <comment xml:lang="nb">RAR-arkiv</comment>
+ <comment xml:lang="nl">RAR-archief</comment>
+ <comment xml:lang="nn">RAR-arkiv</comment>
+ <comment xml:lang="oc">archiu RAR</comment>
+ <comment xml:lang="pl">Archiwum RAR</comment>
+ <comment xml:lang="pt">arquivo RAR</comment>
+ <comment xml:lang="pt_BR">Pacote RAR</comment>
+ <comment xml:lang="ro">Arhivă RAR</comment>
+ <comment xml:lang="ru">Архив RAR</comment>
+ <comment xml:lang="sk">Archív RAR</comment>
+ <comment xml:lang="sl">Datoteka arhiva RAR</comment>
+ <comment xml:lang="sq">Arkiv RAR</comment>
+ <comment xml:lang="sr">РАР архива</comment>
+ <comment xml:lang="sv">RAR-arkiv</comment>
+ <comment xml:lang="tr">RAR arşivi</comment>
+ <comment xml:lang="uk">архів RAR</comment>
+ <comment xml:lang="vi">Kho nén RAR</comment>
+ <comment xml:lang="zh_CN">RAR 归档文件</comment>
+ <comment xml:lang="zh_TW">RAR 封存檔</comment>
+ <acronym>RAR</acronym>
+ <expanded-acronym>Roshal ARchive</expanded-acronym>
+ <alias type="application/x-rar"/>
+ <alias type="application/x-rar-compressed"/>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="Rar!" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.rar"/>
+ </mime-type>
+ <mime-type type="application/x-dar">
+ <comment>DAR archive</comment>
+ <comment xml:lang="ar">أرشيف DAR</comment>
+ <comment xml:lang="be@latin">Archiŭ DAR</comment>
+ <comment xml:lang="bg">Архив — DAR</comment>
+ <comment xml:lang="ca">arxiu DAR</comment>
+ <comment xml:lang="cs">archiv DAR</comment>
+ <comment xml:lang="da">DAR-arkiv</comment>
+ <comment xml:lang="de">DAR-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο DAR</comment>
+ <comment xml:lang="en_GB">DAR archive</comment>
+ <comment xml:lang="eo">DAR-arkivo</comment>
+ <comment xml:lang="es">archivador DAR</comment>
+ <comment xml:lang="eu">DAR artxiboa</comment>
+ <comment xml:lang="fi">DAR-arkisto</comment>
+ <comment xml:lang="fo">DAR skjalasavn</comment>
+ <comment xml:lang="fr">archive DAR</comment>
+ <comment xml:lang="ga">cartlann DAR</comment>
+ <comment xml:lang="gl">arquivo DAR</comment>
+ <comment xml:lang="he">ארכיון DAR</comment>
+ <comment xml:lang="hr">DAR arhiva</comment>
+ <comment xml:lang="hu">DAR archívum</comment>
+ <comment xml:lang="ia">Archivo DAR</comment>
+ <comment xml:lang="id">Arsip DAR</comment>
+ <comment xml:lang="it">Archivio DAR</comment>
+ <comment xml:lang="ja">DAR アーカイブ</comment>
+ <comment xml:lang="ka">DAR არქივი</comment>
+ <comment xml:lang="kk">DAR архиві</comment>
+ <comment xml:lang="ko">DAR 묶음 파일</comment>
+ <comment xml:lang="lt">DAR archyvas</comment>
+ <comment xml:lang="lv">DAR arhīvs</comment>
+ <comment xml:lang="nb">DAR-arkiv</comment>
+ <comment xml:lang="nl">DAR-archief</comment>
+ <comment xml:lang="nn">DAR-arkiv</comment>
+ <comment xml:lang="oc">archiu DAR</comment>
+ <comment xml:lang="pl">Archiwum DAR</comment>
+ <comment xml:lang="pt">arquivo DAR</comment>
+ <comment xml:lang="pt_BR">Pacote DAR</comment>
+ <comment xml:lang="ro">Arhivă DAR</comment>
+ <comment xml:lang="ru">Архив DAR</comment>
+ <comment xml:lang="sk">Archív DAR</comment>
+ <comment xml:lang="sl">Datoteka arhiva DAR</comment>
+ <comment xml:lang="sq">Arkiv DAR</comment>
+ <comment xml:lang="sr">ДАР архива</comment>
+ <comment xml:lang="sv">DAR-arkiv</comment>
+ <comment xml:lang="tr">DAR arşivi</comment>
+ <comment xml:lang="uk">архів DAR</comment>
+ <comment xml:lang="vi">Kho nén DAR</comment>
+ <comment xml:lang="zh_CN">DAR 归档文件</comment>
+ <comment xml:lang="zh_TW">DAR 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic>
+ <match value="123" type="big32" offset="0"/>
+ </magic>
+ <glob pattern="*.dar"/>
+ </mime-type>
+ <mime-type type="application/x-alz">
+ <comment>Alzip archive</comment>
+ <comment xml:lang="ar">أرشيف Alzip</comment>
+ <comment xml:lang="be@latin">Archiŭ Alzip</comment>
+ <comment xml:lang="bg">Архив — alzip</comment>
+ <comment xml:lang="ca">arxiu Alzip</comment>
+ <comment xml:lang="cs">archiv Alzip</comment>
+ <comment xml:lang="da">Alziparkiv</comment>
+ <comment xml:lang="de">Alzip-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Alzip</comment>
+ <comment xml:lang="en_GB">Alzip archive</comment>
+ <comment xml:lang="eo">Alzip-arkivo</comment>
+ <comment xml:lang="es">archivador Alzip</comment>
+ <comment xml:lang="eu">Alzip artxiboa</comment>
+ <comment xml:lang="fi">Alzip-arkisto</comment>
+ <comment xml:lang="fo">Alsip skjalasavn</comment>
+ <comment xml:lang="fr">archive alzip</comment>
+ <comment xml:lang="ga">cartlann Alzip</comment>
+ <comment xml:lang="gl">arquivo Alzip</comment>
+ <comment xml:lang="he">ארכיון Alzip</comment>
+ <comment xml:lang="hr">Alzip arhiva</comment>
+ <comment xml:lang="hu">Alzip archívum</comment>
+ <comment xml:lang="ia">Archivo Alzip</comment>
+ <comment xml:lang="id">Arsip Alzip</comment>
+ <comment xml:lang="it">Archivio Alzip</comment>
+ <comment xml:lang="ja">Alzip アーカイブ</comment>
+ <comment xml:lang="ka">Alzip არქივი</comment>
+ <comment xml:lang="kk">Alzip архиві</comment>
+ <comment xml:lang="ko">알집 압축 파일</comment>
+ <comment xml:lang="lt">Alzip archyvas</comment>
+ <comment xml:lang="lv">Alzip arhīvs</comment>
+ <comment xml:lang="nb">Alzip-arkiv</comment>
+ <comment xml:lang="nl">Alzip-archief</comment>
+ <comment xml:lang="nn">Alzip-arkiv</comment>
+ <comment xml:lang="oc">archiu alzip</comment>
+ <comment xml:lang="pl">Archiwum alzip</comment>
+ <comment xml:lang="pt">arquivo Alzip</comment>
+ <comment xml:lang="pt_BR">Pacote Alzip</comment>
+ <comment xml:lang="ro">Arhivă Alzip</comment>
+ <comment xml:lang="ru">Архив ALZIP</comment>
+ <comment xml:lang="sk">Archív Alzip</comment>
+ <comment xml:lang="sl">Datoteka arhiva Alzip</comment>
+ <comment xml:lang="sq">Arkiv Alzip</comment>
+ <comment xml:lang="sr">Алзип архива</comment>
+ <comment xml:lang="sv">Alzip-arkiv</comment>
+ <comment xml:lang="tr">Alzip arşivi</comment>
+ <comment xml:lang="uk">архів Alzip</comment>
+ <comment xml:lang="vi">Kho nén Alzip</comment>
+ <comment xml:lang="zh_CN">Alzip 归档文件</comment>
+ <comment xml:lang="zh_TW">Alzip 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="ALZ" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.alz"/>
+ </mime-type>
+ <mime-type type="text/x-reject">
+ <comment>rejected patch</comment>
+ <comment xml:lang="ar">رقعة مرفوضة</comment>
+ <comment xml:lang="be@latin">niepryniaty patch</comment>
+ <comment xml:lang="bg">Отхвърлен файл с кръпка</comment>
+ <comment xml:lang="ca">pedaç rebutjat</comment>
+ <comment xml:lang="cs">odmítnutá záplata</comment>
+ <comment xml:lang="da">afvist tekstlap</comment>
+ <comment xml:lang="de">Abgelehnter Patch</comment>
+ <comment xml:lang="el">Διόρθωση που απορρίφθηκε</comment>
+ <comment xml:lang="en_GB">rejected patch</comment>
+ <comment xml:lang="eo">reĵeta flikaĵo</comment>
+ <comment xml:lang="es">parche rechazado</comment>
+ <comment xml:lang="eu">baztertutako adabakia</comment>
+ <comment xml:lang="fi">hylättyjen muutosten tiedosto</comment>
+ <comment xml:lang="fo">vrakað rætting</comment>
+ <comment xml:lang="fr">correctif rejeté</comment>
+ <comment xml:lang="ga">paiste diúltaithe</comment>
+ <comment xml:lang="gl">parche rexeitado</comment>
+ <comment xml:lang="he">טלאי שנדחה</comment>
+ <comment xml:lang="hr">Odbijena zakrpa</comment>
+ <comment xml:lang="hu">visszautasított folt</comment>
+ <comment xml:lang="ia">Patch rejectate</comment>
+ <comment xml:lang="id">patch ditolak</comment>
+ <comment xml:lang="it">Patch rifiutata</comment>
+ <comment xml:lang="ja">拒否されたパッチ</comment>
+ <comment xml:lang="kk">алынбаған патч</comment>
+ <comment xml:lang="ko">거부된 패치 파일</comment>
+ <comment xml:lang="lt">atmestas lopas</comment>
+ <comment xml:lang="lv">noraidītais ceļš</comment>
+ <comment xml:lang="ms">Tampungan ditolak</comment>
+ <comment xml:lang="nb">avvist patchfil</comment>
+ <comment xml:lang="nl">verworpen patch</comment>
+ <comment xml:lang="nn">avvist programfiks</comment>
+ <comment xml:lang="oc">correctiu regetat</comment>
+ <comment xml:lang="pl">Odrzucona łata</comment>
+ <comment xml:lang="pt">patch rejeitado</comment>
+ <comment xml:lang="pt_BR">Arquivo de patch rejeitado</comment>
+ <comment xml:lang="ro">petec respsins</comment>
+ <comment xml:lang="ru">Отклонённый патч</comment>
+ <comment xml:lang="sk">Odmietnutá záplata</comment>
+ <comment xml:lang="sl">zavrnjen popravek</comment>
+ <comment xml:lang="sq">Patch i kthyer mbrapsht</comment>
+ <comment xml:lang="sr">одбијена закрпа</comment>
+ <comment xml:lang="sv">avvisad programfix</comment>
+ <comment xml:lang="tr">reddedilmiş yama</comment>
+ <comment xml:lang="uk">відхилена латка</comment>
+ <comment xml:lang="vi">đắp vá bị từ chối</comment>
+ <comment xml:lang="zh_CN">拒绝的补丁</comment>
+ <comment xml:lang="zh_TW">回絕的修補</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <alias type="application/x-reject"/>
+ <glob pattern="*.rej"/>
+ </mime-type>
+ <mime-type type="application/x-rpm">
+ <comment>RPM package</comment>
+ <comment xml:lang="ar">حزمة RPM</comment>
+ <comment xml:lang="be@latin">Pakunak RPM</comment>
+ <comment xml:lang="bg">Пакет — RPM</comment>
+ <comment xml:lang="ca">paquet RPM</comment>
+ <comment xml:lang="cs">balíček RPM</comment>
+ <comment xml:lang="da">RPM-pakke</comment>
+ <comment xml:lang="de">RPM-Paket</comment>
+ <comment xml:lang="el">Πακέτο RPM</comment>
+ <comment xml:lang="en_GB">RPM package</comment>
+ <comment xml:lang="eo">RPM-pakaĵo</comment>
+ <comment xml:lang="es">paquete RPM</comment>
+ <comment xml:lang="eu">RPM paketea</comment>
+ <comment xml:lang="fi">RPM-paketti</comment>
+ <comment xml:lang="fo">RPM pakki</comment>
+ <comment xml:lang="fr">paquet RPM</comment>
+ <comment xml:lang="ga">pacáiste RPM</comment>
+ <comment xml:lang="gl">paquete RFM</comment>
+ <comment xml:lang="he">חבילת RPM</comment>
+ <comment xml:lang="hr">RPM paket</comment>
+ <comment xml:lang="hu">RPM-csomag</comment>
+ <comment xml:lang="ia">Pacchetto RPM</comment>
+ <comment xml:lang="id">Paket RPM</comment>
+ <comment xml:lang="it">Pacchetto RPM</comment>
+ <comment xml:lang="ja">RPM パッケージ</comment>
+ <comment xml:lang="kk">RPM дестесі</comment>
+ <comment xml:lang="ko">RPM 패키지</comment>
+ <comment xml:lang="lt">RPM paketas</comment>
+ <comment xml:lang="lv">RPM pakotne</comment>
+ <comment xml:lang="ms">Pakej RPM</comment>
+ <comment xml:lang="nb">RPM-pakke</comment>
+ <comment xml:lang="nl">RPM-pakket</comment>
+ <comment xml:lang="nn">RPM-pakke</comment>
+ <comment xml:lang="oc">paquet RPM</comment>
+ <comment xml:lang="pl">Pakiet RPM</comment>
+ <comment xml:lang="pt">pacote RPM</comment>
+ <comment xml:lang="pt_BR">Pacote RPM</comment>
+ <comment xml:lang="ro">Pachet RPM</comment>
+ <comment xml:lang="ru">Пакет RPM</comment>
+ <comment xml:lang="sk">Balík RPM</comment>
+ <comment xml:lang="sl">Datoteka paketa RPM</comment>
+ <comment xml:lang="sq">Paketë RPM</comment>
+ <comment xml:lang="sr">РПМ пакет</comment>
+ <comment xml:lang="sv">RPM-paket</comment>
+ <comment xml:lang="tr">RPM paketi</comment>
+ <comment xml:lang="uk">пакунок RPM</comment>
+ <comment xml:lang="vi">Gói RPM</comment>
+ <comment xml:lang="zh_CN">RPM 软件包</comment>
+ <comment xml:lang="zh_TW">RPM 軟體包</comment>
+ <generic-icon name="package-x-generic"/>
+ <alias type="application/x-redhat-package-manager"/>
+ <magic priority="50">
+ <match value="\xed\xab\xee\xdb" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.rpm"/>
+ </mime-type>
+ <mime-type type="application/x-source-rpm">
+ <comment>Source RPM package</comment>
+ <comment xml:lang="ca">paquet RPM de codi font</comment>
+ <comment xml:lang="cs">zdrojový balíček RPM</comment>
+ <comment xml:lang="da">Kilde RPM-pakke</comment>
+ <comment xml:lang="de">Quell-RPM-Paket</comment>
+ <comment xml:lang="el">Πακέτο πηγής RPM</comment>
+ <comment xml:lang="en_GB">Source RPM package</comment>
+ <comment xml:lang="es">paquete de fuente RPM</comment>
+ <comment xml:lang="eu">Iturburu RPM paketea</comment>
+ <comment xml:lang="fi">RPM-lähdepaketti</comment>
+ <comment xml:lang="fr">paquet source RPM</comment>
+ <comment xml:lang="ga">pacáiste foinse RPM</comment>
+ <comment xml:lang="gl">Paquete RPM de fontes</comment>
+ <comment xml:lang="he">חבילת מקור RPM</comment>
+ <comment xml:lang="hr">RPM paket izvora</comment>
+ <comment xml:lang="hu">Forrás RPM-csomag</comment>
+ <comment xml:lang="ia">Pacchetto de fonte RPM</comment>
+ <comment xml:lang="id">Paket RPM sumber</comment>
+ <comment xml:lang="it">Pacchetto sorgente RPM</comment>
+ <comment xml:lang="ja">ソース RPM パッケージ</comment>
+ <comment xml:lang="kk">RPM бастапқы код дестесі</comment>
+ <comment xml:lang="ko">소스 RPM 패키지</comment>
+ <comment xml:lang="lv">Avota RPM pakotne</comment>
+ <comment xml:lang="oc">paquet font RPM</comment>
+ <comment xml:lang="pl">Źródłowy pakiet RPM</comment>
+ <comment xml:lang="pt">pacote origem RPM</comment>
+ <comment xml:lang="pt_BR">Pacote fonte RPM</comment>
+ <comment xml:lang="ru">Пакет RPM с исходным кодом</comment>
+ <comment xml:lang="sk">Zdrojový balík RPM</comment>
+ <comment xml:lang="sl">Paket izvorne kode RPM</comment>
+ <comment xml:lang="sr">изворни РПМ пакет</comment>
+ <comment xml:lang="sv">Käll-RPM-paket</comment>
+ <comment xml:lang="tr">Kaynak RPM paketi</comment>
+ <comment xml:lang="uk">пакунок RPM з початковим кодом</comment>
+ <comment xml:lang="zh_CN">源码 RPM 软件包</comment>
+ <comment xml:lang="zh_TW">來源 RPM 軟體包</comment>
+ <generic-icon name="package-x-generic"/>
+ <sub-class-of type="application/x-rpm"/>
+ <glob pattern="*.src.rpm"/>
+ <glob pattern="*.spm"/>
+ </mime-type>
+ <mime-type type="application/x-ruby">
+ <comment>Ruby script</comment>
+ <comment xml:lang="ar">سكربت روبي</comment>
+ <comment xml:lang="be@latin">Skrypt Ruby</comment>
+ <comment xml:lang="bg">Скрипт — Ruby</comment>
+ <comment xml:lang="ca">script Ruby</comment>
+ <comment xml:lang="cs">skript Ruby</comment>
+ <comment xml:lang="da">Rubyprogram</comment>
+ <comment xml:lang="de">Ruby-Skript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών Ruby</comment>
+ <comment xml:lang="en_GB">Ruby script</comment>
+ <comment xml:lang="eo">Ruby-skripto</comment>
+ <comment xml:lang="es">secuencia de órdenes en Ruby</comment>
+ <comment xml:lang="eu">Ruby script-a</comment>
+ <comment xml:lang="fi">Ruby-komentotiedosto</comment>
+ <comment xml:lang="fo">Ruby boðrøð</comment>
+ <comment xml:lang="fr">script Ruby</comment>
+ <comment xml:lang="ga">script Ruby</comment>
+ <comment xml:lang="gl">Script de Ruby</comment>
+ <comment xml:lang="he">תסריט Ruby</comment>
+ <comment xml:lang="hr">Ruby skripta</comment>
+ <comment xml:lang="hu">Ruby-parancsfájl</comment>
+ <comment xml:lang="ia">Script Ruby</comment>
+ <comment xml:lang="id">Skrip Ruby</comment>
+ <comment xml:lang="it">Script Ruby</comment>
+ <comment xml:lang="ja">Ruby スクリプト</comment>
+ <comment xml:lang="kk">Ruby сценарийі</comment>
+ <comment xml:lang="ko">루비 스크립트</comment>
+ <comment xml:lang="lt">Ruby scenarijus</comment>
+ <comment xml:lang="lv">Ruby skripts</comment>
+ <comment xml:lang="ms">Skrip Ruby</comment>
+ <comment xml:lang="nb">Ruby-skript</comment>
+ <comment xml:lang="nl">Ruby-script</comment>
+ <comment xml:lang="nn">Ruby-skript</comment>
+ <comment xml:lang="oc">escript Ruby</comment>
+ <comment xml:lang="pl">Skrypt Ruby</comment>
+ <comment xml:lang="pt">script Ruby</comment>
+ <comment xml:lang="pt_BR">Script Ruby</comment>
+ <comment xml:lang="ro">Script Ruby</comment>
+ <comment xml:lang="ru">Сценарий Ruby</comment>
+ <comment xml:lang="sk">Skript Ruby</comment>
+ <comment xml:lang="sl">Skriptna datoteka Ruby</comment>
+ <comment xml:lang="sq">Script Ruby</comment>
+ <comment xml:lang="sr">Руби скрипта</comment>
+ <comment xml:lang="sv">Ruby-skript</comment>
+ <comment xml:lang="tr">Ruby betiği</comment>
+ <comment xml:lang="uk">скрипт Ruby</comment>
+ <comment xml:lang="vi">Văn lệnh Ruby</comment>
+ <comment xml:lang="zh_CN">Ruby 脚本</comment>
+ <comment xml:lang="zh_TW">Ruby 指令稿</comment>
+ <sub-class-of type="application/x-executable"/>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-script"/>
+ <magic>
+ <match value="/bin/env ruby" type="string" offset="2:16"/>
+ <match value="/bin/ruby" type="string" offset="2:16"/>
+ </magic>
+ <glob pattern="*.rb"/>
+ </mime-type>
+ <mime-type type="application/x-markaby">
+ <comment>Markaby script</comment>
+ <comment xml:lang="ar">سكربت Markaby</comment>
+ <comment xml:lang="be@latin">Skrypt Markaby</comment>
+ <comment xml:lang="bg">Скрипт — Markaby</comment>
+ <comment xml:lang="ca">script Markaby</comment>
+ <comment xml:lang="cs">skript Markaby</comment>
+ <comment xml:lang="da">Markabyprogram</comment>
+ <comment xml:lang="de">Markaby-Skript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών Markaby</comment>
+ <comment xml:lang="en_GB">Markaby script</comment>
+ <comment xml:lang="eo">Markaby-skripto</comment>
+ <comment xml:lang="es">secuencia de órdenes en Markaby</comment>
+ <comment xml:lang="eu">Markaby script-a</comment>
+ <comment xml:lang="fi">Markaby-komentotiedosto</comment>
+ <comment xml:lang="fo">Markaby boðrøð</comment>
+ <comment xml:lang="fr">script Markaby</comment>
+ <comment xml:lang="ga">script Markaby</comment>
+ <comment xml:lang="gl">Script de Markaby</comment>
+ <comment xml:lang="he">תסריט Markby</comment>
+ <comment xml:lang="hr">Markaby skripta</comment>
+ <comment xml:lang="hu">Markaby parancsfájl</comment>
+ <comment xml:lang="ia">Script Markaby</comment>
+ <comment xml:lang="id">Skrip Markaby</comment>
+ <comment xml:lang="it">Script Markaby</comment>
+ <comment xml:lang="ja">Markaby スクリプト</comment>
+ <comment xml:lang="ka">Markaby-ის სცენარი</comment>
+ <comment xml:lang="kk">Markaby сценарийі</comment>
+ <comment xml:lang="ko">Markaby 스크립트</comment>
+ <comment xml:lang="lt">Markaby scenarijus</comment>
+ <comment xml:lang="lv">Markaby skripts</comment>
+ <comment xml:lang="nb">Markaby-skript</comment>
+ <comment xml:lang="nl">Markaby-script</comment>
+ <comment xml:lang="nn">Markaby-skript</comment>
+ <comment xml:lang="oc">escript Markaby</comment>
+ <comment xml:lang="pl">Skrypt Markaby</comment>
+ <comment xml:lang="pt">script Markaby</comment>
+ <comment xml:lang="pt_BR">Script Markaby</comment>
+ <comment xml:lang="ro">Script Markaby</comment>
+ <comment xml:lang="ru">Сценарий Markaby</comment>
+ <comment xml:lang="sk">Skript Markaby</comment>
+ <comment xml:lang="sl">Skriptna datoteka Markaby</comment>
+ <comment xml:lang="sq">Script Markaby</comment>
+ <comment xml:lang="sr">Маркаби скрипта</comment>
+ <comment xml:lang="sv">Markaby-skript</comment>
+ <comment xml:lang="tr">Markaby betiği</comment>
+ <comment xml:lang="uk">скрипт Markaby</comment>
+ <comment xml:lang="vi">Văn lệnh Markaby</comment>
+ <comment xml:lang="zh_CN">RMarkaby 脚本</comment>
+ <comment xml:lang="zh_TW">Markaby 指令稿</comment>
+ <sub-class-of type="application/x-ruby"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.mab"/>
+ </mime-type>
+ <mime-type type="text/rust">
+ <comment>Rust source code</comment>
+ <comment xml:lang="ca">codi font en Rust</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Rust</comment>
+ <comment xml:lang="da">Rust-kildekode</comment>
+ <comment xml:lang="de">Rust-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Rust</comment>
+ <comment xml:lang="en_GB">Rust source code</comment>
+ <comment xml:lang="es">código fuente en Rust</comment>
+ <comment xml:lang="eu">Rust iturburu-kodea</comment>
+ <comment xml:lang="fi">Rust-lähdekoodi</comment>
+ <comment xml:lang="fr">code source Rust</comment>
+ <comment xml:lang="ga">cód foinseach Rust</comment>
+ <comment xml:lang="he">קוד מקור של Rust</comment>
+ <comment xml:lang="hr">Rust izvorni kôd</comment>
+ <comment xml:lang="hu">Rust forrásfájl</comment>
+ <comment xml:lang="ia">Codice-fonte Rust</comment>
+ <comment xml:lang="id">Kode program Rust</comment>
+ <comment xml:lang="it">Codice sorgente Rust</comment>
+ <comment xml:lang="kk">Rust бастапқы коды</comment>
+ <comment xml:lang="ko">Rust 소스 코드</comment>
+ <comment xml:lang="oc">còde font Rust</comment>
+ <comment xml:lang="pl">Kod źródłowy Rust</comment>
+ <comment xml:lang="pt">código origem Rust</comment>
+ <comment xml:lang="pt_BR">Código-fonte Rust</comment>
+ <comment xml:lang="ru">Исходный код Rust</comment>
+ <comment xml:lang="sk">Zdrojový kód Rust</comment>
+ <comment xml:lang="sr">Раст изворни ко̂д</comment>
+ <comment xml:lang="sv">Rust-källkod</comment>
+ <comment xml:lang="tr">Rust kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Rust</comment>
+ <comment xml:lang="zh_CN">Rust 源代码</comment>
+ <comment xml:lang="zh_TW">Rust 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.rs"/>
+ </mime-type>
+ <mime-type type="application/x-sc">
+ <comment>SC/Xspread spreadsheet</comment>
+ <comment xml:lang="ar">جدول SC/Xspread</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš SC/Xspread</comment>
+ <comment xml:lang="bg">Таблица — SC/Xspread</comment>
+ <comment xml:lang="ca">full de càlcul de SC/Xspread</comment>
+ <comment xml:lang="cs">sešit SC/Xspread</comment>
+ <comment xml:lang="da">SC/Xspread-regneark</comment>
+ <comment xml:lang="de">SX/Xspread-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο SC/Xspread</comment>
+ <comment xml:lang="en_GB">SC/Xspread spreadsheet</comment>
+ <comment xml:lang="eo">SC/Xspread-kalkultabelo</comment>
+ <comment xml:lang="es">hoja de cálculo SC/Xspread</comment>
+ <comment xml:lang="eu">SC/Xspread kalkulu-orria</comment>
+ <comment xml:lang="fi">SC/Xspread-taulukko</comment>
+ <comment xml:lang="fo">SC/Xspread rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul SC/Xspread</comment>
+ <comment xml:lang="ga">scarbhileog SC/Xspread</comment>
+ <comment xml:lang="gl">folla de cálculo SC/Xspread</comment>
+ <comment xml:lang="he">גליון נתונים של SC/Xspread</comment>
+ <comment xml:lang="hr">SC/Xspread proračunska tablica</comment>
+ <comment xml:lang="hu">SC/Xspread táblázat</comment>
+ <comment xml:lang="ia">Folio de calculo SC/Xspread</comment>
+ <comment xml:lang="id">Lembar sebar SC/Xspread</comment>
+ <comment xml:lang="it">Foglio di calcolo SC/Xspread</comment>
+ <comment xml:lang="ja">SC/Xspread スプレッドシート</comment>
+ <comment xml:lang="kk">SC/Xspread электрондық кестесі</comment>
+ <comment xml:lang="ko">SC/Xspread 스프레드시트</comment>
+ <comment xml:lang="lt">SC/Xspread skaičialentė</comment>
+ <comment xml:lang="lv">SC/Xspread izklājlapa</comment>
+ <comment xml:lang="nb">SC/Xspread-regneark</comment>
+ <comment xml:lang="nl">SC/Xspread-rekenblad</comment>
+ <comment xml:lang="nn">SC/Xspread-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul SC/Xspread</comment>
+ <comment xml:lang="pl">Arkusz SC/Xspread</comment>
+ <comment xml:lang="pt">folha de cálculo SC/Xspread</comment>
+ <comment xml:lang="pt_BR">Planilha do SC/Xspread</comment>
+ <comment xml:lang="ro">Foaie de calcul SC/Xspread</comment>
+ <comment xml:lang="ru">Электронная таблица SC/Xspread</comment>
+ <comment xml:lang="sk">Zošit SC/Xspread</comment>
+ <comment xml:lang="sl">Preglednica SC/Xspread</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh SC/Xspread</comment>
+ <comment xml:lang="sr">табела СЦ/Икс-табеле</comment>
+ <comment xml:lang="sv">SC/Xspread-kalkylblad</comment>
+ <comment xml:lang="tr">SC/Xspread çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця SC/Xspread</comment>
+ <comment xml:lang="vi">Bảng tính SC/Xspread</comment>
+ <comment xml:lang="zh_CN">SC/Xspread 电子表格</comment>
+ <comment xml:lang="zh_TW">SC/Xspread 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <magic priority="50">
+ <match value="Spreadsheet" type="string" offset="38"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-shar">
+ <comment>shell archive</comment>
+ <comment xml:lang="ar">أرشيف شِل</comment>
+ <comment xml:lang="az">qabıq arxivi</comment>
+ <comment xml:lang="be@latin">archiŭ abałonki</comment>
+ <comment xml:lang="bg">Архив на обвивката</comment>
+ <comment xml:lang="ca">arxiu de shell</comment>
+ <comment xml:lang="cs">archiv shellu</comment>
+ <comment xml:lang="cy">archif plisgyn</comment>
+ <comment xml:lang="da">skalarkiv</comment>
+ <comment xml:lang="de">Shell-Archiv</comment>
+ <comment xml:lang="el">Αρχείο κέλυφους</comment>
+ <comment xml:lang="en_GB">shell archive</comment>
+ <comment xml:lang="eo">ŝel-arkivo</comment>
+ <comment xml:lang="es">archivador shell</comment>
+ <comment xml:lang="eu">shell artxiboa</comment>
+ <comment xml:lang="fi">komentotulkkiarkisto</comment>
+ <comment xml:lang="fo">skel savn</comment>
+ <comment xml:lang="fr">archive shell</comment>
+ <comment xml:lang="ga">cartlann bhlaoisce</comment>
+ <comment xml:lang="gl">ficheiro shell</comment>
+ <comment xml:lang="he">ארכיון מעטפת</comment>
+ <comment xml:lang="hr">Arhiva ljuske</comment>
+ <comment xml:lang="hu">héjarchívum</comment>
+ <comment xml:lang="ia">Archivo de shell</comment>
+ <comment xml:lang="id">arsip shell</comment>
+ <comment xml:lang="it">Archivio shell</comment>
+ <comment xml:lang="ja">シェルアーカイブ</comment>
+ <comment xml:lang="kk">қоршам архиві</comment>
+ <comment xml:lang="ko">셸 압축 파일</comment>
+ <comment xml:lang="lt">shell archyvas</comment>
+ <comment xml:lang="lv">čaulas arhīvs</comment>
+ <comment xml:lang="ms">Arkib shell</comment>
+ <comment xml:lang="nb">skallarkiv</comment>
+ <comment xml:lang="nl">shell-archief</comment>
+ <comment xml:lang="nn">skal-arkiv</comment>
+ <comment xml:lang="oc">archiu shell</comment>
+ <comment xml:lang="pl">Archiwum powłoki</comment>
+ <comment xml:lang="pt">arquivo de terminal</comment>
+ <comment xml:lang="pt_BR">Pacote shell</comment>
+ <comment xml:lang="ro">arhivă shell</comment>
+ <comment xml:lang="ru">Архив shell</comment>
+ <comment xml:lang="sk">Archív shellu</comment>
+ <comment xml:lang="sl">lupinski arhiv</comment>
+ <comment xml:lang="sq">Arkiv shell</comment>
+ <comment xml:lang="sr">архива љуске</comment>
+ <comment xml:lang="sv">skalarkiv</comment>
+ <comment xml:lang="tr">kabuk arşivi</comment>
+ <comment xml:lang="uk">архів оболонки</comment>
+ <comment xml:lang="vi">kho trình bao</comment>
+ <comment xml:lang="zh_CN">shell 归档文件</comment>
+ <comment xml:lang="zh_TW">shell 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.shar"/>
+ </mime-type>
+ <mime-type type="application/x-shared-library-la">
+ <comment>libtool shared library</comment>
+ <comment xml:lang="ar">مكتبة libtool المشتركة</comment>
+ <comment xml:lang="be@latin">supolnaja biblijateka libtool</comment>
+ <comment xml:lang="bg">Споделена библиотека — libtool</comment>
+ <comment xml:lang="ca">biblioteca compartida libtool</comment>
+ <comment xml:lang="cs">sdílená knihovna libtool</comment>
+ <comment xml:lang="da">libtool delt bibliotek</comment>
+ <comment xml:lang="de">Gemeinsame libtool-Bibliothek</comment>
+ <comment xml:lang="el">Κοινόχρηστη βιβλιοθήκη libtool</comment>
+ <comment xml:lang="en_GB">libtool shared library</comment>
+ <comment xml:lang="es">biblioteca compartida de libtool</comment>
+ <comment xml:lang="eu">libtool partekatutako liburutegia</comment>
+ <comment xml:lang="fi">jaettu libtool-kirjasto</comment>
+ <comment xml:lang="fo">libtool felagssavn</comment>
+ <comment xml:lang="fr">bibliothèque partagée libtool</comment>
+ <comment xml:lang="ga">comhleabharlann libtool</comment>
+ <comment xml:lang="gl">biblioteca compartida de libtool</comment>
+ <comment xml:lang="he">ספרייה משותפת של libtool</comment>
+ <comment xml:lang="hr">libtool dijeljena biblioteka</comment>
+ <comment xml:lang="hu">libtool osztott programkönyvtár</comment>
+ <comment xml:lang="ia">Bibliotheca commun Libtool</comment>
+ <comment xml:lang="id">pustaka bersama libtool</comment>
+ <comment xml:lang="it">Libreria condivisa libtool</comment>
+ <comment xml:lang="ja">libtool 共有ライブラリ</comment>
+ <comment xml:lang="kk">libtool ортақ жинағы</comment>
+ <comment xml:lang="ko">libtool 공유 라이브러리</comment>
+ <comment xml:lang="lt">libtool bendroji biblioteka</comment>
+ <comment xml:lang="lv">libtool koplietotā bibliotēka</comment>
+ <comment xml:lang="nb">libtool delt bibliotek</comment>
+ <comment xml:lang="nl">gedeelde libtool-bibliotheek</comment>
+ <comment xml:lang="nn">libtool delt bibliotek</comment>
+ <comment xml:lang="oc">bibliotèca partejada libtool</comment>
+ <comment xml:lang="pl">Biblioteka współdzielona libtool</comment>
+ <comment xml:lang="pt">biblioteca partilhada libtool</comment>
+ <comment xml:lang="pt_BR">Biblioteca compartilhada libtool</comment>
+ <comment xml:lang="ro">bibliotecă partajată libtool</comment>
+ <comment xml:lang="ru">Разделяемая библиотека libtool</comment>
+ <comment xml:lang="sk">Zdieľaná knižnica libtool</comment>
+ <comment xml:lang="sl">Souporabna knjižnica libtool</comment>
+ <comment xml:lang="sq">Librari e përbashkët libtool</comment>
+ <comment xml:lang="sr">дељена библиотека библ-алата</comment>
+ <comment xml:lang="sv">delat libtool-bibliotek</comment>
+ <comment xml:lang="tr">libtool paylaşımlı kitaplığı</comment>
+ <comment xml:lang="uk">спільна бібліотека libtool</comment>
+ <comment xml:lang="vi">thư viện dùng chung libtool</comment>
+ <comment xml:lang="zh_CN">libtool 共享库</comment>
+ <comment xml:lang="zh_TW">libtool 共享函式庫</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-script"/>
+ <glob pattern="*.la"/>
+ </mime-type>
+ <mime-type type="application/x-sharedlib">
+ <comment>shared library</comment>
+ <comment xml:lang="ar">مكتبة مشتركة</comment>
+ <comment xml:lang="az">bölüşülmüş kitabxana</comment>
+ <comment xml:lang="be@latin">supolnaja biblijateka</comment>
+ <comment xml:lang="bg">Споделена библиотека</comment>
+ <comment xml:lang="ca">biblioteca compartida</comment>
+ <comment xml:lang="cs">sdílená knihovna</comment>
+ <comment xml:lang="cy">llyfrgell wedi ei rhannu</comment>
+ <comment xml:lang="da">delt bibliotek</comment>
+ <comment xml:lang="de">Gemeinsame Bibliothek</comment>
+ <comment xml:lang="el">Αρχείο κοινόχρηστης βιβλιοθήκης</comment>
+ <comment xml:lang="en_GB">shared library</comment>
+ <comment xml:lang="eo">dinamike bindebla biblioteko</comment>
+ <comment xml:lang="es">biblioteca compartida</comment>
+ <comment xml:lang="eu">partekatutako liburutegia</comment>
+ <comment xml:lang="fi">jaettu kirjasto</comment>
+ <comment xml:lang="fo">felagssavn</comment>
+ <comment xml:lang="fr">bibliothèque partagée</comment>
+ <comment xml:lang="ga">comhleabharlann</comment>
+ <comment xml:lang="gl">biblioteca compartida</comment>
+ <comment xml:lang="he">ספרייה משותפת</comment>
+ <comment xml:lang="hr">dijeljena biblioteka</comment>
+ <comment xml:lang="hu">osztott programkönyvtár</comment>
+ <comment xml:lang="ia">Bibliotheca commun</comment>
+ <comment xml:lang="id">pustaka bersama</comment>
+ <comment xml:lang="it">Libreria condivisa</comment>
+ <comment xml:lang="ja">共有ライブラリ</comment>
+ <comment xml:lang="kk">бөлісетін библиотека</comment>
+ <comment xml:lang="ko">공유 라이브러리</comment>
+ <comment xml:lang="lt">bendroji biblioteka</comment>
+ <comment xml:lang="lv">koplietotā bibliotēka</comment>
+ <comment xml:lang="ms">Pustaka terkongsi</comment>
+ <comment xml:lang="nb">delt bibliotek</comment>
+ <comment xml:lang="nl">gedeelde bibliotheek</comment>
+ <comment xml:lang="nn">delt bibliotek</comment>
+ <comment xml:lang="oc">bibliotèca partejada</comment>
+ <comment xml:lang="pl">Biblioteka współdzielona</comment>
+ <comment xml:lang="pt">biblioteca partilhada</comment>
+ <comment xml:lang="pt_BR">Biblioteca compartilhada</comment>
+ <comment xml:lang="ro">bibliotecă partajată</comment>
+ <comment xml:lang="ru">Разделяемая библиотека</comment>
+ <comment xml:lang="sk">Zdieľaná knižnica</comment>
+ <comment xml:lang="sl">souporabljena knjižnica</comment>
+ <comment xml:lang="sq">Librari e përbashkët</comment>
+ <comment xml:lang="sr">дељена библиотека</comment>
+ <comment xml:lang="sv">delat bibliotek</comment>
+ <comment xml:lang="tr">paylaşımlı kitaplık</comment>
+ <comment xml:lang="uk">спільна бібліотека</comment>
+ <comment xml:lang="vi">thư viện dùng chung</comment>
+ <comment xml:lang="zh_CN">共享库</comment>
+ <comment xml:lang="zh_TW">共享函式庫</comment>
+ <magic priority="50">
+ <match value="\177ELF" type="string" offset="0">
+ <match value="1" type="byte" offset="5">
+ <match value="3" type="little16" offset="16"/>
+ </match>
+ </match>
+ <match value="\177ELF" type="string" offset="0">
+ <match value="2" type="byte" offset="5">
+ <match value="3" type="big16" offset="16"/>
+ </match>
+ </match>
+ <match value="0603" type="little16" offset="0">
+ <match value="020000" type="little16" offset="22" mask="030000"/>
+ </match>
+ <match value="\177ELF \003" type="string" offset="0" mask="0xffffffff000000000000000000000000ff"/>
+ </magic>
+ <glob pattern="*.so"/>
+ </mime-type>
+ <mime-type type="application/x-shellscript">
+ <comment>shell script</comment>
+ <comment xml:lang="ar">سكربت شِل</comment>
+ <comment xml:lang="az">qabıq skripti</comment>
+ <comment xml:lang="be@latin">skrypt abałonki</comment>
+ <comment xml:lang="bg">Скрипт на обвивката</comment>
+ <comment xml:lang="ca">script shell</comment>
+ <comment xml:lang="cs">skript shellu</comment>
+ <comment xml:lang="cy">sgript plisgyn</comment>
+ <comment xml:lang="da">skalprogram</comment>
+ <comment xml:lang="de">Shell-Skript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών κελύφους</comment>
+ <comment xml:lang="en_GB">shell script</comment>
+ <comment xml:lang="eo">ŝelskripto</comment>
+ <comment xml:lang="es">secuencia de órdenes en shell</comment>
+ <comment xml:lang="eu">shell script-a</comment>
+ <comment xml:lang="fi">komentotulkin komentotiedosto</comment>
+ <comment xml:lang="fo">skel boðrøð</comment>
+ <comment xml:lang="fr">script shell</comment>
+ <comment xml:lang="ga">script bhlaoisce</comment>
+ <comment xml:lang="gl">script de shell</comment>
+ <comment xml:lang="he">תסריט מעטפת</comment>
+ <comment xml:lang="hr">Skripta ljuske</comment>
+ <comment xml:lang="hu">héj-parancsfájl</comment>
+ <comment xml:lang="ia">Script de shell</comment>
+ <comment xml:lang="id">skrip shell</comment>
+ <comment xml:lang="it">Script shell</comment>
+ <comment xml:lang="ja">シェルスクリプト</comment>
+ <comment xml:lang="kk">қоршам сценарийі</comment>
+ <comment xml:lang="ko">셸 스크립트</comment>
+ <comment xml:lang="lt">shell scenarijus</comment>
+ <comment xml:lang="lv">čaulas skripts</comment>
+ <comment xml:lang="ms">Skrip shell</comment>
+ <comment xml:lang="nb">skallskript</comment>
+ <comment xml:lang="nl">shellscript</comment>
+ <comment xml:lang="nn">skalskript</comment>
+ <comment xml:lang="oc">escript shell</comment>
+ <comment xml:lang="pl">Skrypt powłoki</comment>
+ <comment xml:lang="pt">script de terminal</comment>
+ <comment xml:lang="pt_BR">Script shell</comment>
+ <comment xml:lang="ro">script shell</comment>
+ <comment xml:lang="ru">Сценарий shell</comment>
+ <comment xml:lang="sk">Skript shellu</comment>
+ <comment xml:lang="sl">lupinski skript</comment>
+ <comment xml:lang="sq">Script shell</comment>
+ <comment xml:lang="sr">скрипта љуске</comment>
+ <comment xml:lang="sv">skalskript</comment>
+ <comment xml:lang="tr">kabuk betiği</comment>
+ <comment xml:lang="uk">скрипт оболонки</comment>
+ <comment xml:lang="vi">văn lệnh trình bao</comment>
+ <comment xml:lang="zh_CN">shell 脚本</comment>
+ <comment xml:lang="zh_TW">shell 指令稿</comment>
+ <sub-class-of type="application/x-executable"/>
+ <sub-class-of type="text/plain"/>
+ <alias type="text/x-sh"/>
+ <generic-icon name="text-x-script"/>
+ <magic priority="50">
+ <match value="# This is a shell archive" type="string" offset="10"/>
+ <match value="/bin/bash" type="string" offset="2:16"/>
+ <match value="/bin/nawk" type="string" offset="2:16"/>
+ <match value="/bin/zsh" type="string" offset="2:16"/>
+ <match value="/bin/sh" type="string" offset="2:16"/>
+ <match value="/bin/ksh" type="string" offset="2:16"/>
+ <match value="/bin/dash" type="string" offset="2:16"/>
+ <match value="/bin/env sh" type="string" offset="2:16"/>
+ <match value="/bin/env bash" type="string" offset="2:16"/>
+ <match value="/bin/env zsh" type="string" offset="2:16"/>
+ <match value="/bin/env ksh" type="string" offset="2:16"/>
+ </magic>
+ <glob pattern="*.sh"/>
+ </mime-type>
+ <mime-type type="application/vnd.adobe.flash.movie">
+ <comment>Shockwave Flash file</comment>
+ <comment xml:lang="ar">ملف Shockwave Flash</comment>
+ <comment xml:lang="be@latin">Fajł Shockwave Flash</comment>
+ <comment xml:lang="bg">Файл — Shockwave Flash</comment>
+ <comment xml:lang="ca">fitxer Shockwave Flash</comment>
+ <comment xml:lang="cs">soubor Shockwave Flash</comment>
+ <comment xml:lang="da">Shockwave Flash-fil</comment>
+ <comment xml:lang="de">Shockwave-Flash-Datei</comment>
+ <comment xml:lang="el">Αρχείο Shockwave Flash</comment>
+ <comment xml:lang="en_GB">Shockwave Flash file</comment>
+ <comment xml:lang="eo">dosiero de Shockwave Flash</comment>
+ <comment xml:lang="es">archivo Shockwave Flash</comment>
+ <comment xml:lang="eu">Shockwave Flash fitxategia</comment>
+ <comment xml:lang="fi">Shockwave Flash -tiedosto</comment>
+ <comment xml:lang="fo">Shockwave Flash fíla</comment>
+ <comment xml:lang="fr">fichier Shockwave Flash</comment>
+ <comment xml:lang="ga">comhad Shockwave Flash</comment>
+ <comment xml:lang="gl">ficheiro sockwave Flash</comment>
+ <comment xml:lang="he">קובץ של Shockwave Flash</comment>
+ <comment xml:lang="hr">Shockwave Flash datoteka</comment>
+ <comment xml:lang="hu">Shockwave Flash-fájl</comment>
+ <comment xml:lang="ia">File Shockwave Flash</comment>
+ <comment xml:lang="id">Berkas Shockwave Flash</comment>
+ <comment xml:lang="it">File Shockwave Flash</comment>
+ <comment xml:lang="ja">Shockwave Flash ファイル</comment>
+ <comment xml:lang="kk">Shockwave Flash файлы</comment>
+ <comment xml:lang="ko">Shockwave 플래시 파일</comment>
+ <comment xml:lang="lt">Shockwave Flash failas</comment>
+ <comment xml:lang="lv">Shockwave Flash datne</comment>
+ <comment xml:lang="ms">Fail Shockwave Flash</comment>
+ <comment xml:lang="nb">Shockwave Flash-fil</comment>
+ <comment xml:lang="nl">Shockwave Flash-bestand</comment>
+ <comment xml:lang="nn">Shockwave Flash-fil</comment>
+ <comment xml:lang="oc">fichièr Shockwave Flash</comment>
+ <comment xml:lang="pl">Plik Shockwave Flash</comment>
+ <comment xml:lang="pt">ficheiro Shockwave Flash</comment>
+ <comment xml:lang="pt_BR">Arquivo Shockwave Flash</comment>
+ <comment xml:lang="ro">Fișier Shockwave Flash</comment>
+ <comment xml:lang="ru">Файл Shockwave Flash</comment>
+ <comment xml:lang="sk">Súbor Shockwave Flash</comment>
+ <comment xml:lang="sl">Datoteka Shockwave Flash</comment>
+ <comment xml:lang="sq">File Flash Shockwave</comment>
+ <comment xml:lang="sr">Шоквејв Флеш датотека</comment>
+ <comment xml:lang="sv">Shockwave Flash-fil</comment>
+ <comment xml:lang="tr">Shockwave Flash dosyası</comment>
+ <comment xml:lang="uk">файл Shockwave Flash</comment>
+ <comment xml:lang="vi">Tập tin Flash Shockwave</comment>
+ <comment xml:lang="zh_CN">Shockwave Flash 文件</comment>
+ <comment xml:lang="zh_TW">Shockwave Flash 檔案</comment>
+ <alias type="application/x-shockwave-flash"/>
+ <alias type="application/futuresplash"/>
+ <generic-icon name="video-x-generic"/>
+ <magic priority="50">
+ <match value="FWS" type="string" offset="0"/>
+ <match value="CWS" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.swf"/>
+ <glob pattern="*.spl"/>
+ </mime-type>
+ <mime-type type="application/x-shorten">
+
+ <comment>Shorten audio</comment>
+ <comment xml:lang="ar">Shorten سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Shorten</comment>
+ <comment xml:lang="bg">Аудио — Shorten</comment>
+ <comment xml:lang="ca">àudio de Shorten</comment>
+ <comment xml:lang="cs">zvuk Shorten</comment>
+ <comment xml:lang="da">Shortenlyd</comment>
+ <comment xml:lang="de">Shorten-Audio</comment>
+ <comment xml:lang="el">Ήχος Shorten</comment>
+ <comment xml:lang="en_GB">Shorten audio</comment>
+ <comment xml:lang="eo">Shorten-sondosiero</comment>
+ <comment xml:lang="es">sonido Shorten</comment>
+ <comment xml:lang="eu">Shorten audioa</comment>
+ <comment xml:lang="fi">Shorten-ääni</comment>
+ <comment xml:lang="fo">Shorten ljóður</comment>
+ <comment xml:lang="fr">audio Shorten</comment>
+ <comment xml:lang="ga">fuaim Shorten</comment>
+ <comment xml:lang="gl">son Shorten</comment>
+ <comment xml:lang="he">שמע של Shorten</comment>
+ <comment xml:lang="hr">Shorten zvučni zapis</comment>
+ <comment xml:lang="hu">Shorten hang</comment>
+ <comment xml:lang="ia">Audio Shorten</comment>
+ <comment xml:lang="id">Audio Shorten</comment>
+ <comment xml:lang="it">Audio Shorten</comment>
+ <comment xml:lang="ja">Shorten オーディオ</comment>
+ <comment xml:lang="kk">Shorten аудиосы</comment>
+ <comment xml:lang="ko">Shorten 오디오</comment>
+ <comment xml:lang="lt">Shorten garso įrašas</comment>
+ <comment xml:lang="lv">Shorten audio</comment>
+ <comment xml:lang="nb">Shorten lyd</comment>
+ <comment xml:lang="nl">Shorten-audio</comment>
+ <comment xml:lang="nn">Shorten-lyd</comment>
+ <comment xml:lang="oc">àudio Shorten</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Shorten</comment>
+ <comment xml:lang="pt">áudio Shorten</comment>
+ <comment xml:lang="pt_BR">Áudio Shorten</comment>
+ <comment xml:lang="ro">Audio Shorten</comment>
+ <comment xml:lang="ru">Аудио Shorten</comment>
+ <comment xml:lang="sk">Zvuk Shorten</comment>
+ <comment xml:lang="sl">Zvočna datoteka Shorten</comment>
+ <comment xml:lang="sq">Audio Shorten</comment>
+ <comment xml:lang="sr">Шортен звук</comment>
+ <comment xml:lang="sv">Shorten-ljud</comment>
+ <comment xml:lang="tr">Kısaltılmış ses</comment>
+ <comment xml:lang="uk">звук Shorten</comment>
+ <comment xml:lang="vi">Âm thanh Shorten</comment>
+ <comment xml:lang="zh_CN">Shorten 音频</comment>
+ <comment xml:lang="zh_TW">Shorten 音訊</comment>
+ <generic-icon name="audio-x-generic"/>
+ <magic priority="50">
+ <match value="ajkg" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.shn"/>
+ <alias type="audio/x-shorten"/>
+ </mime-type>
+ <mime-type type="application/x-siag">
+ <comment>Siag spreadsheet</comment>
+ <comment xml:lang="ar">جدول Siag</comment>
+ <comment xml:lang="be@latin">Raźlikovy arkuš Siag</comment>
+ <comment xml:lang="bg">Таблица — Siag</comment>
+ <comment xml:lang="ca">full de càlcul Siag</comment>
+ <comment xml:lang="cs">sešit Siag</comment>
+ <comment xml:lang="da">Siagregneark</comment>
+ <comment xml:lang="de">Siag-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο Siag</comment>
+ <comment xml:lang="en_GB">Siag spreadsheet</comment>
+ <comment xml:lang="eo">Siag-kalkultabelo</comment>
+ <comment xml:lang="es">hoja de cálculo de Siag</comment>
+ <comment xml:lang="eu">Siag kalkulu-orria</comment>
+ <comment xml:lang="fi">Siag-taulukko</comment>
+ <comment xml:lang="fo">Siag rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul Siag</comment>
+ <comment xml:lang="ga">scarbhileog Siag</comment>
+ <comment xml:lang="gl">folla de cálculo de Siag</comment>
+ <comment xml:lang="he">גליון נתונים של Siag</comment>
+ <comment xml:lang="hr">Siag proračunska tablica</comment>
+ <comment xml:lang="hu">Siag-munkafüzet</comment>
+ <comment xml:lang="ia">Folio de calculo Siag</comment>
+ <comment xml:lang="id">Lembar sebar Siag</comment>
+ <comment xml:lang="it">Foglio di calcolo Siag</comment>
+ <comment xml:lang="ja">Siag スプレッドシート</comment>
+ <comment xml:lang="kk">Siag электрондық кестесі</comment>
+ <comment xml:lang="ko">Siag 스프레드시트</comment>
+ <comment xml:lang="lt">Siag skaičialentė</comment>
+ <comment xml:lang="lv">Siag izklājlapa</comment>
+ <comment xml:lang="ms">Hamparan Siag</comment>
+ <comment xml:lang="nb">Siag-regneark</comment>
+ <comment xml:lang="nl">Siag-rekenblad</comment>
+ <comment xml:lang="nn">Siag-rekneark</comment>
+ <comment xml:lang="oc">fuèlh de calcul Siag</comment>
+ <comment xml:lang="pl">Arkusz Siag</comment>
+ <comment xml:lang="pt">folha de cálculo Siag</comment>
+ <comment xml:lang="pt_BR">Planilha do Siag</comment>
+ <comment xml:lang="ro">Foaie de calcul Siag</comment>
+ <comment xml:lang="ru">Электронная таблица Siag</comment>
+ <comment xml:lang="sk">Zošit Siag</comment>
+ <comment xml:lang="sl">Preglednica Siag</comment>
+ <comment xml:lang="sq">Fletë llogaritjesh Siag</comment>
+ <comment xml:lang="sr">Сјаг табела</comment>
+ <comment xml:lang="sv">Siag-kalkylblad</comment>
+ <comment xml:lang="tr">Siag çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця Siag</comment>
+ <comment xml:lang="vi">Bảng tính Slag</comment>
+ <comment xml:lang="zh_CN">Siag 电子表格</comment>
+ <comment xml:lang="zh_TW">Siag 試算表</comment>
+ <generic-icon name="x-office-spreadsheet"/>
+ <glob pattern="*.siag"/>
+ </mime-type>
+ <mime-type type="image/x-skencil">
+ <comment>Skencil document</comment>
+ <comment xml:lang="ar">مستند Skencil</comment>
+ <comment xml:lang="ast">Documentu de Skencil</comment>
+ <comment xml:lang="be@latin">Dakument Skencil</comment>
+ <comment xml:lang="bg">Документ — Skencil</comment>
+ <comment xml:lang="ca">document Skencil</comment>
+ <comment xml:lang="cs">dokument Skencil</comment>
+ <comment xml:lang="da">Skencildokument</comment>
+ <comment xml:lang="de">Skencil-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Skencil</comment>
+ <comment xml:lang="en_GB">Skencil document</comment>
+ <comment xml:lang="eo">Skencil-dokumento</comment>
+ <comment xml:lang="es">documento de Skencil</comment>
+ <comment xml:lang="eu">Skencil dokumentua</comment>
+ <comment xml:lang="fi">Skencil-asiakirja</comment>
+ <comment xml:lang="fo">Skencil skjal</comment>
+ <comment xml:lang="fr">document Skencil</comment>
+ <comment xml:lang="ga">cáipéis Skencil</comment>
+ <comment xml:lang="gl">documento Skencil</comment>
+ <comment xml:lang="he">מסמך Skencil</comment>
+ <comment xml:lang="hr">Skencil dokument</comment>
+ <comment xml:lang="hu">Skencil-dokumentum</comment>
+ <comment xml:lang="ia">Documento Skencil</comment>
+ <comment xml:lang="id">Dokumen Skencil</comment>
+ <comment xml:lang="it">Documento Skencil</comment>
+ <comment xml:lang="ja">Skencil ドキュメント</comment>
+ <comment xml:lang="kk">Skencil құжаты</comment>
+ <comment xml:lang="ko">Skencil 문서</comment>
+ <comment xml:lang="lt">Skencil dokumentas</comment>
+ <comment xml:lang="lv">Skencil dokuments</comment>
+ <comment xml:lang="nl">Skencil-document</comment>
+ <comment xml:lang="nn">Skencil-dokument</comment>
+ <comment xml:lang="oc">document Skencil</comment>
+ <comment xml:lang="pl">Dokument Skencil</comment>
+ <comment xml:lang="pt">documento Skencil</comment>
+ <comment xml:lang="pt_BR">Documento do Skencil</comment>
+ <comment xml:lang="ro">Document Skencil</comment>
+ <comment xml:lang="ru">Документ Skencil</comment>
+ <comment xml:lang="sk">Dokument Skencil</comment>
+ <comment xml:lang="sl">Dokument Skencil</comment>
+ <comment xml:lang="sq">Dokument Skencil</comment>
+ <comment xml:lang="sr">Скенцил документ</comment>
+ <comment xml:lang="sv">Skencil-dokument</comment>
+ <comment xml:lang="tr">Skencil belgesi</comment>
+ <comment xml:lang="uk">документ Skencil</comment>
+ <comment xml:lang="vi">Tài liệu Skencil</comment>
+ <comment xml:lang="zh_CN">Skencil 文档</comment>
+ <comment xml:lang="zh_TW">Skencil 文件</comment>
+ <glob pattern="*.sk"/>
+ <glob pattern="*.sk1"/>
+ <magic priority="50">
+ <match value="##Sketch" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-slp">
+ <comment>Stampede package</comment>
+ <comment xml:lang="ar">حزمة Stampede</comment>
+ <comment xml:lang="az">Stampede paketi</comment>
+ <comment xml:lang="be@latin">Pakunak Stampede</comment>
+ <comment xml:lang="bg">Пакет — Stampede</comment>
+ <comment xml:lang="ca">paquet Stampede</comment>
+ <comment xml:lang="cs">balíček Stampede</comment>
+ <comment xml:lang="cy">Pecyn Stampede</comment>
+ <comment xml:lang="da">Stampedepakke</comment>
+ <comment xml:lang="de">Stampede-Paket</comment>
+ <comment xml:lang="el">Πακέτο Stampede</comment>
+ <comment xml:lang="en_GB">Stampede package</comment>
+ <comment xml:lang="eo">Stampede-pakaĵo</comment>
+ <comment xml:lang="es">paquete Stampede</comment>
+ <comment xml:lang="eu">Stampede paketea</comment>
+ <comment xml:lang="fi">Stampede-paketti</comment>
+ <comment xml:lang="fo">Stampede pakki</comment>
+ <comment xml:lang="fr">paquet Stampede</comment>
+ <comment xml:lang="ga">pacáiste Stampede</comment>
+ <comment xml:lang="gl">paquete Stampede</comment>
+ <comment xml:lang="he">חבילה של Stampede</comment>
+ <comment xml:lang="hr">Stampede paket</comment>
+ <comment xml:lang="hu">Stampede-csomag</comment>
+ <comment xml:lang="ia">Pacchetto Stampede</comment>
+ <comment xml:lang="id">Paket Stampede</comment>
+ <comment xml:lang="it">Pacchetto Stampede</comment>
+ <comment xml:lang="ja">Stampede パッケージ</comment>
+ <comment xml:lang="kk">Stampede дестесі</comment>
+ <comment xml:lang="ko">Stampete 패키지</comment>
+ <comment xml:lang="lt">Stampede paketas</comment>
+ <comment xml:lang="lv">Stampede pakotne</comment>
+ <comment xml:lang="ms">Pakej Stampede</comment>
+ <comment xml:lang="nb">Stampede-pakke</comment>
+ <comment xml:lang="nl">Stampede-pakket</comment>
+ <comment xml:lang="nn">Stampede-pakke</comment>
+ <comment xml:lang="oc">paquet Stampede</comment>
+ <comment xml:lang="pl">Pakiet Stampede</comment>
+ <comment xml:lang="pt">pacote Stampede</comment>
+ <comment xml:lang="pt_BR">Pacote Stampede</comment>
+ <comment xml:lang="ro">Pachet Stampede</comment>
+ <comment xml:lang="ru">Пакет Stampede</comment>
+ <comment xml:lang="sk">Balíček Stampede</comment>
+ <comment xml:lang="sl">Datoteka paketa Stampede</comment>
+ <comment xml:lang="sq">Paketë Stampede</comment>
+ <comment xml:lang="sr">Стампеде пакет</comment>
+ <comment xml:lang="sv">Stampede-paket</comment>
+ <comment xml:lang="tr">Stampede paketi</comment>
+ <comment xml:lang="uk">пакунок Stampede</comment>
+ <comment xml:lang="vi">Gói Stampede</comment>
+ <comment xml:lang="zh_CN">Stampede 软件包</comment>
+ <comment xml:lang="zh_TW">Stampede 軟體包</comment>
+ <generic-icon name="package-x-generic"/>
+ </mime-type>
+ <mime-type type="application/x-sg1000-rom">
+ <comment>SG-1000 ROM</comment>
+ <comment xml:lang="ca">ROM de SG-1000</comment>
+ <comment xml:lang="cs">ROM pro SG-1000</comment>
+ <comment xml:lang="da">SG-1000 ROM</comment>
+ <comment xml:lang="de">SG-1000 ROM</comment>
+ <comment xml:lang="en_GB">SG-1000 ROM</comment>
+ <comment xml:lang="es">ROM de SG-1000</comment>
+ <comment xml:lang="eu">SG-1000 ROM</comment>
+ <comment xml:lang="fi">SG-1000 -ROM</comment>
+ <comment xml:lang="fr">ROM SG-1000</comment>
+ <comment xml:lang="ga">ROM SG-1000</comment>
+ <comment xml:lang="hr">SG-1000 ROM</comment>
+ <comment xml:lang="hu">SG-1000 ROM</comment>
+ <comment xml:lang="id">ROM SG-1000</comment>
+ <comment xml:lang="it">ROM SG-1000</comment>
+ <comment xml:lang="kk">SG-1000 ROM</comment>
+ <comment xml:lang="ko">SG-1000 롬</comment>
+ <comment xml:lang="pl">Plik ROM konsoli SG-1000</comment>
+ <comment xml:lang="pt_BR">ROM de SG-1000</comment>
+ <comment xml:lang="ru">SG-1000 ROM</comment>
+ <comment xml:lang="sk">ROM pre SG-1000</comment>
+ <comment xml:lang="sr">СГ-1000 РОМ</comment>
+ <comment xml:lang="sv">SG-1000-rom</comment>
+ <comment xml:lang="tr">SG-1000 ROM</comment>
+ <comment xml:lang="uk">ППП SG-1000</comment>
+ <comment xml:lang="zh_CN">SG-1000 ROM</comment>
+ <comment xml:lang="zh_TW">SG-1000 ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.sg"/>
+ </mime-type>
+ <mime-type type="application/x-sms-rom">
+
+ <comment>Master System ROM</comment>
+ <comment xml:lang="ca">ROM de Master System</comment>
+ <comment xml:lang="cs">ROM pro Master System</comment>
+ <comment xml:lang="da">Master System ROM</comment>
+ <comment xml:lang="de">Master System ROM</comment>
+ <comment xml:lang="en_GB">Master System ROM</comment>
+ <comment xml:lang="es">ROM de Master System</comment>
+ <comment xml:lang="eu">Master System ROM</comment>
+ <comment xml:lang="fi">Master System -ROM</comment>
+ <comment xml:lang="fr">ROM Master System</comment>
+ <comment xml:lang="ga">ROM Master System</comment>
+ <comment xml:lang="hr">Master System ROM</comment>
+ <comment xml:lang="hu">Master System ROM</comment>
+ <comment xml:lang="id">ROM Master System</comment>
+ <comment xml:lang="it">ROM Master System</comment>
+ <comment xml:lang="kk">Master System ROM</comment>
+ <comment xml:lang="ko">마스터 시스템 롬</comment>
+ <comment xml:lang="pl">Plik ROM konsoli SMS</comment>
+ <comment xml:lang="pt_BR">ROM de Master System</comment>
+ <comment xml:lang="ru">Master System ROM</comment>
+ <comment xml:lang="sk">ROM pre Master System</comment>
+ <comment xml:lang="sr">Мастер Систем РОМ</comment>
+ <comment xml:lang="sv">Master System-rom</comment>
+ <comment xml:lang="tr">Master System ROM</comment>
+ <comment xml:lang="uk">ППП Master System</comment>
+ <comment xml:lang="zh_CN">Master System ROM</comment>
+ <comment xml:lang="zh_TW">Master System ROM</comment>
+ <generic-icon name="application-x-executable"/>
+
+ <glob pattern="*.sms"/>
+ </mime-type>
+ <mime-type type="application/x-gamegear-rom">
+ <comment>Game Gear ROM</comment>
+ <comment xml:lang="ca">ROM de Game Gear</comment>
+ <comment xml:lang="cs">ROM pro Game Gear</comment>
+ <comment xml:lang="da">Game Gear ROM</comment>
+ <comment xml:lang="de">Game Gear ROM</comment>
+ <comment xml:lang="en_GB">Game Gear ROM</comment>
+ <comment xml:lang="es">ROM de Game Gear</comment>
+ <comment xml:lang="eu">Game Gear ROM</comment>
+ <comment xml:lang="fi">Game Gear -ROM</comment>
+ <comment xml:lang="fr">ROM Game Gear</comment>
+ <comment xml:lang="ga">ROM Game Gear</comment>
+ <comment xml:lang="hr">Game Gear ROM</comment>
+ <comment xml:lang="hu">Game Gear ROM</comment>
+ <comment xml:lang="id">ROM Game Gear</comment>
+ <comment xml:lang="it">ROM Game Gear</comment>
+ <comment xml:lang="kk">Game Gear ROM</comment>
+ <comment xml:lang="ko">게임 기어 롬</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Game Gear</comment>
+ <comment xml:lang="pt_BR">ROM de Game Gear</comment>
+ <comment xml:lang="ru">Game Gear ROM</comment>
+ <comment xml:lang="sk">ROM pre Game Gear</comment>
+ <comment xml:lang="sr">Гејм Гир РОМ</comment>
+ <comment xml:lang="sv">Game Gear-rom</comment>
+ <comment xml:lang="tr">Game Gear ROM</comment>
+ <comment xml:lang="uk">ППП Game Gear</comment>
+ <comment xml:lang="zh_CN">Game Gear ROM</comment>
+ <comment xml:lang="zh_TW">Game Gear ROM</comment>
+ <generic-icon name="application-x-executable"/>
+
+ <glob pattern="*.gg"/>
+ </mime-type>
+ <mime-type type="application/vnd.nintendo.snes.rom">
+
+ <comment>Super NES ROM</comment>
+ <comment xml:lang="ar">Super NES ROM</comment>
+ <comment xml:lang="be@latin">Super Nintendo ROM</comment>
+ <comment xml:lang="bg">ROM — Super NES</comment>
+ <comment xml:lang="ca">ROM de Super NES</comment>
+ <comment xml:lang="cs">ROM pro Super Nintendo</comment>
+ <comment xml:lang="da">Super NES-rom</comment>
+ <comment xml:lang="de">Super NES ROM</comment>
+ <comment xml:lang="el">Super NES ROM</comment>
+ <comment xml:lang="en_GB">Super NES ROM</comment>
+ <comment xml:lang="es">ROM de Super NES</comment>
+ <comment xml:lang="eu">Super Nintendo-ko ROMa</comment>
+ <comment xml:lang="fi">Super Nintendo -ROM</comment>
+ <comment xml:lang="fo">Super NES ROM</comment>
+ <comment xml:lang="fr">ROM Super Nintendo</comment>
+ <comment xml:lang="ga">ROM Super NES</comment>
+ <comment xml:lang="gl">ROM de Super NES</comment>
+ <comment xml:lang="he">ROM של Super NES</comment>
+ <comment xml:lang="hr">Super NES ROM</comment>
+ <comment xml:lang="hu">Super NES ROM</comment>
+ <comment xml:lang="ia">ROM pro Super Nintendo</comment>
+ <comment xml:lang="id">Memori baca-saja Super Nintendo</comment>
+ <comment xml:lang="it">ROM Super Nintendo</comment>
+ <comment xml:lang="ja">スーパーファミコン ROM</comment>
+ <comment xml:lang="kk">Super NES ROM</comment>
+ <comment xml:lang="ko">수퍼 NES 롬</comment>
+ <comment xml:lang="lt">Super NES ROM</comment>
+ <comment xml:lang="lv">Super NES ROM</comment>
+ <comment xml:lang="nb">Super Nintendo ROM</comment>
+ <comment xml:lang="nl">Super Nintendo</comment>
+ <comment xml:lang="nn">Super NES-ROM</comment>
+ <comment xml:lang="oc">ROM Super Nintendo</comment>
+ <comment xml:lang="pl">Plik ROM konsoli SNES</comment>
+ <comment xml:lang="pt">ROM Super Nintendo</comment>
+ <comment xml:lang="pt_BR">ROM de Super Nintendo</comment>
+ <comment xml:lang="ro">ROM Super Nintendo</comment>
+ <comment xml:lang="ru">Super NES ROM</comment>
+ <comment xml:lang="sk">ROM pre Super Nintendo</comment>
+ <comment xml:lang="sl">Bralni pomnilnik Super NES</comment>
+ <comment xml:lang="sq">ROM Super NES</comment>
+ <comment xml:lang="sr">Супер НЕС РОМ</comment>
+ <comment xml:lang="sv">Super NES-rom</comment>
+ <comment xml:lang="tr">Super NES ROM</comment>
+ <comment xml:lang="uk">ППП Super NES</comment>
+ <comment xml:lang="vi">ROM Super Nintendo</comment>
+ <comment xml:lang="zh_CN">Super NES ROM</comment>
+ <comment xml:lang="zh_TW">超級任天堂 ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <alias type="application/x-snes-rom"/>
+ <glob pattern="*.sfc"/>
+ <glob pattern="*.smc"/>
+ </mime-type>
+ <mime-type type="application/x-stuffit">
+ <comment>StuffIt archive</comment>
+ <comment xml:lang="ar">أرشيف StuffIt</comment>
+ <comment xml:lang="be@latin">Archiŭ StuffIt</comment>
+ <comment xml:lang="bg">Архив — StuffIt</comment>
+ <comment xml:lang="ca">arxiu StuffIt</comment>
+ <comment xml:lang="cs">archiv StuffIt</comment>
+ <comment xml:lang="da">StuffIt-arkiv</comment>
+ <comment xml:lang="de">StuffIt-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο StuffIt</comment>
+ <comment xml:lang="en_GB">StuffIt archive</comment>
+ <comment xml:lang="eo">StuffIt-arkivo</comment>
+ <comment xml:lang="es">archivador de StuffIt</comment>
+ <comment xml:lang="eu">StuffIt artxiboa</comment>
+ <comment xml:lang="fi">StuffIt-arkisto</comment>
+ <comment xml:lang="fo">StuffIt skjalasavn</comment>
+ <comment xml:lang="fr">archive StuffIt</comment>
+ <comment xml:lang="ga">cartlann StuffIt</comment>
+ <comment xml:lang="gl">arquivo StuffIt</comment>
+ <comment xml:lang="he">ארכיון של StuffIt</comment>
+ <comment xml:lang="hr">StuffIt arhiva</comment>
+ <comment xml:lang="hu">StuffIt-archívum</comment>
+ <comment xml:lang="ia">Archivo StuffIt</comment>
+ <comment xml:lang="id">Arsip StuffIt</comment>
+ <comment xml:lang="it">Archivio StuffIt</comment>
+ <comment xml:lang="ja">StuffIt アーカイブ</comment>
+ <comment xml:lang="kk">StuffIt архиві</comment>
+ <comment xml:lang="ko">StuffIt 압축 파일</comment>
+ <comment xml:lang="lt">StuffIt archyvas</comment>
+ <comment xml:lang="lv">StuffIt arhīvs</comment>
+ <comment xml:lang="nb">StuffIt arkiv</comment>
+ <comment xml:lang="nl">StuffIt-archief</comment>
+ <comment xml:lang="nn">StuffIt-arkiv</comment>
+ <comment xml:lang="oc">archiu StuffIt</comment>
+ <comment xml:lang="pl">Archiwum StuffIt</comment>
+ <comment xml:lang="pt">arquivo StuffIt</comment>
+ <comment xml:lang="pt_BR">Pacote StuffIt</comment>
+ <comment xml:lang="ro">Arhivă StuffIt</comment>
+ <comment xml:lang="ru">Архив StuffIt</comment>
+ <comment xml:lang="sk">Archív StuffIt</comment>
+ <comment xml:lang="sl">Datoteka arhiva StuffIt</comment>
+ <comment xml:lang="sq">Arkiv StuffIt</comment>
+ <comment xml:lang="sr">Стаф Ит архива</comment>
+ <comment xml:lang="sv">StuffIt-arkiv </comment>
+ <comment xml:lang="tr">StuffIt arşivi</comment>
+ <comment xml:lang="uk">архів StuffIt</comment>
+ <comment xml:lang="vi">Kho nén Stuffit</comment>
+ <comment xml:lang="zh_CN">Macintosh StuffIt 归档文件</comment>
+ <comment xml:lang="zh_TW">StuffIt 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <alias type="application/stuffit"/>
+ <alias type="application/x-sit"/>
+ <magic priority="60">
+ <match value="StuffIt " type="string" offset="0"/>
+ <match value="SIT!" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.sit"/>
+ </mime-type>
+ <mime-type type="application/x-subrip">
+ <comment>SubRip subtitles</comment>
+ <comment xml:lang="ar">ترجمات SubRip</comment>
+ <comment xml:lang="be@latin">Subtytry SubRip</comment>
+ <comment xml:lang="bg">Субтитри — SubRip</comment>
+ <comment xml:lang="ca">subtítols SubRip</comment>
+ <comment xml:lang="cs">titulky SubRip</comment>
+ <comment xml:lang="da">SubRip-undertekster</comment>
+ <comment xml:lang="de">SubRip-Untertitel</comment>
+ <comment xml:lang="el">Υπότιτλοι SubRip</comment>
+ <comment xml:lang="en_GB">SubRip subtitles</comment>
+ <comment xml:lang="eo">SubRip-subtekstoj</comment>
+ <comment xml:lang="es">subtítulos SubRip</comment>
+ <comment xml:lang="eu">SubRip azpitituluak</comment>
+ <comment xml:lang="fi">SubRip-tekstitykset</comment>
+ <comment xml:lang="fo">SubRip undirtekstir</comment>
+ <comment xml:lang="fr">sous-titres SubRip</comment>
+ <comment xml:lang="ga">fotheidil SubRip</comment>
+ <comment xml:lang="gl">subtítulos SubRip</comment>
+ <comment xml:lang="he">כתוביות של SubRip</comment>
+ <comment xml:lang="hr">SubRip podnaslovi</comment>
+ <comment xml:lang="hu">SubRip feliratok</comment>
+ <comment xml:lang="ia">Subtitulos SubRip</comment>
+ <comment xml:lang="id">Subjudul SubRip</comment>
+ <comment xml:lang="it">Sottotitoli SubRip</comment>
+ <comment xml:lang="ja">SubRip 字幕</comment>
+ <comment xml:lang="kk">SubRip субтитрлары</comment>
+ <comment xml:lang="ko">SubRip 자막 파일</comment>
+ <comment xml:lang="lt">SubRip subtitrai</comment>
+ <comment xml:lang="lv">SubRip subtitri</comment>
+ <comment xml:lang="nb">SubRip undertekst</comment>
+ <comment xml:lang="nl">SubRip-ondertitels</comment>
+ <comment xml:lang="nn">SubRip-teksting</comment>
+ <comment xml:lang="oc">sostítols SubRip</comment>
+ <comment xml:lang="pl">Napisy SubRip</comment>
+ <comment xml:lang="pt">legendas SubRip</comment>
+ <comment xml:lang="pt_BR">Legendas SubRip</comment>
+ <comment xml:lang="ro">Subtitrare SubRip</comment>
+ <comment xml:lang="ru">Субтитры SubRip</comment>
+ <comment xml:lang="sk">Titulky SubRip</comment>
+ <comment xml:lang="sl">Datoteka podnapisov SubRip</comment>
+ <comment xml:lang="sq">Nëntituj SubRip</comment>
+ <comment xml:lang="sr">Суб Рип преводи</comment>
+ <comment xml:lang="sv">SubRip-undertexter</comment>
+ <comment xml:lang="tr">SubRip altyazıları</comment>
+ <comment xml:lang="uk">субтитри SubRip</comment>
+ <comment xml:lang="vi">Phụ đề SubRip</comment>
+ <comment xml:lang="zh_CN">SubRip 字幕</comment>
+ <comment xml:lang="zh_TW">SubRip 字幕</comment>
+ <alias type="application/x-srt"/>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <magic priority="50">
+ <match value="1" type="string" offset="0">
+ <match value=" --> " type="string" offset="0:256"/>
+ </match>
+ </magic>
+ <glob pattern="*.srt"/>
+ </mime-type>
+ <mime-type type="text/vtt">
+ <comment>WebVTT subtitles</comment>
+ <comment xml:lang="bg">Субтитри — WebVTT</comment>
+ <comment xml:lang="ca">subtítols WebVTT</comment>
+ <comment xml:lang="cs">titulky WebVTT</comment>
+ <comment xml:lang="da">WebVTT-undertekster</comment>
+ <comment xml:lang="de">WebVTT-Untertitel</comment>
+ <comment xml:lang="el">Υπότιτλοι WebVTT</comment>
+ <comment xml:lang="en_GB">WebVTT subtitles</comment>
+ <comment xml:lang="es">subtítulos WebVTT</comment>
+ <comment xml:lang="eu">WebVTT azpitituluak</comment>
+ <comment xml:lang="fi">WebVTT-tekstitykset</comment>
+ <comment xml:lang="fr">sous-titres WebVTT</comment>
+ <comment xml:lang="ga">fotheidil WebVTT</comment>
+ <comment xml:lang="gl">subtítulos WebVTT</comment>
+ <comment xml:lang="he">כתוביות WebVTT</comment>
+ <comment xml:lang="hr">WebVTT podnaslovi</comment>
+ <comment xml:lang="hu">WebVTT feliratok</comment>
+ <comment xml:lang="ia">Subtitulos WebVTT</comment>
+ <comment xml:lang="id">Subjudul WebVTT</comment>
+ <comment xml:lang="it">Sottotitoli WebVTT</comment>
+ <comment xml:lang="ja">WebVTT サブタイトル</comment>
+ <comment xml:lang="ka">WebVTT ქვეტიტრები</comment>
+ <comment xml:lang="kk">WebVTT субтитрлары</comment>
+ <comment xml:lang="ko">WebVTT 자막</comment>
+ <comment xml:lang="lv">WebVTT subtitri</comment>
+ <comment xml:lang="nl">WebVTT ondertitels</comment>
+ <comment xml:lang="oc">sostítols WebVTT</comment>
+ <comment xml:lang="pl">Napisy WebVTT</comment>
+ <comment xml:lang="pt">legendas WebVTT</comment>
+ <comment xml:lang="pt_BR">Legendas WebVTT</comment>
+ <comment xml:lang="ru">Субтитры WebVTT</comment>
+ <comment xml:lang="sk">Titulky WebVTT</comment>
+ <comment xml:lang="sl">Podnapisi WebVTT</comment>
+ <comment xml:lang="sr">Веб ВТТ преводи</comment>
+ <comment xml:lang="sv">WebVTT-undertexter</comment>
+ <comment xml:lang="tr">WebVTT altyazıları</comment>
+ <comment xml:lang="uk">субтитри WebVTT</comment>
+ <comment xml:lang="zh_CN">WebVTT 字幕</comment>
+ <comment xml:lang="zh_TW">WebVTT 字幕</comment>
+ <acronym>VTT</acronym>
+ <expanded-acronym>Video Text Tracks</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <magic priority="50">
+ <match value="WEBVTT" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.vtt"/>
+ </mime-type>
+ <mime-type type="application/x-sami">
+ <comment>SAMI subtitles</comment>
+ <comment xml:lang="ar">ترجمات SAMI</comment>
+ <comment xml:lang="be@latin">Subtytry SAMI</comment>
+ <comment xml:lang="bg">Субтитри — SAMI</comment>
+ <comment xml:lang="ca">subtítols SAMI</comment>
+ <comment xml:lang="cs">titulky SAMI</comment>
+ <comment xml:lang="da">SAMI-undertekster</comment>
+ <comment xml:lang="de">SAMI-Untertitel</comment>
+ <comment xml:lang="el">Υπότιτλοι SAMI</comment>
+ <comment xml:lang="en_GB">SAMI subtitles</comment>
+ <comment xml:lang="eo">SAMI-subtekstoj</comment>
+ <comment xml:lang="es">subtítulos SAMI</comment>
+ <comment xml:lang="eu">SAMI azpitituluak</comment>
+ <comment xml:lang="fi">SAMI-tekstitykset</comment>
+ <comment xml:lang="fo">SAMI undirtekstir</comment>
+ <comment xml:lang="fr">sous-titres SAMI</comment>
+ <comment xml:lang="ga">fotheidil SAMI</comment>
+ <comment xml:lang="gl">subtítulos SAMI</comment>
+ <comment xml:lang="he">כתוביות SAMI</comment>
+ <comment xml:lang="hr">SAMI podnaslovi</comment>
+ <comment xml:lang="hu">SAMI feliratok</comment>
+ <comment xml:lang="ia">Subtitulos SAMI</comment>
+ <comment xml:lang="id">Subjudul SAMI</comment>
+ <comment xml:lang="it">Sottotitoli SAMI</comment>
+ <comment xml:lang="ja">SAMI 字幕</comment>
+ <comment xml:lang="kk">SAMI субтитрлары</comment>
+ <comment xml:lang="ko">SAMI 자막 파일</comment>
+ <comment xml:lang="lt">SAMI subtitrai</comment>
+ <comment xml:lang="lv">SAMI subtitri</comment>
+ <comment xml:lang="nb">SAMI undertekst</comment>
+ <comment xml:lang="nl">SAMI-ondertitels</comment>
+ <comment xml:lang="nn">SAMI teksting</comment>
+ <comment xml:lang="oc">sostítols SAMI</comment>
+ <comment xml:lang="pl">Napisy SAMI</comment>
+ <comment xml:lang="pt">legendas SAMI</comment>
+ <comment xml:lang="pt_BR">Legendas SAMI</comment>
+ <comment xml:lang="ro">Subtitrări SAMI</comment>
+ <comment xml:lang="ru">Субтитры SAMI</comment>
+ <comment xml:lang="sk">Titulky SAMI</comment>
+ <comment xml:lang="sl">Datoteka podnapisov SAMI</comment>
+ <comment xml:lang="sq">Nëntituj SAMI</comment>
+ <comment xml:lang="sr">САМИ преводи</comment>
+ <comment xml:lang="sv">SAMI-undertexter</comment>
+ <comment xml:lang="tr">SAMI altyazıları</comment>
+ <comment xml:lang="uk">субтитри SAMI</comment>
+ <comment xml:lang="vi">Phụ đề SAMI</comment>
+ <comment xml:lang="zh_CN">SAMI 字幕</comment>
+ <comment xml:lang="zh_TW">SAMI 字幕</comment>
+ <acronym>SAMI</acronym>
+ <expanded-acronym>Synchronized Accessible Media Interchange</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <magic priority="50">
+ <match value="&lt;SAMI&gt;" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.smi"/>
+ <glob pattern="*.sami"/>
+ </mime-type>
+ <mime-type type="text/x-microdvd">
+ <comment>MicroDVD subtitles</comment>
+ <comment xml:lang="ar">ترجمات MicroDVD</comment>
+ <comment xml:lang="be@latin">Subtytry MicroDVD</comment>
+ <comment xml:lang="bg">Субтитри — MicroDVD</comment>
+ <comment xml:lang="ca">subtítols MicroDVD</comment>
+ <comment xml:lang="cs">titulky MicroDVD</comment>
+ <comment xml:lang="da">MicroDVD-undertekster</comment>
+ <comment xml:lang="de">MicroDVD-Untertitel</comment>
+ <comment xml:lang="el">Υπότιτλοι MicroDVD</comment>
+ <comment xml:lang="en_GB">MicroDVD subtitles</comment>
+ <comment xml:lang="eo">MicroDVD-subtekstoj</comment>
+ <comment xml:lang="es">subtítulos de MicroDVD</comment>
+ <comment xml:lang="eu">MicroDVD azpitituluak</comment>
+ <comment xml:lang="fi">MicroDVD-tekstitykset</comment>
+ <comment xml:lang="fo">MicroDVD undirtekstir</comment>
+ <comment xml:lang="fr">sous-titres MicroDVD</comment>
+ <comment xml:lang="ga">fotheidil MicroDVD</comment>
+ <comment xml:lang="gl">subtítulos de MicroDVD</comment>
+ <comment xml:lang="he">כתוביות של MicroDVD</comment>
+ <comment xml:lang="hr">MicroDVD podnaslovi</comment>
+ <comment xml:lang="hu">MicroDVD feliratok</comment>
+ <comment xml:lang="ia">Subtitulos MicroDVD</comment>
+ <comment xml:lang="id">Subjudul MicroDVD</comment>
+ <comment xml:lang="it">Sottotitoli MicroDVD</comment>
+ <comment xml:lang="ja">MicroDVD 字幕</comment>
+ <comment xml:lang="ka">MicroDVD-ის ქვეტიტრები</comment>
+ <comment xml:lang="kk">MicroDVD субтитрлары</comment>
+ <comment xml:lang="ko">MicroDVD 자막 파일</comment>
+ <comment xml:lang="lt">MicroDVD subtitrai</comment>
+ <comment xml:lang="lv">MicroDVD subtitri</comment>
+ <comment xml:lang="nb">MicroDVD undertekst</comment>
+ <comment xml:lang="nl">MicroDVD-ondertitels</comment>
+ <comment xml:lang="nn">MicroDVD-teksting</comment>
+ <comment xml:lang="oc">sostítols MicroDVD</comment>
+ <comment xml:lang="pl">Napisy MicroDVD</comment>
+ <comment xml:lang="pt">legendas MicroDVD</comment>
+ <comment xml:lang="pt_BR">Legendas MicroDVD</comment>
+ <comment xml:lang="ro">Subtitrări MicroDVD</comment>
+ <comment xml:lang="ru">Субтитры MicroDVD</comment>
+ <comment xml:lang="sk">Titulky MicroDVD</comment>
+ <comment xml:lang="sl">Datoteka podnapisov MicroDVD</comment>
+ <comment xml:lang="sq">Nëntituj MicroDVD</comment>
+ <comment xml:lang="sr">Микро ДВД преводи</comment>
+ <comment xml:lang="sv">MicroDVD-undertexter</comment>
+ <comment xml:lang="tr">MicroDVD altyazısı</comment>
+ <comment xml:lang="uk">субтитри MicroDVD</comment>
+ <comment xml:lang="vi">Phụ đề MicroDVD</comment>
+ <comment xml:lang="zh_CN">MicroDVD 字幕</comment>
+ <comment xml:lang="zh_TW">MicroDVD 字幕</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="{1}" type="string" offset="0"/>
+ <match value="{0}" type="string" offset="0"/>
+ <match value="}{" type="string" offset="0:6"/>
+ </magic>
+ <glob pattern="*.sub"/>
+ </mime-type>
+ <mime-type type="text/x-mpsub">
+ <comment>MPSub subtitles</comment>
+ <comment xml:lang="ar">ترجمات MPSub</comment>
+ <comment xml:lang="be@latin">Subtytry MPSub</comment>
+ <comment xml:lang="bg">Субтитри — MPSub</comment>
+ <comment xml:lang="ca">subtítols MPSub</comment>
+ <comment xml:lang="cs">titulky MPSub</comment>
+ <comment xml:lang="da">MPSub-undertekster</comment>
+ <comment xml:lang="de">MPSub-Untertitel</comment>
+ <comment xml:lang="el">Υπότιτλοι MPSub</comment>
+ <comment xml:lang="en_GB">MPSub subtitles</comment>
+ <comment xml:lang="eo">MPSub-subtekstoj</comment>
+ <comment xml:lang="es">subtítulos MPSub</comment>
+ <comment xml:lang="eu">MPSub azpitituluak</comment>
+ <comment xml:lang="fi">MPSub-tekstitykset</comment>
+ <comment xml:lang="fo">MPSub undirtekstir</comment>
+ <comment xml:lang="fr">sous-titres MPSub</comment>
+ <comment xml:lang="ga">fotheidil MPSub</comment>
+ <comment xml:lang="gl">subtítulos MPSub</comment>
+ <comment xml:lang="he">כתוביות MPSub</comment>
+ <comment xml:lang="hr">MPSub podnaslovi</comment>
+ <comment xml:lang="hu">MPSub feliratok</comment>
+ <comment xml:lang="ia">Subtitulos MPSub</comment>
+ <comment xml:lang="id">Subjudul MPSub</comment>
+ <comment xml:lang="it">Sottotitoli MPSub</comment>
+ <comment xml:lang="ja">MPSub サブタイトル</comment>
+ <comment xml:lang="ka">MPSub ქვეტიტრები</comment>
+ <comment xml:lang="kk">MPSub субтитрлары</comment>
+ <comment xml:lang="ko">MPSub 자막 파일</comment>
+ <comment xml:lang="lt">MPSub subtitrai</comment>
+ <comment xml:lang="lv">MPSub subtitri</comment>
+ <comment xml:lang="nb">MPSub undertekst</comment>
+ <comment xml:lang="nl">MPSub-ondertitels</comment>
+ <comment xml:lang="nn">MPSub-undertekstar</comment>
+ <comment xml:lang="oc">sostítols MPSub</comment>
+ <comment xml:lang="pl">Napisy MPSub</comment>
+ <comment xml:lang="pt">legendas MPSub</comment>
+ <comment xml:lang="pt_BR">Legendas MPSub</comment>
+ <comment xml:lang="ro">Subtitrări MPSub</comment>
+ <comment xml:lang="ru">Субтитры MPSub</comment>
+ <comment xml:lang="sk">Titulky MPSub</comment>
+ <comment xml:lang="sl">Datoteka podnapisov MPSub</comment>
+ <comment xml:lang="sq">Nëntituj MPSub</comment>
+ <comment xml:lang="sr">МПСуб преводи</comment>
+ <comment xml:lang="sv">MPSub-undertexter</comment>
+ <comment xml:lang="tr">MPSub altyazıları</comment>
+ <comment xml:lang="uk">субтитри MPSub</comment>
+ <comment xml:lang="vi">Phụ đề MPSub</comment>
+ <comment xml:lang="zh_CN">MPSub 字幕</comment>
+ <comment xml:lang="zh_TW">MPSub 字幕</comment>
+ <acronym>MPSub</acronym>
+ <expanded-acronym>MPlayer Subtitle</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="FORMAT=" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.sub"/>
+ </mime-type>
+ <mime-type type="text/x-ssa">
+ <comment>SSA subtitles</comment>
+ <comment xml:lang="ar">ترجمات SSA</comment>
+ <comment xml:lang="be@latin">Subtytry SSA</comment>
+ <comment xml:lang="bg">Субтитри — SSA</comment>
+ <comment xml:lang="ca">subtítols SSA</comment>
+ <comment xml:lang="cs">titulky SSA</comment>
+ <comment xml:lang="da">SSA-undertekster</comment>
+ <comment xml:lang="de">SSA-Untertitel</comment>
+ <comment xml:lang="el">Υπότιτλοι SSA</comment>
+ <comment xml:lang="en_GB">SSA subtitles</comment>
+ <comment xml:lang="eo">SSA-subtekstoj</comment>
+ <comment xml:lang="es">subtítulos SSA</comment>
+ <comment xml:lang="eu">SSA azpitituluak</comment>
+ <comment xml:lang="fi">SSA-tekstitykset</comment>
+ <comment xml:lang="fo">SSA undirtekstir</comment>
+ <comment xml:lang="fr">sous-titres SSA</comment>
+ <comment xml:lang="ga">fotheidil SSA</comment>
+ <comment xml:lang="gl">Subtitulos SSA</comment>
+ <comment xml:lang="he">כתובית SSA</comment>
+ <comment xml:lang="hr">SSA podnaslovi</comment>
+ <comment xml:lang="hu">SSA feliratok</comment>
+ <comment xml:lang="ia">Subtitulos SSA</comment>
+ <comment xml:lang="id">Subjudul SSA</comment>
+ <comment xml:lang="it">Sottotitoli SSA</comment>
+ <comment xml:lang="ja">SSA 字幕</comment>
+ <comment xml:lang="kk">SSA субтитрлары</comment>
+ <comment xml:lang="ko">SSA 자막 파일</comment>
+ <comment xml:lang="lt">SSA subtitrai</comment>
+ <comment xml:lang="lv">SSA subtitri</comment>
+ <comment xml:lang="nb">SSA undertekst</comment>
+ <comment xml:lang="nl">SSA-ondertitels</comment>
+ <comment xml:lang="nn">SSA-teksting</comment>
+ <comment xml:lang="oc">sostítols SSA</comment>
+ <comment xml:lang="pl">Napisy SSA</comment>
+ <comment xml:lang="pt">legendas SSA</comment>
+ <comment xml:lang="pt_BR">Legendas SSA</comment>
+ <comment xml:lang="ro">Subtitrări SSA</comment>
+ <comment xml:lang="ru">Субтитры SSA</comment>
+ <comment xml:lang="sk">Titulky SSA</comment>
+ <comment xml:lang="sl">Datoteka podnapisov SSA</comment>
+ <comment xml:lang="sq">Nëntituj SSA</comment>
+ <comment xml:lang="sr">ССА преводи</comment>
+ <comment xml:lang="sv">SSA-undertexter</comment>
+ <comment xml:lang="tr">SSA altyazıları</comment>
+ <comment xml:lang="uk">субтитри SSA</comment>
+ <comment xml:lang="vi">Phụ đề SSA</comment>
+ <comment xml:lang="zh_CN">SSA 字幕</comment>
+ <comment xml:lang="zh_TW">SSA 字幕</comment>
+ <acronym>SSA</acronym>
+ <expanded-acronym>SubStation Alpha</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="[Script Info]" type="string" offset="0:256"/>
+ <match value="Dialogue: " type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.ssa"/>
+ <glob pattern="*.ass"/>
+ </mime-type>
+ <mime-type type="text/x-subviewer">
+ <comment>SubViewer subtitles</comment>
+ <comment xml:lang="ar">ترجمات SubViewer</comment>
+ <comment xml:lang="be@latin">Subtytry SubViewer</comment>
+ <comment xml:lang="bg">Субтитри — SubViewer</comment>
+ <comment xml:lang="ca">subtítols SubViewer</comment>
+ <comment xml:lang="cs">titulky SubViewer</comment>
+ <comment xml:lang="da">SubViewer-undertekster</comment>
+ <comment xml:lang="de">SubViewer-Untertitel</comment>
+ <comment xml:lang="el">Υπότιτλοι SubViewer</comment>
+ <comment xml:lang="en_GB">SubViewer subtitles</comment>
+ <comment xml:lang="eo">SubViewer-subtekstoj</comment>
+ <comment xml:lang="es">subtítulos SubViewer</comment>
+ <comment xml:lang="eu">SubViewer azpitituluak</comment>
+ <comment xml:lang="fi">SubViewer-tekstitykset</comment>
+ <comment xml:lang="fo">SubViewer undirtekstir</comment>
+ <comment xml:lang="fr">sous-titres SubViewer</comment>
+ <comment xml:lang="ga">fotheidil SubViewer</comment>
+ <comment xml:lang="gl">subtítulos SubViewer</comment>
+ <comment xml:lang="he">כתוביות של SubViewer</comment>
+ <comment xml:lang="hr">SubViewer podnaslovi</comment>
+ <comment xml:lang="hu">SubViewer feliratok</comment>
+ <comment xml:lang="ia">Subtitulos SubViewer</comment>
+ <comment xml:lang="id">Subjudul SubViewer</comment>
+ <comment xml:lang="it">Sottotitoli SubViewer</comment>
+ <comment xml:lang="ja">SubViewer 字幕</comment>
+ <comment xml:lang="kk">SubViewer субтитрлары</comment>
+ <comment xml:lang="ko">SubViewer 자막 파일</comment>
+ <comment xml:lang="lt">SubViewer subtitrai</comment>
+ <comment xml:lang="lv">SubViewer subtitri</comment>
+ <comment xml:lang="nb">SubViewer undertekst</comment>
+ <comment xml:lang="nl">SubViewer-ondertitels</comment>
+ <comment xml:lang="nn">SubViewer-teksting</comment>
+ <comment xml:lang="oc">sostítols SubViewer</comment>
+ <comment xml:lang="pl">Napisy SubViewer</comment>
+ <comment xml:lang="pt">legendas SubViewer</comment>
+ <comment xml:lang="pt_BR">Legendas SubViewer</comment>
+ <comment xml:lang="ro">Subtitrare SubViewer</comment>
+ <comment xml:lang="ru">Субтитры SubViewer</comment>
+ <comment xml:lang="sk">Titulky SubViewer</comment>
+ <comment xml:lang="sl">Datoteka podnapisov SubViewer</comment>
+ <comment xml:lang="sq">Nëntituj SubViewer</comment>
+ <comment xml:lang="sr">Суб Вјивер преводи</comment>
+ <comment xml:lang="sv">SubViewer-undertexter</comment>
+ <comment xml:lang="tr">SubViewer altyazıları</comment>
+ <comment xml:lang="uk">субтитри SubViewer</comment>
+ <comment xml:lang="vi">Phụ đề SubViewer</comment>
+ <comment xml:lang="zh_CN">SubViewer 字幕</comment>
+ <comment xml:lang="zh_TW">SubViewer 字幕</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="[INFORMATION]" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.sub"/>
+ </mime-type>
+ <mime-type type="text/x-iMelody">
+ <comment>iMelody ringtone</comment>
+ <comment xml:lang="ar">نغمة iMelody</comment>
+ <comment xml:lang="be@latin">Rington iMelody</comment>
+ <comment xml:lang="bg">Аудио — iMelody</comment>
+ <comment xml:lang="ca">to de trucada iMelody</comment>
+ <comment xml:lang="cs">vyzváněcí melodie iMelody</comment>
+ <comment xml:lang="da">iMelody-ringetone</comment>
+ <comment xml:lang="de">iMelody-Klingelton</comment>
+ <comment xml:lang="el">ringtone iMelody</comment>
+ <comment xml:lang="en_GB">iMelody ringtone</comment>
+ <comment xml:lang="es">tono de llamada iMelody</comment>
+ <comment xml:lang="eu">iMelody doinua</comment>
+ <comment xml:lang="fi">iMelody-soittoääni</comment>
+ <comment xml:lang="fo">iMelody ringitóni</comment>
+ <comment xml:lang="fr">sonnerie iMelody</comment>
+ <comment xml:lang="ga">ton buailte iMelody</comment>
+ <comment xml:lang="gl">Melodía de iMelody</comment>
+ <comment xml:lang="he">צלצול של iMelody</comment>
+ <comment xml:lang="hr">iMelody melodija zvona</comment>
+ <comment xml:lang="hu">iMelody csengőhang</comment>
+ <comment xml:lang="ia">Tono de appello iMelody</comment>
+ <comment xml:lang="id">nada dering iMelody</comment>
+ <comment xml:lang="it">Suoneria iMelody</comment>
+ <comment xml:lang="ja">iMelody リングトーン</comment>
+ <comment xml:lang="kk">iMelody әуені</comment>
+ <comment xml:lang="ko">iMelody 벨소리</comment>
+ <comment xml:lang="lt">iMelody skambučio melodija</comment>
+ <comment xml:lang="lv">iMelody melodija</comment>
+ <comment xml:lang="nb">iMelody ringetone</comment>
+ <comment xml:lang="nl">iMelody-beltoon</comment>
+ <comment xml:lang="nn">iMelody-ringetone</comment>
+ <comment xml:lang="oc">sonariá iMelody</comment>
+ <comment xml:lang="pl">Dzwonek iMelody</comment>
+ <comment xml:lang="pt">toque iMelody</comment>
+ <comment xml:lang="pt_BR">Toque de celular do iMelody</comment>
+ <comment xml:lang="ro">Sonerie iMelody</comment>
+ <comment xml:lang="ru">Мелодия iMelody</comment>
+ <comment xml:lang="sk">Vyzváňacie melódie iMelody</comment>
+ <comment xml:lang="sl">Zvonjenje iMelody</comment>
+ <comment xml:lang="sq">Zile iMelody</comment>
+ <comment xml:lang="sr">звоно ајМелодије</comment>
+ <comment xml:lang="sv">iMelody-ringsignal</comment>
+ <comment xml:lang="tr">iMelody melodisi</comment>
+ <comment xml:lang="uk">рінгтон iMelody</comment>
+ <comment xml:lang="vi">tiếng réo iMelody</comment>
+ <comment xml:lang="zh_CN">iMelody 铃声</comment>
+ <comment xml:lang="zh_TW">iMelody 鈴聲</comment>
+ <magic priority="50">
+ <match value="BEGIN:IMELODY" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.imy"/>
+ <glob pattern="*.ime"/>
+ <alias type="audio/x-iMelody"/>
+ <alias type="audio/iMelody"/>
+ </mime-type>
+ <mime-type type="application/x-smaf">
+ <comment>SMAF audio</comment>
+ <comment xml:lang="ar">SMAF سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo SMAF</comment>
+ <comment xml:lang="bg">Аудио — SMAF</comment>
+ <comment xml:lang="ca">àudio SMAF</comment>
+ <comment xml:lang="cs">zvuk SMAF</comment>
+ <comment xml:lang="da">SMAF-lyd</comment>
+ <comment xml:lang="de">SMAF-Audio</comment>
+ <comment xml:lang="el">Ήχος SMAF</comment>
+ <comment xml:lang="en_GB">SMAF audio</comment>
+ <comment xml:lang="eo">SMAF-sondosiero</comment>
+ <comment xml:lang="es">sonido SMAF</comment>
+ <comment xml:lang="eu">SMAF audioa</comment>
+ <comment xml:lang="fi">SMAF-ääni</comment>
+ <comment xml:lang="fo">SMAF ljóður</comment>
+ <comment xml:lang="fr">audio SMAF</comment>
+ <comment xml:lang="ga">fuaim SMAF</comment>
+ <comment xml:lang="gl">son SMAF</comment>
+ <comment xml:lang="he">שמע SMAF</comment>
+ <comment xml:lang="hr">SMAF zvučni zapis</comment>
+ <comment xml:lang="hu">SMAF hang</comment>
+ <comment xml:lang="ia">Audio SMAF</comment>
+ <comment xml:lang="id">Audio SMAF</comment>
+ <comment xml:lang="it">Audio SMAF</comment>
+ <comment xml:lang="ja">SMAF オーディオ</comment>
+ <comment xml:lang="kk">SMAF аудиосы</comment>
+ <comment xml:lang="ko">SMAF 오디오</comment>
+ <comment xml:lang="lt">SMAF garso įrašas</comment>
+ <comment xml:lang="lv">SMAF audio</comment>
+ <comment xml:lang="nb">SMAF-lyd</comment>
+ <comment xml:lang="nl">SMAF-audio</comment>
+ <comment xml:lang="nn">SMAF-lyd</comment>
+ <comment xml:lang="oc">àudio SMAF</comment>
+ <comment xml:lang="pl">Plik dźwiękowy SMAF</comment>
+ <comment xml:lang="pt">áudio SMAF</comment>
+ <comment xml:lang="pt_BR">Áudio SMAF</comment>
+ <comment xml:lang="ro">Audio SMAF</comment>
+ <comment xml:lang="ru">Аудио SMAF</comment>
+ <comment xml:lang="sk">Zvuk SMAF</comment>
+ <comment xml:lang="sl">Zvočna datoteka SMAF</comment>
+ <comment xml:lang="sq">Audio SMAF</comment>
+ <comment xml:lang="sr">СМАФ звук</comment>
+ <comment xml:lang="sv">SMAF-ljud</comment>
+ <comment xml:lang="tr">SMAF sesi</comment>
+ <comment xml:lang="uk">звук SMAF</comment>
+ <comment xml:lang="vi">Âm thanh SMAF</comment>
+ <comment xml:lang="zh_CN">SMAF 音频</comment>
+ <comment xml:lang="zh_TW">SMAF 音訊</comment>
+ <acronym>SMAF</acronym>
+ <expanded-acronym>Synthetic music Mobile Application Format</expanded-acronym>
+ <generic-icon name="audio-x-generic"/>
+ <magic priority="50">
+ <match value="MMMD" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mmf"/>
+ <glob pattern="*.smaf"/>
+ <alias type="application/vnd.smaf"/>
+ </mime-type>
+ <mime-type type="text/x-mrml">
+ <comment>MRML playlist</comment>
+ <comment xml:lang="ar">قائمة تشغيل MRML</comment>
+ <comment xml:lang="be@latin">Śpis piesień MRML</comment>
+ <comment xml:lang="bg">Списък за изпълнение — MRML</comment>
+ <comment xml:lang="ca">llista de reproducció MRML</comment>
+ <comment xml:lang="cs">seznam k přehrání MRML</comment>
+ <comment xml:lang="da">MRML-afspilningsliste</comment>
+ <comment xml:lang="de">MRML-Wiedergabeliste</comment>
+ <comment xml:lang="el">Λίστα αναπαραγωγής MRML</comment>
+ <comment xml:lang="en_GB">MRML playlist</comment>
+ <comment xml:lang="eo">MRML-ludlisto</comment>
+ <comment xml:lang="es">lista de reproducción MRML</comment>
+ <comment xml:lang="eu">MRML erreprodukzio-zerrenda</comment>
+ <comment xml:lang="fi">MRML-soittolista</comment>
+ <comment xml:lang="fo">MRML avspælingarlisti</comment>
+ <comment xml:lang="fr">liste de lecture MRML</comment>
+ <comment xml:lang="ga">seinmliosta MRML</comment>
+ <comment xml:lang="gl">lista de reprodución MRML</comment>
+ <comment xml:lang="he">רשימת השמעה MRML</comment>
+ <comment xml:lang="hr">MRML popis izvođenja</comment>
+ <comment xml:lang="hu">MRML-lejátszólista</comment>
+ <comment xml:lang="ia">Lista de selection MRML</comment>
+ <comment xml:lang="id">Senarai putar MRML</comment>
+ <comment xml:lang="it">Playlist MRML</comment>
+ <comment xml:lang="ja">MRML 再生リスト</comment>
+ <comment xml:lang="ka">MRML რეპერტუარი</comment>
+ <comment xml:lang="kk">MRML ойнау тізімі</comment>
+ <comment xml:lang="ko">MRML 재생 목록</comment>
+ <comment xml:lang="lt">MRML grojaraštis</comment>
+ <comment xml:lang="lv">MRML repertuārs</comment>
+ <comment xml:lang="nb">MRML-spilleliste</comment>
+ <comment xml:lang="nl">MRML-afspeellijst</comment>
+ <comment xml:lang="nn">MRML-speleliste</comment>
+ <comment xml:lang="oc">lista de lectura MRML</comment>
+ <comment xml:lang="pl">Lista odtwarzania MRML</comment>
+ <comment xml:lang="pt">lista de reprodução MRML</comment>
+ <comment xml:lang="pt_BR">Lista de reprodução do MRML</comment>
+ <comment xml:lang="ro">Listă redare MRML</comment>
+ <comment xml:lang="ru">Список воспроизведения MRML</comment>
+ <comment xml:lang="sk">Zoznam skladieb MRML</comment>
+ <comment xml:lang="sl">Seznam predvajanja MRML</comment>
+ <comment xml:lang="sq">Listë titujsh MRML</comment>
+ <comment xml:lang="sr">МРМЛ списак нумера</comment>
+ <comment xml:lang="sv">MRML-spellista</comment>
+ <comment xml:lang="tr">MRML oynatma listesi</comment>
+ <comment xml:lang="uk">список відтворення MRML</comment>
+ <comment xml:lang="vi">Danh mục nhạc MRML</comment>
+ <comment xml:lang="zh_CN">MRML 播放列表</comment>
+ <comment xml:lang="zh_TW">MRML 播放清單</comment>
+ <acronym>MRML</acronym>
+ <expanded-acronym>Multimedia Retrieval Markup Language</expanded-acronym>
+ <magic priority="50">
+ <match value="&lt;mrml " type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mrml"/>
+ <glob pattern="*.mrl"/>
+ </mime-type>
+ <mime-type type="audio/x-xmf">
+ <comment>XMF audio</comment>
+ <comment xml:lang="ar">XMF سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo XMF</comment>
+ <comment xml:lang="bg">Аудио — XMF</comment>
+ <comment xml:lang="ca">àudio XMF</comment>
+ <comment xml:lang="cs">zvuk XMF</comment>
+ <comment xml:lang="da">XMF-lyd</comment>
+ <comment xml:lang="de">XMF-Audio</comment>
+ <comment xml:lang="el">Ήχος XMF</comment>
+ <comment xml:lang="en_GB">XMF audio</comment>
+ <comment xml:lang="eo">XMF-sondosiero</comment>
+ <comment xml:lang="es">sonido XMF</comment>
+ <comment xml:lang="eu">XMF audioa</comment>
+ <comment xml:lang="fi">XMF-ääni</comment>
+ <comment xml:lang="fo">XMF ljóður</comment>
+ <comment xml:lang="fr">audio XMF</comment>
+ <comment xml:lang="ga">fuaim XMF</comment>
+ <comment xml:lang="gl">son XMF</comment>
+ <comment xml:lang="he">שמע XMF</comment>
+ <comment xml:lang="hr">XMF zvučni zapis</comment>
+ <comment xml:lang="hu">XMF hang</comment>
+ <comment xml:lang="ia">Audio XMF</comment>
+ <comment xml:lang="id">Audio XMF</comment>
+ <comment xml:lang="it">Audio XMF</comment>
+ <comment xml:lang="ja">XMF オーディオ</comment>
+ <comment xml:lang="kk">XMF аудиосы</comment>
+ <comment xml:lang="ko">XMF 오디오</comment>
+ <comment xml:lang="lt">XMF garso įrašas</comment>
+ <comment xml:lang="lv">XMF audio</comment>
+ <comment xml:lang="nb">XMF-lyd</comment>
+ <comment xml:lang="nl">XMF-audio</comment>
+ <comment xml:lang="nn">XMF-lyd</comment>
+ <comment xml:lang="oc">àudio XMF</comment>
+ <comment xml:lang="pl">Plik dźwiękowy XMF</comment>
+ <comment xml:lang="pt">aúdio XMF</comment>
+ <comment xml:lang="pt_BR">Áudio XMF</comment>
+ <comment xml:lang="ro">Audio XMF</comment>
+ <comment xml:lang="ru">Аудио XMF</comment>
+ <comment xml:lang="sk">Zvuk XMF</comment>
+ <comment xml:lang="sl">Zvočna datoteka XMF</comment>
+ <comment xml:lang="sq">Audio XMF</comment>
+ <comment xml:lang="sr">ИксМФ звук</comment>
+ <comment xml:lang="sv">XMF-ljud</comment>
+ <comment xml:lang="tr">XMF sesi</comment>
+ <comment xml:lang="uk">звук XMF</comment>
+ <comment xml:lang="vi">Âm thanh XMF</comment>
+ <comment xml:lang="zh_CN">XMF 音频</comment>
+ <comment xml:lang="zh_TW">XMF 音訊</comment>
+ <acronym>XMF</acronym>
+ <expanded-acronym>eXtensible Music Format</expanded-acronym>
+ <magic priority="50">
+ <match value="XMF_" type="string" offset="0"/>
+ <match value="\130\115\106\137\062\056\060\060\000\000\000\002" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.xmf"/>
+ <alias type="audio/xmf"/>
+ <alias type="audio/mobile-xmf"/>
+ </mime-type>
+ <mime-type type="application/x-sv4cpio">
+ <comment>SV4 CPIO archive</comment>
+ <comment xml:lang="ar">أرشيف SV4 CPIO</comment>
+ <comment xml:lang="az">SV4 CPIO arxivi</comment>
+ <comment xml:lang="be@latin">Archiŭ SV4 CPIO</comment>
+ <comment xml:lang="bg">Архив — SV4 CPIO</comment>
+ <comment xml:lang="ca">arxiu CPIO SV4</comment>
+ <comment xml:lang="cs">archiv SV4 CPIO</comment>
+ <comment xml:lang="cy">Archif CPIO SV4</comment>
+ <comment xml:lang="da">SV4 CPIO-arkiv</comment>
+ <comment xml:lang="de">SV4-CPIO-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο SV4 CPIO</comment>
+ <comment xml:lang="en_GB">SV4 CPIO archive</comment>
+ <comment xml:lang="eo">SV4-CPIO-arkivo</comment>
+ <comment xml:lang="es">archivador SV4 CPIO</comment>
+ <comment xml:lang="eu">SV4 CPIO artxiboa</comment>
+ <comment xml:lang="fi">SV4 CPIO -arkisto</comment>
+ <comment xml:lang="fo">SV4 CPIO skjalasavn</comment>
+ <comment xml:lang="fr">archive SV4 CPIO</comment>
+ <comment xml:lang="ga">cartlann SV4 CPIO</comment>
+ <comment xml:lang="gl">arquivo SV4 CPIO</comment>
+ <comment xml:lang="he">ארכיון של SV4 SPIO</comment>
+ <comment xml:lang="hr">SV4 CPIO arhiva</comment>
+ <comment xml:lang="hu">SV4 CPIO-archívum</comment>
+ <comment xml:lang="ia">Archivo CPIO SV4</comment>
+ <comment xml:lang="id">Arsip SV4 CPIO</comment>
+ <comment xml:lang="it">Archivio SV4 CPIO</comment>
+ <comment xml:lang="ja">SV4 CPIO アーカイブ</comment>
+ <comment xml:lang="kk">SV4 CPIO архиві</comment>
+ <comment xml:lang="ko">SV4 CPIO 묶음 파일</comment>
+ <comment xml:lang="lt">SV4 CPIO archyvas</comment>
+ <comment xml:lang="lv">SV4 CPIO arhīvs</comment>
+ <comment xml:lang="ms">Arkib CPIO SV4</comment>
+ <comment xml:lang="nb">SV4 CPIO-arkiv</comment>
+ <comment xml:lang="nl">SV4 CPIO-archief</comment>
+ <comment xml:lang="nn">SV4 CPIO-arkiv</comment>
+ <comment xml:lang="oc">archiu SV4 CPIO</comment>
+ <comment xml:lang="pl">Archiwum SV4 CPIO</comment>
+ <comment xml:lang="pt">arquivo SV4 CPIO</comment>
+ <comment xml:lang="pt_BR">Pacote SV4 CPIO</comment>
+ <comment xml:lang="ro">Arhivă SV4 CPIO</comment>
+ <comment xml:lang="ru">Архив SV4 CPIO</comment>
+ <comment xml:lang="sk">Archív SV4 CPIO</comment>
+ <comment xml:lang="sl">Datoteka arhiva SV4 CPIO</comment>
+ <comment xml:lang="sq">Arkiv SV4 CPIO</comment>
+ <comment xml:lang="sr">СВ4 ЦПИО архива</comment>
+ <comment xml:lang="sv">SV4 CPIO-arkiv</comment>
+ <comment xml:lang="tr">SV4 CPIO arşivi</comment>
+ <comment xml:lang="uk">архів SV4 CPIO</comment>
+ <comment xml:lang="vi">Kho nén CPIO SV4</comment>
+ <comment xml:lang="zh_CN">SV4 CPIO 归档文件</comment>
+ <comment xml:lang="zh_TW">SV4 CPIO 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.sv4cpio"/>
+ </mime-type>
+ <mime-type type="application/x-sv4crc">
+ <comment>SV4 CPIO archive (with CRC)</comment>
+ <comment xml:lang="ar">أرشيف SV4 CPIO (مع CRC)</comment>
+ <comment xml:lang="be@latin">Archiŭ SV4 CPIO (z CRC)</comment>
+ <comment xml:lang="bg">Архив — SV4 CPIO, проверка за грешки CRC</comment>
+ <comment xml:lang="ca">arxiu CPIO SV4 (amb CRC)</comment>
+ <comment xml:lang="cs">archiv SV4 CPIO (s CRC)</comment>
+ <comment xml:lang="da">SV4 CPIO-arkiv (med CRC)</comment>
+ <comment xml:lang="de">SV4-CPIO-Archiv (mit CRC)</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο SV4 CPIO (με CRC)</comment>
+ <comment xml:lang="en_GB">SV4 CPIO archive (with CRC)</comment>
+ <comment xml:lang="eo">SV4-CPIO-arkivo (kun CRC)</comment>
+ <comment xml:lang="es">archivador SV4 CPIO (con CRC)</comment>
+ <comment xml:lang="eu">SV4 CPIO artxiboa (CRC-rekin)</comment>
+ <comment xml:lang="fi">SV4 CPIO -arkisto (CRC:llä)</comment>
+ <comment xml:lang="fo">SV4 CPIO skjalasavn (við CRC)</comment>
+ <comment xml:lang="fr">archive SV4 CPIO (avec CRC)</comment>
+ <comment xml:lang="ga">cartlann SV4 CPIO (le CRC)</comment>
+ <comment xml:lang="gl">Arquivador SV4 CPIO (con CRC)</comment>
+ <comment xml:lang="he">ארכיון של SV4 SPIO (עם CRC)</comment>
+ <comment xml:lang="hr">SV4 CPIO arhiva (s CRC-om)</comment>
+ <comment xml:lang="hu">SV4 CPIO-archívum (CRC-vel)</comment>
+ <comment xml:lang="ia">Archivo CPIO SV4 (con CRC)</comment>
+ <comment xml:lang="id">Arsip SV4 CPIO (dengan CRC)</comment>
+ <comment xml:lang="it">Archivio SV4 CPIO (con CRC)</comment>
+ <comment xml:lang="ja">SV4 CPIO アーカイブ (CRC 有り)</comment>
+ <comment xml:lang="kk">SV4 CPIO архиві (CRC бар)</comment>
+ <comment xml:lang="ko">SV4 CPIO 묶음 파일(CRC 포함)</comment>
+ <comment xml:lang="lt">SV4 CPII archyvas (su CRC)</comment>
+ <comment xml:lang="lv">SV4 CPIO arhīvs (ar CRC)</comment>
+ <comment xml:lang="ms">Arkib CPIO SV4 (dengan CRC)</comment>
+ <comment xml:lang="nb">SV4 CPIO-arkiv (med CRC)</comment>
+ <comment xml:lang="nl">SV4 CPIO-archief (met CRC)</comment>
+ <comment xml:lang="nn">SV4 CPIO arkiv (med CRC)</comment>
+ <comment xml:lang="oc">archiu SV4 CPIO (avec CRC)</comment>
+ <comment xml:lang="pl">Archiwum SV4 CPIO (z sumą kontrolną)</comment>
+ <comment xml:lang="pt">arquivo SV4 CPIO (com CRC)</comment>
+ <comment xml:lang="pt_BR">Pacote SV4 CPIO (com CRC)</comment>
+ <comment xml:lang="ro">Arhivă SV4 CPIO (cu CRC)</comment>
+ <comment xml:lang="ru">Архив SV4 CPIO (с CRC)</comment>
+ <comment xml:lang="sk">Archív SV4 CPIO (s CRC)</comment>
+ <comment xml:lang="sl">Datoteka arhiva SV4 CPIO (z razpršilom CRC)</comment>
+ <comment xml:lang="sq">Arkiv SV4 CPIO (me CRC)</comment>
+ <comment xml:lang="sr">СВ4 ЦПИО архива (са ЦРЦ-ом)</comment>
+ <comment xml:lang="sv">SV4 CPIO-arkiv (med CRC)</comment>
+ <comment xml:lang="tr">SV4 CPIO arşivi (CRC ile)</comment>
+ <comment xml:lang="uk">архів SV4 CPIO (з CRC)</comment>
+ <comment xml:lang="vi">Kho nén CPIO SV4 (với CRC)</comment>
+ <comment xml:lang="zh_CN">SV4 CPIP 归档文件(带 CRC)</comment>
+ <comment xml:lang="zh_TW">SV4 CPIO 封存檔 (具有 CRC)</comment>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.sv4crc"/>
+ </mime-type>
+ <mime-type type="application/x-tar">
+ <comment>Tar archive</comment>
+ <comment xml:lang="ar">أرشيف Tar</comment>
+ <comment xml:lang="az">Tar arxivi</comment>
+ <comment xml:lang="be@latin">Archiŭ tar</comment>
+ <comment xml:lang="bg">Архив — tar</comment>
+ <comment xml:lang="ca">arxiu tar</comment>
+ <comment xml:lang="cs">archiv Tar</comment>
+ <comment xml:lang="cy">Archif tar</comment>
+ <comment xml:lang="da">Tar-arkiv</comment>
+ <comment xml:lang="de">Tar-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Tar</comment>
+ <comment xml:lang="en_GB">Tar archive</comment>
+ <comment xml:lang="es">archivador Tar</comment>
+ <comment xml:lang="eu">Tar artxiboa</comment>
+ <comment xml:lang="fi">Tar-arkisto</comment>
+ <comment xml:lang="fo">Tar skjalasavn</comment>
+ <comment xml:lang="fr">archive tar</comment>
+ <comment xml:lang="ga">cartlann Tar</comment>
+ <comment xml:lang="gl">arquivo Tar</comment>
+ <comment xml:lang="he">ארכיון Tar</comment>
+ <comment xml:lang="hr">Tar arhiva</comment>
+ <comment xml:lang="hu">Tar archívum</comment>
+ <comment xml:lang="ia">Archivo Tar</comment>
+ <comment xml:lang="id">Arsip Tar</comment>
+ <comment xml:lang="it">Archivio tar</comment>
+ <comment xml:lang="ja">Tar アーカイブ</comment>
+ <comment xml:lang="kk">Tar архиві</comment>
+ <comment xml:lang="ko">TAR 묶음 파일</comment>
+ <comment xml:lang="lt">Tar archyvas</comment>
+ <comment xml:lang="lv">Tar arhīvs</comment>
+ <comment xml:lang="ms">Arkib Tar</comment>
+ <comment xml:lang="nb">Tar-arkiv</comment>
+ <comment xml:lang="nl">Tar-archief</comment>
+ <comment xml:lang="nn">Tar-arkiv</comment>
+ <comment xml:lang="oc">archiu tar</comment>
+ <comment xml:lang="pl">Archiwum tar</comment>
+ <comment xml:lang="pt">arquivo Tar</comment>
+ <comment xml:lang="pt_BR">Pacote Tar</comment>
+ <comment xml:lang="ro">Arhivă Tar</comment>
+ <comment xml:lang="ru">Архив TAR</comment>
+ <comment xml:lang="sk">Archív tar</comment>
+ <comment xml:lang="sl">Datoteka arhiva Tar</comment>
+ <comment xml:lang="sq">Arkiv tar</comment>
+ <comment xml:lang="sr">Тар архива</comment>
+ <comment xml:lang="sv">Tar-arkiv</comment>
+ <comment xml:lang="tr">Tar arşivi</comment>
+ <comment xml:lang="uk">архів tar</comment>
+ <comment xml:lang="vi">Kho nén tar</comment>
+ <comment xml:lang="zh_CN">Tar 归档文件</comment>
+ <comment xml:lang="zh_TW">Tar 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <alias type="application/x-gtar"/>
+ <magic priority="60">
+ <match value="ustar\0" type="string" offset="257"/>
+ <match value="ustar\040\040\0" type="string" offset="257"/>
+ </magic>
+ <glob pattern="*.tar"/>
+ <glob pattern="*.gtar"/>
+ <glob pattern="*.gem"/>
+ </mime-type>
+ <mime-type type="application/x-tarz">
+ <comment>Tar archive (compressed)</comment>
+ <comment xml:lang="ar">أرشيف Tar (مضغوط)</comment>
+ <comment xml:lang="be@latin">Archiŭ tar (skampresavany)</comment>
+ <comment xml:lang="bg">Архив — tar, компресиран</comment>
+ <comment xml:lang="ca">arxiu tar (amb compressió)</comment>
+ <comment xml:lang="cs">archiv Tar (komprimovaný)</comment>
+ <comment xml:lang="da">Tar-arkiv (komprimeret)</comment>
+ <comment xml:lang="de">Tar-Archiv (komprimiert)</comment>
+ <comment xml:lang="el">Αρχείο Tar (συμπιεσμένο)</comment>
+ <comment xml:lang="en_GB">Tar archive (compressed)</comment>
+ <comment xml:lang="es">archivador Tar (comprimido)</comment>
+ <comment xml:lang="eu">Tar artxiboa (konprimitua)</comment>
+ <comment xml:lang="fi">Tar-arkisto (pakattu)</comment>
+ <comment xml:lang="fo">Tar skjalasavn (stappað)</comment>
+ <comment xml:lang="fr">archive tar (compressée)</comment>
+ <comment xml:lang="ga">cartlann Tar (comhbhrúite)</comment>
+ <comment xml:lang="gl">arquivo Tar (comprimido)</comment>
+ <comment xml:lang="he">ארכיון Tar (מכווץ)</comment>
+ <comment xml:lang="hr">Tar arhiva (sažeta)</comment>
+ <comment xml:lang="hu">Tar archívum (tömörített)</comment>
+ <comment xml:lang="ia">Archivo Tar (comprimite)</comment>
+ <comment xml:lang="id">Arsip Tar (terkompresi)</comment>
+ <comment xml:lang="it">Archivio tar (compresso)</comment>
+ <comment xml:lang="ja">Tar アーカイブ (compress 圧縮)</comment>
+ <comment xml:lang="kk">Tar архиві (сығылған)</comment>
+ <comment xml:lang="ko">TAR 묶음 파일(압축) </comment>
+ <comment xml:lang="lt">Tar archyvas (suglaudintas)</comment>
+ <comment xml:lang="lv">Tar arhīvs (saspiests)</comment>
+ <comment xml:lang="nb">Tar-arkiv (komprimert)</comment>
+ <comment xml:lang="nl">Tar-archief (ingepakt)</comment>
+ <comment xml:lang="nn">Tar-arkiv (pakka)</comment>
+ <comment xml:lang="oc">archiu tar (compressat)</comment>
+ <comment xml:lang="pl">Archiwum tar (skompresowane)</comment>
+ <comment xml:lang="pt">arquivo Tar (comprimido)</comment>
+ <comment xml:lang="pt_BR">Pacote Tar (compactado)</comment>
+ <comment xml:lang="ro">Arhivă Tar (comprimată)</comment>
+ <comment xml:lang="ru">Архив TAR (сжатый)</comment>
+ <comment xml:lang="sk">Archív tar (komprimovaný)</comment>
+ <comment xml:lang="sl">Datoteka arhiva Tar (stisnjen)</comment>
+ <comment xml:lang="sq">Arkiv tar (i kompresuar)</comment>
+ <comment xml:lang="sr">Тар архива (запакована)</comment>
+ <comment xml:lang="sv">Tar-arkiv (komprimerat)</comment>
+ <comment xml:lang="tr">Tar arşivi (sıkıştırılmış)</comment>
+ <comment xml:lang="uk">архів tar (стиснений)</comment>
+ <comment xml:lang="vi">Kho nén tar (đã nén)</comment>
+ <comment xml:lang="zh_CN">Tar 归档文件(压缩)</comment>
+ <comment xml:lang="zh_TW">Tar 封存檔 (UNIX 格式壓縮)</comment>
+ <sub-class-of type="application/x-compress"/>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.tar.Z"/>
+ <glob pattern="*.taz"/>
+ </mime-type>
+ <mime-type type="application/x-tex-gf">
+ <comment>generic font file</comment>
+ <comment xml:lang="ar">ملف الخط العام</comment>
+ <comment xml:lang="be@latin">zvyčajny fajł šryftu</comment>
+ <comment xml:lang="bg">Шрифт</comment>
+ <comment xml:lang="ca">fitxer de lletra genèrica</comment>
+ <comment xml:lang="cs">obecný soubor s fontem</comment>
+ <comment xml:lang="da">general skrifttypefil</comment>
+ <comment xml:lang="de">Allgemeine Schriftdatei</comment>
+ <comment xml:lang="el">Γενικό αρχείο γραμματοσειράς</comment>
+ <comment xml:lang="en_GB">generic font file</comment>
+ <comment xml:lang="eo">genera tipara dosiero</comment>
+ <comment xml:lang="es">tipo de letra genérico</comment>
+ <comment xml:lang="eu">letra-tipo orokorra</comment>
+ <comment xml:lang="fi">yleinen fonttitiedosto</comment>
+ <comment xml:lang="fo">felagsstavasniðsfíla</comment>
+ <comment xml:lang="fr">fichier de polices générique</comment>
+ <comment xml:lang="ga">comhad cló ginearálta</comment>
+ <comment xml:lang="gl">ficheiro de tipo de fonte xenérica</comment>
+ <comment xml:lang="he">קובץ גופן גנרי</comment>
+ <comment xml:lang="hr">Izvorna datoteka slova</comment>
+ <comment xml:lang="hu">általános betűkészletfájl</comment>
+ <comment xml:lang="ia">File de typo de litteras generic</comment>
+ <comment xml:lang="id">berkas fonta generik</comment>
+ <comment xml:lang="it">File tipo carattere generico</comment>
+ <comment xml:lang="ja">一般フォントファイル</comment>
+ <comment xml:lang="kk">қаріп файлы</comment>
+ <comment xml:lang="ko">일반 글꼴 파일</comment>
+ <comment xml:lang="lt">bendras šrifto failas</comment>
+ <comment xml:lang="lv">vispārēja fonta datne</comment>
+ <comment xml:lang="ms">Fail font generik</comment>
+ <comment xml:lang="nb">vanlig skriftfil</comment>
+ <comment xml:lang="nl">algemeen lettertypebestand</comment>
+ <comment xml:lang="nn">vanleg skrifttypefil</comment>
+ <comment xml:lang="oc">fichièr de poliças generic</comment>
+ <comment xml:lang="pl">Zwykły plik czcionki</comment>
+ <comment xml:lang="pt">ficheiro genérico de letra</comment>
+ <comment xml:lang="pt_BR">Arquivo de fonte genérico</comment>
+ <comment xml:lang="ro">fișier de font generic</comment>
+ <comment xml:lang="ru">Обычный файл шрифта</comment>
+ <comment xml:lang="sk">Obyčajný súbor písma</comment>
+ <comment xml:lang="sl">izvorna datoteka pisave</comment>
+ <comment xml:lang="sq">File lloj gërme i përgjithshëm</comment>
+ <comment xml:lang="sr">општа датотека слова</comment>
+ <comment xml:lang="sv">allmän typsnittsfil</comment>
+ <comment xml:lang="tr">genel yazı tipi dosyası</comment>
+ <comment xml:lang="uk">загальний файл шрифту</comment>
+ <comment xml:lang="vi">tập tin phông giống loài</comment>
+ <comment xml:lang="zh_CN">通用字体文件</comment>
+ <comment xml:lang="zh_TW">通用字型檔</comment>
+ <generic-icon name="font-x-generic"/>
+ <glob pattern="*.gf"/>
+ </mime-type>
+ <mime-type type="application/x-tex-pk">
+ <comment>packed font file</comment>
+ <comment xml:lang="ar">ملف الخط المرزم</comment>
+ <comment xml:lang="be@latin">zapakavany fajł šryftu</comment>
+ <comment xml:lang="bg">Шрифт — компресиран</comment>
+ <comment xml:lang="ca">fitxer de lletra empaquetada</comment>
+ <comment xml:lang="cs">komprimovaný soubor s fontem</comment>
+ <comment xml:lang="da">pakket skrifttypefil</comment>
+ <comment xml:lang="de">Gepackte Schriftdatei</comment>
+ <comment xml:lang="el">Αρχείο συμπιεσμένης γραμματοσειράς</comment>
+ <comment xml:lang="en_GB">packed font file</comment>
+ <comment xml:lang="eo">pakigita tipara dosiero</comment>
+ <comment xml:lang="es">tipo de letra empaquetado</comment>
+ <comment xml:lang="eu">Letra-tipo fitxategi paketatua</comment>
+ <comment xml:lang="fi">pakattu fonttitiedosto</comment>
+ <comment xml:lang="fo">pakkað stavasniðsfíla</comment>
+ <comment xml:lang="fr">fichier de polices empaquetées</comment>
+ <comment xml:lang="ga">comhad cló pacáilte</comment>
+ <comment xml:lang="gl">ficheiro de fonte empaquetada</comment>
+ <comment xml:lang="he">קובץ גופן ארוז</comment>
+ <comment xml:lang="hr">Zapakirana datoteka slova</comment>
+ <comment xml:lang="hu">packed font-fájl</comment>
+ <comment xml:lang="ia">File de typos de litteras impacchettate</comment>
+ <comment xml:lang="id">berkas fonta terkemas</comment>
+ <comment xml:lang="it">File tipo carattere condensato</comment>
+ <comment xml:lang="ja">パックされたフォントファイル</comment>
+ <comment xml:lang="kk">қаріп файлы (дестеленген)</comment>
+ <comment xml:lang="ko">글꼴 묶음 파일</comment>
+ <comment xml:lang="lt">supakuotas šrifto failas</comment>
+ <comment xml:lang="lv">sapakota fonta datne</comment>
+ <comment xml:lang="ms">Fail font dipek</comment>
+ <comment xml:lang="nb">pakket skriftfil</comment>
+ <comment xml:lang="nl">ingepakt lettertypebestand</comment>
+ <comment xml:lang="nn">pakka skrifttypefil</comment>
+ <comment xml:lang="oc">fichièr de poliças empaquetadas</comment>
+ <comment xml:lang="pl">Plik ze spakowaną czcionką</comment>
+ <comment xml:lang="pt">ficheiro de letras empacotadas</comment>
+ <comment xml:lang="pt_BR">Arquivo de fonte empacotado</comment>
+ <comment xml:lang="ro">fișier font împachetat</comment>
+ <comment xml:lang="ru">Сжатый файл шрифта</comment>
+ <comment xml:lang="sk">Komprimovaný súbor písma</comment>
+ <comment xml:lang="sl">pakirana datoteka pisave</comment>
+ <comment xml:lang="sq">File lloj gërmash i kondensuar</comment>
+ <comment xml:lang="sr">пакована датотека слова</comment>
+ <comment xml:lang="sv">packad typsnittsfil</comment>
+ <comment xml:lang="tr">paketlenmiş yazı tipi dosyası</comment>
+ <comment xml:lang="uk">запакований файл шрифту</comment>
+ <comment xml:lang="vi">tập tin phông chữ đã đóng gói</comment>
+ <comment xml:lang="zh_CN">打包的字体文件</comment>
+ <comment xml:lang="zh_TW">包裝字型檔</comment>
+ <generic-icon name="font-x-generic"/>
+ <glob pattern="*.pk"/>
+ </mime-type>
+ <mime-type type="application/x-tgif">
+ <comment>TGIF document</comment>
+ <comment xml:lang="ar">مستند TGIF</comment>
+ <comment xml:lang="ast">Documentu de TGIF</comment>
+ <comment xml:lang="be@latin">Dakument TGIF</comment>
+ <comment xml:lang="bg">Документ — TGIF</comment>
+ <comment xml:lang="ca">document TGIF</comment>
+ <comment xml:lang="cs">dokument TGIF</comment>
+ <comment xml:lang="da">TGIF-dokument</comment>
+ <comment xml:lang="de">TGIF-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο TGIF</comment>
+ <comment xml:lang="en_GB">TGIF document</comment>
+ <comment xml:lang="eo">TGIF-dokumento</comment>
+ <comment xml:lang="es">documento TGIF</comment>
+ <comment xml:lang="eu">TGIF dokumentua</comment>
+ <comment xml:lang="fi">TGIF-asiakirja</comment>
+ <comment xml:lang="fo">TGIF skjal</comment>
+ <comment xml:lang="fr">document TGIF</comment>
+ <comment xml:lang="ga">cáipéis TGIF</comment>
+ <comment xml:lang="gl">documento TGIF</comment>
+ <comment xml:lang="he">מסמך TGIF</comment>
+ <comment xml:lang="hr">TGIF dokument</comment>
+ <comment xml:lang="hu">TGIF-dokumentum</comment>
+ <comment xml:lang="ia">Documento TGIF</comment>
+ <comment xml:lang="id">Dokumen TGIF</comment>
+ <comment xml:lang="it">Documento TGIF</comment>
+ <comment xml:lang="ja">TGIF ドキュメント</comment>
+ <comment xml:lang="kk">TGIF құжаты</comment>
+ <comment xml:lang="ko">TGIF 문서</comment>
+ <comment xml:lang="lt">TGIF dokumentas</comment>
+ <comment xml:lang="lv">TGIF dokuments</comment>
+ <comment xml:lang="ms">Dokumen TGIF</comment>
+ <comment xml:lang="nb">TGIF-dokument</comment>
+ <comment xml:lang="nl">TGIF-document</comment>
+ <comment xml:lang="nn">TGIF-dokument</comment>
+ <comment xml:lang="oc">document TGIF</comment>
+ <comment xml:lang="pl">Dokument TGIF</comment>
+ <comment xml:lang="pt">documento TGIF</comment>
+ <comment xml:lang="pt_BR">Documento TGIF</comment>
+ <comment xml:lang="ro">Document TGIF</comment>
+ <comment xml:lang="ru">Документ TGIF</comment>
+ <comment xml:lang="sk">Dokument TGIF</comment>
+ <comment xml:lang="sl">Dokument TGIF</comment>
+ <comment xml:lang="sq">Dokument TGIF</comment>
+ <comment xml:lang="sr">ТГИФ документ</comment>
+ <comment xml:lang="sv">TGIF-dokument</comment>
+ <comment xml:lang="tr">TGIF belgesi</comment>
+ <comment xml:lang="uk">документ TGIF</comment>
+ <comment xml:lang="vi">Tài liệu TGIF</comment>
+ <comment xml:lang="zh_CN">TGIF 文档</comment>
+ <comment xml:lang="zh_TW">TGIF 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="%TGIF" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.obj"/>
+ </mime-type>
+ <mime-type type="application/x-theme">
+ <comment>theme</comment>
+ <comment xml:lang="ar">سمة</comment>
+ <comment xml:lang="az">örtük</comment>
+ <comment xml:lang="be@latin">matyŭ</comment>
+ <comment xml:lang="bg">Тема</comment>
+ <comment xml:lang="ca">tema</comment>
+ <comment xml:lang="cs">motiv</comment>
+ <comment xml:lang="cy">thema</comment>
+ <comment xml:lang="da">tema</comment>
+ <comment xml:lang="de">Thema</comment>
+ <comment xml:lang="el">Θέμα</comment>
+ <comment xml:lang="en_GB">theme</comment>
+ <comment xml:lang="eo">etoso</comment>
+ <comment xml:lang="es">tema</comment>
+ <comment xml:lang="eu">gaia</comment>
+ <comment xml:lang="fi">teema</comment>
+ <comment xml:lang="fo">tema</comment>
+ <comment xml:lang="fr">thème</comment>
+ <comment xml:lang="ga">téama</comment>
+ <comment xml:lang="gl">tema</comment>
+ <comment xml:lang="he">ערכת נושא</comment>
+ <comment xml:lang="hr">Tema</comment>
+ <comment xml:lang="hu">téma</comment>
+ <comment xml:lang="ia">Thema</comment>
+ <comment xml:lang="id">tema</comment>
+ <comment xml:lang="it">Tema</comment>
+ <comment xml:lang="ja">テーマ</comment>
+ <comment xml:lang="ka">თემა</comment>
+ <comment xml:lang="kk">тема</comment>
+ <comment xml:lang="ko">테마</comment>
+ <comment xml:lang="lt">tema</comment>
+ <comment xml:lang="lv">motīvs</comment>
+ <comment xml:lang="ms">Tema</comment>
+ <comment xml:lang="nb">tema</comment>
+ <comment xml:lang="nl">thema</comment>
+ <comment xml:lang="nn">drakt</comment>
+ <comment xml:lang="oc">tèma</comment>
+ <comment xml:lang="pl">Motyw</comment>
+ <comment xml:lang="pt">tema</comment>
+ <comment xml:lang="pt_BR">Tema</comment>
+ <comment xml:lang="ro">temă</comment>
+ <comment xml:lang="ru">Тема</comment>
+ <comment xml:lang="sk">Motív</comment>
+ <comment xml:lang="sl">tema</comment>
+ <comment xml:lang="sq">Temë</comment>
+ <comment xml:lang="sr">тема</comment>
+ <comment xml:lang="sv">tema</comment>
+ <comment xml:lang="tr">tema</comment>
+ <comment xml:lang="uk">тема</comment>
+ <comment xml:lang="vi">sắc thái</comment>
+ <comment xml:lang="zh_CN">主题</comment>
+ <comment xml:lang="zh_TW">佈景主題</comment>
+ <sub-class-of type="application/x-desktop"/>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.theme"/>
+ </mime-type>
+ <mime-type type="application/x-toutdoux">
+ <comment>ToutDoux document</comment>
+ <comment xml:lang="ar">مستند ToutDoux</comment>
+ <comment xml:lang="ast">Documentu de ToutDoux</comment>
+ <comment xml:lang="az">ToutDoux sənədi</comment>
+ <comment xml:lang="be@latin">Dakument ToutDoux</comment>
+ <comment xml:lang="bg">Документ — ToutDoux</comment>
+ <comment xml:lang="ca">document ToutDoux</comment>
+ <comment xml:lang="cs">dokument ToutDoux</comment>
+ <comment xml:lang="cy">Dogfen ToutDoux</comment>
+ <comment xml:lang="da">ToutDoux-dokument</comment>
+ <comment xml:lang="de">ToutDoux-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο ToutDoux</comment>
+ <comment xml:lang="en_GB">ToutDoux document</comment>
+ <comment xml:lang="eo">ToutDoux-dokumento</comment>
+ <comment xml:lang="es">documento de ToutDoux</comment>
+ <comment xml:lang="eu">ToutDoux dokumentua</comment>
+ <comment xml:lang="fi">ToutDoux-asiakirja</comment>
+ <comment xml:lang="fo">ToutDoux skjal</comment>
+ <comment xml:lang="fr">document ToutDoux</comment>
+ <comment xml:lang="ga">cáipéis ToutDoux</comment>
+ <comment xml:lang="gl">documento de ToutDoux</comment>
+ <comment xml:lang="he">מסמך של ToutDoux</comment>
+ <comment xml:lang="hr">ToutDoux dokument</comment>
+ <comment xml:lang="hu">ToutDoux-dokumentum</comment>
+ <comment xml:lang="ia">Documento ToutDoux</comment>
+ <comment xml:lang="id">Dokumen ToutDoux</comment>
+ <comment xml:lang="it">Documento ToutDoux</comment>
+ <comment xml:lang="ja">ToutDoux ドキュメント</comment>
+ <comment xml:lang="kk">ToutDoux құжаты</comment>
+ <comment xml:lang="ko">ToutDoux 문서</comment>
+ <comment xml:lang="lt">ToutDoux dokumentas</comment>
+ <comment xml:lang="lv">ToutDoux dokuments</comment>
+ <comment xml:lang="ms">Dokumen ToutDoux</comment>
+ <comment xml:lang="nb">ToutDoux-dokument</comment>
+ <comment xml:lang="nl">ToutDoux-document</comment>
+ <comment xml:lang="nn">ToutDoux-dokument</comment>
+ <comment xml:lang="oc">document ToutDoux</comment>
+ <comment xml:lang="pl">Dokument ToutDoux</comment>
+ <comment xml:lang="pt">documento ToutDoux</comment>
+ <comment xml:lang="pt_BR">Documento do ToutDoux</comment>
+ <comment xml:lang="ro">Document ToutDoux</comment>
+ <comment xml:lang="ru">Документ ToutDoux</comment>
+ <comment xml:lang="sk">Dokument ToutDoux</comment>
+ <comment xml:lang="sl">Dokument ToutDoux</comment>
+ <comment xml:lang="sq">Dokument ToutDoux</comment>
+ <comment xml:lang="sr">Туду документ</comment>
+ <comment xml:lang="sv">ToutDoux-dokument</comment>
+ <comment xml:lang="tr">ToutDoux belgesi</comment>
+ <comment xml:lang="uk">документ ToutDoux</comment>
+ <comment xml:lang="vi">Tài liệu ToutDoux</comment>
+ <comment xml:lang="zh_CN">ToutDoux 文档</comment>
+ <comment xml:lang="zh_TW">ToutDoux 文件</comment>
+ <generic-icon name="x-office-document"/>
+ </mime-type>
+ <mime-type type="application/x-trash">
+ <comment>backup file</comment>
+ <comment xml:lang="ar">ملف النسخ الاحتياطي</comment>
+ <comment xml:lang="be@latin">zapasny fajł</comment>
+ <comment xml:lang="bg">Резервно копие</comment>
+ <comment xml:lang="ca">fitxer de còpia de seguretat</comment>
+ <comment xml:lang="cs">záložní soubor</comment>
+ <comment xml:lang="da">sikkerhedskopi</comment>
+ <comment xml:lang="de">Sicherungsdatei</comment>
+ <comment xml:lang="el">Αντίγραφο ασφαλείας</comment>
+ <comment xml:lang="en_GB">backup file</comment>
+ <comment xml:lang="eo">restaŭrkopio</comment>
+ <comment xml:lang="es">archivo de respaldo</comment>
+ <comment xml:lang="eu">babes-kopiako fitxategia</comment>
+ <comment xml:lang="fi">varmuuskopio</comment>
+ <comment xml:lang="fo">trygdarritsfíla</comment>
+ <comment xml:lang="fr">fichier de sauvegarde</comment>
+ <comment xml:lang="ga">comhad cúltaca</comment>
+ <comment xml:lang="gl">ficheiro de copia de seguridade</comment>
+ <comment xml:lang="he">קובץ גיבוי</comment>
+ <comment xml:lang="hr">Datoteka sigurnosne kopije</comment>
+ <comment xml:lang="hu">biztonsági mentés</comment>
+ <comment xml:lang="ia">Copia de reserva</comment>
+ <comment xml:lang="id">berkas cadangan</comment>
+ <comment xml:lang="it">File di backup</comment>
+ <comment xml:lang="ja">バックアップファイル</comment>
+ <comment xml:lang="kk">резервті көшірмесі</comment>
+ <comment xml:lang="ko">백업 파일</comment>
+ <comment xml:lang="lt">atsarginis failas</comment>
+ <comment xml:lang="lv">dublējuma datne</comment>
+ <comment xml:lang="ms">Fail backup</comment>
+ <comment xml:lang="nb">sikkerhetskopi</comment>
+ <comment xml:lang="nl">reservekopiebestand</comment>
+ <comment xml:lang="nn">tryggleikskopi</comment>
+ <comment xml:lang="oc">fichièr de salvament</comment>
+ <comment xml:lang="pl">Plik zapasowy</comment>
+ <comment xml:lang="pt">cópia de segurança</comment>
+ <comment xml:lang="pt_BR">Arquivo de backup</comment>
+ <comment xml:lang="ro">fișier de backup</comment>
+ <comment xml:lang="ru">Резервная копия</comment>
+ <comment xml:lang="sk">Záložný súbor</comment>
+ <comment xml:lang="sl">varnostna kopija datoteke</comment>
+ <comment xml:lang="sq">File backup</comment>
+ <comment xml:lang="sr">датотека резерве</comment>
+ <comment xml:lang="sv">säkerhetskopia</comment>
+ <comment xml:lang="tr">yedek dosyası</comment>
+ <comment xml:lang="uk">резервна копія</comment>
+ <comment xml:lang="vi">tập tin sao lưu</comment>
+ <comment xml:lang="zh_CN">备份文件</comment>
+ <comment xml:lang="zh_TW">備份檔</comment>
+ <glob pattern="*~"/>
+ <glob pattern="*%"/>
+ <glob pattern="*.bak"/>
+ <glob pattern="*.old"/>
+ <glob pattern="*.sik"/>
+ </mime-type>
+ <mime-type type="text/troff">
+ <comment>Troff document</comment>
+ <comment xml:lang="ar">مستند Troff</comment>
+ <comment xml:lang="ast">Documentu de Troff</comment>
+ <comment xml:lang="az">Troff sənədi</comment>
+ <comment xml:lang="be@latin">Dakument Troff</comment>
+ <comment xml:lang="bg">Документ — Troff</comment>
+ <comment xml:lang="ca">document Troff</comment>
+ <comment xml:lang="cs">dokument Troff</comment>
+ <comment xml:lang="cy">Dogfen troff</comment>
+ <comment xml:lang="da">Troffdokument</comment>
+ <comment xml:lang="de">Troff-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο troff</comment>
+ <comment xml:lang="en_GB">Troff document</comment>
+ <comment xml:lang="eo">Troff-dokumento</comment>
+ <comment xml:lang="es">documento de Troff</comment>
+ <comment xml:lang="eu">Troff dokumentua</comment>
+ <comment xml:lang="fi">Troff-asiakirja</comment>
+ <comment xml:lang="fo">Troff skjal</comment>
+ <comment xml:lang="fr">document Troff</comment>
+ <comment xml:lang="ga">cáipéis Troff</comment>
+ <comment xml:lang="gl">documento Troff</comment>
+ <comment xml:lang="he">מסמך Troff</comment>
+ <comment xml:lang="hr">Troff dokument</comment>
+ <comment xml:lang="hu">Troff-dokumentum</comment>
+ <comment xml:lang="ia">Documento Troff</comment>
+ <comment xml:lang="id">Dokumen Troff</comment>
+ <comment xml:lang="it">Documento Troff</comment>
+ <comment xml:lang="ja">Troff 入力ドキュメント</comment>
+ <comment xml:lang="kk">Troff құжаты</comment>
+ <comment xml:lang="ko">Troff 문서</comment>
+ <comment xml:lang="lt">Troff dokumentas</comment>
+ <comment xml:lang="lv">Troff dokuments</comment>
+ <comment xml:lang="ms">Dokumen Troff</comment>
+ <comment xml:lang="nb">Troff-dokument</comment>
+ <comment xml:lang="nl">Troff-document</comment>
+ <comment xml:lang="nn">Troff-dokument</comment>
+ <comment xml:lang="oc">document Troff</comment>
+ <comment xml:lang="pl">Dokument Troff</comment>
+ <comment xml:lang="pt">documento Troff</comment>
+ <comment xml:lang="pt_BR">Documento Troff</comment>
+ <comment xml:lang="ro">Document Troff</comment>
+ <comment xml:lang="ru">Документ Troff</comment>
+ <comment xml:lang="sk">Dokument troff</comment>
+ <comment xml:lang="sl">Dokument Troff</comment>
+ <comment xml:lang="sq">Dokument Troff</comment>
+ <comment xml:lang="sr">Трофф документ</comment>
+ <comment xml:lang="sv">Troff-dokument</comment>
+ <comment xml:lang="tr">Troff belgesi</comment>
+ <comment xml:lang="uk">документ Troff</comment>
+ <comment xml:lang="vi">Tài liệu Troff</comment>
+ <comment xml:lang="zh_CN">Troff 文档</comment>
+ <comment xml:lang="zh_TW">Troff 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <alias type="application/x-troff"/>
+ <alias type="text/x-troff"/>
+ <magic priority="50">
+ <match value='.\\\"' type="string" offset="0"/>
+ <match value="'\\\&quot;" type="string" offset="0"/>
+ <match value="'.\\\&quot;" type="string" offset="0"/>
+ <match value='\\\"' type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.tr"/>
+ <glob pattern="*.roff"/>
+ <glob weight="10" pattern="*.t"/>
+ </mime-type>
+ <mime-type type="application/x-troff-man">
+ <comment>Manpage manual document</comment>
+ <comment xml:lang="ca">document de pàgina man</comment>
+ <comment xml:lang="cs">manuálová stránka</comment>
+ <comment xml:lang="da">Manpage-manualdokument</comment>
+ <comment xml:lang="de">Manpage-Handbuchdokument</comment>
+ <comment xml:lang="el">Έγγραφο βοήθειας manpage</comment>
+ <comment xml:lang="en_GB">Manpage manual document</comment>
+ <comment xml:lang="es">documento de manual de Manpage</comment>
+ <comment xml:lang="eu">Manpage eskuliburu dokumentua</comment>
+ <comment xml:lang="fr">document manuel Manpage</comment>
+ <comment xml:lang="ga">cáipéis lámhleabhair Man</comment>
+ <comment xml:lang="he">מסמך תיעוד man</comment>
+ <comment xml:lang="hr">Manpage dokument priručnika</comment>
+ <comment xml:lang="hu">Manpage kézikönyv-dokumentum</comment>
+ <comment xml:lang="ia">Pagina de manual "man"</comment>
+ <comment xml:lang="id">Dokumen manual manpage</comment>
+ <comment xml:lang="it">Documento di manuale manpage</comment>
+ <comment xml:lang="kk">Manpage нұсқаулық құжаты</comment>
+ <comment xml:lang="ko">맨 페이지 설명서 문서</comment>
+ <comment xml:lang="oc">document de manual Manpage</comment>
+ <comment xml:lang="pl">Dokument podręcznika stron pomocy</comment>
+ <comment xml:lang="pt">documento de ajuda Manpage</comment>
+ <comment xml:lang="pt_BR">Documento Manpage</comment>
+ <comment xml:lang="ru">Документ справочной системы Manpage</comment>
+ <comment xml:lang="sk">Dokument manuálu Manpage</comment>
+ <comment xml:lang="sr">документ упутства странице упутства</comment>
+ <comment xml:lang="sv">Manpage-manualdokument</comment>
+ <comment xml:lang="tr">Man sayfası el kitabı belgesi</comment>
+ <comment xml:lang="uk">документ підручника man</comment>
+ <comment xml:lang="zh_CN">Manpage 手册文档</comment>
+ <comment xml:lang="zh_TW">Manpage 手冊說明文件</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.man"/>
+ </mime-type>
+ <mime-type type="application/x-troff-man-compressed">
+ <comment>manual page (compressed)</comment>
+ <comment xml:lang="ar">صفحة المساعدة (مضغوطة)</comment>
+ <comment xml:lang="az">man səhifəsi (sıxışdırılmış)</comment>
+ <comment xml:lang="be@latin">staronka dapamohi (skampresavanaja)</comment>
+ <comment xml:lang="bg">Страница от справочника, компресирана</comment>
+ <comment xml:lang="ca">pàgina de manual (amb compressió)</comment>
+ <comment xml:lang="cs">manuálová stránka (komprimovaná)</comment>
+ <comment xml:lang="cy">tudalen llawlyfr (wedi ei gywasgu)</comment>
+ <comment xml:lang="da">manualside (komprimeret)</comment>
+ <comment xml:lang="de">Handbuchseite (komprimiert)</comment>
+ <comment xml:lang="el">Σελίδα οδηγιών (συμπιεσμένη)</comment>
+ <comment xml:lang="en_GB">manual page (compressed)</comment>
+ <comment xml:lang="eo">manpaĝo (kunpremita)</comment>
+ <comment xml:lang="es">página de manual (comprimida)</comment>
+ <comment xml:lang="eu">eskuliburu orria (konprimitua)</comment>
+ <comment xml:lang="fi">manuaalisivu (pakattu)</comment>
+ <comment xml:lang="fo">handbókasíða (stappað)</comment>
+ <comment xml:lang="fr">page de manuel (compressée)</comment>
+ <comment xml:lang="ga">leathanach lámhleabhair (comhbhrúite)</comment>
+ <comment xml:lang="gl">páxina de manual (comprimida)</comment>
+ <comment xml:lang="he">דף עזר (מכווץ)</comment>
+ <comment xml:lang="hr">Stranica priručnika (sažeta)</comment>
+ <comment xml:lang="hu">kézikönyvoldal (tömörített)</comment>
+ <comment xml:lang="ia">Pagina de manual (comprimite)</comment>
+ <comment xml:lang="id">halaman manual (terkompresi)</comment>
+ <comment xml:lang="it">Pagina di manuale (compressa)</comment>
+ <comment xml:lang="ja">(圧縮) man ページ</comment>
+ <comment xml:lang="kk">әдістемелік парағы (сығылған)</comment>
+ <comment xml:lang="ko">man 페이지(압축)</comment>
+ <comment xml:lang="lt">žinyno puslapis (suglaudintas)</comment>
+ <comment xml:lang="lv">rokasgrāmatas lapa (saspiesta)</comment>
+ <comment xml:lang="ms">Halaman manual (termampat)</comment>
+ <comment xml:lang="nb">manualside (komprimert)</comment>
+ <comment xml:lang="nl">handleidingspagina (ingepakt)</comment>
+ <comment xml:lang="nn">manualside (pakka)</comment>
+ <comment xml:lang="oc">pagina de manual (compressat)</comment>
+ <comment xml:lang="pl">Strona podręcznika (skompresowana)</comment>
+ <comment xml:lang="pt">página de manual (comprimida)</comment>
+ <comment xml:lang="pt_BR">Página de manual (compactada)</comment>
+ <comment xml:lang="ro">pagină de manual (comprimată)</comment>
+ <comment xml:lang="ru">Страница руководства (сжатая)</comment>
+ <comment xml:lang="sk">Manuálová stránka (komprimovaná)</comment>
+ <comment xml:lang="sl">stran priročnika (stisnjena)</comment>
+ <comment xml:lang="sq">Faqe manuali (e kompresuar)</comment>
+ <comment xml:lang="sr">страница упутства (запакована)</comment>
+ <comment xml:lang="sv">manualsida (komprimerad)</comment>
+ <comment xml:lang="tr">kılavuz dosyası (sıkıştırılmış)</comment>
+ <comment xml:lang="uk">сторінка посібника (стиснена)</comment>
+ <comment xml:lang="vi">trang hướng dẫn (đã nén)</comment>
+ <comment xml:lang="zh_CN">手册页(压缩)</comment>
+ <comment xml:lang="zh_TW">手冊頁面 (壓縮版)</comment>
+ <generic-icon name="text-x-generic"/>
+ </mime-type>
+ <mime-type type="application/x-tzo">
+ <comment>Tar archive (LZO-compressed)</comment>
+ <comment xml:lang="ar">أرشيف Tar (مضغوط-LZO)</comment>
+ <comment xml:lang="be@latin">Archiŭ tar (LZO-skampresavany)</comment>
+ <comment xml:lang="bg">Архив — tar, компресиран с LZO</comment>
+ <comment xml:lang="ca">arxiu tar (amb compressió LZO)</comment>
+ <comment xml:lang="cs">archiv Tar (komprimovaný pomocí LZO)</comment>
+ <comment xml:lang="da">Tar-arkiv (LZO-komprimeret)</comment>
+ <comment xml:lang="de">Tar-Archiv (LZO-komprimiert)</comment>
+ <comment xml:lang="el">Αρχείο Tar (συμπιεσμένο με LZO)</comment>
+ <comment xml:lang="en_GB">Tar archive (LZO-compressed)</comment>
+ <comment xml:lang="es">archivador Tar (comprimido con LZO)</comment>
+ <comment xml:lang="eu">Tar artxiboa (LZO-rekin konprimitua)</comment>
+ <comment xml:lang="fi">Tar-arkisto (LZO-pakattu)</comment>
+ <comment xml:lang="fo">Tar skjalasavn (LZO-stappað)</comment>
+ <comment xml:lang="fr">archive tar (compression LZO)</comment>
+ <comment xml:lang="ga">cartlann Tar (comhbhrúite le LZO)</comment>
+ <comment xml:lang="gl">arquivo Tar (comprimido con LZO)</comment>
+ <comment xml:lang="he">ארכיון Tar (מכווץ ע״י LZO)</comment>
+ <comment xml:lang="hr">Tar arhiva (LZO sažeta)</comment>
+ <comment xml:lang="hu">Tar archívum (LZO-val tömörítve)</comment>
+ <comment xml:lang="ia">Archivo Tar (comprimite con LZO)</comment>
+ <comment xml:lang="id">Arsip Tar (terkompresi LZO)</comment>
+ <comment xml:lang="it">Archivio tar (compresso con LZO)</comment>
+ <comment xml:lang="ja">Tar アーカイブ (LZO 圧縮)</comment>
+ <comment xml:lang="kk">Tar архиві (LZO-мен сығылған)</comment>
+ <comment xml:lang="ko">TAR 묶음 파일(LZO 압축)</comment>
+ <comment xml:lang="lt">Tar archyvas (suglaudintas su LZO)</comment>
+ <comment xml:lang="lv">Tar arhīvs (saspiests ar LZO)</comment>
+ <comment xml:lang="nb">Tar-arkiv (LZO-komprimert)</comment>
+ <comment xml:lang="nl">Tar-archief (ingepakt met LZO)</comment>
+ <comment xml:lang="nn">Tar-arkiv (pakka med LZO)</comment>
+ <comment xml:lang="oc">archiu tar (compression LZO)</comment>
+ <comment xml:lang="pl">Archiwum tar (kompresja LZO)</comment>
+ <comment xml:lang="pt">arquivo Tar (compressão LZO)</comment>
+ <comment xml:lang="pt_BR">Pacote Tar (compactado com LZO)</comment>
+ <comment xml:lang="ro">Arhivă Tar (comprimată LZO)</comment>
+ <comment xml:lang="ru">Архив TAR (сжатый lzo)</comment>
+ <comment xml:lang="sk">Archív tar (komprimovaný pomocou LZO)</comment>
+ <comment xml:lang="sl">Datoteka arhiva Tar (stisnjen z LZO)</comment>
+ <comment xml:lang="sq">Arkiv tar (i kompresuar me LZO)</comment>
+ <comment xml:lang="sr">Тар архива (запакована ЛЗО-ом)</comment>
+ <comment xml:lang="sv">Tar-arkiv (LZO-komprimerat)</comment>
+ <comment xml:lang="tr">Tar arşivi (LZO ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">архів tar (стиснений LZO)</comment>
+ <comment xml:lang="vi">Kho nén tar (đã nén LZO)</comment>
+ <comment xml:lang="zh_CN">Tar 归档文件(LZO 压缩)</comment>
+ <comment xml:lang="zh_TW">Tar 封存檔 (LZO 格式壓縮)</comment>
+ <sub-class-of type="application/x-lzop"/>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.tar.lzo"/>
+ <glob pattern="*.tzo"/>
+ </mime-type>
+ <mime-type type="application/x-xz">
+ <comment>XZ archive</comment>
+ <comment xml:lang="ar">أرشيف XZ</comment>
+ <comment xml:lang="bg">Архив — XZ</comment>
+ <comment xml:lang="ca">arxiu XZ</comment>
+ <comment xml:lang="cs">archiv XZ</comment>
+ <comment xml:lang="da">XZ-arkiv</comment>
+ <comment xml:lang="de">XZ-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο XZ</comment>
+ <comment xml:lang="en_GB">XZ archive</comment>
+ <comment xml:lang="eo">XZ-arkivo</comment>
+ <comment xml:lang="es">archivador XZ</comment>
+ <comment xml:lang="eu">XZ artxiboa</comment>
+ <comment xml:lang="fi">XZ-arkisto</comment>
+ <comment xml:lang="fo">XZ skjalasavn</comment>
+ <comment xml:lang="fr">archive XZ</comment>
+ <comment xml:lang="ga">cartlann XZ</comment>
+ <comment xml:lang="gl">ficheiro XZ</comment>
+ <comment xml:lang="he">ארכיון XZ</comment>
+ <comment xml:lang="hr">XZ arhiva</comment>
+ <comment xml:lang="hu">XZ-archívum</comment>
+ <comment xml:lang="ia">Archivo XZ</comment>
+ <comment xml:lang="id">Arsip XZ</comment>
+ <comment xml:lang="it">Archivio xz</comment>
+ <comment xml:lang="ja">XZ アーカイブ</comment>
+ <comment xml:lang="kk">XZ архиві</comment>
+ <comment xml:lang="ko">XZ 압축 파일</comment>
+ <comment xml:lang="lt">XZ archyvas</comment>
+ <comment xml:lang="lv">XZ arhīvs</comment>
+ <comment xml:lang="nl">XZ archief</comment>
+ <comment xml:lang="oc">archiu XZ</comment>
+ <comment xml:lang="pl">Archiwum XZ</comment>
+ <comment xml:lang="pt">arquivo XZ</comment>
+ <comment xml:lang="pt_BR">Pacote XZ</comment>
+ <comment xml:lang="ro">Arhivă XZ</comment>
+ <comment xml:lang="ru">Архив XZ</comment>
+ <comment xml:lang="sk">Archív XZ</comment>
+ <comment xml:lang="sl">Datoteka arhiva XZ</comment>
+ <comment xml:lang="sr">ИксЗ архива</comment>
+ <comment xml:lang="sv">XZ-arkiv</comment>
+ <comment xml:lang="tr">XZ arşivi</comment>
+ <comment xml:lang="uk">архів XZ</comment>
+ <comment xml:lang="zh_CN">XZ 归档文件</comment>
+ <comment xml:lang="zh_TW">XZ 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="\xfd\x37\x7a\x58\x5a\x00" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.xz"/>
+ </mime-type>
+ <mime-type type="application/x-xz-compressed-tar">
+ <comment>Tar archive (XZ-compressed)</comment>
+ <comment xml:lang="ar">أرشيف Tar (مضغوط-XZ)</comment>
+ <comment xml:lang="bg">Архив — tar, компресиран с XZ</comment>
+ <comment xml:lang="ca">arxiu tar (amb compressió XZ)</comment>
+ <comment xml:lang="cs">archiv Tar (komprimovaný pomocí XZ)</comment>
+ <comment xml:lang="da">Tar-arkiv (XZ-komprimeret)</comment>
+ <comment xml:lang="de">Tar-Archiv (XZ-komprimiert)</comment>
+ <comment xml:lang="el">Αρχείο Tar (συμπιεσμένο με XZ)</comment>
+ <comment xml:lang="en_GB">Tar archive (XZ-compressed)</comment>
+ <comment xml:lang="es">archivador Tar (comprimido con XZ)</comment>
+ <comment xml:lang="eu">Tar artxiboa (XZ-rekin konprimitua)</comment>
+ <comment xml:lang="fi">Tar-arkisto (XZ-pakattu)</comment>
+ <comment xml:lang="fo">Tar skjalasavn(XZ-stappað)</comment>
+ <comment xml:lang="fr">archive tar (compression XZ)</comment>
+ <comment xml:lang="ga">cartlann Tar (comhbhrúite le XZ)</comment>
+ <comment xml:lang="gl">arquivo Tar (comprimido con XZ)</comment>
+ <comment xml:lang="he">ארכיון Tar (מכווץ ע״י XZ)</comment>
+ <comment xml:lang="hr">Tar arhiva ( XZ sažeta)</comment>
+ <comment xml:lang="hu">Tar archívum (XZ-vel tömörítve)</comment>
+ <comment xml:lang="ia">Archivo Tar (comprimite con XZ)</comment>
+ <comment xml:lang="id">Arsip Tar (terkompresi XZ)</comment>
+ <comment xml:lang="it">Archivio tar (compresso con XZ)</comment>
+ <comment xml:lang="ja">Tar アーカイブ (XZ 圧縮)</comment>
+ <comment xml:lang="kk">Tar архиві (XZ-мен сығылған)</comment>
+ <comment xml:lang="ko">TAR 묶음 파일(XZ 압축)</comment>
+ <comment xml:lang="lt">Tar archyvas (suglaudintas su XZ)</comment>
+ <comment xml:lang="lv">Tar arhīvs (saspiests ar XZ)</comment>
+ <comment xml:lang="nl">Tar archief (XZ-compressed)</comment>
+ <comment xml:lang="oc">archiu tar (compression XZ)</comment>
+ <comment xml:lang="pl">Archiwum tar (kompresja XZ)</comment>
+ <comment xml:lang="pt">arquivo Tar (compressão XZ)</comment>
+ <comment xml:lang="pt_BR">Pacote Tar (compactado com XZ)</comment>
+ <comment xml:lang="ro">Arhivă Tar (comprimată XZ)</comment>
+ <comment xml:lang="ru">Архив TAR (сжатый xz)</comment>
+ <comment xml:lang="sk">Archív tar (komprimovaný pomocou XZ)</comment>
+ <comment xml:lang="sl">Datoteka arhiva Tar (stisnjen z XZ)</comment>
+ <comment xml:lang="sr">Тар архива (запакована ИксЗ-ом)</comment>
+ <comment xml:lang="sv">Tar-arkiv (XZ-komprimerat)</comment>
+ <comment xml:lang="tr">Tar arşivi (XZ ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">архів tar (стиснений XZ)</comment>
+ <comment xml:lang="zh_CN">Tar 归档文件(XZ 压缩)</comment>
+ <comment xml:lang="zh_TW">Tar 封存檔 (XZ 格式壓縮)</comment>
+ <sub-class-of type="application/x-xz"/>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.tar.xz"/>
+ <glob pattern="*.txz"/>
+ </mime-type>
+ <mime-type type="application/x-xzpdf">
+ <comment>PDF document (XZ-compressed)</comment>
+ <comment xml:lang="ast">Documentu PDF (comprimíu en XZ)</comment>
+ <comment xml:lang="bg">Документ — PDF, компресиран с XZ</comment>
+ <comment xml:lang="ca">document PDF (amb compressió XZ)</comment>
+ <comment xml:lang="cs">dokument PDF (komprimovaný pomocí XZ)</comment>
+ <comment xml:lang="da">PDF-dokument (XZ-komprimeret)</comment>
+ <comment xml:lang="de">PDF-Dokument (XZ-komprimiert)</comment>
+ <comment xml:lang="el">Έγγραφο PDF (συμπιεσμένο με XZ)</comment>
+ <comment xml:lang="en_GB">PDF document (XZ-compressed)</comment>
+ <comment xml:lang="es">documento PDF (comprimido con XZ)</comment>
+ <comment xml:lang="eu">PDF dokumentua (XZ-rekin konprimitua)</comment>
+ <comment xml:lang="fi">PDF-asiakirja (XZ-pakattu)</comment>
+ <comment xml:lang="fr">document PDF (compressé XZ)</comment>
+ <comment xml:lang="ga">cáipéis PDF (comhbhrúite le XZ)</comment>
+ <comment xml:lang="gl">documento PDF (comprimido en XZ)</comment>
+ <comment xml:lang="he">מסמך PDF (מכווץ ע״י XZ)</comment>
+ <comment xml:lang="hr">PDF dokument ( XZ sažet)</comment>
+ <comment xml:lang="hu">PDF dokumentum (XZ-vel tömörített)</comment>
+ <comment xml:lang="ia">Documento PDF (comprimite con XZ)</comment>
+ <comment xml:lang="id">Dokumen PDF (terkompresi XZ)</comment>
+ <comment xml:lang="it">Documento PDF (compresso con XZ)</comment>
+ <comment xml:lang="ja">PDF 文書(XZ 圧縮)</comment>
+ <comment xml:lang="ka">PDF დოკუმენტი (XZ-ით შეკუმშული)</comment>
+ <comment xml:lang="kk">PDF құжаты (XZ-мен сығылған)</comment>
+ <comment xml:lang="ko">PDF 문서(XZ 압축)</comment>
+ <comment xml:lang="lv">PDF dokuments (saspiests ar XZ)</comment>
+ <comment xml:lang="nl">PDF document (XZ-compressed)</comment>
+ <comment xml:lang="oc">document PDF (compressat XZ)</comment>
+ <comment xml:lang="pl">Dokument PDF (kompresja XZ)</comment>
+ <comment xml:lang="pt">documento PDF (compressão XZ)</comment>
+ <comment xml:lang="pt_BR">Documento PDF (compactado com XZ)</comment>
+ <comment xml:lang="ru">Документ PDF (сжатый xz)</comment>
+ <comment xml:lang="sk">Dokument PDF (komprimovaný pomocou XZ)</comment>
+ <comment xml:lang="sl">Dokument PDF (XZ-stisnjen)</comment>
+ <comment xml:lang="sr">ПДФ документ (запакован ИксЗ-ом)</comment>
+ <comment xml:lang="sv">PDF-dokument (XZ-komprimerat)</comment>
+ <comment xml:lang="tr">PDF belgesi (XZ ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">документ PDF (стиснений xz)</comment>
+ <comment xml:lang="zh_CN">PDF 文档(XZ)</comment>
+ <comment xml:lang="zh_TW">PDF 文件 (XZ 格式壓縮)</comment>
+ <sub-class-of type="application/x-xz"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.pdf.xz"/>
+ </mime-type>
+ <mime-type type="application/x-ustar">
+ <comment>Ustar archive</comment>
+ <comment xml:lang="ar">أرشيف Ustar</comment>
+ <comment xml:lang="be@latin">Archiŭ ustar</comment>
+ <comment xml:lang="bg">Архив — ustar</comment>
+ <comment xml:lang="ca">arxiu ustar</comment>
+ <comment xml:lang="cs">archiv Ustar</comment>
+ <comment xml:lang="da">Ustararkiv</comment>
+ <comment xml:lang="de">Ustar-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Ustar</comment>
+ <comment xml:lang="en_GB">Ustar archive</comment>
+ <comment xml:lang="eo">Ustar-arkivo</comment>
+ <comment xml:lang="es">archivador de Ustar</comment>
+ <comment xml:lang="eu">Ustar artxiboa</comment>
+ <comment xml:lang="fi">Ustar-arkisto</comment>
+ <comment xml:lang="fo">Ustar skjalasavn</comment>
+ <comment xml:lang="fr">archive Ustar</comment>
+ <comment xml:lang="ga">cartlann Ustar</comment>
+ <comment xml:lang="gl">arquivo Ustar</comment>
+ <comment xml:lang="he">ארכיון Ustar</comment>
+ <comment xml:lang="hr">Ustar arhiva</comment>
+ <comment xml:lang="hu">Ustar archívum</comment>
+ <comment xml:lang="ia">Archivo Ustar</comment>
+ <comment xml:lang="id">Arsip Ustar</comment>
+ <comment xml:lang="it">Archivio ustar</comment>
+ <comment xml:lang="ja">Ustar アーカイブ</comment>
+ <comment xml:lang="kk">Ustar архиві</comment>
+ <comment xml:lang="ko">Ustar 압축 파일</comment>
+ <comment xml:lang="lt">Ustar archyvas</comment>
+ <comment xml:lang="lv">Ustar arhīvs</comment>
+ <comment xml:lang="nb">Ustar-arkiv</comment>
+ <comment xml:lang="nl">Ustar-archief</comment>
+ <comment xml:lang="nn">Ustar-arkiv</comment>
+ <comment xml:lang="oc">archiu Ustar</comment>
+ <comment xml:lang="pl">Archiwum ustar</comment>
+ <comment xml:lang="pt">arquivo Ustar</comment>
+ <comment xml:lang="pt_BR">Pacote Ustar</comment>
+ <comment xml:lang="ro">Arhivă Ustar</comment>
+ <comment xml:lang="ru">Архив Ustar</comment>
+ <comment xml:lang="sk">Archív ustar</comment>
+ <comment xml:lang="sl">Datoteka arhiva Ustar</comment>
+ <comment xml:lang="sq">Arkiv Ustar</comment>
+ <comment xml:lang="sr">Устар архива</comment>
+ <comment xml:lang="sv">Ustar-arkiv</comment>
+ <comment xml:lang="tr">Ustar arşivi</comment>
+ <comment xml:lang="uk">архів ustar</comment>
+ <comment xml:lang="vi">Kho nén ustar</comment>
+ <comment xml:lang="zh_CN">Ustar 归档文件</comment>
+ <comment xml:lang="zh_TW">Ustar 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <glob pattern="*.ustar"/>
+ </mime-type>
+ <mime-type type="application/x-wais-source">
+ <comment>WAIS source code</comment>
+ <comment xml:lang="ar">شفرة مصدر WAIS</comment>
+ <comment xml:lang="az">WAIS mənbə faylı</comment>
+ <comment xml:lang="be@latin">Kryničny kod WAIS</comment>
+ <comment xml:lang="bg">Изходен код — WAIS</comment>
+ <comment xml:lang="ca">codi font en WAIS</comment>
+ <comment xml:lang="cs">zdrojový kód WAIS</comment>
+ <comment xml:lang="cy">Ffynhonnell Rhaglen WAIS</comment>
+ <comment xml:lang="da">WAIS-kildekode</comment>
+ <comment xml:lang="de">WAIS-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας WAIS</comment>
+ <comment xml:lang="en_GB">WAIS source code</comment>
+ <comment xml:lang="eo">WAIS-fontkodo</comment>
+ <comment xml:lang="es">código fuente en WAIS</comment>
+ <comment xml:lang="eu">WAIS iturburu-kodea</comment>
+ <comment xml:lang="fi">WAIS-lähdekoodi</comment>
+ <comment xml:lang="fo">WAIS keldukota</comment>
+ <comment xml:lang="fr">code source WAIS</comment>
+ <comment xml:lang="ga">cód foinseach WAIS</comment>
+ <comment xml:lang="gl">código fonte WAIS</comment>
+ <comment xml:lang="he">קוד מקור של WAIS</comment>
+ <comment xml:lang="hr">WAIS izvorni kôd</comment>
+ <comment xml:lang="hu">WAIS-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte WAIS</comment>
+ <comment xml:lang="id">Kode program WAIS</comment>
+ <comment xml:lang="it">Codice sorgente WAIS</comment>
+ <comment xml:lang="ja">WAIS ソースコード</comment>
+ <comment xml:lang="kk">WAIS бастапқы коды</comment>
+ <comment xml:lang="ko">WAIS 소스 코드</comment>
+ <comment xml:lang="lt">WAIS pradinis kodas</comment>
+ <comment xml:lang="lv">WAIS pirmkods</comment>
+ <comment xml:lang="ms">Kod sumber WAIS</comment>
+ <comment xml:lang="nb">WAIS-kildekode</comment>
+ <comment xml:lang="nl">WAIS-broncode</comment>
+ <comment xml:lang="nn">WAIS-kjeldekode</comment>
+ <comment xml:lang="oc">còde font WAIS</comment>
+ <comment xml:lang="pl">Plik źródłowy WAIS</comment>
+ <comment xml:lang="pt">código origem WAIS</comment>
+ <comment xml:lang="pt_BR">Código-fonte WAIS</comment>
+ <comment xml:lang="ro">Cod sursă WAIS</comment>
+ <comment xml:lang="ru">Исходный код WAIS</comment>
+ <comment xml:lang="sk">Zdrojový kód WAIS</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode WAIS</comment>
+ <comment xml:lang="sq">Kod burues WAIS</comment>
+ <comment xml:lang="sr">ВАИС изворни ко̂д</comment>
+ <comment xml:lang="sv">WAIS-källkod</comment>
+ <comment xml:lang="tr">WAIS kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою WAIS</comment>
+ <comment xml:lang="vi">Mã nguồn WAIS</comment>
+ <comment xml:lang="zh_CN">WAIS 源代码</comment>
+ <comment xml:lang="zh_TW">WAIS 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.src"/>
+ </mime-type>
+ <mime-type type="application/x-wpg">
+ <comment>WordPerfect/Drawperfect image</comment>
+ <comment xml:lang="ar">صورة WordPerfect/Drawperfect</comment>
+ <comment xml:lang="be@latin">Vyjava WordPerfect/Drawperfect</comment>
+ <comment xml:lang="bg">Изображение — WordPerfect/Drawperfect</comment>
+ <comment xml:lang="ca">imatge de WordPerfect/Drawperfect</comment>
+ <comment xml:lang="cs">obrázek WordPerfect/Drawperfect</comment>
+ <comment xml:lang="da">WordPerfect/Drawperfect-billede</comment>
+ <comment xml:lang="de">WordPerfect/DrawPerfect-Bild</comment>
+ <comment xml:lang="el">Εικόνα WordPerfect/Drawperfect</comment>
+ <comment xml:lang="en_GB">WordPerfect/Drawperfect image</comment>
+ <comment xml:lang="eo">WordPerfect/Drawperfect-bildo</comment>
+ <comment xml:lang="es">imagen de WordPerfect/Drawperfect</comment>
+ <comment xml:lang="eu">WordPerfect/Drawperfect irudia</comment>
+ <comment xml:lang="fi">WordPerfect/Drawperfect-kuva</comment>
+ <comment xml:lang="fo">WordPerfect/Drawperfect mynd</comment>
+ <comment xml:lang="fr">image WordPerfect/DrawPerfect</comment>
+ <comment xml:lang="ga">íomhá WordPerfect/Drawperfect</comment>
+ <comment xml:lang="gl">imaxe de WordPerfect/DrawPerfect</comment>
+ <comment xml:lang="he">תמונה של WordPerfect/Drawperfect</comment>
+ <comment xml:lang="hr">WordPerfect/Drawperfect slika</comment>
+ <comment xml:lang="hu">WordPerfect/Drawperfect-kép</comment>
+ <comment xml:lang="ia">Imagine WordPerfect/DrawPerfect</comment>
+ <comment xml:lang="id">Gambar WordPerfect/Drawperfect</comment>
+ <comment xml:lang="it">Immagine WordPerfect/Drawperfect</comment>
+ <comment xml:lang="ja">WordPerfect/Drawperfect 画像</comment>
+ <comment xml:lang="kk">WordPerfect/Drawperfect суреті</comment>
+ <comment xml:lang="ko">WordPerfect/Drawperfect 그림</comment>
+ <comment xml:lang="lt">WordPerfect/Drawperfect paveikslėlis</comment>
+ <comment xml:lang="lv">WordPerfect/Drawperfect attēls</comment>
+ <comment xml:lang="ms">Imej WordPerfect/Drawperfect</comment>
+ <comment xml:lang="nb">WordPerfect-/Drawperfect-bilde</comment>
+ <comment xml:lang="nl">WordPerfect/Drawperfect-afbeelding</comment>
+ <comment xml:lang="nn">WordPerfect/DrawPerfect-bilete</comment>
+ <comment xml:lang="oc">imatge WordPerfect/DrawPerfect</comment>
+ <comment xml:lang="pl">Obraz WordPerfect/DrawPerfect</comment>
+ <comment xml:lang="pt">imagem do WordPerfect/Drawperfect</comment>
+ <comment xml:lang="pt_BR">Imagem do WordPerfect/Drawperfect</comment>
+ <comment xml:lang="ro">Imagine WordPerfect/Drawperfect</comment>
+ <comment xml:lang="ru">Изображение WordPerfect/Drawperfect</comment>
+ <comment xml:lang="sk">Obrázok WordPerfect/Drawperfect</comment>
+ <comment xml:lang="sl">Slikovna datoteka Drawperfect</comment>
+ <comment xml:lang="sq">Figurë WordPerfect/Drawperfect</comment>
+ <comment xml:lang="sr">Ворд Перфект/Дров Перфект слика</comment>
+ <comment xml:lang="sv">WordPerfect/Drawperfect-bild</comment>
+ <comment xml:lang="tr">WordPerfect/DrawPerfect görüntüsü</comment>
+ <comment xml:lang="uk">зображення WordPerfect/Drawperfect</comment>
+ <comment xml:lang="vi">Ảnh WordPerfect/Drawperfect</comment>
+ <comment xml:lang="zh_CN">WordPerfect/Drawperfect 图像</comment>
+ <comment xml:lang="zh_TW">WordPerfect/Drawperfect 影像</comment>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.wpg"/>
+ </mime-type>
+ <mime-type type="application/x-wonderswan-rom">
+ <comment>Bandai WonderSwan ROM</comment>
+ <comment xml:lang="ca">ROM de Bandai WonderSwan</comment>
+ <comment xml:lang="cs">ROM pro Bandai WonderSwan</comment>
+ <comment xml:lang="de">Bandai WonderSwan ROM</comment>
+ <comment xml:lang="en_GB">Bandai WonderSwan ROM</comment>
+ <comment xml:lang="es">ROM de Bandai WonderSwan</comment>
+ <comment xml:lang="hr">Bandai WonderSwan ROM</comment>
+ <comment xml:lang="hu">Bandai WonderSwan ROM</comment>
+ <comment xml:lang="id">ROM Bandai WonderSwan</comment>
+ <comment xml:lang="it">ROM Bandai WonderSwan</comment>
+ <comment xml:lang="kk">Bandai WonderSwan ROM</comment>
+ <comment xml:lang="ko">반다이 원더스완 롬</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Bandai WonderSwan</comment>
+ <comment xml:lang="pt_BR">ROM de WonderSwan da Bandai</comment>
+ <comment xml:lang="ru">Bandai WonderSwan ROM</comment>
+ <comment xml:lang="sk">ROM pre Bandai WonderSwan</comment>
+ <comment xml:lang="sv">Bandai WonderSwan-rom</comment>
+ <comment xml:lang="uk">ROM Bandai WonderSwan</comment>
+ <comment xml:lang="zh_CN">万代 WonderSwan ROM</comment>
+ <comment xml:lang="zh_TW">Bandai WonderSwan ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.ws"/>
+ </mime-type>
+ <mime-type type="application/x-wonderswan-color-rom">
+ <comment>Bandai WonderSwan Color ROM</comment>
+ <comment xml:lang="ca">ROM de Bandai WonderSwan Color</comment>
+ <comment xml:lang="cs">ROM pro Bandai WonderSwan Color</comment>
+ <comment xml:lang="de">Bandai WonderSwan Color ROM</comment>
+ <comment xml:lang="en_GB">Bandai WonderSwan Color ROM</comment>
+ <comment xml:lang="es">ROM de Bandai WonderSwan Color</comment>
+ <comment xml:lang="hr">Bandai WonderSwan Color ROM</comment>
+ <comment xml:lang="hu">Bandai WonderSwan Color ROM</comment>
+ <comment xml:lang="id">ROM Bandai WonderSwan Color</comment>
+ <comment xml:lang="it">ROM Bandai WonderSwan Color</comment>
+ <comment xml:lang="kk">Bandai WonderSwan Color ROM</comment>
+ <comment xml:lang="ko">반다이 원더스완 컬러 롬</comment>
+ <comment xml:lang="pl">Plik ROM konsoli Bandai WonderSwan Color</comment>
+ <comment xml:lang="pt_BR">ROM de WonderSwan Color da Bandai</comment>
+ <comment xml:lang="ru">Bandai WonderSwan Color ROM</comment>
+ <comment xml:lang="sk">ROM pre Bandai WonderSwan Color</comment>
+ <comment xml:lang="sv">Bandai WonderSwan Color-rom</comment>
+ <comment xml:lang="uk">ROM Bandai WonderSwan Color</comment>
+ <comment xml:lang="zh_CN">万代 WonderSwan Color ROM</comment>
+ <comment xml:lang="zh_TW">Bandai WonderSwan Color ROM</comment>
+ <generic-icon name="application-x-executable"/>
+ <glob pattern="*.wsc"/>
+ </mime-type>
+ <mime-type type="application/x-x509-ca-cert">
+ <comment>DER/PEM/Netscape-encoded X.509 certificate</comment>
+ <comment xml:lang="ar">شهادة DER/PEM/Netscape-encoded X.509</comment>
+ <comment xml:lang="be@latin">Sertyfikat X.509, zakadavany ŭ DER/PEM/Netscape</comment>
+ <comment xml:lang="bg">Сертификат — DER/PEM/Netscape X.509</comment>
+ <comment xml:lang="ca">certificat X.509 codificat com DER/PEM/Netscape</comment>
+ <comment xml:lang="cs">certifikát X.509 kódovaný jako DER/PEM/Netscape</comment>
+ <comment xml:lang="da">DER-/PEM-/Netscapekodet X.509-certifikat</comment>
+ <comment xml:lang="de">DER/PEM/Netscape-kodiertes X.509-Zertifikat</comment>
+ <comment xml:lang="el">Ψηφιακό πιστοποιητικό X.509 κωδικοποιημένο κατά DER/PEM/Netscape</comment>
+ <comment xml:lang="en_GB">DER/PEM/Netscape-encoded X.509 certificate</comment>
+ <comment xml:lang="eo">DER/PEM/Netscape-kodigita X.509-atestilo</comment>
+ <comment xml:lang="es">certificado X.509 codificado con DER/PEM/Netscape</comment>
+ <comment xml:lang="eu">X.509rekin kodetutako DER, PEM edo Netscape zertifikatua</comment>
+ <comment xml:lang="fi">DER/PEM/Netscape-koodattu X.509-varmenne</comment>
+ <comment xml:lang="fo">DER/PEM/Netscape-encoded X.509 váttan</comment>
+ <comment xml:lang="fr">certificat X.509 codé DER/PEM/Netscape</comment>
+ <comment xml:lang="ga">teastas X.509 ionchódaithe le DER/PEM/Netscape</comment>
+ <comment xml:lang="gl">certificado X.509 codificado con DER/PEM/Netscape</comment>
+ <comment xml:lang="he">אישור מסוג X.509 של DER/PEM/Netscape-encoded</comment>
+ <comment xml:lang="hr">DER/PEM/Netscape-kodiran X.509 vjerodajnica</comment>
+ <comment xml:lang="hu">DER/PEM/Netscape formátumú X.509-tanúsítvány</comment>
+ <comment xml:lang="ia">Certificato X.509 codificate in DER/PEM/Netscape</comment>
+ <comment xml:lang="id">Sertifikat DER/PEM/Netscape-tersandi X.509</comment>
+ <comment xml:lang="it">Certificato X.509 DER/PEM/Netscape</comment>
+ <comment xml:lang="ja">DER/PEM/Netscape エンコード X.509 証明書</comment>
+ <comment xml:lang="ka">DER/PEM/Netscape კოდირებული X.509 სერტიფიკატი</comment>
+ <comment xml:lang="kk">X.509 сертификаты (DER/PEM/Netscape кодталған)</comment>
+ <comment xml:lang="ko">DER/PEM/넷스케이프로 인코딩된 X.509 인증서</comment>
+ <comment xml:lang="lt">DER/PEM/Netscape-encoded X.509 liudijimas</comment>
+ <comment xml:lang="lv">DER/PEM/Netscape-encoded X.509 sertifikāts</comment>
+ <comment xml:lang="ms">Sijil X.509 dienkod /DER/PEM/Netscape</comment>
+ <comment xml:lang="nb">DER/PEM/Netscape-kodet X.509-sertifikat</comment>
+ <comment xml:lang="nl">DER/PEM/Netscape-gecodeerd X.509-certificaat</comment>
+ <comment xml:lang="nn">DER/PEM/Netscape-koda X.509-sertifikat</comment>
+ <comment xml:lang="oc">certificat X.509 encodat DER/PEM/Netscape</comment>
+ <comment xml:lang="pl">Zakodowany w DER/PEM/Netscape certyfikat X.509</comment>
+ <comment xml:lang="pt">certificado X.509 codificado com DER/PEM/Netscape</comment>
+ <comment xml:lang="pt_BR">Certificado X.509 codificado com DER/PEM/Netscape</comment>
+ <comment xml:lang="ro">Certificat DER/PEM/Netscape-codat X.509</comment>
+ <comment xml:lang="ru">Сертификат X.509 (DER/PEM/Netscape-закодированный)</comment>
+ <comment xml:lang="sk">Certifikát X.509 kódovaný ako DER/PEM/Netscape</comment>
+ <comment xml:lang="sl">Datoteka potrdila DER/PEM/Netscape X.509</comment>
+ <comment xml:lang="sq">Çertifikatë DER/PEM/Netscape-encoded X.509</comment>
+ <comment xml:lang="sr">ДЕР/ПЕМ/Нетскејп кодирано уверење Икс.509</comment>
+ <comment xml:lang="sv">DER/PEM/Netscape-kodat X.509-certifikat</comment>
+ <comment xml:lang="tr">DER/PEM/Netscape-kodlanmış X.509 sertfikası</comment>
+ <comment xml:lang="uk">сертифікат X.509 у форматі DER/PEM/Netscape</comment>
+ <comment xml:lang="vi">Chứng nhận X.509 mã hoá bằng Netscape/PEM/DER</comment>
+ <comment xml:lang="zh_CN">DER/PEM/Netscape-encoded X.509 证书</comment>
+ <comment xml:lang="zh_TW">DER/PEM/Netscape 編碼的 X.509 憑證</comment>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.der"/>
+ <glob pattern="*.crt"/>
+ <glob pattern="*.cert"/>
+ <glob pattern="*.pem"/>
+ </mime-type>
+ <mime-type type="application/x-zerosize">
+ <comment>empty document</comment>
+ <comment xml:lang="ar">مستند فارغ</comment>
+ <comment xml:lang="ast">documentu baleru</comment>
+ <comment xml:lang="be@latin">pusty dakument</comment>
+ <comment xml:lang="bg">Празен документ</comment>
+ <comment xml:lang="ca">document buit</comment>
+ <comment xml:lang="cs">prázdný dokument</comment>
+ <comment xml:lang="da">tomt dokument</comment>
+ <comment xml:lang="de">Leeres Dokument</comment>
+ <comment xml:lang="el">Κενό έγγραφο</comment>
+ <comment xml:lang="en_GB">empty document</comment>
+ <comment xml:lang="eo">malplena dokumento</comment>
+ <comment xml:lang="es">documento vacío</comment>
+ <comment xml:lang="eu">dokumentu hutsa</comment>
+ <comment xml:lang="fi">tyhjä asiakirja</comment>
+ <comment xml:lang="fo">tómt skjal</comment>
+ <comment xml:lang="fr">document vide</comment>
+ <comment xml:lang="ga">cáipéis fholamh</comment>
+ <comment xml:lang="gl">documeto baleiro</comment>
+ <comment xml:lang="he">מסמך ריק</comment>
+ <comment xml:lang="hr">Prazan dokument</comment>
+ <comment xml:lang="hu">üres dokumentum</comment>
+ <comment xml:lang="ia">Documento vacue</comment>
+ <comment xml:lang="id">dokumen kosong</comment>
+ <comment xml:lang="it">Documento vuoto</comment>
+ <comment xml:lang="ja">空のドキュメント</comment>
+ <comment xml:lang="kk">бос құжат</comment>
+ <comment xml:lang="ko">빈 문서</comment>
+ <comment xml:lang="lt">tuščias dokumentas</comment>
+ <comment xml:lang="lv">tukšs dokuments</comment>
+ <comment xml:lang="ms">Dokumen kosong</comment>
+ <comment xml:lang="nb">tomt dokument</comment>
+ <comment xml:lang="nl">leeg document</comment>
+ <comment xml:lang="nn">tomt dokument</comment>
+ <comment xml:lang="oc">document void</comment>
+ <comment xml:lang="pl">Pusty dokument</comment>
+ <comment xml:lang="pt">documento vazio</comment>
+ <comment xml:lang="pt_BR">Documento vazio</comment>
+ <comment xml:lang="ro">document gol</comment>
+ <comment xml:lang="ru">Пустой документ</comment>
+ <comment xml:lang="sk">Prázdny dokument</comment>
+ <comment xml:lang="sl">prazen dokument</comment>
+ <comment xml:lang="sq">Dokument bosh</comment>
+ <comment xml:lang="sr">празан документ</comment>
+ <comment xml:lang="sv">tomt dokument</comment>
+ <comment xml:lang="tr">boş belge</comment>
+ <comment xml:lang="uk">порожній документ</comment>
+ <comment xml:lang="vi">tài liệu rỗng</comment>
+ <comment xml:lang="zh_CN">空文档</comment>
+ <comment xml:lang="zh_TW">空白文件</comment>
+ </mime-type>
+ <mime-type type="application/x-zoo">
+ <comment>Zoo archive</comment>
+ <comment xml:lang="ar">أرشيف Zoo</comment>
+ <comment xml:lang="az">Zoo arxivi</comment>
+ <comment xml:lang="be@latin">Archiŭ zoo</comment>
+ <comment xml:lang="bg">Архив — zoo</comment>
+ <comment xml:lang="ca">arxiu zoo</comment>
+ <comment xml:lang="cs">archiv Zoo</comment>
+ <comment xml:lang="cy">Archif zoo</comment>
+ <comment xml:lang="da">Zooarkiv</comment>
+ <comment xml:lang="de">Zoo-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Zoo</comment>
+ <comment xml:lang="en_GB">Zoo archive</comment>
+ <comment xml:lang="eo">Zoo-arkivo</comment>
+ <comment xml:lang="es">archivador Zoo</comment>
+ <comment xml:lang="eu">Zoo artxiboa</comment>
+ <comment xml:lang="fi">Zoo-arkisto</comment>
+ <comment xml:lang="fo">Zoo skjalasavn</comment>
+ <comment xml:lang="fr">archive zoo</comment>
+ <comment xml:lang="ga">cartlann Zoo</comment>
+ <comment xml:lang="gl">ficheiro Zoo</comment>
+ <comment xml:lang="he">ארכיון Zoo</comment>
+ <comment xml:lang="hr">Zoo arhiva</comment>
+ <comment xml:lang="hu">Zoo archívum</comment>
+ <comment xml:lang="ia">Archivo Zoo</comment>
+ <comment xml:lang="id">Arsip Zoo</comment>
+ <comment xml:lang="it">Archivio zoo</comment>
+ <comment xml:lang="ja">Zoo アーカイブ</comment>
+ <comment xml:lang="kk">Zoo архиві</comment>
+ <comment xml:lang="ko">ZOO 압축 파일</comment>
+ <comment xml:lang="lt">Zoo archyvas</comment>
+ <comment xml:lang="lv">Zoo arhīvs</comment>
+ <comment xml:lang="nb">Zoo-arkiv</comment>
+ <comment xml:lang="nl">Zoo-archief</comment>
+ <comment xml:lang="nn">Zoo-arkiv</comment>
+ <comment xml:lang="oc">archiu zoo</comment>
+ <comment xml:lang="pl">Archiwum zoo</comment>
+ <comment xml:lang="pt">arquivo Zoo</comment>
+ <comment xml:lang="pt_BR">Pacote Zoo</comment>
+ <comment xml:lang="ro">Arhivă Zoo</comment>
+ <comment xml:lang="ru">Архив ZOO</comment>
+ <comment xml:lang="sk">Archív zoo</comment>
+ <comment xml:lang="sl">Datoteka arhiva ZOO</comment>
+ <comment xml:lang="sq">Arkiv zoo</comment>
+ <comment xml:lang="sr">Зoo архива</comment>
+ <comment xml:lang="sv">Zoo-arkiv</comment>
+ <comment xml:lang="tr">Zoo arşivi</comment>
+ <comment xml:lang="uk">архів zoo</comment>
+ <comment xml:lang="vi">Kho nén zoo</comment>
+ <comment xml:lang="zh_CN">Zoo 归档文件</comment>
+ <comment xml:lang="zh_TW">Zoo 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="0xfdc4a7dc" type="little32" offset="20"/>
+ </magic>
+ <glob pattern="*.zoo"/>
+ </mime-type>
+ <mime-type type="application/xhtml+xml">
+ <comment>XHTML page</comment>
+ <comment xml:lang="ar">صفحة XHTML</comment>
+ <comment xml:lang="be@latin">Staronka XHTML</comment>
+ <comment xml:lang="bg">Страница — XHTML</comment>
+ <comment xml:lang="ca">pàgina XHTML</comment>
+ <comment xml:lang="cs">stránka XHTML</comment>
+ <comment xml:lang="da">XHTML-side</comment>
+ <comment xml:lang="de">XHTML-Seite</comment>
+ <comment xml:lang="el">Σελίδα XHTML</comment>
+ <comment xml:lang="en_GB">XHTML page</comment>
+ <comment xml:lang="eo">XHTML-paĝo</comment>
+ <comment xml:lang="es">página XHTML</comment>
+ <comment xml:lang="eu">XHTML orria</comment>
+ <comment xml:lang="fi">XHTML-sivu</comment>
+ <comment xml:lang="fo">XHTML síða</comment>
+ <comment xml:lang="fr">page XHTML</comment>
+ <comment xml:lang="ga">leathanach XHTML</comment>
+ <comment xml:lang="gl">Páxina XHTML</comment>
+ <comment xml:lang="he">דף XHTML</comment>
+ <comment xml:lang="hr">XHTML stranica</comment>
+ <comment xml:lang="hu">XHTML-oldal</comment>
+ <comment xml:lang="ia">Pagina XHTML</comment>
+ <comment xml:lang="id">Halaman XHTML</comment>
+ <comment xml:lang="it">Pagina XHTML</comment>
+ <comment xml:lang="ja">XHTML ページ</comment>
+ <comment xml:lang="kk">XHTML парағы</comment>
+ <comment xml:lang="ko">XHTML 페이지</comment>
+ <comment xml:lang="lt">XHTML puslapis</comment>
+ <comment xml:lang="lv">XHTML lapa</comment>
+ <comment xml:lang="ms">Laman XHTML</comment>
+ <comment xml:lang="nb">XHTML-side</comment>
+ <comment xml:lang="nl">XHTML-pagina</comment>
+ <comment xml:lang="nn">XHTML-side</comment>
+ <comment xml:lang="oc">pagina XHTML</comment>
+ <comment xml:lang="pl">Strona XHTML</comment>
+ <comment xml:lang="pt">página XHTML</comment>
+ <comment xml:lang="pt_BR">Página XHTML</comment>
+ <comment xml:lang="ro">Pagină XHTML</comment>
+ <comment xml:lang="ru">Страница XHTML</comment>
+ <comment xml:lang="sk">Stránka XHTML</comment>
+ <comment xml:lang="sl">Datoteka spletne strani XHTML</comment>
+ <comment xml:lang="sq">Faqe XHTML</comment>
+ <comment xml:lang="sr">ИксХТМЛ страница</comment>
+ <comment xml:lang="sv">XHTML-sida</comment>
+ <comment xml:lang="tr">XHTML sayfası</comment>
+ <comment xml:lang="uk">сторінка XHTML</comment>
+ <comment xml:lang="vi">Trang XHTML</comment>
+ <comment xml:lang="zh_CN">XHTML 页面</comment>
+ <comment xml:lang="zh_TW">XHTML 網頁</comment>
+ <acronym>XHTML</acronym>
+ <expanded-acronym>Extensible HyperText Markup Language</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="text-html"/>
+ <glob pattern="*.xhtml"/>
+ <glob pattern="*.xht"/>
+ <magic priority="60">
+ <match value="//W3C//DTD XHTML " type="string" offset="0:256"/>
+ <match value="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" type="string" offset="0:256"/>
+ <match value="&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml" type="string" offset="0:256"/>
+ <match value="&lt;HTML xmlns=&quot;http://www.w3.org/1999/xhtml" type="string" offset="0:256"/>
+ </magic>
+ <root-XML namespaceURI='http://www.w3.org/1999/xhtml' localName='html'/>
+ </mime-type>
+ <mime-type type="application/zip">
+ <comment>Zip archive</comment>
+ <comment xml:lang="ar">أرشيف Zip</comment>
+ <comment xml:lang="az">Zip arxivi</comment>
+ <comment xml:lang="be@latin">Archiŭ zip</comment>
+ <comment xml:lang="bg">Архив — zip</comment>
+ <comment xml:lang="ca">arxiu zip</comment>
+ <comment xml:lang="cs">archiv ZIP</comment>
+ <comment xml:lang="cy">Archif ZIP</comment>
+ <comment xml:lang="da">Ziparkiv</comment>
+ <comment xml:lang="de">Zip-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο Zip</comment>
+ <comment xml:lang="en_GB">Zip archive</comment>
+ <comment xml:lang="eo">Zip-arkivo</comment>
+ <comment xml:lang="es">archivador Zip</comment>
+ <comment xml:lang="eu">Zip artxiboa</comment>
+ <comment xml:lang="fi">Zip-arkisto</comment>
+ <comment xml:lang="fo">Zip skjalasavn</comment>
+ <comment xml:lang="fr">archive zip</comment>
+ <comment xml:lang="ga">cartlann Zip</comment>
+ <comment xml:lang="gl">ficheiro Zip</comment>
+ <comment xml:lang="he">ארכיון Zip</comment>
+ <comment xml:lang="hr">Zip arhiva</comment>
+ <comment xml:lang="hu">Zip archívum</comment>
+ <comment xml:lang="ia">Archivo Zip</comment>
+ <comment xml:lang="id">Arsip Zip</comment>
+ <comment xml:lang="it">Archivio zip</comment>
+ <comment xml:lang="ja">Zip アーカイブ</comment>
+ <comment xml:lang="kk">Zip архиві</comment>
+ <comment xml:lang="ko">ZIP 압축 파일</comment>
+ <comment xml:lang="lt">Zip archyvas</comment>
+ <comment xml:lang="lv">Zip arhīvs</comment>
+ <comment xml:lang="nb">Zip-arkiv</comment>
+ <comment xml:lang="nl">Zip-archief</comment>
+ <comment xml:lang="nn">Zip-arkiv</comment>
+ <comment xml:lang="oc">archiu zip</comment>
+ <comment xml:lang="pl">Archiwum ZIP</comment>
+ <comment xml:lang="pt">arquivo Zip</comment>
+ <comment xml:lang="pt_BR">Pacote Zip</comment>
+ <comment xml:lang="ro">Arhivă zip</comment>
+ <comment xml:lang="ru">Архив ZIP</comment>
+ <comment xml:lang="sk">Archív ZIP</comment>
+ <comment xml:lang="sl">Datoteka arhiva ZIP</comment>
+ <comment xml:lang="sq">Arkiv zip</comment>
+ <comment xml:lang="sr">Зип архива</comment>
+ <comment xml:lang="sv">Zip-arkiv</comment>
+ <comment xml:lang="tr">Zip arşivi</comment>
+ <comment xml:lang="uk">архів zip</comment>
+ <comment xml:lang="vi">Kho nén zip</comment>
+ <comment xml:lang="zh_CN">Zip 归档文件</comment>
+ <comment xml:lang="zh_TW">Zip 封存檔</comment>
+ <alias type="application/x-zip-compressed"/>
+ <alias type="application/x-zip"/>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="PK\003\004" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.zip"/>
+ </mime-type>
+ <mime-type type="application/x-ms-wim">
+ <comment>WIM disk Image</comment>
+ <comment xml:lang="ca">imatge de disc WIM</comment>
+ <comment xml:lang="cs">obraz disku WIM</comment>
+ <comment xml:lang="da">WIM-diskaftryk</comment>
+ <comment xml:lang="de">WIM-Datenträgerabbild</comment>
+ <comment xml:lang="el">Εικόνα δίσκου WIM</comment>
+ <comment xml:lang="en_GB">WIM disk Image</comment>
+ <comment xml:lang="es">imagen de disco WIM</comment>
+ <comment xml:lang="eu">WIM disko irudia</comment>
+ <comment xml:lang="fi">WIM-levytiedosto</comment>
+ <comment xml:lang="fr">image disque WIM</comment>
+ <comment xml:lang="ga">íomhá diosca WIM</comment>
+ <comment xml:lang="he">דמות דיסק WIM</comment>
+ <comment xml:lang="hr">WIM slika diska</comment>
+ <comment xml:lang="hu">WIM lemezkép</comment>
+ <comment xml:lang="ia">Imagine de disco WIM</comment>
+ <comment xml:lang="id">Image disk WIM</comment>
+ <comment xml:lang="it">Immagine disco WIM</comment>
+ <comment xml:lang="kk">WIM диск бейнесі</comment>
+ <comment xml:lang="ko">WIM 디스크 이미지</comment>
+ <comment xml:lang="oc">imatge disc WIM</comment>
+ <comment xml:lang="pl">Obraz dysku WIM</comment>
+ <comment xml:lang="pt">imagem de disco WIM</comment>
+ <comment xml:lang="pt_BR">Imagem de disco WIM</comment>
+ <comment xml:lang="ru">Образ диска WIM</comment>
+ <comment xml:lang="sk">Obraz disku WIM</comment>
+ <comment xml:lang="sr">слика диска ВИМ-а</comment>
+ <comment xml:lang="sv">WIM-diskavbild</comment>
+ <comment xml:lang="tr">WIM disk kalıbı</comment>
+ <comment xml:lang="uk">образ диска WIM</comment>
+ <comment xml:lang="zh_CN">WIM 磁盘映像</comment>
+ <comment xml:lang="zh_TW">WIM 磁碟映像檔</comment>
+ <acronym>WIM</acronym>
+ <expanded-acronym>Windows Imaging Format</expanded-acronym>
+ <magic>
+ <match value="MSWIM\000\000\000" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.wim"/>
+ <glob pattern="*.swm"/>
+ </mime-type>
+ <mime-type type="audio/ac3">
+ <comment>Dolby Digital audio</comment>
+ <comment xml:lang="ar">Dolby Digital سمعي</comment>
+ <comment xml:lang="az">Dolby Digital audio</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Dolby Digital</comment>
+ <comment xml:lang="bg">Аудио — Dolby Digital</comment>
+ <comment xml:lang="ca">àudio de Dolby Digital</comment>
+ <comment xml:lang="cs">zvuk Dolby Digital</comment>
+ <comment xml:lang="cy">Sain Dolby Digital</comment>
+ <comment xml:lang="da">Dolby Ditital-lyd</comment>
+ <comment xml:lang="de">Dolby-Digital-Audio</comment>
+ <comment xml:lang="el">Ψηφιακός Ήχος Dolby</comment>
+ <comment xml:lang="en_GB">Dolby Digital audio</comment>
+ <comment xml:lang="eo">Sondosiero en Dolby Digital</comment>
+ <comment xml:lang="es">sonido Dolby Digital</comment>
+ <comment xml:lang="eu">Dolby audio digitala</comment>
+ <comment xml:lang="fi">Dolby Digital -ääni</comment>
+ <comment xml:lang="fo">Dolby Digital ljóður</comment>
+ <comment xml:lang="fr">audio Dolby Digital</comment>
+ <comment xml:lang="ga">fuaim Dolby Digital</comment>
+ <comment xml:lang="gl">son Dolby Digital</comment>
+ <comment xml:lang="he">שמע Dolby Digital</comment>
+ <comment xml:lang="hr">Dolby Digital zvučni zapis</comment>
+ <comment xml:lang="hu">Dolby Digital hang</comment>
+ <comment xml:lang="ia">Audio Dolby Digital</comment>
+ <comment xml:lang="id">Audio Dolby Digital</comment>
+ <comment xml:lang="it">Audio Dolby Digital</comment>
+ <comment xml:lang="ja">ドルビーデジタルオーディオ</comment>
+ <comment xml:lang="ka">Dolby Digital-ის აუდიო</comment>
+ <comment xml:lang="kk">Dolby Digital аудиосы</comment>
+ <comment xml:lang="ko">돌비 디지털 오디오</comment>
+ <comment xml:lang="lt">Dolby Digital garso įrašas</comment>
+ <comment xml:lang="lv">Dolby Digital audio</comment>
+ <comment xml:lang="ms">Audio Digital Dolby</comment>
+ <comment xml:lang="nb">Dolby digital lyd</comment>
+ <comment xml:lang="nl">Dolby Digital-audio</comment>
+ <comment xml:lang="nn">Dolby Digital lyd</comment>
+ <comment xml:lang="oc">àudio Dolby Digital</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Dolby Digital</comment>
+ <comment xml:lang="pt">áudio Dolby Digital</comment>
+ <comment xml:lang="pt_BR">Áudio Dolby Digital</comment>
+ <comment xml:lang="ro">Audio Dolby Digital</comment>
+ <comment xml:lang="ru">Аудио Dolby Digital</comment>
+ <comment xml:lang="sk">Zvuk Dolby Digital</comment>
+ <comment xml:lang="sl">Zvočna datoteka Dolby Digital</comment>
+ <comment xml:lang="sq">Audio Dolby Digital</comment>
+ <comment xml:lang="sr">Дигитални Долби звук</comment>
+ <comment xml:lang="sv">Dolby Digital-ljud</comment>
+ <comment xml:lang="tr">Dolby Digital sesi</comment>
+ <comment xml:lang="uk">звук Dolby Digital</comment>
+ <comment xml:lang="vi">Âm thanh Dolby Digital</comment>
+ <comment xml:lang="zh_CN">杜比数字音频</comment>
+ <comment xml:lang="zh_TW">杜比數位音訊</comment>
+ <magic priority="50">
+ <match value="0x0b77" type="big16" offset="0"/>
+ </magic>
+ <glob pattern="*.ac3"/>
+ </mime-type>
+ <mime-type type="audio/vnd.dts">
+ <comment>DTS audio</comment>
+ <comment xml:lang="ca">àudio DTS</comment>
+ <comment xml:lang="cs">zvuk DTS</comment>
+ <comment xml:lang="da">DTS-lyd</comment>
+ <comment xml:lang="de">DTS-Audio</comment>
+ <comment xml:lang="el">Ήχος DTS</comment>
+ <comment xml:lang="en_GB">DTS audio</comment>
+ <comment xml:lang="es">sonido DTS</comment>
+ <comment xml:lang="eu">DTS audioa</comment>
+ <comment xml:lang="fi">DTS-ääni</comment>
+ <comment xml:lang="fr">audio DTS</comment>
+ <comment xml:lang="ga">fuaim DTS</comment>
+ <comment xml:lang="gl">Son DTS</comment>
+ <comment xml:lang="he">שמע DTS</comment>
+ <comment xml:lang="hr">DTS zvučni zapis</comment>
+ <comment xml:lang="hu">DTS hang</comment>
+ <comment xml:lang="ia">Audio DTS</comment>
+ <comment xml:lang="id">Audio DTS</comment>
+ <comment xml:lang="it">Audio DTS</comment>
+ <comment xml:lang="ja">DTS オーディオ</comment>
+ <comment xml:lang="kk">DTS аудиосы</comment>
+ <comment xml:lang="ko">DTS 오디오</comment>
+ <comment xml:lang="lv">DTS audio</comment>
+ <comment xml:lang="oc">àudio DTS</comment>
+ <comment xml:lang="pl">Plik dźwiękowy DTS</comment>
+ <comment xml:lang="pt">aúdio DTS</comment>
+ <comment xml:lang="pt_BR">Áudio DTS</comment>
+ <comment xml:lang="ru">Аудио DTS</comment>
+ <comment xml:lang="sk">Zvuk DTS</comment>
+ <comment xml:lang="sl">Zvok DTS</comment>
+ <comment xml:lang="sr">ДТС звук</comment>
+ <comment xml:lang="sv">DTS-ljud</comment>
+ <comment xml:lang="tr">DTS sesi</comment>
+ <comment xml:lang="uk">звукові дані DTS</comment>
+ <comment xml:lang="zh_CN">DTS 音频</comment>
+ <comment xml:lang="zh_TW">DTS 音訊</comment>
+ <magic priority="50">
+ <match value="0x7FFE8001" type="big32" offset="0"/>
+ <match value="0xFE7F0180" type="little32" offset="0"/>
+ <match value="0x1FFFE800" type="big32" offset="0"/>
+ <match value="0xFF1F00E8" type="little32" offset="0"/>
+ </magic>
+ <alias type="audio/x-dts"/>
+ <glob pattern="*.dts"/>
+ </mime-type>
+ <mime-type type="audio/vnd.dts.hd">
+ <comment>DTSHD audio</comment>
+ <comment xml:lang="ca">àudio DTSHD</comment>
+ <comment xml:lang="cs">zvuk DTSHD</comment>
+ <comment xml:lang="da">DTSDH-lyd</comment>
+ <comment xml:lang="de">DTSHD-Audio</comment>
+ <comment xml:lang="el">Ήχος DTSHD</comment>
+ <comment xml:lang="en_GB">DTSHD audio</comment>
+ <comment xml:lang="es">sonido DTSHD</comment>
+ <comment xml:lang="eu">DTSHD audioa</comment>
+ <comment xml:lang="fi">DTS-HD-ääni</comment>
+ <comment xml:lang="fr">audio DTSHD</comment>
+ <comment xml:lang="ga">fuaim DTSHD</comment>
+ <comment xml:lang="gl">Son DTSHD</comment>
+ <comment xml:lang="he">שמע DTSHD</comment>
+ <comment xml:lang="hr">DTSHD zvučni zapis</comment>
+ <comment xml:lang="hu">DTSHD hang</comment>
+ <comment xml:lang="ia">Audio DTSHD</comment>
+ <comment xml:lang="id">Audio DTSHD</comment>
+ <comment xml:lang="it">Audio DTSHD</comment>
+ <comment xml:lang="ja">DTSHD オーディオ</comment>
+ <comment xml:lang="kk">DTSHD аудиосы</comment>
+ <comment xml:lang="ko">DTSHD 오디오</comment>
+ <comment xml:lang="lv">DTSHD audio</comment>
+ <comment xml:lang="oc">àudio DTSHD</comment>
+ <comment xml:lang="pl">Plik dźwiękowy DTSHD</comment>
+ <comment xml:lang="pt">áudio DTSHD</comment>
+ <comment xml:lang="pt_BR">Áudio DTSHD</comment>
+ <comment xml:lang="ru">Аудио DTSHD</comment>
+ <comment xml:lang="sk">Zvuk DTSHD</comment>
+ <comment xml:lang="sl">Zvok DTSHD</comment>
+ <comment xml:lang="sr">ДТСХД звук</comment>
+ <comment xml:lang="sv">DTSHD-ljud</comment>
+ <comment xml:lang="tr">DTSHD sesi</comment>
+ <comment xml:lang="uk">звукові дані DTSHD</comment>
+ <comment xml:lang="zh_CN">DTSHD 音频</comment>
+ <comment xml:lang="zh_TW">DTSHD 音訊</comment>
+ <sub-class-of type="audio/vnd.dts"/>
+ <magic priority="60">
+ <match value="0x64582025" type="big32" offset="0:18725"/>
+ </magic>
+ <alias type="audio/x-dtshd"/>
+ <glob pattern="*.dtshd"/>
+ </mime-type>
+ <mime-type type="audio/AMR">
+ <comment>AMR audio</comment>
+ <comment xml:lang="ar">AMR سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo AMR</comment>
+ <comment xml:lang="bg">Аудио — AMR</comment>
+ <comment xml:lang="ca">àudio AMR</comment>
+ <comment xml:lang="cs">zvuk AMR</comment>
+ <comment xml:lang="da">AMR-lyd</comment>
+ <comment xml:lang="de">AMR-Audio</comment>
+ <comment xml:lang="el">Ήχος AMR</comment>
+ <comment xml:lang="en_GB">AMR audio</comment>
+ <comment xml:lang="eo">AMR-sondosiero</comment>
+ <comment xml:lang="es">sonido AMR</comment>
+ <comment xml:lang="eu">AMR audioa</comment>
+ <comment xml:lang="fi">AMR-ääni</comment>
+ <comment xml:lang="fo">AMR ljóður</comment>
+ <comment xml:lang="fr">audio AMR</comment>
+ <comment xml:lang="ga">fuaim AMR</comment>
+ <comment xml:lang="gl">son AMR</comment>
+ <comment xml:lang="he">שמע AMR</comment>
+ <comment xml:lang="hr">AMR zvučni zapis</comment>
+ <comment xml:lang="hu">AMR hang</comment>
+ <comment xml:lang="ia">Audio AMR</comment>
+ <comment xml:lang="id">Audio AMR</comment>
+ <comment xml:lang="it">Audio AMR</comment>
+ <comment xml:lang="ja">AMR オーディオ</comment>
+ <comment xml:lang="ka">AMR აუდიო</comment>
+ <comment xml:lang="kk">AMR аудиосы</comment>
+ <comment xml:lang="ko">AMR 오디오</comment>
+ <comment xml:lang="lt">AMR garso įrašas</comment>
+ <comment xml:lang="lv">AMR audio</comment>
+ <comment xml:lang="nb">AMR-lyd</comment>
+ <comment xml:lang="nl">AMR-audio</comment>
+ <comment xml:lang="nn">AMR-lyd</comment>
+ <comment xml:lang="oc">àudio AMR</comment>
+ <comment xml:lang="pl">Plik dźwiękowy AMR</comment>
+ <comment xml:lang="pt">áudio AMR</comment>
+ <comment xml:lang="pt_BR">Áudio AMR</comment>
+ <comment xml:lang="ro">Audio AMR</comment>
+ <comment xml:lang="ru">Аудио AMR</comment>
+ <comment xml:lang="sk">Zvuk AMR</comment>
+ <comment xml:lang="sl">Zvočna datoteka AMR</comment>
+ <comment xml:lang="sq">Audio AMR</comment>
+ <comment xml:lang="sr">АМР звук</comment>
+ <comment xml:lang="sv">AMR-ljud</comment>
+ <comment xml:lang="tr">AMR sesi</comment>
+ <comment xml:lang="uk">звук AMR</comment>
+ <comment xml:lang="vi">Âm thanh AMR</comment>
+ <comment xml:lang="zh_CN">AMR 音频</comment>
+ <comment xml:lang="zh_TW">AMR 音訊</comment>
+ <acronym>AMR</acronym>
+ <expanded-acronym>Adaptive Multi-Rate</expanded-acronym>
+ <magic priority="50">
+ <match value="#!AMR\n" type="string" offset="0"/>
+ <match value="#!AMR_MC1.0\n" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.amr"/>
+ <alias type="audio/amr-encrypted"/>
+ </mime-type>
+ <mime-type type="audio/AMR-WB">
+ <comment>AMR-WB audio</comment>
+ <comment xml:lang="ar">AMR-WB سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo AMR-WB</comment>
+ <comment xml:lang="bg">Аудио — AMR-WB</comment>
+ <comment xml:lang="ca">àudio AMR-WB</comment>
+ <comment xml:lang="cs">zvuk AMR-WB</comment>
+ <comment xml:lang="da">AMR-WB-lyd</comment>
+ <comment xml:lang="de">AMR-WB-Audio</comment>
+ <comment xml:lang="el">Ήχος AMR-WB</comment>
+ <comment xml:lang="en_GB">AMR-WB audio</comment>
+ <comment xml:lang="eo">AMR-WB-sondosiero</comment>
+ <comment xml:lang="es">sonido AMR-WB</comment>
+ <comment xml:lang="eu">AMR-WB audioa</comment>
+ <comment xml:lang="fi">AMR-WB-ääni</comment>
+ <comment xml:lang="fo">AMR-WB ljóður</comment>
+ <comment xml:lang="fr">audio AMR-WB</comment>
+ <comment xml:lang="ga">fuaim AMR-WB</comment>
+ <comment xml:lang="gl">son AMR-WB</comment>
+ <comment xml:lang="he">שמע AMR-WN</comment>
+ <comment xml:lang="hr">AMR-WB zvučni zapis</comment>
+ <comment xml:lang="hu">AMR-WB hang</comment>
+ <comment xml:lang="ia">Audio AMR-WB</comment>
+ <comment xml:lang="id">Audio AMR-WB</comment>
+ <comment xml:lang="it">Audio AMR-WB</comment>
+ <comment xml:lang="ja">AMR-WB オーディオ</comment>
+ <comment xml:lang="ka">AMR-WB აუდიო</comment>
+ <comment xml:lang="kk">AMR-WB аудиосы</comment>
+ <comment xml:lang="ko">AMR-WB 오디오</comment>
+ <comment xml:lang="lt">AMR-WB garso įrašas</comment>
+ <comment xml:lang="lv">AMR-WB audio</comment>
+ <comment xml:lang="nb">AMR-WB-lyd</comment>
+ <comment xml:lang="nl">AMR-WB-audio</comment>
+ <comment xml:lang="nn">AMR-WB-lyd</comment>
+ <comment xml:lang="oc">àudio AMR-WB</comment>
+ <comment xml:lang="pl">Plik dźwiękowy AMR-WB</comment>
+ <comment xml:lang="pt">áudio AMR-WB</comment>
+ <comment xml:lang="pt_BR">Áudio AMR-WB</comment>
+ <comment xml:lang="ro">Audio AMR-WB</comment>
+ <comment xml:lang="ru">Аудио AMR-WB</comment>
+ <comment xml:lang="sk">Zvuk AMR-WB</comment>
+ <comment xml:lang="sl">Zvočna datoteka AMR-WB</comment>
+ <comment xml:lang="sq">Audio AMR-WB</comment>
+ <comment xml:lang="sr">АМР-ВБ звук</comment>
+ <comment xml:lang="sv">AMR-WB-ljud</comment>
+ <comment xml:lang="tr">AMR-WB sesi</comment>
+ <comment xml:lang="uk">звук AMR-WB</comment>
+ <comment xml:lang="vi">Âm thanh AMR-WB</comment>
+ <comment xml:lang="zh_CN">AMR-WB 音频</comment>
+ <comment xml:lang="zh_TW">AMR-WB 音訊</comment>
+ <acronym>AMR-WB</acronym>
+ <expanded-acronym>Adaptive Multi-Rate Wideband</expanded-acronym>
+ <magic priority="50">
+ <match value="#!AMR-WB\n" type="string" offset="0"/>
+ <match value="#!AMR-WB_MC1.0\n" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.awb"/>
+ <alias type="audio/amr-wb-encrypted"/>
+ </mime-type>
+ <mime-type type="audio/basic">
+ <comment>ULAW (Sun) audio</comment>
+ <comment xml:lang="ar">ULAW (صن) سمعي</comment>
+ <comment xml:lang="az">ULAW (Sun) audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo ULAW (Sun)</comment>
+ <comment xml:lang="bg">Аудио — ULAW, Sun</comment>
+ <comment xml:lang="ca">àudio ULAW (Sun)</comment>
+ <comment xml:lang="cs">zvuk ULAW (Sun)</comment>
+ <comment xml:lang="cy">Sain ULAW (Sun)</comment>
+ <comment xml:lang="da">ULAW-lyd (Sun)</comment>
+ <comment xml:lang="de">ULAW-Audio (Sun)</comment>
+ <comment xml:lang="el">Ήχος ULAW (Sun)</comment>
+ <comment xml:lang="en_GB">ULAW (Sun) audio</comment>
+ <comment xml:lang="eo">ULAW-sondosiero (Sun)</comment>
+ <comment xml:lang="es">sonido ULAW (Sun)</comment>
+ <comment xml:lang="eu">ULAW (sun) audioa</comment>
+ <comment xml:lang="fi">ULAW (Sun) -ääni</comment>
+ <comment xml:lang="fo">ULAW (Sun) ljóður</comment>
+ <comment xml:lang="fr">audio ULAW (Sun)</comment>
+ <comment xml:lang="ga">fuaim ULAW (Sun)</comment>
+ <comment xml:lang="gl">son ULAW (Sun)</comment>
+ <comment xml:lang="he">שמע ULAW (של Sun)</comment>
+ <comment xml:lang="hr">ULAW (Sun) zvučni zapis</comment>
+ <comment xml:lang="hu">ULAW (Sun) hang</comment>
+ <comment xml:lang="ia">Audio ULAW (sun)</comment>
+ <comment xml:lang="id">Audio ULAW (Sun)</comment>
+ <comment xml:lang="it">Audio ULAW (Sun)</comment>
+ <comment xml:lang="ja">ULAW (Sun) オーディオ</comment>
+ <comment xml:lang="kk">ULAW (Sun) аудиосы</comment>
+ <comment xml:lang="ko">ULAW(Sun) 오디오</comment>
+ <comment xml:lang="lt">ULAW (Sun) garso įrašas</comment>
+ <comment xml:lang="lv">ULAW (Sun) audio</comment>
+ <comment xml:lang="ms">Audio ULAW (Sun)</comment>
+ <comment xml:lang="nb">ULAW-lyd (Sun)</comment>
+ <comment xml:lang="nl">(Sun) ULAW-audio</comment>
+ <comment xml:lang="nn">ULAW (Sun)-lyd</comment>
+ <comment xml:lang="oc">àudio ULAW (Sun)</comment>
+ <comment xml:lang="pl">Plik dźwiękowy ULAW (Sun)</comment>
+ <comment xml:lang="pt">áudio ULAW (Sun)</comment>
+ <comment xml:lang="pt_BR">Áudio ULAW (Sun)</comment>
+ <comment xml:lang="ro">Fișier audio ULAW (Sun)</comment>
+ <comment xml:lang="ru">Аудио ULAW (Sun)</comment>
+ <comment xml:lang="sk">Zvuk ULAW (Sun)</comment>
+ <comment xml:lang="sl">Zvočna datoteka ULAW (Sun)</comment>
+ <comment xml:lang="sq">Audio ULAW (Sun)</comment>
+ <comment xml:lang="sr">УЛАВ (Сан) звук</comment>
+ <comment xml:lang="sv">ULAW-ljud (Sun)</comment>
+ <comment xml:lang="tr">ULAW (Sun) sesi</comment>
+ <comment xml:lang="uk">звук ULAW (Sun)</comment>
+ <comment xml:lang="vi">Âm thanh ULAW (Sun)</comment>
+ <comment xml:lang="zh_CN">ULAW (Sun) 音频</comment>
+ <comment xml:lang="zh_TW">ULAW (Sun) 音訊</comment>
+ <magic priority="40">
+ <match value=".snd" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.au"/>
+ <glob pattern="*.snd"/>
+ </mime-type>
+ <mime-type type="audio/prs.sid">
+ <comment>Commodore 64 audio</comment>
+ <comment xml:lang="ar">Commodore 64 سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Commodore 64</comment>
+ <comment xml:lang="bg">Аудио — Commodore 64</comment>
+ <comment xml:lang="ca">àudio de Commodore 64</comment>
+ <comment xml:lang="cs">zvuk Commodore 64</comment>
+ <comment xml:lang="da">Commodore 64-lyd</comment>
+ <comment xml:lang="de">Commodore-64-Audio</comment>
+ <comment xml:lang="el">Ήχος Commodore 64</comment>
+ <comment xml:lang="en_GB">Commodore 64 audio</comment>
+ <comment xml:lang="eo">Sondosiero de Commodore 64</comment>
+ <comment xml:lang="es">sonido de Commodore 64</comment>
+ <comment xml:lang="eu">Commodore 64 Audioa</comment>
+ <comment xml:lang="fi">Commodore 64 -ääni</comment>
+ <comment xml:lang="fo">Commodore 64 ljóð</comment>
+ <comment xml:lang="fr">audio Commodore 64</comment>
+ <comment xml:lang="ga">fuaim Commodore 64</comment>
+ <comment xml:lang="gl">son de Commodore 64</comment>
+ <comment xml:lang="he">שמע של Commodore 64</comment>
+ <comment xml:lang="hr">Commodore 64 zvučni zapis</comment>
+ <comment xml:lang="hu">Commodore 64 hang</comment>
+ <comment xml:lang="ia">Audio Commodore 64</comment>
+ <comment xml:lang="id">Audio Commodore 64</comment>
+ <comment xml:lang="it">Audio Commodore 64</comment>
+ <comment xml:lang="ja">Commodore 64 オーディオ</comment>
+ <comment xml:lang="ka">Commodore 64-ის აუდიო</comment>
+ <comment xml:lang="kk">Commodore 64 аудиосы</comment>
+ <comment xml:lang="ko">Commodore 64 오디오</comment>
+ <comment xml:lang="lt">Commodore 64 garso įrašas</comment>
+ <comment xml:lang="lv">Commodore 64 audio</comment>
+ <comment xml:lang="ms">Audio Commodore 64</comment>
+ <comment xml:lang="nb">Commodore 64-lyd</comment>
+ <comment xml:lang="nl">Commodore 64-audio</comment>
+ <comment xml:lang="nn">Commodore 64-lyd</comment>
+ <comment xml:lang="oc">àudio Commodore 64</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Commodore 64</comment>
+ <comment xml:lang="pt">áudio Commodore 64</comment>
+ <comment xml:lang="pt_BR">Áudio Commodore 64</comment>
+ <comment xml:lang="ro">Audio Commodore 64</comment>
+ <comment xml:lang="ru">Аудио Commodore 64</comment>
+ <comment xml:lang="sk">Zvuk Commodore 64</comment>
+ <comment xml:lang="sl">Zvočna datoteka Commodore 64</comment>
+ <comment xml:lang="sq">Audio Commodore 64</comment>
+ <comment xml:lang="sr">звук Комодора 64</comment>
+ <comment xml:lang="sv">Commodore 64-ljud</comment>
+ <comment xml:lang="tr">Commodore 64 sesi</comment>
+ <comment xml:lang="uk">звук Commodore 64</comment>
+ <comment xml:lang="vi">Âm thanh Commodore 64</comment>
+ <comment xml:lang="zh_CN">Commodore 64 音频</comment>
+ <comment xml:lang="zh_TW">Commodore 64 音訊</comment>
+ <magic priority="50">
+ <match value="PSID" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.sid"/>
+ <glob pattern="*.psid"/>
+ </mime-type>
+ <mime-type type="audio/x-adpcm">
+ <comment>PCM audio</comment>
+ <comment xml:lang="ar">سمعي PCM</comment>
+ <comment xml:lang="az">PCM audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo PCM</comment>
+ <comment xml:lang="bg">Аудио — PCM</comment>
+ <comment xml:lang="ca">àudio PCM</comment>
+ <comment xml:lang="cs">zvuk PCM</comment>
+ <comment xml:lang="cy">Sain PCM</comment>
+ <comment xml:lang="da">PCM-lyd</comment>
+ <comment xml:lang="de">PCM-Audio</comment>
+ <comment xml:lang="el">Ήχος PCM</comment>
+ <comment xml:lang="en_GB">PCM audio</comment>
+ <comment xml:lang="eo">PCM-sondosiero</comment>
+ <comment xml:lang="es">sonido PCM</comment>
+ <comment xml:lang="eu">PCM audioa</comment>
+ <comment xml:lang="fi">PCM-ääni</comment>
+ <comment xml:lang="fo">PCM ljóður</comment>
+ <comment xml:lang="fr">audio PCM</comment>
+ <comment xml:lang="ga">fuaim PCM</comment>
+ <comment xml:lang="gl">son PCM</comment>
+ <comment xml:lang="he">שמע PCM</comment>
+ <comment xml:lang="hr">PCM zvučni zapis</comment>
+ <comment xml:lang="hu">PCM hang</comment>
+ <comment xml:lang="ia">Audio PCM</comment>
+ <comment xml:lang="id">Audio PCM</comment>
+ <comment xml:lang="it">Audio PCM</comment>
+ <comment xml:lang="ja">PCM オーディオ</comment>
+ <comment xml:lang="kk">PCM аудиосы</comment>
+ <comment xml:lang="ko">PCM 오디오</comment>
+ <comment xml:lang="lt">PCM garso įrašas</comment>
+ <comment xml:lang="lv">PCM audio</comment>
+ <comment xml:lang="ms">Audio PCM</comment>
+ <comment xml:lang="nb">PCM-lyd</comment>
+ <comment xml:lang="nl">PCM-audio</comment>
+ <comment xml:lang="nn">PCM-lyd</comment>
+ <comment xml:lang="oc">àudio PCM</comment>
+ <comment xml:lang="pl">Plik dźwiękowy PCM</comment>
+ <comment xml:lang="pt">áudio PCM</comment>
+ <comment xml:lang="pt_BR">Áudio PCM</comment>
+ <comment xml:lang="ro">Audio PCM</comment>
+ <comment xml:lang="ru">Аудио PCM</comment>
+ <comment xml:lang="sk">Zvuk PCM</comment>
+ <comment xml:lang="sl">Zvočna datoteka PCM</comment>
+ <comment xml:lang="sq">Audio PCM</comment>
+ <comment xml:lang="sr">ПЦМ звук</comment>
+ <comment xml:lang="sv">PCM-ljud</comment>
+ <comment xml:lang="tr">PCM sesi</comment>
+ <comment xml:lang="uk">звук PCM</comment>
+ <comment xml:lang="vi">Âm thanh PCM</comment>
+ <comment xml:lang="zh_CN">PCM 音频</comment>
+ <comment xml:lang="zh_TW">PCM 音訊</comment>
+ <magic priority="50">
+ <match value=".snd" type="string" offset="0">
+ <match value="23" type="big32" offset="12"/>
+ </match>
+ <match value="0x0064732E" type="little32" offset="0">
+ <match value="1" type="little32" offset="12"/>
+ <match value="2" type="little32" offset="12"/>
+ <match value="3" type="little32" offset="12"/>
+ <match value="4" type="little32" offset="12"/>
+ <match value="5" type="little32" offset="12"/>
+ <match value="6" type="little32" offset="12"/>
+ <match value="7" type="little32" offset="12"/>
+ <match value="23" type="little32" offset="12"/>
+ </match>
+ </magic>
+ </mime-type>
+ <mime-type type="audio/x-aifc">
+ <comment>AIFC audio</comment>
+ <comment xml:lang="ar">AIFC سمعي</comment>
+ <comment xml:lang="az">AIFC audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo AIFC</comment>
+ <comment xml:lang="bg">Аудио — AIFC</comment>
+ <comment xml:lang="ca">àudio AIFC</comment>
+ <comment xml:lang="cs">zvuk AIFC</comment>
+ <comment xml:lang="cy">Sain AIFC</comment>
+ <comment xml:lang="da">AIFC-lyd</comment>
+ <comment xml:lang="de">AIFC-Audio</comment>
+ <comment xml:lang="el">Ήχος AIFC</comment>
+ <comment xml:lang="en_GB">AIFC audio</comment>
+ <comment xml:lang="eo">AIFC-sondosiero</comment>
+ <comment xml:lang="es">sonido AIFC</comment>
+ <comment xml:lang="eu">AIFC audioa</comment>
+ <comment xml:lang="fi">AIFC-ääni</comment>
+ <comment xml:lang="fo">AIFC ljóður</comment>
+ <comment xml:lang="fr">audio AIFC</comment>
+ <comment xml:lang="ga">fuaim AIFC</comment>
+ <comment xml:lang="gl">son AIFC</comment>
+ <comment xml:lang="he">שמע AIFC</comment>
+ <comment xml:lang="hr">AIFC zvučni zapis</comment>
+ <comment xml:lang="hu">AIFC hang</comment>
+ <comment xml:lang="ia">Audio AIFC</comment>
+ <comment xml:lang="id">Audio AIFC</comment>
+ <comment xml:lang="it">Audio AIFC</comment>
+ <comment xml:lang="ja">AIFC オーディオ</comment>
+ <comment xml:lang="ka">AIFC აუდიო</comment>
+ <comment xml:lang="kk">AIFC аудиосы</comment>
+ <comment xml:lang="ko">AIFC 오디오</comment>
+ <comment xml:lang="lt">AIFC garso įrašas</comment>
+ <comment xml:lang="lv">AIFC audio</comment>
+ <comment xml:lang="ms">Audio AIFC</comment>
+ <comment xml:lang="nb">AIFC-lyd</comment>
+ <comment xml:lang="nl">AIFC-audio</comment>
+ <comment xml:lang="nn">AIFC-lyd</comment>
+ <comment xml:lang="oc">àudio AIFC</comment>
+ <comment xml:lang="pl">Plik dźwiękowy AIFC</comment>
+ <comment xml:lang="pt">áudio AIFC</comment>
+ <comment xml:lang="pt_BR">Áudio AIFC</comment>
+ <comment xml:lang="ro">Fișier audio AIFC</comment>
+ <comment xml:lang="ru">Аудио AIFC</comment>
+ <comment xml:lang="sk">Zvuk AIFC</comment>
+ <comment xml:lang="sl">Zvočna datoteka AIFC</comment>
+ <comment xml:lang="sq">Audio AIFC</comment>
+ <comment xml:lang="sr">АИФЦ звук</comment>
+ <comment xml:lang="sv">AIFC-ljud</comment>
+ <comment xml:lang="tr">AIFC sesi</comment>
+ <comment xml:lang="uk">звук AIFC</comment>
+ <comment xml:lang="vi">Âm thanh AIFC</comment>
+ <comment xml:lang="zh_CN">AIFC 音频</comment>
+ <comment xml:lang="zh_TW">AIFC 音訊</comment>
+ <acronym>AIFC</acronym>
+ <expanded-acronym>Audio Interchange File format Compressed</expanded-acronym>
+ <sub-class-of type="application/x-iff"/>
+ <magic priority="50">
+ <match value="AIFC" type="string" offset="8"/>
+ </magic>
+ <glob pattern="*.aifc"/>
+ <glob pattern="*.aiffc"/>
+ <alias type="audio/x-aiffc"/>
+ </mime-type>
+ <mime-type type="audio/x-aiff">
+ <comment>AIFF/Amiga/Mac audio</comment>
+ <comment xml:lang="ar">AIFF/Amiga/Mac سمعي</comment>
+ <comment xml:lang="az">AIFF/Amiga/Mac audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo AIFF/Amiga/Mac</comment>
+ <comment xml:lang="bg">Аудио — AIFF/Amiga/Mac</comment>
+ <comment xml:lang="ca">àudio AIFF/Amiga/Mac</comment>
+ <comment xml:lang="cs">zvuk AIFF/Amiga/Mac</comment>
+ <comment xml:lang="cy">Sain AIFF/Amiga/Mac</comment>
+ <comment xml:lang="da">AIFF-/Amiga-/Maclyd</comment>
+ <comment xml:lang="de">AIFF/Amiga/Mac-Audio</comment>
+ <comment xml:lang="el">Ήχος AIFF/Amiga/Mac</comment>
+ <comment xml:lang="en_GB">AIFF/Amiga/Mac audio</comment>
+ <comment xml:lang="eo">AIFF/Amiga/Mac-sondosiero</comment>
+ <comment xml:lang="es">sonido AIFF/Amiga/Mac</comment>
+ <comment xml:lang="eu">AIFF/Amiga/Mac audioa</comment>
+ <comment xml:lang="fi">AIFF/Amiga/Mac-ääni</comment>
+ <comment xml:lang="fo">AIFF/Amiga/Mac ljóður</comment>
+ <comment xml:lang="fr">audio AIFF/Amiga/Mac</comment>
+ <comment xml:lang="ga">fuaim AIFF/Amiga/Mac</comment>
+ <comment xml:lang="gl">son AIFF/Amiga/Mac</comment>
+ <comment xml:lang="he">שמע AIFF/Amiga/Mac</comment>
+ <comment xml:lang="hr">AIFF/Amiga/Mac zvučni zapis</comment>
+ <comment xml:lang="hu">AIFF/Amiga/Mac hang</comment>
+ <comment xml:lang="ia">Audio AIFF/Amiga/Mac</comment>
+ <comment xml:lang="id">Audio AIFF/Amiga/Mac</comment>
+ <comment xml:lang="it">Audio AIFF/Amiga/Mac</comment>
+ <comment xml:lang="ja">AIFF/Amiga/Mac オーディオ</comment>
+ <comment xml:lang="ka">AIFF/Amiga/Mac აუდიო</comment>
+ <comment xml:lang="kk">AIFF/Amiga/Mac аудиосы</comment>
+ <comment xml:lang="ko">AIFF/Amiga/Mac 오디오</comment>
+ <comment xml:lang="lt">AIFF/Amiga/Mac garso įrašas</comment>
+ <comment xml:lang="lv">AIFF/Amiga/Mac audio</comment>
+ <comment xml:lang="ms">Audio AIFF/Amiga/Mac</comment>
+ <comment xml:lang="nb">AIFF/Amiga/Mac-lyd</comment>
+ <comment xml:lang="nl">AIFF/Amiga/Mac-audio</comment>
+ <comment xml:lang="nn">AIFF/Amiga/Mac-lyd</comment>
+ <comment xml:lang="oc">àudio AIFF/Amiga/Mac</comment>
+ <comment xml:lang="pl">Plik dźwiękowy AIFF/Amiga/Mac</comment>
+ <comment xml:lang="pt">áudio AIFF/Amiga/Mac</comment>
+ <comment xml:lang="pt_BR">Áudio AIFF/Amiga/Mac</comment>
+ <comment xml:lang="ro">Audio AIFF/Amiga/Mac</comment>
+ <comment xml:lang="ru">Аудио AIFF/Amiga/Mac</comment>
+ <comment xml:lang="sk">Zvuk AIFF/Amiga/Mac</comment>
+ <comment xml:lang="sl">Zvočna datoteka AIFF/Amiga/Mac</comment>
+ <comment xml:lang="sq">Audio AIFF/Amiga/Mac</comment>
+ <comment xml:lang="sr">АИФФ/Амига/Мекинтош звук</comment>
+ <comment xml:lang="sv">AIFF/Amiga/Mac-ljud</comment>
+ <comment xml:lang="tr">AIFF/Amiga/Mac sesi</comment>
+ <comment xml:lang="uk">звук AIFF/Amiga/Mac</comment>
+ <comment xml:lang="vi">Âm thanh AIFF/Amiga/Mac</comment>
+ <comment xml:lang="zh_CN">AIFF/Amiga/Mac 音频</comment>
+ <comment xml:lang="zh_TW">AIFF/Amiga/Mac 音訊</comment>
+ <acronym>AIFF</acronym>
+ <expanded-acronym>Audio Interchange File Format</expanded-acronym>
+ <sub-class-of type="application/x-iff"/>
+ <magic priority="50">
+ <match value="AIFF" type="string" offset="8"/>
+ <match value="8SVX" type="string" offset="8"/>
+ </magic>
+ <glob pattern="*.aiff"/>
+ <glob pattern="*.aif"/>
+ </mime-type>
+ <mime-type type="audio/x-ape">
+ <comment>Monkey's audio</comment>
+ <comment xml:lang="ar">Monkey سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Monkey's</comment>
+ <comment xml:lang="bg">Аудио — Monkey</comment>
+ <comment xml:lang="ca">àudio de Monkey</comment>
+ <comment xml:lang="cs">zvuk Monkey's</comment>
+ <comment xml:lang="da">Monkeys lyd</comment>
+ <comment xml:lang="de">Monkey's-Audio</comment>
+ <comment xml:lang="el">Ήχος Monkey's</comment>
+ <comment xml:lang="en_GB">Monkey's audio</comment>
+ <comment xml:lang="es">sonido de Monkey</comment>
+ <comment xml:lang="eu">Monkey audioa</comment>
+ <comment xml:lang="fi">Monkey's Audio -ääni</comment>
+ <comment xml:lang="fo">Monkey's ljóður</comment>
+ <comment xml:lang="fr">audio Monkey</comment>
+ <comment xml:lang="ga">fuaim Monkey's</comment>
+ <comment xml:lang="gl">son de Monkey</comment>
+ <comment xml:lang="he">שמע של Monkey's</comment>
+ <comment xml:lang="hr">Monkey zvučni zapis</comment>
+ <comment xml:lang="hu">Monkey hang</comment>
+ <comment xml:lang="ia">Audio Monkey's</comment>
+ <comment xml:lang="id">Audio Monkey</comment>
+ <comment xml:lang="it">Audio Monkey's</comment>
+ <comment xml:lang="ja">Monkey's オーディオ</comment>
+ <comment xml:lang="kk">Monkey аудиосы</comment>
+ <comment xml:lang="ko">Monkey's 오디오</comment>
+ <comment xml:lang="lt">Monkey garso įrašas</comment>
+ <comment xml:lang="lv">Monkey's audio</comment>
+ <comment xml:lang="nb">Monkey's-lyd</comment>
+ <comment xml:lang="nl">Monkey's-audio</comment>
+ <comment xml:lang="nn">Monkey's Audio-lyd</comment>
+ <comment xml:lang="oc">àudio Monkey</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Monkey's Audio</comment>
+ <comment xml:lang="pt">áudio Monkey</comment>
+ <comment xml:lang="pt_BR">Áudio Monkey's</comment>
+ <comment xml:lang="ro">Audio Monkey's</comment>
+ <comment xml:lang="ru">Аудио Monkey's</comment>
+ <comment xml:lang="sk">Zvuk Monkey's</comment>
+ <comment xml:lang="sl">Zvočna datoteka Monkey</comment>
+ <comment xml:lang="sq">Audio Monkey's</comment>
+ <comment xml:lang="sr">Монкијев звук</comment>
+ <comment xml:lang="sv">Monkey's audio</comment>
+ <comment xml:lang="tr">Monkey's sesi</comment>
+ <comment xml:lang="uk">звук Monkey's</comment>
+ <comment xml:lang="vi">Âm thanh cua Monkey</comment>
+ <comment xml:lang="zh_CN">Monkey's Audio 音频</comment>
+ <comment xml:lang="zh_TW">Monkey's 音訊</comment>
+ <magic priority="50">
+ <match value="MAC " type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.ape"/>
+ </mime-type>
+ <mime-type type="audio/x-pn-audibleaudio">
+ <comment>Audible.Com audio</comment>
+ <comment xml:lang="ca">àudio Audible.Com</comment>
+ <comment xml:lang="cs">zvuk Audible.Com</comment>
+ <comment xml:lang="de">Audible.Com-Audio</comment>
+ <comment xml:lang="en_GB">Audible.Com audio</comment>
+ <comment xml:lang="es">sonido de Audible.com</comment>
+ <comment xml:lang="hr">Audible.Com zvučni zapis</comment>
+ <comment xml:lang="hu">Audible.Com hang</comment>
+ <comment xml:lang="id">Audio Audible.Com</comment>
+ <comment xml:lang="it">Audio Audible.Com</comment>
+ <comment xml:lang="kk">Audible.Com аудиосы</comment>
+ <comment xml:lang="ko">Audible.Com 오디오</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Audible.com</comment>
+ <comment xml:lang="pt_BR">Áudio de audible.com</comment>
+ <comment xml:lang="ru">Аудио Audible.Com</comment>
+ <comment xml:lang="sk">Audio Audible.Com</comment>
+ <comment xml:lang="sv">Audible.Com-ljud</comment>
+ <comment xml:lang="uk">звук Audible.Com</comment>
+ <comment xml:lang="zh_CN">Audible.Com 音频</comment>
+ <comment xml:lang="zh_TW">Audible.Com 音訊</comment>
+ <magic priority="50">
+
+ <match value="1469084982" type="big32" offset="4"/>
+ <match value="aax " type="string" offset="8"/>
+ </magic>
+ <glob pattern="*.aa"/>
+ <glob pattern="*.aax"/>
+ <alias type="audio/vnd.audible"/>
+ <alias type="audio/vnd.audible.aax"/>
+ </mime-type>
+ <mime-type type="audio/x-it">
+ <comment>Impulse Tracker audio</comment>
+ <comment xml:lang="ar">Impulse Tracker سمعي</comment>
+ <comment xml:lang="az">Impulse Tracker audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Impulse Tracker</comment>
+ <comment xml:lang="bg">Аудио — Impulse Tracker</comment>
+ <comment xml:lang="ca">àudio d'Impulse Tracker</comment>
+ <comment xml:lang="cs">zvuk Impulse Tracker</comment>
+ <comment xml:lang="cy">Sain Impulse Tracker</comment>
+ <comment xml:lang="da">Impulse Tracker-lyd</comment>
+ <comment xml:lang="de">Impulse-Tracker-Audio</comment>
+ <comment xml:lang="el">Ήχος Impulse Tracker</comment>
+ <comment xml:lang="en_GB">Impulse Tracker audio</comment>
+ <comment xml:lang="eo">Sondosiero de Impulse Tracker</comment>
+ <comment xml:lang="es">sonido de Impulse Tracker</comment>
+ <comment xml:lang="eu">Impulse Tracker audioa</comment>
+ <comment xml:lang="fi">Impulse Tracker -ääni</comment>
+ <comment xml:lang="fo">Impulse Tracker ljóður</comment>
+ <comment xml:lang="fr">audio Impulse Tracker</comment>
+ <comment xml:lang="ga">fuaim Impulse Tracker</comment>
+ <comment xml:lang="gl">son de Impulse Tracker</comment>
+ <comment xml:lang="he">שמע של Impulse Tracker</comment>
+ <comment xml:lang="hr">Impulse Tracker zvučni zapis</comment>
+ <comment xml:lang="hu">Impulse Tracker hang</comment>
+ <comment xml:lang="ia">Audio Impulse Tracker</comment>
+ <comment xml:lang="id">Audio Impulse Tracker</comment>
+ <comment xml:lang="it">Audio Impulse Tracker</comment>
+ <comment xml:lang="ja">Impulse Tracker オーディオ</comment>
+ <comment xml:lang="kk">Impulse Tracker аудиосы</comment>
+ <comment xml:lang="ko">Impulse Tracker 오디오</comment>
+ <comment xml:lang="lt">Impulse Tracker garso įrašas</comment>
+ <comment xml:lang="lv">Impulse Tracker audio</comment>
+ <comment xml:lang="ms">Audio Impulse Tracker</comment>
+ <comment xml:lang="nb">Impulse Tracker-lyd</comment>
+ <comment xml:lang="nl">Impulse Tracker-audio</comment>
+ <comment xml:lang="nn">Impulse Tracker lyd</comment>
+ <comment xml:lang="oc">àudio Impulse Tracker</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Impulse Tracker</comment>
+ <comment xml:lang="pt">áudio Impulse Tracker</comment>
+ <comment xml:lang="pt_BR">Áudio Impulse Tracker</comment>
+ <comment xml:lang="ro">Audio Impulse Tracker</comment>
+ <comment xml:lang="ru">Аудио Impulse Tracker</comment>
+ <comment xml:lang="sk">Zvuk Impulse Tracker</comment>
+ <comment xml:lang="sl">Zvočna datoteka Impulse Tracker</comment>
+ <comment xml:lang="sq">Audio Impulse Tracker</comment>
+ <comment xml:lang="sr">звук Пратиоца Импулса</comment>
+ <comment xml:lang="sv">Impulse Tracker-ljud</comment>
+ <comment xml:lang="tr">Impulse Tracker sesi</comment>
+ <comment xml:lang="uk">звук Impulse Tracker</comment>
+ <comment xml:lang="vi">Âm thanh Impulse Tracker</comment>
+ <comment xml:lang="zh_CN">Impulse Tracker 音频</comment>
+ <comment xml:lang="zh_TW">Impulse Tracker 音訊</comment>
+ <magic priority="50">
+ <match value="IMPM" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.it"/>
+ </mime-type>
+ <mime-type type="audio/flac">
+ <comment>FLAC audio</comment>
+ <comment xml:lang="ar">FLAC سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo FLAC</comment>
+ <comment xml:lang="bg">Аудио — FLAC</comment>
+ <comment xml:lang="ca">àudio FLAC</comment>
+ <comment xml:lang="cs">zvuk FLAC</comment>
+ <comment xml:lang="da">FLAC-lyd</comment>
+ <comment xml:lang="de">FLAC-Audio</comment>
+ <comment xml:lang="el">Ήχος FLAC</comment>
+ <comment xml:lang="en_GB">FLAC audio</comment>
+ <comment xml:lang="eo">FLAC-sondosiero</comment>
+ <comment xml:lang="es">sonido FLAC</comment>
+ <comment xml:lang="eu">FLAC audioa</comment>
+ <comment xml:lang="fi">FLAC-ääni</comment>
+ <comment xml:lang="fo">FLAC ljóður</comment>
+ <comment xml:lang="fr">audio FLAC</comment>
+ <comment xml:lang="ga">fuaim FLAC</comment>
+ <comment xml:lang="gl">son FLAC</comment>
+ <comment xml:lang="he">קובץ שמע מסוג FLAC</comment>
+ <comment xml:lang="hr">FLAC zvučni zapis</comment>
+ <comment xml:lang="hu">FLAC hang</comment>
+ <comment xml:lang="ia">Audio FLAC</comment>
+ <comment xml:lang="id">Audio FLAC</comment>
+ <comment xml:lang="it">Audio FLAC</comment>
+ <comment xml:lang="ja">FLAC オーディオ</comment>
+ <comment xml:lang="ka">FLAC აუდიო</comment>
+ <comment xml:lang="kk">FLAC аудиосы</comment>
+ <comment xml:lang="ko">FLAC 오디오</comment>
+ <comment xml:lang="lt">FLAC garso įrašas</comment>
+ <comment xml:lang="lv">FLAC audio</comment>
+ <comment xml:lang="ms">Audio FLAC</comment>
+ <comment xml:lang="nb">FLAC-lyd</comment>
+ <comment xml:lang="nl">FLAC-audio</comment>
+ <comment xml:lang="nn">FLAC-lyd</comment>
+ <comment xml:lang="oc">àudio FLAC</comment>
+ <comment xml:lang="pl">Plik dźwiękowy FLAC</comment>
+ <comment xml:lang="pt">áudio FLAC</comment>
+ <comment xml:lang="pt_BR">Áudio FLAC</comment>
+ <comment xml:lang="ro">Audio FLAC</comment>
+ <comment xml:lang="ru">Аудио FLAC</comment>
+ <comment xml:lang="sk">Zvuk FLAC</comment>
+ <comment xml:lang="sl">Zvočna datoteka Flac</comment>
+ <comment xml:lang="sq">Audio FLAC</comment>
+ <comment xml:lang="sr">ФЛАЦ звук</comment>
+ <comment xml:lang="sv">FLAC-ljud</comment>
+ <comment xml:lang="tr">FLAC sesi</comment>
+ <comment xml:lang="uk">звук FLAC</comment>
+ <comment xml:lang="vi">Âm thanh FLAC</comment>
+ <comment xml:lang="zh_CN">FLAC 音频</comment>
+ <comment xml:lang="zh_TW">FLAC 音訊</comment>
+ <magic priority="50">
+ <match value="fLaC" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.flac"/>
+ <alias type="audio/x-flac"/>
+ </mime-type>
+ <mime-type type="audio/x-wavpack">
+ <comment>WavPack audio</comment>
+ <comment xml:lang="ar">WavPack سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo WavPack</comment>
+ <comment xml:lang="bg">Аудио — WavPack</comment>
+ <comment xml:lang="ca">àudio de WavPack</comment>
+ <comment xml:lang="cs">zvuk WavPack</comment>
+ <comment xml:lang="da">WavPack-lyd</comment>
+ <comment xml:lang="de">WavPack-Audio</comment>
+ <comment xml:lang="el">Ήχος WavePack</comment>
+ <comment xml:lang="en_GB">WavPack audio</comment>
+ <comment xml:lang="eo">WavPack-sondosiero</comment>
+ <comment xml:lang="es">sonido WavPack</comment>
+ <comment xml:lang="eu">WavPack audioa</comment>
+ <comment xml:lang="fi">WavPack-ääni</comment>
+ <comment xml:lang="fo">WavPack ljóður</comment>
+ <comment xml:lang="fr">audio WavPack</comment>
+ <comment xml:lang="ga">fuaim WavPack</comment>
+ <comment xml:lang="gl">son WavPack</comment>
+ <comment xml:lang="he">שמע WavPack</comment>
+ <comment xml:lang="hr">WavPack zvučni zapis</comment>
+ <comment xml:lang="hu">WavPack hang</comment>
+ <comment xml:lang="ia">Audio WavPack</comment>
+ <comment xml:lang="id">Audio WavPack</comment>
+ <comment xml:lang="it">Audio WavPack</comment>
+ <comment xml:lang="ja">WavPack オーディオ</comment>
+ <comment xml:lang="kk">WavPack аудиосы</comment>
+ <comment xml:lang="ko">WavPack 오디오</comment>
+ <comment xml:lang="lt">WavPack garso įrašas</comment>
+ <comment xml:lang="lv">WavPack audio</comment>
+ <comment xml:lang="nb">WavPack-lyd</comment>
+ <comment xml:lang="nl">WavPack-audio</comment>
+ <comment xml:lang="nn">WavPack-lyd</comment>
+ <comment xml:lang="oc">àudio WavPack</comment>
+ <comment xml:lang="pl">Plik dźwiękowy WavPack</comment>
+ <comment xml:lang="pt">áudio WavPack</comment>
+ <comment xml:lang="pt_BR">Áudio WavPack</comment>
+ <comment xml:lang="ro">Audio WavPack</comment>
+ <comment xml:lang="ru">Аудио WavPack</comment>
+ <comment xml:lang="sk">Zvuk WavPack</comment>
+ <comment xml:lang="sl">Zvočna datoteka WavPack</comment>
+ <comment xml:lang="sq">Audio WavPack</comment>
+ <comment xml:lang="sr">Вејвпак звук</comment>
+ <comment xml:lang="sv">WavPack-ljud</comment>
+ <comment xml:lang="tr">WavPack sesi</comment>
+ <comment xml:lang="uk">звук WavPack</comment>
+ <comment xml:lang="vi">Âm thanh WavPack</comment>
+ <comment xml:lang="zh_CN">WavPack 音频</comment>
+ <comment xml:lang="zh_TW">WavPack 音訊</comment>
+ <magic priority="50">
+ <match value="wvpk" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.wv"/>
+ <glob pattern="*.wvp"/>
+ </mime-type>
+ <mime-type type="audio/x-wavpack-correction">
+ <comment>WavPack audio correction file</comment>
+ <comment xml:lang="ar">ملف تصحيح WavPack السمعي</comment>
+ <comment xml:lang="be@latin">Fajł aŭdyjokarekcyi WavPack</comment>
+ <comment xml:lang="bg">Файл за корекции на аудио — WavPack</comment>
+ <comment xml:lang="ca">fitxer de correcció d'àudio de WavPack</comment>
+ <comment xml:lang="cs">opravný zvukový soubor WavPack</comment>
+ <comment xml:lang="da">WavPack-lydkorrektionsfil</comment>
+ <comment xml:lang="de">WavPack-Audiokorrekturdatei</comment>
+ <comment xml:lang="el">Αρχείο διόρθωσης ήχου WavePack</comment>
+ <comment xml:lang="en_GB">WavPack audio correction file</comment>
+ <comment xml:lang="es">archivo de corrección de sonido WavPack</comment>
+ <comment xml:lang="eu">WavPack audio-zuzenketaren fitxategia</comment>
+ <comment xml:lang="fi">WavPack-äänikorjaustiedosto</comment>
+ <comment xml:lang="fo">WavPack ljóðrættingarfíla</comment>
+ <comment xml:lang="fr">fichier de correction audio WavPack</comment>
+ <comment xml:lang="ga">comhad ceartúchán fuaime WavPack</comment>
+ <comment xml:lang="gl">ficheiro de corrección de son WavPack</comment>
+ <comment xml:lang="he">קובץ תיקון שמע של WavPack</comment>
+ <comment xml:lang="hr">WavPack datoteka ispravke zvuka</comment>
+ <comment xml:lang="hu">WavPack hangjavítási fájl</comment>
+ <comment xml:lang="ia">File de correction audio WavPack</comment>
+ <comment xml:lang="id">Berkas koreksi audio WavPack</comment>
+ <comment xml:lang="it">File correzione audio WavPack</comment>
+ <comment xml:lang="ja">WavPack オーディオコレクションファイル</comment>
+ <comment xml:lang="kk">WavPack аудио түзету файлы</comment>
+ <comment xml:lang="ko">WavPack 오디오 교정 파일</comment>
+ <comment xml:lang="lt">WavPack garso korekcijos failas</comment>
+ <comment xml:lang="lv">WavPack audio korekciju datne</comment>
+ <comment xml:lang="nb">WavPack lydkorrigeringsfil</comment>
+ <comment xml:lang="nl">WavPack-audio-correctiebestand</comment>
+ <comment xml:lang="nn">WawPack lydopprettingsfil</comment>
+ <comment xml:lang="oc">fichièr de correccion àudio WavPack</comment>
+ <comment xml:lang="pl">Plik korekcji dźwięku WavPack</comment>
+ <comment xml:lang="pt">ficheiro de correção áudio WavPack</comment>
+ <comment xml:lang="pt_BR">Arquivo de correção de áudio WavPack</comment>
+ <comment xml:lang="ro">Fișier audio de corecție WavPack</comment>
+ <comment xml:lang="ru">Файл коррекции аудио WavPack</comment>
+ <comment xml:lang="sk">Opravný zvukový súbor WavPack</comment>
+ <comment xml:lang="sl">popravljalna zvočna datoteka WavPack</comment>
+ <comment xml:lang="sq">File korrigjgimi audio WavPack</comment>
+ <comment xml:lang="sr">датотека поправке Вејвпак звука</comment>
+ <comment xml:lang="sv">WavPack-ljudkorrigeringsfil</comment>
+ <comment xml:lang="tr">WavPack ses düzeltme dosyası</comment>
+ <comment xml:lang="uk">файл корекції звуку WavPack</comment>
+ <comment xml:lang="vi">Tập tin sửa chữa âm thanh WavPack</comment>
+ <comment xml:lang="zh_CN">WavPack 音频校正文件</comment>
+ <comment xml:lang="zh_TW">WavPack 音訊校正檔</comment>
+ <magic priority="50">
+ <match value="wvpk" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.wvc"/>
+ </mime-type>
+ <mime-type type="audio/midi">
+ <comment>MIDI audio</comment>
+ <comment xml:lang="ar">MIDI سمعي</comment>
+ <comment xml:lang="az">MIDI audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo MIDI</comment>
+ <comment xml:lang="bg">Аудио — MIDI</comment>
+ <comment xml:lang="ca">àudio MIDI</comment>
+ <comment xml:lang="cs">zvuk MIDI</comment>
+ <comment xml:lang="cy">Sain MIDI</comment>
+ <comment xml:lang="da">MIDI-lyd</comment>
+ <comment xml:lang="de">MIDI-Audio</comment>
+ <comment xml:lang="el">Ήχος MIDI</comment>
+ <comment xml:lang="en_GB">MIDI audio</comment>
+ <comment xml:lang="eo">MIDI-sondosiero</comment>
+ <comment xml:lang="es">sonido MIDI</comment>
+ <comment xml:lang="eu">MIDI audioa</comment>
+ <comment xml:lang="fi">MIDI-ääni</comment>
+ <comment xml:lang="fo">MIDI ljóður</comment>
+ <comment xml:lang="fr">audio MIDI</comment>
+ <comment xml:lang="ga">fuaim MIDI</comment>
+ <comment xml:lang="gl">son MIDI</comment>
+ <comment xml:lang="he">שמע MIDI</comment>
+ <comment xml:lang="hr">MIDI zvučni zapis</comment>
+ <comment xml:lang="hu">MIDI hang</comment>
+ <comment xml:lang="ia">Audio MIDI</comment>
+ <comment xml:lang="id">Audio MIDI</comment>
+ <comment xml:lang="it">Audio MIDI</comment>
+ <comment xml:lang="ja">MIDI オーディオ</comment>
+ <comment xml:lang="kk">MIDI аудиосы</comment>
+ <comment xml:lang="ko">미디 오디오</comment>
+ <comment xml:lang="lt">MIDI garso įrašas</comment>
+ <comment xml:lang="lv">MIDI audio</comment>
+ <comment xml:lang="ms">Audio MIDI</comment>
+ <comment xml:lang="nb">MIDI-lyd</comment>
+ <comment xml:lang="nl">MIDI-audio</comment>
+ <comment xml:lang="nn">MIDI-lyd</comment>
+ <comment xml:lang="oc">àudio MIDI</comment>
+ <comment xml:lang="pl">Plik dźwiękowy MIDI</comment>
+ <comment xml:lang="pt">áudio MIDI</comment>
+ <comment xml:lang="pt_BR">Áudio MIDI</comment>
+ <comment xml:lang="ro">Audio MIDI</comment>
+ <comment xml:lang="ru">Аудио MIDI</comment>
+ <comment xml:lang="sk">Zvuk MIDI</comment>
+ <comment xml:lang="sl">Zvočna datoteka MIDI</comment>
+ <comment xml:lang="sq">Audio MIDI</comment>
+ <comment xml:lang="sr">МИДИ звук</comment>
+ <comment xml:lang="sv">MIDI-ljud</comment>
+ <comment xml:lang="tr">MIDI sesi</comment>
+ <comment xml:lang="uk">звук MIDI</comment>
+ <comment xml:lang="vi">Âm thanh MIDI</comment>
+ <comment xml:lang="zh_CN">MIDI 音频</comment>
+ <comment xml:lang="zh_TW">MIDI 音訊</comment>
+ <alias type="audio/x-midi"/>
+ <magic priority="50">
+ <match value="MThd" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mid"/>
+ <glob pattern="*.midi"/>
+ <glob pattern="*.kar"/>
+ </mime-type>
+ <mime-type type="audio/x-mo3">
+ <comment>compressed Tracker audio</comment>
+ <comment xml:lang="ar">Tracker سمعي مضغوط</comment>
+ <comment xml:lang="be@latin">aŭdyjo skampresavanaha Trackera</comment>
+ <comment xml:lang="bg">Аудио — Tracker, компресирано</comment>
+ <comment xml:lang="ca">àudio Tracker amb compressió</comment>
+ <comment xml:lang="cs">komprimovaný zvuk Tracker</comment>
+ <comment xml:lang="da">Trackerkomprimeret lyd</comment>
+ <comment xml:lang="de">Komprimiertes Tracker-Audio</comment>
+ <comment xml:lang="el">Συμπιεσμένος ήχος Tracker</comment>
+ <comment xml:lang="en_GB">compressed Tracker audio</comment>
+ <comment xml:lang="es">sonido de Tracker comprimido</comment>
+ <comment xml:lang="eu">konprimitutako Tracker audioa</comment>
+ <comment xml:lang="fi">pakattu Tracker-ääni</comment>
+ <comment xml:lang="fo">stappað Tracker ljóður</comment>
+ <comment xml:lang="fr">audio Tracker compressé</comment>
+ <comment xml:lang="ga">fuaim chomhbhrúite Tracker</comment>
+ <comment xml:lang="gl">son comprimido de Tracker</comment>
+ <comment xml:lang="he">שמע גשש מכווץ</comment>
+ <comment xml:lang="hr">Sažeti Tracker zvučni zapis</comment>
+ <comment xml:lang="hu">tömörített Tracker hang</comment>
+ <comment xml:lang="ia">Audio Tracker comprimite</comment>
+ <comment xml:lang="id">audio Tracker terkompresi</comment>
+ <comment xml:lang="it">Audio compresso Tracker</comment>
+ <comment xml:lang="ja">圧縮 Tracker オーディオ</comment>
+ <comment xml:lang="kk">сығылған Tracker аудиосы</comment>
+ <comment xml:lang="ko">압축된 Tracker 오디오</comment>
+ <comment xml:lang="lt">suglaudintas Tracker garso įrašas</comment>
+ <comment xml:lang="lv">saspiests Tracker audio</comment>
+ <comment xml:lang="nl">ingepakte Tracker-audio</comment>
+ <comment xml:lang="nn">komprimert Tracker-lyd</comment>
+ <comment xml:lang="oc">àudio Tracker compressat</comment>
+ <comment xml:lang="pl">Skompresowany plik dźwiękowy Tracker</comment>
+ <comment xml:lang="pt">áudio comprimido Tracker</comment>
+ <comment xml:lang="pt_BR">Áudio Tracker compactado</comment>
+ <comment xml:lang="ro">Tracker audio comprimat</comment>
+ <comment xml:lang="ru">Сжатое аудио Tracker</comment>
+ <comment xml:lang="sk">Komprimovaný zvuk Tracker</comment>
+ <comment xml:lang="sl">Skrčena zvočna datoteka Tracker</comment>
+ <comment xml:lang="sq">Audio Tracker e kompresuar</comment>
+ <comment xml:lang="sr">запаковани звук Пратиоца</comment>
+ <comment xml:lang="sv">komprimerat Tracker-ljud</comment>
+ <comment xml:lang="tr">sıkıştırılmış Tracker sesi</comment>
+ <comment xml:lang="uk">стиснутий звук Tracker</comment>
+ <comment xml:lang="vi">âm thanh Tracker đã nén</comment>
+ <comment xml:lang="zh_CN">压缩的 Tracker 音频</comment>
+ <comment xml:lang="zh_TW">壓縮版 Tracker 音訊</comment>
+ <magic priority="50">
+ <match value="MO3" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mo3"/>
+ </mime-type>
+ <mime-type type="audio/aac">
+ <comment>AAC audio</comment>
+ <comment xml:lang="ca">àudio AAC</comment>
+ <comment xml:lang="cs">zvuk AAC</comment>
+ <comment xml:lang="da">AAC-lyd</comment>
+ <comment xml:lang="de">AAC-Audio</comment>
+ <comment xml:lang="el">Ήχος AAC</comment>
+ <comment xml:lang="en_GB">AAC audio</comment>
+ <comment xml:lang="es">sonido AAC</comment>
+ <comment xml:lang="eu">AAC audioa</comment>
+ <comment xml:lang="fi">AAC-ääni</comment>
+ <comment xml:lang="fr">audio AAC</comment>
+ <comment xml:lang="ga">fuaim AAC</comment>
+ <comment xml:lang="gl">Son AAC</comment>
+ <comment xml:lang="he">שמע AAC</comment>
+ <comment xml:lang="hr">AAC zvučni zapis</comment>
+ <comment xml:lang="hu">AAC hang</comment>
+ <comment xml:lang="ia">Audio ACC</comment>
+ <comment xml:lang="id">Audio AAC</comment>
+ <comment xml:lang="it">Audio AAC</comment>
+ <comment xml:lang="ja">AAC オーディオ</comment>
+ <comment xml:lang="kk">AAC аудиосы</comment>
+ <comment xml:lang="ko">AAC 오디오</comment>
+ <comment xml:lang="lv">AAC audio</comment>
+ <comment xml:lang="oc">àudio AAC</comment>
+ <comment xml:lang="pl">Plik dźwiękowy AAC</comment>
+ <comment xml:lang="pt">áudio AAC</comment>
+ <comment xml:lang="pt_BR">Áudio AAC</comment>
+ <comment xml:lang="ru">Аудио AAC</comment>
+ <comment xml:lang="sk">Zvuk AAC</comment>
+ <comment xml:lang="sl">Zvok AAC</comment>
+ <comment xml:lang="sr">ААЦ звук</comment>
+ <comment xml:lang="sv">AAC-ljud</comment>
+ <comment xml:lang="tr">AAC sesi</comment>
+ <comment xml:lang="uk">звукові дані AAC</comment>
+ <comment xml:lang="zh_CN">AAC 音频</comment>
+ <comment xml:lang="zh_TW">AAC 音訊</comment>
+ <acronym>AAC</acronym>
+ <expanded-acronym>Advanced Audio Coding</expanded-acronym>
+ <magic priority="50">
+ <match value="ADIF" type="string" offset="0"/>
+ <match value="0xFFF0" type="big16" offset="0" mask="0xFFF6"/>
+ </magic>
+ <glob pattern="*.aac"/>
+ <glob pattern="*.adts"/>
+ <glob weight="10" pattern="*.ass"/>
+ <alias type="audio/x-aac"/>
+ </mime-type>
+ <mime-type type="audio/usac">
+ <comment>USAC audio</comment>
+ <acronym>USAC</acronym>
+ <expanded-acronym>Unified Speech and Audio Coding</expanded-acronym>
+ <glob pattern="*.loas"/>
+ <glob pattern="*.xhe"/>
+ </mime-type>
+ <mime-type type="audio/mp4">
+ <comment>MPEG-4 audio</comment>
+ <comment xml:lang="ar">MPEG-4 سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo MPEG-4</comment>
+ <comment xml:lang="bg">Аудио — MPEG-4</comment>
+ <comment xml:lang="ca">àudio MPEG-4</comment>
+ <comment xml:lang="cs">zvuk MPEG-4</comment>
+ <comment xml:lang="da">MPEG4-lyd</comment>
+ <comment xml:lang="de">MPEG-4-Audio</comment>
+ <comment xml:lang="el">Ήχος MPEG-4</comment>
+ <comment xml:lang="en_GB">MPEG-4 audio</comment>
+ <comment xml:lang="eo">MPEG4-sondosiero</comment>
+ <comment xml:lang="es">sonido MPEG-4</comment>
+ <comment xml:lang="eu">MPEG-4 audioa</comment>
+ <comment xml:lang="fi">MPEG-4-ääni</comment>
+ <comment xml:lang="fo">MPEG-4 ljóður</comment>
+ <comment xml:lang="fr">audio MPEG-4</comment>
+ <comment xml:lang="ga">fuaim MPEG-4</comment>
+ <comment xml:lang="gl">son MPEG-4</comment>
+ <comment xml:lang="he">שמע MPEG-4</comment>
+ <comment xml:lang="hr">MPEG-4 zvučni zapis</comment>
+ <comment xml:lang="hu">MPEG-4 hang</comment>
+ <comment xml:lang="ia">Audio MPEG-4</comment>
+ <comment xml:lang="id">Audio MPEG-4</comment>
+ <comment xml:lang="it">Audio MPEG-4</comment>
+ <comment xml:lang="ja">MPEG-4 オーディオ</comment>
+ <comment xml:lang="ka">MPEG-4 აუდიო</comment>
+ <comment xml:lang="kk">MPEG-4 аудиосы</comment>
+ <comment xml:lang="ko">MPEG-4 오디오</comment>
+ <comment xml:lang="lt">MPEG-4 garso įrašas</comment>
+ <comment xml:lang="lv">MPEG-4 audio</comment>
+ <comment xml:lang="nb">MPEG-4-lyd</comment>
+ <comment xml:lang="nl">MPEG4-audio</comment>
+ <comment xml:lang="nn">MPEG-4-lyd</comment>
+ <comment xml:lang="oc">àudio MPEG-4</comment>
+ <comment xml:lang="pl">Plik dźwiękowy MPEG-4</comment>
+ <comment xml:lang="pt">áudio MPEG-4</comment>
+ <comment xml:lang="pt_BR">Áudio MPEG-4</comment>
+ <comment xml:lang="ro">Audio MPEG-4</comment>
+ <comment xml:lang="ru">Аудио MPEG-4</comment>
+ <comment xml:lang="sk">Zvuk MPEG-4</comment>
+ <comment xml:lang="sl">Zvočna datoteka MPEG-4</comment>
+ <comment xml:lang="sq">Audio MPEG-4</comment>
+ <comment xml:lang="sr">МПЕГ-4 звук</comment>
+ <comment xml:lang="sv">MPEG-4-ljud</comment>
+ <comment xml:lang="tr">MPEG-4 sesi</comment>
+ <comment xml:lang="uk">звук MPEG-4</comment>
+ <comment xml:lang="vi">Âm thanh MPEG-4</comment>
+ <comment xml:lang="zh_CN">MPEG-4 音频</comment>
+ <comment xml:lang="zh_TW">MPEG-4 音訊</comment>
+ <alias type="audio/x-m4a"/>
+ <alias type="audio/m4a"/>
+ <magic priority="50">
+ <match value="ftypM4A" type="string" offset="4"/>
+ </magic>
+ <glob pattern="*.m4a"/>
+ <glob pattern="*.f4a"/>
+ </mime-type>
+ <mime-type type="audio/x-m4r">
+ <comment>MPEG-4 Ringtone</comment>
+ <glob pattern="*.m4r"/>
+ <sub-class-of type="video/mp4"/>
+ </mime-type>
+ <mime-type type="video/mp4">
+ <comment>MPEG-4 video</comment>
+ <comment xml:lang="ar">MPEG-4 مرئي</comment>
+ <comment xml:lang="ast">Videu en MPEG-4</comment>
+ <comment xml:lang="be@latin">Videa MPEG-4</comment>
+ <comment xml:lang="bg">Видео — MPEG-4</comment>
+ <comment xml:lang="ca">vídeo MPEG-4</comment>
+ <comment xml:lang="cs">video MPEG-4</comment>
+ <comment xml:lang="da">MPEG4-video</comment>
+ <comment xml:lang="de">MPEG-4-Video</comment>
+ <comment xml:lang="el">Βίντεο MPEG-4</comment>
+ <comment xml:lang="en_GB">MPEG-4 video</comment>
+ <comment xml:lang="eo">MPEG-4-video</comment>
+ <comment xml:lang="es">vídeo MPEG-4</comment>
+ <comment xml:lang="eu">MPEG-4 bideoa</comment>
+ <comment xml:lang="fi">MPEG-4-video</comment>
+ <comment xml:lang="fo">MPEG-4 video</comment>
+ <comment xml:lang="fr">vidéo MPEG-4</comment>
+ <comment xml:lang="ga">físeán MPEG-4</comment>
+ <comment xml:lang="gl">vídeo MPEG-4</comment>
+ <comment xml:lang="he">וידאו MPEG-4</comment>
+ <comment xml:lang="hr">MPEG-4 video snimka</comment>
+ <comment xml:lang="hu">MPEG-4 videó</comment>
+ <comment xml:lang="ia">Video MPEG-4</comment>
+ <comment xml:lang="id">Video MPEG-4</comment>
+ <comment xml:lang="it">Video MPEG-4</comment>
+ <comment xml:lang="ja">MPEG-4 動画</comment>
+ <comment xml:lang="ka">MPEG-4 ვიდეო</comment>
+ <comment xml:lang="kk">MPEG-4 видеосы</comment>
+ <comment xml:lang="ko">MPEG-4 동영상</comment>
+ <comment xml:lang="lt">MPEG-4 vaizdo įrašas</comment>
+ <comment xml:lang="lv">MPEG-4 video</comment>
+ <comment xml:lang="nb">MPEG-4-film</comment>
+ <comment xml:lang="nl">MPEG4-video</comment>
+ <comment xml:lang="nn">MPEG-4-video</comment>
+ <comment xml:lang="oc">vidèo MPEG-4</comment>
+ <comment xml:lang="pl">Plik wideo MPEG-4</comment>
+ <comment xml:lang="pt">vídeo MPEG-4</comment>
+ <comment xml:lang="pt_BR">Vídeo MPEG-4</comment>
+ <comment xml:lang="ro">Video MPEG-4</comment>
+ <comment xml:lang="ru">Видео MPEG-4</comment>
+ <comment xml:lang="sk">Video MPEG-4</comment>
+ <comment xml:lang="sl">Video datoteka MPEG-4</comment>
+ <comment xml:lang="sq">Video MPEG-4</comment>
+ <comment xml:lang="sr">МПЕГ-4 видео</comment>
+ <comment xml:lang="sv">MPEG-4-video</comment>
+ <comment xml:lang="tr">MPEG-4 video</comment>
+ <comment xml:lang="uk">відеокліп MPEG-4</comment>
+ <comment xml:lang="vi">Ảnh động MPEG-4</comment>
+ <comment xml:lang="zh_CN">MPEG-4 视频</comment>
+ <comment xml:lang="zh_TW">MPEG-4 視訊</comment>
+ <alias type="video/mp4v-es"/>
+ <magic priority="50">
+ <match value="ftypisom" type="string" offset="4"/>
+ <match value="ftypmp42" type="string" offset="4"/>
+ <match value="ftypMSNV" type="string" offset="4"/>
+ <match value="ftypM4V " type="string" offset="4"/>
+ <match value="ftypf4v " type="string" offset="4"/>
+ </magic>
+ <glob pattern="*.mp4"/>
+ <glob pattern="*.m4v"/>
+ <glob pattern="*.f4v"/>
+ <glob pattern="*.lrv"/>
+ <alias type="video/x-m4v"/>
+ </mime-type>
+ <mime-type type="audio/x-m4b">
+ <comment>MPEG-4 audio book</comment>
+ <comment xml:lang="ar">كتاب MPEG-4 السمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjokniha MPEG-4</comment>
+ <comment xml:lang="bg">Аудио книга — MPEG-4</comment>
+ <comment xml:lang="ca">llibre d'àudio MPEG-4</comment>
+ <comment xml:lang="cs">zvuková kniha MPEG-4</comment>
+ <comment xml:lang="da">MPEG4-lydbog</comment>
+ <comment xml:lang="de">MPEG-4-Hörbuch</comment>
+ <comment xml:lang="el">Ηχητικό βιβλίο MPEG-4</comment>
+ <comment xml:lang="en_GB">MPEG-4 audio book</comment>
+ <comment xml:lang="eo">MPEG-4-sonlibro</comment>
+ <comment xml:lang="es">audiolibro MPEG-4</comment>
+ <comment xml:lang="eu">MPEG-4 audio-liburua</comment>
+ <comment xml:lang="fi">MPEG-4-äänikirja</comment>
+ <comment xml:lang="fo">MPEG-4 ljóðbók</comment>
+ <comment xml:lang="fr">livre audio MPEG-4</comment>
+ <comment xml:lang="ga">closleabhar MPEG-4</comment>
+ <comment xml:lang="gl">sonlibro de MPEG-4</comment>
+ <comment xml:lang="he">ספר דיגיטלי MPEG-4</comment>
+ <comment xml:lang="hr">MPEG-4 zvučna knjiga</comment>
+ <comment xml:lang="hu">MPEG-4 hangoskönyv</comment>
+ <comment xml:lang="ia">Libro audio MPEG-4</comment>
+ <comment xml:lang="id">Buku audio MPEG-4</comment>
+ <comment xml:lang="it">Audiolibro MPEG-4</comment>
+ <comment xml:lang="ja">MPEG-4 オーディオブック</comment>
+ <comment xml:lang="ka">MPEG-4 აუდიოწიგნი</comment>
+ <comment xml:lang="kk">MPEG-4 аудио кітабы</comment>
+ <comment xml:lang="ko">MPEG-4 오디오북</comment>
+ <comment xml:lang="lt">MPEG-4 garso knyga</comment>
+ <comment xml:lang="lv">MPEG-4 audio grāmata</comment>
+ <comment xml:lang="nb">MPEG-4-lydbok</comment>
+ <comment xml:lang="nl">MPEG4-audioboek</comment>
+ <comment xml:lang="nn">MPEG-4-lydbok</comment>
+ <comment xml:lang="oc">libre àudio MPEG-4</comment>
+ <comment xml:lang="pl">Książka dźwiękowa MPEG-4</comment>
+ <comment xml:lang="pt">livro áudio MPEG-4</comment>
+ <comment xml:lang="pt_BR">Áudio livro MPEG-4</comment>
+ <comment xml:lang="ro">Carte audio MPEG-4</comment>
+ <comment xml:lang="ru">Аудиокнига MPEG-4</comment>
+ <comment xml:lang="sk">Zvuková kniha MPEG-4</comment>
+ <comment xml:lang="sl">Zvočna knjiga MPEG-4</comment>
+ <comment xml:lang="sq">Audiolibër MPEG-4</comment>
+ <comment xml:lang="sr">МПЕГ-4 звукотека</comment>
+ <comment xml:lang="sv">MPEG-4-ljudbok</comment>
+ <comment xml:lang="tr">MPEG-4 sesli kitabı</comment>
+ <comment xml:lang="uk">аудіокнига MPEG-4</comment>
+ <comment xml:lang="vi">Sách âm thanh MPEG-4</comment>
+ <comment xml:lang="zh_CN">MPEG-4 有声书</comment>
+ <comment xml:lang="zh_TW">MPEG-4 音訊書</comment>
+ <sub-class-of type="audio/mp4"/>
+ <magic priority="50">
+ <match value="ftypM4B" type="string" offset="4"/>
+ </magic>
+ <glob pattern="*.m4b"/>
+ <glob pattern="*.f4b"/>
+ </mime-type>
+ <mime-type type="video/3gpp">
+ <comment>3GPP multimedia file</comment>
+ <comment xml:lang="ar">ملف وسائط متعددة 3GPP</comment>
+ <comment xml:lang="be@latin">Multymedyjny fajł 3GPP</comment>
+ <comment xml:lang="bg">Мултимедия — 3GPP</comment>
+ <comment xml:lang="ca">fitxer multimèdia 3GPP</comment>
+ <comment xml:lang="cs">multimediální soubor 3GPP</comment>
+ <comment xml:lang="da">3GPP multimedie-fil</comment>
+ <comment xml:lang="de">3GPP-Multimediadatei</comment>
+ <comment xml:lang="el">Αρχείο πολυμέσων 3GPP</comment>
+ <comment xml:lang="en_GB">3GPP multimedia file</comment>
+ <comment xml:lang="es">archivo multimedia 3GPP</comment>
+ <comment xml:lang="eu">3GPP multimediako fitxategia</comment>
+ <comment xml:lang="fi">3GPP-multimediatiedosto</comment>
+ <comment xml:lang="fo">3GGP margmiðlafíla</comment>
+ <comment xml:lang="fr">fichier multimédia 3GPP</comment>
+ <comment xml:lang="ga">comhad ilmheán 3GPP</comment>
+ <comment xml:lang="gl">ficheiro multimedia 3GPP</comment>
+ <comment xml:lang="he">קובץ מולטימדיה מסוג 3GPP</comment>
+ <comment xml:lang="hr">3GPP multimedijska datoteka</comment>
+ <comment xml:lang="hu">3GPP multimédiafájl</comment>
+ <comment xml:lang="ia">File multimedial 3GPP</comment>
+ <comment xml:lang="id">Berkas multimedia 3GPP</comment>
+ <comment xml:lang="it">File multimediale 3GPP</comment>
+ <comment xml:lang="ja">3GPP マルチメディアファイル</comment>
+ <comment xml:lang="ka">3GPP მულტიმედიური ფაილი</comment>
+ <comment xml:lang="kk">3GPP мультимедиялық файлы</comment>
+ <comment xml:lang="ko">3GPP 멀티미디어 파일</comment>
+ <comment xml:lang="lt">3GPP multimedijos failas</comment>
+ <comment xml:lang="lv">3GPP multimediju datne</comment>
+ <comment xml:lang="nb">3GPP-multimediafil</comment>
+ <comment xml:lang="nl">3GPP-multimediabestand</comment>
+ <comment xml:lang="nn">3GPP-multimediafil</comment>
+ <comment xml:lang="oc">fichièr multimèdia 3GPP</comment>
+ <comment xml:lang="pl">Plik multimedialny 3GPP</comment>
+ <comment xml:lang="pt">ficheiro multimédia 3GPP</comment>
+ <comment xml:lang="pt_BR">Arquivo multimídia 3GPP</comment>
+ <comment xml:lang="ro">Fișier multimedia 3GPP</comment>
+ <comment xml:lang="ru">Мультимедийный файл 3GPP</comment>
+ <comment xml:lang="sk">Súbor multimédií 3GPP</comment>
+ <comment xml:lang="sl">Večpredstavnostna datoteka 3GPP</comment>
+ <comment xml:lang="sq">File multimedial 3GPP</comment>
+ <comment xml:lang="sr">3ГПП мултимедијална датотека</comment>
+ <comment xml:lang="sv">3GPP-multimediafil</comment>
+ <comment xml:lang="tr">3GPP multimedya dosyası</comment>
+ <comment xml:lang="uk">файл мультимедійних даних 3GPP</comment>
+ <comment xml:lang="vi">Tập tin đa phương tiện 3GPP</comment>
+ <comment xml:lang="zh_CN">3GPP 多媒体文件</comment>
+ <comment xml:lang="zh_TW">3GPP 多媒體檔案</comment>
+ <acronym>3GPP</acronym>
+ <expanded-acronym>3rd Generation Partnership Project</expanded-acronym>
+ <sub-class-of type="video/mp4"/>
+ <magic priority="50">
+ <match value="ftyp3ge" type="string" offset="4"/>
+ <match value="ftyp3gg" type="string" offset="4"/>
+ <match value="ftyp3gp" type="string" offset="4"/>
+ <match value="ftyp3gs" type="string" offset="4"/>
+ </magic>
+ <glob pattern="*.3gp"/>
+ <glob pattern="*.3gpp"/>
+ <glob pattern="*.3ga"/>
+ <alias type="video/3gp"/>
+ <alias type="audio/3gpp"/>
+ <alias type="video/3gpp-encrypted"/>
+ <alias type="audio/3gpp-encrypted"/>
+ <alias type="audio/x-rn-3gpp-amr"/>
+ <alias type="audio/x-rn-3gpp-amr-encrypted"/>
+ <alias type="audio/x-rn-3gpp-amr-wb"/>
+ <alias type="audio/x-rn-3gpp-amr-wb-encrypted"/>
+ </mime-type>
+ <mime-type type="video/3gpp2">
+ <comment>3GPP2 multimedia file</comment>
+ <comment xml:lang="ar">ملف وسائط متعددة 3GPP2</comment>
+ <comment xml:lang="bg">Мултимедия — 3GPP2</comment>
+ <comment xml:lang="ca">fitxer multimèdia 3GPP2</comment>
+ <comment xml:lang="cs">multimediální soubor 3GPP2</comment>
+ <comment xml:lang="da">3GPP2 multimedie-fil</comment>
+ <comment xml:lang="de">3GPP2-Multimediadatei</comment>
+ <comment xml:lang="el">Αρχείο πολυμέσων 3GPP2</comment>
+ <comment xml:lang="en_GB">3GPP2 multimedia file</comment>
+ <comment xml:lang="es">archivo multimedia 3GPP2</comment>
+ <comment xml:lang="eu">3GPP2 multimediako fitxategia</comment>
+ <comment xml:lang="fi">3GPP2-multimediatiedosto</comment>
+ <comment xml:lang="fo">3GGP2 margmiðlafíla</comment>
+ <comment xml:lang="fr">fichier multimédia 3GPP2</comment>
+ <comment xml:lang="ga">comhad ilmheán 3GPP2</comment>
+ <comment xml:lang="gl">ficheiro multimedia 3GPP2</comment>
+ <comment xml:lang="he">קובץ מולטימדיה 3GPP2</comment>
+ <comment xml:lang="hr">3GPP2 multimedijska datoteka</comment>
+ <comment xml:lang="hu">3GPP2 multimédiafájl</comment>
+ <comment xml:lang="ia">File multimedial 3GPP2</comment>
+ <comment xml:lang="id">Berkas multimedia 3GPP2</comment>
+ <comment xml:lang="it">File multimediale 3GPP2</comment>
+ <comment xml:lang="ja">3GPP2 マルチメディアファイル</comment>
+ <comment xml:lang="ka">3GPP2 მულტიმედიური ფაილი</comment>
+ <comment xml:lang="kk">3GPP2 мультимедиялық файлы</comment>
+ <comment xml:lang="ko">3GPP2 멀티미디어 파일</comment>
+ <comment xml:lang="lv">3GPP2 multimediju datne</comment>
+ <comment xml:lang="nl">3GPP2 multimedia bestand</comment>
+ <comment xml:lang="oc">fichièr multimèdia 3GPP2</comment>
+ <comment xml:lang="pl">Plik multimedialny 3GPP2</comment>
+ <comment xml:lang="pt">ficheiro multimédia 3GPP2</comment>
+ <comment xml:lang="pt_BR">Arquivo multimídia 3GPP2</comment>
+ <comment xml:lang="ro">Fișier multimedia 3GPP2</comment>
+ <comment xml:lang="ru">Мультимедийный файл 3GPP2</comment>
+ <comment xml:lang="sk">Súbor multimédií 3GPP2</comment>
+ <comment xml:lang="sl">Večpredstavnostna datoteka 3GPP2</comment>
+ <comment xml:lang="sr">3ГПП2 мултимедијална датотека</comment>
+ <comment xml:lang="sv">3GPP2-multimediafil</comment>
+ <comment xml:lang="tr">3GPP2 multimedya dosyası</comment>
+ <comment xml:lang="uk">файл мультимедійних даних 3GPP2</comment>
+ <comment xml:lang="zh_CN">3GPP2 多媒体文件</comment>
+ <comment xml:lang="zh_TW">3GPP2 多媒體檔案</comment>
+ <acronym>3GPP2</acronym>
+ <expanded-acronym>3rd Generation Partnership Project 2</expanded-acronym>
+ <sub-class-of type="video/mp4"/>
+ <magic priority="50">
+ <match value="ftyp3g2" type="string" offset="4"/>
+ </magic>
+ <glob pattern="*.3g2"/>
+ <glob pattern="*.3gp2"/>
+ <glob pattern="*.3gpp2"/>
+ <alias type="audio/3gpp2"/>
+ </mime-type>
+ <mime-type type="audio/x-mod">
+ <comment>Amiga SoundTracker audio</comment>
+ <comment xml:lang="ar">مقتفي صوت Amiga السمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Amiga SoundTracker</comment>
+ <comment xml:lang="bg">Аудио — Amiga SoundTracker</comment>
+ <comment xml:lang="ca">àudio SoundTracker d'Amiga</comment>
+ <comment xml:lang="cs">zvuk Amiga SoundTracker</comment>
+ <comment xml:lang="da">Amiga SoundTracker-lyd</comment>
+ <comment xml:lang="de">Amiga-SoundTracker-Audio</comment>
+ <comment xml:lang="el">Ήχος Amiga SoundTracker</comment>
+ <comment xml:lang="en_GB">Amiga SoundTracker audio</comment>
+ <comment xml:lang="eo">Sondosiero de Amiga SoundTracker</comment>
+ <comment xml:lang="es">sonido de Amiga SoundTracker</comment>
+ <comment xml:lang="eu">Amiga soundtracker audioa</comment>
+ <comment xml:lang="fi">Amiga SoundTracker -ääni</comment>
+ <comment xml:lang="fo">Amiga SoundTracker ljóður</comment>
+ <comment xml:lang="fr">audio SoundTracker Amiga</comment>
+ <comment xml:lang="ga">fuaim Amiga SoundTracker</comment>
+ <comment xml:lang="gl">son de Amiga SoundTracker</comment>
+ <comment xml:lang="he">קובץ שמע של Amiga SoundTracker</comment>
+ <comment xml:lang="hr">Amiga SoundTracker zvučni zapis</comment>
+ <comment xml:lang="hu">Amiga SoundTracker hang</comment>
+ <comment xml:lang="ia">Audio Amiga SoundTracker</comment>
+ <comment xml:lang="id">Audio Amida SoundTracker</comment>
+ <comment xml:lang="it">Audio Amiga SoundTracker</comment>
+ <comment xml:lang="ja">Amiga SoundTracker オーディオ</comment>
+ <comment xml:lang="ka">Amiga SoundTracker-ის აუდიო</comment>
+ <comment xml:lang="kk">Amiga SoundTracker аудиосы</comment>
+ <comment xml:lang="ko">Amiga SoundTracker 오디오</comment>
+ <comment xml:lang="lt">Amiga SoundTracker garso įrašas</comment>
+ <comment xml:lang="lv">Amiga SoundTracker audio</comment>
+ <comment xml:lang="ms">Audio Amiga Soundtracker</comment>
+ <comment xml:lang="nb">Amiga SoundTracker-lyd</comment>
+ <comment xml:lang="nl">Amiga SoundTracker-audio</comment>
+ <comment xml:lang="nn">Amiga soundtracker-lyd</comment>
+ <comment xml:lang="oc">àudio SoundTracker Amiga</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Amiga SoundTracker</comment>
+ <comment xml:lang="pt">áudio SoundTracker do Amiga</comment>
+ <comment xml:lang="pt_BR">Áudio Amiga SoundTracker</comment>
+ <comment xml:lang="ro">Audio Amiga SoundTracker</comment>
+ <comment xml:lang="ru">Аудио Amiga SoundTracker</comment>
+ <comment xml:lang="sk">Zvuk Amiga SoundTracker</comment>
+ <comment xml:lang="sl">Zvočna datoteka Amiga SoundTracker</comment>
+ <comment xml:lang="sq">Audio Amiga SoundTracker</comment>
+ <comment xml:lang="sr">звук Амигиног Пратиоца Звука</comment>
+ <comment xml:lang="sv">Amiga SoundTracker-ljud</comment>
+ <comment xml:lang="tr">Amiga SoundTracker sesi</comment>
+ <comment xml:lang="uk">звук Amiga SoundTracker</comment>
+ <comment xml:lang="vi">Âm thanh Amiga SoundTracker</comment>
+ <comment xml:lang="zh_CN">Amiga SoundTracker 音频</comment>
+ <comment xml:lang="zh_TW">Amiga SoundTracker 音訊</comment>
+ <magic priority="40">
+ <match value="MTM" type="string" offset="0"/>
+ <match value="MMD0" type="string" offset="0"/>
+ <match value="MMD1" type="string" offset="0"/>
+ <match value="if" type="string" offset="0">
+
+ <match value="0x0" type="byte" offset="110" mask="0xc0">
+
+ <match value="0x0" type="byte" offset="111" mask="0x80"/>
+ <match value="0x80" type="byte" offset="111"/>
+ </match>
+
+ <match value="0x40" type="byte" offset="110">
+
+ <match value="0x0" type="byte" offset="111" mask="0x80"/>
+ <match value="0x80" type="byte" offset="111"/>
+ </match>
+ </match>
+ <match value="JN" type="string" offset="0">
+
+ <match value="0x0" type="byte" offset="110" mask="0xc0">
+
+ <match value="0x0" type="byte" offset="111" mask="0x80"/>
+ <match value="0x80" type="byte" offset="111"/>
+ </match>
+
+ <match value="0x40" type="byte" offset="110">
+
+ <match value="0x0" type="byte" offset="111" mask="0x80"/>
+ <match value="0x80" type="byte" offset="111"/>
+ </match>
+ </match>
+ <match value="MAS_UTrack_V00" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mod"/>
+ <glob pattern="*.ult"/>
+ <glob pattern="*.uni"/>
+ <glob pattern="*.m15"/>
+ <glob pattern="*.mtm"/>
+ <glob pattern="*.669"/>
+ <glob pattern="*.med"/>
+ </mime-type>
+ <mime-type type="audio/mp2">
+ <comment>MP2 audio</comment>
+ <comment xml:lang="ar">MP2 سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo MP2</comment>
+ <comment xml:lang="bg">Аудио — MP2</comment>
+ <comment xml:lang="ca">àudio MP2</comment>
+ <comment xml:lang="cs">zvuk MP2</comment>
+ <comment xml:lang="da">MP2-lyd</comment>
+ <comment xml:lang="de">MP2-Audio</comment>
+ <comment xml:lang="el">Ήχος MP2</comment>
+ <comment xml:lang="en_GB">MP2 audio</comment>
+ <comment xml:lang="eo">MP2-sondosiero</comment>
+ <comment xml:lang="es">sonido MP2</comment>
+ <comment xml:lang="eu">MP2 audioa</comment>
+ <comment xml:lang="fi">MP2-ääni</comment>
+ <comment xml:lang="fo">MP2 ljóður</comment>
+ <comment xml:lang="fr">audio MP2</comment>
+ <comment xml:lang="ga">fuaim MP2</comment>
+ <comment xml:lang="gl">son MP2</comment>
+ <comment xml:lang="he">שמע MP2</comment>
+ <comment xml:lang="hr">MP2 zvučni zapis</comment>
+ <comment xml:lang="hu">MP2 hang</comment>
+ <comment xml:lang="ia">Audio MP2</comment>
+ <comment xml:lang="id">Audio MP2</comment>
+ <comment xml:lang="it">Audio MP2</comment>
+ <comment xml:lang="ja">MP2 オーディオ</comment>
+ <comment xml:lang="kk">MP2 аудиосы</comment>
+ <comment xml:lang="ko">MP2 오디오</comment>
+ <comment xml:lang="lt">MP2 garso įrašas</comment>
+ <comment xml:lang="lv">MP2 audio</comment>
+ <comment xml:lang="nb">MP2-lyd</comment>
+ <comment xml:lang="nl">MP2-audio</comment>
+ <comment xml:lang="nn">MP2-lyd</comment>
+ <comment xml:lang="oc">àudio MP2</comment>
+ <comment xml:lang="pl">Plik dźwiękowy MP2</comment>
+ <comment xml:lang="pt">áudio MP2</comment>
+ <comment xml:lang="pt_BR">Áudio MP2</comment>
+ <comment xml:lang="ro">Audio MP2</comment>
+ <comment xml:lang="ru">Аудио MP2</comment>
+ <comment xml:lang="sk">Zvuk MP2</comment>
+ <comment xml:lang="sl">Zvočna datoteka MP2</comment>
+ <comment xml:lang="sq">Audio MP2</comment>
+ <comment xml:lang="sr">МП2 звук</comment>
+ <comment xml:lang="sv">MP2-ljud</comment>
+ <comment xml:lang="tr">MP2 sesi</comment>
+ <comment xml:lang="uk">звук MP2</comment>
+ <comment xml:lang="vi">Âm thanh MP2</comment>
+ <comment xml:lang="zh_CN">MP2 音频</comment>
+ <comment xml:lang="zh_TW">MP2 音訊</comment>
+ <alias type="audio/x-mp2"/>
+ <glob pattern="*.mp2"/>
+ </mime-type>
+ <mime-type type="audio/mpeg">
+ <comment>MP3 audio</comment>
+ <comment xml:lang="ar">MP3 سمعي</comment>
+ <comment xml:lang="az">MP3 audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo MP3</comment>
+ <comment xml:lang="bg">Аудио — MP3</comment>
+ <comment xml:lang="ca">àudio MP3</comment>
+ <comment xml:lang="cs">zvuk MP3</comment>
+ <comment xml:lang="cy">Sain MP3</comment>
+ <comment xml:lang="da">MP3-lyd</comment>
+ <comment xml:lang="de">MP3-Audio</comment>
+ <comment xml:lang="el">Ήχος MP3</comment>
+ <comment xml:lang="en_GB">MP3 audio</comment>
+ <comment xml:lang="eo">MP3-sondosiero</comment>
+ <comment xml:lang="es">sonido MP3</comment>
+ <comment xml:lang="eu">MP3 audioa</comment>
+ <comment xml:lang="fi">MP3-ääni</comment>
+ <comment xml:lang="fo">MP3 ljóður</comment>
+ <comment xml:lang="fr">audio MP3</comment>
+ <comment xml:lang="ga">fuaim MP3</comment>
+ <comment xml:lang="gl">son MP3</comment>
+ <comment xml:lang="he">שמע MP3</comment>
+ <comment xml:lang="hr">MP3 zvučni zapis</comment>
+ <comment xml:lang="hu">MP3 hang</comment>
+ <comment xml:lang="ia">Audio MP3</comment>
+ <comment xml:lang="id">Audio MP3</comment>
+ <comment xml:lang="it">Audio MP3</comment>
+ <comment xml:lang="ja">MP3 オーディオ</comment>
+ <comment xml:lang="ka">MP3 აუდიო</comment>
+ <comment xml:lang="kk">MP3 аудиосы</comment>
+ <comment xml:lang="ko">MP3 오디오</comment>
+ <comment xml:lang="lt">MP3 garso įrašas</comment>
+ <comment xml:lang="lv">MP3 audio</comment>
+ <comment xml:lang="ms">Audio MP3</comment>
+ <comment xml:lang="nb">MP3-lyd</comment>
+ <comment xml:lang="nl">MP3-audio</comment>
+ <comment xml:lang="nn">MP3-lyd</comment>
+ <comment xml:lang="oc">àudio MP3</comment>
+ <comment xml:lang="pl">Plik dźwiękowy MP3</comment>
+ <comment xml:lang="pt">áudio MP3</comment>
+ <comment xml:lang="pt_BR">Áudio MP3</comment>
+ <comment xml:lang="ro">Audio MP3</comment>
+ <comment xml:lang="ru">Аудио MP3</comment>
+ <comment xml:lang="sk">Zvuk MP3</comment>
+ <comment xml:lang="sl">Zvočna datoteka MP3</comment>
+ <comment xml:lang="sq">Audio MP3</comment>
+ <comment xml:lang="sr">МП3 звук</comment>
+ <comment xml:lang="sv">MP3-ljud</comment>
+ <comment xml:lang="tr">MP3 sesi</comment>
+ <comment xml:lang="uk">звук MP3</comment>
+ <comment xml:lang="vi">Âm thanh MP3</comment>
+ <comment xml:lang="zh_CN">MP3 音频</comment>
+ <comment xml:lang="zh_TW">MP3 音訊</comment>
+ <alias type="audio/x-mp3"/>
+ <alias type="audio/x-mpg"/>
+ <alias type="audio/x-mpeg"/>
+ <alias type="audio/mp3"/>
+ <magic priority="50">
+ <match value="0xfffb" type="big16" offset="0"/>
+ <match value="ID3" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mp3"/>
+ <glob pattern="*.mpga"/>
+ </mime-type>
+ <mime-type type="audio/x-mpegurl">
+ <comment>MP3 audio (streamed)</comment>
+ <comment xml:lang="ar">MP3 سمعي (تدفق)</comment>
+ <comment xml:lang="be@latin">Aŭdyjo MP3 (płyń)</comment>
+ <comment xml:lang="bg">Аудио — MP3, поточно</comment>
+ <comment xml:lang="ca">àudio MP3 (flux)</comment>
+ <comment xml:lang="cs">zvuk MP3 (proud)</comment>
+ <comment xml:lang="da">MP3-lyd (strøm)</comment>
+ <comment xml:lang="de">MP3-Audio (Stream)</comment>
+ <comment xml:lang="el">Ήχος MP3 (εκπεμπόμενος)</comment>
+ <comment xml:lang="en_GB">MP3 audio (streamed)</comment>
+ <comment xml:lang="eo">MP3-sondosiero (fluigate)</comment>
+ <comment xml:lang="es">sonido MP3 (en transmisión)</comment>
+ <comment xml:lang="eu">MP3 audioa (korrontea)</comment>
+ <comment xml:lang="fi">MP3-ääni (virtaus)</comment>
+ <comment xml:lang="fo">MP3 ljóður (streymað)</comment>
+ <comment xml:lang="fr">audio MP3 (flux)</comment>
+ <comment xml:lang="ga">fuaim MP3 (sruthaithe)</comment>
+ <comment xml:lang="gl">son MP3 (en stream)</comment>
+ <comment xml:lang="he">שמע MP3 (מוזרם)</comment>
+ <comment xml:lang="hr">MP3 zvučni zapis (strujanje)</comment>
+ <comment xml:lang="hu">MP3 hang (sugárzott)</comment>
+ <comment xml:lang="ia">Audio MP3 (fluxo)</comment>
+ <comment xml:lang="id">Audio MP3 (stream)</comment>
+ <comment xml:lang="it">Audio MP3 (in streaming)</comment>
+ <comment xml:lang="ja">MP3 オーディオ (ストリーム)</comment>
+ <comment xml:lang="ka">MP3 აუდიო (ნაკადი)</comment>
+ <comment xml:lang="kk">MP3 аудиосы (ағымдық)</comment>
+ <comment xml:lang="ko">MP3 오디오(스트림)</comment>
+ <comment xml:lang="lt">MP3 garso įrašas (transliuojamas)</comment>
+ <comment xml:lang="lv">MP3 audio (straumēts)</comment>
+ <comment xml:lang="ms">Audio MP3 (aliran)</comment>
+ <comment xml:lang="nb">MP3-lyd (streaming)</comment>
+ <comment xml:lang="nl">MP3-audio (gestreamd)</comment>
+ <comment xml:lang="nn">Strauma MP3-lyd</comment>
+ <comment xml:lang="oc">àudio MP3 (flux)</comment>
+ <comment xml:lang="pl">Plik dźwiękowy MP3 (strumień)</comment>
+ <comment xml:lang="pt">áudio MP3 (em fluxo)</comment>
+ <comment xml:lang="pt_BR">Áudio MP3 (em fluxo)</comment>
+ <comment xml:lang="ro">Audio MP3 (flux)</comment>
+ <comment xml:lang="ru">Аудио MP3 (потоковое)</comment>
+ <comment xml:lang="sk">Zvuk MP3 (streamovaný)</comment>
+ <comment xml:lang="sl">Zvočna datoteka MP3 (pretočna)</comment>
+ <comment xml:lang="sq">Audio MP3 (streamed)</comment>
+ <comment xml:lang="sr">МП3 звук (проточан)</comment>
+ <comment xml:lang="sv">MP3-ljud (flöde)</comment>
+ <comment xml:lang="tr">MP3 sesi (akış)</comment>
+ <comment xml:lang="uk">звук MP3 (потоковий)</comment>
+ <comment xml:lang="vi">Âm thanh MP3 (chạy luồng)</comment>
+ <comment xml:lang="zh_CN">MP3 音频流媒体</comment>
+ <comment xml:lang="zh_TW">MP3 音訊 (串流)</comment>
+ <sub-class-of type="text/plain"/>
+ <alias type="audio/mpegurl"/>
+ <alias type="application/m3u"/>
+ <alias type="audio/x-mp3-playlist"/>
+ <alias type="audio/m3u"/>
+ <alias type="audio/x-m3u"/>
+ <glob pattern="*.m3u"/>
+ <glob pattern="*.m3u8"/>
+ <glob pattern="*.vlc"/>
+ <magic priority="50">
+ <match value="#EXTM3U" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/vnd.apple.mpegurl">
+ <comment>HTTP Live Streaming playlist</comment>
+ <comment xml:lang="ar">قائمة بث HTTP حية</comment>
+ <comment xml:lang="bg">Списък за изпълнение — поток по HTTP</comment>
+ <comment xml:lang="ca">llista de reproducció en temps real HTTP</comment>
+ <comment xml:lang="cs">seznam k přehrání HTTP Live Streaming</comment>
+ <comment xml:lang="da">Afspilningsliste til HTTP-livestrøm</comment>
+ <comment xml:lang="de">HTTP Live-Streaming-Wiedergabeliste</comment>
+ <comment xml:lang="el">Λίστα αναπαραγωγής ζωντανής μετάδοσης σε HTTP</comment>
+ <comment xml:lang="en_GB">HTTP Live Streaming playlist</comment>
+ <comment xml:lang="es">lista de reproducción de flujo en directo HTTP</comment>
+ <comment xml:lang="eu">HTTP zuzeneko korrontearen erreprodukzio-zerrenda</comment>
+ <comment xml:lang="fo">HTTP beinleiðis streymaður avspælingarlisti</comment>
+ <comment xml:lang="fr">liste de lecture de flux HTTP Live</comment>
+ <comment xml:lang="ga">seinmliosta sruthaithe bheo HTTP</comment>
+ <comment xml:lang="gl">lista de reprodución de fluxo HTTP</comment>
+ <comment xml:lang="he">רשימת השמעה הזרימה של HTTP</comment>
+ <comment xml:lang="hr">HTTP popis izvođenja emitiranja uživo</comment>
+ <comment xml:lang="hu">HTTP élő lejátszólista</comment>
+ <comment xml:lang="ia">Lista de selection HTTP Live Streaming</comment>
+ <comment xml:lang="id">Daftar putar HTTP Live Streaming</comment>
+ <comment xml:lang="it">Playlist Live Steaming HTTP</comment>
+ <comment xml:lang="ja">HTTP ライブストリーミング再生リスト</comment>
+ <comment xml:lang="kk">HTTP тірі ағым ойнау тізімі</comment>
+ <comment xml:lang="ko">HTTP 라이브 스트리밍 재생 목록</comment>
+ <comment xml:lang="lt">HTTP tiesioginio transliavimo grojaraštis</comment>
+ <comment xml:lang="lv">HTTP dzīvās straumēšanas repertuārs</comment>
+ <comment xml:lang="nl">HTTP Live Streaming afspeellijst</comment>
+ <comment xml:lang="oc">lista de lectura de flux HTTP Live</comment>
+ <comment xml:lang="pl">Lista odtwarzania strumieniowego na żywo HTTP</comment>
+ <comment xml:lang="pt">lista de reprodução HTTP Live Streaming</comment>
+ <comment xml:lang="pt_BR">Lista de Reprodução Streaming ao Vivo de HTTP</comment>
+ <comment xml:lang="ro">Listă de redare difuzată ca flux HTTP</comment>
+ <comment xml:lang="ru">Список воспроизведения HTTP-потока</comment>
+ <comment xml:lang="sk">Zoznam stôp HTTP Live Streaming</comment>
+ <comment xml:lang="sl">Seznam predvajanja živega pretoka HTTP</comment>
+ <comment xml:lang="sr">ХТТП списак нумера Живог Протока</comment>
+ <comment xml:lang="sv">HTTP Live Streaming-spellista</comment>
+ <comment xml:lang="tr">HTTP Canlı Akış çalma listesi</comment>
+ <comment xml:lang="uk">список відтворення HTTP Live Streaming</comment>
+ <comment xml:lang="zh_CN">HTTP 实时流播放列表</comment>
+ <comment xml:lang="zh_TW">HTTP 即時串流播放清單</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.m3u"/>
+ <glob pattern="*.m3u8"/>
+ <magic priority="70">
+ <match value="#EXTM3U" type="string" offset="0">
+ <match value="#EXT-X-TARGETDURATION" type="string" offset="0:128"/>
+ <match value="#EXT-X-STREAM-INF" type="string" offset="0:128"/>
+ </match>
+ </magic>
+ </mime-type>
+ <mime-type type="audio/x-ms-asx">
+ <comment>Microsoft ASX playlist</comment>
+ <comment xml:lang="ar">قائمة تشغيل مايكروسوفت ASX</comment>
+ <comment xml:lang="be@latin">Śpis Microsoft ASX</comment>
+ <comment xml:lang="bg">Списък за изпълнение — Microsoft ASX</comment>
+ <comment xml:lang="ca">llista de reproducció de Microsoft ASX</comment>
+ <comment xml:lang="cs">seznam k přehrání Microsoft ASX</comment>
+ <comment xml:lang="da">Microsoft ASX-afspilningsliste</comment>
+ <comment xml:lang="de">Microsoft-ASX-Wiedergabeliste</comment>
+ <comment xml:lang="el">Λίστα αναπαραγωγής Microsoft ASX</comment>
+ <comment xml:lang="en_GB">Microsoft ASX playlist</comment>
+ <comment xml:lang="es">lista de reproducción ASX de Microsoft</comment>
+ <comment xml:lang="eu">Microsoft ASX erreprodukzio-zerrenda</comment>
+ <comment xml:lang="fi">Microsoft ASX -soittolista</comment>
+ <comment xml:lang="fo">Microsoft ASX avspælingarlisti</comment>
+ <comment xml:lang="fr">liste de lecture Microsoft ASX</comment>
+ <comment xml:lang="ga">seinmliosta Microsoft ASX</comment>
+ <comment xml:lang="gl">lista de reprodución Microsoft ASX</comment>
+ <comment xml:lang="he">רשימת השמעה ASX (מיקרוסופט)</comment>
+ <comment xml:lang="hr">Microsoft ASX popis izvođenja</comment>
+ <comment xml:lang="hu">Microsoft ASX lejátszólista</comment>
+ <comment xml:lang="ia">Lista de selection Microsoft ASX</comment>
+ <comment xml:lang="id">Senarai putar Microsoft ASX</comment>
+ <comment xml:lang="it">Playlist Microsoft ASX</comment>
+ <comment xml:lang="ja">Microsoft ASX 再生リスト</comment>
+ <comment xml:lang="ka">Microsoft-ის ASX რეპერტუარი</comment>
+ <comment xml:lang="kk">Microsoft ASX ойнау тізімі</comment>
+ <comment xml:lang="ko">Microsoft ASX 재생 목록</comment>
+ <comment xml:lang="lt">Microsoft ASX grojaraštis</comment>
+ <comment xml:lang="lv">Microsoft ASX repertuārs</comment>
+ <comment xml:lang="nb">Microsoft ASX-spilleliste</comment>
+ <comment xml:lang="nl">Microsoft ASX-afspeellijst</comment>
+ <comment xml:lang="nn">Microsoft ASX-speleliste</comment>
+ <comment xml:lang="oc">lista de lectura Microsoft ASX</comment>
+ <comment xml:lang="pl">Lista odtwarzania Microsoft ASX</comment>
+ <comment xml:lang="pt">lista de reprodução Microsoft ASX</comment>
+ <comment xml:lang="pt_BR">Lista de reprodução do Microsoft ASX</comment>
+ <comment xml:lang="ro">Listă redare Microsoft ASX</comment>
+ <comment xml:lang="ru">Список воспроизведения Microsoft ASX</comment>
+ <comment xml:lang="sk">Zoznam skladieb Microsoft ASX</comment>
+ <comment xml:lang="sl">Seznam predvajanja Microsoft ASX</comment>
+ <comment xml:lang="sq">Listë titujsh Microsoft ASF</comment>
+ <comment xml:lang="sr">Мајкрософтов АСИкс списак нумера</comment>
+ <comment xml:lang="sv">Microsoft ASX-spellista</comment>
+ <comment xml:lang="tr">Microsoft ASX çalma listesi</comment>
+ <comment xml:lang="uk">список відтворення ASX Microsoft</comment>
+ <comment xml:lang="vi">Danh mục nhạc Microsoft ASX</comment>
+ <comment xml:lang="zh_CN">Microsoft ASX 播放列表</comment>
+ <comment xml:lang="zh_TW">微軟 ASX 播放清單</comment>
+ <alias type="video/x-ms-wvx"/>
+ <alias type="video/x-ms-wax"/>
+ <alias type="video/x-ms-wmx"/>
+ <alias type="application/x-ms-asx"/>
+ <glob pattern="*.asx"/>
+ <glob pattern="*.wax"/>
+ <glob pattern="*.wvx"/>
+ <glob pattern="*.wmx"/>
+ <magic priority="51">
+ <match value="ASF " type="string" offset="0"/>
+ <match value="&lt;ASX" type="string" offset="0:64"/>
+ <match value="&lt;asx" type="string" offset="0:64"/>
+ <match value="&lt;Asx" type="string" offset="0:64"/>
+ </magic>
+ </mime-type>
+ <mime-type type="audio/x-psf">
+ <comment>PSF audio</comment>
+ <comment xml:lang="ar">PSF سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo PSF</comment>
+ <comment xml:lang="bg">Аудио — PSF</comment>
+ <comment xml:lang="ca">àudio PSF</comment>
+ <comment xml:lang="cs">zvuk PSF</comment>
+ <comment xml:lang="da">PSF-lyd</comment>
+ <comment xml:lang="de">PSF-Audio</comment>
+ <comment xml:lang="el">Ήχος PSF</comment>
+ <comment xml:lang="en_GB">PSF audio</comment>
+ <comment xml:lang="eo">PSF-sondosiero</comment>
+ <comment xml:lang="es">sonido PSF</comment>
+ <comment xml:lang="eu">PSF audioa</comment>
+ <comment xml:lang="fi">PSF-ääni</comment>
+ <comment xml:lang="fo">PSF ljóður</comment>
+ <comment xml:lang="fr">audio PSF</comment>
+ <comment xml:lang="ga">fuaim PSF</comment>
+ <comment xml:lang="gl">son PSF</comment>
+ <comment xml:lang="he">שמע PSF</comment>
+ <comment xml:lang="hr">PSF zvučni zapis</comment>
+ <comment xml:lang="hu">PSF hang</comment>
+ <comment xml:lang="ia">Audio PSF</comment>
+ <comment xml:lang="id">Audio PSF</comment>
+ <comment xml:lang="it">Audio PSF</comment>
+ <comment xml:lang="ja">PSF オーディオ</comment>
+ <comment xml:lang="kk">PSF аудиосы</comment>
+ <comment xml:lang="ko">PSF 오디오</comment>
+ <comment xml:lang="lt">PSF garso įrašas</comment>
+ <comment xml:lang="lv">PSF audio</comment>
+ <comment xml:lang="nb">PSF-lyd</comment>
+ <comment xml:lang="nl">PSF-audio</comment>
+ <comment xml:lang="nn">PSF-lyd</comment>
+ <comment xml:lang="oc">àudio PSF</comment>
+ <comment xml:lang="pl">Plik dźwiękowy PSF</comment>
+ <comment xml:lang="pt">áudio PSF</comment>
+ <comment xml:lang="pt_BR">Áudio PSF</comment>
+ <comment xml:lang="ro">Audio PSF</comment>
+ <comment xml:lang="ru">Аудио PSF</comment>
+ <comment xml:lang="sk">Zvuk PSF</comment>
+ <comment xml:lang="sl">Zvočna datoteka PSF</comment>
+ <comment xml:lang="sq">Audio PSF</comment>
+ <comment xml:lang="sr">ПСФ звук</comment>
+ <comment xml:lang="sv">PSF-ljud</comment>
+ <comment xml:lang="tr">PSF sesi</comment>
+ <comment xml:lang="uk">звук PSF</comment>
+ <comment xml:lang="vi">Âm thanh PSF</comment>
+ <comment xml:lang="zh_CN">PSF 音频</comment>
+ <comment xml:lang="zh_TW">PSF 音訊</comment>
+ <acronym>PSF</acronym>
+ <expanded-acronym>Portable Sound Format</expanded-acronym>
+ <magic priority="50">
+ <match value="PSF" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.psf"/>
+ </mime-type>
+ <mime-type type="audio/x-minipsf">
+ <comment>MiniPSF audio</comment>
+ <comment xml:lang="ar">MiniPSF سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo MiniPSF</comment>
+ <comment xml:lang="bg">Аудио — MiniPSF</comment>
+ <comment xml:lang="ca">àudio MiniPSF</comment>
+ <comment xml:lang="cs">zvuk MiniPSF</comment>
+ <comment xml:lang="da">MiniPSF-lyd</comment>
+ <comment xml:lang="de">MiniPSF-Audio</comment>
+ <comment xml:lang="el">Ήχος MiniPSF</comment>
+ <comment xml:lang="en_GB">MiniPSF audio</comment>
+ <comment xml:lang="eo">MiniPSF-sondosiero</comment>
+ <comment xml:lang="es">sonido MiniPSF</comment>
+ <comment xml:lang="eu">MiniPSF audioa</comment>
+ <comment xml:lang="fi">MiniPSF-ääni</comment>
+ <comment xml:lang="fo">MiniPSF ljóður</comment>
+ <comment xml:lang="fr">audio MiniPSF</comment>
+ <comment xml:lang="ga">fuaim MiniPSF</comment>
+ <comment xml:lang="gl">son MiniPSF</comment>
+ <comment xml:lang="he">שמע של MiniPSP</comment>
+ <comment xml:lang="hr">MiniPSF zvučni zapis</comment>
+ <comment xml:lang="hu">MiniPSF hang</comment>
+ <comment xml:lang="ia">Audio MiniPSF</comment>
+ <comment xml:lang="id">Audio MiniPSF</comment>
+ <comment xml:lang="it">Audio MiniPSF</comment>
+ <comment xml:lang="ja">MiniPSF オーディオ</comment>
+ <comment xml:lang="ka">MiniPSF-ის აუდიო</comment>
+ <comment xml:lang="kk">MiniPSF аудиосы</comment>
+ <comment xml:lang="ko">MiniPSF 오디오</comment>
+ <comment xml:lang="lt">MiniPSF garso įrašas</comment>
+ <comment xml:lang="lv">MiniPSF audio</comment>
+ <comment xml:lang="nb">MiniPSF-lyd</comment>
+ <comment xml:lang="nl">MiniPSF-audio</comment>
+ <comment xml:lang="nn">MiniPSF-lyd</comment>
+ <comment xml:lang="oc">àudio MiniPSF</comment>
+ <comment xml:lang="pl">Plik dźwiękowy MiniPSF</comment>
+ <comment xml:lang="pt">áudio MiniPSF</comment>
+ <comment xml:lang="pt_BR">Áudio MiniPSF</comment>
+ <comment xml:lang="ro">Audio MiniPSF</comment>
+ <comment xml:lang="ru">Аудио MiniPSF</comment>
+ <comment xml:lang="sk">Zvuk MiniPSF</comment>
+ <comment xml:lang="sl">Zvočna datoteka MiniPSF</comment>
+ <comment xml:lang="sq">Audio MiniPSF</comment>
+ <comment xml:lang="sr">Мини ПСФ звук</comment>
+ <comment xml:lang="sv">MiniPSF-ljud</comment>
+ <comment xml:lang="tr">MiniPSF sesi</comment>
+ <comment xml:lang="uk">звук MiniPSF</comment>
+ <comment xml:lang="vi">Âm thanh MiniPSF</comment>
+ <comment xml:lang="zh_CN">MiniPSF 音频</comment>
+ <comment xml:lang="zh_TW">MiniPSF 音訊</comment>
+ <acronym>MiniPSF</acronym>
+ <expanded-acronym>Miniature Portable Sound Format</expanded-acronym>
+ <sub-class-of type="audio/x-psf"/>
+ <glob pattern="*.minipsf"/>
+ </mime-type>
+ <mime-type type="audio/x-psflib">
+ <comment>PSFlib audio library</comment>
+ <comment xml:lang="ar">مكتبة PSFlib السمعية</comment>
+ <comment xml:lang="be@latin">Aŭdyjobiblijateka PSFlib</comment>
+ <comment xml:lang="bg">Аудио библиотека — PSFlib</comment>
+ <comment xml:lang="ca">biblioteca d'àudio PSFlib</comment>
+ <comment xml:lang="cs">zvuková knihovna PSFlib</comment>
+ <comment xml:lang="da">PSFlib-lydbibliotek</comment>
+ <comment xml:lang="de">PSFlib-Audiobibliothek</comment>
+ <comment xml:lang="el">Βιβλιοθήκη ήχου PSFlib</comment>
+ <comment xml:lang="en_GB">PSFlib audio library</comment>
+ <comment xml:lang="es">biblioteca de sonido PSFlib</comment>
+ <comment xml:lang="eu">PSFlib audioaren liburutegia</comment>
+ <comment xml:lang="fi">PSFlib-äänikirjasto</comment>
+ <comment xml:lang="fo">PSFlib ljóðsavn</comment>
+ <comment xml:lang="fr">bibliothèque audio PSFlib</comment>
+ <comment xml:lang="ga">leabharlann fhuaime PSFlib</comment>
+ <comment xml:lang="gl">Biblioteca de son PSFlib</comment>
+ <comment xml:lang="he">ספריית שמע PSFlib</comment>
+ <comment xml:lang="hr">PSFlib zvučna biblioteka</comment>
+ <comment xml:lang="hu">PSFlib hanggyűjtemény</comment>
+ <comment xml:lang="ia">Bibliotheca audio PSFlib</comment>
+ <comment xml:lang="id">Pustaka audio PSFlib</comment>
+ <comment xml:lang="it">Libreria audio PSFlib</comment>
+ <comment xml:lang="ja">PSFlib オーディオライブラリ</comment>
+ <comment xml:lang="kk">PSFlib аудио жинағы</comment>
+ <comment xml:lang="ko">PSFlib 오디오 라이브러리</comment>
+ <comment xml:lang="lt">PSFlib garso biblioteka</comment>
+ <comment xml:lang="lv">PSFlib fonotēka</comment>
+ <comment xml:lang="nb">PSFlib-lydbibliotek</comment>
+ <comment xml:lang="nl">PSFlib-audiobibliotheek</comment>
+ <comment xml:lang="nn">PSFlib lydbibliotek</comment>
+ <comment xml:lang="oc">bibliotèca àudio PSFlib</comment>
+ <comment xml:lang="pl">Biblioteka dźwiękowa PSFlib</comment>
+ <comment xml:lang="pt">biblioteca áudio PSFlib</comment>
+ <comment xml:lang="pt_BR">Biblioteca de áudio PSFlib</comment>
+ <comment xml:lang="ro">Bibliotecă audio PSFlib</comment>
+ <comment xml:lang="ru">Фонотека PSFlib</comment>
+ <comment xml:lang="sk">Zvuková knižnica PSFlib</comment>
+ <comment xml:lang="sl">Zvočna knjižnica PSFlib</comment>
+ <comment xml:lang="sq">Librari audio PSFlib</comment>
+ <comment xml:lang="sr">библиотека звука ПСФ библиотеке</comment>
+ <comment xml:lang="sv">PSFlib-ljudbibliotek</comment>
+ <comment xml:lang="tr">PSFlib ses kitaplığı</comment>
+ <comment xml:lang="uk">аудіобібліотека PSFlib</comment>
+ <comment xml:lang="vi">Thư viện âm thanh PSFlib</comment>
+ <comment xml:lang="zh_CN">PSFlib 音频库</comment>
+ <comment xml:lang="zh_TW">PSFlib 音訊庫</comment>
+ <acronym>PSFlib</acronym>
+ <expanded-acronym>Portable Sound Format Library</expanded-acronym>
+ <sub-class-of type="audio/x-psf"/>
+ <glob pattern="*.psflib"/>
+ </mime-type>
+ <mime-type type="audio/x-ms-wma">
+ <comment>Windows Media audio</comment>
+ <comment xml:lang="ar">Windows Media سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Windows Media</comment>
+ <comment xml:lang="bg">Аудио — Windows Media</comment>
+ <comment xml:lang="ca">àudio de Windows Media</comment>
+ <comment xml:lang="cs">zvuk Windows Media</comment>
+ <comment xml:lang="da">Windows Media-lyd</comment>
+ <comment xml:lang="de">Windows-Media-Audio</comment>
+ <comment xml:lang="el">Ήχος Windows Media</comment>
+ <comment xml:lang="en_GB">Windows Media audio</comment>
+ <comment xml:lang="es">sonido de Windows Media</comment>
+ <comment xml:lang="eu">Windows Media audioa</comment>
+ <comment xml:lang="fi">Windows Media -ääni</comment>
+ <comment xml:lang="fo">Windows Media ljóður</comment>
+ <comment xml:lang="fr">audio Windows Media</comment>
+ <comment xml:lang="ga">fuaim Windows Media</comment>
+ <comment xml:lang="gl">son de Windows Media</comment>
+ <comment xml:lang="he">שמע של Windows Media</comment>
+ <comment xml:lang="hr">Windows Media zvučni zapis</comment>
+ <comment xml:lang="hu">Windows Media hang</comment>
+ <comment xml:lang="ia">Audio Windows Media</comment>
+ <comment xml:lang="id">Audio Windows Media</comment>
+ <comment xml:lang="it">Audio Windows Media</comment>
+ <comment xml:lang="ja">Windows Media オーディオ</comment>
+ <comment xml:lang="kk">Windows Media аудиосы</comment>
+ <comment xml:lang="ko">Windows 미디어 오디오</comment>
+ <comment xml:lang="lt">Windows Media garso įrašas</comment>
+ <comment xml:lang="lv">Windows Media audio</comment>
+ <comment xml:lang="nb">Windows Media lyd</comment>
+ <comment xml:lang="nl">Windows Media-audio</comment>
+ <comment xml:lang="nn">Windows Media-lyd</comment>
+ <comment xml:lang="oc">àudio Windows Media</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Windows Media</comment>
+ <comment xml:lang="pt">áudio Windows Media</comment>
+ <comment xml:lang="pt_BR">Áudio do Windows Media</comment>
+ <comment xml:lang="ro">Audio Windows Media</comment>
+ <comment xml:lang="ru">Аудио Windows Media</comment>
+ <comment xml:lang="sk">Zvuk Windows Media</comment>
+ <comment xml:lang="sl">Zvočna datoteka Windows Media</comment>
+ <comment xml:lang="sq">Audio Windows Media</comment>
+ <comment xml:lang="sr">Виндоуз Медија звук</comment>
+ <comment xml:lang="sv">Windows Media-ljud</comment>
+ <comment xml:lang="tr">Windows Media sesi</comment>
+ <comment xml:lang="uk">звук Windows Media</comment>
+ <comment xml:lang="vi">Âm thanh Windows Media</comment>
+ <comment xml:lang="zh_CN">Windows Media 音频</comment>
+ <comment xml:lang="zh_TW">Windows Media 音訊</comment>
+ <sub-class-of type="application/vnd.ms-asf"/>
+ <glob pattern="*.wma"/>
+ <alias type="audio/wma"/>
+ </mime-type>
+ <mime-type type="audio/x-musepack">
+ <comment>Musepack audio</comment>
+ <comment xml:lang="ar">Musepack سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Musepack</comment>
+ <comment xml:lang="bg">Аудио — Musepack</comment>
+ <comment xml:lang="ca">àudio de Musepack</comment>
+ <comment xml:lang="cs">zvuk Musepack</comment>
+ <comment xml:lang="da">Musepacklyd</comment>
+ <comment xml:lang="de">Musepack-Audio</comment>
+ <comment xml:lang="el">Ήχος Musepack</comment>
+ <comment xml:lang="en_GB">Musepack audio</comment>
+ <comment xml:lang="es">sonido Musepack</comment>
+ <comment xml:lang="eu">Musepack audioa</comment>
+ <comment xml:lang="fi">Musepack-ääni</comment>
+ <comment xml:lang="fo">Musepack ljóður</comment>
+ <comment xml:lang="fr">audio Musepack</comment>
+ <comment xml:lang="ga">fuaim Musepack</comment>
+ <comment xml:lang="gl">son de Musepack</comment>
+ <comment xml:lang="he">שמע של Musepack</comment>
+ <comment xml:lang="hr">Musepack zvučni zapis</comment>
+ <comment xml:lang="hu">Musepack hang</comment>
+ <comment xml:lang="ia">Audio Musepack</comment>
+ <comment xml:lang="id">Audio Musepack</comment>
+ <comment xml:lang="it">Audio Musepack</comment>
+ <comment xml:lang="ja">Musepack オーディオ</comment>
+ <comment xml:lang="kk">Musepack аудиосы</comment>
+ <comment xml:lang="ko">Musepack 오디오</comment>
+ <comment xml:lang="lt">Musepack garso įrašas</comment>
+ <comment xml:lang="lv">Musepack audio</comment>
+ <comment xml:lang="nb">Musepack-lyd</comment>
+ <comment xml:lang="nl">Musepack-audio</comment>
+ <comment xml:lang="nn">Musepack-lyd</comment>
+ <comment xml:lang="oc">àudio Musepack</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Musepack</comment>
+ <comment xml:lang="pt">áudio Musepack</comment>
+ <comment xml:lang="pt_BR">Áudio Musepack</comment>
+ <comment xml:lang="ro">Audio Musepack</comment>
+ <comment xml:lang="ru">Аудио Musepack</comment>
+ <comment xml:lang="sk">Zvuk Musepack</comment>
+ <comment xml:lang="sl">Zvočna datoteka Musepack</comment>
+ <comment xml:lang="sq">Audio Musepack</comment>
+ <comment xml:lang="sr">звук Мјузпака</comment>
+ <comment xml:lang="sv">Musepack-ljud</comment>
+ <comment xml:lang="tr">Musepack sesi</comment>
+ <comment xml:lang="uk">звук Musepack</comment>
+ <comment xml:lang="vi">Âm thanh Musepack</comment>
+ <comment xml:lang="zh_CN">Musepack 音频</comment>
+ <comment xml:lang="zh_TW">Musepack 音訊</comment>
+ <magic priority="50">
+ <match value="MP+" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mpc"/>
+ <glob pattern="*.mpp"/>
+ <glob pattern="*.mp+"/>
+ </mime-type>
+ <mime-type type="audio/vnd.rn-realaudio">
+ <comment>RealAudio document</comment>
+ <comment xml:lang="ar">مستند RealAudio</comment>
+ <comment xml:lang="ast">Documentu RealAudio</comment>
+ <comment xml:lang="be@latin">Dakument RealAudio</comment>
+ <comment xml:lang="bg">Документ — RealAudio</comment>
+ <comment xml:lang="ca">document RealAudio</comment>
+ <comment xml:lang="cs">dokument RealAudio</comment>
+ <comment xml:lang="da">RealAudio-dokument</comment>
+ <comment xml:lang="de">RealAudio-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο RealAudio</comment>
+ <comment xml:lang="en_GB">RealAudio document</comment>
+ <comment xml:lang="eo">RealAudio-dokumento</comment>
+ <comment xml:lang="es">documento RealAudio</comment>
+ <comment xml:lang="eu">RealAudio dokumentua</comment>
+ <comment xml:lang="fi">RealAudio-asiakirja</comment>
+ <comment xml:lang="fo">RealAudio skjal</comment>
+ <comment xml:lang="fr">document RealAudio</comment>
+ <comment xml:lang="ga">cáipéis RealAudio</comment>
+ <comment xml:lang="gl">documento Realson</comment>
+ <comment xml:lang="he">מסמך של RealAudio</comment>
+ <comment xml:lang="hr">RealAudio dokument</comment>
+ <comment xml:lang="hu">RealAudio dokumentum</comment>
+ <comment xml:lang="ia">Documento RealAudio</comment>
+ <comment xml:lang="id">Dokumen RealAudio</comment>
+ <comment xml:lang="it">Documento RealAudio</comment>
+ <comment xml:lang="ja">RealAudio ドキュメント</comment>
+ <comment xml:lang="kk">RealAudio құжаты</comment>
+ <comment xml:lang="ko">RealAudio 문서</comment>
+ <comment xml:lang="lt">RealAudio dokumentas</comment>
+ <comment xml:lang="lv">RealAudio dokuments</comment>
+ <comment xml:lang="nb">RealAudio-dokument</comment>
+ <comment xml:lang="nl">RealAudio-document</comment>
+ <comment xml:lang="nn">RealAudio-dokument</comment>
+ <comment xml:lang="oc">document RealAudio</comment>
+ <comment xml:lang="pl">Dokument RealAudio</comment>
+ <comment xml:lang="pt">documento RealAudio</comment>
+ <comment xml:lang="pt_BR">Documento RealAudio</comment>
+ <comment xml:lang="ro">Document RealAudio</comment>
+ <comment xml:lang="ru">Документ RealAudio</comment>
+ <comment xml:lang="sk">Dokument RealAudio</comment>
+ <comment xml:lang="sl">Dokument RealAudio</comment>
+ <comment xml:lang="sq">Dokument RealAudio</comment>
+ <comment xml:lang="sr">документ Рил Аудиа</comment>
+ <comment xml:lang="sv">RealAudio-dokument</comment>
+ <comment xml:lang="tr">RealAudio belgesi</comment>
+ <comment xml:lang="uk">документ RealAudio</comment>
+ <comment xml:lang="vi">Tài liệu âm thanh RealAudio</comment>
+ <comment xml:lang="zh_CN">RealAudio 文档</comment>
+ <comment xml:lang="zh_TW">RealAudio 文件</comment>
+ <glob pattern="*.ra"/>
+ <glob pattern="*.rax"/>
+ <alias type="audio/x-pn-realaudio"/>
+ <alias type="audio/vnd.m-realaudio"/>
+ </mime-type>
+ <mime-type type="application/ram">
+ <comment>RealMedia Metafile</comment>
+ <comment xml:lang="ar">ملف تعريف RealMedia</comment>
+ <comment xml:lang="be@latin">Metafajł RealMedia</comment>
+ <comment xml:lang="bg">Метафайл — RealMedia</comment>
+ <comment xml:lang="ca">metafitxer RealMedia</comment>
+ <comment xml:lang="cs">RealMedia Metafile</comment>
+ <comment xml:lang="da">RealMedia-metafil</comment>
+ <comment xml:lang="de">RealMedia-Metadatei</comment>
+ <comment xml:lang="el">Metafile RealMedia</comment>
+ <comment xml:lang="en_GB">RealMedia Metafile</comment>
+ <comment xml:lang="es">metaarchivo RealMedia</comment>
+ <comment xml:lang="eu">RealMedia metafitxategia</comment>
+ <comment xml:lang="fi">RealMedia-metatiedosto</comment>
+ <comment xml:lang="fo">RealMedia metafíla</comment>
+ <comment xml:lang="fr">métafichier RealMedia</comment>
+ <comment xml:lang="ga">Meiteachomhad RealMedia</comment>
+ <comment xml:lang="gl">Metaficheiro RealMedia</comment>
+ <comment xml:lang="he">קובץ מטא של RealMedia</comment>
+ <comment xml:lang="hr">RealMedia meta datoteka</comment>
+ <comment xml:lang="hu">RealMedia metafájl</comment>
+ <comment xml:lang="ia">Metafile RealMedia</comment>
+ <comment xml:lang="id">RealMedia Metafile</comment>
+ <comment xml:lang="it">Metafile RealMedia</comment>
+ <comment xml:lang="ja">RealMedia メタファイル</comment>
+ <comment xml:lang="kk">RealMedia метафайлы</comment>
+ <comment xml:lang="ko">RealMedia 메타 파일</comment>
+ <comment xml:lang="lt">RealMedia metafailas</comment>
+ <comment xml:lang="lv">RealMedia metadatne</comment>
+ <comment xml:lang="nb">RealMedia-metafil</comment>
+ <comment xml:lang="nl">RealMedia-metabestand</comment>
+ <comment xml:lang="nn">RealMedia-metafil</comment>
+ <comment xml:lang="oc">metafichièr RealMedia</comment>
+ <comment xml:lang="pl">Metaplik RealMedia</comment>
+ <comment xml:lang="pt">metaficheiro RealMedia</comment>
+ <comment xml:lang="pt_BR">Meta arquivo do RealMedia</comment>
+ <comment xml:lang="ro">Metafișier RealMedia</comment>
+ <comment xml:lang="ru">Мета-файл RealMedia</comment>
+ <comment xml:lang="sk">RealMedia Metafile</comment>
+ <comment xml:lang="sl">Metadatoteka RealMedia</comment>
+ <comment xml:lang="sq">Metafile RealMedia</comment>
+ <comment xml:lang="sr">метадатотека Рил Медија</comment>
+ <comment xml:lang="sv">RealMedia-metafil</comment>
+ <comment xml:lang="tr">RealMedia Meta Dosyası</comment>
+ <comment xml:lang="uk">метафайл RealMedia</comment>
+ <comment xml:lang="vi">Siêu tập tin RealMedia</comment>
+ <comment xml:lang="zh_CN">RealMedia 元文件</comment>
+ <comment xml:lang="zh_TW">RealMedia 中介檔</comment>
+ <glob pattern="*.ram"/>
+ </mime-type>
+ <mime-type type="video/vnd.rn-realvideo">
+ <comment>RealVideo document</comment>
+ <comment xml:lang="ar">مستند RealVideo</comment>
+ <comment xml:lang="ast">Documentu RealVideo</comment>
+ <comment xml:lang="be@latin">Dakument RealVideo</comment>
+ <comment xml:lang="bg">Документ — RealVideo</comment>
+ <comment xml:lang="ca">document RealVideo</comment>
+ <comment xml:lang="cs">dokument RealVideo</comment>
+ <comment xml:lang="da">RealAudio-dokument</comment>
+ <comment xml:lang="de">RealVideo-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο RealVideo</comment>
+ <comment xml:lang="en_GB">RealVideo document</comment>
+ <comment xml:lang="eo">RealVideo-dokumento</comment>
+ <comment xml:lang="es">documento RealVideo</comment>
+ <comment xml:lang="eu">RealVideo dokumentua</comment>
+ <comment xml:lang="fi">RealVideo-asiakirja</comment>
+ <comment xml:lang="fo">RealVideo skjal</comment>
+ <comment xml:lang="fr">document RealVideo</comment>
+ <comment xml:lang="ga">cáipéis RealVideo</comment>
+ <comment xml:lang="gl">documento RealVideo</comment>
+ <comment xml:lang="he">מסמך של RealVideo</comment>
+ <comment xml:lang="hr">RealVideo dokument</comment>
+ <comment xml:lang="hu">RealVideo dokumentum</comment>
+ <comment xml:lang="ia">Documento RealVideo</comment>
+ <comment xml:lang="id">Dokumen RealVideo</comment>
+ <comment xml:lang="it">Documento RealVideo</comment>
+ <comment xml:lang="ja">RealVideo ドキュメント</comment>
+ <comment xml:lang="kk">RealVideo құжаты</comment>
+ <comment xml:lang="ko">RealVideo 문서</comment>
+ <comment xml:lang="lt">RealVideo dokumentas</comment>
+ <comment xml:lang="lv">RealVideo dokuments</comment>
+ <comment xml:lang="nb">RealAudio-dokument</comment>
+ <comment xml:lang="nl">RealVideo-document</comment>
+ <comment xml:lang="nn">RealVideo-dokument</comment>
+ <comment xml:lang="oc">document RealVideo</comment>
+ <comment xml:lang="pl">Dokument RealVideo</comment>
+ <comment xml:lang="pt">documento RealVideo</comment>
+ <comment xml:lang="pt_BR">Documento RealVideo</comment>
+ <comment xml:lang="ro">Document RealVideo</comment>
+ <comment xml:lang="ru">Документ RealVideo</comment>
+ <comment xml:lang="sk">Dokument RealVideo</comment>
+ <comment xml:lang="sl">Video datoteka RealVideo</comment>
+ <comment xml:lang="sq">Dokument RealVideo</comment>
+ <comment xml:lang="sr">документ Рил Видеа</comment>
+ <comment xml:lang="sv">RealVideo-dokument</comment>
+ <comment xml:lang="tr">RealAudio belgesi</comment>
+ <comment xml:lang="uk">документ RealVideo</comment>
+ <comment xml:lang="vi">Tài liệu ảnh động RealVideo</comment>
+ <comment xml:lang="zh_CN">RealAudio 文档</comment>
+ <comment xml:lang="zh_TW">RealVideo 文件</comment>
+ <glob pattern="*.rv"/>
+ <glob pattern="*.rvx"/>
+ <alias type="video/x-real-video"/>
+ </mime-type>
+ <mime-type type="application/vnd.rn-realmedia">
+ <comment>RealMedia document</comment>
+ <comment xml:lang="ar">مستند RealMedia</comment>
+ <comment xml:lang="ast">Documentu RealMedia</comment>
+ <comment xml:lang="be@latin">Dakument RealMedia</comment>
+ <comment xml:lang="bg">Документ — RealMedia</comment>
+ <comment xml:lang="ca">document RealMedia</comment>
+ <comment xml:lang="cs">dokument RealMedia</comment>
+ <comment xml:lang="da">RealMedia-dokument</comment>
+ <comment xml:lang="de">RealMedia-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο RealMedia</comment>
+ <comment xml:lang="en_GB">RealMedia document</comment>
+ <comment xml:lang="eo">RealMedia-dokumento</comment>
+ <comment xml:lang="es">documento RealMedia</comment>
+ <comment xml:lang="eu">RealMedia dokumentua</comment>
+ <comment xml:lang="fi">RealMedia-asiakirja</comment>
+ <comment xml:lang="fo">RealMedia skjal</comment>
+ <comment xml:lang="fr">document RealMedia</comment>
+ <comment xml:lang="ga">cáipéis RealMedia</comment>
+ <comment xml:lang="gl">documento RealMedia</comment>
+ <comment xml:lang="he">מסמך של RealMedia</comment>
+ <comment xml:lang="hr">RealMedia dokument</comment>
+ <comment xml:lang="hu">RealMedia dokumentum</comment>
+ <comment xml:lang="ia">Documento RealMedia</comment>
+ <comment xml:lang="id">Dokumen RealMedia</comment>
+ <comment xml:lang="it">Documento RealMedia</comment>
+ <comment xml:lang="ja">RealMedia ドキュメント</comment>
+ <comment xml:lang="kk">RealMedia құжаты</comment>
+ <comment xml:lang="ko">RealMedia 문서</comment>
+ <comment xml:lang="lt">RealMedia dokumentas</comment>
+ <comment xml:lang="lv">RealMedia dokuments</comment>
+ <comment xml:lang="nb">RealMedia-dokument</comment>
+ <comment xml:lang="nl">RealMedia-document</comment>
+ <comment xml:lang="nn">RealMedia-dokument</comment>
+ <comment xml:lang="oc">document RealMedia</comment>
+ <comment xml:lang="pl">Dokument RealMedia</comment>
+ <comment xml:lang="pt">documento RealMedia</comment>
+ <comment xml:lang="pt_BR">Documento RealMedia</comment>
+ <comment xml:lang="ro">Document RealMedia</comment>
+ <comment xml:lang="ru">Документ RealMedia</comment>
+ <comment xml:lang="sk">Dokument RealMedia</comment>
+ <comment xml:lang="sl">Dokument RealMedia</comment>
+ <comment xml:lang="sq">Dokument RealMedia</comment>
+ <comment xml:lang="sr">документ Рил Медија</comment>
+ <comment xml:lang="sv">RealMedia-dokument</comment>
+ <comment xml:lang="tr">RealMedia belgesi</comment>
+ <comment xml:lang="uk">документ RealMedia</comment>
+ <comment xml:lang="vi">Tài liệu RealMedia</comment>
+ <comment xml:lang="zh_CN">RealMedia 文档</comment>
+ <comment xml:lang="zh_TW">RealMedia 文件</comment>
+ <generic-icon name="video-x-generic"/>
+ <magic priority="50">
+ <match value=".RMF" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.rm"/>
+ <glob pattern="*.rmj"/>
+ <glob pattern="*.rmm"/>
+ <glob pattern="*.rms"/>
+ <glob pattern="*.rmx"/>
+ <glob pattern="*.rmvb"/>
+ <alias type="application/vnd.rn-realmedia-vbr"/>
+ </mime-type>
+ <mime-type type="image/vnd.rn-realpix">
+ <comment>RealPix document</comment>
+ <comment xml:lang="ar">مستند RealPix</comment>
+ <comment xml:lang="ast">Documentu RealPix</comment>
+ <comment xml:lang="be@latin">Dakument RealPix</comment>
+ <comment xml:lang="bg">Документ — RealPix</comment>
+ <comment xml:lang="ca">document RealPix</comment>
+ <comment xml:lang="cs">dokument RealPix</comment>
+ <comment xml:lang="da">RealPix-dokument</comment>
+ <comment xml:lang="de">RealPix-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο RealPix</comment>
+ <comment xml:lang="en_GB">RealPix document</comment>
+ <comment xml:lang="eo">RealPix-dokumento</comment>
+ <comment xml:lang="es">documento RealPix</comment>
+ <comment xml:lang="eu">RealPix dokumentua</comment>
+ <comment xml:lang="fi">RealPix-asiakirja</comment>
+ <comment xml:lang="fo">RealPix skjal</comment>
+ <comment xml:lang="fr">document RealPix</comment>
+ <comment xml:lang="ga">cáipéis RealPix</comment>
+ <comment xml:lang="gl">documento RealPix</comment>
+ <comment xml:lang="he">מסמך של RealPix</comment>
+ <comment xml:lang="hr">RealPix dokument</comment>
+ <comment xml:lang="hu">RealPix dokumentum</comment>
+ <comment xml:lang="ia">Documento RealPix</comment>
+ <comment xml:lang="id">Dokumen RealPix</comment>
+ <comment xml:lang="it">Documento RealPix</comment>
+ <comment xml:lang="ja">RealPix ドキュメント</comment>
+ <comment xml:lang="kk">RealPix құжаты</comment>
+ <comment xml:lang="ko">RealPix 문서</comment>
+ <comment xml:lang="lt">RealPix dokumentas</comment>
+ <comment xml:lang="lv">RealPix dokuments</comment>
+ <comment xml:lang="nb">RealPix-dokument</comment>
+ <comment xml:lang="nl">RealPix-document</comment>
+ <comment xml:lang="nn">RealPix-dokument</comment>
+ <comment xml:lang="oc">document RealPix</comment>
+ <comment xml:lang="pl">Dokument RealPix</comment>
+ <comment xml:lang="pt">documento RealPix</comment>
+ <comment xml:lang="pt_BR">Documento RealPix</comment>
+ <comment xml:lang="ro">Document RealPix</comment>
+ <comment xml:lang="ru">Документ RealPix</comment>
+ <comment xml:lang="sk">Dokument RealPix</comment>
+ <comment xml:lang="sl">Dokument RealPix</comment>
+ <comment xml:lang="sq">Dokument RealPix</comment>
+ <comment xml:lang="sr">документ Рил Пикса</comment>
+ <comment xml:lang="sv">RealPix-dokument</comment>
+ <comment xml:lang="tr">RealPix belgesi</comment>
+ <comment xml:lang="uk">документ RealPix</comment>
+ <comment xml:lang="vi">Tài liệu ảnh RealPix</comment>
+ <comment xml:lang="zh_CN">RealPix 文档</comment>
+ <comment xml:lang="zh_TW">RealPix 文件</comment>
+ <glob pattern="*.rp"/>
+ </mime-type>
+ <mime-type type="text/vnd.rn-realtext">
+ <comment>RealText document</comment>
+ <comment xml:lang="ar">مستند RealText</comment>
+ <comment xml:lang="ast">Documentu RealText</comment>
+ <comment xml:lang="be@latin">Dakument RealText</comment>
+ <comment xml:lang="bg">Документ — RealText</comment>
+ <comment xml:lang="ca">document RealText</comment>
+ <comment xml:lang="cs">dokument RealText</comment>
+ <comment xml:lang="da">RealText-dokument</comment>
+ <comment xml:lang="de">RealText-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο RealText</comment>
+ <comment xml:lang="en_GB">RealText document</comment>
+ <comment xml:lang="eo">RealText-dokumento</comment>
+ <comment xml:lang="es">documento RealText</comment>
+ <comment xml:lang="eu">RealText dokumentua</comment>
+ <comment xml:lang="fi">RealText-asiakirja</comment>
+ <comment xml:lang="fo">RealText skjal</comment>
+ <comment xml:lang="fr">document RealText</comment>
+ <comment xml:lang="ga">cáipéis RealText</comment>
+ <comment xml:lang="gl">documento RealText</comment>
+ <comment xml:lang="he">מסמך של RealText</comment>
+ <comment xml:lang="hr">RealText dokument</comment>
+ <comment xml:lang="hu">RealText dokumentum</comment>
+ <comment xml:lang="ia">Documento RealText</comment>
+ <comment xml:lang="id">Dokumen RealText</comment>
+ <comment xml:lang="it">Documento RealText</comment>
+ <comment xml:lang="ja">RealText ドキュメント</comment>
+ <comment xml:lang="kk">RealText құжаты</comment>
+ <comment xml:lang="ko">RealText 문서</comment>
+ <comment xml:lang="lt">RealText dokumentas</comment>
+ <comment xml:lang="lv">RealText dokuments</comment>
+ <comment xml:lang="nb">RealText-dokument</comment>
+ <comment xml:lang="nl">RealText-document</comment>
+ <comment xml:lang="nn">RealText-dokument</comment>
+ <comment xml:lang="oc">document RealText</comment>
+ <comment xml:lang="pl">Dokument RealText</comment>
+ <comment xml:lang="pt">documento RealText</comment>
+ <comment xml:lang="pt_BR">Documento RealText</comment>
+ <comment xml:lang="ro">Document RealText</comment>
+ <comment xml:lang="ru">Документ RealText</comment>
+ <comment xml:lang="sk">Dokument RealText</comment>
+ <comment xml:lang="sl">Dokument RealText</comment>
+ <comment xml:lang="sq">Dokument RealText</comment>
+ <comment xml:lang="sr">документ Рил Текста</comment>
+ <comment xml:lang="sv">RealText-dokument</comment>
+ <comment xml:lang="tr">RealText belgesi</comment>
+ <comment xml:lang="uk">документ RealText</comment>
+ <comment xml:lang="vi">Tài liệu văn bản RealText</comment>
+ <comment xml:lang="zh_CN">RealText 文档</comment>
+ <comment xml:lang="zh_TW">RealText 文件</comment>
+ <glob pattern="*.rt"/>
+ </mime-type>
+ <mime-type type="audio/x-riff">
+ <comment>RIFF audio</comment>
+ <comment xml:lang="ar">RIFF سمعي</comment>
+ <comment xml:lang="az">RIFF audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo RIFF</comment>
+ <comment xml:lang="bg">Аудио — RIFF</comment>
+ <comment xml:lang="ca">àudio RIFF</comment>
+ <comment xml:lang="cs">zvuk RIFF</comment>
+ <comment xml:lang="cy">Sain RIFF</comment>
+ <comment xml:lang="da">RIFF-lyd</comment>
+ <comment xml:lang="de">RIFF-Audio</comment>
+ <comment xml:lang="el">Ήχος RIFF</comment>
+ <comment xml:lang="en_GB">RIFF audio</comment>
+ <comment xml:lang="eo">RIFF-sondosiero</comment>
+ <comment xml:lang="es">sonido RIFF</comment>
+ <comment xml:lang="eu">RIFF audioa</comment>
+ <comment xml:lang="fi">RIFF-ääni</comment>
+ <comment xml:lang="fo">RIFF ljóð</comment>
+ <comment xml:lang="fr">audio RIFF</comment>
+ <comment xml:lang="ga">fuaim RIFF</comment>
+ <comment xml:lang="gl">son RIFF</comment>
+ <comment xml:lang="he">שמע RIFF</comment>
+ <comment xml:lang="hr">RIFF zvučni zapis</comment>
+ <comment xml:lang="hu">RIFF-kép</comment>
+ <comment xml:lang="ia">Audio RIFF</comment>
+ <comment xml:lang="id">Audio RIFF</comment>
+ <comment xml:lang="it">Audio RIFF</comment>
+ <comment xml:lang="ja">RIFF オーディオ</comment>
+ <comment xml:lang="kk">RIFF аудиосы</comment>
+ <comment xml:lang="ko">RIFF 오디오</comment>
+ <comment xml:lang="lt">RIFF garso įrašas</comment>
+ <comment xml:lang="lv">RIFF audio</comment>
+ <comment xml:lang="ms">Audio RIFF</comment>
+ <comment xml:lang="nb">RIFF-lyd</comment>
+ <comment xml:lang="nl">RIFF-audio</comment>
+ <comment xml:lang="nn">RIFF-lyd</comment>
+ <comment xml:lang="oc">àudio RIFF</comment>
+ <comment xml:lang="pl">Plik dźwiękowy RIFF</comment>
+ <comment xml:lang="pt">áudio RIFF</comment>
+ <comment xml:lang="pt_BR">Áudio RIFF</comment>
+ <comment xml:lang="ro">Audio RIFF</comment>
+ <comment xml:lang="ru">Аудио RIFF</comment>
+ <comment xml:lang="sk">Zvuk RIFF</comment>
+ <comment xml:lang="sl">Zvočna datoteka RIFF</comment>
+ <comment xml:lang="sq">Audio RIFF</comment>
+ <comment xml:lang="sr">РИФФ звук</comment>
+ <comment xml:lang="sv">RIFF-ljud</comment>
+ <comment xml:lang="tr">RIFF sesi</comment>
+ <comment xml:lang="uk">звук RIFF</comment>
+ <comment xml:lang="vi">Âm thanh RIFF</comment>
+ <comment xml:lang="zh_CN">RIFF 音频</comment>
+ <comment xml:lang="zh_TW">RIFF 音訊</comment>
+ </mime-type>
+ <mime-type type="application/x-riff">
+ <comment>RIFF container</comment>
+ <comment xml:lang="ca">contenidor RIFF</comment>
+ <comment xml:lang="cs">kontejner RIFF</comment>
+ <comment xml:lang="da">RIFF-container</comment>
+ <comment xml:lang="de">RIFF-Container</comment>
+ <comment xml:lang="el">Περιέκτης RIFF</comment>
+ <comment xml:lang="en_GB">RIFF container</comment>
+ <comment xml:lang="es">contenedor RIFF</comment>
+ <comment xml:lang="eu">RIFF edukitzailea</comment>
+ <comment xml:lang="fr">conteneur RIFF</comment>
+ <comment xml:lang="ga">coimeádán RIFF</comment>
+ <comment xml:lang="gl">Contenedor RIFF</comment>
+ <comment xml:lang="he">מכולת RIFF</comment>
+ <comment xml:lang="hr">RIFF spremnik</comment>
+ <comment xml:lang="hu">RIFF konténer</comment>
+ <comment xml:lang="ia">Receptaculo RIFF</comment>
+ <comment xml:lang="id">Wadah RIFF</comment>
+ <comment xml:lang="it">Container RIFF</comment>
+ <comment xml:lang="kk">RIFF контейнері</comment>
+ <comment xml:lang="ko">RIFF 컨테이너</comment>
+ <comment xml:lang="oc">contenidor RIFF</comment>
+ <comment xml:lang="pl">Kontener RIFF</comment>
+ <comment xml:lang="pt">contentor RIFF</comment>
+ <comment xml:lang="pt_BR">Contêiner RIFF</comment>
+ <comment xml:lang="ru">Контейнер RIFF</comment>
+ <comment xml:lang="sk">Kontajner RIFF</comment>
+ <comment xml:lang="sl">Vsebnik RIFF</comment>
+ <comment xml:lang="sr">РИФФ садржалац</comment>
+ <comment xml:lang="sv">RIFF-behållare</comment>
+ <comment xml:lang="tr">RIFF deposu</comment>
+ <comment xml:lang="uk">контейнер RIFF</comment>
+ <comment xml:lang="zh_CN">RIFF 容器</comment>
+ <comment xml:lang="zh_TW">RIFF 容器</comment>
+
+ <magic priority="45">
+ <match value="RIFF" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="audio/x-s3m">
+ <comment>Scream Tracker 3 audio</comment>
+ <comment xml:lang="ar">Scream Tracker 3 سمعي</comment>
+ <comment xml:lang="az">Scream Tracker 3 audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Scream Tracker 3</comment>
+ <comment xml:lang="bg">Аудио — Scream Tracker 3</comment>
+ <comment xml:lang="ca">àudio de Scream Tracker 3</comment>
+ <comment xml:lang="cs">skladba Scream Tracker 3</comment>
+ <comment xml:lang="cy">Sain Scream Tracker 3</comment>
+ <comment xml:lang="da">Scream Tracker 3-lyd</comment>
+ <comment xml:lang="de">Scream-Tracker-3-Audio</comment>
+ <comment xml:lang="el">Ήχος Scream Tracker 3</comment>
+ <comment xml:lang="en_GB">Scream Tracker 3 audio</comment>
+ <comment xml:lang="eo">Sondosiero de Scream Tracker 3</comment>
+ <comment xml:lang="es">sonido Scream Tracker 3</comment>
+ <comment xml:lang="eu">Scream Tracker 3 audioa</comment>
+ <comment xml:lang="fi">Scream Tracker 3 -ääni</comment>
+ <comment xml:lang="fo">Scream Tracker 3 ljóður</comment>
+ <comment xml:lang="fr">audio Scream Tracker 3</comment>
+ <comment xml:lang="ga">fuaim Scream Tracker 3</comment>
+ <comment xml:lang="gl">son Scream Tracker 3</comment>
+ <comment xml:lang="he">שמע של Scream Tracker 3</comment>
+ <comment xml:lang="hr">Scream Tracker 3 zvučni zapis</comment>
+ <comment xml:lang="hu">Scream Tracker 3 hang</comment>
+ <comment xml:lang="ia">Audio Scream Tracker 3</comment>
+ <comment xml:lang="id">Audio Scream Tracker 3</comment>
+ <comment xml:lang="it">Audio Scream Tracker 3</comment>
+ <comment xml:lang="ja">Scream Tracker 3 オーディオ</comment>
+ <comment xml:lang="kk">Scream Tracker 3 аудиосы</comment>
+ <comment xml:lang="ko">Scream Tracker 3 오디오</comment>
+ <comment xml:lang="lt">Scream Tracker 3 garso įrašas</comment>
+ <comment xml:lang="lv">Scream Tracker 3 audio</comment>
+ <comment xml:lang="ms">Audio Scream Tracker 3</comment>
+ <comment xml:lang="nb">Scream Tracker 3-lyd</comment>
+ <comment xml:lang="nl">Scream Tracker 3-audio</comment>
+ <comment xml:lang="nn">Sream Tracker 3 lyd</comment>
+ <comment xml:lang="oc">àudio Scream Tracker 3</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Scream Tracker 3</comment>
+ <comment xml:lang="pt">áudio Scream Tracker 3</comment>
+ <comment xml:lang="pt_BR">Áudio Scream Tracker 3</comment>
+ <comment xml:lang="ro">Audio Scream Tracker 3</comment>
+ <comment xml:lang="ru">Аудио Scream Tracker 3</comment>
+ <comment xml:lang="sk">Skladba Scream Tracker 3</comment>
+ <comment xml:lang="sl">Zvočna datoteka Scream Tracker 3</comment>
+ <comment xml:lang="sq">Audio Scream Tracker 3</comment>
+ <comment xml:lang="sr">звук Скрим Тракера 3</comment>
+ <comment xml:lang="sv">Scream Tracker 3-ljud</comment>
+ <comment xml:lang="tr">Scream Tracker 3 sesi</comment>
+ <comment xml:lang="uk">звук Scream Tracker 3</comment>
+ <comment xml:lang="vi">Âm thanh Scream Tracker 3</comment>
+ <comment xml:lang="zh_CN">Scheme Tracker 3 音频</comment>
+ <comment xml:lang="zh_TW">Scream Tracker 3 音訊</comment>
+ <magic priority="50">
+ <match value="SCRM" type="string" offset="44"/>
+ </magic>
+ <glob pattern="*.s3m"/>
+ </mime-type>
+ <mime-type type="audio/x-scpls">
+ <comment>MP3 ShoutCast playlist</comment>
+ <comment xml:lang="ar">قائمة تشغيل MP3 ShoutCast</comment>
+ <comment xml:lang="be@latin">Śpis piesień dla tranślacyi MP3</comment>
+ <comment xml:lang="bg">Списък за изпълнение — MP3 ShoutCast</comment>
+ <comment xml:lang="ca">llista de reproducció MP3 ShoutCast</comment>
+ <comment xml:lang="cs">seznam k přehrání MP3 ShoutCast</comment>
+ <comment xml:lang="da">MP3 ShoutCast-afspilningsliste</comment>
+ <comment xml:lang="de">MP3-ShoutCast-Wiedergabeliste</comment>
+ <comment xml:lang="el">Λίστα αναπαραγωγής MP3 ShoutCast</comment>
+ <comment xml:lang="en_GB">MP3 ShoutCast playlist</comment>
+ <comment xml:lang="eo">MP3-ludlisto de ShoutCast</comment>
+ <comment xml:lang="es">lista de reproducción MP3 ShoutCast</comment>
+ <comment xml:lang="eu">MP3 ShoutCast erreprodukzio-zerrenda</comment>
+ <comment xml:lang="fi">MP3 ShoutCast -soittolista</comment>
+ <comment xml:lang="fo">MP3 ShoutCast avspælingarlisti</comment>
+ <comment xml:lang="fr">liste de lecture MP3 ShoutCast</comment>
+ <comment xml:lang="ga">seinmliosta MP3 ShoutCast</comment>
+ <comment xml:lang="gl">lista de reprodución MP3 de ShoutCast</comment>
+ <comment xml:lang="he">רשימת השמעה MP3 של ShoutCast</comment>
+ <comment xml:lang="hr">MP3 ShoutCast popis izvođenja</comment>
+ <comment xml:lang="hu">MP3 ShoutCast-lejátszólista</comment>
+ <comment xml:lang="ia">Lista de selection MP3 ShoutCast</comment>
+ <comment xml:lang="id">Senarai putar MP3 ShoutCast</comment>
+ <comment xml:lang="it">Playlist MP3 ShoutCast</comment>
+ <comment xml:lang="ja">MP3 ShoutCast 再生リスト</comment>
+ <comment xml:lang="kk">MP3 ShoutCast ойнау тізімі</comment>
+ <comment xml:lang="ko">MP3 ShoutCast 재생 목록</comment>
+ <comment xml:lang="lt">MP3 ShoutCast grojaraštis</comment>
+ <comment xml:lang="lv">MP3 ShoutCast repertuārs</comment>
+ <comment xml:lang="ms">Senaraimain ShoutCast MP3</comment>
+ <comment xml:lang="nb">MP3 ShoutCast-spilleliste</comment>
+ <comment xml:lang="nl">MP3 ShoutCast-afspeellijst</comment>
+ <comment xml:lang="nn">MP3 ShoutCast-speleliste</comment>
+ <comment xml:lang="oc">lista de lectura MP3 ShoutCast</comment>
+ <comment xml:lang="pl">Lista odtwarzania MP3 ShoutCast</comment>
+ <comment xml:lang="pt">lista de reprodução MP3 ShoutCast</comment>
+ <comment xml:lang="pt_BR">Lista de reprodução MP3 ShoutCast</comment>
+ <comment xml:lang="ro">Listă MP3 ShoutCast</comment>
+ <comment xml:lang="ru">Список воспроизведения MP3 ShoutCast</comment>
+ <comment xml:lang="sk">Zoznam skladieb MP3 ShoutCast</comment>
+ <comment xml:lang="sl">Seznam predvajanja MP3 ShoutCast</comment>
+ <comment xml:lang="sq">Listë titujsh MP3 ShoutCast</comment>
+ <comment xml:lang="sr">списак МП3 песама Шаут Каста</comment>
+ <comment xml:lang="sv">MP3 ShoutCast-spellista</comment>
+ <comment xml:lang="tr">MP3 ShoutCast çalma listesi</comment>
+ <comment xml:lang="uk">список програвання MP3 ShoutCast</comment>
+ <comment xml:lang="vi">Danh mục nhạc MP3 ShoutCast</comment>
+ <comment xml:lang="zh_CN">MP3 ShoutCast 播放列表</comment>
+ <comment xml:lang="zh_TW">MP3 ShoutCast 播放清單</comment>
+ <alias type="application/pls"/>
+ <alias type="audio/scpls"/>
+ <magic priority="50">
+ <match value="[playlist]" type="string" offset="0"/>
+ <match value="[Playlist]" type="string" offset="0"/>
+ <match value="[PLAYLIST]" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.pls"/>
+ </mime-type>
+ <mime-type type="audio/x-stm">
+ <comment>Scream Tracker audio</comment>
+ <comment xml:lang="ar">Scream Tracker سمعي</comment>
+ <comment xml:lang="az">Scream Tracker audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo Scream Tracker</comment>
+ <comment xml:lang="bg">Аудио — Scream Tracker</comment>
+ <comment xml:lang="ca">àudio de Scream Tracker</comment>
+ <comment xml:lang="cs">skladba Scream Tracker</comment>
+ <comment xml:lang="cy">Sain Scream Tracker</comment>
+ <comment xml:lang="da">Scream Tracker-lyd</comment>
+ <comment xml:lang="de">Scream-Tracker-Audio</comment>
+ <comment xml:lang="el">Ήχος Scream Tracker</comment>
+ <comment xml:lang="en_GB">Scream Tracker audio</comment>
+ <comment xml:lang="eo">Sondosiero de Scream Tracker</comment>
+ <comment xml:lang="es">sonido Scream Tracker</comment>
+ <comment xml:lang="eu">Scream Tracker audioa</comment>
+ <comment xml:lang="fi">Scream Tracker -ääni</comment>
+ <comment xml:lang="fo">Scream Tracker ljóður</comment>
+ <comment xml:lang="fr">audio Scream Tracker</comment>
+ <comment xml:lang="ga">fuaim Scream Tracker</comment>
+ <comment xml:lang="gl">son Scream Tracker</comment>
+ <comment xml:lang="he">שמע של Scream Tracker</comment>
+ <comment xml:lang="hr">Scream Tracker zvučni zapis</comment>
+ <comment xml:lang="hu">Scream Tracker hang</comment>
+ <comment xml:lang="ia">Audio Scream Tracker</comment>
+ <comment xml:lang="id">Audio Scream Tracker</comment>
+ <comment xml:lang="it">Audio Scream Tracker</comment>
+ <comment xml:lang="ja">Scream Tracker オーディオ</comment>
+ <comment xml:lang="kk">Scream Tracker аудиосы</comment>
+ <comment xml:lang="ko">Scream Tracker 오디오</comment>
+ <comment xml:lang="lt">Scream Tracker garso įrašas</comment>
+ <comment xml:lang="lv">Scream Tracker audio</comment>
+ <comment xml:lang="ms">Audio Scream Tracker</comment>
+ <comment xml:lang="nb">Scream Tracker-lyd</comment>
+ <comment xml:lang="nl">Scream Tracker-audio</comment>
+ <comment xml:lang="nn">Scream Tracker lyd</comment>
+ <comment xml:lang="oc">àudio Scream Tracker</comment>
+ <comment xml:lang="pl">Plik dźwiękowy Scream Tracker</comment>
+ <comment xml:lang="pt">áudio Scream Tracker</comment>
+ <comment xml:lang="pt_BR">Áudio Scream Tracker</comment>
+ <comment xml:lang="ro">Audio Scream Tracker</comment>
+ <comment xml:lang="ru">Аудио Scream Tracker</comment>
+ <comment xml:lang="sk">Skladba Scream Tracker</comment>
+ <comment xml:lang="sl">Zvočna datoteka Scream Tracker</comment>
+ <comment xml:lang="sq">Audio Scream Tracker</comment>
+ <comment xml:lang="sr">звук Скрим Тракера</comment>
+ <comment xml:lang="sv">Scream Tracker-ljud</comment>
+ <comment xml:lang="tr">Scream Tracker sesi</comment>
+ <comment xml:lang="uk">звук Scream Tracker</comment>
+ <comment xml:lang="vi">Âm thanh Scream Tracker</comment>
+ <comment xml:lang="zh_CN">Scream Tracker 音频</comment>
+ <comment xml:lang="zh_TW">Scream Tracker 音訊</comment>
+ <magic>
+ <match value="!Scream!\x1A" type="string" offset="20"/>
+ <match value="!SCREAM!\x1A" type="string" offset="20"/>
+ <match value="BMOD2STM\x1A" type="string" offset="20"/>
+ </magic>
+ <glob pattern="*.stm"/>
+ </mime-type>
+ <mime-type type="audio/x-voc">
+ <comment>VOC audio</comment>
+ <comment xml:lang="ar">VOC سمعي</comment>
+ <comment xml:lang="az">VOC audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo VOC</comment>
+ <comment xml:lang="bg">Аудио — VOC</comment>
+ <comment xml:lang="ca">àudio VOC</comment>
+ <comment xml:lang="cs">zvuk VOC</comment>
+ <comment xml:lang="cy">Sain VOC</comment>
+ <comment xml:lang="da">VOC-lyd</comment>
+ <comment xml:lang="de">VOC-Audio</comment>
+ <comment xml:lang="el">Ήχος VOC</comment>
+ <comment xml:lang="en_GB">VOC audio</comment>
+ <comment xml:lang="eo">VOC-sondosiero</comment>
+ <comment xml:lang="es">sonido VOC</comment>
+ <comment xml:lang="eu">VOC audioa</comment>
+ <comment xml:lang="fi">VOC-ääni</comment>
+ <comment xml:lang="fo">VOC ljóður</comment>
+ <comment xml:lang="fr">audio VOC</comment>
+ <comment xml:lang="ga">fuaim VOC</comment>
+ <comment xml:lang="gl">son VOC</comment>
+ <comment xml:lang="he">שמע VOC</comment>
+ <comment xml:lang="hr">VOC zvučni zapis</comment>
+ <comment xml:lang="hu">VOC hang</comment>
+ <comment xml:lang="ia">Audio VOC</comment>
+ <comment xml:lang="id">Audio VOC</comment>
+ <comment xml:lang="it">Audio VOC</comment>
+ <comment xml:lang="ja">VOC オーディオ</comment>
+ <comment xml:lang="kk">VOC аудиосы</comment>
+ <comment xml:lang="ko">VOC 오디오</comment>
+ <comment xml:lang="lt">VOC garso įrašas</comment>
+ <comment xml:lang="lv">VOC audio</comment>
+ <comment xml:lang="ms">Audio VOC</comment>
+ <comment xml:lang="nb">VOC-lyd</comment>
+ <comment xml:lang="nl">VOC-audio</comment>
+ <comment xml:lang="nn">VOC-lyd</comment>
+ <comment xml:lang="oc">àudio VOC</comment>
+ <comment xml:lang="pl">Plik dźwiękowy VOC</comment>
+ <comment xml:lang="pt">áudio VOC</comment>
+ <comment xml:lang="pt_BR">Áudio VOC</comment>
+ <comment xml:lang="ro">Audio VOC</comment>
+ <comment xml:lang="ru">Аудио VOC</comment>
+ <comment xml:lang="sk">Zvuk VOC</comment>
+ <comment xml:lang="sl">Zvočna datoteka VOC</comment>
+ <comment xml:lang="sq">Audio VOC</comment>
+ <comment xml:lang="sr">ВОЦ звук</comment>
+ <comment xml:lang="sv">VOC-ljud</comment>
+ <comment xml:lang="tr">VOC sesi</comment>
+ <comment xml:lang="uk">звук VOC</comment>
+ <comment xml:lang="vi">Âm thanh VOC</comment>
+ <comment xml:lang="zh_CN">VOC 音频</comment>
+ <comment xml:lang="zh_TW">VOC 音訊</comment>
+ <glob pattern="*.voc"/>
+ </mime-type>
+ <mime-type type="audio/x-wav">
+ <comment>WAV audio</comment>
+ <comment xml:lang="ar">WAV سمعي</comment>
+ <comment xml:lang="az">WAV audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo WAV</comment>
+ <comment xml:lang="bg">Аудио — WAV</comment>
+ <comment xml:lang="ca">àudio WAV</comment>
+ <comment xml:lang="cs">zvuk WAV</comment>
+ <comment xml:lang="cy">Sain WAV</comment>
+ <comment xml:lang="da">WAV-lyd</comment>
+ <comment xml:lang="de">WAV-Audio</comment>
+ <comment xml:lang="el">Ήχος WAV</comment>
+ <comment xml:lang="en_GB">WAV audio</comment>
+ <comment xml:lang="eo">WAV-sonkodo</comment>
+ <comment xml:lang="es">sonido WAV</comment>
+ <comment xml:lang="eu">WAV audioa</comment>
+ <comment xml:lang="fi">WAV-ääni</comment>
+ <comment xml:lang="fo">WAV ljóður</comment>
+ <comment xml:lang="fr">audio WAV</comment>
+ <comment xml:lang="ga">fuaim WAV</comment>
+ <comment xml:lang="gl">son WAV</comment>
+ <comment xml:lang="he">שמע WAV</comment>
+ <comment xml:lang="hr">WAV zvučni zapis</comment>
+ <comment xml:lang="hu">WAV hang</comment>
+ <comment xml:lang="ia">Audio WAV</comment>
+ <comment xml:lang="id">Audio WAV</comment>
+ <comment xml:lang="it">Audio WAV</comment>
+ <comment xml:lang="ja">WAV オーディオ</comment>
+ <comment xml:lang="kk">WAV аудиосы</comment>
+ <comment xml:lang="ko">WAV 오디오</comment>
+ <comment xml:lang="lt">WAV garso įrašas</comment>
+ <comment xml:lang="lv">WAV audio</comment>
+ <comment xml:lang="ms">Audio VOC</comment>
+ <comment xml:lang="nb">WAV-lyd</comment>
+ <comment xml:lang="nl">WAV-audio</comment>
+ <comment xml:lang="nn">WAV-lyd</comment>
+ <comment xml:lang="oc">àudio WAV</comment>
+ <comment xml:lang="pl">Plik dźwiękowy WAV</comment>
+ <comment xml:lang="pt">áudio WAV</comment>
+ <comment xml:lang="pt_BR">Áudio WAV</comment>
+ <comment xml:lang="ro">Audio WAV</comment>
+ <comment xml:lang="ru">Аудио WAV</comment>
+ <comment xml:lang="sk">Zvuk WAV</comment>
+ <comment xml:lang="sl">Zvočna datoteka WAV</comment>
+ <comment xml:lang="sq">Audio WAV</comment>
+ <comment xml:lang="sr">ВАВ звук</comment>
+ <comment xml:lang="sv">WAV-ljud</comment>
+ <comment xml:lang="tr">WAV sesi</comment>
+ <comment xml:lang="uk">звук WAV</comment>
+ <comment xml:lang="vi">Âm thanh WAV</comment>
+ <comment xml:lang="zh_CN">WAV 音频</comment>
+ <comment xml:lang="zh_TW">WAV 音訊</comment>
+ <alias type="audio/wav"/>
+ <alias type="audio/vnd.wave"/>
+ <magic priority="50">
+ <match value="WAVE" type="string" offset="8"/>
+ <match value="WAV " type="string" offset="8"/>
+ </magic>
+ <glob pattern="*.wav"/>
+ </mime-type>
+ <mime-type type="audio/x-xi">
+ <comment>Scream Tracker instrument</comment>
+ <comment xml:lang="ar">آلة Scream Tracker</comment>
+ <comment xml:lang="az">Scream Tracker instrumenti</comment>
+ <comment xml:lang="be@latin">Instrument Scream Tracker</comment>
+ <comment xml:lang="bg">Инструмент — Scream Tracker</comment>
+ <comment xml:lang="ca">instrument de Scream Tracker</comment>
+ <comment xml:lang="cs">nástroj pro Scream Tracker</comment>
+ <comment xml:lang="cy">Offeryn Scream Tracker</comment>
+ <comment xml:lang="da">Scream Tracker-instrument</comment>
+ <comment xml:lang="de">Scream-Tracker-Instrument</comment>
+ <comment xml:lang="el">Μουσικό όργανο Scream Tracker</comment>
+ <comment xml:lang="en_GB">Scream Tracker instrument</comment>
+ <comment xml:lang="eo">instrumento de Scream Tracker</comment>
+ <comment xml:lang="es">instrumento Scream Tracker</comment>
+ <comment xml:lang="eu">Scream Tracker instrumentua</comment>
+ <comment xml:lang="fi">Scream Tracker -soitin</comment>
+ <comment xml:lang="fo">Scream Tracker ljóðføri</comment>
+ <comment xml:lang="fr">instrument Scream Tracker</comment>
+ <comment xml:lang="ga">ionstraim Scream Tracker</comment>
+ <comment xml:lang="gl">Instrumento Scream Tracker</comment>
+ <comment xml:lang="he">כלי של Scream Tracker</comment>
+ <comment xml:lang="hr">Scream Tracker instrument</comment>
+ <comment xml:lang="hu">Scream Tracker hangszer</comment>
+ <comment xml:lang="ia">Instrumento Scream Tracker</comment>
+ <comment xml:lang="id">Instrumen Scream Tracker</comment>
+ <comment xml:lang="it">Strumento Scream Tracker</comment>
+ <comment xml:lang="ja">Scream Tracker インストゥルメント</comment>
+ <comment xml:lang="kk">Scream Tracker сайманы</comment>
+ <comment xml:lang="ko">Scream Tracker 악기</comment>
+ <comment xml:lang="lt">Scream Tracker instrumentas</comment>
+ <comment xml:lang="lv">Scream Tracker instrumenti</comment>
+ <comment xml:lang="ms">Instrumen Scream Tracker</comment>
+ <comment xml:lang="nb">Scream Tracker-instrument</comment>
+ <comment xml:lang="nl">Scream Tracker-instrument</comment>
+ <comment xml:lang="nn">Scream Tracker instrument</comment>
+ <comment xml:lang="oc">instrument Scream Tracker</comment>
+ <comment xml:lang="pl">Instrument Scream Tracker</comment>
+ <comment xml:lang="pt">instrumento Scream Tracker</comment>
+ <comment xml:lang="pt_BR">Instrumento Scream Tracker</comment>
+ <comment xml:lang="ro">Instrument Scream Tracker</comment>
+ <comment xml:lang="ru">Инструмент Scream Tracker</comment>
+ <comment xml:lang="sk">Nástroj pre Scream Tracker</comment>
+ <comment xml:lang="sl">Datoteka zvoka glasbila Scream Tracker</comment>
+ <comment xml:lang="sq">Instrument Scream Tracker</comment>
+ <comment xml:lang="sr">инструмент Скрим Тракера</comment>
+ <comment xml:lang="sv">Scream Tracker-instrument</comment>
+ <comment xml:lang="tr">Scream Tracker çalgısı</comment>
+ <comment xml:lang="uk">інструмент Scream Tracker</comment>
+ <comment xml:lang="vi">Nhạc khí Scream Tracker</comment>
+ <comment xml:lang="zh_CN">Scream Tracker 乐器</comment>
+ <comment xml:lang="zh_TW">Scream Tracker 樂器檔</comment>
+ <magic priority="50">
+ <match value="Extended Instrument:" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.xi"/>
+ </mime-type>
+ <mime-type type="audio/x-xm">
+ <comment>FastTracker II audio</comment>
+ <comment xml:lang="ar">FastTracker II سمعي</comment>
+ <comment xml:lang="az">FastTracker II audio faylı</comment>
+ <comment xml:lang="be@latin">Aŭdyjo FastTracker II</comment>
+ <comment xml:lang="bg">Аудио — FastTracker II</comment>
+ <comment xml:lang="ca">àudio de FastTracker II</comment>
+ <comment xml:lang="cs">zvuk FastTracker II</comment>
+ <comment xml:lang="cy">Sain FastTracker II</comment>
+ <comment xml:lang="da">FastTracker II-lyd</comment>
+ <comment xml:lang="de">FastTracker-II-Audio</comment>
+ <comment xml:lang="el">Ήχος FastTracker II</comment>
+ <comment xml:lang="en_GB">FastTracker II audio</comment>
+ <comment xml:lang="eo">Sondosiero de FastTracker II</comment>
+ <comment xml:lang="es">sonido FastTracker II</comment>
+ <comment xml:lang="eu">FastTracker II.ren audioa</comment>
+ <comment xml:lang="fi">FastTracker II -ääni</comment>
+ <comment xml:lang="fo">FastTracker II ljóður</comment>
+ <comment xml:lang="fr">audio FastTracker II</comment>
+ <comment xml:lang="ga">fuaim FastTracker II</comment>
+ <comment xml:lang="gl">son de FastTracker II</comment>
+ <comment xml:lang="he">שמע FastTracker II</comment>
+ <comment xml:lang="hr">FastTracker II zvučni zapis</comment>
+ <comment xml:lang="hu">FastTracker II hang</comment>
+ <comment xml:lang="ia">Audio FastTracker II</comment>
+ <comment xml:lang="id">Audio FastTracker II</comment>
+ <comment xml:lang="it">Audio FastTracker II</comment>
+ <comment xml:lang="ja">FastTracker II オーディオ</comment>
+ <comment xml:lang="ka">FastTracker II-ის აუდიო</comment>
+ <comment xml:lang="kk">FastTracker II аудиосы</comment>
+ <comment xml:lang="ko">FastTracker II 오디오</comment>
+ <comment xml:lang="lt">FastTracker II garso įrašas</comment>
+ <comment xml:lang="lv">FastTracker II audio</comment>
+ <comment xml:lang="ms">Audio FastTracker II</comment>
+ <comment xml:lang="nb">FastTracker II-lyd</comment>
+ <comment xml:lang="nl">FastTracker II-audio</comment>
+ <comment xml:lang="nn">FastTracker II lyd</comment>
+ <comment xml:lang="oc">àudio FastTracker II</comment>
+ <comment xml:lang="pl">Plik dźwiękowy FastTracker II</comment>
+ <comment xml:lang="pt">áudio FastTracker II</comment>
+ <comment xml:lang="pt_BR">Áudio FastTracker II</comment>
+ <comment xml:lang="ro">Audio FastTracker II</comment>
+ <comment xml:lang="ru">Аудио FastTracker II</comment>
+ <comment xml:lang="sk">Zvuk FastTracker II</comment>
+ <comment xml:lang="sl">Zvočna datoteka FastTracker II</comment>
+ <comment xml:lang="sq">Audio FastTracker II</comment>
+ <comment xml:lang="sr">звук Фаст Тракера ИИ</comment>
+ <comment xml:lang="sv">FastTracker II-ljud</comment>
+ <comment xml:lang="tr">FastTracker II sesi</comment>
+ <comment xml:lang="uk">звук FastTracker II</comment>
+ <comment xml:lang="vi">Âm thanh FastTracker II</comment>
+ <comment xml:lang="zh_CN">FastTracker II 音频</comment>
+ <comment xml:lang="zh_TW">FastTracker II 音訊</comment>
+ <magic priority="50">
+ <match value="Extended Module:" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.xm"/>
+ </mime-type>
+ <mime-type type="audio/x-tta">
+ <comment>TrueAudio audio</comment>
+ <comment xml:lang="ar">TrueAudio سمعي</comment>
+ <comment xml:lang="be@latin">Aŭdyjo TrueAudio</comment>
+ <comment xml:lang="bg">Аудио — TrueAudio</comment>
+ <comment xml:lang="ca">àudio de TrueAudio</comment>
+ <comment xml:lang="cs">zvuk TrueAudio</comment>
+ <comment xml:lang="da">TrueAudio-lyd</comment>
+ <comment xml:lang="de">TrueAudio-Audio</comment>
+ <comment xml:lang="el">Ήχος TrueAudio</comment>
+ <comment xml:lang="en_GB">TrueAudio audio</comment>
+ <comment xml:lang="eo">TrueAudio-sondosiero</comment>
+ <comment xml:lang="es">sonido TrueAudio</comment>
+ <comment xml:lang="eu">TrueAudio audioa</comment>
+ <comment xml:lang="fi">TrueAudio-ääni</comment>
+ <comment xml:lang="fo">TrueAudio ljóður</comment>
+ <comment xml:lang="fr">audio TrueAudio</comment>
+ <comment xml:lang="ga">fuaim TrueAudio</comment>
+ <comment xml:lang="gl">son Trueson</comment>
+ <comment xml:lang="he">שמע TrueAudio</comment>
+ <comment xml:lang="hr">TrueAudio zvučni zapis</comment>
+ <comment xml:lang="hu">TrueAudio hang</comment>
+ <comment xml:lang="ia">Audio TrueAudio</comment>
+ <comment xml:lang="id">Audio TrueAudio</comment>
+ <comment xml:lang="it">Audio TrueAudio</comment>
+ <comment xml:lang="ja">TrueAudio オーディオ</comment>
+ <comment xml:lang="kk">TrueAudio аудиосы</comment>
+ <comment xml:lang="ko">TrueAudio 오디오</comment>
+ <comment xml:lang="lt">TrueAudio garso įrašas</comment>
+ <comment xml:lang="lv">TrueAudio audio</comment>
+ <comment xml:lang="nb">TrueAudio-lyd</comment>
+ <comment xml:lang="nl">TrueAudio-audio</comment>
+ <comment xml:lang="nn">TrueAudio-lyd</comment>
+ <comment xml:lang="oc">àudio TrueAudio</comment>
+ <comment xml:lang="pl">Plik dźwiękowy TrueAudio</comment>
+ <comment xml:lang="pt">áudio TrueAudio</comment>
+ <comment xml:lang="pt_BR">Áudio TrueAudio</comment>
+ <comment xml:lang="ro">Audio TrueAudio</comment>
+ <comment xml:lang="ru">Аудио TrueAudio</comment>
+ <comment xml:lang="sk">Zvuk TrueAudio</comment>
+ <comment xml:lang="sl">Zvočna datoteka TrueAudio</comment>
+ <comment xml:lang="sq">Audio TrueAudio</comment>
+ <comment xml:lang="sr">Тру Аудио звук</comment>
+ <comment xml:lang="sv">TrueAudio-ljud</comment>
+ <comment xml:lang="tr">TrueAudio sesi</comment>
+ <comment xml:lang="uk">звук TrueAudio</comment>
+ <comment xml:lang="vi">Âm thanh TrueAudio</comment>
+ <comment xml:lang="zh_CN">TrueAudio 音频</comment>
+ <comment xml:lang="zh_TW">TrueAudio 音訊</comment>
+ <alias type="audio/tta"/>
+ <magic priority="50">
+ <match value="TTA1" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.tta"/>
+ </mime-type>
+ <mime-type type="image/bmp">
+ <comment>Windows BMP image</comment>
+ <comment xml:lang="ar">صورة Windows BMP</comment>
+ <comment xml:lang="az">Windows BMP rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava Windows BMP</comment>
+ <comment xml:lang="bg">Изображение — Windows BMP</comment>
+ <comment xml:lang="ca">imatge BMP de Windows</comment>
+ <comment xml:lang="cs">obrázek Windows BMP</comment>
+ <comment xml:lang="cy">Delwedd BMP Windows</comment>
+ <comment xml:lang="da">Windows BMP-billede</comment>
+ <comment xml:lang="de">Windows-BMP-Bild</comment>
+ <comment xml:lang="el">Εικόνα Windows BMP</comment>
+ <comment xml:lang="en_GB">Windows BMP image</comment>
+ <comment xml:lang="eo">BMP-bildo de Vindozo</comment>
+ <comment xml:lang="es">imagen BMP de Windows</comment>
+ <comment xml:lang="eu">Windows BMP irudia</comment>
+ <comment xml:lang="fi">Windows BMP -kuva</comment>
+ <comment xml:lang="fo">Windows BMP mynd</comment>
+ <comment xml:lang="fr">image Windows BMP</comment>
+ <comment xml:lang="ga">íomhá BMP Windows</comment>
+ <comment xml:lang="gl">imaxe BMP de Windows</comment>
+ <comment xml:lang="he">תמונת BMP של Windows</comment>
+ <comment xml:lang="hr">Windows BMP slika</comment>
+ <comment xml:lang="hu">Windows BMP-kép</comment>
+ <comment xml:lang="ia">Imagine BMP de Windows</comment>
+ <comment xml:lang="id">Citra Windows BMP</comment>
+ <comment xml:lang="it">Immagine Windows BMP</comment>
+ <comment xml:lang="ja">Windows BMP 画像</comment>
+ <comment xml:lang="kk">Windows BMP суреті</comment>
+ <comment xml:lang="ko">Windows BMP 그림</comment>
+ <comment xml:lang="lt">Windows BMP paveikslėlis</comment>
+ <comment xml:lang="lv">Windows BMP attēls</comment>
+ <comment xml:lang="ms">Imej BMP Windows</comment>
+ <comment xml:lang="nb">Windows BMP-bilde</comment>
+ <comment xml:lang="nl">Windows BMP-afbeelding</comment>
+ <comment xml:lang="nn">Windows BMP-bilete</comment>
+ <comment xml:lang="oc">imatge Windows BMP</comment>
+ <comment xml:lang="pl">Obraz BMP Windows</comment>
+ <comment xml:lang="pt">imagem BMP Windows</comment>
+ <comment xml:lang="pt_BR">Imagem BMP do Windows</comment>
+ <comment xml:lang="ro">Imagine Windows BMP</comment>
+ <comment xml:lang="ru">Изображение Windows BMP</comment>
+ <comment xml:lang="sk">Obrázok Windows BMP</comment>
+ <comment xml:lang="sl">Slikovna datoteka Windows BMP</comment>
+ <comment xml:lang="sq">Figurë Windows BMP</comment>
+ <comment xml:lang="sr">Виндоузова БМП слика</comment>
+ <comment xml:lang="sv">Windows BMP-bild</comment>
+ <comment xml:lang="tr">Windows BMP görüntüsü</comment>
+ <comment xml:lang="uk">зображення Windows BMP</comment>
+ <comment xml:lang="vi">Ảnh BMP Windows</comment>
+ <comment xml:lang="zh_CN">Windows BMP 图像</comment>
+ <comment xml:lang="zh_TW">Windows BMP 影像</comment>
+ <magic priority="50">
+ <match value="BMxxxx\000\000" type="string" offset="0" mask="0xffff00000000ffff"/>
+ <match value="BM" type="string" offset="0">
+ <match value="12" type="byte" offset="14"/>
+ <match value="64" type="byte" offset="14"/>
+ <match value="40" type="byte" offset="14"/>
+ </match>
+ </magic>
+ <glob pattern="*.bmp"/>
+ <glob pattern="*.dib"/>
+ <alias type="image/x-bmp"/>
+ <alias type="image/x-MS-bmp"/>
+ </mime-type>
+ <mime-type type="image/vnd.wap.wbmp">
+ <comment>WBMP image</comment>
+ <comment xml:lang="ar">صورة WBMP</comment>
+ <comment xml:lang="be@latin">Vyjava WBMP</comment>
+ <comment xml:lang="bg">Изображение — WBMP</comment>
+ <comment xml:lang="ca">imatge WBMP</comment>
+ <comment xml:lang="cs">obrázek WBMP</comment>
+ <comment xml:lang="da">WBMP-billede</comment>
+ <comment xml:lang="de">WBMP-Bild</comment>
+ <comment xml:lang="el">Εικόνα WBMP</comment>
+ <comment xml:lang="en_GB">WBMP image</comment>
+ <comment xml:lang="eo">WBMP-bildo</comment>
+ <comment xml:lang="es">imagen WBMP</comment>
+ <comment xml:lang="eu">WBMP irudia</comment>
+ <comment xml:lang="fi">WBMP-kuva</comment>
+ <comment xml:lang="fo">WBMP mynd</comment>
+ <comment xml:lang="fr">image WBMP</comment>
+ <comment xml:lang="ga">íomhá WBMP</comment>
+ <comment xml:lang="gl">imaxe WBMP</comment>
+ <comment xml:lang="he">תמונת WBMP</comment>
+ <comment xml:lang="hr">WBMP slika</comment>
+ <comment xml:lang="hu">WBMP kép</comment>
+ <comment xml:lang="ia">Imagine WBMP</comment>
+ <comment xml:lang="id">Citra WBMP</comment>
+ <comment xml:lang="it">Immagine WBMP</comment>
+ <comment xml:lang="ja">WBMP 画像</comment>
+ <comment xml:lang="kk">WBMP суреті</comment>
+ <comment xml:lang="ko">WBMP 그림</comment>
+ <comment xml:lang="lt">WBMP paveikslėlis</comment>
+ <comment xml:lang="lv">WBMP attēls</comment>
+ <comment xml:lang="nb">WBMP-bilde</comment>
+ <comment xml:lang="nl">WBMP-afbeelding</comment>
+ <comment xml:lang="nn">WBMP-bilete</comment>
+ <comment xml:lang="oc">imatge WBMP</comment>
+ <comment xml:lang="pl">Obraz WBMP</comment>
+ <comment xml:lang="pt">imagem WBMP</comment>
+ <comment xml:lang="pt_BR">Imagem WBMP</comment>
+ <comment xml:lang="ro">Imagine WBMP</comment>
+ <comment xml:lang="ru">Изображение WBMP</comment>
+ <comment xml:lang="sk">Obrázok WBMP</comment>
+ <comment xml:lang="sl">Slikovna datoteka WBMP</comment>
+ <comment xml:lang="sq">Figurë WBMP</comment>
+ <comment xml:lang="sr">ВБМП слика</comment>
+ <comment xml:lang="sv">WBMP-bild</comment>
+ <comment xml:lang="tr">WBMP görüntüsü</comment>
+ <comment xml:lang="uk">зображення WBMP</comment>
+ <comment xml:lang="vi">Ảnh WBMP</comment>
+ <comment xml:lang="zh_CN">WBMP 图像</comment>
+ <comment xml:lang="zh_TW">WBMP 影像</comment>
+ <acronym>WBMP</acronym>
+ <expanded-acronym>WAP bitmap</expanded-acronym>
+ <glob pattern="*.wbmp"/>
+ </mime-type>
+ <mime-type type="image/cgm">
+ <comment>Computer Graphics Metafile</comment>
+ <comment xml:lang="ar">ملف تعريف رسوميات الحاسوب</comment>
+ <comment xml:lang="az">Kompüter Qrafikası Meta Faylı</comment>
+ <comment xml:lang="be@latin">Metafajł Computer Graphics</comment>
+ <comment xml:lang="bg">Метафайл — Computer Graphics</comment>
+ <comment xml:lang="ca">metafitxer de Computer Graphics</comment>
+ <comment xml:lang="cs">Computer Graphics Metafile</comment>
+ <comment xml:lang="cy">Delwedd ffurf CGM</comment>
+ <comment xml:lang="da">Computer Graphics-metafil</comment>
+ <comment xml:lang="de">CGM-Datei</comment>
+ <comment xml:lang="el">Αρχείο Computer Graphics Metafile</comment>
+ <comment xml:lang="en_GB">Computer Graphics Metafile</comment>
+ <comment xml:lang="es">metaarchivo de Computer Graphics</comment>
+ <comment xml:lang="eu">Ordenagailuko grafikoen meta-fitxategia</comment>
+ <comment xml:lang="fi">Computer Graphics -metatiedosto</comment>
+ <comment xml:lang="fo">Teldugrafikk metafíla</comment>
+ <comment xml:lang="fr">métafichier Computer Graphics</comment>
+ <comment xml:lang="ga">Meiteachomhad Grafaicí Ríomhaire</comment>
+ <comment xml:lang="gl">metaficheiro de Computer Graphics</comment>
+ <comment xml:lang="he">קובץ-מטה מסוג Computer Graphics</comment>
+ <comment xml:lang="hr">Computer Graphics meta datoteka</comment>
+ <comment xml:lang="hu">Computer Graphics-metafájl</comment>
+ <comment xml:lang="ia">Metafile Computer Graphics</comment>
+ <comment xml:lang="id">Computer Graphics Metafile</comment>
+ <comment xml:lang="it">Computer Graphics Metafile</comment>
+ <comment xml:lang="ja">コンピューターグラフィックメタファイル</comment>
+ <comment xml:lang="kk">компьютерлік графика метафайлы</comment>
+ <comment xml:lang="ko">컴퓨터 그래픽스 메타 파일</comment>
+ <comment xml:lang="lt">Computer Graphics metafailas</comment>
+ <comment xml:lang="lv">Datorgrafikas metadatne</comment>
+ <comment xml:lang="ms">Failmeta Grafik Komputer</comment>
+ <comment xml:lang="nb">Computer Graphics Metafile</comment>
+ <comment xml:lang="nl">Computer Graphics-metabestand</comment>
+ <comment xml:lang="nn">Computer Graphics Metafile</comment>
+ <comment xml:lang="oc">metafichièr Computer Graphics</comment>
+ <comment xml:lang="pl">Metaplik grafiki komputerowej (CGM)</comment>
+ <comment xml:lang="pt">metaficheiro Computer Graphics</comment>
+ <comment xml:lang="pt_BR">Meta-arquivo do Computer Graphics</comment>
+ <comment xml:lang="ro">Metafișier Computer Graphics</comment>
+ <comment xml:lang="ru">Метафайл компьютерной графики</comment>
+ <comment xml:lang="sk">Computer Graphics Metafile</comment>
+ <comment xml:lang="sl">Metadatoteka računalniške grafike (CGM)</comment>
+ <comment xml:lang="sq">Metafile Computer Graphics</comment>
+ <comment xml:lang="sr">Метадатотека рачунарске графике</comment>
+ <comment xml:lang="sv">Computer Graphics Metafil</comment>
+ <comment xml:lang="tr">Computer Graphics Meta dosyası</comment>
+ <comment xml:lang="uk">метафайл комп'ютерної графіки</comment>
+ <comment xml:lang="vi">Siêu tập tin đồ họa máy tính (CMF)</comment>
+ <comment xml:lang="zh_CN">计算机图形图元文件 (CGM)</comment>
+ <comment xml:lang="zh_TW">CGM 影像</comment>
+ <glob pattern="*.cgm"/>
+ </mime-type>
+ <mime-type type="image/fax-g3">
+ <comment>CCITT G3 fax</comment>
+ <comment xml:lang="ar">فاكس CCITT G3</comment>
+ <comment xml:lang="be@latin">Faks CCITT G3</comment>
+ <comment xml:lang="bg">Факс — CCITT G3</comment>
+ <comment xml:lang="ca">fax CCITT G3</comment>
+ <comment xml:lang="cs">fax CCITT G3</comment>
+ <comment xml:lang="da">CCITT G3-fax</comment>
+ <comment xml:lang="de">CCITT-G3-Fax</comment>
+ <comment xml:lang="el">φαξ σε μορφή CCITT G3</comment>
+ <comment xml:lang="en_GB">CCITT G3 fax</comment>
+ <comment xml:lang="eo">G3-fakso de CCITT</comment>
+ <comment xml:lang="es">fax de CCITT G3</comment>
+ <comment xml:lang="eu">CCITT G3 faxa</comment>
+ <comment xml:lang="fi">CCITT G3 -faksi</comment>
+ <comment xml:lang="fo">CCITT G3 telefaks</comment>
+ <comment xml:lang="fr">télécopie G3 CCITT</comment>
+ <comment xml:lang="ga">facs CCITT G3</comment>
+ <comment xml:lang="gl">fax de CCITT G3</comment>
+ <comment xml:lang="he">פקס של CCITT G3</comment>
+ <comment xml:lang="hr">CCITT G3 faks</comment>
+ <comment xml:lang="hu">CCITT G3-fax</comment>
+ <comment xml:lang="ia">Fax CCITT G3</comment>
+ <comment xml:lang="id">Faks CCITT G3</comment>
+ <comment xml:lang="it">Fax CCITT G3</comment>
+ <comment xml:lang="ja">CCITT G3 FAX</comment>
+ <comment xml:lang="ka">CCITT G3 ფაქსი</comment>
+ <comment xml:lang="kk">CCITT G3 факсі</comment>
+ <comment xml:lang="ko">CCITT G3 팩스</comment>
+ <comment xml:lang="lt">CCITT G3 faksas</comment>
+ <comment xml:lang="lv">CCITT G3 fakss</comment>
+ <comment xml:lang="ms">Faks g3 CCITT</comment>
+ <comment xml:lang="nb">CCITT G3-faks</comment>
+ <comment xml:lang="nl">CCITT G3-fax</comment>
+ <comment xml:lang="nn">CCITT G3-fax</comment>
+ <comment xml:lang="oc">telecòpia G3 CCITT</comment>
+ <comment xml:lang="pl">Faks CCITT G3</comment>
+ <comment xml:lang="pt">fax CCITT G3</comment>
+ <comment xml:lang="pt_BR">Fax do CCITT G3</comment>
+ <comment xml:lang="ro">Fax CCITT G3</comment>
+ <comment xml:lang="ru">Факс CCITT G3</comment>
+ <comment xml:lang="sk">Fax CCITT G3</comment>
+ <comment xml:lang="sl">Datoteka faksimila CCITT G3</comment>
+ <comment xml:lang="sq">Fax CCITT G3</comment>
+ <comment xml:lang="sr">ЦЦИТТ Г3 факс</comment>
+ <comment xml:lang="sv">CCITT G3-fax</comment>
+ <comment xml:lang="tr">CCITT G3 faksı</comment>
+ <comment xml:lang="uk">факс CCITT G3</comment>
+ <comment xml:lang="vi">Điện thư G3 CCITT</comment>
+ <comment xml:lang="zh_CN">CCITT G3 传真</comment>
+ <comment xml:lang="zh_TW">CCITT G3 傳真檔</comment>
+ <glob pattern="*.g3"/>
+ </mime-type>
+ <mime-type type="image/g3fax">
+ <comment>G3 fax image</comment>
+ <comment xml:lang="ar">صورة فاكس G3</comment>
+ <comment xml:lang="az">G3 faks rəsmi</comment>
+ <comment xml:lang="be@latin">Faksavaja vyjava G3</comment>
+ <comment xml:lang="bg">Изображение — факс G3</comment>
+ <comment xml:lang="ca">imatge de fax G3</comment>
+ <comment xml:lang="cs">faxový obrázek G3</comment>
+ <comment xml:lang="cy">Delwedd Ffacs G3</comment>
+ <comment xml:lang="da">G3-faxbillede</comment>
+ <comment xml:lang="de">G3-Faxbild</comment>
+ <comment xml:lang="el">Εικόνα φαξ G3</comment>
+ <comment xml:lang="en_GB">G3 fax image</comment>
+ <comment xml:lang="eo">G3-faksbildo</comment>
+ <comment xml:lang="es">imagen de fax G3</comment>
+ <comment xml:lang="eu">G3 fax-irudia</comment>
+ <comment xml:lang="fi">G3-faksikuva</comment>
+ <comment xml:lang="fo">G3 fax mynd</comment>
+ <comment xml:lang="fr">image de télécopie G3</comment>
+ <comment xml:lang="ga">íomhá fhacs G3</comment>
+ <comment xml:lang="gl">imaxe de fax G3</comment>
+ <comment xml:lang="he">תמונת פקס של G3</comment>
+ <comment xml:lang="hr">G3 slika faksa</comment>
+ <comment xml:lang="hu">G3-faxkép</comment>
+ <comment xml:lang="ia">Imagine de fax G3</comment>
+ <comment xml:lang="id">Citra faks G3</comment>
+ <comment xml:lang="it">Immagine fax G3</comment>
+ <comment xml:lang="ja">G3 FAX 画像</comment>
+ <comment xml:lang="ka">G3 fax გამოსახულება</comment>
+ <comment xml:lang="kk">G3 факс суреті</comment>
+ <comment xml:lang="ko">G3 팩스 그림</comment>
+ <comment xml:lang="lt">G3 fax paveikslėlis</comment>
+ <comment xml:lang="lv">G3 faksa attēls</comment>
+ <comment xml:lang="ms">Imej fax G3</comment>
+ <comment xml:lang="nb">G3-faksbilde</comment>
+ <comment xml:lang="nl">G3 faxafbeelding</comment>
+ <comment xml:lang="nn">G3 faksbilete</comment>
+ <comment xml:lang="oc">imatge de telecòpia G3</comment>
+ <comment xml:lang="pl">Obraz faksowy G3</comment>
+ <comment xml:lang="pt">imagem de fax G3</comment>
+ <comment xml:lang="pt_BR">Imagem de fax G3</comment>
+ <comment xml:lang="ro">Imagine fax G3</comment>
+ <comment xml:lang="ru">Факсовое изображение G3</comment>
+ <comment xml:lang="sk">Obrázok fax G3</comment>
+ <comment xml:lang="sl">Slikovna datoteka G3 fax</comment>
+ <comment xml:lang="sq">Figurë Fax G3</comment>
+ <comment xml:lang="sr">слика Г3 факса</comment>
+ <comment xml:lang="sv">G3-faxbild</comment>
+ <comment xml:lang="tr">G3 fax görüntüsü</comment>
+ <comment xml:lang="uk">факс G3</comment>
+ <comment xml:lang="vi">Ảnh điện thư G3</comment>
+ <comment xml:lang="zh_CN">G3 传真图像</comment>
+ <comment xml:lang="zh_TW">G3 傳真圖</comment>
+ </mime-type>
+ <mime-type type="image/gif">
+ <comment>GIF image</comment>
+ <comment xml:lang="ar">صورة GIF</comment>
+ <comment xml:lang="az">GIF rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava GIF</comment>
+ <comment xml:lang="bg">Изображение — GIF</comment>
+ <comment xml:lang="ca">imatge GIF</comment>
+ <comment xml:lang="cs">obrázek GIF</comment>
+ <comment xml:lang="cy">Delwedd GIF</comment>
+ <comment xml:lang="da">GIF-billede</comment>
+ <comment xml:lang="de">GIF-Bild</comment>
+ <comment xml:lang="el">Εικόνα GIF</comment>
+ <comment xml:lang="en_GB">GIF image</comment>
+ <comment xml:lang="eo">GIF-bildo</comment>
+ <comment xml:lang="es">imagen GIF</comment>
+ <comment xml:lang="eu">GIF irudia</comment>
+ <comment xml:lang="fi">GIF-kuva</comment>
+ <comment xml:lang="fo">GIF mynd</comment>
+ <comment xml:lang="fr">image GIF</comment>
+ <comment xml:lang="ga">íomhá GIF</comment>
+ <comment xml:lang="gl">imaxe GIF</comment>
+ <comment xml:lang="he">תמונת GIF</comment>
+ <comment xml:lang="hr">GIF slika</comment>
+ <comment xml:lang="hu">GIF-kép</comment>
+ <comment xml:lang="ia">Imagine GIF</comment>
+ <comment xml:lang="id">Citra GIF</comment>
+ <comment xml:lang="it">Immagine GIF</comment>
+ <comment xml:lang="ja">GIF 画像</comment>
+ <comment xml:lang="ka">GIF გამოსახულება</comment>
+ <comment xml:lang="kk">GIF суреті</comment>
+ <comment xml:lang="ko">GIF 그림</comment>
+ <comment xml:lang="lt">GIF paveikslėlis</comment>
+ <comment xml:lang="lv">GIF attēls</comment>
+ <comment xml:lang="ms">Imej GIF</comment>
+ <comment xml:lang="nb">GIF-bilde</comment>
+ <comment xml:lang="nl">GIF-afbeelding</comment>
+ <comment xml:lang="nn">GIF-bilete</comment>
+ <comment xml:lang="oc">imatge GIF</comment>
+ <comment xml:lang="pl">Obraz GIF</comment>
+ <comment xml:lang="pt">imagem GIF</comment>
+ <comment xml:lang="pt_BR">Imagem GIF</comment>
+ <comment xml:lang="ro">Imagine GIF</comment>
+ <comment xml:lang="ru">Изображение GIF</comment>
+ <comment xml:lang="sk">Obrázok GIF</comment>
+ <comment xml:lang="sl">Slikovna datoteka GIF</comment>
+ <comment xml:lang="sq">Figurë GIF</comment>
+ <comment xml:lang="sr">ГИФ слика</comment>
+ <comment xml:lang="sv">GIF-bild</comment>
+ <comment xml:lang="tr">GIF görüntüsü</comment>
+ <comment xml:lang="uk">зображення GIF</comment>
+ <comment xml:lang="vi">Ảnh GIF</comment>
+ <comment xml:lang="zh_CN">GIF 图像</comment>
+ <comment xml:lang="zh_TW">GIF 影像</comment>
+ <magic priority="50">
+ <match value="GIF8" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.gif"/>
+ </mime-type>
+ <mime-type type="image/heif">
+ <comment>HEIF image</comment>
+ <acronym>HEIF</acronym>
+ <expanded-acronym>High Efficiency Image File</expanded-acronym>
+ <glob pattern="*.heic"/>
+ <glob pattern="*.heif"/>
+ <alias type="image/heic"/>
+ <alias type="image/heic-sequence"/>
+ <alias type="image/heif-sequence"/>
+ </mime-type>
+ <mime-type type="image/ief">
+ <comment>IEF image</comment>
+ <comment xml:lang="ar">صورة IEF</comment>
+ <comment xml:lang="az">IEF rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava IEF</comment>
+ <comment xml:lang="bg">Изображение — IEF</comment>
+ <comment xml:lang="ca">imatge IEF</comment>
+ <comment xml:lang="cs">obrázek IEF</comment>
+ <comment xml:lang="cy">Delwedd IEF</comment>
+ <comment xml:lang="da">IEF-billede</comment>
+ <comment xml:lang="de">IEF-Bild</comment>
+ <comment xml:lang="el">Εικόνα IEF</comment>
+ <comment xml:lang="en_GB">IEF image</comment>
+ <comment xml:lang="eo">IEF-bildo</comment>
+ <comment xml:lang="es">imagen IEF</comment>
+ <comment xml:lang="eu">IEF irudia</comment>
+ <comment xml:lang="fi">IEF-kuva</comment>
+ <comment xml:lang="fo">IEF mynd</comment>
+ <comment xml:lang="fr">image IEF</comment>
+ <comment xml:lang="ga">íomhá IEF</comment>
+ <comment xml:lang="gl">imaxe IEF</comment>
+ <comment xml:lang="he">תמונת IEF</comment>
+ <comment xml:lang="hr">IEF slika</comment>
+ <comment xml:lang="hu">IEF-kép</comment>
+ <comment xml:lang="ia">Imagine IEF</comment>
+ <comment xml:lang="id">Citra IEF</comment>
+ <comment xml:lang="it">Immagine IEF</comment>
+ <comment xml:lang="ja">IEF 画像</comment>
+ <comment xml:lang="kk">IEF суреті</comment>
+ <comment xml:lang="ko">IEF 그림</comment>
+ <comment xml:lang="lt">IEF paveikslėlis</comment>
+ <comment xml:lang="lv">IEF attēls</comment>
+ <comment xml:lang="ms">Imej IEF</comment>
+ <comment xml:lang="nb">IEF-bilde</comment>
+ <comment xml:lang="nl">IEF-afbeelding</comment>
+ <comment xml:lang="nn">IEF-bilete</comment>
+ <comment xml:lang="oc">imatge IEF</comment>
+ <comment xml:lang="pl">Obraz IEF</comment>
+ <comment xml:lang="pt">imagem IEF</comment>
+ <comment xml:lang="pt_BR">Imagem IEF</comment>
+ <comment xml:lang="ro">Imagine IEF</comment>
+ <comment xml:lang="ru">Изображение IEF</comment>
+ <comment xml:lang="sk">Obrázok IEF</comment>
+ <comment xml:lang="sl">Slikovna datoteka IEF</comment>
+ <comment xml:lang="sq">Figurë IEF</comment>
+ <comment xml:lang="sr">ИЕФ слика</comment>
+ <comment xml:lang="sv">IEF-bild</comment>
+ <comment xml:lang="tr">IEF görüntüsü</comment>
+ <comment xml:lang="uk">зображення IEF</comment>
+ <comment xml:lang="vi">Ảnh IEF</comment>
+ <comment xml:lang="zh_CN">IEF 图像</comment>
+ <comment xml:lang="zh_TW">IEF 影像</comment>
+ <glob pattern="*.ief"/>
+ </mime-type>
+ <mime-type type="image/jpeg">
+ <comment>JPEG image</comment>
+ <comment xml:lang="ar">صورة JPEG</comment>
+ <comment xml:lang="az">JPEG rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava JPEG</comment>
+ <comment xml:lang="bg">Изображение — JPEG</comment>
+ <comment xml:lang="ca">imatge JPEG</comment>
+ <comment xml:lang="cs">obrázek JPEG</comment>
+ <comment xml:lang="cy">Delwedd JPEG</comment>
+ <comment xml:lang="da">JPEG-billede</comment>
+ <comment xml:lang="de">JPEG-Bild</comment>
+ <comment xml:lang="el">Εικόνα JPEG</comment>
+ <comment xml:lang="en_GB">JPEG image</comment>
+ <comment xml:lang="eo">JPEG-bildo</comment>
+ <comment xml:lang="es">imagen JPEG</comment>
+ <comment xml:lang="eu">JPEG irudia</comment>
+ <comment xml:lang="fi">JPEG-kuva</comment>
+ <comment xml:lang="fo">JPEG mynd</comment>
+ <comment xml:lang="fr">image JPEG</comment>
+ <comment xml:lang="ga">íomhá JPEG</comment>
+ <comment xml:lang="gl">imaxe JPEG</comment>
+ <comment xml:lang="he">תמונת JPEG</comment>
+ <comment xml:lang="hr">JPEG slika</comment>
+ <comment xml:lang="hu">JPEG-kép</comment>
+ <comment xml:lang="ia">Imagine JPEG</comment>
+ <comment xml:lang="id">Citra JPEG</comment>
+ <comment xml:lang="it">Immagine JPEG</comment>
+ <comment xml:lang="ja">JPEG 画像 </comment>
+ <comment xml:lang="kk">JPEG суреті</comment>
+ <comment xml:lang="ko">JPEG 그림</comment>
+ <comment xml:lang="lt">JPEG paveikslėlis</comment>
+ <comment xml:lang="lv">JPEG attēls</comment>
+ <comment xml:lang="ms">Imej JPEG</comment>
+ <comment xml:lang="nb">JPEG-bilde</comment>
+ <comment xml:lang="nl">JPEG-afbeelding</comment>
+ <comment xml:lang="nn">JPEG-bilete</comment>
+ <comment xml:lang="oc">imatge JPEG</comment>
+ <comment xml:lang="pl">Obraz JPEG</comment>
+ <comment xml:lang="pt">imagem JPEG</comment>
+ <comment xml:lang="pt_BR">Imagem JPEG</comment>
+ <comment xml:lang="ro">Imagine JPEG</comment>
+ <comment xml:lang="ru">Изображение JPEG</comment>
+ <comment xml:lang="sk">Obrázok JPEG</comment>
+ <comment xml:lang="sl">Slikovna datoteka JPEG</comment>
+ <comment xml:lang="sq">Figurë JPEG</comment>
+ <comment xml:lang="sr">ЈПЕГ слика</comment>
+ <comment xml:lang="sv">JPEG-bild</comment>
+ <comment xml:lang="tr">JPEG görüntüsü</comment>
+ <comment xml:lang="uk">зображення JPEG</comment>
+ <comment xml:lang="vi">Ảnh JPEG</comment>
+ <comment xml:lang="zh_CN">JPEG 图像</comment>
+ <comment xml:lang="zh_TW">JPEG 影像</comment>
+ <magic priority="50">
+ <match value="\377\330\377" type="string" offset="0"/>
+ <match value="0xffd8" type="big16" offset="0"/>
+ </magic>
+ <glob pattern="*.jpeg"/>
+ <glob pattern="*.jpg"/>
+ <glob pattern="*.jpe"/>
+ <alias type="image/pjpeg"/>
+ </mime-type>
+ <mime-type type="video/x-mjpeg">
+ <comment>MJPEG video stream</comment>
+ <acronym>MJPEG</acronym>
+ <expanded-acronym>Motion JPEG</expanded-acronym>
+ <sub-class-of type="image/jpeg"/>
+ <glob pattern="*.mjpeg"/>
+ <glob pattern="*.mjpg"/>
+ </mime-type>
+ <mime-type type="image/x-jp2-codestream">
+ <comment>JPEG-2000 codestream</comment>
+ <comment xml:lang="ca">flux de codis JPEG-2000</comment>
+ <comment xml:lang="cs">datový tok JPEG-2000</comment>
+ <comment xml:lang="de">JPEG-2000 Codestream</comment>
+ <comment xml:lang="en_GB">JPEG-2000 codestream</comment>
+ <comment xml:lang="es">secuencia de código JPEG-2000</comment>
+ <comment xml:lang="hr">JPEG-2000 kôd strujanja</comment>
+ <comment xml:lang="hu">JPEG-2000 kódfolyam</comment>
+ <comment xml:lang="id">codestream JPEG-2000</comment>
+ <comment xml:lang="it">Codestream JPEG-2000</comment>
+ <comment xml:lang="kk">JPEG-2000 код ағыны</comment>
+ <comment xml:lang="ko">JPEG-2000 코드스트림</comment>
+ <comment xml:lang="pl">Strumień kodu JPEG-2000</comment>
+ <comment xml:lang="pt_BR">Imagem JPEG-2000</comment>
+ <comment xml:lang="ru">Кодовый поток JPEG-2000</comment>
+ <comment xml:lang="sk">JPEG-2000 codestream</comment>
+ <comment xml:lang="sv">JPEG-2000-kodström</comment>
+ <comment xml:lang="uk">потік коду JPEG-2000</comment>
+ <comment xml:lang="zh_CN">JPEG-2000 码流</comment>
+ <comment xml:lang="zh_TW">JPEG-2000 代碼串流</comment>
+ <magic priority="50">
+ <match value="0xff4fff51" type="big32" offset="0"/>
+ </magic>
+ <glob pattern="*.j2c"/>
+ <glob pattern="*.j2k"/>
+ <glob pattern="*.jpc"/>
+ </mime-type>
+ <mime-type type="image/jp2">
+ <comment>JPEG-2000 JP2 image</comment>
+ <comment xml:lang="ca">imatge JPEG-2000 JP2</comment>
+ <comment xml:lang="cs">obrázek JPEG-2000 JP2</comment>
+ <comment xml:lang="de">JPEG-2000 JP2-Bild</comment>
+ <comment xml:lang="en_GB">JPEG-2000 JP2 image</comment>
+ <comment xml:lang="es">imagen JPEG-2000 JP2</comment>
+ <comment xml:lang="fi">JPEG-2000 JP2 -kuva</comment>
+ <comment xml:lang="hr">JPEG-2000 JP2 slika</comment>
+ <comment xml:lang="hu">JPEG-2000 JP2 kép</comment>
+ <comment xml:lang="id">Citra JPEG-2000 JP2</comment>
+ <comment xml:lang="it">Immagine JPEG-2000 JP2</comment>
+ <comment xml:lang="kk">JPEG-2000 JP2 суреті</comment>
+ <comment xml:lang="ko">JPEG-2000 JP2 그림</comment>
+ <comment xml:lang="pl">Obraz JP2 JPEG-2000</comment>
+ <comment xml:lang="pt_BR">Imagem JP2 de JPEG-2000</comment>
+ <comment xml:lang="ru">Изоражение JPEG-2000 JP2</comment>
+ <comment xml:lang="sk">Obrázok JPEG-2000 JP2</comment>
+ <comment xml:lang="sv">JPEG-2000 JP2-bild</comment>
+ <comment xml:lang="uk">зображення JP2 JPEG-2000</comment>
+ <comment xml:lang="zh_CN">JPEG-2000 JP2 图像</comment>
+ <comment xml:lang="zh_TW">JPEG-2000 JP2 影像</comment>
+ <acronym>JP2</acronym>
+ <expanded-acronym>JPEG-2000</expanded-acronym>
+ <alias type="image/jpeg2000"/>
+ <alias type="image/jpeg2000-image"/>
+ <alias type="image/x-jpeg2000-image"/>
+ <magic priority="50">
+ <match value="\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a jp2\x20" type="string" offset="0" mask="0xffffffffffffffffffffffff0000000000000000ffffffff"/>
+ </magic>
+ <glob pattern="*.jp2"/>
+ <glob pattern="*.jpg2"/>
+ </mime-type>
+ <mime-type type="image/jpx">
+ <comment>JPEG-2000 JPX image</comment>
+ <comment xml:lang="ca">imatge JPEG-2000 JPX</comment>
+ <comment xml:lang="cs">obrázek JPEG-2000 JPX</comment>
+ <comment xml:lang="de">JPEG-2000 JPX-Bild</comment>
+ <comment xml:lang="en_GB">JPEG-2000 JPX image</comment>
+ <comment xml:lang="es">imagen JPEG-2000 JPX</comment>
+ <comment xml:lang="fi">JPEG-2000 JPX -kuva</comment>
+ <comment xml:lang="hr">JPEG-2000 JPX slika</comment>
+ <comment xml:lang="hu">JPEG-2000 JPX kép</comment>
+ <comment xml:lang="id">Citra JPEG-2000 JPX</comment>
+ <comment xml:lang="it">Immagine JPEG-2000 JPX</comment>
+ <comment xml:lang="kk">JPEG-2000 JPX суреті</comment>
+ <comment xml:lang="ko">JPEG-2000 JPX 그림</comment>
+ <comment xml:lang="pl">Obraz JPX JPEG-2000</comment>
+ <comment xml:lang="pt_BR">Imagem JPX de JPEG-2000</comment>
+ <comment xml:lang="ru">Изображение JPEG-2000 JPX</comment>
+ <comment xml:lang="sk">Obrázok JPEG-2000 JPX</comment>
+ <comment xml:lang="sv">JPEG-2000 JPX-bild</comment>
+ <comment xml:lang="uk">зображення JPX JPEG-2000</comment>
+ <comment xml:lang="zh_CN">JPEG-2000 JPX 图像</comment>
+ <comment xml:lang="zh_TW">JPEG-2000 JPX 影像</comment>
+ <acronym>JPX</acronym>
+ <expanded-acronym>JPEG-2000 eXtended</expanded-acronym>
+ <magic priority="50">
+ <match value="\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a jpx\x20" type="string" offset="0" mask="0xffffffffffffffffffffffff0000000000000000ffffffff"/>
+ </magic>
+ <glob pattern="*.jpf"/>
+ <glob pattern="*.jpx"/>
+ </mime-type>
+ <mime-type type="image/jpm">
+ <comment>JPEG-2000 JPM image</comment>
+ <comment xml:lang="ca">imatge JPEG-2000 JPM</comment>
+ <comment xml:lang="cs">obrázek JPEG-2000 JPM</comment>
+ <comment xml:lang="de">JPEG-2000 JPM-Bild</comment>
+ <comment xml:lang="en_GB">JPEG-2000 JPM image</comment>
+ <comment xml:lang="es">imagen JPEG-2000 JPM</comment>
+ <comment xml:lang="fi">JPEG-2000 JPM -kuva</comment>
+ <comment xml:lang="hr">JPEG-2000 JPM slika</comment>
+ <comment xml:lang="hu">JPEG-2000 JPM kép</comment>
+ <comment xml:lang="id">Citra JPEG-2000 JPM</comment>
+ <comment xml:lang="it">Immagine JPEG-2000 JPM</comment>
+ <comment xml:lang="kk">JPEG-2000 JPM суреті</comment>
+ <comment xml:lang="ko">JPEG-2000 JPM 그림</comment>
+ <comment xml:lang="pl">Obraz JPM JPEG-2000</comment>
+ <comment xml:lang="pt_BR">Imagem JPM de JPEG-2000</comment>
+ <comment xml:lang="ru">Изображение JPEG-2000 JPM</comment>
+ <comment xml:lang="sk">Obrázok JPEG-2000 JPM</comment>
+ <comment xml:lang="sv">JPEG-2000 JPM-bild</comment>
+ <comment xml:lang="uk">зображення JPM JPEG-2000</comment>
+ <comment xml:lang="zh_CN">JPEG-2000 JPM 图像</comment>
+ <comment xml:lang="zh_TW">JPEG-2000 JPM 影像</comment>
+ <acronym>JPM</acronym>
+ <expanded-acronym>JPEG-2000 Mixed</expanded-acronym>
+ <magic priority="50">
+ <match value="\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a jpm\x20" type="string" offset="0" mask="0xffffffffffffffffffffffff0000000000000000ffffffff"/>
+ </magic>
+ <glob pattern="*.jpm"/>
+ <glob pattern="*.jpgm"/>
+ </mime-type>
+ <mime-type type="video/mj2">
+ <comment>JPEG-2000 MJ2 video</comment>
+ <comment xml:lang="ca">vídeo JPEG-2000 MJ2</comment>
+ <comment xml:lang="cs">video JPEG-2000 MJ2</comment>
+ <comment xml:lang="de">JPEG-2000 MJ2-Video</comment>
+ <comment xml:lang="en_GB">JPEG-2000 MJ2 video</comment>
+ <comment xml:lang="es">vídeo JPEG-2000 MJ2</comment>
+ <comment xml:lang="fi">JPEG-2000 MJ2 -video</comment>
+ <comment xml:lang="hr">JPEG-2000 MJ2 video snimka</comment>
+ <comment xml:lang="hu">JPEG-2000 MJ2 videó</comment>
+ <comment xml:lang="id">Video JPEG-2000 MJ2</comment>
+ <comment xml:lang="it">Video JPEG-2000 MJ2</comment>
+ <comment xml:lang="kk">JPEG-2000 MJ2 видеосы</comment>
+ <comment xml:lang="ko">JPEG-2000 MJ2 동영상</comment>
+ <comment xml:lang="pl">Plik wideo MJ2 JPEG-2000</comment>
+ <comment xml:lang="pt_BR">Imagem MJ2 de JPEG-2000</comment>
+ <comment xml:lang="ru">Видео JPEG-2000 MJ2</comment>
+ <comment xml:lang="sk">Video JPEG-2000 MJ2</comment>
+ <comment xml:lang="sv">JPEG-2000 MJ2-bild</comment>
+ <comment xml:lang="uk">зображення MJ2 JPEG-2000</comment>
+ <comment xml:lang="zh_CN">JPEG-2000 MJ2 视频</comment>
+ <comment xml:lang="zh_TW">JPEG-2000 MJ2 視訊</comment>
+ <acronym>MJ2</acronym>
+ <expanded-acronym>Motion JPEG-2000</expanded-acronym>
+ <magic priority="50">
+ <match value="\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a mjp2" type="string" offset="0" mask="0xffffffffffffffffffffffff0000000000000000ffffffff"/>
+ </magic>
+ <glob pattern="*.mj2"/>
+ <glob pattern="*.mjp2"/>
+ </mime-type>
+ <mime-type type="image/openraster">
+ <comment>OpenRaster archiving image</comment>
+ <comment xml:lang="ar">صورة أرشيف OpenRaster</comment>
+ <comment xml:lang="bg">Изображение — OpenRaster</comment>
+ <comment xml:lang="ca">imatge d'arxivat OpenRaster</comment>
+ <comment xml:lang="cs">archivační obraz OpenRaster</comment>
+ <comment xml:lang="da">OpenRaster-arkivaftryk</comment>
+ <comment xml:lang="de">OpenRaster-Archivierungsbild</comment>
+ <comment xml:lang="el">Εικόνα αρχειοθέτησης OpenRaster</comment>
+ <comment xml:lang="en_GB">OpenRaster archiving image</comment>
+ <comment xml:lang="es">imagen de archivado de OpenRaster</comment>
+ <comment xml:lang="eu">OpenRaster artxiboaren irudia</comment>
+ <comment xml:lang="fi">OpenRaster-arkistokuva</comment>
+ <comment xml:lang="fo">OpenRaster goymslumynd</comment>
+ <comment xml:lang="fr">image d'archive OpenRaster</comment>
+ <comment xml:lang="ga">íomhá chartlannaithe OpenRaster</comment>
+ <comment xml:lang="gl">imaxe arquivada de OpenRaster</comment>
+ <comment xml:lang="he">תמונת ארכיון של OpenRaster</comment>
+ <comment xml:lang="hr">OpenRaster slika arhive</comment>
+ <comment xml:lang="hu">OpenRaster archiválási kép</comment>
+ <comment xml:lang="ia">Imagine de archivo OpenRaster</comment>
+ <comment xml:lang="id">Gambar pengarsipan OpenRaster</comment>
+ <comment xml:lang="it">Immagine archiviazione OpenRaster</comment>
+ <comment xml:lang="ja">OpenRaster アーカイブイメージ</comment>
+ <comment xml:lang="ka">OpenRaster-ის საარქივო გამოსახულება</comment>
+ <comment xml:lang="kk">OpenRaster архивтеу суреті</comment>
+ <comment xml:lang="ko">OpenRaster 압축 이미지</comment>
+ <comment xml:lang="lt">OpenRaster archyvavimo paveikslėlis</comment>
+ <comment xml:lang="lv">OpenRaster arhivēšanas attēls</comment>
+ <comment xml:lang="nl">OpenRaster archiverings-image</comment>
+ <comment xml:lang="oc">imatge d'archiu OpenRaster</comment>
+ <comment xml:lang="pl">Archiwalny obraz OpenRaster</comment>
+ <comment xml:lang="pt">imagem arquivo OpenRaster</comment>
+ <comment xml:lang="pt_BR">Imagem de arquivamento OpenRaster</comment>
+ <comment xml:lang="ro">Arhivă imagine OpenRaster</comment>
+ <comment xml:lang="ru">Архивное изображение OpenRaster</comment>
+ <comment xml:lang="sk">Archivačný obrázok OpenRaster</comment>
+ <comment xml:lang="sl">Odtis arhiva OpenRaster</comment>
+ <comment xml:lang="sr">слика Опен Растер архивирања</comment>
+ <comment xml:lang="sv">OpenRaster-arkivbild</comment>
+ <comment xml:lang="tr">OpenRaster arşivleme görüntüsü</comment>
+ <comment xml:lang="uk">архівоване зображення OpenRaster</comment>
+ <comment xml:lang="zh_CN">OpenRaster 归档图像</comment>
+ <comment xml:lang="zh_TW">OpenRaster 封存影像</comment>
+ <sub-class-of type="application/zip"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="mimetype" type="string" offset="30">
+ <match value="image/openraster" type="string" offset="38"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.ora"/>
+ </mime-type>
+ <mime-type type="image/x-dds">
+ <comment>DirectDraw surface</comment>
+ <comment xml:lang="ar">مساحة DirectDraw</comment>
+ <comment xml:lang="be@latin">Pavierchnia DirectDraw</comment>
+ <comment xml:lang="bg">Изображение — повърхност на DirectDraw</comment>
+ <comment xml:lang="ca">superfície DirectDraw</comment>
+ <comment xml:lang="cs">povrch DirectDraw</comment>
+ <comment xml:lang="da">DirectDraw-overflade</comment>
+ <comment xml:lang="de">DirectDraw-Oberfläche</comment>
+ <comment xml:lang="el">Επιφάνεια DirectDraw</comment>
+ <comment xml:lang="en_GB">DirectDraw surface</comment>
+ <comment xml:lang="es">superficie de DirectDraw</comment>
+ <comment xml:lang="eu">DirectDraw gainazala</comment>
+ <comment xml:lang="fi">DirectDraw-piirtoalue</comment>
+ <comment xml:lang="fo">DirectDraw yvirflata</comment>
+ <comment xml:lang="fr">surface DirectDraw</comment>
+ <comment xml:lang="ga">dromchla DirectDraw</comment>
+ <comment xml:lang="gl">superficie de DirectDraw</comment>
+ <comment xml:lang="he">משטח של DirectDraw</comment>
+ <comment xml:lang="hr">DirectDraw ploha</comment>
+ <comment xml:lang="hu">DirectDraw felület</comment>
+ <comment xml:lang="ia">Superficie DirectDraw</comment>
+ <comment xml:lang="id">Permukaan DirectDraw</comment>
+ <comment xml:lang="it">Superficie DirectDraw</comment>
+ <comment xml:lang="ja">DirectDraw サーフェイス</comment>
+ <comment xml:lang="ka">DirectDraw-ის ზედაპირი</comment>
+ <comment xml:lang="kk">DirectDraw жазықтығы</comment>
+ <comment xml:lang="ko">DirectDraw 서피스</comment>
+ <comment xml:lang="lt">DirectDraw paviršius</comment>
+ <comment xml:lang="lv">DirectDraw virsma</comment>
+ <comment xml:lang="nb">DirectDraw-overflate</comment>
+ <comment xml:lang="nl">DirectDraw-oppervlak</comment>
+ <comment xml:lang="nn">DirectDraw-overflate</comment>
+ <comment xml:lang="oc">surfàcia DirectDraw</comment>
+ <comment xml:lang="pl">Powierzchnia DirectDraw</comment>
+ <comment xml:lang="pt">superfície DirectDraw</comment>
+ <comment xml:lang="pt_BR">Superfície do DirectDraw</comment>
+ <comment xml:lang="ro">Suprafață DirectDraw</comment>
+ <comment xml:lang="ru">Плоскость DirectDraw</comment>
+ <comment xml:lang="sk">Plocha DirectDraw</comment>
+ <comment xml:lang="sl">Datoteka predmeta DirectDraw</comment>
+ <comment xml:lang="sq">Superfaqe DirectDraw</comment>
+ <comment xml:lang="sr">Директ Дров површина</comment>
+ <comment xml:lang="sv">DirectDraw-yta</comment>
+ <comment xml:lang="tr">DirectDraw yüzeyi</comment>
+ <comment xml:lang="uk">поверхня DirectDraw</comment>
+ <comment xml:lang="vi">Mặt DirectDraw</comment>
+ <comment xml:lang="zh_CN">DirectDraw 表面</comment>
+ <comment xml:lang="zh_TW">DirectDraw 表面</comment>
+ <magic priority="50">
+ <match value="DDS" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.dds"/>
+ </mime-type>
+ <mime-type type="image/x-xcursor">
+ <comment>X11 cursor</comment>
+ <comment xml:lang="ar">مؤشر X11</comment>
+ <comment xml:lang="be@latin">Kursor X11</comment>
+ <comment xml:lang="bg">Курсор — X11</comment>
+ <comment xml:lang="ca">cursor X11</comment>
+ <comment xml:lang="cs">kurzor X11</comment>
+ <comment xml:lang="da">X11-markør</comment>
+ <comment xml:lang="de">X11-Zeiger</comment>
+ <comment xml:lang="el">Δρομέας X11</comment>
+ <comment xml:lang="en_GB">X11 cursor</comment>
+ <comment xml:lang="es">cursor de X11</comment>
+ <comment xml:lang="eu">X11 kurtsorea</comment>
+ <comment xml:lang="fi">X11-osoitin</comment>
+ <comment xml:lang="fo">X11 vísi</comment>
+ <comment xml:lang="fr">curseur X11</comment>
+ <comment xml:lang="ga">cúrsóir X11</comment>
+ <comment xml:lang="gl">Cursor X11</comment>
+ <comment xml:lang="he">סמן של X11</comment>
+ <comment xml:lang="hr">X11 pokazivač</comment>
+ <comment xml:lang="hu">X11 kurzor</comment>
+ <comment xml:lang="ia">Cursor X11</comment>
+ <comment xml:lang="id">Kursor X11</comment>
+ <comment xml:lang="it">Cursore X11</comment>
+ <comment xml:lang="ja">X11 カーソル</comment>
+ <comment xml:lang="kk">X11 курсоры</comment>
+ <comment xml:lang="ko">X11 커서</comment>
+ <comment xml:lang="lt">X11 žymiklis</comment>
+ <comment xml:lang="lv">X11 kursors</comment>
+ <comment xml:lang="nb">X11-markør</comment>
+ <comment xml:lang="nl">X11-muisaanwijzer</comment>
+ <comment xml:lang="nn">X11-peikar</comment>
+ <comment xml:lang="oc">cursor X11</comment>
+ <comment xml:lang="pl">Kursor X11</comment>
+ <comment xml:lang="pt">cursor X11</comment>
+ <comment xml:lang="pt_BR">Cursor do X11</comment>
+ <comment xml:lang="ro">Cursor X11</comment>
+ <comment xml:lang="ru">Курсор X11</comment>
+ <comment xml:lang="sk">Kurzor X11</comment>
+ <comment xml:lang="sl">Datoteka kazalke X11</comment>
+ <comment xml:lang="sq">Kursor X11</comment>
+ <comment xml:lang="sr">Икс11 курсор</comment>
+ <comment xml:lang="sv">X11-muspekare</comment>
+ <comment xml:lang="tr">X11 imleci</comment>
+ <comment xml:lang="uk">курсор X11</comment>
+ <comment xml:lang="vi">Con chạy X11</comment>
+ <comment xml:lang="zh_CN">X11 指针</comment>
+ <comment xml:lang="zh_TW">X11 滑鼠游標</comment>
+ <magic priority="50">
+ <match value="Xcur" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/x-exr">
+ <comment>EXR image</comment>
+ <comment xml:lang="ar">صورة EXR</comment>
+ <comment xml:lang="be@latin">Vyjava EXR</comment>
+ <comment xml:lang="bg">Изображение — EXR</comment>
+ <comment xml:lang="ca">imatge EXR</comment>
+ <comment xml:lang="cs">obrázek EXR</comment>
+ <comment xml:lang="da">EXR-billede</comment>
+ <comment xml:lang="de">EXR-Bild</comment>
+ <comment xml:lang="el">Εικόνα EXR</comment>
+ <comment xml:lang="en_GB">EXR image</comment>
+ <comment xml:lang="eo">EXR-bildo</comment>
+ <comment xml:lang="es">imagen EXR</comment>
+ <comment xml:lang="eu">EXR irudia</comment>
+ <comment xml:lang="fi">EXR-kuva</comment>
+ <comment xml:lang="fo">EXR mynd</comment>
+ <comment xml:lang="fr">image EXR</comment>
+ <comment xml:lang="ga">íomhá EXR</comment>
+ <comment xml:lang="gl">imaxe EXR</comment>
+ <comment xml:lang="he">תמונת EXR</comment>
+ <comment xml:lang="hr">EXR slika</comment>
+ <comment xml:lang="hu">EXR kép</comment>
+ <comment xml:lang="ia">Imagine EXR</comment>
+ <comment xml:lang="id">Citra EXR</comment>
+ <comment xml:lang="it">Immagine EXR</comment>
+ <comment xml:lang="ja">EXR 画像</comment>
+ <comment xml:lang="ka">EXR გამოსახულება</comment>
+ <comment xml:lang="kk">EXR суреті</comment>
+ <comment xml:lang="ko">EXR 그림</comment>
+ <comment xml:lang="lt">EXR paveikslėlis</comment>
+ <comment xml:lang="lv">EXR attēls</comment>
+ <comment xml:lang="nb">EXR-bilde</comment>
+ <comment xml:lang="nl">EXR-afbeelding</comment>
+ <comment xml:lang="nn">EXR-bilete</comment>
+ <comment xml:lang="oc">imatge EXR</comment>
+ <comment xml:lang="pl">Obraz EXR</comment>
+ <comment xml:lang="pt">imagem EXR</comment>
+ <comment xml:lang="pt_BR">Imagem EXR</comment>
+ <comment xml:lang="ro">Imagine EXR</comment>
+ <comment xml:lang="ru">Изображение EXR</comment>
+ <comment xml:lang="sk">Obrázok EXR</comment>
+ <comment xml:lang="sl">Slikovna datoteka EXR</comment>
+ <comment xml:lang="sq">Figurë EXR</comment>
+ <comment xml:lang="sr">ЕИксР слика</comment>
+ <comment xml:lang="sv">EXR-bild</comment>
+ <comment xml:lang="tr">EXR görüntüsü</comment>
+ <comment xml:lang="uk">зображення EXR</comment>
+ <comment xml:lang="vi">Ảnh EXR</comment>
+ <comment xml:lang="zh_CN">EXR 图像</comment>
+ <comment xml:lang="zh_TW">EXR 影像</comment>
+ <magic priority="50">
+ <match value="20000630" type="little32" offset="0"/>
+ </magic>
+ <glob pattern="*.exr"/>
+ </mime-type>
+ <mime-type type="image/x-pict">
+ <comment>Macintosh Quickdraw/PICT drawing</comment>
+ <comment xml:lang="ar">رسمة ماكنتوش Quickdraw/PICT</comment>
+ <comment xml:lang="be@latin">Rysunak Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="bg">Чертеж — Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="ca">dibuix Quickdraw/PICT de Macintosh</comment>
+ <comment xml:lang="cs">kresba Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="da">Macintosh Quickdraw/PICT-tegning</comment>
+ <comment xml:lang="de">Macintosh-Quickdraw/PICT-Zeichnung</comment>
+ <comment xml:lang="el">Σχέδιο Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="en_GB">Macintosh Quickdraw/PICT drawing</comment>
+ <comment xml:lang="eo">Quickdraw/PICT-grafikaĵo de Macintosh</comment>
+ <comment xml:lang="es">dibujo de Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="eu">Macintosh Quickdraw/PICT marrazkia</comment>
+ <comment xml:lang="fi">Macintosh Quickdraw/PICT -piirros</comment>
+ <comment xml:lang="fo">Macintosh Quickdraw/PICT tekning</comment>
+ <comment xml:lang="fr">dessin Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="ga">líníocht Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="gl">debuxo de Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="he">ציור של Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="hr">Macintosh Quickdraw/PICT crtež</comment>
+ <comment xml:lang="hu">Macintosh Quickdraw/PICT-rajz</comment>
+ <comment xml:lang="ia">Designo QuickDraw/PICT de Macintosh</comment>
+ <comment xml:lang="id">Gambar Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="it">Disegno Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="ja">Macintosh Quickdraw/PICT ドロー</comment>
+ <comment xml:lang="kk">Macintosh Quickdraw/PICT суреті</comment>
+ <comment xml:lang="ko">매킨토시 Quickdraw/PICT 그림</comment>
+ <comment xml:lang="lt">Macintosh Quickdraw/PICT piešinys</comment>
+ <comment xml:lang="lv">Macintosh Quickdraw/PICT zīmējums</comment>
+ <comment xml:lang="ms">Lukisan Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="nb">Macintosh Quickdraw/PICT-tegning</comment>
+ <comment xml:lang="nl">Macintosh Quickdraw/PICT-tekening</comment>
+ <comment xml:lang="nn">Macintosh Quickdraw/PICT-teikning</comment>
+ <comment xml:lang="oc">dessenh Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="pl">Rysunek QuickDraw/PICT Macintosh</comment>
+ <comment xml:lang="pt">desenho Quickdraw/PICT de Macintosh</comment>
+ <comment xml:lang="pt_BR">Desenho do Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="ro">Desen Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="ru">Рисунок Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="sk">Kresba Macintosh QuickDraw/PICT</comment>
+ <comment xml:lang="sl">Datoteka risbe Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="sq">Vizatim Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="sr">Мекинтошов Квикдров/ПИЦТ цртеж</comment>
+ <comment xml:lang="sv">Macintosh Quickdraw/PICT-teckning</comment>
+ <comment xml:lang="tr">Macintosh Quickdraw/PICT çizimi</comment>
+ <comment xml:lang="uk">малюнок Macintosh Quickdraw/PICT</comment>
+ <comment xml:lang="vi">Bản vẽ Quickdraw/PICT của Macintosh</comment>
+ <comment xml:lang="zh_CN">Macintosh Quickdraw/PICT 绘图</comment>
+ <comment xml:lang="zh_TW">Macintosh Quickdraw/PICT 繪圖</comment>
+ <magic priority="50">
+ <match value="0x0011" type="big16" offset="10">
+ <match value="0x02FF" type="big16" offset="12">
+ <match value="0x0C00" type="big16" offset="14">
+ <match value="0xFFFE" type="big16" offset="16"/>
+ </match>
+ </match>
+ </match>
+ </magic>
+ <magic priority="50">
+ <match value="0x0011" type="big16" offset="522">
+ <match value="0x02FF" type="big16" offset="524">
+ <match value="0x0C00" type="big16" offset="526">
+ <match value="0xFFFE" type="big16" offset="528"/>
+ </match>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.pct"/>
+ <glob pattern="*.pict"/>
+ <glob pattern="*.pict1"/>
+ <glob pattern="*.pict2"/>
+ </mime-type>
+ <mime-type type="application/x-ufraw">
+ <comment>UFRaw ID image</comment>
+ <comment xml:lang="ar">صورة UFRaw ID</comment>
+ <comment xml:lang="be@latin">Vyjava UFRaw ID</comment>
+ <comment xml:lang="bg">Изображение — UFRaw ID</comment>
+ <comment xml:lang="ca">imatge ID UFRaw</comment>
+ <comment xml:lang="cs">obrázek ID UFRaw</comment>
+ <comment xml:lang="da">UFRaw ID-billede</comment>
+ <comment xml:lang="de">UFRaw-Bildbeschreibungsdatei</comment>
+ <comment xml:lang="el">Εικόνα UFRaw</comment>
+ <comment xml:lang="en_GB">UFRaw ID image</comment>
+ <comment xml:lang="es">imagen de identificación UFRaw</comment>
+ <comment xml:lang="eu">UFRaw ID irudia</comment>
+ <comment xml:lang="fi">UFRaw ID -kuva</comment>
+ <comment xml:lang="fo">UFRaw ID mynd</comment>
+ <comment xml:lang="fr">image ID UFRaw</comment>
+ <comment xml:lang="ga">íomhá aitheantais UFRaw</comment>
+ <comment xml:lang="gl">imaxe de identificación UFRaw</comment>
+ <comment xml:lang="he">תמונה של UFRaw ID</comment>
+ <comment xml:lang="hr">UFRaw ID slika</comment>
+ <comment xml:lang="hu">UFRaw azonosítófájl</comment>
+ <comment xml:lang="ia">Imagine UFRaw ID</comment>
+ <comment xml:lang="id">Citra UFRaw ID</comment>
+ <comment xml:lang="it">Immagine UFRaw ID</comment>
+ <comment xml:lang="ja">UFRaw ID イメージ</comment>
+ <comment xml:lang="kk">UFRaw ID суреті</comment>
+ <comment xml:lang="ko">UFRaw ID 그림</comment>
+ <comment xml:lang="lt">UFRaw ID paveikslėlis</comment>
+ <comment xml:lang="lv">UFRaw ID attēls</comment>
+ <comment xml:lang="nb">UFRaw ID-bilde</comment>
+ <comment xml:lang="nl">UFRaw ID-afbeelding</comment>
+ <comment xml:lang="nn">UFRaw ID-bilete</comment>
+ <comment xml:lang="oc">imatge ID UFRaw</comment>
+ <comment xml:lang="pl">Obraz UFRaw ID</comment>
+ <comment xml:lang="pt">imagem UFRaw ID</comment>
+ <comment xml:lang="pt_BR">Imagem ID do UFRaw</comment>
+ <comment xml:lang="ro">ID imagine UFRaw</comment>
+ <comment xml:lang="ru">Изображение UFRaw ID</comment>
+ <comment xml:lang="sk">Obrázok ID UFRaw</comment>
+ <comment xml:lang="sl">Slikovna datoteka UFRaw ID</comment>
+ <comment xml:lang="sq">Figurë UFRaw ID</comment>
+ <comment xml:lang="sr">УФ сирова ИД слика</comment>
+ <comment xml:lang="sv">UFRaw ID-bild</comment>
+ <comment xml:lang="tr">UFRaw ID görüntüsü</comment>
+ <comment xml:lang="uk">зображення UFRaw ID</comment>
+ <comment xml:lang="vi">Ảnh ID UFRaw</comment>
+ <comment xml:lang="zh_CN">UFRaw ID 图像</comment>
+ <comment xml:lang="zh_TW">UFRaw ID 影像</comment>
+ <acronym>UFRaw</acronym>
+ <expanded-acronym>Unidentified Flying Raw</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="*.ufraw"/>
+ </mime-type>
+ <mime-type type="image/x-dcraw">
+ <comment>digital raw image</comment>
+ <comment xml:lang="ar">صورة رقمية خامة</comment>
+ <comment xml:lang="be@latin">suvoraja ličbavaja vyjava</comment>
+ <comment xml:lang="bg">Изображение — digital raw</comment>
+ <comment xml:lang="ca">imatge digital en cru</comment>
+ <comment xml:lang="cs">digitální surový obrázek</comment>
+ <comment xml:lang="da">digitalt råbillede</comment>
+ <comment xml:lang="de">Digitales Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη ψηφιακή εικόνα</comment>
+ <comment xml:lang="en_GB">digital raw image</comment>
+ <comment xml:lang="es">imagen digital en bruto</comment>
+ <comment xml:lang="eu">irudi gordin digitala</comment>
+ <comment xml:lang="fi">digitaalinen raakakuva</comment>
+ <comment xml:lang="fo">talgild rámynd</comment>
+ <comment xml:lang="fr">image brute numérique</comment>
+ <comment xml:lang="ga">amhíomhá dhigiteach</comment>
+ <comment xml:lang="gl">imaxe en bruto dixital</comment>
+ <comment xml:lang="he">תמונה דיגטלית גולמית</comment>
+ <comment xml:lang="hr">Digitalna osnovna slika</comment>
+ <comment xml:lang="hu">digitális nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute digital</comment>
+ <comment xml:lang="id">citra mentah digital</comment>
+ <comment xml:lang="it">Immagine raw digitale</comment>
+ <comment xml:lang="ja">デジタル raw 画像</comment>
+ <comment xml:lang="kk">өңделмеген сандық суреттер</comment>
+ <comment xml:lang="ko">디지털 원본 사진</comment>
+ <comment xml:lang="lt">skaitmeninis neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">digitāls jēlattēls</comment>
+ <comment xml:lang="nb">digitalt raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt digitaal beeld</comment>
+ <comment xml:lang="nn">digitalt råbilete</comment>
+ <comment xml:lang="oc">imatge brut numeric</comment>
+ <comment xml:lang="pl">Surowy obraz cyfrowy</comment>
+ <comment xml:lang="pt">imagem digital em bruto</comment>
+ <comment xml:lang="pt_BR">Imagem digital bruta</comment>
+ <comment xml:lang="ro">imagine digitală brută</comment>
+ <comment xml:lang="ru">Необработанное цифровое изображение</comment>
+ <comment xml:lang="sk">Digitálny surový obrázok</comment>
+ <comment xml:lang="sl">surova digitalna slika</comment>
+ <comment xml:lang="sq">Figurë raw dixhitale</comment>
+ <comment xml:lang="sr">дигитална сирова слика</comment>
+ <comment xml:lang="sv">digital råbild</comment>
+ <comment xml:lang="tr">sayısal ham görüntü</comment>
+ <comment xml:lang="uk">зображення цифрового негатива</comment>
+ <comment xml:lang="vi">ảnh thô số</comment>
+ <comment xml:lang="zh_CN">数字化原始图像</comment>
+ <comment xml:lang="zh_TW">數位原生影像</comment>
+ </mime-type>
+ <mime-type type="image/x-adobe-dng">
+ <comment>Adobe DNG negative</comment>
+ <comment xml:lang="ar">Adobe DNG negative</comment>
+ <comment xml:lang="be@latin">Adobe DNG Negative</comment>
+ <comment xml:lang="bg">Изображение — Adobe DNG negative</comment>
+ <comment xml:lang="ca">negatiu DNG d'Adobe</comment>
+ <comment xml:lang="cs">negativ Adobe (DNG)</comment>
+ <comment xml:lang="da">Adobe DNG-negativ</comment>
+ <comment xml:lang="de">Adobe Digitales Negativ</comment>
+ <comment xml:lang="el">Αρνητικό Adobe DNG</comment>
+ <comment xml:lang="en_GB">Adobe DNG negative</comment>
+ <comment xml:lang="es">negativo DNG de Adobe</comment>
+ <comment xml:lang="eu">Adobe DNG negatiboa</comment>
+ <comment xml:lang="fi">Adobe-DNG-negatiivi</comment>
+ <comment xml:lang="fo">Adobe DNG negativ</comment>
+ <comment xml:lang="fr">négatif DNG Adobe</comment>
+ <comment xml:lang="ga">claonchló DNG Adobe</comment>
+ <comment xml:lang="gl">negativo DNG de Adobe</comment>
+ <comment xml:lang="he">תשליל Adobe DNG</comment>
+ <comment xml:lang="hr">Adobe DNG negativ</comment>
+ <comment xml:lang="hu">Adobe DNG negatív</comment>
+ <comment xml:lang="ia">Negativo Adobe DNG</comment>
+ <comment xml:lang="id">Negatif Adobe DNG</comment>
+ <comment xml:lang="it">Negativo Adobe DNG</comment>
+ <comment xml:lang="ja">Adobe DNG ネガ</comment>
+ <comment xml:lang="ka">Adobe DNG-ის ნეგატივი</comment>
+ <comment xml:lang="kk">Adobe DNG негативі</comment>
+ <comment xml:lang="ko">Adobe DNG 네거티브</comment>
+ <comment xml:lang="lt">Adobe DNG negatyvas</comment>
+ <comment xml:lang="lv">Adobe DNG negatīvs</comment>
+ <comment xml:lang="nb">Adobe DNG-negativ</comment>
+ <comment xml:lang="nl">Adobe DNG-negatief</comment>
+ <comment xml:lang="nn">Adobe DNG-negativ</comment>
+ <comment xml:lang="oc">négatif DNG Adobe</comment>
+ <comment xml:lang="pl">Negatyw DNG Adobe</comment>
+ <comment xml:lang="pt">negativo Adobe DNG</comment>
+ <comment xml:lang="pt_BR">Negativo DNG da Adobe</comment>
+ <comment xml:lang="ro">Negativ Adobe DNG</comment>
+ <comment xml:lang="ru">Негатив Adobe DNG</comment>
+ <comment xml:lang="sk">Adobe Digital Negative (DNG)</comment>
+ <comment xml:lang="sl">Datoteka negativa Adobe DNG</comment>
+ <comment xml:lang="sq">Negativ Adobe DNG</comment>
+ <comment xml:lang="sr">Адобов ДНГ негатив</comment>
+ <comment xml:lang="sv">Adobe DNG-negativ</comment>
+ <comment xml:lang="tr">Adobe DNG negatifi</comment>
+ <comment xml:lang="uk">цифровий негатив DNG Adobe</comment>
+ <comment xml:lang="vi">Âm bản Adobe DNG</comment>
+ <comment xml:lang="zh_CN">Adobe DNG 负片</comment>
+ <comment xml:lang="zh_TW">Adobe DNG 負片</comment>
+ <acronym>DNG</acronym>
+ <expanded-acronym>Digital Negative</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <sub-class-of type="image/tiff"/>
+ <glob pattern="*.dng"/>
+ </mime-type>
+
+
+ <mime-type type="image/x-canon-crw">
+ <comment>Canon CRW raw image</comment>
+ <comment xml:lang="ar">صورة Canon CRW خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Canon CRW</comment>
+ <comment xml:lang="bg">Изображение — Canon CRW raw</comment>
+ <comment xml:lang="ca">imatge en cru de Canon CRW</comment>
+ <comment xml:lang="cs">surový obrázek Canon CRW</comment>
+ <comment xml:lang="da">Canon CRW-råbillede</comment>
+ <comment xml:lang="de">Canon-CRW-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Canon CRW</comment>
+ <comment xml:lang="en_GB">Canon CRW raw image</comment>
+ <comment xml:lang="es">imagen en bruto CRW de Canon</comment>
+ <comment xml:lang="eu">Canon CRW irudi gordina</comment>
+ <comment xml:lang="fi">Canon-CRW-raakakuva</comment>
+ <comment xml:lang="fo">Canon CRW rámynd</comment>
+ <comment xml:lang="fr">image brute CRW Canon</comment>
+ <comment xml:lang="ga">amhíomhá Canon CRW</comment>
+ <comment xml:lang="gl">imaxe en bruto de Canon CRW</comment>
+ <comment xml:lang="he">תמונה גולמית של Canon CRW</comment>
+ <comment xml:lang="hr">Canon CRW osnovna slika</comment>
+ <comment xml:lang="hu">Canon CRW nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute CRW Canon</comment>
+ <comment xml:lang="id">Citra mentah Canon CRW</comment>
+ <comment xml:lang="it">Immagine raw Canon CRW</comment>
+ <comment xml:lang="ja">Canon CRW raw 画像</comment>
+ <comment xml:lang="ka">Canon CRW raw გამოსახულება</comment>
+ <comment xml:lang="kk">Canon CRW өңделмеген суреті</comment>
+ <comment xml:lang="ko">캐논 CRW RAW 사진</comment>
+ <comment xml:lang="lt">Canon CRW neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Canon CRW jēlattēls</comment>
+ <comment xml:lang="nb">Canon CRW raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Canon CRW-beeld</comment>
+ <comment xml:lang="nn">Canon CRW råbilete</comment>
+ <comment xml:lang="oc">imatge brut CRW Canon</comment>
+ <comment xml:lang="pl">Surowy obraz CRW Canon</comment>
+ <comment xml:lang="pt">imagem em bruto Canon CRW</comment>
+ <comment xml:lang="pt_BR">Imagem bruta CRW da Canon</comment>
+ <comment xml:lang="ro">Imagine brută Canon CRW</comment>
+ <comment xml:lang="ru">Необработанное изображение Canon CRW</comment>
+ <comment xml:lang="sk">Surový obrázok Canon CRW</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Canon CRW</comment>
+ <comment xml:lang="sq">Figurë raw Canon CRW</comment>
+ <comment xml:lang="sr">Кенон ЦРВ сирова слика</comment>
+ <comment xml:lang="sv">Canon CRW-råbild</comment>
+ <comment xml:lang="tr">Canon CRW ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив CRW Canon</comment>
+ <comment xml:lang="vi">Ảnh thô Canon CRW</comment>
+ <comment xml:lang="zh_CN">佳能 CRW 原始图像</comment>
+ <comment xml:lang="zh_TW">Canon CRW 原生影像</comment>
+ <acronym>CRW</acronym>
+ <expanded-acronym>Canon RaW</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <magic priority="50">
+ <match value="II\x1a\x00\x00\x00HEAPCCDR" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.crw"/>
+ </mime-type>
+
+ <mime-type type="image/x-canon-cr2">
+ <comment>Canon CR2 raw image</comment>
+ <comment xml:lang="ar">صورة Canon CR2 خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Canon CR2</comment>
+ <comment xml:lang="bg">Изображение — Canon CR2 raw</comment>
+ <comment xml:lang="ca">imatge en cru de Canon CR2</comment>
+ <comment xml:lang="cs">surový obrázek Canon CR2</comment>
+ <comment xml:lang="da">Canon CR2-råbillede</comment>
+ <comment xml:lang="de">Canon-CR2-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Canon CR2</comment>
+ <comment xml:lang="en_GB">Canon CR2 raw image</comment>
+ <comment xml:lang="es">imagen en bruto CR2 de Canon</comment>
+ <comment xml:lang="eu">Canon CR2 irudi gordina</comment>
+ <comment xml:lang="fi">Canon-CR2-raakakuva</comment>
+ <comment xml:lang="fo">Canon CR2 rámynd</comment>
+ <comment xml:lang="fr">image brute CR2 Canon</comment>
+ <comment xml:lang="ga">amhíomhá Canon CR2</comment>
+ <comment xml:lang="gl">imaxe en bruto de Canon CR2</comment>
+ <comment xml:lang="he">תמונה גולמית של Canon CR2</comment>
+ <comment xml:lang="hr">Canon CR2 osnovna slika</comment>
+ <comment xml:lang="hu">Canon CR2 nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute CR2 Canon</comment>
+ <comment xml:lang="id">Citra mentah Canon CR2</comment>
+ <comment xml:lang="it">Immagine raw Canon CR2</comment>
+ <comment xml:lang="ja">Canon CR2 raw 画像</comment>
+ <comment xml:lang="ka">Canon CR2 raw გამოსახულება</comment>
+ <comment xml:lang="kk">Canon CR2 өңделмеген суреті</comment>
+ <comment xml:lang="ko">캐논 CR2 RAW 사진</comment>
+ <comment xml:lang="lt">Canon CR2 neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Canon CR2 jēlattēls</comment>
+ <comment xml:lang="nb">Canon CR2 raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Canon CR2-beeld</comment>
+ <comment xml:lang="nn">Canon CR2 råbilete</comment>
+ <comment xml:lang="oc">imatge brut CR2 Canon</comment>
+ <comment xml:lang="pl">Surowy obraz CR2 Canon</comment>
+ <comment xml:lang="pt">imagem em bruto Canon CR2</comment>
+ <comment xml:lang="pt_BR">Imagem bruta CR2 da Canon</comment>
+ <comment xml:lang="ro">Imagine brută Canon CR2</comment>
+ <comment xml:lang="ru">Необработанное изображение Canon CR2</comment>
+ <comment xml:lang="sk">Surový obrázok Canon CR2</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Canon CR2</comment>
+ <comment xml:lang="sq">Figurë raw Canon CR2</comment>
+ <comment xml:lang="sr">Кенон ЦР2 сирова слика</comment>
+ <comment xml:lang="sv">Canon CR2-råbild</comment>
+ <comment xml:lang="tr">Canon CR2 ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив CR2 Canon</comment>
+ <comment xml:lang="vi">Ảnh thô Canon CR2</comment>
+ <comment xml:lang="zh_CN">佳能 CR2 原始图像</comment>
+ <comment xml:lang="zh_TW">Canon CR2 原生影像</comment>
+ <acronym>CR2</acronym>
+ <expanded-acronym>Canon Raw 2</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <sub-class-of type="image/tiff"/>
+ <glob pattern="*.cr2"/>
+ </mime-type>
+ <mime-type type="image/x-fuji-raf">
+ <comment>Fuji RAF raw image</comment>
+ <comment xml:lang="ar">صورة Fuji RAF خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Fuji RAF</comment>
+ <comment xml:lang="bg">Изображение — Fuji RAF raw</comment>
+ <comment xml:lang="ca">imatge en cru de Fuji RAF</comment>
+ <comment xml:lang="cs">surový obrázek Fuji RAF</comment>
+ <comment xml:lang="da">Fuji RAF-råbillede</comment>
+ <comment xml:lang="de">Fuji-RAF-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Fuji RAF</comment>
+ <comment xml:lang="en_GB">Fuji RAF raw image</comment>
+ <comment xml:lang="es">imagen en bruto RAF de Fuji</comment>
+ <comment xml:lang="eu">Fuji RAF irudi gordina</comment>
+ <comment xml:lang="fi">Fuji-RAF-raakakuva</comment>
+ <comment xml:lang="fo">Fuji RAF raw mynd</comment>
+ <comment xml:lang="fr">image brute RAF Fuji</comment>
+ <comment xml:lang="ga">amhíomhá Fuji RAF</comment>
+ <comment xml:lang="gl">imaxe en bruto de Fuji RAF</comment>
+ <comment xml:lang="he">תמונה גולמית של Fuji RAF</comment>
+ <comment xml:lang="hr">Fuji RAF osnovna slika</comment>
+ <comment xml:lang="hu">Fuji RAF nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute RAF de Fuji</comment>
+ <comment xml:lang="id">Citra mentah Fuji RAF</comment>
+ <comment xml:lang="it">Immagine raw Fuji RAF</comment>
+ <comment xml:lang="ja">Fuji RAF raw 画像</comment>
+ <comment xml:lang="ka">Fuji RAF-ის raw გამოსახულება</comment>
+ <comment xml:lang="kk">Fuji RAF өңделмеген суреті</comment>
+ <comment xml:lang="ko">후지 RAF RAW 사진</comment>
+ <comment xml:lang="lt">Fuji RAF neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Fuji RAF jēlattēls</comment>
+ <comment xml:lang="nb">Fuji RAF raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Fuji RAF-beeld</comment>
+ <comment xml:lang="nn">Fuji RAF rått bilete</comment>
+ <comment xml:lang="oc">imatge brut RAF Fuji</comment>
+ <comment xml:lang="pl">Surowy obraz RAF Fuji</comment>
+ <comment xml:lang="pt">imagem em bruto Fuji RAF</comment>
+ <comment xml:lang="pt_BR">Imagem bruta RAF da Fuji</comment>
+ <comment xml:lang="ro">Imagine brută Fuji RAF</comment>
+ <comment xml:lang="ru">Необработанное изображение Fuji RAF</comment>
+ <comment xml:lang="sk">Surový obrázok Fuji RAF</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Fuji RAF</comment>
+ <comment xml:lang="sq">Figurë raw Fuji RAF</comment>
+ <comment xml:lang="sr">Фуџи РАФ сирова слика</comment>
+ <comment xml:lang="sv">Fuji RAF-råbild</comment>
+ <comment xml:lang="tr">Fuji RAF ham görüntüsü</comment>
+ <comment xml:lang="uk">Цифровий негатив RAF Fuji</comment>
+ <comment xml:lang="vi">Ảnh thô Fuji RAF</comment>
+ <comment xml:lang="zh_CN">富士 RAF 原始图像</comment>
+ <comment xml:lang="zh_TW">Fuji RAF 原生影像</comment>
+ <acronym>RAF</acronym>
+ <expanded-acronym>RAw Format</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <magic priority="50">
+ <match value="FUJIFILMCCD-RAW " type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.raf"/>
+ </mime-type>
+ <mime-type type="image/x-kodak-dcr">
+ <comment>Kodak DCR raw image</comment>
+ <comment xml:lang="ar">صورة Kodak DCR خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Kodak DCR</comment>
+ <comment xml:lang="bg">Изображение — Kodak DCR raw</comment>
+ <comment xml:lang="ca">imatge en cru de Kodak DCR</comment>
+ <comment xml:lang="cs">surový obrázek Kodak DCR</comment>
+ <comment xml:lang="da">Kodak DCR-råbillede</comment>
+ <comment xml:lang="de">Kodak-DCR-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Kodak DCR</comment>
+ <comment xml:lang="en_GB">Kodak DCR raw image</comment>
+ <comment xml:lang="es">imagen en bruto DCR de Kodak</comment>
+ <comment xml:lang="eu">Kodak DCR irudi gordina</comment>
+ <comment xml:lang="fi">Kodak-DCR-raakakuva</comment>
+ <comment xml:lang="fo">Kodak DCR rámynd</comment>
+ <comment xml:lang="fr">image brute DCR Kodak</comment>
+ <comment xml:lang="ga">amhíomhá Kodak DCR</comment>
+ <comment xml:lang="gl">imaxe en bruto de Kodad DCR</comment>
+ <comment xml:lang="he">תמונה גולמית של Kodak DCR</comment>
+ <comment xml:lang="hr">Kodak DCR osnovna slika</comment>
+ <comment xml:lang="hu">Kodak DCR nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute DCR de Kodak</comment>
+ <comment xml:lang="id">Citra mentah Kodak DCR</comment>
+ <comment xml:lang="it">Immagine raw Kodak DCR</comment>
+ <comment xml:lang="ja">Kodak DCR raw 画像</comment>
+ <comment xml:lang="kk">Kodak DCR өңделмеген суреті</comment>
+ <comment xml:lang="ko">코닥 DCR RAW 사진</comment>
+ <comment xml:lang="lt">Kodak DCR neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Kodak DCR jēlattēls</comment>
+ <comment xml:lang="nb">Kodak DCR raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Kodak DCR-beeld</comment>
+ <comment xml:lang="nn">Kodak DCR råbilete</comment>
+ <comment xml:lang="oc">imatge brut DCR Kodak</comment>
+ <comment xml:lang="pl">Surowy obraz DCR Kodak</comment>
+ <comment xml:lang="pt">imagem em bruto Kodak DCR</comment>
+ <comment xml:lang="pt_BR">Imagem bruta DCR da Kodak</comment>
+ <comment xml:lang="ro">Imagine brută Kodak DCR</comment>
+ <comment xml:lang="ru">Необработанное изображение Kodak DCR</comment>
+ <comment xml:lang="sk">Surový obrázok Kodak DCR</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Kodak DCR</comment>
+ <comment xml:lang="sq">Figurë raw Kodak DCR</comment>
+ <comment xml:lang="sr">Кодак ДЦР сирова слика</comment>
+ <comment xml:lang="sv">Kodak DCR-råbild</comment>
+ <comment xml:lang="tr">Kodak DCR ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив DCR Kodak</comment>
+ <comment xml:lang="vi">Ảnh thô Kodak DCR</comment>
+ <comment xml:lang="zh_CN">柯达 DCR 原始图像</comment>
+ <comment xml:lang="zh_TW">Kodak DCR 原生影像</comment>
+ <acronym>DCR</acronym>
+ <expanded-acronym>Digital Camera Raw</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <sub-class-of type="image/tiff"/>
+ <glob pattern="*.dcr"/>
+ </mime-type>
+ <mime-type type="image/x-kodak-k25">
+ <comment>Kodak K25 raw image</comment>
+ <comment xml:lang="ar">صورة Kodak K25 خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Kodak K25</comment>
+ <comment xml:lang="bg">Изображение — Kodak K25 raw</comment>
+ <comment xml:lang="ca">imatge en cru de Kodak K25</comment>
+ <comment xml:lang="cs">surový obrázek Kodak K25</comment>
+ <comment xml:lang="da">Kodak K25-råbillede</comment>
+ <comment xml:lang="de">Kodak-K25-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Kodak K25</comment>
+ <comment xml:lang="en_GB">Kodak K25 raw image</comment>
+ <comment xml:lang="es">imagen en bruto K25 de Kodak</comment>
+ <comment xml:lang="eu">Kodak K25 raw image</comment>
+ <comment xml:lang="fi">Kodak-K25-raakakuva</comment>
+ <comment xml:lang="fo">Kodak K25 rámynd</comment>
+ <comment xml:lang="fr">image brute K25 Kodak</comment>
+ <comment xml:lang="ga">amhíomhá Kodak K25</comment>
+ <comment xml:lang="gl">imaxe en bruto de Kodad K25</comment>
+ <comment xml:lang="he">תמונה גולמית של Kodak K25</comment>
+ <comment xml:lang="hr">Kodak K25 osnovna slika</comment>
+ <comment xml:lang="hu">Kodak K25 nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute K25 de Kodak</comment>
+ <comment xml:lang="id">Citra mentah Kodak K25</comment>
+ <comment xml:lang="it">Immagine raw Kodak K25</comment>
+ <comment xml:lang="ja">Kodak K25 raw 画像</comment>
+ <comment xml:lang="kk">Kodak K25 өңделмеген суреті</comment>
+ <comment xml:lang="ko">코닥 K25 RAW 사진</comment>
+ <comment xml:lang="lt">Kodak K25 neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Kodak K25 jēlattēls</comment>
+ <comment xml:lang="nb">Kodak K25 raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Kodak K25-beeld</comment>
+ <comment xml:lang="nn">Kodak K25 råbilete</comment>
+ <comment xml:lang="oc">imatge brut K25 Kodak</comment>
+ <comment xml:lang="pl">Surowy obraz K25 Kodak</comment>
+ <comment xml:lang="pt">imagem em bruto Kodak K25</comment>
+ <comment xml:lang="pt_BR">Imagem bruta K25 da Kodak</comment>
+ <comment xml:lang="ro">Imagine brută Kodak K25</comment>
+ <comment xml:lang="ru">Необработанное изображение Kodak K25</comment>
+ <comment xml:lang="sk">Surový obrázok Kodak K25</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Kodak K25</comment>
+ <comment xml:lang="sq">Figurë raw Kodak K25</comment>
+ <comment xml:lang="sr">Кодак К25 сирова слика</comment>
+ <comment xml:lang="sv">Kodak K25-råbild</comment>
+ <comment xml:lang="tr">Kodak K25 ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив K25 Kodak</comment>
+ <comment xml:lang="vi">Ảnh thô Kodak K25</comment>
+ <comment xml:lang="zh_CN">柯达 K25 原始图像</comment>
+ <comment xml:lang="zh_TW">Kodak K25 原生影像</comment>
+ <acronym>K25</acronym>
+ <expanded-acronym>Kodak DC25</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <sub-class-of type="image/tiff"/>
+ <glob pattern="*.k25"/>
+ </mime-type>
+ <mime-type type="image/x-kodak-kdc">
+ <comment>Kodak KDC raw image</comment>
+ <comment xml:lang="ar">صورة Kodak KDC خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Kodak KDC</comment>
+ <comment xml:lang="bg">Изображение — Kodak KDC raw</comment>
+ <comment xml:lang="ca">imatge en cru de Kodak KDC</comment>
+ <comment xml:lang="cs">surový obrázek Kodak KDC</comment>
+ <comment xml:lang="da">Kodak KDC-råbillede</comment>
+ <comment xml:lang="de">Kodak-KDC-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Kodak KDC</comment>
+ <comment xml:lang="en_GB">Kodak KDC raw image</comment>
+ <comment xml:lang="es">imagen en bruto KDC de Kodak</comment>
+ <comment xml:lang="eu">Kodak KDC irudi gordina</comment>
+ <comment xml:lang="fi">Kodak-KDC-raakakuva</comment>
+ <comment xml:lang="fo">Kodak KDC rámynd</comment>
+ <comment xml:lang="fr">image brute KDC Kodak</comment>
+ <comment xml:lang="ga">amhíomhá Kodak KDC</comment>
+ <comment xml:lang="gl">imaxe en bruto de Kodad KDC</comment>
+ <comment xml:lang="he">תמונה גולמית של Kodak KDC</comment>
+ <comment xml:lang="hr">Kodak KDC osnovna slika</comment>
+ <comment xml:lang="hu">Kodak KDC nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute KDC de Kodak</comment>
+ <comment xml:lang="id">Citra mentah Kodak KDC</comment>
+ <comment xml:lang="it">Immagine raw Kodak KDC</comment>
+ <comment xml:lang="ja">Kodak KDC raw 画像</comment>
+ <comment xml:lang="kk">Kodak KDC өңделмеген суреті</comment>
+ <comment xml:lang="ko">코닥 KDC RAW 사진</comment>
+ <comment xml:lang="lt">Kodak KDC neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Kodak KDC jēlattēls</comment>
+ <comment xml:lang="nb">Kodak KDC raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Kodak KDC-beeld</comment>
+ <comment xml:lang="nn">Kodak KDC råbilete</comment>
+ <comment xml:lang="oc">imatge brut KDC Kodak</comment>
+ <comment xml:lang="pl">Surowy obraz KDC Kodak</comment>
+ <comment xml:lang="pt">imagem em bruto Kodak KDC</comment>
+ <comment xml:lang="pt_BR">Imagem bruta KDC da Kodak</comment>
+ <comment xml:lang="ro">Imagine brută Kodak KDC</comment>
+ <comment xml:lang="ru">Необработанное изображение Kodak KDC</comment>
+ <comment xml:lang="sk">Surový obrázok Kodak KDC</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Kodak KDC</comment>
+ <comment xml:lang="sq">Figurë raw Kodak KDC</comment>
+ <comment xml:lang="sr">Кодак КДЦ сирова слика</comment>
+ <comment xml:lang="sv">Kodak KDC-råbild</comment>
+ <comment xml:lang="tr">Kodak KDC ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив KDC Kodak</comment>
+ <comment xml:lang="vi">Ảnh thô Kodak KDC</comment>
+ <comment xml:lang="zh_CN">柯达 KDC 原始图像</comment>
+ <comment xml:lang="zh_TW">Kodak KDC 原生影像</comment>
+ <acronym>KDC</acronym>
+ <expanded-acronym>Kodak Digital Camera</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <sub-class-of type="image/tiff"/>
+ <magic priority="80">
+ <match value="EASTMAN KODAK COMPANY" type="string" offset="242"/>
+ </magic>
+ <glob pattern="*.kdc"/>
+ </mime-type>
+ <mime-type type="image/x-minolta-mrw">
+ <comment>Minolta MRW raw image</comment>
+ <comment xml:lang="ar">صورة Minolta MRW خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Minolta MRW</comment>
+ <comment xml:lang="bg">Изображение — Minolta MRW raw</comment>
+ <comment xml:lang="ca">imatge en cru de Minolta MRW</comment>
+ <comment xml:lang="cs">surový obrázek Minolta MRW</comment>
+ <comment xml:lang="da">Minolta MRW-råbillede</comment>
+ <comment xml:lang="de">Minolta-MRW-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Minolta MRW</comment>
+ <comment xml:lang="en_GB">Minolta MRW raw image</comment>
+ <comment xml:lang="es">imagen en bruto MRW de Minolta</comment>
+ <comment xml:lang="eu">Minolta MRW irudi gordina</comment>
+ <comment xml:lang="fi">Minolta-MRW-raakakuva</comment>
+ <comment xml:lang="fo">Minolta MRW rámynd</comment>
+ <comment xml:lang="fr">image brute MRW Minolta</comment>
+ <comment xml:lang="ga">amhíomhá Minolta MRW</comment>
+ <comment xml:lang="gl">imaxe RAW de Minolta MRW</comment>
+ <comment xml:lang="he">תמונה גולמית של Minolta MRW</comment>
+ <comment xml:lang="hr">Minolta MRW osnovna slika</comment>
+ <comment xml:lang="hu">Minolta MRW nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute Minolta MRW</comment>
+ <comment xml:lang="id">Citra mentah Minolta MRW</comment>
+ <comment xml:lang="it">Immagine raw Minolta MRW</comment>
+ <comment xml:lang="ja">Minolta MRW raw 画像</comment>
+ <comment xml:lang="kk">Minolta MRW өңделмеген суреті</comment>
+ <comment xml:lang="ko">미놀타 MRW RAW 사진</comment>
+ <comment xml:lang="lt">Minolta MRW neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Minolta MRW jēlattēls</comment>
+ <comment xml:lang="nb">Minolta MRW raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Minolta MRW-beeld</comment>
+ <comment xml:lang="nn">Minolta MRW råbilete</comment>
+ <comment xml:lang="oc">imatge brut MRW Minolta</comment>
+ <comment xml:lang="pl">Surowy obraz MRW Minolta</comment>
+ <comment xml:lang="pt">imagem em bruto Minolta MRW</comment>
+ <comment xml:lang="pt_BR">Imagem bruta MRW do Minolta</comment>
+ <comment xml:lang="ro">Imagine brută Minolta MRW</comment>
+ <comment xml:lang="ru">Необработанное изображение Minolta MRW</comment>
+ <comment xml:lang="sk">Surový obrázok Minolta MRW</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Minolta MRW</comment>
+ <comment xml:lang="sq">Figurë raw Minolta MRW</comment>
+ <comment xml:lang="sr">Минолта МРВ сирова слика</comment>
+ <comment xml:lang="sv">Minolta MRW-råbild</comment>
+ <comment xml:lang="tr">Minolta MRW ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив MRW Minolta</comment>
+ <comment xml:lang="vi">Ảnh thô Minolta MRW</comment>
+ <comment xml:lang="zh_CN">美能达 MRW 原始图像</comment>
+ <comment xml:lang="zh_TW">Minolta MRW 原生影像</comment>
+ <acronym>MRW</acronym>
+ <expanded-acronym>Minolta RaW</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <magic priority="50">
+ <match value="\x00MRM" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mrw"/>
+ </mime-type>
+ <mime-type type="image/x-nikon-nef">
+ <comment>Nikon NEF raw image</comment>
+ <comment xml:lang="ar">صورة Nikon NEF خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Nikon NEF</comment>
+ <comment xml:lang="bg">Изображение — Nikon NEF raw</comment>
+ <comment xml:lang="ca">imatge en cru de Nikon NEF</comment>
+ <comment xml:lang="cs">surový obrázek Nikon NEF</comment>
+ <comment xml:lang="da">Nikon NEF-råbillede</comment>
+ <comment xml:lang="de">Nikon-NEF-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Nikon NEF</comment>
+ <comment xml:lang="en_GB">Nikon NEF raw image</comment>
+ <comment xml:lang="es">imagen en bruto NEF de Nikon</comment>
+ <comment xml:lang="eu">Nikon NEF irudi gordina</comment>
+ <comment xml:lang="fi">Nikon-NEF-raakakuva</comment>
+ <comment xml:lang="fo">Nikon NEF rámynd</comment>
+ <comment xml:lang="fr">image brute NEF Nikon</comment>
+ <comment xml:lang="ga">amhíomhá Nikon NEF</comment>
+ <comment xml:lang="gl">imaxe RAW NEF Nikon</comment>
+ <comment xml:lang="he">תמונה גולמית של Nikon NEF</comment>
+ <comment xml:lang="hr">Nikon NEF osnovna slika</comment>
+ <comment xml:lang="hu">Nikon NEF nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute Nikon NEF</comment>
+ <comment xml:lang="id">Citra mentah Nikon NEF</comment>
+ <comment xml:lang="it">Immagine raw Nikon NEF</comment>
+ <comment xml:lang="ja">Nikon NEF raw イメージ</comment>
+ <comment xml:lang="kk">Nikon NEF өңделмеген суреті</comment>
+ <comment xml:lang="ko">니콘 NEF RAW 사진</comment>
+ <comment xml:lang="lt">Nikon NEF neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Nikon NEF jēlattēls</comment>
+ <comment xml:lang="nb">Nikon NEF raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Nikon NEF-beeld</comment>
+ <comment xml:lang="nn">Nikon NEF råbilete</comment>
+ <comment xml:lang="oc">imatge brut NEF Nikon</comment>
+ <comment xml:lang="pl">Surowy obraz NEF Nikon</comment>
+ <comment xml:lang="pt">imagem em bruto Nikon NEF</comment>
+ <comment xml:lang="pt_BR">Imagem bruta NEF da Nikon</comment>
+ <comment xml:lang="ro">Imagine brută Nikon NEF</comment>
+ <comment xml:lang="ru">Необработанное изображение Nikon NEF</comment>
+ <comment xml:lang="sk">Surový obrázok Nikon NEF</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Nikon NEF</comment>
+ <comment xml:lang="sq">Figurë raw Nikon NEF</comment>
+ <comment xml:lang="sr">Никон НЕФ сирова слика</comment>
+ <comment xml:lang="sv">Nikon NEF-råbild</comment>
+ <comment xml:lang="tr">Nikon NEF ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив NEF Nikon</comment>
+ <comment xml:lang="vi">Ảnh thô Nikon NEF</comment>
+ <comment xml:lang="zh_CN">尼康 NEF 原始图像</comment>
+ <comment xml:lang="zh_TW">Nikon NEF 原生影像</comment>
+ <acronym>NEF</acronym>
+ <expanded-acronym>Nikon Electronic Format</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <sub-class-of type="image/tiff"/>
+ <glob pattern="*.nef"/>
+ </mime-type>
+ <mime-type type="image/x-olympus-orf">
+ <comment>Olympus ORF raw image</comment>
+ <comment xml:lang="ar">صورة Olympus ORF خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Olympus ORF</comment>
+ <comment xml:lang="bg">Изображение — Olympus ORF raw</comment>
+ <comment xml:lang="ca">imatge en cru d'Olympus ORF</comment>
+ <comment xml:lang="cs">surový obrázek Olympus ORF</comment>
+ <comment xml:lang="da">Olympus ORF-råbillede</comment>
+ <comment xml:lang="de">Olympus-ORF-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Olympus ORF</comment>
+ <comment xml:lang="en_GB">Olympus ORF raw image</comment>
+ <comment xml:lang="es">imagen en bruto ORF de Olympus</comment>
+ <comment xml:lang="eu">Olympus ORF irudi gordina</comment>
+ <comment xml:lang="fi">Olympus-ORF-raakakuva</comment>
+ <comment xml:lang="fo">Olympus ORF rámynd</comment>
+ <comment xml:lang="fr">image brute ORF Olympus</comment>
+ <comment xml:lang="ga">amhíomhá Olympus ORF</comment>
+ <comment xml:lang="gl">imaxe en bruto de Olympus ORF</comment>
+ <comment xml:lang="he">תמונה גולמית של Olympus ORF</comment>
+ <comment xml:lang="hr">Olympus ORF osnovna slika</comment>
+ <comment xml:lang="hu">Olympus ORF nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute Olympus ORF</comment>
+ <comment xml:lang="id">Citra mentah Olympus ORF</comment>
+ <comment xml:lang="it">Immagine raw Olympus ORF</comment>
+ <comment xml:lang="ja">Olympus ORF raw 画像</comment>
+ <comment xml:lang="ka">Olympus ORF-ის raw გამოსახულება</comment>
+ <comment xml:lang="kk">Olympus ORF өңделмеген суреті</comment>
+ <comment xml:lang="ko">올림푸스 ORF RAW 사진</comment>
+ <comment xml:lang="lt">Olympus ORF neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Olympus ORF jēlattēls</comment>
+ <comment xml:lang="nb">Olympus ORF raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Olympus ORF-beeld</comment>
+ <comment xml:lang="nn">Olympus ORF råbilete</comment>
+ <comment xml:lang="oc">imatge brut ORF Olympus</comment>
+ <comment xml:lang="pl">Surowy obraz Olympus ORF</comment>
+ <comment xml:lang="pt">imagem em bruto Olympus ORF</comment>
+ <comment xml:lang="pt_BR">Imagem bruta ORF da Olympus</comment>
+ <comment xml:lang="ro">Imagine brută Olympus ORF</comment>
+ <comment xml:lang="ru">Необработанное изображение Olympus ORF</comment>
+ <comment xml:lang="sk">Surový obrázok Olympus ORF</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Olympus ORF</comment>
+ <comment xml:lang="sq">Figurë raw Olympus ORF</comment>
+ <comment xml:lang="sr">Олимпус ОРФ сирова слика</comment>
+ <comment xml:lang="sv">Olympus ORF-råbild</comment>
+ <comment xml:lang="tr">Olympus ORF ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив ORF Olympus</comment>
+ <comment xml:lang="vi">Ảnh thô Olympus ORF</comment>
+ <comment xml:lang="zh_CN">奥林巴斯 ORF 原始图像</comment>
+ <comment xml:lang="zh_TW">Olympus ORF 原生影像</comment>
+ <acronym>ORF</acronym>
+ <expanded-acronym>Olympus Raw Format</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <magic priority="50">
+
+
+
+
+
+ <match value="IIRO\x08\x00\x00\x00" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.orf"/>
+ </mime-type>
+ <mime-type type="image/x-panasonic-rw">
+ <comment>Panasonic raw image</comment>
+ <comment xml:lang="ar">صورة Panasonic خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Panasonic</comment>
+ <comment xml:lang="bg">Изображение — Panasonic raw</comment>
+ <comment xml:lang="ca">imatge en cru de Panasonic</comment>
+ <comment xml:lang="cs">surový obrázek Panasonic</comment>
+ <comment xml:lang="da">Panasonicråbillede (raw)</comment>
+ <comment xml:lang="de">Panasonic-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Panasonic</comment>
+ <comment xml:lang="en_GB">Panasonic raw image</comment>
+ <comment xml:lang="es">imagen en bruto de Panasonic</comment>
+ <comment xml:lang="eu">Panasonic irudi gordina</comment>
+ <comment xml:lang="fi">Panasonic-raakakuva</comment>
+ <comment xml:lang="fo">Panasonic rámynd</comment>
+ <comment xml:lang="fr">image brute Panasonic</comment>
+ <comment xml:lang="ga">amhíomhá Panasonic</comment>
+ <comment xml:lang="gl">imaxe en bruto de Panasonic</comment>
+ <comment xml:lang="he">תמונה גולמית של Panasonic</comment>
+ <comment xml:lang="hr">Panasonic osnovna slika</comment>
+ <comment xml:lang="hu">Panasonic nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute Panasonic</comment>
+ <comment xml:lang="id">Citra mentah Panasonic</comment>
+ <comment xml:lang="it">Immagine raw Panasonic</comment>
+ <comment xml:lang="ja">Panasonic raw 画像</comment>
+ <comment xml:lang="kk">Panasonic өңделмеген суреті</comment>
+ <comment xml:lang="ko">파나소닉 RAW 사진</comment>
+ <comment xml:lang="lt">Panasonic neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Panasonic jēlattēls</comment>
+ <comment xml:lang="nb">Panasonic raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Panasonic-beeld</comment>
+ <comment xml:lang="nn">Panasonic råbilete</comment>
+ <comment xml:lang="oc">imatge brut Panasonic</comment>
+ <comment xml:lang="pl">Obraz raw Panasonic</comment>
+ <comment xml:lang="pt">imagem em bruto Panasonic</comment>
+ <comment xml:lang="pt_BR">Imagem bruta da Panasonic</comment>
+ <comment xml:lang="ro">Imagine brută Panasonic</comment>
+ <comment xml:lang="ru">Необработанное изображение Panasonic</comment>
+ <comment xml:lang="sk">Surový obrázok Panasonic</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Panasonic</comment>
+ <comment xml:lang="sq">Figurë raw Panasonic</comment>
+ <comment xml:lang="sr">Панасоник сирова слика</comment>
+ <comment xml:lang="sv">Panasonic-råbild</comment>
+ <comment xml:lang="tr">Panasonic ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив Panasonic</comment>
+ <comment xml:lang="vi">Ảnh thô Panasonic</comment>
+ <comment xml:lang="zh_CN">松下原始图像</comment>
+ <comment xml:lang="zh_TW">Panasonic 原生影像</comment>
+ <sub-class-of type="image/x-dcraw"/>
+ <magic priority="50">
+
+ <match value="IIU\x00\x08\x00\x00\x00" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.raw"/>
+ <alias type="image/x-panasonic-raw"/>
+ </mime-type>
+ <mime-type type="image/x-panasonic-rw2">
+ <comment>Panasonic raw2 image</comment>
+ <comment xml:lang="bg">Изображение — Panasonic raw2</comment>
+ <comment xml:lang="ca">imatge «RAW2» de Panasonic</comment>
+ <comment xml:lang="cs">surový obrázek Panasonic raw2</comment>
+ <comment xml:lang="da">Panasonic-rå2-billede (raw)</comment>
+ <comment xml:lang="de">Panasonic raw2-Bild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Panasonic (raw2)</comment>
+ <comment xml:lang="en_GB">Panasonic raw2 image</comment>
+ <comment xml:lang="es">imagen en bruto raw2 de Panasonic</comment>
+ <comment xml:lang="eu">Panasonic raw2 irudia</comment>
+ <comment xml:lang="fi">Panasonic raw2 -kuva</comment>
+ <comment xml:lang="fr">image raw2 Panasonic</comment>
+ <comment xml:lang="ga">íomhá raw2 Panasonic</comment>
+ <comment xml:lang="gl">imaxe en bruto raw2 de Panasonic</comment>
+ <comment xml:lang="he">תמונת raw2 של Panasonic</comment>
+ <comment xml:lang="hr">Panasonic raw2 image</comment>
+ <comment xml:lang="hu">Panasonic raw2 kép</comment>
+ <comment xml:lang="ia">Imagine raw2 Panasonic</comment>
+ <comment xml:lang="id">Image Panasonic raw2</comment>
+ <comment xml:lang="it">Immagine raw2 Panasonic</comment>
+ <comment xml:lang="ja">Panasonic raw2 画像</comment>
+ <comment xml:lang="kk">Panasonic raw2 суреті</comment>
+ <comment xml:lang="ko">파나소닉 RAW2 사진</comment>
+ <comment xml:lang="lv">Panasonic raw2 jēlattēls</comment>
+ <comment xml:lang="nl">Panasonic raw2 image</comment>
+ <comment xml:lang="oc">imatge raw2 Panasonic</comment>
+ <comment xml:lang="pl">Obraz raw2 Panasonic</comment>
+ <comment xml:lang="pt">imagem em bruto Panasonic</comment>
+ <comment xml:lang="pt_BR">Imagem raw2 da Panasonic</comment>
+ <comment xml:lang="ru">Необработанное изображение Panasonic raw2</comment>
+ <comment xml:lang="sk">Surový obrázok Panasonic raw2</comment>
+ <comment xml:lang="sl">Slikovna datoteka Panasonic raw2</comment>
+ <comment xml:lang="sr">Панасоник сирова2 слика</comment>
+ <comment xml:lang="sv">Panasonic raw2-bild</comment>
+ <comment xml:lang="tr">Panasonic raw2 görüntüsü</comment>
+ <comment xml:lang="uk">зображення формату raw2 Panasonic</comment>
+ <comment xml:lang="zh_CN">松下 raw2 图像</comment>
+ <comment xml:lang="zh_TW">Panasonic raw2 影像</comment>
+ <sub-class-of type="image/x-dcraw"/>
+ <magic priority="50">
+
+ <match value="IIU\x00\x18\x00\x00\x00" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.rw2"/>
+ <alias type="image/x-panasonic-raw2"/>
+ </mime-type>
+ <mime-type type="image/x-pentax-pef">
+ <comment>Pentax PEF raw image</comment>
+ <comment xml:lang="ar">صورة Pentax PEF خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Pentax PEF</comment>
+ <comment xml:lang="bg">Изображение — Pentax PEF raw</comment>
+ <comment xml:lang="ca">imatge en cru de Pentax PEF</comment>
+ <comment xml:lang="cs">surový obrázek Pentax PEF</comment>
+ <comment xml:lang="da">Pentax PEF-råbillede</comment>
+ <comment xml:lang="de">Pentax-PEF-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Pentax PEF</comment>
+ <comment xml:lang="en_GB">Pentax PEF raw image</comment>
+ <comment xml:lang="es">imagen en bruto PEF de Pentax</comment>
+ <comment xml:lang="eu">Pentax PEF irudi gordina</comment>
+ <comment xml:lang="fi">Pentax-PEF-raakakuva</comment>
+ <comment xml:lang="fo">Pentax PEF rámynd</comment>
+ <comment xml:lang="fr">image brute PEF Pentax</comment>
+ <comment xml:lang="ga">amhíomhá Pentax PEF</comment>
+ <comment xml:lang="gl">imaxe en bruto PEF de Pentax</comment>
+ <comment xml:lang="he">תמונה גולמית של Pentax PEF</comment>
+ <comment xml:lang="hr">Pentax PEF osnovna slika</comment>
+ <comment xml:lang="hu">Pentax PEF nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute Pentax PEF</comment>
+ <comment xml:lang="id">Citra mentah Pentax PEF</comment>
+ <comment xml:lang="it">Immagine raw Pentax PEF</comment>
+ <comment xml:lang="ja">Pentax PEF raw 画像</comment>
+ <comment xml:lang="kk">Pentax PEF өңделмеген суреті</comment>
+ <comment xml:lang="ko">펜탁스 PEF RAW 사진</comment>
+ <comment xml:lang="lt">Pentax PEF neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Pentax PEF jēlattēls</comment>
+ <comment xml:lang="nb">Pentax PEF raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Pentax PEF-beeld</comment>
+ <comment xml:lang="nn">Pentax PEF råbilete</comment>
+ <comment xml:lang="oc">imatge brut PEF Pentax</comment>
+ <comment xml:lang="pl">Surowy obraz Pentax PEF</comment>
+ <comment xml:lang="pt">imagem em bruto Pentax PEF</comment>
+ <comment xml:lang="pt_BR">Imagem bruta PEF da Pentax</comment>
+ <comment xml:lang="ro">Imagine brută Pentax PEF</comment>
+ <comment xml:lang="ru">Необработанное изображение Pentax PEF</comment>
+ <comment xml:lang="sk">Surový obrázok Pentax PEF</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Pentax PEF</comment>
+ <comment xml:lang="sq">Figurë raw Pentax PEF</comment>
+ <comment xml:lang="sr">Пентакс ПЕФ сирова слика</comment>
+ <comment xml:lang="sv">Pentax PEF-råbild</comment>
+ <comment xml:lang="tr">Pentax PEF ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив PEF Pentax</comment>
+ <comment xml:lang="vi">Ảnh thô Pentax PEF</comment>
+ <comment xml:lang="zh_CN">宾得 PEF 原始图像</comment>
+ <comment xml:lang="zh_TW">Pentax PEF 原生影像</comment>
+ <acronym>PEF</acronym>
+ <expanded-acronym>Pentax Electronic Format</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <sub-class-of type="image/tiff"/>
+ <glob pattern="*.pef"/>
+ </mime-type>
+ <mime-type type="image/x-sigma-x3f">
+ <comment>Sigma X3F raw image</comment>
+ <comment xml:lang="ar">صورة Sigma X3F خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Sigma X3F</comment>
+ <comment xml:lang="bg">Изображение — Sigma X3F raw</comment>
+ <comment xml:lang="ca">imatge en cru de Sigma X3F</comment>
+ <comment xml:lang="cs">surový obrázek Sigma X3F</comment>
+ <comment xml:lang="da">Sigma X3F-råbillede</comment>
+ <comment xml:lang="de">Sigma-X3F-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Sigma X3F</comment>
+ <comment xml:lang="en_GB">Sigma X3F raw image</comment>
+ <comment xml:lang="es">imagen en bruto X3F de Sigma</comment>
+ <comment xml:lang="eu">Sigma X3F irudi gordina</comment>
+ <comment xml:lang="fi">Sigma-X3F-raakakuva</comment>
+ <comment xml:lang="fo">Sigma X3F rámynd</comment>
+ <comment xml:lang="fr">image brute X3F Sigma</comment>
+ <comment xml:lang="ga">amhíomhá Sigma X3F</comment>
+ <comment xml:lang="gl">imaxe en bruto X3F de Sigma</comment>
+ <comment xml:lang="he">תמונה גולמית של Sigma X3F</comment>
+ <comment xml:lang="hr">Sigma X3F osnovna slika</comment>
+ <comment xml:lang="hu">Sigma XF3 nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute Sigma X3F</comment>
+ <comment xml:lang="id">Citra mentah Sigma X3F</comment>
+ <comment xml:lang="it">Immagine raw Sigma X3F</comment>
+ <comment xml:lang="ja">Sigma X3F raw 画像</comment>
+ <comment xml:lang="kk">Sigma X3F өңделмеген суреті</comment>
+ <comment xml:lang="ko">시그마 X3F RAW 사진</comment>
+ <comment xml:lang="lt">Sigma X3F neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Sigma X3F jēlattēls</comment>
+ <comment xml:lang="nb">Sigma X3F raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Sigma X3F-beeld</comment>
+ <comment xml:lang="nn">Sigma X3F råbilete</comment>
+ <comment xml:lang="oc">imatge brut X3F Sigma</comment>
+ <comment xml:lang="pl">Surowy obraz X3F Sigma</comment>
+ <comment xml:lang="pt">imagem em bruto Sigma X3F</comment>
+ <comment xml:lang="pt_BR">Imagem bruta X3F da Sigma</comment>
+ <comment xml:lang="ro">Imagine brută Sigma X3F</comment>
+ <comment xml:lang="ru">Необработанное изображение Sigma X3F</comment>
+ <comment xml:lang="sk">Surový obrázok Sigma X3F</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Sigma X3F</comment>
+ <comment xml:lang="sq">Fifurë raw Sigma X3F</comment>
+ <comment xml:lang="sr">Сигма Икс3Ф сирова слика</comment>
+ <comment xml:lang="sv">Sigma X3F-råbild</comment>
+ <comment xml:lang="tr">Sigma X3F ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив X3F Sigma</comment>
+ <comment xml:lang="vi">Ảnh thô Sigma X3F</comment>
+ <comment xml:lang="zh_CN">适马 X3F 原始图像</comment>
+ <comment xml:lang="zh_TW">Sigma X3F 原生影像</comment>
+ <acronym>X3F</acronym>
+ <expanded-acronym>X3 Foveon</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <magic priority="50">
+
+ <match value="FOVb" type="string" offset="0">
+
+ <match value="0x00FF00FF" type="little32" offset="4" mask="0xFF00FF00"/>
+ </match>
+ </magic>
+ <glob pattern="*.x3f"/>
+ </mime-type>
+ <mime-type type="image/x-sony-srf">
+ <comment>Sony SRF raw image</comment>
+ <comment xml:lang="ar">صورة Sony SRF خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Sony SRF</comment>
+ <comment xml:lang="bg">Изображение — Sony SRF raw</comment>
+ <comment xml:lang="ca">imatge en cru de Sony SRF</comment>
+ <comment xml:lang="cs">surový obrázek Sony SRF</comment>
+ <comment xml:lang="da">Sony SRF-råbillede</comment>
+ <comment xml:lang="de">Sony-SRF-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Sony SRF</comment>
+ <comment xml:lang="en_GB">Sony SRF raw image</comment>
+ <comment xml:lang="es">imagen en bruto SRF de Sony</comment>
+ <comment xml:lang="eu">Sony SRF irudi gordina</comment>
+ <comment xml:lang="fi">Sony-SRF-raakakuva</comment>
+ <comment xml:lang="fo">Sony SRF rámynd</comment>
+ <comment xml:lang="fr">image brute SRF Sony</comment>
+ <comment xml:lang="ga">amhíomhá Sony SRF</comment>
+ <comment xml:lang="gl">imaxe en bruto SRF de sony</comment>
+ <comment xml:lang="he">תמונה גולמית של Sony SRF</comment>
+ <comment xml:lang="hr">Sony SRF osnovna slika</comment>
+ <comment xml:lang="hu">Sony SRF nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute Sony SRF</comment>
+ <comment xml:lang="id">Citra mentah Sony SRF</comment>
+ <comment xml:lang="it">Immagine raw Sony SRF</comment>
+ <comment xml:lang="ja">Sony SRF raw 画像</comment>
+ <comment xml:lang="kk">Sony SRF өңделмеген суреті</comment>
+ <comment xml:lang="ko">소니 SRF RAW 사진</comment>
+ <comment xml:lang="lt">Sony SRF neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Sony SRF jēlattēls</comment>
+ <comment xml:lang="nb">Sony SRF raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Sony SRF-beeld</comment>
+ <comment xml:lang="nn">Sony SRF råbilete</comment>
+ <comment xml:lang="oc">imatge brut SRF Sony</comment>
+ <comment xml:lang="pl">Surowy obraz SRF Sony</comment>
+ <comment xml:lang="pt">imagem em bruto Sony SRF</comment>
+ <comment xml:lang="pt_BR">Imagem bruta SRF da Sony</comment>
+ <comment xml:lang="ro">Imagine brută Sony SRF</comment>
+ <comment xml:lang="ru">Необработанное изображение Sony SRF</comment>
+ <comment xml:lang="sk">Surový obrázok Sony SRF</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Sony SRF</comment>
+ <comment xml:lang="sq">Figurë raw Sony SRF</comment>
+ <comment xml:lang="sr">Сони СРФ сирова слика</comment>
+ <comment xml:lang="sv">Sony SRF-råbild</comment>
+ <comment xml:lang="tr">Sony SRF ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив SRF Sony</comment>
+ <comment xml:lang="vi">Ảnh thô Sony SRF</comment>
+ <comment xml:lang="zh_CN">索尼 SRF 原始映像</comment>
+ <comment xml:lang="zh_TW">Sony SRF 原生影像</comment>
+ <acronym>SRF</acronym>
+ <expanded-acronym>Sony Raw Format</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <sub-class-of type="image/tiff"/>
+ <glob pattern="*.srf"/>
+ </mime-type>
+ <mime-type type="image/x-sony-sr2">
+ <comment>Sony SR2 raw image</comment>
+ <comment xml:lang="ar">صورة Sony SR2 خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Sony SR2</comment>
+ <comment xml:lang="bg">Изображение — Sony SR2 raw</comment>
+ <comment xml:lang="ca">imatge en cru de Sony SR2</comment>
+ <comment xml:lang="cs">surový obrázek Sony SR2</comment>
+ <comment xml:lang="da">Sony SR2-råbillede</comment>
+ <comment xml:lang="de">Sony-SR2-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Sony SR2</comment>
+ <comment xml:lang="en_GB">Sony SR2 raw image</comment>
+ <comment xml:lang="es">imagen en bruto SR2 de Sony</comment>
+ <comment xml:lang="eu">Sony SR2 irudi gordina</comment>
+ <comment xml:lang="fi">Sony-SR2-raakakuva</comment>
+ <comment xml:lang="fo">Sony SR2 rámynd</comment>
+ <comment xml:lang="fr">image brute SR2 Sony</comment>
+ <comment xml:lang="ga">amhíomhá Sony SR2</comment>
+ <comment xml:lang="gl">imaxe en bruto SR2 de sony</comment>
+ <comment xml:lang="he">תמונה גולמית של Sony SR2</comment>
+ <comment xml:lang="hr">Sony SR2 osnovna slika</comment>
+ <comment xml:lang="hu">Sony SR2 nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute Sony SR2</comment>
+ <comment xml:lang="id">Citra mentah Sony SR2</comment>
+ <comment xml:lang="it">Immagine raw Sony SR2</comment>
+ <comment xml:lang="ja">Sony SR2 raw 画像</comment>
+ <comment xml:lang="kk">Sony SR2 өңделмеген суреті</comment>
+ <comment xml:lang="ko">소니 SR2 RAW 사진</comment>
+ <comment xml:lang="lt">Sony SR2 neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Sony SR2 jēlattēls</comment>
+ <comment xml:lang="nb">Sony SR2 raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Sony SR2-beeld</comment>
+ <comment xml:lang="nn">Sony SR2 råbilete</comment>
+ <comment xml:lang="oc">imatge brut SR2 Sony</comment>
+ <comment xml:lang="pl">Surowy obraz SR2 Sony</comment>
+ <comment xml:lang="pt">imagem em bruto Sony SR2</comment>
+ <comment xml:lang="pt_BR">Imagem bruta SR2 da Sony</comment>
+ <comment xml:lang="ro">Imagine brută Sony SR2</comment>
+ <comment xml:lang="ru">Необработанное изображение Sony SR2</comment>
+ <comment xml:lang="sk">Surový obrázok Sony SR2</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Sony SR2</comment>
+ <comment xml:lang="sq">Figurë raw Sony SR2</comment>
+ <comment xml:lang="sr">Сони СР2 сирова слика</comment>
+ <comment xml:lang="sv">Sony SR2-råbild</comment>
+ <comment xml:lang="tr">Sony SR2 ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив SR2 Sony</comment>
+ <comment xml:lang="vi">Ảnh thô Sony SR2</comment>
+ <comment xml:lang="zh_CN">索尼 SR2 原始映像</comment>
+ <comment xml:lang="zh_TW">Sony SR2 原生影像</comment>
+ <acronym>SR2</acronym>
+ <expanded-acronym>Sony Raw format 2</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <sub-class-of type="image/tiff"/>
+ <glob pattern="*.sr2"/>
+ </mime-type>
+ <mime-type type="image/x-sony-arw">
+ <comment>Sony ARW raw image</comment>
+ <comment xml:lang="ar">صورة Sony ARW خامة</comment>
+ <comment xml:lang="be@latin">Suvoraja vyjava Sony ARW</comment>
+ <comment xml:lang="bg">Изображение — Sony ARW raw</comment>
+ <comment xml:lang="ca">imatge en cru de Sony ARW</comment>
+ <comment xml:lang="cs">surový obrázek Sony ARW</comment>
+ <comment xml:lang="da">Sony ARW-råbillede</comment>
+ <comment xml:lang="de">Sony-ARW-Rohbild</comment>
+ <comment xml:lang="el">Ανεπεξέργαστη εικόνα Sony ARW</comment>
+ <comment xml:lang="en_GB">Sony ARW raw image</comment>
+ <comment xml:lang="es">imagen en bruto ARW de Sony</comment>
+ <comment xml:lang="eu">Sony ARW irudi gordina</comment>
+ <comment xml:lang="fi">Sony-ARW-raakakuva</comment>
+ <comment xml:lang="fo">Sony ARW rámynd</comment>
+ <comment xml:lang="fr">image brute ARW Sony</comment>
+ <comment xml:lang="ga">amhíomhá Sony ARW</comment>
+ <comment xml:lang="gl">imaxe en bruto ARW de sony</comment>
+ <comment xml:lang="he">תמונה גולמית של Sony ARW</comment>
+ <comment xml:lang="hr">Sony ARW osnovna slika</comment>
+ <comment xml:lang="hu">Sony ARW nyers kép</comment>
+ <comment xml:lang="ia">Imagine brute Sony ARW</comment>
+ <comment xml:lang="id">Citra mentah Sony ARW</comment>
+ <comment xml:lang="it">Immagine raw Sony ARW</comment>
+ <comment xml:lang="ja">Sony ARW raw 画像</comment>
+ <comment xml:lang="kk">Sony ARW өңделмеген суреті</comment>
+ <comment xml:lang="ko">소니 ARW RAW 사진</comment>
+ <comment xml:lang="lt">Sony ARW neapdorotas paveikslėlis</comment>
+ <comment xml:lang="lv">Sony ARW jēlattēls</comment>
+ <comment xml:lang="nb">Sony ARW raw-bilde</comment>
+ <comment xml:lang="nl">onbewerkt Sony ARW-beeld</comment>
+ <comment xml:lang="nn">Sony ARW råbilete</comment>
+ <comment xml:lang="oc">imatge brut ARW Sony</comment>
+ <comment xml:lang="pl">Surowy obraz ARW Sony</comment>
+ <comment xml:lang="pt">imagem em bruto Sony ARW</comment>
+ <comment xml:lang="pt_BR">Imagem bruta ARW da Sony</comment>
+ <comment xml:lang="ro">Imagine brută Sony ARW</comment>
+ <comment xml:lang="ru">Необработанное изображение Sony ARW</comment>
+ <comment xml:lang="sk">Surový obrázok Sony ARW</comment>
+ <comment xml:lang="sl">Surova slikovna datoteka Sony ARW</comment>
+ <comment xml:lang="sq">Figurë raw Sony ARW</comment>
+ <comment xml:lang="sr">Сони АРВ сирова слика</comment>
+ <comment xml:lang="sv">Sony ARW-råbild</comment>
+ <comment xml:lang="tr">Sony ARW ham görüntüsü</comment>
+ <comment xml:lang="uk">цифровий негатив ARW Sony</comment>
+ <comment xml:lang="vi">Ảnh thô Sony ARW</comment>
+ <comment xml:lang="zh_CN">索尼 ARW 原始映像</comment>
+ <comment xml:lang="zh_TW">Sony ARW 原生影像</comment>
+ <acronym>ARW</acronym>
+ <expanded-acronym>Alpha Raw format</expanded-acronym>
+ <sub-class-of type="image/x-dcraw"/>
+ <sub-class-of type="image/tiff"/>
+ <glob pattern="*.arw"/>
+ </mime-type>
+ <mime-type type="image/png">
+ <comment>PNG image</comment>
+ <comment xml:lang="ar">صورة PNG</comment>
+ <comment xml:lang="az">PNG rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava PNG</comment>
+ <comment xml:lang="bg">Изображение — PNG</comment>
+ <comment xml:lang="ca">imatge PNG</comment>
+ <comment xml:lang="cs">obrázek PNG</comment>
+ <comment xml:lang="cy">Delwedd PNG</comment>
+ <comment xml:lang="da">PNG-billede</comment>
+ <comment xml:lang="de">PNG-Bild</comment>
+ <comment xml:lang="el">Εικόνα PNG</comment>
+ <comment xml:lang="en_GB">PNG image</comment>
+ <comment xml:lang="eo">PNG-bildo</comment>
+ <comment xml:lang="es">imagen PNG</comment>
+ <comment xml:lang="eu">PNG irudia</comment>
+ <comment xml:lang="fi">PNG-kuva</comment>
+ <comment xml:lang="fo">PNG mynd</comment>
+ <comment xml:lang="fr">image PNG</comment>
+ <comment xml:lang="ga">íomhá PNG</comment>
+ <comment xml:lang="gl">imaxe PNG</comment>
+ <comment xml:lang="he">תמונת PNG</comment>
+ <comment xml:lang="hr">PNG slika</comment>
+ <comment xml:lang="hu">PNG-kép</comment>
+ <comment xml:lang="ia">Imagine PNG</comment>
+ <comment xml:lang="id">Citra PNG</comment>
+ <comment xml:lang="it">Immagine PNG</comment>
+ <comment xml:lang="ja">PNG 画像</comment>
+ <comment xml:lang="kk">PNG суреті</comment>
+ <comment xml:lang="ko">PNG 그림</comment>
+ <comment xml:lang="lt">PNG paveikslėlis</comment>
+ <comment xml:lang="lv">PNG attēls</comment>
+ <comment xml:lang="ms">Imej PNG</comment>
+ <comment xml:lang="nb">PNG-bilde</comment>
+ <comment xml:lang="nl">PNG-afbeelding</comment>
+ <comment xml:lang="nn">PNG-bilete</comment>
+ <comment xml:lang="oc">imatge PNG</comment>
+ <comment xml:lang="pl">Obraz PNG</comment>
+ <comment xml:lang="pt">imagem PNG</comment>
+ <comment xml:lang="pt_BR">Imagem PNG</comment>
+ <comment xml:lang="ro">Imagine PNG</comment>
+ <comment xml:lang="ru">Изображение PNG</comment>
+ <comment xml:lang="sk">Obrázok PNG</comment>
+ <comment xml:lang="sl">Slikovna datoteka PNG</comment>
+ <comment xml:lang="sq">Figurë PNG</comment>
+ <comment xml:lang="sr">ПНГ слика</comment>
+ <comment xml:lang="sv">PNG-bild</comment>
+ <comment xml:lang="tr">PNG görüntüsü</comment>
+ <comment xml:lang="uk">зображення PNG</comment>
+ <comment xml:lang="vi">Ảnh PNG</comment>
+ <comment xml:lang="zh_CN">PNG 图像</comment>
+ <comment xml:lang="zh_TW">PNG 影像</comment>
+ <magic priority="50">
+ <match value="\x89PNG" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.png"/>
+ </mime-type>
+ <mime-type type="image/rle">
+ <comment>Run Length Encoded bitmap image</comment>
+ <comment xml:lang="ar">تشغيل صورة نقطية طولية الترميز</comment>
+ <comment xml:lang="be@latin">Bitmapnaja vyjava, zakadavanaja ŭ Run Length</comment>
+ <comment xml:lang="bg">Изображение — RLE Bitmap</comment>
+ <comment xml:lang="ca">imatge de mapa de bits «Run Lenght Encoded»</comment>
+ <comment xml:lang="cs">obrázek bitové mapy Run Length Encoded</comment>
+ <comment xml:lang="da">Run Length Encoded-bitmapbillede</comment>
+ <comment xml:lang="de">Lauflängenkodiertes Bitmap-Bild</comment>
+ <comment xml:lang="el">Εικόνα bitmap κωδικοποιημένου μήκος εκτέλεσης</comment>
+ <comment xml:lang="en_GB">Run Length Encoded bitmap image</comment>
+ <comment xml:lang="es">mapa de bits con codificación del tamaño durante la ejecución</comment>
+ <comment xml:lang="eu">'Run Lenght Encoded' bitmap irudia</comment>
+ <comment xml:lang="fi">RLE-koodattu bittikartta</comment>
+ <comment xml:lang="fr">image matricielle Run Length Encoded</comment>
+ <comment xml:lang="ga">íomhá ghiotánmhapach ionchódaithe fad reatha</comment>
+ <comment xml:lang="gl">mapa de bits con codificación do tamaño durante a execución</comment>
+ <comment xml:lang="he">מקודד מפת סיביות של Run Length</comment>
+ <comment xml:lang="hr">Run Length Encoded bitmap slika</comment>
+ <comment xml:lang="hu">Run Length Encoded bitkép</comment>
+ <comment xml:lang="ia">Imagine raster in codification Run-Length</comment>
+ <comment xml:lang="id">Citra peta bit Run Length Encoded</comment>
+ <comment xml:lang="it">Immagine bitmap RLE (Run Length Encoded)</comment>
+ <comment xml:lang="ja">ランレングス符号化ビットマップ画像</comment>
+ <comment xml:lang="kk">RLE сығылған растрлік суреті</comment>
+ <comment xml:lang="ko">RLE 인코딩된 비트맵 그림</comment>
+ <comment xml:lang="lt">Run Length Encoded rastrinis paveikslėlis</comment>
+ <comment xml:lang="lv">Secīgo atkārtojumu kodēts bitkartes attēls</comment>
+ <comment xml:lang="nb">Run Length Encoded bitmap bilde</comment>
+ <comment xml:lang="nl">RLE-gecodeerde bitmap-afbeelding</comment>
+ <comment xml:lang="nn">Run Length Encoded punktgrafikk</comment>
+ <comment xml:lang="oc">imatge matriciala Run Length Encoded</comment>
+ <comment xml:lang="pl">Obraz bitmapy RLE</comment>
+ <comment xml:lang="pt">mapa de bitas Run Length Encoded</comment>
+ <comment xml:lang="pt_BR">Classe de comprimento imagem bitmap codificada</comment>
+ <comment xml:lang="ro">Imagine bitmap codată RLE</comment>
+ <comment xml:lang="ru">Растровое изображение, сжатое RLE</comment>
+ <comment xml:lang="sk">Bitmapový obrázok Run Length Encoded</comment>
+ <comment xml:lang="sl">Zaporedno kodirana bitna slika (RLE)</comment>
+ <comment xml:lang="sq">Figurë bitmap RLE (Run Length Encoded)</comment>
+ <comment xml:lang="sr">битмап слика кодирана дужином скупине</comment>
+ <comment xml:lang="sv">Körlängdskodad bitmappbild</comment>
+ <comment xml:lang="tr">Run Length Encoded bit eşlem görüntüsü</comment>
+ <comment xml:lang="uk">растрове зображення RLE</comment>
+ <comment xml:lang="vi">Ảnh mảng mã hóa chiều dài chạy (RLE)</comment>
+ <comment xml:lang="zh_CN">游程编码位图</comment>
+ <comment xml:lang="zh_TW">Run Length Encoded 點陣影像</comment>
+ <glob pattern="*.rle"/>
+ </mime-type>
+ <mime-type type="image/svg+xml">
+ <comment>SVG image</comment>
+ <comment xml:lang="ar">صورة SVG</comment>
+ <comment xml:lang="be@latin">Vyjava SVG</comment>
+ <comment xml:lang="bg">Изображение — SVG</comment>
+ <comment xml:lang="ca">imatge SVG</comment>
+ <comment xml:lang="cs">obrázek SVG</comment>
+ <comment xml:lang="da">SVG-billede</comment>
+ <comment xml:lang="de">SVG-Bild</comment>
+ <comment xml:lang="el">Εικόνα SVG</comment>
+ <comment xml:lang="en_GB">SVG image</comment>
+ <comment xml:lang="eo">SVG-bildo</comment>
+ <comment xml:lang="es">imagen SVG</comment>
+ <comment xml:lang="eu">SVG irudia</comment>
+ <comment xml:lang="fi">SVG-kuva</comment>
+ <comment xml:lang="fo">SVG mynd</comment>
+ <comment xml:lang="fr">image SVG</comment>
+ <comment xml:lang="ga">íomhá SVG</comment>
+ <comment xml:lang="gl">imaxe SVG</comment>
+ <comment xml:lang="he">תמונת SVG</comment>
+ <comment xml:lang="hr">SVG slika</comment>
+ <comment xml:lang="hu">SVG kép</comment>
+ <comment xml:lang="ia">Imagine SVG</comment>
+ <comment xml:lang="id">Citra SVG</comment>
+ <comment xml:lang="it">Immagine SVG</comment>
+ <comment xml:lang="ja">SVG 画像</comment>
+ <comment xml:lang="kk">SVG суреті</comment>
+ <comment xml:lang="ko">SVG 그림</comment>
+ <comment xml:lang="lt">SVG paveikslėlis</comment>
+ <comment xml:lang="lv">SVG attēls</comment>
+ <comment xml:lang="nb">SVG-bilde</comment>
+ <comment xml:lang="nl">SVG-afbeelding</comment>
+ <comment xml:lang="nn">SVG-bilete</comment>
+ <comment xml:lang="oc">imatge SVG</comment>
+ <comment xml:lang="pl">Obraz SVG</comment>
+ <comment xml:lang="pt">imagem SVG</comment>
+ <comment xml:lang="pt_BR">Imagem SVG</comment>
+ <comment xml:lang="ro">Imagine SVG</comment>
+ <comment xml:lang="ru">Изображение SVG</comment>
+ <comment xml:lang="sk">Obrázok SVG</comment>
+ <comment xml:lang="sl">Slikovna vektorska datoteka SVG</comment>
+ <comment xml:lang="sq">Figurë SVG</comment>
+ <comment xml:lang="sr">СВГ слика</comment>
+ <comment xml:lang="sv">SVG-bild</comment>
+ <comment xml:lang="tr">SVG görüntüsü</comment>
+ <comment xml:lang="uk">зображення SVG</comment>
+ <comment xml:lang="vi">Ảnh SVG</comment>
+ <comment xml:lang="zh_CN">SVG 图像</comment>
+ <comment xml:lang="zh_TW">SVG 影像</comment>
+ <acronym>SVG</acronym>
+ <expanded-acronym>Scalable Vector Graphics</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <magic priority="80">
+ <match value="&lt;!DOCTYPE svg" type="string" offset="0:256"/>
+ <match value="&lt;svg" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.svg"/>
+ <root-XML namespaceURI="http://www.w3.org/2000/svg" localName="svg"/>
+ </mime-type>
+ <mime-type type="image/svg+xml-compressed">
+ <comment>compressed SVG image</comment>
+ <comment xml:lang="ar">صورة SVG مضغوطة</comment>
+ <comment xml:lang="be@latin">skampresavanaja vyjava SVG</comment>
+ <comment xml:lang="bg">Изображение — SVG, компресирано</comment>
+ <comment xml:lang="ca">imatge SVG amb compressió</comment>
+ <comment xml:lang="cs">komprimovaný obrázek SVG</comment>
+ <comment xml:lang="da">SVG-komprimeret billede</comment>
+ <comment xml:lang="de">Komprimiertes SVG-Bild</comment>
+ <comment xml:lang="el">Συμπιεσμένη εικόνα SVG</comment>
+ <comment xml:lang="en_GB">compressed SVG image</comment>
+ <comment xml:lang="es">imagen SVG comprimida</comment>
+ <comment xml:lang="eu">konprimitutako SVG irudia</comment>
+ <comment xml:lang="fi">pakattu SVG-kuva</comment>
+ <comment xml:lang="fo">stappað SVG mynd</comment>
+ <comment xml:lang="fr">image SVG compressée</comment>
+ <comment xml:lang="ga">íomhá SVG comhbhrúite</comment>
+ <comment xml:lang="gl">imaxe SVG comprimida</comment>
+ <comment xml:lang="he">תמונת SVG מכווצת</comment>
+ <comment xml:lang="hr">Sažeta SVG slika</comment>
+ <comment xml:lang="hu">tömörített SVG kép</comment>
+ <comment xml:lang="ia">Imagine SVG comprimite</comment>
+ <comment xml:lang="id">Citra SVG terkompresi</comment>
+ <comment xml:lang="it">Immagine SVG compressa</comment>
+ <comment xml:lang="ja">圧縮 SVG 画像</comment>
+ <comment xml:lang="kk">сығылған SVG суреті</comment>
+ <comment xml:lang="ko">압축된 SVG 그림</comment>
+ <comment xml:lang="lt">suglaudintas SVG paveikslėlis</comment>
+ <comment xml:lang="lv">saspiests SVG attēls</comment>
+ <comment xml:lang="nb">komprimert SVG-bilde</comment>
+ <comment xml:lang="nl">ingepakte SVG-afbeelding</comment>
+ <comment xml:lang="nn">komprimert SVG-bilete</comment>
+ <comment xml:lang="oc">imatge SVG compressat</comment>
+ <comment xml:lang="pl">Skompresowany obraz SVG</comment>
+ <comment xml:lang="pt">imagem SVG comprimida</comment>
+ <comment xml:lang="pt_BR">Imagem SVG compactada</comment>
+ <comment xml:lang="ro">imagine comprimată SVG</comment>
+ <comment xml:lang="ru">Сжатое изображение SVG</comment>
+ <comment xml:lang="sk">Komprimovaný obrázok SVG</comment>
+ <comment xml:lang="sl">Slikovna datoteka SVG (stisnjena)</comment>
+ <comment xml:lang="sq">Figurë SVG e kompresuar</comment>
+ <comment xml:lang="sr">запакована СВГ слика</comment>
+ <comment xml:lang="sv">komprimerad SVG-bild</comment>
+ <comment xml:lang="tr">sıkıştırılmış SVG görüntüsü</comment>
+ <comment xml:lang="uk">стиснене зображення SVG</comment>
+ <comment xml:lang="vi">ảnh SVG đã nén</comment>
+ <comment xml:lang="zh_CN">压缩的 SVG 图像</comment>
+ <comment xml:lang="zh_TW">壓縮版 SVG 影像</comment>
+ <acronym>SVG</acronym>
+ <expanded-acronym>Scalable Vector Graphics</expanded-acronym>
+ <sub-class-of type="application/gzip"/>
+ <glob pattern="*.svgz"/>
+ </mime-type>
+ <mime-type type="image/tiff">
+ <comment>TIFF image</comment>
+ <comment xml:lang="ar">صورة TIFF</comment>
+ <comment xml:lang="be@latin">Vyjava TIFF</comment>
+ <comment xml:lang="bg">Изображение — TIFF</comment>
+ <comment xml:lang="ca">imatge TIFF</comment>
+ <comment xml:lang="cs">obrázek TIFF</comment>
+ <comment xml:lang="da">TIFF-billede</comment>
+ <comment xml:lang="de">TIFF-Bild</comment>
+ <comment xml:lang="el">Εικόνα TIFF</comment>
+ <comment xml:lang="en_GB">TIFF image</comment>
+ <comment xml:lang="eo">TIFF-bildo</comment>
+ <comment xml:lang="es">imagen TIFF</comment>
+ <comment xml:lang="eu">TIFF irudia</comment>
+ <comment xml:lang="fi">TIFF-kuva</comment>
+ <comment xml:lang="fo">TIFF mynd</comment>
+ <comment xml:lang="fr">image TIFF</comment>
+ <comment xml:lang="ga">íomhá TIFF</comment>
+ <comment xml:lang="gl">imaxe TIFF</comment>
+ <comment xml:lang="he">תמונת TIFF</comment>
+ <comment xml:lang="hr">TIFF slika</comment>
+ <comment xml:lang="hu">TIFF-kép</comment>
+ <comment xml:lang="ia">Imagine TIFF</comment>
+ <comment xml:lang="id">Citra TIFF</comment>
+ <comment xml:lang="it">Immagine TIFF</comment>
+ <comment xml:lang="ja">TIFF 画像</comment>
+ <comment xml:lang="kk">TIFF суреті</comment>
+ <comment xml:lang="ko">TIFF 그림</comment>
+ <comment xml:lang="lt">TIFF paveikslėlis</comment>
+ <comment xml:lang="lv">TIFF attēls</comment>
+ <comment xml:lang="ms">Imej TIFF</comment>
+ <comment xml:lang="nb">TIFF-bilde</comment>
+ <comment xml:lang="nl">TIFF-afbeelding</comment>
+ <comment xml:lang="nn">TIFF-bilete</comment>
+ <comment xml:lang="oc">imatge TIFF</comment>
+ <comment xml:lang="pl">Obraz TIFF</comment>
+ <comment xml:lang="pt">imagem TIFF</comment>
+ <comment xml:lang="pt_BR">Imagem TIFF</comment>
+ <comment xml:lang="ro">Imagine TIFF</comment>
+ <comment xml:lang="ru">Изображение TIFF</comment>
+ <comment xml:lang="sk">Obrázok TIFF</comment>
+ <comment xml:lang="sl">Slikovna datoteka TIFF</comment>
+ <comment xml:lang="sq">Figurë TIFF</comment>
+ <comment xml:lang="sr">ТИФФ слика</comment>
+ <comment xml:lang="sv">TIFF-bild</comment>
+ <comment xml:lang="tr">TIFF görüntüsü</comment>
+ <comment xml:lang="uk">зображення TIFF</comment>
+ <comment xml:lang="vi">Ảnh TIFF</comment>
+ <comment xml:lang="zh_CN">TIFF 图像</comment>
+ <comment xml:lang="zh_TW">TIFF 影像</comment>
+ <acronym>TIFF</acronym>
+ <expanded-acronym>Tagged Image File Format</expanded-acronym>
+ <magic priority="50">
+ <match value="MM\x00\x2a" type="string" offset="0"/>
+ <match value="II\x2a\x00" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.tif"/>
+ <glob pattern="*.tiff"/>
+ </mime-type>
+ <mime-type type="image/x-tiff-multipage">
+ <comment>Multi-page TIFF image</comment>
+ <comment xml:lang="ca">imatge TIFF multipàgina</comment>
+ <comment xml:lang="cs">vícestránkový obrázek TIFF</comment>
+ <comment xml:lang="da">Flersidet TIFF-billede</comment>
+ <comment xml:lang="de">Mehrseitiges TIFF-Bild</comment>
+ <comment xml:lang="el">Πολυσέλιδη εικόνα TIFF</comment>
+ <comment xml:lang="en_GB">Multi-page TIFF image</comment>
+ <comment xml:lang="es">imagen TIFF de varias páginas</comment>
+ <comment xml:lang="fi">Monisivuinen TIFF-kuva</comment>
+ <comment xml:lang="fr">Image TIFF multi-page</comment>
+ <comment xml:lang="ga">íomhá il-leathanach TIFF</comment>
+ <comment xml:lang="gl">Imaxe TIFF multipáxina</comment>
+ <comment xml:lang="he">תמונת TIFF עם ריבוי עמודים</comment>
+ <comment xml:lang="hr">Višestrana TIFF slika</comment>
+ <comment xml:lang="hu">Többoldalas TIFF kép</comment>
+ <comment xml:lang="ia">Imagine TIFF multi-pagina</comment>
+ <comment xml:lang="id">Citra TIFF multi halaman</comment>
+ <comment xml:lang="it">Immagine TIFF multi-pagina</comment>
+ <comment xml:lang="kk">Көпбеттік TIFF суреті</comment>
+ <comment xml:lang="ko">다중 페이지 TIFF 그림</comment>
+ <comment xml:lang="oc">Imatge TIFF multipagina</comment>
+ <comment xml:lang="pl">Wielostronnicowy obraz TIFF</comment>
+ <comment xml:lang="pt">imagem TIFF multipágina</comment>
+ <comment xml:lang="pt_BR">Imagem TIFF multipágina</comment>
+ <comment xml:lang="ru">Многостраничное изображение TIFF</comment>
+ <comment xml:lang="sk">Viacstránkový obrázok TIFF</comment>
+ <comment xml:lang="sl">Večstranska slika TIFF</comment>
+ <comment xml:lang="sr">Вишестранична ТИФФ слика</comment>
+ <comment xml:lang="sv">Flersidig TIFF-bild</comment>
+ <comment xml:lang="tr">Çok sayfalı TIFF görüntüsü</comment>
+ <comment xml:lang="uk">багатосторінкове зображення TIFF</comment>
+ <comment xml:lang="zh_CN">多页 TIFF 图像</comment>
+ <comment xml:lang="zh_TW">多頁 TIFF 影像</comment>
+ <acronym>TIFF</acronym>
+ <expanded-acronym>Tagged Image File Format</expanded-acronym>
+ <sub-class-of type="image/tiff"/>
+ </mime-type>
+ <mime-type type="image/vnd.dwg">
+ <comment>AutoCAD image</comment>
+ <comment xml:lang="ar">صورة AutoCAD</comment>
+ <comment xml:lang="az">AutoCAD rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava AutoCAD</comment>
+ <comment xml:lang="bg">Изображение — AutoCAD</comment>
+ <comment xml:lang="ca">imatge d'AutoCAD</comment>
+ <comment xml:lang="cs">výkres AutoCAD</comment>
+ <comment xml:lang="cy">Delwedd AutoCAD</comment>
+ <comment xml:lang="da">AutoCAD-billede</comment>
+ <comment xml:lang="de">AutoCAD-Bild</comment>
+ <comment xml:lang="el">Εικόνα AutoCAD</comment>
+ <comment xml:lang="en_GB">AutoCAD image</comment>
+ <comment xml:lang="eo">AutoCAD-bildo</comment>
+ <comment xml:lang="es">imagen de AutoCAD</comment>
+ <comment xml:lang="eu">AutoCAD-eko irudia</comment>
+ <comment xml:lang="fi">AutoCAD-kuva</comment>
+ <comment xml:lang="fo">AutoCAD mynd</comment>
+ <comment xml:lang="fr">image AutoCAD</comment>
+ <comment xml:lang="ga">íomhá AutoCAD</comment>
+ <comment xml:lang="gl">imaxe de AutoCAD</comment>
+ <comment xml:lang="he">תמונה של AutoCAD</comment>
+ <comment xml:lang="hr">AutoCAD slika</comment>
+ <comment xml:lang="hu">AutoCAD-kép</comment>
+ <comment xml:lang="ia">Imagine AutoCAD</comment>
+ <comment xml:lang="id">Citra AutoCAD</comment>
+ <comment xml:lang="it">Immagine AutoCAD</comment>
+ <comment xml:lang="ja">AutoCAD 画像</comment>
+ <comment xml:lang="ka">AutoCAD-ის გამოსახულება</comment>
+ <comment xml:lang="kk">AutoCAD суреті</comment>
+ <comment xml:lang="ko">AutoCAD 그림</comment>
+ <comment xml:lang="lt">AutoCAD paveikslėlis</comment>
+ <comment xml:lang="lv">AutoCAD attēls</comment>
+ <comment xml:lang="ms">Imej AutoCAD</comment>
+ <comment xml:lang="nb">AutoCAD-bilde</comment>
+ <comment xml:lang="nl">AutoCAD-afbeelding</comment>
+ <comment xml:lang="nn">AutoCAD-bilete</comment>
+ <comment xml:lang="oc">imatge AutoCAD</comment>
+ <comment xml:lang="pl">Obraz AutoCAD</comment>
+ <comment xml:lang="pt">imagem AutoCAD</comment>
+ <comment xml:lang="pt_BR">Imagem do AutoCAD</comment>
+ <comment xml:lang="ro">Imagine AutoCAD</comment>
+ <comment xml:lang="ru">Изображение AutoCAD</comment>
+ <comment xml:lang="sk">Obrázok AutoCAD</comment>
+ <comment xml:lang="sl">Slikovna datoteka AutoCAD</comment>
+ <comment xml:lang="sq">Figurë AutoCAD</comment>
+ <comment xml:lang="sr">АутоКАД слика</comment>
+ <comment xml:lang="sv">AutoCAD-bild</comment>
+ <comment xml:lang="tr">AutoCAD görüntüsü</comment>
+ <comment xml:lang="uk">зображення AutoCAD</comment>
+ <comment xml:lang="vi">Ảnh AutoCAD</comment>
+ <comment xml:lang="zh_CN">AutoCAD 图像</comment>
+ <comment xml:lang="zh_TW">AutoCAD 影像</comment>
+ <glob pattern="*.dwg"/>
+ </mime-type>
+ <mime-type type="image/vnd.dxf">
+ <comment>DXF vector image</comment>
+ <comment xml:lang="ar">صورة DXF نقطية</comment>
+ <comment xml:lang="be@latin">Vektarnaja vyjava DXF</comment>
+ <comment xml:lang="bg">Изображение — DXF</comment>
+ <comment xml:lang="ca">imatge vectorial DXF</comment>
+ <comment xml:lang="cs">vektorový obrázek DXF</comment>
+ <comment xml:lang="da">DXF-vektorbillede</comment>
+ <comment xml:lang="de">DXF-Vektorbild</comment>
+ <comment xml:lang="el">Διανυσματική εικόνα DXF</comment>
+ <comment xml:lang="en_GB">DXF vector image</comment>
+ <comment xml:lang="eo">vektora DXF-bildo</comment>
+ <comment xml:lang="es">imagen vectorial DXF</comment>
+ <comment xml:lang="eu">DXF bektore-grafikoa</comment>
+ <comment xml:lang="fi">DXF-vektorikuva</comment>
+ <comment xml:lang="fo">DXF vektormynd</comment>
+ <comment xml:lang="fr">image vectorielle DXF</comment>
+ <comment xml:lang="ga">íomhá veicteoireach DXF</comment>
+ <comment xml:lang="gl">imaxe de vector DXF</comment>
+ <comment xml:lang="he">תמונת DXF וקטורית</comment>
+ <comment xml:lang="hr">DXF vektorska slika</comment>
+ <comment xml:lang="hu">DXF-vektorkép</comment>
+ <comment xml:lang="ia">Imagine vectorial DXF</comment>
+ <comment xml:lang="id">Citra vektor DXF</comment>
+ <comment xml:lang="it">Immagine vettoriale DXF</comment>
+ <comment xml:lang="ja">DXF ベクター画像</comment>
+ <comment xml:lang="ka">DXF ვექტორული გამოსახულება</comment>
+ <comment xml:lang="kk">DXF векторлық суреті</comment>
+ <comment xml:lang="ko">DXF 벡터 그림</comment>
+ <comment xml:lang="lt">DXF vektorinis paveikslėlis</comment>
+ <comment xml:lang="lv">DXF vektora attēls</comment>
+ <comment xml:lang="ms">Imej vektor DXF</comment>
+ <comment xml:lang="nb">DXF-vektorgrafikk</comment>
+ <comment xml:lang="nl">DXF-vectorafbeelding</comment>
+ <comment xml:lang="nn">DXF-vektorgrafikk</comment>
+ <comment xml:lang="oc">imatge vectorial DXF</comment>
+ <comment xml:lang="pl">Obraz wektorowy DXF</comment>
+ <comment xml:lang="pt">imagem de vectores DXF</comment>
+ <comment xml:lang="pt_BR">Imagem vetorial DXF</comment>
+ <comment xml:lang="ro">Imagine vectorială DXF</comment>
+ <comment xml:lang="ru">Векторное изображение DXF</comment>
+ <comment xml:lang="sk">Vektorový obrázok DXF</comment>
+ <comment xml:lang="sl">Slikovna vektorska datoteka DXF</comment>
+ <comment xml:lang="sq">Figurë vektoriale DFX</comment>
+ <comment xml:lang="sr">ДИксФ векторска графика</comment>
+ <comment xml:lang="sv">DXF-vektorbild</comment>
+ <comment xml:lang="tr">DXF vektör görüntüsü</comment>
+ <comment xml:lang="uk">векторне зображення DXF</comment>
+ <comment xml:lang="vi">Ảnh véc-tơ DXF</comment>
+ <comment xml:lang="zh_CN">DXF 矢量图像</comment>
+ <comment xml:lang="zh_TW">DXF 向量圖</comment>
+ <glob pattern="*.dxf"/>
+ <magic priority="50">
+ <match value="\nHEADER\n" type="string" offset="0:64"/>
+ <match value="\x0d\nHEADER\x0d\n" type="string" offset="0:64"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/vnd.ms-modi">
+ <comment>Microsoft Document Imaging format</comment>
+ <comment xml:lang="ar">صيغة مستند تصوير مايكروسوفت</comment>
+ <comment xml:lang="ast">Formatu d'imáxenes de Microsoft Document</comment>
+ <comment xml:lang="bg">Изображение — Microsoft Document Imaging</comment>
+ <comment xml:lang="ca">format Microsoft Document Imaging</comment>
+ <comment xml:lang="cs">formát Microsoft Document Imaging</comment>
+ <comment xml:lang="da">Microsofts dokumentbilledformat</comment>
+ <comment xml:lang="de">Microsoft-Document-Imaging-Bildformat</comment>
+ <comment xml:lang="el">Μορφή Microsoft Document Imaging</comment>
+ <comment xml:lang="en_GB">Microsoft Document Imaging format</comment>
+ <comment xml:lang="es">formato de imagen para documentos de Microsoft</comment>
+ <comment xml:lang="eu">Microsoft Document Imaging formatua</comment>
+ <comment xml:lang="fi">Microsoft Document Imaging -muoto</comment>
+ <comment xml:lang="fo">Microsoft Document Imaging snið</comment>
+ <comment xml:lang="fr">format Document Imaging Microsoft</comment>
+ <comment xml:lang="ga">formáid Microsoft Document Imaging</comment>
+ <comment xml:lang="gl">formato de Microsoft Document Imaging</comment>
+ <comment xml:lang="he">תבנית של Microsoft Document Imaging</comment>
+ <comment xml:lang="hr">Microsoft Document Imaging format</comment>
+ <comment xml:lang="hu">Microsoft Document Imaging formátum</comment>
+ <comment xml:lang="ia">File in formato Microsoft Document Imaging</comment>
+ <comment xml:lang="id">Format Microsoft Document Imaging</comment>
+ <comment xml:lang="it">Formato MDI (Microsoft Document Imaging)</comment>
+ <comment xml:lang="ja">Microsoft ドキュメントイメージフォーマット</comment>
+ <comment xml:lang="kk">Microsoft Document Imaging пішімі</comment>
+ <comment xml:lang="ko">Microsoft 문서 이미지 형식</comment>
+ <comment xml:lang="lt">Microsoft Document Imaging formatas</comment>
+ <comment xml:lang="lv">Microsoft dokumentu attēlošanas formāts</comment>
+ <comment xml:lang="nl">Microsoft Document Imaging</comment>
+ <comment xml:lang="oc">format Document Imaging Microsoft</comment>
+ <comment xml:lang="pl">Format Microsoft Document Imaging</comment>
+ <comment xml:lang="pt">formato Microsoft Document Imaging</comment>
+ <comment xml:lang="pt_BR">Formato do Microsoft Document Imaging</comment>
+ <comment xml:lang="ro">Format Microsoft Document Imaging</comment>
+ <comment xml:lang="ru">Формат Microsoft Document Imaging</comment>
+ <comment xml:lang="sk">Formát Microsoft Document Imaging</comment>
+ <comment xml:lang="sl">Zapis Microsoft Document Imaging</comment>
+ <comment xml:lang="sr">запис слика Мајкрософтовог документа</comment>
+ <comment xml:lang="sv">Microsoft Document Imaging-format</comment>
+ <comment xml:lang="tr">Microsoft Belge Görüntüleme biçimi</comment>
+ <comment xml:lang="uk">формат Microsoft Document Imaging</comment>
+ <comment xml:lang="vi">Định dạng tạo ảnh tài liệu Microsoft</comment>
+ <comment xml:lang="zh_CN">Microsoft Document Imaging 格式</comment>
+ <comment xml:lang="zh_TW">微軟文件影像格式</comment>
+ <acronym>MDI</acronym>
+ <expanded-acronym>Microsoft Document Imaging</expanded-acronym>
+ <glob pattern="*.mdi"/>
+ <magic priority="50">
+ <match value="\x45\x50\x2A\x00" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/webp">
+ <comment>WebP image</comment>
+ <comment xml:lang="ca">imatge WebP</comment>
+ <comment xml:lang="cs">obrázek WebP</comment>
+ <comment xml:lang="da">WebP-billede</comment>
+ <comment xml:lang="de">WebP-Bild</comment>
+ <comment xml:lang="el">Εικόνα WebP</comment>
+ <comment xml:lang="en_GB">WebP image</comment>
+ <comment xml:lang="es">imagen WebP</comment>
+ <comment xml:lang="eu">WebP irudia</comment>
+ <comment xml:lang="fi">WebP-kuva</comment>
+ <comment xml:lang="fr">image WebP</comment>
+ <comment xml:lang="ga">íomhá WebP</comment>
+ <comment xml:lang="gl">Imaxe WebP</comment>
+ <comment xml:lang="he">תמונת WebP</comment>
+ <comment xml:lang="hr">WebP slika</comment>
+ <comment xml:lang="hu">WebP kép</comment>
+ <comment xml:lang="ia">Imagine WebP</comment>
+ <comment xml:lang="id">Citra WebP</comment>
+ <comment xml:lang="it">Immagine WebP</comment>
+ <comment xml:lang="kk">WebP суреті</comment>
+ <comment xml:lang="ko">WebP 그림</comment>
+ <comment xml:lang="oc">imatge WebP</comment>
+ <comment xml:lang="pl">Obraz WebP</comment>
+ <comment xml:lang="pt">imagem WebP</comment>
+ <comment xml:lang="pt_BR">Imagem WebP</comment>
+ <comment xml:lang="ru">Изображение WebP</comment>
+ <comment xml:lang="sk">Obrázok WebP</comment>
+ <comment xml:lang="sl">Slika WebP</comment>
+ <comment xml:lang="sr">ВебП слика</comment>
+ <comment xml:lang="sv">WebP-bild</comment>
+ <comment xml:lang="tr">WebP görüntüsü</comment>
+ <comment xml:lang="uk">зображення WebP</comment>
+ <comment xml:lang="zh_CN">WebP 图像</comment>
+ <comment xml:lang="zh_TW">WebP 影像</comment>
+ <magic priority="50">
+ <match value="RIFF" type="string" offset="0">
+ <match value="WEBP" type="string" offset="8"/>
+ </match>
+ </magic>
+ <glob pattern="*.webp"/>
+ </mime-type>
+ <mime-type type="image/x-3ds">
+ <comment>3D Studio image</comment>
+ <comment xml:lang="ar">صورة استديو ثلاثية الأبعاد</comment>
+ <comment xml:lang="az">3D Studio rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava 3D Studio</comment>
+ <comment xml:lang="bg">Изображение — 3D Studio</comment>
+ <comment xml:lang="ca">imatge de 3D Studio</comment>
+ <comment xml:lang="cs">obrázek 3D Studio</comment>
+ <comment xml:lang="cy">Delwedd "3D Studio"</comment>
+ <comment xml:lang="da">3D Studio-billede</comment>
+ <comment xml:lang="de">3D-Studio-Bild</comment>
+ <comment xml:lang="el">Εικόνα 3D Studio</comment>
+ <comment xml:lang="en_GB">3D Studio image</comment>
+ <comment xml:lang="eo">bildo de 3D Studio</comment>
+ <comment xml:lang="es">imagen de 3D Studio</comment>
+ <comment xml:lang="eu">3D Studio-ko irudia</comment>
+ <comment xml:lang="fi">3D Studio -kuva</comment>
+ <comment xml:lang="fo">3D Studio mynd</comment>
+ <comment xml:lang="fr">image 3D Studio</comment>
+ <comment xml:lang="ga">íomhá 3D Studio</comment>
+ <comment xml:lang="gl">Imaxe de 3D Studio</comment>
+ <comment xml:lang="he">תמונת 3D Studio</comment>
+ <comment xml:lang="hr">3D Studio slika</comment>
+ <comment xml:lang="hu">3D Studio-kép</comment>
+ <comment xml:lang="ia">Imagine 3D Studio</comment>
+ <comment xml:lang="id">Citra 3D Studio</comment>
+ <comment xml:lang="it">Immagine 3D Studio</comment>
+ <comment xml:lang="ja">3D Studio 画像</comment>
+ <comment xml:lang="ka">3D Studio-ის გამოსახულება</comment>
+ <comment xml:lang="kk">3D Studio суреті</comment>
+ <comment xml:lang="ko">3D Studio 그림</comment>
+ <comment xml:lang="lt">3D Studio paveikslėlis</comment>
+ <comment xml:lang="lv">3D Studio attēls</comment>
+ <comment xml:lang="ms">Imej 3D Studio</comment>
+ <comment xml:lang="nb">3D Studio-bilde</comment>
+ <comment xml:lang="nl">3D-Studio-afbeelding</comment>
+ <comment xml:lang="nn">3D Studio-bilete</comment>
+ <comment xml:lang="oc">imatge 3D Studio</comment>
+ <comment xml:lang="pl">Obraz 3D Studio</comment>
+ <comment xml:lang="pt">imagem 3D Studio</comment>
+ <comment xml:lang="pt_BR">Imagem do 3D Studio</comment>
+ <comment xml:lang="ro">Imagine 3D Studio</comment>
+ <comment xml:lang="ru">Сцена 3D Studio</comment>
+ <comment xml:lang="sk">Obrázok 3D Studio</comment>
+ <comment xml:lang="sl">Slikovna datoteka 3D Studio</comment>
+ <comment xml:lang="sq">Figurë 3D Studio</comment>
+ <comment xml:lang="sr">слика 3Д Студија</comment>
+ <comment xml:lang="sv">3D Studio-bild</comment>
+ <comment xml:lang="tr">3D Studio görüntüsü</comment>
+ <comment xml:lang="uk">зображення 3D Studio</comment>
+ <comment xml:lang="vi">Ảnh xuởng vẽ 3D</comment>
+ <comment xml:lang="zh_CN">3D Studio 图像</comment>
+ <comment xml:lang="zh_TW">3D Studio 影像</comment>
+ <glob pattern="*.3ds"/>
+ </mime-type>
+ <mime-type type="image/x-applix-graphics">
+ <comment>Applix Graphics image</comment>
+ <comment xml:lang="ar">صورة رسوميات Applix</comment>
+ <comment xml:lang="be@latin">Vyjava Applix Graphics</comment>
+ <comment xml:lang="bg">Изображение — Applix Graphics</comment>
+ <comment xml:lang="ca">imatge d'Applix Graphics</comment>
+ <comment xml:lang="cs">obrázek Applix Graphics</comment>
+ <comment xml:lang="da">Applix Graphics-billede</comment>
+ <comment xml:lang="de">Applix-Graphics-Bild</comment>
+ <comment xml:lang="el">Εικόνα Applix Graphics</comment>
+ <comment xml:lang="en_GB">Applix Graphics image</comment>
+ <comment xml:lang="eo">bildo de Applix Graphics</comment>
+ <comment xml:lang="es">imagen de Applix Graphics</comment>
+ <comment xml:lang="eu">Applix Graphics irudia</comment>
+ <comment xml:lang="fi">Applix Graphics -kuva</comment>
+ <comment xml:lang="fo">Applix Graphics mynd</comment>
+ <comment xml:lang="fr">image Applix Graphics</comment>
+ <comment xml:lang="ga">íomhá Applix Graphics</comment>
+ <comment xml:lang="gl">imaxe de Applix Graphics</comment>
+ <comment xml:lang="he">תמונה של Applix Graphics</comment>
+ <comment xml:lang="hr">Applix Graphics slika</comment>
+ <comment xml:lang="hu">Applix Graphics-kép</comment>
+ <comment xml:lang="ia">Imagine Applix Graphics</comment>
+ <comment xml:lang="id">Citra Applix Graphics</comment>
+ <comment xml:lang="it">Immagine Applix Graphics</comment>
+ <comment xml:lang="ja">Applix Graphics 画像</comment>
+ <comment xml:lang="ka">Applix Graphics-ის გამოსახულება</comment>
+ <comment xml:lang="kk">Applix Graphics суреті</comment>
+ <comment xml:lang="ko">Applix Graphics 그림</comment>
+ <comment xml:lang="lt">Applix Graphics paveikslėlis</comment>
+ <comment xml:lang="lv">Applix Graphics attēls</comment>
+ <comment xml:lang="ms">Imej Applix Graphics</comment>
+ <comment xml:lang="nb">Applix Graphics-dokument</comment>
+ <comment xml:lang="nl">Applix Graphics-afbeelding</comment>
+ <comment xml:lang="nn">Applix Graphics-dokument</comment>
+ <comment xml:lang="oc">imatge Applix Graphics</comment>
+ <comment xml:lang="pl">Obraz Applix Graphics</comment>
+ <comment xml:lang="pt">imagem Applix Graphics</comment>
+ <comment xml:lang="pt_BR">Imagem do Applix Graphics</comment>
+ <comment xml:lang="ro">Imagine Applix Graphics</comment>
+ <comment xml:lang="ru">Изображение Applix Graphics</comment>
+ <comment xml:lang="sk">Obrázok Applix Graphics</comment>
+ <comment xml:lang="sl">Slikovna datoteka Applix Graphics</comment>
+ <comment xml:lang="sq">Figurë Applix Graphics</comment>
+ <comment xml:lang="sr">Апликсов графички документ</comment>
+ <comment xml:lang="sv">Applix Graphics-bild</comment>
+ <comment xml:lang="tr">Applix Graphics görüntüsü</comment>
+ <comment xml:lang="uk">зображення Applix Graphics</comment>
+ <comment xml:lang="vi">Ảnh Applix Graphics</comment>
+ <comment xml:lang="zh_CN">Applix Graphics 图像</comment>
+ <comment xml:lang="zh_TW">Applix Graphics 影像</comment>
+ <magic priority="50">
+ <match value="*BEGIN" type="string" offset="0">
+ <match value="GRAPHICS" type="string" offset="7"/>
+ </match>
+ </magic>
+ <glob pattern="*.ag"/>
+ </mime-type>
+ <mime-type type="image/x-bzeps">
+ <comment>EPS image (bzip-compressed)</comment>
+ <comment xml:lang="ar">صورة EPS (مضغوط-bzip)</comment>
+ <comment xml:lang="be@latin">Vyjava EPS (bzip-skampresavanaja)</comment>
+ <comment xml:lang="bg">Изображение — EPS, компресирано с bzip</comment>
+ <comment xml:lang="ca">imatge EPS (amb compressió bzip)</comment>
+ <comment xml:lang="cs">obrázek EPS (komprimovaný pomocí bzip)</comment>
+ <comment xml:lang="da">EPS-billede (bzip-komprimeret)</comment>
+ <comment xml:lang="de">EPS-Bild (bzip-komprimiert)</comment>
+ <comment xml:lang="el">Εικόνα EPS (συμπιεσμένη bzip)</comment>
+ <comment xml:lang="en_GB">EPS image (bzip-compressed)</comment>
+ <comment xml:lang="es">imagen EPS (comprimida con bzip)</comment>
+ <comment xml:lang="eu">EPS irudia (bzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">EPS-kuva (bzip-pakattu)</comment>
+ <comment xml:lang="fo">EPS mynd (bzip-stappað)</comment>
+ <comment xml:lang="fr">image EPS (compressée bzip)</comment>
+ <comment xml:lang="ga">íomhá EPS (comhbhrúite le bzip)</comment>
+ <comment xml:lang="gl">imaxe EPS (comprimida con bzip)</comment>
+ <comment xml:lang="he">תמונת EPS (מכווץ בbzip)</comment>
+ <comment xml:lang="hr">EPS slika (bzip sažeta)</comment>
+ <comment xml:lang="hu">EPS kép (bzip-tömörítésű)</comment>
+ <comment xml:lang="ia">Imagine EPS (comprimite con bzip)</comment>
+ <comment xml:lang="id">Citra EPS (terkompresi bzip)</comment>
+ <comment xml:lang="it">Immagine EPS (compressa con bzip)</comment>
+ <comment xml:lang="ja">EPS 画像 (bzip 圧縮)</comment>
+ <comment xml:lang="ka">EPS გამოსახულება (bzip-ით შეკუმშული)</comment>
+ <comment xml:lang="kk">EPS суреті (bzip-пен сығылған)</comment>
+ <comment xml:lang="ko">EPS 그림(BZIP 압축)</comment>
+ <comment xml:lang="lt">EPS paveikslėlis (suglaudintas su bzip)</comment>
+ <comment xml:lang="lv">EPS attēls (saspiests ar bzip)</comment>
+ <comment xml:lang="nb">EPS-bilde (bzip-komprimert)</comment>
+ <comment xml:lang="nl">EPS-afbeelding (ingepakt met bzip)</comment>
+ <comment xml:lang="nn">EPS-bilete (pakka med bzip)</comment>
+ <comment xml:lang="oc">imatge EPS (compressat bzip)</comment>
+ <comment xml:lang="pl">Obraz EPS (kompresja bzip)</comment>
+ <comment xml:lang="pt">imagem EPS (compressão bzip)</comment>
+ <comment xml:lang="pt_BR">Imagem EPS (compactada com bzip)</comment>
+ <comment xml:lang="ro">Imagine EPS (compresie bzip)</comment>
+ <comment xml:lang="ru">Изображение EPS (сжатое bzip)</comment>
+ <comment xml:lang="sk">Obrázok EPS (komprimovaný pomocou bzip)</comment>
+ <comment xml:lang="sl">Slikovna datoteka EPS (stisnjena z bzip)</comment>
+ <comment xml:lang="sq">Figurë EPS (e kompresuar me bzip)</comment>
+ <comment xml:lang="sr">ЕПС слика (запакована бзип-ом)</comment>
+ <comment xml:lang="sv">EPS-bild (bzip-komprimerad)</comment>
+ <comment xml:lang="tr">EPS görüntüsü (bzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">зображення EPS (стиснене bzip)</comment>
+ <comment xml:lang="vi">Ảnh EPS (đã nén bzip)</comment>
+ <comment xml:lang="zh_CN">EPS 图像(bzip 压缩)</comment>
+ <comment xml:lang="zh_TW">EPS 影像 (bzip 格式壓縮)</comment>
+ <sub-class-of type="application/x-bzip"/>
+ <glob pattern="*.eps.bz2"/>
+ <glob pattern="*.epsi.bz2"/>
+ <glob pattern="*.epsf.bz2"/>
+ </mime-type>
+ <mime-type type="image/x-cmu-raster">
+ <comment>CMU raster image</comment>
+ <comment xml:lang="ar">صورة CMU نقطية</comment>
+ <comment xml:lang="az">CMU raster rəsmi</comment>
+ <comment xml:lang="be@latin">Rastravaja vyjava CMU</comment>
+ <comment xml:lang="bg">Изображение — CMU raster</comment>
+ <comment xml:lang="ca">imatge ràster CMU</comment>
+ <comment xml:lang="cs">rastrový obrázek CMU</comment>
+ <comment xml:lang="cy">Delwedd raster CMU</comment>
+ <comment xml:lang="da">CMU-rasterbillede</comment>
+ <comment xml:lang="de">CMU-Rasterbild</comment>
+ <comment xml:lang="el">Εικόνα ράστερ CMU</comment>
+ <comment xml:lang="en_GB">CMU raster image</comment>
+ <comment xml:lang="eo">rastruma bildo de CMU</comment>
+ <comment xml:lang="es">imagen ráster CMU</comment>
+ <comment xml:lang="eu">CMU bilbe-irudia</comment>
+ <comment xml:lang="fi">CMU-rasterikuva</comment>
+ <comment xml:lang="fo">CMU raster mynd</comment>
+ <comment xml:lang="fr">image raster CMU</comment>
+ <comment xml:lang="ga">íomhá rastar CMU</comment>
+ <comment xml:lang="gl">imaxe raster CMU</comment>
+ <comment xml:lang="he">תמונת סריקה CMU</comment>
+ <comment xml:lang="hr">CMU iscrtana slika</comment>
+ <comment xml:lang="hu">CMU-raszterkép</comment>
+ <comment xml:lang="ia">Imagine raster CMU</comment>
+ <comment xml:lang="id">Citra raster CMU</comment>
+ <comment xml:lang="it">Immagine raster CMU</comment>
+ <comment xml:lang="ja">CMU ラスター画像</comment>
+ <comment xml:lang="ka">CMU-ის რასტრული გამოსახულება</comment>
+ <comment xml:lang="kk">CMU растрлық суреті</comment>
+ <comment xml:lang="ko">CMU 래스터 그림</comment>
+ <comment xml:lang="lt">CMU rastrinis paveikslėlis</comment>
+ <comment xml:lang="lv">CMU rastra attēls</comment>
+ <comment xml:lang="ms">Imej raster CMU</comment>
+ <comment xml:lang="nb">CMU-rasterbilde</comment>
+ <comment xml:lang="nl">CMU-rasterafbeelding</comment>
+ <comment xml:lang="nn">CMU rasterbilete</comment>
+ <comment xml:lang="oc">imatge raster CMU</comment>
+ <comment xml:lang="pl">Obraz rastrowy CMU</comment>
+ <comment xml:lang="pt">imagem raster CMU</comment>
+ <comment xml:lang="pt_BR">Imagem raster CMU</comment>
+ <comment xml:lang="ro">Imagine raster CMU</comment>
+ <comment xml:lang="ru">Растровое изображение CMU</comment>
+ <comment xml:lang="sk">Rastrový obrázok CMU</comment>
+ <comment xml:lang="sl">Slikovna rastrska datoteka CMU</comment>
+ <comment xml:lang="sq">Figurë raster CMU</comment>
+ <comment xml:lang="sr">ЦМУ растерска слика</comment>
+ <comment xml:lang="sv">CMU-rasterbild</comment>
+ <comment xml:lang="tr">CMU tarama görüntüsü</comment>
+ <comment xml:lang="uk">растрове зображення CMU</comment>
+ <comment xml:lang="vi">Ảnh mành CMU</comment>
+ <comment xml:lang="zh_CN">CMU 光栅图像</comment>
+ <comment xml:lang="zh_TW">CMU raster 影像</comment>
+ <glob pattern="*.ras"/>
+ </mime-type>
+ <mime-type type="image/x-compressed-xcf">
+ <comment>compressed GIMP image</comment>
+ <comment xml:lang="ar">صورة GIMP مضغوطة</comment>
+ <comment xml:lang="be@latin">skampresavanaja vyjava GIMP</comment>
+ <comment xml:lang="bg">Изображение — GIMP, компресирано</comment>
+ <comment xml:lang="ca">imatge GIMP amb compressió</comment>
+ <comment xml:lang="cs">komprimovaný obrázek GIMP</comment>
+ <comment xml:lang="da">komprimeret GIMP-billede</comment>
+ <comment xml:lang="de">Komprimiertes GIMP-Bild</comment>
+ <comment xml:lang="el">Συμπιεσμένη εικόνα GIMP</comment>
+ <comment xml:lang="en_GB">compressed GIMP image</comment>
+ <comment xml:lang="es">imagen GIMP comprimida</comment>
+ <comment xml:lang="eu">konprimitutako GIMP irudia</comment>
+ <comment xml:lang="fi">pakattu GIMP-kuva</comment>
+ <comment xml:lang="fo">stappað GIMP mynd</comment>
+ <comment xml:lang="fr">image GIMP compressée</comment>
+ <comment xml:lang="ga">íomhá GIMP comhbhrúite</comment>
+ <comment xml:lang="gl">imaxe de GIMP comprimida</comment>
+ <comment xml:lang="he">תמונת GIMP מכווצת</comment>
+ <comment xml:lang="hr">Sažeta GIMP slika</comment>
+ <comment xml:lang="hu">tömörített GIMP kép</comment>
+ <comment xml:lang="ia">Imagine GIMP comprimite</comment>
+ <comment xml:lang="id">Citra GIMP terkompresi</comment>
+ <comment xml:lang="it">Immagine GIMP compressa</comment>
+ <comment xml:lang="ja">圧縮 GIMP 画像</comment>
+ <comment xml:lang="kk">сығылған GIMP суреті</comment>
+ <comment xml:lang="ko">압축된 GIMP 그림</comment>
+ <comment xml:lang="lt">suglaudintas GIMP paveikslėlis</comment>
+ <comment xml:lang="lv">saspiests GIMP attēls</comment>
+ <comment xml:lang="nb">komprimert GIMP-bilde</comment>
+ <comment xml:lang="nl">ingepakte GIMP-afbeelding</comment>
+ <comment xml:lang="nn">komprimert GIMP-bilete</comment>
+ <comment xml:lang="oc">imatge GIMP compressat</comment>
+ <comment xml:lang="pl">Skompresowany obraz GIMP</comment>
+ <comment xml:lang="pt">imagem GIMP comprimida</comment>
+ <comment xml:lang="pt_BR">Imagem do GIMP compactada</comment>
+ <comment xml:lang="ro">imagine comprimată GIMP</comment>
+ <comment xml:lang="ru">Сжатое изображение GIMP</comment>
+ <comment xml:lang="sk">Komprimovaný obrázok GIMP</comment>
+ <comment xml:lang="sl">Slikovna datoteka GIMP (stisnjena)</comment>
+ <comment xml:lang="sq">Figurë GIMP e kompresuar</comment>
+ <comment xml:lang="sr">запакована ГИМП слика</comment>
+ <comment xml:lang="sv">komprimerad GIMP-bild</comment>
+ <comment xml:lang="tr">sıkıştırılmış GIMP görüntüsü</comment>
+ <comment xml:lang="uk">стиснене зображення GIMP</comment>
+ <comment xml:lang="vi">ảnh GIMP đã nén</comment>
+ <comment xml:lang="zh_CN">压缩的 GIMP 图像</comment>
+ <comment xml:lang="zh_TW">壓縮版 GIMP 影像</comment>
+ <glob pattern="*.xcf.gz"/>
+ <glob pattern="*.xcf.bz2"/>
+ </mime-type>
+ <mime-type type="application/dicom">
+ <comment>DICOM image</comment>
+ <comment xml:lang="ar">صورة DICOM</comment>
+ <comment xml:lang="be@latin">Vyjava DICOM</comment>
+ <comment xml:lang="bg">Изображение — DICOM</comment>
+ <comment xml:lang="ca">imatge DICOM</comment>
+ <comment xml:lang="cs">obrázek DICOM</comment>
+ <comment xml:lang="da">DICOM-billede</comment>
+ <comment xml:lang="de">DICOM-Bild</comment>
+ <comment xml:lang="el">Εικόνα DICOM</comment>
+ <comment xml:lang="en_GB">DICOM image</comment>
+ <comment xml:lang="eo">DICOM-bildo</comment>
+ <comment xml:lang="es">imagen DICOM</comment>
+ <comment xml:lang="eu">DICOM irudia</comment>
+ <comment xml:lang="fi">DICOM-kuva</comment>
+ <comment xml:lang="fo">DICOM mynd</comment>
+ <comment xml:lang="fr">image DICOM</comment>
+ <comment xml:lang="ga">íomhá DICOM</comment>
+ <comment xml:lang="gl">imaxe DICOM</comment>
+ <comment xml:lang="he">תמונת DICOM</comment>
+ <comment xml:lang="hr">DICOM slika</comment>
+ <comment xml:lang="hu">DICOM kép</comment>
+ <comment xml:lang="ia">Imagine DICOM</comment>
+ <comment xml:lang="id">Citra DICOM</comment>
+ <comment xml:lang="it">Immagine DICOM</comment>
+ <comment xml:lang="ja">DICOM 画像</comment>
+ <comment xml:lang="ka">DICOM გამოსახულება</comment>
+ <comment xml:lang="kk">DICOM суреті</comment>
+ <comment xml:lang="ko">DICOM 그림</comment>
+ <comment xml:lang="lt">DICOM paveikslėlis</comment>
+ <comment xml:lang="lv">DICOM attēls</comment>
+ <comment xml:lang="nb">DICOM-bilde</comment>
+ <comment xml:lang="nl">DICOM-afbeelding</comment>
+ <comment xml:lang="nn">DICOM-bilete</comment>
+ <comment xml:lang="oc">imatge DICOM</comment>
+ <comment xml:lang="pl">Obraz DICOM</comment>
+ <comment xml:lang="pt">imagem DICOM</comment>
+ <comment xml:lang="pt_BR">Imagem DICOM</comment>
+ <comment xml:lang="ro">Imagine DICOM</comment>
+ <comment xml:lang="ru">Изображение DICOM</comment>
+ <comment xml:lang="sk">Obrázok DICOM</comment>
+ <comment xml:lang="sl">Slikovna datoteka DICOM</comment>
+ <comment xml:lang="sq">Figurë DICOM</comment>
+ <comment xml:lang="sr">ДИКОМ слика</comment>
+ <comment xml:lang="sv">DICOM-bild</comment>
+ <comment xml:lang="tr">DICOM görüntüsü</comment>
+ <comment xml:lang="uk">зображення DICOM</comment>
+ <comment xml:lang="vi">Ảnh DICOM</comment>
+ <comment xml:lang="zh_CN">DICOM 图像</comment>
+ <comment xml:lang="zh_TW">DICOM 影像</comment>
+ <acronym>DICOM</acronym>
+ <expanded-acronym>Digital Imaging and Communications in Medicine</expanded-acronym>
+ <generic-icon name="image-x-generic"/>
+ <glob pattern="dicomdir"/>
+ <glob pattern="*.dcm"/>
+ <magic priority="50">
+ <match value="DICM" type="string" offset="128"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-docbook+xml">
+ <comment>DocBook document</comment>
+ <comment xml:lang="ar">مستند DocBook</comment>
+ <comment xml:lang="ast">Documentu DocBook</comment>
+ <comment xml:lang="be@latin">Dakument DocBook</comment>
+ <comment xml:lang="bg">Документ — DocBook</comment>
+ <comment xml:lang="ca">document DocBook</comment>
+ <comment xml:lang="cs">dokument DocBook</comment>
+ <comment xml:lang="da">DocBook-dokument</comment>
+ <comment xml:lang="de">DocBook-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο DocBook</comment>
+ <comment xml:lang="en_GB">DocBook document</comment>
+ <comment xml:lang="eo">DocBook-dokumento</comment>
+ <comment xml:lang="es">documento DocBook</comment>
+ <comment xml:lang="eu">DocBook dokumentua</comment>
+ <comment xml:lang="fi">DocBook-asiakirja</comment>
+ <comment xml:lang="fo">DocBook skjal</comment>
+ <comment xml:lang="fr">document DocBook</comment>
+ <comment xml:lang="ga">cáipéis DocBook</comment>
+ <comment xml:lang="gl">documento de DocBook</comment>
+ <comment xml:lang="he">מסמך DocBook</comment>
+ <comment xml:lang="hr">DocBook dokument</comment>
+ <comment xml:lang="hu">DocBook dokumentum</comment>
+ <comment xml:lang="ia">Documento DocBook</comment>
+ <comment xml:lang="id">Dokumen DocBook</comment>
+ <comment xml:lang="it">Documento DocBook</comment>
+ <comment xml:lang="ja">DocBook ドキュメント</comment>
+ <comment xml:lang="ka">DocBook-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">DocBook құжаты</comment>
+ <comment xml:lang="ko">DocBook 문서</comment>
+ <comment xml:lang="lt">DocBook dokumentas</comment>
+ <comment xml:lang="lv">DocBook dokuments</comment>
+ <comment xml:lang="nb">DocBook-dokument</comment>
+ <comment xml:lang="nl">DocBook-document</comment>
+ <comment xml:lang="nn">DocBook-dokument</comment>
+ <comment xml:lang="oc">document DocBook</comment>
+ <comment xml:lang="pl">Dokument DocBook</comment>
+ <comment xml:lang="pt">documento DocBook</comment>
+ <comment xml:lang="pt_BR">Documento DocBook</comment>
+ <comment xml:lang="ro">Document DocBook</comment>
+ <comment xml:lang="ru">Документ DocBook</comment>
+ <comment xml:lang="sk">Dokument DocBook</comment>
+ <comment xml:lang="sl">Dokument DocBook</comment>
+ <comment xml:lang="sq">Dokument DocBook</comment>
+ <comment xml:lang="sr">Док Бук документ</comment>
+ <comment xml:lang="sv">DocBook-dokument</comment>
+ <comment xml:lang="tr">DocBook belgesi</comment>
+ <comment xml:lang="uk">документ DocBook</comment>
+ <comment xml:lang="vi">Tài liệu DocBook</comment>
+ <comment xml:lang="zh_CN">DocBook 文档</comment>
+ <comment xml:lang="zh_TW">DocBook 文件</comment>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="x-office-document"/>
+ <magic priority="90">
+ <match value="&lt;?xml" type="string" offset="0">
+ <match value="-//OASIS//DTD DocBook XML" type="string" offset="0:100"/>
+ <match value="-//KDE//DTD DocBook XML" type="string" offset="0:100"/>
+ </match>
+ </magic>
+ <glob pattern="*.dbk"/>
+ <glob pattern="*.docbook"/>
+ <alias type="application/docbook+xml"/>
+ <alias type="application/vnd.oasis.docbook+xml"/>
+ </mime-type>
+ <mime-type type="image/x-dib">
+ <comment>DIB image</comment>
+ <comment xml:lang="ar">صورة DIB</comment>
+ <comment xml:lang="be@latin">Vyjava DIB</comment>
+ <comment xml:lang="bg">Изображение — DIB</comment>
+ <comment xml:lang="ca">imatge DIB</comment>
+ <comment xml:lang="cs">obrázek DIB</comment>
+ <comment xml:lang="da">DIB-billede</comment>
+ <comment xml:lang="de">DIB-Bild</comment>
+ <comment xml:lang="el">Εικόνα DIB</comment>
+ <comment xml:lang="en_GB">DIB image</comment>
+ <comment xml:lang="eo">DIB-bildo</comment>
+ <comment xml:lang="es">imagen DIB</comment>
+ <comment xml:lang="eu">DIB irudia</comment>
+ <comment xml:lang="fi">DIB-kuva</comment>
+ <comment xml:lang="fo">DIB mynd</comment>
+ <comment xml:lang="fr">image DIB</comment>
+ <comment xml:lang="ga">íomhá DIB</comment>
+ <comment xml:lang="gl">imaxe DIB</comment>
+ <comment xml:lang="he">תמונת DIB</comment>
+ <comment xml:lang="hr">DIB slika</comment>
+ <comment xml:lang="hu">DIB kép</comment>
+ <comment xml:lang="ia">Imagine DIB</comment>
+ <comment xml:lang="id">Citra DIB</comment>
+ <comment xml:lang="it">Immagine DIB</comment>
+ <comment xml:lang="ja">DIB 画像</comment>
+ <comment xml:lang="ka">DIB გამოსახულება</comment>
+ <comment xml:lang="kk">DIB суреті</comment>
+ <comment xml:lang="ko">DIB 그림</comment>
+ <comment xml:lang="lt">DIB paveikslėlis</comment>
+ <comment xml:lang="lv">DIB attēls</comment>
+ <comment xml:lang="nb">DIB-bilde</comment>
+ <comment xml:lang="nl">DIB-afbeelding</comment>
+ <comment xml:lang="nn">DIB-bilete</comment>
+ <comment xml:lang="oc">imatge DIB</comment>
+ <comment xml:lang="pl">Obraz DIB</comment>
+ <comment xml:lang="pt">imagem DIB</comment>
+ <comment xml:lang="pt_BR">Imagem DIB</comment>
+ <comment xml:lang="ro">Imagine DIB</comment>
+ <comment xml:lang="ru">Изображение DIB</comment>
+ <comment xml:lang="sk">Obrázok DIB</comment>
+ <comment xml:lang="sl">Slikovna datoteka DIB</comment>
+ <comment xml:lang="sq">Figurë DIB</comment>
+ <comment xml:lang="sr">ДИБ слика</comment>
+ <comment xml:lang="sv">DIB-bild</comment>
+ <comment xml:lang="tr">DIB görüntüsü</comment>
+ <comment xml:lang="uk">зображення DIB</comment>
+ <comment xml:lang="vi">Ảnh DIB</comment>
+ <comment xml:lang="zh_CN">DIB 图像</comment>
+ <comment xml:lang="zh_TW">DIB 影像</comment>
+ <acronym>DIB</acronym>
+ <expanded-acronym>Device Independent Bitmap</expanded-acronym>
+ <magic priority="50">
+ <match value="\x28\00\00\00" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/vnd.djvu">
+ <comment>DjVu image</comment>
+ <comment xml:lang="ar">صورة DjVu</comment>
+ <comment xml:lang="be@latin">Vyjava DjVu</comment>
+ <comment xml:lang="bg">Изображение — DjVu</comment>
+ <comment xml:lang="ca">imatge DjVu</comment>
+ <comment xml:lang="cs">obrázek DjVu</comment>
+ <comment xml:lang="da">DjVu-billede</comment>
+ <comment xml:lang="de">DjVu-Bild</comment>
+ <comment xml:lang="el">Εικόνα DjVu</comment>
+ <comment xml:lang="en_GB">DjVu image</comment>
+ <comment xml:lang="eo">DjVu-bildo</comment>
+ <comment xml:lang="es">imagen DjVu</comment>
+ <comment xml:lang="eu">DjVU-ko irudia</comment>
+ <comment xml:lang="fi">DjVu-kuva</comment>
+ <comment xml:lang="fo">DjVu mynd</comment>
+ <comment xml:lang="fr">image DjVu</comment>
+ <comment xml:lang="ga">íomhá DjVu</comment>
+ <comment xml:lang="gl">imaxe de DjVu</comment>
+ <comment xml:lang="he">תמונת DjVu</comment>
+ <comment xml:lang="hr">DjVu slika</comment>
+ <comment xml:lang="hu">DjVu-kép</comment>
+ <comment xml:lang="ia">Imagine DjVu</comment>
+ <comment xml:lang="id">Citra DjVu</comment>
+ <comment xml:lang="it">Immagine DjVu</comment>
+ <comment xml:lang="ja">DjVu 画像</comment>
+ <comment xml:lang="ka">DjVu გამოსახულება</comment>
+ <comment xml:lang="kk">DjVu суреті</comment>
+ <comment xml:lang="ko">DjVu 그림</comment>
+ <comment xml:lang="lt">DjVu paveikslėlis</comment>
+ <comment xml:lang="lv">DjVu attēls</comment>
+ <comment xml:lang="ms">Imej DjVu</comment>
+ <comment xml:lang="nb">DjVu-bilde</comment>
+ <comment xml:lang="nl">DjVu-afbeelding</comment>
+ <comment xml:lang="nn">DjVu-bilete</comment>
+ <comment xml:lang="oc">imatge DjVu</comment>
+ <comment xml:lang="pl">Obraz DjVu</comment>
+ <comment xml:lang="pt">imagem DjVu</comment>
+ <comment xml:lang="pt_BR">Imagem DjVu</comment>
+ <comment xml:lang="ro">Imagine DjVu</comment>
+ <comment xml:lang="ru">Изображение DjVu</comment>
+ <comment xml:lang="sk">Obrázok DjVu</comment>
+ <comment xml:lang="sl">Slikovna datoteka DjVu</comment>
+ <comment xml:lang="sq">Figurë DjVu</comment>
+ <comment xml:lang="sr">ДјВу слика</comment>
+ <comment xml:lang="sv">DjVu-bild</comment>
+ <comment xml:lang="tr">DjVu görüntüsü</comment>
+ <comment xml:lang="uk">зображення DjVu</comment>
+ <comment xml:lang="vi">Ảnh DjVu</comment>
+ <comment xml:lang="zh_CN">DjVu 图像</comment>
+ <comment xml:lang="zh_TW">DjVu 影像</comment>
+ <alias type="image/x-djvu"/>
+ <alias type="image/x.djvu"/>
+ <magic priority="80">
+ <match value="AT&amp;TFORM" type="string" offset="0">
+ <match value="DJVU" type="string" offset="12"/>
+ </match>
+ <match value="FORM" type="string" offset="0">
+ <match value="DJVU" type="string" offset="8"/>
+ </match>
+ </magic>
+ <glob pattern="*.djvu"/>
+ <glob pattern="*.djv"/>
+ </mime-type>
+ <mime-type type="image/vnd.djvu+multipage">
+ <comment>DjVu document</comment>
+ <comment xml:lang="ast">Documentu DjVu</comment>
+ <comment xml:lang="ca">document DjVu</comment>
+ <comment xml:lang="cs">dokument DjVu</comment>
+ <comment xml:lang="da">DjVu-dokument</comment>
+ <comment xml:lang="de">DjVu-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο DjVu</comment>
+ <comment xml:lang="en_GB">DjVu document</comment>
+ <comment xml:lang="es">documento DjVu</comment>
+ <comment xml:lang="eu">DjVu dokumentua</comment>
+ <comment xml:lang="fi">DjVu-asiakirja</comment>
+ <comment xml:lang="fr">document DjVu</comment>
+ <comment xml:lang="ga">cáipéis DjVu</comment>
+ <comment xml:lang="he">מסמך DjVu</comment>
+ <comment xml:lang="hr">DjVu dokument</comment>
+ <comment xml:lang="hu">DjVu dokumentum</comment>
+ <comment xml:lang="ia">Documento DjVu</comment>
+ <comment xml:lang="id">Dokumen DjVu</comment>
+ <comment xml:lang="it">Documento DjVu</comment>
+ <comment xml:lang="kk">DjVu құжаты</comment>
+ <comment xml:lang="ko">DjVu 문서</comment>
+ <comment xml:lang="oc">document DjVu</comment>
+ <comment xml:lang="pl">Dokument DjVu</comment>
+ <comment xml:lang="pt">documento DjVu</comment>
+ <comment xml:lang="pt_BR">Documento DjVu</comment>
+ <comment xml:lang="ru">Документ DjVu</comment>
+ <comment xml:lang="sk">Dokument DjVu</comment>
+ <comment xml:lang="sr">ДјВу документ</comment>
+ <comment xml:lang="sv">DjVu-dokument</comment>
+ <comment xml:lang="tr">DjVu belgesi</comment>
+ <comment xml:lang="uk">документ DjVu</comment>
+ <comment xml:lang="zh_CN">DjVu 文档</comment>
+ <comment xml:lang="zh_TW">DjVu 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="80">
+ <match value="AT&amp;TFORM" type="string" offset="0">
+ <match value="DJVM" type="string" offset="12"/>
+ </match>
+ <match value="FORM" type="string" offset="0">
+ <match value="DJVM" type="string" offset="8"/>
+ </match>
+ </magic>
+ <sub-class-of type="image/vnd.djvu"/>
+ <glob pattern="*.djvu"/>
+ <glob pattern="*.djv"/>
+ </mime-type>
+ <mime-type type="image/dpx">
+ <comment>DPX image</comment>
+ <comment xml:lang="ar">صورة DPX</comment>
+ <comment xml:lang="be@latin">Vyjava DPX</comment>
+ <comment xml:lang="bg">Изображение — DPX</comment>
+ <comment xml:lang="ca">imatge DPX</comment>
+ <comment xml:lang="cs">obrázek DPX</comment>
+ <comment xml:lang="da">DPX-billede</comment>
+ <comment xml:lang="de">DPX-Bild</comment>
+ <comment xml:lang="el">Εικόνα DPX</comment>
+ <comment xml:lang="en_GB">DPX image</comment>
+ <comment xml:lang="eo">DPX-bildo</comment>
+ <comment xml:lang="es">imagen DPX</comment>
+ <comment xml:lang="eu">DPX irudia</comment>
+ <comment xml:lang="fi">DPX-kuva</comment>
+ <comment xml:lang="fo">DPX mynd</comment>
+ <comment xml:lang="fr">image DPX</comment>
+ <comment xml:lang="ga">íomhá DPX</comment>
+ <comment xml:lang="gl">imaxe DPX</comment>
+ <comment xml:lang="he">תמונת DPX</comment>
+ <comment xml:lang="hr">DPX slika</comment>
+ <comment xml:lang="hu">DPX kép</comment>
+ <comment xml:lang="ia">Imagine DPX</comment>
+ <comment xml:lang="id">Citra DPX</comment>
+ <comment xml:lang="it">Immagine DPX</comment>
+ <comment xml:lang="ja">DPX 画像</comment>
+ <comment xml:lang="ka">DPX გამოსახულება</comment>
+ <comment xml:lang="kk">DPX суреті</comment>
+ <comment xml:lang="ko">DPX 그림</comment>
+ <comment xml:lang="lt">DPX paveikslėlis</comment>
+ <comment xml:lang="lv">DPX attēls</comment>
+ <comment xml:lang="nb">DPX-bilde</comment>
+ <comment xml:lang="nl">DPX-afbeelding</comment>
+ <comment xml:lang="nn">DPX-bilete</comment>
+ <comment xml:lang="oc">imatge DPX</comment>
+ <comment xml:lang="pl">Obraz DPX</comment>
+ <comment xml:lang="pt">imagem DPX</comment>
+ <comment xml:lang="pt_BR">Imagem DPX</comment>
+ <comment xml:lang="ro">Imagine DPX</comment>
+ <comment xml:lang="ru">Изображение DPX</comment>
+ <comment xml:lang="sk">Obrázok DPX</comment>
+ <comment xml:lang="sl">Slikovna datoteka DPX</comment>
+ <comment xml:lang="sq">Figurë DPX</comment>
+ <comment xml:lang="sr">ДПИкс слика</comment>
+ <comment xml:lang="sv">DPX-bild</comment>
+ <comment xml:lang="tr">DPX görüntüsü</comment>
+ <comment xml:lang="uk">зображення DPX</comment>
+ <comment xml:lang="vi">Ảnh DPX</comment>
+ <comment xml:lang="zh_CN">DPX 图像</comment>
+ <comment xml:lang="zh_TW">DPX 影像</comment>
+ <acronym>DPX</acronym>
+ <expanded-acronym>Digital Moving Picture Exchange</expanded-acronym>
+ <magic priority="50">
+ <match value="0x53445058" type="big32" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/x-eps">
+ <comment>EPS image</comment>
+ <comment xml:lang="ar">صورة EPS</comment>
+ <comment xml:lang="be@latin">Vyjava EPS</comment>
+ <comment xml:lang="bg">Изображение — EPS</comment>
+ <comment xml:lang="ca">imatge EPS</comment>
+ <comment xml:lang="cs">obrázek EPS</comment>
+ <comment xml:lang="da">EPS-billede</comment>
+ <comment xml:lang="de">EPS-Bild</comment>
+ <comment xml:lang="el">Εικόνα EPS</comment>
+ <comment xml:lang="en_GB">EPS image</comment>
+ <comment xml:lang="eo">EPS-bildo</comment>
+ <comment xml:lang="es">imagen EPS</comment>
+ <comment xml:lang="eu">EPS irudia</comment>
+ <comment xml:lang="fi">EPS-kuva</comment>
+ <comment xml:lang="fo">EPS mynd</comment>
+ <comment xml:lang="fr">image EPS</comment>
+ <comment xml:lang="ga">íomhá EPS</comment>
+ <comment xml:lang="gl">imaxe EPS</comment>
+ <comment xml:lang="he">תמונת EPS</comment>
+ <comment xml:lang="hr">EPS slika</comment>
+ <comment xml:lang="hu">EPS kép</comment>
+ <comment xml:lang="ia">Imagine EPS</comment>
+ <comment xml:lang="id">Citra EPS</comment>
+ <comment xml:lang="it">Immagine EPS</comment>
+ <comment xml:lang="ja">EPS 画像</comment>
+ <comment xml:lang="ka">EPS გამოსახულება</comment>
+ <comment xml:lang="kk">EPS суреті</comment>
+ <comment xml:lang="ko">EPS 그림</comment>
+ <comment xml:lang="lt">EPS paveikslėlis</comment>
+ <comment xml:lang="lv">EPS attēls</comment>
+ <comment xml:lang="nb">EPS-bilde</comment>
+ <comment xml:lang="nl">EPS-afbeelding</comment>
+ <comment xml:lang="nn">EPS-bilete</comment>
+ <comment xml:lang="oc">imatge EPS</comment>
+ <comment xml:lang="pl">Obraz EPS</comment>
+ <comment xml:lang="pt">imagem EPS</comment>
+ <comment xml:lang="pt_BR">Imagem EPS</comment>
+ <comment xml:lang="ro">Imagine EPS</comment>
+ <comment xml:lang="ru">Изображение EPS</comment>
+ <comment xml:lang="sk">Obrázok EPS</comment>
+ <comment xml:lang="sl">Slikovna datoteka EPS</comment>
+ <comment xml:lang="sq">Figurë EPS</comment>
+ <comment xml:lang="sr">ЕПС слика</comment>
+ <comment xml:lang="sv">EPS-bild</comment>
+ <comment xml:lang="tr">EPS görüntüsü</comment>
+ <comment xml:lang="uk">зображення EPS</comment>
+ <comment xml:lang="vi">Ảnh EPS</comment>
+ <comment xml:lang="zh_CN">EPS 图像</comment>
+ <comment xml:lang="zh_TW">EPS 影像</comment>
+ <acronym>EPS</acronym>
+ <expanded-acronym>Encapsulated PostScript</expanded-acronym>
+ <sub-class-of type="application/postscript"/>
+ <magic priority="90">
+ <match value="%!" type="string" offset="0">
+ <match value="EPS" type="string" offset="15"/>
+ </match>
+ <match value="\004%!" type="string" offset="0">
+ <match value="EPS" type="string" offset="16"/>
+ </match>
+ <match value="0xC5D0D3C6" type="big32" offset="0"/>
+ </magic>
+ <glob pattern="*.eps"/>
+ <glob pattern="*.epsi"/>
+ <glob pattern="*.epsf"/>
+ </mime-type>
+ <mime-type type="image/fits">
+ <comment>FITS document</comment>
+ <comment xml:lang="ar">مستند FITS</comment>
+ <comment xml:lang="ast">Documentu FITS</comment>
+ <comment xml:lang="be@latin">Dakument FITS</comment>
+ <comment xml:lang="bg">Документ — FITS</comment>
+ <comment xml:lang="ca">document FITS</comment>
+ <comment xml:lang="cs">dokument FITS</comment>
+ <comment xml:lang="da">FITS-dokument</comment>
+ <comment xml:lang="de">FITS-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο FITS</comment>
+ <comment xml:lang="en_GB">FITS document</comment>
+ <comment xml:lang="eo">FITS-dokumento</comment>
+ <comment xml:lang="es">documento FITS</comment>
+ <comment xml:lang="eu">FITS dokumentua</comment>
+ <comment xml:lang="fi">FITS-asiakirja</comment>
+ <comment xml:lang="fo">FITS skjal</comment>
+ <comment xml:lang="fr">document FITS</comment>
+ <comment xml:lang="ga">cáipéis FITS</comment>
+ <comment xml:lang="gl">documento FICT</comment>
+ <comment xml:lang="he">מסמך FITS</comment>
+ <comment xml:lang="hr">FITS dokument</comment>
+ <comment xml:lang="hu">FITS dokumentum</comment>
+ <comment xml:lang="ia">Documento FITS</comment>
+ <comment xml:lang="id">Dokumen FITS</comment>
+ <comment xml:lang="it">Documento FITS</comment>
+ <comment xml:lang="ja">FITS ドキュメント</comment>
+ <comment xml:lang="ka">FITS დოკუმენტი</comment>
+ <comment xml:lang="kk">FITS құжаты</comment>
+ <comment xml:lang="ko">FITS 문서</comment>
+ <comment xml:lang="lt">FITS dokumentas</comment>
+ <comment xml:lang="lv">FITS dokuments</comment>
+ <comment xml:lang="nb">FITS-dokument</comment>
+ <comment xml:lang="nl">FITS-document</comment>
+ <comment xml:lang="nn">FITS-dokument</comment>
+ <comment xml:lang="oc">document FITS</comment>
+ <comment xml:lang="pl">Dokument FITS</comment>
+ <comment xml:lang="pt">documento FITS</comment>
+ <comment xml:lang="pt_BR">Documento FITS</comment>
+ <comment xml:lang="ro">Document FITS</comment>
+ <comment xml:lang="ru">Документ FITS</comment>
+ <comment xml:lang="sk">Dokument FITS</comment>
+ <comment xml:lang="sl">Dokument FITS</comment>
+ <comment xml:lang="sq">Dokument FITS</comment>
+ <comment xml:lang="sr">ФИТС документ</comment>
+ <comment xml:lang="sv">FITS-dokument</comment>
+ <comment xml:lang="tr">FITS belgesi</comment>
+ <comment xml:lang="uk">документ FITS</comment>
+ <comment xml:lang="vi">Tài liệu FITS</comment>
+ <comment xml:lang="zh_CN">FITS 文档</comment>
+ <comment xml:lang="zh_TW">FITS 文件</comment>
+ <acronym>FITS</acronym>
+ <expanded-acronym>Flexible Image Transport System</expanded-acronym>
+ <magic priority="50">
+ <match value="SIMPLE =" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.fits"/>
+ <alias type="image/x-fits"/>
+ </mime-type>
+ <mime-type type="image/x-fpx">
+ <comment>FPX image</comment>
+ <comment xml:lang="ar">صورة FPX</comment>
+ <comment xml:lang="be@latin">Vyjava FPX</comment>
+ <comment xml:lang="bg">Изображение — FPX</comment>
+ <comment xml:lang="ca">imatge FPX</comment>
+ <comment xml:lang="cs">obrázek FPX</comment>
+ <comment xml:lang="da">FPX-billede</comment>
+ <comment xml:lang="de">FPX-Bild</comment>
+ <comment xml:lang="el">Εικόνα FPX</comment>
+ <comment xml:lang="en_GB">FPX image</comment>
+ <comment xml:lang="eo">FPX-bildo</comment>
+ <comment xml:lang="es">imagen FPX</comment>
+ <comment xml:lang="eu">FPX irudia</comment>
+ <comment xml:lang="fi">FPX-kuva</comment>
+ <comment xml:lang="fo">FPX mynd</comment>
+ <comment xml:lang="fr">image FPX</comment>
+ <comment xml:lang="ga">íomhá FPX</comment>
+ <comment xml:lang="gl">imaxe FPX</comment>
+ <comment xml:lang="he">תמונת FPX</comment>
+ <comment xml:lang="hr">FPX slika</comment>
+ <comment xml:lang="hu">FPX kép</comment>
+ <comment xml:lang="ia">Imagine FPX</comment>
+ <comment xml:lang="id">Citra FPX</comment>
+ <comment xml:lang="it">Immagine FPX</comment>
+ <comment xml:lang="ja">FPX 画像</comment>
+ <comment xml:lang="ka">FPX გამოსახულება</comment>
+ <comment xml:lang="kk">FPX суреті</comment>
+ <comment xml:lang="ko">FPX 그림</comment>
+ <comment xml:lang="lt">FPX paveikslėlis</comment>
+ <comment xml:lang="lv">FPX attēls</comment>
+ <comment xml:lang="nb">FPX-bilde</comment>
+ <comment xml:lang="nl">FPX-afbeelding</comment>
+ <comment xml:lang="nn">FPX-bilete</comment>
+ <comment xml:lang="oc">imatge FPX</comment>
+ <comment xml:lang="pl">Obraz FPX</comment>
+ <comment xml:lang="pt">imagem FPX</comment>
+ <comment xml:lang="pt_BR">Imagem FPX</comment>
+ <comment xml:lang="ro">Imagine FPX</comment>
+ <comment xml:lang="ru">Изображение FPX</comment>
+ <comment xml:lang="sk">Obrázok FPX</comment>
+ <comment xml:lang="sl">Slikovna datoteka FPX</comment>
+ <comment xml:lang="sq">Figurë FPX</comment>
+ <comment xml:lang="sr">ФПИкс слика</comment>
+ <comment xml:lang="sv">FPX-bild</comment>
+ <comment xml:lang="tr">FPX görüntüsü</comment>
+ <comment xml:lang="uk">зображення FPX</comment>
+ <comment xml:lang="vi">Ảnh FPX</comment>
+ <comment xml:lang="zh_CN">FPX 图像</comment>
+ <comment xml:lang="zh_TW">FPX 影像</comment>
+ <acronym>FPX</acronym>
+ <expanded-acronym>FlashPiX</expanded-acronym>
+ <magic priority="50">
+ <match value="0x46506978" type="big32" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/x-gzeps">
+ <comment>EPS image (gzip-compressed)</comment>
+ <comment xml:lang="ar">صورة EPS (مضغوط-gzip)</comment>
+ <comment xml:lang="be@latin">Vyjava EPS (gzip-skampresavanaja)</comment>
+ <comment xml:lang="bg">Изображение — EPS, компресирано с gzip</comment>
+ <comment xml:lang="ca">imatge EPS (amb compressió gzip)</comment>
+ <comment xml:lang="cs">obrázek EPS (komprimovaný pomocí gzip)</comment>
+ <comment xml:lang="da">EPS-billede (gzip-komprimeret)</comment>
+ <comment xml:lang="de">EPS-Bild (gzip-komprimiert)</comment>
+ <comment xml:lang="el">Εικόνα EPS (συμπιεσμένη gzip)</comment>
+ <comment xml:lang="en_GB">EPS image (gzip-compressed)</comment>
+ <comment xml:lang="es">imagen EPS (comprimida con gzip)</comment>
+ <comment xml:lang="eu">EPS irudia (gzip-ekin konprimitua)</comment>
+ <comment xml:lang="fi">EPS-kuva (gzip-pakattu)</comment>
+ <comment xml:lang="fo">EPS mynd (gzip-stappað)</comment>
+ <comment xml:lang="fr">image EPS (compressée gzip)</comment>
+ <comment xml:lang="ga">íomhá EPS (comhbhrúite le gzip)</comment>
+ <comment xml:lang="gl">imaxe EPS (comprimida con gzip)</comment>
+ <comment xml:lang="he">תמונת EPS (מכווץ ע״י gzip)</comment>
+ <comment xml:lang="hr">EPS slika (gzip sažeta)</comment>
+ <comment xml:lang="hu">EPS kép (gzip-tömörítésű)</comment>
+ <comment xml:lang="ia">Imagine EPS (comprimite con gzip)</comment>
+ <comment xml:lang="id">Citra EPS (terkompresi gzip)</comment>
+ <comment xml:lang="it">Immagine EPS (compressa con gzip)</comment>
+ <comment xml:lang="ja">EPS 画像 (gzip 圧縮)</comment>
+ <comment xml:lang="ka">EPS გამოსახულება (gzip-ით შეკუმშული)</comment>
+ <comment xml:lang="kk">EPS суреті (gzip-пен сығылған)</comment>
+ <comment xml:lang="ko">EPS 그림(GZIP 압축)</comment>
+ <comment xml:lang="lt">EPS paveikslėlis (suglaudintas su gzip)</comment>
+ <comment xml:lang="lv">EPS attēls (saspiests ar gzip)</comment>
+ <comment xml:lang="nb">EPS-bilde (gzip-komprimert)</comment>
+ <comment xml:lang="nl">EPS-afbeelding (ingepakt met gzip)</comment>
+ <comment xml:lang="nn">EPS-bilete (pakka med gzip)</comment>
+ <comment xml:lang="oc">imatge EPS (compressat gzip)</comment>
+ <comment xml:lang="pl">Obraz EPS (kompresja gzip)</comment>
+ <comment xml:lang="pt">imagem EPS (compressão gzip)</comment>
+ <comment xml:lang="pt_BR">Imagem EPS (compactada com gzip)</comment>
+ <comment xml:lang="ro">Imagine EPS (compresie gzip)</comment>
+ <comment xml:lang="ru">Изображение EPS (сжатое gzip)</comment>
+ <comment xml:lang="sk">Obrázok EPS (komprimovaný pomocou gzip)</comment>
+ <comment xml:lang="sl">Slikovna datoteka EPS (stisnjena z gzip)</comment>
+ <comment xml:lang="sq">Figurë EPS (e kompresuar me gzip)</comment>
+ <comment xml:lang="sr">ЕПС слика (запакована гзип-ом)</comment>
+ <comment xml:lang="sv">EPS-bild (gzip-komprimerad)</comment>
+ <comment xml:lang="tr">EPS görüntüsü (gzip ile sıkıştırılmış)</comment>
+ <comment xml:lang="uk">зображення EPS (стиснене gzip)</comment>
+ <comment xml:lang="vi">Ảnh EPS (đã nén gzip)</comment>
+ <comment xml:lang="zh_CN">EPS 图像(gzip 压缩)</comment>
+ <comment xml:lang="zh_TW">EPS 影像 (gzip 格式壓縮)</comment>
+ <sub-class-of type="application/gzip"/>
+ <glob pattern="*.eps.gz"/>
+ <glob pattern="*.epsi.gz"/>
+ <glob pattern="*.epsf.gz"/>
+ </mime-type>
+ <mime-type type="image/vnd.microsoft.icon">
+ <comment>Windows icon</comment>
+ <comment xml:lang="ca">icona de Windows</comment>
+ <comment xml:lang="cs">ikona Windows</comment>
+ <comment xml:lang="da">Windows-ikon</comment>
+ <comment xml:lang="de">Windows-Symbol</comment>
+ <comment xml:lang="el">Εικονίδιο Windows</comment>
+ <comment xml:lang="en_GB">Windows icon</comment>
+ <comment xml:lang="es">icono de Windows</comment>
+ <comment xml:lang="eu">Windows ikonoa</comment>
+ <comment xml:lang="fi">Windows-kuvake</comment>
+ <comment xml:lang="fr">icône Windows</comment>
+ <comment xml:lang="ga">deilbhín Windows</comment>
+ <comment xml:lang="he">סמל של Windows</comment>
+ <comment xml:lang="hr">Windows ikona</comment>
+ <comment xml:lang="hu">Windows ikon</comment>
+ <comment xml:lang="ia">Icone pro Windows</comment>
+ <comment xml:lang="id">Ikon Windows</comment>
+ <comment xml:lang="it">Icona Windows</comment>
+ <comment xml:lang="kk">Windows таңбашасы</comment>
+ <comment xml:lang="ko">윈도우 아이콘</comment>
+ <comment xml:lang="oc">icòna Windows</comment>
+ <comment xml:lang="pl">Ikona Windows</comment>
+ <comment xml:lang="pt">ícone Windows</comment>
+ <comment xml:lang="pt_BR">Ícone do Windows</comment>
+ <comment xml:lang="ru">Значок Windows</comment>
+ <comment xml:lang="sk">Ikona Windows</comment>
+ <comment xml:lang="sl">Ikona Windows</comment>
+ <comment xml:lang="sr">Иконица Виндоуза</comment>
+ <comment xml:lang="sv">Windows-ikon</comment>
+ <comment xml:lang="tr">Windows simgesi</comment>
+ <comment xml:lang="uk">піктограма Windows</comment>
+ <comment xml:lang="zh_CN">Windows 图标</comment>
+ <comment xml:lang="zh_TW">Windows 圖示</comment>
+ <magic priority="50">
+ <match value="\0\0\1\0" type="string" offset="0">
+ <match value="\0" type="string" offset="5"/>
+ </match>
+ </magic>
+ <glob pattern="*.ico"/>
+ <alias type="application/ico"/>
+ <alias type="image/ico"/>
+ <alias type="image/icon"/>
+ <alias type="image/x-ico"/>
+ <alias type="image/x-icon"/>
+ <alias type="text/ico"/>
+ </mime-type>
+ <mime-type type="image/x-icns">
+ <comment>MacOS X icon</comment>
+ <comment xml:lang="ar">أيقونة MacOS X</comment>
+ <comment xml:lang="be@latin">Ikona MacOS X</comment>
+ <comment xml:lang="bg">Икона — MacOS X</comment>
+ <comment xml:lang="ca">icona MacOS X</comment>
+ <comment xml:lang="cs">ikona MacOS X</comment>
+ <comment xml:lang="da">MacOS X-ikon</comment>
+ <comment xml:lang="de">MacOS-X-Symbol</comment>
+ <comment xml:lang="el">Εικονίδιο MacOS X</comment>
+ <comment xml:lang="en_GB">MacOS X icon</comment>
+ <comment xml:lang="eo">MacOS-X-piktogramo</comment>
+ <comment xml:lang="es">icono de OS X</comment>
+ <comment xml:lang="eu">MacOS X ikonoa</comment>
+ <comment xml:lang="fi">MacOS X -kuvake</comment>
+ <comment xml:lang="fo">MacOS X ímynd</comment>
+ <comment xml:lang="fr">icône MacOS X</comment>
+ <comment xml:lang="ga">deilbhín MacOS X</comment>
+ <comment xml:lang="gl">Icona de MacOS X</comment>
+ <comment xml:lang="he">סמל בתקן MacOS X</comment>
+ <comment xml:lang="hr">MacOS X ikona</comment>
+ <comment xml:lang="hu">MacOS X ikon</comment>
+ <comment xml:lang="ia">Icone de Mac OS X</comment>
+ <comment xml:lang="id">Ikon MacOS X</comment>
+ <comment xml:lang="it">Icona MacOS X</comment>
+ <comment xml:lang="ja">MacOS X アイコン</comment>
+ <comment xml:lang="ka">MacOS X-ის ხატულა</comment>
+ <comment xml:lang="kk">MacOS X таңбашасы</comment>
+ <comment xml:lang="ko">Mac OS X 아이콘</comment>
+ <comment xml:lang="lt">MacOS X piktograma</comment>
+ <comment xml:lang="lv">MacOS X ikona</comment>
+ <comment xml:lang="nb">MacOS X-ikon</comment>
+ <comment xml:lang="nl">MacOS-X-pictogram</comment>
+ <comment xml:lang="nn">MacOS X-ikon</comment>
+ <comment xml:lang="oc">icòna MacOS X</comment>
+ <comment xml:lang="pl">Ikona Mac OS X</comment>
+ <comment xml:lang="pt">ćone MacOS X</comment>
+ <comment xml:lang="pt_BR">Ícone do MacOS X</comment>
+ <comment xml:lang="ro">Iconiță MacOS X</comment>
+ <comment xml:lang="ru">Значок MacOS X</comment>
+ <comment xml:lang="sk">Ikona MacOS X</comment>
+ <comment xml:lang="sl">Datoteka ikone MacOS X</comment>
+ <comment xml:lang="sq">Ikonë MacOS X</comment>
+ <comment xml:lang="sr">МекОС Икс иконица</comment>
+ <comment xml:lang="sv">MacOS X-ikon</comment>
+ <comment xml:lang="tr">MacOS X simgesi</comment>
+ <comment xml:lang="uk">піктограма MacOS X</comment>
+ <comment xml:lang="vi">Biểu tượng MacOS X</comment>
+ <comment xml:lang="zh_CN">MacOS X 图标</comment>
+ <comment xml:lang="zh_TW">MacOS X 圖示</comment>
+ <glob pattern="*.icns"/>
+ <magic priority="50">
+ <match value="icns" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/x-ilbm">
+ <comment>ILBM image</comment>
+ <comment xml:lang="ar">صورة ILBM</comment>
+ <comment xml:lang="az">ILBM rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava ILBM</comment>
+ <comment xml:lang="bg">Изображение — ILBM</comment>
+ <comment xml:lang="ca">imatge ILBM</comment>
+ <comment xml:lang="cs">obrázek ILMB</comment>
+ <comment xml:lang="cy">Delwedd ILBM</comment>
+ <comment xml:lang="da">ILBM-billede</comment>
+ <comment xml:lang="de">ILBM-Bild</comment>
+ <comment xml:lang="el">Εικόνα ILBM</comment>
+ <comment xml:lang="en_GB">ILBM image</comment>
+ <comment xml:lang="eo">ILBM-bildo</comment>
+ <comment xml:lang="es">imagen ILBM</comment>
+ <comment xml:lang="eu">ILBM irudia</comment>
+ <comment xml:lang="fi">ILBM-kuva</comment>
+ <comment xml:lang="fo">ILBM mynd</comment>
+ <comment xml:lang="fr">image ILBM</comment>
+ <comment xml:lang="ga">íomhá ILBM</comment>
+ <comment xml:lang="gl">imaxe ILBM</comment>
+ <comment xml:lang="he">תמונת ILBM</comment>
+ <comment xml:lang="hr">ILBM slika</comment>
+ <comment xml:lang="hu">ILBM-kép</comment>
+ <comment xml:lang="ia">Imagine ILBM</comment>
+ <comment xml:lang="id">Citra ILBM</comment>
+ <comment xml:lang="it">Immagine ILBM</comment>
+ <comment xml:lang="ja">ILBM 画像</comment>
+ <comment xml:lang="kk">ILBM суреті</comment>
+ <comment xml:lang="ko">ILBM 그림</comment>
+ <comment xml:lang="lt">ILBM paveikslėlis</comment>
+ <comment xml:lang="lv">ILBM attēls</comment>
+ <comment xml:lang="ms">Imej ILBM</comment>
+ <comment xml:lang="nb">ILBM-bilde</comment>
+ <comment xml:lang="nl">ILBM-afbeelding</comment>
+ <comment xml:lang="nn">ILMB-bilete</comment>
+ <comment xml:lang="oc">imatge ILBM</comment>
+ <comment xml:lang="pl">Obraz ILBM</comment>
+ <comment xml:lang="pt">imagem ILBM</comment>
+ <comment xml:lang="pt_BR">Imagem ILBM</comment>
+ <comment xml:lang="ro">Imagine ILBM</comment>
+ <comment xml:lang="ru">Изображение ILBM</comment>
+ <comment xml:lang="sk">Obrázok ILMB</comment>
+ <comment xml:lang="sl">Slikovna datoteka ILBM</comment>
+ <comment xml:lang="sq">Figurë ILBM</comment>
+ <comment xml:lang="sr">ИЛБМ слика</comment>
+ <comment xml:lang="sv">ILBM-bild</comment>
+ <comment xml:lang="tr">ILBM görüntüsü</comment>
+ <comment xml:lang="uk">зображення ILBM</comment>
+ <comment xml:lang="vi">Ảnh ILBM</comment>
+ <comment xml:lang="zh_CN">ILBM 图像</comment>
+ <comment xml:lang="zh_TW">ILBM 影像</comment>
+ <acronym>ILBM</acronym>
+ <expanded-acronym>InterLeaved BitMap</expanded-acronym>
+ <sub-class-of type="application/x-iff"/>
+ <magic priority="50">
+ <match value="ILBM" type="string" offset="8"/>
+ <match value="PBM " type="string" offset="8"/>
+ </magic>
+ <glob pattern="*.iff"/>
+ <glob pattern="*.ilbm"/>
+ <glob pattern="*.lbm"/>
+ <alias type="image/x-iff"/>
+ </mime-type>
+ <mime-type type="image/x-jng">
+ <comment>JNG image</comment>
+ <comment xml:lang="ar">صورة JNG</comment>
+ <comment xml:lang="az">JNG rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava JNG</comment>
+ <comment xml:lang="bg">Изображение — JNG</comment>
+ <comment xml:lang="ca">imatge JNG</comment>
+ <comment xml:lang="cs">obrázek JNG</comment>
+ <comment xml:lang="cy">Delwedd JNG</comment>
+ <comment xml:lang="da">JNG-billede</comment>
+ <comment xml:lang="de">JNG-Bild</comment>
+ <comment xml:lang="el">Εικόνα JNG</comment>
+ <comment xml:lang="en_GB">JNG image</comment>
+ <comment xml:lang="eo">JNG-bildo</comment>
+ <comment xml:lang="es">imagen JNG</comment>
+ <comment xml:lang="eu">JNG irudia</comment>
+ <comment xml:lang="fi">JNG-kuva</comment>
+ <comment xml:lang="fo">JNG mynd</comment>
+ <comment xml:lang="fr">image JNG</comment>
+ <comment xml:lang="ga">íomhá JNG</comment>
+ <comment xml:lang="gl">imaxe JNG</comment>
+ <comment xml:lang="he">תמונת JNG</comment>
+ <comment xml:lang="hr">JNG slika</comment>
+ <comment xml:lang="hu">JNG-kép</comment>
+ <comment xml:lang="ia">Imagine JNG</comment>
+ <comment xml:lang="id">Citra JNG</comment>
+ <comment xml:lang="it">Immagine JNG</comment>
+ <comment xml:lang="ja">JNG 画像</comment>
+ <comment xml:lang="kk">JNG суреті</comment>
+ <comment xml:lang="ko">JNG 그림</comment>
+ <comment xml:lang="lt">JNG paveikslėlis</comment>
+ <comment xml:lang="lv">JNG attēls</comment>
+ <comment xml:lang="ms">Imej PNG</comment>
+ <comment xml:lang="nb">JNG-bilde</comment>
+ <comment xml:lang="nl">JNG-afbeelding</comment>
+ <comment xml:lang="nn">JNG-bilete</comment>
+ <comment xml:lang="oc">imatge JNG</comment>
+ <comment xml:lang="pl">Obraz JNG</comment>
+ <comment xml:lang="pt">imagem JNG</comment>
+ <comment xml:lang="pt_BR">Imagem JNG</comment>
+ <comment xml:lang="ro">Imagine JNG</comment>
+ <comment xml:lang="ru">Изображение JNG</comment>
+ <comment xml:lang="sk">Obrázok JNG</comment>
+ <comment xml:lang="sl">Slikovna datoteka JNG</comment>
+ <comment xml:lang="sq">Figurë JNG</comment>
+ <comment xml:lang="sr">ЈНГ слика</comment>
+ <comment xml:lang="sv">JNG-bild</comment>
+ <comment xml:lang="tr">JNG görüntüsü</comment>
+ <comment xml:lang="uk">зображення JNG</comment>
+ <comment xml:lang="vi">Ảnh JNG</comment>
+ <comment xml:lang="zh_CN">JNG 图像</comment>
+ <comment xml:lang="zh_TW">JNG 影像</comment>
+ <acronym>JNG</acronym>
+ <expanded-acronym>JPEG Network Graphics</expanded-acronym>
+ <glob pattern="*.jng"/>
+ </mime-type>
+ <mime-type type="image/x-lwo">
+ <comment>LightWave object</comment>
+ <comment xml:lang="ar">كائن LightWave</comment>
+ <comment xml:lang="az">LightWave cismi</comment>
+ <comment xml:lang="be@latin">Abjekt LightWave</comment>
+ <comment xml:lang="bg">Обект — LightWave</comment>
+ <comment xml:lang="ca">objecte de LightWave</comment>
+ <comment xml:lang="cs">objekt LightWave</comment>
+ <comment xml:lang="cy">Gwrthrych LightWave</comment>
+ <comment xml:lang="da">LightWave-objekt</comment>
+ <comment xml:lang="de">LightWave-Objekt</comment>
+ <comment xml:lang="el">Αντικείμενο LightWave</comment>
+ <comment xml:lang="en_GB">LightWave object</comment>
+ <comment xml:lang="eo">LightWave-objekto</comment>
+ <comment xml:lang="es">objeto de LightWave</comment>
+ <comment xml:lang="eu">LightWave objektua</comment>
+ <comment xml:lang="fi">LightWave-esine</comment>
+ <comment xml:lang="fo">LightWave lutur</comment>
+ <comment xml:lang="fr">objet LightWave</comment>
+ <comment xml:lang="ga">réad LightWave</comment>
+ <comment xml:lang="gl">obxecto de LightWave</comment>
+ <comment xml:lang="he">עצם LightWave</comment>
+ <comment xml:lang="hr">LightWave objekt</comment>
+ <comment xml:lang="hu">LightWave-objektum</comment>
+ <comment xml:lang="ia">Objecto LightWave</comment>
+ <comment xml:lang="id">Proyek LightWave</comment>
+ <comment xml:lang="it">Oggetto LightWave</comment>
+ <comment xml:lang="ja">LightWave オブジェクト</comment>
+ <comment xml:lang="kk">LightWave объекті</comment>
+ <comment xml:lang="ko">LightWave 개체</comment>
+ <comment xml:lang="lt">LightWave objektas</comment>
+ <comment xml:lang="lv">LightWave objekts</comment>
+ <comment xml:lang="ms">Objek LightWave</comment>
+ <comment xml:lang="nb">LightWave-objekt</comment>
+ <comment xml:lang="nl">LightWave-object</comment>
+ <comment xml:lang="nn">LightWave-objekt</comment>
+ <comment xml:lang="oc">objècte LightWave</comment>
+ <comment xml:lang="pl">Obiekt LightWave</comment>
+ <comment xml:lang="pt">Objecto LightWave</comment>
+ <comment xml:lang="pt_BR">Objeto LightWave</comment>
+ <comment xml:lang="ro">Obiect LightWave</comment>
+ <comment xml:lang="ru">Объект LightWave</comment>
+ <comment xml:lang="sk">Objekt LightWave</comment>
+ <comment xml:lang="sl">Datoteka predmeta LightWave</comment>
+ <comment xml:lang="sq">Objekt LightWave</comment>
+ <comment xml:lang="sr">Лајт Вејв објекат</comment>
+ <comment xml:lang="sv">LightWave-objekt</comment>
+ <comment xml:lang="tr">LightWave nesnesi</comment>
+ <comment xml:lang="uk">об'єкт LightWave</comment>
+ <comment xml:lang="vi">Đối tượng LightWave</comment>
+ <comment xml:lang="zh_CN">LightWave 对象</comment>
+ <comment xml:lang="zh_TW">LightWave 物件</comment>
+ <glob pattern="*.lwo"/>
+ <glob pattern="*.lwob"/>
+ </mime-type>
+ <mime-type type="image/x-lws">
+ <comment>LightWave scene</comment>
+ <comment xml:lang="ar">مشهد LightWave</comment>
+ <comment xml:lang="az">LightWave səhnəsi</comment>
+ <comment xml:lang="be@latin">Scena LightWave</comment>
+ <comment xml:lang="bg">Сцена — LightWave</comment>
+ <comment xml:lang="ca">escena de LightWave</comment>
+ <comment xml:lang="cs">scéna LightWave</comment>
+ <comment xml:lang="cy">Golygfa LightWave</comment>
+ <comment xml:lang="da">LightWave-scene</comment>
+ <comment xml:lang="de">LightWave-Szene</comment>
+ <comment xml:lang="el">Σκηνή LightWave</comment>
+ <comment xml:lang="en_GB">LightWave scene</comment>
+ <comment xml:lang="eo">LightWave-sceno</comment>
+ <comment xml:lang="es">escena de LightWave</comment>
+ <comment xml:lang="eu">LightWave eszena</comment>
+ <comment xml:lang="fi">LightWave-maisema</comment>
+ <comment xml:lang="fo">LightWave leikmynd</comment>
+ <comment xml:lang="fr">scène LightWave</comment>
+ <comment xml:lang="ga">radharc LightWave</comment>
+ <comment xml:lang="gl">escena de LightWave</comment>
+ <comment xml:lang="he">סצנה של LightWave</comment>
+ <comment xml:lang="hr">LightWave scena</comment>
+ <comment xml:lang="hu">LightWave-jelenet</comment>
+ <comment xml:lang="ia">Scena LightWave</comment>
+ <comment xml:lang="id">Scene LightWave</comment>
+ <comment xml:lang="it">Scena LightWave</comment>
+ <comment xml:lang="ja">LightWave シーン</comment>
+ <comment xml:lang="kk">LightWave сахнасы</comment>
+ <comment xml:lang="ko">LightWave 장면</comment>
+ <comment xml:lang="lt">LightWave scena</comment>
+ <comment xml:lang="lv">LightWave aina</comment>
+ <comment xml:lang="ms">Babak LightWave</comment>
+ <comment xml:lang="nb">LightWave-scene</comment>
+ <comment xml:lang="nl">LightWave-scène</comment>
+ <comment xml:lang="nn">LightWave-scene</comment>
+ <comment xml:lang="oc">scèna LightWave</comment>
+ <comment xml:lang="pl">Scena Lightwave</comment>
+ <comment xml:lang="pt">cenário LightWave</comment>
+ <comment xml:lang="pt_BR">Cena LightWave</comment>
+ <comment xml:lang="ro">Scenă LightWave</comment>
+ <comment xml:lang="ru">Сцена LightWave</comment>
+ <comment xml:lang="sk">Scéna LightWave</comment>
+ <comment xml:lang="sl">Datoteka scene LightWave</comment>
+ <comment xml:lang="sq">Skenë LightWave</comment>
+ <comment xml:lang="sr">Лајт Вејв сцена</comment>
+ <comment xml:lang="sv">LightWave-scen</comment>
+ <comment xml:lang="tr">LightWave sahnesi</comment>
+ <comment xml:lang="uk">сцена LightWave</comment>
+ <comment xml:lang="vi">Cảnh LightWave</comment>
+ <comment xml:lang="zh_CN">LightWave 场景</comment>
+ <comment xml:lang="zh_TW">LightWave 場景</comment>
+ <glob pattern="*.lws"/>
+ </mime-type>
+ <mime-type type="image/x-macpaint">
+ <comment>MacPaint Bitmap image</comment>
+ <comment xml:lang="ar">صورة MacPaint Bitmap</comment>
+ <comment xml:lang="be@latin">Bitmapnaja vyjava MacPaint</comment>
+ <comment xml:lang="bg">Изображение — MacPaint Bitmap</comment>
+ <comment xml:lang="ca">imatge de mapa de bits MacPaint</comment>
+ <comment xml:lang="cs">obrázek MacPaint Bitmap</comment>
+ <comment xml:lang="da">MacPaint BitMap-billede</comment>
+ <comment xml:lang="de">MacPaint-Bitmap-Datei</comment>
+ <comment xml:lang="el">Εικόνα Bitmap MacPaint</comment>
+ <comment xml:lang="en_GB">MacPaint Bitmap image</comment>
+ <comment xml:lang="es">imagen de mapa de bits de MacPaint</comment>
+ <comment xml:lang="eu">MacPaint Bitmap irudia</comment>
+ <comment xml:lang="fi">MacPaint-bittikartta</comment>
+ <comment xml:lang="fo">MacPaint Bitmap mynd</comment>
+ <comment xml:lang="fr">image matricielle MacPaint</comment>
+ <comment xml:lang="ga">íomhá ghiotánmhapach MacPaint</comment>
+ <comment xml:lang="gl">imaxe de mapa de bits MacPaint</comment>
+ <comment xml:lang="he">תמונת מפת-סיביות של MacPaint</comment>
+ <comment xml:lang="hr">MacPaint Bitmap slika</comment>
+ <comment xml:lang="hu">MacPaint bitkép</comment>
+ <comment xml:lang="ia">Imagine bitmap de MacPaint</comment>
+ <comment xml:lang="id">Citra MacPaint Bitmap</comment>
+ <comment xml:lang="it">Immagine Bitmap MacPaint</comment>
+ <comment xml:lang="ja">MacPaint ビットマップ画像</comment>
+ <comment xml:lang="kk">MacPaint растрлық суреті</comment>
+ <comment xml:lang="ko">MacPaint 비트맵 그림</comment>
+ <comment xml:lang="lt">MacPaint rastrinis paveikslėlis</comment>
+ <comment xml:lang="lv">MacPaint bitkartes attēls</comment>
+ <comment xml:lang="nb">MacPaint Bitmap-bilde</comment>
+ <comment xml:lang="nl">MacPaint-bitmap-afbeelding</comment>
+ <comment xml:lang="nn">MacPaint punktbilete</comment>
+ <comment xml:lang="oc">imatge matricial MacPaint</comment>
+ <comment xml:lang="pl">Obraz bitmapowy MacPaint</comment>
+ <comment xml:lang="pt">imagem MacPaint Bitmap</comment>
+ <comment xml:lang="pt_BR">Imagem de bitmap do MacPaint</comment>
+ <comment xml:lang="ro">Imagine MacPaint Bitmap</comment>
+ <comment xml:lang="ru">Растровое изображение MacPaint</comment>
+ <comment xml:lang="sk">Obrázok MacPaint Bitmap</comment>
+ <comment xml:lang="sl">Slikovna bitna datoteka MacPaint</comment>
+ <comment xml:lang="sq">Figurë BitMap MacPaint</comment>
+ <comment xml:lang="sr">Мек Пеинт битмап слика</comment>
+ <comment xml:lang="sv">MacPaint Bitmap-bild</comment>
+ <comment xml:lang="tr">MacPaint bit eşlem görüntüsü</comment>
+ <comment xml:lang="uk">растрове зображення MacPaint</comment>
+ <comment xml:lang="vi">Ảnh mảng MacPaint</comment>
+ <comment xml:lang="zh_CN">MacPaint 位图</comment>
+ <comment xml:lang="zh_TW">MacPaint 點陣影像</comment>
+ <glob pattern="*.pntg"/>
+ </mime-type>
+ <mime-type type="image/x-msod">
+ <comment>Office drawing</comment>
+ <comment xml:lang="ar">تصميم أوفيس</comment>
+ <comment xml:lang="be@latin">Ofisny rysunak</comment>
+ <comment xml:lang="bg">Чертеж — Office</comment>
+ <comment xml:lang="ca">dibuix d'Office</comment>
+ <comment xml:lang="cs">kresba Office</comment>
+ <comment xml:lang="da">Officetegning</comment>
+ <comment xml:lang="de">Office-Zeichnung</comment>
+ <comment xml:lang="el">Σχέδιο Office</comment>
+ <comment xml:lang="en_GB">Office drawing</comment>
+ <comment xml:lang="es">dibujo de Office</comment>
+ <comment xml:lang="eu">Office marrazkia</comment>
+ <comment xml:lang="fi">Office-piirros</comment>
+ <comment xml:lang="fo">Office tekning</comment>
+ <comment xml:lang="fr">dessin Office</comment>
+ <comment xml:lang="ga">líníocht Office</comment>
+ <comment xml:lang="gl">debuxo de Office</comment>
+ <comment xml:lang="he">ציור של Office</comment>
+ <comment xml:lang="hr">Office crtež</comment>
+ <comment xml:lang="hu">Office rajz</comment>
+ <comment xml:lang="ia">Designo Office</comment>
+ <comment xml:lang="id">Gambar Office</comment>
+ <comment xml:lang="it">Disegno Office</comment>
+ <comment xml:lang="ja">Office ドロー</comment>
+ <comment xml:lang="kk">Office суреті</comment>
+ <comment xml:lang="ko">오피스 그리기</comment>
+ <comment xml:lang="lt">Office piešinys</comment>
+ <comment xml:lang="lv">Office zīmējums</comment>
+ <comment xml:lang="nb">Office-tegning</comment>
+ <comment xml:lang="nl">Office-tekening</comment>
+ <comment xml:lang="nn">Office-teikning</comment>
+ <comment xml:lang="oc">dessenh Office</comment>
+ <comment xml:lang="pl">Rysunek Office</comment>
+ <comment xml:lang="pt">desenho Office</comment>
+ <comment xml:lang="pt_BR">Desenho do Office</comment>
+ <comment xml:lang="ro">Desen Office</comment>
+ <comment xml:lang="ru">Рисунок Office</comment>
+ <comment xml:lang="sk">Kresba Office</comment>
+ <comment xml:lang="sl">Datoteka risbe Office</comment>
+ <comment xml:lang="sq">Vizatim Office</comment>
+ <comment xml:lang="sr">Канцеларијски цртеж</comment>
+ <comment xml:lang="sv">Office-teckning</comment>
+ <comment xml:lang="tr">Ofis çizimi</comment>
+ <comment xml:lang="uk">малюнок Office</comment>
+ <comment xml:lang="vi">Bản vẽ Office</comment>
+ <comment xml:lang="zh_CN">Office 绘图</comment>
+ <comment xml:lang="zh_TW">Office 繪圖</comment>
+ <glob pattern="*.msod"/>
+ </mime-type>
+ <mime-type type="image/x-niff">
+ <comment>NIFF image</comment>
+ <comment xml:lang="ar">صورة NIFF</comment>
+ <comment xml:lang="be@latin">Vyjava NIFF</comment>
+ <comment xml:lang="bg">Изображение — NIFF</comment>
+ <comment xml:lang="ca">imatge NIFF</comment>
+ <comment xml:lang="cs">obrázek NIFF</comment>
+ <comment xml:lang="da">NIFF-billede</comment>
+ <comment xml:lang="de">NIFF-Bild</comment>
+ <comment xml:lang="el">Εικόνα NIFF</comment>
+ <comment xml:lang="en_GB">NIFF image</comment>
+ <comment xml:lang="eo">NIFF-bildo</comment>
+ <comment xml:lang="es">imagen NIFF</comment>
+ <comment xml:lang="eu">NIFF irudia</comment>
+ <comment xml:lang="fi">NIFF-kuva</comment>
+ <comment xml:lang="fo">NIFF mynd</comment>
+ <comment xml:lang="fr">image NIFF</comment>
+ <comment xml:lang="ga">íomhá NIFF</comment>
+ <comment xml:lang="gl">imaxe NIFF</comment>
+ <comment xml:lang="he">תמונת NIFF</comment>
+ <comment xml:lang="hr">NIFF slika</comment>
+ <comment xml:lang="hu">NIFF kép</comment>
+ <comment xml:lang="ia">Imagine NIFF</comment>
+ <comment xml:lang="id">Citra NIFF</comment>
+ <comment xml:lang="it">Immagine NIFF</comment>
+ <comment xml:lang="ja">NIFF 画像</comment>
+ <comment xml:lang="kk">NIFF суреті</comment>
+ <comment xml:lang="ko">NIFF 그림</comment>
+ <comment xml:lang="lt">NIFF paveikslėlis</comment>
+ <comment xml:lang="lv">NIFF attēls</comment>
+ <comment xml:lang="nb">NIFF-bilde</comment>
+ <comment xml:lang="nl">NIFF-afbeelding</comment>
+ <comment xml:lang="nn">NIFF-bilete</comment>
+ <comment xml:lang="oc">imatge NIFF</comment>
+ <comment xml:lang="pl">Obraz NIFF</comment>
+ <comment xml:lang="pt">imagem NIFF</comment>
+ <comment xml:lang="pt_BR">Imagem NIFF</comment>
+ <comment xml:lang="ro">Imagine NIF</comment>
+ <comment xml:lang="ru">Изображение NIFF</comment>
+ <comment xml:lang="sk">Obrázok NIFF</comment>
+ <comment xml:lang="sl">Slikovna datoteka NIFF</comment>
+ <comment xml:lang="sq">Figurë NIFF</comment>
+ <comment xml:lang="sr">НИФФ слика</comment>
+ <comment xml:lang="sv">NIFF-bild</comment>
+ <comment xml:lang="tr">NIFF görüntüsü</comment>
+ <comment xml:lang="uk">зображення NIFF</comment>
+ <comment xml:lang="vi">Ảnh NIFF</comment>
+ <comment xml:lang="zh_CN">NIFF 图像</comment>
+ <comment xml:lang="zh_TW">NIFF 影像</comment>
+ <magic priority="80">
+ <match value="IIN1" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/vnd.zbrush.pcx">
+ <comment>PCX image</comment>
+ <comment xml:lang="ar">صورة PCX</comment>
+ <comment xml:lang="be@latin">Vyjava PCX</comment>
+ <comment xml:lang="bg">Изображение — PCX</comment>
+ <comment xml:lang="ca">imatge PCX</comment>
+ <comment xml:lang="cs">obrázek PCX</comment>
+ <comment xml:lang="da">PCX-billede</comment>
+ <comment xml:lang="de">PCX-Bild</comment>
+ <comment xml:lang="el">Εικόνα PCX</comment>
+ <comment xml:lang="en_GB">PCX image</comment>
+ <comment xml:lang="eo">PCX-bildo</comment>
+ <comment xml:lang="es">imagen PCX</comment>
+ <comment xml:lang="eu">PCX irudia</comment>
+ <comment xml:lang="fi">PCX-kuva</comment>
+ <comment xml:lang="fo">PCX mynd</comment>
+ <comment xml:lang="fr">image PCX</comment>
+ <comment xml:lang="ga">íomhá PCX</comment>
+ <comment xml:lang="gl">imaxe PCX</comment>
+ <comment xml:lang="he">תמונת PCX</comment>
+ <comment xml:lang="hr">PCX slika</comment>
+ <comment xml:lang="hu">PCX kép</comment>
+ <comment xml:lang="ia">Imagine PCX</comment>
+ <comment xml:lang="id">Citra PCX</comment>
+ <comment xml:lang="it">Immagine PCX</comment>
+ <comment xml:lang="ja">PCX 画像</comment>
+ <comment xml:lang="kk">PCX суреті</comment>
+ <comment xml:lang="ko">PCX 그림</comment>
+ <comment xml:lang="lt">PCX paveikslėlis</comment>
+ <comment xml:lang="lv">PCX attēls</comment>
+ <comment xml:lang="nb">PCX-bilde</comment>
+ <comment xml:lang="nl">PCX-afbeelding</comment>
+ <comment xml:lang="nn">PCX-bilete</comment>
+ <comment xml:lang="oc">imatge PCX</comment>
+ <comment xml:lang="pl">Obraz PCX</comment>
+ <comment xml:lang="pt">imagem PCX</comment>
+ <comment xml:lang="pt_BR">Imagem PCX</comment>
+ <comment xml:lang="ro">Imagine PCX</comment>
+ <comment xml:lang="ru">Изображение PCX</comment>
+ <comment xml:lang="sk">Obrázok PCX</comment>
+ <comment xml:lang="sl">Slikovna datoteka PCX</comment>
+ <comment xml:lang="sq">Figurë PCX</comment>
+ <comment xml:lang="sr">ПЦИкс слика</comment>
+ <comment xml:lang="sv">PCX-bild</comment>
+ <comment xml:lang="tr">PCX görüntüsü</comment>
+ <comment xml:lang="uk">зображення PCX</comment>
+ <comment xml:lang="vi">Ảnh PCX</comment>
+ <comment xml:lang="zh_CN">PCX 图像</comment>
+ <comment xml:lang="zh_TW">PCX 影像</comment>
+ <acronym>PCX</acronym>
+ <expanded-acronym>PiCture eXchange</expanded-acronym>
+ <magic priority="50">
+ <match value="10" type="byte" offset="0">
+ <match value="0" type="byte" offset="1"/>
+ <match value="2" type="byte" offset="1"/>
+ <match value="3" type="byte" offset="1"/>
+ <match value="5" type="byte" offset="1"/>
+ </match>
+ </magic>
+ <glob pattern="*.pcx"/>
+ <alias type="image/x-pcx"/>
+ </mime-type>
+ <mime-type type="image/x-photo-cd">
+ <comment>PCD image</comment>
+ <comment xml:lang="ar">صورة PCD</comment>
+ <comment xml:lang="be@latin">Vyjava PCD</comment>
+ <comment xml:lang="bg">Изображение — PCD</comment>
+ <comment xml:lang="ca">imatge PCD</comment>
+ <comment xml:lang="cs">obrázek PCD</comment>
+ <comment xml:lang="da">PCD-billede</comment>
+ <comment xml:lang="de">PCD-Bild</comment>
+ <comment xml:lang="el">Εικόνα PCD</comment>
+ <comment xml:lang="en_GB">PCD image</comment>
+ <comment xml:lang="eo">PCD-bildo</comment>
+ <comment xml:lang="es">imagen PCD</comment>
+ <comment xml:lang="eu">PCD irudia</comment>
+ <comment xml:lang="fi">PCD-kuva</comment>
+ <comment xml:lang="fo">PCD mynd</comment>
+ <comment xml:lang="fr">image PCD</comment>
+ <comment xml:lang="ga">íomhá PCD</comment>
+ <comment xml:lang="gl">imaxe PCD</comment>
+ <comment xml:lang="he">תמונת PCD</comment>
+ <comment xml:lang="hr">PCD slika</comment>
+ <comment xml:lang="hu">PCD kép</comment>
+ <comment xml:lang="ia">Imagine PCD</comment>
+ <comment xml:lang="id">Citra PCD</comment>
+ <comment xml:lang="it">Immagine PCD</comment>
+ <comment xml:lang="ja">PCD 画像</comment>
+ <comment xml:lang="ka">PCD გამოსახულება</comment>
+ <comment xml:lang="kk">PCD суреті</comment>
+ <comment xml:lang="ko">PCD 그림</comment>
+ <comment xml:lang="lt">PCD paveikslėlis</comment>
+ <comment xml:lang="lv">PCD attēls</comment>
+ <comment xml:lang="nb">PCD-bilde</comment>
+ <comment xml:lang="nl">PCD-afbeelding</comment>
+ <comment xml:lang="nn">PCD-bilete</comment>
+ <comment xml:lang="oc">imatge PCD</comment>
+ <comment xml:lang="pl">Obraz PCD</comment>
+ <comment xml:lang="pt">imagem PCD</comment>
+ <comment xml:lang="pt_BR">Imagem PCD</comment>
+ <comment xml:lang="ro">Imagine PCD</comment>
+ <comment xml:lang="ru">Изображение PCD</comment>
+ <comment xml:lang="sk">Obrázok PCD</comment>
+ <comment xml:lang="sl">Slikovna datoteka PCD</comment>
+ <comment xml:lang="sq">Figurë PCD</comment>
+ <comment xml:lang="sr">ПЦД слика</comment>
+ <comment xml:lang="sv">PCD-bild</comment>
+ <comment xml:lang="tr">PCD görüntüsü</comment>
+ <comment xml:lang="uk">зображення PCD</comment>
+ <comment xml:lang="vi">Ảnh PCD</comment>
+ <comment xml:lang="zh_CN">PCD 图像</comment>
+ <comment xml:lang="zh_TW">PCD 影像</comment>
+ <acronym>PCD</acronym>
+ <expanded-acronym>PhotoCD</expanded-acronym>
+ <glob pattern="*.pcd"/>
+ </mime-type>
+ <mime-type type="image/x-portable-anymap">
+ <comment>PNM image</comment>
+ <comment xml:lang="ar">صورة PNM</comment>
+ <comment xml:lang="az">PNM rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava PNM</comment>
+ <comment xml:lang="bg">Изображение — PNM</comment>
+ <comment xml:lang="ca">imatge PNM</comment>
+ <comment xml:lang="cs">obrázek PNM</comment>
+ <comment xml:lang="cy">Delwedd PNM</comment>
+ <comment xml:lang="da">PNM-billede</comment>
+ <comment xml:lang="de">PNM-Bild</comment>
+ <comment xml:lang="el">Εικόνα PNM</comment>
+ <comment xml:lang="en_GB">PNM image</comment>
+ <comment xml:lang="eo">PNM-bildo</comment>
+ <comment xml:lang="es">imagen PNM</comment>
+ <comment xml:lang="eu">PNM irudia</comment>
+ <comment xml:lang="fi">PNM-kuva</comment>
+ <comment xml:lang="fo">PNM mynd</comment>
+ <comment xml:lang="fr">image PNM</comment>
+ <comment xml:lang="ga">íomhá PNM</comment>
+ <comment xml:lang="gl">imaxe PNM</comment>
+ <comment xml:lang="he">תמונת PNM</comment>
+ <comment xml:lang="hr">PNM slika</comment>
+ <comment xml:lang="hu">PNM-kép</comment>
+ <comment xml:lang="ia">Imagine PNM</comment>
+ <comment xml:lang="id">Citra PNM</comment>
+ <comment xml:lang="it">Immagine PNM</comment>
+ <comment xml:lang="ja">PNM 画像</comment>
+ <comment xml:lang="kk">PNM суреті</comment>
+ <comment xml:lang="ko">PNM 그림</comment>
+ <comment xml:lang="lt">PNM paveikslėlis</comment>
+ <comment xml:lang="lv">PNM attēls</comment>
+ <comment xml:lang="ms">Imej PNM</comment>
+ <comment xml:lang="nb">PNM-bilde</comment>
+ <comment xml:lang="nl">PNM-afbeelding</comment>
+ <comment xml:lang="nn">PNM-bilete</comment>
+ <comment xml:lang="oc">imatge PNM</comment>
+ <comment xml:lang="pl">Obraz PNM</comment>
+ <comment xml:lang="pt">imagem PNM</comment>
+ <comment xml:lang="pt_BR">Imagem PNM</comment>
+ <comment xml:lang="ro">Imagine PNM</comment>
+ <comment xml:lang="ru">Изображение PNM</comment>
+ <comment xml:lang="sk">Obrázok PNM</comment>
+ <comment xml:lang="sl">Slikovna datoteka PNM</comment>
+ <comment xml:lang="sq">Figurë PNM</comment>
+ <comment xml:lang="sr">ПНМ слика</comment>
+ <comment xml:lang="sv">PNM-bild</comment>
+ <comment xml:lang="tr">PNM görüntüsü</comment>
+ <comment xml:lang="uk">зображення PNM</comment>
+ <comment xml:lang="vi">Ảnh PNM</comment>
+ <comment xml:lang="zh_CN">PNM 图像</comment>
+ <comment xml:lang="zh_TW">PNM 影像</comment>
+ <glob pattern="*.pnm"/>
+ </mime-type>
+ <mime-type type="image/x-portable-bitmap">
+ <comment>PBM image</comment>
+ <comment xml:lang="ar">صورة PBM</comment>
+ <comment xml:lang="be@latin">Vyjava PBM</comment>
+ <comment xml:lang="bg">Изображение — PBM</comment>
+ <comment xml:lang="ca">imatge PBM</comment>
+ <comment xml:lang="cs">obrázek PBM</comment>
+ <comment xml:lang="cy">Delwedd PBM</comment>
+ <comment xml:lang="da">PBM-billede</comment>
+ <comment xml:lang="de">PBM-Bild</comment>
+ <comment xml:lang="el">Εικόνα PBM</comment>
+ <comment xml:lang="en_GB">PBM image</comment>
+ <comment xml:lang="eo">PBM-bildo</comment>
+ <comment xml:lang="es">imagen PBM</comment>
+ <comment xml:lang="eu">PBM irudia</comment>
+ <comment xml:lang="fi">PBM-kuva</comment>
+ <comment xml:lang="fo">PBM mynd</comment>
+ <comment xml:lang="fr">image PBM</comment>
+ <comment xml:lang="ga">íomhá PBM</comment>
+ <comment xml:lang="gl">imaxe PBM</comment>
+ <comment xml:lang="he">תמונת PBM</comment>
+ <comment xml:lang="hr">PBM slika</comment>
+ <comment xml:lang="hu">PBM kép</comment>
+ <comment xml:lang="ia">Imagine PBM</comment>
+ <comment xml:lang="id">Citra PBM</comment>
+ <comment xml:lang="it">Immagine PBM</comment>
+ <comment xml:lang="ja">PBM 画像</comment>
+ <comment xml:lang="ka">PBM გამოსახულება</comment>
+ <comment xml:lang="kk">PBM суреті</comment>
+ <comment xml:lang="ko">PBM 그림</comment>
+ <comment xml:lang="lt">PBM paveikslėlis</comment>
+ <comment xml:lang="lv">PBM attēls</comment>
+ <comment xml:lang="nb">PBM-bilde</comment>
+ <comment xml:lang="nl">PBM-afbeelding</comment>
+ <comment xml:lang="nn">PBM-bilete</comment>
+ <comment xml:lang="oc">imatge PBM</comment>
+ <comment xml:lang="pl">Obraz PBM</comment>
+ <comment xml:lang="pt">imagem PBM</comment>
+ <comment xml:lang="pt_BR">Imagem PBM</comment>
+ <comment xml:lang="ro">Imagine PBM</comment>
+ <comment xml:lang="ru">Изображение PBM</comment>
+ <comment xml:lang="sk">Obrázok PBM</comment>
+ <comment xml:lang="sl">Slikovna datoteka PBM</comment>
+ <comment xml:lang="sq">Figurë PBM</comment>
+ <comment xml:lang="sr">ПБМ слика</comment>
+ <comment xml:lang="sv">PBM-bild</comment>
+ <comment xml:lang="tr">PBM görüntüsü</comment>
+ <comment xml:lang="uk">зображення PBM</comment>
+ <comment xml:lang="vi">Ảnh PBM</comment>
+ <comment xml:lang="zh_CN">PBM 图像</comment>
+ <comment xml:lang="zh_TW">PBM 影像</comment>
+ <acronym>PBM</acronym>
+ <expanded-acronym>Portable BitMap</expanded-acronym>
+ <sub-class-of type="image/x-portable-anymap"/>
+ <magic priority="50">
+ <match value="P1" type="string" offset="0">
+ <match value="0x0a" type="byte" offset="2"/>
+ <match value="0x20" type="byte" offset="2"/>
+ <match value="0x09" type="byte" offset="2"/>
+ <match value="0x0d" type="byte" offset="2"/>
+ </match>
+ <match value="P4" type="string" offset="0">
+ <match value="0x0a" type="byte" offset="2"/>
+ <match value="0x20" type="byte" offset="2"/>
+ <match value="0x09" type="byte" offset="2"/>
+ <match value="0x0d" type="byte" offset="2"/>
+ </match>
+ </magic>
+ <glob pattern="*.pbm"/>
+ </mime-type>
+ <mime-type type="image/x-portable-graymap">
+ <comment>PGM image</comment>
+ <comment xml:lang="ar">صورة PGM</comment>
+ <comment xml:lang="be@latin">Vyjava PGM</comment>
+ <comment xml:lang="bg">Изображение — PGM</comment>
+ <comment xml:lang="ca">imatge PGM</comment>
+ <comment xml:lang="cs">obrázek PGM</comment>
+ <comment xml:lang="cy">Delwedd PGM</comment>
+ <comment xml:lang="da">PGM-billede</comment>
+ <comment xml:lang="de">PGM-Bild</comment>
+ <comment xml:lang="el">Εικόνα PGM</comment>
+ <comment xml:lang="en_GB">PGM image</comment>
+ <comment xml:lang="eo">PGM-bildo</comment>
+ <comment xml:lang="es">imagen PGM</comment>
+ <comment xml:lang="eu">PGM irudia</comment>
+ <comment xml:lang="fi">PGM-kuva</comment>
+ <comment xml:lang="fo">PGM mynd</comment>
+ <comment xml:lang="fr">image PGM</comment>
+ <comment xml:lang="ga">íomhá PGM</comment>
+ <comment xml:lang="gl">imaxe PGM</comment>
+ <comment xml:lang="he">תמונת PGM</comment>
+ <comment xml:lang="hr">PGM slika</comment>
+ <comment xml:lang="hu">PGM kép</comment>
+ <comment xml:lang="ia">Imagine PGM</comment>
+ <comment xml:lang="id">Citra PGM</comment>
+ <comment xml:lang="it">Immagine PGM</comment>
+ <comment xml:lang="ja">PGM 画像</comment>
+ <comment xml:lang="kk">PGM суреті</comment>
+ <comment xml:lang="ko">PGM 그림</comment>
+ <comment xml:lang="lt">PGM paveikslėlis</comment>
+ <comment xml:lang="lv">PGM attēls</comment>
+ <comment xml:lang="nb">PGM-bilde</comment>
+ <comment xml:lang="nl">PGM-afbeelding</comment>
+ <comment xml:lang="nn">PGM-bilete</comment>
+ <comment xml:lang="oc">imatge PGM</comment>
+ <comment xml:lang="pl">Obraz PGM</comment>
+ <comment xml:lang="pt">imagem PGM</comment>
+ <comment xml:lang="pt_BR">Imagem PGM</comment>
+ <comment xml:lang="ro">Imagine PGM</comment>
+ <comment xml:lang="ru">Изображение PGM</comment>
+ <comment xml:lang="sk">Obrázok PGM</comment>
+ <comment xml:lang="sl">Slikovna datoteka PGM</comment>
+ <comment xml:lang="sq">Figurë PGM</comment>
+ <comment xml:lang="sr">ПГМ слика</comment>
+ <comment xml:lang="sv">PGM-bild</comment>
+ <comment xml:lang="tr">PGM görüntüsü</comment>
+ <comment xml:lang="uk">зображення PGM</comment>
+ <comment xml:lang="vi">Ảnh PGM</comment>
+ <comment xml:lang="zh_CN">PGM 图像</comment>
+ <comment xml:lang="zh_TW">PGM 影像</comment>
+ <acronym>PGM</acronym>
+ <expanded-acronym>Portable GrayMap</expanded-acronym>
+ <sub-class-of type="image/x-portable-anymap"/>
+ <magic priority="50">
+ <match value="P2" type="string" offset="0">
+ <match value="0x0a" type="byte" offset="2"/>
+ <match value="0x20" type="byte" offset="2"/>
+ <match value="0x09" type="byte" offset="2"/>
+ <match value="0x0d" type="byte" offset="2"/>
+ </match>
+ <match value="P5" type="string" offset="0">
+ <match value="0x0a" type="byte" offset="2"/>
+ <match value="0x20" type="byte" offset="2"/>
+ <match value="0x09" type="byte" offset="2"/>
+ <match value="0x0d" type="byte" offset="2"/>
+ </match>
+ </magic>
+ <glob pattern="*.pgm"/>
+ </mime-type>
+ <mime-type type="image/x-portable-pixmap">
+ <comment>PPM image</comment>
+ <comment xml:lang="ar">صورة PPM</comment>
+ <comment xml:lang="be@latin">Vyjava PPM</comment>
+ <comment xml:lang="bg">Изображение — PPM</comment>
+ <comment xml:lang="ca">imatge PPM</comment>
+ <comment xml:lang="cs">obrázek PPM</comment>
+ <comment xml:lang="cy">Delwedd PPM</comment>
+ <comment xml:lang="da">PPM-billede</comment>
+ <comment xml:lang="de">PPM-Bild</comment>
+ <comment xml:lang="el">Εικόνα PPM</comment>
+ <comment xml:lang="en_GB">PPM image</comment>
+ <comment xml:lang="eo">PPM-bildo</comment>
+ <comment xml:lang="es">imagen PPM</comment>
+ <comment xml:lang="eu">PPM irudia</comment>
+ <comment xml:lang="fi">PPM-kuva</comment>
+ <comment xml:lang="fo">PPM mynd</comment>
+ <comment xml:lang="fr">image PPM</comment>
+ <comment xml:lang="ga">íomhá PPM</comment>
+ <comment xml:lang="gl">imaxe PPM</comment>
+ <comment xml:lang="he">תמונת PPM</comment>
+ <comment xml:lang="hr">PPM slika</comment>
+ <comment xml:lang="hu">PPM kép</comment>
+ <comment xml:lang="ia">Imagine PPM</comment>
+ <comment xml:lang="id">Citra PPM</comment>
+ <comment xml:lang="it">Immagine PPM</comment>
+ <comment xml:lang="ja">PPM 画像</comment>
+ <comment xml:lang="kk">PPM суреті</comment>
+ <comment xml:lang="ko">PPM 그림</comment>
+ <comment xml:lang="lt">PPM paveikslėlis</comment>
+ <comment xml:lang="lv">PPM attēls</comment>
+ <comment xml:lang="nb">PPM-bilde</comment>
+ <comment xml:lang="nl">PPM-afbeelding</comment>
+ <comment xml:lang="nn">PPM-bilete</comment>
+ <comment xml:lang="oc">imatge PPM</comment>
+ <comment xml:lang="pl">Obraz PPM</comment>
+ <comment xml:lang="pt">imagem PPM</comment>
+ <comment xml:lang="pt_BR">Imagem PPM</comment>
+ <comment xml:lang="ro">Imagine PPM</comment>
+ <comment xml:lang="ru">Изображение PPM</comment>
+ <comment xml:lang="sk">Obrázok PPM</comment>
+ <comment xml:lang="sl">Slikovna datoteka PPM</comment>
+ <comment xml:lang="sq">Figurë PPM</comment>
+ <comment xml:lang="sr">ППМ слика</comment>
+ <comment xml:lang="sv">PPM-bild</comment>
+ <comment xml:lang="tr">PPM görüntüsü</comment>
+ <comment xml:lang="uk">зображення PPM</comment>
+ <comment xml:lang="vi">Ảnh PPM</comment>
+ <comment xml:lang="zh_CN">PPM 图像</comment>
+ <comment xml:lang="zh_TW">PPM 影像</comment>
+ <acronym>PPM</acronym>
+ <expanded-acronym>Portable PixMap</expanded-acronym>
+ <sub-class-of type="image/x-portable-anymap"/>
+ <magic priority="50">
+ <match value="P3" type="string" offset="0">
+ <match value="0x0a" type="byte" offset="2"/>
+ <match value="0x20" type="byte" offset="2"/>
+ <match value="0x09" type="byte" offset="2"/>
+ <match value="0x0d" type="byte" offset="2"/>
+ </match>
+ <match value="P6" type="string" offset="0">
+ <match value="0x0a" type="byte" offset="2"/>
+ <match value="0x20" type="byte" offset="2"/>
+ <match value="0x09" type="byte" offset="2"/>
+ <match value="0x0d" type="byte" offset="2"/>
+ </match>
+ </magic>
+ <glob pattern="*.ppm"/>
+ </mime-type>
+ <mime-type type="image/vnd.adobe.photoshop">
+ <comment>Photoshop image</comment>
+ <comment xml:lang="ar">صورة فوتوشوب</comment>
+ <comment xml:lang="bg">Изображение — Photoshop</comment>
+ <comment xml:lang="ca">imatge de Photoshop</comment>
+ <comment xml:lang="cs">obrázek Photoshop</comment>
+ <comment xml:lang="da">Photoshop-billede</comment>
+ <comment xml:lang="de">Photoshop-Bild</comment>
+ <comment xml:lang="el">Εικόνα Photoshop</comment>
+ <comment xml:lang="en_GB">Photoshop image</comment>
+ <comment xml:lang="eo">Photoshop-bildo</comment>
+ <comment xml:lang="es">imagen de Photoshop</comment>
+ <comment xml:lang="eu">Photoshop irudia</comment>
+ <comment xml:lang="fi">Photoshop-kuva</comment>
+ <comment xml:lang="fo">Photoshop mynd</comment>
+ <comment xml:lang="fr">image Photoshop</comment>
+ <comment xml:lang="ga">íomhá Photoshop</comment>
+ <comment xml:lang="gl">imaxe de Photoshop</comment>
+ <comment xml:lang="he">תמונת Photoshop</comment>
+ <comment xml:lang="hr">Photoshop slika</comment>
+ <comment xml:lang="hu">Photoshop-kép</comment>
+ <comment xml:lang="ia">Imagine Photoshop</comment>
+ <comment xml:lang="id">Citra Photoshop</comment>
+ <comment xml:lang="it">Immagine Photoshop</comment>
+ <comment xml:lang="ja">Photoshop 画像</comment>
+ <comment xml:lang="kk">изображение Photoshop</comment>
+ <comment xml:lang="ko">포토샵 이미지</comment>
+ <comment xml:lang="lt">Photoshop paveikslėlis</comment>
+ <comment xml:lang="lv">Photoshop attēls</comment>
+ <comment xml:lang="ms">Imej Photoshop</comment>
+ <comment xml:lang="nl">Photoshop-afbeelding</comment>
+ <comment xml:lang="oc">imatge Photoshop</comment>
+ <comment xml:lang="pl">Obraz Photoshop</comment>
+ <comment xml:lang="pt">imagem Photoshop</comment>
+ <comment xml:lang="pt_BR">Imagem do Photoshop</comment>
+ <comment xml:lang="ro">Imagine Photoshop</comment>
+ <comment xml:lang="ru">Изображение Photoshop</comment>
+ <comment xml:lang="sk">Obrázok Photoshop</comment>
+ <comment xml:lang="sl">Slikovna datoteka Photoshop</comment>
+ <comment xml:lang="sr">Фотошоп слика</comment>
+ <comment xml:lang="sv">Photoshop-bild</comment>
+ <comment xml:lang="tr">Photoshop görüntüsü</comment>
+ <comment xml:lang="uk">зображення Photoshop</comment>
+ <comment xml:lang="vi">Ảnh Photoshop</comment>
+ <comment xml:lang="zh_CN">Photoshop 图像</comment>
+ <comment xml:lang="zh_TW">Photoshop 影像</comment>
+ <magic priority="50">
+ <match value="8BPS \000\000\000\000" type="string" offset="0" mask="0xffffffff0000ffffffff"/>
+ </magic>
+ <glob pattern="*.psd"/>
+ <alias type="image/psd"/>
+ <alias type="image/x-psd"/>
+ <alias type="image/photoshop"/>
+ <alias type="image/x-photoshop"/>
+ <alias type="application/photoshop"/>
+ <alias type="application/x-photoshop"/>
+ </mime-type>
+ <mime-type type="image/x-rgb">
+ <comment>RGB image</comment>
+ <comment xml:lang="ar">صورة RGB</comment>
+ <comment xml:lang="az">RGB rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava RGB</comment>
+ <comment xml:lang="bg">Изображение — RGB</comment>
+ <comment xml:lang="ca">imatge RGB</comment>
+ <comment xml:lang="cs">obrázek RGB</comment>
+ <comment xml:lang="cy">Delwedd RGB</comment>
+ <comment xml:lang="da">RGB-billede</comment>
+ <comment xml:lang="de">RGB-Bild</comment>
+ <comment xml:lang="el">Εικόνα RGB</comment>
+ <comment xml:lang="en_GB">RGB image</comment>
+ <comment xml:lang="eo">RGB-bildo</comment>
+ <comment xml:lang="es">imagen RGB</comment>
+ <comment xml:lang="eu">RGB irudia</comment>
+ <comment xml:lang="fi">RGB-kuva</comment>
+ <comment xml:lang="fo">RGB mynd</comment>
+ <comment xml:lang="fr">image RGB</comment>
+ <comment xml:lang="ga">íomhá RGB</comment>
+ <comment xml:lang="gl">imaxe RGB</comment>
+ <comment xml:lang="he">תמונת RGB</comment>
+ <comment xml:lang="hr">RGB slika</comment>
+ <comment xml:lang="hu">RGB-kép</comment>
+ <comment xml:lang="ia">Imagine RGB</comment>
+ <comment xml:lang="id">Citra RGB</comment>
+ <comment xml:lang="it">Immagine RGB</comment>
+ <comment xml:lang="ja">RGB 画像</comment>
+ <comment xml:lang="kk">RGB суреті</comment>
+ <comment xml:lang="ko">RGB 그림</comment>
+ <comment xml:lang="lt">RGB paveikslėlis</comment>
+ <comment xml:lang="lv">RGB attēls</comment>
+ <comment xml:lang="ms">Imej RGB</comment>
+ <comment xml:lang="nb">RGB-bilde</comment>
+ <comment xml:lang="nl">RGB-afbeelding</comment>
+ <comment xml:lang="nn">RGB-bilete</comment>
+ <comment xml:lang="oc">imatge RGB</comment>
+ <comment xml:lang="pl">Obraz RGB</comment>
+ <comment xml:lang="pt">imagem RGB</comment>
+ <comment xml:lang="pt_BR">Imagem RGB</comment>
+ <comment xml:lang="ro">Imagine RGB</comment>
+ <comment xml:lang="ru">Изображение RGB</comment>
+ <comment xml:lang="sk">Obrázok RGB</comment>
+ <comment xml:lang="sl">Slikovna datoteka RGB</comment>
+ <comment xml:lang="sq">Figurë RGB</comment>
+ <comment xml:lang="sr">РГБ слика</comment>
+ <comment xml:lang="sv">RGB-bild</comment>
+ <comment xml:lang="tr">RGB görüntüsü</comment>
+ <comment xml:lang="uk">зображення RGB</comment>
+ <comment xml:lang="vi">Ảnh kiểu RGB</comment>
+ <comment xml:lang="zh_CN">RGB 图像</comment>
+ <comment xml:lang="zh_TW">RGB 影像</comment>
+ <glob pattern="*.rgb"/>
+ </mime-type>
+ <mime-type type="image/x-sgi">
+ <comment>SGI image</comment>
+ <comment xml:lang="ar">صورة SGI</comment>
+ <comment xml:lang="be@latin">Vyjava SGI</comment>
+ <comment xml:lang="bg">Изображение — SGI</comment>
+ <comment xml:lang="ca">imatge SGI</comment>
+ <comment xml:lang="cs">obrázek SGI</comment>
+ <comment xml:lang="da">SGI-billede</comment>
+ <comment xml:lang="de">SGI-Bild</comment>
+ <comment xml:lang="el">Εικόνα SGI</comment>
+ <comment xml:lang="en_GB">SGI image</comment>
+ <comment xml:lang="eo">SGI-bildo</comment>
+ <comment xml:lang="es">imagen SGI</comment>
+ <comment xml:lang="eu">SGI irudia</comment>
+ <comment xml:lang="fi">SGI-kuva</comment>
+ <comment xml:lang="fo">SGI mynd</comment>
+ <comment xml:lang="fr">image SGI</comment>
+ <comment xml:lang="ga">íomhá SGI</comment>
+ <comment xml:lang="gl">imaxe SGI</comment>
+ <comment xml:lang="he">תמונת SGI</comment>
+ <comment xml:lang="hr">SGI slika</comment>
+ <comment xml:lang="hu">SGI kép</comment>
+ <comment xml:lang="ia">Imagine SGI</comment>
+ <comment xml:lang="id">Citra SGI</comment>
+ <comment xml:lang="it">Immagine SGI</comment>
+ <comment xml:lang="ja">SGI 画像</comment>
+ <comment xml:lang="kk">SGI суреті</comment>
+ <comment xml:lang="ko">SGI 그림</comment>
+ <comment xml:lang="lt">SGI paveikslėlis</comment>
+ <comment xml:lang="lv">SGI attēls</comment>
+ <comment xml:lang="nb">SGI-bilde</comment>
+ <comment xml:lang="nl">SGI-afbeelding</comment>
+ <comment xml:lang="nn">SGI-bilete</comment>
+ <comment xml:lang="oc">imatge SGI</comment>
+ <comment xml:lang="pl">Obraz SGI</comment>
+ <comment xml:lang="pt">imagem SGI</comment>
+ <comment xml:lang="pt_BR">Imagem SGI</comment>
+ <comment xml:lang="ro">Imagine SGI</comment>
+ <comment xml:lang="ru">Изображение SGI</comment>
+ <comment xml:lang="sk">Obrázok SGI</comment>
+ <comment xml:lang="sl">Slikovna datoteka SGI</comment>
+ <comment xml:lang="sq">Figurë SGI</comment>
+ <comment xml:lang="sr">СГИ слика</comment>
+ <comment xml:lang="sv">SGI-bild</comment>
+ <comment xml:lang="tr">SGI görüntüsü</comment>
+ <comment xml:lang="uk">зображення SGI</comment>
+ <comment xml:lang="vi">Ảnh SGI</comment>
+ <comment xml:lang="zh_CN">SGI 图像</comment>
+ <comment xml:lang="zh_TW">SGI 影像</comment>
+ <glob pattern="*.sgi"/>
+ </mime-type>
+ <mime-type type="image/x-sun-raster">
+ <comment>Sun raster image</comment>
+ <comment xml:lang="ar">صورة Sun raster</comment>
+ <comment xml:lang="be@latin">Rastravaja vyjava Sun</comment>
+ <comment xml:lang="bg">Изображение — Sun raster</comment>
+ <comment xml:lang="ca">imatge ràster Sun</comment>
+ <comment xml:lang="cs">rastrový obrázek Sun</comment>
+ <comment xml:lang="da">Sun rasterbillede</comment>
+ <comment xml:lang="de">Sun-Rasterbild</comment>
+ <comment xml:lang="el">Εικόνα Sun raster</comment>
+ <comment xml:lang="en_GB">Sun raster image</comment>
+ <comment xml:lang="es">imagen rasterizada de Sun</comment>
+ <comment xml:lang="eu">Sun raster irudia</comment>
+ <comment xml:lang="fi">Sun-rasterikuva</comment>
+ <comment xml:lang="fo">Sun raster mynd</comment>
+ <comment xml:lang="fr">image raster Sun</comment>
+ <comment xml:lang="ga">íomhá rastar Sun</comment>
+ <comment xml:lang="gl">imaxe ráster de Sun</comment>
+ <comment xml:lang="he">תמונה סרוקה של Sun</comment>
+ <comment xml:lang="hr">Sun iscrtana slika</comment>
+ <comment xml:lang="hu">SUN raszterkép</comment>
+ <comment xml:lang="ia">Imagine raster Sun</comment>
+ <comment xml:lang="id">Citra raster Sun</comment>
+ <comment xml:lang="it">Immagine raster Sun</comment>
+ <comment xml:lang="ja">Sun ラスタ画像</comment>
+ <comment xml:lang="kk">Sun растрлық суреті</comment>
+ <comment xml:lang="ko">Sun 래스터 그림</comment>
+ <comment xml:lang="lt">Sun rastrinis paveikslėlis</comment>
+ <comment xml:lang="lv">Sun rastra attēls</comment>
+ <comment xml:lang="nb">Sun rasterbilde</comment>
+ <comment xml:lang="nl">Sun-rasterafbeelding</comment>
+ <comment xml:lang="nn">Sun rasterbilete</comment>
+ <comment xml:lang="oc">imatge raster Sun</comment>
+ <comment xml:lang="pl">Obraz rastrowy Sun</comment>
+ <comment xml:lang="pt">imagem raster Sun</comment>
+ <comment xml:lang="pt_BR">Imagem raster da Sun</comment>
+ <comment xml:lang="ro">Imagine rasterizată Sun</comment>
+ <comment xml:lang="ru">Растровое изображение Sun</comment>
+ <comment xml:lang="sk">Rastrový obrázok Sun</comment>
+ <comment xml:lang="sl">Slikovna rastrska datoteka Sun</comment>
+ <comment xml:lang="sq">Figurë raster Sun</comment>
+ <comment xml:lang="sr">слика Сановог растера</comment>
+ <comment xml:lang="sv">Sun-rasterbild</comment>
+ <comment xml:lang="tr">Sun raster görüntüsü</comment>
+ <comment xml:lang="uk">растрове зображення Sun</comment>
+ <comment xml:lang="vi">Ảnh mành Sun</comment>
+ <comment xml:lang="zh_CN">Sun 光栅图像</comment>
+ <comment xml:lang="zh_TW">Sun raster 影像</comment>
+ <magic priority="50">
+ <match value="0x59a66a95" type="big32" offset="0"/>
+ </magic>
+ <glob pattern="*.sun"/>
+ </mime-type>
+ <mime-type type="image/x-tga">
+ <comment>TGA image</comment>
+ <comment xml:lang="ar">صورة TGA</comment>
+ <comment xml:lang="be@latin">Vyjava TGA</comment>
+ <comment xml:lang="bg">Изображение — TGA</comment>
+ <comment xml:lang="ca">imatge TGA</comment>
+ <comment xml:lang="cs">obrázek TGA</comment>
+ <comment xml:lang="da">TGA-billede</comment>
+ <comment xml:lang="de">TGA-Bild</comment>
+ <comment xml:lang="el">Εικόνα TGA</comment>
+ <comment xml:lang="en_GB">TGA image</comment>
+ <comment xml:lang="eo">TGA-bildo</comment>
+ <comment xml:lang="es">imagen TGA</comment>
+ <comment xml:lang="eu">TGA irudia</comment>
+ <comment xml:lang="fi">TGA-kuva</comment>
+ <comment xml:lang="fo">TGA mynd</comment>
+ <comment xml:lang="fr">image TGA</comment>
+ <comment xml:lang="ga">íomhá TGA</comment>
+ <comment xml:lang="gl">imaxe TGA</comment>
+ <comment xml:lang="he">תמונת TGA</comment>
+ <comment xml:lang="hr">TGA slika</comment>
+ <comment xml:lang="hu">TGA kép</comment>
+ <comment xml:lang="ia">Imagine TGA</comment>
+ <comment xml:lang="id">Citra TGA</comment>
+ <comment xml:lang="it">Immagine TGA</comment>
+ <comment xml:lang="ja">TGA 画像</comment>
+ <comment xml:lang="kk">TGA суреті</comment>
+ <comment xml:lang="ko">TGA 그림</comment>
+ <comment xml:lang="lt">TGA paveikslėlis</comment>
+ <comment xml:lang="lv">TGA attēls</comment>
+ <comment xml:lang="nb">TGA-bilde</comment>
+ <comment xml:lang="nl">TGA-afbeelding</comment>
+ <comment xml:lang="nn">TGA-bilete</comment>
+ <comment xml:lang="oc">imatge TGA</comment>
+ <comment xml:lang="pl">Obraz TGA</comment>
+ <comment xml:lang="pt">imagem TGA</comment>
+ <comment xml:lang="pt_BR">Imagem TGA</comment>
+ <comment xml:lang="ro">Imagine TGA</comment>
+ <comment xml:lang="ru">Изображение TGA</comment>
+ <comment xml:lang="sk">Obrázok TGA</comment>
+ <comment xml:lang="sl">Slikovna datoteka TGA</comment>
+ <comment xml:lang="sq">Figurë TGA</comment>
+ <comment xml:lang="sr">ТГА слика</comment>
+ <comment xml:lang="sv">TGA-bild</comment>
+ <comment xml:lang="tr">TGA görüntüsü</comment>
+ <comment xml:lang="uk">зображення TGA</comment>
+ <comment xml:lang="vi">Ảnh TGA</comment>
+ <comment xml:lang="zh_CN">TGA 图像</comment>
+ <comment xml:lang="zh_TW">TGA 影像</comment>
+ <acronym>TGA</acronym>
+ <expanded-acronym>Truevision Graphics Adapter</expanded-acronym>
+ <magic priority="10">
+ <match value="\1\1" type="string" offset="1"/>
+ <match value="\1\11" type="string" offset="1"/>
+ <match value="\0\3" type="string" offset="1"/>
+ <match value="\0\xa" type="string" offset="1"/>
+ <match value="\0\xb" type="string" offset="1"/>
+ </magic>
+ <magic priority="50">
+ <match value="\0\2" type="string" offset="1">
+ <match value="0x08" type="byte" offset="16"/>
+ <match value="0x10" type="byte" offset="16"/>
+ <match value="0x18" type="byte" offset="16"/>
+ <match value="0x20" type="byte" offset="16"/>
+ </match>
+ </magic>
+ <glob pattern="*.tga"/>
+ <glob pattern="*.icb"/>
+ <glob pattern="*.tpic"/>
+ <glob pattern="*.vda"/>
+ <glob pattern="*.vst"/>
+ <alias type="image/x-icb"/>
+ </mime-type>
+ <mime-type type="image/x-win-bitmap">
+ <comment>Windows cursor</comment>
+ <comment xml:lang="ar">مؤشر ويندوز</comment>
+ <comment xml:lang="be@latin">Kursor Windows</comment>
+ <comment xml:lang="bg">Курсор — Windows</comment>
+ <comment xml:lang="ca">cursor de Windows</comment>
+ <comment xml:lang="cs">kurzor Windows</comment>
+ <comment xml:lang="da">Windowsmarkør</comment>
+ <comment xml:lang="de">Windows-Cursor</comment>
+ <comment xml:lang="el">Δρομέας Windows</comment>
+ <comment xml:lang="en_GB">Windows cursor</comment>
+ <comment xml:lang="eo">Windows-kursoro</comment>
+ <comment xml:lang="es">cursor de Windows</comment>
+ <comment xml:lang="eu">Windows kurtsorea</comment>
+ <comment xml:lang="fi">Windows-osoitin</comment>
+ <comment xml:lang="fo">Windows vísi</comment>
+ <comment xml:lang="fr">curseur Windows</comment>
+ <comment xml:lang="ga">cúrsóir Windows</comment>
+ <comment xml:lang="gl">Cursor de Windows</comment>
+ <comment xml:lang="he">סמן של Windows</comment>
+ <comment xml:lang="hr">Windows pokazivač</comment>
+ <comment xml:lang="hu">Windows-kurzor</comment>
+ <comment xml:lang="ia">Cursor pro Windows</comment>
+ <comment xml:lang="id">Kursor Windows</comment>
+ <comment xml:lang="it">Cursore Windows</comment>
+ <comment xml:lang="ja">Windows カーソル</comment>
+ <comment xml:lang="kk">Windows курсоры</comment>
+ <comment xml:lang="ko">Windows 커서</comment>
+ <comment xml:lang="lt">Windows žymiklis</comment>
+ <comment xml:lang="lv">Windows kursors</comment>
+ <comment xml:lang="ms">Kursor Windows</comment>
+ <comment xml:lang="nb">Windows-markør</comment>
+ <comment xml:lang="nl">Windows-muisaanwijzer</comment>
+ <comment xml:lang="nn">Windows-peikar</comment>
+ <comment xml:lang="oc">cursor Windows</comment>
+ <comment xml:lang="pl">Kursor Windows</comment>
+ <comment xml:lang="pt">cursor Windows</comment>
+ <comment xml:lang="pt_BR">Cursor do Windows</comment>
+ <comment xml:lang="ro">Cursor Windows</comment>
+ <comment xml:lang="ru">Курсор Windows</comment>
+ <comment xml:lang="sk">Kurzor Windows</comment>
+ <comment xml:lang="sl">Datoteka kazalke Windows</comment>
+ <comment xml:lang="sq">Kursor Windows</comment>
+ <comment xml:lang="sr">Виндоузов курсор</comment>
+ <comment xml:lang="sv">Windows-muspekare</comment>
+ <comment xml:lang="tr">Windows imleci</comment>
+ <comment xml:lang="uk">курсор Windows</comment>
+ <comment xml:lang="vi">Con chạy Windows</comment>
+ <comment xml:lang="zh_CN">Windows 光标</comment>
+ <comment xml:lang="zh_TW">Windows 滑鼠游標</comment>
+ <magic priority="50">
+ <match value="\0\0\2\0" type="string" offset="0">
+ <match value="\0" type="string" offset="5"/>
+ </match>
+ </magic>
+ <glob pattern="*.cur"/>
+ </mime-type>
+ <mime-type type="application/x-navi-animation">
+ <comment>Windows animated cursor</comment>
+ <comment xml:lang="ar">مؤشر ويندوز المتحرك</comment>
+ <comment xml:lang="be@latin">Animavany kursor Windows</comment>
+ <comment xml:lang="bg">Курсор — Windows, анимиран</comment>
+ <comment xml:lang="ca">cursor animat de Windows</comment>
+ <comment xml:lang="cs">animovaný kurzor Windows</comment>
+ <comment xml:lang="da">Windowsanimeret markør</comment>
+ <comment xml:lang="de">Animierter Windows-Cursor</comment>
+ <comment xml:lang="el">Κινούμενος δρομέας Windows</comment>
+ <comment xml:lang="en_GB">Windows animated cursor</comment>
+ <comment xml:lang="es">cursor animado de Windows</comment>
+ <comment xml:lang="eu">Windows-eko kurtsore animatua</comment>
+ <comment xml:lang="fi">animoitu Windows-osoitin</comment>
+ <comment xml:lang="fo">Windows livindaigjørdur vísi</comment>
+ <comment xml:lang="fr">curseur animé Windows</comment>
+ <comment xml:lang="ga">cúrsóir beoite Windows</comment>
+ <comment xml:lang="gl">Cursor animado de Windows</comment>
+ <comment xml:lang="he">סמן מונפש של Windows</comment>
+ <comment xml:lang="hr">Windows animirani pokazivač</comment>
+ <comment xml:lang="hu">Windows animált kurzor</comment>
+ <comment xml:lang="ia">Cursor animate pro Windows</comment>
+ <comment xml:lang="id">Kursor animasi Windows</comment>
+ <comment xml:lang="it">Cursore animato Windows</comment>
+ <comment xml:lang="ja">Windows アニメーションカーソル</comment>
+ <comment xml:lang="kk">Windows анимациясы бар курсор</comment>
+ <comment xml:lang="ko">Windows 움직이는 커서</comment>
+ <comment xml:lang="lt">Animuotas Windows žymiklis</comment>
+ <comment xml:lang="lv">Windows animēts kursors</comment>
+ <comment xml:lang="nl">geanimeerde Windows-muisaanwijzer</comment>
+ <comment xml:lang="nn">Windows animert peikar</comment>
+ <comment xml:lang="oc">cursor animat Windows</comment>
+ <comment xml:lang="pl">Animowany kursor Windows</comment>
+ <comment xml:lang="pt">cursor animado Windows</comment>
+ <comment xml:lang="pt_BR">Cursor animado do Windows</comment>
+ <comment xml:lang="ro">Cursor animat Windows</comment>
+ <comment xml:lang="ru">Анимированный курсор Windows</comment>
+ <comment xml:lang="sk">Animovaný kurzor Windows</comment>
+ <comment xml:lang="sl">Datoteka animirane kazalke Windows</comment>
+ <comment xml:lang="sq">Kursor i animuar Windows</comment>
+ <comment xml:lang="sr">Виндоузов анимирани курсор</comment>
+ <comment xml:lang="sv">Animerad Windows-muspekare</comment>
+ <comment xml:lang="tr">Windows canlandırmalı imleci</comment>
+ <comment xml:lang="uk">анімований курсор Windows</comment>
+ <comment xml:lang="vi">Con chạy hoạt họa Windows</comment>
+ <comment xml:lang="zh_CN">Windows 动态光标</comment>
+ <comment xml:lang="zh_TW">Windows 滑鼠動畫游標</comment>
+ <magic priority="50">
+ <match value="RIFF" type="string" offset="0">
+ <match value="ACON" type="string" offset="8"/>
+ </match>
+ </magic>
+ <glob pattern="*.ani"/>
+ </mime-type>
+ <mime-type type="image/emf">
+ <comment>EMF image</comment>
+ <comment xml:lang="ar">صورة EMF</comment>
+ <comment xml:lang="be@latin">Vyjava EMF</comment>
+ <comment xml:lang="bg">Изображение — EMF</comment>
+ <comment xml:lang="ca">imatge EMF</comment>
+ <comment xml:lang="cs">obrázek EMF</comment>
+ <comment xml:lang="da">EMF-billede</comment>
+ <comment xml:lang="de">EMF-Bild</comment>
+ <comment xml:lang="el">Εικόνα EMF</comment>
+ <comment xml:lang="en_GB">EMF image</comment>
+ <comment xml:lang="eo">EMF-bildo</comment>
+ <comment xml:lang="es">imagen EMF</comment>
+ <comment xml:lang="eu">EMF irudia</comment>
+ <comment xml:lang="fi">EMF-kuva</comment>
+ <comment xml:lang="fo">EMF mynd</comment>
+ <comment xml:lang="fr">image EMF</comment>
+ <comment xml:lang="ga">íomhá EMF</comment>
+ <comment xml:lang="gl">imaxe EMF</comment>
+ <comment xml:lang="he">תמונת EMF</comment>
+ <comment xml:lang="hr">EMF slika</comment>
+ <comment xml:lang="hu">EMF kép</comment>
+ <comment xml:lang="ia">Imagine EMF</comment>
+ <comment xml:lang="id">Citra EMF</comment>
+ <comment xml:lang="it">Immagine EMF</comment>
+ <comment xml:lang="ja">EMF 画像</comment>
+ <comment xml:lang="ka">EMF გამოსახულება</comment>
+ <comment xml:lang="kk">EMF суреті</comment>
+ <comment xml:lang="ko">EMF 그림</comment>
+ <comment xml:lang="lt">EMF paveikslėlis</comment>
+ <comment xml:lang="lv">EMF attēls</comment>
+ <comment xml:lang="nb">EMF-bilde</comment>
+ <comment xml:lang="nl">EMF-afbeelding</comment>
+ <comment xml:lang="nn">EMF-bilete</comment>
+ <comment xml:lang="oc">imatge EMF</comment>
+ <comment xml:lang="pl">Obraz EMF</comment>
+ <comment xml:lang="pt">imagem EMF</comment>
+ <comment xml:lang="pt_BR">Imagem EMF</comment>
+ <comment xml:lang="ro">Imagine EMF</comment>
+ <comment xml:lang="ru">Изображение EMF</comment>
+ <comment xml:lang="sk">Obrázok EMF</comment>
+ <comment xml:lang="sl">Slikovna datoteka EMF</comment>
+ <comment xml:lang="sq">Figurë EMF</comment>
+ <comment xml:lang="sr">ЕМФ слика</comment>
+ <comment xml:lang="sv">EMF-bild</comment>
+ <comment xml:lang="tr">EMF görüntüsü</comment>
+ <comment xml:lang="uk">зображення EMF</comment>
+ <comment xml:lang="vi">Ảnh EMF</comment>
+ <comment xml:lang="zh_CN">EMF 图像</comment>
+ <comment xml:lang="zh_TW">EMF 影像</comment>
+ <acronym>EMF</acronym>
+ <expanded-acronym>Enhanced MetaFile</expanded-acronym>
+ <glob pattern="*.emf"/>
+ <alias type="image/x-emf"/>
+ <alias type="application/x-emf"/>
+ <alias type="application/emf"/>
+ <magic priority="50">
+ <match value="0x00000001" type="little32" offset="0">
+ <match value="0x464D4520" type="little32" offset="40">
+ <match value="0x00010000" type="little32" offset="44">
+ <match value="0x0000" type="little16" offset="58"/>
+ </match>
+ </match>
+ </match>
+ </magic>
+ </mime-type>
+ <mime-type type="image/wmf">
+ <comment>WMF image</comment>
+ <comment xml:lang="ar">صورة WMF</comment>
+ <comment xml:lang="be@latin">Vyjava WMF</comment>
+ <comment xml:lang="bg">Изображение — WMF</comment>
+ <comment xml:lang="ca">imatge WMF</comment>
+ <comment xml:lang="cs">obrázek WMF</comment>
+ <comment xml:lang="da">WMF-billede</comment>
+ <comment xml:lang="de">WMF-Bild</comment>
+ <comment xml:lang="el">Εικόνα WML</comment>
+ <comment xml:lang="en_GB">WMF image</comment>
+ <comment xml:lang="eo">WMF-bildo</comment>
+ <comment xml:lang="es">imagen WMF</comment>
+ <comment xml:lang="eu">WMF irudia</comment>
+ <comment xml:lang="fi">WMF-kuva</comment>
+ <comment xml:lang="fo">WMF mynd</comment>
+ <comment xml:lang="fr">image WMF</comment>
+ <comment xml:lang="ga">íomhá WMF</comment>
+ <comment xml:lang="gl">imaxe WMF</comment>
+ <comment xml:lang="he">תמונת WMF</comment>
+ <comment xml:lang="hr">WMF slika</comment>
+ <comment xml:lang="hu">WMF kép</comment>
+ <comment xml:lang="ia">Imagine WMF</comment>
+ <comment xml:lang="id">Citra WMF</comment>
+ <comment xml:lang="it">Immagine WMF</comment>
+ <comment xml:lang="ja">WMF 画像</comment>
+ <comment xml:lang="kk">WMF суреті</comment>
+ <comment xml:lang="ko">WMF 그림</comment>
+ <comment xml:lang="lt">WMF paveikslėlis</comment>
+ <comment xml:lang="lv">WMF attēls</comment>
+ <comment xml:lang="nb">WMF-bilde</comment>
+ <comment xml:lang="nl">WMF-afbeelding</comment>
+ <comment xml:lang="nn">WMF-bilete</comment>
+ <comment xml:lang="oc">imatge WMF</comment>
+ <comment xml:lang="pl">Obraz WMF</comment>
+ <comment xml:lang="pt">imagem WMF</comment>
+ <comment xml:lang="pt_BR">Imagem WMF</comment>
+ <comment xml:lang="ro">Imagine WMF</comment>
+ <comment xml:lang="ru">Изображение WMF</comment>
+ <comment xml:lang="sk">Obrázok WMF</comment>
+ <comment xml:lang="sl">Slikovna datoteka WMF</comment>
+ <comment xml:lang="sq">Figurë WMF</comment>
+ <comment xml:lang="sr">ВМФ слика</comment>
+ <comment xml:lang="sv">WMF-bild</comment>
+ <comment xml:lang="tr">WMF görüntüsü</comment>
+ <comment xml:lang="uk">зображення WMF</comment>
+ <comment xml:lang="vi">Ảnh WMF</comment>
+ <comment xml:lang="zh_CN">WMF 图像</comment>
+ <comment xml:lang="zh_TW">WMF 影像</comment>
+ <acronym>WMF</acronym>
+ <expanded-acronym>Windows Metafile</expanded-acronym>
+ <magic priority="50">
+
+ <match value="0x9AC6CDD7" type="little32" offset="0">
+
+ <match value="0x0001" type="little16" offset="22">
+ <match value="0x0009" type="little16" offset="24"/>
+ </match>
+ </match>
+
+ <match value="0x0001" type="little16" offset="0">
+ <match value="0x0009" type="little16" offset="2"/>
+ </match>
+ </magic>
+ <glob pattern="*.wmf"/>
+ <alias type="image/x-wmf"/>
+ <alias type="image/x-win-metafile"/>
+ <alias type="application/x-wmf"/>
+ <alias type="application/wmf"/>
+ <alias type="application/x-msmetafile"/>
+ </mime-type>
+ <mime-type type="image/x-xbitmap">
+ <comment>XBM image</comment>
+ <comment xml:lang="ar">صورة XBM</comment>
+ <comment xml:lang="be@latin">Vyjava XBM</comment>
+ <comment xml:lang="bg">Изображение — XBM</comment>
+ <comment xml:lang="ca">imatge XBM</comment>
+ <comment xml:lang="cs">obrázek XBM</comment>
+ <comment xml:lang="da">XBM-billede</comment>
+ <comment xml:lang="de">XBM-Bild</comment>
+ <comment xml:lang="el">Εικόνα XBM</comment>
+ <comment xml:lang="en_GB">XBM image</comment>
+ <comment xml:lang="eo">XBM-bildo</comment>
+ <comment xml:lang="es">imagen XBM</comment>
+ <comment xml:lang="eu">XBM irudia</comment>
+ <comment xml:lang="fi">XBM-kuva</comment>
+ <comment xml:lang="fo">XBM mynd</comment>
+ <comment xml:lang="fr">image XBM</comment>
+ <comment xml:lang="ga">íomhá XBM</comment>
+ <comment xml:lang="gl">imaxe XBM</comment>
+ <comment xml:lang="he">תמונת XBM</comment>
+ <comment xml:lang="hr">XBM slika</comment>
+ <comment xml:lang="hu">XBM-kép</comment>
+ <comment xml:lang="ia">Imagine XBM</comment>
+ <comment xml:lang="id">Citra XBM</comment>
+ <comment xml:lang="it">Immagine XBM</comment>
+ <comment xml:lang="ja">XBM 画像</comment>
+ <comment xml:lang="kk">XBM суреті</comment>
+ <comment xml:lang="ko">XBM 그림</comment>
+ <comment xml:lang="lt">XBM paveikslėlis</comment>
+ <comment xml:lang="lv">XBM attēls</comment>
+ <comment xml:lang="nb">XBM-bilde</comment>
+ <comment xml:lang="nl">XBM-afbeelding</comment>
+ <comment xml:lang="nn">XBM-bilete</comment>
+ <comment xml:lang="oc">imatge XBM</comment>
+ <comment xml:lang="pl">Obraz XBM</comment>
+ <comment xml:lang="pt">imagem XBM</comment>
+ <comment xml:lang="pt_BR">Imagem XBM</comment>
+ <comment xml:lang="ro">Imagine XBM</comment>
+ <comment xml:lang="ru">Изображение XBM</comment>
+ <comment xml:lang="sk">Obrázok XBM</comment>
+ <comment xml:lang="sl">Slikovna datoteka XBM</comment>
+ <comment xml:lang="sq">Figurë XBM</comment>
+ <comment xml:lang="sr">ИксБМ слика</comment>
+ <comment xml:lang="sv">XBM-bild</comment>
+ <comment xml:lang="tr">XBM görüntüsü</comment>
+ <comment xml:lang="uk">зображення XBM</comment>
+ <comment xml:lang="vi">Ảnh XBM</comment>
+ <comment xml:lang="zh_CN">XBM 图像</comment>
+ <comment xml:lang="zh_TW">XBM 影像</comment>
+ <acronym>XBM</acronym>
+ <expanded-acronym>X BitMap</expanded-acronym>
+ <glob pattern="*.xbm"/>
+ </mime-type>
+ <mime-type type="image/x-xcf">
+ <comment>GIMP image</comment>
+ <comment xml:lang="ar">صورة GIMP</comment>
+ <comment xml:lang="be@latin">Vyjava GIMP</comment>
+ <comment xml:lang="bg">Изображение — GIMP</comment>
+ <comment xml:lang="ca">imatge de GIMP</comment>
+ <comment xml:lang="cs">obrázek GIMP</comment>
+ <comment xml:lang="da">GIMP-billede</comment>
+ <comment xml:lang="de">GIMP-Bild</comment>
+ <comment xml:lang="el">Εικόνα GIMP</comment>
+ <comment xml:lang="en_GB">GIMP image</comment>
+ <comment xml:lang="eo">GIMP-bildo</comment>
+ <comment xml:lang="es">imagen del GIMP</comment>
+ <comment xml:lang="eu">GIMP irudia</comment>
+ <comment xml:lang="fi">GIMP-kuva</comment>
+ <comment xml:lang="fo">GIMP mynd</comment>
+ <comment xml:lang="fr">image GIMP</comment>
+ <comment xml:lang="ga">íomhá GIMP</comment>
+ <comment xml:lang="gl">imaxe de GIMP</comment>
+ <comment xml:lang="he">תמונת GIMP</comment>
+ <comment xml:lang="hr">GIMP slika</comment>
+ <comment xml:lang="hu">GIMP-kép</comment>
+ <comment xml:lang="ia">Imagine GIMP</comment>
+ <comment xml:lang="id">Citra GIMP</comment>
+ <comment xml:lang="it">Immagine GIMP</comment>
+ <comment xml:lang="ja">GIMP 画像</comment>
+ <comment xml:lang="ka">GIMP გამოსახულება</comment>
+ <comment xml:lang="kk">GIMP суреті</comment>
+ <comment xml:lang="ko">GIMP 그림</comment>
+ <comment xml:lang="lt">GIMP paveikslėlis</comment>
+ <comment xml:lang="lv">GIMP attēls</comment>
+ <comment xml:lang="ms">Imej GIMP</comment>
+ <comment xml:lang="nb">GIMP-bilde</comment>
+ <comment xml:lang="nl">GIMP-afbeelding</comment>
+ <comment xml:lang="nn">GIMP-bilete</comment>
+ <comment xml:lang="oc">imatge GIMP</comment>
+ <comment xml:lang="pl">Obraz GIMP</comment>
+ <comment xml:lang="pt">imagem GIMP</comment>
+ <comment xml:lang="pt_BR">Imagem do GIMP</comment>
+ <comment xml:lang="ro">Imagine GIMP</comment>
+ <comment xml:lang="ru">Изображение GIMP</comment>
+ <comment xml:lang="sk">Obrázok GIMP</comment>
+ <comment xml:lang="sl">Slikovna datoteka GIMP</comment>
+ <comment xml:lang="sq">Figurë GIMP</comment>
+ <comment xml:lang="sr">Гимпова слика</comment>
+ <comment xml:lang="sv">GIMP-bild</comment>
+ <comment xml:lang="tr">GIMP görüntüsü</comment>
+ <comment xml:lang="uk">зображення GIMP</comment>
+ <comment xml:lang="vi">Ảnh GIMP</comment>
+ <comment xml:lang="zh_CN">GIMP 图像</comment>
+ <comment xml:lang="zh_TW">GIMP 影像</comment>
+ <glob pattern="*.xcf"/>
+ <magic priority="50">
+ <match value="gimp xcf file" type="string" offset="0"/>
+ <match value="gimp xcf v" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/x-gimp-gbr">
+ <comment>GIMP brush</comment>
+ <comment xml:lang="ca">pinzell de GIMP</comment>
+ <comment xml:lang="cs">štětec GIMP</comment>
+ <comment xml:lang="de">GIMP-Pinsel</comment>
+ <comment xml:lang="en_GB">GIMP brush</comment>
+ <comment xml:lang="es">pincel del GIMP</comment>
+ <comment xml:lang="hr">GIMP kist</comment>
+ <comment xml:lang="hu">GIMP ecset</comment>
+ <comment xml:lang="id">Kuas GIMP</comment>
+ <comment xml:lang="it">Pennello GIMP</comment>
+ <comment xml:lang="kk">GIMP бояу жаққышы</comment>
+ <comment xml:lang="ko">GIMP 붓</comment>
+ <comment xml:lang="pl">Pędzel programu GIMP</comment>
+ <comment xml:lang="pt_BR">Pincel do GIMP</comment>
+ <comment xml:lang="ru">Кисть GIMP</comment>
+ <comment xml:lang="sk">Štetec aplikácie GIMP</comment>
+ <comment xml:lang="sv">GIMP-pensel</comment>
+ <comment xml:lang="uk">пензель GIMP</comment>
+ <comment xml:lang="zh_CN">GIMP 笔刷</comment>
+ <comment xml:lang="zh_TW">GIMP 筆刷</comment>
+ <glob pattern="*.gbr"/>
+ <magic priority="50">
+ <match value="GIMP" type="string" offset="20"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/x-gimp-gih">
+ <comment>GIMP brush pipe</comment>
+ <comment xml:lang="ca">conducte del pinzell de GIMP</comment>
+ <comment xml:lang="cs">zřetězení štětců GIMP</comment>
+ <comment xml:lang="de">GIMP-Pinselanimation</comment>
+ <comment xml:lang="en_GB">GIMP brush pipe</comment>
+ <comment xml:lang="es">pincel animado del GIMP</comment>
+ <comment xml:lang="hr">GIMP valjkasti kist</comment>
+ <comment xml:lang="hu">GIMP ecsetcsatorna</comment>
+ <comment xml:lang="id">Pipa kuas GIMP</comment>
+ <comment xml:lang="it">Pipe pennello GIMP</comment>
+ <comment xml:lang="kk">GIMP бояу жаққыш түтігі</comment>
+ <comment xml:lang="ko">GIMP 붓 파이프</comment>
+ <comment xml:lang="pl">Potok pędzla programu GIMP</comment>
+ <comment xml:lang="pt_BR">Tubo de pincel do GIMP</comment>
+ <comment xml:lang="ru">Анимированная кисть GIMP</comment>
+ <comment xml:lang="sv">GIMP-penselrör</comment>
+ <comment xml:lang="uk">канал пензлів GIMP</comment>
+ <comment xml:lang="zh_CN">GIMP 管刷</comment>
+ <comment xml:lang="zh_TW">GIMP 筆刷導管</comment>
+ <glob pattern="*.gih"/>
+ </mime-type>
+ <mime-type type="image/x-gimp-pat">
+ <comment>GIMP pattern</comment>
+ <comment xml:lang="ca">patró de GIMP</comment>
+ <comment xml:lang="cs">vzorek GIMP</comment>
+ <comment xml:lang="de">GIMP-Muster</comment>
+ <comment xml:lang="en_GB">GIMP pattern</comment>
+ <comment xml:lang="es">patrón del GIMP</comment>
+ <comment xml:lang="hr">GIMP uzorak</comment>
+ <comment xml:lang="hu">GIMP minta</comment>
+ <comment xml:lang="id">Pola GIMP</comment>
+ <comment xml:lang="it">Motivo GIMP</comment>
+ <comment xml:lang="kk">GIMP оюы</comment>
+ <comment xml:lang="ko">GIMP 패턴</comment>
+ <comment xml:lang="pl">Deseń programu GIMP</comment>
+ <comment xml:lang="pt_BR">Textura do GIMP</comment>
+ <comment xml:lang="ru">Шаблон GIMP</comment>
+ <comment xml:lang="sk">Vzor aplikácie GIMP</comment>
+ <comment xml:lang="sv">GIMP-mönster</comment>
+ <comment xml:lang="uk">візерунок GIMP</comment>
+ <comment xml:lang="zh_CN">GIMP 图案</comment>
+ <comment xml:lang="zh_TW">GIMP 樣式</comment>
+ <glob pattern="*.pat"/>
+ <magic priority="50">
+ <match value="GPAT" type="string" offset="20"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/x-xfig">
+ <comment>XFig image</comment>
+ <comment xml:lang="ar">صورة XFig</comment>
+ <comment xml:lang="be@latin">Vyjava XFig</comment>
+ <comment xml:lang="bg">Изображение — XFig</comment>
+ <comment xml:lang="ca">imatge de XFig</comment>
+ <comment xml:lang="cs">obrázek XFig</comment>
+ <comment xml:lang="da">XFig-billede</comment>
+ <comment xml:lang="de">XFig-Bild</comment>
+ <comment xml:lang="el">Εικόνα XFig</comment>
+ <comment xml:lang="en_GB">XFig image</comment>
+ <comment xml:lang="eo">XFig-bildo</comment>
+ <comment xml:lang="es">imagen de XFig</comment>
+ <comment xml:lang="eu">XFig irudia</comment>
+ <comment xml:lang="fi">XFig-kuva</comment>
+ <comment xml:lang="fo">XFig mynd</comment>
+ <comment xml:lang="fr">image XFig</comment>
+ <comment xml:lang="ga">íomhá XFig</comment>
+ <comment xml:lang="gl">imaxe de XFig</comment>
+ <comment xml:lang="he">תמונת XFig</comment>
+ <comment xml:lang="hr">XFig slika</comment>
+ <comment xml:lang="hu">XFig-kép</comment>
+ <comment xml:lang="ia">Imagine XFig</comment>
+ <comment xml:lang="id">Citra XFig</comment>
+ <comment xml:lang="it">Immagine XFig</comment>
+ <comment xml:lang="ja">XFig 画像</comment>
+ <comment xml:lang="kk">XFig суреті</comment>
+ <comment xml:lang="ko">XFig 그림</comment>
+ <comment xml:lang="lt">XFig paveikslėlis</comment>
+ <comment xml:lang="lv">XFig attēls</comment>
+ <comment xml:lang="ms">Imej XFig</comment>
+ <comment xml:lang="nb">XFig-bilde</comment>
+ <comment xml:lang="nl">XFig-afbeelding</comment>
+ <comment xml:lang="nn">XFig-bilete</comment>
+ <comment xml:lang="oc">imatge XFig</comment>
+ <comment xml:lang="pl">Obraz XFig</comment>
+ <comment xml:lang="pt">imagem XFig</comment>
+ <comment xml:lang="pt_BR">Imagem do XFig</comment>
+ <comment xml:lang="ro">Imagine XFig</comment>
+ <comment xml:lang="ru">Изображение XFig</comment>
+ <comment xml:lang="sk">Obrázok XFig</comment>
+ <comment xml:lang="sl">Slikovna datoteka XFig</comment>
+ <comment xml:lang="sq">Figurë XFig</comment>
+ <comment xml:lang="sr">ИксФиг слика</comment>
+ <comment xml:lang="sv">XFig-bild</comment>
+ <comment xml:lang="tr">XFig görüntüsü</comment>
+ <comment xml:lang="uk">зображення XFig</comment>
+ <comment xml:lang="vi">Ảnh XFig</comment>
+ <comment xml:lang="zh_CN">XFig 图像</comment>
+ <comment xml:lang="zh_TW">XFig 影像</comment>
+ <glob pattern="*.fig"/>
+ <magic priority="50">
+ <match value="#FIG" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="image/x-xpixmap">
+ <comment>XPM image</comment>
+ <comment xml:lang="ar">صورة XPM</comment>
+ <comment xml:lang="be@latin">Vyjava XPM</comment>
+ <comment xml:lang="bg">Изображение — XPM</comment>
+ <comment xml:lang="ca">imatge XPM</comment>
+ <comment xml:lang="cs">obrázek XPM</comment>
+ <comment xml:lang="cy">Delwedd XPM</comment>
+ <comment xml:lang="da">XPM-billede</comment>
+ <comment xml:lang="de">XPM-Bild</comment>
+ <comment xml:lang="el">Εικόνα XPM</comment>
+ <comment xml:lang="en_GB">XPM image</comment>
+ <comment xml:lang="eo">XPM-bildo</comment>
+ <comment xml:lang="es">imagen XPM</comment>
+ <comment xml:lang="eu">XPM irudia</comment>
+ <comment xml:lang="fi">XPM-kuva</comment>
+ <comment xml:lang="fo">XPM mynd</comment>
+ <comment xml:lang="fr">image XPM</comment>
+ <comment xml:lang="ga">íomhá XPM</comment>
+ <comment xml:lang="gl">imaxe XPM</comment>
+ <comment xml:lang="he">תמונת XPM</comment>
+ <comment xml:lang="hr">XPM slika</comment>
+ <comment xml:lang="hu">XPM kép</comment>
+ <comment xml:lang="ia">Imagine XPM</comment>
+ <comment xml:lang="id">Citra XPM</comment>
+ <comment xml:lang="it">Immagine XPM</comment>
+ <comment xml:lang="ja">XPM 画像</comment>
+ <comment xml:lang="kk">XPM суреті</comment>
+ <comment xml:lang="ko">XPM 그림</comment>
+ <comment xml:lang="lt">XPM paveikslėlis</comment>
+ <comment xml:lang="lv">XPM attēls</comment>
+ <comment xml:lang="nb">XPM-bilde</comment>
+ <comment xml:lang="nl">XPM-afbeelding</comment>
+ <comment xml:lang="nn">XPM-bilete</comment>
+ <comment xml:lang="oc">imatge XPM</comment>
+ <comment xml:lang="pl">Obraz XPM</comment>
+ <comment xml:lang="pt">imagem XPM</comment>
+ <comment xml:lang="pt_BR">Imagem XPM</comment>
+ <comment xml:lang="ro">Imagine XPM</comment>
+ <comment xml:lang="ru">Изображение XPM</comment>
+ <comment xml:lang="sk">Obrázok XPM</comment>
+ <comment xml:lang="sl">Slikovna datoteka XPM</comment>
+ <comment xml:lang="sq">Figurë XPM</comment>
+ <comment xml:lang="sr">ИксПМ слика</comment>
+ <comment xml:lang="sv">XPM-bild</comment>
+ <comment xml:lang="tr">XPM görüntüsü</comment>
+ <comment xml:lang="uk">зображення XPM</comment>
+ <comment xml:lang="vi">Ảnh XPM</comment>
+ <comment xml:lang="zh_CN">XPM 图像</comment>
+ <comment xml:lang="zh_TW">XPM 影像</comment>
+ <acronym>XPM</acronym>
+ <expanded-acronym>X PixMap</expanded-acronym>
+ <magic priority="50">
+ <match value="/* XPM" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.xpm"/>
+ <alias type="image/x-xpm"/>
+ </mime-type>
+ <mime-type type="image/x-xwindowdump">
+ <comment>X window image</comment>
+ <comment xml:lang="ar">صورة X window</comment>
+ <comment xml:lang="az">X window rəsmi</comment>
+ <comment xml:lang="be@latin">Vyjava vakna X</comment>
+ <comment xml:lang="bg">Изображение — X Window</comment>
+ <comment xml:lang="ca">imatge de X window</comment>
+ <comment xml:lang="cs">obrázek X window</comment>
+ <comment xml:lang="cy">Delwedd ffenest X</comment>
+ <comment xml:lang="da">X-billede</comment>
+ <comment xml:lang="de">X-Window-Bild</comment>
+ <comment xml:lang="el">Εικόνα περιβάλλοντος X</comment>
+ <comment xml:lang="en_GB">X window image</comment>
+ <comment xml:lang="eo">bildo de X window</comment>
+ <comment xml:lang="es">imagen de ventana de X</comment>
+ <comment xml:lang="eu">X window irudia</comment>
+ <comment xml:lang="fi">X-ikkunakuva</comment>
+ <comment xml:lang="fo">X vindeyga mynd</comment>
+ <comment xml:lang="fr">image X window</comment>
+ <comment xml:lang="ga">íomhá X-windows</comment>
+ <comment xml:lang="gl">imaxe de X Window</comment>
+ <comment xml:lang="he">תמונת חלון של X</comment>
+ <comment xml:lang="hr">X window slika</comment>
+ <comment xml:lang="hu">X window-kép</comment>
+ <comment xml:lang="ia">Imagine X Window</comment>
+ <comment xml:lang="id">Citra X window</comment>
+ <comment xml:lang="it">Immagine X window</comment>
+ <comment xml:lang="ja">X window 画像</comment>
+ <comment xml:lang="kk">X window суреті</comment>
+ <comment xml:lang="ko">X 윈도 그림</comment>
+ <comment xml:lang="lt">X window paveikslėlis</comment>
+ <comment xml:lang="lv">X window attēls</comment>
+ <comment xml:lang="ms">Imej tetingkap X</comment>
+ <comment xml:lang="nb">X-Windows skjermbilde</comment>
+ <comment xml:lang="nl">X-window-afbeelding</comment>
+ <comment xml:lang="nn">X window bilete</comment>
+ <comment xml:lang="oc">imatge X window</comment>
+ <comment xml:lang="pl">Obraz X Window</comment>
+ <comment xml:lang="pt">imagem de janela X</comment>
+ <comment xml:lang="pt_BR">Imagem de janela do X</comment>
+ <comment xml:lang="ro">Imagine X window</comment>
+ <comment xml:lang="ru">Изображение X window</comment>
+ <comment xml:lang="sk">Obrázok X window</comment>
+ <comment xml:lang="sl">slika X oken</comment>
+ <comment xml:lang="sq">Figurë X window</comment>
+ <comment xml:lang="sr">слика Икс прозора</comment>
+ <comment xml:lang="sv">X-fönsterbild</comment>
+ <comment xml:lang="tr">X pencere görüntüsü</comment>
+ <comment xml:lang="uk">зображення X window</comment>
+ <comment xml:lang="vi">Ảnh cửa sổ X</comment>
+ <comment xml:lang="zh_CN">X Window 图像</comment>
+ <comment xml:lang="zh_TW">X window 影像</comment>
+ <glob pattern="*.xwd"/>
+ </mime-type>
+ <mime-type type="inode/blockdevice">
+ <comment>block device</comment>
+ <comment xml:lang="ar">جهاز كتلي</comment>
+ <comment xml:lang="ast">preséu de bloques</comment>
+ <comment xml:lang="be@latin">blokavaja pryłada</comment>
+ <comment xml:lang="bg">Блоково устройство</comment>
+ <comment xml:lang="ca">dispositiu de blocs</comment>
+ <comment xml:lang="cs">blokové zařízení</comment>
+ <comment xml:lang="da">blokenhed</comment>
+ <comment xml:lang="de">Blockorientiertes Gerät</comment>
+ <comment xml:lang="el">Συσκευή block</comment>
+ <comment xml:lang="en_GB">block device</comment>
+ <comment xml:lang="eo">bloka disponaĵo</comment>
+ <comment xml:lang="es">dispositivo de bloques</comment>
+ <comment xml:lang="eu">bloke-gailua</comment>
+ <comment xml:lang="fi">laitetiedosto</comment>
+ <comment xml:lang="fo">blokka tóleind</comment>
+ <comment xml:lang="fr">périphérique de blocs</comment>
+ <comment xml:lang="ga">gléas bloc</comment>
+ <comment xml:lang="gl">dispositivo de bloque</comment>
+ <comment xml:lang="he">התקן בלוק</comment>
+ <comment xml:lang="hr">Blokovski uređaj</comment>
+ <comment xml:lang="hu">blokkos eszköz</comment>
+ <comment xml:lang="ia">Dispositivo de blocos</comment>
+ <comment xml:lang="id">peranti blok</comment>
+ <comment xml:lang="it">Device a blocchi</comment>
+ <comment xml:lang="ja">ブロックデバイス</comment>
+ <comment xml:lang="kk">блоктық құрылғысы</comment>
+ <comment xml:lang="ko">블록 장치</comment>
+ <comment xml:lang="lt">blokinis įrenginys</comment>
+ <comment xml:lang="lv">bloka ierīce</comment>
+ <comment xml:lang="ms">Peranti blok</comment>
+ <comment xml:lang="nb">blokkenhet</comment>
+ <comment xml:lang="nl">blok-apparaat</comment>
+ <comment xml:lang="nn">blokk-eining</comment>
+ <comment xml:lang="oc">periferic de blòts</comment>
+ <comment xml:lang="pl">Urządzenie blokowe</comment>
+ <comment xml:lang="pt">dispositivo de bloco</comment>
+ <comment xml:lang="pt_BR">Dispositivo de bloco</comment>
+ <comment xml:lang="ro">dispozitiv bloc</comment>
+ <comment xml:lang="ru">Блочное устройство</comment>
+ <comment xml:lang="sk">Blokové zariadenie</comment>
+ <comment xml:lang="sl">bločna naprava</comment>
+ <comment xml:lang="sq">device me blloqe</comment>
+ <comment xml:lang="sr">блок уређај</comment>
+ <comment xml:lang="sv">blockenhet</comment>
+ <comment xml:lang="tr">blok aygıtı</comment>
+ <comment xml:lang="uk">блоковий пристрій</comment>
+ <comment xml:lang="vi">thiết bị khối</comment>
+ <comment xml:lang="zh_CN">块设备</comment>
+ <comment xml:lang="zh_TW">區塊裝置</comment>
+ </mime-type>
+ <mime-type type="inode/chardevice">
+ <comment>character device</comment>
+ <comment xml:lang="ar">جهاز حرفي</comment>
+ <comment xml:lang="ast">preséu de caráuteres</comment>
+ <comment xml:lang="be@latin">znakavaja pryłada</comment>
+ <comment xml:lang="bg">Символно устройство</comment>
+ <comment xml:lang="ca">dispositiu de caràcters</comment>
+ <comment xml:lang="cs">znakové zařízení</comment>
+ <comment xml:lang="da">tegnenhed</comment>
+ <comment xml:lang="de">Zeichenorientiertes Gerät</comment>
+ <comment xml:lang="el">Συσκευή χαρακτήρων</comment>
+ <comment xml:lang="en_GB">character device</comment>
+ <comment xml:lang="eo">signa disponaĵo</comment>
+ <comment xml:lang="es">dispositivo de caracteres</comment>
+ <comment xml:lang="eu">karaktereen gailua</comment>
+ <comment xml:lang="fi">merkkilaite</comment>
+ <comment xml:lang="fo">stavatóleind</comment>
+ <comment xml:lang="fr">périphérique de caractères</comment>
+ <comment xml:lang="ga">gléas carachtar</comment>
+ <comment xml:lang="gl">dispositivo de caracter</comment>
+ <comment xml:lang="he">התקן תכונה</comment>
+ <comment xml:lang="hr">Znakovni uređaj</comment>
+ <comment xml:lang="hu">karakteres eszköz</comment>
+ <comment xml:lang="ia">Dispositivo de characteres</comment>
+ <comment xml:lang="id">peranti karakter</comment>
+ <comment xml:lang="it">Device a caratteri</comment>
+ <comment xml:lang="ja">キャラクタデバイス</comment>
+ <comment xml:lang="kk">символдық құрылғысы</comment>
+ <comment xml:lang="ko">문자 장치</comment>
+ <comment xml:lang="lt">simbolinis įrenginys</comment>
+ <comment xml:lang="lv">rakstzīmju ierīce</comment>
+ <comment xml:lang="ms">Peranti aksara</comment>
+ <comment xml:lang="nb">tegnenhet</comment>
+ <comment xml:lang="nl">byte-apparaat</comment>
+ <comment xml:lang="nn">teikneining</comment>
+ <comment xml:lang="oc">periferic de caractèrs</comment>
+ <comment xml:lang="pl">Urządzenie znakowe</comment>
+ <comment xml:lang="pt">dispositivo de caracteres</comment>
+ <comment xml:lang="pt_BR">Dispositivo de caractere</comment>
+ <comment xml:lang="ro">dispozitiv caracter</comment>
+ <comment xml:lang="ru">Символьное устройство</comment>
+ <comment xml:lang="sk">Znakové zariadenie</comment>
+ <comment xml:lang="sl">znakovna naprava</comment>
+ <comment xml:lang="sq">device me karaktere</comment>
+ <comment xml:lang="sr">знаковни уређај</comment>
+ <comment xml:lang="sv">teckenenhet</comment>
+ <comment xml:lang="tr">karakter aygıtı</comment>
+ <comment xml:lang="uk">символьний пристрій</comment>
+ <comment xml:lang="vi">thiết bị ký tự</comment>
+ <comment xml:lang="zh_CN">字符设备</comment>
+ <comment xml:lang="zh_TW">字元裝置</comment>
+ </mime-type>
+ <mime-type type="inode/directory">
+ <comment>folder</comment>
+ <comment xml:lang="ar">مجلّد</comment>
+ <comment xml:lang="ast">carpeta</comment>
+ <comment xml:lang="be@latin">kataloh</comment>
+ <comment xml:lang="bg">Папка</comment>
+ <comment xml:lang="ca">carpeta</comment>
+ <comment xml:lang="cs">složka</comment>
+ <comment xml:lang="da">mappe</comment>
+ <comment xml:lang="de">Ordner</comment>
+ <comment xml:lang="el">Φάκελος</comment>
+ <comment xml:lang="en_GB">folder</comment>
+ <comment xml:lang="eo">dosierujo</comment>
+ <comment xml:lang="es">carpeta</comment>
+ <comment xml:lang="eu">karpeta</comment>
+ <comment xml:lang="fi">kansio</comment>
+ <comment xml:lang="fo">mappa</comment>
+ <comment xml:lang="fr">dossier</comment>
+ <comment xml:lang="ga">fillteán</comment>
+ <comment xml:lang="gl">cartafol</comment>
+ <comment xml:lang="he">תיקייה</comment>
+ <comment xml:lang="hr">Mapa</comment>
+ <comment xml:lang="hu">mappa</comment>
+ <comment xml:lang="ia">Dossier</comment>
+ <comment xml:lang="id">folder</comment>
+ <comment xml:lang="it">Cartella</comment>
+ <comment xml:lang="ja">フォルダー</comment>
+ <comment xml:lang="kk">бума</comment>
+ <comment xml:lang="ko">폴더</comment>
+ <comment xml:lang="lt">aplankas</comment>
+ <comment xml:lang="lv">mape</comment>
+ <comment xml:lang="ms">Folder</comment>
+ <comment xml:lang="nb">mappe</comment>
+ <comment xml:lang="nl">map</comment>
+ <comment xml:lang="nn">mappe</comment>
+ <comment xml:lang="oc">dorsièr</comment>
+ <comment xml:lang="pl">Katalog</comment>
+ <comment xml:lang="pt">pasta</comment>
+ <comment xml:lang="pt_BR">Pasta</comment>
+ <comment xml:lang="ro">dosar</comment>
+ <comment xml:lang="ru">Папка</comment>
+ <comment xml:lang="sk">Priečinok</comment>
+ <comment xml:lang="sl">mapa</comment>
+ <comment xml:lang="sq">Kartelë</comment>
+ <comment xml:lang="sr">фасцикла</comment>
+ <comment xml:lang="sv">mapp</comment>
+ <comment xml:lang="tr">dizin</comment>
+ <comment xml:lang="uk">тека</comment>
+ <comment xml:lang="vi">thư mục</comment>
+ <comment xml:lang="zh_CN">文件夹</comment>
+ <comment xml:lang="zh_TW">資料夾</comment>
+ <generic-icon name="folder"/>
+ <alias type="x-directory/normal"/>
+ </mime-type>
+ <mime-type type="inode/fifo">
+ <comment>pipe</comment>
+ <comment xml:lang="ar">إنبوب</comment>
+ <comment xml:lang="be@latin">kanvejer</comment>
+ <comment xml:lang="bg">Конвейер</comment>
+ <comment xml:lang="ca">conducte</comment>
+ <comment xml:lang="cs">roura</comment>
+ <comment xml:lang="da">datakanal</comment>
+ <comment xml:lang="de">Pipe</comment>
+ <comment xml:lang="el">Διοχέτευση</comment>
+ <comment xml:lang="en_GB">pipe</comment>
+ <comment xml:lang="eo">dukto</comment>
+ <comment xml:lang="es">tubería</comment>
+ <comment xml:lang="eu">kanalizazioa</comment>
+ <comment xml:lang="fi">putki</comment>
+ <comment xml:lang="fo">rør</comment>
+ <comment xml:lang="fr">tube</comment>
+ <comment xml:lang="ga">píopa</comment>
+ <comment xml:lang="gl">tubería</comment>
+ <comment xml:lang="he">צינור</comment>
+ <comment xml:lang="hr">Slivnik</comment>
+ <comment xml:lang="hu">adatcsatorna</comment>
+ <comment xml:lang="ia">Tubo</comment>
+ <comment xml:lang="id">pipa</comment>
+ <comment xml:lang="it">Pipe</comment>
+ <comment xml:lang="ja">パイプ</comment>
+ <comment xml:lang="kk">арна</comment>
+ <comment xml:lang="ko">파이프</comment>
+ <comment xml:lang="lt">konvejeris</comment>
+ <comment xml:lang="lv">programmkanāls</comment>
+ <comment xml:lang="ms">Paip</comment>
+ <comment xml:lang="nb">rør</comment>
+ <comment xml:lang="nl">pijp</comment>
+ <comment xml:lang="nn">røyr</comment>
+ <comment xml:lang="oc">tub</comment>
+ <comment xml:lang="pl">Potok</comment>
+ <comment xml:lang="pt">canal</comment>
+ <comment xml:lang="pt_BR">Pipe</comment>
+ <comment xml:lang="ro">canal pipe</comment>
+ <comment xml:lang="ru">Канал</comment>
+ <comment xml:lang="sk">Rúra</comment>
+ <comment xml:lang="sl">cev</comment>
+ <comment xml:lang="sq">Pipe</comment>
+ <comment xml:lang="sr">спојка</comment>
+ <comment xml:lang="sv">rör</comment>
+ <comment xml:lang="tr">boru</comment>
+ <comment xml:lang="uk">канал</comment>
+ <comment xml:lang="vi">ống dẫn</comment>
+ <comment xml:lang="zh_CN">管道</comment>
+ <comment xml:lang="zh_TW">管線</comment>
+ </mime-type>
+ <mime-type type="inode/mount-point">
+ <comment>mount point</comment>
+ <comment xml:lang="ar">نقطة الوصْل</comment>
+ <comment xml:lang="ast">puntu de montaxe</comment>
+ <comment xml:lang="be@latin">punkt mantavańnia</comment>
+ <comment xml:lang="bg">Точка на монтиране</comment>
+ <comment xml:lang="ca">punt de muntatge</comment>
+ <comment xml:lang="cs">přípojné místo</comment>
+ <comment xml:lang="da">monteringspunkt</comment>
+ <comment xml:lang="de">Einhängepunkt</comment>
+ <comment xml:lang="el">Σημείο προσάρτησης</comment>
+ <comment xml:lang="en_GB">mount point</comment>
+ <comment xml:lang="eo">surmetingo</comment>
+ <comment xml:lang="es">punto de montaje</comment>
+ <comment xml:lang="eu">muntatze-puntua</comment>
+ <comment xml:lang="fi">liitospiste</comment>
+ <comment xml:lang="fo">ísetingarpunkt</comment>
+ <comment xml:lang="fr">point d'accès</comment>
+ <comment xml:lang="ga">pointe feistithe</comment>
+ <comment xml:lang="gl">punto de montaxe</comment>
+ <comment xml:lang="he">נקודת עיגון</comment>
+ <comment xml:lang="hr">Točka montiranja</comment>
+ <comment xml:lang="hu">csatolási pont</comment>
+ <comment xml:lang="ia">Puncto de montage</comment>
+ <comment xml:lang="id">titik mount</comment>
+ <comment xml:lang="it">Punto di mount</comment>
+ <comment xml:lang="ja">マウントポイント</comment>
+ <comment xml:lang="kk">тіркеу нүктесі</comment>
+ <comment xml:lang="ko">마운트 위치</comment>
+ <comment xml:lang="lt">prijungimo taškas</comment>
+ <comment xml:lang="lv">montēšanas punkts</comment>
+ <comment xml:lang="ms">Titik lekapan</comment>
+ <comment xml:lang="nb">monteringspunkt</comment>
+ <comment xml:lang="nl">aankoppelingspunt</comment>
+ <comment xml:lang="nn">monteringspunkt</comment>
+ <comment xml:lang="oc">punt d'accès</comment>
+ <comment xml:lang="pl">Punkt montowania</comment>
+ <comment xml:lang="pt">ponto de montagem</comment>
+ <comment xml:lang="pt_BR">Ponto de montagem</comment>
+ <comment xml:lang="ro">loc montare</comment>
+ <comment xml:lang="ru">Точка монтирования</comment>
+ <comment xml:lang="sk">Miesto pripojenia</comment>
+ <comment xml:lang="sl">priklopna točka</comment>
+ <comment xml:lang="sq">Pikë montimi</comment>
+ <comment xml:lang="sr">тачка прикључења</comment>
+ <comment xml:lang="sv">monteringspunkt</comment>
+ <comment xml:lang="tr">bağlama noktası</comment>
+ <comment xml:lang="uk">точка монтування</comment>
+ <comment xml:lang="vi">điểm lắp</comment>
+ <comment xml:lang="zh_CN">挂载点</comment>
+ <comment xml:lang="zh_TW">掛載點</comment>
+ <sub-class-of type="inode/directory"/>
+ </mime-type>
+ <mime-type type="inode/socket">
+ <comment>socket</comment>
+ <comment xml:lang="ar">مقبس</comment>
+ <comment xml:lang="be@latin">sokiet</comment>
+ <comment xml:lang="bg">Гнездо</comment>
+ <comment xml:lang="ca">sòcol</comment>
+ <comment xml:lang="cs">socket</comment>
+ <comment xml:lang="da">sokkel</comment>
+ <comment xml:lang="de">Socket</comment>
+ <comment xml:lang="el">Υποδοχή</comment>
+ <comment xml:lang="en_GB">socket</comment>
+ <comment xml:lang="eo">kontaktoskatolo</comment>
+ <comment xml:lang="es">socket</comment>
+ <comment xml:lang="eu">socketa</comment>
+ <comment xml:lang="fi">pistoke</comment>
+ <comment xml:lang="fo">sokkul</comment>
+ <comment xml:lang="fr">connecteur réseau</comment>
+ <comment xml:lang="ga">soicéad</comment>
+ <comment xml:lang="gl">socket</comment>
+ <comment xml:lang="he">נקודת חיבור</comment>
+ <comment xml:lang="hr">Priključnica</comment>
+ <comment xml:lang="hu">illesztőpont</comment>
+ <comment xml:lang="ia">Socket</comment>
+ <comment xml:lang="id">soket</comment>
+ <comment xml:lang="it">Socket</comment>
+ <comment xml:lang="ja">ソケット</comment>
+ <comment xml:lang="kk">сокет</comment>
+ <comment xml:lang="ko">소켓</comment>
+ <comment xml:lang="lt">lizdas</comment>
+ <comment xml:lang="lv">sokets</comment>
+ <comment xml:lang="ms">Soket</comment>
+ <comment xml:lang="nb">plugg</comment>
+ <comment xml:lang="nl">socket</comment>
+ <comment xml:lang="nn">sokkel</comment>
+ <comment xml:lang="oc">connector ret</comment>
+ <comment xml:lang="pl">Gniazdo</comment>
+ <comment xml:lang="pt">tomada</comment>
+ <comment xml:lang="pt_BR">Socket</comment>
+ <comment xml:lang="ro">socket</comment>
+ <comment xml:lang="ru">Сокет</comment>
+ <comment xml:lang="sk">Soket</comment>
+ <comment xml:lang="sl">vtič</comment>
+ <comment xml:lang="sq">Socket</comment>
+ <comment xml:lang="sr">прикључница</comment>
+ <comment xml:lang="sv">uttag</comment>
+ <comment xml:lang="tr">soket</comment>
+ <comment xml:lang="uk">сокет</comment>
+ <comment xml:lang="vi">ổ cắm</comment>
+ <comment xml:lang="zh_CN">套接字</comment>
+ <comment xml:lang="zh_TW">socket</comment>
+ </mime-type>
+ <mime-type type="inode/symlink">
+ <comment>symbolic link</comment>
+ <comment xml:lang="ar">وصلة رمزية</comment>
+ <comment xml:lang="ast">enllaz simbólicu</comment>
+ <comment xml:lang="az">simvolik körpü</comment>
+ <comment xml:lang="be@latin">symbalnaja spasyłka</comment>
+ <comment xml:lang="bg">Символна връзка</comment>
+ <comment xml:lang="ca">enllaç simbòlic</comment>
+ <comment xml:lang="cs">symbolický odkaz</comment>
+ <comment xml:lang="cy">cyswllt symbolaidd</comment>
+ <comment xml:lang="da">symbolsk henvisning</comment>
+ <comment xml:lang="de">Symbolische Verknüpfung</comment>
+ <comment xml:lang="el">Συμβολικός σύνδεσμος</comment>
+ <comment xml:lang="en_GB">symbolic link</comment>
+ <comment xml:lang="eo">simbola ligilo</comment>
+ <comment xml:lang="es">enlace simbólico</comment>
+ <comment xml:lang="eu">esteka sinbolikoa</comment>
+ <comment xml:lang="fi">symbolinen linkki</comment>
+ <comment xml:lang="fo">tykislig leinkja</comment>
+ <comment xml:lang="fr">lien symbolique</comment>
+ <comment xml:lang="ga">nasc siombalach</comment>
+ <comment xml:lang="gl">ligazón simbólica</comment>
+ <comment xml:lang="he">קישור סימבולי</comment>
+ <comment xml:lang="hr">Simbolička poveznica</comment>
+ <comment xml:lang="hu">szimbolikus link</comment>
+ <comment xml:lang="ia">Ligamine symbolic</comment>
+ <comment xml:lang="id">taut simbolik</comment>
+ <comment xml:lang="it">Collegamento simbolico</comment>
+ <comment xml:lang="ja">シンボリックリンク</comment>
+ <comment xml:lang="ka">სიმბოლური ბმული</comment>
+ <comment xml:lang="kk">символдық сілтеме</comment>
+ <comment xml:lang="ko">심볼릭 링크</comment>
+ <comment xml:lang="lt">simbolinė nuoroda</comment>
+ <comment xml:lang="lv">simboliskā saite</comment>
+ <comment xml:lang="ms">Pautan simbolik</comment>
+ <comment xml:lang="nb">symbolsk lenke</comment>
+ <comment xml:lang="nl">symbolische koppeling</comment>
+ <comment xml:lang="nn">symbolsk lenkje</comment>
+ <comment xml:lang="oc">ligam simbolic</comment>
+ <comment xml:lang="pl">Dowiązanie symboliczne</comment>
+ <comment xml:lang="pt">ligação simbólica</comment>
+ <comment xml:lang="pt_BR">Ligação simbólica</comment>
+ <comment xml:lang="ro">legătură simbolică</comment>
+ <comment xml:lang="ru">Символьная ссылка</comment>
+ <comment xml:lang="sk">Symbolický odkaz</comment>
+ <comment xml:lang="sl">simbolna povezava</comment>
+ <comment xml:lang="sq">Lidhje simbolike</comment>
+ <comment xml:lang="sr">симболичка веза</comment>
+ <comment xml:lang="sv">symbolisk länk</comment>
+ <comment xml:lang="tr">sembolik bağlantı</comment>
+ <comment xml:lang="uk">символічне посилання</comment>
+ <comment xml:lang="vi">liên kết tượng trưng</comment>
+ <comment xml:lang="zh_CN">符号链接</comment>
+ <comment xml:lang="zh_TW">符號鏈結</comment>
+ </mime-type>
+ <mime-type type="message/delivery-status">
+ <comment>mail delivery report</comment>
+ <comment xml:lang="ar">تقرير تسليم البريد</comment>
+ <comment xml:lang="az">poçt yollama raportu</comment>
+ <comment xml:lang="be@latin">rapart ab dastaŭcy pošty</comment>
+ <comment xml:lang="bg">Отчет за пристигналата поща</comment>
+ <comment xml:lang="ca">informe de lliurament de correu</comment>
+ <comment xml:lang="cs">zpráva o doručení pošty</comment>
+ <comment xml:lang="cy">Adroddiad trosgludo post</comment>
+ <comment xml:lang="da">postleveringsrapport</comment>
+ <comment xml:lang="de">E-Mail-Zustellungsbericht</comment>
+ <comment xml:lang="el">Αναφορά παράδοσης μηνύματος</comment>
+ <comment xml:lang="en_GB">mail delivery report</comment>
+ <comment xml:lang="eo">raporto pri transdono de retpoŝto</comment>
+ <comment xml:lang="es">informe de entrega de correo</comment>
+ <comment xml:lang="eu">posta banaketako txostena</comment>
+ <comment xml:lang="fi">viestin jakeluilmoitus</comment>
+ <comment xml:lang="fo">post útberingarfrásøgn</comment>
+ <comment xml:lang="fr">rapport de livraison de courriels</comment>
+ <comment xml:lang="ga">tuairisc sheachadta r-phoist</comment>
+ <comment xml:lang="gl">informe de entrega de correo</comment>
+ <comment xml:lang="he">דוח העברת דואר</comment>
+ <comment xml:lang="hr">Izvještaj dostave pošte</comment>
+ <comment xml:lang="hu">jelentés levélkézbesítésről</comment>
+ <comment xml:lang="ia">Reporto de livration de e-mail</comment>
+ <comment xml:lang="id">laporan pengantaran surat</comment>
+ <comment xml:lang="it">Rapporto di consegna posta</comment>
+ <comment xml:lang="ja">メール配送ポート</comment>
+ <comment xml:lang="kk">пошта жеткізілгені туралы отчет</comment>
+ <comment xml:lang="ko">메일 배달 보고서</comment>
+ <comment xml:lang="lt">pašto pristatymo ataskaita</comment>
+ <comment xml:lang="lv">pasta piegādes atskaite</comment>
+ <comment xml:lang="ms">Laporan penghantaran mel</comment>
+ <comment xml:lang="nb">e-postleveranserapport</comment>
+ <comment xml:lang="nl">e-mail-bezorgingsbericht</comment>
+ <comment xml:lang="nn">e-post-leveringsrapport</comment>
+ <comment xml:lang="oc">rapòrt de liurason de corrièrs electronics</comment>
+ <comment xml:lang="pl">Raport z dostarczenia poczty</comment>
+ <comment xml:lang="pt">relatório de entrega de email</comment>
+ <comment xml:lang="pt_BR">Relatório de entrega de correspondência</comment>
+ <comment xml:lang="ro">raport de trimitere email</comment>
+ <comment xml:lang="ru">Отчёт о доставке сообщения</comment>
+ <comment xml:lang="sk">Správa o doručení pošty</comment>
+ <comment xml:lang="sl">poročilo dostave pošte</comment>
+ <comment xml:lang="sq">Raport mbi dorëzimin e mesazhit</comment>
+ <comment xml:lang="sr">извештај доставе поруке</comment>
+ <comment xml:lang="sv">e-postleveransrapport</comment>
+ <comment xml:lang="tr">posta iletim raporu</comment>
+ <comment xml:lang="uk">звіт про доставку пошти</comment>
+ <comment xml:lang="vi">thông báo phát thư</comment>
+ <comment xml:lang="zh_CN">邮件投递报告</comment>
+ <comment xml:lang="zh_TW">郵件寄送回報</comment>
+ <generic-icon name="text-x-generic"/>
+ <sub-class-of type="text/plain"/>
+ </mime-type>
+ <mime-type type="message/disposition-notification">
+ <comment>mail disposition report</comment>
+ <comment xml:lang="ar">تقرير ترتيب البريد</comment>
+ <comment xml:lang="az">poçt qayıtma raportu</comment>
+ <comment xml:lang="be@latin">rapart ab raźmiaščeńni pošty</comment>
+ <comment xml:lang="bg">Отчет за състоянието на пощата</comment>
+ <comment xml:lang="ca">informe de disposició de correu</comment>
+ <comment xml:lang="cs">zpráva o předání pošty</comment>
+ <comment xml:lang="cy">adroddiad ffurf post</comment>
+ <comment xml:lang="da">postdisponeringsrapport</comment>
+ <comment xml:lang="de">E-Mail-Übertragungsbericht</comment>
+ <comment xml:lang="el">Αναφορά διάθεσης μηνύματος</comment>
+ <comment xml:lang="en_GB">mail disposition report</comment>
+ <comment xml:lang="eo">raporto pri dispono de retpoŝto</comment>
+ <comment xml:lang="es">informe de disposición de correo</comment>
+ <comment xml:lang="eu">posta joerako txostena</comment>
+ <comment xml:lang="fi">viestin kuittausilmoitus</comment>
+ <comment xml:lang="fo">post avhendingarfrásøgn</comment>
+ <comment xml:lang="fr">rapport de disposition de courriels</comment>
+ <comment xml:lang="ga">tuairisc chóirithe r-phoist</comment>
+ <comment xml:lang="gl">informe de disposición de correo</comment>
+ <comment xml:lang="he">דוח אספקת דואר</comment>
+ <comment xml:lang="hr">Izvještaj smještaja e-pošte</comment>
+ <comment xml:lang="hu">jelentés levélkidobásról</comment>
+ <comment xml:lang="ia">Reporto de disposition de e-mail</comment>
+ <comment xml:lang="id">laporan disposisi surat</comment>
+ <comment xml:lang="it">Rapporto di disposizione posta</comment>
+ <comment xml:lang="ja">メール停止レポート</comment>
+ <comment xml:lang="kk">пошта жылжытылғаны туралы отчет</comment>
+ <comment xml:lang="ko">메일 처리 보고서</comment>
+ <comment xml:lang="lt">pašto charakteristikos ataskaita</comment>
+ <comment xml:lang="lv">pasta izvietojuma atskaite</comment>
+ <comment xml:lang="ms">Laporan pelupusan mel</comment>
+ <comment xml:lang="nb">e-postdispositionsrapport</comment>
+ <comment xml:lang="nl">e-mail-plaatsingsbericht</comment>
+ <comment xml:lang="nn">e-post-disposisjonsrapport</comment>
+ <comment xml:lang="oc">rapòrt de disposicion de corrièrs electronics</comment>
+ <comment xml:lang="pl">Raport z wysyłania poczty</comment>
+ <comment xml:lang="pt">relatório de disposição de email</comment>
+ <comment xml:lang="pt_BR">Relatório de disposição de correspondência</comment>
+ <comment xml:lang="ro">confirmare primire email</comment>
+ <comment xml:lang="ru">Отчёт о перемещении почты</comment>
+ <comment xml:lang="sk">Správa o odovzdaní pošty</comment>
+ <comment xml:lang="sl">poročilo razporeditve pošte</comment>
+ <comment xml:lang="sq">Raport mbi njoftimin e mesazhit</comment>
+ <comment xml:lang="sr">извештај слања поруке</comment>
+ <comment xml:lang="sv">e-postdispositionsrapport</comment>
+ <comment xml:lang="tr">posta silinme raporu</comment>
+ <comment xml:lang="uk">звіт про розташування пошти</comment>
+ <comment xml:lang="vi">thông báo chuyển nhượng thư</comment>
+ <comment xml:lang="zh_CN">邮件接收报告</comment>
+ <comment xml:lang="zh_TW">郵件處置回報</comment>
+ <generic-icon name="text-x-generic"/>
+ <sub-class-of type="text/plain"/>
+ </mime-type>
+ <mime-type type="message/external-body">
+ <comment>reference to remote file</comment>
+ <comment xml:lang="ar">مرجع إلى ملف بعيد</comment>
+ <comment xml:lang="az">uzaq fayla göstəriş</comment>
+ <comment xml:lang="be@latin">spasyłka da addalenaha fajłu</comment>
+ <comment xml:lang="bg">Препратка към отдалечен файл</comment>
+ <comment xml:lang="ca">referència a fitxer remot</comment>
+ <comment xml:lang="cs">odkaz na vzdálený soubor</comment>
+ <comment xml:lang="cy">cyfeiriad at ffeil bell</comment>
+ <comment xml:lang="da">reference til fjern fil</comment>
+ <comment xml:lang="de">Verweis auf entfernte Datei</comment>
+ <comment xml:lang="el">Αναφορά σε απομακρυσμένο αρχείο</comment>
+ <comment xml:lang="en_GB">reference to remote file</comment>
+ <comment xml:lang="eo">referenco al fora dosiero</comment>
+ <comment xml:lang="es">referencia a un archivo remoto</comment>
+ <comment xml:lang="eu">erreferentzia urruneko fitxategiari</comment>
+ <comment xml:lang="fi">viittaus etätiedostoon</comment>
+ <comment xml:lang="fo">tilvísing til fjarfílu</comment>
+ <comment xml:lang="fr">référence au fichier distant</comment>
+ <comment xml:lang="ga">tagairt do chomhad cianda</comment>
+ <comment xml:lang="gl">referencia a un ficheiro remoto</comment>
+ <comment xml:lang="he">התיחסות לקובץ מרוחק</comment>
+ <comment xml:lang="hr">Preporuka na udaljenu datoteku</comment>
+ <comment xml:lang="hu">hivatkozás távoli fájlra</comment>
+ <comment xml:lang="ia">Referentia a un file remote</comment>
+ <comment xml:lang="id">referensi ke berkas jarak jauh</comment>
+ <comment xml:lang="it">Riferimento a file remoto</comment>
+ <comment xml:lang="ja">リモートファイルへの参照</comment>
+ <comment xml:lang="kk">қашықтағы файлға сілтеме</comment>
+ <comment xml:lang="ko">원격 파일 참조</comment>
+ <comment xml:lang="lt">nuoroda į nutolusį failą</comment>
+ <comment xml:lang="lv">norāde uz attālinātu datni</comment>
+ <comment xml:lang="ms">Rujukan ke fail jauh</comment>
+ <comment xml:lang="nb">referanse til ekstern fil</comment>
+ <comment xml:lang="nl">verwijzing naar bestand op afstand</comment>
+ <comment xml:lang="nn">referanse til fil over nettverk</comment>
+ <comment xml:lang="oc">referéncia al fichièr distant</comment>
+ <comment xml:lang="pl">Odwołanie do pliku zdalnego</comment>
+ <comment xml:lang="pt">referência a um ficheiro remoto</comment>
+ <comment xml:lang="pt_BR">Referência para arquivo remoto</comment>
+ <comment xml:lang="ro">referință fișier la distanță</comment>
+ <comment xml:lang="ru">Ссылка на удалённый файл</comment>
+ <comment xml:lang="sk">Odkaz na vzdialený súbor</comment>
+ <comment xml:lang="sl">sklic do oddaljene datoteke</comment>
+ <comment xml:lang="sq">Referim për tek file në distancë</comment>
+ <comment xml:lang="sr">упута на удаљену датотеку</comment>
+ <comment xml:lang="sv">referens till fjärrfil</comment>
+ <comment xml:lang="tr">uzaktaki dosyaya başvuru</comment>
+ <comment xml:lang="uk">посилання на віддалений файл</comment>
+ <comment xml:lang="vi">tham chiếu đến tập tin ở xa</comment>
+ <comment xml:lang="zh_CN">到远程文件的引用</comment>
+ <comment xml:lang="zh_TW">遠端檔案的參照</comment>
+ <generic-icon name="text-x-generic"/>
+ </mime-type>
+ <mime-type type="message/news">
+ <comment>Usenet news message</comment>
+ <comment xml:lang="ar">رسالة أخبار Usenet</comment>
+ <comment xml:lang="az">Usenet xəbərlər ismarışı</comment>
+ <comment xml:lang="be@latin">Navina Usenet</comment>
+ <comment xml:lang="bg">Съобщение — Usenet</comment>
+ <comment xml:lang="ca">missatge de notícies Usenet</comment>
+ <comment xml:lang="cs">příspěvek do diskusních skupin Usenet</comment>
+ <comment xml:lang="cy">Neges newyddion Usenet</comment>
+ <comment xml:lang="da">Usenetnyhedsmeddelelse</comment>
+ <comment xml:lang="de">Usenet-News-Nachricht</comment>
+ <comment xml:lang="el">Μήνυμα ομάδων συζητήσεων Usenet</comment>
+ <comment xml:lang="en_GB">Usenet news message</comment>
+ <comment xml:lang="eo">novaĵmesaĝo de Usenet</comment>
+ <comment xml:lang="es">mensaje de noticias de Usenet</comment>
+ <comment xml:lang="eu">Usenet berrien mezua</comment>
+ <comment xml:lang="fi">nyyssiviesti</comment>
+ <comment xml:lang="fo">Usenet news boð</comment>
+ <comment xml:lang="fr">message de groupe d'échange Usenet</comment>
+ <comment xml:lang="ga">teachtaireacht nuachta Usenet</comment>
+ <comment xml:lang="gl">mensaxes de noticias de Usenet</comment>
+ <comment xml:lang="he">הודעת חדשות של Usenet</comment>
+ <comment xml:lang="hr">Usenet poruka novosti</comment>
+ <comment xml:lang="hu">USENET-hírcsoportüzenet</comment>
+ <comment xml:lang="ia">Message de gruppo Usenet</comment>
+ <comment xml:lang="id">Pesan berita Usenet</comment>
+ <comment xml:lang="it">Messaggio news Usenet</comment>
+ <comment xml:lang="ja">Usenet news メッセージ</comment>
+ <comment xml:lang="kk">Usenet жаңалық мәлімдемесі</comment>
+ <comment xml:lang="ko">유즈넷 뉴스 메시지</comment>
+ <comment xml:lang="lt">Usenet naujienų žinutė</comment>
+ <comment xml:lang="lv">Usenet jaunumu ziņojums</comment>
+ <comment xml:lang="ms">Mesej berita USENET</comment>
+ <comment xml:lang="nb">Usenet nyhetsmelding</comment>
+ <comment xml:lang="nl">Usenet-nieuwsbericht</comment>
+ <comment xml:lang="nn">USENET diskusjonsmelding</comment>
+ <comment xml:lang="oc">messatge de grop d'escambi Usenet</comment>
+ <comment xml:lang="pl">Wiadomość grupy dyskusyjnej</comment>
+ <comment xml:lang="pt">mensagem de notícias Usenet</comment>
+ <comment xml:lang="pt_BR">Mensagem de notícias da Usenet</comment>
+ <comment xml:lang="ro">Mesaj Usenet de știri </comment>
+ <comment xml:lang="ru">Новостное сообщение Usenet</comment>
+ <comment xml:lang="sk">Príspevok do diskusných skupín Usenet</comment>
+ <comment xml:lang="sl">novičarsko sporočilo Usenet</comment>
+ <comment xml:lang="sq">Mesazh lajmesh Usenet</comment>
+ <comment xml:lang="sr">Порука новости Јузнета</comment>
+ <comment xml:lang="sv">Usenet-diskussionsgruppsmeddelande</comment>
+ <comment xml:lang="tr">Usenet haber iletisi</comment>
+ <comment xml:lang="uk">повідомлення новин Usenet</comment>
+ <comment xml:lang="vi">Thông điệp tin tức USENET</comment>
+ <comment xml:lang="zh_CN">Usenet 新闻信</comment>
+ <comment xml:lang="zh_TW">Usenet 新聞訊息</comment>
+ <generic-icon name="text-x-generic"/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="Article" type="string" offset="0"/>
+ <match value="Path:" type="string" offset="0"/>
+ <match value="Xref:" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="message/partial">
+ <comment>partial email message</comment>
+ <comment xml:lang="ar">رسالة البريد الإلكتروني الجزئية</comment>
+ <comment xml:lang="az">qismi poçt ismarışı</comment>
+ <comment xml:lang="be@latin">niapoŭny list email</comment>
+ <comment xml:lang="bg">Част от електронно писмо</comment>
+ <comment xml:lang="ca">missatge de correu electrònic parcial</comment>
+ <comment xml:lang="cs">částečná e-mailová zpráva</comment>
+ <comment xml:lang="cy">darn o neges e-bost</comment>
+ <comment xml:lang="da">delvis postmeddelelse</comment>
+ <comment xml:lang="de">E-Mail-Nachrichtenfragment</comment>
+ <comment xml:lang="el">Τμηματικό ηλ. μήνυμα</comment>
+ <comment xml:lang="en_GB">partial email message</comment>
+ <comment xml:lang="eo">parta retpoŝta mesaĝo</comment>
+ <comment xml:lang="es">mensaje de correo electrónico parcial</comment>
+ <comment xml:lang="eu">posta mezu partziala</comment>
+ <comment xml:lang="fi">osittainen sähköpostiviesti</comment>
+ <comment xml:lang="fr">message partiel de courriel</comment>
+ <comment xml:lang="ga">teachtaireacht ríomhphoist neamhiomlán</comment>
+ <comment xml:lang="gl">mensaxe de correo electrónico parcial</comment>
+ <comment xml:lang="he">מסר דוא״ל חלקי</comment>
+ <comment xml:lang="hr">Nepotpuna poruka e-pošte</comment>
+ <comment xml:lang="hu">részleges elektronikus levél</comment>
+ <comment xml:lang="ia">Message de e-mail partial</comment>
+ <comment xml:lang="id">pesan email sebagian</comment>
+ <comment xml:lang="it">Messaggio email parziale</comment>
+ <comment xml:lang="ja">部分メールメッセージ</comment>
+ <comment xml:lang="kk">электронды поштаның үзінді мәлімдемесі</comment>
+ <comment xml:lang="ko">전자 우편 메시지 일부</comment>
+ <comment xml:lang="lt">nepilnas el. laiškas</comment>
+ <comment xml:lang="lv">daļēja e-pasta vēstule</comment>
+ <comment xml:lang="ms">Bahagian mesej emel</comment>
+ <comment xml:lang="nb">del av e-postmelding</comment>
+ <comment xml:lang="nl">gedeeltelijk e-mailbericht</comment>
+ <comment xml:lang="nn">del av e-post-melding</comment>
+ <comment xml:lang="oc">messatge parcial de corrièr electronic</comment>
+ <comment xml:lang="pl">Częściowa wiadomość e-mail</comment>
+ <comment xml:lang="pt">mensagem parcial de email</comment>
+ <comment xml:lang="pt_BR">Mensagem de e-mail parcial</comment>
+ <comment xml:lang="ro">mesaj de email parțial</comment>
+ <comment xml:lang="ru">Фрагмент сообщения электронной почты</comment>
+ <comment xml:lang="sk">Čiastočná e-mailová správa</comment>
+ <comment xml:lang="sl">delno elektronsko sporočilo</comment>
+ <comment xml:lang="sq">Mesazh poste i pjesëshëm</comment>
+ <comment xml:lang="sr">делимична порука ел. поште</comment>
+ <comment xml:lang="sv">del av e-postmeddelande</comment>
+ <comment xml:lang="tr">kısmi eposta iletisi</comment>
+ <comment xml:lang="uk">часткове поштове повідомлення</comment>
+ <comment xml:lang="vi">thư điện tử riêng phần</comment>
+ <comment xml:lang="zh_CN">部分电子邮件</comment>
+ <comment xml:lang="zh_TW">部份電子郵件訊息</comment>
+ <generic-icon name="text-x-generic"/>
+ <sub-class-of type="text/plain"/>
+ </mime-type>
+ <mime-type type="message/rfc822">
+ <comment>email message</comment>
+ <comment xml:lang="ar">رسالة البريد الإلكتروني</comment>
+ <comment xml:lang="be@latin">list email</comment>
+ <comment xml:lang="bg">Съобщение по електронната поща</comment>
+ <comment xml:lang="ca">missatge de correu electrònic</comment>
+ <comment xml:lang="cs">e-mailová zpráva</comment>
+ <comment xml:lang="da">postmeddelelse</comment>
+ <comment xml:lang="de">E-Mail-Nachricht</comment>
+ <comment xml:lang="el">Ηλ. μήνυμα</comment>
+ <comment xml:lang="en_GB">email message</comment>
+ <comment xml:lang="eo">retpoŝta mesaĝo</comment>
+ <comment xml:lang="es">mensaje de correo electrónico</comment>
+ <comment xml:lang="eu">helbide elektronikoen mezua</comment>
+ <comment xml:lang="fi">sähköpostiviesti</comment>
+ <comment xml:lang="fo">t-post boð</comment>
+ <comment xml:lang="fr">message de courriel</comment>
+ <comment xml:lang="ga">teachtaireacht ríomhphoist</comment>
+ <comment xml:lang="gl">mensaxe de correo electrónico</comment>
+ <comment xml:lang="he">הודעת דואר אלקטרוני</comment>
+ <comment xml:lang="hr">Poruka e-pošte</comment>
+ <comment xml:lang="hu">elektronikus levél</comment>
+ <comment xml:lang="ia">Message de e-mail</comment>
+ <comment xml:lang="id">pesan email</comment>
+ <comment xml:lang="it">Messaggio email</comment>
+ <comment xml:lang="ja">メール本文</comment>
+ <comment xml:lang="kk">пошталық мәлімдеме</comment>
+ <comment xml:lang="ko">전자 우편 본문</comment>
+ <comment xml:lang="lt">el. laiškas</comment>
+ <comment xml:lang="lv">e-pasta vēstule</comment>
+ <comment xml:lang="ms">Mesej emel</comment>
+ <comment xml:lang="nb">e-postmelding</comment>
+ <comment xml:lang="nl">e-mailbericht</comment>
+ <comment xml:lang="nn">e-postmelding</comment>
+ <comment xml:lang="oc">messatge de corrièr electronic</comment>
+ <comment xml:lang="pl">Wiadomość e-mail</comment>
+ <comment xml:lang="pt">mensagem de email</comment>
+ <comment xml:lang="pt_BR">Mensagem de e-mail</comment>
+ <comment xml:lang="ro">mesaj email</comment>
+ <comment xml:lang="ru">Почтовое сообщение</comment>
+ <comment xml:lang="sk">E-mailová správa</comment>
+ <comment xml:lang="sl">sporočilo elektronske pošte</comment>
+ <comment xml:lang="sq">Mesazh poste</comment>
+ <comment xml:lang="sr">порука ел. поште</comment>
+ <comment xml:lang="sv">e-postmeddelande</comment>
+ <comment xml:lang="tr">eposta iletisi</comment>
+ <comment xml:lang="uk">повідомлення email</comment>
+ <comment xml:lang="vi">thư điện tử</comment>
+ <comment xml:lang="zh_CN">电子邮件</comment>
+ <comment xml:lang="zh_TW">電子郵件內容</comment>
+ <generic-icon name="text-x-generic"/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="#! rnews" type="string" offset="0"/>
+ <match value="Forward to" type="string" offset="0"/>
+ <match value="From:" type="string" offset="0"/>
+ <match value="N#! rnews" type="string" offset="0"/>
+ <match value="Pipe to" type="string" offset="0"/>
+ <match value="Received:" type="string" offset="0"/>
+ <match value="Relay-Version:" type="string" offset="0"/>
+ <match value="Return-Path:" type="string" offset="0"/>
+ <match value="Return-path:" type="string" offset="0"/>
+ <match value="Subject: " type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.eml"/>
+ </mime-type>
+ <mime-type type="message/x-gnu-rmail">
+ <comment>GNU mail message</comment>
+ <comment xml:lang="ar">رسالة بريد جنو</comment>
+ <comment xml:lang="az">GNU poçt ismarışı</comment>
+ <comment xml:lang="be@latin">List GNU</comment>
+ <comment xml:lang="bg">Съобщение — GNU mail</comment>
+ <comment xml:lang="ca">missatge de GNU mail</comment>
+ <comment xml:lang="cs">zpráva GNU mail</comment>
+ <comment xml:lang="cy">Neges E-Bost GNU</comment>
+ <comment xml:lang="da">GNU-postmeddelelse</comment>
+ <comment xml:lang="de">GNU-Mail-Nachricht</comment>
+ <comment xml:lang="el">Μήνυμα αλληλογραφίας GNU</comment>
+ <comment xml:lang="en_GB">GNU mail message</comment>
+ <comment xml:lang="eo">mesaĝo de GNU mail</comment>
+ <comment xml:lang="es">mensaje de correo de GNU</comment>
+ <comment xml:lang="eu">GNU posta mezua</comment>
+ <comment xml:lang="fi">GNU-postiviesti</comment>
+ <comment xml:lang="fo">GNU mail boð</comment>
+ <comment xml:lang="fr">message de courriel GNU</comment>
+ <comment xml:lang="ga">teachtaireacht r-phoist GNU</comment>
+ <comment xml:lang="gl">mensaxe de correo electrónico de GNU</comment>
+ <comment xml:lang="he">הודעת דואר של GNU</comment>
+ <comment xml:lang="hr">GNU poruka pošte</comment>
+ <comment xml:lang="hu">GNU elektronikus levél</comment>
+ <comment xml:lang="ia">Message electronic de GNU</comment>
+ <comment xml:lang="id">Pesan surat GNU</comment>
+ <comment xml:lang="it">Messaggio GNU mail</comment>
+ <comment xml:lang="ja">GNU メールメッセージ</comment>
+ <comment xml:lang="ka">GNU mail შეტყობინება</comment>
+ <comment xml:lang="kk">GNU пошта хабарламасы</comment>
+ <comment xml:lang="ko">GNU 메일 메시지</comment>
+ <comment xml:lang="lt">GNU pašto žinutė</comment>
+ <comment xml:lang="lv">GNU pasta vēstule</comment>
+ <comment xml:lang="ms">Mesej emel GNU</comment>
+ <comment xml:lang="nb">GNU e-postmelding</comment>
+ <comment xml:lang="nl">GNU-mailbericht</comment>
+ <comment xml:lang="nn">GNU e-postmelding</comment>
+ <comment xml:lang="oc">messatge de corrièr electronic GNU</comment>
+ <comment xml:lang="pl">Wiadomość pocztowa GNU</comment>
+ <comment xml:lang="pt">mensagem de email GNU</comment>
+ <comment xml:lang="pt_BR">Mensagem de e-mail GNU</comment>
+ <comment xml:lang="ro">Mesaj GNU mail</comment>
+ <comment xml:lang="ru">Почтовое сообщение GNU</comment>
+ <comment xml:lang="sk">Správa GNU mail</comment>
+ <comment xml:lang="sl">Sporočilo pošte GNU</comment>
+ <comment xml:lang="sq">Mesazh GNU mail</comment>
+ <comment xml:lang="sr">порука Гнуове поште</comment>
+ <comment xml:lang="sv">GNU-epostmeddelande</comment>
+ <comment xml:lang="tr">GNU posta iletisi</comment>
+ <comment xml:lang="uk">поштове повідомлення GNU</comment>
+ <comment xml:lang="vi">Thư điện tử của GNU</comment>
+ <comment xml:lang="zh_CN">GNU mail 信件</comment>
+ <comment xml:lang="zh_TW">GNU 郵件訊息</comment>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="RMAIL"/>
+ </mime-type>
+ <mime-type type="model/iges">
+ <comment>IGES document</comment>
+ <comment xml:lang="ast">Documentu IGES</comment>
+ <comment xml:lang="ca">document IGES</comment>
+ <comment xml:lang="cs">dokument IGES</comment>
+ <comment xml:lang="da">IGES-dokument</comment>
+ <comment xml:lang="de">IGES-Dokument</comment>
+ <comment xml:lang="en_GB">IGES document</comment>
+ <comment xml:lang="es">documento IGES</comment>
+ <comment xml:lang="eu">IGES dokumentua</comment>
+ <comment xml:lang="fi">IGES-asiakirja</comment>
+ <comment xml:lang="fr">document IGES</comment>
+ <comment xml:lang="ga">cáipéis IGES</comment>
+ <comment xml:lang="he">מסמך IGES</comment>
+ <comment xml:lang="hr">IGES dokument</comment>
+ <comment xml:lang="hu">IGES dokumentum</comment>
+ <comment xml:lang="id">dokumen IGES</comment>
+ <comment xml:lang="it">Documento IGES</comment>
+ <comment xml:lang="kk">IGES құжаты</comment>
+ <comment xml:lang="ko">IGES 문서</comment>
+ <comment xml:lang="pl">Dokument IGES</comment>
+ <comment xml:lang="pt_BR">Documento IGES</comment>
+ <comment xml:lang="ru">Документ IGES</comment>
+ <comment xml:lang="sk">Dokument IGES</comment>
+ <comment xml:lang="sr">ИГЕС документ</comment>
+ <comment xml:lang="sv">IGES-dokument</comment>
+ <comment xml:lang="tr">IGES belgesi</comment>
+ <comment xml:lang="uk">документ IGES</comment>
+ <comment xml:lang="zh_CN">IGES 文档</comment>
+ <comment xml:lang="zh_TW">IGES 文件</comment>
+ <acronym>IGES</acronym>
+ <expanded-acronym>Initial Graphics Exchange Specification</expanded-acronym>
+ <generic-icon name="x-office-document"/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="S 1\x0a" type="string" offset="72"/>
+ <match value="S0000001\x0a" type="string" offset="72"/>
+ </magic>
+ <glob pattern="*.igs"/>
+ <glob pattern="*.iges"/>
+ </mime-type>
+ <mime-type type="model/vrml">
+ <comment>VRML document</comment>
+ <comment xml:lang="ar">مستند VRML</comment>
+ <comment xml:lang="ast">Documentu VRML</comment>
+ <comment xml:lang="az">VRML sənədi</comment>
+ <comment xml:lang="be@latin">Dakument VRML</comment>
+ <comment xml:lang="bg">Документ — VRML</comment>
+ <comment xml:lang="ca">document VRML</comment>
+ <comment xml:lang="cs">dokument VRML</comment>
+ <comment xml:lang="cy">Dogfen VRML</comment>
+ <comment xml:lang="da">VRML-dokument</comment>
+ <comment xml:lang="de">VRML-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο VRML</comment>
+ <comment xml:lang="en_GB">VRML document</comment>
+ <comment xml:lang="eo">VRML-dokumento</comment>
+ <comment xml:lang="es">documento VRML</comment>
+ <comment xml:lang="eu">VRML dokumentua</comment>
+ <comment xml:lang="fi">VRML-asiakirja</comment>
+ <comment xml:lang="fo">VRML skjal</comment>
+ <comment xml:lang="fr">document VRML</comment>
+ <comment xml:lang="ga">cáipéis VRML</comment>
+ <comment xml:lang="gl">documento VRML</comment>
+ <comment xml:lang="he">מסמך VRML</comment>
+ <comment xml:lang="hr">VRML dokument</comment>
+ <comment xml:lang="hu">VRML-dokumentum</comment>
+ <comment xml:lang="ia">Documento VRML</comment>
+ <comment xml:lang="id">Dokumen VRML</comment>
+ <comment xml:lang="it">Documento VRML</comment>
+ <comment xml:lang="ja">VRML ドキュメント</comment>
+ <comment xml:lang="kk">VRML құжаты</comment>
+ <comment xml:lang="ko">VRML 문서</comment>
+ <comment xml:lang="lt">VRML dokumentas</comment>
+ <comment xml:lang="lv">VRML dokuments</comment>
+ <comment xml:lang="ms">Dokumen VRML</comment>
+ <comment xml:lang="nb">VRML-dokument</comment>
+ <comment xml:lang="nl">VRML-document</comment>
+ <comment xml:lang="nn">VRML-dokument</comment>
+ <comment xml:lang="oc">document VRML</comment>
+ <comment xml:lang="pl">Dokument VRML</comment>
+ <comment xml:lang="pt">documento VRML</comment>
+ <comment xml:lang="pt_BR">Documento VRML</comment>
+ <comment xml:lang="ro">Document VRML</comment>
+ <comment xml:lang="ru">Документ VRML</comment>
+ <comment xml:lang="sk">Dokument VRML</comment>
+ <comment xml:lang="sl">Dokument VRML</comment>
+ <comment xml:lang="sq">Dokument VRML</comment>
+ <comment xml:lang="sr">ВРМЛ документ</comment>
+ <comment xml:lang="sv">VRML-dokument</comment>
+ <comment xml:lang="tr">VRML belgesi</comment>
+ <comment xml:lang="uk">документ VRML</comment>
+ <comment xml:lang="vi">Tài liệu VRML</comment>
+ <comment xml:lang="zh_CN">VRML 文档</comment>
+ <comment xml:lang="zh_TW">VRML 文件</comment>
+ <acronym>VRML</acronym>
+ <expanded-acronym>Virtual Reality Modeling Language</expanded-acronym>
+ <generic-icon name="x-office-document"/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="#VRML " type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.vrm"/>
+ <glob pattern="*.vrml"/>
+ <glob pattern="*.wrl"/>
+ </mime-type>
+ <mime-type type="multipart/alternative">
+ <comment>message in several formats</comment>
+ <comment xml:lang="ar">رسالة في عدة صيغ</comment>
+ <comment xml:lang="az">verici formatlarında ismarış</comment>
+ <comment xml:lang="be@latin">paviedamleńnie ŭ niekalkich farmatach</comment>
+ <comment xml:lang="bg">Съобщение в няколко формата</comment>
+ <comment xml:lang="ca">missatge en diversos formats</comment>
+ <comment xml:lang="cs">zpráva v několika formátech</comment>
+ <comment xml:lang="cy">neges mewn sawl fformat</comment>
+ <comment xml:lang="da">meddelelse i flere formater</comment>
+ <comment xml:lang="de">Nachricht in mehreren Formaten</comment>
+ <comment xml:lang="el">Μήνυμα σε διάφορες μορφές</comment>
+ <comment xml:lang="en_GB">message in several formats</comment>
+ <comment xml:lang="eo">mesaĝo en pluraj formatoj</comment>
+ <comment xml:lang="es">mensaje en varios formatos</comment>
+ <comment xml:lang="eu">hainbat formatuko mezua</comment>
+ <comment xml:lang="fi">viesti useissa muodoissa</comment>
+ <comment xml:lang="fo">boð í fleiri sniðum</comment>
+ <comment xml:lang="fr">message en formats divers</comment>
+ <comment xml:lang="ga">teachtaireacht i bhformáidí éagsúla</comment>
+ <comment xml:lang="gl">mensaxe en varios formatos</comment>
+ <comment xml:lang="he">הודעה במספר תבניות</comment>
+ <comment xml:lang="hr">Poruka u nekoliko oblika</comment>
+ <comment xml:lang="hu">többféle formátumú üzenet</comment>
+ <comment xml:lang="ia">Message in plure formatos</comment>
+ <comment xml:lang="id">pesan dalam beberapa format</comment>
+ <comment xml:lang="it">Messaggio in diversi formati</comment>
+ <comment xml:lang="ja">いくつかの形式でのメッセージ</comment>
+ <comment xml:lang="kk">бірнеше пішімдегі мәлімдеме</comment>
+ <comment xml:lang="ko">여러가지 형식의 메시지</comment>
+ <comment xml:lang="lt">laiškas keletu formatų</comment>
+ <comment xml:lang="lv">ziņojums dažādos formātos</comment>
+ <comment xml:lang="ms">Mesej dalam beberapa format</comment>
+ <comment xml:lang="nb">melding i flere formater</comment>
+ <comment xml:lang="nl">bericht in meerdere opmaken</comment>
+ <comment xml:lang="nn">melding i fleire format</comment>
+ <comment xml:lang="oc">messatge en formats divèrses</comment>
+ <comment xml:lang="pl">Wiadomość w wielu formatach</comment>
+ <comment xml:lang="pt">mensagem em vários formatos</comment>
+ <comment xml:lang="pt_BR">Mensagem em vários formatos</comment>
+ <comment xml:lang="ro">mesaj în diferite formate</comment>
+ <comment xml:lang="ru">Сообщение в нескольких форматах</comment>
+ <comment xml:lang="sk">Správa v niekoľkých formátoch</comment>
+ <comment xml:lang="sl">sporočilo v več zapisih</comment>
+ <comment xml:lang="sq">Mesazh në formate të ndryshëm</comment>
+ <comment xml:lang="sr">порука у неколико записа</comment>
+ <comment xml:lang="sv">meddelande i flera format</comment>
+ <comment xml:lang="tr">farklı biçimlerde ileti</comment>
+ <comment xml:lang="uk">повідомлення у кількох форматах</comment>
+ <comment xml:lang="vi">thông điệp có vài định dạng</comment>
+ <comment xml:lang="zh_CN">各种格式的信件</comment>
+ <comment xml:lang="zh_TW">多種格式的訊息</comment>
+ </mime-type>
+ <mime-type type="multipart/appledouble">
+ <comment>Macintosh AppleDouble-encoded file</comment>
+ <comment xml:lang="ar">ملف Macintosh AppleDouble مشفر</comment>
+ <comment xml:lang="az">Macintosh AppleDouble-kodlanmış fayl</comment>
+ <comment xml:lang="be@latin">Fajł Macintosh, AppleDouble-zakadavany</comment>
+ <comment xml:lang="bg">Файл — кодиран с Macintosh AppleDouble</comment>
+ <comment xml:lang="ca">fitxer codificat AppleDouble de Macintosh</comment>
+ <comment xml:lang="cs">soubor kódovaný pomocí Macintosh AppleDouble</comment>
+ <comment xml:lang="cy">Ffeil AppleDouble-amgodedig Macintosh</comment>
+ <comment xml:lang="da">Macintosh AppleDouble-kodet fil</comment>
+ <comment xml:lang="de">Macintosh-Datei (AppleDouble-kodiert)</comment>
+ <comment xml:lang="el">Αρχείο Macintosh κωδικοποίησης AppleDouble</comment>
+ <comment xml:lang="en_GB">Macintosh AppleDouble-encoded file</comment>
+ <comment xml:lang="eo">dosiero kodigita laŭ Macintosh AppleDouble</comment>
+ <comment xml:lang="es">archivo Macintosh codificado con AppleDouble</comment>
+ <comment xml:lang="eu">Macintosh AppleDouble-rekin kodetutako fitxategia</comment>
+ <comment xml:lang="fi">Macintosh AppleDouble -koodattu tiedosto</comment>
+ <comment xml:lang="fo">Macintosh AppleDouble-bronglað fíla</comment>
+ <comment xml:lang="fr">fichier codé Macintosh AppleDouble</comment>
+ <comment xml:lang="ga">comhad ionchódaithe le Macintosh AppleDouble</comment>
+ <comment xml:lang="gl">ficheiro de Macintosh codificado con AppleDouble</comment>
+ <comment xml:lang="he">קובץ מסוג Macintosh AppleDouble-encoded</comment>
+ <comment xml:lang="hr">Macintosh AppleDouble-kodirana datoteka</comment>
+ <comment xml:lang="hu">Macintosh AppleDouble kódolású fájl</comment>
+ <comment xml:lang="ia">File codificate in AppleDouble de Macintosh</comment>
+ <comment xml:lang="id">Berkas tersandi Macintosh AppleDouble</comment>
+ <comment xml:lang="it">File Macintosh codificato AppleDouble</comment>
+ <comment xml:lang="ja">Macintosh AppleDouble エンコードファイル</comment>
+ <comment xml:lang="kk">Macintosh AppleDouble кодталған файлы</comment>
+ <comment xml:lang="ko">매킨토시 AppleDouble 인코딩된 파일</comment>
+ <comment xml:lang="lt">Macintosh AppleDouble-encoded failas</comment>
+ <comment xml:lang="lv">Macintosh AppleDouble-kodēts datne</comment>
+ <comment xml:lang="ms">Fail terenkod-AppleDouble Macintosh</comment>
+ <comment xml:lang="nb">dokument kodet med Macintosh AppleDouble</comment>
+ <comment xml:lang="nl">Macintosh AppleDouble-gecodeerd bestand</comment>
+ <comment xml:lang="nn">Macintosh AppleDouble-koda fil</comment>
+ <comment xml:lang="oc">fichièr encodat Macintosh AppleDouble</comment>
+ <comment xml:lang="pl">Zakodowany w AppleDouble plik Macintosh</comment>
+ <comment xml:lang="pt">ficheiro codificado em AppleDouble de Macintosh</comment>
+ <comment xml:lang="pt_BR">Arquivo do Macintosh codificado com AppleDouble</comment>
+ <comment xml:lang="ro">Fișier codat Macintosh AppleDouble</comment>
+ <comment xml:lang="ru">Файл, закодированный Macintosh AppleDouble</comment>
+ <comment xml:lang="sk">Súbor kódovaný pomocou Macintosh AppleDouble</comment>
+ <comment xml:lang="sl">Kodirana datoteka Macintosh (AppleDouble)</comment>
+ <comment xml:lang="sq">File Macintosh i kodifikuar AppleDouble</comment>
+ <comment xml:lang="sr">Мекинтошова датотека кодирана Епл Дуплим</comment>
+ <comment xml:lang="sv">Macintosh AppleDouble-kodad fil</comment>
+ <comment xml:lang="tr">Macintosh AppleDouble-şifreli dosyası</comment>
+ <comment xml:lang="uk">файл закодований Macintosh AppleDouble</comment>
+ <comment xml:lang="vi">Tập tin đã mã hoá Apple-Double của Macintosh</comment>
+ <comment xml:lang="zh_CN">Macintosh AppleDouble 编码的文件</comment>
+ <comment xml:lang="zh_TW">Macintosh AppleDouble 編碼檔</comment>
+ </mime-type>
+ <mime-type type="multipart/digest">
+ <comment>message digest</comment>
+ <comment xml:lang="ar">خلاصة الرسالة</comment>
+ <comment xml:lang="az">ismarış daycesti</comment>
+ <comment xml:lang="be@latin">digest paviedamleńniaŭ</comment>
+ <comment xml:lang="bg">Извадка от съобщение</comment>
+ <comment xml:lang="ca">recopilació de missatges</comment>
+ <comment xml:lang="cs">přehled zpráv</comment>
+ <comment xml:lang="cy">crynodeb negeseuon</comment>
+ <comment xml:lang="da">meddelelsessammendrag</comment>
+ <comment xml:lang="de">Nachrichtensammlung</comment>
+ <comment xml:lang="el">Περίληψη μηνύματος</comment>
+ <comment xml:lang="en_GB">message digest</comment>
+ <comment xml:lang="eo">mesaĝaro</comment>
+ <comment xml:lang="es">recopilación de mensajes</comment>
+ <comment xml:lang="eu">mezu laburra</comment>
+ <comment xml:lang="fi">viestikokoelma</comment>
+ <comment xml:lang="fo">boð samandráttur</comment>
+ <comment xml:lang="fr">condensé de message</comment>
+ <comment xml:lang="ga">achoimre theachtaireachtaí</comment>
+ <comment xml:lang="gl">recompilación de mensaxe</comment>
+ <comment xml:lang="he">תקציר ההודעה</comment>
+ <comment xml:lang="hr">Poruka kratkg sadržaja</comment>
+ <comment xml:lang="hu">ömlesztett üzenet</comment>
+ <comment xml:lang="ia">Digesto de messages</comment>
+ <comment xml:lang="id">digest pesan</comment>
+ <comment xml:lang="it">Digest di messaggi</comment>
+ <comment xml:lang="ja">メッセージダイジェスト</comment>
+ <comment xml:lang="kk">мәлімдеме профилі</comment>
+ <comment xml:lang="ko">메시지 묶음</comment>
+ <comment xml:lang="lt">laiškų santrauka</comment>
+ <comment xml:lang="lv">ziņojumu apkopojums</comment>
+ <comment xml:lang="ms">Jilid mesej</comment>
+ <comment xml:lang="nb">medldingssamling</comment>
+ <comment xml:lang="nl">berichtenbundel</comment>
+ <comment xml:lang="nn">meldingsamandrag</comment>
+ <comment xml:lang="oc">condensé de messatge</comment>
+ <comment xml:lang="pl">Wiadomość przetwarzania</comment>
+ <comment xml:lang="pt">grupo de mensagens</comment>
+ <comment xml:lang="pt_BR">Resumo de mensagem</comment>
+ <comment xml:lang="ro">colecție mesaje email</comment>
+ <comment xml:lang="ru">Дайджест сообщения</comment>
+ <comment xml:lang="sk">Prehľad správ</comment>
+ <comment xml:lang="sl">povzetek sporočila</comment>
+ <comment xml:lang="sq">Shpërndarje mesazhesh</comment>
+ <comment xml:lang="sr">гомила порука</comment>
+ <comment xml:lang="sv">meddelandesamling</comment>
+ <comment xml:lang="tr">mesaj özeti</comment>
+ <comment xml:lang="uk">збірка повідомлень</comment>
+ <comment xml:lang="vi">bản tóm tắt thông điệp</comment>
+ <comment xml:lang="zh_CN">信件摘要</comment>
+ <comment xml:lang="zh_TW">訊息摘要</comment>
+ </mime-type>
+ <mime-type type="multipart/encrypted">
+ <comment>encrypted message</comment>
+ <comment xml:lang="ar">رسالة مشفرة</comment>
+ <comment xml:lang="az">şifrələnmiş ismarış</comment>
+ <comment xml:lang="be@latin">zašyfravanaje paviedamleńnie</comment>
+ <comment xml:lang="bg">Шифрирано съобщение</comment>
+ <comment xml:lang="ca">missatge xifrat</comment>
+ <comment xml:lang="cs">zašifrovaná zpráva</comment>
+ <comment xml:lang="cy">Neges wedi ei hamgryptio</comment>
+ <comment xml:lang="da">krypteret meddelelse</comment>
+ <comment xml:lang="de">Verschlüsselte Nachricht</comment>
+ <comment xml:lang="el">Κρυπτογραφημένο μήνυμα</comment>
+ <comment xml:lang="en_GB">encrypted message</comment>
+ <comment xml:lang="eo">ĉifrita mesaĝo</comment>
+ <comment xml:lang="es">mensaje cifrado</comment>
+ <comment xml:lang="eu">mezu enkriptatua</comment>
+ <comment xml:lang="fi">salattu viesti</comment>
+ <comment xml:lang="fo">bronglað boð</comment>
+ <comment xml:lang="fr">message chiffré</comment>
+ <comment xml:lang="ga">teachtaireacht chriptithe</comment>
+ <comment xml:lang="gl">mensaxe cifrado</comment>
+ <comment xml:lang="he">הודעה מוצפנת</comment>
+ <comment xml:lang="hr">Šifrirana poruka</comment>
+ <comment xml:lang="hu">titkosított üzenet</comment>
+ <comment xml:lang="ia">Message cryptate</comment>
+ <comment xml:lang="id">pesan terenkripsi</comment>
+ <comment xml:lang="it">Messaggio cifrato</comment>
+ <comment xml:lang="ja">暗号化メッセージ</comment>
+ <comment xml:lang="kk">шифрленген мәлімдеме</comment>
+ <comment xml:lang="ko">암호화된 메시지</comment>
+ <comment xml:lang="lt">užšifruotas laiškas</comment>
+ <comment xml:lang="lv">šifrēta vēstule</comment>
+ <comment xml:lang="ms">Mesej terenkripsi</comment>
+ <comment xml:lang="nb">kryptert melding</comment>
+ <comment xml:lang="nl">versleuteld bericht</comment>
+ <comment xml:lang="nn">kryptert melding</comment>
+ <comment xml:lang="oc">messatge chifrat</comment>
+ <comment xml:lang="pl">Wiadomość zaszyfrowana</comment>
+ <comment xml:lang="pt">mensagem encriptada</comment>
+ <comment xml:lang="pt_BR">Mensagem criptografada</comment>
+ <comment xml:lang="ro">mesaj criptat</comment>
+ <comment xml:lang="ru">Зашифрованное сообщение</comment>
+ <comment xml:lang="sk">Zašifrovaná správa</comment>
+ <comment xml:lang="sl">šifrirano sporočilo</comment>
+ <comment xml:lang="sq">Mesazh i kriptuar</comment>
+ <comment xml:lang="sr">шифрована порука</comment>
+ <comment xml:lang="sv">krypterat meddelande</comment>
+ <comment xml:lang="tr">şifrelenmiş mesaj</comment>
+ <comment xml:lang="uk">шифроване повідомлення</comment>
+ <comment xml:lang="vi">thông điệp đã mật mã</comment>
+ <comment xml:lang="zh_CN">加密信件</comment>
+ <comment xml:lang="zh_TW">加密訊息</comment>
+ </mime-type>
+ <mime-type type="multipart/mixed">
+ <comment>compound documents</comment>
+ <comment xml:lang="ar">مستندات مركبة</comment>
+ <comment xml:lang="ast">documentos compuestos</comment>
+ <comment xml:lang="be@latin">składanyja dakumenty</comment>
+ <comment xml:lang="bg">Съставни документи</comment>
+ <comment xml:lang="ca">documents compostos</comment>
+ <comment xml:lang="cs">složené dokumenty</comment>
+ <comment xml:lang="da">sammensatte dokumenter</comment>
+ <comment xml:lang="de">Verbunddokumente</comment>
+ <comment xml:lang="el">Σύνθετα έγγραφα</comment>
+ <comment xml:lang="en_GB">compound documents</comment>
+ <comment xml:lang="eo">parentezaj dokumentoj</comment>
+ <comment xml:lang="es">documentos compuestos</comment>
+ <comment xml:lang="eu">konposatutako dokumentuak</comment>
+ <comment xml:lang="fi">yhdisteasiakirjat</comment>
+ <comment xml:lang="fo">samansett skjøl</comment>
+ <comment xml:lang="fr">documents composés</comment>
+ <comment xml:lang="ga">cáipéisí comhshuite</comment>
+ <comment xml:lang="gl">documentos compostos</comment>
+ <comment xml:lang="he">מסמכים מורכבים</comment>
+ <comment xml:lang="hr">Složeni dokumenti</comment>
+ <comment xml:lang="hu">összetett dokumentumok</comment>
+ <comment xml:lang="ia">Documentos composite</comment>
+ <comment xml:lang="id">dokumen kompon</comment>
+ <comment xml:lang="it">Documenti composti</comment>
+ <comment xml:lang="ja">複合ドキュメント</comment>
+ <comment xml:lang="kk">құрама құжаттары</comment>
+ <comment xml:lang="ko">복합 문서</comment>
+ <comment xml:lang="lt">sudurtiniai dokumentai</comment>
+ <comment xml:lang="lv">salikti dokumenti</comment>
+ <comment xml:lang="ms">Dokumen halaman</comment>
+ <comment xml:lang="nb">sammensatte dokumenter</comment>
+ <comment xml:lang="nl">samengestelde documenten</comment>
+ <comment xml:lang="nn">samansette dokument</comment>
+ <comment xml:lang="oc">documents compausats</comment>
+ <comment xml:lang="pl">Dokumenty złożone</comment>
+ <comment xml:lang="pt">documentos compostos</comment>
+ <comment xml:lang="pt_BR">Documentos compostos</comment>
+ <comment xml:lang="ro">documente compuse</comment>
+ <comment xml:lang="ru">Составные документы</comment>
+ <comment xml:lang="sk">Zložené dokumenty</comment>
+ <comment xml:lang="sl">združeni dokumenti</comment>
+ <comment xml:lang="sq">dokumente të përbërë</comment>
+ <comment xml:lang="sr">сједињени документи</comment>
+ <comment xml:lang="sv">sammansatta dokument</comment>
+ <comment xml:lang="tr">birleşik belgeleri</comment>
+ <comment xml:lang="uk">складні документи</comment>
+ <comment xml:lang="vi">tài liệu ghép</comment>
+ <comment xml:lang="zh_CN">组合文档</comment>
+ <comment xml:lang="zh_TW">複合文件</comment>
+ </mime-type>
+ <mime-type type="multipart/related">
+ <comment>compound document</comment>
+ <comment xml:lang="ar">مستند مركب</comment>
+ <comment xml:lang="ast">documentu compuestu</comment>
+ <comment xml:lang="az">birləşik sənəd</comment>
+ <comment xml:lang="be@latin">składany dakument</comment>
+ <comment xml:lang="bg">Съставен документ</comment>
+ <comment xml:lang="ca">document compost</comment>
+ <comment xml:lang="cs">složený dokument</comment>
+ <comment xml:lang="cy">dogfen gyfansawdd</comment>
+ <comment xml:lang="da">sammensat dokument</comment>
+ <comment xml:lang="de">Verbunddokument</comment>
+ <comment xml:lang="el">Σύνθετο έγγραφο</comment>
+ <comment xml:lang="en_GB">compound document</comment>
+ <comment xml:lang="eo">parenteza dokumento</comment>
+ <comment xml:lang="es">documento compuesto</comment>
+ <comment xml:lang="eu">konposatutako dokumentua</comment>
+ <comment xml:lang="fi">yhdisteasiakirja</comment>
+ <comment xml:lang="fo">samansett skjal</comment>
+ <comment xml:lang="fr">document composé</comment>
+ <comment xml:lang="ga">cáipéis comhshuite</comment>
+ <comment xml:lang="gl">documento composto</comment>
+ <comment xml:lang="he">מסמך מורכב</comment>
+ <comment xml:lang="hr">Složeni dokument</comment>
+ <comment xml:lang="hu">összetett dokumentum</comment>
+ <comment xml:lang="ia">Documento composite</comment>
+ <comment xml:lang="id">dokumen kompon</comment>
+ <comment xml:lang="it">Documento composto</comment>
+ <comment xml:lang="ja">複合ドキュメント</comment>
+ <comment xml:lang="kk">құрама құжаты</comment>
+ <comment xml:lang="ko">복합 문서</comment>
+ <comment xml:lang="lt">sudurtinis dokumentas</comment>
+ <comment xml:lang="lv">salikts dokuments</comment>
+ <comment xml:lang="ms">Dokumen halaman</comment>
+ <comment xml:lang="nb">sammensatt dokument</comment>
+ <comment xml:lang="nl">samengesteld document</comment>
+ <comment xml:lang="nn">samansett dokument</comment>
+ <comment xml:lang="oc">document compausat</comment>
+ <comment xml:lang="pl">Dokument złożony</comment>
+ <comment xml:lang="pt">documento composto</comment>
+ <comment xml:lang="pt_BR">Documento composto</comment>
+ <comment xml:lang="ro">document compus</comment>
+ <comment xml:lang="ru">Составной документ</comment>
+ <comment xml:lang="sk">Zložený dokument</comment>
+ <comment xml:lang="sl">združeni dokument</comment>
+ <comment xml:lang="sq">dokumet i përbërë</comment>
+ <comment xml:lang="sr">сједињени документ</comment>
+ <comment xml:lang="sv">sammansatt dokument</comment>
+ <comment xml:lang="tr">bileşik belge</comment>
+ <comment xml:lang="uk">складний документ</comment>
+ <comment xml:lang="vi">tài liệu ghép</comment>
+ <comment xml:lang="zh_CN">组合文档</comment>
+ <comment xml:lang="zh_TW">複合文件</comment>
+ </mime-type>
+ <mime-type type="multipart/report">
+ <comment>mail system report</comment>
+ <comment xml:lang="ar">تقرير نظام البريد</comment>
+ <comment xml:lang="az">poçt sistemi raportu</comment>
+ <comment xml:lang="be@latin">rapart paštovaj systemy</comment>
+ <comment xml:lang="bg">Отчет за пощенската система</comment>
+ <comment xml:lang="ca">informe de sistema de correu</comment>
+ <comment xml:lang="cs">zpráva poštovního systému</comment>
+ <comment xml:lang="cy">adroddiad system bost</comment>
+ <comment xml:lang="da">postsystemrapport</comment>
+ <comment xml:lang="de">E-Mail-Systembericht</comment>
+ <comment xml:lang="el">Αναφορά συστήματος ηλ. ταχυδρομείου</comment>
+ <comment xml:lang="en_GB">mail system report</comment>
+ <comment xml:lang="eo">raporto de retpoŝta sistemo</comment>
+ <comment xml:lang="es">informe del sistema de correo</comment>
+ <comment xml:lang="eu">posta sistemako txostena</comment>
+ <comment xml:lang="fi">viestijärjestelmän ilmoitus</comment>
+ <comment xml:lang="fo">postkervisfrásøgn</comment>
+ <comment xml:lang="fr">rapport système de courriels</comment>
+ <comment xml:lang="ga">tuairisc chórais r-phoist</comment>
+ <comment xml:lang="gl">informe do sistema de correo</comment>
+ <comment xml:lang="he">דו״ח של מערכת הדואר</comment>
+ <comment xml:lang="hr">Izvještaj sustava pošte</comment>
+ <comment xml:lang="hu">levelezőrendszer jelentése</comment>
+ <comment xml:lang="ia">Reporto de systema de e-mail</comment>
+ <comment xml:lang="id">laporan sistem surat</comment>
+ <comment xml:lang="it">Rapporto di sistema posta</comment>
+ <comment xml:lang="ja">メールシステムレポート</comment>
+ <comment xml:lang="kk">пошта жүйесінің мәлімдемесі</comment>
+ <comment xml:lang="ko">메일 시스템 보고서</comment>
+ <comment xml:lang="lt">pašto sistemos ataskaita</comment>
+ <comment xml:lang="lv">pasta sistēmas atskaite</comment>
+ <comment xml:lang="ms">Laporan sistem mel</comment>
+ <comment xml:lang="nb">e-postsystemrapport</comment>
+ <comment xml:lang="nl">e-mail-systeembericht</comment>
+ <comment xml:lang="nn">e-post-systemrapport</comment>
+ <comment xml:lang="oc">rapòrt sistèma de corrièrs electronics</comment>
+ <comment xml:lang="pl">Raport systemu pocztowego</comment>
+ <comment xml:lang="pt">relatório de sistema de email</comment>
+ <comment xml:lang="pt_BR">Relatório do sistema de correspondência</comment>
+ <comment xml:lang="ro">raport sistem email</comment>
+ <comment xml:lang="ru">Отчёт почтовой системы</comment>
+ <comment xml:lang="sk">Správa poštového systému</comment>
+ <comment xml:lang="sl">poročilo poštnega sistema</comment>
+ <comment xml:lang="sq">Raport i sistemit të postës</comment>
+ <comment xml:lang="sr">извештај поштанског система</comment>
+ <comment xml:lang="sv">e-postsystemrapport</comment>
+ <comment xml:lang="tr">posta sistem raporu</comment>
+ <comment xml:lang="uk">звіт поштової системи</comment>
+ <comment xml:lang="vi">thông báo hệ thống thư</comment>
+ <comment xml:lang="zh_CN">邮件系统报告</comment>
+ <comment xml:lang="zh_TW">郵件系統回報</comment>
+ </mime-type>
+ <mime-type type="multipart/signed">
+ <comment>signed message</comment>
+ <comment xml:lang="ar">رسالة موقّعة</comment>
+ <comment xml:lang="az">imzalanmış ismarış</comment>
+ <comment xml:lang="be@latin">padpisanaje paviedamleńnie</comment>
+ <comment xml:lang="bg">Подписано съобщение</comment>
+ <comment xml:lang="ca">missatge signat</comment>
+ <comment xml:lang="cs">podepsaná zpráva</comment>
+ <comment xml:lang="cy">neges lofnodwyd</comment>
+ <comment xml:lang="da">signeret meddelelse</comment>
+ <comment xml:lang="de">Signierte Nachricht</comment>
+ <comment xml:lang="el">Υπογεγραμμένο μήνυμα</comment>
+ <comment xml:lang="en_GB">signed message</comment>
+ <comment xml:lang="eo">pruvita mesaĝo</comment>
+ <comment xml:lang="es">mensaje firmado</comment>
+ <comment xml:lang="eu">sinatutako mezua</comment>
+ <comment xml:lang="fi">allekirjoitettu viesti</comment>
+ <comment xml:lang="fo">undirskrivað boð</comment>
+ <comment xml:lang="fr">message signé</comment>
+ <comment xml:lang="ga">teachtaireacht sínithe</comment>
+ <comment xml:lang="gl">mensaxe firmado</comment>
+ <comment xml:lang="he">הודעה חתומה</comment>
+ <comment xml:lang="hr">Potpisana poruka</comment>
+ <comment xml:lang="hu">aláírt üzenet</comment>
+ <comment xml:lang="ia">Message signate</comment>
+ <comment xml:lang="id">pesan ditandatangani</comment>
+ <comment xml:lang="it">Messaggio firmato</comment>
+ <comment xml:lang="ja">署名付きメッセージ</comment>
+ <comment xml:lang="kk">қолтаңбасы бар мәлімдеме</comment>
+ <comment xml:lang="ko">서명된 메시지</comment>
+ <comment xml:lang="lt">pasirašytas laiškas</comment>
+ <comment xml:lang="lv">parakstīta ziņa</comment>
+ <comment xml:lang="ms">Mesej ditandatangani</comment>
+ <comment xml:lang="nb">signert melding</comment>
+ <comment xml:lang="nl">ondertekend bericht</comment>
+ <comment xml:lang="nn">signert melding</comment>
+ <comment xml:lang="oc">messatge signat</comment>
+ <comment xml:lang="pl">Podpisana wiadomość</comment>
+ <comment xml:lang="pt">mensagem assinada</comment>
+ <comment xml:lang="pt_BR">Mensagem assinada</comment>
+ <comment xml:lang="ro">mesaj semnat</comment>
+ <comment xml:lang="ru">Подписанное сообщение</comment>
+ <comment xml:lang="sk">Podpísaná správa</comment>
+ <comment xml:lang="sl">podpisano sporočilo</comment>
+ <comment xml:lang="sq">Mesazh i firmosur</comment>
+ <comment xml:lang="sr">потписана порука</comment>
+ <comment xml:lang="sv">signerat meddelande</comment>
+ <comment xml:lang="tr">imzalı ileti</comment>
+ <comment xml:lang="uk">підписане повідомлення</comment>
+ <comment xml:lang="vi">thông điệp đã ký</comment>
+ <comment xml:lang="zh_CN">签名信件</comment>
+ <comment xml:lang="zh_TW">簽署的訊息</comment>
+ </mime-type>
+ <mime-type type="multipart/x-mixed-replace">
+ <comment>stream of data (server push)</comment>
+ <comment xml:lang="ar">دفق بيانات (دفع خادم)</comment>
+ <comment xml:lang="be@latin">płyń źviestak (ad servera)</comment>
+ <comment xml:lang="bg">Поток от данни, от страна на сървър</comment>
+ <comment xml:lang="ca">flux de dades (enviat pel servidor)</comment>
+ <comment xml:lang="cs">proud dat (posílaný serverem)</comment>
+ <comment xml:lang="da">datastrøm (serverskubbet)</comment>
+ <comment xml:lang="de">Datenstrom (Server-Push)</comment>
+ <comment xml:lang="el">Ροή δεδομένων (στελλόμενα από διακομιστή)</comment>
+ <comment xml:lang="en_GB">stream of data (server push)</comment>
+ <comment xml:lang="eo">datumstrio (puŝata per servilo)</comment>
+ <comment xml:lang="es">flujo de datos (por iniciativa del servidor)</comment>
+ <comment xml:lang="eu">datu-korrontea (zerbitzari igortzailea)</comment>
+ <comment xml:lang="fi">tietovirta (palvelin työntää)</comment>
+ <comment xml:lang="fo">streymur av dáta (ambætara skump)</comment>
+ <comment xml:lang="fr">flux de données (émis par le serveur)</comment>
+ <comment xml:lang="ga">sruth sonraí (brú freastalaí)</comment>
+ <comment xml:lang="gl">fluxo de datos (por iniciativa do servidor)</comment>
+ <comment xml:lang="he">מידע בזרימה (דחיפה ע״י השרת)</comment>
+ <comment xml:lang="hr">Strujanje podataka (poslužiteljem pogurano)</comment>
+ <comment xml:lang="hu">sugárzott adatfolyam (kiszolgálóról)</comment>
+ <comment xml:lang="ia">Fluxo de datos (pulsate per servitor)</comment>
+ <comment xml:lang="id">arus data (dorongan server)</comment>
+ <comment xml:lang="it">Flusso di dati (server push)</comment>
+ <comment xml:lang="ja">データストリーム (サーバープッシュ型)</comment>
+ <comment xml:lang="kk">мәліметтер ағымы (server push)</comment>
+ <comment xml:lang="ko">데이터 스트림(서버 푸시)</comment>
+ <comment xml:lang="lt">duomenų srautas (iš serverio)</comment>
+ <comment xml:lang="lv">datu straume (servera grūsta)</comment>
+ <comment xml:lang="ms">Aliran dara (paksaan pelayan)</comment>
+ <comment xml:lang="nb">datastrøm (server push)</comment>
+ <comment xml:lang="nl">gegevensstroom (server duwt)</comment>
+ <comment xml:lang="nn">datastraum (dytta av tenaren)</comment>
+ <comment xml:lang="oc">flux de donadas (emés pel servidor)</comment>
+ <comment xml:lang="pl">Strumień danych (wymuszenie serwera)</comment>
+ <comment xml:lang="pt">fluxo de dados (empurrados pelo servidor)</comment>
+ <comment xml:lang="pt_BR">Fluxo de dados (por iniciativa do servidor)</comment>
+ <comment xml:lang="ro">flux de date (de la server)</comment>
+ <comment xml:lang="ru">Поток данных (server push)</comment>
+ <comment xml:lang="sk">Prúd dát (posielaný serverom)</comment>
+ <comment xml:lang="sl">pretok podatkov (strežniški)</comment>
+ <comment xml:lang="sq">Fluks me të dhëna (server push)</comment>
+ <comment xml:lang="sr">ток података (гурање са сервера)</comment>
+ <comment xml:lang="sv">dataflöde (serverutsändning)</comment>
+ <comment xml:lang="tr">veri akışı (sunucudan gönderilen)</comment>
+ <comment xml:lang="uk">потік даних (від сервера)</comment>
+ <comment xml:lang="vi">luồng dữ liệu (trình phục vụ đẩy)</comment>
+ <comment xml:lang="zh_CN">数据流(服务器推送)</comment>
+ <comment xml:lang="zh_TW">資料串流 (server push)</comment>
+ </mime-type>
+ <mime-type type="text/calendar">
+ <comment>VCS/ICS calendar</comment>
+ <comment xml:lang="ar">سجل VCS/ICS</comment>
+ <comment xml:lang="be@latin">Kalandar VCS/ICS</comment>
+ <comment xml:lang="bg">Календар — VCS/ICS</comment>
+ <comment xml:lang="ca">calendari VCS/ICS</comment>
+ <comment xml:lang="cs">kalendář VCS/ICS</comment>
+ <comment xml:lang="da">VCS/ICS-kalender</comment>
+ <comment xml:lang="de">VCS/ICS-Kalender</comment>
+ <comment xml:lang="el">Ημερολόγιο VCS/ICS</comment>
+ <comment xml:lang="en_GB">VCS/ICS calendar</comment>
+ <comment xml:lang="eo">VCS/ICS-kalendaro</comment>
+ <comment xml:lang="es">calendario VCS/ICS</comment>
+ <comment xml:lang="eu">VCS/ICS egutegia</comment>
+ <comment xml:lang="fi">VCS/ICS-kalenteri</comment>
+ <comment xml:lang="fo">VCS/ICS kalendari</comment>
+ <comment xml:lang="fr">calendrier VCS/ICS</comment>
+ <comment xml:lang="ga">féilire VCS/ICS</comment>
+ <comment xml:lang="gl">Calendario VCS/ICS</comment>
+ <comment xml:lang="he">לוח שנה VCS/ICS</comment>
+ <comment xml:lang="hr">VCS/ICS kalendar</comment>
+ <comment xml:lang="hu">VCS/ICS naptár</comment>
+ <comment xml:lang="ia">Calendario VCS/ICS</comment>
+ <comment xml:lang="id">Kalender VCS/ICS</comment>
+ <comment xml:lang="it">Calendario VCS/ICS</comment>
+ <comment xml:lang="ja">VCS/ICS カレンダー</comment>
+ <comment xml:lang="kk">VCS/ICS күнтізбесі</comment>
+ <comment xml:lang="ko">VCS/ICS 달력</comment>
+ <comment xml:lang="lt">VCS/ICS kalendorius</comment>
+ <comment xml:lang="lv">VCS/ICS kalendārs</comment>
+ <comment xml:lang="nb">VCS/ICS-kalender</comment>
+ <comment xml:lang="nl">VCS/ICS-kalender</comment>
+ <comment xml:lang="nn">VCS/ICS-kalender</comment>
+ <comment xml:lang="oc">calendièr VCS/ICS</comment>
+ <comment xml:lang="pl">Kalendarz VCS/ICS</comment>
+ <comment xml:lang="pt">calendário VCS/ICS</comment>
+ <comment xml:lang="pt_BR">Calendário VCS/ICS</comment>
+ <comment xml:lang="ro">Calendar VCS/ICS</comment>
+ <comment xml:lang="ru">Календарь VCS/ICS</comment>
+ <comment xml:lang="sk">Kalendár VCS/ICS</comment>
+ <comment xml:lang="sl">Datoteka koledarja VCS/ICS</comment>
+ <comment xml:lang="sq">Kalendar VCS/ICS</comment>
+ <comment xml:lang="sr">ВЦС/ИЦС календар</comment>
+ <comment xml:lang="sv">VCS/ICS-kalender</comment>
+ <comment xml:lang="tr">VCS/ICS takvimi</comment>
+ <comment xml:lang="uk">календар VCS/ICS</comment>
+ <comment xml:lang="vi">Lịch VCS/ICS</comment>
+ <comment xml:lang="zh_CN">VCS/ICS 日历</comment>
+ <comment xml:lang="zh_TW">VCS/ICS 行事曆</comment>
+ <acronym>VCS/ICS</acronym>
+ <expanded-acronym>vCalendar/iCalendar</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <alias type="text/x-vcalendar"/>
+ <alias type="application/ics"/>
+ <magic priority="50">
+ <match value="BEGIN:VCALENDAR" type="string" offset="0"/>
+ <match value="begin:vcalendar" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.vcs"/>
+ <glob pattern="*.ics"/>
+ </mime-type>
+ <mime-type type="text/css">
+ <comment>CSS stylesheet</comment>
+ <comment xml:lang="ar">نمط CSS</comment>
+ <comment xml:lang="be@latin">Arkuš stylaŭ CSS</comment>
+ <comment xml:lang="bg">Стилове — CSS</comment>
+ <comment xml:lang="ca">llista d'estil CSS</comment>
+ <comment xml:lang="cs">stylopis CSS</comment>
+ <comment xml:lang="da">CSS-stilark</comment>
+ <comment xml:lang="de">CSS-Stilvorlage</comment>
+ <comment xml:lang="el">Φύλλο στυλ CSS</comment>
+ <comment xml:lang="en_GB">CSS stylesheet</comment>
+ <comment xml:lang="eo">CSS-stilfolio</comment>
+ <comment xml:lang="es">hoja de estilos CSS</comment>
+ <comment xml:lang="eu">CSS estilo-orria</comment>
+ <comment xml:lang="fi">CSS-tyylitiedosto</comment>
+ <comment xml:lang="fo">CSS sniðark</comment>
+ <comment xml:lang="fr">feuille de style CSS</comment>
+ <comment xml:lang="ga">stílbhileog CSS</comment>
+ <comment xml:lang="gl">folla de estilos CSS</comment>
+ <comment xml:lang="he">גליון עיצוב CSS</comment>
+ <comment xml:lang="hr">CSS stilska tablica</comment>
+ <comment xml:lang="hu">CSS stíluslap</comment>
+ <comment xml:lang="ia">Folio de stilo CSS</comment>
+ <comment xml:lang="id">Lembar gaya CSS</comment>
+ <comment xml:lang="it">Foglio di stile CSS</comment>
+ <comment xml:lang="ja">CSS スタイルシート</comment>
+ <comment xml:lang="ka">CSS სტილი</comment>
+ <comment xml:lang="kk">CSS стильдер кестесі</comment>
+ <comment xml:lang="ko">CSS 스타일시트</comment>
+ <comment xml:lang="lt">CSS stiliaus aprašas</comment>
+ <comment xml:lang="lv">CSS stilu saraksts</comment>
+ <comment xml:lang="nb">CSS-stilark</comment>
+ <comment xml:lang="nl">CSS-stijlblad</comment>
+ <comment xml:lang="nn">CSS-stilark</comment>
+ <comment xml:lang="oc">fuèlh d'estil CSS</comment>
+ <comment xml:lang="pl">Arkusz stylów CSS</comment>
+ <comment xml:lang="pt">folha de estilos CSS</comment>
+ <comment xml:lang="pt_BR">Folha de estilo CSS</comment>
+ <comment xml:lang="ro">Pagină de stil CSS</comment>
+ <comment xml:lang="ru">Таблица стилей CSS</comment>
+ <comment xml:lang="sk">Štýly CSS</comment>
+ <comment xml:lang="sl">Slogovna predloga CSS</comment>
+ <comment xml:lang="sq">Fletë stili CSS</comment>
+ <comment xml:lang="sr">ЦСС стилски лист</comment>
+ <comment xml:lang="sv">CSS-stilmall</comment>
+ <comment xml:lang="tr">CSS stil kağıdı</comment>
+ <comment xml:lang="uk">таблиця стилів CSS</comment>
+ <comment xml:lang="vi">Tờ kiểu dáng CSS</comment>
+ <comment xml:lang="zh_CN">CSS 样式表</comment>
+ <comment xml:lang="zh_TW">CSS 樣式表</comment>
+ <acronym>CSS</acronym>
+ <expanded-acronym>Cascading Style Sheets</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.css"/>
+ </mime-type>
+ <mime-type type="text/vcard">
+ <comment>electronic business card</comment>
+ <comment xml:lang="ar">بطاقة أعمال إلكترونية</comment>
+ <comment xml:lang="be@latin">elektronnaja biznes-kartka</comment>
+ <comment xml:lang="bg">Електронна визитна картичка</comment>
+ <comment xml:lang="ca">targeta de visita electrònica</comment>
+ <comment xml:lang="cs">elektronická navštívenka</comment>
+ <comment xml:lang="da">elektronisk visitkort</comment>
+ <comment xml:lang="de">Elektronische Visitenkarte</comment>
+ <comment xml:lang="el">Ηλεκτρονική επαγγελματική κάρτα</comment>
+ <comment xml:lang="en_GB">electronic business card</comment>
+ <comment xml:lang="eo">elektronika vizitkarto</comment>
+ <comment xml:lang="es">tarjeta de visita electrónica</comment>
+ <comment xml:lang="eu">enpresako txartel elektronikoa</comment>
+ <comment xml:lang="fi">sähköinen käyntikortti</comment>
+ <comment xml:lang="fo">elektroniskt handilskort</comment>
+ <comment xml:lang="fr">carte de visite électronique</comment>
+ <comment xml:lang="ga">cárta gnó leictreonach</comment>
+ <comment xml:lang="gl">tarxeta de negocio electrónica</comment>
+ <comment xml:lang="he">כרטיס ביקור אלקטרוני</comment>
+ <comment xml:lang="hr">Elektronička posjetnica</comment>
+ <comment xml:lang="hu">elektronikus névjegykártya</comment>
+ <comment xml:lang="ia">Carta de visita electronic</comment>
+ <comment xml:lang="id">kartu bisnis elektronik</comment>
+ <comment xml:lang="it">Biglietto da visita elettronico</comment>
+ <comment xml:lang="ja">電子名刺</comment>
+ <comment xml:lang="kk">электронды визит карточкасы</comment>
+ <comment xml:lang="ko">전자 명함</comment>
+ <comment xml:lang="lt">elektroninė vizitinė kortelė</comment>
+ <comment xml:lang="lv">elektroniskā biznesa kartiņa</comment>
+ <comment xml:lang="nl">elektronisch visitekaartje</comment>
+ <comment xml:lang="nn">elektronisk visittkort</comment>
+ <comment xml:lang="oc">carta de visita electronica</comment>
+ <comment xml:lang="pl">Wizytówka elektroniczna</comment>
+ <comment xml:lang="pt">cartão de visita eletrónico</comment>
+ <comment xml:lang="pt_BR">Cartão de visitas eletrônico</comment>
+ <comment xml:lang="ro">carte de vizită electronică</comment>
+ <comment xml:lang="ru">Электронная визитная карточка</comment>
+ <comment xml:lang="sk">Elektronická vizitka</comment>
+ <comment xml:lang="sl">elektronska poslovna vizitka</comment>
+ <comment xml:lang="sq">Skedë elektronike biznesi</comment>
+ <comment xml:lang="sr">електронска пословна картица</comment>
+ <comment xml:lang="sv">elektroniskt visitkort</comment>
+ <comment xml:lang="tr">elektronik iş kartı</comment>
+ <comment xml:lang="uk">електронна бізнес-картка</comment>
+ <comment xml:lang="vi">danh thiếp điện tử</comment>
+ <comment xml:lang="zh_CN">电子商务卡</comment>
+ <comment xml:lang="zh_TW">電子商務名片</comment>
+ <alias type="text/directory"/>
+ <alias type="text/x-vcard"/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="BEGIN:VCARD" type="string" offset="0"/>
+ <match value="begin:vcard" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.vcard"/>
+ <glob pattern="*.vcf"/>
+ <glob pattern="*.vct"/>
+ <glob pattern="*.gcrd"/>
+ </mime-type>
+ <mime-type type="text/turtle">
+ <comment>Turtle document</comment>
+ <comment xml:lang="ast">Documentu Turtle</comment>
+ <comment xml:lang="ca">document Turtle</comment>
+ <comment xml:lang="cs">dokument Turtle</comment>
+ <comment xml:lang="da">Turtle-dokument</comment>
+ <comment xml:lang="de">Turtle-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Turtle</comment>
+ <comment xml:lang="en_GB">Turtle document</comment>
+ <comment xml:lang="es">documento de Turtle</comment>
+ <comment xml:lang="eu">Turtle dokumentua</comment>
+ <comment xml:lang="fi">Turtle-asiakirja</comment>
+ <comment xml:lang="fr">document Turtle</comment>
+ <comment xml:lang="ga">cáipéis Turtle</comment>
+ <comment xml:lang="he">מסמך Turtle</comment>
+ <comment xml:lang="hr">Turtle dokument</comment>
+ <comment xml:lang="hu">Turtle dokumentum</comment>
+ <comment xml:lang="ia">Documento Turtle</comment>
+ <comment xml:lang="id">Dokumen Turtle</comment>
+ <comment xml:lang="it">Documento Turtle</comment>
+ <comment xml:lang="kk">Turtle құжаты</comment>
+ <comment xml:lang="ko">Turtle 문서</comment>
+ <comment xml:lang="oc">document Turtle</comment>
+ <comment xml:lang="pl">Dokument Turtle</comment>
+ <comment xml:lang="pt">documento Turtle</comment>
+ <comment xml:lang="pt_BR">Documento Turtle</comment>
+ <comment xml:lang="ru">Документ Turtle</comment>
+ <comment xml:lang="sk">Dokument Turtle</comment>
+ <comment xml:lang="sr">Тартл документ</comment>
+ <comment xml:lang="sv">Turtle-dokument</comment>
+ <comment xml:lang="tr">Turtle belgesi</comment>
+ <comment xml:lang="uk">документ Turtle</comment>
+ <comment xml:lang="zh_CN">Turtle 文档</comment>
+ <comment xml:lang="zh_TW">Turtle 文件</comment>
+ <glob pattern="*.ttl"/>
+ <sub-class-of type="text/plain"/>
+ </mime-type>
+ <mime-type type="text/x-txt2tags">
+ <comment>txt2tags document</comment>
+ <comment xml:lang="ar">مستند txt2tags</comment>
+ <comment xml:lang="ast">Documentu txt2tags</comment>
+ <comment xml:lang="be@latin">dakument txt2tags</comment>
+ <comment xml:lang="bg">Документ — txt2tags</comment>
+ <comment xml:lang="ca">document txt2tags</comment>
+ <comment xml:lang="cs">dokument txt2tags</comment>
+ <comment xml:lang="da">txt2tags-dokument</comment>
+ <comment xml:lang="de">txt2tags-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο txt2tags</comment>
+ <comment xml:lang="en_GB">txt2tags document</comment>
+ <comment xml:lang="eo">txt2tags-dokumento</comment>
+ <comment xml:lang="es">documento txt2tags</comment>
+ <comment xml:lang="eu">txt2tags dokumentua</comment>
+ <comment xml:lang="fi">txt2tags-asiakirja</comment>
+ <comment xml:lang="fo">txt2tags skjal</comment>
+ <comment xml:lang="fr">document txt2tags</comment>
+ <comment xml:lang="ga">cáipéis txt2tags</comment>
+ <comment xml:lang="gl">documento txt2tags</comment>
+ <comment xml:lang="he">מסמך txt2tags</comment>
+ <comment xml:lang="hr">txt2tags dokument</comment>
+ <comment xml:lang="hu">txt2tags dokumentum</comment>
+ <comment xml:lang="ia">Documento txt2tags</comment>
+ <comment xml:lang="id">Dokumen txt2tags</comment>
+ <comment xml:lang="it">Documento txt2tags</comment>
+ <comment xml:lang="ja">txt2tags ドキュメント</comment>
+ <comment xml:lang="ka">txt2tags დოკუმენტი</comment>
+ <comment xml:lang="kk">txt2tags құжаты</comment>
+ <comment xml:lang="ko">txt2tags 문서</comment>
+ <comment xml:lang="lt">txt2tags dokumentas</comment>
+ <comment xml:lang="lv">txt2tags dokuments</comment>
+ <comment xml:lang="nb">txt2tags-dokument</comment>
+ <comment xml:lang="nl">txt2tags-document</comment>
+ <comment xml:lang="nn">txt2tags-dokument</comment>
+ <comment xml:lang="oc">document txt2tags</comment>
+ <comment xml:lang="pl">Dokument txt2tags</comment>
+ <comment xml:lang="pt">documento txt2tags</comment>
+ <comment xml:lang="pt_BR">Documento do txt2tags</comment>
+ <comment xml:lang="ro">document txt2tags</comment>
+ <comment xml:lang="ru">Документ txt2tags</comment>
+ <comment xml:lang="sk">Dokument txt2tags</comment>
+ <comment xml:lang="sl">Dokument txt2tags</comment>
+ <comment xml:lang="sq">Dokument txt2tags</comment>
+ <comment xml:lang="sr">документ текста-у-ознаке</comment>
+ <comment xml:lang="sv">txt2tags-dokument</comment>
+ <comment xml:lang="tr">txt2tags belgesi</comment>
+ <comment xml:lang="uk">документ txt2tags</comment>
+ <comment xml:lang="vi">tài liệu txt2tags</comment>
+ <comment xml:lang="zh_CN">txt2tags 文档</comment>
+ <comment xml:lang="zh_TW">txt2tags 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="60">
+ <match value="%!postproc" type="string" offset="0"/>
+ <match value="%!encoding" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.t2t"/>
+ </mime-type>
+ <mime-type type="text/x-verilog">
+ <comment>Verilog source code</comment>
+ <comment xml:lang="bg">Изходен код — Verilog</comment>
+ <comment xml:lang="ca">codi font en Verilog</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Verilog</comment>
+ <comment xml:lang="da">Verilog-kildekode</comment>
+ <comment xml:lang="de">Verilog-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Verilog</comment>
+ <comment xml:lang="en_GB">Verilog source code</comment>
+ <comment xml:lang="eo">Verilog-fontkodo</comment>
+ <comment xml:lang="es">código fuente en Verilog</comment>
+ <comment xml:lang="eu">Verilog iturburu-kodea</comment>
+ <comment xml:lang="fi">Verilog-lähdekoodi</comment>
+ <comment xml:lang="fr">code source Verilog</comment>
+ <comment xml:lang="ga">cód foinseach Verilog</comment>
+ <comment xml:lang="gl">código fonte en Verilog</comment>
+ <comment xml:lang="he">קוד מקור של </comment>
+ <comment xml:lang="hr">Verilog izvorni kôd</comment>
+ <comment xml:lang="hu">Verilog-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Verilog</comment>
+ <comment xml:lang="id">Kode sumber Verilog</comment>
+ <comment xml:lang="it">Codice sorgente Verilog</comment>
+ <comment xml:lang="ja">Verilog ソースコード</comment>
+ <comment xml:lang="kk">Verilog бастапқы коды</comment>
+ <comment xml:lang="ko">Verilog 소스 코드</comment>
+ <comment xml:lang="lv">Verilog pirmkods</comment>
+ <comment xml:lang="nl">Verilog broncode</comment>
+ <comment xml:lang="oc">còde font Verilog</comment>
+ <comment xml:lang="pl">Kod źródłowy Verilog</comment>
+ <comment xml:lang="pt">código origem Verilog</comment>
+ <comment xml:lang="pt_BR">Código-fonte Verilog</comment>
+ <comment xml:lang="ru">Исходный код Verilog</comment>
+ <comment xml:lang="sk">Zdrojový kód Verilog</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Verilog</comment>
+ <comment xml:lang="sr">изворни код Верилога</comment>
+ <comment xml:lang="sv">Verilog-källkod</comment>
+ <comment xml:lang="tr">Verilog kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Verilog</comment>
+ <comment xml:lang="zh_CN">Verilog 源代码</comment>
+ <comment xml:lang="zh_TW">Verilog 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.v"/>
+ </mime-type>
+ <mime-type type="text/x-svhdr">
+ <comment>SystemVerilog header</comment>
+ <comment xml:lang="bg">Заглавен файл — SystemVerilog</comment>
+ <comment xml:lang="ca">capçalera de SystemVerilog</comment>
+ <comment xml:lang="cs">záhlaví SystemVerilog</comment>
+ <comment xml:lang="da">SystemVerilog-teksthoved</comment>
+ <comment xml:lang="de">SystemVerilog-Header</comment>
+ <comment xml:lang="el">Κεφαλίδα SystemVerilog</comment>
+ <comment xml:lang="en_GB">SystemVerilog header</comment>
+ <comment xml:lang="es">cabeceras de SystemVerilog</comment>
+ <comment xml:lang="eu">SystemVerilog goiburua</comment>
+ <comment xml:lang="fi">SystemVerilog-otsake</comment>
+ <comment xml:lang="fr">en-tête </comment>
+ <comment xml:lang="ga">ceanntásc SystemVerilog</comment>
+ <comment xml:lang="gl">Cabeceiras de SystemVerilog</comment>
+ <comment xml:lang="he">כותרת SystemVerilog</comment>
+ <comment xml:lang="hr">SystemVerilog zaglavlje</comment>
+ <comment xml:lang="hu">SystemVerilog fejléc</comment>
+ <comment xml:lang="ia">Capite SystemVerilog</comment>
+ <comment xml:lang="id">Header SystemVerilog</comment>
+ <comment xml:lang="it">Header SystemVerilog</comment>
+ <comment xml:lang="ja">SystemVerilog ヘッダー</comment>
+ <comment xml:lang="kk">SystemVerilog тақырыптамасы</comment>
+ <comment xml:lang="ko">SystemVerilog 헤더</comment>
+ <comment xml:lang="lv">SystemVerilog galvene</comment>
+ <comment xml:lang="nl">SystemVerilog header</comment>
+ <comment xml:lang="oc">entèsta SystemVerilog</comment>
+ <comment xml:lang="pl">Nagłówek SystemVerilog</comment>
+ <comment xml:lang="pt">cabeçalho SystemVerilog</comment>
+ <comment xml:lang="pt_BR">Cabeçalho de SystemVerilog</comment>
+ <comment xml:lang="ru">Заголовочный файл SystemVerilog</comment>
+ <comment xml:lang="sk">Hlavičky SystemVerilog</comment>
+ <comment xml:lang="sl">Datoteka glave SystemVerilog</comment>
+ <comment xml:lang="sr">заглавље Система Верилога</comment>
+ <comment xml:lang="sv">SystemVerilog-headerfil</comment>
+ <comment xml:lang="tr">SystemVerilog başlığı</comment>
+ <comment xml:lang="uk">заголовки SystemVerilog</comment>
+ <comment xml:lang="zh_CN">SystemVerilog 头文件</comment>
+ <comment xml:lang="zh_TW">SystemVerilog 標頭</comment>
+ <sub-class-of type="text/x-verilog"/>
+ <glob pattern="*.svh"/>
+ </mime-type>
+ <mime-type type="text/x-svsrc">
+ <comment>SystemVerilog source code</comment>
+ <comment xml:lang="bg">Изходен код — SystemVerilog</comment>
+ <comment xml:lang="ca">codi font en SystemVerilog</comment>
+ <comment xml:lang="cs">zdrojový kód SystemVerilog</comment>
+ <comment xml:lang="da">SystemVerilog-kildekode</comment>
+ <comment xml:lang="de">SystemVerilog-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας SystemVerilog</comment>
+ <comment xml:lang="en_GB">SystemVerilog source code</comment>
+ <comment xml:lang="es">código fuente en SystemVerilog</comment>
+ <comment xml:lang="eu">SystemVerilog iturburu-kodea</comment>
+ <comment xml:lang="fi">SystemVerilog-lähdekoodi</comment>
+ <comment xml:lang="fr">code source </comment>
+ <comment xml:lang="ga">cód foinseach SystemVerilog</comment>
+ <comment xml:lang="gl">código fonte en SystemVerilog</comment>
+ <comment xml:lang="he">קוד מקור של SystemVerilog</comment>
+ <comment xml:lang="hr">SystemVerilog izvorni kôd</comment>
+ <comment xml:lang="hu">SystemVerilog-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte SystemVerilog</comment>
+ <comment xml:lang="id">Kode sumber SystemVerilog</comment>
+ <comment xml:lang="it">Codice sorgente </comment>
+ <comment xml:lang="ja">SystemVerilog ソースコード</comment>
+ <comment xml:lang="kk">SystemVerilog бастапқы коды</comment>
+ <comment xml:lang="ko">SystemVerilog 소스 코드</comment>
+ <comment xml:lang="lv">SystemVerilog pirmkods</comment>
+ <comment xml:lang="nl">SystemVerilog broncode</comment>
+ <comment xml:lang="oc">còde font SystemVerilog</comment>
+ <comment xml:lang="pl">Kod źródłowy SystemVerilog</comment>
+ <comment xml:lang="pt">código origem SystemVerilog</comment>
+ <comment xml:lang="pt_BR">Código-fonte de SystemVerilog</comment>
+ <comment xml:lang="ru">Исходный код SystemVerilog</comment>
+ <comment xml:lang="sk">Zdrojový kód SystemVerilog</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode SystemVerilog</comment>
+ <comment xml:lang="sr">изворни код Система Верилога</comment>
+ <comment xml:lang="sv">SystemVerilog-källkod</comment>
+ <comment xml:lang="tr">SystemVerilog kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний файл мовою SystemVerilog</comment>
+ <comment xml:lang="zh_CN">SystemVerilog 源代码</comment>
+ <comment xml:lang="zh_TW">SystemVerilog 源碼</comment>
+ <sub-class-of type="text/x-verilog"/>
+ <glob pattern="*.sv"/>
+ </mime-type>
+ <mime-type type="text/x-vhdl">
+ <comment>VHDL source code</comment>
+ <comment xml:lang="bg">Изходен код — VHDL</comment>
+ <comment xml:lang="ca">codi font en VHDL</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce VHDL</comment>
+ <comment xml:lang="da">VHDL-kildekode</comment>
+ <comment xml:lang="de">VHDL-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας VHDL</comment>
+ <comment xml:lang="en_GB">VHDL source code</comment>
+ <comment xml:lang="eo">VHDL-fontkodo</comment>
+ <comment xml:lang="es">código fuente en VHDL</comment>
+ <comment xml:lang="eu">VHDL iturburu-kodea</comment>
+ <comment xml:lang="fi">VHDL-lähdekoodi</comment>
+ <comment xml:lang="fr">code source VHDL</comment>
+ <comment xml:lang="ga">cód foinseach VHDL</comment>
+ <comment xml:lang="gl">código fonte en VHDL</comment>
+ <comment xml:lang="he">קוד מקור של VHDL</comment>
+ <comment xml:lang="hr">VHDL izvorni kôd</comment>
+ <comment xml:lang="hu">VHDL-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte VHDL</comment>
+ <comment xml:lang="id">Kode sumber VHDL</comment>
+ <comment xml:lang="it">Codice sorgente VHDL</comment>
+ <comment xml:lang="ja">VHDL ソースコード</comment>
+ <comment xml:lang="kk">VHDL бастапқы коды</comment>
+ <comment xml:lang="ko">VHDL 소스 코드</comment>
+ <comment xml:lang="lv">VHDL pirmkods</comment>
+ <comment xml:lang="nl">VHDL broncode</comment>
+ <comment xml:lang="oc">còde font VHDL</comment>
+ <comment xml:lang="pl">Kod źródłowy VHDL</comment>
+ <comment xml:lang="pt">código origem VHDL</comment>
+ <comment xml:lang="pt_BR">Código-fonte VHDL</comment>
+ <comment xml:lang="ru">Исходный код VHDL</comment>
+ <comment xml:lang="sk">Zdrojový kód VHDL</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode VHDL</comment>
+ <comment xml:lang="sr">ВХДЛ изворни код</comment>
+ <comment xml:lang="sv">VHDL-källkod</comment>
+ <comment xml:lang="tr">VHDL kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою VHDL</comment>
+ <comment xml:lang="zh_CN">VHDL 源代码</comment>
+ <comment xml:lang="zh_TW">VHDL 源碼</comment>
+ <acronym>VHDL</acronym>
+ <expanded-acronym>Very-High-Speed Integrated Circuit Hardware Description Language</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.vhd"/>
+ <glob pattern="*.vhdl"/>
+ </mime-type>
+ <mime-type type="text/enriched">
+ <comment>enriched text document</comment>
+ <comment xml:lang="ar">مستند نصي مغنى</comment>
+ <comment xml:lang="ast">documentu de testu arriquecíu</comment>
+ <comment xml:lang="az">zəngin mətn sənədi</comment>
+ <comment xml:lang="be@latin">azdobleny tekstavy dakument</comment>
+ <comment xml:lang="bg">Документ с обогатен текст</comment>
+ <comment xml:lang="ca">document de text enriquit</comment>
+ <comment xml:lang="cs">rozšířený textový dokument</comment>
+ <comment xml:lang="cy">Dogfen testun wedi ei gyfoethogi</comment>
+ <comment xml:lang="da">beriget tekstdokument</comment>
+ <comment xml:lang="de">Angereichertes Textdokument</comment>
+ <comment xml:lang="el">Έγγραφο εμπλουτισμένου κειμένου</comment>
+ <comment xml:lang="en_GB">enriched text document</comment>
+ <comment xml:lang="eo">riĉigita teksta dokumento</comment>
+ <comment xml:lang="es">documento de texto enriquecido</comment>
+ <comment xml:lang="eu">aberastutako testu dokumentua</comment>
+ <comment xml:lang="fi">rikastettu tekstiasiakirja</comment>
+ <comment xml:lang="fo">ríkað tekstskjal</comment>
+ <comment xml:lang="fr">document texte enrichi</comment>
+ <comment xml:lang="ga">cáipéis téacs saibhrithe</comment>
+ <comment xml:lang="gl">documento de texto enriquecido</comment>
+ <comment xml:lang="he">מסמך טקסט מועשר</comment>
+ <comment xml:lang="hr">Obogaćeni tekstovni dokument</comment>
+ <comment xml:lang="hu">enriched text dokumentum</comment>
+ <comment xml:lang="ia">Documento de texto inricchite</comment>
+ <comment xml:lang="id">dokumen teks diperkaya</comment>
+ <comment xml:lang="it">Documento testo arricchito</comment>
+ <comment xml:lang="ja">リッチテキストドキュメント</comment>
+ <comment xml:lang="kk">пішімделген мәтіндік құжаты</comment>
+ <comment xml:lang="ko">확장된 텍스트 문서</comment>
+ <comment xml:lang="lt">praturtinto teksto dokumentas</comment>
+ <comment xml:lang="lv">bagātināta teksta formāts</comment>
+ <comment xml:lang="ms">Dokumen teks diperkaya</comment>
+ <comment xml:lang="nb">riktekst-dokument</comment>
+ <comment xml:lang="nl">verrijkt tekstdocument</comment>
+ <comment xml:lang="nn">rik tekst tekstdokument</comment>
+ <comment xml:lang="oc">document tèxte enriquit</comment>
+ <comment xml:lang="pl">Wzbogacony dokument tekstowy</comment>
+ <comment xml:lang="pt">documento de texto rico</comment>
+ <comment xml:lang="pt_BR">Documento de texto enriquecido</comment>
+ <comment xml:lang="ro">document text îmbogățit</comment>
+ <comment xml:lang="ru">Форматированный текстовый документ</comment>
+ <comment xml:lang="sk">Rozšírený textový dokument</comment>
+ <comment xml:lang="sl">dokument z obogatenim besedilom</comment>
+ <comment xml:lang="sq">Dokument teksti i pasuruar</comment>
+ <comment xml:lang="sr">обогаћени текстуални документ</comment>
+ <comment xml:lang="sv">berikat textdokument</comment>
+ <comment xml:lang="tr">zenginleştirilmiş metin belgesi</comment>
+ <comment xml:lang="uk">форматований текстовий документ</comment>
+ <comment xml:lang="vi">tài liệu văn bản có kiểu dáng</comment>
+ <comment xml:lang="zh_CN">浓缩文本文档 (ETF)</comment>
+ <comment xml:lang="zh_TW">豐富化文字文件</comment>
+ <sub-class-of type="text/plain"/>
+ </mime-type>
+ <mime-type type="text/htmlh">
+ <comment>help page</comment>
+ <comment xml:lang="ar">صفحة المساعدة</comment>
+ <comment xml:lang="az">yardım səhifəsi</comment>
+ <comment xml:lang="be@latin">staronka dapamohi</comment>
+ <comment xml:lang="bg">Страница от помощта</comment>
+ <comment xml:lang="ca">pàgina d'ajuda</comment>
+ <comment xml:lang="cs">stránka nápovědy</comment>
+ <comment xml:lang="cy">tudalen gymorth</comment>
+ <comment xml:lang="da">hjælpeside</comment>
+ <comment xml:lang="de">Hilfeseite</comment>
+ <comment xml:lang="el">Σελίδα βοήθειας</comment>
+ <comment xml:lang="en_GB">help page</comment>
+ <comment xml:lang="eo">help-paĝo</comment>
+ <comment xml:lang="es">página de ayuda</comment>
+ <comment xml:lang="eu">laguntzako orria</comment>
+ <comment xml:lang="fi">ohjesivu</comment>
+ <comment xml:lang="fo">hjálparsíða</comment>
+ <comment xml:lang="fr">page d'aide</comment>
+ <comment xml:lang="ga">leathanach cabhrach</comment>
+ <comment xml:lang="gl">páxina de axuda</comment>
+ <comment xml:lang="he">דף עזרה</comment>
+ <comment xml:lang="hr">Stranica pomoći</comment>
+ <comment xml:lang="hu">súgóoldal</comment>
+ <comment xml:lang="ia">Pagina de adjuta</comment>
+ <comment xml:lang="id">halaman bantuan</comment>
+ <comment xml:lang="it">Pagina di aiuto</comment>
+ <comment xml:lang="ja">ヘルプページ</comment>
+ <comment xml:lang="kk">анықтама парағы</comment>
+ <comment xml:lang="ko">도움말 페이지</comment>
+ <comment xml:lang="lt">žinyno puslapis</comment>
+ <comment xml:lang="lv">palīdzības lapa</comment>
+ <comment xml:lang="ms">Halaman bantuan</comment>
+ <comment xml:lang="nb">hjelpside</comment>
+ <comment xml:lang="nl">hulppagina</comment>
+ <comment xml:lang="nn">hjelpeside</comment>
+ <comment xml:lang="oc">pagina d'ajuda</comment>
+ <comment xml:lang="pl">Strona pomocy</comment>
+ <comment xml:lang="pt">página de ajuda</comment>
+ <comment xml:lang="pt_BR">Página de ajuda</comment>
+ <comment xml:lang="ro">pagină de ajutor</comment>
+ <comment xml:lang="ru">Страница справки</comment>
+ <comment xml:lang="sk">Stránka Pomocníka</comment>
+ <comment xml:lang="sl">stran pomoči</comment>
+ <comment xml:lang="sq">Faqe ndihme</comment>
+ <comment xml:lang="sr">страница помоћи</comment>
+ <comment xml:lang="sv">hjälpsida</comment>
+ <comment xml:lang="tr">yardım sayfası</comment>
+ <comment xml:lang="uk">сторінка довідки</comment>
+ <comment xml:lang="vi">trang trợ giúp</comment>
+ <comment xml:lang="zh_CN">帮助页面</comment>
+ <comment xml:lang="zh_TW">求助頁面</comment>
+ <sub-class-of type="text/plain"/>
+ </mime-type>
+ <mime-type type="text/plain">
+ <comment>plain text document</comment>
+ <comment xml:lang="ar">مستند نصي مجرد</comment>
+ <comment xml:lang="ast">documentu de testu planu</comment>
+ <comment xml:lang="be@latin">prosty tekstavy dakument</comment>
+ <comment xml:lang="bg">Документ с неформатиран текст</comment>
+ <comment xml:lang="ca">document de text pla</comment>
+ <comment xml:lang="cs">prostý textový dokument</comment>
+ <comment xml:lang="da">rent tekstdokument</comment>
+ <comment xml:lang="de">Einfaches Textdokument</comment>
+ <comment xml:lang="el">Έγγραφο απλού κειμένου</comment>
+ <comment xml:lang="en_GB">plain text document</comment>
+ <comment xml:lang="eo">plata teksta dokumento</comment>
+ <comment xml:lang="es">documento de texto sencillo</comment>
+ <comment xml:lang="eu">testu soileko dokumentua</comment>
+ <comment xml:lang="fi">perustekstiasiakirja</comment>
+ <comment xml:lang="fr">document texte brut</comment>
+ <comment xml:lang="ga">cáipéis ghnáth-théacs</comment>
+ <comment xml:lang="gl">documento de texto sinxelo</comment>
+ <comment xml:lang="he">מסמך טקסט פשוט</comment>
+ <comment xml:lang="hr">Običan tekstovni dokument</comment>
+ <comment xml:lang="hu">egyszerű szöveg</comment>
+ <comment xml:lang="ia">Documento de texto simple</comment>
+ <comment xml:lang="id">dokumen teks polos</comment>
+ <comment xml:lang="it">Documento in testo semplice</comment>
+ <comment xml:lang="ja">平文テキストドキュメント</comment>
+ <comment xml:lang="kk">мәтіндік құжаты</comment>
+ <comment xml:lang="ko">일반 텍스트 문서</comment>
+ <comment xml:lang="lt">paprastas tekstinis dokumentas</comment>
+ <comment xml:lang="lv">vienkāršs teksta dokuments</comment>
+ <comment xml:lang="ms">Dokumen teks jernih</comment>
+ <comment xml:lang="nb">vanlig tekstdokument</comment>
+ <comment xml:lang="nl">plattetekst-document</comment>
+ <comment xml:lang="nn">vanleg tekstdokument</comment>
+ <comment xml:lang="oc">document tèxte brut</comment>
+ <comment xml:lang="pl">Zwykły dokument tekstowy</comment>
+ <comment xml:lang="pt">documento em texto simples</comment>
+ <comment xml:lang="pt_BR">Documento de Texto</comment>
+ <comment xml:lang="ro">document text simplu</comment>
+ <comment xml:lang="ru">Текстовый документ</comment>
+ <comment xml:lang="sk">Obyčajný textový dokument</comment>
+ <comment xml:lang="sl">običajna besedilna datoteka</comment>
+ <comment xml:lang="sq">Dokument në tekst të thjeshtë</comment>
+ <comment xml:lang="sr">обичан текстуални документ</comment>
+ <comment xml:lang="sv">vanligt textdokument</comment>
+ <comment xml:lang="tr">düz metin belgesi</comment>
+ <comment xml:lang="uk">звичайний текстовий документ</comment>
+ <comment xml:lang="vi">tài liệu nhập thô</comment>
+ <comment xml:lang="zh_CN">纯文本文档</comment>
+ <comment xml:lang="zh_TW">純文字文件</comment>
+ <magic priority="50">
+ <match value="This is TeX," type="string" offset="0"/>
+ <match value="This is METAFONT," type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.txt"/>
+ <glob pattern="*.asc"/>
+ <glob pattern="*,v"/>
+ </mime-type>
+ <mime-type type="application/rdf+xml">
+ <comment>RDF file</comment>
+ <comment xml:lang="ar">ملف RDF</comment>
+ <comment xml:lang="be@latin">Fajł RDF</comment>
+ <comment xml:lang="bg">Файл — RDF</comment>
+ <comment xml:lang="ca">fitxer RDF</comment>
+ <comment xml:lang="cs">soubor RDF</comment>
+ <comment xml:lang="da">RDF-fil</comment>
+ <comment xml:lang="de">RDF-Datei</comment>
+ <comment xml:lang="el">Αρχείο RDF</comment>
+ <comment xml:lang="en_GB">RDF file</comment>
+ <comment xml:lang="eo">RDF-dosiero</comment>
+ <comment xml:lang="es">archivo RDF</comment>
+ <comment xml:lang="eu">RDF fitxategia</comment>
+ <comment xml:lang="fi">RDF-tiedosto</comment>
+ <comment xml:lang="fo">RDF fíla</comment>
+ <comment xml:lang="fr">fichier RDF</comment>
+ <comment xml:lang="ga">comhad RDF</comment>
+ <comment xml:lang="gl">ficheiro RDF</comment>
+ <comment xml:lang="he">קובץ RDF</comment>
+ <comment xml:lang="hr">RDF datoteka</comment>
+ <comment xml:lang="hu">RDF fájl</comment>
+ <comment xml:lang="ia">File RDF</comment>
+ <comment xml:lang="id">Arsip RDF</comment>
+ <comment xml:lang="it">File RDF</comment>
+ <comment xml:lang="ja">RDF ファイル</comment>
+ <comment xml:lang="kk">RDF файлы</comment>
+ <comment xml:lang="ko">RDF 파일</comment>
+ <comment xml:lang="lt">RDF failas</comment>
+ <comment xml:lang="lv">RDF datne</comment>
+ <comment xml:lang="nb">RDF-fil</comment>
+ <comment xml:lang="nl">RDF-bestand</comment>
+ <comment xml:lang="nn">RDF-fil</comment>
+ <comment xml:lang="oc">fichièr RDF</comment>
+ <comment xml:lang="pl">Plik RDF</comment>
+ <comment xml:lang="pt">ficheiro RDF</comment>
+ <comment xml:lang="pt_BR">Arquivo RDF</comment>
+ <comment xml:lang="ro">Fișier RDF</comment>
+ <comment xml:lang="ru">Файл RDF</comment>
+ <comment xml:lang="sk">Súbor RDF</comment>
+ <comment xml:lang="sl">Datoteka RDF</comment>
+ <comment xml:lang="sq">File RDF</comment>
+ <comment xml:lang="sr">РДФ датотека</comment>
+ <comment xml:lang="sv">RDF-fil</comment>
+ <comment xml:lang="tr">RDF dosyası</comment>
+ <comment xml:lang="uk">файл RDF</comment>
+ <comment xml:lang="vi">Tập tin RDF</comment>
+ <comment xml:lang="zh_CN">RDF 文件</comment>
+ <comment xml:lang="zh_TW">RDF 檔</comment>
+ <acronym>RDF</acronym>
+ <expanded-acronym>Resource Description Framework</expanded-acronym>
+ <alias type="text/rdf"/>
+ <sub-class-of type="application/xml"/>
+ <glob pattern="*.rdf"/>
+ <glob pattern="*.rdfs"/>
+ <glob pattern="*.owl"/>
+ <root-XML namespaceURI="http://www.w3.org/1999/02/22-rdf-syntax-ns#" localName="RDF"/>
+ </mime-type>
+ <mime-type type="application/owl+xml">
+ <comment>OWL XML file</comment>
+ <comment xml:lang="ca">fitxer XML OWL</comment>
+ <comment xml:lang="cs">soubor OWL XML</comment>
+ <comment xml:lang="da">OWL XML-fil</comment>
+ <comment xml:lang="de">OWL-XML-Datei</comment>
+ <comment xml:lang="el">Αρχείο OWL XML</comment>
+ <comment xml:lang="en_GB">OWL XML file</comment>
+ <comment xml:lang="es">archivo en XML OWL</comment>
+ <comment xml:lang="eu">OWL XML fitxategia</comment>
+ <comment xml:lang="fr">fichier XML OWL</comment>
+ <comment xml:lang="ga">comhad XML OWL</comment>
+ <comment xml:lang="hr">OWL XML datoteka</comment>
+ <comment xml:lang="hu">OWL XML-fájl</comment>
+ <comment xml:lang="ia">File XML OWL</comment>
+ <comment xml:lang="id">Berkas XML OWL</comment>
+ <comment xml:lang="it">File XML OWL</comment>
+ <comment xml:lang="kk">OWL XML файлы</comment>
+ <comment xml:lang="ko">OWL XML 파일</comment>
+ <comment xml:lang="oc">fichièr OWL XML</comment>
+ <comment xml:lang="pl">Plik XML OWL</comment>
+ <comment xml:lang="pt">ficheiro OWL XML</comment>
+ <comment xml:lang="pt_BR">Arquivo OWL XML</comment>
+ <comment xml:lang="ru">Файл XML OWL</comment>
+ <comment xml:lang="sk">Súbor XML OWL</comment>
+ <comment xml:lang="sr">ОВЛ ИксМЛ датотека</comment>
+ <comment xml:lang="sv">OWL XML-fil</comment>
+ <comment xml:lang="tr">OWL XML dosyası</comment>
+ <comment xml:lang="uk">файл XML OWL</comment>
+ <comment xml:lang="zh_CN">OWL XML 文件</comment>
+ <comment xml:lang="zh_TW">OWL XML 檔案</comment>
+ <acronym>OWL</acronym>
+ <expanded-acronym>Web Ontology Language</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <glob pattern="*.owx"/>
+ <magic>
+ <match value="&lt;Ontology" type="string" offset="0:256"/>
+ </magic>
+ <root-XML namespaceURI="http://www.w3.org/2002/07/owl#" localName="Ontology"/>
+ </mime-type>
+ <mime-type type="text/rfc822-headers">
+ <comment>email headers</comment>
+ <comment xml:lang="ar">ترويسة البريد الإلكتروني</comment>
+ <comment xml:lang="az">epoçt başlıqları</comment>
+ <comment xml:lang="be@latin">paštovyja zahałoŭki</comment>
+ <comment xml:lang="bg">Заглавни части на електронни писма</comment>
+ <comment xml:lang="ca">capçaleres de correu electrònic</comment>
+ <comment xml:lang="cs">záhlaví e-mailu</comment>
+ <comment xml:lang="cy">penawdau e-bost</comment>
+ <comment xml:lang="da">posthoveder</comment>
+ <comment xml:lang="de">E-Mail-Kopfzeilen</comment>
+ <comment xml:lang="el">Κεφαλίδες ηλ. μηνυμάτων</comment>
+ <comment xml:lang="en_GB">email headers</comment>
+ <comment xml:lang="eo">retpoŝtaj ĉapoj</comment>
+ <comment xml:lang="es">cabeceras de correo electrónico</comment>
+ <comment xml:lang="eu">helbide elektronikoen goiburuak</comment>
+ <comment xml:lang="fi">sähköpostiotsakkeet</comment>
+ <comment xml:lang="fo">t-post tekshøvd</comment>
+ <comment xml:lang="fr">en-têtes de courriel</comment>
+ <comment xml:lang="ga">ceanntásca ríomhphoist</comment>
+ <comment xml:lang="gl">cabeceiras de correo electrónico</comment>
+ <comment xml:lang="he">כותרת דוא״ל</comment>
+ <comment xml:lang="hr">Zaglavlja e-pošte</comment>
+ <comment xml:lang="hu">levélfejléc</comment>
+ <comment xml:lang="ia">Capites de e-mail</comment>
+ <comment xml:lang="id">header email</comment>
+ <comment xml:lang="it">Intestazioni email</comment>
+ <comment xml:lang="ja">メールヘッダー</comment>
+ <comment xml:lang="kk">пошталық тақырыптамалары</comment>
+ <comment xml:lang="ko">전자메일 헤더</comment>
+ <comment xml:lang="lt">el. laiško antraštės</comment>
+ <comment xml:lang="lv">e-pasta galvene</comment>
+ <comment xml:lang="ms">Pengepala emel</comment>
+ <comment xml:lang="nb">e-posthode</comment>
+ <comment xml:lang="nl">e-mail-kopregels</comment>
+ <comment xml:lang="nn">e-post-hovud</comment>
+ <comment xml:lang="oc">entèstas de corrièr electronic</comment>
+ <comment xml:lang="pl">Nagłówki wiadomości e-mail</comment>
+ <comment xml:lang="pt">cabeçalhos de email</comment>
+ <comment xml:lang="pt_BR">Cabeçalhos de e-mail</comment>
+ <comment xml:lang="ro">antete email</comment>
+ <comment xml:lang="ru">Почтовые заголовки</comment>
+ <comment xml:lang="sk">Hlavičky e-mailu</comment>
+ <comment xml:lang="sl">glava elektronske pošte</comment>
+ <comment xml:lang="sq">Header email</comment>
+ <comment xml:lang="sr">заглавља ел. поште</comment>
+ <comment xml:lang="sv">e-posthuvuden</comment>
+ <comment xml:lang="tr">eposta başlığı</comment>
+ <comment xml:lang="uk">заголовки email</comment>
+ <comment xml:lang="vi">dòng đầu thư điện tử</comment>
+ <comment xml:lang="zh_CN">电子邮件头</comment>
+ <comment xml:lang="zh_TW">電子郵件標頭</comment>
+ <sub-class-of type="text/plain"/>
+ </mime-type>
+ <mime-type type="text/richtext">
+ <comment>rich text document</comment>
+ <comment xml:lang="ar">مستند نصي غني</comment>
+ <comment xml:lang="ast">documentu de testu ricu</comment>
+ <comment xml:lang="az">zəngin mətn sənədi</comment>
+ <comment xml:lang="be@latin">azdobleny tekstavy dakument</comment>
+ <comment xml:lang="bg">Документ — rich text</comment>
+ <comment xml:lang="ca">document de text enriquit</comment>
+ <comment xml:lang="cs">textový dokument RTF</comment>
+ <comment xml:lang="cy">dogfen testun gyfoethog (rtf)</comment>
+ <comment xml:lang="da">richtekstdokument</comment>
+ <comment xml:lang="de">RTF-Textdokument</comment>
+ <comment xml:lang="el">Έγγραφο εμπλουτισμένου κειμένου (RTF)</comment>
+ <comment xml:lang="en_GB">rich text document</comment>
+ <comment xml:lang="eo">riĉteksta dokumento</comment>
+ <comment xml:lang="es">documento de texto enriquecido</comment>
+ <comment xml:lang="eu">aberastutako testu formatua</comment>
+ <comment xml:lang="fi">RTF-asiakirja</comment>
+ <comment xml:lang="fr">document « rich text »</comment>
+ <comment xml:lang="ga">cáipéis mhéith-théacs</comment>
+ <comment xml:lang="gl">documento do texto enriquecido</comment>
+ <comment xml:lang="he">מסמך טקסט עשיר</comment>
+ <comment xml:lang="hr">Obogaćeni tekstovni dokument</comment>
+ <comment xml:lang="hu">rich text-dokumentum</comment>
+ <comment xml:lang="ia">Documento de texto inricchite</comment>
+ <comment xml:lang="id">dokumen teks kaya</comment>
+ <comment xml:lang="it">Documento rich text</comment>
+ <comment xml:lang="ja">リッチテキストドキュメント</comment>
+ <comment xml:lang="kk">пішімделген мәтіні бар құжаты</comment>
+ <comment xml:lang="ko">서식 있는 텍스트 문서</comment>
+ <comment xml:lang="lt">praturtinto teksto dokumentas</comment>
+ <comment xml:lang="lv">bagātā teksta dokuments</comment>
+ <comment xml:lang="ms">Dokumen teks diperkaya</comment>
+ <comment xml:lang="nb">rik tekst-dokument</comment>
+ <comment xml:lang="nl">opgemaakt tekstdocument</comment>
+ <comment xml:lang="nn">rik tekst-dokument</comment>
+ <comment xml:lang="oc">document « rich text »</comment>
+ <comment xml:lang="pl">Dokument Rich Text</comment>
+ <comment xml:lang="pt">documento em texto rico</comment>
+ <comment xml:lang="pt_BR">Documento rich text</comment>
+ <comment xml:lang="ro">document text îmbogățit</comment>
+ <comment xml:lang="ru">Документ с форматированным текстом</comment>
+ <comment xml:lang="sk">Textový dokument RTF</comment>
+ <comment xml:lang="sl">dokument z oblikovanim besedilom</comment>
+ <comment xml:lang="sq">Dokument rich text</comment>
+ <comment xml:lang="sr">богат текстуални документ</comment>
+ <comment xml:lang="sv">RTF-textdokument</comment>
+ <comment xml:lang="tr">zengin metin belgesi</comment>
+ <comment xml:lang="uk">форматований текстовий документ</comment>
+ <comment xml:lang="vi">tài liệu văn bản có kiểu dáng (RTF)</comment>
+ <comment xml:lang="zh_CN">富文本文档 (RTF)</comment>
+ <comment xml:lang="zh_TW">豐富文字文件</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.rtx"/>
+ </mime-type>
+ <mime-type type="application/rss+xml">
+ <comment>RSS summary</comment>
+ <comment xml:lang="ar">ملخص RSS</comment>
+ <comment xml:lang="be@latin">Karotki ahlad RSS</comment>
+ <comment xml:lang="bg">Обобщение за сайтове — RSS</comment>
+ <comment xml:lang="ca">resum RSS</comment>
+ <comment xml:lang="cs">souhrn RSS</comment>
+ <comment xml:lang="da">RSS-sammendrag</comment>
+ <comment xml:lang="de">RSS-Zusammenfassung</comment>
+ <comment xml:lang="el">Σύνοψη RSS</comment>
+ <comment xml:lang="en_GB">RSS summary</comment>
+ <comment xml:lang="es">resumen de RSS</comment>
+ <comment xml:lang="eu">RSS laburpena</comment>
+ <comment xml:lang="fi">RSS-tiivistelmä</comment>
+ <comment xml:lang="fo">RSS samandráttur</comment>
+ <comment xml:lang="fr">résumé RSS</comment>
+ <comment xml:lang="ga">achoimre RSS</comment>
+ <comment xml:lang="gl">Resumo RSS</comment>
+ <comment xml:lang="he">תקציר RSS</comment>
+ <comment xml:lang="hr">RSS sažetak</comment>
+ <comment xml:lang="hu">RSS összefoglaló</comment>
+ <comment xml:lang="ia">Summario RSS</comment>
+ <comment xml:lang="id">Ringkasan RSS</comment>
+ <comment xml:lang="it">Sommario RSS</comment>
+ <comment xml:lang="ja">RSS サマリ</comment>
+ <comment xml:lang="kk">RSS жинақталғаны</comment>
+ <comment xml:lang="ko">RSS 요약</comment>
+ <comment xml:lang="lt">RSS santrauka</comment>
+ <comment xml:lang="lv">RSS kopsavilkums</comment>
+ <comment xml:lang="nb">RSS-sammendrag</comment>
+ <comment xml:lang="nl">RSS-samenvatting</comment>
+ <comment xml:lang="nn">RSS-samandrag</comment>
+ <comment xml:lang="oc">resumit RSS</comment>
+ <comment xml:lang="pl">Podsumowanie RSS</comment>
+ <comment xml:lang="pt">resumo RSS</comment>
+ <comment xml:lang="pt_BR">Resumo RSS</comment>
+ <comment xml:lang="ro">Rezumat RSS</comment>
+ <comment xml:lang="ru">Сводка RSS</comment>
+ <comment xml:lang="sk">Súhrn RSS</comment>
+ <comment xml:lang="sl">Datoteka povzetek RSS</comment>
+ <comment xml:lang="sq">Përmbledhje RSS</comment>
+ <comment xml:lang="sr">РСС сажетак</comment>
+ <comment xml:lang="sv">RSS-sammanfattning</comment>
+ <comment xml:lang="tr">RSS özeti</comment>
+ <comment xml:lang="uk">зведення сайту RSS</comment>
+ <comment xml:lang="vi">Bản tóm tắt RSS</comment>
+ <comment xml:lang="zh_CN">RSS 摘要</comment>
+ <comment xml:lang="zh_TW">RSS 摘要</comment>
+ <acronym>RSS</acronym>
+ <expanded-acronym>RDF Site Summary</expanded-acronym>
+ <alias type="text/rss"/>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="text-html"/>
+ <glob pattern="*.rss"/>
+ <magic priority="70">
+ <match value="&lt;rss " type="string" offset="0:256"/>
+ <match value="&lt;RSS " type="string" offset="0:256"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/atom+xml">
+ <comment>Atom syndication feed</comment>
+ <comment xml:lang="ar">مروج تغذية Atom</comment>
+ <comment xml:lang="be@latin">Syndykacyjny kanał navinaŭ Atom</comment>
+ <comment xml:lang="bg">Емисия — Atom</comment>
+ <comment xml:lang="ca">canal de sindicació Atom</comment>
+ <comment xml:lang="cs">kanál Atom</comment>
+ <comment xml:lang="da">Atom syndication-feed</comment>
+ <comment xml:lang="de">Atom-Nachrichtenquelle</comment>
+ <comment xml:lang="el">Τροφοδοσία διανομής Atom</comment>
+ <comment xml:lang="en_GB">Atom syndication feed</comment>
+ <comment xml:lang="es">canal de noticias Atom</comment>
+ <comment xml:lang="eu">Atom harpidetze-iturria</comment>
+ <comment xml:lang="fi">Atom-yhdistevirta</comment>
+ <comment xml:lang="fr">fil de syndication Atom</comment>
+ <comment xml:lang="ga">fotha sindeacáitithe Atom</comment>
+ <comment xml:lang="gl">fonte de sindicación Atom</comment>
+ <comment xml:lang="he">הזנה דרך הרשת של Atom</comment>
+ <comment xml:lang="hr">Atom kanal objavljivanja</comment>
+ <comment xml:lang="hu">Atom egyesítőfolyam</comment>
+ <comment xml:lang="ia">Fluxo de syndication Atom</comment>
+ <comment xml:lang="id">Umpan sindikasi Atom</comment>
+ <comment xml:lang="it">Feed di distribuzione Atom</comment>
+ <comment xml:lang="ja">Atom 配信フィード</comment>
+ <comment xml:lang="kk">Atom жаңалықтар таспасы</comment>
+ <comment xml:lang="ko">Atom 묶음 피드</comment>
+ <comment xml:lang="lt">Atom sindikacijos kanalas</comment>
+ <comment xml:lang="lv">Atom sindikāta barotne</comment>
+ <comment xml:lang="nb">Atom syndikeringsstrøm</comment>
+ <comment xml:lang="nl">Atom-syndicatie-feed</comment>
+ <comment xml:lang="nn">Atom-kjelde</comment>
+ <comment xml:lang="oc">fial de sindicacion Atom</comment>
+ <comment xml:lang="pl">Kanał Atom</comment>
+ <comment xml:lang="pt">feed Atom</comment>
+ <comment xml:lang="pt_BR">Fonte de notícias Atom</comment>
+ <comment xml:lang="ro">Flux agregare Atom</comment>
+ <comment xml:lang="ru">Лента новостей Atom</comment>
+ <comment xml:lang="sk">Kanál Atom</comment>
+ <comment xml:lang="sl">Sindikalni vir Atom</comment>
+ <comment xml:lang="sq">Feed për përhapje Atom</comment>
+ <comment xml:lang="sr">Атомов довод синдикализације</comment>
+ <comment xml:lang="sv">Atom-syndikeringskanal</comment>
+ <comment xml:lang="tr">Atom besleme kaynağı</comment>
+ <comment xml:lang="uk">трансляція подач Atom</comment>
+ <comment xml:lang="vi">Nguồn tin tức Atom</comment>
+ <comment xml:lang="zh_CN">Atom 聚合种子</comment>
+ <comment xml:lang="zh_TW">Atom 聯合供稿饋流</comment>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="text-html"/>
+ <glob pattern="*.atom"/>
+ <magic priority="70">
+ <match value="&lt;feed " type="string" offset="0:256"/>
+ </magic>
+ <root-XML namespaceURI="http://www.w3.org/2005/Atom" localName="feed"/>
+ </mime-type>
+ <mime-type type="text/x-opml+xml">
+ <comment>OPML syndication feed</comment>
+ <comment xml:lang="ar">مروج تغذية OPML</comment>
+ <comment xml:lang="be@latin">Syndykacyjny kanał OPML</comment>
+ <comment xml:lang="bg">Емисия — OPML</comment>
+ <comment xml:lang="ca">canal de sindicació OPML</comment>
+ <comment xml:lang="cs">kanál OPML</comment>
+ <comment xml:lang="da">OPML-syndikeringsfeed</comment>
+ <comment xml:lang="de">OPML-Nachrichtenquelle</comment>
+ <comment xml:lang="el">Τροφοδοσία OPML</comment>
+ <comment xml:lang="en_GB">OPML syndication feed</comment>
+ <comment xml:lang="es">canal de noticias OPML</comment>
+ <comment xml:lang="eu">OPML harpidetze-iturria</comment>
+ <comment xml:lang="fi">OPML-yhdistevirta</comment>
+ <comment xml:lang="fr">fil de syndication OPML</comment>
+ <comment xml:lang="ga">fotha sindeacáitithe OPML</comment>
+ <comment xml:lang="gl">fonte de sindicación OPML</comment>
+ <comment xml:lang="he">הזנה דרך הרשת OPML</comment>
+ <comment xml:lang="hr">OPML kanal objavljivanja</comment>
+ <comment xml:lang="hu">OPML egyesítőfolyam</comment>
+ <comment xml:lang="ia">Fluxo de syndication OPML</comment>
+ <comment xml:lang="id">Umpan sindikasi OPML</comment>
+ <comment xml:lang="it">Feed di distribuzione OPML</comment>
+ <comment xml:lang="ja">OPML 配信フィード</comment>
+ <comment xml:lang="kk">OPML жаңалықтар таспасы</comment>
+ <comment xml:lang="ko">OPML 묶음 피드</comment>
+ <comment xml:lang="lt">OPML sindikacijos kanalas</comment>
+ <comment xml:lang="lv">OPML sindikāta barotne</comment>
+ <comment xml:lang="nb">OPML syndikeringsstrøm</comment>
+ <comment xml:lang="nl">OPML-syndicatie-feed</comment>
+ <comment xml:lang="nn">OPML-kjelde</comment>
+ <comment xml:lang="oc">fial de sindicacion OPML</comment>
+ <comment xml:lang="pl">Kanał OPML</comment>
+ <comment xml:lang="pt">feed OPML</comment>
+ <comment xml:lang="pt_BR">Fonte de notícias OPML</comment>
+ <comment xml:lang="ro">Flux OPML syndication</comment>
+ <comment xml:lang="ru">Лента новостей OPML</comment>
+ <comment xml:lang="sk">Kanál OPML</comment>
+ <comment xml:lang="sl">Sindikalni vir OPML</comment>
+ <comment xml:lang="sq">Feed për përhapje OPML</comment>
+ <comment xml:lang="sr">ОМПЛ довод синдикализације</comment>
+ <comment xml:lang="sv">OPML-syndikeringskanal</comment>
+ <comment xml:lang="tr">OPML besleme kaynağı</comment>
+ <comment xml:lang="uk">трансляція подач OPML</comment>
+ <comment xml:lang="vi">Nguồn tin tức OPML</comment>
+ <comment xml:lang="zh_CN">OPML 聚合种子</comment>
+ <comment xml:lang="zh_TW">OPML 聯合供稿饋流</comment>
+ <sub-class-of type="application/xml"/>
+ <alias type="text/x-opml"/>
+ <generic-icon name="text-html"/>
+ <glob pattern="*.opml"/>
+ <magic priority="70">
+ <match value="&lt;opml " type="string" offset="0:256"/>
+ </magic>
+ </mime-type>
+ <mime-type type="text/sgml">
+ <comment>SGML document</comment>
+ <comment xml:lang="ar">مستند SGML</comment>
+ <comment xml:lang="ast">Documentu SGML</comment>
+ <comment xml:lang="be@latin">Dakument SGML</comment>
+ <comment xml:lang="bg">Документ — SGML</comment>
+ <comment xml:lang="ca">document SGML</comment>
+ <comment xml:lang="cs">dokument SGML</comment>
+ <comment xml:lang="cy">Dogfen SGML</comment>
+ <comment xml:lang="da">SGML-dokument</comment>
+ <comment xml:lang="de">SGML-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο SGML</comment>
+ <comment xml:lang="en_GB">SGML document</comment>
+ <comment xml:lang="eo">SGML-dokumento</comment>
+ <comment xml:lang="es">documento SGML</comment>
+ <comment xml:lang="eu">SGML dokumentua</comment>
+ <comment xml:lang="fi">SGML-asiakirja</comment>
+ <comment xml:lang="fo">SGML skjal</comment>
+ <comment xml:lang="fr">document SGML</comment>
+ <comment xml:lang="ga">cáipéis SGML</comment>
+ <comment xml:lang="gl">documento SGML</comment>
+ <comment xml:lang="he">מסמך SGML</comment>
+ <comment xml:lang="hr">SGML dokument</comment>
+ <comment xml:lang="hu">SGML-dokumentum</comment>
+ <comment xml:lang="ia">Documento SGML</comment>
+ <comment xml:lang="id">Dokumen SGML</comment>
+ <comment xml:lang="it">Documento SGML</comment>
+ <comment xml:lang="ja">SGML ドキュメント</comment>
+ <comment xml:lang="kk">SGML құжаты</comment>
+ <comment xml:lang="ko">SGML 문서</comment>
+ <comment xml:lang="lt">SGML dokumentas</comment>
+ <comment xml:lang="lv">SGML dokuments</comment>
+ <comment xml:lang="ms">Dokumen SGML</comment>
+ <comment xml:lang="nb">SGML-dokument</comment>
+ <comment xml:lang="nl">SGML-document</comment>
+ <comment xml:lang="nn">SGML-dokument</comment>
+ <comment xml:lang="oc">document SGML</comment>
+ <comment xml:lang="pl">Dokument SGML</comment>
+ <comment xml:lang="pt">documento SGML</comment>
+ <comment xml:lang="pt_BR">Documento SGML</comment>
+ <comment xml:lang="ro">Document SGML</comment>
+ <comment xml:lang="ru">Документ SGML</comment>
+ <comment xml:lang="sk">Dokument SGML</comment>
+ <comment xml:lang="sl">Dokument SGML</comment>
+ <comment xml:lang="sq">Dokument SGML</comment>
+ <comment xml:lang="sr">СГМЛ документ</comment>
+ <comment xml:lang="sv">SGML-dokument</comment>
+ <comment xml:lang="tr">SGML belgesi</comment>
+ <comment xml:lang="uk">документ SGML</comment>
+ <comment xml:lang="vi">Tài liệu SGML</comment>
+ <comment xml:lang="zh_CN">SGML 文档</comment>
+ <comment xml:lang="zh_TW">SGML 文件</comment>
+ <acronym>SGML</acronym>
+ <expanded-acronym>Standard Generalized Markup Language</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.sgml"/>
+ <glob pattern="*.sgm"/>
+ </mime-type>
+ <mime-type type="text/spreadsheet">
+ <comment>spreadsheet interchange document</comment>
+ <comment xml:lang="ar">مستند تبادل الجدول</comment>
+ <comment xml:lang="ast">documentu d'intercambéu de fueyes de cálculu</comment>
+ <comment xml:lang="be@latin">dakument dla abmienu raźlikovymi arkušami</comment>
+ <comment xml:lang="bg">Документ за обмяна между програми за електронни таблици</comment>
+ <comment xml:lang="ca">document d'intercanvi de full de càlcul</comment>
+ <comment xml:lang="cs">sešitový výměnný dokument</comment>
+ <comment xml:lang="da">regnearksudvekslingsdokument</comment>
+ <comment xml:lang="de">Tabellenkalkulations-Austauschdokument</comment>
+ <comment xml:lang="el">Έγγραφο ανταλλαγής λογιστικού φύλλου</comment>
+ <comment xml:lang="en_GB">spreadsheet interchange document</comment>
+ <comment xml:lang="es">documento de intercambio de hojas de cálculo</comment>
+ <comment xml:lang="eu">kalkulu-orriak trukatzeko dokumentua</comment>
+ <comment xml:lang="fi">taulukkovälitysasiakirja</comment>
+ <comment xml:lang="fo">rokniarks umbýtisskjal</comment>
+ <comment xml:lang="fr">document d'échange de feuilles de calcul</comment>
+ <comment xml:lang="ga">cáipéis idirmhalartaithe scarbhileog</comment>
+ <comment xml:lang="gl">documento de intercambio de follas de cálculo</comment>
+ <comment xml:lang="he">מסמך גליון נתונים מתחלף</comment>
+ <comment xml:lang="hr">Dokument razmjene proračunske tablice</comment>
+ <comment xml:lang="hu">spreadsheet-cserélhetődokumentum</comment>
+ <comment xml:lang="ia">Documento de intercambio de folio de calculo</comment>
+ <comment xml:lang="id">dokumen lembar sebar saling tukar</comment>
+ <comment xml:lang="it">Documento di scambio per foglio di calcolo</comment>
+ <comment xml:lang="ja">スプレッドシート交換ドキュメント</comment>
+ <comment xml:lang="kk">spreadsheet interchange құжаты</comment>
+ <comment xml:lang="ko">스프레드시트 교환 문서</comment>
+ <comment xml:lang="lt">skaičialenčių apsikeitimo dokumentas</comment>
+ <comment xml:lang="lv">izklājlapu apmaiņas dokuments</comment>
+ <comment xml:lang="nb">dokument for regnearkutveksling</comment>
+ <comment xml:lang="nl">rekenblad-uitwisselingsdocument</comment>
+ <comment xml:lang="nn">Utvekslingsdokument for rekneark</comment>
+ <comment xml:lang="oc">document d'escambi de fuèlhs de calcul</comment>
+ <comment xml:lang="pl">Dokument wymiany arkuszy kalkulacyjnych</comment>
+ <comment xml:lang="pt">documento de troca interna de folhas de cálculo</comment>
+ <comment xml:lang="pt_BR">Documento de intercâmbio de planilhas</comment>
+ <comment xml:lang="ro">document schimb filă de calcul</comment>
+ <comment xml:lang="ru">Документ Spreadsheet Interchange</comment>
+ <comment xml:lang="sk">Zošitový prenosový dokument</comment>
+ <comment xml:lang="sl">dokument izmenjeve preglednic</comment>
+ <comment xml:lang="sq">Dokument shkëmbimi për fletë llogaritje</comment>
+ <comment xml:lang="sr">документ размене табеле</comment>
+ <comment xml:lang="sv">spreadsheet interchange-dokument</comment>
+ <comment xml:lang="tr">hesap tablosu değişim belgesi</comment>
+ <comment xml:lang="uk">документ обміну ел. таблицями</comment>
+ <comment xml:lang="vi">tài liệu hoán đổi bảng tính</comment>
+ <comment xml:lang="zh_CN">电子表格交换文档</comment>
+ <comment xml:lang="zh_TW">試算表交換文件</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="ID;" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.sylk"/>
+ <glob pattern="*.slk"/>
+ </mime-type>
+ <mime-type type="text/tab-separated-values">
+ <comment>TSV document</comment>
+ <comment xml:lang="ar">مستند TSV</comment>
+ <comment xml:lang="ast">Documentu TSV</comment>
+ <comment xml:lang="be@latin">Dakument TSV</comment>
+ <comment xml:lang="bg">Документ — TSV</comment>
+ <comment xml:lang="ca">document TSV</comment>
+ <comment xml:lang="cs">dokument TSV</comment>
+ <comment xml:lang="da">TSV-dokument</comment>
+ <comment xml:lang="de">TSV-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο TSV</comment>
+ <comment xml:lang="en_GB">TSV document</comment>
+ <comment xml:lang="es">documento TSV</comment>
+ <comment xml:lang="eu">TSV dokumentua</comment>
+ <comment xml:lang="fi">TSV-asiakirja</comment>
+ <comment xml:lang="fo">TSV skjal</comment>
+ <comment xml:lang="fr">document TSV</comment>
+ <comment xml:lang="ga">cáipéis TSV</comment>
+ <comment xml:lang="gl">documento TSV</comment>
+ <comment xml:lang="he">מסמך TSV</comment>
+ <comment xml:lang="hr">TSV dokument</comment>
+ <comment xml:lang="hu">TSV dokumentum</comment>
+ <comment xml:lang="ia">Documento TSV</comment>
+ <comment xml:lang="id">Dokumen TSV</comment>
+ <comment xml:lang="it">Documento TSV</comment>
+ <comment xml:lang="ja">TSV ドキュメント</comment>
+ <comment xml:lang="kk">TSV құжаты</comment>
+ <comment xml:lang="ko">TSV 문서</comment>
+ <comment xml:lang="lt">TSV dokumentas</comment>
+ <comment xml:lang="lv">TSV dokuments</comment>
+ <comment xml:lang="nb">TSV-dokument</comment>
+ <comment xml:lang="nl">TSV-document</comment>
+ <comment xml:lang="nn">TSV-dokument</comment>
+ <comment xml:lang="oc">document TSV</comment>
+ <comment xml:lang="pl">Dokument TSV</comment>
+ <comment xml:lang="pt">documento TSV</comment>
+ <comment xml:lang="pt_BR">Documento TSV</comment>
+ <comment xml:lang="ro">Document TSV</comment>
+ <comment xml:lang="ru">Документ TSV</comment>
+ <comment xml:lang="sk">Dokument TSV</comment>
+ <comment xml:lang="sl">Dokument TSV</comment>
+ <comment xml:lang="sq">Dokument TSV</comment>
+ <comment xml:lang="sr">ТСВ документ</comment>
+ <comment xml:lang="sv">TSV-dokument</comment>
+ <comment xml:lang="tr">TSV belgesi</comment>
+ <comment xml:lang="uk">документ TSV</comment>
+ <comment xml:lang="vi">Tài liệu TSV</comment>
+ <comment xml:lang="zh_CN">TSV 文档</comment>
+ <comment xml:lang="zh_TW">TSV 文件</comment>
+ <acronym>TSV</acronym>
+ <expanded-acronym>Tab Separated Values</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.tsv"/>
+ </mime-type>
+ <mime-type type="text/vnd.graphviz">
+ <comment>Graphviz DOT graph</comment>
+ <comment xml:lang="ar">مبيان Graphviz DOT</comment>
+ <comment xml:lang="bg">Граф — Graphviz DOT</comment>
+ <comment xml:lang="ca">gràfic Graphviz DOT</comment>
+ <comment xml:lang="cs">graf Graphviz DOT</comment>
+ <comment xml:lang="da">Graphviz DOT-graf</comment>
+ <comment xml:lang="de">Graphviz-DOT-Graph</comment>
+ <comment xml:lang="el">Γράφημα Graphviz DOT</comment>
+ <comment xml:lang="en_GB">Graphviz DOT graph</comment>
+ <comment xml:lang="es">gráfico de Graphviz DOT</comment>
+ <comment xml:lang="eu">Graphviz DOT grafikoa</comment>
+ <comment xml:lang="fi">Graphviz DOT -graafi</comment>
+ <comment xml:lang="fo">Graphviz DOT ritmynd</comment>
+ <comment xml:lang="fr">graphe Graphviz DOT</comment>
+ <comment xml:lang="ga">graf DOT Graphviz</comment>
+ <comment xml:lang="gl">gráfica DOT de Graphviz</comment>
+ <comment xml:lang="he">תרשים של Graphviz DOT</comment>
+ <comment xml:lang="hr">Graphviz DOT grafikon</comment>
+ <comment xml:lang="hu">Graphviz DOT-grafikon</comment>
+ <comment xml:lang="ia">Graphico DOT de Graphviz</comment>
+ <comment xml:lang="id">Grafik Graphviz DOT</comment>
+ <comment xml:lang="it">Grafico Graphviz DOT</comment>
+ <comment xml:lang="ja">Graphviz DOT グラフ</comment>
+ <comment xml:lang="kk">Graphviz DOT сызбасы</comment>
+ <comment xml:lang="ko">Graphviz DOT 그래프</comment>
+ <comment xml:lang="lt">Graphviz DOT diagrama</comment>
+ <comment xml:lang="lv">Graphviz DOT grafiks</comment>
+ <comment xml:lang="nl">Graphviz wetenschappelijke grafiek</comment>
+ <comment xml:lang="oc">graf Graphviz DOT</comment>
+ <comment xml:lang="pl">Wykres DOT Graphviz</comment>
+ <comment xml:lang="pt">gráfico Graphviz DOT</comment>
+ <comment xml:lang="pt_BR">Gráfico do Graphviz DOT</comment>
+ <comment xml:lang="ro">Grafic Graphviz DOT</comment>
+ <comment xml:lang="ru">Диаграмма Graphviz DOT</comment>
+ <comment xml:lang="sk">Graf Graphviz DOT</comment>
+ <comment xml:lang="sl">Datoteka grafikona Graphviz DOT</comment>
+ <comment xml:lang="sr">график Графвиз ДОТ-а</comment>
+ <comment xml:lang="sv">Graphviz DOT-graf</comment>
+ <comment xml:lang="tr">Graphviz DOT grafiği</comment>
+ <comment xml:lang="uk">граф DOT Graphviz</comment>
+ <comment xml:lang="vi">Biểu đồ DOT Graphviz</comment>
+ <comment xml:lang="zh_CN">Graphviz DOT 图形</comment>
+ <comment xml:lang="zh_TW">Graphviz DOT 圖</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="digraph " type="string" offset="0"/>
+ <match value="strict digraph " type="string" offset="0"/>
+ <match value="graph " type="string" offset="0"/>
+ <match value="strict graph " type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.gv"/>
+ <glob pattern="*.dot"/>
+ </mime-type>
+ <mime-type type="text/vnd.sun.j2me.app-descriptor">
+ <comment>JAD document</comment>
+ <comment xml:lang="ar">مستند JAD</comment>
+ <comment xml:lang="ast">Documentu JAD</comment>
+ <comment xml:lang="be@latin">Dakument JAD</comment>
+ <comment xml:lang="bg">Документ — JAD</comment>
+ <comment xml:lang="ca">document JAD</comment>
+ <comment xml:lang="cs">dokument JAD</comment>
+ <comment xml:lang="da">JAD-dokument</comment>
+ <comment xml:lang="de">JAD-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο JAD</comment>
+ <comment xml:lang="en_GB">JAD document</comment>
+ <comment xml:lang="eo">JAD-dokumento</comment>
+ <comment xml:lang="es">documento JAD</comment>
+ <comment xml:lang="eu">JAD dokumentua</comment>
+ <comment xml:lang="fi">JAD-asiakirja</comment>
+ <comment xml:lang="fo">JAD skjal</comment>
+ <comment xml:lang="fr">document JAD</comment>
+ <comment xml:lang="ga">cáipéis JAD</comment>
+ <comment xml:lang="gl">documento JAD</comment>
+ <comment xml:lang="he">מסמך JAD</comment>
+ <comment xml:lang="hr">JAD dokument</comment>
+ <comment xml:lang="hu">JAD dokumentum</comment>
+ <comment xml:lang="ia">Documento JAD</comment>
+ <comment xml:lang="id">Dokumen JAD</comment>
+ <comment xml:lang="it">Documento JAD</comment>
+ <comment xml:lang="ja">JAD ドキュメント</comment>
+ <comment xml:lang="kk">JAD құжаты</comment>
+ <comment xml:lang="ko">JAD 문서</comment>
+ <comment xml:lang="lt">JAD dokumentas</comment>
+ <comment xml:lang="lv">JAD dokuments</comment>
+ <comment xml:lang="nb">JAD-dokument</comment>
+ <comment xml:lang="nl">JAD-document</comment>
+ <comment xml:lang="nn">JAD-dokument</comment>
+ <comment xml:lang="oc">document JAD</comment>
+ <comment xml:lang="pl">Dokument JAD</comment>
+ <comment xml:lang="pt">documento JAD</comment>
+ <comment xml:lang="pt_BR">Documento JAD</comment>
+ <comment xml:lang="ro">Document JAD</comment>
+ <comment xml:lang="ru">Документ JAD</comment>
+ <comment xml:lang="sk">Dokument JAD</comment>
+ <comment xml:lang="sl">Dokument JAD</comment>
+ <comment xml:lang="sq">Dokument JAD</comment>
+ <comment xml:lang="sr">ЈАД документ</comment>
+ <comment xml:lang="sv">JAD-dokument</comment>
+ <comment xml:lang="tr">JAD belgesi</comment>
+ <comment xml:lang="uk">документ JAD</comment>
+ <comment xml:lang="vi">Tài liệu JAD</comment>
+ <comment xml:lang="zh_CN">JAD 文档</comment>
+ <comment xml:lang="zh_TW">JAD 文件</comment>
+ <acronym>JAD</acronym>
+ <expanded-acronym>Java Application Descriptor</expanded-acronym>
+ <magic priority="50">
+ <match value="MIDlet-" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.jad"/>
+ </mime-type>
+ <mime-type type="text/vnd.wap.wml">
+ <comment>WML document</comment>
+ <comment xml:lang="ar">مستند WML</comment>
+ <comment xml:lang="ast">Documentu WML</comment>
+ <comment xml:lang="az">WML sənədi</comment>
+ <comment xml:lang="be@latin">Dakument WML</comment>
+ <comment xml:lang="bg">Документ — WML</comment>
+ <comment xml:lang="ca">document WML</comment>
+ <comment xml:lang="cs">dokument WML</comment>
+ <comment xml:lang="cy">Dogfen WML</comment>
+ <comment xml:lang="da">WML-dokument</comment>
+ <comment xml:lang="de">WML-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο WML</comment>
+ <comment xml:lang="en_GB">WML document</comment>
+ <comment xml:lang="eo">WML-dokumento</comment>
+ <comment xml:lang="es">documento WML</comment>
+ <comment xml:lang="eu">WML dokumentua</comment>
+ <comment xml:lang="fi">WML-asiakirja</comment>
+ <comment xml:lang="fo">WML skjal</comment>
+ <comment xml:lang="fr">document WML</comment>
+ <comment xml:lang="ga">cáipéis WML</comment>
+ <comment xml:lang="gl">documento WML</comment>
+ <comment xml:lang="he">מסמך WML</comment>
+ <comment xml:lang="hr">WML dokument</comment>
+ <comment xml:lang="hu">WML-dokumentum</comment>
+ <comment xml:lang="ia">Documento WML</comment>
+ <comment xml:lang="id">Dokumen WML</comment>
+ <comment xml:lang="it">Documento WML</comment>
+ <comment xml:lang="ja">WML ドキュメント</comment>
+ <comment xml:lang="kk">WML құжаты</comment>
+ <comment xml:lang="ko">WML 문서</comment>
+ <comment xml:lang="lt">WML dokumentas</comment>
+ <comment xml:lang="lv">WML dokuments</comment>
+ <comment xml:lang="ms">Dokumen XML</comment>
+ <comment xml:lang="nb">WML-dokument</comment>
+ <comment xml:lang="nl">WML-document</comment>
+ <comment xml:lang="nn">WML-dokument</comment>
+ <comment xml:lang="oc">document WML</comment>
+ <comment xml:lang="pl">Dokument WML</comment>
+ <comment xml:lang="pt">documento WML</comment>
+ <comment xml:lang="pt_BR">Documento WML</comment>
+ <comment xml:lang="ro">Document WML</comment>
+ <comment xml:lang="ru">Документ WML</comment>
+ <comment xml:lang="sk">Dokument WML</comment>
+ <comment xml:lang="sl">Dokument WML</comment>
+ <comment xml:lang="sq">Dokument WML</comment>
+ <comment xml:lang="sr">ВМЛ документ</comment>
+ <comment xml:lang="sv">WML-dokument</comment>
+ <comment xml:lang="tr">WML belgesi</comment>
+ <comment xml:lang="uk">документ WML</comment>
+ <comment xml:lang="vi">Tài liệu WML</comment>
+ <comment xml:lang="zh_CN">WML 文档</comment>
+ <comment xml:lang="zh_TW">WML 文件</comment>
+ <acronym>WML</acronym>
+ <expanded-acronym>Wireless Markup Language</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <glob pattern="*.wml"/>
+ </mime-type>
+ <mime-type type="text/vnd.wap.wmlscript">
+ <comment>WMLScript program</comment>
+ <comment xml:lang="ar">برنامج WMLScript</comment>
+ <comment xml:lang="be@latin">Prahrama WMLScript</comment>
+ <comment xml:lang="bg">Програма — WMLScript</comment>
+ <comment xml:lang="ca">programa WMLScript</comment>
+ <comment xml:lang="cs">program WMLScript</comment>
+ <comment xml:lang="da">WMLScript-program</comment>
+ <comment xml:lang="de">WMLScript-Programm</comment>
+ <comment xml:lang="el">Πρόγραμμα WMLScript</comment>
+ <comment xml:lang="en_GB">WMLScript program</comment>
+ <comment xml:lang="es">programa en WMLScript</comment>
+ <comment xml:lang="eu">WMLScript programa</comment>
+ <comment xml:lang="fi">WMLScript-ohjelma</comment>
+ <comment xml:lang="fo">WMLScript forrit</comment>
+ <comment xml:lang="fr">programme WMLScript</comment>
+ <comment xml:lang="ga">ríomhchlár WMLScript</comment>
+ <comment xml:lang="gl">programa en WMLScript</comment>
+ <comment xml:lang="he">תכנית של WMLScript</comment>
+ <comment xml:lang="hr">WMLScript program</comment>
+ <comment xml:lang="hu">WMLScript program</comment>
+ <comment xml:lang="ia">Programma WMLScript</comment>
+ <comment xml:lang="id">Program WMLScript</comment>
+ <comment xml:lang="it">Programma WMLScript</comment>
+ <comment xml:lang="ja">WMLScript プログラム</comment>
+ <comment xml:lang="kk">WMLScript бағдарламасы</comment>
+ <comment xml:lang="ko">WMLScript 프로그램</comment>
+ <comment xml:lang="lt">WMLScript programa</comment>
+ <comment xml:lang="lv">WMLScript programma</comment>
+ <comment xml:lang="nb">WMLScript-program</comment>
+ <comment xml:lang="nl">WMLScript-programma</comment>
+ <comment xml:lang="nn">WMLScript-program</comment>
+ <comment xml:lang="oc">programa WMLEscript</comment>
+ <comment xml:lang="pl">Pogram WMLScript</comment>
+ <comment xml:lang="pt">programa WMLScript</comment>
+ <comment xml:lang="pt_BR">Programa WMLScript</comment>
+ <comment xml:lang="ro">Program WMLScript</comment>
+ <comment xml:lang="ru">Программа WMLScript</comment>
+ <comment xml:lang="sk">Program WMLScript</comment>
+ <comment xml:lang="sl">Programska datoteka WMLScript</comment>
+ <comment xml:lang="sq">Program WMLScript</comment>
+ <comment xml:lang="sr">програм ВМЛ скрипте</comment>
+ <comment xml:lang="sv">WMLScript-program</comment>
+ <comment xml:lang="tr">WMLScript programı</comment>
+ <comment xml:lang="uk">програма мовою WMLScript</comment>
+ <comment xml:lang="vi">Chương trình WMLScript</comment>
+ <comment xml:lang="zh_CN">WMLScript 程序</comment>
+ <comment xml:lang="zh_TW">WMLScript 程式</comment>
+ <glob pattern="*.wmls"/>
+ </mime-type>
+ <mime-type type="application/x-ace">
+ <comment>ACE archive</comment>
+ <comment xml:lang="ar">أرشيف ACE</comment>
+ <comment xml:lang="be@latin">Archiŭ ACE</comment>
+ <comment xml:lang="bg">Архив — ACE</comment>
+ <comment xml:lang="ca">arxiu ACE</comment>
+ <comment xml:lang="cs">archiv ACE</comment>
+ <comment xml:lang="da">ACE-arkiv</comment>
+ <comment xml:lang="de">ACE-Archiv</comment>
+ <comment xml:lang="el">Συμπιεσμένο αρχείο ACE</comment>
+ <comment xml:lang="en_GB">ACE archive</comment>
+ <comment xml:lang="eo">ACE-arkivo</comment>
+ <comment xml:lang="es">archivador ACE</comment>
+ <comment xml:lang="eu">ACE artxiboa</comment>
+ <comment xml:lang="fi">ACE-arkisto</comment>
+ <comment xml:lang="fo">ACE skjalasavn</comment>
+ <comment xml:lang="fr">archive ACE</comment>
+ <comment xml:lang="ga">cartlann ACE</comment>
+ <comment xml:lang="gl">arquivo ACE</comment>
+ <comment xml:lang="he">ארכיון ACE</comment>
+ <comment xml:lang="hr">ACE arhiva</comment>
+ <comment xml:lang="hu">ACE archívum</comment>
+ <comment xml:lang="ia">Archivo ACE</comment>
+ <comment xml:lang="id">Arsip ACE</comment>
+ <comment xml:lang="it">Archivio ACE</comment>
+ <comment xml:lang="ja">ACE アーカイブ</comment>
+ <comment xml:lang="ka">ACE არქივი</comment>
+ <comment xml:lang="kk">ACE архиві</comment>
+ <comment xml:lang="ko">ACE 압축 파일</comment>
+ <comment xml:lang="lt">ACE archyvas</comment>
+ <comment xml:lang="lv">ACE arhīvs</comment>
+ <comment xml:lang="nb">ACE-arkiv</comment>
+ <comment xml:lang="nl">ACE-archief</comment>
+ <comment xml:lang="nn">ACE-arkiv</comment>
+ <comment xml:lang="oc">archiu ACE</comment>
+ <comment xml:lang="pl">Archiwum ACE</comment>
+ <comment xml:lang="pt">arquivo ACE</comment>
+ <comment xml:lang="pt_BR">Pacote ACE</comment>
+ <comment xml:lang="ro">Arhivă ACE</comment>
+ <comment xml:lang="ru">Архив ACE</comment>
+ <comment xml:lang="sk">Archív ACE</comment>
+ <comment xml:lang="sl">Datoteka arhiva ACE</comment>
+ <comment xml:lang="sq">Arkiv ACE</comment>
+ <comment xml:lang="sr">АЦЕ архива</comment>
+ <comment xml:lang="sv">ACE-arkiv</comment>
+ <comment xml:lang="tr">ACE arşivi</comment>
+ <comment xml:lang="uk">архів ACE</comment>
+ <comment xml:lang="vi">Kho nén ACE</comment>
+ <comment xml:lang="zh_CN">ACE 归档文件</comment>
+ <comment xml:lang="zh_TW">ACE 封存檔</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="60">
+ <match value="**ACE**" type="string" offset="7"/>
+ </magic>
+ <glob pattern="*.ace"/>
+ </mime-type>
+ <mime-type type="text/x-adasrc">
+ <comment>Ada source code</comment>
+ <comment xml:lang="ar">شفرة مصدر Ada</comment>
+ <comment xml:lang="be@latin">Kryničny kod Ada</comment>
+ <comment xml:lang="bg">Изходен код — Ada</comment>
+ <comment xml:lang="ca">codi font en Ada</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Ada</comment>
+ <comment xml:lang="da">Ada-kildekode</comment>
+ <comment xml:lang="de">Ada-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Ada</comment>
+ <comment xml:lang="en_GB">Ada source code</comment>
+ <comment xml:lang="eo">Ada-fontkodo</comment>
+ <comment xml:lang="es">código fuente en Ada</comment>
+ <comment xml:lang="eu">Ada iturburu-kodea</comment>
+ <comment xml:lang="fi">Ada-lähdekoodi</comment>
+ <comment xml:lang="fo">Ada keldukota</comment>
+ <comment xml:lang="fr">code source Ada</comment>
+ <comment xml:lang="ga">cód foinseach Ada</comment>
+ <comment xml:lang="gl">código fonte en Ada</comment>
+ <comment xml:lang="he">קוד מקור Ada</comment>
+ <comment xml:lang="hr">Ada izvorni kôd</comment>
+ <comment xml:lang="hu">Ada-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Ada</comment>
+ <comment xml:lang="id">Kode sumber Ada</comment>
+ <comment xml:lang="it">Codice sorgente Ada</comment>
+ <comment xml:lang="ja">Ada ソースコード</comment>
+ <comment xml:lang="ka">Ada-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">Ada бастапқы коды</comment>
+ <comment xml:lang="ko">Ada 소스 코드</comment>
+ <comment xml:lang="lt">Ada pradinis kodas</comment>
+ <comment xml:lang="lv">Ada pirmkods</comment>
+ <comment xml:lang="ms">Kod sumber Ada</comment>
+ <comment xml:lang="nb">Ada-kildekode</comment>
+ <comment xml:lang="nl">Ada-broncode</comment>
+ <comment xml:lang="nn">Ada-kjeldekode</comment>
+ <comment xml:lang="oc">còde font Ada</comment>
+ <comment xml:lang="pl">Kod źródłowy Ada</comment>
+ <comment xml:lang="pt">código origem Ada</comment>
+ <comment xml:lang="pt_BR">Código-fonte Ada</comment>
+ <comment xml:lang="ro">Cod sursă Ada</comment>
+ <comment xml:lang="ru">Исходный код Ada</comment>
+ <comment xml:lang="sk">Zdrojový kód jazyka Ada</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Ada</comment>
+ <comment xml:lang="sq">Kod burues Ada</comment>
+ <comment xml:lang="sr">Ада изворни ко̂д</comment>
+ <comment xml:lang="sv">Ada-källkod</comment>
+ <comment xml:lang="tr">Ada kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Ada</comment>
+ <comment xml:lang="vi">Mã nguồn Ada</comment>
+ <comment xml:lang="zh_CN">Ada 源代码</comment>
+ <comment xml:lang="zh_TW">Ada 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.adb"/>
+ <glob pattern="*.ads"/>
+ </mime-type>
+ <mime-type type="text/x-authors">
+ <comment>author list</comment>
+ <comment xml:lang="ar">لائحة المؤلف</comment>
+ <comment xml:lang="be@latin">śpis aŭtaraŭ</comment>
+ <comment xml:lang="bg">Списък на авторите</comment>
+ <comment xml:lang="ca">llista d'autors</comment>
+ <comment xml:lang="cs">seznam autorů</comment>
+ <comment xml:lang="da">forfatterliste</comment>
+ <comment xml:lang="de">Autorenliste</comment>
+ <comment xml:lang="el">Κατάλογος συγγραφέων</comment>
+ <comment xml:lang="en_GB">author list</comment>
+ <comment xml:lang="eo">listo de aŭtoroj</comment>
+ <comment xml:lang="es">lista de autores</comment>
+ <comment xml:lang="eu">egile-zerrenda</comment>
+ <comment xml:lang="fi">tekijäluettelo</comment>
+ <comment xml:lang="fo">høvundalisti</comment>
+ <comment xml:lang="fr">liste d'auteurs</comment>
+ <comment xml:lang="ga">liosta údar</comment>
+ <comment xml:lang="gl">lista de autores</comment>
+ <comment xml:lang="he">רשימת יוצרים</comment>
+ <comment xml:lang="hr">Popis autora</comment>
+ <comment xml:lang="hu">szerzőlista</comment>
+ <comment xml:lang="ia">Lista de autores</comment>
+ <comment xml:lang="id">senarai penulis</comment>
+ <comment xml:lang="it">Elenco autori</comment>
+ <comment xml:lang="ja">著者リスト</comment>
+ <comment xml:lang="kk">авторлар тізімі</comment>
+ <comment xml:lang="ko">저자 목록</comment>
+ <comment xml:lang="lt">autorių sąrašas</comment>
+ <comment xml:lang="lv">autoru saraksts</comment>
+ <comment xml:lang="ms">Senarai penulis</comment>
+ <comment xml:lang="nb">forfatterliste</comment>
+ <comment xml:lang="nl">auteurslijst</comment>
+ <comment xml:lang="nn">forfattarliste</comment>
+ <comment xml:lang="oc">lista d'autors</comment>
+ <comment xml:lang="pl">Lista autorów</comment>
+ <comment xml:lang="pt">lista de autores</comment>
+ <comment xml:lang="pt_BR">Lista de autores</comment>
+ <comment xml:lang="ro">listă autori</comment>
+ <comment xml:lang="ru">Список авторов</comment>
+ <comment xml:lang="sk">Zoznam autorov</comment>
+ <comment xml:lang="sl">seznam avtorjev</comment>
+ <comment xml:lang="sq">Lista e autorëve</comment>
+ <comment xml:lang="sr">списак аутора</comment>
+ <comment xml:lang="sv">författarlista</comment>
+ <comment xml:lang="tr">yazar listesi</comment>
+ <comment xml:lang="uk">перелік авторів</comment>
+ <comment xml:lang="vi">danh sách tác giả</comment>
+ <comment xml:lang="zh_CN">作者列表</comment>
+ <comment xml:lang="zh_TW">作者清單</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="AUTHORS"/>
+ </mime-type>
+ <mime-type type="text/x-bibtex">
+ <comment>BibTeX document</comment>
+ <comment xml:lang="ar">مستند BibTeX</comment>
+ <comment xml:lang="ast">Documentu de BibTeX</comment>
+ <comment xml:lang="be@latin">Dakument BibTeX</comment>
+ <comment xml:lang="bg">Документ — BibTeX</comment>
+ <comment xml:lang="ca">document BibTeX</comment>
+ <comment xml:lang="cs">dokument BibTeX</comment>
+ <comment xml:lang="da">BibTeX-dokument</comment>
+ <comment xml:lang="de">BibTeX-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο BibTeX</comment>
+ <comment xml:lang="en_GB">BibTeX document</comment>
+ <comment xml:lang="eo">BibTeX-dokumento</comment>
+ <comment xml:lang="es">documento BibTeX</comment>
+ <comment xml:lang="eu">BibTeX dokumentua</comment>
+ <comment xml:lang="fi">BibTeX-asiakirja</comment>
+ <comment xml:lang="fo">BibTeX skjal</comment>
+ <comment xml:lang="fr">document BibTeX</comment>
+ <comment xml:lang="ga">cáipéis BibTeX</comment>
+ <comment xml:lang="gl">documento BibTex</comment>
+ <comment xml:lang="he">מסמך BibTeX</comment>
+ <comment xml:lang="hr">BibTeX dokument</comment>
+ <comment xml:lang="hu">BibTeX dokumentum</comment>
+ <comment xml:lang="ia">Documento BibTeX</comment>
+ <comment xml:lang="id">Dokumen BibTeX</comment>
+ <comment xml:lang="it">Documento BibTeX</comment>
+ <comment xml:lang="ja">BibTeX ドキュメント</comment>
+ <comment xml:lang="ka">BibTeX-ის დოკუმენტი</comment>
+ <comment xml:lang="kk">BibTeX құжаты</comment>
+ <comment xml:lang="ko">BibTeX 문서</comment>
+ <comment xml:lang="lt">BibTeX dokumentas</comment>
+ <comment xml:lang="lv">BibTeX dokuments</comment>
+ <comment xml:lang="nb">BibTeX-dokument</comment>
+ <comment xml:lang="nl">BibTeX-document</comment>
+ <comment xml:lang="nn">BibTeX-dokument</comment>
+ <comment xml:lang="oc">document BibTeX</comment>
+ <comment xml:lang="pl">Dokument BibTeX</comment>
+ <comment xml:lang="pt">documento BibTeX</comment>
+ <comment xml:lang="pt_BR">Documento BibTeX</comment>
+ <comment xml:lang="ro">Document BibTeX</comment>
+ <comment xml:lang="ru">Документ BibTeX</comment>
+ <comment xml:lang="sk">Dokument BibTeX</comment>
+ <comment xml:lang="sl">Dokument BibTeX</comment>
+ <comment xml:lang="sq">Dokument BibTeX</comment>
+ <comment xml:lang="sr">Биб ТеКс документ</comment>
+ <comment xml:lang="sv">BibTeX-dokument</comment>
+ <comment xml:lang="tr">BibTeX belgesi</comment>
+ <comment xml:lang="uk">документ BibTeX</comment>
+ <comment xml:lang="vi">Tài liệu BibTeX</comment>
+ <comment xml:lang="zh_CN">BibTeX 文档</comment>
+ <comment xml:lang="zh_TW">BibTeX 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <magic>
+ <match value="% This file was created with JabRef" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.bib"/>
+ </mime-type>
+ <mime-type type="text/x-c++hdr">
+ <comment>C++ header</comment>
+ <comment xml:lang="ar">ترويسة سي++</comment>
+ <comment xml:lang="be@latin">Zahałoŭny fajł C++</comment>
+ <comment xml:lang="bg">Заглавен файл — C++</comment>
+ <comment xml:lang="ca">capçalera en C++</comment>
+ <comment xml:lang="cs">hlavičkový soubor C++</comment>
+ <comment xml:lang="da">C++-posthoved</comment>
+ <comment xml:lang="de">C++-Header</comment>
+ <comment xml:lang="el">Κεφαλίδα C++</comment>
+ <comment xml:lang="en_GB">C++ header</comment>
+ <comment xml:lang="es">cabecera de código fuente en C++</comment>
+ <comment xml:lang="eu">C++ goiburua</comment>
+ <comment xml:lang="fi">C++-otsake</comment>
+ <comment xml:lang="fo">C++ tekshøvd</comment>
+ <comment xml:lang="fr">en-tête C++</comment>
+ <comment xml:lang="ga">ceanntásc C++</comment>
+ <comment xml:lang="gl">cabeceira de código fonte en C++</comment>
+ <comment xml:lang="he">כותר C++‎</comment>
+ <comment xml:lang="hr">C++ zaglavlje</comment>
+ <comment xml:lang="hu">C++ fejléc</comment>
+ <comment xml:lang="ia">Capite C++</comment>
+ <comment xml:lang="id">Header C++</comment>
+ <comment xml:lang="it">Header C++</comment>
+ <comment xml:lang="ja">C++ ヘッダー</comment>
+ <comment xml:lang="ka">C++-ის თავსართი</comment>
+ <comment xml:lang="kk">C++ тақырыптама файлы</comment>
+ <comment xml:lang="ko">C++ 헤더</comment>
+ <comment xml:lang="lt">C++ antraštė</comment>
+ <comment xml:lang="lv">C++ galvene</comment>
+ <comment xml:lang="nb">C++-kildekodeheader</comment>
+ <comment xml:lang="nl">C++-header</comment>
+ <comment xml:lang="nn">C++-kjeldekode-hovud</comment>
+ <comment xml:lang="oc">entèsta C++</comment>
+ <comment xml:lang="pl">Plik nagłówkowy C++</comment>
+ <comment xml:lang="pt">cabeçalho C++</comment>
+ <comment xml:lang="pt_BR">Cabeçalho C++</comment>
+ <comment xml:lang="ro">Antet C++</comment>
+ <comment xml:lang="ru">Заголовочный файл C++</comment>
+ <comment xml:lang="sk">Hlavičky jazyka C++</comment>
+ <comment xml:lang="sl">Datoteka glave C++</comment>
+ <comment xml:lang="sq">Header C++</comment>
+ <comment xml:lang="sr">Ц++ заглавље</comment>
+ <comment xml:lang="sv">C++-huvud</comment>
+ <comment xml:lang="tr">C++ başlığı</comment>
+ <comment xml:lang="uk">файл заголовків мовою C++</comment>
+ <comment xml:lang="vi">Phần đầu mã nguồn C++</comment>
+ <comment xml:lang="zh_CN">C++ 头文件</comment>
+ <comment xml:lang="zh_TW">C++ 標頭檔</comment>
+ <sub-class-of type="text/x-chdr"/>
+ <glob pattern="*.hh"/>
+ <glob pattern="*.hp"/>
+ <glob pattern="*.hpp"/>
+ <glob pattern="*.h++"/>
+ <glob pattern="*.hxx"/>
+ </mime-type>
+ <mime-type type="text/x-c++src">
+ <comment>C++ source code</comment>
+ <comment xml:lang="ar">شفرة مصدر سي++</comment>
+ <comment xml:lang="be@latin">Kryničny kod C++</comment>
+ <comment xml:lang="bg">Изходен код — C++</comment>
+ <comment xml:lang="ca">codi font en C++</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce C++</comment>
+ <comment xml:lang="da">C++-kildekode</comment>
+ <comment xml:lang="de">C++-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας C++</comment>
+ <comment xml:lang="en_GB">C++ source code</comment>
+ <comment xml:lang="eo">C++-fontkodo</comment>
+ <comment xml:lang="es">código fuente en C++</comment>
+ <comment xml:lang="eu">C++ iturburu-kodea</comment>
+ <comment xml:lang="fi">C++-lähdekoodi</comment>
+ <comment xml:lang="fo">C++ keldukota</comment>
+ <comment xml:lang="fr">code source C++</comment>
+ <comment xml:lang="ga">cód foinseach C++</comment>
+ <comment xml:lang="gl">código fonte de C++</comment>
+ <comment xml:lang="he">קוד מקור של C++‎</comment>
+ <comment xml:lang="hr">C++ izvorni kôd</comment>
+ <comment xml:lang="hu">C++-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte C++</comment>
+ <comment xml:lang="id">Kode sumber C++</comment>
+ <comment xml:lang="it">Codice sorgente C++</comment>
+ <comment xml:lang="ja">C++ ソースコード</comment>
+ <comment xml:lang="ka">C++-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">C++ бастапқы коды</comment>
+ <comment xml:lang="ko">C++ 소스 코드</comment>
+ <comment xml:lang="lt">C++ pradinis kodas</comment>
+ <comment xml:lang="lv">C++ pirmkods</comment>
+ <comment xml:lang="ms">Kod sumber C++</comment>
+ <comment xml:lang="nb">C++-kildekode</comment>
+ <comment xml:lang="nl">C++-broncode</comment>
+ <comment xml:lang="nn">C++-kjeldekode</comment>
+ <comment xml:lang="oc">còde font C++</comment>
+ <comment xml:lang="pl">Kod źródłowy C++</comment>
+ <comment xml:lang="pt">código origem C++</comment>
+ <comment xml:lang="pt_BR">Código-fonte C++</comment>
+ <comment xml:lang="ro">Cod sursă C++</comment>
+ <comment xml:lang="ru">Исходный код C++</comment>
+ <comment xml:lang="sk">Zdrojový kód jazyka C++</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode C++</comment>
+ <comment xml:lang="sq">Kod burues C++</comment>
+ <comment xml:lang="sr">Ц++ изворни ко̂д</comment>
+ <comment xml:lang="sv">C++-källkod</comment>
+ <comment xml:lang="tr">C++ kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою C++</comment>
+ <comment xml:lang="vi">Mã nguồn C++</comment>
+ <comment xml:lang="zh_CN">C++ 源代码</comment>
+ <comment xml:lang="zh_TW">C++ 源碼</comment>
+ <sub-class-of type="text/x-csrc"/>
+ <glob pattern="*.cpp"/>
+ <glob pattern="*.cxx"/>
+ <glob pattern="*.cc"/>
+ <glob pattern="*.C" case-sensitive="true"/>
+ <glob pattern="*.c++"/>
+ </mime-type>
+ <mime-type type="text/x-changelog">
+ <comment>ChangeLog document</comment>
+ <comment xml:lang="ar">مستند ChangeLog</comment>
+ <comment xml:lang="ast">Documentu de rexistru de cambeos</comment>
+ <comment xml:lang="be@latin">Dakument zafiksavanych źmienaŭ ChangeLog</comment>
+ <comment xml:lang="bg">Дневник за промени — ChangeLog</comment>
+ <comment xml:lang="ca">document de registre de canvis</comment>
+ <comment xml:lang="cs">dokument ChangeLog</comment>
+ <comment xml:lang="da">ChangeLot-dokument</comment>
+ <comment xml:lang="de">Änderungsprotokoll-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο ChangeLog</comment>
+ <comment xml:lang="en_GB">ChangeLog document</comment>
+ <comment xml:lang="es">documento de registro de cambios</comment>
+ <comment xml:lang="eu">ChangeLog dokumentua</comment>
+ <comment xml:lang="fi">Muutoslokiasiakirja</comment>
+ <comment xml:lang="fo">ChangeLog skjal</comment>
+ <comment xml:lang="fr">document ChangeLog</comment>
+ <comment xml:lang="ga">cáipéis ChangeLog</comment>
+ <comment xml:lang="gl">documento Changelog</comment>
+ <comment xml:lang="he">מסמך של ChangeLog</comment>
+ <comment xml:lang="hr">Dokument zapisa promjena</comment>
+ <comment xml:lang="hu">ChangeLog dokumentum</comment>
+ <comment xml:lang="ia">Lista de cambiamentos</comment>
+ <comment xml:lang="id">Dokumen ChangeLog</comment>
+ <comment xml:lang="it">Documento ChangeLog</comment>
+ <comment xml:lang="ja">ChangeLog ドキュメント</comment>
+ <comment xml:lang="ka">ChangeLog დოკუმენტი</comment>
+ <comment xml:lang="kk">ChangeLog құжаты</comment>
+ <comment xml:lang="ko">ChangeLog 문서</comment>
+ <comment xml:lang="lt">ChangeLog dokumentas</comment>
+ <comment xml:lang="lv">ChangeLog dokuments</comment>
+ <comment xml:lang="nb">ChangeLog-dokument</comment>
+ <comment xml:lang="nl">ChangeLog-document</comment>
+ <comment xml:lang="nn">ChangeLog-dokument</comment>
+ <comment xml:lang="oc">document ChangeLog</comment>
+ <comment xml:lang="pl">Dokument zmian (ChangeLog)</comment>
+ <comment xml:lang="pt">documento ChangeLog</comment>
+ <comment xml:lang="pt_BR">Documento ChangeLog</comment>
+ <comment xml:lang="ro">Document ChangeLog</comment>
+ <comment xml:lang="ru">Протокол изменений</comment>
+ <comment xml:lang="sk">Dokument ChangeLog</comment>
+ <comment xml:lang="sl">Dokument ChangeLog</comment>
+ <comment xml:lang="sq">Dokument ChangeLog</comment>
+ <comment xml:lang="sr">документ дневника измена</comment>
+ <comment xml:lang="sv">Ändringsloggsdokument</comment>
+ <comment xml:lang="tr">Değişim Günlüğü belgesi</comment>
+ <comment xml:lang="uk">документ ChangeLog</comment>
+ <comment xml:lang="vi">Tài liệu ChangeLog (ghi lưu thay đổi)</comment>
+ <comment xml:lang="zh_CN">变更日志文档</comment>
+ <comment xml:lang="zh_TW">ChangeLog 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="ChangeLog"/>
+ </mime-type>
+ <mime-type type="text/x-chdr">
+ <comment>C header</comment>
+ <comment xml:lang="ar">ترويسة C</comment>
+ <comment xml:lang="be@latin">Zahałoŭny fajł C</comment>
+ <comment xml:lang="bg">Заглавен файл — C</comment>
+ <comment xml:lang="ca">capçalera en C</comment>
+ <comment xml:lang="cs">hlavičkový soubor C</comment>
+ <comment xml:lang="da">C-posthoved</comment>
+ <comment xml:lang="de">C-Header</comment>
+ <comment xml:lang="el">Κεφαλίδα C</comment>
+ <comment xml:lang="en_GB">C header</comment>
+ <comment xml:lang="es">cabecera de código fuente en C</comment>
+ <comment xml:lang="eu">C goiburua</comment>
+ <comment xml:lang="fi">C-otsake</comment>
+ <comment xml:lang="fo">C tekshøvd</comment>
+ <comment xml:lang="fr">en-tête C</comment>
+ <comment xml:lang="ga">ceanntásc C</comment>
+ <comment xml:lang="gl">cabeceira de códifo fonte de C</comment>
+ <comment xml:lang="he">כותר C</comment>
+ <comment xml:lang="hr">C zaglavlje</comment>
+ <comment xml:lang="hu">C fejléc</comment>
+ <comment xml:lang="ia">Capite C</comment>
+ <comment xml:lang="id">Header C</comment>
+ <comment xml:lang="it">Header C</comment>
+ <comment xml:lang="ja">C ヘッダー</comment>
+ <comment xml:lang="ka">C-ის თავსართი</comment>
+ <comment xml:lang="kk">C тақырыптама файлы</comment>
+ <comment xml:lang="ko">C 헤더</comment>
+ <comment xml:lang="lt">C antraštė</comment>
+ <comment xml:lang="lv">C galvene</comment>
+ <comment xml:lang="nb">C-kildekodeheader</comment>
+ <comment xml:lang="nl">C-header</comment>
+ <comment xml:lang="nn">C-kjeldekode-hovud</comment>
+ <comment xml:lang="oc">entèsta C</comment>
+ <comment xml:lang="pl">Plik nagłówkowy C</comment>
+ <comment xml:lang="pt">cabeçalho C</comment>
+ <comment xml:lang="pt_BR">Cabeçalho C</comment>
+ <comment xml:lang="ro">Antet C</comment>
+ <comment xml:lang="ru">Заголовочный файл C</comment>
+ <comment xml:lang="sk">Hlavičky jazyka C</comment>
+ <comment xml:lang="sl">Datoteka glave C</comment>
+ <comment xml:lang="sq">Header C</comment>
+ <comment xml:lang="sr">Ц заглавље</comment>
+ <comment xml:lang="sv">C-huvud</comment>
+ <comment xml:lang="tr">C başlığı</comment>
+ <comment xml:lang="uk">файл заголовків мовою C</comment>
+ <comment xml:lang="vi">Phần đầu mã nguồn C</comment>
+ <comment xml:lang="zh_CN">C 程序头文件</comment>
+ <comment xml:lang="zh_TW">C 標頭檔</comment>
+ <sub-class-of type="text/x-csrc"/>
+ <glob pattern="*.h"/>
+ </mime-type>
+ <mime-type type="text/x-cmake">
+ <comment>CMake source code</comment>
+ <comment xml:lang="ar">شفرة مصدر CMake</comment>
+ <comment xml:lang="be@latin">Kryničny kod CMake</comment>
+ <comment xml:lang="bg">Изходен код — CMake</comment>
+ <comment xml:lang="ca">codi font en CMake</comment>
+ <comment xml:lang="cs">zdrojový kód CMake</comment>
+ <comment xml:lang="da">CMake-kildekode</comment>
+ <comment xml:lang="de">CMake-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας CMake</comment>
+ <comment xml:lang="en_GB">CMake source code</comment>
+ <comment xml:lang="eo">CMake-fontkodo</comment>
+ <comment xml:lang="es">código fuente en CMake</comment>
+ <comment xml:lang="eu">CMake iturburu-kodea</comment>
+ <comment xml:lang="fi">CMake-lähdekoodi</comment>
+ <comment xml:lang="fo">CMake keldukota</comment>
+ <comment xml:lang="fr">code source CMake</comment>
+ <comment xml:lang="ga">cód foinseach CMake</comment>
+ <comment xml:lang="gl">código fonte de CMake</comment>
+ <comment xml:lang="he">קוד מקור של CMake</comment>
+ <comment xml:lang="hr">CMake izvorni kôd</comment>
+ <comment xml:lang="hu">CMake-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte CMake</comment>
+ <comment xml:lang="id">Kode sumber CMake</comment>
+ <comment xml:lang="it">Codice sorgente CMake</comment>
+ <comment xml:lang="ja">CMake ソースコード</comment>
+ <comment xml:lang="ka">CMake-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">CMake бастапқы коды</comment>
+ <comment xml:lang="ko">CMake 소스 코드</comment>
+ <comment xml:lang="lt">CMake pirminis tekstas</comment>
+ <comment xml:lang="lv">CMake pirmkods</comment>
+ <comment xml:lang="nb">CMake-kildekode</comment>
+ <comment xml:lang="nl">CMake-broncode</comment>
+ <comment xml:lang="nn">CMake-kjeldekode</comment>
+ <comment xml:lang="oc">còde font CMake</comment>
+ <comment xml:lang="pl">Kod źródłowy CMake</comment>
+ <comment xml:lang="pt">código origem CMake</comment>
+ <comment xml:lang="pt_BR">Código-fonte CMake</comment>
+ <comment xml:lang="ro">Cod sursă CMake</comment>
+ <comment xml:lang="ru">Исходный код CMake</comment>
+ <comment xml:lang="sk">Zdrojový kód CMake</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode CMake</comment>
+ <comment xml:lang="sq">Kod burues CMake</comment>
+ <comment xml:lang="sr">Ц Мејк изворни ко̂д</comment>
+ <comment xml:lang="sv">CMake-källkod</comment>
+ <comment xml:lang="tr">CMake kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код CMake</comment>
+ <comment xml:lang="vi">Mã nguồn CMake</comment>
+ <comment xml:lang="zh_CN">CMake 源代码</comment>
+ <comment xml:lang="zh_TW">CMake 源碼</comment>
+ <glob pattern="*.cmake"/>
+ <glob pattern="CMakeLists.txt"/>
+ <sub-class-of type="text/plain"/>
+ </mime-type>
+ <mime-type type="text/csv">
+ <comment>CSV document</comment>
+ <comment xml:lang="ar">مستند CSV</comment>
+ <comment xml:lang="ast">Documentu CVS</comment>
+ <comment xml:lang="be@latin">Dakument CSV</comment>
+ <comment xml:lang="bg">Документ — CSV</comment>
+ <comment xml:lang="ca">document CSV</comment>
+ <comment xml:lang="cs">dokument CSV</comment>
+ <comment xml:lang="da">CSV-dokument</comment>
+ <comment xml:lang="de">CSV-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο CSV</comment>
+ <comment xml:lang="en_GB">CSV document</comment>
+ <comment xml:lang="eo">CSV-dokumento</comment>
+ <comment xml:lang="es">documento CSV</comment>
+ <comment xml:lang="eu">CSV dokumentua</comment>
+ <comment xml:lang="fi">CSV-asiakirja</comment>
+ <comment xml:lang="fo">CSV skjal</comment>
+ <comment xml:lang="fr">document CSV</comment>
+ <comment xml:lang="ga">cáipéis CSV</comment>
+ <comment xml:lang="gl">documento CSV</comment>
+ <comment xml:lang="he">מסמך CSV</comment>
+ <comment xml:lang="hr">CSV dokument</comment>
+ <comment xml:lang="hu">CSV dokumentum</comment>
+ <comment xml:lang="ia">Documento CSV</comment>
+ <comment xml:lang="id">Dokumen CSV</comment>
+ <comment xml:lang="it">Documento CSV</comment>
+ <comment xml:lang="ja">CSV ドキュメント</comment>
+ <comment xml:lang="ka">CSV დოკუმენტი</comment>
+ <comment xml:lang="kk">CSV құжаты</comment>
+ <comment xml:lang="ko">CSV 문서</comment>
+ <comment xml:lang="lt">CSV dokumentas</comment>
+ <comment xml:lang="lv">CSV dokuments</comment>
+ <comment xml:lang="nb">CSV-dokument</comment>
+ <comment xml:lang="nl">CSV-document</comment>
+ <comment xml:lang="nn">CSV-dokument</comment>
+ <comment xml:lang="oc">document CSV</comment>
+ <comment xml:lang="pl">Dokument CSV</comment>
+ <comment xml:lang="pt">documento CSV</comment>
+ <comment xml:lang="pt_BR">Documento CSV</comment>
+ <comment xml:lang="ro">Document CSV</comment>
+ <comment xml:lang="ru">Документ CSV</comment>
+ <comment xml:lang="sk">Dokument CSV</comment>
+ <comment xml:lang="sl">Dokument CSV</comment>
+ <comment xml:lang="sq">Dokument CSV</comment>
+ <comment xml:lang="sr">ЦСВ документ</comment>
+ <comment xml:lang="sv">CSV-dokument</comment>
+ <comment xml:lang="tr">CSV belgesi</comment>
+ <comment xml:lang="uk">документ CSV</comment>
+ <comment xml:lang="vi">Tài liệu CSV</comment>
+ <comment xml:lang="zh_CN">CSV 文档</comment>
+ <comment xml:lang="zh_TW">CSV 文件</comment>
+ <acronym>CSV</acronym>
+ <expanded-acronym>Comma Separated Values</expanded-acronym>
+ <alias type="text/x-comma-separated-values"/>
+ <alias type="text/x-csv"/>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.csv"/>
+ </mime-type>
+ <mime-type type="text/csv-schema">
+ <comment>CSV Schema document</comment>
+ <comment xml:lang="ast">Documentu d'esquema CSV</comment>
+ <comment xml:lang="ca">document Schema de CSV</comment>
+ <comment xml:lang="cs">dokument schématu CSV</comment>
+ <comment xml:lang="da">CSV Schema-dokument</comment>
+ <comment xml:lang="de">CSV-Schemadokument</comment>
+ <comment xml:lang="en_GB">CSV Schema document</comment>
+ <comment xml:lang="es">documento esquemático CSV</comment>
+ <comment xml:lang="eu">CSV Schema dokumentua</comment>
+ <comment xml:lang="fr">document schéma CSV</comment>
+ <comment xml:lang="ga">cáipéis scéimre CSV</comment>
+ <comment xml:lang="he">מסמך פריסת CSV</comment>
+ <comment xml:lang="hr">CSV Shema dokument</comment>
+ <comment xml:lang="hu">CSV sémadokumentum</comment>
+ <comment xml:lang="ia">Documento CSV Schema</comment>
+ <comment xml:lang="id">Dokumen Skema CSV</comment>
+ <comment xml:lang="it">Documento schema CSV</comment>
+ <comment xml:lang="kk">CSV сұлба құжаты</comment>
+ <comment xml:lang="ko">CSV 스키마 문서</comment>
+ <comment xml:lang="pl">Dokument schematu CSV</comment>
+ <comment xml:lang="pt">documento CSV Schema</comment>
+ <comment xml:lang="pt_BR">Documento CSV Schema</comment>
+ <comment xml:lang="ru">Документ CSV Schema</comment>
+ <comment xml:lang="sk">Dokument schémy CSV</comment>
+ <comment xml:lang="sr">документ ЦСВ шеме</comment>
+ <comment xml:lang="sv">CSV Schema-dokument</comment>
+ <comment xml:lang="tr">CSV Şeması belgesi</comment>
+ <comment xml:lang="uk">документ Schema у форматі CSV</comment>
+ <comment xml:lang="zh_CN">CSV 架构文档</comment>
+ <comment xml:lang="zh_TW">CSV Schema 文件</comment>
+ <acronym>CSV</acronym>
+ <expanded-acronym>Comma Separated Values</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.csvs"/>
+ </mime-type>
+ <mime-type type="text/x-copying">
+ <comment>license terms</comment>
+ <comment xml:lang="ar">شروط الترخيص</comment>
+ <comment xml:lang="ast">términos de llicencia</comment>
+ <comment xml:lang="be@latin">licenzijnyja ŭmovy</comment>
+ <comment xml:lang="bg">Лицензни условия</comment>
+ <comment xml:lang="ca">condicions de llicència</comment>
+ <comment xml:lang="cs">licenční podmínky</comment>
+ <comment xml:lang="da">licensbetingelser</comment>
+ <comment xml:lang="de">Lizenzbedingungen</comment>
+ <comment xml:lang="el">Όροι άδειας</comment>
+ <comment xml:lang="en_GB">licence terms</comment>
+ <comment xml:lang="es">términos de licencia</comment>
+ <comment xml:lang="eu">lizentzia baldintzak</comment>
+ <comment xml:lang="fi">lisenssiehdot</comment>
+ <comment xml:lang="fo">loyvistreytir</comment>
+ <comment xml:lang="fr">termes de licence</comment>
+ <comment xml:lang="ga">téarmaí ceadúnais</comment>
+ <comment xml:lang="gl">termos de licenza</comment>
+ <comment xml:lang="he">תנאי רישיון</comment>
+ <comment xml:lang="hr">Uvjeti licence</comment>
+ <comment xml:lang="hu">licencfeltételek</comment>
+ <comment xml:lang="ia">Conditiones de licentia</comment>
+ <comment xml:lang="id">persyaratan lisensi</comment>
+ <comment xml:lang="it">Termini di licenza</comment>
+ <comment xml:lang="ja">ソフトウェアライセンス条項</comment>
+ <comment xml:lang="kk">лицензиялық келісімі</comment>
+ <comment xml:lang="ko">라이선스 조항</comment>
+ <comment xml:lang="lt">licencijos sąlygos</comment>
+ <comment xml:lang="lv">licences nosacījumi</comment>
+ <comment xml:lang="nb">lisensbestemmelser</comment>
+ <comment xml:lang="nl">licentievoorwaarden</comment>
+ <comment xml:lang="nn">lisensvilkår</comment>
+ <comment xml:lang="oc">tèrmes de licéncia</comment>
+ <comment xml:lang="pl">Warunki licencji</comment>
+ <comment xml:lang="pt">termos de licença</comment>
+ <comment xml:lang="pt_BR">Termos de licença</comment>
+ <comment xml:lang="ro">termeni de licență</comment>
+ <comment xml:lang="ru">Лицензионное соглашение</comment>
+ <comment xml:lang="sk">Licenčné podmienky</comment>
+ <comment xml:lang="sl">pogoji in dovoljenja uporabe</comment>
+ <comment xml:lang="sq">Kushte liçence</comment>
+ <comment xml:lang="sr">услови коришћења</comment>
+ <comment xml:lang="sv">licensvillkor</comment>
+ <comment xml:lang="tr">lisans koşulları</comment>
+ <comment xml:lang="uk">ліцензійні умови</comment>
+ <comment xml:lang="vi">điều kiện giấy phép</comment>
+ <comment xml:lang="zh_CN">软件许可条款</comment>
+ <comment xml:lang="zh_TW">授權條款</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="COPYING"/>
+ </mime-type>
+ <mime-type type="text/x-credits">
+ <comment>author credits</comment>
+ <comment xml:lang="ar">شكر وتقدير المؤلف</comment>
+ <comment xml:lang="ast">creitos del autor</comment>
+ <comment xml:lang="be@latin">zasłuhi aŭtara</comment>
+ <comment xml:lang="bg">Благодарности към авторите</comment>
+ <comment xml:lang="ca">atribucions d'autor</comment>
+ <comment xml:lang="cs">autorské zásluhy</comment>
+ <comment xml:lang="da">bidragydere</comment>
+ <comment xml:lang="de">Autorendanksagung</comment>
+ <comment xml:lang="el">Μνεία συγγραφέων</comment>
+ <comment xml:lang="en_GB">author credits</comment>
+ <comment xml:lang="es">reconocimiento de autoría</comment>
+ <comment xml:lang="fi">tekijöiden kiitokset</comment>
+ <comment xml:lang="fo">høvundaheiður</comment>
+ <comment xml:lang="fr">remerciements</comment>
+ <comment xml:lang="ga">admhálacha údar</comment>
+ <comment xml:lang="gl">créditos de autor</comment>
+ <comment xml:lang="he">קרדיטים של היוצר</comment>
+ <comment xml:lang="hr">Zasluge autora</comment>
+ <comment xml:lang="hu">szerzők listája</comment>
+ <comment xml:lang="ia">Recognoscentia de autores</comment>
+ <comment xml:lang="id">kredit penulis</comment>
+ <comment xml:lang="it">Riconoscimenti autori</comment>
+ <comment xml:lang="ja">ソフトウェア作者クレジット</comment>
+ <comment xml:lang="kk">бағдарлама авторлары</comment>
+ <comment xml:lang="ko">작성자 정보</comment>
+ <comment xml:lang="lt">padėkos autoriams</comment>
+ <comment xml:lang="lv">veidotāji</comment>
+ <comment xml:lang="nb">liste med bidragsytere</comment>
+ <comment xml:lang="nl">auteursinformatie</comment>
+ <comment xml:lang="nn">forfattarliste</comment>
+ <comment xml:lang="oc">mercejaments</comment>
+ <comment xml:lang="pl">Podziękowania autorów programu</comment>
+ <comment xml:lang="pt">créditos de autor</comment>
+ <comment xml:lang="pt_BR">Créditos do autor</comment>
+ <comment xml:lang="ro">mulțumiri autori</comment>
+ <comment xml:lang="ru">Авторы программы</comment>
+ <comment xml:lang="sk">Autorské zásluhy</comment>
+ <comment xml:lang="sl">avtorske zasluge</comment>
+ <comment xml:lang="sq">Kreditë e autorëve</comment>
+ <comment xml:lang="sr">заслуге аутора</comment>
+ <comment xml:lang="sv">författarlista</comment>
+ <comment xml:lang="tr">yazar bilgileri</comment>
+ <comment xml:lang="uk">подяки авторам програми</comment>
+ <comment xml:lang="vi">công trạng tác giả</comment>
+ <comment xml:lang="zh_CN">软件作者致谢</comment>
+ <comment xml:lang="zh_TW">作者致謝名單</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="CREDITS"/>
+ </mime-type>
+ <mime-type type="text/x-csrc">
+ <comment>C source code</comment>
+ <comment xml:lang="ar">شفرة مصدر سي</comment>
+ <comment xml:lang="be@latin">Kryničny kod C</comment>
+ <comment xml:lang="bg">Изходен код — C</comment>
+ <comment xml:lang="ca">codi font en C</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce C</comment>
+ <comment xml:lang="da">C-kildekode</comment>
+ <comment xml:lang="de">C-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας C</comment>
+ <comment xml:lang="en_GB">C source code</comment>
+ <comment xml:lang="eo">C-fontkodo</comment>
+ <comment xml:lang="es">código fuente en C</comment>
+ <comment xml:lang="eu">C iturburu-kodea</comment>
+ <comment xml:lang="fi">C-lähdekoodi</comment>
+ <comment xml:lang="fo">C keldukota</comment>
+ <comment xml:lang="fr">code source C</comment>
+ <comment xml:lang="ga">cód foinseach C</comment>
+ <comment xml:lang="gl">código fonte en C</comment>
+ <comment xml:lang="he">קוד מקור של C</comment>
+ <comment xml:lang="hr">C izvorni kôd</comment>
+ <comment xml:lang="hu">C-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte C</comment>
+ <comment xml:lang="id">Kode sumber C</comment>
+ <comment xml:lang="it">Codice sorgente C</comment>
+ <comment xml:lang="ja">C ソースコード</comment>
+ <comment xml:lang="ka">C-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">C бастапқы коды</comment>
+ <comment xml:lang="ko">C 소스 코드</comment>
+ <comment xml:lang="lt">C pradinis kodas</comment>
+ <comment xml:lang="lv">C pirmkods</comment>
+ <comment xml:lang="ms">Kod sumber C</comment>
+ <comment xml:lang="nb">C-kildekode</comment>
+ <comment xml:lang="nl">C-broncode</comment>
+ <comment xml:lang="nn">C-kjeldekode</comment>
+ <comment xml:lang="oc">còde font C</comment>
+ <comment xml:lang="pl">Kod źródłowy C</comment>
+ <comment xml:lang="pt">código origem C</comment>
+ <comment xml:lang="pt_BR">Código-fonte C</comment>
+ <comment xml:lang="ro">Cod sursă C</comment>
+ <comment xml:lang="ru">Исходный код C</comment>
+ <comment xml:lang="sk">Zdrojový kód jazyka C</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode C</comment>
+ <comment xml:lang="sq">Kod burues C</comment>
+ <comment xml:lang="sr">Ц изворни ко̂д</comment>
+ <comment xml:lang="sv">C-källkod</comment>
+ <comment xml:lang="tr">C kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою C</comment>
+ <comment xml:lang="vi">Mã nguồn C</comment>
+ <comment xml:lang="zh_CN">C 源代码</comment>
+ <comment xml:lang="zh_TW">C 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <alias type="text/x-c"/>
+ <glob pattern="*.c" case-sensitive="true"/>
+ <magic priority="30">
+ <match value="/*" type="string" offset="0"/>
+ <match value="//" type="string" offset="0"/>
+ <match value="#include" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="text/x-csharp">
+ <comment>C# source code</comment>
+ <comment xml:lang="ar">شفرة مصدر سي#</comment>
+ <comment xml:lang="be@latin">Kryničny kod C#</comment>
+ <comment xml:lang="bg">Изходен код — C#</comment>
+ <comment xml:lang="ca">codi font en C#</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce C#</comment>
+ <comment xml:lang="da">C#-kildekode</comment>
+ <comment xml:lang="de">C#-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας C#</comment>
+ <comment xml:lang="en_GB">C# source code</comment>
+ <comment xml:lang="eo">C#-fontkodo</comment>
+ <comment xml:lang="es">código fuente en C#</comment>
+ <comment xml:lang="eu">C# iturburu-kodea</comment>
+ <comment xml:lang="fi">C#-lähdekoodi</comment>
+ <comment xml:lang="fo">C# keldukota</comment>
+ <comment xml:lang="fr">code source C#</comment>
+ <comment xml:lang="ga">cód foinseach C#</comment>
+ <comment xml:lang="gl">código fonte en C#</comment>
+ <comment xml:lang="he">קוד מקור של C#‎</comment>
+ <comment xml:lang="hr">C# izvorni kôd</comment>
+ <comment xml:lang="hu">C#-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte C#</comment>
+ <comment xml:lang="id">Kode sumber C#</comment>
+ <comment xml:lang="it">Codice sorgente C#</comment>
+ <comment xml:lang="ja">C# ソースコード</comment>
+ <comment xml:lang="ka">C#-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">C# бастапқы коды</comment>
+ <comment xml:lang="ko">C# 소스 코드</comment>
+ <comment xml:lang="lt">C# pradinis kodas</comment>
+ <comment xml:lang="lv">C# pirmkods</comment>
+ <comment xml:lang="ms">Kod sumber C#</comment>
+ <comment xml:lang="nb">C#-kildekode</comment>
+ <comment xml:lang="nl">C#-broncode</comment>
+ <comment xml:lang="nn">C#-kjeldekode</comment>
+ <comment xml:lang="oc">còde font C#</comment>
+ <comment xml:lang="pl">Kod źródłowy C#</comment>
+ <comment xml:lang="pt">código origem C#</comment>
+ <comment xml:lang="pt_BR">Código-fonte C#</comment>
+ <comment xml:lang="ro">Cod sursă C#</comment>
+ <comment xml:lang="ru">Исходный код C#</comment>
+ <comment xml:lang="sk">Zdrojový kód jazyka C#</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode C#</comment>
+ <comment xml:lang="sq">Kod burues C#</comment>
+ <comment xml:lang="sr">Ц# изворни ко̂д</comment>
+ <comment xml:lang="sv">C#-källkod</comment>
+ <comment xml:lang="tr">C# kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою C#</comment>
+ <comment xml:lang="vi">Mã nguồn C#</comment>
+ <comment xml:lang="zh_CN">C# 源代码</comment>
+ <comment xml:lang="zh_TW">C# 源碼</comment>
+ <sub-class-of type="text/x-csrc"/>
+ <glob pattern="*.cs"/>
+ </mime-type>
+ <mime-type type="text/x-vala">
+ <comment>Vala source code</comment>
+ <comment xml:lang="ar">شفرة مصدر Vala</comment>
+ <comment xml:lang="be@latin">Kryničny kod Vala</comment>
+ <comment xml:lang="bg">Изходен код — Vala</comment>
+ <comment xml:lang="ca">codi font en Vala</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Vala</comment>
+ <comment xml:lang="da">Valakildekode</comment>
+ <comment xml:lang="de">Vala-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Vala</comment>
+ <comment xml:lang="en_GB">Vala source code</comment>
+ <comment xml:lang="eo">Vala-fontkodo</comment>
+ <comment xml:lang="es">código fuente en Vala</comment>
+ <comment xml:lang="eu">Vala iturburu-kodea</comment>
+ <comment xml:lang="fi">Vala-lähdekoodi</comment>
+ <comment xml:lang="fo">Vala keldukota</comment>
+ <comment xml:lang="fr">code source Vala</comment>
+ <comment xml:lang="ga">cód foinseach Vala</comment>
+ <comment xml:lang="gl">código fonte en Vala</comment>
+ <comment xml:lang="he">קוד מקור של Vala</comment>
+ <comment xml:lang="hr">Vala izvorni kôd</comment>
+ <comment xml:lang="hu">Vala forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Vala</comment>
+ <comment xml:lang="id">Kode sumber Vala</comment>
+ <comment xml:lang="it">Codice sorgente Vala</comment>
+ <comment xml:lang="ja">Vala ソースコード</comment>
+ <comment xml:lang="kk">Vala бастапқы коды</comment>
+ <comment xml:lang="ko">Vala 소스 코드</comment>
+ <comment xml:lang="lt">Vala pradinis kodas</comment>
+ <comment xml:lang="lv">Vala pirmkods</comment>
+ <comment xml:lang="nb">Vala-kildekode</comment>
+ <comment xml:lang="nl">Vala-broncode</comment>
+ <comment xml:lang="nn">Vala-kjeldekode</comment>
+ <comment xml:lang="oc">còde font Vala</comment>
+ <comment xml:lang="pl">Kod źródłowy Vala</comment>
+ <comment xml:lang="pt">código origem Vala</comment>
+ <comment xml:lang="pt_BR">Código-fonte Vala</comment>
+ <comment xml:lang="ro">Cod sursă Vala</comment>
+ <comment xml:lang="ru">Исходный код Vala</comment>
+ <comment xml:lang="sk">Zdrojový kód Vala</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Vala</comment>
+ <comment xml:lang="sq">Kod burues Vala</comment>
+ <comment xml:lang="sr">Вала изворни ко̂д</comment>
+ <comment xml:lang="sv">Vala-källkod</comment>
+ <comment xml:lang="tr">Vala kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Vala</comment>
+ <comment xml:lang="vi">Mã nguồn Vala</comment>
+ <comment xml:lang="zh_CN">Vala 源代码</comment>
+ <comment xml:lang="zh_TW">Vala 源碼</comment>
+ <sub-class-of type="text/x-csrc"/>
+ <glob pattern="*.vala"/>
+ <glob pattern="*.vapi"/>
+ </mime-type>
+ <mime-type type="text/x-ooc">
+ <comment>OOC source code</comment>
+ <comment xml:lang="bg">Изходен код — OOC</comment>
+ <comment xml:lang="ca">codi font en OOC</comment>
+ <comment xml:lang="cs">zdrojový kód OOC</comment>
+ <comment xml:lang="da">OOC-kildekode</comment>
+ <comment xml:lang="de">OOC-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας OOC</comment>
+ <comment xml:lang="en_GB">OOC source code</comment>
+ <comment xml:lang="eo">OOC-fontkodo</comment>
+ <comment xml:lang="es">código fuente en OOC</comment>
+ <comment xml:lang="eu">OOC iturburu-kodea</comment>
+ <comment xml:lang="fi">OOC-lähdekoodi</comment>
+ <comment xml:lang="fr">source code OOC</comment>
+ <comment xml:lang="ga">cód foinseach OOC</comment>
+ <comment xml:lang="gl">código fonte de OOC</comment>
+ <comment xml:lang="he">קוד מקור של OOC</comment>
+ <comment xml:lang="hr">OOC izvorni kôd</comment>
+ <comment xml:lang="hu">OOC forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte OCC</comment>
+ <comment xml:lang="id">Kode sumber OOC</comment>
+ <comment xml:lang="it">Codice sorgente OOC</comment>
+ <comment xml:lang="ja">OOC ソースコード</comment>
+ <comment xml:lang="ka">OOC-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">OOC бастапқы коды</comment>
+ <comment xml:lang="ko">OOC 소스 코드</comment>
+ <comment xml:lang="lv">OOC pirmkods</comment>
+ <comment xml:lang="nl">OOC broncode</comment>
+ <comment xml:lang="oc">font còde OOC</comment>
+ <comment xml:lang="pl">Kod źródłowy OOC</comment>
+ <comment xml:lang="pt">código origem OOC</comment>
+ <comment xml:lang="pt_BR">Código-fonte OOC</comment>
+ <comment xml:lang="ru">Исходный код OOC</comment>
+ <comment xml:lang="sk">Zdrojový kód OOC</comment>
+ <comment xml:lang="sl">Izvorna koda OOC</comment>
+ <comment xml:lang="sr">ООЦ изворни ко̂д</comment>
+ <comment xml:lang="sv">OOC-källkod</comment>
+ <comment xml:lang="tr">OOC kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою OOC</comment>
+ <comment xml:lang="zh_CN">OOC 源代码</comment>
+ <comment xml:lang="zh_TW">OOC 源碼</comment>
+ <acronym>OOC</acronym>
+ <expanded-acronym>Out Of Class</expanded-acronym>
+ <sub-class-of type="text/x-csrc"/>
+ <glob pattern="*.ooc"/>
+ </mime-type>
+ <mime-type type="text/x-dcl">
+ <comment>DCL script</comment>
+ <comment xml:lang="ar">سكربت DCL</comment>
+ <comment xml:lang="az">DCL skripti</comment>
+ <comment xml:lang="be@latin">Skrypt DCL</comment>
+ <comment xml:lang="bg">Скрипт — DCL</comment>
+ <comment xml:lang="ca">script DCL</comment>
+ <comment xml:lang="cs">skript DCL</comment>
+ <comment xml:lang="cy">Sgript DCL</comment>
+ <comment xml:lang="da">DCL-program</comment>
+ <comment xml:lang="de">DCL-Skript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών DCL</comment>
+ <comment xml:lang="en_GB">DCL script</comment>
+ <comment xml:lang="eo">DCL-skripto</comment>
+ <comment xml:lang="es">secuencia de órdenes en DCL</comment>
+ <comment xml:lang="eu">DCL script-a</comment>
+ <comment xml:lang="fi">DCL-komentotiedosto</comment>
+ <comment xml:lang="fo">DCL boðrøð</comment>
+ <comment xml:lang="fr">script DCL</comment>
+ <comment xml:lang="ga">script DCL</comment>
+ <comment xml:lang="gl">script de DCL</comment>
+ <comment xml:lang="he">תסריט DCL</comment>
+ <comment xml:lang="hr">DCL skripta</comment>
+ <comment xml:lang="hu">DCL-parancsfájl</comment>
+ <comment xml:lang="ia">Script DCL</comment>
+ <comment xml:lang="id">Skrip DCL</comment>
+ <comment xml:lang="it">Script DCL</comment>
+ <comment xml:lang="ja">DCL スクリプト</comment>
+ <comment xml:lang="ka">DCL სცენარი</comment>
+ <comment xml:lang="kk">DCL сценарийі</comment>
+ <comment xml:lang="ko">DCL 스크립트</comment>
+ <comment xml:lang="lt">DCL scenarijus</comment>
+ <comment xml:lang="lv">DCL skripts</comment>
+ <comment xml:lang="ms">Skrip DCL</comment>
+ <comment xml:lang="nb">DCL-skript</comment>
+ <comment xml:lang="nl">DCL-script</comment>
+ <comment xml:lang="nn">DCL-skript</comment>
+ <comment xml:lang="oc">escript DCL</comment>
+ <comment xml:lang="pl">Skrypt DCL</comment>
+ <comment xml:lang="pt">script DCL</comment>
+ <comment xml:lang="pt_BR">Script DCL</comment>
+ <comment xml:lang="ro">Script DCL</comment>
+ <comment xml:lang="ru">Сценарий DCL</comment>
+ <comment xml:lang="sk">Skript DCL</comment>
+ <comment xml:lang="sl">Skriptna datoteka DCL</comment>
+ <comment xml:lang="sq">Script DCL</comment>
+ <comment xml:lang="sr">ДЦЛ скрипта</comment>
+ <comment xml:lang="sv">DCL-skript</comment>
+ <comment xml:lang="tr">DCL betiği</comment>
+ <comment xml:lang="uk">скрипт DCL</comment>
+ <comment xml:lang="vi">Văn lệnh DCL</comment>
+ <comment xml:lang="zh_CN">DCL 脚本</comment>
+ <comment xml:lang="zh_TW">DCL 指令稿</comment>
+ <acronym>DCL</acronym>
+ <expanded-acronym>Data Conversion Laboratory</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.dcl"/>
+ </mime-type>
+ <mime-type type="text/x-dsl">
+ <comment>DSSSL document</comment>
+ <comment xml:lang="ar">مستند DSSSL</comment>
+ <comment xml:lang="ast">Documentu DSSSL</comment>
+ <comment xml:lang="az">DSSSL sənədi</comment>
+ <comment xml:lang="be@latin">Dakument DSSSL</comment>
+ <comment xml:lang="bg">Документ — DSSSL</comment>
+ <comment xml:lang="ca">document DSSSL</comment>
+ <comment xml:lang="cs">dokument DSSSL</comment>
+ <comment xml:lang="cy">Dogfen DSSSL</comment>
+ <comment xml:lang="da">DSSSL-dokument</comment>
+ <comment xml:lang="de">DSSSL-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο DSSSL</comment>
+ <comment xml:lang="en_GB">DSSSL document</comment>
+ <comment xml:lang="eo">DSSSL-dokumento</comment>
+ <comment xml:lang="es">documento DSSSL</comment>
+ <comment xml:lang="eu">DSSSL dokumentua</comment>
+ <comment xml:lang="fi">DSSSL-asiakirja</comment>
+ <comment xml:lang="fo">DSSSL skjal</comment>
+ <comment xml:lang="fr">document DSSSL</comment>
+ <comment xml:lang="ga">cáipéis DSSSL</comment>
+ <comment xml:lang="gl">documento DSSSL</comment>
+ <comment xml:lang="he">מסמך DSSSL</comment>
+ <comment xml:lang="hr">DSSSL dokument</comment>
+ <comment xml:lang="hu">DSSSL-dokumentum</comment>
+ <comment xml:lang="ia">Documento DSSSL</comment>
+ <comment xml:lang="id">Dokumen DSSSL</comment>
+ <comment xml:lang="it">Documento DSSSL</comment>
+ <comment xml:lang="ja">DSSSL ドキュメント</comment>
+ <comment xml:lang="ka">DSSSL დოკუმენტი</comment>
+ <comment xml:lang="kk">DSSSL құжаты</comment>
+ <comment xml:lang="ko">DSSSL 문서</comment>
+ <comment xml:lang="lt">DSSSL dokumentas</comment>
+ <comment xml:lang="lv">DSSSL dokuments</comment>
+ <comment xml:lang="ms">Dokumen DSSSL</comment>
+ <comment xml:lang="nb">DSSSL-dokument</comment>
+ <comment xml:lang="nl">DSSSL-document</comment>
+ <comment xml:lang="nn">DSSSL-dokument</comment>
+ <comment xml:lang="oc">document DSSSL</comment>
+ <comment xml:lang="pl">Dokument DSSSL</comment>
+ <comment xml:lang="pt">documento DSSSL</comment>
+ <comment xml:lang="pt_BR">Documento DSSSL</comment>
+ <comment xml:lang="ro">Document DSSSL</comment>
+ <comment xml:lang="ru">Документ DSSSL</comment>
+ <comment xml:lang="sk">Dokument DSSSL</comment>
+ <comment xml:lang="sl">Dokument DSSSL</comment>
+ <comment xml:lang="sq">Dokument DSSSL</comment>
+ <comment xml:lang="sr">ДСССЛ документ</comment>
+ <comment xml:lang="sv">DSSSL-dokument</comment>
+ <comment xml:lang="tr">DSSSL belgesi</comment>
+ <comment xml:lang="uk">документ DSSSL</comment>
+ <comment xml:lang="vi">Tài liệu DSSSL</comment>
+ <comment xml:lang="zh_CN">DSSSL 文档</comment>
+ <comment xml:lang="zh_TW">DSSSL 文件</comment>
+ <acronym>DSSSL</acronym>
+ <expanded-acronym>Document Style Semantics and Specification Language</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.dsl"/>
+ </mime-type>
+ <mime-type type="text/x-dsrc">
+ <comment>D source code</comment>
+ <comment xml:lang="ar">شفرة مصدر D</comment>
+ <comment xml:lang="be@latin">Kryničny kod D</comment>
+ <comment xml:lang="bg">Изходен код — D</comment>
+ <comment xml:lang="ca">codi font en D</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce D</comment>
+ <comment xml:lang="da">D-kildekode</comment>
+ <comment xml:lang="de">D-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας D</comment>
+ <comment xml:lang="en_GB">D source code</comment>
+ <comment xml:lang="eo">D-fontkodo</comment>
+ <comment xml:lang="es">código fuente en D</comment>
+ <comment xml:lang="eu">D iturburu-kodea</comment>
+ <comment xml:lang="fi">D-lähdekoodi</comment>
+ <comment xml:lang="fo">D keldukota</comment>
+ <comment xml:lang="fr">code source D</comment>
+ <comment xml:lang="ga">cód foinseach D</comment>
+ <comment xml:lang="gl">código fonte de D</comment>
+ <comment xml:lang="he">קוד מקור לשפת D</comment>
+ <comment xml:lang="hr">D izvorni kôd</comment>
+ <comment xml:lang="hu">D-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte D</comment>
+ <comment xml:lang="id">Kode sumber D</comment>
+ <comment xml:lang="it">Codice sorgente D</comment>
+ <comment xml:lang="ja">D ソースコード</comment>
+ <comment xml:lang="ka">D-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">D бастапқы коды</comment>
+ <comment xml:lang="ko">D 소스 코드</comment>
+ <comment xml:lang="lt">D pradinis kodas</comment>
+ <comment xml:lang="lv">D pirmkods</comment>
+ <comment xml:lang="nb">D-kildekode</comment>
+ <comment xml:lang="nl">D-broncode</comment>
+ <comment xml:lang="nn">D-kjeldekode</comment>
+ <comment xml:lang="oc">còde font D</comment>
+ <comment xml:lang="pl">Kod źródłowy D</comment>
+ <comment xml:lang="pt">código origem D</comment>
+ <comment xml:lang="pt_BR">Código-fonte D</comment>
+ <comment xml:lang="ro">Cod sursă D</comment>
+ <comment xml:lang="ru">Исходный код D</comment>
+ <comment xml:lang="sk">Zdrojový kód jazyka D</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode D</comment>
+ <comment xml:lang="sq">Kod burues D</comment>
+ <comment xml:lang="sr">Д изворни ко̂д</comment>
+ <comment xml:lang="sv">D-källkod</comment>
+ <comment xml:lang="tr">D kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою D</comment>
+ <comment xml:lang="vi">Mã nguồn D</comment>
+ <comment xml:lang="zh_CN">D 源代码</comment>
+ <comment xml:lang="zh_TW">D 源碼</comment>
+ <sub-class-of type="text/x-csrc"/>
+ <glob pattern="*.d"/>
+ <glob pattern="*.di"/>
+ </mime-type>
+ <mime-type type="application/xml-dtd">
+ <comment>DTD file</comment>
+ <comment xml:lang="ar">ملف DTD</comment>
+ <comment xml:lang="be@latin">Fajł DTD</comment>
+ <comment xml:lang="bg">Документ — DTD</comment>
+ <comment xml:lang="ca">fitxer DTD</comment>
+ <comment xml:lang="cs">soubor DTD</comment>
+ <comment xml:lang="da">DTD-fil</comment>
+ <comment xml:lang="de">DTD-Datei</comment>
+ <comment xml:lang="el">Αρχείο DTD</comment>
+ <comment xml:lang="en_GB">DTD file</comment>
+ <comment xml:lang="eo">DTD-dosiero</comment>
+ <comment xml:lang="es">archivo DTD</comment>
+ <comment xml:lang="eu">DTD fitxategia</comment>
+ <comment xml:lang="fi">DTD-tiedosto</comment>
+ <comment xml:lang="fo">DTD fíla</comment>
+ <comment xml:lang="fr">fichier DTD</comment>
+ <comment xml:lang="ga">comhad DTD</comment>
+ <comment xml:lang="gl">ficheiro DTD</comment>
+ <comment xml:lang="he">מסמך DTD</comment>
+ <comment xml:lang="hr">DTD datoteka</comment>
+ <comment xml:lang="hu">DTD fájl</comment>
+ <comment xml:lang="ia">File DTD</comment>
+ <comment xml:lang="id">Berkas DTD</comment>
+ <comment xml:lang="it">File DTD</comment>
+ <comment xml:lang="ja">DTD ファイル</comment>
+ <comment xml:lang="ka">DTD ფაილი</comment>
+ <comment xml:lang="kk">DTD файлы</comment>
+ <comment xml:lang="ko">DTD 파일</comment>
+ <comment xml:lang="lt">DTD failas</comment>
+ <comment xml:lang="lv">DTD datne</comment>
+ <comment xml:lang="nb">DTD-fil</comment>
+ <comment xml:lang="nl">DTD-bestand</comment>
+ <comment xml:lang="nn">DTD-fil</comment>
+ <comment xml:lang="oc">fichièr DTD</comment>
+ <comment xml:lang="pl">Plik DTD</comment>
+ <comment xml:lang="pt">ficheiro DTD</comment>
+ <comment xml:lang="pt_BR">Arquivo DTD</comment>
+ <comment xml:lang="ro">Fișier DTD</comment>
+ <comment xml:lang="ru">Файл DTD</comment>
+ <comment xml:lang="sk">Súbor DTD</comment>
+ <comment xml:lang="sl">Datoteka DTD</comment>
+ <comment xml:lang="sq">File DTD</comment>
+ <comment xml:lang="sr">ДТД датотека</comment>
+ <comment xml:lang="sv">DTD-fil</comment>
+ <comment xml:lang="tr">DTD dosyası</comment>
+ <comment xml:lang="uk">файл DTD</comment>
+ <comment xml:lang="vi">Tập tin DTD</comment>
+ <comment xml:lang="zh_CN">DTD 文件</comment>
+ <comment xml:lang="zh_TW">DTD 檔</comment>
+ <acronym>DTD</acronym>
+ <expanded-acronym>Document Type Definition</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.dtd"/>
+ <alias type="text/x-dtd"/>
+ </mime-type>
+ <mime-type type="text/x-eiffel">
+ <comment>Eiffel source code</comment>
+ <comment xml:lang="ar">شفرة مصدر Eiffel</comment>
+ <comment xml:lang="be@latin">Kryničny kod Eiffel</comment>
+ <comment xml:lang="bg">Изходен код — Eiffel</comment>
+ <comment xml:lang="ca">codi font en Eiffel</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Eiffel</comment>
+ <comment xml:lang="da">Eiffelkildekode</comment>
+ <comment xml:lang="de">Eiffel-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Eiffel</comment>
+ <comment xml:lang="en_GB">Eiffel source code</comment>
+ <comment xml:lang="eo">Eiffel-fontkodo</comment>
+ <comment xml:lang="es">código fuente en Eiffel</comment>
+ <comment xml:lang="eu">Eiffel iturburu-kodea</comment>
+ <comment xml:lang="fi">Eiffel-lähdekoodi</comment>
+ <comment xml:lang="fo">Eiffel keldukota</comment>
+ <comment xml:lang="fr">code source Eiffel</comment>
+ <comment xml:lang="ga">cód foinseach Eiffel</comment>
+ <comment xml:lang="gl">código fone de Eiffel</comment>
+ <comment xml:lang="he">קוד מקור של Eiffel</comment>
+ <comment xml:lang="hr">Eiffel izvorni kôd</comment>
+ <comment xml:lang="hu">Eiffel forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Eiffel</comment>
+ <comment xml:lang="id">Kode sumber Eiffel</comment>
+ <comment xml:lang="it">Codice sorgente Eiffel</comment>
+ <comment xml:lang="ja">Eiffel ソースコード</comment>
+ <comment xml:lang="ka">Eiffel-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">Eiffel бастапқы коды</comment>
+ <comment xml:lang="ko">Eiffel 소스 코드</comment>
+ <comment xml:lang="lt">Eiffel pirminis programos tekstas</comment>
+ <comment xml:lang="lv">Eiffel pirmkods</comment>
+ <comment xml:lang="nb">Eiffel-kildekode</comment>
+ <comment xml:lang="nl">Eiffel-broncode</comment>
+ <comment xml:lang="nn">Eiffel-kjeldekode</comment>
+ <comment xml:lang="oc">còde font Eiffel</comment>
+ <comment xml:lang="pl">Kod źródłowy Eiffel</comment>
+ <comment xml:lang="pt">código origem Eiffel</comment>
+ <comment xml:lang="pt_BR">Código-fonte Eiffel</comment>
+ <comment xml:lang="ro">Cod sursă Eiffel</comment>
+ <comment xml:lang="ru">Исходный код Eiffel</comment>
+ <comment xml:lang="sk">Zdrojový kód Eiffel</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Eiffel</comment>
+ <comment xml:lang="sq">Kod burues Eiffel</comment>
+ <comment xml:lang="sr">Ајфел изворни ко̂д</comment>
+ <comment xml:lang="sv">Eiffel-källkod</comment>
+ <comment xml:lang="tr">Eiffel kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Eiffel</comment>
+ <comment xml:lang="vi">Mã nguồn Eiffel</comment>
+ <comment xml:lang="zh_CN">Eiffel 源代码</comment>
+ <comment xml:lang="zh_TW">Eiffel 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.e"/>
+ <glob pattern="*.eif"/>
+ </mime-type>
+ <mime-type type="text/x-emacs-lisp">
+ <comment>Emacs Lisp source code</comment>
+ <comment xml:lang="ar">شفرة مصدر Emacs Lisp</comment>
+ <comment xml:lang="az">Emacs Lisp mənbə kodu</comment>
+ <comment xml:lang="be@latin">Kryničny kod Emacs Lisp</comment>
+ <comment xml:lang="bg">Изходен код — Emacs Lisp</comment>
+ <comment xml:lang="ca">codi font en Emacs Lisp</comment>
+ <comment xml:lang="cs">zdrojový kód Emacs Lisp</comment>
+ <comment xml:lang="cy">Ffynhonnell rhaglen EMACS LISP</comment>
+ <comment xml:lang="da">Emacs Lisp-kildekode</comment>
+ <comment xml:lang="de">Emacs-Lisp-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Emacs Lisp</comment>
+ <comment xml:lang="en_GB">Emacs Lisp source code</comment>
+ <comment xml:lang="eo">fontkodo en Emacs Lisp</comment>
+ <comment xml:lang="es">código fuente en Lisp de Emacs</comment>
+ <comment xml:lang="eu">Emacs Lisp iturburu-kodea</comment>
+ <comment xml:lang="fi">Emacs Lisp -lähdekoodi</comment>
+ <comment xml:lang="fo">Emacs Lisp keldukota</comment>
+ <comment xml:lang="fr">code source Emacs Lisp</comment>
+ <comment xml:lang="ga">cód foinseach Emacs Lisp</comment>
+ <comment xml:lang="gl">código fonte de Emacs Lisp</comment>
+ <comment xml:lang="he">קוד מקור של Emcas Lisp</comment>
+ <comment xml:lang="hr">Emacs Lisp izvorni kôd</comment>
+ <comment xml:lang="hu">Emacs Lisp-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Lisp de Emacs</comment>
+ <comment xml:lang="id">Kode sumber Emacs Lisp</comment>
+ <comment xml:lang="it">Codice sorgente Emacs Lisp</comment>
+ <comment xml:lang="ja">Emacs Lisp ソースコード</comment>
+ <comment xml:lang="ka">Emacs-ის Lisp საწყისი კოდი</comment>
+ <comment xml:lang="kk">Emacs Lisp бастапқы коды</comment>
+ <comment xml:lang="ko">Emacs Lisp 소스 코드</comment>
+ <comment xml:lang="lt">Emacs Lisp pradinis kodas</comment>
+ <comment xml:lang="lv">Emacs Lisp pirmkods</comment>
+ <comment xml:lang="ms">Kod sumber Emacs Lisp</comment>
+ <comment xml:lang="nb">Emacs Lisp-kildekode</comment>
+ <comment xml:lang="nl">Emacs Lisp-broncode</comment>
+ <comment xml:lang="nn">Emacs Lisp kjeldekode</comment>
+ <comment xml:lang="oc">còde font Emacs Lisp</comment>
+ <comment xml:lang="pl">Plik źródłowy Emacs Lisp</comment>
+ <comment xml:lang="pt">código origem Emacs Lisp</comment>
+ <comment xml:lang="pt_BR">Código-fonte Lisp do Emacs</comment>
+ <comment xml:lang="ro">Cod sursă Emacs Lisp</comment>
+ <comment xml:lang="ru">Исходный код Emacs Lisp</comment>
+ <comment xml:lang="sk">Zdrojový kód Emacs Lisp</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Emacs Lisp</comment>
+ <comment xml:lang="sq">Kod burues Emacs Lisp</comment>
+ <comment xml:lang="sr">Емакс Лисп изворни ко̂д</comment>
+ <comment xml:lang="sv">Emacs Lisp-källkod</comment>
+ <comment xml:lang="tr">Emacs Lisp kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Emacs Lisp</comment>
+ <comment xml:lang="vi">Mã nguồn Lisp Emacs</comment>
+ <comment xml:lang="zh_CN">Emacs Lisp 源代码</comment>
+ <comment xml:lang="zh_TW">Emacs Lisp 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="\012(" type="string" offset="0"/>
+ <match value=";ELC\023\000\000\000" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.el"/>
+ </mime-type>
+ <mime-type type="text/x-erlang">
+ <comment>Erlang source code</comment>
+ <comment xml:lang="ar">شفرة مصدر Erlang</comment>
+ <comment xml:lang="be@latin">Kryničny kod Erlang</comment>
+ <comment xml:lang="bg">Изходен код — Erlang</comment>
+ <comment xml:lang="ca">codi font en Erlang</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Erlang</comment>
+ <comment xml:lang="da">Erlangkildekode</comment>
+ <comment xml:lang="de">Erlang-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Erlang</comment>
+ <comment xml:lang="en_GB">Erlang source code</comment>
+ <comment xml:lang="eo">Erlang-fontkodo</comment>
+ <comment xml:lang="es">código fuente en Erlang</comment>
+ <comment xml:lang="eu">Erlang iturburu-kodea</comment>
+ <comment xml:lang="fi">Erlang-lähdekoodi</comment>
+ <comment xml:lang="fo">Erlang keldukota</comment>
+ <comment xml:lang="fr">code source Erlang</comment>
+ <comment xml:lang="ga">cód foinseach Erlang</comment>
+ <comment xml:lang="gl">código fonte de Erlang</comment>
+ <comment xml:lang="he">קוד מקור של Erlang</comment>
+ <comment xml:lang="hr">Erlang izvorni kôd</comment>
+ <comment xml:lang="hu">Erlang forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Erlang</comment>
+ <comment xml:lang="id">Kode sumber Erlang</comment>
+ <comment xml:lang="it">Codice sorgente Erlang</comment>
+ <comment xml:lang="ja">Erlang ソースコード</comment>
+ <comment xml:lang="ka">Erlang-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">Erlang бастапқы коды</comment>
+ <comment xml:lang="ko">Erlang 소스 코드</comment>
+ <comment xml:lang="lt">Erlang pradinis kodas</comment>
+ <comment xml:lang="lv">Erlang pirmkods</comment>
+ <comment xml:lang="nb">Erlang-kildekode</comment>
+ <comment xml:lang="nl">Erlang-broncode</comment>
+ <comment xml:lang="nn">Erlang-kjeldekode</comment>
+ <comment xml:lang="oc">còde font Erlang</comment>
+ <comment xml:lang="pl">Kod źródłowy Erlang</comment>
+ <comment xml:lang="pt">código origem Erlang</comment>
+ <comment xml:lang="pt_BR">Código-fonte Erlang</comment>
+ <comment xml:lang="ro">Cod sursă Erlang</comment>
+ <comment xml:lang="ru">Исходный код Erlang</comment>
+ <comment xml:lang="sk">Zdrojový kód Erlang</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Erlang</comment>
+ <comment xml:lang="sq">Kod burues Erlang</comment>
+ <comment xml:lang="sr">Ерланг изворни ко̂д</comment>
+ <comment xml:lang="sv">Erlang-källkod</comment>
+ <comment xml:lang="tr">Erlang kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Erlang</comment>
+ <comment xml:lang="vi">Mã nguồn Erlang</comment>
+ <comment xml:lang="zh_CN">Erlang 源代码</comment>
+ <comment xml:lang="zh_TW">Erlang 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.erl"/>
+ </mime-type>
+ <mime-type type="text/x-fortran">
+ <comment>Fortran source code</comment>
+ <comment xml:lang="ar">شفرة مصدر Fortran</comment>
+ <comment xml:lang="az">Fortran mənbə kodu</comment>
+ <comment xml:lang="be@latin">Kryničny kod Fortran</comment>
+ <comment xml:lang="bg">Изходен код — Fortran</comment>
+ <comment xml:lang="ca">codi font en Fortran</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Fortran</comment>
+ <comment xml:lang="cy">Ffynhonnell rhaglen FORTRAN</comment>
+ <comment xml:lang="da">Fortrankildekode</comment>
+ <comment xml:lang="de">Fortran-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Fortran</comment>
+ <comment xml:lang="en_GB">Fortran source code</comment>
+ <comment xml:lang="eo">Fotran-fontkodo</comment>
+ <comment xml:lang="es">código fuente en Fortran</comment>
+ <comment xml:lang="eu">Fortran-en iturburu-kodea</comment>
+ <comment xml:lang="fi">Fortran-lähdekoodi</comment>
+ <comment xml:lang="fo">Fortran keldukota</comment>
+ <comment xml:lang="fr">code source Fortran</comment>
+ <comment xml:lang="ga">cód foinseach Fortran</comment>
+ <comment xml:lang="gl">código fonte de Fortran</comment>
+ <comment xml:lang="he">קוד מקור של Fortran</comment>
+ <comment xml:lang="hr">Fortran izvorni kôd</comment>
+ <comment xml:lang="hu">Fortran-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Fortran</comment>
+ <comment xml:lang="id">Kode sumber Fortran</comment>
+ <comment xml:lang="it">Codice sorgente Fortran</comment>
+ <comment xml:lang="ja">Fortran ソースコード</comment>
+ <comment xml:lang="ka">Fortran-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">Fortran бастапқы коды</comment>
+ <comment xml:lang="ko">포트란 소스 코드</comment>
+ <comment xml:lang="lt">Fortran pradinis kodas</comment>
+ <comment xml:lang="lv">Fortran pirmkods</comment>
+ <comment xml:lang="ms">kod sumber Fortran</comment>
+ <comment xml:lang="nb">Fortran-kildekode</comment>
+ <comment xml:lang="nl">Fortran-broncode</comment>
+ <comment xml:lang="nn">Fortran-kjeldekode</comment>
+ <comment xml:lang="oc">còde font Fortran</comment>
+ <comment xml:lang="pl">Kod źródłowy Fortran</comment>
+ <comment xml:lang="pt">código origem Fortran</comment>
+ <comment xml:lang="pt_BR">Código-fonte Fortran</comment>
+ <comment xml:lang="ro">Cod sursă Fortran</comment>
+ <comment xml:lang="ru">Исходный код Fortran</comment>
+ <comment xml:lang="sk">Zdrojový kód Fortran</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Fortran</comment>
+ <comment xml:lang="sq">Kod burues Fortran</comment>
+ <comment xml:lang="sr">Фортран изворни ко̂д</comment>
+ <comment xml:lang="sv">Fortran-källkod</comment>
+ <comment xml:lang="tr">Fortran kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Fortran</comment>
+ <comment xml:lang="vi">Mã nguồn Fortran</comment>
+ <comment xml:lang="zh_CN">Fortran 源代码</comment>
+ <comment xml:lang="zh_TW">Fortran 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.f"/>
+ <glob pattern="*.f90"/>
+ <glob pattern="*.f95"/>
+ <glob pattern="*.for"/>
+ </mime-type>
+ <mime-type type="text/x-genie">
+ <comment>Genie source code</comment>
+ <comment xml:lang="ca">codi font de Genius</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Genie</comment>
+ <comment xml:lang="da">Genie-kildekode</comment>
+ <comment xml:lang="de">Genie-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Genie</comment>
+ <comment xml:lang="en_GB">Genie source code</comment>
+ <comment xml:lang="es">código fuente en Genie</comment>
+ <comment xml:lang="eu">Genie iturburu-kodea</comment>
+ <comment xml:lang="fi">Genie-lähdekoodi</comment>
+ <comment xml:lang="fr">code source Genie</comment>
+ <comment xml:lang="ga">cód foinseach Genie</comment>
+ <comment xml:lang="he">קוד מקור של Genie</comment>
+ <comment xml:lang="hr">Genie izvorni kôd</comment>
+ <comment xml:lang="hu">Genie forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Genie</comment>
+ <comment xml:lang="id">Kode sumber Genie</comment>
+ <comment xml:lang="it">Codice sorgente Genie</comment>
+ <comment xml:lang="kk">Genie бастапқы коды</comment>
+ <comment xml:lang="ko">Genie 소스 코드</comment>
+ <comment xml:lang="oc">còde font Genie</comment>
+ <comment xml:lang="pl">Kod źródłowy Genie</comment>
+ <comment xml:lang="pt">código origem Genie</comment>
+ <comment xml:lang="pt_BR">Código-fonte Genie</comment>
+ <comment xml:lang="ru">Исходный код Genie</comment>
+ <comment xml:lang="sk">Zdrojový kód Genie</comment>
+ <comment xml:lang="sl">Izvorna koda Genie</comment>
+ <comment xml:lang="sr">Гение изворни ко̂д</comment>
+ <comment xml:lang="sv">Genie-källkod</comment>
+ <comment xml:lang="tr">Genie kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Genie</comment>
+ <comment xml:lang="zh_CN">Genie 源代码</comment>
+ <comment xml:lang="zh_TW">Genie 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.gs" case-sensitive="true"/>
+ <generic-icon name="text-x-generic"/>
+ </mime-type>
+ <mime-type type="text/x-gettext-translation">
+ <comment>translation file</comment>
+ <comment xml:lang="ar">ملف الترجمة</comment>
+ <comment xml:lang="ast">ficheru de traducción</comment>
+ <comment xml:lang="be@latin">fajł pierakładu</comment>
+ <comment xml:lang="bg">Превод</comment>
+ <comment xml:lang="ca">fitxer de traducció</comment>
+ <comment xml:lang="cs">soubor překladu</comment>
+ <comment xml:lang="da">oversættelsesfil</comment>
+ <comment xml:lang="de">Übersetzungsdatei</comment>
+ <comment xml:lang="el">Αρχείο μετάφρασης</comment>
+ <comment xml:lang="en_GB">translation file</comment>
+ <comment xml:lang="eo">tradukad-dosiero</comment>
+ <comment xml:lang="es">archivo de traducción</comment>
+ <comment xml:lang="eu">itzulpen-fitxategia</comment>
+ <comment xml:lang="fi">käännöstiedosto</comment>
+ <comment xml:lang="fo">týðingarfíla</comment>
+ <comment xml:lang="fr">fichier de traduction</comment>
+ <comment xml:lang="ga">comhad aistriúcháin</comment>
+ <comment xml:lang="gl">ficheiro de tradución</comment>
+ <comment xml:lang="he">קובץ תרגום</comment>
+ <comment xml:lang="hr">Datoteka prijevoda</comment>
+ <comment xml:lang="hu">fordítási fájl</comment>
+ <comment xml:lang="ia">File de traduction</comment>
+ <comment xml:lang="id">berkas terjemahan</comment>
+ <comment xml:lang="it">File traduzione</comment>
+ <comment xml:lang="ja">翻訳ファイル</comment>
+ <comment xml:lang="ka">თარგმნის ფაილი</comment>
+ <comment xml:lang="kk">аудармалар файлы</comment>
+ <comment xml:lang="ko">번역 파일</comment>
+ <comment xml:lang="lt">vertimo failas</comment>
+ <comment xml:lang="lv">tulkošanas datne</comment>
+ <comment xml:lang="nb">oversettelsesfil</comment>
+ <comment xml:lang="nl">vertalingsbestand</comment>
+ <comment xml:lang="nn">omsetjingsfil</comment>
+ <comment xml:lang="oc">fichièr de traduccion</comment>
+ <comment xml:lang="pl">Plik tłumaczenia</comment>
+ <comment xml:lang="pt">ficheiro de tradução</comment>
+ <comment xml:lang="pt_BR">Arquivo de tradução</comment>
+ <comment xml:lang="ro">fișier traducere</comment>
+ <comment xml:lang="ru">Файл переводов</comment>
+ <comment xml:lang="sk">Súbor prekladu</comment>
+ <comment xml:lang="sl">datoteka prevoda programa</comment>
+ <comment xml:lang="sq">File përkthimesh</comment>
+ <comment xml:lang="sr">датотека превода</comment>
+ <comment xml:lang="sv">översättningsfil</comment>
+ <comment xml:lang="tr">çeviri dosyası</comment>
+ <comment xml:lang="uk">файл перекладу</comment>
+ <comment xml:lang="vi">tập tin dịch</comment>
+ <comment xml:lang="zh_CN">翻译文件</comment>
+ <comment xml:lang="zh_TW">翻譯檔</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.po"/>
+ <alias type="text/x-po"/>
+ <alias type="application/x-gettext"/>
+ </mime-type>
+ <mime-type type="text/x-gettext-translation-template">
+ <comment>translation template</comment>
+ <comment xml:lang="ar">قالب الترجمة</comment>
+ <comment xml:lang="ast">plantía de traducción</comment>
+ <comment xml:lang="be@latin">šablon dla pierakładu</comment>
+ <comment xml:lang="bg">Шаблон за преводи</comment>
+ <comment xml:lang="ca">plantilla de traducció</comment>
+ <comment xml:lang="cs">šablona překladu</comment>
+ <comment xml:lang="da">oversættelsesskabelon</comment>
+ <comment xml:lang="de">Übersetzungsvorlage</comment>
+ <comment xml:lang="el">Πρότυπο μετάφρασης</comment>
+ <comment xml:lang="en_GB">translation template</comment>
+ <comment xml:lang="eo">tradukad-ŝablono</comment>
+ <comment xml:lang="es">plantilla de traducción</comment>
+ <comment xml:lang="eu">itzulpenen txantiloia</comment>
+ <comment xml:lang="fi">käännösmalli</comment>
+ <comment xml:lang="fo">týðingarformur</comment>
+ <comment xml:lang="fr">modèle de traduction</comment>
+ <comment xml:lang="ga">teimpléad aistriúcháin</comment>
+ <comment xml:lang="gl">plantilla de tradución</comment>
+ <comment xml:lang="he">תבנית תרגום</comment>
+ <comment xml:lang="hr">Predložak prijevoda</comment>
+ <comment xml:lang="hu">fordítási sablon</comment>
+ <comment xml:lang="ia">Patrono de traduction</comment>
+ <comment xml:lang="id">templat terjemahan</comment>
+ <comment xml:lang="it">Modello di traduzione</comment>
+ <comment xml:lang="ja">翻訳テンプレート</comment>
+ <comment xml:lang="ka">თარგმნის შაბლონი</comment>
+ <comment xml:lang="kk">аудармалар үлгісі</comment>
+ <comment xml:lang="ko">메시지 번역 서식</comment>
+ <comment xml:lang="lt">vertimo šablonas</comment>
+ <comment xml:lang="lv">tulkošanas veidne</comment>
+ <comment xml:lang="nb">mal for oversetting</comment>
+ <comment xml:lang="nl">vertalingssjabloon</comment>
+ <comment xml:lang="nn">omsetjingsmal</comment>
+ <comment xml:lang="oc">modèl de traduccion</comment>
+ <comment xml:lang="pl">Szablon tłumaczenia</comment>
+ <comment xml:lang="pt">modelo de tradução</comment>
+ <comment xml:lang="pt_BR">Modelo de tradução</comment>
+ <comment xml:lang="ro">șablon de traducere</comment>
+ <comment xml:lang="ru">Шаблон переводов</comment>
+ <comment xml:lang="sk">Šablóna prekladu</comment>
+ <comment xml:lang="sl">predloga datoteke prevoda programa</comment>
+ <comment xml:lang="sq">Model përkthimesh</comment>
+ <comment xml:lang="sr">шаблон превода</comment>
+ <comment xml:lang="sv">översättningsmall</comment>
+ <comment xml:lang="tr">çeviri şablonu</comment>
+ <comment xml:lang="uk">шаблон перекладу</comment>
+ <comment xml:lang="vi">mẫu dịch</comment>
+ <comment xml:lang="zh_CN">翻译模板</comment>
+ <comment xml:lang="zh_TW">翻譯模版</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.pot"/>
+ <alias type="text/x-pot"/>
+ <magic priority="50">
+ <match value='#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version:' type="string" offset="0:256"/>
+ </magic>
+ </mime-type>
+ <mime-type type="text/x-gherkin">
+ <comment>feature specification in Gherkin format</comment>
+ <comment xml:lang="ca">especificació de funcionalitat en format Gherkin</comment>
+ <comment xml:lang="cs">specifikace vlastností ve formátu Gherkin</comment>
+ <comment xml:lang="da">funktionspecifikation i Gherkin-format</comment>
+ <comment xml:lang="de">Funktionsspezifikation im Gherkin-Format</comment>
+ <comment xml:lang="en_GB">feature specification in Gherkin format</comment>
+ <comment xml:lang="es">especificación de funcionalidad en formato Gherkin</comment>
+ <comment xml:lang="fr">spécification fonctionnelle au format Gherkin</comment>
+ <comment xml:lang="ga">sonraíocht gnéithe i bhformáid Gherkin</comment>
+ <comment xml:lang="he">פירוט תכונות בתבנית Gherkin</comment>
+ <comment xml:lang="hr">opis značajke u Gherkin formatu</comment>
+ <comment xml:lang="hu">funkcióleírás Gherkin formátumban</comment>
+ <comment xml:lang="id">spesifikasi fitur dalam format Gherkin</comment>
+ <comment xml:lang="it">Specifica di funzionalità in formato Gherkin</comment>
+ <comment xml:lang="kk">Gherkin пішіміндегі функционалды анықтамалар</comment>
+ <comment xml:lang="ko">Gherkin 형식의 기능 명세</comment>
+ <comment xml:lang="pl">Specyfikacja funkcji w formacie Gherkin</comment>
+ <comment xml:lang="pt_BR">Especificação de recurso no formato Gherkin</comment>
+ <comment xml:lang="ru">Функциональные определения в формате Gherkin</comment>
+ <comment xml:lang="sk">Špecifikácia funkcie vo formáte Gherkin</comment>
+ <comment xml:lang="sr">одредба функције у запису Геркина</comment>
+ <comment xml:lang="sv">egenskapsspecifikation i Gherkin-format</comment>
+ <comment xml:lang="tr">Gherkin biçiminde özellik belirtimi</comment>
+ <comment xml:lang="uk">специфікація можливості у форматі Gherkin</comment>
+ <comment xml:lang="zh_CN">Gherkin 格式中的功能规范</comment>
+ <comment xml:lang="zh_TW">Gherkin 格式的特徵規格</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.feature"/>
+ </mime-type>
+ <mime-type type="text/html">
+ <comment>HTML document</comment>
+ <comment xml:lang="ar">مستند HTML</comment>
+ <comment xml:lang="ast">Documentu HTML</comment>
+ <comment xml:lang="be@latin">Dakument HTML</comment>
+ <comment xml:lang="bg">Документ — HTML</comment>
+ <comment xml:lang="ca">document HTML</comment>
+ <comment xml:lang="cs">dokument HTML</comment>
+ <comment xml:lang="da">HTML-dokument</comment>
+ <comment xml:lang="de">HTML-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο HTML</comment>
+ <comment xml:lang="en_GB">HTML document</comment>
+ <comment xml:lang="eo">HTML-dokumento</comment>
+ <comment xml:lang="es">documento HTML</comment>
+ <comment xml:lang="eu">HTML dokumentua</comment>
+ <comment xml:lang="fi">HTML-asiakirja</comment>
+ <comment xml:lang="fo">HTML skjal</comment>
+ <comment xml:lang="fr">document HTML</comment>
+ <comment xml:lang="ga">cáipéis HTML</comment>
+ <comment xml:lang="gl">documento HTML</comment>
+ <comment xml:lang="he">מסמך HTML</comment>
+ <comment xml:lang="hr">HTML dokument</comment>
+ <comment xml:lang="hu">HTML dokumentum</comment>
+ <comment xml:lang="ia">Documento HTML</comment>
+ <comment xml:lang="id">Dokumen HTML</comment>
+ <comment xml:lang="it">Documento HTML</comment>
+ <comment xml:lang="ja">HTML ドキュメント</comment>
+ <comment xml:lang="kk">HTML құжаты</comment>
+ <comment xml:lang="ko">HTML 문서</comment>
+ <comment xml:lang="lt">HTML dokumentas</comment>
+ <comment xml:lang="lv">HTML dokuments</comment>
+ <comment xml:lang="nb">HTML-dokument</comment>
+ <comment xml:lang="nl">HTML-document</comment>
+ <comment xml:lang="nn">HTML-dokument</comment>
+ <comment xml:lang="oc">document HTML</comment>
+ <comment xml:lang="pl">Dokument HTML</comment>
+ <comment xml:lang="pt">documento HTML</comment>
+ <comment xml:lang="pt_BR">Documento HTML</comment>
+ <comment xml:lang="ro">Document HTML</comment>
+ <comment xml:lang="ru">Документ HTML</comment>
+ <comment xml:lang="sk">Dokument HTML</comment>
+ <comment xml:lang="sl">Dokument HTML</comment>
+ <comment xml:lang="sq">Dokument HTML</comment>
+ <comment xml:lang="sr">ХТМЛ документ</comment>
+ <comment xml:lang="sv">HTML-dokument</comment>
+ <comment xml:lang="tr">HTML belgesi</comment>
+ <comment xml:lang="uk">документ HTML</comment>
+ <comment xml:lang="vi">Tài liệu HTML</comment>
+ <comment xml:lang="zh_CN">HTML 文档</comment>
+ <comment xml:lang="zh_TW">HTML 文件</comment>
+ <acronym>HTML</acronym>
+ <expanded-acronym>HyperText Markup Language</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="&lt;!DOCTYPE HTML" type="string" offset="0:256"/>
+ <match value="&lt;!doctype html" type="string" offset="0:256"/>
+ <match value="&lt;HEAD" type="string" offset="0:256"/>
+ <match value="&lt;head" type="string" offset="0:256"/>
+ <match value="&lt;TITLE" type="string" offset="0:256"/>
+ <match value="&lt;title" type="string" offset="0:256"/>
+ <match value="&lt;HTML" type="string" offset="0:256"/>
+ <match value="&lt;html" type="string" offset="0:256"/>
+ <match value="&lt;SCRIPT" type="string" offset="0:256"/>
+ <match value="&lt;script" type="string" offset="0:256"/>
+ <match value="&lt;BODY" type="string" offset="0"/>
+ <match value="&lt;body" type="string" offset="0"/>
+ <match value="&lt;!--" type="string" offset="0"/>
+ <match value="&lt;h1" type="string" offset="0"/>
+ <match value="&lt;H1" type="string" offset="0"/>
+ <match value="&lt;!doctype HTML" type="string" offset="0"/>
+ <match value="&lt;!DOCTYPE html" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.html"/>
+ <glob pattern="*.htm"/>
+ </mime-type>
+ <mime-type type="text/cache-manifest">
+ <comment>Web application cache manifest</comment>
+ <comment xml:lang="ar">قائمة التخزين الموقت لتطبيق الويب</comment>
+ <comment xml:lang="bg">Манифест за кеша на уеб приложение</comment>
+ <comment xml:lang="ca">manifest de memòria cau d'aplicació Web</comment>
+ <comment xml:lang="cs">manifest mezipaměti webové aplikace</comment>
+ <comment xml:lang="da">Manifest for internetprogrammellemlager</comment>
+ <comment xml:lang="de">Webanwendungscache-Manifest</comment>
+ <comment xml:lang="el">Δηλωτικό λανθάνουσας μνήμης εφαρμογής Ιστού</comment>
+ <comment xml:lang="en_GB">Web application cache manifest</comment>
+ <comment xml:lang="es">manifiesto de antememoria de aplicación web</comment>
+ <comment xml:lang="eu">Web aplikazioaren cache-aren agiria</comment>
+ <comment xml:lang="fo">Net nýtsluskipanarkova manifest</comment>
+ <comment xml:lang="fr">manifeste de cache d'application Web</comment>
+ <comment xml:lang="ga">lastliosta taisce d'fheidhmchlár Gréasáin</comment>
+ <comment xml:lang="gl">manifesto de caché de aplicativo web</comment>
+ <comment xml:lang="he">הצהרה של מטמון של תוכנית ברשת</comment>
+ <comment xml:lang="hr">Web aplikacija prikaza predmemorije</comment>
+ <comment xml:lang="hu">Webalkalmazás gyorsítótár-összefoglalója</comment>
+ <comment xml:lang="ia">Manifesto de cache de application web</comment>
+ <comment xml:lang="id">Manifes singgahan aplikasi web</comment>
+ <comment xml:lang="it">Manifesto cache applicazione Web</comment>
+ <comment xml:lang="ja">Web アプリケーションキャッシュ manifest</comment>
+ <comment xml:lang="kk">Веб қолданбасының кэш манифесті</comment>
+ <comment xml:lang="ko">웹 애플리케이션 캐시 정의</comment>
+ <comment xml:lang="lt">Žiniatinklio programos podėlio manifestas</comment>
+ <comment xml:lang="lv">Tīmekļa lietotņu keša manifests</comment>
+ <comment xml:lang="nl">Webapplicatie cache manifest</comment>
+ <comment xml:lang="oc">manifèste d'escondedor d'aplicacion Web</comment>
+ <comment xml:lang="pl">Manifest pamięci podręcznej aplikacji WWW</comment>
+ <comment xml:lang="pt">manifesto de cache de aplicação web</comment>
+ <comment xml:lang="pt_BR">Manifest de cache de aplicação web</comment>
+ <comment xml:lang="ro">Manifest de cache pentru aplicații web</comment>
+ <comment xml:lang="ru">Манифест кэша веб-приложения</comment>
+ <comment xml:lang="sk">Manifest vyrovnávacej pamäte webovej aplikácie</comment>
+ <comment xml:lang="sl">Predpomnilnik spletnega programa</comment>
+ <comment xml:lang="sr">проглас оставе Веб програма</comment>
+ <comment xml:lang="sv">Cachemanifest för webbapplikation</comment>
+ <comment xml:lang="tr">Web uygulama önbelleği bildirimi</comment>
+ <comment xml:lang="uk">маніфест кешу веб-програми</comment>
+ <comment xml:lang="zh_CN">网络应用程序缓存清单</comment>
+ <comment xml:lang="zh_TW">網頁應用程式快取聲明</comment>
+ <sub-class-of type="text/plain"/>
+ <magic>
+ <match value="CACHE MANIFEST" type="string" offset="0">
+ <match value="\x20" type="string" offset="14"/>
+ <match value="\x09" type="string" offset="14"/>
+ <match value="\x0a" type="string" offset="14"/>
+ <match value="\x0d" type="string" offset="14"/>
+ </match>
+ </magic>
+ <glob pattern="*.manifest"/>
+ </mime-type>
+ <mime-type type="text/x-google-video-pointer">
+ <comment>Google Video Pointer</comment>
+ <comment xml:lang="ar">مؤشر فيديو جوجل</comment>
+ <comment xml:lang="ast">Google Video Pointer</comment>
+ <comment xml:lang="be@latin">Pakazalnik Google Video</comment>
+ <comment xml:lang="bg">Документ-указател към видео на Google</comment>
+ <comment xml:lang="ca">apuntador a vídeo de Google</comment>
+ <comment xml:lang="cs">Google Video Pointer</comment>
+ <comment xml:lang="da">Google Video-peger</comment>
+ <comment xml:lang="de">Google Video-Zeiger</comment>
+ <comment xml:lang="el">Google Video Pointer</comment>
+ <comment xml:lang="en_GB">Google Video Pointer</comment>
+ <comment xml:lang="es">lista de reproducción de Google Video (GVP)</comment>
+ <comment xml:lang="eu">Google Video-ren erreprodukzio-zerrenda</comment>
+ <comment xml:lang="fi">Google-video-osoitin</comment>
+ <comment xml:lang="fo">Google Video Pointer</comment>
+ <comment xml:lang="fr">pointeur vidéo Google</comment>
+ <comment xml:lang="ga">pointeoir Google Video</comment>
+ <comment xml:lang="gl">punteiro de vídeo de Google</comment>
+ <comment xml:lang="he">מצביע וידאו של Google</comment>
+ <comment xml:lang="hr">Google Video pretraživač</comment>
+ <comment xml:lang="hu">Google Video Pointer</comment>
+ <comment xml:lang="ia">Punctator Google Video</comment>
+ <comment xml:lang="id">Google Video Pointer</comment>
+ <comment xml:lang="it">Puntatore Google Video</comment>
+ <comment xml:lang="ja">Google ビデオポインター</comment>
+ <comment xml:lang="kk">Google Video Pointer</comment>
+ <comment xml:lang="ko">Google 동영상 포인터</comment>
+ <comment xml:lang="lt">Google Video Pointer</comment>
+ <comment xml:lang="lv">Google Video Pointer</comment>
+ <comment xml:lang="nb">Peker til Google Video</comment>
+ <comment xml:lang="nl">Google-videoverwijzing</comment>
+ <comment xml:lang="nn">Google Video-peikar</comment>
+ <comment xml:lang="oc">puntador vidèo Google</comment>
+ <comment xml:lang="pl">Lista odtwarzania Google Video</comment>
+ <comment xml:lang="pt">Ponteiro Google Video</comment>
+ <comment xml:lang="pt_BR">Ponteiro do Google Vídeo</comment>
+ <comment xml:lang="ro">Indicator Google Video</comment>
+ <comment xml:lang="ru">Google Video Pointer</comment>
+ <comment xml:lang="sk">Google Video Pointer</comment>
+ <comment xml:lang="sl">Kazalec Google Video</comment>
+ <comment xml:lang="sq">Puntues Google Video</comment>
+ <comment xml:lang="sr">Гуглов видео показивач</comment>
+ <comment xml:lang="sv">Google Video-pekare</comment>
+ <comment xml:lang="tr">Google Video İşaretçisi</comment>
+ <comment xml:lang="uk">вказівник відео Google</comment>
+ <comment xml:lang="vi">Con trỏ ảnh động Google</comment>
+ <comment xml:lang="zh_CN">Google 视频指向</comment>
+ <comment xml:lang="zh_TW">Google Video Pointer</comment>
+ <magic priority="50">
+ <match value="#.download.the.free.Google.Video.Player" type="string" offset="0"/>
+ <match value="# download the free Google Video Player" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.gvp"/>
+ <alias type="text/google-video-pointer"/>
+ </mime-type>
+ <mime-type type="text/x-haskell">
+ <comment>Haskell source code</comment>
+ <comment xml:lang="ar">شفرة مصدر Haskell</comment>
+ <comment xml:lang="az">Haskell mənbə kodu</comment>
+ <comment xml:lang="be@latin">Kryničny kod Haskell</comment>
+ <comment xml:lang="bg">Изходен код на Haskell</comment>
+ <comment xml:lang="ca">codi font en Haskell</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Haskell</comment>
+ <comment xml:lang="cy">Ffynhonnell rhaglen Haskell</comment>
+ <comment xml:lang="da">Haskellkildekode</comment>
+ <comment xml:lang="de">Haskell-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Haskell</comment>
+ <comment xml:lang="en_GB">Haskell source code</comment>
+ <comment xml:lang="eo">Haskell-fontkodo</comment>
+ <comment xml:lang="es">código fuente en Haskell</comment>
+ <comment xml:lang="eu">Haskell iturburu-kodea</comment>
+ <comment xml:lang="fi">Haskell-lähdekoodi</comment>
+ <comment xml:lang="fo">Haskell keldukota</comment>
+ <comment xml:lang="fr">code source Haskell</comment>
+ <comment xml:lang="ga">cód foinseach Haskell</comment>
+ <comment xml:lang="gl">código fonte de Haskell</comment>
+ <comment xml:lang="he">קוד מקור של Haskell</comment>
+ <comment xml:lang="hr">Haskell izvorni kôd</comment>
+ <comment xml:lang="hu">Haskell-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Haskell</comment>
+ <comment xml:lang="id">Kode sumber Haskell</comment>
+ <comment xml:lang="it">Codice sorgente Haskell</comment>
+ <comment xml:lang="ja">Haskell ソースコード</comment>
+ <comment xml:lang="kk">Haskell бастапқы коды</comment>
+ <comment xml:lang="ko">Haskell 소스 코드</comment>
+ <comment xml:lang="lt">Haskell pradinis kodas</comment>
+ <comment xml:lang="lv">Haskell pirmkods</comment>
+ <comment xml:lang="ms">Kod sumber Haskell</comment>
+ <comment xml:lang="nb">Haskell-kildekode</comment>
+ <comment xml:lang="nl">Haskell-broncode</comment>
+ <comment xml:lang="nn">Haskell-kjeldekode</comment>
+ <comment xml:lang="oc">còde font Haskell</comment>
+ <comment xml:lang="pl">Kod źródłowy Haskell</comment>
+ <comment xml:lang="pt">código origem Haskell</comment>
+ <comment xml:lang="pt_BR">Código-fonte Haskell</comment>
+ <comment xml:lang="ro">Cod sursă Haskell</comment>
+ <comment xml:lang="ru">Исходный код Haskell</comment>
+ <comment xml:lang="sk">Zdrojový kód Haskell</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Haskell</comment>
+ <comment xml:lang="sq">Kod burues Haskell</comment>
+ <comment xml:lang="sr">Хаскел изворни ко̂д</comment>
+ <comment xml:lang="sv">Haskell-källkod</comment>
+ <comment xml:lang="tr">Haskell kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Haskell</comment>
+ <comment xml:lang="vi">Mã nguồn Haskell</comment>
+ <comment xml:lang="zh_CN">Haskell 源代码</comment>
+ <comment xml:lang="zh_TW">Haskell 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.hs"/>
+ </mime-type>
+ <mime-type type="text/x-idl">
+ <comment>IDL document</comment>
+ <comment xml:lang="ar">مستند IDL</comment>
+ <comment xml:lang="ast">Documentu IDL</comment>
+ <comment xml:lang="az">IDL sənədi</comment>
+ <comment xml:lang="be@latin">Dakument IDL</comment>
+ <comment xml:lang="bg">Документ — IDL</comment>
+ <comment xml:lang="ca">document IDL</comment>
+ <comment xml:lang="cs">dokument IDL</comment>
+ <comment xml:lang="cy">Dogfen IDL</comment>
+ <comment xml:lang="da">IDL-dokument</comment>
+ <comment xml:lang="de">IDL-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο IDL</comment>
+ <comment xml:lang="en_GB">IDL document</comment>
+ <comment xml:lang="eo">IDL-dokumento</comment>
+ <comment xml:lang="es">documento IDL</comment>
+ <comment xml:lang="eu">IDL dokumentua</comment>
+ <comment xml:lang="fi">IDL-asiakirja</comment>
+ <comment xml:lang="fo">IDL skjal</comment>
+ <comment xml:lang="fr">document IDL</comment>
+ <comment xml:lang="ga">cáipéis IDL</comment>
+ <comment xml:lang="gl">documento IDL</comment>
+ <comment xml:lang="he">מסמך IDL</comment>
+ <comment xml:lang="hr">IDL dokument</comment>
+ <comment xml:lang="hu">IDL-dokumentum</comment>
+ <comment xml:lang="ia">Documento IDL</comment>
+ <comment xml:lang="id">Dokumen IDL</comment>
+ <comment xml:lang="it">Documento IDL</comment>
+ <comment xml:lang="ja">IDL ドキュメント</comment>
+ <comment xml:lang="kk">IDL құжаты</comment>
+ <comment xml:lang="ko">IDL 문서</comment>
+ <comment xml:lang="lt">IDL dokumentas</comment>
+ <comment xml:lang="lv">IDL dokuments</comment>
+ <comment xml:lang="ms">Dokumen IDL</comment>
+ <comment xml:lang="nb">IDL-dokument</comment>
+ <comment xml:lang="nl">IDL-document</comment>
+ <comment xml:lang="nn">IDL-dokument</comment>
+ <comment xml:lang="oc">document IDL</comment>
+ <comment xml:lang="pl">Dokument IDL</comment>
+ <comment xml:lang="pt">documento IDL</comment>
+ <comment xml:lang="pt_BR">Documento IDL</comment>
+ <comment xml:lang="ro">Document IDL</comment>
+ <comment xml:lang="ru">Документ IDL</comment>
+ <comment xml:lang="sk">Dokument IDL</comment>
+ <comment xml:lang="sl">Dokument IDL</comment>
+ <comment xml:lang="sq">Dokument IDL</comment>
+ <comment xml:lang="sr">ИДЛ документ</comment>
+ <comment xml:lang="sv">IDL-dokument</comment>
+ <comment xml:lang="tr">IDL belgesi</comment>
+ <comment xml:lang="uk">документ IDL</comment>
+ <comment xml:lang="vi">Tài liệu IDL</comment>
+ <comment xml:lang="zh_CN">IDL 文档</comment>
+ <comment xml:lang="zh_TW">IDL 文件</comment>
+ <acronym>IDL</acronym>
+ <expanded-acronym>Interface Definition Language</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.idl"/>
+ </mime-type>
+ <mime-type type="text/x-install">
+ <comment>installation instructions</comment>
+ <comment xml:lang="ar">تعليمات التثبيت</comment>
+ <comment xml:lang="ast">instrucciones d'instalación</comment>
+ <comment xml:lang="be@latin">instrukcyja dla instalavańnia</comment>
+ <comment xml:lang="bg">Инструкции за инсталация</comment>
+ <comment xml:lang="ca">instruccions d'instal·lació</comment>
+ <comment xml:lang="cs">návod k instalaci</comment>
+ <comment xml:lang="da">installationsinstruktioner</comment>
+ <comment xml:lang="de">Installationsanleitung</comment>
+ <comment xml:lang="el">Οδηγίες εγκατάστασης</comment>
+ <comment xml:lang="en_GB">installation instructions</comment>
+ <comment xml:lang="es">instrucciones de instalación</comment>
+ <comment xml:lang="eu">instalazioaren instrukzioak</comment>
+ <comment xml:lang="fi">asennusohjeet</comment>
+ <comment xml:lang="fo">innleggingar vegleiðing</comment>
+ <comment xml:lang="fr">instructions d'installation</comment>
+ <comment xml:lang="ga">treoracha suiteála</comment>
+ <comment xml:lang="gl">instrucións de instalación</comment>
+ <comment xml:lang="he">הוראות התקנה</comment>
+ <comment xml:lang="hr">Upute za instalaciju</comment>
+ <comment xml:lang="hu">telepítési utasítások</comment>
+ <comment xml:lang="ia">Instructiones de installation</comment>
+ <comment xml:lang="id">instruksi instalasi</comment>
+ <comment xml:lang="it">Istruzioni di installazione</comment>
+ <comment xml:lang="ja">ソフトウェアインストール説明</comment>
+ <comment xml:lang="kk">бағдарламаны орнату нұсқаулары</comment>
+ <comment xml:lang="ko">설치 방법</comment>
+ <comment xml:lang="lt">diegimo instrukcijos</comment>
+ <comment xml:lang="lv">instalācijas instrukcijas</comment>
+ <comment xml:lang="nb">installationsinstruksjoner</comment>
+ <comment xml:lang="nl">installatie-instructies</comment>
+ <comment xml:lang="nn">installasjonsinstruksjonar</comment>
+ <comment xml:lang="oc">instructions d'installacion</comment>
+ <comment xml:lang="pl">Instrukcje instalacji</comment>
+ <comment xml:lang="pt">instruções de instalação</comment>
+ <comment xml:lang="pt_BR">Instruções de instalação</comment>
+ <comment xml:lang="ro">instrucțiuni de instalare</comment>
+ <comment xml:lang="ru">Инструкции по установке</comment>
+ <comment xml:lang="sk">Návod na inštaláciu</comment>
+ <comment xml:lang="sl">navodila namestitve</comment>
+ <comment xml:lang="sq">Udhëzime instalimi</comment>
+ <comment xml:lang="sr">упутства инсталације</comment>
+ <comment xml:lang="sv">installationsinstruktioner</comment>
+ <comment xml:lang="tr">kurulum yönergeleri</comment>
+ <comment xml:lang="uk">інструкції з встановлення</comment>
+ <comment xml:lang="vi">hướng dẫn cài đặt</comment>
+ <comment xml:lang="zh_CN">软件安装指南</comment>
+ <comment xml:lang="zh_TW">安裝指引</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="INSTALL"/>
+ </mime-type>
+ <mime-type type="text/x-java">
+ <comment>Java source code</comment>
+ <comment xml:lang="ar">شفرة مصدر Java</comment>
+ <comment xml:lang="be@latin">Kryničny kod Java</comment>
+ <comment xml:lang="bg">Изходен код на Java</comment>
+ <comment xml:lang="ca">codi font en Java</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Java</comment>
+ <comment xml:lang="da">Javakildekode</comment>
+ <comment xml:lang="de">Java-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Java</comment>
+ <comment xml:lang="en_GB">Java source code</comment>
+ <comment xml:lang="eo">Java-fontkodo</comment>
+ <comment xml:lang="es">código fuente en Java</comment>
+ <comment xml:lang="eu">Java iturburu-kodea</comment>
+ <comment xml:lang="fi">Java-lähdekoodi</comment>
+ <comment xml:lang="fo">Java keldukota</comment>
+ <comment xml:lang="fr">code source Java</comment>
+ <comment xml:lang="ga">cód foinseach Java</comment>
+ <comment xml:lang="gl">código fonte de Java</comment>
+ <comment xml:lang="he">קוד מקור ב־Java</comment>
+ <comment xml:lang="hr">Java izvorni kôd</comment>
+ <comment xml:lang="hu">Java-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Java</comment>
+ <comment xml:lang="id">Kode sumber Java</comment>
+ <comment xml:lang="it">Codice sorgente Java</comment>
+ <comment xml:lang="ja">Java ソースコード</comment>
+ <comment xml:lang="kk">Java бастапқы коды</comment>
+ <comment xml:lang="ko">Java 소스 코드</comment>
+ <comment xml:lang="lt">Java pradinis kodas</comment>
+ <comment xml:lang="lv">Java pirmkods</comment>
+ <comment xml:lang="ms">Kod sumber Java</comment>
+ <comment xml:lang="nb">Java-kildekode</comment>
+ <comment xml:lang="nl">Java-broncode</comment>
+ <comment xml:lang="nn">Java-kjeldekode</comment>
+ <comment xml:lang="oc">còde font Java</comment>
+ <comment xml:lang="pl">Kod źródłowy Java</comment>
+ <comment xml:lang="pt">código origem Java</comment>
+ <comment xml:lang="pt_BR">Código-fonte Java</comment>
+ <comment xml:lang="ro">Cod sursă Java</comment>
+ <comment xml:lang="ru">Исходный код Java</comment>
+ <comment xml:lang="sk">Zdrojový kód Java</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Java</comment>
+ <comment xml:lang="sq">Kod burues Java</comment>
+ <comment xml:lang="sr">Јава изворни ко̂д</comment>
+ <comment xml:lang="sv">Java-källkod</comment>
+ <comment xml:lang="tr">Java kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Java</comment>
+ <comment xml:lang="vi">Mã nguồn Java</comment>
+ <comment xml:lang="zh_CN">Java 源代码</comment>
+ <comment xml:lang="zh_TW">Java 源碼</comment>
+ <sub-class-of type="text/x-csrc"/>
+ <glob pattern="*.java"/>
+ </mime-type>
+ <mime-type type="text/x-ldif">
+ <comment>LDIF address book</comment>
+ <comment xml:lang="ar">دفتر عناوين LDIF</comment>
+ <comment xml:lang="be@latin">Adrasnaja kniha LDIF</comment>
+ <comment xml:lang="bg">Адресна книга — LDIF</comment>
+ <comment xml:lang="ca">llibreta d'adreces LDIF</comment>
+ <comment xml:lang="cs">adresář LDIF</comment>
+ <comment xml:lang="da">LDIF-adressebog</comment>
+ <comment xml:lang="de">LDIF-Adressbuch</comment>
+ <comment xml:lang="el">Βιβλίο διευθύνσεων LDIF</comment>
+ <comment xml:lang="en_GB">LDIF address book</comment>
+ <comment xml:lang="eo">LDIF-adresaro</comment>
+ <comment xml:lang="es">libreta de direcciones LDIF</comment>
+ <comment xml:lang="eu">LDIF helbide-liburua</comment>
+ <comment xml:lang="fi">LDIF-osoitekirja</comment>
+ <comment xml:lang="fo">LDIF adressubók</comment>
+ <comment xml:lang="fr">carnet d'adresses LDIF</comment>
+ <comment xml:lang="ga">leabhar seoltaí LDIF</comment>
+ <comment xml:lang="gl">lista de enderezos LDIF</comment>
+ <comment xml:lang="he">ספר כתובות של LDIF</comment>
+ <comment xml:lang="hr">LDIF adresar</comment>
+ <comment xml:lang="hu">LDIF címjegyzék</comment>
+ <comment xml:lang="ia">Adressario LDIF</comment>
+ <comment xml:lang="id">Buku alamat LDIF</comment>
+ <comment xml:lang="it">Rubrica LDIF</comment>
+ <comment xml:lang="ja">LDIF アドレス帳</comment>
+ <comment xml:lang="kk">LDIF адрестер кітабы</comment>
+ <comment xml:lang="ko">LDIF 주소록</comment>
+ <comment xml:lang="lt">LDIF adresų knygelė</comment>
+ <comment xml:lang="lv">LDIF adrešu grāmata</comment>
+ <comment xml:lang="nb">LDIF-adressebok</comment>
+ <comment xml:lang="nl">LDIF-adresboek</comment>
+ <comment xml:lang="nn">LDIF-adressebok</comment>
+ <comment xml:lang="oc">quasernet d'adreças LDIF</comment>
+ <comment xml:lang="pl">Książka adresowa LDIF</comment>
+ <comment xml:lang="pt">livro de endereços LDIF</comment>
+ <comment xml:lang="pt_BR">Livro de endereços LDIF</comment>
+ <comment xml:lang="ro">Agendă LDIF</comment>
+ <comment xml:lang="ru">Адресная книга LDIF</comment>
+ <comment xml:lang="sk">Adresár LDIF</comment>
+ <comment xml:lang="sl">Datoteka imenika naslovov LDIF</comment>
+ <comment xml:lang="sq">Rubrikë LDIF</comment>
+ <comment xml:lang="sr">ЛДИФ адресар</comment>
+ <comment xml:lang="sv">LDIF-adressbok</comment>
+ <comment xml:lang="tr">LDIF adres defteri</comment>
+ <comment xml:lang="uk">адресна книга LDIF</comment>
+ <comment xml:lang="vi">Sổ địa chỉ LDIF</comment>
+ <comment xml:lang="zh_CN">LDIF 地址簿</comment>
+ <comment xml:lang="zh_TW">LDIF 通訊錄</comment>
+ <acronym>LDIF</acronym>
+ <expanded-acronym>LDAP Data Interchange Format</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="dn: cn=" type="string" offset="0"/>
+ <match value="dn: mail=" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.ldif"/>
+ </mime-type>
+ <mime-type type="text/x-lilypond">
+ <comment>Lilypond music sheet</comment>
+ <comment xml:lang="ar">صفحة موسيقى Lilypond</comment>
+ <comment xml:lang="be@latin">Muzyčny arkuš Lilypond</comment>
+ <comment xml:lang="bg">Нотация на Lilypond</comment>
+ <comment xml:lang="ca">full de música Lilypond</comment>
+ <comment xml:lang="cs">notový papír Lilypond</comment>
+ <comment xml:lang="da">Lilypondmusikkort</comment>
+ <comment xml:lang="de">Lilypond-Notenblatt</comment>
+ <comment xml:lang="el">Παρτιτούρα Lilypond</comment>
+ <comment xml:lang="en_GB">Lilypond music sheet</comment>
+ <comment xml:lang="es">partitura de LilyPond</comment>
+ <comment xml:lang="eu">Lilypond musika-orria</comment>
+ <comment xml:lang="fi">Lilypond-nuotit</comment>
+ <comment xml:lang="fo">Lilypond tónleika ark</comment>
+ <comment xml:lang="fr">partition musicale Lilypond</comment>
+ <comment xml:lang="ga">bileog cheoil Lilypond</comment>
+ <comment xml:lang="gl">folla de música de Lilypond</comment>
+ <comment xml:lang="he">דף מוזיקה של Lilypond</comment>
+ <comment xml:lang="hr">Lilypond glazbena ljestvica</comment>
+ <comment xml:lang="hu">Lilypond kotta</comment>
+ <comment xml:lang="ia">Partition musical Lilypond</comment>
+ <comment xml:lang="id">Lembar musik Lilypond</comment>
+ <comment xml:lang="it">Partitura Lilypond</comment>
+ <comment xml:lang="ja">Lilypond 楽譜データ</comment>
+ <comment xml:lang="kk">Lilypond музыка тізімі</comment>
+ <comment xml:lang="ko">Lilypond 악보</comment>
+ <comment xml:lang="lt">Lilypond muzikos lapas</comment>
+ <comment xml:lang="lv">Lilypond mūzikas lapa</comment>
+ <comment xml:lang="nl">Lilypond-muziekblad</comment>
+ <comment xml:lang="nn">Lilypond noteark</comment>
+ <comment xml:lang="oc">particion musicala Lilypond</comment>
+ <comment xml:lang="pl">Plik partytury Lilypond</comment>
+ <comment xml:lang="pt">folha de música Lilypond</comment>
+ <comment xml:lang="pt_BR">Partitura do Lilypond</comment>
+ <comment xml:lang="ro">Fișă muzică Lilypond</comment>
+ <comment xml:lang="ru">Список музыки Lilypond</comment>
+ <comment xml:lang="sk">Notový papier Lilypond</comment>
+ <comment xml:lang="sl">Glasbena predloga Lilypond</comment>
+ <comment xml:lang="sq">Partiturë Lilypond</comment>
+ <comment xml:lang="sr">Лилипонд музички лист</comment>
+ <comment xml:lang="sv">Lilypond-notblad</comment>
+ <comment xml:lang="tr">Lilypond müzik sayfası</comment>
+ <comment xml:lang="uk">нотний запис Lilypond</comment>
+ <comment xml:lang="vi">Bản nhạc Lilypond</comment>
+ <comment xml:lang="zh_CN">Lilypond 乐谱</comment>
+ <comment xml:lang="zh_TW">Lilypond 樂譜</comment>
+ <glob pattern="*.ly"/>
+ <sub-class-of type="text/plain"/>
+ </mime-type>
+ <mime-type type="text/x-literate-haskell">
+ <comment>LHS source code</comment>
+ <comment xml:lang="ar">شفرة مصدر LHS</comment>
+ <comment xml:lang="be@latin">Kryničny kod LHS</comment>
+ <comment xml:lang="bg">Изходен код на LHS</comment>
+ <comment xml:lang="ca">codi font en LHS</comment>
+ <comment xml:lang="cs">zdrojový kód LHS</comment>
+ <comment xml:lang="da">LHS-kildekode</comment>
+ <comment xml:lang="de">LHS-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας LHS</comment>
+ <comment xml:lang="en_GB">LHS source code</comment>
+ <comment xml:lang="eo">LHS-fontkodo</comment>
+ <comment xml:lang="es">código fuente en LHS</comment>
+ <comment xml:lang="eu">LHS iturburu-kodea</comment>
+ <comment xml:lang="fi">LHS-lähdekoodi</comment>
+ <comment xml:lang="fo">LHS keld</comment>
+ <comment xml:lang="fr">code source LHS</comment>
+ <comment xml:lang="ga">cód foinseach LHS</comment>
+ <comment xml:lang="gl">código fonte en LHS</comment>
+ <comment xml:lang="he">קוד מקור של LHS</comment>
+ <comment xml:lang="hr">LHS izvorni kôd</comment>
+ <comment xml:lang="hu">LHS forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte LHS</comment>
+ <comment xml:lang="id">Kode sumber LHS</comment>
+ <comment xml:lang="it">Codice sorgente LHS</comment>
+ <comment xml:lang="ja">LHS ソースコード</comment>
+ <comment xml:lang="kk">LHS бастапқы коды</comment>
+ <comment xml:lang="ko">LHS 소스 코드</comment>
+ <comment xml:lang="lt">LHS pradinis kodas</comment>
+ <comment xml:lang="lv">LHS pirmkods</comment>
+ <comment xml:lang="nb">LHS-kildekode</comment>
+ <comment xml:lang="nl">LHS-broncode</comment>
+ <comment xml:lang="nn">LHS-kjeldekode</comment>
+ <comment xml:lang="oc">còde font LHS</comment>
+ <comment xml:lang="pl">Kod źródłowy LHS</comment>
+ <comment xml:lang="pt">código origem LHS</comment>
+ <comment xml:lang="pt_BR">Código-fonte LHS</comment>
+ <comment xml:lang="ro">Cod sursă LHS</comment>
+ <comment xml:lang="ru">Исходный код LHS</comment>
+ <comment xml:lang="sk">Zdrojový kód LHS</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode LHS</comment>
+ <comment xml:lang="sq">Kod burues LHS</comment>
+ <comment xml:lang="sr">ЛХС изворни ко̂д</comment>
+ <comment xml:lang="sv">LHS-källkod</comment>
+ <comment xml:lang="tr">LHS kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код LHS</comment>
+ <comment xml:lang="vi">Mã nguồn LHS</comment>
+ <comment xml:lang="zh_CN">LHS 源代码</comment>
+ <comment xml:lang="zh_TW">LHS 源碼</comment>
+ <acronym>LHS</acronym>
+ <expanded-acronym>Literate Haskell source code</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.lhs"/>
+ </mime-type>
+ <mime-type type="text/x-log">
+ <comment>application log</comment>
+ <comment xml:lang="ar">سجل التطبيق</comment>
+ <comment xml:lang="ast">rexistru d'aplicación</comment>
+ <comment xml:lang="be@latin">časopis aplikacyi</comment>
+ <comment xml:lang="bg">Файл-дневник на приложение</comment>
+ <comment xml:lang="ca">registre d'aplicació</comment>
+ <comment xml:lang="cs">záznam aplikace</comment>
+ <comment xml:lang="da">programlog</comment>
+ <comment xml:lang="de">Anwendungsprotokoll</comment>
+ <comment xml:lang="el">Καταγραφή εφαρμογή</comment>
+ <comment xml:lang="en_GB">application log</comment>
+ <comment xml:lang="eo">protokolo de aplikaĵo</comment>
+ <comment xml:lang="es">registro de aplicación</comment>
+ <comment xml:lang="eu">aplikazio egunkaria</comment>
+ <comment xml:lang="fi">sovelluksen lokitiedosto</comment>
+ <comment xml:lang="fo">nýtsluskipan logg</comment>
+ <comment xml:lang="fr">journal d'application</comment>
+ <comment xml:lang="ga">logchomhad feidhmchláir</comment>
+ <comment xml:lang="gl">rexistro de aplicativo</comment>
+ <comment xml:lang="he">יומן יישום</comment>
+ <comment xml:lang="hr">Zapis aplikacije</comment>
+ <comment xml:lang="hu">alkalmazás naplója</comment>
+ <comment xml:lang="ia">Registro de application</comment>
+ <comment xml:lang="id">log aplikasi</comment>
+ <comment xml:lang="it">Registro applicazione</comment>
+ <comment xml:lang="ja">アプリケーションログ</comment>
+ <comment xml:lang="kk">мәлімдемелер журналы</comment>
+ <comment xml:lang="ko">프로그램 기록</comment>
+ <comment xml:lang="lt">programos žurnalas</comment>
+ <comment xml:lang="lv">lietotnes žurnāls</comment>
+ <comment xml:lang="ms">Log aplikasi</comment>
+ <comment xml:lang="nb">applikasjonslogg</comment>
+ <comment xml:lang="nl">programma-logbestand</comment>
+ <comment xml:lang="nn">programlogg</comment>
+ <comment xml:lang="oc">jornal d'aplicacion</comment>
+ <comment xml:lang="pl">Dziennik programu</comment>
+ <comment xml:lang="pt">diário de aplicação</comment>
+ <comment xml:lang="pt_BR">Registro de aplicativo</comment>
+ <comment xml:lang="ro">înregistrare aplicație</comment>
+ <comment xml:lang="ru">Журнал сообщений</comment>
+ <comment xml:lang="sk">Záznam aplikácie</comment>
+ <comment xml:lang="sl">dnevnik programa</comment>
+ <comment xml:lang="sq">log i mesazheve të programit</comment>
+ <comment xml:lang="sr">дневник програма</comment>
+ <comment xml:lang="sv">programlogg</comment>
+ <comment xml:lang="tr">uygulama günlüğü</comment>
+ <comment xml:lang="uk">журнал програми</comment>
+ <comment xml:lang="vi">bản ghi ứng dụng</comment>
+ <comment xml:lang="zh_CN">应用程序日志</comment>
+ <comment xml:lang="zh_TW">程式紀錄檔</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.log"/>
+ </mime-type>
+ <mime-type type="text/x-makefile">
+ <comment>Makefile</comment>
+ <comment xml:lang="ar">ملف Makefile</comment>
+ <comment xml:lang="az">İnşa faylı</comment>
+ <comment xml:lang="be@latin">Makefile</comment>
+ <comment xml:lang="bg">Файл — make</comment>
+ <comment xml:lang="ca">Makefile</comment>
+ <comment xml:lang="cs">Makefile</comment>
+ <comment xml:lang="cy">Ffeil "make"</comment>
+ <comment xml:lang="da">Bygningsfil</comment>
+ <comment xml:lang="de">Makefile</comment>
+ <comment xml:lang="el">Makefile</comment>
+ <comment xml:lang="en_GB">Makefile</comment>
+ <comment xml:lang="eo">Muntodosiero</comment>
+ <comment xml:lang="es">Makefile</comment>
+ <comment xml:lang="eu">Makefile</comment>
+ <comment xml:lang="fi">Makefile</comment>
+ <comment xml:lang="fo">Makefile</comment>
+ <comment xml:lang="fr">makefile</comment>
+ <comment xml:lang="ga">Makefile</comment>
+ <comment xml:lang="gl">Makefile</comment>
+ <comment xml:lang="he">Makefile</comment>
+ <comment xml:lang="hr">Makefile</comment>
+ <comment xml:lang="hu">Makefile</comment>
+ <comment xml:lang="ia">Makefile</comment>
+ <comment xml:lang="id">Makefile</comment>
+ <comment xml:lang="it">Makefile</comment>
+ <comment xml:lang="ja">Makefile</comment>
+ <comment xml:lang="ka">Makefile</comment>
+ <comment xml:lang="kk">Makefile (жинау файлы)</comment>
+ <comment xml:lang="ko">Makefile</comment>
+ <comment xml:lang="lt">Makefile</comment>
+ <comment xml:lang="lv">Makefile</comment>
+ <comment xml:lang="ms">Makefile</comment>
+ <comment xml:lang="nb">Makefile</comment>
+ <comment xml:lang="nl">Makefile</comment>
+ <comment xml:lang="nn">Makefile</comment>
+ <comment xml:lang="oc">makefile</comment>
+ <comment xml:lang="pl">Plik make</comment>
+ <comment xml:lang="pt">Makefile</comment>
+ <comment xml:lang="pt_BR">Makefile (arquivo do make)</comment>
+ <comment xml:lang="ro">Makefile</comment>
+ <comment xml:lang="ru">Файл Makefile</comment>
+ <comment xml:lang="sk">Makefile</comment>
+ <comment xml:lang="sl">Datoteka Makefile</comment>
+ <comment xml:lang="sq">Makefile</comment>
+ <comment xml:lang="sr">датотека стварања</comment>
+ <comment xml:lang="sv">Makefil</comment>
+ <comment xml:lang="tr">Makefile</comment>
+ <comment xml:lang="uk">файл проекту make</comment>
+ <comment xml:lang="vi">Tập tin tạo ứng dụng (Makefile)</comment>
+ <comment xml:lang="zh_CN">Makefile</comment>
+ <comment xml:lang="zh_TW">Makefile</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="makefile"/>
+ <glob pattern="GNUmakefile"/>
+ <glob pattern="*.mk"/>
+ <glob pattern="*.mak"/>
+ <glob weight="10" pattern="Makefile.*"/>
+ <magic priority="50">
+ <match value="#!/usr/bin/make" type="string" offset="0"/>
+ <match value="#! /usr/bin/make" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="text/markdown">
+ <comment>Markdown document</comment>
+ <comment xml:lang="ast">Documentu Markdown</comment>
+ <comment xml:lang="bg">Документ — Markdown</comment>
+ <comment xml:lang="ca">document Markdown</comment>
+ <comment xml:lang="cs">dokument Markdown</comment>
+ <comment xml:lang="da">Markdown-dokument</comment>
+ <comment xml:lang="de">Markdown-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Markdown</comment>
+ <comment xml:lang="en_GB">Markdown document</comment>
+ <comment xml:lang="es">documento Markdown</comment>
+ <comment xml:lang="eu">Markdown dokumentua</comment>
+ <comment xml:lang="fi">Markdown-asiakirja</comment>
+ <comment xml:lang="fr">document Markdown</comment>
+ <comment xml:lang="ga">cáipéis Markdown</comment>
+ <comment xml:lang="gl">documento de Markdown</comment>
+ <comment xml:lang="he">מסמך Markdown</comment>
+ <comment xml:lang="hr">Markdown dokument</comment>
+ <comment xml:lang="hu">Markdown dokumentum</comment>
+ <comment xml:lang="ia">Documento Markdown</comment>
+ <comment xml:lang="id">Dokumen markdown</comment>
+ <comment xml:lang="it">Documento Markdown</comment>
+ <comment xml:lang="ja">Markdown </comment>
+ <comment xml:lang="kk">Markdown құжаты</comment>
+ <comment xml:lang="ko">마크다운 문서</comment>
+ <comment xml:lang="lv">Markdown dokuments</comment>
+ <comment xml:lang="nl">Markdown document</comment>
+ <comment xml:lang="oc">document Markdown</comment>
+ <comment xml:lang="pl">Dokument Markdown</comment>
+ <comment xml:lang="pt">documento Markdown</comment>
+ <comment xml:lang="pt_BR">Documento Markdown</comment>
+ <comment xml:lang="ru">Документ Markdown</comment>
+ <comment xml:lang="sk">Dokument Markdown</comment>
+ <comment xml:lang="sl">Dokument Markdown</comment>
+ <comment xml:lang="sr">Маркдаун документ</comment>
+ <comment xml:lang="sv">Markdown-dokument</comment>
+ <comment xml:lang="tr">Markdown belgesi</comment>
+ <comment xml:lang="uk">документ Markdown</comment>
+ <comment xml:lang="zh_CN">Markdown 文档</comment>
+ <comment xml:lang="zh_TW">Markdown 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.md"/>
+ <glob pattern="*.mkd"/>
+ <glob pattern="*.markdown"/>
+ <alias type="text/x-markdown"/>
+ </mime-type>
+ <mime-type type="text/x-moc">
+ <comment>Qt MOC file</comment>
+ <comment xml:lang="ar">ملف Qt MOC</comment>
+ <comment xml:lang="be@latin">Fajł Qt MOC</comment>
+ <comment xml:lang="bg">Файл — Qt MOC</comment>
+ <comment xml:lang="ca">fitxer MOC de Qt</comment>
+ <comment xml:lang="cs">soubor Qt MOC</comment>
+ <comment xml:lang="da">Qt MOC-fil</comment>
+ <comment xml:lang="de">Qt-MOC-Datei</comment>
+ <comment xml:lang="el">Αρχείο Qt MOC</comment>
+ <comment xml:lang="en_GB">Qt MOC file</comment>
+ <comment xml:lang="es">archivo MOC Qt</comment>
+ <comment xml:lang="eu">Qt MOC fitxategia</comment>
+ <comment xml:lang="fi">Qt MOC -tiedosto</comment>
+ <comment xml:lang="fo">Qt MOC fíla</comment>
+ <comment xml:lang="fr">fichier Qt MOC</comment>
+ <comment xml:lang="ga">comhad MOC Qt</comment>
+ <comment xml:lang="gl">ficheiro MOC Qt</comment>
+ <comment xml:lang="he">קובץ Qt MOC</comment>
+ <comment xml:lang="hr">Qt MOC datoteka</comment>
+ <comment xml:lang="hu">Qt MOC fájl</comment>
+ <comment xml:lang="ia">File Qt MOC</comment>
+ <comment xml:lang="id">Berkas Qt MOC</comment>
+ <comment xml:lang="it">File MOC Qt</comment>
+ <comment xml:lang="ja">Qt MOC ファイル</comment>
+ <comment xml:lang="kk">Qt MOC файлы</comment>
+ <comment xml:lang="ko">Qt MOC 파일</comment>
+ <comment xml:lang="lt">Qt MOC failas</comment>
+ <comment xml:lang="lv">Qt MOC datne</comment>
+ <comment xml:lang="nb">Qt MOC-fil</comment>
+ <comment xml:lang="nl">Qt MOC-bestand</comment>
+ <comment xml:lang="nn">Qt MOC-fil</comment>
+ <comment xml:lang="oc">fichièr Qt MOC</comment>
+ <comment xml:lang="pl">Plik Qt MOC</comment>
+ <comment xml:lang="pt">ficheiro Qt MOC</comment>
+ <comment xml:lang="pt_BR">Arquivo Qt MOC</comment>
+ <comment xml:lang="ro">Fișier Qt MOC</comment>
+ <comment xml:lang="ru">Файл Qt MOC</comment>
+ <comment xml:lang="sk">Súbor Qt MOC</comment>
+ <comment xml:lang="sl">Datoteka Qt MOC</comment>
+ <comment xml:lang="sq">File Qt MOC</comment>
+ <comment xml:lang="sr">Кут МОЦ датотека</comment>
+ <comment xml:lang="sv">Qt MOC-fil</comment>
+ <comment xml:lang="tr">Qt MOC dosyası</comment>
+ <comment xml:lang="uk">файл-метаоб'єкт Qt</comment>
+ <comment xml:lang="vi">Tập tin MOC của Qt</comment>
+ <comment xml:lang="zh_CN">Qt 元对象编译器文件</comment>
+ <comment xml:lang="zh_TW">Qt MOC 檔</comment>
+ <acronym>Qt MOC</acronym>
+ <expanded-acronym>Qt Meta Object Compiler</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.moc"/>
+ </mime-type>
+ <mime-type type="text/x-ms-regedit">
+ <comment>Windows Registry extract</comment>
+ <comment xml:lang="ar">استخراج مسجل ويندوز</comment>
+ <comment xml:lang="be@latin">Element rehistru Windows</comment>
+ <comment xml:lang="bg">Извадка от регистъра на Windows</comment>
+ <comment xml:lang="ca">extracte de Windows Registry</comment>
+ <comment xml:lang="cs">výtah registru Windows</comment>
+ <comment xml:lang="da">Windows Registy-udtrækning</comment>
+ <comment xml:lang="de">Windows-Registry-Auszug</comment>
+ <comment xml:lang="el">Αποσυμπίεση Windows Registry</comment>
+ <comment xml:lang="en_GB">Windows Registry extract</comment>
+ <comment xml:lang="es">extracto del registro de Windows</comment>
+ <comment xml:lang="eu">Windows-eko erregistro erauzlea</comment>
+ <comment xml:lang="fi">Windows-rekisteritietue</comment>
+ <comment xml:lang="fo">Windows Registry úrdráttur</comment>
+ <comment xml:lang="fr">extrait de registre Windows</comment>
+ <comment xml:lang="ga">sliocht as Clárlann Windows</comment>
+ <comment xml:lang="gl">Extracto do rexistro de Windows</comment>
+ <comment xml:lang="he">קובץ רשומות מערכת של Windows</comment>
+ <comment xml:lang="hr">Izdvojeni Windows registar</comment>
+ <comment xml:lang="hu">Windows Registry kivonat</comment>
+ <comment xml:lang="ia">Extracto de registro de systema Windows</comment>
+ <comment xml:lang="id">Ekstrak Windows Registry</comment>
+ <comment xml:lang="it">Estratto Windows Registry</comment>
+ <comment xml:lang="ja">WIndows レジストリ抽出ファイル</comment>
+ <comment xml:lang="kk">Windows Registry бөлігі</comment>
+ <comment xml:lang="ko">Windows 레지스트리 파일</comment>
+ <comment xml:lang="lt">Windows registro ištrauka</comment>
+ <comment xml:lang="lv">Windows Registry izvilkums</comment>
+ <comment xml:lang="nb">Utdrag av Windows Registry</comment>
+ <comment xml:lang="nl">Windows Registry-extract</comment>
+ <comment xml:lang="nn">Windows Registry-utdrag</comment>
+ <comment xml:lang="oc">extrait de registre Windows</comment>
+ <comment xml:lang="pl">Wycinek rejestru Windows</comment>
+ <comment xml:lang="pt">extrato do registo do Windows</comment>
+ <comment xml:lang="pt_BR">Extrator de registro do Windows</comment>
+ <comment xml:lang="ro">Extras al registrului Windows</comment>
+ <comment xml:lang="ru">Фрагмент Windows Registry</comment>
+ <comment xml:lang="sk">Časť registrov Windows</comment>
+ <comment xml:lang="sl">izvleček vpisnika Windows</comment>
+ <comment xml:lang="sq">Pjesë Windows Registry</comment>
+ <comment xml:lang="sr">исцедак Виндоузовог регистра</comment>
+ <comment xml:lang="sv">Windows Registry-utdrag</comment>
+ <comment xml:lang="tr">Windows Kayıt Defteri özü</comment>
+ <comment xml:lang="uk">частина реєстру Windows</comment>
+ <comment xml:lang="vi">Bản trích Registry Windows</comment>
+ <comment xml:lang="zh_CN">Windows 注册表提取</comment>
+ <comment xml:lang="zh_TW">Windows Registry 抽出</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="REGEDIT" type="string" offset="0"/>
+ <match value="Windows Registry Editor Version 5.00" type="string" offset="0"/>
+ <match value="\xff\xfeW\x00i\x00n\x00d\x00o\x00w\x00s\x00 \x00R\x00e\x00g\x00i\x00s\x00t\x00r\x00y\x00 \x00E\x00d\x00i\x00t\x00o\x00r\x00" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.reg"/>
+ </mime-type>
+ <mime-type type="text/x-mof">
+ <comment>Managed Object Format</comment>
+ <comment xml:lang="ar">صيغة كائن مدار</comment>
+ <comment xml:lang="be@latin">Farmat Managed Object</comment>
+ <comment xml:lang="bg">Управлявани обекти — MOF</comment>
+ <comment xml:lang="ca">format d'objecte gestionat</comment>
+ <comment xml:lang="cs">Managed Object Format</comment>
+ <comment xml:lang="da">Håndteret objektformat</comment>
+ <comment xml:lang="de">Managed Object Format</comment>
+ <comment xml:lang="el">Μορφή διαχειριζόμενου αντικειμένου</comment>
+ <comment xml:lang="en_GB">Managed Object Format</comment>
+ <comment xml:lang="es">formato de objeto gestionado</comment>
+ <comment xml:lang="eu">Kudeatutako objektu formatua</comment>
+ <comment xml:lang="fi">Managed Object Format</comment>
+ <comment xml:lang="fr">format Managed Object</comment>
+ <comment xml:lang="ga">formáid réada bainistithe</comment>
+ <comment xml:lang="gl">formato de obxecto xestionado</comment>
+ <comment xml:lang="he">תבנית פריט מנוהל</comment>
+ <comment xml:lang="hr">Managed Object Format</comment>
+ <comment xml:lang="hu">Felügyelt objektum (MO) formátum</comment>
+ <comment xml:lang="ia">File in formato Managed Object</comment>
+ <comment xml:lang="id">Managed Object Format</comment>
+ <comment xml:lang="it">Managed Object Format</comment>
+ <comment xml:lang="ja">管理オブジェクトフォーマット</comment>
+ <comment xml:lang="kk">Басқарылатын объект пішімі</comment>
+ <comment xml:lang="ko">관리되는 객체 형식</comment>
+ <comment xml:lang="lt">Sutvarkytų objektų formatas</comment>
+ <comment xml:lang="lv">Pārvaldītu objektu formāts</comment>
+ <comment xml:lang="nb">Managed Object Format</comment>
+ <comment xml:lang="nl">Managed Object Format</comment>
+ <comment xml:lang="nn">Managed Object Format</comment>
+ <comment xml:lang="oc">format Managed Object</comment>
+ <comment xml:lang="pl">Plik Managed Object Format</comment>
+ <comment xml:lang="pt">formato Managed Object</comment>
+ <comment xml:lang="pt_BR">Formato de objeto gerenciado</comment>
+ <comment xml:lang="ro">Managed Object Format</comment>
+ <comment xml:lang="ru">Формат управляемого объекта</comment>
+ <comment xml:lang="sk">Formát Managed Object</comment>
+ <comment xml:lang="sl">Datoteka Managed Object</comment>
+ <comment xml:lang="sq">Managed Object Format</comment>
+ <comment xml:lang="sr">запис управљаног објекта</comment>
+ <comment xml:lang="sv">Managed Object Format</comment>
+ <comment xml:lang="tr">Yönetilen Nesne Biçimi</comment>
+ <comment xml:lang="uk">формат керування об’єктами</comment>
+ <comment xml:lang="vi">Định dạng Đối tượng đã Quản lý</comment>
+ <comment xml:lang="zh_CN">托管对象格式</comment>
+ <comment xml:lang="zh_TW">Managed Object Format</comment>
+ <sub-class-of type="text/x-csrc"/>
+ <glob pattern="*.mof"/>
+ </mime-type>
+ <mime-type type="text/x-mup">
+ <comment>Mup publication</comment>
+ <comment xml:lang="ar">منشور Mup</comment>
+ <comment xml:lang="be@latin">Publikacyja Mup</comment>
+ <comment xml:lang="bg">Издание — Mup</comment>
+ <comment xml:lang="ca">publicació Mup</comment>
+ <comment xml:lang="cs">publikace Mup</comment>
+ <comment xml:lang="da">Mupudgivelse</comment>
+ <comment xml:lang="de">Mup-Veröffentlichung</comment>
+ <comment xml:lang="el">Δημοσίευση Mup</comment>
+ <comment xml:lang="en_GB">Mup publication</comment>
+ <comment xml:lang="es">publicación Mup</comment>
+ <comment xml:lang="eu">Mup publikazioa</comment>
+ <comment xml:lang="fi">Mup-julkaisu</comment>
+ <comment xml:lang="fo">Mup útgáva</comment>
+ <comment xml:lang="fr">publication Mup</comment>
+ <comment xml:lang="ga">foilseachán Mup</comment>
+ <comment xml:lang="gl">publicación Mup</comment>
+ <comment xml:lang="he">פרסום של Mup</comment>
+ <comment xml:lang="hr">Mup publikacija</comment>
+ <comment xml:lang="hu">Mup publikáció</comment>
+ <comment xml:lang="ia">Publication Mup</comment>
+ <comment xml:lang="id">Publikasi Mup</comment>
+ <comment xml:lang="it">Pubblicazione Mup</comment>
+ <comment xml:lang="ja">Mup 出版ファイル</comment>
+ <comment xml:lang="kk">Mup жариялымы</comment>
+ <comment xml:lang="ko">Mup 출판물</comment>
+ <comment xml:lang="lt">Mup leidinys</comment>
+ <comment xml:lang="lv">Mup publikācija</comment>
+ <comment xml:lang="nb">Mup publikasjon</comment>
+ <comment xml:lang="nl">Mup-publicatie</comment>
+ <comment xml:lang="nn">Mup-publikasjon</comment>
+ <comment xml:lang="oc">publication Mup</comment>
+ <comment xml:lang="pl">Publikacja Mup</comment>
+ <comment xml:lang="pt">publicação Mup</comment>
+ <comment xml:lang="pt_BR">Publicação do Mup</comment>
+ <comment xml:lang="ro">Publicație Mup</comment>
+ <comment xml:lang="ru">Публикация Mup</comment>
+ <comment xml:lang="sk">Publikácie Mup</comment>
+ <comment xml:lang="sl">Datoteka objave Mup</comment>
+ <comment xml:lang="sq">Publikim Mup</comment>
+ <comment xml:lang="sr">Муп објава</comment>
+ <comment xml:lang="sv">Mup-publicering</comment>
+ <comment xml:lang="tr">Mup uygulaması</comment>
+ <comment xml:lang="uk">публікація Mup</comment>
+ <comment xml:lang="vi">Bản xuất Mup</comment>
+ <comment xml:lang="zh_CN">Mup 应用程序</comment>
+ <comment xml:lang="zh_TW">Mup 出版品</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="//!Mup" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mup"/>
+ <glob pattern="*.not"/>
+ </mime-type>
+ <mime-type type="text/x-objcsrc">
+ <comment>Objective-C source code</comment>
+ <comment xml:lang="ar">شفرة مصدر الهدف-C </comment>
+ <comment xml:lang="be@latin">Kryničny kod Objective-C</comment>
+ <comment xml:lang="bg">Изходен код — Objective C</comment>
+ <comment xml:lang="ca">codi font en Objective-C</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Objective-C</comment>
+ <comment xml:lang="da">Objektiv C-kildekode</comment>
+ <comment xml:lang="de">Objective-C-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Objective-C</comment>
+ <comment xml:lang="en_GB">Objective-C source code</comment>
+ <comment xml:lang="eo">fontkodo en Objective-C</comment>
+ <comment xml:lang="es">código fuente en Objective-C</comment>
+ <comment xml:lang="eu">Objective-C iturburu-kodea</comment>
+ <comment xml:lang="fi">Objective-C-lähdekoodi</comment>
+ <comment xml:lang="fo">Objective-C keldukota</comment>
+ <comment xml:lang="fr">code source Objective-C</comment>
+ <comment xml:lang="ga">cód foinseach Objective-C</comment>
+ <comment xml:lang="gl">código fonte de Objective-C</comment>
+ <comment xml:lang="he">קוד מקור של Objective-C</comment>
+ <comment xml:lang="hr">Objective-C izvorni kôd</comment>
+ <comment xml:lang="hu">Objective-C forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Objective-C</comment>
+ <comment xml:lang="id">Kode sumber Objective-C</comment>
+ <comment xml:lang="it">Codice sorgente Objective-C</comment>
+ <comment xml:lang="ja">Objective-C ソースコード</comment>
+ <comment xml:lang="ka">Objective-C-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">Objective-C бастапқы коды</comment>
+ <comment xml:lang="ko">Objective-C 소스 코드</comment>
+ <comment xml:lang="lt">Objective-C pradinis kodas</comment>
+ <comment xml:lang="lv">Objective-C pirmkods</comment>
+ <comment xml:lang="ms">Kod sumber Objective-C</comment>
+ <comment xml:lang="nb">Objective-C-kildekode</comment>
+ <comment xml:lang="nl">Objective-C-broncode</comment>
+ <comment xml:lang="nn">Objective-C-kjeldekode</comment>
+ <comment xml:lang="oc">còde font Objective-C</comment>
+ <comment xml:lang="pl">Kod źródłowy Objective-C</comment>
+ <comment xml:lang="pt">código origem Objective-C</comment>
+ <comment xml:lang="pt_BR">Código-fonte Objective-C</comment>
+ <comment xml:lang="ro">Cod sursă Objective-C</comment>
+ <comment xml:lang="ru">Исходный код Objective-C</comment>
+ <comment xml:lang="sk">Zdrojový kód Objective-C</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Objective-C</comment>
+ <comment xml:lang="sq">Kod burues C objekt</comment>
+ <comment xml:lang="sr">Објектни-Ц изворни ко̂д</comment>
+ <comment xml:lang="sv">Objective-C-källkod</comment>
+ <comment xml:lang="tr">Objective-C kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Objective-C</comment>
+ <comment xml:lang="vi">Mã nguồn Objective-C</comment>
+ <comment xml:lang="zh_CN">Objective-C 源代码</comment>
+ <comment xml:lang="zh_TW">Objective-C 源碼</comment>
+ <sub-class-of type="text/x-csrc"/>
+ <magic priority="30">
+ <match value="#import" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.m"/>
+ </mime-type>
+ <mime-type type="text/x-ocaml">
+ <comment>OCaml source code</comment>
+ <comment xml:lang="ar">شفرة مصدر OCaml</comment>
+ <comment xml:lang="be@latin">Kryničny kod OCaml</comment>
+ <comment xml:lang="bg">Изходен код — OCaml</comment>
+ <comment xml:lang="ca">codi font en OCaml</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce OCaml</comment>
+ <comment xml:lang="da">OCaml-kildekode</comment>
+ <comment xml:lang="de">OCaml-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας OCaml</comment>
+ <comment xml:lang="en_GB">OCaml source code</comment>
+ <comment xml:lang="eo">OCaml-fontkodo</comment>
+ <comment xml:lang="es">código fuente en OCaml</comment>
+ <comment xml:lang="eu">OCaml iturburu-kodea</comment>
+ <comment xml:lang="fi">OCaml-lähdekoodi</comment>
+ <comment xml:lang="fo">OCaml keldukota</comment>
+ <comment xml:lang="fr">code source OCaml</comment>
+ <comment xml:lang="ga">cód foinseach OCaml</comment>
+ <comment xml:lang="gl">código fonte de OCaml</comment>
+ <comment xml:lang="he">קוד מקור של OCaml</comment>
+ <comment xml:lang="hr">OCaml izvorni kôd</comment>
+ <comment xml:lang="hu">OCaml forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte OCaml</comment>
+ <comment xml:lang="id">Kode sumber OCaml</comment>
+ <comment xml:lang="it">Codice sorgente OCaml</comment>
+ <comment xml:lang="ja">OCaml ソースコード</comment>
+ <comment xml:lang="kk">OCaml бастапқы коды</comment>
+ <comment xml:lang="ko">OCaml 소스 코드</comment>
+ <comment xml:lang="lt">OCaml pradinis kodas</comment>
+ <comment xml:lang="lv">OCaml pirmkods</comment>
+ <comment xml:lang="nb">OCaml-kildekode</comment>
+ <comment xml:lang="nl">OCaml-broncode</comment>
+ <comment xml:lang="nn">OCaml-kjeldekode</comment>
+ <comment xml:lang="oc">còde font OCaml</comment>
+ <comment xml:lang="pl">Kod źródłowy OCaml</comment>
+ <comment xml:lang="pt">código origem OCaml</comment>
+ <comment xml:lang="pt_BR">Código-fonte OCaml</comment>
+ <comment xml:lang="ro">Cod sursă OCaml</comment>
+ <comment xml:lang="ru">Исходный код OCaml</comment>
+ <comment xml:lang="sk">Zdrojový kód OCaml</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode OCaml</comment>
+ <comment xml:lang="sq">Kod burues OCaml</comment>
+ <comment xml:lang="sr">Окемл изворни ко̂д</comment>
+ <comment xml:lang="sv">OCaml-källkod</comment>
+ <comment xml:lang="tr">OCaml kaynak kodu</comment>
+ <comment xml:lang="uk">первинний код мовою OCaml</comment>
+ <comment xml:lang="vi">Mã nguồn OCaml</comment>
+ <comment xml:lang="zh_CN">OCaml 源代码</comment>
+ <comment xml:lang="zh_TW">OCaml 源碼</comment>
+ <glob pattern="*.ml"/>
+ <glob pattern="*.mli"/>
+ </mime-type>
+ <mime-type type="text/x-opencl-src">
+ <comment>OpenCL source code</comment>
+ <comment xml:lang="ca">codi font en OpenCL</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce OpenCL</comment>
+ <comment xml:lang="de">OpenCL-Quelltext</comment>
+ <comment xml:lang="en_GB">OpenCL source code</comment>
+ <comment xml:lang="es">código fuente en OpenCL</comment>
+ <comment xml:lang="fi">OpenCL-lähdekoodi</comment>
+ <comment xml:lang="hr">OpenCL izvorni kôd</comment>
+ <comment xml:lang="hu">OpenCL forráskód</comment>
+ <comment xml:lang="id">Kode sumber OpenCL</comment>
+ <comment xml:lang="it">Codice sorgente OpenCL</comment>
+ <comment xml:lang="kk">OpenCL бастапқы коды</comment>
+ <comment xml:lang="ko">OpenCL 소스 코드</comment>
+ <comment xml:lang="pl">Kod źródłowy OpenCL</comment>
+ <comment xml:lang="pt_BR">Código-fonte do OpenCL</comment>
+ <comment xml:lang="ru">Исходный код OpenGL</comment>
+ <comment xml:lang="sk">Zdrojový kód OpenCL</comment>
+ <comment xml:lang="sv">OpenCL-källkod</comment>
+ <comment xml:lang="uk">вихідний код мовою OpenCL</comment>
+ <comment xml:lang="zh_CN">OpenCL 源代码</comment>
+ <comment xml:lang="zh_TW">OpenCL 源碼</comment>
+ <acronym>OpenCL</acronym>
+ <expanded-acronym>Open Computing Language</expanded-acronym>
+ <sub-class-of type="text/x-csrc"/>
+ <glob pattern="*.cl"/>
+ </mime-type>
+ <mime-type type="text/x-matlab">
+ <comment>MATLAB script/function</comment>
+ <comment xml:lang="ar">سكربت/وظيفة MATLAB</comment>
+ <comment xml:lang="be@latin">Skrypt/funkcyja MATLAB</comment>
+ <comment xml:lang="bg">Скрипт/функция — MATLAB</comment>
+ <comment xml:lang="ca">script/funció MATLAB</comment>
+ <comment xml:lang="cs">skript/funkce MATLAB</comment>
+ <comment xml:lang="da">MATLAB-program/-funktion</comment>
+ <comment xml:lang="de">MATLAB-Skript/-Funktion</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών/συνάρτηση MATLAB</comment>
+ <comment xml:lang="en_GB">MATLAB script/function</comment>
+ <comment xml:lang="es">secuencia de órdenes/función de MATLAB</comment>
+ <comment xml:lang="eu">MATLAB script/funtzioa</comment>
+ <comment xml:lang="fi">MATLAB-komentotiedosto/funktio</comment>
+ <comment xml:lang="fo">MATLAB boðrøð/funka</comment>
+ <comment xml:lang="fr">script/fonction MATLAB</comment>
+ <comment xml:lang="ga">script/feidhm MATLAB</comment>
+ <comment xml:lang="gl">función/script de MATLAB</comment>
+ <comment xml:lang="he">תסריט/פונקציית MATLAB</comment>
+ <comment xml:lang="hr">MATLAB skripta/funkcija</comment>
+ <comment xml:lang="hu">MATLAB parancsfájl/funkció</comment>
+ <comment xml:lang="ia">Script/function MATLAB</comment>
+ <comment xml:lang="id">Skrip/fungsi MATLAB</comment>
+ <comment xml:lang="it">Script/Funzione MATLAB</comment>
+ <comment xml:lang="ja">MATLAB スクリプト/関数</comment>
+ <comment xml:lang="kk">MATLAB сценарий/функциясы</comment>
+ <comment xml:lang="ko">MATLAB 스크립트/함수</comment>
+ <comment xml:lang="lt">MATLAB scenarijus / funkcija</comment>
+ <comment xml:lang="lv">MATLAB skripts/funkcija</comment>
+ <comment xml:lang="nb">Skript/funksjon for MATLAB</comment>
+ <comment xml:lang="nl">MATLAB-script/functie</comment>
+ <comment xml:lang="nn">MATLAB-skript/funksjon</comment>
+ <comment xml:lang="oc">escript/fonction MATLAB</comment>
+ <comment xml:lang="pl">Skrypt/funkcja MATLAB</comment>
+ <comment xml:lang="pt">script/função MATLAB</comment>
+ <comment xml:lang="pt_BR">Script/função do MATLAB</comment>
+ <comment xml:lang="ro">Funcție/script MATLAB</comment>
+ <comment xml:lang="ru">Сценарий/функция MATLAB</comment>
+ <comment xml:lang="sk">Skript/funkcia MATLAB</comment>
+ <comment xml:lang="sl">Skriptna datoteka MATLAB</comment>
+ <comment xml:lang="sq">Script/Funksion MATLAB</comment>
+ <comment xml:lang="sr">скрипта/функција МАТЛАБ-а</comment>
+ <comment xml:lang="sv">MATLAB-skript/funktion</comment>
+ <comment xml:lang="tr">MATLAB betiği/fonksiyonu</comment>
+ <comment xml:lang="uk">скрипт/функція MATLAB</comment>
+ <comment xml:lang="vi">Văn lệnh/chức năng MATLAB</comment>
+ <comment xml:lang="zh_CN">MATLAB 脚本/函数</comment>
+ <comment xml:lang="zh_TW">MATLAB 指令稿/函式</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="10">
+ <match value="%" type="string" offset="0"/>
+ </magic>
+ <magic priority="10">
+ <match value="##" type="string" offset="0"/>
+ </magic>
+ <magic priority="50">
+ <match value="function" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.m"/>
+ <alias type="text/x-octave"/>
+ </mime-type>
+ <mime-type type="text/x-meson">
+ <comment>Meson source code</comment>
+ <comment xml:lang="ca">codi font en Meson</comment>
+ <comment xml:lang="cs">zdrojový kód Meson</comment>
+ <comment xml:lang="da">Meson-kildekode</comment>
+ <comment xml:lang="de">Meson-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Meson</comment>
+ <comment xml:lang="en_GB">Meson source code</comment>
+ <comment xml:lang="es">código fuente en Meson</comment>
+ <comment xml:lang="eu">Meson iturburu-kodea</comment>
+ <comment xml:lang="fi">Meson-lähdekoodi</comment>
+ <comment xml:lang="fr">code source Meson</comment>
+ <comment xml:lang="ga">cód foinseach Meson</comment>
+ <comment xml:lang="he">קוד מקור Meson</comment>
+ <comment xml:lang="hr">Meson izvorni kôd</comment>
+ <comment xml:lang="hu">Meson forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Meson</comment>
+ <comment xml:lang="id">Kode sumber Meson</comment>
+ <comment xml:lang="it">Codice sorgente Meson</comment>
+ <comment xml:lang="kk">Meson бастапқы коды</comment>
+ <comment xml:lang="ko">Meson 소스 코드</comment>
+ <comment xml:lang="oc">còde font Meson</comment>
+ <comment xml:lang="pl">Kod źródłowy Meson</comment>
+ <comment xml:lang="pt">código origem Meson</comment>
+ <comment xml:lang="pt_BR">Código-fonte Meson</comment>
+ <comment xml:lang="ru">Исходный код Meson</comment>
+ <comment xml:lang="sk">Zdrojový kód Meson</comment>
+ <comment xml:lang="sr">Месон изворни ко̂д</comment>
+ <comment xml:lang="sv">Meson-källkod</comment>
+ <comment xml:lang="tr">Meson kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Meson</comment>
+ <comment xml:lang="zh_CN">Meson 源代码</comment>
+ <comment xml:lang="zh_TW">Meson 源碼</comment>
+ <glob pattern="meson.build"/>
+ <glob pattern="meson_options.txt"/>
+ <sub-class-of type="text/plain"/>
+ </mime-type>
+ <mime-type type="text/x-modelica">
+ <comment>Modelica model</comment>
+ <comment xml:lang="ca">model de Modelica</comment>
+ <comment xml:lang="cs">model Modelica</comment>
+ <comment xml:lang="da">Modelica-model</comment>
+ <comment xml:lang="de">Modelica-Modell</comment>
+ <comment xml:lang="el">Μοντέλο Modelica</comment>
+ <comment xml:lang="en_GB">Modelica model</comment>
+ <comment xml:lang="es">modelo de Modelica</comment>
+ <comment xml:lang="eu">Modelica modeloa</comment>
+ <comment xml:lang="fi">Modelica-malli</comment>
+ <comment xml:lang="fr">modèle Modelica</comment>
+ <comment xml:lang="ga">samhail Modelica</comment>
+ <comment xml:lang="gl">Modelo de Modelica</comment>
+ <comment xml:lang="he">דגם של Modelica</comment>
+ <comment xml:lang="hr">Modelica model</comment>
+ <comment xml:lang="hu">Modelica modell</comment>
+ <comment xml:lang="ia">Modello Modelica</comment>
+ <comment xml:lang="id">Model Modelica</comment>
+ <comment xml:lang="it">Modello Modelica</comment>
+ <comment xml:lang="ja">Modelica モデル</comment>
+ <comment xml:lang="kk">Modelica моделі</comment>
+ <comment xml:lang="ko">Modelica 모델</comment>
+ <comment xml:lang="lv">Modelica modelis</comment>
+ <comment xml:lang="oc">modèl Modelica</comment>
+ <comment xml:lang="pl">Model Modelica</comment>
+ <comment xml:lang="pt">modelo Modelica</comment>
+ <comment xml:lang="pt_BR">Modelo da Modelica</comment>
+ <comment xml:lang="ru">Модель Modelica</comment>
+ <comment xml:lang="sk">Model Modelica</comment>
+ <comment xml:lang="sl">Model Modelica</comment>
+ <comment xml:lang="sr">модел Моделике</comment>
+ <comment xml:lang="sv">Modelica-modell</comment>
+ <comment xml:lang="tr">Modelica modeli</comment>
+ <comment xml:lang="uk">модель Modelica</comment>
+ <comment xml:lang="zh_CN">Modelica 模型</comment>
+ <comment xml:lang="zh_TW">Modelica 模型</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="10">
+ <match value="//" type="string" offset="0"/>
+ </magic>
+ <magic priority="50">
+ <match value="function" type="string" offset="0"/>
+ </magic>
+ <magic priority="50">
+ <match value="class" type="string" offset="0"/>
+ </magic>
+ <magic priority="50">
+ <match value="model" type="string" offset="0"/>
+ </magic>
+ <magic priority="50">
+ <match value="record" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mo"/>
+ </mime-type>
+ <mime-type type="text/x-pascal">
+ <comment>Pascal source code</comment>
+ <comment xml:lang="ar">شفرة مصدر باسكال</comment>
+ <comment xml:lang="be@latin">Kryničny kod Pascal</comment>
+ <comment xml:lang="bg">Изходен код — Pascal</comment>
+ <comment xml:lang="ca">codi font en Pascal</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Pascal</comment>
+ <comment xml:lang="da">Pascalkildekode</comment>
+ <comment xml:lang="de">Pascal-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Pascal</comment>
+ <comment xml:lang="en_GB">Pascal source code</comment>
+ <comment xml:lang="eo">Pascal-fontkodo</comment>
+ <comment xml:lang="es">código fuente en Pascal</comment>
+ <comment xml:lang="eu">Pascal iturburu-kodea</comment>
+ <comment xml:lang="fi">Pascal-lähdekoodi</comment>
+ <comment xml:lang="fo">Pascal keldukota</comment>
+ <comment xml:lang="fr">code source Pascal</comment>
+ <comment xml:lang="ga">cód foinseach Pascal</comment>
+ <comment xml:lang="gl">código fonte en Pascal</comment>
+ <comment xml:lang="he">קוד מקור של Pascal</comment>
+ <comment xml:lang="hr">Pascal izvorni kôd</comment>
+ <comment xml:lang="hu">Pascal-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Pascal</comment>
+ <comment xml:lang="id">Kode sumber Pascal</comment>
+ <comment xml:lang="it">Codice sorgente Pascal</comment>
+ <comment xml:lang="ja">Pascal ソースコード</comment>
+ <comment xml:lang="kk">Pascal бастапқы коды</comment>
+ <comment xml:lang="ko">파스칼 소스 코드</comment>
+ <comment xml:lang="lt">Pascal pradinis kodas</comment>
+ <comment xml:lang="lv">Pascal pirmkods</comment>
+ <comment xml:lang="ms">Kod sumber Pascal</comment>
+ <comment xml:lang="nb">Pascal-kildekode</comment>
+ <comment xml:lang="nl">Pascal-broncode</comment>
+ <comment xml:lang="nn">Pascal-kjeldekode</comment>
+ <comment xml:lang="oc">còde font Pascal</comment>
+ <comment xml:lang="pl">Kod źródłowy Pascal</comment>
+ <comment xml:lang="pt">código origem Pascal</comment>
+ <comment xml:lang="pt_BR">Código-fonte Pascal</comment>
+ <comment xml:lang="ro">Cod sursă Pascal</comment>
+ <comment xml:lang="ru">Исходный код Pascal</comment>
+ <comment xml:lang="sk">Zdrojový kód Pascal</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Pascal</comment>
+ <comment xml:lang="sq">Kod burues Pascal</comment>
+ <comment xml:lang="sr">Паскалов изворни ко̂д</comment>
+ <comment xml:lang="sv">Pascal-källkod</comment>
+ <comment xml:lang="tr">Pascal kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Pascal</comment>
+ <comment xml:lang="vi">Mã nguồn Pascal</comment>
+ <comment xml:lang="zh_CN">Pascal 源代码</comment>
+ <comment xml:lang="zh_TW">Pascal 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.p"/>
+ <glob pattern="*.pas"/>
+ </mime-type>
+ <mime-type type="text/x-patch">
+ <comment>differences between files</comment>
+ <comment xml:lang="ar">الاختلافات بين الملفات</comment>
+ <comment xml:lang="be@latin">adroźnieńni pamiž fajłami</comment>
+ <comment xml:lang="bg">Разлики между файлове</comment>
+ <comment xml:lang="ca">diferències entre fitxers</comment>
+ <comment xml:lang="cs">rozdíly mezi soubory</comment>
+ <comment xml:lang="da">forskel mellem filer</comment>
+ <comment xml:lang="de">Unterschiede zwischen Dateien</comment>
+ <comment xml:lang="el">Διαφορές μεταξύ αρχείων</comment>
+ <comment xml:lang="en_GB">differences between files</comment>
+ <comment xml:lang="eo">diferencoj inter dosieroj</comment>
+ <comment xml:lang="es">diferencias entre archivos</comment>
+ <comment xml:lang="eu">fitxategien arteko ezberdintasunak</comment>
+ <comment xml:lang="fi">tiedostojen väliset erot</comment>
+ <comment xml:lang="fo">munur millum fílur</comment>
+ <comment xml:lang="fr">différences entre fichiers</comment>
+ <comment xml:lang="ga">difríochtaí idir chomhaid</comment>
+ <comment xml:lang="gl">diferenzas entre ficheiros</comment>
+ <comment xml:lang="he">הבדל בין קבצים</comment>
+ <comment xml:lang="hr">Razlike između datoteka</comment>
+ <comment xml:lang="hu">diff-különbségfájl</comment>
+ <comment xml:lang="ia">Differentias inter files</comment>
+ <comment xml:lang="id">perbedaan diantara berkas</comment>
+ <comment xml:lang="it">Differenze tra file</comment>
+ <comment xml:lang="ja">ファイル間差分</comment>
+ <comment xml:lang="kk">файлдар арасындағы айырмашылықтары</comment>
+ <comment xml:lang="ko">파일 사이의 차이점</comment>
+ <comment xml:lang="lt">skirtumai tarp failų</comment>
+ <comment xml:lang="lv">divu datņu atšķirība</comment>
+ <comment xml:lang="ms">Perbezaan antara fail</comment>
+ <comment xml:lang="nb">forskjeller mellom filer</comment>
+ <comment xml:lang="nl">verschillen tussen bestanden</comment>
+ <comment xml:lang="nn">skilnader mellom filer</comment>
+ <comment xml:lang="oc">différences entre fichièrs</comment>
+ <comment xml:lang="pl">Różnica pomiędzy plikami</comment>
+ <comment xml:lang="pt">diferenças entre ficheiros</comment>
+ <comment xml:lang="pt_BR">Diferenças entre arquivos</comment>
+ <comment xml:lang="ro">diferențe între fișiere</comment>
+ <comment xml:lang="ru">Различия между файлами</comment>
+ <comment xml:lang="sk">Rozdiely medzi súbormi</comment>
+ <comment xml:lang="sl">razlike med datotekami</comment>
+ <comment xml:lang="sq">Diferencë midis file</comment>
+ <comment xml:lang="sr">разлике између датотека</comment>
+ <comment xml:lang="sv">skillnader mellan filer</comment>
+ <comment xml:lang="tr">dosyalar arasındaki fark</comment>
+ <comment xml:lang="uk">різниця між файлами</comment>
+ <comment xml:lang="vi">khác biệt giữa các tập tin</comment>
+ <comment xml:lang="zh_CN">文件的区别</comment>
+ <comment xml:lang="zh_TW">檔案內容差異</comment>
+ <alias type="text/x-diff"/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="diff\t" type="string" offset="0"/>
+ <match value="diff " type="string" offset="0"/>
+ <match value="***\t" type="string" offset="0"/>
+ <match value="*** " type="string" offset="0"/>
+ <match value="=== " type="string" offset="0"/>
+ <match value="--- " type="string" offset="0"/>
+ <match value="Only in\t" type="string" offset="0"/>
+ <match value="Only in " type="string" offset="0"/>
+ <match value="Common subdirectories: " type="string" offset="0"/>
+ <match value="Index:" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.diff"/>
+ <glob pattern="*.patch"/>
+ </mime-type>
+ <mime-type type="text/x-go">
+ <comment>Go source code</comment>
+ <comment xml:lang="bg">Изходен код — Go</comment>
+ <comment xml:lang="ca">codi font en Go</comment>
+ <comment xml:lang="cs">zdrojový kód v jazyce Go</comment>
+ <comment xml:lang="da">Go-kildekode</comment>
+ <comment xml:lang="de">Go-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Go</comment>
+ <comment xml:lang="en_GB">Go source code</comment>
+ <comment xml:lang="eo">Go-fontkodo</comment>
+ <comment xml:lang="es">código fuente en Go</comment>
+ <comment xml:lang="eu">Go iturburu-kodea</comment>
+ <comment xml:lang="fi">Go-lähdekoodi</comment>
+ <comment xml:lang="fr">code source Go</comment>
+ <comment xml:lang="ga">cód foinseach Go</comment>
+ <comment xml:lang="gl">código fonte de Go</comment>
+ <comment xml:lang="he">קוד מקור של Go</comment>
+ <comment xml:lang="hr">Go izvorni kôd</comment>
+ <comment xml:lang="hu">Go forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Go</comment>
+ <comment xml:lang="id">Kode sumber Go</comment>
+ <comment xml:lang="it">Codice sorgente Go</comment>
+ <comment xml:lang="ja">Go ソースコード</comment>
+ <comment xml:lang="ka">Go-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">Go бастапқы коды</comment>
+ <comment xml:lang="ko">Go 소스 코드</comment>
+ <comment xml:lang="lv">Go pirmkods</comment>
+ <comment xml:lang="nl">Go broncode</comment>
+ <comment xml:lang="oc">còde font Go</comment>
+ <comment xml:lang="pl">Kod źródłowy Go</comment>
+ <comment xml:lang="pt">cigo origem Go</comment>
+ <comment xml:lang="pt_BR">Código-fonte Go</comment>
+ <comment xml:lang="ru">Исходный код Go</comment>
+ <comment xml:lang="sk">Zdrojový kód Go</comment>
+ <comment xml:lang="sl">Izvorna koda Go</comment>
+ <comment xml:lang="sr">Гоу изворни ко̂д</comment>
+ <comment xml:lang="sv">Go-källkod</comment>
+ <comment xml:lang="tr">Go kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Go</comment>
+ <comment xml:lang="zh_CN">Go 源代码</comment>
+ <comment xml:lang="zh_TW">Go 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.go"/>
+ </mime-type>
+ <mime-type type="text/x-scons">
+ <comment>SCons configuration file</comment>
+ <comment xml:lang="ca">fitxer de configuració de SCons</comment>
+ <comment xml:lang="cs">konfigurační soubor SCons</comment>
+ <comment xml:lang="da">SCons-konfigurationsfil</comment>
+ <comment xml:lang="de">SCons-Konfigurationsdatei</comment>
+ <comment xml:lang="el">Αρχείο ρυθμίσεων SCons</comment>
+ <comment xml:lang="en_GB">SCons configuration file</comment>
+ <comment xml:lang="es">archivo de configuración de SCons</comment>
+ <comment xml:lang="eu">SCons konfigurazio-fitxategia</comment>
+ <comment xml:lang="fi">SCons-asetustiedosto</comment>
+ <comment xml:lang="fr">fichier de configuration SCons</comment>
+ <comment xml:lang="ga">comhad cumraíochta SCons</comment>
+ <comment xml:lang="he">קובץ תצורה של SCons</comment>
+ <comment xml:lang="hr">SCons datoteka podešavanja</comment>
+ <comment xml:lang="hu">SCons beállítófájl</comment>
+ <comment xml:lang="ia">File de cofniguration SCons</comment>
+ <comment xml:lang="id">Berkas konfigurasi SCons</comment>
+ <comment xml:lang="it">File configurazione SCons</comment>
+ <comment xml:lang="kk">SCons баптаулар файлы</comment>
+ <comment xml:lang="ko">SCons 설정 파일</comment>
+ <comment xml:lang="oc">fichièr de configuracion SCons</comment>
+ <comment xml:lang="pl">Plik konfiguracji SCons</comment>
+ <comment xml:lang="pt">ficheiro de configuração SCons</comment>
+ <comment xml:lang="pt_BR">Arquivo de configuração do SCons</comment>
+ <comment xml:lang="ru">Файл настроек SCons</comment>
+ <comment xml:lang="sk">Konfiguračný súbor SCons</comment>
+ <comment xml:lang="sl">Prilagoditvena datoteka SCons</comment>
+ <comment xml:lang="sr">СКонс датотека подешавања</comment>
+ <comment xml:lang="sv">SCons-konfigurationsfil</comment>
+ <comment xml:lang="tr">SCons yapılandırma dosyası</comment>
+ <comment xml:lang="uk">файл налаштувань SCons</comment>
+ <comment xml:lang="zh_CN">SCons 配置文件</comment>
+ <comment xml:lang="zh_TW">SCons 組態檔</comment>
+ <sub-class-of type="text/x-python"/>
+ <glob pattern="SConstruct"/>
+ <glob pattern="SConscript"/>
+ <glob pattern="SConscript.*"/>
+ </mime-type>
+ <mime-type type="text/x-python3">
+ <comment>Python 3 script</comment>
+ <comment xml:lang="ca">script Python 3</comment>
+ <comment xml:lang="cs">skript v jazyce Python 3</comment>
+ <comment xml:lang="de">Python-3-Skript</comment>
+ <comment xml:lang="en_GB">Python 3 script</comment>
+ <comment xml:lang="es">secuencia de órdenes en Python 3</comment>
+ <comment xml:lang="fi">Python 3 -skripti</comment>
+ <comment xml:lang="hr">Python3 skripta</comment>
+ <comment xml:lang="hu">Python 3 parancsfájl</comment>
+ <comment xml:lang="id">Skrip Python 3</comment>
+ <comment xml:lang="it">Script Python 3</comment>
+ <comment xml:lang="kk">Python 3 скрипті</comment>
+ <comment xml:lang="ko">파이썬 3 스크립트</comment>
+ <comment xml:lang="pl">Skrypt Python 3</comment>
+ <comment xml:lang="pt_BR">Script Python 3</comment>
+ <comment xml:lang="ru">Сценарий Python 3</comment>
+ <comment xml:lang="sk">Skript Python 3</comment>
+ <comment xml:lang="sv">Python 3-skript</comment>
+ <comment xml:lang="uk">скрипт мовою Python 3</comment>
+ <comment xml:lang="zh_CN">Python 3 脚本</comment>
+ <comment xml:lang="zh_TW">Python 3 指令稿</comment>
+ <sub-class-of type='text/x-python'/>
+ <magic priority="60">
+ <match value="#!/bin/python3" type="string" offset="0"/>
+ <match value="#! /bin/python3" type="string" offset="0"/>
+ <match value='eval \"exec /bin/python3' type="string" offset="0"/>
+ <match value="#!/usr/bin/python3" type="string" offset="0"/>
+ <match value="#! /usr/bin/python3" type="string" offset="0"/>
+ <match value='eval \"exec /usr/bin/python3' type="string" offset="0"/>
+ <match value="#!/usr/local/bin/python3" type="string" offset="0"/>
+ <match value="#! /usr/local/bin/python3" type="string" offset="0"/>
+ <match value='eval \"exec /usr/local/bin/python3' type="string" offset="0"/>
+ <match value='/bin/env python3' type="string" offset="2:16"/>
+ </magic>
+ <glob weight="50" pattern="*.py"/>
+ <glob weight="60" pattern="*.py3"/>
+ <glob weight="60" pattern="*.py3x"/>
+ </mime-type>
+ <mime-type type="text/x-python">
+ <comment>Python script</comment>
+ <comment xml:lang="ar">سكربت بايثون</comment>
+ <comment xml:lang="be@latin">Skrypt Python</comment>
+ <comment xml:lang="bg">Скрипт — Python</comment>
+ <comment xml:lang="ca">script Python</comment>
+ <comment xml:lang="cs">skript v jazyce Python</comment>
+ <comment xml:lang="da">Pythonprogram</comment>
+ <comment xml:lang="de">Python-Skript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών Python</comment>
+ <comment xml:lang="en_GB">Python script</comment>
+ <comment xml:lang="eo">Python-skripto</comment>
+ <comment xml:lang="es">secuencia de órdenes en Python</comment>
+ <comment xml:lang="eu">Python script-a</comment>
+ <comment xml:lang="fi">Python-komentotiedosto</comment>
+ <comment xml:lang="fo">Python boðrøð</comment>
+ <comment xml:lang="fr">script Python</comment>
+ <comment xml:lang="ga">script Python</comment>
+ <comment xml:lang="gl">Script en Python</comment>
+ <comment xml:lang="he">תסריט Python</comment>
+ <comment xml:lang="hr">Python skripta</comment>
+ <comment xml:lang="hu">Python-parancsfájl</comment>
+ <comment xml:lang="ia">Script Python</comment>
+ <comment xml:lang="id">Skrip Python</comment>
+ <comment xml:lang="it">Script Python</comment>
+ <comment xml:lang="ja">Python スクリプト</comment>
+ <comment xml:lang="kk">Python сценарийі</comment>
+ <comment xml:lang="ko">파이썬 스크립트</comment>
+ <comment xml:lang="lt">Python scenarijus</comment>
+ <comment xml:lang="lv">Python skripts</comment>
+ <comment xml:lang="ms">Skrip Python</comment>
+ <comment xml:lang="nb">Python-skript</comment>
+ <comment xml:lang="nl">Python-script</comment>
+ <comment xml:lang="nn">Python-skript</comment>
+ <comment xml:lang="oc">escript Python</comment>
+ <comment xml:lang="pl">Skrypt Python</comment>
+ <comment xml:lang="pt">script Python</comment>
+ <comment xml:lang="pt_BR">Script Python</comment>
+ <comment xml:lang="ro">Script Python</comment>
+ <comment xml:lang="ru">Сценарий Python</comment>
+ <comment xml:lang="sk">Skript Python</comment>
+ <comment xml:lang="sl">Skriptna datoteka Python</comment>
+ <comment xml:lang="sq">Script Python</comment>
+ <comment xml:lang="sr">Питонова скрипта</comment>
+ <comment xml:lang="sv">Pythonskript</comment>
+ <comment xml:lang="tr">Python betiği</comment>
+ <comment xml:lang="uk">скрипт мовою Python</comment>
+ <comment xml:lang="vi">Văn lệnh Python</comment>
+ <comment xml:lang="zh_CN">Python 脚本</comment>
+ <comment xml:lang="zh_TW">Python 指令稿</comment>
+ <sub-class-of type='application/x-executable'/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="#!/bin/python" type="string" offset="0"/>
+ <match value="#! /bin/python" type="string" offset="0"/>
+ <match value='eval \"exec /bin/python' type="string" offset="0"/>
+ <match value="#!/usr/bin/python" type="string" offset="0"/>
+ <match value="#! /usr/bin/python" type="string" offset="0"/>
+ <match value='eval \"exec /usr/bin/python' type="string" offset="0"/>
+ <match value="#!/usr/local/bin/python" type="string" offset="0"/>
+ <match value="#! /usr/local/bin/python" type="string" offset="0"/>
+ <match value='eval \"exec /usr/local/bin/python' type="string" offset="0"/>
+ <match value='/bin/env python' type="string" offset="2:16"/>
+ </magic>
+ <glob weight="60" pattern="*.py"/>
+ <glob weight="60" pattern="*.pyx"/>
+ <glob weight="60" pattern="*.wsgi"/>
+ </mime-type>
+ <mime-type type="text/x-lua">
+ <comment>Lua script</comment>
+ <comment xml:lang="ar">سكربت Lua</comment>
+ <comment xml:lang="be@latin">Skrypt Lua</comment>
+ <comment xml:lang="bg">Скрипт на Lua</comment>
+ <comment xml:lang="ca">script Lua</comment>
+ <comment xml:lang="cs">skript v jazyce Lua</comment>
+ <comment xml:lang="da">Luaprogram</comment>
+ <comment xml:lang="de">Lua-Skript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών Lua</comment>
+ <comment xml:lang="en_GB">Lua script</comment>
+ <comment xml:lang="eo">Lua-skripto</comment>
+ <comment xml:lang="es">secuencia de órdenes en Lua</comment>
+ <comment xml:lang="eu">Lua script-a</comment>
+ <comment xml:lang="fi">Lua-komentotiedosto</comment>
+ <comment xml:lang="fo">Lua boðrøð</comment>
+ <comment xml:lang="fr">script Lua</comment>
+ <comment xml:lang="ga">script Lua</comment>
+ <comment xml:lang="gl">script de Lua</comment>
+ <comment xml:lang="he">תסריט Lua</comment>
+ <comment xml:lang="hr">Lua skripta</comment>
+ <comment xml:lang="hu">Lua parancsfájl</comment>
+ <comment xml:lang="ia">Script Lua</comment>
+ <comment xml:lang="id">Skrip Lua</comment>
+ <comment xml:lang="it">Script Lua</comment>
+ <comment xml:lang="ja">Lua スクリプト</comment>
+ <comment xml:lang="kk">Lua сценарийі</comment>
+ <comment xml:lang="ko">Lua 스크립트</comment>
+ <comment xml:lang="lt">Lua scenarijus</comment>
+ <comment xml:lang="lv">Lua skripts</comment>
+ <comment xml:lang="nb">Lua-skript</comment>
+ <comment xml:lang="nl">Lua-script</comment>
+ <comment xml:lang="nn">Lua-skript</comment>
+ <comment xml:lang="oc">escript Lua</comment>
+ <comment xml:lang="pl">Skrypt Lua</comment>
+ <comment xml:lang="pt">script Lua</comment>
+ <comment xml:lang="pt_BR">Script Lua</comment>
+ <comment xml:lang="ro">Script Lua</comment>
+ <comment xml:lang="ru">Сценарий Lua</comment>
+ <comment xml:lang="sk">Skript Lua</comment>
+ <comment xml:lang="sl">Skriptna datoteka Lua</comment>
+ <comment xml:lang="sq">Script Lua</comment>
+ <comment xml:lang="sr">Луа скрипта</comment>
+ <comment xml:lang="sv">Lua-skript</comment>
+ <comment xml:lang="tr">Lua betiği</comment>
+ <comment xml:lang="uk">скрипт Lua</comment>
+ <comment xml:lang="vi">Văn lệnh Lua</comment>
+ <comment xml:lang="zh_CN">Lua 脚本</comment>
+ <comment xml:lang="zh_TW">Lua 指令稿</comment>
+ <sub-class-of type='application/x-executable'/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="/bin/lua" type="string" offset="2:16"/>
+ <match value="/bin/luajit" type="string" offset="2:16"/>
+ <match value="/bin/env lua" type="string" offset="2:16"/>
+ <match value="/bin/env luajit" type="string" offset="2:16"/>
+ </magic>
+ <glob pattern="*.lua"/>
+ </mime-type>
+ <mime-type type="text/x-readme">
+ <comment>README document</comment>
+ <comment xml:lang="ar">مستند README</comment>
+ <comment xml:lang="ast">Documentu LLEIME</comment>
+ <comment xml:lang="az">README sənədi</comment>
+ <comment xml:lang="be@latin">Dakument README</comment>
+ <comment xml:lang="bg">Документ — „Да се прочете“</comment>
+ <comment xml:lang="ca">document README</comment>
+ <comment xml:lang="cs">dokument README</comment>
+ <comment xml:lang="cy">Dogfen README</comment>
+ <comment xml:lang="da">README-dokument</comment>
+ <comment xml:lang="de">README-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο README</comment>
+ <comment xml:lang="en_GB">README document</comment>
+ <comment xml:lang="eo">README-dokumento</comment>
+ <comment xml:lang="es">documento README</comment>
+ <comment xml:lang="eu">README dokumentua</comment>
+ <comment xml:lang="fi">LUEMINUT-asiakirja</comment>
+ <comment xml:lang="fo">README skjal</comment>
+ <comment xml:lang="fr">document LISEZ-MOI</comment>
+ <comment xml:lang="ga">cáipéis README</comment>
+ <comment xml:lang="gl">documento README</comment>
+ <comment xml:lang="he">מסמך README</comment>
+ <comment xml:lang="hr">README dokument</comment>
+ <comment xml:lang="hu">README-dokumentum</comment>
+ <comment xml:lang="ia">Documento LEGE-ME</comment>
+ <comment xml:lang="id">Dokumen README</comment>
+ <comment xml:lang="it">Documento README</comment>
+ <comment xml:lang="ja">README ドキュメント</comment>
+ <comment xml:lang="kk">README құжаты</comment>
+ <comment xml:lang="ko">README 문서</comment>
+ <comment xml:lang="lt">README dokumentas</comment>
+ <comment xml:lang="lv">README dokuments</comment>
+ <comment xml:lang="ms">Dokumen README</comment>
+ <comment xml:lang="nb">README-dokument</comment>
+ <comment xml:lang="nl">LEESMIJ-document</comment>
+ <comment xml:lang="nn">README-dokument</comment>
+ <comment xml:lang="oc">document LISEZ-MOI</comment>
+ <comment xml:lang="pl">Dokument README</comment>
+ <comment xml:lang="pt">documento LEIA-ME</comment>
+ <comment xml:lang="pt_BR">Documento README</comment>
+ <comment xml:lang="ro">Document README</comment>
+ <comment xml:lang="ru">Документ README</comment>
+ <comment xml:lang="sk">Dokument README</comment>
+ <comment xml:lang="sl">Dokument README</comment>
+ <comment xml:lang="sq">Dokument README</comment>
+ <comment xml:lang="sr">документ ПРОЧИТАЈМЕ</comment>
+ <comment xml:lang="sv">README-dokument</comment>
+ <comment xml:lang="tr">BENİOKU belgesi</comment>
+ <comment xml:lang="uk">документ README</comment>
+ <comment xml:lang="vi">Tài liệu Đọc Đi (README)</comment>
+ <comment xml:lang="zh_CN">README 文档</comment>
+ <comment xml:lang="zh_TW">README 說明文件</comment>
+ <sub-class-of type="text/plain"/>
+ <glob weight="10" pattern="README*"/>
+ </mime-type>
+ <mime-type type="text/x-nfo">
+ <comment>NFO document</comment>
+ <comment xml:lang="ar">مستند NFO</comment>
+ <comment xml:lang="ast">Documentu NFO</comment>
+ <comment xml:lang="be@latin">Dakument NFO</comment>
+ <comment xml:lang="bg">Документ — NFO</comment>
+ <comment xml:lang="ca">document NFO</comment>
+ <comment xml:lang="cs">dokument NFO</comment>
+ <comment xml:lang="da">NFO-dokument</comment>
+ <comment xml:lang="de">NFO-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο NFO</comment>
+ <comment xml:lang="en_GB">NFO document</comment>
+ <comment xml:lang="eo">NFO-dokumento</comment>
+ <comment xml:lang="es">documento NFO</comment>
+ <comment xml:lang="eu">NFO dokumentua</comment>
+ <comment xml:lang="fi">NFO-asiakirja</comment>
+ <comment xml:lang="fo">NFO skjal</comment>
+ <comment xml:lang="fr">document NFO</comment>
+ <comment xml:lang="ga">cáipéis NFO</comment>
+ <comment xml:lang="gl">documento NFO</comment>
+ <comment xml:lang="he">מסמך NFO</comment>
+ <comment xml:lang="hr">NFO dokument</comment>
+ <comment xml:lang="hu">NFO-dokumentum</comment>
+ <comment xml:lang="ia">Documento NFO</comment>
+ <comment xml:lang="id">Dokumen NFO</comment>
+ <comment xml:lang="it">Documento NFO</comment>
+ <comment xml:lang="ja">NFO ドキュメント</comment>
+ <comment xml:lang="kk">NFO құжаты</comment>
+ <comment xml:lang="ko">NFO 문서</comment>
+ <comment xml:lang="lt">NFO dokumentas</comment>
+ <comment xml:lang="lv">NFO dokuments</comment>
+ <comment xml:lang="nb">NFO-dokument</comment>
+ <comment xml:lang="nl">NFO-document</comment>
+ <comment xml:lang="nn">NFO-dokument</comment>
+ <comment xml:lang="oc">document NFO</comment>
+ <comment xml:lang="pl">Dokument NFO</comment>
+ <comment xml:lang="pt">documento NFO</comment>
+ <comment xml:lang="pt_BR">Documento NFO</comment>
+ <comment xml:lang="ro">Document NFO</comment>
+ <comment xml:lang="ru">Документ NFO</comment>
+ <comment xml:lang="sk">Dokument NFO</comment>
+ <comment xml:lang="sl">Dokument NFO</comment>
+ <comment xml:lang="sq">Dokument NFO</comment>
+ <comment xml:lang="sr">документ НФО</comment>
+ <comment xml:lang="sv">NFO-dokument</comment>
+ <comment xml:lang="tr">NFO belgesi</comment>
+ <comment xml:lang="uk">документ NFO</comment>
+ <comment xml:lang="vi">Tài liệu NFO</comment>
+ <comment xml:lang="zh_CN">NFO 文档</comment>
+ <comment xml:lang="zh_TW">NFO 文件</comment>
+ <sub-class-of type="text/x-readme"/>
+ <glob pattern="*.nfo"/>
+ </mime-type>
+ <mime-type type="text/x-rpm-spec">
+ <comment>RPM spec file</comment>
+ <comment xml:lang="ar">ملف مواصفات RPM</comment>
+ <comment xml:lang="be@latin">Specyfikacyjny fajł RPM</comment>
+ <comment xml:lang="bg">Файл — спецификация за RPM</comment>
+ <comment xml:lang="ca">fitxer spec RPM</comment>
+ <comment xml:lang="cs">soubor specifikace RPM</comment>
+ <comment xml:lang="da">RPM spec-fil</comment>
+ <comment xml:lang="de">RPM-Spezifikationsdatei</comment>
+ <comment xml:lang="el">Αρχείο spec RPM</comment>
+ <comment xml:lang="en_GB">RPM spec file</comment>
+ <comment xml:lang="es">archivo de especificaciones RPM</comment>
+ <comment xml:lang="eu">RPM espezifikazio fitxategia</comment>
+ <comment xml:lang="fi">RPM spec -tiedosto</comment>
+ <comment xml:lang="fo">RPM tøknilýsingarfíla</comment>
+ <comment xml:lang="fr">fichier de spécification RPM</comment>
+ <comment xml:lang="ga">comhad spec RPM</comment>
+ <comment xml:lang="gl">ficheiro de especificacións RPM</comment>
+ <comment xml:lang="he">קובץ מפרט RPM</comment>
+ <comment xml:lang="hr">RPM određena datoteka</comment>
+ <comment xml:lang="hu">RPM spec fájl</comment>
+ <comment xml:lang="ia">File de specification RPM</comment>
+ <comment xml:lang="id">Berkas spesifikasi RPM</comment>
+ <comment xml:lang="it">File specifica RPM</comment>
+ <comment xml:lang="ja">RPM spec ファイル</comment>
+ <comment xml:lang="kk">RPM анықтама файлы</comment>
+ <comment xml:lang="ko">RPM spec 파일</comment>
+ <comment xml:lang="lt">RPM spec failas</comment>
+ <comment xml:lang="lv">RPM specifikācijas datne</comment>
+ <comment xml:lang="nb">RPM-spesifikasjonsfil</comment>
+ <comment xml:lang="nl">RPM-spec-bestand</comment>
+ <comment xml:lang="nn">RPM spec-fil</comment>
+ <comment xml:lang="oc">fichièr d'especificacion RPM</comment>
+ <comment xml:lang="pl">Plik spec RPM</comment>
+ <comment xml:lang="pt">ficheiro de especificações RPM</comment>
+ <comment xml:lang="pt_BR">Arquivo de especificação RPM</comment>
+ <comment xml:lang="ro">Fișier RPM spec</comment>
+ <comment xml:lang="ru">Файл описания RPM</comment>
+ <comment xml:lang="sk">Súbor RPM spec</comment>
+ <comment xml:lang="sl">Določilna datoteka RPM</comment>
+ <comment xml:lang="sq">File specifikimi RPM</comment>
+ <comment xml:lang="sr">РПМ посебна датотека</comment>
+ <comment xml:lang="sv">RPM spec-fil</comment>
+ <comment xml:lang="tr">RPM spec dosyası</comment>
+ <comment xml:lang="uk">spec-файл RPM</comment>
+ <comment xml:lang="vi">Tập tin đặc tả RPM</comment>
+ <comment xml:lang="zh_CN">RPM spec 文件</comment>
+ <comment xml:lang="zh_TW">RPM spec 規格檔</comment>
+ <acronym>RPM</acronym>
+ <expanded-acronym>Red Hat Package Manager</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.spec"/>
+ <magic priority="50">
+ <match value="Summary: " type="string" offset="0"/>
+ <match value="%define " type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="text/x-sass">
+ <comment>Sass CSS pre-processor file</comment>
+ <comment xml:lang="ca">fitxer preprocessador CSS Sass</comment>
+ <comment xml:lang="cs">soubor preprocesoru Sass CSS</comment>
+ <comment xml:lang="da">Sass CSS-forbrænderfil</comment>
+ <comment xml:lang="de">Sass-CSS-Präprozessordatei</comment>
+ <comment xml:lang="en_GB">Sass CSS pre-processor file</comment>
+ <comment xml:lang="es">archivo de preprocesador de CSS Sass</comment>
+ <comment xml:lang="fr">fichier de prétraitement CSS Sass</comment>
+ <comment xml:lang="ga">comhad réamhphróiseálaí CSS Sass</comment>
+ <comment xml:lang="he">קובץ קדם עיבוד Sass CSS</comment>
+ <comment xml:lang="hr">Sass CSS datoteka predobrade</comment>
+ <comment xml:lang="hu">Sass CSS előfeldolgozó fájl</comment>
+ <comment xml:lang="id">berkas pre-processor Sass CSS</comment>
+ <comment xml:lang="it">File CSS Sass</comment>
+ <comment xml:lang="kk">Sass CSS препроцессор файлы</comment>
+ <comment xml:lang="ko">Sass CSS 전처리기 파일</comment>
+ <comment xml:lang="pl">Plik preprocesora CSS Sass</comment>
+ <comment xml:lang="pt_BR">Arquivo de pré-processamento Sass CSS</comment>
+ <comment xml:lang="ru">Файл препроцессора Sass CSS</comment>
+ <comment xml:lang="sk">Súbor Sass CSS pre-procesora</comment>
+ <comment xml:lang="sr">датотека Сас ЦСС пре-процесора</comment>
+ <comment xml:lang="sv">Sass CSS-preprocessorfil</comment>
+ <comment xml:lang="tr">Sass CSS önişlemci dosyası</comment>
+ <comment xml:lang="uk">файл препроцесора CSS Sass</comment>
+ <comment xml:lang="zh_CN">Sass CSS 预处理器文件</comment>
+ <comment xml:lang="zh_TW">Sass CSS 處理器前檔案</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.sass"/>
+ <generic-icon name="text-x-generic"/>
+ </mime-type>
+ <mime-type type="text/x-scala">
+ <comment>Scala source code</comment>
+ <comment xml:lang="bg">Изходен код — Scala</comment>
+ <comment xml:lang="ca">codi font en Scala</comment>
+ <comment xml:lang="cs">zdrojový kód Scala</comment>
+ <comment xml:lang="da">Scala-kildekode</comment>
+ <comment xml:lang="de">Scala-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Scala</comment>
+ <comment xml:lang="en_GB">Scala source code</comment>
+ <comment xml:lang="es">código fuente en Scala</comment>
+ <comment xml:lang="eu">Scala iturburu-kodea</comment>
+ <comment xml:lang="fi">Scala-lähdekoodi</comment>
+ <comment xml:lang="fr">code source Scala</comment>
+ <comment xml:lang="ga">cód foinseach Scala</comment>
+ <comment xml:lang="gl">código fnote en Scala</comment>
+ <comment xml:lang="he">קוד מקור של Scala</comment>
+ <comment xml:lang="hr">Scala izvorni kôd</comment>
+ <comment xml:lang="hu">Scala forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Scala</comment>
+ <comment xml:lang="id">Kode sumber Scala</comment>
+ <comment xml:lang="it">Codice sorgente Scala</comment>
+ <comment xml:lang="ja">Scala ソースコード</comment>
+ <comment xml:lang="ka">Scala-ის საწყისი კოდი</comment>
+ <comment xml:lang="kk">Scala бастапқы коды</comment>
+ <comment xml:lang="ko">Scala 소스 코드</comment>
+ <comment xml:lang="lv">Scala pirmkods</comment>
+ <comment xml:lang="nl">Scala broncode</comment>
+ <comment xml:lang="oc">còde font Scala</comment>
+ <comment xml:lang="pl">Kod źródłowy Scala</comment>
+ <comment xml:lang="pt">código origem Scala</comment>
+ <comment xml:lang="pt_BR">Código-fonte Scala</comment>
+ <comment xml:lang="ru">Исходный код Scala</comment>
+ <comment xml:lang="sk">Zdrojový kód Scala</comment>
+ <comment xml:lang="sl">Izvorna koda Scala</comment>
+ <comment xml:lang="sr">Скала изворни ко̂д</comment>
+ <comment xml:lang="sv">Scala-källkod</comment>
+ <comment xml:lang="tr">Scala kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний код мовою Scala</comment>
+ <comment xml:lang="zh_CN">Scala 源代码</comment>
+ <comment xml:lang="zh_TW">Scala 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.scala"/>
+ </mime-type>
+ <mime-type type="text/x-scheme">
+ <comment>Scheme source code</comment>
+ <comment xml:lang="ar">شفرة مصدر Scheme</comment>
+ <comment xml:lang="az">Sxem mənbə kodu</comment>
+ <comment xml:lang="be@latin">Kryničny kod Scheme</comment>
+ <comment xml:lang="bg">Изходен код — Scheme</comment>
+ <comment xml:lang="ca">codi font en Scheme</comment>
+ <comment xml:lang="cs">zdrojový kód Scheme</comment>
+ <comment xml:lang="cy">Ffynhonnell Rhaglen Scheme</comment>
+ <comment xml:lang="da">Schemekildekode</comment>
+ <comment xml:lang="de">Scheme-Quelltext</comment>
+ <comment xml:lang="el">Πηγαίος κώδικας Scheme</comment>
+ <comment xml:lang="en_GB">Scheme source code</comment>
+ <comment xml:lang="eo">Scheme-fontkodo</comment>
+ <comment xml:lang="es">código fuente en Scheme</comment>
+ <comment xml:lang="eu">Scheme iturburu-kodea</comment>
+ <comment xml:lang="fi">Scheme-lähdekoodi</comment>
+ <comment xml:lang="fo">Scheme keldukota</comment>
+ <comment xml:lang="fr">code source Scheme</comment>
+ <comment xml:lang="ga">cód foinseach Scheme</comment>
+ <comment xml:lang="gl">código fonte en Scheme</comment>
+ <comment xml:lang="he">קוד מקור של Scheme</comment>
+ <comment xml:lang="hr">Scheme izvorni kôd</comment>
+ <comment xml:lang="hu">Scheme-forráskód</comment>
+ <comment xml:lang="ia">Codice-fonte Scheme</comment>
+ <comment xml:lang="id">Kode program Scheme</comment>
+ <comment xml:lang="it">Codice sorgente Scheme</comment>
+ <comment xml:lang="ja">Scheme ソースコード</comment>
+ <comment xml:lang="kk">Scheme бастапқы коды</comment>
+ <comment xml:lang="ko">Scheme 소스 코드</comment>
+ <comment xml:lang="lt">Scheme pradinis kodas</comment>
+ <comment xml:lang="lv">Scheme pirmkods</comment>
+ <comment xml:lang="ms">Kod sumber Scheme</comment>
+ <comment xml:lang="nb">Scheme-kildekode</comment>
+ <comment xml:lang="nl">Scheme-broncode</comment>
+ <comment xml:lang="nn">Scheme-kjeldekode</comment>
+ <comment xml:lang="oc">còde font Scheme</comment>
+ <comment xml:lang="pl">Kod źródłowy Scheme</comment>
+ <comment xml:lang="pt">código origem Scheme</comment>
+ <comment xml:lang="pt_BR">Código-fonte Scheme</comment>
+ <comment xml:lang="ro">Cod sursă Scheme</comment>
+ <comment xml:lang="ru">Исходный код Scheme</comment>
+ <comment xml:lang="sk">Zdrojový kód Scheme</comment>
+ <comment xml:lang="sl">Datoteka izvorne kode Scheme</comment>
+ <comment xml:lang="sq">Kod burues Scheme</comment>
+ <comment xml:lang="sr">Шемски изворни ко̂д</comment>
+ <comment xml:lang="sv">Scheme-källkod</comment>
+ <comment xml:lang="tr">Scheme kaynak kodu</comment>
+ <comment xml:lang="uk">вихідний файл мовою Scheme</comment>
+ <comment xml:lang="vi">Mã nguồn Scheme</comment>
+ <comment xml:lang="zh_CN">Scheme 源代码</comment>
+ <comment xml:lang="zh_TW">Scheme 源碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.scm"/>
+ <glob pattern="*.ss"/>
+ </mime-type>
+ <mime-type type="text/x-scss">
+ <comment>Sass CSS pre-processor file</comment>
+ <comment xml:lang="ca">fitxer preprocessador CSS Sass</comment>
+ <comment xml:lang="cs">soubor preprocesoru Sass CSS</comment>
+ <comment xml:lang="da">Sass CSS-forbrænderfil</comment>
+ <comment xml:lang="de">Sass-CSS-Präprozessordatei</comment>
+ <comment xml:lang="en_GB">Sass CSS pre-processor file</comment>
+ <comment xml:lang="es">archivo de preprocesador de CSS Sass</comment>
+ <comment xml:lang="fr">fichier de prétraitement CSS Sass</comment>
+ <comment xml:lang="ga">comhad réamhphróiseálaí CSS Sass</comment>
+ <comment xml:lang="he">קובץ קדם עיבוד Sass CSS</comment>
+ <comment xml:lang="hr">Sass CSS datoteka predobrade</comment>
+ <comment xml:lang="hu">Sass CSS előfeldolgozó fájl</comment>
+ <comment xml:lang="id">berkas pre-processor Sass CSS</comment>
+ <comment xml:lang="it">File CSS Sass</comment>
+ <comment xml:lang="kk">Sass CSS препроцессор файлы</comment>
+ <comment xml:lang="ko">Sass CSS 전처리기 파일</comment>
+ <comment xml:lang="pl">Plik preprocesora CSS Sass</comment>
+ <comment xml:lang="pt_BR">Arquivo de pré-processamento Sass CSS</comment>
+ <comment xml:lang="ru">Файл препроцессора Sass CSS</comment>
+ <comment xml:lang="sk">Súbor Sass CSS pre-procesora</comment>
+ <comment xml:lang="sr">датотека Сас ЦСС пре-процесора</comment>
+ <comment xml:lang="sv">Sass CSS-preprocessorfil</comment>
+ <comment xml:lang="tr">Sass CSS önişlemci dosyası</comment>
+ <comment xml:lang="uk">файл препроцесора CSS Sass</comment>
+ <comment xml:lang="zh_CN">Sass CSS 预处理器文件</comment>
+ <comment xml:lang="zh_TW">Sass CSS 處理器前檔案</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.scss"/>
+ <generic-icon name="text-x-generic"/>
+ </mime-type>
+ <mime-type type="text/x-setext">
+ <comment>Setext document</comment>
+ <comment xml:lang="ar">مستند Setext</comment>
+ <comment xml:lang="ast">Documentu Setext</comment>
+ <comment xml:lang="az">Setext sənədi</comment>
+ <comment xml:lang="be@latin">Dakument Setext</comment>
+ <comment xml:lang="bg">Документ — Setext</comment>
+ <comment xml:lang="ca">document Setext</comment>
+ <comment xml:lang="cs">dokument Setext</comment>
+ <comment xml:lang="cy">Dogfen Setext</comment>
+ <comment xml:lang="da">Setextdokument</comment>
+ <comment xml:lang="de">Setext-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Setext</comment>
+ <comment xml:lang="en_GB">Setext document</comment>
+ <comment xml:lang="eo">Setext-dokumento</comment>
+ <comment xml:lang="es">documento Setext</comment>
+ <comment xml:lang="eu">Setext dokumentua</comment>
+ <comment xml:lang="fi">Setext-asiakirja</comment>
+ <comment xml:lang="fo">Setext skjal</comment>
+ <comment xml:lang="fr">document Setext</comment>
+ <comment xml:lang="ga">cáipéis Setext</comment>
+ <comment xml:lang="gl">documento Settext</comment>
+ <comment xml:lang="he">מסמך של Setext</comment>
+ <comment xml:lang="hr">Setext dokument</comment>
+ <comment xml:lang="hu">Setext-dokumentum</comment>
+ <comment xml:lang="ia">Documento Setext</comment>
+ <comment xml:lang="id">Dokumen Setext</comment>
+ <comment xml:lang="it">Documento Setext</comment>
+ <comment xml:lang="ja">Setext ドキュメント</comment>
+ <comment xml:lang="kk">Setext құжаты</comment>
+ <comment xml:lang="ko">Setext 문서</comment>
+ <comment xml:lang="lt">Setext dokumentas</comment>
+ <comment xml:lang="lv">Setext dokuments</comment>
+ <comment xml:lang="ms">Dokumen Setext</comment>
+ <comment xml:lang="nb">Setext-dokument</comment>
+ <comment xml:lang="nl">Setext-document</comment>
+ <comment xml:lang="nn">Setext-dokument</comment>
+ <comment xml:lang="oc">document Setext</comment>
+ <comment xml:lang="pl">Dokument Setext</comment>
+ <comment xml:lang="pt">documento Setext</comment>
+ <comment xml:lang="pt_BR">Documento Setext</comment>
+ <comment xml:lang="ro">Document Setext</comment>
+ <comment xml:lang="ru">Документ Setext</comment>
+ <comment xml:lang="sk">Dokument Setext</comment>
+ <comment xml:lang="sl">Dokument Setext</comment>
+ <comment xml:lang="sq">Dokument Setext</comment>
+ <comment xml:lang="sr">Сетекст документ</comment>
+ <comment xml:lang="sv">Setext-dokument</comment>
+ <comment xml:lang="tr">Setext belgesi</comment>
+ <comment xml:lang="uk">документ Setext</comment>
+ <comment xml:lang="vi">Tài liệu Setext</comment>
+ <comment xml:lang="zh_CN">Setext 文档</comment>
+ <comment xml:lang="zh_TW">Setext 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.etx"/>
+ </mime-type>
+ <mime-type type="application/sql">
+ <comment>SQL code</comment>
+ <comment xml:lang="ar">شفرة SQL</comment>
+ <comment xml:lang="az">SQL kodu</comment>
+ <comment xml:lang="be@latin">Kod SQL</comment>
+ <comment xml:lang="bg">Код — SQL</comment>
+ <comment xml:lang="ca">codi en SQL</comment>
+ <comment xml:lang="cs">kód SQL</comment>
+ <comment xml:lang="cy">Côd SQL</comment>
+ <comment xml:lang="da">SQL-kode</comment>
+ <comment xml:lang="de">SQL-Befehle</comment>
+ <comment xml:lang="el">Κώδικας SQL</comment>
+ <comment xml:lang="en_GB">SQL code</comment>
+ <comment xml:lang="eo">SQL-kodo</comment>
+ <comment xml:lang="es">código SQL</comment>
+ <comment xml:lang="eu">SQL kodea</comment>
+ <comment xml:lang="fi">SQL-koodi</comment>
+ <comment xml:lang="fo">SQL kota</comment>
+ <comment xml:lang="fr">code SQL</comment>
+ <comment xml:lang="ga">cód SQL</comment>
+ <comment xml:lang="gl">código SQL</comment>
+ <comment xml:lang="he">קוד SQL</comment>
+ <comment xml:lang="hr">SQL kôd</comment>
+ <comment xml:lang="hu">SQL-kód</comment>
+ <comment xml:lang="ia">Codice SQL</comment>
+ <comment xml:lang="id">Kode SQL</comment>
+ <comment xml:lang="it">Codice SQL</comment>
+ <comment xml:lang="ja">SQL コード</comment>
+ <comment xml:lang="kk">SQL коды</comment>
+ <comment xml:lang="ko">SQL 코드</comment>
+ <comment xml:lang="lt">SQL kodas</comment>
+ <comment xml:lang="lv">SQL kods</comment>
+ <comment xml:lang="ms">Kod SQL</comment>
+ <comment xml:lang="nb">SQL-kildekode</comment>
+ <comment xml:lang="nl">SQL-code</comment>
+ <comment xml:lang="nn">SQL-kode</comment>
+ <comment xml:lang="oc">còde SQL</comment>
+ <comment xml:lang="pl">Kod SQL</comment>
+ <comment xml:lang="pt">código SQL</comment>
+ <comment xml:lang="pt_BR">Código SQL</comment>
+ <comment xml:lang="ro">Cod SQL</comment>
+ <comment xml:lang="ru">Код SQL</comment>
+ <comment xml:lang="sk">Kód SQL</comment>
+ <comment xml:lang="sl">Datoteka kode SQL</comment>
+ <comment xml:lang="sq">Kod SQL</comment>
+ <comment xml:lang="sr">СКуЛ ко̂д</comment>
+ <comment xml:lang="sv">SQL-kod</comment>
+ <comment xml:lang="tr">SQL kodu</comment>
+ <comment xml:lang="uk">код SQL</comment>
+ <comment xml:lang="vi">Mã SQL</comment>
+ <comment xml:lang="zh_CN">SQL 代码</comment>
+ <comment xml:lang="zh_TW">SQL 程式碼</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.sql"/>
+ <alias type="text/x-sql"/>
+ </mime-type>
+ <mime-type type="text/x-tcl">
+ <comment>Tcl script</comment>
+ <comment xml:lang="ar">سكربت Tcl</comment>
+ <comment xml:lang="be@latin">Skrypt Tcl</comment>
+ <comment xml:lang="bg">Скрипт — Tcl</comment>
+ <comment xml:lang="ca">script Tcl</comment>
+ <comment xml:lang="cs">skript Tcl</comment>
+ <comment xml:lang="da">Tcl-program</comment>
+ <comment xml:lang="de">Tcl-Skript</comment>
+ <comment xml:lang="el">Δέσμη ενεργειών Tcl</comment>
+ <comment xml:lang="en_GB">Tcl script</comment>
+ <comment xml:lang="eo">Tcl-skripto</comment>
+ <comment xml:lang="es">secuencia de órdenes en Tcl</comment>
+ <comment xml:lang="eu">Tcl script-a</comment>
+ <comment xml:lang="fi">Tcl-komentotiedosto</comment>
+ <comment xml:lang="fo">Tcl boðrøð</comment>
+ <comment xml:lang="fr">script Tcl</comment>
+ <comment xml:lang="ga">script Tcl</comment>
+ <comment xml:lang="gl">Script en Tcl</comment>
+ <comment xml:lang="he">תסריט Tcl</comment>
+ <comment xml:lang="hr">Tcl skripta</comment>
+ <comment xml:lang="hu">Tcl-parancsfájl</comment>
+ <comment xml:lang="ia">Script Tcl</comment>
+ <comment xml:lang="id">Skrip Tcl</comment>
+ <comment xml:lang="it">Script Tcl</comment>
+ <comment xml:lang="ja">Tcl スクリプト</comment>
+ <comment xml:lang="kk">Tcl сценарийі</comment>
+ <comment xml:lang="ko">Tcl 스크립트</comment>
+ <comment xml:lang="lt">Tcl scenarijus</comment>
+ <comment xml:lang="lv">Tcl skripts</comment>
+ <comment xml:lang="ms">Skrip Tcl</comment>
+ <comment xml:lang="nb">Tcl-skript</comment>
+ <comment xml:lang="nl">Tcl-script</comment>
+ <comment xml:lang="nn">Tcl-skript</comment>
+ <comment xml:lang="oc">escript Tcl</comment>
+ <comment xml:lang="pl">Skrypt Tcl</comment>
+ <comment xml:lang="pt">script Tcl</comment>
+ <comment xml:lang="pt_BR">Script Tcl</comment>
+ <comment xml:lang="ro">Script Tcl</comment>
+ <comment xml:lang="ru">Сценарий Tcl</comment>
+ <comment xml:lang="sk">Skript Tcl</comment>
+ <comment xml:lang="sl">Skriptna datoteka Tcl</comment>
+ <comment xml:lang="sq">Script Tcl</comment>
+ <comment xml:lang="sr">Тцл скрипта</comment>
+ <comment xml:lang="sv">Tcl-skript</comment>
+ <comment xml:lang="tr">Tcl betiği</comment>
+ <comment xml:lang="uk">скрипт Tcl</comment>
+ <comment xml:lang="vi">Văn lệnh Tcl</comment>
+ <comment xml:lang="zh_CN">Tcl 脚本</comment>
+ <comment xml:lang="zh_TW">Tcl 描述語言檔</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.tcl"/>
+ <glob pattern="*.tk"/>
+ </mime-type>
+ <mime-type type="text/x-tex">
+ <comment>TeX document</comment>
+ <comment xml:lang="ar">مستند TeX</comment>
+ <comment xml:lang="ast">Documentu TeX</comment>
+ <comment xml:lang="be@latin">Dakument TeX</comment>
+ <comment xml:lang="bg">Документ — TeX</comment>
+ <comment xml:lang="ca">document TeX</comment>
+ <comment xml:lang="cs">dokument TeX</comment>
+ <comment xml:lang="cy">Dogfen TeX </comment>
+ <comment xml:lang="da">TeX-dokument</comment>
+ <comment xml:lang="de">TeX-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο TeX</comment>
+ <comment xml:lang="en_GB">TeX document</comment>
+ <comment xml:lang="eo">TeX-dokumento</comment>
+ <comment xml:lang="es">documento de TeX</comment>
+ <comment xml:lang="eu">TeX dokumentua</comment>
+ <comment xml:lang="fi">TeX-asiakirja</comment>
+ <comment xml:lang="fo">TeX skjal</comment>
+ <comment xml:lang="fr">document TeX</comment>
+ <comment xml:lang="ga">cáipéis TeX</comment>
+ <comment xml:lang="gl">documenton TeX</comment>
+ <comment xml:lang="he">מסמך TeX</comment>
+ <comment xml:lang="hr">TeX dokument</comment>
+ <comment xml:lang="hu">TeX-dokumentum</comment>
+ <comment xml:lang="ia">Documento TeX</comment>
+ <comment xml:lang="id">Dokumen TeX</comment>
+ <comment xml:lang="it">Documento TeX</comment>
+ <comment xml:lang="ja">TeX ドキュメント</comment>
+ <comment xml:lang="kk">TeX құжаты</comment>
+ <comment xml:lang="ko">TeX 문서</comment>
+ <comment xml:lang="lt">TeX dokumentas</comment>
+ <comment xml:lang="lv">TeX dokuments</comment>
+ <comment xml:lang="ms">Dokumen TeX</comment>
+ <comment xml:lang="nb">TeX-dokument</comment>
+ <comment xml:lang="nl">TeX-document</comment>
+ <comment xml:lang="nn">TeX-dokument</comment>
+ <comment xml:lang="oc">document TeX</comment>
+ <comment xml:lang="pl">Dokument TeX</comment>
+ <comment xml:lang="pt">documento TeX</comment>
+ <comment xml:lang="pt_BR">Documento TeX</comment>
+ <comment xml:lang="ro">Document TeX</comment>
+ <comment xml:lang="ru">Документ TeX</comment>
+ <comment xml:lang="sk">Dokument TeX</comment>
+ <comment xml:lang="sl">Dokument TeX</comment>
+ <comment xml:lang="sq">Dokument TeX</comment>
+ <comment xml:lang="sr">ТеКс документ</comment>
+ <comment xml:lang="sv">TeX-dokument</comment>
+ <comment xml:lang="tr">TeX belgesi</comment>
+ <comment xml:lang="uk">документ TeX</comment>
+ <comment xml:lang="vi">Tài liệu TeX</comment>
+ <comment xml:lang="zh_CN">TeX 文档</comment>
+ <comment xml:lang="zh_TW">TeX 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <alias type="application/x-tex"/>
+ <glob pattern="*.tex"/>
+ <glob pattern="*.ltx"/>
+ <glob pattern="*.sty"/>
+ <glob pattern="*.cls"/>
+ <glob pattern="*.dtx"/>
+ <glob pattern="*.ins"/>
+ <glob pattern="*.latex"/>
+ <magic priority="10">
+ <match value="%" type="string" offset="0"/>
+ </magic>
+ <magic priority="50">
+ <match value="documentclass" type="string" offset="1"/>
+ </magic>
+ </mime-type>
+ <mime-type type="text/x-texinfo">
+ <comment>TeXInfo document</comment>
+ <comment xml:lang="ar">مستند TeXInfo</comment>
+ <comment xml:lang="ast">Documentu TeXInfo</comment>
+ <comment xml:lang="az">TeXInfo sənədi</comment>
+ <comment xml:lang="be@latin">Dakument TeXInfo</comment>
+ <comment xml:lang="bg">Документ — TeXInfo</comment>
+ <comment xml:lang="ca">document TeXInfo</comment>
+ <comment xml:lang="cs">dokument TeXInfo</comment>
+ <comment xml:lang="cy">Dogfen TeXInfo</comment>
+ <comment xml:lang="da">TeXInfo-dokument</comment>
+ <comment xml:lang="de">TeXInfo-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο TeXInfo</comment>
+ <comment xml:lang="en_GB">TeXInfo document</comment>
+ <comment xml:lang="eo">TeXInfo-dokumento</comment>
+ <comment xml:lang="es">documento de TeXInfo</comment>
+ <comment xml:lang="eu">TeXInfo dokumentua</comment>
+ <comment xml:lang="fi">TeXInfo-asiakirja</comment>
+ <comment xml:lang="fo">TeXInfo skjal</comment>
+ <comment xml:lang="fr">document TeXInfo</comment>
+ <comment xml:lang="ga">cáipéis TeXInfo</comment>
+ <comment xml:lang="gl">documento TeXInfo</comment>
+ <comment xml:lang="he">מסמך של TeXInfo</comment>
+ <comment xml:lang="hr">TeXInfo dokument</comment>
+ <comment xml:lang="hu">TeXInfo-dokumentum</comment>
+ <comment xml:lang="ia">Documento TeXInfo</comment>
+ <comment xml:lang="id">Dokumen TeXInfo</comment>
+ <comment xml:lang="it">Documento TeXInfo</comment>
+ <comment xml:lang="ja">TeXInfo ドキュメント</comment>
+ <comment xml:lang="kk">TeXInfo құжаты</comment>
+ <comment xml:lang="ko">TeXInfo 문서</comment>
+ <comment xml:lang="lt">TeXInfo dokumentas</comment>
+ <comment xml:lang="lv">TeXInfo dokuments</comment>
+ <comment xml:lang="ms">Dokumen TeXInfo</comment>
+ <comment xml:lang="nb">TeXInfo-dokument</comment>
+ <comment xml:lang="nl">TeXInfo-document</comment>
+ <comment xml:lang="nn">TeXInfo-dokument</comment>
+ <comment xml:lang="oc">document TeXInfo</comment>
+ <comment xml:lang="pl">Dokument TeXInfo</comment>
+ <comment xml:lang="pt">documento TeXInfo</comment>
+ <comment xml:lang="pt_BR">Documento TeXInfo</comment>
+ <comment xml:lang="ro">Document TexInfo</comment>
+ <comment xml:lang="ru">Документ TeXInfo</comment>
+ <comment xml:lang="sk">Dokument TeXInfo</comment>
+ <comment xml:lang="sl">Dokument TeXInfo</comment>
+ <comment xml:lang="sq">Dokument TeXInfo</comment>
+ <comment xml:lang="sr">ТеКсинфо документ</comment>
+ <comment xml:lang="sv">TeXInfo-dokument</comment>
+ <comment xml:lang="tr">TeXInfo belgesi</comment>
+ <comment xml:lang="uk">документ TeXInfo</comment>
+ <comment xml:lang="vi">Tài liệu TeXInfo</comment>
+ <comment xml:lang="zh_CN">TeXInfo 文档</comment>
+ <comment xml:lang="zh_TW">TeXInfo 文件</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.texi"/>
+ <glob pattern="*.texinfo"/>
+ </mime-type>
+ <mime-type type="text/x-troff-me">
+ <comment>Troff ME input document</comment>
+ <comment xml:lang="ar">مستند Troff ME input</comment>
+ <comment xml:lang="ast">Documentu d'entrada de Troff ME</comment>
+ <comment xml:lang="be@latin">Uvodny dakument Troff ME</comment>
+ <comment xml:lang="bg">Изходен документ — Troff ME</comment>
+ <comment xml:lang="ca">document d'entrada Troff ME</comment>
+ <comment xml:lang="cs">vstupní dokument Troff ME</comment>
+ <comment xml:lang="da">Troff ME inddata-dokument</comment>
+ <comment xml:lang="de">Troff-ME-Eingabedokument</comment>
+ <comment xml:lang="el">Έγγραφο εντολών troff ME</comment>
+ <comment xml:lang="en_GB">Troff ME input document</comment>
+ <comment xml:lang="eo">eniga dokumento de Troff ME</comment>
+ <comment xml:lang="es">documento de entrada Troff ME</comment>
+ <comment xml:lang="eu">Troff ME sarrerako dokumentua</comment>
+ <comment xml:lang="fi">Troff ME -syöteasiakirja</comment>
+ <comment xml:lang="fo">Troff ME inntaksskjal</comment>
+ <comment xml:lang="fr">document d'entrée Troff ME</comment>
+ <comment xml:lang="ga">cáipéis ionchurtha Troff ME</comment>
+ <comment xml:lang="gl">documento de entrada Troff ME</comment>
+ <comment xml:lang="he">מסמך קלט של Troff ME</comment>
+ <comment xml:lang="hr">Troff ME ulazni dokument</comment>
+ <comment xml:lang="hu">Troff ME bemeneti dokumentum</comment>
+ <comment xml:lang="ia">Documento de entrata Troff ME</comment>
+ <comment xml:lang="id">Dokumen masukan Troff ME</comment>
+ <comment xml:lang="it">Documento di input Troff ME</comment>
+ <comment xml:lang="ja">Troff ME 入力ドキュメント</comment>
+ <comment xml:lang="kk">Troff ME кіріс құжаты</comment>
+ <comment xml:lang="ko">Troff ME 입력 문서</comment>
+ <comment xml:lang="lt">Troff ME įvesties dokumentas</comment>
+ <comment xml:lang="lv">Troff ME ievades dokuments</comment>
+ <comment xml:lang="ms">Dokumen input Troff ME</comment>
+ <comment xml:lang="nb">Troff ME-inndatadokument</comment>
+ <comment xml:lang="nl">Troff ME-invoerdocument</comment>
+ <comment xml:lang="nn">Troff ME inndata-dokument</comment>
+ <comment xml:lang="oc">document d'entrada Troff ME</comment>
+ <comment xml:lang="pl">Dokument wejściowy Troff ME</comment>
+ <comment xml:lang="pt">documento origem Troff ME</comment>
+ <comment xml:lang="pt_BR">Documento de entrada Troff ME</comment>
+ <comment xml:lang="ro">Document intrare Troff ME</comment>
+ <comment xml:lang="ru">Входной документ Troff ME</comment>
+ <comment xml:lang="sk">Vstupný dokument Troff ME</comment>
+ <comment xml:lang="sl">Vnosni dokument Troff ME</comment>
+ <comment xml:lang="sq">Dokument i input Troff ME</comment>
+ <comment xml:lang="sr">Трофф МЕ улазни документ</comment>
+ <comment xml:lang="sv">Troff ME-indatadokument</comment>
+ <comment xml:lang="tr">Troff ME girdi belgesi</comment>
+ <comment xml:lang="uk">вхідний документ Troff ME</comment>
+ <comment xml:lang="vi">Tài liệu nhập ME Troff</comment>
+ <comment xml:lang="zh_CN">Troff ME 输入文档</comment>
+ <comment xml:lang="zh_TW">Troff ME 輸入文件</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.me"/>
+ </mime-type>
+ <mime-type type="text/x-troff-mm">
+ <comment>Troff MM input document</comment>
+ <comment xml:lang="ar">مستند Troff MM input</comment>
+ <comment xml:lang="ast">Documentu d'entrada de Troff MM</comment>
+ <comment xml:lang="be@latin">Uvodny dakument Troff MM</comment>
+ <comment xml:lang="bg">Изходен документ — Troff MM</comment>
+ <comment xml:lang="ca">document d'entrada Troff MM</comment>
+ <comment xml:lang="cs">vstupní dokument Troff MM</comment>
+ <comment xml:lang="da">Troff MM inddata-dokument</comment>
+ <comment xml:lang="de">Troff-MM-Eingabedokument</comment>
+ <comment xml:lang="el">Έγγραφο εντολών troff MM</comment>
+ <comment xml:lang="en_GB">Troff MM input document</comment>
+ <comment xml:lang="eo">eniga dokumento de Troff MM</comment>
+ <comment xml:lang="es">documento de entrada Troff MM</comment>
+ <comment xml:lang="eu">Troff MM sarrerako dokumentua</comment>
+ <comment xml:lang="fi">Troff MM -syöteasiakirja</comment>
+ <comment xml:lang="fo">Troff MM inntaksskjal</comment>
+ <comment xml:lang="fr">document d'entrée Troff MM</comment>
+ <comment xml:lang="ga">cáipéis ionchurtha Troff MM</comment>
+ <comment xml:lang="gl">documento de entrada Troff MM</comment>
+ <comment xml:lang="he">מסמך קלט של Troff MM</comment>
+ <comment xml:lang="hr">Troff MM ulazni dokument</comment>
+ <comment xml:lang="hu">Troff MM bemeneti dokumentum</comment>
+ <comment xml:lang="ia">Documento de entrata Troff MM</comment>
+ <comment xml:lang="id">Dokumen masukan Troff MM</comment>
+ <comment xml:lang="it">Documento di input Troff MM</comment>
+ <comment xml:lang="ja">Troff MM 入力ドキュメント</comment>
+ <comment xml:lang="kk">Troff MM кіріс құжаты</comment>
+ <comment xml:lang="ko">Troff MM 입력 문서</comment>
+ <comment xml:lang="lt">Troff MM įvesties dokumentas</comment>
+ <comment xml:lang="lv">Troff MM ievades dokuments</comment>
+ <comment xml:lang="ms">Dokumen input Troff MM</comment>
+ <comment xml:lang="nb">Troff MM-inndatadokument</comment>
+ <comment xml:lang="nl">Troff MM-invoerdocument</comment>
+ <comment xml:lang="nn">Troff MM inndata-dokument</comment>
+ <comment xml:lang="oc">document d'entrada Troff MM</comment>
+ <comment xml:lang="pl">Dokument wejściowy Troff MM</comment>
+ <comment xml:lang="pt">documento origem Troff MM</comment>
+ <comment xml:lang="pt_BR">Documento de entrada Troff MM</comment>
+ <comment xml:lang="ro">Document intrare Troff MM</comment>
+ <comment xml:lang="ru">Входной документ Troff MM</comment>
+ <comment xml:lang="sk">Vstupný dokument Troff MM</comment>
+ <comment xml:lang="sl">Vnosni dokument Troff MM</comment>
+ <comment xml:lang="sq">Dokument i input Troff MM</comment>
+ <comment xml:lang="sr">Трофф ММ улазни документ</comment>
+ <comment xml:lang="sv">Troff MM-indatadokument</comment>
+ <comment xml:lang="tr">Troff MM girdi belgesi</comment>
+ <comment xml:lang="uk">вхідний документ Troff MM</comment>
+ <comment xml:lang="vi">Tài liệu nhập MM Troff</comment>
+ <comment xml:lang="zh_CN">Troff MM 输入文档</comment>
+ <comment xml:lang="zh_TW">Troff MM 輸入文件</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.mm"/>
+ </mime-type>
+ <mime-type type="text/x-troff-ms">
+ <comment>Troff MS input document</comment>
+ <comment xml:lang="ar">مستند Troff MS input</comment>
+ <comment xml:lang="ast">Documentu d'entrada de Troff MS</comment>
+ <comment xml:lang="be@latin">Uvodny dakument Troff MS</comment>
+ <comment xml:lang="bg">Изходен документ — Troff MS</comment>
+ <comment xml:lang="ca">document d'entrada Troff MS</comment>
+ <comment xml:lang="cs">vstupní dokument Troff MS</comment>
+ <comment xml:lang="da">Troff MS inddata-dokument</comment>
+ <comment xml:lang="de">Troff-MS-Eingabedokument</comment>
+ <comment xml:lang="el">Έγγραφο εντολών troff MS</comment>
+ <comment xml:lang="en_GB">Troff MS input document</comment>
+ <comment xml:lang="eo">eniga dokumento de Troff MS</comment>
+ <comment xml:lang="es">documento de entrada Troff MS</comment>
+ <comment xml:lang="eu">Troff MS sarrerako dokumentua</comment>
+ <comment xml:lang="fi">Troff MS -syöteasiakirja</comment>
+ <comment xml:lang="fo">Troff MS inntaksskjal</comment>
+ <comment xml:lang="fr">document d'entrée Troff MS</comment>
+ <comment xml:lang="ga">cáipéis ionchurtha Troff MS</comment>
+ <comment xml:lang="gl">documento de entrada Troff MS</comment>
+ <comment xml:lang="he">מסמך קלט של Troff MS</comment>
+ <comment xml:lang="hr">Troff MS ulazni dokument</comment>
+ <comment xml:lang="hu">Troff MS bemeneti dokumentum</comment>
+ <comment xml:lang="ia">Documento de entrata Troff MS</comment>
+ <comment xml:lang="id">Dokumen masukan Troff MS</comment>
+ <comment xml:lang="it">Documento di input Troff MS</comment>
+ <comment xml:lang="ja">Troff MS 入力ドキュメント</comment>
+ <comment xml:lang="kk">Troff MS кіріс құжаты</comment>
+ <comment xml:lang="ko">Troff MS 입력 문서</comment>
+ <comment xml:lang="lt">Troff MS įvesties dokumentas</comment>
+ <comment xml:lang="lv">Troff MS ievades dokuments</comment>
+ <comment xml:lang="ms">Dokumen input Troff MS</comment>
+ <comment xml:lang="nb">Troff MS-inndatadokument</comment>
+ <comment xml:lang="nl">Troff MS-invoerdocument</comment>
+ <comment xml:lang="nn">Troff MS inndata-dokument</comment>
+ <comment xml:lang="oc">document d'entrada Troff MS</comment>
+ <comment xml:lang="pl">Dokument wejściowy Troff MS</comment>
+ <comment xml:lang="pt">documento origem Troff MS</comment>
+ <comment xml:lang="pt_BR">Documento de entrada Troff MS</comment>
+ <comment xml:lang="ro">Document intrare Troff MS</comment>
+ <comment xml:lang="ru">Входной документ Troff MS</comment>
+ <comment xml:lang="sk">Vstupný dokument Troff MS</comment>
+ <comment xml:lang="sl">Vnosni dokument Troff MS</comment>
+ <comment xml:lang="sq">Dokument i input Troff MS</comment>
+ <comment xml:lang="sr">Трофф МС улазни документ</comment>
+ <comment xml:lang="sv">Troff MS-indatadokument</comment>
+ <comment xml:lang="tr">Troff MS girdi belgesi</comment>
+ <comment xml:lang="uk">вхідний документ Troff MS</comment>
+ <comment xml:lang="vi">Tài liệu nhập MS Troff</comment>
+ <comment xml:lang="zh_CN">Troff MS 输入文档</comment>
+ <comment xml:lang="zh_TW">Troff MS 輸入文件</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.ms"/>
+ </mime-type>
+ <mime-type type="text/x-twig">
+ <comment>Twig template</comment>
+ <comment xml:lang="ca">plantilla Twig</comment>
+ <comment xml:lang="cs">šablona Twig</comment>
+ <comment xml:lang="da">Twig-skabelon</comment>
+ <comment xml:lang="de">Twig-Vorlage</comment>
+ <comment xml:lang="en_GB">Twig template</comment>
+ <comment xml:lang="es">plantilla de Twig</comment>
+ <comment xml:lang="eu">Twig txantiloia</comment>
+ <comment xml:lang="fr">modèle Twig</comment>
+ <comment xml:lang="ga">teimpléad Twig</comment>
+ <comment xml:lang="he">תבנית Twig</comment>
+ <comment xml:lang="hr">Twig predložak</comment>
+ <comment xml:lang="hu">Twig-sablon</comment>
+ <comment xml:lang="id">templat Twig</comment>
+ <comment xml:lang="it">Modello twig</comment>
+ <comment xml:lang="kk">Twig үлгісі</comment>
+ <comment xml:lang="ko">Twig 문서 서식</comment>
+ <comment xml:lang="pl">Szablon Twig</comment>
+ <comment xml:lang="pt_BR">Modelo Twig</comment>
+ <comment xml:lang="ru">Шаблон Twig</comment>
+ <comment xml:lang="sk">Šablóna Twig</comment>
+ <comment xml:lang="sr">Твиг шаблон</comment>
+ <comment xml:lang="sv">Twig-mall</comment>
+ <comment xml:lang="tr">Twig şablonu</comment>
+ <comment xml:lang="uk">шаблон twig</comment>
+ <comment xml:lang="zh_CN">Twig 模板</comment>
+ <comment xml:lang="zh_TW">Twig 範本</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.twig"/>
+ <generic-icon name="text-x-generic-template"/>
+ </mime-type>
+ <mime-type type="text/x-uil">
+ <comment>X-Motif UIL table</comment>
+ <comment xml:lang="ar">جدول X-Motif UIL</comment>
+ <comment xml:lang="be@latin">Tablica X-Motif UIL</comment>
+ <comment xml:lang="bg">Таблица — X-Motif UIL</comment>
+ <comment xml:lang="ca">taula UIL de X-Motif</comment>
+ <comment xml:lang="cs">tabulka X-Motif UIL</comment>
+ <comment xml:lang="da">X-Motif UIL-tabel</comment>
+ <comment xml:lang="de">X-Motif-UIL-Tabelle</comment>
+ <comment xml:lang="el">Πίνακας X-Motif UIL</comment>
+ <comment xml:lang="en_GB">X-Motif UIL table</comment>
+ <comment xml:lang="es">tabla de X-Motif UIL</comment>
+ <comment xml:lang="eu">X-Motif UIL taula</comment>
+ <comment xml:lang="fi">X-Motif UIL -taulukko</comment>
+ <comment xml:lang="fo">X-Motif UIL talva</comment>
+ <comment xml:lang="fr">table X-Motif UIL</comment>
+ <comment xml:lang="ga">tábla X-Motif UIL</comment>
+ <comment xml:lang="gl">Táboa de X-Motif UIL</comment>
+ <comment xml:lang="he">טבלה של X-Motif UIL</comment>
+ <comment xml:lang="hr">X-Motif UIL tablica</comment>
+ <comment xml:lang="hu">X-Motif UIL-táblázat</comment>
+ <comment xml:lang="ia">Tabella X-Motif UIL</comment>
+ <comment xml:lang="id">Tabel X-Motif UIL</comment>
+ <comment xml:lang="it">Tabella UIL X-Motif</comment>
+ <comment xml:lang="ja">X-Motif UIL 表</comment>
+ <comment xml:lang="kk">X-Motif UIL кестесі</comment>
+ <comment xml:lang="ko">X-Motif UIL 테이블</comment>
+ <comment xml:lang="lt">X-Motif UIL lentelė</comment>
+ <comment xml:lang="lv">X-Motif UIL tabula</comment>
+ <comment xml:lang="ms">Jadual X-Motif UIL</comment>
+ <comment xml:lang="nb">X-Motif UIL-tabell</comment>
+ <comment xml:lang="nl">X-Motif UIL-tabel</comment>
+ <comment xml:lang="nn">X-Motif UIL-tabell</comment>
+ <comment xml:lang="oc">taula X-Motif UIL</comment>
+ <comment xml:lang="pl">Tabela UIL X-Motif</comment>
+ <comment xml:lang="pt">tabela UIL do X-Motif</comment>
+ <comment xml:lang="pt_BR">Tabela UIL do X-Motif</comment>
+ <comment xml:lang="ro">Tabel X-Motif UIL</comment>
+ <comment xml:lang="ru">Таблица UIL X-Motif</comment>
+ <comment xml:lang="sk">Tabuľka X-Motif UIL</comment>
+ <comment xml:lang="sl">Preglednica X-Motif UIL</comment>
+ <comment xml:lang="sq">Tabelë X-Motif UIL</comment>
+ <comment xml:lang="sr">Икс-Мотиф УИЛ табела</comment>
+ <comment xml:lang="sv">X-Motif UIL-tabell</comment>
+ <comment xml:lang="tr">X-Motif UIL tablosu</comment>
+ <comment xml:lang="uk">таблиця X-Motif UIL</comment>
+ <comment xml:lang="vi">Bảng UIL X-Motif</comment>
+ <comment xml:lang="zh_CN">X-Motif UIL 表</comment>
+ <comment xml:lang="zh_TW">X-Motif UIL 表格</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.uil"/>
+ </mime-type>
+ <mime-type type="text/x-uri">
+ <comment>resource location</comment>
+ <comment xml:lang="ar">موقع المورد</comment>
+ <comment xml:lang="be@latin">pałažeńnie resursu</comment>
+ <comment xml:lang="bg">Местоположение на ресурс</comment>
+ <comment xml:lang="ca">localització de recurs</comment>
+ <comment xml:lang="cs">umístění prostředku</comment>
+ <comment xml:lang="da">resurseplacering</comment>
+ <comment xml:lang="de">Ressourcenort</comment>
+ <comment xml:lang="el">Τοποθεσία πόρου</comment>
+ <comment xml:lang="en_GB">resource location</comment>
+ <comment xml:lang="eo">loko de risurco</comment>
+ <comment xml:lang="es">ubicación del recurso</comment>
+ <comment xml:lang="eu">baliabidearen kokalekua</comment>
+ <comment xml:lang="fi">resurssisijainti</comment>
+ <comment xml:lang="fo">tilfeingisstaður</comment>
+ <comment xml:lang="fr">localisation de ressource</comment>
+ <comment xml:lang="ga">suíomh acmhainne</comment>
+ <comment xml:lang="gl">localización do recurso</comment>
+ <comment xml:lang="he">מיקום של משאב</comment>
+ <comment xml:lang="hr">Lokacija resursa</comment>
+ <comment xml:lang="hu">erőforrás-hely</comment>
+ <comment xml:lang="ia">Loco de ressources</comment>
+ <comment xml:lang="id">lokasi sumber daya</comment>
+ <comment xml:lang="it">Posizione risorsa</comment>
+ <comment xml:lang="ja">リソースの場所</comment>
+ <comment xml:lang="kk">ресурс орналасуы</comment>
+ <comment xml:lang="ko">자원 위치</comment>
+ <comment xml:lang="lt">resurso vieta</comment>
+ <comment xml:lang="lv">resursa atrašanās vieta</comment>
+ <comment xml:lang="ms">Lokasi sumber</comment>
+ <comment xml:lang="nb">ressurslokasjon</comment>
+ <comment xml:lang="nl">bronlocatie</comment>
+ <comment xml:lang="nn">ressursplassering</comment>
+ <comment xml:lang="oc">localizacion de ressorsa</comment>
+ <comment xml:lang="pl">Położenie zasobu</comment>
+ <comment xml:lang="pt">localização de recurso</comment>
+ <comment xml:lang="pt_BR">Localização de recurso</comment>
+ <comment xml:lang="ro">locație de resursă</comment>
+ <comment xml:lang="ru">Расположение ресурса</comment>
+ <comment xml:lang="sk">Umiestnenie zdroja</comment>
+ <comment xml:lang="sl">mesto vira</comment>
+ <comment xml:lang="sq">Pozicion rezerve</comment>
+ <comment xml:lang="sr">путања изворишта</comment>
+ <comment xml:lang="sv">resursplats</comment>
+ <comment xml:lang="tr">kaynak ayırma</comment>
+ <comment xml:lang="uk">розташування ресурсу</comment>
+ <comment xml:lang="vi">địa điểm tài nguyên</comment>
+ <comment xml:lang="zh_CN">资源位置</comment>
+ <comment xml:lang="zh_TW">資源位置</comment>
+ <sub-class-of type="text/plain"/>
+
+ </mime-type>
+ <mime-type type="text/x-uuencode">
+ <comment>uuencoded file</comment>
+ <comment xml:lang="ca">fitxer uuencoded</comment>
+ <comment xml:lang="cs">soubor kódovaný pomocí uuencoding</comment>
+ <comment xml:lang="da">uuencodede-fil</comment>
+ <comment xml:lang="de">Datei im uuencode-Format</comment>
+ <comment xml:lang="el">Αρχείο κωδικοποιημένο unix σε unix (uuencoded)</comment>
+ <comment xml:lang="en_GB">uuencoded file</comment>
+ <comment xml:lang="es">archivo codificado con uuencode</comment>
+ <comment xml:lang="eu">uuencode-aturiko fitxategia</comment>
+ <comment xml:lang="fr">fichier uuencodé</comment>
+ <comment xml:lang="ga">comhad uuencoded</comment>
+ <comment xml:lang="gl">Ficheiro uuencoded</comment>
+ <comment xml:lang="he">קובץ בקידוד uu</comment>
+ <comment xml:lang="hr">uuencoded datoteka</comment>
+ <comment xml:lang="hu">uuencode-olt fájl</comment>
+ <comment xml:lang="ia">File in uuencode</comment>
+ <comment xml:lang="id">Berkas ter-uuencode</comment>
+ <comment xml:lang="it">File uuencoded</comment>
+ <comment xml:lang="ja">未エンコードファイル</comment>
+ <comment xml:lang="kk">uuencode кодталған файлы</comment>
+ <comment xml:lang="ko">uuencoded 파일</comment>
+ <comment xml:lang="lv">uu kodējuma datne</comment>
+ <comment xml:lang="oc">fichièr uuencodat</comment>
+ <comment xml:lang="pl">Plik zakodowany za pomocą uuencode</comment>
+ <comment xml:lang="pt">ficheiro uuencoded</comment>
+ <comment xml:lang="pt_BR">Arquivo codificado UUE</comment>
+ <comment xml:lang="ru">Файл, кодированный uuencode</comment>
+ <comment xml:lang="sk">Súbor v kódovaní uuencode</comment>
+ <comment xml:lang="sl">Datoteka uuencode</comment>
+ <comment xml:lang="sr">уукодирана датотека</comment>
+ <comment xml:lang="sv">uuencode-fil</comment>
+ <comment xml:lang="tr">uuencoded dosyası</comment>
+ <comment xml:lang="uk">файл даних у форматі UUE</comment>
+ <comment xml:lang="zh_CN">Uuencode 文件</comment>
+ <comment xml:lang="zh_TW">uuencoded 檔</comment>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.uue"/>
+ <magic priority="50">
+ <match value="begin " type="string" offset="0"/>
+ </magic>
+ <alias type="zz-application/zz-winassoc-uu"/>
+ </mime-type>
+ <mime-type type="text/x-xmi">
+ <comment>XMI file</comment>
+ <comment xml:lang="ar">ملف XMI</comment>
+ <comment xml:lang="be@latin">Fajł XMI</comment>
+ <comment xml:lang="bg">Файл — XMI</comment>
+ <comment xml:lang="ca">fitxer XMI</comment>
+ <comment xml:lang="cs">soubor XMI</comment>
+ <comment xml:lang="da">XMI-fil</comment>
+ <comment xml:lang="de">XMI-Datei</comment>
+ <comment xml:lang="el">Αρχείο XML</comment>
+ <comment xml:lang="en_GB">XMI file</comment>
+ <comment xml:lang="eo">XMI-dosiero</comment>
+ <comment xml:lang="es">archivo XMI</comment>
+ <comment xml:lang="eu">XMI fitxategia</comment>
+ <comment xml:lang="fi">XMI-tiedosto</comment>
+ <comment xml:lang="fo">XMI fíla</comment>
+ <comment xml:lang="fr">fichier XMI</comment>
+ <comment xml:lang="ga">comhad XMI</comment>
+ <comment xml:lang="gl">ficheiro XMI</comment>
+ <comment xml:lang="he">קובץ XMI</comment>
+ <comment xml:lang="hr">XMI datoteka</comment>
+ <comment xml:lang="hu">XMI fájl</comment>
+ <comment xml:lang="ia">File XMI</comment>
+ <comment xml:lang="id">Berkas XMI</comment>
+ <comment xml:lang="it">File XMI</comment>
+ <comment xml:lang="ja">XMI ファイル</comment>
+ <comment xml:lang="kk">XMI файлы</comment>
+ <comment xml:lang="ko">XMI 파일</comment>
+ <comment xml:lang="lt">XMI failas</comment>
+ <comment xml:lang="lv">XMI datne</comment>
+ <comment xml:lang="nb">XMI-fil</comment>
+ <comment xml:lang="nl">XMI-bestand</comment>
+ <comment xml:lang="nn">XMI-fil</comment>
+ <comment xml:lang="oc">fichièr XMI</comment>
+ <comment xml:lang="pl">Plik XMI</comment>
+ <comment xml:lang="pt">ficheiro XMI</comment>
+ <comment xml:lang="pt_BR">Arquivo XMI</comment>
+ <comment xml:lang="ro">Fișier XMI</comment>
+ <comment xml:lang="ru">Файл XMI</comment>
+ <comment xml:lang="sk">Súbor XMI</comment>
+ <comment xml:lang="sl">Datoteka XMI</comment>
+ <comment xml:lang="sq">File XMI</comment>
+ <comment xml:lang="sr">ИксМИ датотека</comment>
+ <comment xml:lang="sv">XMI-fil</comment>
+ <comment xml:lang="tr">XMI dosyası</comment>
+ <comment xml:lang="uk">файл XMI</comment>
+ <comment xml:lang="vi">Tập tin XMI</comment>
+ <comment xml:lang="zh_CN">XMI 文件</comment>
+ <comment xml:lang="zh_TW">XMI 檔</comment>
+ <acronym>XMI</acronym>
+ <expanded-acronym>XML Metadata Interchange</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <glob pattern="*.xmi"/>
+ <root-XML namespaceURI="http://schema.omg.org/spec/XMI/2.0" localName="XMI"/>
+ <root-XML namespaceURI="http://schema.omg.org/spec/XMI/2.1" localName="XMI"/>
+ </mime-type>
+ <mime-type type="text/x-xslfo">
+ <comment>XSL FO file</comment>
+ <comment xml:lang="ar">ملف XSL FO</comment>
+ <comment xml:lang="be@latin">Fajł XSL FO</comment>
+ <comment xml:lang="bg">Форматиращ файл — XSL FO</comment>
+ <comment xml:lang="ca">fitxer FO XSL</comment>
+ <comment xml:lang="cs">soubor XSL FO</comment>
+ <comment xml:lang="da">XML FO-fil</comment>
+ <comment xml:lang="de">XSL-FO-Datei</comment>
+ <comment xml:lang="el">Αρχείο XSL FO</comment>
+ <comment xml:lang="en_GB">XSL FO file</comment>
+ <comment xml:lang="eo">XSL-FO-dosiero</comment>
+ <comment xml:lang="es">archivo XSL FO</comment>
+ <comment xml:lang="eu">XSL FO fitxategia</comment>
+ <comment xml:lang="fi">XSL FO -tiedosto</comment>
+ <comment xml:lang="fo">XSL FO fíla</comment>
+ <comment xml:lang="fr">fichier XSL FO</comment>
+ <comment xml:lang="ga">comhad XSL FO</comment>
+ <comment xml:lang="gl">ficheiro XSL FO</comment>
+ <comment xml:lang="he">קובץ XSL FO</comment>
+ <comment xml:lang="hr">XSL FO datoteka</comment>
+ <comment xml:lang="hu">XSL FO fájl</comment>
+ <comment xml:lang="ia">File XSL FO</comment>
+ <comment xml:lang="id">Berkas XSL FO</comment>
+ <comment xml:lang="it">File XSL FO</comment>
+ <comment xml:lang="ja">XSL FO ファイル</comment>
+ <comment xml:lang="kk">XSL FO файлы</comment>
+ <comment xml:lang="ko">XSL 포매팅 개체 파일</comment>
+ <comment xml:lang="lt">XSL FO failas</comment>
+ <comment xml:lang="lv">XSL FO datne</comment>
+ <comment xml:lang="nb">FO-fil for XSL</comment>
+ <comment xml:lang="nl">XSL FO-bestand</comment>
+ <comment xml:lang="nn">XSL FO-fil</comment>
+ <comment xml:lang="oc">fichièr XSL FO</comment>
+ <comment xml:lang="pl">Plik XSL FO</comment>
+ <comment xml:lang="pt">ficheiro XSL FO</comment>
+ <comment xml:lang="pt_BR">Arquivo XSL FO</comment>
+ <comment xml:lang="ro">Fișier XSL FO</comment>
+ <comment xml:lang="ru">Файл XSL FO</comment>
+ <comment xml:lang="sk">Súbor XSL FO</comment>
+ <comment xml:lang="sl">Datoteka XSL FO</comment>
+ <comment xml:lang="sq">File XSL FO</comment>
+ <comment xml:lang="sr">ИксСЛ ФО датотека</comment>
+ <comment xml:lang="sv">XSL FO-fil</comment>
+ <comment xml:lang="tr">XSL FO dosyası</comment>
+ <comment xml:lang="uk">файл XSL FO</comment>
+ <comment xml:lang="vi">Tập tin FO của XSL (XFO)</comment>
+ <comment xml:lang="zh_CN">XSL 格式化对象文件</comment>
+ <comment xml:lang="zh_TW">XSL FO 檔</comment>
+ <acronym>XSL FO</acronym>
+ <expanded-acronym>XSL Formatting Objects</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <glob pattern="*.fo"/>
+ <glob pattern="*.xslfo"/>
+ <root-XML namespaceURI="http://www.w3.org/1999/XSL/Format" localName="root"/>
+ </mime-type>
+ <mime-type type="text/x-iptables">
+ <comment>iptables configuration file</comment>
+ <comment xml:lang="ar">ملف تضبيط iptables</comment>
+ <comment xml:lang="ast">ficheru de configuración d'iptables</comment>
+ <comment xml:lang="be@latin">kanfihuracyjny fajł iptables</comment>
+ <comment xml:lang="bg">Настройки за iptables</comment>
+ <comment xml:lang="ca">fitxer de configuració d'iptables</comment>
+ <comment xml:lang="cs">soubor nastavení iptables</comment>
+ <comment xml:lang="da">iptableskonfigurationsfil</comment>
+ <comment xml:lang="de">iptables-Konfigurationsdatei</comment>
+ <comment xml:lang="el">Αρχείο ρυθμίσεων iptables</comment>
+ <comment xml:lang="en_GB">iptables configuration file</comment>
+ <comment xml:lang="es">archivo de configuración de iptables</comment>
+ <comment xml:lang="eu">iptables konfigurazio-fitxategia</comment>
+ <comment xml:lang="fi">iptables-asetustiedosto</comment>
+ <comment xml:lang="fo">iptables samansetingarfíla</comment>
+ <comment xml:lang="fr">fichier de configuration iptables</comment>
+ <comment xml:lang="ga">comhad cumraíochta iptables</comment>
+ <comment xml:lang="gl">ficheiro de configuración de iptables</comment>
+ <comment xml:lang="he">קובץ הגדרה של iptables</comment>
+ <comment xml:lang="hr">iptables datoteka podešavanja</comment>
+ <comment xml:lang="hu">iptables beállítófájl</comment>
+ <comment xml:lang="ia">File de configuration IPTables</comment>
+ <comment xml:lang="id">berkas konfigurasi iptables</comment>
+ <comment xml:lang="it">File configurazione iptables</comment>
+ <comment xml:lang="ja">iptables 設定ファイル</comment>
+ <comment xml:lang="kk">iptables баптаулар файлы</comment>
+ <comment xml:lang="ko">iptables 설정 파일</comment>
+ <comment xml:lang="lt">iptables konfigūracijos failas</comment>
+ <comment xml:lang="lv">iptables konfigurācijas datne</comment>
+ <comment xml:lang="nb">konfigurasjonsfil for iptables</comment>
+ <comment xml:lang="nl">iptables-configuratiebestand</comment>
+ <comment xml:lang="nn">iptables oppsettfil</comment>
+ <comment xml:lang="oc">fichièr de configuracion iptables</comment>
+ <comment xml:lang="pl">Plik konfiguracji iptables</comment>
+ <comment xml:lang="pt">ficheiro de configuração iptables</comment>
+ <comment xml:lang="pt_BR">Arquivo de configuração do iptables</comment>
+ <comment xml:lang="ro">fișier configurare iptables</comment>
+ <comment xml:lang="ru">Файл настроек iptables</comment>
+ <comment xml:lang="sk">Súbor nastavení iptables</comment>
+ <comment xml:lang="sl">nastavitvena datoteka iptables</comment>
+ <comment xml:lang="sq">File konfigurimi iptables</comment>
+ <comment xml:lang="sr">датотека подешавања иптабела</comment>
+ <comment xml:lang="sv">iptables-konfigurationsfil</comment>
+ <comment xml:lang="tr">iptables yapılandırma dosyası</comment>
+ <comment xml:lang="uk">файл налаштувань iptables</comment>
+ <comment xml:lang="vi">tập tin cấu hình iptables</comment>
+ <comment xml:lang="zh_CN">iptables 防火墙配置文件</comment>
+ <comment xml:lang="zh_TW">iptables 組態檔</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="/etc/sysconfig/iptables" type="string" offset="0:256"/>
+ <match value="*filter" type="string" offset="0:256">
+ <match value=":INPUT" type="string" offset="0:256">
+ <match value=":FORWARD" type="string" offset="0:256">
+ <match value=":OUTPUT" type="string" offset="0:256"/>
+ </match>
+ </match>
+ </match>
+ <match value="-A INPUT" type="string" offset="0:256">
+ <match value="-A FORWARD" type="string" offset="0:256">
+ <match value="-A OUTPUT" type="string" offset="0:256"/>
+ </match>
+ </match>
+ <match value="-P INPUT" type="string" offset="0:256">
+ <match value="-P FORWARD" type="string" offset="0:256">
+ <match value="-P OUTPUT" type="string" offset="0:256"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.iptables"/>
+ </mime-type>
+ <mime-type type="text/x-dbus-service">
+ <comment>D-Bus service file</comment>
+ <comment xml:lang="ca">fitxer de servei de D-Bus</comment>
+ <comment xml:lang="cs">soubor služby D-Bus</comment>
+ <comment xml:lang="de">D-Bus-Dienstdatei</comment>
+ <comment xml:lang="en_GB">D-Bus service file</comment>
+ <comment xml:lang="es">archivo de servicio de D-Bus</comment>
+ <comment xml:lang="eu">D-Bus zerbitzu fitxategia</comment>
+ <comment xml:lang="fi">D-Bus-palvelutiedosto</comment>
+ <comment xml:lang="fr">fichier de service D-Bus</comment>
+ <comment xml:lang="ga">comhad seirbhíse D-Bus</comment>
+ <comment xml:lang="hr">Datoteka D-Bus usluge</comment>
+ <comment xml:lang="hu">D-Bus szolgáltatás fájl</comment>
+ <comment xml:lang="id">berkas layanan D-Bus</comment>
+ <comment xml:lang="it">File servizio D-Bus</comment>
+ <comment xml:lang="kk">D-Bus қызметтік файлы</comment>
+ <comment xml:lang="ko">D-Bus 서비스 파일</comment>
+ <comment xml:lang="pl">Plik usługi D-Bus</comment>
+ <comment xml:lang="pt_BR">Arquivo de serviço do D-Bus</comment>
+ <comment xml:lang="ru">Файл службы D-Bus</comment>
+ <comment xml:lang="sk">Súbor služby D-Bus</comment>
+ <comment xml:lang="sr">датотека услуге Д-сабирнице</comment>
+ <comment xml:lang="sv">D-BUS-tjänstfil</comment>
+ <comment xml:lang="tr">D-Bus hizmeti dosyası</comment>
+ <comment xml:lang="uk">файл служби D-Bus</comment>
+ <comment xml:lang="zh_CN">D-Bus 服务文件</comment>
+ <comment xml:lang="zh_TW">D-Bus 服務檔</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="\n[D-BUS Service]\n" type="string" offset="0:256"/>
+ <match value="[D-BUS Service]\n" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.service"/>
+ </mime-type>
+ <mime-type type="text/x-systemd-unit">
+ <comment>systemd unit file</comment>
+ <comment xml:lang="ast">ficheru d'unidaes de systemd</comment>
+ <comment xml:lang="ca">fitxer d'unitat de systemd</comment>
+ <comment xml:lang="cs">jednotkový soubor systemd</comment>
+ <comment xml:lang="de">systemd-Einheitsdatei</comment>
+ <comment xml:lang="en_GB">systemd unit file</comment>
+ <comment xml:lang="es">archivo de unidad de systemd</comment>
+ <comment xml:lang="eu">systemd unitate fitxategia</comment>
+ <comment xml:lang="fi">systemd-yksikkötiedosto</comment>
+ <comment xml:lang="fr">fichier d'unité systemd</comment>
+ <comment xml:lang="ga">comhad aonaid systemd</comment>
+ <comment xml:lang="hr">Datoteka systemd jedinice</comment>
+ <comment xml:lang="hu">systemd egység fájl</comment>
+ <comment xml:lang="id">berkas unit systemd</comment>
+ <comment xml:lang="it">File unità systemd</comment>
+ <comment xml:lang="kk">systemd юнит файлы</comment>
+ <comment xml:lang="ko">systemd 유닛 파일</comment>
+ <comment xml:lang="pl">Plik jednostki systemd</comment>
+ <comment xml:lang="pt_BR">Arquivo de unit do systemd</comment>
+ <comment xml:lang="ru">Модульный файл Systemd</comment>
+ <comment xml:lang="sk">Súbor jednotky systemd</comment>
+ <comment xml:lang="sr">датотека јединице системд-а</comment>
+ <comment xml:lang="sv">systemd-enhetsfil</comment>
+ <comment xml:lang="tr">systemd birim dosyası</comment>
+ <comment xml:lang="uk">файл модуля systemd</comment>
+ <comment xml:lang="zh_CN">systemd 单元文件</comment>
+ <comment xml:lang="zh_TW">systemd 單位檔</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+
+ <match value="\n[Unit]\n" type="string" offset="0:256"/>
+ <match value="\n[Install]\n" type="string" offset="0:256"/>
+ <match value="\n[Automount]\n" type="string" offset="0:256"/>
+
+ <match value="\n[Mount]\n" type="string" offset="0:256"/>
+ <match value="\n[Path]\n" type="string" offset="0:256"/>
+ <match value="\n[Scope]\n" type="string" offset="0:256"/>
+ <match value="\n[Service]\n" type="string" offset="0:256"/>
+ <match value="\n[Slice]\n" type="string" offset="0:256"/>
+ <match value="\n[Socket]\n" type="string" offset="0:256"/>
+ <match value="\n[Swap]\n" type="string" offset="0:256"/>
+
+ <match value="\n[Timer]\n" type="string" offset="0:256"/>
+
+
+ <match value="[Unit]\n" type="string" offset="0"/>
+ <match value="[Install]\n" type="string" offset="0"/>
+ <match value="[Automount]\n" type="string" offset="0"/>
+ <match value="[Mount]\n" type="string" offset="0"/>
+ <match value="[Path]\n" type="string" offset="0"/>
+ <match value="[Scope]\n" type="string" offset="0"/>
+ <match value="[Service]\n" type="string" offset="0"/>
+ <match value="[Slice]\n" type="string" offset="0"/>
+ <match value="[Socket]\n" type="string" offset="0"/>
+ <match value="[Swap]\n" type="string" offset="0"/>
+ <match value="[Timer]\n" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.automount"/>
+ <glob pattern="*.device"/>
+ <glob pattern="*.mount"/>
+ <glob pattern="*.path"/>
+ <glob pattern="*.scope"/>
+ <glob pattern="*.service"/>
+ <glob pattern="*.slice"/>
+ <glob pattern="*.socket"/>
+ <glob pattern="*.swap"/>
+ <glob pattern="*.target"/>
+ <glob pattern="*.timer"/>
+ </mime-type>
+ <mime-type type="application/xslt+xml">
+ <comment>XSLT stylesheet</comment>
+ <comment xml:lang="ar">نمط XSLT</comment>
+ <comment xml:lang="be@latin">Arkuš stylaŭ XSLT</comment>
+ <comment xml:lang="bg">Стилове — XSLT</comment>
+ <comment xml:lang="ca">full d'estil XSLT</comment>
+ <comment xml:lang="cs">stylopis XSLT</comment>
+ <comment xml:lang="da">XSLT-stilark</comment>
+ <comment xml:lang="de">XSLT-Stylesheet</comment>
+ <comment xml:lang="el">Φύλλο στυλ XSLT</comment>
+ <comment xml:lang="en_GB">XSLT stylesheet</comment>
+ <comment xml:lang="eo">XSLT-stilfolio</comment>
+ <comment xml:lang="es">hoja de estilos XSLT</comment>
+ <comment xml:lang="eu">XSLT estilo-orria</comment>
+ <comment xml:lang="fi">XSLT-tyylitiedosto</comment>
+ <comment xml:lang="fo">XSLT sniðark</comment>
+ <comment xml:lang="fr">feuille de style XSLT</comment>
+ <comment xml:lang="ga">stílbhileog XSLT</comment>
+ <comment xml:lang="gl">folla de estilo XSLT</comment>
+ <comment xml:lang="he">גליון סגנון XSLT</comment>
+ <comment xml:lang="hr">XSLT stilska tablica</comment>
+ <comment xml:lang="hu">XSLT-stíluslap</comment>
+ <comment xml:lang="ia">Folio de stilo XSLT</comment>
+ <comment xml:lang="id">Lembar gaya XSLT</comment>
+ <comment xml:lang="it">Foglio di stile XSLT</comment>
+ <comment xml:lang="ja">XSLT スタイルシート</comment>
+ <comment xml:lang="kk">XSLT стильдер кестесі</comment>
+ <comment xml:lang="ko">XSLT 스타일시트</comment>
+ <comment xml:lang="lt">XSLT stiliaus aprašas</comment>
+ <comment xml:lang="lv">XSLT izklājlapa</comment>
+ <comment xml:lang="ms">Helaian Gaya XSLT</comment>
+ <comment xml:lang="nb">XSLT-stilark</comment>
+ <comment xml:lang="nl">XSLT-stijlblad</comment>
+ <comment xml:lang="nn">XSLT-stilark</comment>
+ <comment xml:lang="oc">fuèlh d'estil XSLT</comment>
+ <comment xml:lang="pl">Arkusz stylów XSLT</comment>
+ <comment xml:lang="pt">folha de estilos XSLT</comment>
+ <comment xml:lang="pt_BR">Folha de estilo XSLT</comment>
+ <comment xml:lang="ro">Fișă de stil XSLT</comment>
+ <comment xml:lang="ru">Таблица стилей XSLT</comment>
+ <comment xml:lang="sk">Štýl XSLT</comment>
+ <comment xml:lang="sl">Slogovna predloga XSLT</comment>
+ <comment xml:lang="sq">Fletë stili XSLT</comment>
+ <comment xml:lang="sr">ИксСЛТ стилски лист</comment>
+ <comment xml:lang="sv">XSLT-stilmall</comment>
+ <comment xml:lang="tr">XSLT çalışma sayfası</comment>
+ <comment xml:lang="uk">таблиця стилів XSLT</comment>
+ <comment xml:lang="vi">Tờ kiểu dáng XSLT</comment>
+ <comment xml:lang="zh_CN">XSLT 样式表</comment>
+ <comment xml:lang="zh_TW">XSLT 樣式表</comment>
+ <acronym>XSLT</acronym>
+ <expanded-acronym>eXtensible Stylesheet Language Transformation</expanded-acronym>
+ <generic-icon name="text-x-generic"/>
+ <magic priority="50">
+ <match value="&lt;xsl:stylesheet" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.xsl"/>
+ <glob pattern="*.xslt"/>
+ <root-XML namespaceURI="http://www.w3.org/1999/XSL/Transform" localName="stylesheet"/>
+ <sub-class-of type="application/xml"/>
+ </mime-type>
+ <mime-type type="text/xmcd">
+ <comment>XMCD CD database</comment>
+ <comment xml:lang="ar">قاعدة بيانات XMCD CD</comment>
+ <comment xml:lang="be@latin">Baza źviestak ab dyskach XMCD</comment>
+ <comment xml:lang="bg">База от данни за CD-та — XMCD</comment>
+ <comment xml:lang="ca">base de dades de CD XMCD</comment>
+ <comment xml:lang="cs">databáze XMCD CD</comment>
+ <comment xml:lang="da">XMCD-cd-database</comment>
+ <comment xml:lang="de">XMCD-CD-Datenbank</comment>
+ <comment xml:lang="el">Βάση δεδομένων CD XMCD</comment>
+ <comment xml:lang="en_GB">XMCD CD database</comment>
+ <comment xml:lang="es">base de datos de CD XMCD</comment>
+ <comment xml:lang="eu">XMCD CD datu-basea</comment>
+ <comment xml:lang="fi">XMCD CD -tietokanta</comment>
+ <comment xml:lang="fo">XMCD fløgu dátustovnur</comment>
+ <comment xml:lang="fr">base de données de CD XMCD</comment>
+ <comment xml:lang="ga">bunachar sonraí XMCD CD</comment>
+ <comment xml:lang="gl">base de datos de CD XMCD</comment>
+ <comment xml:lang="he">מסד נתונים XMCD CD</comment>
+ <comment xml:lang="hr">XMCD CD baza podataka</comment>
+ <comment xml:lang="hu">XMCD CD-adatbázis</comment>
+ <comment xml:lang="ia">Base de datos de CD XMCD</comment>
+ <comment xml:lang="id">Basis data XMCD CD</comment>
+ <comment xml:lang="it">Database XMCD CD</comment>
+ <comment xml:lang="ja">XMCD CD データベース</comment>
+ <comment xml:lang="kk">XMCD CD дерекқоры</comment>
+ <comment xml:lang="ko">XMCD CD 데이터베이스</comment>
+ <comment xml:lang="lt">XMCD CD duomenų bazė</comment>
+ <comment xml:lang="lv">XMCD CD datubāze</comment>
+ <comment xml:lang="nb">XMCD CD-database</comment>
+ <comment xml:lang="nl">XMCD CD-gegevensbank</comment>
+ <comment xml:lang="nn">XMCD CD-database</comment>
+ <comment xml:lang="oc">banca de donadas de CD XMCD</comment>
+ <comment xml:lang="pl">Baza danych CD XMCD</comment>
+ <comment xml:lang="pt">base de dados XMCD CD</comment>
+ <comment xml:lang="pt_BR">Banco de dados de CD XMCD</comment>
+ <comment xml:lang="ro">Bază de date XMCD CD</comment>
+ <comment xml:lang="ru">База данных компакт-дисков XMCD</comment>
+ <comment xml:lang="sk">Databáza XMCD CD</comment>
+ <comment xml:lang="sl">Podatkovna zbirka XMCD CD</comment>
+ <comment xml:lang="sq">Bazë me të dhëna XMCD CD</comment>
+ <comment xml:lang="sr">ИксМЦД ЦД база података</comment>
+ <comment xml:lang="sv">XMCD cd-databas</comment>
+ <comment xml:lang="tr">XMCD CD veritabanı</comment>
+ <comment xml:lang="uk">база даних XMCD CD</comment>
+ <comment xml:lang="vi">Cơ sở dữ liệu CD XMCD</comment>
+ <comment xml:lang="zh_CN">XMCD CD 数据库</comment>
+ <comment xml:lang="zh_TW">XMCD CD 資料庫</comment>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="# xmcd" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/xml">
+ <comment>XML document</comment>
+ <comment xml:lang="ar">مستند XML</comment>
+ <comment xml:lang="ast">Documentu XML</comment>
+ <comment xml:lang="be@latin">Dakument XML</comment>
+ <comment xml:lang="bg">Документ — XML</comment>
+ <comment xml:lang="ca">document XML</comment>
+ <comment xml:lang="cs">dokument XML</comment>
+ <comment xml:lang="da">XML-dokument</comment>
+ <comment xml:lang="de">XML-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο XML</comment>
+ <comment xml:lang="en_GB">XML document</comment>
+ <comment xml:lang="eo">XML-dokumento</comment>
+ <comment xml:lang="es">documento XML</comment>
+ <comment xml:lang="eu">XML dokumentua</comment>
+ <comment xml:lang="fi">XML-asiakirja</comment>
+ <comment xml:lang="fo">XML skjal</comment>
+ <comment xml:lang="fr">document XML</comment>
+ <comment xml:lang="ga">cáipéis XML</comment>
+ <comment xml:lang="gl">documento XML</comment>
+ <comment xml:lang="he">מסמך XML</comment>
+ <comment xml:lang="hr">XML dokument</comment>
+ <comment xml:lang="hu">XML dokumentum</comment>
+ <comment xml:lang="ia">Documento XML</comment>
+ <comment xml:lang="id">Dokumen XML</comment>
+ <comment xml:lang="it">Documento XML</comment>
+ <comment xml:lang="ja">XML ドキュメント</comment>
+ <comment xml:lang="kk">XML құжаты</comment>
+ <comment xml:lang="ko">XML 문서</comment>
+ <comment xml:lang="lt">XML dokumentas</comment>
+ <comment xml:lang="lv">XML dokuments</comment>
+ <comment xml:lang="nb">XML-dokument</comment>
+ <comment xml:lang="nl">XML-document</comment>
+ <comment xml:lang="nn">XML-dokument</comment>
+ <comment xml:lang="oc">document XML</comment>
+ <comment xml:lang="pl">Dokument XML</comment>
+ <comment xml:lang="pt">documento XML</comment>
+ <comment xml:lang="pt_BR">Documento XML</comment>
+ <comment xml:lang="ro">Document XML</comment>
+ <comment xml:lang="ru">Документ XML</comment>
+ <comment xml:lang="sk">Dokument XML</comment>
+ <comment xml:lang="sl">Dokument XML</comment>
+ <comment xml:lang="sq">Dokument XML</comment>
+ <comment xml:lang="sr">ИксМЛ документ</comment>
+ <comment xml:lang="sv">XML-dokument</comment>
+ <comment xml:lang="tr">XML belgesi</comment>
+ <comment xml:lang="uk">документ XML</comment>
+ <comment xml:lang="vi">Tài liệu XML</comment>
+ <comment xml:lang="zh_CN">XML 文档</comment>
+ <comment xml:lang="zh_TW">XML 文件</comment>
+ <acronym>XML</acronym>
+ <expanded-acronym>eXtensible Markup Language</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-html"/>
+ <magic priority="40">
+ <match value="&lt;?xml" type="string" offset="0"/>
+ <match value="&lt;!--" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.xml"/>
+ <glob pattern="*.xbl"/>
+ <glob pattern="*.xsd"/>
+ <glob pattern="*.rng"/>
+ <alias type="text/xml"/>
+ </mime-type>
+ <mime-type type="application/xml-external-parsed-entity">
+ <comment>XML entities document</comment>
+ <comment xml:lang="ar">مستند كيانات XML</comment>
+ <comment xml:lang="ast">Documentu d'entidaes XML</comment>
+ <comment xml:lang="be@latin">Dakument elementaŭ XML</comment>
+ <comment xml:lang="bg">Документ — заместващи последователности в XML</comment>
+ <comment xml:lang="ca">document d'entitats XML</comment>
+ <comment xml:lang="cs">dokument entit XML</comment>
+ <comment xml:lang="da">XML-enhedsdokument</comment>
+ <comment xml:lang="de">XML-Dokument-Entitäten</comment>
+ <comment xml:lang="el">Έγγραφο οντοτήτων XML</comment>
+ <comment xml:lang="en_GB">XML entities document</comment>
+ <comment xml:lang="es">documento de entidades XML</comment>
+ <comment xml:lang="eu">XML entitateen dokumentua</comment>
+ <comment xml:lang="fi">XML-entiteettiasiakirja</comment>
+ <comment xml:lang="fo">XML einindisskjal</comment>
+ <comment xml:lang="fr">document d'entités XML</comment>
+ <comment xml:lang="ga">cáipéis aonán XML</comment>
+ <comment xml:lang="gl">documento de entidades XML</comment>
+ <comment xml:lang="he">מסמך ישויות XML</comment>
+ <comment xml:lang="hr">Dokument XML subjekata</comment>
+ <comment xml:lang="hu">XML egyeddokumentum</comment>
+ <comment xml:lang="ia">Documento de entitates XML</comment>
+ <comment xml:lang="id">Dokumen entitas XML</comment>
+ <comment xml:lang="it">Documento entità XML</comment>
+ <comment xml:lang="ja">XML エントリドキュメント</comment>
+ <comment xml:lang="kk">XML мәндер құжаты</comment>
+ <comment xml:lang="ko">XML 엔티티 문서</comment>
+ <comment xml:lang="lt">XML esybių dokumentas</comment>
+ <comment xml:lang="lv">XML vienību dokuments</comment>
+ <comment xml:lang="nb">XML-entitetsdokument</comment>
+ <comment xml:lang="nl">XML entiteiten-document</comment>
+ <comment xml:lang="nn">XML-entitet-dokument</comment>
+ <comment xml:lang="oc">document d'entitats XML</comment>
+ <comment xml:lang="pl">Dokument jednostek XML</comment>
+ <comment xml:lang="pt">documento de entidades XML</comment>
+ <comment xml:lang="pt_BR">Documento de entidades XML</comment>
+ <comment xml:lang="ro">Document entități XML</comment>
+ <comment xml:lang="ru">Файл сущностей XML</comment>
+ <comment xml:lang="sk">Dokument entít XML</comment>
+ <comment xml:lang="sl">Dokument XML določil</comment>
+ <comment xml:lang="sq">Dokument njësish XML</comment>
+ <comment xml:lang="sr">документ ИксМЛ ставки</comment>
+ <comment xml:lang="sv">XML-entitetsdokument</comment>
+ <comment xml:lang="tr">XML varlıklar belgesi</comment>
+ <comment xml:lang="uk">документ об’єктів XML</comment>
+ <comment xml:lang="vi">Tài liệu thực thể XML</comment>
+ <comment xml:lang="zh_CN">XML 特征文档</comment>
+ <comment xml:lang="zh_TW">XML 實體文件</comment>
+ <acronym>XML</acronym>
+ <expanded-acronym>eXtensible Markup Language</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="text-html"/>
+ <glob pattern="*.ent"/>
+ <alias type="text/xml-external-parsed-entity"/>
+ </mime-type>
+ <mime-type type="video/dv">
+ <comment>DV video</comment>
+ <comment xml:lang="ar">DV مرئي</comment>
+ <comment xml:lang="ast">Videu en DV</comment>
+ <comment xml:lang="be@latin">Videa DV</comment>
+ <comment xml:lang="bg">Видео — DV</comment>
+ <comment xml:lang="ca">vídeo DV</comment>
+ <comment xml:lang="cs">video DV</comment>
+ <comment xml:lang="da">DV-video</comment>
+ <comment xml:lang="de">DV-Video</comment>
+ <comment xml:lang="el">Βίντεο DV</comment>
+ <comment xml:lang="en_GB">DV video</comment>
+ <comment xml:lang="eo">DV-video</comment>
+ <comment xml:lang="es">vídeo DV</comment>
+ <comment xml:lang="eu">DV bideoa</comment>
+ <comment xml:lang="fi">DV-video</comment>
+ <comment xml:lang="fo">DV video</comment>
+ <comment xml:lang="fr">vidéo DV</comment>
+ <comment xml:lang="ga">físeán DV</comment>
+ <comment xml:lang="gl">vídeo DV</comment>
+ <comment xml:lang="he">וידאו DV</comment>
+ <comment xml:lang="hr">DV video snimka</comment>
+ <comment xml:lang="hu">DV videó</comment>
+ <comment xml:lang="ia">Video DV</comment>
+ <comment xml:lang="id">Video DV</comment>
+ <comment xml:lang="it">Video DV</comment>
+ <comment xml:lang="ja">DV 動画</comment>
+ <comment xml:lang="ka">DV ვიდეო</comment>
+ <comment xml:lang="kk">DV видеосы</comment>
+ <comment xml:lang="ko">DV 동영상</comment>
+ <comment xml:lang="lt">DV vaizdo įrašas</comment>
+ <comment xml:lang="lv">DV video</comment>
+ <comment xml:lang="nb">DV-film</comment>
+ <comment xml:lang="nl">DV-video</comment>
+ <comment xml:lang="nn">DV-video</comment>
+ <comment xml:lang="oc">vidèo DV</comment>
+ <comment xml:lang="pl">Plik wideo DV</comment>
+ <comment xml:lang="pt">vídeo DV</comment>
+ <comment xml:lang="pt_BR">Vídeo DV</comment>
+ <comment xml:lang="ro">Video DV</comment>
+ <comment xml:lang="ru">Видео DV</comment>
+ <comment xml:lang="sk">Video DV</comment>
+ <comment xml:lang="sl">Video datoteka DV</comment>
+ <comment xml:lang="sq">Video DV</comment>
+ <comment xml:lang="sr">ДВ видео</comment>
+ <comment xml:lang="sv">DV-video</comment>
+ <comment xml:lang="tr">DV video</comment>
+ <comment xml:lang="uk">відеокліп DV</comment>
+ <comment xml:lang="vi">Ảnh động DV</comment>
+ <comment xml:lang="zh_CN">DV 视频</comment>
+ <comment xml:lang="zh_TW">DV 視訊</comment>
+ <acronym>DV</acronym>
+ <expanded-acronym>Digital Video</expanded-acronym>
+ <magic priority="50">
+ <match value="0x1f070000" type="big32" offset="0" mask="0xffffff00"/>
+ </magic>
+ <glob pattern="*.dv"/>
+ </mime-type>
+ <mime-type type="video/isivideo">
+ <comment>ISI video</comment>
+ <comment xml:lang="ar">مرئي ISI</comment>
+ <comment xml:lang="ast">Videu n'ISI</comment>
+ <comment xml:lang="az">ISI video faylı</comment>
+ <comment xml:lang="be@latin">Videa ISI</comment>
+ <comment xml:lang="bg">Видео — ISI</comment>
+ <comment xml:lang="ca">vídeo ISI</comment>
+ <comment xml:lang="cs">video ISI</comment>
+ <comment xml:lang="cy">Fideo ISI</comment>
+ <comment xml:lang="da">ISI-video</comment>
+ <comment xml:lang="de">ISI-Video</comment>
+ <comment xml:lang="el">Βίντεο ISI</comment>
+ <comment xml:lang="en_GB">ISI video</comment>
+ <comment xml:lang="eo">ISI-video</comment>
+ <comment xml:lang="es">vídeo ISI</comment>
+ <comment xml:lang="eu">ISI bideoa</comment>
+ <comment xml:lang="fi">ISI-video</comment>
+ <comment xml:lang="fo">ISI video</comment>
+ <comment xml:lang="fr">vidéo ISI</comment>
+ <comment xml:lang="ga">físeán ISI</comment>
+ <comment xml:lang="gl">vídeo ISI</comment>
+ <comment xml:lang="he">וידאו ISI</comment>
+ <comment xml:lang="hr">ISI video snimka</comment>
+ <comment xml:lang="hu">ISI-videó</comment>
+ <comment xml:lang="ia">Video ISI</comment>
+ <comment xml:lang="id">Video ISI</comment>
+ <comment xml:lang="it">Video ISI</comment>
+ <comment xml:lang="ja">ISI 動画</comment>
+ <comment xml:lang="kk">ISI видеосы</comment>
+ <comment xml:lang="ko">ISI 동영상</comment>
+ <comment xml:lang="lt">ISI vaizdo įrašas</comment>
+ <comment xml:lang="lv">ISI video</comment>
+ <comment xml:lang="ms">Video ISI</comment>
+ <comment xml:lang="nb">ISI-film</comment>
+ <comment xml:lang="nl">ISI-video</comment>
+ <comment xml:lang="nn">ISI video</comment>
+ <comment xml:lang="oc">vidèo ISI</comment>
+ <comment xml:lang="pl">Plik wideo ISI</comment>
+ <comment xml:lang="pt">vídeo ISI</comment>
+ <comment xml:lang="pt_BR">Vídeo ISI</comment>
+ <comment xml:lang="ro">Video ISI</comment>
+ <comment xml:lang="ru">Видео ISI</comment>
+ <comment xml:lang="sk">Video ISI</comment>
+ <comment xml:lang="sl">Video datoteka ISI</comment>
+ <comment xml:lang="sq">Video ISI</comment>
+ <comment xml:lang="sr">ИСИ видео</comment>
+ <comment xml:lang="sv">ISI-video</comment>
+ <comment xml:lang="tr">ISI videosu</comment>
+ <comment xml:lang="uk">відеокліп ISI</comment>
+ <comment xml:lang="vi">Ảnh động ISI</comment>
+ <comment xml:lang="zh_CN">ISI 视频</comment>
+ <comment xml:lang="zh_TW">ISI 視訊</comment>
+ </mime-type>
+ <mime-type type="video/mp2t">
+ <comment>MPEG-2 transport stream</comment>
+ <comment xml:lang="ar">بث نقل MPEG-2</comment>
+ <comment xml:lang="bg">Поток — транспорт по MPEG-2</comment>
+ <comment xml:lang="ca">flux de transport MPEG-2</comment>
+ <comment xml:lang="cs">přenosový proud MPEG-2</comment>
+ <comment xml:lang="da">MPEG-2-transportstrøm</comment>
+ <comment xml:lang="de">MPEG-2-Transportstrom</comment>
+ <comment xml:lang="el">Ροή μεταφοράς MPEG-2</comment>
+ <comment xml:lang="en_GB">MPEG-2 transport stream</comment>
+ <comment xml:lang="es">flujo de transporte MPEG-2</comment>
+ <comment xml:lang="eu">MPEG-2 korronte garraioa</comment>
+ <comment xml:lang="fi">MPEG-2 -siirtobittivirta</comment>
+ <comment xml:lang="fo">MPEG-2 flutningsstreymur</comment>
+ <comment xml:lang="fr">flux de transport MPEG-2</comment>
+ <comment xml:lang="ga">sruth aistrithe MPEG-2</comment>
+ <comment xml:lang="gl">fluxo de transporte MPEG-2</comment>
+ <comment xml:lang="he">העברת זרימה של MPEG-2</comment>
+ <comment xml:lang="hr">MPEG-2 transportno strujanje</comment>
+ <comment xml:lang="hu">MPEG-2 átviteli adatfolyam</comment>
+ <comment xml:lang="ia">Fluxo de transporto MPEG-2</comment>
+ <comment xml:lang="id">Stream transport MPEG-2</comment>
+ <comment xml:lang="it">Stream di trasporto MPEG-2</comment>
+ <comment xml:lang="ja">MPEG-2 トランスポートストリーム</comment>
+ <comment xml:lang="ka">MPEG-2-ის ტრანსპორტული ნაკადი</comment>
+ <comment xml:lang="kk">MPEG-2 көліктік ағыны</comment>
+ <comment xml:lang="ko">MPEG-2 전송 스트림</comment>
+ <comment xml:lang="lt">MPEG-2 transportavimo srautas</comment>
+ <comment xml:lang="lv">MPEG-2 transporta straume</comment>
+ <comment xml:lang="nl">MPEG-2 transport stream</comment>
+ <comment xml:lang="oc">flux de transpòrt MPEG-2</comment>
+ <comment xml:lang="pl">Strumień przesyłania MPEG-2</comment>
+ <comment xml:lang="pt">fluxo de transporte MPEG-2</comment>
+ <comment xml:lang="pt_BR">Fluxo de transporte de MPEG-2</comment>
+ <comment xml:lang="ro">Flux transport MPEG-2</comment>
+ <comment xml:lang="ru">Транспортный поток MPEG-2</comment>
+ <comment xml:lang="sk">MPEG-2 Transport Stream</comment>
+ <comment xml:lang="sl">Pretočni vir prenosega MPEG</comment>
+ <comment xml:lang="sr">МПЕГ-2 ток преноса</comment>
+ <comment xml:lang="sv">MPEG-2 transportström</comment>
+ <comment xml:lang="tr">MPEG-2 aktarım akışı</comment>
+ <comment xml:lang="uk">потік передавання даних MPEG-2</comment>
+ <comment xml:lang="zh_CN">MPEG-2 传输流</comment>
+ <comment xml:lang="zh_TW">MPEG-2 傳輸串流</comment>
+ <acronym>MPEG-2 TS</acronym>
+ <expanded-acronym>Moving Picture Experts Group 2 Transport Stream</expanded-acronym>
+ <magic priority="50">
+ <match value="0x47" type="byte" offset="0">
+ <match value="0x47" type="byte" offset="188">
+ <match value="0x47" type="byte" offset="376">
+ <match value="0x47" type="byte" offset="564">
+ <match value="0x47" type="byte" offset="752"/>
+ </match>
+ </match>
+ </match>
+ </match>
+ <match value="0x47" type="byte" offset="4">
+ <match value="0x47" type="byte" offset="196">
+ <match value="0x47" type="byte" offset="388">
+ <match value="0x47" type="byte" offset="580">
+ <match value="0x47" type="byte" offset="772"/>
+ </match>
+ </match>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.m2t"/>
+ <glob pattern="*.m2ts"/>
+ <glob pattern="*.ts"/>
+ <glob pattern="*.mts"/>
+ <glob pattern="*.cpi"/>
+ <glob pattern="*.clpi"/>
+ <glob pattern="*.mpl"/>
+ <glob pattern="*.mpls"/>
+ <glob pattern="*.bdm"/>
+ <glob pattern="*.bdmv"/>
+ </mime-type>
+ <mime-type type="video/mpeg">
+ <comment>MPEG video</comment>
+ <comment xml:lang="ar">MPEG مرئي</comment>
+ <comment xml:lang="ast">Videu en MPEG</comment>
+ <comment xml:lang="be@latin">Videa MPEG</comment>
+ <comment xml:lang="bg">Видео — MPEG</comment>
+ <comment xml:lang="ca">vídeo MPEG</comment>
+ <comment xml:lang="cs">video MPEG</comment>
+ <comment xml:lang="da">MPEG-video</comment>
+ <comment xml:lang="de">MPEG-Video</comment>
+ <comment xml:lang="el">Βίντεο MPEG</comment>
+ <comment xml:lang="en_GB">MPEG video</comment>
+ <comment xml:lang="eo">MPEG-video</comment>
+ <comment xml:lang="es">vídeo MPEG</comment>
+ <comment xml:lang="eu">MPEG bideoa</comment>
+ <comment xml:lang="fi">MPEG-video</comment>
+ <comment xml:lang="fo">MPEG video</comment>
+ <comment xml:lang="fr">vidéo MPEG</comment>
+ <comment xml:lang="ga">físeán MPEG</comment>
+ <comment xml:lang="gl">vídeo MPEG</comment>
+ <comment xml:lang="he">וידאו MPEG</comment>
+ <comment xml:lang="hr">MPEG video snimka</comment>
+ <comment xml:lang="hu">MPEG-videó</comment>
+ <comment xml:lang="ia">Video MPEG</comment>
+ <comment xml:lang="id">Video MPEG</comment>
+ <comment xml:lang="it">Video MPEG</comment>
+ <comment xml:lang="ja">MPEG 動画</comment>
+ <comment xml:lang="ka">MPEG ვიდეო</comment>
+ <comment xml:lang="kk">MPEG видеосы</comment>
+ <comment xml:lang="ko">MPEG 동영상</comment>
+ <comment xml:lang="lt">MPEG vaizdo įrašas</comment>
+ <comment xml:lang="lv">MPEG video</comment>
+ <comment xml:lang="ms">Video MPEG</comment>
+ <comment xml:lang="nb">MPEG-film</comment>
+ <comment xml:lang="nl">MPEG-video</comment>
+ <comment xml:lang="nn">MPEG-video</comment>
+ <comment xml:lang="oc">vidèo MPEG</comment>
+ <comment xml:lang="pl">Plik wideo MPEG</comment>
+ <comment xml:lang="pt">vídeo MPEG</comment>
+ <comment xml:lang="pt_BR">Vídeo MPEG</comment>
+ <comment xml:lang="ro">Video MPEG</comment>
+ <comment xml:lang="ru">Видео MPEG</comment>
+ <comment xml:lang="sk">Video MPEG</comment>
+ <comment xml:lang="sl">Video datoteka MPEG</comment>
+ <comment xml:lang="sq">Video MPEG</comment>
+ <comment xml:lang="sr">МПЕГ видео</comment>
+ <comment xml:lang="sv">MPEG-video</comment>
+ <comment xml:lang="tr">MPEG videosu</comment>
+ <comment xml:lang="uk">відеокліп MPEG</comment>
+ <comment xml:lang="vi">Ảnh động MPEG</comment>
+ <comment xml:lang="zh_CN">MPEG 视频</comment>
+ <comment xml:lang="zh_TW">MPEG 視訊</comment>
+ <acronym>MPEG</acronym>
+ <expanded-acronym>Moving Picture Experts Group</expanded-acronym>
+ <alias type="video/x-mpeg"/>
+ <alias type="video/mpeg-system"/>
+ <alias type="video/x-mpeg-system"/>
+ <alias type="video/x-mpeg2"/>
+ <magic priority="50">
+ <match value="\x47\x3f\xff\x10" type="string" offset="0"/>
+ <match value="0x000001b3" type="big32" offset="0"/>
+ <match value="0x000001ba" type="big32" offset="0"/>
+ </magic>
+ <glob pattern="*.mpeg"/>
+ <glob pattern="*.mpg"/>
+ <glob pattern="*.mp2"/>
+ <glob pattern="*.mpe"/>
+ <glob pattern="*.vob"/>
+ <glob pattern="[0-9][0-9][0-9].vdr"/>
+ </mime-type>
+ <mime-type type="video/vnd.mpegurl">
+ <comment>MPEG video (streamed)</comment>
+ <comment xml:lang="ast">Videu en MPEG (tresmitíu)</comment>
+ <comment xml:lang="bg">Видео — MPEG, поточно</comment>
+ <comment xml:lang="ca">vídeo MPEG (flux)</comment>
+ <comment xml:lang="cs">video MPEG (proud)</comment>
+ <comment xml:lang="da">MPEG-video (streamet)</comment>
+ <comment xml:lang="de">MPEG-Video (Datenstrom)</comment>
+ <comment xml:lang="el">Βίντεο MPEG (εκπεμπόμενο)</comment>
+ <comment xml:lang="en_GB">MPEG video (streamed)</comment>
+ <comment xml:lang="es">vídeo MPEG (transmisión)</comment>
+ <comment xml:lang="eu">MPEG bideoa (korronte bidez)</comment>
+ <comment xml:lang="fi">MPEG-video (virtaus)</comment>
+ <comment xml:lang="fr">vidéo MPEG (flux)</comment>
+ <comment xml:lang="ga">físeán MPEG (sruthaithe)</comment>
+ <comment xml:lang="gl">vídeo MPEG (en stream)</comment>
+ <comment xml:lang="he">קובץ MPEG (בהזרמה)</comment>
+ <comment xml:lang="hr">MPEG video snimka (strujanje)</comment>
+ <comment xml:lang="hu">MPEG videó (szórt)</comment>
+ <comment xml:lang="ia">Video MPEG (in fluxo)</comment>
+ <comment xml:lang="id">Video MPEG (di-stream-kan)</comment>
+ <comment xml:lang="it">Video MPEG (streamed)</comment>
+ <comment xml:lang="ja">MPEG ビデオ(ストリーム)</comment>
+ <comment xml:lang="ka">MPEG ვიდეო (ნაკადი)</comment>
+ <comment xml:lang="kk">MPEG видео (ағымдық)</comment>
+ <comment xml:lang="ko">MPEG 동영상(스트리밍)</comment>
+ <comment xml:lang="lv">MPEG video (straumēts)</comment>
+ <comment xml:lang="nl">MPEG video (streamed)</comment>
+ <comment xml:lang="oc">vidèo MPEG (flux)</comment>
+ <comment xml:lang="pl">Plik wideo MPEG (strumień)</comment>
+ <comment xml:lang="pt">vídeo MPEG (em fluxo)</comment>
+ <comment xml:lang="pt_BR">Vídeo MPEG (fluxo)</comment>
+ <comment xml:lang="ru">Видео MPEG (потоковое)</comment>
+ <comment xml:lang="sk">MPEG video (streamované)</comment>
+ <comment xml:lang="sl">MPEG-video (pretočni)</comment>
+ <comment xml:lang="sr">МПЕГ видео (проточни)</comment>
+ <comment xml:lang="sv">MPEG-video (strömmad)</comment>
+ <comment xml:lang="tr">MPEG videosu (akış)</comment>
+ <comment xml:lang="uk">відеокліп MPEG (потоковий)</comment>
+ <comment xml:lang="zh_CN">MPEG 视频流媒体</comment>
+ <comment xml:lang="zh_TW">MPEG 視訊 (串流)</comment>
+ <sub-class-of type="text/plain"/>
+ <alias type="video/x-mpegurl"/>
+ <magic priority="50">
+ <match value="#EXTM4U" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.m1u"/>
+ <glob pattern="*.m4u"/>
+ <glob pattern="*.mxu"/>
+ </mime-type>
+ <mime-type type="video/quicktime">
+ <comment>QuickTime video</comment>
+ <comment xml:lang="ar">QuickTime مرئي</comment>
+ <comment xml:lang="ast">Videu en QuickTime</comment>
+ <comment xml:lang="be@latin">Videa QuickTime</comment>
+ <comment xml:lang="bg">Видео — QuickTime</comment>
+ <comment xml:lang="ca">vídeo QuickTime</comment>
+ <comment xml:lang="cs">video QuickTime</comment>
+ <comment xml:lang="da">QuickTime-video</comment>
+ <comment xml:lang="de">QuickTime-Video</comment>
+ <comment xml:lang="el">Βίντεο QuickTime</comment>
+ <comment xml:lang="en_GB">QuickTime video</comment>
+ <comment xml:lang="eo">QuickTime-video</comment>
+ <comment xml:lang="es">vídeo QuickTime</comment>
+ <comment xml:lang="eu">QuickTime bideoa</comment>
+ <comment xml:lang="fi">QuickTime-video</comment>
+ <comment xml:lang="fo">QuickTime video</comment>
+ <comment xml:lang="fr">vidéo QuickTime</comment>
+ <comment xml:lang="ga">físeán QuickTime</comment>
+ <comment xml:lang="gl">vídeo QuickTime</comment>
+ <comment xml:lang="he">וידאו של QuickTime</comment>
+ <comment xml:lang="hr">QuickTime video snimka</comment>
+ <comment xml:lang="hu">QuickTime videó</comment>
+ <comment xml:lang="ia">Video QuickTime</comment>
+ <comment xml:lang="id">Video QuickTime</comment>
+ <comment xml:lang="it">Video QuickTime</comment>
+ <comment xml:lang="ja">QuickTime 動画</comment>
+ <comment xml:lang="kk">QuickTime видеосы</comment>
+ <comment xml:lang="ko">퀵타임 동영상</comment>
+ <comment xml:lang="lt">QuickTime vaizdo įrašas</comment>
+ <comment xml:lang="lv">QuickTime video</comment>
+ <comment xml:lang="ms">Video QuickTime</comment>
+ <comment xml:lang="nb">Quicktime film</comment>
+ <comment xml:lang="nl">QuickTime-video</comment>
+ <comment xml:lang="nn">QuickTime-video</comment>
+ <comment xml:lang="oc">vidèo QuickTime</comment>
+ <comment xml:lang="pl">Plik wideo QuickTime</comment>
+ <comment xml:lang="pt">vídeo QuickTime</comment>
+ <comment xml:lang="pt_BR">Vídeo do QuickTime</comment>
+ <comment xml:lang="ro">Video QuickTime</comment>
+ <comment xml:lang="ru">Видео QuickTime</comment>
+ <comment xml:lang="sk">Video QuickTime</comment>
+ <comment xml:lang="sl">Video datoteka QuickTime</comment>
+ <comment xml:lang="sq">Video QuickTime</comment>
+ <comment xml:lang="sr">Квик Тајм видео</comment>
+ <comment xml:lang="sv">QuickTime-video</comment>
+ <comment xml:lang="tr">QuickTime videosu</comment>
+ <comment xml:lang="uk">відеокліп QuickTime</comment>
+ <comment xml:lang="vi">Ảnh động QuickTime</comment>
+ <comment xml:lang="zh_CN">QuickTime 视频</comment>
+ <comment xml:lang="zh_TW">QuickTime 視訊</comment>
+ <magic priority="50">
+ <match value="mdat" type="string" offset="12"/>
+ <match value="mdat" type="string" offset="4"/>
+ <match value="moov" type="string" offset="4"/>
+ <match value="ftypqt" type="string" offset="4"/>
+ </magic>
+ <glob pattern="*.qt"/>
+ <glob pattern="*.mov"/>
+ <glob pattern="*.moov"/>
+ <glob pattern="*.qtvr"/>
+ </mime-type>
+ <mime-type type="image/x-quicktime">
+ <comment>QuickTime image</comment>
+ <comment xml:lang="ar">صورة QuickTime</comment>
+ <comment xml:lang="be@latin">Vyjava QuickTime</comment>
+ <comment xml:lang="bg">Изображение — QuickTime</comment>
+ <comment xml:lang="ca">imatge QuickTime</comment>
+ <comment xml:lang="cs">obrázek QuickTime</comment>
+ <comment xml:lang="da">QuickTime-billede</comment>
+ <comment xml:lang="de">QuickTime-Bild</comment>
+ <comment xml:lang="el">Εικόνα QuickTime</comment>
+ <comment xml:lang="en_GB">QuickTime image</comment>
+ <comment xml:lang="eo">QuickTime-bildo</comment>
+ <comment xml:lang="es">imagen de QuickTime</comment>
+ <comment xml:lang="eu">QuickTime irudia</comment>
+ <comment xml:lang="fi">QuickTime-kuva</comment>
+ <comment xml:lang="fo">QuickTime mynd</comment>
+ <comment xml:lang="fr">image QuickTime</comment>
+ <comment xml:lang="ga">íomhá QuickTime</comment>
+ <comment xml:lang="gl">imaxe QuickTime</comment>
+ <comment xml:lang="he">תמונה של QuickTime</comment>
+ <comment xml:lang="hr">QuickTime slika</comment>
+ <comment xml:lang="hu">QuickTime kép</comment>
+ <comment xml:lang="ia">Imagine QuickTime</comment>
+ <comment xml:lang="id">Citra QuickTime</comment>
+ <comment xml:lang="it">Immagine QuickTime</comment>
+ <comment xml:lang="ja">QuickTime 画像</comment>
+ <comment xml:lang="kk">QuickTime суреті</comment>
+ <comment xml:lang="ko">퀵타임 그림</comment>
+ <comment xml:lang="lt">QuickTime paveikslėlis</comment>
+ <comment xml:lang="lv">QuickTime attēls</comment>
+ <comment xml:lang="nb">Quicktime bilde</comment>
+ <comment xml:lang="nl">QuickTime-afbeelding</comment>
+ <comment xml:lang="nn">QuickTime-bilete</comment>
+ <comment xml:lang="oc">imatge QuickTime</comment>
+ <comment xml:lang="pl">Obraz QuickTime</comment>
+ <comment xml:lang="pt">imagem QuickTime</comment>
+ <comment xml:lang="pt_BR">Imagem do QuickTime</comment>
+ <comment xml:lang="ro">Imagine QuickTime</comment>
+ <comment xml:lang="ru">Изображение QuickTime</comment>
+ <comment xml:lang="sk">Obrázok QuickTime</comment>
+ <comment xml:lang="sl">Slikovna datoteka QuickTime</comment>
+ <comment xml:lang="sq">Figurë QuickTime</comment>
+ <comment xml:lang="sr">Квик Тајм слика</comment>
+ <comment xml:lang="sv">QuickTime-bild</comment>
+ <comment xml:lang="tr">QuickTime görüntüsü</comment>
+ <comment xml:lang="uk">зображення QuickTime</comment>
+ <comment xml:lang="vi">Ảnh QuickTime</comment>
+ <comment xml:lang="zh_CN">QuickTime 图像</comment>
+ <comment xml:lang="zh_TW">QuickTime 影像</comment>
+ <magic priority="50">
+ <match value="idat" type="string" offset="4"/>
+ </magic>
+ <glob pattern="*.qtif"/>
+ <glob pattern="*.qif"/>
+ </mime-type>
+ <mime-type type="image/ktx">
+ <comment>Khronos texture image</comment>
+ <comment xml:lang="ca">imatge de textura de Khronos</comment>
+ <comment xml:lang="cs">obrázek s texturou Khronos</comment>
+ <comment xml:lang="de">Khronos-Texturbild</comment>
+ <comment xml:lang="en_GB">Khronos texture image</comment>
+ <comment xml:lang="es">imagen de textura de Khronos</comment>
+ <comment xml:lang="fr">image de texture Khronos</comment>
+ <comment xml:lang="ga">íomhá uigeachta Khronos</comment>
+ <comment xml:lang="hr">Khronos tekstura slika</comment>
+ <comment xml:lang="hu">Khronos textúra kép</comment>
+ <comment xml:lang="id">citra tekstur Khronos</comment>
+ <comment xml:lang="it">Immagine texture Khronos</comment>
+ <comment xml:lang="kk">Khronos текстура суреті</comment>
+ <comment xml:lang="ko">크로노스 텍스처 파일</comment>
+ <comment xml:lang="pl">Obraz tekstury Khronos</comment>
+ <comment xml:lang="pt_BR">Imagem de textura do Khronos</comment>
+ <comment xml:lang="ru">Изображение текстуры Khronos</comment>
+ <comment xml:lang="sk">Obrázok textúry Khronos</comment>
+ <comment xml:lang="sr">слика Кронос текстуре</comment>
+ <comment xml:lang="sv">Khronos-texturbild</comment>
+ <comment xml:lang="tr">Khronos kaplama görüntüsü</comment>
+ <comment xml:lang="uk">зображення текстури Khronos</comment>
+ <comment xml:lang="zh_CN">Khronos 纹理图像</comment>
+ <comment xml:lang="zh_TW">Khronos 紋理影像</comment>
+ <magic priority="80">
+ <match value="0xAB4B5458" type="big32" offset="0">
+ <match value="0x203131BB" type="big32" offset="4">
+ <match value="0x0D0A1A0A" type="big32" offset="8"/>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.ktx"/>
+ </mime-type>
+ <mime-type type="video/vnd.vivo">
+ <comment>Vivo video</comment>
+ <comment xml:lang="ar">Vivo مرئي</comment>
+ <comment xml:lang="ast">Videu en Vivo</comment>
+ <comment xml:lang="az">Vivo video faylı</comment>
+ <comment xml:lang="be@latin">Videa Vivo</comment>
+ <comment xml:lang="bg">Видео — Vivo</comment>
+ <comment xml:lang="ca">vídeo Vivo</comment>
+ <comment xml:lang="cs">video Vivo</comment>
+ <comment xml:lang="cy">Fideo Vivo</comment>
+ <comment xml:lang="da">Vivo-video</comment>
+ <comment xml:lang="de">Vivo-Video</comment>
+ <comment xml:lang="el">Βίντεο Vivo</comment>
+ <comment xml:lang="en_GB">Vivo video</comment>
+ <comment xml:lang="eo">Vivo-video</comment>
+ <comment xml:lang="es">vídeo Vivo</comment>
+ <comment xml:lang="eu">Vivo bideoa</comment>
+ <comment xml:lang="fi">Vivo-video</comment>
+ <comment xml:lang="fo">Vivo video</comment>
+ <comment xml:lang="fr">vidéo Vivo</comment>
+ <comment xml:lang="ga">físeán Vivo</comment>
+ <comment xml:lang="gl">vídeo Vivo</comment>
+ <comment xml:lang="he">וידאו של Vivo</comment>
+ <comment xml:lang="hr">Vivo video snimka</comment>
+ <comment xml:lang="hu">Vivo-videó</comment>
+ <comment xml:lang="ia">Video Vivo</comment>
+ <comment xml:lang="id">Video Vivo</comment>
+ <comment xml:lang="it">Video Vivo</comment>
+ <comment xml:lang="ja">Vivo 動画</comment>
+ <comment xml:lang="kk">Vivo видеосы</comment>
+ <comment xml:lang="ko">Vivo 동영상</comment>
+ <comment xml:lang="lt">Vivo vaizdo įrašas</comment>
+ <comment xml:lang="lv">Vivo video</comment>
+ <comment xml:lang="ms">Video Vivo</comment>
+ <comment xml:lang="nb">Vivo-film</comment>
+ <comment xml:lang="nl">Vivo-video</comment>
+ <comment xml:lang="nn">Vivo-film</comment>
+ <comment xml:lang="oc">vidèo Vivo</comment>
+ <comment xml:lang="pl">Plik wideo Vivo</comment>
+ <comment xml:lang="pt">vídeo Vivo</comment>
+ <comment xml:lang="pt_BR">Vídeo Vivo</comment>
+ <comment xml:lang="ro">Video Vivo</comment>
+ <comment xml:lang="ru">Видео Vivo</comment>
+ <comment xml:lang="sk">Video Vivo</comment>
+ <comment xml:lang="sl">Video datoteka Vivo</comment>
+ <comment xml:lang="sq">Video Vivo</comment>
+ <comment xml:lang="sr">Виво видео</comment>
+ <comment xml:lang="sv">Vivo-video</comment>
+ <comment xml:lang="tr">Vivo videosu</comment>
+ <comment xml:lang="uk">відео Vivo</comment>
+ <comment xml:lang="vi">Ảnh động Vivo</comment>
+ <comment xml:lang="zh_CN">Vivo 视频</comment>
+ <comment xml:lang="zh_TW">Vivo 視訊</comment>
+ <alias type="video/vivo"/>
+ <glob pattern="*.viv"/>
+ <glob pattern="*.vivo"/>
+ </mime-type>
+ <mime-type type="video/wavelet">
+ <comment>Wavelet video</comment>
+ <comment xml:lang="ar">Wavelet مرئي</comment>
+ <comment xml:lang="ast">Videu en Wavelet</comment>
+ <comment xml:lang="az">Wavelet video faylı</comment>
+ <comment xml:lang="be@latin">Videa Wavelet</comment>
+ <comment xml:lang="bg">Видео — Wavelet</comment>
+ <comment xml:lang="ca">vídeo Wavelet</comment>
+ <comment xml:lang="cs">video Wavelet</comment>
+ <comment xml:lang="cy">Fideo Wavelet</comment>
+ <comment xml:lang="da">Waveletvideo</comment>
+ <comment xml:lang="de">Wavelet-Video</comment>
+ <comment xml:lang="el">Βίντεο Wavelet</comment>
+ <comment xml:lang="en_GB">Wavelet video</comment>
+ <comment xml:lang="eo">Wavelet-video</comment>
+ <comment xml:lang="es">vídeo Wavelet</comment>
+ <comment xml:lang="eu">Wavelet bideoa</comment>
+ <comment xml:lang="fi">Wavelet-video</comment>
+ <comment xml:lang="fo">Wavelet video</comment>
+ <comment xml:lang="fr">vidéo Wavelet</comment>
+ <comment xml:lang="ga">físeán Wavelet</comment>
+ <comment xml:lang="gl">vídeo Wavelet</comment>
+ <comment xml:lang="he">וידאו של Wavelet</comment>
+ <comment xml:lang="hr">Wavelet video snimka</comment>
+ <comment xml:lang="hu">Wavelet-videó</comment>
+ <comment xml:lang="ia">Video Wavelet</comment>
+ <comment xml:lang="id">Video Wavelet</comment>
+ <comment xml:lang="it">Video Wavelet</comment>
+ <comment xml:lang="ja">Wavelet 動画</comment>
+ <comment xml:lang="kk">Wavelet видеосы</comment>
+ <comment xml:lang="ko">Wavelet 동영상</comment>
+ <comment xml:lang="lt">Wavelet vaizdo įrašas</comment>
+ <comment xml:lang="lv">Wavelet video</comment>
+ <comment xml:lang="ms">Video Wavelet</comment>
+ <comment xml:lang="nb">Wavelet-film</comment>
+ <comment xml:lang="nl">Wavelet-video</comment>
+ <comment xml:lang="nn">Wavelet video</comment>
+ <comment xml:lang="oc">vidèo Wavelet</comment>
+ <comment xml:lang="pl">Plik wideo Wavelet</comment>
+ <comment xml:lang="pt">vídeo Wavelet</comment>
+ <comment xml:lang="pt_BR">Vídeo Wavelet</comment>
+ <comment xml:lang="ro">Video Wavelet</comment>
+ <comment xml:lang="ru">Видео Wavelet</comment>
+ <comment xml:lang="sk">Video Wavelet</comment>
+ <comment xml:lang="sl">Video datoteka Wavelet</comment>
+ <comment xml:lang="sq">Video Wavelet</comment>
+ <comment xml:lang="sr">Вејвелет видео</comment>
+ <comment xml:lang="sv">Wavelet-video</comment>
+ <comment xml:lang="tr">Wavelet videosu</comment>
+ <comment xml:lang="uk">відеокліп Wavelet</comment>
+ <comment xml:lang="vi">Ảnh động Wavelet</comment>
+ <comment xml:lang="zh_CN">Wavelet 视频</comment>
+ <comment xml:lang="zh_TW">Wavelet 視訊</comment>
+ </mime-type>
+ <mime-type type="video/x-anim">
+ <comment>ANIM animation</comment>
+ <comment xml:lang="ar">تحريكة ANIM</comment>
+ <comment xml:lang="az">ANIM animasiyası</comment>
+ <comment xml:lang="be@latin">Animacyja ANIM</comment>
+ <comment xml:lang="bg">Анимация — ANIM</comment>
+ <comment xml:lang="ca">animació ANIM</comment>
+ <comment xml:lang="cs">animace ANIM</comment>
+ <comment xml:lang="cy">Animeiddiad ANIM</comment>
+ <comment xml:lang="da">ANIM-animation</comment>
+ <comment xml:lang="de">ANIM-Animation</comment>
+ <comment xml:lang="el">Κινούμενο σχέδιο ANIM</comment>
+ <comment xml:lang="en_GB">ANIM animation</comment>
+ <comment xml:lang="eo">ANIM-animacio</comment>
+ <comment xml:lang="es">animación ANIM</comment>
+ <comment xml:lang="eu">ANIM animazioa</comment>
+ <comment xml:lang="fi">ANIM-animaatio</comment>
+ <comment xml:lang="fo">ANIM teknmyndagerð</comment>
+ <comment xml:lang="fr">animation ANIM</comment>
+ <comment xml:lang="ga">beochan ANIM</comment>
+ <comment xml:lang="gl">animación ANIM</comment>
+ <comment xml:lang="he">הנפשת ANIM</comment>
+ <comment xml:lang="hr">ANIM animacija</comment>
+ <comment xml:lang="hu">ANIM-animáció</comment>
+ <comment xml:lang="ia">Animation ANIM</comment>
+ <comment xml:lang="id">Animasi ANIM</comment>
+ <comment xml:lang="it">Animazione ANIM</comment>
+ <comment xml:lang="ja">ANIM アニメーション</comment>
+ <comment xml:lang="ka">ANIM ანიმაცია</comment>
+ <comment xml:lang="kk">ANIM анимациясы</comment>
+ <comment xml:lang="ko">ANIM 동화상</comment>
+ <comment xml:lang="lt">ANIM animacija</comment>
+ <comment xml:lang="lv">ANIM animācija</comment>
+ <comment xml:lang="ms">Animasi ANIM</comment>
+ <comment xml:lang="nb">ANIM-animasjon</comment>
+ <comment xml:lang="nl">ANIM-animatie</comment>
+ <comment xml:lang="nn">ANIM-animasjon</comment>
+ <comment xml:lang="oc">animacion ANIM</comment>
+ <comment xml:lang="pl">Plik animacji ANIM</comment>
+ <comment xml:lang="pt">animação ANIM</comment>
+ <comment xml:lang="pt_BR">Animação ANIM</comment>
+ <comment xml:lang="ro">Animație ANIM</comment>
+ <comment xml:lang="ru">Анимация ANIM</comment>
+ <comment xml:lang="sk">Animácia ANIM</comment>
+ <comment xml:lang="sl">Datoteka animacije ANIM</comment>
+ <comment xml:lang="sq">Animim ANIM</comment>
+ <comment xml:lang="sr">АНИМ анимација</comment>
+ <comment xml:lang="sv">ANIM-animering</comment>
+ <comment xml:lang="tr">ANIM canlandırması</comment>
+ <comment xml:lang="uk">анімація ANIM</comment>
+ <comment xml:lang="vi">Hoạt ảnh ANIM</comment>
+ <comment xml:lang="zh_CN">ANIM 动画</comment>
+ <comment xml:lang="zh_TW">ANIM 動畫</comment>
+ <glob pattern="*.anim[1-9j]"/>
+ </mime-type>
+ <mime-type type="video/x-flic">
+ <comment>FLIC animation</comment>
+ <comment xml:lang="ar">تحريكة FLIC</comment>
+ <comment xml:lang="be@latin">Animacyja FLIC</comment>
+ <comment xml:lang="bg">Анимация — FLIC</comment>
+ <comment xml:lang="ca">animació FLIC</comment>
+ <comment xml:lang="cs">animace FLIC</comment>
+ <comment xml:lang="da">FLIC-animation</comment>
+ <comment xml:lang="de">FLIC-Animation</comment>
+ <comment xml:lang="el">Κινούμενο σχέδιο FLIC</comment>
+ <comment xml:lang="en_GB">FLIC animation</comment>
+ <comment xml:lang="es">animación FLIC</comment>
+ <comment xml:lang="eu">FLIC animazioa</comment>
+ <comment xml:lang="fi">FLIC-animaatio</comment>
+ <comment xml:lang="fo">FLIC teknimyndagerð</comment>
+ <comment xml:lang="fr">animation FLIC</comment>
+ <comment xml:lang="ga">beochan FLIC</comment>
+ <comment xml:lang="gl">animación FLIC</comment>
+ <comment xml:lang="he">הנפשת FLIC</comment>
+ <comment xml:lang="hr">FLIC animacija</comment>
+ <comment xml:lang="hu">FLIC animáció</comment>
+ <comment xml:lang="ia">Animation FLIC</comment>
+ <comment xml:lang="id">Animasi FLIC</comment>
+ <comment xml:lang="it">Animazione FLIC</comment>
+ <comment xml:lang="ja">FLIC アニメーション</comment>
+ <comment xml:lang="ka">FLIC ანიმაცია</comment>
+ <comment xml:lang="kk">FLIC анимациясы</comment>
+ <comment xml:lang="ko">FLIC 동화상</comment>
+ <comment xml:lang="lt">FLIC animacija</comment>
+ <comment xml:lang="lv">FLIC animācija</comment>
+ <comment xml:lang="nb">FLIC-animasjon</comment>
+ <comment xml:lang="nl">FLIC-animatie</comment>
+ <comment xml:lang="nn">FLIC-animasjon</comment>
+ <comment xml:lang="oc">animacion FLIC</comment>
+ <comment xml:lang="pl">Plik animacji FLIC</comment>
+ <comment xml:lang="pt">animação FLIC</comment>
+ <comment xml:lang="pt_BR">Animação FLIC</comment>
+ <comment xml:lang="ro">Animație FLIC</comment>
+ <comment xml:lang="ru">Анимация FLIC</comment>
+ <comment xml:lang="sk">Animácia FLIC</comment>
+ <comment xml:lang="sl">Datoteka animacije FLIC</comment>
+ <comment xml:lang="sq">Animim FLIC</comment>
+ <comment xml:lang="sr">ФЛИЦ анимација</comment>
+ <comment xml:lang="sv">FLIC-animering</comment>
+ <comment xml:lang="tr">FLIC animasyonu</comment>
+ <comment xml:lang="uk">анімація FLIC</comment>
+ <comment xml:lang="vi">Hoạt ảnh FLIC</comment>
+ <comment xml:lang="zh_CN">FLIC 动画</comment>
+ <comment xml:lang="zh_TW">FLIC 動畫</comment>
+ <alias type="video/fli"/>
+ <alias type="video/x-fli"/>
+ <magic priority="50">
+ <match value="0xAF11" type="little16" offset="0"/>
+ <match value="0xAF12" type="little16" offset="0"/>
+ </magic>
+ <glob pattern="*.fli"/>
+ <glob pattern="*.flc"/>
+ </mime-type>
+ <mime-type type="application/x-hwp">
+ <comment>Haansoft Hangul document</comment>
+ <comment xml:lang="ar">مستند Haansoft Hangul</comment>
+ <comment xml:lang="ast">Documentu de Haansoft Hangul</comment>
+ <comment xml:lang="be@latin">Dakument Haansoft Hangul</comment>
+ <comment xml:lang="bg">Документ — Haansoft Hangul</comment>
+ <comment xml:lang="ca">document d'Haansoft Hangul</comment>
+ <comment xml:lang="cs">dokument Haansoft Hangul</comment>
+ <comment xml:lang="da">Haansoft Hangul-dokument</comment>
+ <comment xml:lang="de">Haansoft-Hangul-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Haansoft Hangul</comment>
+ <comment xml:lang="en_GB">Haansoft Hangul document</comment>
+ <comment xml:lang="es">documento de Haansoft Hangul</comment>
+ <comment xml:lang="eu">Haansoft Hangul dokumentua</comment>
+ <comment xml:lang="fi">Haansoft Hangul -asiakirja</comment>
+ <comment xml:lang="fo">Haansoft Hangul skjal</comment>
+ <comment xml:lang="fr">document Haansoft Hangul</comment>
+ <comment xml:lang="ga">cáipéis Haansoft Hangul</comment>
+ <comment xml:lang="gl">documento de Haansoft Hangul</comment>
+ <comment xml:lang="he">מסמך Haansoft Hangul</comment>
+ <comment xml:lang="hr">Haansoft Hangul dokument</comment>
+ <comment xml:lang="hu">Haansoft hangul dokumentum</comment>
+ <comment xml:lang="ia">Documento Haansoft Hangul</comment>
+ <comment xml:lang="id">Dokumen Haansoft Hangul</comment>
+ <comment xml:lang="it">Documento Haansoft Hangul</comment>
+ <comment xml:lang="ja">Haansoft Hangul ドキュメント</comment>
+ <comment xml:lang="kk">Haansoft Hangul құжаты</comment>
+ <comment xml:lang="ko">한소프트 한글 문서</comment>
+ <comment xml:lang="lt">Haansoft Hangul dokumentas</comment>
+ <comment xml:lang="lv">Haansoft Hangul dokuments</comment>
+ <comment xml:lang="nb">Haansoft Hangul-dokument</comment>
+ <comment xml:lang="nl">Haansoft Hangul-document</comment>
+ <comment xml:lang="nn">Haansoft Hangul-dokument</comment>
+ <comment xml:lang="oc">document Haansoft Hangul</comment>
+ <comment xml:lang="pl">Dokument Haansoft Hangul</comment>
+ <comment xml:lang="pt">documento Haansoft Hangul</comment>
+ <comment xml:lang="pt_BR">Documento do Haansoft Hangul</comment>
+ <comment xml:lang="ro">Document Haansoft Hangul</comment>
+ <comment xml:lang="ru">Документ Haansoft Hangul</comment>
+ <comment xml:lang="sk">Dokument Haansoft Hangul</comment>
+ <comment xml:lang="sl">Dokument Haansoft Hangul</comment>
+ <comment xml:lang="sq">Dokument Haansoft Hangul</comment>
+ <comment xml:lang="sr">Хансофт Хангул документ</comment>
+ <comment xml:lang="sv">Haansoft Hangul-dokument</comment>
+ <comment xml:lang="tr">Haansoft Hangul belgesi</comment>
+ <comment xml:lang="uk">документ Haansoft Hangul</comment>
+ <comment xml:lang="vi">Tài liệu Hangul Haansoft</comment>
+ <comment xml:lang="zh_CN">Haansoft Hangul 文档</comment>
+ <comment xml:lang="zh_TW">Haansoft 韓文文件</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="HWP Document File" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.hwp"/>
+ <alias type="application/vnd.haansoft-hwp"/>
+ </mime-type>
+ <mime-type type="application/x-hwt">
+ <comment>Haansoft Hangul document template</comment>
+ <comment xml:lang="ar">قالب مستند Haansoft Hangul</comment>
+ <comment xml:lang="ast">Plantía de documentu de Haansoft Hangul</comment>
+ <comment xml:lang="be@latin">Šablon dakumentu Haansoft Hangul</comment>
+ <comment xml:lang="bg">Шаблон за документи — Haansoft Hangul</comment>
+ <comment xml:lang="ca">plantilla de document d'Haansoft Hangul</comment>
+ <comment xml:lang="cs">šablona dokumentu Haansoft Hangul</comment>
+ <comment xml:lang="da">Haansoft Hangul-dokumentskabelon</comment>
+ <comment xml:lang="de">Haansoft-Hangul-Dokumentvorlage</comment>
+ <comment xml:lang="el">Πρότυπο εγγράφου Haansoft Hangul</comment>
+ <comment xml:lang="en_GB">Haansoft Hangul document template</comment>
+ <comment xml:lang="es">plantilla de documento de Haansoft Hangul</comment>
+ <comment xml:lang="eu">Haansoft Hangul dokumentuaren txantiloia</comment>
+ <comment xml:lang="fi">Haansoft Hangul -asiakirjamalli</comment>
+ <comment xml:lang="fo">Haansoft Hangul skjalaformur</comment>
+ <comment xml:lang="fr">modèle de document Haansoft Hangul</comment>
+ <comment xml:lang="ga">teimpléad cháipéis Haansoft Hangul</comment>
+ <comment xml:lang="gl">modelo de documento de Haansoft Hangul</comment>
+ <comment xml:lang="he">תבנית מסמך של Haansoft Hangul</comment>
+ <comment xml:lang="hr">Haansoft Hangul predložak dokumenta</comment>
+ <comment xml:lang="hu">Haansoft hangul dokumentumsablon</comment>
+ <comment xml:lang="ia">Patrono de documento Haansoft Hangul</comment>
+ <comment xml:lang="id">Templat dokumen Haansoft Hangul</comment>
+ <comment xml:lang="it">Modello documento Haansoft Hangul</comment>
+ <comment xml:lang="ja">Haansoft Hangul ドキュメントテンプレート</comment>
+ <comment xml:lang="kk">Haansoft Hangul құжат үлгісі</comment>
+ <comment xml:lang="ko">한소프트 한글 문서 서식</comment>
+ <comment xml:lang="lt">Haansoft Hangul dokumento šablonas</comment>
+ <comment xml:lang="lv">Haansoft Hangul dokumentu veidne</comment>
+ <comment xml:lang="nb">Haansoft Hangul-dokumentmal</comment>
+ <comment xml:lang="nl">Haansoft Hangul-documentsjabloon</comment>
+ <comment xml:lang="nn">Haansoft Hangul-dokumentmal</comment>
+ <comment xml:lang="oc">modèl de document Haansoft Hangul</comment>
+ <comment xml:lang="pl">Szablon dokumentu Haansoft Hangul</comment>
+ <comment xml:lang="pt">modelo de documento Haansoft Hangul</comment>
+ <comment xml:lang="pt_BR">Modelo de documento do Haansoft Hangul</comment>
+ <comment xml:lang="ro">Document șablon Haansoft Hangul</comment>
+ <comment xml:lang="ru">Шаблон документа Haansoft Hangul</comment>
+ <comment xml:lang="sk">Šablóna dokumentu Haansoft Hangul</comment>
+ <comment xml:lang="sl">Predloga dokumenta Haansoft Hangul</comment>
+ <comment xml:lang="sq">Model dokumenti Haansoft Hangul</comment>
+ <comment xml:lang="sr">шаблон Хансофт Хангул документа</comment>
+ <comment xml:lang="sv">Haansoft Hangul-dokumentmall</comment>
+ <comment xml:lang="tr">Haansoft Hangul belge şablonu</comment>
+ <comment xml:lang="uk">шаблон документа Haansoft Hangul</comment>
+ <comment xml:lang="vi">Mẫu tài liệu Hangul Haansoft</comment>
+ <comment xml:lang="zh_CN">Haansoft Hangul 文档模板</comment>
+ <comment xml:lang="zh_TW">Haansoft 韓文文件範本</comment>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.hwt"/>
+ <alias type="application/vnd.haansoft-hwt"/>
+ </mime-type>
+ <mime-type type="video/x-mng">
+ <comment>MNG animation</comment>
+ <comment xml:lang="ar">تحريكة MNG</comment>
+ <comment xml:lang="be@latin">Animacyja MNG</comment>
+ <comment xml:lang="bg">Анимация — MNG</comment>
+ <comment xml:lang="ca">animació MNG</comment>
+ <comment xml:lang="cs">animace MNG</comment>
+ <comment xml:lang="da">MNG-animation</comment>
+ <comment xml:lang="de">MNG-Animation</comment>
+ <comment xml:lang="el">Κινούμενο σχέδιο MNG</comment>
+ <comment xml:lang="en_GB">MNG animation</comment>
+ <comment xml:lang="eo">MNG-animacio</comment>
+ <comment xml:lang="es">animación MNG</comment>
+ <comment xml:lang="eu">MNG animazioa</comment>
+ <comment xml:lang="fi">MNG-animaatio</comment>
+ <comment xml:lang="fo">MNG teknimyndagerð</comment>
+ <comment xml:lang="fr">animation MNG</comment>
+ <comment xml:lang="ga">beochan MNG</comment>
+ <comment xml:lang="gl">animación MNG</comment>
+ <comment xml:lang="he">הנפשת MNG</comment>
+ <comment xml:lang="hr">MNG animacija</comment>
+ <comment xml:lang="hu">MNG-animáció</comment>
+ <comment xml:lang="ia">Animation MNG</comment>
+ <comment xml:lang="id">Animasi MNG</comment>
+ <comment xml:lang="it">Animazione MNG</comment>
+ <comment xml:lang="ja">MNG アニメーション</comment>
+ <comment xml:lang="kk">MNG анимациясы</comment>
+ <comment xml:lang="ko">MNG 동화상</comment>
+ <comment xml:lang="lt">MNG animacija</comment>
+ <comment xml:lang="lv">MNG animācija</comment>
+ <comment xml:lang="ms">Animasi MNG</comment>
+ <comment xml:lang="nb">MNG-animasjon</comment>
+ <comment xml:lang="nl">MNG-animatie</comment>
+ <comment xml:lang="nn">MNG-animasjon</comment>
+ <comment xml:lang="oc">animacion MNG</comment>
+ <comment xml:lang="pl">Animacja MNG</comment>
+ <comment xml:lang="pt">animação MNG</comment>
+ <comment xml:lang="pt_BR">Animação MNG</comment>
+ <comment xml:lang="ro">Animație MNG</comment>
+ <comment xml:lang="ru">Анимация MNG</comment>
+ <comment xml:lang="sk">Animácia MNG</comment>
+ <comment xml:lang="sl">Datoteka animacije MNG</comment>
+ <comment xml:lang="sq">Animim MNG</comment>
+ <comment xml:lang="sr">МНГ анимација</comment>
+ <comment xml:lang="sv">MNG-animering</comment>
+ <comment xml:lang="tr">MNG canlandırması</comment>
+ <comment xml:lang="uk">анімація MNG</comment>
+ <comment xml:lang="vi">Hoạt ảnh MNG</comment>
+ <comment xml:lang="zh_CN">MNG 动画</comment>
+ <comment xml:lang="zh_TW">MNG 動畫</comment>
+ <acronym>MNG</acronym>
+ <expanded-acronym>Multiple-Image Network Graphics</expanded-acronym>
+ <magic priority="50">
+ <match value="\x8AMNG\x0D\x0A\x1A\x0A" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.mng"/>
+ </mime-type>
+ <mime-type type="application/vnd.ms-asf">
+ <comment>ASF video</comment>
+ <comment xml:lang="ar">ASF مرئي</comment>
+ <comment xml:lang="ast">Videu n'ASF</comment>
+ <comment xml:lang="be@latin">Videa ASF</comment>
+ <comment xml:lang="bg">Видео — ASF</comment>
+ <comment xml:lang="ca">vídeo ASF</comment>
+ <comment xml:lang="cs">video ASF</comment>
+ <comment xml:lang="da">ASF-video</comment>
+ <comment xml:lang="de">ASF-Video</comment>
+ <comment xml:lang="el">Βίντεο ASF</comment>
+ <comment xml:lang="en_GB">ASF video</comment>
+ <comment xml:lang="eo">ASF-video</comment>
+ <comment xml:lang="es">vídeo ASF</comment>
+ <comment xml:lang="eu">ASF bideoa</comment>
+ <comment xml:lang="fi">ASF-video</comment>
+ <comment xml:lang="fo">ASF video</comment>
+ <comment xml:lang="fr">vidéo ASF</comment>
+ <comment xml:lang="ga">físeán ASF</comment>
+ <comment xml:lang="gl">vídeo ASF</comment>
+ <comment xml:lang="he">וידאו ASF</comment>
+ <comment xml:lang="hr">ASF video snimka</comment>
+ <comment xml:lang="hu">ASF videó</comment>
+ <comment xml:lang="ia">Video ASF</comment>
+ <comment xml:lang="id">Video ASF</comment>
+ <comment xml:lang="it">Video ASF</comment>
+ <comment xml:lang="ja">ASF 動画</comment>
+ <comment xml:lang="ka">ASF ვიდეო</comment>
+ <comment xml:lang="kk">ASF видеосы</comment>
+ <comment xml:lang="ko">ASF 동영상</comment>
+ <comment xml:lang="lt">ASF vaizdo įrašas</comment>
+ <comment xml:lang="lv">ASF video</comment>
+ <comment xml:lang="nb">ASF-film</comment>
+ <comment xml:lang="nl">ASF-video</comment>
+ <comment xml:lang="nn">ASF-video</comment>
+ <comment xml:lang="oc">vidèo ASF</comment>
+ <comment xml:lang="pl">Plik wideo ASF</comment>
+ <comment xml:lang="pt">vídeo ASF</comment>
+ <comment xml:lang="pt_BR">Vídeo ASF</comment>
+ <comment xml:lang="ro">Video ASF</comment>
+ <comment xml:lang="ru">Видео ASF</comment>
+ <comment xml:lang="sk">Video ASF</comment>
+ <comment xml:lang="sl">Video datoteka ASF</comment>
+ <comment xml:lang="sq">Video ASF</comment>
+ <comment xml:lang="sr">АСФ видео</comment>
+ <comment xml:lang="sv">ASF-video</comment>
+ <comment xml:lang="tr">ASF videosu</comment>
+ <comment xml:lang="uk">відеокліп ASF</comment>
+ <comment xml:lang="vi">Ảnh động ASF</comment>
+ <comment xml:lang="zh_CN">ASF 视频</comment>
+ <comment xml:lang="zh_TW">ASF 視訊</comment>
+ <acronym>ASF</acronym>
+ <expanded-acronym>Advanced Streaming Format</expanded-acronym>
+ <alias type="video/x-ms-wm"/>
+ <alias type="video/x-ms-asf"/>
+ <alias type="video/x-ms-asf-plugin"/>
+ <glob pattern="*.asf"/>
+ <magic priority="50">
+ <match value="0x3026b275" type="big32" offset="0"/>
+ <match value="[Reference]" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="application/x-netshow-channel">
+ <comment>Windows Media Station file</comment>
+ <comment xml:lang="ar">ملف محطة Windows Media</comment>
+ <comment xml:lang="be@latin">Fajł Windows Media Station</comment>
+ <comment xml:lang="bg">Файл — Windows Media Station</comment>
+ <comment xml:lang="ca">fitxer de Windows Media Station</comment>
+ <comment xml:lang="cs">soubor Windows Media Station</comment>
+ <comment xml:lang="da">Windows Media Station-fil</comment>
+ <comment xml:lang="de">Windows-Media-Streamingbeschreibung</comment>
+ <comment xml:lang="el">Αρχείο Windows Media Station</comment>
+ <comment xml:lang="en_GB">Windows Media Station file</comment>
+ <comment xml:lang="es">archivo de emisora de Windows Media</comment>
+ <comment xml:lang="eu">Windows Media Station fitxategia</comment>
+ <comment xml:lang="fi">Windows Media Station-tiedosto</comment>
+ <comment xml:lang="fo">Windows Media Station fíla</comment>
+ <comment xml:lang="fr">fichier Windows Media Station</comment>
+ <comment xml:lang="ga">comhad Windows Media Station</comment>
+ <comment xml:lang="gl">ficheiro de emisora de Windows Media</comment>
+ <comment xml:lang="he">קובץ תחנה של Windows Media</comment>
+ <comment xml:lang="hr">Windows Media Station datoteka</comment>
+ <comment xml:lang="hu">Windows Media Station fájl</comment>
+ <comment xml:lang="ia">File de station Windows Media</comment>
+ <comment xml:lang="id">Berkas Windows Media Station</comment>
+ <comment xml:lang="it">File Windows Media Station</comment>
+ <comment xml:lang="ja">Windows Media Station ファイル</comment>
+ <comment xml:lang="kk">Windows Media Station файлы</comment>
+ <comment xml:lang="ko">Windows Media Station 파일</comment>
+ <comment xml:lang="lt">Windows Media Station failas</comment>
+ <comment xml:lang="lv">Windows Media Station datne</comment>
+ <comment xml:lang="nb">Windows Media Station-fil</comment>
+ <comment xml:lang="nl">Windows Media Station-bestand</comment>
+ <comment xml:lang="nn">Windows Media Station-fil</comment>
+ <comment xml:lang="oc">fichièr Windows Media Station</comment>
+ <comment xml:lang="pl">Plik Windows Media Station</comment>
+ <comment xml:lang="pt">ficheiro Windows Media Station</comment>
+ <comment xml:lang="pt_BR">Arquivo de estação do Windows Media</comment>
+ <comment xml:lang="ro">Fișier Windows Media Station</comment>
+ <comment xml:lang="ru">Файл Windows Media Station</comment>
+ <comment xml:lang="sk">Súbor Windows Media Station</comment>
+ <comment xml:lang="sl">Datoteka Windows Media Station</comment>
+ <comment xml:lang="sq">File Windows Media Station</comment>
+ <comment xml:lang="sr">датотека станице Виндоузовог Медија</comment>
+ <comment xml:lang="sv">Windows Media Station-fil</comment>
+ <comment xml:lang="tr">Windows Media Station dosyası</comment>
+ <comment xml:lang="uk">файл Windows Media Station</comment>
+ <comment xml:lang="vi">Tập tin Windows Media Station</comment>
+ <comment xml:lang="zh_CN">Windows 媒体工作站文件</comment>
+ <comment xml:lang="zh_TW">Windows Media Station 檔</comment>
+ <sub-class-of type="application/vnd.ms-asf"/>
+ <generic-icon name="video-x-generic"/>
+ <glob pattern="*.nsc"/>
+ <magic priority="50">
+ <match value="[Address]" type="string" offset="0"/>
+ </magic>
+ </mime-type>
+ <mime-type type="video/x-ms-wmv">
+ <comment>Windows Media video</comment>
+ <comment xml:lang="ar">Windows Media مرئي</comment>
+ <comment xml:lang="ast">Videu de Windows Media</comment>
+ <comment xml:lang="be@latin">Videa Windows Media</comment>
+ <comment xml:lang="bg">Видео — Windows Media</comment>
+ <comment xml:lang="ca">vídeo de Windows Media</comment>
+ <comment xml:lang="cs">video Windows Media</comment>
+ <comment xml:lang="da">Windows Medie-video</comment>
+ <comment xml:lang="de">Windows-Media-Video</comment>
+ <comment xml:lang="el">Βίντεο Windows Media</comment>
+ <comment xml:lang="en_GB">Windows Media video</comment>
+ <comment xml:lang="es">vídeo de Windows Media</comment>
+ <comment xml:lang="eu">Windows Media bideoa</comment>
+ <comment xml:lang="fi">Windows Media -video</comment>
+ <comment xml:lang="fo">Windows Media video</comment>
+ <comment xml:lang="fr">vidéo Windows Media</comment>
+ <comment xml:lang="ga">físeán Windows Media</comment>
+ <comment xml:lang="gl">vídeo de Windows Media</comment>
+ <comment xml:lang="he">וידאו של Windows Media</comment>
+ <comment xml:lang="hr">Windows Media video snimka</comment>
+ <comment xml:lang="hu">Windows Media videó</comment>
+ <comment xml:lang="ia">Video Windows Media</comment>
+ <comment xml:lang="id">Video Windows Media</comment>
+ <comment xml:lang="it">Video Windows Media</comment>
+ <comment xml:lang="ja">Windows Media 動画</comment>
+ <comment xml:lang="kk">Windows Media видеосы</comment>
+ <comment xml:lang="ko">Windows 미디어 오디오</comment>
+ <comment xml:lang="lt">Windows Media vaizdo įrašas</comment>
+ <comment xml:lang="lv">Windows Media video</comment>
+ <comment xml:lang="nb">Windows Media film</comment>
+ <comment xml:lang="nl">Windows Media-video</comment>
+ <comment xml:lang="nn">Windows Media-video</comment>
+ <comment xml:lang="oc">vidèo Windows Media</comment>
+ <comment xml:lang="pl">Plik wideo Windows Media</comment>
+ <comment xml:lang="pt">vídeo Windows Media</comment>
+ <comment xml:lang="pt_BR">Vídeo do Windows Media</comment>
+ <comment xml:lang="ro">Video Windows Media</comment>
+ <comment xml:lang="ru">Видео Windows Media</comment>
+ <comment xml:lang="sk">Video Windows Media</comment>
+ <comment xml:lang="sl">Video datoteka Windows Media</comment>
+ <comment xml:lang="sq">Video Windows Media</comment>
+ <comment xml:lang="sr">Виндоуз Медија видео</comment>
+ <comment xml:lang="sv">Windows Media-video</comment>
+ <comment xml:lang="tr">Windows Media videosu</comment>
+ <comment xml:lang="uk">відеокліп Windows Media</comment>
+ <comment xml:lang="vi">Ảnh động Windows Media</comment>
+ <comment xml:lang="zh_CN">Windows Media 视频</comment>
+ <comment xml:lang="zh_TW">Windows Media 視訊</comment>
+ <sub-class-of type="application/vnd.ms-asf"/>
+ <glob pattern="*.wmv"/>
+ </mime-type>
+ <mime-type type="video/x-msvideo">
+ <comment>AVI video</comment>
+ <comment xml:lang="ar">AVI مرئي</comment>
+ <comment xml:lang="ast">Videu n'AVI</comment>
+ <comment xml:lang="az">AVI video faylı</comment>
+ <comment xml:lang="be@latin">Videa AVI</comment>
+ <comment xml:lang="bg">Видео — AVI</comment>
+ <comment xml:lang="ca">vídeo AVI</comment>
+ <comment xml:lang="cs">video AVI</comment>
+ <comment xml:lang="cy">Fideo AVI</comment>
+ <comment xml:lang="da">AVI-video</comment>
+ <comment xml:lang="de">AVI-Video</comment>
+ <comment xml:lang="el">Βίντεο AVI</comment>
+ <comment xml:lang="en_GB">AVI video</comment>
+ <comment xml:lang="eo">AVI-video</comment>
+ <comment xml:lang="es">vídeo AVI</comment>
+ <comment xml:lang="eu">AVI bideoa</comment>
+ <comment xml:lang="fi">AVI-video</comment>
+ <comment xml:lang="fo">AVI video</comment>
+ <comment xml:lang="fr">vidéo AVI</comment>
+ <comment xml:lang="ga">físeán AVI</comment>
+ <comment xml:lang="gl">vídeo AVI</comment>
+ <comment xml:lang="he">וידאו AVI</comment>
+ <comment xml:lang="hr">AVI video snimka</comment>
+ <comment xml:lang="hu">AVI-videó</comment>
+ <comment xml:lang="ia">Video AVI</comment>
+ <comment xml:lang="id">Video AVI</comment>
+ <comment xml:lang="it">Video AVI</comment>
+ <comment xml:lang="ja">AVI 動画</comment>
+ <comment xml:lang="ka">AVI ვიდეო</comment>
+ <comment xml:lang="kk">AVI видеосы</comment>
+ <comment xml:lang="ko">AVI 동영상</comment>
+ <comment xml:lang="lt">AVI vaizdo įrašas</comment>
+ <comment xml:lang="lv">AVI video</comment>
+ <comment xml:lang="ms">Video AVI</comment>
+ <comment xml:lang="nb">AVI-film</comment>
+ <comment xml:lang="nl">AVI-video</comment>
+ <comment xml:lang="nn">AVI-video</comment>
+ <comment xml:lang="oc">vidèo AVI</comment>
+ <comment xml:lang="pl">Plik wideo AVI</comment>
+ <comment xml:lang="pt">vídeo AVI</comment>
+ <comment xml:lang="pt_BR">Vídeo AVI</comment>
+ <comment xml:lang="ro">Video AVI</comment>
+ <comment xml:lang="ru">Видео AVI</comment>
+ <comment xml:lang="sk">Video AVI</comment>
+ <comment xml:lang="sl">Video datoteka AVI</comment>
+ <comment xml:lang="sq">Video AVI</comment>
+ <comment xml:lang="sr">АВИ видео</comment>
+ <comment xml:lang="sv">AVI-video</comment>
+ <comment xml:lang="tr">AVI videosu</comment>
+ <comment xml:lang="uk">відеокліп AVI</comment>
+ <comment xml:lang="vi">Ảnh động AVI</comment>
+ <comment xml:lang="zh_CN">AVI 视频</comment>
+ <comment xml:lang="zh_TW">AVI 視訊</comment>
+ <acronym>AVI</acronym>
+ <expanded-acronym>Audio Video Interleave</expanded-acronym>
+ <alias type="video/x-avi"/>
+ <alias type="video/avi"/>
+ <alias type="video/divx"/>
+ <alias type="video/msvideo"/>
+ <alias type="video/vnd.divx"/>
+ <magic priority="50">
+ <match value="RIFF" type="string" offset="0">
+ <match value="AVI " type="string" offset="8"/>
+ </match>
+ <match value="AVF0" type="string" offset="0">
+ <match value="AVI " type="string" offset="8"/>
+ </match>
+ </magic>
+ <glob pattern="*.avi"/>
+ <glob pattern="*.avf"/>
+ <glob pattern="*.divx"/>
+ </mime-type>
+ <mime-type type="video/x-nsv">
+ <comment>NullSoft video</comment>
+ <comment xml:lang="ar">NullSoft مرئي</comment>
+ <comment xml:lang="ast">Videu de NullSoft</comment>
+ <comment xml:lang="be@latin">Videa NullSoft</comment>
+ <comment xml:lang="bg">Видео — NullSoft</comment>
+ <comment xml:lang="ca">vídeo NullSoft</comment>
+ <comment xml:lang="cs">video NullSoft</comment>
+ <comment xml:lang="da">NullSoft-video</comment>
+ <comment xml:lang="de">NullSoft-Video</comment>
+ <comment xml:lang="el">Βίντεο Nullsoft</comment>
+ <comment xml:lang="en_GB">NullSoft video</comment>
+ <comment xml:lang="eo">NullSoft-video</comment>
+ <comment xml:lang="es">vídeo NullSoft</comment>
+ <comment xml:lang="eu">NullSoft bideoa</comment>
+ <comment xml:lang="fi">NullSoft-video</comment>
+ <comment xml:lang="fo">NullSoft video</comment>
+ <comment xml:lang="fr">vidéo NullSoft</comment>
+ <comment xml:lang="ga">físeán NullSoft</comment>
+ <comment xml:lang="gl">vídeo de NullSoft</comment>
+ <comment xml:lang="he">וידאו של NullSot</comment>
+ <comment xml:lang="hr">NullSoft video snimka</comment>
+ <comment xml:lang="hu">NullSoft videó</comment>
+ <comment xml:lang="ia">Video NullSoft</comment>
+ <comment xml:lang="id">Video NullSoft</comment>
+ <comment xml:lang="it">Video NullSoft</comment>
+ <comment xml:lang="ja">NullSoft 動画</comment>
+ <comment xml:lang="kk">NullSoft видеосы</comment>
+ <comment xml:lang="ko">널소프트 동영상</comment>
+ <comment xml:lang="lt">NullSoft vaizdo įrašas</comment>
+ <comment xml:lang="lv">NullSoft video</comment>
+ <comment xml:lang="nb">Nullsoft-film</comment>
+ <comment xml:lang="nl">NullSoft-video</comment>
+ <comment xml:lang="nn">NullSoft-video</comment>
+ <comment xml:lang="oc">vidèo NullSoft</comment>
+ <comment xml:lang="pl">Plik wideo NullSoft</comment>
+ <comment xml:lang="pt">vídeo NullSoft</comment>
+ <comment xml:lang="pt_BR">Vídeo do NullSoft</comment>
+ <comment xml:lang="ro">Video NullSoft</comment>
+ <comment xml:lang="ru">Видео Nullsoft</comment>
+ <comment xml:lang="sk">Video NullSoft</comment>
+ <comment xml:lang="sl">Video datoteka NullSoft</comment>
+ <comment xml:lang="sq">Video NullSoft</comment>
+ <comment xml:lang="sr">Нул Софт видео</comment>
+ <comment xml:lang="sv">NullSoft-video</comment>
+ <comment xml:lang="tr">Nullsoft videosu</comment>
+ <comment xml:lang="uk">відеокліп NullSoft</comment>
+ <comment xml:lang="vi">Ảnh động NullSoft</comment>
+ <comment xml:lang="zh_CN">NullSoft 视频</comment>
+ <comment xml:lang="zh_TW">NullSoft 視訊</comment>
+ <magic priority="50">
+ <match value="NSVf" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.nsv"/>
+ </mime-type>
+ <mime-type type="application/sdp">
+ <comment>SDP multicast stream file</comment>
+ <comment xml:lang="ar">ملف دفق متعدد البث SDP</comment>
+ <comment xml:lang="be@latin">Šmatadrasny płynievy fajł SDP</comment>
+ <comment xml:lang="bg">Файл за поток — SDP multicast</comment>
+ <comment xml:lang="ca">fitxer de flux de multidifusió SDP</comment>
+ <comment xml:lang="cs">soubor vícesměrového vysílání proudu SDP</comment>
+ <comment xml:lang="da">SDP multicast-strømfil</comment>
+ <comment xml:lang="de">SDP-Multicast-Datenstromdatei</comment>
+ <comment xml:lang="el">Αρχείο ροής πολλαπλής αναμετάδοσης SDP</comment>
+ <comment xml:lang="en_GB">SDP multicast stream file</comment>
+ <comment xml:lang="es">archivo de flujo multicast SDP</comment>
+ <comment xml:lang="eu">SDP multicast korrontearen fitxategia</comment>
+ <comment xml:lang="fi">SDP-monilähetysvirran tiedosto</comment>
+ <comment xml:lang="fo">SDP margvarpað streymafíla</comment>
+ <comment xml:lang="fr">fichier de flux multidiffusion SDP</comment>
+ <comment xml:lang="ga">comhad shruth ilchraolacháin SDP</comment>
+ <comment xml:lang="gl">ficheiro de fluxo multicast SDP</comment>
+ <comment xml:lang="he">קובץ שידור בזרימה SDP</comment>
+ <comment xml:lang="hr">SDP datoteka strujanja emitiranja</comment>
+ <comment xml:lang="hu">SDP multicast műsorfájl</comment>
+ <comment xml:lang="ia">File de fluxo multidiffusion SDP</comment>
+ <comment xml:lang="id">Berkas SDP multicast stream</comment>
+ <comment xml:lang="it">File stream multicast SDP</comment>
+ <comment xml:lang="ja">SDP マルチキャストストリームファイル</comment>
+ <comment xml:lang="kk">SDP мультикаст ағым файлы</comment>
+ <comment xml:lang="ko">SDP 멀티캐스트 스트림 파일</comment>
+ <comment xml:lang="lt">SDP daugiaadresio srauto failas</comment>
+ <comment xml:lang="lv">SDP multiraides straumes datne</comment>
+ <comment xml:lang="nb">SDP-multicaststrøm</comment>
+ <comment xml:lang="nl">SDP-multicast-streambestand</comment>
+ <comment xml:lang="nn">SDP multicast straumfil</comment>
+ <comment xml:lang="oc">fichièr de flux multidifusion SDP</comment>
+ <comment xml:lang="pl">Plik strumienia multicast SDP</comment>
+ <comment xml:lang="pt">ficheiro de fluxo SDP multicast</comment>
+ <comment xml:lang="pt_BR">Arquivo de canal multicast SDP</comment>
+ <comment xml:lang="ro">Fișier flux multicast SDP</comment>
+ <comment xml:lang="ru">Файл мультикаст-потока SDP</comment>
+ <comment xml:lang="sk">Súbor viacsmerového vysielania prúdu SDP</comment>
+ <comment xml:lang="sl">Pretočni vir večsmernega oddajanja</comment>
+ <comment xml:lang="sq">File stream multicast SDP</comment>
+ <comment xml:lang="sr">СДП датотека тока вишеструког емитовања</comment>
+ <comment xml:lang="sv">SDP multicast stream-fil</comment>
+ <comment xml:lang="tr">SDP çoklu yayın akışı dosyası</comment>
+ <comment xml:lang="uk">файл потокової трансляції SDP</comment>
+ <comment xml:lang="vi">Tập tin luồng truyền một-nhiều SDP</comment>
+ <comment xml:lang="zh_CN">SDP 多播流文件</comment>
+ <comment xml:lang="zh_TW">SDP multicast 串流檔</comment>
+ <acronym>SDP</acronym>
+ <expanded-acronym>Session Description Protocol</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <alias type="application/x-sdp"/>
+ <alias type="application/vnd.sdp"/>
+ <generic-icon name="video-x-generic"/>
+ <magic priority="50">
+ <match value="v=" type="string" offset="0">
+ <match value="s=" type="string" offset="0:256"/>
+ </match>
+ </magic>
+ <glob pattern="*.sdp"/>
+ </mime-type>
+ <mime-type type="video/x-sgi-movie">
+ <comment>SGI video</comment>
+ <comment xml:lang="ar">SGI مرئي</comment>
+ <comment xml:lang="ast">Videu en SGI</comment>
+ <comment xml:lang="az">SGI video faylı</comment>
+ <comment xml:lang="be@latin">Videa SGI</comment>
+ <comment xml:lang="bg">Видео — SGI</comment>
+ <comment xml:lang="ca">vídeo SGI</comment>
+ <comment xml:lang="cs">video SGI</comment>
+ <comment xml:lang="cy">Video SGI</comment>
+ <comment xml:lang="da">SGI-video</comment>
+ <comment xml:lang="de">SGI-Video</comment>
+ <comment xml:lang="el">Βίντεο SGI</comment>
+ <comment xml:lang="en_GB">SGI video</comment>
+ <comment xml:lang="eo">SGI-video</comment>
+ <comment xml:lang="es">vídeo SGI</comment>
+ <comment xml:lang="eu">SGI bideoa</comment>
+ <comment xml:lang="fi">SGI-video</comment>
+ <comment xml:lang="fo">SGI video</comment>
+ <comment xml:lang="fr">vidéo SGI</comment>
+ <comment xml:lang="ga">físeán SGI</comment>
+ <comment xml:lang="gl">vídeo SGI</comment>
+ <comment xml:lang="he">וידאו SGI</comment>
+ <comment xml:lang="hr">SGI video snimka</comment>
+ <comment xml:lang="hu">SGI-videó</comment>
+ <comment xml:lang="ia">Video SGI</comment>
+ <comment xml:lang="id">Video SGI</comment>
+ <comment xml:lang="it">Video SGI</comment>
+ <comment xml:lang="ja">SGI 動画</comment>
+ <comment xml:lang="kk">SGI видеосы</comment>
+ <comment xml:lang="ko">SGI 동영상</comment>
+ <comment xml:lang="lt">SGI vaizdo įrašas</comment>
+ <comment xml:lang="lv">SGI video</comment>
+ <comment xml:lang="ms">Video SGI</comment>
+ <comment xml:lang="nb">SGI-film</comment>
+ <comment xml:lang="nl">SGI-video</comment>
+ <comment xml:lang="nn">SGI-video</comment>
+ <comment xml:lang="oc">vidèo SGI</comment>
+ <comment xml:lang="pl">Plik wideo SGI</comment>
+ <comment xml:lang="pt">vídeo SGI</comment>
+ <comment xml:lang="pt_BR">Vídeo SGI</comment>
+ <comment xml:lang="ro">Video SGI</comment>
+ <comment xml:lang="ru">Видео SGI</comment>
+ <comment xml:lang="sk">Video SGI</comment>
+ <comment xml:lang="sl">Video datoteka SGI</comment>
+ <comment xml:lang="sq">Video SGI</comment>
+ <comment xml:lang="sr">СГИ видео</comment>
+ <comment xml:lang="sv">SGI-video</comment>
+ <comment xml:lang="tr">SGI videosu</comment>
+ <comment xml:lang="uk">відеокліп SGI</comment>
+ <comment xml:lang="vi">Ảnh động SGI</comment>
+ <comment xml:lang="zh_CN">SGI 视频</comment>
+ <comment xml:lang="zh_TW">SGI 視訊</comment>
+ <magic priority="50">
+ <match value="MOVI" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.movie"/>
+ </mime-type>
+ <mime-type type="application/vnd.emusic-emusic_package">
+ <comment>eMusic download package</comment>
+ <comment xml:lang="ar">حزمة تنزيل eMusic</comment>
+ <comment xml:lang="be@latin">pakunak zahruzki eMusic</comment>
+ <comment xml:lang="bg">Пакет за сваляне — eMusic</comment>
+ <comment xml:lang="ca">paquet de descàrrega eMusic</comment>
+ <comment xml:lang="cs">balíček stahování eMusic</comment>
+ <comment xml:lang="da">eMusic-hentpakke</comment>
+ <comment xml:lang="de">eMusic-Download-Paket</comment>
+ <comment xml:lang="el">Πακέτο λήψης eMusic</comment>
+ <comment xml:lang="en_GB">eMusic download package</comment>
+ <comment xml:lang="es">paquete de descarga eMusic</comment>
+ <comment xml:lang="eu">eMusic deskargaren paketea</comment>
+ <comment xml:lang="fi">eMusic-imurointipaketti</comment>
+ <comment xml:lang="fo">eMusic niðurtøkupakki</comment>
+ <comment xml:lang="fr">paquet de téléchargement eMusic</comment>
+ <comment xml:lang="ga">pacáiste íosluchtú eMusic</comment>
+ <comment xml:lang="gl">paquete de descarga de eMusic</comment>
+ <comment xml:lang="he">חבילת הורדה של eMusic</comment>
+ <comment xml:lang="hr">eMusic preuzeti paket</comment>
+ <comment xml:lang="hu">eMusic letöltési csomag</comment>
+ <comment xml:lang="ia">Pacchetto de discargamento eMusic</comment>
+ <comment xml:lang="id">paket unduh eMusic</comment>
+ <comment xml:lang="it">Pacchetto scaricamento eMusic</comment>
+ <comment xml:lang="ja">eMusic ダウンロードパッケージ</comment>
+ <comment xml:lang="kk">eMusic жүктемелер дестесі</comment>
+ <comment xml:lang="ko">eMusic 다운로드 패키지</comment>
+ <comment xml:lang="lt">eMusic atsiuntimo paketas</comment>
+ <comment xml:lang="lv">eMusic lejupielādes paciņa</comment>
+ <comment xml:lang="nb">eMusic nedlastingspakke</comment>
+ <comment xml:lang="nl">eMusic-downloadpakket</comment>
+ <comment xml:lang="nn">eMusic nedlastingspakke</comment>
+ <comment xml:lang="oc">paquet de telecargament eMusic</comment>
+ <comment xml:lang="pl">Pobrany pakiet eMusic</comment>
+ <comment xml:lang="pt">pacote transferido eMusic</comment>
+ <comment xml:lang="pt_BR">Pacote de download do eMusic</comment>
+ <comment xml:lang="ro">pachet descărcare eMusic</comment>
+ <comment xml:lang="ru">Пакет загрузок eMusic</comment>
+ <comment xml:lang="sk">Balíček sťahovania eMusic</comment>
+ <comment xml:lang="sl">Datoteka paketa eMusic</comment>
+ <comment xml:lang="sq">Paketë shkarkimi eMusic</comment>
+ <comment xml:lang="sr">пакет преузимања еМузике</comment>
+ <comment xml:lang="sv">eMusic-hämtningspaket</comment>
+ <comment xml:lang="tr">eMusic indirme paketi</comment>
+ <comment xml:lang="uk">пакунок завантаження eMusic</comment>
+ <comment xml:lang="vi">gói nhạc tải xuống eMusic</comment>
+ <comment xml:lang="zh_CN">eMusic 下载包</comment>
+ <comment xml:lang="zh_TW">eMusic 下載包</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="nF7YLao" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.emp"/>
+ </mime-type>
+ <mime-type type="application/vnd.google-earth.kml+xml">
+ <comment>KML geographic data</comment>
+ <comment xml:lang="ar">بيانات جغرافية KML</comment>
+ <comment xml:lang="bg">Географски данни — KML</comment>
+ <comment xml:lang="ca">dades geogràfiques KML</comment>
+ <comment xml:lang="cs">geografická data KML</comment>
+ <comment xml:lang="da">Geografiske data i KML-format</comment>
+ <comment xml:lang="de">KML geographische Daten</comment>
+ <comment xml:lang="el">Γεωγραφικά δεδομένα KML</comment>
+ <comment xml:lang="en_GB">KML geographic data</comment>
+ <comment xml:lang="es">datos geográficos KML</comment>
+ <comment xml:lang="eu">KML datu geografikoak</comment>
+ <comment xml:lang="fi">KML-paikkatieto</comment>
+ <comment xml:lang="fo">KML landafrøðilig dáta</comment>
+ <comment xml:lang="fr">données géographiques KML</comment>
+ <comment xml:lang="ga">sonraí geografacha KML</comment>
+ <comment xml:lang="gl">datos xeográficos KML</comment>
+ <comment xml:lang="he">מידע גאוגרפי KML</comment>
+ <comment xml:lang="hr">KML geografski podaci</comment>
+ <comment xml:lang="hu">KML földrajzi adatok</comment>
+ <comment xml:lang="ia">Datos geographic KML</comment>
+ <comment xml:lang="id">Data geografis KML</comment>
+ <comment xml:lang="it">Dati geografici KML</comment>
+ <comment xml:lang="ja">KML 地理データ</comment>
+ <comment xml:lang="kk">KML географилық ақпараты</comment>
+ <comment xml:lang="ko">KML 지리 정보 데이터</comment>
+ <comment xml:lang="lt">KML geografiniai duomenys</comment>
+ <comment xml:lang="lv">KML ģeogrāfiskie dati</comment>
+ <comment xml:lang="nl">KML geographic data</comment>
+ <comment xml:lang="oc">donadas geograficas KML</comment>
+ <comment xml:lang="pl">Dane geograficzne KML</comment>
+ <comment xml:lang="pt">dados geográficos KML</comment>
+ <comment xml:lang="pt_BR">Dados geográficos KML</comment>
+ <comment xml:lang="ro">Date geografice KML</comment>
+ <comment xml:lang="ru">Географические данные KML</comment>
+ <comment xml:lang="sk">Zemepisné údaje KML</comment>
+ <comment xml:lang="sl">Datoteka geografskih podatkov KML</comment>
+ <comment xml:lang="sr">КМЛ географски подаци</comment>
+ <comment xml:lang="sv">KML geografisk data</comment>
+ <comment xml:lang="tr">KML coğrafi verisi</comment>
+ <comment xml:lang="uk">географічні дані KML</comment>
+ <comment xml:lang="zh_CN">KML 地理数据</comment>
+ <comment xml:lang="zh_TW">KML 地理資料</comment>
+ <acronym>KML</acronym>
+ <expanded-acronym>Keyhole Markup Language</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <glob pattern="*.kml"/>
+ <root-XML namespaceURI="http://www.opengis.net/kml/2.2" localName="kml"/>
+ </mime-type>
+ <mime-type type="application/vnd.google-earth.kmz">
+ <comment>KML geographic compressed data</comment>
+ <comment xml:lang="ar">بيانات جغرافية مضغوطة KML</comment>
+ <comment xml:lang="bg">Географски данни — KML, компресирани</comment>
+ <comment xml:lang="ca">dades geogràfiques KML amb compressió</comment>
+ <comment xml:lang="cs">komprimovaná geografická data KML</comment>
+ <comment xml:lang="da">KML-geografiske komprimerede data</comment>
+ <comment xml:lang="de">KML geographische komprimierte Daten</comment>
+ <comment xml:lang="el">Γεωγραφικά συμπιεσμένα δεδομένα KML</comment>
+ <comment xml:lang="en_GB">KML geographic compressed data</comment>
+ <comment xml:lang="es">datos geográficos comprimidos KML</comment>
+ <comment xml:lang="eu">KML datu geografiko konprimituak</comment>
+ <comment xml:lang="fi">Pakattu KML-paikkatieto</comment>
+ <comment xml:lang="fo">KML landafrøðilig stappað dáta</comment>
+ <comment xml:lang="fr">données géographiques KML compressées</comment>
+ <comment xml:lang="ga">sonraí comhbhrúite geografacha KML</comment>
+ <comment xml:lang="gl">datos xeográficos KML comprimidos </comment>
+ <comment xml:lang="he">מידע גאוגרפי דחוס KML</comment>
+ <comment xml:lang="hr">KML sažeti geografski podaci</comment>
+ <comment xml:lang="hu">KML tömörített földrajzi adatok</comment>
+ <comment xml:lang="ia">Datos geographic KML comprimite</comment>
+ <comment xml:lang="id">Data geografis KML terkompresi</comment>
+ <comment xml:lang="it">Dati geografici KML compressi</comment>
+ <comment xml:lang="ja">KML 地理圧縮データ</comment>
+ <comment xml:lang="kk">KML географиялық сығылған ақпарат</comment>
+ <comment xml:lang="ko">KML 지리 정보 압축 데이터</comment>
+ <comment xml:lang="lt">KML geografiniai suglaudinti duomenys</comment>
+ <comment xml:lang="lv">KML saspiesti ģeogrāfiskie dati</comment>
+ <comment xml:lang="nl">KML geographic compressed data</comment>
+ <comment xml:lang="oc">donadas geograficas KML compressats</comment>
+ <comment xml:lang="pl">Skompresowane dane geograficzne KML</comment>
+ <comment xml:lang="pt">dados geográficos comprimidos KML</comment>
+ <comment xml:lang="pt_BR">Dados geográficos KML compactados</comment>
+ <comment xml:lang="ro">Date geografice comprimate KML</comment>
+ <comment xml:lang="ru">Сжатые географические данные KML</comment>
+ <comment xml:lang="sk">Komprimované zemepisné údaje KML</comment>
+ <comment xml:lang="sl">Skrčeni geografski podatki KML</comment>
+ <comment xml:lang="sr">КМЛ географски запаковани подаци</comment>
+ <comment xml:lang="sv">KML geografiskt komprimerat data</comment>
+ <comment xml:lang="tr">KML sıkıştırılmış coğrafi verisi</comment>
+ <comment xml:lang="uk">стиснуті географічні дані KML</comment>
+ <comment xml:lang="zh_CN">KML 地理压缩数据</comment>
+ <comment xml:lang="zh_TW">KML 地理壓縮資料</comment>
+ <acronym>KML</acronym>
+ <expanded-acronym>Keyhole Markup Language</expanded-acronym>
+ <sub-class-of type="application/zip"/>
+ <glob pattern="*.kmz"/>
+ </mime-type>
+ <mime-type type="application/geo+json">
+ <comment>GeoJSON geospatial data</comment>
+ <comment xml:lang="ca">dades geomàtiques GeoJSON</comment>
+ <comment xml:lang="cs">geoprostorová data GeoJSON</comment>
+ <comment xml:lang="da">GEoJSON-geospatiale data</comment>
+ <comment xml:lang="de">GeoJSON raumbezogene Daten</comment>
+ <comment xml:lang="en_GB">GeoJSON geospatial data</comment>
+ <comment xml:lang="es">datos geoespaciales en GeoJSON</comment>
+ <comment xml:lang="fr">données géospatiales GeoJSON</comment>
+ <comment xml:lang="ga">sonraí geospásúla GeoJSON</comment>
+ <comment xml:lang="hr">GeoJSON geoprostorni podaci</comment>
+ <comment xml:lang="hu">GeoJSON téradatok</comment>
+ <comment xml:lang="id">Data geospasial GeoJSON</comment>
+ <comment xml:lang="it">Dati geo-spaziali GeoJSON</comment>
+ <comment xml:lang="kk">GeoJSON геокеңістіктік деректері</comment>
+ <comment xml:lang="ko">GeoJSON 지리 정보 데이터</comment>
+ <comment xml:lang="pl">Dane geoprzestrzenne GeoJSON</comment>
+ <comment xml:lang="pt_BR">Dados geoespaciais GeoJSON</comment>
+ <comment xml:lang="ru">Геопространственные данные GeoJSON</comment>
+ <comment xml:lang="sk">Geopriestorové údaje GeoJSON</comment>
+ <comment xml:lang="sr">ГеоЈСОН геопросторни подаци</comment>
+ <comment xml:lang="sv">Geospatialt GeoJSON-data</comment>
+ <comment xml:lang="tr">GeoJSON coğrafi veriler</comment>
+ <comment xml:lang="uk">геопросторові дані GeoJSON</comment>
+ <comment xml:lang="zh_CN">GeoJSON 地理空间数据</comment>
+ <comment xml:lang="zh_TW">GeoJSON 地理空間資料</comment>
+ <sub-class-of type="application/json"/>
+ <glob pattern="*.geojson"/>
+ <glob pattern="*.geo.json"/>
+ <alias type="application/vnd.geo+json"/>
+ </mime-type>
+ <mime-type type="application/gpx+xml">
+ <comment>GPX geographic data</comment>
+ <comment xml:lang="ca">dades geogràfiques GPX</comment>
+ <comment xml:lang="cs">geografická data GPX</comment>
+ <comment xml:lang="da">GPX-geografiske data</comment>
+ <comment xml:lang="de">GPX geographische Daten</comment>
+ <comment xml:lang="en_GB">GPX geographic data</comment>
+ <comment xml:lang="es">datos geográficos en GPX</comment>
+ <comment xml:lang="eu">GPX datu geografikoak</comment>
+ <comment xml:lang="fi">GPX-paikkatieto</comment>
+ <comment xml:lang="fr">données géographiques GPX</comment>
+ <comment xml:lang="ga">sonraí geografacha GPX</comment>
+ <comment xml:lang="he">נתונים גאוגרפיים GPX</comment>
+ <comment xml:lang="hr">GPX geografski podaci</comment>
+ <comment xml:lang="hu">GPX földrajzi adatok</comment>
+ <comment xml:lang="id">Data geografis GPX</comment>
+ <comment xml:lang="it">Dati geografici GPX</comment>
+ <comment xml:lang="kk">GPX географикалық деректері</comment>
+ <comment xml:lang="ko">GPX 지리 공간정보 데이터</comment>
+ <comment xml:lang="oc">Donadas geograficas GPX</comment>
+ <comment xml:lang="pl">Dane geograficzne GPX</comment>
+ <comment xml:lang="pt_BR">Dados geográficos GPX</comment>
+ <comment xml:lang="ru">Географические данные GPX</comment>
+ <comment xml:lang="sk">Zemepisné údaje GPX</comment>
+ <comment xml:lang="sr">ГПИкс географски подаци</comment>
+ <comment xml:lang="sv">GPX geografisk data</comment>
+ <comment xml:lang="tr">GPX coğrafi verileri</comment>
+ <comment xml:lang="uk">географічні дані GPX</comment>
+ <comment xml:lang="zh_CN">GPX 地理数据</comment>
+ <comment xml:lang="zh_TW">GPX 地理資料</comment>
+ <acronym>GPX</acronym>
+ <expanded-acronym>GPS Exchange Format</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <alias type="application/gpx"/>
+ <alias type="application/x-gpx+xml"/>
+ <alias type="application/x-gpx"/>
+ <glob pattern="*.gpx"/>
+ <root-XML namespaceURI="http://www.topografix.com/GPX/1/0" localName="gpx"/>
+ <root-XML namespaceURI="http://www.topografix.com/GPX/1/1" localName="gpx"/>
+ </mime-type>
+ <mime-type type="application/x-ica">
+ <comment>Citrix ICA settings file</comment>
+ <comment xml:lang="ar">ملف إعدادات Citrix ICA</comment>
+ <comment xml:lang="be@latin">Fajł naładaŭ Citrix ICA</comment>
+ <comment xml:lang="bg">Настройки — Citrix ICA</comment>
+ <comment xml:lang="ca">fitxer d'ajusts de Citrix ICA</comment>
+ <comment xml:lang="cs">soubor nastavení Citrix ICA</comment>
+ <comment xml:lang="da">Citrix ICA-opsætningsfil</comment>
+ <comment xml:lang="de">Citrix-ICA-Einstellungsdatei</comment>
+ <comment xml:lang="el">Αρχείο ρυθμίσεων Citrix ICA</comment>
+ <comment xml:lang="en_GB">Citrix ICA settings file</comment>
+ <comment xml:lang="es">archivo de configuración de Citrix ICA</comment>
+ <comment xml:lang="eu">Citrix ICA ezarpenen fitxategia</comment>
+ <comment xml:lang="fi">Citrix ICA -asetustiedosto</comment>
+ <comment xml:lang="fo">Citrix ICA stillingarfíla</comment>
+ <comment xml:lang="fr">fichier de paramètres ICA Citrix</comment>
+ <comment xml:lang="ga">comhad socruithe Citrix ICA</comment>
+ <comment xml:lang="gl">ficheiro de configuracións de Citrix ICA</comment>
+ <comment xml:lang="he">קובץ הגדרות של Citrix ICA</comment>
+ <comment xml:lang="hr">Citrix ICA datoteka postavki</comment>
+ <comment xml:lang="hu">Citrix ICA beállításfájl</comment>
+ <comment xml:lang="ia">File de configuration ICA Citrix</comment>
+ <comment xml:lang="id">Berkas penataan Citrix ICA</comment>
+ <comment xml:lang="it">File impostazioni Citrix ICA</comment>
+ <comment xml:lang="ja">Citrix ICA 設定ファイル</comment>
+ <comment xml:lang="ka">Citrix ICA-ის პარამეტრების ფაილი</comment>
+ <comment xml:lang="kk">Citrix ICA баптаулар файлы</comment>
+ <comment xml:lang="ko">시트릭스 ICA 설정 파일</comment>
+ <comment xml:lang="lt">Citrix ICA parametrų failas</comment>
+ <comment xml:lang="lv">Citrix ICA iestatījumu datne</comment>
+ <comment xml:lang="nb">Innstillingsfil for Citrix ICA</comment>
+ <comment xml:lang="nl">Citrix ICA-instellingen</comment>
+ <comment xml:lang="nn">Citrix ICA-innstillingsfil</comment>
+ <comment xml:lang="oc">fichièr de paramètres ICA Citrix</comment>
+ <comment xml:lang="pl">Plik ustawień Citrix ICA</comment>
+ <comment xml:lang="pt">ficheiro de definições Citrix ICA</comment>
+ <comment xml:lang="pt_BR">Arquivo de configuração do Citrix ICA</comment>
+ <comment xml:lang="ro">Fișier de configurări Citrix ICA</comment>
+ <comment xml:lang="ru">Файл настроек Citrix ICA</comment>
+ <comment xml:lang="sk">Súbor nastavení Citrix ICA</comment>
+ <comment xml:lang="sl">Nastavitvena datoteka Citrix ICA</comment>
+ <comment xml:lang="sq">File rregullimesh Citrix ICA</comment>
+ <comment xml:lang="sr">датотека подешавања Цитрикс ИЦА-а</comment>
+ <comment xml:lang="sv">Citrix ICA-inställningsfil</comment>
+ <comment xml:lang="tr">Citrix ICA ayar dosyası</comment>
+ <comment xml:lang="uk">файл параметрів ICA Citrix</comment>
+ <comment xml:lang="vi">Tập tin thiết lập ICA Citrix</comment>
+ <comment xml:lang="zh_CN">Citrix ICA 设置文件</comment>
+ <comment xml:lang="zh_TW">Citrix ICA 設定值檔案</comment>
+ <acronym>ICA</acronym>
+ <expanded-acronym>Independent Computing Architecture</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.ica"/>
+ </mime-type>
+ <mime-type type="application/vnd.mozilla.xul+xml">
+ <comment>XUL interface document</comment>
+ <comment xml:lang="ar">مستند واجهة XUL</comment>
+ <comment xml:lang="ast">Documentu d'interfaz XUL</comment>
+ <comment xml:lang="be@latin">Interfejsny dakument XUL</comment>
+ <comment xml:lang="bg">Документ — интерфейс за XUL</comment>
+ <comment xml:lang="ca">document d'interfície XUL</comment>
+ <comment xml:lang="cs">dokument rozhraní XUL</comment>
+ <comment xml:lang="da">XUL-grænsefladedokument</comment>
+ <comment xml:lang="de">XUL-Oberflächendokument</comment>
+ <comment xml:lang="el">Έγγραφο διεπαφής XUL</comment>
+ <comment xml:lang="en_GB">XUL interface document</comment>
+ <comment xml:lang="es">documento de interfaz XUL</comment>
+ <comment xml:lang="eu">XUL interfazearen dokumentua</comment>
+ <comment xml:lang="fi">XUL-käyttöliittymäasiakirja</comment>
+ <comment xml:lang="fo">XUL markamótsskjal</comment>
+ <comment xml:lang="fr">document d'interface XUL</comment>
+ <comment xml:lang="ga">cáipéis chomhéadan XUL</comment>
+ <comment xml:lang="gl">documento de interface XUL</comment>
+ <comment xml:lang="he">מסמך ממשק XUL</comment>
+ <comment xml:lang="hr">XUL dokument sučelja</comment>
+ <comment xml:lang="hu">XUL-felületdokumentum</comment>
+ <comment xml:lang="ia">Documento de interfacie XUL</comment>
+ <comment xml:lang="id">Dokumen antarmuka XUL</comment>
+ <comment xml:lang="it">Documento interfaccia XUL</comment>
+ <comment xml:lang="ja">XUL インターフェイスドキュメント</comment>
+ <comment xml:lang="kk">XUL интерфейс құжаты</comment>
+ <comment xml:lang="ko">XUL 인터페이스 문서</comment>
+ <comment xml:lang="lt">XUL sąsajos dokumentas</comment>
+ <comment xml:lang="lv">XUL saskarnes dokuments</comment>
+ <comment xml:lang="nb">XUL-grensesnittdokument</comment>
+ <comment xml:lang="nl">XUL-interface-document</comment>
+ <comment xml:lang="nn">XUL-grensesnitt-dokument</comment>
+ <comment xml:lang="oc">document d'interfàcia XUL</comment>
+ <comment xml:lang="pl">Dokument interfejsu XUL</comment>
+ <comment xml:lang="pt">documento de ambiente XUL</comment>
+ <comment xml:lang="pt_BR">Documento de interface XUL</comment>
+ <comment xml:lang="ro">Document interfață XUL</comment>
+ <comment xml:lang="ru">Документ интерфейса XUL</comment>
+ <comment xml:lang="sk">Dokument rozhrania XUL</comment>
+ <comment xml:lang="sl">Dokument vmesnika XUL</comment>
+ <comment xml:lang="sq">Dokument interfaqe XUL</comment>
+ <comment xml:lang="sr">документ ИксУЛ сучеља</comment>
+ <comment xml:lang="sv">XUL-gränssnittsdokument</comment>
+ <comment xml:lang="tr">XUL arayüz belgesi</comment>
+ <comment xml:lang="uk">документ інтерфейсу XUL</comment>
+ <comment xml:lang="vi">Tài liệu giao diện XUL</comment>
+ <comment xml:lang="zh_CN">XUL 界面文档</comment>
+ <comment xml:lang="zh_TW">XUL 介面文件</comment>
+ <acronym>XUL</acronym>
+ <expanded-acronym>XML User interface markup Language</expanded-acronym>
+ <sub-class-of type="application/xml"/>
+ <generic-icon name="x-office-document"/>
+ <root-XML namespaceURI="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" localName="window"/>
+ <glob pattern="*.xul"/>
+ </mime-type>
+ <mime-type type="application/x-xpinstall">
+ <comment>XPInstall installer module</comment>
+ <comment xml:lang="ar">وحدة مثبت XPInstall </comment>
+ <comment xml:lang="bg">Пакет — инсталация XPInstall</comment>
+ <comment xml:lang="ca">mòdul de l'instal·lador XPinstall</comment>
+ <comment xml:lang="cs">modul instalátoru XPInstall</comment>
+ <comment xml:lang="da">XPInstall-installationsmodul</comment>
+ <comment xml:lang="de">XPInstall-Installationsmodul</comment>
+ <comment xml:lang="el">Άρθρωμα εγκατάστασης XPInstall</comment>
+ <comment xml:lang="en_GB">XPInstall installer module</comment>
+ <comment xml:lang="es">módulo del instalador XPInstall</comment>
+ <comment xml:lang="eu">XPInstall instalatzailearen modulua</comment>
+ <comment xml:lang="fi">XPInstall-asennuspaketti</comment>
+ <comment xml:lang="fo">XPInstall innleggjaramótul</comment>
+ <comment xml:lang="fr">module d'installation XPInstall</comment>
+ <comment xml:lang="ga">modúl suiteála XPInstall</comment>
+ <comment xml:lang="gl">Módulo do instalador XPInstall</comment>
+ <comment xml:lang="he">מודול התקנה של XPInstall</comment>
+ <comment xml:lang="hr">XPInstall instalacijski modul</comment>
+ <comment xml:lang="hu">XPInstall telepítőmodul</comment>
+ <comment xml:lang="ia">Modulo de installation XPInstall</comment>
+ <comment xml:lang="id">Modul installer XPInstall</comment>
+ <comment xml:lang="it">Modulo installatore XPInstall</comment>
+ <comment xml:lang="ja">XPInstall インストーラモジュール</comment>
+ <comment xml:lang="kk">XPInstall орнату модулі</comment>
+ <comment xml:lang="ko">XPInstall 설치 프로그램 모듈</comment>
+ <comment xml:lang="lt">XPInstall įdiegiklio modulis</comment>
+ <comment xml:lang="lv">XPInstall instalatora modulis</comment>
+ <comment xml:lang="nl">XPInstall installeer module</comment>
+ <comment xml:lang="oc">modul d'installacion XPInstall</comment>
+ <comment xml:lang="pl">Moduł instalatora XPInstall</comment>
+ <comment xml:lang="pt">módulo de instalador XPInstall</comment>
+ <comment xml:lang="pt_BR">Módulo de instalador XPInstall</comment>
+ <comment xml:lang="ro">Modul de instalare XPInstall</comment>
+ <comment xml:lang="ru">Модуль установщика XPInstall</comment>
+ <comment xml:lang="sk">Modul inštalátora XPInstall</comment>
+ <comment xml:lang="sl">modul namestilnika XPInstall</comment>
+ <comment xml:lang="sr">модул инсталатера Инсталирања ИксПе-а</comment>
+ <comment xml:lang="sv">XPInstall-installeringsmodul</comment>
+ <comment xml:lang="tr">XPInstall kurulum modülü</comment>
+ <comment xml:lang="uk">модуль засобу встановлення XPInstall</comment>
+ <comment xml:lang="zh_CN">XPInstall 安装工具模块</comment>
+ <comment xml:lang="zh_TW">XPInstall 安裝程式模組</comment>
+ <sub-class-of type="application/zip"/>
+ <glob pattern="*.xpi"/>
+ </mime-type>
+ <mime-type type="application/vnd.openxmlformats-officedocument.wordprocessingml.document">
+ <comment>Word 2007 document</comment>
+ <comment xml:lang="ar">مستند Word 2007</comment>
+ <comment xml:lang="ast">Documentu de Word 2007</comment>
+ <comment xml:lang="bg">Документ — Word 2007</comment>
+ <comment xml:lang="ca">document de Word 2007</comment>
+ <comment xml:lang="cs">dokument Word 2007</comment>
+ <comment xml:lang="da">Word 2007-dokument</comment>
+ <comment xml:lang="de">Word-2007-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο Word 2007</comment>
+ <comment xml:lang="en_GB">Word 2007 document</comment>
+ <comment xml:lang="es">documento de Word 2007</comment>
+ <comment xml:lang="eu">Word 2007 dokumentua</comment>
+ <comment xml:lang="fi">Word 2007 -asiakirja</comment>
+ <comment xml:lang="fo">Word 2007 skjal</comment>
+ <comment xml:lang="fr">document Word 2007</comment>
+ <comment xml:lang="ga">cáipéis Word 2007</comment>
+ <comment xml:lang="gl">documento de Word 2007</comment>
+ <comment xml:lang="he">מסמך Word 2007</comment>
+ <comment xml:lang="hr">Word 2007 dokument</comment>
+ <comment xml:lang="hu">Word 2007 dokumentum</comment>
+ <comment xml:lang="ia">Documento Word 2007</comment>
+ <comment xml:lang="id">Dokumen Word 2007</comment>
+ <comment xml:lang="it">Documento Word 2007</comment>
+ <comment xml:lang="ja">Word 2007 ドキュメント</comment>
+ <comment xml:lang="kk">Word 2007 құжаты</comment>
+ <comment xml:lang="ko">Word 2007 문서</comment>
+ <comment xml:lang="lt">Word 2007 dokumentas</comment>
+ <comment xml:lang="lv">Word 2007 dokuments</comment>
+ <comment xml:lang="nl">Word 2007-document</comment>
+ <comment xml:lang="oc">document Word 2007</comment>
+ <comment xml:lang="pl">Dokument Word 2007</comment>
+ <comment xml:lang="pt">documento Word 2007</comment>
+ <comment xml:lang="pt_BR">Documento do Word 2007</comment>
+ <comment xml:lang="ro">Document Word 2007</comment>
+ <comment xml:lang="ru">Документ Word 2007</comment>
+ <comment xml:lang="sk">Dokument Word 2007</comment>
+ <comment xml:lang="sl">Dokument Word 2007</comment>
+ <comment xml:lang="sr">документ Ворда 2007</comment>
+ <comment xml:lang="sv">Word 2007-dokument</comment>
+ <comment xml:lang="tr">Word 2007 belgesi</comment>
+ <comment xml:lang="uk">документ Word 2007</comment>
+ <comment xml:lang="vi">Tài liệu Word 2007</comment>
+ <comment xml:lang="zh_CN">Word 2007 文档</comment>
+ <comment xml:lang="zh_TW">Word 2007 文件</comment>
+ <glob pattern="*.docx"/>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ </mime-type>
+ <mime-type type="application/vnd.openxmlformats-officedocument.wordprocessingml.template">
+ <comment>Word 2007 document template</comment>
+ <comment xml:lang="ast">Plantía de documentu de Word 2007</comment>
+ <comment xml:lang="bg">Шаблон за документи — Word 2007</comment>
+ <comment xml:lang="ca">plantilla de document de Word 2007</comment>
+ <comment xml:lang="cs">šablona dokumentu Word 2007</comment>
+ <comment xml:lang="da">Word 2007-dokumentskabelon</comment>
+ <comment xml:lang="de">Word-2007-Dokumentvorlage</comment>
+ <comment xml:lang="el">Πρότυπο έγγραφο Word 2007</comment>
+ <comment xml:lang="en_GB">Word 2007 document template</comment>
+ <comment xml:lang="es">plantilla de documento de Word 2007</comment>
+ <comment xml:lang="eu">Word 2007 dokumentuaren txantiloia</comment>
+ <comment xml:lang="fi">Word 2007 -asiakirjamalli</comment>
+ <comment xml:lang="fr">modèle de document Word 2007</comment>
+ <comment xml:lang="ga">teimpléad cháipéis Word 2007</comment>
+ <comment xml:lang="gl">Plantilla de documento de Word 2007</comment>
+ <comment xml:lang="he">תבנית מסמך של Word 2007</comment>
+ <comment xml:lang="hr">Word 2007 predložak dokumenta</comment>
+ <comment xml:lang="hu">Word 2007 dokumentumsablon</comment>
+ <comment xml:lang="ia">Patrono de documento Word 2007</comment>
+ <comment xml:lang="id">Templat dokumen Word 2007</comment>
+ <comment xml:lang="it">Modello documento Word 2007</comment>
+ <comment xml:lang="ja">Word 2007 文書テンプレート</comment>
+ <comment xml:lang="ka">Word 2007-ის დოკუმენტის შაბლონი</comment>
+ <comment xml:lang="kk">Word 2007 құжатының үлгісі</comment>
+ <comment xml:lang="ko">Word 2007 문서 서식</comment>
+ <comment xml:lang="lv">Word 2007 dokumenta veidne</comment>
+ <comment xml:lang="nl">Word 2007 document sjabloon</comment>
+ <comment xml:lang="oc">modèl de document Word 2007</comment>
+ <comment xml:lang="pl">Szablon dokumentu Word 2007</comment>
+ <comment xml:lang="pt">modelo de documento Word 2007</comment>
+ <comment xml:lang="pt_BR">Modelo de documento do Word 2007</comment>
+ <comment xml:lang="ru">Шаблон документа Word 2007</comment>
+ <comment xml:lang="sk">Šablóna dokumentu Word 2007</comment>
+ <comment xml:lang="sl">Predloga dokumenta Word 2007</comment>
+ <comment xml:lang="sr">шаблон документа Ворда 2007</comment>
+ <comment xml:lang="sv">Word 2007-dokumentmall</comment>
+ <comment xml:lang="tr">Word 2007 belge şablonu</comment>
+ <comment xml:lang="uk">шаблон документа Word 2007</comment>
+ <comment xml:lang="zh_CN">Word 2007 文档模板</comment>
+ <comment xml:lang="zh_TW">Word 2007 文件範本</comment>
+ <glob pattern="*.dotx"/>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-document"/>
+ </mime-type>
+ <mime-type type="application/vnd.openxmlformats-officedocument.presentationml.presentation">
+ <comment>PowerPoint 2007 presentation</comment>
+ <comment xml:lang="ar">عرض تقديمي PowerPoint 2007</comment>
+ <comment xml:lang="bg">Презентация — PowerPoint 2007</comment>
+ <comment xml:lang="ca">presentació de PowerPoint 2007</comment>
+ <comment xml:lang="cs">prezentace PowerPoint 2007</comment>
+ <comment xml:lang="da">PowerPoint 2007-præsentation</comment>
+ <comment xml:lang="de">PowerPoint-2007-Präsentation</comment>
+ <comment xml:lang="el">Παρουσίαση PowerPoint 2007</comment>
+ <comment xml:lang="en_GB">PowerPoint 2007 presentation</comment>
+ <comment xml:lang="es">presentación de PowerPoint 2007</comment>
+ <comment xml:lang="eu">PowerPoint 2007 aurkezpena</comment>
+ <comment xml:lang="fi">PowerPoint 2007 -esitys</comment>
+ <comment xml:lang="fo">PowerPoint 2007 framløga</comment>
+ <comment xml:lang="fr">présentation PowerPoint 2007</comment>
+ <comment xml:lang="ga">láithreoireacht PowerPoint 2007</comment>
+ <comment xml:lang="gl">presentación de PowerPoint 2007</comment>
+ <comment xml:lang="he">מצגת של PowerPoint 2007</comment>
+ <comment xml:lang="hr">PowerPoint 2007 prezentacija</comment>
+ <comment xml:lang="hu">PowerPoint 2007 prezentáció</comment>
+ <comment xml:lang="ia">Presentation PowerPoint 2007</comment>
+ <comment xml:lang="id">Presentasi PowerPoint 2007</comment>
+ <comment xml:lang="it">Presentazione standard PowerPoint 2007</comment>
+ <comment xml:lang="ja">PowerPoint 2007 プレゼンテーション</comment>
+ <comment xml:lang="kk">PowerPoint 2007 презентациясы</comment>
+ <comment xml:lang="ko">PowerPoint 2007 프레젠테이션</comment>
+ <comment xml:lang="lt">PowerPoint 2007 pateiktis</comment>
+ <comment xml:lang="lv">PowerPoint 2007 prezentācija</comment>
+ <comment xml:lang="nl">PowerPoint 2007-presentatie</comment>
+ <comment xml:lang="oc">presentacion PowerPoint 2007</comment>
+ <comment xml:lang="pl">Prezentacja PowerPoint 2007</comment>
+ <comment xml:lang="pt">apresentação PowerPoint 2007</comment>
+ <comment xml:lang="pt_BR">Apresentação do PowerPoint 2007</comment>
+ <comment xml:lang="ro">Prezentare PowerPoint 2007</comment>
+ <comment xml:lang="ru">Презентация PowerPoint 2007</comment>
+ <comment xml:lang="sk">Prezentácia PowerPoint 2007</comment>
+ <comment xml:lang="sl">Predstavitev Microsoft PowerPoint 2007</comment>
+ <comment xml:lang="sr">презентација Пауер Поинта 2007</comment>
+ <comment xml:lang="sv">PowerPoint 2007-presentation</comment>
+ <comment xml:lang="tr">PowerPoint 2007 sunumu</comment>
+ <comment xml:lang="uk">презентація PowerPoint 2007</comment>
+ <comment xml:lang="vi">Trình diễn PowerPoint 2007</comment>
+ <comment xml:lang="zh_CN">PowerPoint 2007 演示文稿</comment>
+ <comment xml:lang="zh_TW">PowerPoint 2007 簡報</comment>
+ <glob pattern="*.pptx"/>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-presentation"/>
+ </mime-type>
+ <mime-type type="application/vnd.openxmlformats-officedocument.presentationml.slide">
+ <comment>PowerPoint 2007 slide</comment>
+ <comment xml:lang="bg">Кадър — PoerPoint 2007</comment>
+ <comment xml:lang="ca">dispositiva de PowerPoint 2007</comment>
+ <comment xml:lang="cs">snímek PowerPoint 2007</comment>
+ <comment xml:lang="da">PowerPoint 2007-slide</comment>
+ <comment xml:lang="de">PowerPoint 2007-Folie</comment>
+ <comment xml:lang="el">Διαφάνεια PowerPoint 2007</comment>
+ <comment xml:lang="en_GB">PowerPoint 2007 slide</comment>
+ <comment xml:lang="es">diapositiva de PowerPoint 2007</comment>
+ <comment xml:lang="eu">PowerPoint 2007 diapositiba</comment>
+ <comment xml:lang="fi">PowerPoint 2007 -dia</comment>
+ <comment xml:lang="fr">diapositive PowerPoint 2007</comment>
+ <comment xml:lang="ga">sleamhnán PowerPoint 2007</comment>
+ <comment xml:lang="gl">Diaporama de PowerPoint 2007</comment>
+ <comment xml:lang="he">שקופית של PowerPoint 2007</comment>
+ <comment xml:lang="hr">PowerPoint 2007 slikovna prezentacija</comment>
+ <comment xml:lang="hu">PowerPoint 2007 dia</comment>
+ <comment xml:lang="ia">Diapositiva PowerPoint 2007</comment>
+ <comment xml:lang="id">Slide PowerPoint 2007</comment>
+ <comment xml:lang="it">Diapositiva PowerPoint 2007</comment>
+ <comment xml:lang="ja">PowerPoint 2007 スライド</comment>
+ <comment xml:lang="ka">PowerPoint 2007-ის სლაიდი</comment>
+ <comment xml:lang="kk">PowerPoint 2007 слайды</comment>
+ <comment xml:lang="ko">PowerPoint 2007 슬라이드</comment>
+ <comment xml:lang="lv">PowerPoint 2007 slaids</comment>
+ <comment xml:lang="nl">PowerPoint 2007 dia</comment>
+ <comment xml:lang="oc">diapositive PowerPoint 2007</comment>
+ <comment xml:lang="pl">Slajd PowerPoint 2007</comment>
+ <comment xml:lang="pt">diapositivo PowerPoint 2007</comment>
+ <comment xml:lang="pt_BR">Slide do PowerPoint 2007</comment>
+ <comment xml:lang="ru">Слайд PowerPoint 2007</comment>
+ <comment xml:lang="sk">Snímka PowerPoint 2007</comment>
+ <comment xml:lang="sl">Prosojnica PowerPoint 2007</comment>
+ <comment xml:lang="sr">слајд Пауер Поинта 2007</comment>
+ <comment xml:lang="sv">PowerPoint 2007-bildspel</comment>
+ <comment xml:lang="tr">PowerPoint 2007 slaytı</comment>
+ <comment xml:lang="uk">слайд PowerPoint 2007</comment>
+ <comment xml:lang="zh_CN">PowerPoint 2007 幻灯片</comment>
+ <comment xml:lang="zh_TW">PowerPoint 2007 投影片</comment>
+ <glob pattern="*.sldx"/>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-presentation"/>
+ </mime-type>
+ <mime-type type="application/vnd.openxmlformats-officedocument.presentationml.slideshow">
+ <comment>PowerPoint 2007 show</comment>
+ <comment xml:lang="ar">عرض PowerPoint 2007</comment>
+ <comment xml:lang="bg">Презентация-шоу — PowerPoint 2007</comment>
+ <comment xml:lang="ca">exposició de PowerPoint 2007</comment>
+ <comment xml:lang="cs">prezentace PowerPoint 2007</comment>
+ <comment xml:lang="da">PowerPoint 2007-dias</comment>
+ <comment xml:lang="de">PowerPoint-2007-Präsentation</comment>
+ <comment xml:lang="el">Παρουσίαση PowerPoint 2007</comment>
+ <comment xml:lang="en_GB">PowerPoint 2007 show</comment>
+ <comment xml:lang="es">presentación autoejecutable de PowerPoint 2007</comment>
+ <comment xml:lang="eu">PowerPoint 2007 ikuskizuna</comment>
+ <comment xml:lang="fi">PowerPoint 2007 -diaesitys</comment>
+ <comment xml:lang="fo">PowerPoint 2007 framsýning</comment>
+ <comment xml:lang="fr">diaporama PowerPoint 2007</comment>
+ <comment xml:lang="ga">taispeántas PowerPoint 2007</comment>
+ <comment xml:lang="gl">Exposición de PowerPoint 2007</comment>
+ <comment xml:lang="he">תצוגה של PowerPoint 2007</comment>
+ <comment xml:lang="hr">PowerPoint 2007 prezentacija</comment>
+ <comment xml:lang="hu">PowerPoint 2007 bemutató</comment>
+ <comment xml:lang="ia">Projection de diapositivas PowerPoint 2007</comment>
+ <comment xml:lang="id">Presentasi PowerPoint 2007</comment>
+ <comment xml:lang="it">Solo presentazione PowerPoint 2007</comment>
+ <comment xml:lang="ja">PowerPoint 2007 プレゼンテーション</comment>
+ <comment xml:lang="kk">PowerPoint 2007 көрсетілімі</comment>
+ <comment xml:lang="ko">PowerPoint 2007 쇼</comment>
+ <comment xml:lang="lt">PowerPoint 2007 pateiktis</comment>
+ <comment xml:lang="lv">PowerPoint 2007 slīdrāde</comment>
+ <comment xml:lang="nl">PowerPoint 2007 show</comment>
+ <comment xml:lang="oc">diaporama PowerPoint 2007</comment>
+ <comment xml:lang="pl">Pokaz PowerPoint 2007</comment>
+ <comment xml:lang="pt">espetáculo PowerPoint 2007</comment>
+ <comment xml:lang="pt_BR">Apresentação do PowerPoint 2007</comment>
+ <comment xml:lang="ro">Prezentare PowerPoint 2007</comment>
+ <comment xml:lang="ru">Презентация PowerPoint 2007</comment>
+ <comment xml:lang="sk">Ukážka PowerPoint 2007</comment>
+ <comment xml:lang="sl">Zagonska predstavitev PowerPoint 2007</comment>
+ <comment xml:lang="sr">приказ Пауер Поинта 2007</comment>
+ <comment xml:lang="sv">PowerPoint 2007-visning</comment>
+ <comment xml:lang="tr">PowerPoint 2007 gösterisi</comment>
+ <comment xml:lang="uk">показ слайдів PowerPoint 2007</comment>
+ <comment xml:lang="zh_CN">PowerPoint 2007 放映</comment>
+ <comment xml:lang="zh_TW">PowerPoint 2007 展示</comment>
+ <glob pattern="*.ppsx"/>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-presentation"/>
+ </mime-type>
+ <mime-type type="application/vnd.openxmlformats-officedocument.presentationml.template">
+ <comment>PowerPoint 2007 presentation template</comment>
+ <comment xml:lang="bg">Шаблон за презентации — PowerPoint 2007</comment>
+ <comment xml:lang="ca">plantilla de presentació de PowerPoint 2007</comment>
+ <comment xml:lang="cs">šablona prezentace PowerPoint 2007</comment>
+ <comment xml:lang="da">PowerPoint 2007-præsentationsskabelon</comment>
+ <comment xml:lang="de">PowerPoint 2007-Präsentationsvorlage</comment>
+ <comment xml:lang="el">Πρότυπο παρουσίασης PowerPoint 2007</comment>
+ <comment xml:lang="en_GB">PowerPoint 2007 presentation template</comment>
+ <comment xml:lang="es">plantilla de presentación de PowerPoint 2007</comment>
+ <comment xml:lang="eu">PowerPoint 2007 aurkezpen txantiloia</comment>
+ <comment xml:lang="fi">PowerPoint 2007 -esitysmalli</comment>
+ <comment xml:lang="fr">modèle de présentation PowerPoint 2007</comment>
+ <comment xml:lang="ga">teimpléad láithreoireachta PowerPoint 2007</comment>
+ <comment xml:lang="gl">modelo de presentación de PowerPoint 2007</comment>
+ <comment xml:lang="he">תבנית למצגת של PowerPoint 2007</comment>
+ <comment xml:lang="hr">PowerPoint 2007 predložak prezentacije</comment>
+ <comment xml:lang="hu">PowerPoint 2007 bemutatósablon</comment>
+ <comment xml:lang="ia">Patrono de presentation PowerPoint 2007</comment>
+ <comment xml:lang="id">Templat presentasi PowerPoint 2007</comment>
+ <comment xml:lang="it">Modello presentazione PowerPoint 2007</comment>
+ <comment xml:lang="ja">PowerPoint 2007 プレゼンテーションテンプレート</comment>
+ <comment xml:lang="ka">PowerPoint 2007-ის პრეზენტაციის შაბლონი</comment>
+ <comment xml:lang="kk">PowerPoint 2007 презентация шаблоны</comment>
+ <comment xml:lang="ko">PowerPoint 2007 프레젠테이션 서식</comment>
+ <comment xml:lang="lv">PowerPoint 2007 prezentācijas veidne</comment>
+ <comment xml:lang="nl">PowerPoint 2007 presentation sjabloon</comment>
+ <comment xml:lang="oc">modèl de presentacion PowerPoint 2007</comment>
+ <comment xml:lang="pl">Szablon prezentacji PowerPoint 2007</comment>
+ <comment xml:lang="pt">modelo de apresentação PowerPoint 2007</comment>
+ <comment xml:lang="pt_BR">Modelo de apresentação do PowerPoint 2007</comment>
+ <comment xml:lang="ru">Шаблон презентации PowerPoint 2007</comment>
+ <comment xml:lang="sk">Šablóna prezentácie PowerPoint 2007</comment>
+ <comment xml:lang="sl">Predloga predstavitve PowerPoint 2007</comment>
+ <comment xml:lang="sr">шаблон презентације Пауер Поинта 2007</comment>
+ <comment xml:lang="sv">PowerPoint 2007-presentationsmall</comment>
+ <comment xml:lang="tr">PowerPoint 2007 sunum şablonu</comment>
+ <comment xml:lang="uk">шаблон презентації PowerPoint 2007</comment>
+ <comment xml:lang="zh_CN">PowerPoint 2007 演示文稿模板</comment>
+ <comment xml:lang="zh_TW">PowerPoint 2007 簡報範本</comment>
+ <glob pattern="*.potx"/>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-presentation"/>
+ </mime-type>
+ <mime-type type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
+ <comment>Excel 2007 spreadsheet</comment>
+ <comment xml:lang="ar">جدول Excel 2007</comment>
+ <comment xml:lang="bg">Таблица — Excel 2007</comment>
+ <comment xml:lang="ca">full de càlcul d'Excel 2007</comment>
+ <comment xml:lang="cs">sešit Excel 2007</comment>
+ <comment xml:lang="da">Excel 2007-regneark</comment>
+ <comment xml:lang="de">Excel-2007-Tabelle</comment>
+ <comment xml:lang="el">Λογιστικό φύλλο Excel 2007</comment>
+ <comment xml:lang="en_GB">Excel 2007 spreadsheet</comment>
+ <comment xml:lang="es">hoja de cálculo de Excel 2007</comment>
+ <comment xml:lang="eu">Excel 2007 kalkulu-orria</comment>
+ <comment xml:lang="fi">Excel 2007 -taulukko</comment>
+ <comment xml:lang="fo">Excel 2007 rokniark</comment>
+ <comment xml:lang="fr">feuille de calcul Excel 2007</comment>
+ <comment xml:lang="ga">scarbhileog Excel 2007</comment>
+ <comment xml:lang="gl">folla de cálculo de Excel 2007</comment>
+ <comment xml:lang="he">גליון נתונים של Excel 2007</comment>
+ <comment xml:lang="hr">Excel 2007 proračunska tablica</comment>
+ <comment xml:lang="hu">Excel 2007 táblázat</comment>
+ <comment xml:lang="ia">Folio de calculo Excel 2007</comment>
+ <comment xml:lang="id">Lembar sebar Excel 2007</comment>
+ <comment xml:lang="it">Foglio di calcolo Excel 2007</comment>
+ <comment xml:lang="ja">Excel 2007 スプレッドシート</comment>
+ <comment xml:lang="ka">Excel 2007-ის ცხრილი</comment>
+ <comment xml:lang="kk">Excel 2007 электрондық кестесі</comment>
+ <comment xml:lang="ko">Excel 2007 스프레드시트</comment>
+ <comment xml:lang="lt">Excel 2007 skaičialentė</comment>
+ <comment xml:lang="lv">Excel 2007 izklājlapa</comment>
+ <comment xml:lang="nl">Excel 2007-rekenblad</comment>
+ <comment xml:lang="oc">fuèlh de calcul Excel 2007</comment>
+ <comment xml:lang="pl">Arkusz Excel 2007</comment>
+ <comment xml:lang="pt">folha de cálculo Excel 2007</comment>
+ <comment xml:lang="pt_BR">Planilha do Excel 2007</comment>
+ <comment xml:lang="ro">Foaie de calcul Excel 2007</comment>
+ <comment xml:lang="ru">Электронная таблица Excel 2007</comment>
+ <comment xml:lang="sk">Zošit Excel 2007</comment>
+ <comment xml:lang="sl">Razpredelnica Microsoft Excel 2007</comment>
+ <comment xml:lang="sr">табела Ексела 2007</comment>
+ <comment xml:lang="sv">Excel 2007-kalkylblad</comment>
+ <comment xml:lang="tr">Excel 2007 çalışma sayfası</comment>
+ <comment xml:lang="uk">ел. таблиця Excel 2007</comment>
+ <comment xml:lang="vi">Bảng tính Excel 2007</comment>
+ <comment xml:lang="zh_CN">Excel 2007 电子表格</comment>
+ <comment xml:lang="zh_TW">Excel 2007 試算表</comment>
+ <glob pattern="*.xlsx"/>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-spreadsheet"/>
+ </mime-type>
+ <mime-type type="application/vnd.openxmlformats-officedocument.spreadsheetml.template">
+ <comment>Excel 2007 spreadsheet template</comment>
+ <comment xml:lang="bg">Шаблон за таблици — Excel 2007</comment>
+ <comment xml:lang="ca">plantilla de full de càlcul d'Excel 2007</comment>
+ <comment xml:lang="cs">šablona sešitu Excel 2007</comment>
+ <comment xml:lang="da">Excel 2007-regnearksskabelon</comment>
+ <comment xml:lang="de">Excel 2007-Tabellenvorlage</comment>
+ <comment xml:lang="el">Πρότυπο λογιστικού φύλλου Excel 2007</comment>
+ <comment xml:lang="en_GB">Excel 2007 spreadsheet template</comment>
+ <comment xml:lang="es">plantilla de hoja de cálculo de Excel 2007</comment>
+ <comment xml:lang="eu">Excel 2007 kalkulu-orri txantiloia</comment>
+ <comment xml:lang="fi">Excel 2007 -taulukkomalli</comment>
+ <comment xml:lang="fr">modèle de feuille de calcul Excel 2007</comment>
+ <comment xml:lang="ga">teimpléad scarbhileoige Excel 2007</comment>
+ <comment xml:lang="gl">modelo de folla de cálculo Excel 2007</comment>
+ <comment xml:lang="he">תבנית של גיליון נתונים של Excel 2007</comment>
+ <comment xml:lang="hr">Excel 2007 predložak proračunske tablice</comment>
+ <comment xml:lang="hu">Excel 2007 táblázatsablon</comment>
+ <comment xml:lang="ia">Patrono de folio de calculo Excel 2007</comment>
+ <comment xml:lang="id">Templat lembar kerja Excel 2007</comment>
+ <comment xml:lang="it">Modello foglio di calcolo Excel 2007</comment>
+ <comment xml:lang="ja">Excel 2007 スプレッドシートテンプレート</comment>
+ <comment xml:lang="ka">Excel 2007-ის ცხრილის შაბლონი</comment>
+ <comment xml:lang="kk">Excel 2007 кесте шаблоны</comment>
+ <comment xml:lang="ko">Excel 2007 스프레드시트 서식</comment>
+ <comment xml:lang="lv">Excel 2007 izklājlapas veidne</comment>
+ <comment xml:lang="nl">Excel 2007 spreadsheet sjabloon</comment>
+ <comment xml:lang="oc">modèl de fuèlh de calcul Excel 2007</comment>
+ <comment xml:lang="pl">Szablon arkusza Excel 2007</comment>
+ <comment xml:lang="pt">modelo de folha de cálculo Excel 2007</comment>
+ <comment xml:lang="pt_BR">Modelo de planilha do Excel 2007</comment>
+ <comment xml:lang="ru">Шаблон электронной таблицы Excel 2007</comment>
+ <comment xml:lang="sk">Šablóna zošitu Excel 2007</comment>
+ <comment xml:lang="sl">Predloga razpredelnice Excel 2007</comment>
+ <comment xml:lang="sr">шаблон табеле Ексела 2007</comment>
+ <comment xml:lang="sv">Excel 2007-kalkylarksmall</comment>
+ <comment xml:lang="tr">Excel 2007 çalışma sayfası şablonu</comment>
+ <comment xml:lang="uk">шаблон електронної таблиці Excel 2007</comment>
+ <comment xml:lang="zh_CN">Excel 2007 电子表格模板</comment>
+ <comment xml:lang="zh_TW">Excel 2007 試算表範本</comment>
+ <glob pattern="*.xltx"/>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-spreadsheet"/>
+ </mime-type>
+ <mime-type type="application/x-t602">
+ <comment>T602 document</comment>
+ <comment xml:lang="ar">مستند T602</comment>
+ <comment xml:lang="ast">Documentu T602</comment>
+ <comment xml:lang="be@latin">Dakument T602</comment>
+ <comment xml:lang="bg">Документ — T602</comment>
+ <comment xml:lang="ca">document T602</comment>
+ <comment xml:lang="cs">dokument T602</comment>
+ <comment xml:lang="da">T602-dokument</comment>
+ <comment xml:lang="de">T602-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο T602</comment>
+ <comment xml:lang="en_GB">T602 document</comment>
+ <comment xml:lang="eo">T602-dokumento</comment>
+ <comment xml:lang="es">documento T602</comment>
+ <comment xml:lang="eu">T602 dokumentua</comment>
+ <comment xml:lang="fi">T602-asiakirja</comment>
+ <comment xml:lang="fo">T602 skjal</comment>
+ <comment xml:lang="fr">document T602</comment>
+ <comment xml:lang="ga">cáipéis T602</comment>
+ <comment xml:lang="gl">documento T602</comment>
+ <comment xml:lang="he">מסמך T602</comment>
+ <comment xml:lang="hr">T602 dokument</comment>
+ <comment xml:lang="hu">T602 dokumentum</comment>
+ <comment xml:lang="ia">Documento T602</comment>
+ <comment xml:lang="id">Dokumen T602</comment>
+ <comment xml:lang="it">Documento T602</comment>
+ <comment xml:lang="ja">T602 ドキュメント</comment>
+ <comment xml:lang="kk">T602 құжаты</comment>
+ <comment xml:lang="ko">T602 문서</comment>
+ <comment xml:lang="lt">T602 dokumentas</comment>
+ <comment xml:lang="lv">T602 dokuments</comment>
+ <comment xml:lang="nb">T602-dokument</comment>
+ <comment xml:lang="nl">T602-document</comment>
+ <comment xml:lang="nn">T602-dokument</comment>
+ <comment xml:lang="oc">document T602</comment>
+ <comment xml:lang="pl">Dokument T602</comment>
+ <comment xml:lang="pt">documento T602</comment>
+ <comment xml:lang="pt_BR">Documento T602</comment>
+ <comment xml:lang="ro">Document T602</comment>
+ <comment xml:lang="ru">Документ T602</comment>
+ <comment xml:lang="sk">Dokument T602</comment>
+ <comment xml:lang="sl">Dokument T602</comment>
+ <comment xml:lang="sq">Dokument T602</comment>
+ <comment xml:lang="sr">Т602 документ</comment>
+ <comment xml:lang="sv">T602-dokument</comment>
+ <comment xml:lang="tr">T602 belgesi</comment>
+ <comment xml:lang="uk">документ T602</comment>
+ <comment xml:lang="vi">Tài liệu T602</comment>
+ <comment xml:lang="zh_CN">T602 文档</comment>
+ <comment xml:lang="zh_TW">T602 文件</comment>
+ <generic-icon name="x-office-document"/>
+ <magic priority="50">
+ <match value="@CT 0" type="string" offset="0"/>
+ <match value="@CT 1" type="string" offset="0"/>
+ <match value="@CT 2" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.602"/>
+ </mime-type>
+ <mime-type type="application/x-cisco-vpn-settings">
+ <comment>Cisco VPN Settings</comment>
+ <comment xml:lang="ar">إعدادات Cisco VPN</comment>
+ <comment xml:lang="be@latin">Nałady Cisco VPN</comment>
+ <comment xml:lang="bg">Настройки — ВЧМ на Cisco</comment>
+ <comment xml:lang="ca">ajusts VPN de Cisco</comment>
+ <comment xml:lang="cs">nastavení Cisco VPN</comment>
+ <comment xml:lang="da">Cisco VPN-opsætning</comment>
+ <comment xml:lang="de">Cisco-VPN-Einstellungen</comment>
+ <comment xml:lang="el">Ρυθμίσεις Cisco VPN</comment>
+ <comment xml:lang="en_GB">Cisco VPN Settings</comment>
+ <comment xml:lang="es">configuración de VPN de Cisco</comment>
+ <comment xml:lang="eu">Cisco VPN ezarpenak</comment>
+ <comment xml:lang="fi">Cisco VPN -asetukset</comment>
+ <comment xml:lang="fo">Cisco VPN stillingar</comment>
+ <comment xml:lang="fr">paramètres VPN Cisco</comment>
+ <comment xml:lang="ga">socruithe VPN Cisco</comment>
+ <comment xml:lang="gl">configuracións de VPN de Cisco</comment>
+ <comment xml:lang="he">הגדרות של Cisco VPN</comment>
+ <comment xml:lang="hr">Cisco VPN postavke</comment>
+ <comment xml:lang="hu">Cisco VPN beállítások</comment>
+ <comment xml:lang="ia">Configuration VPN Cisco</comment>
+ <comment xml:lang="id">Penataan Cisco VPN</comment>
+ <comment xml:lang="it">Impostazioni VPN Cisco</comment>
+ <comment xml:lang="ja">Cisco VPN 設定</comment>
+ <comment xml:lang="ka">Cisco VPN-ის პარამეტრები</comment>
+ <comment xml:lang="kk">Cisco VPN баптаулары</comment>
+ <comment xml:lang="ko">Cisco VPN 설정</comment>
+ <comment xml:lang="lt">Cisco VPN parametrai</comment>
+ <comment xml:lang="lv">Cisco VPN iestatījumi</comment>
+ <comment xml:lang="nb">Cisco VPN-innstillinger</comment>
+ <comment xml:lang="nl">Cisco VPN-instellingen</comment>
+ <comment xml:lang="nn">Cisco VPN-innstillingar</comment>
+ <comment xml:lang="oc">paramètres VPN Cisco</comment>
+ <comment xml:lang="pl">Ustawienia VPN Cisco</comment>
+ <comment xml:lang="pt">definições de Cisco VPN</comment>
+ <comment xml:lang="pt_BR">Configurações de VPN da Cisco</comment>
+ <comment xml:lang="ro">Configurări VPN Cisco</comment>
+ <comment xml:lang="ru">Файл настроек Cisco VPN</comment>
+ <comment xml:lang="sk">Nastavenia Cisco VPN</comment>
+ <comment xml:lang="sl">Datoteka nastavitev Cisco VPN</comment>
+ <comment xml:lang="sq">Rregullime VPN Cisco</comment>
+ <comment xml:lang="sr">подешавања Циско ВПН-а</comment>
+ <comment xml:lang="sv">Cisco VPN-inställningar</comment>
+ <comment xml:lang="tr">Cisco VPN Ayarları</comment>
+ <comment xml:lang="uk">параметри VPN Cisco</comment>
+ <comment xml:lang="vi">Thiết lập VPN Cisco</comment>
+ <comment xml:lang="zh_CN">Cisco VPN 设置</comment>
+ <comment xml:lang="zh_TW">Cisco VPN 設定值</comment>
+ <generic-icon name="text-x-generic"/>
+ <magic priority="50">
+ <match value="[main]" type="string" offset="0">
+ <match value="AuthType=" type="string" offset="0:256"/>
+ </match>
+ </magic>
+ <glob pattern="*.pcf"/>
+ </mime-type>
+ <mime-type type="application/vnd.iccprofile">
+ <comment>ICC profile</comment>
+ <comment xml:lang="ar">تشكيلة OCL</comment>
+ <comment xml:lang="ast">Perfil ICC</comment>
+ <comment xml:lang="bg">Цветови профил — OCL</comment>
+ <comment xml:lang="ca">perfil ICC</comment>
+ <comment xml:lang="cs">profil ICC</comment>
+ <comment xml:lang="da">ICC-profil</comment>
+ <comment xml:lang="de">ICC-Profil</comment>
+ <comment xml:lang="el">Προφίλ ICC</comment>
+ <comment xml:lang="en_GB">ICC profile</comment>
+ <comment xml:lang="eo">ICC-profilo</comment>
+ <comment xml:lang="es">perfil ICC</comment>
+ <comment xml:lang="eu">ICC profila</comment>
+ <comment xml:lang="fi">ICC-profiili</comment>
+ <comment xml:lang="fo">ICC umhvarv</comment>
+ <comment xml:lang="fr">profil ICC</comment>
+ <comment xml:lang="ga">próifíl ICC</comment>
+ <comment xml:lang="gl">perfíl ICC</comment>
+ <comment xml:lang="he">פרופיל ICC</comment>
+ <comment xml:lang="hr">ICC profil</comment>
+ <comment xml:lang="hu">ICC profil</comment>
+ <comment xml:lang="ia">Profilo ICC</comment>
+ <comment xml:lang="id">Profil ICC</comment>
+ <comment xml:lang="it">Profilo ICC</comment>
+ <comment xml:lang="ja">ICC プロファイル</comment>
+ <comment xml:lang="kk">ICC профайлы</comment>
+ <comment xml:lang="ko">ICC 프로필</comment>
+ <comment xml:lang="lt">ICC profilis</comment>
+ <comment xml:lang="lv">ICC profils</comment>
+ <comment xml:lang="nl">ICC profiel</comment>
+ <comment xml:lang="oc">perfil ICC</comment>
+ <comment xml:lang="pl">Profil ICC</comment>
+ <comment xml:lang="pt">perfil ICC</comment>
+ <comment xml:lang="pt_BR">Perfil ICC</comment>
+ <comment xml:lang="ro">Profil ICC</comment>
+ <comment xml:lang="ru">Профиль ICC</comment>
+ <comment xml:lang="sk">Profil farieb ICC</comment>
+ <comment xml:lang="sl">Datoteka profila ICC</comment>
+ <comment xml:lang="sr">ИЦЦ профил</comment>
+ <comment xml:lang="sv">ICC-profil</comment>
+ <comment xml:lang="tr">ICC profili</comment>
+ <comment xml:lang="uk">профіль ICC</comment>
+ <comment xml:lang="zh_CN">ICC 配置文件</comment>
+ <comment xml:lang="zh_TW">ICC 設定檔</comment>
+ <magic priority="50">
+ <match value="acsp" type="string" offset="36"/>
+ </magic>
+ <glob pattern="*.icc"/>
+ <glob pattern="*.icm"/>
+ </mime-type>
+ <mime-type type="application/x-it87">
+ <comment>IT 8.7 color calibration file</comment>
+ <comment xml:lang="ar">ملف ضبط ألوان IT 8.7</comment>
+ <comment xml:lang="bg">Файл за цветово калибриране — IT 8.7</comment>
+ <comment xml:lang="ca">fitxer de calibratge de color IT 8.7</comment>
+ <comment xml:lang="cs">soubor kalibrace barev IT 8.7</comment>
+ <comment xml:lang="da">IT 8.7 farvekalibreringsfil</comment>
+ <comment xml:lang="de">IT 8.7-Farbkalibrierungsdatei</comment>
+ <comment xml:lang="el">Αρχείο βαθμονόμησης χρώματος ΙΤ 8.7</comment>
+ <comment xml:lang="en_GB">IT 8.7 color calibration file</comment>
+ <comment xml:lang="es">archivo de calibración de color IT 8.7</comment>
+ <comment xml:lang="eu">IT 8.7 kolore-kalibrazioaren fitxategia</comment>
+ <comment xml:lang="fi">IT 8.7 -värikalibrointitiedosto</comment>
+ <comment xml:lang="fo">IT 8.7 litstillingarfíla</comment>
+ <comment xml:lang="fr">fichier de calibration couleur IT 8.7</comment>
+ <comment xml:lang="ga">comhad calabraithe dathanna IT 8.7</comment>
+ <comment xml:lang="gl">ficheiro de calibración de cor IT 8.7</comment>
+ <comment xml:lang="he">קובץ כיול צבע IT 8.7</comment>
+ <comment xml:lang="hr">IT 8.7 datoteka kalibracije boja</comment>
+ <comment xml:lang="hu">IT 8.7 színkalibrációs fájl</comment>
+ <comment xml:lang="ia">File de calibration de colores IT 8.7</comment>
+ <comment xml:lang="id">Berkas kalibrasi warna IT 8.7</comment>
+ <comment xml:lang="it">File calibrazione colore IT 8.7</comment>
+ <comment xml:lang="ja">IT 8.7 カラーキャリブレーションファイル</comment>
+ <comment xml:lang="kk">IT 8.7 түс баптау файлы</comment>
+ <comment xml:lang="ko">IT 8.7 색 조율 파일</comment>
+ <comment xml:lang="lt">IT 8.7 spalvų kalibravimo failas</comment>
+ <comment xml:lang="lv">IT 8.7 krāsu kalibrācijas datne</comment>
+ <comment xml:lang="nl">IT 8.7 kleurcalibratie bestand</comment>
+ <comment xml:lang="oc">fichièr de calibracion color IT 8.7</comment>
+ <comment xml:lang="pl">Plik kalibracji kolorów IT 8.7</comment>
+ <comment xml:lang="pt">ficheiro de calibração de cor IT 8.7</comment>
+ <comment xml:lang="pt_BR">Arquivo de calibração de cor IT 8.7</comment>
+ <comment xml:lang="ro">Fișier de calibrare a culorii IT 8.7</comment>
+ <comment xml:lang="ru">Файл калибровки цвета IT 8.7</comment>
+ <comment xml:lang="sk">Súbor kalibrácie farieb IT 8.7</comment>
+ <comment xml:lang="sl">Umeritvena datoteka barve IT 8.7</comment>
+ <comment xml:lang="sr">ИТ 8.7 датотека калибрације боје</comment>
+ <comment xml:lang="sv">IT 8.7-färgkalibreringsfil</comment>
+ <comment xml:lang="tr">IT 8.7 renk kalibrasyon dosyası</comment>
+ <comment xml:lang="uk">файл калібрування кольорів IT 8.7</comment>
+ <comment xml:lang="zh_CN">IT 8.7 色彩校准文件</comment>
+ <comment xml:lang="zh_TW">IT 8.7 色彩校正檔</comment>
+ <magic priority="50">
+ <match value="IT8.7" type="string" offset="0"/>
+ </magic>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.it87"/>
+ </mime-type>
+ <mime-type type="application/x-ccmx">
+ <comment>CCMX color correction file</comment>
+ <comment xml:lang="ca">fitxer de correcció de color CCMX</comment>
+ <comment xml:lang="cs">soubor korekce barev CCMX</comment>
+ <comment xml:lang="da">CCMX-farvekorrektionsfil</comment>
+ <comment xml:lang="de">CCMX-Farbkorrekturdatei</comment>
+ <comment xml:lang="el">Αρχείο διόρθωσης χρωμάτων CCMX</comment>
+ <comment xml:lang="en_GB">CCMX colour correction file</comment>
+ <comment xml:lang="es">archivo de corrección de color CCMX</comment>
+ <comment xml:lang="eu">CCMX kolore-kalibrazioaren fitxategia</comment>
+ <comment xml:lang="fi">CCMX-värikorjaustiedosto</comment>
+ <comment xml:lang="fr">fichier de correction colorimétrique CCMX</comment>
+ <comment xml:lang="ga">comhad ceartúchán dathanna CCMX</comment>
+ <comment xml:lang="gl">Ficheiro de corrección de cor CCMX</comment>
+ <comment xml:lang="he">קובץ תיקון צבע מסוג CCMX</comment>
+ <comment xml:lang="hr">CCMX datotkea ispravka boja</comment>
+ <comment xml:lang="hu">CCMX színjavítási fájl</comment>
+ <comment xml:lang="ia">File de correction de colores CCMX</comment>
+ <comment xml:lang="id">Berkas koreksi warna CCMX</comment>
+ <comment xml:lang="it">File correzione colore CCMX</comment>
+ <comment xml:lang="ja">CCMX カラー訂正ファイル</comment>
+ <comment xml:lang="kk">CCMX түсті келтіру файлы</comment>
+ <comment xml:lang="ko">CCMX 색상 보정 파일</comment>
+ <comment xml:lang="lv">CCMX krāsu korekciju datne</comment>
+ <comment xml:lang="oc">fichièr de correccion colorimetrica CCMX</comment>
+ <comment xml:lang="pl">Plik korekcji kolorów CCMX</comment>
+ <comment xml:lang="pt">ficheiro de correção de cor CCMX</comment>
+ <comment xml:lang="pt_BR">Arquivo de correção de cor CCMX</comment>
+ <comment xml:lang="ru">Файл цветовой коррекции CCMX</comment>
+ <comment xml:lang="sk">Súbor korekcie farieb CCMX</comment>
+ <comment xml:lang="sl">Datoteka barvne poprave CCMX</comment>
+ <comment xml:lang="sr">ЦЦМИкс датотека поправке боје</comment>
+ <comment xml:lang="sv">CCMX-färgkorrigeringsfil</comment>
+ <comment xml:lang="tr">CCMX renk düzeltme dosyası</comment>
+ <comment xml:lang="uk">файл даних виправлення кольорів CCMX</comment>
+ <comment xml:lang="zh_CN">CCMX 色彩校准文件</comment>
+ <comment xml:lang="zh_TW">CCMX 色彩校正檔</comment>
+ <magic priority="50">
+ <match value="CCMX" type="string" offset="0"/>
+ </magic>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.ccmx"/>
+ </mime-type>
+ <mime-type type="application/winhlp">
+ <comment>WinHelp help file</comment>
+ <comment xml:lang="ca">fitxer d'ajuda WinHelp</comment>
+ <comment xml:lang="cs">soubor nápovědy WinHelp</comment>
+ <comment xml:lang="da">WinHelp-hjælpefil</comment>
+ <comment xml:lang="de">WinHelp-Hilfedatei</comment>
+ <comment xml:lang="el">Αρχείο βοήθειας WinHelp</comment>
+ <comment xml:lang="en_GB">WinHelp help file</comment>
+ <comment xml:lang="es">archivo de ayuda de WinHelp</comment>
+ <comment xml:lang="eu">WinHelp laguntza fitxategia</comment>
+ <comment xml:lang="fi">WinHelp-ohjetiedosto</comment>
+ <comment xml:lang="fr">fichier d'aide WinHelp</comment>
+ <comment xml:lang="ga">comhad cabhrach WinHelp</comment>
+ <comment xml:lang="gl">Ficheiro de axuda WinHelp</comment>
+ <comment xml:lang="he">קובץ עזרה מסוג WinHelp</comment>
+ <comment xml:lang="hr">WinHelp datoteka pomoći</comment>
+ <comment xml:lang="hu">WinHelp súgófájl</comment>
+ <comment xml:lang="ia">File de adjuta WinHelp</comment>
+ <comment xml:lang="id">Berkas bantuan WinHelp</comment>
+ <comment xml:lang="it">File aiuto WInHelp</comment>
+ <comment xml:lang="ja">WinHelp ヘルプファイル</comment>
+ <comment xml:lang="kk">WinHelp көмек файлы</comment>
+ <comment xml:lang="ko">WinHelp 도움말 파일</comment>
+ <comment xml:lang="lv">WinHelp palīdzības datne</comment>
+ <comment xml:lang="oc">fichièr d'ajuda WinHelp</comment>
+ <comment xml:lang="pl">Plik pomocy WinHelp</comment>
+ <comment xml:lang="pt">ficheiro de ajuda WinHelp</comment>
+ <comment xml:lang="pt_BR">Arquivo de ajuda WinHelp</comment>
+ <comment xml:lang="ru">Файл справки WinHelp</comment>
+ <comment xml:lang="sk">Súbor Pomocníka WinHelp</comment>
+ <comment xml:lang="sl">Datoteka pomoči WinHelp</comment>
+ <comment xml:lang="sr">датотека помоћи Вин хелпа</comment>
+ <comment xml:lang="sv">WinHelp-hjälpfil</comment>
+ <comment xml:lang="tr">WinHelp yardım dosyası</comment>
+ <comment xml:lang="uk">файл довідки WinHelp</comment>
+ <comment xml:lang="zh_CN">WinHelp 帮助文件</comment>
+ <comment xml:lang="zh_TW">WinHelp 說明檔</comment>
+ <magic priority="50">
+ <match value="0x00035f3f" type="little32" offset="0"/>
+ </magic>
+ <glob pattern="*.hlp"/>
+ <alias type="zz-application/zz-winassoc-hlp"/>
+ </mime-type>
+ <mime-type type="application/x-bsdiff">
+ <comment>binary differences between files</comment>
+ <comment xml:lang="ca">diferencies binàries entre fitxers</comment>
+ <comment xml:lang="cs">binární rozdíl mezi soubory</comment>
+ <comment xml:lang="da">binære forskelle mellem filer</comment>
+ <comment xml:lang="de">binäre Unterschiede zwischen Dateien</comment>
+ <comment xml:lang="en_GB">binary differences between files</comment>
+ <comment xml:lang="es">diferencias entre archivos binarios</comment>
+ <comment xml:lang="eu">fitxategi binarioen arteko ezberdinstasunak</comment>
+ <comment xml:lang="fr">différences binaires entre fichiers</comment>
+ <comment xml:lang="ga">difríochtaí dénártha idir comhaid</comment>
+ <comment xml:lang="he">הבדלים בינריים בין קבצים</comment>
+ <comment xml:lang="hr">Binarne razlike između datoteka</comment>
+ <comment xml:lang="hu">bináris különbségfájl</comment>
+ <comment xml:lang="id">perbedaan biner antar berkas</comment>
+ <comment xml:lang="it">Differenze binarie tra file</comment>
+ <comment xml:lang="kk">файлдар арасындағы бинарлық айырмашылықтар</comment>
+ <comment xml:lang="ko">바이너리 차이 비교 파일</comment>
+ <comment xml:lang="pl">Binarna różnica pomiędzy plikami</comment>
+ <comment xml:lang="pt_BR">Diferenças binárias entre arquivos</comment>
+ <comment xml:lang="ru">Двоичные различия между файлами</comment>
+ <comment xml:lang="sk">Binárne rozdiely medzi súbormi</comment>
+ <comment xml:lang="sr">бинарне разлике датотека</comment>
+ <comment xml:lang="sv">binära skillnader mellan filer</comment>
+ <comment xml:lang="tr">dosyalar arasındaki ikilik farklar</comment>
+ <comment xml:lang="uk">двійкова різниця між файлами</comment>
+ <comment xml:lang="zh_CN">文件的二进制区别</comment>
+ <comment xml:lang="zh_TW">檔案間的二進位差異</comment>
+ <magic>
+ <match value="BSDIFF40" type="string" offset="0"/>
+ <match value="BSDIFN40" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.bsdiff"/>
+ </mime-type>
+
+
+ <mime-type type="x-content/image-dcf">
+
+ <comment>digital photos</comment>
+ <comment xml:lang="ar">الصور الرقمية</comment>
+ <comment xml:lang="be@latin">ličbavyja zdymki</comment>
+ <comment xml:lang="bg">Цифрови фотографии</comment>
+ <comment xml:lang="ca">fotos digitals</comment>
+ <comment xml:lang="cs">digitální fotografie</comment>
+ <comment xml:lang="da">digitale billeder</comment>
+ <comment xml:lang="de">Digitale Fotos</comment>
+ <comment xml:lang="el">Ψηφιακές φωτογραφίες</comment>
+ <comment xml:lang="en_GB">digital photos</comment>
+ <comment xml:lang="es">fotos digitales</comment>
+ <comment xml:lang="eu">argazki digitalak</comment>
+ <comment xml:lang="fi">digivalokuvia</comment>
+ <comment xml:lang="fo">talgildar myndir</comment>
+ <comment xml:lang="fr">photos numériques</comment>
+ <comment xml:lang="ga">grianghraif dhigiteacha</comment>
+ <comment xml:lang="gl">fotos dixitais</comment>
+ <comment xml:lang="he">תמונות דיגיטליות</comment>
+ <comment xml:lang="hr">Digitalne fotografije</comment>
+ <comment xml:lang="hu">digitális fényképek</comment>
+ <comment xml:lang="ia">Photos digital</comment>
+ <comment xml:lang="id">foto digital</comment>
+ <comment xml:lang="it">Foto digitali</comment>
+ <comment xml:lang="ja">デジタルフォト</comment>
+ <comment xml:lang="kk">сандық фотосуреттер</comment>
+ <comment xml:lang="ko">디지털 사진</comment>
+ <comment xml:lang="lt">skaitmeninės nuotraukos</comment>
+ <comment xml:lang="lv">digitāla fotogrāfija</comment>
+ <comment xml:lang="nl">digitale foto's</comment>
+ <comment xml:lang="nn">digitale fotografi</comment>
+ <comment xml:lang="oc">fòtos numericas</comment>
+ <comment xml:lang="pl">Zdjęcia cyfrowe</comment>
+ <comment xml:lang="pt">fotografias digitais</comment>
+ <comment xml:lang="pt_BR">Fotos digitais</comment>
+ <comment xml:lang="ro">fotografii digitale</comment>
+ <comment xml:lang="ru">Цифровые фотографии</comment>
+ <comment xml:lang="sk">Digitálne fotografie</comment>
+ <comment xml:lang="sl">digitalne fotografije</comment>
+ <comment xml:lang="sq">Fotografi dixhitale</comment>
+ <comment xml:lang="sr">дигиталне фотографије</comment>
+ <comment xml:lang="sv">digitalbilder</comment>
+ <comment xml:lang="tr">sayısal fotoğraflar</comment>
+ <comment xml:lang="uk">цифрові фотографії</comment>
+ <comment xml:lang="vi">ảnh chụp số</comment>
+ <comment xml:lang="zh_CN">数字化图像</comment>
+ <comment xml:lang="zh_TW">數位相片</comment>
+ <treemagic>
+ <treematch type="directory" path="dcim" non-empty="true"/>
+ </treemagic>
+ </mime-type>
+
+ <mime-type type="x-content/video-vcd">
+
+ <comment>Video CD</comment>
+ <comment xml:lang="ar">Video CD</comment>
+ <comment xml:lang="ast">CD de videu</comment>
+ <comment xml:lang="be@latin">Videa CD</comment>
+ <comment xml:lang="bg">CD — видео</comment>
+ <comment xml:lang="ca">Video CD</comment>
+ <comment xml:lang="cs">Video CD</comment>
+ <comment xml:lang="da">Video-cd</comment>
+ <comment xml:lang="de">Video-CD</comment>
+ <comment xml:lang="el">Video CD</comment>
+ <comment xml:lang="en_GB">Video CD</comment>
+ <comment xml:lang="eo">Video-KD</comment>
+ <comment xml:lang="es">Video CD</comment>
+ <comment xml:lang="eu">Bideo CDa</comment>
+ <comment xml:lang="fi">Video CD</comment>
+ <comment xml:lang="fo">Video CD</comment>
+ <comment xml:lang="fr">CD vidéo</comment>
+ <comment xml:lang="ga">dlúthdhiosca físe</comment>
+ <comment xml:lang="gl">Video CD</comment>
+ <comment xml:lang="he">תקליטור וידאו</comment>
+ <comment xml:lang="hr">Video CD</comment>
+ <comment xml:lang="hu">Video CD</comment>
+ <comment xml:lang="ia">Video CD</comment>
+ <comment xml:lang="id">Video CD</comment>
+ <comment xml:lang="it">Video CD</comment>
+ <comment xml:lang="ja">ビデオ CD</comment>
+ <comment xml:lang="kk">видео CD</comment>
+ <comment xml:lang="ko">비디오 CD</comment>
+ <comment xml:lang="lt">Vaizdo CD</comment>
+ <comment xml:lang="lv">Video CD</comment>
+ <comment xml:lang="nl">video-CD</comment>
+ <comment xml:lang="nn">Video-CD</comment>
+ <comment xml:lang="oc">CD vidèo</comment>
+ <comment xml:lang="pl">Video CD</comment>
+ <comment xml:lang="pt">Video CD</comment>
+ <comment xml:lang="pt_BR">CD de vídeo</comment>
+ <comment xml:lang="ro">CD video</comment>
+ <comment xml:lang="ru">Видео CD</comment>
+ <comment xml:lang="sk">Video CD</comment>
+ <comment xml:lang="sl">Video CD</comment>
+ <comment xml:lang="sq">CD Video</comment>
+ <comment xml:lang="sr">Видео ЦД</comment>
+ <comment xml:lang="sv">Video-cd</comment>
+ <comment xml:lang="tr">Video CD</comment>
+ <comment xml:lang="uk">Video CD</comment>
+ <comment xml:lang="vi">Đĩa CD ảnh động</comment>
+ <comment xml:lang="zh_CN">VCD</comment>
+ <comment xml:lang="zh_TW">Video CD</comment>
+ <treemagic>
+ <treematch type="file" path="mpegav/AVSEQ01.DAT"/>
+ </treemagic>
+ </mime-type>
+
+ <mime-type type="x-content/video-svcd">
+
+ <comment>Super Video CD</comment>
+ <comment xml:lang="ar">Super Video CD</comment>
+ <comment xml:lang="ast">CD de Super Video</comment>
+ <comment xml:lang="be@latin">Super Video CD</comment>
+ <comment xml:lang="bg">CD — супер видео</comment>
+ <comment xml:lang="ca">Super Video CD</comment>
+ <comment xml:lang="cs">Super Video CD</comment>
+ <comment xml:lang="da">Super Video-cd</comment>
+ <comment xml:lang="de">Super-Video-CD</comment>
+ <comment xml:lang="el">Super Video CD</comment>
+ <comment xml:lang="en_GB">Super Video CD</comment>
+ <comment xml:lang="eo">Super-Video-KD</comment>
+ <comment xml:lang="es">Super Video CD</comment>
+ <comment xml:lang="eu">Super Bideo CDa</comment>
+ <comment xml:lang="fi">Super Video CD</comment>
+ <comment xml:lang="fo">Super Video CD</comment>
+ <comment xml:lang="fr">Super VCD</comment>
+ <comment xml:lang="ga">dlúthdhiosca Super Video</comment>
+ <comment xml:lang="gl">Super vídeo CD</comment>
+ <comment xml:lang="he">Super Video CD</comment>
+ <comment xml:lang="hr">Super Video CD</comment>
+ <comment xml:lang="hu">Super Video CD</comment>
+ <comment xml:lang="ia">Super Video CD</comment>
+ <comment xml:lang="id">Super Video CD</comment>
+ <comment xml:lang="it">Super Video CD</comment>
+ <comment xml:lang="ja">スーパービデオ CD</comment>
+ <comment xml:lang="kk">Super Video CD</comment>
+ <comment xml:lang="ko">수퍼 비디오 CD</comment>
+ <comment xml:lang="lt">Super vaizdo CD</comment>
+ <comment xml:lang="lv">Super Video CD</comment>
+ <comment xml:lang="nl">super-video-CD</comment>
+ <comment xml:lang="nn">Super Video-CD</comment>
+ <comment xml:lang="oc">Super VCD</comment>
+ <comment xml:lang="pl">Super Video CD</comment>
+ <comment xml:lang="pt">Super Video CD</comment>
+ <comment xml:lang="pt_BR">CD de Super Vídeo (SVCD)</comment>
+ <comment xml:lang="ro">Super Video CD</comment>
+ <comment xml:lang="ru">Super Video CD</comment>
+ <comment xml:lang="sk">Super Video CD</comment>
+ <comment xml:lang="sl">Super Video CD</comment>
+ <comment xml:lang="sq">CD Super Video</comment>
+ <comment xml:lang="sr">Супер видео ЦД</comment>
+ <comment xml:lang="sv">Super Video CD</comment>
+ <comment xml:lang="tr">Super Video CD</comment>
+ <comment xml:lang="uk">Super Video CD</comment>
+ <comment xml:lang="vi">Đĩa CD siêu ảnh động</comment>
+ <comment xml:lang="zh_CN">SVCD</comment>
+ <comment xml:lang="zh_TW">Super Video CD</comment>
+ <treemagic>
+ <treematch type="file" path="MPEG2/AVSEQ01.MPG"/>
+ </treemagic>
+ </mime-type>
+
+ <mime-type type="x-content/video-dvd">
+
+ <comment>video DVD</comment>
+ <comment xml:lang="ar">DVD مرئي</comment>
+ <comment xml:lang="ast">DVD de videu</comment>
+ <comment xml:lang="be@latin">videa DVD</comment>
+ <comment xml:lang="bg">DVD — видео</comment>
+ <comment xml:lang="ca">DVD-Video</comment>
+ <comment xml:lang="cs">videodisk DVD</comment>
+ <comment xml:lang="da">video-dvd</comment>
+ <comment xml:lang="de">Video-DVD</comment>
+ <comment xml:lang="el">Βίντεο DVD</comment>
+ <comment xml:lang="en_GB">video DVD</comment>
+ <comment xml:lang="eo">video-DVD</comment>
+ <comment xml:lang="es">DVD de vídeo</comment>
+ <comment xml:lang="eu">bideo DVDa</comment>
+ <comment xml:lang="fi">video-DVD</comment>
+ <comment xml:lang="fo">video DVD</comment>
+ <comment xml:lang="fr">DVD vidéo</comment>
+ <comment xml:lang="ga">DVD físe</comment>
+ <comment xml:lang="gl">DVD de vídeo</comment>
+ <comment xml:lang="he">DVD וידאו</comment>
+ <comment xml:lang="hr">Video DVD</comment>
+ <comment xml:lang="hu">video DVD</comment>
+ <comment xml:lang="ia">DVD video</comment>
+ <comment xml:lang="id">DVD video</comment>
+ <comment xml:lang="it">DVD video</comment>
+ <comment xml:lang="ja">ビデオ DVD</comment>
+ <comment xml:lang="ka">ვიდეო DVD</comment>
+ <comment xml:lang="kk">видео DVD</comment>
+ <comment xml:lang="ko">동영상 DVD</comment>
+ <comment xml:lang="lt">vaizdo DVD</comment>
+ <comment xml:lang="lv">video DVD</comment>
+ <comment xml:lang="nl">video-DVD</comment>
+ <comment xml:lang="nn">Video-DVD</comment>
+ <comment xml:lang="oc">DVD vidèo</comment>
+ <comment xml:lang="pl">DVD-Video</comment>
+ <comment xml:lang="pt">DVD vídeo</comment>
+ <comment xml:lang="pt_BR">DVD de vídeo</comment>
+ <comment xml:lang="ro">DVD video</comment>
+ <comment xml:lang="ru">Видео DVD</comment>
+ <comment xml:lang="sk">DVD-Video</comment>
+ <comment xml:lang="sl">video DVD</comment>
+ <comment xml:lang="sq">DVD video</comment>
+ <comment xml:lang="sr">видео ДВД</comment>
+ <comment xml:lang="sv">video-dvd</comment>
+ <comment xml:lang="tr">video DVD</comment>
+ <comment xml:lang="uk">відео-DVD</comment>
+ <comment xml:lang="vi">đĩa DVD ảnh động</comment>
+ <comment xml:lang="zh_CN">视频 DVD</comment>
+ <comment xml:lang="zh_TW">視訊 DVD</comment>
+ <treemagic>
+ <treematch type="file" path="VIDEO_TS/VIDEO_TS.IFO"/>
+ <treematch type="file" path="VIDEO_TS/VIDEO_TS.IFO;1"/>
+ <treematch type="file" path="VIDEO_TS.IFO"/>
+ <treematch type="file" path="VIDEO_TS.IFO;1"/>
+ </treemagic>
+ </mime-type>
+
+ <mime-type type="x-content/audio-cdda">
+
+ <comment>audio CD</comment>
+ <comment xml:lang="ar">CD سمعي</comment>
+ <comment xml:lang="be@latin">aŭdyjo CD</comment>
+ <comment xml:lang="bg">CD — аудио</comment>
+ <comment xml:lang="ca">CD d'àudio</comment>
+ <comment xml:lang="cs">zvukové CD</comment>
+ <comment xml:lang="da">lyd-cd</comment>
+ <comment xml:lang="de">Audio-CD</comment>
+ <comment xml:lang="el">CD ήχου</comment>
+ <comment xml:lang="en_GB">audio CD</comment>
+ <comment xml:lang="eo">Son-KD</comment>
+ <comment xml:lang="es">CD de sonido</comment>
+ <comment xml:lang="eu">Audio CDa</comment>
+ <comment xml:lang="fi">ääni-CD</comment>
+ <comment xml:lang="fo">audio CD</comment>
+ <comment xml:lang="fr">CD audio</comment>
+ <comment xml:lang="ga">dlúthdhiosca fuaime</comment>
+ <comment xml:lang="gl">CD de son</comment>
+ <comment xml:lang="he">תקליטור שמע</comment>
+ <comment xml:lang="hr">Glazbeni CD</comment>
+ <comment xml:lang="hu">hang CD</comment>
+ <comment xml:lang="ia">CD audio</comment>
+ <comment xml:lang="id">CD audio</comment>
+ <comment xml:lang="it">CD audio</comment>
+ <comment xml:lang="ja">オーディオ CD</comment>
+ <comment xml:lang="kk">аудио CD</comment>
+ <comment xml:lang="ko">오디오 CD</comment>
+ <comment xml:lang="lt">garso CD</comment>
+ <comment xml:lang="lv">audio CD</comment>
+ <comment xml:lang="nl">audio-CD</comment>
+ <comment xml:lang="nn">lyd-CD</comment>
+ <comment xml:lang="oc">CD àudio</comment>
+ <comment xml:lang="pl">CD-Audio</comment>
+ <comment xml:lang="pt">CD áudio</comment>
+ <comment xml:lang="pt_BR">CD de áudio</comment>
+ <comment xml:lang="ro">CD audio</comment>
+ <comment xml:lang="ru">Аудио CD</comment>
+ <comment xml:lang="sk">Zvukové CD</comment>
+ <comment xml:lang="sl">zvočni CD</comment>
+ <comment xml:lang="sq">CD audio</comment>
+ <comment xml:lang="sr">звучни ЦД</comment>
+ <comment xml:lang="sv">ljud-cd</comment>
+ <comment xml:lang="tr">Müzik CD'si</comment>
+ <comment xml:lang="uk">звуковий CD</comment>
+ <comment xml:lang="vi">đĩa CD âm thanh</comment>
+ <comment xml:lang="zh_CN">音频 CD</comment>
+ <comment xml:lang="zh_TW">音訊 CD</comment>
+ </mime-type>
+
+ <mime-type type="x-content/blank-cd">
+
+ <comment>blank CD disc</comment>
+ <comment xml:lang="ar">قرص CD فارغ</comment>
+ <comment xml:lang="be@latin">čysty dysk CD</comment>
+ <comment xml:lang="bg">CD — празно</comment>
+ <comment xml:lang="ca">disc CD en blanc</comment>
+ <comment xml:lang="cs">prázdný disk CD</comment>
+ <comment xml:lang="da">tom cd-disk</comment>
+ <comment xml:lang="de">Leere CD</comment>
+ <comment xml:lang="el">Κενό CD</comment>
+ <comment xml:lang="en_GB">blank CD disc</comment>
+ <comment xml:lang="es">disco CD en blanco</comment>
+ <comment xml:lang="eu">CD disko hutsa</comment>
+ <comment xml:lang="fi">tyhjä CD-levy</comment>
+ <comment xml:lang="fo">blonk fløga</comment>
+ <comment xml:lang="fr">CD vierge</comment>
+ <comment xml:lang="ga">dlúthdhiosca folamh</comment>
+ <comment xml:lang="gl">disco de CD en brancho</comment>
+ <comment xml:lang="he">תקליטור ריק</comment>
+ <comment xml:lang="hr">Prazni CD disk</comment>
+ <comment xml:lang="hu">üres CD-lemez</comment>
+ <comment xml:lang="ia">Disco CD vacue</comment>
+ <comment xml:lang="id">cakram CD kosong</comment>
+ <comment xml:lang="it">Disco vuoto CD</comment>
+ <comment xml:lang="ja">ブランク CD ディスク</comment>
+ <comment xml:lang="kk">таза CD дискі</comment>
+ <comment xml:lang="ko">빈 CD 디스크</comment>
+ <comment xml:lang="lt">tuščias CD diskas</comment>
+ <comment xml:lang="lv">tukšs CD disks</comment>
+ <comment xml:lang="nl">blanco CD</comment>
+ <comment xml:lang="nn">tom CD-plate</comment>
+ <comment xml:lang="oc">CD verge</comment>
+ <comment xml:lang="pl">Pusta płyta CD</comment>
+ <comment xml:lang="pt">CD vazio</comment>
+ <comment xml:lang="pt_BR">Disco CD vazio</comment>
+ <comment xml:lang="ro">disc gol CD</comment>
+ <comment xml:lang="ru">Чистый диск CD</comment>
+ <comment xml:lang="sk">Prázdny disk CD</comment>
+ <comment xml:lang="sl">prazen CD disk</comment>
+ <comment xml:lang="sq">Disk bosh CD</comment>
+ <comment xml:lang="sr">празан ЦД диск</comment>
+ <comment xml:lang="sv">tom cd-skiva</comment>
+ <comment xml:lang="tr">boş CD diski</comment>
+ <comment xml:lang="uk">порожній компакт-диск</comment>
+ <comment xml:lang="vi">đĩa CD trống</comment>
+ <comment xml:lang="zh_CN">空 CD 光盘</comment>
+ <comment xml:lang="zh_TW">空白 CD 光碟</comment>
+ </mime-type>
+
+ <mime-type type="x-content/blank-dvd">
+
+ <comment>blank DVD disc</comment>
+ <comment xml:lang="ar">قرص DVD فارغ</comment>
+ <comment xml:lang="be@latin">čysty dysk DVD</comment>
+ <comment xml:lang="bg">DVD — празно</comment>
+ <comment xml:lang="ca">disc DVD en blanc</comment>
+ <comment xml:lang="cs">prázdný disk DVD</comment>
+ <comment xml:lang="da">tom dvd-disk</comment>
+ <comment xml:lang="de">Leere DVD</comment>
+ <comment xml:lang="el">Κενό DVD</comment>
+ <comment xml:lang="en_GB">blank DVD disc</comment>
+ <comment xml:lang="es">disco DVD en blanco</comment>
+ <comment xml:lang="eu">DVD disko hutsa</comment>
+ <comment xml:lang="fi">tyhjä DVD-levy</comment>
+ <comment xml:lang="fo">blonk margfløga</comment>
+ <comment xml:lang="fr">DVD vierge</comment>
+ <comment xml:lang="ga">DVD folamh</comment>
+ <comment xml:lang="gl">disco de DVD en branco</comment>
+ <comment xml:lang="he">תקליטור DVD ריק</comment>
+ <comment xml:lang="hr">Prazni DVD disk</comment>
+ <comment xml:lang="hu">üres DVD-lemez</comment>
+ <comment xml:lang="ia">Disco DVD vacue</comment>
+ <comment xml:lang="id">cakram DVD kosong</comment>
+ <comment xml:lang="it">Disco vuoto DVD</comment>
+ <comment xml:lang="ja">ブランク DVD ディスク</comment>
+ <comment xml:lang="kk">таза DVD дискі</comment>
+ <comment xml:lang="ko">빈 DVD 디스크</comment>
+ <comment xml:lang="lt">tuščias DVD diskas</comment>
+ <comment xml:lang="lv">tukšs DVD disks</comment>
+ <comment xml:lang="nl">blanco DVD</comment>
+ <comment xml:lang="nn">tom DVD-plate</comment>
+ <comment xml:lang="oc">DVD verge</comment>
+ <comment xml:lang="pl">Pusta płyta DVD</comment>
+ <comment xml:lang="pt">DVD vazio</comment>
+ <comment xml:lang="pt_BR">Disco DVD vazio</comment>
+ <comment xml:lang="ro">disc gol DVD</comment>
+ <comment xml:lang="ru">Чистый диск DVD</comment>
+ <comment xml:lang="sk">Prázdny disk DVD</comment>
+ <comment xml:lang="sl">prazen DVD disk</comment>
+ <comment xml:lang="sq">Disk bosh DVD</comment>
+ <comment xml:lang="sr">празан ДВД диск</comment>
+ <comment xml:lang="sv">tom dvd-skiva</comment>
+ <comment xml:lang="tr">boş DVD diski</comment>
+ <comment xml:lang="uk">порожній диск DVD</comment>
+ <comment xml:lang="vi">đĩa DVD trống</comment>
+ <comment xml:lang="zh_CN">空 DVD 光盘</comment>
+ <comment xml:lang="zh_TW">空白 DVD 光碟</comment>
+ </mime-type>
+
+ <mime-type type="x-content/blank-bd">
+
+ <comment>blank Blu-ray disc</comment>
+ <comment xml:lang="ar">قرص بلو-راي فارغ</comment>
+ <comment xml:lang="be@latin">čysty dysk Blu-ray</comment>
+ <comment xml:lang="bg">Blu-ray — празно</comment>
+ <comment xml:lang="ca">disc Blu-Ray en blanc</comment>
+ <comment xml:lang="cs">prázdný disk Blu-ray</comment>
+ <comment xml:lang="da">tom Blu-ray-disk</comment>
+ <comment xml:lang="de">Leere Blu-ray-Scheibe</comment>
+ <comment xml:lang="el">Κενό Blu-ray</comment>
+ <comment xml:lang="en_GB">blank Blu-ray disc</comment>
+ <comment xml:lang="es">disco Blu-ray en blanco</comment>
+ <comment xml:lang="eu">Blu-ray disko hutsa</comment>
+ <comment xml:lang="fi">tyhjä Blu-ray-levy</comment>
+ <comment xml:lang="fo">blankur Blu-ray diskur</comment>
+ <comment xml:lang="fr">disque Blu-Ray vierge</comment>
+ <comment xml:lang="ga">diosca folamh Blu-Ray</comment>
+ <comment xml:lang="gl">disco Blu-ray en branco</comment>
+ <comment xml:lang="he">תקליטור בלו־ריי ריק</comment>
+ <comment xml:lang="hr">Prazni Blu-ray disk</comment>
+ <comment xml:lang="hu">üres Blu-Ray lemez</comment>
+ <comment xml:lang="ia">Disco Bly-ray vacue</comment>
+ <comment xml:lang="id">cakram Blu-ray kosong</comment>
+ <comment xml:lang="it">Disco vuoto Blu-ray</comment>
+ <comment xml:lang="ja">ブランク Blu-ray ディスク</comment>
+ <comment xml:lang="kk">таза Blu-ray дискі</comment>
+ <comment xml:lang="ko">빈 블루레이 디스크</comment>
+ <comment xml:lang="lt">tuščias Blu-ray diskas</comment>
+ <comment xml:lang="lv">tukšs Blu-ray disks</comment>
+ <comment xml:lang="nl">blanco Blu-ray-disk</comment>
+ <comment xml:lang="nn">tom Blu-Ray-plate</comment>
+ <comment xml:lang="oc">disc Blu-Ray verge</comment>
+ <comment xml:lang="pl">Pusta płyta Blu-ray</comment>
+ <comment xml:lang="pt">Blu-Ray vazio</comment>
+ <comment xml:lang="pt_BR">Disco Blu-ray vazio</comment>
+ <comment xml:lang="ro">disc gol Blu-ray</comment>
+ <comment xml:lang="ru">Чистый диск Blu-ray</comment>
+ <comment xml:lang="sk">Prázdny disk Blu-ray</comment>
+ <comment xml:lang="sl">prazen Blu-Ray disk</comment>
+ <comment xml:lang="sq">Disk bosh Blu-ray</comment>
+ <comment xml:lang="sr">празан Блу-реј диск</comment>
+ <comment xml:lang="sv">tom Blu-ray-skiva</comment>
+ <comment xml:lang="tr">boş Blue-ray diski</comment>
+ <comment xml:lang="uk">порожній диск Blu-ray</comment>
+ <comment xml:lang="vi">đĩa Blu-ray trống</comment>
+ <comment xml:lang="zh_CN">空蓝光 DVD</comment>
+ <comment xml:lang="zh_TW">空白 Blu-ray 光碟</comment>
+ </mime-type>
+
+ <mime-type type="x-content/blank-hddvd">
+
+ <comment>blank HD DVD disc</comment>
+ <comment xml:lang="ar">قرص HD DVD فارغ</comment>
+ <comment xml:lang="be@latin">čysty dysk HD DVD</comment>
+ <comment xml:lang="bg">HD DVD — празно</comment>
+ <comment xml:lang="ca">disc HD-DVD en blanc</comment>
+ <comment xml:lang="cs">prázdný disk HD DVD</comment>
+ <comment xml:lang="da">tom HD dvd-disk</comment>
+ <comment xml:lang="de">Leere HD-DVD</comment>
+ <comment xml:lang="el">Κενό HD DVD</comment>
+ <comment xml:lang="en_GB">blank HD DVD disc</comment>
+ <comment xml:lang="es">disco HD DVD en blanco</comment>
+ <comment xml:lang="eu">HD DVD disko hutsa</comment>
+ <comment xml:lang="fi">tyhjä HD DVD -levy</comment>
+ <comment xml:lang="fo">blankur HD DVD diskur</comment>
+ <comment xml:lang="fr">disque HD-DVD vierge</comment>
+ <comment xml:lang="ga">HD DVD folamh</comment>
+ <comment xml:lang="gl">disco de HD DVD en branco</comment>
+ <comment xml:lang="he">דיסק HD DVD ריק</comment>
+ <comment xml:lang="hr">Prazni HD DVD disk</comment>
+ <comment xml:lang="hu">üres HD DVD-lemez</comment>
+ <comment xml:lang="ia">Disco HD DVD vacue</comment>
+ <comment xml:lang="id">cakram HD DVD kosong</comment>
+ <comment xml:lang="it">Disco vuoto DVD HD</comment>
+ <comment xml:lang="ja">ブランク HD DVD ディスク</comment>
+ <comment xml:lang="kk">таза HD DVD дискі</comment>
+ <comment xml:lang="ko">빈 HD DVD 디스크</comment>
+ <comment xml:lang="lt">tuščias HD DVD diskas</comment>
+ <comment xml:lang="lv">tukšs HD DVD disks</comment>
+ <comment xml:lang="nl">blanco HD-DVD</comment>
+ <comment xml:lang="nn">tom HD-DVD-plate</comment>
+ <comment xml:lang="oc">disc HD-DVD verge</comment>
+ <comment xml:lang="pl">Pusta płyta HD DVD</comment>
+ <comment xml:lang="pt">HD DVD vazio</comment>
+ <comment xml:lang="pt_BR">Disco HD DVD vazio</comment>
+ <comment xml:lang="ro">disc gol HD DVD</comment>
+ <comment xml:lang="ru">Чистый диск HD DVD</comment>
+ <comment xml:lang="sk">Prázdny disk HD DVD</comment>
+ <comment xml:lang="sl">prazen HD DVD disk</comment>
+ <comment xml:lang="sq">Disk bosh DVD HD</comment>
+ <comment xml:lang="sr">празан ХД ДВД диск</comment>
+ <comment xml:lang="sv">tom HD DVD-skiva</comment>
+ <comment xml:lang="tr">boş HD DVD diski</comment>
+ <comment xml:lang="uk">порожній диск HD DVD</comment>
+ <comment xml:lang="vi">đĩa DVD HD trống</comment>
+ <comment xml:lang="zh_CN">空 HD DVD 光盘</comment>
+ <comment xml:lang="zh_TW">空白 HD DVD 光碟</comment>
+ </mime-type>
+
+ <mime-type type="x-content/audio-dvd">
+
+ <comment>audio DVD</comment>
+ <comment xml:lang="ar">DVD سمعي</comment>
+ <comment xml:lang="be@latin">aŭdyjo DVD</comment>
+ <comment xml:lang="bg">DVD — аудио</comment>
+ <comment xml:lang="ca">DVD d'àudio</comment>
+ <comment xml:lang="cs">zvukové DVD</comment>
+ <comment xml:lang="da">lyd-dvd</comment>
+ <comment xml:lang="de">Audio-DVD</comment>
+ <comment xml:lang="el">DVD ήχου</comment>
+ <comment xml:lang="en_GB">audio DVD</comment>
+ <comment xml:lang="eo">Son-DVD</comment>
+ <comment xml:lang="es">DVD de sonido</comment>
+ <comment xml:lang="eu">audio DVDa</comment>
+ <comment xml:lang="fi">ääni-DVD</comment>
+ <comment xml:lang="fo">Ljóð DVD</comment>
+ <comment xml:lang="fr">DVD audio</comment>
+ <comment xml:lang="ga">DVD fuaime</comment>
+ <comment xml:lang="gl">DVD de son</comment>
+ <comment xml:lang="he">DVD שמע</comment>
+ <comment xml:lang="hr">Glazbeni DVD</comment>
+ <comment xml:lang="hu">hang DVD</comment>
+ <comment xml:lang="ia">DVD audio</comment>
+ <comment xml:lang="id">DVD audio</comment>
+ <comment xml:lang="it">DVD audio</comment>
+ <comment xml:lang="ja">オーディオ DVD</comment>
+ <comment xml:lang="kk">аудио DVD</comment>
+ <comment xml:lang="ko">오디오 DVD</comment>
+ <comment xml:lang="lt">garso DVD</comment>
+ <comment xml:lang="lv">audio DVD</comment>
+ <comment xml:lang="nl">audio-DVD</comment>
+ <comment xml:lang="nn">lyd-DVD</comment>
+ <comment xml:lang="oc">DVD àudio</comment>
+ <comment xml:lang="pl">DVD-Audio</comment>
+ <comment xml:lang="pt">DVD áudio</comment>
+ <comment xml:lang="pt_BR">DVD de áudio</comment>
+ <comment xml:lang="ro">DVD audio</comment>
+ <comment xml:lang="ru">Аудио DVD</comment>
+ <comment xml:lang="sk">Zvukové DVD</comment>
+ <comment xml:lang="sl">zvočni DVD</comment>
+ <comment xml:lang="sq">DVD audio</comment>
+ <comment xml:lang="sr">звучни ДВД</comment>
+ <comment xml:lang="sv">ljud-dvd</comment>
+ <comment xml:lang="tr">Müzik DVD'si</comment>
+ <comment xml:lang="uk">звуковий DVD</comment>
+ <comment xml:lang="vi">đĩa DVD âm thanh</comment>
+ <comment xml:lang="zh_CN">音频 DVD</comment>
+ <comment xml:lang="zh_TW">音訊 DVD</comment>
+ <treemagic>
+ <treematch type="file" path="AUDIO_TS/AUDIO_TS.IFO"/>
+ <treematch type="file" path="AUDIO_TS/AUDIO_TS.IFO;1"/>
+ </treemagic>
+ </mime-type>
+
+ <mime-type type="x-content/video-bluray">
+
+ <comment>Blu-ray video disc</comment>
+ <comment xml:lang="ar">قرص بلو-راي مرئي</comment>
+ <comment xml:lang="ast">Discu Blu-ray de videu</comment>
+ <comment xml:lang="be@latin">Videadysk Blu-ray</comment>
+ <comment xml:lang="bg">Blu-ray — видео</comment>
+ <comment xml:lang="ca">disc de vídeo Blu-Ray</comment>
+ <comment xml:lang="cs">videodisk Blu-ray</comment>
+ <comment xml:lang="da">Blu-ray video-disk</comment>
+ <comment xml:lang="de">Blu-ray-Videoscheibe</comment>
+ <comment xml:lang="el">Δίσκος βίντεο Blu-ray</comment>
+ <comment xml:lang="en_GB">Blu-ray video disc</comment>
+ <comment xml:lang="es">disco de vídeo Blu-ray</comment>
+ <comment xml:lang="eu">Blu-ray bideo-diskoa</comment>
+ <comment xml:lang="fi">Blu-ray-videolevy</comment>
+ <comment xml:lang="fo">Blu-ray diskur</comment>
+ <comment xml:lang="fr">disque vidéo Blu-Ray</comment>
+ <comment xml:lang="ga">diosca físe Blu-Ray</comment>
+ <comment xml:lang="gl">disco de vídeo Blu-ray</comment>
+ <comment xml:lang="he">תקליטור וידאו מסוג בלו־ריי</comment>
+ <comment xml:lang="hr">Blu-ray video disk</comment>
+ <comment xml:lang="hu">Blu-ray videolemez</comment>
+ <comment xml:lang="ia">Disco video Blu-ray</comment>
+ <comment xml:lang="id">Cakram video Blu-ray</comment>
+ <comment xml:lang="it">Disco video Blu-ray</comment>
+ <comment xml:lang="ja">Blu-ray ビデオディスク</comment>
+ <comment xml:lang="ka">Blu-ray ვიდეო დისკი</comment>
+ <comment xml:lang="kk">Blu-ray видео дискі</comment>
+ <comment xml:lang="ko">블루레이 동영상 디스크</comment>
+ <comment xml:lang="lt">Blu-ray vaizdo diskas</comment>
+ <comment xml:lang="lv">Blu-ray video disks</comment>
+ <comment xml:lang="nl">Blu-ray-videodisk</comment>
+ <comment xml:lang="nn">Blu-Ray videoplate</comment>
+ <comment xml:lang="oc">disc vidèo Blu-Ray</comment>
+ <comment xml:lang="pl">Płyta wideo Blu-ray</comment>
+ <comment xml:lang="pt">Blu-ray de vídeo</comment>
+ <comment xml:lang="pt_BR">Disco de vídeo Blu-ray</comment>
+ <comment xml:lang="ro">Disc video Blu-ray</comment>
+ <comment xml:lang="ru">Видеодиск Blu-ray</comment>
+ <comment xml:lang="sk">Videodisk Blu-ray</comment>
+ <comment xml:lang="sl">Blu-ray video disk</comment>
+ <comment xml:lang="sq">Disk video Blu-ray</comment>
+ <comment xml:lang="sr">Блу-реј видео диск</comment>
+ <comment xml:lang="sv">Blu-ray-videoskiva</comment>
+ <comment xml:lang="tr">Blu-ray video diski</comment>
+ <comment xml:lang="uk">відеодиск Blu-ray</comment>
+ <comment xml:lang="vi">Đĩa ảnh động Blu-ray</comment>
+ <comment xml:lang="zh_CN">蓝光视频光盘</comment>
+ <comment xml:lang="zh_TW">Blu-ray 視訊光碟</comment>
+ <treemagic>
+ <treematch type="directory" path="BDAV" non-empty="true"/>
+ <treematch type="directory" path="BDMV" non-empty="true"/>
+ </treemagic>
+ </mime-type>
+
+ <mime-type type="x-content/video-hddvd">
+
+ <comment>HD DVD video disc</comment>
+ <comment xml:lang="ar">قرص HD DVD مرئي</comment>
+ <comment xml:lang="ast">Discu HD DVD de videu</comment>
+ <comment xml:lang="be@latin">Videadysk HD DVD</comment>
+ <comment xml:lang="bg">HD DVD — видео</comment>
+ <comment xml:lang="ca">disc de vídeo HD-DVD</comment>
+ <comment xml:lang="cs">Videodisk HD DVD</comment>
+ <comment xml:lang="da">HD DVD-videodisk</comment>
+ <comment xml:lang="de">HD-DVD-Videoscheibe</comment>
+ <comment xml:lang="el">Δίσκος βίντεο HD DVD</comment>
+ <comment xml:lang="en_GB">HD DVD video disc</comment>
+ <comment xml:lang="es">disco de vídeo HD DVD</comment>
+ <comment xml:lang="eu">HD DVD bideo-diskoa</comment>
+ <comment xml:lang="fi">HD DVD -videolevy</comment>
+ <comment xml:lang="fo">HD DVD video diskur</comment>
+ <comment xml:lang="fr">disque vidéo HD DVD</comment>
+ <comment xml:lang="ga">diosca físe HD DVD</comment>
+ <comment xml:lang="gl">disco de vídeo HD DVD</comment>
+ <comment xml:lang="he">תקליטור וידאו HD DVD</comment>
+ <comment xml:lang="hr">HD DVD video disk</comment>
+ <comment xml:lang="hu">HD DVD videolemez</comment>
+ <comment xml:lang="ia">Disco video HD DVD</comment>
+ <comment xml:lang="id">Cakram video HD DVD</comment>
+ <comment xml:lang="it">Disco video DVD HD</comment>
+ <comment xml:lang="ja">HD DVD ビデオディスク</comment>
+ <comment xml:lang="kk">HD DVD видео дискі</comment>
+ <comment xml:lang="ko">HD DVD 동영상 디스크</comment>
+ <comment xml:lang="lt">HD DVD vaizdo diskas</comment>
+ <comment xml:lang="lv">HD DVD video disks</comment>
+ <comment xml:lang="nl">HD-DVD-videodisk</comment>
+ <comment xml:lang="nn">HD-DVD-videodisk</comment>
+ <comment xml:lang="oc">disc vidèo HD DVD</comment>
+ <comment xml:lang="pl">Płyta wideo HD DVD</comment>
+ <comment xml:lang="pt">HD DVD de vídeo</comment>
+ <comment xml:lang="pt_BR">Disco de vídeo HD DVD</comment>
+ <comment xml:lang="ro">Disc video HD DVD</comment>
+ <comment xml:lang="ru">Видеодиск HD DVD</comment>
+ <comment xml:lang="sk">Videodisk HD DVD</comment>
+ <comment xml:lang="sl">HD DVD video disk</comment>
+ <comment xml:lang="sq">Disk video DVD HD</comment>
+ <comment xml:lang="sr">ХД ДВД видео диск</comment>
+ <comment xml:lang="sv">HD DVD-videoskiva</comment>
+ <comment xml:lang="tr">HD DVD vidyo diski</comment>
+ <comment xml:lang="uk">відеодиск HD DVD</comment>
+ <comment xml:lang="vi">Đĩa ảnh động DVD HD</comment>
+ <comment xml:lang="zh_CN">HD DVD 视频光盘</comment>
+ <comment xml:lang="zh_TW">HD DVD 視訊光碟</comment>
+ <treemagic>
+ <treematch type="file" path="HVDVD_TS/HV000I01.IFO"/>
+ <treematch type="file" path="HVDVD_TS/HV001I01.IFO"/>
+ <treematch type="file" path="HVDVD_TS/HVA00001.VTI"/>
+ </treemagic>
+ </mime-type>
+
+ <mime-type type="x-content/ebook-reader">
+
+ <comment>e-book reader</comment>
+ <comment xml:lang="bg">Четец на е-книги</comment>
+ <comment xml:lang="ca">lector de llibres electrònics</comment>
+ <comment xml:lang="cs">čtečka elektronických knih</comment>
+ <comment xml:lang="da">e-bogslæser</comment>
+ <comment xml:lang="de">E-Book-Leser</comment>
+ <comment xml:lang="el">Αναγνώστης ηλεκτρονικών βιβλίων</comment>
+ <comment xml:lang="en_GB">e-book reader</comment>
+ <comment xml:lang="es">lector de libros electrónicos</comment>
+ <comment xml:lang="eu">e-book irakurlea</comment>
+ <comment xml:lang="fi">e-kirjan lukulaite</comment>
+ <comment xml:lang="fr">lecteur de livre numérique</comment>
+ <comment xml:lang="ga">léitheoir r-leabhair</comment>
+ <comment xml:lang="gl">lector de libros electrónicos</comment>
+ <comment xml:lang="he">קורא ספרים אלקטרוניים</comment>
+ <comment xml:lang="hr">Čitač e-knjiga</comment>
+ <comment xml:lang="hu">e-könyvolvasó</comment>
+ <comment xml:lang="ia">Lector de libro electronic</comment>
+ <comment xml:lang="id">Pembaca e-book</comment>
+ <comment xml:lang="it">Lettore e-book</comment>
+ <comment xml:lang="ja">電子書籍リーダー</comment>
+ <comment xml:lang="kk">электронды кітаптарды оқу құрылғысы</comment>
+ <comment xml:lang="ko">전자책 리더</comment>
+ <comment xml:lang="lv">e-grāmatu lasītājs</comment>
+ <comment xml:lang="nl">e-book reader</comment>
+ <comment xml:lang="oc">lector de libre numeric</comment>
+ <comment xml:lang="pl">Czytnik e-booków</comment>
+ <comment xml:lang="pt">leitor de ebooks</comment>
+ <comment xml:lang="pt_BR">Leitor de e-book</comment>
+ <comment xml:lang="ru">Устройство для чтения электронных книг</comment>
+ <comment xml:lang="sk">Čítačka e-kníh</comment>
+ <comment xml:lang="sl">Bralnik elektronskih knjig</comment>
+ <comment xml:lang="sr">читач ел. књига</comment>
+ <comment xml:lang="sv">e-bokläsare</comment>
+ <comment xml:lang="tr">e-kitap okuyucu</comment>
+ <comment xml:lang="uk">пристрій для читання електронних книг</comment>
+ <comment xml:lang="zh_CN">电子书阅读器</comment>
+ <comment xml:lang="zh_TW">e-book 閱讀器</comment>
+ <treemagic>
+ <treematch type="directory" path=".kobo" non-empty="true"/>
+ <treematch path="system/com.amazon.ebook.booklet.reader" non-empty="false"/>
+ </treemagic>
+ </mime-type>
+
+ <mime-type type="x-content/image-picturecd">
+
+ <comment>Picture CD</comment>
+ <comment xml:lang="ar">Picture CD</comment>
+ <comment xml:lang="be@latin">Picture CD</comment>
+ <comment xml:lang="bg">CD — изображения</comment>
+ <comment xml:lang="ca">Picture CD</comment>
+ <comment xml:lang="cs">Picture CD</comment>
+ <comment xml:lang="da">Billedcd</comment>
+ <comment xml:lang="de">Picture CD</comment>
+ <comment xml:lang="el">CD εικόνων</comment>
+ <comment xml:lang="en_GB">Picture CD</comment>
+ <comment xml:lang="es">Picture CD</comment>
+ <comment xml:lang="eu">Picture CD</comment>
+ <comment xml:lang="fi">Picture CD</comment>
+ <comment xml:lang="fo">Picture CD</comment>
+ <comment xml:lang="fr">CD Picture</comment>
+ <comment xml:lang="ga">dlúthdhiosca grianghraf</comment>
+ <comment xml:lang="gl">Picture CD</comment>
+ <comment xml:lang="he">תקליטור תמונות</comment>
+ <comment xml:lang="hr">Slikovni CD</comment>
+ <comment xml:lang="hu">Picture CD</comment>
+ <comment xml:lang="ia">Disco Picture CD</comment>
+ <comment xml:lang="id">CD Gambar</comment>
+ <comment xml:lang="it">Picture CD</comment>
+ <comment xml:lang="ja">ピクチャー CD</comment>
+ <comment xml:lang="kk">Picture CD</comment>
+ <comment xml:lang="ko">Picture CD</comment>
+ <comment xml:lang="lt">Paveikslėlių CD</comment>
+ <comment xml:lang="lv">Attēlu CD</comment>
+ <comment xml:lang="nl">foto-CD</comment>
+ <comment xml:lang="nn">Bilete-CD</comment>
+ <comment xml:lang="oc">CD Picture</comment>
+ <comment xml:lang="pl">Picture CD</comment>
+ <comment xml:lang="pt">Picture CD</comment>
+ <comment xml:lang="pt_BR">CD de Fotos</comment>
+ <comment xml:lang="ro">CD cu fotografii</comment>
+ <comment xml:lang="ru">Picture CD</comment>
+ <comment xml:lang="sk">Picture CD</comment>
+ <comment xml:lang="sl">Slikovni CD</comment>
+ <comment xml:lang="sq">Picture CD</comment>
+ <comment xml:lang="sr">ЦД са сликама</comment>
+ <comment xml:lang="sv">Picture CD</comment>
+ <comment xml:lang="tr">Resim CD'si</comment>
+ <comment xml:lang="uk">CD з зображеннями</comment>
+ <comment xml:lang="vi">Đĩa CD ảnh</comment>
+ <comment xml:lang="zh_CN">柯达 Picture CD</comment>
+ <comment xml:lang="zh_TW">圖片 CD</comment>
+ <treemagic>
+ <treematch type="directory" path="PICTURES" non-empty="true" match-case="true"/>
+ </treemagic>
+ </mime-type>
+
+ <mime-type type="x-content/audio-player">
+
+ <comment>portable audio player</comment>
+ <comment xml:lang="ar">مشغل الملفات المسموعة المحمولة</comment>
+ <comment xml:lang="be@latin">pieranosny aŭdyjoplayer</comment>
+ <comment xml:lang="bg">Преносим аудио плеър</comment>
+ <comment xml:lang="ca">reproductor d'àudio portàtil</comment>
+ <comment xml:lang="cs">přenosný zvukový přehrávač</comment>
+ <comment xml:lang="da">bærbar lydafspiller</comment>
+ <comment xml:lang="de">Portables Audio-Wiedergabegerät</comment>
+ <comment xml:lang="el">Φορητός αναπαραγωγέας μουσικής</comment>
+ <comment xml:lang="en_GB">portable audio player</comment>
+ <comment xml:lang="es">dispositivo de sonido portátil</comment>
+ <comment xml:lang="eu">audio erreproduzigailu eramangarria</comment>
+ <comment xml:lang="fi">siirrettävä äänisoitin</comment>
+ <comment xml:lang="fo">leysur ljóðavspælari</comment>
+ <comment xml:lang="fr">lecteur audio portable</comment>
+ <comment xml:lang="ga">seinnteoir iniompartha fuaime</comment>
+ <comment xml:lang="gl">dispositivo de son portábel</comment>
+ <comment xml:lang="he">נגן מוזיקה נייד</comment>
+ <comment xml:lang="hr">Prenosivi glazbeni svirač</comment>
+ <comment xml:lang="hu">hordozható zenelejátszó</comment>
+ <comment xml:lang="ia">Lector audio portabile</comment>
+ <comment xml:lang="id">pemutar audio portable</comment>
+ <comment xml:lang="it">Lettore audio portabile</comment>
+ <comment xml:lang="ja">ポータブルオーディオプレイヤー</comment>
+ <comment xml:lang="kk">тасымалы аудио плеер</comment>
+ <comment xml:lang="ko">휴대용 오디오 재생기</comment>
+ <comment xml:lang="lt">nešiojamasis garso leistuvas</comment>
+ <comment xml:lang="lv">portatīvais audio atskaņotājs</comment>
+ <comment xml:lang="nl">draagbare audiospeler</comment>
+ <comment xml:lang="nn">portable audio layer</comment>
+ <comment xml:lang="oc">lector àudio portable</comment>
+ <comment xml:lang="pl">Przenośny odtwarzacz dźwięku</comment>
+ <comment xml:lang="pt">reprodutor áudio portátil</comment>
+ <comment xml:lang="pt_BR">Reprodutor de áudio portátil</comment>
+ <comment xml:lang="ro">player audio portabil</comment>
+ <comment xml:lang="ru">Портативный аудиопроигрыватель</comment>
+ <comment xml:lang="sk">Prenosný hudobný prehrávač</comment>
+ <comment xml:lang="sl">prenosni predvajalnik zvoka</comment>
+ <comment xml:lang="sq">Lexues audio portativ</comment>
+ <comment xml:lang="sr">преносна музичка справица</comment>
+ <comment xml:lang="sv">bärbar ljudspelare</comment>
+ <comment xml:lang="tr">taşınabilir ses oynatıcısı</comment>
+ <comment xml:lang="uk">портативний аудіопрогравач</comment>
+ <comment xml:lang="vi">bộ phát nhạc di động</comment>
+ <comment xml:lang="zh_CN">便携式音频播放器</comment>
+ <comment xml:lang="zh_TW">可攜式音訊播放程式</comment>
+ </mime-type>
+
+ <mime-type type="x-content/software">
+
+ <comment>software</comment>
+ <comment xml:lang="ar">برنامج</comment>
+ <comment xml:lang="be@latin">prahrama</comment>
+ <comment xml:lang="bg">Софтуер</comment>
+ <comment xml:lang="ca">programari</comment>
+ <comment xml:lang="cs">software</comment>
+ <comment xml:lang="da">software</comment>
+ <comment xml:lang="de">Software</comment>
+ <comment xml:lang="el">Λογισμικό</comment>
+ <comment xml:lang="en_GB">software</comment>
+ <comment xml:lang="es">software</comment>
+ <comment xml:lang="eu">softwarea</comment>
+ <comment xml:lang="fi">ohjelmisto</comment>
+ <comment xml:lang="fo">ritbúnaður</comment>
+ <comment xml:lang="fr">logiciel</comment>
+ <comment xml:lang="ga">bogearraí</comment>
+ <comment xml:lang="gl">software</comment>
+ <comment xml:lang="he">תכנה</comment>
+ <comment xml:lang="hr">Softver</comment>
+ <comment xml:lang="hu">szoftver</comment>
+ <comment xml:lang="ia">Software</comment>
+ <comment xml:lang="id">peranti lunak</comment>
+ <comment xml:lang="it">Software</comment>
+ <comment xml:lang="ja">ソフトウェア</comment>
+ <comment xml:lang="ka">პროგრამული უზრუნველყოფა</comment>
+ <comment xml:lang="kk">бағдарламалық қамтама</comment>
+ <comment xml:lang="ko">소프트웨어</comment>
+ <comment xml:lang="lt">programinė įranga</comment>
+ <comment xml:lang="lv">programmatūra</comment>
+ <comment xml:lang="nl">software</comment>
+ <comment xml:lang="nn">programvare</comment>
+ <comment xml:lang="oc">logicial</comment>
+ <comment xml:lang="pl">Oprogramowanie</comment>
+ <comment xml:lang="pt">programa</comment>
+ <comment xml:lang="pt_BR">Aplicativo</comment>
+ <comment xml:lang="ro">software</comment>
+ <comment xml:lang="ru">Программное обеспечение</comment>
+ <comment xml:lang="sk">Softvér</comment>
+ <comment xml:lang="sl">programska oprema</comment>
+ <comment xml:lang="sq">Software</comment>
+ <comment xml:lang="sr">софтвер</comment>
+ <comment xml:lang="sv">programvara</comment>
+ <comment xml:lang="tr">yazılım</comment>
+ <comment xml:lang="uk">програмне забезпечення</comment>
+ <comment xml:lang="vi">phần mềm</comment>
+ <comment xml:lang="zh_CN">软件</comment>
+ <comment xml:lang="zh_TW">軟體</comment>
+ </mime-type>
+
+ <mime-type type="x-content/unix-software">
+
+ <comment>UNIX software</comment>
+ <comment xml:lang="ar">برنامج يونكس</comment>
+ <comment xml:lang="bg">Софтуер за UNIX</comment>
+ <comment xml:lang="ca">programari d'UNIX</comment>
+ <comment xml:lang="cs">software systému UNIX</comment>
+ <comment xml:lang="da">UNIX-programmer</comment>
+ <comment xml:lang="de">UNIX-Software</comment>
+ <comment xml:lang="el">Λογισμικό UNIX</comment>
+ <comment xml:lang="en_GB">UNIX software</comment>
+ <comment xml:lang="es">software de UNIX</comment>
+ <comment xml:lang="eu">UNIXeko softwarea</comment>
+ <comment xml:lang="fi">UNIX-ohjelmisto</comment>
+ <comment xml:lang="fo">UNIX ritbúnaður</comment>
+ <comment xml:lang="fr">logiciel UNIX</comment>
+ <comment xml:lang="ga">bogearraí UNIX</comment>
+ <comment xml:lang="gl">Software de UNIX</comment>
+ <comment xml:lang="he">תכנה ל־UNIX</comment>
+ <comment xml:lang="hr">UNIX softver</comment>
+ <comment xml:lang="hu">UNIX-szoftver</comment>
+ <comment xml:lang="ia">Software pro UNIX</comment>
+ <comment xml:lang="id">Peranti lunak UNIX</comment>
+ <comment xml:lang="it">Software UNIX</comment>
+ <comment xml:lang="ja">UNIX ソフトウェア</comment>
+ <comment xml:lang="kk">UNIX бағдарламасы</comment>
+ <comment xml:lang="ko">UNIX 소프트웨어</comment>
+ <comment xml:lang="lt">UNIX programinė įranga</comment>
+ <comment xml:lang="lv">UNIX programmatūra</comment>
+ <comment xml:lang="nl">UNIX software</comment>
+ <comment xml:lang="oc">logicial UNIX</comment>
+ <comment xml:lang="pl">Oprogramowanie systemu UNIX</comment>
+ <comment xml:lang="pt">programa UNIX</comment>
+ <comment xml:lang="pt_BR">Aplicativo UNIX</comment>
+ <comment xml:lang="ro">Software UNIX</comment>
+ <comment xml:lang="ru">Программа UNIX</comment>
+ <comment xml:lang="sk">Softvér UNIX</comment>
+ <comment xml:lang="sl">Programska datoteka UNIX</comment>
+ <comment xml:lang="sr">ЈУНИКС-ов софтвер</comment>
+ <comment xml:lang="sv">UNIX-programvara</comment>
+ <comment xml:lang="tr">UNIX yazılımı</comment>
+ <comment xml:lang="uk">програмне забезпечення UNIX</comment>
+ <comment xml:lang="zh_CN">UNIX 软件</comment>
+ <comment xml:lang="zh_TW">UNIX 軟體</comment>
+ <sub-class-of type="x-content/software"/>
+ <treemagic>
+ <treematch type="file" path=".autorun" match-case="true"/>
+ <treematch type="file" path="autorun" match-case="true"/>
+ <treematch type="file" path="autorun.sh" match-case="true"/>
+ </treemagic>
+ </mime-type>
+
+ <mime-type type="x-content/win32-software">
+
+ <comment>Windows software</comment>
+ <comment xml:lang="ar">برنامج ويندوز</comment>
+ <comment xml:lang="bg">Софтуер — Windows</comment>
+ <comment xml:lang="ca">programari de Windows</comment>
+ <comment xml:lang="cs">software systému Windows</comment>
+ <comment xml:lang="da">Windowsprogram</comment>
+ <comment xml:lang="de">Windows-Software</comment>
+ <comment xml:lang="el">Λογισμικό Windows</comment>
+ <comment xml:lang="en_GB">Windows software</comment>
+ <comment xml:lang="es">software de Windows</comment>
+ <comment xml:lang="eu">Windows-eko softwarea</comment>
+ <comment xml:lang="fi">Windows-ohjelmisto</comment>
+ <comment xml:lang="fo">Windows ritbúnaður</comment>
+ <comment xml:lang="fr">logiciel Windows</comment>
+ <comment xml:lang="ga">bogearraí Windows</comment>
+ <comment xml:lang="gl">Software de Windows</comment>
+ <comment xml:lang="he">תכנה ל־Windows</comment>
+ <comment xml:lang="hr">Windows softver</comment>
+ <comment xml:lang="hu">Windows-szoftver</comment>
+ <comment xml:lang="ia">Software Windows</comment>
+ <comment xml:lang="id">Piranti lunak Windows</comment>
+ <comment xml:lang="it">Software Windows</comment>
+ <comment xml:lang="ja">Windows ソフトウェア</comment>
+ <comment xml:lang="kk">Windows бағдарламасы</comment>
+ <comment xml:lang="ko">Windows 소프트웨어</comment>
+ <comment xml:lang="lt">Windows programinė įranga</comment>
+ <comment xml:lang="lv">Windows programmatūra</comment>
+ <comment xml:lang="nl">Windows software</comment>
+ <comment xml:lang="oc">logicial Windows</comment>
+ <comment xml:lang="pl">Oprogramowanie systemu Windows</comment>
+ <comment xml:lang="pt">programa Windows</comment>
+ <comment xml:lang="pt_BR">Programa do Windows</comment>
+ <comment xml:lang="ro">Software Windows</comment>
+ <comment xml:lang="ru">Программа Windows</comment>
+ <comment xml:lang="sk">Softvér Windows</comment>
+ <comment xml:lang="sl">Programska oprema za okolje Windows</comment>
+ <comment xml:lang="sr">Виндоузов софтвер</comment>
+ <comment xml:lang="sv">Windows-program</comment>
+ <comment xml:lang="tr">Windows yazılımı</comment>
+ <comment xml:lang="uk">програмне забезпечення Windows</comment>
+ <comment xml:lang="zh_CN">Windows 软件</comment>
+ <comment xml:lang="zh_TW">Windows 軟體</comment>
+ <sub-class-of type="x-content/software"/>
+ <treemagic>
+ <treematch type="file" path="autorun.exe" executable="true"/>
+ <treematch type="file" path="autorun.inf"/>
+ </treemagic>
+ </mime-type>
+
+ <mime-type type="application/trig">
+ <comment>TriG RDF document</comment>
+ <comment xml:lang="ast">Documentu RDF TriG</comment>
+ <comment xml:lang="ca">document TriG RDF</comment>
+ <comment xml:lang="cs">dokument Trig RDF</comment>
+ <comment xml:lang="da">TriG RDF-dokument</comment>
+ <comment xml:lang="de">TriG-RDF-Dokument</comment>
+ <comment xml:lang="el">Έγγραφο TriG RDF</comment>
+ <comment xml:lang="en_GB">TriG RDF document</comment>
+ <comment xml:lang="es">documento RDF de TriG</comment>
+ <comment xml:lang="eu">TriG RDF dokumentua</comment>
+ <comment xml:lang="fi">TriG RDF -asiakirja</comment>
+ <comment xml:lang="fr">document RDF TriG</comment>
+ <comment xml:lang="ga">cáipéis RDF TriG</comment>
+ <comment xml:lang="gl">Documento RDF TriG</comment>
+ <comment xml:lang="he">מסמך RDF של TriG</comment>
+ <comment xml:lang="hr">TriG RDF dokument</comment>
+ <comment xml:lang="hu">TriG RDF dokumentum</comment>
+ <comment xml:lang="ia">Documento TriG RDF</comment>
+ <comment xml:lang="id">Dokumen TriG RDF</comment>
+ <comment xml:lang="it">Documento TriG RDF</comment>
+ <comment xml:lang="kk">TriG RDF құжаты</comment>
+ <comment xml:lang="ko">TriG RDF 문서</comment>
+ <comment xml:lang="oc">document RDF TriG</comment>
+ <comment xml:lang="pl">Dokument RDF TriG</comment>
+ <comment xml:lang="pt">documento TriG RDF</comment>
+ <comment xml:lang="pt_BR">Documento RDF do TriG</comment>
+ <comment xml:lang="ru">Документ TriG RDF</comment>
+ <comment xml:lang="sk">RDF dokument TriG</comment>
+ <comment xml:lang="sl">Dokument TriG RDF</comment>
+ <comment xml:lang="sr">ТриГ РДФ документ</comment>
+ <comment xml:lang="sv">TriG RDF-dokument</comment>
+ <comment xml:lang="tr">TriG RDF belgesi</comment>
+ <comment xml:lang="uk">документ RDF TriG</comment>
+ <comment xml:lang="zh_CN">TriG RDF 文档</comment>
+ <comment xml:lang="zh_TW">TriG RDF 文件</comment>
+ <acronym>TriG</acronym>
+ <expanded-acronym>TriG RDF Graph Triple Language</expanded-acronym>
+ <sub-class-of type="text/plain"/>
+ <glob pattern="*.trig"/>
+ <alias type="application/x-trig"/>
+ </mime-type>
+
+ <mime-type type="application/x-iwork-keynote-sffkey">
+ <comment>Apple Keynote 5 presentation</comment>
+ <comment xml:lang="ca">presentació Keynote 5 d'Apple</comment>
+ <comment xml:lang="cs">prezentace Apple Keynote 5</comment>
+ <comment xml:lang="da">Apple Keynote 5-præsentation</comment>
+ <comment xml:lang="de">Apple-Keynote-5-Präsentation</comment>
+ <comment xml:lang="el">Παρουσίαση Apple Keynote 5</comment>
+ <comment xml:lang="en_GB">Apple Keynote 5 presentation</comment>
+ <comment xml:lang="es">presentación de Apple Keynote 5</comment>
+ <comment xml:lang="eu">Apple Keynote 5 aurkezpena</comment>
+ <comment xml:lang="fi">Apple Keynote 5 -esitys</comment>
+ <comment xml:lang="fr">présentation Apple Keynote 5</comment>
+ <comment xml:lang="ga">láithreoireacht Apple Keynote 5</comment>
+ <comment xml:lang="gl">Presentación de Apple Keynote 5</comment>
+ <comment xml:lang="he">מצגת Apple Keynote 5</comment>
+ <comment xml:lang="hr">Apple Keynote 5 prezentacija</comment>
+ <comment xml:lang="hu">Apple Keynote 5 prezentáció</comment>
+ <comment xml:lang="ia">Presentation Apple Keynote 5</comment>
+ <comment xml:lang="id">Presentasi Apple Keynote 5</comment>
+ <comment xml:lang="it">Presentazione Apple Keynote 5</comment>
+ <comment xml:lang="kk">Apple Keynote 5 презентациясы</comment>
+ <comment xml:lang="ko">Apple 키노트 5 프레젠테이션</comment>
+ <comment xml:lang="oc">presentacion Apple Keynote 5</comment>
+ <comment xml:lang="pl">Prezentacja Apple Keynote 5</comment>
+ <comment xml:lang="pt">apresentação Apple Keynote 5</comment>
+ <comment xml:lang="pt_BR">Apresentação do Apple Keynote 5</comment>
+ <comment xml:lang="ru">Презентация Apple Keynote 5</comment>
+ <comment xml:lang="sk">Prezentácia Apple Keynote 5</comment>
+ <comment xml:lang="sl">Predstavitev Apple Keynote 5</comment>
+ <comment xml:lang="sr">презентација Епл Кинота 5</comment>
+ <comment xml:lang="sv">Apple Keynote 5-presentation</comment>
+ <comment xml:lang="tr">Apple Keynote 5 sunumu</comment>
+ <comment xml:lang="uk">презентація Apple Keynote 5</comment>
+ <comment xml:lang="zh_CN">Apple Keynote 5 演示文稿</comment>
+ <comment xml:lang="zh_TW">Apple Keynote 5 簡報</comment>
+ <sub-class-of type="application/zip"/>
+ <generic-icon name="x-office-presentation"/>
+ <magic priority="70">
+ <match value="PK\003\004" type="string" offset="0">
+ <match value="index.apxl" type="string" offset="30"/>
+ </match>
+ </magic>
+ <glob pattern="*.key"/>
+ <alias type="application/vnd.apple.keynote"/>
+ </mime-type>
+
+ <mime-type type="application/x-pagemaker">
+ <comment>Adobe PageMaker</comment>
+ <comment xml:lang="ca">Adobe PageMaker</comment>
+ <comment xml:lang="cs">Adobe PageMaker</comment>
+ <comment xml:lang="da">Adobe PageMaker</comment>
+ <comment xml:lang="de">Adobe PageMaker</comment>
+ <comment xml:lang="el">Adobe PageMaker</comment>
+ <comment xml:lang="en_GB">Adobe PageMaker</comment>
+ <comment xml:lang="es">Adobe PageMaker</comment>
+ <comment xml:lang="eu">Adobe PageMaker</comment>
+ <comment xml:lang="fi">Adobe PageMaker</comment>
+ <comment xml:lang="fr">Adobe PageMaker</comment>
+ <comment xml:lang="ga">Adobe PageMaker</comment>
+ <comment xml:lang="gl">Adobe PageMaker</comment>
+ <comment xml:lang="he">Adobe PageMaker</comment>
+ <comment xml:lang="hr">Adobe PageMaker</comment>
+ <comment xml:lang="hu">Adobe PageMaker</comment>
+ <comment xml:lang="ia">Adobe PageMaker</comment>
+ <comment xml:lang="id">Adobe PageMaker</comment>
+ <comment xml:lang="it">Adobe PageMaker</comment>
+ <comment xml:lang="kk">Adobe PageMaker</comment>
+ <comment xml:lang="ko">Adobe 페이지메이커</comment>
+ <comment xml:lang="oc">Adobe PageMaker</comment>
+ <comment xml:lang="pl">Adobe PageMaker</comment>
+ <comment xml:lang="pt">Adobe PageMaker</comment>
+ <comment xml:lang="pt_BR">Adobe PageMaker</comment>
+ <comment xml:lang="ru">Adobe PageMaker</comment>
+ <comment xml:lang="sk">Adobe PageMaker</comment>
+ <comment xml:lang="sl">Dokument Adobe PageMaker</comment>
+ <comment xml:lang="sr">Адобе Пејџ Мејкер</comment>
+ <comment xml:lang="sv">Adobe PageMaker</comment>
+ <comment xml:lang="tr">Adobe PageMaker</comment>
+ <comment xml:lang="uk">Adobe PageMaker</comment>
+ <comment xml:lang="zh_CN">Adobe PageMaker</comment>
+ <comment xml:lang="zh_TW">Adobe PageMaker</comment>
+ <sub-class-of type="application/x-ole-storage"/>
+ <generic-icon name="x-office-document"/>
+ <glob pattern="*.p65"/>
+ <glob pattern="*.pm"/>
+ <glob pattern="*.pm6"/>
+ <glob pattern="*.pmd"/>
+ </mime-type>
+
+ <mime-type type="application/x-doom-wad">
+ <comment>Doom WAD</comment>
+ <comment xml:lang="ca">WAD de Doom</comment>
+ <comment xml:lang="cs">datový balík WAD hry Doom</comment>
+ <comment xml:lang="da">Doom WAD</comment>
+ <comment xml:lang="de">Doom WAD</comment>
+ <comment xml:lang="en_GB">Doom WAD</comment>
+ <comment xml:lang="es">WAD de Doom</comment>
+ <comment xml:lang="eu">Doom WAD</comment>
+ <comment xml:lang="fr">WAD Doom</comment>
+ <comment xml:lang="ga">WAD Doom</comment>
+ <comment xml:lang="hr">Doom WAD</comment>
+ <comment xml:lang="hu">Doom WAD</comment>
+ <comment xml:lang="ia">WAD pro Doom</comment>
+ <comment xml:lang="id">WAD Doom</comment>
+ <comment xml:lang="it">WAD Doom</comment>
+ <comment xml:lang="kk">Doom WAD</comment>
+ <comment xml:lang="ko">둠 WAD</comment>
+ <comment xml:lang="pl">Plik WAD gry Doom</comment>
+ <comment xml:lang="pt">Doom WAD</comment>
+ <comment xml:lang="pt_BR">Doom WAD</comment>
+ <comment xml:lang="ru">WAD Doom</comment>
+ <comment xml:lang="sk">Doom WAD</comment>
+ <comment xml:lang="sr">Дум ВАД</comment>
+ <comment xml:lang="sv">Doom-WAD</comment>
+ <comment xml:lang="tr">Doom WAD</comment>
+ <comment xml:lang="uk">WAD Doom</comment>
+ <comment xml:lang="zh_CN">Doom WAD</comment>
+ <comment xml:lang="zh_TW">Doom WAD</comment>
+ <acronym>WAD</acronym>
+ <expanded-acronym>Where's All the Data</expanded-acronym>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="IWAD" type="string" offset="0"/>
+ <match value="PWAD" type="string" offset="0"/>
+ </magic>
+ <glob weight="80" pattern="*.wad"/>
+ </mime-type>
+
+ <mime-type type="application/x-amiga-disk-format">
+ <comment>Amiga disk image</comment>
+ <comment xml:lang="ca">imatge de disc d'Amiga</comment>
+ <comment xml:lang="cs">obraz disku pro Amigu</comment>
+ <comment xml:lang="da">Amiga-diskaftryk</comment>
+ <comment xml:lang="de">Amiga-Datenträgerabbild</comment>
+ <comment xml:lang="el">Εικόνα δίσκου Amiga</comment>
+ <comment xml:lang="en_GB">Amiga disk image</comment>
+ <comment xml:lang="es">imagen de disco de Amiga</comment>
+ <comment xml:lang="eu">Amiga disko irudia</comment>
+ <comment xml:lang="fi">Amiga-levytiedosto</comment>
+ <comment xml:lang="fr">image disque Amiga</comment>
+ <comment xml:lang="ga">íomhá diosca Amiga</comment>
+ <comment xml:lang="he">דמות כונן Amiga</comment>
+ <comment xml:lang="hr">Amiga slika diska</comment>
+ <comment xml:lang="hu">Amiga lemezkép</comment>
+ <comment xml:lang="ia">Imagine de disco Amiga</comment>
+ <comment xml:lang="id">Image disk Amiga</comment>
+ <comment xml:lang="it">Disco immagine Amiga</comment>
+ <comment xml:lang="kk">Amiga диск бейнесі</comment>
+ <comment xml:lang="ko">Amiga 디스크 이미지</comment>
+ <comment xml:lang="oc">imatge disc Amiga</comment>
+ <comment xml:lang="pl">Obraz dysku Amiga</comment>
+ <comment xml:lang="pt">imagem de disco Amiga</comment>
+ <comment xml:lang="pt_BR">Imagem de disco Amiga</comment>
+ <comment xml:lang="ru">Образ диска Amiga</comment>
+ <comment xml:lang="sk">Obraz disku Amiga</comment>
+ <comment xml:lang="sr">слика диска Амиге</comment>
+ <comment xml:lang="sv">Amiga-diskavbild</comment>
+ <comment xml:lang="tr">Amiga disk kalıbı</comment>
+ <comment xml:lang="uk">образ диска Amiga</comment>
+ <comment xml:lang="zh_CN">Amiga 磁盘映像</comment>
+ <comment xml:lang="zh_TW">Amiga 磁碟映像檔</comment>
+ <magic priority="50">
+ <match value="DOS\x00" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.adf"/>
+ </mime-type>
+
+ <mime-type type="application/vnd.flatpak">
+ <comment>Flatpak application bundle</comment>
+ <comment xml:lang="ca">paquet d'aplicació Flatpak</comment>
+ <comment xml:lang="cs">balíček Flatpak s aplikací</comment>
+ <comment xml:lang="da">Flatpak-programsamling</comment>
+ <comment xml:lang="de">Flatpak-Anwendungspaket</comment>
+ <comment xml:lang="en_GB">Flatpak application bundle</comment>
+ <comment xml:lang="es">paquete de aplicación Flatpak</comment>
+ <comment xml:lang="eu">Flatpak aplikazio bilduma</comment>
+ <comment xml:lang="fi">Flatpak-sovelluspaketti</comment>
+ <comment xml:lang="fr">lot applicatif Flatpak</comment>
+ <comment xml:lang="ga">burla feidhmchláir Flatpak</comment>
+ <comment xml:lang="he">חבילת יישומי Flatpak</comment>
+ <comment xml:lang="hr">Flatpak paket aplikacije</comment>
+ <comment xml:lang="hu">Flatpak alkalmazáscsomag</comment>
+ <comment xml:lang="id">bundel aplikasi Flatpak</comment>
+ <comment xml:lang="it">Bundle applicazione Flatpak</comment>
+ <comment xml:lang="kk">Flatpak қолданбалар дестесі</comment>
+ <comment xml:lang="ko">Flatpak 프로그램 번들</comment>
+ <comment xml:lang="pl">Pakiet programu Flatpak</comment>
+ <comment xml:lang="pt_BR">Pacote de aplicativo Flatpak</comment>
+ <comment xml:lang="ru">Пакет приложения Flatpak</comment>
+ <comment xml:lang="sk">Balík aplikácií Flatpak</comment>
+ <comment xml:lang="sr">скуп програма Флатпака</comment>
+ <comment xml:lang="sv">Flatpak-programbunt</comment>
+ <comment xml:lang="tr">Flatpak uygulama paketi</comment>
+ <comment xml:lang="uk">пакунок із програмами Flatpak</comment>
+ <comment xml:lang="zh_CN">Flatpak 应用组合包</comment>
+ <comment xml:lang="zh_TW">Flatpak 應用程式套組</comment>
+ <generic-icon name="package-x-generic"/>
+ <magic priority="50">
+ <match value="xdg-app\x00\x01\x00\x89\xe5" type="string" offset="0"/>
+ <match value="flatpak\x00\x01\x00\x89\xe5" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.flatpak"/>
+ <glob pattern="*.xdgapp"/>
+ <alias type="application/vnd.xdgapp"/>
+ </mime-type>
+
+ <mime-type type="application/vnd.flatpak.repo">
+ <comment>Flatpak repository description</comment>
+ <comment xml:lang="ca">descripció de dipòsit de Flatpak</comment>
+ <comment xml:lang="cs">popis repozitáře Flatpak</comment>
+ <comment xml:lang="da">Flatpak-arkivbeskrivelse</comment>
+ <comment xml:lang="de">Flatpak-Repositoriumsbeschreibung</comment>
+ <comment xml:lang="en_GB">Flatpak repository description</comment>
+ <comment xml:lang="es">descripción de repositorio de Flatpak</comment>
+ <comment xml:lang="eu">Flatpak biltegi deskribapena</comment>
+ <comment xml:lang="fi">Flatpak-ohjelmistolähdekuvaus</comment>
+ <comment xml:lang="fr">description de dépôt Flatpak</comment>
+ <comment xml:lang="ga">cur síos ar stórlann Flatpak</comment>
+ <comment xml:lang="he">תיאור מאגר Flatpak</comment>
+ <comment xml:lang="hr">Flatpak opis repozitorija</comment>
+ <comment xml:lang="hu">Flatpak tárolóleírás</comment>
+ <comment xml:lang="id">deskripsi repositori Flatpak</comment>
+ <comment xml:lang="it">Descrizione repository Flatpack</comment>
+ <comment xml:lang="kk">Flatpak репозиторийі сипаттамасы</comment>
+ <comment xml:lang="ko">Flatpak 저장소 디스크립션</comment>
+ <comment xml:lang="pl">Opis repozytorium Flatpak</comment>
+ <comment xml:lang="pt_BR">Descrição de repositório Flatpak</comment>
+ <comment xml:lang="ru">Описание репозитория Flatpak</comment>
+ <comment xml:lang="sk">Popis repozitára Flatpak</comment>
+ <comment xml:lang="sr">опис ризнице Флатпака</comment>
+ <comment xml:lang="sv">Flatpak-förrådsbeskrivning</comment>
+ <comment xml:lang="tr">Flatpak depo açıklaması</comment>
+ <comment xml:lang="uk">опис сховища Flatpak</comment>
+ <comment xml:lang="zh_CN">Flatpak 软件库描述</comment>
+ <comment xml:lang="zh_TW">Flatpak 軟體庫描述</comment>
+ <generic-icon name="package-x-generic"/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="[Flatpak Repo]" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.flatpakrepo"/>
+ </mime-type>
+
+ <mime-type type="application/vnd.flatpak.ref">
+ <comment>Flatpak repository reference</comment>
+ <comment xml:lang="ca">referència de dipòsit Flatpak</comment>
+ <comment xml:lang="cs">odkaz na repozitář Flatpak</comment>
+ <comment xml:lang="de">Flatpak-Repositoriumsreferenz</comment>
+ <comment xml:lang="en_GB">Flatpak repository reference</comment>
+ <comment xml:lang="es">referencia a repositorio de Flatpak</comment>
+ <comment xml:lang="eu">Flatpak biltegi erreferentzia</comment>
+ <comment xml:lang="fi">Flatpak-ohjelmistolähdeviite</comment>
+ <comment xml:lang="fr">référence de dépôt Flatpak</comment>
+ <comment xml:lang="ga">tagairt do stórlann Flatpak</comment>
+ <comment xml:lang="hr">Flatpak preporučeni repozitorij</comment>
+ <comment xml:lang="hu">Flatpak tárolóhivatkozás</comment>
+ <comment xml:lang="id">acuan repositori Flatpak</comment>
+ <comment xml:lang="it">Riferimento repository Flatpack</comment>
+ <comment xml:lang="kk">Flatpak репозиторийіне сілтеме</comment>
+ <comment xml:lang="ko">Flatpak 저장소 참조</comment>
+ <comment xml:lang="pl">Odwołanie do repozytorium Flatpak</comment>
+ <comment xml:lang="pt_BR">Referência de repositório Flatpak</comment>
+ <comment xml:lang="ru">Ссылка на репозиторий Flatpak</comment>
+ <comment xml:lang="sk">Referencia repozitára Flatpak</comment>
+ <comment xml:lang="sr">упута ризнице Флатпака</comment>
+ <comment xml:lang="sv">Flatpak-förrådsreferens</comment>
+ <comment xml:lang="tr">Flatpak depo başvurusu</comment>
+ <comment xml:lang="uk">посилання на сховище Flatpak</comment>
+ <comment xml:lang="zh_CN">Flatpak 软件库引用</comment>
+ <comment xml:lang="zh_TW">Flatpak 軟體庫參照</comment>
+ <generic-icon name="package-x-generic"/>
+ <sub-class-of type="text/plain"/>
+ <magic priority="50">
+ <match value="[Flatpak Ref]" type="string" offset="0:256"/>
+ </magic>
+ <glob pattern="*.flatpakref"/>
+ </mime-type>
+
+ <mime-type type="application/vnd.squashfs">
+ <comment>Squashfs filesystem</comment>
+ <comment xml:lang="ca">Sistema de fitxers Squashfs</comment>
+ <comment xml:lang="cs">souborový systém Squashfs</comment>
+ <comment xml:lang="da">Squashfs-filsystem</comment>
+ <comment xml:lang="de">Squashfs-Dateisystem</comment>
+ <comment xml:lang="en_GB">Squashfs filesystem</comment>
+ <comment xml:lang="es">sistema de archivos Squashfs</comment>
+ <comment xml:lang="eu">Squashfs fitxategi sistema</comment>
+ <comment xml:lang="fi">Squashfs-tiedostojärjestelmä</comment>
+ <comment xml:lang="fr">système de fichiers Squashfs</comment>
+ <comment xml:lang="ga">córas comhad Squashfs</comment>
+ <comment xml:lang="he">מערכת קבצים Squashfs</comment>
+ <comment xml:lang="hr">Squashfs datotečni sustav</comment>
+ <comment xml:lang="hu">Squashfs fájlrendszer</comment>
+ <comment xml:lang="id">sistem berkas Squashfs</comment>
+ <comment xml:lang="it">File system squashfs</comment>
+ <comment xml:lang="kk">Squashfs файлдық жүйесі</comment>
+ <comment xml:lang="ko">Squashfs 파일 시스템</comment>
+ <comment xml:lang="pl">System plików SquashFS</comment>
+ <comment xml:lang="pt_BR">Sistema de arquivos Squashfs</comment>
+ <comment xml:lang="ru">Файловая система Squashfs</comment>
+ <comment xml:lang="sk">Systém súborov Squashfs</comment>
+ <comment xml:lang="sr">систем датотека Сквошфс</comment>
+ <comment xml:lang="sv">Squashfs-filsystem</comment>
+ <comment xml:lang="tr">Squashfs dosya sistemi</comment>
+ <comment xml:lang="uk">файлова система squashfs</comment>
+ <comment xml:lang="zh_CN">Squashfs 文件系统</comment>
+ <comment xml:lang="zh_TW">Squashfs 檔案系統</comment>
+ <magic priority="50">
+ <match value="sqsh" type="string" offset="0"/>
+ <match value="hsqs" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.sqsh"/>
+ </mime-type>
+
+
+ <mime-type type="application/vnd.appimage">
+ <comment>AppImage application bundle</comment>
+ <comment xml:lang="ca">paquet d'aplicació AppImage</comment>
+ <comment xml:lang="cs">balíček AppImage s aplikací</comment>
+ <comment xml:lang="da">Applmage-programsamling</comment>
+ <comment xml:lang="de">AppImage-Anwendungspaket</comment>
+ <comment xml:lang="en_GB">AppImage application bundle</comment>
+ <comment xml:lang="es">paquete de aplicación AppImage</comment>
+ <comment xml:lang="eu">AppImage aplikazio bilduma</comment>
+ <comment xml:lang="fi">AppImage-sovelluspaketti</comment>
+ <comment xml:lang="fr">lot applicatif AppImage</comment>
+ <comment xml:lang="ga">burla feidhmchláir AppImage</comment>
+ <comment xml:lang="he">חבילת יישומי AppImage</comment>
+ <comment xml:lang="hr">AppImage paket aplikacije</comment>
+ <comment xml:lang="hu">AppImage alkalmazáscsomag</comment>
+ <comment xml:lang="id">bundel aplikasi AppImage</comment>
+ <comment xml:lang="it">Bundle applicazione AppImage</comment>
+ <comment xml:lang="kk">AppImage қолданбалар дестесі</comment>
+ <comment xml:lang="ko">AppImage 프로그램 번들</comment>
+ <comment xml:lang="pl">Pakiet programu AppImage</comment>
+ <comment xml:lang="pt_BR">Pacote de aplicativo AppImage</comment>
+ <comment xml:lang="ru">Пакет приложения AppImage</comment>
+ <comment xml:lang="sk">Balík aplikácií AppImage</comment>
+ <comment xml:lang="sr">скуп програма Ап-слике</comment>
+ <comment xml:lang="sv">AppImage-programbunt</comment>
+ <comment xml:lang="tr">AppImage uygulama paketi</comment>
+ <comment xml:lang="uk">пакунок із програмами AppImage</comment>
+ <comment xml:lang="zh_CN">AppImage 应用组合包</comment>
+ <comment xml:lang="zh_TW">AppImage 應用程式套組</comment>
+ <sub-class-of type="application/x-executable"/>
+ <sub-class-of type="application/vnd.squashfs"/>
+ <generic-icon name="application-x-executable"/>
+ <magic priority="50">
+ <match value="ELF" type="string" offset="1">
+ <match value="0x41" type="byte" offset="8">
+ <match value="0x49" type="byte" offset="9">
+ <match value="0x02" type="byte" offset="10"/>
+ </match>
+ </match>
+ </match>
+ </magic>
+ <glob pattern="*.appimage"/>
+ </mime-type>
+
+ <mime-type type="application/vnd.snap">
+ <comment>Snap package</comment>
+ <comment xml:lang="ca">Paquet Snap</comment>
+ <comment xml:lang="cs">balíček Snap</comment>
+ <comment xml:lang="da">Snap-pakke</comment>
+ <comment xml:lang="de">Snap-Paket</comment>
+ <comment xml:lang="en_GB">Snap package</comment>
+ <comment xml:lang="es">paquete Snap</comment>
+ <comment xml:lang="eu">Snap paketea</comment>
+ <comment xml:lang="fi">Snap-paketti</comment>
+ <comment xml:lang="fr">paquet Snap</comment>
+ <comment xml:lang="ga">pacáiste Snap</comment>
+ <comment xml:lang="he">חבילת Snap</comment>
+ <comment xml:lang="hr">Snap paket</comment>
+ <comment xml:lang="hu">Snap-csomag</comment>
+ <comment xml:lang="id">paket Snap</comment>
+ <comment xml:lang="it">Pacchetto snap</comment>
+ <comment xml:lang="kk">Snap дестесі</comment>
+ <comment xml:lang="ko">Snap 패키지</comment>
+ <comment xml:lang="pl">Pakiet Snap</comment>
+ <comment xml:lang="pt_BR">Pacote Snap</comment>
+ <comment xml:lang="ru">Пакет Snap</comment>
+ <comment xml:lang="sk">Balík Snap</comment>
+ <comment xml:lang="sr">Снап пакет</comment>
+ <comment xml:lang="sv">Snap-paket</comment>
+ <comment xml:lang="tr">Snap paketi</comment>
+ <comment xml:lang="uk">пакунок snap</comment>
+ <comment xml:lang="zh_CN">Snap 软件包</comment>
+ <comment xml:lang="zh_TW">Snap 軟體包</comment>
+ <glob pattern="*.snap"/>
+ <sub-class-of type="application/vnd.squashfs"/>
+ </mime-type>
+
+
+ <mime-type type="model/stl">
+ <comment>STL 3D model</comment>
+ <acronym>STL</acronym>
+ <expanded-acronym>StereoLithography</expanded-acronym>
+ <magic priority="50">
+ <match value="solid" type="string" offset="0"/>
+ <match value="SOLID" type="string" offset="0"/>
+ </magic>
+ <glob pattern="*.stl"/>
+ <alias type="model/x.stl-ascii"/>
+ <alias type="model/x.stl-binary"/>
+ </mime-type>
+
+ <mime-type type="text/x.gcode">
+ <comment>G-code file</comment>
+ <comment xml:lang="ca">fitxer G-code</comment>
+ <comment xml:lang="cs">soubor G-code</comment>
+ <comment xml:lang="de">G-Code-Datei</comment>
+ <comment xml:lang="en_GB">G-code file</comment>
+ <comment xml:lang="es">archivo G-code</comment>
+ <comment xml:lang="eu">G-code fitxategia</comment>
+ <comment xml:lang="fi">G-code-tiedosto</comment>
+ <comment xml:lang="fr">fichier G-code</comment>
+ <comment xml:lang="ga">comhad G-code</comment>
+ <comment xml:lang="hr">G-kôd datoteka</comment>
+ <comment xml:lang="hu">G-code fájl</comment>
+ <comment xml:lang="id">berkas G-code</comment>
+ <comment xml:lang="it">File G-code</comment>
+ <comment xml:lang="kk">G-code файлы</comment>
+ <comment xml:lang="ko">지-코드 파일</comment>
+ <comment xml:lang="pl">Plik G-code</comment>
+ <comment xml:lang="pt_BR">Arquivo G-code</comment>
+ <comment xml:lang="ru">Файл G-code</comment>
+ <comment xml:lang="sk">Súbor G-code</comment>
+ <comment xml:lang="sr">датотека Г-ко̂да</comment>
+ <comment xml:lang="sv">G-code-fil</comment>
+ <comment xml:lang="tr">G-code dosyası</comment>
+ <comment xml:lang="uk">файл G-code</comment>
+ <comment xml:lang="zh_CN">G-code 文件</comment>
+ <comment xml:lang="zh_TW">G-code 檔案</comment>
+ <sub-class-of type="text/plain"/>
+ <generic-icon name="text-x-generic"/>
+ <glob pattern="*.gcode"/>
+ </mime-type>
+
+ <mime-type type="application/x-fds-disk">
+ <comment>Nintendo FDS disk image</comment>
+ <comment xml:lang="ca">Imatge de disc Nintendo FDS</comment>
+ <comment xml:lang="cs">obraz disku pro Nintendo FDS</comment>
+ <comment xml:lang="de">Nintendo-FDS-Datenträgerabbild</comment>
+ <comment xml:lang="en_GB">Nintendo FDS disk image</comment>
+ <comment xml:lang="es">imagen de disco FDS de Nintendo</comment>
+ <comment xml:lang="eu">Nintendo FDS disko irudia</comment>
+ <comment xml:lang="fi">Nintendo FDS -levykuva</comment>
+ <comment xml:lang="fr">image disque Nintendo FDS</comment>
+ <comment xml:lang="ga">íomhá diosca Nintendo FDS</comment>
+ <comment xml:lang="hr">Nintendo FDS slika diska</comment>
+ <comment xml:lang="hu">Nintendo FDS lemezkép</comment>
+ <comment xml:lang="id">image disk Nintendo FDS</comment>
+ <comment xml:lang="it">Immagine disco Nintendo FDS</comment>
+ <comment xml:lang="kk">Nintendo FDS диск бейнесі</comment>
+ <comment xml:lang="ko">닌텐도 FDS 디스크 이미지</comment>
+ <comment xml:lang="pl">Obraz dysku Nintendo FDS</comment>
+ <comment xml:lang="pt_BR">Imagem de disco Nintendo FDS</comment>
+ <comment xml:lang="ru">Образ диска Nintendo FDS</comment>
+ <comment xml:lang="sk">Obraz disku Nintendo FDS</comment>
+ <comment xml:lang="sr">Нинтендо ФДС слика диска</comment>
+ <comment xml:lang="sv">Nintendo FDS-diskavbild</comment>
+ <comment xml:lang="tr">Nintendo FDS disk kalıbı</comment>
+ <comment xml:lang="uk">образ диска FDS Nintendo</comment>
+ <comment xml:lang="zh_CN">任天堂 FDS 磁盘映像</comment>
+ <comment xml:lang="zh_TW">Nintendo FDS 磁碟映像檔</comment>
+ <acronym>FDS</acronym>
+ <expanded-acronym>Famicom Disk System</expanded-acronym>
+ <glob pattern="*.fds"/>
+ <magic>
+ <match value="*NINTENDO-HVC*" type="string" offset="1"/>
+ </magic>
+ </mime-type>
+
+</mime-info> \ No newline at end of file
diff --git a/src/libs/3rdparty/yaml-cpp/.clang-format b/src/libs/3rdparty/yaml-cpp/.clang-format
new file mode 100644
index 0000000000..d6d46fb416
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/.clang-format
@@ -0,0 +1,47 @@
+---
+# BasedOnStyle: Google
+AccessModifierOffset: -1
+ConstructorInitializerIndentWidth: 4
+AlignEscapedNewlinesLeft: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakTemplateDeclarations: true
+AlwaysBreakBeforeMultilineStrings: true
+BreakBeforeBinaryOperators: false
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BinPackParameters: true
+ColumnLimit: 80
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+DerivePointerBinding: true
+ExperimentalAutoDetectBinPacking: false
+IndentCaseLabels: true
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCSpaceBeforeProtocolList: false
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakComment: 60
+PenaltyBreakString: 1000
+PenaltyBreakFirstLessLess: 120
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 200
+PointerBindsToType: true
+SpacesBeforeTrailingComments: 2
+Cpp11BracedListStyle: true
+Standard: Cpp11
+IndentWidth: 2
+TabWidth: 8
+UseTab: Never
+BreakBeforeBraces: Attach
+IndentFunctionDeclarationAfterType: true
+SpacesInParentheses: false
+SpacesInAngles: false
+SpaceInEmptyParentheses: false
+SpacesInCStyleCastParentheses: false
+SpaceAfterControlStatementKeyword: true
+SpaceBeforeAssignmentOperators: true
+ContinuationIndentWidth: 4
+...
+
diff --git a/src/libs/3rdparty/yaml-cpp/LICENSE b/src/libs/3rdparty/yaml-cpp/LICENSE
new file mode 100644
index 0000000000..991fdbbe7d
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2008-2015 Jesse Beder.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/libs/3rdparty/yaml-cpp/README.md b/src/libs/3rdparty/yaml-cpp/README.md
new file mode 100644
index 0000000000..70c231445d
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/README.md
@@ -0,0 +1,58 @@
+# yaml-cpp ![Build Status](https://github.com/jbeder/yaml-cpp/actions/workflows/build.yml/badge.svg) [![Documentation](https://codedocs.xyz/jbeder/yaml-cpp.svg)](https://codedocs.xyz/jbeder/yaml-cpp/)
+
+`yaml-cpp` is a [YAML](http://www.yaml.org/) parser and emitter in C++ matching the [YAML 1.2 spec](http://www.yaml.org/spec/1.2/spec.html).
+
+## Usage
+
+See [Tutorial](https://github.com/jbeder/yaml-cpp/wiki/Tutorial) and [How to Emit YAML](https://github.com/jbeder/yaml-cpp/wiki/How-To-Emit-YAML) for reference. For the old API (until 0.5.0), see [How To Parse A Document](https://github.com/jbeder/yaml-cpp/wiki/How-To-Parse-A-Document-(Old-API)).
+
+## Any Problems?
+
+If you find a bug, post an [issue](https://github.com/jbeder/yaml-cpp/issues)! If you have questions about how to use yaml-cpp, please post it on http://stackoverflow.com and tag it [`yaml-cpp`](http://stackoverflow.com/questions/tagged/yaml-cpp).
+
+## How to Build
+
+`yaml-cpp` uses [CMake](http://www.cmake.org) to support cross-platform building. Install [CMake](http://www.cmake.org) _(Resources -> Download)_ before proceeding. The basic steps to build are:
+
+**Note:** If you don't use the provided installer for your platform, make sure that you add `CMake`'s bin folder to your path.
+
+#### 1. Navigate into the source directory, create build folder and run `CMake`:
+
+```sh
+mkdir build
+cd build
+cmake [-G generator] [-DYAML_BUILD_SHARED_LIBS=on|OFF] ..
+```
+
+ * The `generator` option is the build system you'd like to use. Run `cmake` without arguments to see a full list of available generators.
+ * On Windows, you might use "Visual Studio 12 2013" (VS 2013 32-bits), or "Visual Studio 14 2015 Win64" (VS 2015 64-bits).
+ * On OS X, you might use "Xcode".
+ * On a UNIX-like system, omit the option (for a Makefile).
+
+ * `yaml-cpp` builds a static library by default, you may want to build a shared library by specifying `-DYAML_BUILD_SHARED_LIBS=ON`.
+
+ * For more options on customizing the build, see the [CMakeLists.txt](https://github.com/jbeder/yaml-cpp/blob/master/CMakeLists.txt) file.
+
+#### 2. Build it!
+ * The command you'll need to run depends on the generator you chose earlier.
+
+**Note:** To clean up, just remove the `build` directory.
+
+## Recent Releases
+
+[yaml-cpp 0.6.0](https://github.com/jbeder/yaml-cpp/releases/tag/yaml-cpp-0.6.0) released! This release requires C++11, and no longer depends on Boost.
+
+[yaml-cpp 0.3.0](https://github.com/jbeder/yaml-cpp/releases/tag/release-0.3.0) is still available if you want the old API.
+
+**The old API will continue to be supported, and will still receive bugfixes!** The 0.3.x and 0.4.x versions will be old API releases, and 0.5.x and above will all be new API releases.
+
+# API Documentation
+
+The autogenerated API reference is hosted on [CodeDocs](https://codedocs.xyz/jbeder/yaml-cpp/index.html)
+
+# Third Party Integrations
+
+The following projects are not officially supported:
+
+- [Qt wrapper](https://gist.github.com/brcha/d392b2fe5f1e427cc8a6)
+- [UnrealEngine Wrapper](https://github.com/jwindgassen/UnrealYAML)
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/anchor.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/anchor.h
new file mode 100644
index 0000000000..f46d1d79dd
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/anchor.h
@@ -0,0 +1,17 @@
+#ifndef ANCHOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define ANCHOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <cstddef>
+
+namespace YAML {
+using anchor_t = std::size_t;
+const anchor_t NullAnchor = 0;
+}
+
+#endif // ANCHOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/binary.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/binary.h
new file mode 100644
index 0000000000..1050dae98c
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/binary.h
@@ -0,0 +1,71 @@
+#ifndef BASE64_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define BASE64_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <string>
+#include <vector>
+
+#include "yaml-cpp/dll.h"
+
+namespace YAML {
+YAML_CPP_API std::string EncodeBase64(const unsigned char *data,
+ std::size_t size);
+YAML_CPP_API std::vector<unsigned char> DecodeBase64(const std::string &input);
+
+class YAML_CPP_API Binary {
+ public:
+ Binary(const unsigned char *data_, std::size_t size_)
+ : m_data{}, m_unownedData(data_), m_unownedSize(size_) {}
+ Binary() : Binary(nullptr, 0) {}
+ Binary(const Binary &) = default;
+ Binary(Binary &&) = default;
+ Binary &operator=(const Binary &) = default;
+ Binary &operator=(Binary &&) = default;
+
+ bool owned() const { return !m_unownedData; }
+ std::size_t size() const { return owned() ? m_data.size() : m_unownedSize; }
+ const unsigned char *data() const {
+ return owned() ? &m_data[0] : m_unownedData;
+ }
+
+ void swap(std::vector<unsigned char> &rhs) {
+ if (m_unownedData) {
+ m_data.swap(rhs);
+ rhs.clear();
+ rhs.resize(m_unownedSize);
+ std::copy(m_unownedData, m_unownedData + m_unownedSize, rhs.begin());
+ m_unownedData = nullptr;
+ m_unownedSize = 0;
+ } else {
+ m_data.swap(rhs);
+ }
+ }
+
+ bool operator==(const Binary &rhs) const {
+ const std::size_t s = size();
+ if (s != rhs.size())
+ return false;
+ const unsigned char *d1 = data();
+ const unsigned char *d2 = rhs.data();
+ for (std::size_t i = 0; i < s; i++) {
+ if (*d1++ != *d2++)
+ return false;
+ }
+ return true;
+ }
+
+ bool operator!=(const Binary &rhs) const { return !(*this == rhs); }
+
+ private:
+ std::vector<unsigned char> m_data;
+ const unsigned char *m_unownedData;
+ std::size_t m_unownedSize;
+};
+} // namespace YAML
+
+#endif // BASE64_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/depthguard.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/depthguard.h
new file mode 100644
index 0000000000..8ca61ac6cc
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/depthguard.h
@@ -0,0 +1,77 @@
+#ifndef DEPTH_GUARD_H_00000000000000000000000000000000000000000000000000000000
+#define DEPTH_GUARD_H_00000000000000000000000000000000000000000000000000000000
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "exceptions.h"
+
+namespace YAML {
+
+/**
+ * @brief The DeepRecursion class
+ * An exception class which is thrown by DepthGuard. Ideally it should be
+ * a member of DepthGuard. However, DepthGuard is a templated class which means
+ * that any catch points would then need to know the template parameters. It is
+ * simpler for clients to not have to know at the catch point what was the
+ * maximum depth.
+ */
+class DeepRecursion : public ParserException {
+public:
+ virtual ~DeepRecursion() = default;
+
+ DeepRecursion(int depth, const Mark& mark_, const std::string& msg_);
+
+ // Returns the recursion depth when the exception was thrown
+ int depth() const {
+ return m_depth;
+ }
+
+private:
+ int m_depth = 0;
+};
+
+/**
+ * @brief The DepthGuard class
+ * DepthGuard takes a reference to an integer. It increments the integer upon
+ * construction of DepthGuard and decrements the integer upon destruction.
+ *
+ * If the integer would be incremented past max_depth, then an exception is
+ * thrown. This is ideally geared toward guarding against deep recursion.
+ *
+ * @param max_depth
+ * compile-time configurable maximum depth.
+ */
+template <int max_depth = 2000>
+class DepthGuard final {
+public:
+ DepthGuard(int & depth_, const Mark& mark_, const std::string& msg_) : m_depth(depth_) {
+ ++m_depth;
+ if ( max_depth <= m_depth ) {
+ throw DeepRecursion{m_depth, mark_, msg_};
+ }
+ }
+
+ DepthGuard(const DepthGuard & copy_ctor) = delete;
+ DepthGuard(DepthGuard && move_ctor) = delete;
+ DepthGuard & operator=(const DepthGuard & copy_assign) = delete;
+ DepthGuard & operator=(DepthGuard && move_assign) = delete;
+
+ ~DepthGuard() {
+ --m_depth;
+ }
+
+ int current_depth() const {
+ return m_depth;
+ }
+
+private:
+ int & m_depth;
+};
+
+} // namespace YAML
+
+#endif // DEPTH_GUARD_H_00000000000000000000000000000000000000000000000000000000
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h
new file mode 100644
index 0000000000..eabdda1d95
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h
@@ -0,0 +1,61 @@
+#ifndef DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+// Definition YAML_CPP_STATIC_DEFINE using to building YAML-CPP as static
+// library (definition created by CMake or defined manually)
+
+// Definition yaml_cpp_EXPORTS using to building YAML-CPP as dll/so library
+// (definition created by CMake or defined manually)
+
+#ifdef YAML_CPP_STATIC_DEFINE
+# define YAML_CPP_API
+# define YAML_CPP_NO_EXPORT
+#else
+# if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
+# ifndef YAML_CPP_API
+# ifdef yaml_cpp_EXPORTS
+ /* We are building this library */
+# pragma message( "Defining YAML_CPP_API for DLL export" )
+# define YAML_CPP_API __declspec(dllexport)
+# else
+ /* We are using this library */
+# pragma message( "Defining YAML_CPP_API for DLL import" )
+# define YAML_CPP_API __declspec(dllimport)
+# endif
+# endif
+# ifndef YAML_CPP_NO_EXPORT
+# define YAML_CPP_NO_EXPORT
+# endif
+# else /* No _MSC_VER */
+# ifndef YAML_CPP_API
+# ifdef yaml_cpp_EXPORTS
+ /* We are building this library */
+# define YAML_CPP_API __attribute__((visibility("default")))
+# else
+ /* We are using this library */
+# define YAML_CPP_API __attribute__((visibility("default")))
+# endif
+# endif
+# ifndef YAML_CPP_NO_EXPORT
+# define YAML_CPP_NO_EXPORT __attribute__((visibility("hidden")))
+# endif
+# endif /* _MSC_VER */
+#endif /* YAML_CPP_STATIC_DEFINE */
+
+#ifndef YAML_CPP_DEPRECATED
+# ifdef _MSC_VER
+# define YAML_CPP_DEPRECATED __declspec(deprecated)
+# else
+# define YAML_CPP_DEPRECATED __attribute__ ((__deprecated__))
+# endif
+#endif
+
+#ifndef YAML_CPP_DEPRECATED_EXPORT
+# define YAML_CPP_DEPRECATED_EXPORT YAML_CPP_API YAML_CPP_DEPRECATED
+#endif
+
+#ifndef YAML_CPP_DEPRECATED_NO_EXPORT
+# define YAML_CPP_DEPRECATED_NO_EXPORT YAML_CPP_NO_EXPORT YAML_CPP_DEPRECATED
+#endif
+
+#endif /* DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 */
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitfromevents.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitfromevents.h
new file mode 100644
index 0000000000..1f389c5a13
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitfromevents.h
@@ -0,0 +1,57 @@
+#ifndef EMITFROMEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define EMITFROMEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <stack>
+
+#include "yaml-cpp/anchor.h"
+#include "yaml-cpp/emitterstyle.h"
+#include "yaml-cpp/eventhandler.h"
+
+namespace YAML {
+struct Mark;
+} // namespace YAML
+
+namespace YAML {
+class Emitter;
+
+class EmitFromEvents : public EventHandler {
+ public:
+ EmitFromEvents(Emitter& emitter);
+
+ void OnDocumentStart(const Mark& mark) override;
+ void OnDocumentEnd() override;
+
+ void OnNull(const Mark& mark, anchor_t anchor) override;
+ void OnAlias(const Mark& mark, anchor_t anchor) override;
+ void OnScalar(const Mark& mark, const std::string& tag,
+ anchor_t anchor, const std::string& value) override;
+
+ void OnSequenceStart(const Mark& mark, const std::string& tag,
+ anchor_t anchor, EmitterStyle::value style) override;
+ void OnSequenceEnd() override;
+
+ void OnMapStart(const Mark& mark, const std::string& tag,
+ anchor_t anchor, EmitterStyle::value style) override;
+ void OnMapEnd() override;
+
+ private:
+ void BeginNode();
+ void EmitProps(const std::string& tag, anchor_t anchor);
+
+ private:
+ Emitter& m_emitter;
+
+ struct State {
+ enum value { WaitingForSequenceEntry, WaitingForKey, WaitingForValue };
+ };
+ std::stack<State::value> m_stateStack;
+};
+}
+
+#endif // EMITFROMEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitter.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitter.h
new file mode 100644
index 0000000000..210b1ec974
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitter.h
@@ -0,0 +1,281 @@
+#ifndef EMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define EMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <cmath>
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+#include "yaml-cpp/binary.h"
+#include "yaml-cpp/dll.h"
+#include "yaml-cpp/emitterdef.h"
+#include "yaml-cpp/emittermanip.h"
+#include "yaml-cpp/null.h"
+#include "yaml-cpp/ostream_wrapper.h"
+
+namespace YAML {
+class Binary;
+struct _Null;
+} // namespace YAML
+
+namespace YAML {
+class EmitterState;
+
+class YAML_CPP_API Emitter {
+ public:
+ Emitter();
+ explicit Emitter(std::ostream& stream);
+ Emitter(const Emitter&) = delete;
+ Emitter& operator=(const Emitter&) = delete;
+ ~Emitter();
+
+ // output
+ const char* c_str() const;
+ std::size_t size() const;
+
+ // state checking
+ bool good() const;
+ const std::string GetLastError() const;
+
+ // global setters
+ bool SetOutputCharset(EMITTER_MANIP value);
+ bool SetStringFormat(EMITTER_MANIP value);
+ bool SetBoolFormat(EMITTER_MANIP value);
+ bool SetNullFormat(EMITTER_MANIP value);
+ bool SetIntBase(EMITTER_MANIP value);
+ bool SetSeqFormat(EMITTER_MANIP value);
+ bool SetMapFormat(EMITTER_MANIP value);
+ bool SetIndent(std::size_t n);
+ bool SetPreCommentIndent(std::size_t n);
+ bool SetPostCommentIndent(std::size_t n);
+ bool SetFloatPrecision(std::size_t n);
+ bool SetDoublePrecision(std::size_t n);
+ void RestoreGlobalModifiedSettings();
+
+ // local setters
+ Emitter& SetLocalValue(EMITTER_MANIP value);
+ Emitter& SetLocalIndent(const _Indent& indent);
+ Emitter& SetLocalPrecision(const _Precision& precision);
+
+ // overloads of write
+ Emitter& Write(const std::string& str);
+ Emitter& Write(bool b);
+ Emitter& Write(char ch);
+ Emitter& Write(const _Alias& alias);
+ Emitter& Write(const _Anchor& anchor);
+ Emitter& Write(const _Tag& tag);
+ Emitter& Write(const _Comment& comment);
+ Emitter& Write(const _Null& n);
+ Emitter& Write(const Binary& binary);
+
+ template <typename T>
+ Emitter& WriteIntegralType(T value);
+
+ template <typename T>
+ Emitter& WriteStreamable(T value);
+
+ private:
+ template <typename T>
+ void SetStreamablePrecision(std::stringstream&) {}
+ std::size_t GetFloatPrecision() const;
+ std::size_t GetDoublePrecision() const;
+
+ void PrepareIntegralStream(std::stringstream& stream) const;
+ void StartedScalar();
+
+ private:
+ void EmitBeginDoc();
+ void EmitEndDoc();
+ void EmitBeginSeq();
+ void EmitEndSeq();
+ void EmitBeginMap();
+ void EmitEndMap();
+ void EmitNewline();
+ void EmitKindTag();
+ void EmitTag(bool verbatim, const _Tag& tag);
+
+ void PrepareNode(EmitterNodeType::value child);
+ void PrepareTopNode(EmitterNodeType::value child);
+ void FlowSeqPrepareNode(EmitterNodeType::value child);
+ void BlockSeqPrepareNode(EmitterNodeType::value child);
+
+ void FlowMapPrepareNode(EmitterNodeType::value child);
+
+ void FlowMapPrepareLongKey(EmitterNodeType::value child);
+ void FlowMapPrepareLongKeyValue(EmitterNodeType::value child);
+ void FlowMapPrepareSimpleKey(EmitterNodeType::value child);
+ void FlowMapPrepareSimpleKeyValue(EmitterNodeType::value child);
+
+ void BlockMapPrepareNode(EmitterNodeType::value child);
+
+ void BlockMapPrepareLongKey(EmitterNodeType::value child);
+ void BlockMapPrepareLongKeyValue(EmitterNodeType::value child);
+ void BlockMapPrepareSimpleKey(EmitterNodeType::value child);
+ void BlockMapPrepareSimpleKeyValue(EmitterNodeType::value child);
+
+ void SpaceOrIndentTo(bool requireSpace, std::size_t indent);
+
+ const char* ComputeFullBoolName(bool b) const;
+ const char* ComputeNullName() const;
+ bool CanEmitNewline() const;
+
+ private:
+ std::unique_ptr<EmitterState> m_pState;
+ ostream_wrapper m_stream;
+};
+
+template <typename T>
+inline Emitter& Emitter::WriteIntegralType(T value) {
+ if (!good())
+ return *this;
+
+ PrepareNode(EmitterNodeType::Scalar);
+
+ std::stringstream stream;
+ PrepareIntegralStream(stream);
+ stream << value;
+ m_stream << stream.str();
+
+ StartedScalar();
+
+ return *this;
+}
+
+template <typename T>
+inline Emitter& Emitter::WriteStreamable(T value) {
+ if (!good())
+ return *this;
+
+ PrepareNode(EmitterNodeType::Scalar);
+
+ std::stringstream stream;
+ SetStreamablePrecision<T>(stream);
+
+ bool special = false;
+ if (std::is_floating_point<T>::value) {
+ if ((std::numeric_limits<T>::has_quiet_NaN ||
+ std::numeric_limits<T>::has_signaling_NaN) &&
+ std::isnan(value)) {
+ special = true;
+ stream << ".nan";
+ } else if (std::numeric_limits<T>::has_infinity && std::isinf(value)) {
+ special = true;
+ if (std::signbit(value)) {
+ stream << "-.inf";
+ } else {
+ stream << ".inf";
+ }
+ }
+ }
+
+ if (!special) {
+ stream << value;
+ }
+ m_stream << stream.str();
+
+ StartedScalar();
+
+ return *this;
+}
+
+template <>
+inline void Emitter::SetStreamablePrecision<float>(std::stringstream& stream) {
+ stream.precision(static_cast<std::streamsize>(GetFloatPrecision()));
+}
+
+template <>
+inline void Emitter::SetStreamablePrecision<double>(std::stringstream& stream) {
+ stream.precision(static_cast<std::streamsize>(GetDoublePrecision()));
+}
+
+// overloads of insertion
+inline Emitter& operator<<(Emitter& emitter, const std::string& v) {
+ return emitter.Write(v);
+}
+inline Emitter& operator<<(Emitter& emitter, bool v) {
+ return emitter.Write(v);
+}
+inline Emitter& operator<<(Emitter& emitter, char v) {
+ return emitter.Write(v);
+}
+inline Emitter& operator<<(Emitter& emitter, unsigned char v) {
+ return emitter.Write(static_cast<char>(v));
+}
+inline Emitter& operator<<(Emitter& emitter, const _Alias& v) {
+ return emitter.Write(v);
+}
+inline Emitter& operator<<(Emitter& emitter, const _Anchor& v) {
+ return emitter.Write(v);
+}
+inline Emitter& operator<<(Emitter& emitter, const _Tag& v) {
+ return emitter.Write(v);
+}
+inline Emitter& operator<<(Emitter& emitter, const _Comment& v) {
+ return emitter.Write(v);
+}
+inline Emitter& operator<<(Emitter& emitter, const _Null& v) {
+ return emitter.Write(v);
+}
+inline Emitter& operator<<(Emitter& emitter, const Binary& b) {
+ return emitter.Write(b);
+}
+
+inline Emitter& operator<<(Emitter& emitter, const char* v) {
+ return emitter.Write(std::string(v));
+}
+
+inline Emitter& operator<<(Emitter& emitter, int v) {
+ return emitter.WriteIntegralType(v);
+}
+inline Emitter& operator<<(Emitter& emitter, unsigned int v) {
+ return emitter.WriteIntegralType(v);
+}
+inline Emitter& operator<<(Emitter& emitter, short v) {
+ return emitter.WriteIntegralType(v);
+}
+inline Emitter& operator<<(Emitter& emitter, unsigned short v) {
+ return emitter.WriteIntegralType(v);
+}
+inline Emitter& operator<<(Emitter& emitter, long v) {
+ return emitter.WriteIntegralType(v);
+}
+inline Emitter& operator<<(Emitter& emitter, unsigned long v) {
+ return emitter.WriteIntegralType(v);
+}
+inline Emitter& operator<<(Emitter& emitter, long long v) {
+ return emitter.WriteIntegralType(v);
+}
+inline Emitter& operator<<(Emitter& emitter, unsigned long long v) {
+ return emitter.WriteIntegralType(v);
+}
+
+inline Emitter& operator<<(Emitter& emitter, float v) {
+ return emitter.WriteStreamable(v);
+}
+inline Emitter& operator<<(Emitter& emitter, double v) {
+ return emitter.WriteStreamable(v);
+}
+
+inline Emitter& operator<<(Emitter& emitter, EMITTER_MANIP value) {
+ return emitter.SetLocalValue(value);
+}
+
+inline Emitter& operator<<(Emitter& emitter, _Indent indent) {
+ return emitter.SetLocalIndent(indent);
+}
+
+inline Emitter& operator<<(Emitter& emitter, _Precision precision) {
+ return emitter.SetLocalPrecision(precision);
+}
+} // namespace YAML
+
+#endif // EMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitterdef.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitterdef.h
new file mode 100644
index 0000000000..0b426957fa
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitterdef.h
@@ -0,0 +1,16 @@
+#ifndef EMITTERDEF_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define EMITTERDEF_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+namespace YAML {
+struct EmitterNodeType {
+ enum value { NoType, Property, Scalar, FlowSeq, BlockSeq, FlowMap, BlockMap };
+};
+}
+
+#endif // EMITTERDEF_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emittermanip.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emittermanip.h
new file mode 100644
index 0000000000..976d14950f
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emittermanip.h
@@ -0,0 +1,144 @@
+#ifndef EMITTERMANIP_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define EMITTERMANIP_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <string>
+
+namespace YAML {
+enum EMITTER_MANIP {
+ // general manipulators
+ Auto,
+ TagByKind,
+ Newline,
+
+ // output character set
+ EmitNonAscii,
+ EscapeNonAscii,
+ EscapeAsJson,
+
+ // string manipulators
+ // Auto, // duplicate
+ SingleQuoted,
+ DoubleQuoted,
+ Literal,
+
+ // null manipulators
+ LowerNull,
+ UpperNull,
+ CamelNull,
+ TildeNull,
+
+ // bool manipulators
+ YesNoBool, // yes, no
+ TrueFalseBool, // true, false
+ OnOffBool, // on, off
+ UpperCase, // TRUE, N
+ LowerCase, // f, yes
+ CamelCase, // No, Off
+ LongBool, // yes, On
+ ShortBool, // y, t
+
+ // int manipulators
+ Dec,
+ Hex,
+ Oct,
+
+ // document manipulators
+ BeginDoc,
+ EndDoc,
+
+ // sequence manipulators
+ BeginSeq,
+ EndSeq,
+ Flow,
+ Block,
+
+ // map manipulators
+ BeginMap,
+ EndMap,
+ Key,
+ Value,
+ // Flow, // duplicate
+ // Block, // duplicate
+ // Auto, // duplicate
+ LongKey
+};
+
+struct _Indent {
+ _Indent(int value_) : value(value_) {}
+ int value;
+};
+
+inline _Indent Indent(int value) { return _Indent(value); }
+
+struct _Alias {
+ _Alias(const std::string& content_) : content(content_) {}
+ std::string content;
+};
+
+inline _Alias Alias(const std::string& content) { return _Alias(content); }
+
+struct _Anchor {
+ _Anchor(const std::string& content_) : content(content_) {}
+ std::string content;
+};
+
+inline _Anchor Anchor(const std::string& content) { return _Anchor(content); }
+
+struct _Tag {
+ struct Type {
+ enum value { Verbatim, PrimaryHandle, NamedHandle };
+ };
+
+ explicit _Tag(const std::string& prefix_, const std::string& content_,
+ Type::value type_)
+ : prefix(prefix_), content(content_), type(type_) {}
+ std::string prefix;
+ std::string content;
+ Type::value type;
+};
+
+inline _Tag VerbatimTag(const std::string& content) {
+ return _Tag("", content, _Tag::Type::Verbatim);
+}
+
+inline _Tag LocalTag(const std::string& content) {
+ return _Tag("", content, _Tag::Type::PrimaryHandle);
+}
+
+inline _Tag LocalTag(const std::string& prefix, const std::string content) {
+ return _Tag(prefix, content, _Tag::Type::NamedHandle);
+}
+
+inline _Tag SecondaryTag(const std::string& content) {
+ return _Tag("", content, _Tag::Type::NamedHandle);
+}
+
+struct _Comment {
+ _Comment(const std::string& content_) : content(content_) {}
+ std::string content;
+};
+
+inline _Comment Comment(const std::string& content) { return _Comment(content); }
+
+struct _Precision {
+ _Precision(int floatPrecision_, int doublePrecision_)
+ : floatPrecision(floatPrecision_), doublePrecision(doublePrecision_) {}
+
+ int floatPrecision;
+ int doublePrecision;
+};
+
+inline _Precision FloatPrecision(int n) { return _Precision(n, -1); }
+
+inline _Precision DoublePrecision(int n) { return _Precision(-1, n); }
+
+inline _Precision Precision(int n) { return _Precision(n, n); }
+}
+
+#endif // EMITTERMANIP_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitterstyle.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitterstyle.h
new file mode 100644
index 0000000000..67bb3981b1
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitterstyle.h
@@ -0,0 +1,16 @@
+#ifndef EMITTERSTYLE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define EMITTERSTYLE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+namespace YAML {
+struct EmitterStyle {
+ enum value { Default, Block, Flow };
+};
+}
+
+#endif // EMITTERSTYLE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/eventhandler.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/eventhandler.h
new file mode 100644
index 0000000000..7242fe1f5b
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/eventhandler.h
@@ -0,0 +1,45 @@
+#ifndef EVENTHANDLER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define EVENTHANDLER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <string>
+
+#include "yaml-cpp/anchor.h"
+#include "yaml-cpp/emitterstyle.h"
+
+namespace YAML {
+struct Mark;
+
+class EventHandler {
+ public:
+ virtual ~EventHandler() = default;
+
+ virtual void OnDocumentStart(const Mark& mark) = 0;
+ virtual void OnDocumentEnd() = 0;
+
+ virtual void OnNull(const Mark& mark, anchor_t anchor) = 0;
+ virtual void OnAlias(const Mark& mark, anchor_t anchor) = 0;
+ virtual void OnScalar(const Mark& mark, const std::string& tag,
+ anchor_t anchor, const std::string& value) = 0;
+
+ virtual void OnSequenceStart(const Mark& mark, const std::string& tag,
+ anchor_t anchor, EmitterStyle::value style) = 0;
+ virtual void OnSequenceEnd() = 0;
+
+ virtual void OnMapStart(const Mark& mark, const std::string& tag,
+ anchor_t anchor, EmitterStyle::value style) = 0;
+ virtual void OnMapEnd() = 0;
+
+ virtual void OnAnchor(const Mark& /*mark*/,
+ const std::string& /*anchor_name*/) {
+ // empty default implementation for compatibility
+ }
+};
+} // namespace YAML
+
+#endif // EVENTHANDLER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h
new file mode 100644
index 0000000000..f6b2602ae1
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h
@@ -0,0 +1,303 @@
+#ifndef EXCEPTIONS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define EXCEPTIONS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/mark.h"
+#include "yaml-cpp/noexcept.h"
+#include "yaml-cpp/traits.h"
+#include <sstream>
+#include <stdexcept>
+#include <string>
+
+namespace YAML {
+// error messages
+namespace ErrorMsg {
+const char* const YAML_DIRECTIVE_ARGS =
+ "YAML directives must have exactly one argument";
+const char* const YAML_VERSION = "bad YAML version: ";
+const char* const YAML_MAJOR_VERSION = "YAML major version too large";
+const char* const REPEATED_YAML_DIRECTIVE = "repeated YAML directive";
+const char* const TAG_DIRECTIVE_ARGS =
+ "TAG directives must have exactly two arguments";
+const char* const REPEATED_TAG_DIRECTIVE = "repeated TAG directive";
+const char* const CHAR_IN_TAG_HANDLE =
+ "illegal character found while scanning tag handle";
+const char* const TAG_WITH_NO_SUFFIX = "tag handle with no suffix";
+const char* const END_OF_VERBATIM_TAG = "end of verbatim tag not found";
+const char* const END_OF_MAP = "end of map not found";
+const char* const END_OF_MAP_FLOW = "end of map flow not found";
+const char* const END_OF_SEQ = "end of sequence not found";
+const char* const END_OF_SEQ_FLOW = "end of sequence flow not found";
+const char* const MULTIPLE_TAGS =
+ "cannot assign multiple tags to the same node";
+const char* const MULTIPLE_ANCHORS =
+ "cannot assign multiple anchors to the same node";
+const char* const MULTIPLE_ALIASES =
+ "cannot assign multiple aliases to the same node";
+const char* const ALIAS_CONTENT =
+ "aliases can't have any content, *including* tags";
+const char* const INVALID_HEX = "bad character found while scanning hex number";
+const char* const INVALID_UNICODE = "invalid unicode: ";
+const char* const INVALID_ESCAPE = "unknown escape character: ";
+const char* const UNKNOWN_TOKEN = "unknown token";
+const char* const DOC_IN_SCALAR = "illegal document indicator in scalar";
+const char* const EOF_IN_SCALAR = "illegal EOF in scalar";
+const char* const CHAR_IN_SCALAR = "illegal character in scalar";
+const char* const TAB_IN_INDENTATION =
+ "illegal tab when looking for indentation";
+const char* const FLOW_END = "illegal flow end";
+const char* const BLOCK_ENTRY = "illegal block entry";
+const char* const MAP_KEY = "illegal map key";
+const char* const MAP_VALUE = "illegal map value";
+const char* const ALIAS_NOT_FOUND = "alias not found after *";
+const char* const ANCHOR_NOT_FOUND = "anchor not found after &";
+const char* const CHAR_IN_ALIAS =
+ "illegal character found while scanning alias";
+const char* const CHAR_IN_ANCHOR =
+ "illegal character found while scanning anchor";
+const char* const ZERO_INDENT_IN_BLOCK =
+ "cannot set zero indentation for a block scalar";
+const char* const CHAR_IN_BLOCK = "unexpected character in block scalar";
+const char* const AMBIGUOUS_ANCHOR =
+ "cannot assign the same alias to multiple nodes";
+const char* const UNKNOWN_ANCHOR = "the referenced anchor is not defined: ";
+
+const char* const INVALID_NODE =
+ "invalid node; this may result from using a map iterator as a sequence "
+ "iterator, or vice-versa";
+const char* const INVALID_SCALAR = "invalid scalar";
+const char* const KEY_NOT_FOUND = "key not found";
+const char* const BAD_CONVERSION = "bad conversion";
+const char* const BAD_DEREFERENCE = "bad dereference";
+const char* const BAD_SUBSCRIPT = "operator[] call on a scalar";
+const char* const BAD_PUSHBACK = "appending to a non-sequence";
+const char* const BAD_INSERT = "inserting in a non-convertible-to-map";
+
+const char* const UNMATCHED_GROUP_TAG = "unmatched group tag";
+const char* const UNEXPECTED_END_SEQ = "unexpected end sequence token";
+const char* const UNEXPECTED_END_MAP = "unexpected end map token";
+const char* const SINGLE_QUOTED_CHAR =
+ "invalid character in single-quoted string";
+const char* const INVALID_ANCHOR = "invalid anchor";
+const char* const INVALID_ALIAS = "invalid alias";
+const char* const INVALID_TAG = "invalid tag";
+const char* const BAD_FILE = "bad file";
+
+template <typename T>
+inline const std::string KEY_NOT_FOUND_WITH_KEY(
+ const T&, typename disable_if<is_numeric<T>>::type* = 0) {
+ return KEY_NOT_FOUND;
+}
+
+inline const std::string KEY_NOT_FOUND_WITH_KEY(const std::string& key) {
+ std::stringstream stream;
+ stream << KEY_NOT_FOUND << ": " << key;
+ return stream.str();
+}
+
+inline const std::string KEY_NOT_FOUND_WITH_KEY(const char* key) {
+ std::stringstream stream;
+ stream << KEY_NOT_FOUND << ": " << key;
+ return stream.str();
+}
+
+template <typename T>
+inline const std::string KEY_NOT_FOUND_WITH_KEY(
+ const T& key, typename enable_if<is_numeric<T>>::type* = 0) {
+ std::stringstream stream;
+ stream << KEY_NOT_FOUND << ": " << key;
+ return stream.str();
+}
+
+template <typename T>
+inline const std::string BAD_SUBSCRIPT_WITH_KEY(
+ const T&, typename disable_if<is_numeric<T>>::type* = nullptr) {
+ return BAD_SUBSCRIPT;
+}
+
+inline const std::string BAD_SUBSCRIPT_WITH_KEY(const std::string& key) {
+ std::stringstream stream;
+ stream << BAD_SUBSCRIPT << " (key: \"" << key << "\")";
+ return stream.str();
+}
+
+inline const std::string BAD_SUBSCRIPT_WITH_KEY(const char* key) {
+ std::stringstream stream;
+ stream << BAD_SUBSCRIPT << " (key: \"" << key << "\")";
+ return stream.str();
+}
+
+template <typename T>
+inline const std::string BAD_SUBSCRIPT_WITH_KEY(
+ const T& key, typename enable_if<is_numeric<T>>::type* = nullptr) {
+ std::stringstream stream;
+ stream << BAD_SUBSCRIPT << " (key: \"" << key << "\")";
+ return stream.str();
+}
+
+inline const std::string INVALID_NODE_WITH_KEY(const std::string& key) {
+ std::stringstream stream;
+ if (key.empty()) {
+ return INVALID_NODE;
+ }
+ stream << "invalid node; first invalid key: \"" << key << "\"";
+ return stream.str();
+}
+} // namespace ErrorMsg
+
+class YAML_CPP_API Exception : public std::runtime_error {
+ public:
+ Exception(const Mark& mark_, const std::string& msg_)
+ : std::runtime_error(build_what(mark_, msg_)), mark(mark_), msg(msg_) {}
+ ~Exception() YAML_CPP_NOEXCEPT override;
+
+ Exception(const Exception&) = default;
+
+ Mark mark;
+ std::string msg;
+
+ private:
+ static const std::string build_what(const Mark& mark,
+ const std::string& msg) {
+ if (mark.is_null()) {
+ return msg;
+ }
+
+ std::stringstream output;
+ output << "yaml-cpp: error at line " << mark.line + 1 << ", column "
+ << mark.column + 1 << ": " << msg;
+ return output.str();
+ }
+};
+
+class YAML_CPP_API ParserException : public Exception {
+ public:
+ ParserException(const Mark& mark_, const std::string& msg_)
+ : Exception(mark_, msg_) {}
+ ParserException(const ParserException&) = default;
+ ~ParserException() YAML_CPP_NOEXCEPT override;
+};
+
+class YAML_CPP_API RepresentationException : public Exception {
+ public:
+ RepresentationException(const Mark& mark_, const std::string& msg_)
+ : Exception(mark_, msg_) {}
+ RepresentationException(const RepresentationException&) = default;
+ ~RepresentationException() YAML_CPP_NOEXCEPT override;
+};
+
+// representation exceptions
+class YAML_CPP_API InvalidScalar : public RepresentationException {
+ public:
+ InvalidScalar(const Mark& mark_)
+ : RepresentationException(mark_, ErrorMsg::INVALID_SCALAR) {}
+ InvalidScalar(const InvalidScalar&) = default;
+ ~InvalidScalar() YAML_CPP_NOEXCEPT override;
+};
+
+class YAML_CPP_API KeyNotFound : public RepresentationException {
+ public:
+ template <typename T>
+ KeyNotFound(const Mark& mark_, const T& key_)
+ : RepresentationException(mark_, ErrorMsg::KEY_NOT_FOUND_WITH_KEY(key_)) {
+ }
+ KeyNotFound(const KeyNotFound&) = default;
+ ~KeyNotFound() YAML_CPP_NOEXCEPT override;
+};
+
+template <typename T>
+class YAML_CPP_API TypedKeyNotFound : public KeyNotFound {
+ public:
+ TypedKeyNotFound(const Mark& mark_, const T& key_)
+ : KeyNotFound(mark_, key_), key(key_) {}
+ ~TypedKeyNotFound() YAML_CPP_NOEXCEPT override = default;
+
+ T key;
+};
+
+template <typename T>
+inline TypedKeyNotFound<T> MakeTypedKeyNotFound(const Mark& mark,
+ const T& key) {
+ return TypedKeyNotFound<T>(mark, key);
+}
+
+class YAML_CPP_API InvalidNode : public RepresentationException {
+ public:
+ InvalidNode(const std::string& key)
+ : RepresentationException(Mark::null_mark(),
+ ErrorMsg::INVALID_NODE_WITH_KEY(key)) {}
+ InvalidNode(const InvalidNode&) = default;
+ ~InvalidNode() YAML_CPP_NOEXCEPT override;
+};
+
+class YAML_CPP_API BadConversion : public RepresentationException {
+ public:
+ explicit BadConversion(const Mark& mark_)
+ : RepresentationException(mark_, ErrorMsg::BAD_CONVERSION) {}
+ BadConversion(const BadConversion&) = default;
+ ~BadConversion() YAML_CPP_NOEXCEPT override;
+};
+
+template <typename T>
+class TypedBadConversion : public BadConversion {
+ public:
+ explicit TypedBadConversion(const Mark& mark_) : BadConversion(mark_) {}
+};
+
+class YAML_CPP_API BadDereference : public RepresentationException {
+ public:
+ BadDereference()
+ : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_DEREFERENCE) {}
+ BadDereference(const BadDereference&) = default;
+ ~BadDereference() YAML_CPP_NOEXCEPT override;
+};
+
+class YAML_CPP_API BadSubscript : public RepresentationException {
+ public:
+ template <typename Key>
+ BadSubscript(const Mark& mark_, const Key& key)
+ : RepresentationException(mark_, ErrorMsg::BAD_SUBSCRIPT_WITH_KEY(key)) {}
+ BadSubscript(const BadSubscript&) = default;
+ ~BadSubscript() YAML_CPP_NOEXCEPT override;
+};
+
+class YAML_CPP_API BadPushback : public RepresentationException {
+ public:
+ BadPushback()
+ : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_PUSHBACK) {}
+ BadPushback(const BadPushback&) = default;
+ ~BadPushback() YAML_CPP_NOEXCEPT override;
+};
+
+class YAML_CPP_API BadInsert : public RepresentationException {
+ public:
+ BadInsert()
+ : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_INSERT) {}
+ BadInsert(const BadInsert&) = default;
+ ~BadInsert() YAML_CPP_NOEXCEPT override;
+};
+
+class YAML_CPP_API EmitterException : public Exception {
+ public:
+ EmitterException(const std::string& msg_)
+ : Exception(Mark::null_mark(), msg_) {}
+ EmitterException(const EmitterException&) = default;
+ ~EmitterException() YAML_CPP_NOEXCEPT override;
+};
+
+class YAML_CPP_API BadFile : public Exception {
+ public:
+ explicit BadFile(const std::string& filename)
+ : Exception(Mark::null_mark(),
+ std::string(ErrorMsg::BAD_FILE) + ": " + filename) {}
+ BadFile(const BadFile&) = default;
+ ~BadFile() YAML_CPP_NOEXCEPT override;
+};
+} // namespace YAML
+
+#endif // EXCEPTIONS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/mark.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/mark.h
new file mode 100644
index 0000000000..bf94b4f41f
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/mark.h
@@ -0,0 +1,29 @@
+#ifndef MARK_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define MARK_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/dll.h"
+
+namespace YAML {
+struct YAML_CPP_API Mark {
+ Mark() : pos(0), line(0), column(0) {}
+
+ static const Mark null_mark() { return Mark(-1, -1, -1); }
+
+ bool is_null() const { return pos == -1 && line == -1 && column == -1; }
+
+ int pos;
+ int line, column;
+
+ private:
+ Mark(int pos_, int line_, int column_)
+ : pos(pos_), line(line_), column(column_) {}
+};
+}
+
+#endif // MARK_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/convert.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/convert.h
new file mode 100644
index 0000000000..d0eb450f73
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/convert.h
@@ -0,0 +1,469 @@
+#ifndef NODE_CONVERT_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define NODE_CONVERT_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <array>
+#include <cmath>
+#include <limits>
+#include <list>
+#include <map>
+#include <unordered_map>
+#include <sstream>
+#include <type_traits>
+#include <valarray>
+#include <vector>
+
+#if __cplusplus >= 201703L
+#include <string_view>
+#endif
+
+#include "yaml-cpp/binary.h"
+#include "yaml-cpp/node/impl.h"
+#include "yaml-cpp/node/iterator.h"
+#include "yaml-cpp/node/node.h"
+#include "yaml-cpp/node/type.h"
+#include "yaml-cpp/null.h"
+
+
+namespace YAML {
+class Binary;
+struct _Null;
+template <typename T>
+struct convert;
+} // namespace YAML
+
+namespace YAML {
+namespace conversion {
+inline bool IsInfinity(const std::string& input) {
+ return input == ".inf" || input == ".Inf" || input == ".INF" ||
+ input == "+.inf" || input == "+.Inf" || input == "+.INF";
+}
+
+inline bool IsNegativeInfinity(const std::string& input) {
+ return input == "-.inf" || input == "-.Inf" || input == "-.INF";
+}
+
+inline bool IsNaN(const std::string& input) {
+ return input == ".nan" || input == ".NaN" || input == ".NAN";
+}
+}
+
+// Node
+template <>
+struct convert<Node> {
+ static Node encode(const Node& rhs) { return rhs; }
+
+ static bool decode(const Node& node, Node& rhs) {
+ rhs.reset(node);
+ return true;
+ }
+};
+
+// std::string
+template <>
+struct convert<std::string> {
+ static Node encode(const std::string& rhs) { return Node(rhs); }
+
+ static bool decode(const Node& node, std::string& rhs) {
+ if (!node.IsScalar())
+ return false;
+ rhs = node.Scalar();
+ return true;
+ }
+};
+
+// C-strings can only be encoded
+template <>
+struct convert<const char*> {
+ static Node encode(const char* rhs) { return Node(rhs); }
+};
+
+template <>
+struct convert<char*> {
+ static Node encode(const char* rhs) { return Node(rhs); }
+};
+
+template <std::size_t N>
+struct convert<char[N]> {
+ static Node encode(const char* rhs) { return Node(rhs); }
+};
+
+#if __cplusplus >= 201703L
+template <>
+struct convert<std::string_view> {
+ static Node encode(std::string_view rhs) { return Node(std::string(rhs)); }
+
+ static bool decode(const Node& node, std::string_view& rhs) {
+ if (!node.IsScalar())
+ return false;
+ rhs = node.Scalar();
+ return true;
+ }
+};
+#endif
+
+template <>
+struct convert<_Null> {
+ static Node encode(const _Null& /* rhs */) { return Node(); }
+
+ static bool decode(const Node& node, _Null& /* rhs */) {
+ return node.IsNull();
+ }
+};
+
+namespace conversion {
+template <typename T>
+typename std::enable_if< std::is_floating_point<T>::value, void>::type
+inner_encode(const T& rhs, std::stringstream& stream){
+ if (std::isnan(rhs)) {
+ stream << ".nan";
+ } else if (std::isinf(rhs)) {
+ if (std::signbit(rhs)) {
+ stream << "-.inf";
+ } else {
+ stream << ".inf";
+ }
+ } else {
+ stream << rhs;
+ }
+}
+
+template <typename T>
+typename std::enable_if<!std::is_floating_point<T>::value, void>::type
+inner_encode(const T& rhs, std::stringstream& stream){
+ stream << rhs;
+}
+
+template <typename T>
+typename std::enable_if<(std::is_same<T, unsigned char>::value ||
+ std::is_same<T, signed char>::value), bool>::type
+ConvertStreamTo(std::stringstream& stream, T& rhs) {
+ int num;
+ if ((stream >> std::noskipws >> num) && (stream >> std::ws).eof()) {
+ if (num >= (std::numeric_limits<T>::min)() &&
+ num <= (std::numeric_limits<T>::max)()) {
+ rhs = static_cast<T>(num);
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename T>
+typename std::enable_if<!(std::is_same<T, unsigned char>::value ||
+ std::is_same<T, signed char>::value), bool>::type
+ConvertStreamTo(std::stringstream& stream, T& rhs) {
+ if ((stream >> std::noskipws >> rhs) && (stream >> std::ws).eof()) {
+ return true;
+ }
+ return false;
+}
+}
+
+#define YAML_DEFINE_CONVERT_STREAMABLE(type, negative_op) \
+ template <> \
+ struct convert<type> { \
+ \
+ static Node encode(const type& rhs) { \
+ std::stringstream stream; \
+ stream.precision(std::numeric_limits<type>::max_digits10); \
+ conversion::inner_encode(rhs, stream); \
+ return Node(stream.str()); \
+ } \
+ \
+ static bool decode(const Node& node, type& rhs) { \
+ if (node.Type() != NodeType::Scalar) { \
+ return false; \
+ } \
+ const std::string& input = node.Scalar(); \
+ std::stringstream stream(input); \
+ stream.unsetf(std::ios::dec); \
+ if ((stream.peek() == '-') && std::is_unsigned<type>::value) { \
+ return false; \
+ } \
+ if (conversion::ConvertStreamTo(stream, rhs)) { \
+ return true; \
+ } \
+ if (std::numeric_limits<type>::has_infinity) { \
+ if (conversion::IsInfinity(input)) { \
+ rhs = std::numeric_limits<type>::infinity(); \
+ return true; \
+ } else if (conversion::IsNegativeInfinity(input)) { \
+ rhs = negative_op std::numeric_limits<type>::infinity(); \
+ return true; \
+ } \
+ } \
+ \
+ if (std::numeric_limits<type>::has_quiet_NaN) { \
+ if (conversion::IsNaN(input)) { \
+ rhs = std::numeric_limits<type>::quiet_NaN(); \
+ return true; \
+ } \
+ } \
+ \
+ return false; \
+ } \
+ }
+
+#define YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(type) \
+ YAML_DEFINE_CONVERT_STREAMABLE(type, -)
+
+#define YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(type) \
+ YAML_DEFINE_CONVERT_STREAMABLE(type, +)
+
+YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(int);
+YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(short);
+YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long);
+YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long long);
+YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned);
+YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned short);
+YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned long);
+YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned long long);
+
+YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(char);
+YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(signed char);
+YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED(unsigned char);
+
+YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(float);
+YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(double);
+YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(long double);
+
+#undef YAML_DEFINE_CONVERT_STREAMABLE_SIGNED
+#undef YAML_DEFINE_CONVERT_STREAMABLE_UNSIGNED
+#undef YAML_DEFINE_CONVERT_STREAMABLE
+
+// bool
+template <>
+struct convert<bool> {
+ static Node encode(bool rhs) { return rhs ? Node("true") : Node("false"); }
+
+ YAML_CPP_API static bool decode(const Node& node, bool& rhs);
+};
+
+// std::map
+template <typename K, typename V, typename C, typename A>
+struct convert<std::map<K, V, C, A>> {
+ static Node encode(const std::map<K, V, C, A>& rhs) {
+ Node node(NodeType::Map);
+ for (const auto& element : rhs)
+ node.force_insert(element.first, element.second);
+ return node;
+ }
+
+ static bool decode(const Node& node, std::map<K, V, C, A>& rhs) {
+ if (!node.IsMap())
+ return false;
+
+ rhs.clear();
+ for (const auto& element : node)
+#if defined(__GNUC__) && __GNUC__ < 4
+ // workaround for GCC 3:
+ rhs[element.first.template as<K>()] = element.second.template as<V>();
+#else
+ rhs[element.first.as<K>()] = element.second.as<V>();
+#endif
+ return true;
+ }
+};
+
+// std::unordered_map
+template <typename K, typename V, typename H, typename P, typename A>
+struct convert<std::unordered_map<K, V, H, P, A>> {
+ static Node encode(const std::unordered_map<K, V, H, P, A>& rhs) {
+ Node node(NodeType::Map);
+ for (const auto& element : rhs)
+ node.force_insert(element.first, element.second);
+ return node;
+ }
+
+ static bool decode(const Node& node, std::unordered_map<K, V, H, P, A>& rhs) {
+ if (!node.IsMap())
+ return false;
+
+ rhs.clear();
+ for (const auto& element : node)
+#if defined(__GNUC__) && __GNUC__ < 4
+ // workaround for GCC 3:
+ rhs[element.first.template as<K>()] = element.second.template as<V>();
+#else
+ rhs[element.first.as<K>()] = element.second.as<V>();
+#endif
+ return true;
+ }
+};
+
+// std::vector
+template <typename T, typename A>
+struct convert<std::vector<T, A>> {
+ static Node encode(const std::vector<T, A>& rhs) {
+ Node node(NodeType::Sequence);
+ for (const auto& element : rhs)
+ node.push_back(element);
+ return node;
+ }
+
+ static bool decode(const Node& node, std::vector<T, A>& rhs) {
+ if (!node.IsSequence())
+ return false;
+
+ rhs.clear();
+ for (const auto& element : node)
+#if defined(__GNUC__) && __GNUC__ < 4
+ // workaround for GCC 3:
+ rhs.push_back(element.template as<T>());
+#else
+ rhs.push_back(element.as<T>());
+#endif
+ return true;
+ }
+};
+
+// std::list
+template <typename T, typename A>
+struct convert<std::list<T,A>> {
+ static Node encode(const std::list<T,A>& rhs) {
+ Node node(NodeType::Sequence);
+ for (const auto& element : rhs)
+ node.push_back(element);
+ return node;
+ }
+
+ static bool decode(const Node& node, std::list<T,A>& rhs) {
+ if (!node.IsSequence())
+ return false;
+
+ rhs.clear();
+ for (const auto& element : node)
+#if defined(__GNUC__) && __GNUC__ < 4
+ // workaround for GCC 3:
+ rhs.push_back(element.template as<T>());
+#else
+ rhs.push_back(element.as<T>());
+#endif
+ return true;
+ }
+};
+
+// std::array
+template <typename T, std::size_t N>
+struct convert<std::array<T, N>> {
+ static Node encode(const std::array<T, N>& rhs) {
+ Node node(NodeType::Sequence);
+ for (const auto& element : rhs) {
+ node.push_back(element);
+ }
+ return node;
+ }
+
+ static bool decode(const Node& node, std::array<T, N>& rhs) {
+ if (!isNodeValid(node)) {
+ return false;
+ }
+
+ for (auto i = 0u; i < node.size(); ++i) {
+#if defined(__GNUC__) && __GNUC__ < 4
+ // workaround for GCC 3:
+ rhs[i] = node[i].template as<T>();
+#else
+ rhs[i] = node[i].as<T>();
+#endif
+ }
+ return true;
+ }
+
+ private:
+ static bool isNodeValid(const Node& node) {
+ return node.IsSequence() && node.size() == N;
+ }
+};
+
+
+// std::valarray
+template <typename T>
+struct convert<std::valarray<T>> {
+ static Node encode(const std::valarray<T>& rhs) {
+ Node node(NodeType::Sequence);
+ for (const auto& element : rhs) {
+ node.push_back(element);
+ }
+ return node;
+ }
+
+ static bool decode(const Node& node, std::valarray<T>& rhs) {
+ if (!node.IsSequence()) {
+ return false;
+ }
+
+ rhs.resize(node.size());
+ for (auto i = 0u; i < node.size(); ++i) {
+#if defined(__GNUC__) && __GNUC__ < 4
+ // workaround for GCC 3:
+ rhs[i] = node[i].template as<T>();
+#else
+ rhs[i] = node[i].as<T>();
+#endif
+ }
+ return true;
+ }
+};
+
+
+// std::pair
+template <typename T, typename U>
+struct convert<std::pair<T, U>> {
+ static Node encode(const std::pair<T, U>& rhs) {
+ Node node(NodeType::Sequence);
+ node.push_back(rhs.first);
+ node.push_back(rhs.second);
+ return node;
+ }
+
+ static bool decode(const Node& node, std::pair<T, U>& rhs) {
+ if (!node.IsSequence())
+ return false;
+ if (node.size() != 2)
+ return false;
+
+#if defined(__GNUC__) && __GNUC__ < 4
+ // workaround for GCC 3:
+ rhs.first = node[0].template as<T>();
+#else
+ rhs.first = node[0].as<T>();
+#endif
+#if defined(__GNUC__) && __GNUC__ < 4
+ // workaround for GCC 3:
+ rhs.second = node[1].template as<U>();
+#else
+ rhs.second = node[1].as<U>();
+#endif
+ return true;
+ }
+};
+
+// binary
+template <>
+struct convert<Binary> {
+ static Node encode(const Binary& rhs) {
+ return Node(EncodeBase64(rhs.data(), rhs.size()));
+ }
+
+ static bool decode(const Node& node, Binary& rhs) {
+ if (!node.IsScalar())
+ return false;
+
+ std::vector<unsigned char> data = DecodeBase64(node.Scalar());
+ if (data.empty() && !node.Scalar().empty())
+ return false;
+
+ rhs.swap(data);
+ return true;
+ }
+};
+}
+
+#endif // NODE_CONVERT_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/impl.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/impl.h
new file mode 100644
index 0000000000..b38038dfd2
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/impl.h
@@ -0,0 +1,235 @@
+#ifndef NODE_DETAIL_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define NODE_DETAIL_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/node/detail/node.h"
+#include "yaml-cpp/node/detail/node_data.h"
+
+#include <algorithm>
+#include <type_traits>
+
+namespace YAML {
+namespace detail {
+template <typename Key, typename Enable = void>
+struct get_idx {
+ static node* get(const std::vector<node*>& /* sequence */,
+ const Key& /* key */, shared_memory_holder /* pMemory */) {
+ return nullptr;
+ }
+};
+
+template <typename Key>
+struct get_idx<Key,
+ typename std::enable_if<std::is_unsigned<Key>::value &&
+ !std::is_same<Key, bool>::value>::type> {
+ static node* get(const std::vector<node*>& sequence, const Key& key,
+ shared_memory_holder /* pMemory */) {
+ return key < sequence.size() ? sequence[key] : nullptr;
+ }
+
+ static node* get(std::vector<node*>& sequence, const Key& key,
+ shared_memory_holder pMemory) {
+ if (key > sequence.size() || (key > 0 && !sequence[key - 1]->is_defined()))
+ return nullptr;
+ if (key == sequence.size())
+ sequence.push_back(&pMemory->create_node());
+ return sequence[key];
+ }
+};
+
+template <typename Key>
+struct get_idx<Key, typename std::enable_if<std::is_signed<Key>::value>::type> {
+ static node* get(const std::vector<node*>& sequence, const Key& key,
+ shared_memory_holder pMemory) {
+ return key >= 0 ? get_idx<std::size_t>::get(
+ sequence, static_cast<std::size_t>(key), pMemory)
+ : nullptr;
+ }
+ static node* get(std::vector<node*>& sequence, const Key& key,
+ shared_memory_holder pMemory) {
+ return key >= 0 ? get_idx<std::size_t>::get(
+ sequence, static_cast<std::size_t>(key), pMemory)
+ : nullptr;
+ }
+};
+
+template <typename Key, typename Enable = void>
+struct remove_idx {
+ static bool remove(std::vector<node*>&, const Key&, std::size_t&) {
+ return false;
+ }
+};
+
+template <typename Key>
+struct remove_idx<
+ Key, typename std::enable_if<std::is_unsigned<Key>::value &&
+ !std::is_same<Key, bool>::value>::type> {
+
+ static bool remove(std::vector<node*>& sequence, const Key& key,
+ std::size_t& seqSize) {
+ if (key >= sequence.size()) {
+ return false;
+ } else {
+ sequence.erase(sequence.begin() + key);
+ if (seqSize > key) {
+ --seqSize;
+ }
+ return true;
+ }
+ }
+};
+
+template <typename Key>
+struct remove_idx<Key,
+ typename std::enable_if<std::is_signed<Key>::value>::type> {
+
+ static bool remove(std::vector<node*>& sequence, const Key& key,
+ std::size_t& seqSize) {
+ return key >= 0 ? remove_idx<std::size_t>::remove(
+ sequence, static_cast<std::size_t>(key), seqSize)
+ : false;
+ }
+};
+
+template <typename T>
+inline bool node::equals(const T& rhs, shared_memory_holder pMemory) {
+ T lhs;
+ if (convert<T>::decode(Node(*this, pMemory), lhs)) {
+ return lhs == rhs;
+ }
+ return false;
+}
+
+inline bool node::equals(const char* rhs, shared_memory_holder pMemory) {
+ std::string lhs;
+ if (convert<std::string>::decode(Node(*this, std::move(pMemory)), lhs)) {
+ return lhs == rhs;
+ }
+ return false;
+}
+
+// indexing
+template <typename Key>
+inline node* node_data::get(const Key& key,
+ shared_memory_holder pMemory) const {
+ switch (m_type) {
+ case NodeType::Map:
+ break;
+ case NodeType::Undefined:
+ case NodeType::Null:
+ return nullptr;
+ case NodeType::Sequence:
+ if (node* pNode = get_idx<Key>::get(m_sequence, key, pMemory))
+ return pNode;
+ return nullptr;
+ case NodeType::Scalar:
+ throw BadSubscript(m_mark, key);
+ }
+
+ auto it = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) {
+ return m.first->equals(key, pMemory);
+ });
+
+ return it != m_map.end() ? it->second : nullptr;
+}
+
+template <typename Key>
+inline node& node_data::get(const Key& key, shared_memory_holder pMemory) {
+ switch (m_type) {
+ case NodeType::Map:
+ break;
+ case NodeType::Undefined:
+ case NodeType::Null:
+ case NodeType::Sequence:
+ if (node* pNode = get_idx<Key>::get(m_sequence, key, pMemory)) {
+ m_type = NodeType::Sequence;
+ return *pNode;
+ }
+
+ convert_to_map(pMemory);
+ break;
+ case NodeType::Scalar:
+ throw BadSubscript(m_mark, key);
+ }
+
+ auto it = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) {
+ return m.first->equals(key, pMemory);
+ });
+
+ if (it != m_map.end()) {
+ return *it->second;
+ }
+
+ node& k = convert_to_node(key, pMemory);
+ node& v = pMemory->create_node();
+ insert_map_pair(k, v);
+ return v;
+}
+
+template <typename Key>
+inline bool node_data::remove(const Key& key, shared_memory_holder pMemory) {
+ if (m_type == NodeType::Sequence) {
+ return remove_idx<Key>::remove(m_sequence, key, m_seqSize);
+ }
+
+ if (m_type == NodeType::Map) {
+ kv_pairs::iterator it = m_undefinedPairs.begin();
+ while (it != m_undefinedPairs.end()) {
+ kv_pairs::iterator jt = std::next(it);
+ if (it->first->equals(key, pMemory)) {
+ m_undefinedPairs.erase(it);
+ }
+ it = jt;
+ }
+
+ auto iter = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) {
+ return m.first->equals(key, pMemory);
+ });
+
+ if (iter != m_map.end()) {
+ m_map.erase(iter);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// map
+template <typename Key, typename Value>
+inline void node_data::force_insert(const Key& key, const Value& value,
+ shared_memory_holder pMemory) {
+ switch (m_type) {
+ case NodeType::Map:
+ break;
+ case NodeType::Undefined:
+ case NodeType::Null:
+ case NodeType::Sequence:
+ convert_to_map(pMemory);
+ break;
+ case NodeType::Scalar:
+ throw BadInsert();
+ }
+
+ node& k = convert_to_node(key, pMemory);
+ node& v = convert_to_node(value, pMemory);
+ insert_map_pair(k, v);
+}
+
+template <typename T>
+inline node& node_data::convert_to_node(const T& rhs,
+ shared_memory_holder pMemory) {
+ Node value = convert<T>::encode(rhs);
+ value.EnsureNodeExists();
+ pMemory->merge(*value.m_pMemory);
+ return *value.m_pNode;
+}
+}
+}
+
+#endif // NODE_DETAIL_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator.h
new file mode 100644
index 0000000000..997c69a14c
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator.h
@@ -0,0 +1,96 @@
+#ifndef VALUE_DETAIL_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define VALUE_DETAIL_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/dll.h"
+#include "yaml-cpp/node/detail/node_iterator.h"
+#include "yaml-cpp/node/node.h"
+#include "yaml-cpp/node/ptr.h"
+#include <cstddef>
+#include <iterator>
+
+
+namespace YAML {
+namespace detail {
+struct iterator_value;
+
+template <typename V>
+class iterator_base {
+
+ private:
+ template <typename>
+ friend class iterator_base;
+ struct enabler {};
+ using base_type = node_iterator;
+
+ struct proxy {
+ explicit proxy(const V& x) : m_ref(x) {}
+ V* operator->() { return std::addressof(m_ref); }
+ operator V*() { return std::addressof(m_ref); }
+
+ V m_ref;
+ };
+
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = V;
+ using difference_type = std::ptrdiff_t;
+ using pointer = V*;
+ using reference = V;
+
+ public:
+ iterator_base() : m_iterator(), m_pMemory() {}
+ explicit iterator_base(base_type rhs, shared_memory_holder pMemory)
+ : m_iterator(rhs), m_pMemory(pMemory) {}
+
+ template <class W>
+ iterator_base(const iterator_base<W>& rhs,
+ typename std::enable_if<std::is_convertible<W*, V*>::value,
+ enabler>::type = enabler())
+ : m_iterator(rhs.m_iterator), m_pMemory(rhs.m_pMemory) {}
+
+ iterator_base<V>& operator++() {
+ ++m_iterator;
+ return *this;
+ }
+
+ iterator_base<V> operator++(int) {
+ iterator_base<V> iterator_pre(*this);
+ ++(*this);
+ return iterator_pre;
+ }
+
+ template <typename W>
+ bool operator==(const iterator_base<W>& rhs) const {
+ return m_iterator == rhs.m_iterator;
+ }
+
+ template <typename W>
+ bool operator!=(const iterator_base<W>& rhs) const {
+ return m_iterator != rhs.m_iterator;
+ }
+
+ value_type operator*() const {
+ const typename base_type::value_type& v = *m_iterator;
+ if (v.pNode)
+ return value_type(Node(*v, m_pMemory));
+ if (v.first && v.second)
+ return value_type(Node(*v.first, m_pMemory), Node(*v.second, m_pMemory));
+ return value_type();
+ }
+
+ proxy operator->() const { return proxy(**this); }
+
+ private:
+ base_type m_iterator;
+ shared_memory_holder m_pMemory;
+};
+} // namespace detail
+} // namespace YAML
+
+#endif // VALUE_DETAIL_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator_fwd.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator_fwd.h
new file mode 100644
index 0000000000..75c9de086c
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator_fwd.h
@@ -0,0 +1,27 @@
+#ifndef VALUE_DETAIL_ITERATOR_FWD_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define VALUE_DETAIL_ITERATOR_FWD_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/dll.h"
+#include <list>
+#include <utility>
+#include <vector>
+
+namespace YAML {
+
+namespace detail {
+struct iterator_value;
+template <typename V>
+class iterator_base;
+}
+
+using iterator = detail::iterator_base<detail::iterator_value>;
+using const_iterator = detail::iterator_base<const detail::iterator_value>;
+}
+
+#endif // VALUE_DETAIL_ITERATOR_FWD_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/memory.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/memory.h
new file mode 100644
index 0000000000..e881545bf2
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/memory.h
@@ -0,0 +1,47 @@
+#ifndef VALUE_DETAIL_MEMORY_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define VALUE_DETAIL_MEMORY_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <set>
+
+#include "yaml-cpp/dll.h"
+#include "yaml-cpp/node/ptr.h"
+
+namespace YAML {
+namespace detail {
+class node;
+} // namespace detail
+} // namespace YAML
+
+namespace YAML {
+namespace detail {
+class YAML_CPP_API memory {
+ public:
+ memory() : m_nodes{} {}
+ node& create_node();
+ void merge(const memory& rhs);
+
+ private:
+ using Nodes = std::set<shared_node>;
+ Nodes m_nodes;
+};
+
+class YAML_CPP_API memory_holder {
+ public:
+ memory_holder() : m_pMemory(new memory) {}
+
+ node& create_node() { return m_pMemory->create_node(); }
+ void merge(memory_holder& rhs);
+
+ private:
+ shared_memory m_pMemory;
+};
+} // namespace detail
+} // namespace YAML
+
+#endif // VALUE_DETAIL_MEMORY_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node.h
new file mode 100644
index 0000000000..acf60ffb6d
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node.h
@@ -0,0 +1,177 @@
+#ifndef NODE_DETAIL_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define NODE_DETAIL_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/dll.h"
+#include "yaml-cpp/emitterstyle.h"
+#include "yaml-cpp/node/detail/node_ref.h"
+#include "yaml-cpp/node/ptr.h"
+#include "yaml-cpp/node/type.h"
+#include <set>
+#include <atomic>
+
+namespace YAML {
+namespace detail {
+class node {
+ private:
+ struct less {
+ bool operator ()(const node* l, const node* r) const {return l->m_index < r->m_index;}
+ };
+
+ public:
+ node() : m_pRef(new node_ref), m_dependencies{}, m_index{} {}
+ node(const node&) = delete;
+ node& operator=(const node&) = delete;
+
+ bool is(const node& rhs) const { return m_pRef == rhs.m_pRef; }
+ const node_ref* ref() const { return m_pRef.get(); }
+
+ bool is_defined() const { return m_pRef->is_defined(); }
+ const Mark& mark() const { return m_pRef->mark(); }
+ NodeType::value type() const { return m_pRef->type(); }
+
+ const std::string& scalar() const { return m_pRef->scalar(); }
+ const std::string& tag() const { return m_pRef->tag(); }
+ EmitterStyle::value style() const { return m_pRef->style(); }
+
+ template <typename T>
+ bool equals(const T& rhs, shared_memory_holder pMemory);
+ bool equals(const char* rhs, shared_memory_holder pMemory);
+
+ void mark_defined() {
+ if (is_defined())
+ return;
+
+ m_pRef->mark_defined();
+ for (node* dependency : m_dependencies)
+ dependency->mark_defined();
+ m_dependencies.clear();
+ }
+
+ void add_dependency(node& rhs) {
+ if (is_defined())
+ rhs.mark_defined();
+ else
+ m_dependencies.insert(&rhs);
+ }
+
+ void set_ref(const node& rhs) {
+ if (rhs.is_defined())
+ mark_defined();
+ m_pRef = rhs.m_pRef;
+ }
+ void set_data(const node& rhs) {
+ if (rhs.is_defined())
+ mark_defined();
+ m_pRef->set_data(*rhs.m_pRef);
+ }
+
+ void set_mark(const Mark& mark) { m_pRef->set_mark(mark); }
+
+ void set_type(NodeType::value type) {
+ if (type != NodeType::Undefined)
+ mark_defined();
+ m_pRef->set_type(type);
+ }
+ void set_null() {
+ mark_defined();
+ m_pRef->set_null();
+ }
+ void set_scalar(const std::string& scalar) {
+ mark_defined();
+ m_pRef->set_scalar(scalar);
+ }
+ void set_tag(const std::string& tag) {
+ mark_defined();
+ m_pRef->set_tag(tag);
+ }
+
+ // style
+ void set_style(EmitterStyle::value style) {
+ mark_defined();
+ m_pRef->set_style(style);
+ }
+
+ // size/iterator
+ std::size_t size() const { return m_pRef->size(); }
+
+ const_node_iterator begin() const {
+ return static_cast<const node_ref&>(*m_pRef).begin();
+ }
+ node_iterator begin() { return m_pRef->begin(); }
+
+ const_node_iterator end() const {
+ return static_cast<const node_ref&>(*m_pRef).end();
+ }
+ node_iterator end() { return m_pRef->end(); }
+
+ // sequence
+ void push_back(node& input, shared_memory_holder pMemory) {
+ m_pRef->push_back(input, pMemory);
+ input.add_dependency(*this);
+ m_index = m_amount.fetch_add(1);
+ }
+ void insert(node& key, node& value, shared_memory_holder pMemory) {
+ m_pRef->insert(key, value, pMemory);
+ key.add_dependency(*this);
+ value.add_dependency(*this);
+ }
+
+ // indexing
+ template <typename Key>
+ node* get(const Key& key, shared_memory_holder pMemory) const {
+ // NOTE: this returns a non-const node so that the top-level Node can wrap
+ // it, and returns a pointer so that it can be nullptr (if there is no such
+ // key).
+ return static_cast<const node_ref&>(*m_pRef).get(key, pMemory);
+ }
+ template <typename Key>
+ node& get(const Key& key, shared_memory_holder pMemory) {
+ node& value = m_pRef->get(key, pMemory);
+ value.add_dependency(*this);
+ return value;
+ }
+ template <typename Key>
+ bool remove(const Key& key, shared_memory_holder pMemory) {
+ return m_pRef->remove(key, pMemory);
+ }
+
+ node* get(node& key, shared_memory_holder pMemory) const {
+ // NOTE: this returns a non-const node so that the top-level Node can wrap
+ // it, and returns a pointer so that it can be nullptr (if there is no such
+ // key).
+ return static_cast<const node_ref&>(*m_pRef).get(key, pMemory);
+ }
+ node& get(node& key, shared_memory_holder pMemory) {
+ node& value = m_pRef->get(key, pMemory);
+ key.add_dependency(*this);
+ value.add_dependency(*this);
+ return value;
+ }
+ bool remove(node& key, shared_memory_holder pMemory) {
+ return m_pRef->remove(key, pMemory);
+ }
+
+ // map
+ template <typename Key, typename Value>
+ void force_insert(const Key& key, const Value& value,
+ shared_memory_holder pMemory) {
+ m_pRef->force_insert(key, value, pMemory);
+ }
+
+ private:
+ shared_node_ref m_pRef;
+ using nodes = std::set<node*, less>;
+ nodes m_dependencies;
+ size_t m_index;
+ static YAML_CPP_API std::atomic<size_t> m_amount;
+};
+} // namespace detail
+} // namespace YAML
+
+#endif // NODE_DETAIL_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_data.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_data.h
new file mode 100644
index 0000000000..07cf81aa09
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_data.h
@@ -0,0 +1,127 @@
+#ifndef VALUE_DETAIL_NODE_DATA_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define VALUE_DETAIL_NODE_DATA_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <list>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "yaml-cpp/dll.h"
+#include "yaml-cpp/node/detail/node_iterator.h"
+#include "yaml-cpp/node/iterator.h"
+#include "yaml-cpp/node/ptr.h"
+#include "yaml-cpp/node/type.h"
+
+namespace YAML {
+namespace detail {
+class node;
+} // namespace detail
+} // namespace YAML
+
+namespace YAML {
+namespace detail {
+class YAML_CPP_API node_data {
+ public:
+ node_data();
+ node_data(const node_data&) = delete;
+ node_data& operator=(const node_data&) = delete;
+
+ void mark_defined();
+ void set_mark(const Mark& mark);
+ void set_type(NodeType::value type);
+ void set_tag(const std::string& tag);
+ void set_null();
+ void set_scalar(const std::string& scalar);
+ void set_style(EmitterStyle::value style);
+
+ bool is_defined() const { return m_isDefined; }
+ const Mark& mark() const { return m_mark; }
+ NodeType::value type() const {
+ return m_isDefined ? m_type : NodeType::Undefined;
+ }
+ const std::string& scalar() const { return m_scalar; }
+ const std::string& tag() const { return m_tag; }
+ EmitterStyle::value style() const { return m_style; }
+
+ // size/iterator
+ std::size_t size() const;
+
+ const_node_iterator begin() const;
+ node_iterator begin();
+
+ const_node_iterator end() const;
+ node_iterator end();
+
+ // sequence
+ void push_back(node& node, const shared_memory_holder& pMemory);
+ void insert(node& key, node& value, const shared_memory_holder& pMemory);
+
+ // indexing
+ template <typename Key>
+ node* get(const Key& key, shared_memory_holder pMemory) const;
+ template <typename Key>
+ node& get(const Key& key, shared_memory_holder pMemory);
+ template <typename Key>
+ bool remove(const Key& key, shared_memory_holder pMemory);
+
+ node* get(node& key, const shared_memory_holder& pMemory) const;
+ node& get(node& key, const shared_memory_holder& pMemory);
+ bool remove(node& key, const shared_memory_holder& pMemory);
+
+ // map
+ template <typename Key, typename Value>
+ void force_insert(const Key& key, const Value& value,
+ shared_memory_holder pMemory);
+
+ public:
+ static const std::string& empty_scalar();
+
+ private:
+ void compute_seq_size() const;
+ void compute_map_size() const;
+
+ void reset_sequence();
+ void reset_map();
+
+ void insert_map_pair(node& key, node& value);
+ void convert_to_map(const shared_memory_holder& pMemory);
+ void convert_sequence_to_map(const shared_memory_holder& pMemory);
+
+ template <typename T>
+ static node& convert_to_node(const T& rhs, shared_memory_holder pMemory);
+
+ private:
+ bool m_isDefined;
+ Mark m_mark;
+ NodeType::value m_type;
+ std::string m_tag;
+ EmitterStyle::value m_style;
+
+ // scalar
+ std::string m_scalar;
+
+ // sequence
+ using node_seq = std::vector<node *>;
+ node_seq m_sequence;
+
+ mutable std::size_t m_seqSize;
+
+ // map
+ using node_map = std::vector<std::pair<node*, node*>>;
+ node_map m_map;
+
+ using kv_pair = std::pair<node*, node*>;
+ using kv_pairs = std::list<kv_pair>;
+ mutable kv_pairs m_undefinedPairs;
+};
+}
+}
+
+#endif // VALUE_DETAIL_NODE_DATA_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_iterator.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_iterator.h
new file mode 100644
index 0000000000..49dcf958db
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_iterator.h
@@ -0,0 +1,181 @@
+#ifndef VALUE_DETAIL_NODE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define VALUE_DETAIL_NODE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/dll.h"
+#include "yaml-cpp/node/ptr.h"
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <map>
+#include <utility>
+#include <vector>
+
+namespace YAML {
+namespace detail {
+struct iterator_type {
+ enum value { NoneType, Sequence, Map };
+};
+
+template <typename V>
+struct node_iterator_value : public std::pair<V*, V*> {
+ using kv = std::pair<V*, V*>;
+
+ node_iterator_value() : kv(), pNode(nullptr) {}
+ explicit node_iterator_value(V& rhs) : kv(), pNode(&rhs) {}
+ explicit node_iterator_value(V& key, V& value) : kv(&key, &value), pNode(nullptr) {}
+
+ V& operator*() const { return *pNode; }
+ V& operator->() const { return *pNode; }
+
+ V* pNode;
+};
+
+using node_seq = std::vector<node *>;
+using node_map = std::vector<std::pair<node*, node*>>;
+
+template <typename V>
+struct node_iterator_type {
+ using seq = node_seq::iterator;
+ using map = node_map::iterator;
+};
+
+template <typename V>
+struct node_iterator_type<const V> {
+ using seq = node_seq::const_iterator;
+ using map = node_map::const_iterator;
+};
+
+template <typename V>
+class node_iterator_base {
+ private:
+ struct enabler {};
+
+ struct proxy {
+ explicit proxy(const node_iterator_value<V>& x) : m_ref(x) {}
+ node_iterator_value<V>* operator->() { return std::addressof(m_ref); }
+ operator node_iterator_value<V>*() { return std::addressof(m_ref); }
+
+ node_iterator_value<V> m_ref;
+ };
+
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = node_iterator_value<V>;
+ using difference_type = std::ptrdiff_t;
+ using pointer = node_iterator_value<V>*;
+ using reference = node_iterator_value<V>;
+ using SeqIter = typename node_iterator_type<V>::seq;
+ using MapIter = typename node_iterator_type<V>::map;
+
+ node_iterator_base()
+ : m_type(iterator_type::NoneType), m_seqIt(), m_mapIt(), m_mapEnd() {}
+ explicit node_iterator_base(SeqIter seqIt)
+ : m_type(iterator_type::Sequence),
+ m_seqIt(seqIt),
+ m_mapIt(),
+ m_mapEnd() {}
+ explicit node_iterator_base(MapIter mapIt, MapIter mapEnd)
+ : m_type(iterator_type::Map),
+ m_seqIt(),
+ m_mapIt(mapIt),
+ m_mapEnd(mapEnd) {
+ m_mapIt = increment_until_defined(m_mapIt);
+ }
+
+ template <typename W>
+ node_iterator_base(const node_iterator_base<W>& rhs,
+ typename std::enable_if<std::is_convertible<W*, V*>::value,
+ enabler>::type = enabler())
+ : m_type(rhs.m_type),
+ m_seqIt(rhs.m_seqIt),
+ m_mapIt(rhs.m_mapIt),
+ m_mapEnd(rhs.m_mapEnd) {}
+
+ template <typename>
+ friend class node_iterator_base;
+
+ template <typename W>
+ bool operator==(const node_iterator_base<W>& rhs) const {
+ if (m_type != rhs.m_type)
+ return false;
+
+ switch (m_type) {
+ case iterator_type::NoneType:
+ return true;
+ case iterator_type::Sequence:
+ return m_seqIt == rhs.m_seqIt;
+ case iterator_type::Map:
+ return m_mapIt == rhs.m_mapIt;
+ }
+ return true;
+ }
+
+ template <typename W>
+ bool operator!=(const node_iterator_base<W>& rhs) const {
+ return !(*this == rhs);
+ }
+
+ node_iterator_base<V>& operator++() {
+ switch (m_type) {
+ case iterator_type::NoneType:
+ break;
+ case iterator_type::Sequence:
+ ++m_seqIt;
+ break;
+ case iterator_type::Map:
+ ++m_mapIt;
+ m_mapIt = increment_until_defined(m_mapIt);
+ break;
+ }
+ return *this;
+ }
+
+ node_iterator_base<V> operator++(int) {
+ node_iterator_base<V> iterator_pre(*this);
+ ++(*this);
+ return iterator_pre;
+ }
+
+ value_type operator*() const {
+ switch (m_type) {
+ case iterator_type::NoneType:
+ return value_type();
+ case iterator_type::Sequence:
+ return value_type(**m_seqIt);
+ case iterator_type::Map:
+ return value_type(*m_mapIt->first, *m_mapIt->second);
+ }
+ return value_type();
+ }
+
+ proxy operator->() const { return proxy(**this); }
+
+ MapIter increment_until_defined(MapIter it) {
+ while (it != m_mapEnd && !is_defined(it))
+ ++it;
+ return it;
+ }
+
+ bool is_defined(MapIter it) const {
+ return it->first->is_defined() && it->second->is_defined();
+ }
+
+ private:
+ typename iterator_type::value m_type;
+
+ SeqIter m_seqIt;
+ MapIter m_mapIt, m_mapEnd;
+};
+
+using node_iterator = node_iterator_base<node>;
+using const_node_iterator = node_iterator_base<const node>;
+}
+}
+
+#endif // VALUE_DETAIL_NODE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_ref.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_ref.h
new file mode 100644
index 0000000000..d8a94f8b80
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_ref.h
@@ -0,0 +1,98 @@
+#ifndef VALUE_DETAIL_NODE_REF_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define VALUE_DETAIL_NODE_REF_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/dll.h"
+#include "yaml-cpp/node/type.h"
+#include "yaml-cpp/node/ptr.h"
+#include "yaml-cpp/node/detail/node_data.h"
+
+namespace YAML {
+namespace detail {
+class node_ref {
+ public:
+ node_ref() : m_pData(new node_data) {}
+ node_ref(const node_ref&) = delete;
+ node_ref& operator=(const node_ref&) = delete;
+
+ bool is_defined() const { return m_pData->is_defined(); }
+ const Mark& mark() const { return m_pData->mark(); }
+ NodeType::value type() const { return m_pData->type(); }
+ const std::string& scalar() const { return m_pData->scalar(); }
+ const std::string& tag() const { return m_pData->tag(); }
+ EmitterStyle::value style() const { return m_pData->style(); }
+
+ void mark_defined() { m_pData->mark_defined(); }
+ void set_data(const node_ref& rhs) { m_pData = rhs.m_pData; }
+
+ void set_mark(const Mark& mark) { m_pData->set_mark(mark); }
+ void set_type(NodeType::value type) { m_pData->set_type(type); }
+ void set_tag(const std::string& tag) { m_pData->set_tag(tag); }
+ void set_null() { m_pData->set_null(); }
+ void set_scalar(const std::string& scalar) { m_pData->set_scalar(scalar); }
+ void set_style(EmitterStyle::value style) { m_pData->set_style(style); }
+
+ // size/iterator
+ std::size_t size() const { return m_pData->size(); }
+
+ const_node_iterator begin() const {
+ return static_cast<const node_data&>(*m_pData).begin();
+ }
+ node_iterator begin() { return m_pData->begin(); }
+
+ const_node_iterator end() const {
+ return static_cast<const node_data&>(*m_pData).end();
+ }
+ node_iterator end() { return m_pData->end(); }
+
+ // sequence
+ void push_back(node& node, shared_memory_holder pMemory) {
+ m_pData->push_back(node, pMemory);
+ }
+ void insert(node& key, node& value, shared_memory_holder pMemory) {
+ m_pData->insert(key, value, pMemory);
+ }
+
+ // indexing
+ template <typename Key>
+ node* get(const Key& key, shared_memory_holder pMemory) const {
+ return static_cast<const node_data&>(*m_pData).get(key, pMemory);
+ }
+ template <typename Key>
+ node& get(const Key& key, shared_memory_holder pMemory) {
+ return m_pData->get(key, pMemory);
+ }
+ template <typename Key>
+ bool remove(const Key& key, shared_memory_holder pMemory) {
+ return m_pData->remove(key, pMemory);
+ }
+
+ node* get(node& key, shared_memory_holder pMemory) const {
+ return static_cast<const node_data&>(*m_pData).get(key, pMemory);
+ }
+ node& get(node& key, shared_memory_holder pMemory) {
+ return m_pData->get(key, pMemory);
+ }
+ bool remove(node& key, shared_memory_holder pMemory) {
+ return m_pData->remove(key, pMemory);
+ }
+
+ // map
+ template <typename Key, typename Value>
+ void force_insert(const Key& key, const Value& value,
+ shared_memory_holder pMemory) {
+ m_pData->force_insert(key, value, pMemory);
+ }
+
+ private:
+ shared_node_data m_pData;
+};
+}
+}
+
+#endif // VALUE_DETAIL_NODE_REF_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/emit.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/emit.h
new file mode 100644
index 0000000000..032268c5d0
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/emit.h
@@ -0,0 +1,32 @@
+#ifndef NODE_EMIT_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define NODE_EMIT_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <string>
+#include <iosfwd>
+
+#include "yaml-cpp/dll.h"
+
+namespace YAML {
+class Emitter;
+class Node;
+
+/**
+ * Emits the node to the given {@link Emitter}. If there is an error in writing,
+ * {@link Emitter#good} will return false.
+ */
+YAML_CPP_API Emitter& operator<<(Emitter& out, const Node& node);
+
+/** Emits the node to the given output stream. */
+YAML_CPP_API std::ostream& operator<<(std::ostream& out, const Node& node);
+
+/** Converts the node to a YAML string. */
+YAML_CPP_API std::string Dump(const Node& node);
+} // namespace YAML
+
+#endif // NODE_EMIT_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/impl.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/impl.h
new file mode 100644
index 0000000000..312281f18c
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/impl.h
@@ -0,0 +1,385 @@
+#ifndef NODE_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define NODE_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/exceptions.h"
+#include "yaml-cpp/node/detail/memory.h"
+#include "yaml-cpp/node/detail/node.h"
+#include "yaml-cpp/node/iterator.h"
+#include "yaml-cpp/node/node.h"
+#include <sstream>
+#include <string>
+
+namespace YAML {
+inline Node::Node()
+ : m_isValid(true), m_invalidKey{}, m_pMemory(nullptr), m_pNode(nullptr) {}
+
+inline Node::Node(NodeType::value type)
+ : m_isValid(true),
+ m_invalidKey{},
+ m_pMemory(new detail::memory_holder),
+ m_pNode(&m_pMemory->create_node()) {
+ m_pNode->set_type(type);
+}
+
+template <typename T>
+inline Node::Node(const T& rhs)
+ : m_isValid(true),
+ m_invalidKey{},
+ m_pMemory(new detail::memory_holder),
+ m_pNode(&m_pMemory->create_node()) {
+ Assign(rhs);
+}
+
+inline Node::Node(const detail::iterator_value& rhs)
+ : m_isValid(rhs.m_isValid),
+ m_invalidKey(rhs.m_invalidKey),
+ m_pMemory(rhs.m_pMemory),
+ m_pNode(rhs.m_pNode) {}
+
+inline Node::Node(const Node&) = default;
+
+inline Node::Node(Zombie)
+ : m_isValid(false), m_invalidKey{}, m_pMemory{}, m_pNode(nullptr) {}
+
+inline Node::Node(Zombie, const std::string& key)
+ : m_isValid(false), m_invalidKey(key), m_pMemory{}, m_pNode(nullptr) {}
+
+inline Node::Node(detail::node& node, detail::shared_memory_holder pMemory)
+ : m_isValid(true), m_invalidKey{}, m_pMemory(pMemory), m_pNode(&node) {}
+
+inline Node::~Node() = default;
+
+inline void Node::EnsureNodeExists() const {
+ if (!m_isValid)
+ throw InvalidNode(m_invalidKey);
+ if (!m_pNode) {
+ m_pMemory.reset(new detail::memory_holder);
+ m_pNode = &m_pMemory->create_node();
+ m_pNode->set_null();
+ }
+}
+
+inline bool Node::IsDefined() const {
+ if (!m_isValid) {
+ return false;
+ }
+ return m_pNode ? m_pNode->is_defined() : true;
+}
+
+inline Mark Node::Mark() const {
+ if (!m_isValid) {
+ throw InvalidNode(m_invalidKey);
+ }
+ return m_pNode ? m_pNode->mark() : Mark::null_mark();
+}
+
+inline NodeType::value Node::Type() const {
+ if (!m_isValid)
+ throw InvalidNode(m_invalidKey);
+ return m_pNode ? m_pNode->type() : NodeType::Null;
+}
+
+// access
+
+// template helpers
+template <typename T, typename S>
+struct as_if {
+ explicit as_if(const Node& node_) : node(node_) {}
+ const Node& node;
+
+ T operator()(const S& fallback) const {
+ if (!node.m_pNode)
+ return fallback;
+
+ T t;
+ if (convert<T>::decode(node, t))
+ return t;
+ return fallback;
+ }
+};
+
+template <typename S>
+struct as_if<std::string, S> {
+ explicit as_if(const Node& node_) : node(node_) {}
+ const Node& node;
+
+ std::string operator()(const S& fallback) const {
+ if (node.Type() == NodeType::Null)
+ return "null";
+ if (node.Type() != NodeType::Scalar)
+ return fallback;
+ return node.Scalar();
+ }
+};
+
+template <typename T>
+struct as_if<T, void> {
+ explicit as_if(const Node& node_) : node(node_) {}
+ const Node& node;
+
+ T operator()() const {
+ if (!node.m_pNode)
+ throw TypedBadConversion<T>(node.Mark());
+
+ T t;
+ if (convert<T>::decode(node, t))
+ return t;
+ throw TypedBadConversion<T>(node.Mark());
+ }
+};
+
+template <>
+struct as_if<std::string, void> {
+ explicit as_if(const Node& node_) : node(node_) {}
+ const Node& node;
+
+ std::string operator()() const {
+ if (node.Type() == NodeType::Null)
+ return "null";
+ if (node.Type() != NodeType::Scalar)
+ throw TypedBadConversion<std::string>(node.Mark());
+ return node.Scalar();
+ }
+};
+
+// access functions
+template <typename T>
+inline T Node::as() const {
+ if (!m_isValid)
+ throw InvalidNode(m_invalidKey);
+ return as_if<T, void>(*this)();
+}
+
+template <typename T, typename S>
+inline T Node::as(const S& fallback) const {
+ if (!m_isValid)
+ return fallback;
+ return as_if<T, S>(*this)(fallback);
+}
+
+inline const std::string& Node::Scalar() const {
+ if (!m_isValid)
+ throw InvalidNode(m_invalidKey);
+ return m_pNode ? m_pNode->scalar() : detail::node_data::empty_scalar();
+}
+
+inline const std::string& Node::Tag() const {
+ if (!m_isValid)
+ throw InvalidNode(m_invalidKey);
+ return m_pNode ? m_pNode->tag() : detail::node_data::empty_scalar();
+}
+
+inline void Node::SetTag(const std::string& tag) {
+ EnsureNodeExists();
+ m_pNode->set_tag(tag);
+}
+
+inline EmitterStyle::value Node::Style() const {
+ if (!m_isValid)
+ throw InvalidNode(m_invalidKey);
+ return m_pNode ? m_pNode->style() : EmitterStyle::Default;
+}
+
+inline void Node::SetStyle(EmitterStyle::value style) {
+ EnsureNodeExists();
+ m_pNode->set_style(style);
+}
+
+// assignment
+inline bool Node::is(const Node& rhs) const {
+ if (!m_isValid || !rhs.m_isValid)
+ throw InvalidNode(m_invalidKey);
+ if (!m_pNode || !rhs.m_pNode)
+ return false;
+ return m_pNode->is(*rhs.m_pNode);
+}
+
+template <typename T>
+inline Node& Node::operator=(const T& rhs) {
+ Assign(rhs);
+ return *this;
+}
+
+inline Node& Node::operator=(const Node& rhs) {
+ if (is(rhs))
+ return *this;
+ AssignNode(rhs);
+ return *this;
+}
+
+inline void Node::reset(const YAML::Node& rhs) {
+ if (!m_isValid || !rhs.m_isValid)
+ throw InvalidNode(m_invalidKey);
+ m_pMemory = rhs.m_pMemory;
+ m_pNode = rhs.m_pNode;
+}
+
+template <typename T>
+inline void Node::Assign(const T& rhs) {
+ if (!m_isValid)
+ throw InvalidNode(m_invalidKey);
+ AssignData(convert<T>::encode(rhs));
+}
+
+template <>
+inline void Node::Assign(const std::string& rhs) {
+ EnsureNodeExists();
+ m_pNode->set_scalar(rhs);
+}
+
+inline void Node::Assign(const char* rhs) {
+ EnsureNodeExists();
+ m_pNode->set_scalar(rhs);
+}
+
+inline void Node::Assign(char* rhs) {
+ EnsureNodeExists();
+ m_pNode->set_scalar(rhs);
+}
+
+inline void Node::AssignData(const Node& rhs) {
+ EnsureNodeExists();
+ rhs.EnsureNodeExists();
+
+ m_pNode->set_data(*rhs.m_pNode);
+ m_pMemory->merge(*rhs.m_pMemory);
+}
+
+inline void Node::AssignNode(const Node& rhs) {
+ if (!m_isValid)
+ throw InvalidNode(m_invalidKey);
+ rhs.EnsureNodeExists();
+
+ if (!m_pNode) {
+ m_pNode = rhs.m_pNode;
+ m_pMemory = rhs.m_pMemory;
+ return;
+ }
+
+ m_pNode->set_ref(*rhs.m_pNode);
+ m_pMemory->merge(*rhs.m_pMemory);
+ m_pNode = rhs.m_pNode;
+}
+
+// size/iterator
+inline std::size_t Node::size() const {
+ if (!m_isValid)
+ throw InvalidNode(m_invalidKey);
+ return m_pNode ? m_pNode->size() : 0;
+}
+
+inline const_iterator Node::begin() const {
+ if (!m_isValid)
+ return const_iterator();
+ return m_pNode ? const_iterator(m_pNode->begin(), m_pMemory)
+ : const_iterator();
+}
+
+inline iterator Node::begin() {
+ if (!m_isValid)
+ return iterator();
+ return m_pNode ? iterator(m_pNode->begin(), m_pMemory) : iterator();
+}
+
+inline const_iterator Node::end() const {
+ if (!m_isValid)
+ return const_iterator();
+ return m_pNode ? const_iterator(m_pNode->end(), m_pMemory) : const_iterator();
+}
+
+inline iterator Node::end() {
+ if (!m_isValid)
+ return iterator();
+ return m_pNode ? iterator(m_pNode->end(), m_pMemory) : iterator();
+}
+
+// sequence
+template <typename T>
+inline void Node::push_back(const T& rhs) {
+ if (!m_isValid)
+ throw InvalidNode(m_invalidKey);
+ push_back(Node(rhs));
+}
+
+inline void Node::push_back(const Node& rhs) {
+ EnsureNodeExists();
+ rhs.EnsureNodeExists();
+
+ m_pNode->push_back(*rhs.m_pNode, m_pMemory);
+ m_pMemory->merge(*rhs.m_pMemory);
+}
+
+template<typename Key>
+std::string key_to_string(const Key& key) {
+ return streamable_to_string<Key, is_streamable<std::stringstream, Key>::value>().impl(key);
+}
+
+// indexing
+template <typename Key>
+inline const Node Node::operator[](const Key& key) const {
+ EnsureNodeExists();
+ detail::node* value =
+ static_cast<const detail::node&>(*m_pNode).get(key, m_pMemory);
+ if (!value) {
+ return Node(ZombieNode, key_to_string(key));
+ }
+ return Node(*value, m_pMemory);
+}
+
+template <typename Key>
+inline Node Node::operator[](const Key& key) {
+ EnsureNodeExists();
+ detail::node& value = m_pNode->get(key, m_pMemory);
+ return Node(value, m_pMemory);
+}
+
+template <typename Key>
+inline bool Node::remove(const Key& key) {
+ EnsureNodeExists();
+ return m_pNode->remove(key, m_pMemory);
+}
+
+inline const Node Node::operator[](const Node& key) const {
+ EnsureNodeExists();
+ key.EnsureNodeExists();
+ m_pMemory->merge(*key.m_pMemory);
+ detail::node* value =
+ static_cast<const detail::node&>(*m_pNode).get(*key.m_pNode, m_pMemory);
+ if (!value) {
+ return Node(ZombieNode, key_to_string(key));
+ }
+ return Node(*value, m_pMemory);
+}
+
+inline Node Node::operator[](const Node& key) {
+ EnsureNodeExists();
+ key.EnsureNodeExists();
+ m_pMemory->merge(*key.m_pMemory);
+ detail::node& value = m_pNode->get(*key.m_pNode, m_pMemory);
+ return Node(value, m_pMemory);
+}
+
+inline bool Node::remove(const Node& key) {
+ EnsureNodeExists();
+ key.EnsureNodeExists();
+ return m_pNode->remove(*key.m_pNode, m_pMemory);
+}
+
+// map
+template <typename Key, typename Value>
+inline void Node::force_insert(const Key& key, const Value& value) {
+ EnsureNodeExists();
+ m_pNode->force_insert(key, value, m_pMemory);
+}
+
+// free functions
+inline bool operator==(const Node& lhs, const Node& rhs) { return lhs.is(rhs); }
+} // namespace YAML
+
+#endif // NODE_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/iterator.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/iterator.h
new file mode 100644
index 0000000000..1fcf6e400f
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/iterator.h
@@ -0,0 +1,34 @@
+#ifndef VALUE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define VALUE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/dll.h"
+#include "yaml-cpp/node/node.h"
+#include "yaml-cpp/node/detail/iterator_fwd.h"
+#include "yaml-cpp/node/detail/iterator.h"
+#include <list>
+#include <utility>
+#include <vector>
+
+// Assert in place so gcc + libc++ combination properly builds
+static_assert(std::is_constructible<YAML::Node, const YAML::Node&>::value, "Node must be copy constructable");
+
+namespace YAML {
+namespace detail {
+struct iterator_value : public Node, std::pair<Node, Node> {
+ iterator_value() = default;
+ explicit iterator_value(const Node& rhs)
+ : Node(rhs),
+ std::pair<Node, Node>(Node(Node::ZombieNode), Node(Node::ZombieNode)) {}
+ explicit iterator_value(const Node& key, const Node& value)
+ : Node(Node::ZombieNode), std::pair<Node, Node>(key, value) {}
+};
+}
+}
+
+#endif // VALUE_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/node.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/node.h
new file mode 100644
index 0000000000..c9e9a0a4bc
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/node.h
@@ -0,0 +1,148 @@
+#ifndef NODE_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define NODE_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <stdexcept>
+#include <string>
+
+#include "yaml-cpp/dll.h"
+#include "yaml-cpp/emitterstyle.h"
+#include "yaml-cpp/mark.h"
+#include "yaml-cpp/node/detail/iterator_fwd.h"
+#include "yaml-cpp/node/ptr.h"
+#include "yaml-cpp/node/type.h"
+
+namespace YAML {
+namespace detail {
+class node;
+class node_data;
+struct iterator_value;
+} // namespace detail
+} // namespace YAML
+
+namespace YAML {
+class YAML_CPP_API Node {
+ public:
+ friend class NodeBuilder;
+ friend class NodeEvents;
+ friend struct detail::iterator_value;
+ friend class detail::node;
+ friend class detail::node_data;
+ template <typename>
+ friend class detail::iterator_base;
+ template <typename T, typename S>
+ friend struct as_if;
+
+ using iterator = YAML::iterator;
+ using const_iterator = YAML::const_iterator;
+
+ Node();
+ explicit Node(NodeType::value type);
+ template <typename T>
+ explicit Node(const T& rhs);
+ explicit Node(const detail::iterator_value& rhs);
+ Node(const Node& rhs);
+ ~Node();
+
+ YAML::Mark Mark() const;
+ NodeType::value Type() const;
+ bool IsDefined() const;
+ bool IsNull() const { return Type() == NodeType::Null; }
+ bool IsScalar() const { return Type() == NodeType::Scalar; }
+ bool IsSequence() const { return Type() == NodeType::Sequence; }
+ bool IsMap() const { return Type() == NodeType::Map; }
+
+ // bool conversions
+ explicit operator bool() const { return IsDefined(); }
+ bool operator!() const { return !IsDefined(); }
+
+ // access
+ template <typename T>
+ T as() const;
+ template <typename T, typename S>
+ T as(const S& fallback) const;
+ const std::string& Scalar() const;
+
+ const std::string& Tag() const;
+ void SetTag(const std::string& tag);
+
+ // style
+ // WARNING: This API might change in future releases.
+ EmitterStyle::value Style() const;
+ void SetStyle(EmitterStyle::value style);
+
+ // assignment
+ bool is(const Node& rhs) const;
+ template <typename T>
+ Node& operator=(const T& rhs);
+ Node& operator=(const Node& rhs);
+ void reset(const Node& rhs = Node());
+
+ // size/iterator
+ std::size_t size() const;
+
+ const_iterator begin() const;
+ iterator begin();
+
+ const_iterator end() const;
+ iterator end();
+
+ // sequence
+ template <typename T>
+ void push_back(const T& rhs);
+ void push_back(const Node& rhs);
+
+ // indexing
+ template <typename Key>
+ const Node operator[](const Key& key) const;
+ template <typename Key>
+ Node operator[](const Key& key);
+ template <typename Key>
+ bool remove(const Key& key);
+
+ const Node operator[](const Node& key) const;
+ Node operator[](const Node& key);
+ bool remove(const Node& key);
+
+ // map
+ template <typename Key, typename Value>
+ void force_insert(const Key& key, const Value& value);
+
+ private:
+ enum Zombie { ZombieNode };
+ explicit Node(Zombie);
+ explicit Node(Zombie, const std::string&);
+ explicit Node(detail::node& node, detail::shared_memory_holder pMemory);
+
+ void EnsureNodeExists() const;
+
+ template <typename T>
+ void Assign(const T& rhs);
+ void Assign(const char* rhs);
+ void Assign(char* rhs);
+
+ void AssignData(const Node& rhs);
+ void AssignNode(const Node& rhs);
+
+ private:
+ bool m_isValid;
+ // String representation of invalid key, if the node is invalid.
+ std::string m_invalidKey;
+ mutable detail::shared_memory_holder m_pMemory;
+ mutable detail::node* m_pNode;
+};
+
+YAML_CPP_API bool operator==(const Node& lhs, const Node& rhs);
+
+YAML_CPP_API Node Clone(const Node& node);
+
+template <typename T>
+struct convert;
+}
+
+#endif // NODE_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/parse.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/parse.h
new file mode 100644
index 0000000000..7745fd7245
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/parse.h
@@ -0,0 +1,78 @@
+#ifndef VALUE_PARSE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define VALUE_PARSE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "yaml-cpp/dll.h"
+
+namespace YAML {
+class Node;
+
+/**
+ * Loads the input string as a single YAML document.
+ *
+ * @throws {@link ParserException} if it is malformed.
+ */
+YAML_CPP_API Node Load(const std::string& input);
+
+/**
+ * Loads the input string as a single YAML document.
+ *
+ * @throws {@link ParserException} if it is malformed.
+ */
+YAML_CPP_API Node Load(const char* input);
+
+/**
+ * Loads the input stream as a single YAML document.
+ *
+ * @throws {@link ParserException} if it is malformed.
+ */
+YAML_CPP_API Node Load(std::istream& input);
+
+/**
+ * Loads the input file as a single YAML document.
+ *
+ * @throws {@link ParserException} if it is malformed.
+ * @throws {@link BadFile} if the file cannot be loaded.
+ */
+YAML_CPP_API Node LoadFile(const std::string& filename);
+
+/**
+ * Loads the input string as a list of YAML documents.
+ *
+ * @throws {@link ParserException} if it is malformed.
+ */
+YAML_CPP_API std::vector<Node> LoadAll(const std::string& input);
+
+/**
+ * Loads the input string as a list of YAML documents.
+ *
+ * @throws {@link ParserException} if it is malformed.
+ */
+YAML_CPP_API std::vector<Node> LoadAll(const char* input);
+
+/**
+ * Loads the input stream as a list of YAML documents.
+ *
+ * @throws {@link ParserException} if it is malformed.
+ */
+YAML_CPP_API std::vector<Node> LoadAll(std::istream& input);
+
+/**
+ * Loads the input file as a list of YAML documents.
+ *
+ * @throws {@link ParserException} if it is malformed.
+ * @throws {@link BadFile} if the file cannot be loaded.
+ */
+YAML_CPP_API std::vector<Node> LoadAllFromFile(const std::string& filename);
+} // namespace YAML
+
+#endif // VALUE_PARSE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/ptr.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/ptr.h
new file mode 100644
index 0000000000..f55d95ed9c
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/ptr.h
@@ -0,0 +1,28 @@
+#ifndef VALUE_PTR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define VALUE_PTR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <memory>
+
+namespace YAML {
+namespace detail {
+class node;
+class node_ref;
+class node_data;
+class memory;
+class memory_holder;
+
+using shared_node = std::shared_ptr<node>;
+using shared_node_ref = std::shared_ptr<node_ref>;
+using shared_node_data = std::shared_ptr<node_data>;
+using shared_memory_holder = std::shared_ptr<memory_holder>;
+using shared_memory = std::shared_ptr<memory>;
+}
+}
+
+#endif // VALUE_PTR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/type.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/type.h
new file mode 100644
index 0000000000..9d55ca9662
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/type.h
@@ -0,0 +1,16 @@
+#ifndef VALUE_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define VALUE_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+namespace YAML {
+struct NodeType {
+ enum value { Undefined, Null, Scalar, Sequence, Map };
+};
+}
+
+#endif // VALUE_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/noexcept.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/noexcept.h
new file mode 100644
index 0000000000..6aac63516f
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/noexcept.h
@@ -0,0 +1,18 @@
+#ifndef NOEXCEPT_H_768872DA_476C_11EA_88B8_90B11C0C0FF8
+#define NOEXCEPT_H_768872DA_476C_11EA_88B8_90B11C0C0FF8
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+// This is here for compatibility with older versions of Visual Studio
+// which don't support noexcept.
+#if defined(_MSC_VER) && _MSC_VER < 1900
+ #define YAML_CPP_NOEXCEPT _NOEXCEPT
+#else
+ #define YAML_CPP_NOEXCEPT noexcept
+#endif
+
+#endif
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/null.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/null.h
new file mode 100644
index 0000000000..b9521d488a
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/null.h
@@ -0,0 +1,26 @@
+#ifndef NULL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define NULL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/dll.h"
+#include <string>
+
+namespace YAML {
+class Node;
+
+struct YAML_CPP_API _Null {};
+inline bool operator==(const _Null&, const _Null&) { return true; }
+inline bool operator!=(const _Null&, const _Null&) { return false; }
+
+YAML_CPP_API bool IsNull(const Node& node); // old API only
+YAML_CPP_API bool IsNullString(const std::string& str);
+
+extern YAML_CPP_API _Null Null;
+}
+
+#endif // NULL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/ostream_wrapper.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/ostream_wrapper.h
new file mode 100644
index 0000000000..cf89741d09
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/ostream_wrapper.h
@@ -0,0 +1,76 @@
+#ifndef OSTREAM_WRAPPER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define OSTREAM_WRAPPER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <string>
+#include <vector>
+
+#include "yaml-cpp/dll.h"
+
+namespace YAML {
+class YAML_CPP_API ostream_wrapper {
+ public:
+ ostream_wrapper();
+ explicit ostream_wrapper(std::ostream& stream);
+ ostream_wrapper(const ostream_wrapper&) = delete;
+ ostream_wrapper(ostream_wrapper&&) = delete;
+ ostream_wrapper& operator=(const ostream_wrapper&) = delete;
+ ostream_wrapper& operator=(ostream_wrapper&&) = delete;
+ ~ostream_wrapper();
+
+ void write(const std::string& str);
+ void write(const char* str, std::size_t size);
+
+ void set_comment() { m_comment = true; }
+
+ const char* str() const {
+ if (m_pStream) {
+ return nullptr;
+ } else {
+ m_buffer[m_pos] = '\0';
+ return &m_buffer[0];
+ }
+ }
+
+ std::size_t row() const { return m_row; }
+ std::size_t col() const { return m_col; }
+ std::size_t pos() const { return m_pos; }
+ bool comment() const { return m_comment; }
+
+ private:
+ void update_pos(char ch);
+
+ private:
+ mutable std::vector<char> m_buffer;
+ std::ostream* const m_pStream;
+
+ std::size_t m_pos;
+ std::size_t m_row, m_col;
+ bool m_comment;
+};
+
+template <std::size_t N>
+inline ostream_wrapper& operator<<(ostream_wrapper& stream,
+ const char (&str)[N]) {
+ stream.write(str, N - 1);
+ return stream;
+}
+
+inline ostream_wrapper& operator<<(ostream_wrapper& stream,
+ const std::string& str) {
+ stream.write(str);
+ return stream;
+}
+
+inline ostream_wrapper& operator<<(ostream_wrapper& stream, char ch) {
+ stream.write(&ch, 1);
+ return stream;
+}
+} // namespace YAML
+
+#endif // OSTREAM_WRAPPER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/parser.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/parser.h
new file mode 100644
index 0000000000..2f403c3504
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/parser.h
@@ -0,0 +1,90 @@
+#ifndef PARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define PARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <ios>
+#include <memory>
+
+#include "yaml-cpp/dll.h"
+
+namespace YAML {
+class EventHandler;
+class Node;
+class Scanner;
+struct Directives;
+struct Token;
+
+/**
+ * A parser turns a stream of bytes into one stream of "events" per YAML
+ * document in the input stream.
+ */
+class YAML_CPP_API Parser {
+ public:
+ /** Constructs an empty parser (with no input. */
+ Parser();
+
+ Parser(const Parser&) = delete;
+ Parser(Parser&&) = delete;
+ Parser& operator=(const Parser&) = delete;
+ Parser& operator=(Parser&&) = delete;
+
+ /**
+ * Constructs a parser from the given input stream. The input stream must
+ * live as long as the parser.
+ */
+ explicit Parser(std::istream& in);
+
+ ~Parser();
+
+ /** Evaluates to true if the parser has some valid input to be read. */
+ explicit operator bool() const;
+
+ /**
+ * Resets the parser with the given input stream. Any existing state is
+ * erased.
+ */
+ void Load(std::istream& in);
+
+ /**
+ * Handles the next document by calling events on the {@code eventHandler}.
+ *
+ * @throw a ParserException on error.
+ * @return false if there are no more documents
+ */
+ bool HandleNextDocument(EventHandler& eventHandler);
+
+ void PrintTokens(std::ostream& out);
+
+ private:
+ /**
+ * Reads any directives that are next in the queue, setting the internal
+ * {@code m_pDirectives} state.
+ */
+ void ParseDirectives();
+
+ void HandleDirective(const Token& token);
+
+ /**
+ * Handles a "YAML" directive, which should be of the form 'major.minor' (like
+ * a version number).
+ */
+ void HandleYamlDirective(const Token& token);
+
+ /**
+ * Handles a "TAG" directive, which should be of the form 'handle prefix',
+ * where 'handle' is converted to 'prefix' in the file.
+ */
+ void HandleTagDirective(const Token& token);
+
+ private:
+ std::unique_ptr<Scanner> m_pScanner;
+ std::unique_ptr<Directives> m_pDirectives;
+};
+} // namespace YAML
+
+#endif // PARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/stlemitter.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/stlemitter.h
new file mode 100644
index 0000000000..210a2f64e6
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/stlemitter.h
@@ -0,0 +1,50 @@
+#ifndef STLEMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define STLEMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <vector>
+#include <list>
+#include <set>
+#include <map>
+
+namespace YAML {
+template <typename Seq>
+inline Emitter& EmitSeq(Emitter& emitter, const Seq& seq) {
+ emitter << BeginSeq;
+ for (const auto& v : seq)
+ emitter << v;
+ emitter << EndSeq;
+ return emitter;
+}
+
+template <typename T>
+inline Emitter& operator<<(Emitter& emitter, const std::vector<T>& v) {
+ return EmitSeq(emitter, v);
+}
+
+template <typename T>
+inline Emitter& operator<<(Emitter& emitter, const std::list<T>& v) {
+ return EmitSeq(emitter, v);
+}
+
+template <typename T>
+inline Emitter& operator<<(Emitter& emitter, const std::set<T>& v) {
+ return EmitSeq(emitter, v);
+}
+
+template <typename K, typename V>
+inline Emitter& operator<<(Emitter& emitter, const std::map<K, V>& m) {
+ emitter << BeginMap;
+ for (const auto& v : m)
+ emitter << Key << v.first << Value << v.second;
+ emitter << EndMap;
+ return emitter;
+}
+}
+
+#endif // STLEMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/traits.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/traits.h
new file mode 100644
index 0000000000..ffe9999f19
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/traits.h
@@ -0,0 +1,135 @@
+#ifndef TRAITS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define TRAITS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <type_traits>
+#include <utility>
+#include <string>
+#include <sstream>
+
+namespace YAML {
+template <typename>
+struct is_numeric {
+ enum { value = false };
+};
+
+template <>
+struct is_numeric<char> {
+ enum { value = true };
+};
+template <>
+struct is_numeric<unsigned char> {
+ enum { value = true };
+};
+template <>
+struct is_numeric<int> {
+ enum { value = true };
+};
+template <>
+struct is_numeric<unsigned int> {
+ enum { value = true };
+};
+template <>
+struct is_numeric<long int> {
+ enum { value = true };
+};
+template <>
+struct is_numeric<unsigned long int> {
+ enum { value = true };
+};
+template <>
+struct is_numeric<short int> {
+ enum { value = true };
+};
+template <>
+struct is_numeric<unsigned short int> {
+ enum { value = true };
+};
+#if defined(_MSC_VER) && (_MSC_VER < 1310)
+template <>
+struct is_numeric<__int64> {
+ enum { value = true };
+};
+template <>
+struct is_numeric<unsigned __int64> {
+ enum { value = true };
+};
+#else
+template <>
+struct is_numeric<long long> {
+ enum { value = true };
+};
+template <>
+struct is_numeric<unsigned long long> {
+ enum { value = true };
+};
+#endif
+template <>
+struct is_numeric<float> {
+ enum { value = true };
+};
+template <>
+struct is_numeric<double> {
+ enum { value = true };
+};
+template <>
+struct is_numeric<long double> {
+ enum { value = true };
+};
+
+template <bool, class T = void>
+struct enable_if_c {
+ using type = T;
+};
+
+template <class T>
+struct enable_if_c<false, T> {};
+
+template <class Cond, class T = void>
+struct enable_if : public enable_if_c<Cond::value, T> {};
+
+template <bool, class T = void>
+struct disable_if_c {
+ using type = T;
+};
+
+template <class T>
+struct disable_if_c<true, T> {};
+
+template <class Cond, class T = void>
+struct disable_if : public disable_if_c<Cond::value, T> {};
+}
+
+template <typename S, typename T>
+struct is_streamable {
+ template <typename StreamT, typename ValueT>
+ static auto test(int)
+ -> decltype(std::declval<StreamT&>() << std::declval<ValueT>(), std::true_type());
+
+ template <typename, typename>
+ static auto test(...) -> std::false_type;
+
+ static const bool value = decltype(test<S, T>(0))::value;
+};
+
+template<typename Key, bool Streamable>
+struct streamable_to_string {
+ static std::string impl(const Key& key) {
+ std::stringstream ss;
+ ss << key;
+ return ss.str();
+ }
+};
+
+template<typename Key>
+struct streamable_to_string<Key, false> {
+ static std::string impl(const Key&) {
+ return "";
+ }
+};
+#endif // TRAITS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/yaml.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/yaml.h
new file mode 100644
index 0000000000..7f515efb96
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/yaml.h
@@ -0,0 +1,24 @@
+#ifndef YAML_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define YAML_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/parser.h"
+#include "yaml-cpp/emitter.h"
+#include "yaml-cpp/emitterstyle.h"
+#include "yaml-cpp/stlemitter.h"
+#include "yaml-cpp/exceptions.h"
+
+#include "yaml-cpp/node/node.h"
+#include "yaml-cpp/node/impl.h"
+#include "yaml-cpp/node/convert.h"
+#include "yaml-cpp/node/iterator.h"
+#include "yaml-cpp/node/detail/impl.h"
+#include "yaml-cpp/node/parse.h"
+#include "yaml-cpp/node/emit.h"
+
+#endif // YAML_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/patches/0001-yaml-cpp-Strip-unneeded-sources.patch b/src/libs/3rdparty/yaml-cpp/patches/0001-yaml-cpp-Strip-unneeded-sources.patch
new file mode 100644
index 0000000000..218b9e1ef7
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/patches/0001-yaml-cpp-Strip-unneeded-sources.patch
@@ -0,0 +1,1556 @@
+From 484b421d0004ce37d7abd63bd0043819ad3a9d9f Mon Sep 17 00:00:00 2001
+From: Eike Ziller <eike.ziller@qt.io>
+Date: Fri, 25 Aug 2023 13:00:39 +0200
+Subject: [PATCH] yaml-cpp: Strip unneeded sources
+
+---
+ .codedocs | 50 -
+ .github/workflows/build.yml | 72 -
+ .gitignore | 3 -
+ BUILD.bazel | 21 -
+ CMakeLists.txt | 207 -
+ CONTRIBUTING.md | 26 -
+ SECURITY.md | 13 -
+ WORKSPACE | 10 -
+ cmake_uninstall.cmake.in | 21 -
+ docs/Breaking-Changes.md | 52 -
+ docs/How-To-Emit-YAML.md | 230 -
+ docs/How-To-Parse-A-Document-(Old-API).md | 265 -
+ docs/Strings.md | 18 -
+ docs/Tutorial.md | 201 -
+ docs/_config.yml | 1 -
+ docs/index.md | 1 -
+ include/yaml-cpp/contrib/anchordict.h | 40 -
+ include/yaml-cpp/contrib/graphbuilder.h | 149 -
+ install.txt | 24 -
+ src/contrib/graphbuilder.cpp | 16 -
+ src/contrib/graphbuilderadapter.cpp | 94 -
+ src/contrib/graphbuilderadapter.h | 86 -
+ src/contrib/yaml-cpp.natvis | 32 -
+ src/contrib/yaml-cpp.natvis.md | 9 -
+ test/BUILD.bazel | 14 -
+ test/CMakeLists.txt | 56 -
+ test/binary_test.cpp | 14 -
+ test/create-emitter-tests.py | 223 -
+ test/gtest-1.11.0/.clang-format | 4 -
+ .../.github/ISSUE_TEMPLATE/00-bug_report.md | 43 -
+ .../ISSUE_TEMPLATE/10-feature_request.md | 24 -
+ .../.github/ISSUE_TEMPLATE/config.yml | 1 -
+ test/gtest-1.11.0/.gitignore | 84 -
+ test/gtest-1.11.0/BUILD.bazel | 190 -
+ test/gtest-1.11.0/CMakeLists.txt | 32 -
+ test/gtest-1.11.0/CONTRIBUTING.md | 130 -
+ test/gtest-1.11.0/CONTRIBUTORS | 63 -
+ test/gtest-1.11.0/LICENSE | 28 -
+ test/gtest-1.11.0/README.md | 140 -
+ test/gtest-1.11.0/WORKSPACE | 24 -
+ test/gtest-1.11.0/ci/linux-presubmit.sh | 126 -
+ test/gtest-1.11.0/ci/macos-presubmit.sh | 73 -
+ test/gtest-1.11.0/docs/_config.yml | 1 -
+ test/gtest-1.11.0/docs/_data/navigation.yml | 43 -
+ test/gtest-1.11.0/docs/_layouts/default.html | 58 -
+ test/gtest-1.11.0/docs/_sass/main.scss | 200 -
+ test/gtest-1.11.0/docs/advanced.md | 2318 ----
+ test/gtest-1.11.0/docs/assets/css/style.scss | 5 -
+ .../docs/community_created_documentation.md | 7 -
+ test/gtest-1.11.0/docs/faq.md | 693 --
+ test/gtest-1.11.0/docs/gmock_cheat_sheet.md | 241 -
+ test/gtest-1.11.0/docs/gmock_cook_book.md | 4301 -------
+ test/gtest-1.11.0/docs/gmock_faq.md | 390 -
+ test/gtest-1.11.0/docs/gmock_for_dummies.md | 700 --
+ test/gtest-1.11.0/docs/index.md | 22 -
+ test/gtest-1.11.0/docs/pkgconfig.md | 148 -
+ test/gtest-1.11.0/docs/platforms.md | 35 -
+ test/gtest-1.11.0/docs/primer.md | 482 -
+ test/gtest-1.11.0/docs/quickstart-bazel.md | 161 -
+ test/gtest-1.11.0/docs/quickstart-cmake.md | 156 -
+ test/gtest-1.11.0/docs/reference/actions.md | 115 -
+ .../gtest-1.11.0/docs/reference/assertions.md | 633 --
+ test/gtest-1.11.0/docs/reference/matchers.md | 283 -
+ test/gtest-1.11.0/docs/reference/mocking.md | 587 -
+ test/gtest-1.11.0/docs/reference/testing.md | 1431 ---
+ test/gtest-1.11.0/docs/samples.md | 22 -
+ test/gtest-1.11.0/googlemock/CMakeLists.txt | 218 -
+ test/gtest-1.11.0/googlemock/README.md | 44 -
+ .../gtest-1.11.0/googlemock/cmake/gmock.pc.in | 10 -
+ .../googlemock/cmake/gmock_main.pc.in | 10 -
+ test/gtest-1.11.0/googlemock/docs/README.md | 4 -
+ .../googlemock/include/gmock/gmock-actions.h | 1687 ---
+ .../include/gmock/gmock-cardinalities.h | 157 -
+ .../include/gmock/gmock-function-mocker.h | 479 -
+ .../googlemock/include/gmock/gmock-matchers.h | 5392 ---------
+ .../include/gmock/gmock-more-actions.h | 573 -
+ .../include/gmock/gmock-more-matchers.h | 92 -
+ .../include/gmock/gmock-nice-strict.h | 261 -
+ .../include/gmock/gmock-spec-builders.h | 2038 ----
+ .../googlemock/include/gmock/gmock.h | 98 -
+ .../include/gmock/internal/custom/README.md | 16 -
+ .../internal/custom/gmock-generated-actions.h | 6 -
+ .../gmock/internal/custom/gmock-matchers.h | 36 -
+ .../gmock/internal/custom/gmock-port.h | 39 -
+ .../gmock/internal/gmock-internal-utils.h | 459 -
+ .../include/gmock/internal/gmock-port.h | 87 -
+ .../include/gmock/internal/gmock-pp.h | 279 -
+ .../gtest-1.11.0/googlemock/scripts/README.md | 5 -
+ .../googlemock/scripts/fuse_gmock_files.py | 256 -
+ .../googlemock/scripts/generator/LICENSE | 203 -
+ .../googlemock/scripts/generator/README | 34 -
+ .../scripts/generator/README.cppclean | 115 -
+ .../scripts/generator/cpp/__init__.py | 0
+ .../googlemock/scripts/generator/cpp/ast.py | 1773 ---
+ .../scripts/generator/cpp/gmock_class.py | 247 -
+ .../scripts/generator/cpp/gmock_class_test.py | 570 -
+ .../scripts/generator/cpp/keywords.py | 56 -
+ .../scripts/generator/cpp/tokenize.py | 284 -
+ .../googlemock/scripts/generator/cpp/utils.py | 37 -
+ .../googlemock/scripts/generator/gmock_gen.py | 30 -
+ test/gtest-1.11.0/googlemock/src/gmock-all.cc | 46 -
+ .../googlemock/src/gmock-cardinalities.cc | 155 -
+ .../googlemock/src/gmock-internal-utils.cc | 200 -
+ .../googlemock/src/gmock-matchers.cc | 459 -
+ .../googlemock/src/gmock-spec-builders.cc | 908 --
+ test/gtest-1.11.0/googlemock/src/gmock.cc | 213 -
+ .../gtest-1.11.0/googlemock/src/gmock_main.cc | 72 -
+ test/gtest-1.11.0/googlemock/test/BUILD.bazel | 118 -
+ .../googlemock/test/gmock-actions_test.cc | 1583 ---
+ .../test/gmock-cardinalities_test.cc | 429 -
+ .../test/gmock-function-mocker_test.cc | 986 --
+ .../test/gmock-internal-utils_test.cc | 720 --
+ .../googlemock/test/gmock-matchers_test.cc | 8562 --------------
+ .../test/gmock-more-actions_test.cc | 1547 ---
+ .../googlemock/test/gmock-nice-strict_test.cc | 539 -
+ .../googlemock/test/gmock-port_test.cc | 42 -
+ .../googlemock/test/gmock-pp-string_test.cc | 206 -
+ .../googlemock/test/gmock-pp_test.cc | 83 -
+ .../test/gmock-spec-builders_test.cc | 2775 -----
+ .../googlemock/test/gmock_all_test.cc | 46 -
+ .../googlemock/test/gmock_ex_test.cc | 80 -
+ .../googlemock/test/gmock_leak_test.py | 104 -
+ .../googlemock/test/gmock_leak_test_.cc | 99 -
+ .../googlemock/test/gmock_link2_test.cc | 39 -
+ .../googlemock/test/gmock_link_test.cc | 39 -
+ .../googlemock/test/gmock_link_test.h | 690 --
+ .../googlemock/test/gmock_output_test.py | 183 -
+ .../googlemock/test/gmock_output_test_.cc | 309 -
+ .../test/gmock_output_test_golden.txt | 317 -
+ .../googlemock/test/gmock_stress_test.cc | 240 -
+ .../googlemock/test/gmock_test.cc | 181 -
+ .../googlemock/test/gmock_test_utils.py | 108 -
+ test/gtest-1.11.0/googletest/CMakeLists.txt | 323 -
+ test/gtest-1.11.0/googletest/README.md | 215 -
+ .../googletest/cmake/Config.cmake.in | 9 -
+ .../gtest-1.11.0/googletest/cmake/gtest.pc.in | 9 -
+ .../googletest/cmake/gtest_main.pc.in | 10 -
+ .../googletest/cmake/internal_utils.cmake | 344 -
+ .../googletest/cmake/libgtest.la.in | 21 -
+ test/gtest-1.11.0/googletest/docs/README.md | 4 -
+ .../include/gtest/gtest-death-test.h | 346 -
+ .../googletest/include/gtest/gtest-matchers.h | 930 --
+ .../googletest/include/gtest/gtest-message.h | 219 -
+ .../include/gtest/gtest-param-test.h | 507 -
+ .../googletest/include/gtest/gtest-printers.h | 1029 --
+ .../googletest/include/gtest/gtest-spi.h | 238 -
+ .../include/gtest/gtest-test-part.h | 184 -
+ .../include/gtest/gtest-typed-test.h | 329 -
+ .../googletest/include/gtest/gtest.h | 2495 -----
+ .../include/gtest/gtest_pred_impl.h | 359 -
+ .../googletest/include/gtest/gtest_prod.h | 61 -
+ .../include/gtest/internal/custom/README.md | 56 -
+ .../gtest/internal/custom/gtest-port.h | 37 -
+ .../gtest/internal/custom/gtest-printers.h | 42 -
+ .../include/gtest/internal/custom/gtest.h | 37 -
+ .../internal/gtest-death-test-internal.h | 304 -
+ .../include/gtest/internal/gtest-filepath.h | 211 -
+ .../include/gtest/internal/gtest-internal.h | 1560 ---
+ .../include/gtest/internal/gtest-param-util.h | 947 --
+ .../include/gtest/internal/gtest-port-arch.h | 114 -
+ .../include/gtest/internal/gtest-port.h | 2389 ----
+ .../include/gtest/internal/gtest-string.h | 175 -
+ .../include/gtest/internal/gtest-type-util.h | 183 -
+ .../googletest/samples/prime_tables.h | 126 -
+ .../googletest/samples/sample1.cc | 66 -
+ .../gtest-1.11.0/googletest/samples/sample1.h | 41 -
+ .../googletest/samples/sample10_unittest.cc | 139 -
+ .../googletest/samples/sample1_unittest.cc | 151 -
+ .../googletest/samples/sample2.cc | 54 -
+ .../gtest-1.11.0/googletest/samples/sample2.h | 80 -
+ .../googletest/samples/sample2_unittest.cc | 107 -
+ .../googletest/samples/sample3-inl.h | 172 -
+ .../googletest/samples/sample3_unittest.cc | 149 -
+ .../googletest/samples/sample4.cc | 54 -
+ .../gtest-1.11.0/googletest/samples/sample4.h | 53 -
+ .../googletest/samples/sample4_unittest.cc | 53 -
+ .../googletest/samples/sample5_unittest.cc | 196 -
+ .../googletest/samples/sample6_unittest.cc | 217 -
+ .../googletest/samples/sample7_unittest.cc | 117 -
+ .../googletest/samples/sample8_unittest.cc | 154 -
+ .../googletest/samples/sample9_unittest.cc | 156 -
+ .../gtest-1.11.0/googletest/scripts/README.md | 5 -
+ .../gtest-1.11.0/googletest/scripts/common.py | 83 -
+ .../googletest/scripts/fuse_gtest_files.py | 253 -
+ .../googletest/scripts/gen_gtest_pred_impl.py | 733 --
+ .../googletest/scripts/gtest-config.in | 274 -
+ .../googletest/scripts/release_docs.py | 158 -
+ .../googletest/scripts/run_with_path.py | 32 -
+ .../gtest-1.11.0/googletest/scripts/upload.py | 1402 ---
+ .../googletest/scripts/upload_gtest.py | 78 -
+ test/gtest-1.11.0/googletest/src/gtest-all.cc | 48 -
+ .../googletest/src/gtest-death-test.cc | 1644 ---
+ .../googletest/src/gtest-filepath.cc | 369 -
+ .../googletest/src/gtest-internal-inl.h | 1221 --
+ .../googletest/src/gtest-matchers.cc | 97 -
+ .../gtest-1.11.0/googletest/src/gtest-port.cc | 1433 ---
+ .../googletest/src/gtest-printers.cc | 533 -
+ .../googletest/src/gtest-test-part.cc | 108 -
+ .../googletest/src/gtest-typed-test.cc | 107 -
+ test/gtest-1.11.0/googletest/src/gtest.cc | 6746 -----------
+ .../gtest-1.11.0/googletest/src/gtest_main.cc | 54 -
+ test/gtest-1.11.0/googletest/test/BUILD.bazel | 590 -
+ .../googletest-break-on-failure-unittest.py | 208 -
+ .../googletest-break-on-failure-unittest_.cc | 86 -
+ .../test/googletest-catch-exceptions-test.py | 236 -
+ .../test/googletest-catch-exceptions-test_.cc | 293 -
+ .../googletest/test/googletest-color-test.py | 127 -
+ .../googletest/test/googletest-color-test_.cc | 62 -
+ .../test/googletest-death-test-test.cc | 1542 ---
+ .../test/googletest-death-test_ex_test.cc | 92 -
+ .../test/googletest-env-var-test.py | 120 -
+ .../test/googletest-env-var-test_.cc | 132 -
+ .../test/googletest-failfast-unittest.py | 410 -
+ .../test/googletest-failfast-unittest_.cc | 167 -
+ .../test/googletest-filepath-test.cc | 649 --
+ .../test/googletest-filter-unittest.py | 639 --
+ .../test/googletest-filter-unittest_.cc | 137 -
+ .../googletest-global-environment-unittest.py | 72 -
+ ...googletest-global-environment-unittest_.cc | 58 -
+ .../test/googletest-json-outfiles-test.py | 191 -
+ .../test/googletest-json-output-unittest.py | 848 --
+ .../test/googletest-list-tests-unittest.py | 205 -
+ .../test/googletest-list-tests-unittest_.cc | 156 -
+ .../test/googletest-listener-test.cc | 518 -
+ .../test/googletest-message-test.cc | 158 -
+ .../test/googletest-options-test.cc | 219 -
+ .../googletest-output-test-golden-lin.txt | 1180 --
+ .../googletest/test/googletest-output-test.py | 346 -
+ .../test/googletest-output-test_.cc | 1108 --
+ ...oogletest-param-test-invalid-name1-test.py | 63 -
+ ...ogletest-param-test-invalid-name1-test_.cc | 50 -
+ ...oogletest-param-test-invalid-name2-test.py | 62 -
+ ...ogletest-param-test-invalid-name2-test_.cc | 55 -
+ .../test/googletest-param-test-test.cc | 1119 --
+ .../test/googletest-param-test-test.h | 51 -
+ .../test/googletest-param-test2-test.cc | 61 -
+ .../googletest/test/googletest-port-test.cc | 1276 ---
+ .../test/googletest-printers-test.cc | 1962 ----
+ .../test/googletest-setuptestsuite-test.py | 54 -
+ .../test/googletest-setuptestsuite-test_.cc | 49 -
+ .../test/googletest-shuffle-test.py | 323 -
+ .../test/googletest-shuffle-test_.cc | 101 -
+ .../test/googletest-test-part-test.cc | 230 -
+ .../test/googletest-throw-on-failure-test.py | 168 -
+ .../test/googletest-throw-on-failure-test_.cc | 71 -
+ .../test/googletest-uninitialized-test.py | 67 -
+ .../test/googletest-uninitialized-test_.cc | 42 -
+ .../googletest/test/gtest-typed-test2_test.cc | 40 -
+ .../googletest/test/gtest-typed-test_test.cc | 437 -
+ .../googletest/test/gtest-typed-test_test.h | 60 -
+ .../test/gtest-unittest-api_test.cc | 328 -
+ .../googletest/test/gtest_all_test.cc | 46 -
+ .../test/gtest_assert_by_exception_test.cc | 116 -
+ .../googletest/test/gtest_environment_test.cc | 188 -
+ .../googletest/test/gtest_help_test.py | 172 -
+ .../googletest/test/gtest_help_test_.cc | 45 -
+ .../googletest/test/gtest_json_test_utils.py | 60 -
+ .../test/gtest_list_output_unittest.py | 286 -
+ .../test/gtest_list_output_unittest_.cc | 77 -
+ .../googletest/test/gtest_main_unittest.cc | 44 -
+ .../googletest/test/gtest_no_test_unittest.cc | 54 -
+ .../test/gtest_pred_impl_unittest.cc | 2422 ----
+ .../test/gtest_premature_exit_test.cc | 126 -
+ .../googletest/test/gtest_prod_test.cc | 56 -
+ .../googletest/test/gtest_repeat_test.cc | 233 -
+ .../test/gtest_skip_check_output_test.py | 59 -
+ ...test_skip_environment_check_output_test.py | 54 -
+ .../gtest_skip_in_environment_setup_test.cc | 49 -
+ .../googletest/test/gtest_skip_test.cc | 55 -
+ .../googletest/test/gtest_sole_header_test.cc | 56 -
+ .../googletest/test/gtest_stress_test.cc | 248 -
+ .../gtest_test_macro_stack_footprint_test.cc | 89 -
+ .../googletest/test/gtest_test_utils.py | 312 -
+ .../googletest/test/gtest_testbridge_test.py | 63 -
+ .../googletest/test/gtest_testbridge_test_.cc | 43 -
+ .../test/gtest_throw_on_failure_ex_test.cc | 90 -
+ .../googletest/test/gtest_unittest.cc | 7784 -------------
+ .../test/gtest_xml_outfile1_test_.cc | 43 -
+ .../test/gtest_xml_outfile2_test_.cc | 43 -
+ .../test/gtest_xml_outfiles_test.py | 135 -
+ .../test/gtest_xml_output_unittest.py | 415 -
+ .../test/gtest_xml_output_unittest_.cc | 193 -
+ .../googletest/test/gtest_xml_test_utils.py | 197 -
+ .../googletest/test/production.cc | 35 -
+ .../gtest-1.11.0/googletest/test/production.h | 54 -
+ test/gtest-1.11.0/library.json | 62 -
+ test/handler_test.h | 32 -
+ test/integration/emitter_test.cpp | 1740 ---
+ test/integration/encoding_test.cpp | 182 -
+ test/integration/error_messages_test.cpp | 61 -
+ test/integration/gen_emitter_test.cpp | 9936 -----------------
+ test/integration/handler_spec_test.cpp | 1686 ---
+ test/integration/handler_test.cpp | 76 -
+ test/integration/load_node_test.cpp | 364 -
+ test/integration/node_spec_test.cpp | 1136 --
+ test/main.cpp | 6 -
+ test/mock_event_handler.h | 30 -
+ test/node/node_test.cpp | 853 --
+ test/ostream_wrapper_test.cpp | 66 -
+ test/parser_test.cpp | 64 -
+ test/regex_test.cpp | 177 -
+ test/specexamples.h | 868 --
+ util/CMakeLists.txt | 32 -
+ util/api.cpp | 137 -
+ util/parse.cpp | 46 -
+ util/read.cpp | 103 -
+ util/sandbox.cpp | 36 -
+ yaml-cpp-config.cmake.in | 22 -
+ yaml-cpp.pc.in | 11 -
+ 309 files changed, 133651 deletions(-)
+ delete mode 100644 .codedocs
+ delete mode 100644 .github/workflows/build.yml
+ delete mode 100644 .gitignore
+ delete mode 100644 BUILD.bazel
+ delete mode 100644 CMakeLists.txt
+ delete mode 100644 CONTRIBUTING.md
+ delete mode 100644 SECURITY.md
+ delete mode 100644 WORKSPACE
+ delete mode 100644 cmake_uninstall.cmake.in
+ delete mode 100644 docs/Breaking-Changes.md
+ delete mode 100644 docs/How-To-Emit-YAML.md
+ delete mode 100644 docs/How-To-Parse-A-Document-(Old-API).md
+ delete mode 100644 docs/Strings.md
+ delete mode 100644 docs/Tutorial.md
+ delete mode 100644 docs/_config.yml
+ delete mode 100644 docs/index.md
+ delete mode 100644 include/yaml-cpp/contrib/anchordict.h
+ delete mode 100644 include/yaml-cpp/contrib/graphbuilder.h
+ delete mode 100644 install.txt
+ delete mode 100644 src/contrib/graphbuilder.cpp
+ delete mode 100644 src/contrib/graphbuilderadapter.cpp
+ delete mode 100644 src/contrib/graphbuilderadapter.h
+ delete mode 100644 src/contrib/yaml-cpp.natvis
+ delete mode 100644 src/contrib/yaml-cpp.natvis.md
+ delete mode 100644 test/BUILD.bazel
+ delete mode 100644 test/CMakeLists.txt
+ delete mode 100644 test/binary_test.cpp
+ delete mode 100644 test/create-emitter-tests.py
+ delete mode 100644 test/gtest-1.11.0/.clang-format
+ delete mode 100644 test/gtest-1.11.0/.github/ISSUE_TEMPLATE/00-bug_report.md
+ delete mode 100644 test/gtest-1.11.0/.github/ISSUE_TEMPLATE/10-feature_request.md
+ delete mode 100644 test/gtest-1.11.0/.github/ISSUE_TEMPLATE/config.yml
+ delete mode 100644 test/gtest-1.11.0/.gitignore
+ delete mode 100644 test/gtest-1.11.0/BUILD.bazel
+ delete mode 100644 test/gtest-1.11.0/CMakeLists.txt
+ delete mode 100644 test/gtest-1.11.0/CONTRIBUTING.md
+ delete mode 100644 test/gtest-1.11.0/CONTRIBUTORS
+ delete mode 100644 test/gtest-1.11.0/LICENSE
+ delete mode 100644 test/gtest-1.11.0/README.md
+ delete mode 100644 test/gtest-1.11.0/WORKSPACE
+ delete mode 100644 test/gtest-1.11.0/ci/linux-presubmit.sh
+ delete mode 100644 test/gtest-1.11.0/ci/macos-presubmit.sh
+ delete mode 100644 test/gtest-1.11.0/docs/_config.yml
+ delete mode 100644 test/gtest-1.11.0/docs/_data/navigation.yml
+ delete mode 100644 test/gtest-1.11.0/docs/_layouts/default.html
+ delete mode 100644 test/gtest-1.11.0/docs/_sass/main.scss
+ delete mode 100644 test/gtest-1.11.0/docs/advanced.md
+ delete mode 100644 test/gtest-1.11.0/docs/assets/css/style.scss
+ delete mode 100644 test/gtest-1.11.0/docs/community_created_documentation.md
+ delete mode 100644 test/gtest-1.11.0/docs/faq.md
+ delete mode 100644 test/gtest-1.11.0/docs/gmock_cheat_sheet.md
+ delete mode 100644 test/gtest-1.11.0/docs/gmock_cook_book.md
+ delete mode 100644 test/gtest-1.11.0/docs/gmock_faq.md
+ delete mode 100644 test/gtest-1.11.0/docs/gmock_for_dummies.md
+ delete mode 100644 test/gtest-1.11.0/docs/index.md
+ delete mode 100644 test/gtest-1.11.0/docs/pkgconfig.md
+ delete mode 100644 test/gtest-1.11.0/docs/platforms.md
+ delete mode 100644 test/gtest-1.11.0/docs/primer.md
+ delete mode 100644 test/gtest-1.11.0/docs/quickstart-bazel.md
+ delete mode 100644 test/gtest-1.11.0/docs/quickstart-cmake.md
+ delete mode 100644 test/gtest-1.11.0/docs/reference/actions.md
+ delete mode 100644 test/gtest-1.11.0/docs/reference/assertions.md
+ delete mode 100644 test/gtest-1.11.0/docs/reference/matchers.md
+ delete mode 100644 test/gtest-1.11.0/docs/reference/mocking.md
+ delete mode 100644 test/gtest-1.11.0/docs/reference/testing.md
+ delete mode 100644 test/gtest-1.11.0/docs/samples.md
+ delete mode 100644 test/gtest-1.11.0/googlemock/CMakeLists.txt
+ delete mode 100644 test/gtest-1.11.0/googlemock/README.md
+ delete mode 100644 test/gtest-1.11.0/googlemock/cmake/gmock.pc.in
+ delete mode 100644 test/gtest-1.11.0/googlemock/cmake/gmock_main.pc.in
+ delete mode 100644 test/gtest-1.11.0/googlemock/docs/README.md
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-actions.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-cardinalities.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-function-mocker.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-matchers.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-more-actions.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-more-matchers.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-nice-strict.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-spec-builders.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/custom/README.md
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-matchers.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-port.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-internal-utils.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-port.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-pp.h
+ delete mode 100644 test/gtest-1.11.0/googlemock/scripts/README.md
+ delete mode 100755 test/gtest-1.11.0/googlemock/scripts/fuse_gmock_files.py
+ delete mode 100644 test/gtest-1.11.0/googlemock/scripts/generator/LICENSE
+ delete mode 100644 test/gtest-1.11.0/googlemock/scripts/generator/README
+ delete mode 100644 test/gtest-1.11.0/googlemock/scripts/generator/README.cppclean
+ delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/__init__.py
+ delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/ast.py
+ delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class.py
+ delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class_test.py
+ delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/keywords.py
+ delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/tokenize.py
+ delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/utils.py
+ delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/gmock_gen.py
+ delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock-all.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock-cardinalities.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock-internal-utils.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock-matchers.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock-spec-builders.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock_main.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/BUILD.bazel
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-actions_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-cardinalities_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-function-mocker_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-internal-utils_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-matchers_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-more-actions_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-nice-strict_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-port_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-pp-string_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-pp_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-spec-builders_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_all_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_ex_test.cc
+ delete mode 100755 test/gtest-1.11.0/googlemock/test/gmock_leak_test.py
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_leak_test_.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_link2_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_link_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_link_test.h
+ delete mode 100755 test/gtest-1.11.0/googlemock/test/gmock_output_test.py
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_output_test_.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_output_test_golden.txt
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_stress_test.cc
+ delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_test.cc
+ delete mode 100755 test/gtest-1.11.0/googlemock/test/gmock_test_utils.py
+ delete mode 100644 test/gtest-1.11.0/googletest/CMakeLists.txt
+ delete mode 100644 test/gtest-1.11.0/googletest/README.md
+ delete mode 100644 test/gtest-1.11.0/googletest/cmake/Config.cmake.in
+ delete mode 100644 test/gtest-1.11.0/googletest/cmake/gtest.pc.in
+ delete mode 100644 test/gtest-1.11.0/googletest/cmake/gtest_main.pc.in
+ delete mode 100644 test/gtest-1.11.0/googletest/cmake/internal_utils.cmake
+ delete mode 100644 test/gtest-1.11.0/googletest/cmake/libgtest.la.in
+ delete mode 100644 test/gtest-1.11.0/googletest/docs/README.md
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-death-test.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-matchers.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-message.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-param-test.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-printers.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-spi.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-test-part.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-typed-test.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest_pred_impl.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest_prod.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/custom/README.md
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-port.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-printers.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-death-test-internal.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-filepath.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-internal.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-param-util.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port-arch.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-string.h
+ delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-type-util.h
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/prime_tables.h
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample1.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample1.h
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample10_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample1_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample2.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample2.h
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample2_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample3-inl.h
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample3_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample4.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample4.h
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample4_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample5_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample6_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample7_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample8_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/samples/sample9_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/scripts/README.md
+ delete mode 100644 test/gtest-1.11.0/googletest/scripts/common.py
+ delete mode 100755 test/gtest-1.11.0/googletest/scripts/fuse_gtest_files.py
+ delete mode 100755 test/gtest-1.11.0/googletest/scripts/gen_gtest_pred_impl.py
+ delete mode 100755 test/gtest-1.11.0/googletest/scripts/gtest-config.in
+ delete mode 100755 test/gtest-1.11.0/googletest/scripts/release_docs.py
+ delete mode 100755 test/gtest-1.11.0/googletest/scripts/run_with_path.py
+ delete mode 100755 test/gtest-1.11.0/googletest/scripts/upload.py
+ delete mode 100755 test/gtest-1.11.0/googletest/scripts/upload_gtest.py
+ delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-all.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-death-test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-filepath.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-internal-inl.h
+ delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-matchers.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-port.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-printers.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-test-part.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-typed-test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/src/gtest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/src/gtest_main.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/BUILD.bazel
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest_.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test_.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-color-test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-color-test_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-death-test-test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-death-test_ex_test.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-env-var-test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-env-var-test_.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-failfast-unittest.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-failfast-unittest_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-filepath-test.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-filter-unittest.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-filter-unittest_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-json-outfiles-test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-json-output-unittest.py
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-listener-test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-message-test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-options-test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-output-test-golden-lin.txt
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-output-test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-output-test_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-test.h
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test2-test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-port-test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-printers-test.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test_.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-shuffle-test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-shuffle-test_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-test-part-test.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test_.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-uninitialized-test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-uninitialized-test_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest-typed-test2_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest-typed-test_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest-typed-test_test.h
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest-unittest-api_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_all_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_assert_by_exception_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_environment_test.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_help_test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_help_test_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_json_test_utils.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_list_output_unittest.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_list_output_unittest_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_main_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_no_test_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_pred_impl_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_premature_exit_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_prod_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_repeat_test.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_skip_check_output_test.py
+ delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_skip_environment_check_output_test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_skip_in_environment_setup_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_skip_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_sole_header_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_stress_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_test_macro_stack_footprint_test.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_test_utils.py
+ delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_testbridge_test.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_testbridge_test_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_throw_on_failure_ex_test.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_unittest.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_xml_outfile1_test_.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_xml_outfile2_test_.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_xml_outfiles_test.py
+ delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest_.cc
+ delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_xml_test_utils.py
+ delete mode 100644 test/gtest-1.11.0/googletest/test/production.cc
+ delete mode 100644 test/gtest-1.11.0/googletest/test/production.h
+ delete mode 100644 test/gtest-1.11.0/library.json
+ delete mode 100644 test/handler_test.h
+ delete mode 100644 test/integration/emitter_test.cpp
+ delete mode 100644 test/integration/encoding_test.cpp
+ delete mode 100644 test/integration/error_messages_test.cpp
+ delete mode 100644 test/integration/gen_emitter_test.cpp
+ delete mode 100644 test/integration/handler_spec_test.cpp
+ delete mode 100644 test/integration/handler_test.cpp
+ delete mode 100644 test/integration/load_node_test.cpp
+ delete mode 100644 test/integration/node_spec_test.cpp
+ delete mode 100644 test/main.cpp
+ delete mode 100644 test/mock_event_handler.h
+ delete mode 100644 test/node/node_test.cpp
+ delete mode 100644 test/ostream_wrapper_test.cpp
+ delete mode 100644 test/parser_test.cpp
+ delete mode 100644 test/regex_test.cpp
+ delete mode 100644 test/specexamples.h
+ delete mode 100644 util/CMakeLists.txt
+ delete mode 100644 util/api.cpp
+ delete mode 100644 util/parse.cpp
+ delete mode 100644 util/read.cpp
+ delete mode 100644 util/sandbox.cpp
+ delete mode 100644 yaml-cpp-config.cmake.in
+ delete mode 100644 yaml-cpp.pc.in
+
+diff --git a/.codedocs b/.codedocs
+deleted file mode 100644
+index 02e4382..0000000
+diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
+deleted file mode 100644
+index a408a9d..0000000
+diff --git a/.gitignore b/.gitignore
+deleted file mode 100644
+index 2f9d10f..0000000
+diff --git a/BUILD.bazel b/BUILD.bazel
+deleted file mode 100644
+index 23e847e..0000000
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+deleted file mode 100644
+index 46dc180..0000000
+diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
+deleted file mode 100644
+index 5705fe2..0000000
+diff --git a/SECURITY.md b/SECURITY.md
+deleted file mode 100644
+index 06a1751..0000000
+diff --git a/WORKSPACE b/WORKSPACE
+deleted file mode 100644
+index d5ecc0b..0000000
+diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in
+deleted file mode 100644
+index c2d34d4..0000000
+diff --git a/docs/Breaking-Changes.md b/docs/Breaking-Changes.md
+deleted file mode 100644
+index 959adea..0000000
+diff --git a/docs/How-To-Emit-YAML.md b/docs/How-To-Emit-YAML.md
+deleted file mode 100644
+index 9340701..0000000
+diff --git a/docs/How-To-Parse-A-Document-(Old-API).md b/docs/How-To-Parse-A-Document-(Old-API).md
+deleted file mode 100644
+index 82fac71..0000000
+diff --git a/docs/Strings.md b/docs/Strings.md
+deleted file mode 100644
+index f2328a1..0000000
+diff --git a/docs/Tutorial.md b/docs/Tutorial.md
+deleted file mode 100644
+index a7b0e21..0000000
+diff --git a/docs/_config.yml b/docs/_config.yml
+deleted file mode 100644
+index c741881..0000000
+diff --git a/docs/index.md b/docs/index.md
+deleted file mode 100644
+index 17f1315..0000000
+diff --git a/include/yaml-cpp/contrib/anchordict.h b/include/yaml-cpp/contrib/anchordict.h
+deleted file mode 100644
+index 1b7809b..0000000
+diff --git a/include/yaml-cpp/contrib/graphbuilder.h b/include/yaml-cpp/contrib/graphbuilder.h
+deleted file mode 100644
+index dbffd92..0000000
+diff --git a/install.txt b/install.txt
+deleted file mode 100644
+index 9392362..0000000
+diff --git a/src/contrib/graphbuilder.cpp b/src/contrib/graphbuilder.cpp
+deleted file mode 100644
+index 0352054..0000000
+diff --git a/src/contrib/graphbuilderadapter.cpp b/src/contrib/graphbuilderadapter.cpp
+deleted file mode 100644
+index c386a92..0000000
+diff --git a/src/contrib/graphbuilderadapter.h b/src/contrib/graphbuilderadapter.h
+deleted file mode 100644
+index c1cbcff..0000000
+diff --git a/src/contrib/yaml-cpp.natvis b/src/contrib/yaml-cpp.natvis
+deleted file mode 100644
+index d5c222b..0000000
+diff --git a/src/contrib/yaml-cpp.natvis.md b/src/contrib/yaml-cpp.natvis.md
+deleted file mode 100644
+index f1d68a8..0000000
+diff --git a/test/BUILD.bazel b/test/BUILD.bazel
+deleted file mode 100644
+index d30fa73..0000000
+diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
+deleted file mode 100644
+index 351b03f..0000000
+diff --git a/test/binary_test.cpp b/test/binary_test.cpp
+deleted file mode 100644
+index 7b17823..0000000
+diff --git a/test/create-emitter-tests.py b/test/create-emitter-tests.py
+deleted file mode 100644
+index 7295544..0000000
+diff --git a/test/gtest-1.11.0/.clang-format b/test/gtest-1.11.0/.clang-format
+deleted file mode 100644
+index 5b9bfe6..0000000
+diff --git a/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/00-bug_report.md b/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/00-bug_report.md
+deleted file mode 100644
+index 0f7e8b5..0000000
+diff --git a/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/10-feature_request.md b/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/10-feature_request.md
+deleted file mode 100644
+index 70a3a20..0000000
+diff --git a/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/config.yml b/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/config.yml
+deleted file mode 100644
+index 3ba13e0..0000000
+diff --git a/test/gtest-1.11.0/.gitignore b/test/gtest-1.11.0/.gitignore
+deleted file mode 100644
+index f08cb72..0000000
+diff --git a/test/gtest-1.11.0/BUILD.bazel b/test/gtest-1.11.0/BUILD.bazel
+deleted file mode 100644
+index 965c518..0000000
+diff --git a/test/gtest-1.11.0/CMakeLists.txt b/test/gtest-1.11.0/CMakeLists.txt
+deleted file mode 100644
+index ea81ab1..0000000
+diff --git a/test/gtest-1.11.0/CONTRIBUTING.md b/test/gtest-1.11.0/CONTRIBUTING.md
+deleted file mode 100644
+index da45e44..0000000
+diff --git a/test/gtest-1.11.0/CONTRIBUTORS b/test/gtest-1.11.0/CONTRIBUTORS
+deleted file mode 100644
+index 76db0b4..0000000
+diff --git a/test/gtest-1.11.0/LICENSE b/test/gtest-1.11.0/LICENSE
+deleted file mode 100644
+index 1941a11..0000000
+diff --git a/test/gtest-1.11.0/README.md b/test/gtest-1.11.0/README.md
+deleted file mode 100644
+index 7d872a5..0000000
+diff --git a/test/gtest-1.11.0/WORKSPACE b/test/gtest-1.11.0/WORKSPACE
+deleted file mode 100644
+index 614f557..0000000
+diff --git a/test/gtest-1.11.0/ci/linux-presubmit.sh b/test/gtest-1.11.0/ci/linux-presubmit.sh
+deleted file mode 100644
+index 6bea1cd..0000000
+diff --git a/test/gtest-1.11.0/ci/macos-presubmit.sh b/test/gtest-1.11.0/ci/macos-presubmit.sh
+deleted file mode 100644
+index d6423fa..0000000
+diff --git a/test/gtest-1.11.0/docs/_config.yml b/test/gtest-1.11.0/docs/_config.yml
+deleted file mode 100644
+index d12867e..0000000
+diff --git a/test/gtest-1.11.0/docs/_data/navigation.yml b/test/gtest-1.11.0/docs/_data/navigation.yml
+deleted file mode 100644
+index 9f33327..0000000
+diff --git a/test/gtest-1.11.0/docs/_layouts/default.html b/test/gtest-1.11.0/docs/_layouts/default.html
+deleted file mode 100644
+index dcb42d9..0000000
+diff --git a/test/gtest-1.11.0/docs/_sass/main.scss b/test/gtest-1.11.0/docs/_sass/main.scss
+deleted file mode 100644
+index 92edc87..0000000
+diff --git a/test/gtest-1.11.0/docs/advanced.md b/test/gtest-1.11.0/docs/advanced.md
+deleted file mode 100644
+index 8dff5ba..0000000
+diff --git a/test/gtest-1.11.0/docs/assets/css/style.scss b/test/gtest-1.11.0/docs/assets/css/style.scss
+deleted file mode 100644
+index bb30f41..0000000
+diff --git a/test/gtest-1.11.0/docs/community_created_documentation.md b/test/gtest-1.11.0/docs/community_created_documentation.md
+deleted file mode 100644
+index 4569075..0000000
+diff --git a/test/gtest-1.11.0/docs/faq.md b/test/gtest-1.11.0/docs/faq.md
+deleted file mode 100644
+index 9042da1..0000000
+diff --git a/test/gtest-1.11.0/docs/gmock_cheat_sheet.md b/test/gtest-1.11.0/docs/gmock_cheat_sheet.md
+deleted file mode 100644
+index 17ed7a5..0000000
+diff --git a/test/gtest-1.11.0/docs/gmock_cook_book.md b/test/gtest-1.11.0/docs/gmock_cook_book.md
+deleted file mode 100644
+index c08958e..0000000
+diff --git a/test/gtest-1.11.0/docs/gmock_faq.md b/test/gtest-1.11.0/docs/gmock_faq.md
+deleted file mode 100644
+index 2cd9b3f..0000000
+diff --git a/test/gtest-1.11.0/docs/gmock_for_dummies.md b/test/gtest-1.11.0/docs/gmock_for_dummies.md
+deleted file mode 100644
+index 1f4cc24..0000000
+diff --git a/test/gtest-1.11.0/docs/index.md b/test/gtest-1.11.0/docs/index.md
+deleted file mode 100644
+index b162c74..0000000
+diff --git a/test/gtest-1.11.0/docs/pkgconfig.md b/test/gtest-1.11.0/docs/pkgconfig.md
+deleted file mode 100644
+index 768e9b4..0000000
+diff --git a/test/gtest-1.11.0/docs/platforms.md b/test/gtest-1.11.0/docs/platforms.md
+deleted file mode 100644
+index eba6ef8..0000000
+diff --git a/test/gtest-1.11.0/docs/primer.md b/test/gtest-1.11.0/docs/primer.md
+deleted file mode 100644
+index 6d8fdf4..0000000
+diff --git a/test/gtest-1.11.0/docs/quickstart-bazel.md b/test/gtest-1.11.0/docs/quickstart-bazel.md
+deleted file mode 100644
+index 362ee6d..0000000
+diff --git a/test/gtest-1.11.0/docs/quickstart-cmake.md b/test/gtest-1.11.0/docs/quickstart-cmake.md
+deleted file mode 100644
+index 420f1d3..0000000
+diff --git a/test/gtest-1.11.0/docs/reference/actions.md b/test/gtest-1.11.0/docs/reference/actions.md
+deleted file mode 100644
+index 166d2a8..0000000
+diff --git a/test/gtest-1.11.0/docs/reference/assertions.md b/test/gtest-1.11.0/docs/reference/assertions.md
+deleted file mode 100644
+index 7bf03a3..0000000
+diff --git a/test/gtest-1.11.0/docs/reference/matchers.md b/test/gtest-1.11.0/docs/reference/matchers.md
+deleted file mode 100644
+index 9e40cab..0000000
+diff --git a/test/gtest-1.11.0/docs/reference/mocking.md b/test/gtest-1.11.0/docs/reference/mocking.md
+deleted file mode 100644
+index c29f716..0000000
+diff --git a/test/gtest-1.11.0/docs/reference/testing.md b/test/gtest-1.11.0/docs/reference/testing.md
+deleted file mode 100644
+index 554d6c9..0000000
+diff --git a/test/gtest-1.11.0/docs/samples.md b/test/gtest-1.11.0/docs/samples.md
+deleted file mode 100644
+index 2d97ca5..0000000
+diff --git a/test/gtest-1.11.0/googlemock/CMakeLists.txt b/test/gtest-1.11.0/googlemock/CMakeLists.txt
+deleted file mode 100644
+index e7df8ec..0000000
+diff --git a/test/gtest-1.11.0/googlemock/README.md b/test/gtest-1.11.0/googlemock/README.md
+deleted file mode 100644
+index ead6883..0000000
+diff --git a/test/gtest-1.11.0/googlemock/cmake/gmock.pc.in b/test/gtest-1.11.0/googlemock/cmake/gmock.pc.in
+deleted file mode 100644
+index 23c67b5..0000000
+diff --git a/test/gtest-1.11.0/googlemock/cmake/gmock_main.pc.in b/test/gtest-1.11.0/googlemock/cmake/gmock_main.pc.in
+deleted file mode 100644
+index 66ffea7..0000000
+diff --git a/test/gtest-1.11.0/googlemock/docs/README.md b/test/gtest-1.11.0/googlemock/docs/README.md
+deleted file mode 100644
+index 1bc57b7..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-actions.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-actions.h
+deleted file mode 100644
+index f2393bd..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-cardinalities.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-cardinalities.h
+deleted file mode 100644
+index fc7f803..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-function-mocker.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-function-mocker.h
+deleted file mode 100644
+index 0fc6f6f..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-matchers.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-matchers.h
+deleted file mode 100644
+index 86be9c1..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-more-actions.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-more-actions.h
+deleted file mode 100644
+index fd29335..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-more-matchers.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-more-matchers.h
+deleted file mode 100644
+index dfc77e3..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-nice-strict.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-nice-strict.h
+deleted file mode 100644
+index b03b770..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-spec-builders.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-spec-builders.h
+deleted file mode 100644
+index 41323c1..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock.h
+deleted file mode 100644
+index 12469bc..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/README.md b/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/README.md
+deleted file mode 100644
+index f6c93f6..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h
+deleted file mode 100644
+index 63f8999..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-matchers.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-matchers.h
+deleted file mode 100644
+index 6384294..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-port.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-port.h
+deleted file mode 100644
+index 1437869..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-internal-utils.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-internal-utils.h
+deleted file mode 100644
+index 317544a..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-port.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-port.h
+deleted file mode 100644
+index 367a44d..0000000
+diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-pp.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-pp.h
+deleted file mode 100644
+index 94d61c0..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/README.md b/test/gtest-1.11.0/googlemock/scripts/README.md
+deleted file mode 100644
+index a3301e5..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/fuse_gmock_files.py b/test/gtest-1.11.0/googlemock/scripts/fuse_gmock_files.py
+deleted file mode 100755
+index 7fa9b3a..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/LICENSE b/test/gtest-1.11.0/googlemock/scripts/generator/LICENSE
+deleted file mode 100644
+index 87ea063..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/README b/test/gtest-1.11.0/googlemock/scripts/generator/README
+deleted file mode 100644
+index 01fd463..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/README.cppclean b/test/gtest-1.11.0/googlemock/scripts/generator/README.cppclean
+deleted file mode 100644
+index 65431b6..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/__init__.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/__init__.py
+deleted file mode 100755
+index e69de29..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/ast.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/ast.py
+deleted file mode 100755
+index 0e77016..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class.py
+deleted file mode 100755
+index 3e21022..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class_test.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class_test.py
+deleted file mode 100755
+index eff475f..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/keywords.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/keywords.py
+deleted file mode 100755
+index e428271..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/tokenize.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/tokenize.py
+deleted file mode 100755
+index a75edcb..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/utils.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/utils.py
+deleted file mode 100755
+index 6f5fc09..0000000
+diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/gmock_gen.py b/test/gtest-1.11.0/googlemock/scripts/generator/gmock_gen.py
+deleted file mode 100755
+index 9d528a5..0000000
+diff --git a/test/gtest-1.11.0/googlemock/src/gmock-all.cc b/test/gtest-1.11.0/googlemock/src/gmock-all.cc
+deleted file mode 100644
+index e43c9b7..0000000
+diff --git a/test/gtest-1.11.0/googlemock/src/gmock-cardinalities.cc b/test/gtest-1.11.0/googlemock/src/gmock-cardinalities.cc
+deleted file mode 100644
+index 7463f43..0000000
+diff --git a/test/gtest-1.11.0/googlemock/src/gmock-internal-utils.cc b/test/gtest-1.11.0/googlemock/src/gmock-internal-utils.cc
+deleted file mode 100644
+index e5b5479..0000000
+diff --git a/test/gtest-1.11.0/googlemock/src/gmock-matchers.cc b/test/gtest-1.11.0/googlemock/src/gmock-matchers.cc
+deleted file mode 100644
+index dded437..0000000
+diff --git a/test/gtest-1.11.0/googlemock/src/gmock-spec-builders.cc b/test/gtest-1.11.0/googlemock/src/gmock-spec-builders.cc
+deleted file mode 100644
+index c7266a3..0000000
+diff --git a/test/gtest-1.11.0/googlemock/src/gmock.cc b/test/gtest-1.11.0/googlemock/src/gmock.cc
+deleted file mode 100644
+index 7bcdb0b..0000000
+diff --git a/test/gtest-1.11.0/googlemock/src/gmock_main.cc b/test/gtest-1.11.0/googlemock/src/gmock_main.cc
+deleted file mode 100644
+index 18c500f..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/BUILD.bazel b/test/gtest-1.11.0/googlemock/test/BUILD.bazel
+deleted file mode 100644
+index efb7306..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock-actions_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-actions_test.cc
+deleted file mode 100644
+index e1ca7fe..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock-cardinalities_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-cardinalities_test.cc
+deleted file mode 100644
+index ca97cae..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock-function-mocker_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-function-mocker_test.cc
+deleted file mode 100644
+index cf76fa9..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock-internal-utils_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-internal-utils_test.cc
+deleted file mode 100644
+index 0d15e8f..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock-matchers_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-matchers_test.cc
+deleted file mode 100644
+index 1f48a76..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock-more-actions_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-more-actions_test.cc
+deleted file mode 100644
+index 53bb029..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock-nice-strict_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-nice-strict_test.cc
+deleted file mode 100644
+index 25558eb..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock-port_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-port_test.cc
+deleted file mode 100644
+index a2c2be2..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock-pp-string_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-pp-string_test.cc
+deleted file mode 100644
+index 6f66cf1..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock-pp_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-pp_test.cc
+deleted file mode 100644
+index 5d1566e..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock-spec-builders_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-spec-builders_test.cc
+deleted file mode 100644
+index fa97411..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_all_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_all_test.cc
+deleted file mode 100644
+index fffbb8b..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_ex_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_ex_test.cc
+deleted file mode 100644
+index 72eb43f..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_leak_test.py b/test/gtest-1.11.0/googlemock/test/gmock_leak_test.py
+deleted file mode 100755
+index 7e4b1ee..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_leak_test_.cc b/test/gtest-1.11.0/googlemock/test/gmock_leak_test_.cc
+deleted file mode 100644
+index 2e095ab..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_link2_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_link2_test.cc
+deleted file mode 100644
+index d27ce17..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_link_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_link_test.cc
+deleted file mode 100644
+index e7c54cc..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_link_test.h b/test/gtest-1.11.0/googlemock/test/gmock_link_test.h
+deleted file mode 100644
+index 5734b2e..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_output_test.py b/test/gtest-1.11.0/googlemock/test/gmock_output_test.py
+deleted file mode 100755
+index 25f99f2..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_output_test_.cc b/test/gtest-1.11.0/googlemock/test/gmock_output_test_.cc
+deleted file mode 100644
+index 3955c73..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_output_test_golden.txt b/test/gtest-1.11.0/googlemock/test/gmock_output_test_golden.txt
+deleted file mode 100644
+index 4846c12..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_stress_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_stress_test.cc
+deleted file mode 100644
+index 20725d6..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_test.cc
+deleted file mode 100644
+index e9840a3..0000000
+diff --git a/test/gtest-1.11.0/googlemock/test/gmock_test_utils.py b/test/gtest-1.11.0/googlemock/test/gmock_test_utils.py
+deleted file mode 100755
+index 7dc4e11..0000000
+diff --git a/test/gtest-1.11.0/googletest/CMakeLists.txt b/test/gtest-1.11.0/googletest/CMakeLists.txt
+deleted file mode 100644
+index abdd98b..0000000
+diff --git a/test/gtest-1.11.0/googletest/README.md b/test/gtest-1.11.0/googletest/README.md
+deleted file mode 100644
+index 1f8b349..0000000
+diff --git a/test/gtest-1.11.0/googletest/cmake/Config.cmake.in b/test/gtest-1.11.0/googletest/cmake/Config.cmake.in
+deleted file mode 100644
+index 12be449..0000000
+diff --git a/test/gtest-1.11.0/googletest/cmake/gtest.pc.in b/test/gtest-1.11.0/googletest/cmake/gtest.pc.in
+deleted file mode 100644
+index b4148fa..0000000
+diff --git a/test/gtest-1.11.0/googletest/cmake/gtest_main.pc.in b/test/gtest-1.11.0/googletest/cmake/gtest_main.pc.in
+deleted file mode 100644
+index 38c88c5..0000000
+diff --git a/test/gtest-1.11.0/googletest/cmake/internal_utils.cmake b/test/gtest-1.11.0/googletest/cmake/internal_utils.cmake
+deleted file mode 100644
+index 8d8d60a..0000000
+diff --git a/test/gtest-1.11.0/googletest/cmake/libgtest.la.in b/test/gtest-1.11.0/googletest/cmake/libgtest.la.in
+deleted file mode 100644
+index 840c838..0000000
+diff --git a/test/gtest-1.11.0/googletest/docs/README.md b/test/gtest-1.11.0/googletest/docs/README.md
+deleted file mode 100644
+index 1bc57b7..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-death-test.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-death-test.h
+deleted file mode 100644
+index 9b4d4d1..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-matchers.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-matchers.h
+deleted file mode 100644
+index 9fa34a0..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-message.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-message.h
+deleted file mode 100644
+index becfd49..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-param-test.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-param-test.h
+deleted file mode 100644
+index 804e702..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-printers.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-printers.h
+deleted file mode 100644
+index 076c9de..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-spi.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-spi.h
+deleted file mode 100644
+index eacef44..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-test-part.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-test-part.h
+deleted file mode 100644
+index 203fdf9..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-typed-test.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-typed-test.h
+deleted file mode 100644
+index 9fdc6be..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest.h b/test/gtest-1.11.0/googletest/include/gtest/gtest.h
+deleted file mode 100644
+index 7a5d057..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest_pred_impl.h b/test/gtest-1.11.0/googletest/include/gtest/gtest_pred_impl.h
+deleted file mode 100644
+index 5029a9b..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest_prod.h b/test/gtest-1.11.0/googletest/include/gtest/gtest_prod.h
+deleted file mode 100644
+index 38b9d85..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/custom/README.md b/test/gtest-1.11.0/googletest/include/gtest/internal/custom/README.md
+deleted file mode 100644
+index ff391fb..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-port.h b/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-port.h
+deleted file mode 100644
+index db02881..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-printers.h b/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-printers.h
+deleted file mode 100644
+index b9495d8..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest.h b/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest.h
+deleted file mode 100644
+index afaaf17..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-death-test-internal.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-death-test-internal.h
+deleted file mode 100644
+index 490296d..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-filepath.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-filepath.h
+deleted file mode 100644
+index 0c033ab..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-internal.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-internal.h
+deleted file mode 100644
+index f8cbdbd..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-param-util.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-param-util.h
+deleted file mode 100644
+index c2ef6e3..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port-arch.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port-arch.h
+deleted file mode 100644
+index dd84591..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port.h
+deleted file mode 100644
+index 0953a78..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-string.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-string.h
+deleted file mode 100644
+index 10f774f..0000000
+diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-type-util.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-type-util.h
+deleted file mode 100644
+index b87a2e2..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/prime_tables.h b/test/gtest-1.11.0/googletest/samples/prime_tables.h
+deleted file mode 100644
+index 3a10352..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample1.cc b/test/gtest-1.11.0/googletest/samples/sample1.cc
+deleted file mode 100644
+index 1d42759..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample1.h b/test/gtest-1.11.0/googletest/samples/sample1.h
+deleted file mode 100644
+index ba392cf..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample10_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample10_unittest.cc
+deleted file mode 100644
+index 36cdac2..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample1_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample1_unittest.cc
+deleted file mode 100644
+index cb08b61..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample2.cc b/test/gtest-1.11.0/googletest/samples/sample2.cc
+deleted file mode 100644
+index d8e8723..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample2.h b/test/gtest-1.11.0/googletest/samples/sample2.h
+deleted file mode 100644
+index 0f98689..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample2_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample2_unittest.cc
+deleted file mode 100644
+index 41e31c1..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample3-inl.h b/test/gtest-1.11.0/googletest/samples/sample3-inl.h
+deleted file mode 100644
+index 659e0f0..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample3_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample3_unittest.cc
+deleted file mode 100644
+index b19416d..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample4.cc b/test/gtest-1.11.0/googletest/samples/sample4.cc
+deleted file mode 100644
+index b0ee609..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample4.h b/test/gtest-1.11.0/googletest/samples/sample4.h
+deleted file mode 100644
+index 0c4ed92..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample4_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample4_unittest.cc
+deleted file mode 100644
+index d5144c0..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample5_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample5_unittest.cc
+deleted file mode 100644
+index 0a21dd2..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample6_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample6_unittest.cc
+deleted file mode 100644
+index da317ee..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample7_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample7_unittest.cc
+deleted file mode 100644
+index e0efc29..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample8_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample8_unittest.cc
+deleted file mode 100644
+index 10488b0..0000000
+diff --git a/test/gtest-1.11.0/googletest/samples/sample9_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample9_unittest.cc
+deleted file mode 100644
+index e502d08..0000000
+diff --git a/test/gtest-1.11.0/googletest/scripts/README.md b/test/gtest-1.11.0/googletest/scripts/README.md
+deleted file mode 100644
+index fa359fe..0000000
+diff --git a/test/gtest-1.11.0/googletest/scripts/common.py b/test/gtest-1.11.0/googletest/scripts/common.py
+deleted file mode 100644
+index 3c0347a..0000000
+diff --git a/test/gtest-1.11.0/googletest/scripts/fuse_gtest_files.py b/test/gtest-1.11.0/googletest/scripts/fuse_gtest_files.py
+deleted file mode 100755
+index d0dd464..0000000
+diff --git a/test/gtest-1.11.0/googletest/scripts/gen_gtest_pred_impl.py b/test/gtest-1.11.0/googletest/scripts/gen_gtest_pred_impl.py
+deleted file mode 100755
+index e09a6e0..0000000
+diff --git a/test/gtest-1.11.0/googletest/scripts/gtest-config.in b/test/gtest-1.11.0/googletest/scripts/gtest-config.in
+deleted file mode 100755
+index 780f843..0000000
+diff --git a/test/gtest-1.11.0/googletest/scripts/release_docs.py b/test/gtest-1.11.0/googletest/scripts/release_docs.py
+deleted file mode 100755
+index 8d24f28..0000000
+diff --git a/test/gtest-1.11.0/googletest/scripts/run_with_path.py b/test/gtest-1.11.0/googletest/scripts/run_with_path.py
+deleted file mode 100755
+index d46ab4d..0000000
+diff --git a/test/gtest-1.11.0/googletest/scripts/upload.py b/test/gtest-1.11.0/googletest/scripts/upload.py
+deleted file mode 100755
+index eba5711..0000000
+diff --git a/test/gtest-1.11.0/googletest/scripts/upload_gtest.py b/test/gtest-1.11.0/googletest/scripts/upload_gtest.py
+deleted file mode 100755
+index be19ae8..0000000
+diff --git a/test/gtest-1.11.0/googletest/src/gtest-all.cc b/test/gtest-1.11.0/googletest/src/gtest-all.cc
+deleted file mode 100644
+index ad29290..0000000
+diff --git a/test/gtest-1.11.0/googletest/src/gtest-death-test.cc b/test/gtest-1.11.0/googletest/src/gtest-death-test.cc
+deleted file mode 100644
+index bf4f633..0000000
+diff --git a/test/gtest-1.11.0/googletest/src/gtest-filepath.cc b/test/gtest-1.11.0/googletest/src/gtest-filepath.cc
+deleted file mode 100644
+index 0b56294..0000000
+diff --git a/test/gtest-1.11.0/googletest/src/gtest-internal-inl.h b/test/gtest-1.11.0/googletest/src/gtest-internal-inl.h
+deleted file mode 100644
+index 6d8cecb..0000000
+diff --git a/test/gtest-1.11.0/googletest/src/gtest-matchers.cc b/test/gtest-1.11.0/googletest/src/gtest-matchers.cc
+deleted file mode 100644
+index 65104eb..0000000
+diff --git a/test/gtest-1.11.0/googletest/src/gtest-port.cc b/test/gtest-1.11.0/googletest/src/gtest-port.cc
+deleted file mode 100644
+index 53a4d37..0000000
+diff --git a/test/gtest-1.11.0/googletest/src/gtest-printers.cc b/test/gtest-1.11.0/googletest/src/gtest-printers.cc
+deleted file mode 100644
+index 1b68fcb..0000000
+diff --git a/test/gtest-1.11.0/googletest/src/gtest-test-part.cc b/test/gtest-1.11.0/googletest/src/gtest-test-part.cc
+deleted file mode 100644
+index a938683..0000000
+diff --git a/test/gtest-1.11.0/googletest/src/gtest-typed-test.cc b/test/gtest-1.11.0/googletest/src/gtest-typed-test.cc
+deleted file mode 100644
+index c02c3df..0000000
+diff --git a/test/gtest-1.11.0/googletest/src/gtest.cc b/test/gtest-1.11.0/googletest/src/gtest.cc
+deleted file mode 100644
+index 21c611a..0000000
+diff --git a/test/gtest-1.11.0/googletest/src/gtest_main.cc b/test/gtest-1.11.0/googletest/src/gtest_main.cc
+deleted file mode 100644
+index 46b27c3..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/BUILD.bazel b/test/gtest-1.11.0/googletest/test/BUILD.bazel
+deleted file mode 100644
+index b06a00a..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest.py
+deleted file mode 100755
+index a5dfbc6..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest_.cc b/test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest_.cc
+deleted file mode 100644
+index f84957a..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test.py b/test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test.py
+deleted file mode 100755
+index 94a5b33..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test_.cc
+deleted file mode 100644
+index 8c127d4..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-color-test.py b/test/gtest-1.11.0/googletest/test/googletest-color-test.py
+deleted file mode 100755
+index f3b7c99..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-color-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-color-test_.cc
+deleted file mode 100644
+index 220a3a0..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-death-test-test.cc b/test/gtest-1.11.0/googletest/test/googletest-death-test-test.cc
+deleted file mode 100644
+index c0b3d1f..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-death-test_ex_test.cc b/test/gtest-1.11.0/googletest/test/googletest-death-test_ex_test.cc
+deleted file mode 100644
+index 7219680..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-env-var-test.py b/test/gtest-1.11.0/googletest/test/googletest-env-var-test.py
+deleted file mode 100755
+index 02c3655..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-env-var-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-env-var-test_.cc
+deleted file mode 100644
+index 52f9586..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-failfast-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-failfast-unittest.py
+deleted file mode 100755
+index 3aeb2df..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-failfast-unittest_.cc b/test/gtest-1.11.0/googletest/test/googletest-failfast-unittest_.cc
+deleted file mode 100644
+index 0b2c951..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-filepath-test.cc b/test/gtest-1.11.0/googletest/test/googletest-filepath-test.cc
+deleted file mode 100644
+index aafad36..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-filter-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-filter-unittest.py
+deleted file mode 100755
+index 6b32f2d..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-filter-unittest_.cc b/test/gtest-1.11.0/googletest/test/googletest-filter-unittest_.cc
+deleted file mode 100644
+index d30ec9c..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest.py
+deleted file mode 100644
+index 32ba628..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest_.cc b/test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest_.cc
+deleted file mode 100644
+index f401b2f..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-json-outfiles-test.py b/test/gtest-1.11.0/googletest/test/googletest-json-outfiles-test.py
+deleted file mode 100644
+index 8ef47b8..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-json-output-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-json-output-unittest.py
+deleted file mode 100644
+index 41c8565..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest.py
+deleted file mode 100755
+index 81423a3..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest_.cc b/test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest_.cc
+deleted file mode 100644
+index 493c6f0..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-listener-test.cc b/test/gtest-1.11.0/googletest/test/googletest-listener-test.cc
+deleted file mode 100644
+index 10457af..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-message-test.cc b/test/gtest-1.11.0/googletest/test/googletest-message-test.cc
+deleted file mode 100644
+index 962d519..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-options-test.cc b/test/gtest-1.11.0/googletest/test/googletest-options-test.cc
+deleted file mode 100644
+index 11fb1f2..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-output-test-golden-lin.txt b/test/gtest-1.11.0/googletest/test/googletest-output-test-golden-lin.txt
+deleted file mode 100644
+index 3fab3b9..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-output-test.py b/test/gtest-1.11.0/googletest/test/googletest-output-test.py
+deleted file mode 100755
+index 09028f6..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-output-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-output-test_.cc
+deleted file mode 100644
+index 074f64e..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test.py b/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test.py
+deleted file mode 100644
+index 2a08477..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test_.cc
+deleted file mode 100644
+index 955d699..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test.py b/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test.py
+deleted file mode 100644
+index ab838f4..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test_.cc
+deleted file mode 100644
+index 76371df..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-test.cc b/test/gtest-1.11.0/googletest/test/googletest-param-test-test.cc
+deleted file mode 100644
+index 023aa46..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-test.h b/test/gtest-1.11.0/googletest/test/googletest-param-test-test.h
+deleted file mode 100644
+index 8919375..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test2-test.cc b/test/gtest-1.11.0/googletest/test/googletest-param-test2-test.cc
+deleted file mode 100644
+index 2a29fb1..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-port-test.cc b/test/gtest-1.11.0/googletest/test/googletest-port-test.cc
+deleted file mode 100644
+index 1e0c861..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-printers-test.cc b/test/gtest-1.11.0/googletest/test/googletest-printers-test.cc
+deleted file mode 100644
+index e1e8e1c..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test.py b/test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test.py
+deleted file mode 100755
+index c82162f..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test_.cc
+deleted file mode 100644
+index a4bc4ef..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-shuffle-test.py b/test/gtest-1.11.0/googletest/test/googletest-shuffle-test.py
+deleted file mode 100755
+index 573cc5e..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-shuffle-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-shuffle-test_.cc
+deleted file mode 100644
+index 4505663..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-test-part-test.cc b/test/gtest-1.11.0/googletest/test/googletest-test-part-test.cc
+deleted file mode 100644
+index 44cf7ca..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test.py b/test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test.py
+deleted file mode 100755
+index ea627c4..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test_.cc
+deleted file mode 100644
+index 83bb914..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-uninitialized-test.py b/test/gtest-1.11.0/googletest/test/googletest-uninitialized-test.py
+deleted file mode 100755
+index 69595a0..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/googletest-uninitialized-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-uninitialized-test_.cc
+deleted file mode 100644
+index b4434d5..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest-typed-test2_test.cc b/test/gtest-1.11.0/googletest/test/gtest-typed-test2_test.cc
+deleted file mode 100644
+index e83ca2e..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest-typed-test_test.cc b/test/gtest-1.11.0/googletest/test/gtest-typed-test_test.cc
+deleted file mode 100644
+index 5fc678c..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest-typed-test_test.h b/test/gtest-1.11.0/googletest/test/gtest-typed-test_test.h
+deleted file mode 100644
+index 8ce559c..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest-unittest-api_test.cc b/test/gtest-1.11.0/googletest/test/gtest-unittest-api_test.cc
+deleted file mode 100644
+index 8ef5058..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_all_test.cc b/test/gtest-1.11.0/googletest/test/gtest_all_test.cc
+deleted file mode 100644
+index 615b29b..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_assert_by_exception_test.cc b/test/gtest-1.11.0/googletest/test/gtest_assert_by_exception_test.cc
+deleted file mode 100644
+index ada4cb3..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_environment_test.cc b/test/gtest-1.11.0/googletest/test/gtest_environment_test.cc
+deleted file mode 100644
+index 064bfc5..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_help_test.py b/test/gtest-1.11.0/googletest/test/gtest_help_test.py
+deleted file mode 100755
+index 8d953bb..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_help_test_.cc b/test/gtest-1.11.0/googletest/test/gtest_help_test_.cc
+deleted file mode 100644
+index 750ae6c..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_json_test_utils.py b/test/gtest-1.11.0/googletest/test/gtest_json_test_utils.py
+deleted file mode 100644
+index 62bbfc2..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_list_output_unittest.py b/test/gtest-1.11.0/googletest/test/gtest_list_output_unittest.py
+deleted file mode 100644
+index a442fc1..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_list_output_unittest_.cc b/test/gtest-1.11.0/googletest/test/gtest_list_output_unittest_.cc
+deleted file mode 100644
+index 92b9d4f..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_main_unittest.cc b/test/gtest-1.11.0/googletest/test/gtest_main_unittest.cc
+deleted file mode 100644
+index eddedea..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_no_test_unittest.cc b/test/gtest-1.11.0/googletest/test/gtest_no_test_unittest.cc
+deleted file mode 100644
+index d4f88db..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_pred_impl_unittest.cc b/test/gtest-1.11.0/googletest/test/gtest_pred_impl_unittest.cc
+deleted file mode 100644
+index bbef994..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_premature_exit_test.cc b/test/gtest-1.11.0/googletest/test/gtest_premature_exit_test.cc
+deleted file mode 100644
+index 1d1187e..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_prod_test.cc b/test/gtest-1.11.0/googletest/test/gtest_prod_test.cc
+deleted file mode 100644
+index ede81a0..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_repeat_test.cc b/test/gtest-1.11.0/googletest/test/gtest_repeat_test.cc
+deleted file mode 100644
+index 7da4a15..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_skip_check_output_test.py b/test/gtest-1.11.0/googletest/test/gtest_skip_check_output_test.py
+deleted file mode 100755
+index 14e63ab..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_skip_environment_check_output_test.py b/test/gtest-1.11.0/googletest/test/gtest_skip_environment_check_output_test.py
+deleted file mode 100755
+index 6e79155..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_skip_in_environment_setup_test.cc b/test/gtest-1.11.0/googletest/test/gtest_skip_in_environment_setup_test.cc
+deleted file mode 100644
+index 9372310..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_skip_test.cc b/test/gtest-1.11.0/googletest/test/gtest_skip_test.cc
+deleted file mode 100644
+index 4a23004..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_sole_header_test.cc b/test/gtest-1.11.0/googletest/test/gtest_sole_header_test.cc
+deleted file mode 100644
+index 1d94ac6..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_stress_test.cc b/test/gtest-1.11.0/googletest/test/gtest_stress_test.cc
+deleted file mode 100644
+index 8434819..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_test_macro_stack_footprint_test.cc b/test/gtest-1.11.0/googletest/test/gtest_test_macro_stack_footprint_test.cc
+deleted file mode 100644
+index a48db05..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_test_utils.py b/test/gtest-1.11.0/googletest/test/gtest_test_utils.py
+deleted file mode 100755
+index d0c2446..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_testbridge_test.py b/test/gtest-1.11.0/googletest/test/gtest_testbridge_test.py
+deleted file mode 100755
+index 87ffad7..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_testbridge_test_.cc b/test/gtest-1.11.0/googletest/test/gtest_testbridge_test_.cc
+deleted file mode 100644
+index 24617b2..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_throw_on_failure_ex_test.cc b/test/gtest-1.11.0/googletest/test/gtest_throw_on_failure_ex_test.cc
+deleted file mode 100644
+index 1d95adb..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_unittest.cc b/test/gtest-1.11.0/googletest/test/gtest_unittest.cc
+deleted file mode 100644
+index 1730e8b..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_outfile1_test_.cc b/test/gtest-1.11.0/googletest/test/gtest_xml_outfile1_test_.cc
+deleted file mode 100644
+index 19aa252..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_outfile2_test_.cc b/test/gtest-1.11.0/googletest/test/gtest_xml_outfile2_test_.cc
+deleted file mode 100644
+index f9a2a6e..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_outfiles_test.py b/test/gtest-1.11.0/googletest/test/gtest_xml_outfiles_test.py
+deleted file mode 100755
+index ac66feb..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest.py b/test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest.py
+deleted file mode 100755
+index eade7aa..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest_.cc b/test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest_.cc
+deleted file mode 100644
+index c0036aa..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_test_utils.py b/test/gtest-1.11.0/googletest/test/gtest_xml_test_utils.py
+deleted file mode 100755
+index ec42c62..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/production.cc b/test/gtest-1.11.0/googletest/test/production.cc
+deleted file mode 100644
+index 0f69f6d..0000000
+diff --git a/test/gtest-1.11.0/googletest/test/production.h b/test/gtest-1.11.0/googletest/test/production.h
+deleted file mode 100644
+index 41a5472..0000000
+diff --git a/test/gtest-1.11.0/library.json b/test/gtest-1.11.0/library.json
+deleted file mode 100644
+index f61bf00..0000000
+diff --git a/test/handler_test.h b/test/handler_test.h
+deleted file mode 100644
+index 668a58d..0000000
+diff --git a/test/integration/emitter_test.cpp b/test/integration/emitter_test.cpp
+deleted file mode 100644
+index b277d57..0000000
+diff --git a/test/integration/encoding_test.cpp b/test/integration/encoding_test.cpp
+deleted file mode 100644
+index 9bd6586..0000000
+diff --git a/test/integration/error_messages_test.cpp b/test/integration/error_messages_test.cpp
+deleted file mode 100644
+index 64ab6b9..0000000
+diff --git a/test/integration/gen_emitter_test.cpp b/test/integration/gen_emitter_test.cpp
+deleted file mode 100644
+index 3536144..0000000
+diff --git a/test/integration/handler_spec_test.cpp b/test/integration/handler_spec_test.cpp
+deleted file mode 100644
+index 8cba902..0000000
+diff --git a/test/integration/handler_test.cpp b/test/integration/handler_test.cpp
+deleted file mode 100644
+index 6011460..0000000
+diff --git a/test/integration/load_node_test.cpp b/test/integration/load_node_test.cpp
+deleted file mode 100644
+index 9d0c790..0000000
+diff --git a/test/integration/node_spec_test.cpp b/test/integration/node_spec_test.cpp
+deleted file mode 100644
+index bfc8578..0000000
+diff --git a/test/main.cpp b/test/main.cpp
+deleted file mode 100644
+index 443e2db..0000000
+diff --git a/test/mock_event_handler.h b/test/mock_event_handler.h
+deleted file mode 100644
+index 0b7e7da..0000000
+diff --git a/test/node/node_test.cpp b/test/node/node_test.cpp
+deleted file mode 100644
+index 5f41ef2..0000000
+diff --git a/test/ostream_wrapper_test.cpp b/test/ostream_wrapper_test.cpp
+deleted file mode 100644
+index ff4f635..0000000
+diff --git a/test/parser_test.cpp b/test/parser_test.cpp
+deleted file mode 100644
+index e5002a4..0000000
+diff --git a/test/regex_test.cpp b/test/regex_test.cpp
+deleted file mode 100644
+index 658db9e..0000000
+diff --git a/test/specexamples.h b/test/specexamples.h
+deleted file mode 100644
+index 46e2c4c..0000000
+diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt
+deleted file mode 100644
+index 87ea4f9..0000000
+diff --git a/util/api.cpp b/util/api.cpp
+deleted file mode 100644
+index 8ae5ff2..0000000
+diff --git a/util/parse.cpp b/util/parse.cpp
+deleted file mode 100644
+index ea4295a..0000000
+diff --git a/util/read.cpp b/util/read.cpp
+deleted file mode 100644
+index 3455ea3..0000000
+diff --git a/util/sandbox.cpp b/util/sandbox.cpp
+deleted file mode 100644
+index f21490e..0000000
+diff --git a/yaml-cpp-config.cmake.in b/yaml-cpp-config.cmake.in
+deleted file mode 100644
+index 799b9b4..0000000
+diff --git a/yaml-cpp.pc.in b/yaml-cpp.pc.in
+deleted file mode 100644
+index d02dc9e..0000000
+--
+2.42.0
+
diff --git a/src/libs/3rdparty/yaml-cpp/src/binary.cpp b/src/libs/3rdparty/yaml-cpp/src/binary.cpp
new file mode 100644
index 0000000000..d27762a243
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/binary.cpp
@@ -0,0 +1,100 @@
+#include "yaml-cpp/binary.h"
+
+#include <cctype>
+
+namespace YAML {
+static const char encoding[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+std::string EncodeBase64(const unsigned char *data, std::size_t size) {
+ const char PAD = '=';
+
+ std::string ret;
+ ret.resize(4 * size / 3 + 3);
+ char *out = &ret[0];
+
+ std::size_t chunks = size / 3;
+ std::size_t remainder = size % 3;
+
+ for (std::size_t i = 0; i < chunks; i++, data += 3) {
+ *out++ = encoding[data[0] >> 2];
+ *out++ = encoding[((data[0] & 0x3) << 4) | (data[1] >> 4)];
+ *out++ = encoding[((data[1] & 0xf) << 2) | (data[2] >> 6)];
+ *out++ = encoding[data[2] & 0x3f];
+ }
+
+ switch (remainder) {
+ case 0:
+ break;
+ case 1:
+ *out++ = encoding[data[0] >> 2];
+ *out++ = encoding[((data[0] & 0x3) << 4)];
+ *out++ = PAD;
+ *out++ = PAD;
+ break;
+ case 2:
+ *out++ = encoding[data[0] >> 2];
+ *out++ = encoding[((data[0] & 0x3) << 4) | (data[1] >> 4)];
+ *out++ = encoding[((data[1] & 0xf) << 2)];
+ *out++ = PAD;
+ break;
+ }
+
+ ret.resize(out - &ret[0]);
+ return ret;
+}
+
+static const unsigned char decoding[] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255,
+ 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
+ 255, 0, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255,
+};
+
+std::vector<unsigned char> DecodeBase64(const std::string &input) {
+ using ret_type = std::vector<unsigned char>;
+ if (input.empty())
+ return ret_type();
+
+ ret_type ret(3 * input.size() / 4 + 1);
+ unsigned char *out = &ret[0];
+
+ unsigned value = 0;
+ for (std::size_t i = 0, cnt = 0; i < input.size(); i++) {
+ if (std::isspace(static_cast<unsigned char>(input[i]))) {
+ // skip newlines
+ continue;
+ }
+ unsigned char d = decoding[static_cast<unsigned char>(input[i])];
+ if (d == 255)
+ return ret_type();
+
+ value = (value << 6) | d;
+ if (cnt % 4 == 3) {
+ *out++ = value >> 16;
+ if (i > 0 && input[i - 1] != '=')
+ *out++ = value >> 8;
+ if (input[i] != '=')
+ *out++ = value;
+ }
+ ++cnt;
+ }
+
+ ret.resize(out - &ret[0]);
+ return ret;
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/collectionstack.h b/src/libs/3rdparty/yaml-cpp/src/collectionstack.h
new file mode 100644
index 0000000000..9feba96795
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/collectionstack.h
@@ -0,0 +1,41 @@
+#ifndef COLLECTIONSTACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define COLLECTIONSTACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <cassert>
+#include <stack>
+
+namespace YAML {
+struct CollectionType {
+ enum value { NoCollection, BlockMap, BlockSeq, FlowMap, FlowSeq, CompactMap };
+};
+
+class CollectionStack {
+ public:
+ CollectionStack() : collectionStack{} {}
+ CollectionType::value GetCurCollectionType() const {
+ if (collectionStack.empty())
+ return CollectionType::NoCollection;
+ return collectionStack.top();
+ }
+
+ void PushCollectionType(CollectionType::value type) {
+ collectionStack.push(type);
+ }
+ void PopCollectionType(CollectionType::value type) {
+ assert(type == GetCurCollectionType());
+ (void)type;
+ collectionStack.pop();
+ }
+
+ private:
+ std::stack<CollectionType::value> collectionStack;
+};
+} // namespace YAML
+
+#endif // COLLECTIONSTACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/convert.cpp b/src/libs/3rdparty/yaml-cpp/src/convert.cpp
new file mode 100644
index 0000000000..9658b36035
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/convert.cpp
@@ -0,0 +1,74 @@
+#include <algorithm>
+
+#include "yaml-cpp/node/convert.h"
+
+namespace {
+// we're not gonna mess with the mess that is all the isupper/etc. functions
+bool IsLower(char ch) { return 'a' <= ch && ch <= 'z'; }
+bool IsUpper(char ch) { return 'A' <= ch && ch <= 'Z'; }
+char ToLower(char ch) { return IsUpper(ch) ? ch + 'a' - 'A' : ch; }
+
+std::string tolower(const std::string& str) {
+ std::string s(str);
+ std::transform(s.begin(), s.end(), s.begin(), ToLower);
+ return s;
+}
+
+template <typename T>
+bool IsEntirely(const std::string& str, T func) {
+ return std::all_of(str.begin(), str.end(), [=](char ch) { return func(ch); });
+}
+
+// IsFlexibleCase
+// . Returns true if 'str' is:
+// . UPPERCASE
+// . lowercase
+// . Capitalized
+bool IsFlexibleCase(const std::string& str) {
+ if (str.empty())
+ return true;
+
+ if (IsEntirely(str, IsLower))
+ return true;
+
+ bool firstcaps = IsUpper(str[0]);
+ std::string rest = str.substr(1);
+ return firstcaps && (IsEntirely(rest, IsLower) || IsEntirely(rest, IsUpper));
+}
+} // namespace
+
+namespace YAML {
+bool convert<bool>::decode(const Node& node, bool& rhs) {
+ if (!node.IsScalar())
+ return false;
+
+ // we can't use iostream bool extraction operators as they don't
+ // recognize all possible values in the table below (taken from
+ // http://yaml.org/type/bool.html)
+ static const struct {
+ std::string truename, falsename;
+ } names[] = {
+ {"y", "n"},
+ {"yes", "no"},
+ {"true", "false"},
+ {"on", "off"},
+ };
+
+ if (!IsFlexibleCase(node.Scalar()))
+ return false;
+
+ for (const auto& name : names) {
+ if (name.truename == tolower(node.Scalar())) {
+ rhs = true;
+ return true;
+ }
+
+ if (name.falsename == tolower(node.Scalar())) {
+ rhs = false;
+ return true;
+ }
+ }
+
+ return false;
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/depthguard.cpp b/src/libs/3rdparty/yaml-cpp/src/depthguard.cpp
new file mode 100644
index 0000000000..5bf6cdf03b
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/depthguard.cpp
@@ -0,0 +1,9 @@
+#include "yaml-cpp/depthguard.h"
+
+namespace YAML {
+
+DeepRecursion::DeepRecursion(int depth, const Mark& mark_,
+ const std::string& msg_)
+ : ParserException(mark_, msg_), m_depth(depth) {}
+
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/directives.cpp b/src/libs/3rdparty/yaml-cpp/src/directives.cpp
new file mode 100644
index 0000000000..4c1dc201d7
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/directives.cpp
@@ -0,0 +1,17 @@
+#include "directives.h"
+
+namespace YAML {
+Directives::Directives() : version{true, 1, 2}, tags{} {}
+
+std::string Directives::TranslateTagHandle(
+ const std::string& handle) const {
+ auto it = tags.find(handle);
+ if (it == tags.end()) {
+ if (handle == "!!")
+ return "tag:yaml.org,2002:";
+ return handle;
+ }
+
+ return it->second;
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/directives.h b/src/libs/3rdparty/yaml-cpp/src/directives.h
new file mode 100644
index 0000000000..18d14c9c0b
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/directives.h
@@ -0,0 +1,29 @@
+#ifndef DIRECTIVES_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define DIRECTIVES_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <string>
+#include <map>
+
+namespace YAML {
+struct Version {
+ bool isDefault;
+ int major, minor;
+};
+
+struct Directives {
+ Directives();
+
+ std::string TranslateTagHandle(const std::string& handle) const;
+
+ Version version;
+ std::map<std::string, std::string> tags;
+};
+}
+
+#endif // DIRECTIVES_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/emit.cpp b/src/libs/3rdparty/yaml-cpp/src/emit.cpp
new file mode 100644
index 0000000000..b0efb8401c
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/emit.cpp
@@ -0,0 +1,25 @@
+#include "yaml-cpp/node/emit.h"
+#include "nodeevents.h"
+#include "yaml-cpp/emitfromevents.h"
+#include "yaml-cpp/emitter.h"
+
+namespace YAML {
+Emitter& operator<<(Emitter& out, const Node& node) {
+ EmitFromEvents emitFromEvents(out);
+ NodeEvents events(node);
+ events.Emit(emitFromEvents);
+ return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const Node& node) {
+ Emitter emitter(out);
+ emitter << node;
+ return out;
+}
+
+std::string Dump(const Node& node) {
+ Emitter emitter;
+ emitter << node;
+ return emitter.c_str();
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/emitfromevents.cpp b/src/libs/3rdparty/yaml-cpp/src/emitfromevents.cpp
new file mode 100644
index 0000000000..2e97187b90
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/emitfromevents.cpp
@@ -0,0 +1,124 @@
+#include <cassert>
+#include <sstream>
+
+#include "yaml-cpp/emitfromevents.h"
+#include "yaml-cpp/emitter.h"
+#include "yaml-cpp/emittermanip.h"
+#include "yaml-cpp/null.h"
+
+namespace YAML {
+struct Mark;
+} // namespace YAML
+
+namespace {
+std::string ToString(YAML::anchor_t anchor) {
+ std::stringstream stream;
+ stream << anchor;
+ return stream.str();
+}
+} // namespace
+
+namespace YAML {
+EmitFromEvents::EmitFromEvents(Emitter& emitter)
+ : m_emitter(emitter), m_stateStack{} {}
+
+void EmitFromEvents::OnDocumentStart(const Mark&) {}
+
+void EmitFromEvents::OnDocumentEnd() {}
+
+void EmitFromEvents::OnNull(const Mark&, anchor_t anchor) {
+ BeginNode();
+ EmitProps("", anchor);
+ m_emitter << Null;
+}
+
+void EmitFromEvents::OnAlias(const Mark&, anchor_t anchor) {
+ BeginNode();
+ m_emitter << Alias(ToString(anchor));
+}
+
+void EmitFromEvents::OnScalar(const Mark&, const std::string& tag,
+ anchor_t anchor, const std::string& value) {
+ BeginNode();
+ EmitProps(tag, anchor);
+ m_emitter << value;
+}
+
+void EmitFromEvents::OnSequenceStart(const Mark&, const std::string& tag,
+ anchor_t anchor,
+ EmitterStyle::value style) {
+ BeginNode();
+ EmitProps(tag, anchor);
+ switch (style) {
+ case EmitterStyle::Block:
+ m_emitter << Block;
+ break;
+ case EmitterStyle::Flow:
+ m_emitter << Flow;
+ break;
+ default:
+ break;
+ }
+ // Restore the global settings to eliminate the override from node style
+ m_emitter.RestoreGlobalModifiedSettings();
+ m_emitter << BeginSeq;
+ m_stateStack.push(State::WaitingForSequenceEntry);
+}
+
+void EmitFromEvents::OnSequenceEnd() {
+ m_emitter << EndSeq;
+ assert(m_stateStack.top() == State::WaitingForSequenceEntry);
+ m_stateStack.pop();
+}
+
+void EmitFromEvents::OnMapStart(const Mark&, const std::string& tag,
+ anchor_t anchor, EmitterStyle::value style) {
+ BeginNode();
+ EmitProps(tag, anchor);
+ switch (style) {
+ case EmitterStyle::Block:
+ m_emitter << Block;
+ break;
+ case EmitterStyle::Flow:
+ m_emitter << Flow;
+ break;
+ default:
+ break;
+ }
+ // Restore the global settings to eliminate the override from node style
+ m_emitter.RestoreGlobalModifiedSettings();
+ m_emitter << BeginMap;
+ m_stateStack.push(State::WaitingForKey);
+}
+
+void EmitFromEvents::OnMapEnd() {
+ m_emitter << EndMap;
+ assert(m_stateStack.top() == State::WaitingForKey);
+ m_stateStack.pop();
+}
+
+void EmitFromEvents::BeginNode() {
+ if (m_stateStack.empty())
+ return;
+
+ switch (m_stateStack.top()) {
+ case State::WaitingForKey:
+ m_emitter << Key;
+ m_stateStack.top() = State::WaitingForValue;
+ break;
+ case State::WaitingForValue:
+ m_emitter << Value;
+ m_stateStack.top() = State::WaitingForKey;
+ break;
+ default:
+ break;
+ }
+}
+
+void EmitFromEvents::EmitProps(const std::string& tag, anchor_t anchor) {
+ if (!tag.empty() && tag != "?" && tag != "!")
+ m_emitter << VerbatimTag(tag);
+ if (anchor)
+ m_emitter << Anchor(ToString(anchor));
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/emitter.cpp b/src/libs/3rdparty/yaml-cpp/src/emitter.cpp
new file mode 100644
index 0000000000..4d483075bd
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/emitter.cpp
@@ -0,0 +1,972 @@
+#include <sstream>
+
+#include "emitterutils.h"
+#include "indentation.h" // IWYU pragma: keep
+#include "yaml-cpp/emitter.h"
+#include "yaml-cpp/emitterdef.h"
+#include "yaml-cpp/emittermanip.h"
+#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
+
+namespace YAML {
+class Binary;
+struct _Null;
+
+Emitter::Emitter() : m_pState(new EmitterState), m_stream{} {}
+
+Emitter::Emitter(std::ostream& stream)
+ : m_pState(new EmitterState), m_stream(stream) {}
+
+Emitter::~Emitter() = default;
+
+const char* Emitter::c_str() const { return m_stream.str(); }
+
+std::size_t Emitter::size() const { return m_stream.pos(); }
+
+// state checking
+bool Emitter::good() const { return m_pState->good(); }
+
+const std::string Emitter::GetLastError() const {
+ return m_pState->GetLastError();
+}
+
+// global setters
+bool Emitter::SetOutputCharset(EMITTER_MANIP value) {
+ return m_pState->SetOutputCharset(value, FmtScope::Global);
+}
+
+bool Emitter::SetStringFormat(EMITTER_MANIP value) {
+ return m_pState->SetStringFormat(value, FmtScope::Global);
+}
+
+bool Emitter::SetBoolFormat(EMITTER_MANIP value) {
+ bool ok = false;
+ if (m_pState->SetBoolFormat(value, FmtScope::Global))
+ ok = true;
+ if (m_pState->SetBoolCaseFormat(value, FmtScope::Global))
+ ok = true;
+ if (m_pState->SetBoolLengthFormat(value, FmtScope::Global))
+ ok = true;
+ return ok;
+}
+
+bool Emitter::SetNullFormat(EMITTER_MANIP value) {
+ return m_pState->SetNullFormat(value, FmtScope::Global);
+}
+
+bool Emitter::SetIntBase(EMITTER_MANIP value) {
+ return m_pState->SetIntFormat(value, FmtScope::Global);
+}
+
+bool Emitter::SetSeqFormat(EMITTER_MANIP value) {
+ return m_pState->SetFlowType(GroupType::Seq, value, FmtScope::Global);
+}
+
+bool Emitter::SetMapFormat(EMITTER_MANIP value) {
+ bool ok = false;
+ if (m_pState->SetFlowType(GroupType::Map, value, FmtScope::Global))
+ ok = true;
+ if (m_pState->SetMapKeyFormat(value, FmtScope::Global))
+ ok = true;
+ return ok;
+}
+
+bool Emitter::SetIndent(std::size_t n) {
+ return m_pState->SetIndent(n, FmtScope::Global);
+}
+
+bool Emitter::SetPreCommentIndent(std::size_t n) {
+ return m_pState->SetPreCommentIndent(n, FmtScope::Global);
+}
+
+bool Emitter::SetPostCommentIndent(std::size_t n) {
+ return m_pState->SetPostCommentIndent(n, FmtScope::Global);
+}
+
+bool Emitter::SetFloatPrecision(std::size_t n) {
+ return m_pState->SetFloatPrecision(n, FmtScope::Global);
+}
+
+bool Emitter::SetDoublePrecision(std::size_t n) {
+ return m_pState->SetDoublePrecision(n, FmtScope::Global);
+}
+
+void Emitter::RestoreGlobalModifiedSettings() {
+ m_pState->RestoreGlobalModifiedSettings();
+}
+
+// SetLocalValue
+// . Either start/end a group, or set a modifier locally
+Emitter& Emitter::SetLocalValue(EMITTER_MANIP value) {
+ if (!good())
+ return *this;
+
+ switch (value) {
+ case BeginDoc:
+ EmitBeginDoc();
+ break;
+ case EndDoc:
+ EmitEndDoc();
+ break;
+ case BeginSeq:
+ EmitBeginSeq();
+ break;
+ case EndSeq:
+ EmitEndSeq();
+ break;
+ case BeginMap:
+ EmitBeginMap();
+ break;
+ case EndMap:
+ EmitEndMap();
+ break;
+ case Key:
+ case Value:
+ // deprecated (these can be deduced by the parity of nodes in a map)
+ break;
+ case TagByKind:
+ EmitKindTag();
+ break;
+ case Newline:
+ EmitNewline();
+ break;
+ default:
+ m_pState->SetLocalValue(value);
+ break;
+ }
+ return *this;
+}
+
+Emitter& Emitter::SetLocalIndent(const _Indent& indent) {
+ m_pState->SetIndent(indent.value, FmtScope::Local);
+ return *this;
+}
+
+Emitter& Emitter::SetLocalPrecision(const _Precision& precision) {
+ if (precision.floatPrecision >= 0)
+ m_pState->SetFloatPrecision(precision.floatPrecision, FmtScope::Local);
+ if (precision.doublePrecision >= 0)
+ m_pState->SetDoublePrecision(precision.doublePrecision, FmtScope::Local);
+ return *this;
+}
+
+// EmitBeginDoc
+void Emitter::EmitBeginDoc() {
+ if (!good())
+ return;
+
+ if (m_pState->CurGroupType() != GroupType::NoType) {
+ m_pState->SetError("Unexpected begin document");
+ return;
+ }
+
+ if (m_pState->HasAnchor() || m_pState->HasTag()) {
+ m_pState->SetError("Unexpected begin document");
+ return;
+ }
+
+ if (m_stream.col() > 0)
+ m_stream << "\n";
+ m_stream << "---\n";
+
+ m_pState->StartedDoc();
+}
+
+// EmitEndDoc
+void Emitter::EmitEndDoc() {
+ if (!good())
+ return;
+
+ if (m_pState->CurGroupType() != GroupType::NoType) {
+ m_pState->SetError("Unexpected begin document");
+ return;
+ }
+
+ if (m_pState->HasAnchor() || m_pState->HasTag()) {
+ m_pState->SetError("Unexpected begin document");
+ return;
+ }
+
+ if (m_stream.col() > 0)
+ m_stream << "\n";
+ m_stream << "...\n";
+}
+
+// EmitBeginSeq
+void Emitter::EmitBeginSeq() {
+ if (!good())
+ return;
+
+ PrepareNode(m_pState->NextGroupType(GroupType::Seq));
+
+ m_pState->StartedGroup(GroupType::Seq);
+}
+
+// EmitEndSeq
+void Emitter::EmitEndSeq() {
+ if (!good())
+ return;
+ FlowType::value originalType = m_pState->CurGroupFlowType();
+
+ if (m_pState->CurGroupChildCount() == 0)
+ m_pState->ForceFlow();
+
+ if (m_pState->CurGroupFlowType() == FlowType::Flow) {
+ if (m_stream.comment())
+ m_stream << "\n";
+ m_stream << IndentTo(m_pState->CurIndent());
+ if (originalType == FlowType::Block) {
+ m_stream << "[";
+ } else {
+ if (m_pState->CurGroupChildCount() == 0 && !m_pState->HasBegunNode())
+ m_stream << "[";
+ }
+ m_stream << "]";
+ }
+
+ m_pState->EndedGroup(GroupType::Seq);
+}
+
+// EmitBeginMap
+void Emitter::EmitBeginMap() {
+ if (!good())
+ return;
+
+ PrepareNode(m_pState->NextGroupType(GroupType::Map));
+
+ m_pState->StartedGroup(GroupType::Map);
+}
+
+// EmitEndMap
+void Emitter::EmitEndMap() {
+ if (!good())
+ return;
+ FlowType::value originalType = m_pState->CurGroupFlowType();
+
+ if (m_pState->CurGroupChildCount() == 0)
+ m_pState->ForceFlow();
+
+ if (m_pState->CurGroupFlowType() == FlowType::Flow) {
+ if (m_stream.comment())
+ m_stream << "\n";
+ m_stream << IndentTo(m_pState->CurIndent());
+ if (originalType == FlowType::Block) {
+ m_stream << "{";
+ } else {
+ if (m_pState->CurGroupChildCount() == 0 && !m_pState->HasBegunNode())
+ m_stream << "{";
+ }
+ m_stream << "}";
+ }
+
+ m_pState->EndedGroup(GroupType::Map);
+}
+
+// EmitNewline
+void Emitter::EmitNewline() {
+ if (!good())
+ return;
+
+ PrepareNode(EmitterNodeType::NoType);
+ m_stream << "\n";
+ m_pState->SetNonContent();
+}
+
+bool Emitter::CanEmitNewline() const { return true; }
+
+// Put the stream in a state so we can simply write the next node
+// E.g., if we're in a sequence, write the "- "
+void Emitter::PrepareNode(EmitterNodeType::value child) {
+ switch (m_pState->CurGroupNodeType()) {
+ case EmitterNodeType::NoType:
+ PrepareTopNode(child);
+ break;
+ case EmitterNodeType::FlowSeq:
+ FlowSeqPrepareNode(child);
+ break;
+ case EmitterNodeType::BlockSeq:
+ BlockSeqPrepareNode(child);
+ break;
+ case EmitterNodeType::FlowMap:
+ FlowMapPrepareNode(child);
+ break;
+ case EmitterNodeType::BlockMap:
+ BlockMapPrepareNode(child);
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ assert(false);
+ break;
+ }
+}
+
+void Emitter::PrepareTopNode(EmitterNodeType::value child) {
+ if (child == EmitterNodeType::NoType)
+ return;
+
+ if (m_pState->CurGroupChildCount() > 0 && m_stream.col() > 0)
+ EmitBeginDoc();
+
+ switch (child) {
+ case EmitterNodeType::NoType:
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ case EmitterNodeType::FlowSeq:
+ case EmitterNodeType::FlowMap:
+ // TODO: if we were writing null, and
+ // we wanted it blank, we wouldn't want a space
+ SpaceOrIndentTo(m_pState->HasBegunContent(), 0);
+ break;
+ case EmitterNodeType::BlockSeq:
+ case EmitterNodeType::BlockMap:
+ if (m_pState->HasBegunNode())
+ m_stream << "\n";
+ break;
+ }
+}
+
+void Emitter::FlowSeqPrepareNode(EmitterNodeType::value child) {
+ const std::size_t lastIndent = m_pState->LastIndent();
+
+ if (!m_pState->HasBegunNode()) {
+ if (m_stream.comment())
+ m_stream << "\n";
+ m_stream << IndentTo(lastIndent);
+ if (m_pState->CurGroupChildCount() == 0)
+ m_stream << "[";
+ else
+ m_stream << ",";
+ }
+
+ switch (child) {
+ case EmitterNodeType::NoType:
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ case EmitterNodeType::FlowSeq:
+ case EmitterNodeType::FlowMap:
+ SpaceOrIndentTo(
+ m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0,
+ lastIndent);
+ break;
+ case EmitterNodeType::BlockSeq:
+ case EmitterNodeType::BlockMap:
+ assert(false);
+ break;
+ }
+}
+
+void Emitter::BlockSeqPrepareNode(EmitterNodeType::value child) {
+ const std::size_t curIndent = m_pState->CurIndent();
+ const std::size_t nextIndent = curIndent + m_pState->CurGroupIndent();
+
+ if (child == EmitterNodeType::NoType)
+ return;
+
+ if (!m_pState->HasBegunContent()) {
+ if (m_pState->CurGroupChildCount() > 0 || m_stream.comment()) {
+ m_stream << "\n";
+ }
+ m_stream << IndentTo(curIndent);
+ m_stream << "-";
+ }
+
+ switch (child) {
+ case EmitterNodeType::NoType:
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ case EmitterNodeType::FlowSeq:
+ case EmitterNodeType::FlowMap:
+ SpaceOrIndentTo(m_pState->HasBegunContent(), nextIndent);
+ break;
+ case EmitterNodeType::BlockSeq:
+ m_stream << "\n";
+ break;
+ case EmitterNodeType::BlockMap:
+ if (m_pState->HasBegunContent() || m_stream.comment())
+ m_stream << "\n";
+ break;
+ }
+}
+
+void Emitter::FlowMapPrepareNode(EmitterNodeType::value child) {
+ if (m_pState->CurGroupChildCount() % 2 == 0) {
+ if (m_pState->GetMapKeyFormat() == LongKey)
+ m_pState->SetLongKey();
+
+ if (m_pState->CurGroupLongKey())
+ FlowMapPrepareLongKey(child);
+ else
+ FlowMapPrepareSimpleKey(child);
+ } else {
+ if (m_pState->CurGroupLongKey())
+ FlowMapPrepareLongKeyValue(child);
+ else
+ FlowMapPrepareSimpleKeyValue(child);
+ }
+}
+
+void Emitter::FlowMapPrepareLongKey(EmitterNodeType::value child) {
+ const std::size_t lastIndent = m_pState->LastIndent();
+
+ if (!m_pState->HasBegunNode()) {
+ if (m_stream.comment())
+ m_stream << "\n";
+ m_stream << IndentTo(lastIndent);
+ if (m_pState->CurGroupChildCount() == 0)
+ m_stream << "{ ?";
+ else
+ m_stream << ", ?";
+ }
+
+ switch (child) {
+ case EmitterNodeType::NoType:
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ case EmitterNodeType::FlowSeq:
+ case EmitterNodeType::FlowMap:
+ SpaceOrIndentTo(
+ m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0,
+ lastIndent);
+ break;
+ case EmitterNodeType::BlockSeq:
+ case EmitterNodeType::BlockMap:
+ assert(false);
+ break;
+ }
+}
+
+void Emitter::FlowMapPrepareLongKeyValue(EmitterNodeType::value child) {
+ const std::size_t lastIndent = m_pState->LastIndent();
+
+ if (!m_pState->HasBegunNode()) {
+ if (m_stream.comment())
+ m_stream << "\n";
+ m_stream << IndentTo(lastIndent);
+ m_stream << ":";
+ }
+
+ switch (child) {
+ case EmitterNodeType::NoType:
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ case EmitterNodeType::FlowSeq:
+ case EmitterNodeType::FlowMap:
+ SpaceOrIndentTo(
+ m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0,
+ lastIndent);
+ break;
+ case EmitterNodeType::BlockSeq:
+ case EmitterNodeType::BlockMap:
+ assert(false);
+ break;
+ }
+}
+
+void Emitter::FlowMapPrepareSimpleKey(EmitterNodeType::value child) {
+ const std::size_t lastIndent = m_pState->LastIndent();
+
+ if (!m_pState->HasBegunNode()) {
+ if (m_stream.comment())
+ m_stream << "\n";
+ m_stream << IndentTo(lastIndent);
+ if (m_pState->CurGroupChildCount() == 0)
+ m_stream << "{";
+ else
+ m_stream << ",";
+ }
+
+ switch (child) {
+ case EmitterNodeType::NoType:
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ case EmitterNodeType::FlowSeq:
+ case EmitterNodeType::FlowMap:
+ SpaceOrIndentTo(
+ m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0,
+ lastIndent);
+ break;
+ case EmitterNodeType::BlockSeq:
+ case EmitterNodeType::BlockMap:
+ assert(false);
+ break;
+ }
+}
+
+void Emitter::FlowMapPrepareSimpleKeyValue(EmitterNodeType::value child) {
+ const std::size_t lastIndent = m_pState->LastIndent();
+
+ if (!m_pState->HasBegunNode()) {
+ if (m_stream.comment())
+ m_stream << "\n";
+ m_stream << IndentTo(lastIndent);
+ if (m_pState->HasAlias()) {
+ m_stream << " ";
+ }
+ m_stream << ":";
+ }
+
+ switch (child) {
+ case EmitterNodeType::NoType:
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ case EmitterNodeType::FlowSeq:
+ case EmitterNodeType::FlowMap:
+ SpaceOrIndentTo(
+ m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0,
+ lastIndent);
+ break;
+ case EmitterNodeType::BlockSeq:
+ case EmitterNodeType::BlockMap:
+ assert(false);
+ break;
+ }
+}
+
+void Emitter::BlockMapPrepareNode(EmitterNodeType::value child) {
+ if (m_pState->CurGroupChildCount() % 2 == 0) {
+ if (m_pState->GetMapKeyFormat() == LongKey)
+ m_pState->SetLongKey();
+ if (child == EmitterNodeType::BlockSeq ||
+ child == EmitterNodeType::BlockMap ||
+ child == EmitterNodeType::Property)
+ m_pState->SetLongKey();
+
+ if (m_pState->CurGroupLongKey())
+ BlockMapPrepareLongKey(child);
+ else
+ BlockMapPrepareSimpleKey(child);
+ } else {
+ if (m_pState->CurGroupLongKey())
+ BlockMapPrepareLongKeyValue(child);
+ else
+ BlockMapPrepareSimpleKeyValue(child);
+ }
+}
+
+void Emitter::BlockMapPrepareLongKey(EmitterNodeType::value child) {
+ const std::size_t curIndent = m_pState->CurIndent();
+ const std::size_t childCount = m_pState->CurGroupChildCount();
+
+ if (child == EmitterNodeType::NoType)
+ return;
+
+ if (!m_pState->HasBegunContent()) {
+ if (childCount > 0) {
+ m_stream << "\n";
+ }
+ if (m_stream.comment()) {
+ m_stream << "\n";
+ }
+ m_stream << IndentTo(curIndent);
+ m_stream << "?";
+ }
+
+ switch (child) {
+ case EmitterNodeType::NoType:
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ case EmitterNodeType::FlowSeq:
+ case EmitterNodeType::FlowMap:
+ SpaceOrIndentTo(true, curIndent + 1);
+ break;
+ case EmitterNodeType::BlockSeq:
+ case EmitterNodeType::BlockMap:
+ if (m_pState->HasBegunContent())
+ m_stream << "\n";
+ break;
+ }
+}
+
+void Emitter::BlockMapPrepareLongKeyValue(EmitterNodeType::value child) {
+ const std::size_t curIndent = m_pState->CurIndent();
+
+ if (child == EmitterNodeType::NoType)
+ return;
+
+ if (!m_pState->HasBegunContent()) {
+ m_stream << "\n";
+ m_stream << IndentTo(curIndent);
+ m_stream << ":";
+ }
+
+ switch (child) {
+ case EmitterNodeType::NoType:
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ case EmitterNodeType::FlowSeq:
+ case EmitterNodeType::FlowMap:
+ SpaceOrIndentTo(true, curIndent + 1);
+ break;
+ case EmitterNodeType::BlockSeq:
+ case EmitterNodeType::BlockMap:
+ if (m_pState->HasBegunContent())
+ m_stream << "\n";
+ SpaceOrIndentTo(true, curIndent + 1);
+ break;
+ }
+}
+
+void Emitter::BlockMapPrepareSimpleKey(EmitterNodeType::value child) {
+ const std::size_t curIndent = m_pState->CurIndent();
+ const std::size_t childCount = m_pState->CurGroupChildCount();
+
+ if (child == EmitterNodeType::NoType)
+ return;
+
+ if (!m_pState->HasBegunNode()) {
+ if (childCount > 0) {
+ m_stream << "\n";
+ }
+ }
+
+ switch (child) {
+ case EmitterNodeType::NoType:
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ case EmitterNodeType::FlowSeq:
+ case EmitterNodeType::FlowMap:
+ SpaceOrIndentTo(m_pState->HasBegunContent(), curIndent);
+ break;
+ case EmitterNodeType::BlockSeq:
+ case EmitterNodeType::BlockMap:
+ break;
+ }
+}
+
+void Emitter::BlockMapPrepareSimpleKeyValue(EmitterNodeType::value child) {
+ const std::size_t curIndent = m_pState->CurIndent();
+ const std::size_t nextIndent = curIndent + m_pState->CurGroupIndent();
+
+ if (!m_pState->HasBegunNode()) {
+ if (m_pState->HasAlias()) {
+ m_stream << " ";
+ }
+ m_stream << ":";
+ }
+
+ switch (child) {
+ case EmitterNodeType::NoType:
+ break;
+ case EmitterNodeType::Property:
+ case EmitterNodeType::Scalar:
+ case EmitterNodeType::FlowSeq:
+ case EmitterNodeType::FlowMap:
+ SpaceOrIndentTo(true, nextIndent);
+ break;
+ case EmitterNodeType::BlockSeq:
+ case EmitterNodeType::BlockMap:
+ m_stream << "\n";
+ break;
+ }
+}
+
+// SpaceOrIndentTo
+// . Prepares for some more content by proper spacing
+void Emitter::SpaceOrIndentTo(bool requireSpace, std::size_t indent) {
+ if (m_stream.comment())
+ m_stream << "\n";
+ if (m_stream.col() > 0 && requireSpace)
+ m_stream << " ";
+ m_stream << IndentTo(indent);
+}
+
+void Emitter::PrepareIntegralStream(std::stringstream& stream) const {
+
+ switch (m_pState->GetIntFormat()) {
+ case Dec:
+ stream << std::dec;
+ break;
+ case Hex:
+ stream << "0x";
+ stream << std::hex;
+ break;
+ case Oct:
+ stream << "0";
+ stream << std::oct;
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void Emitter::StartedScalar() { m_pState->StartedScalar(); }
+
+// *******************************************************************************************
+// overloads of Write
+
+StringEscaping::value GetStringEscapingStyle(const EMITTER_MANIP emitterManip) {
+ switch (emitterManip) {
+ case EscapeNonAscii:
+ return StringEscaping::NonAscii;
+ case EscapeAsJson:
+ return StringEscaping::JSON;
+ default:
+ return StringEscaping::None;
+ break;
+ }
+}
+
+Emitter& Emitter::Write(const std::string& str) {
+ if (!good())
+ return *this;
+
+ StringEscaping::value stringEscaping = GetStringEscapingStyle(m_pState->GetOutputCharset());
+
+ const StringFormat::value strFormat =
+ Utils::ComputeStringFormat(str, m_pState->GetStringFormat(),
+ m_pState->CurGroupFlowType(), stringEscaping == StringEscaping::NonAscii);
+
+ if (strFormat == StringFormat::Literal || str.size() > 1024)
+ m_pState->SetMapKeyFormat(YAML::LongKey, FmtScope::Local);
+
+ PrepareNode(EmitterNodeType::Scalar);
+
+ switch (strFormat) {
+ case StringFormat::Plain:
+ m_stream << str;
+ break;
+ case StringFormat::SingleQuoted:
+ Utils::WriteSingleQuotedString(m_stream, str);
+ break;
+ case StringFormat::DoubleQuoted:
+ Utils::WriteDoubleQuotedString(m_stream, str, stringEscaping);
+ break;
+ case StringFormat::Literal:
+ Utils::WriteLiteralString(m_stream, str,
+ m_pState->CurIndent() + m_pState->GetIndent());
+ break;
+ }
+
+ StartedScalar();
+
+ return *this;
+}
+
+std::size_t Emitter::GetFloatPrecision() const {
+ return m_pState->GetFloatPrecision();
+}
+
+std::size_t Emitter::GetDoublePrecision() const {
+ return m_pState->GetDoublePrecision();
+}
+
+const char* Emitter::ComputeFullBoolName(bool b) const {
+ const EMITTER_MANIP mainFmt = (m_pState->GetBoolLengthFormat() == ShortBool
+ ? YesNoBool
+ : m_pState->GetBoolFormat());
+ const EMITTER_MANIP caseFmt = m_pState->GetBoolCaseFormat();
+ switch (mainFmt) {
+ case YesNoBool:
+ switch (caseFmt) {
+ case UpperCase:
+ return b ? "YES" : "NO";
+ case CamelCase:
+ return b ? "Yes" : "No";
+ case LowerCase:
+ return b ? "yes" : "no";
+ default:
+ break;
+ }
+ break;
+ case OnOffBool:
+ switch (caseFmt) {
+ case UpperCase:
+ return b ? "ON" : "OFF";
+ case CamelCase:
+ return b ? "On" : "Off";
+ case LowerCase:
+ return b ? "on" : "off";
+ default:
+ break;
+ }
+ break;
+ case TrueFalseBool:
+ switch (caseFmt) {
+ case UpperCase:
+ return b ? "TRUE" : "FALSE";
+ case CamelCase:
+ return b ? "True" : "False";
+ case LowerCase:
+ return b ? "true" : "false";
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return b ? "y" : "n"; // should never get here, but it can't hurt to give
+ // these answers
+}
+
+const char* Emitter::ComputeNullName() const {
+ switch (m_pState->GetNullFormat()) {
+ case LowerNull:
+ return "null";
+ case UpperNull:
+ return "NULL";
+ case CamelNull:
+ return "Null";
+ case TildeNull:
+ // fallthrough
+ default:
+ return "~";
+ }
+}
+
+Emitter& Emitter::Write(bool b) {
+ if (!good())
+ return *this;
+
+ PrepareNode(EmitterNodeType::Scalar);
+
+ const char* name = ComputeFullBoolName(b);
+ if (m_pState->GetBoolLengthFormat() == ShortBool)
+ m_stream << name[0];
+ else
+ m_stream << name;
+
+ StartedScalar();
+
+ return *this;
+}
+
+Emitter& Emitter::Write(char ch) {
+ if (!good())
+ return *this;
+
+
+
+ PrepareNode(EmitterNodeType::Scalar);
+ Utils::WriteChar(m_stream, ch, GetStringEscapingStyle(m_pState->GetOutputCharset()));
+ StartedScalar();
+
+ return *this;
+}
+
+Emitter& Emitter::Write(const _Alias& alias) {
+ if (!good())
+ return *this;
+
+ if (m_pState->HasAnchor() || m_pState->HasTag()) {
+ m_pState->SetError(ErrorMsg::INVALID_ALIAS);
+ return *this;
+ }
+
+ PrepareNode(EmitterNodeType::Scalar);
+
+ if (!Utils::WriteAlias(m_stream, alias.content)) {
+ m_pState->SetError(ErrorMsg::INVALID_ALIAS);
+ return *this;
+ }
+
+ StartedScalar();
+
+ m_pState->SetAlias();
+
+ return *this;
+}
+
+Emitter& Emitter::Write(const _Anchor& anchor) {
+ if (!good())
+ return *this;
+
+ if (m_pState->HasAnchor()) {
+ m_pState->SetError(ErrorMsg::INVALID_ANCHOR);
+ return *this;
+ }
+
+ PrepareNode(EmitterNodeType::Property);
+
+ if (!Utils::WriteAnchor(m_stream, anchor.content)) {
+ m_pState->SetError(ErrorMsg::INVALID_ANCHOR);
+ return *this;
+ }
+
+ m_pState->SetAnchor();
+
+ return *this;
+}
+
+Emitter& Emitter::Write(const _Tag& tag) {
+ if (!good())
+ return *this;
+
+ if (m_pState->HasTag()) {
+ m_pState->SetError(ErrorMsg::INVALID_TAG);
+ return *this;
+ }
+
+ PrepareNode(EmitterNodeType::Property);
+
+ bool success = false;
+ if (tag.type == _Tag::Type::Verbatim)
+ success = Utils::WriteTag(m_stream, tag.content, true);
+ else if (tag.type == _Tag::Type::PrimaryHandle)
+ success = Utils::WriteTag(m_stream, tag.content, false);
+ else
+ success = Utils::WriteTagWithPrefix(m_stream, tag.prefix, tag.content);
+
+ if (!success) {
+ m_pState->SetError(ErrorMsg::INVALID_TAG);
+ return *this;
+ }
+
+ m_pState->SetTag();
+
+ return *this;
+}
+
+void Emitter::EmitKindTag() { Write(LocalTag("")); }
+
+Emitter& Emitter::Write(const _Comment& comment) {
+ if (!good())
+ return *this;
+
+ PrepareNode(EmitterNodeType::NoType);
+
+ if (m_stream.col() > 0)
+ m_stream << Indentation(m_pState->GetPreCommentIndent());
+ Utils::WriteComment(m_stream, comment.content,
+ m_pState->GetPostCommentIndent());
+
+ m_pState->SetNonContent();
+
+ return *this;
+}
+
+Emitter& Emitter::Write(const _Null& /*null*/) {
+ if (!good())
+ return *this;
+
+ PrepareNode(EmitterNodeType::Scalar);
+
+ m_stream << ComputeNullName();
+
+ StartedScalar();
+
+ return *this;
+}
+
+Emitter& Emitter::Write(const Binary& binary) {
+ Write(SecondaryTag("binary"));
+
+ if (!good())
+ return *this;
+
+ PrepareNode(EmitterNodeType::Scalar);
+ Utils::WriteBinary(m_stream, binary);
+ StartedScalar();
+
+ return *this;
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/emitterstate.cpp b/src/libs/3rdparty/yaml-cpp/src/emitterstate.cpp
new file mode 100644
index 0000000000..3dbe401108
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/emitterstate.cpp
@@ -0,0 +1,400 @@
+#include <limits>
+
+#include "emitterstate.h"
+#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
+
+namespace YAML {
+EmitterState::EmitterState()
+ : m_isGood(true),
+ m_lastError{},
+ // default global manipulators
+ m_charset(EmitNonAscii),
+ m_strFmt(Auto),
+ m_boolFmt(TrueFalseBool),
+ m_boolLengthFmt(LongBool),
+ m_boolCaseFmt(LowerCase),
+ m_nullFmt(TildeNull),
+ m_intFmt(Dec),
+ m_indent(2),
+ m_preCommentIndent(2),
+ m_postCommentIndent(1),
+ m_seqFmt(Block),
+ m_mapFmt(Block),
+ m_mapKeyFmt(Auto),
+ m_floatPrecision(std::numeric_limits<float>::max_digits10),
+ m_doublePrecision(std::numeric_limits<double>::max_digits10),
+ //
+ m_modifiedSettings{},
+ m_globalModifiedSettings{},
+ m_groups{},
+ m_curIndent(0),
+ m_hasAnchor(false),
+ m_hasAlias(false),
+ m_hasTag(false),
+ m_hasNonContent(false),
+ m_docCount(0) {}
+
+EmitterState::~EmitterState() = default;
+
+// SetLocalValue
+// . We blindly tries to set all possible formatters to this value
+// . Only the ones that make sense will be accepted
+void EmitterState::SetLocalValue(EMITTER_MANIP value) {
+ SetOutputCharset(value, FmtScope::Local);
+ SetStringFormat(value, FmtScope::Local);
+ SetBoolFormat(value, FmtScope::Local);
+ SetBoolCaseFormat(value, FmtScope::Local);
+ SetBoolLengthFormat(value, FmtScope::Local);
+ SetNullFormat(value, FmtScope::Local);
+ SetIntFormat(value, FmtScope::Local);
+ SetFlowType(GroupType::Seq, value, FmtScope::Local);
+ SetFlowType(GroupType::Map, value, FmtScope::Local);
+ SetMapKeyFormat(value, FmtScope::Local);
+}
+
+void EmitterState::SetAnchor() { m_hasAnchor = true; }
+
+void EmitterState::SetAlias() { m_hasAlias = true; }
+
+void EmitterState::SetTag() { m_hasTag = true; }
+
+void EmitterState::SetNonContent() { m_hasNonContent = true; }
+
+void EmitterState::SetLongKey() {
+ assert(!m_groups.empty());
+ if (m_groups.empty()) {
+ return;
+ }
+
+ assert(m_groups.back()->type == GroupType::Map);
+ m_groups.back()->longKey = true;
+}
+
+void EmitterState::ForceFlow() {
+ assert(!m_groups.empty());
+ if (m_groups.empty()) {
+ return;
+ }
+
+ m_groups.back()->flowType = FlowType::Flow;
+}
+
+void EmitterState::StartedNode() {
+ if (m_groups.empty()) {
+ m_docCount++;
+ } else {
+ m_groups.back()->childCount++;
+ if (m_groups.back()->childCount % 2 == 0) {
+ m_groups.back()->longKey = false;
+ }
+ }
+
+ m_hasAnchor = false;
+ m_hasAlias = false;
+ m_hasTag = false;
+ m_hasNonContent = false;
+}
+
+EmitterNodeType::value EmitterState::NextGroupType(
+ GroupType::value type) const {
+ if (type == GroupType::Seq) {
+ if (GetFlowType(type) == Block)
+ return EmitterNodeType::BlockSeq;
+ return EmitterNodeType::FlowSeq;
+ }
+
+ if (GetFlowType(type) == Block)
+ return EmitterNodeType::BlockMap;
+ return EmitterNodeType::FlowMap;
+
+ // can't happen
+ assert(false);
+ return EmitterNodeType::NoType;
+}
+
+void EmitterState::StartedDoc() {
+ m_hasAnchor = false;
+ m_hasTag = false;
+ m_hasNonContent = false;
+}
+
+void EmitterState::EndedDoc() {
+ m_hasAnchor = false;
+ m_hasTag = false;
+ m_hasNonContent = false;
+}
+
+void EmitterState::StartedScalar() {
+ StartedNode();
+ ClearModifiedSettings();
+}
+
+void EmitterState::StartedGroup(GroupType::value type) {
+ StartedNode();
+
+ const std::size_t lastGroupIndent =
+ (m_groups.empty() ? 0 : m_groups.back()->indent);
+ m_curIndent += lastGroupIndent;
+
+ // TODO: Create move constructors for settings types to simplify transfer
+ std::unique_ptr<Group> pGroup(new Group(type));
+
+ // transfer settings (which last until this group is done)
+ //
+ // NB: if pGroup->modifiedSettings == m_modifiedSettings,
+ // m_modifiedSettings is not changed!
+ pGroup->modifiedSettings = std::move(m_modifiedSettings);
+
+ // set up group
+ if (GetFlowType(type) == Block) {
+ pGroup->flowType = FlowType::Block;
+ } else {
+ pGroup->flowType = FlowType::Flow;
+ }
+ pGroup->indent = GetIndent();
+
+ m_groups.push_back(std::move(pGroup));
+}
+
+void EmitterState::EndedGroup(GroupType::value type) {
+ if (m_groups.empty()) {
+ if (type == GroupType::Seq) {
+ return SetError(ErrorMsg::UNEXPECTED_END_SEQ);
+ }
+ return SetError(ErrorMsg::UNEXPECTED_END_MAP);
+ }
+
+ if (m_hasTag) {
+ SetError(ErrorMsg::INVALID_TAG);
+ }
+ if (m_hasAnchor) {
+ SetError(ErrorMsg::INVALID_ANCHOR);
+ }
+
+ // get rid of the current group
+ {
+ std::unique_ptr<Group> pFinishedGroup = std::move(m_groups.back());
+ m_groups.pop_back();
+ if (pFinishedGroup->type != type) {
+ return SetError(ErrorMsg::UNMATCHED_GROUP_TAG);
+ }
+ }
+
+ // reset old settings
+ std::size_t lastIndent = (m_groups.empty() ? 0 : m_groups.back()->indent);
+ assert(m_curIndent >= lastIndent);
+ m_curIndent -= lastIndent;
+
+ // some global settings that we changed may have been overridden
+ // by a local setting we just popped, so we need to restore them
+ m_globalModifiedSettings.restore();
+
+ ClearModifiedSettings();
+ m_hasAnchor = false;
+ m_hasTag = false;
+ m_hasNonContent = false;
+}
+
+EmitterNodeType::value EmitterState::CurGroupNodeType() const {
+ if (m_groups.empty()) {
+ return EmitterNodeType::NoType;
+ }
+
+ return m_groups.back()->NodeType();
+}
+
+GroupType::value EmitterState::CurGroupType() const {
+ return m_groups.empty() ? GroupType::NoType : m_groups.back()->type;
+}
+
+FlowType::value EmitterState::CurGroupFlowType() const {
+ return m_groups.empty() ? FlowType::NoType : m_groups.back()->flowType;
+}
+
+std::size_t EmitterState::CurGroupIndent() const {
+ return m_groups.empty() ? 0 : m_groups.back()->indent;
+}
+
+std::size_t EmitterState::CurGroupChildCount() const {
+ return m_groups.empty() ? m_docCount : m_groups.back()->childCount;
+}
+
+bool EmitterState::CurGroupLongKey() const {
+ return m_groups.empty() ? false : m_groups.back()->longKey;
+}
+
+std::size_t EmitterState::LastIndent() const {
+ if (m_groups.size() <= 1) {
+ return 0;
+ }
+
+ return m_curIndent - m_groups[m_groups.size() - 2]->indent;
+}
+
+void EmitterState::ClearModifiedSettings() { m_modifiedSettings.clear(); }
+
+void EmitterState::RestoreGlobalModifiedSettings() {
+ m_globalModifiedSettings.restore();
+}
+
+bool EmitterState::SetOutputCharset(EMITTER_MANIP value,
+ FmtScope::value scope) {
+ switch (value) {
+ case EmitNonAscii:
+ case EscapeNonAscii:
+ case EscapeAsJson:
+ _Set(m_charset, value, scope);
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool EmitterState::SetStringFormat(EMITTER_MANIP value, FmtScope::value scope) {
+ switch (value) {
+ case Auto:
+ case SingleQuoted:
+ case DoubleQuoted:
+ case Literal:
+ _Set(m_strFmt, value, scope);
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool EmitterState::SetBoolFormat(EMITTER_MANIP value, FmtScope::value scope) {
+ switch (value) {
+ case OnOffBool:
+ case TrueFalseBool:
+ case YesNoBool:
+ _Set(m_boolFmt, value, scope);
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool EmitterState::SetBoolLengthFormat(EMITTER_MANIP value,
+ FmtScope::value scope) {
+ switch (value) {
+ case LongBool:
+ case ShortBool:
+ _Set(m_boolLengthFmt, value, scope);
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool EmitterState::SetBoolCaseFormat(EMITTER_MANIP value,
+ FmtScope::value scope) {
+ switch (value) {
+ case UpperCase:
+ case LowerCase:
+ case CamelCase:
+ _Set(m_boolCaseFmt, value, scope);
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool EmitterState::SetNullFormat(EMITTER_MANIP value, FmtScope::value scope) {
+ switch (value) {
+ case LowerNull:
+ case UpperNull:
+ case CamelNull:
+ case TildeNull:
+ _Set(m_nullFmt, value, scope);
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool EmitterState::SetIntFormat(EMITTER_MANIP value, FmtScope::value scope) {
+ switch (value) {
+ case Dec:
+ case Hex:
+ case Oct:
+ _Set(m_intFmt, value, scope);
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool EmitterState::SetIndent(std::size_t value, FmtScope::value scope) {
+ if (value <= 1)
+ return false;
+
+ _Set(m_indent, value, scope);
+ return true;
+}
+
+bool EmitterState::SetPreCommentIndent(std::size_t value,
+ FmtScope::value scope) {
+ if (value == 0)
+ return false;
+
+ _Set(m_preCommentIndent, value, scope);
+ return true;
+}
+
+bool EmitterState::SetPostCommentIndent(std::size_t value,
+ FmtScope::value scope) {
+ if (value == 0)
+ return false;
+
+ _Set(m_postCommentIndent, value, scope);
+ return true;
+}
+
+bool EmitterState::SetFlowType(GroupType::value groupType, EMITTER_MANIP value,
+ FmtScope::value scope) {
+ switch (value) {
+ case Block:
+ case Flow:
+ _Set(groupType == GroupType::Seq ? m_seqFmt : m_mapFmt, value, scope);
+ return true;
+ default:
+ return false;
+ }
+}
+
+EMITTER_MANIP EmitterState::GetFlowType(GroupType::value groupType) const {
+ // force flow style if we're currently in a flow
+ if (CurGroupFlowType() == FlowType::Flow)
+ return Flow;
+
+ // otherwise, go with what's asked of us
+ return (groupType == GroupType::Seq ? m_seqFmt.get() : m_mapFmt.get());
+}
+
+bool EmitterState::SetMapKeyFormat(EMITTER_MANIP value, FmtScope::value scope) {
+ switch (value) {
+ case Auto:
+ case LongKey:
+ _Set(m_mapKeyFmt, value, scope);
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool EmitterState::SetFloatPrecision(std::size_t value, FmtScope::value scope) {
+ if (value > std::numeric_limits<float>::max_digits10)
+ return false;
+ _Set(m_floatPrecision, value, scope);
+ return true;
+}
+
+bool EmitterState::SetDoublePrecision(std::size_t value,
+ FmtScope::value scope) {
+ if (value > std::numeric_limits<double>::max_digits10)
+ return false;
+ _Set(m_doublePrecision, value, scope);
+ return true;
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/emitterstate.h b/src/libs/3rdparty/yaml-cpp/src/emitterstate.h
new file mode 100644
index 0000000000..8f379ca952
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/emitterstate.h
@@ -0,0 +1,216 @@
+#ifndef EMITTERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define EMITTERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "setting.h"
+#include "yaml-cpp/emitterdef.h"
+#include "yaml-cpp/emittermanip.h"
+
+#include <cassert>
+#include <memory>
+#include <stack>
+#include <stdexcept>
+#include <vector>
+
+namespace YAML {
+struct FmtScope {
+ enum value { Local, Global };
+};
+struct GroupType {
+ enum value { NoType, Seq, Map };
+};
+struct FlowType {
+ enum value { NoType, Flow, Block };
+};
+
+class EmitterState {
+ public:
+ EmitterState();
+ ~EmitterState();
+
+ // basic state checking
+ bool good() const { return m_isGood; }
+ const std::string GetLastError() const { return m_lastError; }
+ void SetError(const std::string& error) {
+ m_isGood = false;
+ m_lastError = error;
+ }
+
+ // node handling
+ void SetAnchor();
+ void SetAlias();
+ void SetTag();
+ void SetNonContent();
+ void SetLongKey();
+ void ForceFlow();
+ void StartedDoc();
+ void EndedDoc();
+ void StartedScalar();
+ void StartedGroup(GroupType::value type);
+ void EndedGroup(GroupType::value type);
+
+ EmitterNodeType::value NextGroupType(GroupType::value type) const;
+ EmitterNodeType::value CurGroupNodeType() const;
+
+ GroupType::value CurGroupType() const;
+ FlowType::value CurGroupFlowType() const;
+ std::size_t CurGroupIndent() const;
+ std::size_t CurGroupChildCount() const;
+ bool CurGroupLongKey() const;
+
+ std::size_t LastIndent() const;
+ std::size_t CurIndent() const { return m_curIndent; }
+ bool HasAnchor() const { return m_hasAnchor; }
+ bool HasAlias() const { return m_hasAlias; }
+ bool HasTag() const { return m_hasTag; }
+ bool HasBegunNode() const {
+ return m_hasAnchor || m_hasTag || m_hasNonContent;
+ }
+ bool HasBegunContent() const { return m_hasAnchor || m_hasTag; }
+
+ void ClearModifiedSettings();
+ void RestoreGlobalModifiedSettings();
+
+ // formatters
+ void SetLocalValue(EMITTER_MANIP value);
+
+ bool SetOutputCharset(EMITTER_MANIP value, FmtScope::value scope);
+ EMITTER_MANIP GetOutputCharset() const { return m_charset.get(); }
+
+ bool SetStringFormat(EMITTER_MANIP value, FmtScope::value scope);
+ EMITTER_MANIP GetStringFormat() const { return m_strFmt.get(); }
+
+ bool SetBoolFormat(EMITTER_MANIP value, FmtScope::value scope);
+ EMITTER_MANIP GetBoolFormat() const { return m_boolFmt.get(); }
+
+ bool SetBoolLengthFormat(EMITTER_MANIP value, FmtScope::value scope);
+ EMITTER_MANIP GetBoolLengthFormat() const { return m_boolLengthFmt.get(); }
+
+ bool SetBoolCaseFormat(EMITTER_MANIP value, FmtScope::value scope);
+ EMITTER_MANIP GetBoolCaseFormat() const { return m_boolCaseFmt.get(); }
+
+ bool SetNullFormat(EMITTER_MANIP value, FmtScope::value scope);
+ EMITTER_MANIP GetNullFormat() const { return m_nullFmt.get(); }
+
+ bool SetIntFormat(EMITTER_MANIP value, FmtScope::value scope);
+ EMITTER_MANIP GetIntFormat() const { return m_intFmt.get(); }
+
+ bool SetIndent(std::size_t value, FmtScope::value scope);
+ std::size_t GetIndent() const { return m_indent.get(); }
+
+ bool SetPreCommentIndent(std::size_t value, FmtScope::value scope);
+ std::size_t GetPreCommentIndent() const { return m_preCommentIndent.get(); }
+ bool SetPostCommentIndent(std::size_t value, FmtScope::value scope);
+ std::size_t GetPostCommentIndent() const { return m_postCommentIndent.get(); }
+
+ bool SetFlowType(GroupType::value groupType, EMITTER_MANIP value,
+ FmtScope::value scope);
+ EMITTER_MANIP GetFlowType(GroupType::value groupType) const;
+
+ bool SetMapKeyFormat(EMITTER_MANIP value, FmtScope::value scope);
+ EMITTER_MANIP GetMapKeyFormat() const { return m_mapKeyFmt.get(); }
+
+ bool SetFloatPrecision(std::size_t value, FmtScope::value scope);
+ std::size_t GetFloatPrecision() const { return m_floatPrecision.get(); }
+ bool SetDoublePrecision(std::size_t value, FmtScope::value scope);
+ std::size_t GetDoublePrecision() const { return m_doublePrecision.get(); }
+
+ private:
+ template <typename T>
+ void _Set(Setting<T>& fmt, T value, FmtScope::value scope);
+
+ void StartedNode();
+
+ private:
+ // basic state ok?
+ bool m_isGood;
+ std::string m_lastError;
+
+ // other state
+ Setting<EMITTER_MANIP> m_charset;
+ Setting<EMITTER_MANIP> m_strFmt;
+ Setting<EMITTER_MANIP> m_boolFmt;
+ Setting<EMITTER_MANIP> m_boolLengthFmt;
+ Setting<EMITTER_MANIP> m_boolCaseFmt;
+ Setting<EMITTER_MANIP> m_nullFmt;
+ Setting<EMITTER_MANIP> m_intFmt;
+ Setting<std::size_t> m_indent;
+ Setting<std::size_t> m_preCommentIndent, m_postCommentIndent;
+ Setting<EMITTER_MANIP> m_seqFmt;
+ Setting<EMITTER_MANIP> m_mapFmt;
+ Setting<EMITTER_MANIP> m_mapKeyFmt;
+ Setting<std::size_t> m_floatPrecision;
+ Setting<std::size_t> m_doublePrecision;
+
+ SettingChanges m_modifiedSettings;
+ SettingChanges m_globalModifiedSettings;
+
+ struct Group {
+ explicit Group(GroupType::value type_)
+ : type(type_),
+ flowType{},
+ indent(0),
+ childCount(0),
+ longKey(false),
+ modifiedSettings{} {}
+
+ GroupType::value type;
+ FlowType::value flowType;
+ std::size_t indent;
+ std::size_t childCount;
+ bool longKey;
+
+ SettingChanges modifiedSettings;
+
+ EmitterNodeType::value NodeType() const {
+ if (type == GroupType::Seq) {
+ if (flowType == FlowType::Flow)
+ return EmitterNodeType::FlowSeq;
+ else
+ return EmitterNodeType::BlockSeq;
+ } else {
+ if (flowType == FlowType::Flow)
+ return EmitterNodeType::FlowMap;
+ else
+ return EmitterNodeType::BlockMap;
+ }
+
+ // can't get here
+ assert(false);
+ return EmitterNodeType::NoType;
+ }
+ };
+
+ std::vector<std::unique_ptr<Group>> m_groups;
+ std::size_t m_curIndent;
+ bool m_hasAnchor;
+ bool m_hasAlias;
+ bool m_hasTag;
+ bool m_hasNonContent;
+ std::size_t m_docCount;
+};
+
+template <typename T>
+void EmitterState::_Set(Setting<T>& fmt, T value, FmtScope::value scope) {
+ switch (scope) {
+ case FmtScope::Local:
+ m_modifiedSettings.push(fmt.set(value));
+ break;
+ case FmtScope::Global:
+ fmt.set(value);
+ m_globalModifiedSettings.push(
+ fmt.set(value)); // this pushes an identity set, so when we restore,
+ // it restores to the value here, and not the previous one
+ break;
+ default:
+ assert(false);
+ }
+}
+} // namespace YAML
+
+#endif // EMITTERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/emitterutils.cpp b/src/libs/3rdparty/yaml-cpp/src/emitterutils.cpp
new file mode 100644
index 0000000000..6cdf6de7e2
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/emitterutils.cpp
@@ -0,0 +1,497 @@
+#include <algorithm>
+#include <iomanip>
+#include <sstream>
+
+#include "emitterutils.h"
+#include "exp.h"
+#include "indentation.h"
+#include "regex_yaml.h"
+#include "regeximpl.h"
+#include "stringsource.h"
+#include "yaml-cpp/binary.h" // IWYU pragma: keep
+#include "yaml-cpp/null.h"
+#include "yaml-cpp/ostream_wrapper.h"
+
+namespace YAML {
+namespace Utils {
+namespace {
+enum { REPLACEMENT_CHARACTER = 0xFFFD };
+
+bool IsAnchorChar(int ch) { // test for ns-anchor-char
+ switch (ch) {
+ case ',':
+ case '[':
+ case ']':
+ case '{':
+ case '}': // c-flow-indicator
+ case ' ':
+ case '\t': // s-white
+ case 0xFEFF: // c-byte-order-mark
+ case 0xA:
+ case 0xD: // b-char
+ return false;
+ case 0x85:
+ return true;
+ }
+
+ if (ch < 0x20) {
+ return false;
+ }
+
+ if (ch < 0x7E) {
+ return true;
+ }
+
+ if (ch < 0xA0) {
+ return false;
+ }
+ if (ch >= 0xD800 && ch <= 0xDFFF) {
+ return false;
+ }
+ if ((ch & 0xFFFE) == 0xFFFE) {
+ return false;
+ }
+ if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) {
+ return false;
+ }
+ if (ch > 0x10FFFF) {
+ return false;
+ }
+
+ return true;
+}
+
+int Utf8BytesIndicated(char ch) {
+ int byteVal = static_cast<unsigned char>(ch);
+ switch (byteVal >> 4) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ return 1;
+ case 12:
+ case 13:
+ return 2;
+ case 14:
+ return 3;
+ case 15:
+ return 4;
+ default:
+ return -1;
+ }
+}
+
+bool IsTrailingByte(char ch) { return (ch & 0xC0) == 0x80; }
+
+bool GetNextCodePointAndAdvance(int& codePoint,
+ std::string::const_iterator& first,
+ std::string::const_iterator last) {
+ if (first == last)
+ return false;
+
+ int nBytes = Utf8BytesIndicated(*first);
+ if (nBytes < 1) {
+ // Bad lead byte
+ ++first;
+ codePoint = REPLACEMENT_CHARACTER;
+ return true;
+ }
+
+ if (nBytes == 1) {
+ codePoint = *first++;
+ return true;
+ }
+
+ // Gather bits from trailing bytes
+ codePoint = static_cast<unsigned char>(*first) & ~(0xFF << (7 - nBytes));
+ ++first;
+ --nBytes;
+ for (; nBytes > 0; ++first, --nBytes) {
+ if ((first == last) || !IsTrailingByte(*first)) {
+ codePoint = REPLACEMENT_CHARACTER;
+ break;
+ }
+ codePoint <<= 6;
+ codePoint |= *first & 0x3F;
+ }
+
+ // Check for illegal code points
+ if (codePoint > 0x10FFFF)
+ codePoint = REPLACEMENT_CHARACTER;
+ else if (codePoint >= 0xD800 && codePoint <= 0xDFFF)
+ codePoint = REPLACEMENT_CHARACTER;
+ else if ((codePoint & 0xFFFE) == 0xFFFE)
+ codePoint = REPLACEMENT_CHARACTER;
+ else if (codePoint >= 0xFDD0 && codePoint <= 0xFDEF)
+ codePoint = REPLACEMENT_CHARACTER;
+ return true;
+}
+
+void WriteCodePoint(ostream_wrapper& out, int codePoint) {
+ if (codePoint < 0 || codePoint > 0x10FFFF) {
+ codePoint = REPLACEMENT_CHARACTER;
+ }
+ if (codePoint <= 0x7F) {
+ out << static_cast<char>(codePoint);
+ } else if (codePoint <= 0x7FF) {
+ out << static_cast<char>(0xC0 | (codePoint >> 6))
+ << static_cast<char>(0x80 | (codePoint & 0x3F));
+ } else if (codePoint <= 0xFFFF) {
+ out << static_cast<char>(0xE0 | (codePoint >> 12))
+ << static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F))
+ << static_cast<char>(0x80 | (codePoint & 0x3F));
+ } else {
+ out << static_cast<char>(0xF0 | (codePoint >> 18))
+ << static_cast<char>(0x80 | ((codePoint >> 12) & 0x3F))
+ << static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F))
+ << static_cast<char>(0x80 | (codePoint & 0x3F));
+ }
+}
+
+bool IsValidPlainScalar(const std::string& str, FlowType::value flowType,
+ bool allowOnlyAscii) {
+ // check against null
+ if (IsNullString(str)) {
+ return false;
+ }
+
+ // check the start
+ const RegEx& start = (flowType == FlowType::Flow ? Exp::PlainScalarInFlow()
+ : Exp::PlainScalar());
+ if (!start.Matches(str)) {
+ return false;
+ }
+
+ // and check the end for plain whitespace (which can't be faithfully kept in a
+ // plain scalar)
+ if (!str.empty() && *str.rbegin() == ' ') {
+ return false;
+ }
+
+ // then check until something is disallowed
+ static const RegEx& disallowed_flow =
+ Exp::EndScalarInFlow() | (Exp::BlankOrBreak() + Exp::Comment()) |
+ Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() |
+ Exp::Tab() | Exp::Ampersand();
+ static const RegEx& disallowed_block =
+ Exp::EndScalar() | (Exp::BlankOrBreak() + Exp::Comment()) |
+ Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() |
+ Exp::Tab() | Exp::Ampersand();
+ const RegEx& disallowed =
+ flowType == FlowType::Flow ? disallowed_flow : disallowed_block;
+
+ StringCharSource buffer(str.c_str(), str.size());
+ while (buffer) {
+ if (disallowed.Matches(buffer)) {
+ return false;
+ }
+ if (allowOnlyAscii && (0x80 <= static_cast<unsigned char>(buffer[0]))) {
+ return false;
+ }
+ ++buffer;
+ }
+
+ return true;
+}
+
+bool IsValidSingleQuotedScalar(const std::string& str, bool escapeNonAscii) {
+ // TODO: check for non-printable characters?
+ return std::none_of(str.begin(), str.end(), [=](char ch) {
+ return (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch))) ||
+ (ch == '\n');
+ });
+}
+
+bool IsValidLiteralScalar(const std::string& str, FlowType::value flowType,
+ bool escapeNonAscii) {
+ if (flowType == FlowType::Flow) {
+ return false;
+ }
+
+ // TODO: check for non-printable characters?
+ return std::none_of(str.begin(), str.end(), [=](char ch) {
+ return (escapeNonAscii && (0x80 <= static_cast<unsigned char>(ch)));
+ });
+}
+
+std::pair<uint16_t, uint16_t> EncodeUTF16SurrogatePair(int codePoint) {
+ const uint32_t leadOffset = 0xD800 - (0x10000 >> 10);
+
+ return {
+ leadOffset | (codePoint >> 10),
+ 0xDC00 | (codePoint & 0x3FF),
+ };
+}
+
+void WriteDoubleQuoteEscapeSequence(ostream_wrapper& out, int codePoint, StringEscaping::value stringEscapingStyle) {
+ static const char hexDigits[] = "0123456789abcdef";
+
+ out << "\\";
+ int digits = 8;
+ if (codePoint < 0xFF && stringEscapingStyle != StringEscaping::JSON) {
+ out << "x";
+ digits = 2;
+ } else if (codePoint < 0xFFFF) {
+ out << "u";
+ digits = 4;
+ } else if (stringEscapingStyle != StringEscaping::JSON) {
+ out << "U";
+ digits = 8;
+ } else {
+ auto surrogatePair = EncodeUTF16SurrogatePair(codePoint);
+ WriteDoubleQuoteEscapeSequence(out, surrogatePair.first, stringEscapingStyle);
+ WriteDoubleQuoteEscapeSequence(out, surrogatePair.second, stringEscapingStyle);
+ return;
+ }
+
+ // Write digits into the escape sequence
+ for (; digits > 0; --digits)
+ out << hexDigits[(codePoint >> (4 * (digits - 1))) & 0xF];
+}
+
+bool WriteAliasName(ostream_wrapper& out, const std::string& str) {
+ int codePoint;
+ for (std::string::const_iterator i = str.begin();
+ GetNextCodePointAndAdvance(codePoint, i, str.end());) {
+ if (!IsAnchorChar(codePoint)) {
+ return false;
+ }
+
+ WriteCodePoint(out, codePoint);
+ }
+ return true;
+}
+} // namespace
+
+StringFormat::value ComputeStringFormat(const std::string& str,
+ EMITTER_MANIP strFormat,
+ FlowType::value flowType,
+ bool escapeNonAscii) {
+ switch (strFormat) {
+ case Auto:
+ if (IsValidPlainScalar(str, flowType, escapeNonAscii)) {
+ return StringFormat::Plain;
+ }
+ return StringFormat::DoubleQuoted;
+ case SingleQuoted:
+ if (IsValidSingleQuotedScalar(str, escapeNonAscii)) {
+ return StringFormat::SingleQuoted;
+ }
+ return StringFormat::DoubleQuoted;
+ case DoubleQuoted:
+ return StringFormat::DoubleQuoted;
+ case Literal:
+ if (IsValidLiteralScalar(str, flowType, escapeNonAscii)) {
+ return StringFormat::Literal;
+ }
+ return StringFormat::DoubleQuoted;
+ default:
+ break;
+ }
+
+ return StringFormat::DoubleQuoted;
+}
+
+bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str) {
+ out << "'";
+ int codePoint;
+ for (std::string::const_iterator i = str.begin();
+ GetNextCodePointAndAdvance(codePoint, i, str.end());) {
+ if (codePoint == '\n') {
+ return false; // We can't handle a new line and the attendant indentation
+ // yet
+ }
+
+ if (codePoint == '\'') {
+ out << "''";
+ } else {
+ WriteCodePoint(out, codePoint);
+ }
+ }
+ out << "'";
+ return true;
+}
+
+bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str,
+ StringEscaping::value stringEscaping) {
+ out << "\"";
+ int codePoint;
+ for (std::string::const_iterator i = str.begin();
+ GetNextCodePointAndAdvance(codePoint, i, str.end());) {
+ switch (codePoint) {
+ case '\"':
+ out << "\\\"";
+ break;
+ case '\\':
+ out << "\\\\";
+ break;
+ case '\n':
+ out << "\\n";
+ break;
+ case '\t':
+ out << "\\t";
+ break;
+ case '\r':
+ out << "\\r";
+ break;
+ case '\b':
+ out << "\\b";
+ break;
+ case '\f':
+ out << "\\f";
+ break;
+ default:
+ if (codePoint < 0x20 ||
+ (codePoint >= 0x80 &&
+ codePoint <= 0xA0)) { // Control characters and non-breaking space
+ WriteDoubleQuoteEscapeSequence(out, codePoint, stringEscaping);
+ } else if (codePoint == 0xFEFF) { // Byte order marks (ZWNS) should be
+ // escaped (YAML 1.2, sec. 5.2)
+ WriteDoubleQuoteEscapeSequence(out, codePoint, stringEscaping);
+ } else if (stringEscaping == StringEscaping::NonAscii && codePoint > 0x7E) {
+ WriteDoubleQuoteEscapeSequence(out, codePoint, stringEscaping);
+ } else {
+ WriteCodePoint(out, codePoint);
+ }
+ }
+ }
+ out << "\"";
+ return true;
+}
+
+bool WriteLiteralString(ostream_wrapper& out, const std::string& str,
+ std::size_t indent) {
+ out << "|\n";
+ int codePoint;
+ for (std::string::const_iterator i = str.begin();
+ GetNextCodePointAndAdvance(codePoint, i, str.end());) {
+ if (codePoint == '\n') {
+ out << "\n";
+ } else {
+ out<< IndentTo(indent);
+ WriteCodePoint(out, codePoint);
+ }
+ }
+ return true;
+}
+
+bool WriteChar(ostream_wrapper& out, char ch, StringEscaping::value stringEscapingStyle) {
+ if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) {
+ out << ch;
+ } else if (ch == '\"') {
+ out << R"("\"")";
+ } else if (ch == '\t') {
+ out << R"("\t")";
+ } else if (ch == '\n') {
+ out << R"("\n")";
+ } else if (ch == '\b') {
+ out << R"("\b")";
+ } else if (ch == '\r') {
+ out << R"("\r")";
+ } else if (ch == '\f') {
+ out << R"("\f")";
+ } else if (ch == '\\') {
+ out << R"("\\")";
+ } else if (0x20 <= ch && ch <= 0x7e) {
+ out << "\"" << ch << "\"";
+ } else {
+ out << "\"";
+ WriteDoubleQuoteEscapeSequence(out, ch, stringEscapingStyle);
+ out << "\"";
+ }
+ return true;
+}
+
+bool WriteComment(ostream_wrapper& out, const std::string& str,
+ std::size_t postCommentIndent) {
+ const std::size_t curIndent = out.col();
+ out << "#" << Indentation(postCommentIndent);
+ out.set_comment();
+ int codePoint;
+ for (std::string::const_iterator i = str.begin();
+ GetNextCodePointAndAdvance(codePoint, i, str.end());) {
+ if (codePoint == '\n') {
+ out << "\n"
+ << IndentTo(curIndent) << "#" << Indentation(postCommentIndent);
+ out.set_comment();
+ } else {
+ WriteCodePoint(out, codePoint);
+ }
+ }
+ return true;
+}
+
+bool WriteAlias(ostream_wrapper& out, const std::string& str) {
+ out << "*";
+ return WriteAliasName(out, str);
+}
+
+bool WriteAnchor(ostream_wrapper& out, const std::string& str) {
+ out << "&";
+ return WriteAliasName(out, str);
+}
+
+bool WriteTag(ostream_wrapper& out, const std::string& str, bool verbatim) {
+ out << (verbatim ? "!<" : "!");
+ StringCharSource buffer(str.c_str(), str.size());
+ const RegEx& reValid = verbatim ? Exp::URI() : Exp::Tag();
+ while (buffer) {
+ int n = reValid.Match(buffer);
+ if (n <= 0) {
+ return false;
+ }
+
+ while (--n >= 0) {
+ out << buffer[0];
+ ++buffer;
+ }
+ }
+ if (verbatim) {
+ out << ">";
+ }
+ return true;
+}
+
+bool WriteTagWithPrefix(ostream_wrapper& out, const std::string& prefix,
+ const std::string& tag) {
+ out << "!";
+ StringCharSource prefixBuffer(prefix.c_str(), prefix.size());
+ while (prefixBuffer) {
+ int n = Exp::URI().Match(prefixBuffer);
+ if (n <= 0) {
+ return false;
+ }
+
+ while (--n >= 0) {
+ out << prefixBuffer[0];
+ ++prefixBuffer;
+ }
+ }
+
+ out << "!";
+ StringCharSource tagBuffer(tag.c_str(), tag.size());
+ while (tagBuffer) {
+ int n = Exp::Tag().Match(tagBuffer);
+ if (n <= 0) {
+ return false;
+ }
+
+ while (--n >= 0) {
+ out << tagBuffer[0];
+ ++tagBuffer;
+ }
+ }
+ return true;
+}
+
+bool WriteBinary(ostream_wrapper& out, const Binary& binary) {
+ WriteDoubleQuotedString(out, EncodeBase64(binary.data(), binary.size()),
+ StringEscaping::None);
+ return true;
+}
+} // namespace Utils
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/emitterutils.h b/src/libs/3rdparty/yaml-cpp/src/emitterutils.h
new file mode 100644
index 0000000000..3a7d598252
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/emitterutils.h
@@ -0,0 +1,55 @@
+#ifndef EMITTERUTILS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define EMITTERUTILS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <string>
+
+#include "emitterstate.h"
+#include "yaml-cpp/emittermanip.h"
+#include "yaml-cpp/ostream_wrapper.h"
+
+namespace YAML {
+class ostream_wrapper;
+} // namespace YAML
+
+namespace YAML {
+class Binary;
+
+struct StringFormat {
+ enum value { Plain, SingleQuoted, DoubleQuoted, Literal };
+};
+
+struct StringEscaping {
+ enum value { None, NonAscii, JSON };
+};
+
+namespace Utils {
+StringFormat::value ComputeStringFormat(const std::string& str,
+ EMITTER_MANIP strFormat,
+ FlowType::value flowType,
+ bool escapeNonAscii);
+
+bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str);
+bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str,
+ StringEscaping::value stringEscaping);
+bool WriteLiteralString(ostream_wrapper& out, const std::string& str,
+ std::size_t indent);
+bool WriteChar(ostream_wrapper& out, char ch,
+ StringEscaping::value stringEscapingStyle);
+bool WriteComment(ostream_wrapper& out, const std::string& str,
+ std::size_t postCommentIndent);
+bool WriteAlias(ostream_wrapper& out, const std::string& str);
+bool WriteAnchor(ostream_wrapper& out, const std::string& str);
+bool WriteTag(ostream_wrapper& out, const std::string& str, bool verbatim);
+bool WriteTagWithPrefix(ostream_wrapper& out, const std::string& prefix,
+ const std::string& tag);
+bool WriteBinary(ostream_wrapper& out, const Binary& binary);
+}
+}
+
+#endif // EMITTERUTILS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/exceptions.cpp b/src/libs/3rdparty/yaml-cpp/src/exceptions.cpp
new file mode 100644
index 0000000000..43a7976e90
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/exceptions.cpp
@@ -0,0 +1,20 @@
+#include "yaml-cpp/exceptions.h"
+#include "yaml-cpp/noexcept.h"
+
+namespace YAML {
+
+// These destructors are defined out-of-line so the vtable is only emitted once.
+Exception::~Exception() YAML_CPP_NOEXCEPT = default;
+ParserException::~ParserException() YAML_CPP_NOEXCEPT = default;
+RepresentationException::~RepresentationException() YAML_CPP_NOEXCEPT = default;
+InvalidScalar::~InvalidScalar() YAML_CPP_NOEXCEPT = default;
+KeyNotFound::~KeyNotFound() YAML_CPP_NOEXCEPT = default;
+InvalidNode::~InvalidNode() YAML_CPP_NOEXCEPT = default;
+BadConversion::~BadConversion() YAML_CPP_NOEXCEPT = default;
+BadDereference::~BadDereference() YAML_CPP_NOEXCEPT = default;
+BadSubscript::~BadSubscript() YAML_CPP_NOEXCEPT = default;
+BadPushback::~BadPushback() YAML_CPP_NOEXCEPT = default;
+BadInsert::~BadInsert() YAML_CPP_NOEXCEPT = default;
+EmitterException::~EmitterException() YAML_CPP_NOEXCEPT = default;
+BadFile::~BadFile() YAML_CPP_NOEXCEPT = default;
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/exp.cpp b/src/libs/3rdparty/yaml-cpp/src/exp.cpp
new file mode 100644
index 0000000000..992620ff94
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/exp.cpp
@@ -0,0 +1,137 @@
+#include <sstream>
+
+#include "exp.h"
+#include "stream.h"
+#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
+
+namespace YAML {
+struct Mark;
+} // namespace YAML
+
+namespace YAML {
+namespace Exp {
+unsigned ParseHex(const std::string& str, const Mark& mark) {
+ unsigned value = 0;
+ for (char ch : str) {
+ int digit = 0;
+ if ('a' <= ch && ch <= 'f')
+ digit = ch - 'a' + 10;
+ else if ('A' <= ch && ch <= 'F')
+ digit = ch - 'A' + 10;
+ else if ('0' <= ch && ch <= '9')
+ digit = ch - '0';
+ else
+ throw ParserException(mark, ErrorMsg::INVALID_HEX);
+
+ value = (value << 4) + digit;
+ }
+
+ return value;
+}
+
+std::string Str(unsigned ch) { return std::string(1, static_cast<char>(ch)); }
+
+// Escape
+// . Translates the next 'codeLength' characters into a hex number and returns
+// the result.
+// . Throws if it's not actually hex.
+std::string Escape(Stream& in, int codeLength) {
+ // grab string
+ std::string str;
+ for (int i = 0; i < codeLength; i++)
+ str += in.get();
+
+ // get the value
+ unsigned value = ParseHex(str, in.mark());
+
+ // legal unicode?
+ if ((value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF) {
+ std::stringstream msg;
+ msg << ErrorMsg::INVALID_UNICODE << value;
+ throw ParserException(in.mark(), msg.str());
+ }
+
+ // now break it up into chars
+ if (value <= 0x7F)
+ return Str(value);
+
+ if (value <= 0x7FF)
+ return Str(0xC0 + (value >> 6)) + Str(0x80 + (value & 0x3F));
+
+ if (value <= 0xFFFF)
+ return Str(0xE0 + (value >> 12)) + Str(0x80 + ((value >> 6) & 0x3F)) +
+ Str(0x80 + (value & 0x3F));
+
+ return Str(0xF0 + (value >> 18)) + Str(0x80 + ((value >> 12) & 0x3F)) +
+ Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F));
+}
+
+// Escape
+// . Escapes the sequence starting 'in' (it must begin with a '\' or single
+// quote)
+// and returns the result.
+// . Throws if it's an unknown escape character.
+std::string Escape(Stream& in) {
+ // eat slash
+ char escape = in.get();
+
+ // switch on escape character
+ char ch = in.get();
+
+ // first do single quote, since it's easier
+ if (escape == '\'' && ch == '\'')
+ return "\'";
+
+ // now do the slash (we're not gonna check if it's a slash - you better pass
+ // one!)
+ switch (ch) {
+ case '0':
+ return std::string(1, '\x00');
+ case 'a':
+ return "\x07";
+ case 'b':
+ return "\x08";
+ case 't':
+ case '\t':
+ return "\x09";
+ case 'n':
+ return "\x0A";
+ case 'v':
+ return "\x0B";
+ case 'f':
+ return "\x0C";
+ case 'r':
+ return "\x0D";
+ case 'e':
+ return "\x1B";
+ case ' ':
+ return R"( )";
+ case '\"':
+ return "\"";
+ case '\'':
+ return "\'";
+ case '\\':
+ return "\\";
+ case '/':
+ return "/";
+ case 'N':
+ return "\x85";
+ case '_':
+ return "\xA0";
+ case 'L':
+ return "\xE2\x80\xA8"; // LS (#x2028)
+ case 'P':
+ return "\xE2\x80\xA9"; // PS (#x2029)
+ case 'x':
+ return Escape(in, 2);
+ case 'u':
+ return Escape(in, 4);
+ case 'U':
+ return Escape(in, 8);
+ }
+
+ std::stringstream msg;
+ throw ParserException(in.mark(), std::string(ErrorMsg::INVALID_ESCAPE) + ch);
+}
+} // namespace Exp
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/exp.h b/src/libs/3rdparty/yaml-cpp/src/exp.h
new file mode 100644
index 0000000000..c8837f0f82
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/exp.h
@@ -0,0 +1,226 @@
+#ifndef EXP_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define EXP_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <ios>
+#include <string>
+
+#include "regex_yaml.h"
+#include "stream.h"
+
+namespace YAML {
+////////////////////////////////////////////////////////////////////////////////
+// Here we store a bunch of expressions for matching different parts of the
+// file.
+
+namespace Exp {
+// misc
+inline const RegEx& Empty() {
+ static const RegEx e;
+ return e;
+}
+inline const RegEx& Space() {
+ static const RegEx e = RegEx(' ');
+ return e;
+}
+inline const RegEx& Tab() {
+ static const RegEx e = RegEx('\t');
+ return e;
+}
+inline const RegEx& Blank() {
+ static const RegEx e = Space() | Tab();
+ return e;
+}
+inline const RegEx& Break() {
+ static const RegEx e = RegEx('\n') | RegEx("\r\n") | RegEx('\r');
+ return e;
+}
+inline const RegEx& BlankOrBreak() {
+ static const RegEx e = Blank() | Break();
+ return e;
+}
+inline const RegEx& Digit() {
+ static const RegEx e = RegEx('0', '9');
+ return e;
+}
+inline const RegEx& Alpha() {
+ static const RegEx e = RegEx('a', 'z') | RegEx('A', 'Z');
+ return e;
+}
+inline const RegEx& AlphaNumeric() {
+ static const RegEx e = Alpha() | Digit();
+ return e;
+}
+inline const RegEx& Word() {
+ static const RegEx e = AlphaNumeric() | RegEx('-');
+ return e;
+}
+inline const RegEx& Hex() {
+ static const RegEx e = Digit() | RegEx('A', 'F') | RegEx('a', 'f');
+ return e;
+}
+// Valid Unicode code points that are not part of c-printable (YAML 1.2, sec.
+// 5.1)
+inline const RegEx& NotPrintable() {
+ static const RegEx e =
+ RegEx(0) |
+ RegEx("\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x7F", REGEX_OR) |
+ RegEx(0x0E, 0x1F) |
+ (RegEx('\xC2') + (RegEx('\x80', '\x84') | RegEx('\x86', '\x9F')));
+ return e;
+}
+inline const RegEx& Utf8_ByteOrderMark() {
+ static const RegEx e = RegEx("\xEF\xBB\xBF");
+ return e;
+}
+
+// actual tags
+
+inline const RegEx& DocStart() {
+ static const RegEx e = RegEx("---") + (BlankOrBreak() | RegEx());
+ return e;
+}
+inline const RegEx& DocEnd() {
+ static const RegEx e = RegEx("...") + (BlankOrBreak() | RegEx());
+ return e;
+}
+inline const RegEx& DocIndicator() {
+ static const RegEx e = DocStart() | DocEnd();
+ return e;
+}
+inline const RegEx& BlockEntry() {
+ static const RegEx e = RegEx('-') + (BlankOrBreak() | RegEx());
+ return e;
+}
+inline const RegEx& Key() {
+ static const RegEx e = RegEx('?') + BlankOrBreak();
+ return e;
+}
+inline const RegEx& KeyInFlow() {
+ static const RegEx e = RegEx('?') + BlankOrBreak();
+ return e;
+}
+inline const RegEx& Value() {
+ static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx());
+ return e;
+}
+inline const RegEx& ValueInFlow() {
+ static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx(",]}", REGEX_OR));
+ return e;
+}
+inline const RegEx& ValueInJSONFlow() {
+ static const RegEx e = RegEx(':');
+ return e;
+}
+inline const RegEx& Ampersand() {
+ static const RegEx e = RegEx('&');
+ return e;
+}
+inline const RegEx Comment() {
+ static const RegEx e = RegEx('#');
+ return e;
+}
+inline const RegEx& Anchor() {
+ static const RegEx e = !(RegEx("[]{},", REGEX_OR) | BlankOrBreak());
+ return e;
+}
+inline const RegEx& AnchorEnd() {
+ static const RegEx e = RegEx("?:,]}%@`", REGEX_OR) | BlankOrBreak();
+ return e;
+}
+inline const RegEx& URI() {
+ static const RegEx e = Word() | RegEx("#;/?:@&=+$,_.!~*'()[]", REGEX_OR) |
+ (RegEx('%') + Hex() + Hex());
+ return e;
+}
+inline const RegEx& Tag() {
+ static const RegEx e = Word() | RegEx("#;/?:@&=+$_.~*'()", REGEX_OR) |
+ (RegEx('%') + Hex() + Hex());
+ return e;
+}
+
+// Plain scalar rules:
+// . Cannot start with a blank.
+// . Can never start with any of , [ ] { } # & * ! | > \' \" % @ `
+// . In the block context - ? : must be not be followed with a space.
+// . In the flow context ? is illegal and : and - must not be followed with a
+// space.
+inline const RegEx& PlainScalar() {
+ static const RegEx e =
+ !(BlankOrBreak() | RegEx(",[]{}#&*!|>\'\"%@`", REGEX_OR) |
+ (RegEx("-?:", REGEX_OR) + (BlankOrBreak() | RegEx())));
+ return e;
+}
+inline const RegEx& PlainScalarInFlow() {
+ static const RegEx e =
+ !(BlankOrBreak() | RegEx("?,[]{}#&*!|>\'\"%@`", REGEX_OR) |
+ (RegEx("-:", REGEX_OR) + (Blank() | RegEx())));
+ return e;
+}
+inline const RegEx& EndScalar() {
+ static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx());
+ return e;
+}
+inline const RegEx& EndScalarInFlow() {
+ static const RegEx e =
+ (RegEx(':') + (BlankOrBreak() | RegEx() | RegEx(",]}", REGEX_OR))) |
+ RegEx(",?[]{}", REGEX_OR);
+ return e;
+}
+
+inline const RegEx& ScanScalarEndInFlow() {
+ static const RegEx e = (EndScalarInFlow() | (BlankOrBreak() + Comment()));
+ return e;
+}
+
+inline const RegEx& ScanScalarEnd() {
+ static const RegEx e = EndScalar() | (BlankOrBreak() + Comment());
+ return e;
+}
+inline const RegEx& EscSingleQuote() {
+ static const RegEx e = RegEx("\'\'");
+ return e;
+}
+inline const RegEx& EscBreak() {
+ static const RegEx e = RegEx('\\') + Break();
+ return e;
+}
+
+inline const RegEx& ChompIndicator() {
+ static const RegEx e = RegEx("+-", REGEX_OR);
+ return e;
+}
+inline const RegEx& Chomp() {
+ static const RegEx e = (ChompIndicator() + Digit()) |
+ (Digit() + ChompIndicator()) | ChompIndicator() |
+ Digit();
+ return e;
+}
+
+// and some functions
+std::string Escape(Stream& in);
+} // namespace Exp
+
+namespace Keys {
+const char Directive = '%';
+const char FlowSeqStart = '[';
+const char FlowSeqEnd = ']';
+const char FlowMapStart = '{';
+const char FlowMapEnd = '}';
+const char FlowEntry = ',';
+const char Alias = '*';
+const char Anchor = '&';
+const char Tag = '!';
+const char LiteralScalar = '|';
+const char FoldedScalar = '>';
+const char VerbatimTagStart = '<';
+const char VerbatimTagEnd = '>';
+} // namespace Keys
+} // namespace YAML
+
+#endif // EXP_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/indentation.h b/src/libs/3rdparty/yaml-cpp/src/indentation.h
new file mode 100644
index 0000000000..1a2ccaea2e
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/indentation.h
@@ -0,0 +1,41 @@
+#ifndef INDENTATION_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define INDENTATION_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <iostream>
+#include <cstddef>
+
+#include "yaml-cpp/ostream_wrapper.h"
+
+namespace YAML {
+struct Indentation {
+ Indentation(std::size_t n_) : n(n_) {}
+ std::size_t n;
+};
+
+inline ostream_wrapper& operator<<(ostream_wrapper& out,
+ const Indentation& indent) {
+ for (std::size_t i = 0; i < indent.n; i++)
+ out << ' ';
+ return out;
+}
+
+struct IndentTo {
+ IndentTo(std::size_t n_) : n(n_) {}
+ std::size_t n;
+};
+
+inline ostream_wrapper& operator<<(ostream_wrapper& out,
+ const IndentTo& indent) {
+ while (out.col() < indent.n)
+ out << ' ';
+ return out;
+}
+}
+
+#endif // INDENTATION_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/memory.cpp b/src/libs/3rdparty/yaml-cpp/src/memory.cpp
new file mode 100644
index 0000000000..676e4c7f15
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/memory.cpp
@@ -0,0 +1,26 @@
+#include "yaml-cpp/node/detail/memory.h"
+#include "yaml-cpp/node/detail/node.h" // IWYU pragma: keep
+#include "yaml-cpp/node/ptr.h"
+
+namespace YAML {
+namespace detail {
+
+void memory_holder::merge(memory_holder& rhs) {
+ if (m_pMemory == rhs.m_pMemory)
+ return;
+
+ m_pMemory->merge(*rhs.m_pMemory);
+ rhs.m_pMemory = m_pMemory;
+}
+
+node& memory::create_node() {
+ shared_node pNode(new node);
+ m_nodes.insert(pNode);
+ return *pNode;
+}
+
+void memory::merge(const memory& rhs) {
+ m_nodes.insert(rhs.m_nodes.begin(), rhs.m_nodes.end());
+}
+} // namespace detail
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/node.cpp b/src/libs/3rdparty/yaml-cpp/src/node.cpp
new file mode 100644
index 0000000000..badc3110ec
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/node.cpp
@@ -0,0 +1,12 @@
+#include "yaml-cpp/node/node.h"
+#include "nodebuilder.h"
+#include "nodeevents.h"
+
+namespace YAML {
+Node Clone(const Node& node) {
+ NodeEvents events(node);
+ NodeBuilder builder;
+ events.Emit(builder);
+ return builder.Root();
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/node_data.cpp b/src/libs/3rdparty/yaml-cpp/src/node_data.cpp
new file mode 100644
index 0000000000..8f5422ae6e
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/node_data.cpp
@@ -0,0 +1,324 @@
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <sstream>
+
+#include "yaml-cpp/exceptions.h"
+#include "yaml-cpp/node/detail/memory.h"
+#include "yaml-cpp/node/detail/node.h" // IWYU pragma: keep
+#include "yaml-cpp/node/detail/node_data.h"
+#include "yaml-cpp/node/detail/node_iterator.h"
+#include "yaml-cpp/node/ptr.h"
+#include "yaml-cpp/node/type.h"
+
+namespace YAML {
+namespace detail {
+YAML_CPP_API std::atomic<size_t> node::m_amount{0};
+
+const std::string& node_data::empty_scalar() {
+ static const std::string svalue;
+ return svalue;
+}
+
+node_data::node_data()
+ : m_isDefined(false),
+ m_mark(Mark::null_mark()),
+ m_type(NodeType::Null),
+ m_tag{},
+ m_style(EmitterStyle::Default),
+ m_scalar{},
+ m_sequence{},
+ m_seqSize(0),
+ m_map{},
+ m_undefinedPairs{} {}
+
+void node_data::mark_defined() {
+ if (m_type == NodeType::Undefined)
+ m_type = NodeType::Null;
+ m_isDefined = true;
+}
+
+void node_data::set_mark(const Mark& mark) { m_mark = mark; }
+
+void node_data::set_type(NodeType::value type) {
+ if (type == NodeType::Undefined) {
+ m_type = type;
+ m_isDefined = false;
+ return;
+ }
+
+ m_isDefined = true;
+ if (type == m_type)
+ return;
+
+ m_type = type;
+
+ switch (m_type) {
+ case NodeType::Null:
+ break;
+ case NodeType::Scalar:
+ m_scalar.clear();
+ break;
+ case NodeType::Sequence:
+ reset_sequence();
+ break;
+ case NodeType::Map:
+ reset_map();
+ break;
+ case NodeType::Undefined:
+ assert(false);
+ break;
+ }
+}
+
+void node_data::set_tag(const std::string& tag) { m_tag = tag; }
+
+void node_data::set_style(EmitterStyle::value style) { m_style = style; }
+
+void node_data::set_null() {
+ m_isDefined = true;
+ m_type = NodeType::Null;
+}
+
+void node_data::set_scalar(const std::string& scalar) {
+ m_isDefined = true;
+ m_type = NodeType::Scalar;
+ m_scalar = scalar;
+}
+
+// size/iterator
+std::size_t node_data::size() const {
+ if (!m_isDefined)
+ return 0;
+
+ switch (m_type) {
+ case NodeType::Sequence:
+ compute_seq_size();
+ return m_seqSize;
+ case NodeType::Map:
+ compute_map_size();
+ return m_map.size() - m_undefinedPairs.size();
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+void node_data::compute_seq_size() const {
+ while (m_seqSize < m_sequence.size() && m_sequence[m_seqSize]->is_defined())
+ m_seqSize++;
+}
+
+void node_data::compute_map_size() const {
+ auto it = m_undefinedPairs.begin();
+ while (it != m_undefinedPairs.end()) {
+ auto jt = std::next(it);
+ if (it->first->is_defined() && it->second->is_defined())
+ m_undefinedPairs.erase(it);
+ it = jt;
+ }
+}
+
+const_node_iterator node_data::begin() const {
+ if (!m_isDefined)
+ return {};
+
+ switch (m_type) {
+ case NodeType::Sequence:
+ return const_node_iterator(m_sequence.begin());
+ case NodeType::Map:
+ return const_node_iterator(m_map.begin(), m_map.end());
+ default:
+ return {};
+ }
+}
+
+node_iterator node_data::begin() {
+ if (!m_isDefined)
+ return {};
+
+ switch (m_type) {
+ case NodeType::Sequence:
+ return node_iterator(m_sequence.begin());
+ case NodeType::Map:
+ return node_iterator(m_map.begin(), m_map.end());
+ default:
+ return {};
+ }
+}
+
+const_node_iterator node_data::end() const {
+ if (!m_isDefined)
+ return {};
+
+ switch (m_type) {
+ case NodeType::Sequence:
+ return const_node_iterator(m_sequence.end());
+ case NodeType::Map:
+ return const_node_iterator(m_map.end(), m_map.end());
+ default:
+ return {};
+ }
+}
+
+node_iterator node_data::end() {
+ if (!m_isDefined)
+ return {};
+
+ switch (m_type) {
+ case NodeType::Sequence:
+ return node_iterator(m_sequence.end());
+ case NodeType::Map:
+ return node_iterator(m_map.end(), m_map.end());
+ default:
+ return {};
+ }
+}
+
+// sequence
+void node_data::push_back(node& node,
+ const shared_memory_holder& /* pMemory */) {
+ if (m_type == NodeType::Undefined || m_type == NodeType::Null) {
+ m_type = NodeType::Sequence;
+ reset_sequence();
+ }
+
+ if (m_type != NodeType::Sequence)
+ throw BadPushback();
+
+ m_sequence.push_back(&node);
+}
+
+void node_data::insert(node& key, node& value,
+ const shared_memory_holder& pMemory) {
+ switch (m_type) {
+ case NodeType::Map:
+ break;
+ case NodeType::Undefined:
+ case NodeType::Null:
+ case NodeType::Sequence:
+ convert_to_map(pMemory);
+ break;
+ case NodeType::Scalar:
+ throw BadSubscript(m_mark, key);
+ }
+
+ insert_map_pair(key, value);
+}
+
+// indexing
+node* node_data::get(node& key,
+ const shared_memory_holder& /* pMemory */) const {
+ if (m_type != NodeType::Map) {
+ return nullptr;
+ }
+
+ for (const auto& it : m_map) {
+ if (it.first->is(key))
+ return it.second;
+ }
+
+ return nullptr;
+}
+
+node& node_data::get(node& key, const shared_memory_holder& pMemory) {
+ switch (m_type) {
+ case NodeType::Map:
+ break;
+ case NodeType::Undefined:
+ case NodeType::Null:
+ case NodeType::Sequence:
+ convert_to_map(pMemory);
+ break;
+ case NodeType::Scalar:
+ throw BadSubscript(m_mark, key);
+ }
+
+ for (const auto& it : m_map) {
+ if (it.first->is(key))
+ return *it.second;
+ }
+
+ node& value = pMemory->create_node();
+ insert_map_pair(key, value);
+ return value;
+}
+
+bool node_data::remove(node& key, const shared_memory_holder& /* pMemory */) {
+ if (m_type != NodeType::Map)
+ return false;
+
+ for (auto it = m_undefinedPairs.begin(); it != m_undefinedPairs.end();) {
+ auto jt = std::next(it);
+ if (it->first->is(key))
+ m_undefinedPairs.erase(it);
+ it = jt;
+ }
+
+ auto it =
+ std::find_if(m_map.begin(), m_map.end(),
+ [&](std::pair<YAML::detail::node*, YAML::detail::node*> j) {
+ return (j.first->is(key));
+ });
+
+ if (it != m_map.end()) {
+ m_map.erase(it);
+ return true;
+ }
+
+ return false;
+}
+
+void node_data::reset_sequence() {
+ m_sequence.clear();
+ m_seqSize = 0;
+}
+
+void node_data::reset_map() {
+ m_map.clear();
+ m_undefinedPairs.clear();
+}
+
+void node_data::insert_map_pair(node& key, node& value) {
+ m_map.emplace_back(&key, &value);
+
+ if (!key.is_defined() || !value.is_defined())
+ m_undefinedPairs.emplace_back(&key, &value);
+}
+
+void node_data::convert_to_map(const shared_memory_holder& pMemory) {
+ switch (m_type) {
+ case NodeType::Undefined:
+ case NodeType::Null:
+ reset_map();
+ m_type = NodeType::Map;
+ break;
+ case NodeType::Sequence:
+ convert_sequence_to_map(pMemory);
+ break;
+ case NodeType::Map:
+ break;
+ case NodeType::Scalar:
+ assert(false);
+ break;
+ }
+}
+
+void node_data::convert_sequence_to_map(const shared_memory_holder& pMemory) {
+ assert(m_type == NodeType::Sequence);
+
+ reset_map();
+ for (std::size_t i = 0; i < m_sequence.size(); i++) {
+ std::stringstream stream;
+ stream << i;
+
+ node& key = pMemory->create_node();
+ key.set_scalar(stream.str());
+ insert_map_pair(key, *m_sequence[i]);
+ }
+
+ reset_sequence();
+ m_type = NodeType::Map;
+}
+} // namespace detail
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/nodebuilder.cpp b/src/libs/3rdparty/yaml-cpp/src/nodebuilder.cpp
new file mode 100644
index 0000000000..bbaefac8a6
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/nodebuilder.cpp
@@ -0,0 +1,134 @@
+#include <cassert>
+
+#include "nodebuilder.h"
+#include "yaml-cpp/node/detail/node.h"
+#include "yaml-cpp/node/impl.h"
+#include "yaml-cpp/node/node.h"
+#include "yaml-cpp/node/type.h"
+
+namespace YAML {
+struct Mark;
+
+NodeBuilder::NodeBuilder()
+ : m_pMemory(new detail::memory_holder),
+ m_pRoot(nullptr),
+ m_stack{},
+ m_anchors{},
+ m_keys{},
+ m_mapDepth(0) {
+ m_anchors.push_back(nullptr); // since the anchors start at 1
+}
+
+NodeBuilder::~NodeBuilder() = default;
+
+Node NodeBuilder::Root() {
+ if (!m_pRoot)
+ return Node();
+
+ return Node(*m_pRoot, m_pMemory);
+}
+
+void NodeBuilder::OnDocumentStart(const Mark&) {}
+
+void NodeBuilder::OnDocumentEnd() {}
+
+void NodeBuilder::OnNull(const Mark& mark, anchor_t anchor) {
+ detail::node& node = Push(mark, anchor);
+ node.set_null();
+ Pop();
+}
+
+void NodeBuilder::OnAlias(const Mark& /* mark */, anchor_t anchor) {
+ detail::node& node = *m_anchors[anchor];
+ Push(node);
+ Pop();
+}
+
+void NodeBuilder::OnScalar(const Mark& mark, const std::string& tag,
+ anchor_t anchor, const std::string& value) {
+ detail::node& node = Push(mark, anchor);
+ node.set_scalar(value);
+ node.set_tag(tag);
+ Pop();
+}
+
+void NodeBuilder::OnSequenceStart(const Mark& mark, const std::string& tag,
+ anchor_t anchor, EmitterStyle::value style) {
+ detail::node& node = Push(mark, anchor);
+ node.set_tag(tag);
+ node.set_type(NodeType::Sequence);
+ node.set_style(style);
+}
+
+void NodeBuilder::OnSequenceEnd() { Pop(); }
+
+void NodeBuilder::OnMapStart(const Mark& mark, const std::string& tag,
+ anchor_t anchor, EmitterStyle::value style) {
+ detail::node& node = Push(mark, anchor);
+ node.set_type(NodeType::Map);
+ node.set_tag(tag);
+ node.set_style(style);
+ m_mapDepth++;
+}
+
+void NodeBuilder::OnMapEnd() {
+ assert(m_mapDepth > 0);
+ m_mapDepth--;
+ Pop();
+}
+
+detail::node& NodeBuilder::Push(const Mark& mark, anchor_t anchor) {
+ detail::node& node = m_pMemory->create_node();
+ node.set_mark(mark);
+ RegisterAnchor(anchor, node);
+ Push(node);
+ return node;
+}
+
+void NodeBuilder::Push(detail::node& node) {
+ const bool needsKey =
+ (!m_stack.empty() && m_stack.back()->type() == NodeType::Map &&
+ m_keys.size() < m_mapDepth);
+
+ m_stack.push_back(&node);
+ if (needsKey)
+ m_keys.emplace_back(&node, false);
+}
+
+void NodeBuilder::Pop() {
+ assert(!m_stack.empty());
+ if (m_stack.size() == 1) {
+ m_pRoot = m_stack[0];
+ m_stack.pop_back();
+ return;
+ }
+
+ detail::node& node = *m_stack.back();
+ m_stack.pop_back();
+
+ detail::node& collection = *m_stack.back();
+
+ if (collection.type() == NodeType::Sequence) {
+ collection.push_back(node, m_pMemory);
+ } else if (collection.type() == NodeType::Map) {
+ assert(!m_keys.empty());
+ PushedKey& key = m_keys.back();
+ if (key.second) {
+ collection.insert(*key.first, node, m_pMemory);
+ m_keys.pop_back();
+ } else {
+ key.second = true;
+ }
+ } else {
+ assert(false);
+ m_stack.clear();
+ }
+}
+
+void NodeBuilder::RegisterAnchor(anchor_t anchor, detail::node& node) {
+ if (anchor) {
+ assert(anchor == m_anchors.size());
+ m_anchors.push_back(&node);
+ }
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/nodebuilder.h b/src/libs/3rdparty/yaml-cpp/src/nodebuilder.h
new file mode 100644
index 0000000000..c580d40e29
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/nodebuilder.h
@@ -0,0 +1,74 @@
+#ifndef NODE_NODEBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define NODE_NODEBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <vector>
+
+#include "yaml-cpp/anchor.h"
+#include "yaml-cpp/emitterstyle.h"
+#include "yaml-cpp/eventhandler.h"
+#include "yaml-cpp/node/ptr.h"
+
+namespace YAML {
+namespace detail {
+class node;
+} // namespace detail
+struct Mark;
+} // namespace YAML
+
+namespace YAML {
+class Node;
+
+class NodeBuilder : public EventHandler {
+ public:
+ NodeBuilder();
+ NodeBuilder(const NodeBuilder&) = delete;
+ NodeBuilder(NodeBuilder&&) = delete;
+ NodeBuilder& operator=(const NodeBuilder&) = delete;
+ NodeBuilder& operator=(NodeBuilder&&) = delete;
+ ~NodeBuilder() override;
+
+ Node Root();
+
+ void OnDocumentStart(const Mark& mark) override;
+ void OnDocumentEnd() override;
+
+ void OnNull(const Mark& mark, anchor_t anchor) override;
+ void OnAlias(const Mark& mark, anchor_t anchor) override;
+ void OnScalar(const Mark& mark, const std::string& tag,
+ anchor_t anchor, const std::string& value) override;
+
+ void OnSequenceStart(const Mark& mark, const std::string& tag,
+ anchor_t anchor, EmitterStyle::value style) override;
+ void OnSequenceEnd() override;
+
+ void OnMapStart(const Mark& mark, const std::string& tag,
+ anchor_t anchor, EmitterStyle::value style) override;
+ void OnMapEnd() override;
+
+ private:
+ detail::node& Push(const Mark& mark, anchor_t anchor);
+ void Push(detail::node& node);
+ void Pop();
+ void RegisterAnchor(anchor_t anchor, detail::node& node);
+
+ private:
+ detail::shared_memory_holder m_pMemory;
+ detail::node* m_pRoot;
+
+ using Nodes = std::vector<detail::node *>;
+ Nodes m_stack;
+ Nodes m_anchors;
+
+ using PushedKey = std::pair<detail::node*, bool>;
+ std::vector<PushedKey> m_keys;
+ std::size_t m_mapDepth;
+};
+} // namespace YAML
+
+#endif // NODE_NODEBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/nodeevents.cpp b/src/libs/3rdparty/yaml-cpp/src/nodeevents.cpp
new file mode 100644
index 0000000000..b1774fef3e
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/nodeevents.cpp
@@ -0,0 +1,98 @@
+#include "nodeevents.h"
+#include "yaml-cpp/eventhandler.h"
+#include "yaml-cpp/mark.h"
+#include "yaml-cpp/node/detail/node.h"
+#include "yaml-cpp/node/detail/node_iterator.h"
+#include "yaml-cpp/node/node.h"
+#include "yaml-cpp/node/type.h"
+
+namespace YAML {
+void NodeEvents::AliasManager::RegisterReference(const detail::node& node) {
+ m_anchorByIdentity.insert(std::make_pair(node.ref(), _CreateNewAnchor()));
+}
+
+anchor_t NodeEvents::AliasManager::LookupAnchor(
+ const detail::node& node) const {
+ auto it = m_anchorByIdentity.find(node.ref());
+ if (it == m_anchorByIdentity.end())
+ return 0;
+ return it->second;
+}
+
+NodeEvents::NodeEvents(const Node& node)
+ : m_pMemory(node.m_pMemory), m_root(node.m_pNode), m_refCount{} {
+ if (m_root)
+ Setup(*m_root);
+}
+
+void NodeEvents::Setup(const detail::node& node) {
+ int& refCount = m_refCount[node.ref()];
+ refCount++;
+ if (refCount > 1)
+ return;
+
+ if (node.type() == NodeType::Sequence) {
+ for (auto element : node)
+ Setup(*element);
+ } else if (node.type() == NodeType::Map) {
+ for (auto element : node) {
+ Setup(*element.first);
+ Setup(*element.second);
+ }
+ }
+}
+
+void NodeEvents::Emit(EventHandler& handler) {
+ AliasManager am;
+
+ handler.OnDocumentStart(Mark());
+ if (m_root)
+ Emit(*m_root, handler, am);
+ handler.OnDocumentEnd();
+}
+
+void NodeEvents::Emit(const detail::node& node, EventHandler& handler,
+ AliasManager& am) const {
+ anchor_t anchor = NullAnchor;
+ if (IsAliased(node)) {
+ anchor = am.LookupAnchor(node);
+ if (anchor) {
+ handler.OnAlias(Mark(), anchor);
+ return;
+ }
+
+ am.RegisterReference(node);
+ anchor = am.LookupAnchor(node);
+ }
+
+ switch (node.type()) {
+ case NodeType::Undefined:
+ break;
+ case NodeType::Null:
+ handler.OnNull(Mark(), anchor);
+ break;
+ case NodeType::Scalar:
+ handler.OnScalar(Mark(), node.tag(), anchor, node.scalar());
+ break;
+ case NodeType::Sequence:
+ handler.OnSequenceStart(Mark(), node.tag(), anchor, node.style());
+ for (auto element : node)
+ Emit(*element, handler, am);
+ handler.OnSequenceEnd();
+ break;
+ case NodeType::Map:
+ handler.OnMapStart(Mark(), node.tag(), anchor, node.style());
+ for (auto element : node) {
+ Emit(*element.first, handler, am);
+ Emit(*element.second, handler, am);
+ }
+ handler.OnMapEnd();
+ break;
+ }
+}
+
+bool NodeEvents::IsAliased(const detail::node& node) const {
+ auto it = m_refCount.find(node.ref());
+ return it != m_refCount.end() && it->second > 1;
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/nodeevents.h b/src/libs/3rdparty/yaml-cpp/src/nodeevents.h
new file mode 100644
index 0000000000..efca9149ed
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/nodeevents.h
@@ -0,0 +1,68 @@
+#ifndef NODE_NODEEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define NODE_NODEEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <map>
+#include <vector>
+
+#include "yaml-cpp/anchor.h"
+#include "yaml-cpp/node/ptr.h"
+
+namespace YAML {
+namespace detail {
+class node;
+} // namespace detail
+} // namespace YAML
+
+namespace YAML {
+class EventHandler;
+class Node;
+
+class NodeEvents {
+ public:
+ explicit NodeEvents(const Node& node);
+ NodeEvents(const NodeEvents&) = delete;
+ NodeEvents(NodeEvents&&) = delete;
+ NodeEvents& operator=(const NodeEvents&) = delete;
+ NodeEvents& operator=(NodeEvents&&) = delete;
+
+ void Emit(EventHandler& handler);
+
+ private:
+ class AliasManager {
+ public:
+ AliasManager() : m_anchorByIdentity{}, m_curAnchor(0) {}
+
+ void RegisterReference(const detail::node& node);
+ anchor_t LookupAnchor(const detail::node& node) const;
+
+ private:
+ anchor_t _CreateNewAnchor() { return ++m_curAnchor; }
+
+ private:
+ using AnchorByIdentity = std::map<const detail::node_ref*, anchor_t>;
+ AnchorByIdentity m_anchorByIdentity;
+
+ anchor_t m_curAnchor;
+ };
+
+ void Setup(const detail::node& node);
+ void Emit(const detail::node& node, EventHandler& handler,
+ AliasManager& am) const;
+ bool IsAliased(const detail::node& node) const;
+
+ private:
+ detail::shared_memory_holder m_pMemory;
+ detail::node* m_root;
+
+ using RefCount = std::map<const detail::node_ref*, int>;
+ RefCount m_refCount;
+};
+} // namespace YAML
+
+#endif // NODE_NODEEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/null.cpp b/src/libs/3rdparty/yaml-cpp/src/null.cpp
new file mode 100644
index 0000000000..db7daebf1d
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/null.cpp
@@ -0,0 +1,10 @@
+#include "yaml-cpp/null.h"
+
+namespace YAML {
+_Null Null;
+
+bool IsNullString(const std::string& str) {
+ return str.empty() || str == "~" || str == "null" || str == "Null" ||
+ str == "NULL";
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/ostream_wrapper.cpp b/src/libs/3rdparty/yaml-cpp/src/ostream_wrapper.cpp
new file mode 100644
index 0000000000..047a9f7c8e
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/ostream_wrapper.cpp
@@ -0,0 +1,62 @@
+#include "yaml-cpp/ostream_wrapper.h"
+
+#include <algorithm>
+#include <cstring>
+#include <iostream>
+
+namespace YAML {
+ostream_wrapper::ostream_wrapper()
+ : m_buffer(1, '\0'),
+ m_pStream(nullptr),
+ m_pos(0),
+ m_row(0),
+ m_col(0),
+ m_comment(false) {}
+
+ostream_wrapper::ostream_wrapper(std::ostream& stream)
+ : m_buffer{},
+ m_pStream(&stream),
+ m_pos(0),
+ m_row(0),
+ m_col(0),
+ m_comment(false) {}
+
+ostream_wrapper::~ostream_wrapper() = default;
+
+void ostream_wrapper::write(const std::string& str) {
+ if (m_pStream) {
+ m_pStream->write(str.c_str(), str.size());
+ } else {
+ m_buffer.resize(std::max(m_buffer.size(), m_pos + str.size() + 1));
+ std::copy(str.begin(), str.end(), m_buffer.begin() + m_pos);
+ }
+
+ for (char ch : str) {
+ update_pos(ch);
+ }
+}
+
+void ostream_wrapper::write(const char* str, std::size_t size) {
+ if (m_pStream) {
+ m_pStream->write(str, size);
+ } else {
+ m_buffer.resize(std::max(m_buffer.size(), m_pos + size + 1));
+ std::copy(str, str + size, m_buffer.begin() + m_pos);
+ }
+
+ for (std::size_t i = 0; i < size; i++) {
+ update_pos(str[i]);
+ }
+}
+
+void ostream_wrapper::update_pos(char ch) {
+ m_pos++;
+ m_col++;
+
+ if (ch == '\n') {
+ m_row++;
+ m_col = 0;
+ m_comment = false;
+ }
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/parse.cpp b/src/libs/3rdparty/yaml-cpp/src/parse.cpp
new file mode 100644
index 0000000000..262536b85a
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/parse.cpp
@@ -0,0 +1,72 @@
+#include "yaml-cpp/node/parse.h"
+
+#include <fstream>
+#include <sstream>
+
+#include "nodebuilder.h"
+#include "yaml-cpp/node/impl.h"
+#include "yaml-cpp/node/node.h"
+#include "yaml-cpp/parser.h"
+
+namespace YAML {
+Node Load(const std::string& input) {
+ std::stringstream stream(input);
+ return Load(stream);
+}
+
+Node Load(const char* input) {
+ std::stringstream stream(input);
+ return Load(stream);
+}
+
+Node Load(std::istream& input) {
+ Parser parser(input);
+ NodeBuilder builder;
+ if (!parser.HandleNextDocument(builder)) {
+ return Node();
+ }
+
+ return builder.Root();
+}
+
+Node LoadFile(const std::string& filename) {
+ std::ifstream fin(filename);
+ if (!fin) {
+ throw BadFile(filename);
+ }
+ return Load(fin);
+}
+
+std::vector<Node> LoadAll(const std::string& input) {
+ std::stringstream stream(input);
+ return LoadAll(stream);
+}
+
+std::vector<Node> LoadAll(const char* input) {
+ std::stringstream stream(input);
+ return LoadAll(stream);
+}
+
+std::vector<Node> LoadAll(std::istream& input) {
+ std::vector<Node> docs;
+
+ Parser parser(input);
+ while (true) {
+ NodeBuilder builder;
+ if (!parser.HandleNextDocument(builder)) {
+ break;
+ }
+ docs.push_back(builder.Root());
+ }
+
+ return docs;
+}
+
+std::vector<Node> LoadAllFromFile(const std::string& filename) {
+ std::ifstream fin(filename);
+ if (!fin) {
+ throw BadFile(filename);
+ }
+ return LoadAll(fin);
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/parser.cpp b/src/libs/3rdparty/yaml-cpp/src/parser.cpp
new file mode 100644
index 0000000000..b8b78ebabc
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/parser.cpp
@@ -0,0 +1,119 @@
+#include <cstdio>
+#include <sstream>
+
+#include "directives.h" // IWYU pragma: keep
+#include "scanner.h" // IWYU pragma: keep
+#include "singledocparser.h"
+#include "token.h"
+#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
+#include "yaml-cpp/parser.h"
+
+namespace YAML {
+class EventHandler;
+
+Parser::Parser() : m_pScanner{}, m_pDirectives{} {}
+
+Parser::Parser(std::istream& in) : Parser() { Load(in); }
+
+Parser::~Parser() = default;
+
+Parser::operator bool() const { return m_pScanner && !m_pScanner->empty(); }
+
+void Parser::Load(std::istream& in) {
+ m_pScanner.reset(new Scanner(in));
+ m_pDirectives.reset(new Directives);
+}
+
+bool Parser::HandleNextDocument(EventHandler& eventHandler) {
+ if (!m_pScanner)
+ return false;
+
+ ParseDirectives();
+ if (m_pScanner->empty()) {
+ return false;
+ }
+
+ SingleDocParser sdp(*m_pScanner, *m_pDirectives);
+ sdp.HandleDocument(eventHandler);
+ return true;
+}
+
+void Parser::ParseDirectives() {
+ bool readDirective = false;
+
+ while (!m_pScanner->empty()) {
+ Token& token = m_pScanner->peek();
+ if (token.type != Token::DIRECTIVE) {
+ break;
+ }
+
+ // we keep the directives from the last document if none are specified;
+ // but if any directives are specific, then we reset them
+ if (!readDirective) {
+ m_pDirectives.reset(new Directives);
+ }
+
+ readDirective = true;
+ HandleDirective(token);
+ m_pScanner->pop();
+ }
+}
+
+void Parser::HandleDirective(const Token& token) {
+ if (token.value == "YAML") {
+ HandleYamlDirective(token);
+ } else if (token.value == "TAG") {
+ HandleTagDirective(token);
+ }
+}
+
+void Parser::HandleYamlDirective(const Token& token) {
+ if (token.params.size() != 1) {
+ throw ParserException(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS);
+ }
+
+ if (!m_pDirectives->version.isDefault) {
+ throw ParserException(token.mark, ErrorMsg::REPEATED_YAML_DIRECTIVE);
+ }
+
+ std::stringstream str(token.params[0]);
+ str >> m_pDirectives->version.major;
+ str.get();
+ str >> m_pDirectives->version.minor;
+ if (!str || str.peek() != EOF) {
+ throw ParserException(
+ token.mark, std::string(ErrorMsg::YAML_VERSION) + token.params[0]);
+ }
+
+ if (m_pDirectives->version.major > 1) {
+ throw ParserException(token.mark, ErrorMsg::YAML_MAJOR_VERSION);
+ }
+
+ m_pDirectives->version.isDefault = false;
+ // TODO: warning on major == 1, minor > 2?
+}
+
+void Parser::HandleTagDirective(const Token& token) {
+ if (token.params.size() != 2)
+ throw ParserException(token.mark, ErrorMsg::TAG_DIRECTIVE_ARGS);
+
+ const std::string& handle = token.params[0];
+ const std::string& prefix = token.params[1];
+ if (m_pDirectives->tags.find(handle) != m_pDirectives->tags.end()) {
+ throw ParserException(token.mark, ErrorMsg::REPEATED_TAG_DIRECTIVE);
+ }
+
+ m_pDirectives->tags[handle] = prefix;
+}
+
+void Parser::PrintTokens(std::ostream& out) {
+ if (!m_pScanner) {
+ return;
+ }
+
+ while (!m_pScanner->empty()) {
+ out << m_pScanner->peek() << "\n";
+ m_pScanner->pop();
+ }
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/ptr_vector.h b/src/libs/3rdparty/yaml-cpp/src/ptr_vector.h
new file mode 100644
index 0000000000..d58de04cb6
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/ptr_vector.h
@@ -0,0 +1,45 @@
+#ifndef PTR_VECTOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define PTR_VECTOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <cstddef>
+#include <cstdlib>
+#include <memory>
+#include <vector>
+
+namespace YAML {
+
+// TODO: This class is no longer needed
+template <typename T>
+class ptr_vector {
+ public:
+ ptr_vector() : m_data{} {}
+ ptr_vector(const ptr_vector&) = delete;
+ ptr_vector(ptr_vector&&) = default;
+ ptr_vector& operator=(const ptr_vector&) = delete;
+ ptr_vector& operator=(ptr_vector&&) = default;
+
+ void clear() { m_data.clear(); }
+
+ std::size_t size() const { return m_data.size(); }
+ bool empty() const { return m_data.empty(); }
+
+ void push_back(std::unique_ptr<T>&& t) { m_data.push_back(std::move(t)); }
+ T& operator[](std::size_t i) { return *m_data[i]; }
+ const T& operator[](std::size_t i) const { return *m_data[i]; }
+
+ T& back() { return *(m_data.back().get()); }
+
+ const T& back() const { return *(m_data.back().get()); }
+
+ private:
+ std::vector<std::unique_ptr<T>> m_data;
+};
+} // namespace YAML
+
+#endif // PTR_VECTOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/regex_yaml.cpp b/src/libs/3rdparty/yaml-cpp/src/regex_yaml.cpp
new file mode 100644
index 0000000000..bf1784b41d
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/regex_yaml.cpp
@@ -0,0 +1,43 @@
+#include "regex_yaml.h"
+
+namespace YAML {
+// constructors
+
+RegEx::RegEx(REGEX_OP op) : m_op(op), m_a(0), m_z(0), m_params{} {}
+RegEx::RegEx() : RegEx(REGEX_EMPTY) {}
+
+RegEx::RegEx(char ch) : m_op(REGEX_MATCH), m_a(ch), m_z(0), m_params{} {}
+
+RegEx::RegEx(char a, char z) : m_op(REGEX_RANGE), m_a(a), m_z(z), m_params{} {}
+
+RegEx::RegEx(const std::string& str, REGEX_OP op)
+ : m_op(op), m_a(0), m_z(0), m_params(str.begin(), str.end()) {}
+
+// combination constructors
+RegEx operator!(const RegEx& ex) {
+ RegEx ret(REGEX_NOT);
+ ret.m_params.push_back(ex);
+ return ret;
+}
+
+RegEx operator|(const RegEx& ex1, const RegEx& ex2) {
+ RegEx ret(REGEX_OR);
+ ret.m_params.push_back(ex1);
+ ret.m_params.push_back(ex2);
+ return ret;
+}
+
+RegEx operator&(const RegEx& ex1, const RegEx& ex2) {
+ RegEx ret(REGEX_AND);
+ ret.m_params.push_back(ex1);
+ ret.m_params.push_back(ex2);
+ return ret;
+}
+
+RegEx operator+(const RegEx& ex1, const RegEx& ex2) {
+ RegEx ret(REGEX_SEQ);
+ ret.m_params.push_back(ex1);
+ ret.m_params.push_back(ex2);
+ return ret;
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/regex_yaml.h b/src/libs/3rdparty/yaml-cpp/src/regex_yaml.h
new file mode 100644
index 0000000000..c70ab60dcc
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/regex_yaml.h
@@ -0,0 +1,88 @@
+#ifndef REGEX_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define REGEX_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <string>
+#include <vector>
+
+#include "yaml-cpp/dll.h"
+
+namespace YAML {
+class Stream;
+
+enum REGEX_OP {
+ REGEX_EMPTY,
+ REGEX_MATCH,
+ REGEX_RANGE,
+ REGEX_OR,
+ REGEX_AND,
+ REGEX_NOT,
+ REGEX_SEQ
+};
+
+// simplified regular expressions
+// . Only straightforward matches (no repeated characters)
+// . Only matches from start of string
+class YAML_CPP_API RegEx {
+ public:
+ RegEx();
+ explicit RegEx(char ch);
+ RegEx(char a, char z);
+ RegEx(const std::string& str, REGEX_OP op = REGEX_SEQ);
+ ~RegEx() = default;
+
+ friend YAML_CPP_API RegEx operator!(const RegEx& ex);
+ friend YAML_CPP_API RegEx operator|(const RegEx& ex1, const RegEx& ex2);
+ friend YAML_CPP_API RegEx operator&(const RegEx& ex1, const RegEx& ex2);
+ friend YAML_CPP_API RegEx operator+(const RegEx& ex1, const RegEx& ex2);
+
+ bool Matches(char ch) const;
+ bool Matches(const std::string& str) const;
+ bool Matches(const Stream& in) const;
+ template <typename Source>
+ bool Matches(const Source& source) const;
+
+ int Match(const std::string& str) const;
+ int Match(const Stream& in) const;
+ template <typename Source>
+ int Match(const Source& source) const;
+
+ private:
+ explicit RegEx(REGEX_OP op);
+
+ template <typename Source>
+ bool IsValidSource(const Source& source) const;
+ template <typename Source>
+ int MatchUnchecked(const Source& source) const;
+
+ template <typename Source>
+ int MatchOpEmpty(const Source& source) const;
+ template <typename Source>
+ int MatchOpMatch(const Source& source) const;
+ template <typename Source>
+ int MatchOpRange(const Source& source) const;
+ template <typename Source>
+ int MatchOpOr(const Source& source) const;
+ template <typename Source>
+ int MatchOpAnd(const Source& source) const;
+ template <typename Source>
+ int MatchOpNot(const Source& source) const;
+ template <typename Source>
+ int MatchOpSeq(const Source& source) const;
+
+ private:
+ REGEX_OP m_op;
+ char m_a{};
+ char m_z{};
+ std::vector<RegEx> m_params;
+};
+} // namespace YAML
+
+#include "regeximpl.h"
+
+#endif // REGEX_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/regeximpl.h b/src/libs/3rdparty/yaml-cpp/src/regeximpl.h
new file mode 100644
index 0000000000..a742cdc305
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/regeximpl.h
@@ -0,0 +1,185 @@
+#ifndef REGEXIMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define REGEXIMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "stream.h"
+#include "streamcharsource.h"
+#include "stringsource.h"
+
+namespace YAML {
+// query matches
+inline bool RegEx::Matches(char ch) const {
+ std::string str;
+ str += ch;
+ return Matches(str);
+}
+
+inline bool RegEx::Matches(const std::string& str) const {
+ return Match(str) >= 0;
+}
+
+inline bool RegEx::Matches(const Stream& in) const { return Match(in) >= 0; }
+
+template <typename Source>
+inline bool RegEx::Matches(const Source& source) const {
+ return Match(source) >= 0;
+}
+
+// Match
+// . Matches the given string against this regular expression.
+// . Returns the number of characters matched.
+// . Returns -1 if no characters were matched (the reason for
+// not returning zero is that we may have an empty regex
+// which is ALWAYS successful at matching zero characters).
+// . REMEMBER that we only match from the start of the buffer!
+inline int RegEx::Match(const std::string& str) const {
+ StringCharSource source(str.c_str(), str.size());
+ return Match(source);
+}
+
+inline int RegEx::Match(const Stream& in) const {
+ StreamCharSource source(in);
+ return Match(source);
+}
+
+template <typename Source>
+inline bool RegEx::IsValidSource(const Source& source) const {
+ return source;
+}
+
+template <>
+inline bool RegEx::IsValidSource<StringCharSource>(
+ const StringCharSource& source) const {
+ switch (m_op) {
+ case REGEX_MATCH:
+ case REGEX_RANGE:
+ return source;
+ default:
+ return true;
+ }
+}
+
+template <typename Source>
+inline int RegEx::Match(const Source& source) const {
+ return IsValidSource(source) ? MatchUnchecked(source) : -1;
+}
+
+template <typename Source>
+inline int RegEx::MatchUnchecked(const Source& source) const {
+ switch (m_op) {
+ case REGEX_EMPTY:
+ return MatchOpEmpty(source);
+ case REGEX_MATCH:
+ return MatchOpMatch(source);
+ case REGEX_RANGE:
+ return MatchOpRange(source);
+ case REGEX_OR:
+ return MatchOpOr(source);
+ case REGEX_AND:
+ return MatchOpAnd(source);
+ case REGEX_NOT:
+ return MatchOpNot(source);
+ case REGEX_SEQ:
+ return MatchOpSeq(source);
+ }
+
+ return -1;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Operators
+// Note: the convention MatchOp*<Source> is that we can assume
+// IsSourceValid(source).
+// So we do all our checks *before* we call these functions
+
+// EmptyOperator
+template <typename Source>
+inline int RegEx::MatchOpEmpty(const Source& source) const {
+ return source[0] == Stream::eof() ? 0 : -1;
+}
+
+template <>
+inline int RegEx::MatchOpEmpty<StringCharSource>(
+ const StringCharSource& source) const {
+ return !source ? 0 : -1; // the empty regex only is successful on the empty
+ // string
+}
+
+// MatchOperator
+template <typename Source>
+inline int RegEx::MatchOpMatch(const Source& source) const {
+ if (source[0] != m_a)
+ return -1;
+ return 1;
+}
+
+// RangeOperator
+template <typename Source>
+inline int RegEx::MatchOpRange(const Source& source) const {
+ if (m_a > source[0] || m_z < source[0])
+ return -1;
+ return 1;
+}
+
+// OrOperator
+template <typename Source>
+inline int RegEx::MatchOpOr(const Source& source) const {
+ for (const RegEx& param : m_params) {
+ int n = param.MatchUnchecked(source);
+ if (n >= 0)
+ return n;
+ }
+ return -1;
+}
+
+// AndOperator
+// Note: 'AND' is a little funny, since we may be required to match things
+// of different lengths. If we find a match, we return the length of
+// the FIRST entry on the list.
+template <typename Source>
+inline int RegEx::MatchOpAnd(const Source& source) const {
+ int first = -1;
+ for (std::size_t i = 0; i < m_params.size(); i++) {
+ int n = m_params[i].MatchUnchecked(source);
+ if (n == -1)
+ return -1;
+ if (i == 0)
+ first = n;
+ }
+ return first;
+}
+
+// NotOperator
+template <typename Source>
+inline int RegEx::MatchOpNot(const Source& source) const {
+ if (m_params.empty())
+ return -1;
+ if (m_params[0].MatchUnchecked(source) >= 0)
+ return -1;
+ return 1;
+}
+
+// SeqOperator
+template <typename Source>
+inline int RegEx::MatchOpSeq(const Source& source) const {
+ int offset = 0;
+ for (const RegEx& param : m_params) {
+ int n = param.Match(source + offset); // note Match, not
+ // MatchUnchecked because we
+ // need to check validity after
+ // the offset
+ if (n == -1)
+ return -1;
+ offset += n;
+ }
+
+ return offset;
+}
+} // namespace YAML
+
+#endif // REGEXIMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/scanner.cpp b/src/libs/3rdparty/yaml-cpp/src/scanner.cpp
new file mode 100644
index 0000000000..ea5511a114
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/scanner.cpp
@@ -0,0 +1,391 @@
+#include <cassert>
+#include <memory>
+
+#include "exp.h"
+#include "scanner.h"
+#include "token.h"
+#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
+
+namespace YAML {
+Scanner::Scanner(std::istream& in)
+ : INPUT(in),
+ m_tokens{},
+ m_startedStream(false),
+ m_endedStream(false),
+ m_simpleKeyAllowed(false),
+ m_canBeJSONFlow(false),
+ m_simpleKeys{},
+ m_indents{},
+ m_indentRefs{},
+ m_flows{} {}
+
+Scanner::~Scanner() = default;
+
+bool Scanner::empty() {
+ EnsureTokensInQueue();
+ return m_tokens.empty();
+}
+
+void Scanner::pop() {
+ EnsureTokensInQueue();
+ if (!m_tokens.empty())
+ m_tokens.pop();
+}
+
+Token& Scanner::peek() {
+ EnsureTokensInQueue();
+ assert(!m_tokens.empty()); // should we be asserting here? I mean, we really
+ // just be checking
+ // if it's empty before peeking.
+
+#if 0
+ static Token *pLast = 0;
+ if(pLast != &m_tokens.front())
+ std::cerr << "peek: " << m_tokens.front() << "\n";
+ pLast = &m_tokens.front();
+#endif
+
+ return m_tokens.front();
+}
+
+Mark Scanner::mark() const { return INPUT.mark(); }
+
+void Scanner::EnsureTokensInQueue() {
+ while (true) {
+ if (!m_tokens.empty()) {
+ Token& token = m_tokens.front();
+
+ // if this guy's valid, then we're done
+ if (token.status == Token::VALID) {
+ return;
+ }
+
+ // here's where we clean up the impossible tokens
+ if (token.status == Token::INVALID) {
+ m_tokens.pop();
+ continue;
+ }
+
+ // note: what's left are the unverified tokens
+ }
+
+ // no token? maybe we've actually finished
+ if (m_endedStream) {
+ return;
+ }
+
+ // no? then scan...
+ ScanNextToken();
+ }
+}
+
+void Scanner::ScanNextToken() {
+ if (m_endedStream) {
+ return;
+ }
+
+ if (!m_startedStream) {
+ return StartStream();
+ }
+
+ // get rid of whitespace, etc. (in between tokens it should be irrelevant)
+ ScanToNextToken();
+
+ // maybe need to end some blocks
+ PopIndentToHere();
+
+ // *****
+ // And now branch based on the next few characters!
+ // *****
+
+ // end of stream
+ if (!INPUT) {
+ return EndStream();
+ }
+
+ if (INPUT.column() == 0 && INPUT.peek() == Keys::Directive) {
+ return ScanDirective();
+ }
+
+ // document token
+ if (INPUT.column() == 0 && Exp::DocStart().Matches(INPUT)) {
+ return ScanDocStart();
+ }
+
+ if (INPUT.column() == 0 && Exp::DocEnd().Matches(INPUT)) {
+ return ScanDocEnd();
+ }
+
+ // flow start/end/entry
+ if (INPUT.peek() == Keys::FlowSeqStart ||
+ INPUT.peek() == Keys::FlowMapStart) {
+ return ScanFlowStart();
+ }
+
+ if (INPUT.peek() == Keys::FlowSeqEnd || INPUT.peek() == Keys::FlowMapEnd) {
+ return ScanFlowEnd();
+ }
+
+ if (INPUT.peek() == Keys::FlowEntry) {
+ return ScanFlowEntry();
+ }
+
+ // block/map stuff
+ if (Exp::BlockEntry().Matches(INPUT)) {
+ return ScanBlockEntry();
+ }
+
+ if ((InBlockContext() ? Exp::Key() : Exp::KeyInFlow()).Matches(INPUT)) {
+ return ScanKey();
+ }
+
+ if (GetValueRegex().Matches(INPUT)) {
+ return ScanValue();
+ }
+
+ // alias/anchor
+ if (INPUT.peek() == Keys::Alias || INPUT.peek() == Keys::Anchor) {
+ return ScanAnchorOrAlias();
+ }
+
+ // tag
+ if (INPUT.peek() == Keys::Tag) {
+ return ScanTag();
+ }
+
+ // special scalars
+ if (InBlockContext() && (INPUT.peek() == Keys::LiteralScalar ||
+ INPUT.peek() == Keys::FoldedScalar)) {
+ return ScanBlockScalar();
+ }
+
+ if (INPUT.peek() == '\'' || INPUT.peek() == '\"') {
+ return ScanQuotedScalar();
+ }
+
+ // plain scalars
+ if ((InBlockContext() ? Exp::PlainScalar() : Exp::PlainScalarInFlow())
+ .Matches(INPUT)) {
+ return ScanPlainScalar();
+ }
+
+ // don't know what it is!
+ throw ParserException(INPUT.mark(), ErrorMsg::UNKNOWN_TOKEN);
+}
+
+void Scanner::ScanToNextToken() {
+ while (true) {
+ // first eat whitespace
+ while (INPUT && IsWhitespaceToBeEaten(INPUT.peek())) {
+ if (InBlockContext() && Exp::Tab().Matches(INPUT)) {
+ m_simpleKeyAllowed = false;
+ }
+ INPUT.eat(1);
+ }
+
+ // then eat a comment
+ if (Exp::Comment().Matches(INPUT)) {
+ // eat until line break
+ while (INPUT && !Exp::Break().Matches(INPUT)) {
+ INPUT.eat(1);
+ }
+ }
+
+ // if it's NOT a line break, then we're done!
+ if (!Exp::Break().Matches(INPUT)) {
+ break;
+ }
+
+ // otherwise, let's eat the line break and keep going
+ int n = Exp::Break().Match(INPUT);
+ INPUT.eat(n);
+
+ // oh yeah, and let's get rid of that simple key
+ InvalidateSimpleKey();
+
+ // new line - we may be able to accept a simple key now
+ if (InBlockContext()) {
+ m_simpleKeyAllowed = true;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////
+// Misc. helpers
+
+// IsWhitespaceToBeEaten
+// . We can eat whitespace if it's a space or tab
+// . Note: originally tabs in block context couldn't be eaten
+// "where a simple key could be allowed
+// (i.e., not at the beginning of a line, or following '-', '?', or
+// ':')"
+// I think this is wrong, since tabs can be non-content whitespace; it's just
+// that they can't contribute to indentation, so once you've seen a tab in a
+// line, you can't start a simple key
+bool Scanner::IsWhitespaceToBeEaten(char ch) {
+ if (ch == ' ') {
+ return true;
+ }
+
+ if (ch == '\t') {
+ return true;
+ }
+
+ return false;
+}
+
+const RegEx& Scanner::GetValueRegex() const {
+ if (InBlockContext()) {
+ return Exp::Value();
+ }
+
+ return m_canBeJSONFlow ? Exp::ValueInJSONFlow() : Exp::ValueInFlow();
+}
+
+void Scanner::StartStream() {
+ m_startedStream = true;
+ m_simpleKeyAllowed = true;
+ std::unique_ptr<IndentMarker> pIndent(
+ new IndentMarker(-1, IndentMarker::NONE));
+ m_indentRefs.push_back(std::move(pIndent));
+ m_indents.push(&m_indentRefs.back());
+}
+
+void Scanner::EndStream() {
+ // force newline
+ if (INPUT.column() > 0) {
+ INPUT.ResetColumn();
+ }
+
+ PopAllIndents();
+ PopAllSimpleKeys();
+
+ m_simpleKeyAllowed = false;
+ m_endedStream = true;
+}
+
+Token* Scanner::PushToken(Token::TYPE type) {
+ m_tokens.push(Token(type, INPUT.mark()));
+ return &m_tokens.back();
+}
+
+Token::TYPE Scanner::GetStartTokenFor(IndentMarker::INDENT_TYPE type) const {
+ switch (type) {
+ case IndentMarker::SEQ:
+ return Token::BLOCK_SEQ_START;
+ case IndentMarker::MAP:
+ return Token::BLOCK_MAP_START;
+ case IndentMarker::NONE:
+ assert(false);
+ break;
+ }
+ assert(false);
+ throw std::runtime_error("yaml-cpp: internal error, invalid indent type");
+}
+
+Scanner::IndentMarker* Scanner::PushIndentTo(int column,
+ IndentMarker::INDENT_TYPE type) {
+ // are we in flow?
+ if (InFlowContext()) {
+ return nullptr;
+ }
+
+ std::unique_ptr<IndentMarker> pIndent(new IndentMarker(column, type));
+ IndentMarker& indent = *pIndent;
+ const IndentMarker& lastIndent = *m_indents.top();
+
+ // is this actually an indentation?
+ if (indent.column < lastIndent.column) {
+ return nullptr;
+ }
+ if (indent.column == lastIndent.column &&
+ !(indent.type == IndentMarker::SEQ &&
+ lastIndent.type == IndentMarker::MAP)) {
+ return nullptr;
+ }
+
+ // push a start token
+ indent.pStartToken = PushToken(GetStartTokenFor(type));
+
+ // and then the indent
+ m_indents.push(&indent);
+ m_indentRefs.push_back(std::move(pIndent));
+ return &m_indentRefs.back();
+}
+
+void Scanner::PopIndentToHere() {
+ // are we in flow?
+ if (InFlowContext()) {
+ return;
+ }
+
+ // now pop away
+ while (!m_indents.empty()) {
+ const IndentMarker& indent = *m_indents.top();
+ if (indent.column < INPUT.column()) {
+ break;
+ }
+ if (indent.column == INPUT.column() &&
+ !(indent.type == IndentMarker::SEQ &&
+ !Exp::BlockEntry().Matches(INPUT))) {
+ break;
+ }
+
+ PopIndent();
+ }
+
+ while (!m_indents.empty() &&
+ m_indents.top()->status == IndentMarker::INVALID) {
+ PopIndent();
+ }
+}
+
+void Scanner::PopAllIndents() {
+ // are we in flow?
+ if (InFlowContext()) {
+ return;
+ }
+
+ // now pop away
+ while (!m_indents.empty()) {
+ const IndentMarker& indent = *m_indents.top();
+ if (indent.type == IndentMarker::NONE) {
+ break;
+ }
+
+ PopIndent();
+ }
+}
+
+void Scanner::PopIndent() {
+ const IndentMarker& indent = *m_indents.top();
+ m_indents.pop();
+
+ if (indent.status != IndentMarker::VALID) {
+ InvalidateSimpleKey();
+ return;
+ }
+
+ if (indent.type == IndentMarker::SEQ) {
+ m_tokens.push(Token(Token::BLOCK_SEQ_END, INPUT.mark()));
+ } else if (indent.type == IndentMarker::MAP) {
+ m_tokens.push(Token(Token::BLOCK_MAP_END, INPUT.mark()));
+ }
+}
+
+int Scanner::GetTopIndent() const {
+ if (m_indents.empty()) {
+ return 0;
+ }
+ return m_indents.top()->column;
+}
+
+void Scanner::ThrowParserException(const std::string& msg) const {
+ Mark mark = Mark::null_mark();
+ if (!m_tokens.empty()) {
+ const Token& token = m_tokens.front();
+ mark = token.mark;
+ }
+ throw ParserException(mark, msg);
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/scanner.h b/src/libs/3rdparty/yaml-cpp/src/scanner.h
new file mode 100644
index 0000000000..4af938e69c
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/scanner.h
@@ -0,0 +1,188 @@
+#ifndef SCANNER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define SCANNER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <cstddef>
+#include <ios>
+#include <queue>
+#include <stack>
+#include <string>
+
+#include "ptr_vector.h"
+#include "stream.h"
+#include "token.h"
+#include "yaml-cpp/mark.h"
+
+namespace YAML {
+class Node;
+class RegEx;
+
+/**
+ * A scanner transforms a stream of characters into a stream of tokens.
+ */
+class Scanner {
+ public:
+ explicit Scanner(std::istream &in);
+ ~Scanner();
+
+ /** Returns true if there are no more tokens to be read. */
+ bool empty();
+
+ /** Removes the next token in the queue. */
+ void pop();
+
+ /** Returns, but does not remove, the next token in the queue. */
+ Token &peek();
+
+ /** Returns the current mark in the input stream. */
+ Mark mark() const;
+
+ private:
+ struct IndentMarker {
+ enum INDENT_TYPE { MAP, SEQ, NONE };
+ enum STATUS { VALID, INVALID, UNKNOWN };
+ IndentMarker(int column_, INDENT_TYPE type_)
+ : column(column_), type(type_), status(VALID), pStartToken(nullptr) {}
+
+ int column;
+ INDENT_TYPE type;
+ STATUS status;
+ Token *pStartToken;
+ };
+
+ enum FLOW_MARKER { FLOW_MAP, FLOW_SEQ };
+
+ private:
+ // scanning
+
+ /**
+ * Scans until there's a valid token at the front of the queue, or the queue
+ * is empty. The state can be checked by {@link #empty}, and the next token
+ * retrieved by {@link #peek}.
+ */
+ void EnsureTokensInQueue();
+
+ /**
+ * The main scanning function; this method branches out to scan whatever the
+ * next token should be.
+ */
+ void ScanNextToken();
+
+ /** Eats the input stream until it reaches the next token-like thing. */
+ void ScanToNextToken();
+
+ /** Sets the initial conditions for starting a stream. */
+ void StartStream();
+
+ /** Closes out the stream, finish up, etc. */
+ void EndStream();
+
+ Token *PushToken(Token::TYPE type);
+
+ bool InFlowContext() const { return !m_flows.empty(); }
+ bool InBlockContext() const { return m_flows.empty(); }
+ std::size_t GetFlowLevel() const { return m_flows.size(); }
+
+ Token::TYPE GetStartTokenFor(IndentMarker::INDENT_TYPE type) const;
+
+ /**
+ * Pushes an indentation onto the stack, and enqueues the proper token
+ * (sequence start or mapping start).
+ *
+ * @return the indent marker it generates (if any).
+ */
+ IndentMarker *PushIndentTo(int column, IndentMarker::INDENT_TYPE type);
+
+ /**
+ * Pops indentations off the stack until it reaches the current indentation
+ * level, and enqueues the proper token each time. Then pops all invalid
+ * indentations off.
+ */
+ void PopIndentToHere();
+
+ /**
+ * Pops all indentations (except for the base empty one) off the stack, and
+ * enqueues the proper token each time.
+ */
+ void PopAllIndents();
+
+ /** Pops a single indent, pushing the proper token. */
+ void PopIndent();
+ int GetTopIndent() const;
+
+ // checking input
+ bool CanInsertPotentialSimpleKey() const;
+ bool ExistsActiveSimpleKey() const;
+ void InsertPotentialSimpleKey();
+ void InvalidateSimpleKey();
+ bool VerifySimpleKey();
+ void PopAllSimpleKeys();
+
+ /**
+ * Throws a ParserException with the current token location (if available),
+ * and does not parse any more tokens.
+ */
+ void ThrowParserException(const std::string &msg) const;
+
+ bool IsWhitespaceToBeEaten(char ch);
+
+ /**
+ * Returns the appropriate regex to check if the next token is a value token.
+ */
+ const RegEx &GetValueRegex() const;
+
+ struct SimpleKey {
+ SimpleKey(const Mark &mark_, std::size_t flowLevel_);
+
+ void Validate();
+ void Invalidate();
+
+ Mark mark;
+ std::size_t flowLevel;
+ IndentMarker *pIndent;
+ Token *pMapStart, *pKey;
+ };
+
+ // and the tokens
+ void ScanDirective();
+ void ScanDocStart();
+ void ScanDocEnd();
+ void ScanBlockSeqStart();
+ void ScanBlockMapSTart();
+ void ScanBlockEnd();
+ void ScanBlockEntry();
+ void ScanFlowStart();
+ void ScanFlowEnd();
+ void ScanFlowEntry();
+ void ScanKey();
+ void ScanValue();
+ void ScanAnchorOrAlias();
+ void ScanTag();
+ void ScanPlainScalar();
+ void ScanQuotedScalar();
+ void ScanBlockScalar();
+
+ private:
+ // the stream
+ Stream INPUT;
+
+ // the output (tokens)
+ std::queue<Token> m_tokens;
+
+ // state info
+ bool m_startedStream, m_endedStream;
+ bool m_simpleKeyAllowed;
+ bool m_canBeJSONFlow;
+ std::stack<SimpleKey> m_simpleKeys;
+ std::stack<IndentMarker *> m_indents;
+ ptr_vector<IndentMarker> m_indentRefs; // for "garbage collection"
+ std::stack<FLOW_MARKER> m_flows;
+};
+}
+
+#endif // SCANNER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/scanscalar.cpp b/src/libs/3rdparty/yaml-cpp/src/scanscalar.cpp
new file mode 100644
index 0000000000..be57b1cd5d
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/scanscalar.cpp
@@ -0,0 +1,251 @@
+#include "scanscalar.h"
+
+#include <algorithm>
+
+#include "exp.h"
+#include "regeximpl.h"
+#include "stream.h"
+#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
+
+namespace YAML {
+// ScanScalar
+// . This is where the scalar magic happens.
+//
+// . We do the scanning in three phases:
+// 1. Scan until newline
+// 2. Eat newline
+// 3. Scan leading blanks.
+//
+// . Depending on the parameters given, we store or stop
+// and different places in the above flow.
+std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) {
+ bool foundNonEmptyLine = false;
+ bool pastOpeningBreak = (params.fold == FOLD_FLOW);
+ bool emptyLine = false, moreIndented = false;
+ int foldedNewlineCount = 0;
+ bool foldedNewlineStartedMoreIndented = false;
+ std::size_t lastEscapedChar = std::string::npos;
+ std::string scalar;
+ params.leadingSpaces = false;
+
+ if (!params.end) {
+ params.end = &Exp::Empty();
+ }
+
+ while (INPUT) {
+ // ********************************
+ // Phase #1: scan until line ending
+
+ std::size_t lastNonWhitespaceChar = scalar.size();
+ bool escapedNewline = false;
+ while (!params.end->Matches(INPUT) && !Exp::Break().Matches(INPUT)) {
+ if (!INPUT) {
+ break;
+ }
+
+ // document indicator?
+ if (INPUT.column() == 0 && Exp::DocIndicator().Matches(INPUT)) {
+ if (params.onDocIndicator == BREAK) {
+ break;
+ }
+ if (params.onDocIndicator == THROW) {
+ throw ParserException(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR);
+ }
+ }
+
+ foundNonEmptyLine = true;
+ pastOpeningBreak = true;
+
+ // escaped newline? (only if we're escaping on slash)
+ if (params.escape == '\\' && Exp::EscBreak().Matches(INPUT)) {
+ // eat escape character and get out (but preserve trailing whitespace!)
+ INPUT.get();
+ lastNonWhitespaceChar = scalar.size();
+ lastEscapedChar = scalar.size();
+ escapedNewline = true;
+ break;
+ }
+
+ // escape this?
+ if (INPUT.peek() == params.escape) {
+ scalar += Exp::Escape(INPUT);
+ lastNonWhitespaceChar = scalar.size();
+ lastEscapedChar = scalar.size();
+ continue;
+ }
+
+ // otherwise, just add the damn character
+ char ch = INPUT.get();
+ scalar += ch;
+ if (ch != ' ' && ch != '\t') {
+ lastNonWhitespaceChar = scalar.size();
+ }
+ }
+
+ // eof? if we're looking to eat something, then we throw
+ if (!INPUT) {
+ if (params.eatEnd) {
+ throw ParserException(INPUT.mark(), ErrorMsg::EOF_IN_SCALAR);
+ }
+ break;
+ }
+
+ // doc indicator?
+ if (params.onDocIndicator == BREAK && INPUT.column() == 0 &&
+ Exp::DocIndicator().Matches(INPUT)) {
+ break;
+ }
+
+ // are we done via character match?
+ int n = params.end->Match(INPUT);
+ if (n >= 0) {
+ if (params.eatEnd) {
+ INPUT.eat(n);
+ }
+ break;
+ }
+
+ // do we remove trailing whitespace?
+ if (params.fold == FOLD_FLOW)
+ scalar.erase(lastNonWhitespaceChar);
+
+ // ********************************
+ // Phase #2: eat line ending
+ n = Exp::Break().Match(INPUT);
+ INPUT.eat(n);
+
+ // ********************************
+ // Phase #3: scan initial spaces
+
+ // first the required indentation
+ while (INPUT.peek() == ' ' &&
+ (INPUT.column() < params.indent ||
+ (params.detectIndent && !foundNonEmptyLine)) &&
+ !params.end->Matches(INPUT)) {
+ INPUT.eat(1);
+ }
+
+ // update indent if we're auto-detecting
+ if (params.detectIndent && !foundNonEmptyLine) {
+ params.indent = std::max(params.indent, INPUT.column());
+ }
+
+ // and then the rest of the whitespace
+ while (Exp::Blank().Matches(INPUT)) {
+ // we check for tabs that masquerade as indentation
+ if (INPUT.peek() == '\t' && INPUT.column() < params.indent &&
+ params.onTabInIndentation == THROW) {
+ throw ParserException(INPUT.mark(), ErrorMsg::TAB_IN_INDENTATION);
+ }
+
+ if (!params.eatLeadingWhitespace) {
+ break;
+ }
+
+ if (params.end->Matches(INPUT)) {
+ break;
+ }
+
+ INPUT.eat(1);
+ }
+
+ // was this an empty line?
+ bool nextEmptyLine = Exp::Break().Matches(INPUT);
+ bool nextMoreIndented = Exp::Blank().Matches(INPUT);
+ if (params.fold == FOLD_BLOCK && foldedNewlineCount == 0 && nextEmptyLine)
+ foldedNewlineStartedMoreIndented = moreIndented;
+
+ // for block scalars, we always start with a newline, so we should ignore it
+ // (not fold or keep)
+ if (pastOpeningBreak) {
+ switch (params.fold) {
+ case DONT_FOLD:
+ scalar += "\n";
+ break;
+ case FOLD_BLOCK:
+ if (!emptyLine && !nextEmptyLine && !moreIndented &&
+ !nextMoreIndented && INPUT.column() >= params.indent) {
+ scalar += " ";
+ } else if (nextEmptyLine) {
+ foldedNewlineCount++;
+ } else {
+ scalar += "\n";
+ }
+
+ if (!nextEmptyLine && foldedNewlineCount > 0) {
+ scalar += std::string(foldedNewlineCount - 1, '\n');
+ if (foldedNewlineStartedMoreIndented ||
+ nextMoreIndented | !foundNonEmptyLine) {
+ scalar += "\n";
+ }
+ foldedNewlineCount = 0;
+ }
+ break;
+ case FOLD_FLOW:
+ if (nextEmptyLine) {
+ scalar += "\n";
+ } else if (!emptyLine && !escapedNewline) {
+ scalar += " ";
+ }
+ break;
+ }
+ }
+
+ emptyLine = nextEmptyLine;
+ moreIndented = nextMoreIndented;
+ pastOpeningBreak = true;
+
+ // are we done via indentation?
+ if (!emptyLine && INPUT.column() < params.indent) {
+ params.leadingSpaces = true;
+ break;
+ }
+ }
+
+ // post-processing
+ if (params.trimTrailingSpaces) {
+ std::size_t pos = scalar.find_last_not_of(" \t");
+ if (lastEscapedChar != std::string::npos) {
+ if (pos < lastEscapedChar || pos == std::string::npos) {
+ pos = lastEscapedChar;
+ }
+ }
+ if (pos < scalar.size()) {
+ scalar.erase(pos + 1);
+ }
+ }
+
+ switch (params.chomp) {
+ case CLIP: {
+ std::size_t pos = scalar.find_last_not_of('\n');
+ if (lastEscapedChar != std::string::npos) {
+ if (pos < lastEscapedChar || pos == std::string::npos) {
+ pos = lastEscapedChar;
+ }
+ }
+ if (pos == std::string::npos) {
+ scalar.erase();
+ } else if (pos + 1 < scalar.size()) {
+ scalar.erase(pos + 2);
+ }
+ } break;
+ case STRIP: {
+ std::size_t pos = scalar.find_last_not_of('\n');
+ if (lastEscapedChar != std::string::npos) {
+ if (pos < lastEscapedChar || pos == std::string::npos) {
+ pos = lastEscapedChar;
+ }
+ }
+ if (pos == std::string::npos) {
+ scalar.erase();
+ } else if (pos < scalar.size()) {
+ scalar.erase(pos + 1);
+ }
+ } break;
+ default:
+ break;
+ }
+
+ return scalar;
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/scanscalar.h b/src/libs/3rdparty/yaml-cpp/src/scanscalar.h
new file mode 100644
index 0000000000..296b885a51
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/scanscalar.h
@@ -0,0 +1,63 @@
+#ifndef SCANSCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define SCANSCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <string>
+
+#include "regex_yaml.h"
+#include "stream.h"
+
+namespace YAML {
+enum CHOMP { STRIP = -1, CLIP, KEEP };
+enum ACTION { NONE, BREAK, THROW };
+enum FOLD { DONT_FOLD, FOLD_BLOCK, FOLD_FLOW };
+
+struct ScanScalarParams {
+ ScanScalarParams()
+ : end(nullptr),
+ eatEnd(false),
+ indent(0),
+ detectIndent(false),
+ eatLeadingWhitespace(0),
+ escape(0),
+ fold(DONT_FOLD),
+ trimTrailingSpaces(0),
+ chomp(CLIP),
+ onDocIndicator(NONE),
+ onTabInIndentation(NONE),
+ leadingSpaces(false) {}
+
+ // input:
+ const RegEx* end; // what condition ends this scalar?
+ // unowned.
+ bool eatEnd; // should we eat that condition when we see it?
+ int indent; // what level of indentation should be eaten and ignored?
+ bool detectIndent; // should we try to autodetect the indent?
+ bool eatLeadingWhitespace; // should we continue eating this delicious
+ // indentation after 'indent' spaces?
+ char escape; // what character do we escape on (i.e., slash or single quote)
+ // (0 for none)
+ FOLD fold; // how do we fold line ends?
+ bool trimTrailingSpaces; // do we remove all trailing spaces (at the very
+ // end)
+ CHOMP chomp; // do we strip, clip, or keep trailing newlines (at the very
+ // end)
+ // Note: strip means kill all, clip means keep at most one, keep means keep
+ // all
+ ACTION onDocIndicator; // what do we do if we see a document indicator?
+ ACTION onTabInIndentation; // what do we do if we see a tab where we should
+ // be seeing indentation spaces
+
+ // output:
+ bool leadingSpaces;
+};
+
+std::string ScanScalar(Stream& INPUT, ScanScalarParams& params);
+}
+
+#endif // SCANSCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/scantag.cpp b/src/libs/3rdparty/yaml-cpp/src/scantag.cpp
new file mode 100644
index 0000000000..176cc5c711
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/scantag.cpp
@@ -0,0 +1,81 @@
+#include "exp.h"
+#include "regex_yaml.h"
+#include "regeximpl.h"
+#include "stream.h"
+#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
+#include "yaml-cpp/mark.h"
+
+namespace YAML {
+const std::string ScanVerbatimTag(Stream& INPUT) {
+ std::string tag;
+
+ // eat the start character
+ INPUT.get();
+
+ while (INPUT) {
+ if (INPUT.peek() == Keys::VerbatimTagEnd) {
+ // eat the end character
+ INPUT.get();
+ return tag;
+ }
+
+ int n = Exp::URI().Match(INPUT);
+ if (n <= 0)
+ break;
+
+ tag += INPUT.get(n);
+ }
+
+ throw ParserException(INPUT.mark(), ErrorMsg::END_OF_VERBATIM_TAG);
+}
+
+const std::string ScanTagHandle(Stream& INPUT, bool& canBeHandle) {
+ std::string tag;
+ canBeHandle = true;
+ Mark firstNonWordChar;
+
+ while (INPUT) {
+ if (INPUT.peek() == Keys::Tag) {
+ if (!canBeHandle)
+ throw ParserException(firstNonWordChar, ErrorMsg::CHAR_IN_TAG_HANDLE);
+ break;
+ }
+
+ int n = 0;
+ if (canBeHandle) {
+ n = Exp::Word().Match(INPUT);
+ if (n <= 0) {
+ canBeHandle = false;
+ firstNonWordChar = INPUT.mark();
+ }
+ }
+
+ if (!canBeHandle)
+ n = Exp::Tag().Match(INPUT);
+
+ if (n <= 0)
+ break;
+
+ tag += INPUT.get(n);
+ }
+
+ return tag;
+}
+
+const std::string ScanTagSuffix(Stream& INPUT) {
+ std::string tag;
+
+ while (INPUT) {
+ int n = Exp::Tag().Match(INPUT);
+ if (n <= 0)
+ break;
+
+ tag += INPUT.get(n);
+ }
+
+ if (tag.empty())
+ throw ParserException(INPUT.mark(), ErrorMsg::TAG_WITH_NO_SUFFIX);
+
+ return tag;
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/scantag.h b/src/libs/3rdparty/yaml-cpp/src/scantag.h
new file mode 100644
index 0000000000..522ba5495e
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/scantag.h
@@ -0,0 +1,19 @@
+#ifndef SCANTAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define SCANTAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <string>
+#include "stream.h"
+
+namespace YAML {
+const std::string ScanVerbatimTag(Stream& INPUT);
+const std::string ScanTagHandle(Stream& INPUT, bool& canBeHandle);
+const std::string ScanTagSuffix(Stream& INPUT);
+}
+
+#endif // SCANTAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/scantoken.cpp b/src/libs/3rdparty/yaml-cpp/src/scantoken.cpp
new file mode 100644
index 0000000000..1a94ab1d7d
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/scantoken.cpp
@@ -0,0 +1,437 @@
+#include <sstream>
+
+#include "exp.h"
+#include "regex_yaml.h"
+#include "regeximpl.h"
+#include "scanner.h"
+#include "scanscalar.h"
+#include "scantag.h" // IWYU pragma: keep
+#include "tag.h" // IWYU pragma: keep
+#include "token.h"
+#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
+#include "yaml-cpp/mark.h"
+
+namespace YAML {
+///////////////////////////////////////////////////////////////////////
+// Specialization for scanning specific tokens
+
+// Directive
+// . Note: no semantic checking is done here (that's for the parser to do)
+void Scanner::ScanDirective() {
+ std::string name;
+ std::vector<std::string> params;
+
+ // pop indents and simple keys
+ PopAllIndents();
+ PopAllSimpleKeys();
+
+ m_simpleKeyAllowed = false;
+ m_canBeJSONFlow = false;
+
+ // store pos and eat indicator
+ Token token(Token::DIRECTIVE, INPUT.mark());
+ INPUT.eat(1);
+
+ // read name
+ while (INPUT && !Exp::BlankOrBreak().Matches(INPUT))
+ token.value += INPUT.get();
+
+ // read parameters
+ while (true) {
+ // first get rid of whitespace
+ while (Exp::Blank().Matches(INPUT))
+ INPUT.eat(1);
+
+ // break on newline or comment
+ if (!INPUT || Exp::Break().Matches(INPUT) || Exp::Comment().Matches(INPUT))
+ break;
+
+ // now read parameter
+ std::string param;
+ while (INPUT && !Exp::BlankOrBreak().Matches(INPUT))
+ param += INPUT.get();
+
+ token.params.push_back(param);
+ }
+
+ m_tokens.push(token);
+}
+
+// DocStart
+void Scanner::ScanDocStart() {
+ PopAllIndents();
+ PopAllSimpleKeys();
+ m_simpleKeyAllowed = false;
+ m_canBeJSONFlow = false;
+
+ // eat
+ Mark mark = INPUT.mark();
+ INPUT.eat(3);
+ m_tokens.push(Token(Token::DOC_START, mark));
+}
+
+// DocEnd
+void Scanner::ScanDocEnd() {
+ PopAllIndents();
+ PopAllSimpleKeys();
+ m_simpleKeyAllowed = false;
+ m_canBeJSONFlow = false;
+
+ // eat
+ Mark mark = INPUT.mark();
+ INPUT.eat(3);
+ m_tokens.push(Token(Token::DOC_END, mark));
+}
+
+// FlowStart
+void Scanner::ScanFlowStart() {
+ // flows can be simple keys
+ InsertPotentialSimpleKey();
+ m_simpleKeyAllowed = true;
+ m_canBeJSONFlow = false;
+
+ // eat
+ Mark mark = INPUT.mark();
+ char ch = INPUT.get();
+ FLOW_MARKER flowType = (ch == Keys::FlowSeqStart ? FLOW_SEQ : FLOW_MAP);
+ m_flows.push(flowType);
+ Token::TYPE type =
+ (flowType == FLOW_SEQ ? Token::FLOW_SEQ_START : Token::FLOW_MAP_START);
+ m_tokens.push(Token(type, mark));
+}
+
+// FlowEnd
+void Scanner::ScanFlowEnd() {
+ if (InBlockContext())
+ throw ParserException(INPUT.mark(), ErrorMsg::FLOW_END);
+
+ // we might have a solo entry in the flow context
+ if (InFlowContext()) {
+ if (m_flows.top() == FLOW_MAP && VerifySimpleKey())
+ m_tokens.push(Token(Token::VALUE, INPUT.mark()));
+ else if (m_flows.top() == FLOW_SEQ)
+ InvalidateSimpleKey();
+ }
+
+ m_simpleKeyAllowed = false;
+ m_canBeJSONFlow = true;
+
+ // eat
+ Mark mark = INPUT.mark();
+ char ch = INPUT.get();
+
+ // check that it matches the start
+ FLOW_MARKER flowType = (ch == Keys::FlowSeqEnd ? FLOW_SEQ : FLOW_MAP);
+ if (m_flows.top() != flowType)
+ throw ParserException(mark, ErrorMsg::FLOW_END);
+ m_flows.pop();
+
+ Token::TYPE type = (flowType ? Token::FLOW_SEQ_END : Token::FLOW_MAP_END);
+ m_tokens.push(Token(type, mark));
+}
+
+// FlowEntry
+void Scanner::ScanFlowEntry() {
+ // we might have a solo entry in the flow context
+ if (InFlowContext()) {
+ if (m_flows.top() == FLOW_MAP && VerifySimpleKey())
+ m_tokens.push(Token(Token::VALUE, INPUT.mark()));
+ else if (m_flows.top() == FLOW_SEQ)
+ InvalidateSimpleKey();
+ }
+
+ m_simpleKeyAllowed = true;
+ m_canBeJSONFlow = false;
+
+ // eat
+ Mark mark = INPUT.mark();
+ INPUT.eat(1);
+ m_tokens.push(Token(Token::FLOW_ENTRY, mark));
+}
+
+// BlockEntry
+void Scanner::ScanBlockEntry() {
+ // we better be in the block context!
+ if (InFlowContext())
+ throw ParserException(INPUT.mark(), ErrorMsg::BLOCK_ENTRY);
+
+ // can we put it here?
+ if (!m_simpleKeyAllowed)
+ throw ParserException(INPUT.mark(), ErrorMsg::BLOCK_ENTRY);
+
+ PushIndentTo(INPUT.column(), IndentMarker::SEQ);
+ m_simpleKeyAllowed = true;
+ m_canBeJSONFlow = false;
+
+ // eat
+ Mark mark = INPUT.mark();
+ INPUT.eat(1);
+ m_tokens.push(Token(Token::BLOCK_ENTRY, mark));
+}
+
+// Key
+void Scanner::ScanKey() {
+ // handle keys differently in the block context (and manage indents)
+ if (InBlockContext()) {
+ if (!m_simpleKeyAllowed)
+ throw ParserException(INPUT.mark(), ErrorMsg::MAP_KEY);
+
+ PushIndentTo(INPUT.column(), IndentMarker::MAP);
+ }
+
+ // can only put a simple key here if we're in block context
+ m_simpleKeyAllowed = InBlockContext();
+
+ // eat
+ Mark mark = INPUT.mark();
+ INPUT.eat(1);
+ m_tokens.push(Token(Token::KEY, mark));
+}
+
+// Value
+void Scanner::ScanValue() {
+ // and check that simple key
+ bool isSimpleKey = VerifySimpleKey();
+ m_canBeJSONFlow = false;
+
+ if (isSimpleKey) {
+ // can't follow a simple key with another simple key (dunno why, though - it
+ // seems fine)
+ m_simpleKeyAllowed = false;
+ } else {
+ // handle values differently in the block context (and manage indents)
+ if (InBlockContext()) {
+ if (!m_simpleKeyAllowed)
+ throw ParserException(INPUT.mark(), ErrorMsg::MAP_VALUE);
+
+ PushIndentTo(INPUT.column(), IndentMarker::MAP);
+ }
+
+ // can only put a simple key here if we're in block context
+ m_simpleKeyAllowed = InBlockContext();
+ }
+
+ // eat
+ Mark mark = INPUT.mark();
+ INPUT.eat(1);
+ m_tokens.push(Token(Token::VALUE, mark));
+}
+
+// AnchorOrAlias
+void Scanner::ScanAnchorOrAlias() {
+ bool alias;
+ std::string name;
+
+ // insert a potential simple key
+ InsertPotentialSimpleKey();
+ m_simpleKeyAllowed = false;
+ m_canBeJSONFlow = false;
+
+ // eat the indicator
+ Mark mark = INPUT.mark();
+ char indicator = INPUT.get();
+ alias = (indicator == Keys::Alias);
+
+ // now eat the content
+ while (INPUT && Exp::Anchor().Matches(INPUT))
+ name += INPUT.get();
+
+ // we need to have read SOMETHING!
+ if (name.empty())
+ throw ParserException(INPUT.mark(), alias ? ErrorMsg::ALIAS_NOT_FOUND
+ : ErrorMsg::ANCHOR_NOT_FOUND);
+
+ // and needs to end correctly
+ if (INPUT && !Exp::AnchorEnd().Matches(INPUT))
+ throw ParserException(INPUT.mark(), alias ? ErrorMsg::CHAR_IN_ALIAS
+ : ErrorMsg::CHAR_IN_ANCHOR);
+
+ // and we're done
+ Token token(alias ? Token::ALIAS : Token::ANCHOR, mark);
+ token.value = name;
+ m_tokens.push(token);
+}
+
+// Tag
+void Scanner::ScanTag() {
+ // insert a potential simple key
+ InsertPotentialSimpleKey();
+ m_simpleKeyAllowed = false;
+ m_canBeJSONFlow = false;
+
+ Token token(Token::TAG, INPUT.mark());
+
+ // eat the indicator
+ INPUT.get();
+
+ if (INPUT && INPUT.peek() == Keys::VerbatimTagStart) {
+ std::string tag = ScanVerbatimTag(INPUT);
+
+ token.value = tag;
+ token.data = Tag::VERBATIM;
+ } else {
+ bool canBeHandle;
+ token.value = ScanTagHandle(INPUT, canBeHandle);
+ if (!canBeHandle && token.value.empty())
+ token.data = Tag::NON_SPECIFIC;
+ else if (token.value.empty())
+ token.data = Tag::SECONDARY_HANDLE;
+ else
+ token.data = Tag::PRIMARY_HANDLE;
+
+ // is there a suffix?
+ if (canBeHandle && INPUT.peek() == Keys::Tag) {
+ // eat the indicator
+ INPUT.get();
+ token.params.push_back(ScanTagSuffix(INPUT));
+ token.data = Tag::NAMED_HANDLE;
+ }
+ }
+
+ m_tokens.push(token);
+}
+
+// PlainScalar
+void Scanner::ScanPlainScalar() {
+ std::string scalar;
+
+ // set up the scanning parameters
+ ScanScalarParams params;
+ params.end =
+ (InFlowContext() ? &Exp::ScanScalarEndInFlow() : &Exp::ScanScalarEnd());
+ params.eatEnd = false;
+ params.indent = (InFlowContext() ? 0 : GetTopIndent() + 1);
+ params.fold = FOLD_FLOW;
+ params.eatLeadingWhitespace = true;
+ params.trimTrailingSpaces = true;
+ params.chomp = STRIP;
+ params.onDocIndicator = BREAK;
+ params.onTabInIndentation = THROW;
+
+ // insert a potential simple key
+ InsertPotentialSimpleKey();
+
+ Mark mark = INPUT.mark();
+ scalar = ScanScalar(INPUT, params);
+
+ // can have a simple key only if we ended the scalar by starting a new line
+ m_simpleKeyAllowed = params.leadingSpaces;
+ m_canBeJSONFlow = false;
+
+ // finally, check and see if we ended on an illegal character
+ // if(Exp::IllegalCharInScalar.Matches(INPUT))
+ // throw ParserException(INPUT.mark(), ErrorMsg::CHAR_IN_SCALAR);
+
+ Token token(Token::PLAIN_SCALAR, mark);
+ token.value = scalar;
+ m_tokens.push(token);
+}
+
+// QuotedScalar
+void Scanner::ScanQuotedScalar() {
+ std::string scalar;
+
+ // peek at single or double quote (don't eat because we need to preserve (for
+ // the time being) the input position)
+ char quote = INPUT.peek();
+ bool single = (quote == '\'');
+
+ // setup the scanning parameters
+ ScanScalarParams params;
+ RegEx end = (single ? RegEx(quote) & !Exp::EscSingleQuote() : RegEx(quote));
+ params.end = &end;
+ params.eatEnd = true;
+ params.escape = (single ? '\'' : '\\');
+ params.indent = 0;
+ params.fold = FOLD_FLOW;
+ params.eatLeadingWhitespace = true;
+ params.trimTrailingSpaces = false;
+ params.chomp = CLIP;
+ params.onDocIndicator = THROW;
+
+ // insert a potential simple key
+ InsertPotentialSimpleKey();
+
+ Mark mark = INPUT.mark();
+
+ // now eat that opening quote
+ INPUT.get();
+
+ // and scan
+ scalar = ScanScalar(INPUT, params);
+ m_simpleKeyAllowed = false;
+ m_canBeJSONFlow = true;
+
+ Token token(Token::NON_PLAIN_SCALAR, mark);
+ token.value = scalar;
+ m_tokens.push(token);
+}
+
+// BlockScalarToken
+// . These need a little extra processing beforehand.
+// . We need to scan the line where the indicator is (this doesn't count as part
+// of the scalar),
+// and then we need to figure out what level of indentation we'll be using.
+void Scanner::ScanBlockScalar() {
+ std::string scalar;
+
+ ScanScalarParams params;
+ params.indent = 1;
+ params.detectIndent = true;
+
+ // eat block indicator ('|' or '>')
+ Mark mark = INPUT.mark();
+ char indicator = INPUT.get();
+ params.fold = (indicator == Keys::FoldedScalar ? FOLD_BLOCK : DONT_FOLD);
+
+ // eat chomping/indentation indicators
+ params.chomp = CLIP;
+ int n = Exp::Chomp().Match(INPUT);
+ for (int i = 0; i < n; i++) {
+ char ch = INPUT.get();
+ if (ch == '+')
+ params.chomp = KEEP;
+ else if (ch == '-')
+ params.chomp = STRIP;
+ else if (Exp::Digit().Matches(ch)) {
+ if (ch == '0')
+ throw ParserException(INPUT.mark(), ErrorMsg::ZERO_INDENT_IN_BLOCK);
+
+ params.indent = ch - '0';
+ params.detectIndent = false;
+ }
+ }
+
+ // now eat whitespace
+ while (Exp::Blank().Matches(INPUT))
+ INPUT.eat(1);
+
+ // and comments to the end of the line
+ if (Exp::Comment().Matches(INPUT))
+ while (INPUT && !Exp::Break().Matches(INPUT))
+ INPUT.eat(1);
+
+ // if it's not a line break, then we ran into a bad character inline
+ if (INPUT && !Exp::Break().Matches(INPUT))
+ throw ParserException(INPUT.mark(), ErrorMsg::CHAR_IN_BLOCK);
+
+ // set the initial indentation
+ if (GetTopIndent() >= 0)
+ params.indent += GetTopIndent();
+
+ params.eatLeadingWhitespace = false;
+ params.trimTrailingSpaces = false;
+ params.onTabInIndentation = THROW;
+
+ scalar = ScanScalar(INPUT, params);
+
+ // simple keys always ok after block scalars (since we're gonna start a new
+ // line anyways)
+ m_simpleKeyAllowed = true;
+ m_canBeJSONFlow = false;
+
+ Token token(Token::NON_PLAIN_SCALAR, mark);
+ token.value = scalar;
+ m_tokens.push(token);
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/setting.h b/src/libs/3rdparty/yaml-cpp/src/setting.h
new file mode 100644
index 0000000000..4960bbf75c
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/setting.h
@@ -0,0 +1,100 @@
+#ifndef SETTING_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define SETTING_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/noexcept.h"
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace YAML {
+
+class SettingChangeBase {
+ public:
+ virtual ~SettingChangeBase() = default;
+ virtual void pop() = 0;
+};
+
+template <typename T>
+class Setting {
+ public:
+ Setting() : m_value() {}
+ Setting(const T& value) : m_value() { set(value); }
+
+ const T get() const { return m_value; }
+ std::unique_ptr<SettingChangeBase> set(const T& value);
+ void restore(const Setting<T>& oldSetting) { m_value = oldSetting.get(); }
+
+ private:
+ T m_value;
+};
+
+template <typename T>
+class SettingChange : public SettingChangeBase {
+ public:
+ SettingChange(Setting<T>* pSetting)
+ : m_pCurSetting(pSetting),
+ m_oldSetting(*pSetting) // copy old setting to save its state
+ {}
+ SettingChange(const SettingChange&) = delete;
+ SettingChange(SettingChange&&) = delete;
+ SettingChange& operator=(const SettingChange&) = delete;
+ SettingChange& operator=(SettingChange&&) = delete;
+
+ void pop() override { m_pCurSetting->restore(m_oldSetting); }
+
+ private:
+ Setting<T>* m_pCurSetting;
+ Setting<T> m_oldSetting;
+};
+
+template <typename T>
+inline std::unique_ptr<SettingChangeBase> Setting<T>::set(const T& value) {
+ std::unique_ptr<SettingChangeBase> pChange(new SettingChange<T>(this));
+ m_value = value;
+ return pChange;
+}
+
+class SettingChanges {
+ public:
+ SettingChanges() : m_settingChanges{} {}
+ SettingChanges(const SettingChanges&) = delete;
+ SettingChanges(SettingChanges&&) YAML_CPP_NOEXCEPT = default;
+ SettingChanges& operator=(const SettingChanges&) = delete;
+ SettingChanges& operator=(SettingChanges&& rhs) YAML_CPP_NOEXCEPT {
+ if (this == &rhs)
+ return *this;
+
+ clear();
+ std::swap(m_settingChanges, rhs.m_settingChanges);
+
+ return *this;
+ }
+ ~SettingChanges() { clear(); }
+
+ void clear() YAML_CPP_NOEXCEPT {
+ restore();
+ m_settingChanges.clear();
+ }
+
+ void restore() YAML_CPP_NOEXCEPT {
+ for (const auto& setting : m_settingChanges)
+ setting->pop();
+ }
+
+ void push(std::unique_ptr<SettingChangeBase> pSettingChange) {
+ m_settingChanges.push_back(std::move(pSettingChange));
+ }
+
+ private:
+ using setting_changes = std::vector<std::unique_ptr<SettingChangeBase>>;
+ setting_changes m_settingChanges;
+};
+} // namespace YAML
+
+#endif // SETTING_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/simplekey.cpp b/src/libs/3rdparty/yaml-cpp/src/simplekey.cpp
new file mode 100644
index 0000000000..67c2d712ef
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/simplekey.cpp
@@ -0,0 +1,132 @@
+#include "scanner.h"
+#include "token.h"
+
+namespace YAML {
+struct Mark;
+
+Scanner::SimpleKey::SimpleKey(const Mark& mark_, std::size_t flowLevel_)
+ : mark(mark_),
+ flowLevel(flowLevel_),
+ pIndent(nullptr),
+ pMapStart(nullptr),
+ pKey(nullptr) {}
+
+void Scanner::SimpleKey::Validate() {
+ // Note: pIndent will *not* be garbage here;
+ // we "garbage collect" them so we can
+ // always refer to them
+ if (pIndent)
+ pIndent->status = IndentMarker::VALID;
+ if (pMapStart)
+ pMapStart->status = Token::VALID;
+ if (pKey)
+ pKey->status = Token::VALID;
+}
+
+void Scanner::SimpleKey::Invalidate() {
+ if (pIndent)
+ pIndent->status = IndentMarker::INVALID;
+ if (pMapStart)
+ pMapStart->status = Token::INVALID;
+ if (pKey)
+ pKey->status = Token::INVALID;
+}
+
+// CanInsertPotentialSimpleKey
+bool Scanner::CanInsertPotentialSimpleKey() const {
+ if (!m_simpleKeyAllowed)
+ return false;
+
+ return !ExistsActiveSimpleKey();
+}
+
+// ExistsActiveSimpleKey
+// . Returns true if there's a potential simple key at our flow level
+// (there's allowed at most one per flow level, i.e., at the start of the flow
+// start token)
+bool Scanner::ExistsActiveSimpleKey() const {
+ if (m_simpleKeys.empty())
+ return false;
+
+ const SimpleKey& key = m_simpleKeys.top();
+ return key.flowLevel == GetFlowLevel();
+}
+
+// InsertPotentialSimpleKey
+// . If we can, add a potential simple key to the queue,
+// and save it on a stack.
+void Scanner::InsertPotentialSimpleKey() {
+ if (!CanInsertPotentialSimpleKey())
+ return;
+
+ SimpleKey key(INPUT.mark(), GetFlowLevel());
+
+ // first add a map start, if necessary
+ if (InBlockContext()) {
+ key.pIndent = PushIndentTo(INPUT.column(), IndentMarker::MAP);
+ if (key.pIndent) {
+ key.pIndent->status = IndentMarker::UNKNOWN;
+ key.pMapStart = key.pIndent->pStartToken;
+ key.pMapStart->status = Token::UNVERIFIED;
+ }
+ }
+
+ // then add the (now unverified) key
+ m_tokens.push(Token(Token::KEY, INPUT.mark()));
+ key.pKey = &m_tokens.back();
+ key.pKey->status = Token::UNVERIFIED;
+
+ m_simpleKeys.push(key);
+}
+
+// InvalidateSimpleKey
+// . Automatically invalidate the simple key in our flow level
+void Scanner::InvalidateSimpleKey() {
+ if (m_simpleKeys.empty())
+ return;
+
+ // grab top key
+ SimpleKey& key = m_simpleKeys.top();
+ if (key.flowLevel != GetFlowLevel())
+ return;
+
+ key.Invalidate();
+ m_simpleKeys.pop();
+}
+
+// VerifySimpleKey
+// . Determines whether the latest simple key to be added is valid,
+// and if so, makes it valid.
+bool Scanner::VerifySimpleKey() {
+ if (m_simpleKeys.empty())
+ return false;
+
+ // grab top key
+ SimpleKey key = m_simpleKeys.top();
+
+ // only validate if we're in the correct flow level
+ if (key.flowLevel != GetFlowLevel())
+ return false;
+
+ m_simpleKeys.pop();
+
+ bool isValid = true;
+
+ // needs to be less than 1024 characters and inline
+ if (INPUT.line() != key.mark.line || INPUT.pos() - key.mark.pos > 1024)
+ isValid = false;
+
+ // invalidate key
+ if (isValid)
+ key.Validate();
+ else
+ key.Invalidate();
+
+ return isValid;
+}
+
+void Scanner::PopAllSimpleKeys() {
+ while (!m_simpleKeys.empty())
+ m_simpleKeys.pop();
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/singledocparser.cpp b/src/libs/3rdparty/yaml-cpp/src/singledocparser.cpp
new file mode 100644
index 0000000000..22913d198c
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/singledocparser.cpp
@@ -0,0 +1,435 @@
+#include <algorithm>
+#include <cstdio>
+#include <sstream>
+
+#include "collectionstack.h" // IWYU pragma: keep
+#include "scanner.h"
+#include "singledocparser.h"
+#include "tag.h"
+#include "token.h"
+#include "yaml-cpp/depthguard.h"
+#include "yaml-cpp/emitterstyle.h"
+#include "yaml-cpp/eventhandler.h"
+#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
+#include "yaml-cpp/mark.h"
+#include "yaml-cpp/null.h"
+
+namespace YAML {
+SingleDocParser::SingleDocParser(Scanner& scanner, const Directives& directives)
+ : m_scanner(scanner),
+ m_directives(directives),
+ m_pCollectionStack(new CollectionStack),
+ m_anchors{},
+ m_curAnchor(0) {}
+
+SingleDocParser::~SingleDocParser() = default;
+
+// HandleDocument
+// . Handles the next document
+// . Throws a ParserException on error.
+void SingleDocParser::HandleDocument(EventHandler& eventHandler) {
+ assert(!m_scanner.empty()); // guaranteed that there are tokens
+ assert(!m_curAnchor);
+
+ eventHandler.OnDocumentStart(m_scanner.peek().mark);
+
+ // eat doc start
+ if (m_scanner.peek().type == Token::DOC_START)
+ m_scanner.pop();
+
+ // recurse!
+ HandleNode(eventHandler);
+
+ eventHandler.OnDocumentEnd();
+
+ // and finally eat any doc ends we see
+ while (!m_scanner.empty() && m_scanner.peek().type == Token::DOC_END)
+ m_scanner.pop();
+}
+
+void SingleDocParser::HandleNode(EventHandler& eventHandler) {
+ DepthGuard<500> depthguard(depth, m_scanner.mark(), ErrorMsg::BAD_FILE);
+
+ // an empty node *is* a possibility
+ if (m_scanner.empty()) {
+ eventHandler.OnNull(m_scanner.mark(), NullAnchor);
+ return;
+ }
+
+ // save location
+ Mark mark = m_scanner.peek().mark;
+
+ // special case: a value node by itself must be a map, with no header
+ if (m_scanner.peek().type == Token::VALUE) {
+ eventHandler.OnMapStart(mark, "?", NullAnchor, EmitterStyle::Default);
+ HandleMap(eventHandler);
+ eventHandler.OnMapEnd();
+ return;
+ }
+
+ // special case: an alias node
+ if (m_scanner.peek().type == Token::ALIAS) {
+ eventHandler.OnAlias(mark, LookupAnchor(mark, m_scanner.peek().value));
+ m_scanner.pop();
+ return;
+ }
+
+ std::string tag;
+ std::string anchor_name;
+ anchor_t anchor;
+ ParseProperties(tag, anchor, anchor_name);
+
+ if (!anchor_name.empty())
+ eventHandler.OnAnchor(mark, anchor_name);
+
+ // after parsing properties, an empty node is again a possibility
+ if (m_scanner.empty()) {
+ eventHandler.OnNull(mark, anchor);
+ return;
+ }
+
+ const Token& token = m_scanner.peek();
+
+ // add non-specific tags
+ if (tag.empty())
+ tag = (token.type == Token::NON_PLAIN_SCALAR ? "!" : "?");
+
+ if (token.type == Token::PLAIN_SCALAR
+ && tag.compare("?") == 0 && IsNullString(token.value)) {
+ eventHandler.OnNull(mark, anchor);
+ m_scanner.pop();
+ return;
+ }
+
+ // now split based on what kind of node we should be
+ switch (token.type) {
+ case Token::PLAIN_SCALAR:
+ case Token::NON_PLAIN_SCALAR:
+ eventHandler.OnScalar(mark, tag, anchor, token.value);
+ m_scanner.pop();
+ return;
+ case Token::FLOW_SEQ_START:
+ eventHandler.OnSequenceStart(mark, tag, anchor, EmitterStyle::Flow);
+ HandleSequence(eventHandler);
+ eventHandler.OnSequenceEnd();
+ return;
+ case Token::BLOCK_SEQ_START:
+ eventHandler.OnSequenceStart(mark, tag, anchor, EmitterStyle::Block);
+ HandleSequence(eventHandler);
+ eventHandler.OnSequenceEnd();
+ return;
+ case Token::FLOW_MAP_START:
+ eventHandler.OnMapStart(mark, tag, anchor, EmitterStyle::Flow);
+ HandleMap(eventHandler);
+ eventHandler.OnMapEnd();
+ return;
+ case Token::BLOCK_MAP_START:
+ eventHandler.OnMapStart(mark, tag, anchor, EmitterStyle::Block);
+ HandleMap(eventHandler);
+ eventHandler.OnMapEnd();
+ return;
+ case Token::KEY:
+ // compact maps can only go in a flow sequence
+ if (m_pCollectionStack->GetCurCollectionType() ==
+ CollectionType::FlowSeq) {
+ eventHandler.OnMapStart(mark, tag, anchor, EmitterStyle::Flow);
+ HandleMap(eventHandler);
+ eventHandler.OnMapEnd();
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (tag == "?")
+ eventHandler.OnNull(mark, anchor);
+ else
+ eventHandler.OnScalar(mark, tag, anchor, "");
+}
+
+void SingleDocParser::HandleSequence(EventHandler& eventHandler) {
+ // split based on start token
+ switch (m_scanner.peek().type) {
+ case Token::BLOCK_SEQ_START:
+ HandleBlockSequence(eventHandler);
+ break;
+ case Token::FLOW_SEQ_START:
+ HandleFlowSequence(eventHandler);
+ break;
+ default:
+ break;
+ }
+}
+
+void SingleDocParser::HandleBlockSequence(EventHandler& eventHandler) {
+ // eat start token
+ m_scanner.pop();
+ m_pCollectionStack->PushCollectionType(CollectionType::BlockSeq);
+
+ while (true) {
+ if (m_scanner.empty())
+ throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ);
+
+ Token token = m_scanner.peek();
+ if (token.type != Token::BLOCK_ENTRY && token.type != Token::BLOCK_SEQ_END)
+ throw ParserException(token.mark, ErrorMsg::END_OF_SEQ);
+
+ m_scanner.pop();
+ if (token.type == Token::BLOCK_SEQ_END)
+ break;
+
+ // check for null
+ if (!m_scanner.empty()) {
+ const Token& nextToken = m_scanner.peek();
+ if (nextToken.type == Token::BLOCK_ENTRY ||
+ nextToken.type == Token::BLOCK_SEQ_END) {
+ eventHandler.OnNull(nextToken.mark, NullAnchor);
+ continue;
+ }
+ }
+
+ HandleNode(eventHandler);
+ }
+
+ m_pCollectionStack->PopCollectionType(CollectionType::BlockSeq);
+}
+
+void SingleDocParser::HandleFlowSequence(EventHandler& eventHandler) {
+ // eat start token
+ m_scanner.pop();
+ m_pCollectionStack->PushCollectionType(CollectionType::FlowSeq);
+
+ while (true) {
+ if (m_scanner.empty())
+ throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ_FLOW);
+
+ // first check for end
+ if (m_scanner.peek().type == Token::FLOW_SEQ_END) {
+ m_scanner.pop();
+ break;
+ }
+
+ // then read the node
+ HandleNode(eventHandler);
+
+ if (m_scanner.empty())
+ throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ_FLOW);
+
+ // now eat the separator (or could be a sequence end, which we ignore - but
+ // if it's neither, then it's a bad node)
+ Token& token = m_scanner.peek();
+ if (token.type == Token::FLOW_ENTRY)
+ m_scanner.pop();
+ else if (token.type != Token::FLOW_SEQ_END)
+ throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW);
+ }
+
+ m_pCollectionStack->PopCollectionType(CollectionType::FlowSeq);
+}
+
+void SingleDocParser::HandleMap(EventHandler& eventHandler) {
+ // split based on start token
+ switch (m_scanner.peek().type) {
+ case Token::BLOCK_MAP_START:
+ HandleBlockMap(eventHandler);
+ break;
+ case Token::FLOW_MAP_START:
+ HandleFlowMap(eventHandler);
+ break;
+ case Token::KEY:
+ HandleCompactMap(eventHandler);
+ break;
+ case Token::VALUE:
+ HandleCompactMapWithNoKey(eventHandler);
+ break;
+ default:
+ break;
+ }
+}
+
+void SingleDocParser::HandleBlockMap(EventHandler& eventHandler) {
+ // eat start token
+ m_scanner.pop();
+ m_pCollectionStack->PushCollectionType(CollectionType::BlockMap);
+
+ while (true) {
+ if (m_scanner.empty())
+ throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP);
+
+ Token token = m_scanner.peek();
+ if (token.type != Token::KEY && token.type != Token::VALUE &&
+ token.type != Token::BLOCK_MAP_END)
+ throw ParserException(token.mark, ErrorMsg::END_OF_MAP);
+
+ if (token.type == Token::BLOCK_MAP_END) {
+ m_scanner.pop();
+ break;
+ }
+
+ // grab key (if non-null)
+ if (token.type == Token::KEY) {
+ m_scanner.pop();
+ HandleNode(eventHandler);
+ } else {
+ eventHandler.OnNull(token.mark, NullAnchor);
+ }
+
+ // now grab value (optional)
+ if (!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) {
+ m_scanner.pop();
+ HandleNode(eventHandler);
+ } else {
+ eventHandler.OnNull(token.mark, NullAnchor);
+ }
+ }
+
+ m_pCollectionStack->PopCollectionType(CollectionType::BlockMap);
+}
+
+void SingleDocParser::HandleFlowMap(EventHandler& eventHandler) {
+ // eat start token
+ m_scanner.pop();
+ m_pCollectionStack->PushCollectionType(CollectionType::FlowMap);
+
+ while (true) {
+ if (m_scanner.empty())
+ throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW);
+
+ Token& token = m_scanner.peek();
+ const Mark mark = token.mark;
+ // first check for end
+ if (token.type == Token::FLOW_MAP_END) {
+ m_scanner.pop();
+ break;
+ }
+
+ // grab key (if non-null)
+ if (token.type == Token::KEY) {
+ m_scanner.pop();
+ HandleNode(eventHandler);
+ } else {
+ eventHandler.OnNull(mark, NullAnchor);
+ }
+
+ // now grab value (optional)
+ if (!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) {
+ m_scanner.pop();
+ HandleNode(eventHandler);
+ } else {
+ eventHandler.OnNull(mark, NullAnchor);
+ }
+
+ if (m_scanner.empty())
+ throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW);
+
+ // now eat the separator (or could be a map end, which we ignore - but if
+ // it's neither, then it's a bad node)
+ Token& nextToken = m_scanner.peek();
+ if (nextToken.type == Token::FLOW_ENTRY)
+ m_scanner.pop();
+ else if (nextToken.type != Token::FLOW_MAP_END)
+ throw ParserException(nextToken.mark, ErrorMsg::END_OF_MAP_FLOW);
+ }
+
+ m_pCollectionStack->PopCollectionType(CollectionType::FlowMap);
+}
+
+// . Single "key: value" pair in a flow sequence
+void SingleDocParser::HandleCompactMap(EventHandler& eventHandler) {
+ m_pCollectionStack->PushCollectionType(CollectionType::CompactMap);
+
+ // grab key
+ Mark mark = m_scanner.peek().mark;
+ m_scanner.pop();
+ HandleNode(eventHandler);
+
+ // now grab value (optional)
+ if (!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) {
+ m_scanner.pop();
+ HandleNode(eventHandler);
+ } else {
+ eventHandler.OnNull(mark, NullAnchor);
+ }
+
+ m_pCollectionStack->PopCollectionType(CollectionType::CompactMap);
+}
+
+// . Single ": value" pair in a flow sequence
+void SingleDocParser::HandleCompactMapWithNoKey(EventHandler& eventHandler) {
+ m_pCollectionStack->PushCollectionType(CollectionType::CompactMap);
+
+ // null key
+ eventHandler.OnNull(m_scanner.peek().mark, NullAnchor);
+
+ // grab value
+ m_scanner.pop();
+ HandleNode(eventHandler);
+
+ m_pCollectionStack->PopCollectionType(CollectionType::CompactMap);
+}
+
+// ParseProperties
+// . Grabs any tag or anchor tokens and deals with them.
+void SingleDocParser::ParseProperties(std::string& tag, anchor_t& anchor,
+ std::string& anchor_name) {
+ tag.clear();
+ anchor_name.clear();
+ anchor = NullAnchor;
+
+ while (true) {
+ if (m_scanner.empty())
+ return;
+
+ switch (m_scanner.peek().type) {
+ case Token::TAG:
+ ParseTag(tag);
+ break;
+ case Token::ANCHOR:
+ ParseAnchor(anchor, anchor_name);
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+void SingleDocParser::ParseTag(std::string& tag) {
+ Token& token = m_scanner.peek();
+ if (!tag.empty())
+ throw ParserException(token.mark, ErrorMsg::MULTIPLE_TAGS);
+
+ Tag tagInfo(token);
+ tag = tagInfo.Translate(m_directives);
+ m_scanner.pop();
+}
+
+void SingleDocParser::ParseAnchor(anchor_t& anchor, std::string& anchor_name) {
+ Token& token = m_scanner.peek();
+ if (anchor)
+ throw ParserException(token.mark, ErrorMsg::MULTIPLE_ANCHORS);
+
+ anchor_name = token.value;
+ anchor = RegisterAnchor(token.value);
+ m_scanner.pop();
+}
+
+anchor_t SingleDocParser::RegisterAnchor(const std::string& name) {
+ if (name.empty())
+ return NullAnchor;
+
+ return m_anchors[name] = ++m_curAnchor;
+}
+
+anchor_t SingleDocParser::LookupAnchor(const Mark& mark,
+ const std::string& name) const {
+ auto it = m_anchors.find(name);
+ if (it == m_anchors.end()) {
+ std::stringstream ss;
+ ss << ErrorMsg::UNKNOWN_ANCHOR << name;
+ throw ParserException(mark, ss.str());
+ }
+
+ return it->second;
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/singledocparser.h b/src/libs/3rdparty/yaml-cpp/src/singledocparser.h
new file mode 100644
index 0000000000..f484eb1f95
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/singledocparser.h
@@ -0,0 +1,71 @@
+#ifndef SINGLEDOCPARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define SINGLEDOCPARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "yaml-cpp/anchor.h"
+
+namespace YAML {
+class CollectionStack;
+template <int> class DepthGuard; // depthguard.h
+class EventHandler;
+class Node;
+class Scanner;
+struct Directives;
+struct Mark;
+struct Token;
+
+class SingleDocParser {
+ public:
+ SingleDocParser(Scanner& scanner, const Directives& directives);
+ SingleDocParser(const SingleDocParser&) = delete;
+ SingleDocParser(SingleDocParser&&) = delete;
+ SingleDocParser& operator=(const SingleDocParser&) = delete;
+ SingleDocParser& operator=(SingleDocParser&&) = delete;
+ ~SingleDocParser();
+
+ void HandleDocument(EventHandler& eventHandler);
+
+ private:
+ void HandleNode(EventHandler& eventHandler);
+
+ void HandleSequence(EventHandler& eventHandler);
+ void HandleBlockSequence(EventHandler& eventHandler);
+ void HandleFlowSequence(EventHandler& eventHandler);
+
+ void HandleMap(EventHandler& eventHandler);
+ void HandleBlockMap(EventHandler& eventHandler);
+ void HandleFlowMap(EventHandler& eventHandler);
+ void HandleCompactMap(EventHandler& eventHandler);
+ void HandleCompactMapWithNoKey(EventHandler& eventHandler);
+
+ void ParseProperties(std::string& tag, anchor_t& anchor,
+ std::string& anchor_name);
+ void ParseTag(std::string& tag);
+ void ParseAnchor(anchor_t& anchor, std::string& anchor_name);
+
+ anchor_t RegisterAnchor(const std::string& name);
+ anchor_t LookupAnchor(const Mark& mark, const std::string& name) const;
+
+ private:
+ int depth = 0;
+ Scanner& m_scanner;
+ const Directives& m_directives;
+ std::unique_ptr<CollectionStack> m_pCollectionStack;
+
+ using Anchors = std::map<std::string, anchor_t>;
+ Anchors m_anchors;
+
+ anchor_t m_curAnchor;
+};
+} // namespace YAML
+
+#endif // SINGLEDOCPARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/stream.cpp b/src/libs/3rdparty/yaml-cpp/src/stream.cpp
new file mode 100644
index 0000000000..b1aa092f69
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/stream.cpp
@@ -0,0 +1,446 @@
+#include <iostream>
+
+#include "stream.h"
+
+#ifndef YAML_PREFETCH_SIZE
+#define YAML_PREFETCH_SIZE 2048
+#endif
+
+#define S_ARRAY_SIZE(A) (sizeof(A) / sizeof(*(A)))
+#define S_ARRAY_END(A) ((A) + S_ARRAY_SIZE(A))
+
+#define CP_REPLACEMENT_CHARACTER (0xFFFD)
+
+namespace YAML {
+enum UtfIntroState {
+ uis_start,
+ uis_utfbe_b1,
+ uis_utf32be_b2,
+ uis_utf32be_bom3,
+ uis_utf32be,
+ uis_utf16be,
+ uis_utf16be_bom1,
+ uis_utfle_bom1,
+ uis_utf16le_bom2,
+ uis_utf32le_bom3,
+ uis_utf16le,
+ uis_utf32le,
+ uis_utf8_imp,
+ uis_utf16le_imp,
+ uis_utf32le_imp3,
+ uis_utf8_bom1,
+ uis_utf8_bom2,
+ uis_utf8,
+ uis_error
+};
+
+enum UtfIntroCharType {
+ uict00,
+ uictBB,
+ uictBF,
+ uictEF,
+ uictFE,
+ uictFF,
+ uictAscii,
+ uictOther,
+ uictMax
+};
+
+static bool s_introFinalState[] = {
+ false, // uis_start
+ false, // uis_utfbe_b1
+ false, // uis_utf32be_b2
+ false, // uis_utf32be_bom3
+ true, // uis_utf32be
+ true, // uis_utf16be
+ false, // uis_utf16be_bom1
+ false, // uis_utfle_bom1
+ false, // uis_utf16le_bom2
+ false, // uis_utf32le_bom3
+ true, // uis_utf16le
+ true, // uis_utf32le
+ false, // uis_utf8_imp
+ false, // uis_utf16le_imp
+ false, // uis_utf32le_imp3
+ false, // uis_utf8_bom1
+ false, // uis_utf8_bom2
+ true, // uis_utf8
+ true, // uis_error
+};
+
+static UtfIntroState s_introTransitions[][uictMax] = {
+ // uict00, uictBB, uictBF, uictEF,
+ // uictFE, uictFF, uictAscii, uictOther
+ {uis_utfbe_b1, uis_utf8, uis_utf8, uis_utf8_bom1, uis_utf16be_bom1,
+ uis_utfle_bom1, uis_utf8_imp, uis_utf8},
+ {uis_utf32be_b2, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8,
+ uis_utf16be, uis_utf8},
+ {uis_utf32be, uis_utf8, uis_utf8, uis_utf8, uis_utf32be_bom3, uis_utf8,
+ uis_utf8, uis_utf8},
+ {uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf32be, uis_utf8,
+ uis_utf8},
+ {uis_utf32be, uis_utf32be, uis_utf32be, uis_utf32be, uis_utf32be,
+ uis_utf32be, uis_utf32be, uis_utf32be},
+ {uis_utf16be, uis_utf16be, uis_utf16be, uis_utf16be, uis_utf16be,
+ uis_utf16be, uis_utf16be, uis_utf16be},
+ {uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf16be, uis_utf8,
+ uis_utf8},
+ {uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf16le_bom2, uis_utf8,
+ uis_utf8, uis_utf8},
+ {uis_utf32le_bom3, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le,
+ uis_utf16le, uis_utf16le, uis_utf16le},
+ {uis_utf32le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le,
+ uis_utf16le, uis_utf16le, uis_utf16le},
+ {uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le,
+ uis_utf16le, uis_utf16le, uis_utf16le},
+ {uis_utf32le, uis_utf32le, uis_utf32le, uis_utf32le, uis_utf32le,
+ uis_utf32le, uis_utf32le, uis_utf32le},
+ {uis_utf16le_imp, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8,
+ uis_utf8, uis_utf8},
+ {uis_utf32le_imp3, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le,
+ uis_utf16le, uis_utf16le, uis_utf16le},
+ {uis_utf32le, uis_utf16le, uis_utf16le, uis_utf16le, uis_utf16le,
+ uis_utf16le, uis_utf16le, uis_utf16le},
+ {uis_utf8, uis_utf8_bom2, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8,
+ uis_utf8},
+ {uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8,
+ uis_utf8},
+ {uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8, uis_utf8,
+ uis_utf8},
+};
+
+static char s_introUngetCount[][uictMax] = {
+ // uict00, uictBB, uictBF, uictEF, uictFE, uictFF, uictAscii, uictOther
+ {0, 1, 1, 0, 0, 0, 0, 1}, {0, 2, 2, 2, 2, 2, 2, 2},
+ {3, 3, 3, 3, 0, 3, 3, 3}, {4, 4, 4, 4, 4, 0, 4, 4},
+ {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1},
+ {2, 2, 2, 2, 2, 0, 2, 2}, {2, 2, 2, 2, 0, 2, 2, 2},
+ {0, 1, 1, 1, 1, 1, 1, 1}, {0, 2, 2, 2, 2, 2, 2, 2},
+ {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1},
+ {0, 2, 2, 2, 2, 2, 2, 2}, {0, 3, 3, 3, 3, 3, 3, 3},
+ {4, 4, 4, 4, 4, 4, 4, 4}, {2, 0, 2, 2, 2, 2, 2, 2},
+ {3, 3, 0, 3, 3, 3, 3, 3}, {1, 1, 1, 1, 1, 1, 1, 1},
+};
+
+inline UtfIntroCharType IntroCharTypeOf(std::istream::int_type ch) {
+ if (std::istream::traits_type::eof() == ch) {
+ return uictOther;
+ }
+
+ switch (ch) {
+ case 0:
+ return uict00;
+ case 0xBB:
+ return uictBB;
+ case 0xBF:
+ return uictBF;
+ case 0xEF:
+ return uictEF;
+ case 0xFE:
+ return uictFE;
+ case 0xFF:
+ return uictFF;
+ }
+
+ if ((ch > 0) && (ch < 0xFF)) {
+ return uictAscii;
+ }
+
+ return uictOther;
+}
+
+inline char Utf8Adjust(unsigned long ch, unsigned char lead_bits,
+ unsigned char rshift) {
+ const unsigned char header =
+ static_cast<unsigned char>(((1 << lead_bits) - 1) << (8 - lead_bits));
+ const unsigned char mask = (0xFF >> (lead_bits + 1));
+ return static_cast<char>(
+ static_cast<unsigned char>(header | ((ch >> rshift) & mask)));
+}
+
+inline void QueueUnicodeCodepoint(std::deque<char>& q, unsigned long ch) {
+ // We are not allowed to queue the Stream::eof() codepoint, so
+ // replace it with CP_REPLACEMENT_CHARACTER
+ if (static_cast<unsigned long>(Stream::eof()) == ch) {
+ ch = CP_REPLACEMENT_CHARACTER;
+ }
+
+ if (ch < 0x80) {
+ q.push_back(Utf8Adjust(ch, 0, 0));
+ } else if (ch < 0x800) {
+ q.push_back(Utf8Adjust(ch, 2, 6));
+ q.push_back(Utf8Adjust(ch, 1, 0));
+ } else if (ch < 0x10000) {
+ q.push_back(Utf8Adjust(ch, 3, 12));
+ q.push_back(Utf8Adjust(ch, 1, 6));
+ q.push_back(Utf8Adjust(ch, 1, 0));
+ } else {
+ q.push_back(Utf8Adjust(ch, 4, 18));
+ q.push_back(Utf8Adjust(ch, 1, 12));
+ q.push_back(Utf8Adjust(ch, 1, 6));
+ q.push_back(Utf8Adjust(ch, 1, 0));
+ }
+}
+
+Stream::Stream(std::istream& input)
+ : m_input(input),
+ m_mark{},
+ m_charSet{},
+ m_readahead{},
+ m_pPrefetched(new unsigned char[YAML_PREFETCH_SIZE]),
+ m_nPrefetchedAvailable(0),
+ m_nPrefetchedUsed(0) {
+ using char_traits = std::istream::traits_type;
+
+ if (!input)
+ return;
+
+ // Determine (or guess) the character-set by reading the BOM, if any. See
+ // the YAML specification for the determination algorithm.
+ char_traits::int_type intro[4]{};
+ int nIntroUsed = 0;
+ UtfIntroState state = uis_start;
+ for (; !s_introFinalState[state];) {
+ std::istream::int_type ch = input.get();
+ intro[nIntroUsed++] = ch;
+ UtfIntroCharType charType = IntroCharTypeOf(ch);
+ UtfIntroState newState = s_introTransitions[state][charType];
+ int nUngets = s_introUngetCount[state][charType];
+ if (nUngets > 0) {
+ input.clear();
+ for (; nUngets > 0; --nUngets) {
+ if (char_traits::eof() != intro[--nIntroUsed])
+ input.putback(char_traits::to_char_type(intro[nIntroUsed]));
+ }
+ }
+ state = newState;
+ }
+
+ switch (state) {
+ case uis_utf8:
+ m_charSet = utf8;
+ break;
+ case uis_utf16le:
+ m_charSet = utf16le;
+ break;
+ case uis_utf16be:
+ m_charSet = utf16be;
+ break;
+ case uis_utf32le:
+ m_charSet = utf32le;
+ break;
+ case uis_utf32be:
+ m_charSet = utf32be;
+ break;
+ default:
+ m_charSet = utf8;
+ break;
+ }
+
+ ReadAheadTo(0);
+}
+
+Stream::~Stream() { delete[] m_pPrefetched; }
+
+char Stream::peek() const {
+ if (m_readahead.empty()) {
+ return Stream::eof();
+ }
+
+ return m_readahead[0];
+}
+
+Stream::operator bool() const {
+ return m_input.good() ||
+ (!m_readahead.empty() && m_readahead[0] != Stream::eof());
+}
+
+// get
+// . Extracts a character from the stream and updates our position
+char Stream::get() {
+ char ch = peek();
+ AdvanceCurrent();
+ m_mark.column++;
+
+ if (ch == '\n') {
+ m_mark.column = 0;
+ m_mark.line++;
+ }
+
+ return ch;
+}
+
+// get
+// . Extracts 'n' characters from the stream and updates our position
+std::string Stream::get(int n) {
+ std::string ret;
+ if (n > 0) {
+ ret.reserve(static_cast<std::string::size_type>(n));
+ for (int i = 0; i < n; i++)
+ ret += get();
+ }
+ return ret;
+}
+
+// eat
+// . Eats 'n' characters and updates our position.
+void Stream::eat(int n) {
+ for (int i = 0; i < n; i++)
+ get();
+}
+
+void Stream::AdvanceCurrent() {
+ if (!m_readahead.empty()) {
+ m_readahead.pop_front();
+ m_mark.pos++;
+ }
+
+ ReadAheadTo(0);
+}
+
+bool Stream::_ReadAheadTo(size_t i) const {
+ while (m_input.good() && (m_readahead.size() <= i)) {
+ switch (m_charSet) {
+ case utf8:
+ StreamInUtf8();
+ break;
+ case utf16le:
+ StreamInUtf16();
+ break;
+ case utf16be:
+ StreamInUtf16();
+ break;
+ case utf32le:
+ StreamInUtf32();
+ break;
+ case utf32be:
+ StreamInUtf32();
+ break;
+ }
+ }
+
+ // signal end of stream
+ if (!m_input.good())
+ m_readahead.push_back(Stream::eof());
+
+ return m_readahead.size() > i;
+}
+
+void Stream::StreamInUtf8() const {
+ unsigned char b = GetNextByte();
+ if (m_input.good()) {
+ m_readahead.push_back(static_cast<char>(b));
+ }
+}
+
+void Stream::StreamInUtf16() const {
+ unsigned long ch = 0;
+ unsigned char bytes[2];
+ int nBigEnd = (m_charSet == utf16be) ? 0 : 1;
+
+ bytes[0] = GetNextByte();
+ bytes[1] = GetNextByte();
+ if (!m_input.good()) {
+ return;
+ }
+ ch = (static_cast<unsigned long>(bytes[nBigEnd]) << 8) |
+ static_cast<unsigned long>(bytes[1 ^ nBigEnd]);
+
+ if (ch >= 0xDC00 && ch < 0xE000) {
+ // Trailing (low) surrogate...ugh, wrong order
+ QueueUnicodeCodepoint(m_readahead, CP_REPLACEMENT_CHARACTER);
+ return;
+ }
+
+ if (ch >= 0xD800 && ch < 0xDC00) {
+ // ch is a leading (high) surrogate
+
+ // Four byte UTF-8 code point
+
+ // Read the trailing (low) surrogate
+ for (;;) {
+ bytes[0] = GetNextByte();
+ bytes[1] = GetNextByte();
+ if (!m_input.good()) {
+ QueueUnicodeCodepoint(m_readahead, CP_REPLACEMENT_CHARACTER);
+ return;
+ }
+ unsigned long chLow = (static_cast<unsigned long>(bytes[nBigEnd]) << 8) |
+ static_cast<unsigned long>(bytes[1 ^ nBigEnd]);
+ if (chLow < 0xDC00 || chLow >= 0xE000) {
+ // Trouble...not a low surrogate. Dump a REPLACEMENT CHARACTER into the
+ // stream.
+ QueueUnicodeCodepoint(m_readahead, CP_REPLACEMENT_CHARACTER);
+
+ // Deal with the next UTF-16 unit
+ if (chLow < 0xD800 || chLow >= 0xE000) {
+ // Easiest case: queue the codepoint and return
+ QueueUnicodeCodepoint(m_readahead, ch);
+ return;
+ }
+ // Start the loop over with the new high surrogate
+ ch = chLow;
+ continue;
+ }
+
+ // Select the payload bits from the high surrogate
+ ch &= 0x3FF;
+ ch <<= 10;
+
+ // Include bits from low surrogate
+ ch |= (chLow & 0x3FF);
+
+ // Add the surrogacy offset
+ ch += 0x10000;
+ break;
+ }
+ }
+
+ QueueUnicodeCodepoint(m_readahead, ch);
+}
+
+inline char* ReadBuffer(unsigned char* pBuffer) {
+ return reinterpret_cast<char*>(pBuffer);
+}
+
+unsigned char Stream::GetNextByte() const {
+ if (m_nPrefetchedUsed >= m_nPrefetchedAvailable) {
+ std::streambuf* pBuf = m_input.rdbuf();
+ m_nPrefetchedAvailable = static_cast<std::size_t>(
+ pBuf->sgetn(ReadBuffer(m_pPrefetched), YAML_PREFETCH_SIZE));
+ m_nPrefetchedUsed = 0;
+ if (!m_nPrefetchedAvailable) {
+ m_input.setstate(std::ios_base::eofbit);
+ }
+
+ if (0 == m_nPrefetchedAvailable) {
+ return 0;
+ }
+ }
+
+ return m_pPrefetched[m_nPrefetchedUsed++];
+}
+
+void Stream::StreamInUtf32() const {
+ static int indexes[2][4] = {{3, 2, 1, 0}, {0, 1, 2, 3}};
+
+ unsigned long ch = 0;
+ unsigned char bytes[4];
+ int* pIndexes = (m_charSet == utf32be) ? indexes[1] : indexes[0];
+
+ bytes[0] = GetNextByte();
+ bytes[1] = GetNextByte();
+ bytes[2] = GetNextByte();
+ bytes[3] = GetNextByte();
+ if (!m_input.good()) {
+ return;
+ }
+
+ for (int i = 0; i < 4; ++i) {
+ ch <<= 8;
+ ch |= bytes[pIndexes[i]];
+ }
+
+ QueueUnicodeCodepoint(m_readahead, ch);
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/stream.h b/src/libs/3rdparty/yaml-cpp/src/stream.h
new file mode 100644
index 0000000000..2bc7a15216
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/stream.h
@@ -0,0 +1,82 @@
+#ifndef STREAM_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define STREAM_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/mark.h"
+#include <cstddef>
+#include <deque>
+#include <ios>
+#include <iostream>
+#include <set>
+#include <string>
+
+namespace YAML {
+
+class StreamCharSource;
+
+class Stream {
+ public:
+ friend class StreamCharSource;
+
+ Stream(std::istream& input);
+ Stream(const Stream&) = delete;
+ Stream(Stream&&) = delete;
+ Stream& operator=(const Stream&) = delete;
+ Stream& operator=(Stream&&) = delete;
+ ~Stream();
+
+ operator bool() const;
+ bool operator!() const { return !static_cast<bool>(*this); }
+
+ char peek() const;
+ char get();
+ std::string get(int n);
+ void eat(int n = 1);
+
+ static char eof() { return 0x04; }
+
+ const Mark mark() const { return m_mark; }
+ int pos() const { return m_mark.pos; }
+ int line() const { return m_mark.line; }
+ int column() const { return m_mark.column; }
+ void ResetColumn() { m_mark.column = 0; }
+
+ private:
+ enum CharacterSet { utf8, utf16le, utf16be, utf32le, utf32be };
+
+ std::istream& m_input;
+ Mark m_mark;
+
+ CharacterSet m_charSet;
+ mutable std::deque<char> m_readahead;
+ unsigned char* const m_pPrefetched;
+ mutable size_t m_nPrefetchedAvailable;
+ mutable size_t m_nPrefetchedUsed;
+
+ void AdvanceCurrent();
+ char CharAt(size_t i) const;
+ bool ReadAheadTo(size_t i) const;
+ bool _ReadAheadTo(size_t i) const;
+ void StreamInUtf8() const;
+ void StreamInUtf16() const;
+ void StreamInUtf32() const;
+ unsigned char GetNextByte() const;
+};
+
+// CharAt
+// . Unchecked access
+inline char Stream::CharAt(size_t i) const { return m_readahead[i]; }
+
+inline bool Stream::ReadAheadTo(size_t i) const {
+ if (m_readahead.size() > i)
+ return true;
+ return _ReadAheadTo(i);
+}
+} // namespace YAML
+
+#endif // STREAM_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/streamcharsource.h b/src/libs/3rdparty/yaml-cpp/src/streamcharsource.h
new file mode 100644
index 0000000000..826ba5347e
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/streamcharsource.h
@@ -0,0 +1,50 @@
+#ifndef STREAMCHARSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define STREAMCHARSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/noexcept.h"
+#include "stream.h"
+#include <cstddef>
+
+namespace YAML {
+
+class StreamCharSource {
+ public:
+ StreamCharSource(const Stream& stream) : m_offset(0), m_stream(stream) {}
+ StreamCharSource(const StreamCharSource& source) = default;
+ StreamCharSource(StreamCharSource&&) YAML_CPP_NOEXCEPT = default;
+ StreamCharSource& operator=(const StreamCharSource&) = delete;
+ StreamCharSource& operator=(StreamCharSource&&) = delete;
+ ~StreamCharSource() = default;
+
+ operator bool() const;
+ char operator[](std::size_t i) const { return m_stream.CharAt(m_offset + i); }
+ bool operator!() const { return !static_cast<bool>(*this); }
+
+ const StreamCharSource operator+(int i) const;
+
+ private:
+ std::size_t m_offset;
+ const Stream& m_stream;
+};
+
+inline StreamCharSource::operator bool() const {
+ return m_stream.ReadAheadTo(m_offset);
+}
+
+inline const StreamCharSource StreamCharSource::operator+(int i) const {
+ StreamCharSource source(*this);
+ if (static_cast<int>(source.m_offset) + i >= 0)
+ source.m_offset += static_cast<std::size_t>(i);
+ else
+ source.m_offset = 0;
+ return source;
+}
+} // namespace YAML
+
+#endif // STREAMCHARSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/stringsource.h b/src/libs/3rdparty/yaml-cpp/src/stringsource.h
new file mode 100644
index 0000000000..6fee44bb28
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/stringsource.h
@@ -0,0 +1,48 @@
+#ifndef STRINGSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define STRINGSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <cstddef>
+
+namespace YAML {
+class StringCharSource {
+ public:
+ StringCharSource(const char* str, std::size_t size)
+ : m_str(str), m_size(size), m_offset(0) {}
+
+ operator bool() const { return m_offset < m_size; }
+ char operator[](std::size_t i) const { return m_str[m_offset + i]; }
+ bool operator!() const { return !static_cast<bool>(*this); }
+
+ const StringCharSource operator+(int i) const {
+ StringCharSource source(*this);
+ if (static_cast<int>(source.m_offset) + i >= 0)
+ source.m_offset += i;
+ else
+ source.m_offset = 0;
+ return source;
+ }
+
+ StringCharSource& operator++() {
+ ++m_offset;
+ return *this;
+ }
+
+ StringCharSource& operator+=(std::size_t offset) {
+ m_offset += offset;
+ return *this;
+ }
+
+ private:
+ const char* m_str;
+ std::size_t m_size;
+ std::size_t m_offset;
+};
+}
+
+#endif // STRINGSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/tag.cpp b/src/libs/3rdparty/yaml-cpp/src/tag.cpp
new file mode 100644
index 0000000000..35a1b46560
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/tag.cpp
@@ -0,0 +1,50 @@
+#include <cassert>
+#include <stdexcept>
+
+#include "directives.h" // IWYU pragma: keep
+#include "tag.h"
+#include "token.h"
+
+namespace YAML {
+Tag::Tag(const Token& token)
+ : type(static_cast<TYPE>(token.data)), handle{}, value{} {
+ switch (type) {
+ case VERBATIM:
+ value = token.value;
+ break;
+ case PRIMARY_HANDLE:
+ value = token.value;
+ break;
+ case SECONDARY_HANDLE:
+ value = token.value;
+ break;
+ case NAMED_HANDLE:
+ handle = token.value;
+ value = token.params[0];
+ break;
+ case NON_SPECIFIC:
+ break;
+ default:
+ assert(false);
+ }
+}
+
+std::string Tag::Translate(const Directives& directives) {
+ switch (type) {
+ case VERBATIM:
+ return value;
+ case PRIMARY_HANDLE:
+ return directives.TranslateTagHandle("!") + value;
+ case SECONDARY_HANDLE:
+ return directives.TranslateTagHandle("!!") + value;
+ case NAMED_HANDLE:
+ return directives.TranslateTagHandle("!" + handle + "!") + value;
+ case NON_SPECIFIC:
+ // TODO:
+ return "!";
+ default:
+ assert(false);
+ }
+ throw std::runtime_error("yaml-cpp: internal error, bad tag type");
+}
+} // namespace YAML
diff --git a/src/libs/3rdparty/yaml-cpp/src/tag.h b/src/libs/3rdparty/yaml-cpp/src/tag.h
new file mode 100644
index 0000000000..c811f39559
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/tag.h
@@ -0,0 +1,33 @@
+#ifndef TAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define TAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include <string>
+
+namespace YAML {
+struct Directives;
+struct Token;
+
+struct Tag {
+ enum TYPE {
+ VERBATIM,
+ PRIMARY_HANDLE,
+ SECONDARY_HANDLE,
+ NAMED_HANDLE,
+ NON_SPECIFIC
+ };
+
+ Tag(const Token& token);
+ std::string Translate(const Directives& directives);
+
+ TYPE type;
+ std::string handle, value;
+};
+}
+
+#endif // TAG_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/src/token.h b/src/libs/3rdparty/yaml-cpp/src/token.h
new file mode 100644
index 0000000000..9c9a5b7798
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/src/token.h
@@ -0,0 +1,70 @@
+#ifndef TOKEN_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+#define TOKEN_H_62B23520_7C8E_11DE_8A39_0800200C9A66
+
+#if defined(_MSC_VER) || \
+ (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
+ (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
+#pragma once
+#endif
+
+#include "yaml-cpp/mark.h"
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace YAML {
+const std::string TokenNames[] = {
+ "DIRECTIVE", "DOC_START", "DOC_END", "BLOCK_SEQ_START",
+ "BLOCK_MAP_START", "BLOCK_SEQ_END", "BLOCK_MAP_END", "BLOCK_ENTRY",
+ "FLOW_SEQ_START", "FLOW_MAP_START", "FLOW_SEQ_END", "FLOW_MAP_END",
+ "FLOW_MAP_COMPACT", "FLOW_ENTRY", "KEY", "VALUE",
+ "ANCHOR", "ALIAS", "TAG", "SCALAR"};
+
+struct Token {
+ // enums
+ enum STATUS { VALID, INVALID, UNVERIFIED };
+ enum TYPE {
+ DIRECTIVE,
+ DOC_START,
+ DOC_END,
+ BLOCK_SEQ_START,
+ BLOCK_MAP_START,
+ BLOCK_SEQ_END,
+ BLOCK_MAP_END,
+ BLOCK_ENTRY,
+ FLOW_SEQ_START,
+ FLOW_MAP_START,
+ FLOW_SEQ_END,
+ FLOW_MAP_END,
+ FLOW_MAP_COMPACT,
+ FLOW_ENTRY,
+ KEY,
+ VALUE,
+ ANCHOR,
+ ALIAS,
+ TAG,
+ PLAIN_SCALAR,
+ NON_PLAIN_SCALAR
+ };
+
+ // data
+ Token(TYPE type_, const Mark& mark_)
+ : status(VALID), type(type_), mark(mark_), value{}, params{}, data(0) {}
+
+ friend std::ostream& operator<<(std::ostream& out, const Token& token) {
+ out << TokenNames[token.type] << std::string(": ") << token.value;
+ for (const std::string& param : token.params)
+ out << std::string(" ") << param;
+ return out;
+ }
+
+ STATUS status;
+ TYPE type;
+ Mark mark;
+ std::string value;
+ std::vector<std::string> params;
+ int data;
+};
+} // namespace YAML
+
+#endif // TOKEN_H_62B23520_7C8E_11DE_8A39_0800200C9A66
diff --git a/src/libs/3rdparty/yaml-cpp/yaml-cpp.qbs b/src/libs/3rdparty/yaml-cpp/yaml-cpp.qbs
new file mode 100644
index 0000000000..d6558880f8
--- /dev/null
+++ b/src/libs/3rdparty/yaml-cpp/yaml-cpp.qbs
@@ -0,0 +1,102 @@
+QtcLibrary {
+ name: "yaml-cpp"
+
+ cpp.defines: base.concat(["YAML_CPP_DLL", "yaml_cpp_EXPORTS"])
+ cpp.includePaths: [product.sourceDirectory + "/include/"]
+
+ files: [
+ "include/yaml-cpp/anchor.h",
+ "include/yaml-cpp/binary.h",
+ "include/yaml-cpp/depthguard.h",
+ "include/yaml-cpp/dll.h",
+ "include/yaml-cpp/emitfromevents.h",
+ "include/yaml-cpp/emitter.h",
+ "include/yaml-cpp/emitterdef.h",
+ "include/yaml-cpp/emittermanip.h",
+ "include/yaml-cpp/emitterstyle.h",
+ "include/yaml-cpp/eventhandler.h",
+ "include/yaml-cpp/exceptions.h",
+ "include/yaml-cpp/mark.h",
+ "include/yaml-cpp/noexcept.h",
+ "include/yaml-cpp/node",
+ "include/yaml-cpp/node/convert.h",
+ "include/yaml-cpp/node/detail",
+ "include/yaml-cpp/node/detail/impl.h",
+ "include/yaml-cpp/node/detail/iterator.h",
+ "include/yaml-cpp/node/detail/iterator_fwd.h",
+ "include/yaml-cpp/node/detail/memory.h",
+ "include/yaml-cpp/node/detail/node.h",
+ "include/yaml-cpp/node/detail/node_data.h",
+ "include/yaml-cpp/node/detail/node_iterator.h",
+ "include/yaml-cpp/node/detail/node_ref.h",
+ "include/yaml-cpp/node/emit.h",
+ "include/yaml-cpp/node/impl.h",
+ "include/yaml-cpp/node/iterator.h",
+ "include/yaml-cpp/node/node.h",
+ "include/yaml-cpp/node/parse.h",
+ "include/yaml-cpp/node/ptr.h",
+ "include/yaml-cpp/node/type.h",
+ "include/yaml-cpp/null.h",
+ "include/yaml-cpp/ostream_wrapper.h",
+ "include/yaml-cpp/parser.h",
+ "include/yaml-cpp/stlemitter.h",
+ "include/yaml-cpp/traits.h",
+ "include/yaml-cpp/yaml.h",
+ "src/binary.cpp",
+ "src/collectionstack.h",
+ "src/convert.cpp",
+ "src/depthguard.cpp",
+ "src/directives.cpp",
+ "src/directives.h",
+ "src/emit.cpp",
+ "src/emitfromevents.cpp",
+ "src/emitter.cpp",
+ "src/emitterstate.cpp",
+ "src/emitterstate.h",
+ "src/emitterutils.cpp",
+ "src/emitterutils.h",
+ "src/exceptions.cpp",
+ "src/exp.cpp",
+ "src/exp.h",
+ "src/indentation.h",
+ "src/memory.cpp",
+ "src/node.cpp",
+ "src/node_data.cpp",
+ "src/nodebuilder.cpp",
+ "src/nodebuilder.h",
+ "src/nodeevents.cpp",
+ "src/nodeevents.h",
+ "src/null.cpp",
+ "src/ostream_wrapper.cpp",
+ "src/parse.cpp",
+ "src/parser.cpp",
+ "src/ptr_vector.h",
+ "src/regex_yaml.cpp",
+ "src/regex_yaml.h",
+ "src/regeximpl.h",
+ "src/scanner.cpp",
+ "src/scanner.h",
+ "src/scanscalar.cpp",
+ "src/scanscalar.h",
+ "src/scantag.cpp",
+ "src/scantag.h",
+ "src/scantoken.cpp",
+ "src/setting.h",
+ "src/simplekey.cpp",
+ "src/singledocparser.cpp",
+ "src/singledocparser.h",
+ "src/stream.cpp",
+ "src/stream.h",
+ "src/streamcharsource.h",
+ "src/stringsource.h",
+ "src/tag.cpp",
+ "src/tag.h",
+ "src/token.h",
+ ]
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory + "/include/"]
+ cpp.defines: "YAML_CPP_DLL"
+ }
+}